1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Amlogic Meson GXL Internal PHY Driver 4 * 5 * Copyright (C) 2015 Amlogic, Inc. All rights reserved. 6 * Copyright (C) 2016 BayLibre, SAS. All rights reserved. 7 * Author: Neil Armstrong <narmstrong@baylibre.com> 8 */ 9 #include <linux/kernel.h> 10 #include <linux/module.h> 11 #include <linux/mii.h> 12 #include <linux/ethtool.h> 13 #include <linux/phy.h> 14 #include <linux/netdevice.h> 15 #include <linux/bitfield.h> 16 #include <linux/smscphy.h> 17 18 #define TSTCNTL 20 19 #define TSTCNTL_READ BIT(15) 20 #define TSTCNTL_WRITE BIT(14) 21 #define TSTCNTL_REG_BANK_SEL GENMASK(12, 11) 22 #define TSTCNTL_TEST_MODE BIT(10) 23 #define TSTCNTL_READ_ADDRESS GENMASK(9, 5) 24 #define TSTCNTL_WRITE_ADDRESS GENMASK(4, 0) 25 #define TSTREAD1 21 26 #define TSTWRITE 23 27 28 #define BANK_ANALOG_DSP 0 29 #define BANK_WOL 1 30 #define BANK_BIST 3 31 32 /* WOL Registers */ 33 #define LPI_STATUS 0xc 34 #define LPI_STATUS_RSV12 BIT(12) 35 36 /* BIST Registers */ 37 #define FR_PLL_CONTROL 0x1b 38 #define FR_PLL_DIV0 0x1c 39 #define FR_PLL_DIV1 0x1d 40 41 static int meson_gxl_open_banks(struct phy_device *phydev) 42 { 43 int ret; 44 45 /* Enable Analog and DSP register Bank access by 46 * toggling TSTCNTL_TEST_MODE bit in the TSTCNTL register 47 */ 48 ret = phy_write(phydev, TSTCNTL, 0); 49 if (ret) 50 return ret; 51 ret = phy_write(phydev, TSTCNTL, TSTCNTL_TEST_MODE); 52 if (ret) 53 return ret; 54 ret = phy_write(phydev, TSTCNTL, 0); 55 if (ret) 56 return ret; 57 return phy_write(phydev, TSTCNTL, TSTCNTL_TEST_MODE); 58 } 59 60 static void meson_gxl_close_banks(struct phy_device *phydev) 61 { 62 phy_write(phydev, TSTCNTL, 0); 63 } 64 65 static int meson_gxl_read_reg(struct phy_device *phydev, 66 unsigned int bank, unsigned int reg) 67 { 68 int ret; 69 70 ret = meson_gxl_open_banks(phydev); 71 if (ret) 72 goto out; 73 74 ret = phy_write(phydev, TSTCNTL, TSTCNTL_READ | 75 FIELD_PREP(TSTCNTL_REG_BANK_SEL, bank) | 76 TSTCNTL_TEST_MODE | 77 FIELD_PREP(TSTCNTL_READ_ADDRESS, reg)); 78 if (ret) 79 goto out; 80 81 ret = phy_read(phydev, TSTREAD1); 82 out: 83 /* Close the bank access on our way out */ 84 meson_gxl_close_banks(phydev); 85 return ret; 86 } 87 88 static int meson_gxl_write_reg(struct phy_device *phydev, 89 unsigned int bank, unsigned int reg, 90 uint16_t value) 91 { 92 int ret; 93 94 ret = meson_gxl_open_banks(phydev); 95 if (ret) 96 goto out; 97 98 ret = phy_write(phydev, TSTWRITE, value); 99 if (ret) 100 goto out; 101 102 ret = phy_write(phydev, TSTCNTL, TSTCNTL_WRITE | 103 FIELD_PREP(TSTCNTL_REG_BANK_SEL, bank) | 104 TSTCNTL_TEST_MODE | 105 FIELD_PREP(TSTCNTL_WRITE_ADDRESS, reg)); 106 107 out: 108 /* Close the bank access on our way out */ 109 meson_gxl_close_banks(phydev); 110 return ret; 111 } 112 113 static int meson_gxl_config_init(struct phy_device *phydev) 114 { 115 int ret; 116 117 /* Enable fractional PLL */ 118 ret = meson_gxl_write_reg(phydev, BANK_BIST, FR_PLL_CONTROL, 0x5); 119 if (ret) 120 return ret; 121 122 /* Program fraction FR_PLL_DIV1 */ 123 ret = meson_gxl_write_reg(phydev, BANK_BIST, FR_PLL_DIV1, 0x029a); 124 if (ret) 125 return ret; 126 127 /* Program fraction FR_PLL_DIV1 */ 128 ret = meson_gxl_write_reg(phydev, BANK_BIST, FR_PLL_DIV0, 0xaaaa); 129 if (ret) 130 return ret; 131 132 return 0; 133 } 134 135 /* This function is provided to cope with the possible failures of this phy 136 * during aneg process. When aneg fails, the PHY reports that aneg is done 137 * but the value found in MII_LPA is wrong: 138 * - Early failures: MII_LPA is just 0x0001. if MII_EXPANSION reports that 139 * the link partner (LP) supports aneg but the LP never acked our base 140 * code word, it is likely that we never sent it to begin with. 141 * - Late failures: MII_LPA is filled with a value which seems to make sense 142 * but it actually is not what the LP is advertising. It seems that we 143 * can detect this using a magic bit in the WOL bank (reg 12 - bit 12). 144 * If this particular bit is not set when aneg is reported being done, 145 * it means MII_LPA is likely to be wrong. 146 * 147 * In both case, forcing a restart of the aneg process solve the problem. 148 * When this failure happens, the first retry is usually successful but, 149 * in some cases, it may take up to 6 retries to get a decent result 150 */ 151 static int meson_gxl_read_status(struct phy_device *phydev) 152 { 153 int ret, wol, lpa, exp; 154 155 if (phydev->autoneg == AUTONEG_ENABLE) { 156 ret = genphy_aneg_done(phydev); 157 if (ret < 0) 158 return ret; 159 else if (!ret) 160 goto read_status_continue; 161 162 /* Aneg is done, let's check everything is fine */ 163 wol = meson_gxl_read_reg(phydev, BANK_WOL, LPI_STATUS); 164 if (wol < 0) 165 return wol; 166 167 lpa = phy_read(phydev, MII_LPA); 168 if (lpa < 0) 169 return lpa; 170 171 exp = phy_read(phydev, MII_EXPANSION); 172 if (exp < 0) 173 return exp; 174 175 if (!(wol & LPI_STATUS_RSV12) || 176 ((exp & EXPANSION_NWAY) && !(lpa & LPA_LPACK))) { 177 /* Looks like aneg failed after all */ 178 phydev_dbg(phydev, "LPA corruption - aneg restart\n"); 179 return genphy_restart_aneg(phydev); 180 } 181 } 182 183 read_status_continue: 184 return genphy_read_status(phydev); 185 } 186 187 static struct phy_driver meson_gxl_phy[] = { 188 { 189 PHY_ID_MATCH_EXACT(0x01814400), 190 .name = "Meson GXL Internal PHY", 191 /* PHY_BASIC_FEATURES */ 192 .flags = PHY_IS_INTERNAL, 193 .soft_reset = genphy_soft_reset, 194 .config_init = meson_gxl_config_init, 195 .read_status = meson_gxl_read_status, 196 .config_intr = smsc_phy_config_intr, 197 .handle_interrupt = smsc_phy_handle_interrupt, 198 .suspend = genphy_suspend, 199 .resume = genphy_resume, 200 .read_mmd = genphy_read_mmd_unsupported, 201 .write_mmd = genphy_write_mmd_unsupported, 202 }, { 203 PHY_ID_MATCH_EXACT(0x01803301), 204 .name = "Meson G12A Internal PHY", 205 /* PHY_BASIC_FEATURES */ 206 .flags = PHY_IS_INTERNAL, 207 .probe = smsc_phy_probe, 208 .config_init = smsc_phy_config_init, 209 .soft_reset = genphy_soft_reset, 210 .read_status = lan87xx_read_status, 211 .config_intr = smsc_phy_config_intr, 212 .handle_interrupt = smsc_phy_handle_interrupt, 213 214 .get_tunable = smsc_phy_get_tunable, 215 .set_tunable = smsc_phy_set_tunable, 216 217 .suspend = genphy_suspend, 218 .resume = genphy_resume, 219 .read_mmd = genphy_read_mmd_unsupported, 220 .write_mmd = genphy_write_mmd_unsupported, 221 }, 222 }; 223 224 static struct mdio_device_id __maybe_unused meson_gxl_tbl[] = { 225 { PHY_ID_MATCH_VENDOR(0x01814400) }, 226 { PHY_ID_MATCH_VENDOR(0x01803301) }, 227 { } 228 }; 229 230 module_phy_driver(meson_gxl_phy); 231 232 MODULE_DEVICE_TABLE(mdio, meson_gxl_tbl); 233 234 MODULE_DESCRIPTION("Amlogic Meson GXL Internal PHY driver"); 235 MODULE_AUTHOR("Baoqi wang"); 236 MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); 237 MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); 238 MODULE_LICENSE("GPL"); 239