1/* 2 * COMEDI driver for the ADLINK PCI-723x/743x series boards. 3 * Copyright (C) 2012 H Hartley Sweeten <hsweeten@visionengravers.com> 4 * 5 * Based on the adl_pci7230 driver written by: 6 * David Fernandez <dfcastelao@gmail.com> 7 * and the adl_pci7432 driver written by: 8 * Michel Lachaine <mike@mikelachaine.ca> 9 * 10 * COMEDI - Linux Control and Measurement Device Interface 11 * Copyright (C) 2000 David A. Schleef <ds@schleef.org> 12 * 13 * This program is free software; you can redistribute it and/or modify 14 * it under the terms of the GNU General Public License as published by 15 * the Free Software Foundation; either version 2 of the License, or 16 * (at your option) any later version. 17 * 18 * This program is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 * GNU General Public License for more details. 22 */ 23 24/* 25Driver: adl_pci7x3x 26Description: 32/64-Channel Isolated Digital I/O Boards 27Devices: (ADLink) PCI-7230 [adl_pci7230] - 16 input / 16 output 28 (ADLink) PCI-7233 [adl_pci7233] - 32 input 29 (ADLink) PCI-7234 [adl_pci7234] - 32 output 30 (ADLink) PCI-7432 [adl_pci7432] - 32 input / 32 output 31 (ADLink) PCI-7433 [adl_pci7433] - 64 input 32 (ADLink) PCI-7434 [adl_pci7434] - 64 output 33Author: H Hartley Sweeten <hsweeten@visionengravers.com> 34Updated: Thu, 02 Aug 2012 14:27:46 -0700 35Status: untested 36 37The PCI-7230, PCI-7432 and PCI-7433 boards also support external 38interrupt signals on digital input channels 0 and 1. The PCI-7233 39has dual-interrupt sources for change-of-state (COS) on any 16 40digital input channels of LSB and for COS on any 16 digital input 41lines of MSB. Interrupts are not currently supported by this 42driver. 43 44Configuration Options: not applicable, uses comedi PCI auto config 45*/ 46 47#include <linux/module.h> 48#include <linux/pci.h> 49 50#include "../comedidev.h" 51 52/* 53 * Register I/O map (32-bit access only) 54 */ 55#define PCI7X3X_DIO_REG 0x00 56#define PCI743X_DIO_REG 0x04 57 58enum apci1516_boardid { 59 BOARD_PCI7230, 60 BOARD_PCI7233, 61 BOARD_PCI7234, 62 BOARD_PCI7432, 63 BOARD_PCI7433, 64 BOARD_PCI7434, 65}; 66 67struct adl_pci7x3x_boardinfo { 68 const char *name; 69 int nsubdevs; 70 int di_nchan; 71 int do_nchan; 72}; 73 74static const struct adl_pci7x3x_boardinfo adl_pci7x3x_boards[] = { 75 [BOARD_PCI7230] = { 76 .name = "adl_pci7230", 77 .nsubdevs = 2, 78 .di_nchan = 16, 79 .do_nchan = 16, 80 }, 81 [BOARD_PCI7233] = { 82 .name = "adl_pci7233", 83 .nsubdevs = 1, 84 .di_nchan = 32, 85 }, 86 [BOARD_PCI7234] = { 87 .name = "adl_pci7234", 88 .nsubdevs = 1, 89 .do_nchan = 32, 90 }, 91 [BOARD_PCI7432] = { 92 .name = "adl_pci7432", 93 .nsubdevs = 2, 94 .di_nchan = 32, 95 .do_nchan = 32, 96 }, 97 [BOARD_PCI7433] = { 98 .name = "adl_pci7433", 99 .nsubdevs = 2, 100 .di_nchan = 64, 101 }, 102 [BOARD_PCI7434] = { 103 .name = "adl_pci7434", 104 .nsubdevs = 2, 105 .do_nchan = 64, 106 } 107}; 108 109static int adl_pci7x3x_do_insn_bits(struct comedi_device *dev, 110 struct comedi_subdevice *s, 111 struct comedi_insn *insn, 112 unsigned int *data) 113{ 114 unsigned long reg = (unsigned long)s->private; 115 116 if (comedi_dio_update_state(s, data)) 117 outl(s->state, dev->iobase + reg); 118 119 data[1] = s->state; 120 121 return insn->n; 122} 123 124static int adl_pci7x3x_di_insn_bits(struct comedi_device *dev, 125 struct comedi_subdevice *s, 126 struct comedi_insn *insn, 127 unsigned int *data) 128{ 129 unsigned long reg = (unsigned long)s->private; 130 131 data[1] = inl(dev->iobase + reg); 132 133 return insn->n; 134} 135 136static int adl_pci7x3x_auto_attach(struct comedi_device *dev, 137 unsigned long context) 138{ 139 struct pci_dev *pcidev = comedi_to_pci_dev(dev); 140 const struct adl_pci7x3x_boardinfo *board = NULL; 141 struct comedi_subdevice *s; 142 int subdev; 143 int nchan; 144 int ret; 145 146 if (context < ARRAY_SIZE(adl_pci7x3x_boards)) 147 board = &adl_pci7x3x_boards[context]; 148 if (!board) 149 return -ENODEV; 150 dev->board_ptr = board; 151 dev->board_name = board->name; 152 153 ret = comedi_pci_enable(dev); 154 if (ret) 155 return ret; 156 dev->iobase = pci_resource_start(pcidev, 2); 157 158 /* 159 * One or two subdevices are setup by this driver depending on 160 * the number of digital inputs and/or outputs provided by the 161 * board. Each subdevice has a maximum of 32 channels. 162 * 163 * PCI-7230 - 2 subdevices: 0 - 16 input, 1 - 16 output 164 * PCI-7233 - 1 subdevice: 0 - 32 input 165 * PCI-7234 - 1 subdevice: 0 - 32 output 166 * PCI-7432 - 2 subdevices: 0 - 32 input, 1 - 32 output 167 * PCI-7433 - 2 subdevices: 0 - 32 input, 1 - 32 input 168 * PCI-7434 - 2 subdevices: 0 - 32 output, 1 - 32 output 169 */ 170 ret = comedi_alloc_subdevices(dev, board->nsubdevs); 171 if (ret) 172 return ret; 173 174 subdev = 0; 175 176 if (board->di_nchan) { 177 nchan = min(board->di_nchan, 32); 178 179 s = &dev->subdevices[subdev]; 180 /* Isolated digital inputs 0 to 15/31 */ 181 s->type = COMEDI_SUBD_DI; 182 s->subdev_flags = SDF_READABLE; 183 s->n_chan = nchan; 184 s->maxdata = 1; 185 s->insn_bits = adl_pci7x3x_di_insn_bits; 186 s->range_table = &range_digital; 187 188 s->private = (void *)PCI7X3X_DIO_REG; 189 190 subdev++; 191 192 nchan = board->di_nchan - nchan; 193 if (nchan) { 194 s = &dev->subdevices[subdev]; 195 /* Isolated digital inputs 32 to 63 */ 196 s->type = COMEDI_SUBD_DI; 197 s->subdev_flags = SDF_READABLE; 198 s->n_chan = nchan; 199 s->maxdata = 1; 200 s->insn_bits = adl_pci7x3x_di_insn_bits; 201 s->range_table = &range_digital; 202 203 s->private = (void *)PCI743X_DIO_REG; 204 205 subdev++; 206 } 207 } 208 209 if (board->do_nchan) { 210 nchan = min(board->do_nchan, 32); 211 212 s = &dev->subdevices[subdev]; 213 /* Isolated digital outputs 0 to 15/31 */ 214 s->type = COMEDI_SUBD_DO; 215 s->subdev_flags = SDF_WRITABLE; 216 s->n_chan = nchan; 217 s->maxdata = 1; 218 s->insn_bits = adl_pci7x3x_do_insn_bits; 219 s->range_table = &range_digital; 220 221 s->private = (void *)PCI7X3X_DIO_REG; 222 223 subdev++; 224 225 nchan = board->do_nchan - nchan; 226 if (nchan) { 227 s = &dev->subdevices[subdev]; 228 /* Isolated digital outputs 32 to 63 */ 229 s->type = COMEDI_SUBD_DO; 230 s->subdev_flags = SDF_WRITABLE; 231 s->n_chan = nchan; 232 s->maxdata = 1; 233 s->insn_bits = adl_pci7x3x_do_insn_bits; 234 s->range_table = &range_digital; 235 236 s->private = (void *)PCI743X_DIO_REG; 237 238 subdev++; 239 } 240 } 241 242 return 0; 243} 244 245static struct comedi_driver adl_pci7x3x_driver = { 246 .driver_name = "adl_pci7x3x", 247 .module = THIS_MODULE, 248 .auto_attach = adl_pci7x3x_auto_attach, 249 .detach = comedi_pci_detach, 250}; 251 252static int adl_pci7x3x_pci_probe(struct pci_dev *dev, 253 const struct pci_device_id *id) 254{ 255 return comedi_pci_auto_config(dev, &adl_pci7x3x_driver, 256 id->driver_data); 257} 258 259static const struct pci_device_id adl_pci7x3x_pci_table[] = { 260 { PCI_VDEVICE(ADLINK, 0x7230), BOARD_PCI7230 }, 261 { PCI_VDEVICE(ADLINK, 0x7233), BOARD_PCI7233 }, 262 { PCI_VDEVICE(ADLINK, 0x7234), BOARD_PCI7234 }, 263 { PCI_VDEVICE(ADLINK, 0x7432), BOARD_PCI7432 }, 264 { PCI_VDEVICE(ADLINK, 0x7433), BOARD_PCI7433 }, 265 { PCI_VDEVICE(ADLINK, 0x7434), BOARD_PCI7434 }, 266 { 0 } 267}; 268MODULE_DEVICE_TABLE(pci, adl_pci7x3x_pci_table); 269 270static struct pci_driver adl_pci7x3x_pci_driver = { 271 .name = "adl_pci7x3x", 272 .id_table = adl_pci7x3x_pci_table, 273 .probe = adl_pci7x3x_pci_probe, 274 .remove = comedi_pci_auto_unconfig, 275}; 276module_comedi_pci_driver(adl_pci7x3x_driver, adl_pci7x3x_pci_driver); 277 278MODULE_DESCRIPTION("ADLINK PCI-723x/743x Isolated Digital I/O boards"); 279MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>"); 280MODULE_LICENSE("GPL"); 281