1139749Simp/*-
2136230Simp *  Copyright (c) 2000-2004
3136230Simp *          Diomidis D. Spinellis, Athens, Greece
4136230Simp *      All rights reserved.
5136230Simp *
6136230Simp *  Redistribution and use in source and binary forms, with or without
7136230Simp *  modification, are permitted provided that the following conditions
8136230Simp *  are met:
9136230Simp *  1. Redistributions of source code must retain the above copyright
10136230Simp *     notice, this list of conditions and the following disclaimer as
11136230Simp *     the first lines of this file unmodified.
12136230Simp *  2. Redistributions in binary form must reproduce the above copyright
13136230Simp *     notice, this list of conditions and the following disclaimer in the
14136230Simp *     documentation and/or other materials provided with the distribution.
15136230Simp *
16136230Simp *  THIS SOFTWARE IS PROVIDED BY Diomidis D. Spinellis ``AS IS'' AND ANY
17136230Simp *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18136230Simp *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19136230Simp *  PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL Diomidis D. Spinellis BE
20136230Simp *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21136230Simp *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22136230Simp *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23136230Simp *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24136230Simp *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25136230Simp *  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
26136230Simp *  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27136230Simp *
28136230Simp * $Id: pbio.c,v 1.12 2003/10/11 13:05:08 dds Exp dds $
29136230Simp */
30136230Simp
31136230Simp#include <sys/cdefs.h>
32136305Sobrien__FBSDID("$FreeBSD$");
33136305Sobrien
34136230Simp#include <sys/param.h>
35136230Simp#include <sys/systm.h>
36136230Simp#include <sys/kernel.h>		/* SYSINIT stuff */
37136230Simp#include <sys/bus.h>
38136230Simp#include <sys/resource.h>
39136230Simp#include <sys/syslog.h>
40136230Simp#include <sys/sysctl.h>
41136230Simp#include <sys/conf.h>		/* cdevsw stuff */
42136230Simp#include <sys/malloc.h>		/* malloc region definitions */
43136230Simp#include <sys/module.h>
44136230Simp#include <machine/bus.h>
45136230Simp#include <machine/resource.h>
46136230Simp#include <sys/rman.h>
47137585Sobrien#include <dev/pbio/pbioio.h>		/* pbio IOCTL definitions */
48136230Simp#include <sys/uio.h>
49136230Simp#include <sys/fcntl.h>
50141680Simp#include <isa/isavar.h>
51136230Simp
52136230Simp/* Function prototypes (these should all be static) */
53136305Sobrienstatic	d_open_t	pbioopen;
54136305Sobrienstatic	d_close_t	pbioclose;
55136305Sobrienstatic	d_read_t	pbioread;
56136305Sobrienstatic	d_write_t	pbiowrite;
57136305Sobrienstatic	d_ioctl_t	pbioioctl;
58136305Sobrienstatic	d_poll_t	pbiopoll;
59136230Simpstatic	int		pbioprobe(device_t);
60136230Simpstatic	int		pbioattach(device_t);
61136230Simp
62136230Simp/* Device registers */
63136230Simp#define	PBIO_PORTA	0
64136230Simp#define	PBIO_PORTB	1
65136230Simp#define	PBIO_PORTC	2
66136230Simp#define	PBIO_CFG	3
67136305Sobrien#define	PBIO_IOSIZE	4
68136230Simp
69136230Simp/* Per-port buffer size */
70136305Sobrien#define	PBIO_BUFSIZ 64
71136230Simp
72136230Simp/* Number of /dev entries */
73136305Sobrien#define	PBIO_NPORTS 4
74136230Simp
75136230Simp/* I/O port range */
76136305Sobrien#define	IO_PBIOSIZE 4
77136230Simp
78136230Simpstatic char *port_names[] = {"a", "b", "ch", "cl"};
79136230Simp
80136230Simp#define	PBIO_PNAME(n)		(port_names[(n)])
81136230Simp
82183397Sed#define	UNIT(dev)		(dev2unit(dev) >> 2)
83183397Sed#define	PORT(dev)		(dev2unit(dev) & 0x3)
84136230Simp
85136230Simp#define	PBIOPRI	((PZERO + 5) | PCATCH)
86136230Simp
87136230Simpstatic struct cdevsw pbio_cdevsw = {
88136230Simp	.d_version = D_VERSION,
89136230Simp	.d_flags = D_NEEDGIANT,
90136230Simp	.d_open = pbioopen,
91136230Simp	.d_close = pbioclose,
92136230Simp	.d_read = pbioread,
93136230Simp	.d_write = pbiowrite,
94136230Simp	.d_ioctl = pbioioctl,
95136230Simp	.d_poll = pbiopoll,
96136230Simp	.d_name = "pbio"
97136230Simp};
98136230Simp
99136230Simp/*
100136230Simp * Data specific to each I/O port
101136230Simp */
102136230Simpstruct portdata {
103136230Simp	struct cdev *port;
104136230Simp	int	diff;			/* When true read only differences */
105136230Simp	int	ipace;			/* Input pace */
106136230Simp	int	opace;			/* Output pace */
107136230Simp	char	oldval;			/* Last value read */
108136230Simp	char	buff[PBIO_BUFSIZ];	/* Per-port data buffer */
109136230Simp};
110136230Simp
111136230Simp/*
112136230Simp * One of these per allocated device
113136230Simp */
114136230Simpstruct pbio_softc {
115136305Sobrien	struct portdata pd[PBIO_NPORTS];/* Per port data */
116136230Simp	int	iomode;			/* Virtualized I/O mode port value */
117136230Simp					/* The real port is write-only */
118136341Simp	struct resource *res;
119136341Simp	bus_space_tag_t bst;
120136341Simp	bus_space_handle_t bsh;
121136341Simp};
122136230Simp
123136230Simptypedef	struct pbio_softc *sc_p;
124136230Simp
125136230Simpstatic device_method_t pbio_methods[] = {
126136230Simp	/* Device interface */
127136230Simp	DEVMETHOD(device_probe,		pbioprobe),
128136230Simp	DEVMETHOD(device_attach,	pbioattach),
129136230Simp	{ 0, 0 }
130136230Simp};
131136230Simp
132136230Simpstatic	devclass_t	pbio_devclass;
133136305Sobrien#define	pbio_addr(unit) \
134136305Sobrien	    ((struct pbio_softc *) devclass_get_softc(pbio_devclass, unit))
135136230Simp
136136230Simpstatic char driver_name[] = "pbio";
137136230Simp
138136230Simpstatic driver_t pbio_driver = {
139136230Simp	driver_name,
140136230Simp	pbio_methods,
141136230Simp	sizeof(struct pbio_softc),
142136230Simp};
143136230Simp
144136230SimpDRIVER_MODULE(pbio, isa, pbio_driver, pbio_devclass, 0, 0);
145136230Simp
146136341Simpstatic __inline uint8_t
147136341Simppbinb(struct pbio_softc *scp, int off)
148136341Simp{
149136341Simp
150136341Simp	return bus_space_read_1(scp->bst, scp->bsh, off);
151136341Simp}
152136341Simp
153136341Simpstatic __inline void
154136341Simppboutb(struct pbio_softc *scp, int off, uint8_t val)
155136341Simp{
156136341Simp
157136341Simp	bus_space_write_1(scp->bst, scp->bsh, off, val);
158136341Simp}
159136341Simp
160136341Simp
161136230Simpstatic int
162136230Simppbioprobe(device_t dev)
163136230Simp{
164136341Simp	int		rid;
165136341Simp	struct pbio_softc *scp = device_get_softc(dev);
166136230Simp#ifdef GENERIC_PBIO_PROBE
167136230Simp	unsigned char val;
168136230Simp#endif
169136230Simp
170141680Simp	if (isa_get_logicalid(dev))		/* skip PnP probes */
171141680Simp		return (ENXIO);
172136230Simp	rid = 0;
173136341Simp	scp->res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
174136341Simp	    0, ~0, IO_PBIOSIZE, RF_ACTIVE);
175136341Simp	if (scp->res == NULL)
176136230Simp		return (ENXIO);
177136230Simp
178136230Simp#ifdef GENERIC_PBIO_PROBE
179136341Simp	scp->bst = rman_get_bustag(scp->res);
180136341Simp	scp->bsh = rman_get_bushandle(scp->res);
181136230Simp	/*
182136230Simp	 * try see if the device is there.
183136230Simp	 * This probe works only if the device has no I/O attached to it
184136230Simp	 * XXX Better allow flags to abort testing
185136230Simp	 */
186136230Simp	/* Set all ports to output */
187136341Simp	pboutb(scp, PBIO_CFG, 0x80);
188136230Simp	printf("pbio val(CFG: 0x%03x)=0x%02x (should be 0x80)\n",
189136362Simp		rman_get_start(scp->res), pbinb(scp, PBIO_CFG));
190136341Simp	pboutb(scp, PBIO_PORTA, 0xa5);
191136341Simp	val = pbinb(scp, PBIO_PORTA);
192136230Simp	printf("pbio val=0x%02x (should be 0xa5)\n", val);
193136341Simp	if (val != 0xa5) {
194136341Simp		bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->res);
195136230Simp		return (ENXIO);
196136341Simp	}
197136341Simp	pboutb(scp, PBIO_PORTA, 0x5a);
198136341Simp	val = pbinb(scp, PBIO_PORTA);
199136230Simp	printf("pbio val=0x%02x (should be 0x5a)\n", val);
200136341Simp	if (val != 0x5a) {
201136341Simp		bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->res);
202136230Simp		return (ENXIO);
203136341Simp	}
204136230Simp#endif
205136230Simp	device_set_desc(dev, "Intel 8255 PPI (basic mode)");
206136230Simp	/* Set all ports to input */
207136341Simp	/* pboutb(scp, PBIO_CFG, 0x9b); */
208136341Simp	bus_release_resource(dev, SYS_RES_IOPORT, rid, scp->res);
209136230Simp	return (0);
210136230Simp}
211136230Simp
212136230Simp/*
213136230Simp * Called if the probe succeeded.
214136230Simp * We can be destructive here as we know we have the device.
215136230Simp * we can also trust the unit number.
216136230Simp */
217136230Simpstatic int
218136230Simppbioattach (device_t dev)
219136230Simp{
220136341Simp	int unit;
221136341Simp	int i;
222136341Simp	int		rid;
223136230Simp	struct pbio_softc *sc;
224136230Simp
225136341Simp	sc = device_get_softc(dev);
226136305Sobrien	unit = device_get_unit(dev);
227136230Simp	rid = 0;
228136341Simp	sc->res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
229136341Simp	    0, ~0, IO_PBIOSIZE, RF_ACTIVE);
230136341Simp	if (sc->res == NULL)
231136230Simp		return (ENXIO);
232136341Simp	sc->bst = rman_get_bustag(sc->res);
233136341Simp	sc->bsh = rman_get_bushandle(sc->res);
234136230Simp
235136230Simp	/*
236136230Simp	 * Store whatever seems wise.
237136230Simp	 */
238136230Simp	sc->iomode = 0x9b;		/* All ports to input */
239136230Simp
240136230Simp	for (i = 0; i < PBIO_NPORTS; i++)
241136305Sobrien		sc->pd[i].port = make_dev(&pbio_cdevsw, (unit << 2) + i, 0, 0,
242136305Sobrien		    0600, "pbio%d%s", unit, PBIO_PNAME(i));
243136305Sobrien	return (0);
244136230Simp}
245136230Simp
246136230Simpstatic int
247136230Simppbioioctl (struct cdev *dev, u_long cmd, caddr_t data, int flag,
248136230Simp    struct thread *td)
249136230Simp{
250136230Simp	struct pbio_softc *scp;
251136305Sobrien	int port, unit;
252136230Simp
253136305Sobrien	unit = UNIT(dev);
254136305Sobrien	port = PORT(dev);
255136230Simp	scp = pbio_addr(unit);
256136230Simp	if (scp == NULL)
257136230Simp		return (ENODEV);
258136230Simp	switch (cmd) {
259136305Sobrien	case PBIO_SETDIFF:
260136230Simp		scp->pd[port].diff = *(int *)data;
261136230Simp		break;
262136305Sobrien	case PBIO_SETIPACE:
263136230Simp		scp->pd[port].ipace = *(int *)data;
264136230Simp		break;
265136305Sobrien	case PBIO_SETOPACE:
266136230Simp		scp->pd[port].opace = *(int *)data;
267136230Simp		break;
268136305Sobrien	case PBIO_GETDIFF:
269136230Simp		*(int *)data = scp->pd[port].diff;
270136230Simp		break;
271136305Sobrien	case PBIO_GETIPACE:
272136230Simp		*(int *)data = scp->pd[port].ipace;
273136230Simp		break;
274136305Sobrien	case PBIO_GETOPACE:
275136230Simp		*(int *)data = scp->pd[port].opace;
276136230Simp		break;
277136305Sobrien	default:
278136230Simp		return ENXIO;
279136230Simp	}
280136230Simp	return (0);
281136230Simp}
282136230Simp
283136230Simpstatic  int
284136230Simppbioopen(struct cdev *dev, int oflags, int devtype, struct thread *td)
285136230Simp{
286136230Simp	struct pbio_softc *scp;
287136305Sobrien	int ocfg, port, unit;
288136230Simp	int portbit;			/* Port configuration bit */
289136230Simp
290136305Sobrien	unit = UNIT(dev);
291136305Sobrien	port = PORT(dev);
292136230Simp	scp = pbio_addr(unit);
293136230Simp	if (scp == NULL)
294136230Simp		return (ENODEV);
295136230Simp
296136230Simp	switch (port) {
297136230Simp	case 0: portbit = 0x10; break;	/* Port A */
298136230Simp	case 1: portbit = 0x02; break;	/* Port B */
299136230Simp	case 2: portbit = 0x08; break;	/* Port CH */
300136230Simp	case 3: portbit = 0x01; break;	/* Port CL */
301136230Simp	default: return (ENODEV);
302136230Simp	}
303136230Simp	ocfg = scp->iomode;
304136230Simp
305136230Simp	if (oflags & FWRITE)
306136230Simp		/* Writing == output; zero the bit */
307136341Simp		pboutb(scp, PBIO_CFG, scp->iomode = (ocfg & (~portbit)));
308136230Simp	else if (oflags & FREAD)
309136230Simp		/* Reading == input; set the bit */
310136341Simp		pboutb(scp, PBIO_CFG, scp->iomode = (ocfg | portbit));
311136230Simp	else
312136230Simp		return (EACCES);
313136230Simp
314136230Simp	return (0);
315136230Simp}
316136230Simp
317136230Simpstatic  int
318136230Simppbioclose(struct cdev *dev, int fflag, int devtype, struct thread *td)
319136230Simp{
320136230Simp	struct pbio_softc *scp;
321136305Sobrien	int unit;
322136230Simp
323136305Sobrien	unit = UNIT(dev);
324136230Simp	scp = pbio_addr(unit);
325136230Simp	if (scp == NULL)
326136230Simp		return (ENODEV);
327136230Simp
328136230Simp	return (0);
329136230Simp}
330136230Simp
331136230Simp/*
332136230Simp * Return the value of a given port on a given I/O base address
333136230Simp * Handles the split C port nibbles and blocking
334136230Simp */
335136230Simpstatic int
336136230Simpportval(int port, struct pbio_softc *scp, char *val)
337136230Simp{
338136230Simp	int err;
339136230Simp
340136230Simp	for (;;) {
341136230Simp		switch (port) {
342136230Simp		case 0:
343136341Simp			*val = pbinb(scp, PBIO_PORTA);
344136230Simp			break;
345136230Simp		case 1:
346136341Simp			*val = pbinb(scp, PBIO_PORTB);
347136230Simp			break;
348136230Simp		case 2:
349136341Simp			*val = (pbinb(scp, PBIO_PORTC) >> 4) & 0xf;
350136230Simp			break;
351136230Simp		case 3:
352136341Simp			*val = pbinb(scp, PBIO_PORTC) & 0xf;
353136230Simp			break;
354136230Simp		default:
355136230Simp			*val = 0;
356136230Simp			break;
357136230Simp		}
358136230Simp		if (scp->pd[port].diff) {
359136230Simp			if (*val != scp->pd[port].oldval) {
360136230Simp				scp->pd[port].oldval = *val;
361136230Simp				return (0);
362136230Simp			}
363136230Simp			err = tsleep((caddr_t)&(scp->pd[port].diff), PBIOPRI,
364136230Simp				     "pbiopl", max(1, scp->pd[port].ipace));
365136230Simp			if (err == EINTR)
366136230Simp				return (EINTR);
367136230Simp		} else
368136230Simp			return (0);
369136230Simp	}
370136230Simp}
371136230Simp
372136230Simpstatic  int
373136230Simppbioread(struct cdev *dev, struct uio *uio, int ioflag)
374136230Simp{
375136230Simp	struct pbio_softc *scp;
376136305Sobrien	int err, i, port, ret, toread, unit;
377136230Simp	char val;
378136230Simp
379136305Sobrien	unit = UNIT(dev);
380136305Sobrien	port = PORT(dev);
381136230Simp	scp = pbio_addr(unit);
382136230Simp	if (scp == NULL)
383136230Simp		return (ENODEV);
384136230Simp
385136230Simp	while (uio->uio_resid > 0) {
386136230Simp		toread = min(uio->uio_resid, PBIO_BUFSIZ);
387136230Simp		if ((ret = uiomove(scp->pd[port].buff, toread, uio)) != 0)
388136230Simp			return (ret);
389136230Simp		for (i = 0; i < toread; i++) {
390136230Simp			if ((err = portval(port, scp, &val)) != 0)
391136230Simp				return (err);
392136230Simp			scp->pd[port].buff[i] = val;
393136230Simp			if (!scp->pd[port].diff && scp->pd[port].ipace)
394136230Simp				tsleep((caddr_t)&(scp->pd[port].ipace), PBIOPRI,
395136230Simp					"pbioip", scp->pd[port].ipace);
396136230Simp		}
397136230Simp	}
398136230Simp	return 0;
399136230Simp}
400136230Simp
401136305Sobrienstatic int
402136230Simppbiowrite(struct cdev *dev, struct uio *uio, int ioflag)
403136230Simp{
404136230Simp	struct pbio_softc *scp;
405136362Simp	int i, port, ret, towrite, unit;
406136230Simp	char val, oval;
407136230Simp
408136305Sobrien	unit = UNIT(dev);
409136305Sobrien	port = PORT(dev);
410136230Simp	scp = pbio_addr(unit);
411136230Simp	if (scp == NULL)
412136230Simp		return (ENODEV);
413136230Simp
414136230Simp	while (uio->uio_resid > 0) {
415136230Simp		towrite = min(uio->uio_resid, PBIO_BUFSIZ);
416136230Simp		if ((ret = uiomove(scp->pd[port].buff, towrite, uio)) != 0)
417136230Simp			return (ret);
418136230Simp		for (i = 0; i < towrite; i++) {
419136230Simp			val = scp->pd[port].buff[i];
420136230Simp			switch (port) {
421136230Simp			case 0:
422136341Simp				pboutb(scp, PBIO_PORTA, val);
423136230Simp				break;
424136230Simp			case 1:
425136341Simp				pboutb(scp, PBIO_PORTB, val);
426136230Simp				break;
427136230Simp			case 2:
428136341Simp				oval = pbinb(scp, PBIO_PORTC);
429136230Simp				oval &= 0xf;
430136230Simp				val <<= 4;
431136341Simp				pboutb(scp, PBIO_PORTC, val | oval);
432136230Simp				break;
433136230Simp			case 3:
434136341Simp				oval = pbinb(scp, PBIO_PORTC);
435136230Simp				oval &= 0xf0;
436136230Simp				val &= 0xf;
437136341Simp				pboutb(scp, PBIO_PORTC, oval | val);
438136230Simp				break;
439136230Simp			}
440136230Simp			if (scp->pd[port].opace)
441136230Simp				tsleep((caddr_t)&(scp->pd[port].opace),
442136230Simp					PBIOPRI, "pbioop",
443136230Simp					scp->pd[port].opace);
444136230Simp		}
445136230Simp	}
446136230Simp	return (0);
447136230Simp}
448136230Simp
449136230Simpstatic  int
450136230Simppbiopoll(struct cdev *dev, int which, struct thread *td)
451136230Simp{
452136230Simp	struct pbio_softc *scp;
453136305Sobrien	int unit;
454136230Simp
455136305Sobrien	unit = UNIT(dev);
456136230Simp	scp = pbio_addr(unit);
457136230Simp	if (scp == NULL)
458136230Simp		return (ENODEV);
459136230Simp
460136230Simp	/*
461136230Simp	 * Do processing
462136230Simp	 */
463136230Simp	return (0); /* this is the wrong value I'm sure */
464136230Simp}
465