917 lines
26 KiB
C
917 lines
26 KiB
C
/*
|
|
* This file is subject to the terms and conditions of the GNU General Public
|
|
* License. See the file "COPYING" in the main directory of this archive
|
|
* for more details.
|
|
*
|
|
* Copyright © 2007 Atheros Communications, Inc., All Rights Reserved.
|
|
*/
|
|
|
|
/*
|
|
* Manage the atheros ethernet PHY.
|
|
*
|
|
* All definitions in this file are operating system independent!
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <linux/types.h>
|
|
#include <common.h>
|
|
#include <miiphy.h>
|
|
#include "phy.h"
|
|
#include <asm/addrspace.h>
|
|
#include "ar7240_soc.h"
|
|
#include "ar7240_s26_phy.h"
|
|
|
|
|
|
/* PHY selections and access functions */
|
|
|
|
typedef enum {
|
|
PHY_SRCPORT_INFO,
|
|
PHY_PORTINFO_SIZE,
|
|
} PHY_CAP_TYPE;
|
|
|
|
typedef enum {
|
|
PHY_SRCPORT_NONE,
|
|
PHY_SRCPORT_VLANTAG,
|
|
PHY_SRCPORT_TRAILER,
|
|
} PHY_SRCPORT_TYPE;
|
|
|
|
#define DRV_LOG(DBG_SW, X0, X1, X2, X3, X4, X5, X6)
|
|
#define DRV_MSG(x,a,b,c,d,e,f)
|
|
#define DRV_PRINT(DBG_SW,X)
|
|
|
|
#define ATHR_LAN_PORT_VLAN 1
|
|
#define ATHR_WAN_PORT_VLAN 2
|
|
#define ENET_UNIT_LAN 1
|
|
#define ENET_UNIT_WAN 0
|
|
|
|
#define TRUE 1
|
|
#define FALSE 0
|
|
|
|
#define ATHR_PHY0_ADDR 0x0
|
|
#define ATHR_PHY1_ADDR 0x1
|
|
#define ATHR_PHY2_ADDR 0x2
|
|
#define ATHR_PHY3_ADDR 0x3
|
|
#define ATHR_PHY4_ADDR 0x4
|
|
|
|
#define MODULE_NAME "ATHRS26"
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
static athrPhyInfo_t athrPhyInfo[] = {
|
|
|
|
{TRUE, /* port 1 -- LAN port 1 */
|
|
FALSE,
|
|
ENET_UNIT_LAN,
|
|
0,
|
|
ATHR_PHY0_ADDR,
|
|
ATHR_LAN_PORT_VLAN
|
|
},
|
|
|
|
{TRUE, /* port 2 -- LAN port 2 */
|
|
FALSE,
|
|
ENET_UNIT_LAN,
|
|
0,
|
|
ATHR_PHY1_ADDR,
|
|
ATHR_LAN_PORT_VLAN
|
|
},
|
|
|
|
{TRUE, /* port 3 -- LAN port 3 */
|
|
FALSE,
|
|
ENET_UNIT_LAN,
|
|
0,
|
|
ATHR_PHY2_ADDR,
|
|
ATHR_LAN_PORT_VLAN
|
|
},
|
|
|
|
{TRUE, /* port 4 -- LAN port 4 */
|
|
FALSE,
|
|
ENET_UNIT_LAN,
|
|
0,
|
|
ATHR_PHY3_ADDR,
|
|
ATHR_LAN_PORT_VLAN /* Send to all ports */
|
|
},
|
|
|
|
{TRUE, /* port 5 -- WAN Port 5 */
|
|
FALSE,
|
|
ENET_UNIT_WAN,
|
|
0,
|
|
ATHR_PHY4_ADDR,
|
|
ATHR_LAN_PORT_VLAN /* Send to all ports */
|
|
},
|
|
|
|
{FALSE, /* port 0 -- cpu port 0 */
|
|
TRUE,
|
|
ENET_UNIT_LAN,
|
|
0,
|
|
0x00,
|
|
ATHR_LAN_PORT_VLAN
|
|
},
|
|
|
|
};
|
|
|
|
static uint8_t athr26_init_flag = 0,athr26_init_flag1 = 0;
|
|
|
|
|
|
#define ATHR_GLOBALREGBASE 0
|
|
|
|
#define ATHR_PHY_MAX 5
|
|
|
|
/* Range of valid PHY IDs is [MIN..MAX] */
|
|
#define ATHR_ID_MIN 0
|
|
#define ATHR_ID_MAX (ATHR_PHY_MAX-1)
|
|
|
|
/* Convenience macros to access myPhyInfo */
|
|
#define ATHR_IS_ENET_PORT(phyUnit) (athrPhyInfo[phyUnit].isEnetPort)
|
|
#define ATHR_IS_PHY_ALIVE(phyUnit) (athrPhyInfo[phyUnit].isPhyAlive)
|
|
#define ATHR_ETHUNIT(phyUnit) (athrPhyInfo[phyUnit].ethUnit)
|
|
#define ATHR_PHYBASE(phyUnit) (athrPhyInfo[phyUnit].phyBase)
|
|
#define ATHR_PHYADDR(phyUnit) (athrPhyInfo[phyUnit].phyAddr)
|
|
#define ATHR_VLAN_TABLE_SETTING(phyUnit) (athrPhyInfo[phyUnit].VLANTableSetting)
|
|
|
|
|
|
#define ATHR_IS_ETHUNIT(phyUnit, ethUnit) \
|
|
(ATHR_IS_ENET_PORT(phyUnit) && \
|
|
ATHR_ETHUNIT(phyUnit) == (ethUnit))
|
|
|
|
#define ATHR_IS_WAN_PORT(phyUnit) (!(ATHR_ETHUNIT(phyUnit)==ENET_UNIT_LAN))
|
|
|
|
/* Forward references */
|
|
BOOL athrs26_phy_is_link_alive(int phyUnit);
|
|
uint32_t athrs26_reg_read(uint32_t reg_addr);
|
|
void athrs26_reg_write(uint32_t reg_addr, uint32_t reg_val);
|
|
unsigned int s26_rd_phy(unsigned int phy_addr, unsigned int reg_addr);
|
|
void s26_wr_phy(unsigned int phy_addr, unsigned int reg_addr, unsigned int write_data);
|
|
|
|
|
|
void athrs26_powersave_off(int phy_addr)
|
|
{
|
|
s26_wr_phy(phy_addr,ATHR_DEBUG_PORT_ADDRESS,0x29);
|
|
s26_wr_phy(phy_addr,ATHR_DEBUG_PORT_DATA,0x36c0);
|
|
|
|
}
|
|
void athrs26_sleep_off(int phy_addr)
|
|
{
|
|
s26_wr_phy(phy_addr,ATHR_DEBUG_PORT_ADDRESS,0xb);
|
|
s26_wr_phy(phy_addr,ATHR_DEBUG_PORT_DATA,0x3c00);
|
|
}
|
|
|
|
void athrs26_reg_init(void)
|
|
{
|
|
|
|
#if S26_PHY_DEBUG
|
|
uint32_t rd_val;
|
|
#endif
|
|
uint32_t ar7240_revid;
|
|
/* if using header for register configuration, we have to */
|
|
/* configure s26 register after frame transmission is enabled */
|
|
|
|
if (athr26_init_flag)
|
|
return;
|
|
|
|
ar7240_revid = ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK;
|
|
if(ar7240_revid == AR7240_REV_1_0) {
|
|
#ifdef S26_FORCE_100M
|
|
s26_wr_phy(ATHR_PHY4_ADDR,ATHR_PHY_FUNC_CONTROL,0x800);
|
|
s26_wr_phy(ATHR_PHY4_ADDR,ATHR_PHY_CONTROL,0xa100);
|
|
#endif
|
|
|
|
#ifdef S26_FORCE_10M
|
|
athrs26_powersave_off(ATHR_PHY4_ADDR);
|
|
athrs26_sleep_off(ATHR_PHY4_ADDR);
|
|
s26_wr_phy(ATHR_PHY4_ADDR,ATHR_PHY_FUNC_CONTROL,0x800);
|
|
s26_wr_phy(ATHR_PHY4_ADDR,ATHR_PHY_CONTROL,0x8100);
|
|
s26_wr_phy(ATHR_PHY4_ADDR,ATHR_DEBUG_PORT_ADDRESS,0x0);
|
|
s26_wr_phy(ATHR_PHY4_ADDR,ATHR_DEBUG_PORT_DATA,0x12ee);
|
|
s26_wr_phy(ATHR_PHY4_ADDR,ATHR_DEBUG_PORT_ADDRESS,0x3);
|
|
s26_wr_phy(ATHR_PHY4_ADDR,ATHR_DEBUG_PORT_DATA,0x3bf0);
|
|
s26_wr_phy(ATHR_PHY4_ADDR,ATHR_PHY_CONTROL,0x8100);
|
|
#endif
|
|
} else {
|
|
s26_wr_phy(ATHR_PHY4_ADDR,ATHR_PHY_CONTROL,0x9000);
|
|
}
|
|
|
|
#if S26_PHY_DEBUG
|
|
rd_val = s26_rd_phy(ATHR_PHY4_ADDR,ATHR_PHY_FUNC_CONTROL);
|
|
printf("S26 PHY FUNC CTRL (%d) :%x\n",ATHR_PHY4_ADDR, rd_val);
|
|
rd_val = s26_rd_phy(ATHR_PHY4_ADDR,ATHR_PHY_CONTROL);
|
|
printf("S26 PHY CTRL (%d) :%x\n",ATHR_PHY4_ADDR, rd_val);
|
|
#endif
|
|
|
|
athr26_init_flag = 1;
|
|
}
|
|
|
|
void athrs26_reg_init_lan(void)
|
|
{
|
|
int i = 60;
|
|
#if S26_PHY_DEBUG
|
|
uint32_t rd_val;
|
|
#endif
|
|
int phyUnit;
|
|
uint32_t phyBase = 0;
|
|
BOOL foundPhy = FALSE;
|
|
uint32_t phyAddr = 0;
|
|
uint32_t ar7240_revid;
|
|
|
|
/* if using header for register configuration, we have to */
|
|
/* configure s26 register after frame transmission is enabled */
|
|
if (athr26_init_flag1)
|
|
return;
|
|
|
|
/* reset switch */
|
|
debug(MODULE_NAME ": resetting s26\n");
|
|
athrs26_reg_write(0x0, athrs26_reg_read(0x0)|0x80000000);
|
|
|
|
while(i--) {
|
|
if(!is_ar933x())
|
|
sysMsDelay(100);
|
|
if(!(athrs26_reg_read(0x0)&0x80000000))
|
|
break;
|
|
}
|
|
debug(MODULE_NAME ": s26 reset done\n");
|
|
|
|
for (phyUnit=0; phyUnit < ATHR_PHY_MAX - 1; phyUnit++) {
|
|
|
|
foundPhy = TRUE;
|
|
phyBase = ATHR_PHYBASE(phyUnit);
|
|
phyAddr = ATHR_PHYADDR(phyUnit);
|
|
|
|
ar7240_revid = ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK;
|
|
if(ar7240_revid == AR7240_REV_1_0) {
|
|
#ifdef S26_FORCE_100M
|
|
/*
|
|
* Force MDI and MDX to alternate ports
|
|
* Phy 0 and 2 -- MDI
|
|
* Phy 1 and 3 -- MDX
|
|
*/
|
|
if(phyUnit%2)
|
|
s26_wr_phy(phyAddr,ATHR_PHY_FUNC_CONTROL,0x820);
|
|
else
|
|
s26_wr_phy(phyAddr,ATHR_PHY_FUNC_CONTROL,0x800);
|
|
|
|
s26_wr_phy(phyAddr,ATHR_PHY_CONTROL,0xa100);
|
|
#endif
|
|
|
|
#ifdef S26_FORCE_10M
|
|
/*
|
|
* Force MDI and MDX to alternate ports
|
|
* Phy 0 and 2 -- MDI
|
|
* Phy 1 and 3 -- MDX
|
|
*/
|
|
if(phyUnit%2)
|
|
s26_wr_phy(phyAddr,ATHR_PHY_FUNC_CONTROL,0x820);
|
|
else
|
|
s26_wr_phy(phyAddr,ATHR_PHY_FUNC_CONTROL,0x800);
|
|
|
|
athrs26_powersave_off(phyAddr);
|
|
athrs26_sleep_off(phyAddr);
|
|
|
|
s26_wr_phy(phyAddr,ATHR_PHY_CONTROL,0x8100);
|
|
s26_wr_phy(phyAddr,ATHR_DEBUG_PORT_ADDRESS,0x0);
|
|
s26_wr_phy(phyAddr,ATHR_DEBUG_PORT_DATA,0x12ee);
|
|
s26_wr_phy(phyAddr,ATHR_DEBUG_PORT_ADDRESS,0x3);
|
|
s26_wr_phy(phyAddr,ATHR_DEBUG_PORT_DATA,0x3bf0);
|
|
s26_wr_phy(phyAddr,ATHR_PHY_CONTROL,0x8100);
|
|
#endif
|
|
} else {
|
|
s26_wr_phy(phyAddr,ATHR_PHY_CONTROL,0x9000);
|
|
}
|
|
|
|
#if S26_PHY_DEBUG
|
|
rd_val = s26_rd_phy(phyAddr,ATHR_PHY_ID1);
|
|
printf("S26 PHY ID (%d) :%x\n",phyAddr,rd_val);
|
|
rd_val = s26_rd_phy(phyAddr,ATHR_PHY_CONTROL);
|
|
printf("S26 PHY CTRL (%d) :%x\n",phyAddr,rd_val);
|
|
rd_val = s26_rd_phy(phyAddr,ATHR_PHY_STATUS);
|
|
printf("S26 ATHR PHY STATUS (%d) :%x\n",phyAddr,rd_val);
|
|
#endif
|
|
}
|
|
/*
|
|
* CPU port Enable
|
|
*/
|
|
athrs26_reg_write(CPU_PORT_REGISTER,(1 << 8));
|
|
|
|
/*
|
|
* status[1:0]=2'h2; - (0x10 - 1000 Mbps , 0x0 - 10 Mbps)
|
|
* status[2]=1'h1; - Tx Mac En
|
|
* status[3]=1'h1; - Rx Mac En
|
|
* status[4]=1'h1; - Tx Flow Ctrl En
|
|
* status[5]=1'h1; - Rx Flow Ctrl En
|
|
* status[6]=1'h1; - Duplex Mode
|
|
*/
|
|
#ifdef CONFIG_AR7240_EMU
|
|
athrs26_reg_write(PORT_STATUS_REGISTER0, 0x7e); /* CPU Port */
|
|
athrs26_reg_write(PORT_STATUS_REGISTER1, 0x3c);
|
|
athrs26_reg_write(PORT_STATUS_REGISTER2, 0x3c);
|
|
athrs26_reg_write(PORT_STATUS_REGISTER3, 0x3c);
|
|
athrs26_reg_write(PORT_STATUS_REGISTER4, 0x3c);
|
|
#else
|
|
athrs26_reg_write(PORT_STATUS_REGISTER1, 0x200); /* LAN - 1 */
|
|
athrs26_reg_write(PORT_STATUS_REGISTER2, 0x200); /* LAN - 2 */
|
|
athrs26_reg_write(PORT_STATUS_REGISTER3, 0x200); /* LAN - 3 */
|
|
athrs26_reg_write(PORT_STATUS_REGISTER4, 0x200); /* LAN - 4 */
|
|
#endif
|
|
|
|
/* QM Control */
|
|
athrs26_reg_write(0x38, 0xc000050e);
|
|
|
|
/*
|
|
* status[11]=1'h0; - CPU Disable
|
|
* status[7] = 1'b1; - Learn One Lock
|
|
* status[14] = 1'b0; - Learn Enable
|
|
*/
|
|
#ifdef CONFIG_AR7240_EMU
|
|
athrs26_reg_write(PORT_CONTROL_REGISTER0, 0x04);
|
|
athrs26_reg_write(PORT_CONTROL_REGISTER1, 0x4004);
|
|
#else
|
|
/* Atheros Header Disable */
|
|
athrs26_reg_write(PORT_CONTROL_REGISTER0, 0x4004);
|
|
#endif
|
|
|
|
/* Tag Priority Mapping */
|
|
athrs26_reg_write(0x70, 0xfa50);
|
|
|
|
/* Enable ARP packets to CPU port */
|
|
athrs26_reg_write(S26_ARL_TBL_CTRL_REG,(athrs26_reg_read(S26_ARL_TBL_CTRL_REG) | 0x100000));
|
|
|
|
#if S26_PHY_DEBUG
|
|
rd_val = athrs26_reg_read ( CPU_PORT_REGISTER );
|
|
printf("S26 CPU_PORT_REGISTER :%x\n",rd_val);
|
|
rd_val = athrs26_reg_read ( PORT_STATUS_REGISTER0 );
|
|
printf("S26 PORT_STATUS_REGISTER0 :%x\n",rd_val);
|
|
rd_val = athrs26_reg_read ( PORT_STATUS_REGISTER1 );
|
|
printf("S26 PORT_STATUS_REGISTER1 :%x\n",rd_val);
|
|
rd_val = athrs26_reg_read ( PORT_STATUS_REGISTER2 );
|
|
printf("S26 PORT_STATUS_REGISTER2 :%x\n",rd_val);
|
|
rd_val = athrs26_reg_read ( PORT_STATUS_REGISTER3 );
|
|
printf("S26 PORT_STATUS_REGISTER3 :%x\n",rd_val);
|
|
rd_val = athrs26_reg_read ( PORT_STATUS_REGISTER4 );
|
|
printf("S26 PORT_STATUS_REGISTER4 :%x\n",rd_val);
|
|
|
|
rd_val = athrs26_reg_read ( PORT_CONTROL_REGISTER0 );
|
|
printf("S26 PORT_CONTROL_REGISTER0 :%x\n",rd_val);
|
|
rd_val = athrs26_reg_read ( PORT_CONTROL_REGISTER1 );
|
|
printf("S26 PORT_CONTROL_REGISTER1 :%x\n",rd_val);
|
|
rd_val = athrs26_reg_read ( PORT_CONTROL_REGISTER2 );
|
|
printf("S26 PORT_CONTROL_REGISTER2 :%x\n",rd_val);
|
|
rd_val = athrs26_reg_read ( PORT_CONTROL_REGISTER3 );
|
|
printf("S26 PORT_CONTROL_REGISTER3 :%x\n",rd_val);
|
|
rd_val = athrs26_reg_read ( PORT_CONTROL_REGISTER4 );
|
|
printf("S26 PORT_CONTROL_REGISTER4 :%x\n",rd_val);
|
|
#endif
|
|
|
|
athr26_init_flag1 = 1;
|
|
}
|
|
static unsigned int phy_val_saved = 0;
|
|
/******************************************************************************
|
|
*
|
|
* athrs26_phy_is_link_alive - test to see if the specified link is alive
|
|
*
|
|
* RETURNS:
|
|
* TRUE --> link is alive
|
|
* FALSE --> link is down
|
|
*/
|
|
BOOL
|
|
athrs26_phy_is_link_alive(int phyUnit)
|
|
{
|
|
uint16_t phyHwStatus;
|
|
uint32_t phyBase;
|
|
uint32_t phyAddr;
|
|
phyBase = ATHR_PHYBASE(phyUnit);
|
|
phyAddr = ATHR_PHYADDR(phyUnit);
|
|
|
|
phyHwStatus = s26_rd_phy( phyAddr, ATHR_PHY_SPEC_STATUS);
|
|
|
|
if (phyHwStatus & ATHR_STATUS_LINK_PASS)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/******************************************************************************
|
|
*
|
|
* athrs26_phy_setup - reset and setup the PHY associated with
|
|
* the specified MAC unit number.
|
|
*
|
|
* Resets the associated PHY port.
|
|
*
|
|
* RETURNS:
|
|
* TRUE --> associated PHY is alive
|
|
* FALSE --> no LINKs on this ethernet unit
|
|
*/
|
|
|
|
BOOL
|
|
athrs26_phy_setup(int ethUnit)
|
|
{
|
|
int phyUnit;
|
|
uint16_t phyHwStatus;
|
|
uint16_t timeout;
|
|
int liveLinks = 0;
|
|
uint32_t phyBase = 0;
|
|
BOOL foundPhy = FALSE;
|
|
uint32_t phyAddr = 0;
|
|
#if S26_PHY_DEBUG
|
|
uint32_t rd_val = 0;
|
|
#endif
|
|
uint32_t ar7240_revid;
|
|
|
|
|
|
/* See if there's any configuration data for this enet */
|
|
/* start auto negogiation on each phy */
|
|
for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) {
|
|
if (!ATHR_IS_ETHUNIT(phyUnit, ethUnit)) {
|
|
continue;
|
|
}
|
|
|
|
foundPhy = TRUE;
|
|
phyBase = ATHR_PHYBASE(phyUnit);
|
|
phyAddr = ATHR_PHYADDR(phyUnit);
|
|
|
|
s26_wr_phy(phyAddr, ATHR_AUTONEG_ADVERT,ATHR_ADVERTISE_ALL);
|
|
#if S26_PHY_DEBUG
|
|
rd_val = s26_rd_phy(phyAddr,ATHR_AUTONEG_ADVERT );
|
|
printf("%s ATHR_AUTONEG_ADVERT %d :%x\n",__func__,phyAddr, rd_val);
|
|
#endif
|
|
|
|
ar7240_revid = ar7240_reg_rd(AR7240_REV_ID) & AR7240_REV_ID_MASK;
|
|
if(ar7240_revid != AR7240_REV_1_0) {
|
|
s26_wr_phy( phyAddr, ATHR_PHY_CONTROL,ATHR_CTRL_AUTONEGOTIATION_ENABLE
|
|
| ATHR_CTRL_SOFTWARE_RESET);
|
|
}
|
|
|
|
#if S26_PHY_DEBUG
|
|
rd_val = s26_rd_phy(phyAddr,ATHR_AUTONEG_ADVERT );
|
|
rd_val = s26_rd_phy(phyAddr,ATHR_PHY_CONTROL);
|
|
printf("%s ATHR_PHY_CONTROL %d :%x\n",__func__,phyAddr, rd_val);
|
|
#endif
|
|
}
|
|
|
|
if (!foundPhy) {
|
|
return FALSE; /* No PHY's configured for this ethUnit */
|
|
}
|
|
|
|
/*
|
|
* After the phy is reset, it takes a little while before
|
|
* it can respond properly.
|
|
*/
|
|
if(!is_ar933x()) {
|
|
if (ethUnit == ENET_UNIT_LAN)
|
|
sysMsDelay(1000);
|
|
else
|
|
sysMsDelay(3000);
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
for (phyUnit=0; (phyUnit < ATHR_PHY_MAX) /*&& (timeout > 0) */; phyUnit++) {
|
|
if (!ATHR_IS_ETHUNIT(phyUnit, ethUnit)) {
|
|
continue;
|
|
}
|
|
|
|
timeout=20;
|
|
for (;;) {
|
|
phyHwStatus = s26_rd_phy(phyAddr, ATHR_PHY_CONTROL);
|
|
|
|
if (ATHR_RESET_DONE(phyHwStatus)) {
|
|
DRV_PRINT(DRV_DEBUG_PHYSETUP,
|
|
("Port %d, Neg Success\n", phyUnit));
|
|
break;
|
|
}
|
|
if (timeout == 0) {
|
|
DRV_PRINT(DRV_DEBUG_PHYSETUP,
|
|
("Port %d, Negogiation timeout\n", phyUnit));
|
|
break;
|
|
}
|
|
if (--timeout == 0) {
|
|
DRV_PRINT(DRV_DEBUG_PHYSETUP,
|
|
("Port %d, Negogiation timeout\n", phyUnit));
|
|
break;
|
|
}
|
|
|
|
if(!is_ar933x())
|
|
sysMsDelay(150);
|
|
}
|
|
|
|
|
|
#ifdef S26_VER_1_0
|
|
//turn off power saving
|
|
s26_wr_phy(phyUnit, 29, 41);
|
|
s26_wr_phy(phyUnit, 30, 0);
|
|
printf("def_ S26_VER_1_0\n");
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* All PHYs 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.
|
|
*/
|
|
for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) {
|
|
if (!ATHR_IS_ETHUNIT(phyUnit, ethUnit)) {
|
|
continue;
|
|
}
|
|
|
|
if (athrs26_phy_is_link_alive(phyUnit)) {
|
|
liveLinks++;
|
|
ATHR_IS_PHY_ALIVE(phyUnit) = TRUE;
|
|
} else {
|
|
ATHR_IS_PHY_ALIVE(phyUnit) = FALSE;
|
|
}
|
|
DRV_PRINT(DRV_DEBUG_PHYSETUP,
|
|
("eth%d: Phy Specific Status=%4.4x\n",
|
|
ethUnit,
|
|
s26_rd_phy(ATHR_PHYADDR(phyUnit),ATHR_PHY_SPEC_STATUS)));
|
|
}
|
|
|
|
return (liveLinks > 0);
|
|
}
|
|
|
|
/******************************************************************************
|
|
*
|
|
* athrs26_phy_is_fdx - Determines whether the phy ports associated with the
|
|
* specified device are FULL or HALF duplex.
|
|
*
|
|
* RETURNS:
|
|
* 1 --> FULL
|
|
* 0 --> HALF
|
|
*/
|
|
int
|
|
athrs26_phy_is_fdx(int ethUnit)
|
|
{
|
|
int phyUnit;
|
|
uint32_t phyBase;
|
|
uint32_t phyAddr;
|
|
uint16_t phyHwStatus;
|
|
int ii = 200;
|
|
|
|
if (ethUnit == ENET_UNIT_LAN)
|
|
return TRUE;
|
|
|
|
for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) {
|
|
if (!ATHR_IS_ETHUNIT(phyUnit, ethUnit)) {
|
|
continue;
|
|
}
|
|
|
|
if (athrs26_phy_is_link_alive(phyUnit)) {
|
|
|
|
phyBase = ATHR_PHYBASE(phyUnit);
|
|
phyAddr = ATHR_PHYADDR(phyUnit);
|
|
|
|
do {
|
|
phyHwStatus = s26_rd_phy(ATHR_PHYADDR(phyUnit),ATHR_PHY_SPEC_STATUS);
|
|
sysMsDelay(10);
|
|
} while((!(phyHwStatus & ATHR_STATUS_RESOVLED)) && --ii);
|
|
|
|
if (phyHwStatus & ATHER_STATUS_FULL_DEPLEX)
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
*
|
|
* athrs26_phy_speed - Determines the speed of phy ports associated with the
|
|
* specified device.
|
|
*
|
|
* RETURNS:
|
|
* _10BASET, _100BASET;
|
|
* _1000BASET;
|
|
*/
|
|
|
|
int
|
|
athrs26_phy_speed(int ethUnit)
|
|
{
|
|
int phyUnit;
|
|
uint16_t phyHwStatus;
|
|
uint32_t phyBase;
|
|
uint32_t phyAddr;
|
|
int ii = 200;
|
|
|
|
if (ethUnit == ENET_UNIT_LAN)
|
|
return _1000BASET;
|
|
|
|
for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) {
|
|
if (!ATHR_IS_ETHUNIT(phyUnit, ethUnit)) {
|
|
continue;
|
|
}
|
|
|
|
if (athrs26_phy_is_link_alive(phyUnit)) {
|
|
|
|
phyBase = ATHR_PHYBASE(phyUnit);
|
|
phyAddr = ATHR_PHYADDR(phyUnit);
|
|
do {
|
|
phyHwStatus = s26_rd_phy(ATHR_PHYADDR(phyUnit),ATHR_PHY_SPEC_STATUS);
|
|
sysMsDelay(10);
|
|
} while((!(phyHwStatus & ATHR_STATUS_RESOVLED)) && --ii);
|
|
|
|
phyHwStatus = ((phyHwStatus & ATHER_STATUS_LINK_MASK) >>
|
|
ATHER_STATUS_LINK_SHIFT);
|
|
|
|
switch(phyHwStatus) {
|
|
case 0:
|
|
return _10BASET;
|
|
case 1:
|
|
#ifdef CONFIG_MACH_HORNET
|
|
/* For IEEE 100M voltage test */
|
|
s26_wr_phy(phyAddr, ATHR_DEBUG_PORT_ADDRESS, 0x4);
|
|
s26_wr_phy(phyAddr, ATHR_DEBUG_PORT_DATA, 0xebbb);
|
|
s26_wr_phy(phyAddr, ATHR_DEBUG_PORT_ADDRESS, 0x5);
|
|
s26_wr_phy(phyAddr, ATHR_DEBUG_PORT_DATA, 0x2c47);
|
|
#endif /* CONFIG_MACH_HORNET */
|
|
return _100BASET;
|
|
case 2:
|
|
return _1000BASET;
|
|
default:
|
|
printf("Unkown speed read!\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
return _10BASET;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* athr_phy_is_up -- checks for significant changes in PHY state.
|
|
*
|
|
* A "significant change" is:
|
|
* dropped link (e.g. ethernet cable unplugged) OR
|
|
* autonegotiation completed + link (e.g. ethernet cable plugged in)
|
|
*
|
|
* When a PHY is plugged in, phyLinkGained is called.
|
|
* When a PHY is unplugged, phyLinkLost is called.
|
|
*/
|
|
|
|
int
|
|
athrs26_phy_is_up(int ethUnit)
|
|
{
|
|
int phyUnit;
|
|
uint16_t phyHwStatus, phyHwControl;
|
|
athrPhyInfo_t *lastStatus;
|
|
int linkCount = 0;
|
|
int lostLinks = 0;
|
|
int gainedLinks = 0;
|
|
uint32_t phyBase;
|
|
uint32_t phyAddr;
|
|
|
|
for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) {
|
|
if (!ATHR_IS_ETHUNIT(phyUnit, ethUnit)) {
|
|
continue;
|
|
}
|
|
|
|
phyBase = ATHR_PHYBASE(phyUnit);
|
|
phyAddr = ATHR_PHYADDR(phyUnit);
|
|
|
|
lastStatus = &athrPhyInfo[phyUnit];
|
|
|
|
if (lastStatus->isPhyAlive) { /* last known link status was ALIVE */
|
|
|
|
phyHwStatus = s26_rd_phy(ATHR_PHYADDR(phyUnit),ATHR_PHY_SPEC_STATUS);
|
|
|
|
/* See if we've lost link */
|
|
if (phyHwStatus & ATHR_STATUS_LINK_PASS) { /* check realtime link */
|
|
linkCount++;
|
|
} else {
|
|
phyHwStatus = s26_rd_phy(ATHR_PHYADDR(phyUnit),ATHR_PHY_STATUS);
|
|
/* If realtime failed check link in latch register before
|
|
* asserting link down.
|
|
*/
|
|
if (phyHwStatus & ATHR_LATCH_LINK_PASS)
|
|
linkCount++;
|
|
else {
|
|
lostLinks++;
|
|
}
|
|
DRV_PRINT(DRV_DEBUG_PHYCHANGE,("\nenet%d port%d down\n",
|
|
ethUnit, phyUnit));
|
|
lastStatus->isPhyAlive = FALSE;
|
|
}
|
|
} else { /* last known link status was DEAD */
|
|
|
|
/* Check for reset complete */
|
|
|
|
phyHwStatus = s26_rd_phy(ATHR_PHYADDR(phyUnit),ATHR_PHY_STATUS);
|
|
|
|
if (!ATHR_RESET_DONE(phyHwStatus))
|
|
continue;
|
|
|
|
phyHwControl = s26_rd_phy(ATHR_PHYADDR(phyUnit),ATHR_PHY_CONTROL);
|
|
|
|
/* Check for AutoNegotiation complete */
|
|
|
|
if ((!(phyHwControl & ATHR_CTRL_AUTONEGOTIATION_ENABLE))
|
|
|| ATHR_AUTONEG_DONE(phyHwStatus)) {
|
|
phyHwStatus = s26_rd_phy(ATHR_PHYADDR(phyUnit),ATHR_PHY_SPEC_STATUS);
|
|
|
|
if (phyHwStatus & ATHR_STATUS_LINK_PASS) {
|
|
gainedLinks++;
|
|
linkCount++;
|
|
DRV_PRINT(DRV_DEBUG_PHYCHANGE,("\nenet%d port%d up\n",
|
|
ethUnit, phyUnit));
|
|
lastStatus->isPhyAlive = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return (linkCount);
|
|
|
|
#if S26_PHY_DEBUG
|
|
if (linkCount == 0) {
|
|
if (lostLinks) {
|
|
/* We just lost the last link for this MAC */
|
|
phyLinkLost(ethUnit);
|
|
}
|
|
} else {
|
|
if (gainedLinks == linkCount) {
|
|
/* We just gained our first link(s) for this MAC */
|
|
phyLinkGained(ethUnit);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
uint32_t
|
|
athrs26_reg_read(unsigned int s26_addr)
|
|
{
|
|
unsigned int addr_temp;
|
|
unsigned int s26_rd_csr_low, s26_rd_csr_high, s26_rd_csr;
|
|
unsigned int data, unit = 0;
|
|
unsigned int phy_address, reg_address;
|
|
|
|
addr_temp = (s26_addr & 0xfffffffc) >>2;
|
|
data = addr_temp >> 7;
|
|
|
|
phy_address = 0x1f;
|
|
reg_address = 0x10;
|
|
|
|
if (is_ar7240()) {
|
|
unit = 0;
|
|
}
|
|
else if (is_ar7241() || is_ar7242() || is_ar933x()) {
|
|
unit = 1;
|
|
}
|
|
|
|
phy_reg_write(unit,phy_address, reg_address, data);
|
|
|
|
phy_address = (0x17 & ((addr_temp >> 4) | 0x10));
|
|
reg_address = ((addr_temp << 1) & 0x1e);
|
|
s26_rd_csr_low = (uint32_t) phy_reg_read(unit, phy_address, reg_address);
|
|
|
|
reg_address = reg_address | 0x1;
|
|
s26_rd_csr_high = (uint32_t) phy_reg_read(unit, phy_address, reg_address);
|
|
s26_rd_csr = (s26_rd_csr_high << 16) | s26_rd_csr_low ;
|
|
|
|
return(s26_rd_csr);
|
|
}
|
|
|
|
void
|
|
athrs26_reg_write(unsigned int s26_addr, unsigned int s26_write_data)
|
|
{
|
|
unsigned int addr_temp;
|
|
unsigned int data, unit = 0;
|
|
unsigned int phy_address, reg_address;
|
|
|
|
|
|
addr_temp = (s26_addr & 0xfffffffc) >>2;
|
|
data = addr_temp >> 7;
|
|
|
|
phy_address = 0x1f;
|
|
reg_address = 0x10;
|
|
|
|
if (is_ar7240()) {
|
|
unit = 0;
|
|
}
|
|
else if (is_ar7241() || is_ar7242()|| is_ar933x()) {
|
|
unit = 1;
|
|
}
|
|
|
|
#ifdef CONFIG_MACH_HORNET
|
|
//The write sequence , 0x98: L->H, 0x40 H->L, 0x50 H->L , others should not care.
|
|
if(s26_addr!=0x98)
|
|
{
|
|
//printf("[%s:%d] unit=%d\n",__FUNCTION__,__LINE__,unit);
|
|
phy_reg_write(unit, phy_address, reg_address, data);
|
|
|
|
phy_address = 0x17 & ((addr_temp >> 4) | 0x10);
|
|
reg_address = ((addr_temp << 1) & 0x1e) | 0x1;
|
|
data = s26_write_data >> 16;
|
|
phy_reg_write(unit, phy_address, reg_address, data);
|
|
|
|
reg_address = reg_address & 0x1e;
|
|
data = s26_write_data & 0xffff;
|
|
phy_reg_write(unit, phy_address, reg_address, data);
|
|
}
|
|
else
|
|
{
|
|
phy_reg_write(unit, phy_address, reg_address, data);
|
|
|
|
phy_address = (0x17 & ((addr_temp >> 4) | 0x10));
|
|
reg_address = ((addr_temp << 1) & 0x1e);
|
|
|
|
data = s26_write_data & 0xffff;
|
|
phy_reg_write(unit, phy_address, reg_address, data);
|
|
|
|
reg_address = (((addr_temp << 1) & 0x1e) | 0x1);
|
|
data = s26_write_data >> 16;
|
|
phy_reg_write(unit, phy_address, reg_address, data);
|
|
|
|
}
|
|
#else
|
|
phy_reg_write(unit, phy_address, reg_address, data);
|
|
|
|
phy_address = (0x17 & ((addr_temp >> 4) | 0x10));
|
|
reg_address = ((addr_temp << 1) & 0x1e);
|
|
data = s26_write_data & 0xffff;
|
|
phy_reg_write(unit, phy_address, reg_address, data);
|
|
|
|
reg_address = (((addr_temp << 1) & 0x1e) | 0x1);
|
|
data = s26_write_data >> 16;
|
|
phy_reg_write(unit, phy_address, reg_address, data);
|
|
#endif
|
|
}
|
|
|
|
|
|
unsigned int s26_rd_phy(unsigned int phy_addr, unsigned int reg_addr)
|
|
{
|
|
|
|
unsigned int rddata;
|
|
|
|
// MDIO_CMD is set for read
|
|
|
|
rddata = athrs26_reg_read(0x98);
|
|
rddata = (rddata & 0x0) | (reg_addr<<16) | (phy_addr<<21) | (1<<27) | (1<<30) | (1<<31) ;
|
|
athrs26_reg_write(0x98, rddata);
|
|
|
|
rddata = athrs26_reg_read(0x98);
|
|
rddata = rddata & (1<<31);
|
|
|
|
// Check MDIO_BUSY status
|
|
while(rddata){
|
|
rddata = athrs26_reg_read(0x98);
|
|
rddata = rddata & (1<<31);
|
|
}
|
|
|
|
|
|
// Read the data from phy
|
|
|
|
rddata = athrs26_reg_read(0x98) & 0xffff;
|
|
|
|
return(rddata);
|
|
}
|
|
|
|
void s26_wr_phy(unsigned int phy_addr, unsigned int reg_addr, unsigned int write_data)
|
|
{
|
|
unsigned int rddata;
|
|
|
|
// MDIO_CMD is set for read
|
|
|
|
rddata = athrs26_reg_read(0x98);
|
|
rddata = (rddata & 0x0) | (write_data & 0xffff) | (reg_addr<<16) | (phy_addr<<21) | (0<<27) | (1<<30) | (1<<31) ;
|
|
athrs26_reg_write(0x98, rddata);
|
|
|
|
rddata = athrs26_reg_read(0x98);
|
|
rddata = rddata & (1<<31);
|
|
|
|
// Check MDIO_BUSY status
|
|
while(rddata){
|
|
rddata = athrs26_reg_read(0x98);
|
|
rddata = rddata & (1<<31);
|
|
}
|
|
|
|
}
|
|
|
|
int
|
|
athrs26_mdc_check()
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<4000; i++) {
|
|
if(athrs26_reg_read(0x10c) != 0x18007fff)
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|