diff options
Diffstat (limited to 'src/vendorcode/cavium/bdk/libbdk-hal/if/bdk-if-phy.c')
-rw-r--r-- | src/vendorcode/cavium/bdk/libbdk-hal/if/bdk-if-phy.c | 445 |
1 files changed, 445 insertions, 0 deletions
diff --git a/src/vendorcode/cavium/bdk/libbdk-hal/if/bdk-if-phy.c b/src/vendorcode/cavium/bdk/libbdk-hal/if/bdk-if-phy.c new file mode 100644 index 0000000000..5b02eff0cd --- /dev/null +++ b/src/vendorcode/cavium/bdk/libbdk-hal/if/bdk-if-phy.c @@ -0,0 +1,445 @@ +/***********************license start*********************************** +* Copyright (c) 2003-2017 Cavium Inc. (support@cavium.com). All rights +* reserved. +* +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are +* met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials provided +* with the distribution. +* +* * Neither the name of Cavium Inc. nor the names of +* its contributors may be used to endorse or promote products +* derived from this software without specific prior written +* permission. +* +* This Software, including technical data, may be subject to U.S. export +* control laws, including the U.S. Export Administration Act and its +* associated regulations, and may be subject to export or import +* regulations in other countries. +* +* TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" +* AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR +* WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT +* TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY +* REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT +* DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES +* OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR +* PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, +* QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK +* ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU. +***********************license end**************************************/ +#include <bdk.h> +#include <libbdk-hal/if/bdk-if.h> +#include <libbdk-hal/bdk-config.h> +#include <libbdk-hal/bdk-mdio.h> +#include <libbdk-hal/bdk-qlm.h> +#include <libbdk-hal/bdk-twsi.h> + +/** + * Called when the PHY is connected through TWSI + * + * @param dev_node Node the ethernet device is on + * @param phy_addr Encoded address, see bdk-if.h for format + * + * @return Link status + */ +static bdk_if_link_t __bdk_if_phy_get_twsi(bdk_node_t dev_node, int phy_addr) +{ + /* For TWSI: + Bits[31:24]: Node ID, 0xff for device node + Bits[23:16]: TWSI internal address width in bytes (0-2) + Bits[15:12]: 2=TWSI + Bits[11:8]: TWSI bus number + Bits[7:0]: TWSI address */ + int node = (phy_addr >> 24) & 0xff; + int twsi_ia_width = (phy_addr >> 16) & 0xff; + int twsi_bus = (phy_addr >> 8) & 0xf; + int twsi_addr = phy_addr & 0xff; + if (node == 0xff) + node = dev_node; + + bdk_if_link_t result; + result.u64 = 0; + + /* This is from the Avago SFP 1G Module data sheet + Register 17 (Extended Status 1) */ + int64_t phy_status = bdk_twsix_read_ia(node, twsi_bus, twsi_addr, 17, 2, twsi_ia_width); + if (phy_status != -1) + { + int speed = (phy_status >> 14)& 3; + int duplex = (phy_status >> 13)& 1; + int resolved = (phy_status >> 11)& 1; + int link = (phy_status >> 10)& 1; + if (resolved) + { + result.s.up = link; + result.s.full_duplex = duplex; + switch (speed) + { + case 0: /* 10 Mbps */ + result.s.speed = 10; + break; + case 1: /* 100 Mbps */ + result.s.speed = 100; + break; + case 2: /* 1 Gbps */ + result.s.speed = 1000; + break; + case 3: /* Illegal */ + result.u64 = 0; + break; + } + } + } + + return result; +} + +/** + * Read the status of a PHY + * + * @param dev_node Node the ethernet device is on + * @param phy_addr Encoded PHY address, see bdk-if.h for format + * + * @return Link status + */ +bdk_if_link_t __bdk_if_phy_get(bdk_node_t dev_node, int phy_addr) +{ + int node = (phy_addr >> 24) & 0xff; + int mdio_bus = (phy_addr >> 8) & 0xff; + int mdio_addr = phy_addr & 0xff; + if (node == 0xff) + node = dev_node; + int phy_status; + bdk_if_link_t result; + result.u64 = 0; + + /* PHY address of -1 menas there is no PHY and we should have never + gotten here */ + if (phy_addr == -1) + return result; + + /* A PHY address with the special value 0x1000 represents a PHY we can't + connect to through MDIO which is assumed to be at 1Gbps */ + if (phy_addr == BDK_IF_PHY_FIXED_1GB) + { + result.s.up = 1; + result.s.full_duplex = 1; + result.s.speed = 1000; + return result; + } + + /* A PHY address with the special value 0x1001 represents a PHY we can't + connect to through MDIO which is assumed to be at 100Mbps */ + if (phy_addr == BDK_IF_PHY_FIXED_100MB) + { + result.s.up = 1; + result.s.full_duplex = 1; + result.s.speed = 100; + return result; + } + + /* Check for a PHY connected through TWSI */ + if ((phy_addr & BDK_IF_PHY_TYPE_MASK) == BDK_IF_PHY_TWSI) + return __bdk_if_phy_get_twsi(dev_node, phy_addr); + + phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, BDK_MDIO_PHY_REG_ID1); + if ((phy_status <= 0) || (phy_status == 0xffff)) + return result; + + switch (phy_status) + { + case 0x0141: /* Marvell */ + { + + /* This code assumes we are using a Marvell Gigabit PHY. All the + speed information can be read from register 17 in one go. Somebody + using a different PHY will need to handle it above in the board + specific area */ + phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, 17); + if (phy_status < 0) + return result; + + /* If the resolve bit 11 isn't set, see if autoneg is turned off + (bit 12, reg 0). The resolve bit doesn't get set properly when + autoneg is off, so force it */ + if ((phy_status & (1<<11)) == 0) + { + bdk_mdio_phy_reg_control_t control; + int phy_c = bdk_mdio_read(node, mdio_bus, mdio_addr, BDK_MDIO_PHY_REG_CONTROL); + if (phy_c < 0) + return result; + control.u16 = phy_c; + if (control.s.autoneg_enable == 0) + phy_status |= 1<<11; + } + + /* Only return a link if the PHY has finished auto negotiation + and set the resolved bit (bit 11) */ + if (phy_status & (1<<11)) + { + result.s.up = 1; + result.s.full_duplex = ((phy_status>>13)&1); + switch ((phy_status>>14)&3) + { + case 0: /* 10 Mbps */ + result.s.speed = 10; + break; + case 1: /* 100 Mbps */ + result.s.speed = 100; + break; + case 2: /* 1 Gbps */ + result.s.speed = 1000; + break; + case 3: /* Illegal */ + result.u64 = 0; + break; + } + } + break; + } + case 0x0022: /* Kendin */ + { + /* Register 1Fh - PHY Control */ + /* Micrel KSZ9031RNX, EBB8104 RGMII transceiver */ + /* Reports as "Kendin" in BDK_MDIO_PHY_REG_ID1 */ + phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, 0x1F); + if (phy_status & (1 << 6)) // Speed Status - 1000Base-T + { + result.s.up = 1; + result.s.speed = 1000; + } + else if (phy_status & (1 << 5)) // Speed Status - 100Base-TX + { + result.s.up = 1; + result.s.speed = 100; + } + else if (phy_status & (1 << 4)) // Speed Status - 10Base-T + { + result.s.up = 1; + result.s.speed = 10; + } + if (phy_status & (1 << 3)) // Duplex Status + { + result.s.full_duplex = 1; + } + break; + } + case 0x0007: /* Vitesse */ + { + /* Auxiliary Control and Status, Address 28 (0x1C) */ + phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, 0x1c); + result.s.full_duplex = (phy_status>>5)&1; + switch ((phy_status>>3) & 3) + { + case 0: + result.s.speed = 10; + result.s.up = 1; + break; + case 1: + result.s.speed = 100; + result.s.up = 1; + break; + default: + result.s.speed = 1000; + break; + } + phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, 0x01); + result.s.up = (phy_status>>2)&1; + break; + } + default: /* Treat like Broadcom */ + { + /* Below we are going to read SMI/MDIO register 0x19 which works + on Broadcom parts */ + phy_status = bdk_mdio_read(node, mdio_bus, mdio_addr, 0x19); + if (phy_status < 0) + return result; + + switch ((phy_status>>8) & 0x7) + { + case 0: + result.u64 = 0; + break; + case 1: + result.s.up = 1; + result.s.full_duplex = 0; + result.s.speed = 10; + break; + case 2: + result.s.up = 1; + result.s.full_duplex = 1; + result.s.speed = 10; + break; + case 3: + result.s.up = 1; + result.s.full_duplex = 0; + result.s.speed = 100; + break; + case 4: + result.s.up = 1; + result.s.full_duplex = 1; + result.s.speed = 100; + break; + case 5: + result.s.up = 1; + result.s.full_duplex = 1; + result.s.speed = 100; + break; + case 6: + result.s.up = 1; + result.s.full_duplex = 0; + result.s.speed = 1000; + break; + case 7: + result.s.up = 1; + result.s.full_duplex = 1; + result.s.speed = 1000; + break; + } + break; + } + } + + /* If link is down, return all fields as zero. */ + if (!result.s.up) + result.u64 = 0; + + return result; +} + +/** + * PHY XS initialization, primarily for RXAUI + * + * @param dev_node Node the ethernet device is on + * @param phy_addr Encoded PHY address, see bdk-if.h for format + * + * @return none + */ +void __bdk_if_phy_xs_init(bdk_node_t dev_node, int phy_addr) +{ + /* This code only supports PHYs connected through MDIO */ + if ((phy_addr & BDK_IF_PHY_TYPE_MASK) != BDK_IF_PHY_MDIO) + return; + + int node = (phy_addr >> 24) & 0xff; + int mdio_bus = (phy_addr >> 8) & 0xff; + int mdio_addr = phy_addr & 0xff; + if (node == 0xff) + node = dev_node; + + /* Read the PMA/PMD Device Identifier (1.2, 1.3) + OUI is spread across both registers */ + int dev_addr = 1; + int reg_addr = 2; + int phy_id1 = bdk_mdio_45_read(node, mdio_bus, mdio_addr, dev_addr, reg_addr); + if (phy_id1 == -1) + return; + reg_addr = 3; + int phy_id2 = bdk_mdio_45_read(node, mdio_bus, mdio_addr, dev_addr, reg_addr); + if (phy_id2 == -1) + return; + int model_number = (phy_id2 >> 4) & 0x3F; + int oui = phy_id1; + oui <<= 6; + oui |= (phy_id2 >> 10) & 0x3F; + switch (oui) + { + case 0x5016: /* Marvell */ + if (model_number == 9) /* 88X3140/3120 */ + { + BDK_TRACE(BGX, "N%d.MDIO%d.%d: Performing PHY reset on Marvell RXAUI PHY\n", + node, mdio_bus, mdio_addr); + dev_addr = 4; + reg_addr = 0; + /* Write bit 15, Software Reset, in PHY XS Control 1 (4.0). On CN78xx, + sometimes the PHY/BGX gets stuck in local fault mode, link never comes up, + and this appears to clear it up. Haven't seen this on CN81xx or T88, + but the reset seems like cheap insurance. */ + if (bdk_mdio_45_write(node, mdio_bus, mdio_addr, dev_addr, reg_addr, (1 << 15))) + { + bdk_error("PHY XS: MDIO write to (%d.%d) failed\n", dev_addr, reg_addr); + return; + } + + int reset_pending = 1; + while (reset_pending) + { + reset_pending = bdk_mdio_45_read(node, mdio_bus, mdio_addr, dev_addr, reg_addr); + reset_pending &= (1 << 15); + } + + /* Adjust the RXAUI TX Level for Marvell PHY, per Brendan Metzner + write 5 to register 4.49155 */ + reg_addr = 49155; + if (bdk_mdio_45_write(node, mdio_bus, mdio_addr, dev_addr, reg_addr, 5)) + { + bdk_error("PHY XS: MDIO write to (%d.%d) failed\n", dev_addr, reg_addr); + return; + } + } + break; + + default: /* Unknown PHY, or no PHY present */ + break; + } +} + +int bdk_if_phy_setup(bdk_node_t dev_node) +{ + /* 81xx has only 2 BGX (BGX0-BGX1); BGX2 is RGMII */ + for (int bgx = 0; bgx < 2; bgx++) + { + int port = 0; + int phy_addr = bdk_config_get_int(BDK_CONFIG_PHY_ADDRESS, dev_node, bgx, port); + if (phy_addr != -1) + { + int node = (phy_addr >> 24) & 0xff; + int mdio_bus = (phy_addr >> 8) & 0xff; + int mdio_addr = phy_addr & 0xff; + if (node == 0xff) + node = bdk_numa_local(); + if ((phy_addr & BDK_IF_PHY_TYPE_MASK) == BDK_IF_PHY_MDIO) + { + int qlm = bdk_qlm_get_qlm_num(node, BDK_IF_BGX, bgx, port); + if (qlm == -1) + continue; + + BDK_TRACE(PHY, "N%d.BGX%d.%d: Configuring ...\n", node, bgx, port); + + /* Check PHY id */ + int phy_status_1 = bdk_mdio_read(node, mdio_bus, phy_addr, BDK_MDIO_PHY_REG_ID1); + int phy_status_2 = bdk_mdio_read(node, mdio_bus, phy_addr, BDK_MDIO_PHY_REG_ID2); + + /* Vitesse */ + if (phy_status_1 == 0x0007) + { + if (phy_status_2 == 0x0670) + { + bdk_if_phy_vsc8514_setup(node, qlm, mdio_bus, mdio_addr); + } + else + { + bdk_if_phy_vetesse_setup(node, qlm, mdio_bus, mdio_addr); + } + } + + /* Marvell */ + else if (phy_status_1 == 0x0141) + bdk_if_phy_marvell_setup(node, qlm, mdio_bus, mdio_addr); + else + BDK_TRACE(PHY, "N%d.BGX%d.%d: Unknown PHY %x\n", node, bgx, port, phy_status_1); + } + } + } + return 0; +} + |