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