ni_670x.c revision 0a85b6f0ab0d2edb0d41b32697111ce0e4f43496
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 "../comedidev.h" 46 47#include "mite.h" 48 49#define PCI_VENDOR_ID_NATINST 0x1093 50 51#define AO_VALUE_OFFSET 0x00 52#define AO_CHAN_OFFSET 0x0c 53#define AO_STATUS_OFFSET 0x10 54#define AO_CONTROL_OFFSET 0x10 55#define DIO_PORT0_DIR_OFFSET 0x20 56#define DIO_PORT0_DATA_OFFSET 0x24 57#define DIO_PORT1_DIR_OFFSET 0x28 58#define DIO_PORT1_DATA_OFFSET 0x2c 59#define MISC_STATUS_OFFSET 0x14 60#define MISC_CONTROL_OFFSET 0x14 61 62/* Board description*/ 63 64struct ni_670x_board { 65 unsigned short dev_id; 66 const char *name; 67 unsigned short ao_chans; 68 unsigned short ao_bits; 69}; 70 71static const struct ni_670x_board ni_670x_boards[] = { 72 { 73 .dev_id = 0x2c90, 74 .name = "PCI-6703", 75 .ao_chans = 16, 76 .ao_bits = 16, 77 }, 78 { 79 .dev_id = 0x1920, 80 .name = "PXI-6704", 81 .ao_chans = 32, 82 .ao_bits = 16, 83 }, 84 { 85 .dev_id = 0x1290, 86 .name = "PCI-6704", 87 .ao_chans = 32, 88 .ao_bits = 16, 89 }, 90}; 91 92static DEFINE_PCI_DEVICE_TABLE(ni_670x_pci_table) = { 93 { 94 PCI_VENDOR_ID_NATINST, 0x2c90, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { 95 PCI_VENDOR_ID_NATINST, 0x1920, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 96 /* { PCI_VENDOR_ID_NATINST, 0x0000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, */ 97 { 98 0} 99}; 100 101MODULE_DEVICE_TABLE(pci, ni_670x_pci_table); 102 103#define thisboard ((struct ni_670x_board *)dev->board_ptr) 104 105struct ni_670x_private { 106 107 struct mite_struct *mite; 108 int boardtype; 109 int dio; 110 unsigned int ao_readback[32]; 111}; 112 113#define devpriv ((struct ni_670x_private *)dev->private) 114#define n_ni_670x_boards (sizeof(ni_670x_boards)/sizeof(ni_670x_boards[0])) 115 116static int ni_670x_attach(struct comedi_device *dev, 117 struct comedi_devconfig *it); 118static int ni_670x_detach(struct comedi_device *dev); 119 120static struct comedi_driver driver_ni_670x = { 121 .driver_name = "ni_670x", 122 .module = THIS_MODULE, 123 .attach = ni_670x_attach, 124 .detach = ni_670x_detach, 125}; 126 127COMEDI_PCI_INITCLEANUP(driver_ni_670x, ni_670x_pci_table); 128 129static struct comedi_lrange range_0_20mA = { 1, {RANGE_mA(0, 20)} }; 130 131static int ni_670x_find_device(struct comedi_device *dev, int bus, int slot); 132 133static int ni_670x_ao_winsn(struct comedi_device *dev, 134 struct comedi_subdevice *s, 135 struct comedi_insn *insn, unsigned int *data); 136static int ni_670x_ao_rinsn(struct comedi_device *dev, 137 struct comedi_subdevice *s, 138 struct comedi_insn *insn, unsigned int *data); 139static int ni_670x_dio_insn_bits(struct comedi_device *dev, 140 struct comedi_subdevice *s, 141 struct comedi_insn *insn, unsigned int *data); 142static int ni_670x_dio_insn_config(struct comedi_device *dev, 143 struct comedi_subdevice *s, 144 struct comedi_insn *insn, 145 unsigned int *data); 146 147static int ni_670x_attach(struct comedi_device *dev, 148 struct comedi_devconfig *it) 149{ 150 struct comedi_subdevice *s; 151 int ret; 152 int i; 153 154 printk("comedi%d: ni_670x: ", dev->minor); 155 156 ret = alloc_private(dev, sizeof(struct ni_670x_private)); 157 if (ret < 0) 158 return ret; 159 160 ret = ni_670x_find_device(dev, it->options[0], it->options[1]); 161 if (ret < 0) 162 return ret; 163 164 ret = mite_setup(devpriv->mite); 165 if (ret < 0) { 166 printk("error setting up mite\n"); 167 return ret; 168 } 169 dev->board_name = thisboard->name; 170 dev->irq = mite_irq(devpriv->mite); 171 printk(" %s", dev->board_name); 172 173 if (alloc_subdevices(dev, 2) < 0) 174 return -ENOMEM; 175 176 s = dev->subdevices + 0; 177 /* analog output subdevice */ 178 s->type = COMEDI_SUBD_AO; 179 s->subdev_flags = SDF_WRITABLE; 180 s->n_chan = thisboard->ao_chans; 181 s->maxdata = 0xffff; 182 if (s->n_chan == 32) { 183 const struct comedi_lrange **range_table_list; 184 185 range_table_list = kmalloc(sizeof(struct comedi_lrange *) * 32, 186 GFP_KERNEL); 187 if (!range_table_list) 188 return -ENOMEM; 189 s->range_table_list = range_table_list; 190 for (i = 0; i < 16; i++) { 191 range_table_list[i] = &range_bipolar10; 192 range_table_list[16 + i] = &range_0_20mA; 193 } 194 } else { 195 s->range_table = &range_bipolar10; 196 } 197 s->insn_write = &ni_670x_ao_winsn; 198 s->insn_read = &ni_670x_ao_rinsn; 199 200 s = dev->subdevices + 1; 201 /* digital i/o subdevice */ 202 s->type = COMEDI_SUBD_DIO; 203 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 204 s->n_chan = 8; 205 s->maxdata = 1; 206 s->range_table = &range_digital; 207 s->insn_bits = ni_670x_dio_insn_bits; 208 s->insn_config = ni_670x_dio_insn_config; 209 210 writel(0x10, devpriv->mite->daq_io_addr + MISC_CONTROL_OFFSET); /* Config of misc registers */ 211 writel(0x00, devpriv->mite->daq_io_addr + AO_CONTROL_OFFSET); /* Config of ao registers */ 212 213 printk("attached\n"); 214 215 return 1; 216} 217 218static int ni_670x_detach(struct comedi_device *dev) 219{ 220 printk("comedi%d: ni_670x: remove\n", dev->minor); 221 222 if (dev->subdevices[0].range_table_list) { 223 kfree(dev->subdevices[0].range_table_list); 224 } 225 if (dev->private && devpriv->mite) 226 mite_unsetup(devpriv->mite); 227 228 if (dev->irq) 229 free_irq(dev->irq, dev); 230 231 return 0; 232} 233 234static int ni_670x_ao_winsn(struct comedi_device *dev, 235 struct comedi_subdevice *s, 236 struct comedi_insn *insn, unsigned int *data) 237{ 238 int i; 239 int chan = CR_CHAN(insn->chanspec); 240 241 /* Channel number mapping : 242 243 NI 6703/ NI 6704 | NI 6704 Only 244 ---------------------------------------------------- 245 vch(0) : 0 | ich(16) : 1 246 vch(1) : 2 | ich(17) : 3 247 . : . | . . 248 . : . | . . 249 . : . | . . 250 vch(15) : 30 | ich(31) : 31 */ 251 252 for (i = 0; i < insn->n; i++) { 253 writel(((chan & 15) << 1) | ((chan & 16) >> 4), devpriv->mite->daq_io_addr + AO_CHAN_OFFSET); /* First write in channel register which channel to use */ 254 writel(data[i], devpriv->mite->daq_io_addr + AO_VALUE_OFFSET); /* write channel value */ 255 devpriv->ao_readback[chan] = data[i]; 256 } 257 258 return i; 259} 260 261static int ni_670x_ao_rinsn(struct comedi_device *dev, 262 struct comedi_subdevice *s, 263 struct comedi_insn *insn, unsigned int *data) 264{ 265 int i; 266 int chan = CR_CHAN(insn->chanspec); 267 268 for (i = 0; i < insn->n; i++) 269 data[i] = devpriv->ao_readback[chan]; 270 271 return i; 272} 273 274static int ni_670x_dio_insn_bits(struct comedi_device *dev, 275 struct comedi_subdevice *s, 276 struct comedi_insn *insn, unsigned int *data) 277{ 278 if (insn->n != 2) 279 return -EINVAL; 280 281 /* The insn data is a mask in data[0] and the new data 282 * in data[1], each channel cooresponding to a bit. */ 283 if (data[0]) { 284 s->state &= ~data[0]; 285 s->state |= data[0] & data[1]; 286 writel(s->state, 287 devpriv->mite->daq_io_addr + DIO_PORT0_DATA_OFFSET); 288 } 289 290 /* on return, data[1] contains the value of the digital 291 * input lines. */ 292 data[1] = readl(devpriv->mite->daq_io_addr + DIO_PORT0_DATA_OFFSET); 293 294 return 2; 295} 296 297static int ni_670x_dio_insn_config(struct comedi_device *dev, 298 struct comedi_subdevice *s, 299 struct comedi_insn *insn, unsigned int *data) 300{ 301 int chan = CR_CHAN(insn->chanspec); 302 303 switch (data[0]) { 304 case INSN_CONFIG_DIO_OUTPUT: 305 s->io_bits |= 1 << chan; 306 break; 307 case INSN_CONFIG_DIO_INPUT: 308 s->io_bits &= ~(1 << chan); 309 break; 310 case INSN_CONFIG_DIO_QUERY: 311 data[1] = 312 (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT; 313 return insn->n; 314 break; 315 default: 316 return -EINVAL; 317 break; 318 } 319 writel(s->io_bits, devpriv->mite->daq_io_addr + DIO_PORT0_DIR_OFFSET); 320 321 return insn->n; 322} 323 324static int ni_670x_find_device(struct comedi_device *dev, int bus, int slot) 325{ 326 struct mite_struct *mite; 327 int i; 328 329 for (mite = mite_devices; mite; mite = mite->next) { 330 if (mite->used) 331 continue; 332 if (bus || slot) { 333 if (bus != mite->pcidev->bus->number 334 || slot != PCI_SLOT(mite->pcidev->devfn)) 335 continue; 336 } 337 338 for (i = 0; i < n_ni_670x_boards; i++) { 339 if (mite_device_id(mite) == ni_670x_boards[i].dev_id) { 340 dev->board_ptr = ni_670x_boards + i; 341 devpriv->mite = mite; 342 343 return 0; 344 } 345 } 346 } 347 printk("no device found\n"); 348 mite_list_devices(); 349 return -EIO; 350} 351