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