1 /* 2 * Meson GXL Internal PHY Driver 3 * 4 * Copyright (C) 2015 Amlogic, Inc. All rights reserved. 5 * Copyright (C) 2016 BayLibre, SAS. All rights reserved. 6 * Author: Neil Armstrong <narmstrong@baylibre.com> 7 * 8 * SPDX-License-Identifier: GPL-2.0+ 9 */ 10 #include <config.h> 11 #include <common.h> 12 #include <linux/bitops.h> 13 #include <dm.h> 14 #include <phy.h> 15 16 /* This function is provided to cope with the possible failures of this phy 17 * during aneg process. When aneg fails, the PHY reports that aneg is done 18 * but the value found in MII_LPA is wrong: 19 * - Early failures: MII_LPA is just 0x0001. if MII_EXPANSION reports that 20 * the link partner (LP) supports aneg but the LP never acked our base 21 * code word, it is likely that we never sent it to begin with. 22 * - Late failures: MII_LPA is filled with a value which seems to make sense 23 * but it actually is not what the LP is advertising. It seems that we 24 * can detect this using a magic bit in the WOL bank (reg 12 - bit 12). 25 * If this particular bit is not set when aneg is reported being done, 26 * it means MII_LPA is likely to be wrong. 27 * 28 * In both case, forcing a restart of the aneg process solve the problem. 29 * When this failure happens, the first retry is usually successful but, 30 * in some cases, it may take up to 6 retries to get a decent result 31 */ 32 int meson_gxl_startup(struct phy_device *phydev) 33 { 34 unsigned int retries = 10; 35 int ret, wol, lpa, exp; 36 37 restart_aneg: 38 ret = genphy_update_link(phydev); 39 if (ret) 40 return ret; 41 42 if (phydev->autoneg == AUTONEG_ENABLE) { 43 /* Need to access WOL bank, make sure the access is open */ 44 ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x14, 0x0000); 45 if (ret) 46 return ret; 47 ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x14, 0x0400); 48 if (ret) 49 return ret; 50 ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x14, 0x0000); 51 if (ret) 52 return ret; 53 ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x14, 0x0400); 54 if (ret) 55 return ret; 56 57 /* Request LPI_STATUS WOL register */ 58 ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x14, 0x8D80); 59 if (ret) 60 return ret; 61 62 /* Read LPI_STATUS value */ 63 wol = phy_read(phydev, MDIO_DEVAD_NONE, 0x15); 64 if (wol < 0) 65 return wol; 66 67 lpa = phy_read(phydev, MDIO_DEVAD_NONE, MII_LPA); 68 if (lpa < 0) 69 return lpa; 70 71 exp = phy_read(phydev, MDIO_DEVAD_NONE, MII_EXPANSION); 72 if (exp < 0) 73 return exp; 74 75 if (!(wol & BIT(12)) || 76 ((exp & EXPANSION_NWAY) && !(lpa & LPA_LPACK))) { 77 78 /* Looks like aneg failed after all */ 79 if (!retries) { 80 printf("%s LPA corruption max attempts\n", 81 phydev->dev->name); 82 return -ETIMEDOUT; 83 } 84 85 printf("%s LPA corruption - aneg restart\n", 86 phydev->dev->name); 87 88 ret = genphy_restart_aneg(phydev); 89 if (ret) 90 return ret; 91 92 --retries; 93 94 goto restart_aneg; 95 } 96 } 97 98 return genphy_parse_link(phydev); 99 } 100 101 static int meson_gxl_phy_config(struct phy_device *phydev) 102 { 103 /* Enable Analog and DSP register Bank access by */ 104 phy_write(phydev, MDIO_DEVAD_NONE, 0x14, 0x0000); 105 phy_write(phydev, MDIO_DEVAD_NONE, 0x14, 0x0400); 106 phy_write(phydev, MDIO_DEVAD_NONE, 0x14, 0x0000); 107 phy_write(phydev, MDIO_DEVAD_NONE, 0x14, 0x0400); 108 109 /* Write Analog register 23 */ 110 phy_write(phydev, MDIO_DEVAD_NONE, 0x17, 0x8E0D); 111 phy_write(phydev, MDIO_DEVAD_NONE, 0x14, 0x4417); 112 113 /* Enable fractional PLL */ 114 phy_write(phydev, MDIO_DEVAD_NONE, 0x17, 0x0005); 115 phy_write(phydev, MDIO_DEVAD_NONE, 0x14, 0x5C1B); 116 117 /* Program fraction FR_PLL_DIV1 */ 118 phy_write(phydev, MDIO_DEVAD_NONE, 0x17, 0x029A); 119 phy_write(phydev, MDIO_DEVAD_NONE, 0x14, 0x5C1D); 120 121 /* Program fraction FR_PLL_DIV1 */ 122 phy_write(phydev, MDIO_DEVAD_NONE, 0x17, 0xAAAA); 123 phy_write(phydev, MDIO_DEVAD_NONE, 0x14, 0x5C1C); 124 125 return genphy_config(phydev); 126 } 127 128 static struct phy_driver meson_gxl_phy_driver = { 129 .name = "Meson GXL Internal PHY", 130 .uid = 0x01814400, 131 .mask = 0xfffffff0, 132 .features = PHY_BASIC_FEATURES, 133 .config = &meson_gxl_phy_config, 134 .startup = &meson_gxl_startup, 135 .shutdown = &genphy_shutdown, 136 }; 137 138 int phy_meson_gxl_init(void) 139 { 140 phy_register(&meson_gxl_phy_driver); 141 142 return 0; 143 } 144