]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/staging/comedi/drivers/8255.c
Merge 3.11-rc3 into staging-next
[karo-tx-linux.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 /*
19 Driver: 8255
20 Description: generic 8255 support
21 Devices: [standard] 8255 (8255)
22 Author: ds
23 Status: works
24 Updated: Fri,  7 Jun 2002 12:56:45 -0700
25
26 The classic in digital I/O.  The 8255 appears in Comedi as a single
27 digital I/O subdevice with 24 channels.  The channel 0 corresponds
28 to the 8255's port A, bit 0; channel 23 corresponds to port C, bit
29 7.  Direction configuration is done in blocks, with channels 0-7,
30 8-15, 16-19, and 20-23 making up the 4 blocks.  The only 8255 mode
31 supported is mode 0.
32
33 You should enable compilation this driver if you plan to use a board
34 that has an 8255 chip.  For multifunction boards, the main driver will
35 configure the 8255 subdevice automatically.
36
37 This driver also works independently with ISA and PCI cards that
38 directly map the 8255 registers to I/O ports, including cards with
39 multiple 8255 chips.  To configure the driver for such a card, the
40 option list should be a list of the I/O port bases for each of the
41 8255 chips.  For example,
42
43   comedi_config /dev/comedi0 8255 0x200,0x204,0x208,0x20c
44
45 Note that most PCI 8255 boards do NOT work with this driver, and
46 need a separate driver as a wrapper.  For those that do work, the
47 I/O port base address can be found in the output of 'lspci -v'.
48
49 */
50
51 /*
52    This file contains an exported subdevice for driving an 8255.
53
54    To use this subdevice as part of another driver, you need to
55    set up the subdevice in the attach function of the driver by
56    calling:
57
58      subdev_8255_init(device, subdevice, io_function, iobase)
59
60    device and subdevice are pointers to the device and subdevice
61    structures.  io_function will be called to provide the
62    low-level input/output to the device, i.e., actual register
63    access.  io_function will be called with the value of iobase
64    as the last parameter.  If the 8255 device is mapped as 4
65    consecutive I/O ports, you can use NULL for io_function
66    and the I/O port base for iobase, and an internal function will
67    handle the register access.
68
69    In addition, if the main driver handles interrupts, you can
70    enable commands on the subdevice by calling subdev_8255_init_irq()
71    instead.  Then, when you get an interrupt that is likely to be
72    from the 8255, you should call subdev_8255_interrupt(), which
73    will copy the latched value to a Comedi buffer.
74  */
75
76 #include <linux/module.h>
77 #include "../comedidev.h"
78
79 #include "comedi_fc.h"
80 #include "8255.h"
81
82 #define _8255_SIZE      4
83
84 #define _8255_DATA      0
85 #define _8255_CR        3
86
87 #define CR_C_LO_IO      0x01
88 #define CR_B_IO         0x02
89 #define CR_B_MODE       0x04
90 #define CR_C_HI_IO      0x08
91 #define CR_A_IO         0x10
92 #define CR_A_MODE(a)    ((a)<<5)
93 #define CR_CW           0x80
94
95 struct subdev_8255_private {
96         unsigned long iobase;
97         int (*io) (int, int, int, unsigned long);
98 };
99
100 static int subdev_8255_io(int dir, int port, int data, unsigned long iobase)
101 {
102         if (dir) {
103                 outb(data, iobase + port);
104                 return 0;
105         } else {
106                 return inb(iobase + port);
107         }
108 }
109
110 void subdev_8255_interrupt(struct comedi_device *dev,
111                            struct comedi_subdevice *s)
112 {
113         struct subdev_8255_private *spriv = s->private;
114         unsigned long iobase = spriv->iobase;
115         short d;
116
117         d = spriv->io(0, _8255_DATA, 0, iobase);
118         d |= (spriv->io(0, _8255_DATA + 1, 0, iobase) << 8);
119
120         comedi_buf_put(s->async, d);
121         s->async->events |= COMEDI_CB_EOS;
122
123         comedi_event(dev, s);
124 }
125 EXPORT_SYMBOL_GPL(subdev_8255_interrupt);
126
127 static int subdev_8255_insn(struct comedi_device *dev,
128                             struct comedi_subdevice *s,
129                             struct comedi_insn *insn, unsigned int *data)
130 {
131         struct subdev_8255_private *spriv = s->private;
132         unsigned long iobase = spriv->iobase;
133         unsigned int mask;
134         unsigned int bits;
135         unsigned int v;
136
137         mask = data[0];
138         bits = data[1];
139
140         if (mask) {
141                 v = s->state;
142                 v &= ~mask;
143                 v |= (bits & mask);
144
145                 if (mask & 0xff)
146                         spriv->io(1, _8255_DATA, v & 0xff, iobase);
147                 if (mask & 0xff00)
148                         spriv->io(1, _8255_DATA + 1, (v >> 8) & 0xff, iobase);
149                 if (mask & 0xff0000)
150                         spriv->io(1, _8255_DATA + 2, (v >> 16) & 0xff, iobase);
151
152                 s->state = v;
153         }
154
155         v = spriv->io(0, _8255_DATA, 0, iobase);
156         v |= (spriv->io(0, _8255_DATA + 1, 0, iobase) << 8);
157         v |= (spriv->io(0, _8255_DATA + 2, 0, iobase) << 16);
158
159         data[1] = v;
160
161         return insn->n;
162 }
163
164 static void subdev_8255_do_config(struct comedi_device *dev,
165                                   struct comedi_subdevice *s)
166 {
167         struct subdev_8255_private *spriv = s->private;
168         unsigned long iobase = spriv->iobase;
169         int config;
170
171         config = CR_CW;
172         /* 1 in io_bits indicates output, 1 in config indicates input */
173         if (!(s->io_bits & 0x0000ff))
174                 config |= CR_A_IO;
175         if (!(s->io_bits & 0x00ff00))
176                 config |= CR_B_IO;
177         if (!(s->io_bits & 0x0f0000))
178                 config |= CR_C_LO_IO;
179         if (!(s->io_bits & 0xf00000))
180                 config |= CR_C_HI_IO;
181
182         spriv->io(1, _8255_CR, config, iobase);
183 }
184
185 static int subdev_8255_insn_config(struct comedi_device *dev,
186                                    struct comedi_subdevice *s,
187                                    struct comedi_insn *insn, unsigned int *data)
188 {
189         unsigned int mask;
190         unsigned int bits;
191
192         mask = 1 << CR_CHAN(insn->chanspec);
193         if (mask & 0x0000ff)
194                 bits = 0x0000ff;
195         else if (mask & 0x00ff00)
196                 bits = 0x00ff00;
197         else if (mask & 0x0f0000)
198                 bits = 0x0f0000;
199         else
200                 bits = 0xf00000;
201
202         switch (data[0]) {
203         case INSN_CONFIG_DIO_INPUT:
204                 s->io_bits &= ~bits;
205                 break;
206         case INSN_CONFIG_DIO_OUTPUT:
207                 s->io_bits |= bits;
208                 break;
209         case INSN_CONFIG_DIO_QUERY:
210                 data[1] = (s->io_bits & bits) ? COMEDI_OUTPUT : COMEDI_INPUT;
211                 return insn->n;
212                 break;
213         default:
214                 return -EINVAL;
215         }
216
217         subdev_8255_do_config(dev, s);
218
219         return 1;
220 }
221
222 static int subdev_8255_cmdtest(struct comedi_device *dev,
223                                struct comedi_subdevice *s,
224                                struct comedi_cmd *cmd)
225 {
226         int err = 0;
227
228         /* Step 1 : check if triggers are trivially valid */
229
230         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
231         err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
232         err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
233         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
234         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE);
235
236         if (err)
237                 return 1;
238
239         /* Step 2a : make sure trigger sources are unique */
240         /* Step 2b : and mutually compatible */
241
242         if (err)
243                 return 2;
244
245         /* Step 3: check if arguments are trivially valid */
246
247         err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
248         err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
249         err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
250         err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, 1);
251         err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
252
253         if (err)
254                 return 3;
255
256         /* step 4 */
257
258         if (err)
259                 return 4;
260
261         return 0;
262 }
263
264 static int subdev_8255_cmd(struct comedi_device *dev,
265                            struct comedi_subdevice *s)
266 {
267         /* FIXME */
268
269         return 0;
270 }
271
272 static int subdev_8255_cancel(struct comedi_device *dev,
273                               struct comedi_subdevice *s)
274 {
275         /* FIXME */
276
277         return 0;
278 }
279
280 int subdev_8255_init(struct comedi_device *dev, struct comedi_subdevice *s,
281                      int (*io) (int, int, int, unsigned long),
282                      unsigned long iobase)
283 {
284         struct subdev_8255_private *spriv;
285
286         spriv = comedi_alloc_spriv(s, sizeof(*spriv));
287         if (!spriv)
288                 return -ENOMEM;
289
290         spriv->iobase   = iobase;
291         spriv->io       = io ? io : subdev_8255_io;
292
293         s->type         = COMEDI_SUBD_DIO;
294         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
295         s->n_chan       = 24;
296         s->range_table  = &range_digital;
297         s->maxdata      = 1;
298         s->insn_bits    = subdev_8255_insn;
299         s->insn_config  = subdev_8255_insn_config;
300
301         s->state        = 0;
302         s->io_bits      = 0;
303
304         subdev_8255_do_config(dev, s);
305
306         return 0;
307 }
308 EXPORT_SYMBOL_GPL(subdev_8255_init);
309
310 int subdev_8255_init_irq(struct comedi_device *dev, struct comedi_subdevice *s,
311                          int (*io) (int, int, int, unsigned long),
312                          unsigned long iobase)
313 {
314         int ret;
315
316         ret = subdev_8255_init(dev, s, io, iobase);
317         if (ret)
318                 return ret;
319
320         s->do_cmdtest   = subdev_8255_cmdtest;
321         s->do_cmd       = subdev_8255_cmd;
322         s->cancel       = subdev_8255_cancel;
323
324         return 0;
325 }
326 EXPORT_SYMBOL_GPL(subdev_8255_init_irq);
327
328 /*
329
330    Start of the 8255 standalone device
331
332  */
333
334 static int dev_8255_attach(struct comedi_device *dev,
335                            struct comedi_devconfig *it)
336 {
337         struct comedi_subdevice *s;
338         int ret;
339         unsigned long iobase;
340         int i;
341
342         for (i = 0; i < COMEDI_NDEVCONFOPTS; i++) {
343                 iobase = it->options[i];
344                 if (!iobase)
345                         break;
346         }
347         if (i == 0) {
348                 dev_warn(dev->class_dev, "no devices specified\n");
349                 return -EINVAL;
350         }
351
352         ret = comedi_alloc_subdevices(dev, i);
353         if (ret)
354                 return ret;
355
356         for (i = 0; i < dev->n_subdevices; i++) {
357                 s = &dev->subdevices[i];
358                 iobase = it->options[i];
359
360                 ret = __comedi_request_region(dev, iobase, _8255_SIZE);
361                 if (ret) {
362                         s->type = COMEDI_SUBD_UNUSED;
363                 } else {
364                         ret = subdev_8255_init(dev, s, NULL, iobase);
365                         if (ret)
366                                 return ret;
367                 }
368         }
369
370         return 0;
371 }
372
373 static void dev_8255_detach(struct comedi_device *dev)
374 {
375         struct comedi_subdevice *s;
376         struct subdev_8255_private *spriv;
377         int i;
378
379         for (i = 0; i < dev->n_subdevices; i++) {
380                 s = &dev->subdevices[i];
381                 if (s->type != COMEDI_SUBD_UNUSED) {
382                         spriv = s->private;
383                         release_region(spriv->iobase, _8255_SIZE);
384                 }
385         }
386 }
387
388 static struct comedi_driver dev_8255_driver = {
389         .driver_name    = "8255",
390         .module         = THIS_MODULE,
391         .attach         = dev_8255_attach,
392         .detach         = dev_8255_detach,
393 };
394 module_comedi_driver(dev_8255_driver);
395
396 MODULE_AUTHOR("Comedi http://www.comedi.org");
397 MODULE_DESCRIPTION("Comedi low-level driver");
398 MODULE_LICENSE("GPL");