staging: comedi: remove FSF address from boilerplate text
[deliverable/linux.git] / drivers / staging / comedi / drivers / 8255.c
CommitLineData
6ca27334
DS
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.
6ca27334
DS
17*/
18/*
19Driver: 8255
20Description: generic 8255 support
21Devices: [standard] 8255 (8255)
22Author: ds
23Status: works
24Updated: Fri, 7 Jun 2002 12:56:45 -0700
25
26The classic in digital I/O. The 8255 appears in Comedi as a single
27digital I/O subdevice with 24 channels. The channel 0 corresponds
28to the 8255's port A, bit 0; channel 23 corresponds to port C, bit
297. Direction configuration is done in blocks, with channels 0-7,
308-15, 16-19, and 20-23 making up the 4 blocks. The only 8255 mode
31supported is mode 0.
32
33You should enable compilation this driver if you plan to use a board
34that has an 8255 chip. For multifunction boards, the main driver will
35configure the 8255 subdevice automatically.
36
37This driver also works independently with ISA and PCI cards that
38directly map the 8255 registers to I/O ports, including cards with
39multiple 8255 chips. To configure the driver for such a card, the
40option list should be a list of the I/O port bases for each of the
418255 chips. For example,
42
43 comedi_config /dev/comedi0 8255 0x200,0x204,0x208,0x20c
44
45Note that most PCI 8255 boards do NOT work with this driver, and
46need a separate driver as a wrapper. For those that do work, the
47I/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
d29a18dc 58 subdev_8255_init(device, subdevice, io_function, iobase)
6ca27334
DS
59
60 device and subdevice are pointers to the device and subdevice
d29a18dc 61 structures. io_function will be called to provide the
6ca27334 62 low-level input/output to the device, i.e., actual register
d29a18dc 63 access. io_function will be called with the value of iobase
6ca27334 64 as the last parameter. If the 8255 device is mapped as 4
d29a18dc
HS
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
6ca27334
DS
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 "../comedidev.h"
77
78#include <linux/ioport.h>
5a0e3ad6 79#include <linux/slab.h>
27020ffe
HS
80
81#include "comedi_fc.h"
c5efe58b 82#include "8255.h"
6ca27334 83
03ae8189 84#define _8255_SIZE 4
6ca27334 85
03ae8189
HS
86#define _8255_DATA 0
87#define _8255_CR 3
6ca27334
DS
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
a9044d91 97struct subdev_8255_private {
7a583163
HS
98 unsigned long iobase;
99 int (*io) (int, int, int, unsigned long);
6ca27334
DS
100};
101
f9af899b
HS
102static 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
c5efe58b
GKH
112void subdev_8255_interrupt(struct comedi_device *dev,
113 struct comedi_subdevice *s)
6ca27334 114{
7c61452a 115 struct subdev_8255_private *spriv = s->private;
f218d9f5 116 unsigned long iobase = spriv->iobase;
790c5541 117 short d;
6ca27334 118
f218d9f5
HS
119 d = spriv->io(0, _8255_DATA, 0, iobase);
120 d |= (spriv->io(0, _8255_DATA + 1, 0, iobase) << 8);
6ca27334
DS
121
122 comedi_buf_put(s->async, d);
123 s->async->events |= COMEDI_CB_EOS;
124
125 comedi_event(dev, s);
126}
5660e742 127EXPORT_SYMBOL_GPL(subdev_8255_interrupt);
6ca27334 128
0a85b6f0
MT
129static int subdev_8255_insn(struct comedi_device *dev,
130 struct comedi_subdevice *s,
131 struct comedi_insn *insn, unsigned int *data)
6ca27334 132{
7c61452a 133 struct subdev_8255_private *spriv = s->private;
f218d9f5 134 unsigned long iobase = spriv->iobase;
459f299e
HS
135 unsigned int mask;
136 unsigned int bits;
137 unsigned int v;
7c61452a 138
459f299e
HS
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;
6ca27334
DS
155 }
156
459f299e
HS
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;
6ca27334 162
459f299e 163 return insn->n;
6ca27334
DS
164}
165
cf8a6b3d
HS
166static void subdev_8255_do_config(struct comedi_device *dev,
167 struct comedi_subdevice *s)
3e699ed1 168{
7c61452a 169 struct subdev_8255_private *spriv = s->private;
f218d9f5 170 unsigned long iobase = spriv->iobase;
3e699ed1
HS
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;
f218d9f5
HS
183
184 spriv->io(1, _8255_CR, config, iobase);
3e699ed1
HS
185}
186
0a85b6f0
MT
187static int subdev_8255_insn_config(struct comedi_device *dev,
188 struct comedi_subdevice *s,
189 struct comedi_insn *insn, unsigned int *data)
6ca27334
DS
190{
191 unsigned int mask;
192 unsigned int bits;
193
194 mask = 1 << CR_CHAN(insn->chanspec);
228ec340 195 if (mask & 0x0000ff)
6ca27334 196 bits = 0x0000ff;
228ec340 197 else if (mask & 0x00ff00)
6ca27334 198 bits = 0x00ff00;
228ec340 199 else if (mask & 0x0f0000)
6ca27334 200 bits = 0x0f0000;
228ec340 201 else
6ca27334 202 bits = 0xf00000;
6ca27334
DS
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
cf8a6b3d 219 subdev_8255_do_config(dev, s);
6ca27334
DS
220
221 return 1;
222}
223
0a85b6f0
MT
224static int subdev_8255_cmdtest(struct comedi_device *dev,
225 struct comedi_subdevice *s,
226 struct comedi_cmd *cmd)
6ca27334
DS
227{
228 int err = 0;
6ca27334 229
27020ffe 230 /* Step 1 : check if triggers are trivially valid */
6ca27334 231
27020ffe
HS
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);
6ca27334
DS
237
238 if (err)
239 return 1;
240
27020ffe
HS
241 /* Step 2a : make sure trigger sources are unique */
242 /* Step 2b : and mutually compatible */
6ca27334
DS
243
244 if (err)
245 return 2;
246
851eef80 247 /* Step 3: check if arguments are trivially valid */
6ca27334 248
851eef80
HS
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);
6ca27334
DS
254
255 if (err)
256 return 3;
257
258 /* step 4 */
259
260 if (err)
261 return 4;
262
263 return 0;
264}
265
0a85b6f0
MT
266static int subdev_8255_cmd(struct comedi_device *dev,
267 struct comedi_subdevice *s)
6ca27334
DS
268{
269 /* FIXME */
270
271 return 0;
272}
273
0a85b6f0
MT
274static int subdev_8255_cancel(struct comedi_device *dev,
275 struct comedi_subdevice *s)
6ca27334
DS
276{
277 /* FIXME */
278
279 return 0;
280}
281
c5efe58b 282int subdev_8255_init(struct comedi_device *dev, struct comedi_subdevice *s,
d29a18dc
HS
283 int (*io) (int, int, int, unsigned long),
284 unsigned long iobase)
6ca27334 285{
7c61452a
HS
286 struct subdev_8255_private *spriv;
287
e64fb55b 288 spriv = kzalloc(sizeof(*spriv), GFP_KERNEL);
7c61452a 289 if (!spriv)
6ca27334
DS
290 return -ENOMEM;
291
cc31b1be
HS
292 spriv->iobase = iobase;
293 spriv->io = io ? io : subdev_8255_io;
294
295 s->private = spriv;
296
297 s->type = COMEDI_SUBD_DIO;
298 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
299 s->n_chan = 24;
300 s->range_table = &range_digital;
301 s->maxdata = 1;
302 s->insn_bits = subdev_8255_insn;
303 s->insn_config = subdev_8255_insn_config;
304
305 s->state = 0;
306 s->io_bits = 0;
6ca27334 307
cf8a6b3d 308 subdev_8255_do_config(dev, s);
6ca27334
DS
309
310 return 0;
311}
5660e742 312EXPORT_SYMBOL_GPL(subdev_8255_init);
6ca27334 313
c5efe58b 314int subdev_8255_init_irq(struct comedi_device *dev, struct comedi_subdevice *s,
d29a18dc
HS
315 int (*io) (int, int, int, unsigned long),
316 unsigned long iobase)
6ca27334
DS
317{
318 int ret;
319
d29a18dc 320 ret = subdev_8255_init(dev, s, io, iobase);
3e189f08 321 if (ret)
6ca27334
DS
322 return ret;
323
34cfcf9a
HS
324 s->do_cmdtest = subdev_8255_cmdtest;
325 s->do_cmd = subdev_8255_cmd;
326 s->cancel = subdev_8255_cancel;
6ca27334 327
6ca27334
DS
328 return 0;
329}
5660e742 330EXPORT_SYMBOL_GPL(subdev_8255_init_irq);
6ca27334 331
6ca27334
DS
332/*
333
334 Start of the 8255 standalone device
335
336 */
337
0a85b6f0
MT
338static int dev_8255_attach(struct comedi_device *dev,
339 struct comedi_devconfig *it)
6ca27334 340{
e40e8375 341 struct comedi_subdevice *s;
6ca27334
DS
342 int ret;
343 unsigned long iobase;
344 int i;
345
6ca27334
DS
346 for (i = 0; i < COMEDI_NDEVCONFOPTS; i++) {
347 iobase = it->options[i];
348 if (!iobase)
349 break;
350 }
351 if (i == 0) {
01bd3e3f 352 dev_warn(dev->class_dev, "no devices specified\n");
6ca27334
DS
353 return -EINVAL;
354 }
355
2f0b9d08 356 ret = comedi_alloc_subdevices(dev, i);
8b6c5694 357 if (ret)
6ca27334 358 return ret;
02638697 359
6ca27334 360 for (i = 0; i < dev->n_subdevices; i++) {
5101b4d1 361 s = &dev->subdevices[i];
6ca27334
DS
362 iobase = it->options[i];
363
e9720fd2
HS
364 ret = __comedi_request_region(dev, iobase, _8255_SIZE);
365 if (ret) {
e40e8375 366 s->type = COMEDI_SUBD_UNUSED;
6ca27334 367 } else {
3e189f08
HS
368 ret = subdev_8255_init(dev, s, NULL, iobase);
369 if (ret)
370 return ret;
6ca27334
DS
371 }
372 }
373
6ca27334
DS
374 return 0;
375}
376
484ecc95 377static void dev_8255_detach(struct comedi_device *dev)
6ca27334 378{
34c43922 379 struct comedi_subdevice *s;
7c61452a
HS
380 struct subdev_8255_private *spriv;
381 int i;
6ca27334 382
6ca27334 383 for (i = 0; i < dev->n_subdevices; i++) {
5101b4d1 384 s = &dev->subdevices[i];
6ca27334 385 if (s->type != COMEDI_SUBD_UNUSED) {
7c61452a 386 spriv = s->private;
7a583163 387 release_region(spriv->iobase, _8255_SIZE);
6ca27334 388 }
2f69915c 389 comedi_spriv_free(dev, i);
6ca27334 390 }
6ca27334 391}
90f703d3 392
294f930d 393static struct comedi_driver dev_8255_driver = {
3e699ed1
HS
394 .driver_name = "8255",
395 .module = THIS_MODULE,
396 .attach = dev_8255_attach,
397 .detach = dev_8255_detach,
398};
294f930d 399module_comedi_driver(dev_8255_driver);
3e699ed1 400
90f703d3
AT
401MODULE_AUTHOR("Comedi http://www.comedi.org");
402MODULE_DESCRIPTION("Comedi low-level driver");
403MODULE_LICENSE("GPL");
This page took 0.452223 seconds and 5 git commands to generate.