linux/drivers/staging/comedi/drivers/pcl730.c
<<
>>
Prefs
   1/*
   2 * comedi/drivers/pcl730.c
   3 * Driver for Advantech PCL-730 and clones
   4 * José Luis Sánchez
   5 */
   6
   7/*
   8 * Driver: pcl730
   9 * Description: Advantech PCL-730 (& compatibles)
  10 * Devices: [Advantech] PCL-730 (pcl730), PCM-3730 (pcm3730), PCL-725 (pcl725),
  11 *   PCL-733 (pcl733), PCL-734 (pcl734),
  12 *   [ADLink] ACL-7130 (acl7130), ACL-7225b (acl7225b),
  13 *   [ICP] ISO-730 (iso730), P8R8-DIO (p8r8dio), P16R16-DIO (p16r16dio),
  14 *   [Diamond Systems] OPMM-1616-XT (opmm-1616-xt), PEARL-MM-P (pearl-mm-p),
  15 *   IR104-PBF (ir104-pbf),
  16 * Author: José Luis Sánchez (jsanchezv@teleline.es)
  17 * Status: untested
  18 *
  19 * Configuration options:
  20 *   [0] - I/O port base
  21 *
  22 * Interrupts are not supported.
  23 * The ACL-7130 card has an 8254 timer/counter not supported by this driver.
  24 */
  25
  26#include <linux/module.h>
  27#include "../comedidev.h"
  28
  29/*
  30 * Register map
  31 *
  32 * The register map varies slightly depending on the board type but
  33 * all registers are 8-bit.
  34 *
  35 * The boardinfo 'io_range' is used to allow comedi to request the
  36 * proper range required by the board.
  37 *
  38 * The comedi_subdevice 'private' data is used to pass the register
  39 * offset to the (*insn_bits) functions to read/write the correct
  40 * registers.
  41 *
  42 * The basic register mapping looks like this:
  43 *
  44 *     BASE+0  Isolated outputs 0-7 (write) / inputs 0-7 (read)
  45 *     BASE+1  Isolated outputs 8-15 (write) / inputs 8-15 (read)
  46 *     BASE+2  TTL outputs 0-7 (write) / inputs 0-7 (read)
  47 *     BASE+3  TTL outputs 8-15 (write) / inputs 8-15 (read)
  48 *
  49 * The pcm3730 board does not have register BASE+1.
  50 *
  51 * The pcl725 and p8r8dio only have registers BASE+0 and BASE+1:
  52 *
  53 *     BASE+0  Isolated outputs 0-7 (write) (read back on p8r8dio)
  54 *     BASE+1  Isolated inputs 0-7 (read)
  55 *
  56 * The acl7225b and p16r16dio boards have this register mapping:
  57 *
  58 *     BASE+0  Isolated outputs 0-7 (write) (read back)
  59 *     BASE+1  Isolated outputs 8-15 (write) (read back)
  60 *     BASE+2  Isolated inputs 0-7 (read)
  61 *     BASE+3  Isolated inputs 8-15 (read)
  62 *
  63 * The pcl733 and pcl733 boards have this register mapping:
  64 *
  65 *     BASE+0  Isolated outputs 0-7 (write) or inputs 0-7 (read)
  66 *     BASE+1  Isolated outputs 8-15 (write) or inputs 8-15 (read)
  67 *     BASE+2  Isolated outputs 16-23 (write) or inputs 16-23 (read)
  68 *     BASE+3  Isolated outputs 24-31 (write) or inputs 24-31 (read)
  69 *
  70 * The opmm-1616-xt board has this register mapping:
  71 *
  72 *     BASE+0  Isolated outputs 0-7 (write) (read back)
  73 *     BASE+1  Isolated outputs 8-15 (write) (read back)
  74 *     BASE+2  Isolated inputs 0-7 (read)
  75 *     BASE+3  Isolated inputs 8-15 (read)
  76 *
  77 *     These registers are not currently supported:
  78 *
  79 *     BASE+2  Relay select register (write)
  80 *     BASE+3  Board reset control register (write)
  81 *     BASE+4  Interrupt control register (write)
  82 *     BASE+4  Change detect 7-0 status register (read)
  83 *     BASE+5  LED control register (write)
  84 *     BASE+5  Change detect 15-8 status register (read)
  85 *
  86 * The pearl-mm-p board has this register mapping:
  87 *
  88 *     BASE+0  Isolated outputs 0-7 (write)
  89 *     BASE+1  Isolated outputs 8-15 (write)
  90 *
  91 * The ir104-pbf board has this register mapping:
  92 *
  93 *     BASE+0  Isolated outputs 0-7 (write) (read back)
  94 *     BASE+1  Isolated outputs 8-15 (write) (read back)
  95 *     BASE+2  Isolated outputs 16-19 (write) (read back)
  96 *     BASE+4  Isolated inputs 0-7 (read)
  97 *     BASE+5  Isolated inputs 8-15 (read)
  98 *     BASE+6  Isolated inputs 16-19 (read)
  99 */
 100
 101struct pcl730_board {
 102        const char *name;
 103        unsigned int io_range;
 104        unsigned is_pcl725:1;
 105        unsigned is_acl7225b:1;
 106        unsigned is_ir104:1;
 107        unsigned has_readback:1;
 108        unsigned has_ttl_io:1;
 109        int n_subdevs;
 110        int n_iso_out_chan;
 111        int n_iso_in_chan;
 112        int n_ttl_chan;
 113};
 114
 115static const struct pcl730_board pcl730_boards[] = {
 116        {
 117                .name           = "pcl730",
 118                .io_range       = 0x04,
 119                .has_ttl_io     = 1,
 120                .n_subdevs      = 4,
 121                .n_iso_out_chan = 16,
 122                .n_iso_in_chan  = 16,
 123                .n_ttl_chan     = 16,
 124        }, {
 125                .name           = "iso730",
 126                .io_range       = 0x04,
 127                .n_subdevs      = 4,
 128                .n_iso_out_chan = 16,
 129                .n_iso_in_chan  = 16,
 130                .n_ttl_chan     = 16,
 131        }, {
 132                .name           = "acl7130",
 133                .io_range       = 0x08,
 134                .has_ttl_io     = 1,
 135                .n_subdevs      = 4,
 136                .n_iso_out_chan = 16,
 137                .n_iso_in_chan  = 16,
 138                .n_ttl_chan     = 16,
 139        }, {
 140                .name           = "pcm3730",
 141                .io_range       = 0x04,
 142                .has_ttl_io     = 1,
 143                .n_subdevs      = 4,
 144                .n_iso_out_chan = 8,
 145                .n_iso_in_chan  = 8,
 146                .n_ttl_chan     = 16,
 147        }, {
 148                .name           = "pcl725",
 149                .io_range       = 0x02,
 150                .is_pcl725      = 1,
 151                .n_subdevs      = 2,
 152                .n_iso_out_chan = 8,
 153                .n_iso_in_chan  = 8,
 154        }, {
 155                .name           = "p8r8dio",
 156                .io_range       = 0x02,
 157                .is_pcl725      = 1,
 158                .has_readback   = 1,
 159                .n_subdevs      = 2,
 160                .n_iso_out_chan = 8,
 161                .n_iso_in_chan  = 8,
 162        }, {
 163                .name           = "acl7225b",
 164                .io_range       = 0x08,         /* only 4 are used */
 165                .is_acl7225b    = 1,
 166                .has_readback   = 1,
 167                .n_subdevs      = 2,
 168                .n_iso_out_chan = 16,
 169                .n_iso_in_chan  = 16,
 170        }, {
 171                .name           = "p16r16dio",
 172                .io_range       = 0x04,
 173                .is_acl7225b    = 1,
 174                .has_readback   = 1,
 175                .n_subdevs      = 2,
 176                .n_iso_out_chan = 16,
 177                .n_iso_in_chan  = 16,
 178        }, {
 179                .name           = "pcl733",
 180                .io_range       = 0x04,
 181                .n_subdevs      = 1,
 182                .n_iso_in_chan  = 32,
 183        }, {
 184                .name           = "pcl734",
 185                .io_range       = 0x04,
 186                .n_subdevs      = 1,
 187                .n_iso_out_chan = 32,
 188        }, {
 189                .name           = "opmm-1616-xt",
 190                .io_range       = 0x10,
 191                .is_acl7225b    = 1,
 192                .has_readback   = 1,
 193                .n_subdevs      = 2,
 194                .n_iso_out_chan = 16,
 195                .n_iso_in_chan  = 16,
 196        }, {
 197                .name           = "pearl-mm-p",
 198                .io_range       = 0x02,
 199                .n_subdevs      = 1,
 200                .n_iso_out_chan = 16,
 201        }, {
 202                .name           = "ir104-pbf",
 203                .io_range       = 0x08,
 204                .is_ir104       = 1,
 205                .has_readback   = 1,
 206                .n_iso_out_chan = 20,
 207                .n_iso_in_chan  = 20,
 208        },
 209};
 210
 211static int pcl730_do_insn_bits(struct comedi_device *dev,
 212                               struct comedi_subdevice *s,
 213                               struct comedi_insn *insn,
 214                               unsigned int *data)
 215{
 216        unsigned long reg = (unsigned long)s->private;
 217        unsigned int mask;
 218
 219        mask = comedi_dio_update_state(s, data);
 220        if (mask) {
 221                if (mask & 0x00ff)
 222                        outb(s->state & 0xff, dev->iobase + reg);
 223                if ((mask & 0xff00) && (s->n_chan > 8))
 224                        outb((s->state >> 8) & 0xff, dev->iobase + reg + 1);
 225                if ((mask & 0xff0000) && (s->n_chan > 16))
 226                        outb((s->state >> 16) & 0xff, dev->iobase + reg + 2);
 227                if ((mask & 0xff000000) && (s->n_chan > 24))
 228                        outb((s->state >> 24) & 0xff, dev->iobase + reg + 3);
 229        }
 230
 231        data[1] = s->state;
 232
 233        return insn->n;
 234}
 235
 236static unsigned int pcl730_get_bits(struct comedi_device *dev,
 237                                    struct comedi_subdevice *s)
 238{
 239        unsigned long reg = (unsigned long)s->private;
 240        unsigned int val;
 241
 242        val = inb(dev->iobase + reg);
 243        if (s->n_chan > 8)
 244                val |= (inb(dev->iobase + reg + 1) << 8);
 245        if (s->n_chan > 16)
 246                val |= (inb(dev->iobase + reg + 2) << 16);
 247        if (s->n_chan > 24)
 248                val |= (inb(dev->iobase + reg + 3) << 24);
 249
 250        return val;
 251}
 252
 253static int pcl730_di_insn_bits(struct comedi_device *dev,
 254                               struct comedi_subdevice *s,
 255                               struct comedi_insn *insn,
 256                               unsigned int *data)
 257{
 258        data[1] = pcl730_get_bits(dev, s);
 259
 260        return insn->n;
 261}
 262
 263static int pcl730_attach(struct comedi_device *dev,
 264                         struct comedi_devconfig *it)
 265{
 266        const struct pcl730_board *board = dev->board_ptr;
 267        struct comedi_subdevice *s;
 268        int subdev;
 269        int ret;
 270
 271        ret = comedi_request_region(dev, it->options[0], board->io_range);
 272        if (ret)
 273                return ret;
 274
 275        ret = comedi_alloc_subdevices(dev, board->n_subdevs);
 276        if (ret)
 277                return ret;
 278
 279        subdev = 0;
 280
 281        if (board->n_iso_out_chan) {
 282                /* Isolated Digital Outputs */
 283                s = &dev->subdevices[subdev++];
 284                s->type         = COMEDI_SUBD_DO;
 285                s->subdev_flags = SDF_WRITABLE;
 286                s->n_chan       = board->n_iso_out_chan;
 287                s->maxdata      = 1;
 288                s->range_table  = &range_digital;
 289                s->insn_bits    = pcl730_do_insn_bits;
 290                s->private      = (void *)0;
 291
 292                /* get the initial state if supported */
 293                if (board->has_readback)
 294                        s->state = pcl730_get_bits(dev, s);
 295        }
 296
 297        if (board->n_iso_in_chan) {
 298                /* Isolated Digital Inputs */
 299                s = &dev->subdevices[subdev++];
 300                s->type         = COMEDI_SUBD_DI;
 301                s->subdev_flags = SDF_READABLE;
 302                s->n_chan       = board->n_iso_in_chan;
 303                s->maxdata      = 1;
 304                s->range_table  = &range_digital;
 305                s->insn_bits    = pcl730_di_insn_bits;
 306                s->private      = board->is_ir104 ? (void *)4 :
 307                                  board->is_acl7225b ? (void *)2 :
 308                                  board->is_pcl725 ? (void *)1 : (void *)0;
 309        }
 310
 311        if (board->has_ttl_io) {
 312                /* TTL Digital Outputs */
 313                s = &dev->subdevices[subdev++];
 314                s->type         = COMEDI_SUBD_DO;
 315                s->subdev_flags = SDF_WRITABLE;
 316                s->n_chan       = board->n_ttl_chan;
 317                s->maxdata      = 1;
 318                s->range_table  = &range_digital;
 319                s->insn_bits    = pcl730_do_insn_bits;
 320                s->private      = (void *)2;
 321
 322                /* TTL Digital Inputs */
 323                s = &dev->subdevices[subdev++];
 324                s->type         = COMEDI_SUBD_DI;
 325                s->subdev_flags = SDF_READABLE;
 326                s->n_chan       = board->n_ttl_chan;
 327                s->maxdata      = 1;
 328                s->range_table  = &range_digital;
 329                s->insn_bits    = pcl730_di_insn_bits;
 330                s->private      = (void *)2;
 331        }
 332
 333        return 0;
 334}
 335
 336static struct comedi_driver pcl730_driver = {
 337        .driver_name    = "pcl730",
 338        .module         = THIS_MODULE,
 339        .attach         = pcl730_attach,
 340        .detach         = comedi_legacy_detach,
 341        .board_name     = &pcl730_boards[0].name,
 342        .num_names      = ARRAY_SIZE(pcl730_boards),
 343        .offset         = sizeof(struct pcl730_board),
 344};
 345module_comedi_driver(pcl730_driver);
 346
 347MODULE_AUTHOR("Comedi http://www.comedi.org");
 348MODULE_DESCRIPTION("Comedi low-level driver");
 349MODULE_LICENSE("GPL");
 350