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