/* * board/ums/ar8236.c * * U-Boot driver for the Atheros 8236 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 8236 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 #include #include #include #include #include #include "ar8236.h" //////////////////////////////////////////////////////////////////// // Defines //////////////////////////////////////////////////////////////////// #define AR8236_MODE_CTRL (0x04) #define AR8236_MODE_MII_PHY (0x80000600) #define AR8236_MODE_MII_MAC (0x80000004) #define AR8236_MASK_CTL (0) #define AR8236_MASK_CTL_RESET (0x80000000) #define AR8236_FLOOD_MASK (0x2c) #define AR8236_FLOOD_MASK_DEF (0xfe7f007f) #define AR8236_PORT0_CTRL (0x100) #define AR8236_PORT0_CTRL_DEF (0x7d) #define AR8236_FLOW_LINK_EN (0x1000) #define AR8236_MDIO_START (1 << 15) #define AR8236_MDIO_WRITE (0 << 10) #define AR8236_MDIO_READ (1 << 10) #define AR8236_MDIO_HIGH_WORD (1 << 0) #define AR8236_MDIO_TIMEOUT (0x1000) #define AR8236_MDIO_PAGE (0x18) #define AR8236_MDIO_NORM (0x10) #define AR8236_ARP_LEAKY_EN (1 << 22) #define AR8236_LEARN_EN (1 << 14) #define AR8236_FORWARD (1 << 2) #define AR8236_MIN_PHY_NUM (0) #define AR8236_MAX_PHY_NUM (4) //////////////////////////////////////////////////////////////////// // Types //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// // Data //////////////////////////////////////////////////////////////////// static ts8236 g8236Dev; //////////////////////////////////////////////////////////////////// // Functions //////////////////////////////////////////////////////////////////// inline u32 ar8236_emac_rdreg(int reg) { return *(volatile u32 *)(g8236Dev.base + reg); } inline void ar8236_emac_wrreg(int reg, u32 val) { *(volatile u32 *)(g8236Dev.base + reg) = val; } /********************************************************************* Name: ar8236_mdio_poll Purpose: mdio poll routine for AR8236 device Notes: Checks for mdio operation complete *********************************************************************/ int ar8236_mdio_poll(void) { u32 timeout = AR8236_MDIO_TIMEOUT; // check for clear MDIO status while (timeout--) { int status = ar8236_emac_rdreg(EMAC_MAC_MDIO_CTRL); if ((status & AR8236_MDIO_START) == 0) { break; } } if (timeout == 0) { printf("ar8236 mdio timeout\n"); return -1; } return 0; } /********************************************************************* Name: ar8236_mdio_write Purpose: mdio write routine for AR8236 device Notes: This is a partial blocking call since we require more than one cycle to complete the write. checks for completion first *********************************************************************/ int ar8236_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; // check for clear MDIO status if (ar8236_mdio_poll() != 0) { return -1; } ar8236_emac_wrreg(EMAC_MAC_MDIO_DATA, highAddr); ar8236_emac_wrreg(EMAC_MAC_MDIO_CTRL, (phyAddr & 0x1f) | AR8236_MDIO_START | AR8236_MDIO_PAGE); // wait for completion if (ar8236_mdio_poll() != 0) { return -1; } ar8236_emac_wrreg(EMAC_MAC_MDIO_DATA, (data >> 16) & 0xffff); ar8236_emac_wrreg(EMAC_MAC_MDIO_CTRL, ((rg | AR8236_MDIO_HIGH_WORD) << 5) | ph | AR8236_MDIO_START | AR8236_MDIO_NORM); if (ar8236_mdio_poll() != 0) { return -1; } ar8236_emac_wrreg(EMAC_MAC_MDIO_DATA, data & 0xffff); ar8236_emac_wrreg(EMAC_MAC_MDIO_CTRL, (rg << 5) | ph | AR8236_MDIO_START | AR8236_MDIO_NORM); if (ar8236_mdio_poll() != 0) { return -1; } // return without waiting for final completion return 0; } /********************************************************************* Name: ar8236_mdio_read Purpose: mdio read routine for AR8236 device Notes: This is a blocking call since we require more than one cycle to complete the write. checks for completion first *********************************************************************/ int ar8236_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 (ar8236_mdio_poll() != 0) { return -1; } ar8236_emac_wrreg(EMAC_MAC_MDIO_DATA, highAddr); ar8236_emac_wrreg(EMAC_MAC_MDIO_CTRL, phyAddr | AR8236_MDIO_START | AR8236_MDIO_PAGE); if (ar8236_mdio_poll() != 0) { return -1; } ar8236_emac_wrreg(EMAC_MAC_MDIO_CTRL, (rg << 5) | AR8236_MDIO_START | AR8236_MDIO_READ | ph | AR8236_MDIO_NORM); if (ar8236_mdio_poll() != 0) { return -1; } *data = ar8236_emac_rdreg(EMAC_MAC_MDIO_DATA); ar8236_emac_wrreg(EMAC_MAC_MDIO_CTRL, ((rg | AR8236_MDIO_HIGH_WORD) << 5) | AR8236_MDIO_START | AR8236_MDIO_READ | ph | AR8236_MDIO_NORM); if (ar8236_mdio_poll() != 0) { return -1; } *data = *data | (ar8236_emac_rdreg(EMAC_MAC_MDIO_DATA) << 16); return 0; } /********************************************************************* Name: ar8236_init Purpose: Check for Atheros 8236 switch, return pointer to device if found, NULL otherwise Notes: pass phy addr as -1 to scan for phy *********************************************************************/ u32 ar8236_init(u32 baseAddr, u32 phy_addr) { u32 addr; u32 devID; g8236Dev.base = baseAddr; // need to scan? if (phy_addr >=EMAC_PHY_ADDR_SCAN) { addr = AR8236_MIN_PHY_NUM; } else { if (phy_addr >= AR8236_MAX_PHY_NUM) { printf("config error: phy addr out of range\n"); return EMAC_PHY_ADDR_SCAN; } addr = phy_addr; } while (addr < AR8236_MAX_PHY_NUM) { int reset = AR8236_MASK_CTL_RESET; ar8236_mdio_read(addr, AR8236_MASK_CTL, &devID); if ((devID & 0xff00) == 0x300) { printf("Detected AR8236 Switch %d:%x - set for MII, 100FD\n",addr,devID); // do a softreset ar8236_mdio_write(phy_addr, AR8236_MODE_CTRL, AR8236_MODE_MII_PHY); // do a clean reset and wait for completion ar8236_mdio_write(phy_addr, AR8236_MASK_CTL, AR8236_MASK_CTL_RESET); while (reset & AR8236_MASK_CTL_RESET) { ar8236_mdio_read(addr, AR8236_MASK_CTL, &reset); } ar8236_mdio_write(phy_addr, AR8236_FLOOD_MASK, AR8236_FLOOD_MASK_DEF); ar8236_mdio_write(phy_addr, AR8236_PORT0_CTRL, AR8236_PORT0_CTRL_DEF); g8236Dev.phy = addr; return addr; } if (phy_addr == MAX_PHY_ADDR) { addr++; } else { // not found on passed addr break; } } printf("Error: AR8236 not found\n"); return EMAC_PHY_ADDR_SCAN; }