10f488f0e5ee732f49f6ad319ad046aa5e637a4a
[safe/jmp/linux-2.6] / drivers / staging / comedi / drivers / 8255.c
1 /*
2     comedi/drivers/8255.c
3     Driver for 8255
4
5     COMEDI - Linux Control and Measurement Device Interface
6     Copyright (C) 1998 David A. Schleef <ds@schleef.org>
7
8     This program is free software; you can redistribute it and/or modify
9     it under the terms of the GNU General Public License as published by
10     the Free Software Foundation; either version 2 of the License, or
11     (at your option) any later version.
12
13     This program is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17
18     You should have received a copy of the GNU General Public License
19     along with this program; if not, write to the Free Software
20     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22 */
23 /*
24 Driver: 8255
25 Description: generic 8255 support
26 Devices: [standard] 8255 (8255)
27 Author: ds
28 Status: works
29 Updated: Fri,  7 Jun 2002 12:56:45 -0700
30
31 The classic in digital I/O.  The 8255 appears in Comedi as a single
32 digital I/O subdevice with 24 channels.  The channel 0 corresponds
33 to the 8255's port A, bit 0; channel 23 corresponds to port C, bit
34 7.  Direction configuration is done in blocks, with channels 0-7,
35 8-15, 16-19, and 20-23 making up the 4 blocks.  The only 8255 mode
36 supported is mode 0.
37
38 You should enable compilation this driver if you plan to use a board
39 that has an 8255 chip.  For multifunction boards, the main driver will
40 configure the 8255 subdevice automatically.
41
42 This driver also works independently with ISA and PCI cards that
43 directly map the 8255 registers to I/O ports, including cards with
44 multiple 8255 chips.  To configure the driver for such a card, the
45 option list should be a list of the I/O port bases for each of the
46 8255 chips.  For example,
47
48   comedi_config /dev/comedi0 8255 0x200,0x204,0x208,0x20c
49
50 Note that most PCI 8255 boards do NOT work with this driver, and
51 need a separate driver as a wrapper.  For those that do work, the
52 I/O port base address can be found in the output of 'lspci -v'.
53
54 */
55
56 /*
57    This file contains an exported subdevice for driving an 8255.
58
59    To use this subdevice as part of another driver, you need to
60    set up the subdevice in the attach function of the driver by
61    calling:
62
63      subdev_8255_init(device, subdevice, callback_function, arg)
64
65    device and subdevice are pointers to the device and subdevice
66    structures.  callback_function will be called to provide the
67    low-level input/output to the device, i.e., actual register
68    access.  callback_function will be called with the value of arg
69    as the last parameter.  If the 8255 device is mapped as 4
70    consecutive I/O ports, you can use NULL for callback_function
71    and the I/O port base for arg, and an internal function will
72    handle the register access.
73
74    In addition, if the main driver handles interrupts, you can
75    enable commands on the subdevice by calling subdev_8255_init_irq()
76    instead.  Then, when you get an interrupt that is likely to be
77    from the 8255, you should call subdev_8255_interrupt(), which
78    will copy the latched value to a Comedi buffer.
79  */
80
81 #include "../comedidev.h"
82
83 #include <linux/ioport.h>
84
85 #define _8255_SIZE 4
86
87 #define _8255_DATA 0
88 #define _8255_CR 3
89
90 #define CR_C_LO_IO      0x01
91 #define CR_B_IO         0x02
92 #define CR_B_MODE       0x04
93 #define CR_C_HI_IO      0x08
94 #define CR_A_IO         0x10
95 #define CR_A_MODE(a)    ((a)<<5)
96 #define CR_CW           0x80
97
98 struct subdev_8255_struct {
99         unsigned long cb_arg;
100         int (*cb_func) (int, int, int, unsigned long);
101         int have_irq;
102 };
103
104 #define CALLBACK_ARG    (((struct subdev_8255_struct *)s->private)->cb_arg)
105 #define CALLBACK_FUNC   (((struct subdev_8255_struct *)s->private)->cb_func)
106 #define subdevpriv      ((struct subdev_8255_struct *)s->private)
107
108 static int dev_8255_attach(struct comedi_device *dev,
109                            struct comedi_devconfig *it);
110 static int dev_8255_detach(struct comedi_device *dev);
111 static struct comedi_driver driver_8255 = {
112         .driver_name = "8255",
113         .module = THIS_MODULE,
114         .attach = dev_8255_attach,
115         .detach = dev_8255_detach,
116 };
117
118 COMEDI_INITCLEANUP(driver_8255);
119
120 static void do_config(struct comedi_device *dev, struct comedi_subdevice *s);
121
122 void subdev_8255_interrupt(struct comedi_device *dev,
123                            struct comedi_subdevice *s)
124 {
125         short d;
126
127         d = CALLBACK_FUNC(0, _8255_DATA, 0, CALLBACK_ARG);
128         d |= (CALLBACK_FUNC(0, _8255_DATA + 1, 0, CALLBACK_ARG) << 8);
129
130         comedi_buf_put(s->async, d);
131         s->async->events |= COMEDI_CB_EOS;
132
133         comedi_event(dev, s);
134 }
135 EXPORT_SYMBOL(subdev_8255_interrupt);
136
137 static int subdev_8255_cb(int dir, int port, int data, unsigned long arg)
138 {
139         unsigned long iobase = arg;
140
141         if (dir) {
142                 outb(data, iobase + port);
143                 return 0;
144         } else {
145                 return inb(iobase + port);
146         }
147 }
148
149 static int subdev_8255_insn(struct comedi_device *dev,
150                             struct comedi_subdevice *s,
151                             struct comedi_insn *insn, unsigned int *data)
152 {
153         if (data[0]) {
154                 s->state &= ~data[0];
155                 s->state |= (data[0] & data[1]);
156
157                 if (data[0] & 0xff)
158                         CALLBACK_FUNC(1, _8255_DATA, s->state & 0xff,
159                                       CALLBACK_ARG);
160                 if (data[0] & 0xff00)
161                         CALLBACK_FUNC(1, _8255_DATA + 1, (s->state >> 8) & 0xff,
162                                       CALLBACK_ARG);
163                 if (data[0] & 0xff0000)
164                         CALLBACK_FUNC(1, _8255_DATA + 2,
165                                       (s->state >> 16) & 0xff, CALLBACK_ARG);
166         }
167
168         data[1] = CALLBACK_FUNC(0, _8255_DATA, 0, CALLBACK_ARG);
169         data[1] |= (CALLBACK_FUNC(0, _8255_DATA + 1, 0, CALLBACK_ARG) << 8);
170         data[1] |= (CALLBACK_FUNC(0, _8255_DATA + 2, 0, CALLBACK_ARG) << 16);
171
172         return 2;
173 }
174
175 static int subdev_8255_insn_config(struct comedi_device *dev,
176                                    struct comedi_subdevice *s,
177                                    struct comedi_insn *insn, unsigned int *data)
178 {
179         unsigned int mask;
180         unsigned int bits;
181
182         mask = 1 << CR_CHAN(insn->chanspec);
183         if (mask & 0x0000ff)
184                 bits = 0x0000ff;
185         else if (mask & 0x00ff00)
186                 bits = 0x00ff00;
187         else if (mask & 0x0f0000)
188                 bits = 0x0f0000;
189         else
190                 bits = 0xf00000;
191
192         switch (data[0]) {
193         case INSN_CONFIG_DIO_INPUT:
194                 s->io_bits &= ~bits;
195                 break;
196         case INSN_CONFIG_DIO_OUTPUT:
197                 s->io_bits |= bits;
198                 break;
199         case INSN_CONFIG_DIO_QUERY:
200                 data[1] = (s->io_bits & bits) ? COMEDI_OUTPUT : COMEDI_INPUT;
201                 return insn->n;
202                 break;
203         default:
204                 return -EINVAL;
205         }
206
207         do_config(dev, s);
208
209         return 1;
210 }
211
212 static void do_config(struct comedi_device *dev, struct comedi_subdevice *s)
213 {
214         int config;
215
216         config = CR_CW;
217         /* 1 in io_bits indicates output, 1 in config indicates input */
218         if (!(s->io_bits & 0x0000ff))
219                 config |= CR_A_IO;
220         if (!(s->io_bits & 0x00ff00))
221                 config |= CR_B_IO;
222         if (!(s->io_bits & 0x0f0000))
223                 config |= CR_C_LO_IO;
224         if (!(s->io_bits & 0xf00000))
225                 config |= CR_C_HI_IO;
226         CALLBACK_FUNC(1, _8255_CR, config, CALLBACK_ARG);
227 }
228
229 static int subdev_8255_cmdtest(struct comedi_device *dev,
230                                struct comedi_subdevice *s,
231                                struct comedi_cmd *cmd)
232 {
233         int err = 0;
234         unsigned int tmp;
235
236         /* step 1 */
237
238         tmp = cmd->start_src;
239         cmd->start_src &= TRIG_NOW;
240         if (!cmd->start_src || tmp != cmd->start_src)
241                 err++;
242
243         tmp = cmd->scan_begin_src;
244         cmd->scan_begin_src &= TRIG_EXT;
245         if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
246                 err++;
247
248         tmp = cmd->convert_src;
249         cmd->convert_src &= TRIG_FOLLOW;
250         if (!cmd->convert_src || tmp != cmd->convert_src)
251                 err++;
252
253         tmp = cmd->scan_end_src;
254         cmd->scan_end_src &= TRIG_COUNT;
255         if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
256                 err++;
257
258         tmp = cmd->stop_src;
259         cmd->stop_src &= TRIG_NONE;
260         if (!cmd->stop_src || tmp != cmd->stop_src)
261                 err++;
262
263         if (err)
264                 return 1;
265
266         /* step 2 */
267
268         if (err)
269                 return 2;
270
271         /* step 3 */
272
273         if (cmd->start_arg != 0) {
274                 cmd->start_arg = 0;
275                 err++;
276         }
277         if (cmd->scan_begin_arg != 0) {
278                 cmd->scan_begin_arg = 0;
279                 err++;
280         }
281         if (cmd->convert_arg != 0) {
282                 cmd->convert_arg = 0;
283                 err++;
284         }
285         if (cmd->scan_end_arg != 1) {
286                 cmd->scan_end_arg = 1;
287                 err++;
288         }
289         if (cmd->stop_arg != 0) {
290                 cmd->stop_arg = 0;
291                 err++;
292         }
293
294         if (err)
295                 return 3;
296
297         /* step 4 */
298
299         if (err)
300                 return 4;
301
302         return 0;
303 }
304
305 static int subdev_8255_cmd(struct comedi_device *dev,
306                            struct comedi_subdevice *s)
307 {
308         /* FIXME */
309
310         return 0;
311 }
312
313 static int subdev_8255_cancel(struct comedi_device *dev,
314                               struct comedi_subdevice *s)
315 {
316         /* FIXME */
317
318         return 0;
319 }
320
321 int subdev_8255_init(struct comedi_device *dev, struct comedi_subdevice *s,
322                      int (*cb) (int, int, int, unsigned long),
323                      unsigned long arg)
324 {
325         s->type = COMEDI_SUBD_DIO;
326         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
327         s->n_chan = 24;
328         s->range_table = &range_digital;
329         s->maxdata = 1;
330
331         s->private = kmalloc(sizeof(struct subdev_8255_struct), GFP_KERNEL);
332         if (!s->private)
333                 return -ENOMEM;
334
335         CALLBACK_ARG = arg;
336         if (cb == NULL)
337                 CALLBACK_FUNC = subdev_8255_cb;
338         else
339                 CALLBACK_FUNC = cb;
340         s->insn_bits = subdev_8255_insn;
341         s->insn_config = subdev_8255_insn_config;
342
343         s->state = 0;
344         s->io_bits = 0;
345         do_config(dev, s);
346
347         return 0;
348 }
349 EXPORT_SYMBOL(subdev_8255_init);
350
351 int subdev_8255_init_irq(struct comedi_device *dev, struct comedi_subdevice *s,
352                          int (*cb) (int, int, int, unsigned long),
353                          unsigned long arg)
354 {
355         int ret;
356
357         ret = subdev_8255_init(dev, s, cb, arg);
358         if (ret < 0)
359                 return ret;
360
361         s->do_cmdtest = subdev_8255_cmdtest;
362         s->do_cmd = subdev_8255_cmd;
363         s->cancel = subdev_8255_cancel;
364
365         subdevpriv->have_irq = 1;
366
367         return 0;
368 }
369 EXPORT_SYMBOL(subdev_8255_init_irq);
370
371 void subdev_8255_cleanup(struct comedi_device *dev, struct comedi_subdevice *s)
372 {
373         if (s->private) {
374                 /* this test does nothing, so comment it out
375                  * if (subdevpriv->have_irq) {
376                  * }
377                  */
378
379                 kfree(s->private);
380         }
381 }
382 EXPORT_SYMBOL(subdev_8255_cleanup);
383
384 /*
385
386    Start of the 8255 standalone device
387
388  */
389
390 static int dev_8255_attach(struct comedi_device *dev,
391                            struct comedi_devconfig *it)
392 {
393         int ret;
394         unsigned long iobase;
395         int i;
396
397         printk("comedi%d: 8255:", dev->minor);
398
399         dev->board_name = "8255";
400
401         for (i = 0; i < COMEDI_NDEVCONFOPTS; i++) {
402                 iobase = it->options[i];
403                 if (!iobase)
404                         break;
405         }
406         if (i == 0) {
407                 printk(" no devices specified\n");
408                 return -EINVAL;
409         }
410
411         ret = alloc_subdevices(dev, i);
412         if (ret < 0)
413                 return ret;
414
415         for (i = 0; i < dev->n_subdevices; i++) {
416                 iobase = it->options[i];
417
418                 printk(" 0x%04lx", iobase);
419                 if (!request_region(iobase, _8255_SIZE, "8255")) {
420                         printk(" (I/O port conflict)");
421
422                         dev->subdevices[i].type = COMEDI_SUBD_UNUSED;
423                 } else {
424                         subdev_8255_init(dev, dev->subdevices + i, NULL,
425                                          iobase);
426                 }
427         }
428
429         printk("\n");
430
431         return 0;
432 }
433
434 static int dev_8255_detach(struct comedi_device *dev)
435 {
436         int i;
437         unsigned long iobase;
438         struct comedi_subdevice *s;
439
440         printk("comedi%d: 8255: remove\n", dev->minor);
441
442         for (i = 0; i < dev->n_subdevices; i++) {
443                 s = dev->subdevices + i;
444                 if (s->type != COMEDI_SUBD_UNUSED) {
445                         iobase = CALLBACK_ARG;
446                         release_region(iobase, _8255_SIZE);
447                 }
448                 subdev_8255_cleanup(dev, s);
449         }
450
451         return 0;
452 }