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