/*
 * File:        am79c874.c
 * Purpose:     Driver for the AMD AM79C874 10/100 Ethernet PHY
 */

#include "net.h"
#include "fec.h"
#include "am79c874.h"

#include "bas_printf.h"

#if defined(MACHINE_FIREBEE)
#include "firebee.h"
#elif defined(MACHINE_M5484LITE)
#include "m5484l.h"
#else
#error "unknown machine"
#endif

//#define DBG_AM79
#ifdef DBG_AM79
#define dbg(format, arg...) do { xprintf("DEBUG: " format, ##arg); } while (0)
#else
#define dbg(format, arg...) do { ; } while (0)
#endif /* DBG_AM79 */

/********************************************************************/
/* Initialize the AM79C874 PHY
 *
 * This function sets up the Auto-Negotiate Advertisement register
 * within the PHY and then forces the PHY to auto-negotiate for
 * it's settings.
 * 
 * Params:
 *  fec_ch      FEC channel
 *  phy_addr    Address of the PHY.
 *  speed       Desired speed (10BaseT or 100BaseTX)
 *  duplex      Desired duplex (Full or Half)
 *
 * Return Value:
 *  0 if MII commands fail
 *  1 otherwise
 */
int am79c874_init(uint8_t fec_ch, uint8_t phy_addr, uint8_t speed, uint8_t duplex)
{
	int timeout;
	uint16_t settings;
	if (speed); /* to do */
	if (duplex); /* to do */
	
	/* Initialize the MII interface */
	fec_mii_init(fec_ch, SYSCLK / 1000);
	dbg("%s: PHY reset\r\n", __FUNCTION__);
	
	/* Reset the PHY */
	if (!fec_mii_write(fec_ch, phy_addr, MII_AM79C874_CR, MII_AM79C874_CR_RESET))
		return 0;

	/* Wait for the PHY to reset */
	for (timeout = 0; timeout < FEC_MII_TIMEOUT; timeout++)
	{
		fec_mii_read(fec_ch, phy_addr, MII_AM79C874_CR, &settings);
		if (!(settings & MII_AM79C874_CR_RESET))
			break;
	}

	if (timeout >= FEC_MII_TIMEOUT)
	{
		dbg("%s: PHY reset failed\r\n", __FUNCTION__);
		return 0;
	};
	dbg("%s: PHY reset OK\r\n", __FUNCTION__);
	dbg("%s: PHY Enable Auto-Negotiation\r\n", __FUNCTION__);

	/* Enable Auto-Negotiation */
	if (!fec_mii_write(fec_ch, phy_addr, MII_AM79C874_CR, MII_AM79C874_CR_AUTON | MII_AM79C874_CR_RST_NEG))
		return 0;
		
	dbg("%s:PHY Wait for auto-negotiation to complete\r\n", __FUNCTION__);

	/* Wait for auto-negotiation to complete */
	for (timeout = 0; timeout < FEC_MII_TIMEOUT; timeout++)
	{
		settings = 0;
		fec_mii_read(fec_ch, phy_addr, MII_AM79C874_SR, &settings);
		if ((settings & AUTONEGLINK) == AUTONEGLINK)
			break;
	}

	if (timeout >= FEC_MII_TIMEOUT)
	{
		dbg("%s: Auto-negotiation failed (timeout). Set default mode (100Mbps, full duplex)\r\n", __FUNCTION__);

		/* Set the default mode (Full duplex, 100 Mbps) */
		if (!fec_mii_write(fec_ch, phy_addr, MII_AM79C874_CR, MII_AM79C874_CR_100MB | MII_AM79C874_CR_DPLX))
		{
			dbg("%s: forced setting 100Mbps/full failed.\r\n", __FUNCTION__);
			return 0;
		}
	}

#ifdef DBG_AM79
	settings = 0;

	fec_mii_read(fec_ch, phy_addr, MII_AM79C874_DR, &settings);

	dbg("%s: PHY Mode:\r\n", __FUNCTION__);	
	if (settings & MII_AM79C874_DR_DATA_RATE)
		dbg("%s: 100Mbps", __FUNCTION__);
	else
		dbg("%s: 10Mbps ", __FUNCTION__);

	if (settings & MII_AM79C874_DR_DPLX)
		dbg("%s: Full-duplex\r\n", __FUNCTION__);
	else
		dbg("%s: Half-duplex\r\n", __FUNCTION__);

	dbg("%s:PHY auto-negotiation complete\r\n", __FUNCTION__);
#endif /* DBG_AM79 */

	return 1;
}
