/* * Basic low-level function to powerdown the PHY, if supported * If powerdown support is compiled out, this function does nothing. */ static void bge_phy_powerdown(bge_t *bgep) { BGE_TRACE(("bge_phy_powerdown")); #if BGE_COPPER_IDLEOFF bge_mii_put16(bgep, MII_CONTROL, MII_CONTROL_PWRDN); #endif /* BGE_COPPER_IDLEOFF */ }
/* * Reset and power off the physical layer. * * Another RESET should get it back to working, but it may take a few * seconds it may take a few moments to return to normal operation ... */ int bge_phys_idle(bge_t *bgep) { BGE_TRACE(("bge_phys_idle($%p)", (void *)bgep)); ASSERT(mutex_owned(bgep->genlock)); return ((*bgep->physops->phys_restart)(bgep, B_TRUE)); }
/* * Synchronise the PHYSICAL layer's speed/duplex/autonegotiation capabilities * and advertisements with the required settings as specified by the various * param_* variables that can be poked via the NDD interface. * * We always reset the PHYSICAL layer and reprogram *all* relevant registers. * This is expected to cause the link to go down, and then back up again once * the link is stable and autonegotiation (if enabled) is complete. We should * get a link state change interrupt somewhere along the way ... * * NOTE: <genlock> must already be held by the caller */ int bge_phys_update(bge_t *bgep) { BGE_TRACE(("bge_phys_update($%p)", (void *)bgep)); ASSERT(mutex_owned(bgep->genlock)); return ((*bgep->physops->phys_update)(bgep)); }
void bge_fini_kstats(bge_t *bgep) { int i; BGE_TRACE(("bge_fini_kstats($%p)", (void *)bgep)); for (i = BGE_KSTAT_COUNT; --i >= 0; ) if (bgep->bge_kstats[i] != NULL) kstat_delete(bgep->bge_kstats[i]); }
/* * Reset the physical layer */ void bge_phys_reset(bge_t *bgep) { BGE_TRACE(("bge_phys_reset($%p)", (void *)bgep)); mutex_enter(bgep->genlock); if ((*bgep->physops->phys_restart)(bgep, B_FALSE) != DDI_SUCCESS) ddi_fm_service_impact(bgep->devinfo, DDI_SERVICE_UNAFFECTED); if (bge_check_acc_handle(bgep, bgep->io_handle) != DDI_FM_OK) ddi_fm_service_impact(bgep->devinfo, DDI_SERVICE_UNAFFECTED); mutex_exit(bgep->genlock); }
/* * Reinitialise the SerDes interface. Note that it normally powers * up in the disabled state, so we need to explicitly activate it. */ static int bge_restart_serdes(bge_t *bgep, boolean_t powerdown) { uint32_t macmode; BGE_TRACE(("bge_restart_serdes($%p, %d)", (void *)bgep, powerdown)); ASSERT(mutex_owned(bgep->genlock)); /* * Ensure that the main Ethernet MAC mode register is programmed * appropriately for the SerDes interface ... */ macmode = bge_reg_get32(bgep, ETHERNET_MAC_MODE_REG); if (DEVICE_5714_SERIES_CHIPSETS(bgep)) { macmode |= ETHERNET_MODE_LINK_POLARITY; macmode &= ~ETHERNET_MODE_PORTMODE_MASK; macmode |= ETHERNET_MODE_PORTMODE_GMII; } else { macmode &= ~ETHERNET_MODE_LINK_POLARITY; macmode &= ~ETHERNET_MODE_PORTMODE_MASK; macmode |= ETHERNET_MODE_PORTMODE_TBI; } bge_reg_put32(bgep, ETHERNET_MAC_MODE_REG, macmode); /* * Ensure that loopback is OFF and comma detection is enabled. Then * disable the SerDes output (the first time through, it may/will * already be disabled). If we're shutting down, leave it disabled. */ bge_reg_clr32(bgep, SERDES_CONTROL_REG, SERDES_CONTROL_TBI_LOOPBACK); bge_reg_set32(bgep, SERDES_CONTROL_REG, SERDES_CONTROL_COMMA_DETECT); bge_reg_set32(bgep, SERDES_CONTROL_REG, SERDES_CONTROL_TX_DISABLE); if (powerdown) return (DDI_SUCCESS); /* * Otherwise, pause, (re-)enable the SerDes output, and send * all-zero config words in order to force autoneg restart. * Invalidate the saved "link partners received configs", as * we're starting over ... */ drv_usecwait(10000); bge_reg_clr32(bgep, SERDES_CONTROL_REG, SERDES_CONTROL_TX_DISABLE); bge_reg_put32(bgep, TX_1000BASEX_AUTONEG_REG, 0); bge_reg_set32(bgep, ETHERNET_MAC_MODE_REG, ETHERNET_MODE_SEND_CFGS); drv_usecwait(10); bge_reg_clr32(bgep, ETHERNET_MAC_MODE_REG, ETHERNET_MODE_SEND_CFGS); bgep->serdes_lpadv = AUTONEG_CODE_FAULT_ANEG_ERR; bgep->serdes_status = ~0U; return (DDI_SUCCESS); }
/* * Basic low-level function to probe for a PHY * * Returns TRUE if the PHY responds with valid data, FALSE otherwise */ static boolean_t bge_phy_probe(bge_t *bgep) { uint16_t miicfg; uint32_t nicsig, niccfg; BGE_TRACE(("bge_phy_probe($%p)", (void *)bgep)); ASSERT(mutex_owned(bgep->genlock)); nicsig = bge_nic_read32(bgep, BGE_NIC_DATA_SIG_ADDR); if (nicsig == BGE_NIC_DATA_SIG) { niccfg = bge_nic_read32(bgep, BGE_NIC_DATA_NIC_CFG_ADDR); switch (niccfg & BGE_NIC_CFG_PHY_TYPE_MASK) { default: case BGE_NIC_CFG_PHY_TYPE_COPPER: return (B_TRUE); case BGE_NIC_CFG_PHY_TYPE_FIBER: return (B_FALSE); } } else { /* * Read the MII_STATUS register twice, in * order to clear any sticky bits (but they should * have been cleared by the RESET, I think). */ miicfg = bge_mii_get16(bgep, MII_STATUS); miicfg = bge_mii_get16(bgep, MII_STATUS); BGE_DEBUG(("bge_phy_probe: status 0x%x", miicfg)); /* * Now check the value read; it should have at least one bit set * (for the device capabilities) and at least one clear (one of * the error bits). So if we see all 0s or all 1s, there's a * problem. In particular, bge_mii_get16() returns all 1s if * communications fails ... */ switch (miicfg) { case 0x0000: case 0xffff: return (B_FALSE); default : return (B_TRUE); } } }
void bge_init_kstats(bge_t *bgep, int instance) { kstat_t *ksp; BGE_TRACE(("bge_init_kstats($%p, %d)", (void *)bgep, instance)); if (bgep->chipid.statistic_type == BGE_STAT_BLK) { DMA_ZERO(bgep->statistics); bgep->bge_kstats[BGE_KSTAT_RAW] = ksp = kstat_create(BGE_DRIVER_NAME, instance, "raw_statistics", "net", KSTAT_TYPE_RAW, sizeof (bge_statistics_t), KSTAT_FLAG_VIRTUAL); if (ksp != NULL) { ksp->ks_data = DMA_VPTR(bgep->statistics); kstat_install(ksp); } bgep->bge_kstats[BGE_KSTAT_STATS] = bge_setup_named_kstat(bgep, instance, "statistics", bge_statistics, sizeof (bge_statistics), bge_statistics_update); } else { bgep->bge_kstats[BGE_KSTAT_STATS] = bge_setup_named_kstat(bgep, instance, "statistics", bge_stat_val, sizeof (bge_stat_val), bge_statistics_update); } bgep->bge_kstats[BGE_KSTAT_CHIPID] = bge_setup_named_kstat(bgep, instance, "chipid", bge_chipid, sizeof (bge_chipid), bge_chipid_update); bgep->bge_kstats[BGE_KSTAT_DRIVER] = bge_setup_named_kstat(bgep, instance, "driverinfo", bge_driverinfo, sizeof (bge_driverinfo), bge_driverinfo_update); if (bgep->chipid.flags & CHIP_FLAG_SERDES) bgep->bge_kstats[BGE_KSTAT_PHYS] = bge_setup_named_kstat(bgep, instance, "serdes", bge_serdes, sizeof (bge_serdes), bge_serdes_update); else bgep->bge_kstats[BGE_KSTAT_PHYS] = bge_setup_named_kstat(bgep, instance, "phydata", bge_phydata, sizeof (bge_phydata), bge_phydata_update); }
/* * Read the link status and determine whether anything's changed ... * * This routine should be called whenever the chip flags a change * in the hardware link state. * * This routine returns B_FALSE if the link state has not changed, * returns B_TRUE when the change to the new state should be accepted. * In such a case, the param_* variables give the new hardware state, * which the caller should use to update link_state etc. * * The caller must already hold <genlock> */ boolean_t bge_phys_check(bge_t *bgep) { int32_t orig_state; boolean_t recheck; BGE_TRACE(("bge_phys_check($%p)", (void *)bgep)); ASSERT(mutex_owned(bgep->genlock)); orig_state = bgep->link_state; recheck = orig_state == LINK_STATE_UNKNOWN; recheck = (*bgep->physops->phys_check)(bgep, recheck); if (!recheck) return (B_FALSE); return (B_TRUE); }
/* * Here we have to determine which media we're using (copper or serdes). * Once that's done, we can initialise the physical layer appropriately. */ int bge_phys_init(bge_t *bgep) { BGE_TRACE(("bge_phys_init($%p)", (void *)bgep)); mutex_enter(bgep->genlock); /* * Probe for the (internal) PHY. If it's not there, we'll assume * that this is a 5703/4S, with a SerDes interface rather than * a PHY. BCM5714S/BCM5715S are not supported.It are based on * BCM800x PHY. */ bgep->phy_mii_addr = 1; if (DEVICE_5717_SERIES_CHIPSETS(bgep)) { int regval = bge_reg_get32(bgep, CPMU_STATUS_REG); if (regval & CPMU_STATUS_FUN_NUM) bgep->phy_mii_addr += 1; regval = bge_reg_get32(bgep, SGMII_STATUS_REG); if (regval & MEDIA_SELECTION_MODE) bgep->phy_mii_addr += 7; } if (bge_phy_probe(bgep)) { bgep->chipid.flags &= ~CHIP_FLAG_SERDES; bgep->physops = &copper_ops; } else { bgep->chipid.flags |= CHIP_FLAG_SERDES; bgep->physops = &serdes_ops; } if ((*bgep->physops->phys_restart)(bgep, B_FALSE) != DDI_SUCCESS) { mutex_exit(bgep->genlock); return (EIO); } if (bge_check_acc_handle(bgep, bgep->io_handle) != DDI_FM_OK) { mutex_exit(bgep->genlock); return (EIO); } mutex_exit(bgep->genlock); return (0); }
/* * Basic low-level function to reset the PHY. * Doesn't incorporate any special-case workarounds. * * Returns TRUE on success, FALSE if the RESET bit doesn't clear */ static boolean_t bge_phy_reset(bge_t *bgep) { uint16_t control; uint_t count; BGE_TRACE(("bge_phy_reset($%p)", (void *)bgep)); ASSERT(mutex_owned(bgep->genlock)); if (DEVICE_5906_SERIES_CHIPSETS(bgep)) { drv_usecwait(40); /* put PHY into ready state */ bge_reg_clr32(bgep, MISC_CONFIG_REG, MISC_CONFIG_EPHY_IDDQ); (void) bge_reg_get32(bgep, MISC_CONFIG_REG); /* flush */ drv_usecwait(40); } /* * Set the PHY RESET bit, then wait up to 5 ms for it to self-clear */ bge_mii_put16(bgep, MII_CONTROL, MII_CONTROL_RESET); for (count = 0; ++count < 1000; ) { drv_usecwait(5); control = bge_mii_get16(bgep, MII_CONTROL); if (BIC(control, MII_CONTROL_RESET)) return (B_TRUE); } if (DEVICE_5906_SERIES_CHIPSETS(bgep)) (void) bge_adj_volt_5906(bgep); BGE_DEBUG(("bge_phy_reset: FAILED, control now 0x%x", control)); return (B_FALSE); }
/* * Synchronise the (copper) PHY's speed/duplex/autonegotiation capabilities * and advertisements with the required settings as specified by the various * param_* variables that can be poked via the NDD interface. * * We always reset the PHY and reprogram *all* the relevant registers, * not just those changed. This should cause the link to go down, and then * back up again once the link is stable and autonegotiation (if enabled) * is complete. We should get a link state change interrupt somewhere along * the way ... * * NOTE: <genlock> must already be held by the caller */ static int bge_update_copper(bge_t *bgep) { boolean_t adv_autoneg; boolean_t adv_pause; boolean_t adv_asym_pause; boolean_t adv_1000fdx; boolean_t adv_1000hdx; boolean_t adv_100fdx; boolean_t adv_100hdx; boolean_t adv_10fdx; boolean_t adv_10hdx; uint16_t control; uint16_t gigctrl; uint16_t auxctrl; uint16_t anar; BGE_TRACE(("bge_update_copper($%p)", (void *)bgep)); ASSERT(mutex_owned(bgep->genlock)); BGE_DEBUG(("bge_update_copper: autoneg %d " "pause %d asym_pause %d " "1000fdx %d 1000hdx %d " "100fdx %d 100hdx %d " "10fdx %d 10hdx %d ", bgep->param_adv_autoneg, bgep->param_adv_pause, bgep->param_adv_asym_pause, bgep->param_adv_1000fdx, bgep->param_adv_1000hdx, bgep->param_adv_100fdx, bgep->param_adv_100hdx, bgep->param_adv_10fdx, bgep->param_adv_10hdx)); control = gigctrl = auxctrl = anar = 0; /* * PHY settings are normally based on the param_* variables, * but if any loopback mode is in effect, that takes precedence. * * BGE supports MAC-internal loopback, PHY-internal loopback, * and External loopback at a variety of speeds (with a special * cable). In all cases, autoneg is turned OFF, full-duplex * is turned ON, and the speed/mastership is forced. */ switch (bgep->param_loop_mode) { case BGE_LOOP_NONE: default: adv_autoneg = bgep->param_adv_autoneg; adv_pause = bgep->param_adv_pause; adv_asym_pause = bgep->param_adv_asym_pause; adv_1000fdx = bgep->param_adv_1000fdx; adv_1000hdx = bgep->param_adv_1000hdx; adv_100fdx = bgep->param_adv_100fdx; adv_100hdx = bgep->param_adv_100hdx; adv_10fdx = bgep->param_adv_10fdx; adv_10hdx = bgep->param_adv_10hdx; break; case BGE_LOOP_EXTERNAL_1000: case BGE_LOOP_EXTERNAL_100: case BGE_LOOP_EXTERNAL_10: case BGE_LOOP_INTERNAL_PHY: case BGE_LOOP_INTERNAL_MAC: adv_autoneg = adv_pause = adv_asym_pause = B_FALSE; adv_1000fdx = adv_100fdx = adv_10fdx = B_FALSE; adv_1000hdx = adv_100hdx = adv_10hdx = B_FALSE; bgep->param_link_duplex = LINK_DUPLEX_FULL; switch (bgep->param_loop_mode) { case BGE_LOOP_EXTERNAL_1000: bgep->param_link_speed = 1000; adv_1000fdx = B_TRUE; auxctrl = MII_AUX_CTRL_NORM_EXT_LOOPBACK; gigctrl |= MII_MSCONTROL_MANUAL; gigctrl |= MII_MSCONTROL_MASTER; break; case BGE_LOOP_EXTERNAL_100: bgep->param_link_speed = 100; adv_100fdx = B_TRUE; auxctrl = MII_AUX_CTRL_NORM_EXT_LOOPBACK; break; case BGE_LOOP_EXTERNAL_10: bgep->param_link_speed = 10; adv_10fdx = B_TRUE; auxctrl = MII_AUX_CTRL_NORM_EXT_LOOPBACK; break; case BGE_LOOP_INTERNAL_PHY: bgep->param_link_speed = 1000; adv_1000fdx = B_TRUE; control = MII_CONTROL_LOOPBACK; break; case BGE_LOOP_INTERNAL_MAC: bgep->param_link_speed = 1000; adv_1000fdx = B_TRUE; break; } } BGE_DEBUG(("bge_update_copper: autoneg %d " "pause %d asym_pause %d " "1000fdx %d 1000hdx %d " "100fdx %d 100hdx %d " "10fdx %d 10hdx %d ", adv_autoneg, adv_pause, adv_asym_pause, adv_1000fdx, adv_1000hdx, adv_100fdx, adv_100hdx, adv_10fdx, adv_10hdx)); /* * We should have at least one technology capability set; * if not, we select a default of 1000Mb/s full-duplex */ if (!adv_1000fdx && !adv_100fdx && !adv_10fdx && !adv_1000hdx && !adv_100hdx && !adv_10hdx) adv_1000fdx = B_TRUE; /* * Now transform the adv_* variables into the proper settings * of the PHY registers ... * * If autonegotiation is (now) enabled, we want to trigger * a new autonegotiation cycle once the PHY has been * programmed with the capabilities to be advertised. */ if (adv_autoneg) control |= MII_CONTROL_ANE|MII_CONTROL_RSAN; if (adv_1000fdx) control |= MII_CONTROL_1GB|MII_CONTROL_FDUPLEX; else if (adv_1000hdx) control |= MII_CONTROL_1GB; else if (adv_100fdx) control |= MII_CONTROL_100MB|MII_CONTROL_FDUPLEX; else if (adv_100hdx) control |= MII_CONTROL_100MB; else if (adv_10fdx) control |= MII_CONTROL_FDUPLEX; else if (adv_10hdx) control |= 0; else { _NOTE(EMPTY); } /* Can't get here anyway ... */ if (adv_1000fdx) gigctrl |= MII_MSCONTROL_1000T_FD; if (adv_1000hdx) gigctrl |= MII_MSCONTROL_1000T; if (adv_100fdx) anar |= MII_ABILITY_100BASE_TX_FD; if (adv_100hdx) anar |= MII_ABILITY_100BASE_TX; if (adv_10fdx) anar |= MII_ABILITY_10BASE_T_FD; if (adv_10hdx) anar |= MII_ABILITY_10BASE_T; if (adv_pause) anar |= MII_ABILITY_PAUSE; if (adv_asym_pause) anar |= MII_ABILITY_ASMPAUSE; /* * Munge in any other fixed bits we require ... */ anar |= MII_AN_SELECTOR_8023; auxctrl |= MII_AUX_CTRL_NORM_TX_MODE; auxctrl |= MII_AUX_CTRL_NORMAL; /* * Restart the PHY and write the new values. Note the * time, so that we can say whether subsequent link state * changes can be attributed to our reprogramming the PHY */ if ((*bgep->physops->phys_restart)(bgep, B_FALSE) == DDI_FAILURE) return (DDI_FAILURE); bge_mii_put16(bgep, MII_AN_ADVERT, anar); if (auxctrl & MII_AUX_CTRL_NORM_EXT_LOOPBACK) bge_mii_put16(bgep, MII_AUX_CONTROL, auxctrl); bge_mii_put16(bgep, MII_MSCONTROL, gigctrl); bge_mii_put16(bgep, MII_CONTROL, control); BGE_DEBUG(("bge_update_copper: anar <- 0x%x", anar)); BGE_DEBUG(("bge_update_copper: auxctrl <- 0x%x", auxctrl)); BGE_DEBUG(("bge_update_copper: gigctrl <- 0x%x", gigctrl)); BGE_DEBUG(("bge_update_copper: control <- 0x%x", control)); #if BGE_COPPER_WIRESPEED /* * Enable the 'wire-speed' feature, if the chip supports it * and we haven't got (any) loopback mode selected. */ switch (bgep->chipid.device) { case DEVICE_ID_5700: case DEVICE_ID_5700x: case DEVICE_ID_5705C: case DEVICE_ID_5782: /* * These chips are known or assumed not to support it */ break; default: /* * All other Broadcom chips are expected to support it. */ if (bgep->param_loop_mode == BGE_LOOP_NONE) bge_mii_put16(bgep, MII_AUX_CONTROL, MII_AUX_CTRL_MISC_WRITE_ENABLE | MII_AUX_CTRL_MISC_WIRE_SPEED | MII_AUX_CTRL_MISC); break; } #endif /* BGE_COPPER_WIRESPEED */ return (DDI_SUCCESS); }
static int bge_restart_copper(bge_t *bgep, boolean_t powerdown) { uint16_t phy_status; boolean_t reset_ok; uint16_t extctrl, auxctrl; BGE_TRACE(("bge_restart_copper($%p, %d)", (void *)bgep, powerdown)); ASSERT(mutex_owned(bgep->genlock)); switch (MHCR_CHIP_ASIC_REV(bgep->chipid.asic_rev)) { default: /* * Shouldn't happen; it means we don't recognise this chip. * It's probably a new one, so we'll try our best anyway ... */ case MHCR_CHIP_ASIC_REV_5703: case MHCR_CHIP_ASIC_REV_5704: case MHCR_CHIP_ASIC_REV_5705: case MHCR_CHIP_ASIC_REV_5752: case MHCR_CHIP_ASIC_REV_5714: case MHCR_CHIP_ASIC_REV_5715: reset_ok = bge_phy_reset_and_check(bgep); break; case MHCR_CHIP_ASIC_REV_5906: case MHCR_CHIP_ASIC_REV_5700: case MHCR_CHIP_ASIC_REV_5701: case MHCR_CHIP_ASIC_REV_5723: case MHCR_CHIP_ASIC_REV_5721_5751: /* * Just a plain reset; the "check" code breaks these chips */ reset_ok = bge_phy_reset(bgep); if (!reset_ok) bge_fm_ereport(bgep, DDI_FM_DEVICE_NO_RESPONSE); break; } if (!reset_ok) { BGE_REPORT((bgep, "PHY failed to reset correctly")); return (DDI_FAILURE); } /* * Step 5: disable WOL (not required after RESET) * * Step 6: refer to errata */ switch (bgep->chipid.asic_rev) { default: break; case MHCR_CHIP_REV_5704_A0: bge_phy_tweak_gmii(bgep); break; } switch (MHCR_CHIP_ASIC_REV(bgep->chipid.asic_rev)) { case MHCR_CHIP_ASIC_REV_5705: case MHCR_CHIP_ASIC_REV_5721_5751: bge_phy_bit_err_fix(bgep); break; } if (!(bgep->chipid.flags & CHIP_FLAG_NO_JUMBO) && (bgep->chipid.default_mtu > BGE_DEFAULT_MTU)) { /* Set the GMII Fifo Elasticity to high latency */ extctrl = bge_mii_get16(bgep, 0x10); bge_mii_put16(bgep, 0x10, extctrl | 0x1); /* Allow reception of extended length packets */ bge_mii_put16(bgep, MII_AUX_CONTROL, 0x0007); auxctrl = bge_mii_get16(bgep, MII_AUX_CONTROL); auxctrl |= 0x4000; bge_mii_put16(bgep, MII_AUX_CONTROL, auxctrl); } /* * Step 7: read the MII_INTR_STATUS register twice, * in order to clear any sticky bits (but they should * have been cleared by the RESET, I think), and we're * not using PHY interrupts anyway. * * Step 8: enable the PHY to interrupt on link status * change (not required) * * Step 9: configure PHY LED Mode - not applicable? * * Step 10: read the MII_STATUS register twice, in * order to clear any sticky bits (but they should * have been cleared by the RESET, I think). */ phy_status = bge_mii_get16(bgep, MII_STATUS); phy_status = bge_mii_get16(bgep, MII_STATUS); BGE_DEBUG(("bge_restart_copper: status 0x%x", phy_status)); /* * Finally, shut down the PHY, if required */ if (powerdown) bge_phy_powerdown(bgep); return (DDI_SUCCESS); }
/* * Synchronise the SerDes speed/duplex/autonegotiation capabilities and * advertisements with the required settings as specified by the various * param_* variables that can be poked via the NDD interface. * * We always reinitalise the SerDes; this should cause the link to go down, * and then back up again once the link is stable and autonegotiation * (if enabled) is complete. We should get a link state change interrupt * somewhere along the way ... * * NOTE: SerDes only supports 1000FDX/HDX (with or without pause) so the * param_* variables relating to lower speeds are ignored. * * NOTE: <genlock> must already be held by the caller */ static int bge_update_serdes(bge_t *bgep) { boolean_t adv_autoneg; boolean_t adv_pause; boolean_t adv_asym_pause; boolean_t adv_1000fdx; boolean_t adv_1000hdx; uint32_t serdes; uint32_t advert; BGE_TRACE(("bge_update_serdes($%p)", (void *)bgep)); ASSERT(mutex_owned(bgep->genlock)); BGE_DEBUG(("bge_update_serdes: autoneg %d " "pause %d asym_pause %d " "1000fdx %d 1000hdx %d " "100fdx %d 100hdx %d " "10fdx %d 10hdx %d ", bgep->param_adv_autoneg, bgep->param_adv_pause, bgep->param_adv_asym_pause, bgep->param_adv_1000fdx, bgep->param_adv_1000hdx, bgep->param_adv_100fdx, bgep->param_adv_100hdx, bgep->param_adv_10fdx, bgep->param_adv_10hdx)); serdes = advert = 0; /* * SerDes settings are normally based on the param_* variables, * but if any loopback mode is in effect, that takes precedence. * * BGE supports MAC-internal loopback, PHY-internal loopback, * and External loopback at a variety of speeds (with a special * cable). In all cases, autoneg is turned OFF, full-duplex * is turned ON, and the speed/mastership is forced. * * Note: for the SerDes interface, "PHY" internal loopback is * interpreted as SerDes internal loopback, and all external * loopback modes are treated equivalently, as 1Gb/external. */ switch (bgep->param_loop_mode) { case BGE_LOOP_NONE: default: adv_autoneg = bgep->param_adv_autoneg; adv_pause = bgep->param_adv_pause; adv_asym_pause = bgep->param_adv_asym_pause; adv_1000fdx = bgep->param_adv_1000fdx; adv_1000hdx = bgep->param_adv_1000hdx; break; case BGE_LOOP_INTERNAL_PHY: serdes |= SERDES_CONTROL_TBI_LOOPBACK; /* FALLTHRU */ case BGE_LOOP_INTERNAL_MAC: case BGE_LOOP_EXTERNAL_1000: case BGE_LOOP_EXTERNAL_100: case BGE_LOOP_EXTERNAL_10: adv_autoneg = adv_pause = adv_asym_pause = B_FALSE; adv_1000fdx = B_TRUE; adv_1000hdx = B_FALSE; break; } BGE_DEBUG(("bge_update_serdes: autoneg %d " "pause %d asym_pause %d " "1000fdx %d 1000hdx %d ", adv_autoneg, adv_pause, adv_asym_pause, adv_1000fdx, adv_1000hdx)); /* * We should have at least one gigabit technology capability * set; if not, we select a default of 1000Mb/s full-duplex */ if (!adv_1000fdx && !adv_1000hdx) adv_1000fdx = B_TRUE; /* * Now transform the adv_* variables into the proper settings * of the SerDes registers ... * * If autonegotiation is (now) not enabled, pretend it's been * done and failed ... */ if (!adv_autoneg) advert |= AUTONEG_CODE_FAULT_ANEG_ERR; if (adv_1000fdx) { advert |= AUTONEG_CODE_FULL_DUPLEX; bgep->param_adv_1000fdx = adv_1000fdx; bgep->param_link_duplex = LINK_DUPLEX_FULL; bgep->param_link_speed = 1000; } if (adv_1000hdx) { advert |= AUTONEG_CODE_HALF_DUPLEX; bgep->param_adv_1000hdx = adv_1000hdx; bgep->param_link_duplex = LINK_DUPLEX_HALF; bgep->param_link_speed = 1000; } if (adv_pause) advert |= AUTONEG_CODE_PAUSE; if (adv_asym_pause) advert |= AUTONEG_CODE_ASYM_PAUSE; /* * Restart the SerDes and write the new values. Note the * time, so that we can say whether subsequent link state * changes can be attributed to our reprogramming the SerDes */ bgep->serdes_advert = advert; (void) bge_restart_serdes(bgep, B_FALSE); bge_reg_set32(bgep, SERDES_CONTROL_REG, serdes); BGE_DEBUG(("bge_update_serdes: serdes |= 0x%x, advert 0x%x", serdes, advert)); return (DDI_SUCCESS); }