1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Comedi driver for NI AT-MIO E series cards 4 * 5 * COMEDI - Linux Control and Measurement Device Interface 6 * Copyright (C) 1997-2001 David A. Schleef <ds@schleef.org> 7 */ 8 9 /* 10 * Driver: ni_atmio 11 * Description: National Instruments AT-MIO-E series 12 * Author: ds 13 * Devices: [National Instruments] AT-MIO-16E-1 (ni_atmio), 14 * AT-MIO-16E-2, AT-MIO-16E-10, AT-MIO-16DE-10, AT-MIO-64E-3, 15 * AT-MIO-16XE-50, AT-MIO-16XE-10, AT-AI-16XE-10 16 * Status: works 17 * Updated: Thu May 1 20:03:02 CDT 2003 18 * 19 * The driver has 2.6 kernel isapnp support, and will automatically probe for 20 * a supported board if the I/O base is left unspecified with comedi_config. 21 * However, many of the isapnp id numbers are unknown. If your board is not 22 * recognized, please send the output of 'cat /proc/isapnp' (you may need to 23 * modprobe the isa-pnp module for /proc/isapnp to exist) so the id numbers 24 * for your board can be added to the driver. 25 * 26 * Otherwise, you can use the isapnptools package to configure your board. 27 * Use isapnp to configure the I/O base and IRQ for the board, and then pass 28 * the same values as parameters in comedi_config. A sample isapnp.conf file 29 * is included in the etc/ directory of Comedilib. 30 * 31 * Comedilib includes a utility to autocalibrate these boards. The boards 32 * seem to boot into a state where the all calibration DACs are at one 33 * extreme of their range, thus the default calibration is terrible. 34 * Calibration at boot is strongly encouraged. 35 * 36 * To use the extended digital I/O on some of the boards, enable the 37 * 8255 driver when configuring the Comedi source tree. 38 * 39 * External triggering is supported for some events. The channel index 40 * (scan_begin_arg, etc.) maps to PFI0 - PFI9. 41 * 42 * Some of the more esoteric triggering possibilities of these boards are 43 * not supported. 44 */ 45 46 /* 47 * The real guts of the driver is in ni_mio_common.c, which is included 48 * both here and in ni_pcimio.c 49 * 50 * Interrupt support added by Truxton Fulton <trux@truxton.com> 51 * 52 * References for specifications: 53 * 340747b.pdf Register Level Programmer Manual (obsolete) 54 * 340747c.pdf Register Level Programmer Manual (new) 55 * DAQ-STC reference manual 56 * 57 * Other possibly relevant info: 58 * 320517c.pdf User manual (obsolete) 59 * 320517f.pdf User manual (new) 60 * 320889a.pdf delete 61 * 320906c.pdf maximum signal ratings 62 * 321066a.pdf about 16x 63 * 321791a.pdf discontinuation of at-mio-16e-10 rev. c 64 * 321808a.pdf about at-mio-16e-10 rev P 65 * 321837a.pdf discontinuation of at-mio-16de-10 rev d 66 * 321838a.pdf about at-mio-16de-10 rev N 67 * 68 * ISSUES: 69 * - need to deal with external reference for DAC, and other DAC 70 * properties in board properties 71 * - deal with at-mio-16de-10 revision D to N changes, etc. 72 */ 73 74 #include <linux/module.h> 75 #include <linux/interrupt.h> 76 #include "../comedidev.h" 77 78 #include <linux/isapnp.h> 79 80 #include "ni_stc.h" 81 #include "8255.h" 82 83 /* AT specific setup */ 84 static const struct ni_board_struct ni_boards[] = { 85 { 86 .name = "at-mio-16e-1", 87 .device_id = 44, 88 .isapnp_id = 0x0000, /* XXX unknown */ 89 .n_adchan = 16, 90 .ai_maxdata = 0x0fff, 91 .ai_fifo_depth = 8192, 92 .gainlkup = ai_gain_16, 93 .ai_speed = 800, 94 .n_aochan = 2, 95 .ao_maxdata = 0x0fff, 96 .ao_fifo_depth = 2048, 97 .ao_range_table = &range_ni_E_ao_ext, 98 .ao_speed = 1000, 99 .caldac = { mb88341 }, 100 }, { 101 .name = "at-mio-16e-2", 102 .device_id = 25, 103 .isapnp_id = 0x1900, 104 .n_adchan = 16, 105 .ai_maxdata = 0x0fff, 106 .ai_fifo_depth = 2048, 107 .gainlkup = ai_gain_16, 108 .ai_speed = 2000, 109 .n_aochan = 2, 110 .ao_maxdata = 0x0fff, 111 .ao_fifo_depth = 2048, 112 .ao_range_table = &range_ni_E_ao_ext, 113 .ao_speed = 1000, 114 .caldac = { mb88341 }, 115 }, { 116 .name = "at-mio-16e-10", 117 .device_id = 36, 118 .isapnp_id = 0x2400, 119 .n_adchan = 16, 120 .ai_maxdata = 0x0fff, 121 .ai_fifo_depth = 512, 122 .gainlkup = ai_gain_16, 123 .ai_speed = 10000, 124 .n_aochan = 2, 125 .ao_maxdata = 0x0fff, 126 .ao_range_table = &range_ni_E_ao_ext, 127 .ao_speed = 10000, 128 .caldac = { ad8804_debug }, 129 }, { 130 .name = "at-mio-16de-10", 131 .device_id = 37, 132 .isapnp_id = 0x2500, 133 .n_adchan = 16, 134 .ai_maxdata = 0x0fff, 135 .ai_fifo_depth = 512, 136 .gainlkup = ai_gain_16, 137 .ai_speed = 10000, 138 .n_aochan = 2, 139 .ao_maxdata = 0x0fff, 140 .ao_range_table = &range_ni_E_ao_ext, 141 .ao_speed = 10000, 142 .caldac = { ad8804_debug }, 143 .has_8255 = 1, 144 }, { 145 .name = "at-mio-64e-3", 146 .device_id = 38, 147 .isapnp_id = 0x2600, 148 .n_adchan = 64, 149 .ai_maxdata = 0x0fff, 150 .ai_fifo_depth = 2048, 151 .gainlkup = ai_gain_16, 152 .ai_speed = 2000, 153 .n_aochan = 2, 154 .ao_maxdata = 0x0fff, 155 .ao_fifo_depth = 2048, 156 .ao_range_table = &range_ni_E_ao_ext, 157 .ao_speed = 1000, 158 .caldac = { ad8804_debug }, 159 }, { 160 .name = "at-mio-16xe-50", 161 .device_id = 39, 162 .isapnp_id = 0x2700, 163 .n_adchan = 16, 164 .ai_maxdata = 0xffff, 165 .ai_fifo_depth = 512, 166 .alwaysdither = 1, 167 .gainlkup = ai_gain_8, 168 .ai_speed = 50000, 169 .n_aochan = 2, 170 .ao_maxdata = 0x0fff, 171 .ao_range_table = &range_bipolar10, 172 .ao_speed = 50000, 173 .caldac = { dac8800, dac8043 }, 174 }, { 175 .name = "at-mio-16xe-10", 176 .device_id = 50, 177 .isapnp_id = 0x0000, /* XXX unknown */ 178 .n_adchan = 16, 179 .ai_maxdata = 0xffff, 180 .ai_fifo_depth = 512, 181 .alwaysdither = 1, 182 .gainlkup = ai_gain_14, 183 .ai_speed = 10000, 184 .n_aochan = 2, 185 .ao_maxdata = 0xffff, 186 .ao_fifo_depth = 2048, 187 .ao_range_table = &range_ni_E_ao_ext, 188 .ao_speed = 1000, 189 .caldac = { dac8800, dac8043, ad8522 }, 190 }, { 191 .name = "at-ai-16xe-10", 192 .device_id = 51, 193 .isapnp_id = 0x0000, /* XXX unknown */ 194 .n_adchan = 16, 195 .ai_maxdata = 0xffff, 196 .ai_fifo_depth = 512, 197 .alwaysdither = 1, /* unknown */ 198 .gainlkup = ai_gain_14, 199 .ai_speed = 10000, 200 .caldac = { dac8800, dac8043, ad8522 }, 201 }, 202 }; 203 204 static const int ni_irqpin[] = { 205 -1, -1, -1, 0, 1, 2, -1, 3, -1, -1, 4, 5, 6, -1, -1, 7 206 }; 207 208 #include "ni_mio_common.c" 209 210 static const struct pnp_device_id device_ids[] = { 211 {.id = "NIC1900", .driver_data = 0}, 212 {.id = "NIC2400", .driver_data = 0}, 213 {.id = "NIC2500", .driver_data = 0}, 214 {.id = "NIC2600", .driver_data = 0}, 215 {.id = "NIC2700", .driver_data = 0}, 216 {.id = ""} 217 }; 218 219 MODULE_DEVICE_TABLE(pnp, device_ids); 220 221 static int ni_isapnp_find_board(struct pnp_dev **dev) 222 { 223 struct pnp_dev *isapnp_dev = NULL; 224 int i; 225 226 for (i = 0; i < ARRAY_SIZE(ni_boards); i++) { 227 isapnp_dev = 228 pnp_find_dev(NULL, 229 ISAPNP_VENDOR('N', 'I', 'C'), 230 ISAPNP_FUNCTION(ni_boards[i].isapnp_id), 231 NULL); 232 233 if (!isapnp_dev || !isapnp_dev->card) 234 continue; 235 236 if (pnp_device_attach(isapnp_dev) < 0) 237 continue; 238 239 if (pnp_activate_dev(isapnp_dev) < 0) { 240 pnp_device_detach(isapnp_dev); 241 return -EAGAIN; 242 } 243 244 if (!pnp_port_valid(isapnp_dev, 0) || 245 !pnp_irq_valid(isapnp_dev, 0)) { 246 pnp_device_detach(isapnp_dev); 247 return -ENOMEM; 248 } 249 break; 250 } 251 if (i == ARRAY_SIZE(ni_boards)) 252 return -ENODEV; 253 *dev = isapnp_dev; 254 return 0; 255 } 256 257 static const struct ni_board_struct *ni_atmio_probe(struct comedi_device *dev) 258 { 259 int device_id = ni_read_eeprom(dev, 511); 260 int i; 261 262 for (i = 0; i < ARRAY_SIZE(ni_boards); i++) { 263 const struct ni_board_struct *board = &ni_boards[i]; 264 265 if (board->device_id == device_id) 266 return board; 267 } 268 if (device_id == 255) 269 dev_err(dev->class_dev, "can't find board\n"); 270 else if (device_id == 0) 271 dev_err(dev->class_dev, 272 "EEPROM read error (?) or device not found\n"); 273 else 274 dev_err(dev->class_dev, 275 "unknown device ID %d -- contact author\n", device_id); 276 277 return NULL; 278 } 279 280 static int ni_atmio_attach(struct comedi_device *dev, 281 struct comedi_devconfig *it) 282 { 283 const struct ni_board_struct *board; 284 struct pnp_dev *isapnp_dev; 285 int ret; 286 unsigned long iobase; 287 unsigned int irq; 288 289 ret = ni_alloc_private(dev); 290 if (ret) 291 return ret; 292 293 iobase = it->options[0]; 294 irq = it->options[1]; 295 isapnp_dev = NULL; 296 if (iobase == 0) { 297 ret = ni_isapnp_find_board(&isapnp_dev); 298 if (ret < 0) 299 return ret; 300 301 iobase = pnp_port_start(isapnp_dev, 0); 302 irq = pnp_irq(isapnp_dev, 0); 303 comedi_set_hw_dev(dev, &isapnp_dev->dev); 304 } 305 306 ret = comedi_request_region(dev, iobase, 0x20); 307 if (ret) 308 return ret; 309 310 board = ni_atmio_probe(dev); 311 if (!board) 312 return -ENODEV; 313 dev->board_ptr = board; 314 dev->board_name = board->name; 315 316 /* irq stuff */ 317 318 if (irq != 0) { 319 if (irq > 15 || ni_irqpin[irq] == -1) 320 return -EINVAL; 321 ret = request_irq(irq, ni_E_interrupt, 0, 322 dev->board_name, dev); 323 if (ret < 0) 324 return -EINVAL; 325 dev->irq = irq; 326 } 327 328 /* generic E series stuff in ni_mio_common.c */ 329 330 ret = ni_E_init(dev, ni_irqpin[dev->irq], 0); 331 if (ret < 0) 332 return ret; 333 334 return 0; 335 } 336 337 static void ni_atmio_detach(struct comedi_device *dev) 338 { 339 struct pnp_dev *isapnp_dev; 340 341 mio_common_detach(dev); 342 comedi_legacy_detach(dev); 343 344 isapnp_dev = dev->hw_dev ? to_pnp_dev(dev->hw_dev) : NULL; 345 if (isapnp_dev) 346 pnp_device_detach(isapnp_dev); 347 } 348 349 static struct comedi_driver ni_atmio_driver = { 350 .driver_name = "ni_atmio", 351 .module = THIS_MODULE, 352 .attach = ni_atmio_attach, 353 .detach = ni_atmio_detach, 354 }; 355 module_comedi_driver(ni_atmio_driver); 356 357 MODULE_AUTHOR("Comedi https://www.comedi.org"); 358 MODULE_DESCRIPTION("Comedi low-level driver"); 359 MODULE_LICENSE("GPL"); 360 361