linux/drivers/staging/comedi/drivers/pcm3724.c
<<
>>
Prefs
   1/*
   2    comedi/drivers/pcm724.c
   3
   4    Drew Csillag <drew_csillag@yahoo.com>
   5
   6    hardware driver for Advantech card:
   7     card:   PCM-3724
   8     driver: pcm3724
   9
  10    Options for PCM-3724
  11     [0] - IO Base
  12*/
  13/*
  14Driver: pcm3724
  15Description: Advantech PCM-3724
  16Author: Drew Csillag <drew_csillag@yahoo.com>
  17Devices: [Advantech] PCM-3724 (pcm724)
  18Status: tested
  19
  20This is driver for digital I/O boards PCM-3724 with 48 DIO.
  21It needs 8255.o for operations and only immediate mode is supported.
  22See the source for configuration details.
  23
  24Copy/pasted/hacked from pcm724.c
  25*/
  26/*
  27 * check_driver overrides:
  28 *   struct comedi_insn
  29 */
  30
  31#include <linux/module.h>
  32#include "../comedidev.h"
  33
  34#include "8255.h"
  35
  36#define PCM3724_SIZE   16
  37#define SIZE_8255       4
  38
  39#define BUF_C0 0x1
  40#define BUF_B0 0x2
  41#define BUF_A0 0x4
  42#define BUF_C1 0x8
  43#define BUF_B1 0x10
  44#define BUF_A1 0x20
  45
  46#define GATE_A0 0x4
  47#define GATE_B0 0x2
  48#define GATE_C0 0x1
  49#define GATE_A1 0x20
  50#define GATE_B1 0x10
  51#define GATE_C1 0x8
  52
  53/* from 8255.c */
  54#define CR_CW           0x80
  55#define _8255_CR 3
  56#define CR_B_IO         0x02
  57#define CR_B_MODE       0x04
  58#define CR_C_IO         0x09
  59#define CR_A_IO         0x10
  60#define CR_A_MODE(a)    ((a)<<5)
  61#define CR_CW           0x80
  62
  63/* used to track configured dios */
  64struct priv_pcm3724 {
  65        int dio_1;
  66        int dio_2;
  67};
  68
  69static int subdev_8255_cb(int dir, int port, int data, unsigned long arg)
  70{
  71        unsigned long iobase = arg;
  72        unsigned char inbres;
  73        if (dir) {
  74                outb(data, iobase + port);
  75                return 0;
  76        } else {
  77                inbres = inb(iobase + port);
  78                return inbres;
  79        }
  80}
  81
  82static int compute_buffer(int config, int devno, struct comedi_subdevice *s)
  83{
  84        /* 1 in io_bits indicates output */
  85        if (s->io_bits & 0x0000ff) {
  86                if (devno == 0)
  87                        config |= BUF_A0;
  88                else
  89                        config |= BUF_A1;
  90        }
  91        if (s->io_bits & 0x00ff00) {
  92                if (devno == 0)
  93                        config |= BUF_B0;
  94                else
  95                        config |= BUF_B1;
  96        }
  97        if (s->io_bits & 0xff0000) {
  98                if (devno == 0)
  99                        config |= BUF_C0;
 100                else
 101                        config |= BUF_C1;
 102        }
 103        return config;
 104}
 105
 106static void do_3724_config(struct comedi_device *dev,
 107                           struct comedi_subdevice *s, int chanspec)
 108{
 109        struct comedi_subdevice *s_dio1 = &dev->subdevices[0];
 110        struct comedi_subdevice *s_dio2 = &dev->subdevices[1];
 111        int config;
 112        int buffer_config;
 113        unsigned long port_8255_cfg;
 114
 115        config = CR_CW;
 116        buffer_config = 0;
 117
 118        /* 1 in io_bits indicates output, 1 in config indicates input */
 119        if (!(s->io_bits & 0x0000ff))
 120                config |= CR_A_IO;
 121
 122        if (!(s->io_bits & 0x00ff00))
 123                config |= CR_B_IO;
 124
 125        if (!(s->io_bits & 0xff0000))
 126                config |= CR_C_IO;
 127
 128        buffer_config = compute_buffer(0, 0, s_dio1);
 129        buffer_config = compute_buffer(buffer_config, 1, s_dio2);
 130
 131        if (s == s_dio1)
 132                port_8255_cfg = dev->iobase + _8255_CR;
 133        else
 134                port_8255_cfg = dev->iobase + SIZE_8255 + _8255_CR;
 135
 136        outb(buffer_config, dev->iobase + 8);   /* update buffer register */
 137
 138        outb(config, port_8255_cfg);
 139}
 140
 141static void enable_chan(struct comedi_device *dev, struct comedi_subdevice *s,
 142                        int chanspec)
 143{
 144        struct priv_pcm3724 *priv = dev->private;
 145        struct comedi_subdevice *s_dio1 = &dev->subdevices[0];
 146        unsigned int mask;
 147        int gatecfg;
 148
 149        gatecfg = 0;
 150
 151        mask = 1 << CR_CHAN(chanspec);
 152        if (s == s_dio1)
 153                priv->dio_1 |= mask;
 154        else
 155                priv->dio_2 |= mask;
 156
 157        if (priv->dio_1 & 0xff0000)
 158                gatecfg |= GATE_C0;
 159
 160        if (priv->dio_1 & 0xff00)
 161                gatecfg |= GATE_B0;
 162
 163        if (priv->dio_1 & 0xff)
 164                gatecfg |= GATE_A0;
 165
 166        if (priv->dio_2 & 0xff0000)
 167                gatecfg |= GATE_C1;
 168
 169        if (priv->dio_2 & 0xff00)
 170                gatecfg |= GATE_B1;
 171
 172        if (priv->dio_2 & 0xff)
 173                gatecfg |= GATE_A1;
 174
 175        outb(gatecfg, dev->iobase + 9);
 176}
 177
 178/* overriding the 8255 insn config */
 179static int subdev_3724_insn_config(struct comedi_device *dev,
 180                                   struct comedi_subdevice *s,
 181                                   struct comedi_insn *insn,
 182                                   unsigned int *data)
 183{
 184        unsigned int chan = CR_CHAN(insn->chanspec);
 185        unsigned int mask;
 186        int ret;
 187
 188        if (chan < 8)
 189                mask = 0x0000ff;
 190        else if (chan < 16)
 191                mask = 0x00ff00;
 192        else if (chan < 20)
 193                mask = 0x0f0000;
 194        else
 195                mask = 0xf00000;
 196
 197        ret = comedi_dio_insn_config(dev, s, insn, data, mask);
 198        if (ret)
 199                return ret;
 200
 201        do_3724_config(dev, s, insn->chanspec);
 202        enable_chan(dev, s, insn->chanspec);
 203
 204        return insn->n;
 205}
 206
 207static int pcm3724_attach(struct comedi_device *dev,
 208                          struct comedi_devconfig *it)
 209{
 210        struct priv_pcm3724 *priv;
 211        struct comedi_subdevice *s;
 212        int ret, i;
 213
 214        priv = comedi_alloc_devpriv(dev, sizeof(*priv));
 215        if (!priv)
 216                return -ENOMEM;
 217
 218        ret = comedi_request_region(dev, it->options[0], PCM3724_SIZE);
 219        if (ret)
 220                return ret;
 221
 222        ret = comedi_alloc_subdevices(dev, 2);
 223        if (ret)
 224                return ret;
 225
 226        for (i = 0; i < dev->n_subdevices; i++) {
 227                s = &dev->subdevices[i];
 228                subdev_8255_init(dev, s, subdev_8255_cb,
 229                                 (unsigned long)(dev->iobase + SIZE_8255 * i));
 230                s->insn_config = subdev_3724_insn_config;
 231        }
 232        return 0;
 233}
 234
 235static struct comedi_driver pcm3724_driver = {
 236        .driver_name    = "pcm3724",
 237        .module         = THIS_MODULE,
 238        .attach         = pcm3724_attach,
 239        .detach         = comedi_legacy_detach,
 240};
 241module_comedi_driver(pcm3724_driver);
 242
 243MODULE_AUTHOR("Comedi http://www.comedi.org");
 244MODULE_DESCRIPTION("Comedi low-level driver");
 245MODULE_LICENSE("GPL");
 246