staging: comedi: pass subdevice to comedi_buf_put()
[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
ce157f80 76#include <linux/module.h>
6ca27334
DS
77#include "../comedidev.h"
78
27020ffe 79#include "comedi_fc.h"
c5efe58b 80#include "8255.h"
6ca27334 81
03ae8189 82#define _8255_SIZE 4
6ca27334 83
03ae8189
HS
84#define _8255_DATA 0
85#define _8255_CR 3
6ca27334
DS
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
a9044d91 95struct subdev_8255_private {
7a583163 96 unsigned long iobase;
b2815398 97 int (*io)(int, int, int, unsigned long);
6ca27334
DS
98};
99
f9af899b
HS
100static 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
c5efe58b
GKH
110void subdev_8255_interrupt(struct comedi_device *dev,
111 struct comedi_subdevice *s)
6ca27334 112{
7c61452a 113 struct subdev_8255_private *spriv = s->private;
f218d9f5 114 unsigned long iobase = spriv->iobase;
830e273c 115 unsigned short d;
6ca27334 116
f218d9f5
HS
117 d = spriv->io(0, _8255_DATA, 0, iobase);
118 d |= (spriv->io(0, _8255_DATA + 1, 0, iobase) << 8);
6ca27334 119
3672effd 120 comedi_buf_put(s, d);
6ca27334
DS
121 s->async->events |= COMEDI_CB_EOS;
122
123 comedi_event(dev, s);
124}
5660e742 125EXPORT_SYMBOL_GPL(subdev_8255_interrupt);
6ca27334 126
0a85b6f0
MT
127static int subdev_8255_insn(struct comedi_device *dev,
128 struct comedi_subdevice *s,
b3ff824a
HS
129 struct comedi_insn *insn,
130 unsigned int *data)
6ca27334 131{
7c61452a 132 struct subdev_8255_private *spriv = s->private;
f218d9f5 133 unsigned long iobase = spriv->iobase;
459f299e 134 unsigned int mask;
459f299e 135 unsigned int v;
7c61452a 136
b3ff824a 137 mask = comedi_dio_update_state(s, data);
459f299e 138 if (mask) {
459f299e 139 if (mask & 0xff)
b3ff824a 140 spriv->io(1, _8255_DATA, s->state & 0xff, iobase);
459f299e 141 if (mask & 0xff00)
b3ff824a
HS
142 spriv->io(1, _8255_DATA + 1, (s->state >> 8) & 0xff,
143 iobase);
459f299e 144 if (mask & 0xff0000)
b3ff824a
HS
145 spriv->io(1, _8255_DATA + 2, (s->state >> 16) & 0xff,
146 iobase);
6ca27334
DS
147 }
148
459f299e
HS
149 v = spriv->io(0, _8255_DATA, 0, iobase);
150 v |= (spriv->io(0, _8255_DATA + 1, 0, iobase) << 8);
151 v |= (spriv->io(0, _8255_DATA + 2, 0, iobase) << 16);
152
153 data[1] = v;
6ca27334 154
459f299e 155 return insn->n;
6ca27334
DS
156}
157
cf8a6b3d
HS
158static void subdev_8255_do_config(struct comedi_device *dev,
159 struct comedi_subdevice *s)
3e699ed1 160{
7c61452a 161 struct subdev_8255_private *spriv = s->private;
f218d9f5 162 unsigned long iobase = spriv->iobase;
3e699ed1
HS
163 int config;
164
165 config = CR_CW;
166 /* 1 in io_bits indicates output, 1 in config indicates input */
167 if (!(s->io_bits & 0x0000ff))
168 config |= CR_A_IO;
169 if (!(s->io_bits & 0x00ff00))
170 config |= CR_B_IO;
171 if (!(s->io_bits & 0x0f0000))
172 config |= CR_C_LO_IO;
173 if (!(s->io_bits & 0xf00000))
174 config |= CR_C_HI_IO;
f218d9f5
HS
175
176 spriv->io(1, _8255_CR, config, iobase);
3e699ed1
HS
177}
178
0a85b6f0
MT
179static int subdev_8255_insn_config(struct comedi_device *dev,
180 struct comedi_subdevice *s,
5dacadcc
HS
181 struct comedi_insn *insn,
182 unsigned int *data)
6ca27334 183{
5dacadcc 184 unsigned int chan = CR_CHAN(insn->chanspec);
6ca27334 185 unsigned int mask;
5dacadcc
HS
186 int ret;
187
188 if (chan < 8)
189 mask = 0x0000ff;
190 else if (chan < 16)
191 mask = 0x00ff00;
192 else if (chan < 20)
193 mask = 0x0f0000;
3457bfd6 194 else
5dacadcc
HS
195 mask = 0xf00000;
196
197 ret = comedi_dio_insn_config(dev, s, insn, data, mask);
198 if (ret)
199 return ret;
6ca27334 200
cf8a6b3d 201 subdev_8255_do_config(dev, s);
6ca27334 202
5dacadcc 203 return insn->n;
6ca27334
DS
204}
205
0a85b6f0
MT
206static int subdev_8255_cmdtest(struct comedi_device *dev,
207 struct comedi_subdevice *s,
208 struct comedi_cmd *cmd)
6ca27334
DS
209{
210 int err = 0;
6ca27334 211
27020ffe 212 /* Step 1 : check if triggers are trivially valid */
6ca27334 213
27020ffe
HS
214 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
215 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
216 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
217 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
218 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE);
6ca27334
DS
219
220 if (err)
221 return 1;
222
27020ffe
HS
223 /* Step 2a : make sure trigger sources are unique */
224 /* Step 2b : and mutually compatible */
6ca27334
DS
225
226 if (err)
227 return 2;
228
851eef80 229 /* Step 3: check if arguments are trivially valid */
6ca27334 230
851eef80
HS
231 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
232 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
233 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
f50cebb9 234 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
851eef80 235 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
6ca27334
DS
236
237 if (err)
238 return 3;
239
240 /* step 4 */
241
242 if (err)
243 return 4;
244
245 return 0;
246}
247
0a85b6f0
MT
248static int subdev_8255_cmd(struct comedi_device *dev,
249 struct comedi_subdevice *s)
6ca27334
DS
250{
251 /* FIXME */
252
253 return 0;
254}
255
0a85b6f0
MT
256static int subdev_8255_cancel(struct comedi_device *dev,
257 struct comedi_subdevice *s)
6ca27334
DS
258{
259 /* FIXME */
260
261 return 0;
262}
263
c5efe58b 264int subdev_8255_init(struct comedi_device *dev, struct comedi_subdevice *s,
b2815398 265 int (*io)(int, int, int, unsigned long),
d29a18dc 266 unsigned long iobase)
6ca27334 267{
7c61452a
HS
268 struct subdev_8255_private *spriv;
269
0480bcb9 270 spriv = comedi_alloc_spriv(s, sizeof(*spriv));
7c61452a 271 if (!spriv)
6ca27334
DS
272 return -ENOMEM;
273
cc31b1be
HS
274 spriv->iobase = iobase;
275 spriv->io = io ? io : subdev_8255_io;
276
cc31b1be
HS
277 s->type = COMEDI_SUBD_DIO;
278 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
279 s->n_chan = 24;
280 s->range_table = &range_digital;
281 s->maxdata = 1;
282 s->insn_bits = subdev_8255_insn;
283 s->insn_config = subdev_8255_insn_config;
284
cf8a6b3d 285 subdev_8255_do_config(dev, s);
6ca27334
DS
286
287 return 0;
288}
5660e742 289EXPORT_SYMBOL_GPL(subdev_8255_init);
6ca27334 290
c5efe58b 291int subdev_8255_init_irq(struct comedi_device *dev, struct comedi_subdevice *s,
b2815398 292 int (*io)(int, int, int, unsigned long),
d29a18dc 293 unsigned long iobase)
6ca27334
DS
294{
295 int ret;
296
d29a18dc 297 ret = subdev_8255_init(dev, s, io, iobase);
3e189f08 298 if (ret)
6ca27334
DS
299 return ret;
300
f50cebb9 301 s->len_chanlist = 1;
34cfcf9a
HS
302 s->do_cmdtest = subdev_8255_cmdtest;
303 s->do_cmd = subdev_8255_cmd;
304 s->cancel = subdev_8255_cancel;
6ca27334 305
6ca27334
DS
306 return 0;
307}
5660e742 308EXPORT_SYMBOL_GPL(subdev_8255_init_irq);
6ca27334 309
6ca27334
DS
310/*
311
312 Start of the 8255 standalone device
313
314 */
315
0a85b6f0
MT
316static int dev_8255_attach(struct comedi_device *dev,
317 struct comedi_devconfig *it)
6ca27334 318{
e40e8375 319 struct comedi_subdevice *s;
6ca27334
DS
320 int ret;
321 unsigned long iobase;
322 int i;
323
6ca27334
DS
324 for (i = 0; i < COMEDI_NDEVCONFOPTS; i++) {
325 iobase = it->options[i];
326 if (!iobase)
327 break;
328 }
329 if (i == 0) {
01bd3e3f 330 dev_warn(dev->class_dev, "no devices specified\n");
6ca27334
DS
331 return -EINVAL;
332 }
333
2f0b9d08 334 ret = comedi_alloc_subdevices(dev, i);
8b6c5694 335 if (ret)
6ca27334 336 return ret;
02638697 337
6ca27334 338 for (i = 0; i < dev->n_subdevices; i++) {
5101b4d1 339 s = &dev->subdevices[i];
6ca27334
DS
340 iobase = it->options[i];
341
e9720fd2
HS
342 ret = __comedi_request_region(dev, iobase, _8255_SIZE);
343 if (ret) {
e40e8375 344 s->type = COMEDI_SUBD_UNUSED;
6ca27334 345 } else {
3e189f08
HS
346 ret = subdev_8255_init(dev, s, NULL, iobase);
347 if (ret)
348 return ret;
6ca27334
DS
349 }
350 }
351
6ca27334
DS
352 return 0;
353}
354
484ecc95 355static void dev_8255_detach(struct comedi_device *dev)
6ca27334 356{
34c43922 357 struct comedi_subdevice *s;
7c61452a
HS
358 struct subdev_8255_private *spriv;
359 int i;
6ca27334 360
6ca27334 361 for (i = 0; i < dev->n_subdevices; i++) {
5101b4d1 362 s = &dev->subdevices[i];
6ca27334 363 if (s->type != COMEDI_SUBD_UNUSED) {
7c61452a 364 spriv = s->private;
7a583163 365 release_region(spriv->iobase, _8255_SIZE);
6ca27334 366 }
6ca27334 367 }
6ca27334 368}
90f703d3 369
294f930d 370static struct comedi_driver dev_8255_driver = {
3e699ed1
HS
371 .driver_name = "8255",
372 .module = THIS_MODULE,
373 .attach = dev_8255_attach,
374 .detach = dev_8255_detach,
375};
294f930d 376module_comedi_driver(dev_8255_driver);
3e699ed1 377
90f703d3
AT
378MODULE_AUTHOR("Comedi http://www.comedi.org");
379MODULE_DESCRIPTION("Comedi low-level driver");
380MODULE_LICENSE("GPL");
This page took 0.63788 seconds and 5 git commands to generate.