pbio.c revision 136230
150397Sobrien/*
290075Sobrien *  Copyright (c) 2000-2004
390075Sobrien *          Diomidis D. Spinellis, Athens, Greece
418334Speter *      All rights reserved.
518334Speter *
618334Speter *  Redistribution and use in source and binary forms, with or without
718334Speter *  modification, are permitted provided that the following conditions
818334Speter *  are met:
918334Speter *  1. Redistributions of source code must retain the above copyright
1018334Speter *     notice, this list of conditions and the following disclaimer as
1118334Speter *     the first lines of this file unmodified.
1218334Speter *  2. Redistributions in binary form must reproduce the above copyright
1318334Speter *     notice, this list of conditions and the following disclaimer in the
1418334Speter *     documentation and/or other materials provided with the distribution.
1518334Speter *
1618334Speter *  THIS SOFTWARE IS PROVIDED BY Diomidis D. Spinellis ``AS IS'' AND ANY
1718334Speter *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1818334Speter *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1918334Speter *  PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL Diomidis D. Spinellis BE
2018334Speter *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2118334Speter *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2218334Speter *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
2318334Speter *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
2418334Speter *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
2518334Speter *  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
2690075Sobrien *  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2790075Sobrien *
2890075Sobrien * $Id: pbio.c,v 1.12 2003/10/11 13:05:08 dds Exp dds $
2990075Sobrien * $FreeBSD: head/sys/dev/pbio/pbio.c 136230 2004-10-07 16:21:03Z imp $
3090075Sobrien *
3190075Sobrien */
3290075Sobrien
3390075Sobrien#include <sys/cdefs.h>
3490075Sobrien#include <sys/param.h>
3518334Speter#include <sys/systm.h>
3618334Speter#include <sys/kernel.h>		/* SYSINIT stuff */
3718334Speter#include <sys/bus.h>
3818334Speter#include <sys/resource.h>
3918334Speter#include <sys/syslog.h>
4018334Speter#include <sys/sysctl.h>
4118334Speter#include <sys/conf.h>		/* cdevsw stuff */
4218334Speter#include <sys/malloc.h>		/* malloc region definitions */
4318334Speter#include <sys/module.h>
4450397Sobrien#include <machine/bus_pio.h>
4550397Sobrien#include <machine/bus.h>
4650397Sobrien#include <machine/resource.h>
4718334Speter#include <machine/clock.h>	/* DELAY() */
4818334Speter#include <sys/rman.h>
4990075Sobrien#include <sys/pbioio.h>		/* pbio IOCTL definitions */
5018334Speter#include <sys/uio.h>
5190075Sobrien#include <sys/fcntl.h>
5290075Sobrien
5390075Sobrien/* Function prototypes (these should all be static) */
5418334Speterstatic  d_open_t	pbioopen;
5590075Sobrienstatic  d_close_t	pbioclose;
5690075Sobrienstatic  d_read_t	pbioread;
5718334Speterstatic  d_write_t	pbiowrite;
5818334Speterstatic  d_ioctl_t	pbioioctl;
5918334Speterstatic  d_poll_t	pbiopoll;
6018334Speterstatic	int		pbioprobe(device_t);
6190075Sobrienstatic	int		pbioattach(device_t);
6218334Speter
6390075Sobrien/* Device registers */
6418334Speter#define	PBIO_PORTA	0
6518334Speter#define	PBIO_PORTB	1
6690075Sobrien#define	PBIO_PORTC	2
6790075Sobrien#define	PBIO_CFG	3
6890075Sobrien#define PBIO_IOSIZE	4
6990075Sobrien
7090075Sobrien/* Per-port buffer size */
7190075Sobrien#define PBIO_BUFSIZ 64
7290075Sobrien
7390075Sobrien/* Number of /dev entries */
7490075Sobrien#define PBIO_NPORTS 4
7518334Speter
7618334Speter/* I/O port range */
7718334Speter#define IO_PBIOSIZE 4
7818334Speter
7918334Speterstatic char *port_names[] = {"a", "b", "ch", "cl"};
8018334Speter
8118334Speter#define	PBIO_PNAME(n)		(port_names[(n)])
8218334Speter
8318334Speter#define UNIT(dev)		(minor(dev) >> 2)
8418334Speter#define PORT(dev)		(minor(dev) & 0x3)
8518334Speter
8618334Speter#define	PBIOPRI	((PZERO + 5) | PCATCH)
8718334Speter
8890075Sobrienstatic struct cdevsw pbio_cdevsw = {
8918334Speter	.d_version = D_VERSION,
9018334Speter	.d_flags = D_NEEDGIANT,
9148743Sobrien	.d_open = pbioopen,
9250397Sobrien	.d_close = pbioclose,
9318334Speter	.d_read = pbioread,
9450397Sobrien	.d_write = pbiowrite,
9518334Speter	.d_ioctl = pbioioctl,
9618334Speter	.d_poll = pbiopoll,
9750397Sobrien	.d_name = "pbio"
9850397Sobrien};
9948743Sobrien
10018334Speter/*
10118334Speter * Data specific to each I/O port
10218334Speter */
10318334Speterstruct portdata {
10418334Speter	struct cdev *port;
10518334Speter	int	diff;			/* When true read only differences */
10618334Speter	int	ipace;			/* Input pace */
10718334Speter	int	opace;			/* Output pace */
10818334Speter	char	oldval;			/* Last value read */
10918334Speter	char	buff[PBIO_BUFSIZ];	/* Per-port data buffer */
11018334Speter};
11118334Speter
11218334Speter/*
11318334Speter * One of these per allocated device
11490075Sobrien */
11518334Speterstruct pbio_softc {
11618334Speter	int	iobase;			/* I/O base */
11748743Sobrien	struct portdata pd[PBIO_NPORTS];	/* Per port data */
11818334Speter	int	iomode;			/* Virtualized I/O mode port value */
11918334Speter					/* The real port is write-only */
12018334Speter} ;
12118334Speter
12218334Spetertypedef	struct pbio_softc *sc_p;
12318334Speter
12418334Speterstatic device_method_t pbio_methods[] = {
12518334Speter	/* Device interface */
12618334Speter	DEVMETHOD(device_probe,		pbioprobe),
12718334Speter	DEVMETHOD(device_attach,	pbioattach),
12818334Speter
12918334Speter	{ 0, 0 }
13018334Speter};
13118334Speter
13218334Speterstatic	devclass_t	pbio_devclass;
13318334Speter#define	pbio_addr(unit)	((struct pbio_softc *) \
13418334Speter			 devclass_get_softc(pbio_devclass, unit))
13550397Sobrien
13648743Sobrienstatic char driver_name[] = "pbio";
13748743Sobrien
13848743Sobrienstatic driver_t pbio_driver = {
13948743Sobrien	driver_name,
14048743Sobrien	pbio_methods,
14148743Sobrien	sizeof(struct pbio_softc),
14248743Sobrien};
14350397Sobrien
14418334SpeterDRIVER_MODULE(pbio, isa, pbio_driver, pbio_devclass, 0, 0);
14550397Sobrien
14650397Sobrienstatic int
14750397Sobrienpbioprobe(device_t dev)
14850397Sobrien{
14950397Sobrien	int iobase;
15050397Sobrien	int		rid;
15150397Sobrien	struct resource *port;
15250397Sobrien#ifdef GENERIC_PBIO_PROBE
15350397Sobrien	unsigned char val;
15450397Sobrien#endif
15550397Sobrien
15650397Sobrien	rid = 0;
15750397Sobrien	port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
15850397Sobrien				  0, ~0, IO_PBIOSIZE, RF_ACTIVE);
15950397Sobrien	if (!port)
16052284Sobrien		return (ENXIO);
16152284Sobrien
16252284Sobrien	iobase = rman_get_start(port);
16352284Sobrien
16452284Sobrien#ifdef GENERIC_PBIO_PROBE
16552284Sobrien	/*
16652284Sobrien	 * try see if the device is there.
16750397Sobrien	 * This probe works only if the device has no I/O attached to it
16890075Sobrien	 * XXX Better allow flags to abort testing
16990075Sobrien	 */
17090075Sobrien	/* Set all ports to output */
17190075Sobrien	outb(iobase + PBIO_CFG, 0x80);
17290075Sobrien	printf("pbio val(CFG: 0x%03x)=0x%02x (should be 0x80)\n",
17390075Sobrien		iobase, inb(iobase + PBIO_CFG));
17490075Sobrien	outb(iobase + PBIO_PORTA, 0xa5);
17590075Sobrien	val = inb(iobase + PBIO_PORTA);
17690075Sobrien	printf("pbio val=0x%02x (should be 0xa5)\n", val);
17790075Sobrien	if (val != 0xa5)
17890075Sobrien		return (ENXIO);
17990075Sobrien	outb(iobase + PBIO_PORTA, 0x5a);
18090075Sobrien	val = inb(iobase + PBIO_PORTA);
18190075Sobrien	printf("pbio val=0x%02x (should be 0x5a)\n", val);
18290075Sobrien	if (val != 0x5a)
18390075Sobrien		return (ENXIO);
18490075Sobrien#endif
18590075Sobrien	device_set_desc(dev, "Intel 8255 PPI (basic mode)");
18690075Sobrien	/* Set all ports to input */
18790075Sobrien	/* outb(iobase + PBIO_CFG, 0x9b); */
18890075Sobrien	bus_release_resource(dev, SYS_RES_IOPORT, rid, port);
18990075Sobrien	return (0);
19090075Sobrien}
19190075Sobrien
19290075Sobrien/*
19390075Sobrien * Called if the probe succeeded.
19490075Sobrien * We can be destructive here as we know we have the device.
19590075Sobrien * we can also trust the unit number.
19690075Sobrien */
19790075Sobrienstatic int
19890075Sobrienpbioattach (device_t dev)
19990075Sobrien{
20090075Sobrien	int unit = device_get_unit(dev);
20190075Sobrien	int flags;
20290075Sobrien	int i;
20390075Sobrien	int iobase;
20490075Sobrien	int		rid;
20590075Sobrien	struct resource *port;
20690075Sobrien	struct pbio_softc *sc;
20790075Sobrien
20890075Sobrien	rid = 0;
20990075Sobrien	port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
21090075Sobrien				  0, ~0, IO_PBIOSIZE, RF_ACTIVE);
21190075Sobrien	if (!port)
21290075Sobrien		return (ENXIO);
21390075Sobrien
21490075Sobrien	iobase = rman_get_start(port);
21590075Sobrien	sc = device_get_softc(dev);
21690075Sobrien
21790075Sobrien	/*
21890075Sobrien	 * Allocate storage for this instance .
21990075Sobrien	 */
22090075Sobrien	bzero(sc, sizeof(*sc));
22190075Sobrien	flags = device_get_flags(dev);
22290075Sobrien
22390075Sobrien	/*
22490075Sobrien	 * Store whatever seems wise.
22590075Sobrien	 */
22690075Sobrien	sc->iobase = iobase;
22790075Sobrien	sc->iomode = 0x9b;		/* All ports to input */
22890075Sobrien
22990075Sobrien	for (i = 0; i < PBIO_NPORTS; i++)
23090075Sobrien		sc->pd[i].port = make_dev(&pbio_cdevsw, (unit << 2) + i, 0, 0, 0600,
23190075Sobrien				     "pbio%d%s", unit, PBIO_PNAME(i));
23290075Sobrien	return 0;
23390075Sobrien}
23490075Sobrien
23590075Sobrienstatic int
23690075Sobrienpbioioctl (struct cdev *dev, u_long cmd, caddr_t data, int flag,
23790075Sobrien    struct thread *td)
23890075Sobrien{
23990075Sobrien	int unit = UNIT(dev);
24090075Sobrien	int port = PORT(dev);
24190075Sobrien	struct pbio_softc *scp;
24290075Sobrien
24390075Sobrien	scp = pbio_addr(unit);
24490075Sobrien	if (scp == NULL)
24590075Sobrien		return (ENODEV);
24690075Sobrien	switch (cmd) {
24790075Sobrien	    case PBIO_SETDIFF:
24890075Sobrien		scp->pd[port].diff = *(int *)data;
24990075Sobrien		break;
25090075Sobrien	    case PBIO_SETIPACE:
25190075Sobrien		scp->pd[port].ipace = *(int *)data;
25290075Sobrien		break;
25390075Sobrien	    case PBIO_SETOPACE:
25490075Sobrien		scp->pd[port].opace = *(int *)data;
25590075Sobrien		break;
25690075Sobrien	    case PBIO_GETDIFF:
25790075Sobrien		*(int *)data = scp->pd[port].diff;
25890075Sobrien		break;
25990075Sobrien	    case PBIO_GETIPACE:
26090075Sobrien		*(int *)data = scp->pd[port].ipace;
26190075Sobrien		break;
26290075Sobrien	    case PBIO_GETOPACE:
26390075Sobrien		*(int *)data = scp->pd[port].opace;
26490075Sobrien		break;
26590075Sobrien	    default:
26690075Sobrien		return ENXIO;
26790075Sobrien	}
26890075Sobrien	return (0);
26990075Sobrien}
27090075Sobrien
27190075Sobrienstatic  int
27290075Sobrienpbioopen(struct cdev *dev, int oflags, int devtype, struct thread *td)
27390075Sobrien{
27490075Sobrien	int unit = UNIT(dev);
27590075Sobrien	int port = PORT(dev);
27690075Sobrien	struct pbio_softc *scp;
27790075Sobrien	int ocfg;
27890075Sobrien	int portbit;			/* Port configuration bit */
27990075Sobrien
28090075Sobrien	scp = pbio_addr(unit);
281	if (scp == NULL)
282		return (ENODEV);
283
284	switch (port) {
285	case 0: portbit = 0x10; break;	/* Port A */
286	case 1: portbit = 0x02; break;	/* Port B */
287	case 2: portbit = 0x08; break;	/* Port CH */
288	case 3: portbit = 0x01; break;	/* Port CL */
289	default: return (ENODEV);
290	}
291	ocfg = scp->iomode;
292
293	if (oflags & FWRITE)
294		/* Writing == output; zero the bit */
295		outb(scp->iobase + PBIO_CFG, scp->iomode = (ocfg & (~portbit)));
296	else if (oflags & FREAD)
297		/* Reading == input; set the bit */
298		outb(scp->iobase + PBIO_CFG, scp->iomode = (ocfg | portbit));
299	else
300		return (EACCES);
301
302	return (0);
303}
304
305static  int
306pbioclose(struct cdev *dev, int fflag, int devtype, struct thread *td)
307{
308	int unit = UNIT(dev);
309	struct pbio_softc *scp;
310
311	scp = pbio_addr(unit);
312	if (scp == NULL)
313		return (ENODEV);
314
315	return (0);
316}
317
318/*
319 * Return the value of a given port on a given I/O base address
320 * Handles the split C port nibbles and blocking
321 */
322static int
323portval(int port, struct pbio_softc *scp, char *val)
324{
325	int err;
326
327	for (;;) {
328		switch (port) {
329		case 0:
330			*val = inb(scp->iobase + PBIO_PORTA);
331			break;
332		case 1:
333			*val = inb(scp->iobase + PBIO_PORTB);
334			break;
335		case 2:
336			*val = (inb(scp->iobase + PBIO_PORTC) >> 4) & 0xf;
337			break;
338		case 3:
339			*val = inb(scp->iobase + PBIO_PORTC) & 0xf;
340			break;
341		default:
342			*val = 0;
343			break;
344		}
345		if (scp->pd[port].diff) {
346			if (*val != scp->pd[port].oldval) {
347				scp->pd[port].oldval = *val;
348				return (0);
349			}
350			err = tsleep((caddr_t)&(scp->pd[port].diff), PBIOPRI,
351				     "pbiopl", max(1, scp->pd[port].ipace));
352			if (err == EINTR)
353				return (EINTR);
354		} else
355			return (0);
356	}
357}
358
359static  int
360pbioread(struct cdev *dev, struct uio *uio, int ioflag)
361{
362	int unit = UNIT(dev);
363	int port = PORT(dev);
364	struct pbio_softc *scp;
365	int     toread;
366	int	err;
367	int ret, i;
368	char val;
369
370	scp = pbio_addr(unit);
371	if (scp == NULL)
372		return (ENODEV);
373
374	while (uio->uio_resid > 0) {
375		toread = min(uio->uio_resid, PBIO_BUFSIZ);
376		if ((ret = uiomove(scp->pd[port].buff, toread, uio)) != 0)
377			return (ret);
378		for (i = 0; i < toread; i++) {
379			if ((err = portval(port, scp, &val)) != 0)
380				return (err);
381			scp->pd[port].buff[i] = val;
382			if (!scp->pd[port].diff && scp->pd[port].ipace)
383				tsleep((caddr_t)&(scp->pd[port].ipace), PBIOPRI,
384					"pbioip", scp->pd[port].ipace);
385		}
386	}
387	return 0;
388}
389
390static  int
391pbiowrite(struct cdev *dev, struct uio *uio, int ioflag)
392{
393	int unit = UNIT(dev);
394	int port = PORT(dev);
395	struct pbio_softc *scp;
396	char val, oval;
397	int	towrite;
398	int	ret;
399	int iobase;
400	int i;
401
402	scp = pbio_addr(unit);
403	if (scp == NULL)
404		return (ENODEV);
405
406	iobase = scp->iobase;
407	while (uio->uio_resid > 0) {
408		towrite = min(uio->uio_resid, PBIO_BUFSIZ);
409		if ((ret = uiomove(scp->pd[port].buff, towrite, uio)) != 0)
410			return (ret);
411		for (i = 0; i < towrite; i++) {
412			val = scp->pd[port].buff[i];
413			switch (port) {
414			case 0:
415				outb(iobase + PBIO_PORTA, val);
416				break;
417			case 1:
418				outb(iobase + PBIO_PORTB, val);
419				break;
420			case 2:
421				oval = inb(iobase + PBIO_PORTC);
422				oval &= 0xf;
423				val <<= 4;
424				outb(iobase + PBIO_PORTC, val | oval);
425				break;
426			case 3:
427				oval = inb(iobase + PBIO_PORTC);
428				oval &= 0xf0;
429				val &= 0xf;
430				outb(iobase + PBIO_PORTC, oval | val);
431				break;
432			}
433			if (scp->pd[port].opace)
434				tsleep((caddr_t)&(scp->pd[port].opace),
435					PBIOPRI, "pbioop",
436					scp->pd[port].opace);
437		}
438	}
439	return (0);
440}
441
442static  int
443pbiopoll(struct cdev *dev, int which, struct thread *td)
444{
445	int unit = UNIT(dev);
446	struct pbio_softc *scp;
447
448	scp = pbio_addr(unit);
449	if (scp == NULL)
450		return (ENODEV);
451
452	/*
453	 * Do processing
454	 */
455	return (0); /* this is the wrong value I'm sure */
456}
457