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