staging: comedi: amplc_dio200: internalize 8255 DIO implementation
authorIan Abbott <abbotti@mev.co.uk>
Wed, 24 Oct 2012 15:47:57 +0000 (16:47 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 24 Oct 2012 22:25:12 +0000 (15:25 -0700)
Implement the '8255' DIO subdevice internally to this module instead of
using the external "8255" module.  I plan to add support for additional
cards to this driver that would require the I/O callback functionality
of the 8255 module, but the existing callback functions do not have much
context to handle this elegantly.  The additional cards also have extra
DIO features which cannot be handled by the existing "8255" module and
that I'd like to support some time in the future.

The bottom line is I _could_ continue using the "8255" module for a
while with a callback function, but it would turn out to be a very ugly
callback function and I'd have to ditch the use of the "8255" module as
soon as I added an extra feature to the DIO subdevice.

Signed-off-by: Ian Abbott <abbotti@mev.co.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/comedi/drivers/amplc_dio200.c

index d874bdfed6ab894dc80b9d72d73741156b03aefb..0c824a99beda88a7c48704a985228d2bc7f0f8a1 100644 (file)
 #include "../comedidev.h"
 
 #include "comedi_fc.h"
-#include "8255.h"
 #include "8253.h"
 
 #define DIO200_DRIVER_NAME     "amplc_dio200"
 #define PCI_DEVICE_ID_AMPLICON_PCI215 0x000b
 #define PCI_DEVICE_ID_INVALID 0xffff
 
+/* 8255 control register bits */
+#define CR_C_LO_IO     0x01
+#define CR_B_IO                0x02
+#define CR_B_MODE      0x04
+#define CR_C_HI_IO     0x08
+#define CR_A_IO                0x10
+#define CR_A_MODE(a)   ((a)<<5)
+#define CR_CW          0x80
+
 /* 200 series registers */
 #define DIO200_IO_SIZE         0x20
 #define DIO200_XCLK_SCE                0x18    /* Group X clock selection register */
@@ -430,6 +438,10 @@ struct dio200_subdev_8254 {
        spinlock_t spinlock;
 };
 
+struct dio200_subdev_8255 {
+       unsigned int ofs;               /* DIO base offset */
+};
+
 struct dio200_subdev_intr {
        unsigned int ofs;
        spinlock_t spinlock;
@@ -1245,6 +1257,133 @@ dio200_subdev_8254_cleanup(struct comedi_device *dev,
        kfree(subpriv);
 }
 
+/*
+ * This function sets I/O directions for an '8255' DIO subdevice.
+ */
+static void dio200_subdev_8255_set_dir(struct comedi_device *dev,
+                                      struct comedi_subdevice *s)
+{
+       struct dio200_subdev_8255 *subpriv = s->private;
+       int config;
+
+       config = CR_CW;
+       /* 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 & 0x0f0000))
+               config |= CR_C_LO_IO;
+       if (!(s->io_bits & 0xf00000))
+               config |= CR_C_HI_IO;
+       outb(config, dev->iobase + subpriv->ofs + 3);
+}
+
+/*
+ * Handle 'insn_bits' for an '8255' DIO subdevice.
+ */
+static int dio200_subdev_8255_bits(struct comedi_device *dev,
+                                  struct comedi_subdevice *s,
+                                  struct comedi_insn *insn, unsigned int *data)
+{
+       struct dio200_subdev_8255 *subpriv = s->private;
+
+       if (data[0]) {
+               s->state &= ~data[0];
+               s->state |= (data[0] & data[1]);
+               if (data[0] & 0xff)
+                       outb(s->state & 0xff, dev->iobase + subpriv->ofs);
+               if (data[0] & 0xff00)
+                       outb((s->state >> 8) & 0xff,
+                            dev->iobase + subpriv->ofs + 1);
+               if (data[0] & 0xff0000)
+                       outb((s->state >> 16) & 0xff,
+                            dev->iobase + subpriv->ofs + 2);
+       }
+       data[1] = inb(dev->iobase + subpriv->ofs);
+       data[1] |= inb(dev->iobase + subpriv->ofs + 1) << 8;
+       data[1] |= inb(dev->iobase + subpriv->ofs + 2) << 16;
+       return 2;
+}
+
+/*
+ * Handle 'insn_config' for an '8255' DIO subdevice.
+ */
+static int dio200_subdev_8255_config(struct comedi_device *dev,
+                                    struct comedi_subdevice *s,
+                                    struct comedi_insn *insn,
+                                    unsigned int *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;
+       }
+       dio200_subdev_8255_set_dir(dev, s);
+       return 1;
+}
+
+/*
+ * This function initializes an '8255' DIO subdevice.
+ *
+ * offset is the offset to the 8255 chip.
+ */
+static int dio200_subdev_8255_init(struct comedi_device *dev,
+                                  struct comedi_subdevice *s,
+                                  unsigned int offset)
+{
+       struct dio200_subdev_8255 *subpriv;
+
+       subpriv = kzalloc(sizeof(*subpriv), GFP_KERNEL);
+       if (!subpriv)
+               return -ENOMEM;
+       subpriv->ofs = offset;
+       s->private = subpriv;
+       s->type = COMEDI_SUBD_DIO;
+       s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+       s->n_chan = 24;
+       s->range_table = &range_digital;
+       s->maxdata = 1;
+       s->insn_bits = dio200_subdev_8255_bits;
+       s->insn_config = dio200_subdev_8255_config;
+       s->state = 0;
+       s->io_bits = 0;
+       dio200_subdev_8255_set_dir(dev, s);
+       return 0;
+}
+
+/*
+ * This function cleans up an '8255' DIO subdevice.
+ */
+static void dio200_subdev_8255_cleanup(struct comedi_device *dev,
+                                      struct comedi_subdevice *s)
+{
+       struct dio200_subdev_8255 *subpriv = s->private;
+
+       kfree(subpriv);
+}
+
 static void dio200_report_attach(struct comedi_device *dev, unsigned int irq)
 {
        const struct dio200_board *thisboard = comedi_board(dev);
@@ -1301,8 +1440,8 @@ static int dio200_common_attach(struct comedi_device *dev, unsigned long iobase,
                        break;
                case sd_8255:
                        /* digital i/o subdevice (8255) */
-                       ret = subdev_8255_init(dev, s, NULL,
-                                              iobase + layout->sdinfo[n]);
+                       ret = dio200_subdev_8255_init(dev, s,
+                                                     layout->sdinfo[n]);
                        if (ret < 0)
                                return ret;
                        break;
@@ -1438,7 +1577,7 @@ static void dio200_detach(struct comedi_device *dev)
                                dio200_subdev_8254_cleanup(dev, s);
                                break;
                        case sd_8255:
-                               subdev_8255_cleanup(dev, s);
+                               dio200_subdev_8255_cleanup(dev, s);
                                break;
                        case sd_intr:
                                dio200_subdev_intr_cleanup(dev, s);