Add SGMII support to the tsec

Adds support for configuring the TBI to talk properly with the SerDes.

Signed-off-by: Andy Fleming <afleming@freescale.com>
Signed-off-by: Ben Warren <biggerbadderben@gmail.com>
This commit is contained in:
Andy Fleming 2008-08-31 16:33:27 -05:00 committed by Ben Warren
parent 75b9d4ae0d
commit 2abe361c03
2 changed files with 72 additions and 27 deletions

View File

@ -216,61 +216,80 @@ int tsec_init(struct eth_device *dev, bd_t * bd)
return (priv->link ? 0 : -1); return (priv->link ? 0 : -1);
} }
/* Write value to the device's PHY through the registers /* Writes the given phy's reg with value, using the specified MDIO regs */
* specified in priv, modifying the register specified in regnum. static void tsec_local_mdio_write(volatile tsec_t *phyregs, uint addr,
* It will wait for the write to be done (or for a timeout to uint reg, uint value)
* expire) before exiting
*/
void write_any_phy_reg(struct tsec_private *priv, uint phyid, uint regnum, uint value)
{ {
volatile tsec_t *regbase = priv->phyregs;
int timeout = 1000000; int timeout = 1000000;
regbase->miimadd = (phyid << 8) | regnum; phyregs->miimadd = (addr << 8) | reg;
regbase->miimcon = value; phyregs->miimcon = value;
asm("sync"); asm("sync");
timeout = 1000000; timeout = 1000000;
while ((regbase->miimind & MIIMIND_BUSY) && timeout--) ; while ((phyregs->miimind & MIIMIND_BUSY) && timeout--) ;
} }
/* #define to provide old write_phy_reg functionality without duplicating code */
#define write_phy_reg(priv, regnum, value) write_any_phy_reg(priv,priv->phyaddr,regnum,value) /* Provide the default behavior of writing the PHY of this ethernet device */
#define write_phy_reg(priv, regnum, value) tsec_local_mdio_write(priv->phyregs,priv->phyaddr,regnum,value)
/* Reads register regnum on the device's PHY through the /* Reads register regnum on the device's PHY through the
* registers specified in priv. It lowers and raises the read * specified registers. It lowers and raises the read
* command, and waits for the data to become valid (miimind * command, and waits for the data to become valid (miimind
* notvalid bit cleared), and the bus to cease activity (miimind * notvalid bit cleared), and the bus to cease activity (miimind
* busy bit cleared), and then returns the value * busy bit cleared), and then returns the value
*/ */
uint read_any_phy_reg(struct tsec_private *priv, uint phyid, uint regnum) uint tsec_local_mdio_read(volatile tsec_t *phyregs, uint phyid, uint regnum)
{ {
uint value; uint value;
volatile tsec_t *regbase = priv->phyregs;
/* Put the address of the phy, and the register /* Put the address of the phy, and the register
* number into MIIMADD */ * number into MIIMADD */
regbase->miimadd = (phyid << 8) | regnum; phyregs->miimadd = (phyid << 8) | regnum;
/* Clear the command register, and wait */ /* Clear the command register, and wait */
regbase->miimcom = 0; phyregs->miimcom = 0;
asm("sync"); asm("sync");
/* Initiate a read command, and wait */ /* Initiate a read command, and wait */
regbase->miimcom = MIIM_READ_COMMAND; phyregs->miimcom = MIIM_READ_COMMAND;
asm("sync"); asm("sync");
/* Wait for the the indication that the read is done */ /* Wait for the the indication that the read is done */
while ((regbase->miimind & (MIIMIND_NOTVALID | MIIMIND_BUSY))) ; while ((phyregs->miimind & (MIIMIND_NOTVALID | MIIMIND_BUSY))) ;
/* Grab the value read from the PHY */ /* Grab the value read from the PHY */
value = regbase->miimstat; value = phyregs->miimstat;
return value; return value;
} }
/* #define to provide old read_phy_reg functionality without duplicating code */ /* #define to provide old read_phy_reg functionality without duplicating code */
#define read_phy_reg(priv,regnum) read_any_phy_reg(priv,priv->phyaddr,regnum) #define read_phy_reg(priv,regnum) tsec_local_mdio_read(priv->phyregs,priv->phyaddr,regnum)
#define TBIANA_SETTINGS ( \
TBIANA_ASYMMETRIC_PAUSE \
| TBIANA_SYMMETRIC_PAUSE \
| TBIANA_FULL_DUPLEX \
)
#define TBICR_SETTINGS ( \
TBICR_PHY_RESET \
| TBICR_ANEG_ENABLE \
| TBICR_FULL_DUPLEX \
| TBICR_SPEED1_SET \
)
/* Configure the TBI for SGMII operation */
static void tsec_configure_serdes(struct tsec_private *priv)
{
tsec_local_mdio_write(priv->phyregs, CFG_TBIPA_VALUE, TBI_ANA,
TBIANA_SETTINGS);
tsec_local_mdio_write(priv->phyregs, CFG_TBIPA_VALUE, TBI_TBICON,
TBICON_CLK_SELECT);
tsec_local_mdio_write(priv->phyregs, CFG_TBIPA_VALUE, TBI_CR,
TBICR_SETTINGS);
}
/* Discover which PHY is attached to the device, and configure it /* Discover which PHY is attached to the device, and configure it
* properly. If the PHY is not recognized, then return 0 * properly. If the PHY is not recognized, then return 0
@ -280,12 +299,12 @@ static int init_phy(struct eth_device *dev)
{ {
struct tsec_private *priv = (struct tsec_private *)dev->priv; struct tsec_private *priv = (struct tsec_private *)dev->priv;
struct phy_info *curphy; struct phy_info *curphy;
volatile tsec_t *regs = (volatile tsec_t *)(TSEC_BASE_ADDR); volatile tsec_t *phyregs = priv->phyregs;
volatile tsec_t *regs = priv->regs;
/* Assign a Physical address to the TBI */ /* Assign a Physical address to the TBI */
regs->tbipa = CFG_TBIPA_VALUE; regs->tbipa = CFG_TBIPA_VALUE;
regs = (volatile tsec_t *)(TSEC_BASE_ADDR + TSEC_SIZE); phyregs->tbipa = CFG_TBIPA_VALUE;
regs->tbipa = CFG_TBIPA_VALUE;
asm("sync"); asm("sync");
/* Reset MII (due to new addresses) */ /* Reset MII (due to new addresses) */
@ -309,6 +328,9 @@ static int init_phy(struct eth_device *dev)
return 0; return 0;
} }
if (regs->ecntrl & ECNTRL_SGMII_MODE)
tsec_configure_serdes(priv);
priv->phyinfo = curphy; priv->phyinfo = curphy;
phy_run_commands(priv, priv->phyinfo->config); phy_run_commands(priv, priv->phyinfo->config);
@ -1700,7 +1722,7 @@ static int tsec_miiphy_read(char *devname, unsigned char addr,
return -1; return -1;
} }
ret = (unsigned short)read_any_phy_reg(priv, addr, reg); ret = (unsigned short)tsec_local_mdio_read(priv->phyregs, addr, reg);
*value = ret; *value = ret;
return 0; return 0;
@ -1722,7 +1744,7 @@ static int tsec_miiphy_write(char *devname, unsigned char addr,
return -1; return -1;
} }
write_any_phy_reg(priv, addr, reg, value); tsec_local_mdio_write(priv->phyregs, addr, reg, value);
return 0; return 0;
} }

