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