xref: /openbmc/linux/drivers/net/phy/realtek.c (revision 48e4adf9afbe5256d0dab383baf310889973811d)
1a2443fd1SAndrew Lunn // SPDX-License-Identifier: GPL-2.0+
2097c2aa8SJohnson Leung /*
3097c2aa8SJohnson Leung  * drivers/net/phy/realtek.c
4097c2aa8SJohnson Leung  *
5097c2aa8SJohnson Leung  * Driver for Realtek PHYs
6097c2aa8SJohnson Leung  *
7097c2aa8SJohnson Leung  * Author: Johnson Leung <r58129@freescale.com>
8097c2aa8SJohnson Leung  *
9097c2aa8SJohnson Leung  * Copyright (c) 2004 Freescale Semiconductor, Inc.
10097c2aa8SJohnson Leung  */
118cc5baefSMartin Blumenstingl #include <linux/bitops.h>
12097c2aa8SJohnson Leung #include <linux/phy.h>
139d9779e7SPaul Gortmaker #include <linux/module.h>
14097c2aa8SJohnson Leung 
15097c2aa8SJohnson Leung #define RTL821x_PHYSR				0x11
168cc5baefSMartin Blumenstingl #define RTL821x_PHYSR_DUPLEX			BIT(13)
178cc5baefSMartin Blumenstingl #define RTL821x_PHYSR_SPEED			GENMASK(15, 14)
18a82f266dSMartin Blumenstingl 
19097c2aa8SJohnson Leung #define RTL821x_INER				0x12
2069021e32SMartin Blumenstingl #define RTL8211B_INER_INIT			0x6400
218cc5baefSMartin Blumenstingl #define RTL8211E_INER_LINK_STATUS		BIT(10)
228cc5baefSMartin Blumenstingl #define RTL8211F_INER_LINK_STATUS		BIT(4)
23a82f266dSMartin Blumenstingl 
24a82f266dSMartin Blumenstingl #define RTL821x_INSR				0x13
25a82f266dSMartin Blumenstingl 
26a82f266dSMartin Blumenstingl #define RTL821x_PAGE_SELECT			0x1f
27a82f266dSMartin Blumenstingl 
283447cf2eSShengzhou Liu #define RTL8211F_INSR				0x1d
29f609ab0eSMartin Blumenstingl 
308cc5baefSMartin Blumenstingl #define RTL8211F_TX_DELAY			BIT(8)
313447cf2eSShengzhou Liu 
32513588ddSJassi Brar #define RTL8201F_ISR				0x1e
33513588ddSJassi Brar #define RTL8201F_IER				0x13
34513588ddSJassi Brar 
35d8545825SLinus Walleij #define RTL8366RB_POWER_SAVE			0x15
36d8545825SLinus Walleij #define RTL8366RB_POWER_SAVE_ON			BIT(12)
37d8545825SLinus Walleij 
38097c2aa8SJohnson Leung MODULE_DESCRIPTION("Realtek PHY driver");
39097c2aa8SJohnson Leung MODULE_AUTHOR("Johnson Leung");
40097c2aa8SJohnson Leung MODULE_LICENSE("GPL");
41097c2aa8SJohnson Leung 
42d98c8ccdSHeiner Kallweit static int rtl821x_read_page(struct phy_device *phydev)
43136819a6SMartin Blumenstingl {
44d98c8ccdSHeiner Kallweit 	return __phy_read(phydev, RTL821x_PAGE_SELECT);
45136819a6SMartin Blumenstingl }
46136819a6SMartin Blumenstingl 
47d98c8ccdSHeiner Kallweit static int rtl821x_write_page(struct phy_device *phydev, int page)
48136819a6SMartin Blumenstingl {
49d98c8ccdSHeiner Kallweit 	return __phy_write(phydev, RTL821x_PAGE_SELECT, page);
50136819a6SMartin Blumenstingl }
51136819a6SMartin Blumenstingl 
52513588ddSJassi Brar static int rtl8201_ack_interrupt(struct phy_device *phydev)
53513588ddSJassi Brar {
54513588ddSJassi Brar 	int err;
55513588ddSJassi Brar 
56513588ddSJassi Brar 	err = phy_read(phydev, RTL8201F_ISR);
57513588ddSJassi Brar 
58513588ddSJassi Brar 	return (err < 0) ? err : 0;
59513588ddSJassi Brar }
60513588ddSJassi Brar 
61097c2aa8SJohnson Leung static int rtl821x_ack_interrupt(struct phy_device *phydev)
62097c2aa8SJohnson Leung {
63097c2aa8SJohnson Leung 	int err;
64097c2aa8SJohnson Leung 
65097c2aa8SJohnson Leung 	err = phy_read(phydev, RTL821x_INSR);
66097c2aa8SJohnson Leung 
67097c2aa8SJohnson Leung 	return (err < 0) ? err : 0;
68097c2aa8SJohnson Leung }
69097c2aa8SJohnson Leung 
703447cf2eSShengzhou Liu static int rtl8211f_ack_interrupt(struct phy_device *phydev)
713447cf2eSShengzhou Liu {
723447cf2eSShengzhou Liu 	int err;
733447cf2eSShengzhou Liu 
74d98c8ccdSHeiner Kallweit 	err = phy_read_paged(phydev, 0xa43, RTL8211F_INSR);
753447cf2eSShengzhou Liu 
763447cf2eSShengzhou Liu 	return (err < 0) ? err : 0;
773447cf2eSShengzhou Liu }
783447cf2eSShengzhou Liu 
79513588ddSJassi Brar static int rtl8201_config_intr(struct phy_device *phydev)
80513588ddSJassi Brar {
81136819a6SMartin Blumenstingl 	u16 val;
82513588ddSJassi Brar 
83513588ddSJassi Brar 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
84136819a6SMartin Blumenstingl 		val = BIT(13) | BIT(12) | BIT(11);
85513588ddSJassi Brar 	else
86136819a6SMartin Blumenstingl 		val = 0;
87513588ddSJassi Brar 
88d98c8ccdSHeiner Kallweit 	return phy_write_paged(phydev, 0x7, RTL8201F_IER, val);
89513588ddSJassi Brar }
90513588ddSJassi Brar 
91ef3d9049SGiuseppe CAVALLARO static int rtl8211b_config_intr(struct phy_device *phydev)
92097c2aa8SJohnson Leung {
93097c2aa8SJohnson Leung 	int err;
94097c2aa8SJohnson Leung 
95097c2aa8SJohnson Leung 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
96097c2aa8SJohnson Leung 		err = phy_write(phydev, RTL821x_INER,
9769021e32SMartin Blumenstingl 				RTL8211B_INER_INIT);
98097c2aa8SJohnson Leung 	else
99097c2aa8SJohnson Leung 		err = phy_write(phydev, RTL821x_INER, 0);
100097c2aa8SJohnson Leung 
101097c2aa8SJohnson Leung 	return err;
102097c2aa8SJohnson Leung }
103097c2aa8SJohnson Leung 
104ef3d9049SGiuseppe CAVALLARO static int rtl8211e_config_intr(struct phy_device *phydev)
105ef3d9049SGiuseppe CAVALLARO {
106ef3d9049SGiuseppe CAVALLARO 	int err;
107ef3d9049SGiuseppe CAVALLARO 
108ef3d9049SGiuseppe CAVALLARO 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
109ef3d9049SGiuseppe CAVALLARO 		err = phy_write(phydev, RTL821x_INER,
1108b64fd61SGiuseppe CAVALLARO 				RTL8211E_INER_LINK_STATUS);
111ef3d9049SGiuseppe CAVALLARO 	else
112ef3d9049SGiuseppe CAVALLARO 		err = phy_write(phydev, RTL821x_INER, 0);
113ef3d9049SGiuseppe CAVALLARO 
114ef3d9049SGiuseppe CAVALLARO 	return err;
115ef3d9049SGiuseppe CAVALLARO }
116ef3d9049SGiuseppe CAVALLARO 
1173447cf2eSShengzhou Liu static int rtl8211f_config_intr(struct phy_device *phydev)
1183447cf2eSShengzhou Liu {
119136819a6SMartin Blumenstingl 	u16 val;
1203447cf2eSShengzhou Liu 
1213447cf2eSShengzhou Liu 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
122136819a6SMartin Blumenstingl 		val = RTL8211F_INER_LINK_STATUS;
1233447cf2eSShengzhou Liu 	else
124136819a6SMartin Blumenstingl 		val = 0;
1253447cf2eSShengzhou Liu 
126d98c8ccdSHeiner Kallweit 	return phy_write_paged(phydev, 0xa42, RTL821x_INER, val);
1273447cf2eSShengzhou Liu }
1283447cf2eSShengzhou Liu 
129d241d4aaSHeiner Kallweit static int rtl8211_config_aneg(struct phy_device *phydev)
130d241d4aaSHeiner Kallweit {
131d241d4aaSHeiner Kallweit 	int ret;
132d241d4aaSHeiner Kallweit 
133d241d4aaSHeiner Kallweit 	ret = genphy_config_aneg(phydev);
134d241d4aaSHeiner Kallweit 	if (ret < 0)
135d241d4aaSHeiner Kallweit 		return ret;
136d241d4aaSHeiner Kallweit 
137d241d4aaSHeiner Kallweit 	/* Quirk was copied from vendor driver. Unfortunately it includes no
138d241d4aaSHeiner Kallweit 	 * description of the magic numbers.
139d241d4aaSHeiner Kallweit 	 */
140d241d4aaSHeiner Kallweit 	if (phydev->speed == SPEED_100 && phydev->autoneg == AUTONEG_DISABLE) {
141d241d4aaSHeiner Kallweit 		phy_write(phydev, 0x17, 0x2138);
142d241d4aaSHeiner Kallweit 		phy_write(phydev, 0x0e, 0x0260);
143d241d4aaSHeiner Kallweit 	} else {
144d241d4aaSHeiner Kallweit 		phy_write(phydev, 0x17, 0x2108);
145d241d4aaSHeiner Kallweit 		phy_write(phydev, 0x0e, 0x0000);
146d241d4aaSHeiner Kallweit 	}
147d241d4aaSHeiner Kallweit 
148d241d4aaSHeiner Kallweit 	return 0;
149d241d4aaSHeiner Kallweit }
150d241d4aaSHeiner Kallweit 
151cf87915cSHeiner Kallweit static int rtl8211c_config_init(struct phy_device *phydev)
152cf87915cSHeiner Kallweit {
153cf87915cSHeiner Kallweit 	/* RTL8211C has an issue when operating in Gigabit slave mode */
154*48e4adf9SHeiner Kallweit 	return phy_set_bits(phydev, MII_CTRL1000,
155cf87915cSHeiner Kallweit 			    CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER);
156cf87915cSHeiner Kallweit }
157cf87915cSHeiner Kallweit 
1583447cf2eSShengzhou Liu static int rtl8211f_config_init(struct phy_device *phydev)
1593447cf2eSShengzhou Liu {
160d98c8ccdSHeiner Kallweit 	u16 val = 0;
1613447cf2eSShengzhou Liu 
162e3230494SMartin Blumenstingl 	/* enable TX-delay for rgmii-id and rgmii-txid, otherwise disable it */
163e3230494SMartin Blumenstingl 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
164e3230494SMartin Blumenstingl 	    phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
165d98c8ccdSHeiner Kallweit 		val = RTL8211F_TX_DELAY;
166e3230494SMartin Blumenstingl 
167d98c8ccdSHeiner Kallweit 	return phy_modify_paged(phydev, 0xd08, 0x11, RTL8211F_TX_DELAY, val);
1683447cf2eSShengzhou Liu }
1693447cf2eSShengzhou Liu 
170049ff57aSHeiner Kallweit static int rtl8211b_suspend(struct phy_device *phydev)
171049ff57aSHeiner Kallweit {
172049ff57aSHeiner Kallweit 	phy_write(phydev, MII_MMD_DATA, BIT(9));
173049ff57aSHeiner Kallweit 
174049ff57aSHeiner Kallweit 	return genphy_suspend(phydev);
175049ff57aSHeiner Kallweit }
176049ff57aSHeiner Kallweit 
177049ff57aSHeiner Kallweit static int rtl8211b_resume(struct phy_device *phydev)
178049ff57aSHeiner Kallweit {
179049ff57aSHeiner Kallweit 	phy_write(phydev, MII_MMD_DATA, 0);
180049ff57aSHeiner Kallweit 
181049ff57aSHeiner Kallweit 	return genphy_resume(phydev);
182049ff57aSHeiner Kallweit }
183049ff57aSHeiner Kallweit 
184d8545825SLinus Walleij static int rtl8366rb_config_init(struct phy_device *phydev)
185d8545825SLinus Walleij {
186d8545825SLinus Walleij 	int ret;
187d8545825SLinus Walleij 
188d8545825SLinus Walleij 	ret = phy_set_bits(phydev, RTL8366RB_POWER_SAVE,
189d8545825SLinus Walleij 			   RTL8366RB_POWER_SAVE_ON);
190d8545825SLinus Walleij 	if (ret) {
191d8545825SLinus Walleij 		dev_err(&phydev->mdio.dev,
192d8545825SLinus Walleij 			"error enabling power management\n");
193d8545825SLinus Walleij 	}
194d8545825SLinus Walleij 
195d8545825SLinus Walleij 	return ret;
196d8545825SLinus Walleij }
197d8545825SLinus Walleij 
19871b9c4a8SJongsung Kim static struct phy_driver realtek_drvs[] = {
19971b9c4a8SJongsung Kim 	{
200ca494936SHeiner Kallweit 		PHY_ID_MATCH_EXACT(0x00008201),
20112959667SJonas Jensen 		.name           = "RTL8201CP Ethernet",
202*48e4adf9SHeiner Kallweit 		.get_features	= genphy_read_abilities,
20371b9c4a8SJongsung Kim 	}, {
204ca494936SHeiner Kallweit 		PHY_ID_MATCH_EXACT(0x001cc816),
2050432e833SHolger Hoffstätte 		.name		= "RTL8201F Fast Ethernet",
206*48e4adf9SHeiner Kallweit 		.get_features	= genphy_read_abilities,
207513588ddSJassi Brar 		.ack_interrupt	= &rtl8201_ack_interrupt,
208513588ddSJassi Brar 		.config_intr	= &rtl8201_config_intr,
209513588ddSJassi Brar 		.suspend	= genphy_suspend,
210513588ddSJassi Brar 		.resume		= genphy_resume,
211d98c8ccdSHeiner Kallweit 		.read_page	= rtl821x_read_page,
212d98c8ccdSHeiner Kallweit 		.write_page	= rtl821x_write_page,
213513588ddSJassi Brar 	}, {
214ca494936SHeiner Kallweit 		PHY_ID_MATCH_EXACT(0x001cc910),
215d241d4aaSHeiner Kallweit 		.name		= "RTL8211 Gigabit Ethernet",
216*48e4adf9SHeiner Kallweit 		.get_features	= genphy_read_abilities,
217d241d4aaSHeiner Kallweit 		.config_aneg	= rtl8211_config_aneg,
218d241d4aaSHeiner Kallweit 		.read_mmd	= &genphy_read_mmd_unsupported,
219d241d4aaSHeiner Kallweit 		.write_mmd	= &genphy_write_mmd_unsupported,
220d241d4aaSHeiner Kallweit 	}, {
221ca494936SHeiner Kallweit 		PHY_ID_MATCH_EXACT(0x001cc912),
222ef3d9049SGiuseppe CAVALLARO 		.name		= "RTL8211B Gigabit Ethernet",
223*48e4adf9SHeiner Kallweit 		.get_features	= genphy_read_abilities,
224097c2aa8SJohnson Leung 		.ack_interrupt	= &rtl821x_ack_interrupt,
225ef3d9049SGiuseppe CAVALLARO 		.config_intr	= &rtl8211b_config_intr,
2260231b1a0SKevin Hao 		.read_mmd	= &genphy_read_mmd_unsupported,
2270231b1a0SKevin Hao 		.write_mmd	= &genphy_write_mmd_unsupported,
228049ff57aSHeiner Kallweit 		.suspend	= rtl8211b_suspend,
229049ff57aSHeiner Kallweit 		.resume		= rtl8211b_resume,
23071b9c4a8SJongsung Kim 	}, {
231ca494936SHeiner Kallweit 		PHY_ID_MATCH_EXACT(0x001cc913),
232cf87915cSHeiner Kallweit 		.name		= "RTL8211C Gigabit Ethernet",
233*48e4adf9SHeiner Kallweit 		.get_features	= genphy_read_abilities,
234cf87915cSHeiner Kallweit 		.config_init	= rtl8211c_config_init,
235cf87915cSHeiner Kallweit 		.read_mmd	= &genphy_read_mmd_unsupported,
236cf87915cSHeiner Kallweit 		.write_mmd	= &genphy_write_mmd_unsupported,
237cf87915cSHeiner Kallweit 	}, {
238ca494936SHeiner Kallweit 		PHY_ID_MATCH_EXACT(0x001cc914),
2390024f892SShaohui Xie 		.name		= "RTL8211DN Gigabit Ethernet",
240*48e4adf9SHeiner Kallweit 		.get_features	= genphy_read_abilities,
2410024f892SShaohui Xie 		.ack_interrupt	= rtl821x_ack_interrupt,
2420024f892SShaohui Xie 		.config_intr	= rtl8211e_config_intr,
2430024f892SShaohui Xie 		.suspend	= genphy_suspend,
2440024f892SShaohui Xie 		.resume		= genphy_resume,
2450024f892SShaohui Xie 	}, {
246ca494936SHeiner Kallweit 		PHY_ID_MATCH_EXACT(0x001cc915),
247ef3d9049SGiuseppe CAVALLARO 		.name		= "RTL8211E Gigabit Ethernet",
248*48e4adf9SHeiner Kallweit 		.get_features	= genphy_read_abilities,
249ef3d9049SGiuseppe CAVALLARO 		.ack_interrupt	= &rtl821x_ack_interrupt,
250ef3d9049SGiuseppe CAVALLARO 		.config_intr	= &rtl8211e_config_intr,
251ef3d9049SGiuseppe CAVALLARO 		.suspend	= genphy_suspend,
252ef3d9049SGiuseppe CAVALLARO 		.resume		= genphy_resume,
2533447cf2eSShengzhou Liu 	}, {
254ca494936SHeiner Kallweit 		PHY_ID_MATCH_EXACT(0x001cc916),
2553447cf2eSShengzhou Liu 		.name		= "RTL8211F Gigabit Ethernet",
256*48e4adf9SHeiner Kallweit 		.get_features	= genphy_read_abilities,
2573447cf2eSShengzhou Liu 		.config_init	= &rtl8211f_config_init,
2583447cf2eSShengzhou Liu 		.ack_interrupt	= &rtl8211f_ack_interrupt,
2593447cf2eSShengzhou Liu 		.config_intr	= &rtl8211f_config_intr,
2603447cf2eSShengzhou Liu 		.suspend	= genphy_suspend,
2613447cf2eSShengzhou Liu 		.resume		= genphy_resume,
262d98c8ccdSHeiner Kallweit 		.read_page	= rtl821x_read_page,
263d98c8ccdSHeiner Kallweit 		.write_page	= rtl821x_write_page,
264d8545825SLinus Walleij 	}, {
265f66ebd14SHeiner Kallweit 		PHY_ID_MATCH_EXACT(0x001cc800),
266f66ebd14SHeiner Kallweit 		.name		= "Generic Realtek PHY",
267*48e4adf9SHeiner Kallweit 		.get_features	= genphy_read_abilities,
268f66ebd14SHeiner Kallweit 		.suspend	= genphy_suspend,
269f66ebd14SHeiner Kallweit 		.resume		= genphy_resume,
270f66ebd14SHeiner Kallweit 		.read_page	= rtl821x_read_page,
271f66ebd14SHeiner Kallweit 		.write_page	= rtl821x_write_page,
272f66ebd14SHeiner Kallweit 	}, {
273ca494936SHeiner Kallweit 		PHY_ID_MATCH_EXACT(0x001cc961),
274d8545825SLinus Walleij 		.name		= "RTL8366RB Gigabit Ethernet",
275*48e4adf9SHeiner Kallweit 		.get_features	= genphy_read_abilities,
276d8545825SLinus Walleij 		.config_init	= &rtl8366rb_config_init,
2774c8e0459SLinus Walleij 		/* These interrupts are handled by the irq controller
2784c8e0459SLinus Walleij 		 * embedded inside the RTL8366RB, they get unmasked when the
2794c8e0459SLinus Walleij 		 * irq is requested and ACKed by reading the status register,
2804c8e0459SLinus Walleij 		 * which is done by the irqchip code.
2814c8e0459SLinus Walleij 		 */
2824c8e0459SLinus Walleij 		.ack_interrupt	= genphy_no_ack_interrupt,
2834c8e0459SLinus Walleij 		.config_intr	= genphy_no_config_intr,
284d8545825SLinus Walleij 		.suspend	= genphy_suspend,
285d8545825SLinus Walleij 		.resume		= genphy_resume,
28671b9c4a8SJongsung Kim 	},
287097c2aa8SJohnson Leung };
288097c2aa8SJohnson Leung 
28950fd7150SJohan Hovold module_phy_driver(realtek_drvs);
2904e4f10f6SDavid Woodhouse 
2913b73e842SHeiner Kallweit static const struct mdio_device_id __maybe_unused realtek_tbl[] = {
292ca494936SHeiner Kallweit 	{ PHY_ID_MATCH_VENDOR(0x001cc800) },
2934e4f10f6SDavid Woodhouse 	{ }
2944e4f10f6SDavid Woodhouse };
2954e4f10f6SDavid Woodhouse 
2964e4f10f6SDavid Woodhouse MODULE_DEVICE_TABLE(mdio, realtek_tbl);
297