2 * drivers/net/phy/rockchip.c
4 * Driver for ROCKCHIP Ethernet PHYs
6 * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd
8 * David Wu <david.wu@rock-chips.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
17 #include <linux/ethtool.h>
18 #include <linux/kernel.h>
19 #include <linux/module.h>
20 #include <linux/mii.h>
21 #include <linux/netdevice.h>
22 #include <linux/phy.h>
24 #define INTERNAL_EPHY_ID 0x1234d400
26 #define MII_INTERNAL_CTRL_STATUS 17
27 #define SMI_ADDR_TSTCNTL 20
28 #define SMI_ADDR_TSTREAD1 21
29 #define SMI_ADDR_TSTREAD2 22
30 #define SMI_ADDR_TSTWRITE 23
31 #define MII_SPECIAL_CONTROL_STATUS 31
33 #define MII_AUTO_MDIX_EN BIT(7)
34 #define MII_MDIX_EN BIT(6)
36 #define MII_SPEED_10 BIT(2)
37 #define MII_SPEED_100 BIT(3)
39 #define TSTCNTL_RD (BIT(15) | BIT(10))
40 #define TSTCNTL_WR (BIT(14) | BIT(10))
42 #define TSTMODE_ENABLE 0x400
43 #define TSTMODE_DISABLE 0x0
45 #define WR_ADDR_A7CFG 0x18
47 static int rockchip_init_tstmode(struct phy_device
*phydev
)
51 /* Enable access to Analog and DSP register banks */
52 ret
= phy_write(phydev
, SMI_ADDR_TSTCNTL
, TSTMODE_ENABLE
);
56 ret
= phy_write(phydev
, SMI_ADDR_TSTCNTL
, TSTMODE_DISABLE
);
60 return phy_write(phydev
, SMI_ADDR_TSTCNTL
, TSTMODE_ENABLE
);
63 static int rockchip_close_tstmode(struct phy_device
*phydev
)
65 /* Back to basic register bank */
66 return phy_write(phydev
, SMI_ADDR_TSTCNTL
, TSTMODE_DISABLE
);
69 static int rockchip_integrated_phy_analog_init(struct phy_device
*phydev
)
73 ret
= rockchip_init_tstmode(phydev
);
78 * Adjust tx amplitude to make sginal better,
79 * the default value is 0x8.
81 ret
= phy_write(phydev
, SMI_ADDR_TSTWRITE
, 0xB);
84 ret
= phy_write(phydev
, SMI_ADDR_TSTCNTL
, TSTCNTL_WR
| WR_ADDR_A7CFG
);
88 return rockchip_close_tstmode(phydev
);
91 static int rockchip_integrated_phy_config_init(struct phy_device
*phydev
)
96 * The auto MIDX has linked problem on some board,
97 * workround to disable auto MDIX.
99 val
= phy_read(phydev
, MII_INTERNAL_CTRL_STATUS
);
102 val
&= ~MII_AUTO_MDIX_EN
;
103 ret
= phy_write(phydev
, MII_INTERNAL_CTRL_STATUS
, val
);
107 return rockchip_integrated_phy_analog_init(phydev
);
110 static void rockchip_link_change_notify(struct phy_device
*phydev
)
112 int speed
= SPEED_10
;
114 if (phydev
->autoneg
== AUTONEG_ENABLE
) {
115 int reg
= phy_read(phydev
, MII_SPECIAL_CONTROL_STATUS
);
118 phydev_err(phydev
, "phy_read err: %d.\n", reg
);
122 if (reg
& MII_SPEED_100
)
124 else if (reg
& MII_SPEED_10
)
127 int bmcr
= phy_read(phydev
, MII_BMCR
);
130 phydev_err(phydev
, "phy_read err: %d.\n", bmcr
);
134 if (bmcr
& BMCR_SPEED100
)
141 * If mode switch happens from 10BT to 100BT, all DSP/AFE
142 * registers are set to default values. So any AFE/DSP
143 * registers have to be re-initialized in this case.
145 if ((phydev
->speed
== SPEED_10
) && (speed
== SPEED_100
)) {
146 int ret
= rockchip_integrated_phy_analog_init(phydev
);
148 phydev_err(phydev
, "rockchip_integrated_phy_analog_init err: %d.\n",
153 static int rockchip_set_polarity(struct phy_device
*phydev
, int polarity
)
157 /* get the current settings */
158 reg
= phy_read(phydev
, MII_INTERNAL_CTRL_STATUS
);
162 reg
&= ~MII_AUTO_MDIX_EN
;
171 case ETH_TP_MDI_AUTO
:
172 case ETH_TP_MDI_INVALID
:
178 /* Set the new polarity value in the register */
179 err
= phy_write(phydev
, MII_INTERNAL_CTRL_STATUS
, val
);
187 static int rockchip_config_aneg(struct phy_device
*phydev
)
191 err
= rockchip_set_polarity(phydev
, phydev
->mdix
);
195 return genphy_config_aneg(phydev
);
198 static int rockchip_phy_resume(struct phy_device
*phydev
)
200 genphy_resume(phydev
);
202 return rockchip_integrated_phy_config_init(phydev
);
205 static struct phy_driver rockchip_phy_driver
[] = {
207 .phy_id
= INTERNAL_EPHY_ID
,
208 .phy_id_mask
= 0xfffffff0,
209 .name
= "Rockchip integrated EPHY",
210 .features
= PHY_BASIC_FEATURES
,
212 .link_change_notify
= rockchip_link_change_notify
,
213 .soft_reset
= genphy_soft_reset
,
214 .config_init
= rockchip_integrated_phy_config_init
,
215 .config_aneg
= rockchip_config_aneg
,
216 .suspend
= genphy_suspend
,
217 .resume
= rockchip_phy_resume
,
221 module_phy_driver(rockchip_phy_driver
);
223 static struct mdio_device_id __maybe_unused rockchip_phy_tbl
[] = {
224 { INTERNAL_EPHY_ID
, 0xfffffff0 },
228 MODULE_DEVICE_TABLE(mdio
, rockchip_phy_tbl
);
230 MODULE_AUTHOR("David Wu <david.wu@rock-chips.com>");
231 MODULE_DESCRIPTION("Rockchip Ethernet PHY driver");
232 MODULE_LICENSE("GPL v2");