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 +__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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#include + +#include +#include +#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 #include + #include -#include -#include - #include +#include #include "iicbus_if.h" -#define IO_PCFSIZE 2 +/* Not so official debugging option. */ +/* #define PCFDEBUG */ -#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); -} - -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 +__FBSDID("$FreeBSD$"); + +/* + * Hardware driver for a Philips PCF8584 I2C bus controller sitting + * on a generic ISA bus. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include +#include +#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