blob: d5b6d487af765053dd5f4de0f76c309590ffa1e5 [file] [log] [blame]
/*
* Copyright (c) 2013 Qualcomm Atheros, Inc.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
/*
* 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 <atheros.h>
#include "athrs17_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_GE0 0
#define ENET_UNIT_GE1 1
#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 ATHR_IND_PHY 4
#define MODULE_NAME "ATHRS17"
#define S17_PHY_DEBUG 1
extern int xmii_val;
/*
* 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;
#if defined(ATH_S17_MAC0_SGMII)
#if (CFG_ATH_GMAC_NMACS == 1) /* QCA9563 only have 1 GMAC working */
#define ENET_UNIT ENET_UNIT_GE0
#define ENET_UNIT_WAN ENET_UNIT_GE0
#else
#define ENET_UNIT ENET_UNIT_GE1
#define ENET_UNIT_WAN ENET_UNIT_GE0
#endif
#else
#define ENET_UNIT ENET_UNIT_GE0
#define ENET_UNIT_WAN ENET_UNIT_GE1
#endif
/*
* Per-PHY information, indexed by PHY unit number.
*/
static athrPhyInfo_t athrPhyInfo[] = {
{
TRUE, /* phy port 0 -- LAN port 0 */
FALSE,
ENET_UNIT,
0,
ATHR_PHY0_ADDR,
ATHR_LAN_PORT_VLAN
},
{
TRUE, /* phy port 1 -- LAN port 1 */
FALSE,
ENET_UNIT,
0,
ATHR_PHY1_ADDR,
ATHR_LAN_PORT_VLAN
},
{
TRUE, /* phy port 2 -- LAN port 2 */
FALSE,
ENET_UNIT,
0,
ATHR_PHY2_ADDR,
ATHR_LAN_PORT_VLAN
},
{
TRUE, /* phy port 3 -- LAN port 3 */
FALSE,
ENET_UNIT,
0,
ATHR_PHY3_ADDR,
ATHR_LAN_PORT_VLAN
},
{
TRUE, /* phy port 4 -- WAN port or LAN port 4 */
FALSE,
#if defined(CONFIG_ATH_S17_WAN) || defined (ATH_S17_MAC0_SGMII)
ENET_UNIT_WAN,
#else
ENET_UNIT,
#endif
0,
ATHR_PHY4_ADDR,
ATHR_LAN_PORT_VLAN /* Send to all ports */
},
{
FALSE, /* phy port 5 -- CPU port (no RJ45 connector) */
TRUE,
ENET_UNIT,
0,
0x00,
ATHR_LAN_PORT_VLAN /* Send to all ports */
},
};
static uint8_t athr17_init_flag = 0;
//#define ATHR_PHY_MAX (sizeof(ipPhyInfo) / sizeof(ipPhyInfo[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_GE0))
/* Forward references */
BOOL athrs17_phy_is_link_alive(int phyUnit);
uint32_t athrs17_reg_read(uint32_t reg_addr);
void athrs17_reg_write(uint32_t reg_addr, uint32_t reg_val);
static void phy_mode_setup(void);
#define sysMsDelay(_x) udelay((_x) * 1000)
static void phy_mode_setup(void)
{
#ifdef ATHRS17_VER_1_0
/*work around for phy4 rgmii mode*/
phy_reg_write(ATHR_PHYBASE(ATHR_IND_PHY), ATHR_PHYADDR(ATHR_IND_PHY), 29, 18);
phy_reg_write(ATHR_PHYBASE(ATHR_IND_PHY), ATHR_PHYADDR(ATHR_IND_PHY), 30, 0x480c);
/*rx delay*/
phy_reg_write(ATHR_PHYBASE(ATHR_IND_PHY), ATHR_PHYADDR(ATHR_IND_PHY), 29, 0);
phy_reg_write(ATHR_PHYBASE(ATHR_IND_PHY), ATHR_PHYADDR(ATHR_IND_PHY), 30, 0x824e);
/*tx delay*/
phy_reg_write(ATHR_PHYBASE(ATHR_IND_PHY), ATHR_PHYADDR(ATHR_IND_PHY), 29, 5);
phy_reg_write(ATHR_PHYBASE(ATHR_IND_PHY), ATHR_PHYADDR(ATHR_IND_PHY), 30, 0x3d47);
#endif
}
/*
* V-lan configuration given by Switch team
* Vlan 1:PHY0,1,2,3 and Mac 0 of s17
* Vlam 2:PHY4 and Mac 6 of s17
*/
void athrs17_vlan_config()
{
athrs17_reg_write(S17_P0LOOKUP_CTRL_REG, 0x0014001e);
athrs17_reg_write(S17_P0VLAN_CTRL0_REG, 0x10001);
athrs17_reg_write(S17_P1LOOKUP_CTRL_REG, 0x0014001d);
athrs17_reg_write(S17_P1VLAN_CTRL0_REG, 0x10001);
athrs17_reg_write(S17_P2LOOKUP_CTRL_REG, 0x0014001b);
athrs17_reg_write(S17_P2VLAN_CTRL0_REG, 0x10001);
athrs17_reg_write(S17_P3LOOKUP_CTRL_REG, 0x00140017);
athrs17_reg_write(S17_P3VLAN_CTRL0_REG, 0x10001);
athrs17_reg_write(S17_P4LOOKUP_CTRL_REG, 0x0014000f);
athrs17_reg_write(S17_P4VLAN_CTRL0_REG, 0x10001);
athrs17_reg_write(S17_P5LOOKUP_CTRL_REG, 0x00140040);
athrs17_reg_write(S17_P5VLAN_CTRL0_REG, 0x20001);
athrs17_reg_write(S17_P6LOOKUP_CTRL_REG, 0x00140020);
athrs17_reg_write(S17_P6VLAN_CTRL0_REG, 0x20001);
}
void athrs17_reg_init_wan(void)
{
#ifdef ATH_S17_MAC0_SGMII
athrs17_reg_write(S17_P6PAD_MODE_REG,0x07600000);
#else
athrs17_reg_write(S17_P6PAD_MODE_REG,
athrs17_reg_read(S17_P6PAD_MODE_REG)|S17_MAC6_SGMII_EN);
#endif
athrs17_reg_write(S17_P6STATUS_REG, S17_PORT_STATUS_AZ_DEFAULT);
athrs17_reg_write(S17_SGMII_CTRL_REG , 0xc74164d0); /* SGMII control */
athrs17_vlan_config();
printf("%s done\n",__func__);
}
void athrs17_reg_init()
{
int phy_addr = 0;
uint32_t sgmii_ctrl_value;
/* if using header for register configuration, we have to */
/* configure s17 register after frame transmission is enabled */
if ((athrs17_reg_read(S17_MASK_CTRL_REG) & 0xFFFF) == S17C_DEVICEID){
sgmii_ctrl_value = 0xc74164de;
}else{
sgmii_ctrl_value = 0xc74164d0;
}
if (athr17_init_flag){
return;
}
#if (CFG_ATH_GMAC_NMACS == 1)
athrs17_reg_write(S17_P0PAD_MODE_REG, S17_MAC0_SGMII_EN);
athrs17_reg_write(S17_SGMII_CTRL_REG , sgmii_ctrl_value); /* SGMII control */
athrs17_reg_write(S17_GLOFW_CTRL1_REG, 0x7f7f7f7f);
#else
if (is_drqfn()) {
athrs17_reg_write(S17_P0PAD_MODE_REG, S17_MAC0_SGMII_EN);
athrs17_reg_write(S17_SGMII_CTRL_REG , sgmii_ctrl_value); /* SGMII control */
} else {
athrs17_reg_write(S17_GLOFW_CTRL1_REG, 0x7f7f7f7f);
/*
* If defined S17 Mac0 sgmii val of 0x4(S17_P0PAD_MODE_REG)
* should be configured as 0x80
*/
#ifdef ATH_S17_MAC0_SGMII
athrs17_reg_write(S17_P0PAD_MODE_REG, 0x80080);
#else
athrs17_reg_write(S17_P0PAD_MODE_REG, 0x07680000);
#endif
athrs17_reg_write(S17_P6PAD_MODE_REG, 0x01000000);
}
#endif /* CFG_ATH_GMAC_NMACS == 1 */
/*
* Values suggested by the swich team when s17 in sgmii configuration
* operates in forced mode.
* 0x10(S17_PWS_REG)=0x602613a0
*/
#ifdef ATH_SGMII_FORCED_MODE
athrs17_reg_write(S17_PWS_REG, 0x602613a0);
#else
athrs17_reg_write(S17_PWS_REG, 0x40000000);
#endif
athrs17_reg_write(S17_P0STATUS_REG, 0x0000007e);
/* AR8327/AR8328 v1.0 fixup */
if ((athrs17_reg_read(0x0) & 0xffff) == 0x1201) {
for (phy_addr = 0x0; phy_addr <= ATHR_PHY_MAX; phy_addr++) {
/* For 100M waveform */
phy_reg_write(0, phy_addr, 0x1d, 0x0);
phy_reg_write(0, phy_addr, 0x1e, 0x02ea);
/* Turn On Gigabit Clock */
phy_reg_write(0, phy_addr, 0x1d, 0x3d);
phy_reg_write(0, phy_addr, 0x1e, 0x68a0);
}
}
#if CONFIG_S17_SWMAC6_CONNECTED
printf ("Configuring Mac6 of s17 to slave scorpion\n");
athrs17_reg_write(S17_P6PAD_MODE_REG, S17_MAC6_RGMII_EN | S17_MAC6_RGMII_TXCLK_DELAY | \
S17_MAC6_RGMII_RXCLK_DELAY | (1 << S17_MAC6_RGMII_TXCLK_SHIFT) | \
(2 << S17_MAC6_RGMII_RXCLK_SHIFT));
athrs17_reg_write(S17_P6STATUS_REG, 0x7e);
athrs17_vlan_config();
#endif
athr17_init_flag = 1;
printf("%s: complete\n",__func__);
}
/******************************************************************************
*
* 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(int phyUnit)
{
uint16_t phyHwStatus;
uint32_t phyBase;
uint32_t phyAddr;
phyBase = ATHR_PHYBASE(phyUnit);
phyAddr = ATHR_PHYADDR(phyUnit);
phyHwStatus = phy_reg_read(phyBase, phyAddr, ATHR_PHY_SPEC_STATUS);
if (phyHwStatus & ATHR_STATUS_LINK_PASS)
return TRUE;
return FALSE;
}
/******************************************************************************
*
* athrs17_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
athrs17_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;
/* See if there's any configuration data for this enet */
/* start auto negogiation on each phy */
if (is_drqfn())
ethUnit=0;
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);
phy_reg_write(phyBase, phyAddr, ATHR_AUTONEG_ADVERT,
ATHR_ADVERTISE_ALL);
phy_reg_write(phyBase, phyAddr, ATHR_1000BASET_CONTROL,
ATHR_ADVERTISE_1000FULL);
/* Reset PHYs*/
phy_reg_write(phyBase, phyAddr, ATHR_PHY_CONTROL,
ATHR_CTRL_AUTONEGOTIATION_ENABLE
| ATHR_CTRL_SOFTWARE_RESET);
}
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.
*/
sysMsDelay(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.
*/
for (phyUnit=0; (phyUnit < ATHR_PHY_MAX) /*&& (timeout > 0) */; phyUnit++) {
if (!ATHR_IS_ETHUNIT(phyUnit, ethUnit)) {
continue;
}
timeout=20;
for (;;) {
phyHwStatus = phy_reg_read(phyBase, 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;
}
sysMsDelay(150);
}
}
/*
* 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 0
/* Enable RGMII */
phy_reg_write(0,phyUnit,0x1d,0x12);
phy_reg_write(0,phyUnit,0x1e,0x8);
/* Tx delay on PHY */
phy_reg_write(0,phyUnit,0x1d,0x5);
phy_reg_write(0,phyUnit,0x1e,0x100);
/* Rx delay on PHY */
phy_reg_write(0,phyUnit,0x1d,0x0);
phy_reg_write(0,phyUnit,0x1e,0x8000);
#endif
if (athrs17_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,
phy_reg_read(ATHR_PHYBASE(phyUnit),
ATHR_PHYADDR(phyUnit),
ATHR_PHY_SPEC_STATUS)));
}
phy_mode_setup();
return (liveLinks > 0);
}
/******************************************************************************
*
* athrs17_phy_is_fdx - Determines whether the phy ports associated with the
* specified device are FULL or HALF duplex.
*
* RETURNS:
* 1 --> FULL
* 0 --> HALF
*/
int
athrs17_phy_is_fdx(int ethUnit)
{
int phyUnit;
uint32_t phyBase;
uint32_t phyAddr;
uint16_t phyHwStatus;
int ii = 200;
if (is_drqfn())
ethUnit = 0;
for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) {
if (!ATHR_IS_ETHUNIT(phyUnit, ethUnit)) {
continue;
}
if (athrs17_phy_is_link_alive(phyUnit)) {
phyBase = ATHR_PHYBASE(phyUnit);
phyAddr = ATHR_PHYADDR(phyUnit);
do {
phyHwStatus = phy_reg_read (phyBase, phyAddr,
ATHR_PHY_SPEC_STATUS);
if(phyHwStatus & ATHR_STATUS_RESOVLED)
break;
sysMsDelay(10);
} while(--ii);
if (phyHwStatus & ATHER_STATUS_FULL_DEPLEX)
return TRUE;
}
}
if (ethUnit == ENET_UNIT_GE0 || ethUnit == ENET_UNIT_GE1)
return TRUE;
return FALSE;
}
/******************************************************************************
*
* athrs17_phy_speed - Determines the speed of phy ports associated with the
* specified device.
*
* RETURNS: _10BASET, _100BASETX, _1000BASET
*/
int
athrs17_phy_speed(int ethUnit)
{
int phyUnit;
uint16_t phyHwStatus;
uint32_t phyBase;
uint32_t phyAddr;
int ii = 200;
if ((ethUnit == ENET_UNIT_GE0) || (ethUnit == ENET_UNIT_GE1))
return _1000BASET;
for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) {
if (!ATHR_IS_ETHUNIT(phyUnit, ethUnit)) {
continue;
}
if (athrs17_phy_is_link_alive(phyUnit)) {
phyBase = ATHR_PHYBASE(phyUnit);
phyAddr = ATHR_PHYADDR(phyUnit);
do {
phyHwStatus = phy_reg_read(phyBase, phyAddr,
ATHR_PHY_SPEC_STATUS);
if(phyHwStatus & ATHR_STATUS_RESOVLED)
break;
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: 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
athrs17_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;
if (is_drqfn())
ethUnit = 0;
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 = phy_reg_read(phyBase, phyAddr, ATHR_PHY_SPEC_STATUS);
/* See if we've lost link */
if (phyHwStatus & ATHR_STATUS_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 = phy_reg_read(phyBase, phyAddr, ATHR_PHY_STATUS);
if (!ATHR_RESET_DONE(phyHwStatus)) {
continue;
}
phyHwControl = phy_reg_read(phyBase, phyAddr, ATHR_PHY_CONTROL);
/* Check for AutoNegotiation complete */
if ((!(phyHwControl & ATHR_CTRL_AUTONEGOTIATION_ENABLE))
|| ATHR_AUTONEG_DONE(phyHwStatus)) {
phyHwStatus = phy_reg_read(phyBase, phyAddr,
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);
}
uint32_t
athrs17_reg_read(uint32_t 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) & 0x1ff); /* bit16-8 of reg address */
phy_reg_write(0, 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 */
reg_val = (uint32_t) phy_reg_read(0, phy_addr, phy_reg);
/* 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 */
tmp_val = (uint32_t) phy_reg_read(0, phy_addr, phy_reg);
reg_val |= (tmp_val << 16);
return reg_val;
}
void
athrs17_reg_write(uint32_t reg_addr, uint32_t reg_val)
{
uint32_t reg_word_addr;
uint32_t phy_addr;
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) & 0x1ff); /* bit16-8 of reg address */
phy_reg_write(0, phy_addr, phy_reg, phy_val);
/* For some registers such as ARL and VLAN, since they include BUSY bit */
/* in lower address, we should write the higher 16-bit register then the */
/* lower one */
/* 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);
phy_reg_write(0, phy_addr, phy_reg, phy_val);
/* write register in lower 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 & 0xffff);
phy_reg_write(0, phy_addr, phy_reg, phy_val);
}
unsigned int s17_rd_phy(unsigned int phy_addr, unsigned int reg_addr)
{
return ((uint32_t) phy_reg_read(0, phy_addr, reg_addr));
}
void s17_wr_phy(unsigned int phy_addr, unsigned int reg_addr, unsigned int write_data)
{
phy_reg_write(0, phy_addr, reg_addr, write_data);
}