Commit fb8fb72a authored by Federico Vaga's avatar Federico Vaga

FMC ADC first commit

Signed-off-by: 's avatarFederico Vaga <federico.vaga@gmail.com>
parent c5b2dedd
*~
.*cmd
.*ersions
*.o
*.ko
*.order
*.symvers
*.mod.c
*.pdf
*.toc
*.aux
*.log
*.lof
*.bib
*.bbl
*.blg
*.dvi
*.out
\#*\#
*.sh
# eclipse files
.pydevproject
.cproject
.project
.settings
LINUX ?= /lib/modules/$(shell uname -r)/build
ZIO ?= $(HOME)/zio
SPEC_SW ?= $(HOME)/spec-sw
KBUILD_EXTRA_SYMBOLS := $(ZIO)/Module.symvers $(SPEC_SW)/kernel/Module.symvers
ccflags-y = -I$(ZIO)/include -I$(SPEC_SW)/kernel -I$M
ccflags-y += -DDEBUG # temporary
subdirs-ccflags-y = $(ccflags-y)
obj-m := spec-fmc-adc.o
spec-fmc-adc-objs = fa-zio-drv.o
spec-fmc-adc-objs += fa-core.o
spec-fmc-adc-objs += fa-spec.o
spec-fmc-adc-objs += fa-zio-trg.o
all: modules
modules_install clean modules:
$(MAKE) -C $(LINUX) M=$(shell /bin/pwd) $@
/*
* core fmc-adc driver
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@gmail.com>
* Copied from fine-delay fd-core.c
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include "spec.h"
#include "fmc-adc.h"
int fa_probe(struct spec_dev *spec)
{
struct spec_fa *fa;
int err;
pr_info("%s:%d\n", __func__, __LINE__);
fa = kzalloc(sizeof(struct spec_fa), GFP_KERNEL);
if (!fa)
return -ENOMEM;
spec->sub_priv = fa;
fa->spec = spec;
fa->base = spec->remap[0];
/* Initliaze sub-system (FIXME only ZIO at the moment) */
err = fa_zio_init(fa);
if (err) {
kfree(fa);
return err;
}
return 0;
}
void fa_remove(struct spec_dev *dev)
{
fa_zio_exit(dev->sub_priv);
kfree(dev->sub_priv);
}
static int fa_init(void)
{
int ret;
pr_debug("%s\n",__func__);
ret = fa_zio_register();
if (ret < 0)
return ret;
ret = fa_spec_init();
if (ret < 0) {
fa_zio_unregister();
return ret;
}
return 0;
}
static void fa_exit(void)
{
fa_spec_exit();
fa_zio_unregister();
}
module_init(fa_init);
module_exit(fa_exit);
MODULE_AUTHOR("Federico Vaga");
MODULE_DESCRIPTION("FMC-ADC Linux Driver");
MODULE_LICENSE("GPL");
/*
* SPEC interface for the fmc-adc driver
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@gmail.com>
* Copied from fine-delay fd-spec.c
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/io.h>
#include "spec.h"
#include "fmc-adc.h"
static int fa_is_valid(int bus, int devfn)
{
/* FIXME: restrict to some of the spec devices with moduleparam */
pr_debug("%s: %x %x\n", __func__, bus, devfn);
return 1;
}
int fa_spec_init(void)
{
struct spec_dev *dev;
int ret, success = 0, retsave = 0, err = 0;
/* Scan the list and see what is there. Take hold of everything */
list_for_each_entry(dev, &spec_list, list) {
if (!fa_is_valid(dev->pdev->bus->number, dev->pdev->devfn))
continue;
pr_debug("%s: init %04x:%04x (%pR - %p)\n", __func__,
dev->pdev->bus->number, dev->pdev->devfn,
dev->area[0], dev->remap[0]);
ret = fa_probe(dev);
if (ret < 0) {
retsave = ret;
err++;
} else {
success++;
}
}
if (err) {
pr_err("%s: Setup of %i boards failed (%i succeeded)\n",
KBUILD_MODNAME, err, success);
pr_err("%s: last error: %i\n", KBUILD_MODNAME, retsave);
}
if (success) {
/* At least one board has been successfully initialized */
return 0;
}
return retsave; /* last error code */
}
void fa_spec_exit(void)
{
struct spec_dev *dev;
list_for_each_entry(dev, &spec_list, list) {
if (!fa_is_valid(dev->pdev->bus->number, dev->pdev->devfn))
continue;
pr_debug("%s: release %04x:%04x (%pR - %p)\n", __func__,
dev->pdev->bus->number, dev->pdev->devfn,
dev->area[0], dev->remap[0]);
fa_remove(dev);
}
}
/*
* Copyright CERN 2012
* Author: Federico Vaga <federico.vaga@gmail.com>
*
* Driver for the mezzanine ADC for the SPEC
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/list.h>
#include <linux/zio.h>
#include <linux/zio-buffer.h>
#include <linux/zio-trigger.h>
#include "spec.h"
#include "fmc-adc.h"
/* Definition of the fmc-adc registers address - mask - mask offset */
const struct zio_reg_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},
/* 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},
/* Trigger */
/* Config register */
[ZFDAC_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},
/* Delay */
[ZFAT_DLY] = {FA_ADC_MEM_OFF + 0x0C, 0xFFFFFFFF, 0},
/* Software */
[ZFAT_SW] = {FA_ADC_MEM_OFF + 0x10, 0xFFFFFFFF, 0},
/* Number of shots */
[ZFAT_SHOTS_NB] = {FA_ADC_MEM_OFF + 0x14, 0x0000FFFF, 0},
/* Sample rate */
[ZFAT_SR_DECI] = {FA_ADC_MEM_OFF + 0x1C, 0xFFFF, 0},
/* Position address */
[ZFAT_POS] = {FA_ADC_MEM_OFF + 0x18, 0xFFFFFFFF, 0},
/* Pre-sample */
[ZFAT_PRE] = {FA_ADC_MEM_OFF + 0x20, 0xFFFFFFFF, 0},
/* Post-sample */
[ZFAT_POST] = {FA_ADC_MEM_OFF + 0x24, 0xFFFFFFFF, 0},
/* Sample counter */
[ZFAT_CNT] = {FA_ADC_MEM_OFF + 0x28, 0xFFFFFFFF, 0},
/* Channel 1 */
[ZFA_CH1_CTL_RANGE] = {FA_ADC_MEM_OFF + 0x2C, 0x007B, 0},
[ZFA_CH1_CTL_TERM] = {FA_ADC_MEM_OFF + 0x2C, 0x0001, 3},
[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},
/* Channel 2 */
[ZFA_CH2_CTL_RANGE] = {FA_ADC_MEM_OFF + 0x3C, 0x007B, 0},
[ZFA_CH2_CTL_TERM] = {FA_ADC_MEM_OFF + 0x3C, 0x0001, 3},
[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},
/* Channel 3 */
[ZFA_CH3_CTL_RANGE] = {FA_ADC_MEM_OFF + 0x4C, 0x007B, 0},
[ZFA_CH3_CTL_TERM] = {FA_ADC_MEM_OFF + 0x4C, 0x0001, 3},
[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},
/* Channel 4 */
[ZFA_CH4_CTL_RANGE] = {FA_ADC_MEM_OFF + 0x5C, 0x007B, 0},
[ZFA_CH4_CTL_TERM] = {FA_ADC_MEM_OFF + 0x5C, 0x0001, 3},
[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},
/* 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},
/* 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}
};
/* zio device attributes */
static DEFINE_ZATTR_STD(ZDEV, zfad_dev_std_zattr) = {
ZATTR_REG(zdev, ZATTR_NBITS, S_IRUGO, ZFA_SW_R_NOADDRES, 14),
/* Sample rate */
ZATTR_REG(trig, ZATTR_MAXRATE, S_IRUGO | S_IWUGO, ZFAT_SR_DECI, 0),
};
static struct zio_attribute zfad_dev_ext_zattr[] = {
/* Control register */
/*
* State machine commands
* 1: start
* 2: stop
*/
PARAM_EXT_REG("fsm_cmd", S_IRUGO | S_IWUGO, ZFA_CTL_FMS_CMD, 0),
ZATTR_EXT_REG("fmc_clk_en", S_IRUGO | S_IWUGO, ZFA_CTL_CLK_EN, 0),
ZATTR_EXT_REG("offset_dac_clr_n", S_IRUGO | S_IWUGO, ZFA_CTL_DAC_CLR_N, 0),
ZATTR_EXT_REG("bitslip", S_IRUGO | S_IWUGO, ZFA_CTL_BSLIP, 0),
ZATTR_EXT_REG("test_data_en", S_IRUGO | S_IWUGO, ZFA_CTL_TEST_DATA_EN, 0),
PARAM_EXT_REG("trig_led", S_IRUGO | S_IWUGO, ZFA_CTL_TRIG_LED, 0),
PARAM_EXT_REG("acq_led", S_IRUGO | S_IWUGO, ZFA_CTL_ACQ_LED, 0),
/* Status register */
/*
* fsm - status of the state machine:
* 1: IDLE
* 2: PRE_TRIG
* 3: WAIT_TRIG
* 4: POST_TRIG
* 5: DECR_SHOT
* 7: Illegal
* */
PARAM_EXT_REG("fsm", S_IRUGO, ZFA_STA_FSM, 0),
PARAM_EXT_REG("serdes_pll", S_IRUGO, ZFA_STA_SERDES_PLL, 0),
PARAM_EXT_REG("serdes_synced", S_IRUGO, ZFA_STA_SERDES_SYNCED, 0),
};
static DEFINE_ZATTR_STD(ZDEV, zfad_chan_std_zattr) = {
ZATTR_REG(zdev, ZATTR_GAIN, S_IRUGO | S_IWUGO, ZFA_CHx_GAIN, 0x30),
ZATTR_REG(zdev, ZATTR_OFFSET, S_IRUGO | S_IWUGO, ZFA_CHx_OFFSET, 14),
};
static struct zio_attribute zfad_chan_ext_zattr[] = {
ZATTR_EXT_REG("in_range", S_IRUGO, ZFA_CHx_CTL_RANGE, 0),
ZATTR_EXT_REG("termination_en", S_IRUGO, ZFA_CHx_CTL_TERM, 0),
PARAM_EXT_REG("current_value", S_IRUGO, ZFA_CHx_STA, 0),
};
/* set a value to a FMC-ADC registers */
static int zfad_conf_set(struct device *dev, struct zio_attribute *zattr,
uint32_t usr_val)
{
const struct zio_reg_desc *reg;
int i, chan_index;
switch (zattr->priv.addr) {
case ZFA_CHx_CTL_RANGE:
if (usr_val != 0x23 || usr_val != 0x11 ||
usr_val != 0x45 || usr_val != 0x00) {
dev_err(dev, " in_range valid value: 0 17 35 69");
return -EINVAL;
}
case ZFA_CHx_CTL_TERM:
case ZFA_CHx_GAIN:
case ZFA_CHx_OFFSET:
chan_index = to_zio_chan(dev)->index;
i = zattr->priv.addr + ZFA_CHx_MULT * (chan_index -4);
reg = &zfad_regs[ZFA_CH1_OFFSET + i];
break;
default:
reg = &zfad_regs[zattr->priv.addr];
}
return zfa_common_conf_set(dev, reg, usr_val);
}
/* get the value of a FMC-ADC register */
static int zfad_info_get(struct device *dev, struct zio_attribute *zattr,
uint32_t *usr_val)
{
const struct zio_reg_desc *reg;
int i, chan_index;
switch (zattr->priv.addr) {
case ZFA_CHx_CTL_RANGE:
case ZFA_CHx_CTL_TERM:
case ZFA_CHx_GAIN:
case ZFA_CHx_OFFSET:
chan_index = to_zio_chan(dev)->index;
i = zattr->priv.addr + ZFA_CHx_MULT * (chan_index -4);
reg = &zfad_regs[ZFA_CH1_OFFSET + i];
break;
default:
reg = &zfad_regs[zattr->priv.addr];
}
zfa_common_info_get(dev, reg, usr_val);
return 0;
}
static const struct zio_sysfs_operations zfad_s_op = {
.conf_set = zfad_conf_set,
.info_get = zfad_info_get,
};
/*
* Prepare the FMC-ADC for the DMA transfer. FMC-ADC fire the hardware trigger,
* it acquires all samples in its DDR memory and then it allows the driver to
* transfer data through DMA. So zfad_input_cset only configure and start the
* DMA transfer.
*/
static int zfad_input_cset(struct zio_cset *cset)
{
/* ZIO should configure only the interleaved channel */
if (!cset->interleave)
return -EINVAL;
/* FIXME configure DMA */
/* Start DMA transefer */
zfa_common_conf_set(&cset->head.dev, &zfad_regs[ZFA_DMA_CTL_START], 1);
/* FIXME DMA start autoclear is there? */
return 0; /* data_done on DMA_DONE interrupt */
}
static int zfad_zio_probe(struct zio_device *zdev)
{
struct spec_fa *fa = zdev->priv_d;
const struct zio_reg_desc *reg;
int i;
/* Initialize channels range FIXME*/
/* Initialize channels gain to 1*/
for (i = 0; i < 4; ++i) {
reg = &zfad_regs[ZFA_CH1_GAIN + (i*4)];
fa_write_reg(0x8000, fa, reg);
}
/* Enable mezzanine clock and offset DACs */
zfa_common_conf_set(&zdev->head.dev, &zfad_regs[ZFA_CTL_CLK_EN], 1);
zfa_common_conf_set(&zdev->head.dev, &zfad_regs[ZFA_CTL_DAC_CLR_N], 1);
/* Set DMA to transfer data from device to host */
zfa_common_conf_set(&zdev->head.dev, &zfad_regs[ZFA_DMA_BR_DIR], 0);
/* Disable all interrupt */
zfa_common_conf_set(&zdev->head.dev, &zfad_regs[ZFA_IRQ_MASK],
ZFAT_NONE);
return 0;
}
/* Device description */
static struct zio_channel zfad_chan_tmpl = {
.zattr_set = {
.std_zattr = zfad_chan_std_zattr,
.ext_zattr = zfad_chan_ext_zattr,
.n_ext_attr = ARRAY_SIZE(zfad_chan_ext_zattr),
},
};
static struct zio_cset zfad_cset[] = {
{
.raw_io = zfad_input_cset,
.ssize = 2,
.n_chan = 4,
.chan_template = &zfad_chan_tmpl,
.flags = ZCSET_TYPE_ANALOG | /* is analog */
ZIO_DIR_INPUT | /* is input */
ZCSET_INTERLEAVE_ONLY,/* interleave only */
}
};
static struct zio_device zfad_tmpl = {
.owner = THIS_MODULE,
.s_op = &zfad_s_op,
.flags = 0,
.cset = zfad_cset,
.n_cset = ARRAY_SIZE(zfad_cset),
.zattr_set = {
.std_zattr = zfad_dev_std_zattr,
.ext_zattr = zfad_dev_ext_zattr,
.n_ext_attr = ARRAY_SIZE(zfad_dev_ext_zattr),
},
/* This driver work only with the fmc-adc-trig */
.preferred_trigger = "fmc-adc-trig",
};
static const struct zio_device_id zfad_table[] = {
{"fmc-adc", &zfad_tmpl},
{},
};
static struct zio_driver fa_zdrv = {
.driver = {
.name = "fmc-adc",
.owner = THIS_MODULE,
},
.id_table = zfad_table,
.probe = zfad_zio_probe,
};
/* Register and unregister are used to set up the template driver */
int fa_zio_register(void)
{
return zio_register_driver(&fa_zdrv);
}
void fa_zio_unregister(void)
{
zio_unregister_driver(&fa_zdrv);
}
/* Init and exit are called for each FMC-ADC card we have */
int fa_zio_init(struct spec_fa *fa)
{
struct pci_dev *pdev;
uint32_t dev_id;
int err;
pr_info("%s:%d\n", __func__, __LINE__);
/* Allocate the hardware zio_device for registration */
fa->hwzdev = zio_allocate_device();
if (IS_ERR(fa->hwzdev))
return PTR_ERR(fa->hwzdev);
/* Mandatory fields */
fa->hwzdev->owner = THIS_MODULE;
fa->hwzdev->priv_d = fa;
/* Our dev_id is bus+devfn*/
pdev = fa->spec->pdev;
dev_id = (pdev->bus->number << 8) | pdev->devfn;
/* Register our trigger hardware */
err = zio_register_trig(&zfat_type, "fmc-adc-trig");
if (err)
goto out_trg;
/* Register our device */
err = zio_register_device(fa->hwzdev, "fmc-adc", dev_id);
if (err)
goto out_dev;
return 0;
out_dev:
zio_unregister_device(fa->hwzdev);
out_trg:
zio_free_device(fa->hwzdev);
return err;
}
void fa_zio_exit(struct spec_fa *fa)
{
zio_unregister_device(fa->hwzdev);
zio_free_device(fa->hwzdev);
zio_unregister_trig(&zfat_type);
}
/*
* Copyright CERN 2012, author Federico Vaga
*
* Trigger for the driver of the mezzanine ADC
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/zio.h>
#include <linux/zio-sysfs.h>
#include <linux/zio-buffer.h>
#include <linux/zio-trigger.h>
#include "spec.h"
#include "fmc-adc.h"
struct zfat_instance {
struct zio_ti ti;
struct spec_fa *fa;
unsigned int n_acq_dev; /* number of acquisitions on device memory */
unsigned int n_err; /* number of errors */
};
#define to_zfat_instance(_ti) container_of(_ti, struct zfat_instance, ti)
/* Device registers */
extern const struct zio_reg_desc zfad_regs[];
/* zio trigger attributes */
static DEFINE_ZATTR_STD(TRIG, zfat_std_zattr) = {
/* Number of shots */
ZATTR_REG(trig, ZATTR_TRIG_REENABLE, S_IRUGO | S_IWUGO, ZFAT_SHOTS_NB, 0),
/*
* The ADC has pre-sample and post-sample configuration. NSAMPLES doesn't
* apply in this case, so it is a read-only attribute update with the
* value calculated pre-sample + post-sample.
* If read from device, it contains the numer of samples acquired in a
* particular moment
*/
ZATTR_REG(trig, ZATTR_TRIG_NSAMPLES, S_IRUGO, ZFAT_CNT, 0),
};
static struct zio_attribute zfat_ext_zattr[] = {
/* Config register */
/* Hardware trigger selction
* 0: internal (data threshold)
* 1: external (front panel trigger input)
*/
ZATTR_EXT_REG("hw_select", S_IRUGO | S_IWUGO, ZFAT_CFG_INT_SEL, 0),
/*
* Hardware trigger polarity
* 0: positive edge/slope
* 1: negative edge/slope
*/
ZATTR_EXT_REG("polarity", S_IRUGO | S_IWUGO, ZFAT_CFG_HW_POL, 0),
/* Enable (1) or disable (0) hardware trigger */
ZATTR_EXT_REG("hw_trig_enable", S_IRUGO | S_IWUGO, ZFAT_CFG_HW_EN, 0),
/* Enable (1) or disable (0) software trigger */
ZATTR_EXT_REG("sw_trig_enable", S_IRUGO | S_IWUGO, ZFAT_CFG_SW_EN, 0),
/*
* Channel selection for internal trigger
* 0: channel 1
* 1: channel 2
* 2: channel 3
* 3: channel 4
*/
ZATTR_EXT_REG("int_select", S_IRUGO | S_IWUGO, ZFAT_CFG_INT_SEL, 0),
/* Threshold value for internal trigger */
ZATTR_EXT_REG("int_threshold", S_IRUGO | S_IWUGO, ZFAT_CFG_THRES, 0),
/* Delay */
ZATTR_EXT_REG("delay", S_IRUGO | S_IWUGO, ZFAT_DLY, 0),
/* Software */
PARAM_EXT_REG("sw_fire", S_IWUGO, ZFAT_SW, 0),
/* Position address */
ZATTR_EXT_REG("position_addr", S_IRUGO, ZFAT_POS, 0),
/* Pre-sample*/
ZATTR_EXT_REG("pre-sample", S_IRUGO | S_IWUGO, ZFAT_PRE, 0),
/* Post-sample*/
ZATTR_EXT_REG("post-sample", S_IRUGO | S_IWUGO, ZFAT_POST, 0),
};
/* FIXME: zio need an update, introduce PRE and POST and replace NSAMPLES*/
static void zfat_update_nsample(struct zio_ti *ti, enum zfadc_dregs_enum type,
uint32_t val)
{
struct zio_attribute_set *zattr_set = &ti->zattr_set;
struct zio_attribute *n = &zattr_set->std_zattr[ZATTR_TRIG_NSAMPLES];
/* 9-th attribute is PRE */
struct zio_attribute *pre = &zattr_set->ext_zattr[9];
/* 10-th attribute is POST */
struct zio_attribute *post = &zattr_set->ext_zattr[10];
if (type == ZFAT_PRE)
n->value = val + post->value;
else if (type == ZFAT_POST)
n->value = pre->value + val;
/*
* FIXME n->value must update current_control. Make propagete_value
* a public function?
*/
}
/* set a value to a FMC-ADC trigger register */
static int zfat_conf_set(struct device *dev, struct zio_attribute *zattr,
uint32_t usr_val)
{
const struct zio_reg_desc *reg = &zfad_regs[zattr->priv.addr];
struct zio_ti *ti = to_zio_ti(dev);
int err;
switch (zattr->priv.addr) {
case ZFAT_SW:
/* Fire if software trigger is enabled */
if (!ti->zattr_set.ext_zattr[3].value) {
dev_err(dev, "sw trigger must be enable");
return -EPERM;
}
/* Abort current acquisition if any */
err = zfa_common_conf_set(dev,
&zfad_regs[ZFA_CTL_FMS_CMD], 2);
if (err)
return err;
zio_trigger_abort(ti->cset);
/* Start a new acquisition */
zio_fire_trigger(ti);
break;
}
err = zfa_common_conf_set(dev, reg, usr_val);
if (err)
return err;
if ( zattr->priv.addr == ZFAT_PRE || zattr->priv.addr == ZFAT_POST) {
zfat_update_nsample(to_zio_ti(dev), zattr->priv.addr, usr_val);
}
return 0;
}
/* get the value of a FMC-ADC trigger register */
static int zfat_info_get(struct device *dev, struct zio_attribute *zattr,
uint32_t *usr_val)
{
zfa_common_info_get(dev, &zfad_regs[zattr->priv.addr], usr_val);
return 0;
}
static const struct zio_sysfs_operations zfat_s_op = {
.conf_set = zfat_conf_set,
.info_get = zfat_info_get,
};
irqreturn_t zfadc_irq(int irq, void *ptr)
{
struct zfat_instance *zfat = ptr;
uint32_t irq_status = 0, val;
zfa_common_info_get(&zfat->ti.cset->head.dev,
&zfad_regs[ZFA_IRQ_SRC],&irq_status);
dev_dbg(&zfat->ti.head.dev, "irq status = 0x%x\n", irq_status);
if (irq_status & (ZFAT_DMA_DONE | ZFAT_DMA_ERR)) {
if (irq_status & ZFAT_DMA_DONE) { /* DMA done*/
zio_trigger_data_done(zfat->ti.cset);
zfat->n_acq_dev--;
} else { /* DMA error */
zio_trigger_abort(zfat->ti.cset);
zfat->n_err++;
}
/* Enable all triggers */
zfa_common_conf_set(&zfat->ti.cset->head.dev,
&zfad_regs[ZFAT_CFG_SW_EN], 0);
zfa_common_conf_set(&zfat->ti.cset->head.dev,
&zfad_regs[ZFAT_CFG_HW_EN], 0);
/* Start state machine */
zfa_common_conf_set(&zfat->ti.cset->head.dev,
&zfad_regs[ZFA_CTL_FMS_CMD], 2);
}
if (irq_status & ZFAT_TRG_FIRE) { /* Trigger fire */
/*
* FIXME: I think we don't care about this because ZIO fires
* a fake trigger before DMA
*/
zfat->n_acq_dev++;
}
if (irq_status & ZFAT_ACQ_END) { /* Acquisition end */
/*
* We fire the trigger at the and of the acquisition because
* FMC-ADC allow DMA only when acquisition end and the
* state machine is in the idle status. When the real trigger
* fire the state machine is not idle. This has not any
* influence on the ZIO side: we are interested to DMA data
*/
zfa_common_info_get(&zfat->ti.cset->head.dev,
&zfad_regs[ZFA_STA_FSM],&val);
if (val == ZFA_STATE_IDLE) {
/* Stop state machine */
zfa_common_conf_set(&zfat->ti.cset->head.dev,
&zfad_regs[ZFA_CTL_FMS_CMD], 2);
/* Disable all triggers */
zfa_common_conf_set(&zfat->ti.cset->head.dev,
&zfad_regs[ZFAT_CFG_HW_EN], 0);
zfa_common_conf_set(&zfat->ti.cset->head.dev,
&zfad_regs[ZFAT_CFG_SW_EN], 0);
/* Fire ZIO trigger so it will DMA */
zio_fire_trigger(&zfat->ti);
} else {
/* we can't DMA if the state machine is not idle */
dev_warn(&zfat->ti.cset->head.dev,
"Can't start DMA on the last acquisition\n");
}
}
return IRQ_HANDLED;
}
/* create an instance of the FMC-ADC trigger */
static struct zio_ti *zfat_create(struct zio_trigger_type *trig,
struct zio_cset *cset,
struct zio_control *ctrl, fmode_t flags)
{
struct spec_dev *spec = cset->zdev->priv_d;
struct zfat_instance *zfat;
int err;
if (!spec) {
dev_err(&cset->head.dev, "no spec device defined\n");
return ERR_PTR(-ENODEV);
}
zfat = kzalloc(sizeof(struct zfat_instance), GFP_KERNEL);
if (!zfat)
return ERR_PTR(-ENOMEM);
zfat->fa = spec->sub_priv;
err = request_irq(spec->pdev->irq, zfadc_irq, IRQF_SHARED, "wr-nic", zfat);
if (err) {
dev_err(&spec->pdev->dev, "can't request irq %i (err %i)\n",
spec->pdev->irq, err);
return ERR_PTR(err);
}
/* Enable all interrupt */
zfa_common_conf_set(&cset->head.dev, &zfad_regs[ZFA_IRQ_MASK],
ZFAT_ALL);
return &zfat->ti;
}
static void zfat_destroy(struct zio_ti *ti)
{
/* Disable all interrupt */
zfa_common_conf_set(&ti->cset->head.dev, &zfad_regs[ZFA_IRQ_MASK],
ZFAT_NONE);
kfree(to_zfat_instance(ti));
}
/* status is active low on ZIO but active high on the FMC-ADC */
static void zfat_change_status(struct zio_ti *ti, unsigned int status)
{
/* Enable/Disable HW trigger */
zfa_common_conf_set(&ti->head.dev, &zfad_regs[ZFAT_CFG_HW_EN], !status);
/* Enable/Disable SW trigger */
zfa_common_conf_set(&ti->head.dev, &zfad_regs[ZFAT_CFG_SW_EN], !status);
}
/*
* Abort depends on the state machine status and DMA status.*/
static void zfat_abort(struct zio_cset *cset)
{
}
static const struct zio_trigger_operations zfat_ops = {
.create = zfat_create,
.destroy = zfat_destroy,
.change_status = zfat_change_status,
.abort = zfat_abort,
};
struct zio_trigger_type zfat_type = {
.owner = THIS_MODULE,
.zattr_set = {
.std_zattr = zfat_std_zattr,
.ext_zattr = zfat_ext_zattr,
.n_ext_attr = ARRAY_SIZE(zfat_ext_zattr),
},
.s_op = &zfat_s_op,
.t_op = &zfat_ops,
};
/*
* Copyright CERN 2012 (author Federico Vaga)
*
* Driver for the mezzanine ADC for the SPEC
*/
#ifndef _FMC_ADC_H_
#define _FMC_ADC_H_
#include "spec.h"
/* ADC register offset */
#define FA_DMA_MEM_OFF 0x00000
#define FA_IRQ_MEM_OFF 0x50000
#define FA_ADC_MEM_OFF 0x90000
struct spec_fa {
struct spec_dev *spec;
struct zio_device *hwzdev;
unsigned char __iomem *base; /* regs files are byte-oriented */
};
/*
* ZFA_CHx_MULT
* address offset between two registers of the same type on consecutive channel
*/
#define ZFA_CHx_MULT 5
/* Device registers */
enum zfadc_dregs_enum {
/* Device */
/* Control registers */
ZFA_CTL_FMS_CMD,
ZFA_CTL_CLK_EN,
ZFA_CTL_DAC_CLR_N,
ZFA_CTL_BSLIP,
ZFA_CTL_TEST_DATA_EN,
ZFA_CTL_TRIG_LED,
ZFA_CTL_ACQ_LED,
/* Status registers */
ZFA_STA_FSM,
ZFA_STA_SERDES_PLL,
ZFA_STA_SERDES_SYNCED,
/* Configuration register */
ZFDAC_CFG_HW_SEL,
ZFAT_CFG_HW_POL,
ZFAT_CFG_HW_EN,
ZFAT_CFG_SW_EN,
ZFAT_CFG_INT_SEL,
ZFAT_CFG_THRES,
/* Delay*/
ZFAT_DLY,
/* Software */
ZFAT_SW,
/* Number of shots */
ZFAT_SHOTS_NB,
/* Sample rate */
ZFAT_SR_DECI,
/* Position address */
ZFAT_POS,
/* Pre-sample */
ZFAT_PRE,
/* Post-sample */
ZFAT_POST,
/* Sample counter */
ZFAT_CNT,
/* Channel 1 */
ZFA_CH1_CTL_RANGE,
ZFA_CH1_CTL_TERM,
ZFA_CH1_STA,
ZFA_CH1_GAIN,
ZFA_CH1_OFFSET,
/* Channel 2 */
ZFA_CH2_CTL_RANGE,
ZFA_CH2_CTL_TERM,
ZFA_CH2_STA,
ZFA_CH2_GAIN,
ZFA_CH2_OFFSET,
/* Channel 3 */
ZFA_CH3_CTL_RANGE,
ZFA_CH3_CTL_TERM,
ZFA_CH3_STA,
ZFA_CH3_GAIN,
ZFA_CH3_OFFSET,
/* Channel 4 */
ZFA_CH4_CTL_RANGE,
ZFA_CH4_CTL_TERM,
ZFA_CH4_STA,
ZFA_CH4_GAIN,
ZFA_CH4_OFFSET,
/* Other*/
ZFA_CHx_CTL_RANGE,
ZFA_CHx_CTL_TERM,
ZFA_CHx_STA,
ZFA_CHx_GAIN,
ZFA_CHx_OFFSET,
ZFA_SW_R_NOADDRES,
/* 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,
/* IRQ */
ZFA_IRQ_MULTI,
ZFA_IRQ_SRC,
ZFA_IRQ_MASK,
};
/* All possible state of the state machine, other values are invalid*/
enum zfa_fsm_state {
ZFA_STATE_IDLE = 0x1,
ZFA_STATE_PRE,
ZFA_STATE_POST,
ZFA_STATE_WAIT,
ZFA_STATE_DECR,
};
/* All possible interrupt available */
enum zfat_irq {
ZFAT_NONE = 0x0,
ZFAT_DMA_DONE = 0x1,
ZFAT_DMA_ERR = 0x2,
ZFAT_TRG_FIRE = 0x4,
ZFAT_ACQ_END = 0x8,
ZFAT_ALL = 0xF,
};
#ifdef __KERNEL__ /* All the rest is only of kernel users */
#include <linux/zio.h>
#include <linux/zio-trigger.h>
static inline struct spec_fa *get_zfadc(struct device *dev)
{
switch (to_zio_head(dev)->zobj_type) {
case ZDEV:
return to_zio_dev(dev)->priv_d;
case ZCSET:
return to_zio_cset(dev)->zdev->priv_d;
case ZCHAN:
return to_zio_chan(dev)->cset->zdev->priv_d;
case ZTI:
return to_zio_ti(dev)->cset->zdev->priv_d;
default:
return NULL;
}
return NULL;
}
static inline struct spec_dev *get_spec(struct device *dev)
{
return get_zfadc(dev)->spec;
}
static inline uint32_t fa_read_reg(struct spec_fa *fa,
const struct zio_reg_desc *reg)
{
return readl(fa->base + reg->addr);
}
static inline void fa_write_reg(uint32_t val, struct spec_fa *fa,
const struct zio_reg_desc *reg)
{
writel(val, fa->base + reg->addr);
}
static inline int zfa_common_conf_set(struct device *dev,
const struct zio_reg_desc *reg,
uint32_t usr_val)
{
uint32_t cur, val;
if ((usr_val & (~reg->mask))) {
dev_err(dev, "the value must fit the mask 0x%x\n", reg->mask);
return -EINVAL;
}
/* Read current register*/
cur = fa_read_reg(get_zfadc(dev), reg);
/* Mask the value */
cur &= (~(reg->mask << reg->off));
/* Write the new value */
val = cur | (usr_val << reg->off);
/* FIXME re-write usr_val when possible (zio need a patch) */
/* If the attribute has a valid address */
fa_write_reg(val, get_zfadc(dev), reg);
return 0;
}
static inline void zfa_common_info_get(struct device *dev,
const struct zio_reg_desc *reg,
uint32_t *usr_val)
{
uint32_t cur;
/* Read current register*/
cur = fa_read_reg(get_zfadc(dev), reg);
/* Mask the value */
cur &= (reg->mask << reg->off);
/* Return the value */
*usr_val = cur >> reg->off;
}
extern struct zio_trigger_type zfat_type;
/* Registers lists used in fd-zio-drv.c and fd-zio-trg.c */
extern const struct zio_reg_desc zfad_regs[];
/* Functions exported by fd-core.c */
extern int fa_probe(struct spec_dev *dev);
extern void fa_remove(struct spec_dev *dev);
/* Functions exported by fa-zio.c */
extern int fa_zio_register(void);
extern void fa_zio_unregister(void);
extern int fa_zio_init(struct spec_fa *fa);
extern void fa_zio_exit(struct spec_fa *fa);
/* Functions exported by fa-spec.c */
extern int fa_spec_init(void);
extern void fa_spec_exit(void);
#endif /* __KERNEL__ */
#endif /* _FMC_ADC_H_ */
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