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
31
32
33
34
35
36
37
38
39
40
41
42
43
44#include "../comedidev.h"
45#include <linux/ioport.h>
46#include <asm/byteorder.h>
47
48#define S526_SIZE 64
49
50#define S526_START_AI_CONV 0
51#define S526_AI_READ 0
52
53
54#define S526_IOSIZE 0x40
55#define S526_NUM_PORTS 27
56
57
58#define REG_TCR 0x00
59#define REG_WDC 0x02
60#define REG_DAC 0x04
61#define REG_ADC 0x06
62#define REG_ADD 0x08
63#define REG_DIO 0x0A
64#define REG_IER 0x0C
65#define REG_ISR 0x0E
66#define REG_MSC 0x10
67#define REG_C0L 0x12
68#define REG_C0H 0x14
69#define REG_C0M 0x16
70#define REG_C0C 0x18
71#define REG_C1L 0x1A
72#define REG_C1H 0x1C
73#define REG_C1M 0x1E
74#define REG_C1C 0x20
75#define REG_C2L 0x22
76#define REG_C2H 0x24
77#define REG_C2M 0x26
78#define REG_C2C 0x28
79#define REG_C3L 0x2A
80#define REG_C3H 0x2C
81#define REG_C3M 0x2E
82#define REG_C3C 0x30
83#define REG_EED 0x32
84#define REG_EEC 0x34
85
86struct counter_mode_register_t {
87#if defined(__LITTLE_ENDIAN_BITFIELD)
88 unsigned short coutSource:1;
89 unsigned short coutPolarity:1;
90 unsigned short autoLoadResetRcap:3;
91 unsigned short hwCtEnableSource:2;
92 unsigned short ctEnableCtrl:2;
93 unsigned short clockSource:2;
94 unsigned short countDir:1;
95 unsigned short countDirCtrl:1;
96 unsigned short outputRegLatchCtrl:1;
97 unsigned short preloadRegSel:1;
98 unsigned short reserved:1;
99 #elif defined(__BIG_ENDIAN_BITFIELD)
100 unsigned short reserved:1;
101 unsigned short preloadRegSel:1;
102 unsigned short outputRegLatchCtrl:1;
103 unsigned short countDirCtrl:1;
104 unsigned short countDir:1;
105 unsigned short clockSource:2;
106 unsigned short ctEnableCtrl:2;
107 unsigned short hwCtEnableSource:2;
108 unsigned short autoLoadResetRcap:3;
109 unsigned short coutPolarity:1;
110 unsigned short coutSource:1;
111#else
112#error Unknown bit field order
113#endif
114};
115
116union cmReg {
117 struct counter_mode_register_t reg;
118 unsigned short value;
119};
120
121struct s526_private {
122 unsigned int ao_readback[2];
123 unsigned int gpct_config[4];
124 unsigned short ai_config;
125};
126
127static int s526_gpct_rinsn(struct comedi_device *dev,
128 struct comedi_subdevice *s,
129 struct comedi_insn *insn,
130 unsigned int *data)
131{
132 unsigned int chan = CR_CHAN(insn->chanspec);
133 unsigned long chan_iobase = dev->iobase + chan * 8;
134 unsigned int lo;
135 unsigned int hi;
136 int i;
137
138 for (i = 0; i < insn->n; i++) {
139
140 lo = inw(chan_iobase + REG_C0L) & 0xffff;
141 hi = inw(chan_iobase + REG_C0H) & 0xff;
142
143 data[i] = (hi << 16) | lo;
144 }
145
146 return insn->n;
147}
148
149static int s526_gpct_insn_config(struct comedi_device *dev,
150 struct comedi_subdevice *s,
151 struct comedi_insn *insn,
152 unsigned int *data)
153{
154 struct s526_private *devpriv = dev->private;
155 unsigned int chan = CR_CHAN(insn->chanspec);
156 unsigned long chan_iobase = dev->iobase + chan * 8;
157 unsigned int val;
158 union cmReg cmReg;
159
160
161
162 switch (data[0]) {
163 case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
164
165
166
167
168
169
170 devpriv->gpct_config[chan] = data[0];
171
172#if 0
173
174
175 cmReg.reg.coutSource = 0;
176 cmReg.reg.coutPolarity = 1;
177 cmReg.reg.autoLoadResetRcap = 0;
178 cmReg.reg.hwCtEnableSource = 3;
179 cmReg.reg.ctEnableCtrl = 2;
180 cmReg.reg.clockSource = 2;
181 cmReg.reg.countDir = 1;
182 cmReg.reg.countDirCtrl = 1;
183 cmReg.reg.outputRegLatchCtrl = 0;
184 cmReg.reg.preloadRegSel = 0;
185 cmReg.reg.reserved = 0;
186
187 outw(cmReg.value, chan_iobase + REG_C0M);
188
189 outw(0x0001, chan_iobase + REG_C0H);
190 outw(0x3C68, chan_iobase + REG_C0L);
191
192
193 outw(0x8000, chan_iobase + REG_C0C);
194
195 outw(0x4000, chan_iobase + REG_C0C);
196
197
198 outw(0x0008, chan_iobase + REG_C0C);
199
200#endif
201
202#if 1
203
204 cmReg.value = data[1] & 0xffff;
205 outw(cmReg.value, chan_iobase + REG_C0M);
206
207
208 if (cmReg.reg.autoLoadResetRcap == 0) {
209
210 outw(0x8000, chan_iobase + REG_C0C);
211
212
213
214 }
215#else
216
217 cmReg.reg.countDirCtrl = 0;
218
219
220 if (data[1] == GPCT_X2)
221 cmReg.reg.clockSource = 1;
222 else if (data[1] == GPCT_X4)
223 cmReg.reg.clockSource = 2;
224 else
225 cmReg.reg.clockSource = 0;
226
227
228
229
230
231
232
233
234 if (data[3] == GPCT_RESET_COUNTER_ON_INDEX)
235
236 cmReg.reg.autoLoadResetRcap = 4;
237
238
239 cmReg.value = data[1] & 0xffff;
240 outw(cmReg.value, chan_iobase + REG_C0M);
241
242
243 val = (data[2] >> 16) & 0xffff;
244 outw(val, chan_iobase + REG_C0H);
245
246
247 val = data[2] & 0xffff;
248 outw(val, chan_iobase + REG_C0L);
249
250
251 if (data[3]) {
252 val = data[3] & 0xffff;
253 outw(val, chan_iobase + REG_C0C);
254 }
255
256 if (cmReg.reg.autoLoadResetRcap == 0) {
257
258 outw(0x8000, chan_iobase + REG_C0C);
259
260 outw(0x4000, chan_iobase + REG_C0C);
261 }
262#endif
263 break;
264
265 case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
266
267
268
269
270
271
272
273 devpriv->gpct_config[chan] = data[0];
274
275
276 cmReg.value = data[1] & 0xffff;
277 cmReg.reg.preloadRegSel = 0;
278 outw(cmReg.value, chan_iobase + REG_C0M);
279
280
281 val = (data[2] >> 16) & 0xffff;
282 outw(val, chan_iobase + REG_C0H);
283
284
285 val = data[2] & 0xffff;
286 outw(val, chan_iobase + REG_C0L);
287
288
289 cmReg.value = data[1] & 0xffff;
290 cmReg.reg.preloadRegSel = 1;
291 outw(cmReg.value, chan_iobase + REG_C0M);
292
293
294 val = (data[3] >> 16) & 0xffff;
295 outw(val, chan_iobase + REG_C0H);
296
297
298 val = data[3] & 0xffff;
299 outw(val, chan_iobase + REG_C0L);
300
301
302 if (data[4]) {
303 val = data[4] & 0xffff;
304 outw(val, chan_iobase + REG_C0C);
305 }
306 break;
307
308 case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
309
310
311
312
313
314
315
316 devpriv->gpct_config[chan] = data[0];
317
318
319 cmReg.value = data[1] & 0xffff;
320 cmReg.reg.preloadRegSel = 0;
321 outw(cmReg.value, chan_iobase + REG_C0M);
322
323
324 val = (data[2] >> 16) & 0xffff;
325 outw(val, chan_iobase + REG_C0H);
326
327
328 val = data[2] & 0xffff;
329 outw(val, chan_iobase + REG_C0L);
330
331
332 cmReg.value = data[1] & 0xffff;
333 cmReg.reg.preloadRegSel = 1;
334 outw(cmReg.value, chan_iobase + REG_C0M);
335
336
337 val = (data[3] >> 16) & 0xffff;
338 outw(val, chan_iobase + REG_C0H);
339
340
341 val = data[3] & 0xffff;
342 outw(val, chan_iobase + REG_C0L);
343
344
345 if (data[4]) {
346 val = data[4] & 0xffff;
347 outw(val, chan_iobase + REG_C0C);
348 }
349 break;
350
351 default:
352 return -EINVAL;
353 break;
354 }
355
356 return insn->n;
357}
358
359static int s526_gpct_winsn(struct comedi_device *dev,
360 struct comedi_subdevice *s,
361 struct comedi_insn *insn,
362 unsigned int *data)
363{
364 struct s526_private *devpriv = dev->private;
365 unsigned int chan = CR_CHAN(insn->chanspec);
366 unsigned long chan_iobase = dev->iobase + chan * 8;
367
368 inw(chan_iobase + REG_C0M);
369
370
371 switch (devpriv->gpct_config[chan]) {
372 case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
373
374
375
376
377
378
379 if ((data[1] <= data[0]) || !data[0])
380 return -EINVAL;
381
382
383
384 case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
385 case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
386 outw((data[0] >> 16) & 0xffff, chan_iobase + REG_C0H);
387 outw(data[0] & 0xffff, chan_iobase + REG_C0L);
388 break;
389
390 default:
391 return -EINVAL;
392 }
393
394 return insn->n;
395}
396
397#define ISR_ADC_DONE 0x4
398static int s526_ai_insn_config(struct comedi_device *dev,
399 struct comedi_subdevice *s,
400 struct comedi_insn *insn, unsigned int *data)
401{
402 struct s526_private *devpriv = dev->private;
403 int result = -EINVAL;
404
405 if (insn->n < 1)
406 return result;
407
408 result = insn->n;
409
410
411
412
413
414
415
416
417
418 outw(ISR_ADC_DONE, dev->iobase + REG_IER);
419 devpriv->ai_config = (data[0] & 0x3ff) << 5;
420 if (data[1] > 0)
421 devpriv->ai_config |= 0x8000;
422
423 devpriv->ai_config |= 0x0001;
424
425 return result;
426}
427
428static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
429 struct comedi_insn *insn, unsigned int *data)
430{
431 struct s526_private *devpriv = dev->private;
432 unsigned int chan = CR_CHAN(insn->chanspec);
433 int n, i;
434 unsigned short value;
435 unsigned int d;
436 unsigned int status;
437
438
439
440 value = (devpriv->ai_config & 0x8000) |
441 ((1 << 5) << chan) | (chan << 1) | 0x0001;
442
443
444 for (n = 0; n < insn->n; n++) {
445
446 outw(value, dev->iobase + REG_ADC);
447
448#define TIMEOUT 100
449
450 for (i = 0; i < TIMEOUT; i++) {
451 status = inw(dev->iobase + REG_ISR);
452 if (status & ISR_ADC_DONE) {
453 outw(ISR_ADC_DONE, dev->iobase + REG_ISR);
454 break;
455 }
456 }
457 if (i == TIMEOUT)
458 return -ETIMEDOUT;
459
460
461 d = inw(dev->iobase + REG_ADD);
462
463
464 data[n] = d ^ 0x8000;
465 }
466
467
468 return n;
469}
470
471static int s526_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
472 struct comedi_insn *insn, unsigned int *data)
473{
474 struct s526_private *devpriv = dev->private;
475 unsigned int chan = CR_CHAN(insn->chanspec);
476 unsigned short val;
477 int i;
478
479 val = chan << 1;
480 outw(val, dev->iobase + REG_DAC);
481
482 for (i = 0; i < insn->n; i++) {
483 outw(data[i], dev->iobase + REG_ADD);
484 devpriv->ao_readback[chan] = data[i];
485
486 outw(val + 1, dev->iobase + REG_DAC);
487 }
488
489 return i;
490}
491
492static int s526_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
493 struct comedi_insn *insn, unsigned int *data)
494{
495 struct s526_private *devpriv = dev->private;
496 unsigned int chan = CR_CHAN(insn->chanspec);
497 int i;
498
499 for (i = 0; i < insn->n; i++)
500 data[i] = devpriv->ao_readback[chan];
501
502 return i;
503}
504
505static int s526_dio_insn_bits(struct comedi_device *dev,
506 struct comedi_subdevice *s,
507 struct comedi_insn *insn, unsigned int *data)
508{
509 if (data[0]) {
510 s->state &= ~data[0];
511 s->state |= data[0] & data[1];
512
513 outw(s->state, dev->iobase + REG_DIO);
514 }
515
516 data[1] = inw(dev->iobase + REG_DIO) & 0xff;
517
518 return insn->n;
519}
520
521static int s526_dio_insn_config(struct comedi_device *dev,
522 struct comedi_subdevice *s,
523 struct comedi_insn *insn, unsigned int *data)
524{
525 unsigned int chan = CR_CHAN(insn->chanspec);
526 int group, mask;
527
528 group = chan >> 2;
529 mask = 0xF << (group << 2);
530 switch (data[0]) {
531 case INSN_CONFIG_DIO_OUTPUT:
532
533 s->state |= 1 << (group + 10);
534 s->io_bits |= mask;
535 break;
536 case INSN_CONFIG_DIO_INPUT:
537 s->state &= ~(1 << (group + 10));
538 s->io_bits &= ~mask;
539 break;
540 case INSN_CONFIG_DIO_QUERY:
541 data[1] = (s->io_bits & mask) ? COMEDI_OUTPUT : COMEDI_INPUT;
542 return insn->n;
543 default:
544 return -EINVAL;
545 }
546 outw(s->state, dev->iobase + REG_DIO);
547
548 return 1;
549}
550
551static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it)
552{
553 struct s526_private *devpriv;
554 struct comedi_subdevice *s;
555 int ret;
556
557 ret = comedi_request_region(dev, it->options[0], S526_IOSIZE);
558 if (ret)
559 return ret;
560
561 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
562 if (!devpriv)
563 return -ENOMEM;
564 dev->private = devpriv;
565
566 ret = comedi_alloc_subdevices(dev, 4);
567 if (ret)
568 return ret;
569
570 s = &dev->subdevices[0];
571
572 s->type = COMEDI_SUBD_COUNTER;
573 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
574 s->n_chan = 4;
575 s->maxdata = 0x00ffffff;
576 s->insn_read = s526_gpct_rinsn;
577 s->insn_config = s526_gpct_insn_config;
578 s->insn_write = s526_gpct_winsn;
579
580 s = &dev->subdevices[1];
581
582 s->type = COMEDI_SUBD_AI;
583 s->subdev_flags = SDF_READABLE | SDF_DIFF;
584
585
586 s->n_chan = 10;
587 s->maxdata = 0xffff;
588 s->range_table = &range_bipolar10;
589 s->len_chanlist = 16;
590 s->insn_read = s526_ai_rinsn;
591 s->insn_config = s526_ai_insn_config;
592
593 s = &dev->subdevices[2];
594
595 s->type = COMEDI_SUBD_AO;
596 s->subdev_flags = SDF_WRITABLE;
597 s->n_chan = 4;
598 s->maxdata = 0xffff;
599 s->range_table = &range_bipolar10;
600 s->insn_write = s526_ao_winsn;
601 s->insn_read = s526_ao_rinsn;
602
603 s = &dev->subdevices[3];
604
605 s->type = COMEDI_SUBD_DIO;
606 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
607 s->n_chan = 8;
608 s->maxdata = 1;
609 s->range_table = &range_digital;
610 s->insn_bits = s526_dio_insn_bits;
611 s->insn_config = s526_dio_insn_config;
612
613 return 1;
614}
615
616static struct comedi_driver s526_driver = {
617 .driver_name = "s526",
618 .module = THIS_MODULE,
619 .attach = s526_attach,
620 .detach = comedi_legacy_detach,
621};
622module_comedi_driver(s526_driver);
623
624MODULE_AUTHOR("Comedi http://www.comedi.org");
625MODULE_DESCRIPTION("Comedi low-level driver");
626MODULE_LICENSE("GPL");
627