Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
8255.c
Go to the documentation of this file.
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 <[email protected]>
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, io_function, iobase)
64 
65  device and subdevice are pointers to the device and subdevice
66  structures. io_function will be called to provide the
67  low-level input/output to the device, i.e., actual register
68  access. io_function will be called with the value of iobase
69  as the last parameter. If the 8255 device is mapped as 4
70  consecutive I/O ports, you can use NULL for io_function
71  and the I/O port base for iobase, 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 #include "comedi_fc.h"
87 #include "8255.h"
88 
89 #define _8255_SIZE 4
90 
91 #define _8255_DATA 0
92 #define _8255_CR 3
93 
94 #define CR_C_LO_IO 0x01
95 #define CR_B_IO 0x02
96 #define CR_B_MODE 0x04
97 #define CR_C_HI_IO 0x08
98 #define CR_A_IO 0x10
99 #define CR_A_MODE(a) ((a)<<5)
100 #define CR_CW 0x80
101 
103  unsigned long iobase;
104  int (*io) (int, int, int, unsigned long);
105 };
106 
107 static int subdev_8255_io(int dir, int port, int data, unsigned long iobase)
108 {
109  if (dir) {
110  outb(data, iobase + port);
111  return 0;
112  } else {
113  return inb(iobase + port);
114  }
115 }
116 
118  struct comedi_subdevice *s)
119 {
120  struct subdev_8255_private *spriv = s->private;
121  unsigned long iobase = spriv->iobase;
122  short d;
123 
124  d = spriv->io(0, _8255_DATA, 0, iobase);
125  d |= (spriv->io(0, _8255_DATA + 1, 0, iobase) << 8);
126 
127  comedi_buf_put(s->async, d);
128  s->async->events |= COMEDI_CB_EOS;
129 
130  comedi_event(dev, s);
131 }
133 
134 static int subdev_8255_insn(struct comedi_device *dev,
135  struct comedi_subdevice *s,
136  struct comedi_insn *insn, unsigned int *data)
137 {
138  struct subdev_8255_private *spriv = s->private;
139  unsigned long iobase = spriv->iobase;
140  unsigned int mask;
141  unsigned int bits;
142  unsigned int v;
143 
144  mask = data[0];
145  bits = data[1];
146 
147  if (mask) {
148  v = s->state;
149  v &= ~mask;
150  v |= (bits & mask);
151 
152  if (mask & 0xff)
153  spriv->io(1, _8255_DATA, v & 0xff, iobase);
154  if (mask & 0xff00)
155  spriv->io(1, _8255_DATA + 1, (v >> 8) & 0xff, iobase);
156  if (mask & 0xff0000)
157  spriv->io(1, _8255_DATA + 2, (v >> 16) & 0xff, iobase);
158 
159  s->state = v;
160  }
161 
162  v = spriv->io(0, _8255_DATA, 0, iobase);
163  v |= (spriv->io(0, _8255_DATA + 1, 0, iobase) << 8);
164  v |= (spriv->io(0, _8255_DATA + 2, 0, iobase) << 16);
165 
166  data[1] = v;
167 
168  return insn->n;
169 }
170 
171 static void subdev_8255_do_config(struct comedi_device *dev,
172  struct comedi_subdevice *s)
173 {
174  struct subdev_8255_private *spriv = s->private;
175  unsigned long iobase = spriv->iobase;
176  int config;
177 
178  config = CR_CW;
179  /* 1 in io_bits indicates output, 1 in config indicates input */
180  if (!(s->io_bits & 0x0000ff))
181  config |= CR_A_IO;
182  if (!(s->io_bits & 0x00ff00))
183  config |= CR_B_IO;
184  if (!(s->io_bits & 0x0f0000))
185  config |= CR_C_LO_IO;
186  if (!(s->io_bits & 0xf00000))
187  config |= CR_C_HI_IO;
188 
189  spriv->io(1, _8255_CR, config, iobase);
190 }
191 
192 static int subdev_8255_insn_config(struct comedi_device *dev,
193  struct comedi_subdevice *s,
194  struct comedi_insn *insn, unsigned int *data)
195 {
196  unsigned int mask;
197  unsigned int bits;
198 
199  mask = 1 << CR_CHAN(insn->chanspec);
200  if (mask & 0x0000ff)
201  bits = 0x0000ff;
202  else if (mask & 0x00ff00)
203  bits = 0x00ff00;
204  else if (mask & 0x0f0000)
205  bits = 0x0f0000;
206  else
207  bits = 0xf00000;
208 
209  switch (data[0]) {
211  s->io_bits &= ~bits;
212  break;
214  s->io_bits |= bits;
215  break;
217  data[1] = (s->io_bits & bits) ? COMEDI_OUTPUT : COMEDI_INPUT;
218  return insn->n;
219  break;
220  default:
221  return -EINVAL;
222  }
223 
224  subdev_8255_do_config(dev, s);
225 
226  return 1;
227 }
228 
229 static int subdev_8255_cmdtest(struct comedi_device *dev,
230  struct comedi_subdevice *s,
231  struct comedi_cmd *cmd)
232 {
233  int err = 0;
234 
235  /* Step 1 : check if triggers are trivially valid */
236 
237  err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
238  err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
239  err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
240  err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
241  err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE);
242 
243  if (err)
244  return 1;
245 
246  /* Step 2a : make sure trigger sources are unique */
247  /* Step 2b : and mutually compatible */
248 
249  if (err)
250  return 2;
251 
252  /* step 3 */
253 
254  if (cmd->start_arg != 0) {
255  cmd->start_arg = 0;
256  err++;
257  }
258  if (cmd->scan_begin_arg != 0) {
259  cmd->scan_begin_arg = 0;
260  err++;
261  }
262  if (cmd->convert_arg != 0) {
263  cmd->convert_arg = 0;
264  err++;
265  }
266  if (cmd->scan_end_arg != 1) {
267  cmd->scan_end_arg = 1;
268  err++;
269  }
270  if (cmd->stop_arg != 0) {
271  cmd->stop_arg = 0;
272  err++;
273  }
274 
275  if (err)
276  return 3;
277 
278  /* step 4 */
279 
280  if (err)
281  return 4;
282 
283  return 0;
284 }
285 
286 static int subdev_8255_cmd(struct comedi_device *dev,
287  struct comedi_subdevice *s)
288 {
289  /* FIXME */
290 
291  return 0;
292 }
293 
294 static int subdev_8255_cancel(struct comedi_device *dev,
295  struct comedi_subdevice *s)
296 {
297  /* FIXME */
298 
299  return 0;
300 }
301 
303  int (*io) (int, int, int, unsigned long),
304  unsigned long iobase)
305 {
306  struct subdev_8255_private *spriv;
307 
308  spriv = kzalloc(sizeof(*spriv), GFP_KERNEL);
309  if (!spriv)
310  return -ENOMEM;
311 
312  spriv->iobase = iobase;
313  spriv->io = io ? io : subdev_8255_io;
314 
315  s->private = spriv;
316 
317  s->type = COMEDI_SUBD_DIO;
319  s->n_chan = 24;
321  s->maxdata = 1;
322  s->insn_bits = subdev_8255_insn;
323  s->insn_config = subdev_8255_insn_config;
324 
325  s->state = 0;
326  s->io_bits = 0;
327 
328  subdev_8255_do_config(dev, s);
329 
330  return 0;
331 }
333 
335  int (*io) (int, int, int, unsigned long),
336  unsigned long iobase)
337 {
338  int ret;
339 
340  ret = subdev_8255_init(dev, s, io, iobase);
341  if (ret)
342  return ret;
343 
344  s->do_cmdtest = subdev_8255_cmdtest;
345  s->do_cmd = subdev_8255_cmd;
346  s->cancel = subdev_8255_cancel;
347 
348  return 0;
349 }
351 
353 {
354  kfree(s->private);
355 }
357 
358 /*
359 
360  Start of the 8255 standalone device
361 
362  */
363 
364 static int dev_8255_attach(struct comedi_device *dev,
365  struct comedi_devconfig *it)
366 {
367  struct comedi_subdevice *s;
368  int ret;
369  unsigned long iobase;
370  int i;
371 
372  dev->board_name = "8255";
373 
374  for (i = 0; i < COMEDI_NDEVCONFOPTS; i++) {
375  iobase = it->options[i];
376  if (!iobase)
377  break;
378  }
379  if (i == 0) {
380  dev_warn(dev->class_dev, "no devices specified\n");
381  return -EINVAL;
382  }
383 
384  ret = comedi_alloc_subdevices(dev, i);
385  if (ret)
386  return ret;
387 
388  for (i = 0; i < dev->n_subdevices; i++) {
389  s = &dev->subdevices[i];
390  iobase = it->options[i];
391 
392  if (!request_region(iobase, _8255_SIZE, "8255")) {
393  dev_warn(dev->class_dev,
394  "0x%04lx (I/O port conflict)\n", iobase);
395 
397  } else {
398  ret = subdev_8255_init(dev, s, NULL, iobase);
399  if (ret)
400  return ret;
401  dev_info(dev->class_dev, "0x%04lx\n", iobase);
402  }
403  }
404 
405  return 0;
406 }
407 
408 static void dev_8255_detach(struct comedi_device *dev)
409 {
410  struct comedi_subdevice *s;
411  struct subdev_8255_private *spriv;
412  int i;
413 
414  for (i = 0; i < dev->n_subdevices; i++) {
415  s = &dev->subdevices[i];
416  if (s->type != COMEDI_SUBD_UNUSED) {
417  spriv = s->private;
419  }
420  subdev_8255_cleanup(dev, s);
421  }
422 }
423 
424 static struct comedi_driver dev_8255_driver = {
425  .driver_name = "8255",
426  .module = THIS_MODULE,
427  .attach = dev_8255_attach,
428  .detach = dev_8255_detach,
429 };
430 module_comedi_driver(dev_8255_driver);
431 
432 MODULE_AUTHOR("Comedi http://www.comedi.org");
433 MODULE_DESCRIPTION("Comedi low-level driver");
434 MODULE_LICENSE("GPL");