[go: nahoru, domu]

1/*
2 * comedi/drivers/pcl730.c
3 * Driver for Advantech PCL-730 and clones
4 * José Luis Sánchez
5 */
6
7/*
8 * Driver: pcl730
9 * Description: Advantech PCL-730 (& compatibles)
10 * Devices: (Advantech) PCL-730 [pcl730]
11 *	    (ICP) ISO-730 [iso730]
12 *	    (Adlink) ACL-7130 [acl7130]
13 *	    (Advantech) PCM-3730 [pcm3730]
14 *	    (Advantech) PCL-725 [pcl725]
15 *	    (ICP) P8R8-DIO [p16r16dio]
16 *	    (Adlink) ACL-7225b [acl7225b]
17 *	    (ICP) P16R16-DIO [p16r16dio]
18 *	    (Advantech) PCL-733 [pcl733]
19 *	    (Advantech) PCL-734 [pcl734]
20 *	    (Diamond Systems) OPMM-1616-XT [opmm-1616-xt]
21 *	    (Diamond Systems) PEARL-MM-P [prearl-mm-p]
22 *	    (Diamond Systems) IR104-PBF [ir104-pbf]
23 * Author: José Luis Sánchez (jsanchezv@teleline.es)
24 * Status: untested
25 *
26 * Configuration options:
27 *   [0] - I/O port base
28 *
29 * Interrupts are not supported.
30 * The ACL-7130 card has an 8254 timer/counter not supported by this driver.
31 */
32
33#include <linux/module.h>
34#include "../comedidev.h"
35
36/*
37 * Register map
38 *
39 * The register map varies slightly depending on the board type but
40 * all registers are 8-bit.
41 *
42 * The boardinfo 'io_range' is used to allow comedi to request the
43 * proper range required by the board.
44 *
45 * The comedi_subdevice 'private' data is used to pass the register
46 * offset to the (*insn_bits) functions to read/write the correct
47 * registers.
48 *
49 * The basic register mapping looks like this:
50 *
51 *     BASE+0  Isolated outputs 0-7 (write) / inputs 0-7 (read)
52 *     BASE+1  Isolated outputs 8-15 (write) / inputs 8-15 (read)
53 *     BASE+2  TTL outputs 0-7 (write) / inputs 0-7 (read)
54 *     BASE+3  TTL outputs 8-15 (write) / inputs 8-15 (read)
55 *
56 * The pcm3730 board does not have register BASE+1.
57 *
58 * The pcl725 and p8r8dio only have registers BASE+0 and BASE+1:
59 *
60 *     BASE+0  Isolated outputs 0-7 (write) (read back on p8r8dio)
61 *     BASE+1  Isolated inputs 0-7 (read)
62 *
63 * The acl7225b and p16r16dio boards have this register mapping:
64 *
65 *     BASE+0  Isolated outputs 0-7 (write) (read back)
66 *     BASE+1  Isolated outputs 8-15 (write) (read back)
67 *     BASE+2  Isolated inputs 0-7 (read)
68 *     BASE+3  Isolated inputs 8-15 (read)
69 *
70 * The pcl733 and pcl733 boards have this register mapping:
71 *
72 *     BASE+0  Isolated outputs 0-7 (write) or inputs 0-7 (read)
73 *     BASE+1  Isolated outputs 8-15 (write) or inputs 8-15 (read)
74 *     BASE+2  Isolated outputs 16-23 (write) or inputs 16-23 (read)
75 *     BASE+3  Isolated outputs 24-31 (write) or inputs 24-31 (read)
76 *
77 * The opmm-1616-xt board has this register mapping:
78 *
79 *     BASE+0  Isolated outputs 0-7 (write) (read back)
80 *     BASE+1  Isolated outputs 8-15 (write) (read back)
81 *     BASE+2  Isolated inputs 0-7 (read)
82 *     BASE+3  Isolated inputs 8-15 (read)
83 *
84 *     These registers are not currently supported:
85 *
86 *     BASE+2  Relay select register (write)
87 *     BASE+3  Board reset control register (write)
88 *     BASE+4  Interrupt control register (write)
89 *     BASE+4  Change detect 7-0 status register (read)
90 *     BASE+5  LED control register (write)
91 *     BASE+5  Change detect 15-8 status register (read)
92 *
93 * The pearl-mm-p board has this register mapping:
94 *
95 *     BASE+0  Isolated outputs 0-7 (write)
96 *     BASE+1  Isolated outputs 8-15 (write)
97 *
98 * The ir104-pbf board has this register mapping:
99 *
100 *     BASE+0  Isolated outputs 0-7 (write) (read back)
101 *     BASE+1  Isolated outputs 8-15 (write) (read back)
102 *     BASE+2  Isolated outputs 16-19 (write) (read back)
103 *     BASE+4  Isolated inputs 0-7 (read)
104 *     BASE+5  Isolated inputs 8-15 (read)
105 *     BASE+6  Isolated inputs 16-19 (read)
106 */
107
108struct pcl730_board {
109	const char *name;
110	unsigned int io_range;
111	unsigned is_pcl725:1;
112	unsigned is_acl7225b:1;
113	unsigned is_ir104:1;
114	unsigned has_readback:1;
115	unsigned has_ttl_io:1;
116	int n_subdevs;
117	int n_iso_out_chan;
118	int n_iso_in_chan;
119	int n_ttl_chan;
120};
121
122static const struct pcl730_board pcl730_boards[] = {
123	{
124		.name		= "pcl730",
125		.io_range	= 0x04,
126		.has_ttl_io	= 1,
127		.n_subdevs	= 4,
128		.n_iso_out_chan	= 16,
129		.n_iso_in_chan	= 16,
130		.n_ttl_chan	= 16,
131	}, {
132		.name		= "iso730",
133		.io_range	= 0x04,
134		.n_subdevs	= 4,
135		.n_iso_out_chan	= 16,
136		.n_iso_in_chan	= 16,
137		.n_ttl_chan	= 16,
138	}, {
139		.name		= "acl7130",
140		.io_range	= 0x08,
141		.has_ttl_io	= 1,
142		.n_subdevs	= 4,
143		.n_iso_out_chan	= 16,
144		.n_iso_in_chan	= 16,
145		.n_ttl_chan	= 16,
146	}, {
147		.name		= "pcm3730",
148		.io_range	= 0x04,
149		.has_ttl_io	= 1,
150		.n_subdevs	= 4,
151		.n_iso_out_chan	= 8,
152		.n_iso_in_chan	= 8,
153		.n_ttl_chan	= 16,
154	}, {
155		.name		= "pcl725",
156		.io_range	= 0x02,
157		.is_pcl725	= 1,
158		.n_subdevs	= 2,
159		.n_iso_out_chan	= 8,
160		.n_iso_in_chan	= 8,
161	}, {
162		.name		= "p8r8dio",
163		.io_range	= 0x02,
164		.is_pcl725	= 1,
165		.has_readback	= 1,
166		.n_subdevs	= 2,
167		.n_iso_out_chan	= 8,
168		.n_iso_in_chan	= 8,
169	}, {
170		.name		= "acl7225b",
171		.io_range	= 0x08,		/* only 4 are used */
172		.is_acl7225b	= 1,
173		.has_readback	= 1,
174		.n_subdevs	= 2,
175		.n_iso_out_chan	= 16,
176		.n_iso_in_chan	= 16,
177	}, {
178		.name		= "p16r16dio",
179		.io_range	= 0x04,
180		.is_acl7225b	= 1,
181		.has_readback	= 1,
182		.n_subdevs	= 2,
183		.n_iso_out_chan	= 16,
184		.n_iso_in_chan	= 16,
185	}, {
186		.name		= "pcl733",
187		.io_range	= 0x04,
188		.n_subdevs	= 1,
189		.n_iso_in_chan	= 32,
190	}, {
191		.name		= "pcl734",
192		.io_range	= 0x04,
193		.n_subdevs	= 1,
194		.n_iso_out_chan	= 32,
195	}, {
196		.name		= "opmm-1616-xt",
197		.io_range	= 0x10,
198		.is_acl7225b	= 1,
199		.has_readback	= 1,
200		.n_subdevs	= 2,
201		.n_iso_out_chan	= 16,
202		.n_iso_in_chan	= 16,
203	}, {
204		.name		= "pearl-mm-p",
205		.io_range	= 0x02,
206		.n_subdevs	= 1,
207		.n_iso_out_chan	= 16,
208	}, {
209		.name		= "ir104-pbf",
210		.io_range	= 0x08,
211		.is_ir104	= 1,
212		.has_readback	= 1,
213		.n_iso_out_chan	= 20,
214		.n_iso_in_chan	= 20,
215	},
216};
217
218static int pcl730_do_insn_bits(struct comedi_device *dev,
219			       struct comedi_subdevice *s,
220			       struct comedi_insn *insn,
221			       unsigned int *data)
222{
223	unsigned long reg = (unsigned long)s->private;
224	unsigned int mask;
225
226	mask = comedi_dio_update_state(s, data);
227	if (mask) {
228		if (mask & 0x00ff)
229			outb(s->state & 0xff, dev->iobase + reg);
230		if ((mask & 0xff00) && (s->n_chan > 8))
231			outb((s->state >> 8) & 0xff, dev->iobase + reg + 1);
232		if ((mask & 0xff0000) && (s->n_chan > 16))
233			outb((s->state >> 16) & 0xff, dev->iobase + reg + 2);
234		if ((mask & 0xff000000) && (s->n_chan > 24))
235			outb((s->state >> 24) & 0xff, dev->iobase + reg + 3);
236	}
237
238	data[1] = s->state;
239
240	return insn->n;
241}
242
243static unsigned int pcl730_get_bits(struct comedi_device *dev,
244				    struct comedi_subdevice *s)
245{
246	unsigned long reg = (unsigned long)s->private;
247	unsigned int val;
248
249	val = inb(dev->iobase + reg);
250	if (s->n_chan > 8)
251		val |= (inb(dev->iobase + reg + 1) << 8);
252	if (s->n_chan > 16)
253		val |= (inb(dev->iobase + reg + 2) << 16);
254	if (s->n_chan > 24)
255		val |= (inb(dev->iobase + reg + 3) << 24);
256
257	return val;
258}
259
260static int pcl730_di_insn_bits(struct comedi_device *dev,
261			       struct comedi_subdevice *s,
262			       struct comedi_insn *insn,
263			       unsigned int *data)
264{
265	data[1] = pcl730_get_bits(dev, s);
266
267	return insn->n;
268}
269
270static int pcl730_attach(struct comedi_device *dev,
271			 struct comedi_devconfig *it)
272{
273	const struct pcl730_board *board = dev->board_ptr;
274	struct comedi_subdevice *s;
275	int subdev;
276	int ret;
277
278	ret = comedi_request_region(dev, it->options[0], board->io_range);
279	if (ret)
280		return ret;
281
282	ret = comedi_alloc_subdevices(dev, board->n_subdevs);
283	if (ret)
284		return ret;
285
286	subdev = 0;
287
288	if (board->n_iso_out_chan) {
289		/* Isolated Digital Outputs */
290		s = &dev->subdevices[subdev++];
291		s->type		= COMEDI_SUBD_DO;
292		s->subdev_flags	= SDF_WRITABLE;
293		s->n_chan	= board->n_iso_out_chan;
294		s->maxdata	= 1;
295		s->range_table	= &range_digital;
296		s->insn_bits	= pcl730_do_insn_bits;
297		s->private	= (void *)0;
298
299		/* get the initial state if supported */
300		if (board->has_readback)
301			s->state = pcl730_get_bits(dev, s);
302	}
303
304	if (board->n_iso_in_chan) {
305		/* Isolated Digital Inputs */
306		s = &dev->subdevices[subdev++];
307		s->type		= COMEDI_SUBD_DI;
308		s->subdev_flags	= SDF_READABLE;
309		s->n_chan	= board->n_iso_in_chan;
310		s->maxdata	= 1;
311		s->range_table	= &range_digital;
312		s->insn_bits	= pcl730_di_insn_bits;
313		s->private	= board->is_ir104 ? (void *)4 :
314				  board->is_acl7225b ? (void *)2 :
315				  board->is_pcl725 ? (void *)1 : (void *)0;
316	}
317
318	if (board->has_ttl_io) {
319		/* TTL Digital Outputs */
320		s = &dev->subdevices[subdev++];
321		s->type		= COMEDI_SUBD_DO;
322		s->subdev_flags	= SDF_WRITABLE;
323		s->n_chan	= board->n_ttl_chan;
324		s->maxdata	= 1;
325		s->range_table	= &range_digital;
326		s->insn_bits	= pcl730_do_insn_bits;
327		s->private	= (void *)2;
328
329		/* TTL Digital Inputs */
330		s = &dev->subdevices[subdev++];
331		s->type		= COMEDI_SUBD_DI;
332		s->subdev_flags	= SDF_READABLE;
333		s->n_chan	= board->n_ttl_chan;
334		s->maxdata	= 1;
335		s->range_table	= &range_digital;
336		s->insn_bits	= pcl730_di_insn_bits;
337		s->private	= (void *)2;
338	}
339
340	return 0;
341}
342
343static struct comedi_driver pcl730_driver = {
344	.driver_name	= "pcl730",
345	.module		= THIS_MODULE,
346	.attach		= pcl730_attach,
347	.detach		= comedi_legacy_detach,
348	.board_name	= &pcl730_boards[0].name,
349	.num_names	= ARRAY_SIZE(pcl730_boards),
350	.offset		= sizeof(struct pcl730_board),
351};
352module_comedi_driver(pcl730_driver);
353
354MODULE_AUTHOR("Comedi http://www.comedi.org");
355MODULE_DESCRIPTION("Comedi low-level driver");
356MODULE_LICENSE("GPL");
357