/**
 * \brief Find a valid PHY Address ( from addrStart to 31 ).
 *
 * \param p_emac   Pointer to the EMAC instance. 
 * \param uc_phy_addr PHY address.
 * \param uc_start_addr Start address of the PHY to be searched. 
 *
 * \return 0xFF when no valid PHY address is found. 
 */
static uint8_t ethernet_phy_find_valid(Emac *p_emac, uint8_t uc_phy_addr,
		uint8_t addrStart)
{
	uint32_t ul_value = 0;
	uint8_t uc_rc;
	uint8_t uc_cnt;
	uint8_t uc_phy_address = uc_phy_addr;

	emac_enable_management(p_emac, true);

	/* Check the current PHY address */
	uc_rc = uc_phy_address;
	if (emac_phy_read(p_emac, uc_phy_addr, MII_PHYID1, &ul_value) != EMAC_OK) {
	}

	/* Find another one */
	if (ul_value != MII_OUI1) {
		uc_rc = 0xFF;
		for (uc_cnt = addrStart; uc_cnt <= ETH_PHY_MAX_ADDR; uc_cnt++) {
			uc_phy_address = (uc_phy_address + 1) & 0x1F;
			emac_phy_read(p_emac, uc_phy_address, MII_PHYID1, &ul_value);
			if (ul_value == MII_OUI1) {
				uc_rc = uc_phy_address;
				break;
			}
		}
	}

	emac_enable_management(p_emac, false);

	if (uc_rc != 0xFF) {
		emac_phy_read(p_emac, uc_phy_address, MII_OMSS, &ul_value);
	}
	return uc_rc;
}
/**
 * \brief Issue a SW reset to reset all registers of the PHY.
 *
 * \param p_emac   Pointer to the EMAC instance. 
 * \param uc_phy_addr PHY address.
 *
 * \Return EMAC_OK if successfully, EMAC_TIMEOUT if timeout.
 */
uint8_t ethernet_phy_reset(Emac *p_emac, uint8_t uc_phy_addr)
{
	uint32_t ul_bmcr = MII_RESET;
	uint8_t uc_phy_address = uc_phy_addr;
	uint32_t ul_timeout = ETH_PHY_TIMEOUT;
	uint8_t uc_rc = EMAC_TIMEOUT;

	emac_enable_management(p_emac, true);

	ul_bmcr = MII_RESET;
	emac_phy_write(p_emac, uc_phy_address, MII_BMCR, ul_bmcr);

	do {
		emac_phy_read(p_emac, uc_phy_address, MII_BMCR, &ul_bmcr);
		ul_timeout--;
	} while ((ul_bmcr & MII_RESET) && ul_timeout);

	emac_enable_management(p_emac, false);

	if (!ul_timeout) {
		uc_rc = EMAC_OK;
	}

	return (uc_rc);
}
void emac_phy_dump(struct net_device *dev)
{
	struct ocp_enet_private *fep = dev->priv;
	unsigned long i;
	uint data;

	printk(KERN_DEBUG " Prepare for Phy dump....\n");
	for (i = 0; i < 0x1A; i++) {
		data = emac_phy_read(dev, fep->mii_phy_addr, i);
		printk(KERN_DEBUG "Phy reg 0x%lx ==> %4x\n", i, data);
		if (i == 0x07)
			i = 0x0f;
	}
}
/**
 * \brief Read a Register of the PHY.
 *
 * \param p_emac   Pointer to the EMAC instance.
 * \param uc_phy_addr PHY address.
 * \param ul_phy_reg PHY Register to be read
 *
 * Return Register contents
 */
uint8_t ethernet_phy_read_register(Emac *p_emac, uint8_t uc_phy_addr, uint32_t ul_phy_reg,uint32_t *p_ul_reg_cont)
{
uint32_t ul_phy_register;
uint8_t uc_phy_address, uc_speed, uc_fd;
uint8_t uc_rc = EMAC_TIMEOUT;

emac_enable_management(p_emac, true);

uc_phy_address = uc_phy_addr;
ul_phy_register = ul_phy_reg;

uc_rc = emac_phy_read(p_emac, uc_phy_address, ul_phy_reg, p_ul_reg_cont);
if (uc_rc != EMAC_OK) {
/* Disable PHY management and start the EMAC transfer */
emac_enable_management(p_emac, false);
return uc_rc;
}
/* Start the EMAC transfers */
emac_enable_management(p_emac, false);
return uc_rc;
}
/**
 * \brief Issue an auto negotiation of the PHY.
 *
 * \param p_emac   Pointer to the EMAC instance. 
 * \param uc_phy_addr PHY address.
 *
 * Return EMAC_OK if successfully, EMAC_TIMEOUT if timeout (in which case we can keep calling this).
 * This has been refactored as a state machine, because we must not spend much time calling this when
 * no network cable is connected, otherwise it slows down printing.
 */
