root/drivers/staging/comedi/drivers/multiq3.c

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

DEFINITIONS

This source file includes following definitions.
  1. multiq3_set_ctrl
  2. multiq3_ai_status
  3. multiq3_ai_insn_read
  4. multiq3_ao_insn_write
  5. multiq3_di_insn_bits
  6. multiq3_do_insn_bits
  7. multiq3_encoder_insn_read
  8. multiq3_encoder_reset
  9. multiq3_encoder_insn_config
  10. multiq3_attach

   1 // SPDX-License-Identifier: GPL-2.0+
   2 /*
   3  * multiq3.c
   4  * Hardware driver for Quanser Consulting MultiQ-3 board
   5  *
   6  * COMEDI - Linux Control and Measurement Device Interface
   7  * Copyright (C) 1999 Anders Blomdell <anders.blomdell@control.lth.se>
   8  */
   9 
  10 /*
  11  * Driver: multiq3
  12  * Description: Quanser Consulting MultiQ-3
  13  * Devices: [Quanser Consulting] MultiQ-3 (multiq3)
  14  * Author: Anders Blomdell <anders.blomdell@control.lth.se>
  15  * Status: works
  16  *
  17  * Configuration Options:
  18  *  [0] - I/O port base address
  19  *  [1] - IRQ (not used)
  20  *  [2] - Number of optional encoder chips installed on board
  21  *        0 = none
  22  *        1 = 2 inputs (Model -2E)
  23  *        2 = 4 inputs (Model -4E)
  24  *        3 = 6 inputs (Model -6E)
  25  *        4 = 8 inputs (Model -8E)
  26  */
  27 
  28 #include <linux/module.h>
  29 
  30 #include "../comedidev.h"
  31 
  32 /*
  33  * Register map
  34  */
  35 #define MULTIQ3_DI_REG                  0x00
  36 #define MULTIQ3_DO_REG                  0x00
  37 #define MULTIQ3_AO_REG                  0x02
  38 #define MULTIQ3_AI_REG                  0x04
  39 #define MULTIQ3_AI_CONV_REG             0x04
  40 #define MULTIQ3_STATUS_REG              0x06
  41 #define MULTIQ3_STATUS_EOC              BIT(3)
  42 #define MULTIQ3_STATUS_EOC_I            BIT(4)
  43 #define MULTIQ3_CTRL_REG                0x06
  44 #define MULTIQ3_CTRL_AO_CHAN(x)         (((x) & 0x7) << 0)
  45 #define MULTIQ3_CTRL_RC(x)              (((x) & 0x3) << 0)
  46 #define MULTIQ3_CTRL_AI_CHAN(x)         (((x) & 0x7) << 3)
  47 #define MULTIQ3_CTRL_E_CHAN(x)          (((x) & 0x7) << 3)
  48 #define MULTIQ3_CTRL_EN                 BIT(6)
  49 #define MULTIQ3_CTRL_AZ                 BIT(7)
  50 #define MULTIQ3_CTRL_CAL                BIT(8)
  51 #define MULTIQ3_CTRL_SH                 BIT(9)
  52 #define MULTIQ3_CTRL_CLK                BIT(10)
  53 #define MULTIQ3_CTRL_LD                 (3 << 11)
  54 #define MULTIQ3_CLK_REG                 0x08
  55 #define MULTIQ3_ENC_DATA_REG            0x0c
  56 #define MULTIQ3_ENC_CTRL_REG            0x0e
  57 
  58 /*
  59  * Encoder chip commands (from the programming manual)
  60  */
  61 #define MULTIQ3_CLOCK_DATA              0x00    /* FCK frequency divider */
  62 #define MULTIQ3_CLOCK_SETUP             0x18    /* xfer PR0 to PSC */
  63 #define MULTIQ3_INPUT_SETUP             0x41    /* enable inputs A and B */
  64 #define MULTIQ3_QUAD_X4                 0x38    /* quadrature */
  65 #define MULTIQ3_BP_RESET                0x01    /* reset byte pointer */
  66 #define MULTIQ3_CNTR_RESET              0x02    /* reset counter */
  67 #define MULTIQ3_TRSFRPR_CTR             0x08    /* xfre preset reg to counter */
  68 #define MULTIQ3_TRSFRCNTR_OL            0x10    /* xfer CNTR to OL (x and y) */
  69 #define MULTIQ3_EFLAG_RESET             0x06    /* reset E bit of flag reg */
  70 
  71 static void multiq3_set_ctrl(struct comedi_device *dev, unsigned int bits)
  72 {
  73         /*
  74          * According to the programming manual, the SH and CLK bits should
  75          * be kept high at all times.
  76          */
  77         outw(MULTIQ3_CTRL_SH | MULTIQ3_CTRL_CLK | bits,
  78              dev->iobase + MULTIQ3_CTRL_REG);
  79 }
  80 
  81 static int multiq3_ai_status(struct comedi_device *dev,
  82                              struct comedi_subdevice *s,
  83                              struct comedi_insn *insn,
  84                              unsigned long context)
  85 {
  86         unsigned int status;
  87 
  88         status = inw(dev->iobase + MULTIQ3_STATUS_REG);
  89         if (status & context)
  90                 return 0;
  91         return -EBUSY;
  92 }
  93 
  94 static int multiq3_ai_insn_read(struct comedi_device *dev,
  95                                 struct comedi_subdevice *s,
  96                                 struct comedi_insn *insn,
  97                                 unsigned int *data)
  98 {
  99         unsigned int chan = CR_CHAN(insn->chanspec);
 100         unsigned int val;
 101         int ret;
 102         int i;
 103 
 104         multiq3_set_ctrl(dev, MULTIQ3_CTRL_EN | MULTIQ3_CTRL_AI_CHAN(chan));
 105 
 106         ret = comedi_timeout(dev, s, insn, multiq3_ai_status,
 107                              MULTIQ3_STATUS_EOC);
 108         if (ret)
 109                 return ret;
 110 
 111         for (i = 0; i < insn->n; i++) {
 112                 outw(0, dev->iobase + MULTIQ3_AI_CONV_REG);
 113 
 114                 ret = comedi_timeout(dev, s, insn, multiq3_ai_status,
 115                                      MULTIQ3_STATUS_EOC_I);
 116                 if (ret)
 117                         return ret;
 118 
 119                 /* get a 16-bit sample; mask it to the subdevice resolution */
 120                 val = inb(dev->iobase + MULTIQ3_AI_REG) << 8;
 121                 val |= inb(dev->iobase + MULTIQ3_AI_REG);
 122                 val &= s->maxdata;
 123 
 124                 /* munge the 2's complement value to offset binary */
 125                 data[i] = comedi_offset_munge(s, val);
 126         }
 127 
 128         return insn->n;
 129 }
 130 
 131 static int multiq3_ao_insn_write(struct comedi_device *dev,
 132                                  struct comedi_subdevice *s,
 133                                  struct comedi_insn *insn,
 134                                  unsigned int *data)
 135 {
 136         unsigned int chan = CR_CHAN(insn->chanspec);
 137         unsigned int val = s->readback[chan];
 138         int i;
 139 
 140         for (i = 0; i < insn->n; i++) {
 141                 val = data[i];
 142                 multiq3_set_ctrl(dev, MULTIQ3_CTRL_LD |
 143                                       MULTIQ3_CTRL_AO_CHAN(chan));
 144                 outw(val, dev->iobase + MULTIQ3_AO_REG);
 145                 multiq3_set_ctrl(dev, 0);
 146         }
 147         s->readback[chan] = val;
 148 
 149         return insn->n;
 150 }
 151 
 152 static int multiq3_di_insn_bits(struct comedi_device *dev,
 153                                 struct comedi_subdevice *s,
 154                                 struct comedi_insn *insn, unsigned int *data)
 155 {
 156         data[1] = inw(dev->iobase + MULTIQ3_DI_REG);
 157 
 158         return insn->n;
 159 }
 160 
 161 static int multiq3_do_insn_bits(struct comedi_device *dev,
 162                                 struct comedi_subdevice *s,
 163                                 struct comedi_insn *insn,
 164                                 unsigned int *data)
 165 {
 166         if (comedi_dio_update_state(s, data))
 167                 outw(s->state, dev->iobase + MULTIQ3_DO_REG);
 168 
 169         data[1] = s->state;
 170 
 171         return insn->n;
 172 }
 173 
 174 static int multiq3_encoder_insn_read(struct comedi_device *dev,
 175                                      struct comedi_subdevice *s,
 176                                      struct comedi_insn *insn,
 177                                      unsigned int *data)
 178 {
 179         unsigned int chan = CR_CHAN(insn->chanspec);
 180         unsigned int val;
 181         int i;
 182 
 183         for (i = 0; i < insn->n; i++) {
 184                 /* select encoder channel */
 185                 multiq3_set_ctrl(dev, MULTIQ3_CTRL_EN |
 186                                       MULTIQ3_CTRL_E_CHAN(chan));
 187 
 188                 /* reset the byte pointer */
 189                 outb(MULTIQ3_BP_RESET, dev->iobase + MULTIQ3_ENC_CTRL_REG);
 190 
 191                 /* latch the data */
 192                 outb(MULTIQ3_TRSFRCNTR_OL, dev->iobase + MULTIQ3_ENC_CTRL_REG);
 193 
 194                 /* read the 24-bit encoder data (lsb/mid/msb) */
 195                 val = inb(dev->iobase + MULTIQ3_ENC_DATA_REG);
 196                 val |= (inb(dev->iobase + MULTIQ3_ENC_DATA_REG) << 8);
 197                 val |= (inb(dev->iobase + MULTIQ3_ENC_DATA_REG) << 16);
 198 
 199                 /*
 200                  * Munge the data so that the reset value is in the middle
 201                  * of the maxdata range, i.e.:
 202                  *
 203                  * real value   comedi value
 204                  * 0xffffff     0x7fffff        1 negative count
 205                  * 0x000000     0x800000        reset value
 206                  * 0x000001     0x800001        1 positive count
 207                  *
 208                  * It's possible for the 24-bit counter to overflow but it
 209                  * would normally take _quite_ a few turns. A 2000 line
 210                  * encoder in quadrature results in 8000 counts/rev. So about
 211                  * 1048 turns in either direction can be measured without
 212                  * an overflow.
 213                  */
 214                 data[i] = (val + ((s->maxdata + 1) >> 1)) & s->maxdata;
 215         }
 216 
 217         return insn->n;
 218 }
 219 
 220 static void multiq3_encoder_reset(struct comedi_device *dev,
 221                                   unsigned int chan)
 222 {
 223         multiq3_set_ctrl(dev, MULTIQ3_CTRL_EN | MULTIQ3_CTRL_E_CHAN(chan));
 224         outb(MULTIQ3_EFLAG_RESET, dev->iobase + MULTIQ3_ENC_CTRL_REG);
 225         outb(MULTIQ3_BP_RESET, dev->iobase + MULTIQ3_ENC_CTRL_REG);
 226         outb(MULTIQ3_CLOCK_DATA, dev->iobase + MULTIQ3_ENC_DATA_REG);
 227         outb(MULTIQ3_CLOCK_SETUP, dev->iobase + MULTIQ3_ENC_CTRL_REG);
 228         outb(MULTIQ3_INPUT_SETUP, dev->iobase + MULTIQ3_ENC_CTRL_REG);
 229         outb(MULTIQ3_QUAD_X4, dev->iobase + MULTIQ3_ENC_CTRL_REG);
 230         outb(MULTIQ3_CNTR_RESET, dev->iobase + MULTIQ3_ENC_CTRL_REG);
 231 }
 232 
 233 static int multiq3_encoder_insn_config(struct comedi_device *dev,
 234                                        struct comedi_subdevice *s,
 235                                        struct comedi_insn *insn,
 236                                        unsigned int *data)
 237 {
 238         unsigned int chan = CR_CHAN(insn->chanspec);
 239 
 240         switch (data[0]) {
 241         case INSN_CONFIG_RESET:
 242                 multiq3_encoder_reset(dev, chan);
 243                 break;
 244         default:
 245                 return -EINVAL;
 246         }
 247 
 248         return insn->n;
 249 }
 250 
 251 static int multiq3_attach(struct comedi_device *dev,
 252                           struct comedi_devconfig *it)
 253 {
 254         struct comedi_subdevice *s;
 255         int ret;
 256         int i;
 257 
 258         ret = comedi_request_region(dev, it->options[0], 0x10);
 259         if (ret)
 260                 return ret;
 261 
 262         ret = comedi_alloc_subdevices(dev, 5);
 263         if (ret)
 264                 return ret;
 265 
 266         /* Analog Input subdevice */
 267         s = &dev->subdevices[0];
 268         s->type         = COMEDI_SUBD_AI;
 269         s->subdev_flags = SDF_READABLE | SDF_GROUND;
 270         s->n_chan       = 8;
 271         s->maxdata      = 0x1fff;
 272         s->range_table  = &range_bipolar5;
 273         s->insn_read    = multiq3_ai_insn_read;
 274 
 275         /* Analog Output subdevice */
 276         s = &dev->subdevices[1];
 277         s->type         = COMEDI_SUBD_AO;
 278         s->subdev_flags = SDF_WRITABLE;
 279         s->n_chan       = 8;
 280         s->maxdata      = 0x0fff;
 281         s->range_table  = &range_bipolar5;
 282         s->insn_write   = multiq3_ao_insn_write;
 283 
 284         ret = comedi_alloc_subdev_readback(s);
 285         if (ret)
 286                 return ret;
 287 
 288         /* Digital Input subdevice */
 289         s = &dev->subdevices[2];
 290         s->type         = COMEDI_SUBD_DI;
 291         s->subdev_flags = SDF_READABLE;
 292         s->n_chan       = 16;
 293         s->maxdata      = 1;
 294         s->range_table  = &range_digital;
 295         s->insn_bits    = multiq3_di_insn_bits;
 296 
 297         /* Digital Output subdevice */
 298         s = &dev->subdevices[3];
 299         s->type         = COMEDI_SUBD_DO;
 300         s->subdev_flags = SDF_WRITABLE;
 301         s->n_chan       = 16;
 302         s->maxdata      = 1;
 303         s->range_table  = &range_digital;
 304         s->insn_bits    = multiq3_do_insn_bits;
 305 
 306         /* Encoder (Counter) subdevice */
 307         s = &dev->subdevices[4];
 308         s->type         = COMEDI_SUBD_COUNTER;
 309         s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
 310         s->n_chan       = it->options[2] * 2;
 311         s->maxdata      = 0x00ffffff;
 312         s->range_table  = &range_unknown;
 313         s->insn_read    = multiq3_encoder_insn_read;
 314         s->insn_config  = multiq3_encoder_insn_config;
 315 
 316         for (i = 0; i < s->n_chan; i++)
 317                 multiq3_encoder_reset(dev, i);
 318 
 319         return 0;
 320 }
 321 
 322 static struct comedi_driver multiq3_driver = {
 323         .driver_name    = "multiq3",
 324         .module         = THIS_MODULE,
 325         .attach         = multiq3_attach,
 326         .detach         = comedi_legacy_detach,
 327 };
 328 module_comedi_driver(multiq3_driver);
 329 
 330 MODULE_AUTHOR("Comedi http://www.comedi.org");
 331 MODULE_DESCRIPTION("Comedi driver for Quanser Consulting MultiQ-3 board");
 332 MODULE_LICENSE("GPL");

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