pcl724.c revision 68c3dbff9fc9f25872408d0e95980d41733d48d0
1/*
2    comedi/drivers/pcl724.c
3
4    Michal Dobes <dobes@tesnet.cz>
5
6    hardware driver for Advantech cards:
7     card:   PCL-724, PCL-722, PCL-731
8     driver: pcl724,  pcl722,  pcl731
9    and ADLink cards:
10     card:   ACL-7122, ACL-7124, PET-48DIO
11     driver: acl7122,  acl7124,  pet48dio
12
13    Options for PCL-724, PCL-731, ACL-7124 and PET-48DIO:
14     [0] - IO Base
15
16    Options for PCL-722 and ACL-7122:
17     [0] - IO Base
18     [1] - IRQ (0=disable IRQ) IRQ isn't supported at this time!
19     [2] -number of DIO:
20              0, 144: 144 DIO configuration
21	      1,  96:  96 DIO configuration
22*/
23/*
24Driver: pcl724
25Description: Advantech PCL-724, PCL-722, PCL-731 ADLink ACL-7122, ACL-7124,
26  PET-48DIO
27Author: Michal Dobes <dobes@tesnet.cz>
28Devices: [Advantech] PCL-724 (pcl724), PCL-722 (pcl722), PCL-731 (pcl731),
29  [ADLink] ACL-7122 (acl7122), ACL-7124 (acl7124), PET-48DIO (pet48dio)
30Status: untested
31
32This is driver for digital I/O boards PCL-722/724/731 with 144/24/48 DIO
33and for digital I/O boards ACL-7122/7124/PET-48DIO with 144/24/48 DIO.
34It need 8255.o for operations and only immediate mode is supported.
35See the source for configuration details.
36*/
37/*
38 * check_driver overrides:
39 *   struct comedi_insn
40 */
41
42#include "../comedidev.h"
43
44#include <linux/ioport.h>
45#include <linux/delay.h>
46
47#include "8255.h"
48
49#define PCL722_SIZE    32
50#define PCL722_96_SIZE 16
51#define PCL724_SIZE     4
52#define PCL731_SIZE     8
53#define PET48_SIZE      2
54
55#define SIZE_8255	4
56
57/* #define PCL724_IRQ   1  no IRQ support now */
58
59static int pcl724_attach(struct comedi_device *dev, struct comedi_devconfig *it);
60static int pcl724_detach(struct comedi_device *dev);
61
62struct pcl724_board {
63
64	const char *name;	/*  board name */
65	int dio;		/*  num of DIO */
66	int numofports;		/*  num of 8255 subdevices */
67	unsigned int IRQbits;	/*  allowed interrupts */
68	unsigned int io_range;	/*  len of IO space */
69	char can_have96;
70	char is_pet48;
71};
72
73
74static const struct pcl724_board boardtypes[] = {
75	{"pcl724", 24, 1, 0x00fc, PCL724_SIZE, 0, 0,},
76	{"pcl722", 144, 6, 0x00fc, PCL722_SIZE, 1, 0,},
77	{"pcl731", 48, 2, 0x9cfc, PCL731_SIZE, 0, 0,},
78	{"acl7122", 144, 6, 0x9ee8, PCL722_SIZE, 1, 0,},
79	{"acl7124", 24, 1, 0x00fc, PCL724_SIZE, 0, 0,},
80	{"pet48dio", 48, 2, 0x9eb8, PET48_SIZE, 0, 1,},
81};
82
83#define n_boardtypes (sizeof(boardtypes)/sizeof(struct pcl724_board))
84#define this_board ((const struct pcl724_board *)dev->board_ptr)
85
86static struct comedi_driver driver_pcl724 = {
87	.driver_name = "pcl724",
88	.module = THIS_MODULE,
89	.attach = pcl724_attach,
90	.detach = pcl724_detach,
91	.board_name = &boardtypes[0].name,
92	.num_names = n_boardtypes,
93	.offset = sizeof(struct pcl724_board),
94};
95
96COMEDI_INITCLEANUP(driver_pcl724);
97
98static int subdev_8255_cb(int dir, int port, int data, unsigned long arg)
99{
100	unsigned long iobase = arg;
101
102	if (dir) {
103		outb(data, iobase + port);
104		return 0;
105	} else {
106		return inb(iobase + port);
107	}
108}
109
110static int subdev_8255mapped_cb(int dir, int port, int data,
111	unsigned long iobase)
112{
113	int movport = SIZE_8255 * (iobase >> 12);
114
115	iobase &= 0x0fff;
116
117	if (dir) {
118		outb(port + movport, iobase);
119		outb(data, iobase + 1);
120		return 0;
121	} else {
122		outb(port + movport, iobase);
123		return inb(iobase + 1);
124	}
125}
126
127static int pcl724_attach(struct comedi_device *dev, struct comedi_devconfig *it)
128{
129	unsigned long iobase;
130	unsigned int iorange;
131	int ret, i, n_subdevices;
132#ifdef PCL724_IRQ
133	unsigned int irq;
134#endif
135
136	iobase = it->options[0];
137	iorange = this_board->io_range;
138	if ((this_board->can_have96) && ((it->options[1] == 1)
139			|| (it->options[1] == 96)))
140		iorange = PCL722_96_SIZE;	/*  PCL-724 in 96 DIO configuration */
141	printk("comedi%d: pcl724: board=%s, 0x%03lx ", dev->minor,
142		this_board->name, iobase);
143	if (!request_region(iobase, iorange, "pcl724")) {
144		printk("I/O port conflict\n");
145		return -EIO;
146	}
147
148	dev->iobase = iobase;
149
150	dev->board_name = this_board->name;
151
152#ifdef PCL724_IRQ
153	irq = 0;
154	if (this_board->IRQbits != 0) {	/* board support IRQ */
155		irq = it->options[1];
156		if (irq) {	/* we want to use IRQ */
157			if (((1 << irq) & this_board->IRQbits) == 0) {
158				rt_printk
159					(", IRQ %u is out of allowed range, DISABLING IT",
160					irq);
161				irq = 0;	/* Bad IRQ */
162			} else {
163				if (comedi_request_irq(irq, interrupt_pcl724, 0,
164						"pcl724", dev)) {
165					rt_printk
166						(", unable to allocate IRQ %u, DISABLING IT",
167						irq);
168					irq = 0;	/* Can't use IRQ */
169				} else {
170					rt_printk(", irq=%u", irq);
171				}
172			}
173		}
174	}
175
176	dev->irq = irq;
177#endif
178
179	printk("\n");
180
181	n_subdevices = this_board->numofports;
182	if ((this_board->can_have96) && ((it->options[1] == 1)
183			|| (it->options[1] == 96)))
184		n_subdevices = 4;	/*  PCL-724 in 96 DIO configuration */
185
186	ret = alloc_subdevices(dev, n_subdevices);
187	if (ret < 0)
188		return ret;
189
190	for (i = 0; i < dev->n_subdevices; i++) {
191		if (this_board->is_pet48) {
192			subdev_8255_init(dev, dev->subdevices + i,
193				subdev_8255mapped_cb,
194				(unsigned long)(dev->iobase + i * 0x1000));
195		} else
196			subdev_8255_init(dev, dev->subdevices + i,
197				subdev_8255_cb,
198				(unsigned long)(dev->iobase + SIZE_8255 * i));
199	};
200
201	return 0;
202}
203
204static int pcl724_detach(struct comedi_device *dev)
205{
206	int i;
207
208/* printk("comedi%d: pcl724: remove\n",dev->minor); */
209
210	for (i = 0; i < dev->n_subdevices; i++) {
211		subdev_8255_cleanup(dev, dev->subdevices + i);
212	}
213
214#ifdef PCL724_IRQ
215	if (dev->irq) {
216		comedi_free_irq(dev->irq, dev);
217	}
218#endif
219
220	release_region(dev->iobase, this_board->io_range);
221
222	return 0;
223}
224