uint8_t ethernet_phy_auto_negotiate(Emac *p_emac, uint8_t uc_phy_addr)
{
	static uint8_t state = 0;
	static uint8_t ul_retry_count;
	static uint32_t ul_value;
	static uint32_t ul_phy_anar;

	switch(state)
	{
	case 0:
		{
			emac_enable_management(p_emac, true);

			/* Set up control register */
			uint8_t uc_rc = emac_phy_read(p_emac, uc_phy_addr, MII_BMCR, &ul_value);
			if (uc_rc != EMAC_OK)
			{
				emac_enable_management(p_emac, false);
				state = 0;
				return uc_rc;
			}

			++state;
		}
		break;

	case 1:
		{
			ul_value &= ~MII_AUTONEG; /* Remove auto-negotiation enable */
			ul_value &= ~(MII_LOOPBACK | MII_POWER_DOWN);
			ul_value |= MII_ISOLATE; /* Electrically isolate PHY */
			uint8_t uc_rc = emac_phy_write(p_emac, uc_phy_addr, MII_BMCR, ul_value);
			if (uc_rc != EMAC_OK)
			{
				emac_enable_management(p_emac, false);
				state = 0;
				return uc_rc;
			}

			++state;
		}
		break;

	case 2:
		{
			/*
			 * Set the Auto_negotiation Advertisement Register.
			 * MII advertising for Next page.
			 * 100BaseTxFD and HD, 10BaseTFD and HD, IEEE 802.3.
			 */
			ul_phy_anar = MII_TX_FDX | MII_TX_HDX | MII_10_FDX | MII_10_HDX | MII_AN_IEEE_802_3;
			uint8_t uc_rc = emac_phy_write(p_emac, uc_phy_addr, MII_ANAR, ul_phy_anar);
			if (uc_rc != EMAC_OK)
			{
				emac_enable_management(p_emac, false);
				state = 0;
				return uc_rc;
			}

			++state;
		}
		break;

	case 3:
		{
			/* Read & modify control register */
			uint8_t uc_rc = emac_phy_read(p_emac, uc_phy_addr, MII_BMCR, &ul_value);
			if (uc_rc != EMAC_OK)
			{
				emac_enable_management(p_emac, false);
				state = 0;
				return uc_rc;
			}

			++state;
		}
		break;

	case 4:
		{
			ul_value |= MII_SPEED_SELECT | MII_AUTONEG | MII_DUPLEX_MODE;
			uint8_t uc_rc = emac_phy_write(p_emac, uc_phy_addr, MII_BMCR, ul_value);
			if (uc_rc != EMAC_OK)
			{
				emac_enable_management(p_emac, false);
				state = 0;
				return uc_rc;
			}

			++state;
		}
		break;

	case 5:
		{
			/* Restart auto negotiation */
			ul_value |= MII_RESTART_AUTONEG;
			ul_value &= ~MII_ISOLATE;
			uint8_t uc_rc = emac_phy_write(p_emac, uc_phy_addr, MII_BMCR, ul_value);
			if (uc_rc != EMAC_OK)
			{
				emac_enable_management(p_emac, false);
				state = 0;
				return uc_rc;
			}

			ul_retry_count = 0;
			++state;
		}
		break;

	case 6:
		{
			/* Check if auto negotiation is completed */
			uint8_t uc_rc = emac_phy_read(p_emac, uc_phy_addr, MII_BMSR, &ul_value);
			if (uc_rc != EMAC_OK)
			{
				emac_enable_management(p_emac, false);
				state = 0;
				return uc_rc;
			}

			/* Done successfully */
			if (!(ul_value & MII_AUTONEG_COMP))
			{
				++ul_retry_count;
				if (ul_retry_count >= 10000)	// timeout check
				{
					state = 0;					// start again
				}
				return EMAC_TIMEOUT;
			}
			++state;
		}
		break;

	case 7:
		{
			uint32_t ul_phy_analpar;

			/* Get the auto negotiate link partner base page */
			uint8_t uc_rc = emac_phy_read(p_emac, uc_phy_addr, MII_ANLPAR, &ul_phy_analpar);
			if (uc_rc != EMAC_OK)
			{
				emac_enable_management(p_emac, false);
				state = 0;
				return uc_rc;
			}

			// The rest only happens once. so we do it all in one go
			uint8_t uc_fd = false;
			uint8_t uc_speed = false;

			/* Set up the EMAC link speed */
			if ((ul_phy_anar & ul_phy_analpar) & MII_TX_FDX)
			{
				/* Set MII for 100BaseTX and Full Duplex */
				uc_speed = true;
				uc_fd = true;
			}
			else if ((ul_phy_anar & ul_phy_analpar) & MII_10_FDX)
			{
				/* Set MII for 10BaseT and Full Duplex */
				uc_speed = false;
				uc_fd = true;
			}
			else if ((ul_phy_anar & ul_phy_analpar) & MII_TX_HDX)
			{
				/* Set MII for 100BaseTX and half Duplex */
				uc_speed = true;
				uc_fd = false;
			}
			else if ((ul_phy_anar & ul_phy_analpar) & MII_10_HDX)
			{
				/* Set MII for 10BaseT and half Duplex */
				uc_speed = false;
				uc_fd = false;
			}

			emac_set_speed(p_emac, uc_speed);
			emac_enable_full_duplex(p_emac, uc_fd);
			emac_enable_rmii(p_emac, ETH_PHY_MODE);
			emac_enable_transceiver_clock(p_emac, true);
			emac_enable_management(p_emac, false);

			state = 0;			// in case we get called again
			return EMAC_OK;
		}
	}

	return EMAC_TIMEOUT;	// this just means that we needs to be called again
}
/**
 * \brief Get the Link & speed settings, and automatically set up the EMAC with the
 * settings.
 *
 * \param p_emac   Pointer to the EMAC instance. 
 * \param uc_phy_addr PHY address.
 * \param uc_apply_setting_flag Set to 0 to not apply the PHY configurations, else to apply.
 *
 * Return EMAC_OK if successfully, EMAC_TIMEOUT if timeout. 
 */
