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