Skip to content

Commit

Permalink
staging: comedi: amplc_dio200: internalize 8255 DIO implementation
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Ian Abbott authored and Greg Kroah-Hartman committed Oct 24, 2012
1 parent 155857c commit 57054dc
Showing 1 changed file with 143 additions and 4 deletions.
147 changes: 143 additions & 4 deletions drivers/staging/comedi/drivers/amplc_dio200.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,6 @@
#include "../comedidev.h"

#include "comedi_fc.h"
#include "8255.h"
#include "8253.h"

#define DIO200_DRIVER_NAME "amplc_dio200"
Expand All @@ -221,6 +220,15 @@
#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 */
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 57054dc

Please sign in to comment.