blob: 7b4249ac105f507b1e21dc090686baf403b8d5ae [file] [log] [blame]
#include <common.h>
#include <miiphy.h>
#include "ar8328.h"
#define S17_LAN_PORT_VLAN 1
#define S17_WAN_PORT_VLAN 2
#define ENET_UNIT_GE1 1
#define ENET_UNIT_GE0 0 /* Connected to the switch */
#define TRUE 1
#define FALSE 0
#define S17_PHY0_ADDR 0x0
#define S17_PHY1_ADDR 0x1
#define S17_PHY2_ADDR 0x2
#define S17_PHY3_ADDR 0x3
#define S17_PHY4_ADDR 0x4
#define S17_IND_PHY 4
/*
* Track per-PHY port information.
*/
typedef struct {
BOOL isEnetPort; /* normal enet port */
BOOL isPhyAlive; /* last known state of link */
int ethUnit; /* MAC associated with this phy port */
uint32_t phyBase;
uint32_t phyAddr; /* PHY registers associated with this phy port */
uint32_t VLANTableSetting; /* Value to be written to VLAN table */
} athrPhyInfo_t;
/*
* Per-PHY information, indexed by PHY unit number
* MAC port 0 - CPU port 0x100
* All ports are connected to GE0 of the SoC MAC
* LAN/WAN seperation by VLAN tags (port 0-3 with VLANID 1; port 4 with VLANID 2
*/
static athrPhyInfo_t athrPhyInfo[] = {
{TRUE, /* phy port 0 -- MAC port 1 0x200 */
FALSE,
ENET_UNIT_GE0,
0,
S17_PHY0_ADDR,
S17_LAN_PORT_VLAN
},
{TRUE, /* phy port 1 -- MAC port 2 0x300 */
FALSE,
ENET_UNIT_GE0,
0,
S17_PHY1_ADDR,
S17_LAN_PORT_VLAN
},
{TRUE, /* phy port 2 -- MAC port 3 0x400 */
FALSE,
ENET_UNIT_GE0,
0,
S17_PHY2_ADDR,
S17_LAN_PORT_VLAN
},
{TRUE, /* phy port 3 -- MAC port 4 0x500 */
FALSE,
ENET_UNIT_GE0,
0,
S17_PHY3_ADDR,
S17_LAN_PORT_VLAN
},
{TRUE, /* phy port 4 -- WAN port or MAC port 5 0x600 */
FALSE,
ENET_UNIT_GE1,
0,
S17_PHY4_ADDR,
S17_WAN_PORT_VLAN /* set as WAN port */
},
{FALSE, /* phy port 5 -- CPU port (no RJ45 connector) */
TRUE,
ENET_UNIT_GE0,
0,
0x00,
S17_LAN_PORT_VLAN /* Send to all ports */
},
};
#define S17_PHY_MAX 5
/* Range of valid PHY IDs is [MIN..MAX] */
#define S17_ID_MIN 0
#define S17_ID_MAX (S17_PHY_MAX-1)
/* Convenience macros to access myPhyInfo */
#define S17_IS_ENET_PORT(phyUnit) (athrPhyInfo[phyUnit].isEnetPort)
#define S17_IS_PHY_ALIVE(phyUnit) (athrPhyInfo[phyUnit].isPhyAlive)
#define S17_ETHUNIT(phyUnit) (athrPhyInfo[phyUnit].ethUnit)
#define S17_PHYBASE(phyUnit) (athrPhyInfo[phyUnit].phyBase)
#define S17_PHYADDR(phyUnit) (athrPhyInfo[phyUnit].phyAddr)
#define S17_VLAN_TABLE_SETTING(phyUnit) (athrPhyInfo[phyUnit].VLANTableSetting)
int athrs17_init(char *devname)
{
int phy_addr;
unsigned int dummy;
//configure the RGMII
/* FIXME Configure broadcast ports: the configuration below
* will broadcast on ports 0-5 of the switch.
*/
athrs17_reg_write(devname, S17_GLOFW_CTRL1_REG, \
S17_BROAD_DPALL | S17_MULTI_FLOOD_DPALL | S17_UNI_FLOOD_DPALL);
/* FIXME bit 30 of the PWS_REG is marked as reserved in the datasheet. */
athrs17_reg_write(devname, S17_PWS_REG, S17_PWS_CHIP_AR8327);
/* Set delays for MAC0 */
athrs17_reg_write(devname, S17_P0PAD_MODE_REG, S17_MAC0_RGMII_EN | S17_MAC0_RGMII_TXCLK_DELAY | \
S17_MAC0_RGMII_RXCLK_DELAY | (1 << S17_MAC0_RGMII_TXCLK_SHIFT) | \
(1 << S17_MAC0_RGMII_RXCLK_SHIFT));
/* Set bit 24 to enable MAC0 RGMII delay; set MAC6 as PHY mode (PHY4), QCA */
athrs17_reg_write(devname, S17_P6PAD_MODE_REG, S17_PHY4_RGMII_EN | S17_MAC6_RGMII_RXCLK_DELAY | \
(0 << S17_MAC6_RGMII_RXCLK_SHIFT));
/* Disable MAC5 and MAC6 (due to PHY4), QCA */
athrs17_reg_write(devname, S17_P5STATUS_REG, 0);
athrs17_reg_write(devname, S17_P6STATUS_REG, 0);
athrs17_reg_write(devname, S17_P0STATUS_REG, S17_PORT_STATUS_DEFAULT);
/* AR8327/AR8328 v1.0 fixup */
if ((athrs17_reg_read(devname, S17_MASK_CTRL_REG) & 0xffff) == S17_CHIPID_V1_0)
{
for (phy_addr = 0x0; phy_addr <= S17_PHY_MAX; phy_addr++)
{
/* For 100M waveform */
miiphy_debug_write(devname, phy_addr, 0x0, 0x02ea);
/* Turn On Gigabit Clock */
miiphy_debug_write(devname, phy_addr, 0x3d, 0x68a0);
}
}
/* Set delays for PHY4 (connected to MAC5)
*/
dummy = miiphy_debug_read(devname, S17_PHY4_ADDR, 0x0);
dummy |= (1 << 15); // Enable RGMII Rx clock delay
miiphy_debug_write(devname, S17_PHY4_ADDR, 0x0, dummy);
dummy = miiphy_debug_read(devname, S17_PHY4_ADDR, 0x5);
dummy |= (1 << 8); // Enable RGMII Tx clock delay
miiphy_debug_write(devname, S17_PHY4_ADDR, 0x5, dummy);
/* enable HOL by default */
for (phy_addr = 0; phy_addr < S17_MAC_MAX; phy_addr++)
{
switch (phy_addr) {
case 0:
case 5:
case 6:
athrs17_reg_write(devname, S17_PORT0_HOL_CTRL0 + phy_addr * 8, \
S17_HOL_CTRL0_WAN);
break;
default:
athrs17_reg_write(devname, S17_PORT0_HOL_CTRL0 + phy_addr * 8, \
S17_HOL_CTRL0_LAN);
break;
}
athrs17_reg_write(devname, S17_PORT0_HOL_CTRL1 + phy_addr * 8, \
S17_HOL_CTRL1_DEFAULT);
}
/* LED control */
athrs17_reg_write(devname, S17_LED_CTRL0_REG, 0xffb7ffb7);
athrs17_reg_write(devname, S17_LED_CTRL1_REG, 0xffb7ffb7);
athrs17_reg_write(devname, S17_LED_CTRL2_REG, 0xffb7ffb7);
printf ("%s:done\n",__func__);
return 0;
}
/******************************************************************************
*
* athrs17_phy_is_link_alive - test to see if the specified link is alive
*
* RETURNS:
* TRUE --> link is alive
* FALSE --> link is down
*/
BOOL
athrs17_phy_is_link_alive(char *devname,int phyUnit)
{
uint16_t phyHwStatus;
uint32_t phyAddr;
phyAddr = S17_PHYADDR(phyUnit);
miiphy_read (devname, phyAddr, S17_PHY_SPEC_STATUS, &phyHwStatus);
if (phyHwStatus & S17_STATUS_LINK_PASS)
return TRUE;
printf("phy%d link down\n",phyUnit);
return FALSE;
}
/******************************************************************************
*
* athrs17_phy_stat
*
*/
int
athrs17_phy_stat(char *devname)
{
uint16_t phyHwStatus;
uint32_t phyAddr;
int phyUnit;
int ii = 200;
for (phyUnit=0; phyUnit < S17_PHY_MAX; phyUnit++) {
phyAddr = S17_PHYADDR(phyUnit);
if (athrs17_phy_is_link_alive(devname, phyUnit)) {
do {
miiphy_read (devname, phyAddr, S17_PHY_SPEC_STATUS, &phyHwStatus);
if(phyHwStatus & S17_STATUS_RESOLVED)
break;
udelay(10*1000);
}while(--ii);
phyHwStatus = ((phyHwStatus & S17_STATUS_LINK_MASK) >>
S17_STATUS_LINK_SHIFT);
printf("phy%d phyhwstat:%x\n",phyUnit,phyHwStatus);
}
}
return 0;
}
/******************************************************************************
*
* athrs17_phy_init - reset and setup the PHY associated
*
* Resets the associated PHY port.
*
* RETURNS:
* TRUE --> associated PHY is alive
* FALSE --> no LINKs on this ethernet unit
*/
int
athrs17_phy_init(char *devname, int phyUnit)
{
uint16_t phyHwStatus;
uint16_t timeout;
int liveLinks = 0;
uint32_t phyBase = 0;
uint32_t phyAddr = 0;
printf("athrs17_phy_init %d\n", phyUnit);
phyBase = S17_PHYBASE(phyUnit);
phyAddr = S17_PHYADDR(phyUnit);
miiphy_write(devname, phyAddr, S17_AUTONEG_ADVERT,
S17_ADVERTISE_ALL);
miiphy_write(devname, phyAddr, S17_1000BASET_CONTROL,
S17_ADVERTISE_1000FULL);
/* Reset PHYs*/
miiphy_write(devname, phyAddr, S17_PHY_CONTROL,
S17_CTRL_AUTONEGOTIATION_ENABLE
| S17_CTRL_SOFTWARE_RESET);
/*
* After the phy is reset, it takes a little while before
* it can respond properly.
*/
//FIXME: Now since this function is called multiple time for each LAN PHY the overrall delay
//will also increase. Maybe this is not required as the phy status is being check
//So this is subjected to change during the bringup
udelay(1000*1000);
/*
* Wait up to 3 seconds for ALL associated PHYs to finish
* autonegotiation. The only way we get out of here sooner is
* if ALL PHYs are connected AND finish autonegotiation.
*/
timeout=20;
for (;;) {
miiphy_read (devname, phyAddr, S17_PHY_CONTROL, &phyHwStatus);
if (S17_RESET_DONE(phyHwStatus)) {
printf(
("Port %d, Neg Success\n", phyUnit));
break;
}
if (timeout == 0) {
printf("Port %d, Negogiation timeout\n", phyUnit);
break;
}
if (--timeout == 0) {
printf("Port %d, Negogiation timeout\n", phyUnit);
break;
}
udelay(150*1000);
}
/*
* The PHY have had adequate time to autonegotiate.
* Now initialize software status.
*
* It's possible that some ports may take a bit longer
* to autonegotiate; but we can't wait forever. They'll
* get noticed by mv_phyCheckStatusChange during regular
* polling activities.
*/
/* fine-tune PHY 0 and PHY 1*/
if ((phyUnit == 0) || (phyUnit == 1))
{
miiphy_write(devname, S17_PHYADDR(phyUnit), S17_MMD_CTRL_REG, 0x3);
miiphy_write(devname, S17_PHYADDR(phyUnit), S17_MMD_DATA_REG, 0x8007);
miiphy_write(devname, S17_PHYADDR(phyUnit), S17_MMD_CTRL_REG, 0x4003);
miiphy_write(devname, S17_PHYADDR(phyUnit), S17_MMD_DATA_REG, 0x8315);
}
/* fine-tune PHYs */
miiphy_write(devname, S17_PHYADDR(phyUnit), S17_MMD_CTRL_REG, 0x3);
miiphy_write(devname, S17_PHYADDR(phyUnit), S17_MMD_DATA_REG, 0x800d);
miiphy_write(devname, S17_PHYADDR(phyUnit), S17_MMD_CTRL_REG, 0x4003);
miiphy_write(devname, S17_PHYADDR(phyUnit), S17_MMD_DATA_REG, 0x103f);
miiphy_debug_write(devname, S17_PHYADDR(phyUnit), 0x3d, 0x6860);
/* for PHY4, QCA */
if (phyUnit == S17_PHY4_ADDR)
{
miiphy_debug_write(devname, S17_PHYADDR(phyUnit), 0x12, 0x4c0c);
miiphy_debug_write(devname, S17_PHYADDR(phyUnit), 0x0, 0x82ee);
miiphy_debug_write(devname, S17_PHYADDR(phyUnit), 0x5, 0x3d46);
miiphy_debug_write(devname, S17_PHYADDR(phyUnit), 0xb, 0xbc20);
}
if (athrs17_phy_is_link_alive(devname, phyUnit)) {
liveLinks++;
S17_IS_PHY_ALIVE(phyUnit) = TRUE;
} else {
S17_IS_PHY_ALIVE(phyUnit) = FALSE;
}
// printf("eth%d: Phy Specific Status=%4.4x\n", mdev->read(mdev, S17_PHYADDR(phyUnit),S17_PHY_SPEC_STATUS));
// printk("Phy setup Complete\n");
}
int
athrs17_phy_setup(char *devname, int phyUnit)
{
int i;
if(phyUnit == S17_PHY4_ADDR)
{//WAN PHY
athrs17_phy_init(devname, phyUnit);
}
else if(phyUnit < S17_PHY4_ADDR)
{//LAN PHYs
for(i = 0 ; i <S17_PHY4_ADDR; i++)
{
athrs17_phy_init(devname, i);
}
}
}
unsigned int athrs17_reg_read(char *devname, unsigned int reg_addr)
{
uint32_t reg_word_addr;
uint32_t phy_addr, tmp_val, reg_val;
uint16_t phy_val;
uint8_t phy_reg;
/* change reg_addr to 16-bit word address, 32-bit aligned */
reg_word_addr = (reg_addr & 0xfffffffc) >> 1;
/* configure register high address */
phy_addr = 0x18;
phy_reg = 0x0;
phy_val = (uint16_t) ((reg_word_addr >> 8) & 0x3ff); /* bit16-8 of reg address */
miiphy_write (devname, phy_addr, phy_reg, phy_val);
/* For some registers such as MIBs, since it is read/clear, we should */
/* read the lower 16-bit register then the higher one */
/* read register in lower address */
phy_addr = 0x10 | ((reg_word_addr >> 5) & 0x7); /* bit7-5 of reg address */
phy_reg = (uint8_t) (reg_word_addr & 0x1f); /* bit4-0 of reg address */
miiphy_read (devname, phy_addr, phy_reg, &reg_val);
/* read register in higher address */
reg_word_addr++;
phy_addr = 0x10 | ((reg_word_addr >> 5) & 0x7); /* bit7-5 of reg address */
phy_reg = (uint8_t) (reg_word_addr & 0x1f); /* bit4-0 of reg address */
miiphy_read (devname, phy_addr, phy_reg, &tmp_val);
reg_val |= (tmp_val << 16);
// printf("%s: reg 0x%x val 0x%x\n",__func__, reg_addr, reg_val);
return reg_val;
}
void athrs17_reg_write(char *devname, unsigned int reg_addr, unsigned int reg_val)
{
uint32_t reg_word_addr;
uint32_t phy_addr;
uint16_t phy_val;
uint8_t phy_reg;
// printf("%s: reg 0x%x val 0x%x\n",__func__, reg_addr, reg_val);
/* change reg_addr to 16-bit word address, 32-bit aligned */
reg_word_addr = (reg_addr & 0xfffffffc) >> 1;
/* configure register high address */
phy_addr = 0x18;
phy_reg = 0x0;
phy_val = (uint16_t) ((reg_word_addr >> 8) & 0x3ff); /* bit16-8 of reg address */
miiphy_write (devname, phy_addr, phy_reg, phy_val);
/* For S17 registers such as ARL and VLAN, since they include BUSY bit */
/* in higher address, we should write the lower 16-bit register then the */
/* higher one */
/* write register in lower address */
phy_addr = 0x10 | ((reg_word_addr >> 5) & 0x7); /* bit7-5 of reg address */
phy_reg = (uint8_t) (reg_word_addr & 0x1f); /* bit4-0 of reg address */
phy_val = (uint16_t) (reg_val & 0xffff);
miiphy_write (devname, phy_addr, phy_reg, phy_val);
/* read register in higher address */
reg_word_addr++;
phy_addr = 0x10 | ((reg_word_addr >> 5) & 0x7); /* bit7-5 of reg address */
phy_reg = (uint8_t) (reg_word_addr & 0x1f); /* bit4-0 of reg address */
phy_val = (uint16_t) ((reg_val >> 16) & 0xffff);
miiphy_write (devname, phy_addr, phy_reg, phy_val);
}