From fd356607fe4d213f221028bcb073942807eaab75 Mon Sep 17 00:00:00 2001 From: Drew Csillag Date: Thu, 19 Feb 2009 09:38:46 -0800 Subject: Staging: comedi: add pcm3724 driver From: Drew Csillag Driver Advantech PCM-3724 card From: Drew Csillag Cc: David Schleef Cc: Ian Abbott Cc: Frank Mori Hess Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/drivers/pcm3724.c | 306 +++++++++++++++++++++++++++++++ 1 file changed, 306 insertions(+) --- /dev/null +++ b/drivers/staging/comedi/drivers/pcm3724.c @@ -0,0 +1,306 @@ +/* + comedi/drivers/pcm724.c + + Drew Csillag + + hardware driver for Advantech card: + card: PCM-3724 + driver: pcm3724 + + Options for PCM-3724 + [0] - IO Base +*/ +/* +Driver: pcm3724 +Description: Advantech PCM-3724 +Author: Drew Csillag +Devices: [Advantech] PCM-3724 (pcm724) +Status: tested + +This is driver for digital I/O boards PCM-3724 with 48 DIO. +It needs 8255.o for operations and only immediate mode is supported. +See the source for configuration details. + +Copy/pasted/hacked from pcm724.c +*/ +/* + * check_driver overrides: + * comedi_insn + */ + +#include "../comedidev.h" + +#include +#include + +#include "8255.h" + +#define PCM3724_SIZE 16 +#define SIZE_8255 4 + +#define BUF_C0 0x1 +#define BUF_B0 0x2 +#define BUF_A0 0x4 +#define BUF_C1 0x8 +#define BUF_B1 0x10 +#define BUF_A1 0x20 + +#define GATE_A0 0x4 +#define GATE_B0 0x2 +#define GATE_C0 0x1 +#define GATE_A1 0x20 +#define GATE_B1 0x10 +#define GATE_C1 0x8 + +/* from 8255.c */ +#define CR_CW 0x80 +#define _8255_CR 3 +#define CR_B_IO 0x02 +#define CR_B_MODE 0x04 +#define CR_C_IO 0x09 +#define CR_A_IO 0x10 +#define CR_A_MODE(a) ((a)<<5) +#define CR_CW 0x80 + +static int pcm3724_attach(comedi_device * dev, comedi_devconfig * it); +static int pcm3724_detach(comedi_device * dev); + +typedef struct { + const char *name; // driver name + int dio; // num of DIO + int numofports; // num of 8255 subdevices + unsigned int IRQbits; // allowed interrupts + unsigned int io_range; // len of IO space +} boardtype; + +//used to track configured dios +typedef struct { + int dio_1; + int dio_2; +} priv_pcm3724; +static const boardtype boardtypes[] = { + {"pcm3724", 48, 2, 0x00fc, PCM3724_SIZE,}, +}; + +#define n_boardtypes (sizeof(boardtypes)/sizeof(boardtype)) +#define this_board ((const boardtype *)dev->board_ptr) + +static comedi_driver driver_pcm3724 = { + driver_name:"pcm3724", + module:THIS_MODULE, + attach:pcm3724_attach, + detach:pcm3724_detach, + board_name:&boardtypes[0].name, + num_names:n_boardtypes, + offset:sizeof(boardtype), +}; + +COMEDI_INITCLEANUP(driver_pcm3724); + +// (setq c-basic-offset 8) + +static int subdev_8255_cb(int dir, int port, int data, unsigned long arg) +{ + unsigned long iobase = arg; + unsigned char inbres; + //printk("8255cb %d %d %d %lx\n", dir,port,data,arg); + if (dir) { + //printk("8255 cb outb(%x, %lx)\n", data, iobase+port); + outb(data, iobase + port); + return 0; + } else { + inbres = inb(iobase + port); + //printk("8255 cb inb(%lx) = %x\n", iobase+port, inbres); + return inbres; + } +} + +static int compute_buffer(int config, int devno, comedi_subdevice * s) +{ + /* 1 in io_bits indicates output */ + if (s->io_bits & 0x0000ff) { + if (devno == 0) { + config |= BUF_A0; + } else { + config |= BUF_A1; + } + } + if (s->io_bits & 0x00ff00) { + if (devno == 0) { + config |= BUF_B0; + } else { + config |= BUF_B1; + } + } + if (s->io_bits & 0xff0000) { + if (devno == 0) { + config |= BUF_C0; + } else { + config |= BUF_C1; + } + } + return config; +} + +static void do_3724_config(comedi_device * dev, comedi_subdevice * s, + int chanspec) +{ + int config; + int buffer_config; + unsigned long port_8255_cfg; + + config = CR_CW; + buffer_config = 0; + + /* 1 in io_bits indicates output, 1 in config indicates input */ + if (!(s->io_bits & 0x0000ff)) { + config |= CR_A_IO; + } + if (!(s->io_bits & 0x00ff00)) { + config |= CR_B_IO; + } + if (!(s->io_bits & 0xff0000)) { + config |= CR_C_IO; + } + + buffer_config = compute_buffer(0, 0, dev->subdevices); + buffer_config = compute_buffer(buffer_config, 1, (dev->subdevices) + 1); + + if (s == dev->subdevices) { + port_8255_cfg = dev->iobase + _8255_CR; + } else { + port_8255_cfg = dev->iobase + SIZE_8255 + _8255_CR; + } + outb(buffer_config, dev->iobase + 8); /* update buffer register */ + //printk("pcm3724 buffer_config (%lx) %d, %x\n", dev->iobase + _8255_CR, chanspec, buffer_config); + outb(config, port_8255_cfg); +} + +static void enable_chan(comedi_device * dev, comedi_subdevice * s, int chanspec) +{ + unsigned int mask; + int gatecfg; + priv_pcm3724 *priv; + + gatecfg = 0; + priv = (priv_pcm3724 *) (dev->private); + + mask = 1 << CR_CHAN(chanspec); + if (s == dev->subdevices) { // subdev 0 + priv->dio_1 |= mask; + } else { //subdev 1 + priv->dio_2 |= mask; + } + if (priv->dio_1 & 0xff0000) { + gatecfg |= GATE_C0; + } + if (priv->dio_1 & 0xff00) { + gatecfg |= GATE_B0; + } + if (priv->dio_1 & 0xff) { + gatecfg |= GATE_A0; + } + if (priv->dio_2 & 0xff0000) { + gatecfg |= GATE_C1; + } + if (priv->dio_2 & 0xff00) { + gatecfg |= GATE_B1; + } + if (priv->dio_2 & 0xff) { + gatecfg |= GATE_A1; + } + // printk("gate control %x\n", gatecfg); + outb(gatecfg, dev->iobase + 9); +} + +/* overriding the 8255 insn config */ +static int subdev_3724_insn_config(comedi_device * dev, comedi_subdevice * s, + comedi_insn * insn, lsampl_t * data) +{ + unsigned int mask; + unsigned int bits; + + mask = 1 << CR_CHAN(insn->chanspec); + if (mask & 0x0000ff) { + bits = 0x0000ff; + } else if (mask & 0x00ff00) { + bits = 0x00ff00; + } else if (mask & 0x0f0000) { + bits = 0x0f0000; + } else { + bits = 0xf00000; + } + + switch (data[0]) { + case INSN_CONFIG_DIO_INPUT: + s->io_bits &= ~bits; + break; + case INSN_CONFIG_DIO_OUTPUT: + s->io_bits |= bits; + break; + case INSN_CONFIG_DIO_QUERY: + data[1] = (s->io_bits & bits) ? COMEDI_OUTPUT : COMEDI_INPUT; + return insn->n; + break; + default: + return -EINVAL; + } + + do_3724_config(dev, s, insn->chanspec); + enable_chan(dev, s, insn->chanspec); + return 1; +} + +static int pcm3724_attach(comedi_device * dev, comedi_devconfig * it) +{ + unsigned long iobase; + unsigned int iorange; + int ret, i, n_subdevices; + + iobase = it->options[0]; + iorange = this_board->io_range; + if ((ret = alloc_private(dev, sizeof(priv_pcm3724))) < 0) + return -ENOMEM; + + ((priv_pcm3724 *) (dev->private))->dio_1 = 0; + ((priv_pcm3724 *) (dev->private))->dio_2 = 0; + + printk("comedi%d: pcm3724: board=%s, 0x%03lx ", dev->minor, + this_board->name, iobase); + if (!iobase || !request_region(iobase, iorange, "pcm3724")) { + printk("I/O port conflict\n"); + return -EIO; + } + + dev->iobase = iobase; + dev->board_name = this_board->name; + printk("\n"); + + n_subdevices = this_board->numofports; + + if ((ret = alloc_subdevices(dev, n_subdevices)) < 0) + return ret; + + for (i = 0; i < dev->n_subdevices; i++) { + subdev_8255_init(dev, dev->subdevices + i, subdev_8255_cb, + (unsigned long)(dev->iobase + SIZE_8255 * i)); + ((dev->subdevices) + i)->insn_config = subdev_3724_insn_config; + }; + return 0; +} + +static int pcm3724_detach(comedi_device * dev) +{ + int i; + + if (dev->subdevices) { + for (i = 0; i < dev->n_subdevices; i++) { + subdev_8255_cleanup(dev, dev->subdevices + i); + } + } + if (dev->iobase) { + release_region(dev->iobase, this_board->io_range); + } + + return 0; +}