staging: comedi: ni_6527: remove subdevice pointer math
[linux-2.6-block.git] / drivers / staging / comedi / drivers / ni_6527.c
CommitLineData
ef2ccffb
DS
1/*
2 comedi/drivers/ni_6527.c
3 driver for National Instruments PCI-6527
4
5 COMEDI - Linux Control and Measurement Device Interface
6 Copyright (C) 1999,2002,2003 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_6527
25Description: National Instruments 6527
26Author: ds
27Status: works
28Devices: [National Instruments] PCI-6527 (ni6527), PXI-6527
29Updated: Sat, 25 Jan 2003 13:24:40 -0800
30
31
32*/
33
34/*
35 Manuals (available from ftp://ftp.natinst.com/support/manuals)
36
37 370106b.pdf 6527 Register Level Programmer Manual
38
39 */
40
41#define DEBUG 1
42#define DEBUG_FLAGS
43
25436dc9 44#include <linux/interrupt.h>
ef2ccffb
DS
45#include "../comedidev.h"
46
47#include "mite.h"
48
49#define NI6527_DIO_SIZE 4096
50#define NI6527_MITE_SIZE 4096
51
52#define Port_Register(x) (0x00+(x))
53#define ID_Register 0x06
54
55#define Clear_Register 0x07
56#define ClrEdge 0x08
57#define ClrOverflow 0x04
58#define ClrFilter 0x02
59#define ClrInterval 0x01
60
61#define Filter_Interval(x) (0x08+(x))
62#define Filter_Enable(x) (0x0c+(x))
63
64#define Change_Status 0x14
65#define MasterInterruptStatus 0x04
66#define Overflow 0x02
67#define EdgeStatus 0x01
68
69#define Master_Interrupt_Control 0x15
70#define FallingEdgeIntEnable 0x10
71#define RisingEdgeIntEnable 0x08
72#define MasterInterruptEnable 0x04
73#define OverflowIntEnable 0x02
74#define EdgeIntEnable 0x01
75
76#define Rising_Edge_Detection_Enable(x) (0x018+(x))
77#define Falling_Edge_Detection_Enable(x) (0x020+(x))
78
0a85b6f0
MT
79static int ni6527_attach(struct comedi_device *dev,
80 struct comedi_devconfig *it);
484ecc95 81static void ni6527_detach(struct comedi_device *dev);
139dfbdf 82static struct comedi_driver driver_ni6527 = {
68c3dbff
BP
83 .driver_name = "ni6527",
84 .module = THIS_MODULE,
85 .attach = ni6527_attach,
86 .detach = ni6527_detach,
ef2ccffb
DS
87};
88
16d38ca3
BP
89struct ni6527_board {
90
ef2ccffb
DS
91 int dev_id;
92 const char *name;
16d38ca3
BP
93};
94
95static const struct ni6527_board ni6527_boards[] = {
ef2ccffb 96 {
0a85b6f0
MT
97 .dev_id = 0x2b20,
98 .name = "pci-6527",
99 },
ef2ccffb 100 {
0a85b6f0
MT
101 .dev_id = 0x2b10,
102 .name = "pxi-6527",
103 },
ef2ccffb
DS
104};
105
b6ac1613 106#define n_ni6527_boards ARRAY_SIZE(ni6527_boards)
16d38ca3 107#define this_board ((const struct ni6527_board *)dev->board_ptr)
ef2ccffb
DS
108
109static DEFINE_PCI_DEVICE_TABLE(ni6527_pci_table) = {
4e40cee9
GKH
110 {PCI_DEVICE(PCI_VENDOR_ID_NI, 0x2b10)},
111 {PCI_DEVICE(PCI_VENDOR_ID_NI, 0x2b20)},
112 {0}
ef2ccffb
DS
113};
114
115MODULE_DEVICE_TABLE(pci, ni6527_pci_table);
116
f4c6b319 117struct ni6527_private {
ef2ccffb
DS
118 struct mite_struct *mite;
119 unsigned int filter_interval;
120 unsigned int filter_enable;
f4c6b319
BP
121};
122
123#define devpriv ((struct ni6527_private *)dev->private)
ef2ccffb 124
da91b269 125static int ni6527_find_device(struct comedi_device *dev, int bus, int slot);
ef2ccffb 126
0a85b6f0
MT
127static int ni6527_di_insn_config(struct comedi_device *dev,
128 struct comedi_subdevice *s,
129 struct comedi_insn *insn, unsigned int *data)
ef2ccffb
DS
130{
131 int chan = CR_CHAN(insn->chanspec);
132 unsigned int interval;
133
134 if (insn->n != 2)
135 return -EINVAL;
136
137 if (data[0] != INSN_CONFIG_FILTER)
138 return -EINVAL;
139
140 if (data[1]) {
141 interval = (data[1] + 100) / 200;
142 data[1] = interval * 200;
143
144 if (interval != devpriv->filter_interval) {
145 writeb(interval & 0xff,
0a85b6f0 146 devpriv->mite->daq_io_addr + Filter_Interval(0));
ef2ccffb 147 writeb((interval >> 8) & 0xff,
0a85b6f0 148 devpriv->mite->daq_io_addr + Filter_Interval(1));
ef2ccffb 149 writeb((interval >> 16) & 0x0f,
0a85b6f0 150 devpriv->mite->daq_io_addr + Filter_Interval(2));
ef2ccffb
DS
151
152 writeb(ClrInterval,
0a85b6f0 153 devpriv->mite->daq_io_addr + Clear_Register);
ef2ccffb
DS
154
155 devpriv->filter_interval = interval;
156 }
157
158 devpriv->filter_enable |= 1 << chan;
159 } else {
160 devpriv->filter_enable &= ~(1 << chan);
161 }
162
163 writeb(devpriv->filter_enable,
0a85b6f0 164 devpriv->mite->daq_io_addr + Filter_Enable(0));
ef2ccffb 165 writeb(devpriv->filter_enable >> 8,
0a85b6f0 166 devpriv->mite->daq_io_addr + Filter_Enable(1));
ef2ccffb 167 writeb(devpriv->filter_enable >> 16,
0a85b6f0 168 devpriv->mite->daq_io_addr + Filter_Enable(2));
ef2ccffb
DS
169
170 return 2;
171}
172
0a85b6f0
MT
173static int ni6527_di_insn_bits(struct comedi_device *dev,
174 struct comedi_subdevice *s,
175 struct comedi_insn *insn, unsigned int *data)
ef2ccffb 176{
ef2ccffb
DS
177 data[1] = readb(devpriv->mite->daq_io_addr + Port_Register(0));
178 data[1] |= readb(devpriv->mite->daq_io_addr + Port_Register(1)) << 8;
179 data[1] |= readb(devpriv->mite->daq_io_addr + Port_Register(2)) << 16;
180
a2714e3e 181 return insn->n;
ef2ccffb
DS
182}
183
0a85b6f0
MT
184static int ni6527_do_insn_bits(struct comedi_device *dev,
185 struct comedi_subdevice *s,
186 struct comedi_insn *insn, unsigned int *data)
ef2ccffb 187{
ef2ccffb
DS
188 if (data[0]) {
189 s->state &= ~data[0];
190 s->state |= (data[0] & data[1]);
191
192 /* The open relay state on the board cooresponds to 1,
193 * but in Comedi, it is represented by 0. */
194 if (data[0] & 0x0000ff) {
195 writeb((s->state ^ 0xff),
0a85b6f0 196 devpriv->mite->daq_io_addr + Port_Register(3));
ef2ccffb
DS
197 }
198 if (data[0] & 0x00ff00) {
199 writeb((s->state >> 8) ^ 0xff,
0a85b6f0 200 devpriv->mite->daq_io_addr + Port_Register(4));
ef2ccffb
DS
201 }
202 if (data[0] & 0xff0000) {
203 writeb((s->state >> 16) ^ 0xff,
0a85b6f0 204 devpriv->mite->daq_io_addr + Port_Register(5));
ef2ccffb
DS
205 }
206 }
207 data[1] = s->state;
208
a2714e3e 209 return insn->n;
ef2ccffb
DS
210}
211
70265d24 212static irqreturn_t ni6527_interrupt(int irq, void *d)
ef2ccffb 213{
71b5f4f1 214 struct comedi_device *dev = d;
3d30dca5 215 struct comedi_subdevice *s = &dev->subdevices[2];
ef2ccffb
DS
216 unsigned int status;
217
218 status = readb(devpriv->mite->daq_io_addr + Change_Status);
219 if ((status & MasterInterruptStatus) == 0)
220 return IRQ_NONE;
221 if ((status & EdgeStatus) == 0)
222 return IRQ_NONE;
223
224 writeb(ClrEdge | ClrOverflow,
0a85b6f0 225 devpriv->mite->daq_io_addr + Clear_Register);
ef2ccffb
DS
226
227 comedi_buf_put(s->async, 0);
228 s->async->events |= COMEDI_CB_EOS;
229 comedi_event(dev, s);
230 return IRQ_HANDLED;
231}
232
0a85b6f0
MT
233static int ni6527_intr_cmdtest(struct comedi_device *dev,
234 struct comedi_subdevice *s,
235 struct comedi_cmd *cmd)
ef2ccffb
DS
236{
237 int err = 0;
238 int tmp;
239
240 /* step 1: make sure trigger sources are trivially valid */
241
242 tmp = cmd->start_src;
243 cmd->start_src &= TRIG_NOW;
244 if (!cmd->start_src || tmp != cmd->start_src)
245 err++;
246
247 tmp = cmd->scan_begin_src;
248 cmd->scan_begin_src &= TRIG_OTHER;
249 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
250 err++;
251
252 tmp = cmd->convert_src;
253 cmd->convert_src &= TRIG_FOLLOW;
254 if (!cmd->convert_src || tmp != cmd->convert_src)
255 err++;
256
257 tmp = cmd->scan_end_src;
258 cmd->scan_end_src &= TRIG_COUNT;
259 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
260 err++;
261
262 tmp = cmd->stop_src;
263 cmd->stop_src &= TRIG_COUNT;
264 if (!cmd->stop_src || tmp != cmd->stop_src)
265 err++;
266
267 if (err)
268 return 1;
269
a8c5c198
KG
270 /* step 2: make sure trigger sources are unique and */
271 /* are mutually compatible */
ef2ccffb
DS
272
273 if (err)
274 return 2;
275
276 /* step 3: make sure arguments are trivially compatible */
277
278 if (cmd->start_arg != 0) {
279 cmd->start_arg = 0;
280 err++;
281 }
282 if (cmd->scan_begin_arg != 0) {
283 cmd->scan_begin_arg = 0;
284 err++;
285 }
286 if (cmd->convert_arg != 0) {
287 cmd->convert_arg = 0;
288 err++;
289 }
290
291 if (cmd->scan_end_arg != 1) {
292 cmd->scan_end_arg = 1;
293 err++;
294 }
295 if (cmd->stop_arg != 0) {
296 cmd->stop_arg = 0;
297 err++;
298 }
299
300 if (err)
301 return 3;
302
303 /* step 4: fix up any arguments */
304
305 if (err)
306 return 4;
307
308 return 0;
309}
310
0a85b6f0
MT
311static int ni6527_intr_cmd(struct comedi_device *dev,
312 struct comedi_subdevice *s)
ef2ccffb 313{
2696fb57 314 /* struct comedi_cmd *cmd = &s->async->cmd; */
ef2ccffb
DS
315
316 writeb(ClrEdge | ClrOverflow,
0a85b6f0 317 devpriv->mite->daq_io_addr + Clear_Register);
ef2ccffb 318 writeb(FallingEdgeIntEnable | RisingEdgeIntEnable |
0a85b6f0
MT
319 MasterInterruptEnable | EdgeIntEnable,
320 devpriv->mite->daq_io_addr + Master_Interrupt_Control);
ef2ccffb
DS
321
322 return 0;
323}
324
0a85b6f0
MT
325static int ni6527_intr_cancel(struct comedi_device *dev,
326 struct comedi_subdevice *s)
ef2ccffb
DS
327{
328 writeb(0x00, devpriv->mite->daq_io_addr + Master_Interrupt_Control);
329
330 return 0;
331}
332
0a85b6f0
MT
333static int ni6527_intr_insn_bits(struct comedi_device *dev,
334 struct comedi_subdevice *s,
335 struct comedi_insn *insn, unsigned int *data)
ef2ccffb 336{
ef2ccffb 337 data[1] = 0;
a2714e3e 338 return insn->n;
ef2ccffb
DS
339}
340
0a85b6f0
MT
341static int ni6527_intr_insn_config(struct comedi_device *dev,
342 struct comedi_subdevice *s,
343 struct comedi_insn *insn, unsigned int *data)
ef2ccffb
DS
344{
345 if (insn->n < 1)
346 return -EINVAL;
347 if (data[0] != INSN_CONFIG_CHANGE_NOTIFY)
348 return -EINVAL;
349
350 writeb(data[1],
0a85b6f0 351 devpriv->mite->daq_io_addr + Rising_Edge_Detection_Enable(0));
ef2ccffb 352 writeb(data[1] >> 8,
0a85b6f0 353 devpriv->mite->daq_io_addr + Rising_Edge_Detection_Enable(1));
ef2ccffb 354 writeb(data[1] >> 16,
0a85b6f0 355 devpriv->mite->daq_io_addr + Rising_Edge_Detection_Enable(2));
ef2ccffb
DS
356
357 writeb(data[2],
0a85b6f0 358 devpriv->mite->daq_io_addr + Falling_Edge_Detection_Enable(0));
ef2ccffb 359 writeb(data[2] >> 8,
0a85b6f0 360 devpriv->mite->daq_io_addr + Falling_Edge_Detection_Enable(1));
ef2ccffb 361 writeb(data[2] >> 16,
0a85b6f0 362 devpriv->mite->daq_io_addr + Falling_Edge_Detection_Enable(2));
ef2ccffb
DS
363
364 return 2;
365}
366
da91b269 367static int ni6527_attach(struct comedi_device *dev, struct comedi_devconfig *it)
ef2ccffb 368{
34c43922 369 struct comedi_subdevice *s;
ef2ccffb
DS
370 int ret;
371
a8c5c198 372 printk(KERN_INFO "comedi%d: ni6527\n", dev->minor);
ef2ccffb 373
c3744138
BP
374 ret = alloc_private(dev, sizeof(struct ni6527_private));
375 if (ret < 0)
ef2ccffb
DS
376 return ret;
377
378 ret = ni6527_find_device(dev, it->options[0], it->options[1]);
379 if (ret < 0)
380 return ret;
381
382 ret = mite_setup(devpriv->mite);
383 if (ret < 0) {
a8c5c198 384 printk(KERN_ERR "comedi: error setting up mite\n");
ef2ccffb
DS
385 return ret;
386 }
387
388 dev->board_name = this_board->name;
a8c5c198
KG
389 printk(KERN_INFO "comedi board: %s, ID=0x%02x\n", dev->board_name,
390 readb(devpriv->mite->daq_io_addr + ID_Register));
ef2ccffb 391
2f0b9d08 392 ret = comedi_alloc_subdevices(dev, 3);
8b6c5694 393 if (ret)
ef2ccffb
DS
394 return ret;
395
3d30dca5 396 s = &dev->subdevices[0];
ef2ccffb
DS
397 s->type = COMEDI_SUBD_DI;
398 s->subdev_flags = SDF_READABLE;
399 s->n_chan = 24;
400 s->range_table = &range_digital;
401 s->maxdata = 1;
402 s->insn_config = ni6527_di_insn_config;
403 s->insn_bits = ni6527_di_insn_bits;
404
3d30dca5 405 s = &dev->subdevices[1];
ef2ccffb
DS
406 s->type = COMEDI_SUBD_DO;
407 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
408 s->n_chan = 24;
a8c5c198 409 s->range_table = &range_unknown; /* FIXME: actually conductance */
ef2ccffb
DS
410 s->maxdata = 1;
411 s->insn_bits = ni6527_do_insn_bits;
412
3d30dca5 413 s = &dev->subdevices[2];
ef2ccffb
DS
414 dev->read_subdev = s;
415 s->type = COMEDI_SUBD_DI;
416 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
417 s->n_chan = 1;
418 s->range_table = &range_unknown;
419 s->maxdata = 1;
420 s->do_cmdtest = ni6527_intr_cmdtest;
421 s->do_cmd = ni6527_intr_cmd;
422 s->cancel = ni6527_intr_cancel;
423 s->insn_bits = ni6527_intr_insn_bits;
424 s->insn_config = ni6527_intr_insn_config;
425
426 writeb(0x00, devpriv->mite->daq_io_addr + Filter_Enable(0));
427 writeb(0x00, devpriv->mite->daq_io_addr + Filter_Enable(1));
428 writeb(0x00, devpriv->mite->daq_io_addr + Filter_Enable(2));
429
430 writeb(ClrEdge | ClrOverflow | ClrFilter | ClrInterval,
0a85b6f0 431 devpriv->mite->daq_io_addr + Clear_Register);
ef2ccffb
DS
432 writeb(0x00, devpriv->mite->daq_io_addr + Master_Interrupt_Control);
433
5f74ea14
GKH
434 ret = request_irq(mite_irq(devpriv->mite), ni6527_interrupt,
435 IRQF_SHARED, "ni6527", dev);
a8c5c198
KG
436 if (ret < 0)
437 printk(KERN_WARNING "comedi i6527 irq not available\n");
438 else
ef2ccffb
DS
439 dev->irq = mite_irq(devpriv->mite);
440
ef2ccffb
DS
441 return 0;
442}
443
484ecc95 444static void ni6527_detach(struct comedi_device *dev)
ef2ccffb 445{
a8c5c198 446 if (devpriv && devpriv->mite && devpriv->mite->daq_io_addr)
ef2ccffb 447 writeb(0x00,
0a85b6f0 448 devpriv->mite->daq_io_addr + Master_Interrupt_Control);
a8c5c198 449 if (dev->irq)
5f74ea14 450 free_irq(dev->irq, dev);
a8c5c198 451 if (devpriv && devpriv->mite)
ef2ccffb 452 mite_unsetup(devpriv->mite);
ef2ccffb
DS
453}
454
da91b269 455static int ni6527_find_device(struct comedi_device *dev, int bus, int slot)
ef2ccffb
DS
456{
457 struct mite_struct *mite;
458 int i;
459
460 for (mite = mite_devices; mite; mite = mite->next) {
461 if (mite->used)
462 continue;
463 if (bus || slot) {
464 if (bus != mite->pcidev->bus->number ||
0a85b6f0 465 slot != PCI_SLOT(mite->pcidev->devfn))
ef2ccffb
DS
466 continue;
467 }
468 for (i = 0; i < n_ni6527_boards; i++) {
469 if (mite_device_id(mite) == ni6527_boards[i].dev_id) {
470 dev->board_ptr = ni6527_boards + i;
471 devpriv->mite = mite;
472 return 0;
473 }
474 }
475 }
a8c5c198 476 printk(KERN_ERR "comedi 6527: no device found\n");
ef2ccffb
DS
477 mite_list_devices();
478 return -EIO;
479}
480
727b286b
AT
481static int __devinit driver_ni6527_pci_probe(struct pci_dev *dev,
482 const struct pci_device_id *ent)
483{
4c093a6d 484 return comedi_pci_auto_config(dev, &driver_ni6527);
727b286b
AT
485}
486
487static void __devexit driver_ni6527_pci_remove(struct pci_dev *dev)
488{
489 comedi_pci_auto_unconfig(dev);
490}
491
492static struct pci_driver driver_ni6527_pci_driver = {
493 .id_table = ni6527_pci_table,
494 .probe = &driver_ni6527_pci_probe,
495 .remove = __devexit_p(&driver_ni6527_pci_remove)
496};
497
498static int __init driver_ni6527_init_module(void)
499{
500 int retval;
501
502 retval = comedi_driver_register(&driver_ni6527);
503 if (retval < 0)
504 return retval;
505
506 driver_ni6527_pci_driver.name = (char *)driver_ni6527.driver_name;
507 return pci_register_driver(&driver_ni6527_pci_driver);
508}
509
510static void __exit driver_ni6527_cleanup_module(void)
511{
512 pci_unregister_driver(&driver_ni6527_pci_driver);
513 comedi_driver_unregister(&driver_ni6527);
514}
515
516module_init(driver_ni6527_init_module);
517module_exit(driver_ni6527_cleanup_module);
3c323c01
IA
518
519MODULE_AUTHOR("Comedi http://www.comedi.org");
520MODULE_DESCRIPTION("Comedi low-level driver");
521MODULE_LICENSE("GPL");