View File

@ -60,6 +60,27 @@
#define PHY_AUTONEGOTIATE_TIMEOUT 5000 /* in ms */ #define PHY_AUTONEGOTIATE_TIMEOUT 5000 /* in ms */
/* TBI register addresses */
#define TBI_CR 0x00
#define TBI_SR 0x01
#define TBI_ANA 0x04
#define TBI_ANLPBPA 0x05
#define TBI_ANEX 0x06
#define TBI_TBICON 0x11
/* TBI MDIO register bit fields*/
#define TBICON_CLK_SELECT 0x0020
#define TBIANA_ASYMMETRIC_PAUSE 0x0100
#define TBIANA_SYMMETRIC_PAUSE 0x0080
#define TBIANA_HALF_DUPLEX 0x0040
#define TBIANA_FULL_DUPLEX 0x0020
#define TBICR_PHY_RESET 0x8000
#define TBICR_ANEG_ENABLE 0x1000
#define TBICR_RESTART_ANEG 0x0200
#define TBICR_FULL_DUPLEX 0x0100
#define TBICR_SPEED1_SET 0x0040
/* MAC register bits */ /* MAC register bits */
#define MACCFG1_SOFT_RESET 0x80000000 #define MACCFG1_SOFT_RESET 0x80000000
#define MACCFG1_RESET_RX_MC 0x00080000 #define MACCFG1_RESET_RX_MC 0x00080000
@ -540,7 +561,9 @@ typedef struct tsec
/* This flag currently only has /* This flag currently only has
* meaning if we're using the eTSEC */ * meaning if we're using the eTSEC */
#define TSEC_REDUCED (1 << 1) #define TSEC_REDUCED (1 << 1)
#define TSEC_SGMII (1 << 2)
struct tsec_private { struct tsec_private {
volatile tsec_t *regs; volatile tsec_t *regs;