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