root/drivers/staging/comedi/drivers/cb_das16_cs.c

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

DEFINITIONS

This source file includes following definitions.
  1. das16cs_ai_eoc
  2. das16cs_ai_insn_read
  3. das16cs_ao_insn_write
  4. das16cs_dio_insn_bits
  5. das16cs_dio_insn_config
  6. das16cs_counter_insn_config
  7. das16cs_find_boardinfo
  8. das16cs_auto_attach
  9. das16cs_pcmcia_attach

   1 // SPDX-License-Identifier: GPL-2.0+
   2 /*
   3  * cb_das16_cs.c
   4  * Driver for Computer Boards PC-CARD DAS16/16.
   5  *
   6  * COMEDI - Linux Control and Measurement Device Interface
   7  * Copyright (C) 2000, 2001, 2002 David A. Schleef <ds@schleef.org>
   8  *
   9  * PCMCIA support code for this driver is adapted from the dummy_cs.c
  10  * driver of the Linux PCMCIA Card Services package.
  11  *
  12  * The initial developer of the original code is David A. Hinds
  13  * <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
  14  * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
  15  */
  16 
  17 /*
  18  * Driver: cb_das16_cs
  19  * Description: Computer Boards PC-CARD DAS16/16
  20  * Devices: [ComputerBoards] PC-CARD DAS16/16 (cb_das16_cs),
  21  *   PC-CARD DAS16/16-AO
  22  * Author: ds
  23  * Updated: Mon, 04 Nov 2002 20:04:21 -0800
  24  * Status: experimental
  25  */
  26 
  27 #include <linux/module.h>
  28 #include <linux/interrupt.h>
  29 #include <linux/delay.h>
  30 
  31 #include "../comedi_pcmcia.h"
  32 
  33 #include "comedi_8254.h"
  34 
  35 /*
  36  * Register I/O map
  37  */
  38 #define DAS16CS_AI_DATA_REG             0x00
  39 #define DAS16CS_AI_MUX_REG              0x02
  40 #define DAS16CS_AI_MUX_HI_CHAN(x)       (((x) & 0xf) << 4)
  41 #define DAS16CS_AI_MUX_LO_CHAN(x)       (((x) & 0xf) << 0)
  42 #define DAS16CS_AI_MUX_SINGLE_CHAN(x)   (DAS16CS_AI_MUX_HI_CHAN(x) |    \
  43                                          DAS16CS_AI_MUX_LO_CHAN(x))
  44 #define DAS16CS_MISC1_REG               0x04
  45 #define DAS16CS_MISC1_INTE              BIT(15) /* 1=enable; 0=disable */
  46 #define DAS16CS_MISC1_INT_SRC(x)        (((x) & 0x7) << 12) /* interrupt src */
  47 #define DAS16CS_MISC1_INT_SRC_NONE      DAS16CS_MISC1_INT_SRC(0)
  48 #define DAS16CS_MISC1_INT_SRC_PACER     DAS16CS_MISC1_INT_SRC(1)
  49 #define DAS16CS_MISC1_INT_SRC_EXT       DAS16CS_MISC1_INT_SRC(2)
  50 #define DAS16CS_MISC1_INT_SRC_FNE       DAS16CS_MISC1_INT_SRC(3)
  51 #define DAS16CS_MISC1_INT_SRC_FHF       DAS16CS_MISC1_INT_SRC(4)
  52 #define DAS16CS_MISC1_INT_SRC_EOS       DAS16CS_MISC1_INT_SRC(5)
  53 #define DAS16CS_MISC1_INT_SRC_MASK      DAS16CS_MISC1_INT_SRC(7)
  54 #define DAS16CS_MISC1_OVR               BIT(10) /* ro - 1=FIFO overflow */
  55 #define DAS16CS_MISC1_AI_CONV(x)        (((x) & 0x3) << 8) /* AI convert src */
  56 #define DAS16CS_MISC1_AI_CONV_SW        DAS16CS_MISC1_AI_CONV(0)
  57 #define DAS16CS_MISC1_AI_CONV_EXT_NEG   DAS16CS_MISC1_AI_CONV(1)
  58 #define DAS16CS_MISC1_AI_CONV_EXT_POS   DAS16CS_MISC1_AI_CONV(2)
  59 #define DAS16CS_MISC1_AI_CONV_PACER     DAS16CS_MISC1_AI_CONV(3)
  60 #define DAS16CS_MISC1_AI_CONV_MASK      DAS16CS_MISC1_AI_CONV(3)
  61 #define DAS16CS_MISC1_EOC               BIT(7)  /* ro - 0=busy; 1=ready */
  62 #define DAS16CS_MISC1_SEDIFF            BIT(5)  /* 0=diff; 1=se */
  63 #define DAS16CS_MISC1_INTB              BIT(4)  /* ro - 0=latched; 1=cleared */
  64 #define DAS16CS_MISC1_MA_MASK           (0xf << 0) /* ro - current ai mux */
  65 #define DAS16CS_MISC1_DAC1CS            BIT(3)  /* wo - DAC1 chip select */
  66 #define DAS16CS_MISC1_DACCLK            BIT(2)  /* wo - Serial DAC clock */
  67 #define DAS16CS_MISC1_DACSD             BIT(1)  /* wo - Serial DAC data */
  68 #define DAS16CS_MISC1_DAC0CS            BIT(0)  /* wo - DAC0 chip select */
  69 #define DAS16CS_MISC1_DAC_MASK          (0x0f << 0)
  70 #define DAS16CS_MISC2_REG               0x06
  71 #define DAS16CS_MISC2_BME               BIT(14) /* 1=burst enable; 0=disable */
  72 #define DAS16CS_MISC2_AI_GAIN(x)        (((x) & 0xf) << 8) /* AI gain */
  73 #define DAS16CS_MISC2_AI_GAIN_1         DAS16CS_MISC2_AI_GAIN(4) /* +/-10V */
  74 #define DAS16CS_MISC2_AI_GAIN_2         DAS16CS_MISC2_AI_GAIN(0) /* +/-5V */
  75 #define DAS16CS_MISC2_AI_GAIN_4         DAS16CS_MISC2_AI_GAIN(1) /* +/-2.5V */
  76 #define DAS16CS_MISC2_AI_GAIN_8         DAS16CS_MISC2_AI_GAIN(2) /* +-1.25V */
  77 #define DAS16CS_MISC2_AI_GAIN_MASK      DAS16CS_MISC2_AI_GAIN(0xf)
  78 #define DAS16CS_MISC2_UDIR              BIT(7)  /* 1=dio7:4 output; 0=input */
  79 #define DAS16CS_MISC2_LDIR              BIT(6)  /* 1=dio3:0 output; 0=input */
  80 #define DAS16CS_MISC2_TRGPOL            BIT(5)  /* 1=active lo; 0=hi */
  81 #define DAS16CS_MISC2_TRGSEL            BIT(4)  /* 1=edge; 0=level */
  82 #define DAS16CS_MISC2_FFNE              BIT(3)  /* ro - 1=FIFO not empty */
  83 #define DAS16CS_MISC2_TRGCLR            BIT(3)  /* wo - 1=clr (monstable) */
  84 #define DAS16CS_MISC2_CLK2              BIT(2)  /* 1=10 MHz; 0=1 MHz */
  85 #define DAS16CS_MISC2_CTR1              BIT(1)  /* 1=int. 100 kHz; 0=ext. clk */
  86 #define DAS16CS_MISC2_TRG0              BIT(0)  /* 1=enable; 0=disable */
  87 #define DAS16CS_TIMER_BASE              0x08
  88 #define DAS16CS_DIO_REG                 0x10
  89 
  90 struct das16cs_board {
  91         const char *name;
  92         int device_id;
  93         unsigned int has_ao:1;
  94         unsigned int has_4dio:1;
  95 };
  96 
  97 static const struct das16cs_board das16cs_boards[] = {
  98         {
  99                 .name           = "PC-CARD DAS16/16-AO",
 100                 .device_id      = 0x0039,
 101                 .has_ao         = 1,
 102                 .has_4dio       = 1,
 103         }, {
 104                 .name           = "PCM-DAS16s/16",
 105                 .device_id      = 0x4009,
 106         }, {
 107                 .name           = "PC-CARD DAS16/16",
 108                 .device_id      = 0x0000,       /* unknown */
 109         },
 110 };
 111 
 112 struct das16cs_private {
 113         unsigned short misc1;
 114         unsigned short misc2;
 115 };
 116 
 117 static const struct comedi_lrange das16cs_ai_range = {
 118         4, {
 119                 BIP_RANGE(10),
 120                 BIP_RANGE(5),
 121                 BIP_RANGE(2.5),
 122                 BIP_RANGE(1.25),
 123         }
 124 };
 125 
 126 static int das16cs_ai_eoc(struct comedi_device *dev,
 127                           struct comedi_subdevice *s,
 128                           struct comedi_insn *insn,
 129                           unsigned long context)
 130 {
 131         unsigned int status;
 132 
 133         status = inw(dev->iobase + DAS16CS_MISC1_REG);
 134         if (status & DAS16CS_MISC1_EOC)
 135                 return 0;
 136         return -EBUSY;
 137 }
 138 
 139 static int das16cs_ai_insn_read(struct comedi_device *dev,
 140                                 struct comedi_subdevice *s,
 141                                 struct comedi_insn *insn,
 142                                 unsigned int *data)
 143 {
 144         struct das16cs_private *devpriv = dev->private;
 145         int chan = CR_CHAN(insn->chanspec);
 146         int range = CR_RANGE(insn->chanspec);
 147         int aref = CR_AREF(insn->chanspec);
 148         int ret;
 149         int i;
 150 
 151         outw(DAS16CS_AI_MUX_SINGLE_CHAN(chan),
 152              dev->iobase + DAS16CS_AI_MUX_REG);
 153 
 154         /* disable interrupts, software convert */
 155         devpriv->misc1 &= ~(DAS16CS_MISC1_INTE | DAS16CS_MISC1_INT_SRC_MASK |
 156                               DAS16CS_MISC1_AI_CONV_MASK);
 157         if (aref == AREF_DIFF)
 158                 devpriv->misc1 &= ~DAS16CS_MISC1_SEDIFF;
 159         else
 160                 devpriv->misc1 |= DAS16CS_MISC1_SEDIFF;
 161         outw(devpriv->misc1, dev->iobase + DAS16CS_MISC1_REG);
 162 
 163         devpriv->misc2 &= ~(DAS16CS_MISC2_BME | DAS16CS_MISC2_AI_GAIN_MASK);
 164         switch (range) {
 165         case 0:
 166                 devpriv->misc2 |= DAS16CS_MISC2_AI_GAIN_1;
 167                 break;
 168         case 1:
 169                 devpriv->misc2 |= DAS16CS_MISC2_AI_GAIN_2;
 170                 break;
 171         case 2:
 172                 devpriv->misc2 |= DAS16CS_MISC2_AI_GAIN_4;
 173                 break;
 174         case 3:
 175                 devpriv->misc2 |= DAS16CS_MISC2_AI_GAIN_8;
 176                 break;
 177         }
 178         outw(devpriv->misc2, dev->iobase + DAS16CS_MISC2_REG);
 179 
 180         for (i = 0; i < insn->n; i++) {
 181                 outw(0, dev->iobase + DAS16CS_AI_DATA_REG);
 182 
 183                 ret = comedi_timeout(dev, s, insn, das16cs_ai_eoc, 0);
 184                 if (ret)
 185                         return ret;
 186 
 187                 data[i] = inw(dev->iobase + DAS16CS_AI_DATA_REG);
 188         }
 189 
 190         return i;
 191 }
 192 
 193 static int das16cs_ao_insn_write(struct comedi_device *dev,
 194                                  struct comedi_subdevice *s,
 195                                  struct comedi_insn *insn,
 196                                  unsigned int *data)
 197 {
 198         struct das16cs_private *devpriv = dev->private;
 199         unsigned int chan = CR_CHAN(insn->chanspec);
 200         unsigned int val = s->readback[chan];
 201         unsigned short misc1;
 202         int bit;
 203         int i;
 204 
 205         for (i = 0; i < insn->n; i++) {
 206                 val = data[i];
 207 
 208                 outw(devpriv->misc1, dev->iobase + DAS16CS_MISC1_REG);
 209                 udelay(1);
 210 
 211                 /* raise the DACxCS line for the non-selected channel */
 212                 misc1 = devpriv->misc1 & ~DAS16CS_MISC1_DAC_MASK;
 213                 if (chan)
 214                         misc1 |= DAS16CS_MISC1_DAC0CS;
 215                 else
 216                         misc1 |= DAS16CS_MISC1_DAC1CS;
 217 
 218                 outw(misc1, dev->iobase + DAS16CS_MISC1_REG);
 219                 udelay(1);
 220 
 221                 for (bit = 15; bit >= 0; bit--) {
 222                         if ((val >> bit) & 0x1)
 223                                 misc1 |= DAS16CS_MISC1_DACSD;
 224                         else
 225                                 misc1 &= ~DAS16CS_MISC1_DACSD;
 226                         outw(misc1, dev->iobase + DAS16CS_MISC1_REG);
 227                         udelay(1);
 228                         outw(misc1 | DAS16CS_MISC1_DACCLK,
 229                              dev->iobase + DAS16CS_MISC1_REG);
 230                         udelay(1);
 231                 }
 232                 /*
 233                  * Make both DAC0CS and DAC1CS high to load
 234                  * the new data and update analog the output
 235                  */
 236                 outw(misc1 | DAS16CS_MISC1_DAC0CS | DAS16CS_MISC1_DAC1CS,
 237                      dev->iobase + DAS16CS_MISC1_REG);
 238         }
 239         s->readback[chan] = val;
 240 
 241         return insn->n;
 242 }
 243 
 244 static int das16cs_dio_insn_bits(struct comedi_device *dev,
 245                                  struct comedi_subdevice *s,
 246                                  struct comedi_insn *insn,
 247                                  unsigned int *data)
 248 {
 249         if (comedi_dio_update_state(s, data))
 250                 outw(s->state, dev->iobase + DAS16CS_DIO_REG);
 251 
 252         data[1] = inw(dev->iobase + DAS16CS_DIO_REG);
 253 
 254         return insn->n;
 255 }
 256 
 257 static int das16cs_dio_insn_config(struct comedi_device *dev,
 258                                    struct comedi_subdevice *s,
 259                                    struct comedi_insn *insn,
 260                                    unsigned int *data)
 261 {
 262         struct das16cs_private *devpriv = dev->private;
 263         unsigned int chan = CR_CHAN(insn->chanspec);
 264         unsigned int mask;
 265         int ret;
 266 
 267         if (chan < 4)
 268                 mask = 0x0f;
 269         else
 270                 mask = 0xf0;
 271 
 272         ret = comedi_dio_insn_config(dev, s, insn, data, mask);
 273         if (ret)
 274                 return ret;
 275 
 276         if (s->io_bits & 0xf0)
 277                 devpriv->misc2 |= DAS16CS_MISC2_UDIR;
 278         else
 279                 devpriv->misc2 &= ~DAS16CS_MISC2_UDIR;
 280         if (s->io_bits & 0x0f)
 281                 devpriv->misc2 |= DAS16CS_MISC2_LDIR;
 282         else
 283                 devpriv->misc2 &= ~DAS16CS_MISC2_LDIR;
 284         outw(devpriv->misc2, dev->iobase + DAS16CS_MISC2_REG);
 285 
 286         return insn->n;
 287 }
 288 
 289 static int das16cs_counter_insn_config(struct comedi_device *dev,
 290                                        struct comedi_subdevice *s,
 291                                        struct comedi_insn *insn,
 292                                        unsigned int *data)
 293 {
 294         struct das16cs_private *devpriv = dev->private;
 295 
 296         switch (data[0]) {
 297         case INSN_CONFIG_SET_CLOCK_SRC:
 298                 switch (data[1]) {
 299                 case 0: /* internal 100 kHz */
 300                         devpriv->misc2 |= DAS16CS_MISC2_CTR1;
 301                         break;
 302                 case 1: /* external */
 303                         devpriv->misc2 &= ~DAS16CS_MISC2_CTR1;
 304                         break;
 305                 default:
 306                         return -EINVAL;
 307                 }
 308                 outw(devpriv->misc2, dev->iobase + DAS16CS_MISC2_REG);
 309                 break;
 310         case INSN_CONFIG_GET_CLOCK_SRC:
 311                 if (devpriv->misc2 & DAS16CS_MISC2_CTR1) {
 312                         data[1] = 0;
 313                         data[2] = I8254_OSC_BASE_100KHZ;
 314                 } else {
 315                         data[1] = 1;
 316                         data[2] = 0;    /* unknown */
 317                 }
 318                 break;
 319         default:
 320                 return -EINVAL;
 321         }
 322 
 323         return insn->n;
 324 }
 325 
 326 static const void *das16cs_find_boardinfo(struct comedi_device *dev,
 327                                           struct pcmcia_device *link)
 328 {
 329         const struct das16cs_board *board;
 330         int i;
 331 
 332         for (i = 0; i < ARRAY_SIZE(das16cs_boards); i++) {
 333                 board = &das16cs_boards[i];
 334                 if (board->device_id == link->card_id)
 335                         return board;
 336         }
 337 
 338         return NULL;
 339 }
 340 
 341 static int das16cs_auto_attach(struct comedi_device *dev,
 342                                unsigned long context)
 343 {
 344         struct pcmcia_device *link = comedi_to_pcmcia_dev(dev);
 345         const struct das16cs_board *board;
 346         struct das16cs_private *devpriv;
 347         struct comedi_subdevice *s;
 348         int ret;
 349 
 350         board = das16cs_find_boardinfo(dev, link);
 351         if (!board)
 352                 return -ENODEV;
 353         dev->board_ptr = board;
 354         dev->board_name = board->name;
 355 
 356         link->config_flags |= CONF_AUTO_SET_IO | CONF_ENABLE_IRQ;
 357         ret = comedi_pcmcia_enable(dev, NULL);
 358         if (ret)
 359                 return ret;
 360         dev->iobase = link->resource[0]->start;
 361 
 362         link->priv = dev;
 363 
 364         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
 365         if (!devpriv)
 366                 return -ENOMEM;
 367 
 368         dev->pacer = comedi_8254_init(dev->iobase + DAS16CS_TIMER_BASE,
 369                                       I8254_OSC_BASE_10MHZ, I8254_IO16, 0);
 370         if (!dev->pacer)
 371                 return -ENOMEM;
 372 
 373         ret = comedi_alloc_subdevices(dev, 4);
 374         if (ret)
 375                 return ret;
 376 
 377         /* Analog Input subdevice */
 378         s = &dev->subdevices[0];
 379         s->type         = COMEDI_SUBD_AI;
 380         s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
 381         s->n_chan       = 16;
 382         s->maxdata      = 0xffff;
 383         s->range_table  = &das16cs_ai_range;
 384         s->insn_read    = das16cs_ai_insn_read;
 385 
 386         /* Analog Output subdevice */
 387         s = &dev->subdevices[1];
 388         if (board->has_ao) {
 389                 s->type         = COMEDI_SUBD_AO;
 390                 s->subdev_flags = SDF_WRITABLE;
 391                 s->n_chan       = 2;
 392                 s->maxdata      = 0xffff;
 393                 s->range_table  = &range_bipolar10;
 394                 s->insn_write   = &das16cs_ao_insn_write;
 395 
 396                 ret = comedi_alloc_subdev_readback(s);
 397                 if (ret)
 398                         return ret;
 399         } else {
 400                 s->type         = COMEDI_SUBD_UNUSED;
 401         }
 402 
 403         /* Digital I/O subdevice */
 404         s = &dev->subdevices[2];
 405         s->type         = COMEDI_SUBD_DIO;
 406         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
 407         s->n_chan       = board->has_4dio ? 4 : 8;
 408         s->maxdata      = 1;
 409         s->range_table  = &range_digital;
 410         s->insn_bits    = das16cs_dio_insn_bits;
 411         s->insn_config  = das16cs_dio_insn_config;
 412 
 413         /* Counter subdevice (8254) */
 414         s = &dev->subdevices[3];
 415         comedi_8254_subdevice_init(s, dev->pacer);
 416 
 417         dev->pacer->insn_config = das16cs_counter_insn_config;
 418 
 419         /* counters 1 and 2 are used internally for the pacer */
 420         comedi_8254_set_busy(dev->pacer, 1, true);
 421         comedi_8254_set_busy(dev->pacer, 2, true);
 422 
 423         return 0;
 424 }
 425 
 426 static struct comedi_driver driver_das16cs = {
 427         .driver_name    = "cb_das16_cs",
 428         .module         = THIS_MODULE,
 429         .auto_attach    = das16cs_auto_attach,
 430         .detach         = comedi_pcmcia_disable,
 431 };
 432 
 433 static int das16cs_pcmcia_attach(struct pcmcia_device *link)
 434 {
 435         return comedi_pcmcia_auto_config(link, &driver_das16cs);
 436 }
 437 
 438 static const struct pcmcia_device_id das16cs_id_table[] = {
 439         PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x0039),
 440         PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x4009),
 441         PCMCIA_DEVICE_NULL
 442 };
 443 MODULE_DEVICE_TABLE(pcmcia, das16cs_id_table);
 444 
 445 static struct pcmcia_driver das16cs_driver = {
 446         .name           = "cb_das16_cs",
 447         .owner          = THIS_MODULE,
 448         .id_table       = das16cs_id_table,
 449         .probe          = das16cs_pcmcia_attach,
 450         .remove         = comedi_pcmcia_auto_unconfig,
 451 };
 452 module_comedi_pcmcia_driver(driver_das16cs, das16cs_driver);
 453 
 454 MODULE_AUTHOR("David A. Schleef <ds@schleef.org>");
 455 MODULE_DESCRIPTION("Comedi driver for Computer Boards PC-CARD DAS16/16");
 456 MODULE_LICENSE("GPL");

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