Commit 5a213fa6 authored by H Hartley Sweeten's avatar H Hartley Sweeten Committed by Greg Kroah-Hartman

staging: comedi: amplc_dio200_common: convert driver to use the comedi_8254 module

Convert this driver to use the comedi_8254 module to provide the 8254 timer support.

Add 'clock_src' and 'gate_src' members to the comedi_8254 data for convienence.
Signed-off-by: default avatarH Hartley Sweeten <hsweeten@visionengravers.com>
Reviewed-by: default avatarIan Abbott <abbotti@mev.co.uk>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 99ca4e46
...@@ -1275,6 +1275,7 @@ config COMEDI_KCOMEDILIB ...@@ -1275,6 +1275,7 @@ config COMEDI_KCOMEDILIB
called kcomedilib. called kcomedilib.
config COMEDI_AMPLC_DIO200 config COMEDI_AMPLC_DIO200
select COMEDI_8254
tristate tristate
config COMEDI_AMPLC_PC236 config COMEDI_AMPLC_PC236
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
#include "amplc_dio200.h" #include "amplc_dio200.h"
#include "comedi_fc.h" #include "comedi_fc.h"
#include "8253.h" #include "comedi_8254.h"
#include "8255.h" /* only for register defines */ #include "8255.h" /* only for register defines */
/* 200 series registers */ /* 200 series registers */
...@@ -97,12 +97,6 @@ static const unsigned int ts_clock_period[TS_CONFIG_MAX_CLK_SRC + 1] = { ...@@ -97,12 +97,6 @@ static const unsigned int ts_clock_period[TS_CONFIG_MAX_CLK_SRC + 1] = {
1000000, /* 1 millisecond. */ 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 { struct dio200_subdev_8255 {
unsigned int ofs; /* DIO base offset */ unsigned int ofs; /* DIO base offset */
}; };
...@@ -169,6 +163,27 @@ static void dio200_write32(struct comedi_device *dev, ...@@ -169,6 +163,27 @@ static void dio200_write32(struct comedi_device *dev,
outl(val, dev->iobase + offset); 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, static int dio200_subdev_intr_insn_bits(struct comedi_device *dev,
struct comedi_subdevice *s, struct comedi_subdevice *s,
struct comedi_insn *insn, struct comedi_insn *insn,
...@@ -474,211 +489,71 @@ static irqreturn_t dio200_interrupt(int irq, void *d) ...@@ -474,211 +489,71 @@ static irqreturn_t dio200_interrupt(int irq, void *d)
return IRQ_RETVAL(handled); return IRQ_RETVAL(handled);
} }
static unsigned int dio200_subdev_8254_read_chan(struct comedi_device *dev, static void dio200_subdev_8254_set_gate_src(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, struct comedi_subdevice *s,
unsigned int chan, unsigned int chan,
unsigned int count) unsigned int src)
{ {
struct dio200_subdev_8254 *subpriv = s->private; unsigned int offset = dio200_subdev_8254_offset(dev, s);
/* write lsb, msb */ dio200_write8(dev, DIO200_GAT_SCE(offset >> 3),
dio200_write8(dev, subpriv->ofs + chan, count & 0xff); gat_sce((offset >> 2) & 1, chan, src));
dio200_write8(dev, subpriv->ofs + chan, (count >> 8) & 0xff);
} }
static void dio200_subdev_8254_set_mode(struct comedi_device *dev, static void dio200_subdev_8254_set_clock_src(struct comedi_device *dev,
struct comedi_subdevice *s, struct comedi_subdevice *s,
unsigned int chan, unsigned int chan,
unsigned int mode) unsigned int src)
{
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 offset = dio200_subdev_8254_offset(dev, s);
unsigned int n;
for (n = 0; n < insn->n; n++)
data[n] = dio200_subdev_8254_read_chan(dev, s, chan);
return insn->n; dio200_write8(dev, DIO200_CLK_SCE(offset >> 3),
clk_sce((offset >> 2) & 1, chan, src));
} }
static int dio200_subdev_8254_write(struct comedi_device *dev, static int dio200_subdev_8254_config(struct comedi_device *dev,
struct comedi_subdevice *s, struct comedi_subdevice *s,
struct comedi_insn *insn, struct comedi_insn *insn,
unsigned int *data) 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; const struct dio200_board *board = dev->board_ptr;
struct dio200_subdev_8254 *subpriv = s->private; struct comedi_8254 *i8254 = s->private;
unsigned char byte; unsigned int chan = CR_CHAN(insn->chanspec);
unsigned int max_src = board->is_pcie ? 31 : 7;
if (!board->has_clk_gat_sce) unsigned int src;
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,
struct comedi_subdevice *s,
unsigned int counter_number,
unsigned int clock_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);
return 0;
}
static int dio200_subdev_8254_get_clock_src(struct comedi_device *dev,
struct comedi_subdevice *s,
unsigned int counter_number,
unsigned int *period_ns)
{
const struct dio200_board *board = dev->board_ptr;
struct dio200_subdev_8254 *subpriv = s->private;
unsigned clock_src;
if (!board->has_clk_gat_sce) if (!board->has_clk_gat_sce)
return -1; return -EINVAL;
clock_src = subpriv->clock_src[counter_number];
*period_ns = clock_period[clock_src];
return clock_src;
}
static int dio200_subdev_8254_config(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
{
int ret = 0;
int chan = CR_CHAN(insn->chanspec);
switch (data[0]) { 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: case INSN_CONFIG_SET_GATE_SRC:
ret = dio200_subdev_8254_set_gate_src(dev, s, chan, data[2]); src = data[2];
if (ret < 0) if (src > max_src)
ret = -EINVAL; return -EINVAL;
dio200_subdev_8254_set_gate_src(dev, s, chan, src);
i8254->gate_src[chan] = src;
break; break;
case INSN_CONFIG_GET_GATE_SRC: case INSN_CONFIG_GET_GATE_SRC:
ret = dio200_subdev_8254_get_gate_src(dev, s, chan); data[2] = i8254->gate_src[chan];
if (ret < 0) {
ret = -EINVAL;
break;
}
data[2] = ret;
break; break;
case INSN_CONFIG_SET_CLOCK_SRC: case INSN_CONFIG_SET_CLOCK_SRC:
ret = dio200_subdev_8254_set_clock_src(dev, s, chan, data[1]); src = data[1];
if (ret < 0) if (src > max_src)
ret = -EINVAL; return -EINVAL;
dio200_subdev_8254_set_clock_src(dev, s, chan, src);
i8254->clock_src[chan] = src;
break; break;
case INSN_CONFIG_GET_CLOCK_SRC: case INSN_CONFIG_GET_CLOCK_SRC:
ret = dio200_subdev_8254_get_clock_src(dev, s, chan, &data[2]); data[1] = i8254->clock_src[chan];
if (ret < 0) { data[2] = clock_period[i8254->clock_src[chan]];
ret = -EINVAL;
break;
}
data[1] = ret;
break; break;
default: default:
ret = -EINVAL; return -EINVAL;
break;
} }
return ret < 0 ? ret : insn->n; return insn->n;
} }
static int dio200_subdev_8254_init(struct comedi_device *dev, static int dio200_subdev_8254_init(struct comedi_device *dev,
...@@ -686,28 +561,46 @@ 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) unsigned int offset)
{ {
const struct dio200_board *board = dev->board_ptr; const struct dio200_board *board = dev->board_ptr;
struct dio200_subdev_8254 *subpriv; struct comedi_8254 *i8254;
unsigned int chan; unsigned int regshift;
int chan;
subpriv = comedi_alloc_spriv(s, sizeof(*subpriv)); /*
if (!subpriv) * 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;
}
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; return -ENOMEM;
s->type = COMEDI_SUBD_COUNTER; comedi_8254_subdevice_init(s, i8254);
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;
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. */ /* 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). */ /* Gate source 0 is VCC (logic 1). */
dio200_subdev_8254_set_gate_src(dev, s, chan, 0); dio200_subdev_8254_set_gate_src(dev, s, chan, 0);
/* Clock source 0 is the dedicated clock input. */ /* Clock source 0 is the dedicated clock input. */
......
...@@ -71,6 +71,8 @@ ...@@ -71,6 +71,8 @@
* #next_div: next divisor for single counter * #next_div: next divisor for single counter
* @next_div1: next divisor to use for first cascaded counter * @next_div1: next divisor to use for first cascaded counter
* @next_div2: next divisor to use for second cascaded counter * @next_div2: next divisor to use for second cascaded counter
* @clock_src; current clock source for each counter (driver specific)
* @gate_src; current gate source for each counter (driver specific)
* @busy: flags used to indicate that a counter is "busy" * @busy: flags used to indicate that a counter is "busy"
* @insn_config: driver specific (*insn_config) callback * @insn_config: driver specific (*insn_config) callback
*/ */
...@@ -86,6 +88,8 @@ struct comedi_8254 { ...@@ -86,6 +88,8 @@ struct comedi_8254 {
unsigned int next_div; unsigned int next_div;
unsigned int next_div1; unsigned int next_div1;
unsigned int next_div2; unsigned int next_div2;
unsigned int clock_src[3];
unsigned int gate_src[3];
bool busy[3]; bool busy[3];
int (*insn_config)(struct comedi_device *, struct comedi_subdevice *s, int (*insn_config)(struct comedi_device *, struct comedi_subdevice *s,
......
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