root/drivers/staging/comedi/drivers/ii_pci20kc.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ii20k_module_iobase
  2. ii20k_ao_insn_write
  3. ii20k_ai_eoc
  4. ii20k_ai_setup
  5. ii20k_ai_insn_read
  6. ii20k_dio_config
  7. ii20k_dio_insn_config
  8. ii20k_dio_insn_bits
  9. ii20k_init_module
  10. ii20k_attach
  11. ii20k_detach

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * ii_pci20kc.c
   4  * Driver for Intelligent Instruments PCI-20001C carrier board and modules.
   5  *
   6  * Copyright (C) 2000 Markus Kempf <kempf@matsci.uni-sb.de>
   7  * with suggestions from David Schleef          16.06.2000
   8  */
   9 
  10 /*
  11  * Driver: ii_pci20kc
  12  * Description: Intelligent Instruments PCI-20001C carrier board
  13  * Devices: [Intelligent Instrumentation] PCI-20001C (ii_pci20kc)
  14  * Author: Markus Kempf <kempf@matsci.uni-sb.de>
  15  * Status: works
  16  *
  17  * Supports the PCI-20001C-1a and PCI-20001C-2a carrier boards. The
  18  * -2a version has 32 on-board DIO channels. Three add-on modules
  19  * can be added to the carrier board for additional functionality.
  20  *
  21  * Supported add-on modules:
  22  *      PCI-20006M-1   1 channel, 16-bit analog output module
  23  *      PCI-20006M-2   2 channel, 16-bit analog output module
  24  *      PCI-20341M-1A  4 channel, 16-bit analog input module
  25  *
  26  * Options:
  27  *   0   Board base address
  28  *   1   IRQ (not-used)
  29  */
  30 
  31 #include <linux/module.h>
  32 #include <linux/io.h>
  33 #include "../comedidev.h"
  34 
  35 /*
  36  * Register I/O map
  37  */
  38 #define II20K_SIZE                      0x400
  39 #define II20K_MOD_OFFSET                0x100
  40 #define II20K_ID_REG                    0x00
  41 #define II20K_ID_MOD1_EMPTY             BIT(7)
  42 #define II20K_ID_MOD2_EMPTY             BIT(6)
  43 #define II20K_ID_MOD3_EMPTY             BIT(5)
  44 #define II20K_ID_MASK                   0x1f
  45 #define II20K_ID_PCI20001C_1A           0x1b    /* no on-board DIO */
  46 #define II20K_ID_PCI20001C_2A           0x1d    /* on-board DIO */
  47 #define II20K_MOD_STATUS_REG            0x40
  48 #define II20K_MOD_STATUS_IRQ_MOD1       BIT(7)
  49 #define II20K_MOD_STATUS_IRQ_MOD2       BIT(6)
  50 #define II20K_MOD_STATUS_IRQ_MOD3       BIT(5)
  51 #define II20K_DIO0_REG                  0x80
  52 #define II20K_DIO1_REG                  0x81
  53 #define II20K_DIR_ENA_REG               0x82
  54 #define II20K_DIR_DIO3_OUT              BIT(7)
  55 #define II20K_DIR_DIO2_OUT              BIT(6)
  56 #define II20K_BUF_DISAB_DIO3            BIT(5)
  57 #define II20K_BUF_DISAB_DIO2            BIT(4)
  58 #define II20K_DIR_DIO1_OUT              BIT(3)
  59 #define II20K_DIR_DIO0_OUT              BIT(2)
  60 #define II20K_BUF_DISAB_DIO1            BIT(1)
  61 #define II20K_BUF_DISAB_DIO0            BIT(0)
  62 #define II20K_CTRL01_REG                0x83
  63 #define II20K_CTRL01_SET                BIT(7)
  64 #define II20K_CTRL01_DIO0_IN            BIT(4)
  65 #define II20K_CTRL01_DIO1_IN            BIT(1)
  66 #define II20K_DIO2_REG                  0xc0
  67 #define II20K_DIO3_REG                  0xc1
  68 #define II20K_CTRL23_REG                0xc3
  69 #define II20K_CTRL23_SET                BIT(7)
  70 #define II20K_CTRL23_DIO2_IN            BIT(4)
  71 #define II20K_CTRL23_DIO3_IN            BIT(1)
  72 
  73 #define II20K_ID_PCI20006M_1            0xe2    /* 1 AO channels */
  74 #define II20K_ID_PCI20006M_2            0xe3    /* 2 AO channels */
  75 #define II20K_AO_STRB_REG(x)            (0x0b + ((x) * 0x08))
  76 #define II20K_AO_LSB_REG(x)             (0x0d + ((x) * 0x08))
  77 #define II20K_AO_MSB_REG(x)             (0x0e + ((x) * 0x08))
  78 #define II20K_AO_STRB_BOTH_REG          0x1b
  79 
  80 #define II20K_ID_PCI20341M_1            0x77    /* 4 AI channels */
  81 #define II20K_AI_STATUS_CMD_REG         0x01
  82 #define II20K_AI_STATUS_CMD_BUSY        BIT(7)
  83 #define II20K_AI_STATUS_CMD_HW_ENA      BIT(1)
  84 #define II20K_AI_STATUS_CMD_EXT_START   BIT(0)
  85 #define II20K_AI_LSB_REG                0x02
  86 #define II20K_AI_MSB_REG                0x03
  87 #define II20K_AI_PACER_RESET_REG        0x04
  88 #define II20K_AI_16BIT_DATA_REG         0x06
  89 #define II20K_AI_CONF_REG               0x10
  90 #define II20K_AI_CONF_ENA               BIT(2)
  91 #define II20K_AI_OPT_REG                0x11
  92 #define II20K_AI_OPT_TRIG_ENA           BIT(5)
  93 #define II20K_AI_OPT_TRIG_INV           BIT(4)
  94 #define II20K_AI_OPT_TIMEBASE(x)        (((x) & 0x3) << 1)
  95 #define II20K_AI_OPT_BURST_MODE         BIT(0)
  96 #define II20K_AI_STATUS_REG             0x12
  97 #define II20K_AI_STATUS_INT             BIT(7)
  98 #define II20K_AI_STATUS_TRIG            BIT(6)
  99 #define II20K_AI_STATUS_TRIG_ENA        BIT(5)
 100 #define II20K_AI_STATUS_PACER_ERR       BIT(2)
 101 #define II20K_AI_STATUS_DATA_ERR        BIT(1)
 102 #define II20K_AI_STATUS_SET_TIME_ERR    BIT(0)
 103 #define II20K_AI_LAST_CHAN_ADDR_REG     0x13
 104 #define II20K_AI_CUR_ADDR_REG           0x14
 105 #define II20K_AI_SET_TIME_REG           0x15
 106 #define II20K_AI_DELAY_LSB_REG          0x16
 107 #define II20K_AI_DELAY_MSB_REG          0x17
 108 #define II20K_AI_CHAN_ADV_REG           0x18
 109 #define II20K_AI_CHAN_RESET_REG         0x19
 110 #define II20K_AI_START_TRIG_REG         0x1a
 111 #define II20K_AI_COUNT_RESET_REG        0x1b
 112 #define II20K_AI_CHANLIST_REG           0x80
 113 #define II20K_AI_CHANLIST_ONBOARD_ONLY  BIT(5)
 114 #define II20K_AI_CHANLIST_GAIN(x)       (((x) & 0x3) << 3)
 115 #define II20K_AI_CHANLIST_MUX_ENA       BIT(2)
 116 #define II20K_AI_CHANLIST_CHAN(x)       (((x) & 0x3) << 0)
 117 #define II20K_AI_CHANLIST_LEN           0x80
 118 
 119 /* the AO range is set by jumpers on the 20006M module */
 120 static const struct comedi_lrange ii20k_ao_ranges = {
 121         3, {
 122                 BIP_RANGE(5),   /* Chan 0 - W1/W3 in   Chan 1 - W2/W4 in  */
 123                 UNI_RANGE(10),  /* Chan 0 - W1/W3 out  Chan 1 - W2/W4 in  */
 124                 BIP_RANGE(10)   /* Chan 0 - W1/W3 in   Chan 1 - W2/W4 out */
 125         }
 126 };
 127 
 128 static const struct comedi_lrange ii20k_ai_ranges = {
 129         4, {
 130                 BIP_RANGE(5),           /* gain 1 */
 131                 BIP_RANGE(0.5),         /* gain 10 */
 132                 BIP_RANGE(0.05),        /* gain 100 */
 133                 BIP_RANGE(0.025)        /* gain 200 */
 134         },
 135 };
 136 
 137 static void __iomem *ii20k_module_iobase(struct comedi_device *dev,
 138                                          struct comedi_subdevice *s)
 139 {
 140         return dev->mmio + (s->index + 1) * II20K_MOD_OFFSET;
 141 }
 142 
 143 static int ii20k_ao_insn_write(struct comedi_device *dev,
 144                                struct comedi_subdevice *s,
 145                                struct comedi_insn *insn,
 146                                unsigned int *data)
 147 {
 148         void __iomem *iobase = ii20k_module_iobase(dev, s);
 149         unsigned int chan = CR_CHAN(insn->chanspec);
 150         int i;
 151 
 152         for (i = 0; i < insn->n; i++) {
 153                 unsigned int val = data[i];
 154 
 155                 s->readback[chan] = val;
 156 
 157                 /* munge the offset binary data to 2's complement */
 158                 val = comedi_offset_munge(s, val);
 159 
 160                 writeb(val & 0xff, iobase + II20K_AO_LSB_REG(chan));
 161                 writeb((val >> 8) & 0xff, iobase + II20K_AO_MSB_REG(chan));
 162                 writeb(0x00, iobase + II20K_AO_STRB_REG(chan));
 163         }
 164 
 165         return insn->n;
 166 }
 167 
 168 static int ii20k_ai_eoc(struct comedi_device *dev,
 169                         struct comedi_subdevice *s,
 170                         struct comedi_insn *insn,
 171                         unsigned long context)
 172 {
 173         void __iomem *iobase = ii20k_module_iobase(dev, s);
 174         unsigned char status;
 175 
 176         status = readb(iobase + II20K_AI_STATUS_REG);
 177         if ((status & II20K_AI_STATUS_INT) == 0)
 178                 return 0;
 179         return -EBUSY;
 180 }
 181 
 182 static void ii20k_ai_setup(struct comedi_device *dev,
 183                            struct comedi_subdevice *s,
 184                            unsigned int chanspec)
 185 {
 186         void __iomem *iobase = ii20k_module_iobase(dev, s);
 187         unsigned int chan = CR_CHAN(chanspec);
 188         unsigned int range = CR_RANGE(chanspec);
 189         unsigned char val;
 190 
 191         /* initialize module */
 192         writeb(II20K_AI_CONF_ENA, iobase + II20K_AI_CONF_REG);
 193 
 194         /* software conversion */
 195         writeb(0, iobase + II20K_AI_STATUS_CMD_REG);
 196 
 197         /* set the time base for the settling time counter based on the gain */
 198         val = (range < 3) ? II20K_AI_OPT_TIMEBASE(0) : II20K_AI_OPT_TIMEBASE(2);
 199         writeb(val, iobase + II20K_AI_OPT_REG);
 200 
 201         /* set the settling time counter based on the gain */
 202         val = (range < 2) ? 0x58 : (range < 3) ? 0x93 : 0x99;
 203         writeb(val, iobase + II20K_AI_SET_TIME_REG);
 204 
 205         /* set number of input channels */
 206         writeb(1, iobase + II20K_AI_LAST_CHAN_ADDR_REG);
 207 
 208         /* set the channel list byte */
 209         val = II20K_AI_CHANLIST_ONBOARD_ONLY |
 210               II20K_AI_CHANLIST_MUX_ENA |
 211               II20K_AI_CHANLIST_GAIN(range) |
 212               II20K_AI_CHANLIST_CHAN(chan);
 213         writeb(val, iobase + II20K_AI_CHANLIST_REG);
 214 
 215         /* reset settling time counter and trigger delay counter */
 216         writeb(0, iobase + II20K_AI_COUNT_RESET_REG);
 217 
 218         /* reset channel scanner */
 219         writeb(0, iobase + II20K_AI_CHAN_RESET_REG);
 220 }
 221 
 222 static int ii20k_ai_insn_read(struct comedi_device *dev,
 223                               struct comedi_subdevice *s,
 224                               struct comedi_insn *insn,
 225                               unsigned int *data)
 226 {
 227         void __iomem *iobase = ii20k_module_iobase(dev, s);
 228         int ret;
 229         int i;
 230 
 231         ii20k_ai_setup(dev, s, insn->chanspec);
 232 
 233         for (i = 0; i < insn->n; i++) {
 234                 unsigned int val;
 235 
 236                 /* generate a software start convert signal */
 237                 readb(iobase + II20K_AI_PACER_RESET_REG);
 238 
 239                 ret = comedi_timeout(dev, s, insn, ii20k_ai_eoc, 0);
 240                 if (ret)
 241                         return ret;
 242 
 243                 val = readb(iobase + II20K_AI_LSB_REG);
 244                 val |= (readb(iobase + II20K_AI_MSB_REG) << 8);
 245 
 246                 /* munge the 2's complement data to offset binary */
 247                 data[i] = comedi_offset_munge(s, val);
 248         }
 249 
 250         return insn->n;
 251 }
 252 
 253 static void ii20k_dio_config(struct comedi_device *dev,
 254                              struct comedi_subdevice *s)
 255 {
 256         unsigned char ctrl01 = 0;
 257         unsigned char ctrl23 = 0;
 258         unsigned char dir_ena = 0;
 259 
 260         /* port 0 - channels 0-7 */
 261         if (s->io_bits & 0x000000ff) {
 262                 /* output port */
 263                 ctrl01 &= ~II20K_CTRL01_DIO0_IN;
 264                 dir_ena &= ~II20K_BUF_DISAB_DIO0;
 265                 dir_ena |= II20K_DIR_DIO0_OUT;
 266         } else {
 267                 /* input port */
 268                 ctrl01 |= II20K_CTRL01_DIO0_IN;
 269                 dir_ena &= ~II20K_DIR_DIO0_OUT;
 270         }
 271 
 272         /* port 1 - channels 8-15 */
 273         if (s->io_bits & 0x0000ff00) {
 274                 /* output port */
 275                 ctrl01 &= ~II20K_CTRL01_DIO1_IN;
 276                 dir_ena &= ~II20K_BUF_DISAB_DIO1;
 277                 dir_ena |= II20K_DIR_DIO1_OUT;
 278         } else {
 279                 /* input port */
 280                 ctrl01 |= II20K_CTRL01_DIO1_IN;
 281                 dir_ena &= ~II20K_DIR_DIO1_OUT;
 282         }
 283 
 284         /* port 2 - channels 16-23 */
 285         if (s->io_bits & 0x00ff0000) {
 286                 /* output port */
 287                 ctrl23 &= ~II20K_CTRL23_DIO2_IN;
 288                 dir_ena &= ~II20K_BUF_DISAB_DIO2;
 289                 dir_ena |= II20K_DIR_DIO2_OUT;
 290         } else {
 291                 /* input port */
 292                 ctrl23 |= II20K_CTRL23_DIO2_IN;
 293                 dir_ena &= ~II20K_DIR_DIO2_OUT;
 294         }
 295 
 296         /* port 3 - channels 24-31 */
 297         if (s->io_bits & 0xff000000) {
 298                 /* output port */
 299                 ctrl23 &= ~II20K_CTRL23_DIO3_IN;
 300                 dir_ena &= ~II20K_BUF_DISAB_DIO3;
 301                 dir_ena |= II20K_DIR_DIO3_OUT;
 302         } else {
 303                 /* input port */
 304                 ctrl23 |= II20K_CTRL23_DIO3_IN;
 305                 dir_ena &= ~II20K_DIR_DIO3_OUT;
 306         }
 307 
 308         ctrl23 |= II20K_CTRL01_SET;
 309         ctrl23 |= II20K_CTRL23_SET;
 310 
 311         /* order is important */
 312         writeb(ctrl01, dev->mmio + II20K_CTRL01_REG);
 313         writeb(ctrl23, dev->mmio + II20K_CTRL23_REG);
 314         writeb(dir_ena, dev->mmio + II20K_DIR_ENA_REG);
 315 }
 316 
 317 static int ii20k_dio_insn_config(struct comedi_device *dev,
 318                                  struct comedi_subdevice *s,
 319                                  struct comedi_insn *insn,
 320                                  unsigned int *data)
 321 {
 322         unsigned int chan = CR_CHAN(insn->chanspec);
 323         unsigned int mask;
 324         int ret;
 325 
 326         if (chan < 8)
 327                 mask = 0x000000ff;
 328         else if (chan < 16)
 329                 mask = 0x0000ff00;
 330         else if (chan < 24)
 331                 mask = 0x00ff0000;
 332         else
 333                 mask = 0xff000000;
 334 
 335         ret = comedi_dio_insn_config(dev, s, insn, data, mask);
 336         if (ret)
 337                 return ret;
 338 
 339         ii20k_dio_config(dev, s);
 340 
 341         return insn->n;
 342 }
 343 
 344 static int ii20k_dio_insn_bits(struct comedi_device *dev,
 345                                struct comedi_subdevice *s,
 346                                struct comedi_insn *insn,
 347                                unsigned int *data)
 348 {
 349         unsigned int mask;
 350 
 351         mask = comedi_dio_update_state(s, data);
 352         if (mask) {
 353                 if (mask & 0x000000ff)
 354                         writeb((s->state >> 0) & 0xff,
 355                                dev->mmio + II20K_DIO0_REG);
 356                 if (mask & 0x0000ff00)
 357                         writeb((s->state >> 8) & 0xff,
 358                                dev->mmio + II20K_DIO1_REG);
 359                 if (mask & 0x00ff0000)
 360                         writeb((s->state >> 16) & 0xff,
 361                                dev->mmio + II20K_DIO2_REG);
 362                 if (mask & 0xff000000)
 363                         writeb((s->state >> 24) & 0xff,
 364                                dev->mmio + II20K_DIO3_REG);
 365         }
 366 
 367         data[1] = readb(dev->mmio + II20K_DIO0_REG);
 368         data[1] |= readb(dev->mmio + II20K_DIO1_REG) << 8;
 369         data[1] |= readb(dev->mmio + II20K_DIO2_REG) << 16;
 370         data[1] |= readb(dev->mmio + II20K_DIO3_REG) << 24;
 371 
 372         return insn->n;
 373 }
 374 
 375 static int ii20k_init_module(struct comedi_device *dev,
 376                              struct comedi_subdevice *s)
 377 {
 378         void __iomem *iobase = ii20k_module_iobase(dev, s);
 379         unsigned char id;
 380         int ret;
 381 
 382         id = readb(iobase + II20K_ID_REG);
 383         switch (id) {
 384         case II20K_ID_PCI20006M_1:
 385         case II20K_ID_PCI20006M_2:
 386                 /* Analog Output subdevice */
 387                 s->type         = COMEDI_SUBD_AO;
 388                 s->subdev_flags = SDF_WRITABLE;
 389                 s->n_chan       = (id == II20K_ID_PCI20006M_2) ? 2 : 1;
 390                 s->maxdata      = 0xffff;
 391                 s->range_table  = &ii20k_ao_ranges;
 392                 s->insn_write   = ii20k_ao_insn_write;
 393 
 394                 ret = comedi_alloc_subdev_readback(s);
 395                 if (ret)
 396                         return ret;
 397                 break;
 398         case II20K_ID_PCI20341M_1:
 399                 /* Analog Input subdevice */
 400                 s->type         = COMEDI_SUBD_AI;
 401                 s->subdev_flags = SDF_READABLE | SDF_DIFF;
 402                 s->n_chan       = 4;
 403                 s->maxdata      = 0xffff;
 404                 s->range_table  = &ii20k_ai_ranges;
 405                 s->insn_read    = ii20k_ai_insn_read;
 406                 break;
 407         default:
 408                 s->type = COMEDI_SUBD_UNUSED;
 409                 break;
 410         }
 411 
 412         return 0;
 413 }
 414 
 415 static int ii20k_attach(struct comedi_device *dev,
 416                         struct comedi_devconfig *it)
 417 {
 418         struct comedi_subdevice *s;
 419         unsigned int membase;
 420         unsigned char id;
 421         bool has_dio;
 422         int ret;
 423 
 424         membase = it->options[0];
 425         if (!membase || (membase & ~(0x100000 - II20K_SIZE))) {
 426                 dev_warn(dev->class_dev,
 427                          "%s: invalid memory address specified\n",
 428                          dev->board_name);
 429                 return -EINVAL;
 430         }
 431 
 432         if (!request_mem_region(membase, II20K_SIZE, dev->board_name)) {
 433                 dev_warn(dev->class_dev, "%s: I/O mem conflict (%#x,%u)\n",
 434                          dev->board_name, membase, II20K_SIZE);
 435                 return -EIO;
 436         }
 437         dev->iobase = membase;  /* actually, a memory address */
 438 
 439         dev->mmio = ioremap(membase, II20K_SIZE);
 440         if (!dev->mmio)
 441                 return -ENOMEM;
 442 
 443         id = readb(dev->mmio + II20K_ID_REG);
 444         switch (id & II20K_ID_MASK) {
 445         case II20K_ID_PCI20001C_1A:
 446                 has_dio = false;
 447                 break;
 448         case II20K_ID_PCI20001C_2A:
 449                 has_dio = true;
 450                 break;
 451         default:
 452                 return -ENODEV;
 453         }
 454 
 455         ret = comedi_alloc_subdevices(dev, 4);
 456         if (ret)
 457                 return ret;
 458 
 459         s = &dev->subdevices[0];
 460         if (id & II20K_ID_MOD1_EMPTY) {
 461                 s->type = COMEDI_SUBD_UNUSED;
 462         } else {
 463                 ret = ii20k_init_module(dev, s);
 464                 if (ret)
 465                         return ret;
 466         }
 467 
 468         s = &dev->subdevices[1];
 469         if (id & II20K_ID_MOD2_EMPTY) {
 470                 s->type = COMEDI_SUBD_UNUSED;
 471         } else {
 472                 ret = ii20k_init_module(dev, s);
 473                 if (ret)
 474                         return ret;
 475         }
 476 
 477         s = &dev->subdevices[2];
 478         if (id & II20K_ID_MOD3_EMPTY) {
 479                 s->type = COMEDI_SUBD_UNUSED;
 480         } else {
 481                 ret = ii20k_init_module(dev, s);
 482                 if (ret)
 483                         return ret;
 484         }
 485 
 486         /* Digital I/O subdevice */
 487         s = &dev->subdevices[3];
 488         if (has_dio) {
 489                 s->type         = COMEDI_SUBD_DIO;
 490                 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
 491                 s->n_chan       = 32;
 492                 s->maxdata      = 1;
 493                 s->range_table  = &range_digital;
 494                 s->insn_bits    = ii20k_dio_insn_bits;
 495                 s->insn_config  = ii20k_dio_insn_config;
 496 
 497                 /* default all channels to input */
 498                 ii20k_dio_config(dev, s);
 499         } else {
 500                 s->type = COMEDI_SUBD_UNUSED;
 501         }
 502 
 503         return 0;
 504 }
 505 
 506 static void ii20k_detach(struct comedi_device *dev)
 507 {
 508         if (dev->mmio)
 509                 iounmap(dev->mmio);
 510         if (dev->iobase)        /* actually, a memory address */
 511                 release_mem_region(dev->iobase, II20K_SIZE);
 512 }
 513 
 514 static struct comedi_driver ii20k_driver = {
 515         .driver_name    = "ii_pci20kc",
 516         .module         = THIS_MODULE,
 517         .attach         = ii20k_attach,
 518         .detach         = ii20k_detach,
 519 };
 520 module_comedi_driver(ii20k_driver);
 521 
 522 MODULE_AUTHOR("Comedi http://www.comedi.org");
 523 MODULE_DESCRIPTION("Comedi driver for Intelligent Instruments PCI-20001C");
 524 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */