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