| /* |
| * 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; |
| } |