root/drivers/net/phy/meson-gxl.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. meson_gxl_open_banks
  2. meson_gxl_close_banks
  3. meson_gxl_read_reg
  4. meson_gxl_write_reg
  5. meson_gxl_config_init
  6. meson_gxl_read_status
  7. meson_gxl_ack_interrupt
  8. meson_gxl_config_intr

   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                 val = INTSRC_ANEG_PR
 208                         | INTSRC_PARALLEL_FAULT
 209                         | INTSRC_ANEG_LP_ACK
 210                         | INTSRC_LINK_DOWN
 211                         | INTSRC_REMOTE_FAULT
 212                         | INTSRC_ANEG_COMPLETE;
 213         } else {
 214                 val = 0;
 215         }
 216 
 217         /* Ack any pending IRQ */
 218         ret = meson_gxl_ack_interrupt(phydev);
 219         if (ret)
 220                 return ret;
 221 
 222         return phy_write(phydev, INTSRC_MASK, val);
 223 }
 224 
 225 static struct phy_driver meson_gxl_phy[] = {
 226         {
 227                 PHY_ID_MATCH_EXACT(0x01814400),
 228                 .name           = "Meson GXL Internal PHY",
 229                 /* PHY_BASIC_FEATURES */
 230                 .flags          = PHY_IS_INTERNAL,
 231                 .soft_reset     = genphy_soft_reset,
 232                 .config_init    = meson_gxl_config_init,
 233                 .read_status    = meson_gxl_read_status,
 234                 .ack_interrupt  = meson_gxl_ack_interrupt,
 235                 .config_intr    = meson_gxl_config_intr,
 236                 .suspend        = genphy_suspend,
 237                 .resume         = genphy_resume,
 238         }, {
 239                 PHY_ID_MATCH_EXACT(0x01803301),
 240                 .name           = "Meson G12A Internal PHY",
 241                 /* PHY_BASIC_FEATURES */
 242                 .flags          = PHY_IS_INTERNAL,
 243                 .soft_reset     = genphy_soft_reset,
 244                 .ack_interrupt  = meson_gxl_ack_interrupt,
 245                 .config_intr    = meson_gxl_config_intr,
 246                 .suspend        = genphy_suspend,
 247                 .resume         = genphy_resume,
 248         },
 249 };
 250 
 251 static struct mdio_device_id __maybe_unused meson_gxl_tbl[] = {
 252         { PHY_ID_MATCH_VENDOR(0x01814400) },
 253         { PHY_ID_MATCH_VENDOR(0x01803301) },
 254         { }
 255 };
 256 
 257 module_phy_driver(meson_gxl_phy);
 258 
 259 MODULE_DEVICE_TABLE(mdio, meson_gxl_tbl);
 260 
 261 MODULE_DESCRIPTION("Amlogic Meson GXL Internal PHY driver");
 262 MODULE_AUTHOR("Baoqi wang");
 263 MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
 264 MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
 265 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */