Example #1
0
/*
 * 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 */
}
Example #2
0
/*
 * 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));
}
Example #3
0
/*
 * 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));
}
Example #4
0
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]);
}
Example #5
0
/*
 * 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);
}
Example #6
0
/*
 * 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);
}
Example #7
0
/*
 * 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);
		}
	}
}
Example #8
0
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);

}
Example #9
0
/*
 * 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);
}
Example #10
0
/*
 * 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);
}
Example #11
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);
}
Example #12
0
/*
 * 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);
}
Example #13
0
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);
}
Example #14
0
/*
 * 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);
}