uint8_t ethernet_phy_set_link(Emac *p_emac, uint8_t uc_phy_addr,
		uint8_t uc_apply_setting_flag)
{

	uint32_t ul_stat1;
	uint32_t ul_stat2;
	uint8_t uc_phy_address, uc_speed, uc_fd;
	uint8_t uc_rc = EMAC_TIMEOUT;
	
	emac_enable_management(p_emac, true);

	uc_phy_address = uc_phy_addr;

	uc_rc = emac_phy_read(p_emac, uc_phy_address, MII_BMSR, &ul_stat1);
	if (uc_rc != EMAC_OK) {
		/* Disable PHY management and start the EMAC transfer */
		emac_enable_management(p_emac, false);
		return uc_rc;
	}
	
	if ((ul_stat1 & MII_LINK_STATUS) == 0) {
		/* Disable PHY management and start the EMAC transfer */
		emac_enable_management(p_emac, false);
		return EMAC_INVALID;
	}

	if (uc_apply_setting_flag == 0) {
		/* Disable PHY management and start the EMAC transfer */
		emac_enable_management(p_emac, false);
		return uc_rc;
	}

	// Re-configure Link speed 
	uc_rc = emac_phy_read(p_emac, uc_phy_address, MII_PC1, &ul_stat2);
	if (uc_rc != EMAC_OK) {
		//Disable PHY management and start the EMAC transfer
		emac_enable_management(p_emac, false);
		return uc_rc;
	}

	if ((ul_stat1 & MII_100BASE_TX_FD) && (ul_stat2 & MII_OMI_100_FD)) {
		// Set EMAC for 100BaseTX and Full Duplex 
		uc_speed = true;
		uc_fd = true;
	}
	
	else if ((ul_stat1 & MII_10BASE_T_FD) && (ul_stat2 & MII_OMI_10_FD)) {
		// Set MII for 10BaseT and Full Duplex
		uc_speed = false;
		uc_fd = true;
	}
	
	else if ((ul_stat1 & MII_100BASE_TX_HD) && (ul_stat2 & MII_OMI_100_HD)) {
		// Set MII for 100BaseTX and Half Duplex 
		uc_speed = true;
		uc_fd = false;
	}

	else if ((ul_stat1 & MII_10BASE_T_HD) && (ul_stat2 & MII_OMI_10_HD)) {
		// Set MII for 10BaseT and Half Duplex 
		uc_speed = false;
		uc_fd = false;
	}
	emac_set_speed(p_emac, uc_speed);
	emac_enable_full_duplex(p_emac, uc_fd);

	/* Start the EMAC transfers */
	emac_enable_management(p_emac, false);
	return uc_rc;
}
Exemple #7
0
/**
 * \brief Issue an auto negotiation of the PHY.
 *
 * \param p_emac   Pointer to the EMAC instance. 
 * \param uc_phy_addr PHY address.
 *
 * Return EMAC_OK if successfully, EMAC_TIMEOUT if timeout. 
 */
