1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23#include <linux/module.h>
24#include <linux/delay.h>
25
26#include "../comedi_pci.h"
27
28#include "8255.h"
29#include "comedi_8254.h"
30
31
32
33
34
35
36#define PCI173X_INT_EN_REG 0x08
37#define PCI173X_INT_RF_REG 0x0c
38#define PCI173X_INT_CLR_REG 0x10
39
40
41#define PCI1750_INT_REG 0x20
42
43
44#define PCI1753_INT_REG(x) (0x10 + (x))
45#define PCI1753E_INT_REG(x) (0x30 + (x))
46
47
48#define PCI1754_INT_REG(x) (0x08 + (x) * 2)
49
50
51#define PCI1752_CFC_REG 0x12
52
53
54#define PCI1762_INT_REG 0x06
55
56
57#define PCI_DIO_MAX_DI_SUBDEVS 2
58#define PCI_DIO_MAX_DO_SUBDEVS 2
59#define PCI_DIO_MAX_DIO_SUBDEVG 2
60
61enum pci_dio_boardid {
62 TYPE_PCI1730,
63 TYPE_PCI1733,
64 TYPE_PCI1734,
65 TYPE_PCI1735,
66 TYPE_PCI1736,
67 TYPE_PCI1739,
68 TYPE_PCI1750,
69 TYPE_PCI1751,
70 TYPE_PCI1752,
71 TYPE_PCI1753,
72 TYPE_PCI1753E,
73 TYPE_PCI1754,
74 TYPE_PCI1756,
75 TYPE_PCI1762
76};
77
78struct diosubd_data {
79 int chans;
80 unsigned long addr;
81};
82
83struct dio_boardtype {
84 const char *name;
85 int nsubdevs;
86 struct diosubd_data sdi[PCI_DIO_MAX_DI_SUBDEVS];
87 struct diosubd_data sdo[PCI_DIO_MAX_DO_SUBDEVS];
88 struct diosubd_data sdio[PCI_DIO_MAX_DIO_SUBDEVG];
89 unsigned long id_reg;
90 unsigned long timer_regbase;
91 unsigned int is_16bit:1;
92};
93
94static const struct dio_boardtype boardtypes[] = {
95 [TYPE_PCI1730] = {
96 .name = "pci1730",
97 .nsubdevs = 5,
98 .sdi[0] = { 16, 0x02, },
99 .sdi[1] = { 16, 0x00, },
100 .sdo[0] = { 16, 0x02, },
101 .sdo[1] = { 16, 0x00, },
102 .id_reg = 0x04,
103 },
104 [TYPE_PCI1733] = {
105 .name = "pci1733",
106 .nsubdevs = 2,
107 .sdi[1] = { 32, 0x00, },
108 .id_reg = 0x04,
109 },
110 [TYPE_PCI1734] = {
111 .name = "pci1734",
112 .nsubdevs = 2,
113 .sdo[1] = { 32, 0x00, },
114 .id_reg = 0x04,
115 },
116 [TYPE_PCI1735] = {
117 .name = "pci1735",
118 .nsubdevs = 4,
119 .sdi[0] = { 32, 0x00, },
120 .sdo[0] = { 32, 0x00, },
121 .id_reg = 0x08,
122 .timer_regbase = 0x04,
123 },
124 [TYPE_PCI1736] = {
125 .name = "pci1736",
126 .nsubdevs = 3,
127 .sdi[1] = { 16, 0x00, },
128 .sdo[1] = { 16, 0x00, },
129 .id_reg = 0x04,
130 },
131 [TYPE_PCI1739] = {
132 .name = "pci1739",
133 .nsubdevs = 3,
134 .sdio[0] = { 2, 0x00, },
135 .id_reg = 0x08,
136 },
137 [TYPE_PCI1750] = {
138 .name = "pci1750",
139 .nsubdevs = 2,
140 .sdi[1] = { 16, 0x00, },
141 .sdo[1] = { 16, 0x00, },
142 },
143 [TYPE_PCI1751] = {
144 .name = "pci1751",
145 .nsubdevs = 3,
146 .sdio[0] = { 2, 0x00, },
147 .timer_regbase = 0x18,
148 },
149 [TYPE_PCI1752] = {
150 .name = "pci1752",
151 .nsubdevs = 3,
152 .sdo[0] = { 32, 0x00, },
153 .sdo[1] = { 32, 0x04, },
154 .id_reg = 0x10,
155 .is_16bit = 1,
156 },
157 [TYPE_PCI1753] = {
158 .name = "pci1753",
159 .nsubdevs = 4,
160 .sdio[0] = { 4, 0x00, },
161 },
162 [TYPE_PCI1753E] = {
163 .name = "pci1753e",
164 .nsubdevs = 8,
165 .sdio[0] = { 4, 0x00, },
166 .sdio[1] = { 4, 0x20, },
167 },
168 [TYPE_PCI1754] = {
169 .name = "pci1754",
170 .nsubdevs = 3,
171 .sdi[0] = { 32, 0x00, },
172 .sdi[1] = { 32, 0x04, },
173 .id_reg = 0x10,
174 .is_16bit = 1,
175 },
176 [TYPE_PCI1756] = {
177 .name = "pci1756",
178 .nsubdevs = 3,
179 .sdi[1] = { 32, 0x00, },
180 .sdo[1] = { 32, 0x04, },
181 .id_reg = 0x10,
182 .is_16bit = 1,
183 },
184 [TYPE_PCI1762] = {
185 .name = "pci1762",
186 .nsubdevs = 3,
187 .sdi[1] = { 16, 0x02, },
188 .sdo[1] = { 16, 0x00, },
189 .id_reg = 0x04,
190 .is_16bit = 1,
191 },
192};
193
194static int pci_dio_insn_bits_di_b(struct comedi_device *dev,
195 struct comedi_subdevice *s,
196 struct comedi_insn *insn,
197 unsigned int *data)
198{
199 unsigned long reg = (unsigned long)s->private;
200 unsigned long iobase = dev->iobase + reg;
201
202 data[1] = inb(iobase);
203 if (s->n_chan > 8)
204 data[1] |= (inb(iobase + 1) << 8);
205 if (s->n_chan > 16)
206 data[1] |= (inb(iobase + 2) << 16);
207 if (s->n_chan > 24)
208 data[1] |= (inb(iobase + 3) << 24);
209
210 return insn->n;
211}
212
213static int pci_dio_insn_bits_di_w(struct comedi_device *dev,
214 struct comedi_subdevice *s,
215 struct comedi_insn *insn,
216 unsigned int *data)
217{
218 unsigned long reg = (unsigned long)s->private;
219 unsigned long iobase = dev->iobase + reg;
220
221 data[1] = inw(iobase);
222 if (s->n_chan > 16)
223 data[1] |= (inw(iobase + 2) << 16);
224
225 return insn->n;
226}
227
228static int pci_dio_insn_bits_do_b(struct comedi_device *dev,
229 struct comedi_subdevice *s,
230 struct comedi_insn *insn,
231 unsigned int *data)
232{
233 unsigned long reg = (unsigned long)s->private;
234 unsigned long iobase = dev->iobase + reg;
235
236 if (comedi_dio_update_state(s, data)) {
237 outb(s->state & 0xff, iobase);
238 if (s->n_chan > 8)
239 outb((s->state >> 8) & 0xff, iobase + 1);
240 if (s->n_chan > 16)
241 outb((s->state >> 16) & 0xff, iobase + 2);
242 if (s->n_chan > 24)
243 outb((s->state >> 24) & 0xff, iobase + 3);
244 }
245
246 data[1] = s->state;
247
248 return insn->n;
249}
250
251static int pci_dio_insn_bits_do_w(struct comedi_device *dev,
252 struct comedi_subdevice *s,
253 struct comedi_insn *insn,
254 unsigned int *data)
255{
256 unsigned long reg = (unsigned long)s->private;
257 unsigned long iobase = dev->iobase + reg;
258
259 if (comedi_dio_update_state(s, data)) {
260 outw(s->state & 0xffff, iobase);
261 if (s->n_chan > 16)
262 outw((s->state >> 16) & 0xffff, iobase + 2);
263 }
264
265 data[1] = s->state;
266
267 return insn->n;
268}
269
270static int pci_dio_reset(struct comedi_device *dev, unsigned long cardtype)
271{
272
273 if (cardtype == TYPE_PCI1752 || cardtype == TYPE_PCI1756)
274 outw(0, dev->iobase + PCI1752_CFC_REG);
275
276
277 switch (cardtype) {
278 case TYPE_PCI1730:
279 case TYPE_PCI1733:
280 case TYPE_PCI1736:
281 outb(0, dev->iobase + PCI173X_INT_EN_REG);
282 outb(0x0f, dev->iobase + PCI173X_INT_CLR_REG);
283 outb(0, dev->iobase + PCI173X_INT_RF_REG);
284 break;
285 case TYPE_PCI1739:
286 case TYPE_PCI1750:
287 case TYPE_PCI1751:
288 outb(0x88, dev->iobase + PCI1750_INT_REG);
289 break;
290 case TYPE_PCI1753:
291 case TYPE_PCI1753E:
292 outb(0x88, dev->iobase + PCI1753_INT_REG(0));
293 outb(0x80, dev->iobase + PCI1753_INT_REG(1));
294 outb(0x80, dev->iobase + PCI1753_INT_REG(2));
295 outb(0x80, dev->iobase + PCI1753_INT_REG(3));
296 if (cardtype == TYPE_PCI1753E) {
297 outb(0x88, dev->iobase + PCI1753E_INT_REG(0));
298 outb(0x80, dev->iobase + PCI1753E_INT_REG(1));
299 outb(0x80, dev->iobase + PCI1753E_INT_REG(2));
300 outb(0x80, dev->iobase + PCI1753E_INT_REG(3));
301 }
302 break;
303 case TYPE_PCI1754:
304 case TYPE_PCI1756:
305 outw(0x08, dev->iobase + PCI1754_INT_REG(0));
306 outw(0x08, dev->iobase + PCI1754_INT_REG(1));
307 if (cardtype == TYPE_PCI1754) {
308 outw(0x08, dev->iobase + PCI1754_INT_REG(2));
309 outw(0x08, dev->iobase + PCI1754_INT_REG(3));
310 }
311 break;
312 case TYPE_PCI1762:
313 outw(0x0101, dev->iobase + PCI1762_INT_REG);
314 break;
315 default:
316 break;
317 }
318
319 return 0;
320}
321
322static int pci_dio_auto_attach(struct comedi_device *dev,
323 unsigned long context)
324{
325 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
326 const struct dio_boardtype *board = NULL;
327 const struct diosubd_data *d;
328 struct comedi_subdevice *s;
329 int ret, subdev, i, j;
330
331 if (context < ARRAY_SIZE(boardtypes))
332 board = &boardtypes[context];
333 if (!board)
334 return -ENODEV;
335 dev->board_ptr = board;
336 dev->board_name = board->name;
337
338 ret = comedi_pci_enable(dev);
339 if (ret)
340 return ret;
341 if (context == TYPE_PCI1736)
342 dev->iobase = pci_resource_start(pcidev, 0);
343 else
344 dev->iobase = pci_resource_start(pcidev, 2);
345
346 pci_dio_reset(dev, context);
347
348 ret = comedi_alloc_subdevices(dev, board->nsubdevs);
349 if (ret)
350 return ret;
351
352 subdev = 0;
353 for (i = 0; i < PCI_DIO_MAX_DI_SUBDEVS; i++) {
354 d = &board->sdi[i];
355 if (d->chans) {
356 s = &dev->subdevices[subdev++];
357 s->type = COMEDI_SUBD_DI;
358 s->subdev_flags = SDF_READABLE;
359 s->n_chan = d->chans;
360 s->maxdata = 1;
361 s->range_table = &range_digital;
362 s->insn_bits = board->is_16bit
363 ? pci_dio_insn_bits_di_w
364 : pci_dio_insn_bits_di_b;
365 s->private = (void *)d->addr;
366 }
367 }
368
369 for (i = 0; i < PCI_DIO_MAX_DO_SUBDEVS; i++) {
370 d = &board->sdo[i];
371 if (d->chans) {
372 s = &dev->subdevices[subdev++];
373 s->type = COMEDI_SUBD_DO;
374 s->subdev_flags = SDF_WRITABLE;
375 s->n_chan = d->chans;
376 s->maxdata = 1;
377 s->range_table = &range_digital;
378 s->insn_bits = board->is_16bit
379 ? pci_dio_insn_bits_do_w
380 : pci_dio_insn_bits_do_b;
381 s->private = (void *)d->addr;
382
383
384 if (board->is_16bit) {
385 outw(0, dev->iobase + d->addr);
386 if (s->n_chan > 16)
387 outw(0, dev->iobase + d->addr + 2);
388 } else {
389 outb(0, dev->iobase + d->addr);
390 if (s->n_chan > 8)
391 outb(0, dev->iobase + d->addr + 1);
392 if (s->n_chan > 16)
393 outb(0, dev->iobase + d->addr + 2);
394 if (s->n_chan > 24)
395 outb(0, dev->iobase + d->addr + 3);
396 }
397 }
398 }
399
400 for (i = 0; i < PCI_DIO_MAX_DIO_SUBDEVG; i++) {
401 d = &board->sdio[i];
402 for (j = 0; j < d->chans; j++) {
403 s = &dev->subdevices[subdev++];
404 ret = subdev_8255_init(dev, s, NULL,
405 d->addr + j * I8255_SIZE);
406 if (ret)
407 return ret;
408 }
409 }
410
411 if (board->id_reg) {
412 s = &dev->subdevices[subdev++];
413 s->type = COMEDI_SUBD_DI;
414 s->subdev_flags = SDF_READABLE | SDF_INTERNAL;
415 s->n_chan = 4;
416 s->maxdata = 1;
417 s->range_table = &range_digital;
418 s->insn_bits = board->is_16bit ? pci_dio_insn_bits_di_w
419 : pci_dio_insn_bits_di_b;
420 s->private = (void *)board->id_reg;
421 }
422
423 if (board->timer_regbase) {
424 s = &dev->subdevices[subdev++];
425
426 dev->pacer = comedi_8254_init(dev->iobase +
427 board->timer_regbase,
428 0, I8254_IO8, 0);
429 if (!dev->pacer)
430 return -ENOMEM;
431
432 comedi_8254_subdevice_init(s, dev->pacer);
433 }
434
435 return 0;
436}
437
438static struct comedi_driver adv_pci_dio_driver = {
439 .driver_name = "adv_pci_dio",
440 .module = THIS_MODULE,
441 .auto_attach = pci_dio_auto_attach,
442 .detach = comedi_pci_detach,
443};
444
445static unsigned long pci_dio_override_cardtype(struct pci_dev *pcidev,
446 unsigned long cardtype)
447{
448
449
450
451
452
453 if (cardtype != TYPE_PCI1753)
454 return cardtype;
455 if (pci_enable_device(pcidev) < 0)
456 return cardtype;
457 if (pci_request_region(pcidev, 2, "adv_pci_dio") == 0) {
458
459
460
461
462
463 unsigned long reg = pci_resource_start(pcidev, 2) + 53;
464
465 outb(0x05, reg);
466 if ((inb(reg) & 0x07) == 0x02) {
467 outb(0x02, reg);
468 if ((inb(reg) & 0x07) == 0x05)
469 cardtype = TYPE_PCI1753E;
470 }
471 pci_release_region(pcidev, 2);
472 }
473 pci_disable_device(pcidev);
474 return cardtype;
475}
476
477static int adv_pci_dio_pci_probe(struct pci_dev *dev,
478 const struct pci_device_id *id)
479{
480 unsigned long cardtype;
481
482 cardtype = pci_dio_override_cardtype(dev, id->driver_data);
483 return comedi_pci_auto_config(dev, &adv_pci_dio_driver, cardtype);
484}
485
486static const struct pci_device_id adv_pci_dio_pci_table[] = {
487 { PCI_VDEVICE(ADVANTECH, 0x1730), TYPE_PCI1730 },
488 { PCI_VDEVICE(ADVANTECH, 0x1733), TYPE_PCI1733 },
489 { PCI_VDEVICE(ADVANTECH, 0x1734), TYPE_PCI1734 },
490 { PCI_VDEVICE(ADVANTECH, 0x1735), TYPE_PCI1735 },
491 { PCI_VDEVICE(ADVANTECH, 0x1736), TYPE_PCI1736 },
492 { PCI_VDEVICE(ADVANTECH, 0x1739), TYPE_PCI1739 },
493 { PCI_VDEVICE(ADVANTECH, 0x1750), TYPE_PCI1750 },
494 { PCI_VDEVICE(ADVANTECH, 0x1751), TYPE_PCI1751 },
495 { PCI_VDEVICE(ADVANTECH, 0x1752), TYPE_PCI1752 },
496 { PCI_VDEVICE(ADVANTECH, 0x1753), TYPE_PCI1753 },
497 { PCI_VDEVICE(ADVANTECH, 0x1754), TYPE_PCI1754 },
498 { PCI_VDEVICE(ADVANTECH, 0x1756), TYPE_PCI1756 },
499 { PCI_VDEVICE(ADVANTECH, 0x1762), TYPE_PCI1762 },
500 { 0 }
501};
502MODULE_DEVICE_TABLE(pci, adv_pci_dio_pci_table);
503
504static struct pci_driver adv_pci_dio_pci_driver = {
505 .name = "adv_pci_dio",
506 .id_table = adv_pci_dio_pci_table,
507 .probe = adv_pci_dio_pci_probe,
508 .remove = comedi_pci_auto_unconfig,
509};
510module_comedi_pci_driver(adv_pci_dio_driver, adv_pci_dio_pci_driver);
511
512MODULE_AUTHOR("Comedi http://www.comedi.org");
513MODULE_DESCRIPTION("Comedi driver for Advantech Digital I/O Cards");
514MODULE_LICENSE("GPL");
515