1// SPDX-License-Identifier: GPL-2.0
2/*
3 * pcl724.c
4 * Comedi driver for 8255 based ISA and PC/104 DIO boards
5 *
6 * Michal Dobes <dobes@tesnet.cz>
7 */
8
9/*
10 * Driver: pcl724
11 * Description: Comedi driver for 8255 based ISA DIO boards
12 * Devices: [Advantech] PCL-724 (pcl724), PCL-722 (pcl722), PCL-731 (pcl731),
13 *  [ADLink] ACL-7122 (acl7122), ACL-7124 (acl7124), PET-48DIO (pet48dio),
14 *  [WinSystems] PCM-IO48 (pcmio48),
15 *  [Diamond Systems] ONYX-MM-DIO (onyx-mm-dio)
16 * Author: Michal Dobes <dobes@tesnet.cz>
17 * Status: untested
18 *
19 * Configuration options:
20 *   [0] - IO Base
21 *   [1] - IRQ (not supported)
22 *   [2] - number of DIO (pcl722 and acl7122 boards)
23 *	   0, 144: 144 DIO configuration
24 *	   1,  96:  96 DIO configuration
25 */
26
27#include <linux/module.h>
28#include <linux/comedi/comedidev.h>
29#include <linux/comedi/comedi_8255.h>
30
31struct pcl724_board {
32	const char *name;
33	unsigned int io_range;
34	unsigned int can_have96:1;
35	unsigned int is_pet48:1;
36	int numofports;
37};
38
39static const struct pcl724_board boardtypes[] = {
40	{
41		.name		= "pcl724",
42		.io_range	= 0x04,
43		.numofports	= 1,	/* 24 DIO channels */
44	}, {
45		.name		= "pcl722",
46		.io_range	= 0x20,
47		.can_have96	= 1,
48		.numofports	= 6,	/* 144 (or 96) DIO channels */
49	}, {
50		.name		= "pcl731",
51		.io_range	= 0x08,
52		.numofports	= 2,	/* 48 DIO channels */
53	}, {
54		.name		= "acl7122",
55		.io_range	= 0x20,
56		.can_have96	= 1,
57		.numofports	= 6,	/* 144 (or 96) DIO channels */
58	}, {
59		.name		= "acl7124",
60		.io_range	= 0x04,
61		.numofports	= 1,	/* 24 DIO channels */
62	}, {
63		.name		= "pet48dio",
64		.io_range	= 0x02,
65		.is_pet48	= 1,
66		.numofports	= 2,	/* 48 DIO channels */
67	}, {
68		.name		= "pcmio48",
69		.io_range	= 0x08,
70		.numofports	= 2,	/* 48 DIO channels */
71	}, {
72		.name		= "onyx-mm-dio",
73		.io_range	= 0x10,
74		.numofports	= 2,	/* 48 DIO channels */
75	},
76};
77
78static int pcl724_8255mapped_io(struct comedi_device *dev,
79				int dir, int port, int data,
80				unsigned long iobase)
81{
82	int movport = I8255_SIZE * (iobase >> 12);
83
84	iobase &= 0x0fff;
85
86	outb(port + movport, iobase);
87	if (dir) {
88		outb(data, iobase + 1);
89		return 0;
90	}
91	return inb(iobase + 1);
92}
93
94static int pcl724_attach(struct comedi_device *dev,
95			 struct comedi_devconfig *it)
96{
97	const struct pcl724_board *board = dev->board_ptr;
98	struct comedi_subdevice *s;
99	unsigned long iobase;
100	unsigned int iorange;
101	int n_subdevices;
102	int ret;
103	int i;
104
105	iorange = board->io_range;
106	n_subdevices = board->numofports;
107
108	/* Handle PCL-724 in 96 DIO configuration */
109	if (board->can_have96 &&
110	    (it->options[2] == 1 || it->options[2] == 96)) {
111		iorange = 0x10;
112		n_subdevices = 4;
113	}
114
115	ret = comedi_request_region(dev, it->options[0], iorange);
116	if (ret)
117		return ret;
118
119	ret = comedi_alloc_subdevices(dev, n_subdevices);
120	if (ret)
121		return ret;
122
123	for (i = 0; i < dev->n_subdevices; i++) {
124		s = &dev->subdevices[i];
125		if (board->is_pet48) {
126			iobase = dev->iobase + (i * 0x1000);
127			ret = subdev_8255_cb_init(dev, s, pcl724_8255mapped_io,
128						  iobase);
129		} else {
130			ret = subdev_8255_io_init(dev, s, i * I8255_SIZE);
131		}
132		if (ret)
133			return ret;
134	}
135
136	return 0;
137}
138
139static struct comedi_driver pcl724_driver = {
140	.driver_name	= "pcl724",
141	.module		= THIS_MODULE,
142	.attach		= pcl724_attach,
143	.detach		= comedi_legacy_detach,
144	.board_name	= &boardtypes[0].name,
145	.num_names	= ARRAY_SIZE(boardtypes),
146	.offset		= sizeof(struct pcl724_board),
147};
148module_comedi_driver(pcl724_driver);
149
150MODULE_AUTHOR("Comedi https://www.comedi.org");
151MODULE_DESCRIPTION("Comedi driver for 8255 based ISA and PC/104 DIO boards");
152MODULE_LICENSE("GPL");
153