diff options
author | imp <imp@FreeBSD.org> | 2004-10-07 16:21:03 +0000 |
---|---|---|
committer | imp <imp@FreeBSD.org> | 2004-10-07 16:21:03 +0000 |
commit | 03e8fdc42185a26d87ad1d9fbd0e5480167f2d79 (patch) | |
tree | a70766952b97d9332bd2ed1b0448e1f79ff14433 /sys/i386 | |
parent | d848661392593c73b4e590a8985c6af75e83b0e0 (diff) | |
download | FreeBSD-src-03e8fdc42185a26d87ad1d9fbd0e5480167f2d79.zip FreeBSD-src-03e8fdc42185a26d87ad1d9fbd0e5480167f2d79.tar.gz |
Port pbio to HEAD.
OK'd by: dds
Diffstat (limited to 'sys/i386')
-rw-r--r-- | sys/i386/conf/NOTES | 4 | ||||
-rw-r--r-- | sys/i386/isa/pbio.c | 456 |
2 files changed, 460 insertions, 0 deletions
diff --git a/sys/i386/conf/NOTES b/sys/i386/conf/NOTES index 7f3cbee..55df752 100644 --- a/sys/i386/conf/NOTES +++ b/sys/i386/conf/NOTES @@ -682,6 +682,10 @@ device digi_EPCX_PCI device digi_Xe device digi_Xem device digi_Xr +# Parallel (8255 PPI) basic I/O (mode 0) port (e.g. Advantech PCL-724) +device pbio +hint.pbio.0.at="isa" +hint.pbio.0.port="0x360" device spic hint.spic.0.at="isa" hint.spic.0.port="0x10a0" diff --git a/sys/i386/isa/pbio.c b/sys/i386/isa/pbio.c new file mode 100644 index 0000000..6a0eb7d --- /dev/null +++ b/sys/i386/isa/pbio.c @@ -0,0 +1,456 @@ +/* + * Copyright (c) 2000-2004 + * Diomidis D. Spinellis, Athens, Greece + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer as + * the first lines of this file unmodified. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY Diomidis D. Spinellis ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Diomidis D. Spinellis BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: pbio.c,v 1.12 2003/10/11 13:05:08 dds Exp dds $ + * $FreeBSD$ + * + */ + +#include <sys/cdefs.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> /* SYSINIT stuff */ +#include <sys/bus.h> +#include <sys/resource.h> +#include <sys/syslog.h> +#include <sys/sysctl.h> +#include <sys/conf.h> /* cdevsw stuff */ +#include <sys/malloc.h> /* malloc region definitions */ +#include <sys/module.h> +#include <machine/bus_pio.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <machine/clock.h> /* DELAY() */ +#include <sys/rman.h> +#include <sys/pbioio.h> /* pbio IOCTL definitions */ +#include <sys/uio.h> +#include <sys/fcntl.h> + +/* Function prototypes (these should all be static) */ +static d_open_t pbioopen; +static d_close_t pbioclose; +static d_read_t pbioread; +static d_write_t pbiowrite; +static d_ioctl_t pbioioctl; +static d_poll_t pbiopoll; +static int pbioprobe(device_t); +static int pbioattach(device_t); + +/* Device registers */ +#define PBIO_PORTA 0 +#define PBIO_PORTB 1 +#define PBIO_PORTC 2 +#define PBIO_CFG 3 +#define PBIO_IOSIZE 4 + +/* Per-port buffer size */ +#define PBIO_BUFSIZ 64 + +/* Number of /dev entries */ +#define PBIO_NPORTS 4 + +/* I/O port range */ +#define IO_PBIOSIZE 4 + +static char *port_names[] = {"a", "b", "ch", "cl"}; + +#define PBIO_PNAME(n) (port_names[(n)]) + +#define UNIT(dev) (minor(dev) >> 2) +#define PORT(dev) (minor(dev) & 0x3) + +#define PBIOPRI ((PZERO + 5) | PCATCH) + +static struct cdevsw pbio_cdevsw = { + .d_version = D_VERSION, + .d_flags = D_NEEDGIANT, + .d_open = pbioopen, + .d_close = pbioclose, + .d_read = pbioread, + .d_write = pbiowrite, + .d_ioctl = pbioioctl, + .d_poll = pbiopoll, + .d_name = "pbio" +}; + +/* + * Data specific to each I/O port + */ +struct portdata { + struct cdev *port; + int diff; /* When true read only differences */ + int ipace; /* Input pace */ + int opace; /* Output pace */ + char oldval; /* Last value read */ + char buff[PBIO_BUFSIZ]; /* Per-port data buffer */ +}; + +/* + * One of these per allocated device + */ +struct pbio_softc { + int iobase; /* I/O base */ + struct portdata pd[PBIO_NPORTS]; /* Per port data */ + int iomode; /* Virtualized I/O mode port value */ + /* The real port is write-only */ +} ; + +typedef struct pbio_softc *sc_p; + +static device_method_t pbio_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, pbioprobe), + DEVMETHOD(device_attach, pbioattach), + + { 0, 0 } +}; + +static devclass_t pbio_devclass; +#define pbio_addr(unit) ((struct pbio_softc *) \ + devclass_get_softc(pbio_devclass, unit)) + +static char driver_name[] = "pbio"; + +static driver_t pbio_driver = { + driver_name, + pbio_methods, + sizeof(struct pbio_softc), +}; + +DRIVER_MODULE(pbio, isa, pbio_driver, pbio_devclass, 0, 0); + +static int +pbioprobe(device_t dev) +{ + int iobase; + int rid; + struct resource *port; +#ifdef GENERIC_PBIO_PROBE + unsigned char val; +#endif + + rid = 0; + port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, + 0, ~0, IO_PBIOSIZE, RF_ACTIVE); + if (!port) + return (ENXIO); + + iobase = rman_get_start(port); + +#ifdef GENERIC_PBIO_PROBE + /* + * try see if the device is there. + * This probe works only if the device has no I/O attached to it + * XXX Better allow flags to abort testing + */ + /* Set all ports to output */ + outb(iobase + PBIO_CFG, 0x80); + printf("pbio val(CFG: 0x%03x)=0x%02x (should be 0x80)\n", + iobase, inb(iobase + PBIO_CFG)); + outb(iobase + PBIO_PORTA, 0xa5); + val = inb(iobase + PBIO_PORTA); + printf("pbio val=0x%02x (should be 0xa5)\n", val); + if (val != 0xa5) + return (ENXIO); + outb(iobase + PBIO_PORTA, 0x5a); + val = inb(iobase + PBIO_PORTA); + printf("pbio val=0x%02x (should be 0x5a)\n", val); + if (val != 0x5a) + return (ENXIO); +#endif + device_set_desc(dev, "Intel 8255 PPI (basic mode)"); + /* Set all ports to input */ + /* outb(iobase + PBIO_CFG, 0x9b); */ + bus_release_resource(dev, SYS_RES_IOPORT, rid, port); + return (0); +} + +/* + * Called if the probe succeeded. + * We can be destructive here as we know we have the device. + * we can also trust the unit number. + */ +static int +pbioattach (device_t dev) +{ + int unit = device_get_unit(dev); + int flags; + int i; + int iobase; + int rid; + struct resource *port; + struct pbio_softc *sc; + + rid = 0; + port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, + 0, ~0, IO_PBIOSIZE, RF_ACTIVE); + if (!port) + return (ENXIO); + + iobase = rman_get_start(port); + sc = device_get_softc(dev); + + /* + * Allocate storage for this instance . + */ + bzero(sc, sizeof(*sc)); + flags = device_get_flags(dev); + + /* + * Store whatever seems wise. + */ + sc->iobase = iobase; + sc->iomode = 0x9b; /* All ports to input */ + + for (i = 0; i < PBIO_NPORTS; i++) + sc->pd[i].port = make_dev(&pbio_cdevsw, (unit << 2) + i, 0, 0, 0600, + "pbio%d%s", unit, PBIO_PNAME(i)); + return 0; +} + +static int +pbioioctl (struct cdev *dev, u_long cmd, caddr_t data, int flag, + struct thread *td) +{ + int unit = UNIT(dev); + int port = PORT(dev); + struct pbio_softc *scp; + + scp = pbio_addr(unit); + if (scp == NULL) + return (ENODEV); + switch (cmd) { + case PBIO_SETDIFF: + scp->pd[port].diff = *(int *)data; + break; + case PBIO_SETIPACE: + scp->pd[port].ipace = *(int *)data; + break; + case PBIO_SETOPACE: + scp->pd[port].opace = *(int *)data; + break; + case PBIO_GETDIFF: + *(int *)data = scp->pd[port].diff; + break; + case PBIO_GETIPACE: + *(int *)data = scp->pd[port].ipace; + break; + case PBIO_GETOPACE: + *(int *)data = scp->pd[port].opace; + break; + default: + return ENXIO; + } + return (0); +} + +static int +pbioopen(struct cdev *dev, int oflags, int devtype, struct thread *td) +{ + int unit = UNIT(dev); + int port = PORT(dev); + struct pbio_softc *scp; + int ocfg; + int portbit; /* Port configuration bit */ + + scp = pbio_addr(unit); + if (scp == NULL) + return (ENODEV); + + switch (port) { + case 0: portbit = 0x10; break; /* Port A */ + case 1: portbit = 0x02; break; /* Port B */ + case 2: portbit = 0x08; break; /* Port CH */ + case 3: portbit = 0x01; break; /* Port CL */ + default: return (ENODEV); + } + ocfg = scp->iomode; + + if (oflags & FWRITE) + /* Writing == output; zero the bit */ + outb(scp->iobase + PBIO_CFG, scp->iomode = (ocfg & (~portbit))); + else if (oflags & FREAD) + /* Reading == input; set the bit */ + outb(scp->iobase + PBIO_CFG, scp->iomode = (ocfg | portbit)); + else + return (EACCES); + + return (0); +} + +static int +pbioclose(struct cdev *dev, int fflag, int devtype, struct thread *td) +{ + int unit = UNIT(dev); + struct pbio_softc *scp; + + scp = pbio_addr(unit); + if (scp == NULL) + return (ENODEV); + + return (0); +} + +/* + * Return the value of a given port on a given I/O base address + * Handles the split C port nibbles and blocking + */ +static int +portval(int port, struct pbio_softc *scp, char *val) +{ + int err; + + for (;;) { + switch (port) { + case 0: + *val = inb(scp->iobase + PBIO_PORTA); + break; + case 1: + *val = inb(scp->iobase + PBIO_PORTB); + break; + case 2: + *val = (inb(scp->iobase + PBIO_PORTC) >> 4) & 0xf; + break; + case 3: + *val = inb(scp->iobase + PBIO_PORTC) & 0xf; + break; + default: + *val = 0; + break; + } + if (scp->pd[port].diff) { + if (*val != scp->pd[port].oldval) { + scp->pd[port].oldval = *val; + return (0); + } + err = tsleep((caddr_t)&(scp->pd[port].diff), PBIOPRI, + "pbiopl", max(1, scp->pd[port].ipace)); + if (err == EINTR) + return (EINTR); + } else + return (0); + } +} + +static int +pbioread(struct cdev *dev, struct uio *uio, int ioflag) +{ + int unit = UNIT(dev); + int port = PORT(dev); + struct pbio_softc *scp; + int toread; + int err; + int ret, i; + char val; + + scp = pbio_addr(unit); + if (scp == NULL) + return (ENODEV); + + while (uio->uio_resid > 0) { + toread = min(uio->uio_resid, PBIO_BUFSIZ); + if ((ret = uiomove(scp->pd[port].buff, toread, uio)) != 0) + return (ret); + for (i = 0; i < toread; i++) { + if ((err = portval(port, scp, &val)) != 0) + return (err); + scp->pd[port].buff[i] = val; + if (!scp->pd[port].diff && scp->pd[port].ipace) + tsleep((caddr_t)&(scp->pd[port].ipace), PBIOPRI, + "pbioip", scp->pd[port].ipace); + } + } + return 0; +} + +static int +pbiowrite(struct cdev *dev, struct uio *uio, int ioflag) +{ + int unit = UNIT(dev); + int port = PORT(dev); + struct pbio_softc *scp; + char val, oval; + int towrite; + int ret; + int iobase; + int i; + + scp = pbio_addr(unit); + if (scp == NULL) + return (ENODEV); + + iobase = scp->iobase; + while (uio->uio_resid > 0) { + towrite = min(uio->uio_resid, PBIO_BUFSIZ); + if ((ret = uiomove(scp->pd[port].buff, towrite, uio)) != 0) + return (ret); + for (i = 0; i < towrite; i++) { + val = scp->pd[port].buff[i]; + switch (port) { + case 0: + outb(iobase + PBIO_PORTA, val); + break; + case 1: + outb(iobase + PBIO_PORTB, val); + break; + case 2: + oval = inb(iobase + PBIO_PORTC); + oval &= 0xf; + val <<= 4; + outb(iobase + PBIO_PORTC, val | oval); + break; + case 3: + oval = inb(iobase + PBIO_PORTC); + oval &= 0xf0; + val &= 0xf; + outb(iobase + PBIO_PORTC, oval | val); + break; + } + if (scp->pd[port].opace) + tsleep((caddr_t)&(scp->pd[port].opace), + PBIOPRI, "pbioop", + scp->pd[port].opace); + } + } + return (0); +} + +static int +pbiopoll(struct cdev *dev, int which, struct thread *td) +{ + int unit = UNIT(dev); + struct pbio_softc *scp; + + scp = pbio_addr(unit); + if (scp == NULL) + return (ENODEV); + + /* + * Do processing + */ + return (0); /* this is the wrong value I'm sure */ +} |