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