diff options
Diffstat (limited to 'drivers/staging/comedi/drivers/cb_pcimdda.c')
-rw-r--r-- | drivers/staging/comedi/drivers/cb_pcimdda.c | 393 |
1 files changed, 92 insertions, 301 deletions
diff --git a/drivers/staging/comedi/drivers/cb_pcimdda.c b/drivers/staging/comedi/drivers/cb_pcimdda.c index a80146133c0..ba9f0599be2 100644 --- a/drivers/staging/comedi/drivers/cb_pcimdda.c +++ b/drivers/staging/comedi/drivers/cb_pcimdda.c @@ -57,12 +57,7 @@ output modes on the board: then issue one comedi_data_read() on any channel on the AO subdevice to initiate the simultaneous XFER. -Configuration Options: - [0] PCI bus (optional) - [1] PCI slot (optional) - [2] analog output range jumper setting - 0 == +/- 5 V - 1 == +/- 10 V +Configuration Options: not applicable, uses PCI auto config */ /* @@ -93,337 +88,133 @@ Configuration Options: #define PCI_ID_PCIM_DDA06_16 0x0053 /* - * This is straight from skel.c -- I did this in case this source file - * will someday support more than 1 board... + * Register map, 8-bit access only */ -struct board_struct { - const char *name; - unsigned short device_id; - int ao_chans; - int ao_bits; - int dio_chans; - int dio_method; - /* how many bytes into the BADR are the DIO ports */ - int dio_offset; - int regs_badrindex; /* IO Region for the control, analog output, - and DIO registers */ - int reg_sz; /* number of bytes of registers in io region */ -}; +#define PCIMDDA_DA_CHAN(x) (0x00 + (x) * 2) +#define PCIMDDA_8255_BASE_REG 0x0c -enum DIO_METHODS { - DIO_NONE = 0, - DIO_8255, - DIO_INTERNAL /* unimplemented */ -}; +#define MAX_AO_READBACK_CHANNELS 6 -static const struct board_struct boards[] = { - { - .name = "cb_pcimdda06-16", - .device_id = PCI_ID_PCIM_DDA06_16, - .ao_chans = 6, - .ao_bits = 16, - .dio_chans = 24, - .dio_method = DIO_8255, - .dio_offset = 12, - .regs_badrindex = 3, - .reg_sz = 16, - } +struct cb_pcimdda_private { + unsigned int ao_readback[MAX_AO_READBACK_CHANNELS]; }; -/* - * Useful for shorthand access to the particular board structure - */ -#define thisboard ((const struct board_struct *)dev->board_ptr) - -#define REG_SZ (thisboard->reg_sz) -#define REGS_BADRINDEX (thisboard->regs_badrindex) - -/* - * 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 board_private_struct { - unsigned long registers; /* set by probe */ - unsigned long dio_registers; - char attached_to_8255; /* boolean */ - /* would be useful for a PCI device */ - struct pci_dev *pci_dev; - -#define MAX_AO_READBACK_CHANNELS 6 - /* Used for AO readback */ - unsigned int ao_readback[MAX_AO_READBACK_CHANNELS]; +static int cb_pcimdda_ao_winsn(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) +{ + struct cb_pcimdda_private *devpriv = dev->private; + unsigned int chan = CR_CHAN(insn->chanspec); + unsigned long offset = dev->iobase + PCIMDDA_DA_CHAN(chan); + unsigned int val = 0; + int i; -}; + for (i = 0; i < insn->n; i++) { + val = data[i]; -/* - * most drivers define the following macro to make it easy to - * access the private structure. - */ -#define devpriv ((struct board_private_struct *)dev->private) + /* + * Write the LSB then MSB. + * + * If the simultaneous xfer mode is selected by the + * jumper on the card, a read instruction is needed + * in order to initiate the simultaneous transfer. + * Otherwise, the DAC will be updated when the MSB + * is written. + */ + outb(val & 0x00ff, offset); + outb((val >> 8) & 0x00ff, offset + 1); + } -static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data); -static int ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data); + /* Cache the last value for readback */ + devpriv->ao_readback[chan] = val; -/*--------------------------------------------------------------------------- - HELPER FUNCTION DECLARATIONS ------------------------------------------------------------------------------*/ + return insn->n; +} -/* returns a maxdata value for a given n_bits */ -static inline unsigned int figure_out_maxdata(int bits) +static int cb_pcimdda_ao_rinsn(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) { - return ((unsigned int)1 << bits) - 1; -} + struct cb_pcimdda_private *devpriv = dev->private; + int chan = CR_CHAN(insn->chanspec); + unsigned long offset = dev->iobase + PCIMDDA_DA_CHAN(chan); + int i; -/* - * Probes for a supported device. - * - * Prerequisite: private be allocated already inside dev - * - * If the device is found, it returns 0 and has the following side effects: - * - * o assigns a struct pci_dev * to dev->private->pci_dev - * o assigns a struct board * to dev->board_ptr - * o sets dev->private->registers - * o sets dev->private->dio_registers - * - * Otherwise, returns a -errno on error - */ -static int probe(struct comedi_device *dev, const struct comedi_devconfig *it); + for (i = 0; i < insn->n; i++) { + /* Initiate the simultaneous transfer */ + inw(offset); + + data[i] = devpriv->ao_readback[chan]; + } -/*--------------------------------------------------------------------------- - FUNCTION DEFINITIONS ------------------------------------------------------------------------------*/ + return insn->n; +} -/* - * 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 attach(struct comedi_device *dev, struct comedi_devconfig *it) +static int cb_pcimdda_attach_pci(struct comedi_device *dev, + struct pci_dev *pcidev) { + struct cb_pcimdda_private *devpriv; struct comedi_subdevice *s; - int err; + int ret; -/* - * Allocate the private structure area. alloc_private() is a - * convenient macro defined in comedidev.h. - * if this function fails (returns negative) then the private area is - * kfree'd by comedi - */ - if (alloc_private(dev, sizeof(struct board_private_struct)) < 0) - return -ENOMEM; + comedi_set_hw_dev(dev, &pcidev->dev); + dev->board_name = dev->driver->driver_name; -/* - * If you can probe the device to determine what device in a series - * it is, this is the place to do it. Otherwise, dev->board_ptr - * should already be initialized. - */ - err = probe(dev, it); - if (err) - return err; + ret = alloc_private(dev, sizeof(*devpriv)); + if (ret) + return ret; + devpriv = dev->private; -/* Output some info */ - printk("comedi%d: %s: ", dev->minor, thisboard->name); + ret = comedi_pci_enable(pcidev, dev->board_name); + if (ret) + return ret; + dev->iobase = pci_resource_start(pcidev, 3); -/* - * Initialize dev->board_name. Note that we can use the "thisboard" - * macro now, since we just initialized it in the last line. - */ - dev->board_name = thisboard->name; - - err = comedi_alloc_subdevices(dev, 2); - if (err) - return err; - - s = dev->subdevices + 0; + 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 | SDF_READABLE; - s->n_chan = thisboard->ao_chans; - s->maxdata = figure_out_maxdata(thisboard->ao_bits); - /* this is hard-coded here */ - if (it->options[2]) - s->range_table = &range_bipolar10; - else - s->range_table = &range_bipolar5; - s->insn_write = &ao_winsn; - s->insn_read = &ao_rinsn; - - s = dev->subdevices + 1; + s->type = COMEDI_SUBD_AO; + s->subdev_flags = SDF_WRITABLE | SDF_READABLE; + s->n_chan = 6; + s->maxdata = 0xffff; + s->range_table = &range_bipolar5; + s->insn_write = cb_pcimdda_ao_winsn; + s->insn_read = cb_pcimdda_ao_rinsn; + + s = &dev->subdevices[1]; /* digital i/o subdevice */ - if (thisboard->dio_chans) { - switch (thisboard->dio_method) { - case DIO_8255: - /* - * this is a straight 8255, so register us with - * the 8255 driver - */ - subdev_8255_init(dev, s, NULL, devpriv->dio_registers); - devpriv->attached_to_8255 = 1; - break; - case DIO_INTERNAL: - default: - printk("DIO_INTERNAL not implemented yet!\n"); - return -ENXIO; - break; - } - } else { - s->type = COMEDI_SUBD_UNUSED; - } + ret = subdev_8255_init(dev, s, NULL, + dev->iobase + PCIMDDA_8255_BASE_REG); + if (ret) + return ret; - printk("attached\n"); + dev_info(dev->class_dev, "%s attached\n", dev->board_name); return 1; } -static void detach(struct comedi_device *dev) -{ - if (devpriv) { - if (dev->subdevices && devpriv->attached_to_8255) { - subdev_8255_cleanup(dev, dev->subdevices + 2); - devpriv->attached_to_8255 = 0; - } - if (devpriv->pci_dev) { - if (devpriv->registers) - comedi_pci_disable(devpriv->pci_dev); - pci_dev_put(devpriv->pci_dev); - } - } -} - -static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data) +static void cb_pcimdda_detach(struct comedi_device *dev) { - int i; - int chan = CR_CHAN(insn->chanspec); - unsigned long offset = devpriv->registers + chan * 2; + struct pci_dev *pcidev = comedi_to_pci_dev(dev); - /* 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++) { - /* first, load the low byte */ - outb((char)(data[i] & 0x00ff), offset); - /* next, write the high byte -- only after this is written is - the channel voltage updated in the DAC, unless - we're in simultaneous xfer mode (jumper on card) - then a rinsn is necessary to actually update the DAC -- - see ao_rinsn() below... */ - outb((char)(data[i] >> 8 & 0x00ff), offset + 1); - - /* for testing only.. the actual rinsn SHOULD do an inw! - (see the stuff about simultaneous XFER mode on this board) */ - devpriv->ao_readback[chan] = data[i]; + if (dev->subdevices) + subdev_8255_cleanup(dev, &dev->subdevices[1]); + if (pcidev) { + if (dev->iobase) + comedi_pci_disable(pcidev); } - - /* 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->ao_readback. - However, since this board has this jumper setting called "Simultaneous - Xfer mode" (off by default), we will support it. Simultaneaous xfer - mode is accomplished by loading ALL the values you want for AO in all the - channels, then READing off one of the AO registers to initiate the - instantaneous simultaneous update of all DAC outputs, which makes - all AO channels update simultaneously. This is useful for some control - applications, I would imagine. -*/ -static int ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data) -{ - int i; - int chan = CR_CHAN(insn->chanspec); - - for (i = 0; i < insn->n; i++) { - inw(devpriv->registers + chan * 2); - /* - * should I set data[i] to the result of the actual read - * on the register or the cached unsigned int in - * devpriv->ao_readback[]? - */ - data[i] = devpriv->ao_readback[chan]; - } - - return i; -} - -/*--------------------------------------------------------------------------- - HELPER FUNCTION DEFINITIONS ------------------------------------------------------------------------------*/ - -/* - * Probes for a supported device. - * - * Prerequisite: private be allocated already inside dev - * - * If the device is found, it returns 0 and has the following side effects: - * - * o assigns a struct pci_dev * to dev->private->pci_dev - * o assigns a struct board * to dev->board_ptr - * o sets dev->private->registers - * o sets dev->private->dio_registers - * - * Otherwise, returns a -errno on error - */ -static int probe(struct comedi_device *dev, const struct comedi_devconfig *it) -{ - struct pci_dev *pcidev = NULL; - int index; - unsigned long registers; - - for_each_pci_dev(pcidev) { - /* is it not a computer boards card? */ - if (pcidev->vendor != PCI_VENDOR_ID_COMPUTERBOARDS) - continue; - /* loop through cards supported by this driver */ - for (index = 0; index < ARRAY_SIZE(boards); index++) { - if (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; - } - } - /* found ! */ - - devpriv->pci_dev = pcidev; - dev->board_ptr = boards + index; - if (comedi_pci_enable(pcidev, thisboard->name)) { - printk - ("cb_pcimdda: Failed to enable PCI device and request regions\n"); - return -EIO; - } - registers = - pci_resource_start(devpriv->pci_dev, - REGS_BADRINDEX); - devpriv->registers = registers; - devpriv->dio_registers - = devpriv->registers + thisboard->dio_offset; - return 0; - } - } - - printk("cb_pcimdda: No supported ComputerBoards/MeasurementComputing " - "card found at the requested position\n"); - return -ENODEV; } static struct comedi_driver cb_pcimdda_driver = { .driver_name = "cb_pcimdda", .module = THIS_MODULE, - .attach = attach, - .detach = detach, + .attach_pci = cb_pcimdda_attach_pci, + .detach = cb_pcimdda_detach, }; static int __devinit cb_pcimdda_pci_probe(struct pci_dev *dev, |