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 "../comedidev.h"
  32
  33#include <linux/ioport.h>
  34#include <linux/delay.h>
  35
  36#include "8255.h"
  37
  38#define PCM3724_SIZE   16
  39#define SIZE_8255       4
  40
  41#define BUF_C0 0x1
  42#define BUF_B0 0x2
  43#define BUF_A0 0x4
  44#define BUF_C1 0x8
  45#define BUF_B1 0x10
  46#define BUF_A1 0x20
  47
  48#define GATE_A0 0x4
  49#define GATE_B0 0x2
  50#define GATE_C0 0x1
  51#define GATE_A1 0x20
  52#define GATE_B1 0x10
  53#define GATE_C1 0x8
  54
  55/* from 8255.c */
  56#define CR_CW           0x80
  57#define _8255_CR 3
  58#define CR_B_IO         0x02
  59#define CR_B_MODE       0x04
  60#define CR_C_IO         0x09
  61#define CR_A_IO         0x10
  62#define CR_A_MODE(a)    ((a)<<5)
  63#define CR_CW           0x80
  64
  65/* used to track configured dios */
  66struct priv_pcm3724 {
  67        int dio_1;
  68        int dio_2;
  69};
  70
  71static int subdev_8255_cb(int dir, int port, int data, unsigned long arg)
  72{
  73        unsigned long iobase = arg;
  74        unsigned char inbres;
  75        /* printk("8255cb %d %d %d %lx\n", dir,port,data,arg); */
  76        if (dir) {
  77                /* printk("8255 cb   outb(%x, %lx)\n", data, iobase+port); */
  78                outb(data, iobase + port);
  79                return 0;
  80        } else {
  81                inbres = inb(iobase + port);
  82                /* printk("8255 cb   inb(%lx) = %x\n", iobase+port, inbres); */
  83                return inbres;
  84        }
  85}
  86
  87static int compute_buffer(int config, int devno, struct comedi_subdevice *s)
  88{
  89        /* 1 in io_bits indicates output */
  90        if (s->io_bits & 0x0000ff) {
  91                if (devno == 0)
  92                        config |= BUF_A0;
  93                else
  94                        config |= BUF_A1;
  95        }
  96        if (s->io_bits & 0x00ff00) {
  97                if (devno == 0)
  98                        config |= BUF_B0;
  99                else
 100                        config |= BUF_B1;
 101        }
 102        if (s->io_bits & 0xff0000) {
 103                if (devno == 0)
 104                        config |= BUF_C0;
 105                else
 106                        config |= BUF_C1;
 107        }
 108        return config;
 109}
 110
 111static void do_3724_config(struct comedi_device *dev,
 112                           struct comedi_subdevice *s, int chanspec)
 113{
 114        struct comedi_subdevice *s_dio1 = &dev->subdevices[0];
 115        struct comedi_subdevice *s_dio2 = &dev->subdevices[1];
 116        int config;
 117        int buffer_config;
 118        unsigned long port_8255_cfg;
 119
 120        config = CR_CW;
 121        buffer_config = 0;
 122
 123        /* 1 in io_bits indicates output, 1 in config indicates input */
 124        if (!(s->io_bits & 0x0000ff))
 125                config |= CR_A_IO;
 126
 127        if (!(s->io_bits & 0x00ff00))
 128                config |= CR_B_IO;
 129
 130        if (!(s->io_bits & 0xff0000))
 131                config |= CR_C_IO;
 132
 133        buffer_config = compute_buffer(0, 0, s_dio1);
 134        buffer_config = compute_buffer(buffer_config, 1, s_dio2);
 135
 136        if (s == s_dio1)
 137                port_8255_cfg = dev->iobase + _8255_CR;
 138        else
 139                port_8255_cfg = dev->iobase + SIZE_8255 + _8255_CR;
 140
 141        outb(buffer_config, dev->iobase + 8);   /* update buffer register */
 142        /* printk("pcm3724 buffer_config (%lx) %d, %x\n",
 143               dev->iobase + _8255_CR, chanspec, buffer_config); */
 144
 145        outb(config, port_8255_cfg);
 146}
 147
 148static void enable_chan(struct comedi_device *dev, struct comedi_subdevice *s,
 149                        int chanspec)
 150{
 151        struct priv_pcm3724 *priv = dev->private;
 152        struct comedi_subdevice *s_dio1 = &dev->subdevices[0];
 153        unsigned int mask;
 154        int gatecfg;
 155
 156        gatecfg = 0;
 157
 158        mask = 1 << CR_CHAN(chanspec);
 159        if (s == s_dio1)
 160                priv->dio_1 |= mask;
 161        else
 162                priv->dio_2 |= mask;
 163
 164        if (priv->dio_1 & 0xff0000)
 165                gatecfg |= GATE_C0;
 166
 167        if (priv->dio_1 & 0xff00)
 168                gatecfg |= GATE_B0;
 169
 170        if (priv->dio_1 & 0xff)
 171                gatecfg |= GATE_A0;
 172
 173        if (priv->dio_2 & 0xff0000)
 174                gatecfg |= GATE_C1;
 175
 176        if (priv->dio_2 & 0xff00)
 177                gatecfg |= GATE_B1;
 178
 179        if (priv->dio_2 & 0xff)
 180                gatecfg |= GATE_A1;
 181
 182        /*       printk("gate control %x\n", gatecfg); */
 183        outb(gatecfg, dev->iobase + 9);
 184}
 185
 186/* overriding the 8255 insn config */
 187static int subdev_3724_insn_config(struct comedi_device *dev,
 188                                   struct comedi_subdevice *s,
 189                                   struct comedi_insn *insn, unsigned int *data)
 190{
 191        unsigned int mask;
 192        unsigned int bits;
 193
 194        mask = 1 << CR_CHAN(insn->chanspec);
 195        if (mask & 0x0000ff)
 196                bits = 0x0000ff;
 197        else if (mask & 0x00ff00)
 198                bits = 0x00ff00;
 199        else if (mask & 0x0f0000)
 200                bits = 0x0f0000;
 201        else
 202                bits = 0xf00000;
 203
 204        switch (data[0]) {
 205        case INSN_CONFIG_DIO_INPUT:
 206                s->io_bits &= ~bits;
 207                break;
 208        case INSN_CONFIG_DIO_OUTPUT:
 209                s->io_bits |= bits;
 210                break;
 211        case INSN_CONFIG_DIO_QUERY:
 212                data[1] = (s->io_bits & bits) ? COMEDI_OUTPUT : COMEDI_INPUT;
 213                return insn->n;
 214                break;
 215        default:
 216                return -EINVAL;
 217        }
 218
 219        do_3724_config(dev, s, insn->chanspec);
 220        enable_chan(dev, s, insn->chanspec);
 221        return 1;
 222}
 223
 224static int pcm3724_attach(struct comedi_device *dev,
 225                          struct comedi_devconfig *it)
 226{
 227        struct priv_pcm3724 *priv;
 228        struct comedi_subdevice *s;
 229        int ret, i;
 230
 231        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 232        if (!priv)
 233                return -ENOMEM;
 234        dev->private = priv;
 235
 236        ret = comedi_request_region(dev, it->options[0], PCM3724_SIZE);
 237        if (ret)
 238                return ret;
 239
 240        ret = comedi_alloc_subdevices(dev, 2);
 241        if (ret)
 242                return ret;
 243
 244        for (i = 0; i < dev->n_subdevices; i++) {
 245                s = &dev->subdevices[i];
 246                subdev_8255_init(dev, s, subdev_8255_cb,
 247                                 (unsigned long)(dev->iobase + SIZE_8255 * i));
 248                s->insn_config = subdev_3724_insn_config;
 249        }
 250        return 0;
 251}
 252
 253static void pcm3724_detach(struct comedi_device *dev)
 254{
 255        int i;
 256
 257        for (i = 0; i < dev->n_subdevices; i++)
 258                comedi_spriv_free(dev, i);
 259        comedi_legacy_detach(dev);
 260}
 261
 262static struct comedi_driver pcm3724_driver = {
 263        .driver_name    = "pcm3724",
 264        .module         = THIS_MODULE,
 265        .attach         = pcm3724_attach,
 266        .detach         = pcm3724_detach,
 267};
 268module_comedi_driver(pcm3724_driver);
 269
 270MODULE_AUTHOR("Comedi http://www.comedi.org");
 271MODULE_DESCRIPTION("Comedi low-level driver");
 272MODULE_LICENSE("GPL");
 273