From owner-freebsd-embedded@FreeBSD.ORG Tue Dec 27 17:18:55 2011 Return-Path: Delivered-To: freebsd-embedded@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 0051F106566B; Tue, 27 Dec 2011 17:18:54 +0000 (UTC) (envelope-from stb@lassitu.de) Received: from gilb.zs64.net (gilb.zs64.net [IPv6:2001:470:1f0b:105e::1ea]) by mx1.freebsd.org (Postfix) with ESMTP id 34C488FC17; Tue, 27 Dec 2011 17:18:53 +0000 (UTC) Received: by gilb.zs64.net (Postfix, from stb@lassitu.de) id D4E3741A91; Tue, 27 Dec 2011 17:18:51 +0000 (UTC) Mime-Version: 1.0 (Apple Message framework v1251.1) Content-Type: multipart/mixed; boundary="Apple-Mail=_8BCC333A-BF48-41F3-90E3-140840423ED9" From: Stefan Bethke In-Reply-To: Date: Tue, 27 Dec 2011 18:18:51 +0100 Message-Id: <267FB3D6-830E-4A2F-8C1C-A96873EDCD31@lassitu.de> References: <0F6CC18F-6973-42A2-AC03-F01BF59458AE@lassitu.de> <1100F70E-9DA9-4163-AC9A-423ECE5AA9A3@lassitu.de> <18CABB46-9B9A-41CB-8742-6723C5FF4D67@lassitu.de> <2CBD8651-E132-49DC-A082-37A8F5C626EA@bsdimp.com> <09670C 34-0D30-46BC-BA7E-4AAA22193B61@lassitu.de> <45529EC2-73BE-4F69-A9BE-E22D9FEAADD7@lassitu.de> To: Adrian Chadd X-Mailer: Apple Mail (2.1251.1) Cc: Oleksandr Tymoshenko , "freebsd-embedded@freebsd.org" Subject: Re: Updated switch/glue patch? X-BeenThere: freebsd-embedded@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Dedicated and Embedded Systems List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 27 Dec 2011 17:18:55 -0000 --Apple-Mail=_8BCC333A-BF48-41F3-90E3-140840423ED9 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=us-ascii Am 27.12.2011 um 01:07 schrieb Stefan Bethke: > Am 26.12.2011 um 21:46 schrieb Adrian Chadd: >=20 >> Hi, >>=20 >> I've tested the two patches. >>=20 >> * the iicbb patch is fine at first glance, but I haven't yet sat down >> to figure out whether it's fine for older (and non-rtl8366rb) = devices. >> I'll commit it to my repo, so you can just pull it into yours. >> We should likely change it to default to 10uS to preserve behaviour >> and then override it in hints (hint.iicbb.0.udelay=3D3) for this >> particular board. >>=20 >> * the rtl8366rb change however applied and compiled, but it has >> completely broken things. I don't get any port status updates and >> etherswitchcfg doesn't actually return any configuration. Would you >> mind retesting all of that? >=20 > Here's a slightly updated patch (pushed to = https://gitorious.org/~stb/freebsd/stb-adrianchadd-freebsd-work/commits/wo= rk/ath): Another update: --Apple-Mail=_8BCC333A-BF48-41F3-90E3-140840423ED9 Content-Disposition: attachment; filename=rtl8366rb.patch Content-Type: application/octet-stream; name="rtl8366rb.patch" Content-Transfer-Encoding: 7bit diff --git a/sbin/etherswitchcfg/etherswitchcfg.8 b/sbin/etherswitchcfg/etherswitchcfg.8 index de43a26..538950c 100644 --- a/sbin/etherswitchcfg/etherswitchcfg.8 +++ b/sbin/etherswitchcfg/etherswitchcfg.8 @@ -11,10 +11,18 @@ .Ar info .Nm .Op Fl "f control file" +.Ar phy +.Ar phy.register[=value] +.Nm +.Op Fl "f control file" .Ar port%d .Ar command parameter .Nm .Op Fl "f control file" +.Ar reg +.Ar register[=value] +.Nm +.Op Fl "f control file" .Ar vlangroup%d .Ar command parameter .Sh DESCRIPTION @@ -23,27 +31,55 @@ The utility is used to configure an Ethernet switch built into the system. .Nm accepts a number of options: -.Bl -tag -width Fl "f control file" -compact +.Bl -tag -width ".Fl f" -compact .It Fl "f control file" Specifies the .Xr etherswitch 4 control file that represents the switch to be configured. +It defaults to +.Li /dev/etherswitch0 . +.It Fl m +When reporting port information, also list available media options for +that port. .It Fl v Produce more verbose output. Without this flag, lines that represent inactive or empty configuration options are omitted. .El +.Ss phy +The phy command provides access to the registers of the PHYs attached +to or integrated into the switch controller. +PHY registers are specified as phy.register, +where +.Ar phy +is usually the port number, and +.Ar register +is the register number. +Both can be provided as decimal, octal or hexadecimal numbers in any of the formats +understood by +.Xr strtol 4 . +To set the register value, use the form instance.register=value. .Ss port The port command selects one of the ports of the switch. It supports the following commands: -.Bl -tag -width Ar vlangroup number -compact +.Bl -tag -width ".Ar vlangroup number" -compact .It Ar vlangroup number Sets the VLAN group number that is used to process incoming frames that are not tagged. +.It Ar media mediaspec +Specifies the physical media configuration to be configured for a port. +.It Ar mediaopt mediaoption +Specifies a list of media options for a port. See +.Xr ifconfig 8 +for details on +.Ar media and +.Ar mediaopt . .El +.Ss reg +The reg command provides access to the registers of the switch controller. .Ss vlangroup The vlangroup command selects one of the VLAN groups for configuration. It supports the following commands: -.Bl -tag -width Ar vlangroup -compact +.Bl -tag -width ".Ar vlangroup" -compact .It Ar vlan VID Sets the VLAN ID (802.1q VID) for this VLAN group. Frames transmitted on tagged member ports of this group will be tagged diff --git a/sbin/etherswitchcfg/etherswitchcfg.c b/sbin/etherswitchcfg/etherswitchcfg.c index 9994a3d..e6129f3 100644 --- a/sbin/etherswitchcfg/etherswitchcfg.c +++ b/sbin/etherswitchcfg/etherswitchcfg.c @@ -59,7 +59,8 @@ enum cmdmode { MODE_NONE = 0, MODE_PORT, MODE_VLANGROUP, - MODE_REGISTER + MODE_REGISTER, + MODE_PHYREG }; struct cfg { @@ -105,6 +106,30 @@ write_register(struct cfg *cfg, int r, int v) err(EX_OSERR, "ioctl(IOETHERSWITCHSETREG)"); } +static int +read_phyregister(struct cfg *cfg, int phy, int reg) +{ + struct etherswitch_phyreg er; + + er.phy = phy; + er.reg = reg; + if (ioctl(cfg->fd, IOETHERSWITCHGETPHYREG, &er) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHGETPHYREG)"); + return (er.val); +} + +static void +write_phyregister(struct cfg *cfg, int phy, int reg, int val) +{ + struct etherswitch_phyreg er; + + er.phy = phy; + er.reg = reg; + er.val = val; + if (ioctl(cfg->fd, IOETHERSWITCHSETPHYREG, &er) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHSETPHYREG)"); +} + static void set_port_vlangroup(struct cfg *cfg, char *argv[]) { @@ -239,6 +264,29 @@ set_register(struct cfg *cfg, char *arg) return (0); } +static int +set_phyregister(struct cfg *cfg, char *arg) +{ + int phy, reg, val; + char *c, *d; + + phy = strtol(arg, &c, 0); + if (c==arg) + return (1); + if (*c != '.') + return (1); + d = c+1; + reg = strtol(d, &c, 0); + if (d == c) + return (1); + if (*c == '=') { + val = strtol(c+1, NULL, 0); + write_phyregister(cfg, phy, reg, val); + } + printf("\treg %d.0x%02x=0x%04x\n", phy, reg, read_phyregister(cfg, phy, reg)); + return (0); +} + static void print_port(struct cfg *cfg, int port) { @@ -351,6 +399,7 @@ newmode(struct cfg *cfg, enum cmdmode mode) print_vlangroup(cfg, cfg->unit); break; case MODE_REGISTER: + case MODE_PHYREG: break; } cfg->mode = mode; @@ -406,6 +455,8 @@ main(int argc, char *argv[]) if (cfg.unit < 0 || cfg.unit >= cfg.info.es_nvlangroups) errx(EX_USAGE, "port unit must be between 0 and %d", cfg.info.es_nvlangroups); newmode(&cfg, MODE_VLANGROUP); + } else if (strcmp(argv[0], "phy") == 0) { + newmode(&cfg, MODE_PHYREG); } else if (strcmp(argv[0], "reg") == 0) { newmode(&cfg, MODE_REGISTER); } else { @@ -434,6 +485,12 @@ main(int argc, char *argv[]) continue; } break; + case MODE_PHYREG: + if (set_phyregister(&cfg, argv[0]) != 0) { + newmode(&cfg, MODE_NONE); + continue; + } + break; } argc--; argv++; diff --git a/sys/dev/etherswitch/etherswitch.c b/sys/dev/etherswitch/etherswitch.c index 19b4630..9d1a865 100644 --- a/sys/dev/etherswitch/etherswitch.c +++ b/sys/dev/etherswitch/etherswitch.c @@ -202,6 +202,7 @@ etherswitchioctl(struct cdev *cdev, u_long cmd, caddr_t data, int flags, struct device_t etherswitch = device_get_parent(dev); etherswitch_info_t *info; etherswitch_reg_t *reg; + etherswitch_phyreg_t *phyreg; int error = 0; switch (cmd) { @@ -210,16 +211,16 @@ etherswitchioctl(struct cdev *cdev, u_long cmd, caddr_t data, int flags, struct bcopy(info, data, sizeof(etherswitch_info_t)); break; - case IOETHERSWITCHSETREG: - reg = (etherswitch_reg_t *)data; - error = ETHERSWITCH_WRITEREG(etherswitch, reg->reg, reg->val); - break; - case IOETHERSWITCHGETREG: reg = (etherswitch_reg_t *)data; reg->val = ETHERSWITCH_READREG(etherswitch, reg->reg); break; + case IOETHERSWITCHSETREG: + reg = (etherswitch_reg_t *)data; + error = ETHERSWITCH_WRITEREG(etherswitch, reg->reg, reg->val); + break; + case IOETHERSWITCHGETPORT: error = ETHERSWITCH_GETPORT(etherswitch, (etherswitch_port_t *)data); break; @@ -236,6 +237,16 @@ etherswitchioctl(struct cdev *cdev, u_long cmd, caddr_t data, int flags, struct error = ETHERSWITCH_SETVGROUP(etherswitch, (etherswitch_vlangroup_t *)data); break; + case IOETHERSWITCHGETPHYREG: + phyreg = (etherswitch_phyreg_t *)data; + phyreg->val = ETHERSWITCH_READPHYREG(etherswitch, phyreg->phy, phyreg->reg); + break; + + case IOETHERSWITCHSETPHYREG: + phyreg = (etherswitch_phyreg_t *)data; + error = ETHERSWITCH_WRITEPHYREG(etherswitch, phyreg->phy, phyreg->reg, phyreg->val); + break; + default: error = ENOTTY; } diff --git a/sys/dev/etherswitch/etherswitch.h b/sys/dev/etherswitch/etherswitch.h index df59c12..90ecb48 100644 --- a/sys/dev/etherswitch/etherswitch.h +++ b/sys/dev/etherswitch/etherswitch.h @@ -18,6 +18,13 @@ struct etherswitch_reg { }; typedef struct etherswitch_reg etherswitch_reg_t; +struct etherswitch_phyreg { + uint16_t phy; + uint16_t reg; + uint16_t val; +}; +typedef struct etherswitch_phyreg etherswitch_phyreg_t; + #define ETHERSWITCH_NAMEMAX 64 struct etherswitch_info { @@ -56,5 +63,7 @@ typedef struct etherswitch_vlangroup etherswitch_vlangroup_t; #define IOETHERSWITCHSETPORT _IOW('i', 5, etherswitch_port_t) #define IOETHERSWITCHGETVLANGROUP _IOWR('i', 6, etherswitch_vlangroup_t) #define IOETHERSWITCHSETVLANGROUP _IOW('i', 7, etherswitch_vlangroup_t) +#define IOETHERSWITCHGETPHYREG _IOWR('i', 8, etherswitch_phyreg_t) +#define IOETHERSWITCHSETPHYREG _IOW('i', 9, etherswitch_phyreg_t) #endif diff --git a/sys/dev/etherswitch/etherswitch_if.m b/sys/dev/etherswitch/etherswitch_if.m index d704ee3..7429c04 100644 --- a/sys/dev/etherswitch/etherswitch_if.m +++ b/sys/dev/etherswitch/etherswitch_if.m @@ -35,6 +35,25 @@ METHOD int writereg { }; # +# Read PHY register +# +METHOD int readphyreg { + device_t dev; + int phy; + int reg; +}; + +# +# Write PHY register +# +METHOD int writephyreg { + device_t dev; + int phy; + int reg; + int value; +}; + +# # Get port configuration # METHOD int getport { diff --git a/sys/dev/etherswitch/rtl8366rb.c b/sys/dev/etherswitch/rtl8366rb.c index 6f3e378..b9ca71e 100644 --- a/sys/dev/etherswitch/rtl8366rb.c +++ b/sys/dev/etherswitch/rtl8366rb.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -43,6 +44,7 @@ #include #include +#include #include #include #include @@ -51,16 +53,17 @@ #include #include +#include "iicbus_if.h" #include "miibus_if.h" #include "etherswitch_if.h" struct rtl8366rb_softc { - struct mtx sc_mtx; + struct mtx smi_mtx; device_t dev; - char *ifname[RTL8366RB_NUM_PORTS-1]; - device_t miibus[RTL8366RB_NUM_PORTS-1]; - struct ifnet *ifp[RTL8366RB_NUM_PORTS-1]; + char *ifname[RTL8366RB_NUM_PHYS]; + device_t miibus[RTL8366RB_NUM_PHYS]; + struct ifnet *ifp[RTL8366RB_NUM_PHYS]; device_t etherswitch; struct callout callout_tick; }; @@ -71,10 +74,29 @@ static etherswitch_info_t etherswitch_info = { .es_name = "Realtek RTL8366RB" }; +#if 1 || defined(DEBUG) +#define DPRINTF(dev, args...) device_printf(dev, args) +#define DEVERR(dev, err, fmt, args...) do { \ + if (err != 0) device_printf(dev, fmt, err, args); \ + } while (0) +#else +#define DPRINTF(dev, args...) +#define DEVERR(dev, err, fmt, args...) +#endif + +static int reset_count = 0; +static int delayed_select = 0; +static SYSCTL_NODE(_debug, OID_AUTO, rtl8366rb, CTLFLAG_RD, 0, "rtl8366rb"); +SYSCTL_INT(_debug_rtl8366rb, OID_AUTO, reset_count, CTLFLAG_RW, &reset_count, 0, + "number of times the bus had to be reset due to an error"); +SYSCTL_INT(_debug_rtl8366rb, OID_AUTO, delayed_select, CTLFLAG_RW, &delayed_select, 0, + "number of times we had to wait for the chip to become selectable"); static int smi_read(device_t dev, uint16_t addr, uint16_t *data); +static int smi_read_lockheld(device_t dev, uint16_t addr, uint16_t *data); static int smi_write(device_t dev, uint16_t addr, uint16_t data); static int smi_rmw(device_t dev, uint16_t addr, uint16_t mask, uint16_t data); +static void smi_reset(device_t dev); static void rtl8366rb_tick(void *arg); static int rtl8366rb_ifmedia_upd(struct ifnet *); static void rtl8366rb_ifmedia_sts(struct ifnet *, struct ifmediareq *); @@ -97,7 +119,8 @@ rtl8366rb_probe(device_t dev) { uint16_t chipid; - if (smi_read(dev, RTL8366RB_CIR, &chipid)) + smi_reset(dev); + if (smi_read_lockheld(dev, RTL8366RB_CIR, &chipid)) return (ENXIO); if (chipid != RTL8366RB_CIR_ID8366RB) return (ENXIO); @@ -109,6 +132,10 @@ static void rtl8366rb_init(device_t dev) { /* Initialisation for TL-WR1043ND */ + smi_rmw(dev, RTL8366RB_RCR, + RTL8366RB_RCR_HARD_RESET, + RTL8366RB_RCR_HARD_RESET); + DELAY(100000); /* Enable 16 VLAN mode */ smi_rmw(dev, RTL8366RB_SGCR, RTL8366RB_SGCR_EN_VLAN | RTL8366RB_SGCR_EN_VLAN_4KTB, @@ -139,8 +166,7 @@ rtl8366rb_attach(device_t dev) sc = device_get_softc(dev); sc->dev = dev; - mtx_init(&sc->sc_mtx, device_get_nameunit(dev), "rtl8366rb", - MTX_DEF); + mtx_init(&sc->smi_mtx, "smi", NULL, MTX_DEF); rtl8366rb_init(dev); smi_read(dev, RTL8366RB_CVCR, &rev); device_printf(dev, "rev. %d\n", rev & 0x000f); @@ -148,7 +174,7 @@ rtl8366rb_attach(device_t dev) /* attach miibus and phys */ /* PHYs need an interface, so we generate a dummy one */ /* The sixth port doesn't have a PHY */ - for (i = 0; i < RTL8366RB_NUM_PORTS-1; i++) { + for (i = 0; i < RTL8366RB_NUM_PHYS; i++) { sc->ifp[i] = if_alloc(IFT_ETHER); sc->ifp[i]->if_softc = sc; sc->ifp[i]->if_flags |= IFF_UP | IFF_BROADCAST | IFF_DRV_RUNNING @@ -165,13 +191,14 @@ rtl8366rb_attach(device_t dev) return (err); } } + device_printf(dev, "done attaching phys\n"); sc->etherswitch = device_add_child(dev, "etherswitch", -1); err = device_probe_and_attach(sc->etherswitch); if (err != 0) return (err); - callout_init_mtx(&sc->callout_tick, &sc->sc_mtx, 0); + callout_init(&sc->callout_tick, 1); rtl8366rb_tick(sc); return (err); @@ -186,7 +213,7 @@ rtl8366rb_detach(device_t dev) sc = device_get_softc(dev); if (sc->etherswitch) device_delete_child(dev, sc->etherswitch); - for (i=0; i < RTL8366RB_NUM_PORTS-1; i++) { + for (i=0; i < RTL8366RB_NUM_PHYS; i++) { if (sc->miibus[i]) device_delete_child(dev, sc->miibus[i]); if (sc->ifp[i] != NULL) @@ -195,11 +222,68 @@ rtl8366rb_detach(device_t dev) } bus_generic_detach(dev); callout_drain(&sc->callout_tick); - mtx_destroy(&sc->sc_mtx); + mtx_destroy(&sc->smi_mtx); return (0); } +static int +rtl8366rb_media_from_portstatus(int portstatus) +{ + int media; + + if ((portstatus & RTL8366RB_PLSR_LINK) == 0) + return IFM_ETHER | IFM_NONE; + media = IFM_ETHER; + switch (portstatus & RTL8366RB_PLSR_SPEED_MASK) { + case RTL8366RB_PLSR_SPEED_10: + media |= IFM_10_T; + break; + case RTL8366RB_PLSR_SPEED_100: + media |= IFM_100_TX; + break; + case RTL8366RB_PLSR_SPEED_1000: + media |= IFM_1000_T; + break; + } + if ((portstatus & RTL8366RB_PLSR_FULLDUPLEX) != 0) + media |= IFM_FDX; + else + media |= IFM_HDX; + if ((portstatus & RTL8366RB_PLSR_TXPAUSE) != 0) + media |= IFM_ETH_TXPAUSE; + if ((portstatus & RTL8366RB_PLSR_RXPAUSE) != 0) + media |= IFM_ETH_RXPAUSE; + return media; +} + +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_PHYS; 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 = rtl8366rb_media_from_portstatus(portstatus); + LIST_FOREACH(child, &mii->mii_phys, mii_list) { + if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) != child->mii_inst) + continue; + mii_phy_update(child, MII_POLLSTAT); + } + } +} + static void rtl833rb_miipollstat(struct rtl8366rb_softc *sc) { @@ -253,80 +337,161 @@ static void rtl8366rb_tick(void *arg) { struct rtl8366rb_softc *sc = arg; - + rtl833rb_miipollstat(sc); callout_reset(&sc->callout_tick, hz, rtl8366rb_tick, sc); } +static void +smi_reset(device_t dev) +{ + device_t iicbus, iicbb; + int i; + + DPRINTF(dev, "resetting bus\n"); + iicbus = device_get_parent(dev); + iicbb = device_get_parent(iicbus); + iicbus_reset(iicbus, IIC_FASTEST, RTL8366RB_IIC_ADDR, NULL); + for (i=3; i--; ) { + IICBUS_STOP(iicbb); + } + reset_count++; +} + static int -smi_read(device_t dev, uint16_t addr, uint16_t *data) +smi_read_lockheld(device_t dev, uint16_t addr, uint16_t *data) { int err; - device_t parent = device_get_parent(dev); + device_t iicbus = device_get_parent(dev); struct iicbus_ivar *devi = IICBUS_IVAR(dev); int slave = devi->addr; char bytes[2]; - int xferd; + int xferd, i; bytes[0] = addr & 0xff; bytes[1] = (addr >> 8) & 0xff; - err = iicbus_start(parent, slave | IICBUS_READ, IICBUS_TIMEOUT); + /* + * The chip does not use clock stretching when it is busy, + * instead ignoring the command. Retry a few times. + */ + for (i = RTL_IICBUS_RETRIES; i--; ) { + err = iicbus_start(iicbus, slave | RTL_IICBUS_READ, RTL_IICBUS_TIMEOUT); + if (err != IIC_ENOACK) + break; + DELAY(RTL_IICBUS_TIMEOUT); + delayed_select++; + } if (err != 0) goto out; - err = iicbus_write(parent, bytes, 2, &xferd, IICBUS_TIMEOUT); + err = iicbus_write(iicbus, bytes, 2, &xferd, RTL_IICBUS_TIMEOUT); if (err != 0) goto out; - err = iicbus_read(parent, bytes, 2, &xferd, IIC_LAST_READ, 0); + err = iicbus_read(iicbus, bytes, 2, &xferd, IIC_LAST_READ, 0); if (err != 0) goto out; *data = ((bytes[1] & 0xff) << 8) | (bytes[0] & 0xff); out: - iicbus_stop(parent); + iicbus_stop(iicbus); return err; } static int -smi_write(device_t dev, uint16_t addr, uint16_t data) +smi_write_lockheld(device_t dev, uint16_t addr, uint16_t data) { int err; - device_t parent = device_get_parent(dev); + device_t iicbus = device_get_parent(dev); struct iicbus_ivar *devi = IICBUS_IVAR(dev); int slave = devi->addr; char bytes[4]; - int xferd; + int xferd, i; bytes[0] = addr & 0xff; bytes[1] = (addr >> 8) & 0xff; bytes[2] = data & 0xff; bytes[3] = (data >> 8) & 0xff; - err = iicbus_request_bus(parent, dev, IIC_WAIT); - if (err == 0) { - err = iicbus_start(parent, slave | IICBUS_WRITE, IICBUS_TIMEOUT); - if (err == 0) - err = iicbus_write(parent, bytes, 4, &xferd, IICBUS_TIMEOUT); - iicbus_stop(parent); + /* + * The chip does not use clock stretching when it is busy, + * instead ignoring the command. Retry a few times. + */ + for (i = RTL_IICBUS_RETRIES; i--; ) { + err = iicbus_start(iicbus, slave | RTL_IICBUS_WRITE, RTL_IICBUS_TIMEOUT); + if (err != IIC_ENOACK) + break; + DELAY(RTL_IICBUS_TIMEOUT); + delayed_select++; } - iicbus_release_bus(parent, dev); + if (err == 0) + err = iicbus_write(iicbus, bytes, 4, &xferd, RTL_IICBUS_TIMEOUT); + iicbus_stop(iicbus); return err; } static int +smi_read(device_t dev, uint16_t addr, uint16_t *data) +{ + struct rtl8366rb_softc *sc = device_get_softc(dev); + device_t iicbus = device_get_parent(dev); + int err; + + mtx_lock(&sc->smi_mtx); + err = iicbus_request_bus(iicbus, dev, IIC_WAIT); + if (err == 0) + err = smi_read_lockheld(dev, addr, data); + iicbus_release_bus(iicbus, dev); + if (err != 0) + smi_reset(dev); + mtx_unlock(&sc->smi_mtx); + DEVERR(dev, err, "smi_read()=%d: addr=%04x\n", addr); + return (err == 0 ? 0 : EIO); +} + +static int +smi_write(device_t dev, uint16_t addr, uint16_t data) +{ + struct rtl8366rb_softc *sc = device_get_softc(dev); + device_t iicbus = device_get_parent(dev); + int err; + + mtx_lock(&sc->smi_mtx); + err = iicbus_request_bus(iicbus, dev, IIC_WAIT); + if (err == 0) + err = smi_write_lockheld(dev, addr, data); + iicbus_release_bus(iicbus, dev); + if (err != 0) + smi_reset(dev); + mtx_unlock(&sc->smi_mtx); + DEVERR(dev, err, "smi_write()=%d: addr=%04x\n", addr); + return (err == 0 ? 0 : EIO); +} + +static int smi_rmw(device_t dev, uint16_t addr, uint16_t mask, uint16_t data) { + struct rtl8366rb_softc *sc = device_get_softc(dev); + device_t iicbus = device_get_parent(dev); int err; uint16_t oldv, newv; - err = smi_read(dev, addr, &oldv); + mtx_lock(&sc->smi_mtx); + err = iicbus_request_bus(iicbus, dev, IIC_WAIT); if (err == 0) { - newv = oldv & ~mask; - newv |= data & mask; - if (newv != oldv) - err = smi_write(dev, addr, newv); + err = smi_read_lockheld(dev, addr, &oldv); + if (err == 0) { + newv = oldv & ~mask; + newv |= data & mask; + if (newv != oldv) + err = smi_write_lockheld(dev, addr, newv); + } } - return err; + iicbus_release_bus(iicbus, dev); + if (err != 0) + smi_reset(dev); + mtx_unlock(&sc->smi_mtx); + DEVERR(dev, err, "smi_rmw()=%d: addr=%04x\n", addr); + return (err == 0 ? 0 : EIO); } static etherswitch_info_t * @@ -365,7 +530,7 @@ rtl_getport(device_t dev, etherswitch_port_t *p) p->es_vlangroup = RTL8366RB_PVCR_GET(p->es_port, rtl_readreg(dev, RTL8366RB_PVCR_REG(p->es_port))); - if (p->es_port < RTL8366RB_NUM_PORTS-1) { + if (p->es_port < RTL8366RB_NUM_PHYS) { sc = device_get_softc(dev); mii = device_get_softc(sc->miibus[p->es_port]); ifm = &mii->mii_media; @@ -375,22 +540,9 @@ rtl_getport(device_t dev, etherswitch_port_t *p) } else { /* fill in fixed values for CPU port */ ifmr->ifm_count = 0; - smi_read(dev, RTL8366RB_PLSR_BASE + (RTL8366RB_NUM_PORTS-1)/2, &v); - v = v >> (8 * ((RTL8366RB_NUM_PORTS-1) % 2)); - switch (v & RTL8366RB_PLSR_SPEED_MASK) { - case RTL8366RB_PLSR_SPEED_10: - ifmr->ifm_current = IFM_ETHER | IFM_10_T; - break; - case RTL8366RB_PLSR_SPEED_100: - ifmr->ifm_current = IFM_ETHER | IFM_100_TX; - break; - case RTL8366RB_PLSR_SPEED_1000: - ifmr->ifm_current = IFM_ETHER | IFM_1000_T; - break; - } - if ((v & RTL8366RB_PLSR_FULLDUPLEX) != 0) - ifmr->ifm_current |= IFM_FDX; - ifmr->ifm_active = ifmr->ifm_current; + smi_read(dev, RTL8366RB_PLSR_BASE + (RTL8366RB_NUM_PHYS)/2, &v); + v = v >> (8 * ((RTL8366RB_NUM_PHYS) % 2)); + ifmr->ifm_active = ifmr->ifm_current = rtl8366rb_media_from_portstatus(v); ifmr->ifm_mask = 0; ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID; } @@ -412,7 +564,7 @@ rtl_setport(device_t dev, etherswitch_port_t *p) RTL8366RB_PVCR_VAL(p->es_port, p->es_vlangroup)); if (err) return (err); - if (p->es_port >= RTL8366RB_NUM_PORTS-1) + if (p->es_port >= RTL8366RB_NUM_PHYS) return (0); sc = device_get_softc(dev); mii = device_get_softc(sc->miibus[p->es_port]); @@ -455,23 +607,58 @@ rtl_setvgroup(device_t dev, etherswitch_vlangroup_t *vg) static int rtl_readphy(device_t dev, int phy, int reg) { + struct rtl8366rb_softc *sc = device_get_softc(dev); + device_t iicbus = device_get_parent(dev); uint16_t data = 0; - - smi_write(dev, RTL8366RB_PACR, RTL8366RB_PACR_READ); - smi_write(dev, RTL8366RB_PHYREG(phy, 0, reg), 0); - smi_read(dev, RTL8366RB_PADR, &data); + int err; + + if (phy < 0 || phy >= RTL8366RB_NUM_PHYS) + return (ENXIO); + if (reg < 0 || reg >= RTL8366RB_NUM_PHY_REG) + return (ENXIO); + mtx_lock(&sc->smi_mtx); + err = iicbus_request_bus(iicbus, dev, IIC_WAIT); + if (err == 0) + err = smi_write_lockheld(dev, RTL8366RB_PACR, RTL8366RB_PACR_READ); + if (err == 0) + err = smi_write_lockheld(dev, RTL8366RB_PHYREG(phy, 0, reg), 0); + if (err == 0) { + DELAY(RTL_IICBUS_PHYDELAY); /* chip needs time to talk to PHY */ + err = smi_read_lockheld(dev, RTL8366RB_PADR, &data); + } + iicbus_release_bus(iicbus, dev); + if (err != 0) + smi_reset(dev); + mtx_unlock(&sc->smi_mtx); + DEVERR(dev, err, "rtl_readphy()=%d: phy=%d.%02x\n", phy, reg); return (data); } static int rtl_writephy(device_t dev, int phy, int reg, int data) { + struct rtl8366rb_softc *sc = device_get_softc(dev); + device_t iicbus = device_get_parent(dev); int err; - err = smi_write(dev, RTL8366RB_PACR, RTL8366RB_PACR_WRITE); - if (err) - return (err); - return smi_write(dev, RTL8366RB_PHYREG(phy, 0, reg), data); + if (phy < 0 || phy >= RTL8366RB_NUM_PHYS) + return (ENXIO); + if (reg < 0 || reg >= RTL8366RB_NUM_PHY_REG) + return (ENXIO); + mtx_lock(&sc->smi_mtx); + err = iicbus_request_bus(iicbus, dev, IIC_WAIT); + if (err == 0) + err = smi_write_lockheld(dev, RTL8366RB_PACR, RTL8366RB_PACR_WRITE); + if (err == 0) + err = smi_write_lockheld(dev, RTL8366RB_PHYREG(phy, 0, reg), data); + if (err == 0) + DELAY(RTL_IICBUS_PHYDELAY); /* chip needs time to talk to PHY */ + iicbus_release_bus(iicbus, dev); + if (err != 0) + smi_reset(dev); + mtx_unlock(&sc->smi_mtx); + DEVERR(dev, err, "rtl_writephy()=%d: phy=%d.%02x\n", phy, reg); + return (err == 0 ? 0 : EIO); } static int @@ -511,6 +698,8 @@ static device_method_t rtl8366rb_methods[] = { DEVMETHOD(etherswitch_getinfo, rtl_getinfo), DEVMETHOD(etherswitch_readreg, rtl_readreg), DEVMETHOD(etherswitch_writereg, rtl_writereg), + DEVMETHOD(etherswitch_readphyreg, rtl_readphy), + DEVMETHOD(etherswitch_writephyreg, rtl_writephy), DEVMETHOD(etherswitch_getport, rtl_getport), DEVMETHOD(etherswitch_setport, rtl_setport), DEVMETHOD(etherswitch_getvgroup, rtl_getvgroup), diff --git a/sys/dev/etherswitch/rtl8366rbvar.h b/sys/dev/etherswitch/rtl8366rbvar.h index df17cb6..354ca95 100644 --- a/sys/dev/etherswitch/rtl8366rbvar.h +++ b/sys/dev/etherswitch/rtl8366rbvar.h @@ -28,9 +28,12 @@ #define RTL8366RB_IIC_ADDR 0xa8 -#define IICBUS_TIMEOUT 100 /* us */ -#define IICBUS_READ 1 -#define IICBUS_WRITE 0 +#define RTL_IICBUS_TIMEOUT 100 /* us */ +#define RTL_IICBUS_READ 1 +#define RTL_IICBUS_WRITE 0 +/* number of times to try and select the chip on the I2C bus */ +#define RTL_IICBUS_RETRIES 3 +#define RTL_IICBUS_PHYDELAY 500 /* us */ /* Register definitions */ @@ -43,8 +46,9 @@ #define RTL8366RB_SGCR_MAX_LENGTH_1536 0x0010 #define RTL8366RB_SGCR_MAX_LENGTH_1552 0x0020 #define RTL8366RB_SGCR_MAX_LENGTH_9216 0x0030 -#define RTL8366RB_SGCR_EN_VLAN 0x2000 -#define RTL8366RB_SGCR_EN_VLAN_4KTB 0x4000 +#define RTL8366RB_SGCR_EN_VLAN 0x2000 +#define RTL8366RB_SGCR_EN_VLAN_4KTB 0x4000 +#define RTL8366RB_SGCR_EN_QOS 0x8000 /* Port Enable Control: DISABLE_PORT[5:0] */ #define RTL8366RB_PECR 0x0001 @@ -73,20 +77,20 @@ /* VLAN Member Configuration, 3 registers per VLAN */ #define RTL8366RB_VMCR_BASE 0x0020 -#define RTL8366RB_VMCR_MULT 3 -#define RTL8366RB_VMCR_DOT1Q_REG 0 +#define RTL8366RB_VMCR_MULT 3 +#define RTL8366RB_VMCR_DOT1Q_REG 0 #define RTL8366RB_VMCR_DOT1Q_VID_SHIFT 0 #define RTL8366RB_VMCR_DOT1Q_VID_MASK 0x0fff #define RTL8366RB_VMCR_DOT1Q_PCP_SHIFT 12 #define RTL8366RB_VMCR_DOT1Q_PCP_MASK 0x7000 -#define RTL8366RB_VMCR_MU_REG 1 +#define RTL8366RB_VMCR_MU_REG 1 #define RTL8366RB_VMCR_MU_MEMBER_SHIFT 0 #define RTL8366RB_VMCR_MU_MEMBER_MASK 0x00ff #define RTL8366RB_VMCR_MU_UNTAG_SHIFT 8 #define RTL8366RB_VMCR_MU_UNTAG_MASK 0xff00 -#define RTL8366RB_VMCR_FID_REG 2 +#define RTL8366RB_VMCR_FID_REG 2 #define RTL8366RB_VMCR_FID_FID_SHIFT 0 -#define RTL8366RB_VMCR_FID_FID_MASK 0x0007 +#define RTL8366RB_VMCR_FID_FID_MASK 0x0007 #define RTL8366RB_VMCR(_reg, _vlan) \ (RTL8366RB_VMCR_BASE + _reg + _vlan * RTL8366RB_VMCR_MULT) /* VLAN Identifier */ @@ -94,13 +98,15 @@ (_r[RTL8366RB_VMCR_DOT1Q_REG] & RTL8366RB_VMCR_DOT1Q_VID_MASK) /* Priority Code Point */ #define RTL8366RB_VMCR_PCP(_r) \ - ((_r[RTL8366RB_VMCR_DOT1Q_REG] & RTL8366RB_VMCR_DOT1Q_PCP_MASK) >> RTL8366RB_VMCR_DOT1Q_PCP_SHIFT) + ((_r[RTL8366RB_VMCR_DOT1Q_REG] & RTL8366RB_VMCR_DOT1Q_PCP_MASK) \ + >> RTL8366RB_VMCR_DOT1Q_PCP_SHIFT) /* Member ports */ #define RTL8366RB_VMCR_MEMBER(_r) \ (_r[RTL8366RB_VMCR_MU_REG] & RTL8366RB_VMCR_MU_MEMBER_MASK) /* Untagged ports */ #define RTL8366RB_VMCR_UNTAG(_r) \ - ((_r[RTL8366RB_VMCR_MU_REG] & RTL8366RB_VMCR_MU_UNTAG_MASK) >> RTL8366RB_VMCR_MU_UNTAG_SHIFT) + ((_r[RTL8366RB_VMCR_MU_REG] & RTL8366RB_VMCR_MU_UNTAG_MASK) \ + >> RTL8366RB_VMCR_MU_UNTAG_SHIFT) /* Forwarding ID */ #define RTL8366RB_VMCR_FID(_r) \ (_r[RTL8366RB_VMCR_FID_REG] & RTL8366RB_VMCR_FID_FID_MASK) @@ -136,6 +142,19 @@ /* VLAN Ingress Control 2: [5:0] */ #define RTL8366RB_VIC2R 0x037f +/* MIB registers */ +#define RTL8366RB_MCNT_BASE 0x1000 +#define RTL8366RB_MCTLR 0x13f0 +#define RTL8366RB_MCTLR_BUSY 0x0001 +#define RTL8366RB_MCTLR_RESET 0x0002 +#define RTL8366RB_MCTLR_RESET_PORT_MASK 0x00fc +#define RTL8366RB_MCTLR_RESET_ALL 0x0800 + +#define RTL8366RB_MCNT(_port, _r) \ + (RTL8366RB_MCNT_BASE + 0x50 * (_port) + (_r)) +#define RTL8366RB_MCTLR_RESET_PORT(_p) \ + (1 << ((_p) + 2)) + /* PHY Access Control */ #define RTL8366RB_PACR 0x8000 #define RTL8366RB_PACR_WRITE 0x0000 @@ -149,5 +168,6 @@ /* general characteristics of the chip */ #define RTL8366RB_NUM_PORTS 6 +#define RTL8366RB_NUM_PHYS (RTL8366RB_NUM_PORTS-1) #define RTL8366RB_NUM_VLANS 16 - +#define RTL8366RB_NUM_PHY_REG 32 diff --git a/sys/dev/flash/mx25l.c b/sys/dev/flash/mx25l.c index d5c2270..3ac1d02 100644 --- a/sys/dev/flash/mx25l.c +++ b/sys/dev/flash/mx25l.c @@ -102,6 +102,7 @@ struct mx25l_flash_ident flash_devices[] = { { "mx25ll128", 0xc2, 0x2018, 64 * 1024, 256, FL_ERASE_4K | FL_ERASE_32K }, { "s25fl128", 0x01, 0x2018, 64 * 1024, 256, FL_NONE }, { "s25sl064a", 0x01, 0x0216, 64 * 1024, 128, FL_NONE }, + { "w25q64bv", 0xef, 0x4017, 64 * 1024, 128, FL_ERASE_4K }, }; static uint8_t diff --git a/sys/dev/gpio/gpiobus.c b/sys/dev/gpio/gpiobus.c index 09ebe4e..c6fbaae 100644 --- a/sys/dev/gpio/gpiobus.c +++ b/sys/dev/gpio/gpiobus.c @@ -349,7 +349,9 @@ gpiobus_acquire_bus(device_t busdev, device_t child) GPIOBUS_ASSERT_LOCKED(sc); if (sc->sc_owner) - panic("rb_cpldbus: cannot serialize the access to device."); + panic("%s: acquire_bus(%s): already acquired by %s", + device_get_nameunit(busdev), device_get_nameunit(busdev), + device_get_nameunit(child)); sc->sc_owner = child; } @@ -362,9 +364,12 @@ gpiobus_release_bus(device_t busdev, device_t child) GPIOBUS_ASSERT_LOCKED(sc); if (!sc->sc_owner) - panic("rb_cpldbus: releasing unowned bus."); + panic("%s: release_bus(%s): not owned", + device_get_nameunit(busdev), device_get_nameunit(busdev)); if (sc->sc_owner != child) - panic("rb_cpldbus: you don't own the bus. game over."); + panic("%s: release_bus(%s): acquired by %s", + device_get_nameunit(busdev), device_get_nameunit(busdev), + device_get_nameunit(child)); sc->sc_owner = NULL; } diff --git a/sys/dev/iicbus/iicbb.c b/sys/dev/iicbus/iicbb.c index fed203c..a0e6255 100644 --- a/sys/dev/iicbus/iicbb.c +++ b/sys/dev/iicbus/iicbb.c @@ -276,24 +276,31 @@ iicbb_zero(device_t dev, int timeout) static int iicbb_ack(device_t dev, int timeout) { - 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); + /* 1 */ + IICBB_SETSCL(device_get_parent(dev), 1); + while (1) { + DELAY(sc->iicdelay); + /* allow clock stretching */ + 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; + } + /* 2 */ + value = IICBB_GETSDA(device_get_parent(dev)); + DELAY(sc->iicdelay); + /* 3 */ + IICBB_SETSCL(device_get_parent(dev), 0); + DELAY(sc->iicdelay); + return (value != 0 ? 1 : 0); } static void @@ -346,6 +353,7 @@ static int iicbb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) { return (IICBB_RESET(device_get_parent(dev), speed, addr, oldaddr)); + i2c_stop(dev); } static int @@ -354,38 +362,31 @@ iicbb_start(device_t dev, u_char slave, int timeout) struct iicbb_softc *sc = device_get_softc(dev); int error; - 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; + 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 ? '-' : '+'); + if (error != 0) { + iicbb_stop(dev); + return (IIC_ENOACK); } - - return(0); - -error: - iicbb_stop(dev); - return (error); + return (0); } static int iicbb_stop(device_t dev) { - struct iicbb_softc *sc = device_get_softc(dev); - - I2C_SET(sc,dev,0,0); - I2C_SET(sc,dev,1,0); - I2C_SET(sc,dev,1,1); - I2C_DEBUG(printf(">")); - I2C_DEBUG(printf("\n")); + i2c_stop(dev); + I2C_LOG(">\n"); return (0); } diff --git a/sys/modules/rtl8366rb/Makefile b/sys/modules/rtl8366rb/Makefile index 41cacd3..8d9800f 100644 --- a/sys/modules/rtl8366rb/Makefile +++ b/sys/modules/rtl8366rb/Makefile @@ -5,6 +5,6 @@ CFLAGS+= -DDEBUG .PATH: ${.CURDIR}/../../dev/etherswitch KMOD= rtl8366rb SRCS= rtl8366rb.c rtl8366rbvar.h -SRCS+= device_if.h bus_if.h miibus_if.h etherswitch_if.h opt_cputype.h +SRCS+= device_if.h bus_if.h iicbus_if.h miibus_if.h etherswitch_if.h opt_cputype.h .include --Apple-Mail=_8BCC333A-BF48-41F3-90E3-140840423ED9 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset=us-ascii Make code more robust. o Add ioctls to read and write PHY registers. o properly stop I2C transaction when selection fails, or the transaction fails halfway through. With debugging enabled, debug.rtl8366rb.reset_count counts the number of times the bus has been reset after an error. o Add a DELAY() to reading and writing PHY registers to give the chip time to talk to the PHY. Without this DELAY, the chip may be unresponsive. o Add debugging code to print info on failed I2C transactions. o When selecting the chip fails, retry a number of times. With debugging enabled, debug.rtl8366rb.delayed_select counts the number of times this has happened. o Don't call PHY_SERVICE() (because that runs the phy service code that queries the PHY for the currently selected media); call mii_phy_update() -- Stefan Bethke Fon +49 151 14070811 --Apple-Mail=_8BCC333A-BF48-41F3-90E3-140840423ED9--