Commit 1598de1a authored by Michel Arruat's avatar Michel Arruat Committed by Federico Vaga

driver: major rewrite, with carrier dma-ops and other things

This is a major change, work of Michel Arruat, then re-committed as a
single commit by Alessandro.  The original development happened mainly
on the SVEC carrier, and support for SPEC (PCI) was added back at a
later time.  I (Alessandro) chose to pick individual changes one at
a time, for better documentation and later "git blame", up to this point.

Now, this is a big commit that completely changes the driver,
mainly in the following places:

  - split carrier-specific from generic code, like the TDC driver is now
    (this is mainly the dma-specific and irq-specific code)
  - use of SDB to get base addresses (with new fmc-util file)
  - use the VIC core to manage interrupts, relying on carrier support
  - change the field-desc table for the better
  - add carrier-specific init, reset, exit
  - split irq and adc-specific stuff out of zio-drv.c
  - DMA data and interrupt is now different: the timestamp is at end-of-data,
    thus we don't have the "trigger-happened" interrupt any more
  - use a work queue for interrupt management, instead of a tasklet, this
    allows sleeping, which is needed by this code base
Signed-off-by: Alessandro Rubini's avatarAlessandro Rubini <rubini@gnudd.com>
parent 8a06eff4
......@@ -24,14 +24,18 @@ subdirs-ccflags-y = $(ccflags-y)
obj-m := fmc-adc-100m14b.o
fmc-adc-100m14b-objs = fa-core.o
fmc-adc-100m14b-objs += fa-spec-core.o
fmc-adc-100m14b-objs += fa-zio-drv.o
fmc-adc-100m14b-objs += fa-calibration.o
fmc-adc-100m14b-objs += fa-regtable.o
fmc-adc-100m14b-objs += fa-spec-regtable.o
fmc-adc-100m14b-objs += fa-zio-trg.o
fmc-adc-100m14b-objs += fa-spec-dma.o
fmc-adc-100m14b-objs += fa-irq.o
fmc-adc-100m14b-objs += fa-irq.o fa-spec-irq.o
fmc-adc-100m14b-objs += onewire.o
fmc-adc-100m14b-objs += spi.o
fmc-adc-100m14b-objs += fmc-util.o
all modules:
$(MAKE) -C $(LINUX) M=$(shell /bin/pwd) modules
......
......@@ -12,13 +12,22 @@
#include <linux/init.h>
#include <linux/fmc.h>
#include <linux/fmc-sdb.h>
#include "fmc-adc.h"
/* Module parameters */
static struct fmc_driver fa_dev_drv;
FMC_PARAM_BUSID(fa_dev_drv);
FMC_PARAM_GATEWARE(fa_dev_drv);
static int fa_show_sdb;
module_param_named(show_sdb, fa_show_sdb, int, 0444);
MODULE_PARM_DESC(show_sdb, "Print a dump of the gateware's SDB tree.");
static int fa_enable_test_data = 0;
module_param_named(enable_test_data, fa_enable_test_data, int, 0444);
static const int zfad_hw_range[] = {
[ZFA_RANGE_10V] = 0x45,
[ZFA_RANGE_1V] = 0x11,
......@@ -73,7 +82,7 @@ int zfad_apply_user_offset(struct fa_dev *fa, struct zio_channel *chan,
return -EINVAL;
i = zfad_get_chx_index(ZFA_CHx_CTL_RANGE, chan);
zfa_hardware_read(fa, i, &range_reg);
range_reg = fa_readl(fa, fa->fa_adc_csr_base, &zfad_regs[i]);
range = zfad_convert_hw_range(range_reg);
if (range < 0)
......@@ -131,7 +140,7 @@ int zfad_set_range(struct fa_dev *fa, struct zio_channel *chan,
/* Actually set the range */
i = zfad_get_chx_index(ZFA_CHx_CTL_RANGE, chan);
zfa_hardware_write(fa, i, zfad_hw_range[range]);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[i], zfad_hw_range[range]);
if (range == ZFA_RANGE_OPEN) {
offset = FA_CAL_NO_OFFSET;
......@@ -147,9 +156,10 @@ int zfad_set_range(struct fa_dev *fa, struct zio_channel *chan,
}
i = zfad_get_chx_index(ZFA_CHx_OFFSET, chan);
zfa_hardware_write(fa, i, offset & 0xffff); /* prevent warning */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[i],
offset & 0xffff /* prevent warning */ );
i = zfad_get_chx_index(ZFA_CHx_GAIN, chan);
zfa_hardware_write(fa, i, gain);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[i], gain);
/* recalculate user offset for the new range */
zfad_apply_user_offset(fa, chan, fa->user_offset[chan->index]);
......@@ -157,7 +167,6 @@ int zfad_set_range(struct fa_dev *fa, struct zio_channel *chan,
return 0;
}
/*
* zfad_fsm_command
* @fa: the fmc-adc descriptor
......@@ -201,14 +210,16 @@ int zfad_fsm_command(struct fa_dev *fa, uint32_t command)
/* If START, check if we can start */
if (command == ZFA_START) {
/* Verify that SerDes PLL is lockes */
zfa_hardware_read(fa, ZFA_STA_SERDES_PLL, &val);
val = fa_readl(fa, fa->fa_adc_csr_base,
&zfad_regs[ZFA_STA_SERDES_PLL]);
if (!val) {
dev_info(dev, "Cannot start acquisition: "
"SerDes PLL not locked\n");
return -EBUSY;
}
/* Verify that SerDes is synched */
zfa_hardware_read(fa, ZFA_STA_SERDES_SYNCED, &val);
val = fa_readl(fa, fa->fa_adc_csr_base,
&zfad_regs[ZFA_STA_SERDES_SYNCED]);
if (!val) {
dev_info(dev, "Cannot start acquisition: "
"SerDes not synchronized\n");
......@@ -231,13 +242,125 @@ int zfad_fsm_command(struct fa_dev *fa, uint32_t command)
}
dev_dbg(dev, "FSM START Command, Enable interrupts\n");
zfa_hardware_write(fa, ZFA_IRQ_MASK, ZFAT_ALL);
fa_enable_irqs(fa);
} else {
dev_dbg(dev, "FSM STOP Command, Disable interrupts\n");
zfa_hardware_write(fa, ZFA_IRQ_MASK, ZFAT_NONE);
fa_disable_irqs(fa);
}
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFA_CTL_FMS_CMD],
command);
return 0;
}
/* Extract from SDB the base address of the core components */
/* which are not carrier specific */
static int __fa_sdb_get_device(struct fa_dev *fa)
{
struct fmc_device *fmc = fa->fmc;
struct device *dev = fmc->hwdev;
int ret;
ret = fmc_scan_sdb_tree(fmc, 0);
if (ret < 0) {
dev_err(dev,
"%s: no SDB in the bitstream."
"Are you sure you've provided the correct one?\n",
KBUILD_MODNAME);
return ret;
}
/* FIXME: this is obsoleted by fmc-bus internal parameters */
if (fa_show_sdb)
fmc_show_sdb_tree(fmc);
/* Now use SDB to find the base addresses */
fa->fa_irq_vic_base = fmc_find_sdb_device(fmc->sdb, 0xce42,
0x13, NULL);
fa->fa_adc_csr_base = fmc_find_sdb_device_ext(fmc->sdb, 0xce42,
0x608,
fmc->slot_id, NULL);
fa->fa_irq_adc_base = fmc_find_sdb_device_ext(fmc->sdb, 0xce42,
0x26ec6086,
fmc->slot_id, NULL);
fa->fa_utc_base = fmc_find_sdb_device_ext(fmc->sdb, 0xce42,
0x604, fmc->slot_id, NULL);
fa->fa_spi_base = fmc_find_sdb_device_ext(fmc->sdb, 0xce42, 0xe503947e,
fmc->slot_id, NULL);
fa->fa_ow_base = fmc_find_sdb_device_ext(fmc->sdb, 0xce42, 0x779c5443,
fmc->slot_id, NULL);
return ret;
}
/*
* Specific check and init
*/
static int __fa_init(struct fa_dev *fa)
{
struct device *hwdev = fa->fmc->hwdev;
struct device *msgdev = &fa->fmc->dev;
struct zio_device *zdev = fa->zdev;
int i, addr;
/* Check if hardware supports 64-bit DMA */
if (dma_set_mask(hwdev, DMA_BIT_MASK(64))) {
/* Check if hardware supports 32-bit DMA */
if (dma_set_mask(hwdev, DMA_BIT_MASK(32))) {
dev_err(msgdev, "32-bit DMA addressing not available\n");
return -EINVAL;
}
}
/* Retrieve calibration from the eeprom and validate*/
fa_read_eeprom_calib(fa);
/* Force stop FSM to prevent early trigger fire */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFA_CTL_FMS_CMD],
ZFA_STOP);
/* Initialize channels to use 1V range */
for (i = 0; i < 4; ++i) {
addr = zfad_get_chx_index(ZFA_CHx_CTL_RANGE,
&zdev->cset->chan[i]);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[addr],
ZFA_RANGE_1V);
zfad_set_range(fa, &zdev->cset->chan[i], ZFA_RANGE_1V);
}
zfad_reset_offset(fa);
/* Enable mezzanine clock */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFA_CTL_CLK_EN], 1);
/* Set decimation to minimum */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_SR_DECI], 1);
/* Set test data register */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFA_CTL_TEST_DATA_EN],
fa_enable_test_data);
/* Set to single shot mode by default */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_SHOTS_NB], 1);
if (zdev->cset->ti->cset->trig == &zfat_type) {
/* Select external trigger (index 0) */
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_HW_SEL],
1);
zdev->cset->ti->zattr_set.ext_zattr[0].value = 1;
} else {
/* Enable Software trigger*/
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_SW_EN],
1);
/* Disable Hardware trigger*/
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_HW_EN],
0);
}
zfa_hardware_write(fa, ZFA_CTL_FMS_CMD, command);
/* Zero offsets and release the DAC clear */
zfad_reset_offset(fa);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFA_CTL_DAC_CLR_N], 1);
/* Set UTC seconds from the kernel seconds */
fa_writel(fa, fa->fa_utc_base, &zfad_regs[ZFA_UTC_SECONDS],
get_seconds());
/* disable auto_start */
fa->enable_auto_start = 0;
return 0;
}
......@@ -277,10 +400,18 @@ int fa_probe(struct fmc_device *fmc)
fmc_set_drvdata(fmc, fa);
fa->fmc = fmc;
/* apply carrier-specific hacks and workarounds */
if (!strcmp(fmc->carrier_name, "SPEC"))
fa->carrier_op = &fa_spec_op;
else {
dev_err(fmc->hwdev, "unsupported carrier\n");
return -ENODEV;
}
if (fa_dev_drv.gw_n)
fwname = ""; /* reprogram will pick from module parameter */
else
fwname = FA_GATEWARE_DEFAULT_NAME;
fwname = fa->carrier_op->get_gwname();
dev_info(fmc->hwdev, "Gateware (%s)\n", fwname);
/* We first write a new binary (and lm32) within the carrier */
err = fmc->op->reprogram(fmc, &fa_dev_drv, fwname);
......@@ -291,13 +422,18 @@ int fa_probe(struct fmc_device *fmc)
}
dev_info(fmc->hwdev, "Gateware successfully loaded\n");
/* Mark base addresses (will come from sdb, later) */
//fa->fa_irq_vic_base -- not existent yet in this gateware
//fa->fa_adc_csr_base = CHx_GAIN;
//fa->fa_irq_adc_base = ENABLE_MASK;
fa->fa_utc_base = FA_UTC_MEM_OFF;
fa->fa_spi_base = FA_SPI_MEM_OFF;
fa->fa_ow_base = FA_OWI_MEM_OFF;
/* Extract whisbone core base address fron SDB */
err = __fa_sdb_get_device(fa);
if (err < 0)
goto out;
err = fa->carrier_op->init(fa);
if (err < 0)
goto out;
err = fa->carrier_op->reset_core(fa);
if (err < 0)
goto out;
/* init all subsystems */
for (i = 0, m = mods; i < ARRAY_SIZE(mods); i++, m++) {
......@@ -307,28 +443,42 @@ int fa_probe(struct fmc_device *fmc)
dev_err(&fmc->dev, "error initializing %s\n", m->name);
goto out;
}
}
/* time to execute specific driver init */
err = __fa_init(fa);
if (err < 0)
goto out;
err = fa_setup_irqs(fa);
if (err < 0)
goto out;
return 0;
out:
while (--m, --i >= 0)
if (m->exit)
m->exit(fa);
return err;
}
int fa_remove(struct fmc_device *fmc)
{
struct fa_dev *fa = fmc_get_drvdata(fmc);
struct fa_modlist *m;
int i = ARRAY_SIZE(mods);
fa_free_irqs(fa);
while (--i >= 0) {
m = mods + i;
if (m->exit)
m->exit(fa);
}
fa->carrier_op->exit(fa);
return 0;
}
......
This diff is collapsed.
......@@ -6,107 +6,94 @@
*/
#include "fmc-adc.h"
/* Definition of the fmc-adc registers address - mask - mask offset */
/* Definition of the fmc-adc registers fields: offset - mask - isbitfield */
const struct zfa_field_desc zfad_regs[] = {
/* Control registers */
[ZFA_CTL_FMS_CMD] = {FA_ADC_MEM_OFF + 0x00, 0x0003, 0},
[ZFA_CTL_CLK_EN] = {FA_ADC_MEM_OFF + 0x00, 0x0001, 2},
[ZFA_CTL_DAC_CLR_N] = {FA_ADC_MEM_OFF + 0x00, 0x0001, 3},
[ZFA_CTL_BSLIP] = {FA_ADC_MEM_OFF + 0x00, 0x0001, 4},
[ZFA_CTL_TEST_DATA_EN] = {FA_ADC_MEM_OFF + 0x00, 0x0001, 5},
[ZFA_CTL_TRIG_LED] = {FA_ADC_MEM_OFF + 0x00, 0x0001, 6},
[ZFA_CTL_ACQ_LED] = {FA_ADC_MEM_OFF + 0x00, 0x0001, 7},
[ZFA_CTL_FMS_CMD] = {0x00, 0x00000003, 1},
[ZFA_CTL_CLK_EN] = {0x00, 0x00000004, 1},
[ZFA_CTL_DAC_CLR_N] = {0x00, 0x00000008, 1},
[ZFA_CTL_BSLIP] = {0x00, 0x00000010, 1},
[ZFA_CTL_TEST_DATA_EN] = {0x00, 0x00000020, 1},
[ZFA_CTL_TRIG_LED] = {0x00, 0x00000040, 1},
[ZFA_CTL_ACQ_LED] = {0x00, 0x00000080, 1},
/* Status registers */
[ZFA_STA_FSM] = {FA_ADC_MEM_OFF + 0x04, 0x0007, 0},
[ZFA_STA_SERDES_PLL] = {FA_ADC_MEM_OFF + 0x04, 0x0001, 3},
[ZFA_STA_SERDES_SYNCED] = {FA_ADC_MEM_OFF + 0x04, 0x0001, 4},
[ZFA_STA_FSM] = {0x04, 0x00000007, 1},
[ZFA_STA_SERDES_PLL] = {0x04, 0x00000008, 1},
[ZFA_STA_SERDES_SYNCED] = {0x04, 0x00000010, 1},
/* Trigger */
/* Config register */
[ZFAT_CFG_HW_SEL] = {FA_ADC_MEM_OFF + 0x08, 0x00000001, 0},
[ZFAT_CFG_HW_POL] = {FA_ADC_MEM_OFF + 0x08, 0x00000001, 1},
[ZFAT_CFG_HW_EN] = {FA_ADC_MEM_OFF + 0x08, 0x00000001, 2},
[ZFAT_CFG_SW_EN] = {FA_ADC_MEM_OFF + 0x08, 0x00000001, 3},
[ZFAT_CFG_INT_SEL] = {FA_ADC_MEM_OFF + 0x08, 0x00000003, 4},
[ZFAT_CFG_THRES] = {FA_ADC_MEM_OFF + 0x08, 0x0000FFFF, 16},
[ZFAT_CFG_HW_SEL] = {0x08, 0x00000001, 1},
[ZFAT_CFG_HW_POL] = {0x08, 0x00000002, 1},
[ZFAT_CFG_HW_EN] = {0x08, 0x00000004, 1},
[ZFAT_CFG_SW_EN] = {0x08, 0x00000008, 1},
[ZFAT_CFG_INT_SEL] = {0x08, 0x00000070, 1},
[ZFAT_CFG_THRES] = {0x08, 0xFFFF0000, 1},
/* Delay */
[ZFAT_DLY] = {FA_ADC_MEM_OFF + 0x0C, 0xFFFFFFFF, 0},
[ZFAT_DLY] = {0x0C, 0xFFFFFFFF, 0},
/* Software */
[ZFAT_SW] = {FA_ADC_MEM_OFF + 0x10, 0xFFFFFFFF, 0},
[ZFAT_SW] = {0x10, 0xFFFFFFFF, 0},
/* Number of shots */
[ZFAT_SHOTS_NB] = {FA_ADC_MEM_OFF + 0x14, 0x0000FFFF, 0},
[ZFAT_SHOTS_NB] = {0x14, 0x0000FFFF, 0},
/* Sample rate */
[ZFAT_SR_DECI] = {FA_ADC_MEM_OFF + 0x1C, 0xFFFF, 0},
[ZFAT_SR_DECI] = {0x1C, 0x0000FFFF, 0},
/* Position address */
[ZFAT_POS] = {FA_ADC_MEM_OFF + 0x18, 0xFFFFFFFF, 0},
[ZFAT_POS] = {0x18, 0xFFFFFFFF, 0},
/* Pre-sample */
[ZFAT_PRE] = {FA_ADC_MEM_OFF + 0x20, 0xFFFFFFFF, 0},
[ZFAT_PRE] = {0x20, 0xFFFFFFFF, 0},
/* Post-sample */
[ZFAT_POST] = {FA_ADC_MEM_OFF + 0x24, 0xFFFFFFFF, 0},
[ZFAT_POST] = {0x24, 0xFFFFFFFF, 0},
/* Sample counter */
[ZFAT_CNT] = {FA_ADC_MEM_OFF + 0x28, 0xFFFFFFFF, 0},
[ZFAT_CNT] = {0x28, 0xFFFFFFFF, 0},
/* Channel 1 */
[ZFA_CH1_CTL_RANGE] = {FA_ADC_MEM_OFF + 0x2C, 0x0077, 0},
[ZFA_CH1_STA] = {FA_ADC_MEM_OFF + 0x30, 0xFFFF, 0},
[ZFA_CH1_GAIN] = {FA_ADC_MEM_OFF + 0x34, 0xFFFF, 0},
[ZFA_CH1_OFFSET] = {FA_ADC_MEM_OFF + 0x38, 0xFFFF, 0},
[ZFA_CH1_CTL_TERM] = {FA_ADC_MEM_OFF + 0x2C, 0x0001, 3},
[ZFA_CH1_CTL_RANGE] = {0x2C, 0x00000077, 1},
[ZFA_CH1_CTL_TERM] = {0x2C, 0x00000008, 1},
[ZFA_CH1_STA] = {0x30, 0x0000FFFF, 0},
[ZFA_CH1_GAIN] = {0x34, 0x0000FFFF, 0},
[ZFA_CH1_OFFSET] = {0x38, 0x0000FFFF, 0},
/* Channel 2 */
[ZFA_CH2_CTL_RANGE] = {FA_ADC_MEM_OFF + 0x3C, 0x0077, 0},
[ZFA_CH2_STA] = {FA_ADC_MEM_OFF + 0x40, 0xFFFF, 0},
[ZFA_CH2_GAIN] = {FA_ADC_MEM_OFF + 0x44, 0xFFFF, 0},
[ZFA_CH2_OFFSET] = {FA_ADC_MEM_OFF + 0x48, 0xFFFF, 0},
[ZFA_CH2_CTL_TERM] = {FA_ADC_MEM_OFF + 0x3C, 0x0001, 3},
[ZFA_CH2_CTL_RANGE] = {0x3C, 0x00000077, 1},
[ZFA_CH2_CTL_TERM] = {0x3C, 0x00000008, 1},
[ZFA_CH2_STA] = {0x40, 0x0000FFFF, 0},
[ZFA_CH2_GAIN] = {0x44, 0x0000FFFF, 0},
[ZFA_CH2_OFFSET] = {0x48, 0x0000FFFF, 0},
/* Channel 3 */
[ZFA_CH3_CTL_RANGE] = {FA_ADC_MEM_OFF + 0x4C, 0x0077, 0},
[ZFA_CH3_STA] = {FA_ADC_MEM_OFF + 0x50, 0xFFFF, 0},
[ZFA_CH3_GAIN] = {FA_ADC_MEM_OFF + 0x54, 0xFFFF, 0},
[ZFA_CH3_OFFSET] = {FA_ADC_MEM_OFF + 0x58, 0xFFFF, 0},
[ZFA_CH3_CTL_TERM] = {FA_ADC_MEM_OFF + 0x4C, 0x0001, 3},
[ZFA_CH3_CTL_RANGE] = {0x4C, 0x00000077, 1},
[ZFA_CH3_CTL_TERM] = {0x4C, 0x00000008, 1},
[ZFA_CH3_STA] = {0x50, 0x0000FFFF, 0},
[ZFA_CH3_GAIN] = {0x54, 0x0000FFFF, 0},
[ZFA_CH3_OFFSET] = {0x58, 0x0000FFFF, 0},
/* Channel 4 */
[ZFA_CH4_CTL_RANGE] = {FA_ADC_MEM_OFF + 0x5C, 0x0077, 0},
[ZFA_CH4_STA] = {FA_ADC_MEM_OFF + 0x60, 0xFFFF, 0},
[ZFA_CH4_GAIN] = {FA_ADC_MEM_OFF + 0x64, 0xFFFF, 0},
[ZFA_CH4_OFFSET] = {FA_ADC_MEM_OFF + 0x68, 0xFFFF, 0},
[ZFA_CH4_CTL_TERM] = {FA_ADC_MEM_OFF + 0x5C, 0x0001, 3},
/* DMA */
[ZFA_DMA_CTL_SWP] = {FA_DMA_MEM_OFF + 0x00, 0x0003, 2},
[ZFA_DMA_CTL_ABORT] = {FA_DMA_MEM_OFF + 0x00, 0x0001, 1},
[ZFA_DMA_CTL_START] = {FA_DMA_MEM_OFF + 0x00, 0x0001, 0},
[ZFA_DMA_STA] = {FA_DMA_MEM_OFF + 0x04, 0x0007, 0},
[ZFA_DMA_ADDR] = {FA_DMA_MEM_OFF + 0x08, 0xFFFFFFFF, 0},
[ZFA_DMA_ADDR_L] = {FA_DMA_MEM_OFF + 0x0C, 0xFFFFFFFF, 0},
[ZFA_DMA_ADDR_H] = {FA_DMA_MEM_OFF + 0x10, 0xFFFFFFFF, 0},
[ZFA_DMA_LEN] = {FA_DMA_MEM_OFF + 0x14, 0xFFFFFFFF, 0},
[ZFA_DMA_NEXT_L] = {FA_DMA_MEM_OFF + 0x18, 0xFFFFFFFF, 0},
[ZFA_DMA_NEXT_H] = {FA_DMA_MEM_OFF + 0x1C, 0xFFFFFFFF, 0},
[ZFA_DMA_BR_DIR] = {FA_DMA_MEM_OFF + 0x20, 0x0001, 1},
[ZFA_DMA_BR_LAST] = {FA_DMA_MEM_OFF + 0x20, 0x0001, 0},
[ZFA_CH4_CTL_RANGE] = {0x5C, 0x00000077, 1},
[ZFA_CH4_CTL_TERM] = {0x5C, 0x00000008, 1},
[ZFA_CH4_STA] = {0x60, 0x0000FFFF, 0},
[ZFA_CH4_GAIN] = {0x64, 0x0000FFFF, 0},
[ZFA_CH4_OFFSET] = {0x68, 0x0000FFFF, 0},
/* IRQ */
[ZFA_IRQ_MULTI] = {FA_IRQ_MEM_OFF + 0x00, 0x000F, 0},
[ZFA_IRQ_SRC] = {FA_IRQ_MEM_OFF + 0x04, 0x000F, 0},
[ZFA_IRQ_MASK] = {FA_IRQ_MEM_OFF + 0x08, 0x000F, 0},
[ZFA_IRQ_ADC_DISABLE_MASK] = {0x00, 0x00000003, 0},
[ZFA_IRQ_ADC_ENABLE_MASK] = {0x04, 0x00000003, 0},
[ZFA_IRQ_ADC_MASK_STATUS] = {0x08, 0x00000003, 0},
[ZFA_IRQ_ADC_SRC] = {0x0C, 0x00000003, 0},
[ZFA_IRQ_VIC_CTRL] = {0x00, 0x000FFFFF, 0},
[ZFA_IRQ_VIC_ENABLE_MASK] = {0x08, 0x00000003, 0},
[ZFA_IRQ_VIC_DISABLE_MASK] = {0x0C, 0x00000003, 0},
[ZFA_IRQ_VIC_MASK_STATUS] = {0x10, 0x00000003, 0},
/* UTC */
[ZFA_UTC_SECONDS] = {FA_UTC_MEM_OFF + 0x00, ~0x0, 0},
[ZFA_UTC_COARSE] = {FA_UTC_MEM_OFF + 0x04, ~0x0, 0},
[ZFA_UTC_TRIG_META] = {FA_UTC_MEM_OFF + 0x08, ~0x0, 0},
[ZFA_UTC_TRIG_SECONDS] = {FA_UTC_MEM_OFF + 0x0C, ~0x0, 0},
[ZFA_UTC_TRIG_COARSE] = {FA_UTC_MEM_OFF + 0x10, ~0x0, 0},
[ZFA_UTC_TRIG_FINE] = {FA_UTC_MEM_OFF + 0x14, ~0x0, 0},
[ZFA_UTC_ACQ_START_META] {FA_UTC_MEM_OFF + 0x18, ~0x0, 0},
[ZFA_UTC_ACQ_START_SECONDS] = {FA_UTC_MEM_OFF + 0x1C, ~0x0, 0},
[ZFA_UTC_ACQ_START_COARSE] = {FA_UTC_MEM_OFF + 0x20, ~0x0, 0},
[ZFA_UTC_ACQ_START_FINE] = {FA_UTC_MEM_OFF + 0x24, ~0x0, 0},
[ZFA_UTC_ACQ_STOP_META] = {FA_UTC_MEM_OFF + 0x28, ~0x0, 0},
[ZFA_UTC_ACQ_STOP_SECONDS] = {FA_UTC_MEM_OFF + 0x2C, ~0x0, 0},
[ZFA_UTC_ACQ_STOP_COARSE] = {FA_UTC_MEM_OFF + 0x30, ~0x0, 0},
[ZFA_UTC_ACQ_STOP_FINE] = {FA_UTC_MEM_OFF + 0x34, ~0x0, 0},
[ZFA_UTC_ACQ_END_META] = {FA_UTC_MEM_OFF + 0x38, ~0x0, 0},
[ZFA_UTC_ACQ_END_SECONDS] = {FA_UTC_MEM_OFF + 0x3C, ~0x0, 0},
[ZFA_UTC_ACQ_END_COARSE] = {FA_UTC_MEM_OFF + 0x40, ~0x0, 0},
[ZFA_UTC_ACQ_END_FINE] = {FA_UTC_MEM_OFF + 0x44, ~0x0, 0},
/* Carrier CSR */
[ZFA_CAR_FMC_PRES] = {FA_CAR_MEM_OFF + 0x04, 0x1, 0},
[ZFA_CAR_P2L_PLL] = {FA_CAR_MEM_OFF + 0x04, 0x1, 1},
[ZFA_CAR_SYS_PLL] = {FA_CAR_MEM_OFF + 0x04, 0x1, 2},
[ZFA_CAR_DDR_CAL] = {FA_CAR_MEM_OFF + 0x04, 0x1, 3},
[ZFA_UTC_SECONDS] = {0x00, ~0x0, 0},
[ZFA_UTC_COARSE] = {0x04, ~0x0, 0},
[ZFA_UTC_TRIG_META] = {0x08, ~0x0, 0},
[ZFA_UTC_TRIG_SECONDS] = {0x0C, ~0x0, 0},
[ZFA_UTC_TRIG_COARSE] = {0x10, ~0x0, 0},
[ZFA_UTC_TRIG_FINE] = {0x14, ~0x0, 0},
[ZFA_UTC_ACQ_START_META] = {0x18, ~0x0, 0},
[ZFA_UTC_ACQ_START_SECONDS] = {0x1C, ~0x0, 0},
[ZFA_UTC_ACQ_START_COARSE] = {0x20, ~0x0, 0},
[ZFA_UTC_ACQ_START_FINE] = {0x24, ~0x0, 0},
[ZFA_UTC_ACQ_STOP_META] = {0x28, ~0x0, 0},
[ZFA_UTC_ACQ_STOP_SECONDS] = {0x2C, ~0x0, 0},
[ZFA_UTC_ACQ_STOP_COARSE] = {0x30, ~0x0, 0},
[ZFA_UTC_ACQ_STOP_FINE] = {0x34, ~0x0, 0},
[ZFA_UTC_ACQ_END_META] = {0x38, ~0x0, 0},
[ZFA_UTC_ACQ_END_SECONDS] = {0x3C, ~0x0, 0},
[ZFA_UTC_ACQ_END_COARSE] = {0x40, ~0x0, 0},
[ZFA_UTC_ACQ_END_FINE] = {0x44, ~0x0, 0},
};
/*
* core-spec fmc-adc-100m14b driver
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@gmail.com>
* Copied from fine-delay
*
*/
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/fmc.h>
#include <linux/fmc-sdb.h>
#include "fmc-adc.h"
#include "fa-spec.h"
static char *fa_spec_get_gwname(void)
{
return FA_GATEWARE_SPEC;
}
static int fa_spec_init(struct fa_dev *fa)
{
struct device *msgdev = &fa->fmc->dev;
struct fa_spec_data *cdata;
uint32_t val;
fa->fa_carrier_csr_base = fmc_find_sdb_device(fa->fmc->sdb, 0xce42,
0x603, NULL);
cdata = kzalloc(sizeof(struct fa_spec_data), GFP_KERNEL);
if (!cdata)
return -ENOMEM;
/* SDB carrier specific */
cdata->fa_dma_base =
fmc_find_sdb_device(fa->fmc->sdb, 0xce42, 0x601, NULL);
cdata->fa_irq_dma_base =
fmc_find_sdb_device(fa->fmc->sdb, 0xce42, 0xd5735ab4, NULL);
dev_info(msgdev,
"Spec Base addrs: irq_dmma:0x%x, dma_ctrl:0x%x, csr:0x%x \n",
cdata->fa_irq_dma_base, cdata->fa_dma_base,
fa->fa_carrier_csr_base);
/* Wait 50ms, so device has time to calibrate */
mdelay(50);
/* set FMC0 in normal FMC operation */
fa_writel(fa, fa->fa_carrier_csr_base,
&fa_spec_regs[ZFA_CAR_FMC_RES], 1);
/* Verify that the FMC is plugged (0 is plugged) */
val = fa_readl(fa, fa->fa_carrier_csr_base,
&fa_spec_regs[ZFA_CAR_FMC_PRES]);
if (val) {
dev_err(msgdev, "No FCM ADC plugged\n");
return -ENODEV;
}
/* Verify that system PLL is locked (1 is calibrated) */
val = fa_readl(fa, fa->fa_carrier_csr_base,
&fa_spec_regs[ZFA_CAR_SYS_PLL]);
if (!val) {
dev_err(msgdev, "System PLL not locked\n");
return -ENODEV;
}
/* Verify that DDR3 calibration is done (1 is calibrated) */
val = fa_readl(fa, fa->fa_carrier_csr_base,
&fa_spec_regs[ZFA_CAR_DDR_CAL]);
if (!val) {
dev_err(msgdev, "DDR3 Calibration not done\n");
return -ENODEV;
}
/* Set DMA to transfer data from device to host */
fa_writel(fa, cdata->fa_dma_base,
&fa_spec_regs[ZFA_DMA_BR_DIR], 0);
/* register carrier data */
fa->carrier_data = cdata;
dev_info(msgdev, "spec::%s successfully executed\n", __func__);
return 0;
}
static int fa_spec_reset(struct fa_dev *fa)
{
// struct spec_dev *spec = fa->fmc->carrier_data;
dev_info(&fa->fmc->dev, "%s: resetting TDC core through Gennum.\n",
__func__);
return 0;
}
static void fa_spec_exit(struct fa_dev *fa)
{
kfree(fa->carrier_data);
}
/* Unfortunately, on the spec this is GPIO9, i.e. IRQ(1) */
static struct fmc_gpio fa_gpio_on[] = {
{
.gpio = FMC_GPIO_IRQ(0),
.mode = GPIOF_DIR_IN,
.irqmode = IRQF_TRIGGER_RISING,
}
};
static struct fmc_gpio fa_gpio_off[] = {
{
.gpio = FMC_GPIO_IRQ(0),
.mode = GPIOF_DIR_IN,
.irqmode = 0,
}
};
static int fa_spec_setup_irqs(struct fa_dev *fa)
{
struct fmc_device *fmc = fa->fmc;
struct fa_spec_data *spec_data = fa->carrier_data;
int err;
/* Request IRQ
* trick : vic needs the base address of teh core firing the irq
* It cannot provided throught irq_request() call therefore the trick
* is to set it by means of the field irq provided by the fmc device
*/
fmc->irq = spec_data->fa_irq_dma_base;
err = fmc->op->irq_request(fmc, fa_spec_irq_handler,
"fmc-adc-100m14b", 0);
if (err) {
dev_err(&fmc->dev, "can't request irq 0x%x (error %i)\n",
fmc->irq, err);
return err;
}
fmc->op->gpio_config(fmc, fa_gpio_on, ARRAY_SIZE(fa_gpio_on));
dev_info(&fmc->dev, "spec::%s successfully executed\n", __func__);
/* Add SPEC specific IRQ sources to listen */
fa->irq_src |= FA_IRQ_SRC_DMA;
return 0;
}
static int fa_spec_free_irqs(struct fa_dev *fa)
{
struct fmc_device *fmc = fa->fmc;
fmc->op->gpio_config(fmc, fa_gpio_off, ARRAY_SIZE(fa_gpio_off));
return 0;
}
static int fa_spec_enable_irqs(struct fa_dev *fa)
{
struct fa_spec_data *spec_data = fa->carrier_data;
fa_writel(fa, spec_data->fa_irq_dma_base,
&fa_spec_regs[ZFA_IRQ_DMA_ENABLE_MASK],
FA_SPEC_IRQ_DMA_ALL);
return 0;
}
static int fa_spec_disable_irqs(struct fa_dev *fa)
{
struct fa_spec_data *spec_data = fa->carrier_data;
fa_writel(fa, spec_data->fa_irq_dma_base,
&fa_spec_regs[ZFA_IRQ_DMA_DISABLE_MASK],
FA_SPEC_IRQ_DMA_NONE);
return 0;
}
static int fa_spec_ack_irq(struct fa_dev *fa, int irq_id)
{
return 0;
}
struct fa_carrier_op fa_spec_op = {
fa_spec_get_gwname,
fa_spec_init,
fa_spec_reset,
fa_spec_exit,
fa_spec_setup_irqs,
fa_spec_free_irqs,
fa_spec_enable_irqs,
fa_spec_disable_irqs,
fa_spec_ack_irq,
fa_spec_dma_start,
fa_spec_dma_done,
fa_spec_dma_error,
};
......@@ -21,7 +21,7 @@
#include <linux/zio-trigger.h>
#include "fmc-adc.h"
#include "fa-spec.h"
/*
* zfat_calculate_nents
......@@ -62,6 +62,7 @@ static void zfad_setup_dma_scatter(struct fa_dev *fa,
struct zfad_block *zfad_block,
unsigned int n_blocks)
{
struct fa_spec_data *spec_data = fa->carrier_data;
struct scatterlist *sg;
int bytesleft = 0;
void *bufp = NULL;
......@@ -72,7 +73,7 @@ static void zfad_setup_dma_scatter(struct fa_dev *fa,
zfad_block->block->datalen);
i_blk = 0;
for_each_sg(fa->sgt.sgl, sg, fa->sgt.nents, i) {
for_each_sg(spec_data->sgt.sgl, sg, spec_data->sgt.nents, i) {
if (i_blk < n_blocks && i == zfad_block[i_blk].first_nent) {
WARN(bytesleft, "unmapped byte in block %i\n",
i_blk - 1);
......@@ -125,10 +126,11 @@ static void zfad_setup_dma_scatter(struct fa_dev *fa,
* The DMA controller can store a single item, but more then one transfer
* could be necessary
*/
int zfad_map_dma(struct zio_cset *cset, struct zfad_block *zfad_block,
static int zfad_map_dma(struct zio_cset *cset, struct zfad_block *zfad_block,
unsigned int n_blocks)
{
struct fa_dev *fa = cset->zdev->priv_d;
struct fa_spec_data *spec_data = fa->carrier_data;
struct device *dev = &fa->fmc->dev;
struct scatterlist *sg;
struct fa_dma_item *items;
......@@ -147,30 +149,30 @@ int zfad_map_dma(struct zio_cset *cset, struct zfad_block *zfad_block,
pages, n_blocks);
/* Create sglists for the transfers */
err = sg_alloc_table(&fa->sgt, pages, GFP_ATOMIC);
err = sg_alloc_table(&spec_data->sgt, pages, GFP_ATOMIC);
if (err) {
dev_err(dev, "cannot allocate sg table (%i pages)\n", pages);
goto out;
}
/* Limited to 32-bit (kernel limit) */
size = sizeof(*items) * fa->sgt.nents;
size = sizeof(*items) * spec_data->sgt.nents;
items = kzalloc(size, GFP_ATOMIC);
if (!items) {
dev_err(fa->fmc->hwdev, "cannot allocate coherent dma memory\n");
goto out_mem;
}
fa->items = items;
fa->dma_list_item = dma_map_single(fa->fmc->hwdev, items, size,
spec_data->items = items;
spec_data->dma_list_item = dma_map_single(fa->fmc->hwdev, items, size,
DMA_TO_DEVICE);
if (!fa->dma_list_item) {
if (!spec_data->dma_list_item)
goto out_free;
}
/* Setup the scatter list for the provided block */
zfad_setup_dma_scatter(fa, zfad_block, n_blocks);
/* Map DMA buffers */
sglen = dma_map_sg(fa->fmc->hwdev, fa->sgt.sgl,
fa->sgt.nents, DMA_FROM_DEVICE);
sglen = dma_map_sg(fa->fmc->hwdev, spec_data->sgt.sgl,
spec_data->sgt.nents, DMA_FROM_DEVICE);
if (!sglen) {
dev_err(dev, "cannot map dma memory\n");
goto out_map;
......@@ -178,7 +180,7 @@ int zfad_map_dma(struct zio_cset *cset, struct zfad_block *zfad_block,
/* Configure DMA items */
i_blk = 0;
for_each_sg(fa->sgt.sgl, sg, fa->sgt.nents, i) {
for_each_sg(spec_data->sgt.sgl, sg, spec_data->sgt.nents, i) {
if (i_blk < n_blocks && i == zfad_block[i_blk].first_nent) {
/*
* FIXME if we trust our configuration, dev_mem_off is
......@@ -201,8 +203,8 @@ int zfad_map_dma(struct zio_cset *cset, struct zfad_block *zfad_block,
dev_mem_off += items[i].dma_len;
if (!sg_is_last(sg)) {/* more transfers */
/* uint64_t so it works on 32 and 64 bit */
tmp = fa->dma_list_item;
tmp += (sizeof(struct fa_dma_item) * ( i+ 1 ));
tmp = spec_data->dma_list_item;
tmp += (sizeof(struct fa_dma_item) * (i+ 1));
items[i].next_addr_l = ((uint64_t)tmp) & 0xFFFFFFFF;
items[i].next_addr_h = ((uint64_t)tmp) >> 32;
items[i].attribute = 0x1; /* more items */
......@@ -212,26 +214,33 @@ int zfad_map_dma(struct zio_cset *cset, struct zfad_block *zfad_block,
pr_debug("configure DMA item %d "
"(addr: 0x%llx len: %d)(dev off: 0x%x)"
"(next item: 0x%x)",
"(next item: 0x%x)\n",
i, (long long)sg_dma_address(sg),
sg_dma_len(sg), dev_mem_off, items[i].next_addr_l);
/* The first item is written on the device */
if (i == 0) {
zfa_hardware_write(fa, ZFA_DMA_ADDR,
fa_writel(fa, spec_data->fa_dma_base,
&fa_spec_regs[ZFA_DMA_ADDR],
items[i].start_addr);
zfa_hardware_write(fa, ZFA_DMA_ADDR_L,
fa_writel(fa, spec_data->fa_dma_base,
&fa_spec_regs[ZFA_DMA_ADDR_L],
items[i].dma_addr_l);
zfa_hardware_write(fa, ZFA_DMA_ADDR_H,
fa_writel(fa, spec_data->fa_dma_base,
&fa_spec_regs[ZFA_DMA_ADDR_H],
items[i].dma_addr_h);
zfa_hardware_write(fa, ZFA_DMA_LEN,
fa_writel(fa, spec_data->fa_dma_base,
&fa_spec_regs[ZFA_DMA_LEN],
items[i].dma_len);
zfa_hardware_write(fa, ZFA_DMA_NEXT_L,
fa_writel(fa, spec_data->fa_dma_base,
&fa_spec_regs[ZFA_DMA_NEXT_L],
items[i].next_addr_l);
zfa_hardware_write(fa, ZFA_DMA_NEXT_H,
fa_writel(fa, spec_data->fa_dma_base,
&fa_spec_regs[ZFA_DMA_NEXT_H],
items[i].next_addr_h);
/* Set that there is a next item */
zfa_hardware_write(fa, ZFA_DMA_BR_LAST,
fa_writel(fa, spec_data->fa_dma_base,
&fa_spec_regs[ZFA_DMA_BR_LAST],
items[i].attribute);
}
}
......@@ -239,17 +248,16 @@ int zfad_map_dma(struct zio_cset *cset, struct zfad_block *zfad_block,
return 0;
out_map:
dma_unmap_single(fa->fmc->hwdev, fa->dma_list_item, size,
dma_unmap_single(fa->fmc->hwdev, spec_data->dma_list_item, size,
DMA_TO_DEVICE);
out_free:
kfree(fa->items);
kfree(spec_data->items);
out_mem:
sg_free_table(&fa->sgt);
sg_free_table(&spec_data->sgt);
out:
return -ENOMEM;
}
/*
* zfad_unmap_dma
* @cset: channel set
......@@ -257,20 +265,58 @@ out:
*
* It unmaps a blocks
*/
void zfad_unmap_dma(struct zio_cset *cset, struct zfad_block *zfad_block)
static void zfad_unmap_dma(struct zio_cset *cset)
{
struct fa_dev *fa = cset->zdev->priv_d;
unsigned int size;
struct fa_spec_data *spec_data = fa->carrier_data;
dev_dbg(fa->fmc->hwdev, "unmap DMA\n");
size = sizeof(struct fa_dma_item) * fa->sgt.nents;
dma_unmap_single(fa->fmc->hwdev, fa->dma_list_item, size,
size = sizeof(struct fa_dma_item) * spec_data->sgt.nents;
dma_unmap_single(fa->fmc->hwdev, spec_data->dma_list_item, size,
DMA_TO_DEVICE);
dma_unmap_sg(fa->fmc->hwdev, fa->sgt.sgl, fa->sgt.nents,
dma_unmap_sg(fa->fmc->hwdev, spec_data->sgt.sgl, spec_data->sgt.nents,
DMA_FROM_DEVICE);
kfree(fa->items);
fa->items = NULL;
fa->dma_list_item = 0;
sg_free_table(&fa->sgt);
kfree(spec_data->items);
spec_data->items = NULL;
spec_data->dma_list_item = 0;
sg_free_table(&spec_data->sgt);
}
int fa_spec_dma_start(struct zio_cset *cset)
{
struct fa_dev *fa = cset->zdev->priv_d;
struct fa_spec_data *spec_data = fa->carrier_data;
struct zio_channel *interleave = cset->interleave;
struct zfad_block *zfad_block = interleave->priv_d;
int res;
res = zfad_map_dma(cset, zfad_block, fa->n_shots);
if (res)
return res;
/* Start DMA transfer */
fa_writel(fa, spec_data->fa_dma_base,
&fa_spec_regs[ZFA_DMA_CTL_START], 1);
return 0;
}
void fa_spec_dma_done(struct zio_cset *cset)
{
zfad_unmap_dma(cset);
}
void fa_spec_dma_error(struct zio_cset *cset)
{
struct fa_dev *fa = cset->zdev->priv_d;
struct fa_spec_data *spec_data = fa->carrier_data;
uint32_t val;
zfad_unmap_dma(cset);
val = fa_readl(fa, spec_data->fa_dma_base,
&fa_spec_regs[ZFA_DMA_STA]);
if (val)
dev_err(&fa->fmc->dev,
"DMA error (status 0x%x). All acquisition lost\n", val);
}
/*
* Copyright CERN 2012
* Author: Federico Vaga <federico.vaga@gmail.com>
*
* IRQ function handler
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/zio.h>
#include <linux/zio-trigger.h>
#include "fmc-adc.h"
#include "fa-spec.h"
/*
* fa_get_irq_status
* @fa: adc descriptor
* @irq_status: destination of irq status
* @irq_multi: destination of irq multi
*
* Get irq and clear the register. To clear an interrupt we have to write 1
* on the handled interrupt. We handle all interrupt so we clear all interrupts
*/
static void fa_get_irq_status(struct fa_dev *fa, int irq_core_base,
uint32_t *irq_status)
{
/* Get current interrupts status */
*irq_status = fa_readl(fa, irq_core_base,
&fa_spec_regs[ZFA_IRQ_DMA_SRC]);
dev_dbg(&fa->fmc->dev, "core DMA: 0x%x fired an interrupt. IRQ status register: 0x%x\n",
irq_core_base, *irq_status);
if (*irq_status)
/* Clear current interrupts status */
fa_writel(fa, irq_core_base,
&fa_spec_regs[ZFA_IRQ_DMA_SRC], *irq_status);
}
/*
* zfad_irq
* @irq:
* @ptr: pointer to fmc_device
*
* The ADC svec firmware fires interrupt from a single wishbone core
* and throught the VIC ACQ_END and TRIG events. Note about "TRIG"
* event: the main reason to listen this interrupt was to read the
* intermediate time stamps in case of multishots.
* With the new firmware (>=3.0) the stamps come with the data,
* therefore the driver doesn't have to listen "TRIG" event. This
* enhancement remove completely the risk of loosing interrupt in case
* of small number of samples and makes the retry loop in the hanlder
* obsolete.
*/
irqreturn_t fa_spec_irq_handler(int irq_core_base, void *ptr)
{
struct fmc_device *fmc = ptr;
struct fa_dev *fa = fmc_get_drvdata(fmc);
struct zio_cset *cset = fa->zdev->cset;
uint32_t status;
if (unlikely(fa->last_irq_core_src == irq_core_base)) {
WARN(1, "Cannot handle two consecutives %s interrupt."
"The ADC doesn't behave properly\n",
(irq_core_base == fa->fa_irq_adc_base) ? "ACQ" : "DMA");
/* Stop Acquisition, ADC it is not working properly */
zfad_fsm_command(fa, ZFA_STOP);
fa->last_irq_core_src = FA_SPEC_IRQ_SRC_NONE;
return IRQ_HANDLED;
}
/* irq to handle */
fa_get_irq_status(fa, irq_core_base, &status);
if (!status)
return IRQ_NONE;
dev_dbg(&fa->fmc->dev, "Handle ADC interrupts\n");
if (status & FA_SPEC_IRQ_DMA_DONE)
zfad_dma_done(cset);
else if (unlikely(status & FA_SPEC_IRQ_DMA_ERR))
zfad_dma_error(cset);
/* register the core which just fired the IRQ */
/* check proper sequence of IRQ in case of multi IRQ (ACQ + DMA)*/
fa->last_irq_core_src = irq_core_base;
/* ack the irq */
fa->fmc->op->irq_ack(fa->fmc);
return IRQ_HANDLED;
}
/*
* Copyright CERN 2012
* Author: Federico Vaga <federico.vaga@gmail.com>
*
* Table of register masks, used by driver functions
*/
#include "fa-spec.h"
/* Definition of the fa spec registers field: offset - mask - isbitfield */
const struct zfa_field_desc fa_spec_regs[] = {
/* Carrier CSR */
[ZFA_CAR_FMC_PRES] = {0x04, 0x1, 1},
[ZFA_CAR_P2L_PLL] = {0x04, 0x2, 1},
[ZFA_CAR_SYS_PLL] = {0x04, 0x4, 1},
[ZFA_CAR_DDR_CAL] = {0x04, 0x8, 1},
[ZFA_CAR_FMC_RES] = {0x0c, 0x1, 1},
/* IRQ */
[ZFA_IRQ_DMA_DISABLE_MASK] = {0x00, 0x00000003, 0},
[ZFA_IRQ_DMA_ENABLE_MASK] = {0x04, 0x00000003, 0},
[ZFA_IRQ_DMA_MASK_STATUS] = {0x08, 0x00000003, 0},
[ZFA_IRQ_DMA_SRC] = {0x0C, 0x00000003, 0},
/* DMA */
[ZFA_DMA_CTL_SWP] = {0x00, 0x0000000C, 1},
[ZFA_DMA_CTL_ABORT] = {0x00, 0x00000002, 1},
[ZFA_DMA_CTL_START] = {0x00, 0x00000001, 1},
[ZFA_DMA_STA] = {0x04, 0x00000007, 0},
[ZFA_DMA_ADDR] = {0x08, 0xFFFFFFFF, 0},
[ZFA_DMA_ADDR_L] = {0x0C, 0xFFFFFFFF, 0},
[ZFA_DMA_ADDR_H] = {0x10, 0xFFFFFFFF, 0},
[ZFA_DMA_LEN] = {0x14, 0xFFFFFFFF, 0},
[ZFA_DMA_NEXT_L] = {0x18, 0xFFFFFFFF, 0},
[ZFA_DMA_NEXT_H] = {0x1C, 0xFFFFFFFF, 0},
[ZFA_DMA_BR_DIR] = {0x20, 0x00000002, 1},
[ZFA_DMA_BR_LAST] = {0x20, 0x00000001, 1},
};
/*
* Copyright CERN 2012
*
* Header for the fmc-adc-100m14b spec driver
*
*/
#ifndef __FA_SPEC_CORE_H__
#define __FA_SPEC_CORE_H__
#include <linux/scatterlist.h>
#include <linux/irqreturn.h>
#include "fmc-adc.h"
#include "field-desc.h"
/* default spec gateware */
#define FA_GATEWARE_SPEC "fmc/spec-fmc-adc-100m14b.bin"
/* Should be replaced by an sdb query */
#define SPEC_FA_DMA_MEM_OFF 0x01000
/*
* fa_dma_item: The information about a DMA transfer
* @start_addr: pointer where start to retrieve data from device memory
* @dma_addr_l: low 32bit of the dma address on host memory
* @dma_addr_h: high 32bit of the dma address on host memory
* @dma_len: number of bytes to transfer from device to host
* @next_addr_l: low 32bit of the address of the next memory area to use
* @next_addr_h: high 32bit of the address of the next memory area to use
* @attribute: dma information about data transferm. At the moment it is used
* only to provide the "last item" bit, direction is fixed to
* device->host
*/
struct fa_dma_item {
uint32_t start_addr; /* 0x00 */
uint32_t dma_addr_l; /* 0x04 */
uint32_t dma_addr_h; /* 0x08 */
uint32_t dma_len; /* 0x0C */
uint32_t next_addr_l; /* 0x10 */
uint32_t next_addr_h; /* 0x14 */
uint32_t attribute; /* 0x18 */
uint32_t reserved; /* ouch */
};
/* SPEC CSR */
enum fa_spec_regs_id {
/* CSR */
ZFA_CAR_FMC_PRES,
ZFA_CAR_P2L_PLL,
ZFA_CAR_SYS_PLL,
ZFA_CAR_DDR_CAL,
ZFA_CAR_FMC_RES,
/* IRQ DMA: DMA spec specific irq controller */
ZFA_IRQ_DMA_DISABLE_MASK,
ZFA_IRQ_DMA_ENABLE_MASK,
ZFA_IRQ_DMA_MASK_STATUS,
ZFA_IRQ_DMA_SRC,
/* DMA */
ZFA_DMA_CTL_SWP,
ZFA_DMA_CTL_ABORT,
ZFA_DMA_CTL_START,
ZFA_DMA_STA,
ZFA_DMA_ADDR,
ZFA_DMA_ADDR_L,
ZFA_DMA_ADDR_H,
ZFA_DMA_LEN,
ZFA_DMA_NEXT_L,
ZFA_DMA_NEXT_H,
ZFA_DMA_BR_DIR,
ZFA_DMA_BR_LAST,
};
/* SPEC ADC have to listen two IRQ sources managed by two different cores */
#define FA_SPEC_IRQ_SRC_NONE 0
#define FA_SPEC_IRQ_SRC_ACQ 1
#define FA_SPEC_IRQ_SRC_DMA 2
/* DMA spec specific IRQ values */
enum fa_spec_irq {
FA_SPEC_IRQ_DMA_NONE = 0x0,
FA_SPEC_IRQ_DMA_DONE = 0x1,
FA_SPEC_IRQ_DMA_ERR = 0x2,
FA_SPEC_IRQ_DMA_ALL = 0x3,
};
/* specific carrier data */
struct fa_spec_data {
/* DMA attributes */
unsigned int fa_dma_base;
unsigned int fa_irq_dma_base;
struct sg_table sgt;
struct fa_dma_item *items;
dma_addr_t dma_list_item;
unsigned int n_dma_err; /* statistics */
};
/* spec specific hardware registers */
extern const struct zfa_field_desc fa_spec_regs[];
/* spec irq handler */
extern irqreturn_t fa_spec_irq_handler(int irq, void *dev_id);
/* functions exported by fa-dma.c */
extern int fa_spec_dma_start(struct zio_cset *cset);
extern void fa_spec_dma_done(struct zio_cset *cset);
extern void fa_spec_dma_error(struct zio_cset *cset);
#endif /* __FA_SPEC_CORE_H__*/
This diff is collapsed.
......@@ -36,7 +36,7 @@ static ZIO_ATTR_DEFINE_STD(ZIO_TRG, zfat_std_zattr) = {
/* Number of shots */
ZIO_ATTR(trig, ZIO_ATTR_TRIG_N_SHOTS, ZIO_RW_PERM, ZFAT_SHOTS_NB, 1),
ZIO_ATTR(trig, ZIO_ATTR_TRIG_PRE_SAMP, ZIO_RW_PERM, ZFAT_PRE, 0),
ZIO_ATTR(trig, ZIO_ATTR_TRIG_POST_SAMP, ZIO_RW_PERM, ZFAT_POST, 0),
ZIO_ATTR(trig, ZIO_ATTR_TRIG_POST_SAMP, ZIO_RW_PERM, ZFAT_POST, 1),
};
static struct zio_attribute zfat_ext_zattr[] = {
/* Config register */
......@@ -95,8 +95,29 @@ static int zfat_conf_set(struct device *dev, struct zio_attribute *zattr,
dev_err(dev, "nshots cannot be 0\n");
return -EINVAL;
}
case ZFAT_PRE:
if (tmp_val > 1) { /* multishot restricts samples count */
err = zfat_overflow_detection(ti, zattr->id, tmp_val);
if (err)
return err;
}
break;
case ZFAT_POST:
/*
* actually the HW adds systematically a sample
* corresponding to the trigger itself. therefore the
* client ask pre-samp+post-samp and the HW returns
* pre-samp+1+post-samp To make this behaviour
* invisible from the user we consider that the sample
* corresponding to the trigger is part of the post
* samples. Therefore the value written in the HW is
* post-samples-1 and value 0 is excluded
*/
if (!tmp_val) {
dev_err(dev, "post samples cannot be 0 (minimum 1)\n");
return -EINVAL;
}
tmp_val -= 1;
case ZFAT_PRE:
err = zfat_overflow_detection(ti, zattr->id, tmp_val);
if (err)
return err;
......@@ -120,7 +141,8 @@ static int zfat_conf_set(struct device *dev, struct zio_attribute *zattr,
break;
}
return zfa_hardware_write(fa, zattr->id, tmp_val);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[zattr->id], tmp_val);
return 0;
}
......@@ -133,7 +155,14 @@ static int zfat_info_get(struct device *dev, struct zio_attribute *zattr,
{
struct fa_dev *fa = get_zfadc(dev);
zfa_hardware_read(fa, zattr->id, usr_val);
*usr_val = fa_readl(fa, fa->fa_adc_csr_base, &zfad_regs[zattr->id]);
/*
* See in zfat-conf-set explanations (just above)
* Post sample value read from the HW is incrmented by 1
*/
if (zattr->id == ZFAT_POST)
*usr_val += 1;
return 0;
}
......@@ -162,9 +191,9 @@ static struct zio_ti *zfat_create(struct zio_trigger_type *trig,
return ERR_PTR(-ENOMEM);
/* Disable Software trigger*/
zfa_hardware_write(fa, ZFAT_CFG_SW_EN, 0);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_SW_EN], 0);
/* Enable Hardware trigger*/
zfa_hardware_write(fa, ZFAT_CFG_HW_EN, 1);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_HW_EN], 1);
zfat->fa = fa;
zfat->ti.cset = cset;
......@@ -178,15 +207,15 @@ static void zfat_destroy(struct zio_ti *ti)
struct zfat_instance *zfat = to_zfat_instance(ti);
/* Enable Software trigger */
zfa_hardware_write(fa, ZFAT_CFG_SW_EN, 1);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_SW_EN], 1);
/* Disable Hardware trigger */
zfa_hardware_write(fa, ZFAT_CFG_HW_EN, 0);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_HW_EN], 0);
/* Other triggers cannot use pre-samples */
zfa_hardware_write(fa, ZFAT_PRE, 0);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_PRE], 0);
/* Reset post samples */
zfa_hardware_write(fa, ZFAT_POST, 0);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_POST], 0);
/* Other triggers can handle only 1 shot */
zfa_hardware_write(fa, ZFAT_SHOTS_NB, 1);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_SHOTS_NB], 1);
kfree(zfat);
}
......@@ -201,7 +230,7 @@ static void zfat_change_status(struct zio_ti *ti, unsigned int status)
{
struct fa_dev *fa = ti->cset->zdev->priv_d;
zfa_hardware_write(fa, ZFAT_CFG_HW_EN, !status);
fa_writel(fa, fa->fa_adc_csr_base, &zfad_regs[ZFAT_CFG_HW_EN], !status);
}
/*
......@@ -228,7 +257,7 @@ static int zfat_data_done(struct zio_cset *cset)
return 0;
/* Store blocks */
for(i = 0; i < fa->n_shots; ++i)
for (i = 0; i < fa->n_shots; ++i)
if (likely(i < fa->n_fires)) {/* Store filled blocks */
dev_dbg(&fa->fmc->dev, "Store Block %i/%i\n",
i + 1, fa->n_shots);
......@@ -292,12 +321,25 @@ static int zfat_arm_trigger(struct zio_ti *ti)
interleave->priv_d = zfad_block;
/*
* Calculate the required size to store all channels.
* This is an interleaved acquisition, so nsamples represents the
* Calculate the required size to store all channels. This is
* an interleaved acquisition, so nsamples represents the
* number of sample on all channels (n_chan * chan_samples)
* Trig time stamp are appended after the post samples
* (4*32bits word) size should be 32bits word aligned
* ti->nsamples is the sum of (pre-samp+ post-samp)*4chan
* because it's the interleave channel.
*/
size = interleave->current_ctrl->ssize * ti->nsamples;
size = (interleave->current_ctrl->ssize * ti->nsamples)
+ FA_TRIG_TIMETAG_BYTES;
/* check if size is 32 bits word aligned: should be always the case */
if (size % 4) {
/* should never happen: increase the size accordling */
dev_warn(msgdev,
"\nzio data block size should 32bit word aligned."
"original size:%d was increased by %d bytes\n",
size, size%4);
size += size % 4;
}
dev_mem_off = 0;
/* Allocate ZIO blocks */
for (i = 0; i < fa->n_shots; ++i) {
......@@ -305,7 +347,7 @@ static int zfat_arm_trigger(struct zio_ti *ti)
block = zbuf->b_op->alloc_block(interleave->bi, size, gfp);
if (!block) {
dev_err(msgdev,
"arm trigger fail, cannot allocate block\n");
"\narm trigger fail, cannot allocate block\n");
err = -ENOMEM;
goto out_allocate;
}
......@@ -327,7 +369,7 @@ static int zfat_arm_trigger(struct zio_ti *ti)
return err;
out_allocate:
while((--i) >= 0)
while ((--i) >= 0)
zbuf->b_op->free_block(interleave->bi, zfad_block[i].block);
kfree(zfad_block);
interleave->priv_d = NULL;
......@@ -351,7 +393,7 @@ static void zfat_abort(struct zio_ti *ti)
dev_dbg(&fa->fmc->dev, "Aborting trigger\n");
/* Free all blocks */
for(i = 0; i < fa->n_shots; ++i)
for (i = 0; i < fa->n_shots; ++i)
bi->b_op->free_block(bi, zfad_block[i].block);
kfree(zfad_block);
cset->interleave->priv_d = NULL;
......
......@@ -8,35 +8,16 @@
#ifndef _FIELD_DESC_H_
#define _FIELD_DESC_H_
#include <linux/types.h>
/*
* zfa_field_desc is a field register descriptor. By using address, mask
* and shift the driver can describe every fields in registers.
*/
struct zfa_field_desc {
unsigned long addr; /* address of the register, or base offset */
unsigned long offset; /* related to its component base */
uint32_t mask; /* bit mask a register field */
uint32_t shift; /* shift of the mask into the register */
int is_bitfield; /* whether it maps full register or a field */
};
/*
* Get a field from a register value. You read a register from your device,
* then you use this function to get a filed defined with zfa_field_desc
* from the read value
*/
static inline uint32_t zfa_get_field(const struct zfa_field_desc *fld,
uint32_t fld_val)
{
return (fld_val & (fld->mask << fld->shift)) >> fld->shift;
}
/*
* Set a field to a register value. You read a register from your device,
* then you use this function to set a field defined with zfa_field_desc
* into the read value. Then you can write the register
*/
static inline uint32_t zfa_set_field(const struct zfa_field_desc *fld,
uint32_t fld_val, uint32_t usr_val)
{
return (fld_val & (~(fld->mask << fld->shift))) |
(usr_val << fld->shift);
}
#endif /* _FIELD_DESC_H_ */
This diff is collapsed.
/*
* Some utility functions not supported in the current version of fmc-bus.
*
* Copyright (C) 2012-2013 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#include <linux/fmc.h>
#include <linux/fmc-sdb.h>
#include <linux/err.h>
#include <asm/byteorder.h>
/* Finds index-th SDB device that matches (vid/did) pair. */
signed long fmc_find_sdb_device_ext(struct sdb_array *tree,
uint64_t vid, uint32_t did, int index,
unsigned long *sz)
{
signed long res = -ENODEV;
union sdb_record *r;
struct sdb_product *p;
struct sdb_component *c;
int i, n = tree->len;
uint64_t last, first;
int ci = 0;
/* FIXME: what if the first interconnect is not at zero? */
for (i = 0; i < n; i++) {
r = &tree->record[i];
c = &r->dev.sdb_component;
p = &c->product;
if (!IS_ERR(tree->subtree[i]))
res = fmc_find_sdb_device(tree->subtree[i],
vid, did, sz);
/* FIXME: this index SHOULD be recursive, too */
if (ci == index && res >= 0)
return res + tree->baseaddr;
if (r->empty.record_type != sdb_type_device)
continue;
if (__be64_to_cpu(p->vendor_id) != vid)
continue;
if (__be32_to_cpu(p->device_id) != did)
continue;
/* found */
last = __be64_to_cpu(c->addr_last);
first = __be64_to_cpu(c->addr_first);
if (sz)
*sz = (typeof(*sz)) (last + 1 - first);
if (ci == index)
return first + tree->baseaddr;
ci++;
}
return res;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment