1289947Szbb/*- 2289947Szbb * Copyright (c) 2015 Semihalf 3289947Szbb * Copyright (c) 2015 Stormshield 4289947Szbb * All rights reserved. 5289947Szbb * 6289947Szbb * Redistribution and use in source and binary forms, with or without 7289947Szbb * modification, are permitted provided that the following conditions 8289947Szbb * are met: 9289947Szbb * 1. Redistributions of source code must retain the above copyright 10289947Szbb * notice, this list of conditions and the following disclaimer. 11289947Szbb * 2. Redistributions in binary form must reproduce the above copyright 12289947Szbb * notice, this list of conditions and the following disclaimer in the 13289947Szbb * documentation and/or other materials provided with the distribution. 14289947Szbb * 15289947Szbb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16289947Szbb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17289947Szbb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18289947Szbb * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19289947Szbb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20289947Szbb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21289947Szbb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22289947Szbb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23289947Szbb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24289947Szbb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25289947Szbb * SUCH DAMAGE. 26289947Szbb */ 27289947Szbb 28289947Szbb#include <sys/cdefs.h> 29289947Szbb__FBSDID("$FreeBSD$"); 30289947Szbb 31289947Szbb#include <sys/types.h> 32289947Szbb#include <sys/param.h> 33289947Szbb#include <sys/systm.h> 34289947Szbb#include <sys/sockio.h> 35289947Szbb#include <sys/kernel.h> 36289947Szbb#include <sys/kthread.h> 37289947Szbb#include <sys/socket.h> 38289947Szbb#include <sys/module.h> 39289947Szbb#include <sys/errno.h> 40289947Szbb#include <sys/bus.h> 41289947Szbb#include <sys/conf.h> 42289947Szbb#include <sys/uio.h> 43289947Szbb#include <sys/fcntl.h> 44289947Szbb 45289947Szbb#include <net/if.h> 46289947Szbb#include <net/if_media.h> 47289947Szbb#include <net/if_types.h> 48289947Szbb 49289947Szbb#include <machine/bus.h> 50289947Szbb#include <machine/resource.h> 51289947Szbb 52289947Szbb#include <arm/mv/mvwin.h> 53289947Szbb#include <arm/mv/mvreg.h> 54289947Szbb#include <arm/mv/mvvar.h> 55289947Szbb 56289947Szbb#include <dev/etherswitch/etherswitch.h> 57292738Sadrian#include <dev/mdio/mdio.h> 58289947Szbb#include <dev/mii/mii.h> 59289947Szbb#include <dev/mii/miivar.h> 60289947Szbb#include <dev/mge/if_mgevar.h> 61289947Szbb 62289947Szbb#include "e6000swreg.h" 63289947Szbb#include "etherswitch_if.h" 64289947Szbb#include "miibus_if.h" 65289947Szbb#include "mdio_if.h" 66289947Szbb 67289947SzbbMALLOC_DECLARE(M_E6000SW); 68289947SzbbMALLOC_DEFINE(M_E6000SW, "e6000sw", "e6000sw switch"); 69289947Szbb 70289947Szbb#define E6000SW_LOCK(_sc) \ 71289947Szbb sx_xlock(&(_sc)->sx) 72289947Szbb#define E6000SW_UNLOCK(_sc) \ 73289947Szbb sx_unlock(&(_sc)->sx) 74289947Szbb#define E6000SW_LOCK_ASSERT(_sc, _what) \ 75289947Szbb sx_assert(&(_sc)->sx, (_what)) 76289947Szbb#define E6000SW_TRYLOCK(_sc) \ 77289947Szbb sx_tryxlock(&(_sc)->sx) 78289947Szbb 79289947Szbbtypedef struct e6000sw_softc { 80289947Szbb device_t dev; 81289947Szbb 82289947Szbb struct sx sx; 83289947Szbb struct ifnet *ifp[E6000SW_NUM_PHYS]; 84289947Szbb char *ifname[E6000SW_NUM_PHYS]; 85289947Szbb device_t miibus[E6000SW_NUM_PHYS]; 86289947Szbb struct mii_data *mii[E6000SW_NUM_PHYS]; 87289947Szbb struct callout tick_callout; 88289947Szbb 89289947Szbb uint32_t cpuports_mask; 90289947Szbb 91289947Szbb int vid[E6000SW_NUM_VGROUPS]; 92289947Szbb int members[E6000SW_NUM_VGROUPS]; 93289947Szbb int vgroup[E6000SW_NUM_PORTS]; 94289947Szbb} e6000sw_softc_t; 95289947Szbb 96289947Szbbstatic etherswitch_info_t etherswitch_info = { 97289947Szbb .es_nports = E6000SW_NUM_PORTS, 98289947Szbb .es_nvlangroups = E6000SW_NUM_VGROUPS, 99289947Szbb .es_name = "Marvell 6000 series switch" 100289947Szbb}; 101289947Szbb 102289947Szbbstatic void e6000sw_identify(driver_t *driver, device_t parent); 103289947Szbbstatic int e6000sw_probe(device_t dev); 104289947Szbbstatic int e6000sw_attach(device_t dev); 105289947Szbbstatic int e6000sw_detach(device_t dev); 106289947Szbbstatic int e6000sw_readphy(device_t dev, int phy, int reg); 107289947Szbbstatic int e6000sw_writephy(device_t dev, int phy, int reg, int data); 108289947Szbbstatic etherswitch_info_t* e6000sw_getinfo(device_t dev); 109289947Szbbstatic void e6000sw_lock(device_t dev); 110289947Szbbstatic void e6000sw_unlock(device_t dev); 111289947Szbbstatic int e6000sw_getport(device_t dev, etherswitch_port_t *p); 112289947Szbbstatic int e6000sw_setport(device_t dev, etherswitch_port_t *p); 113289947Szbbstatic int e6000sw_readreg_wrapper(device_t dev, int addr_reg); 114289947Szbbstatic int e6000sw_writereg_wrapper(device_t dev, int addr_reg, int val); 115289947Szbbstatic int e6000sw_readphy_wrapper(device_t dev, int phy, int reg); 116289947Szbbstatic int e6000sw_writephy_wrapper(device_t dev, int phy, int reg, int data); 117289947Szbbstatic int e6000sw_getvgroup_wrapper(device_t dev, etherswitch_vlangroup_t *vg); 118289947Szbbstatic int e6000sw_setvgroup_wrapper(device_t dev, etherswitch_vlangroup_t *vg); 119289947Szbbstatic int e6000sw_setvgroup(device_t dev, etherswitch_vlangroup_t *vg); 120289947Szbbstatic int e6000sw_getvgroup(device_t dev, etherswitch_vlangroup_t *vg); 121289947Szbbstatic void e6000sw_setup(device_t dev, e6000sw_softc_t *sc); 122289947Szbbstatic void e6000sw_port_vlan_conf(e6000sw_softc_t *sc); 123289947Szbbstatic void e6000sw_tick(void *arg); 124289947Szbbstatic void e6000sw_set_atustat(device_t dev, e6000sw_softc_t *sc, int bin, 125289947Szbb int flag); 126289947Szbbstatic int e6000sw_atu_flush(device_t dev, e6000sw_softc_t *sc, int flag); 127289947Szbbstatic __inline void e6000sw_writereg(e6000sw_softc_t *sc, int addr, int reg, 128289947Szbb int val); 129289947Szbbstatic __inline uint32_t e6000sw_readreg(e6000sw_softc_t *sc, int addr, 130289947Szbb int reg); 131289947Szbbstatic int e6000sw_ifmedia_upd(struct ifnet *ifp); 132289947Szbbstatic void e6000sw_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr); 133289947Szbbstatic int e6000sw_atu_mac_table(device_t dev, e6000sw_softc_t *sc, struct 134289947Szbb atu_opt *atu, int flag); 135289947Szbbstatic int e6000sw_get_pvid(e6000sw_softc_t *sc, int port, int *pvid); 136289947Szbbstatic int e6000sw_set_pvid(e6000sw_softc_t *sc, int port, int pvid); 137289947Szbbstatic __inline int e6000sw_cpuport(e6000sw_softc_t *sc, int port); 138289947Szbbstatic __inline struct mii_data *e6000sw_miiforphy(e6000sw_softc_t *sc, 139289947Szbb unsigned int phy); 140289947Szbb 141289947Szbbstatic struct proc *e6000sw_kproc; 142289947Szbb 143289947Szbbstatic device_method_t e6000sw_methods[] = { 144289947Szbb /* device interface */ 145289947Szbb DEVMETHOD(device_identify, e6000sw_identify), 146289947Szbb DEVMETHOD(device_probe, e6000sw_probe), 147289947Szbb DEVMETHOD(device_attach, e6000sw_attach), 148289947Szbb DEVMETHOD(device_detach, e6000sw_detach), 149289947Szbb 150289947Szbb /* bus interface */ 151289947Szbb DEVMETHOD(bus_add_child, device_add_child_ordered), 152289947Szbb 153289947Szbb /* mii interface */ 154289947Szbb DEVMETHOD(miibus_readreg, e6000sw_readphy), 155289947Szbb DEVMETHOD(miibus_writereg, e6000sw_writephy), 156289947Szbb 157289947Szbb /* etherswitch interface */ 158289947Szbb DEVMETHOD(etherswitch_getinfo, e6000sw_getinfo), 159289947Szbb DEVMETHOD(etherswitch_lock, e6000sw_lock), 160289947Szbb DEVMETHOD(etherswitch_unlock, e6000sw_unlock), 161289947Szbb DEVMETHOD(etherswitch_getport, e6000sw_getport), 162289947Szbb DEVMETHOD(etherswitch_setport, e6000sw_setport), 163289947Szbb DEVMETHOD(etherswitch_readreg, e6000sw_readreg_wrapper), 164289947Szbb DEVMETHOD(etherswitch_writereg, e6000sw_writereg_wrapper), 165289947Szbb DEVMETHOD(etherswitch_readphyreg, e6000sw_readphy_wrapper), 166289947Szbb DEVMETHOD(etherswitch_writephyreg, e6000sw_writephy_wrapper), 167289947Szbb DEVMETHOD(etherswitch_setvgroup, e6000sw_setvgroup_wrapper), 168289947Szbb DEVMETHOD(etherswitch_getvgroup, e6000sw_getvgroup_wrapper), 169289947Szbb 170289947Szbb DEVMETHOD_END 171289947Szbb}; 172289947Szbb 173289947Szbbstatic devclass_t e6000sw_devclass; 174289947Szbb 175289947SzbbDEFINE_CLASS_0(e6000sw, e6000sw_driver, e6000sw_methods, 176289947Szbb sizeof(e6000sw_softc_t)); 177289947Szbb 178289947SzbbDRIVER_MODULE(e6000sw, mdio, e6000sw_driver, e6000sw_devclass, 0, 0); 179289947SzbbDRIVER_MODULE(etherswitch, e6000sw, etherswitch_driver, etherswitch_devclass, 0, 180289947Szbb 0); 181289947SzbbDRIVER_MODULE(miibus, e6000sw, miibus_driver, miibus_devclass, 0, 0); 182289947SzbbMODULE_DEPEND(e6000sw, mdio, 1, 1, 1); 183289947Szbb 184289947Szbbstatic void 185289947Szbbe6000sw_identify(driver_t *driver, device_t parent) 186289947Szbb{ 187289947Szbb 188289947Szbb if (device_find_child(parent, "e6000sw", -1) == NULL) 189289947Szbb BUS_ADD_CHILD(parent, 0, "e6000sw", -1); 190289947Szbb} 191289947Szbb 192289947Szbbstatic int 193289947Szbbe6000sw_probe(device_t dev) 194289947Szbb{ 195289947Szbb e6000sw_softc_t *sc; 196289947Szbb const char *description; 197289947Szbb unsigned int id; 198289947Szbb 199289947Szbb sc = device_get_softc(dev); 200289947Szbb bzero(sc, sizeof(e6000sw_softc_t)); 201289947Szbb sc->dev = dev; 202289947Szbb /* Lock is necessary due to assertions. */ 203289947Szbb sx_init(&sc->sx, "e6000sw"); 204289947Szbb E6000SW_LOCK(sc); 205289947Szbb 206289947Szbb id = e6000sw_readreg(sc, REG_PORT(0), SWITCH_ID); 207289947Szbb 208289947Szbb switch (id & 0xfff0) { 209289947Szbb case 0x3520: 210289947Szbb description = "Marvell 88E6352"; 211289947Szbb break; 212289947Szbb case 0x1720: 213289947Szbb description = "Marvell 88E6172"; 214289947Szbb break; 215289947Szbb case 0x1760: 216289947Szbb description = "Marvell 88E6176"; 217289947Szbb break; 218289947Szbb default: 219289947Szbb E6000SW_UNLOCK(sc); 220289947Szbb sx_destroy(&sc->sx); 221289947Szbb device_printf(dev, "Unrecognized device.\n"); 222289947Szbb return (ENXIO); 223289947Szbb } 224289947Szbb 225289947Szbb device_set_desc(dev, description); 226289947Szbb 227289947Szbb E6000SW_UNLOCK(sc); 228289947Szbb 229289947Szbb return (BUS_PROBE_DEFAULT); 230289947Szbb} 231289947Szbb 232289947Szbbstatic int 233289947Szbbe6000sw_attach(device_t dev) 234289947Szbb{ 235289947Szbb e6000sw_softc_t *sc; 236289947Szbb int phy, err, port; 237289947Szbb char name[IFNAMSIZ]; 238289947Szbb 239289947Szbb err = 0; 240289947Szbb sc = device_get_softc(dev); 241289947Szbb E6000SW_LOCK(sc); 242289947Szbb sc->cpuports_mask = E6000SW_CPUPORTS_MASK; 243289947Szbb for (port = 0; port < E6000SW_NUM_PORTS; port++) 244289947Szbb sc->vgroup[port] = E6000SW_PORT_NO_VGROUP; 245289947Szbb e6000sw_setup(dev, sc); 246289947Szbb 247289947Szbb snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->dev)); 248289947Szbb for (phy = 0; phy < E6000SW_NUM_PHYS; phy++) { 249289947Szbb sc->ifp[phy] = if_alloc(IFT_ETHER); 250289947Szbb if (sc->ifp[phy] == NULL) 251289947Szbb goto out_fail; 252289947Szbb sc->ifp[phy]->if_softc = sc; 253289947Szbb sc->ifp[phy]->if_flags |= IFF_UP | IFF_BROADCAST | 254289947Szbb IFF_DRV_RUNNING | IFF_SIMPLEX; 255289947Szbb sc->ifname[phy] = malloc(strlen(name) + 1, M_E6000SW, M_WAITOK); 256289947Szbb if (sc->ifname[phy] == NULL) 257289947Szbb goto out_fail; 258289947Szbb bcopy(name, sc->ifname[phy], strlen(name) + 1); 259289947Szbb if_initname(sc->ifp[phy], sc->ifname[phy], phy); 260289947Szbb err = mii_attach(sc->dev, &sc->miibus[phy], sc->ifp[phy], 261289947Szbb e6000sw_ifmedia_upd, e6000sw_ifmedia_sts, BMSR_DEFCAPMASK, 262289947Szbb phy, MII_OFFSET_ANY, 0); 263289947Szbb if (err != 0) { 264289947Szbb device_printf(sc->dev, 265289947Szbb "attaching PHY %d failed\n", 266289947Szbb phy); 267289947Szbb goto out_fail; 268289947Szbb } 269289947Szbb sc->mii[phy] = device_get_softc(sc->miibus[phy]); 270289947Szbb } 271289947Szbb E6000SW_UNLOCK(sc); 272289947Szbb 273289947Szbb bus_generic_probe(dev); 274289947Szbb bus_enumerate_hinted_children(dev); 275289947Szbb bus_generic_attach(dev); 276289947Szbb 277289947Szbb kproc_create(e6000sw_tick, sc, &e6000sw_kproc, 0, 0, 278289947Szbb "e6000sw tick kproc"); 279289947Szbb 280289947Szbb return (0); 281289947Szbb 282289947Szbbout_fail: 283289947Szbb e6000sw_detach(dev); 284289947Szbb 285289947Szbb return (ENXIO); 286289947Szbb} 287289947Szbb 288289947Szbbstatic __inline void 289289947Szbbe6000sw_poll_done(e6000sw_softc_t *sc) 290289947Szbb{ 291289947Szbb 292289947Szbb while (e6000sw_readreg(sc, REG_GLOBAL2, PHY_CMD) & 293289947Szbb (1 << PHY_CMD_SMI_BUSY)) 294289947Szbb continue; 295289947Szbb} 296289947Szbb 297289947Szbb 298289947Szbb/* 299289947Szbb * PHY registers are paged. Put page index in reg 22 (accessible from every 300289947Szbb * page), then access specific register. 301289947Szbb */ 302289947Szbbstatic int 303289947Szbbe6000sw_readphy(device_t dev, int phy, int reg) 304289947Szbb{ 305289947Szbb e6000sw_softc_t *sc; 306289947Szbb uint32_t val; 307289947Szbb 308289947Szbb sc = device_get_softc(dev); 309289947Szbb val = 0; 310289947Szbb 311289947Szbb if (phy >= E6000SW_NUM_PHYS || reg >= E6000SW_NUM_PHY_REGS) { 312289947Szbb device_printf(dev, "Wrong register address.\n"); 313289947Szbb return (EINVAL); 314289947Szbb } 315289947Szbb 316289947Szbb E6000SW_LOCK_ASSERT(sc, SA_XLOCKED); 317289947Szbb 318289947Szbb e6000sw_poll_done(sc); 319289947Szbb val |= 1 << PHY_CMD_SMI_BUSY; 320289947Szbb val |= PHY_CMD_MODE_MDIO << PHY_CMD_MODE; 321289947Szbb val |= PHY_CMD_OPCODE_READ << PHY_CMD_OPCODE; 322289947Szbb val |= (reg << PHY_CMD_REG_ADDR) & PHY_CMD_REG_ADDR_MASK; 323289947Szbb val |= (phy << PHY_CMD_DEV_ADDR) & PHY_CMD_DEV_ADDR_MASK; 324289947Szbb e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_CMD_REG, val); 325289947Szbb e6000sw_poll_done(sc); 326289947Szbb val = e6000sw_readreg(sc, REG_GLOBAL2, SMI_PHY_DATA_REG) 327289947Szbb & PHY_DATA_MASK; 328289947Szbb 329289947Szbb return (val); 330289947Szbb} 331289947Szbb 332289947Szbbstatic int 333289947Szbbe6000sw_writephy(device_t dev, int phy, int reg, int data) 334289947Szbb{ 335289947Szbb e6000sw_softc_t *sc; 336289947Szbb uint32_t val; 337289947Szbb 338289947Szbb sc = device_get_softc(dev); 339289947Szbb val = 0; 340289947Szbb 341289947Szbb if (phy >= E6000SW_NUM_PHYS || reg >= E6000SW_NUM_PHY_REGS) { 342289947Szbb device_printf(dev, "Wrong register address.\n"); 343289947Szbb return (EINVAL); 344289947Szbb } 345289947Szbb 346289947Szbb E6000SW_LOCK_ASSERT(sc, SA_XLOCKED); 347289947Szbb 348289947Szbb e6000sw_poll_done(sc); 349289947Szbb val |= PHY_CMD_MODE_MDIO << PHY_CMD_MODE; 350289947Szbb val |= 1 << PHY_CMD_SMI_BUSY; 351289947Szbb val |= PHY_CMD_OPCODE_WRITE << PHY_CMD_OPCODE; 352289947Szbb val |= (reg << PHY_CMD_REG_ADDR) & PHY_CMD_REG_ADDR_MASK; 353289947Szbb val |= (phy << PHY_CMD_DEV_ADDR) & PHY_CMD_DEV_ADDR_MASK; 354289947Szbb e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_DATA_REG, 355289947Szbb data & PHY_DATA_MASK); 356289947Szbb e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_CMD_REG, val); 357289947Szbb e6000sw_poll_done(sc); 358289947Szbb 359289947Szbb return (0); 360289947Szbb} 361289947Szbb 362289947Szbbstatic int 363289947Szbbe6000sw_detach(device_t dev) 364289947Szbb{ 365289947Szbb int phy; 366289947Szbb e6000sw_softc_t *sc; 367289947Szbb 368289947Szbb sc = device_get_softc(dev); 369289947Szbb bus_generic_detach(dev); 370289947Szbb sx_destroy(&sc->sx); 371289947Szbb for (phy = 0; phy < E6000SW_NUM_PHYS; phy++) { 372289947Szbb if (sc->miibus[phy] != NULL) 373289947Szbb device_delete_child(dev, sc->miibus[phy]); 374289947Szbb if (sc->ifp[phy] != NULL) 375289947Szbb if_free(sc->ifp[phy]); 376289947Szbb if (sc->ifname[phy] != NULL) 377289947Szbb free(sc->ifname[phy], M_E6000SW); 378289947Szbb } 379289947Szbb 380289947Szbb return (0); 381289947Szbb} 382289947Szbb 383289947Szbbstatic etherswitch_info_t* 384289947Szbbe6000sw_getinfo(device_t dev) 385289947Szbb{ 386289947Szbb 387289947Szbb return (ðerswitch_info); 388289947Szbb} 389289947Szbb 390289947Szbbstatic void 391289947Szbbe6000sw_lock(device_t dev) 392289947Szbb{ 393289947Szbb struct e6000sw_softc *sc; 394289947Szbb 395289947Szbb sc = device_get_softc(dev); 396289947Szbb 397289947Szbb E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED); 398289947Szbb E6000SW_LOCK(sc); 399289947Szbb} 400289947Szbb 401289947Szbbstatic void 402289947Szbbe6000sw_unlock(device_t dev) 403289947Szbb{ 404289947Szbb struct e6000sw_softc *sc; 405289947Szbb 406289947Szbb sc = device_get_softc(dev); 407289947Szbb 408289947Szbb E6000SW_LOCK_ASSERT(sc, SA_XLOCKED); 409289947Szbb E6000SW_UNLOCK(sc); 410289947Szbb} 411289947Szbb 412289947Szbbstatic int 413289947Szbbe6000sw_getport(device_t dev, etherswitch_port_t *p) 414289947Szbb{ 415289947Szbb struct mii_data *mii; 416289947Szbb int err; 417289947Szbb struct ifmediareq *ifmr; 418289947Szbb 419289947Szbb err = 0; 420289947Szbb e6000sw_softc_t *sc = device_get_softc(dev); 421289947Szbb E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED); 422289947Szbb 423289947Szbb E6000SW_LOCK(sc); 424289947Szbb 425289947Szbb if (p->es_port >= E6000SW_NUM_PORTS || 426289947Szbb p->es_port < 0) { 427289947Szbb err = EINVAL; 428289947Szbb goto out; 429289947Szbb } 430289947Szbb 431289947Szbb e6000sw_get_pvid(sc, p->es_port, &p->es_pvid); 432289947Szbb 433289947Szbb if (e6000sw_cpuport(sc, p->es_port)) { 434289947Szbb p->es_flags |= ETHERSWITCH_PORT_CPU; 435289947Szbb ifmr = &p->es_ifmr; 436289947Szbb ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID; 437289947Szbb ifmr->ifm_count = 0; 438289947Szbb ifmr->ifm_current = ifmr->ifm_active = 439289947Szbb IFM_ETHER | IFM_1000_T | IFM_FDX; 440289947Szbb ifmr->ifm_mask = 0; 441289947Szbb } else { 442289947Szbb mii = e6000sw_miiforphy(sc, p->es_port); 443289947Szbb err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr, 444289947Szbb &mii->mii_media, SIOCGIFMEDIA); 445289947Szbb } 446289947Szbb 447289947Szbbout: 448289947Szbb E6000SW_UNLOCK(sc); 449289947Szbb return (err); 450289947Szbb} 451289947Szbb 452289947Szbbstatic int 453289947Szbbe6000sw_setport(device_t dev, etherswitch_port_t *p) 454289947Szbb{ 455289947Szbb e6000sw_softc_t *sc; 456289947Szbb int err; 457289947Szbb struct mii_data *mii; 458289947Szbb 459289947Szbb err = 0; 460289947Szbb sc = device_get_softc(dev); 461289947Szbb E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED); 462289947Szbb 463289947Szbb E6000SW_LOCK(sc); 464289947Szbb 465289947Szbb if (p->es_port >= E6000SW_NUM_PORTS || 466289947Szbb p->es_port < 0) { 467289947Szbb err = EINVAL; 468289947Szbb goto out; 469289947Szbb } 470289947Szbb 471289947Szbb if (p->es_pvid != 0) 472289947Szbb e6000sw_set_pvid(sc, p->es_port, p->es_pvid); 473289947Szbb if (!e6000sw_cpuport(sc, p->es_port)) { 474289947Szbb mii = e6000sw_miiforphy(sc, p->es_port); 475289947Szbb err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr, &mii->mii_media, 476289947Szbb SIOCSIFMEDIA); 477289947Szbb } 478289947Szbb 479289947Szbbout: 480289947Szbb E6000SW_UNLOCK(sc); 481289947Szbb return (err); 482289947Szbb} 483289947Szbb 484289947Szbb/* 485289947Szbb * Registers in this switch are divided into sections, specified in 486289947Szbb * documentation. So as to access any of them, section index and reg index 487289947Szbb * is necessary. etherswitchcfg uses only one variable, so indexes were 488289947Szbb * compressed into addr_reg: 32 * section_index + reg_index. 489289947Szbb */ 490289947Szbbstatic int 491289947Szbbe6000sw_readreg_wrapper(device_t dev, int addr_reg) 492289947Szbb{ 493289947Szbb 494289947Szbb if ((addr_reg > (REG_GLOBAL2 * 32 + REG_NUM_MAX)) || 495289947Szbb (addr_reg < (REG_PORT(0) * 32))) { 496289947Szbb device_printf(dev, "Wrong register address.\n"); 497289947Szbb return (EINVAL); 498289947Szbb } 499289947Szbb 500289947Szbb return (e6000sw_readreg(device_get_softc(dev), addr_reg / 32, 501289947Szbb addr_reg % 32)); 502289947Szbb} 503289947Szbb 504289947Szbbstatic int 505289947Szbbe6000sw_writereg_wrapper(device_t dev, int addr_reg, int val) 506289947Szbb{ 507289947Szbb 508289947Szbb if ((addr_reg > (REG_GLOBAL2 * 32 + REG_NUM_MAX)) || 509289947Szbb (addr_reg < (REG_PORT(0) * 32))) { 510289947Szbb device_printf(dev, "Wrong register address.\n"); 511289947Szbb return (EINVAL); 512289947Szbb } 513289947Szbb e6000sw_writereg(device_get_softc(dev), addr_reg / 5, 514289947Szbb addr_reg % 32, val); 515289947Szbb 516289947Szbb return (0); 517289947Szbb} 518289947Szbb 519289947Szbb/* 520289947Szbb * These wrappers are necessary because PHY accesses from etherswitchcfg 521289947Szbb * need to be synchronized with locks, while miibus PHY accesses do not. 522289947Szbb */ 523289947Szbbstatic int 524289947Szbbe6000sw_readphy_wrapper(device_t dev, int phy, int reg) 525289947Szbb{ 526289947Szbb e6000sw_softc_t *sc; 527289947Szbb int ret; 528289947Szbb 529289947Szbb sc = device_get_softc(dev); 530289947Szbb E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED); 531289947Szbb 532289947Szbb E6000SW_LOCK(sc); 533289947Szbb ret = e6000sw_readphy(dev, phy, reg); 534289947Szbb E6000SW_UNLOCK(sc); 535289947Szbb 536289947Szbb return (ret); 537289947Szbb} 538289947Szbb 539289947Szbbstatic int 540289947Szbbe6000sw_writephy_wrapper(device_t dev, int phy, int reg, int data) 541289947Szbb{ 542289947Szbb e6000sw_softc_t *sc; 543289947Szbb int ret; 544289947Szbb 545289947Szbb sc = device_get_softc(dev); 546289947Szbb E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED); 547289947Szbb 548289947Szbb E6000SW_LOCK(sc); 549289947Szbb ret = e6000sw_writephy(dev, phy, reg, data); 550289947Szbb E6000SW_UNLOCK(sc); 551289947Szbb 552289947Szbb return (ret); 553289947Szbb} 554289947Szbb 555289947Szbb/* 556289947Szbb * setvgroup/getvgroup called from etherswitchfcg need to be locked, 557289947Szbb * while internal calls do not. 558289947Szbb */ 559289947Szbbstatic int 560289947Szbbe6000sw_setvgroup_wrapper(device_t dev, etherswitch_vlangroup_t *vg) 561289947Szbb{ 562289947Szbb e6000sw_softc_t *sc; 563289947Szbb int ret; 564289947Szbb 565289947Szbb sc = device_get_softc(dev); 566289947Szbb E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED); 567289947Szbb 568289947Szbb E6000SW_LOCK(sc); 569289947Szbb ret = e6000sw_setvgroup(dev, vg); 570289947Szbb E6000SW_UNLOCK(sc); 571289947Szbb 572289947Szbb return (ret); 573289947Szbb} 574289947Szbb 575289947Szbbstatic int 576289947Szbbe6000sw_getvgroup_wrapper(device_t dev, etherswitch_vlangroup_t *vg) 577289947Szbb{ 578289947Szbb e6000sw_softc_t *sc; 579289947Szbb int ret; 580289947Szbb 581289947Szbb sc = device_get_softc(dev); 582289947Szbb E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED); 583289947Szbb 584289947Szbb E6000SW_LOCK(sc); 585289947Szbb ret = e6000sw_getvgroup(dev, vg); 586289947Szbb E6000SW_UNLOCK(sc); 587289947Szbb 588289947Szbb return (ret); 589289947Szbb} 590289947Szbb 591289947Szbbstatic __inline void 592289947Szbbe6000sw_flush_port(e6000sw_softc_t *sc, int port) 593289947Szbb{ 594289947Szbb uint32_t reg; 595289947Szbb 596289947Szbb reg = e6000sw_readreg(sc, REG_PORT(port), 597289947Szbb PORT_VLAN_MAP); 598289947Szbb reg &= ~PORT_VLAN_MAP_TABLE_MASK; 599289947Szbb reg &= ~PORT_VLAN_MAP_FID_MASK; 600289947Szbb e6000sw_writereg(sc, REG_PORT(port), 601289947Szbb PORT_VLAN_MAP, reg); 602289947Szbb if (sc->vgroup[port] != E6000SW_PORT_NO_VGROUP) { 603289947Szbb /* 604289947Szbb * If port belonged somewhere, owner-group 605289947Szbb * should have its entry removed. 606289947Szbb */ 607289947Szbb sc->members[sc->vgroup[port]] &= ~(1 << port); 608289947Szbb sc->vgroup[port] = E6000SW_PORT_NO_VGROUP; 609289947Szbb } 610289947Szbb} 611289947Szbb 612289947Szbbstatic __inline void 613289947Szbbe6000sw_port_assign_vgroup(e6000sw_softc_t *sc, int port, int fid, int vgroup, 614289947Szbb int members) 615289947Szbb{ 616289947Szbb uint32_t reg; 617289947Szbb 618289947Szbb reg = e6000sw_readreg(sc, REG_PORT(port), 619289947Szbb PORT_VLAN_MAP); 620289947Szbb reg &= ~PORT_VLAN_MAP_TABLE_MASK; 621289947Szbb reg &= ~PORT_VLAN_MAP_FID_MASK; 622289947Szbb reg |= members & ~(1 << port); 623289947Szbb reg |= (fid << PORT_VLAN_MAP_FID) & PORT_VLAN_MAP_FID_MASK; 624289947Szbb e6000sw_writereg(sc, REG_PORT(port), PORT_VLAN_MAP, 625289947Szbb reg); 626289947Szbb sc->vgroup[port] = vgroup; 627289947Szbb} 628289947Szbb 629289947Szbbstatic int 630289947Szbbe6000sw_setvgroup(device_t dev, etherswitch_vlangroup_t *vg) 631289947Szbb{ 632289947Szbb e6000sw_softc_t *sc; 633289947Szbb int port, fid; 634289947Szbb 635289947Szbb sc = device_get_softc(dev); 636289947Szbb E6000SW_LOCK_ASSERT(sc, SA_XLOCKED); 637289947Szbb 638289947Szbb if (vg->es_vlangroup >= E6000SW_NUM_VGROUPS) 639289947Szbb return (EINVAL); 640289947Szbb if (vg->es_member_ports != vg->es_untagged_ports) { 641289947Szbb device_printf(dev, "Tagged ports not supported.\n"); 642289947Szbb return (EINVAL); 643289947Szbb } 644289947Szbb 645289947Szbb vg->es_untagged_ports &= PORT_VLAN_MAP_TABLE_MASK; 646289947Szbb fid = vg->es_vlangroup + 1; 647289947Szbb for (port = 0; port < E6000SW_NUM_PORTS; port++) { 648289947Szbb if ((sc->members[vg->es_vlangroup] & (1 << port)) || 649289947Szbb (vg->es_untagged_ports & (1 << port))) 650289947Szbb e6000sw_flush_port(sc, port); 651289947Szbb if (vg->es_untagged_ports & (1 << port)) 652289947Szbb e6000sw_port_assign_vgroup(sc, port, fid, 653289947Szbb vg->es_vlangroup, vg->es_untagged_ports); 654289947Szbb } 655289947Szbb sc->vid[vg->es_vlangroup] = vg->es_vid; 656289947Szbb sc->members[vg->es_vlangroup] = vg->es_untagged_ports; 657289947Szbb 658289947Szbb return (0); 659289947Szbb} 660289947Szbb 661289947Szbbstatic int 662289947Szbbe6000sw_getvgroup(device_t dev, etherswitch_vlangroup_t *vg) 663289947Szbb{ 664289947Szbb e6000sw_softc_t *sc; 665289947Szbb 666289947Szbb sc = device_get_softc(dev); 667289947Szbb E6000SW_LOCK_ASSERT(sc, SA_XLOCKED); 668289947Szbb 669289947Szbb if (vg->es_vlangroup >= E6000SW_NUM_VGROUPS) 670289947Szbb return (EINVAL); 671289947Szbb vg->es_untagged_ports = vg->es_member_ports = 672289947Szbb sc->members[vg->es_vlangroup]; 673289947Szbb vg->es_vid = ETHERSWITCH_VID_VALID; 674289947Szbb 675289947Szbb return (0); 676289947Szbb} 677289947Szbb 678289947Szbbstatic __inline struct mii_data* 679289947Szbbe6000sw_miiforphy(e6000sw_softc_t *sc, unsigned int phy) 680289947Szbb{ 681289947Szbb 682289947Szbb if (phy >= E6000SW_NUM_PHYS) 683289947Szbb return (NULL); 684289947Szbb 685289947Szbb return (device_get_softc(sc->miibus[phy])); 686289947Szbb} 687289947Szbb 688289947Szbbstatic int 689289947Szbbe6000sw_ifmedia_upd(struct ifnet *ifp) 690289947Szbb{ 691289947Szbb e6000sw_softc_t *sc; 692289947Szbb struct mii_data *mii; 693289947Szbb 694289947Szbb sc = ifp->if_softc; 695289947Szbb mii = e6000sw_miiforphy(sc, ifp->if_dunit); 696289947Szbb if (mii == NULL) 697289947Szbb return (ENXIO); 698289947Szbb mii_mediachg(mii); 699289947Szbb 700289947Szbb return (0); 701289947Szbb} 702289947Szbb 703289947Szbbstatic void 704289947Szbbe6000sw_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) 705289947Szbb{ 706289947Szbb e6000sw_softc_t *sc; 707289947Szbb struct mii_data *mii; 708289947Szbb 709289947Szbb sc = ifp->if_softc; 710289947Szbb mii = e6000sw_miiforphy(sc, ifp->if_dunit); 711289947Szbb 712289947Szbb if (mii == NULL) 713289947Szbb return; 714289947Szbb 715289947Szbb mii_pollstat(mii); 716289947Szbb ifmr->ifm_active = mii->mii_media_active; 717289947Szbb ifmr->ifm_status = mii->mii_media_status; 718289947Szbb} 719289947Szbb 720289947Szbbstatic __inline uint32_t 721289947Szbbe6000sw_readreg(e6000sw_softc_t *sc, int addr, int reg) 722289947Szbb{ 723289947Szbb 724289947Szbb E6000SW_LOCK_ASSERT(sc, SA_XLOCKED); 725289947Szbb 726289947Szbb return (MDIO_READREG(device_get_parent(sc->dev), addr, reg)); 727289947Szbb} 728289947Szbb 729289947Szbbstatic __inline void 730289947Szbbe6000sw_writereg(e6000sw_softc_t *sc, int addr, int reg, int val) 731289947Szbb{ 732289947Szbb 733289947Szbb E6000SW_LOCK_ASSERT(sc, SA_XLOCKED); 734289947Szbb 735289947Szbb MDIO_WRITEREG(device_get_parent(sc->dev), addr, reg, val); 736289947Szbb} 737289947Szbb 738289947Szbbstatic __inline int 739289947Szbbe6000sw_cpuport(e6000sw_softc_t *sc, int port) 740289947Szbb{ 741289947Szbb 742289947Szbb return (sc->cpuports_mask & (1 << port)); 743289947Szbb} 744289947Szbb 745289947Szbbstatic __inline int 746289947Szbbe6000sw_set_pvid(e6000sw_softc_t *sc, int port, int pvid) 747289947Szbb{ 748289947Szbb 749289947Szbb e6000sw_writereg(sc, REG_PORT(port), PORT_VID, pvid & 750289947Szbb PORT_VID_DEF_VID_MASK); 751289947Szbb 752289947Szbb return (0); 753289947Szbb} 754289947Szbb 755289947Szbbstatic __inline int 756289947Szbbe6000sw_get_pvid(e6000sw_softc_t *sc, int port, int *pvid) 757289947Szbb{ 758289947Szbb 759289947Szbb if (pvid == NULL) 760289947Szbb return (ENXIO); 761289947Szbb 762289947Szbb *pvid = e6000sw_readreg(sc, REG_PORT(port), PORT_VID) & 763289947Szbb PORT_VID_DEF_VID_MASK; 764289947Szbb 765289947Szbb return (0); 766289947Szbb} 767289947Szbb 768289947Szbbstatic void 769289947Szbbe6000sw_tick (void *arg) 770289947Szbb{ 771289947Szbb e6000sw_softc_t *sc; 772289947Szbb struct mii_softc *miisc; 773289947Szbb int i; 774289947Szbb 775289947Szbb sc = arg; 776289947Szbb 777289947Szbb E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED); 778289947Szbb for (;;) { 779289947Szbb E6000SW_LOCK(sc); 780289947Szbb for (i = 0; i < E6000SW_NUM_PHYS; i++) { 781289947Szbb mii_tick(sc->mii[i]); 782289947Szbb LIST_FOREACH(miisc, &sc->mii[i]->mii_phys, mii_list) { 783289947Szbb if (IFM_INST(sc->mii[i]->mii_media.ifm_cur->ifm_media) 784289947Szbb != miisc->mii_inst) 785289947Szbb continue; 786289947Szbb mii_phy_update(miisc, MII_POLLSTAT); 787289947Szbb } 788289947Szbb } 789289947Szbb E6000SW_UNLOCK(sc); 790289947Szbb pause("e6000sw tick", 1000); 791289947Szbb } 792289947Szbb} 793289947Szbb 794289947Szbbstatic void 795289947Szbbe6000sw_setup(device_t dev, e6000sw_softc_t *sc) 796289947Szbb{ 797289947Szbb uint16_t atu_ctrl, atu_age; 798289947Szbb 799289947Szbb /* Set aging time */ 800289947Szbb e6000sw_writereg(sc, REG_GLOBAL, ATU_CONTROL, 801289947Szbb (E6000SW_DEFAULT_AGETIME << ATU_CONTROL_AGETIME) | 802289947Szbb (1 << ATU_CONTROL_LEARN2ALL)); 803289947Szbb 804289947Szbb /* Send all with specific mac address to cpu port */ 805289947Szbb e6000sw_writereg(sc, REG_GLOBAL2, MGMT_EN_2x, MGMT_EN_ALL); 806289947Szbb e6000sw_writereg(sc, REG_GLOBAL2, MGMT_EN_0x, MGMT_EN_ALL); 807289947Szbb 808298955Spfg /* Disable Remote Management */ 809289947Szbb e6000sw_writereg(sc, REG_GLOBAL, SWITCH_GLOBAL_CONTROL2, 0); 810289947Szbb 811289947Szbb /* Disable loopback filter and flow control messages */ 812289947Szbb e6000sw_writereg(sc, REG_GLOBAL2, SWITCH_MGMT, 813289947Szbb SWITCH_MGMT_PRI_MASK | 814289947Szbb (1 << SWITCH_MGMT_RSVD2CPU) | 815289947Szbb SWITCH_MGMT_FC_PRI_MASK | 816289947Szbb (1 << SWITCH_MGMT_FORCEFLOW)); 817289947Szbb 818289947Szbb /* Set VLAN configuration */ 819289947Szbb e6000sw_port_vlan_conf(sc); 820289947Szbb 821289947Szbb e6000sw_atu_flush(dev, sc, NO_OPERATION); 822289947Szbb e6000sw_atu_mac_table(dev, sc, NULL, NO_OPERATION); 823289947Szbb e6000sw_set_atustat(dev, sc, 0, COUNT_ALL); 824289947Szbb 825289947Szbb /* Set ATU AgeTime to 15 seconds */ 826289947Szbb atu_age = 1; 827289947Szbb 828289947Szbb atu_ctrl = e6000sw_readreg(sc, REG_GLOBAL, ATU_CONTROL); 829289947Szbb 830289947Szbb /* Set new AgeTime field */ 831289947Szbb atu_ctrl &= ~ATU_CONTROL_AGETIME_MASK; 832289947Szbb e6000sw_writereg(sc, REG_GLOBAL, ATU_CONTROL, atu_ctrl | 833289947Szbb (atu_age << ATU_CONTROL_AGETIME)); 834289947Szbb} 835289947Szbb 836289947Szbbstatic void 837289947Szbbe6000sw_port_vlan_conf(e6000sw_softc_t *sc) 838289947Szbb{ 839289947Szbb int port, ret; 840289947Szbb etherswitch_vlangroup_t vg; 841289947Szbb device_t dev; 842289947Szbb 843289947Szbb dev = sc->dev; 844289947Szbb /* Disable all ports */ 845289947Szbb for (port = 0; port < E6000SW_NUM_PORTS; port++) { 846289947Szbb ret = e6000sw_readreg(sc, REG_PORT(port), PORT_CONTROL); 847289947Szbb e6000sw_writereg(sc, REG_PORT(port), PORT_CONTROL, 848289947Szbb (ret & ~PORT_CONTROL_ENABLE)); 849289947Szbb } 850289947Szbb 851289947Szbb /* Set port priority */ 852289947Szbb for (port = 0; port < E6000SW_NUM_PORTS; port++) { 853289947Szbb ret = e6000sw_readreg(sc, REG_PORT(port), PORT_VID); 854289947Szbb ret &= ~PORT_VID_PRIORITY_MASK; 855289947Szbb e6000sw_writereg(sc, REG_PORT(port), PORT_VID, ret); 856289947Szbb } 857289947Szbb 858289947Szbb vg.es_vlangroup = 0; 859289947Szbb vg.es_vid = 0; 860289947Szbb vg.es_member_ports = vg.es_untagged_ports = E6000SW_DEF_VLANGROUP0; 861289947Szbb e6000sw_setvgroup(dev, &vg); 862289947Szbb vg.es_vlangroup = 1; 863289947Szbb vg.es_vid = 1; 864289947Szbb vg.es_member_ports = vg.es_untagged_ports = E6000SW_DEF_VLANGROUP1; 865289947Szbb e6000sw_setvgroup(dev, &vg); 866289947Szbb 867289947Szbb device_printf(dev, "Default vlangroups set.\n"); 868289947Szbb /* Set VID map */ 869289947Szbb for (port = 0; port < E6000SW_NUM_PORTS; port++) { 870289947Szbb ret = e6000sw_readreg(sc, REG_PORT(port), PORT_VID); 871289947Szbb ret &= ~PORT_VID_DEF_VID_MASK; 872289947Szbb ret |= (port + 1); 873289947Szbb e6000sw_writereg(sc, REG_PORT(port), PORT_VID, ret); 874289947Szbb } 875289947Szbb 876289947Szbb /* Enable all ports */ 877289947Szbb for (port = 0; port < E6000SW_NUM_PORTS; port++) { 878289947Szbb ret = e6000sw_readreg(sc, REG_PORT(port), PORT_CONTROL); 879289947Szbb e6000sw_writereg(sc, REG_PORT(port), PORT_CONTROL, (ret | 880289947Szbb PORT_CONTROL_ENABLE)); 881289947Szbb } 882289947Szbb} 883289947Szbb 884289947Szbbstatic void 885289947Szbbe6000sw_set_atustat(device_t dev, e6000sw_softc_t *sc, int bin, int flag) 886289947Szbb{ 887289947Szbb uint16_t ret; 888289947Szbb 889289947Szbb ret = e6000sw_readreg(sc, REG_GLOBAL2, ATU_STATS); 890289947Szbb e6000sw_writereg(sc, REG_GLOBAL2, ATU_STATS, (bin << ATU_STATS_BIN ) | 891289947Szbb (flag << ATU_STATS_FLAG)); 892289947Szbb} 893289947Szbb 894289947Szbbstatic int 895289947Szbbe6000sw_atu_mac_table(device_t dev, e6000sw_softc_t *sc, struct atu_opt *atu, 896289947Szbb int flag) 897289947Szbb{ 898289947Szbb uint16_t ret_opt; 899289947Szbb uint16_t ret_data; 900289947Szbb int retries; 901289947Szbb 902289947Szbb if (flag == NO_OPERATION) 903289947Szbb return (0); 904289947Szbb else if ((flag & (LOAD_FROM_FIB | PURGE_FROM_FIB | GET_NEXT_IN_FIB | 905289947Szbb GET_VIOLATION_DATA | CLEAR_VIOLATION_DATA)) == 0) { 906289947Szbb device_printf(dev, "Wrong Opcode for ATU operation\n"); 907289947Szbb return (EINVAL); 908289947Szbb } 909289947Szbb 910289947Szbb ret_opt = e6000sw_readreg(sc, REG_GLOBAL, ATU_OPERATION); 911289947Szbb 912289947Szbb if (ret_opt & ATU_UNIT_BUSY) { 913289947Szbb device_printf(dev, "ATU unit is busy, cannot access" 914289947Szbb "register\n"); 915289947Szbb return (EBUSY); 916289947Szbb } else { 917289947Szbb if(flag & LOAD_FROM_FIB) { 918289947Szbb ret_data = e6000sw_readreg(sc, REG_GLOBAL, ATU_DATA); 919289947Szbb e6000sw_writereg(sc, REG_GLOBAL2, ATU_DATA, (ret_data & 920289947Szbb ~ENTRY_STATE)); 921289947Szbb } 922289947Szbb e6000sw_writereg(sc, REG_GLOBAL, ATU_MAC_ADDR01, atu->mac_01); 923289947Szbb e6000sw_writereg(sc, REG_GLOBAL, ATU_MAC_ADDR23, atu->mac_23); 924289947Szbb e6000sw_writereg(sc, REG_GLOBAL, ATU_MAC_ADDR45, atu->mac_45); 925289947Szbb e6000sw_writereg(sc, REG_GLOBAL, ATU_FID, atu->fid); 926289947Szbb 927289947Szbb e6000sw_writereg(sc, REG_GLOBAL, ATU_OPERATION, (ret_opt | 928289947Szbb ATU_UNIT_BUSY | flag)); 929289947Szbb 930289947Szbb retries = E6000SW_RETRIES; 931289947Szbb while (--retries & (e6000sw_readreg(sc, REG_GLOBAL, 932289947Szbb ATU_OPERATION) & ATU_UNIT_BUSY)) 933289947Szbb DELAY(1); 934289947Szbb 935289947Szbb if (retries == 0) 936289947Szbb device_printf(dev, "Timeout while flushing\n"); 937289947Szbb else if (flag & GET_NEXT_IN_FIB) { 938289947Szbb atu->mac_01 = e6000sw_readreg(sc, REG_GLOBAL, 939289947Szbb ATU_MAC_ADDR01); 940289947Szbb atu->mac_23 = e6000sw_readreg(sc, REG_GLOBAL, 941289947Szbb ATU_MAC_ADDR23); 942289947Szbb atu->mac_45 = e6000sw_readreg(sc, REG_GLOBAL, 943289947Szbb ATU_MAC_ADDR45); 944289947Szbb } 945289947Szbb } 946289947Szbb 947289947Szbb return (0); 948289947Szbb} 949289947Szbb 950289947Szbbstatic int 951289947Szbbe6000sw_atu_flush(device_t dev, e6000sw_softc_t *sc, int flag) 952289947Szbb{ 953289947Szbb uint16_t ret; 954289947Szbb int retries; 955289947Szbb 956289947Szbb if (flag == NO_OPERATION) 957289947Szbb return (0); 958289947Szbb 959289947Szbb ret = e6000sw_readreg(sc, REG_GLOBAL, ATU_OPERATION); 960289947Szbb if (ret & ATU_UNIT_BUSY) { 961289947Szbb device_printf(dev, "Atu unit is busy, cannot flush\n"); 962289947Szbb return (EBUSY); 963289947Szbb } else { 964289947Szbb e6000sw_writereg(sc, REG_GLOBAL, ATU_OPERATION, (ret | 965289947Szbb ATU_UNIT_BUSY | flag)); 966289947Szbb retries = E6000SW_RETRIES; 967289947Szbb while (--retries & (e6000sw_readreg(sc, REG_GLOBAL, 968289947Szbb ATU_OPERATION) & ATU_UNIT_BUSY)) 969289947Szbb DELAY(1); 970289947Szbb 971289947Szbb if (retries == 0) 972289947Szbb device_printf(dev, "Timeout while flushing\n"); 973289947Szbb } 974289947Szbb 975289947Szbb return (0); 976289947Szbb} 977