1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * comedi/drivers/8255.c
4 * Driver for 8255
5 *
6 * COMEDI - Linux Control and Measurement Device Interface
7 * Copyright (C) 1998 David A. Schleef <ds@schleef.org>
8 */
9
10/*
11 * Driver: 8255
12 * Description: generic 8255 support
13 * Devices: [standard] 8255 (8255)
14 * Author: ds
15 * Status: works
16 * Updated: Fri,  7 Jun 2002 12:56:45 -0700
17 *
18 * The classic in digital I/O.  The 8255 appears in Comedi as a single
19 * digital I/O subdevice with 24 channels.  The channel 0 corresponds
20 * to the 8255's port A, bit 0; channel 23 corresponds to port C, bit
21 * 7.  Direction configuration is done in blocks, with channels 0-7,
22 * 8-15, 16-19, and 20-23 making up the 4 blocks.  The only 8255 mode
23 * supported is mode 0.
24 *
25 * You should enable compilation this driver if you plan to use a board
26 * that has an 8255 chip.  For multifunction boards, the main driver will
27 * configure the 8255 subdevice automatically.
28 *
29 * This driver also works independently with ISA and PCI cards that
30 * directly map the 8255 registers to I/O ports, including cards with
31 * multiple 8255 chips.  To configure the driver for such a card, the
32 * option list should be a list of the I/O port bases for each of the
33 * 8255 chips.  For example,
34 *
35 *   comedi_config /dev/comedi0 8255 0x200,0x204,0x208,0x20c
36 *
37 * Note that most PCI 8255 boards do NOT work with this driver, and
38 * need a separate driver as a wrapper.  For those that do work, the
39 * I/O port base address can be found in the output of 'lspci -v'.
40 */
41
42#include <linux/module.h>
43#include <linux/comedi/comedidev.h>
44#include <linux/comedi/comedi_8255.h>
45
46static int dev_8255_attach(struct comedi_device *dev,
47			   struct comedi_devconfig *it)
48{
49	struct comedi_subdevice *s;
50	unsigned long iobase;
51	int ret;
52	int i;
53
54	for (i = 0; i < COMEDI_NDEVCONFOPTS; i++) {
55		iobase = it->options[i];
56		if (!iobase)
57			break;
58	}
59	if (i == 0) {
60		dev_warn(dev->class_dev, "no devices specified\n");
61		return -EINVAL;
62	}
63
64	ret = comedi_alloc_subdevices(dev, i);
65	if (ret)
66		return ret;
67
68	for (i = 0; i < dev->n_subdevices; i++) {
69		s = &dev->subdevices[i];
70		iobase = it->options[i];
71
72		/*
73		 * __comedi_request_region() does not set dev->iobase.
74		 *
75		 * For 8255 devices that are manually attached using
76		 * comedi_config, the 'iobase' is the actual I/O port
77		 * base address of the chip.
78		 */
79		ret = __comedi_request_region(dev, iobase, I8255_SIZE);
80		if (ret) {
81			s->type = COMEDI_SUBD_UNUSED;
82		} else {
83			ret = subdev_8255_io_init(dev, s, iobase);
84			if (ret) {
85				/*
86				 * Release the I/O port region here, as the
87				 * "detach" handler cannot find it.
88				 */
89				release_region(iobase, I8255_SIZE);
90				s->type = COMEDI_SUBD_UNUSED;
91				return ret;
92			}
93		}
94	}
95
96	return 0;
97}
98
99static void dev_8255_detach(struct comedi_device *dev)
100{
101	struct comedi_subdevice *s;
102	int i;
103
104	for (i = 0; i < dev->n_subdevices; i++) {
105		s = &dev->subdevices[i];
106		if (s->type != COMEDI_SUBD_UNUSED) {
107			unsigned long regbase = subdev_8255_regbase(s);
108
109			release_region(regbase, I8255_SIZE);
110		}
111	}
112}
113
114static struct comedi_driver dev_8255_driver = {
115	.driver_name	= "8255",
116	.module		= THIS_MODULE,
117	.attach		= dev_8255_attach,
118	.detach		= dev_8255_detach,
119};
120module_comedi_driver(dev_8255_driver);
121
122MODULE_AUTHOR("Comedi https://www.comedi.org");
123MODULE_DESCRIPTION("Comedi driver for standalone 8255 devices");
124MODULE_LICENSE("GPL");
125