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 | ||
71b5f4f1 BP |
65 | static int pcm3724_attach(struct comedi_device * dev, comedi_devconfig * it); |
66 | static int pcm3724_detach(struct comedi_device * dev); | |
e30ec011 DC |
67 | |
68 | typedef struct { | |
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 | |
74 | } boardtype; | |
75 | ||
76 | //used to track configured dios | |
77 | typedef struct { | |
78 | int dio_1; | |
79 | int dio_2; | |
80 | } priv_pcm3724; | |
81 | static const boardtype boardtypes[] = { | |
82 | {"pcm3724", 48, 2, 0x00fc, PCM3724_SIZE,}, | |
83 | }; | |
84 | ||
85 | #define n_boardtypes (sizeof(boardtypes)/sizeof(boardtype)) | |
86 | #define this_board ((const boardtype *)dev->board_ptr) | |
87 | ||
139dfbdf | 88 | static struct comedi_driver driver_pcm3724 = { |
e30ec011 DC |
89 | driver_name:"pcm3724", |
90 | module:THIS_MODULE, | |
91 | attach:pcm3724_attach, | |
92 | detach:pcm3724_detach, | |
93 | board_name:&boardtypes[0].name, | |
94 | num_names:n_boardtypes, | |
95 | offset:sizeof(boardtype), | |
96 | }; | |
97 | ||
98 | COMEDI_INITCLEANUP(driver_pcm3724); | |
99 | ||
100 | // (setq c-basic-offset 8) | |
101 | ||
102 | static int subdev_8255_cb(int dir, int port, int data, unsigned long arg) | |
103 | { | |
104 | unsigned long iobase = arg; | |
105 | unsigned char inbres; | |
106 | //printk("8255cb %d %d %d %lx\n", dir,port,data,arg); | |
107 | if (dir) { | |
108 | //printk("8255 cb outb(%x, %lx)\n", data, iobase+port); | |
109 | outb(data, iobase + port); | |
110 | return 0; | |
111 | } else { | |
112 | inbres = inb(iobase + port); | |
113 | //printk("8255 cb inb(%lx) = %x\n", iobase+port, inbres); | |
114 | return inbres; | |
115 | } | |
116 | } | |
117 | ||
34c43922 | 118 | static int compute_buffer(int config, int devno, struct comedi_subdevice * s) |
e30ec011 DC |
119 | { |
120 | /* 1 in io_bits indicates output */ | |
121 | if (s->io_bits & 0x0000ff) { | |
122 | if (devno == 0) { | |
123 | config |= BUF_A0; | |
124 | } else { | |
125 | config |= BUF_A1; | |
126 | } | |
127 | } | |
128 | if (s->io_bits & 0x00ff00) { | |
129 | if (devno == 0) { | |
130 | config |= BUF_B0; | |
131 | } else { | |
132 | config |= BUF_B1; | |
133 | } | |
134 | } | |
135 | if (s->io_bits & 0xff0000) { | |
136 | if (devno == 0) { | |
137 | config |= BUF_C0; | |
138 | } else { | |
139 | config |= BUF_C1; | |
140 | } | |
141 | } | |
142 | return config; | |
143 | } | |
144 | ||
34c43922 | 145 | static void do_3724_config(struct comedi_device * dev, struct comedi_subdevice * s, |
e30ec011 DC |
146 | int chanspec) |
147 | { | |
148 | int config; | |
149 | int buffer_config; | |
150 | unsigned long port_8255_cfg; | |
151 | ||
152 | config = CR_CW; | |
153 | buffer_config = 0; | |
154 | ||
155 | /* 1 in io_bits indicates output, 1 in config indicates input */ | |
156 | if (!(s->io_bits & 0x0000ff)) { | |
157 | config |= CR_A_IO; | |
158 | } | |
159 | if (!(s->io_bits & 0x00ff00)) { | |
160 | config |= CR_B_IO; | |
161 | } | |
162 | if (!(s->io_bits & 0xff0000)) { | |
163 | config |= CR_C_IO; | |
164 | } | |
165 | ||
166 | buffer_config = compute_buffer(0, 0, dev->subdevices); | |
167 | buffer_config = compute_buffer(buffer_config, 1, (dev->subdevices) + 1); | |
168 | ||
169 | if (s == dev->subdevices) { | |
170 | port_8255_cfg = dev->iobase + _8255_CR; | |
171 | } else { | |
172 | port_8255_cfg = dev->iobase + SIZE_8255 + _8255_CR; | |
173 | } | |
174 | outb(buffer_config, dev->iobase + 8); /* update buffer register */ | |
175 | //printk("pcm3724 buffer_config (%lx) %d, %x\n", dev->iobase + _8255_CR, chanspec, buffer_config); | |
176 | outb(config, port_8255_cfg); | |
177 | } | |
178 | ||
34c43922 | 179 | static void enable_chan(struct comedi_device * dev, struct comedi_subdevice * s, int chanspec) |
e30ec011 DC |
180 | { |
181 | unsigned int mask; | |
182 | int gatecfg; | |
183 | priv_pcm3724 *priv; | |
184 | ||
185 | gatecfg = 0; | |
186 | priv = (priv_pcm3724 *) (dev->private); | |
187 | ||
188 | mask = 1 << CR_CHAN(chanspec); | |
189 | if (s == dev->subdevices) { // subdev 0 | |
190 | priv->dio_1 |= mask; | |
191 | } else { //subdev 1 | |
192 | priv->dio_2 |= mask; | |
193 | } | |
194 | if (priv->dio_1 & 0xff0000) { | |
195 | gatecfg |= GATE_C0; | |
196 | } | |
197 | if (priv->dio_1 & 0xff00) { | |
198 | gatecfg |= GATE_B0; | |
199 | } | |
200 | if (priv->dio_1 & 0xff) { | |
201 | gatecfg |= GATE_A0; | |
202 | } | |
203 | if (priv->dio_2 & 0xff0000) { | |
204 | gatecfg |= GATE_C1; | |
205 | } | |
206 | if (priv->dio_2 & 0xff00) { | |
207 | gatecfg |= GATE_B1; | |
208 | } | |
209 | if (priv->dio_2 & 0xff) { | |
210 | gatecfg |= GATE_A1; | |
211 | } | |
212 | // printk("gate control %x\n", gatecfg); | |
213 | outb(gatecfg, dev->iobase + 9); | |
214 | } | |
215 | ||
216 | /* overriding the 8255 insn config */ | |
34c43922 | 217 | static int subdev_3724_insn_config(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 218 | struct comedi_insn * insn, unsigned int * data) |
e30ec011 DC |
219 | { |
220 | unsigned int mask; | |
221 | unsigned int bits; | |
222 | ||
223 | mask = 1 << CR_CHAN(insn->chanspec); | |
224 | if (mask & 0x0000ff) { | |
225 | bits = 0x0000ff; | |
226 | } else if (mask & 0x00ff00) { | |
227 | bits = 0x00ff00; | |
228 | } else if (mask & 0x0f0000) { | |
229 | bits = 0x0f0000; | |
230 | } else { | |
231 | bits = 0xf00000; | |
232 | } | |
233 | ||
234 | switch (data[0]) { | |
235 | case INSN_CONFIG_DIO_INPUT: | |
236 | s->io_bits &= ~bits; | |
237 | break; | |
238 | case INSN_CONFIG_DIO_OUTPUT: | |
239 | s->io_bits |= bits; | |
240 | break; | |
241 | case INSN_CONFIG_DIO_QUERY: | |
242 | data[1] = (s->io_bits & bits) ? COMEDI_OUTPUT : COMEDI_INPUT; | |
243 | return insn->n; | |
244 | break; | |
245 | default: | |
246 | return -EINVAL; | |
247 | } | |
248 | ||
249 | do_3724_config(dev, s, insn->chanspec); | |
250 | enable_chan(dev, s, insn->chanspec); | |
251 | return 1; | |
252 | } | |
253 | ||
71b5f4f1 | 254 | static int pcm3724_attach(struct comedi_device * dev, comedi_devconfig * it) |
e30ec011 DC |
255 | { |
256 | unsigned long iobase; | |
257 | unsigned int iorange; | |
258 | int ret, i, n_subdevices; | |
259 | ||
260 | iobase = it->options[0]; | |
261 | iorange = this_board->io_range; | |
262 | if ((ret = alloc_private(dev, sizeof(priv_pcm3724))) < 0) | |
263 | return -ENOMEM; | |
264 | ||
265 | ((priv_pcm3724 *) (dev->private))->dio_1 = 0; | |
266 | ((priv_pcm3724 *) (dev->private))->dio_2 = 0; | |
267 | ||
268 | printk("comedi%d: pcm3724: board=%s, 0x%03lx ", dev->minor, | |
269 | this_board->name, iobase); | |
270 | if (!iobase || !request_region(iobase, iorange, "pcm3724")) { | |
271 | printk("I/O port conflict\n"); | |
272 | return -EIO; | |
273 | } | |
274 | ||
275 | dev->iobase = iobase; | |
276 | dev->board_name = this_board->name; | |
277 | printk("\n"); | |
278 | ||
279 | n_subdevices = this_board->numofports; | |
280 | ||
281 | if ((ret = alloc_subdevices(dev, n_subdevices)) < 0) | |
282 | return ret; | |
283 | ||
284 | for (i = 0; i < dev->n_subdevices; i++) { | |
285 | subdev_8255_init(dev, dev->subdevices + i, subdev_8255_cb, | |
286 | (unsigned long)(dev->iobase + SIZE_8255 * i)); | |
287 | ((dev->subdevices) + i)->insn_config = subdev_3724_insn_config; | |
288 | }; | |
289 | return 0; | |
290 | } | |
291 | ||
71b5f4f1 | 292 | static int pcm3724_detach(struct comedi_device * dev) |
e30ec011 DC |
293 | { |
294 | int i; | |
295 | ||
296 | if (dev->subdevices) { | |
297 | for (i = 0; i < dev->n_subdevices; i++) { | |
298 | subdev_8255_cleanup(dev, dev->subdevices + i); | |
299 | } | |
300 | } | |
301 | if (dev->iobase) { | |
302 | release_region(dev->iobase, this_board->io_range); | |
303 | } | |
304 | ||
305 | return 0; | |
306 | } |