Files
Jungfraujoch/fpga/pcie_driver/jfjoch_function.c

453 lines
16 KiB
C

// Copyright (2019-2023) Paul Scherrer Institute
#include "jfjoch_drv.h"
#include <linux/bitops.h>
#include <linux/types.h>
#include <linux/delay.h>
DEFINE_MUTEX(set_config_mutex);
DEFINE_MUTEX(set_mac_mutex);
DEFINE_MUTEX(send_wr_mutex);
DEFINE_MUTEX(read_wc_mutex);
void jfjoch_write_register(struct jfjoch_drvdata *drvdata, uint32_t addr, uint32_t val) {
iowrite32(val, drvdata->bar0 + addr);
}
uint32_t jfjoch_read_register(struct jfjoch_drvdata *drvdata, uint32_t addr) {
return ioread32(drvdata->bar0 + addr);
}
void jfjoch_start(struct jfjoch_drvdata *drvdata) {
// Set PCIe beats counters
iowrite32((1 << 1), drvdata->bar0 + PCIE_OFFSET + (1<<12) + 0xC0);
iowrite32((1 << 2), drvdata->bar0 + PCIE_OFFSET + (1<<12) + 0xC0);
// Start DMA
// Run C2H
iowrite32(JFJOCH_DMA_SETTINGS, drvdata->bar0 + PCIE_OFFSET + (1<<12) + 0x04);
// Set Mailbox FIFOs, so interrupt threshold is 16 messages
// => This way it ensures that one can always execute read/write operation on the FIFO
iowrite32(255-16, drvdata->bar0 + MAILBOX_OFFSET + ADDR_MAILBOX_SIT);
iowrite32(15 , drvdata->bar0 + MAILBOX_OFFSET + ADDR_MAILBOX_RIT);
// Write Start value to action config register
iowrite32(0x1, drvdata->bar0 + ACTION_CONFIG_OFFSET);
}
void jfjoch_end(struct jfjoch_drvdata *drvdata) {
// Write cancel register
iowrite32(0x4, drvdata->bar0 + ACTION_CONFIG_OFFSET);
// RUN ==> C2H channel 0 control register
iowrite32(0, drvdata->bar0 + PCIE_OFFSET + (1<<12) + 0x04);
}
void jfjoch_cancel(struct jfjoch_drvdata *drvdata) {
iowrite32(0x4, drvdata->bar0 + ACTION_CONFIG_OFFSET);
}
int jfjoch_send_wr(struct jfjoch_drvdata *drvdata, u32 handle) {
u32 sta;
dma_addr_t addr = 0;
u32 parity = 0;
if (handle != 0xFFFFFFFFLU) {
if (handle >= nbuffer)
return -EFAULT;
addr = drvdata->bufs[handle].dma_address;
}
parity = (hweight32(handle) + hweight64(addr)) % 2;
mutex_lock(&send_wr_mutex);
sta = ioread32(drvdata->bar0 + MAILBOX_OFFSET + ADDR_MAILBOX_STATUS) & MAILBOX_STA;
if (!sta) {
mutex_unlock(&send_wr_mutex);
return -EAGAIN;
}
iowrite32(handle, drvdata->bar0 + MAILBOX_OFFSET + ADDR_MAILBOX_WRDATA);
iowrite32(PCI_DMA_H(addr), drvdata->bar0 + MAILBOX_OFFSET + ADDR_MAILBOX_WRDATA);
iowrite32(PCI_DMA_L(addr), drvdata->bar0 + MAILBOX_OFFSET + ADDR_MAILBOX_WRDATA);
iowrite32(parity, drvdata->bar0 + MAILBOX_OFFSET + ADDR_MAILBOX_WRDATA);
mutex_unlock(&send_wr_mutex);
return 0;
}
int jfjoch_read_wc(struct jfjoch_drvdata *drvdata, u32 *output) {
u32 rta;
int i;
mutex_lock(&read_wc_mutex);
rta = ioread32(drvdata->bar0 + MAILBOX_OFFSET + ADDR_MAILBOX_STATUS) & MAILBOX_RTA;
if (!rta) {
mutex_unlock(&read_wc_mutex);
return -EAGAIN;
}
for (i = 0; i < 16; i++)
output[i] = ioread32(drvdata->bar0 + MAILBOX_OFFSET + ADDR_MAILBOX_RDDATA);
mutex_unlock(&read_wc_mutex);
return 0;
}
void jfjoch_set_config(struct jfjoch_drvdata *drvdata, const struct DataCollectionConfig *config) {
mutex_lock(&set_config_mutex);
memcpy_toio((drvdata->bar0) + ACTION_CONFIG_OFFSET + ADDR_NMODULES, config, sizeof(struct DataCollectionConfig));
mutex_unlock(&set_config_mutex);
}
void jfjoch_get_config(struct jfjoch_drvdata *drvdata, struct DataCollectionConfig *config) {
memcpy_fromio(config, (drvdata->bar0) + ACTION_CONFIG_OFFSET + ADDR_NMODULES, sizeof(struct DataCollectionConfig));
}
void jfjoch_get_status(struct jfjoch_drvdata *drvdata, struct DataCollectionStatus *status) {
memcpy_fromio(status, drvdata->bar0 + ACTION_CONFIG_OFFSET, sizeof(struct DataCollectionStatus));
}
void jfjoch_set_mac_addr(struct jfjoch_drvdata *drvdata, u64 *mac_addr) {
mutex_lock(&set_mac_mutex);
memcpy_toio((drvdata->bar0) + ACTION_CONFIG_OFFSET + ADDR_MAC_ADDR_LO, mac_addr, sizeof(uint64_t));
mutex_unlock(&set_mac_mutex);
}
void jfjoch_get_mac_addr(struct jfjoch_drvdata *drvdata, u64 *mac_addr) {
memcpy_fromio(mac_addr, (drvdata->bar0) + ACTION_CONFIG_OFFSET + ADDR_MAC_ADDR_LO, sizeof(uint64_t));
}
void jfjoch_set_ipv4_addr(struct jfjoch_drvdata *drvdata, const u32 *addr) {
mutex_lock(&set_mac_mutex);
iowrite32(*addr, (drvdata->bar0) + ACTION_CONFIG_OFFSET + ADDR_IPV4_ADDR);
mutex_unlock(&set_mac_mutex);
}
void jfjoch_get_ipv4_addr(struct jfjoch_drvdata *drvdata, u32 *addr) {
*addr = ioread32((drvdata->bar0) + ACTION_CONFIG_OFFSET + ADDR_IPV4_ADDR);
}
u64 jfjoch_read_mac_addr(struct jfjoch_drvdata *drvdata) {
struct device *const dev = &drvdata->pdev->dev;
u32 tmp, host_msg_offset, msg_len;
u32 field_type, field_len;
u32 i, j;
u8 output[256];
u64 mac = 0;
u64 design_number;
// Check which card is this
if (ioread32(drvdata->bar0 + ACTION_CONFIG_OFFSET) & (1<<7))
design_number = 1;
else
design_number = 0;
if (ioread32(drvdata->bar0 + CMS_OFFSET + ADDR_CMS_CONTROL_REG) & (1 << 5)) {
dev_err(dev, "Mailbox not available");
return 0;
}
host_msg_offset = ioread32(drvdata->bar0 + CMS_OFFSET + 0x28300);
dev_info(dev, "Host msg offset %x", host_msg_offset);
iowrite32(0x04000000, drvdata->bar0 + CMS_OFFSET + 0x28000 + host_msg_offset);
iowrite32(1<<5, drvdata->bar0 + CMS_OFFSET + ADDR_CMS_CONTROL_REG);
i = 0;
while (i < 50) {
if (!(ioread32(drvdata->bar0 + CMS_OFFSET + ADDR_CMS_CONTROL_REG) & (1 << 5)))
break;
msleep(100);
i++;
}
tmp = ioread32(drvdata->bar0 + CMS_OFFSET + ADDR_CMS_CONTROL_REG) & (1 << 5);
if (tmp) {
dev_err(dev, "Mailbox not ready %x", tmp);
return 0;
}
tmp = ioread32(drvdata->bar0 + CMS_OFFSET + 0x28304);
if (tmp) {
dev_err(dev, "Error in mailbox %x", tmp);
return 0;
}
tmp = ioread32(drvdata->bar0 + CMS_OFFSET + 0x28000 + host_msg_offset);
dev_info(dev, "Mailbox response %x", tmp);
if ((tmp & 0xFF000000) != 0x04000000) {
dev_err(dev, "Opcode in return message doesn't match %x", tmp);
return 0;
}
msg_len = tmp & 0xFFF;
dev_info(dev, "Mailbox response length %x", msg_len);
i = 0;
while (i < msg_len) {
field_type = ioread8(drvdata->bar0 + CMS_OFFSET + 0x28000 + host_msg_offset + 0x4 + i);
field_len = ioread8(drvdata->bar0 + CMS_OFFSET + 0x28000 + host_msg_offset + 0x4 + i + 1);
for (j = 0; j < field_len; j++)
output[j] = ioread8(drvdata->bar0 + CMS_OFFSET + 0x28000 + host_msg_offset + 0x4 + i + 2 + j);
output[field_len] = 0x0;
if (field_type == 0x21)
dev_info(dev, "Card serial number %s", output);
if (field_type == 0x4B) {
dev_info(dev, "MAC Addr %d %02x:%02x:%02x:%02x:%02x:%02x",
output[0], output[2], output[3], output[4], output[5], output[6], output[7]);
mac = ((u64)(output[2]))
| ((u64)(output[3]) << 8)
| ((u64)(output[4]) << 16)
| ((u64)(output[5]) << 24)
| ((u64)(output[6]) << 32)
| ((u64)(output[7]) << 40);
mac += (design_number << 40);
jfjoch_set_mac_addr(drvdata, &mac);
}
i += field_len + 2;
}
return mac;
}
void jfjoch_is_idle(struct jfjoch_drvdata *drvdata, uint32_t *output) {
if (ioread32(drvdata->bar0 + ACTION_CONFIG_OFFSET + ADDR_CTRL_REGISTER) & CTRL_REGISTER_IDLE)
*output = 1;
else
*output = 0;
}
void jfjoch_setup_cms(struct jfjoch_drvdata *drvdata) {
iowrite32(0x1, drvdata->bar0 + CMS_OFFSET + ADDR_CMS_MB_RESETN_REG);
iowrite32(1 << 27, drvdata->bar0 + CMS_OFFSET + ADDR_CMS_CONTROL_REG); // enable HBM monitoring
}
void jfjoch_setup_network(struct jfjoch_drvdata *drvdata) {
// Enable RS_FEC
iowrite32(0x03, drvdata->bar0 + CMAC_OFFSET + 0x107C);
// Restart TX and RX cores + deassert resets
iowrite32((1 << 31) + (1 << 30), drvdata->bar0 + CMAC_OFFSET + 0x0004);
iowrite32(0x0, drvdata->bar0 + CMAC_OFFSET + 0x0004);
// TX Send RFI
iowrite32(0x10, drvdata->bar0 + CMAC_OFFSET + 0x000C);
// Enable RX
iowrite32(0x1, drvdata->bar0 + CMAC_OFFSET + 0x0014);
// start communication
iowrite32(0x1, drvdata->bar0 + CMAC_OFFSET + 0x000C);
// enable counters
iowrite32(0, drvdata->bar0 + ACTION_CONFIG_OFFSET);
}
void jfjoch_get_env_data(struct jfjoch_drvdata *drvdata, struct DeviceStatus *env_params) {
env_params->mailbox_status_reg = ioread32(drvdata->bar0 + MAILBOX_OFFSET + 0x10);
env_params->mailbox_err_reg = ioread32(drvdata->bar0 + MAILBOX_OFFSET + 0x14);
env_params->fpga_temp_C = ioread32(drvdata->bar0 + CMS_OFFSET + ADDR_CMS_FPGA_TEMP_INS_REG);
env_params->fpga_pcie_3p3V_I_mA = ioread32(drvdata->bar0 + CMS_OFFSET + ADDR_CMS_3V3PEX_I_IN_INS_REG);
env_params->fpga_pcie_12V_I_mA = ioread32(drvdata->bar0 + CMS_OFFSET + ADDR_CMS_12VPEX_I_IN_INS_REG);
env_params->fpga_pcie_3p3V_V_mV = ioread32(drvdata->bar0 + CMS_OFFSET + ADDR_CMS_3V3_PEX_INS_REG);
env_params->fpga_pcie_12V_V_mV = ioread32(drvdata->bar0 + CMS_OFFSET + ADDR_CMS_12V_PEX_INS_REG);
env_params->pcie_h2c_descriptors = ioread32(drvdata->bar0 + PCIE_OFFSET + (0<<12) + 0x48);
env_params->pcie_h2c_beats = ioread32(drvdata->bar0 + PCIE_OFFSET + (0<<12) + 0xCC);
env_params->pcie_h2c_status = ioread32(drvdata->bar0 + PCIE_OFFSET + (0<<12) + 0x40);
env_params->pcie_c2h_descriptors = ioread32(drvdata->bar0 + PCIE_OFFSET + (1<<12) + 0x48);
env_params->pcie_c2h_beats = ioread32(drvdata->bar0 + PCIE_OFFSET + (1<<12) + 0xCC);
env_params->pcie_c2h_status = ioread32(drvdata->bar0 + PCIE_OFFSET + (1<<12) + 0x40);
env_params->hbm_0_temp_C = ioread32(drvdata->bar0 + CMS_OFFSET + ADDR_CMS_HBM_TEMP1_INS_REG);
env_params->hbm_1_temp_C = ioread32(drvdata->bar0 + CMS_OFFSET + ADDR_CMS_HBM_TEMP2_INS_REG);
// Somehow it is better to ask twice
env_params->ethernet_aligned = ioread32(drvdata->bar0 + CMAC_OFFSET + 0x0204) & 0x2;
env_params->ethernet_aligned = ioread32(drvdata->bar0 + CMAC_OFFSET + 0x0204) & 0x2;
}
void jfjoch_clr_net_counters(struct jfjoch_drvdata *drvdata) {
iowrite32(1 << 3, drvdata->bar0 + ACTION_CONFIG_OFFSET);
iowrite32(0, drvdata->bar0 + ACTION_CONFIG_OFFSET);
}
int jfjoch_load_calibration(struct jfjoch_drvdata *drvdata, struct DataCollectionConfig *config) {
struct device *const dev = &drvdata->pdev->dev;
u32 i;
u32 cell_count = config->nmodules * (3 + 3 * config->nstorage_cells);
if (cell_count > drvdata->nbuf) {
dev_err(dev, "Not enough buffers to support this card\n");
return -EINVAL;
}
for (i = 0; i < cell_count; i++) {
u64 addr = drvdata->bufs[i].dma_address;
iowrite32(PCI_DMA_L(addr), drvdata->bar0 + ADDR_LOAD_CALIBRATION_MEM + i * 2 * 4);
iowrite32(PCI_DMA_H(addr), drvdata->bar0 + ADDR_LOAD_CALIBRATION_MEM + (i * 2 + 1) * 4);
}
// Start DMA
// Clear counters and RUN H2C
iowrite32((1 << 1), drvdata->bar0 + PCIE_OFFSET + (0<<12) + 0xC0);
iowrite32((1 << 2), drvdata->bar0 + PCIE_OFFSET + (0<<12) + 0xC0);
iowrite32(JFJOCH_DMA_SETTINGS, drvdata->bar0 + PCIE_OFFSET + (0<<12) + 0x04);
iowrite32(config->nmodules, drvdata->bar0 + ADDR_LOAD_CALIBRATION_MOD);
iowrite32(config->nstorage_cells, drvdata->bar0 + ADDR_LOAD_CALIBRATION_SC);
iowrite32(LOAD_CALIBRATION_DEST_CALIB, drvdata->bar0 + ADDR_LOAD_CALIBRATION_DEST);
iowrite32(0x1, drvdata->bar0 + ADDR_LOAD_CALIBRATION_CTRL);
i = 0;
while (i < 1000) {
if (ioread32(drvdata->bar0 + ADDR_LOAD_CALIBRATION_CTRL) & (1 << 1))
break;
msleep(10);
i++;
}
// STOP H2C channel
iowrite32(0, drvdata->bar0 + PCIE_OFFSET + (0<<12) + 0x04);
if (i == 1000) {
dev_err(dev, "Load calibration didn't finish in 10 seconds\n");
return -ETIMEDOUT;
}
return 0;
}
int jfjoch_load_integration_map(struct jfjoch_drvdata *drvdata, struct DataCollectionConfig *config) {
struct device *const dev = &drvdata->pdev->dev;
u32 i;
u32 cell_count = config->nmodules;
if (cell_count > drvdata->nbuf) {
dev_err(dev, "Not enough buffers to support this card\n");
return -EINVAL;
}
for (i = 0; i < cell_count; i++) {
u64 addr = drvdata->bufs[i].dma_address;
iowrite32(PCI_DMA_L(addr), drvdata->bar0 + ADDR_LOAD_CALIBRATION_MEM + i * 2 * 4);
iowrite32(PCI_DMA_H(addr), drvdata->bar0 + ADDR_LOAD_CALIBRATION_MEM + (i * 2 + 1) * 4);
}
// Start DMA
// Clear counters and RUN H2C
iowrite32((1 << 1), drvdata->bar0 + PCIE_OFFSET + (0<<12) + 0xC0);
iowrite32((1 << 2), drvdata->bar0 + PCIE_OFFSET + (0<<12) + 0xC0);
iowrite32(JFJOCH_DMA_SETTINGS, drvdata->bar0 + PCIE_OFFSET + (0<<12) + 0x04);
iowrite32(config->nmodules, drvdata->bar0 + ADDR_LOAD_CALIBRATION_MOD);
iowrite32(LOAD_CALIBRATION_DEST_INTEGRATION, drvdata->bar0 + ADDR_LOAD_CALIBRATION_DEST);
iowrite32(0x1, drvdata->bar0 + ADDR_LOAD_CALIBRATION_CTRL);
i = 0;
while (i < 1000) {
if (ioread32(drvdata->bar0 + ADDR_LOAD_CALIBRATION_CTRL) & (1 << 1))
break;
msleep(10);
i++;
}
// STOP H2C channel
iowrite32(0, drvdata->bar0 + PCIE_OFFSET + (0<<12) + 0x04);
if (i == 1000) {
dev_err(dev, "Load calibration didn't finish in 10 seconds\n");
return -ETIMEDOUT;
}
return 0;
}
int jfjoch_load_internal_generator_frame(struct jfjoch_drvdata *drvdata, struct DataCollectionConfig *config) {
struct device *const dev = &drvdata->pdev->dev;
u32 i;
u32 cell_count = config->nmodules;
if (cell_count > drvdata->nbuf) {
dev_err(dev, "Not enough buffers to support this card\n");
return -EINVAL;
}
for (i = 0; i < cell_count; i++) {
u64 addr = drvdata->bufs[i].dma_address;
iowrite32(PCI_DMA_L(addr), drvdata->bar0 + ADDR_LOAD_CALIBRATION_MEM + i * 2 * 4);
iowrite32(PCI_DMA_H(addr), drvdata->bar0 + ADDR_LOAD_CALIBRATION_MEM + (i * 2 + 1) * 4);
}
// Start DMA
// Clear counters and RUN H2C
iowrite32((1 << 1), drvdata->bar0 + PCIE_OFFSET + (0<<12) + 0xC0);
iowrite32((1 << 2), drvdata->bar0 + PCIE_OFFSET + (0<<12) + 0xC0);
iowrite32(JFJOCH_DMA_SETTINGS, drvdata->bar0 + PCIE_OFFSET + (0<<12) + 0x04);
iowrite32(config->nmodules, drvdata->bar0 + ADDR_LOAD_CALIBRATION_MOD);
iowrite32(LOAD_CALIBRATION_DEST_FRAME_GEN, drvdata->bar0 + ADDR_LOAD_CALIBRATION_DEST);
iowrite32(0x1, drvdata->bar0 + ADDR_LOAD_CALIBRATION_CTRL);
i = 0;
while (i < 1000) {
if (ioread32(drvdata->bar0 + ADDR_LOAD_CALIBRATION_CTRL) & (1 << 1))
break;
msleep(10);
i++;
}
// STOP H2C channel
iowrite32(0, drvdata->bar0 + PCIE_OFFSET + (0<<12) + 0x04);
if (i == 1000) {
dev_err(dev, "Load calibration didn't finish in 10 seconds\n");
return -ETIMEDOUT;
}
return 0;
}
int jfjoch_run_frame_gen(struct jfjoch_drvdata *drvdata, struct FrameGeneratorConfig *config) {
struct device *const dev = &drvdata->pdev->dev;
if (ioread32(drvdata->bar0 + ADDR_FRAME_GEN_CTRL) & 0x1) {
dev_err(dev, "Frame generator busy\n");
return -EBUSY;
}
iowrite32(config->frames, drvdata->bar0 + ADDR_FRAME_GEN_FRAMES);
iowrite32(config->modules, drvdata->bar0 + ADDR_FRAME_GEN_MODULES);
iowrite32(config->dest_ipv4_addr, drvdata->bar0 + ADDR_FRAME_GEN_DEST_IPV4_ADDR);
iowrite32(config->dest_mac_addr & 0xFFFFFFFF, drvdata->bar0 + ADDR_FRAME_GEN_DEST_MAC_LO);
iowrite32(config->dest_mac_addr >> 32, drvdata->bar0 + ADDR_FRAME_GEN_DEST_MAC_HI);
iowrite32(config->bunchid & 0xFFFFFFFF, drvdata->bar0 + ADDR_FRAME_GEN_BUNCHID_LO);
iowrite32(config->bunchid >> 32, drvdata->bar0 + ADDR_FRAME_GEN_BUNCHID_HI);
iowrite32(config->exptime, drvdata->bar0 + ADDR_FRAME_GEN_EXPTIME);
iowrite32(config->debug, drvdata->bar0 + ADDR_FRAME_GEN_DEBUG);
iowrite32(0x1, drvdata->bar0 + ADDR_FRAME_GEN_CTRL);
return 0;
}
u32 jfjoch_get_c2h_descriptors(struct jfjoch_drvdata *drvdata) {
return ioread32(drvdata->bar0 + PCIE_OFFSET + (1<<12) + 0x48);
}
void jfjoch_set_spot_finder_parameters(struct jfjoch_drvdata *drvdata, struct SpotFinderParameters *params) {
iowrite32(params->count_threshold, drvdata->bar0 + ADDR_SPOT_FINDER_THRESHOLD);
iowrite32(params->snr_threshold, drvdata->bar0 + ADDR_SPOT_FINDER_SNR);
}