diff options
Diffstat (limited to 'drivers/staging/comedi/drivers/adl_pci6208.c')
-rw-r--r-- | drivers/staging/comedi/drivers/adl_pci6208.c | 381 |
1 files changed, 153 insertions, 228 deletions
diff --git a/drivers/staging/comedi/drivers/adl_pci6208.c b/drivers/staging/comedi/drivers/adl_pci6208.c index de8c68a..3bec0f6 100644 --- a/drivers/staging/comedi/drivers/adl_pci6208.c +++ b/drivers/staging/comedi/drivers/adl_pci6208.c @@ -41,301 +41,230 @@ References: - adl_pci9111.c copied the entire pci setup section - adl_pci9118.c */ + +#include "../comedidev.h" + /* - * 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. + * PCI-6208/6216-GL register map */ -#include "../comedidev.h" -#include "comedi_pci.h" +#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 -/* Board descriptions */ struct pci6208_board { const char *name; - unsigned short dev_id; /* `lspci` will show you this */ + unsigned short dev_id; 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 - /* , .ao_bits = 16 */ - } + .name = "pci6208a", + .dev_id = 0x6208, + .ao_chans = 8, + }, }; -/* Will be initialized in pci6208_find device(). */ -#define thisboard ((const struct pci6208_board *)dev->board_ptr) - struct pci6208_private { - int data; - struct pci_dev *pci_dev; /* for a PCI device */ - unsigned int ao_readback[2]; /* Used for AO readback */ + unsigned int ao_readback[PCI6208_MAX_AO_CHANNELS]; }; -#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) { - int i = 0, Data_Read; - unsigned short chan = CR_CHAN(insn->chanspec); + struct pci6208_private *devpriv = dev->private; + int chan = CR_CHAN(insn->chanspec); unsigned long invert = 1 << (16 - 1); - 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. */ + unsigned long value = 0; + unsigned short status; + int i; + for (i = 0; i < insn->n; i++) { - out_value = data[i] ^ invert; - /* a typical programming sequence */ + value = data[i] ^ invert; + do { - Data_Read = (inw(dev->iobase) & 1); - } while (Data_Read); - outw(out_value, dev->iobase + (0x02 * chan)); - devpriv->ao_readback[chan] = out_value; + status = inw(dev->iobase + PCI6208_AO_STATUS); + } while (status & PCI6208_AO_STATUS_DATA_SEND); + + outw(value, dev->iobase + PCI6208_AO_CONTROL(chan)); } + devpriv->ao_readback[chan] = value; - /* return the number of samples read/written */ - return i; + return insn->n; } -/* 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) { - int i; + struct pci6208_private *devpriv = dev->private; int chan = CR_CHAN(insn->chanspec); + int i; for (i = 0; i < insn->n; i++) data[i] = devpriv->ao_readback[chan]; - return i; + return insn->n; } -/* 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) */ -/* { */ -/* if(insn->n!=2)return -EINVAL; */ - - /* 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 2; */ -/* } */ - -/* 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<<chan; */ -/* }else{ */ -/* s->io_bits &= ~(1<<chan); */ -/* } */ - /* outw(s->io_bits,dev->iobase + SKEL_DIO_CONFIG); */ - -/* return 1; */ -/* } */ - -static int pci6208_find_device(struct comedi_device *dev, int bus, int slot) +static int pci6208_dio_insn_bits(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) { - struct pci_dev *pci_dev = NULL; - int i; + unsigned int mask = data[0] & PCI6208_DIO_DO_MASK; + unsigned int bits = data[1]; - 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; - } - } - } - } - - printk(KERN_ERR "comedi%d: no supported board found! " - "(req. bus/slot : %d/%d)\n", - dev->minor, bus, slot); - return -EIO; + if (mask) { + s->state &= ~mask; + s->state |= bits & mask; -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); - - /* TODO: Warn about non-tested boards. */ - /* switch(board->device_id) */ - /* { */ - /* }; */ + outw(s->state, dev->iobase + PCI6208_DIO); + } - devpriv->pci_dev = pci_dev; + s->state = inw(dev->iobase + PCI6208_DIO); + data[1] = s->state; - return 0; + return insn->n; } -static int -pci6208_pci_setup(struct pci_dev *pci_dev, unsigned long *io_base_ptr, - int dev_minor) +static int pci6208_dio_insn_config(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) { - unsigned long io_base, io_range, lcr_io_base, lcr_io_range; - - /* 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; + 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; } - /* 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); + return insn->n; +} - *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; */ +static struct pci_dev *pci6208_find_device(struct comedi_device *dev, + struct comedi_devconfig *it) +{ + const struct pci6208_board *thisboard; + struct pci_dev *pci_dev = NULL; + int bus = it->options[0]; + int slot = it->options[1]; + int i; - return 0; + 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; + } + } + dev_err(dev->class_dev, + "No supported board found! (req. bus %d, slot %d)\n", + bus, slot); + return NULL; } static int pci6208_attach(struct comedi_device *dev, struct comedi_devconfig *it) { + const struct pci6208_board *thisboard; + struct pci6208_private *devpriv; + struct pci_dev *pcidev; struct comedi_subdevice *s; - int retval; - unsigned long io_base; + int ret; - printk(KERN_INFO "comedi%d: pci6208: ", dev->minor); + ret = alloc_private(dev, sizeof(*devpriv)); + if (ret < 0) + return ret; + devpriv = dev->private; - retval = alloc_private(dev, sizeof(struct pci6208_private)); - if (retval < 0) - return retval; - - retval = pci6208_find_device(dev, it->options[0], it->options[1]); - if (retval < 0) - return retval; - - retval = pci6208_pci_setup(devpriv->pci_dev, &io_base, dev->minor); - if (retval < 0) - return retval; + pcidev = pci6208_find_device(dev, it); + if (!pcidev) + return -EIO; + comedi_set_hw_dev(dev, &pcidev->dev); + thisboard = comedi_board(dev); - dev->iobase = io_base; dev->board_name = thisboard->name; - if (alloc_subdevices(dev, 2) < 0) - return -ENOMEM; + ret = comedi_pci_enable(pcidev, 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(pcidev, 2); + + 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; /* 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; */ + 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; /* digital i/o subdevice */ - /* 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; */ + 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); - printk(KERN_INFO "attached\n"); + dev_info(dev->class_dev, "%s: %s, I/O base=0x%04lx\n", + dev->driver->driver_name, dev->board_name, dev->iobase); - return 1; + return 0; } static void pci6208_detach(struct comedi_device *dev) { - if (devpriv && devpriv->pci_dev) { + struct pci_dev *pcidev = comedi_to_pci_dev(dev); + + if (pcidev) { if (dev->iobase) - comedi_pci_disable(devpriv->pci_dev); - pci_dev_put(devpriv->pci_dev); + comedi_pci_disable(pcidev); + pci_dev_put(pcidev); } } @@ -357,11 +286,7 @@ 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 } }; |