diff --git a/drivers/staging/comedi/drivers/ii_pci20kc.c b/drivers/staging/comedi/drivers/ii_pci20kc.c index 1119a1a33135ce311370e30a48ca33d3a7247c3b..069cd959ba4b17e84be4a4190df06ce5e38e7d57 100644 --- a/drivers/staging/comedi/drivers/ii_pci20kc.c +++ b/drivers/staging/comedi/drivers/ii_pci20kc.c @@ -55,21 +55,6 @@ the other carrier boards with small modifications. Modules supported Options: 0 Board base address 1 IRQ - 2 first option for module 1 - 3 second option for module 1 - 4 first option for module 2 - 5 second option for module 2 - 6 first option for module 3 - 7 second option for module 3 - -options for PCI-20006M: - -options for PCI-20341M: - first: Analog input gain configuration - 0 1 - 1 10 - 2 100 - 3 200 */ #include <linux/module.h> @@ -119,26 +104,44 @@ options for PCI-20341M: #define II20K_AO_MSB_REG(x) (0x0e + ((x) * 0x08)) #define II20K_AO_STRB_BOTH_REG 0x1b -#define PCI20341_ID 0x77 -#define PCI20xxx_EMPTY_ID 0xff - -#define PCI20341_INIT 0x04 -#define PCI20341_REPMODE 0x00 /* single shot mode */ -#define PCI20341_PACER 0x00 /* Hardware Pacer disabled */ -#define PCI20341_CONFIG_REG 0x10 -#define PCI20341_MOD_STATUS 0x01 -#define PCI20341_OPT_REG 0x11 -#define PCI20341_SET_TIME_REG 0x15 -#define PCI20341_LCHAN_ADDR_REG 0x13 -#define PCI20341_CHAN_LIST 0x80 -#define PCI20341_CC_RESET 0x1b -#define PCI20341_CHAN_RESET 0x19 -#define PCI20341_SOFT_PACER 0x04 -#define PCI20341_STATUS_REG 0x12 -#define PCI20341_LDATA 0x02 -#define PCI20341_DAISY_CHAIN 0x20 /* On-board inputs only */ -#define PCI20341_MUX 0x04 /* Enable on-board MUX */ -#define PCI20341_SCANLIST 0x80 /* Channel/Gain Scan List */ +#define II20K_ID_PCI200341M_1 0x77 /* 4 AI channels */ +#define II20K_AI_STATUS_CMD_REG 0x01 +#define II20K_AI_STATUS_CMD_BUSY (1 << 7) +#define II20K_AI_STATUS_CMD_HW_ENA (1 << 1) +#define II20K_AI_STATUS_CMD_EXT_START (1 << 0) +#define II20K_AI_LSB_REG 0x02 +#define II20K_AI_MSB_REG 0x03 +#define II20K_AI_PACER_RESET_REG 0x04 +#define II20K_AI_16BIT_DATA_REG 0x06 +#define II20K_AI_CONF_REG 0x10 +#define II20K_AI_CONF_ENA (1 << 2) +#define II20K_AI_OPT_REG 0x11 +#define II20K_AI_OPT_TRIG_ENA (1 << 5) +#define II20K_AI_OPT_TRIG_INV (1 << 4) +#define II20K_AI_OPT_TIMEBASE(x) (((x) & 0x3) << 1) +#define II20K_AI_OPT_BURST_MODE (1 << 0) +#define II20K_AI_STATUS_REG 0x12 +#define II20K_AI_STATUS_INT (1 << 7) +#define II20K_AI_STATUS_TRIG (1 << 6) +#define II20K_AI_STATUS_TRIG_ENA (1 << 5) +#define II20K_AI_STATUS_PACER_ERR (1 << 2) +#define II20K_AI_STATUS_DATA_ERR (1 << 1) +#define II20K_AI_STATUS_SET_TIME_ERR (1 << 0) +#define II20K_AI_LAST_CHAN_ADDR_REG 0x13 +#define II20K_AI_CUR_ADDR_REG 0x14 +#define II20K_AI_SET_TIME_REG 0x15 +#define II20K_AI_DELAY_LSB_REG 0x16 +#define II20K_AI_DELAY_MSB_REG 0x17 +#define II20K_AI_CHAN_ADV_REG 0x18 +#define II20K_AI_CHAN_RESET_REG 0x19 +#define II20K_AI_START_TRIG_REG 0x1a +#define II20K_AI_COUNT_RESET_REG 0x1b +#define II20K_AI_CHANLIST_REG 0x80 +#define II20K_AI_CHANLIST_ONBOARD_ONLY (1 << 5) +#define II20K_AI_CHANLIST_GAIN(x) (((x) & 0x3) << 3) +#define II20K_AI_CHANLIST_MUX_ENA (1 << 2) +#define II20K_AI_CHANLIST_CHAN(x) (((x) & 0x3) << 0) +#define II20K_AI_CHANLIST_LEN 0x80 /* the AO range is set by jumpers on the 20006M module */ static const struct comedi_lrange ii20k_ao_ranges = { @@ -148,44 +151,20 @@ static const struct comedi_lrange ii20k_ao_ranges = { BIP_RANGE(10) /* Chan 0 - W1/W3 in Chan 1 - W2/W4 out */ } }; -static const struct comedi_lrange range_bipolar0_5 = { - 1, { - BIP_RANGE(0.5) - } -}; - -static const struct comedi_lrange range_bipolar0_05 = { - 1, { - BIP_RANGE(0.05) - } -}; - -static const struct comedi_lrange range_bipolar0_025 = { - 1, { - BIP_RANGE(0.025) - } -}; -static const struct comedi_lrange *const ii20k_ai_ranges[] = { - &range_bipolar5, - &range_bipolar0_5, - &range_bipolar0_05, - &range_bipolar0_025, +static const struct comedi_lrange ii20k_ai_ranges = { + 4, { + BIP_RANGE(5), /* gain 1 */ + BIP_RANGE(0.5), /* gain 10 */ + BIP_RANGE(0.05), /* gain 100 */ + BIP_RANGE(0.025) /* gain 200 */ + }, }; -static const int pci20341_timebase[] = { 0x00, 0x00, 0x00, 0x04 }; -static const int pci20341_settling_time[] = { 0x58, 0x58, 0x93, 0x99 }; - struct ii20k_ao_private { unsigned int last_data[2]; }; -struct ii20k_ai_private { - int timebase; - int settling_time; - int ai_gain; -}; - struct pci20xxx_private { void __iomem *ioaddr; }; @@ -241,82 +220,94 @@ static int ii20k_ao_insn_write(struct comedi_device *dev, return insn->n; } +static int ii20k_ai_wait_eoc(struct comedi_device *dev, + struct comedi_subdevice *s, + int timeout) +{ + void __iomem *iobase = ii20k_module_iobase(dev, s); + unsigned char status; + + do { + status = readb(iobase + II20K_AI_STATUS_REG); + if ((status & II20K_AI_STATUS_INT) == 0) + return 0; + } while (timeout--); + + return -ETIME; +} + +static void ii20k_ai_setup(struct comedi_device *dev, + struct comedi_subdevice *s, + unsigned int chanspec) +{ + void __iomem *iobase = ii20k_module_iobase(dev, s); + unsigned int chan = CR_CHAN(chanspec); + unsigned int range = CR_RANGE(chanspec); + unsigned char val; + + /* initialize module */ + writeb(II20K_AI_CONF_ENA, iobase + II20K_AI_CONF_REG); + + /* software conversion */ + writeb(0, iobase + II20K_AI_STATUS_CMD_REG); + + /* set the time base for the settling time counter based on the gain */ + val = (range < 3) ? II20K_AI_OPT_TIMEBASE(0) : II20K_AI_OPT_TIMEBASE(2); + writeb(val, iobase + II20K_AI_OPT_REG); + + /* set the settling time counter based on the gain */ + val = (range < 2) ? 0x58 : (range < 3) ? 0x93 : 0x99; + writeb(val, iobase + II20K_AI_SET_TIME_REG); + + /* set number of input channels */ + writeb(1, iobase + II20K_AI_LAST_CHAN_ADDR_REG); + + /* set the channel list byte */ + val = II20K_AI_CHANLIST_ONBOARD_ONLY | + II20K_AI_CHANLIST_MUX_ENA | + II20K_AI_CHANLIST_GAIN(range) | + II20K_AI_CHANLIST_CHAN(chan); + writeb(val, iobase + II20K_AI_CHANLIST_REG); + + /* reset settling time counter and trigger delay counter */ + writeb(0, iobase + II20K_AI_COUNT_RESET_REG); + + /* reset channel scanner */ + writeb(0, iobase + II20K_AI_CHAN_RESET_REG); +} + static int ii20k_ai_insn_read(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { - struct ii20k_ai_private *ai_spriv = s->private; void __iomem *iobase = ii20k_module_iobase(dev, s); - unsigned int i = 0, j = 0; - int lo, hi; - unsigned char eoc; /* end of conversion */ - unsigned int clb; /* channel list byte */ - unsigned int boarddata; - - /* write number of input channels */ - writeb(1, iobase + PCI20341_LCHAN_ADDR_REG); - clb = PCI20341_DAISY_CHAIN | PCI20341_MUX | (ai_spriv->ai_gain << 3) - | CR_CHAN(insn->chanspec); - writeb(clb, iobase + PCI20341_CHAN_LIST); + int ret; + int i; - /* reset settling time counter and trigger delay counter */ - writeb(0x00, iobase + PCI20341_CC_RESET); + ii20k_ai_setup(dev, s, insn->chanspec); - writeb(0x00, iobase + PCI20341_CHAN_RESET); + for (i = 0; i < insn->n; i++) { + unsigned int val; - /* generate Pacer */ + /* generate a software start convert signal */ + readb(iobase + II20K_AI_PACER_RESET_REG); - for (i = 0; i < insn->n; i++) { - /* data polling isn't the niciest way to get the data, I know, - * but there are only 6 cycles (mean) and it is easier than - * the whole interrupt stuff - */ - j = 0; - /* generate Pacer */ - readb(iobase + PCI20341_SOFT_PACER); - - eoc = readb(iobase + PCI20341_STATUS_REG); - /* poll Interrupt Flag */ - while ((eoc < 0x80) && j < 100) { - j++; - eoc = readb(iobase + PCI20341_STATUS_REG); - } - if (j >= 100) { - dev_warn(dev->class_dev, - "AI interrupt channel %i polling exit !\n", i); - return -EINVAL; - } - lo = readb(iobase + PCI20341_LDATA); - hi = readb(iobase + PCI20341_LDATA + 1); - boarddata = lo + 0x100 * hi; - - /* board-data -> comedi-data */ - data[i] = (short)((boarddata + 0x8000) & 0xffff); - } + ret = ii20k_ai_wait_eoc(dev, s, 100); + if (ret) + return ret; - return i; -} + val = readb(iobase + II20K_AI_LSB_REG); + val |= (readb(iobase + II20K_AI_MSB_REG) << 8); -static void ii20k_ai_init(struct comedi_device *dev, - struct comedi_subdevice *s) -{ - struct ii20k_ai_private *ai_spriv = s->private; - void __iomem *iobase = ii20k_module_iobase(dev, s); - unsigned char option; - - /* depends on gain, trigger, repetition mode */ - option = ai_spriv->timebase | PCI20341_REPMODE; - - /* initialize Module */ - writeb(PCI20341_INIT, iobase + PCI20341_CONFIG_REG); - /* set Pacer */ - writeb(PCI20341_PACER, iobase + PCI20341_MOD_STATUS); - /* option register */ - writeb(option, iobase + PCI20341_OPT_REG); - /* settling time counter */ - writeb(ai_spriv->settling_time, iobase + PCI20341_SET_TIME_REG); - /* trigger not implemented */ + /* munge two's complement data */ + val += ((s->maxdata + 1) >> 1); + val &= s->maxdata; + + data[i] = val; + } + + return insn->n; } static void ii20k_dio_config(struct comedi_device *dev, @@ -456,13 +447,10 @@ static int ii20k_dio_insn_bits(struct comedi_device *dev, } static int ii20k_init_module(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_devconfig *it) + struct comedi_subdevice *s) { struct ii20k_ao_private *ao_spriv; - struct ii20k_ai_private *ai_spriv; void __iomem *iobase = ii20k_module_iobase(dev, s); - unsigned int opt0 = it->options[(2 * s->index) + 2]; unsigned char id; id = readb(iobase + II20K_ID_REG); @@ -482,28 +470,15 @@ static int ii20k_init_module(struct comedi_device *dev, s->insn_read = ii20k_ao_insn_read; s->insn_write = ii20k_ao_insn_write; break; - case PCI20341_ID: - if (opt0 < 0 || opt0 > 3) - opt0 = 0; - - ai_spriv = comedi_alloc_spriv(s, sizeof(*ai_spriv)); - if (!ai_spriv) - return -ENOMEM; - - ai_spriv->timebase = pci20341_timebase[opt0]; - ai_spriv->settling_time = pci20341_settling_time[opt0]; - + case II20K_ID_PCI200341M_1: /* Analog Input subdevice */ s->type = COMEDI_SUBD_AI; - s->subdev_flags = SDF_READABLE; + s->subdev_flags = SDF_READABLE | SDF_DIFF; s->n_chan = 4; s->maxdata = 0xffff; - s->range_table = ii20k_ai_ranges[opt0]; + s->range_table = &ii20k_ai_ranges; s->insn_read = ii20k_ai_insn_read; - - ii20k_ai_init(dev, s); break; - case PCI20xxx_EMPTY_ID: default: s->type = COMEDI_SUBD_UNUSED; break; @@ -546,7 +521,7 @@ static int pci20xxx_attach(struct comedi_device *dev, if (id & II20K_ID_MOD1_EMPTY) { s->type = COMEDI_SUBD_UNUSED; } else { - ret = ii20k_init_module(dev, s, it); + ret = ii20k_init_module(dev, s); if (ret) return ret; } @@ -555,7 +530,7 @@ static int pci20xxx_attach(struct comedi_device *dev, if (id & II20K_ID_MOD2_EMPTY) { s->type = COMEDI_SUBD_UNUSED; } else { - ret = ii20k_init_module(dev, s, it); + ret = ii20k_init_module(dev, s); if (ret) return ret; } @@ -564,7 +539,7 @@ static int pci20xxx_attach(struct comedi_device *dev, if (id & II20K_ID_MOD3_EMPTY) { s->type = COMEDI_SUBD_UNUSED; } else { - ret = ii20k_init_module(dev, s, it); + ret = ii20k_init_module(dev, s); if (ret) return ret; }