1/* 2 comedi/drivers/ni_670x.c 3 Hardware driver for NI 670x devices 4 5 COMEDI - Linux Control and Measurement Device Interface 6 Copyright (C) 1997-2001 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 You should have received a copy of the GNU General Public License 19 along with this program; if not, write to the Free Software 20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 22*/ 23/* 24Driver: ni_670x 25Description: National Instruments 670x 26Author: Bart Joris <bjoris@advalvas.be> 27Updated: Wed, 11 Dec 2002 18:25:35 -0800 28Devices: [National Instruments] PCI-6703 (ni_670x), PCI-6704 29Status: unknown 30 31Commands are not supported. 32*/ 33 34/* 35 Bart Joris <bjoris@advalvas.be> Last updated on 20/08/2001 36 37 Manuals: 38 39 322110a.pdf PCI/PXI-6704 User Manual 40 322110b.pdf PCI/PXI-6703/6704 User Manual 41 42*/ 43 44#include <linux/interrupt.h> 45#include <linux/slab.h> 46#include "../comedidev.h" 47 48#include "mite.h" 49 50#define AO_VALUE_OFFSET 0x00 51#define AO_CHAN_OFFSET 0x0c 52#define AO_STATUS_OFFSET 0x10 53#define AO_CONTROL_OFFSET 0x10 54#define DIO_PORT0_DIR_OFFSET 0x20 55#define DIO_PORT0_DATA_OFFSET 0x24 56#define DIO_PORT1_DIR_OFFSET 0x28 57#define DIO_PORT1_DATA_OFFSET 0x2c 58#define MISC_STATUS_OFFSET 0x14 59#define MISC_CONTROL_OFFSET 0x14 60 61/* Board description*/ 62 63struct ni_670x_board { 64 unsigned short dev_id; 65 const char *name; 66 unsigned short ao_chans; 67 unsigned short ao_bits; 68}; 69 70static const struct ni_670x_board ni_670x_boards[] = { 71 { 72 .dev_id = 0x2c90, 73 .name = "PCI-6703", 74 .ao_chans = 16, 75 .ao_bits = 16, 76 }, 77 { 78 .dev_id = 0x1920, 79 .name = "PXI-6704", 80 .ao_chans = 32, 81 .ao_bits = 16, 82 }, 83 { 84 .dev_id = 0x1290, 85 .name = "PCI-6704", 86 .ao_chans = 32, 87 .ao_bits = 16, 88 }, 89}; 90 91static DEFINE_PCI_DEVICE_TABLE(ni_670x_pci_table) = { 92 {PCI_DEVICE(PCI_VENDOR_ID_NI, 0x2c90)}, 93 {PCI_DEVICE(PCI_VENDOR_ID_NI, 0x1920)}, 94 {0} 95}; 96 97MODULE_DEVICE_TABLE(pci, ni_670x_pci_table); 98 99#define thisboard ((struct ni_670x_board *)dev->board_ptr) 100 101struct ni_670x_private { 102 103 struct mite_struct *mite; 104 int boardtype; 105 int dio; 106 unsigned int ao_readback[32]; 107}; 108 109#define devpriv ((struct ni_670x_private *)dev->private) 110#define n_ni_670x_boards ARRAY_SIZE(ni_670x_boards) 111 112static int ni_670x_attach(struct comedi_device *dev, 113 struct comedi_devconfig *it); 114static int ni_670x_detach(struct comedi_device *dev); 115 116static struct comedi_driver driver_ni_670x = { 117 .driver_name = "ni_670x", 118 .module = THIS_MODULE, 119 .attach = ni_670x_attach, 120 .detach = ni_670x_detach, 121}; 122 123static int __devinit driver_ni_670x_pci_probe(struct pci_dev *dev, 124 const struct pci_device_id *ent) 125{ 126 return comedi_pci_auto_config(dev, driver_ni_670x.driver_name); 127} 128 129static void __devexit driver_ni_670x_pci_remove(struct pci_dev *dev) 130{ 131 comedi_pci_auto_unconfig(dev); 132} 133 134static struct pci_driver driver_ni_670x_pci_driver = { 135 .id_table = ni_670x_pci_table, 136 .probe = &driver_ni_670x_pci_probe, 137 .remove = __devexit_p(&driver_ni_670x_pci_remove) 138}; 139 140static int __init driver_ni_670x_init_module(void) 141{ 142 int retval; 143 144 retval = comedi_driver_register(&driver_ni_670x); 145 if (retval < 0) 146 return retval; 147 148 driver_ni_670x_pci_driver.name = (char *)driver_ni_670x.driver_name; 149 return pci_register_driver(&driver_ni_670x_pci_driver); 150} 151 152static void __exit driver_ni_670x_cleanup_module(void) 153{ 154 pci_unregister_driver(&driver_ni_670x_pci_driver); 155 comedi_driver_unregister(&driver_ni_670x); 156} 157 158module_init(driver_ni_670x_init_module); 159module_exit(driver_ni_670x_cleanup_module); 160 161static struct comedi_lrange range_0_20mA = { 1, {RANGE_mA(0, 20)} }; 162 163static int ni_670x_find_device(struct comedi_device *dev, int bus, int slot); 164 165static int ni_670x_ao_winsn(struct comedi_device *dev, 166 struct comedi_subdevice *s, 167 struct comedi_insn *insn, unsigned int *data); 168static int ni_670x_ao_rinsn(struct comedi_device *dev, 169 struct comedi_subdevice *s, 170 struct comedi_insn *insn, unsigned int *data); 171static int ni_670x_dio_insn_bits(struct comedi_device *dev, 172 struct comedi_subdevice *s, 173 struct comedi_insn *insn, unsigned int *data); 174static int ni_670x_dio_insn_config(struct comedi_device *dev, 175 struct comedi_subdevice *s, 176 struct comedi_insn *insn, 177 unsigned int *data); 178 179static int ni_670x_attach(struct comedi_device *dev, 180 struct comedi_devconfig *it) 181{ 182 struct comedi_subdevice *s; 183 int ret; 184 int i; 185 186 printk(KERN_INFO "comedi%d: ni_670x: ", dev->minor); 187 188 ret = alloc_private(dev, sizeof(struct ni_670x_private)); 189 if (ret < 0) 190 return ret; 191 192 ret = ni_670x_find_device(dev, it->options[0], it->options[1]); 193 if (ret < 0) 194 return ret; 195 196 ret = mite_setup(devpriv->mite); 197 if (ret < 0) { 198 printk(KERN_WARNING "error setting up mite\n"); 199 return ret; 200 } 201 dev->board_name = thisboard->name; 202 dev->irq = mite_irq(devpriv->mite); 203 printk(KERN_INFO " %s", dev->board_name); 204 205 if (alloc_subdevices(dev, 2) < 0) 206 return -ENOMEM; 207 208 s = dev->subdevices + 0; 209 /* analog output subdevice */ 210 s->type = COMEDI_SUBD_AO; 211 s->subdev_flags = SDF_WRITABLE; 212 s->n_chan = thisboard->ao_chans; 213 s->maxdata = 0xffff; 214 if (s->n_chan == 32) { 215 const struct comedi_lrange **range_table_list; 216 217 range_table_list = kmalloc(sizeof(struct comedi_lrange *) * 32, 218 GFP_KERNEL); 219 if (!range_table_list) 220 return -ENOMEM; 221 s->range_table_list = range_table_list; 222 for (i = 0; i < 16; i++) { 223 range_table_list[i] = &range_bipolar10; 224 range_table_list[16 + i] = &range_0_20mA; 225 } 226 } else { 227 s->range_table = &range_bipolar10; 228 } 229 s->insn_write = &ni_670x_ao_winsn; 230 s->insn_read = &ni_670x_ao_rinsn; 231 232 s = dev->subdevices + 1; 233 /* digital i/o subdevice */ 234 s->type = COMEDI_SUBD_DIO; 235 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 236 s->n_chan = 8; 237 s->maxdata = 1; 238 s->range_table = &range_digital; 239 s->insn_bits = ni_670x_dio_insn_bits; 240 s->insn_config = ni_670x_dio_insn_config; 241 242 /* Config of misc registers */ 243 writel(0x10, devpriv->mite->daq_io_addr + MISC_CONTROL_OFFSET); 244 /* Config of ao registers */ 245 writel(0x00, devpriv->mite->daq_io_addr + AO_CONTROL_OFFSET); 246 247 printk(KERN_INFO "attached\n"); 248 249 return 1; 250} 251 252static int ni_670x_detach(struct comedi_device *dev) 253{ 254 printk(KERN_INFO "comedi%d: ni_670x: remove\n", dev->minor); 255 256 kfree(dev->subdevices[0].range_table_list); 257 258 if (dev->private && devpriv->mite) 259 mite_unsetup(devpriv->mite); 260 261 if (dev->irq) 262 free_irq(dev->irq, dev); 263 264 return 0; 265} 266 267static int ni_670x_ao_winsn(struct comedi_device *dev, 268 struct comedi_subdevice *s, 269 struct comedi_insn *insn, unsigned int *data) 270{ 271 int i; 272 int chan = CR_CHAN(insn->chanspec); 273 274 /* Channel number mapping : 275 276 NI 6703/ NI 6704 | NI 6704 Only 277 ---------------------------------------------------- 278 vch(0) : 0 | ich(16) : 1 279 vch(1) : 2 | ich(17) : 3 280 . : . | . . 281 . : . | . . 282 . : . | . . 283 vch(15) : 30 | ich(31) : 31 */ 284 285 for (i = 0; i < insn->n; i++) { 286 /* First write in channel register which channel to use */ 287 writel(((chan & 15) << 1) | ((chan & 16) >> 4), 288 devpriv->mite->daq_io_addr + AO_CHAN_OFFSET); 289 /* write channel value */ 290 writel(data[i], devpriv->mite->daq_io_addr + AO_VALUE_OFFSET); 291 devpriv->ao_readback[chan] = data[i]; 292 } 293 294 return i; 295} 296 297static int ni_670x_ao_rinsn(struct comedi_device *dev, 298 struct comedi_subdevice *s, 299 struct comedi_insn *insn, unsigned int *data) 300{ 301 int i; 302 int chan = CR_CHAN(insn->chanspec); 303 304 for (i = 0; i < insn->n; i++) 305 data[i] = devpriv->ao_readback[chan]; 306 307 return i; 308} 309 310static int ni_670x_dio_insn_bits(struct comedi_device *dev, 311 struct comedi_subdevice *s, 312 struct comedi_insn *insn, unsigned int *data) 313{ 314 if (insn->n != 2) 315 return -EINVAL; 316 317 /* The insn data is a mask in data[0] and the new data 318 * in data[1], each channel cooresponding to a bit. */ 319 if (data[0]) { 320 s->state &= ~data[0]; 321 s->state |= data[0] & data[1]; 322 writel(s->state, 323 devpriv->mite->daq_io_addr + DIO_PORT0_DATA_OFFSET); 324 } 325 326 /* on return, data[1] contains the value of the digital 327 * input lines. */ 328 data[1] = readl(devpriv->mite->daq_io_addr + DIO_PORT0_DATA_OFFSET); 329 330 return 2; 331} 332 333static int ni_670x_dio_insn_config(struct comedi_device *dev, 334 struct comedi_subdevice *s, 335 struct comedi_insn *insn, unsigned int *data) 336{ 337 int chan = CR_CHAN(insn->chanspec); 338 339 switch (data[0]) { 340 case INSN_CONFIG_DIO_OUTPUT: 341 s->io_bits |= 1 << chan; 342 break; 343 case INSN_CONFIG_DIO_INPUT: 344 s->io_bits &= ~(1 << chan); 345 break; 346 case INSN_CONFIG_DIO_QUERY: 347 data[1] = 348 (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT; 349 return insn->n; 350 break; 351 default: 352 return -EINVAL; 353 break; 354 } 355 writel(s->io_bits, devpriv->mite->daq_io_addr + DIO_PORT0_DIR_OFFSET); 356 357 return insn->n; 358} 359 360static int ni_670x_find_device(struct comedi_device *dev, int bus, int slot) 361{ 362 struct mite_struct *mite; 363 int i; 364 365 for (mite = mite_devices; mite; mite = mite->next) { 366 if (mite->used) 367 continue; 368 if (bus || slot) { 369 if (bus != mite->pcidev->bus->number 370 || slot != PCI_SLOT(mite->pcidev->devfn)) 371 continue; 372 } 373 374 for (i = 0; i < n_ni_670x_boards; i++) { 375 if (mite_device_id(mite) == ni_670x_boards[i].dev_id) { 376 dev->board_ptr = ni_670x_boards + i; 377 devpriv->mite = mite; 378 379 return 0; 380 } 381 } 382 } 383 printk(KERN_INFO "no device found\n"); 384 mite_list_devices(); 385 return -EIO; 386} 387 388MODULE_AUTHOR("Comedi http://www.comedi.org"); 389MODULE_DESCRIPTION("Comedi low-level driver"); 390MODULE_LICENSE("GPL"); 391