The board has an 8254 timer/counter. Add support for this subdevice. Signed-off-by: H Hartley Sweeten <hsweeten@xxxxxxxxxxxxxxxxxxx> Cc: Ian Abbott <abbotti@xxxxxxxxx> Cc: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx> --- drivers/staging/comedi/drivers/multiq3.c | 147 ++++++++++++++++++++++++++++++- 1 file changed, 146 insertions(+), 1 deletion(-) diff --git a/drivers/staging/comedi/drivers/multiq3.c b/drivers/staging/comedi/drivers/multiq3.c index fc743df..50efc7c 100644 --- a/drivers/staging/comedi/drivers/multiq3.c +++ b/drivers/staging/comedi/drivers/multiq3.c @@ -38,6 +38,8 @@ #include "../comedidev.h" +#include "comedi_8254.h" /* just for the register map */ + /* * Register map */ @@ -47,6 +49,8 @@ #define MULTIQ3_AI_REG 0x04 #define MULTIQ3_AI_CONV_REG 0x04 #define MULTIQ3_STATUS_REG 0x06 +#define MULTIQ3_STATUS_CT(x) (((x) == 0) ? BIT(0) : \ + ((x) == 1) ? BIT(2) : BIT(1)) #define MULTIQ3_STATUS_EOC BIT(3) #define MULTIQ3_STATUS_EOC_I BIT(4) #define MULTIQ3_CTRL_REG 0x06 @@ -257,6 +261,126 @@ static int multiq3_encoder_insn_config(struct comedi_device *dev, return insn->n; } +static unsigned int multiq3_i8254_read(struct comedi_device *dev, + unsigned int reg) +{ + /* select the 8254 register then read the value */ + multiq3_set_ctrl(dev, MULTIQ3_CTRL_RC(reg)); + return inb(dev->iobase + MULTIQ3_CLK_REG); +} + +static void multiq3_i8254_write(struct comedi_device *dev, + unsigned int val, unsigned int reg) +{ + /* select the 8254 register then write the value */ + multiq3_set_ctrl(dev, MULTIQ3_CTRL_RC(reg)); + outb(val, dev->iobase + MULTIQ3_CLK_REG); +} + +static int multiq3_8254_set_mode(struct comedi_device *dev, + unsigned int chan, unsigned int mode) +{ + unsigned int byte; + + if (mode > (I8254_MODE5 | I8254_BCD)) + return -EINVAL; + + byte = I8254_CTRL_SEL_CTR(chan) | /* select counter */ + I8254_CTRL_LSB_MSB | /* load LSB then MSB */ + mode; /* mode and BCD|binary */ + multiq3_i8254_write(dev, byte, I8254_CTRL_REG); + + return 0; +} + +static int multiq3_8254_insn_read(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) +{ + unsigned int chan = CR_CHAN(insn->chanspec); + unsigned int ctrl = I8254_CTRL_LATCH | I8254_CTRL_SEL_CTR(chan); + unsigned int val; + int i; + + for (i = 0; i < insn->n; i++) { + /* latch counter */ + multiq3_i8254_write(dev, ctrl, I8254_CTRL_REG); + + /* read LSB then MSB */ + val = multiq3_i8254_read(dev, chan); + val |= (multiq3_i8254_read(dev, chan) << 8); + + data[i] = val; + } + + return insn->n; +} + +static int multiq3_8254_insn_write(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) +{ + unsigned int chan = CR_CHAN(insn->chanspec); + + if (insn->n) { + unsigned int val = data[insn->n - 1]; + + /* load LSB then MSB */ + multiq3_i8254_write(dev, val & 0xff, chan); + multiq3_i8254_write(dev, (val >> 8) & 0xff, chan); + } + + return insn->n; +} + +static int multiq3_8254_insn_config(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) +{ + unsigned int chan = CR_CHAN(insn->chanspec); + unsigned int status; + int ret; + + switch (data[0]) { + case INSN_CONFIG_RESET: + ret = multiq3_8254_set_mode(dev, chan, + I8254_MODE0 | I8254_BINARY); + if (ret) + return ret; + break; + case INSN_CONFIG_SET_COUNTER_MODE: + ret = multiq3_8254_set_mode(dev, chan, data[1]); + if (ret) + return ret; + break; + case INSN_CONFIG_GET_COUNTER_STATUS: + data[1] = 0; + status = inw(dev->iobase + MULTIQ3_STATUS_REG); + if (status & MULTIQ3_STATUS_CT(chan)) + data[1] |= COMEDI_COUNTER_TERMINAL_COUNT; + data[2] = COMEDI_COUNTER_TERMINAL_COUNT; + break; + case INSN_CONFIG_8254_READ_STATUS: + multiq3_i8254_write(dev, I8254_CTRL_READBACK_STATUS | + I8254_CTRL_READBACK_SEL_CTR(chan), + I8254_CTRL_REG); + + data[1] = multiq3_i8254_read(dev, chan); + break; + case INSN_CONFIG_GET_CLOCK_SRC: + data[1] = 0; + data[2] = I8254_OSC_BASE_2MHZ; + break; + default: + return -EINVAL; + } + + return insn->n; +} + static int multiq3_attach(struct comedi_device *dev, struct comedi_devconfig *it) { @@ -268,7 +392,7 @@ static int multiq3_attach(struct comedi_device *dev, if (ret) return ret; - ret = comedi_alloc_subdevices(dev, 5); + ret = comedi_alloc_subdevices(dev, 6); if (ret) return ret; @@ -325,6 +449,27 @@ static int multiq3_attach(struct comedi_device *dev, for (i = 0; i < s->n_chan; i++) multiq3_encoder_reset(dev, i); + /* + * 8254 Counter/Timer subdevice + * + * This board uses the control register to address the four + * registers in the 8254 timer. Because of this, the comedi_8254 + * driver cannot be used to provide support for the timer. + */ + s = &dev->subdevices[5]; + s->type = COMEDI_SUBD_COUNTER; + s->subdev_flags = SDF_READABLE | SDF_WRITABLE; + s->n_chan = 3; + s->maxdata = 0xffff; + s->range_table = &range_unknown; + s->insn_read = multiq3_8254_insn_read; + s->insn_write = multiq3_8254_insn_write; + s->insn_config = multiq3_8254_insn_config; + + /* reset all the counters by setting them to I8254_MODE0 */ + for (i = 0; i < 3; i++) + multiq3_8254_set_mode(dev, i, I8254_MODE0 | I8254_BINARY); + return 0; } -- 2.5.1 _______________________________________________ devel mailing list devel@xxxxxxxxxxxxxxxxxxxxxx http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel