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 genphy_config_init(phydev); 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 205 if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 206 val = INTSRC_ANEG_PR 207 | INTSRC_PARALLEL_FAULT 208 | INTSRC_ANEG_LP_ACK 209 | INTSRC_LINK_DOWN 210 | INTSRC_REMOTE_FAULT 211 | INTSRC_ANEG_COMPLETE; 212 } else { 213 val = 0; 214 } 215 216 return phy_write(phydev, INTSRC_MASK, val); 217 } 218 219 static struct phy_driver meson_gxl_phy[] = { 220 { 221 .phy_id = 0x01814400, 222 .phy_id_mask = 0xfffffff0, 223 .name = "Meson GXL Internal PHY", 224 .features = PHY_BASIC_FEATURES, 225 .flags = PHY_IS_INTERNAL, 226 .soft_reset = genphy_soft_reset, 227 .config_init = meson_gxl_config_init, 228 .aneg_done = genphy_aneg_done, 229 .read_status = meson_gxl_read_status, 230 .ack_interrupt = meson_gxl_ack_interrupt, 231 .config_intr = meson_gxl_config_intr, 232 .suspend = genphy_suspend, 233 .resume = genphy_resume, 234 }, 235 }; 236 237 static struct mdio_device_id __maybe_unused meson_gxl_tbl[] = { 238 { 0x01814400, 0xfffffff0 }, 239 { } 240 }; 241 242 module_phy_driver(meson_gxl_phy); 243 244 MODULE_DEVICE_TABLE(mdio, meson_gxl_tbl); 245 246 MODULE_DESCRIPTION("Amlogic Meson GXL Internal PHY driver"); 247 MODULE_AUTHOR("Baoqi wang"); 248 MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); 249 MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); 250 MODULE_LICENSE("GPL"); 251