s526.c revision ca98ee7bb4005365ad395b53229e2f6d01ca7cbd
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
115struct counter_mode_register_t {
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;
127};
128
129union cmReg {
130	struct counter_mode_register_t reg;
131	unsigned short value;
132};
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! */
138enum S526_GPCT_APP_CLASS {
139	CountingAndTimeMeasurement,
140	SinglePulseGeneration,
141	PulseTrainGeneration,
142	PositionMeasurement,
143	Miscellaneous
144};
145
146/* Config struct for different GPCT subdevice Application Classes and
147   their options
148*/
149struct s526GPCTConfig {
150	enum S526_GPCT_APP_CLASS app;
151	int data[MAX_GPCT_CONFIG_DATA];
152};
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 */
159struct s526_board {
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;
168};
169
170static const struct s526_board s526_boards[] = {
171	{
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	 }
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 */
189#define thisboard ((const struct s526_board *)dev->board_ptr)
190
191/* this structure is for data unique to this hardware driver.  If
192   several hardware drivers keep similar information in this structure,
193   feel free to suggest moving the variable to the struct comedi_device struct.  */
194struct s526_private {
195
196	int data;
197
198	/* would be useful for a PCI device */
199	struct pci_dev *pci_dev;
200
201	/* Used for AO readback */
202	unsigned int ao_readback[2];
203
204	struct s526GPCTConfig s526_gpct_config[4];
205	unsigned short s526_ai_config;
206};
207
208/*
209 * most drivers define the following macro to make it easy to
210 * access the private structure.
211 */
212#define devpriv ((struct s526_private *)dev->private)
213
214/*
215 * The struct comedi_driver structure tells the Comedi core module
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 */
220static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it);
221static int s526_detach(struct comedi_device *dev);
222static struct comedi_driver driver_s526 = {
223	.driver_name = "s526",
224	.module = THIS_MODULE,
225	.attach = s526_attach,
226	.detach = s526_detach,
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	 */
245	.board_name = &s526_boards[0].name,
246	.offset = sizeof(struct s526_board),
247	.num_names = ARRAY_SIZE(s526_boards),
248};
249
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);
262static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
263			 struct comedi_insn *insn, unsigned int *data);
264static int s526_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
265			 struct comedi_insn *insn, unsigned int *data);
266static int s526_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
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);
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 */
281static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it)
282{
283	struct comedi_subdevice *s;
284	int iobase;
285	int i, n;
286/* short value; */
287/* int subdev_channel = 0; */
288	union cmReg cmReg;
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 */
319	if (alloc_private(dev, sizeof(struct s526_private)) < 0)
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 */
344	/* s->do_cmd = s526_gpct_cmd; */
345	/* s->do_cmdtest = s526_gpct_cmdtest; */
346	/* s->cancel = s526_gpct_cancel; */
347
348	s = dev->subdevices + 1;
349	/* dev->read_subdev=s; */
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;
379		s->n_chan = 8;
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
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 */
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
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 */
414
415	outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel));	/*  Reset RCAP (fires one-shot) */
416
417#else
418
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 */
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,
434									n));
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
439	/*  Load the pre-laod register high word */
440/* value = (short) (0x55); */
441/* outw(value, ADDR_CHAN_REG(REG_C0H, n)); */
442
443	/*  Load the pre-laod register low word */
444/* value = (short)(0xaa55); */
445/* outw(value, ADDR_CHAN_REG(REG_C0L, n)); */
446
447	/*  Write the Counter Control Register */
448/* outw(value, ADDR_CHAN_REG(REG_C0C, 0)); */
449
450	/*  Reset the counter if it is software preload */
451	if (cmReg.reg.autoLoadResetRcap == 0) {
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 */
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]),
465		       inw(ADDR_REG(s526_ports[i])));
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 */
478static int s526_detach(struct comedi_device *dev)
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
488static int s526_gpct_rinsn(struct comedi_device *dev,
489			   struct comedi_subdevice *s, struct comedi_insn *insn,
490			   unsigned int *data)
491{
492	int i;			/*  counts the Data */
493	int counter_channel = CR_CHAN(insn->chanspec);
494	unsigned short datalow;
495	unsigned short datahigh;
496
497	/*  Check if (n > 0) */
498	if (insn->n <= 0) {
499		printk("s526: INSN_READ: n should be > 0\n");
500		return -EINVAL;
501	}
502	/*  Read the low word first */
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);
508/* printk("s526 GPCT[%d]: %x(0x%04x, 0x%04x)\n", counter_channel, data[i], datahigh, datalow); */
509	}
510	return i;
511}
512
513static int s526_gpct_insn_config(struct comedi_device *dev,
514				 struct comedi_subdevice *s,
515				 struct comedi_insn *insn, unsigned int *data)
516{
517	int subdev_channel = CR_CHAN(insn->chanspec);	/*  Unpack chanspec */
518	int i;
519	short value;
520	union cmReg cmReg;
521
522/* printk("s526: GPCT_INSN_CONFIG: Configuring Channel %d\n", subdev_channel); */
523
524	for (i = 0; i < MAX_GPCT_CONFIG_DATA; i++) {
525		devpriv->s526_gpct_config[subdev_channel].data[i] =
526		    insn->data[i];
527/* printk("data[%d]=%x\n", i, insn->data[i]); */
528	}
529
530	/*  Check what type of Counter the user requested, data[0] contains */
531	/*  the Application type */
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 =
542		    PositionMeasurement;
543
544#if 0
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;
558
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 */
566
567		outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel));	/*  Reset RCAP (fires one-shot) */
568
569#endif
570
571#if 1
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 */
583		cmReg.reg.reserved = 0;
584
585		/*  Set Counter Mode Register */
586/* printk("s526: Counter Mode register=%x\n", cmReg.value); */
587		outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
588
589		/*  Reset the counter if it is software preload */
590		if (cmReg.reg.autoLoadResetRcap == 0) {
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 */
593		}
594#else
595		cmReg.reg.countDirCtrl = 0;	/*  0 quadrature, 1 software control */
596
597		/*  data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4 */
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
606		/*  When to take into account the indexpulse: */
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		}
612		/*  Take into account the index pulse? */
613		if (insn->data[3] == GPCT_RESET_COUNTER_ON_INDEX)
614			cmReg.reg.autoLoadResetRcap = 4;	/*  Auto load with INDEX^ */
615
616		/*  Set Counter Mode Register */
617		cmReg.value = (short)(insn->data[1] & 0xFFFF);
618		outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
619
620		/*  Load the pre-laod register high word */
621		value = (short)((insn->data[2] >> 16) & 0xFFFF);
622		outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
623
624		/*  Load the pre-laod register low word */
625		value = (short)(insn->data[2] & 0xFFFF);
626		outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
627
628		/*  Write the Counter Control Register */
629		if (insn->data[3] != 0) {
630			value = (short)(insn->data[3] & 0xFFFF);
631			outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
632		}
633		/*  Reset the counter if it is software preload */
634		if (cmReg.reg.autoLoadResetRcap == 0) {
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 */
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 =
651		    SinglePulseGeneration;
652
653		/*  Set Counter Mode Register */
654		cmReg.value = (short)(insn->data[1] & 0xFFFF);
655		cmReg.reg.preloadRegSel = 0;	/*  PR0 */
656		outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
657
658		/*  Load the pre-laod register 0 high word */
659		value = (short)((insn->data[2] >> 16) & 0xFFFF);
660		outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
661
662		/*  Load the pre-laod register 0 low word */
663		value = (short)(insn->data[2] & 0xFFFF);
664		outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
665
666		/*  Set Counter Mode Register */
667		cmReg.value = (short)(insn->data[1] & 0xFFFF);
668		cmReg.reg.preloadRegSel = 1;	/*  PR1 */
669		outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
670
671		/*  Load the pre-laod register 1 high word */
672		value = (short)((insn->data[3] >> 16) & 0xFFFF);
673		outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
674
675		/*  Load the pre-laod register 1 low word */
676		value = (short)(insn->data[3] & 0xFFFF);
677		outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
678
679		/*  Write the Counter Control Register */
680		if (insn->data[3] != 0) {
681			value = (short)(insn->data[3] & 0xFFFF);
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 =
696		    PulseTrainGeneration;
697
698		/*  Set Counter Mode Register */
699		cmReg.value = (short)(insn->data[1] & 0xFFFF);
700		cmReg.reg.preloadRegSel = 0;	/*  PR0 */
701		outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
702
703		/*  Load the pre-laod register 0 high word */
704		value = (short)((insn->data[2] >> 16) & 0xFFFF);
705		outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
706
707		/*  Load the pre-laod register 0 low word */
708		value = (short)(insn->data[2] & 0xFFFF);
709		outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
710
711		/*  Set Counter Mode Register */
712		cmReg.value = (short)(insn->data[1] & 0xFFFF);
713		cmReg.reg.preloadRegSel = 1;	/*  PR1 */
714		outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
715
716		/*  Load the pre-laod register 1 high word */
717		value = (short)((insn->data[3] >> 16) & 0xFFFF);
718		outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
719
720		/*  Load the pre-laod register 1 low word */
721		value = (short)(insn->data[3] & 0xFFFF);
722		outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
723
724		/*  Write the Counter Control Register */
725		if (insn->data[3] != 0) {
726			value = (short)(insn->data[3] & 0xFFFF);
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
740static int s526_gpct_winsn(struct comedi_device *dev,
741			   struct comedi_subdevice *s, struct comedi_insn *insn,
742			   unsigned int *data)
743{
744	int subdev_channel = CR_CHAN(insn->chanspec);	/*  Unpack chanspec */
745	short value;
746	union cmReg cmReg;
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);
751	/*  Check what Application of Counter this channel is configured for */
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,
756							     subdev_channel));
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,
763							     subdev_channel));
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] =
777			    insn->data[0];
778			(devpriv->s526_gpct_config[subdev_channel]).data[1] =
779			    insn->data[1];
780		} else {
781			printk("%d \t %d\n", insn->data[1], insn->data[2]);
782			printk
783			    ("s526: INSN_WRITE: PTG: Problem with Pulse params\n");
784			return -EINVAL;
785		}
786
787		value = (short)((*data >> 16) & 0xFFFF);
788		outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
789		value = (short)(*data & 0xFFFF);
790		outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
791		break;
792	default:		/*  Impossible */
793		printk
794		    ("s526: INSN_WRITE: Functionality %d not implemented yet\n",
795		     devpriv->s526_gpct_config[subdev_channel].app);
796		return -EINVAL;
797		break;
798	}
799	/*  return the number of samples written */
800	return insn->n;
801}
802
803#define ISR_ADC_DONE 0x4
804static int s526_ai_insn_config(struct comedi_device *dev,
805			       struct comedi_subdevice *s,
806			       struct comedi_insn *insn, unsigned int *data)
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
822	/*  Enable ADC interrupt */
823	outw(ISR_ADC_DONE, ADDR_REG(REG_IER));
824/* printk("s526: ADC current value: 0x%04x\n", inw(ADDR_REG(REG_ADC))); */
825	devpriv->s526_ai_config = (data[0] & 0x3FF) << 5;
826	if (data[1] > 0)
827		devpriv->s526_ai_config |= 0x8000;	/* set the delay */
828
829	devpriv->s526_ai_config |= 0x0001;	/*  ADC start bit. */
830
831	return result;
832}
833
834/*
835 * "instructions" read/write data in "one-shot" or "software-triggered"
836 * mode.
837 */
838static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
839			 struct comedi_insn *insn, unsigned int *data)
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) |
850	    ((1 << 5) << chan) | (chan << 1) | 0x0001;
851
852	/* convert n samples */
853	for (n = 0; n < insn->n; n++) {
854		/* trigger conversion */
855		outw(value, ADDR_REG(REG_ADC));
856/* printk("s526: Wrote 0x%04x to ADC\n", value); */
857/* printk("s526: ADC reg=0x%04x\n", inw(ADDR_REG(REG_ADC))); */
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) {
869			/* printk() should be used instead of printk()
870			 * whenever the code can be called from real-time. */
871			printk("s526: ADC(0x%04x) timeout\n",
872			       inw(ADDR_REG(REG_ISR)));
873			return -ETIMEDOUT;
874		}
875
876		/* read data */
877		d = inw(ADDR_REG(REG_ADD));
878/* printk("AI[%d]=0x%04x\n", n, (unsigned short)(d & 0xFFFF)); */
879
880		/* munge data */
881		data[n] = d ^ 0x8000;
882	}
883
884	/* return the number of samples read/written */
885	return n;
886}
887
888static int s526_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
889			 struct comedi_insn *insn, unsigned int *data)
890{
891	int i;
892	int chan = CR_CHAN(insn->chanspec);
893	unsigned short val;
894
895/* printk("s526_ao_winsn\n"); */
896	val = chan << 1;
897/* outw(val, dev->iobase + REG_DAC); */
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 */
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 */
906		devpriv->ao_readback[chan] = data[i];
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. */
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. */
917static int s526_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
918			 struct comedi_insn *insn, unsigned int *data)
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 */
934static int s526_dio_insn_bits(struct comedi_device *dev,
935			      struct comedi_subdevice *s,
936			      struct comedi_insn *insn, unsigned int *data)
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. */
952	data[1] = inw(ADDR_REG(REG_DIO)) & 0xFF;	/*  low 8 bits are the data */
953	/* or we could just return the software copy of the output values if
954	 * it was a purely digital output subdevice */
955	/* data[1]=s->state & 0xFF; */
956
957	return 2;
958}
959
960static int s526_dio_insn_config(struct comedi_device *dev,
961				struct comedi_subdevice *s,
962				struct comedi_insn *insn, unsigned int *data)
963{
964	int chan = CR_CHAN(insn->chanspec);
965	int group, mask;
966
967	printk("S526 DIO insn_config\n");
968
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
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;
990	}
991	outw(s->state, ADDR_REG(REG_DIO));
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);
1001