|
@@ -26,7 +26,7 @@
|
|
|
|
|
|
#include "amplc_dio200.h"
|
|
|
#include "comedi_fc.h"
|
|
|
-#include "8253.h"
|
|
|
+#include "comedi_8254.h"
|
|
|
#include "8255.h" /* only for register defines */
|
|
|
|
|
|
/* 200 series registers */
|
|
@@ -97,12 +97,6 @@ static const unsigned int ts_clock_period[TS_CONFIG_MAX_CLK_SRC + 1] = {
|
|
|
1000000, /* 1 millisecond. */
|
|
|
};
|
|
|
|
|
|
-struct dio200_subdev_8254 {
|
|
|
- unsigned int ofs; /* Counter base offset */
|
|
|
- unsigned int clock_src[3]; /* Current clock sources */
|
|
|
- unsigned int gate_src[3]; /* Current gate sources */
|
|
|
-};
|
|
|
-
|
|
|
struct dio200_subdev_8255 {
|
|
|
unsigned int ofs; /* DIO base offset */
|
|
|
};
|
|
@@ -169,6 +163,27 @@ static void dio200_write32(struct comedi_device *dev,
|
|
|
outl(val, dev->iobase + offset);
|
|
|
}
|
|
|
|
|
|
+static unsigned int dio200_subdev_8254_offset(struct comedi_device *dev,
|
|
|
+ struct comedi_subdevice *s)
|
|
|
+{
|
|
|
+ const struct dio200_board *board = dev->board_ptr;
|
|
|
+ struct comedi_8254 *i8254 = s->private;
|
|
|
+ unsigned int offset;
|
|
|
+
|
|
|
+ /* get the offset that was passed to comedi_8254_*_init() */
|
|
|
+ if (dev->mmio)
|
|
|
+ offset = i8254->mmio - dev->mmio;
|
|
|
+ else
|
|
|
+ offset = i8254->iobase - dev->iobase;
|
|
|
+
|
|
|
+ /* remove the shift that was added for PCIe boards */
|
|
|
+ if (board->is_pcie)
|
|
|
+ offset >>= 3;
|
|
|
+
|
|
|
+ /* this offset now works for the dio200_{read,write} helpers */
|
|
|
+ return offset;
|
|
|
+}
|
|
|
+
|
|
|
static int dio200_subdev_intr_insn_bits(struct comedi_device *dev,
|
|
|
struct comedi_subdevice *s,
|
|
|
struct comedi_insn *insn,
|
|
@@ -474,159 +489,26 @@ static irqreturn_t dio200_interrupt(int irq, void *d)
|
|
|
return IRQ_RETVAL(handled);
|
|
|
}
|
|
|
|
|
|
-static unsigned int dio200_subdev_8254_read_chan(struct comedi_device *dev,
|
|
|
- struct comedi_subdevice *s,
|
|
|
- unsigned int chan)
|
|
|
-{
|
|
|
- struct dio200_subdev_8254 *subpriv = s->private;
|
|
|
- unsigned int val;
|
|
|
-
|
|
|
- /* latch counter */
|
|
|
- val = chan << 6;
|
|
|
- dio200_write8(dev, subpriv->ofs + i8254_control_reg, val);
|
|
|
- /* read lsb, msb */
|
|
|
- val = dio200_read8(dev, subpriv->ofs + chan);
|
|
|
- val += dio200_read8(dev, subpriv->ofs + chan) << 8;
|
|
|
- return val;
|
|
|
-}
|
|
|
-
|
|
|
-static void dio200_subdev_8254_write_chan(struct comedi_device *dev,
|
|
|
- struct comedi_subdevice *s,
|
|
|
- unsigned int chan,
|
|
|
- unsigned int count)
|
|
|
-{
|
|
|
- struct dio200_subdev_8254 *subpriv = s->private;
|
|
|
-
|
|
|
- /* write lsb, msb */
|
|
|
- dio200_write8(dev, subpriv->ofs + chan, count & 0xff);
|
|
|
- dio200_write8(dev, subpriv->ofs + chan, (count >> 8) & 0xff);
|
|
|
-}
|
|
|
-
|
|
|
-static void dio200_subdev_8254_set_mode(struct comedi_device *dev,
|
|
|
- struct comedi_subdevice *s,
|
|
|
- unsigned int chan,
|
|
|
- unsigned int mode)
|
|
|
-{
|
|
|
- struct dio200_subdev_8254 *subpriv = s->private;
|
|
|
- unsigned int byte;
|
|
|
-
|
|
|
- byte = chan << 6;
|
|
|
- byte |= 0x30; /* access order: lsb, msb */
|
|
|
- byte |= (mode & 0xf); /* counter mode and BCD|binary */
|
|
|
- dio200_write8(dev, subpriv->ofs + i8254_control_reg, byte);
|
|
|
-}
|
|
|
-
|
|
|
-static unsigned int dio200_subdev_8254_status(struct comedi_device *dev,
|
|
|
- struct comedi_subdevice *s,
|
|
|
- unsigned int chan)
|
|
|
-{
|
|
|
- struct dio200_subdev_8254 *subpriv = s->private;
|
|
|
-
|
|
|
- /* latch status */
|
|
|
- dio200_write8(dev, subpriv->ofs + i8254_control_reg,
|
|
|
- 0xe0 | (2 << chan));
|
|
|
- /* read status */
|
|
|
- return dio200_read8(dev, subpriv->ofs + chan);
|
|
|
-}
|
|
|
-
|
|
|
-static int dio200_subdev_8254_read(struct comedi_device *dev,
|
|
|
- struct comedi_subdevice *s,
|
|
|
- struct comedi_insn *insn,
|
|
|
- unsigned int *data)
|
|
|
-{
|
|
|
- int chan = CR_CHAN(insn->chanspec);
|
|
|
- unsigned int n;
|
|
|
-
|
|
|
- for (n = 0; n < insn->n; n++)
|
|
|
- data[n] = dio200_subdev_8254_read_chan(dev, s, chan);
|
|
|
-
|
|
|
- return insn->n;
|
|
|
-}
|
|
|
-
|
|
|
-static int dio200_subdev_8254_write(struct comedi_device *dev,
|
|
|
- struct comedi_subdevice *s,
|
|
|
- struct comedi_insn *insn,
|
|
|
- unsigned int *data)
|
|
|
-{
|
|
|
- int chan = CR_CHAN(insn->chanspec);
|
|
|
- unsigned int n;
|
|
|
-
|
|
|
- for (n = 0; n < insn->n; n++)
|
|
|
- dio200_subdev_8254_write_chan(dev, s, chan, data[n]);
|
|
|
-
|
|
|
- return insn->n;
|
|
|
-}
|
|
|
-
|
|
|
-static int dio200_subdev_8254_set_gate_src(struct comedi_device *dev,
|
|
|
- struct comedi_subdevice *s,
|
|
|
- unsigned int counter_number,
|
|
|
- unsigned int gate_src)
|
|
|
-{
|
|
|
- const struct dio200_board *board = dev->board_ptr;
|
|
|
- struct dio200_subdev_8254 *subpriv = s->private;
|
|
|
- unsigned char byte;
|
|
|
-
|
|
|
- if (!board->has_clk_gat_sce)
|
|
|
- return -1;
|
|
|
- if (gate_src > (board->is_pcie ? 31 : 7))
|
|
|
- return -1;
|
|
|
-
|
|
|
- subpriv->gate_src[counter_number] = gate_src;
|
|
|
- byte = gat_sce((subpriv->ofs >> 2) & 1, counter_number, gate_src);
|
|
|
- dio200_write8(dev, DIO200_GAT_SCE(subpriv->ofs >> 3), byte);
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static int dio200_subdev_8254_get_gate_src(struct comedi_device *dev,
|
|
|
- struct comedi_subdevice *s,
|
|
|
- unsigned int counter_number)
|
|
|
-{
|
|
|
- const struct dio200_board *board = dev->board_ptr;
|
|
|
- struct dio200_subdev_8254 *subpriv = s->private;
|
|
|
-
|
|
|
- if (!board->has_clk_gat_sce)
|
|
|
- return -1;
|
|
|
-
|
|
|
- return subpriv->gate_src[counter_number];
|
|
|
-}
|
|
|
-
|
|
|
-static int dio200_subdev_8254_set_clock_src(struct comedi_device *dev,
|
|
|
+static void dio200_subdev_8254_set_gate_src(struct comedi_device *dev,
|
|
|
struct comedi_subdevice *s,
|
|
|
- unsigned int counter_number,
|
|
|
- unsigned int clock_src)
|
|
|
+ unsigned int chan,
|
|
|
+ unsigned int src)
|
|
|
{
|
|
|
- const struct dio200_board *board = dev->board_ptr;
|
|
|
- struct dio200_subdev_8254 *subpriv = s->private;
|
|
|
- unsigned char byte;
|
|
|
-
|
|
|
- if (!board->has_clk_gat_sce)
|
|
|
- return -1;
|
|
|
- if (clock_src > (board->is_pcie ? 31 : 7))
|
|
|
- return -1;
|
|
|
-
|
|
|
- subpriv->clock_src[counter_number] = clock_src;
|
|
|
- byte = clk_sce((subpriv->ofs >> 2) & 1, counter_number, clock_src);
|
|
|
- dio200_write8(dev, DIO200_CLK_SCE(subpriv->ofs >> 3), byte);
|
|
|
+ unsigned int offset = dio200_subdev_8254_offset(dev, s);
|
|
|
|
|
|
- return 0;
|
|
|
+ dio200_write8(dev, DIO200_GAT_SCE(offset >> 3),
|
|
|
+ gat_sce((offset >> 2) & 1, chan, src));
|
|
|
}
|
|
|
|
|
|
-static int dio200_subdev_8254_get_clock_src(struct comedi_device *dev,
|
|
|
- struct comedi_subdevice *s,
|
|
|
- unsigned int counter_number,
|
|
|
- unsigned int *period_ns)
|
|
|
+static void dio200_subdev_8254_set_clock_src(struct comedi_device *dev,
|
|
|
+ struct comedi_subdevice *s,
|
|
|
+ unsigned int chan,
|
|
|
+ unsigned int src)
|
|
|
{
|
|
|
- const struct dio200_board *board = dev->board_ptr;
|
|
|
- struct dio200_subdev_8254 *subpriv = s->private;
|
|
|
- unsigned clock_src;
|
|
|
-
|
|
|
- if (!board->has_clk_gat_sce)
|
|
|
- return -1;
|
|
|
+ unsigned int offset = dio200_subdev_8254_offset(dev, s);
|
|
|
|
|
|
- clock_src = subpriv->clock_src[counter_number];
|
|
|
- *period_ns = clock_period[clock_src];
|
|
|
- return clock_src;
|
|
|
+ dio200_write8(dev, DIO200_CLK_SCE(offset >> 3),
|
|
|
+ clk_sce((offset >> 2) & 1, chan, src));
|
|
|
}
|
|
|
|
|
|
static int dio200_subdev_8254_config(struct comedi_device *dev,
|
|
@@ -634,51 +516,44 @@ static int dio200_subdev_8254_config(struct comedi_device *dev,
|
|
|
struct comedi_insn *insn,
|
|
|
unsigned int *data)
|
|
|
{
|
|
|
- int ret = 0;
|
|
|
- int chan = CR_CHAN(insn->chanspec);
|
|
|
+ const struct dio200_board *board = dev->board_ptr;
|
|
|
+ struct comedi_8254 *i8254 = s->private;
|
|
|
+ unsigned int chan = CR_CHAN(insn->chanspec);
|
|
|
+ unsigned int max_src = board->is_pcie ? 31 : 7;
|
|
|
+ unsigned int src;
|
|
|
+
|
|
|
+ if (!board->has_clk_gat_sce)
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
switch (data[0]) {
|
|
|
- case INSN_CONFIG_SET_COUNTER_MODE:
|
|
|
- if (data[1] > (I8254_MODE5 | I8254_BCD))
|
|
|
- ret = -EINVAL;
|
|
|
- else
|
|
|
- dio200_subdev_8254_set_mode(dev, s, chan, data[1]);
|
|
|
- break;
|
|
|
- case INSN_CONFIG_8254_READ_STATUS:
|
|
|
- data[1] = dio200_subdev_8254_status(dev, s, chan);
|
|
|
- break;
|
|
|
case INSN_CONFIG_SET_GATE_SRC:
|
|
|
- ret = dio200_subdev_8254_set_gate_src(dev, s, chan, data[2]);
|
|
|
- if (ret < 0)
|
|
|
- ret = -EINVAL;
|
|
|
+ src = data[2];
|
|
|
+ if (src > max_src)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ dio200_subdev_8254_set_gate_src(dev, s, chan, src);
|
|
|
+ i8254->gate_src[chan] = src;
|
|
|
break;
|
|
|
case INSN_CONFIG_GET_GATE_SRC:
|
|
|
- ret = dio200_subdev_8254_get_gate_src(dev, s, chan);
|
|
|
- if (ret < 0) {
|
|
|
- ret = -EINVAL;
|
|
|
- break;
|
|
|
- }
|
|
|
- data[2] = ret;
|
|
|
+ data[2] = i8254->gate_src[chan];
|
|
|
break;
|
|
|
case INSN_CONFIG_SET_CLOCK_SRC:
|
|
|
- ret = dio200_subdev_8254_set_clock_src(dev, s, chan, data[1]);
|
|
|
- if (ret < 0)
|
|
|
- ret = -EINVAL;
|
|
|
+ src = data[1];
|
|
|
+ if (src > max_src)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ dio200_subdev_8254_set_clock_src(dev, s, chan, src);
|
|
|
+ i8254->clock_src[chan] = src;
|
|
|
break;
|
|
|
case INSN_CONFIG_GET_CLOCK_SRC:
|
|
|
- ret = dio200_subdev_8254_get_clock_src(dev, s, chan, &data[2]);
|
|
|
- if (ret < 0) {
|
|
|
- ret = -EINVAL;
|
|
|
- break;
|
|
|
- }
|
|
|
- data[1] = ret;
|
|
|
+ data[1] = i8254->clock_src[chan];
|
|
|
+ data[2] = clock_period[i8254->clock_src[chan]];
|
|
|
break;
|
|
|
default:
|
|
|
- ret = -EINVAL;
|
|
|
- break;
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
|
|
|
- return ret < 0 ? ret : insn->n;
|
|
|
+ return insn->n;
|
|
|
}
|
|
|
|
|
|
static int dio200_subdev_8254_init(struct comedi_device *dev,
|
|
@@ -686,28 +561,46 @@ static int dio200_subdev_8254_init(struct comedi_device *dev,
|
|
|
unsigned int offset)
|
|
|
{
|
|
|
const struct dio200_board *board = dev->board_ptr;
|
|
|
- struct dio200_subdev_8254 *subpriv;
|
|
|
- unsigned int chan;
|
|
|
+ struct comedi_8254 *i8254;
|
|
|
+ unsigned int regshift;
|
|
|
+ int chan;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * PCIe boards need the offset shifted in order to get the
|
|
|
+ * correct base address of the timer.
|
|
|
+ */
|
|
|
+ if (board->is_pcie) {
|
|
|
+ offset <<= 3;
|
|
|
+ regshift = 3;
|
|
|
+ } else {
|
|
|
+ regshift = 0;
|
|
|
+ }
|
|
|
|
|
|
- subpriv = comedi_alloc_spriv(s, sizeof(*subpriv));
|
|
|
- if (!subpriv)
|
|
|
+ if (dev->mmio)
|
|
|
+ i8254 = comedi_8254_mm_init(dev->mmio + offset,
|
|
|
+ 0, I8254_IO8, regshift);
|
|
|
+ else
|
|
|
+ i8254 = comedi_8254_init(dev->iobase + offset,
|
|
|
+ 0, I8254_IO8, regshift);
|
|
|
+ if (!i8254)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
- s->type = COMEDI_SUBD_COUNTER;
|
|
|
- s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
|
|
|
- s->n_chan = 3;
|
|
|
- s->maxdata = 0xFFFF;
|
|
|
- s->insn_read = dio200_subdev_8254_read;
|
|
|
- s->insn_write = dio200_subdev_8254_write;
|
|
|
- s->insn_config = dio200_subdev_8254_config;
|
|
|
+ comedi_8254_subdevice_init(s, i8254);
|
|
|
|
|
|
- subpriv->ofs = offset;
|
|
|
+ i8254->insn_config = dio200_subdev_8254_config;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * There could be multiple timers so this driver does not
|
|
|
+ * use dev->pacer to save the i8254 pointer. Instead,
|
|
|
+ * comedi_8254_subdevice_init() saved the i8254 pointer in
|
|
|
+ * s->private. Set the runflag bit so that the core will
|
|
|
+ * automatically free it when the driver is detached.
|
|
|
+ */
|
|
|
+ s->runflags |= COMEDI_SRF_FREE_SPRIV;
|
|
|
|
|
|
/* Initialize channels. */
|
|
|
- for (chan = 0; chan < 3; chan++) {
|
|
|
- dio200_subdev_8254_set_mode(dev, s, chan,
|
|
|
- I8254_MODE0 | I8254_BINARY);
|
|
|
- if (board->has_clk_gat_sce) {
|
|
|
+ if (board->has_clk_gat_sce) {
|
|
|
+ for (chan = 0; chan < 3; chan++) {
|
|
|
/* Gate source 0 is VCC (logic 1). */
|
|
|
dio200_subdev_8254_set_gate_src(dev, s, chan, 0);
|
|
|
/* Clock source 0 is the dedicated clock input. */
|