Date: Thu, 22 Dec 2011 12:55:13 +0100 From: Stefan Bethke <stb@lassitu.de> To: Adrian Chadd <adrian@freebsd.org> Cc: Oleksandr Tymoshenko <gonzo@freebsd.org>, "freebsd-embedded@freebsd.org" <freebsd-embedded@freebsd.org> Subject: Re: Updated switch/glue patch? Message-ID: <45529EC2-73BE-4F69-A9BE-E22D9FEAADD7@lassitu.de> In-Reply-To: <CAJ-VmokwSHN8U2=HJQT9kxFQ9oE-6H2h3KDb%2BMHN1Z8sur9=yw@mail.gmail.com> References: <CAJ-Vmon8%2BOXQ4g752zZEB-O0BR0sFWO0QUvw--xp2jsBDkx6tQ@mail.gmail.com> <0F6CC18F-6973-42A2-AC03-F01BF59458AE@lassitu.de> <CAJ-Vmo=Y8pp4iFnw%2B1hcPae6QXFboz=a7puwgC1kVSZ3JwMgPQ@mail.gmail.com> <1100F70E-9DA9-4163-AC9A-423ECE5AA9A3@lassitu.de> <CAJ-VmonrnJ7cC6u2LsL9AGusz_%2BkSwY62Rr1__sg5U_NynJ1SQ@mail.gmail.com> <CAJ-Vmo=WSN1oLM=B2HqSHrWyOaOD9BSwwu8=1Wys0CLRJ_N-TA@mail.gmail.com> <C637C171-A1A2-4296-84FA-6DE97137DC42@lassitu.de> <CAJ-Vmon2boy7OCh_4O0MeCi0yCdZu0OYb5dxHCEK=-%2B46zBGtg@mail.gmail.com> <CAJ-Vmoku5eLEYi5_DXVxK=0=4Ewn2aGepv3YUw4ApuVh_7y2%2Bw@mail.gmail.com> <CAJ-VmonvpnaS1rAO%2BsDRh1E5WfsrZTYE297Kc96prhfKjrM89Q@mail.gmail.com> <CAJ-VmokQxQs2DUKL=ONyxnnS7Q28ytmwZJ_thqvc4SvMkmS=cQ@mail.gmail.com> <18CABB46-9B9A-41CB-8742-6723C5FF4D67@lassitu.de> <C0BF20FD-E30F-4E9C-A0FE-500BE4807B99@bsdimp.com> <CAJ-VmokgiQCEG4et3X=3o_MuCMkO9MqkKqa-fjdpEqQNucn=Lw@mail.gmail.com> <2CBD8651-E132-49DC-A082-37A8F5C626EA@bsdimp.com> <AFE755D6-E462-40B4-A97B-9261303B3A4F@lassitu.de> <CAJ-Vm o=ERixAN96QSOaaej8avPHYkGC28AJZErdEhO0%2B=1R0Pw@mail.gmail.com> <09670C34-0D30-46BC-BA7E-4AAA22193B61@lassitu.de> <CAJ-VmokwSHN8U2=HJQT9kxFQ9oE-6H2h3KDb%2BMHN1Z8sur9=yw@mail.gmail.com>
next in thread | previous in thread | raw e-mail | index | archive | help
[-- Attachment #1 --] Am 22.12.2011 um 06:41 schrieb Adrian Chadd: > On 21 December 2011 07:13, Stefan Bethke <stb@lassitu.de> wrote: > >> I've replaced the generic PHY status poll with a custom one that uses the switch registers instead of the PHYs. I've cloned your git and have started adding my changes to it (branch also named work/ath): >> http://gitorious.org/~stb/freebsd/stb-adrianchadd-freebsd-work >> >> I tried a merge request, but gitorious didn't like it (at least at the top level). > > Cool! Can you please either figure out why that's the case, or just > throw me some diffs? It complains about the merge being too big to display in a browser? Anyway, here's two diffs. I believe you can track my tree by adding it to your local repository, and then merge it locally. That should be easier and less error prone than applying diffs. > We should figure out how to merge stuff back into your tree to mine > (and then propagate the changes back to you) so we don't have to face > this when the changes are bigger. I'm tracking your tree and merging changes into mine. One option could be to allow me write access to your repo and let me stuff my changes into a branch there. > After that's done, I'll work on you to tidy up the management utility. > Then we can start merging in whatever's needed from zrouter, and start > pushing this into -HEAD. I don't mind if it's a work in progress - at > least something will be in the tree. Cool! I've got a WRT160NL here with an RTL8306S which I could write a driver for. I started bringing FreeBSD up on that last night, but accidentally overwrote half my uboot. Waiting for the JTAG adapter to ship... Stefan -- Stefan Bethke <stb@lassitu.de> Fon +49 151 14070811 [-- Attachment #2 --] diff --git a/sys/dev/etherswitch/rtl8366rb.c b/sys/dev/etherswitch/rtl8366rb.c index f41a425..6f3e378 100644 --- a/sys/dev/etherswitch/rtl8366rb.c +++ b/sys/dev/etherswitch/rtl8366rb.c @@ -139,7 +139,7 @@ rtl8366rb_attach(device_t dev) sc = device_get_softc(dev); sc->dev = dev; - mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), "rtl8366rb", MTX_DEF); rtl8366rb_init(dev); smi_read(dev, RTL8366RB_CVCR, &rev); @@ -201,16 +201,60 @@ rtl8366rb_detach(device_t dev) } static void +rtl833rb_miipollstat(struct rtl8366rb_softc *sc) +{ + int i; + struct mii_data *mii; + struct mii_softc *child; + uint16_t value; + int portstatus; + + for (i = 0; i < RTL8366RB_NUM_PORTS-1; i++) { + mii = device_get_softc(sc->miibus[i]); + if ((i % 2) == 0) { + smi_read(sc->dev, RTL8366RB_PLSR_BASE + i/2, &value); + portstatus = value & 0xff; + } else { + portstatus = (value >> 8) & 0xff; + } + mii->mii_media_status = IFM_AVALID; + mii->mii_media_active = IFM_ETHER; + if ((portstatus & RTL8366RB_PLSR_LINK) != 0) { + switch (portstatus & RTL8366RB_PLSR_SPEED_MASK) { + case RTL8366RB_PLSR_SPEED_10: + mii->mii_media_active |= IFM_10_T; + break; + case RTL8366RB_PLSR_SPEED_100: + mii->mii_media_active |= IFM_100_TX; + break; + case RTL8366RB_PLSR_SPEED_1000: + mii->mii_media_active |= IFM_1000_T; + break; + } + if ((portstatus & RTL8366RB_PLSR_FULLDUPLEX) != 0) + mii->mii_media_active |= IFM_FDX; + else + mii->mii_media_active |= IFM_HDX; + if ((portstatus & RTL8366RB_PLSR_TXPAUSE) != 0) + mii->mii_media_active |= IFM_ETH_TXPAUSE; + if ((portstatus & RTL8366RB_PLSR_RXPAUSE) != 0) + mii->mii_media_active |= IFM_ETH_RXPAUSE; + } else + mii->mii_media_active |= IFM_NONE; + LIST_FOREACH(child, &mii->mii_phys, mii_list) { + if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) != child->mii_inst) + continue; + (void)PHY_SERVICE(child, mii, MII_POLLSTAT); + } + } +} + +static void rtl8366rb_tick(void *arg) { struct rtl8366rb_softc *sc = arg; - int i; - for (i=0; i < RTL8366RB_NUM_PORTS-1; i++) { - if (sc->miibus[i] != NULL) - mii_tick(device_get_softc(sc->miibus[i])); - mii_pollstat(device_get_softc(sc->miibus[i])); - } + rtl833rb_miipollstat(sc); callout_reset(&sc->callout_tick, hz, rtl8366rb_tick, sc); } @@ -435,10 +479,7 @@ rtl8366rb_ifmedia_upd(struct ifnet *ifp) { struct rtl8366rb_softc *sc = ifp->if_softc; struct mii_data *mii = device_get_softc(sc->miibus[ifp->if_dunit]); - struct mii_softc *miisc; - LIST_FOREACH(miisc, &mii->mii_phys, mii_list) - PHY_RESET(miisc); mii_mediachg(mii); return (0); } diff --git a/sys/dev/etherswitch/rtl8366rbvar.h b/sys/dev/etherswitch/rtl8366rbvar.h index f14373a..df17cb6 100644 --- a/sys/dev/etherswitch/rtl8366rbvar.h +++ b/sys/dev/etherswitch/rtl8366rbvar.h @@ -65,7 +65,8 @@ #define RTL8366RB_PLSR_SPEED_10 0x00 #define RTL8366RB_PLSR_SPEED_100 0x01 #define RTL8366RB_PLSR_SPEED_1000 0x02 -#define RTL8366RB_PLSR_FULLDUPLEX 0x10 +#define RTL8366RB_PLSR_FULLDUPLEX 0x08 +#define RTL8366RB_PLSR_LINK 0x10 #define RTL8366RB_PLSR_TXPAUSE 0x20 #define RTL8366RB_PLSR_RXPAUSE 0x40 #define RTL8366RB_PLSR_NO_AUTO 0x80 [-- Attachment #3 --] diff --git a/sys/dev/iicbus/iicbb.c b/sys/dev/iicbus/iicbb.c index fed203c..e060a75 100644 --- a/sys/dev/iicbus/iicbb.c +++ b/sys/dev/iicbus/iicbb.c @@ -25,7 +25,7 @@ */ #include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); +__FBSDID("$FreeBSD: head/sys/dev/iicbus/iicbb.c 188461 2009-02-10 22:50:23Z imp $"); /* * Generic I2C bit-banging code @@ -48,9 +48,9 @@ __FBSDID("$FreeBSD$"); #include <sys/systm.h> #include <sys/module.h> #include <sys/bus.h> +#include <sys/sysctl.h> #include <sys/uio.h> - #include <dev/iicbus/iiconf.h> #include <dev/iicbus/iicbus.h> @@ -59,9 +59,31 @@ __FBSDID("$FreeBSD$"); #include "iicbus_if.h" #include "iicbb_if.h" +#define IIC_DELAY_100KHZ 3 /* 3 microseconds per quarter cycle, should be 2.5 */ + +#if defined(DEBUG) +static int i2c_debug = 0; +#define I2C_DEBUG(x) \ + do { \ + if (i2c_debug) (x); \ + } while (0) + +#define I2C_LOG(args...) \ + do { \ + if (i2c_debug) printf(args); \ + } while (0) +static SYSCTL_NODE(_debug, OID_AUTO, iicbb, CTLFLAG_RD, 0, "iicbb"); +SYSCTL_INT(_debug_iicbb, OID_AUTO, debug, CTLFLAG_RW, &i2c_debug, 0, + "print bus transactions"); +#else +#define I2C_DEBUG(x) +#define I2C_LOG(args...) +#endif + struct iicbb_softc { device_t iicbus; - int udelay; /* signal toggle delay in usec */ + int iicdelay; + int iictimeout; }; static int iicbb_attach(device_t); @@ -126,11 +148,11 @@ iicbb_attach(device_t dev) if (!sc->iicbus) return (ENXIO); - sc->udelay = 10; /* 10 uS default */ + sc->udelay = IIC_DELAY_100KHZ; if (resource_int_value(device_get_name(dev), device_get_unit(dev), "udelay", &udelay) == 0) - sc->udelay = udelay; - device_printf(dev, "udelay=%d microseconds\n", sc->udelay); + sc->iicdelay = udelay; + device_printf(dev, "udelay=%d microseconds\n", sc->iicdelay); bus_generic_attach(dev); @@ -193,149 +215,191 @@ iicbb_print_child(device_t bus, device_t dev) return (retval); } -#define I2C_SETSDA(sc,dev,val) do { \ - IICBB_SETSDA(device_get_parent(dev), val); \ - DELAY(sc->udelay); \ - } while (0) - -#define I2C_SETSCL(dev,val) do { \ - iicbb_setscl(dev, val, 100); \ - } while (0) - -#define I2C_SET(sc,dev,ctrl,data) do { \ - I2C_SETSCL(dev, ctrl); \ - I2C_SETSDA(sc, dev, data); \ - } while (0) - -#define I2C_GETSDA(dev) (IICBB_GETSDA(device_get_parent(dev))) - -#define I2C_GETSCL(dev) (IICBB_GETSCL(device_get_parent(dev))) - -static int i2c_debug = 0; -#define I2C_DEBUG(x) do { \ - if (i2c_debug) (x); \ - } while (0) -#define I2C_LOG(format,args...) do { \ - printf(format, args); \ - } while (0) +/* + * Low-level bus state functions. The following functions implement the + * basic bus states for I2C. We divide the full clock cycle into four + * phases. Transitions of SCL and SDA occur between these phases. The + * nominal duration of each phase is 100 kHz/4, or 2.5 us. Implementations + * for busses and devices can choose a lower delay for faster operation. + * + * Each of the functions returns -1 if the operation could not be completed. + */ -static void -iicbb_setscl(device_t dev, int val, int timeout) +/* + * I2C start (S) and repeated start (Sr) condition + * 0 1 2 3 + * __.__ + * SCL XXXX \__. + * __ + * SDA XXXX \__.__. + * + */ +static __inline int +i2c_start(device_t dev) { - struct iicbb_softc *sc = device_get_softc(dev); - int k = 0; - - IICBB_SETSCL(device_get_parent(dev), val); - DELAY(sc->udelay); - - while (val && !I2C_GETSCL(dev) && k++ < timeout) { - IICBB_SETSCL(device_get_parent(dev), val); - DELAY(sc->udelay); + struct iicbb_softc *sc = (struct iicbb_softc *)device_get_softc(dev); + int timeout = sc->iictimeout; + + /* 0 */ + IICBB_SETSDA(device_get_parent(dev), 1); + IICBB_SETSCL(device_get_parent(dev), 1); + /* wait for the bus to become idle */ + while(1) { + DELAY(sc->iicdelay); + if (IICBB_GETSDA(device_get_parent(dev)) != 0 && + IICBB_GETSCL(device_get_parent(dev)) != 0) + break; + if (timeout <= 0) + return (-1); + timeout -= sc->iicdelay; } - - return; + /* 1 */ + DELAY(sc->iicdelay); + /* 2 */ + IICBB_SETSDA(device_get_parent(dev), 0); + DELAY(sc->iicdelay); + /* 3 */ + IICBB_SETSCL(device_get_parent(dev), 0); + DELAY(sc->iicdelay); + return (0); } -static void -iicbb_one(device_t dev, int timeout) +/* + * I2C stop (P) condition + * 0 1 2 3 + * __.__.__. + * SCL .__/ + * _____. + * SDA XXXX__/ + */ +static __inline int +i2c_stop(device_t dev) { - struct iicbb_softc *sc = device_get_softc(dev); + struct iicbb_softc *sc = (struct iicbb_softc *)device_get_softc(dev); - I2C_SET(sc,dev,0,1); - I2C_SET(sc,dev,1,1); - I2C_SET(sc,dev,0,1); - return; + /* 0 */ + IICBB_SETSDA(device_get_parent(dev), 0); + DELAY(sc->iicdelay); + /* 1 */ + IICBB_SETSCL(device_get_parent(dev), 1); + DELAY(sc->iicdelay); + /* 2 */ + IICBB_SETSDA(device_get_parent(dev), 1); + DELAY(sc->iicdelay); + /* + * we can skip the last delay since the bus will either be idle, + * or the next S will wait long enough to keep the timing correct. + */ + return (0); } -static void -iicbb_zero(device_t dev, int timeout) +/* + * I2C transmit one bit + * 0 1 2 3 + * __.__ + * SCL .__/ \__. + * __.__ + * SDA .XXX__.__XXX. + */ +static __inline int +i2c_xmitbit(device_t dev, int value) { - struct iicbb_softc *sc = device_get_softc(dev); - - I2C_SET(sc,dev,0,0); - I2C_SET(sc,dev,1,0); - I2C_SET(sc,dev,0,0); - return; + struct iicbb_softc *sc = (struct iicbb_softc *)device_get_softc(dev); + int timeout = sc->iictimeout; + + /* 0 */ + IICBB_SETSDA(device_get_parent(dev), value); + DELAY(sc->iicdelay); + /* 1 */ + IICBB_SETSCL(device_get_parent(dev), 1); + while (1) { + DELAY(sc->iicdelay); + if (IICBB_GETSCL(device_get_parent(dev)) != 0) + break; + if (timeout <= 0) + return (-1); + timeout -= sc->iicdelay; + } + /* 2 */ + if (value != 0 && IICBB_GETSDA(device_get_parent(dev)) == 0) + return (-1); /* another master is pulling down SDA */ + DELAY(sc->iicdelay); + /* 3 */ + IICBB_SETSCL(device_get_parent(dev), 0); + DELAY(sc->iicdelay); + return (0); } /* - * Waiting for ACKNOWLEDGE. - * - * When a chip is being addressed or has received data it will issue an - * ACKNOWLEDGE pulse. Therefore the MASTER must release the DATA line - * (set it to high level) and then release the CLOCK line. - * Now it must wait for the SLAVE to pull the DATA line low. - * Actually on the bus this looks like a START condition so nothing happens - * because of the fact that the IC's that have not been addressed are doing - * nothing. - * - * When the SLAVE has pulled this line low the MASTER will take the CLOCK - * line low and then the SLAVE will release the SDA (data) line. + * I2C receive one bit + * 0 1 2 3 + * __.__ + * SCL .__/ \__. + * __.__ + * SDA .XXX__.__XXX. */ -static int -iicbb_ack(device_t dev, int timeout) +static __inline int +i2c_recvbit(device_t dev) { - struct iicbb_softc *sc = device_get_softc(dev); - int noack; - int k = 0; - - I2C_SET(sc,dev,0,1); - I2C_SET(sc,dev,1,1); - do { - noack = I2C_GETSDA(dev); - if (!noack) + struct iicbb_softc *sc = (struct iicbb_softc *)device_get_softc(dev); + int timeout = sc->iictimeout; + int value; + + /* 0 */ + IICBB_SETSDA(device_get_parent(dev), 1); + DELAY(sc->iicdelay); + IICBB_SETSCL(device_get_parent(dev), 1); + while (1) { + DELAY(sc->iicdelay); + if (IICBB_GETSCL(device_get_parent(dev)) != 0) break; - DELAY(1); - k++; - } while (k < timeout); - - I2C_SET(sc,dev,0,1); - I2C_DEBUG(printf("%c ",noack?'-':'+')); - - return (noack); + if (timeout <= 0) + return (-1); + timeout -= sc->iicdelay; + } + value = IICBB_GETSDA(device_get_parent(dev)); + DELAY(sc->iicdelay); + IICBB_SETSCL(device_get_parent(dev), 0); + DELAY(sc->iicdelay); + return (value != 0 ? 1 : 0); } -static void -iicbb_sendbyte(device_t dev, u_char data, int timeout) +/* + * Transmit a byte. Returns -1 on error. + */ +static int +i2c_xmitbyte(device_t dev, int value) { - int i; - - for (i=7; i>=0; i--) { - if (data&(1<<i)) { - iicbb_one(dev, timeout); - } else { - iicbb_zero(dev, timeout); - } + int i, error; + + for (i = 7; i >= 0; i--) { + error = i2c_xmitbit(dev, (value >> i) & 0x01); + if (error < 0) + return (error); } - I2C_DEBUG(printf("w%02x",(int)data)); - return; + return (error); } -static u_char -iicbb_readbyte(device_t dev, int last, int timeout) +/* + * Receive a byte. Returns the byte value (0..255), or -1 on error. + */ +static int +i2c_recvbyte(device_t dev) { - struct iicbb_softc *sc = device_get_softc(dev); - int i; - unsigned char data=0; - - I2C_SET(sc,dev,0,1); - for (i=7; i>=0; i--) - { - I2C_SET(sc,dev,1,1); - if (I2C_GETSDA(dev)) - data |= (1<<i); - I2C_SET(sc,dev,0,1); - } - if (last) { - iicbb_one(dev, timeout); - } else { - iicbb_zero(dev, timeout); + int i, error, value; + + value = 0; + for (i = 7; i >= 0; i--) { + error = i2c_recvbit(dev); + if (error < 0) + return (error); + value |= error << i; } - I2C_DEBUG(printf("r%02x%c ",(int)data,last?'-':'+')); - return data; + return (value); } + static int iicbb_callback(device_t dev, int index, caddr_t data) { @@ -351,34 +415,32 @@ iicbb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) static int iicbb_start(device_t dev, u_char slave, int timeout) { - struct iicbb_softc *sc = device_get_softc(dev); int error; + struct iicbb_softc *sc = (struct iicbb_softc *)device_get_softc(dev); - I2C_DEBUG(printf("<")); - - I2C_SET(sc,dev,1,1); - I2C_SET(sc,dev,1,0); - I2C_SET(sc,dev,0,0); - - /* send address */ - iicbb_sendbyte(dev, slave, timeout); - - /* check for ack */ - if (iicbb_ack(dev, timeout)) { - error = IIC_ENOACK; - goto error; - } - - return(0); - -error: - iicbb_stop(dev); - return (error); + I2C_LOG("<"); + + sc->iictimeout = timeout; + if (i2c_start(dev) < 0) + return (IIC_ENOACK); + error = i2c_xmitbyte(dev, slave); + if (error < 0) + return (IIC_EBUSERR); /* lost arbitration */ + I2C_LOG("%02x", slave); + error = i2c_recvbit(dev); + if (error < 0) + return (IIC_EBUSERR); /* lost arbitration */ + I2C_LOG("%c", error != 0 ? '-' : '+'); + return(error != 0 ? IIC_ENOACK : 0); } static int iicbb_stop(device_t dev) { +<<<<<<< HEAD + i2c_stop(dev); + I2C_LOG(">\n"); +======= struct iicbb_softc *sc = device_get_softc(dev); I2C_SET(sc,dev,0,0); @@ -386,26 +448,30 @@ iicbb_stop(device_t dev) I2C_SET(sc,dev,1,1); I2C_DEBUG(printf(">")); I2C_DEBUG(printf("\n")); +>>>>>>> adrian/work/ath return (0); } static int iicbb_write(device_t dev, const char *buf, int len, int *sent, int timeout) { - int bytes, error = 0; + int bytes, ack, error = 0; + I2C_LOG(" W%d:", len); bytes = 0; while (len) { - /* send byte */ - iicbb_sendbyte(dev,(u_char)*buf++, timeout); - - /* check for ack */ - if (iicbb_ack(dev, timeout)) { + I2C_LOG(" %02x", (u_char)*buf); + i2c_xmitbyte(dev,(u_char)*buf++); + ack = i2c_recvbit(dev); + if (ack < 0) + return (IIC_EBUSERR); + I2C_LOG("%c", ack != 0 ? '-' : '+'); + if (len != 1 && ack != 0) { error = IIC_ENOACK; goto error; } - bytes ++; - len --; + bytes++; + len--; } error: @@ -416,19 +482,31 @@ error: static int iicbb_read(device_t dev, char * buf, int len, int *read, int last, int delay) { - int bytes; + int bytes, value, error = 0; + I2C_LOG(" R%d:", len); bytes = 0; while (len) { - /* XXX should insert delay here */ - *buf++ = (char)iicbb_readbyte(dev, (len == 1) ? last : 0, delay); - - bytes ++; - len --; + value = i2c_recvbyte(dev); + if (value < 0) { + error = IIC_EBUSERR; + goto error; + } + I2C_LOG(" %02x", value); + error = i2c_xmitbit(dev, len == 1 && last ? 1 : 0); + if (value < 0) { + error = IIC_EBUSERR; + goto error; + } + I2C_LOG("%c", len == 1 && last ? '-' : '+'); + *buf++ = value; + bytes++; + len--; } +error: *read = bytes; - return (0); + return (error); } DRIVER_MODULE(iicbus, iicbb, iicbus_driver, iicbus_devclass, 0, 0);
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?45529EC2-73BE-4F69-A9BE-E22D9FEAADD7>
