0001
0002
0003
0004
0005
0006
0007
0008
0009
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
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
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
0074
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
0092
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
0109
0110
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
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
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
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");