summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoerg Wunsch <joerg@FreeBSD.org>2004-05-25 07:42:45 +0000
committerJoerg Wunsch <joerg@FreeBSD.org>2004-05-25 07:42:45 +0000
commite0efc557e743a79bec43f17a42ab0b8ffc80db37 (patch)
tree443bb078c2a5d2f80d0822545b68d651d3746418
parent146408992ff0aa7a81b68b455bf80fa4ffd26523 (diff)
Notes
-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 000000000000..81269241a7f3
--- /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 64c059e51416..08b088f60bec 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 000000000000..0b0968a9e46c
--- /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 000000000000..ce7e9a96cee1
--- /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