summaryrefslogtreecommitdiffstats
path: root/sys/i386
diff options
context:
space:
mode:
authorimp <imp@FreeBSD.org>2004-10-07 16:21:03 +0000
committerimp <imp@FreeBSD.org>2004-10-07 16:21:03 +0000
commit03e8fdc42185a26d87ad1d9fbd0e5480167f2d79 (patch)
treea70766952b97d9332bd2ed1b0448e1f79ff14433 /sys/i386
parentd848661392593c73b4e590a8985c6af75e83b0e0 (diff)
downloadFreeBSD-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/NOTES4
-rw-r--r--sys/i386/isa/pbio.c456
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 */
+}
OpenPOWER on IntegriCloud