staging: comedi: comedi_test: simplify time since last AI scan
[deliverable/linux.git] / drivers / staging / comedi / drivers / s526.c
1 /*
2 * s526.c
3 * Sensoray s526 Comedi driver
4 *
5 * COMEDI - Linux Control and Measurement Device Interface
6 * Copyright (C) 2000 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
19 /*
20 * Driver: s526
21 * Description: Sensoray 526 driver
22 * Devices: [Sensoray] 526 (s526)
23 * Author: Richie
24 * Everett Wang <everett.wang@everteq.com>
25 * Updated: Thu, 14 Sep. 2006
26 * Status: experimental
27 *
28 * Encoder works
29 * Analog input works
30 * Analog output works
31 * PWM output works
32 * Commands are not supported yet.
33 *
34 * Configuration Options:
35 * [0] - I/O port base address
36 */
37
38 #include <linux/module.h>
39 #include "../comedidev.h"
40 #include <asm/byteorder.h>
41
42 /*
43 * Register I/O map
44 */
45 #define S526_TIMER_REG 0x00
46 #define S526_TIMER_LOAD(x) (((x) & 0xff) << 8)
47 #define S526_TIMER_MODE ((x) << 1)
48 #define S526_TIMER_MANUAL S526_TIMER_MODE(0)
49 #define S526_TIMER_AUTO S526_TIMER_MODE(1)
50 #define S526_TIMER_RESTART BIT(0)
51 #define S526_WDOG_REG 0x02
52 #define S526_WDOG_INVERTED BIT(4)
53 #define S526_WDOG_ENA BIT(3)
54 #define S526_WDOG_INTERVAL(x) (((x) & 0x7) << 0)
55 #define S526_AO_CTRL_REG 0x04
56 #define S526_AO_CTRL_RESET BIT(3)
57 #define S526_AO_CTRL_CHAN(x) (((x) & 0x3) << 1)
58 #define S526_AO_CTRL_START BIT(0)
59 #define S526_AI_CTRL_REG 0x06
60 #define S526_AI_CTRL_DELAY BIT(15)
61 #define S526_AI_CTRL_CONV(x) (1 << (5 + ((x) & 0x9)))
62 #define S526_AI_CTRL_READ(x) (((x) & 0xf) << 1)
63 #define S526_AI_CTRL_START BIT(0)
64 #define S526_AO_REG 0x08
65 #define S526_AI_REG 0x08
66 #define S526_DIO_CTRL_REG 0x0a
67 #define S526_DIO_CTRL_DIO3_NEG BIT(15) /* irq on DIO3 neg/pos edge */
68 #define S526_DIO_CTRL_DIO2_NEG BIT(14) /* irq on DIO2 neg/pos edge */
69 #define S526_DIO_CTRL_DIO1_NEG BIT(13) /* irq on DIO1 neg/pos edge */
70 #define S526_DIO_CTRL_DIO0_NEG BIT(12) /* irq on DIO0 neg/pos edge */
71 #define S526_DIO_CTRL_GRP2_OUT BIT(11)
72 #define S526_DIO_CTRL_GRP1_OUT BIT(10)
73 #define S526_DIO_CTRL_GRP2_NEG BIT(8) /* irq on DIO[4-7] neg/pos edge */
74 #define S526_INT_ENA_REG 0x0c
75 #define S526_INT_STATUS_REG 0x0e
76 #define S526_INT_DIO(x) BIT(8 + ((x) & 0x7))
77 #define S526_INT_EEPROM BIT(7) /* status only */
78 #define S526_INT_CNTR(x) BIT(3 + (3 - ((x) & 0x3)))
79 #define S526_INT_AI BIT(2)
80 #define S526_INT_AO BIT(1)
81 #define S526_INT_TIMER BIT(0)
82 #define S526_MISC_REG 0x10
83 #define S526_MISC_LED_OFF BIT(0)
84 #define S526_GPCT_LSB_REG(x) (0x12 + ((x) * 8))
85 #define S526_GPCT_MSB_REG(x) (0x14 + ((x) * 8))
86 #define S526_GPCT_MODE_REG(x) (0x16 + ((x) * 8))
87 #define S526_GPCT_CTRL_REG(x) (0x18 + ((x) * 8))
88 #define S526_EEPROM_DATA_REG 0x32
89 #define S526_EEPROM_CTRL_REG 0x34
90 #define S526_EEPROM_CTRL_ADDR(x) (((x) & 0x3f) << 3)
91 #define S526_EEPROM_CTRL(x) (((x) & 0x3) << 1)
92 #define S526_EEPROM_CTRL_READ S526_EEPROM_CTRL(2)
93 #define S526_EEPROM_CTRL_START BIT(0)
94
95 struct counter_mode_register_t {
96 #if defined(__LITTLE_ENDIAN_BITFIELD)
97 unsigned short coutSource:1;
98 unsigned short coutPolarity:1;
99 unsigned short autoLoadResetRcap:3;
100 unsigned short hwCtEnableSource:2;
101 unsigned short ctEnableCtrl:2;
102 unsigned short clockSource:2;
103 unsigned short countDir:1;
104 unsigned short countDirCtrl:1;
105 unsigned short outputRegLatchCtrl:1;
106 unsigned short preloadRegSel:1;
107 unsigned short reserved:1;
108 #elif defined(__BIG_ENDIAN_BITFIELD)
109 unsigned short reserved:1;
110 unsigned short preloadRegSel:1;
111 unsigned short outputRegLatchCtrl:1;
112 unsigned short countDirCtrl:1;
113 unsigned short countDir:1;
114 unsigned short clockSource:2;
115 unsigned short ctEnableCtrl:2;
116 unsigned short hwCtEnableSource:2;
117 unsigned short autoLoadResetRcap:3;
118 unsigned short coutPolarity:1;
119 unsigned short coutSource:1;
120 #else
121 #error Unknown bit field order
122 #endif
123 };
124
125 union cmReg {
126 struct counter_mode_register_t reg;
127 unsigned short value;
128 };
129
130 struct s526_private {
131 unsigned int gpct_config[4];
132 unsigned short ai_ctrl;
133 };
134
135 static void s526_gpct_write(struct comedi_device *dev,
136 unsigned int chan, unsigned int val)
137 {
138 /* write high word then low word */
139 outw((val >> 16) & 0xffff, dev->iobase + S526_GPCT_MSB_REG(chan));
140 outw(val & 0xffff, dev->iobase + S526_GPCT_LSB_REG(chan));
141 }
142
143 static unsigned int s526_gpct_read(struct comedi_device *dev,
144 unsigned int chan)
145 {
146 unsigned int val;
147
148 /* read the low word then high word */
149 val = inw(dev->iobase + S526_GPCT_LSB_REG(chan)) & 0xffff;
150 val |= (inw(dev->iobase + S526_GPCT_MSB_REG(chan)) & 0xff) << 16;
151
152 return val;
153 }
154
155 static int s526_gpct_rinsn(struct comedi_device *dev,
156 struct comedi_subdevice *s,
157 struct comedi_insn *insn,
158 unsigned int *data)
159 {
160 unsigned int chan = CR_CHAN(insn->chanspec);
161 int i;
162
163 for (i = 0; i < insn->n; i++)
164 data[i] = s526_gpct_read(dev, chan);
165
166 return insn->n;
167 }
168
169 static int s526_gpct_insn_config(struct comedi_device *dev,
170 struct comedi_subdevice *s,
171 struct comedi_insn *insn,
172 unsigned int *data)
173 {
174 struct s526_private *devpriv = dev->private;
175 unsigned int chan = CR_CHAN(insn->chanspec);
176 unsigned int val;
177 union cmReg cmReg;
178
179 /*
180 * Check what type of Counter the user requested
181 * data[0] contains the Application type
182 */
183 switch (data[0]) {
184 case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
185 /*
186 * data[0]: Application Type
187 * data[1]: Counter Mode Register Value
188 * data[2]: Pre-load Register Value
189 * data[3]: Conter Control Register
190 */
191 devpriv->gpct_config[chan] = data[0];
192
193 #if 1
194 /* Set Counter Mode Register */
195 cmReg.value = data[1] & 0xffff;
196 outw(cmReg.value, dev->iobase + S526_GPCT_MODE_REG(chan));
197
198 /* Reset the counter if it is software preload */
199 if (cmReg.reg.autoLoadResetRcap == 0) {
200 /* Reset the counter */
201 outw(0x8000, dev->iobase + S526_GPCT_CTRL_REG(chan));
202 /* Load the counter from PR0
203 * outw(0x4000, dev->iobase + S526_GPCT_CTRL_REG(chan));
204 */
205 }
206 #else
207 /* 0 quadrature, 1 software control */
208 cmReg.reg.countDirCtrl = 0;
209
210 /* data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4 */
211 if (data[1] == GPCT_X2)
212 cmReg.reg.clockSource = 1;
213 else if (data[1] == GPCT_X4)
214 cmReg.reg.clockSource = 2;
215 else
216 cmReg.reg.clockSource = 0;
217
218 /* When to take into account the indexpulse: */
219 /*
220 * if (data[2] == GPCT_IndexPhaseLowLow) {
221 * } else if (data[2] == GPCT_IndexPhaseLowHigh) {
222 * } else if (data[2] == GPCT_IndexPhaseHighLow) {
223 * } else if (data[2] == GPCT_IndexPhaseHighHigh) {
224 * }
225 */
226 /* Take into account the index pulse? */
227 if (data[3] == GPCT_RESET_COUNTER_ON_INDEX)
228 /* Auto load with INDEX^ */
229 cmReg.reg.autoLoadResetRcap = 4;
230
231 /* Set Counter Mode Register */
232 cmReg.value = data[1] & 0xffff;
233 outw(cmReg.value, dev->iobase + S526_GPCT_MODE_REG(chan));
234
235 /* Load the pre-load register */
236 s526_gpct_write(dev, chan, data[2]);
237
238 /* Write the Counter Control Register */
239 if (data[3])
240 outw(data[3] & 0xffff,
241 dev->iobase + S526_GPCT_CTRL_REG(chan));
242
243 /* Reset the counter if it is software preload */
244 if (cmReg.reg.autoLoadResetRcap == 0) {
245 /* Reset the counter */
246 outw(0x8000, dev->iobase + S526_GPCT_CTRL_REG(chan));
247 /* Load the counter from PR0 */
248 outw(0x4000, dev->iobase + S526_GPCT_CTRL_REG(chan));
249 }
250 #endif
251 break;
252
253 case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
254 /*
255 * data[0]: Application Type
256 * data[1]: Counter Mode Register Value
257 * data[2]: Pre-load Register 0 Value
258 * data[3]: Pre-load Register 1 Value
259 * data[4]: Conter Control Register
260 */
261 devpriv->gpct_config[chan] = data[0];
262
263 /* Set Counter Mode Register */
264 cmReg.value = data[1] & 0xffff;
265 cmReg.reg.preloadRegSel = 0; /* PR0 */
266 outw(cmReg.value, dev->iobase + S526_GPCT_MODE_REG(chan));
267
268 /* Load the pre-load register 0 */
269 s526_gpct_write(dev, chan, data[2]);
270
271 /* Set Counter Mode Register */
272 cmReg.value = data[1] & 0xffff;
273 cmReg.reg.preloadRegSel = 1; /* PR1 */
274 outw(cmReg.value, dev->iobase + S526_GPCT_MODE_REG(chan));
275
276 /* Load the pre-load register 1 */
277 s526_gpct_write(dev, chan, data[3]);
278
279 /* Write the Counter Control Register */
280 if (data[4]) {
281 val = data[4] & 0xffff;
282 outw(val, dev->iobase + S526_GPCT_CTRL_REG(chan));
283 }
284 break;
285
286 case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
287 /*
288 * data[0]: Application Type
289 * data[1]: Counter Mode Register Value
290 * data[2]: Pre-load Register 0 Value
291 * data[3]: Pre-load Register 1 Value
292 * data[4]: Conter Control Register
293 */
294 devpriv->gpct_config[chan] = data[0];
295
296 /* Set Counter Mode Register */
297 cmReg.value = data[1] & 0xffff;
298 cmReg.reg.preloadRegSel = 0; /* PR0 */
299 outw(cmReg.value, dev->iobase + S526_GPCT_MODE_REG(chan));
300
301 /* Load the pre-load register 0 */
302 s526_gpct_write(dev, chan, data[2]);
303
304 /* Set Counter Mode Register */
305 cmReg.value = data[1] & 0xffff;
306 cmReg.reg.preloadRegSel = 1; /* PR1 */
307 outw(cmReg.value, dev->iobase + S526_GPCT_MODE_REG(chan));
308
309 /* Load the pre-load register 1 */
310 s526_gpct_write(dev, chan, data[3]);
311
312 /* Write the Counter Control Register */
313 if (data[4]) {
314 val = data[4] & 0xffff;
315 outw(val, dev->iobase + S526_GPCT_CTRL_REG(chan));
316 }
317 break;
318
319 default:
320 return -EINVAL;
321 }
322
323 return insn->n;
324 }
325
326 static int s526_gpct_winsn(struct comedi_device *dev,
327 struct comedi_subdevice *s,
328 struct comedi_insn *insn,
329 unsigned int *data)
330 {
331 struct s526_private *devpriv = dev->private;
332 unsigned int chan = CR_CHAN(insn->chanspec);
333
334 inw(dev->iobase + S526_GPCT_MODE_REG(chan)); /* Is this required? */
335
336 /* Check what Application of Counter this channel is configured for */
337 switch (devpriv->gpct_config[chan]) {
338 case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
339 /*
340 * data[0] contains the PULSE_WIDTH
341 * data[1] contains the PULSE_PERIOD
342 * @pre PULSE_PERIOD > PULSE_WIDTH > 0
343 * The above periods must be expressed as a multiple of the
344 * pulse frequency on the selected source
345 */
346 if ((data[1] <= data[0]) || !data[0])
347 return -EINVAL;
348
349 /* Fall thru to write the PULSE_WIDTH */
350
351 case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
352 case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
353 s526_gpct_write(dev, chan, data[0]);
354 break;
355
356 default:
357 return -EINVAL;
358 }
359
360 return insn->n;
361 }
362
363 static int s526_eoc(struct comedi_device *dev,
364 struct comedi_subdevice *s,
365 struct comedi_insn *insn,
366 unsigned long context)
367 {
368 unsigned int status;
369
370 status = inw(dev->iobase + S526_INT_STATUS_REG);
371 if (status & context) {
372 /* we got our eoc event, clear it */
373 outw(context, dev->iobase + S526_INT_STATUS_REG);
374 return 0;
375 }
376 return -EBUSY;
377 }
378
379 static int s526_ai_insn_read(struct comedi_device *dev,
380 struct comedi_subdevice *s,
381 struct comedi_insn *insn,
382 unsigned int *data)
383 {
384 struct s526_private *devpriv = dev->private;
385 unsigned int chan = CR_CHAN(insn->chanspec);
386 unsigned int ctrl;
387 unsigned int val;
388 int ret;
389 int i;
390
391 ctrl = S526_AI_CTRL_CONV(chan) | S526_AI_CTRL_READ(chan) |
392 S526_AI_CTRL_START;
393 if (ctrl != devpriv->ai_ctrl) {
394 /*
395 * The multiplexor needs to change, enable the 15us
396 * delay for the first sample.
397 */
398 devpriv->ai_ctrl = ctrl;
399 ctrl |= S526_AI_CTRL_DELAY;
400 }
401
402 for (i = 0; i < insn->n; i++) {
403 /* trigger conversion */
404 outw(ctrl, dev->iobase + S526_AI_CTRL_REG);
405 ctrl &= ~S526_AI_CTRL_DELAY;
406
407 /* wait for conversion to end */
408 ret = comedi_timeout(dev, s, insn, s526_eoc, S526_INT_AI);
409 if (ret)
410 return ret;
411
412 val = inw(dev->iobase + S526_AI_REG);
413 data[i] = comedi_offset_munge(s, val);
414 }
415
416 return insn->n;
417 }
418
419 static int s526_ao_insn_write(struct comedi_device *dev,
420 struct comedi_subdevice *s,
421 struct comedi_insn *insn,
422 unsigned int *data)
423 {
424 unsigned int chan = CR_CHAN(insn->chanspec);
425 unsigned int ctrl = S526_AO_CTRL_CHAN(chan);
426 unsigned int val = s->readback[chan];
427 int ret;
428 int i;
429
430 outw(ctrl, dev->iobase + S526_AO_CTRL_REG);
431 ctrl |= S526_AO_CTRL_START;
432
433 for (i = 0; i < insn->n; i++) {
434 val = data[i];
435 outw(val, dev->iobase + S526_AO_REG);
436 outw(ctrl, dev->iobase + S526_AO_CTRL_REG);
437
438 /* wait for conversion to end */
439 ret = comedi_timeout(dev, s, insn, s526_eoc, S526_INT_AO);
440 if (ret)
441 return ret;
442 }
443 s->readback[chan] = val;
444
445 return insn->n;
446 }
447
448 static int s526_dio_insn_bits(struct comedi_device *dev,
449 struct comedi_subdevice *s,
450 struct comedi_insn *insn,
451 unsigned int *data)
452 {
453 if (comedi_dio_update_state(s, data))
454 outw(s->state, dev->iobase + S526_DIO_CTRL_REG);
455
456 data[1] = inw(dev->iobase + S526_DIO_CTRL_REG) & 0xff;
457
458 return insn->n;
459 }
460
461 static int s526_dio_insn_config(struct comedi_device *dev,
462 struct comedi_subdevice *s,
463 struct comedi_insn *insn,
464 unsigned int *data)
465 {
466 unsigned int chan = CR_CHAN(insn->chanspec);
467 unsigned int mask;
468 int ret;
469
470 /*
471 * Digital I/O can be configured as inputs or outputs in
472 * groups of 4; DIO group 1 (DIO0-3) and DIO group 2 (DIO4-7).
473 */
474 if (chan < 4)
475 mask = 0x0f;
476 else
477 mask = 0xf0;
478
479 ret = comedi_dio_insn_config(dev, s, insn, data, mask);
480 if (ret)
481 return ret;
482
483 if (s->io_bits & 0x0f)
484 s->state |= S526_DIO_CTRL_GRP1_OUT;
485 else
486 s->state &= ~S526_DIO_CTRL_GRP1_OUT;
487 if (s->io_bits & 0xf0)
488 s->state |= S526_DIO_CTRL_GRP2_OUT;
489 else
490 s->state &= ~S526_DIO_CTRL_GRP2_OUT;
491
492 outw(s->state, dev->iobase + S526_DIO_CTRL_REG);
493
494 return insn->n;
495 }
496
497 static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it)
498 {
499 struct s526_private *devpriv;
500 struct comedi_subdevice *s;
501 int ret;
502
503 ret = comedi_request_region(dev, it->options[0], 0x40);
504 if (ret)
505 return ret;
506
507 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
508 if (!devpriv)
509 return -ENOMEM;
510
511 ret = comedi_alloc_subdevices(dev, 4);
512 if (ret)
513 return ret;
514
515 /* General-Purpose Counter/Timer (GPCT) */
516 s = &dev->subdevices[0];
517 s->type = COMEDI_SUBD_COUNTER;
518 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
519 s->n_chan = 4;
520 s->maxdata = 0x00ffffff;
521 s->insn_read = s526_gpct_rinsn;
522 s->insn_config = s526_gpct_insn_config;
523 s->insn_write = s526_gpct_winsn;
524
525 /*
526 * Analog Input subdevice
527 * channels 0 to 7 are the regular differential inputs
528 * channel 8 is "reference 0" (+10V)
529 * channel 9 is "reference 1" (0V)
530 */
531 s = &dev->subdevices[1];
532 s->type = COMEDI_SUBD_AI;
533 s->subdev_flags = SDF_READABLE | SDF_DIFF;
534 s->n_chan = 10;
535 s->maxdata = 0xffff;
536 s->range_table = &range_bipolar10;
537 s->len_chanlist = 16;
538 s->insn_read = s526_ai_insn_read;
539
540 /* Analog Output subdevice */
541 s = &dev->subdevices[2];
542 s->type = COMEDI_SUBD_AO;
543 s->subdev_flags = SDF_WRITABLE;
544 s->n_chan = 4;
545 s->maxdata = 0xffff;
546 s->range_table = &range_bipolar10;
547 s->insn_write = s526_ao_insn_write;
548
549 ret = comedi_alloc_subdev_readback(s);
550 if (ret)
551 return ret;
552
553 /* Digital I/O subdevice */
554 s = &dev->subdevices[3];
555 s->type = COMEDI_SUBD_DIO;
556 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
557 s->n_chan = 8;
558 s->maxdata = 1;
559 s->range_table = &range_digital;
560 s->insn_bits = s526_dio_insn_bits;
561 s->insn_config = s526_dio_insn_config;
562
563 return 0;
564 }
565
566 static struct comedi_driver s526_driver = {
567 .driver_name = "s526",
568 .module = THIS_MODULE,
569 .attach = s526_attach,
570 .detach = comedi_legacy_detach,
571 };
572 module_comedi_driver(s526_driver);
573
574 MODULE_AUTHOR("Comedi http://www.comedi.org");
575 MODULE_DESCRIPTION("Comedi low-level driver");
576 MODULE_LICENSE("GPL");
This page took 0.061947 seconds and 5 git commands to generate.