linux/drivers/staging/comedi/drivers/das08.c
<<
>>
Prefs
   1/*
   2 *  comedi/drivers/das08.c
   3 *  comedi driver for common DAS08 support (used by ISA/PCI/PCMCIA drivers)
   4 *
   5 *  COMEDI - Linux Control and Measurement Device Interface
   6 *  Copyright (C) 2000 David A. Schleef <ds@schleef.org>
   7 *  Copyright (C) 2001,2002,2003 Frank Mori Hess <fmhess@users.sourceforge.net>
   8 *  Copyright (C) 2004 Salvador E. Tropea <set@users.sf.net> <set@ieee.org>
   9 *
  10 *  This program is free software; you can redistribute it and/or modify
  11 *  it under the terms of the GNU General Public License as published by
  12 *  the Free Software Foundation; either version 2 of the License, or
  13 *  (at your option) any later version.
  14 *
  15 *  This program is distributed in the hope that it will be useful,
  16 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  17 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18 *  GNU General Public License for more details.
  19 */
  20
  21/*
  22 * Driver: das08
  23 * Description: DAS-08 compatible boards
  24 * Devices: various, see das08_isa, das08_cs, and das08_pci drivers
  25 * Author: Warren Jasper, ds, Frank Hess
  26 * Updated: Fri, 31 Aug 2012 19:19:06 +0100
  27 * Status: works
  28 *
  29 * This driver is used by the das08_isa, das08_cs, and das08_pci
  30 * drivers to provide the common support for the DAS-08 hardware.
  31 *
  32 * The driver doesn't support asynchronous commands, since the
  33 * cheap das08 hardware doesn't really support them.
  34 */
  35
  36#include <linux/module.h>
  37
  38#include "../comedidev.h"
  39
  40#include "8255.h"
  41#include "8253.h"
  42#include "das08.h"
  43
  44/*
  45    cio-das08.pdf
  46
  47  "isa-das08"
  48
  49  0     a/d bits 0-3            start 8 bit
  50  1     a/d bits 4-11           start 12 bit
  51  2     eoc, ip1-3, irq, mux    op1-4, inte, mux
  52  3     unused                  unused
  53  4567  8254
  54  89ab  8255
  55
  56  requires hard-wiring for async ai
  57
  58*/
  59
  60#define DAS08_LSB               0
  61#define DAS08_MSB               1
  62#define DAS08_TRIG_12BIT        1
  63#define DAS08_STATUS            2
  64#define   DAS08_EOC                     (1<<7)
  65#define   DAS08_IRQ                     (1<<3)
  66#define   DAS08_IP(x)                   (((x)>>4)&0x7)
  67#define DAS08_CONTROL           2
  68#define   DAS08_MUX_MASK        0x7
  69#define   DAS08_MUX(x)          ((x) & DAS08_MUX_MASK)
  70#define   DAS08_INTE                    (1<<3)
  71#define   DAS08_DO_MASK         0xf0
  72#define   DAS08_OP(x)           (((x) << 4) & DAS08_DO_MASK)
  73
  74/*
  75    cio-das08jr.pdf
  76
  77  "das08/jr-ao"
  78
  79  0     a/d bits 0-3            unused
  80  1     a/d bits 4-11           start 12 bit
  81  2     eoc, mux                mux
  82  3     di                      do
  83  4     unused                  ao0_lsb
  84  5     unused                  ao0_msb
  85  6     unused                  ao1_lsb
  86  7     unused                  ao1_msb
  87
  88*/
  89
  90#define DAS08JR_DIO             3
  91#define DAS08JR_AO_LSB(x)       ((x) ? 6 : 4)
  92#define DAS08JR_AO_MSB(x)       ((x) ? 7 : 5)
  93
  94/*
  95    cio-das08_aox.pdf
  96
  97  "das08-aoh"
  98  "das08-aol"
  99  "das08-aom"
 100
 101  0     a/d bits 0-3            start 8 bit
 102  1     a/d bits 4-11           start 12 bit
 103  2     eoc, ip1-3, irq, mux    op1-4, inte, mux
 104  3     mux, gain status        gain control
 105  4567  8254
 106  8     unused                  ao0_lsb
 107  9     unused                  ao0_msb
 108  a     unused                  ao1_lsb
 109  b     unused                  ao1_msb
 110  89ab
 111  cdef  8255
 112*/
 113
 114#define DAS08AO_GAIN_CONTROL    3
 115#define DAS08AO_GAIN_STATUS     3
 116
 117#define DAS08AO_AO_LSB(x)       ((x) ? 0xa : 8)
 118#define DAS08AO_AO_MSB(x)       ((x) ? 0xb : 9)
 119#define DAS08AO_AO_UPDATE       8
 120
 121/* gainlist same as _pgx_ below */
 122
 123static const struct comedi_lrange range_das08_pgl = { 9, {
 124                                                          BIP_RANGE(10),
 125                                                          BIP_RANGE(5),
 126                                                          BIP_RANGE(2.5),
 127                                                          BIP_RANGE(1.25),
 128                                                          BIP_RANGE(0.625),
 129                                                          UNI_RANGE(10),
 130                                                          UNI_RANGE(5),
 131                                                          UNI_RANGE(2.5),
 132                                                          UNI_RANGE(1.25)
 133                                                          }
 134};
 135
 136static const struct comedi_lrange range_das08_pgh = { 12, {
 137                                                           BIP_RANGE(10),
 138                                                           BIP_RANGE(5),
 139                                                           BIP_RANGE(1),
 140                                                           BIP_RANGE(0.5),
 141                                                           BIP_RANGE(0.1),
 142                                                           BIP_RANGE(0.05),
 143                                                           BIP_RANGE(0.01),
 144                                                           BIP_RANGE(0.005),
 145                                                           UNI_RANGE(10),
 146                                                           UNI_RANGE(1),
 147                                                           UNI_RANGE(0.1),
 148                                                           UNI_RANGE(0.01),
 149                                                           }
 150};
 151
 152static const struct comedi_lrange range_das08_pgm = { 9, {
 153                                                          BIP_RANGE(10),
 154                                                          BIP_RANGE(5),
 155                                                          BIP_RANGE(0.5),
 156                                                          BIP_RANGE(0.05),
 157                                                          BIP_RANGE(0.01),
 158                                                          UNI_RANGE(10),
 159                                                          UNI_RANGE(1),
 160                                                          UNI_RANGE(0.1),
 161                                                          UNI_RANGE(0.01)
 162                                                          }
 163};                              /*
 164                                   cio-das08jr.pdf
 165
 166                                   "das08/jr-ao"
 167
 168                                   0 a/d bits 0-3            unused
 169                                   1 a/d bits 4-11           start 12 bit
 170                                   2 eoc, mux                mux
 171                                   3 di                      do
 172                                   4 unused                  ao0_lsb
 173                                   5 unused                  ao0_msb
 174                                   6 unused                  ao1_lsb
 175                                   7 unused                  ao1_msb
 176
 177                                 */
 178
 179static const struct comedi_lrange *const das08_ai_lranges[] = {
 180        &range_unknown,
 181        &range_bipolar5,
 182        &range_das08_pgh,
 183        &range_das08_pgl,
 184        &range_das08_pgm,
 185};
 186
 187static const int das08_pgh_gainlist[] = {
 188        8, 0, 10, 2, 12, 4, 14, 6, 1, 3, 5, 7
 189};
 190static const int das08_pgl_gainlist[] = { 8, 0, 2, 4, 6, 1, 3, 5, 7 };
 191static const int das08_pgm_gainlist[] = { 8, 0, 10, 12, 14, 9, 11, 13, 15 };
 192
 193static const int *const das08_gainlists[] = {
 194        NULL,
 195        NULL,
 196        das08_pgh_gainlist,
 197        das08_pgl_gainlist,
 198        das08_pgm_gainlist,
 199};
 200
 201#define TIMEOUT 100000
 202
 203static int das08_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
 204                          struct comedi_insn *insn, unsigned int *data)
 205{
 206        const struct das08_board_struct *thisboard = comedi_board(dev);
 207        struct das08_private_struct *devpriv = dev->private;
 208        int i, n;
 209        int chan;
 210        int range;
 211        int lsb, msb;
 212
 213        chan = CR_CHAN(insn->chanspec);
 214        range = CR_RANGE(insn->chanspec);
 215
 216        /* clear crap */
 217        inb(dev->iobase + DAS08_LSB);
 218        inb(dev->iobase + DAS08_MSB);
 219
 220        /* set multiplexer */
 221        /*  lock to prevent race with digital output */
 222        spin_lock(&dev->spinlock);
 223        devpriv->do_mux_bits &= ~DAS08_MUX_MASK;
 224        devpriv->do_mux_bits |= DAS08_MUX(chan);
 225        outb(devpriv->do_mux_bits, dev->iobase + DAS08_CONTROL);
 226        spin_unlock(&dev->spinlock);
 227
 228        if (s->range_table->length > 1) {
 229                /* set gain/range */
 230                range = CR_RANGE(insn->chanspec);
 231                outb(devpriv->pg_gainlist[range],
 232                     dev->iobase + DAS08AO_GAIN_CONTROL);
 233        }
 234
 235        for (n = 0; n < insn->n; n++) {
 236                /* clear over-range bits for 16-bit boards */
 237                if (thisboard->ai_nbits == 16)
 238                        if (inb(dev->iobase + DAS08_MSB) & 0x80)
 239                                dev_info(dev->class_dev, "over-range\n");
 240
 241                /* trigger conversion */
 242                outb_p(0, dev->iobase + DAS08_TRIG_12BIT);
 243
 244                for (i = 0; i < TIMEOUT; i++) {
 245                        if (!(inb(dev->iobase + DAS08_STATUS) & DAS08_EOC))
 246                                break;
 247                }
 248                if (i == TIMEOUT) {
 249                        dev_err(dev->class_dev, "timeout\n");
 250                        return -ETIME;
 251                }
 252                msb = inb(dev->iobase + DAS08_MSB);
 253                lsb = inb(dev->iobase + DAS08_LSB);
 254                if (thisboard->ai_encoding == das08_encode12) {
 255                        data[n] = (lsb >> 4) | (msb << 4);
 256                } else if (thisboard->ai_encoding == das08_pcm_encode12) {
 257                        data[n] = (msb << 8) + lsb;
 258                } else if (thisboard->ai_encoding == das08_encode16) {
 259                        /* FPOS 16-bit boards are sign-magnitude */
 260                        if (msb & 0x80)
 261                                data[n] = (1 << 15) | lsb | ((msb & 0x7f) << 8);
 262                        else
 263                                data[n] = (1 << 15) - (lsb | (msb & 0x7f) << 8);
 264                } else {
 265                        comedi_error(dev, "bug! unknown ai encoding");
 266                        return -1;
 267                }
 268        }
 269
 270        return n;
 271}
 272
 273static int das08_di_rbits(struct comedi_device *dev, struct comedi_subdevice *s,
 274                          struct comedi_insn *insn, unsigned int *data)
 275{
 276        data[0] = 0;
 277        data[1] = DAS08_IP(inb(dev->iobase + DAS08_STATUS));
 278
 279        return insn->n;
 280}
 281
 282static int das08_do_wbits(struct comedi_device *dev,
 283                          struct comedi_subdevice *s,
 284                          struct comedi_insn *insn,
 285                          unsigned int *data)
 286{
 287        struct das08_private_struct *devpriv = dev->private;
 288
 289        if (comedi_dio_update_state(s, data)) {
 290                /* prevent race with setting of analog input mux */
 291                spin_lock(&dev->spinlock);
 292                devpriv->do_mux_bits &= ~DAS08_DO_MASK;
 293                devpriv->do_mux_bits |= DAS08_OP(s->state);
 294                outb(devpriv->do_mux_bits, dev->iobase + DAS08_CONTROL);
 295                spin_unlock(&dev->spinlock);
 296        }
 297
 298        data[1] = s->state;
 299
 300        return insn->n;
 301}
 302
 303static int das08jr_di_rbits(struct comedi_device *dev,
 304                            struct comedi_subdevice *s,
 305                            struct comedi_insn *insn, unsigned int *data)
 306{
 307        data[0] = 0;
 308        data[1] = inb(dev->iobase + DAS08JR_DIO);
 309
 310        return insn->n;
 311}
 312
 313static int das08jr_do_wbits(struct comedi_device *dev,
 314                            struct comedi_subdevice *s,
 315                            struct comedi_insn *insn,
 316                            unsigned int *data)
 317{
 318        if (comedi_dio_update_state(s, data))
 319                outb(s->state, dev->iobase + DAS08JR_DIO);
 320
 321        data[1] = s->state;
 322
 323        return insn->n;
 324}
 325
 326static void das08_ao_set_data(struct comedi_device *dev,
 327                              unsigned int chan, unsigned int data)
 328{
 329        const struct das08_board_struct *thisboard = comedi_board(dev);
 330        struct das08_private_struct *devpriv = dev->private;
 331        unsigned char lsb;
 332        unsigned char msb;
 333
 334        lsb = data & 0xff;
 335        msb = (data >> 8) & 0xff;
 336        if (thisboard->is_jr) {
 337                outb(lsb, dev->iobase + DAS08JR_AO_LSB(chan));
 338                outb(msb, dev->iobase + DAS08JR_AO_MSB(chan));
 339                /* load DACs */
 340                inb(dev->iobase + DAS08JR_DIO);
 341        } else {
 342                outb(lsb, dev->iobase + DAS08AO_AO_LSB(chan));
 343                outb(msb, dev->iobase + DAS08AO_AO_MSB(chan));
 344                /* load DACs */
 345                inb(dev->iobase + DAS08AO_AO_UPDATE);
 346        }
 347        devpriv->ao_readback[chan] = data;
 348}
 349
 350static void das08_ao_initialize(struct comedi_device *dev,
 351                                struct comedi_subdevice *s)
 352{
 353        int n;
 354        unsigned int data;
 355
 356        data = s->maxdata / 2;  /* should be about 0 volts */
 357        for (n = 0; n < s->n_chan; n++)
 358                das08_ao_set_data(dev, n, data);
 359}
 360
 361static int das08_ao_winsn(struct comedi_device *dev,
 362                          struct comedi_subdevice *s,
 363                          struct comedi_insn *insn, unsigned int *data)
 364{
 365        unsigned int n;
 366        unsigned int chan;
 367
 368        chan = CR_CHAN(insn->chanspec);
 369
 370        for (n = 0; n < insn->n; n++)
 371                das08_ao_set_data(dev, chan, *data);
 372
 373        return n;
 374}
 375
 376static int das08_ao_rinsn(struct comedi_device *dev,
 377                          struct comedi_subdevice *s,
 378                          struct comedi_insn *insn, unsigned int *data)
 379{
 380        struct das08_private_struct *devpriv = dev->private;
 381        unsigned int n;
 382        unsigned int chan;
 383
 384        chan = CR_CHAN(insn->chanspec);
 385
 386        for (n = 0; n < insn->n; n++)
 387                data[n] = devpriv->ao_readback[chan];
 388
 389        return n;
 390}
 391
 392static void i8254_initialize(struct comedi_device *dev)
 393{
 394        const struct das08_board_struct *thisboard = comedi_board(dev);
 395        unsigned long i8254_iobase = dev->iobase + thisboard->i8254_offset;
 396        unsigned int mode = I8254_MODE0 | I8254_BINARY;
 397        int i;
 398
 399        for (i = 0; i < 3; ++i)
 400                i8254_set_mode(i8254_iobase, 0, i, mode);
 401}
 402
 403static int das08_counter_read(struct comedi_device *dev,
 404                              struct comedi_subdevice *s,
 405                              struct comedi_insn *insn, unsigned int *data)
 406{
 407        const struct das08_board_struct *thisboard = comedi_board(dev);
 408        unsigned long i8254_iobase = dev->iobase + thisboard->i8254_offset;
 409        int chan = insn->chanspec;
 410
 411        data[0] = i8254_read(i8254_iobase, 0, chan);
 412        return 1;
 413}
 414
 415static int das08_counter_write(struct comedi_device *dev,
 416                               struct comedi_subdevice *s,
 417                               struct comedi_insn *insn, unsigned int *data)
 418{
 419        const struct das08_board_struct *thisboard = comedi_board(dev);
 420        unsigned long i8254_iobase = dev->iobase + thisboard->i8254_offset;
 421        int chan = insn->chanspec;
 422
 423        i8254_write(i8254_iobase, 0, chan, data[0]);
 424        return 1;
 425}
 426
 427static int das08_counter_config(struct comedi_device *dev,
 428                                struct comedi_subdevice *s,
 429                                struct comedi_insn *insn, unsigned int *data)
 430{
 431        const struct das08_board_struct *thisboard = comedi_board(dev);
 432        unsigned long i8254_iobase = dev->iobase + thisboard->i8254_offset;
 433        int chan = insn->chanspec;
 434
 435        switch (data[0]) {
 436        case INSN_CONFIG_SET_COUNTER_MODE:
 437                i8254_set_mode(i8254_iobase, 0, chan, data[1]);
 438                break;
 439        case INSN_CONFIG_8254_READ_STATUS:
 440                data[1] = i8254_status(i8254_iobase, 0, chan);
 441                break;
 442        default:
 443                return -EINVAL;
 444                break;
 445        }
 446        return 2;
 447}
 448
 449int das08_common_attach(struct comedi_device *dev, unsigned long iobase)
 450{
 451        const struct das08_board_struct *thisboard = comedi_board(dev);
 452        struct das08_private_struct *devpriv = dev->private;
 453        struct comedi_subdevice *s;
 454        int ret;
 455
 456        dev->iobase = iobase;
 457
 458        dev->board_name = thisboard->name;
 459
 460        ret = comedi_alloc_subdevices(dev, 6);
 461        if (ret)
 462                return ret;
 463
 464        s = &dev->subdevices[0];
 465        /* ai */
 466        if (thisboard->ai_nbits) {
 467                s->type = COMEDI_SUBD_AI;
 468                /* XXX some boards actually have differential
 469                 * inputs instead of single ended.
 470                 * The driver does nothing with arefs though,
 471                 * so it's no big deal.
 472                 */
 473                s->subdev_flags = SDF_READABLE | SDF_GROUND;
 474                s->n_chan = 8;
 475                s->maxdata = (1 << thisboard->ai_nbits) - 1;
 476                s->range_table = das08_ai_lranges[thisboard->ai_pg];
 477                s->insn_read = das08_ai_rinsn;
 478                devpriv->pg_gainlist = das08_gainlists[thisboard->ai_pg];
 479        } else {
 480                s->type = COMEDI_SUBD_UNUSED;
 481        }
 482
 483        s = &dev->subdevices[1];
 484        /* ao */
 485        if (thisboard->ao_nbits) {
 486                s->type = COMEDI_SUBD_AO;
 487                s->subdev_flags = SDF_WRITABLE;
 488                s->n_chan = 2;
 489                s->maxdata = (1 << thisboard->ao_nbits) - 1;
 490                s->range_table = &range_bipolar5;
 491                s->insn_write = das08_ao_winsn;
 492                s->insn_read = das08_ao_rinsn;
 493                das08_ao_initialize(dev, s);
 494        } else {
 495                s->type = COMEDI_SUBD_UNUSED;
 496        }
 497
 498        s = &dev->subdevices[2];
 499        /* di */
 500        if (thisboard->di_nchan) {
 501                s->type = COMEDI_SUBD_DI;
 502                s->subdev_flags = SDF_READABLE;
 503                s->n_chan = thisboard->di_nchan;
 504                s->maxdata = 1;
 505                s->range_table = &range_digital;
 506                s->insn_bits =
 507                        thisboard->is_jr ? das08jr_di_rbits : das08_di_rbits;
 508        } else {
 509                s->type = COMEDI_SUBD_UNUSED;
 510        }
 511
 512        s = &dev->subdevices[3];
 513        /* do */
 514        if (thisboard->do_nchan) {
 515                s->type = COMEDI_SUBD_DO;
 516                s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
 517                s->n_chan = thisboard->do_nchan;
 518                s->maxdata = 1;
 519                s->range_table = &range_digital;
 520                s->insn_bits =
 521                        thisboard->is_jr ? das08jr_do_wbits : das08_do_wbits;
 522        } else {
 523                s->type = COMEDI_SUBD_UNUSED;
 524        }
 525
 526        s = &dev->subdevices[4];
 527        /* 8255 */
 528        if (thisboard->i8255_offset != 0) {
 529                subdev_8255_init(dev, s, NULL, (unsigned long)(dev->iobase +
 530                                                               thisboard->
 531                                                               i8255_offset));
 532        } else {
 533                s->type = COMEDI_SUBD_UNUSED;
 534        }
 535
 536        s = &dev->subdevices[5];
 537        /* 8254 */
 538        if (thisboard->i8254_offset != 0) {
 539                s->type = COMEDI_SUBD_COUNTER;
 540                s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
 541                s->n_chan = 3;
 542                s->maxdata = 0xFFFF;
 543                s->insn_read = das08_counter_read;
 544                s->insn_write = das08_counter_write;
 545                s->insn_config = das08_counter_config;
 546                i8254_initialize(dev);
 547        } else {
 548                s->type = COMEDI_SUBD_UNUSED;
 549        }
 550
 551        return 0;
 552}
 553EXPORT_SYMBOL_GPL(das08_common_attach);
 554
 555static int __init das08_init(void)
 556{
 557        return 0;
 558}
 559module_init(das08_init);
 560
 561static void __exit das08_exit(void)
 562{
 563}
 564module_exit(das08_exit);
 565
 566MODULE_AUTHOR("Comedi http://www.comedi.org");
 567MODULE_DESCRIPTION("Comedi low-level driver");
 568MODULE_LICENSE("GPL");
 569