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