summaryrefslogtreecommitdiffstats
path: root/sys/dev/pcf
diff options
context:
space:
mode:
authorjoerg <joerg@FreeBSD.org>2004-05-25 07:42:45 +0000
committerjoerg <joerg@FreeBSD.org>2004-05-25 07:42:45 +0000
commit5bfc126ed81efb2c1438e0f54ecf9287c2fe440d (patch)
tree443bb078c2a5d2f80d0822545b68d651d3746418 /sys/dev/pcf
parent63a829c37a320c9d13bdaf4bd47af42a87445d96 (diff)
downloadFreeBSD-src-5bfc126ed81efb2c1438e0f54ecf9287c2fe440d.zip
FreeBSD-src-5bfc126ed81efb2c1438e0f54ecf9287c2fe440d.tar.gz
Round #1 of improving pcf(4).
This splits the driver into a bus-independant backend, plus bus-specific frontends. The old pcf(4) (i386/ISA) frontend is now in pcf_isa.c, the frontend in envctrl.c is for sparc64/Ebus2 (Sun device name: SUNW,envctrl from Sun E450 machines). More frontends are expected to appear in future. This is not yet ready for public consumption, but it basically works. Nicolas will bring over his ISA-specific fixes soon. Reviewed by: nsouch
Diffstat (limited to 'sys/dev/pcf')
-rw-r--r--sys/dev/pcf/envctrl.c196
-rw-r--r--sys/dev/pcf/pcf.c442
-rw-r--r--sys/dev/pcf/pcf_isa.c185
-rw-r--r--sys/dev/pcf/pcfvar.h142
4 files changed, 645 insertions, 320 deletions
diff --git a/sys/dev/pcf/envctrl.c b/sys/dev/pcf/envctrl.c
new file mode 100644
index 0000000..8126924
--- /dev/null
+++ b/sys/dev/pcf/envctrl.c
@@ -0,0 +1,196 @@
+/*-
+ * Copyright (c) 2004 Joerg Wunsch
+ *
+ * derived from sys/i386/isa/pcf.c which is:
+ *
+ * Copyright (c) 1998 Nicolas Souchu, Marc Bouget
+ * 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.
+ * 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 THE AUTHOR AND CONTRIBUTORS ``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 THE AUTHOR OR CONTRIBUTORS 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.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Device specific driver for the SUNW,envctrl device found on some
+ * UltraSPARC Sun systems. This device is a Philips PCF8584 sitting
+ * on the Ebus2.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/resource.h>
+#include <sys/uio.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <sys/rman.h>
+
+#include <dev/ofw/openfirm.h>
+
+#include <sparc64/ebus/ebusvar.h>
+
+#include <dev/iicbus/iiconf.h>
+#include <dev/pcf/pcfvar.h>
+#include "iicbus_if.h"
+
+#undef PCF_DEFAULT_ADDR
+#define PCF_DEFAULT_ADDR 0x55 /* SUNW,pcf default */
+
+static int envctrl_probe(device_t);
+static int envctrl_attach(device_t);
+static int envctrl_detach(device_t);
+
+static device_method_t envctrl_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, envctrl_probe),
+ DEVMETHOD(device_attach, envctrl_attach),
+ DEVMETHOD(device_detach, envctrl_detach),
+
+ /* iicbus interface */
+ DEVMETHOD(iicbus_callback, iicbus_null_callback),
+ DEVMETHOD(iicbus_repeated_start, pcf_repeated_start),
+ DEVMETHOD(iicbus_start, pcf_start),
+ DEVMETHOD(iicbus_stop, pcf_stop),
+ DEVMETHOD(iicbus_write, pcf_write),
+ DEVMETHOD(iicbus_read, pcf_read),
+ DEVMETHOD(iicbus_reset, pcf_rst_card),
+ { 0, 0 }
+};
+
+static devclass_t envctrl_devclass;
+
+static driver_t envctrl_driver = {
+ "envctrl",
+ envctrl_methods,
+ sizeof(struct pcf_softc),
+};
+
+static int
+envctrl_probe(device_t dev)
+{
+
+ if (strcmp("SUNW,envctrl", ebus_get_name(dev)) == 0) {
+ device_set_desc(dev, "EBus SUNW,envctrl");
+ return (0);
+ }
+ return (ENXIO);
+}
+
+static int
+envctrl_attach(device_t dev)
+{
+ struct pcf_softc *sc;
+ int rv = ENXIO;
+
+ sc = DEVTOSOFTC(dev);
+ bzero(sc, sizeof(struct pcf_softc));
+
+ /* IO port is mandatory */
+ sc->res_ioport = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
+ &sc->rid_ioport, RF_ACTIVE);
+ if (sc->res_ioport == 0) {
+ device_printf(dev, "cannot reserve I/O port range\n");
+ goto error;
+ }
+
+ sc->pcf_flags = device_get_flags(dev);
+
+ if (!(sc->pcf_flags & IIC_POLLED)) {
+ sc->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->rid_irq,
+ RF_ACTIVE);
+ if (sc->res_irq == 0) {
+ device_printf(dev, "can't reserve irq, polled mode.\n");
+ sc->pcf_flags |= IIC_POLLED;
+ }
+ }
+
+ /* reset the chip */
+ pcf_rst_card(dev, IIC_FASTEST, PCF_DEFAULT_ADDR, NULL);
+
+ rv = BUS_SETUP_INTR(device_get_parent(dev), dev, sc->res_irq,
+ INTR_TYPE_NET /* | INTR_ENTROPY */,
+ pcf_intr, sc, &sc->intr_cookie);
+ if (rv) {
+ device_printf(dev, "could not setup IRQ\n");
+ goto error;
+ }
+
+ if ((sc->iicbus = device_add_child(dev, "iicbus", -1)) == NULL)
+ device_printf(dev, "could not allocate iicbus instance\n");
+
+ /* probe and attach the iicbus */
+ bus_generic_attach(dev);
+
+ return (0);
+
+error:
+ if (sc->res_irq != 0) {
+ bus_deactivate_resource(dev, SYS_RES_IRQ, sc->rid_irq,
+ sc->res_irq);
+ bus_release_resource(dev, SYS_RES_IRQ, sc->rid_irq,
+ sc->res_irq);
+ }
+ if (sc->res_ioport != 0) {
+ bus_deactivate_resource(dev, SYS_RES_IOPORT, sc->rid_ioport,
+ sc->res_ioport);
+ bus_release_resource(dev, SYS_RES_IOPORT, sc->rid_ioport,
+ sc->res_ioport);
+ }
+ return (rv);
+}
+
+static int
+envctrl_detach(device_t dev)
+{
+ struct pcf_softc *sc;
+ int rv;
+
+ sc = DEVTOSOFTC(dev);
+
+ if ((rv = bus_generic_detach(dev)) != 0)
+ return (rv);
+
+ if ((rv = device_delete_child(dev, sc->iicbus)) != 0)
+ return (rv);
+
+ if (sc->res_irq != 0) {
+ BUS_TEARDOWN_INTR(device_get_parent(dev), dev, sc->res_irq,
+ sc->intr_cookie);
+ bus_deactivate_resource(dev, SYS_RES_IRQ, sc->rid_irq, sc->res_irq);
+ bus_release_resource(dev, SYS_RES_IRQ, sc->rid_irq, sc->res_irq);
+ }
+
+ bus_deactivate_resource(dev, SYS_RES_IOPORT, sc->rid_ioport, sc->res_ioport);
+ bus_release_resource(dev, SYS_RES_IOPORT, sc->rid_ioport, sc->res_ioport);
+
+ return (0);
+}
+
+DRIVER_MODULE(pcf, ebus, envctrl_driver, envctrl_devclass, 0, 0);
+MODULE_DEPEND(pcf, iicbus, PCF_MINVER, PCF_PREFVER, PCF_MAXVER);
+MODULE_VERSION(pcf, PCF_MODVER);
diff --git a/sys/dev/pcf/pcf.c b/sys/dev/pcf/pcf.c
index 64c059e..08b088f 100644
--- a/sys/dev/pcf/pcf.c
+++ b/sys/dev/pcf/pcf.c
@@ -1,5 +1,6 @@
/*-
* Copyright (c) 1998 Nicolas Souchu, Marc Bouget
+ * Copyright (c) 2004 Joerg Wunsch
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -35,279 +36,70 @@ __FBSDID("$FreeBSD$");
#include <machine/bus.h>
#include <machine/resource.h>
-#include <sys/rman.h>
-#include <isa/isareg.h>
-#include <isa/isavar.h>
+#include <sys/rman.h>
#include <dev/iicbus/iiconf.h>
+#include <dev/pcf/pcfvar.h>
#include "iicbus_if.h"
-#define IO_PCFSIZE 2
-
-#define TIMEOUT 9999 /* XXX */
-
-/* Status bits of S1 register (read only) */
-#define nBB 0x01 /* busy when low set/reset by STOP/START*/
-#define LAB 0x02 /* lost arbitration bit in multi-master mode */
-#define AAS 0x04 /* addressed as slave */
-#define LRB 0x08 /* last received byte when not AAS */
-#define AD0 0x08 /* general call received when AAS */
-#define BER 0x10 /* bus error, misplaced START or STOP */
-#define STS 0x20 /* STOP detected in slave receiver mode */
-#define PIN 0x80 /* pending interrupt not (r/w) */
-
-/* Control bits of S1 register (write only) */
-#define ACK 0x01
-#define STO 0x02
-#define STA 0x04
-#define ENI 0x08
-#define ES2 0x10
-#define ES1 0x20
-#define ES0 0x40
-
-#define BUFSIZE 2048
-
-#define SLAVE_TRANSMITTER 0x1
-#define SLAVE_RECEIVER 0x2
-
-#define PCF_DEFAULT_ADDR 0xaa
-
-struct pcf_softc {
-
- int pcf_base; /* isa port */
- int pcf_flags;
- u_char pcf_addr; /* interface I2C address */
-
- int pcf_slave_mode; /* receiver or transmitter */
- int pcf_started; /* 1 if start condition sent */
-
- device_t iicbus; /* the corresponding iicbus */
-
- int rid_irq, rid_ioport;
- struct resource *res_irq, *res_ioport;
- void *intr_cookie;
-};
-
-static int pcf_probe(device_t);
-static int pcf_attach(device_t);
-static void pcfintr(void *arg);
-
-static int pcf_print_child(device_t, device_t);
-
-static int pcf_repeated_start(device_t, u_char, int);
-static int pcf_start(device_t, u_char, int);
-static int pcf_stop(device_t);
-static int pcf_write(device_t, char *, int, int *, int);
-static int pcf_read(device_t, char *, int, int *, int, int);
-static int pcf_rst_card(device_t, u_char, u_char, u_char *);
-
-static device_method_t pcf_methods[] = {
- /* device interface */
- DEVMETHOD(device_probe, pcf_probe),
- DEVMETHOD(device_attach, pcf_attach),
-
- /* bus interface */
- DEVMETHOD(bus_print_child, pcf_print_child),
-
- /* iicbus interface */
- DEVMETHOD(iicbus_callback, iicbus_null_callback),
- DEVMETHOD(iicbus_repeated_start, pcf_repeated_start),
- DEVMETHOD(iicbus_start, pcf_start),
- DEVMETHOD(iicbus_stop, pcf_stop),
- DEVMETHOD(iicbus_write, pcf_write),
- DEVMETHOD(iicbus_read, pcf_read),
- DEVMETHOD(iicbus_reset, pcf_rst_card),
-
- { 0, 0 }
-};
-
-static driver_t pcf_driver = {
- "pcf",
- pcf_methods,
- sizeof(struct pcf_softc),
-};
-
-static devclass_t pcf_devclass;
-
-#define DEVTOSOFTC(dev) ((struct pcf_softc *)device_get_softc(dev))
-
-static int
-pcf_probe(device_t pcfdev)
-{
- struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
- device_t parent = device_get_parent(pcfdev);
- uintptr_t base;
-
- device_set_desc(pcfdev, "PCF8584 I2C bus controller");
-
- pcf = DEVTOSOFTC(pcfdev);
- bzero(pcf, sizeof(struct pcf_softc));
-
- pcf->rid_irq = pcf->rid_ioport = 0;
- pcf->res_irq = pcf->res_ioport = 0;
-
- /* IO port is mandatory */
- pcf->res_ioport = bus_alloc_resource(pcfdev, SYS_RES_IOPORT,
- &pcf->rid_ioport, 0ul, ~0ul,
- IO_PCFSIZE, RF_ACTIVE);
- if (pcf->res_ioport == 0) {
- device_printf(pcfdev, "cannot reserve I/O port range\n");
- goto error;
- }
- BUS_READ_IVAR(parent, pcfdev, ISA_IVAR_PORT, &base);
- pcf->pcf_base = base;
-
- pcf->pcf_flags = device_get_flags(pcfdev);
-
- if (!(pcf->pcf_flags & IIC_POLLED)) {
- pcf->res_irq = bus_alloc_resource(pcfdev, SYS_RES_IRQ, &pcf->rid_irq,
- 0ul, ~0ul, 1, RF_ACTIVE);
- if (pcf->res_irq == 0) {
- device_printf(pcfdev, "can't reserve irq, polled mode.\n");
- pcf->pcf_flags |= IIC_POLLED;
- }
- }
-
- /* reset the chip */
- pcf_rst_card(pcfdev, IIC_FASTEST, PCF_DEFAULT_ADDR, NULL);
-
- return (0);
-error:
- if (pcf->res_ioport != 0) {
- bus_deactivate_resource(pcfdev, SYS_RES_IOPORT, pcf->rid_ioport,
- pcf->res_ioport);
- bus_release_resource(pcfdev, SYS_RES_IOPORT, pcf->rid_ioport,
- pcf->res_ioport);
- }
- return (ENXIO);
-}
-
-static int
-pcf_attach(device_t pcfdev)
-{
- struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
- device_t parent = device_get_parent(pcfdev);
- int error = 0;
-
- if (pcf->res_irq) {
- /* default to the tty mask for registration */ /* XXX */
- error = BUS_SETUP_INTR(parent, pcfdev, pcf->res_irq, INTR_TYPE_NET,
- pcfintr, pcfdev, &pcf->intr_cookie);
- if (error)
- return (error);
- }
-
- pcf->iicbus = device_add_child(pcfdev, "iicbus", -1);
-
- /* probe and attach the iicbus */
- bus_generic_attach(pcfdev);
-
- return (0);
-}
+/* Not so official debugging option. */
+/* #define PCFDEBUG */
-static int
-pcf_print_child(device_t bus, device_t dev)
-{
- struct pcf_softc *pcf = (struct pcf_softc *)device_get_softc(bus);
- int retval = 0;
-
- retval += bus_print_child_header(bus, dev);
- retval += printf(" on %s addr 0x%x\n", device_get_nameunit(bus),
- (int)pcf->pcf_addr);
-
- return (retval);
-}
-
-/*
- * PCF8584 datasheet : when operate at 8 MHz or more, a minimun time of
- * 6 clocks cycles must be left between two consecutives access
- */
-#define pcf_nops() DELAY(10)
-
-#define dummy_read(pcf) PCF_GET_S0(pcf)
-#define dummy_write(pcf) PCF_SET_S0(pcf, 0)
-
-/*
- * Specific register access to PCF8584
- */
-static void PCF_SET_S0(struct pcf_softc *pcf, int data)
-{
- outb(pcf->pcf_base, data);
- pcf_nops();
-}
-
-static void PCF_SET_S1(struct pcf_softc *pcf, int data)
-{
- outb(pcf->pcf_base+1, data);
- pcf_nops();
-}
-
-static char PCF_GET_S0(struct pcf_softc *pcf)
-{
- char data;
-
- data = inb(pcf->pcf_base);
- pcf_nops();
-
- return (data);
-}
-
-static char PCF_GET_S1(struct pcf_softc *pcf)
-{
- char data;
-
- data = inb(pcf->pcf_base+1);
- pcf_nops();
-
- return (data);
-}
+static int pcf_wait_byte(struct pcf_softc *pcf);
+static int pcf_noack(struct pcf_softc *pcf, int timeout);
/*
* Polling mode for master operations wait for a new
* byte incomming or outgoing
*/
-static int pcf_wait_byte(struct pcf_softc *pcf)
+int
+pcf_wait_byte(struct pcf_softc *sc)
{
int counter = TIMEOUT;
while (counter--) {
- if ((PCF_GET_S1(pcf) & PIN) == 0)
+ if ((pcf_get_S1(sc) & PIN) == 0)
return (0);
}
return (IIC_ETIMEOUT);
}
-static int pcf_stop(device_t pcfdev)
+int
+pcf_stop(device_t dev)
{
- struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
+ struct pcf_softc *sc = DEVTOSOFTC(dev);
+#ifdef PCFDEBUG
+ device_printf(dev, " >> stop\n");
+#endif
/*
* Send STOP condition iff the START condition was previously sent.
* STOP is sent only once even if an iicbus_stop() is called after
- * an iicbus_read()... see pcf_read(): the pcf needs to send the stop
+ * an iicbus_read()... see pcf_read(): the PCF needs to send the stop
* before the last char is read.
*/
- if (pcf->pcf_started) {
+ if (sc->pcf_started) {
/* set stop condition and enable IT */
- PCF_SET_S1(pcf, PIN|ES0|ENI|STO|ACK);
+ pcf_set_S1(sc, PIN|ESO|ENI|STO|ACK);
- pcf->pcf_started = 0;
+ sc->pcf_started = 0;
}
return (0);
}
-static int pcf_noack(struct pcf_softc *pcf, int timeout)
+int
+pcf_noack(struct pcf_softc *sc, int timeout)
{
int noack;
int k = timeout/10;
do {
- noack = PCF_GET_S1(pcf) & LRB;
+ noack = pcf_get_S1(sc) & LRB;
if (!noack)
break;
DELAY(10); /* XXX wait 10 us */
@@ -316,24 +108,29 @@ static int pcf_noack(struct pcf_softc *pcf, int timeout)
return (noack);
}
-static int pcf_repeated_start(device_t pcfdev, u_char slave, int timeout)
+int
+pcf_repeated_start(device_t dev, u_char slave, int timeout)
{
- struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
+ struct pcf_softc *sc = DEVTOSOFTC(dev);
int error = 0;
+#ifdef PCFDEBUG
+ device_printf(dev, " >> repeated start for slave %#x\n",
+ (unsigned)slave);
+#endif
/* repeated start */
- PCF_SET_S1(pcf, ES0|STA|STO|ACK);
+ pcf_set_S1(sc, ESO|STA|STO|ACK);
/* set slave address to PCF. Last bit (LSB) must be set correctly
* according to transfer direction */
- PCF_SET_S0(pcf, slave);
+ pcf_set_S0(sc, slave);
/* wait for address sent, polling */
- if ((error = pcf_wait_byte(pcf)))
+ if ((error = pcf_wait_byte(sc)))
goto error;
/* check for ack */
- if (pcf_noack(pcf, timeout)) {
+ if (pcf_noack(sc, timeout)) {
error = IIC_ENOACK;
goto error;
}
@@ -341,33 +138,37 @@ static int pcf_repeated_start(device_t pcfdev, u_char slave, int timeout)
return (0);
error:
- pcf_stop(pcfdev);
+ pcf_stop(dev);
return (error);
}
-static int pcf_start(device_t pcfdev, u_char slave, int timeout)
+int
+pcf_start(device_t dev, u_char slave, int timeout)
{
- struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
+ struct pcf_softc *sc = DEVTOSOFTC(dev);
int error = 0;
- if ((PCF_GET_S1(pcf) & nBB) == 0)
+#ifdef PCFDEBUG
+ device_printf(dev, " >> start for slave %#x\n", (unsigned)slave);
+#endif
+ if ((pcf_get_S1(sc) & nBB) == 0)
return (IIC_EBUSBSY);
/* set slave address to PCF. Last bit (LSB) must be set correctly
* according to transfer direction */
- PCF_SET_S0(pcf, slave);
+ pcf_set_S0(sc, slave);
/* START only */
- PCF_SET_S1(pcf, PIN|ES0|STA|ACK);
+ pcf_set_S1(sc, PIN|ESO|STA|ACK);
- pcf->pcf_started = 1;
+ sc->pcf_started = 1;
/* wait for address sent, polling */
- if ((error = pcf_wait_byte(pcf)))
+ if ((error = pcf_wait_byte(sc)))
goto error;
/* check for ACK */
- if (pcf_noack(pcf, timeout)) {
+ if (pcf_noack(sc, timeout)) {
error = IIC_ENOACK;
goto error;
}
@@ -375,77 +176,78 @@ static int pcf_start(device_t pcfdev, u_char slave, int timeout)
return (0);
error:
- pcf_stop(pcfdev);
+ pcf_stop(dev);
return (error);
}
-static void
-pcfintr(void *arg)
+void
+pcf_intr(void *arg)
{
- device_t pcfdev = (device_t)arg;
- struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
+ device_t dev = (device_t)arg;
+ struct pcf_softc *sc = DEVTOSOFTC(dev);
char data, status, addr;
char error = 0;
- status = PCF_GET_S1(pcf);
+ status = pcf_get_S1(sc);
if (status & PIN) {
- device_printf(pcfdev, "spurious interrupt, status=0x%x\n", status & 0xff);
+ device_printf(dev, "spurious interrupt, status=0x%x\n",
+ status & 0xff);
goto error;
- }
+ }
if (status & LAB)
- device_printf(pcfdev, "bus arbitration lost!\n");
+ device_printf(dev, "bus arbitration lost!\n");
if (status & BER) {
error = IIC_EBUSERR;
- iicbus_intr(pcf->iicbus, INTR_ERROR, &error);
+ iicbus_intr(sc->iicbus, INTR_ERROR, &error);
goto error;
}
do {
- status = PCF_GET_S1(pcf);
+ status = pcf_get_S1(sc);
- switch(pcf->pcf_slave_mode) {
+ switch(sc->pcf_slave_mode) {
case SLAVE_TRANSMITTER:
if (status & LRB) {
/* ack interrupt line */
- dummy_write(pcf);
+ dummy_write(sc);
/* no ack, don't send anymore */
- pcf->pcf_slave_mode = SLAVE_RECEIVER;
+ sc->pcf_slave_mode = SLAVE_RECEIVER;
- iicbus_intr(pcf->iicbus, INTR_NOACK, NULL);
+ iicbus_intr(sc->iicbus, INTR_NOACK, NULL);
break;
}
/* get data from upper code */
- iicbus_intr(pcf->iicbus, INTR_TRANSMIT, &data);
+ iicbus_intr(sc->iicbus, INTR_TRANSMIT, &data);
+
+ pcf_set_S0(sc, data);
+ break;
- PCF_SET_S0(pcf, data);
- break;
-
case SLAVE_RECEIVER:
if (status & AAS) {
- addr = PCF_GET_S0(pcf);
+ addr = pcf_get_S0(sc);
if (status & AD0)
- iicbus_intr(pcf->iicbus, INTR_GENERAL, &addr);
+ iicbus_intr(sc->iicbus, INTR_GENERAL, &addr);
else
- iicbus_intr(pcf->iicbus, INTR_START, &addr);
+ iicbus_intr(sc->iicbus, INTR_START, &addr);
if (addr & LSB) {
- pcf->pcf_slave_mode = SLAVE_TRANSMITTER;
+ sc->pcf_slave_mode = SLAVE_TRANSMITTER;
/* get the first char from upper code */
- iicbus_intr(pcf->iicbus, INTR_TRANSMIT, &data);
+ iicbus_intr(sc->iicbus, INTR_TRANSMIT, &data);
/* send first data byte */
- PCF_SET_S0(pcf, data);
+ pcf_set_S0(sc, data);
}
break;
@@ -454,105 +256,107 @@ pcfintr(void *arg)
/* stop condition received? */
if (status & STS) {
/* ack interrupt line */
- dummy_read(pcf);
+ dummy_read(sc);
/* emulate intr stop condition */
- iicbus_intr(pcf->iicbus, INTR_STOP, NULL);
+ iicbus_intr(sc->iicbus, INTR_STOP, NULL);
} else {
/* get data, ack interrupt line */
- data = PCF_GET_S0(pcf);
+ data = pcf_get_S0(sc);
/* deliver the character */
- iicbus_intr(pcf->iicbus, INTR_RECEIVE, &data);
+ iicbus_intr(sc->iicbus, INTR_RECEIVE, &data);
}
break;
default:
panic("%s: unknown slave mode (%d)!", __func__,
- pcf->pcf_slave_mode);
+ sc->pcf_slave_mode);
}
- } while ((PCF_GET_S1(pcf) & PIN) == 0);
+ } while ((pcf_get_S1(sc) & PIN) == 0);
return;
error:
/* unknown event on bus...reset PCF */
- PCF_SET_S1(pcf, PIN|ES0|ENI|ACK);
+ pcf_set_S1(sc, PIN|ESO|ENI|ACK);
- pcf->pcf_slave_mode = SLAVE_RECEIVER;
+ sc->pcf_slave_mode = SLAVE_RECEIVER;
return;
}
-static int pcf_rst_card(device_t pcfdev, u_char speed, u_char addr, u_char *oldaddr)
+int
+pcf_rst_card(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
{
- struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
+ struct pcf_softc *sc = DEVTOSOFTC(dev);
if (oldaddr)
- *oldaddr = pcf->pcf_addr;
+ *oldaddr = sc->pcf_addr;
/* retrieve own address from bus level */
if (!addr)
- pcf->pcf_addr = PCF_DEFAULT_ADDR;
+ sc->pcf_addr = PCF_DEFAULT_ADDR;
else
- pcf->pcf_addr = addr;
-
- PCF_SET_S1(pcf, PIN); /* initialize S1 */
+ sc->pcf_addr = addr;
+
+ pcf_set_S1(sc, PIN); /* initialize S1 */
/* own address S'O<>0 */
- PCF_SET_S0(pcf, pcf->pcf_addr >> 1);
+ pcf_set_S0(sc, sc->pcf_addr >> 1);
/* select clock register */
- PCF_SET_S1(pcf, PIN|ES1);
+ pcf_set_S1(sc, PIN|ES1);
/* select bus speed : 18=90kb, 19=45kb, 1A=11kb, 1B=1.5kb */
switch (speed) {
case IIC_SLOW:
- PCF_SET_S0(pcf, 0x1b);
+ pcf_set_S0(sc, 0x1b); /* XXX Sun uses 0x1f */
break;
case IIC_FAST:
- PCF_SET_S0(pcf, 0x19);
+ pcf_set_S0(sc, 0x19); /* XXX Sun: 0x1d */
break;
case IIC_UNKNOWN:
case IIC_FASTEST:
default:
- PCF_SET_S0(pcf, 0x18);
+ pcf_set_S0(sc, 0x18); /* XXX Sun: 0x1c */
break;
}
/* set bus on, ack=yes, INT=yes */
- PCF_SET_S1(pcf, PIN|ES0|ENI|ACK);
+ pcf_set_S1(sc, PIN|ESO|ENI|ACK);
- pcf->pcf_slave_mode = SLAVE_RECEIVER;
+ sc->pcf_slave_mode = SLAVE_RECEIVER;
return (0);
}
-static int
-pcf_write(device_t pcfdev, char *buf, int len, int *sent, int timeout /* us */)
+int
+pcf_write(device_t dev, char *buf, int len, int *sent, int timeout /* us */)
{
- struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
+ struct pcf_softc *sc = DEVTOSOFTC(dev);
int bytes, error = 0;
#ifdef PCFDEBUG
- printf("pcf%d: >> writing %d bytes\n", device_get_unit(pcfdev), len);
+ device_printf(dev, " >> writing %d bytes: %#x%s\n", len,
+ (unsigned)buf[0], len > 1? "...": "");
#endif
bytes = 0;
while (len) {
- PCF_SET_S0(pcf, *buf++);
+ pcf_set_S0(sc, *buf++);
/* wait for the byte to be send */
- if ((error = pcf_wait_byte(pcf)))
+ if ((error = pcf_wait_byte(sc)))
goto error;
/* check if ack received */
- if (pcf_noack(pcf, timeout)) {
+ if (pcf_noack(sc, timeout)) {
error = IIC_ENOACK;
goto error;
}
@@ -565,31 +369,31 @@ error:
*sent = bytes;
#ifdef PCFDEBUG
- printf("pcf%d: >> %d bytes written (%d)\n",
- device_get_unit(pcfdev), bytes, error);
+ device_printf(dev, " >> %d bytes written (%d)\n", bytes, error);
#endif
return (error);
}
-static int
-pcf_read(device_t pcfdev, char *buf, int len, int *read, int last,
- int delay /* us */)
+int
+pcf_read(device_t dev, char *buf, int len, int *read, int last,
+ int delay /* us */)
{
- struct pcf_softc *pcf = DEVTOSOFTC(pcfdev);
+ struct pcf_softc *sc = DEVTOSOFTC(dev);
int bytes, error = 0;
-
#ifdef PCFDEBUG
- printf("pcf%d: << reading %d bytes\n", device_get_unit(pcfdev), len);
+ char *obuf = buf;
+
+ device_printf(dev, " << reading %d bytes\n", len);
#endif
/* trig the bus to get the first data byte in S0 */
if (len) {
if (len == 1 && last)
/* just one byte to read */
- PCF_SET_S1(pcf, ES0); /* no ack */
+ pcf_set_S1(sc, ESO); /* no ack */
- dummy_read(pcf);
+ dummy_read(sc);
}
bytes = 0;
@@ -598,22 +402,22 @@ pcf_read(device_t pcfdev, char *buf, int len, int *read, int last,
/* XXX delay needed here */
/* wait for trigged byte */
- if ((error = pcf_wait_byte(pcf))) {
- pcf_stop(pcfdev);
+ if ((error = pcf_wait_byte(sc))) {
+ pcf_stop(dev);
goto error;
}
if (len == 1 && last)
/* ok, last data byte already in S0, no I2C activity
- * on next PCF_GET_S0() */
- pcf_stop(pcfdev);
+ * on next pcf_get_S0() */
+ pcf_stop(dev);
else if (len == 2 && last)
/* next trigged byte with no ack */
- PCF_SET_S1(pcf, ES0);
+ pcf_set_S1(sc, ESO);
/* receive byte, trig next byte */
- *buf++ = PCF_GET_S0(pcf);
+ *buf++ = pcf_get_S0(sc);
len --;
bytes ++;
@@ -623,11 +427,9 @@ error:
*read = bytes;
#ifdef PCFDEBUG
- printf("pcf%d: << %d bytes read (%d)\n",
- device_get_unit(pcfdev), bytes, error);
+ device_printf(dev, " << %d bytes read (%d): %#x%s\n", bytes, error,
+ (unsigned)obuf[0], bytes > 1? "...": "");
#endif
return (error);
}
-
-DRIVER_MODULE(pcf, isa, pcf_driver, pcf_devclass, 0, 0);
diff --git a/sys/dev/pcf/pcf_isa.c b/sys/dev/pcf/pcf_isa.c
new file mode 100644
index 0000000..0b0968a
--- /dev/null
+++ b/sys/dev/pcf/pcf_isa.c
@@ -0,0 +1,185 @@
+/*-
+ * Copyright (c) 2004 Joerg Wunsch
+ *
+ * derived from sys/i386/isa/pcf.c which is:
+ *
+ * Copyright (c) 1998 Nicolas Souchu, Marc Bouget
+ * 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.
+ * 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 THE AUTHOR AND CONTRIBUTORS ``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 THE AUTHOR OR CONTRIBUTORS 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.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Hardware driver for a Philips PCF8584 I2C bus controller sitting
+ * on a generic ISA bus.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/resource.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <sys/rman.h>
+
+#include <isa/isareg.h>
+#include <isa/isavar.h>
+
+#include <dev/iicbus/iiconf.h>
+#include <dev/pcf/pcfvar.h>
+#include "iicbus_if.h"
+
+static int pcf_probe(device_t);
+static int pcf_attach(device_t);
+static int pcf_detach(device_t);
+
+static device_method_t pcf_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, pcf_probe),
+ DEVMETHOD(device_attach, pcf_attach),
+ DEVMETHOD(device_detach, pcf_detach),
+
+ /* iicbus interface */
+ DEVMETHOD(iicbus_callback, iicbus_null_callback),
+ DEVMETHOD(iicbus_repeated_start, pcf_repeated_start),
+ DEVMETHOD(iicbus_start, pcf_start),
+ DEVMETHOD(iicbus_stop, pcf_stop),
+ DEVMETHOD(iicbus_write, pcf_write),
+ DEVMETHOD(iicbus_read, pcf_read),
+ DEVMETHOD(iicbus_reset, pcf_rst_card),
+ { 0, 0 }
+};
+
+static devclass_t pcf_devclass;
+
+static driver_t pcf_driver = {
+ "pcf",
+ pcf_methods,
+ sizeof(struct pcf_softc),
+};
+
+static int
+pcf_probe(device_t dev)
+{
+
+ device_set_desc(dev, "PCF8584 I2C bus controller");
+ return (0);
+}
+
+static int
+pcf_attach(device_t dev)
+{
+ struct pcf_softc *sc;
+ int rv = ENXIO;
+
+ sc = DEVTOSOFTC(dev);
+ bzero(sc, sizeof(struct pcf_softc));
+
+ /* IO port is mandatory */
+ sc->res_ioport = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
+ &sc->rid_ioport, RF_ACTIVE);
+ if (sc->res_ioport == 0) {
+ device_printf(dev, "cannot reserve I/O port range\n");
+ goto error;
+ }
+
+ sc->pcf_flags = device_get_flags(dev);
+
+ if (!(sc->pcf_flags & IIC_POLLED)) {
+ sc->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->rid_irq,
+ RF_ACTIVE);
+ if (sc->res_irq == 0) {
+ device_printf(dev, "can't reserve irq, polled mode.\n");
+ sc->pcf_flags |= IIC_POLLED;
+ }
+ }
+
+ /* reset the chip */
+ pcf_rst_card(dev, IIC_FASTEST, PCF_DEFAULT_ADDR, NULL);
+
+ rv = BUS_SETUP_INTR(device_get_parent(dev), dev, sc->res_irq,
+ INTR_TYPE_NET /* | INTR_ENTROPY */,
+ pcf_intr, sc, &sc->intr_cookie);
+ if (rv) {
+ device_printf(dev, "could not setup IRQ\n");
+ goto error;
+ }
+
+ if ((sc->iicbus = device_add_child(dev, "iicbus", -1)) == NULL)
+ device_printf(dev, "could not allocate iicbus instance\n");
+
+ /* probe and attach the iicbus */
+ bus_generic_attach(dev);
+
+ return (0);
+
+error:
+ if (sc->res_irq != 0) {
+ bus_deactivate_resource(dev, SYS_RES_IRQ, sc->rid_irq,
+ sc->res_irq);
+ bus_release_resource(dev, SYS_RES_IRQ, sc->rid_irq,
+ sc->res_irq);
+ }
+ if (sc->res_ioport != 0) {
+ bus_deactivate_resource(dev, SYS_RES_IOPORT, sc->rid_ioport,
+ sc->res_ioport);
+ bus_release_resource(dev, SYS_RES_IOPORT, sc->rid_ioport,
+ sc->res_ioport);
+ }
+ return (rv);
+}
+
+static int
+pcf_detach(device_t dev)
+{
+ struct pcf_softc *sc;
+ int rv;
+
+ sc = DEVTOSOFTC(dev);
+
+ if ((rv = bus_generic_detach(dev)) != 0)
+ return (rv);
+
+ if ((rv = device_delete_child(dev, sc->iicbus)) != 0)
+ return (rv);
+
+ if (sc->res_irq != 0) {
+ BUS_TEARDOWN_INTR(device_get_parent(dev), dev, sc->res_irq,
+ sc->intr_cookie);
+ bus_deactivate_resource(dev, SYS_RES_IRQ, sc->rid_irq, sc->res_irq);
+ bus_release_resource(dev, SYS_RES_IRQ, sc->rid_irq, sc->res_irq);
+ }
+
+ bus_deactivate_resource(dev, SYS_RES_IOPORT, sc->rid_ioport, sc->res_ioport);
+ bus_release_resource(dev, SYS_RES_IOPORT, sc->rid_ioport, sc->res_ioport);
+
+ return (0);
+}
+
+DRIVER_MODULE(pcf, ebus, pcf_driver, pcf_devclass, 0, 0);
+MODULE_DEPEND(pcf, iicbus, PCF_MINVER, PCF_PREFVER, PCF_MAXVER);
+MODULE_VERSION(pcf, PCF_MODVER);
diff --git a/sys/dev/pcf/pcfvar.h b/sys/dev/pcf/pcfvar.h
new file mode 100644
index 0000000..ce7e9a9
--- /dev/null
+++ b/sys/dev/pcf/pcfvar.h
@@ -0,0 +1,142 @@
+/*-
+ * Copyright (c) 1998 Nicolas Souchu, Marc Bouget
+ * Copyright (c) 2004 Joerg Wunsch
+ * 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.
+ * 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 THE AUTHOR AND CONTRIBUTORS ``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 THE AUTHOR OR CONTRIBUTORS 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.
+ *
+ * $FreeBSD$
+ */
+
+#define IO_PCFSIZE 2
+
+#define TIMEOUT 9999 /* XXX */
+
+/* Status bits of S1 register (read only) */
+#define nBB 0x01 /* busy when low set/reset by STOP/START*/
+#define LAB 0x02 /* lost arbitration bit in multi-master mode */
+#define AAS 0x04 /* addressed as slave */
+#define LRB 0x08 /* last received byte when not AAS */
+#define AD0 0x08 /* general call received when AAS */
+#define BER 0x10 /* bus error, misplaced START or STOP */
+#define STS 0x20 /* STOP detected in slave receiver mode */
+#define PIN 0x80 /* pending interrupt not (r/w) */
+
+/* Control bits of S1 register (write only) */
+#define ACK 0x01
+#define STO 0x02
+#define STA 0x04
+#define ENI 0x08
+#define ES2 0x10
+#define ES1 0x20
+#define ESO 0x40
+
+#define BUFSIZE 2048
+
+#define SLAVE_TRANSMITTER 0x1
+#define SLAVE_RECEIVER 0x2
+
+#define PCF_DEFAULT_ADDR 0xaa
+
+struct pcf_softc {
+ u_char pcf_addr; /* interface I2C address */
+ int pcf_flags; /* IIC_POLLED? */
+ int pcf_slave_mode; /* receiver or transmitter */
+ int pcf_started; /* 1 if start condition sent */
+
+ device_t iicbus; /* the corresponding iicbus */
+
+ /* Resource handling stuff. */
+ void *intr_cookie;
+ int rid_ioport;
+ int rid_irq;
+ struct resource *res_ioport;
+ struct resource *res_irq;
+};
+#define DEVTOSOFTC(dev) ((struct pcf_softc *)device_get_softc(dev))
+
+/*
+ * PCF8584 datasheet : when operate at 8 MHz or more, a minimun time of
+ * 6 clocks cycles must be left between two consecutives access
+ */
+#define pcf_nops() DELAY(10)
+
+#define dummy_read(sc) pcf_get_S0(sc)
+#define dummy_write(sc) pcf_set_S0(sc, 0)
+
+/*
+ * Specific register access to PCF8584
+ */
+static __inline__ void
+pcf_set_S0(struct pcf_softc *sc, int data)
+{
+ bus_space_write_1(sc->res_ioport->r_bustag,
+ sc->res_ioport->r_bushandle,
+ 0, data);
+ pcf_nops();
+}
+
+static __inline__ void
+pcf_set_S1(struct pcf_softc *sc, int data)
+{
+ bus_space_write_1(sc->res_ioport->r_bustag,
+ sc->res_ioport->r_bushandle,
+ 1, data);
+ pcf_nops();
+}
+
+static __inline__ char
+pcf_get_S0(struct pcf_softc *sc)
+{
+ char data;
+
+ data = bus_space_read_1(sc->res_ioport->r_bustag,
+ sc->res_ioport->r_bushandle, 0);
+ pcf_nops();
+
+ return (data);
+}
+
+static __inline__ char
+pcf_get_S1(struct pcf_softc *sc)
+{
+ char data;
+
+ data = bus_space_read_1(sc->res_ioport->r_bustag,
+ sc->res_ioport->r_bushandle, 1);
+ pcf_nops();
+
+ return (data);
+}
+
+extern int pcf_repeated_start(device_t, u_char, int);
+extern int pcf_start(device_t, u_char, int);
+extern int pcf_stop(device_t);
+extern int pcf_write(device_t, char *, int, int *, int);
+extern int pcf_read(device_t, char *, int, int *, int, int);
+extern int pcf_rst_card(device_t, u_char, u_char, u_char *);
+extern driver_intr_t pcf_intr;
+
+#define PCF_MODVER 1
+#define PCF_MINVER 1
+#define PCF_MAXVER 1
+#define PCF_PREFVER PCF_MODVER
OpenPOWER on IntegriCloud