Commit 3c2d0e1e authored by Tomasz Wlostowski's avatar Tomasz Wlostowski Committed by Tomasz Wlostowski

Initial SVEC version, cleanup & some bugfixing

parent e43f6a68
......@@ -13,7 +13,9 @@ subdirs-ccflags-y = $(ccflags-y)
obj-m := fmc-tdc.o
fmc-tdc-objs = acam.o calibration.o ft-spec.o ft-core.o onewire.o time.o ft-irq.o ft-zio.o ../fmc-bus/sdb-lib/access.o ../fmc-bus/sdb-lib/glue.o
fmc-tdc-objs = acam.o calibration.o fmc-util.o ft-spec.o \
ft-svec.o ft-core.o onewire.o ft-time.o ft-irq.o ft-zio.o \
../fmc-bus/sdb-lib/access.o ../fmc-bus/sdb-lib/glue.o
all: modules
......
......@@ -24,29 +24,27 @@
static struct {
int reg;
u32 value;
} acam_config [NB_ACAM_REGS] =
{
{ 0, AR0_ROsc | AR0_HQSel | AR0_TRiseEn(0) |
AR0_TRiseEn(1) | AR0_TRiseEn(2) |
AR0_TRiseEn(3) | AR0_TRiseEn(4) |
AR0_TRiseEn(5) |
AR0_TFallEn(1) | AR0_TFallEn(2) |
AR0_TFallEn(3) | AR0_TFallEn(4) |
AR0_TFallEn(5)},
{ 1, 0 },
{ 2, AR2_IMode |
AR2_Disable(6) | AR2_Disable(7) | AR2_Disable(8) },
{ 3, 0 },
{ 4, AR4_StartTimer(15) | AR4_EFlagHiZN },
{ 5, AR5_StartOff1(2000) },
{ 6, AR6_Fill(0xfc) },
{ 7, AR7_RefClkDiv(7) | AR7_HSDiv(234) | AR7_NegPhase | AR7_ResAdj },
{ 11, AR11_HFifoErrU(0) | AR11_HFifoErrU(1) |
AR11_HFifoErrU(2) | AR11_HFifoErrU(3) |
AR11_HFifoErrU(4) | AR11_HFifoErrU(5) |
AR11_HFifoErrU(6) | AR11_HFifoErrU(7) },
{ 12, AR12_StartNU | AR12_HFifoE },
{ 14, 0 }
} acam_config[NB_ACAM_REGS] = {
{
0, AR0_ROsc | AR0_HQSel | AR0_TRiseEn(0) |
AR0_TRiseEn(1) | AR0_TRiseEn(2) |
AR0_TRiseEn(3) | AR0_TRiseEn(4) |
AR0_TRiseEn(5) |
AR0_TFallEn(1) | AR0_TFallEn(2) |
AR0_TFallEn(3) | AR0_TFallEn(4) | AR0_TFallEn(5)}, {
1, 0}, {
2, AR2_IMode | AR2_Disable(6) | AR2_Disable(7) | AR2_Disable(8)}, {
3, 0}, {
4, AR4_StartTimer(15) | AR4_EFlagHiZN}, {
5, AR5_StartOff1(2000)}, {
6, AR6_Fill(0xfc)}, {
7, AR7_RefClkDiv(7) | AR7_HSDiv(234) | AR7_NegPhase | AR7_ResAdj}, {
11, AR11_HFifoErrU(0) | AR11_HFifoErrU(1) |
AR11_HFifoErrU(2) | AR11_HFifoErrU(3) |
AR11_HFifoErrU(4) | AR11_HFifoErrU(5) |
AR11_HFifoErrU(6) | AR11_HFifoErrU(7)}, {
12, AR12_StartNU | AR12_HFifoE}, {
14, 0}
};
static inline int acam_is_pll_locked(struct fmctdc_dev *ft)
......@@ -54,43 +52,42 @@ static inline int acam_is_pll_locked(struct fmctdc_dev *ft)
uint32_t status;
ft_writel(ft, TDC_CTRL_READ_ACAM_CFG, TDC_REG_CTRL);
udelay(100);
status = ft_readl(ft, TDC_REG_ACAM_READBACK(12));
return !(status & AR12_NotLocked);
return !(status & AR12_NotLocked);
}
int ft_acam_init(struct fmctdc_dev *ft)
{
int i;
unsigned long tmo;
unsigned long tmo;
pr_debug("%s: initializing ACAM TDC...\n", __func__);
ft_writel(ft, TDC_CTRL_RESET_ACAM, TDC_REG_CTRL);
udelay(100);
for(i = 0; i < NB_ACAM_REGS; i++)
{
if(acam_config[i].reg == 0)
ft->acam_r0 = acam_config[i].value;
ft_writel(ft, acam_config[i].value, TDC_REG_ACAM_CONFIG (acam_config[i].reg));
for (i = 0; i < NB_ACAM_REGS; i++) {
ft_writel(ft, acam_config[i].value,
TDC_REG_ACAM_CONFIG(acam_config[i].reg));
}
ft_writel(ft, TDC_CTRL_LOAD_ACAM_CFG, TDC_REG_CTRL);
/* commit ACAM config regs */
ft_writel(ft, TDC_CTRL_LOAD_ACAM_CFG, TDC_REG_CTRL);
udelay(100);
ft_writel(ft, TDC_CTRL_RESET_ACAM, TDC_REG_CTRL);
/* and reset the chip (keeps configuration) */
ft_writel(ft, TDC_CTRL_RESET_ACAM, TDC_REG_CTRL);
udelay(100);
/* wait for the ACAM's PLL to lock (2 seconds) */
tmo = jiffies + 2 * HZ;
while (time_before(jiffies, tmo))
{
if(acam_is_pll_locked(ft))
{
dev_info(&ft->fmc->dev, "%s: ACAM initialization OK.\n", __func__);
while (time_before(jiffies, tmo)) {
if (acam_is_pll_locked(ft)) {
dev_info(&ft->fmc->dev, "%s: ACAM initialization OK.\n",
__func__);
return 0;
}
}
......@@ -99,24 +96,13 @@ int ft_acam_init(struct fmctdc_dev *ft)
return -EIO;
}
void dump_acam_regs(struct fmctdc_dev *ft)
{
int i;
ft_writel(ft, TDC_CTRL_READ_ACAM_CFG, TDC_REG_CTRL);
udelay(1000);
for(i = 0; i <= 14 ; i++)
printk("ACAM reg %d: 0x%x\n", i, ft_readl(ft, TDC_REG_ACAM_READBACK(i)));
}
void ft_acam_exit(struct fmctdc_dev *ft)
{
/* Disable ACAM inputs and PLL */
ft_writel(ft, TDC_CTRL_DIS_ACQ, TDC_REG_CTRL);
ft_writel(ft, 0, TDC_REG_ACAM_CONFIG (0));
ft_writel(ft, 0, TDC_REG_ACAM_CONFIG (7));
ft_writel(ft, TDC_CTRL_LOAD_ACAM_CFG, TDC_REG_CTRL);
ft_writel(ft, 0, TDC_REG_ACAM_CONFIG(0));
ft_writel(ft, 0, TDC_REG_ACAM_CONFIG(7));
ft_writel(ft, TDC_CTRL_LOAD_ACAM_CFG, TDC_REG_CTRL);
udelay(100);
}
......@@ -21,17 +21,17 @@
/* dummy calibration data - used in case of empty/corrupted EEPROM */
static struct ft_calibration default_calibration = {
{ 0, 86, 609, 572, 335}, /* zero_offset */
43343 /* vcxo_default_tune */
{0, 86, 609, 572, 335}, /* zero_offset */
43343 /* vcxo_default_tune */
};
/* sdbfs-related function */
static int ft_read_calibration_eeprom(struct fmc_device *fmc, void *buf, int length)
static int ft_read_calibration_eeprom(struct fmc_device *fmc, void *buf,
int length)
{
int i, ret = 0;
static struct sdbfs fs;
fs.data = fmc->eeprom;
fs.datalen = fmc->eeprom_len;
......@@ -76,16 +76,17 @@ int ft_handle_eeprom_calibration(struct fmctdc_dev *ft)
offsets that could be added to TDC timestamps right away (picoseconds, referenced to WR) */
calib->zero_offset[0] = 0;
for(i = FT_CH_1 + 1; i < FT_NUM_CHANNELS; i++)
calib->zero_offset[i] = le32_to_cpu(raw_calib[i - 1]) / 100 - calib->zero_offset[0];
for (i = FT_CH_1 + 1; i < FT_NUM_CHANNELS; i++)
calib->zero_offset[i] =
le32_to_cpu(raw_calib[i - 1]) / 100 - calib->zero_offset[0];
calib->vcxo_default_tune = le32_to_cpu(raw_calib[4]);
for (i = 0; i < ARRAY_SIZE(calib->zero_offset); i++)
dev_info(d, "calib: zero_offset[%i] = %li\n", i,
(long)calib->zero_offset[i]);
dev_info(d, "calib: zero_offset[%i] = %li\n", i,
(long)calib->zero_offset[i]);
dev_info(d, "calib: vcxo_default_tune %i\n", calib->vcxo_default_tune);
return 0;
return 0;
}
......@@ -14,14 +14,14 @@
#ifndef __FMC_TDC_H__
#define __FMC_TDC_H__
#ifdef __KERNEL__ /* All the rest is only of kernel users */
#ifdef __KERNEL__ /* All the rest is only of kernel users */
#include <linux/spinlock.h>
#include <linux/timer.h>
#include <linux/fmc.h>
#include <linux/version.h>
#endif
#define FT_VERSION 2 /* version of the driver */
#define FT_VERSION 2 /* version of the driver */
/* default gatewares */
#define FT_GATEWARE_SVEC "fmc/svec-fmc-tdc.bin"
......@@ -32,164 +32,166 @@
#define FT_CH_1 1
#define FT_NUM_CHANNELS 5
enum ft_channel_flags {
FT_FLAG_CH_TERMINATED = 0,
FT_FLAG_CH_DO_INPUT,
FT_FLAG_CH_INPUT_READY
};
enum ft_zattr_dev_idx {
FT_ATTR_DEV_VERSION = 0,
FT_ATTR_DEV_SECONDS,
FT_ATTR_DEV_COARSE,
FT_ATTR_DEV_COMMAND, /* see below for commands */
FT_ATTR_DEV_TEMP,
FT_ATTR_DEV_RESERVE_6,
FT_ATTR_DEV_RESERVE_7,
FT_ATTR_DEV__LAST,
FT_ATTR_DEV_VERSION = 0,
FT_ATTR_DEV_SECONDS,
FT_ATTR_DEV_COARSE,
FT_ATTR_DEV_COMMAND, /* see below for commands */
FT_ATTR_DEV_TEMP,
FT_ATTR_DEV_ENABLE_INPUTS,
FT_ATTR_DEV_RESERVE_7,
FT_ATTR_DEV__LAST,
};
enum ft_zattr_in_idx {
/* PLEASE check "NOTE:" above if you edit this*/
FT_ATTR_TDC_SECONDS = FT_ATTR_DEV__LAST,
FT_ATTR_TDC_COARSE,
FT_ATTR_TDC_FRAC,
FT_ATTR_TDC_SEQ,
FT_ATTR_TDC_TERMINATION,
FT_ATTR_TDC_OFFSET,
FT_ATTR_TDC_USER_OFFSET,
FT_ATTR_TDC_PURGE_FIFO,
FT_ATTR_TDC__LAST,
/* PLEASE check "NOTE:" above if you edit this */
FT_ATTR_TDC_SECONDS = FT_ATTR_DEV__LAST,
FT_ATTR_TDC_COARSE,
FT_ATTR_TDC_FRAC,
FT_ATTR_TDC_SEQ,
FT_ATTR_TDC_TERMINATION,
FT_ATTR_TDC_OFFSET,
FT_ATTR_TDC_USER_OFFSET,
FT_ATTR_TDC__LAST,
};
enum ft_command {
FT_CMD_WR_ENABLE = 0,
FT_CMD_WR_DISABLE,
FT_CMD_WR_QUERY,
FT_CMD_IDENTIFY_ON,
FT_CMD_IDENTIFY_OFF
FT_CMD_WR_ENABLE = 0,
FT_CMD_WR_DISABLE,
FT_CMD_WR_QUERY,
FT_CMD_IDENTIFY_ON,
FT_CMD_IDENTIFY_OFF
};
/* rest of the file is kernel-only */
#ifdef __KERNEL__
enum ft_channel_flags {
FT_FLAG_CH_TERMINATED = 0,
FT_FLAG_CH_DO_INPUT,
FT_FLAG_CH_INPUT_READY
};
/* Carrier-specific operations (gateware does not fully decouple carrier specific stuff, such as
DMA or resets, from mezzanine-specific operations). */
struct fmctdc_dev;
struct ft_carrier_specific {
char *gateware_name;
int (*init)(struct fmctdc_dev *);
int (*reset_core)(struct fmctdc_dev *);
int (*copy_timestamps) (struct fmctdc_dev *, int base_addr, int size, void *dst );
int (*setup_irqs)(struct fmctdc_dev *, irq_handler_t handler);
int (*disable_irqs)(struct fmctdc_dev *);
int (*ack_irq)(struct fmctdc_dev *);
char *gateware_name;
int (*init) (struct fmctdc_dev *);
int (*reset_core) (struct fmctdc_dev *);
int (*copy_timestamps) (struct fmctdc_dev *, int base_addr, int size,
void *dst);
int (*setup_irqs) (struct fmctdc_dev *, irq_handler_t handler);
int (*disable_irqs) (struct fmctdc_dev *);
int (*ack_irq) (struct fmctdc_dev *, int irq_id);
void (*exit) (struct fmctdc_dev *);
};
struct ft_calibration { /* All of these are big endian in the EEPROM */
/* Input-to-WR timebase offset in ps. */
int32_t zero_offset[5];
struct ft_calibration { /* All of these are big endian */
/* Input-to-internal-timebase offset in ps. Add to all timestamps. */
int32_t zero_offset[5];
/* Default DAC value for VCXO. Set during init and for local timing */
uint32_t vcxo_default_tune;
/* Default DAC value for VCXO. Set during init and for local timing */
uint32_t vcxo_default_tune;
};
/* Hardware TDC timestamp */
struct ft_hw_timestamp {
uint32_t bins; /* In BIN (81 ps resolution) */
uint32_t coarse; /* 8 ns resolution */
uint32_t utc; /* 1 second resolution */
uint32_t metadata; /* channel, polarity, etc. */
uint32_t bins; /* In ACAM bins (81 ps) */
uint32_t coarse; /* 8 ns resolution */
uint32_t utc; /* 1 second resolution */
uint32_t metadata; /* channel, polarity, etc. */
} __packed;
/* White Rabbit timestamp */
struct ft_wr_timestamp {
uint64_t seconds;
uint32_t coarse;
uint32_t frac;
int seq_id;
int channel;
uint64_t seconds;
uint32_t coarse;
uint32_t frac;
int seq_id;
int channel;
};
struct ft_sw_fifo {
unsigned long head, tail, count, size;
struct ft_wr_timestamp *t;
unsigned long head, tail, count, size;
struct ft_wr_timestamp *t;
};
struct ft_channel_state {
unsigned long flags;
int expected_edge;
int cur_seq_id;
int32_t user_offset;
struct ft_wr_timestamp prev_ts;
struct ft_sw_fifo fifo;
unsigned long flags;
int expected_edge;
int cur_seq_id;
int32_t user_offset;
struct ft_wr_timestamp prev_ts;
struct ft_sw_fifo fifo;
};
/* Main TDC device context */
struct fmctdc_dev {
spinlock_t lock;
int ft_core_base;
/* HW buffer/FIFO access lock */
spinlock_t lock;
/* base addresses, taken from SDB */
int ft_core_base;
int ft_i2c_base;
int ft_owregs_base;
int ft_dma_base;
int ft_carrier_base;
int ft_irq_base;
struct fmc_device *fmc;
struct zio_device *zdev, *hwzdev;
struct timer_list temp_timer;
struct ft_carrier_specific *carrier_specific;
void *carrier_data;
struct ft_calibration calib;
struct tasklet_struct readout_tasklet;
int initialized;
int wr_mode_active;
uint8_t ds18_id[8];
unsigned long next_t;
int temp; /* temperature: scaled by 4 bits */
int temp_ready;
int verbose;
uint32_t acam_r0;
struct ft_channel_state channels[FT_NUM_CHANNELS];
uint32_t cur_wr_ptr, prev_wr_ptr;
struct ft_hw_timestamp *raw_events;
int ft_carrier_base;
int ft_irq_base;
/* IRQ base index (for SVEC) */
int irq_shift;
struct fmc_device *fmc;
struct zio_device *zdev, *hwzdev;
/* is acquisition mode active? */
int acquisition_on;
/* temperature readout timer */
struct timer_list temp_timer;
/* carrier specific functions (init/exit/reset/readout/irq handling) */
struct ft_carrier_specific *carrier_specific;
/* carrier private data */
void *carrier_data;
/* current calibration block */
struct ft_calibration calib;
struct tasklet_struct readout_tasklet;
int initialized;
/* DS18S20 temperature sensor 1-wire ID */
uint8_t ds18_id[8];
/* next temperature measurement pending? */
unsigned long next_t;
/* temperature, degrees Celsius scaled by 16 and its ready flag */
int temp;
int temp_ready;
/* output lots of debug stuff? */
int verbose;
struct ft_channel_state channels[FT_NUM_CHANNELS];
/* hardware buffer pointers / dacapo regs */
uint32_t cur_wr_ptr, prev_wr_ptr;
/* DMA buffer */
struct ft_hw_timestamp *raw_events;
};
extern struct ft_carrier_specific ft_carrier_spec;
extern struct ft_carrier_specific ft_carrier_svec;
static inline uint32_t ft_readl(struct fmctdc_dev *ft, unsigned long reg)
{
return fmc_readl(ft->fmc, ft->ft_core_base + reg);
return fmc_readl(ft->fmc, ft->ft_core_base + reg);
}
static inline void ft_writel(struct fmctdc_dev *ft, uint32_t v, unsigned long reg)
static inline void ft_writel(struct fmctdc_dev *ft, uint32_t v,
unsigned long reg)
{
fmc_writel(ft->fmc, v, ft->ft_core_base + reg);
fmc_writel(ft->fmc, v, ft->ft_core_base + reg);
}
void ft_enable_acquisition(struct fmctdc_dev *ft, int enable);
int ft_acam_init(struct fmctdc_dev *ft);
void ft_acam_exit(struct fmctdc_dev *ft);
int ft_acam_enable_channel(struct fmctdc_dev *ft, int channel, int enable);
int ft_acam_enable_termination(struct fmctdc_dev *dev, int channel, int enable);
int ft_acam_enable_acquisition(struct fmctdc_dev *ft, int enable);
int ft_onewire_init(struct fmctdc_dev *ft);
void ft_onewire_exit(struct fmctdc_dev *ft);
......@@ -198,13 +200,14 @@ int ft_read_temp(struct fmctdc_dev *ft, int verbose);
int ft_pll_init(struct fmctdc_dev *ft);
void ft_pll_exit(struct fmctdc_dev *ft);
void ft_ts_apply_offset(struct ft_wr_timestamp *ts, int32_t offset_picos );
void ft_ts_sub (struct ft_wr_timestamp *a, struct ft_wr_timestamp *b);
void ft_ts_apply_offset(struct ft_wr_timestamp *ts, int32_t offset_picos);
void ft_ts_sub(struct ft_wr_timestamp *a, struct ft_wr_timestamp *b);
int ft_set_tai_time(struct fmctdc_dev *ft, uint64_t seconds, uint32_t coarse);
int ft_get_tai_time(struct fmctdc_dev *ft, uint64_t *seconds, uint32_t *coarse);
int ft_enable_wr_mode (struct fmctdc_dev *ft, int enable);
int ft_check_wr_mode (struct fmctdc_dev *ft);
int ft_get_tai_time(struct fmctdc_dev *ft, uint64_t * seconds,
uint32_t * coarse);
int ft_enable_wr_mode(struct fmctdc_dev *ft, int enable);
int ft_check_wr_mode(struct fmctdc_dev *ft);
int ft_handle_eeprom_calibration(struct fmctdc_dev *ft);
......@@ -221,9 +224,14 @@ void ft_zio_exit(struct fmctdc_dev *ft);
struct zio_channel;
int ft_read_sw_fifo(struct fmctdc_dev *ft, int channel, struct zio_channel *chan);
int ft_read_sw_fifo(struct fmctdc_dev *ft, int channel,
struct zio_channel *chan);
int ft_enable_termination(struct fmctdc_dev *ft, int channel, int enable);
#endif
signed long fmc_find_sdb_device_ext(struct sdb_array *tree,
uint64_t vid, uint32_t did, int index,
unsigned long *sz);
#endif
#endif // __KERNEL__
#endif // __FMC_TDC_H__
/*
* 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>
#include "fmc-tdc.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;
}
This diff is collapsed.
This diff is collapsed.
......@@ -3,7 +3,6 @@
*
* Copyright (C) 2012-2013 CERN (http://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
......@@ -27,35 +26,32 @@ struct ft_spec_data {
static inline uint32_t dma_readl(struct fmctdc_dev *ft, unsigned long reg)
{
uint32_t rv = fmc_readl(ft->fmc, ft->ft_dma_base + reg);
//printk("dma_readl: addr %x val %x\n", ft->ft_dma_base + reg, rv);
return rv;
return fmc_readl(ft->fmc, ft->ft_dma_base + reg);
}
static inline void dma_writel(struct fmctdc_dev *ft, uint32_t v, unsigned long reg)
static inline void dma_writel(struct fmctdc_dev *ft, uint32_t v,
unsigned long reg)
{
//printk("dma_writel: addr %x val %x\n", ft->ft_dma_base + reg, v);
fmc_writel(ft->fmc, v, ft->ft_dma_base + reg);
}
static int spec_ft_init ( struct fmctdc_dev *ft )
static int ft_spec_init(struct fmctdc_dev *ft)
{
ft->carrier_data = kzalloc(sizeof(struct ft_spec_data), GFP_KERNEL );
ft->carrier_data = kzalloc(sizeof(struct ft_spec_data), GFP_KERNEL);
if(!ft->carrier_data)
if (!ft->carrier_data)
return -ENOMEM;
return 0;
}
static int spec_ft_reset( struct fmctdc_dev *dev )
static int ft_spec_reset(struct fmctdc_dev *ft)
{
struct spec_dev *spec = (struct spec_dev *) dev->fmc->carrier_data;
struct spec_dev *spec = (struct spec_dev *)ft->fmc->carrier_data;
dev_info(&dev->fmc->dev, "%s: resetting TDC core through Gennum.\n", __func__);
dev_info(&ft->fmc->dev, "%s: resetting TDC core through Gennum.\n",
__func__);
/* set local bus clock to 160 MHz. The FPGA can't handle more. */
/* set local bus clock to 160 MHz. The FPGA can't handle more. */
gennum_writel(spec, 0xE001F04C, 0x808);
/* fixme: there is no possibility of doing a software reset of the TDC core
......@@ -66,52 +62,53 @@ static int spec_ft_reset( struct fmctdc_dev *dev )
mdelay(10);
gennum_writel(spec, 0x00025000, GNPCI_SYS_CFG_SYSTEM);
msleep(3000); /* it takes a while for the PLL to bootstrap.... or not!
We have no possibility to check :( */
msleep(3000); /* it takes a while for the PLL to bootstrap.... or not!
We have no possibility to check :( */
return 0;
}
static int spec_ft_copy_timestamps (struct fmctdc_dev *dev, int base_addr, int size, void *dst )
static int ft_spec_copy_timestamps(struct fmctdc_dev *ft, int base_addr,
int size, void *dst)
{
struct ft_spec_data *hw = dev->carrier_data;
struct ft_spec_data *cspec = ft->carrier_data;
uint32_t status;
int i, ret;
hw->dma_addr = dma_map_single(dev->fmc->hwdev, (char *)dst, size, DMA_FROM_DEVICE);
if (dma_mapping_error(dev->fmc->hwdev, hw->dma_addr)) {
dev_err(&dev->fmc->dev, "dma_map_single failed\n");
cspec->dma_addr =
dma_map_single(ft->fmc->hwdev, (char *)dst, size, DMA_FROM_DEVICE);
if (dma_mapping_error(cspec->dma_addr)) {
dev_err(&ft->fmc->dev, "dma_map_single failed\n");
return -ENOMEM;
}
dma_writel(dev, 0, TDC_REG_DMA_CTRL);
dma_writel(dev, base_addr, TDC_REG_DMA_C_START);
dma_writel(dev, hw->dma_addr & 0xffffffff, TDC_REG_DMA_H_START_L);
dma_writel(dev, ((uint64_t)hw->dma_addr >> 32) & 0x00ffffffff, TDC_REG_DMA_H_START_H);
dma_writel(dev, 0, TDC_REG_DMA_NEXT_L);
dma_writel(dev, 0, TDC_REG_DMA_NEXT_H);
dma_writel(ft, 0, TDC_REG_DMA_CTRL);
dma_writel(ft, base_addr, TDC_REG_DMA_C_START);
dma_writel(ft, cspec->dma_addr & 0xffffffff, TDC_REG_DMA_H_START_L);
dma_writel(ft, ((uint64_t) cspec->dma_addr >> 32) & 0x00ffffffff,
TDC_REG_DMA_H_START_H);
dma_writel(ft, 0, TDC_REG_DMA_NEXT_L);
dma_writel(ft, 0, TDC_REG_DMA_NEXT_H);
/* Write the DMA length */
dma_writel(dev, size, TDC_REG_DMA_LEN);
dma_writel(ft, size, TDC_REG_DMA_LEN);
/* No chained xfers, PCIe to host */
dma_writel(dev, 0, TDC_REG_DMA_ATTRIB);
dma_writel(ft, 0, TDC_REG_DMA_ATTRIB);
/* Start the transfer */
dma_writel(dev, 1, TDC_REG_DMA_CTRL);
udelay(50);
dma_writel(dev, 0, TDC_REG_DMA_CTRL);
dma_writel(ft, 1, TDC_REG_DMA_CTRL);
udelay(1);
dma_writel(ft, 0, TDC_REG_DMA_CTRL);
/* Don't bother about end-of-DMA IRQ, it only makes the driver unnecessarily complicated. */
for(i = 0; i < 1000; i++)
{
status = dma_readl (dev, TDC_REG_DMA_STAT) & TDC_DMA_STAT_MASK;
for (i = 0; i < 1000; i++) {
status = dma_readl(ft, TDC_REG_DMA_STAT) & TDC_DMA_STAT_MASK;
if(status == TDC_DMA_STAT_DONE)
{
if (status == TDC_DMA_STAT_DONE) {
ret = 0;
break;
} else if (status == TDC_DMA_STAT_ERROR) {
......@@ -122,43 +119,45 @@ static int spec_ft_copy_timestamps (struct fmctdc_dev *dev, int base_addr, int s
udelay(1);
}
if(i == 1000)
{
dev_err(&dev->fmc->dev, "%s: DMA transfer taking way too long. Something's really weird.\n", __func__);
if (i == 1000) {
dev_err(&ft->fmc->dev,
"%s: DMA transfer taking way too long. Something's really weird.\n",
__func__);
ret = -EIO;
}
dma_sync_single_for_cpu(dev->fmc->hwdev, hw->dma_addr, size, DMA_FROM_DEVICE);
dma_unmap_single(dev->fmc->hwdev, hw->dma_addr, size, DMA_FROM_DEVICE);
return ret;
dma_sync_single_for_cpu(ft->fmc->hwdev, cspec->dma_addr, size,
DMA_FROM_DEVICE);
dma_unmap_single(ft->fmc->hwdev, cspec->dma_addr, size,
DMA_FROM_DEVICE);
return ret;
}
/* Unfortunately, on the spec this is GPIO9, i.e. IRQ(1) */
static struct fmc_gpio ft_gpio_on[] = {
{
.gpio = FMC_GPIO_IRQ(1),
.mode = GPIOF_DIR_IN,
.irqmode = IRQF_TRIGGER_RISING,
}
.gpio = FMC_GPIO_IRQ(1),
.mode = GPIOF_DIR_IN,
.irqmode = IRQF_TRIGGER_RISING,
}
};
static struct fmc_gpio ft_gpio_off[] = {
{
.gpio = FMC_GPIO_IRQ(1),
.mode = GPIOF_DIR_IN,
.irqmode = 0,
}
.gpio = FMC_GPIO_IRQ(1),
.mode = GPIOF_DIR_IN,
.irqmode = 0,
}
};
static int spec_ft_setup_irqs (struct fmctdc_dev *ft, irq_handler_t handler)
static int ft_spec_setup_irqs(struct fmctdc_dev *ft, irq_handler_t handler)
{
struct fmc_device *fmc = ft->fmc;
int ret;
ret = fmc->op->irq_request(fmc, handler, "fmc-tdc", IRQF_SHARED);
if(ret < 0)
{
if (ret < 0) {
dev_err(&fmc->dev, "Request interrupt failed: %d\n", ret);
return ret;
}
......@@ -167,7 +166,7 @@ static int spec_ft_setup_irqs (struct fmctdc_dev *ft, irq_handler_t handler)
return 0;
}
static int spec_ft_disable_irqs (struct fmctdc_dev *ft)
static int ft_spec_disable_irqs(struct fmctdc_dev *ft)
{
struct fmc_device *fmc = ft->fmc;
......@@ -177,18 +176,23 @@ static int spec_ft_disable_irqs (struct fmctdc_dev *ft)
return 0;
}
static int spec_ft_ack_irq (struct fmctdc_dev *ft)
static int ft_spec_ack_irq(struct fmctdc_dev *ft, int irq_id)
{
return 0;
}
static void ft_spec_exit(struct fmctdc_dev *ft)
{
kfree(ft->carrier_data);
}
struct ft_carrier_specific ft_carrier_spec = {
FT_GATEWARE_SPEC,
spec_ft_init,
spec_ft_reset,
spec_ft_copy_timestamps,
spec_ft_setup_irqs,
spec_ft_disable_irqs,
spec_ft_ack_irq
};
\ No newline at end of file
ft_spec_init,
ft_spec_reset,
ft_spec_copy_timestamps,
ft_spec_setup_irqs,
ft_spec_disable_irqs,
ft_spec_ack_irq,
ft_spec_exit,
};
/*
* SVEC-specific workarounds for the fmc-tdc driver.
*
* Copyright (C) 2012-2013 CERN (http://www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* 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.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/delay.h>
#include <linux/fmc.h>
#include "fmc-tdc.h"
#include "hw/tdc_regs.h"
static int ft_poll_interval = 200;
module_param_named(poll_interval, ft_poll_interval, int, 0444);
MODULE_PARM_DESC(poll_interval,
"Buffer polling interval in milliseconds. Applies to SVEC version only.");
/* SVEC-specific private data structure */
struct ft_svec_data {
irq_handler_t fake_irq_handler;
struct timer_list poll_timer;
};
static void ft_poll_timer_handler(unsigned long arg)
{
struct fmctdc_dev *ft = (struct fmctdc_dev *)arg;
struct ft_svec_data *cdata = ft->carrier_data;
uint32_t irq_stat;
irq_stat = fmc_readl(ft->fmc, ft->ft_irq_base + TDC_REG_IRQ_STATUS);
irq_stat >>= ft->irq_shift;
/* irq status bit set for the TS buffer irq? call the "real" handler */
if (irq_stat & TDC_IRQ_TDC_TSTAMP)
cdata->fake_irq_handler(TDC_IRQ_TDC_TSTAMP, ft->fmc);
mod_timer(&cdata->poll_timer, jiffies + (ft_poll_interval * HZ / 1000));
}
static int ft_svec_init(struct fmctdc_dev *ft)
{
struct ft_svec_data *cdata;;
ft->carrier_data = kzalloc(sizeof(struct ft_svec_data), GFP_KERNEL);
if (!ft->carrier_data)
return -ENOMEM;
/* FIXME: use SDB (after fixing the HDL) */
cdata = ft->carrier_data;
return 0;
}
static int ft_svec_reset(struct fmctdc_dev *ft)
{
unsigned long tmo;
/* FIXME: An UGLY hack: ft_svec_reset() executed on slot 0 (first mezzanine to
be initialized) resets BOTH cards. The reason is that we need both mezzanines PLLs
running to read the entire SDB tree (parts of the system interconnect are clocked from
FMC clock lines - a f***up in the HDL. */
if (ft->fmc->slot_id != 0)
return 0;
dev_info(&ft->fmc->dev, "Resetting FMCs...\n");
fmc_writel(ft->fmc, TDC_CARRIER_CTL1_RSTN_FMC0 |
TDC_CARRIER_CTL1_RSTN_FMC1,
TDC_SVEC_CARRIER_BASE + TDC_REG_CARRIER_CTL1);
tmo = jiffies + 2 * HZ;
while (time_before(jiffies, tmo)) {
uint32_t stat;
stat =
fmc_readl(ft->fmc,
TDC_SVEC_CARRIER_BASE + TDC_REG_CARRIER_CTL0);
if ((stat & TDC_CARRIER_CTL0_PLL_STAT_FMC0) &&
(stat & TDC_CARRIER_CTL0_PLL_STAT_FMC1))
return 0;
msleep(10);
}
dev_err(&ft->fmc->dev, "PLL lock timeout.\n");
return -EIO;
}
static int ft_svec_copy_timestamps(struct fmctdc_dev *ft, int base_addr,
int size, void *dst)
{
int i;
uint32_t addr;
uint32_t *dptr;
if (unlikely(size & 3 || base_addr & 3)) /* no unaligned reads, please. */
return -EIO;
/* FIXME: use SDB to determine buffer base address (after fixing the HDL) */
addr = ft->ft_core_base + 0x4000 + base_addr;
for (i = 0, dptr = (uint32_t *) dst; i < size / 4; i++, dptr++)
*dptr = fmc_readl(ft->fmc, addr + i * 4);
return 0;
}
static int ft_svec_setup_irqs(struct fmctdc_dev *ft, irq_handler_t handler)
{
struct ft_svec_data *cdata = ft->carrier_data;
/* FIXME: The code below doesn't work because current SVEC driver... does not support
interrupts. We use a timer instead. */
#if 0
ret = fmc->op->irq_request(fmc, handler, "fmc-tdc", IRQF_SHARED);
if (ret < 0) {
dev_err(&fmc->dev, "Request interrupt failed: %d\n", ret);
return ret;
}
#endif
cdata->fake_irq_handler = handler;
setup_timer(&cdata->poll_timer, ft_poll_timer_handler,
(unsigned long)ft);
mod_timer(&cdata->poll_timer, jiffies + (ft_poll_interval * HZ / 1000));
return 0;
}
static int ft_svec_disable_irqs(struct fmctdc_dev *ft)
{
struct ft_svec_data *cdata = ft->carrier_data;
del_timer_sync(&cdata->poll_timer);
/* fmc->op->irq_free(fmc);*/
return 0;
}
static int ft_svec_ack_irq(struct fmctdc_dev *ft, int irq_id)
{
return 0;
}
static void ft_svec_exit(struct fmctdc_dev *ft)
{
kfree(ft->carrier_data);
}
struct ft_carrier_specific ft_carrier_svec = {
FT_GATEWARE_SVEC,
ft_svec_init,
ft_svec_reset,
ft_svec_copy_timestamps,
ft_svec_setup_irqs,
ft_svec_disable_irqs,
ft_svec_ack_irq,
ft_svec_exit
};
......@@ -18,7 +18,10 @@
#include "hw/tdc_regs.h"
void ft_ts_from_picos ( uint32_t picos, struct ft_wr_timestamp *result )
/* WARNING: the seconds register name is a bit misleading - it is not UTC time
as the core is not aware of leap seconds, making it TAI time. */
void ft_ts_from_picos(uint32_t picos, struct ft_wr_timestamp *result)
{
result->frac = picos % 4096;
picos -= picos % 4096;
......@@ -29,55 +32,56 @@ void ft_ts_from_picos ( uint32_t picos, struct ft_wr_timestamp *result )
result->seconds = picos;
}
void ft_ts_add (struct ft_wr_timestamp *a, struct ft_wr_timestamp *b)
void ft_ts_add(struct ft_wr_timestamp *a, struct ft_wr_timestamp *b)
{
a->frac += b->frac;
if (unlikely(a->frac >= 4096)) {
a->frac -= 4096;
a->coarse++;
}
a->coarse += b->coarse;
if (unlikely(a->coarse >= 125000000)) {
a->coarse -= 125000000;
a->seconds ++;
}
a->seconds += b->seconds;
if (unlikely(a->frac >= 4096)) {
a->frac -= 4096;
a->coarse++;
}
a->coarse += b->coarse;
if (unlikely(a->coarse >= 125000000)) {
a->coarse -= 125000000;
a->seconds++;
}
a->seconds += b->seconds;
}
void ft_ts_sub (struct ft_wr_timestamp *a, struct ft_wr_timestamp *b)
void ft_ts_sub(struct ft_wr_timestamp *a, struct ft_wr_timestamp *b)
{
int32_t d_frac, d_coarse = 0;
d_frac = a->frac - b->frac;
if (unlikely(d_frac < 0)) {
d_frac += 4096;
d_coarse--;
}
d_coarse += a->coarse - b->coarse;
if (unlikely(d_coarse < 0)) {
d_coarse += 125000000;
a->seconds --;
}
a->coarse = d_coarse;
a->frac = d_frac;
a->seconds -= b->seconds;
d_frac = a->frac - b->frac;
if (unlikely(d_frac < 0)) {
d_frac += 4096;
d_coarse--;
}
d_coarse += a->coarse - b->coarse;
if (unlikely(d_coarse < 0)) {
d_coarse += 125000000;
a->seconds--;
}
a->coarse = d_coarse;
a->frac = d_frac;
a->seconds -= b->seconds;
}
void ft_ts_apply_offset(struct ft_wr_timestamp *ts, int32_t offset_picos )
void ft_ts_apply_offset(struct ft_wr_timestamp *ts, int32_t offset_picos)
{
struct ft_wr_timestamp offset_ts;
ft_ts_from_picos(offset_picos < 0 ? -offset_picos : offset_picos, &offset_ts);
ft_ts_from_picos(offset_picos < 0 ? -offset_picos : offset_picos,
&offset_ts);
if(offset_picos < 0)
if (offset_picos < 0)
ft_ts_sub(ts, &offset_ts);
else
ft_ts_add(ts, &offset_ts);
......@@ -85,26 +89,28 @@ void ft_ts_apply_offset(struct ft_wr_timestamp *ts, int32_t offset_picos )
int ft_set_tai_time(struct fmctdc_dev *ft, uint64_t seconds, uint32_t coarse)
{
if(ft->verbose)
dev_info(&ft->fmc->dev, "setting TAI time to %lld:%d\n", seconds, coarse);
if (ft->verbose)
dev_info(&ft->fmc->dev, "Setting TAI time to %lld:%d\n",
seconds, coarse);
ft_writel(ft, seconds & 0xffffffff, TDC_REG_CURRENT_UTC);
ft_writel(ft, TDC_CTRL_LOAD_UTC, TDC_REG_CTRL);
return 0;
}
int ft_get_tai_time(struct fmctdc_dev *ft, uint64_t *seconds, uint32_t *coarse)
int ft_get_tai_time(struct fmctdc_dev *ft, uint64_t * seconds,
uint32_t * coarse)
{
*seconds = ft_readl(ft, TDC_REG_CURRENT_UTC);
*coarse = 0;
return 0;
}
int ft_enable_wr_mode (struct fmctdc_dev *ft, int enable)
int ft_enable_wr_mode(struct fmctdc_dev *ft, int enable)
{
return -ENOTSUPP;
}
int ft_check_wr_mode (struct fmctdc_dev *ft)
int ft_check_wr_mode(struct fmctdc_dev *ft)
{
return -ENOTSUPP;
}
......@@ -120,5 +126,4 @@ int ft_time_init(struct fmctdc_dev *ft)
void ft_time_exit(struct fmctdc_dev *ft)
{
}
\ No newline at end of file
}
This diff is collapsed.
......@@ -60,10 +60,15 @@
#define TDC_EVENT_DACAPO_FLAG BIT(0)
/* Carrier CSRs */
#define TDC_REG_CARRIER_CTL0 0x0
#define TDC_REG_CARRIER_CTL1 0x4
#define TDC_REG_CARRIER_CTL0 0x0 /* a.k.a. Carrier revision/PCB id reg */
#define TDC_REG_CARRIER_STATUS 0x4
#define TDC_REG_CARRIER_CTL1 0x8
#define TDC_CARRIER_CTL0_PLL_STAT BIT(4)
#define TDC_CARRIER_CTL0_PLL_STAT_FMC0 BIT(4)
#define TDC_CARRIER_CTL0_PLL_STAT_FMC1 BIT(5)
#define TDC_CARRIER_CTL1_RSTN_FMC0 BIT(3)
#define TDC_CARRIER_CTL1_RSTN_FMC1 BIT(4)
/* Gennum DMA registers (not defined in the SPEC driver headers) */
#define TDC_REG_DMA_CTRL 0x0
......@@ -81,4 +86,6 @@
#define TDC_DMA_STAT_DONE 0x1
#define TDC_DMA_STAT_ERROR 0x3
#define TDC_SVEC_CARRIER_BASE 0x20000
#endif /* __TDC_REGISTERS_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