From c427431565db6050c3447bfd10b12ec14669f20e Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sat, 30 Jun 2012 10:14:29 +0100 Subject: [PATCH] --- yaml --- r: 317718 b: refs/heads/master c: c1f1a9d48fd18cd380858637fda1c69732547e1f h: refs/heads/master v: v3 --- [refs] | 2 +- .../staging/comedi/drivers/adl_pci6208.c | 372 ++-- .../staging/comedi/drivers/cb_pcidas.c | 1703 +++++++++-------- trunk/drivers/staging/comedi/drivers/das08.c | 110 +- trunk/drivers/staging/comedi/drivers/das08.h | 14 +- .../drivers/staging/comedi/drivers/das08_cs.c | 182 +- .../drivers/staging/comedi/drivers/dmm32at.c | 611 +++--- .../drivers/staging/comedi/drivers/ni_670x.c | 348 ++-- trunk/drivers/staging/comedi/drivers/s626.c | 2 +- 9 files changed, 1962 insertions(+), 1382 deletions(-) diff --git a/[refs] b/[refs] index 5b2e81d18d5d..59abd38776b1 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: b600c5b569c2f62eacf8d71dd9975d7a996073f3 +refs/heads/master: c1f1a9d48fd18cd380858637fda1c69732547e1f diff --git a/trunk/drivers/staging/comedi/drivers/adl_pci6208.c b/trunk/drivers/staging/comedi/drivers/adl_pci6208.c index 487fd4a8124a..79f6765c46c1 100644 --- a/trunk/drivers/staging/comedi/drivers/adl_pci6208.c +++ b/trunk/drivers/staging/comedi/drivers/adl_pci6208.c @@ -41,225 +41,295 @@ Configuration Options: - adl_pci9111.c copied the entire pci setup section - adl_pci9118.c */ - -#include "../comedidev.h" - /* - * PCI-6208/6216-GL register map + * These headers should be followed by a blank line, and any comments + * you wish to say about the driver. The comment area is the place + * to put any known bugs, limitations, unsupported features, supported + * command triggers, whether or not commands are supported on particular + * subdevices, etc. + * + * Somewhere in the comment should be information about configuration + * options that are used with comedi_config. */ -#define PCI6208_AO_CONTROL(x) (0x00 + (2 * (x))) -#define PCI6208_AO_STATUS 0x00 -#define PCI6208_AO_STATUS_DATA_SEND (1 << 0) -#define PCI6208_DIO 0x40 -#define PCI6208_DIO_DO_MASK (0x0f) -#define PCI6208_DIO_DO_SHIFT (0) -#define PCI6208_DIO_DI_MASK (0xf0) -#define PCI6208_DIO_DI_SHIFT (4) - -#define PCI6208_MAX_AO_CHANNELS 8 +#include "../comedidev.h" +/* Board descriptions */ struct pci6208_board { const char *name; - unsigned short dev_id; + unsigned short dev_id; /* `lspci` will show you this */ int ao_chans; + /* int ao_bits; */ }; static const struct pci6208_board pci6208_boards[] = { + /*{ + .name = "pci6208v", + .dev_id = 0x6208, // not sure + .ao_chans = 8 + // , .ao_bits = 16 + }, + { + .name = "pci6216v", + .dev_id = 0x6208, // not sure + .ao_chans = 16 + // , .ao_bits = 16 + }, */ { - .name = "pci6208a", - .dev_id = 0x6208, - .ao_chans = 8, - }, + .name = "pci6208a", + .dev_id = 0x6208, + .ao_chans = 8 + /* , .ao_bits = 16 */ + } }; +/* Will be initialized in pci6208_find device(). */ +#define thisboard ((const struct pci6208_board *)dev->board_ptr) + struct pci6208_private { - struct pci_dev *pci_dev; - unsigned int ao_readback[PCI6208_MAX_AO_CHANNELS]; + int data; + struct pci_dev *pci_dev; /* for a PCI device */ + unsigned int ao_readback[2]; /* Used for AO readback */ }; +#define devpriv ((struct pci6208_private *)dev->private) + static int pci6208_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { - struct pci6208_private *devpriv = dev->private; - int chan = CR_CHAN(insn->chanspec); + int i = 0, Data_Read; + unsigned short chan = CR_CHAN(insn->chanspec); unsigned long invert = 1 << (16 - 1); - unsigned long value = 0; - unsigned short status; - int i; - + unsigned long out_value; + /* Writing a list of values to an AO channel is probably not + * very useful, but that's how the interface is defined. */ for (i = 0; i < insn->n; i++) { - value = data[i] ^ invert; - + out_value = data[i] ^ invert; + /* a typical programming sequence */ do { - status = inw(dev->iobase + PCI6208_AO_STATUS); - } while (status & PCI6208_AO_STATUS_DATA_SEND); - - outw(value, dev->iobase + PCI6208_AO_CONTROL(chan)); + Data_Read = (inw(dev->iobase) & 1); + } while (Data_Read); + outw(out_value, dev->iobase + (0x02 * chan)); + devpriv->ao_readback[chan] = out_value; } - devpriv->ao_readback[chan] = value; - return insn->n; + /* return the number of samples read/written */ + return i; } +/* AO subdevices should have a read insn as well as a write insn. + * Usually this means copying a value stored in devpriv. */ static int pci6208_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { - struct pci6208_private *devpriv = dev->private; - int chan = CR_CHAN(insn->chanspec); int i; + int chan = CR_CHAN(insn->chanspec); for (i = 0; i < insn->n; i++) data[i] = devpriv->ao_readback[chan]; - return insn->n; + return i; } -static int pci6208_dio_insn_bits(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) +/* DIO devices are slightly special. Although it is possible to + * implement the insn_read/insn_write interface, it is much more + * useful to applications if you implement the insn_bits interface. + * This allows packed reading/writing of the DIO channels. The + * comedi core can convert between insn_bits and insn_read/write */ +/* static int pci6208_dio_insn_bits(struct comedi_device *dev, + * struct comedi_subdevice *s, */ +/* struct comedi_insn *insn,unsigned int *data) */ +/* { */ + /* The insn data is a mask in data[0] and the new data + * in data[1], each channel cooresponding to a bit. */ +/* if(data[0]){ */ +/* s->state &= ~data[0]; */ +/* s->state |= data[0]&data[1]; */ + /* Write out the new digital output lines */ + /* outw(s->state,dev->iobase + SKEL_DIO); */ +/* } */ + + /* on return, data[1] contains the value of the digital + * input and output lines. */ + /* data[1]=inw(dev->iobase + SKEL_DIO); */ + /* or we could just return the software copy of the output values if + * it was a purely digital output subdevice */ + /* data[1]=s->state; */ + +/* return insn->n; */ +/* } */ + +/* static int pci6208_dio_insn_config(struct comedi_device *dev, + * struct comedi_subdevice *s, */ +/* struct comedi_insn *insn,unsigned int *data) */ +/* { */ +/* int chan=CR_CHAN(insn->chanspec); */ + + /* The input or output configuration of each digital line is + * configured by a special insn_config instruction. chanspec + * contains the channel to be changed, and data[0] contains the + * value COMEDI_INPUT or COMEDI_OUTPUT. */ + +/* if(data[0]==COMEDI_OUTPUT){ */ +/* s->io_bits |= 1<io_bits &= ~(1<io_bits,dev->iobase + SKEL_DIO_CONFIG); */ + +/* return 1; */ +/* } */ + +static int pci6208_find_device(struct comedi_device *dev, int bus, int slot) { - unsigned int mask = data[0] & PCI6208_DIO_DO_MASK; - unsigned int bits = data[1]; - - if (mask) { - s->state &= ~mask; - s->state |= bits & mask; + struct pci_dev *pci_dev = NULL; + int i; - outw(s->state, dev->iobase + PCI6208_DIO); + for_each_pci_dev(pci_dev) { + if (pci_dev->vendor == PCI_VENDOR_ID_ADLINK) { + for (i = 0; i < ARRAY_SIZE(pci6208_boards); i++) { + if (pci6208_boards[i].dev_id == + pci_dev->device) { + /* + * was a particular bus/slot requested? + */ + if ((bus != 0) || (slot != 0)) { + /* + * are we on the + * wrong bus/slot? + */ + if (pci_dev->bus->number + != bus || + PCI_SLOT(pci_dev->devfn) + != slot) { + continue; + } + } + dev->board_ptr = pci6208_boards + i; + goto found; + } + } + } } - s->state = inw(dev->iobase + PCI6208_DIO); - data[1] = s->state; + printk(KERN_ERR "comedi%d: no supported board found! " + "(req. bus/slot : %d/%d)\n", + dev->minor, bus, slot); + return -EIO; - return insn->n; -} +found: + printk("comedi%d: found %s (b:s:f=%d:%d:%d) , irq=%d\n", + dev->minor, + pci6208_boards[i].name, + pci_dev->bus->number, + PCI_SLOT(pci_dev->devfn), + PCI_FUNC(pci_dev->devfn), pci_dev->irq); -static int pci6208_dio_insn_config(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - int chan = CR_CHAN(insn->chanspec); - unsigned int mask = 1 << chan; - - switch (data[0]) { - case INSN_CONFIG_DIO_QUERY: - data[1] = (s->io_bits & mask) ? COMEDI_OUTPUT : COMEDI_INPUT; - break; - default: - return -EINVAL; - } + /* TODO: Warn about non-tested boards. */ + /* switch(board->device_id) */ + /* { */ + /* }; */ + + devpriv->pci_dev = pci_dev; - return insn->n; + return 0; } -static struct pci_dev *pci6208_find_device(struct comedi_device *dev, - struct comedi_devconfig *it) +static int +pci6208_pci_setup(struct pci_dev *pci_dev, unsigned long *io_base_ptr, + int dev_minor) { - const struct pci6208_board *thisboard; - struct pci_dev *pci_dev = NULL; - int bus = it->options[0]; - int slot = it->options[1]; - int i; + unsigned long io_base, io_range, lcr_io_base, lcr_io_range; - for_each_pci_dev(pci_dev) { - if (pci_dev->vendor != PCI_VENDOR_ID_ADLINK) - continue; - for (i = 0; i < ARRAY_SIZE(pci6208_boards); i++) { - thisboard = &pci6208_boards[i]; - if (thisboard->dev_id != pci_dev->device) - continue; - /* was a particular bus/slot requested? */ - if (bus || slot) { - /* are we on the wrong bus/slot? */ - if (pci_dev->bus->number != bus || - PCI_SLOT(pci_dev->devfn) != slot) - continue; - } - dev_dbg(dev->class_dev, - "Found %s on bus %d, slot, %d, irq=%d\n", - thisboard->name, - pci_dev->bus->number, - PCI_SLOT(pci_dev->devfn), - pci_dev->irq); - dev->board_ptr = thisboard; - return pci_dev; - } + /* Enable PCI device and request regions */ + if (comedi_pci_enable(pci_dev, "adl_pci6208") < 0) { + printk(KERN_ERR "comedi%d: Failed to enable PCI device " + "and request regions\n", + dev_minor); + return -EIO; } - dev_err(dev->class_dev, - "No supported board found! (req. bus %d, slot %d)\n", - bus, slot); - return NULL; + /* Read local configuration register + * base address [PCI_BASE_ADDRESS #1]. + */ + lcr_io_base = pci_resource_start(pci_dev, 1); + lcr_io_range = pci_resource_len(pci_dev, 1); + + printk(KERN_INFO "comedi%d: local config registers at address" + " 0x%4lx [0x%4lx]\n", + dev_minor, lcr_io_base, lcr_io_range); + + /* Read PCI6208 register base address [PCI_BASE_ADDRESS #2]. */ + io_base = pci_resource_start(pci_dev, 2); + io_range = pci_resource_end(pci_dev, 2) - io_base + 1; + + printk("comedi%d: 6208 registers at address 0x%4lx [0x%4lx]\n", + dev_minor, io_base, io_range); + + *io_base_ptr = io_base; + /* devpriv->io_range = io_range; */ + /* devpriv->is_valid=0; */ + /* devpriv->lcr_io_base=lcr_io_base; */ + /* devpriv->lcr_io_range=lcr_io_range; */ + + return 0; } static int pci6208_attach(struct comedi_device *dev, struct comedi_devconfig *it) { - const struct pci6208_board *thisboard; - struct pci6208_private *devpriv; struct comedi_subdevice *s; - int ret; + int retval; + unsigned long io_base; - ret = alloc_private(dev, sizeof(*devpriv)); - if (ret < 0) - return ret; - devpriv = dev->private; + printk(KERN_INFO "comedi%d: pci6208: ", dev->minor); - devpriv->pci_dev = pci6208_find_device(dev, it); - if (!devpriv->pci_dev) - return -EIO; - thisboard = comedi_board(dev); + retval = alloc_private(dev, sizeof(struct pci6208_private)); + if (retval < 0) + return retval; - dev->board_name = thisboard->name; + retval = pci6208_find_device(dev, it->options[0], it->options[1]); + if (retval < 0) + return retval; - ret = comedi_pci_enable(devpriv->pci_dev, dev->driver->driver_name); - if (ret) { - dev_err(dev->class_dev, - "Failed to enable PCI device and request regions\n"); - return ret; - } - dev->iobase = pci_resource_start(devpriv->pci_dev, 2); + retval = pci6208_pci_setup(devpriv->pci_dev, &io_base, dev->minor); + if (retval < 0) + return retval; - ret = comedi_alloc_subdevices(dev, 2); - if (ret) - return ret; + dev->iobase = io_base; + dev->board_name = thisboard->name; + + retval = comedi_alloc_subdevices(dev, 2); + if (retval) + return retval; s = dev->subdevices + 0; /* analog output subdevice */ - s->type = COMEDI_SUBD_AO; - s->subdev_flags = SDF_WRITABLE; - s->n_chan = thisboard->ao_chans; - s->maxdata = 0xffff; - s->range_table = &range_bipolar10; - s->insn_write = pci6208_ao_winsn; - s->insn_read = pci6208_ao_rinsn; - - s = dev->subdevices + 1; + s->type = COMEDI_SUBD_AO; + s->subdev_flags = SDF_WRITABLE; /* anything else to add here?? */ + s->n_chan = thisboard->ao_chans; + s->maxdata = 0xffff; /* 16-bit DAC */ + s->range_table = &range_bipolar10; /* this needs to be checked. */ + s->insn_write = pci6208_ao_winsn; + s->insn_read = pci6208_ao_rinsn; + + /* s=dev->subdevices+1; */ /* digital i/o subdevice */ - s->type = COMEDI_SUBD_DIO; - s->subdev_flags = SDF_READABLE | SDF_WRITABLE; - s->n_chan = 8; - s->maxdata = 1; - s->range_table = &range_digital; - s->insn_bits = pci6208_dio_insn_bits; - s->insn_config = pci6208_dio_insn_config; - - s->io_bits = 0x0f; - s->state = inw(dev->iobase + PCI6208_DIO); + /* s->type=COMEDI_SUBD_DIO; */ + /* s->subdev_flags=SDF_READABLE|SDF_WRITABLE; */ + /* s->n_chan=16; */ + /* s->maxdata=1; */ + /* s->range_table=&range_digital; */ + /* s->insn_bits = pci6208_dio_insn_bits; */ + /* s->insn_config = pci6208_dio_insn_config; */ - dev_info(dev->class_dev, "%s: %s, I/O base=0x%04lx\n", - dev->driver->driver_name, dev->board_name, dev->iobase); + printk(KERN_INFO "attached\n"); - return 0; + return 1; } static void pci6208_detach(struct comedi_device *dev) { - struct pci6208_private *devpriv = dev->private; - if (devpriv && devpriv->pci_dev) { if (dev->iobase) comedi_pci_disable(devpriv->pci_dev); @@ -285,7 +355,11 @@ static void __devexit adl_pci6208_pci_remove(struct pci_dev *dev) comedi_pci_auto_unconfig(dev); } +/* This is used by modprobe to translate PCI IDs to drivers. Should + * only be used for PCI and ISA-PnP devices */ static DEFINE_PCI_DEVICE_TABLE(adl_pci6208_pci_table) = { + /* { PCI_VENDOR_ID_ADLINK, 0x6208, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, */ + /* { PCI_VENDOR_ID_ADLINK, 0x6208, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, */ { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, 0x6208) }, { 0 } }; diff --git a/trunk/drivers/staging/comedi/drivers/cb_pcidas.c b/trunk/drivers/staging/comedi/drivers/cb_pcidas.c index 301444e09433..e2bf22270561 100644 --- a/trunk/drivers/staging/comedi/drivers/cb_pcidas.c +++ b/trunk/drivers/staging/comedi/drivers/cb_pcidas.c @@ -28,8 +28,7 @@ */ /* Driver: cb_pcidas -Description: MeasurementComputing PCI-DAS series - with the AMCC S5933 PCI controller +Description: MeasurementComputing PCI-DAS series with the AMCC S5933 PCI controller Author: Ivan Martinez , Frank Mori Hess Updated: 2003-3-11 @@ -80,72 +79,89 @@ analog triggering on 1602 series #include "amcc_s5933.h" #include "comedi_fc.h" +#undef CB_PCIDAS_DEBUG /* disable debugging code */ +/* #define CB_PCIDAS_DEBUG enable debugging code */ + /* PCI vendor number of ComputerBoards/MeasurementComputing */ #define PCI_VENDOR_ID_CB 0x1307 - -#define TIMER_BASE 100 /* 10MHz master clock */ -#define AI_BUFFER_SIZE 1024 /* max ai fifo size */ -#define AO_BUFFER_SIZE 1024 /* max ao fifo size */ -#define NUM_CHANNELS_8800 8 -#define NUM_CHANNELS_7376 1 -#define NUM_CHANNELS_8402 2 -#define NUM_CHANNELS_DAC08 1 +#define TIMER_BASE 100 /* 10MHz master clock */ +#define AI_BUFFER_SIZE 1024 /* maximum fifo size of any supported board */ +#define AO_BUFFER_SIZE 1024 /* maximum fifo size of any supported board */ +#define NUM_CHANNELS_8800 8 +#define NUM_CHANNELS_7376 1 +#define NUM_CHANNELS_8402 2 +#define NUM_CHANNELS_DAC08 1 + +/* PCI-DAS base addresses */ + +/* indices of base address regions */ +#define S5933_BADRINDEX 0 +#define CONT_STAT_BADRINDEX 1 +#define ADC_FIFO_BADRINDEX 2 +#define PACER_BADRINDEX 3 +#define AO_BADRINDEX 4 +/* sizes of io regions */ +#define CONT_STAT_SIZE 10 +#define ADC_FIFO_SIZE 4 +#define PACER_SIZE 12 +#define AO_SIZE 4 /* Control/Status registers */ -#define INT_ADCFIFO 0 /* INTERRUPT / ADC FIFO register */ -#define INT_EOS 0x1 /* int end of scan */ -#define INT_FHF 0x2 /* int fifo half full */ -#define INT_FNE 0x3 /* int fifo not empty */ -#define INT_MASK 0x3 /* mask of int select bits */ -#define INTE 0x4 /* int enable */ -#define DAHFIE 0x8 /* dac half full int enable */ -#define EOAIE 0x10 /* end of acq. int enable */ -#define DAHFI 0x20 /* dac half full status / clear */ -#define EOAI 0x40 /* end of acq. int status / clear */ -#define INT 0x80 /* int status / clear */ -#define EOBI 0x200 /* end of burst int status */ -#define ADHFI 0x400 /* half-full int status */ -#define ADNEI 0x800 /* fifo not empty int status (latch) */ -#define ADNE 0x1000 /* fifo not empty status (realtime) */ -#define DAEMIE 0x1000 /* dac empty int enable */ -#define LADFUL 0x2000 /* fifo overflow / clear */ -#define DAEMI 0x4000 /* dac fifo empty int status / clear */ - -#define ADCMUX_CONT 2 /* ADC CHANNEL MUX AND CONTROL reg */ -#define BEGIN_SCAN(x) ((x) & 0xf) -#define END_SCAN(x) (((x) & 0xf) << 4) -#define GAIN_BITS(x) (((x) & 0x3) << 8) -#define UNIP 0x800 /* Analog front-end unipolar mode */ -#define SE 0x400 /* Inputs in single-ended mode */ -#define PACER_MASK 0x3000 /* pacer source bits */ -#define PACER_INT 0x1000 /* int. pacer */ -#define PACER_EXT_FALL 0x2000 /* ext. falling edge */ -#define PACER_EXT_RISE 0x3000 /* ext. rising edge */ -#define EOC 0x4000 /* adc not busy */ - -#define TRIG_CONTSTAT 4 /* TRIGGER CONTROL/STATUS register */ -#define SW_TRIGGER 0x1 /* software start trigger */ -#define EXT_TRIGGER 0x2 /* ext. start trigger */ -#define ANALOG_TRIGGER 0x3 /* ext. analog trigger */ -#define TRIGGER_MASK 0x3 /* start trigger mask */ -#define TGPOL 0x04 /* invert trigger (1602 only) */ -#define TGSEL 0x08 /* edge/level trigerred (1602 only) */ -#define TGEN 0x10 /* enable external start trigger */ -#define BURSTE 0x20 /* burst mode enable */ -#define XTRCL 0x80 /* clear external trigger */ - -#define CALIBRATION_REG 6 /* CALIBRATION register */ -#define SELECT_8800_BIT 0x100 /* select 8800 caldac */ -#define SELECT_TRIMPOT_BIT 0x200 /* select ad7376 trim pot */ -#define SELECT_DAC08_BIT 0x400 /* select dac08 caldac */ +#define INT_ADCFIFO 0 /* INTERRUPT / ADC FIFO register */ +#define INT_EOS 0x1 /* interrupt end of scan */ +#define INT_FHF 0x2 /* interrupt fifo half full */ +#define INT_FNE 0x3 /* interrupt fifo not empty */ +#define INT_MASK 0x3 /* mask of interrupt select bits */ +#define INTE 0x4 /* interrupt enable */ +#define DAHFIE 0x8 /* dac half full interrupt enable */ +#define EOAIE 0x10 /* end of acquisition interrupt enable */ +#define DAHFI 0x20 /* dac half full read status / write interrupt clear */ +#define EOAI 0x40 /* read end of acq. interrupt status / write clear */ +#define INT 0x80 /* read interrupt status / write clear */ +#define EOBI 0x200 /* read end of burst interrupt status */ +#define ADHFI 0x400 /* read half-full interrupt status */ +#define ADNEI 0x800 /* read fifo not empty interrupt latch status */ +#define ADNE 0x1000 /* read, fifo not empty (realtime, not latched) status */ +#define DAEMIE 0x1000 /* write, dac empty interrupt enable */ +#define LADFUL 0x2000 /* read fifo overflow / write clear */ +#define DAEMI 0x4000 /* dac fifo empty interrupt status / write clear */ + +#define ADCMUX_CONT 2 /* ADC CHANNEL MUX AND CONTROL register */ +#define BEGIN_SCAN(x) ((x) & 0xf) +#define END_SCAN(x) (((x) & 0xf) << 4) +#define GAIN_BITS(x) (((x) & 0x3) << 8) +#define UNIP 0x800 /* Analog front-end unipolar for range */ +#define SE 0x400 /* Inputs in single-ended mode */ +#define PACER_MASK 0x3000 /* pacer source bits */ +#define PACER_INT 0x1000 /* internal pacer */ +#define PACER_EXT_FALL 0x2000 /* external falling edge */ +#define PACER_EXT_RISE 0x3000 /* external rising edge */ +#define EOC 0x4000 /* adc not busy */ + +#define TRIG_CONTSTAT 4 /* TRIGGER CONTROL/STATUS register */ +#define SW_TRIGGER 0x1 /* software start trigger */ +#define EXT_TRIGGER 0x2 /* external start trigger */ +#define ANALOG_TRIGGER 0x3 /* external analog trigger */ +#define TRIGGER_MASK 0x3 /* mask of bits that determine start trigger */ +#define TGPOL 0x04 /* invert the edge/level of the external trigger (1602 only) */ +#define TGSEL 0x08 /* if set edge triggered, otherwise level trigerred (1602 only) */ +#define TGEN 0x10 /* enable external start trigger */ +#define BURSTE 0x20 /* burst mode enable */ +#define XTRCL 0x80 /* clear external trigger */ + +#define CALIBRATION_REG 6 /* CALIBRATION register */ +#define SELECT_8800_BIT 0x100 /* select 8800 caldac */ +#define SELECT_TRIMPOT_BIT 0x200 /* select ad7376 trim pot */ +#define SELECT_DAC08_BIT 0x400 /* select dac08 caldac */ #define CAL_SRC_BITS(x) (((x) & 0x7) << 11) -#define CAL_EN_BIT 0x4000 /* calibration source enable */ -#define SERIAL_DATA_IN_BIT 0x8000 /* serial data bit going to caldac */ - -#define DAC_CSR 0x8 /* dac control and status register */ -#define DACEN 0x02 /* dac enable */ -#define DAC_MODE_UPDATE_BOTH 0x80 /* update both dacs */ +#define CAL_EN_BIT 0x4000 /* read calibration source instead of analog input channel 0 */ +#define SERIAL_DATA_IN_BIT 0x8000 /* serial data stream going to 8800 and 7376 */ +#define DAC_CSR 0x8 /* dac control and status register */ +enum dac_csr_bits { + DACEN = 0x2, /* dac enable */ + DAC_MODE_UPDATE_BOTH = 0x80, /* update both dacs when dac0 is written */ +}; static inline unsigned int DAC_RANGE(unsigned int channel, unsigned int range) { return (range & 0x3) << (8 + 2 * (channel & 0x1)); @@ -157,26 +173,27 @@ static inline unsigned int DAC_RANGE_MASK(unsigned int channel) }; /* bits for 1602 series only */ -#define DAC_EMPTY 0x1 /* fifo empty, read, write clear */ -#define DAC_START 0x4 /* start/arm fifo operations */ -#define DAC_PACER_MASK 0x18 /* bits that set pacer source */ -#define DAC_PACER_INT 0x8 /* int. pacing */ -#define DAC_PACER_EXT_FALL 0x10 /* ext. pacing, falling edge */ -#define DAC_PACER_EXT_RISE 0x18 /* ext. pacing, rising edge */ - +enum dac_csr_bits_1602 { + DAC_EMPTY = 0x1, /* dac fifo empty, read, write clear */ + DAC_START = 0x4, /* start/arm dac fifo operations */ + DAC_PACER_MASK = 0x18, /* bits that set dac pacer source */ + DAC_PACER_INT = 0x8, /* dac internal pacing */ + DAC_PACER_EXT_FALL = 0x10, /* dac external pacing, falling edge */ + DAC_PACER_EXT_RISE = 0x18, /* dac external pacing, rising edge */ +}; static inline unsigned int DAC_CHAN_EN(unsigned int channel) { return 1 << (5 + (channel & 0x1)); /* enable channel 0 or 1 */ }; /* analog input fifo */ -#define ADCDATA 0 /* ADC DATA register */ -#define ADCFIFOCLR 2 /* ADC FIFO CLEAR */ +#define ADCDATA 0 /* ADC DATA register */ +#define ADCFIFOCLR 2 /* ADC FIFO CLEAR */ /* pacer, counter, dio registers */ -#define ADC8254 0 -#define DIO_8255 4 -#define DAC8254 8 +#define ADC8254 0 +#define DIO_8255 4 +#define DAC8254 8 /* analog output registers for 100x, 1200 series */ static inline unsigned int DAC_DATA_REG(unsigned int channel) @@ -185,11 +202,11 @@ static inline unsigned int DAC_DATA_REG(unsigned int channel) } /* analog output registers for 1602 series*/ -#define DACDATA 0 /* DAC DATA register */ -#define DACFIFOCLR 2 /* DAC FIFO CLEAR */ - -#define IS_UNIPOLAR 0x4 /* unipolar range mask */ +#define DACDATA 0 /* DAC DATA register */ +#define DACFIFOCLR 2 /* DAC FIFO CLEAR */ +/* bit in hexadecimal representation of range index that indicates unipolar input range */ +#define IS_UNIPOLAR 0x4 /* analog input ranges for most boards */ static const struct comedi_lrange cb_pcidas_ranges = { 8, @@ -239,176 +256,524 @@ enum trimpot_model { struct cb_pcidas_board { const char *name; unsigned short device_id; - int ai_nchan; /* Inputs in single-ended mode */ + int ai_se_chans; /* Inputs in single-ended mode */ + int ai_diff_chans; /* Inputs in differential mode */ int ai_bits; /* analog input resolution */ int ai_speed; /* fastest conversion period in ns */ int ao_nchan; /* number of analog out channels */ int has_ao_fifo; /* analog output has fifo */ - int ao_scan_speed; /* analog output scan speed for 1602 series */ + int ao_scan_speed; /* analog output speed for 1602 series (for a scan, not conversion) */ int fifo_size; /* number of samples fifo can hold */ const struct comedi_lrange *ranges; enum trimpot_model trimpot; unsigned has_dac08:1; - unsigned is_1602:1; + unsigned has_ai_trig_gated:1; /* Tells if the AI trigger can be gated */ + unsigned has_ai_trig_invert:1; /* Tells if the AI trigger can be inverted */ }; static const struct cb_pcidas_board cb_pcidas_boards[] = { { - .name = "pci-das1602/16", - .device_id = 0x1, - .ai_nchan = 16, - .ai_bits = 16, - .ai_speed = 5000, - .ao_nchan = 2, - .has_ao_fifo = 1, - .ao_scan_speed = 10000, - .fifo_size = 512, - .ranges = &cb_pcidas_ranges, - .trimpot = AD8402, - .has_dac08 = 1, - .is_1602 = 1, - }, { - .name = "pci-das1200", - .device_id = 0xF, - .ai_nchan = 16, - .ai_bits = 12, - .ai_speed = 3200, - .ao_nchan = 2, - .fifo_size = 1024, - .ranges = &cb_pcidas_ranges, - .trimpot = AD7376, - }, { - .name = "pci-das1602/12", - .device_id = 0x10, - .ai_nchan = 16, - .ai_bits = 12, - .ai_speed = 3200, - .ao_nchan = 2, - .has_ao_fifo = 1, - .ao_scan_speed = 4000, - .fifo_size = 1024, - .ranges = &cb_pcidas_ranges, - .trimpot = AD7376, - .is_1602 = 1, - }, { - .name = "pci-das1200/jr", - .device_id = 0x19, - .ai_nchan = 16, - .ai_bits = 12, - .ai_speed = 3200, - .fifo_size = 1024, - .ranges = &cb_pcidas_ranges, - .trimpot = AD7376, - }, { - .name = "pci-das1602/16/jr", - .device_id = 0x1C, - .ai_nchan = 16, - .ai_bits = 16, - .ai_speed = 5000, - .fifo_size = 512, - .ranges = &cb_pcidas_ranges, - .trimpot = AD8402, - .has_dac08 = 1, - .is_1602 = 1, - }, { - .name = "pci-das1000", - .device_id = 0x4C, - .ai_nchan = 16, - .ai_bits = 12, - .ai_speed = 4000, - .fifo_size = 1024, - .ranges = &cb_pcidas_ranges, - .trimpot = AD7376, - }, { - .name = "pci-das1001", - .device_id = 0x1a, - .ai_nchan = 16, - .ai_bits = 12, - .ai_speed = 6800, - .ao_nchan = 2, - .fifo_size = 1024, - .ranges = &cb_pcidas_alt_ranges, - .trimpot = AD7376, - }, { - .name = "pci-das1002", - .device_id = 0x1b, - .ai_nchan = 16, - .ai_bits = 12, - .ai_speed = 6800, - .ao_nchan = 2, - .fifo_size = 1024, - .ranges = &cb_pcidas_ranges, - .trimpot = AD7376, - }, + .name = "pci-das1602/16", + .device_id = 0x1, + .ai_se_chans = 16, + .ai_diff_chans = 8, + .ai_bits = 16, + .ai_speed = 5000, + .ao_nchan = 2, + .has_ao_fifo = 1, + .ao_scan_speed = 10000, + .fifo_size = 512, + .ranges = &cb_pcidas_ranges, + .trimpot = AD8402, + .has_dac08 = 1, + .has_ai_trig_gated = 1, + .has_ai_trig_invert = 1, + }, + { + .name = "pci-das1200", + .device_id = 0xF, + .ai_se_chans = 16, + .ai_diff_chans = 8, + .ai_bits = 12, + .ai_speed = 3200, + .ao_nchan = 2, + .has_ao_fifo = 0, + .fifo_size = 1024, + .ranges = &cb_pcidas_ranges, + .trimpot = AD7376, + .has_dac08 = 0, + .has_ai_trig_gated = 0, + .has_ai_trig_invert = 0, + }, + { + .name = "pci-das1602/12", + .device_id = 0x10, + .ai_se_chans = 16, + .ai_diff_chans = 8, + .ai_bits = 12, + .ai_speed = 3200, + .ao_nchan = 2, + .has_ao_fifo = 1, + .ao_scan_speed = 4000, + .fifo_size = 1024, + .ranges = &cb_pcidas_ranges, + .trimpot = AD7376, + .has_dac08 = 0, + .has_ai_trig_gated = 1, + .has_ai_trig_invert = 1, + }, + { + .name = "pci-das1200/jr", + .device_id = 0x19, + .ai_se_chans = 16, + .ai_diff_chans = 8, + .ai_bits = 12, + .ai_speed = 3200, + .ao_nchan = 0, + .has_ao_fifo = 0, + .fifo_size = 1024, + .ranges = &cb_pcidas_ranges, + .trimpot = AD7376, + .has_dac08 = 0, + .has_ai_trig_gated = 0, + .has_ai_trig_invert = 0, + }, + { + .name = "pci-das1602/16/jr", + .device_id = 0x1C, + .ai_se_chans = 16, + .ai_diff_chans = 8, + .ai_bits = 16, + .ai_speed = 5000, + .ao_nchan = 0, + .has_ao_fifo = 0, + .fifo_size = 512, + .ranges = &cb_pcidas_ranges, + .trimpot = AD8402, + .has_dac08 = 1, + .has_ai_trig_gated = 1, + .has_ai_trig_invert = 1, + }, + { + .name = "pci-das1000", + .device_id = 0x4C, + .ai_se_chans = 16, + .ai_diff_chans = 8, + .ai_bits = 12, + .ai_speed = 4000, + .ao_nchan = 0, + .has_ao_fifo = 0, + .fifo_size = 1024, + .ranges = &cb_pcidas_ranges, + .trimpot = AD7376, + .has_dac08 = 0, + .has_ai_trig_gated = 0, + .has_ai_trig_invert = 0, + }, + { + .name = "pci-das1001", + .device_id = 0x1a, + .ai_se_chans = 16, + .ai_diff_chans = 8, + .ai_bits = 12, + .ai_speed = 6800, + .ao_nchan = 2, + .has_ao_fifo = 0, + .fifo_size = 1024, + .ranges = &cb_pcidas_alt_ranges, + .trimpot = AD7376, + .has_dac08 = 0, + .has_ai_trig_gated = 0, + .has_ai_trig_invert = 0, + }, + { + .name = "pci-das1002", + .device_id = 0x1b, + .ai_se_chans = 16, + .ai_diff_chans = 8, + .ai_bits = 12, + .ai_speed = 6800, + .ao_nchan = 2, + .has_ao_fifo = 0, + .fifo_size = 1024, + .ranges = &cb_pcidas_ranges, + .trimpot = AD7376, + .has_dac08 = 0, + .has_ai_trig_gated = 0, + .has_ai_trig_invert = 0, + }, }; +/* + * Useful for shorthand access to the particular board structure + */ +#define thisboard ((const struct cb_pcidas_board *)dev->board_ptr) + +/* this structure is for data unique to this hardware driver. If + several hardware drivers keep similar information in this structure, + feel free to suggest moving the variable to the struct comedi_device struct. */ struct cb_pcidas_private { + /* would be useful for a PCI device */ struct pci_dev *pci_dev; - /* base addresses */ + /* base addresses */ unsigned long s5933_config; unsigned long control_status; unsigned long adc_fifo; unsigned long pacer_counter_dio; unsigned long ao_registers; - /* divisors of master clock for analog input pacing */ + /* divisors of master clock for analog input pacing */ unsigned int divisor1; unsigned int divisor2; - /* number of analog input samples remaining */ - unsigned int count; - /* bits to write to registers */ - unsigned int adc_fifo_bits; - unsigned int s5933_intcsr_bits; - unsigned int ao_control_bits; - /* fifo buffers */ + volatile unsigned int count; /* number of analog input samples remaining */ + volatile unsigned int adc_fifo_bits; /* bits to write to interrupt/adcfifo register */ + volatile unsigned int s5933_intcsr_bits; /* bits to write to amcc s5933 interrupt control/status register */ + volatile unsigned int ao_control_bits; /* bits to write to ao control and status register */ short ai_buffer[AI_BUFFER_SIZE]; short ao_buffer[AO_BUFFER_SIZE]; - /* divisors of master clock for analog output pacing */ + /* divisors of master clock for analog output pacing */ unsigned int ao_divisor1; unsigned int ao_divisor2; - /* number of analog output samples remaining */ - unsigned int ao_count; - /* cached values for readback */ - int ao_value[2]; - unsigned int caldac_value[NUM_CHANNELS_8800]; - unsigned int trimpot_value[NUM_CHANNELS_8402]; + volatile unsigned int ao_count; /* number of analog output samples remaining */ + int ao_value[2]; /* remember what the analog outputs are set to, to allow readback */ + unsigned int caldac_value[NUM_CHANNELS_8800]; /* for readback of caldac */ + unsigned int trimpot_value[NUM_CHANNELS_8402]; /* for readback of trimpot */ unsigned int dac08_value; unsigned int calibration_source; }; +/* + * most drivers define the following macro to make it easy to + * access the private structure. + */ +#define devpriv ((struct cb_pcidas_private *)dev->private) + +static int cb_pcidas_ai_rinsn(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data); +static int ai_config_insn(struct comedi_device *dev, struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data); +static int cb_pcidas_ao_nofifo_winsn(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data); +static int cb_pcidas_ao_fifo_winsn(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data); +static int cb_pcidas_ao_readback_insn(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data); +static int cb_pcidas_ai_cmd(struct comedi_device *dev, + struct comedi_subdevice *s); +static int cb_pcidas_ai_cmdtest(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_cmd *cmd); +static int cb_pcidas_ao_cmd(struct comedi_device *dev, + struct comedi_subdevice *s); +static int cb_pcidas_ao_inttrig(struct comedi_device *dev, + struct comedi_subdevice *subdev, + unsigned int trig_num); +static int cb_pcidas_ao_cmdtest(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_cmd *cmd); +static irqreturn_t cb_pcidas_interrupt(int irq, void *d); +static void handle_ao_interrupt(struct comedi_device *dev, unsigned int status); +static int cb_pcidas_cancel(struct comedi_device *dev, + struct comedi_subdevice *s); +static int cb_pcidas_ao_cancel(struct comedi_device *dev, + struct comedi_subdevice *s); +static void cb_pcidas_load_counters(struct comedi_device *dev, unsigned int *ns, + int round_flags); +static int eeprom_read_insn(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data); +static int caldac_read_insn(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data); +static int caldac_write_insn(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data); +static int trimpot_read_insn(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data); +static int cb_pcidas_trimpot_write(struct comedi_device *dev, + unsigned int channel, unsigned int value); +static int trimpot_write_insn(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data); +static int dac08_read_insn(struct comedi_device *dev, + struct comedi_subdevice *s, struct comedi_insn *insn, + unsigned int *data); +static int dac08_write(struct comedi_device *dev, unsigned int value); +static int dac08_write_insn(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data); +static int caldac_8800_write(struct comedi_device *dev, unsigned int address, + uint8_t value); +static int trimpot_7376_write(struct comedi_device *dev, uint8_t value); +static int trimpot_8402_write(struct comedi_device *dev, unsigned int channel, + uint8_t value); +static int nvram_read(struct comedi_device *dev, unsigned int address, + uint8_t *data); + static inline unsigned int cal_enable_bits(struct comedi_device *dev) { - struct cb_pcidas_private *devpriv = dev->private; - return CAL_EN_BIT | CAL_SRC_BITS(devpriv->calibration_source); } +/* + * Attach is called by the Comedi core to configure the driver + * for a particular board. + */ +static int cb_pcidas_attach(struct comedi_device *dev, + struct comedi_devconfig *it) +{ + struct comedi_subdevice *s; + struct pci_dev *pcidev = NULL; + int index; + int i; + int ret; + +/* + * Allocate the private structure area. + */ + if (alloc_private(dev, sizeof(struct cb_pcidas_private)) < 0) + return -ENOMEM; + +/* + * Probe the device to determine what device in the series it is. + */ + + for_each_pci_dev(pcidev) { + /* is it not a computer boards card? */ + if (pcidev->vendor != PCI_VENDOR_ID_CB) + continue; + /* loop through cards supported by this driver */ + for (index = 0; index < ARRAY_SIZE(cb_pcidas_boards); index++) { + if (cb_pcidas_boards[index].device_id != pcidev->device) + continue; + /* was a particular bus/slot requested? */ + if (it->options[0] || it->options[1]) { + /* are we on the wrong bus/slot? */ + if (pcidev->bus->number != it->options[0] || + PCI_SLOT(pcidev->devfn) != it->options[1]) { + continue; + } + } + devpriv->pci_dev = pcidev; + dev->board_ptr = cb_pcidas_boards + index; + goto found; + } + } + + dev_err(dev->class_dev, + "No supported ComputerBoards/MeasurementComputing card found on requested position\n"); + return -EIO; + +found: + + dev_dbg(dev->class_dev, "Found %s on bus %i, slot %i\n", + cb_pcidas_boards[index].name, pcidev->bus->number, + PCI_SLOT(pcidev->devfn)); + + /* + * Enable PCI device and reserve I/O ports. + */ + if (comedi_pci_enable(pcidev, "cb_pcidas")) { + dev_err(dev->class_dev, + "Failed to enable PCI device and request regions\n"); + return -EIO; + } + /* + * Initialize devpriv->control_status and devpriv->adc_fifo to point to + * their base address. + */ + devpriv->s5933_config = + pci_resource_start(devpriv->pci_dev, S5933_BADRINDEX); + devpriv->control_status = + pci_resource_start(devpriv->pci_dev, CONT_STAT_BADRINDEX); + devpriv->adc_fifo = + pci_resource_start(devpriv->pci_dev, ADC_FIFO_BADRINDEX); + devpriv->pacer_counter_dio = + pci_resource_start(devpriv->pci_dev, PACER_BADRINDEX); + if (thisboard->ao_nchan) { + devpriv->ao_registers = + pci_resource_start(devpriv->pci_dev, AO_BADRINDEX); + } + /* disable and clear interrupts on amcc s5933 */ + outl(INTCSR_INBOX_INTR_STATUS, + devpriv->s5933_config + AMCC_OP_REG_INTCSR); + + /* get irq */ + if (request_irq(devpriv->pci_dev->irq, cb_pcidas_interrupt, + IRQF_SHARED, "cb_pcidas", dev)) { + dev_dbg(dev->class_dev, "unable to allocate irq %d\n", + devpriv->pci_dev->irq); + return -EINVAL; + } + dev->irq = devpriv->pci_dev->irq; + + /* Initialize dev->board_name */ + dev->board_name = thisboard->name; + + ret = comedi_alloc_subdevices(dev, 7); + if (ret) + return ret; + + s = dev->subdevices + 0; + /* analog input subdevice */ + dev->read_subdev = s; + s->type = COMEDI_SUBD_AI; + s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ; + /* WARNING: Number of inputs in differential mode is ignored */ + s->n_chan = thisboard->ai_se_chans; + s->len_chanlist = thisboard->ai_se_chans; + s->maxdata = (1 << thisboard->ai_bits) - 1; + s->range_table = thisboard->ranges; + s->insn_read = cb_pcidas_ai_rinsn; + s->insn_config = ai_config_insn; + s->do_cmd = cb_pcidas_ai_cmd; + s->do_cmdtest = cb_pcidas_ai_cmdtest; + s->cancel = cb_pcidas_cancel; + + /* analog output subdevice */ + s = dev->subdevices + 1; + if (thisboard->ao_nchan) { + s->type = COMEDI_SUBD_AO; + s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_GROUND; + s->n_chan = thisboard->ao_nchan; + /* analog out resolution is the same as analog input resolution, so use ai_bits */ + s->maxdata = (1 << thisboard->ai_bits) - 1; + s->range_table = &cb_pcidas_ao_ranges; + s->insn_read = cb_pcidas_ao_readback_insn; + if (thisboard->has_ao_fifo) { + dev->write_subdev = s; + s->subdev_flags |= SDF_CMD_WRITE; + s->insn_write = cb_pcidas_ao_fifo_winsn; + s->do_cmdtest = cb_pcidas_ao_cmdtest; + s->do_cmd = cb_pcidas_ao_cmd; + s->cancel = cb_pcidas_ao_cancel; + } else { + s->insn_write = cb_pcidas_ao_nofifo_winsn; + } + } else { + s->type = COMEDI_SUBD_UNUSED; + } + + /* 8255 */ + s = dev->subdevices + 2; + subdev_8255_init(dev, s, NULL, devpriv->pacer_counter_dio + DIO_8255); + + /* serial EEPROM, */ + s = dev->subdevices + 3; + s->type = COMEDI_SUBD_MEMORY; + s->subdev_flags = SDF_READABLE | SDF_INTERNAL; + s->n_chan = 256; + s->maxdata = 0xff; + s->insn_read = eeprom_read_insn; + + /* 8800 caldac */ + s = dev->subdevices + 4; + s->type = COMEDI_SUBD_CALIB; + s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL; + s->n_chan = NUM_CHANNELS_8800; + s->maxdata = 0xff; + s->insn_read = caldac_read_insn; + s->insn_write = caldac_write_insn; + for (i = 0; i < s->n_chan; i++) + caldac_8800_write(dev, i, s->maxdata / 2); + + /* trim potentiometer */ + s = dev->subdevices + 5; + s->type = COMEDI_SUBD_CALIB; + s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL; + if (thisboard->trimpot == AD7376) { + s->n_chan = NUM_CHANNELS_7376; + s->maxdata = 0x7f; + } else { + s->n_chan = NUM_CHANNELS_8402; + s->maxdata = 0xff; + } + s->insn_read = trimpot_read_insn; + s->insn_write = trimpot_write_insn; + for (i = 0; i < s->n_chan; i++) + cb_pcidas_trimpot_write(dev, i, s->maxdata / 2); + + /* dac08 caldac */ + s = dev->subdevices + 6; + if (thisboard->has_dac08) { + s->type = COMEDI_SUBD_CALIB; + s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL; + s->n_chan = NUM_CHANNELS_DAC08; + s->insn_read = dac08_read_insn; + s->insn_write = dac08_write_insn; + s->maxdata = 0xff; + dac08_write(dev, s->maxdata / 2); + } else + s->type = COMEDI_SUBD_UNUSED; + + /* make sure mailbox 4 is empty */ + inl(devpriv->s5933_config + AMCC_OP_REG_IMB4); + /* Set bits to enable incoming mailbox interrupts on amcc s5933. */ + devpriv->s5933_intcsr_bits = + INTCSR_INBOX_BYTE(3) | INTCSR_INBOX_SELECT(3) | + INTCSR_INBOX_FULL_INT; + /* clear and enable interrupt on amcc s5933 */ + outl(devpriv->s5933_intcsr_bits | INTCSR_INBOX_INTR_STATUS, + devpriv->s5933_config + AMCC_OP_REG_INTCSR); + + return 1; +} + +static void cb_pcidas_detach(struct comedi_device *dev) +{ + if (devpriv) { + if (devpriv->s5933_config) { + outl(INTCSR_INBOX_INTR_STATUS, + devpriv->s5933_config + AMCC_OP_REG_INTCSR); + } + } + if (dev->irq) + free_irq(dev->irq, dev); + if (dev->subdevices) + subdev_8255_cleanup(dev, dev->subdevices + 2); + if (devpriv && devpriv->pci_dev) { + if (devpriv->s5933_config) + comedi_pci_disable(devpriv->pci_dev); + pci_dev_put(devpriv->pci_dev); + } +} + +/* + * "instructions" read/write data in "one-shot" or "software-triggered" + * mode. + */ static int cb_pcidas_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { - struct cb_pcidas_private *devpriv = dev->private; - unsigned int chan = CR_CHAN(insn->chanspec); - unsigned int range = CR_RANGE(insn->chanspec); - unsigned int aref = CR_AREF(insn->chanspec); - unsigned int bits; int n, i; - - /* enable calibration input if appropriate */ + unsigned int bits; + static const int timeout = 10000; + int channel; + /* enable calibration input if appropriate */ if (insn->chanspec & CR_ALT_SOURCE) { outw(cal_enable_bits(dev), devpriv->control_status + CALIBRATION_REG); - chan = 0; + channel = 0; } else { outw(0, devpriv->control_status + CALIBRATION_REG); + channel = CR_CHAN(insn->chanspec); } - - /* set mux limits and gain */ - bits = BEGIN_SCAN(chan) | END_SCAN(chan) | GAIN_BITS(range); - /* set unipolar/bipolar */ - if (range & IS_UNIPOLAR) + /* set mux limits and gain */ + bits = BEGIN_SCAN(channel) | + END_SCAN(channel) | GAIN_BITS(CR_RANGE(insn->chanspec)); + /* set unipolar/bipolar */ + if (CR_RANGE(insn->chanspec) & IS_UNIPOLAR) bits |= UNIP; - /* set single-ended/differential */ - if (aref != AREF_DIFF) + /* set singleended/differential */ + if (CR_AREF(insn->chanspec) != AREF_DIFF) bits |= SE; outw(bits, devpriv->control_status + ADCMUX_CONT); @@ -422,11 +787,11 @@ static int cb_pcidas_ai_rinsn(struct comedi_device *dev, /* wait for conversion to end */ /* return -ETIMEDOUT if there is a timeout */ - for (i = 0; i < 10000; i++) { + for (i = 0; i < timeout; i++) { if (inw(devpriv->control_status + ADCMUX_CONT) & EOC) break; } - if (i == 10000) + if (i == timeout) return -ETIMEDOUT; /* read data */ @@ -437,28 +802,37 @@ static int cb_pcidas_ai_rinsn(struct comedi_device *dev, return n; } +static int ai_config_calibration_source(struct comedi_device *dev, + unsigned int *data) +{ + static const int num_calibration_sources = 8; + unsigned int source = data[1]; + + if (source >= num_calibration_sources) { + dev_err(dev->class_dev, "invalid calibration source: %i\n", + source); + return -EINVAL; + } + + devpriv->calibration_source = source; + + return 2; +} + static int ai_config_insn(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { - struct cb_pcidas_private *devpriv = dev->private; int id = data[0]; - unsigned int source = data[1]; switch (id) { case INSN_CONFIG_ALT_SOURCE: - if (source >= 8) { - dev_err(dev->class_dev, - "invalid calibration source: %i\n", - source); - return -EINVAL; - } - devpriv->calibration_source = source; + return ai_config_calibration_source(dev, data); break; default: return -EINVAL; break; } - return insn->n; + return -EINVAL; } /* analog output insn for pcidas-1000 and 1200 series */ @@ -467,26 +841,25 @@ static int cb_pcidas_ao_nofifo_winsn(struct comedi_device *dev, struct comedi_insn *insn, unsigned int *data) { - struct cb_pcidas_private *devpriv = dev->private; - unsigned int chan = CR_CHAN(insn->chanspec); - unsigned int range = CR_RANGE(insn->chanspec); + int channel; unsigned long flags; - /* set channel and range */ + /* set channel and range */ + channel = CR_CHAN(insn->chanspec); spin_lock_irqsave(&dev->spinlock, flags); - devpriv->ao_control_bits &= (~DAC_MODE_UPDATE_BOTH & - ~DAC_RANGE_MASK(chan)); - devpriv->ao_control_bits |= (DACEN | DAC_RANGE(chan, range)); + devpriv->ao_control_bits &= + ~DAC_MODE_UPDATE_BOTH & ~DAC_RANGE_MASK(channel); + devpriv->ao_control_bits |= + DACEN | DAC_RANGE(channel, CR_RANGE(insn->chanspec)); outw(devpriv->ao_control_bits, devpriv->control_status + DAC_CSR); spin_unlock_irqrestore(&dev->spinlock, flags); - /* remember value for readback */ - devpriv->ao_value[chan] = data[0]; + /* remember value for readback */ + devpriv->ao_value[channel] = data[0]; + /* send data */ + outw(data[0], devpriv->ao_registers + DAC_DATA_REG(channel)); - /* send data */ - outw(data[0], devpriv->ao_registers + DAC_DATA_REG(chan)); - - return insn->n; + return 1; } /* analog output insn for pcidas-1602 series */ @@ -494,84 +867,46 @@ static int cb_pcidas_ao_fifo_winsn(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { - struct cb_pcidas_private *devpriv = dev->private; - unsigned int chan = CR_CHAN(insn->chanspec); - unsigned int range = CR_RANGE(insn->chanspec); + int channel; unsigned long flags; - /* clear dac fifo */ + /* clear dac fifo */ outw(0, devpriv->ao_registers + DACFIFOCLR); - /* set channel and range */ + /* set channel and range */ + channel = CR_CHAN(insn->chanspec); spin_lock_irqsave(&dev->spinlock, flags); - devpriv->ao_control_bits &= (~DAC_CHAN_EN(0) & ~DAC_CHAN_EN(1) & - ~DAC_RANGE_MASK(chan) & ~DAC_PACER_MASK); - devpriv->ao_control_bits |= (DACEN | DAC_RANGE(chan, range) | - DAC_CHAN_EN(chan) | DAC_START); + devpriv->ao_control_bits &= + ~DAC_CHAN_EN(0) & ~DAC_CHAN_EN(1) & ~DAC_RANGE_MASK(channel) & + ~DAC_PACER_MASK; + devpriv->ao_control_bits |= + DACEN | DAC_RANGE(channel, + CR_RANGE(insn-> + chanspec)) | DAC_CHAN_EN(channel) | + DAC_START; outw(devpriv->ao_control_bits, devpriv->control_status + DAC_CSR); spin_unlock_irqrestore(&dev->spinlock, flags); - /* remember value for readback */ - devpriv->ao_value[chan] = data[0]; - - /* send data */ + /* remember value for readback */ + devpriv->ao_value[channel] = data[0]; + /* send data */ outw(data[0], devpriv->ao_registers + DACDATA); - return insn->n; + return 1; } +/* analog output readback insn */ +/* XXX loses track of analog output value back after an analog ouput command is executed */ static int cb_pcidas_ao_readback_insn(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { - struct cb_pcidas_private *devpriv = dev->private; - data[0] = devpriv->ao_value[CR_CHAN(insn->chanspec)]; return 1; } -static int wait_for_nvram_ready(unsigned long s5933_base_addr) -{ - static const int timeout = 1000; - unsigned int i; - - for (i = 0; i < timeout; i++) { - if ((inb(s5933_base_addr + - AMCC_OP_REG_MCSR_NVCMD) & MCSR_NV_BUSY) - == 0) - return 0; - udelay(1); - } - return -1; -} - -static int nvram_read(struct comedi_device *dev, unsigned int address, - uint8_t *data) -{ - struct cb_pcidas_private *devpriv = dev->private; - unsigned long iobase = devpriv->s5933_config; - - if (wait_for_nvram_ready(iobase) < 0) - return -ETIMEDOUT; - - outb(MCSR_NV_ENABLE | MCSR_NV_LOAD_LOW_ADDR, - iobase + AMCC_OP_REG_MCSR_NVCMD); - outb(address & 0xff, iobase + AMCC_OP_REG_MCSR_NVDATA); - outb(MCSR_NV_ENABLE | MCSR_NV_LOAD_HIGH_ADDR, - iobase + AMCC_OP_REG_MCSR_NVCMD); - outb((address >> 8) & 0xff, iobase + AMCC_OP_REG_MCSR_NVDATA); - outb(MCSR_NV_ENABLE | MCSR_NV_READ, iobase + AMCC_OP_REG_MCSR_NVCMD); - - if (wait_for_nvram_ready(iobase) < 0) - return -ETIMEDOUT; - - *data = inb(iobase + AMCC_OP_REG_MCSR_NVDATA); - - return 0; -} - static int eeprom_read_insn(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) @@ -588,56 +923,6 @@ static int eeprom_read_insn(struct comedi_device *dev, return 1; } -static void write_calibration_bitstream(struct comedi_device *dev, - unsigned int register_bits, - unsigned int bitstream, - unsigned int bitstream_length) -{ - struct cb_pcidas_private *devpriv = dev->private; - static const int write_delay = 1; - unsigned int bit; - - for (bit = 1 << (bitstream_length - 1); bit; bit >>= 1) { - if (bitstream & bit) - register_bits |= SERIAL_DATA_IN_BIT; - else - register_bits &= ~SERIAL_DATA_IN_BIT; - udelay(write_delay); - outw(register_bits, devpriv->control_status + CALIBRATION_REG); - } -} - -static int caldac_8800_write(struct comedi_device *dev, unsigned int address, - uint8_t value) -{ - struct cb_pcidas_private *devpriv = dev->private; - static const int num_caldac_channels = 8; - static const int bitstream_length = 11; - unsigned int bitstream = ((address & 0x7) << 8) | value; - static const int caldac_8800_udelay = 1; - - if (address >= num_caldac_channels) { - comedi_error(dev, "illegal caldac channel"); - return -1; - } - - if (value == devpriv->caldac_value[address]) - return 1; - - devpriv->caldac_value[address] = value; - - write_calibration_bitstream(dev, cal_enable_bits(dev), bitstream, - bitstream_length); - - udelay(caldac_8800_udelay); - outw(cal_enable_bits(dev) | SELECT_8800_BIT, - devpriv->control_status + CALIBRATION_REG); - udelay(caldac_8800_udelay); - outw(cal_enable_bits(dev), devpriv->control_status + CALIBRATION_REG); - - return 1; -} - static int caldac_write_insn(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) @@ -651,112 +936,51 @@ static int caldac_read_insn(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { - struct cb_pcidas_private *devpriv = dev->private; - data[0] = devpriv->caldac_value[CR_CHAN(insn->chanspec)]; return 1; } /* 1602/16 pregain offset */ -static void dac08_write(struct comedi_device *dev, unsigned int value) +static int dac08_write(struct comedi_device *dev, unsigned int value) { - struct cb_pcidas_private *devpriv = dev->private; - unsigned long cal_reg; - - if (devpriv->dac08_value != value) { - devpriv->dac08_value = value; + if (devpriv->dac08_value == value) + return 1; - cal_reg = devpriv->control_status + CALIBRATION_REG; + devpriv->dac08_value = value; - value &= 0xff; - value |= cal_enable_bits(dev); + outw(cal_enable_bits(dev) | (value & 0xff), + devpriv->control_status + CALIBRATION_REG); + udelay(1); + outw(cal_enable_bits(dev) | SELECT_DAC08_BIT | (value & 0xff), + devpriv->control_status + CALIBRATION_REG); + udelay(1); + outw(cal_enable_bits(dev) | (value & 0xff), + devpriv->control_status + CALIBRATION_REG); + udelay(1); - /* latch the new value into the caldac */ - outw(value, cal_reg); - udelay(1); - outw(value | SELECT_DAC08_BIT, cal_reg); - udelay(1); - outw(value, cal_reg); - udelay(1); - } + return 1; } static int dac08_write_insn(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { - int i; - - for (i = 0; i < insn->n; i++) - dac08_write(dev, data[i]); - - return insn->n; + return dac08_write(dev, data[0]); } static int dac08_read_insn(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { - struct cb_pcidas_private *devpriv = dev->private; - data[0] = devpriv->dac08_value; return 1; } -static int trimpot_7376_write(struct comedi_device *dev, uint8_t value) -{ - struct cb_pcidas_private *devpriv = dev->private; - static const int bitstream_length = 7; - unsigned int bitstream = value & 0x7f; - unsigned int register_bits; - static const int ad7376_udelay = 1; - - register_bits = cal_enable_bits(dev) | SELECT_TRIMPOT_BIT; - udelay(ad7376_udelay); - outw(register_bits, devpriv->control_status + CALIBRATION_REG); - - write_calibration_bitstream(dev, register_bits, bitstream, - bitstream_length); - - udelay(ad7376_udelay); - outw(cal_enable_bits(dev), devpriv->control_status + CALIBRATION_REG); - - return 0; -} - -/* For 1602/16 only - * ch 0 : adc gain - * ch 1 : adc postgain offset */ -static int trimpot_8402_write(struct comedi_device *dev, unsigned int channel, - uint8_t value) -{ - struct cb_pcidas_private *devpriv = dev->private; - static const int bitstream_length = 10; - unsigned int bitstream = ((channel & 0x3) << 8) | (value & 0xff); - unsigned int register_bits; - static const int ad8402_udelay = 1; - - register_bits = cal_enable_bits(dev) | SELECT_TRIMPOT_BIT; - udelay(ad8402_udelay); - outw(register_bits, devpriv->control_status + CALIBRATION_REG); - - write_calibration_bitstream(dev, register_bits, bitstream, - bitstream_length); - - udelay(ad8402_udelay); - outw(cal_enable_bits(dev), devpriv->control_status + CALIBRATION_REG); - - return 0; -} - static int cb_pcidas_trimpot_write(struct comedi_device *dev, unsigned int channel, unsigned int value) { - const struct cb_pcidas_board *thisboard = comedi_board(dev); - struct cb_pcidas_private *devpriv = dev->private; - if (devpriv->trimpot_value[channel] == value) return 1; @@ -790,7 +1014,6 @@ static int trimpot_read_insn(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { - struct cb_pcidas_private *devpriv = dev->private; unsigned int channel = CR_CHAN(insn->chanspec); data[0] = devpriv->trimpot_value[channel]; @@ -802,13 +1025,18 @@ static int cb_pcidas_ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_cmd *cmd) { - const struct cb_pcidas_board *thisboard = comedi_board(dev); - struct cb_pcidas_private *devpriv = dev->private; int err = 0; int tmp; int i, gain, start_chan; - /* step 1: trigger sources are trivially valid */ + /* cmdtest tests a particular command to see if it is valid. + * Using the cmdtest ioctl, a user can create a valid cmd + * and then have it executes by the cmd ioctl. + * + * cmdtest returns 1,2,3,4 or 0, depending on which tests + * the command passes. */ + + /* step 1: make sure trigger sources are trivially valid */ tmp = cmd->start_src; cmd->start_src &= TRIG_NOW | TRIG_EXT; @@ -838,7 +1066,7 @@ static int cb_pcidas_ai_cmdtest(struct comedi_device *dev, if (err) return 1; - /* step 2: trigger sources are unique and mutually compatible */ + /* step 2: make sure trigger sources are unique and mutually compatible */ if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT) err++; @@ -864,7 +1092,7 @@ static int cb_pcidas_ai_cmdtest(struct comedi_device *dev, if (err) return 2; - /* step 3: arguments are trivially compatible */ + /* step 3: make sure arguments are trivially compatible */ switch (cmd->start_src) { case TRIG_EXT: @@ -875,7 +1103,8 @@ static int cb_pcidas_ai_cmdtest(struct comedi_device *dev, ~(CR_FLAGS_MASK & ~(CR_EDGE | CR_INVERT)); err++; } - if (!thisboard->is_1602 && (cmd->start_arg & CR_INVERT)) { + if (!thisboard->has_ai_trig_invert && + (cmd->start_arg & CR_INVERT)) { cmd->start_arg &= (CR_FLAGS_MASK & ~CR_INVERT); err++; } @@ -969,27 +1198,9 @@ static int cb_pcidas_ai_cmdtest(struct comedi_device *dev, return 0; } -static void cb_pcidas_load_counters(struct comedi_device *dev, unsigned int *ns, - int rounding_flags) -{ - struct cb_pcidas_private *devpriv = dev->private; - - i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1), - &(devpriv->divisor2), ns, - rounding_flags & TRIG_ROUND_MASK); - - /* Write the values of ctr1 and ctr2 into counters 1 and 2 */ - i8254_load(devpriv->pacer_counter_dio + ADC8254, 0, 1, - devpriv->divisor1, 2); - i8254_load(devpriv->pacer_counter_dio + ADC8254, 0, 2, - devpriv->divisor2, 2); -} - static int cb_pcidas_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) { - const struct cb_pcidas_board *thisboard = comedi_board(dev); - struct cb_pcidas_private *devpriv = dev->private; struct comedi_async *async = s->async; struct comedi_cmd *cmd = &async->cmd; unsigned int bits; @@ -1019,6 +1230,10 @@ static int cb_pcidas_ai_cmd(struct comedi_device *dev, bits |= PACER_INT; outw(bits, devpriv->control_status + ADCMUX_CONT); +#ifdef CB_PCIDAS_DEBUG + dev_dbg(dev->class_dev, "sent 0x%x to adcmux control\n", bits); +#endif + /* load counters */ if (cmd->convert_src == TRIG_TIMER) cb_pcidas_load_counters(dev, &cmd->convert_arg, @@ -1035,18 +1250,17 @@ static int cb_pcidas_ai_cmd(struct comedi_device *dev, devpriv->adc_fifo_bits |= INTE; devpriv->adc_fifo_bits &= ~INT_MASK; if (cmd->flags & TRIG_WAKE_EOS) { - if (cmd->convert_src == TRIG_NOW && cmd->chanlist_len > 1) { - /* interrupt end of burst */ - devpriv->adc_fifo_bits |= INT_EOS; - } else { - /* interrupt fifo not empty */ - devpriv->adc_fifo_bits |= INT_FNE; - } + if (cmd->convert_src == TRIG_NOW && cmd->chanlist_len > 1) + devpriv->adc_fifo_bits |= INT_EOS; /* interrupt end of burst */ + else + devpriv->adc_fifo_bits |= INT_FNE; /* interrupt fifo not empty */ } else { - /* interrupt fifo half full */ - devpriv->adc_fifo_bits |= INT_FHF; + devpriv->adc_fifo_bits |= INT_FHF; /* interrupt fifo half full */ } - +#ifdef CB_PCIDAS_DEBUG + dev_dbg(dev->class_dev, "adc_fifo_bits are 0x%x\n", + devpriv->adc_fifo_bits); +#endif /* enable (and clear) interrupts */ outw(devpriv->adc_fifo_bits | EOAI | INT | LADFUL, devpriv->control_status + INT_ADCFIFO); @@ -1058,12 +1272,11 @@ static int cb_pcidas_ai_cmd(struct comedi_device *dev, bits |= SW_TRIGGER; else if (cmd->start_src == TRIG_EXT) { bits |= EXT_TRIGGER | TGEN | XTRCL; - if (thisboard->is_1602) { - if (cmd->start_arg & CR_INVERT) - bits |= TGPOL; - if (cmd->start_arg & CR_EDGE) - bits |= TGSEL; - } + if (thisboard->has_ai_trig_invert + && (cmd->start_arg & CR_INVERT)) + bits |= TGPOL; + if (thisboard->has_ai_trig_gated && (cmd->start_arg & CR_EDGE)) + bits |= TGSEL; } else { comedi_error(dev, "bug!"); return -1; @@ -1071,6 +1284,9 @@ static int cb_pcidas_ai_cmd(struct comedi_device *dev, if (cmd->convert_src == TRIG_NOW && cmd->chanlist_len > 1) bits |= BURSTE; outw(bits, devpriv->control_status + TRIG_CONTSTAT); +#ifdef CB_PCIDAS_DEBUG + dev_dbg(dev->class_dev, "sent 0x%x to trig control\n", bits); +#endif return 0; } @@ -1079,12 +1295,17 @@ static int cb_pcidas_ao_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_cmd *cmd) { - const struct cb_pcidas_board *thisboard = comedi_board(dev); - struct cb_pcidas_private *devpriv = dev->private; int err = 0; int tmp; - /* step 1: trigger sources are trivially valid */ + /* cmdtest tests a particular command to see if it is valid. + * Using the cmdtest ioctl, a user can create a valid cmd + * and then have it executes by the cmd ioctl. + * + * cmdtest returns 1,2,3,4 or 0, depending on which tests + * the command passes. */ + + /* step 1: make sure trigger sources are trivially valid */ tmp = cmd->start_src; cmd->start_src &= TRIG_INT; @@ -1114,7 +1335,7 @@ static int cb_pcidas_ao_cmdtest(struct comedi_device *dev, if (err) return 1; - /* step 2: trigger sources are unique and mutually compatible */ + /* step 2: make sure trigger sources are unique and mutually compatible */ if (cmd->scan_begin_src != TRIG_TIMER && cmd->scan_begin_src != TRIG_EXT) @@ -1125,7 +1346,7 @@ static int cb_pcidas_ao_cmdtest(struct comedi_device *dev, if (err) return 2; - /* step 3: arguments are trivially compatible */ + /* step 3: make sure arguments are trivially compatible */ if (cmd->start_arg != 0) { cmd->start_arg = 0; @@ -1186,77 +1407,9 @@ static int cb_pcidas_ao_cmdtest(struct comedi_device *dev, return 0; } -/* cancel analog input command */ -static int cb_pcidas_cancel(struct comedi_device *dev, - struct comedi_subdevice *s) -{ - struct cb_pcidas_private *devpriv = dev->private; - unsigned long flags; - - spin_lock_irqsave(&dev->spinlock, flags); - /* disable interrupts */ - devpriv->adc_fifo_bits &= ~INTE & ~EOAIE; - outw(devpriv->adc_fifo_bits, devpriv->control_status + INT_ADCFIFO); - spin_unlock_irqrestore(&dev->spinlock, flags); - - /* disable start trigger source and burst mode */ - outw(0, devpriv->control_status + TRIG_CONTSTAT); - /* software pacer source */ - outw(0, devpriv->control_status + ADCMUX_CONT); - - return 0; -} - -static int cb_pcidas_ao_inttrig(struct comedi_device *dev, - struct comedi_subdevice *s, - unsigned int trig_num) -{ - const struct cb_pcidas_board *thisboard = comedi_board(dev); - struct cb_pcidas_private *devpriv = dev->private; - unsigned int num_bytes, num_points = thisboard->fifo_size; - struct comedi_async *async = s->async; - struct comedi_cmd *cmd = &s->async->cmd; - unsigned long flags; - - if (trig_num != 0) - return -EINVAL; - - /* load up fifo */ - if (cmd->stop_src == TRIG_COUNT && devpriv->ao_count < num_points) - num_points = devpriv->ao_count; - - num_bytes = cfc_read_array_from_buffer(s, devpriv->ao_buffer, - num_points * sizeof(short)); - num_points = num_bytes / sizeof(short); - - if (cmd->stop_src == TRIG_COUNT) - devpriv->ao_count -= num_points; - /* write data to board's fifo */ - outsw(devpriv->ao_registers + DACDATA, devpriv->ao_buffer, num_bytes); - - /* enable dac half-full and empty interrupts */ - spin_lock_irqsave(&dev->spinlock, flags); - devpriv->adc_fifo_bits |= DAEMIE | DAHFIE; - - /* enable and clear interrupts */ - outw(devpriv->adc_fifo_bits | DAEMI | DAHFI, - devpriv->control_status + INT_ADCFIFO); - - /* start dac */ - devpriv->ao_control_bits |= DAC_START | DACEN | DAC_EMPTY; - outw(devpriv->ao_control_bits, devpriv->control_status + DAC_CSR); - - spin_unlock_irqrestore(&dev->spinlock, flags); - - async->inttrig = NULL; - - return 0; -} - static int cb_pcidas_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s) { - struct cb_pcidas_private *devpriv = dev->private; struct comedi_async *async = s->async; struct comedi_cmd *cmd = &async->cmd; unsigned int i; @@ -1320,88 +1473,59 @@ static int cb_pcidas_ao_cmd(struct comedi_device *dev, return 0; } -/* cancel analog output command */ -static int cb_pcidas_ao_cancel(struct comedi_device *dev, - struct comedi_subdevice *s) +static int cb_pcidas_ao_inttrig(struct comedi_device *dev, + struct comedi_subdevice *s, + unsigned int trig_num) { - struct cb_pcidas_private *devpriv = dev->private; + unsigned int num_bytes, num_points = thisboard->fifo_size; + struct comedi_async *async = s->async; + struct comedi_cmd *cmd = &s->async->cmd; unsigned long flags; - spin_lock_irqsave(&dev->spinlock, flags); - /* disable interrupts */ - devpriv->adc_fifo_bits &= ~DAHFIE & ~DAEMIE; - outw(devpriv->adc_fifo_bits, devpriv->control_status + INT_ADCFIFO); + if (trig_num != 0) + return -EINVAL; - /* disable output */ - devpriv->ao_control_bits &= ~DACEN & ~DAC_PACER_MASK; - outw(devpriv->ao_control_bits, devpriv->control_status + DAC_CSR); - spin_unlock_irqrestore(&dev->spinlock, flags); - - return 0; -} + /* load up fifo */ + if (cmd->stop_src == TRIG_COUNT && devpriv->ao_count < num_points) + num_points = devpriv->ao_count; -static void handle_ao_interrupt(struct comedi_device *dev, unsigned int status) -{ - const struct cb_pcidas_board *thisboard = comedi_board(dev); - struct cb_pcidas_private *devpriv = dev->private; - struct comedi_subdevice *s = dev->write_subdev; - struct comedi_async *async = s->async; - struct comedi_cmd *cmd = &async->cmd; - unsigned int half_fifo = thisboard->fifo_size / 2; - unsigned int num_points; - unsigned long flags; + num_bytes = cfc_read_array_from_buffer(s, devpriv->ao_buffer, + num_points * sizeof(short)); + num_points = num_bytes / sizeof(short); - async->events = 0; + if (cmd->stop_src == TRIG_COUNT) + devpriv->ao_count -= num_points; + /* write data to board's fifo */ + outsw(devpriv->ao_registers + DACDATA, devpriv->ao_buffer, num_bytes); - if (status & DAEMI) { - /* clear dac empty interrupt latch */ - spin_lock_irqsave(&dev->spinlock, flags); - outw(devpriv->adc_fifo_bits | DAEMI, - devpriv->control_status + INT_ADCFIFO); - spin_unlock_irqrestore(&dev->spinlock, flags); - if (inw(devpriv->ao_registers + DAC_CSR) & DAC_EMPTY) { - if (cmd->stop_src == TRIG_NONE || - (cmd->stop_src == TRIG_COUNT - && devpriv->ao_count)) { - comedi_error(dev, "dac fifo underflow"); - cb_pcidas_ao_cancel(dev, s); - async->events |= COMEDI_CB_ERROR; - } - async->events |= COMEDI_CB_EOA; - } - } else if (status & DAHFI) { - unsigned int num_bytes; + /* enable dac half-full and empty interrupts */ + spin_lock_irqsave(&dev->spinlock, flags); + devpriv->adc_fifo_bits |= DAEMIE | DAHFIE; +#ifdef CB_PCIDAS_DEBUG + dev_dbg(dev->class_dev, "adc_fifo_bits are 0x%x\n", + devpriv->adc_fifo_bits); +#endif + /* enable and clear interrupts */ + outw(devpriv->adc_fifo_bits | DAEMI | DAHFI, + devpriv->control_status + INT_ADCFIFO); - /* figure out how many points we are writing to fifo */ - num_points = half_fifo; - if (cmd->stop_src == TRIG_COUNT && - devpriv->ao_count < num_points) - num_points = devpriv->ao_count; - num_bytes = - cfc_read_array_from_buffer(s, devpriv->ao_buffer, - num_points * sizeof(short)); - num_points = num_bytes / sizeof(short); + /* start dac */ + devpriv->ao_control_bits |= DAC_START | DACEN | DAC_EMPTY; + outw(devpriv->ao_control_bits, devpriv->control_status + DAC_CSR); +#ifdef CB_PCIDAS_DEBUG + dev_dbg(dev->class_dev, "sent 0x%x to dac control\n", + devpriv->ao_control_bits); +#endif + spin_unlock_irqrestore(&dev->spinlock, flags); - if (async->cmd.stop_src == TRIG_COUNT) - devpriv->ao_count -= num_points; - /* write data to board's fifo */ - outsw(devpriv->ao_registers + DACDATA, devpriv->ao_buffer, - num_points); - /* clear half-full interrupt latch */ - spin_lock_irqsave(&dev->spinlock, flags); - outw(devpriv->adc_fifo_bits | DAHFI, - devpriv->control_status + INT_ADCFIFO); - spin_unlock_irqrestore(&dev->spinlock, flags); - } + async->inttrig = NULL; - comedi_event(dev, s); + return 0; } static irqreturn_t cb_pcidas_interrupt(int irq, void *d) { struct comedi_device *dev = (struct comedi_device *)d; - const struct cb_pcidas_board *thisboard = comedi_board(dev); - struct cb_pcidas_private *devpriv = dev->private; struct comedi_subdevice *s = dev->read_subdev; struct comedi_async *async; int status, s5933_status; @@ -1417,6 +1541,11 @@ static irqreturn_t cb_pcidas_interrupt(int irq, void *d) async->events = 0; s5933_status = inl(devpriv->s5933_config + AMCC_OP_REG_INTCSR); +#ifdef CB_PCIDAS_DEBUG + dev_dbg(dev->class_dev, "intcsr 0x%x\n", s5933_status); + dev_dbg(dev->class_dev, "mbef 0x%x\n", + inl(devpriv->s5933_config + AMCC_OP_REG_MBEF)); +#endif if ((INTCSR_INTR_ASSERTED & s5933_status) == 0) return IRQ_NONE; @@ -1428,6 +1557,10 @@ static irqreturn_t cb_pcidas_interrupt(int irq, void *d) devpriv->s5933_config + AMCC_OP_REG_INTCSR); status = inw(devpriv->control_status + INT_ADCFIFO); +#ifdef CB_PCIDAS_DEBUG + if ((status & (INT | EOAI | LADFUL | DAHFI | DAEMI)) == 0) + comedi_error(dev, "spurious interrupt"); +#endif /* check for analog output interrupt */ if (status & (DAHFI | DAEMI)) @@ -1463,9 +1596,7 @@ static irqreturn_t cb_pcidas_interrupt(int irq, void *d) INT_ADCFIFO)) == 0) break; cfc_write_to_buffer(s, inw(devpriv->adc_fifo)); - if (async->cmd.stop_src == TRIG_COUNT && - --devpriv->count == 0) { - /* end of acquisition */ + if (async->cmd.stop_src == TRIG_COUNT && --devpriv->count == 0) { /* end of acquisition */ cb_pcidas_cancel(dev, s); async->events |= COMEDI_CB_EOA; break; @@ -1502,223 +1633,243 @@ static irqreturn_t cb_pcidas_interrupt(int irq, void *d) return IRQ_HANDLED; } -static struct pci_dev *cb_pcidas_find_pci_device(struct comedi_device *dev, - struct comedi_devconfig *it) +static void handle_ao_interrupt(struct comedi_device *dev, unsigned int status) { - const struct cb_pcidas_board *thisboard; - struct pci_dev *pcidev = NULL; - int bus = it->options[0]; - int slot = it->options[1]; - int i; + struct comedi_subdevice *s = dev->write_subdev; + struct comedi_async *async = s->async; + struct comedi_cmd *cmd = &async->cmd; + unsigned int half_fifo = thisboard->fifo_size / 2; + unsigned int num_points; + unsigned long flags; - for_each_pci_dev(pcidev) { - /* is it not a computer boards card? */ - if (pcidev->vendor != PCI_VENDOR_ID_CB) - continue; - /* loop through cards supported by this driver */ - for (i = 0; i < ARRAY_SIZE(cb_pcidas_boards); i++) { - thisboard = &cb_pcidas_boards[i]; - if (thisboard->device_id != pcidev->device) - continue; - /* was a particular bus/slot requested? */ - if (bus || slot) { - /* are we on the wrong bus/slot? */ - if (pcidev->bus->number != bus || - PCI_SLOT(pcidev->devfn) != slot) { - continue; - } + async->events = 0; + + if (status & DAEMI) { + /* clear dac empty interrupt latch */ + spin_lock_irqsave(&dev->spinlock, flags); + outw(devpriv->adc_fifo_bits | DAEMI, + devpriv->control_status + INT_ADCFIFO); + spin_unlock_irqrestore(&dev->spinlock, flags); + if (inw(devpriv->ao_registers + DAC_CSR) & DAC_EMPTY) { + if (cmd->stop_src == TRIG_NONE || + (cmd->stop_src == TRIG_COUNT + && devpriv->ao_count)) { + comedi_error(dev, "dac fifo underflow"); + cb_pcidas_ao_cancel(dev, s); + async->events |= COMEDI_CB_ERROR; } - dev_dbg(dev->class_dev, - "Found %s on bus %i, slot %i\n", - thisboard->name, - pcidev->bus->number, PCI_SLOT(pcidev->devfn)); - dev->board_ptr = thisboard; - return pcidev; + async->events |= COMEDI_CB_EOA; } + } else if (status & DAHFI) { + unsigned int num_bytes; + + /* figure out how many points we are writing to fifo */ + num_points = half_fifo; + if (cmd->stop_src == TRIG_COUNT && + devpriv->ao_count < num_points) + num_points = devpriv->ao_count; + num_bytes = + cfc_read_array_from_buffer(s, devpriv->ao_buffer, + num_points * sizeof(short)); + num_points = num_bytes / sizeof(short); + + if (async->cmd.stop_src == TRIG_COUNT) + devpriv->ao_count -= num_points; + /* write data to board's fifo */ + outsw(devpriv->ao_registers + DACDATA, devpriv->ao_buffer, + num_points); + /* clear half-full interrupt latch */ + spin_lock_irqsave(&dev->spinlock, flags); + outw(devpriv->adc_fifo_bits | DAHFI, + devpriv->control_status + INT_ADCFIFO); + spin_unlock_irqrestore(&dev->spinlock, flags); } - dev_err(dev->class_dev, "No supported card found\n"); - return NULL; + + comedi_event(dev, s); } -static int cb_pcidas_attach(struct comedi_device *dev, - struct comedi_devconfig *it) +/* cancel analog input command */ +static int cb_pcidas_cancel(struct comedi_device *dev, + struct comedi_subdevice *s) { - const struct cb_pcidas_board *thisboard; - struct cb_pcidas_private *devpriv; - struct comedi_subdevice *s; - int i; - int ret; + unsigned long flags; - if (alloc_private(dev, sizeof(struct cb_pcidas_private)) < 0) - return -ENOMEM; - devpriv = dev->private; + spin_lock_irqsave(&dev->spinlock, flags); + /* disable interrupts */ + devpriv->adc_fifo_bits &= ~INTE & ~EOAIE; + outw(devpriv->adc_fifo_bits, devpriv->control_status + INT_ADCFIFO); + spin_unlock_irqrestore(&dev->spinlock, flags); - devpriv->pci_dev = cb_pcidas_find_pci_device(dev, it); - if (!devpriv->pci_dev) - return -EIO; - thisboard = comedi_board(dev); + /* disable start trigger source and burst mode */ + outw(0, devpriv->control_status + TRIG_CONTSTAT); + /* software pacer source */ + outw(0, devpriv->control_status + ADCMUX_CONT); - if (comedi_pci_enable(devpriv->pci_dev, dev->driver->driver_name)) { - dev_err(dev->class_dev, - "Failed to enable PCI device and request regions\n"); - return -EIO; - } + return 0; +} - devpriv->s5933_config = pci_resource_start(devpriv->pci_dev, 0); - devpriv->control_status = pci_resource_start(devpriv->pci_dev, 1); - devpriv->adc_fifo = pci_resource_start(devpriv->pci_dev, 2); - devpriv->pacer_counter_dio = pci_resource_start(devpriv->pci_dev, 3); - if (thisboard->ao_nchan) - devpriv->ao_registers = pci_resource_start(devpriv->pci_dev, 4); +/* cancel analog output command */ +static int cb_pcidas_ao_cancel(struct comedi_device *dev, + struct comedi_subdevice *s) +{ + unsigned long flags; - /* disable and clear interrupts on amcc s5933 */ - outl(INTCSR_INBOX_INTR_STATUS, - devpriv->s5933_config + AMCC_OP_REG_INTCSR); + spin_lock_irqsave(&dev->spinlock, flags); + /* disable interrupts */ + devpriv->adc_fifo_bits &= ~DAHFIE & ~DAEMIE; + outw(devpriv->adc_fifo_bits, devpriv->control_status + INT_ADCFIFO); - if (request_irq(devpriv->pci_dev->irq, cb_pcidas_interrupt, - IRQF_SHARED, dev->driver->driver_name, dev)) { - dev_dbg(dev->class_dev, "unable to allocate irq %d\n", - devpriv->pci_dev->irq); - return -EINVAL; - } - dev->irq = devpriv->pci_dev->irq; + /* disable output */ + devpriv->ao_control_bits &= ~DACEN & ~DAC_PACER_MASK; + outw(devpriv->ao_control_bits, devpriv->control_status + DAC_CSR); + spin_unlock_irqrestore(&dev->spinlock, flags); - dev->board_name = thisboard->name; + return 0; +} - ret = comedi_alloc_subdevices(dev, 7); - if (ret) - return ret; +static void cb_pcidas_load_counters(struct comedi_device *dev, unsigned int *ns, + int rounding_flags) +{ + i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1), + &(devpriv->divisor2), ns, + rounding_flags & TRIG_ROUND_MASK); - s = dev->subdevices + 0; - /* analog input subdevice */ - dev->read_subdev = s; - s->type = COMEDI_SUBD_AI; - s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ; - /* WARNING: Number of inputs in differential mode is ignored */ - s->n_chan = thisboard->ai_nchan; - s->len_chanlist = thisboard->ai_nchan; - s->maxdata = (1 << thisboard->ai_bits) - 1; - s->range_table = thisboard->ranges; - s->insn_read = cb_pcidas_ai_rinsn; - s->insn_config = ai_config_insn; - s->do_cmd = cb_pcidas_ai_cmd; - s->do_cmdtest = cb_pcidas_ai_cmdtest; - s->cancel = cb_pcidas_cancel; + /* Write the values of ctr1 and ctr2 into counters 1 and 2 */ + i8254_load(devpriv->pacer_counter_dio + ADC8254, 0, 1, + devpriv->divisor1, 2); + i8254_load(devpriv->pacer_counter_dio + ADC8254, 0, 2, + devpriv->divisor2, 2); +} - /* analog output subdevice */ - s = dev->subdevices + 1; - if (thisboard->ao_nchan) { - s->type = COMEDI_SUBD_AO; - s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_GROUND; - s->n_chan = thisboard->ao_nchan; - /* - * analog out resolution is the same as - * analog input resolution, so use ai_bits - */ - s->maxdata = (1 << thisboard->ai_bits) - 1; - s->range_table = &cb_pcidas_ao_ranges; - s->insn_read = cb_pcidas_ao_readback_insn; - if (thisboard->has_ao_fifo) { - dev->write_subdev = s; - s->subdev_flags |= SDF_CMD_WRITE; - s->insn_write = cb_pcidas_ao_fifo_winsn; - s->do_cmdtest = cb_pcidas_ao_cmdtest; - s->do_cmd = cb_pcidas_ao_cmd; - s->cancel = cb_pcidas_ao_cancel; - } else { - s->insn_write = cb_pcidas_ao_nofifo_winsn; - } - } else { - s->type = COMEDI_SUBD_UNUSED; +static void write_calibration_bitstream(struct comedi_device *dev, + unsigned int register_bits, + unsigned int bitstream, + unsigned int bitstream_length) +{ + static const int write_delay = 1; + unsigned int bit; + + for (bit = 1 << (bitstream_length - 1); bit; bit >>= 1) { + if (bitstream & bit) + register_bits |= SERIAL_DATA_IN_BIT; + else + register_bits &= ~SERIAL_DATA_IN_BIT; + udelay(write_delay); + outw(register_bits, devpriv->control_status + CALIBRATION_REG); } +} - /* 8255 */ - s = dev->subdevices + 2; - ret = subdev_8255_init(dev, s, NULL, - devpriv->pacer_counter_dio + DIO_8255); - if (ret) - return ret; +static int caldac_8800_write(struct comedi_device *dev, unsigned int address, + uint8_t value) +{ + static const int num_caldac_channels = 8; + static const int bitstream_length = 11; + unsigned int bitstream = ((address & 0x7) << 8) | value; + static const int caldac_8800_udelay = 1; - /* serial EEPROM, */ - s = dev->subdevices + 3; - s->type = COMEDI_SUBD_MEMORY; - s->subdev_flags = SDF_READABLE | SDF_INTERNAL; - s->n_chan = 256; - s->maxdata = 0xff; - s->insn_read = eeprom_read_insn; + if (address >= num_caldac_channels) { + comedi_error(dev, "illegal caldac channel"); + return -1; + } - /* 8800 caldac */ - s = dev->subdevices + 4; - s->type = COMEDI_SUBD_CALIB; - s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL; - s->n_chan = NUM_CHANNELS_8800; - s->maxdata = 0xff; - s->insn_read = caldac_read_insn; - s->insn_write = caldac_write_insn; - for (i = 0; i < s->n_chan; i++) - caldac_8800_write(dev, i, s->maxdata / 2); + if (value == devpriv->caldac_value[address]) + return 1; - /* trim potentiometer */ - s = dev->subdevices + 5; - s->type = COMEDI_SUBD_CALIB; - s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL; - if (thisboard->trimpot == AD7376) { - s->n_chan = NUM_CHANNELS_7376; - s->maxdata = 0x7f; - } else { - s->n_chan = NUM_CHANNELS_8402; - s->maxdata = 0xff; - } - s->insn_read = trimpot_read_insn; - s->insn_write = trimpot_write_insn; - for (i = 0; i < s->n_chan; i++) - cb_pcidas_trimpot_write(dev, i, s->maxdata / 2); + devpriv->caldac_value[address] = value; - /* dac08 caldac */ - s = dev->subdevices + 6; - if (thisboard->has_dac08) { - s->type = COMEDI_SUBD_CALIB; - s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL; - s->n_chan = NUM_CHANNELS_DAC08; - s->insn_read = dac08_read_insn; - s->insn_write = dac08_write_insn; - s->maxdata = 0xff; - dac08_write(dev, s->maxdata / 2); - } else - s->type = COMEDI_SUBD_UNUSED; + write_calibration_bitstream(dev, cal_enable_bits(dev), bitstream, + bitstream_length); - /* make sure mailbox 4 is empty */ - inl(devpriv->s5933_config + AMCC_OP_REG_IMB4); - /* Set bits to enable incoming mailbox interrupts on amcc s5933. */ - devpriv->s5933_intcsr_bits = - INTCSR_INBOX_BYTE(3) | INTCSR_INBOX_SELECT(3) | - INTCSR_INBOX_FULL_INT; - /* clear and enable interrupt on amcc s5933 */ - outl(devpriv->s5933_intcsr_bits | INTCSR_INBOX_INTR_STATUS, - devpriv->s5933_config + AMCC_OP_REG_INTCSR); + udelay(caldac_8800_udelay); + outw(cal_enable_bits(dev) | SELECT_8800_BIT, + devpriv->control_status + CALIBRATION_REG); + udelay(caldac_8800_udelay); + outw(cal_enable_bits(dev), devpriv->control_status + CALIBRATION_REG); return 1; } -static void cb_pcidas_detach(struct comedi_device *dev) +static int trimpot_7376_write(struct comedi_device *dev, uint8_t value) { - struct cb_pcidas_private *devpriv = dev->private; + static const int bitstream_length = 7; + unsigned int bitstream = value & 0x7f; + unsigned int register_bits; + static const int ad7376_udelay = 1; - if (devpriv) { - if (devpriv->s5933_config) { - outl(INTCSR_INBOX_INTR_STATUS, - devpriv->s5933_config + AMCC_OP_REG_INTCSR); - } - } - if (dev->irq) - free_irq(dev->irq, dev); - if (dev->subdevices) - subdev_8255_cleanup(dev, dev->subdevices + 2); - if (devpriv && devpriv->pci_dev) { - if (devpriv->s5933_config) - comedi_pci_disable(devpriv->pci_dev); - pci_dev_put(devpriv->pci_dev); + register_bits = cal_enable_bits(dev) | SELECT_TRIMPOT_BIT; + udelay(ad7376_udelay); + outw(register_bits, devpriv->control_status + CALIBRATION_REG); + + write_calibration_bitstream(dev, register_bits, bitstream, + bitstream_length); + + udelay(ad7376_udelay); + outw(cal_enable_bits(dev), devpriv->control_status + CALIBRATION_REG); + + return 0; +} + +/* For 1602/16 only + * ch 0 : adc gain + * ch 1 : adc postgain offset */ +static int trimpot_8402_write(struct comedi_device *dev, unsigned int channel, + uint8_t value) +{ + static const int bitstream_length = 10; + unsigned int bitstream = ((channel & 0x3) << 8) | (value & 0xff); + unsigned int register_bits; + static const int ad8402_udelay = 1; + + register_bits = cal_enable_bits(dev) | SELECT_TRIMPOT_BIT; + udelay(ad8402_udelay); + outw(register_bits, devpriv->control_status + CALIBRATION_REG); + + write_calibration_bitstream(dev, register_bits, bitstream, + bitstream_length); + + udelay(ad8402_udelay); + outw(cal_enable_bits(dev), devpriv->control_status + CALIBRATION_REG); + + return 0; +} + +static int wait_for_nvram_ready(unsigned long s5933_base_addr) +{ + static const int timeout = 1000; + unsigned int i; + + for (i = 0; i < timeout; i++) { + if ((inb(s5933_base_addr + + AMCC_OP_REG_MCSR_NVCMD) & MCSR_NV_BUSY) + == 0) + return 0; + udelay(1); } + return -1; +} + +static int nvram_read(struct comedi_device *dev, unsigned int address, + uint8_t *data) +{ + unsigned long iobase = devpriv->s5933_config; + + if (wait_for_nvram_ready(iobase) < 0) + return -ETIMEDOUT; + + outb(MCSR_NV_ENABLE | MCSR_NV_LOAD_LOW_ADDR, + iobase + AMCC_OP_REG_MCSR_NVCMD); + outb(address & 0xff, iobase + AMCC_OP_REG_MCSR_NVDATA); + outb(MCSR_NV_ENABLE | MCSR_NV_LOAD_HIGH_ADDR, + iobase + AMCC_OP_REG_MCSR_NVCMD); + outb((address >> 8) & 0xff, iobase + AMCC_OP_REG_MCSR_NVDATA); + outb(MCSR_NV_ENABLE | MCSR_NV_READ, iobase + AMCC_OP_REG_MCSR_NVCMD); + + if (wait_for_nvram_ready(iobase) < 0) + return -ETIMEDOUT; + + *data = inb(iobase + AMCC_OP_REG_MCSR_NVDATA); + + return 0; } static struct comedi_driver cb_pcidas_driver = { diff --git a/trunk/drivers/staging/comedi/drivers/das08.c b/trunk/drivers/staging/comedi/drivers/das08.c index 874e02e47668..d0128e0e15cc 100644 --- a/trunk/drivers/staging/comedi/drivers/das08.c +++ b/trunk/drivers/staging/comedi/drivers/das08.c @@ -56,7 +56,6 @@ #include #include "8255.h" -#include "8253.h" #include "das08.h" #define DRV_NAME "das08" @@ -433,14 +432,90 @@ das08ao_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s, return n; } -static void i8254_initialize(struct comedi_device *dev) +static unsigned int i8254_read_channel_low(unsigned int base, int chan) { - struct das08_private_struct *devpriv = dev->private; - unsigned int mode = I8254_MODE0 | I8254_BINARY; - int i; + unsigned int msb, lsb; + + /* The following instructions must be in order. + We must avoid other process reading the counter's value in the + middle. + The spin_lock isn't needed since ioctl calls grab the big kernel + lock automatically */ + /*spin_lock(sp); */ + outb(chan << 6, base + I8254_CTRL); + base += chan; + lsb = inb(base); + msb = inb(base); + /*spin_unlock(sp); */ + + return lsb | (msb << 8); +} + +static void i8254_write_channel_low(unsigned int base, int chan, + unsigned int value) +{ + unsigned int msb, lsb; + + lsb = value & 0xFF; + msb = value >> 8; + + /* write lsb, then msb */ + base += chan; + /* See comments in i8254_read_channel_low */ + /*spin_lock(sp); */ + outb(lsb, base); + outb(msb, base); + /*spin_unlock(sp); */ +} + +static unsigned int i8254_read_channel(struct i8254_struct *st, int channel) +{ + int chan = st->logic2phys[channel]; + + return i8254_read_channel_low(st->iobase, chan); +} + +static void i8254_write_channel(struct i8254_struct *st, int channel, + unsigned int value) +{ + int chan = st->logic2phys[channel]; + + i8254_write_channel_low(st->iobase, chan, value); +} + +static void i8254_set_mode_low(unsigned int base, int channel, + unsigned int mode) +{ + outb((channel << 6) | 0x30 | (mode & 0x0F), base + I8254_CTRL); +} +static void i8254_set_mode(struct i8254_struct *st, int channel, + unsigned int mode) +{ + int chan = st->logic2phys[channel]; + + st->mode[chan] = mode; + return i8254_set_mode_low(st->iobase, chan, mode); +} + +static unsigned int i8254_read_status_low(unsigned int base, int channel) +{ + outb(0xE0 | (2 << channel), base + I8254_CTRL); + return inb(base + channel); +} + +static unsigned int i8254_read_status(struct i8254_struct *st, int channel) +{ + int chan = st->logic2phys[channel]; + + return i8254_read_status_low(st->iobase, chan); +} + +static void i8254_initialize(struct i8254_struct *st) +{ + int i; for (i = 0; i < 3; ++i) - i8254_set_mode(devpriv->i8254_iobase, 0, i, mode); + i8254_set_mode_low(st->iobase, i, st->mode[i]); } static int das08_counter_read(struct comedi_device *dev, @@ -449,8 +524,7 @@ static int das08_counter_read(struct comedi_device *dev, { struct das08_private_struct *devpriv = dev->private; int chan = insn->chanspec; - - data[0] = i8254_read(devpriv->i8254_iobase, 0, chan); + data[0] = i8254_read_channel(&devpriv->i8254, chan); return 1; } @@ -460,8 +534,7 @@ static int das08_counter_write(struct comedi_device *dev, { struct das08_private_struct *devpriv = dev->private; int chan = insn->chanspec; - - i8254_write(devpriv->i8254_iobase, 0, chan, data[0]); + i8254_write_channel(&devpriv->i8254, chan, data[0]); return 1; } @@ -477,10 +550,10 @@ static int das08_counter_config(struct comedi_device *dev, switch (data[0]) { case INSN_CONFIG_SET_COUNTER_MODE: - i8254_set_mode(devpriv->i8254_iobase, 0, chan, data[1]); + i8254_set_mode(&devpriv->i8254, chan, data[1]); break; case INSN_CONFIG_8254_READ_STATUS: - data[1] = i8254_status(devpriv->i8254_iobase, 0, chan); + data[1] = i8254_read_status(&devpriv->i8254, chan); break; default: return -EINVAL; @@ -844,9 +917,16 @@ int das08_common_attach(struct comedi_device *dev, unsigned long iobase) s->insn_read = das08_counter_read; s->insn_write = das08_counter_write; s->insn_config = das08_counter_config; - - devpriv->i8254_iobase = iobase + thisboard->i8254_offset; - i8254_initialize(dev); + /* Set-up the 8254 structure */ + devpriv->i8254.channels = 3; + devpriv->i8254.logic2phys[0] = 0; + devpriv->i8254.logic2phys[1] = 1; + devpriv->i8254.logic2phys[2] = 2; + devpriv->i8254.iobase = iobase + thisboard->i8254_offset; + devpriv->i8254.mode[0] = + devpriv->i8254.mode[1] = + devpriv->i8254.mode[2] = I8254_MODE0 | I8254_BINARY; + i8254_initialize(&devpriv->i8254); } else { s->type = COMEDI_SUBD_UNUSED; } diff --git a/trunk/drivers/staging/comedi/drivers/das08.h b/trunk/drivers/staging/comedi/drivers/das08.h index 27b6d4ec9032..0b92f24b261f 100644 --- a/trunk/drivers/staging/comedi/drivers/das08.h +++ b/trunk/drivers/staging/comedi/drivers/das08.h @@ -49,13 +49,25 @@ struct das08_board_struct { unsigned int iosize; /* number of ioports used */ }; +struct i8254_struct { + int channels; /* available channels. Some could be used internally. */ + int logic2phys[3]; /* to know which physical channel is. */ + int mode[3]; /* the index is the real counter. */ + unsigned int iobase; +}; + +#define I8254_CNT0 0 +#define I8254_CNT1 1 +#define I8254_CNT2 2 +#define I8254_CTRL 3 + struct das08_private_struct { unsigned int do_mux_bits; /* bits for do/mux register on boards without separate do register */ unsigned int do_bits; /* bits for do register on boards with register dedicated to digital out only */ const unsigned int *pg_gainlist; struct pci_dev *pdev; /* struct for pci-das08 */ unsigned int pci_iobase; /* additional base address for pci-das08 */ - unsigned int i8254_iobase; + struct i8254_struct i8254; }; #define NUM_DAS08_CS_BOARDS 2 diff --git a/trunk/drivers/staging/comedi/drivers/das08_cs.c b/trunk/drivers/staging/comedi/drivers/das08_cs.c index f5700de7b6c0..8bf6ec2dddba 100644 --- a/trunk/drivers/staging/comedi/drivers/das08_cs.c +++ b/trunk/drivers/staging/comedi/drivers/das08_cs.c @@ -20,13 +20,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - PCMCIA support code for this driver is adapted from the dummy_cs.c - driver of the Linux PCMCIA Card Services package. - - The initial developer of the original code is David A. Hinds - . Portions created by David A. Hinds - are Copyright (C) 1999 David A. Hinds. All Rights Reserved. - ***************************************************************** */ @@ -60,10 +53,24 @@ Command support does not exist, but could be added for this board. static struct pcmcia_device *cur_dev; +#define thisboard ((const struct das08_board_struct *)dev->board_ptr) + +static int das08_cs_attach(struct comedi_device *dev, + struct comedi_devconfig *it); + +static struct comedi_driver driver_das08_cs = { + .driver_name = "das08_cs", + .module = THIS_MODULE, + .attach = das08_cs_attach, + .detach = das08_common_detach, + .board_name = &das08_cs_boards[0].name, + .num_names = ARRAY_SIZE(das08_cs_boards), + .offset = sizeof(struct das08_board_struct), +}; + static int das08_cs_attach(struct comedi_device *dev, struct comedi_devconfig *it) { - const struct das08_board_struct *thisboard = comedi_board(dev); int ret; unsigned long iobase; struct pcmcia_device *link = cur_dev; /* XXX hack */ @@ -90,16 +97,65 @@ static int das08_cs_attach(struct comedi_device *dev, return das08_common_attach(dev, iobase); } -static struct comedi_driver driver_das08_cs = { - .driver_name = "das08_cs", - .module = THIS_MODULE, - .attach = das08_cs_attach, - .detach = das08_common_detach, - .board_name = &das08_cs_boards[0].name, - .num_names = ARRAY_SIZE(das08_cs_boards), - .offset = sizeof(struct das08_board_struct), +/*====================================================================== + + The following pcmcia code for the pcm-das08 is adapted from the + dummy_cs.c driver of the Linux PCMCIA Card Services package. + + The initial developer of the original code is David A. Hinds + . Portions created by David A. Hinds + are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + +======================================================================*/ + +static void das08_pcmcia_config(struct pcmcia_device *link); +static void das08_pcmcia_release(struct pcmcia_device *link); +static int das08_pcmcia_suspend(struct pcmcia_device *p_dev); +static int das08_pcmcia_resume(struct pcmcia_device *p_dev); + +static int das08_pcmcia_attach(struct pcmcia_device *); +static void das08_pcmcia_detach(struct pcmcia_device *); + +struct local_info_t { + struct pcmcia_device *link; + int stop; + struct bus_operations *bus; }; +static int das08_pcmcia_attach(struct pcmcia_device *link) +{ + struct local_info_t *local; + + dev_dbg(&link->dev, "das08_pcmcia_attach()\n"); + + /* Allocate space for private device-specific data */ + local = kzalloc(sizeof(struct local_info_t), GFP_KERNEL); + if (!local) + return -ENOMEM; + local->link = link; + link->priv = local; + + cur_dev = link; + + das08_pcmcia_config(link); + + return 0; +} /* das08_pcmcia_attach */ + +static void das08_pcmcia_detach(struct pcmcia_device *link) +{ + + dev_dbg(&link->dev, "das08_pcmcia_detach\n"); + + ((struct local_info_t *)link->priv)->stop = 1; + das08_pcmcia_release(link); + + /* This points to the parent struct local_info_t struct */ + kfree(link->priv); + +} /* das08_pcmcia_detach */ + + static int das08_pcmcia_config_loop(struct pcmcia_device *p_dev, void *priv_data) { @@ -109,15 +165,19 @@ static int das08_pcmcia_config_loop(struct pcmcia_device *p_dev, return pcmcia_request_io(p_dev); } -static int das08_pcmcia_attach(struct pcmcia_device *link) +static void das08_pcmcia_config(struct pcmcia_device *link) { int ret; + dev_dbg(&link->dev, "das08_pcmcia_config\n"); + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; ret = pcmcia_loop_config(link, das08_pcmcia_config_loop, NULL); - if (ret) + if (ret) { + dev_warn(&link->dev, "no configuration found\n"); goto failed; + } if (!link->irq) goto failed; @@ -126,61 +186,87 @@ static int das08_pcmcia_attach(struct pcmcia_device *link) if (ret) goto failed; - cur_dev = link; - return 0; + return; failed: - pcmcia_disable_device(link); - return ret; -} + das08_pcmcia_release(link); -static void das08_pcmcia_detach(struct pcmcia_device *link) +} /* das08_pcmcia_config */ + +static void das08_pcmcia_release(struct pcmcia_device *link) { + dev_dbg(&link->dev, "das08_pcmcia_release\n"); pcmcia_disable_device(link); - cur_dev = NULL; -} +} /* das08_pcmcia_release */ + +static int das08_pcmcia_suspend(struct pcmcia_device *link) +{ + struct local_info_t *local = link->priv; + /* Mark the device as stopped, to block IO until later */ + local->stop = 1; + + return 0; +} /* das08_pcmcia_suspend */ + +static int das08_pcmcia_resume(struct pcmcia_device *link) +{ + struct local_info_t *local = link->priv; + + local->stop = 0; + return 0; +} /* das08_pcmcia_resume */ + +/*====================================================================*/ static const struct pcmcia_device_id das08_cs_id_table[] = { PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x4001), PCMCIA_DEVICE_NULL }; + MODULE_DEVICE_TABLE(pcmcia, das08_cs_id_table); +MODULE_AUTHOR("David A. Schleef , " + "Frank Mori Hess "); +MODULE_DESCRIPTION("Comedi driver for ComputerBoards DAS-08 PCMCIA boards"); +MODULE_LICENSE("GPL"); -static struct pcmcia_driver das08_cs_driver = { - .name = "pcm-das08", - .owner = THIS_MODULE, - .probe = das08_pcmcia_attach, - .remove = das08_pcmcia_detach, - .id_table = das08_cs_id_table, +struct pcmcia_driver das08_cs_driver = { + .probe = das08_pcmcia_attach, + .remove = das08_pcmcia_detach, + .suspend = das08_pcmcia_suspend, + .resume = das08_pcmcia_resume, + .id_table = das08_cs_id_table, + .owner = THIS_MODULE, + .name = "pcm-das08", }; +static int __init init_das08_pcmcia_cs(void) +{ + pcmcia_register_driver(&das08_cs_driver); + return 0; +} + +static void __exit exit_das08_pcmcia_cs(void) +{ + pr_debug("das08_pcmcia_cs: unloading\n"); + pcmcia_unregister_driver(&das08_cs_driver); +} + static int __init das08_cs_init_module(void) { int ret; - ret = comedi_driver_register(&driver_das08_cs); + ret = init_das08_pcmcia_cs(); if (ret < 0) return ret; - ret = pcmcia_register_driver(&das08_cs_driver); - if (ret < 0) { - comedi_driver_unregister(&driver_das08_cs); - return ret; - } - - return 0; - + return comedi_driver_register(&driver_das08_cs); } -module_init(das08_cs_init_module); static void __exit das08_cs_exit_module(void) { - pcmcia_unregister_driver(&das08_cs_driver); + exit_das08_pcmcia_cs(); comedi_driver_unregister(&driver_das08_cs); } -module_exit(das08_cs_exit_module); -MODULE_AUTHOR("David A. Schleef , " - "Frank Mori Hess "); -MODULE_DESCRIPTION("Comedi driver for ComputerBoards DAS-08 PCMCIA boards"); -MODULE_LICENSE("GPL"); +module_init(das08_cs_init_module); +module_exit(das08_cs_exit_module); diff --git a/trunk/drivers/staging/comedi/drivers/dmm32at.c b/trunk/drivers/staging/comedi/drivers/dmm32at.c index 7107f590b1fe..af3531676e81 100644 --- a/trunk/drivers/staging/comedi/drivers/dmm32at.c +++ b/trunk/drivers/staging/comedi/drivers/dmm32at.c @@ -78,6 +78,9 @@ Configuration Options: #define DMM32AT_DIOC 0x0e #define DMM32AT_DIOCONF 0x0f +#define dmm_inb(cdev, reg) inb((cdev->iobase)+reg) +#define dmm_outb(cdev, reg, valu) outb(valu, (cdev->iobase)+reg) + /* Board register values. */ /* DMM32AT_DACSTAT 0x04 */ @@ -156,10 +159,40 @@ static const struct comedi_lrange dmm32at_aoranges = { } }; +/* + * Board descriptions for two imaginary boards. Describing the + * boards in this way is optional, and completely driver-dependent. + * Some drivers use arrays such as this, other do not. + */ struct dmm32at_board { const char *name; + int ai_chans; + int ai_bits; + const struct comedi_lrange *ai_ranges; + int ao_chans; + int ao_bits; + const struct comedi_lrange *ao_ranges; + int have_dio; + int dio_chans; +}; +static const struct dmm32at_board dmm32at_boards[] = { + { + .name = "dmm32at", + .ai_chans = 32, + .ai_bits = 16, + .ai_ranges = &dmm32at_airanges, + .ao_chans = 4, + .ao_bits = 12, + .ao_ranges = &dmm32at_aoranges, + .have_dio = 1, + .dio_chans = 24, + }, }; +/* this structure is for data unique to this hardware driver. If + * several hardware drivers keep similar information in this structure, + * feel free to suggest moving the variable to the struct comedi_device struct. + */ struct dmm32at_private { int data; @@ -172,6 +205,246 @@ struct dmm32at_private { }; +/* + * most drivers define the following macro to make it easy to + * access the private structure. + */ +#define devpriv ((struct dmm32at_private *)dev->private) + +/* + * The struct comedi_driver structure tells the Comedi core module + * which functions to call to configure/deconfigure (attach/detach) + * the board, and also about the kernel module that contains + * the device code. + */ +static int dmm32at_attach(struct comedi_device *dev, + struct comedi_devconfig *it); +static void dmm32at_detach(struct comedi_device *dev); +static struct comedi_driver driver_dmm32at = { + .driver_name = "dmm32at", + .module = THIS_MODULE, + .attach = dmm32at_attach, + .detach = dmm32at_detach, +/* It is not necessary to implement the following members if you are + * writing a driver for a ISA PnP or PCI card */ +/* Most drivers will support multiple types of boards by + * having an array of board structures. These were defined + * in dmm32at_boards[] above. Note that the element 'name' + * was first in the structure -- Comedi uses this fact to + * extract the name of the board without knowing any details + * about the structure except for its length. + * When a device is attached (by comedi_config), the name + * of the device is given to Comedi, and Comedi tries to + * match it by going through the list of board names. If + * there is a match, the address of the pointer is put + * into dev->board_ptr and driver->attach() is called. + * + * Note that these are not necessary if you can determine + * the type of board in software. ISA PnP, PCI, and PCMCIA + * devices are such boards. + */ + .board_name = &dmm32at_boards[0].name, + .offset = sizeof(struct dmm32at_board), + .num_names = ARRAY_SIZE(dmm32at_boards), +}; + +/* prototypes for driver functions below */ +static int dmm32at_ai_rinsn(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data); +static int dmm32at_ao_winsn(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data); +static int dmm32at_ao_rinsn(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data); +static int dmm32at_dio_insn_bits(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data); +static int dmm32at_dio_insn_config(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data); +static int dmm32at_ai_cmdtest(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_cmd *cmd); +static int dmm32at_ai_cmd(struct comedi_device *dev, + struct comedi_subdevice *s); +static int dmm32at_ai_cancel(struct comedi_device *dev, + struct comedi_subdevice *s); +static int dmm32at_ns_to_timer(unsigned int *ns, int round); +static irqreturn_t dmm32at_isr(int irq, void *d); +void dmm32at_setaitimer(struct comedi_device *dev, unsigned int nansec); + +/* + * Attach is called by the Comedi core to configure the driver + * for a particular board. If you specified a board_name array + * in the driver structure, dev->board_ptr contains that + * address. + */ +static int dmm32at_attach(struct comedi_device *dev, + struct comedi_devconfig *it) +{ + const struct dmm32at_board *board = comedi_board(dev); + int ret; + struct comedi_subdevice *s; + unsigned char aihi, ailo, fifostat, aistat, intstat, airback; + unsigned long iobase; + unsigned int irq; + + iobase = it->options[0]; + irq = it->options[1]; + + printk(KERN_INFO "comedi%d: dmm32at: attaching\n", dev->minor); + printk(KERN_DEBUG "dmm32at: probing at address 0x%04lx, irq %u\n", + iobase, irq); + + /* register address space */ + if (!request_region(iobase, DMM32AT_MEMSIZE, board->name)) { + printk(KERN_ERR "comedi%d: dmm32at: I/O port conflict\n", + dev->minor); + return -EIO; + } + dev->iobase = iobase; + + /* the following just makes sure the board is there and gets + it to a known state */ + + /* reset the board */ + dmm_outb(dev, DMM32AT_CNTRL, DMM32AT_RESET); + + /* allow a millisecond to reset */ + udelay(1000); + + /* zero scan and fifo control */ + dmm_outb(dev, DMM32AT_FIFOCNTRL, 0x0); + + /* zero interrupt and clock control */ + dmm_outb(dev, DMM32AT_INTCLOCK, 0x0); + + /* write a test channel range, the high 3 bits should drop */ + dmm_outb(dev, DMM32AT_AILOW, 0x80); + dmm_outb(dev, DMM32AT_AIHIGH, 0xff); + + /* set the range at 10v unipolar */ + dmm_outb(dev, DMM32AT_AICONF, DMM32AT_RANGE_U10); + + /* should take 10 us to settle, here's a hundred */ + udelay(100); + + /* read back the values */ + ailo = dmm_inb(dev, DMM32AT_AILOW); + aihi = dmm_inb(dev, DMM32AT_AIHIGH); + fifostat = dmm_inb(dev, DMM32AT_FIFOSTAT); + aistat = dmm_inb(dev, DMM32AT_AISTAT); + intstat = dmm_inb(dev, DMM32AT_INTCLOCK); + airback = dmm_inb(dev, DMM32AT_AIRBACK); + + printk(KERN_DEBUG "dmm32at: lo=0x%02x hi=0x%02x fifostat=0x%02x\n", + ailo, aihi, fifostat); + printk(KERN_DEBUG + "dmm32at: aistat=0x%02x intstat=0x%02x airback=0x%02x\n", + aistat, intstat, airback); + + if ((ailo != 0x00) || (aihi != 0x1f) || (fifostat != 0x80) || + (aistat != 0x60 || (intstat != 0x00) || airback != 0x0c)) { + printk(KERN_ERR "dmmat32: board detection failed\n"); + return -EIO; + } + + /* board is there, register interrupt */ + if (irq) { + ret = request_irq(irq, dmm32at_isr, 0, board->name, dev); + if (ret < 0) { + printk(KERN_ERR "dmm32at: irq conflict\n"); + return ret; + } + dev->irq = irq; + } + + dev->board_name = board->name; + +/* + * Allocate the private structure area. alloc_private() is a + * convenient macro defined in comedidev.h. + */ + if (alloc_private(dev, sizeof(struct dmm32at_private)) < 0) + return -ENOMEM; + + ret = comedi_alloc_subdevices(dev, 3); + if (ret) + return ret; + + s = dev->subdevices + 0; + dev->read_subdev = s; + /* analog input subdevice */ + s->type = COMEDI_SUBD_AI; + /* we support single-ended (ground) and differential */ + s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ; + s->n_chan = board->ai_chans; + s->maxdata = (1 << board->ai_bits) - 1; + s->range_table = board->ai_ranges; + s->len_chanlist = 32; /* This is the maximum chanlist length that + the board can handle */ + s->insn_read = dmm32at_ai_rinsn; + s->do_cmd = dmm32at_ai_cmd; + s->do_cmdtest = dmm32at_ai_cmdtest; + s->cancel = dmm32at_ai_cancel; + + s = dev->subdevices + 1; + /* analog output subdevice */ + s->type = COMEDI_SUBD_AO; + s->subdev_flags = SDF_WRITABLE; + s->n_chan = board->ao_chans; + s->maxdata = (1 << board->ao_bits) - 1; + s->range_table = board->ao_ranges; + s->insn_write = dmm32at_ao_winsn; + s->insn_read = dmm32at_ao_rinsn; + + s = dev->subdevices + 2; + /* digital i/o subdevice */ + if (board->have_dio) { + + /* get access to the DIO regs */ + dmm_outb(dev, DMM32AT_CNTRL, DMM32AT_DIOACC); + /* set the DIO's to the defualt input setting */ + devpriv->dio_config = DMM32AT_DIRA | DMM32AT_DIRB | + DMM32AT_DIRCL | DMM32AT_DIRCH | DMM32AT_DIENABLE; + dmm_outb(dev, DMM32AT_DIOCONF, devpriv->dio_config); + + /* set up the subdevice */ + s->type = COMEDI_SUBD_DIO; + s->subdev_flags = SDF_READABLE | SDF_WRITABLE; + s->n_chan = board->dio_chans; + s->maxdata = 1; + s->state = 0; + s->range_table = &range_digital; + s->insn_bits = dmm32at_dio_insn_bits; + s->insn_config = dmm32at_dio_insn_config; + } else { + s->type = COMEDI_SUBD_UNUSED; + } + + /* success */ + printk(KERN_INFO "comedi%d: dmm32at: attached\n", dev->minor); + + return 1; + +} + +static void dmm32at_detach(struct comedi_device *dev) +{ + if (dev->irq) + free_irq(dev->irq, dev); + if (dev->iobase) + release_region(dev->iobase, DMM32AT_MEMSIZE); +} + +/* + * "instructions" read/write data in "one-shot" or "software-triggered" + * mode. + */ + static int dmm32at_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) @@ -191,17 +464,17 @@ static int dmm32at_ai_rinsn(struct comedi_device *dev, /* printk("channel=0x%02x, range=%d\n",chan,range); */ /* zero scan and fifo control and reset fifo */ - outb(DMM32AT_FIFORESET, dev->iobase + DMM32AT_FIFOCNTRL); + dmm_outb(dev, DMM32AT_FIFOCNTRL, DMM32AT_FIFORESET); /* write the ai channel range regs */ - outb(chan, dev->iobase + DMM32AT_AILOW); - outb(chan, dev->iobase + DMM32AT_AIHIGH); + dmm_outb(dev, DMM32AT_AILOW, chan); + dmm_outb(dev, DMM32AT_AIHIGH, chan); /* set the range bits */ - outb(dmm32at_rangebits[range], dev->iobase + DMM32AT_AICONF); + dmm_outb(dev, DMM32AT_AICONF, dmm32at_rangebits[range]); /* wait for circuit to settle */ for (i = 0; i < 40000; i++) { - status = inb(dev->iobase + DMM32AT_AIRBACK); + status = dmm_inb(dev, DMM32AT_AIRBACK); if ((status & DMM32AT_STATUS) == 0) break; } @@ -213,10 +486,10 @@ static int dmm32at_ai_rinsn(struct comedi_device *dev, /* convert n samples */ for (n = 0; n < insn->n; n++) { /* trigger conversion */ - outb(0xff, dev->iobase + DMM32AT_CONV); + dmm_outb(dev, DMM32AT_CONV, 0xff); /* wait for conversion to end */ for (i = 0; i < 40000; i++) { - status = inb(dev->iobase + DMM32AT_AISTAT); + status = dmm_inb(dev, DMM32AT_AISTAT); if ((status & DMM32AT_STATUS) == 0) break; } @@ -226,8 +499,8 @@ static int dmm32at_ai_rinsn(struct comedi_device *dev, } /* read data */ - lsb = inb(dev->iobase + DMM32AT_AILSB); - msb = inb(dev->iobase + DMM32AT_AIMSB); + lsb = dmm_inb(dev, DMM32AT_AILSB); + msb = dmm_inb(dev, DMM32AT_AIMSB); /* invert sign bit to make range unsigned, this is an idiosyncrasy of the diamond board, it return @@ -244,12 +517,6 @@ static int dmm32at_ai_rinsn(struct comedi_device *dev, return n; } -static int dmm32at_ns_to_timer(unsigned int *ns, int round) -{ - /* trivial timer */ - return *ns; -} - static int dmm32at_ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_cmd *cmd) @@ -258,6 +525,15 @@ static int dmm32at_ai_cmdtest(struct comedi_device *dev, int tmp; int start_chan, gain, i; + /* printk("dmmat32 in command test\n"); */ + + /* cmdtest tests a particular command to see if it is valid. + * Using the cmdtest ioctl, a user can create a valid cmd + * and then have it executes by the cmd ioctl. + * + * cmdtest returns 1,2,3,4 or 0, depending on which tests + * the command passes. */ + /* step 1: make sure trigger sources are trivially valid */ tmp = cmd->start_src; @@ -427,39 +703,8 @@ static int dmm32at_ai_cmdtest(struct comedi_device *dev, return 0; } -static void dmm32at_setaitimer(struct comedi_device *dev, unsigned int nansec) -{ - unsigned char lo1, lo2, hi2; - unsigned short both2; - - /* based on 10mhz clock */ - lo1 = 200; - both2 = nansec / 20000; - hi2 = (both2 & 0xff00) >> 8; - lo2 = both2 & 0x00ff; - - /* set the counter frequency to 10mhz */ - outb(0, dev->iobase + DMM32AT_CNTRDIO); - - /* get access to the clock regs */ - outb(DMM32AT_CLKACC, dev->iobase + DMM32AT_CNTRL); - - /* write the counter 1 control word and low byte to counter */ - outb(DMM32AT_CLKCT1, dev->iobase + DMM32AT_CLKCT); - outb(lo1, dev->iobase + DMM32AT_CLK1); - - /* write the counter 2 control word and low byte then to counter */ - outb(DMM32AT_CLKCT2, dev->iobase + DMM32AT_CLKCT); - outb(lo2, dev->iobase + DMM32AT_CLK2); - outb(hi2, dev->iobase + DMM32AT_CLK2); - - /* enable the ai conversion interrupt and the clock to start scans */ - outb(DMM32AT_ADINT | DMM32AT_CLKSEL, dev->iobase + DMM32AT_INTCLOCK); -} - static int dmm32at_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) { - struct dmm32at_private *devpriv = dev->private; struct comedi_cmd *cmd = &s->async->cmd; int i, range; unsigned char chanlo, chanhi, status; @@ -475,20 +720,20 @@ static int dmm32at_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) range = CR_RANGE(cmd->chanlist[0]); /* reset fifo */ - outb(DMM32AT_FIFORESET, dev->iobase + DMM32AT_FIFOCNTRL); + dmm_outb(dev, DMM32AT_FIFOCNTRL, DMM32AT_FIFORESET); /* set scan enable */ - outb(DMM32AT_SCANENABLE, dev->iobase + DMM32AT_FIFOCNTRL); + dmm_outb(dev, DMM32AT_FIFOCNTRL, DMM32AT_SCANENABLE); /* write the ai channel range regs */ - outb(chanlo, dev->iobase + DMM32AT_AILOW); - outb(chanhi, dev->iobase + DMM32AT_AIHIGH); + dmm_outb(dev, DMM32AT_AILOW, chanlo); + dmm_outb(dev, DMM32AT_AIHIGH, chanhi); /* set the range bits */ - outb(dmm32at_rangebits[range], dev->iobase + DMM32AT_AICONF); + dmm_outb(dev, DMM32AT_AICONF, dmm32at_rangebits[range]); /* reset the interrupt just in case */ - outb(DMM32AT_INTRESET, dev->iobase + DMM32AT_CNTRL); + dmm_outb(dev, DMM32AT_CNTRL, DMM32AT_INTRESET); if (cmd->stop_src == TRIG_COUNT) devpriv->ai_scans_left = cmd->stop_arg; @@ -499,7 +744,7 @@ static int dmm32at_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) /* wait for circuit to settle */ for (i = 0; i < 40000; i++) { - status = inb(dev->iobase + DMM32AT_AIRBACK); + status = dmm_inb(dev, DMM32AT_AIRBACK); if ((status & DMM32AT_STATUS) == 0) break; } @@ -513,8 +758,8 @@ static int dmm32at_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) dmm32at_setaitimer(dev, cmd->scan_begin_arg); } else { /* start the interrups and initiate a single scan */ - outb(DMM32AT_ADINT, dev->iobase + DMM32AT_INTCLOCK); - outb(0xff, dev->iobase + DMM32AT_CONV); + dmm_outb(dev, DMM32AT_INTCLOCK, DMM32AT_ADINT); + dmm_outb(dev, DMM32AT_CONV, 0xff); } /* printk("dmmat32 in command\n"); */ @@ -532,27 +777,24 @@ static int dmm32at_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) static int dmm32at_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s) { - struct dmm32at_private *devpriv = dev->private; - devpriv->ai_scans_left = 1; return 0; } static irqreturn_t dmm32at_isr(int irq, void *d) { - struct comedi_device *dev = d; - struct dmm32at_private *devpriv = dev->private; unsigned char intstat; unsigned int samp; unsigned short msb, lsb; int i; + struct comedi_device *dev = d; if (!dev->attached) { comedi_error(dev, "spurious interrupt"); return IRQ_HANDLED; } - intstat = inb(dev->iobase + DMM32AT_INTCLOCK); + intstat = dmm_inb(dev, DMM32AT_INTCLOCK); if (intstat & DMM32AT_ADINT) { struct comedi_subdevice *s = dev->read_subdev; @@ -560,8 +802,8 @@ static irqreturn_t dmm32at_isr(int irq, void *d) for (i = 0; i < cmd->chanlist_len; i++) { /* read data */ - lsb = inb(dev->iobase + DMM32AT_AILSB); - msb = inb(dev->iobase + DMM32AT_AIMSB); + lsb = dmm_inb(dev, DMM32AT_AILSB); + msb = dmm_inb(dev, DMM32AT_AIMSB); /* invert sign bit to make range unsigned */ samp = ((msb ^ 0x0080) << 8) + lsb; @@ -572,7 +814,7 @@ static irqreturn_t dmm32at_isr(int irq, void *d) devpriv->ai_scans_left--; if (devpriv->ai_scans_left == 0) { /* disable further interrupts and clocks */ - outb(0x0, dev->iobase + DMM32AT_INTCLOCK); + dmm_outb(dev, DMM32AT_INTCLOCK, 0x0); /* set the buffer to be flushed with an EOF */ s->async->events |= COMEDI_CB_EOA; } @@ -583,15 +825,31 @@ static irqreturn_t dmm32at_isr(int irq, void *d) } /* reset the interrupt */ - outb(DMM32AT_INTRESET, dev->iobase + DMM32AT_CNTRL); + dmm_outb(dev, DMM32AT_CNTRL, DMM32AT_INTRESET); return IRQ_HANDLED; } +/* This function doesn't require a particular form, this is just + * what happens to be used in some of the drivers. It should + * convert ns nanoseconds to a counter value suitable for programming + * the device. Also, it should adjust ns so that it cooresponds to + * the actual time that the device will use. */ +static int dmm32at_ns_to_timer(unsigned int *ns, int round) +{ + /* trivial timer */ + /* if your timing is done through two cascaded timers, the + * i8253_cascade_ns_to_timer() function in 8253.h can be + * very helpful. There are also i8254_load() and i8254_mm_load() + * which can be used to load values into the ubiquitous 8254 counters + */ + + return *ns; +} + static int dmm32at_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { - struct dmm32at_private *devpriv = dev->private; int i; int chan = CR_CHAN(insn->chanspec); unsigned char hi, lo, status; @@ -608,12 +866,12 @@ static int dmm32at_ao_winsn(struct comedi_device *dev, hi = (data[i] >> 8) + chan * (1 << 6); /* printk("writing 0x%02x 0x%02x\n",hi,lo); */ /* write the low and high values to the board */ - outb(lo, dev->iobase + DMM32AT_DACLSB); - outb(hi, dev->iobase + DMM32AT_DACMSB); + dmm_outb(dev, DMM32AT_DACLSB, lo); + dmm_outb(dev, DMM32AT_DACMSB, hi); /* wait for circuit to settle */ for (i = 0; i < 40000; i++) { - status = inb(dev->iobase + DMM32AT_DACSTAT); + status = dmm_inb(dev, DMM32AT_DACSTAT); if ((status & DMM32AT_DACBUSY) == 0) break; } @@ -622,7 +880,7 @@ static int dmm32at_ao_winsn(struct comedi_device *dev, return -ETIMEDOUT; } /* dummy read to update trigger the output */ - status = inb(dev->iobase + DMM32AT_DACMSB); + status = dmm_inb(dev, DMM32AT_DACMSB); } @@ -630,11 +888,12 @@ static int dmm32at_ao_winsn(struct comedi_device *dev, return i; } +/* AO subdevices should have a read insn as well as a write insn. + * Usually this means copying a value stored in devpriv. */ static int dmm32at_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { - struct dmm32at_private *devpriv = dev->private; int i; int chan = CR_CHAN(insn->chanspec); @@ -644,11 +903,15 @@ static int dmm32at_ao_rinsn(struct comedi_device *dev, return i; } +/* DIO devices are slightly special. Although it is possible to + * implement the insn_read/insn_write interface, it is much more + * useful to applications if you implement the insn_bits interface. + * This allows packed reading/writing of the DIO channels. The + * comedi core can convert between insn_bits and insn_read/write */ static int dmm32at_dio_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { - struct dmm32at_private *devpriv = dev->private; unsigned char diobits; /* The insn data is a mask in data[0] and the new data @@ -661,29 +924,29 @@ static int dmm32at_dio_insn_bits(struct comedi_device *dev, } /* get access to the DIO regs */ - outb(DMM32AT_DIOACC, dev->iobase + DMM32AT_CNTRL); + dmm_outb(dev, DMM32AT_CNTRL, DMM32AT_DIOACC); /* if either part of dio is set for output */ if (((devpriv->dio_config & DMM32AT_DIRCL) == 0) || ((devpriv->dio_config & DMM32AT_DIRCH) == 0)) { diobits = (s->state & 0x00ff0000) >> 16; - outb(diobits, dev->iobase + DMM32AT_DIOC); + dmm_outb(dev, DMM32AT_DIOC, diobits); } if ((devpriv->dio_config & DMM32AT_DIRB) == 0) { diobits = (s->state & 0x0000ff00) >> 8; - outb(diobits, dev->iobase + DMM32AT_DIOB); + dmm_outb(dev, DMM32AT_DIOB, diobits); } if ((devpriv->dio_config & DMM32AT_DIRA) == 0) { diobits = (s->state & 0x000000ff); - outb(diobits, dev->iobase + DMM32AT_DIOA); + dmm_outb(dev, DMM32AT_DIOA, diobits); } /* now read the state back in */ - s->state = inb(dev->iobase + DMM32AT_DIOC); + s->state = dmm_inb(dev, DMM32AT_DIOC); s->state <<= 8; - s->state |= inb(dev->iobase + DMM32AT_DIOB); + s->state |= dmm_inb(dev, DMM32AT_DIOB); s->state <<= 8; - s->state |= inb(dev->iobase + DMM32AT_DIOA); + s->state |= dmm_inb(dev, DMM32AT_DIOA); data[1] = s->state; /* on return, data[1] contains the value of the digital @@ -700,7 +963,6 @@ static int dmm32at_dio_insn_config(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { - struct dmm32at_private *devpriv = dev->private; unsigned char chanbit; int chan = CR_CHAN(insn->chanspec); @@ -727,181 +989,60 @@ static int dmm32at_dio_insn_config(struct comedi_device *dev, else devpriv->dio_config |= chanbit; /* get access to the DIO regs */ - outb(DMM32AT_DIOACC, dev->iobase + DMM32AT_CNTRL); + dmm_outb(dev, DMM32AT_CNTRL, DMM32AT_DIOACC); /* set the DIO's to the new configuration setting */ - outb(devpriv->dio_config, dev->iobase + DMM32AT_DIOCONF); + dmm_outb(dev, DMM32AT_DIOCONF, devpriv->dio_config); return 1; } -static int dmm32at_attach(struct comedi_device *dev, - struct comedi_devconfig *it) +void dmm32at_setaitimer(struct comedi_device *dev, unsigned int nansec) { - const struct dmm32at_board *board = comedi_board(dev); - struct dmm32at_private *devpriv; - int ret; - struct comedi_subdevice *s; - unsigned char aihi, ailo, fifostat, aistat, intstat, airback; - unsigned long iobase; - unsigned int irq; - - iobase = it->options[0]; - irq = it->options[1]; - - printk(KERN_INFO "comedi%d: dmm32at: attaching\n", dev->minor); - printk(KERN_DEBUG "dmm32at: probing at address 0x%04lx, irq %u\n", - iobase, irq); - - /* register address space */ - if (!request_region(iobase, DMM32AT_MEMSIZE, board->name)) { - printk(KERN_ERR "comedi%d: dmm32at: I/O port conflict\n", - dev->minor); - return -EIO; - } - dev->iobase = iobase; - - /* the following just makes sure the board is there and gets - it to a known state */ - - /* reset the board */ - outb(DMM32AT_RESET, dev->iobase + DMM32AT_CNTRL); - - /* allow a millisecond to reset */ - udelay(1000); - - /* zero scan and fifo control */ - outb(0x0, dev->iobase + DMM32AT_FIFOCNTRL); - - /* zero interrupt and clock control */ - outb(0x0, dev->iobase + DMM32AT_INTCLOCK); - - /* write a test channel range, the high 3 bits should drop */ - outb(0x80, dev->iobase + DMM32AT_AILOW); - outb(0xff, dev->iobase + DMM32AT_AIHIGH); - - /* set the range at 10v unipolar */ - outb(DMM32AT_RANGE_U10, dev->iobase + DMM32AT_AICONF); - - /* should take 10 us to settle, here's a hundred */ - udelay(100); - - /* read back the values */ - ailo = inb(dev->iobase + DMM32AT_AILOW); - aihi = inb(dev->iobase + DMM32AT_AIHIGH); - fifostat = inb(dev->iobase + DMM32AT_FIFOSTAT); - aistat = inb(dev->iobase + DMM32AT_AISTAT); - intstat = inb(dev->iobase + DMM32AT_INTCLOCK); - airback = inb(dev->iobase + DMM32AT_AIRBACK); - - printk(KERN_DEBUG "dmm32at: lo=0x%02x hi=0x%02x fifostat=0x%02x\n", - ailo, aihi, fifostat); - printk(KERN_DEBUG - "dmm32at: aistat=0x%02x intstat=0x%02x airback=0x%02x\n", - aistat, intstat, airback); - - if ((ailo != 0x00) || (aihi != 0x1f) || (fifostat != 0x80) || - (aistat != 0x60 || (intstat != 0x00) || airback != 0x0c)) { - printk(KERN_ERR "dmmat32: board detection failed\n"); - return -EIO; - } - - /* board is there, register interrupt */ - if (irq) { - ret = request_irq(irq, dmm32at_isr, 0, board->name, dev); - if (ret < 0) { - printk(KERN_ERR "dmm32at: irq conflict\n"); - return ret; - } - dev->irq = irq; - } - - dev->board_name = board->name; - - if (alloc_private(dev, sizeof(*devpriv)) < 0) - return -ENOMEM; - devpriv = dev->private; - - ret = comedi_alloc_subdevices(dev, 3); - if (ret) - return ret; + unsigned char lo1, lo2, hi2; + unsigned short both2; - s = dev->subdevices + 0; - dev->read_subdev = s; - /* analog input subdevice */ - s->type = COMEDI_SUBD_AI; - /* we support single-ended (ground) and differential */ - s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ; - s->n_chan = 32; - s->maxdata = 0xffff; - s->range_table = &dmm32at_airanges; - s->len_chanlist = 32; /* This is the maximum chanlist length that - the board can handle */ - s->insn_read = dmm32at_ai_rinsn; - s->do_cmd = dmm32at_ai_cmd; - s->do_cmdtest = dmm32at_ai_cmdtest; - s->cancel = dmm32at_ai_cancel; + /* based on 10mhz clock */ + lo1 = 200; + both2 = nansec / 20000; + hi2 = (both2 & 0xff00) >> 8; + lo2 = both2 & 0x00ff; - s = dev->subdevices + 1; - /* analog output subdevice */ - s->type = COMEDI_SUBD_AO; - s->subdev_flags = SDF_WRITABLE; - s->n_chan = 4; - s->maxdata = 0x0fff; - s->range_table = &dmm32at_aoranges; - s->insn_write = dmm32at_ao_winsn; - s->insn_read = dmm32at_ao_rinsn; + /* set the counter frequency to 10mhz */ + dmm_outb(dev, DMM32AT_CNTRDIO, 0); - s = dev->subdevices + 2; - /* digital i/o subdevice */ + /* get access to the clock regs */ + dmm_outb(dev, DMM32AT_CNTRL, DMM32AT_CLKACC); - /* get access to the DIO regs */ - outb(DMM32AT_DIOACC, dev->iobase + DMM32AT_CNTRL); - /* set the DIO's to the defualt input setting */ - devpriv->dio_config = DMM32AT_DIRA | DMM32AT_DIRB | - DMM32AT_DIRCL | DMM32AT_DIRCH | DMM32AT_DIENABLE; - outb(devpriv->dio_config, dev->iobase + DMM32AT_DIOCONF); - - /* set up the subdevice */ - s->type = COMEDI_SUBD_DIO; - s->subdev_flags = SDF_READABLE | SDF_WRITABLE; - s->n_chan = 24; - s->maxdata = 1; - s->state = 0; - s->range_table = &range_digital; - s->insn_bits = dmm32at_dio_insn_bits; - s->insn_config = dmm32at_dio_insn_config; + /* write the counter 1 control word and low byte to counter */ + dmm_outb(dev, DMM32AT_CLKCT, DMM32AT_CLKCT1); + dmm_outb(dev, DMM32AT_CLK1, lo1); - /* success */ - printk(KERN_INFO "comedi%d: dmm32at: attached\n", dev->minor); + /* write the counter 2 control word and low byte then to counter */ + dmm_outb(dev, DMM32AT_CLKCT, DMM32AT_CLKCT2); + dmm_outb(dev, DMM32AT_CLK2, lo2); + dmm_outb(dev, DMM32AT_CLK2, hi2); - return 1; + /* enable the ai conversion interrupt and the clock to start scans */ + dmm_outb(dev, DMM32AT_INTCLOCK, DMM32AT_ADINT | DMM32AT_CLKSEL); } -static void dmm32at_detach(struct comedi_device *dev) +/* + * A convenient macro that defines init_module() and cleanup_module(), + * as necessary. + */ +static int __init driver_dmm32at_init_module(void) { - if (dev->irq) - free_irq(dev->irq, dev); - if (dev->iobase) - release_region(dev->iobase, DMM32AT_MEMSIZE); + return comedi_driver_register(&driver_dmm32at); } -static const struct dmm32at_board dmm32at_boards[] = { - { - .name = "dmm32at", - }, -}; +static void __exit driver_dmm32at_cleanup_module(void) +{ + comedi_driver_unregister(&driver_dmm32at); +} -static struct comedi_driver dmm32at_driver = { - .driver_name = "dmm32at", - .module = THIS_MODULE, - .attach = dmm32at_attach, - .detach = dmm32at_detach, - .board_name = &dmm32at_boards[0].name, - .offset = sizeof(struct dmm32at_board), - .num_names = ARRAY_SIZE(dmm32at_boards), -}; -module_comedi_driver(dmm32at_driver); +module_init(driver_dmm32at_init_module); +module_exit(driver_dmm32at_cleanup_module); MODULE_AUTHOR("Comedi http://www.comedi.org"); MODULE_DESCRIPTION("Comedi low-level driver"); diff --git a/trunk/drivers/staging/comedi/drivers/ni_670x.c b/trunk/drivers/staging/comedi/drivers/ni_670x.c index 9c57618f2c5b..9032baccf3aa 100644 --- a/trunk/drivers/staging/comedi/drivers/ni_670x.c +++ b/trunk/drivers/staging/comedi/drivers/ni_670x.c @@ -61,27 +61,43 @@ Commands are not supported. /* Board description*/ struct ni_670x_board { - const char *name; unsigned short dev_id; + const char *name; unsigned short ao_chans; + unsigned short ao_bits; }; static const struct ni_670x_board ni_670x_boards[] = { { - .name = "PCI-6703", - .dev_id = 0x2c90, - .ao_chans = 16, - }, { - .name = "PXI-6704", - .dev_id = 0x1920, - .ao_chans = 32, - }, { - .name = "PCI-6704", - .dev_id = 0x1290, - .ao_chans = 32, - }, + .dev_id = 0x2c90, + .name = "PCI-6703", + .ao_chans = 16, + .ao_bits = 16, + }, + { + .dev_id = 0x1920, + .name = "PXI-6704", + .ao_chans = 32, + .ao_bits = 16, + }, + { + .dev_id = 0x1290, + .name = "PCI-6704", + .ao_chans = 32, + .ao_bits = 16, + }, +}; + +static DEFINE_PCI_DEVICE_TABLE(ni_670x_pci_table) = { + {PCI_DEVICE(PCI_VENDOR_ID_NI, 0x2c90)}, + {PCI_DEVICE(PCI_VENDOR_ID_NI, 0x1920)}, + {0} }; +MODULE_DEVICE_TABLE(pci, ni_670x_pci_table); + +#define thisboard ((struct ni_670x_board *)dev->board_ptr) + struct ni_670x_private { struct mite_struct *mite; @@ -90,13 +106,163 @@ struct ni_670x_private { unsigned int ao_readback[32]; }; +#define devpriv ((struct ni_670x_private *)dev->private) +#define n_ni_670x_boards ARRAY_SIZE(ni_670x_boards) + +static int ni_670x_attach(struct comedi_device *dev, + struct comedi_devconfig *it); +static void ni_670x_detach(struct comedi_device *dev); + +static struct comedi_driver driver_ni_670x = { + .driver_name = "ni_670x", + .module = THIS_MODULE, + .attach = ni_670x_attach, + .detach = ni_670x_detach, +}; + +static int __devinit driver_ni_670x_pci_probe(struct pci_dev *dev, + const struct pci_device_id *ent) +{ + return comedi_pci_auto_config(dev, &driver_ni_670x); +} + +static void __devexit driver_ni_670x_pci_remove(struct pci_dev *dev) +{ + comedi_pci_auto_unconfig(dev); +} + +static struct pci_driver driver_ni_670x_pci_driver = { + .id_table = ni_670x_pci_table, + .probe = &driver_ni_670x_pci_probe, + .remove = __devexit_p(&driver_ni_670x_pci_remove) +}; + +static int __init driver_ni_670x_init_module(void) +{ + int retval; + + retval = comedi_driver_register(&driver_ni_670x); + if (retval < 0) + return retval; + + driver_ni_670x_pci_driver.name = (char *)driver_ni_670x.driver_name; + return pci_register_driver(&driver_ni_670x_pci_driver); +} + +static void __exit driver_ni_670x_cleanup_module(void) +{ + pci_unregister_driver(&driver_ni_670x_pci_driver); + comedi_driver_unregister(&driver_ni_670x); +} + +module_init(driver_ni_670x_init_module); +module_exit(driver_ni_670x_cleanup_module); + static struct comedi_lrange range_0_20mA = { 1, {RANGE_mA(0, 20)} }; +static int ni_670x_find_device(struct comedi_device *dev, int bus, int slot); + +static int ni_670x_ao_winsn(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data); +static int ni_670x_ao_rinsn(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data); +static int ni_670x_dio_insn_bits(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data); +static int ni_670x_dio_insn_config(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data); + +static int ni_670x_attach(struct comedi_device *dev, + struct comedi_devconfig *it) +{ + struct comedi_subdevice *s; + int ret; + int i; + + printk(KERN_INFO "comedi%d: ni_670x: ", dev->minor); + + ret = alloc_private(dev, sizeof(struct ni_670x_private)); + if (ret < 0) + return ret; + + ret = ni_670x_find_device(dev, it->options[0], it->options[1]); + if (ret < 0) + return ret; + + ret = mite_setup(devpriv->mite); + if (ret < 0) { + printk(KERN_WARNING "error setting up mite\n"); + return ret; + } + dev->board_name = thisboard->name; + dev->irq = mite_irq(devpriv->mite); + printk(KERN_INFO " %s", dev->board_name); + + ret = comedi_alloc_subdevices(dev, 2); + if (ret) + return ret; + + s = dev->subdevices + 0; + /* analog output subdevice */ + s->type = COMEDI_SUBD_AO; + s->subdev_flags = SDF_WRITABLE; + s->n_chan = thisboard->ao_chans; + s->maxdata = 0xffff; + if (s->n_chan == 32) { + const struct comedi_lrange **range_table_list; + + range_table_list = kmalloc(sizeof(struct comedi_lrange *) * 32, + GFP_KERNEL); + if (!range_table_list) + return -ENOMEM; + s->range_table_list = range_table_list; + for (i = 0; i < 16; i++) { + range_table_list[i] = &range_bipolar10; + range_table_list[16 + i] = &range_0_20mA; + } + } else { + s->range_table = &range_bipolar10; + } + s->insn_write = &ni_670x_ao_winsn; + s->insn_read = &ni_670x_ao_rinsn; + + s = dev->subdevices + 1; + /* digital i/o subdevice */ + s->type = COMEDI_SUBD_DIO; + s->subdev_flags = SDF_READABLE | SDF_WRITABLE; + s->n_chan = 8; + s->maxdata = 1; + s->range_table = &range_digital; + s->insn_bits = ni_670x_dio_insn_bits; + s->insn_config = ni_670x_dio_insn_config; + + /* Config of misc registers */ + writel(0x10, devpriv->mite->daq_io_addr + MISC_CONTROL_OFFSET); + /* Config of ao registers */ + writel(0x00, devpriv->mite->daq_io_addr + AO_CONTROL_OFFSET); + + printk(KERN_INFO "attached\n"); + + return 1; +} + +static void ni_670x_detach(struct comedi_device *dev) +{ + kfree(dev->subdevices[0].range_table_list); + if (dev->private && devpriv->mite) + mite_unsetup(devpriv->mite); + if (dev->irq) + free_irq(dev->irq, dev); +} + static int ni_670x_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { - struct ni_670x_private *devpriv = dev->private; int i; int chan = CR_CHAN(insn->chanspec); @@ -127,7 +293,6 @@ static int ni_670x_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { - struct ni_670x_private *devpriv = dev->private; int i; int chan = CR_CHAN(insn->chanspec); @@ -141,20 +306,18 @@ static int ni_670x_dio_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { - struct ni_670x_private *devpriv = dev->private; - void __iomem *io_addr = devpriv->mite->daq_io_addr + - DIO_PORT0_DATA_OFFSET; - unsigned int mask = data[0]; - unsigned int bits = data[1]; - - if (mask) { - s->state &= ~mask; - s->state |= (bits & mask); - - writel(s->state, io_addr); + /* The insn data is a mask in data[0] and the new data + * in data[1], each channel cooresponding to a bit. */ + if (data[0]) { + s->state &= ~data[0]; + s->state |= data[0] & data[1]; + writel(s->state, + devpriv->mite->daq_io_addr + DIO_PORT0_DATA_OFFSET); } - data[1] = readl(io_addr); + /* on return, data[1] contains the value of the digital + * input lines. */ + data[1] = readl(devpriv->mite->daq_io_addr + DIO_PORT0_DATA_OFFSET); return insn->n; } @@ -163,7 +326,6 @@ static int ni_670x_dio_insn_config(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { - struct ni_670x_private *devpriv = dev->private; int chan = CR_CHAN(insn->chanspec); switch (data[0]) { @@ -189,7 +351,6 @@ static int ni_670x_dio_insn_config(struct comedi_device *dev, static int ni_670x_find_device(struct comedi_device *dev, int bus, int slot) { - struct ni_670x_private *devpriv = dev->private; struct mite_struct *mite; int i; @@ -202,7 +363,7 @@ static int ni_670x_find_device(struct comedi_device *dev, int bus, int slot) continue; } - for (i = 0; i < ARRAY_SIZE(ni_670x_boards); i++) { + for (i = 0; i < n_ni_670x_boards; i++) { if (mite_device_id(mite) == ni_670x_boards[i].dev_id) { dev->board_ptr = ni_670x_boards + i; devpriv->mite = mite; @@ -211,136 +372,11 @@ static int ni_670x_find_device(struct comedi_device *dev, int bus, int slot) } } } - dev_warn(dev->class_dev, "no device found\n"); + printk(KERN_INFO "no device found\n"); mite_list_devices(); return -EIO; } -static int ni_670x_attach(struct comedi_device *dev, - struct comedi_devconfig *it) -{ - const struct ni_670x_board *thisboard; - struct ni_670x_private *devpriv; - struct comedi_subdevice *s; - int ret; - int i; - - ret = alloc_private(dev, sizeof(*devpriv)); - if (ret < 0) - return ret; - devpriv = dev->private; - - ret = ni_670x_find_device(dev, it->options[0], it->options[1]); - if (ret < 0) - return ret; - thisboard = comedi_board(dev); - - ret = mite_setup(devpriv->mite); - if (ret < 0) { - dev_warn(dev->class_dev, "error setting up mite\n"); - return ret; - } - dev->board_name = thisboard->name; - dev->irq = mite_irq(devpriv->mite); - - ret = comedi_alloc_subdevices(dev, 2); - if (ret) - return ret; - - s = dev->subdevices + 0; - /* analog output subdevice */ - s->type = COMEDI_SUBD_AO; - s->subdev_flags = SDF_WRITABLE; - s->n_chan = thisboard->ao_chans; - s->maxdata = 0xffff; - if (s->n_chan == 32) { - const struct comedi_lrange **range_table_list; - - range_table_list = kmalloc(sizeof(struct comedi_lrange *) * 32, - GFP_KERNEL); - if (!range_table_list) - return -ENOMEM; - s->range_table_list = range_table_list; - for (i = 0; i < 16; i++) { - range_table_list[i] = &range_bipolar10; - range_table_list[16 + i] = &range_0_20mA; - } - } else { - s->range_table = &range_bipolar10; - } - s->insn_write = &ni_670x_ao_winsn; - s->insn_read = &ni_670x_ao_rinsn; - - s = dev->subdevices + 1; - /* digital i/o subdevice */ - s->type = COMEDI_SUBD_DIO; - s->subdev_flags = SDF_READABLE | SDF_WRITABLE; - s->n_chan = 8; - s->maxdata = 1; - s->range_table = &range_digital; - s->insn_bits = ni_670x_dio_insn_bits; - s->insn_config = ni_670x_dio_insn_config; - - /* Config of misc registers */ - writel(0x10, devpriv->mite->daq_io_addr + MISC_CONTROL_OFFSET); - /* Config of ao registers */ - writel(0x00, devpriv->mite->daq_io_addr + AO_CONTROL_OFFSET); - - dev_info(dev->class_dev, "%s: %s attached\n", - dev->driver->driver_name, dev->board_name); - - return 0; -} - -static void ni_670x_detach(struct comedi_device *dev) -{ - struct ni_670x_private *devpriv = dev->private; - struct comedi_subdevice *s; - - if (dev->n_subdevices) { - s = dev->subdevices + 0; - if (s) - kfree(s->range_table_list); - } - if (devpriv && devpriv->mite) - mite_unsetup(devpriv->mite); - if (dev->irq) - free_irq(dev->irq, dev); -} - -static struct comedi_driver ni_670x_driver = { - .driver_name = "ni_670x", - .module = THIS_MODULE, - .attach = ni_670x_attach, - .detach = ni_670x_detach, -}; - -static int __devinit ni_670x_pci_probe(struct pci_dev *dev, - const struct pci_device_id *ent) -{ - return comedi_pci_auto_config(dev, &ni_670x_driver); -} - -static void __devexit ni_670x_pci_remove(struct pci_dev *dev) -{ - comedi_pci_auto_unconfig(dev); -} - -static DEFINE_PCI_DEVICE_TABLE(ni_670x_pci_table) = { - { PCI_DEVICE(PCI_VENDOR_ID_NI, 0x2c90) }, - { PCI_DEVICE(PCI_VENDOR_ID_NI, 0x1920) }, - { 0 } -}; -MODULE_DEVICE_TABLE(pci, ni_670x_pci_table); - -static struct pci_driver ni_670x_pci_driver = { - .name ="ni_670x", - .id_table = ni_670x_pci_table, - .probe = ni_670x_pci_probe, - .remove = __devexit_p(ni_670x_pci_remove), -}; -module_comedi_pci_driver(ni_670x_driver, ni_670x_pci_driver); - MODULE_AUTHOR("Comedi http://www.comedi.org"); MODULE_DESCRIPTION("Comedi low-level driver"); MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/staging/comedi/drivers/s626.c b/trunk/drivers/staging/comedi/drivers/s626.c index f90578e5e727..bef9649960b8 100644 --- a/trunk/drivers/staging/comedi/drivers/s626.c +++ b/trunk/drivers/staging/comedi/drivers/s626.c @@ -2636,7 +2636,7 @@ static int s626_attach(struct comedi_device *dev, struct comedi_devconfig *it) /* digital I/O subdevice */ s->type = COMEDI_SUBD_DIO; s->subdev_flags = SDF_WRITABLE | SDF_READABLE; - s->n_chan = 16; + s->n_chan = S626_DIO_CHANNELS; s->maxdata = 1; s->io_bits = 0xffff; s->private = &dio_private_A;