Commit 8cd60f90 authored by Jorge Machado's avatar Jorge Machado

Add support for new version in fmc-dio-internal.c

parent 821f8f8a
......@@ -32,8 +32,10 @@
#define fmc_dio_stat 0
#endif
#define CHANNEL_5_IRQ_EN_MASK 0x100000
/* We need a clear mapping for the registers of the various bits */
struct regmap {
/*struct regmap {
int trig_l;
int trig_h;
int cycle;
......@@ -92,15 +94,23 @@ static struct regmap regmap[] = {
.fifo_cycle = R(TSF4_R2),
.fifo_status = R(TSF4_CSR),
}
};
};*/
#define FMC_DIO_IRQ_MASK \
#define FMC_DIO_IRQ_MASK \
(DIO_EIC_ISR_NEMPTY_0 \
| DIO_EIC_ISR_NEMPTY_1 \
| DIO_EIC_ISR_NEMPTY_2 \
| DIO_EIC_ISR_NEMPTY_1\
| DIO_EIC_ISR_NEMPTY_2\
| DIO_EIC_ISR_NEMPTY_3\
| DIO_EIC_ISR_NEMPTY_4)
#define FMC_DIO_IRQ_MASK_V2 \
(DIO_EIC_ISR_NEMPTY_0 \
| DIO_EIC_ISR_NEMPTY_1\
| DIO_EIC_ISR_NEMPTY_2\
| DIO_EIC_ISR_NEMPTY_3\
| DIO_EIC_ISR_NEMPTY_4\
| DIO_EIC_ISR_NEMPTY_5)
/* This is the structure we need to manage interrupts and loop internally */
#define FMC_DIO_BUFFER_LEN 512
struct dio_channel {
......@@ -115,7 +125,7 @@ struct dio_channel {
};
struct dio_device {
struct dio_channel ch[5];
struct dio_channel ch[6];
};
/* Instead of timespec_sub, just subtract the nanos */
......@@ -128,25 +138,30 @@ static inline void fmc_dio_int_ts_sub(struct timespec *ts, int nano)
static void __fmc_dio_int_new_pulse(struct fmc_dio *dev, int ch,
struct timespec *ts)
{
struct DIO_WB __iomem *dio = dev->dio;
void __iomem *base = dio;
//struct DIO_WB __iomem *dio = dev->dio;
void __iomem *base = dev->dio;
struct regmap *map;
map = regmap + ch;
struct regmap_common dio;
dio = get_regmap_common(dev->version);
//map = regmap + ch;
map = get_regmap(dev->version);
map += ch;
fmc_dio_int_ts_sub(ts, 8); /* 1 cycle, to account for output latencies */
writel(ts->tv_nsec / 8, base + map->cycle);
writel(GET_HI32(ts->tv_sec), base + map->trig_h);
writel(ts->tv_sec, base + map->trig_l);
writel(1 << ch, &dio->R_LATCH);
writel(1 << ch, base + dio.latch_reg);
}
static int fmc_dio_int_cmd_pulse(struct fmc_dio *dev,
struct wr_dio_cmd *cmd)
{
struct DIO_WB __iomem *dio = dev->dio;
void __iomem *base = dio;
//struct DIO_WB __iomem *dio = dev->dio;
void __iomem *base = dev->dio;
struct PPSG_WB __iomem *ppsg = dev->ppsg;
struct dio_device *d = dev->priv;
struct dio_channel *c;
......@@ -154,31 +169,51 @@ static int fmc_dio_int_cmd_pulse(struct fmc_dio *dev,
struct timespec *ts;
uint32_t reg;
int ch;
struct regmap_common dio;
printk("PULSE");
dio = get_regmap_common(dev->version);
ch = cmd->channel;
if (ch > 4)
return -EINVAL; /* mask not supported */
c = d->ch + ch;
map = regmap + ch;
//map = regmap + ch;
map = get_regmap(dev->version);
map += ch;
ts = cmd->t;
/* First, configure this bit as DIO output */
reg = readl(&dio->IOMODE);
writel(reg | (1 << 4*ch), &dio->IOMODE);
reg = readl(base + dio.iomode_reg);
writel(reg | (1 << 4*ch), base + dio.iomode_reg);
writel(ts[1].tv_nsec / 8, base + map->pulse); /* width */
if (cmd->flags & WR_DIO_F_NOW) {
printk("NOW");
/* Generate a pulse train from current time in V2 DIO version*/
if(dev->version == 1) {
if (cmd->flags & WR_DIO_F_LOOP && cmd->value != 1) {
printk("loop enabled %lu, %d", ts[2].tv_nsec, cmd->value);
writel(ts[2].tv_nsec / 8, base + map->pulse_per);
c->target_channel = ch;
if (cmd->value > 1) {
cmd->value-=2;
}
atomic_set(&c->count, cmd->value);
}
}
/* if "now" we are done */
writel(1 << ch, &dio->PULSE);
writel(1 << ch, base + dio.pulse_reg);
return 0;
}
/* if relative, add current 40-bit second to timespec */
if (cmd->flags & WR_DIO_F_REL) {
if (cmd->flags & WR_DIO_F_REL) {
uint32_t h1, l, h2;
unsigned long now;
printk("with relative time");
h1 = readl(&ppsg->CNTR_UTCHI);
l = readl(&ppsg->CNTR_UTCLO);
h2 = readl(&ppsg->CNTR_UTCHI);
......@@ -189,15 +224,26 @@ static int fmc_dio_int_cmd_pulse(struct fmc_dio *dev,
ts->tv_sec += now;
}
if (cmd->flags & WR_DIO_F_LOOP) {
if (cmd->flags & WR_DIO_F_LOOP && cmd->value != 1) {
printk("loop enabled");
c->target_channel = ch;
printk("%d times", cmd->value);
if(dev->version == 0) {
c->delay = ts[2];
/* c->count is used after the pulse, so remove the first */
if (cmd->value > 0)
cmd->value--;
}
else {
writel(ts[2].tv_nsec / 8, base + map->pulse_per);
if (cmd->value > 1) {
cmd->value-=2;
}
}
/* c->count is used after the pulse, so remove the first */
if (cmd->value > 0)
cmd->value--;
atomic_set(&c->count, cmd->value);
c->prevts = ts[0]; /* our current setpoint */
c->delay = ts[2];
}
__fmc_dio_int_new_pulse(dev, ch, ts);
......@@ -221,7 +267,10 @@ static int fmc_dio_int_cmd_stamp(struct fmc_dio *dev,
again:
if (cmd->flags & WR_DIO_F_MASK) {
ch = 0;
last = 4;
if(dev->version == 0)
last = 4;
else
last = 5;
mask = cmd->channel;
} else {
ch = cmd->channel;
......@@ -233,7 +282,12 @@ again:
for (; ch <= last; ch++, c++) {
if (((1 << ch) & mask) == 0)
continue;
map = regmap + ch;
printk("Getting timestamps of channel %d", ch);
//map = regmap + ch;
map = get_regmap(dev->version);
map += ch;
while (1) {
if (nstamp == WR_DIO_N_STAMP)
break;
......@@ -270,10 +324,14 @@ again:
static int fmc_dio_int_cmd_inout(struct fmc_dio *dev,
struct wr_dio_cmd *cmd)
{
struct DIO_WB __iomem *dio = dev->dio;
//struct DIO_WB __iomem *dio = dev->dio;
struct fmc_dio_gpio_block __iomem *gpio = dev->gpio;
int mask, ch, last, bits;
uint32_t reg, iomode;
void __iomem *base = dev->dio;
struct regmap_common dio;
dio = get_regmap_common(dev->version);
if (cmd->flags & WR_DIO_F_MASK) {
ch = 0;
......@@ -294,7 +352,7 @@ static int fmc_dio_int_cmd_inout(struct fmc_dio *dev,
bits = cmd->value >> ch;
/* Obtain the current value in iomode */
reg = readl(&dio->IOMODE) & ~(0xF << 4*ch);
reg = readl(base + dio.iomode_reg) & ~(0xF << 4*ch);
/* Select IO mode */
if (bits & WR_DIO_INOUT_DIO) {
......@@ -315,9 +373,97 @@ static int fmc_dio_int_cmd_inout(struct fmc_dio *dev,
/* Appends to iomode TERM and OUTPUT_ENABLE_N bits */
iomode |= (((bits & WR_DIO_INOUT_TERM) != 0) << 3)
| (((bits & WR_DIO_INOUT_OUTPUT) == 0) << 2);
writel(reg | (iomode << 4*ch), &dio->IOMODE);
writel(reg | (iomode << 4*ch), base + dio.iomode_reg);
}
return 0;
}
static int fmc_dio_int_cmd_irq(struct fmc_dio *dev,
struct wr_dio_cmd *cmd)
{
void __iomem *base = dev->dio;
struct PPSG_WB __iomem *ppsg = dev->ppsg;
struct dio_device *d = dev->priv;
struct dio_channel *c;
struct regmap *map;
struct timespec *ts;
uint32_t reg;
int ch;
struct regmap_common dio;
if(dev->version != 1)
return -ENOTSUPP;
printk("IRQ");
dio = get_regmap_common(dev->version);
ch = 5;
c = d->ch + ch;
//map = regmap + ch;
map = get_regmap(dev->version);
map += ch;
ts = cmd->t;
/* First, configure the IRQ channel */
reg = readl(base + dio.iomode_reg);
writel((reg | CHANNEL_5_IRQ_EN_MASK), base + dio.iomode_reg);
writel(100, base + map->pulse); /* width */
if (cmd->flags & WR_DIO_F_NOW) {
printk("NOW");
/* Generate a pulse train from current time*/
if (cmd->flags & WR_DIO_F_LOOP && cmd->value != 1) {
printk("loop enabled %lu, %d", ts[1].tv_nsec, cmd->value);
writel(ts[1].tv_nsec / 8, base + map->pulse_per);
c->target_channel = ch;
if (cmd->value > 1) {
cmd->value-=2;
}
atomic_set(&c->count, cmd->value);
}
printk("writing %d in 0x%08x",1 << ch, dio.pulse_reg);
writel(1 << ch, base + dio.pulse_reg);
return 0;
}
/* if relative, add current 40-bit second to timespec */
if (cmd->flags & WR_DIO_F_REL) {
uint32_t h1, l, h2;
unsigned long now;
printk("with relative time");
h1 = readl(&ppsg->CNTR_UTCHI);
l = readl(&ppsg->CNTR_UTCLO);
h2 = readl(&ppsg->CNTR_UTCHI);
if (h2 != h1)
l = readl(&ppsg->CNTR_UTCLO);
now = l;
SET_HI32(now, h2);
ts->tv_sec += now;
}
if (cmd->flags & WR_DIO_F_LOOP && cmd->value != 1) {
printk("loop enabled");
c->target_channel = ch;
printk("%d times", cmd->value);
writel(ts[1].tv_nsec / 8, base + map->pulse_per);
if (cmd->value > 1) {
cmd->value-=2;
}
atomic_set(&c->count, cmd->value);
c->prevts = ts[0]; /* our current setpoint */
}
__fmc_dio_int_new_pulse(dev, ch, ts);
return 0;
}
......@@ -359,6 +505,9 @@ int fmc_dio_int_ioctl(struct fmc_dio *dev, unsigned int ioctlcmd,
case WR_DIO_CMD_DAC:
ret = -ENOTSUPP;
goto out;
case WR_DIO_CMD_IRQ:
ret = fmc_dio_int_cmd_irq(dev, cmd);
break;
default:
ret = -EINVAL;
goto out;
......@@ -397,7 +546,7 @@ static void fmc_dio_int_trig_next_pulse(struct fmc_dio *dev, int ch,
irqreturn_t fmc_dio_int_interrupt(struct fmc_dio *dev)
{
struct DIO_WB __iomem *dio = dev->dio;
//struct DIO_WB __iomem *dio = dev->dio;
struct fmc_device *fmc = dev->fmc;
void __iomem *base = dev->dio;
struct dio_device *d = dev->priv;
......@@ -409,10 +558,13 @@ irqreturn_t fmc_dio_int_interrupt(struct fmc_dio *dev)
uint32_t mask, reg;
int ch, chm;
struct regmap_common dio;
dio = get_regmap_common(dev->version);
if (unlikely(!fmc->eeprom)) {
dev_err(fmc->hwdev, "WR-DIO: No mezzanine, disabling irqs\n");
writel(~0, &dio->EIC_IDR);
writel(~0, &dio->EIC_ISR);
writel(~0, base + dio.eic_idr_reg);
writel(~0, base + dio.eic_isr_reg);
return IRQ_NONE;
}
......@@ -439,7 +591,10 @@ irqreturn_t fmc_dio_int_interrupt(struct fmc_dio *dev)
}
}
mask = readl(&dio->EIC_ISR) & FMC_DIO_IRQ_MASK;
if(dev->version == 0)
mask = readl(base + dio.eic_isr_reg) & FMC_DIO_IRQ_MASK;
else
mask = readl(base + dio.eic_isr_reg) & FMC_DIO_IRQ_MASK_V2;
/* Three indexes: channel, channel-mask, channel pointer */
for (ch = 0, chm = 1, c = d->ch; mask; ch++, chm <<= 1, c++) {
......@@ -450,12 +605,16 @@ irqreturn_t fmc_dio_int_interrupt(struct fmc_dio *dev)
mask &= ~chm;
/* Pull the FIFOs to the device structure */
map = regmap + ch;
//map = regmap + ch;
map = get_regmap(dev->version);
map += ch;
ts = NULL;
while (1) {
reg = readl(base + map->fifo_status);
if (reg & 0x20000) /* empty */
break;
printk("channel %d not empty", ch);
h = c->bhead;
ts = c->tsbuf + h;
c->bhead = (h + 1) % FMC_DIO_BUFFER_LEN;
......@@ -472,10 +631,32 @@ irqreturn_t fmc_dio_int_interrupt(struct fmc_dio *dev)
/* subtract 5 cycles lost in input sync circuits */
fmc_dio_int_ts_sub(ts, 40);
}
writel(chm, &dio->EIC_ISR); /* ack */
if (ts && atomic_read(&c->count) != 0) {
fmc_dio_int_trig_next_pulse(dev, ch, c, ts);
writel(chm, base + dio.eic_isr_reg); /* ack */
if(dev->version == 0) {
if (ts && atomic_read(&c->count) != 0 ) {
/* Only program new pulse if we are on the DIO V1 */
fmc_dio_int_trig_next_pulse(dev, ch, c, ts);
}
}
else
{
printk("channel %d %d", ch, atomic_read(&c->count));
if(atomic_read(&c->count) == 0) {
/* If we are on the DIO V2, clear pulse period register
* when all the pulses have been generated
*/
writel(0, base + map->pulse_per);
writel(1 << ch, base + dio.latch_reg);
}
else {
/* If the count is not-infinite, decrement it */
if (atomic_read(&c->count) > 0)
atomic_dec(&c->count);
}
}
wake_up_interruptible(&c->q);
}
t_end = ktime_get();
......@@ -485,10 +666,14 @@ irqreturn_t fmc_dio_int_interrupt(struct fmc_dio *dev)
/* Init and exit below are called when a netdevice is created/destroyed */
int fmc_dio_internal_create(struct fmc_dio *dev)
{
struct DIO_WB __iomem *dio = dev->dio;
//struct DIO_WB __iomem *dio = dev->dio;
struct dio_device *d;
void __iomem *base = dev->dio;
int i;
struct regmap_common dio;
dio = get_regmap_common(dev->version);
/* Allocate the data structure and enable interrupts for stamping */
d = kzalloc(sizeof(*d), GFP_KERNEL);
if (!d)
......@@ -501,15 +686,23 @@ int fmc_dio_internal_create(struct fmc_dio *dev)
* Enable interrupts for FIFO, if there's no mezzanine the
* handler will notice and disable the interrupts
*/
writel(FMC_DIO_IRQ_MASK, &dio->EIC_IER);
//writel(FMC_DIO_IRQ_MASK, base + dio.eic_ier_reg);
if(dev->version == 0)
writel(FMC_DIO_IRQ_MASK, base + dio.eic_ier_reg);
else
writel(FMC_DIO_IRQ_MASK_V2, base + dio.eic_ier_reg);
return 0;
}
void fmc_dio_internal_destroy(struct fmc_dio *dev)
{
struct DIO_WB __iomem *dio = dev->dio;
//struct DIO_WB __iomem *dio = dev->dio;
struct regmap_common dio;
void __iomem *base = dev->dio;
dio = get_regmap_common(dev->version);
writel(~0, &dio->EIC_IDR);
writel(~0, base + dio.eic_idr_reg);
if (dev->priv)
kfree(dev->priv);
}
......
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