blob: e5e9f61b7a1eed5e566b8fa34a3e56bef39e049b [file] [log] [blame]
/*
* board/ums/ar8237.c
*
* U-Boot driver for the Atheros 8237 switch.
*
* Copyright (c) Quantenna Communications Incorporated 2009.
* All rights reserved.
*
* 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
*
*/
/********************************************************************
Atheros 8237 MDIO programming details
The Arasan MDIO IP on our chip uses two registers to program
MDIO.
data[16:0]
control
1 1111 1 00000 00000
5 4321 0 98765 43210
- ---- -- ----- -----
st xxxx op reg phy
st: 1 - start, poll for completion
op: 1 - read, 0 write
These are encoded into the serial MDIO protocol as follows
IEEE MDIO frame
33 22 22222 22211 11 1111110000000000
10 98 76543 21098 76 5432109876543210
-- -- ----- ----- -- ----------------
ST OP PHY REG TA DATA[15:0]
TA and ST are encoded automatically by Arasan IP.
This device uses 18 bits to specify register addresses.
These bits are programmed into the device by paging as follows.
aaaaaaaaa aaaaaaaa aa
111111111 00000000 00
876543210 98765432 10
--------- -------- ------
page addr reg addr ignore
Since the registers are all 32-bit, the lower two address
bits are discarded. The page is written first using the
following format. Note PHY is limited to three bits.
8213 Page program command
-----------------------------------------------
33 22 22 222 22211 11 111111 0000000000
10 98 76 543 21098 76 543210 9876543210
-- -- -- --- ----- -- ------ ----------
ST OP CMD PHY xxxxx TA xxxxxx page addr
CMD: 11 - page address write
CMD: 10 - reg access
The tricky part is the reg access step following page programming
Since the register format of arasan swaps the order of reg and
phy, and since our register address spans these two fields, we
have to swizzle the bits into place.
8213 Reg read/write command
------------------------------------------------
33 22 22 2222221 1 11 1111110000000000
10 98 76 5432109 8 76 5432109876543210
-- -- -- ------- - -- ----------------
ST OP CMD reg adr W TA DATA[15:0]
W: 0 - lower 16 bits, 1: upper 16 bits
Programming this operation into Arasan requires
phy = 'b10 << 3 | regAddr[9:7]
reg = regAddr[6:2]
mdioCmd = phy | reg << 5 | op | start
********************************************************************/
////////////////////////////////////////////////////////////////////
// NOTE - we do not check for valid base in mdio access routines
// use must ensure device is initialized and valid prior
// to using MDIO funtions
///////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Includes
////////////////////////////////////////////////////////////////////
#include "ruby.h"
#include <common.h>
#include <malloc.h>
#include <net.h>
#include <asm/io.h>
#include <asm/arch/platform.h>
#include <asm/arch/arasan_emac_ahb.h>
#include "ar8237.h"
////////////////////////////////////////////////////////////////////
// Defines
////////////////////////////////////////////////////////////////////
#define AR8237_MASK_CTL (0)
#define AR8237_MASK_CTL_RESET (0x80000000)
#define AR8237_MASK_CLEAR_DEF (0)
#define AR8237_MODE_CTRL (0x04)
#define S17_MAC0_RGMII_RXCLK_SHIFT 20
#define S17_MAC0_RGMII_TXCLK_SHIFT 22
#define S17_MAC0_RGMII_TXCLK_DELAY_EN (1 << 25)
#define S17_MAC0_RGMII_EN (1 << 26)
#define AR8237_MODE_RGMII_PHY (S17_MAC0_RGMII_EN)
//#define AR8237_MODE_RGMII_PHY (0x07600000)
//#define AR8237_MODE_RGMII_PHY (0x07402000)
#define AR8237_PWS_CTRL (0x10)
#define AR8237_PWS_CTRL_DEF (0x40000000)
#define AR8237_FWCTL_CTRL (0x0624)
#define AR8237_FWCTL_CTRL_DEF (0x007f7f7f)
#define S17_P0STATUS_REG 0x007c
#define S17_P5STATUS_REG 0x0090
#define S17_P6STATUS_REG 0x0094
#define AR8237_PORT6_CTRL (0xc)
#define S17_PHY4_RGMII_EN (1 << 17)
#define S17_MAC6_RGMII_EN (1 << 26)
#define AR8237_PORT6_CTRL_DEF (S17_PHY4_RGMII_EN)
#define AR8237_PORT0_CTRL (0x7c)
#define AR8237_PORT0_CTRL_DEF (0x7e)
#define AR8237_MDIO_START (1 << 15)
#define AR8237_MDIO_WRITE (0 << 10)
#define AR8237_MDIO_READ (1 << 10)
#define AR8237_MDIO_HIGH_WORD (1 << 0)
#define AR8237_MDIO_TIMEOUT (0x1000)
#define AR8237_MDIO_PAGE (0x18)
#define AR8237_MDIO_NORM (0x10)
#define AR8237_MIN_PHY_NUM (0)
#define AR8237_MAX_PHY_NUM (4)
#define S17_P5PAD_MODE_REG 0x0008
#define S17_MAC_RGMII_RXCLK_DELAY_EN (1 << 24)
#define S17_SPEED_10M (0 << 0)
#define S17_SPEED_100M (1 << 0)
#define S17_SPEED_1000M (2 << 0)
#define S17_TXMAC_EN (1 << 2)
#define S17_RXMAC_EN (1 << 3)
#define S17_TX_FLOW_EN (1 << 4)
#define S17_RX_FLOW_EN (1 << 5)
#define S17_DUPLEX_FULL (1 << 6)
#define S17_DUPLEX_HALF (0 << 6)
#define S17_TX_HALF_FLOW_EN (1 << 7)
#define S17_LINK_EN (1 << 9)
#define S17_FLOW_LINK_EN (1 << 12)
#define S17_PORT_STATUS_DEFAULT (S17_SPEED_1000M | S17_TXMAC_EN | \
S17_RXMAC_EN | \
S17_DUPLEX_FULL )
////////////////////////////////////////////////////////////////////
// Types
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Data
////////////////////////////////////////////////////////////////////
static ts8237 g8237Dev;
extern int mdc_clk_divisor;
////////////////////////////////////////////////////////////////////
// Functions
////////////////////////////////////////////////////////////////////
u32 ar8237_emac_rdreg(int reg)
{
return *(volatile u32 *)(g8237Dev.base + reg);
}
void ar8237_emac_wrreg(int reg, u32 val)
{
*(volatile u32 *)(g8237Dev.base + reg) = val;
}
/*********************************************************************
Name: ar8237_mdio_poll
Purpose: mdio poll routine for AR8237 device
Notes: Checks for mdio operation complete
*********************************************************************/
int ar8237_mdio_poll(void)
{
u32 timeout = AR8237_MDIO_TIMEOUT;
// check for clear MDIO status
while (timeout--) {
int status = ar8237_emac_rdreg(EMAC_MAC_MDIO_CTRL);
if ((status & AR8237_MDIO_START) == 0) {
break;
}
}
if (timeout == 0) {
printf("ar8237 mdio timeout\n");
return -1;
}
return 0;
}
int ar8237_normal_mdio_write(u16 phyAddr, u8 regAddr, u16 data)
{
// check for clear MDIO status
if (ar8237_mdio_poll() != 0) {
return -1;
}
ar8237_emac_wrreg(EMAC_MAC_MDIO_DATA, data & 0xffff);
ar8237_emac_wrreg(EMAC_MAC_MDIO_CTRL,
((mdc_clk_divisor & MacMdioCtrlClkMask) << MacMdioCtrlClkShift) |
(regAddr << 5) | (phyAddr & 0x1f) |
AR8237_MDIO_START);
if (ar8237_mdio_poll() != 0) {
return -1;
}
// return without waiting for final completion
return 0;
}
int ar8237_normal_mdio_read(u16 phyAddr, u8 regAddr, u16 *data)
{
if (ar8237_mdio_poll() != 0) {
return -1;
}
ar8237_emac_wrreg(EMAC_MAC_MDIO_CTRL, ( (regAddr & 0x1f) << 5) | AR8237_MDIO_START |
((mdc_clk_divisor & MacMdioCtrlClkMask) << MacMdioCtrlClkShift) |
AR8237_MDIO_READ | (phyAddr & 0x1f));
if (ar8237_mdio_poll() != 0) {
return -1;
}
*data = ar8237_emac_rdreg(EMAC_MAC_MDIO_DATA);
return 0;
}
int __ar8237_mdio_read(u32 phyAddr, u32 regAddr, u32 * data);
/*********************************************************************
Name: ar8237_mdio_write
Purpose: mdio write routine for AR8237 device
Notes: This is a partial blocking call since we require
more than one cycle to complete the write.
checks for completion first
*********************************************************************/
int ar8237_mdio_write(u32 phyAddr, u32 regAddr, u32 data)
{
u32 highAddr = regAddr >> 9;
// need to swizzle the bits into arasan's fields which are different
u32 rg = (regAddr & 0x3c) >> 1;
u32 ph = (regAddr & 0x1c0) >> 6;
u32 prev;
__ar8237_mdio_read(phyAddr, regAddr, &prev);
// check for clear MDIO status
if (ar8237_mdio_poll() != 0) {
return -1;
}
ar8237_emac_wrreg(EMAC_MAC_MDIO_DATA, highAddr);
ar8237_emac_wrreg(EMAC_MAC_MDIO_CTRL, (phyAddr & 0x1f) |
((mdc_clk_divisor & MacMdioCtrlClkMask) << MacMdioCtrlClkShift) |
AR8237_MDIO_START | AR8237_MDIO_PAGE);
// wait for completion
if (ar8237_mdio_poll() != 0) {
return -1;
}
ar8237_emac_wrreg(EMAC_MAC_MDIO_DATA, (data >> 16) & 0xffff);
ar8237_emac_wrreg(EMAC_MAC_MDIO_CTRL,
((mdc_clk_divisor & MacMdioCtrlClkMask) << MacMdioCtrlClkShift) |
((rg | AR8237_MDIO_HIGH_WORD) << 5) | ph |
AR8237_MDIO_START | AR8237_MDIO_NORM);
if (ar8237_mdio_poll() != 0) {
return -1;
}
ar8237_emac_wrreg(EMAC_MAC_MDIO_DATA, data & 0xffff);
ar8237_emac_wrreg(EMAC_MAC_MDIO_CTRL,
((mdc_clk_divisor & MacMdioCtrlClkMask) << MacMdioCtrlClkShift) |
(rg << 5) | ph | AR8237_MDIO_START |
AR8237_MDIO_NORM);
if (ar8237_mdio_poll() != 0) {
return -1;
}
// printf("%s reg 0x%04x data 0x%08x previously %08x\n", __func__, regAddr, data, prev);
// return without waiting for final completion
return 0;
}
/*********************************************************************
Name: ar8237_mdio_read
Purpose: mdio read routine for AR8237 device
Notes: This is a blocking call since we require
more than one cycle to complete the write.
checks for completion first
*********************************************************************/
int __ar8237_mdio_read(u32 phyAddr, u32 regAddr, u32 * data)
{
u32 highAddr = regAddr >> 9;
// need to swizzle the bits into arasan's fields which are different
u32 rg = (regAddr & 0x3c) >> 1;
u32 ph = (regAddr & 0x1c0) >> 6;
if (ar8237_mdio_poll() != 0) {
return -1;
}
ar8237_emac_wrreg(EMAC_MAC_MDIO_DATA, highAddr);
ar8237_emac_wrreg(EMAC_MAC_MDIO_CTRL, phyAddr | AR8237_MDIO_START |
((mdc_clk_divisor & MacMdioCtrlClkMask) << MacMdioCtrlClkShift) |
AR8237_MDIO_PAGE);
if (ar8237_mdio_poll() != 0) {
return -1;
}
ar8237_emac_wrreg(EMAC_MAC_MDIO_CTRL, (rg << 5) | AR8237_MDIO_START |
((mdc_clk_divisor & MacMdioCtrlClkMask) << MacMdioCtrlClkShift) |
AR8237_MDIO_READ | ph | AR8237_MDIO_NORM);
if (ar8237_mdio_poll() != 0) {
return -1;
}
*data = ar8237_emac_rdreg(EMAC_MAC_MDIO_DATA);
ar8237_emac_wrreg(EMAC_MAC_MDIO_CTRL,
((mdc_clk_divisor & MacMdioCtrlClkMask) << MacMdioCtrlClkShift) |
((rg | AR8237_MDIO_HIGH_WORD) << 5) |
AR8237_MDIO_START | AR8237_MDIO_READ | ph |
AR8237_MDIO_NORM);
if (ar8237_mdio_poll() != 0) {
return -1;
}
*data = *data | (ar8237_emac_rdreg(EMAC_MAC_MDIO_DATA) << 16);
return 0;
}
int ar8237_mdio_read(u32 phyAddr, u32 regAddr, u32 * data) {
int rc;
rc = __ar8237_mdio_read(phyAddr, regAddr, data);
if (!rc) {
// printf("%s reg 0x%04x data 0x%08x\n", __func__, regAddr, *data);
} else {
printf("%s failed reg 0x%04x data 0x%08x\n", __func__, regAddr, *data);
}
return rc;
}
/*********************************************************************
Name: ar8237_init
Purpose: Check for Atheros 8237 switch, return pointer to device
if found, NULL otherwise
Notes: pass phy addr as -1 to scan for phy
*********************************************************************/
u32 ar8237_init(u32 baseAddr, u32 phy_addr)
{
u32 addr;
u32 devID;
u16 val;
g8237Dev.base = baseAddr;
// need to scan?
if (phy_addr >=EMAC_PHY_ADDR_SCAN) {
addr = AR8237_MIN_PHY_NUM;
} else {
if (phy_addr >= AR8237_MAX_PHY_NUM) {
printf("config error: phy addr out of range\n");
return EMAC_PHY_ADDR_SCAN;
}
addr = phy_addr;
}
while (addr < AR8237_MAX_PHY_NUM) {
uint32_t reset = AR8237_MASK_CTL_RESET;
ar8237_mdio_read(addr, AR8237_MASK_CTL, &devID);
if ((devID & 0xff00) == 0x1300) {
printf("Detected AR8337 Switch %d - set for RGMII, 1000FD devID 0x%x\n",addr, devID);
// do a softreset
// do a clean reset and wait for completion
ar8237_mdio_write(phy_addr, AR8237_MASK_CTL, AR8237_MASK_CTL_RESET);
while (reset & AR8237_MASK_CTL_RESET) {
ar8237_mdio_read(addr, AR8237_MASK_CTL, &reset);
}
ar8237_mdio_write(phy_addr, AR8237_MODE_CTRL, AR8237_MODE_RGMII_PHY);
ar8237_mdio_write(phy_addr, AR8237_MASK_CTL, AR8237_MASK_CLEAR_DEF);
ar8237_mdio_write(phy_addr, AR8237_FWCTL_CTRL, AR8237_FWCTL_CTRL_DEF);
ar8237_mdio_write(phy_addr, AR8237_PORT6_CTRL, AR8237_PORT6_CTRL_DEF);
/* Disable MAC5 and MAC6 (due to PHY4), QCA */
ar8237_mdio_write(phy_addr, S17_P5STATUS_REG, 0);
ar8237_mdio_write(phy_addr, S17_P6STATUS_REG, 0);
ar8237_mdio_write(phy_addr, S17_P0STATUS_REG, S17_PORT_STATUS_DEFAULT);
ar8237_normal_mdio_write(4, 29, 0x12);
ar8237_normal_mdio_read(4, 30, &val);
val |= (1<<3);
ar8237_normal_mdio_write(4, 30, val);
ar8237_normal_mdio_write(4, 29, 0);
g8237Dev.phy = addr;
return addr;
}
if (phy_addr == MAX_PHY_ADDR) {
addr++;
} else {
// not found on passed addr
break;
}
}
printf("Error: AR8237 not found\n");
return EMAC_PHY_ADDR_SCAN;
}