1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * ni_6527.c 4 * Comedi driver for National Instruments PCI-6527 5 * 6 * COMEDI - Linux Control and Measurement Device Interface 7 * Copyright (C) 1999,2002,2003 David A. Schleef <ds@schleef.org> 8 */ 9 10 /* 11 * Driver: ni_6527 12 * Description: National Instruments 6527 13 * Devices: [National Instruments] PCI-6527 (pci-6527), PXI-6527 (pxi-6527) 14 * Author: David A. Schleef <ds@schleef.org> 15 * Updated: Sat, 25 Jan 2003 13:24:40 -0800 16 * Status: works 17 * 18 * Configuration Options: not applicable, uses PCI auto config 19 */ 20 21 #include <linux/module.h> 22 #include <linux/interrupt.h> 23 #include <linux/comedi/comedi_pci.h> 24 25 /* 26 * PCI BAR1 - Register memory map 27 * 28 * Manuals (available from ftp://ftp.natinst.com/support/manuals) 29 * 370106b.pdf 6527 Register Level Programmer Manual 30 */ 31 #define NI6527_DI_REG(x) (0x00 + (x)) 32 #define NI6527_DO_REG(x) (0x03 + (x)) 33 #define NI6527_ID_REG 0x06 34 #define NI6527_CLR_REG 0x07 35 #define NI6527_CLR_EDGE BIT(3) 36 #define NI6527_CLR_OVERFLOW BIT(2) 37 #define NI6527_CLR_FILT BIT(1) 38 #define NI6527_CLR_INTERVAL BIT(0) 39 #define NI6527_CLR_IRQS (NI6527_CLR_EDGE | NI6527_CLR_OVERFLOW) 40 #define NI6527_CLR_RESET_FILT (NI6527_CLR_FILT | NI6527_CLR_INTERVAL) 41 #define NI6527_FILT_INTERVAL_REG(x) (0x08 + (x)) 42 #define NI6527_FILT_ENA_REG(x) (0x0c + (x)) 43 #define NI6527_STATUS_REG 0x14 44 #define NI6527_STATUS_IRQ BIT(2) 45 #define NI6527_STATUS_OVERFLOW BIT(1) 46 #define NI6527_STATUS_EDGE BIT(0) 47 #define NI6527_CTRL_REG 0x15 48 #define NI6527_CTRL_FALLING BIT(4) 49 #define NI6527_CTRL_RISING BIT(3) 50 #define NI6527_CTRL_IRQ BIT(2) 51 #define NI6527_CTRL_OVERFLOW BIT(1) 52 #define NI6527_CTRL_EDGE BIT(0) 53 #define NI6527_CTRL_DISABLE_IRQS 0 54 #define NI6527_CTRL_ENABLE_IRQS (NI6527_CTRL_FALLING | \ 55 NI6527_CTRL_RISING | \ 56 NI6527_CTRL_IRQ | NI6527_CTRL_EDGE) 57 #define NI6527_RISING_EDGE_REG(x) (0x18 + (x)) 58 #define NI6527_FALLING_EDGE_REG(x) (0x20 + (x)) 59 60 enum ni6527_boardid { 61 BOARD_PCI6527, 62 BOARD_PXI6527, 63 }; 64 65 struct ni6527_board { 66 const char *name; 67 }; 68 69 static const struct ni6527_board ni6527_boards[] = { 70 [BOARD_PCI6527] = { 71 .name = "pci-6527", 72 }, 73 [BOARD_PXI6527] = { 74 .name = "pxi-6527", 75 }, 76 }; 77 78 struct ni6527_private { 79 unsigned int filter_interval; 80 unsigned int filter_enable; 81 }; 82 83 static void ni6527_set_filter_interval(struct comedi_device *dev, 84 unsigned int val) 85 { 86 struct ni6527_private *devpriv = dev->private; 87 88 if (val != devpriv->filter_interval) { 89 writeb(val & 0xff, dev->mmio + NI6527_FILT_INTERVAL_REG(0)); 90 writeb((val >> 8) & 0xff, 91 dev->mmio + NI6527_FILT_INTERVAL_REG(1)); 92 writeb((val >> 16) & 0x0f, 93 dev->mmio + NI6527_FILT_INTERVAL_REG(2)); 94 95 writeb(NI6527_CLR_INTERVAL, dev->mmio + NI6527_CLR_REG); 96 97 devpriv->filter_interval = val; 98 } 99 } 100 101 static void ni6527_set_filter_enable(struct comedi_device *dev, 102 unsigned int val) 103 { 104 writeb(val & 0xff, dev->mmio + NI6527_FILT_ENA_REG(0)); 105 writeb((val >> 8) & 0xff, dev->mmio + NI6527_FILT_ENA_REG(1)); 106 writeb((val >> 16) & 0xff, dev->mmio + NI6527_FILT_ENA_REG(2)); 107 } 108 109 static int ni6527_di_insn_config(struct comedi_device *dev, 110 struct comedi_subdevice *s, 111 struct comedi_insn *insn, 112 unsigned int *data) 113 { 114 struct ni6527_private *devpriv = dev->private; 115 unsigned int chan = CR_CHAN(insn->chanspec); 116 unsigned int interval; 117 118 switch (data[0]) { 119 case INSN_CONFIG_FILTER: 120 /* 121 * The deglitch filter interval is specified in nanoseconds. 122 * The hardware supports intervals in 200ns increments. Round 123 * the user values up and return the actual interval. 124 */ 125 interval = (data[1] + 100) / 200; 126 data[1] = interval * 200; 127 128 if (interval) { 129 ni6527_set_filter_interval(dev, interval); 130 devpriv->filter_enable |= 1 << chan; 131 } else { 132 devpriv->filter_enable &= ~(1 << chan); 133 } 134 ni6527_set_filter_enable(dev, devpriv->filter_enable); 135 break; 136 default: 137 return -EINVAL; 138 } 139 140 return insn->n; 141 } 142 143 static int ni6527_di_insn_bits(struct comedi_device *dev, 144 struct comedi_subdevice *s, 145 struct comedi_insn *insn, 146 unsigned int *data) 147 { 148 unsigned int val; 149 150 val = readb(dev->mmio + NI6527_DI_REG(0)); 151 val |= (readb(dev->mmio + NI6527_DI_REG(1)) << 8); 152 val |= (readb(dev->mmio + NI6527_DI_REG(2)) << 16); 153 154 data[1] = val; 155 156 return insn->n; 157 } 158 159 static int ni6527_do_insn_bits(struct comedi_device *dev, 160 struct comedi_subdevice *s, 161 struct comedi_insn *insn, 162 unsigned int *data) 163 { 164 unsigned int mask; 165 166 mask = comedi_dio_update_state(s, data); 167 if (mask) { 168 /* Outputs are inverted */ 169 unsigned int val = s->state ^ 0xffffff; 170 171 if (mask & 0x0000ff) 172 writeb(val & 0xff, dev->mmio + NI6527_DO_REG(0)); 173 if (mask & 0x00ff00) 174 writeb((val >> 8) & 0xff, 175 dev->mmio + NI6527_DO_REG(1)); 176 if (mask & 0xff0000) 177 writeb((val >> 16) & 0xff, 178 dev->mmio + NI6527_DO_REG(2)); 179 } 180 181 data[1] = s->state; 182 183 return insn->n; 184 } 185 186 static irqreturn_t ni6527_interrupt(int irq, void *d) 187 { 188 struct comedi_device *dev = d; 189 struct comedi_subdevice *s = dev->read_subdev; 190 unsigned int status; 191 192 status = readb(dev->mmio + NI6527_STATUS_REG); 193 if (!(status & NI6527_STATUS_IRQ)) 194 return IRQ_NONE; 195 196 if (status & NI6527_STATUS_EDGE) { 197 unsigned short val = 0; 198 199 comedi_buf_write_samples(s, &val, 1); 200 comedi_handle_events(dev, s); 201 } 202 203 writeb(NI6527_CLR_IRQS, dev->mmio + NI6527_CLR_REG); 204 205 return IRQ_HANDLED; 206 } 207 208 static int ni6527_intr_cmdtest(struct comedi_device *dev, 209 struct comedi_subdevice *s, 210 struct comedi_cmd *cmd) 211 { 212 int err = 0; 213 214 /* Step 1 : check if triggers are trivially valid */ 215 216 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW); 217 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_OTHER); 218 err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW); 219 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); 220 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT); 221 222 if (err) 223 return 1; 224 225 /* Step 2a : make sure trigger sources are unique */ 226 /* Step 2b : and mutually compatible */ 227 228 /* Step 3: check if arguments are trivially valid */ 229 230 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); 231 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0); 232 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0); 233 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg, 234 cmd->chanlist_len); 235 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0); 236 237 if (err) 238 return 3; 239 240 /* Step 4: fix up any arguments */ 241 242 /* Step 5: check channel list if it exists */ 243 244 return 0; 245 } 246 247 static int ni6527_intr_cmd(struct comedi_device *dev, 248 struct comedi_subdevice *s) 249 { 250 writeb(NI6527_CLR_IRQS, dev->mmio + NI6527_CLR_REG); 251 writeb(NI6527_CTRL_ENABLE_IRQS, dev->mmio + NI6527_CTRL_REG); 252 253 return 0; 254 } 255 256 static int ni6527_intr_cancel(struct comedi_device *dev, 257 struct comedi_subdevice *s) 258 { 259 writeb(NI6527_CTRL_DISABLE_IRQS, dev->mmio + NI6527_CTRL_REG); 260 261 return 0; 262 } 263 264 static int ni6527_intr_insn_bits(struct comedi_device *dev, 265 struct comedi_subdevice *s, 266 struct comedi_insn *insn, unsigned int *data) 267 { 268 data[1] = 0; 269 return insn->n; 270 } 271 272 static void ni6527_set_edge_detection(struct comedi_device *dev, 273 unsigned int mask, 274 unsigned int rising, 275 unsigned int falling) 276 { 277 unsigned int i; 278 279 rising &= mask; 280 falling &= mask; 281 for (i = 0; i < 2; i++) { 282 if (mask & 0xff) { 283 if (~mask & 0xff) { 284 /* preserve rising-edge detection channels */ 285 rising |= readb(dev->mmio + 286 NI6527_RISING_EDGE_REG(i)) & 287 (~mask & 0xff); 288 /* preserve falling-edge detection channels */ 289 falling |= readb(dev->mmio + 290 NI6527_FALLING_EDGE_REG(i)) & 291 (~mask & 0xff); 292 } 293 /* update rising-edge detection channels */ 294 writeb(rising & 0xff, 295 dev->mmio + NI6527_RISING_EDGE_REG(i)); 296 /* update falling-edge detection channels */ 297 writeb(falling & 0xff, 298 dev->mmio + NI6527_FALLING_EDGE_REG(i)); 299 } 300 rising >>= 8; 301 falling >>= 8; 302 mask >>= 8; 303 } 304 } 305 306 static int ni6527_intr_insn_config(struct comedi_device *dev, 307 struct comedi_subdevice *s, 308 struct comedi_insn *insn, 309 unsigned int *data) 310 { 311 unsigned int mask = 0xffffffff; 312 unsigned int rising, falling, shift; 313 314 switch (data[0]) { 315 case INSN_CONFIG_CHANGE_NOTIFY: 316 /* check_insn_config_length() does not check this instruction */ 317 if (insn->n != 3) 318 return -EINVAL; 319 rising = data[1]; 320 falling = data[2]; 321 ni6527_set_edge_detection(dev, mask, rising, falling); 322 break; 323 case INSN_CONFIG_DIGITAL_TRIG: 324 /* check trigger number */ 325 if (data[1] != 0) 326 return -EINVAL; 327 /* check digital trigger operation */ 328 switch (data[2]) { 329 case COMEDI_DIGITAL_TRIG_DISABLE: 330 rising = 0; 331 falling = 0; 332 break; 333 case COMEDI_DIGITAL_TRIG_ENABLE_EDGES: 334 /* check shift amount */ 335 shift = data[3]; 336 if (shift >= 32) { 337 mask = 0; 338 rising = 0; 339 falling = 0; 340 } else { 341 mask <<= shift; 342 rising = data[4] << shift; 343 falling = data[5] << shift; 344 } 345 break; 346 default: 347 return -EINVAL; 348 } 349 ni6527_set_edge_detection(dev, mask, rising, falling); 350 break; 351 default: 352 return -EINVAL; 353 } 354 355 return insn->n; 356 } 357 358 static void ni6527_reset(struct comedi_device *dev) 359 { 360 /* disable deglitch filters on all channels */ 361 ni6527_set_filter_enable(dev, 0); 362 363 /* disable edge detection */ 364 ni6527_set_edge_detection(dev, 0xffffffff, 0, 0); 365 366 writeb(NI6527_CLR_IRQS | NI6527_CLR_RESET_FILT, 367 dev->mmio + NI6527_CLR_REG); 368 writeb(NI6527_CTRL_DISABLE_IRQS, dev->mmio + NI6527_CTRL_REG); 369 } 370 371 static int ni6527_auto_attach(struct comedi_device *dev, 372 unsigned long context) 373 { 374 struct pci_dev *pcidev = comedi_to_pci_dev(dev); 375 const struct ni6527_board *board = NULL; 376 struct ni6527_private *devpriv; 377 struct comedi_subdevice *s; 378 int ret; 379 380 if (context < ARRAY_SIZE(ni6527_boards)) 381 board = &ni6527_boards[context]; 382 if (!board) 383 return -ENODEV; 384 dev->board_ptr = board; 385 dev->board_name = board->name; 386 387 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 388 if (!devpriv) 389 return -ENOMEM; 390 391 ret = comedi_pci_enable(dev); 392 if (ret) 393 return ret; 394 395 dev->mmio = pci_ioremap_bar(pcidev, 1); 396 if (!dev->mmio) 397 return -ENOMEM; 398 399 /* make sure this is actually a 6527 device */ 400 if (readb(dev->mmio + NI6527_ID_REG) != 0x27) 401 return -ENODEV; 402 403 ni6527_reset(dev); 404 405 ret = request_irq(pcidev->irq, ni6527_interrupt, IRQF_SHARED, 406 dev->board_name, dev); 407 if (ret == 0) 408 dev->irq = pcidev->irq; 409 410 ret = comedi_alloc_subdevices(dev, 3); 411 if (ret) 412 return ret; 413 414 /* Digital Input subdevice */ 415 s = &dev->subdevices[0]; 416 s->type = COMEDI_SUBD_DI; 417 s->subdev_flags = SDF_READABLE; 418 s->n_chan = 24; 419 s->maxdata = 1; 420 s->range_table = &range_digital; 421 s->insn_config = ni6527_di_insn_config; 422 s->insn_bits = ni6527_di_insn_bits; 423 424 /* Digital Output subdevice */ 425 s = &dev->subdevices[1]; 426 s->type = COMEDI_SUBD_DO; 427 s->subdev_flags = SDF_WRITABLE; 428 s->n_chan = 24; 429 s->maxdata = 1; 430 s->range_table = &range_digital; 431 s->insn_bits = ni6527_do_insn_bits; 432 433 /* Edge detection interrupt subdevice */ 434 s = &dev->subdevices[2]; 435 if (dev->irq) { 436 dev->read_subdev = s; 437 s->type = COMEDI_SUBD_DI; 438 s->subdev_flags = SDF_READABLE | SDF_CMD_READ; 439 s->n_chan = 1; 440 s->maxdata = 1; 441 s->range_table = &range_digital; 442 s->insn_config = ni6527_intr_insn_config; 443 s->insn_bits = ni6527_intr_insn_bits; 444 s->len_chanlist = 1; 445 s->do_cmdtest = ni6527_intr_cmdtest; 446 s->do_cmd = ni6527_intr_cmd; 447 s->cancel = ni6527_intr_cancel; 448 } else { 449 s->type = COMEDI_SUBD_UNUSED; 450 } 451 452 return 0; 453 } 454 455 static void ni6527_detach(struct comedi_device *dev) 456 { 457 if (dev->mmio) 458 ni6527_reset(dev); 459 comedi_pci_detach(dev); 460 } 461 462 static struct comedi_driver ni6527_driver = { 463 .driver_name = "ni_6527", 464 .module = THIS_MODULE, 465 .auto_attach = ni6527_auto_attach, 466 .detach = ni6527_detach, 467 }; 468 469 static int ni6527_pci_probe(struct pci_dev *dev, 470 const struct pci_device_id *id) 471 { 472 return comedi_pci_auto_config(dev, &ni6527_driver, id->driver_data); 473 } 474 475 static const struct pci_device_id ni6527_pci_table[] = { 476 { PCI_VDEVICE(NI, 0x2b10), BOARD_PXI6527 }, 477 { PCI_VDEVICE(NI, 0x2b20), BOARD_PCI6527 }, 478 { 0 } 479 }; 480 MODULE_DEVICE_TABLE(pci, ni6527_pci_table); 481 482 static struct pci_driver ni6527_pci_driver = { 483 .name = "ni_6527", 484 .id_table = ni6527_pci_table, 485 .probe = ni6527_pci_probe, 486 .remove = comedi_pci_auto_unconfig, 487 }; 488 module_comedi_pci_driver(ni6527_driver, ni6527_pci_driver); 489 490 MODULE_AUTHOR("Comedi https://www.comedi.org"); 491 MODULE_DESCRIPTION("Comedi driver for National Instruments PCI-6527"); 492 MODULE_LICENSE("GPL"); 493