Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * drivers/net/phy/rockchip.c
0004  *
0005  * Driver for ROCKCHIP Ethernet PHYs
0006  *
0007  * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd
0008  *
0009  * David Wu <david.wu@rock-chips.com>
0010  */
0011 
0012 #include <linux/ethtool.h>
0013 #include <linux/kernel.h>
0014 #include <linux/module.h>
0015 #include <linux/mii.h>
0016 #include <linux/netdevice.h>
0017 #include <linux/phy.h>
0018 
0019 #define INTERNAL_EPHY_ID            0x1234d400
0020 
0021 #define MII_INTERNAL_CTRL_STATUS        17
0022 #define SMI_ADDR_TSTCNTL            20
0023 #define SMI_ADDR_TSTREAD1           21
0024 #define SMI_ADDR_TSTREAD2           22
0025 #define SMI_ADDR_TSTWRITE           23
0026 #define MII_SPECIAL_CONTROL_STATUS      31
0027 
0028 #define MII_AUTO_MDIX_EN            BIT(7)
0029 #define MII_MDIX_EN             BIT(6)
0030 
0031 #define MII_SPEED_10                BIT(2)
0032 #define MII_SPEED_100               BIT(3)
0033 
0034 #define TSTCNTL_RD              (BIT(15) | BIT(10))
0035 #define TSTCNTL_WR              (BIT(14) | BIT(10))
0036 
0037 #define TSTMODE_ENABLE              0x400
0038 #define TSTMODE_DISABLE             0x0
0039 
0040 #define WR_ADDR_A7CFG               0x18
0041 
0042 static int rockchip_init_tstmode(struct phy_device *phydev)
0043 {
0044     int ret;
0045 
0046     /* Enable access to Analog and DSP register banks */
0047     ret = phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_ENABLE);
0048     if (ret)
0049         return ret;
0050 
0051     ret = phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_DISABLE);
0052     if (ret)
0053         return ret;
0054 
0055     return phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_ENABLE);
0056 }
0057 
0058 static int rockchip_close_tstmode(struct phy_device *phydev)
0059 {
0060     /* Back to basic register bank */
0061     return phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_DISABLE);
0062 }
0063 
0064 static int rockchip_integrated_phy_analog_init(struct phy_device *phydev)
0065 {
0066     int ret;
0067 
0068     ret = rockchip_init_tstmode(phydev);
0069     if (ret)
0070         return ret;
0071 
0072     /*
0073      * Adjust tx amplitude to make sginal better,
0074      * the default value is 0x8.
0075      */
0076     ret = phy_write(phydev, SMI_ADDR_TSTWRITE, 0xB);
0077     if (ret)
0078         return ret;
0079     ret = phy_write(phydev, SMI_ADDR_TSTCNTL, TSTCNTL_WR | WR_ADDR_A7CFG);
0080     if (ret)
0081         return ret;
0082 
0083     return rockchip_close_tstmode(phydev);
0084 }
0085 
0086 static int rockchip_integrated_phy_config_init(struct phy_device *phydev)
0087 {
0088     int val, ret;
0089 
0090     /*
0091      * The auto MIDX has linked problem on some board,
0092      * workround to disable auto MDIX.
0093      */
0094     val = phy_read(phydev, MII_INTERNAL_CTRL_STATUS);
0095     if (val < 0)
0096         return val;
0097     val &= ~MII_AUTO_MDIX_EN;
0098     ret = phy_write(phydev, MII_INTERNAL_CTRL_STATUS, val);
0099     if (ret)
0100         return ret;
0101 
0102     return rockchip_integrated_phy_analog_init(phydev);
0103 }
0104 
0105 static void rockchip_link_change_notify(struct phy_device *phydev)
0106 {
0107     /*
0108      * If mode switch happens from 10BT to 100BT, all DSP/AFE
0109      * registers are set to default values. So any AFE/DSP
0110      * registers have to be re-initialized in this case.
0111      */
0112     if (phydev->state == PHY_RUNNING && phydev->speed == SPEED_100) {
0113         int ret = rockchip_integrated_phy_analog_init(phydev);
0114 
0115         if (ret)
0116             phydev_err(phydev, "rockchip_integrated_phy_analog_init err: %d.\n",
0117                    ret);
0118     }
0119 }
0120 
0121 static int rockchip_set_polarity(struct phy_device *phydev, int polarity)
0122 {
0123     int reg, err, val;
0124 
0125     /* get the current settings */
0126     reg = phy_read(phydev, MII_INTERNAL_CTRL_STATUS);
0127     if (reg < 0)
0128         return reg;
0129 
0130     reg &= ~MII_AUTO_MDIX_EN;
0131     val = reg;
0132     switch (polarity) {
0133     case ETH_TP_MDI:
0134         val &= ~MII_MDIX_EN;
0135         break;
0136     case ETH_TP_MDI_X:
0137         val |= MII_MDIX_EN;
0138         break;
0139     case ETH_TP_MDI_AUTO:
0140     case ETH_TP_MDI_INVALID:
0141     default:
0142         return 0;
0143     }
0144 
0145     if (val != reg) {
0146         /* Set the new polarity value in the register */
0147         err = phy_write(phydev, MII_INTERNAL_CTRL_STATUS, val);
0148         if (err)
0149             return err;
0150     }
0151 
0152     return 0;
0153 }
0154 
0155 static int rockchip_config_aneg(struct phy_device *phydev)
0156 {
0157     int err;
0158 
0159     err = rockchip_set_polarity(phydev, phydev->mdix);
0160     if (err < 0)
0161         return err;
0162 
0163     return genphy_config_aneg(phydev);
0164 }
0165 
0166 static int rockchip_phy_resume(struct phy_device *phydev)
0167 {
0168     genphy_resume(phydev);
0169 
0170     return rockchip_integrated_phy_config_init(phydev);
0171 }
0172 
0173 static struct phy_driver rockchip_phy_driver[] = {
0174 {
0175     .phy_id         = INTERNAL_EPHY_ID,
0176     .phy_id_mask        = 0xfffffff0,
0177     .name           = "Rockchip integrated EPHY",
0178     /* PHY_BASIC_FEATURES */
0179     .flags          = 0,
0180     .link_change_notify = rockchip_link_change_notify,
0181     .soft_reset     = genphy_soft_reset,
0182     .config_init        = rockchip_integrated_phy_config_init,
0183     .config_aneg        = rockchip_config_aneg,
0184     .suspend        = genphy_suspend,
0185     .resume         = rockchip_phy_resume,
0186 },
0187 };
0188 
0189 module_phy_driver(rockchip_phy_driver);
0190 
0191 static struct mdio_device_id __maybe_unused rockchip_phy_tbl[] = {
0192     { INTERNAL_EPHY_ID, 0xfffffff0 },
0193     { }
0194 };
0195 
0196 MODULE_DEVICE_TABLE(mdio, rockchip_phy_tbl);
0197 
0198 MODULE_AUTHOR("David Wu <david.wu@rock-chips.com>");
0199 MODULE_DESCRIPTION("Rockchip Ethernet PHY driver");
0200 MODULE_LICENSE("GPL");