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