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 <linux/comedi/comedidev.h> 77#include <linux/isapnp.h> 78#include <linux/comedi/comedi_8255.h> 79 80#include "ni_stc.h" 81 82/* AT specific setup */ 83static const struct ni_board_struct ni_boards[] = { 84 { 85 .name = "at-mio-16e-1", 86 .device_id = 44, 87 .isapnp_id = 0x0000, /* XXX unknown */ 88 .n_adchan = 16, 89 .ai_maxdata = 0x0fff, 90 .ai_fifo_depth = 8192, 91 .gainlkup = ai_gain_16, 92 .ai_speed = 800, 93 .n_aochan = 2, 94 .ao_maxdata = 0x0fff, 95 .ao_fifo_depth = 2048, 96 .ao_range_table = &range_ni_E_ao_ext, 97 .ao_speed = 1000, 98 .caldac = { mb88341 }, 99 }, { 100 .name = "at-mio-16e-2", 101 .device_id = 25, 102 .isapnp_id = 0x1900, 103 .n_adchan = 16, 104 .ai_maxdata = 0x0fff, 105 .ai_fifo_depth = 2048, 106 .gainlkup = ai_gain_16, 107 .ai_speed = 2000, 108 .n_aochan = 2, 109 .ao_maxdata = 0x0fff, 110 .ao_fifo_depth = 2048, 111 .ao_range_table = &range_ni_E_ao_ext, 112 .ao_speed = 1000, 113 .caldac = { mb88341 }, 114 }, { 115 .name = "at-mio-16e-10", 116 .device_id = 36, 117 .isapnp_id = 0x2400, 118 .n_adchan = 16, 119 .ai_maxdata = 0x0fff, 120 .ai_fifo_depth = 512, 121 .gainlkup = ai_gain_16, 122 .ai_speed = 10000, 123 .n_aochan = 2, 124 .ao_maxdata = 0x0fff, 125 .ao_range_table = &range_ni_E_ao_ext, 126 .ao_speed = 10000, 127 .caldac = { ad8804_debug }, 128 }, { 129 .name = "at-mio-16de-10", 130 .device_id = 37, 131 .isapnp_id = 0x2500, 132 .n_adchan = 16, 133 .ai_maxdata = 0x0fff, 134 .ai_fifo_depth = 512, 135 .gainlkup = ai_gain_16, 136 .ai_speed = 10000, 137 .n_aochan = 2, 138 .ao_maxdata = 0x0fff, 139 .ao_range_table = &range_ni_E_ao_ext, 140 .ao_speed = 10000, 141 .caldac = { ad8804_debug }, 142 .has_8255 = 1, 143 }, { 144 .name = "at-mio-64e-3", 145 .device_id = 38, 146 .isapnp_id = 0x2600, 147 .n_adchan = 64, 148 .ai_maxdata = 0x0fff, 149 .ai_fifo_depth = 2048, 150 .gainlkup = ai_gain_16, 151 .ai_speed = 2000, 152 .n_aochan = 2, 153 .ao_maxdata = 0x0fff, 154 .ao_fifo_depth = 2048, 155 .ao_range_table = &range_ni_E_ao_ext, 156 .ao_speed = 1000, 157 .caldac = { ad8804_debug }, 158 }, { 159 .name = "at-mio-16xe-50", 160 .device_id = 39, 161 .isapnp_id = 0x2700, 162 .n_adchan = 16, 163 .ai_maxdata = 0xffff, 164 .ai_fifo_depth = 512, 165 .alwaysdither = 1, 166 .gainlkup = ai_gain_8, 167 .ai_speed = 50000, 168 .n_aochan = 2, 169 .ao_maxdata = 0x0fff, 170 .ao_range_table = &range_bipolar10, 171 .ao_speed = 50000, 172 .caldac = { dac8800, dac8043 }, 173 }, { 174 .name = "at-mio-16xe-10", 175 .device_id = 50, 176 .isapnp_id = 0x0000, /* XXX unknown */ 177 .n_adchan = 16, 178 .ai_maxdata = 0xffff, 179 .ai_fifo_depth = 512, 180 .alwaysdither = 1, 181 .gainlkup = ai_gain_14, 182 .ai_speed = 10000, 183 .n_aochan = 2, 184 .ao_maxdata = 0xffff, 185 .ao_fifo_depth = 2048, 186 .ao_range_table = &range_ni_E_ao_ext, 187 .ao_speed = 1000, 188 .caldac = { dac8800, dac8043, ad8522 }, 189 }, { 190 .name = "at-ai-16xe-10", 191 .device_id = 51, 192 .isapnp_id = 0x0000, /* XXX unknown */ 193 .n_adchan = 16, 194 .ai_maxdata = 0xffff, 195 .ai_fifo_depth = 512, 196 .alwaysdither = 1, /* unknown */ 197 .gainlkup = ai_gain_14, 198 .ai_speed = 10000, 199 .caldac = { dac8800, dac8043, ad8522 }, 200 }, 201}; 202 203static const int ni_irqpin[] = { 204 -1, -1, -1, 0, 1, 2, -1, 3, -1, -1, 4, 5, 6, -1, -1, 7 205}; 206 207#include "ni_mio_common.c" 208 209static const struct pnp_device_id device_ids[] = { 210 {.id = "NIC1900", .driver_data = 0}, 211 {.id = "NIC2400", .driver_data = 0}, 212 {.id = "NIC2500", .driver_data = 0}, 213 {.id = "NIC2600", .driver_data = 0}, 214 {.id = "NIC2700", .driver_data = 0}, 215 {.id = ""} 216}; 217 218MODULE_DEVICE_TABLE(pnp, device_ids); 219 220static int ni_isapnp_find_board(struct pnp_dev **dev) 221{ 222 struct pnp_dev *isapnp_dev = NULL; 223 int i; 224 225 for (i = 0; i < ARRAY_SIZE(ni_boards); i++) { 226 isapnp_dev = 227 pnp_find_dev(NULL, 228 ISAPNP_VENDOR('N', 'I', 'C'), 229 ISAPNP_FUNCTION(ni_boards[i].isapnp_id), 230 NULL); 231 232 if (!isapnp_dev || !isapnp_dev->card) 233 continue; 234 235 if (pnp_device_attach(isapnp_dev) < 0) 236 continue; 237 238 if (pnp_activate_dev(isapnp_dev) < 0) { 239 pnp_device_detach(isapnp_dev); 240 return -EAGAIN; 241 } 242 243 if (!pnp_port_valid(isapnp_dev, 0) || 244 !pnp_irq_valid(isapnp_dev, 0)) { 245 pnp_device_detach(isapnp_dev); 246 return -ENOMEM; 247 } 248 break; 249 } 250 if (i == ARRAY_SIZE(ni_boards)) 251 return -ENODEV; 252 *dev = isapnp_dev; 253 return 0; 254} 255 256static const struct ni_board_struct *ni_atmio_probe(struct comedi_device *dev) 257{ 258 int device_id = ni_read_eeprom(dev, 511); 259 int i; 260 261 for (i = 0; i < ARRAY_SIZE(ni_boards); i++) { 262 const struct ni_board_struct *board = &ni_boards[i]; 263 264 if (board->device_id == device_id) 265 return board; 266 } 267 if (device_id == 255) 268 dev_err(dev->class_dev, "can't find board\n"); 269 else if (device_id == 0) 270 dev_err(dev->class_dev, 271 "EEPROM read error (?) or device not found\n"); 272 else 273 dev_err(dev->class_dev, 274 "unknown device ID %d -- contact author\n", device_id); 275 276 return NULL; 277} 278 279static int ni_atmio_attach(struct comedi_device *dev, 280 struct comedi_devconfig *it) 281{ 282 const struct ni_board_struct *board; 283 struct pnp_dev *isapnp_dev; 284 int ret; 285 unsigned long iobase; 286 unsigned int irq; 287 288 ret = ni_alloc_private(dev); 289 if (ret) 290 return ret; 291 292 iobase = it->options[0]; 293 irq = it->options[1]; 294 isapnp_dev = NULL; 295 if (iobase == 0) { 296 ret = ni_isapnp_find_board(&isapnp_dev); 297 if (ret < 0) 298 return ret; 299 300 iobase = pnp_port_start(isapnp_dev, 0); 301 irq = pnp_irq(isapnp_dev, 0); 302 comedi_set_hw_dev(dev, &isapnp_dev->dev); 303 } 304 305 ret = comedi_request_region(dev, iobase, 0x20); 306 if (ret) 307 return ret; 308 309 board = ni_atmio_probe(dev); 310 if (!board) 311 return -ENODEV; 312 dev->board_ptr = board; 313 dev->board_name = board->name; 314 315 /* irq stuff */ 316 317 if (irq != 0) { 318 if (irq > 15 || ni_irqpin[irq] == -1) 319 return -EINVAL; 320 ret = request_irq(irq, ni_E_interrupt, 0, 321 dev->board_name, dev); 322 if (ret < 0) 323 return -EINVAL; 324 dev->irq = irq; 325 } 326 327 /* generic E series stuff in ni_mio_common.c */ 328 329 ret = ni_E_init(dev, ni_irqpin[dev->irq], 0); 330 if (ret < 0) 331 return ret; 332 333 return 0; 334} 335 336static void ni_atmio_detach(struct comedi_device *dev) 337{ 338 struct pnp_dev *isapnp_dev; 339 340 mio_common_detach(dev); 341 comedi_legacy_detach(dev); 342 343 isapnp_dev = dev->hw_dev ? to_pnp_dev(dev->hw_dev) : NULL; 344 if (isapnp_dev) 345 pnp_device_detach(isapnp_dev); 346} 347 348static struct comedi_driver ni_atmio_driver = { 349 .driver_name = "ni_atmio", 350 .module = THIS_MODULE, 351 .attach = ni_atmio_attach, 352 .detach = ni_atmio_detach, 353}; 354module_comedi_driver(ni_atmio_driver); 355 356MODULE_AUTHOR("Comedi https://www.comedi.org"); 357MODULE_DESCRIPTION("Comedi low-level driver"); 358MODULE_LICENSE("GPL"); 359 360