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