linux/drivers/staging/comedi/drivers/adv_pci1723.c
<<
>>
Prefs
   1/*******************************************************************************
   2   comedi/drivers/pci1723.c
   3
   4   COMEDI - Linux Control and Measurement Device Interface
   5   Copyright (C) 2000 David A. Schleef <ds@schleef.org>
   6
   7   This program is free software; you can redistribute it and/or modify
   8   it under the terms of the GNU General Public License as published by
   9   the Free Software Foundation; either version 2 of the License, or
  10   (at your option) any later version.
  11
  12   This program is distributed in the hope that it will be useful,
  13   but WITHOUT ANY WARRANTY; without even the implied warranty of
  14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15   GNU General Public License for more details.
  16
  17   You should have received a copy of the GNU General Public License
  18   along with this program; if not, write to the Free Software
  19   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20
  21*******************************************************************************/
  22/*
  23Driver: adv_pci1723
  24Description: Advantech PCI-1723
  25Author: yonggang <rsmgnu@gmail.com>, Ian Abbott <abbotti@mev.co.uk>
  26Devices: [Advantech] PCI-1723 (adv_pci1723)
  27Updated: Mon, 14 Apr 2008 15:12:56 +0100
  28Status: works
  29
  30Configuration Options:
  31  [0] - PCI bus of device (optional)
  32  [1] - PCI slot of device (optional)
  33
  34  If bus/slot is not specified, the first supported
  35  PCI device found will be used.
  36
  37Subdevice 0 is 8-channel AO, 16-bit, range +/- 10 V.
  38
  39Subdevice 1 is 16-channel DIO.  The channels are configurable as input or
  40output in 2 groups (0 to 7, 8 to 15).  Configuring any channel implicitly
  41configures all channels in the same group.
  42
  43TODO:
  44
  451. Add the two milliamp ranges to the AO subdevice (0 to 20 mA, 4 to 20 mA).
  462. Read the initial ranges and values of the AO subdevice at start-up instead
  47   of reinitializing them.
  483. Implement calibration.
  49*/
  50
  51#include "../comedidev.h"
  52
  53#include "comedi_pci.h"
  54
  55#define PCI_VENDOR_ID_ADVANTECH         0x13fe  /* Advantech PCI vendor ID */
  56
  57/* hardware types of the cards */
  58#define TYPE_PCI1723 0
  59
  60#define IORANGE_1723  0x2A
  61
  62/* all the registers for the pci1723 board */
  63#define PCI1723_DA(N)   ((N)<<1)        /* W: D/A register N (0 to 7) */
  64
  65#define PCI1723_SYN_SET  0x12           /* synchronized set register */
  66#define PCI1723_ALL_CHNNELE_SYN_STROBE  0x12
  67                                        /* synchronized status register */
  68
  69#define PCI1723_RANGE_CALIBRATION_MODE 0x14
  70                                        /* range and calibration mode */
  71#define PCI1723_RANGE_CALIBRATION_STATUS 0x14
  72                                        /* range and calibration status */
  73
  74#define PCI1723_CONTROL_CMD_CALIBRATION_FUN 0x16
  75                                                /*
  76                                                 * SADC control command for
  77                                                 * calibration function
  78                                                 */
  79#define PCI1723_STATUS_CMD_CALIBRATION_FUN 0x16
  80                                                /*
  81                                                 * SADC control status for
  82                                                 * calibration function
  83                                                 */
  84
  85#define PCI1723_CALIBRATION_PARA_STROBE 0x18
  86                                        /* Calibration parameter strobe */
  87
  88#define PCI1723_DIGITAL_IO_PORT_SET 0x1A        /* Digital I/O port setting */
  89#define PCI1723_DIGITAL_IO_PORT_MODE 0x1A       /* Digital I/O port mode */
  90
  91#define PCI1723_WRITE_DIGITAL_OUTPUT_CMD 0x1C
  92                                        /* Write digital output command */
  93#define PCI1723_READ_DIGITAL_INPUT_DATA 0x1C    /* Read digital input data */
  94
  95#define PCI1723_WRITE_CAL_CMD 0x1E              /* Write calibration command */
  96#define PCI1723_READ_CAL_STATUS 0x1E            /* Read calibration status */
  97
  98#define PCI1723_SYN_STROBE 0x20                 /* Synchronized strobe */
  99
 100#define PCI1723_RESET_ALL_CHN_STROBE 0x22
 101                                        /* Reset all D/A channels strobe */
 102
 103#define PCI1723_RESET_CAL_CONTROL_STROBE 0x24
 104                                                /*
 105                                                 * Reset the calibration
 106                                                 * controller strobe
 107                                                 */
 108
 109#define PCI1723_CHANGE_CHA_OUTPUT_TYPE_STROBE 0x26
 110                                                /*
 111                                                 * Change D/A channels output
 112                                                 * type strobe
 113                                                 */
 114
 115#define PCI1723_SELECT_CALIBRATION 0x28 /* Select the calibration Ref_V */
 116
 117/* static unsigned short pci_list_builded=0;      =1 list of card is know */
 118
 119static const struct comedi_lrange range_pci1723 = { 1, {
 120                                                        BIP_RANGE(10)
 121                                                        }
 122};
 123
 124/*
 125 * Board descriptions for pci1723 boards.
 126 */
 127struct pci1723_board {
 128        const char *name;
 129        int vendor_id;          /* PCI vendor a device ID of card */
 130        int device_id;
 131        int iorange;
 132        char cardtype;
 133        int n_aochan;           /* num of D/A chans */
 134        int n_diochan;          /* num of DIO chans */
 135        int ao_maxdata;         /* resolution of D/A */
 136        const struct comedi_lrange *rangelist_ao;       /* rangelist for D/A */
 137};
 138
 139static const struct pci1723_board boardtypes[] = {
 140        {
 141         .name = "pci1723",
 142         .vendor_id = PCI_VENDOR_ID_ADVANTECH,
 143         .device_id = 0x1723,
 144         .iorange = IORANGE_1723,
 145         .cardtype = TYPE_PCI1723,
 146         .n_aochan = 8,
 147         .n_diochan = 16,
 148         .ao_maxdata = 0xffff,
 149         .rangelist_ao = &range_pci1723,
 150         },
 151};
 152
 153/*
 154 * This is used by modprobe to translate PCI IDs to drivers.
 155 * Should only be used for PCI and ISA-PnP devices
 156 */
 157static DEFINE_PCI_DEVICE_TABLE(pci1723_pci_table) = {
 158        { PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1723) },
 159        { 0 }
 160};
 161
 162MODULE_DEVICE_TABLE(pci, pci1723_pci_table);
 163
 164/*
 165 * The struct comedi_driver structure tells the Comedi core module
 166 * which functions to call to configure/deconfigure (attach/detach)
 167 * the board, and also about the kernel module that contains
 168 * the device code.
 169 */
 170static int pci1723_attach(struct comedi_device *dev,
 171                          struct comedi_devconfig *it);
 172static int pci1723_detach(struct comedi_device *dev);
 173
 174#define n_boardtypes (sizeof(boardtypes)/sizeof(struct pci1723_board))
 175
 176static struct comedi_driver driver_pci1723 = {
 177        .driver_name = "adv_pci1723",
 178        .module = THIS_MODULE,
 179        .attach = pci1723_attach,
 180        .detach = pci1723_detach,
 181};
 182
 183/* This structure is for data unique to this hardware driver. */
 184struct pci1723_private {
 185        int valid;              /* card is usable; */
 186
 187        struct pci_dev *pcidev;
 188        unsigned char da_range[8];      /* D/A output range for each channel */
 189
 190        short ao_data[8];       /* data output buffer */
 191};
 192
 193/* The following macro to make it easy to access the private structure. */
 194#define devpriv ((struct pci1723_private *)dev->private)
 195
 196#define this_board boardtypes
 197
 198/*
 199 * The pci1723 card reset;
 200 */
 201static int pci1723_reset(struct comedi_device *dev)
 202{
 203        int i;
 204        DPRINTK("adv_pci1723 EDBG: BGN: pci1723_reset(...)\n");
 205
 206        outw(0x01, dev->iobase + PCI1723_SYN_SET);
 207                                               /* set synchronous output mode */
 208
 209        for (i = 0; i < 8; i++) {
 210                /* set all outputs to 0V */
 211                devpriv->ao_data[i] = 0x8000;
 212                outw(devpriv->ao_data[i], dev->iobase + PCI1723_DA(i));
 213                /* set all ranges to +/- 10V */
 214                devpriv->da_range[i] = 0;
 215                outw(((devpriv->da_range[i] << 4) | i),
 216                     PCI1723_RANGE_CALIBRATION_MODE);
 217        }
 218
 219        outw(0, dev->iobase + PCI1723_CHANGE_CHA_OUTPUT_TYPE_STROBE);
 220                                                            /* update ranges */
 221        outw(0, dev->iobase + PCI1723_SYN_STROBE);          /* update outputs */
 222
 223        /* set asynchronous output mode */
 224        outw(0, dev->iobase + PCI1723_SYN_SET);
 225
 226        DPRINTK("adv_pci1723 EDBG: END: pci1723_reset(...)\n");
 227        return 0;
 228}
 229
 230static int pci1723_insn_read_ao(struct comedi_device *dev,
 231                                struct comedi_subdevice *s,
 232                                struct comedi_insn *insn, unsigned int *data)
 233{
 234        int n, chan;
 235
 236        chan = CR_CHAN(insn->chanspec);
 237        DPRINTK(" adv_PCI1723 DEBUG: pci1723_insn_read_ao() -----\n");
 238        for (n = 0; n < insn->n; n++)
 239                data[n] = devpriv->ao_data[chan];
 240
 241        return n;
 242}
 243
 244/*
 245  analog data output;
 246*/
 247static int pci1723_ao_write_winsn(struct comedi_device *dev,
 248                                  struct comedi_subdevice *s,
 249                                  struct comedi_insn *insn, unsigned int *data)
 250{
 251        int n, chan;
 252        chan = CR_CHAN(insn->chanspec);
 253
 254        DPRINTK("PCI1723: the pci1723_ao_write_winsn() ------\n");
 255
 256        for (n = 0; n < insn->n; n++) {
 257
 258                devpriv->ao_data[chan] = data[n];
 259                outw(data[n], dev->iobase + PCI1723_DA(chan));
 260        }
 261
 262        return n;
 263}
 264
 265/*
 266  digital i/o config/query
 267*/
 268static int pci1723_dio_insn_config(struct comedi_device *dev,
 269                                   struct comedi_subdevice *s,
 270                                   struct comedi_insn *insn, unsigned int *data)
 271{
 272        unsigned int mask;
 273        unsigned int bits;
 274        unsigned short dio_mode;
 275
 276        mask = 1 << CR_CHAN(insn->chanspec);
 277        if (mask & 0x00FF)
 278                bits = 0x00FF;
 279        else
 280                bits = 0xFF00;
 281
 282        switch (data[0]) {
 283        case INSN_CONFIG_DIO_INPUT:
 284                s->io_bits &= ~bits;
 285                break;
 286        case INSN_CONFIG_DIO_OUTPUT:
 287                s->io_bits |= bits;
 288                break;
 289        case INSN_CONFIG_DIO_QUERY:
 290                data[1] = (s->io_bits & bits) ? COMEDI_OUTPUT : COMEDI_INPUT;
 291                return insn->n;
 292        default:
 293                return -EINVAL;
 294        }
 295
 296        /* update hardware DIO mode */
 297        dio_mode = 0x0000;      /* low byte output, high byte output */
 298        if ((s->io_bits & 0x00FF) == 0)
 299                dio_mode |= 0x0001;     /* low byte input */
 300        if ((s->io_bits & 0xFF00) == 0)
 301                dio_mode |= 0x0002;     /* high byte input */
 302        outw(dio_mode, dev->iobase + PCI1723_DIGITAL_IO_PORT_SET);
 303        return 1;
 304}
 305
 306/*
 307  digital i/o bits read/write
 308*/
 309static int pci1723_dio_insn_bits(struct comedi_device *dev,
 310                                 struct comedi_subdevice *s,
 311                                 struct comedi_insn *insn, unsigned int *data)
 312{
 313        if (data[0]) {
 314                s->state &= ~data[0];
 315                s->state |= (data[0] & data[1]);
 316                outw(s->state, dev->iobase + PCI1723_WRITE_DIGITAL_OUTPUT_CMD);
 317        }
 318        data[1] = inw(dev->iobase + PCI1723_READ_DIGITAL_INPUT_DATA);
 319        return 2;
 320}
 321
 322/*
 323 * Attach is called by the Comedi core to configure the driver
 324 * for a pci1723 board.
 325 */
 326static int pci1723_attach(struct comedi_device *dev,
 327                          struct comedi_devconfig *it)
 328{
 329        struct comedi_subdevice *s;
 330        int ret, subdev, n_subdevices;
 331        struct pci_dev *pcidev;
 332        unsigned int iobase;
 333        unsigned char pci_bus, pci_slot, pci_func;
 334        int opt_bus, opt_slot;
 335        const char *errstr;
 336
 337        printk(KERN_ERR "comedi%d: adv_pci1723: board=%s",
 338                                                dev->minor, this_board->name);
 339
 340        opt_bus = it->options[0];
 341        opt_slot = it->options[1];
 342
 343        ret = alloc_private(dev, sizeof(struct pci1723_private));
 344        if (ret < 0) {
 345                printk(" - Allocation failed!\n");
 346                return -ENOMEM;
 347        }
 348
 349        /* Look for matching PCI device */
 350        errstr = "not found!";
 351        pcidev = NULL;
 352        while (NULL != (pcidev =
 353                        pci_get_device(PCI_VENDOR_ID_ADVANTECH,
 354                                       this_board->device_id, pcidev))) {
 355                /* Found matching vendor/device. */
 356                if (opt_bus || opt_slot) {
 357                        /* Check bus/slot. */
 358                        if (opt_bus != pcidev->bus->number
 359                            || opt_slot != PCI_SLOT(pcidev->devfn))
 360                                continue;       /* no match */
 361                }
 362                /*
 363                 * Look for device that isn't in use.
 364                 * Enable PCI device and request regions.
 365                 */
 366                if (comedi_pci_enable(pcidev, "adv_pci1723")) {
 367                        errstr =
 368                            "failed to enable PCI device and request regions!";
 369                        continue;
 370                }
 371                break;
 372        }
 373
 374        if (!pcidev) {
 375                if (opt_bus || opt_slot) {
 376                        printk(KERN_ERR " - Card at b:s %d:%d %s\n",
 377                                                     opt_bus, opt_slot, errstr);
 378                } else {
 379                        printk(KERN_ERR " - Card %s\n", errstr);
 380                }
 381                return -EIO;
 382        }
 383
 384        pci_bus = pcidev->bus->number;
 385        pci_slot = PCI_SLOT(pcidev->devfn);
 386        pci_func = PCI_FUNC(pcidev->devfn);
 387        iobase = pci_resource_start(pcidev, 2);
 388
 389        printk(KERN_ERR ", b:s:f=%d:%d:%d, io=0x%4x",
 390                                           pci_bus, pci_slot, pci_func, iobase);
 391
 392        dev->iobase = iobase;
 393
 394        dev->board_name = this_board->name;
 395        devpriv->pcidev = pcidev;
 396
 397        n_subdevices = 0;
 398
 399        if (this_board->n_aochan)
 400                n_subdevices++;
 401        if (this_board->n_diochan)
 402                n_subdevices++;
 403
 404        ret = alloc_subdevices(dev, n_subdevices);
 405        if (ret < 0) {
 406                printk(" - Allocation failed!\n");
 407                return ret;
 408        }
 409
 410        pci1723_reset(dev);
 411        subdev = 0;
 412        if (this_board->n_aochan) {
 413                s = dev->subdevices + subdev;
 414                dev->write_subdev = s;
 415                s->type = COMEDI_SUBD_AO;
 416                s->subdev_flags = SDF_WRITEABLE | SDF_GROUND | SDF_COMMON;
 417                s->n_chan = this_board->n_aochan;
 418                s->maxdata = this_board->ao_maxdata;
 419                s->len_chanlist = this_board->n_aochan;
 420                s->range_table = this_board->rangelist_ao;
 421
 422                s->insn_write = pci1723_ao_write_winsn;
 423                s->insn_read = pci1723_insn_read_ao;
 424
 425                /* read DIO config */
 426                switch (inw(dev->iobase + PCI1723_DIGITAL_IO_PORT_MODE)
 427                                                                       & 0x03) {
 428                case 0x00:      /* low byte output, high byte output */
 429                        s->io_bits = 0xFFFF;
 430                        break;
 431                case 0x01:      /* low byte input, high byte output */
 432                        s->io_bits = 0xFF00;
 433                        break;
 434                case 0x02:      /* low byte output, high byte input */
 435                        s->io_bits = 0x00FF;
 436                        break;
 437                case 0x03:      /* low byte input, high byte input */
 438                        s->io_bits = 0x0000;
 439                        break;
 440                }
 441                /* read DIO port state */
 442                s->state = inw(dev->iobase + PCI1723_READ_DIGITAL_INPUT_DATA);
 443
 444                subdev++;
 445        }
 446
 447        if (this_board->n_diochan) {
 448                s = dev->subdevices + subdev;
 449                s->type = COMEDI_SUBD_DIO;
 450                s->subdev_flags =
 451                    SDF_READABLE | SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
 452                s->n_chan = this_board->n_diochan;
 453                s->maxdata = 1;
 454                s->len_chanlist = this_board->n_diochan;
 455                s->range_table = &range_digital;
 456                s->insn_config = pci1723_dio_insn_config;
 457                s->insn_bits = pci1723_dio_insn_bits;
 458                subdev++;
 459        }
 460
 461        devpriv->valid = 1;
 462
 463        pci1723_reset(dev);
 464
 465        return 0;
 466}
 467
 468/*
 469 * _detach is called to deconfigure a device.  It should deallocate
 470 * resources.
 471 * This function is also called when _attach() fails, so it should be
 472 * careful not to release resources that were not necessarily
 473 * allocated by _attach().  dev->private and dev->subdevices are
 474 * deallocated automatically by the core.
 475 */
 476static int pci1723_detach(struct comedi_device *dev)
 477{
 478        printk(KERN_ERR "comedi%d: pci1723: remove\n", dev->minor);
 479
 480        if (dev->private) {
 481                if (devpriv->valid)
 482                        pci1723_reset(dev);
 483
 484                if (devpriv->pcidev) {
 485                        if (dev->iobase)
 486                                comedi_pci_disable(devpriv->pcidev);
 487                        pci_dev_put(devpriv->pcidev);
 488                }
 489        }
 490
 491        return 0;
 492}
 493
 494/*
 495 * A convenient macro that defines init_module() and cleanup_module(),
 496 * as necessary.
 497 */
 498static int __devinit driver_pci1723_pci_probe(struct pci_dev *dev,
 499                                              const struct pci_device_id *ent)
 500{
 501        return comedi_pci_auto_config(dev, driver_pci1723.driver_name);
 502}
 503
 504static void __devexit driver_pci1723_pci_remove(struct pci_dev *dev)
 505{
 506        comedi_pci_auto_unconfig(dev);
 507}
 508
 509static struct pci_driver driver_pci1723_pci_driver = {
 510        .id_table = pci1723_pci_table,
 511        .probe = &driver_pci1723_pci_probe,
 512        .remove = __devexit_p(&driver_pci1723_pci_remove)
 513};
 514
 515static int __init driver_pci1723_init_module(void)
 516{
 517        int retval;
 518
 519        retval = comedi_driver_register(&driver_pci1723);
 520        if (retval < 0)
 521                return retval;
 522
 523        driver_pci1723_pci_driver.name = (char *)driver_pci1723.driver_name;
 524        return pci_register_driver(&driver_pci1723_pci_driver);
 525}
 526
 527static void __exit driver_pci1723_cleanup_module(void)
 528{
 529        pci_unregister_driver(&driver_pci1723_pci_driver);
 530        comedi_driver_unregister(&driver_pci1723);
 531}
 532
 533module_init(driver_pci1723_init_module);
 534module_exit(driver_pci1723_cleanup_module);
 535
 536MODULE_AUTHOR("Comedi http://www.comedi.org");
 537MODULE_DESCRIPTION("Comedi low-level driver");
 538MODULE_LICENSE("GPL");
 539