1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30#include <linux/module.h>
31#include <linux/interrupt.h>
32
33#include "../comedi_pci.h"
34
35
36
37
38
39
40
41#define NI6527_DI_REG(x) (0x00 + (x))
42#define NI6527_DO_REG(x) (0x03 + (x))
43#define NI6527_ID_REG 0x06
44#define NI6527_CLR_REG 0x07
45#define NI6527_CLR_EDGE BIT(3)
46#define NI6527_CLR_OVERFLOW BIT(2)
47#define NI6527_CLR_FILT BIT(1)
48#define NI6527_CLR_INTERVAL BIT(0)
49#define NI6527_CLR_IRQS (NI6527_CLR_EDGE | NI6527_CLR_OVERFLOW)
50#define NI6527_CLR_RESET_FILT (NI6527_CLR_FILT | NI6527_CLR_INTERVAL)
51#define NI6527_FILT_INTERVAL_REG(x) (0x08 + (x))
52#define NI6527_FILT_ENA_REG(x) (0x0c + (x))
53#define NI6527_STATUS_REG 0x14
54#define NI6527_STATUS_IRQ BIT(2)
55#define NI6527_STATUS_OVERFLOW BIT(1)
56#define NI6527_STATUS_EDGE BIT(0)
57#define NI6527_CTRL_REG 0x15
58#define NI6527_CTRL_FALLING BIT(4)
59#define NI6527_CTRL_RISING BIT(3)
60#define NI6527_CTRL_IRQ BIT(2)
61#define NI6527_CTRL_OVERFLOW BIT(1)
62#define NI6527_CTRL_EDGE BIT(0)
63#define NI6527_CTRL_DISABLE_IRQS 0
64#define NI6527_CTRL_ENABLE_IRQS (NI6527_CTRL_FALLING | \
65 NI6527_CTRL_RISING | \
66 NI6527_CTRL_IRQ | NI6527_CTRL_EDGE)
67#define NI6527_RISING_EDGE_REG(x) (0x18 + (x))
68#define NI6527_FALLING_EDGE_REG(x) (0x20 + (x))
69
70enum ni6527_boardid {
71 BOARD_PCI6527,
72 BOARD_PXI6527,
73};
74
75struct ni6527_board {
76 const char *name;
77};
78
79static const struct ni6527_board ni6527_boards[] = {
80 [BOARD_PCI6527] = {
81 .name = "pci-6527",
82 },
83 [BOARD_PXI6527] = {
84 .name = "pxi-6527",
85 },
86};
87
88struct ni6527_private {
89 unsigned int filter_interval;
90 unsigned int filter_enable;
91};
92
93static void ni6527_set_filter_interval(struct comedi_device *dev,
94 unsigned int val)
95{
96 struct ni6527_private *devpriv = dev->private;
97
98 if (val != devpriv->filter_interval) {
99 writeb(val & 0xff, dev->mmio + NI6527_FILT_INTERVAL_REG(0));
100 writeb((val >> 8) & 0xff,
101 dev->mmio + NI6527_FILT_INTERVAL_REG(1));
102 writeb((val >> 16) & 0x0f,
103 dev->mmio + NI6527_FILT_INTERVAL_REG(2));
104
105 writeb(NI6527_CLR_INTERVAL, dev->mmio + NI6527_CLR_REG);
106
107 devpriv->filter_interval = val;
108 }
109}
110
111static void ni6527_set_filter_enable(struct comedi_device *dev,
112 unsigned int val)
113{
114 writeb(val & 0xff, dev->mmio + NI6527_FILT_ENA_REG(0));
115 writeb((val >> 8) & 0xff, dev->mmio + NI6527_FILT_ENA_REG(1));
116 writeb((val >> 16) & 0xff, dev->mmio + NI6527_FILT_ENA_REG(2));
117}
118
119static int ni6527_di_insn_config(struct comedi_device *dev,
120 struct comedi_subdevice *s,
121 struct comedi_insn *insn,
122 unsigned int *data)
123{
124 struct ni6527_private *devpriv = dev->private;
125 unsigned int chan = CR_CHAN(insn->chanspec);
126 unsigned int interval;
127
128 switch (data[0]) {
129 case INSN_CONFIG_FILTER:
130
131
132
133
134
135 interval = (data[1] + 100) / 200;
136 data[1] = interval * 200;
137
138 if (interval) {
139 ni6527_set_filter_interval(dev, interval);
140 devpriv->filter_enable |= 1 << chan;
141 } else {
142 devpriv->filter_enable &= ~(1 << chan);
143 }
144 ni6527_set_filter_enable(dev, devpriv->filter_enable);
145 break;
146 default:
147 return -EINVAL;
148 }
149
150 return insn->n;
151}
152
153static int ni6527_di_insn_bits(struct comedi_device *dev,
154 struct comedi_subdevice *s,
155 struct comedi_insn *insn,
156 unsigned int *data)
157{
158 unsigned int val;
159
160 val = readb(dev->mmio + NI6527_DI_REG(0));
161 val |= (readb(dev->mmio + NI6527_DI_REG(1)) << 8);
162 val |= (readb(dev->mmio + NI6527_DI_REG(2)) << 16);
163
164 data[1] = val;
165
166 return insn->n;
167}
168
169static int ni6527_do_insn_bits(struct comedi_device *dev,
170 struct comedi_subdevice *s,
171 struct comedi_insn *insn,
172 unsigned int *data)
173{
174 unsigned int mask;
175
176 mask = comedi_dio_update_state(s, data);
177 if (mask) {
178
179 unsigned int val = s->state ^ 0xffffff;
180
181 if (mask & 0x0000ff)
182 writeb(val & 0xff, dev->mmio + NI6527_DO_REG(0));
183 if (mask & 0x00ff00)
184 writeb((val >> 8) & 0xff,
185 dev->mmio + NI6527_DO_REG(1));
186 if (mask & 0xff0000)
187 writeb((val >> 16) & 0xff,
188 dev->mmio + NI6527_DO_REG(2));
189 }
190
191 data[1] = s->state;
192
193 return insn->n;
194}
195
196static irqreturn_t ni6527_interrupt(int irq, void *d)
197{
198 struct comedi_device *dev = d;
199 struct comedi_subdevice *s = dev->read_subdev;
200 unsigned int status;
201
202 status = readb(dev->mmio + NI6527_STATUS_REG);
203 if (!(status & NI6527_STATUS_IRQ))
204 return IRQ_NONE;
205
206 if (status & NI6527_STATUS_EDGE) {
207 comedi_buf_write_samples(s, &s->state, 1);
208 comedi_handle_events(dev, s);
209 }
210
211 writeb(NI6527_CLR_IRQS, dev->mmio + NI6527_CLR_REG);
212
213 return IRQ_HANDLED;
214}
215
216static int ni6527_intr_cmdtest(struct comedi_device *dev,
217 struct comedi_subdevice *s,
218 struct comedi_cmd *cmd)
219{
220 int err = 0;
221
222
223
224 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
225 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_OTHER);
226 err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
227 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
228 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT);
229
230 if (err)
231 return 1;
232
233
234
235
236
237
238 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
239 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
240 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
241 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
242 cmd->chanlist_len);
243 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
244
245 if (err)
246 return 3;
247
248
249
250
251
252 return 0;
253}
254
255static int ni6527_intr_cmd(struct comedi_device *dev,
256 struct comedi_subdevice *s)
257{
258 writeb(NI6527_CLR_IRQS, dev->mmio + NI6527_CLR_REG);
259 writeb(NI6527_CTRL_ENABLE_IRQS, dev->mmio + NI6527_CTRL_REG);
260
261 return 0;
262}
263
264static int ni6527_intr_cancel(struct comedi_device *dev,
265 struct comedi_subdevice *s)
266{
267 writeb(NI6527_CTRL_DISABLE_IRQS, dev->mmio + NI6527_CTRL_REG);
268
269 return 0;
270}
271
272static int ni6527_intr_insn_bits(struct comedi_device *dev,
273 struct comedi_subdevice *s,
274 struct comedi_insn *insn, unsigned int *data)
275{
276 data[1] = 0;
277 return insn->n;
278}
279
280static void ni6527_set_edge_detection(struct comedi_device *dev,
281 unsigned int mask,
282 unsigned int rising,
283 unsigned int falling)
284{
285 unsigned int i;
286
287 rising &= mask;
288 falling &= mask;
289 for (i = 0; i < 2; i++) {
290 if (mask & 0xff) {
291 if (~mask & 0xff) {
292
293 rising |= readb(dev->mmio +
294 NI6527_RISING_EDGE_REG(i)) &
295 (~mask & 0xff);
296
297 falling |= readb(dev->mmio +
298 NI6527_FALLING_EDGE_REG(i)) &
299 (~mask & 0xff);
300 }
301
302 writeb(rising & 0xff,
303 dev->mmio + NI6527_RISING_EDGE_REG(i));
304
305 writeb(falling & 0xff,
306 dev->mmio + NI6527_FALLING_EDGE_REG(i));
307 }
308 rising >>= 8;
309 falling >>= 8;
310 mask >>= 8;
311 }
312}
313
314static int ni6527_intr_insn_config(struct comedi_device *dev,
315 struct comedi_subdevice *s,
316 struct comedi_insn *insn,
317 unsigned int *data)
318{
319 unsigned int mask = 0xffffffff;
320 unsigned int rising, falling, shift;
321
322 switch (data[0]) {
323 case INSN_CONFIG_CHANGE_NOTIFY:
324
325 if (insn->n != 3)
326 return -EINVAL;
327 rising = data[1];
328 falling = data[2];
329 ni6527_set_edge_detection(dev, mask, rising, falling);
330 break;
331 case INSN_CONFIG_DIGITAL_TRIG:
332
333 if (data[1] != 0)
334 return -EINVAL;
335
336 switch (data[2]) {
337 case COMEDI_DIGITAL_TRIG_DISABLE:
338 rising = 0;
339 falling = 0;
340 break;
341 case COMEDI_DIGITAL_TRIG_ENABLE_EDGES:
342
343 shift = data[3];
344 if (shift >= s->n_chan) {
345 mask = 0;
346 rising = 0;
347 falling = 0;
348 } else {
349 mask <<= shift;
350 rising = data[4] << shift;
351 falling = data[5] << shift;
352 }
353 break;
354 default:
355 return -EINVAL;
356 }
357 ni6527_set_edge_detection(dev, mask, rising, falling);
358 break;
359 default:
360 return -EINVAL;
361 }
362
363 return insn->n;
364}
365
366static void ni6527_reset(struct comedi_device *dev)
367{
368
369 ni6527_set_filter_enable(dev, 0);
370
371
372 ni6527_set_edge_detection(dev, 0xffffffff, 0, 0);
373
374 writeb(NI6527_CLR_IRQS | NI6527_CLR_RESET_FILT,
375 dev->mmio + NI6527_CLR_REG);
376 writeb(NI6527_CTRL_DISABLE_IRQS, dev->mmio + NI6527_CTRL_REG);
377}
378
379static int ni6527_auto_attach(struct comedi_device *dev,
380 unsigned long context)
381{
382 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
383 const struct ni6527_board *board = NULL;
384 struct ni6527_private *devpriv;
385 struct comedi_subdevice *s;
386 int ret;
387
388 if (context < ARRAY_SIZE(ni6527_boards))
389 board = &ni6527_boards[context];
390 if (!board)
391 return -ENODEV;
392 dev->board_ptr = board;
393 dev->board_name = board->name;
394
395 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
396 if (!devpriv)
397 return -ENOMEM;
398
399 ret = comedi_pci_enable(dev);
400 if (ret)
401 return ret;
402
403 dev->mmio = pci_ioremap_bar(pcidev, 1);
404 if (!dev->mmio)
405 return -ENOMEM;
406
407
408 if (readb(dev->mmio + NI6527_ID_REG) != 0x27)
409 return -ENODEV;
410
411 ni6527_reset(dev);
412
413 ret = request_irq(pcidev->irq, ni6527_interrupt, IRQF_SHARED,
414 dev->board_name, dev);
415 if (ret == 0)
416 dev->irq = pcidev->irq;
417
418 ret = comedi_alloc_subdevices(dev, 3);
419 if (ret)
420 return ret;
421
422
423 s = &dev->subdevices[0];
424 s->type = COMEDI_SUBD_DI;
425 s->subdev_flags = SDF_READABLE;
426 s->n_chan = 24;
427 s->maxdata = 1;
428 s->range_table = &range_digital;
429 s->insn_config = ni6527_di_insn_config;
430 s->insn_bits = ni6527_di_insn_bits;
431
432
433 s = &dev->subdevices[1];
434 s->type = COMEDI_SUBD_DO;
435 s->subdev_flags = SDF_WRITABLE;
436 s->n_chan = 24;
437 s->maxdata = 1;
438 s->range_table = &range_digital;
439 s->insn_bits = ni6527_do_insn_bits;
440
441
442 s = &dev->subdevices[2];
443 if (dev->irq) {
444 dev->read_subdev = s;
445 s->type = COMEDI_SUBD_DI;
446 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
447 s->n_chan = 1;
448 s->maxdata = 1;
449 s->range_table = &range_digital;
450 s->insn_config = ni6527_intr_insn_config;
451 s->insn_bits = ni6527_intr_insn_bits;
452 s->len_chanlist = 1;
453 s->do_cmdtest = ni6527_intr_cmdtest;
454 s->do_cmd = ni6527_intr_cmd;
455 s->cancel = ni6527_intr_cancel;
456 } else {
457 s->type = COMEDI_SUBD_UNUSED;
458 }
459
460 return 0;
461}
462
463static void ni6527_detach(struct comedi_device *dev)
464{
465 if (dev->mmio)
466 ni6527_reset(dev);
467 comedi_pci_detach(dev);
468}
469
470static struct comedi_driver ni6527_driver = {
471 .driver_name = "ni_6527",
472 .module = THIS_MODULE,
473 .auto_attach = ni6527_auto_attach,
474 .detach = ni6527_detach,
475};
476
477static int ni6527_pci_probe(struct pci_dev *dev,
478 const struct pci_device_id *id)
479{
480 return comedi_pci_auto_config(dev, &ni6527_driver, id->driver_data);
481}
482
483static const struct pci_device_id ni6527_pci_table[] = {
484 { PCI_VDEVICE(NI, 0x2b10), BOARD_PXI6527 },
485 { PCI_VDEVICE(NI, 0x2b20), BOARD_PCI6527 },
486 { 0 }
487};
488MODULE_DEVICE_TABLE(pci, ni6527_pci_table);
489
490static struct pci_driver ni6527_pci_driver = {
491 .name = "ni_6527",
492 .id_table = ni6527_pci_table,
493 .probe = ni6527_pci_probe,
494 .remove = comedi_pci_auto_unconfig,
495};
496module_comedi_pci_driver(ni6527_driver, ni6527_pci_driver);
497
498MODULE_AUTHOR("Comedi http://www.comedi.org");
499MODULE_DESCRIPTION("Comedi driver for National Instruments PCI-6527");
500MODULE_LICENSE("GPL");
501