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