]> bbs.cooldavid.org Git - net-next-2.6.git/blame - drivers/staging/comedi/drivers/s526.c
Staging: comedi: s526: Get rid of global variable 'cmReg'.
[net-next-2.6.git] / drivers / staging / comedi / drivers / s526.c
CommitLineData
0c988d00
EW
1/*
2 comedi/drivers/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 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: s526
25Description: Sensoray 526 driver
26Devices: [Sensoray] 526 (s526)
27Author: Richie
28 Everett Wang <everett.wang@everteq.com>
29Updated: Thu, 14 Sep. 2006
30Status: experimental
31
32Encoder works
33Analog input works
34Analog output works
35PWM output works
36Commands are not supported yet.
37
38Configuration Options:
39
40comedi_config /dev/comedi0 s526 0x2C0,0x3
41
42*/
43
44#include "../comedidev.h"
45#include <linux/ioport.h>
46
47#define S526_SIZE 64
48
49#define S526_START_AI_CONV 0
50#define S526_AI_READ 0
51
52/* Ports */
53#define S526_IOSIZE 0x40
54#define S526_NUM_PORTS 27
55
56/* registers */
57#define REG_TCR 0x00
58#define REG_WDC 0x02
59#define REG_DAC 0x04
60#define REG_ADC 0x06
61#define REG_ADD 0x08
62#define REG_DIO 0x0A
63#define REG_IER 0x0C
64#define REG_ISR 0x0E
65#define REG_MSC 0x10
66#define REG_C0L 0x12
67#define REG_C0H 0x14
68#define REG_C0M 0x16
69#define REG_C0C 0x18
70#define REG_C1L 0x1A
71#define REG_C1H 0x1C
72#define REG_C1M 0x1E
73#define REG_C1C 0x20
74#define REG_C2L 0x22
75#define REG_C2H 0x24
76#define REG_C2M 0x26
77#define REG_C2C 0x28
78#define REG_C3L 0x2A
79#define REG_C3H 0x2C
80#define REG_C3M 0x2E
81#define REG_C3C 0x30
82#define REG_EED 0x32
83#define REG_EEC 0x34
84
85static const int s526_ports[] = {
86 REG_TCR,
87 REG_WDC,
88 REG_DAC,
89 REG_ADC,
90 REG_ADD,
91 REG_DIO,
92 REG_IER,
93 REG_ISR,
94 REG_MSC,
95 REG_C0L,
96 REG_C0H,
97 REG_C0M,
98 REG_C0C,
99 REG_C1L,
100 REG_C1H,
101 REG_C1M,
102 REG_C1C,
103 REG_C2L,
104 REG_C2H,
105 REG_C2M,
106 REG_C2C,
107 REG_C3L,
108 REG_C3H,
109 REG_C3M,
110 REG_C3C,
111 REG_EED,
112 REG_EEC
113};
114
4b1d53f0 115struct counter_mode_register_t {
0c988d00
EW
116 unsigned short coutSource:1;
117 unsigned short coutPolarity:1;
118 unsigned short autoLoadResetRcap:3;
119 unsigned short hwCtEnableSource:2;
120 unsigned short ctEnableCtrl:2;
121 unsigned short clockSource:2;
122 unsigned short countDir:1;
123 unsigned short countDirCtrl:1;
124 unsigned short outputRegLatchCtrl:1;
125 unsigned short preloadRegSel:1;
126 unsigned short reserved:1;
4b1d53f0 127};
0c988d00 128
ca98ee7b 129union cmReg {
4b1d53f0 130 struct counter_mode_register_t reg;
0c988d00 131 unsigned short value;
ca98ee7b 132};
0c988d00
EW
133
134#define MAX_GPCT_CONFIG_DATA 6
135
136/* Different Application Classes for GPCT Subdevices */
137/* The list is not exhaustive and needs discussion! */
dfb0503e 138enum S526_GPCT_APP_CLASS {
0c988d00
EW
139 CountingAndTimeMeasurement,
140 SinglePulseGeneration,
141 PulseTrainGeneration,
142 PositionMeasurement,
143 Miscellaneous
dfb0503e 144};
0c988d00
EW
145
146/* Config struct for different GPCT subdevice Application Classes and
147 their options
148*/
39f76660 149struct s526GPCTConfig {
dfb0503e 150 enum S526_GPCT_APP_CLASS app;
0c988d00 151 int data[MAX_GPCT_CONFIG_DATA];
39f76660 152};
0c988d00
EW
153
154/*
155 * Board descriptions for two imaginary boards. Describing the
156 * boards in this way is optional, and completely driver-dependent.
157 * Some drivers use arrays such as this, other do not.
158 */
c611ad33 159struct s526_board {
0c988d00
EW
160 const char *name;
161 int gpct_chans;
162 int gpct_bits;
163 int ad_chans;
164 int ad_bits;
165 int da_chans;
166 int da_bits;
167 int have_dio;
c611ad33 168};
0c988d00 169
c611ad33 170static const struct s526_board s526_boards[] = {
0c988d00 171 {
0a85b6f0
MT
172 .name = "s526",
173 .gpct_chans = 4,
174 .gpct_bits = 24,
175 .ad_chans = 8,
176 .ad_bits = 16,
177 .da_chans = 4,
178 .da_bits = 16,
179 .have_dio = 1,
180 }
0c988d00
EW
181};
182
183#define ADDR_REG(reg) (dev->iobase + (reg))
184#define ADDR_CHAN_REG(reg, chan) (dev->iobase + (reg) + (chan) * 8)
185
186/*
187 * Useful for shorthand access to the particular board structure
188 */
c611ad33 189#define thisboard ((const struct s526_board *)dev->board_ptr)
0c988d00
EW
190
191/* this structure is for data unique to this hardware driver. If
192 several hardware drivers keep similar information in this structure,
71b5f4f1 193 feel free to suggest moving the variable to the struct comedi_device struct. */
6dc1ece0
BP
194struct s526_private {
195
0c988d00
EW
196 int data;
197
198 /* would be useful for a PCI device */
199 struct pci_dev *pci_dev;
200
201 /* Used for AO readback */
790c5541 202 unsigned int ao_readback[2];
0c988d00 203
39f76660 204 struct s526GPCTConfig s526_gpct_config[4];
0c988d00 205 unsigned short s526_ai_config;
6dc1ece0
BP
206};
207
0c988d00
EW
208/*
209 * most drivers define the following macro to make it easy to
210 * access the private structure.
211 */
6dc1ece0 212#define devpriv ((struct s526_private *)dev->private)
0c988d00
EW
213
214/*
139dfbdf 215 * The struct comedi_driver structure tells the Comedi core module
0c988d00
EW
216 * which functions to call to configure/deconfigure (attach/detach)
217 * the board, and also about the kernel module that contains
218 * the device code.
219 */
da91b269
BP
220static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it);
221static int s526_detach(struct comedi_device *dev);
139dfbdf 222static struct comedi_driver driver_s526 = {
68c3dbff
BP
223 .driver_name = "s526",
224 .module = THIS_MODULE,
225 .attach = s526_attach,
226 .detach = s526_detach,
0c988d00
EW
227/* It is not necessary to implement the following members if you are
228 * writing a driver for a ISA PnP or PCI card */
229 /* Most drivers will support multiple types of boards by
230 * having an array of board structures. These were defined
231 * in s526_boards[] above. Note that the element 'name'
232 * was first in the structure -- Comedi uses this fact to
233 * extract the name of the board without knowing any details
234 * about the structure except for its length.
235 * When a device is attached (by comedi_config), the name
236 * of the device is given to Comedi, and Comedi tries to
237 * match it by going through the list of board names. If
238 * there is a match, the address of the pointer is put
239 * into dev->board_ptr and driver->attach() is called.
240 *
241 * Note that these are not necessary if you can determine
242 * the type of board in software. ISA PnP, PCI, and PCMCIA
243 * devices are such boards.
244 */
68c3dbff
BP
245 .board_name = &s526_boards[0].name,
246 .offset = sizeof(struct s526_board),
8629efa4 247 .num_names = ARRAY_SIZE(s526_boards),
0c988d00
EW
248};
249
0a85b6f0
MT
250static int s526_gpct_rinsn(struct comedi_device *dev,
251 struct comedi_subdevice *s, struct comedi_insn *insn,
252 unsigned int *data);
253static int s526_gpct_insn_config(struct comedi_device *dev,
254 struct comedi_subdevice *s,
255 struct comedi_insn *insn, unsigned int *data);
256static int s526_gpct_winsn(struct comedi_device *dev,
257 struct comedi_subdevice *s, struct comedi_insn *insn,
258 unsigned int *data);
259static int s526_ai_insn_config(struct comedi_device *dev,
260 struct comedi_subdevice *s,
261 struct comedi_insn *insn, unsigned int *data);
da91b269 262static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 263 struct comedi_insn *insn, unsigned int *data);
da91b269 264static int s526_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 265 struct comedi_insn *insn, unsigned int *data);
da91b269 266static int s526_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0
MT
267 struct comedi_insn *insn, unsigned int *data);
268static int s526_dio_insn_bits(struct comedi_device *dev,
269 struct comedi_subdevice *s,
270 struct comedi_insn *insn, unsigned int *data);
271static int s526_dio_insn_config(struct comedi_device *dev,
272 struct comedi_subdevice *s,
273 struct comedi_insn *insn, unsigned int *data);
0c988d00
EW
274
275/*
276 * Attach is called by the Comedi core to configure the driver
277 * for a particular board. If you specified a board_name array
278 * in the driver structure, dev->board_ptr contains that
279 * address.
280 */
da91b269 281static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it)
0c988d00 282{
34c43922 283 struct comedi_subdevice *s;
0c988d00
EW
284 int iobase;
285 int i, n;
232f6502
BP
286/* short value; */
287/* int subdev_channel = 0; */
ca98ee7b 288 union cmReg cmReg;
0c988d00
EW
289
290 printk("comedi%d: s526: ", dev->minor);
291
292 iobase = it->options[0];
293 if (!iobase || !request_region(iobase, S526_IOSIZE, thisboard->name)) {
294 comedi_error(dev, "I/O port conflict");
295 return -EIO;
296 }
297 dev->iobase = iobase;
298
299 printk("iobase=0x%lx\n", dev->iobase);
300
301 /*** make it a little quieter, exw, 8/29/06
302 for (i = 0; i < S526_NUM_PORTS; i++) {
303 printk("0x%02x: 0x%04x\n", ADDR_REG(s526_ports[i]), inw(ADDR_REG(s526_ports[i])));
304 }
305 ***/
306
307/*
308 * Initialize dev->board_name. Note that we can use the "thisboard"
309 * macro now, since we just initialized it in the last line.
310 */
311 dev->board_ptr = &s526_boards[0];
312
313 dev->board_name = thisboard->name;
314
315/*
316 * Allocate the private structure area. alloc_private() is a
317 * convenient macro defined in comedidev.h.
318 */
6dc1ece0 319 if (alloc_private(dev, sizeof(struct s526_private)) < 0)
0c988d00
EW
320 return -ENOMEM;
321
322/*
323 * Allocate the subdevice structures. alloc_subdevice() is a
324 * convenient macro defined in comedidev.h.
325 */
326 dev->n_subdevices = 4;
327 if (alloc_subdevices(dev, dev->n_subdevices) < 0)
328 return -ENOMEM;
329
330 s = dev->subdevices + 0;
331 /* GENERAL-PURPOSE COUNTER/TIME (GPCT) */
332 s->type = COMEDI_SUBD_COUNTER;
333 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
334 /* KG: What does SDF_LSAMPL (see multiq3.c) mean? */
335 s->n_chan = thisboard->gpct_chans;
336 s->maxdata = 0x00ffffff; /* 24 bit counter */
337 s->insn_read = s526_gpct_rinsn;
338 s->insn_config = s526_gpct_insn_config;
339 s->insn_write = s526_gpct_winsn;
340
341 /* Command are not implemented yet, however they are necessary to
342 allocate the necessary memory for the comedi_async struct (used
343 to trigger the GPCT in case of pulsegenerator function */
232f6502
BP
344 /* s->do_cmd = s526_gpct_cmd; */
345 /* s->do_cmdtest = s526_gpct_cmdtest; */
346 /* s->cancel = s526_gpct_cancel; */
0c988d00
EW
347
348 s = dev->subdevices + 1;
232f6502 349 /* dev->read_subdev=s; */
0c988d00
EW
350 /* analog input subdevice */
351 s->type = COMEDI_SUBD_AI;
352 /* we support differential */
353 s->subdev_flags = SDF_READABLE | SDF_DIFF;
354 /* channels 0 to 7 are the regular differential inputs */
355 /* channel 8 is "reference 0" (+10V), channel 9 is "reference 1" (0V) */
356 s->n_chan = 10;
357 s->maxdata = 0xffff;
358 s->range_table = &range_bipolar10;
359 s->len_chanlist = 16; /* This is the maximum chanlist length that
360 the board can handle */
361 s->insn_read = s526_ai_rinsn;
362 s->insn_config = s526_ai_insn_config;
363
364 s = dev->subdevices + 2;
365 /* analog output subdevice */
366 s->type = COMEDI_SUBD_AO;
367 s->subdev_flags = SDF_WRITABLE;
368 s->n_chan = 4;
369 s->maxdata = 0xffff;
370 s->range_table = &range_bipolar10;
371 s->insn_write = s526_ao_winsn;
372 s->insn_read = s526_ao_rinsn;
373
374 s = dev->subdevices + 3;
375 /* digital i/o subdevice */
376 if (thisboard->have_dio) {
377 s->type = COMEDI_SUBD_DIO;
378 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
10f27014 379 s->n_chan = 8;
0c988d00
EW
380 s->maxdata = 1;
381 s->range_table = &range_digital;
382 s->insn_bits = s526_dio_insn_bits;
383 s->insn_config = s526_dio_insn_config;
384 } else {
385 s->type = COMEDI_SUBD_UNUSED;
386 }
387
388 printk("attached\n");
389
390 return 1;
391
392#if 0
232f6502
BP
393 /* Example of Counter Application */
394 /* One-shot (software trigger) */
395 cmReg.reg.coutSource = 0; /* out RCAP */
396 cmReg.reg.coutPolarity = 1; /* Polarity inverted */
397 cmReg.reg.autoLoadResetRcap = 1; /* Auto load 0:disabled, 1:enabled */
398 cmReg.reg.hwCtEnableSource = 3; /* NOT RCAP */
399 cmReg.reg.ctEnableCtrl = 2; /* Hardware */
400 cmReg.reg.clockSource = 2; /* Internal */
401 cmReg.reg.countDir = 1; /* Down */
402 cmReg.reg.countDirCtrl = 1; /* Software */
403 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
404 cmReg.reg.preloadRegSel = 0; /* PR0 */
0c988d00
EW
405 cmReg.reg.reserved = 0;
406
407 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
408
409 outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
410 outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
411
232f6502
BP
412 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset the counter */
413 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Load the counter from PR0 */
0c988d00 414
232f6502 415 outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset RCAP (fires one-shot) */
0c988d00
EW
416
417#else
418
232f6502
BP
419 /* Set Counter Mode Register */
420 cmReg.reg.coutSource = 0; /* out RCAP */
421 cmReg.reg.coutPolarity = 0; /* Polarity inverted */
422 cmReg.reg.autoLoadResetRcap = 0; /* Auto load disabled */
423 cmReg.reg.hwCtEnableSource = 2; /* NOT RCAP */
424 cmReg.reg.ctEnableCtrl = 1; /* 1: Software, >1 : Hardware */
425 cmReg.reg.clockSource = 3; /* x4 */
426 cmReg.reg.countDir = 0; /* up */
427 cmReg.reg.countDirCtrl = 0; /* quadrature */
428 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
429 cmReg.reg.preloadRegSel = 0; /* PR0 */
0c988d00
EW
430 cmReg.reg.reserved = 0;
431
432 n = 0;
433 printk("Mode reg=0x%04x, 0x%04lx\n", cmReg.value, ADDR_CHAN_REG(REG_C0M,
0a85b6f0 434 n));
0c988d00
EW
435 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
436 udelay(1000);
437 printk("Read back mode reg=0x%04x\n", inw(ADDR_CHAN_REG(REG_C0M, n)));
438
232f6502
BP
439 /* Load the pre-laod register high word */
440/* value = (short) (0x55); */
441/* outw(value, ADDR_CHAN_REG(REG_C0H, n)); */
0c988d00 442
232f6502
BP
443 /* Load the pre-laod register low word */
444/* value = (short)(0xaa55); */
445/* outw(value, ADDR_CHAN_REG(REG_C0L, n)); */
0c988d00 446
232f6502
BP
447 /* Write the Counter Control Register */
448/* outw(value, ADDR_CHAN_REG(REG_C0C, 0)); */
0c988d00 449
232f6502 450 /* Reset the counter if it is software preload */
0c988d00 451 if (cmReg.reg.autoLoadResetRcap == 0) {
232f6502
BP
452 outw(0x8000, ADDR_CHAN_REG(REG_C0C, n)); /* Reset the counter */
453 outw(0x4000, ADDR_CHAN_REG(REG_C0C, n)); /* Load the counter from PR0 */
0c988d00
EW
454 }
455
456 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
457 udelay(1000);
458 printk("Read back mode reg=0x%04x\n", inw(ADDR_CHAN_REG(REG_C0M, n)));
459
460#endif
461 printk("Current registres:\n");
462
463 for (i = 0; i < S526_NUM_PORTS; i++) {
464 printk("0x%02lx: 0x%04x\n", ADDR_REG(s526_ports[i]),
0a85b6f0 465 inw(ADDR_REG(s526_ports[i])));
0c988d00
EW
466 }
467 return 1;
468}
469
470/*
471 * _detach is called to deconfigure a device. It should deallocate
472 * resources.
473 * This function is also called when _attach() fails, so it should be
474 * careful not to release resources that were not necessarily
475 * allocated by _attach(). dev->private and dev->subdevices are
476 * deallocated automatically by the core.
477 */
da91b269 478static int s526_detach(struct comedi_device *dev)
0c988d00
EW
479{
480 printk("comedi%d: s526: remove\n", dev->minor);
481
482 if (dev->iobase > 0)
483 release_region(dev->iobase, S526_IOSIZE);
484
485 return 0;
486}
487
0a85b6f0
MT
488static int s526_gpct_rinsn(struct comedi_device *dev,
489 struct comedi_subdevice *s, struct comedi_insn *insn,
490 unsigned int *data)
0c988d00 491{
232f6502 492 int i; /* counts the Data */
0c988d00
EW
493 int counter_channel = CR_CHAN(insn->chanspec);
494 unsigned short datalow;
495 unsigned short datahigh;
496
232f6502 497 /* Check if (n > 0) */
0c988d00
EW
498 if (insn->n <= 0) {
499 printk("s526: INSN_READ: n should be > 0\n");
500 return -EINVAL;
501 }
232f6502 502 /* Read the low word first */
0c988d00
EW
503 for (i = 0; i < insn->n; i++) {
504 datalow = inw(ADDR_CHAN_REG(REG_C0L, counter_channel));
505 datahigh = inw(ADDR_CHAN_REG(REG_C0H, counter_channel));
506 data[i] = (int)(datahigh & 0x00FF);
507 data[i] = (data[i] << 16) | (datalow & 0xFFFF);
232f6502 508/* printk("s526 GPCT[%d]: %x(0x%04x, 0x%04x)\n", counter_channel, data[i], datahigh, datalow); */
0c988d00
EW
509 }
510 return i;
511}
512
0a85b6f0
MT
513static int s526_gpct_insn_config(struct comedi_device *dev,
514 struct comedi_subdevice *s,
515 struct comedi_insn *insn, unsigned int *data)
0c988d00 516{
232f6502 517 int subdev_channel = CR_CHAN(insn->chanspec); /* Unpack chanspec */
0c988d00 518 int i;
790c5541 519 short value;
ca98ee7b 520 union cmReg cmReg;
0c988d00 521
232f6502 522/* printk("s526: GPCT_INSN_CONFIG: Configuring Channel %d\n", subdev_channel); */
0c988d00
EW
523
524 for (i = 0; i < MAX_GPCT_CONFIG_DATA; i++) {
525 devpriv->s526_gpct_config[subdev_channel].data[i] =
0a85b6f0 526 insn->data[i];
232f6502 527/* printk("data[%d]=%x\n", i, insn->data[i]); */
0c988d00
EW
528 }
529
232f6502
BP
530 /* Check what type of Counter the user requested, data[0] contains */
531 /* the Application type */
0c988d00
EW
532 switch (insn->data[0]) {
533 case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
534 /*
535 data[0]: Application Type
536 data[1]: Counter Mode Register Value
537 data[2]: Pre-load Register Value
538 data[3]: Conter Control Register
539 */
540 printk("s526: GPCT_INSN_CONFIG: Configuring Encoder\n");
541 devpriv->s526_gpct_config[subdev_channel].app =
0a85b6f0 542 PositionMeasurement;
0c988d00 543
232f6502 544#if 0
0a85b6f0
MT
545 /* Example of Counter Application */
546 /* One-shot (software trigger) */
547 cmReg.reg.coutSource = 0; /* out RCAP */
548 cmReg.reg.coutPolarity = 1; /* Polarity inverted */
549 cmReg.reg.autoLoadResetRcap = 0; /* Auto load disabled */
550 cmReg.reg.hwCtEnableSource = 3; /* NOT RCAP */
551 cmReg.reg.ctEnableCtrl = 2; /* Hardware */
552 cmReg.reg.clockSource = 2; /* Internal */
553 cmReg.reg.countDir = 1; /* Down */
554 cmReg.reg.countDirCtrl = 1; /* Software */
555 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
556 cmReg.reg.preloadRegSel = 0; /* PR0 */
557 cmReg.reg.reserved = 0;
0c988d00 558
0a85b6f0
MT
559 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
560
561 outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
562 outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
563
564 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset the counter */
565 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Load the counter from PR0 */
0c988d00 566
0a85b6f0 567 outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset RCAP (fires one-shot) */
0c988d00 568
232f6502 569#endif
0c988d00
EW
570
571#if 1
232f6502
BP
572 /* Set Counter Mode Register */
573 cmReg.reg.coutSource = 0; /* out RCAP */
574 cmReg.reg.coutPolarity = 0; /* Polarity inverted */
575 cmReg.reg.autoLoadResetRcap = 0; /* Auto load disabled */
576 cmReg.reg.hwCtEnableSource = 2; /* NOT RCAP */
577 cmReg.reg.ctEnableCtrl = 1; /* 1: Software, >1 : Hardware */
578 cmReg.reg.clockSource = 3; /* x4 */
579 cmReg.reg.countDir = 0; /* up */
580 cmReg.reg.countDirCtrl = 0; /* quadrature */
581 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
582 cmReg.reg.preloadRegSel = 0; /* PR0 */
0c988d00
EW
583 cmReg.reg.reserved = 0;
584
232f6502
BP
585 /* Set Counter Mode Register */
586/* printk("s526: Counter Mode register=%x\n", cmReg.value); */
0c988d00
EW
587 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
588
232f6502 589 /* Reset the counter if it is software preload */
0c988d00 590 if (cmReg.reg.autoLoadResetRcap == 0) {
232f6502
BP
591 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset the counter */
592/* outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); Load the counter from PR0 */
0c988d00
EW
593 }
594#else
232f6502 595 cmReg.reg.countDirCtrl = 0; /* 0 quadrature, 1 software control */
0c988d00 596
232f6502 597 /* data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4 */
0c988d00
EW
598 if (insn->data[1] == GPCT_X2) {
599 cmReg.reg.clockSource = 1;
600 } else if (insn->data[1] == GPCT_X4) {
601 cmReg.reg.clockSource = 2;
602 } else {
603 cmReg.reg.clockSource = 0;
604 }
605
232f6502 606 /* When to take into account the indexpulse: */
0c988d00
EW
607 if (insn->data[2] == GPCT_IndexPhaseLowLow) {
608 } else if (insn->data[2] == GPCT_IndexPhaseLowHigh) {
609 } else if (insn->data[2] == GPCT_IndexPhaseHighLow) {
610 } else if (insn->data[2] == GPCT_IndexPhaseHighHigh) {
611 }
232f6502 612 /* Take into account the index pulse? */
0c988d00 613 if (insn->data[3] == GPCT_RESET_COUNTER_ON_INDEX)
232f6502 614 cmReg.reg.autoLoadResetRcap = 4; /* Auto load with INDEX^ */
0c988d00 615
232f6502 616 /* Set Counter Mode Register */
0a85b6f0 617 cmReg.value = (short)(insn->data[1] & 0xFFFF);
0c988d00
EW
618 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
619
232f6502 620 /* Load the pre-laod register high word */
0a85b6f0 621 value = (short)((insn->data[2] >> 16) & 0xFFFF);
0c988d00
EW
622 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
623
232f6502 624 /* Load the pre-laod register low word */
0a85b6f0 625 value = (short)(insn->data[2] & 0xFFFF);
0c988d00
EW
626 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
627
232f6502 628 /* Write the Counter Control Register */
0c988d00 629 if (insn->data[3] != 0) {
0a85b6f0 630 value = (short)(insn->data[3] & 0xFFFF);
0c988d00
EW
631 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
632 }
232f6502 633 /* Reset the counter if it is software preload */
0c988d00 634 if (cmReg.reg.autoLoadResetRcap == 0) {
232f6502
BP
635 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset the counter */
636 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Load the counter from PR0 */
0c988d00
EW
637 }
638#endif
639 break;
640
641 case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
642 /*
643 data[0]: Application Type
644 data[1]: Counter Mode Register Value
645 data[2]: Pre-load Register 0 Value
646 data[3]: Pre-load Register 1 Value
647 data[4]: Conter Control Register
648 */
649 printk("s526: GPCT_INSN_CONFIG: Configuring SPG\n");
650 devpriv->s526_gpct_config[subdev_channel].app =
0a85b6f0 651 SinglePulseGeneration;
0c988d00 652
232f6502 653 /* Set Counter Mode Register */
0a85b6f0 654 cmReg.value = (short)(insn->data[1] & 0xFFFF);
232f6502 655 cmReg.reg.preloadRegSel = 0; /* PR0 */
0c988d00
EW
656 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
657
232f6502 658 /* Load the pre-laod register 0 high word */
0a85b6f0 659 value = (short)((insn->data[2] >> 16) & 0xFFFF);
0c988d00
EW
660 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
661
232f6502 662 /* Load the pre-laod register 0 low word */
0a85b6f0 663 value = (short)(insn->data[2] & 0xFFFF);
0c988d00
EW
664 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
665
232f6502 666 /* Set Counter Mode Register */
0a85b6f0 667 cmReg.value = (short)(insn->data[1] & 0xFFFF);
232f6502 668 cmReg.reg.preloadRegSel = 1; /* PR1 */
0c988d00
EW
669 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
670
232f6502 671 /* Load the pre-laod register 1 high word */
0a85b6f0 672 value = (short)((insn->data[3] >> 16) & 0xFFFF);
0c988d00
EW
673 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
674
232f6502 675 /* Load the pre-laod register 1 low word */
0a85b6f0 676 value = (short)(insn->data[3] & 0xFFFF);
0c988d00
EW
677 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
678
232f6502 679 /* Write the Counter Control Register */
0c988d00 680 if (insn->data[3] != 0) {
0a85b6f0 681 value = (short)(insn->data[3] & 0xFFFF);
0c988d00
EW
682 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
683 }
684 break;
685
686 case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
687 /*
688 data[0]: Application Type
689 data[1]: Counter Mode Register Value
690 data[2]: Pre-load Register 0 Value
691 data[3]: Pre-load Register 1 Value
692 data[4]: Conter Control Register
693 */
694 printk("s526: GPCT_INSN_CONFIG: Configuring PTG\n");
695 devpriv->s526_gpct_config[subdev_channel].app =
0a85b6f0 696 PulseTrainGeneration;
0c988d00 697
232f6502 698 /* Set Counter Mode Register */
0a85b6f0 699 cmReg.value = (short)(insn->data[1] & 0xFFFF);
232f6502 700 cmReg.reg.preloadRegSel = 0; /* PR0 */
0c988d00
EW
701 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
702
232f6502 703 /* Load the pre-laod register 0 high word */
0a85b6f0 704 value = (short)((insn->data[2] >> 16) & 0xFFFF);
0c988d00
EW
705 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
706
232f6502 707 /* Load the pre-laod register 0 low word */
0a85b6f0 708 value = (short)(insn->data[2] & 0xFFFF);
0c988d00
EW
709 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
710
232f6502 711 /* Set Counter Mode Register */
0a85b6f0 712 cmReg.value = (short)(insn->data[1] & 0xFFFF);
232f6502 713 cmReg.reg.preloadRegSel = 1; /* PR1 */
0c988d00
EW
714 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
715
232f6502 716 /* Load the pre-laod register 1 high word */
0a85b6f0 717 value = (short)((insn->data[3] >> 16) & 0xFFFF);
0c988d00
EW
718 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
719
232f6502 720 /* Load the pre-laod register 1 low word */
0a85b6f0 721 value = (short)(insn->data[3] & 0xFFFF);
0c988d00
EW
722 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
723
232f6502 724 /* Write the Counter Control Register */
0c988d00 725 if (insn->data[3] != 0) {
0a85b6f0 726 value = (short)(insn->data[3] & 0xFFFF);
0c988d00
EW
727 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
728 }
729 break;
730
731 default:
732 printk("s526: unsupported GPCT_insn_config\n");
733 return -EINVAL;
734 break;
735 }
736
737 return insn->n;
738}
739
0a85b6f0
MT
740static int s526_gpct_winsn(struct comedi_device *dev,
741 struct comedi_subdevice *s, struct comedi_insn *insn,
742 unsigned int *data)
0c988d00 743{
232f6502 744 int subdev_channel = CR_CHAN(insn->chanspec); /* Unpack chanspec */
790c5541 745 short value;
ca98ee7b 746 union cmReg cmReg;
0c988d00
EW
747
748 printk("s526: GPCT_INSN_WRITE on channel %d\n", subdev_channel);
749 cmReg.value = inw(ADDR_CHAN_REG(REG_C0M, subdev_channel));
750 printk("s526: Counter Mode Register: %x\n", cmReg.value);
232f6502 751 /* Check what Application of Counter this channel is configured for */
0c988d00
EW
752 switch (devpriv->s526_gpct_config[subdev_channel].app) {
753 case PositionMeasurement:
754 printk("S526: INSN_WRITE: PM\n");
755 outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H,
0a85b6f0 756 subdev_channel));
0c988d00
EW
757 outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
758 break;
759
760 case SinglePulseGeneration:
761 printk("S526: INSN_WRITE: SPG\n");
762 outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H,
0a85b6f0 763 subdev_channel));
0c988d00
EW
764 outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
765 break;
766
767 case PulseTrainGeneration:
768 /* data[0] contains the PULSE_WIDTH
769 data[1] contains the PULSE_PERIOD
770 @pre PULSE_PERIOD > PULSE_WIDTH > 0
771 The above periods must be expressed as a multiple of the
772 pulse frequency on the selected source
773 */
774 printk("S526: INSN_WRITE: PTG\n");
775 if ((insn->data[1] > insn->data[0]) && (insn->data[0] > 0)) {
776 (devpriv->s526_gpct_config[subdev_channel]).data[0] =
0a85b6f0 777 insn->data[0];
0c988d00 778 (devpriv->s526_gpct_config[subdev_channel]).data[1] =
0a85b6f0 779 insn->data[1];
0c988d00
EW
780 } else {
781 printk("%d \t %d\n", insn->data[1], insn->data[2]);
0a85b6f0
MT
782 printk
783 ("s526: INSN_WRITE: PTG: Problem with Pulse params\n");
0c988d00
EW
784 return -EINVAL;
785 }
786
0a85b6f0 787 value = (short)((*data >> 16) & 0xFFFF);
0c988d00 788 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
0a85b6f0 789 value = (short)(*data & 0xFFFF);
0c988d00
EW
790 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
791 break;
232f6502 792 default: /* Impossible */
0a85b6f0
MT
793 printk
794 ("s526: INSN_WRITE: Functionality %d not implemented yet\n",
795 devpriv->s526_gpct_config[subdev_channel].app);
0c988d00
EW
796 return -EINVAL;
797 break;
798 }
232f6502 799 /* return the number of samples written */
0c988d00
EW
800 return insn->n;
801}
802
803#define ISR_ADC_DONE 0x4
0a85b6f0
MT
804static int s526_ai_insn_config(struct comedi_device *dev,
805 struct comedi_subdevice *s,
806 struct comedi_insn *insn, unsigned int *data)
0c988d00
EW
807{
808 int result = -EINVAL;
809
810 if (insn->n < 1)
811 return result;
812
813 result = insn->n;
814
815 /* data[0] : channels was set in relevant bits.
816 data[1] : delay
817 */
818 /* COMMENT: abbotti 2008-07-24: I don't know why you'd want to
819 * enable channels here. The channel should be enabled in the
820 * INSN_READ handler. */
821
232f6502 822 /* Enable ADC interrupt */
0c988d00 823 outw(ISR_ADC_DONE, ADDR_REG(REG_IER));
232f6502 824/* printk("s526: ADC current value: 0x%04x\n", inw(ADDR_REG(REG_ADC))); */
0c988d00
EW
825 devpriv->s526_ai_config = (data[0] & 0x3FF) << 5;
826 if (data[1] > 0)
232f6502 827 devpriv->s526_ai_config |= 0x8000; /* set the delay */
0c988d00 828
232f6502 829 devpriv->s526_ai_config |= 0x0001; /* ADC start bit. */
0c988d00
EW
830
831 return result;
832}
833
834/*
835 * "instructions" read/write data in "one-shot" or "software-triggered"
836 * mode.
837 */
da91b269 838static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 839 struct comedi_insn *insn, unsigned int *data)
0c988d00
EW
840{
841 int n, i;
842 int chan = CR_CHAN(insn->chanspec);
843 unsigned short value;
844 unsigned int d;
845 unsigned int status;
846
847 /* Set configured delay, enable channel for this channel only,
848 * select "ADC read" channel, set "ADC start" bit. */
849 value = (devpriv->s526_ai_config & 0x8000) |
0a85b6f0 850 ((1 << 5) << chan) | (chan << 1) | 0x0001;
0c988d00
EW
851
852 /* convert n samples */
853 for (n = 0; n < insn->n; n++) {
854 /* trigger conversion */
855 outw(value, ADDR_REG(REG_ADC));
232f6502
BP
856/* printk("s526: Wrote 0x%04x to ADC\n", value); */
857/* printk("s526: ADC reg=0x%04x\n", inw(ADDR_REG(REG_ADC))); */
0c988d00
EW
858
859#define TIMEOUT 100
860 /* wait for conversion to end */
861 for (i = 0; i < TIMEOUT; i++) {
862 status = inw(ADDR_REG(REG_ISR));
863 if (status & ISR_ADC_DONE) {
864 outw(ISR_ADC_DONE, ADDR_REG(REG_ISR));
865 break;
866 }
867 }
868 if (i == TIMEOUT) {
5f74ea14 869 /* printk() should be used instead of printk()
0c988d00 870 * whenever the code can be called from real-time. */
5f74ea14 871 printk("s526: ADC(0x%04x) timeout\n",
0a85b6f0 872 inw(ADDR_REG(REG_ISR)));
0c988d00
EW
873 return -ETIMEDOUT;
874 }
875
876 /* read data */
877 d = inw(ADDR_REG(REG_ADD));
232f6502 878/* printk("AI[%d]=0x%04x\n", n, (unsigned short)(d & 0xFFFF)); */
0c988d00
EW
879
880 /* munge data */
881 data[n] = d ^ 0x8000;
882 }
883
884 /* return the number of samples read/written */
885 return n;
886}
887
da91b269 888static int s526_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 889 struct comedi_insn *insn, unsigned int *data)
0c988d00
EW
890{
891 int i;
892 int chan = CR_CHAN(insn->chanspec);
893 unsigned short val;
894
232f6502 895/* printk("s526_ao_winsn\n"); */
0c988d00 896 val = chan << 1;
232f6502 897/* outw(val, dev->iobase + REG_DAC); */
0c988d00
EW
898 outw(val, ADDR_REG(REG_DAC));
899
900 /* Writing a list of values to an AO channel is probably not
901 * very useful, but that's how the interface is defined. */
902 for (i = 0; i < insn->n; i++) {
903 /* a typical programming sequence */
232f6502
BP
904/* outw(data[i], dev->iobase + REG_ADD); write the data to preload register */
905 outw(data[i], ADDR_REG(REG_ADD)); /* write the data to preload register */
0c988d00 906 devpriv->ao_readback[chan] = data[i];
232f6502
BP
907/* outw(val + 1, dev->iobase + REG_DAC); starts the D/A conversion. */
908 outw(val + 1, ADDR_REG(REG_DAC)); /* starts the D/A conversion. */
0c988d00
EW
909 }
910
911 /* return the number of samples read/written */
912 return i;
913}
914
915/* AO subdevices should have a read insn as well as a write insn.
916 * Usually this means copying a value stored in devpriv. */
da91b269 917static int s526_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 918 struct comedi_insn *insn, unsigned int *data)
0c988d00
EW
919{
920 int i;
921 int chan = CR_CHAN(insn->chanspec);
922
923 for (i = 0; i < insn->n; i++)
924 data[i] = devpriv->ao_readback[chan];
925
926 return i;
927}
928
929/* DIO devices are slightly special. Although it is possible to
930 * implement the insn_read/insn_write interface, it is much more
931 * useful to applications if you implement the insn_bits interface.
932 * This allows packed reading/writing of the DIO channels. The
933 * comedi core can convert between insn_bits and insn_read/write */
0a85b6f0
MT
934static int s526_dio_insn_bits(struct comedi_device *dev,
935 struct comedi_subdevice *s,
936 struct comedi_insn *insn, unsigned int *data)
0c988d00
EW
937{
938 if (insn->n != 2)
939 return -EINVAL;
940
941 /* The insn data is a mask in data[0] and the new data
942 * in data[1], each channel cooresponding to a bit. */
943 if (data[0]) {
944 s->state &= ~data[0];
945 s->state |= data[0] & data[1];
946 /* Write out the new digital output lines */
947 outw(s->state, ADDR_REG(REG_DIO));
948 }
949
950 /* on return, data[1] contains the value of the digital
951 * input and output lines. */
232f6502 952 data[1] = inw(ADDR_REG(REG_DIO)) & 0xFF; /* low 8 bits are the data */
0c988d00
EW
953 /* or we could just return the software copy of the output values if
954 * it was a purely digital output subdevice */
10f27014 955 /* data[1]=s->state & 0xFF; */
0c988d00
EW
956
957 return 2;
958}
959
0a85b6f0
MT
960static int s526_dio_insn_config(struct comedi_device *dev,
961 struct comedi_subdevice *s,
962 struct comedi_insn *insn, unsigned int *data)
0c988d00
EW
963{
964 int chan = CR_CHAN(insn->chanspec);
10f27014 965 int group, mask;
0c988d00
EW
966
967 printk("S526 DIO insn_config\n");
968
0c988d00
EW
969 /* The input or output configuration of each digital line is
970 * configured by a special insn_config instruction. chanspec
971 * contains the channel to be changed, and data[0] contains the
972 * value COMEDI_INPUT or COMEDI_OUTPUT. */
973
10f27014
IA
974 group = chan >> 2;
975 mask = 0xF << (group << 2);
976 switch (data[0]) {
977 case INSN_CONFIG_DIO_OUTPUT:
978 s->state |= 1 << (group + 10); // bit 10/11 set the group 1/2's mode
979 s->io_bits |= mask;
980 break;
981 case INSN_CONFIG_DIO_INPUT:
982 s->state &= ~(1 << (group + 10));// 1 is output, 0 is input.
983 s->io_bits &= ~mask;
984 break;
985 case INSN_CONFIG_DIO_QUERY:
986 data[1] = (s->io_bits & mask) ? COMEDI_OUTPUT : COMEDI_INPUT;
987 return insn->n;
988 default:
989 return -EINVAL;
0c988d00 990 }
10f27014 991 outw(s->state, ADDR_REG(REG_DIO));
0c988d00
EW
992
993 return 1;
994}
995
996/*
997 * A convenient macro that defines init_module() and cleanup_module(),
998 * as necessary.
999 */
1000COMEDI_INITCLEANUP(driver_s526);