rtl8366rb.c revision 235288
1235288Sadrian/*- 2235288Sadrian * Copyright (c) 2011-2012 Stefan Bethke. 3235288Sadrian * All rights reserved. 4235288Sadrian * 5235288Sadrian * Redistribution and use in source and binary forms, with or without 6235288Sadrian * modification, are permitted provided that the following conditions 7235288Sadrian * are met: 8235288Sadrian * 1. Redistributions of source code must retain the above copyright 9235288Sadrian * notice, this list of conditions and the following disclaimer. 10235288Sadrian * 2. Redistributions in binary form must reproduce the above copyright 11235288Sadrian * notice, this list of conditions and the following disclaimer in the 12235288Sadrian * documentation and/or other materials provided with the distribution. 13235288Sadrian * 14235288Sadrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15235288Sadrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16235288Sadrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17235288Sadrian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18235288Sadrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19235288Sadrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20235288Sadrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21235288Sadrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22235288Sadrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23235288Sadrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24235288Sadrian * SUCH DAMAGE. 25235288Sadrian * 26235288Sadrian * $FreeBSD: head/sys/dev/etherswitch/rtl8366/rtl8366rb.c 235288 2012-05-11 20:53:20Z adrian $ 27235288Sadrian */ 28235288Sadrian 29235288Sadrian#include <sys/param.h> 30235288Sadrian#include <sys/bus.h> 31235288Sadrian#include <sys/errno.h> 32235288Sadrian#include <sys/kernel.h> 33235288Sadrian#include <sys/module.h> 34235288Sadrian#include <sys/socket.h> 35235288Sadrian#include <sys/sockio.h> 36235288Sadrian#include <sys/sysctl.h> 37235288Sadrian#include <sys/systm.h> 38235288Sadrian 39235288Sadrian#include <net/if.h> 40235288Sadrian#include <net/if_arp.h> 41235288Sadrian#include <net/ethernet.h> 42235288Sadrian#include <net/if_dl.h> 43235288Sadrian#include <net/if_media.h> 44235288Sadrian#include <net/if_types.h> 45235288Sadrian 46235288Sadrian#include <machine/bus.h> 47235288Sadrian#include <dev/iicbus/iic.h> 48235288Sadrian#include <dev/iicbus/iiconf.h> 49235288Sadrian#include <dev/iicbus/iicbus.h> 50235288Sadrian#include <dev/mii/mii.h> 51235288Sadrian#include <dev/mii/miivar.h> 52235288Sadrian 53235288Sadrian#include <dev/etherswitch/etherswitch.h> 54235288Sadrian#include <dev/etherswitch/rtl8366/rtl8366rbvar.h> 55235288Sadrian 56235288Sadrian#include "iicbus_if.h" 57235288Sadrian#include "miibus_if.h" 58235288Sadrian#include "etherswitch_if.h" 59235288Sadrian 60235288Sadrian 61235288Sadrianstruct rtl8366rb_softc { 62235288Sadrian struct mtx sc_mtx; /* serialize access to softc */ 63235288Sadrian int smi_acquired; /* serialize access to SMI/I2C bus */ 64235288Sadrian struct mtx callout_mtx; /* serialize callout */ 65235288Sadrian device_t dev; 66235288Sadrian char *ifname[RTL8366RB_NUM_PHYS]; 67235288Sadrian device_t miibus[RTL8366RB_NUM_PHYS]; 68235288Sadrian struct ifnet *ifp[RTL8366RB_NUM_PHYS]; 69235288Sadrian struct callout callout_tick; 70235288Sadrian}; 71235288Sadrian 72235288Sadrianstatic etherswitch_info_t etherswitch_info = { 73235288Sadrian .es_nports = 6, 74235288Sadrian .es_nvlangroups = 16, 75235288Sadrian .es_name = "Realtek RTL8366RB" 76235288Sadrian}; 77235288Sadrian 78235288Sadrian#define RTL_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) 79235288Sadrian#define RTL_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) 80235288Sadrian#define RTL_LOCK_ASSERT(_sc, _what) mtx_assert(&(_s)c->sc_mtx, (_what)) 81235288Sadrian#define RTL_TRYLOCK(_sc) mtx_trylock(&(_sc)->sc_mtx) 82235288Sadrian 83235288Sadrian#define RTL_WAITOK 0 84235288Sadrian#define RTL_NOWAIT 1 85235288Sadrian 86235288Sadrian#define RTL_SMI_ACQUIRED 1 87235288Sadrian#define RTL_SMI_ACQUIRED_ASSERT(_sc) \ 88235288Sadrian KASSERT((_sc)->smi_acquired == RTL_SMI_ACQUIRED, ("smi must be acquired @%s", __FUNCTION__)) 89235288Sadrian 90235288Sadrian#if defined(DEBUG) 91235288Sadrian#define DPRINTF(dev, args...) device_printf(dev, args) 92235288Sadrian#define DEVERR(dev, err, fmt, args...) do { \ 93235288Sadrian if (err != 0) device_printf(dev, fmt, err, args); \ 94235288Sadrian } while (0) 95235288Sadrian#define DEBUG_INCRVAR(var) do { \ 96235288Sadrian var++; \ 97235288Sadrian } while (0) 98235288Sadrian 99235288Sadrianstatic int callout_blocked = 0; 100235288Sadrianstatic int iic_select_retries = 0; 101235288Sadrianstatic int phy_access_retries = 0; 102235288Sadrianstatic SYSCTL_NODE(_debug, OID_AUTO, rtl8366rb, CTLFLAG_RD, 0, "rtl8366rb"); 103235288SadrianSYSCTL_INT(_debug_rtl8366rb, OID_AUTO, callout_blocked, CTLFLAG_RW, &callout_blocked, 0, 104235288Sadrian "number of times the callout couldn't acquire the bus"); 105235288SadrianSYSCTL_INT(_debug_rtl8366rb, OID_AUTO, iic_select_retries, CTLFLAG_RW, &iic_select_retries, 0, 106235288Sadrian "number of times the I2C bus selection had to be retried"); 107235288SadrianSYSCTL_INT(_debug_rtl8366rb, OID_AUTO, phy_access_retries, CTLFLAG_RW, &phy_access_retries, 0, 108235288Sadrian "number of times PHY register access had to be retried"); 109235288Sadrian#else 110235288Sadrian#define DPRINTF(dev, args...) 111235288Sadrian#define DEVERR(dev, err, fmt, args...) 112235288Sadrian#define DEBUG_INCRVAR(var) 113235288Sadrian#endif 114235288Sadrian 115235288Sadrianstatic int smi_probe(device_t dev); 116235288Sadrianstatic int smi_read(device_t dev, uint16_t addr, uint16_t *data, int sleep); 117235288Sadrianstatic int smi_write(device_t dev, uint16_t addr, uint16_t data, int sleep); 118235288Sadrianstatic int smi_rmw(device_t dev, uint16_t addr, uint16_t mask, uint16_t data, int sleep); 119235288Sadrianstatic void rtl8366rb_tick(void *arg); 120235288Sadrianstatic int rtl8366rb_ifmedia_upd(struct ifnet *); 121235288Sadrianstatic void rtl8366rb_ifmedia_sts(struct ifnet *, struct ifmediareq *); 122235288Sadrian 123235288Sadrianstatic void 124235288Sadrianrtl8366rb_identify(driver_t *driver, device_t parent) 125235288Sadrian{ 126235288Sadrian device_t child; 127235288Sadrian struct iicbus_ivar *devi; 128235288Sadrian 129235288Sadrian if (device_find_child(parent, "rtl8366rb", -1) == NULL) { 130235288Sadrian child = BUS_ADD_CHILD(parent, 0, "rtl8366rb", -1); 131235288Sadrian devi = IICBUS_IVAR(child); 132235288Sadrian devi->addr = RTL8366RB_IIC_ADDR; 133235288Sadrian } 134235288Sadrian} 135235288Sadrian 136235288Sadrianstatic int 137235288Sadrianrtl8366rb_probe(device_t dev) 138235288Sadrian{ 139235288Sadrian if (smi_probe(dev) != 0) 140235288Sadrian return (ENXIO); 141235288Sadrian device_set_desc(dev, "RTL8366RB Ethernet Switch Controller"); 142235288Sadrian return (BUS_PROBE_DEFAULT); 143235288Sadrian} 144235288Sadrian 145235288Sadrianstatic void 146235288Sadrianrtl8366rb_init(device_t dev) 147235288Sadrian{ 148235288Sadrian /* Initialisation for TL-WR1043ND */ 149235288Sadrian smi_rmw(dev, RTL8366RB_RCR, 150235288Sadrian RTL8366RB_RCR_HARD_RESET, 151235288Sadrian RTL8366RB_RCR_HARD_RESET, RTL_WAITOK); 152235288Sadrian DELAY(100000); 153235288Sadrian /* Enable 16 VLAN mode */ 154235288Sadrian smi_rmw(dev, RTL8366RB_SGCR, 155235288Sadrian RTL8366RB_SGCR_EN_VLAN | RTL8366RB_SGCR_EN_VLAN_4KTB, 156235288Sadrian RTL8366RB_SGCR_EN_VLAN, RTL_WAITOK); 157235288Sadrian /* remove port 0 form VLAN 0 */ 158235288Sadrian smi_rmw(dev, RTL8366RB_VMCR(RTL8366RB_VMCR_MU_REG, 0), 159235288Sadrian (1 << 0), 0, RTL_WAITOK); 160235288Sadrian /* add port 0 untagged and port 5 tagged to VLAN 1 */ 161235288Sadrian smi_rmw(dev, RTL8366RB_VMCR(RTL8366RB_VMCR_MU_REG, 1), 162235288Sadrian ((1 << 5 | 1 << 0) << RTL8366RB_VMCR_MU_MEMBER_SHIFT) 163235288Sadrian | ((1 << 5 | 1 << 0) << RTL8366RB_VMCR_MU_UNTAG_SHIFT), 164235288Sadrian ((1 << 5 | 1 << 0) << RTL8366RB_VMCR_MU_MEMBER_SHIFT 165235288Sadrian | ((1 << 0) << RTL8366RB_VMCR_MU_UNTAG_SHIFT)), 166235288Sadrian RTL_WAITOK); 167235288Sadrian /* set PVLAN 1 for port 0 */ 168235288Sadrian smi_rmw(dev, RTL8366RB_PVCR_REG(0), 169235288Sadrian RTL8366RB_PVCR_VAL(0, RTL8366RB_PVCR_PORT_MASK), 170235288Sadrian RTL8366RB_PVCR_VAL(0, 1), RTL_WAITOK); 171235288Sadrian} 172235288Sadrian 173235288Sadrianstatic int 174235288Sadrianrtl8366rb_attach(device_t dev) 175235288Sadrian{ 176235288Sadrian uint16_t rev = 0; 177235288Sadrian struct rtl8366rb_softc *sc; 178235288Sadrian char name[IFNAMSIZ]; 179235288Sadrian int err = 0; 180235288Sadrian int i; 181235288Sadrian 182235288Sadrian sc = device_get_softc(dev); 183235288Sadrian bzero(sc, sizeof(*sc)); 184235288Sadrian sc->dev = dev; 185235288Sadrian mtx_init(&sc->sc_mtx, "rtl8366rb", NULL, MTX_DEF); 186235288Sadrian sc->smi_acquired = 0; 187235288Sadrian mtx_init(&sc->callout_mtx, "rtl8366rbcallout", NULL, MTX_DEF); 188235288Sadrian 189235288Sadrian rtl8366rb_init(dev); 190235288Sadrian smi_read(dev, RTL8366RB_CVCR, &rev, RTL_WAITOK); 191235288Sadrian device_printf(dev, "rev. %d\n", rev & 0x000f); 192235288Sadrian 193235288Sadrian /* attach miibus and phys */ 194235288Sadrian /* PHYs need an interface, so we generate a dummy one */ 195235288Sadrian for (i = 0; i < RTL8366RB_NUM_PHYS; i++) { 196235288Sadrian sc->ifp[i] = if_alloc(IFT_ETHER); 197235288Sadrian sc->ifp[i]->if_softc = sc; 198235288Sadrian sc->ifp[i]->if_flags |= IFF_UP | IFF_BROADCAST | IFF_DRV_RUNNING 199235288Sadrian | IFF_SIMPLEX; 200235288Sadrian snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(dev)); 201235288Sadrian sc->ifname[i] = malloc(strlen(name)+1, M_DEVBUF, M_WAITOK); 202235288Sadrian bcopy(name, sc->ifname[i], strlen(name)+1); 203235288Sadrian if_initname(sc->ifp[i], sc->ifname[i], i); 204235288Sadrian err = mii_attach(dev, &sc->miibus[i], sc->ifp[i], rtl8366rb_ifmedia_upd, \ 205235288Sadrian rtl8366rb_ifmedia_sts, BMSR_DEFCAPMASK, \ 206235288Sadrian i, MII_OFFSET_ANY, 0); 207235288Sadrian if (err != 0) { 208235288Sadrian device_printf(dev, "attaching PHY %d failed\n", i); 209235288Sadrian return (err); 210235288Sadrian } 211235288Sadrian } 212235288Sadrian 213235288Sadrian bus_generic_probe(dev); 214235288Sadrian bus_enumerate_hinted_children(dev); 215235288Sadrian err = bus_generic_attach(dev); 216235288Sadrian if (err != 0) 217235288Sadrian return (err); 218235288Sadrian 219235288Sadrian callout_init_mtx(&sc->callout_tick, &sc->callout_mtx, 0); 220235288Sadrian rtl8366rb_tick(sc); 221235288Sadrian 222235288Sadrian return (err); 223235288Sadrian} 224235288Sadrian 225235288Sadrianstatic int 226235288Sadrianrtl8366rb_detach(device_t dev) 227235288Sadrian{ 228235288Sadrian struct rtl8366rb_softc *sc = device_get_softc(dev); 229235288Sadrian int i; 230235288Sadrian 231235288Sadrian for (i=0; i < RTL8366RB_NUM_PHYS; i++) { 232235288Sadrian if (sc->miibus[i]) 233235288Sadrian device_delete_child(dev, sc->miibus[i]); 234235288Sadrian if (sc->ifp[i] != NULL) 235235288Sadrian if_free(sc->ifp[i]); 236235288Sadrian free(sc->ifname[i], M_DEVBUF); 237235288Sadrian } 238235288Sadrian bus_generic_detach(dev); 239235288Sadrian callout_drain(&sc->callout_tick); 240235288Sadrian mtx_destroy(&sc->callout_mtx); 241235288Sadrian mtx_destroy(&sc->sc_mtx); 242235288Sadrian 243235288Sadrian return (0); 244235288Sadrian} 245235288Sadrian 246235288Sadrianstatic void 247235288Sadrianrtl8366rb_update_ifmedia(int portstatus, u_int *media_status, u_int *media_active) 248235288Sadrian{ 249235288Sadrian *media_active = IFM_ETHER; 250235288Sadrian *media_status = IFM_AVALID; 251235288Sadrian if ((portstatus & RTL8366RB_PLSR_LINK) != 0) 252235288Sadrian *media_status |= IFM_ACTIVE; 253235288Sadrian else { 254235288Sadrian *media_active |= IFM_NONE; 255235288Sadrian return; 256235288Sadrian } 257235288Sadrian switch (portstatus & RTL8366RB_PLSR_SPEED_MASK) { 258235288Sadrian case RTL8366RB_PLSR_SPEED_10: 259235288Sadrian *media_active |= IFM_10_T; 260235288Sadrian break; 261235288Sadrian case RTL8366RB_PLSR_SPEED_100: 262235288Sadrian *media_active |= IFM_100_TX; 263235288Sadrian break; 264235288Sadrian case RTL8366RB_PLSR_SPEED_1000: 265235288Sadrian *media_active |= IFM_1000_T; 266235288Sadrian break; 267235288Sadrian } 268235288Sadrian if ((portstatus & RTL8366RB_PLSR_FULLDUPLEX) == 0) 269235288Sadrian *media_active |= IFM_FDX; 270235288Sadrian else 271235288Sadrian *media_active |= IFM_HDX; 272235288Sadrian if ((portstatus & RTL8366RB_PLSR_TXPAUSE) != 0) 273235288Sadrian *media_active |= IFM_ETH_TXPAUSE; 274235288Sadrian if ((portstatus & RTL8366RB_PLSR_RXPAUSE) != 0) 275235288Sadrian *media_active |= IFM_ETH_RXPAUSE; 276235288Sadrian} 277235288Sadrian 278235288Sadrianstatic void 279235288Sadrianrtl833rb_miipollstat(struct rtl8366rb_softc *sc) 280235288Sadrian{ 281235288Sadrian int i; 282235288Sadrian struct mii_data *mii; 283235288Sadrian struct mii_softc *miisc; 284235288Sadrian uint16_t value; 285235288Sadrian int portstatus; 286235288Sadrian 287235288Sadrian for (i = 0; i < RTL8366RB_NUM_PHYS; i++) { 288235288Sadrian mii = device_get_softc(sc->miibus[i]); 289235288Sadrian if ((i % 2) == 0) { 290235288Sadrian if (smi_read(sc->dev, RTL8366RB_PLSR_BASE + i/2, &value, RTL_NOWAIT) != 0) { 291235288Sadrian DEBUG_INCRVAR(callout_blocked); 292235288Sadrian return; 293235288Sadrian } 294235288Sadrian portstatus = value & 0xff; 295235288Sadrian } else { 296235288Sadrian portstatus = (value >> 8) & 0xff; 297235288Sadrian } 298235288Sadrian rtl8366rb_update_ifmedia(portstatus, &mii->mii_media_status, &mii->mii_media_active); 299235288Sadrian LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { 300235288Sadrian if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) != miisc->mii_inst) 301235288Sadrian continue; 302235288Sadrian mii_phy_update(miisc, MII_POLLSTAT); 303235288Sadrian } 304235288Sadrian } 305235288Sadrian} 306235288Sadrian 307235288Sadrianstatic void 308235288Sadrianrtl8366rb_tick(void *arg) 309235288Sadrian{ 310235288Sadrian struct rtl8366rb_softc *sc = arg; 311235288Sadrian 312235288Sadrian rtl833rb_miipollstat(sc); 313235288Sadrian callout_reset(&sc->callout_tick, hz, rtl8366rb_tick, sc); 314235288Sadrian} 315235288Sadrian 316235288Sadrianstatic int 317235288Sadriansmi_probe(device_t dev) 318235288Sadrian{ 319235288Sadrian device_t iicbus, iicha; 320235288Sadrian int err, i; 321235288Sadrian uint16_t chipid; 322235288Sadrian char bytes[2]; 323235288Sadrian int xferd; 324235288Sadrian 325235288Sadrian bytes[0] = RTL8366RB_CIR & 0xff; 326235288Sadrian bytes[1] = (RTL8366RB_CIR >> 8) & 0xff; 327235288Sadrian iicbus = device_get_parent(dev); 328235288Sadrian iicha = device_get_parent(iicbus); 329235288Sadrian iicbus_reset(iicbus, IIC_FASTEST, RTL8366RB_IIC_ADDR, NULL); 330235288Sadrian for (i=3; i--; ) { 331235288Sadrian IICBUS_STOP(iicha); 332235288Sadrian /* 333235288Sadrian * we go directly to the host adapter because iicbus.c 334235288Sadrian * only issues a stop on a bus that was successfully started. 335235288Sadrian */ 336235288Sadrian } 337235288Sadrian err = iicbus_request_bus(iicbus, dev, IIC_WAIT); 338235288Sadrian if (err != 0) 339235288Sadrian goto out; 340235288Sadrian err = iicbus_start(iicbus, RTL8366RB_IIC_ADDR | RTL_IICBUS_READ, RTL_IICBUS_TIMEOUT); 341235288Sadrian if (err != 0) 342235288Sadrian goto out; 343235288Sadrian err = iicbus_write(iicbus, bytes, 2, &xferd, RTL_IICBUS_TIMEOUT); 344235288Sadrian if (err != 0) 345235288Sadrian goto out; 346235288Sadrian err = iicbus_read(iicbus, bytes, 2, &xferd, IIC_LAST_READ, 0); 347235288Sadrian if (err != 0) 348235288Sadrian goto out; 349235288Sadrian chipid = ((bytes[1] & 0xff) << 8) | (bytes[0] & 0xff); 350235288Sadrian DPRINTF(dev, "chip id 0x%04x\n", chipid); 351235288Sadrian if (chipid != RTL8366RB_CIR_ID8366RB) 352235288Sadrian err = ENXIO; 353235288Sadrianout: 354235288Sadrian iicbus_stop(iicbus); 355235288Sadrian iicbus_release_bus(iicbus, dev); 356235288Sadrian return (err == 0 ? 0 : ENXIO); 357235288Sadrian} 358235288Sadrian 359235288Sadrianstatic int 360235288Sadriansmi_acquire(struct rtl8366rb_softc *sc, int sleep) 361235288Sadrian{ 362235288Sadrian int r = 0; 363235288Sadrian if (sleep == RTL_WAITOK) 364235288Sadrian RTL_LOCK(sc); 365235288Sadrian else 366235288Sadrian if (RTL_TRYLOCK(sc) == 0) 367235288Sadrian return (EWOULDBLOCK); 368235288Sadrian if (sc->smi_acquired == RTL_SMI_ACQUIRED) 369235288Sadrian r = EBUSY; 370235288Sadrian else { 371235288Sadrian r = iicbus_request_bus(device_get_parent(sc->dev), sc->dev, \ 372235288Sadrian sleep == RTL_WAITOK ? IIC_WAIT : IIC_DONTWAIT); 373235288Sadrian if (r == 0) 374235288Sadrian sc->smi_acquired = RTL_SMI_ACQUIRED; 375235288Sadrian } 376235288Sadrian RTL_UNLOCK(sc); 377235288Sadrian return (r); 378235288Sadrian} 379235288Sadrian 380235288Sadrianstatic int 381235288Sadriansmi_release(struct rtl8366rb_softc *sc, int sleep) 382235288Sadrian{ 383235288Sadrian if (sleep == RTL_WAITOK) 384235288Sadrian RTL_LOCK(sc); 385235288Sadrian else 386235288Sadrian if (RTL_TRYLOCK(sc) == 0) 387235288Sadrian return (EWOULDBLOCK); 388235288Sadrian RTL_SMI_ACQUIRED_ASSERT(sc); 389235288Sadrian iicbus_release_bus(device_get_parent(sc->dev), sc->dev); 390235288Sadrian sc->smi_acquired = 0; 391235288Sadrian RTL_UNLOCK(sc); 392235288Sadrian return (0); 393235288Sadrian} 394235288Sadrian 395235288Sadrianstatic int 396235288Sadriansmi_select(device_t dev, int op, int sleep) 397235288Sadrian{ 398235288Sadrian int err, i; 399235288Sadrian device_t iicbus = device_get_parent(dev); 400235288Sadrian struct iicbus_ivar *devi = IICBUS_IVAR(dev); 401235288Sadrian int slave = devi->addr; 402235288Sadrian 403235288Sadrian RTL_SMI_ACQUIRED_ASSERT((struct rtl8366rb_softc *)device_get_softc(dev)); 404235288Sadrian /* 405235288Sadrian * The chip does not use clock stretching when it is busy, 406235288Sadrian * instead ignoring the command. Retry a few times. 407235288Sadrian */ 408235288Sadrian for (i = RTL_IICBUS_RETRIES; i--; ) { 409235288Sadrian err = iicbus_start(iicbus, slave | op, RTL_IICBUS_TIMEOUT); 410235288Sadrian if (err != IIC_ENOACK) 411235288Sadrian break; 412235288Sadrian if (sleep == RTL_WAITOK) { 413235288Sadrian DEBUG_INCRVAR(iic_select_retries); 414235288Sadrian pause("smi_select", RTL_IICBUS_RETRY_SLEEP); 415235288Sadrian } else 416235288Sadrian break; 417235288Sadrian } 418235288Sadrian return (err); 419235288Sadrian} 420235288Sadrian 421235288Sadrianstatic int 422235288Sadriansmi_read_locked(struct rtl8366rb_softc *sc, uint16_t addr, uint16_t *data, int sleep) 423235288Sadrian{ 424235288Sadrian int err; 425235288Sadrian device_t iicbus = device_get_parent(sc->dev); 426235288Sadrian char bytes[2]; 427235288Sadrian int xferd; 428235288Sadrian 429235288Sadrian RTL_SMI_ACQUIRED_ASSERT(sc); 430235288Sadrian bytes[0] = addr & 0xff; 431235288Sadrian bytes[1] = (addr >> 8) & 0xff; 432235288Sadrian err = smi_select(sc->dev, RTL_IICBUS_READ, sleep); 433235288Sadrian if (err != 0) 434235288Sadrian goto out; 435235288Sadrian err = iicbus_write(iicbus, bytes, 2, &xferd, RTL_IICBUS_TIMEOUT); 436235288Sadrian if (err != 0) 437235288Sadrian goto out; 438235288Sadrian err = iicbus_read(iicbus, bytes, 2, &xferd, IIC_LAST_READ, 0); 439235288Sadrian if (err != 0) 440235288Sadrian goto out; 441235288Sadrian *data = ((bytes[1] & 0xff) << 8) | (bytes[0] & 0xff); 442235288Sadrian 443235288Sadrianout: 444235288Sadrian iicbus_stop(iicbus); 445235288Sadrian return (err); 446235288Sadrian} 447235288Sadrian 448235288Sadrianstatic int 449235288Sadriansmi_write_locked(struct rtl8366rb_softc *sc, uint16_t addr, uint16_t data, int sleep) 450235288Sadrian{ 451235288Sadrian int err; 452235288Sadrian device_t iicbus = device_get_parent(sc->dev); 453235288Sadrian char bytes[4]; 454235288Sadrian int xferd; 455235288Sadrian 456235288Sadrian RTL_SMI_ACQUIRED_ASSERT(sc); 457235288Sadrian bytes[0] = addr & 0xff; 458235288Sadrian bytes[1] = (addr >> 8) & 0xff; 459235288Sadrian bytes[2] = data & 0xff; 460235288Sadrian bytes[3] = (data >> 8) & 0xff; 461235288Sadrian 462235288Sadrian err = smi_select(sc->dev, RTL_IICBUS_WRITE, sleep); 463235288Sadrian if (err == 0) 464235288Sadrian err = iicbus_write(iicbus, bytes, 4, &xferd, RTL_IICBUS_TIMEOUT); 465235288Sadrian iicbus_stop(iicbus); 466235288Sadrian 467235288Sadrian return (err); 468235288Sadrian} 469235288Sadrian 470235288Sadrianstatic int 471235288Sadriansmi_read(device_t dev, uint16_t addr, uint16_t *data, int sleep) 472235288Sadrian{ 473235288Sadrian struct rtl8366rb_softc *sc = device_get_softc(dev); 474235288Sadrian int err; 475235288Sadrian 476235288Sadrian err = smi_acquire(sc, sleep); 477235288Sadrian if (err != 0) 478235288Sadrian return (EBUSY); 479235288Sadrian err = smi_read_locked(sc, addr, data, sleep); 480235288Sadrian smi_release(sc, sleep); 481235288Sadrian DEVERR(dev, err, "smi_read()=%d: addr=%04x\n", addr); 482235288Sadrian return (err == 0 ? 0 : EIO); 483235288Sadrian} 484235288Sadrian 485235288Sadrianstatic int 486235288Sadriansmi_write(device_t dev, uint16_t addr, uint16_t data, int sleep) 487235288Sadrian{ 488235288Sadrian struct rtl8366rb_softc *sc = device_get_softc(dev); 489235288Sadrian int err; 490235288Sadrian 491235288Sadrian err = smi_acquire(sc, sleep); 492235288Sadrian if (err != 0) 493235288Sadrian return (EBUSY); 494235288Sadrian err = smi_write_locked(sc, addr, data, sleep); 495235288Sadrian smi_release(sc, sleep); 496235288Sadrian DEVERR(dev, err, "smi_write()=%d: addr=%04x\n", addr); 497235288Sadrian return (err == 0 ? 0 : EIO); 498235288Sadrian} 499235288Sadrian 500235288Sadrianstatic int 501235288Sadriansmi_rmw(device_t dev, uint16_t addr, uint16_t mask, uint16_t data, int sleep) 502235288Sadrian{ 503235288Sadrian struct rtl8366rb_softc *sc = device_get_softc(dev); 504235288Sadrian int err; 505235288Sadrian uint16_t oldv, newv; 506235288Sadrian 507235288Sadrian err = smi_acquire(sc, sleep); 508235288Sadrian if (err != 0) 509235288Sadrian return (EBUSY); 510235288Sadrian if (err == 0) { 511235288Sadrian err = smi_read_locked(sc, addr, &oldv, sleep); 512235288Sadrian if (err == 0) { 513235288Sadrian newv = oldv & ~mask; 514235288Sadrian newv |= data & mask; 515235288Sadrian if (newv != oldv) 516235288Sadrian err = smi_write_locked(sc, addr, newv, sleep); 517235288Sadrian } 518235288Sadrian } 519235288Sadrian smi_release(sc, sleep); 520235288Sadrian DEVERR(dev, err, "smi_rmw()=%d: addr=%04x\n", addr); 521235288Sadrian return (err == 0 ? 0 : EIO); 522235288Sadrian} 523235288Sadrian 524235288Sadrianstatic etherswitch_info_t * 525235288Sadrianrtl_getinfo(device_t dev) 526235288Sadrian{ 527235288Sadrian return (ðerswitch_info); 528235288Sadrian} 529235288Sadrian 530235288Sadrianstatic int 531235288Sadrianrtl_readreg(device_t dev, int reg) 532235288Sadrian{ 533235288Sadrian uint16_t data = 0; 534235288Sadrian 535235288Sadrian smi_read(dev, reg, &data, RTL_WAITOK); 536235288Sadrian return (data); 537235288Sadrian} 538235288Sadrian 539235288Sadrianstatic int 540235288Sadrianrtl_writereg(device_t dev, int reg, int value) 541235288Sadrian{ 542235288Sadrian return (smi_write(dev, reg, value, RTL_WAITOK)); 543235288Sadrian} 544235288Sadrian 545235288Sadrianstatic int 546235288Sadrianrtl_getport(device_t dev, etherswitch_port_t *p) 547235288Sadrian{ 548235288Sadrian struct rtl8366rb_softc *sc; 549235288Sadrian struct ifmedia *ifm; 550235288Sadrian struct mii_data *mii; 551235288Sadrian struct ifmediareq *ifmr = &p->es_ifmr; 552235288Sadrian uint16_t v; 553235288Sadrian int err; 554235288Sadrian 555235288Sadrian if (p->es_port < 0 || p->es_port >= RTL8366RB_NUM_PORTS) 556235288Sadrian return (ENXIO); 557235288Sadrian p->es_vlangroup = RTL8366RB_PVCR_GET(p->es_port, 558235288Sadrian rtl_readreg(dev, RTL8366RB_PVCR_REG(p->es_port))); 559235288Sadrian 560235288Sadrian if (p->es_port < RTL8366RB_NUM_PHYS) { 561235288Sadrian sc = device_get_softc(dev); 562235288Sadrian mii = device_get_softc(sc->miibus[p->es_port]); 563235288Sadrian ifm = &mii->mii_media; 564235288Sadrian err = ifmedia_ioctl(sc->ifp[p->es_port], &p->es_ifr, ifm, SIOCGIFMEDIA); 565235288Sadrian if (err) 566235288Sadrian return (err); 567235288Sadrian } else { 568235288Sadrian /* fill in fixed values for CPU port */ 569235288Sadrian ifmr->ifm_count = 0; 570235288Sadrian smi_read(dev, RTL8366RB_PLSR_BASE + (RTL8366RB_NUM_PHYS)/2, &v, RTL_WAITOK); 571235288Sadrian v = v >> (8 * ((RTL8366RB_NUM_PHYS) % 2)); 572235288Sadrian rtl8366rb_update_ifmedia(v, &ifmr->ifm_status, &ifmr->ifm_active); 573235288Sadrian ifmr->ifm_current = ifmr->ifm_active; 574235288Sadrian ifmr->ifm_mask = 0; 575235288Sadrian ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID; 576235288Sadrian } 577235288Sadrian return (0); 578235288Sadrian} 579235288Sadrian 580235288Sadrianstatic int 581235288Sadrianrtl_setport(device_t dev, etherswitch_port_t *p) 582235288Sadrian{ 583235288Sadrian int err; 584235288Sadrian struct rtl8366rb_softc *sc; 585235288Sadrian struct ifmedia *ifm; 586235288Sadrian struct mii_data *mii; 587235288Sadrian 588235288Sadrian if (p->es_port < 0 || p->es_port >= RTL8366RB_NUM_PHYS) 589235288Sadrian return (ENXIO); 590235288Sadrian err = smi_rmw(dev, RTL8366RB_PVCR_REG(p->es_port), 591235288Sadrian RTL8366RB_PVCR_VAL(p->es_port, RTL8366RB_PVCR_PORT_MASK), 592235288Sadrian RTL8366RB_PVCR_VAL(p->es_port, p->es_vlangroup), RTL_WAITOK); 593235288Sadrian if (err) 594235288Sadrian return (err); 595235288Sadrian sc = device_get_softc(dev); 596235288Sadrian mii = device_get_softc(sc->miibus[p->es_port]); 597235288Sadrian ifm = &mii->mii_media; 598235288Sadrian err = ifmedia_ioctl(sc->ifp[p->es_port], &p->es_ifr, ifm, SIOCSIFMEDIA); 599235288Sadrian return (err); 600235288Sadrian} 601235288Sadrian 602235288Sadrianstatic int 603235288Sadrianrtl_getvgroup(device_t dev, etherswitch_vlangroup_t *vg) 604235288Sadrian{ 605235288Sadrian uint16_t vmcr[3]; 606235288Sadrian int i; 607235288Sadrian 608235288Sadrian for (i=0; i<3; i++) 609235288Sadrian vmcr[i] = rtl_readreg(dev, RTL8366RB_VMCR(i, vg->es_vlangroup)); 610235288Sadrian 611235288Sadrian vg->es_vid = RTL8366RB_VMCR_VID(vmcr); 612235288Sadrian vg->es_member_ports = RTL8366RB_VMCR_MEMBER(vmcr); 613235288Sadrian vg->es_untagged_ports = RTL8366RB_VMCR_UNTAG(vmcr); 614235288Sadrian vg->es_fid = RTL8366RB_VMCR_FID(vmcr); 615235288Sadrian return (0); 616235288Sadrian} 617235288Sadrian 618235288Sadrianstatic int 619235288Sadrianrtl_setvgroup(device_t dev, etherswitch_vlangroup_t *vg) 620235288Sadrian{ 621235288Sadrian int g = vg->es_vlangroup; 622235288Sadrian 623235288Sadrian rtl_writereg(dev, RTL8366RB_VMCR(RTL8366RB_VMCR_DOT1Q_REG, g), 624235288Sadrian (vg->es_vid << RTL8366RB_VMCR_DOT1Q_VID_SHIFT) & RTL8366RB_VMCR_DOT1Q_VID_MASK); 625235288Sadrian rtl_writereg(dev, RTL8366RB_VMCR(RTL8366RB_VMCR_MU_REG, g), 626235288Sadrian ((vg->es_member_ports << RTL8366RB_VMCR_MU_MEMBER_SHIFT) & RTL8366RB_VMCR_MU_MEMBER_MASK) | 627235288Sadrian ((vg->es_untagged_ports << RTL8366RB_VMCR_MU_UNTAG_SHIFT) & RTL8366RB_VMCR_MU_UNTAG_MASK)); 628235288Sadrian rtl_writereg(dev, RTL8366RB_VMCR(RTL8366RB_VMCR_FID_REG, g), 629235288Sadrian vg->es_fid); 630235288Sadrian return (0); 631235288Sadrian} 632235288Sadrian 633235288Sadrianstatic int 634235288Sadrianrtl_readphy(device_t dev, int phy, int reg) 635235288Sadrian{ 636235288Sadrian struct rtl8366rb_softc *sc = device_get_softc(dev); 637235288Sadrian uint16_t data = 0; 638235288Sadrian int err, i, sleep; 639235288Sadrian 640235288Sadrian if (phy < 0 || phy >= RTL8366RB_NUM_PHYS) 641235288Sadrian return (ENXIO); 642235288Sadrian if (reg < 0 || reg >= RTL8366RB_NUM_PHY_REG) 643235288Sadrian return (ENXIO); 644235288Sadrian sleep = RTL_WAITOK; 645235288Sadrian err = smi_acquire(sc, sleep); 646235288Sadrian if (err != 0) 647235288Sadrian return (EBUSY); 648235288Sadrian for (i = RTL_IICBUS_RETRIES; i--; ) { 649235288Sadrian err = smi_write_locked(sc, RTL8366RB_PACR, RTL8366RB_PACR_READ, sleep); 650235288Sadrian if (err == 0) 651235288Sadrian err = smi_write_locked(sc, RTL8366RB_PHYREG(phy, 0, reg), 0, sleep); 652235288Sadrian if (err == 0) { 653235288Sadrian err = smi_read_locked(sc, RTL8366RB_PADR, &data, sleep); 654235288Sadrian break; 655235288Sadrian } 656235288Sadrian DEBUG_INCRVAR(phy_access_retries); 657235288Sadrian DPRINTF(dev, "rtl_readphy(): chip not responsive, retrying %d more times\n", i); 658235288Sadrian pause("rtl_readphy", RTL_IICBUS_RETRY_SLEEP); 659235288Sadrian } 660235288Sadrian smi_release(sc, sleep); 661235288Sadrian DEVERR(dev, err, "rtl_readphy()=%d: phy=%d.%02x\n", phy, reg); 662235288Sadrian return (data); 663235288Sadrian} 664235288Sadrian 665235288Sadrianstatic int 666235288Sadrianrtl_writephy(device_t dev, int phy, int reg, int data) 667235288Sadrian{ 668235288Sadrian struct rtl8366rb_softc *sc = device_get_softc(dev); 669235288Sadrian int err, i, sleep; 670235288Sadrian 671235288Sadrian if (phy < 0 || phy >= RTL8366RB_NUM_PHYS) 672235288Sadrian return (ENXIO); 673235288Sadrian if (reg < 0 || reg >= RTL8366RB_NUM_PHY_REG) 674235288Sadrian return (ENXIO); 675235288Sadrian sleep = RTL_WAITOK; 676235288Sadrian err = smi_acquire(sc, sleep); 677235288Sadrian if (err != 0) 678235288Sadrian return (EBUSY); 679235288Sadrian for (i = RTL_IICBUS_RETRIES; i--; ) { 680235288Sadrian err = smi_write_locked(sc, RTL8366RB_PACR, RTL8366RB_PACR_WRITE, sleep); 681235288Sadrian if (err == 0) 682235288Sadrian err = smi_write_locked(sc, RTL8366RB_PHYREG(phy, 0, reg), data, sleep); 683235288Sadrian if (err == 0) { 684235288Sadrian break; 685235288Sadrian } 686235288Sadrian DEBUG_INCRVAR(phy_access_retries); 687235288Sadrian DPRINTF(dev, "rtl_writephy(): chip not responsive, retrying %d more tiems\n", i); 688235288Sadrian pause("rtl_writephy", RTL_IICBUS_RETRY_SLEEP); 689235288Sadrian } 690235288Sadrian smi_release(sc, sleep); 691235288Sadrian DEVERR(dev, err, "rtl_writephy()=%d: phy=%d.%02x\n", phy, reg); 692235288Sadrian return (err == 0 ? 0 : EIO); 693235288Sadrian} 694235288Sadrian 695235288Sadrianstatic int 696235288Sadrianrtl8366rb_ifmedia_upd(struct ifnet *ifp) 697235288Sadrian{ 698235288Sadrian struct rtl8366rb_softc *sc = ifp->if_softc; 699235288Sadrian struct mii_data *mii = device_get_softc(sc->miibus[ifp->if_dunit]); 700235288Sadrian 701235288Sadrian mii_mediachg(mii); 702235288Sadrian return (0); 703235288Sadrian} 704235288Sadrian 705235288Sadrianstatic void 706235288Sadrianrtl8366rb_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) 707235288Sadrian{ 708235288Sadrian struct rtl8366rb_softc *sc = ifp->if_softc; 709235288Sadrian struct mii_data *mii = device_get_softc(sc->miibus[ifp->if_dunit]); 710235288Sadrian 711235288Sadrian mii_pollstat(mii); 712235288Sadrian ifmr->ifm_active = mii->mii_media_active; 713235288Sadrian ifmr->ifm_status = mii->mii_media_status; 714235288Sadrian} 715235288Sadrian 716235288Sadrian 717235288Sadrianstatic device_method_t rtl8366rb_methods[] = { 718235288Sadrian /* Device interface */ 719235288Sadrian DEVMETHOD(device_identify, rtl8366rb_identify), 720235288Sadrian DEVMETHOD(device_probe, rtl8366rb_probe), 721235288Sadrian DEVMETHOD(device_attach, rtl8366rb_attach), 722235288Sadrian DEVMETHOD(device_detach, rtl8366rb_detach), 723235288Sadrian 724235288Sadrian /* bus interface */ 725235288Sadrian DEVMETHOD(bus_add_child, device_add_child_ordered), 726235288Sadrian 727235288Sadrian /* MII interface */ 728235288Sadrian DEVMETHOD(miibus_readreg, rtl_readphy), 729235288Sadrian DEVMETHOD(miibus_writereg, rtl_writephy), 730235288Sadrian 731235288Sadrian /* etherswitch interface */ 732235288Sadrian DEVMETHOD(etherswitch_getinfo, rtl_getinfo), 733235288Sadrian DEVMETHOD(etherswitch_readreg, rtl_readreg), 734235288Sadrian DEVMETHOD(etherswitch_writereg, rtl_writereg), 735235288Sadrian DEVMETHOD(etherswitch_readphyreg, rtl_readphy), 736235288Sadrian DEVMETHOD(etherswitch_writephyreg, rtl_writephy), 737235288Sadrian DEVMETHOD(etherswitch_getport, rtl_getport), 738235288Sadrian DEVMETHOD(etherswitch_setport, rtl_setport), 739235288Sadrian DEVMETHOD(etherswitch_getvgroup, rtl_getvgroup), 740235288Sadrian DEVMETHOD(etherswitch_setvgroup, rtl_setvgroup), 741235288Sadrian 742235288Sadrian DEVMETHOD_END 743235288Sadrian}; 744235288Sadrian 745235288SadrianDEFINE_CLASS_0(rtl8366rb, rtl8366rb_driver, rtl8366rb_methods, 746235288Sadrian sizeof(struct rtl8366rb_softc)); 747235288Sadrianstatic devclass_t rtl8366rb_devclass; 748235288Sadrian 749235288SadrianDRIVER_MODULE(rtl8366rb, iicbus, rtl8366rb_driver, rtl8366rb_devclass, 0, 0); 750235288SadrianDRIVER_MODULE(miibus, rtl8366rb, miibus_driver, miibus_devclass, 0, 0); 751235288SadrianDRIVER_MODULE(etherswitch, rtl8366rb, etherswitch_driver, etherswitch_devclass, 0, 0); 752235288SadrianMODULE_VERSION(rtl8366rb, 1); 753235288SadrianMODULE_DEPEND(rtl8366rb, iicbus, 1, 1, 1); /* XXX which versions? */ 754235288SadrianMODULE_DEPEND(rtl8366rb, miibus, 1, 1, 1); /* XXX which versions? */ 755235288SadrianMODULE_DEPEND(rtl8366rb, etherswitch, 1, 1, 1); /* XXX which versions? */ 756