]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/staging/comedi/drivers/8255.c
staging: comedi: don't rely on comedidev.h to include headers
[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 <linux/ioport.h>
80
81 #include "comedi_fc.h"
82 #include "8255.h"
83
84 #define _8255_SIZE      4
85
86 #define _8255_DATA      0
87 #define _8255_CR        3
88
89 #define CR_C_LO_IO      0x01
90 #define CR_B_IO         0x02
91 #define CR_B_MODE       0x04
92 #define CR_C_HI_IO      0x08
93 #define CR_A_IO         0x10
94 #define CR_A_MODE(a)    ((a)<<5)
95 #define CR_CW           0x80
96
97 struct subdev_8255_private {
98         unsigned long iobase;
99         int (*io) (int, int, int, unsigned long);
100 };
101
102 static int subdev_8255_io(int dir, int port, int data, unsigned long iobase)
103 {
104         if (dir) {
105                 outb(data, iobase + port);
106                 return 0;
107         } else {
108                 return inb(iobase + port);
109         }
110 }
111
112 void subdev_8255_interrupt(struct comedi_device *dev,
113                            struct comedi_subdevice *s)
114 {
115         struct subdev_8255_private *spriv = s->private;
116         unsigned long iobase = spriv->iobase;
117         short d;
118
119         d = spriv->io(0, _8255_DATA, 0, iobase);
120         d |= (spriv->io(0, _8255_DATA + 1, 0, iobase) << 8);
121
122         comedi_buf_put(s->async, d);
123         s->async->events |= COMEDI_CB_EOS;
124
125         comedi_event(dev, s);
126 }
127 EXPORT_SYMBOL_GPL(subdev_8255_interrupt);
128
129 static int subdev_8255_insn(struct comedi_device *dev,
130                             struct comedi_subdevice *s,
131                             struct comedi_insn *insn, unsigned int *data)
132 {
133         struct subdev_8255_private *spriv = s->private;
134         unsigned long iobase = spriv->iobase;
135         unsigned int mask;
136         unsigned int bits;
137         unsigned int v;
138
139         mask = data[0];
140         bits = data[1];
141
142         if (mask) {
143                 v = s->state;
144                 v &= ~mask;
145                 v |= (bits & mask);
146
147                 if (mask & 0xff)
148                         spriv->io(1, _8255_DATA, v & 0xff, iobase);
149                 if (mask & 0xff00)
150                         spriv->io(1, _8255_DATA + 1, (v >> 8) & 0xff, iobase);
151                 if (mask & 0xff0000)
152                         spriv->io(1, _8255_DATA + 2, (v >> 16) & 0xff, iobase);
153
154                 s->state = v;
155         }
156
157         v = spriv->io(0, _8255_DATA, 0, iobase);
158         v |= (spriv->io(0, _8255_DATA + 1, 0, iobase) << 8);
159         v |= (spriv->io(0, _8255_DATA + 2, 0, iobase) << 16);
160
161         data[1] = v;
162
163         return insn->n;
164 }
165
166 static void subdev_8255_do_config(struct comedi_device *dev,
167                                   struct comedi_subdevice *s)
168 {
169         struct subdev_8255_private *spriv = s->private;
170         unsigned long iobase = spriv->iobase;
171         int config;
172
173         config = CR_CW;
174         /* 1 in io_bits indicates output, 1 in config indicates input */
175         if (!(s->io_bits & 0x0000ff))
176                 config |= CR_A_IO;
177         if (!(s->io_bits & 0x00ff00))
178                 config |= CR_B_IO;
179         if (!(s->io_bits & 0x0f0000))
180                 config |= CR_C_LO_IO;
181         if (!(s->io_bits & 0xf00000))
182                 config |= CR_C_HI_IO;
183
184         spriv->io(1, _8255_CR, config, iobase);
185 }
186
187 static int subdev_8255_insn_config(struct comedi_device *dev,
188                                    struct comedi_subdevice *s,
189                                    struct comedi_insn *insn, unsigned int *data)
190 {
191         unsigned int mask;
192         unsigned int bits;
193
194         mask = 1 << CR_CHAN(insn->chanspec);
195         if (mask & 0x0000ff)
196                 bits = 0x0000ff;
197         else if (mask & 0x00ff00)
198                 bits = 0x00ff00;
199         else if (mask & 0x0f0000)
200                 bits = 0x0f0000;
201         else
202                 bits = 0xf00000;
203
204         switch (data[0]) {
205         case INSN_CONFIG_DIO_INPUT:
206                 s->io_bits &= ~bits;
207                 break;
208         case INSN_CONFIG_DIO_OUTPUT:
209                 s->io_bits |= bits;
210                 break;
211         case INSN_CONFIG_DIO_QUERY:
212                 data[1] = (s->io_bits & bits) ? COMEDI_OUTPUT : COMEDI_INPUT;
213                 return insn->n;
214                 break;
215         default:
216                 return -EINVAL;
217         }
218
219         subdev_8255_do_config(dev, s);
220
221         return 1;
222 }
223
224 static int subdev_8255_cmdtest(struct comedi_device *dev,
225                                struct comedi_subdevice *s,
226                                struct comedi_cmd *cmd)
227 {
228         int err = 0;
229
230         /* Step 1 : check if triggers are trivially valid */
231
232         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
233         err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
234         err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
235         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
236         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE);
237
238         if (err)
239                 return 1;
240
241         /* Step 2a : make sure trigger sources are unique */
242         /* Step 2b : and mutually compatible */
243
244         if (err)
245                 return 2;
246
247         /* Step 3: check if arguments are trivially valid */
248
249         err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
250         err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
251         err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
252         err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, 1);
253         err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
254
255         if (err)
256                 return 3;
257
258         /* step 4 */
259
260         if (err)
261                 return 4;
262
263         return 0;
264 }
265
266 static int subdev_8255_cmd(struct comedi_device *dev,
267                            struct comedi_subdevice *s)
268 {
269         /* FIXME */
270
271         return 0;
272 }
273
274 static int subdev_8255_cancel(struct comedi_device *dev,
275                               struct comedi_subdevice *s)
276 {
277         /* FIXME */
278
279         return 0;
280 }
281
282 int subdev_8255_init(struct comedi_device *dev, struct comedi_subdevice *s,
283                      int (*io) (int, int, int, unsigned long),
284                      unsigned long iobase)
285 {
286         struct subdev_8255_private *spriv;
287
288         spriv = comedi_alloc_spriv(s, sizeof(*spriv));
289         if (!spriv)
290                 return -ENOMEM;
291
292         spriv->iobase   = iobase;
293         spriv->io       = io ? io : subdev_8255_io;
294
295         s->type         = COMEDI_SUBD_DIO;
296         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
297         s->n_chan       = 24;
298         s->range_table  = &range_digital;
299         s->maxdata      = 1;
300         s->insn_bits    = subdev_8255_insn;
301         s->insn_config  = subdev_8255_insn_config;
302
303         s->state        = 0;
304         s->io_bits      = 0;
305
306         subdev_8255_do_config(dev, s);
307
308         return 0;
309 }
310 EXPORT_SYMBOL_GPL(subdev_8255_init);
311
312 int subdev_8255_init_irq(struct comedi_device *dev, struct comedi_subdevice *s,
313                          int (*io) (int, int, int, unsigned long),
314                          unsigned long iobase)
315 {
316         int ret;
317
318         ret = subdev_8255_init(dev, s, io, iobase);
319         if (ret)
320                 return ret;
321
322         s->do_cmdtest   = subdev_8255_cmdtest;
323         s->do_cmd       = subdev_8255_cmd;
324         s->cancel       = subdev_8255_cancel;
325
326         return 0;
327 }
328 EXPORT_SYMBOL_GPL(subdev_8255_init_irq);
329
330 /*
331
332    Start of the 8255 standalone device
333
334  */
335
336 static int dev_8255_attach(struct comedi_device *dev,
337                            struct comedi_devconfig *it)
338 {
339         struct comedi_subdevice *s;
340         int ret;
341         unsigned long iobase;
342         int i;
343
344         for (i = 0; i < COMEDI_NDEVCONFOPTS; i++) {
345                 iobase = it->options[i];
346                 if (!iobase)
347                         break;
348         }
349         if (i == 0) {
350                 dev_warn(dev->class_dev, "no devices specified\n");
351                 return -EINVAL;
352         }
353
354         ret = comedi_alloc_subdevices(dev, i);
355         if (ret)
356                 return ret;
357
358         for (i = 0; i < dev->n_subdevices; i++) {
359                 s = &dev->subdevices[i];
360                 iobase = it->options[i];
361
362                 ret = __comedi_request_region(dev, iobase, _8255_SIZE);
363                 if (ret) {
364                         s->type = COMEDI_SUBD_UNUSED;
365                 } else {
366                         ret = subdev_8255_init(dev, s, NULL, iobase);
367                         if (ret)
368                                 return ret;
369                 }
370         }
371
372         return 0;
373 }
374
375 static void dev_8255_detach(struct comedi_device *dev)
376 {
377         struct comedi_subdevice *s;
378         struct subdev_8255_private *spriv;
379         int i;
380
381         for (i = 0; i < dev->n_subdevices; i++) {
382                 s = &dev->subdevices[i];
383                 if (s->type != COMEDI_SUBD_UNUSED) {
384                         spriv = s->private;
385                         release_region(spriv->iobase, _8255_SIZE);
386                 }
387         }
388 }
389
390 static struct comedi_driver dev_8255_driver = {
391         .driver_name    = "8255",
392         .module         = THIS_MODULE,
393         .attach         = dev_8255_attach,
394         .detach         = dev_8255_detach,
395 };
396 module_comedi_driver(dev_8255_driver);
397
398 MODULE_AUTHOR("Comedi http://www.comedi.org");
399 MODULE_DESCRIPTION("Comedi low-level driver");
400 MODULE_LICENSE("GPL");