uint8_t ethernet_phy_auto_negotiate(Emac *p_emac, uint8_t uc_phy_addr)
{
	uint32_t ul_retry_max = ETH_PHY_RETRY_MAX;
	uint32_t ul_value;
	uint32_t ul_phy_anar;
	uint32_t ul_phy_analpar;
	uint32_t ul_retry_count = 0;
	uint8_t uc_fd = 0;
	uint8_t uc_speed = 0;
	uint8_t uc_rc = EMAC_TIMEOUT;

	emac_enable_management(p_emac, true);
	
	
	/* Set up control register */
	uc_rc = emac_phy_read(p_emac, uc_phy_addr, MII_BMCR, &ul_value);
	if (uc_rc != EMAC_OK) {
		emac_enable_management(p_emac, false);
		return uc_rc;
	}

	ul_value &= ~MII_AUTONEG; /* Remove auto-negotiation enable */
	ul_value &= ~(MII_LOOPBACK | MII_POWER_DOWN);
	ul_value |= MII_ISOLATE; /* Electrically isolate PHY */
	uc_rc = emac_phy_write(p_emac, uc_phy_addr, MII_BMCR, ul_value);
	if (uc_rc != EMAC_OK) {
		emac_enable_management(p_emac, false);
		return uc_rc;
	}
	
	/* 
	 * Set the Auto_negotiation Advertisement Register.
	 * MII advertising for Next page.
	 * 100BaseTxFD and HD, 10BaseTFD and HD, IEEE 802.3.
	 */
	ul_phy_anar = MII_TX_FDX | MII_TX_HDX | MII_10_FDX | MII_10_HDX | 
			MII_AN_IEEE_802_3;
	uc_rc = emac_phy_write(p_emac, uc_phy_addr, MII_ANAR, ul_phy_anar);
	if (uc_rc != EMAC_OK) {
		emac_enable_management(p_emac, false);
		return uc_rc;
	}
	
	/* Read & modify control register */
	uc_rc = emac_phy_read(p_emac, uc_phy_addr, MII_BMCR, &ul_value);
	if (uc_rc != EMAC_OK) {
		emac_enable_management(p_emac, false);
		return uc_rc;
	}
	ul_value |= MII_SPEED_SELECT | MII_AUTONEG | MII_DUPLEX_MODE;
	uc_rc = emac_phy_write(p_emac, uc_phy_addr, MII_BMCR, ul_value);
	if (uc_rc != EMAC_OK) {
		emac_enable_management(p_emac, false);
		return uc_rc;
	}
	/* Restart auto negotiation */
	ul_value |= MII_RESTART_AUTONEG;
	ul_value &= ~MII_ISOLATE;
	uc_rc = emac_phy_write(p_emac, uc_phy_addr, MII_BMCR, ul_value);
	if (uc_rc != EMAC_OK) {
		emac_enable_management(p_emac, false);
		return uc_rc;
	}
	/* Check if auto negotiation is completed */
	while (1) {
		uc_rc = emac_phy_read(p_emac, uc_phy_addr, MII_BMSR, &ul_value);
		if (uc_rc != EMAC_OK) {
			emac_enable_management(p_emac, false);
			return uc_rc;
		}
		/* Done successfully */
		if (ul_value & MII_AUTONEG_COMP) {
			break;
		}

		/* Timeout check */
		if (ul_retry_max) {
			if (++ul_retry_count >= ul_retry_max) {
				return EMAC_TIMEOUT;
			}
		}
	}

	/* Get the auto negotiate link partner base page */
	uc_rc = emac_phy_read(p_emac, uc_phy_addr, MII_ANLPAR, &ul_phy_analpar);
	if (uc_rc != EMAC_OK) {
		emac_enable_management(p_emac, false);
		return uc_rc;
	}
	
	/* Set up the EMAC link speed */
	if ((ul_phy_anar & ul_phy_analpar) & MII_TX_FDX) {
		/* Set MII for 100BaseTX and Full Duplex */
		uc_speed = true;
		uc_fd = true;
	} else if ((ul_phy_anar & ul_phy_analpar) & MII_10_FDX) {
		/* Set MII for 10BaseT and Full Duplex */
		uc_speed = false;
		uc_fd = true;
	} else if ((ul_phy_anar & ul_phy_analpar) & MII_TX_HDX) {
		/* Set MII for 100BaseTX and half Duplex */
		uc_speed = true;
		uc_fd = false;
	} else if ((ul_phy_anar & ul_phy_analpar) & MII_10_HDX) {
		/* Set MII for 10BaseT and half Duplex */
		uc_speed = false;
		uc_fd = false;
	}
	emac_set_speed(p_emac, uc_speed);
	emac_enable_full_duplex(p_emac, uc_fd);
	emac_enable_rmii(p_emac, ETH_PHY_MODE);
	emac_enable_transceiver_clock(p_emac, true);
	emac_enable_management(p_emac, false);
	return uc_rc;
}