192494Ssobomax// SPDX-License-Identifier: GPL-2.0
292494Ssobomax/*
392494Ssobomax * pcl724.c
492494Ssobomax * Comedi driver for 8255 based ISA and PC/104 DIO boards
592494Ssobomax *
692494Ssobomax * Michal Dobes <dobes@tesnet.cz>
792494Ssobomax */
892494Ssobomax
992494Ssobomax/*
1092494Ssobomax * Driver: pcl724
1192494Ssobomax * Description: Comedi driver for 8255 based ISA DIO boards
1292494Ssobomax * Devices: [Advantech] PCL-724 (pcl724), PCL-722 (pcl722), PCL-731 (pcl731),
1392494Ssobomax *  [ADLink] ACL-7122 (acl7122), ACL-7124 (acl7124), PET-48DIO (pet48dio),
1492494Ssobomax *  [WinSystems] PCM-IO48 (pcmio48),
1592494Ssobomax *  [Diamond Systems] ONYX-MM-DIO (onyx-mm-dio)
1692494Ssobomax * Author: Michal Dobes <dobes@tesnet.cz>
1792494Ssobomax * Status: untested
1892494Ssobomax *
1992494Ssobomax * Configuration options:
2092494Ssobomax *   [0] - IO Base
2192494Ssobomax *   [1] - IRQ (not supported)
2292494Ssobomax *   [2] - number of DIO (pcl722 and acl7122 boards)
2392494Ssobomax *	   0, 144: 144 DIO configuration
2492494Ssobomax *	   1,  96:  96 DIO configuration
2592494Ssobomax */
2692494Ssobomax
2792494Ssobomax#include <linux/module.h>
2892494Ssobomax#include <linux/comedi/comedidev.h>
2992494Ssobomax#include <linux/comedi/comedi_8255.h>
3092494Ssobomax
3192494Ssobomaxstruct pcl724_board {
3292494Ssobomax	const char *name;
3392494Ssobomax	unsigned int io_range;
3492494Ssobomax	unsigned int can_have96:1;
3592494Ssobomax	unsigned int is_pet48:1;
3692494Ssobomax	int numofports;
3792494Ssobomax};
3892494Ssobomax
3992494Ssobomaxstatic const struct pcl724_board boardtypes[] = {
4092494Ssobomax	{
4192494Ssobomax		.name		= "pcl724",
4292494Ssobomax		.io_range	= 0x04,
4392494Ssobomax		.numofports	= 1,	/* 24 DIO channels */
4492494Ssobomax	}, {
4592494Ssobomax		.name		= "pcl722",
4692494Ssobomax		.io_range	= 0x20,
47124572Sjhb		.can_have96	= 1,
4892494Ssobomax		.numofports	= 6,	/* 144 (or 96) DIO channels */
4992494Ssobomax	}, {
5092494Ssobomax		.name		= "pcl731",
5192494Ssobomax		.io_range	= 0x08,
5292494Ssobomax		.numofports	= 2,	/* 48 DIO channels */
5392494Ssobomax	}, {
5492494Ssobomax		.name		= "acl7122",
5592494Ssobomax		.io_range	= 0x20,
5692494Ssobomax		.can_have96	= 1,
5792494Ssobomax		.numofports	= 6,	/* 144 (or 96) DIO channels */
5892494Ssobomax	}, {
5992494Ssobomax		.name		= "acl7124",
6092494Ssobomax		.io_range	= 0x04,
6192494Ssobomax		.numofports	= 1,	/* 24 DIO channels */
6292494Ssobomax	}, {
6392494Ssobomax		.name		= "pet48dio",
6492494Ssobomax		.io_range	= 0x02,
6592494Ssobomax		.is_pet48	= 1,
6692494Ssobomax		.numofports	= 2,	/* 48 DIO channels */
6792494Ssobomax	}, {
68124571Sjhb		.name		= "pcmio48",
6992494Ssobomax		.io_range	= 0x08,
70124571Sjhb		.numofports	= 2,	/* 48 DIO channels */
7192494Ssobomax	}, {
7292494Ssobomax		.name		= "onyx-mm-dio",
7392494Ssobomax		.io_range	= 0x10,
7492494Ssobomax		.numofports	= 2,	/* 48 DIO channels */
7592494Ssobomax	},
7692494Ssobomax};
77124571Sjhb
78124571Sjhbstatic int pcl724_8255mapped_io(struct comedi_device *dev,
7992494Ssobomax				int dir, int port, int data,
8092494Ssobomax				unsigned long iobase)
8192494Ssobomax{
82124572Sjhb	int movport = I8255_SIZE * (iobase >> 12);
83124572Sjhb
84124572Sjhb	iobase &= 0x0fff;
85124572Sjhb
86124572Sjhb	outb(port + movport, iobase);
87124572Sjhb	if (dir) {
88124572Sjhb		outb(data, iobase + 1);
89124572Sjhb		return 0;
90124572Sjhb	}
91124572Sjhb	return inb(iobase + 1);
92124572Sjhb}
93124572Sjhb
94124572Sjhbstatic int pcl724_attach(struct comedi_device *dev,
95124572Sjhb			 struct comedi_devconfig *it)
96124572Sjhb{
97124572Sjhb	const struct pcl724_board *board = dev->board_ptr;
98124572Sjhb	struct comedi_subdevice *s;
99124572Sjhb	unsigned long iobase;
100124572Sjhb	unsigned int iorange;
101124572Sjhb	int n_subdevices;
102124572Sjhb	int ret;
103124572Sjhb	int i;
10492494Ssobomax
10592494Ssobomax	iorange = board->io_range;
10692494Ssobomax	n_subdevices = board->numofports;
10792494Ssobomax
10892494Ssobomax	/* Handle PCL-724 in 96 DIO configuration */
10992494Ssobomax	if (board->can_have96 &&
11092494Ssobomax	    (it->options[2] == 1 || it->options[2] == 96)) {
11192494Ssobomax		iorange = 0x10;
11292494Ssobomax		n_subdevices = 4;
11392494Ssobomax	}
11492494Ssobomax
11592494Ssobomax	ret = comedi_request_region(dev, it->options[0], iorange);
11692494Ssobomax	if (ret)
11792494Ssobomax		return ret;
11892494Ssobomax
11992494Ssobomax	ret = comedi_alloc_subdevices(dev, n_subdevices);
12092494Ssobomax	if (ret)
12192494Ssobomax		return ret;
12292494Ssobomax
12392494Ssobomax	for (i = 0; i < dev->n_subdevices; i++) {
12492494Ssobomax		s = &dev->subdevices[i];
12592494Ssobomax		if (board->is_pet48) {
12692494Ssobomax			iobase = dev->iobase + (i * 0x1000);
12792494Ssobomax			ret = subdev_8255_cb_init(dev, s, pcl724_8255mapped_io,
12892494Ssobomax						  iobase);
12992494Ssobomax		} else {
13092494Ssobomax			ret = subdev_8255_io_init(dev, s, i * I8255_SIZE);
13192494Ssobomax		}
13292494Ssobomax		if (ret)
13392494Ssobomax			return ret;
13492494Ssobomax	}
13592494Ssobomax
13692494Ssobomax	return 0;
13792494Ssobomax}
13892494Ssobomax
13992494Ssobomaxstatic struct comedi_driver pcl724_driver = {
14092494Ssobomax	.driver_name	= "pcl724",
14192494Ssobomax	.module		= THIS_MODULE,
14292494Ssobomax	.attach		= pcl724_attach,
14392494Ssobomax	.detach		= comedi_legacy_detach,
14492494Ssobomax	.board_name	= &boardtypes[0].name,
14592494Ssobomax	.num_names	= ARRAY_SIZE(boardtypes),
14692494Ssobomax	.offset		= sizeof(struct pcl724_board),
14792494Ssobomax};
14892494Ssobomaxmodule_comedi_driver(pcl724_driver);
14992494Ssobomax
15092494SsobomaxMODULE_AUTHOR("Comedi https://www.comedi.org");
15192494SsobomaxMODULE_DESCRIPTION("Comedi driver for 8255 based ISA and PC/104 DIO boards");
15292494SsobomaxMODULE_LICENSE("GPL");
15392494Ssobomax