Staging: comedi: Give the addi_apci_* drivers different driver names
[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 #include <linux/slab.h>
85 #include "8255.h"
86
87 #define _8255_SIZE 4
88
89 #define _8255_DATA 0
90 #define _8255_CR 3
91
92 #define CR_C_LO_IO      0x01
93 #define CR_B_IO         0x02
94 #define CR_B_MODE       0x04
95 #define CR_C_HI_IO      0x08
96 #define CR_A_IO         0x10
97 #define CR_A_MODE(a)    ((a)<<5)
98 #define CR_CW           0x80
99
100 struct subdev_8255_struct {
101         unsigned long cb_arg;
102         int (*cb_func) (int, int, int, unsigned long);
103         int have_irq;
104 };
105
106 #define CALLBACK_ARG    (((struct subdev_8255_struct *)s->private)->cb_arg)
107 #define CALLBACK_FUNC   (((struct subdev_8255_struct *)s->private)->cb_func)
108 #define subdevpriv      ((struct subdev_8255_struct *)s->private)
109
110 static int dev_8255_attach(struct comedi_device *dev,
111                            struct comedi_devconfig *it);
112 static int dev_8255_detach(struct comedi_device *dev);
113 static struct comedi_driver driver_8255 = {
114         .driver_name = "8255",
115         .module = THIS_MODULE,
116         .attach = dev_8255_attach,
117         .detach = dev_8255_detach,
118 };
119
120 COMEDI_INITCLEANUP(driver_8255);
121
122 static void do_config(struct comedi_device *dev, struct comedi_subdevice *s);
123
124 void subdev_8255_interrupt(struct comedi_device *dev,
125                            struct comedi_subdevice *s)
126 {
127         short d;
128
129         d = CALLBACK_FUNC(0, _8255_DATA, 0, CALLBACK_ARG);
130         d |= (CALLBACK_FUNC(0, _8255_DATA + 1, 0, CALLBACK_ARG) << 8);
131
132         comedi_buf_put(s->async, d);
133         s->async->events |= COMEDI_CB_EOS;
134
135         comedi_event(dev, s);
136 }
137 EXPORT_SYMBOL(subdev_8255_interrupt);
138
139 static int subdev_8255_cb(int dir, int port, int data, unsigned long arg)
140 {
141         unsigned long iobase = arg;
142
143         if (dir) {
144                 outb(data, iobase + port);
145                 return 0;
146         } else {
147                 return inb(iobase + port);
148         }
149 }
150
151 static int subdev_8255_insn(struct comedi_device *dev,
152                             struct comedi_subdevice *s,
153                             struct comedi_insn *insn, unsigned int *data)
154 {
155         if (data[0]) {
156                 s->state &= ~data[0];
157                 s->state |= (data[0] & data[1]);
158
159                 if (data[0] & 0xff)
160                         CALLBACK_FUNC(1, _8255_DATA, s->state & 0xff,
161                                       CALLBACK_ARG);
162                 if (data[0] & 0xff00)
163                         CALLBACK_FUNC(1, _8255_DATA + 1, (s->state >> 8) & 0xff,
164                                       CALLBACK_ARG);
165                 if (data[0] & 0xff0000)
166                         CALLBACK_FUNC(1, _8255_DATA + 2,
167                                       (s->state >> 16) & 0xff, CALLBACK_ARG);
168         }
169
170         data[1] = CALLBACK_FUNC(0, _8255_DATA, 0, CALLBACK_ARG);
171         data[1] |= (CALLBACK_FUNC(0, _8255_DATA + 1, 0, CALLBACK_ARG) << 8);
172         data[1] |= (CALLBACK_FUNC(0, _8255_DATA + 2, 0, CALLBACK_ARG) << 16);
173
174         return 2;
175 }
176
177 static int subdev_8255_insn_config(struct comedi_device *dev,
178                                    struct comedi_subdevice *s,
179                                    struct comedi_insn *insn, unsigned int *data)
180 {
181         unsigned int mask;
182         unsigned int bits;
183
184         mask = 1 << CR_CHAN(insn->chanspec);
185         if (mask & 0x0000ff)
186                 bits = 0x0000ff;
187         else if (mask & 0x00ff00)
188                 bits = 0x00ff00;
189         else if (mask & 0x0f0000)
190                 bits = 0x0f0000;
191         else
192                 bits = 0xf00000;
193
194         switch (data[0]) {
195         case INSN_CONFIG_DIO_INPUT:
196                 s->io_bits &= ~bits;
197                 break;
198         case INSN_CONFIG_DIO_OUTPUT:
199                 s->io_bits |= bits;
200                 break;
201         case INSN_CONFIG_DIO_QUERY:
202                 data[1] = (s->io_bits & bits) ? COMEDI_OUTPUT : COMEDI_INPUT;
203                 return insn->n;
204                 break;
205         default:
206                 return -EINVAL;
207         }
208
209         do_config(dev, s);
210
211         return 1;
212 }
213
214 static void do_config(struct comedi_device *dev, struct comedi_subdevice *s)
215 {
216         int config;
217
218         config = CR_CW;
219         /* 1 in io_bits indicates output, 1 in config indicates input */
220         if (!(s->io_bits & 0x0000ff))
221                 config |= CR_A_IO;
222         if (!(s->io_bits & 0x00ff00))
223                 config |= CR_B_IO;
224         if (!(s->io_bits & 0x0f0000))
225                 config |= CR_C_LO_IO;
226         if (!(s->io_bits & 0xf00000))
227                 config |= CR_C_HI_IO;
228         CALLBACK_FUNC(1, _8255_CR, config, CALLBACK_ARG);
229 }
230
231 static int subdev_8255_cmdtest(struct comedi_device *dev,
232                                struct comedi_subdevice *s,
233                                struct comedi_cmd *cmd)
234 {
235         int err = 0;
236         unsigned int tmp;
237
238         /* step 1 */
239
240         tmp = cmd->start_src;
241         cmd->start_src &= TRIG_NOW;
242         if (!cmd->start_src || tmp != cmd->start_src)
243                 err++;
244
245         tmp = cmd->scan_begin_src;
246         cmd->scan_begin_src &= TRIG_EXT;
247         if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
248                 err++;
249
250         tmp = cmd->convert_src;
251         cmd->convert_src &= TRIG_FOLLOW;
252         if (!cmd->convert_src || tmp != cmd->convert_src)
253                 err++;
254
255         tmp = cmd->scan_end_src;
256         cmd->scan_end_src &= TRIG_COUNT;
257         if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
258                 err++;
259
260         tmp = cmd->stop_src;
261         cmd->stop_src &= TRIG_NONE;
262         if (!cmd->stop_src || tmp != cmd->stop_src)
263                 err++;
264
265         if (err)
266                 return 1;
267
268         /* step 2 */
269
270         if (err)
271                 return 2;
272
273         /* step 3 */
274
275         if (cmd->start_arg != 0) {
276                 cmd->start_arg = 0;
277                 err++;
278         }
279         if (cmd->scan_begin_arg != 0) {
280                 cmd->scan_begin_arg = 0;
281                 err++;
282         }
283         if (cmd->convert_arg != 0) {
284                 cmd->convert_arg = 0;
285                 err++;
286         }
287         if (cmd->scan_end_arg != 1) {
288                 cmd->scan_end_arg = 1;
289                 err++;
290         }
291         if (cmd->stop_arg != 0) {
292                 cmd->stop_arg = 0;
293                 err++;
294         }
295
296         if (err)
297                 return 3;
298
299         /* step 4 */
300
301         if (err)
302                 return 4;
303
304         return 0;
305 }
306
307 static int subdev_8255_cmd(struct comedi_device *dev,
308                            struct comedi_subdevice *s)
309 {
310         /* FIXME */
311
312         return 0;
313 }
314
315 static int subdev_8255_cancel(struct comedi_device *dev,
316                               struct comedi_subdevice *s)
317 {
318         /* FIXME */
319
320         return 0;
321 }
322
323 int subdev_8255_init(struct comedi_device *dev, struct comedi_subdevice *s,
324                      int (*cb) (int, int, int, unsigned long),
325                      unsigned long arg)
326 {
327         s->type = COMEDI_SUBD_DIO;
328         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
329         s->n_chan = 24;
330         s->range_table = &range_digital;
331         s->maxdata = 1;
332
333         s->private = kmalloc(sizeof(struct subdev_8255_struct), GFP_KERNEL);
334         if (!s->private)
335                 return -ENOMEM;
336
337         CALLBACK_ARG = arg;
338         if (cb == NULL)
339                 CALLBACK_FUNC = subdev_8255_cb;
340         else
341                 CALLBACK_FUNC = cb;
342         s->insn_bits = subdev_8255_insn;
343         s->insn_config = subdev_8255_insn_config;
344
345         s->state = 0;
346         s->io_bits = 0;
347         do_config(dev, s);
348
349         return 0;
350 }
351 EXPORT_SYMBOL(subdev_8255_init);
352
353 int subdev_8255_init_irq(struct comedi_device *dev, struct comedi_subdevice *s,
354                          int (*cb) (int, int, int, unsigned long),
355                          unsigned long arg)
356 {
357         int ret;
358
359         ret = subdev_8255_init(dev, s, cb, arg);
360         if (ret < 0)
361                 return ret;
362
363         s->do_cmdtest = subdev_8255_cmdtest;
364         s->do_cmd = subdev_8255_cmd;
365         s->cancel = subdev_8255_cancel;
366
367         subdevpriv->have_irq = 1;
368
369         return 0;
370 }
371 EXPORT_SYMBOL(subdev_8255_init_irq);
372
373 void subdev_8255_cleanup(struct comedi_device *dev, struct comedi_subdevice *s)
374 {
375         if (s->private) {
376                 /* this test does nothing, so comment it out
377                  * if (subdevpriv->have_irq) {
378                  * }
379                  */
380
381                 kfree(s->private);
382         }
383 }
384 EXPORT_SYMBOL(subdev_8255_cleanup);
385
386 /*
387
388    Start of the 8255 standalone device
389
390  */
391
392 static int dev_8255_attach(struct comedi_device *dev,
393                            struct comedi_devconfig *it)
394 {
395         int ret;
396         unsigned long iobase;
397         int i;
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(KERN_WARNING
408                        "comedi%d: 8255: no devices specified\n", dev->minor);
409                 return -EINVAL;
410         }
411
412         ret = alloc_subdevices(dev, i);
413         if (ret < 0) {
414                 /* FIXME this printk call should give a proper message, the
415                  * below line just maintains previous functionality */
416                 printk("comedi%d: 8255:", dev->minor);
417                 return ret;
418         }
419
420         printk(KERN_INFO "comedi%d: 8255:", dev->minor);
421
422         for (i = 0; i < dev->n_subdevices; i++) {
423                 iobase = it->options[i];
424
425                 printk(" 0x%04lx", iobase);
426                 if (!request_region(iobase, _8255_SIZE, "8255")) {
427                         printk(" (I/O port conflict)");
428
429                         dev->subdevices[i].type = COMEDI_SUBD_UNUSED;
430                 } else {
431                         subdev_8255_init(dev, dev->subdevices + i, NULL,
432                                          iobase);
433                 }
434         }
435
436         printk("\n");
437
438         return 0;
439 }
440
441 static int dev_8255_detach(struct comedi_device *dev)
442 {
443         int i;
444         unsigned long iobase;
445         struct comedi_subdevice *s;
446
447         printk(KERN_INFO "comedi%d: 8255: remove\n", dev->minor);
448
449         for (i = 0; i < dev->n_subdevices; i++) {
450                 s = dev->subdevices + i;
451                 if (s->type != COMEDI_SUBD_UNUSED) {
452                         iobase = CALLBACK_ARG;
453                         release_region(iobase, _8255_SIZE);
454                 }
455                 subdev_8255_cleanup(dev, s);
456         }
457
458         return 0;
459 }