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