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