linux/drivers/staging/comedi/drivers/adv_pci_dio.c
<<
>>
Prefs
   1/*
   2 * comedi/drivers/adv_pci_dio.c
   3 *
   4 * Author: Michal Dobes <dobes@tesnet.cz>
   5 *
   6 *  Hardware driver for Advantech PCI DIO cards.
   7 */
   8
   9/*
  10 * Driver: adv_pci_dio
  11 * Description: Advantech Digital I/O Cards
  12 * Devices: [Advantech] PCI-1730 (adv_pci_dio), PCI-1733,
  13 *   PCI-1734, PCI-1735U, PCI-1736UP, PCI-1739U, PCI-1750,
  14 *   PCI-1751, PCI-1752, PCI-1753, PCI-1753+PCI-1753E,
  15 *   PCI-1754, PCI-1756, PCI-1762
  16 * Author: Michal Dobes <dobes@tesnet.cz>
  17 * Updated: Mon, 09 Jan 2012 12:40:46 +0000
  18 * Status: untested
  19 *
  20 * Configuration Options: not applicable, uses PCI auto config
  21 */
  22
  23#include <linux/module.h>
  24#include <linux/delay.h>
  25
  26#include "../comedi_pci.h"
  27
  28#include "8255.h"
  29#include "comedi_8254.h"
  30
  31/*
  32 * Register offset definitions
  33 */
  34
  35/* PCI-1730, PCI-1733, PCI-1736 interrupt control registers */
  36#define PCI173X_INT_EN_REG      0x08    /* R/W: enable/disable */
  37#define PCI173X_INT_RF_REG      0x0c    /* R/W: falling/rising edge */
  38#define PCI173X_INT_CLR_REG     0x10    /* R/W: clear */
  39
  40/* PCI-1739U, PCI-1750, PCI1751 interrupt control registers */
  41#define PCI1750_INT_REG         0x20    /* R/W: status/control */
  42
  43/* PCI-1753, PCI-1753E interrupt control registers */
  44#define PCI1753_INT_REG(x)      (0x10 + (x)) /* R/W: control group 0 to 3 */
  45#define PCI1753E_INT_REG(x)     (0x30 + (x)) /* R/W: control group 0 to 3 */
  46
  47/* PCI-1754, PCI-1756 interrupt control registers */
  48#define PCI1754_INT_REG(x)      (0x08 + (x) * 2) /* R/W: control group 0 to 3 */
  49
  50/* PCI-1752, PCI-1756 special registers */
  51#define PCI1752_CFC_REG         0x12    /* R/W: channel freeze function */
  52
  53/* PCI-1762 interrupt control registers */
  54#define PCI1762_INT_REG         0x06    /* R/W: status/control */
  55
  56/* maximum number of subdevice descriptions in the boardinfo */
  57#define PCI_DIO_MAX_DI_SUBDEVS  2       /* 2 x 8/16/32 input channels max */
  58#define PCI_DIO_MAX_DO_SUBDEVS  2       /* 2 x 8/16/32 output channels max */
  59#define PCI_DIO_MAX_DIO_SUBDEVG 2       /* 2 x any number of 8255 devices max */
  60
  61enum pci_dio_boardid {
  62        TYPE_PCI1730,
  63        TYPE_PCI1733,
  64        TYPE_PCI1734,
  65        TYPE_PCI1735,
  66        TYPE_PCI1736,
  67        TYPE_PCI1739,
  68        TYPE_PCI1750,
  69        TYPE_PCI1751,
  70        TYPE_PCI1752,
  71        TYPE_PCI1753,
  72        TYPE_PCI1753E,
  73        TYPE_PCI1754,
  74        TYPE_PCI1756,
  75        TYPE_PCI1762
  76};
  77
  78struct diosubd_data {
  79        int chans;              /*  num of chans or 8255 devices */
  80        unsigned long addr;     /*  PCI address ofset */
  81};
  82
  83struct dio_boardtype {
  84        const char *name;       /*  board name */
  85        int nsubdevs;
  86        struct diosubd_data sdi[PCI_DIO_MAX_DI_SUBDEVS];
  87        struct diosubd_data sdo[PCI_DIO_MAX_DO_SUBDEVS];
  88        struct diosubd_data sdio[PCI_DIO_MAX_DIO_SUBDEVG];
  89        unsigned long id_reg;
  90        unsigned long timer_regbase;
  91        unsigned int is_16bit:1;
  92};
  93
  94static const struct dio_boardtype boardtypes[] = {
  95        [TYPE_PCI1730] = {
  96                .name           = "pci1730",
  97                .nsubdevs       = 5,
  98                .sdi[0]         = { 16, 0x02, },        /* DI 0-15 */
  99                .sdi[1]         = { 16, 0x00, },        /* ISO DI 0-15 */
 100                .sdo[0]         = { 16, 0x02, },        /* DO 0-15 */
 101                .sdo[1]         = { 16, 0x00, },        /* ISO DO 0-15 */
 102                .id_reg         = 0x04,
 103        },
 104        [TYPE_PCI1733] = {
 105                .name           = "pci1733",
 106                .nsubdevs       = 2,
 107                .sdi[1]         = { 32, 0x00, },        /* ISO DI 0-31 */
 108                .id_reg         = 0x04,
 109        },
 110        [TYPE_PCI1734] = {
 111                .name           = "pci1734",
 112                .nsubdevs       = 2,
 113                .sdo[1]         = { 32, 0x00, },        /* ISO DO 0-31 */
 114                .id_reg         = 0x04,
 115        },
 116        [TYPE_PCI1735] = {
 117                .name           = "pci1735",
 118                .nsubdevs       = 4,
 119                .sdi[0]         = { 32, 0x00, },        /* DI 0-31 */
 120                .sdo[0]         = { 32, 0x00, },        /* DO 0-31 */
 121                .id_reg         = 0x08,
 122                .timer_regbase  = 0x04,
 123        },
 124        [TYPE_PCI1736] = {
 125                .name           = "pci1736",
 126                .nsubdevs       = 3,
 127                .sdi[1]         = { 16, 0x00, },        /* ISO DI 0-15 */
 128                .sdo[1]         = { 16, 0x00, },        /* ISO DO 0-15 */
 129                .id_reg         = 0x04,
 130        },
 131        [TYPE_PCI1739] = {
 132                .name           = "pci1739",
 133                .nsubdevs       = 3,
 134                .sdio[0]        = { 2, 0x00, },         /* 8255 DIO */
 135                .id_reg         = 0x08,
 136        },
 137        [TYPE_PCI1750] = {
 138                .name           = "pci1750",
 139                .nsubdevs       = 2,
 140                .sdi[1]         = { 16, 0x00, },        /* ISO DI 0-15 */
 141                .sdo[1]         = { 16, 0x00, },        /* ISO DO 0-15 */
 142        },
 143        [TYPE_PCI1751] = {
 144                .name           = "pci1751",
 145                .nsubdevs       = 3,
 146                .sdio[0]        = { 2, 0x00, },         /* 8255 DIO */
 147                .timer_regbase  = 0x18,
 148        },
 149        [TYPE_PCI1752] = {
 150                .name           = "pci1752",
 151                .nsubdevs       = 3,
 152                .sdo[0]         = { 32, 0x00, },        /* DO 0-31 */
 153                .sdo[1]         = { 32, 0x04, },        /* DO 32-63 */
 154                .id_reg         = 0x10,
 155                .is_16bit       = 1,
 156        },
 157        [TYPE_PCI1753] = {
 158                .name           = "pci1753",
 159                .nsubdevs       = 4,
 160                .sdio[0]        = { 4, 0x00, },         /* 8255 DIO */
 161        },
 162        [TYPE_PCI1753E] = {
 163                .name           = "pci1753e",
 164                .nsubdevs       = 8,
 165                .sdio[0]        = { 4, 0x00, },         /* 8255 DIO */
 166                .sdio[1]        = { 4, 0x20, },         /* 8255 DIO */
 167        },
 168        [TYPE_PCI1754] = {
 169                .name           = "pci1754",
 170                .nsubdevs       = 3,
 171                .sdi[0]         = { 32, 0x00, },        /* DI 0-31 */
 172                .sdi[1]         = { 32, 0x04, },        /* DI 32-63 */
 173                .id_reg         = 0x10,
 174                .is_16bit       = 1,
 175        },
 176        [TYPE_PCI1756] = {
 177                .name           = "pci1756",
 178                .nsubdevs       = 3,
 179                .sdi[1]         = { 32, 0x00, },        /* DI 0-31 */
 180                .sdo[1]         = { 32, 0x04, },        /* DO 0-31 */
 181                .id_reg         = 0x10,
 182                .is_16bit       = 1,
 183        },
 184        [TYPE_PCI1762] = {
 185                .name           = "pci1762",
 186                .nsubdevs       = 3,
 187                .sdi[1]         = { 16, 0x02, },        /* ISO DI 0-15 */
 188                .sdo[1]         = { 16, 0x00, },        /* ISO DO 0-15 */
 189                .id_reg         = 0x04,
 190                .is_16bit       = 1,
 191        },
 192};
 193
 194static int pci_dio_insn_bits_di_b(struct comedi_device *dev,
 195                                  struct comedi_subdevice *s,
 196                                  struct comedi_insn *insn,
 197                                  unsigned int *data)
 198{
 199        unsigned long reg = (unsigned long)s->private;
 200        unsigned long iobase = dev->iobase + reg;
 201
 202        data[1] = inb(iobase);
 203        if (s->n_chan > 8)
 204                data[1] |= (inb(iobase + 1) << 8);
 205        if (s->n_chan > 16)
 206                data[1] |= (inb(iobase + 2) << 16);
 207        if (s->n_chan > 24)
 208                data[1] |= (inb(iobase + 3) << 24);
 209
 210        return insn->n;
 211}
 212
 213static int pci_dio_insn_bits_di_w(struct comedi_device *dev,
 214                                  struct comedi_subdevice *s,
 215                                  struct comedi_insn *insn,
 216                                  unsigned int *data)
 217{
 218        unsigned long reg = (unsigned long)s->private;
 219        unsigned long iobase = dev->iobase + reg;
 220
 221        data[1] = inw(iobase);
 222        if (s->n_chan > 16)
 223                data[1] |= (inw(iobase + 2) << 16);
 224
 225        return insn->n;
 226}
 227
 228static int pci_dio_insn_bits_do_b(struct comedi_device *dev,
 229                                  struct comedi_subdevice *s,
 230                                  struct comedi_insn *insn,
 231                                  unsigned int *data)
 232{
 233        unsigned long reg = (unsigned long)s->private;
 234        unsigned long iobase = dev->iobase + reg;
 235
 236        if (comedi_dio_update_state(s, data)) {
 237                outb(s->state & 0xff, iobase);
 238                if (s->n_chan > 8)
 239                        outb((s->state >> 8) & 0xff, iobase + 1);
 240                if (s->n_chan > 16)
 241                        outb((s->state >> 16) & 0xff, iobase + 2);
 242                if (s->n_chan > 24)
 243                        outb((s->state >> 24) & 0xff, iobase + 3);
 244        }
 245
 246        data[1] = s->state;
 247
 248        return insn->n;
 249}
 250
 251static int pci_dio_insn_bits_do_w(struct comedi_device *dev,
 252                                  struct comedi_subdevice *s,
 253                                  struct comedi_insn *insn,
 254                                  unsigned int *data)
 255{
 256        unsigned long reg = (unsigned long)s->private;
 257        unsigned long iobase = dev->iobase + reg;
 258
 259        if (comedi_dio_update_state(s, data)) {
 260                outw(s->state & 0xffff, iobase);
 261                if (s->n_chan > 16)
 262                        outw((s->state >> 16) & 0xffff, iobase + 2);
 263        }
 264
 265        data[1] = s->state;
 266
 267        return insn->n;
 268}
 269
 270static int pci_dio_reset(struct comedi_device *dev, unsigned long cardtype)
 271{
 272        /* disable channel freeze function on the PCI-1752/1756 boards */
 273        if (cardtype == TYPE_PCI1752 || cardtype == TYPE_PCI1756)
 274                outw(0, dev->iobase + PCI1752_CFC_REG);
 275
 276        /* disable and clear interrupts */
 277        switch (cardtype) {
 278        case TYPE_PCI1730:
 279        case TYPE_PCI1733:
 280        case TYPE_PCI1736:
 281                outb(0, dev->iobase + PCI173X_INT_EN_REG);
 282                outb(0x0f, dev->iobase + PCI173X_INT_CLR_REG);
 283                outb(0, dev->iobase + PCI173X_INT_RF_REG);
 284                break;
 285        case TYPE_PCI1739:
 286        case TYPE_PCI1750:
 287        case TYPE_PCI1751:
 288                outb(0x88, dev->iobase + PCI1750_INT_REG);
 289                break;
 290        case TYPE_PCI1753:
 291        case TYPE_PCI1753E:
 292                outb(0x88, dev->iobase + PCI1753_INT_REG(0));
 293                outb(0x80, dev->iobase + PCI1753_INT_REG(1));
 294                outb(0x80, dev->iobase + PCI1753_INT_REG(2));
 295                outb(0x80, dev->iobase + PCI1753_INT_REG(3));
 296                if (cardtype == TYPE_PCI1753E) {
 297                        outb(0x88, dev->iobase + PCI1753E_INT_REG(0));
 298                        outb(0x80, dev->iobase + PCI1753E_INT_REG(1));
 299                        outb(0x80, dev->iobase + PCI1753E_INT_REG(2));
 300                        outb(0x80, dev->iobase + PCI1753E_INT_REG(3));
 301                }
 302                break;
 303        case TYPE_PCI1754:
 304        case TYPE_PCI1756:
 305                outw(0x08, dev->iobase + PCI1754_INT_REG(0));
 306                outw(0x08, dev->iobase + PCI1754_INT_REG(1));
 307                if (cardtype == TYPE_PCI1754) {
 308                        outw(0x08, dev->iobase + PCI1754_INT_REG(2));
 309                        outw(0x08, dev->iobase + PCI1754_INT_REG(3));
 310                }
 311                break;
 312        case TYPE_PCI1762:
 313                outw(0x0101, dev->iobase + PCI1762_INT_REG);
 314                break;
 315        default:
 316                break;
 317        }
 318
 319        return 0;
 320}
 321
 322static int pci_dio_auto_attach(struct comedi_device *dev,
 323                               unsigned long context)
 324{
 325        struct pci_dev *pcidev = comedi_to_pci_dev(dev);
 326        const struct dio_boardtype *board = NULL;
 327        const struct diosubd_data *d;
 328        struct comedi_subdevice *s;
 329        int ret, subdev, i, j;
 330
 331        if (context < ARRAY_SIZE(boardtypes))
 332                board = &boardtypes[context];
 333        if (!board)
 334                return -ENODEV;
 335        dev->board_ptr = board;
 336        dev->board_name = board->name;
 337
 338        ret = comedi_pci_enable(dev);
 339        if (ret)
 340                return ret;
 341        if (context == TYPE_PCI1736)
 342                dev->iobase = pci_resource_start(pcidev, 0);
 343        else
 344                dev->iobase = pci_resource_start(pcidev, 2);
 345
 346        pci_dio_reset(dev, context);
 347
 348        ret = comedi_alloc_subdevices(dev, board->nsubdevs);
 349        if (ret)
 350                return ret;
 351
 352        subdev = 0;
 353        for (i = 0; i < PCI_DIO_MAX_DI_SUBDEVS; i++) {
 354                d = &board->sdi[i];
 355                if (d->chans) {
 356                        s = &dev->subdevices[subdev++];
 357                        s->type         = COMEDI_SUBD_DI;
 358                        s->subdev_flags = SDF_READABLE;
 359                        s->n_chan       = d->chans;
 360                        s->maxdata      = 1;
 361                        s->range_table  = &range_digital;
 362                        s->insn_bits    = board->is_16bit
 363                                                ? pci_dio_insn_bits_di_w
 364                                                : pci_dio_insn_bits_di_b;
 365                        s->private      = (void *)d->addr;
 366                }
 367        }
 368
 369        for (i = 0; i < PCI_DIO_MAX_DO_SUBDEVS; i++) {
 370                d = &board->sdo[i];
 371                if (d->chans) {
 372                        s = &dev->subdevices[subdev++];
 373                        s->type         = COMEDI_SUBD_DO;
 374                        s->subdev_flags = SDF_WRITABLE;
 375                        s->n_chan       = d->chans;
 376                        s->maxdata      = 1;
 377                        s->range_table  = &range_digital;
 378                        s->insn_bits    = board->is_16bit
 379                                                ? pci_dio_insn_bits_do_w
 380                                                : pci_dio_insn_bits_do_b;
 381                        s->private      = (void *)d->addr;
 382
 383                        /* reset all outputs to 0 */
 384                        if (board->is_16bit) {
 385                                outw(0, dev->iobase + d->addr);
 386                                if (s->n_chan > 16)
 387                                        outw(0, dev->iobase + d->addr + 2);
 388                        } else {
 389                                outb(0, dev->iobase + d->addr);
 390                                if (s->n_chan > 8)
 391                                        outb(0, dev->iobase + d->addr + 1);
 392                                if (s->n_chan > 16)
 393                                        outb(0, dev->iobase + d->addr + 2);
 394                                if (s->n_chan > 24)
 395                                        outb(0, dev->iobase + d->addr + 3);
 396                        }
 397                }
 398        }
 399
 400        for (i = 0; i < PCI_DIO_MAX_DIO_SUBDEVG; i++) {
 401                d = &board->sdio[i];
 402                for (j = 0; j < d->chans; j++) {
 403                        s = &dev->subdevices[subdev++];
 404                        ret = subdev_8255_init(dev, s, NULL,
 405                                               d->addr + j * I8255_SIZE);
 406                        if (ret)
 407                                return ret;
 408                }
 409        }
 410
 411        if (board->id_reg) {
 412                s = &dev->subdevices[subdev++];
 413                s->type         = COMEDI_SUBD_DI;
 414                s->subdev_flags = SDF_READABLE | SDF_INTERNAL;
 415                s->n_chan       = 4;
 416                s->maxdata      = 1;
 417                s->range_table  = &range_digital;
 418                s->insn_bits    = board->is_16bit ? pci_dio_insn_bits_di_w
 419                                                  : pci_dio_insn_bits_di_b;
 420                s->private      = (void *)board->id_reg;
 421        }
 422
 423        if (board->timer_regbase) {
 424                s = &dev->subdevices[subdev++];
 425
 426                dev->pacer = comedi_8254_init(dev->iobase +
 427                                              board->timer_regbase,
 428                                              0, I8254_IO8, 0);
 429                if (!dev->pacer)
 430                        return -ENOMEM;
 431
 432                comedi_8254_subdevice_init(s, dev->pacer);
 433        }
 434
 435        return 0;
 436}
 437
 438static struct comedi_driver adv_pci_dio_driver = {
 439        .driver_name    = "adv_pci_dio",
 440        .module         = THIS_MODULE,
 441        .auto_attach    = pci_dio_auto_attach,
 442        .detach         = comedi_pci_detach,
 443};
 444
 445static unsigned long pci_dio_override_cardtype(struct pci_dev *pcidev,
 446                                               unsigned long cardtype)
 447{
 448        /*
 449         * Change cardtype from TYPE_PCI1753 to TYPE_PCI1753E if expansion
 450         * board available.  Need to enable PCI device and request the main
 451         * registers PCI BAR temporarily to perform the test.
 452         */
 453        if (cardtype != TYPE_PCI1753)
 454                return cardtype;
 455        if (pci_enable_device(pcidev) < 0)
 456                return cardtype;
 457        if (pci_request_region(pcidev, 2, "adv_pci_dio") == 0) {
 458                /*
 459                 * This test is based on Advantech's "advdaq" driver source
 460                 * (which declares its module licence as "GPL" although the
 461                 * driver source does not include a "COPYING" file).
 462                 */
 463                unsigned long reg = pci_resource_start(pcidev, 2) + 53;
 464
 465                outb(0x05, reg);
 466                if ((inb(reg) & 0x07) == 0x02) {
 467                        outb(0x02, reg);
 468                        if ((inb(reg) & 0x07) == 0x05)
 469                                cardtype = TYPE_PCI1753E;
 470                }
 471                pci_release_region(pcidev, 2);
 472        }
 473        pci_disable_device(pcidev);
 474        return cardtype;
 475}
 476
 477static int adv_pci_dio_pci_probe(struct pci_dev *dev,
 478                                 const struct pci_device_id *id)
 479{
 480        unsigned long cardtype;
 481
 482        cardtype = pci_dio_override_cardtype(dev, id->driver_data);
 483        return comedi_pci_auto_config(dev, &adv_pci_dio_driver, cardtype);
 484}
 485
 486static const struct pci_device_id adv_pci_dio_pci_table[] = {
 487        { PCI_VDEVICE(ADVANTECH, 0x1730), TYPE_PCI1730 },
 488        { PCI_VDEVICE(ADVANTECH, 0x1733), TYPE_PCI1733 },
 489        { PCI_VDEVICE(ADVANTECH, 0x1734), TYPE_PCI1734 },
 490        { PCI_VDEVICE(ADVANTECH, 0x1735), TYPE_PCI1735 },
 491        { PCI_VDEVICE(ADVANTECH, 0x1736), TYPE_PCI1736 },
 492        { PCI_VDEVICE(ADVANTECH, 0x1739), TYPE_PCI1739 },
 493        { PCI_VDEVICE(ADVANTECH, 0x1750), TYPE_PCI1750 },
 494        { PCI_VDEVICE(ADVANTECH, 0x1751), TYPE_PCI1751 },
 495        { PCI_VDEVICE(ADVANTECH, 0x1752), TYPE_PCI1752 },
 496        { PCI_VDEVICE(ADVANTECH, 0x1753), TYPE_PCI1753 },
 497        { PCI_VDEVICE(ADVANTECH, 0x1754), TYPE_PCI1754 },
 498        { PCI_VDEVICE(ADVANTECH, 0x1756), TYPE_PCI1756 },
 499        { PCI_VDEVICE(ADVANTECH, 0x1762), TYPE_PCI1762 },
 500        { 0 }
 501};
 502MODULE_DEVICE_TABLE(pci, adv_pci_dio_pci_table);
 503
 504static struct pci_driver adv_pci_dio_pci_driver = {
 505        .name           = "adv_pci_dio",
 506        .id_table       = adv_pci_dio_pci_table,
 507        .probe          = adv_pci_dio_pci_probe,
 508        .remove         = comedi_pci_auto_unconfig,
 509};
 510module_comedi_pci_driver(adv_pci_dio_driver, adv_pci_dio_pci_driver);
 511
 512MODULE_AUTHOR("Comedi http://www.comedi.org");
 513MODULE_DESCRIPTION("Comedi driver for Advantech Digital I/O Cards");
 514MODULE_LICENSE("GPL");
 515
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.