From 2ae8dc409583e4ed96ca74b7f4817f015685e45f Mon Sep 17 00:00:00 2001 From: nsouch Date: Sat, 31 Oct 1998 11:37:09 +0000 Subject: pcf.c: timeout management added ppc.c: nsc code improved. Actually, a complete rewrite. --- sys/dev/pcf/pcf.c | 137 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 90 insertions(+), 47 deletions(-) (limited to 'sys/dev/pcf/pcf.c') diff --git a/sys/dev/pcf/pcf.c b/sys/dev/pcf/pcf.c index bee0e15..771dd30 100644 --- a/sys/dev/pcf/pcf.c +++ b/sys/dev/pcf/pcf.c @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: pcf.c,v 1.1 1998/09/03 21:01:22 nsouch Exp $ + * $Id: pcf.c,v 1.2 1998/10/22 05:58:40 bde Exp $ * */ #include @@ -41,7 +41,7 @@ #include #include "iicbus_if.h" -#define TIMEOUT 99999 /* XXX */ +#define TIMEOUT 9999 /* XXX */ /* Status bits of S1 register (read only) */ #define nBB 0x01 /* busy when low set/reset by STOP/START*/ @@ -67,13 +67,15 @@ #define SLAVE_TRANSMITTER 0x1 #define SLAVE_RECEIVER 0x2 +#define PCF_DEFAULT_ADDR 0xaa + struct pcf_softc { int pcf_base; /* isa port */ + u_char pcf_addr; /* interface I2C address */ - int pcf_count; - int pcf_own_address; /* own address */ int pcf_slave_mode; /* receiver or transmitter */ + int pcf_started; /* 1 if start condition sent */ device_t iicbus; /* the corresponding iicbus */ }; @@ -103,8 +105,8 @@ static int pcf_probe(device_t); static int pcf_attach(device_t); static void pcf_print_child(device_t, device_t); -static int pcf_repeated_start(device_t, u_char); -static int pcf_start(device_t, u_char); +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 *); static int pcf_read(device_t, char *, int, int *); @@ -120,6 +122,7 @@ static device_method_t pcf_methods[] = { 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), @@ -165,6 +168,7 @@ pcfprobe_isa(struct isa_device *dvp) /* XXX add the pcf device to the root_bus until isa bus exists */ pcfdev = device_add_child(root_bus, "pcf", pcf->pcf_unit, NULL); + device_probe_and_attach(pcfdev); if (!pcfdev) goto error; @@ -188,15 +192,20 @@ static int pcf_probe(device_t pcfdev) { struct pcf_softc *pcf = (struct pcf_softc *)device_get_softc(pcfdev); + int unit = device_get_unit(pcfdev); - /* XXX try do detect chipset */ + /* retrieve base address from isa initialization + * + * XXX should use ivars with isabus + */ + pcf->pcf_base = pcfdata[unit]->pcf_base; - device_set_desc(pcfdev, "PCF8584 I2C bus controller"); + /* reset the chip */ + pcf_rst_card(pcfdev, IIC_FASTEST, PCF_DEFAULT_ADDR, NULL); - pcf->iicbus = iicbus_alloc_bus(pcfdev); + /* XXX try do detect chipset */ - if (!pcf->iicbus) - return (EINVAL); + device_set_desc(pcfdev, "PCF8584 I2C bus controller"); return (0); } @@ -205,13 +214,11 @@ static int pcf_attach(device_t pcfdev) { struct pcf_softc *pcf = (struct pcf_softc *)device_get_softc(pcfdev); - int unit = device_get_unit(pcfdev); - /* retrieve base address from isa initialization - * - * XXX should use ivars with isabus - */ - pcf->pcf_base = pcfdata[unit]->pcf_base; + pcf->iicbus = iicbus_alloc_bus(pcfdev); + + if (!pcf->iicbus) + return (EINVAL); /* probe and attach the iicbus */ device_probe_and_attach(pcf->iicbus); @@ -222,8 +229,10 @@ pcf_attach(device_t pcfdev) static void pcf_print_child(device_t bus, device_t dev) { + struct pcf_softc *pcf = (struct pcf_softc *)device_get_softc(bus); + printf(" on %s%d addr 0x%x", device_get_name(bus), - device_get_unit(bus), iicbus_get_own_address(dev)); + device_get_unit(bus), (int)pcf->pcf_addr); return; } @@ -293,13 +302,39 @@ static int pcf_stop(device_t pcfdev) { struct pcf_softc *pcf = DEVTOSOFTC(pcfdev); - /* set stop condition and enable IT */ - PCF_SET_S1(pcf, PIN|ES0|ENI|STO|ACK); + /* + * Send STOP condition iff the START condition was previously sent. + * STOP is sent only once even if a iicbus_stop() is called after + * an iicbus_read()... see pcf_read(): the pcf needs to send the stop + * before the last char is read. + */ + if (pcf->pcf_started) { + /* set stop condition and enable IT */ + PCF_SET_S1(pcf, PIN|ES0|ENI|STO|ACK); + + pcf->pcf_started = 0; + } return (0); } -static int pcf_repeated_start(device_t pcfdev, u_char slave) + +static int pcf_noack(struct pcf_softc *pcf, int timeout) +{ + int noack; + int k = timeout/10; + + do { + noack = PCF_GET_S1(pcf) & LRB; + if (!noack) + break; + DELAY(10); /* XXX wait 10 us */ + } while (k--); + + return (noack); +} + +static int pcf_repeated_start(device_t pcfdev, u_char slave, int timeout) { struct pcf_softc *pcf = DEVTOSOFTC(pcfdev); int error = 0; @@ -315,8 +350,8 @@ static int pcf_repeated_start(device_t pcfdev, u_char slave) if ((error = pcf_wait_byte(pcf))) goto error; - /* check ACK */ - if (PCF_GET_S1(pcf) & LRB) { + /* check for ack */ + if (pcf_noack(pcf, timeout)) { error = IIC_ENOACK; goto error; } @@ -328,7 +363,7 @@ error: return (error); } -static int pcf_start(device_t pcfdev, u_char slave) +static int pcf_start(device_t pcfdev, u_char slave, int timeout) { struct pcf_softc *pcf = DEVTOSOFTC(pcfdev); int error = 0; @@ -343,12 +378,14 @@ static int pcf_start(device_t pcfdev, u_char slave) /* START only */ PCF_SET_S1(pcf, PIN|ES0|STA|ACK); + pcf->pcf_started = 1; + /* wait for address sent, polling */ if ((error = pcf_wait_byte(pcf))) goto error; - /* check ACK */ - if (PCF_GET_S1(pcf) & LRB) { + /* check for ACK */ + if (pcf_noack(pcf, timeout)) { error = IIC_ENOACK; goto error; } @@ -468,19 +505,23 @@ error: return; } -static int pcf_rst_card(device_t pcfdev, u_char speed) +static int pcf_rst_card(device_t pcfdev, u_char speed, u_char addr, u_char *oldaddr) { struct pcf_softc *pcf = DEVTOSOFTC(pcfdev); - u_char ownaddr; + + if (oldaddr) + *oldaddr = pcf->pcf_addr; /* retrieve own address from bus level */ - if ((ownaddr = iicbus_get_own_address(pcf->iicbus)) == 0) - ownaddr = 0xaa; + if (!addr) + pcf->pcf_addr = PCF_DEFAULT_ADDR; + else + pcf->pcf_addr = addr; PCF_SET_S1(pcf, PIN); /* initialize S1 */ /* own address S'O<>0 */ - PCF_SET_S0(pcf, ownaddr >> 1); + PCF_SET_S0(pcf, pcf->pcf_addr >> 1); /* select clock register */ PCF_SET_S1(pcf, PIN|ES1); @@ -511,7 +552,7 @@ static int pcf_rst_card(device_t pcfdev, u_char speed) } static int -pcf_write(device_t pcfdev, char *buf, int len, int *sent) +pcf_write(device_t pcfdev, char *buf, int len, int *sent, int timeout /* us */) { struct pcf_softc *pcf = DEVTOSOFTC(pcfdev); int bytes, error = 0; @@ -525,10 +566,12 @@ pcf_write(device_t pcfdev, char *buf, int len, int *sent) PCF_SET_S0(pcf, *buf++); + /* wait for the byte to be send */ if ((error = pcf_wait_byte(pcf))) goto error; - if (PCF_GET_S1(pcf) & LRB) { + /* check if ack received */ + if (pcf_noack(pcf, timeout)) { error = IIC_ENOACK; goto error; } @@ -549,7 +592,8 @@ error: } static int -pcf_read(device_t pcfdev, char *buf, int len, int *read) +pcf_read(device_t pcfdev, char *buf, int len, int *read, int last, + int delay /* us */) { struct pcf_softc *pcf = DEVTOSOFTC(pcfdev); int bytes, error = 0; @@ -560,7 +604,7 @@ pcf_read(device_t pcfdev, char *buf, int len, int *read) /* trig the bus to get the first data byte in S0 */ if (len) { - if (len == 1) + if (len == 1 && last) /* just one byte to read */ PCF_SET_S1(pcf, ES0); /* no ack */ @@ -570,26 +614,25 @@ pcf_read(device_t pcfdev, char *buf, int len, int *read) bytes = 0; while (len) { + /* XXX delay needed here */ + + /* wait for trigged byte */ if ((error = pcf_wait_byte(pcf))) { pcf_stop(pcfdev); goto error; } - if (len == 1) { - - /* ok, last data byte already in S0 */ + if (len == 1 && last) + /* ok, last data byte already in S0, no I2C activity + * on next PCF_GET_S0() */ pcf_stop(pcfdev); - *buf = PCF_GET_S0(pcf); + else if (len == 2 && last) + /* next trigged byte with no ack */ + PCF_SET_S1(pcf, ES0); - } else { - if (len == 2) - /* next trigged byte with no ack */ - PCF_SET_S1(pcf, ES0); - - /* read last data byte, trig for next data byte */ - *buf++ = PCF_GET_S0(pcf); - } + /* receive byte, trig next byte */ + *buf++ = PCF_GET_S0(pcf); len --; bytes ++; -- cgit v1.1