Commit | Line | Data |
---|---|---|
e30ec011 DC |
1 | /* |
2 | comedi/drivers/pcm724.c | |
3 | ||
4 | Drew Csillag <drew_csillag@yahoo.com> | |
5 | ||
6 | hardware driver for Advantech card: | |
7 | card: PCM-3724 | |
8 | driver: pcm3724 | |
9 | ||
10 | Options for PCM-3724 | |
11 | [0] - IO Base | |
12 | */ | |
13 | /* | |
14 | Driver: pcm3724 | |
15 | Description: Advantech PCM-3724 | |
16 | Author: Drew Csillag <drew_csillag@yahoo.com> | |
17 | Devices: [Advantech] PCM-3724 (pcm724) | |
18 | Status: tested | |
19 | ||
20 | This is driver for digital I/O boards PCM-3724 with 48 DIO. | |
21 | It needs 8255.o for operations and only immediate mode is supported. | |
22 | See the source for configuration details. | |
23 | ||
24 | Copy/pasted/hacked from pcm724.c | |
25 | */ | |
26 | /* | |
27 | * check_driver overrides: | |
90035c08 | 28 | * struct comedi_insn |
e30ec011 DC |
29 | */ |
30 | ||
31 | #include "../comedidev.h" | |
32 | ||
33 | #include <linux/ioport.h> | |
34 | #include <linux/delay.h> | |
35 | ||
36 | #include "8255.h" | |
37 | ||
38 | #define PCM3724_SIZE 16 | |
39 | #define SIZE_8255 4 | |
40 | ||
41 | #define BUF_C0 0x1 | |
42 | #define BUF_B0 0x2 | |
43 | #define BUF_A0 0x4 | |
44 | #define BUF_C1 0x8 | |
45 | #define BUF_B1 0x10 | |
46 | #define BUF_A1 0x20 | |
47 | ||
48 | #define GATE_A0 0x4 | |
49 | #define GATE_B0 0x2 | |
50 | #define GATE_C0 0x1 | |
51 | #define GATE_A1 0x20 | |
52 | #define GATE_B1 0x10 | |
53 | #define GATE_C1 0x8 | |
54 | ||
55 | /* from 8255.c */ | |
56 | #define CR_CW 0x80 | |
57 | #define _8255_CR 3 | |
58 | #define CR_B_IO 0x02 | |
59 | #define CR_B_MODE 0x04 | |
60 | #define CR_C_IO 0x09 | |
61 | #define CR_A_IO 0x10 | |
62 | #define CR_A_MODE(a) ((a)<<5) | |
63 | #define CR_CW 0x80 | |
64 | ||
da91b269 BP |
65 | static int pcm3724_attach(struct comedi_device *dev, struct comedi_devconfig *it); |
66 | static int pcm3724_detach(struct comedi_device *dev); | |
e30ec011 | 67 | |
180089a8 | 68 | struct pcm3724_board { |
2696fb57 BP |
69 | const char *name; /* driver name */ |
70 | int dio; /* num of DIO */ | |
71 | int numofports; /* num of 8255 subdevices */ | |
72 | unsigned int IRQbits; /* allowed interrupts */ | |
73 | unsigned int io_range; /* len of IO space */ | |
180089a8 | 74 | }; |
e30ec011 | 75 | |
2696fb57 | 76 | /* used to track configured dios */ |
8e7b864b | 77 | struct priv_pcm3724 { |
e30ec011 DC |
78 | int dio_1; |
79 | int dio_2; | |
8e7b864b BP |
80 | }; |
81 | ||
180089a8 | 82 | static const struct pcm3724_board boardtypes[] = { |
e30ec011 DC |
83 | {"pcm3724", 48, 2, 0x00fc, PCM3724_SIZE,}, |
84 | }; | |
85 | ||
180089a8 BP |
86 | #define n_boardtypes (sizeof(boardtypes)/sizeof(struct pcm3724_board)) |
87 | #define this_board ((const struct pcm3724_board *)dev->board_ptr) | |
e30ec011 | 88 | |
139dfbdf | 89 | static struct comedi_driver driver_pcm3724 = { |
68c3dbff BP |
90 | .driver_name = "pcm3724", |
91 | .module = THIS_MODULE, | |
92 | .attach = pcm3724_attach, | |
93 | .detach = pcm3724_detach, | |
94 | .board_name = &boardtypes[0].name, | |
95 | .num_names = n_boardtypes, | |
96 | .offset = sizeof(struct pcm3724_board), | |
e30ec011 DC |
97 | }; |
98 | ||
99 | COMEDI_INITCLEANUP(driver_pcm3724); | |
100 | ||
2696fb57 | 101 | /* (setq c-basic-offset 8) */ |
e30ec011 DC |
102 | |
103 | static int subdev_8255_cb(int dir, int port, int data, unsigned long arg) | |
104 | { | |
105 | unsigned long iobase = arg; | |
106 | unsigned char inbres; | |
2696fb57 | 107 | /* printk("8255cb %d %d %d %lx\n", dir,port,data,arg); */ |
e30ec011 | 108 | if (dir) { |
2696fb57 | 109 | /* printk("8255 cb outb(%x, %lx)\n", data, iobase+port); */ |
e30ec011 DC |
110 | outb(data, iobase + port); |
111 | return 0; | |
112 | } else { | |
113 | inbres = inb(iobase + port); | |
2696fb57 | 114 | /* printk("8255 cb inb(%lx) = %x\n", iobase+port, inbres); */ |
e30ec011 DC |
115 | return inbres; |
116 | } | |
117 | } | |
118 | ||
da91b269 | 119 | static int compute_buffer(int config, int devno, struct comedi_subdevice *s) |
e30ec011 DC |
120 | { |
121 | /* 1 in io_bits indicates output */ | |
122 | if (s->io_bits & 0x0000ff) { | |
123 | if (devno == 0) { | |
124 | config |= BUF_A0; | |
125 | } else { | |
126 | config |= BUF_A1; | |
127 | } | |
128 | } | |
129 | if (s->io_bits & 0x00ff00) { | |
130 | if (devno == 0) { | |
131 | config |= BUF_B0; | |
132 | } else { | |
133 | config |= BUF_B1; | |
134 | } | |
135 | } | |
136 | if (s->io_bits & 0xff0000) { | |
137 | if (devno == 0) { | |
138 | config |= BUF_C0; | |
139 | } else { | |
140 | config |= BUF_C1; | |
141 | } | |
142 | } | |
143 | return config; | |
144 | } | |
145 | ||
da91b269 | 146 | static void do_3724_config(struct comedi_device *dev, struct comedi_subdevice *s, |
e30ec011 DC |
147 | int chanspec) |
148 | { | |
149 | int config; | |
150 | int buffer_config; | |
151 | unsigned long port_8255_cfg; | |
152 | ||
153 | config = CR_CW; | |
154 | buffer_config = 0; | |
155 | ||
156 | /* 1 in io_bits indicates output, 1 in config indicates input */ | |
157 | if (!(s->io_bits & 0x0000ff)) { | |
158 | config |= CR_A_IO; | |
159 | } | |
160 | if (!(s->io_bits & 0x00ff00)) { | |
161 | config |= CR_B_IO; | |
162 | } | |
163 | if (!(s->io_bits & 0xff0000)) { | |
164 | config |= CR_C_IO; | |
165 | } | |
166 | ||
167 | buffer_config = compute_buffer(0, 0, dev->subdevices); | |
168 | buffer_config = compute_buffer(buffer_config, 1, (dev->subdevices) + 1); | |
169 | ||
170 | if (s == dev->subdevices) { | |
171 | port_8255_cfg = dev->iobase + _8255_CR; | |
172 | } else { | |
173 | port_8255_cfg = dev->iobase + SIZE_8255 + _8255_CR; | |
174 | } | |
175 | outb(buffer_config, dev->iobase + 8); /* update buffer register */ | |
2696fb57 | 176 | /* printk("pcm3724 buffer_config (%lx) %d, %x\n", dev->iobase + _8255_CR, chanspec, buffer_config); */ |
e30ec011 DC |
177 | outb(config, port_8255_cfg); |
178 | } | |
179 | ||
da91b269 | 180 | static void enable_chan(struct comedi_device *dev, struct comedi_subdevice *s, int chanspec) |
e30ec011 DC |
181 | { |
182 | unsigned int mask; | |
183 | int gatecfg; | |
8e7b864b | 184 | struct priv_pcm3724 *priv; |
e30ec011 DC |
185 | |
186 | gatecfg = 0; | |
8e7b864b | 187 | priv = (struct priv_pcm3724 *) (dev->private); |
e30ec011 DC |
188 | |
189 | mask = 1 << CR_CHAN(chanspec); | |
2696fb57 | 190 | if (s == dev->subdevices) { /* subdev 0 */ |
e30ec011 | 191 | priv->dio_1 |= mask; |
2696fb57 | 192 | } else { /* subdev 1 */ |
e30ec011 DC |
193 | priv->dio_2 |= mask; |
194 | } | |
195 | if (priv->dio_1 & 0xff0000) { | |
196 | gatecfg |= GATE_C0; | |
197 | } | |
198 | if (priv->dio_1 & 0xff00) { | |
199 | gatecfg |= GATE_B0; | |
200 | } | |
201 | if (priv->dio_1 & 0xff) { | |
202 | gatecfg |= GATE_A0; | |
203 | } | |
204 | if (priv->dio_2 & 0xff0000) { | |
205 | gatecfg |= GATE_C1; | |
206 | } | |
207 | if (priv->dio_2 & 0xff00) { | |
208 | gatecfg |= GATE_B1; | |
209 | } | |
210 | if (priv->dio_2 & 0xff) { | |
211 | gatecfg |= GATE_A1; | |
212 | } | |
2696fb57 | 213 | /* printk("gate control %x\n", gatecfg); */ |
e30ec011 DC |
214 | outb(gatecfg, dev->iobase + 9); |
215 | } | |
216 | ||
217 | /* overriding the 8255 insn config */ | |
da91b269 BP |
218 | static int subdev_3724_insn_config(struct comedi_device *dev, struct comedi_subdevice *s, |
219 | struct comedi_insn *insn, unsigned int *data) | |
e30ec011 DC |
220 | { |
221 | unsigned int mask; | |
222 | unsigned int bits; | |
223 | ||
224 | mask = 1 << CR_CHAN(insn->chanspec); | |
225 | if (mask & 0x0000ff) { | |
226 | bits = 0x0000ff; | |
227 | } else if (mask & 0x00ff00) { | |
228 | bits = 0x00ff00; | |
229 | } else if (mask & 0x0f0000) { | |
230 | bits = 0x0f0000; | |
231 | } else { | |
232 | bits = 0xf00000; | |
233 | } | |
234 | ||
235 | switch (data[0]) { | |
236 | case INSN_CONFIG_DIO_INPUT: | |
237 | s->io_bits &= ~bits; | |
238 | break; | |
239 | case INSN_CONFIG_DIO_OUTPUT: | |
240 | s->io_bits |= bits; | |
241 | break; | |
242 | case INSN_CONFIG_DIO_QUERY: | |
243 | data[1] = (s->io_bits & bits) ? COMEDI_OUTPUT : COMEDI_INPUT; | |
244 | return insn->n; | |
245 | break; | |
246 | default: | |
247 | return -EINVAL; | |
248 | } | |
249 | ||
250 | do_3724_config(dev, s, insn->chanspec); | |
251 | enable_chan(dev, s, insn->chanspec); | |
252 | return 1; | |
253 | } | |
254 | ||
da91b269 | 255 | static int pcm3724_attach(struct comedi_device *dev, struct comedi_devconfig *it) |
e30ec011 DC |
256 | { |
257 | unsigned long iobase; | |
258 | unsigned int iorange; | |
259 | int ret, i, n_subdevices; | |
260 | ||
261 | iobase = it->options[0]; | |
262 | iorange = this_board->io_range; | |
c3744138 BP |
263 | |
264 | ret = alloc_private(dev, sizeof(struct priv_pcm3724)); | |
265 | if (ret < 0) | |
e30ec011 DC |
266 | return -ENOMEM; |
267 | ||
8e7b864b BP |
268 | ((struct priv_pcm3724 *) (dev->private))->dio_1 = 0; |
269 | ((struct priv_pcm3724 *) (dev->private))->dio_2 = 0; | |
e30ec011 DC |
270 | |
271 | printk("comedi%d: pcm3724: board=%s, 0x%03lx ", dev->minor, | |
272 | this_board->name, iobase); | |
273 | if (!iobase || !request_region(iobase, iorange, "pcm3724")) { | |
274 | printk("I/O port conflict\n"); | |
275 | return -EIO; | |
276 | } | |
277 | ||
278 | dev->iobase = iobase; | |
279 | dev->board_name = this_board->name; | |
280 | printk("\n"); | |
281 | ||
282 | n_subdevices = this_board->numofports; | |
283 | ||
c3744138 BP |
284 | ret = alloc_subdevices(dev, n_subdevices); |
285 | if (ret < 0) | |
e30ec011 DC |
286 | return ret; |
287 | ||
288 | for (i = 0; i < dev->n_subdevices; i++) { | |
289 | subdev_8255_init(dev, dev->subdevices + i, subdev_8255_cb, | |
290 | (unsigned long)(dev->iobase + SIZE_8255 * i)); | |
291 | ((dev->subdevices) + i)->insn_config = subdev_3724_insn_config; | |
292 | }; | |
293 | return 0; | |
294 | } | |
295 | ||
da91b269 | 296 | static int pcm3724_detach(struct comedi_device *dev) |
e30ec011 DC |
297 | { |
298 | int i; | |
299 | ||
300 | if (dev->subdevices) { | |
301 | for (i = 0; i < dev->n_subdevices; i++) { | |
302 | subdev_8255_cleanup(dev, dev->subdevices + i); | |
303 | } | |
304 | } | |
305 | if (dev->iobase) { | |
306 | release_region(dev->iobase, this_board->io_range); | |
307 | } | |
308 | ||
309 | return 0; | |
310 | } |