[go: nahoru, domu]

1/*
2 * ii_pci20kc.c
3 * Driver for Intelligent Instruments PCI-20001C carrier board and modules.
4 *
5 * Copyright (C) 2000 Markus Kempf <kempf@matsci.uni-sb.de>
6 * with suggestions from David Schleef		16.06.2000
7 */
8
9/*
10 * Driver: ii_pci20kc
11 * Description: Intelligent Instruments PCI-20001C carrier board
12 * Devices: (Intelligent Instrumentation) PCI-20001C [ii_pci20kc]
13 * Author: Markus Kempf <kempf@matsci.uni-sb.de>
14 * Status: works
15 *
16 * Supports the PCI-20001C-1a and PCI-20001C-2a carrier boards. The
17 * -2a version has 32 on-board DIO channels. Three add-on modules
18 * can be added to the carrier board for additional functionality.
19 *
20 * Supported add-on modules:
21 *	PCI-20006M-1   1 channel, 16-bit analog output module
22 *	PCI-20006M-2   2 channel, 16-bit analog output module
23 *	PCI-20341M-1A  4 channel, 16-bit analog input module
24 *
25 * Options:
26 *   0   Board base address
27 *   1   IRQ (not-used)
28 */
29
30#include <linux/module.h>
31#include "../comedidev.h"
32
33/*
34 * Register I/O map
35 */
36#define II20K_SIZE			0x400
37#define II20K_MOD_OFFSET		0x100
38#define II20K_ID_REG			0x00
39#define II20K_ID_MOD1_EMPTY		(1 << 7)
40#define II20K_ID_MOD2_EMPTY		(1 << 6)
41#define II20K_ID_MOD3_EMPTY		(1 << 5)
42#define II20K_ID_MASK			0x1f
43#define II20K_ID_PCI20001C_1A		0x1b	/* no on-board DIO */
44#define II20K_ID_PCI20001C_2A		0x1d	/* on-board DIO */
45#define II20K_MOD_STATUS_REG		0x40
46#define II20K_MOD_STATUS_IRQ_MOD1	(1 << 7)
47#define II20K_MOD_STATUS_IRQ_MOD2	(1 << 6)
48#define II20K_MOD_STATUS_IRQ_MOD3	(1 << 5)
49#define II20K_DIO0_REG			0x80
50#define II20K_DIO1_REG			0x81
51#define II20K_DIR_ENA_REG		0x82
52#define II20K_DIR_DIO3_OUT		(1 << 7)
53#define II20K_DIR_DIO2_OUT		(1 << 6)
54#define II20K_BUF_DISAB_DIO3		(1 << 5)
55#define II20K_BUF_DISAB_DIO2		(1 << 4)
56#define II20K_DIR_DIO1_OUT		(1 << 3)
57#define II20K_DIR_DIO0_OUT		(1 << 2)
58#define II20K_BUF_DISAB_DIO1		(1 << 1)
59#define II20K_BUF_DISAB_DIO0		(1 << 0)
60#define II20K_CTRL01_REG		0x83
61#define II20K_CTRL01_SET		(1 << 7)
62#define II20K_CTRL01_DIO0_IN		(1 << 4)
63#define II20K_CTRL01_DIO1_IN		(1 << 1)
64#define II20K_DIO2_REG			0xc0
65#define II20K_DIO3_REG			0xc1
66#define II20K_CTRL23_REG		0xc3
67#define II20K_CTRL23_SET		(1 << 7)
68#define II20K_CTRL23_DIO2_IN		(1 << 4)
69#define II20K_CTRL23_DIO3_IN		(1 << 1)
70
71#define II20K_ID_PCI20006M_1		0xe2	/* 1 AO channels */
72#define II20K_ID_PCI20006M_2		0xe3	/* 2 AO channels */
73#define II20K_AO_STRB_REG(x)		(0x0b + ((x) * 0x08))
74#define II20K_AO_LSB_REG(x)		(0x0d + ((x) * 0x08))
75#define II20K_AO_MSB_REG(x)		(0x0e + ((x) * 0x08))
76#define II20K_AO_STRB_BOTH_REG		0x1b
77
78#define II20K_ID_PCI20341M_1		0x77	/* 4 AI channels */
79#define II20K_AI_STATUS_CMD_REG		0x01
80#define II20K_AI_STATUS_CMD_BUSY	(1 << 7)
81#define II20K_AI_STATUS_CMD_HW_ENA	(1 << 1)
82#define II20K_AI_STATUS_CMD_EXT_START	(1 << 0)
83#define II20K_AI_LSB_REG		0x02
84#define II20K_AI_MSB_REG		0x03
85#define II20K_AI_PACER_RESET_REG	0x04
86#define II20K_AI_16BIT_DATA_REG		0x06
87#define II20K_AI_CONF_REG		0x10
88#define II20K_AI_CONF_ENA		(1 << 2)
89#define II20K_AI_OPT_REG		0x11
90#define II20K_AI_OPT_TRIG_ENA		(1 << 5)
91#define II20K_AI_OPT_TRIG_INV		(1 << 4)
92#define II20K_AI_OPT_TIMEBASE(x)	(((x) & 0x3) << 1)
93#define II20K_AI_OPT_BURST_MODE		(1 << 0)
94#define II20K_AI_STATUS_REG		0x12
95#define II20K_AI_STATUS_INT		(1 << 7)
96#define II20K_AI_STATUS_TRIG		(1 << 6)
97#define II20K_AI_STATUS_TRIG_ENA	(1 << 5)
98#define II20K_AI_STATUS_PACER_ERR	(1 << 2)
99#define II20K_AI_STATUS_DATA_ERR	(1 << 1)
100#define II20K_AI_STATUS_SET_TIME_ERR	(1 << 0)
101#define II20K_AI_LAST_CHAN_ADDR_REG	0x13
102#define II20K_AI_CUR_ADDR_REG		0x14
103#define II20K_AI_SET_TIME_REG		0x15
104#define II20K_AI_DELAY_LSB_REG		0x16
105#define II20K_AI_DELAY_MSB_REG		0x17
106#define II20K_AI_CHAN_ADV_REG		0x18
107#define II20K_AI_CHAN_RESET_REG		0x19
108#define II20K_AI_START_TRIG_REG		0x1a
109#define II20K_AI_COUNT_RESET_REG	0x1b
110#define II20K_AI_CHANLIST_REG		0x80
111#define II20K_AI_CHANLIST_ONBOARD_ONLY	(1 << 5)
112#define II20K_AI_CHANLIST_GAIN(x)	(((x) & 0x3) << 3)
113#define II20K_AI_CHANLIST_MUX_ENA	(1 << 2)
114#define II20K_AI_CHANLIST_CHAN(x)	(((x) & 0x3) << 0)
115#define II20K_AI_CHANLIST_LEN		0x80
116
117/* the AO range is set by jumpers on the 20006M module */
118static const struct comedi_lrange ii20k_ao_ranges = {
119	3, {
120		BIP_RANGE(5),	/* Chan 0 - W1/W3 in   Chan 1 - W2/W4 in  */
121		UNI_RANGE(10),	/* Chan 0 - W1/W3 out  Chan 1 - W2/W4 in  */
122		BIP_RANGE(10)	/* Chan 0 - W1/W3 in   Chan 1 - W2/W4 out */
123	}
124};
125
126static const struct comedi_lrange ii20k_ai_ranges = {
127	4, {
128		BIP_RANGE(5),		/* gain 1 */
129		BIP_RANGE(0.5),		/* gain 10 */
130		BIP_RANGE(0.05),	/* gain 100 */
131		BIP_RANGE(0.025)	/* gain 200 */
132	},
133};
134
135static void __iomem *ii20k_module_iobase(struct comedi_device *dev,
136					 struct comedi_subdevice *s)
137{
138	return dev->mmio + (s->index + 1) * II20K_MOD_OFFSET;
139}
140
141static int ii20k_ao_insn_write(struct comedi_device *dev,
142			       struct comedi_subdevice *s,
143			       struct comedi_insn *insn,
144			       unsigned int *data)
145{
146	void __iomem *iobase = ii20k_module_iobase(dev, s);
147	unsigned int chan = CR_CHAN(insn->chanspec);
148	int i;
149
150	for (i = 0; i < insn->n; i++) {
151		unsigned int val = data[i];
152
153		s->readback[chan] = val;
154
155		/* munge data */
156		val += ((s->maxdata + 1) >> 1);
157		val &= s->maxdata;
158
159		writeb(val & 0xff, iobase + II20K_AO_LSB_REG(chan));
160		writeb((val >> 8) & 0xff, iobase + II20K_AO_MSB_REG(chan));
161		writeb(0x00, iobase + II20K_AO_STRB_REG(chan));
162	}
163
164	return insn->n;
165}
166
167static int ii20k_ai_eoc(struct comedi_device *dev,
168			struct comedi_subdevice *s,
169			struct comedi_insn *insn,
170			unsigned long context)
171{
172	void __iomem *iobase = ii20k_module_iobase(dev, s);
173	unsigned char status;
174
175	status = readb(iobase + II20K_AI_STATUS_REG);
176	if ((status & II20K_AI_STATUS_INT) == 0)
177		return 0;
178	return -EBUSY;
179}
180
181static void ii20k_ai_setup(struct comedi_device *dev,
182			   struct comedi_subdevice *s,
183			   unsigned int chanspec)
184{
185	void __iomem *iobase = ii20k_module_iobase(dev, s);
186	unsigned int chan = CR_CHAN(chanspec);
187	unsigned int range = CR_RANGE(chanspec);
188	unsigned char val;
189
190	/* initialize module */
191	writeb(II20K_AI_CONF_ENA, iobase + II20K_AI_CONF_REG);
192
193	/* software conversion */
194	writeb(0, iobase + II20K_AI_STATUS_CMD_REG);
195
196	/* set the time base for the settling time counter based on the gain */
197	val = (range < 3) ? II20K_AI_OPT_TIMEBASE(0) : II20K_AI_OPT_TIMEBASE(2);
198	writeb(val, iobase + II20K_AI_OPT_REG);
199
200	/* set the settling time counter based on the gain */
201	val = (range < 2) ? 0x58 : (range < 3) ? 0x93 : 0x99;
202	writeb(val, iobase + II20K_AI_SET_TIME_REG);
203
204	/* set number of input channels */
205	writeb(1, iobase + II20K_AI_LAST_CHAN_ADDR_REG);
206
207	/* set the channel list byte */
208	val = II20K_AI_CHANLIST_ONBOARD_ONLY |
209	      II20K_AI_CHANLIST_MUX_ENA |
210	      II20K_AI_CHANLIST_GAIN(range) |
211	      II20K_AI_CHANLIST_CHAN(chan);
212	writeb(val, iobase + II20K_AI_CHANLIST_REG);
213
214	/* reset settling time counter and trigger delay counter */
215	writeb(0, iobase + II20K_AI_COUNT_RESET_REG);
216
217	/* reset channel scanner */
218	writeb(0, iobase + II20K_AI_CHAN_RESET_REG);
219}
220
221static int ii20k_ai_insn_read(struct comedi_device *dev,
222			      struct comedi_subdevice *s,
223			      struct comedi_insn *insn,
224			      unsigned int *data)
225{
226	void __iomem *iobase = ii20k_module_iobase(dev, s);
227	int ret;
228	int i;
229
230	ii20k_ai_setup(dev, s, insn->chanspec);
231
232	for (i = 0; i < insn->n; i++) {
233		unsigned int val;
234
235		/* generate a software start convert signal */
236		readb(iobase + II20K_AI_PACER_RESET_REG);
237
238		ret = comedi_timeout(dev, s, insn, ii20k_ai_eoc, 0);
239		if (ret)
240			return ret;
241
242		val = readb(iobase + II20K_AI_LSB_REG);
243		val |= (readb(iobase + II20K_AI_MSB_REG) << 8);
244
245		/* munge two's complement data */
246		val += ((s->maxdata + 1) >> 1);
247		val &= s->maxdata;
248
249		data[i] = val;
250	}
251
252	return insn->n;
253}
254
255static void ii20k_dio_config(struct comedi_device *dev,
256			     struct comedi_subdevice *s)
257{
258	unsigned char ctrl01 = 0;
259	unsigned char ctrl23 = 0;
260	unsigned char dir_ena = 0;
261
262	/* port 0 - channels 0-7 */
263	if (s->io_bits & 0x000000ff) {
264		/* output port */
265		ctrl01 &= ~II20K_CTRL01_DIO0_IN;
266		dir_ena &= ~II20K_BUF_DISAB_DIO0;
267		dir_ena |= II20K_DIR_DIO0_OUT;
268	} else {
269		/* input port */
270		ctrl01 |= II20K_CTRL01_DIO0_IN;
271		dir_ena &= ~II20K_DIR_DIO0_OUT;
272	}
273
274	/* port 1 - channels 8-15 */
275	if (s->io_bits & 0x0000ff00) {
276		/* output port */
277		ctrl01 &= ~II20K_CTRL01_DIO1_IN;
278		dir_ena &= ~II20K_BUF_DISAB_DIO1;
279		dir_ena |= II20K_DIR_DIO1_OUT;
280	} else {
281		/* input port */
282		ctrl01 |= II20K_CTRL01_DIO1_IN;
283		dir_ena &= ~II20K_DIR_DIO1_OUT;
284	}
285
286	/* port 2 - channels 16-23 */
287	if (s->io_bits & 0x00ff0000) {
288		/* output port */
289		ctrl23 &= ~II20K_CTRL23_DIO2_IN;
290		dir_ena &= ~II20K_BUF_DISAB_DIO2;
291		dir_ena |= II20K_DIR_DIO2_OUT;
292	} else {
293		/* input port */
294		ctrl23 |= II20K_CTRL23_DIO2_IN;
295		dir_ena &= ~II20K_DIR_DIO2_OUT;
296	}
297
298	/* port 3 - channels 24-31 */
299	if (s->io_bits & 0xff000000) {
300		/* output port */
301		ctrl23 &= ~II20K_CTRL23_DIO3_IN;
302		dir_ena &= ~II20K_BUF_DISAB_DIO3;
303		dir_ena |= II20K_DIR_DIO3_OUT;
304	} else {
305		/* input port */
306		ctrl23 |= II20K_CTRL23_DIO3_IN;
307		dir_ena &= ~II20K_DIR_DIO3_OUT;
308	}
309
310	ctrl23 |= II20K_CTRL01_SET;
311	ctrl23 |= II20K_CTRL23_SET;
312
313	/* order is important */
314	writeb(ctrl01, dev->mmio + II20K_CTRL01_REG);
315	writeb(ctrl23, dev->mmio + II20K_CTRL23_REG);
316	writeb(dir_ena, dev->mmio + II20K_DIR_ENA_REG);
317}
318
319static int ii20k_dio_insn_config(struct comedi_device *dev,
320				 struct comedi_subdevice *s,
321				 struct comedi_insn *insn,
322				 unsigned int *data)
323{
324	unsigned int chan = CR_CHAN(insn->chanspec);
325	unsigned int mask;
326	int ret;
327
328	if (chan < 8)
329		mask = 0x000000ff;
330	else if (chan < 16)
331		mask = 0x0000ff00;
332	else if (chan < 24)
333		mask = 0x00ff0000;
334	else
335		mask = 0xff000000;
336
337	ret = comedi_dio_insn_config(dev, s, insn, data, mask);
338	if (ret)
339		return ret;
340
341	ii20k_dio_config(dev, s);
342
343	return insn->n;
344}
345
346static int ii20k_dio_insn_bits(struct comedi_device *dev,
347			       struct comedi_subdevice *s,
348			       struct comedi_insn *insn,
349			       unsigned int *data)
350{
351	unsigned int mask;
352
353	mask = comedi_dio_update_state(s, data);
354	if (mask) {
355		if (mask & 0x000000ff)
356			writeb((s->state >> 0) & 0xff,
357			       dev->mmio + II20K_DIO0_REG);
358		if (mask & 0x0000ff00)
359			writeb((s->state >> 8) & 0xff,
360			       dev->mmio + II20K_DIO1_REG);
361		if (mask & 0x00ff0000)
362			writeb((s->state >> 16) & 0xff,
363			       dev->mmio + II20K_DIO2_REG);
364		if (mask & 0xff000000)
365			writeb((s->state >> 24) & 0xff,
366			       dev->mmio + II20K_DIO3_REG);
367	}
368
369	data[1] = readb(dev->mmio + II20K_DIO0_REG);
370	data[1] |= readb(dev->mmio + II20K_DIO1_REG) << 8;
371	data[1] |= readb(dev->mmio + II20K_DIO2_REG) << 16;
372	data[1] |= readb(dev->mmio + II20K_DIO3_REG) << 24;
373
374	return insn->n;
375}
376
377static int ii20k_init_module(struct comedi_device *dev,
378			     struct comedi_subdevice *s)
379{
380	void __iomem *iobase = ii20k_module_iobase(dev, s);
381	unsigned char id;
382	int ret;
383
384	id = readb(iobase + II20K_ID_REG);
385	switch (id) {
386	case II20K_ID_PCI20006M_1:
387	case II20K_ID_PCI20006M_2:
388		/* Analog Output subdevice */
389		s->type		= COMEDI_SUBD_AO;
390		s->subdev_flags	= SDF_WRITABLE;
391		s->n_chan	= (id == II20K_ID_PCI20006M_2) ? 2 : 1;
392		s->maxdata	= 0xffff;
393		s->range_table	= &ii20k_ao_ranges;
394		s->insn_write	= ii20k_ao_insn_write;
395		s->insn_read	= comedi_readback_insn_read;
396
397		ret = comedi_alloc_subdev_readback(s);
398		if (ret)
399			return ret;
400		break;
401	case II20K_ID_PCI20341M_1:
402		/* Analog Input subdevice */
403		s->type		= COMEDI_SUBD_AI;
404		s->subdev_flags	= SDF_READABLE | SDF_DIFF;
405		s->n_chan	= 4;
406		s->maxdata	= 0xffff;
407		s->range_table	= &ii20k_ai_ranges;
408		s->insn_read	= ii20k_ai_insn_read;
409		break;
410	default:
411		s->type = COMEDI_SUBD_UNUSED;
412		break;
413	}
414
415	return 0;
416}
417
418static int ii20k_attach(struct comedi_device *dev,
419			struct comedi_devconfig *it)
420{
421	struct comedi_subdevice *s;
422	unsigned int membase;
423	unsigned char id;
424	bool has_dio;
425	int ret;
426
427	membase = it->options[0];
428	if (!membase || (membase & ~(0x100000 - II20K_SIZE))) {
429		dev_warn(dev->class_dev,
430			 "%s: invalid memory address specified\n",
431			 dev->board_name);
432		return -EINVAL;
433	}
434
435	if (!request_mem_region(membase, II20K_SIZE, dev->board_name)) {
436		dev_warn(dev->class_dev, "%s: I/O mem conflict (%#x,%u)\n",
437			 dev->board_name, membase, II20K_SIZE);
438		return -EIO;
439	}
440	dev->iobase = membase;	/* actually, a memory address */
441
442	dev->mmio = ioremap(membase, II20K_SIZE);
443	if (!dev->mmio)
444		return -ENOMEM;
445
446	id = readb(dev->mmio + II20K_ID_REG);
447	switch (id & II20K_ID_MASK) {
448	case II20K_ID_PCI20001C_1A:
449		has_dio = false;
450		break;
451	case II20K_ID_PCI20001C_2A:
452		has_dio = true;
453		break;
454	default:
455		return -ENODEV;
456	}
457
458	ret = comedi_alloc_subdevices(dev, 4);
459	if (ret)
460		return ret;
461
462	s = &dev->subdevices[0];
463	if (id & II20K_ID_MOD1_EMPTY) {
464		s->type = COMEDI_SUBD_UNUSED;
465	} else {
466		ret = ii20k_init_module(dev, s);
467		if (ret)
468			return ret;
469	}
470
471	s = &dev->subdevices[1];
472	if (id & II20K_ID_MOD2_EMPTY) {
473		s->type = COMEDI_SUBD_UNUSED;
474	} else {
475		ret = ii20k_init_module(dev, s);
476		if (ret)
477			return ret;
478	}
479
480	s = &dev->subdevices[2];
481	if (id & II20K_ID_MOD3_EMPTY) {
482		s->type = COMEDI_SUBD_UNUSED;
483	} else {
484		ret = ii20k_init_module(dev, s);
485		if (ret)
486			return ret;
487	}
488
489	/* Digital I/O subdevice */
490	s = &dev->subdevices[3];
491	if (has_dio) {
492		s->type		= COMEDI_SUBD_DIO;
493		s->subdev_flags	= SDF_READABLE | SDF_WRITABLE;
494		s->n_chan	= 32;
495		s->maxdata	= 1;
496		s->range_table	= &range_digital;
497		s->insn_bits	= ii20k_dio_insn_bits;
498		s->insn_config	= ii20k_dio_insn_config;
499
500		/* default all channels to input */
501		ii20k_dio_config(dev, s);
502	} else {
503		s->type = COMEDI_SUBD_UNUSED;
504	}
505
506	return 0;
507}
508
509static void ii20k_detach(struct comedi_device *dev)
510{
511	if (dev->mmio)
512		iounmap(dev->mmio);
513	if (dev->iobase)	/* actually, a memory address */
514		release_mem_region(dev->iobase, II20K_SIZE);
515}
516
517static struct comedi_driver ii20k_driver = {
518	.driver_name	= "ii_pci20kc",
519	.module		= THIS_MODULE,
520	.attach		= ii20k_attach,
521	.detach		= ii20k_detach,
522};
523module_comedi_driver(ii20k_driver);
524
525MODULE_AUTHOR("Comedi http://www.comedi.org");
526MODULE_DESCRIPTION("Comedi low-level driver");
527MODULE_LICENSE("GPL");
528