root/drivers/net/phy/rockchip.c

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

DEFINITIONS

This source file includes following definitions.
  1. rockchip_init_tstmode
  2. rockchip_close_tstmode
  3. rockchip_integrated_phy_analog_init
  4. rockchip_integrated_phy_config_init
  5. rockchip_link_change_notify
  6. rockchip_set_polarity
  7. rockchip_config_aneg
  8. rockchip_phy_resume

   1 // SPDX-License-Identifier: GPL-2.0+
   2 /**
   3  * drivers/net/phy/rockchip.c
   4  *
   5  * Driver for ROCKCHIP Ethernet PHYs
   6  *
   7  * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd
   8  *
   9  * David Wu <david.wu@rock-chips.com>
  10  */
  11 
  12 #include <linux/ethtool.h>
  13 #include <linux/kernel.h>
  14 #include <linux/module.h>
  15 #include <linux/mii.h>
  16 #include <linux/netdevice.h>
  17 #include <linux/phy.h>
  18 
  19 #define INTERNAL_EPHY_ID                        0x1234d400
  20 
  21 #define MII_INTERNAL_CTRL_STATUS                17
  22 #define SMI_ADDR_TSTCNTL                        20
  23 #define SMI_ADDR_TSTREAD1                       21
  24 #define SMI_ADDR_TSTREAD2                       22
  25 #define SMI_ADDR_TSTWRITE                       23
  26 #define MII_SPECIAL_CONTROL_STATUS              31
  27 
  28 #define MII_AUTO_MDIX_EN                        BIT(7)
  29 #define MII_MDIX_EN                             BIT(6)
  30 
  31 #define MII_SPEED_10                            BIT(2)
  32 #define MII_SPEED_100                           BIT(3)
  33 
  34 #define TSTCNTL_RD                              (BIT(15) | BIT(10))
  35 #define TSTCNTL_WR                              (BIT(14) | BIT(10))
  36 
  37 #define TSTMODE_ENABLE                          0x400
  38 #define TSTMODE_DISABLE                         0x0
  39 
  40 #define WR_ADDR_A7CFG                           0x18
  41 
  42 static int rockchip_init_tstmode(struct phy_device *phydev)
  43 {
  44         int ret;
  45 
  46         /* Enable access to Analog and DSP register banks */
  47         ret = phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_ENABLE);
  48         if (ret)
  49                 return ret;
  50 
  51         ret = phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_DISABLE);
  52         if (ret)
  53                 return ret;
  54 
  55         return phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_ENABLE);
  56 }
  57 
  58 static int rockchip_close_tstmode(struct phy_device *phydev)
  59 {
  60         /* Back to basic register bank */
  61         return phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_DISABLE);
  62 }
  63 
  64 static int rockchip_integrated_phy_analog_init(struct phy_device *phydev)
  65 {
  66         int ret;
  67 
  68         ret = rockchip_init_tstmode(phydev);
  69         if (ret)
  70                 return ret;
  71 
  72         /*
  73          * Adjust tx amplitude to make sginal better,
  74          * the default value is 0x8.
  75          */
  76         ret = phy_write(phydev, SMI_ADDR_TSTWRITE, 0xB);
  77         if (ret)
  78                 return ret;
  79         ret = phy_write(phydev, SMI_ADDR_TSTCNTL, TSTCNTL_WR | WR_ADDR_A7CFG);
  80         if (ret)
  81                 return ret;
  82 
  83         return rockchip_close_tstmode(phydev);
  84 }
  85 
  86 static int rockchip_integrated_phy_config_init(struct phy_device *phydev)
  87 {
  88         int val, ret;
  89 
  90         /*
  91          * The auto MIDX has linked problem on some board,
  92          * workround to disable auto MDIX.
  93          */
  94         val = phy_read(phydev, MII_INTERNAL_CTRL_STATUS);
  95         if (val < 0)
  96                 return val;
  97         val &= ~MII_AUTO_MDIX_EN;
  98         ret = phy_write(phydev, MII_INTERNAL_CTRL_STATUS, val);
  99         if (ret)
 100                 return ret;
 101 
 102         return rockchip_integrated_phy_analog_init(phydev);
 103 }
 104 
 105 static void rockchip_link_change_notify(struct phy_device *phydev)
 106 {
 107         /*
 108          * If mode switch happens from 10BT to 100BT, all DSP/AFE
 109          * registers are set to default values. So any AFE/DSP
 110          * registers have to be re-initialized in this case.
 111          */
 112         if (phydev->state == PHY_RUNNING && phydev->speed == SPEED_100) {
 113                 int ret = rockchip_integrated_phy_analog_init(phydev);
 114 
 115                 if (ret)
 116                         phydev_err(phydev, "rockchip_integrated_phy_analog_init err: %d.\n",
 117                                    ret);
 118         }
 119 }
 120 
 121 static int rockchip_set_polarity(struct phy_device *phydev, int polarity)
 122 {
 123         int reg, err, val;
 124 
 125         /* get the current settings */
 126         reg = phy_read(phydev, MII_INTERNAL_CTRL_STATUS);
 127         if (reg < 0)
 128                 return reg;
 129 
 130         reg &= ~MII_AUTO_MDIX_EN;
 131         val = reg;
 132         switch (polarity) {
 133         case ETH_TP_MDI:
 134                 val &= ~MII_MDIX_EN;
 135                 break;
 136         case ETH_TP_MDI_X:
 137                 val |= MII_MDIX_EN;
 138                 break;
 139         case ETH_TP_MDI_AUTO:
 140         case ETH_TP_MDI_INVALID:
 141         default:
 142                 return 0;
 143         }
 144 
 145         if (val != reg) {
 146                 /* Set the new polarity value in the register */
 147                 err = phy_write(phydev, MII_INTERNAL_CTRL_STATUS, val);
 148                 if (err)
 149                         return err;
 150         }
 151 
 152         return 0;
 153 }
 154 
 155 static int rockchip_config_aneg(struct phy_device *phydev)
 156 {
 157         int err;
 158 
 159         err = rockchip_set_polarity(phydev, phydev->mdix);
 160         if (err < 0)
 161                 return err;
 162 
 163         return genphy_config_aneg(phydev);
 164 }
 165 
 166 static int rockchip_phy_resume(struct phy_device *phydev)
 167 {
 168         genphy_resume(phydev);
 169 
 170         return rockchip_integrated_phy_config_init(phydev);
 171 }
 172 
 173 static struct phy_driver rockchip_phy_driver[] = {
 174 {
 175         .phy_id                 = INTERNAL_EPHY_ID,
 176         .phy_id_mask            = 0xfffffff0,
 177         .name                   = "Rockchip integrated EPHY",
 178         /* PHY_BASIC_FEATURES */
 179         .flags                  = 0,
 180         .link_change_notify     = rockchip_link_change_notify,
 181         .soft_reset             = genphy_soft_reset,
 182         .config_init            = rockchip_integrated_phy_config_init,
 183         .config_aneg            = rockchip_config_aneg,
 184         .suspend                = genphy_suspend,
 185         .resume                 = rockchip_phy_resume,
 186 },
 187 };
 188 
 189 module_phy_driver(rockchip_phy_driver);
 190 
 191 static struct mdio_device_id __maybe_unused rockchip_phy_tbl[] = {
 192         { INTERNAL_EPHY_ID, 0xfffffff0 },
 193         { }
 194 };
 195 
 196 MODULE_DEVICE_TABLE(mdio, rockchip_phy_tbl);
 197 
 198 MODULE_AUTHOR("David Wu <david.wu@rock-chips.com>");
 199 MODULE_DESCRIPTION("Rockchip Ethernet PHY driver");
 200 MODULE_LICENSE("GPL");

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