xref: /openbmc/linux/drivers/net/phy/realtek.c (revision a0ae2562c6c4b2721d9fddba63b7286c13517d9f)
1 /*
2  * drivers/net/phy/realtek.c
3  *
4  * Driver for Realtek PHYs
5  *
6  * Author: Johnson Leung <r58129@freescale.com>
7  *
8  * Copyright (c) 2004 Freescale Semiconductor, Inc.
9  *
10  * This program is free software; you can redistribute  it and/or modify it
11  * under  the terms of  the GNU General  Public License as published by the
12  * Free Software Foundation;  either version 2 of the  License, or (at your
13  * option) any later version.
14  *
15  */
16 #include <linux/bitops.h>
17 #include <linux/phy.h>
18 #include <linux/module.h>
19 
20 #define RTL821x_PHYSR				0x11
21 #define RTL821x_PHYSR_DUPLEX			BIT(13)
22 #define RTL821x_PHYSR_SPEED			GENMASK(15, 14)
23 
24 #define RTL821x_INER				0x12
25 #define RTL8211B_INER_INIT			0x6400
26 #define RTL8211E_INER_LINK_STATUS		BIT(10)
27 #define RTL8211F_INER_LINK_STATUS		BIT(4)
28 
29 #define RTL821x_INSR				0x13
30 
31 #define RTL821x_PAGE_SELECT			0x1f
32 
33 #define RTL8211F_INSR				0x1d
34 
35 #define RTL8211F_TX_DELAY			BIT(8)
36 
37 #define RTL8201F_ISR				0x1e
38 #define RTL8201F_IER				0x13
39 
40 MODULE_DESCRIPTION("Realtek PHY driver");
41 MODULE_AUTHOR("Johnson Leung");
42 MODULE_LICENSE("GPL");
43 
44 static int rtl821x_read_page(struct phy_device *phydev)
45 {
46 	return __phy_read(phydev, RTL821x_PAGE_SELECT);
47 }
48 
49 static int rtl821x_write_page(struct phy_device *phydev, int page)
50 {
51 	return __phy_write(phydev, RTL821x_PAGE_SELECT, page);
52 }
53 
54 static int rtl8201_ack_interrupt(struct phy_device *phydev)
55 {
56 	int err;
57 
58 	err = phy_read(phydev, RTL8201F_ISR);
59 
60 	return (err < 0) ? err : 0;
61 }
62 
63 static int rtl821x_ack_interrupt(struct phy_device *phydev)
64 {
65 	int err;
66 
67 	err = phy_read(phydev, RTL821x_INSR);
68 
69 	return (err < 0) ? err : 0;
70 }
71 
72 static int rtl8211f_ack_interrupt(struct phy_device *phydev)
73 {
74 	int err;
75 
76 	err = phy_read_paged(phydev, 0xa43, RTL8211F_INSR);
77 
78 	return (err < 0) ? err : 0;
79 }
80 
81 static int rtl8201_config_intr(struct phy_device *phydev)
82 {
83 	u16 val;
84 
85 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
86 		val = BIT(13) | BIT(12) | BIT(11);
87 	else
88 		val = 0;
89 
90 	return phy_write_paged(phydev, 0x7, RTL8201F_IER, val);
91 }
92 
93 static int rtl8211b_config_intr(struct phy_device *phydev)
94 {
95 	int err;
96 
97 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
98 		err = phy_write(phydev, RTL821x_INER,
99 				RTL8211B_INER_INIT);
100 	else
101 		err = phy_write(phydev, RTL821x_INER, 0);
102 
103 	return err;
104 }
105 
106 static int rtl8211e_config_intr(struct phy_device *phydev)
107 {
108 	int err;
109 
110 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
111 		err = phy_write(phydev, RTL821x_INER,
112 				RTL8211E_INER_LINK_STATUS);
113 	else
114 		err = phy_write(phydev, RTL821x_INER, 0);
115 
116 	return err;
117 }
118 
119 static int rtl8211f_config_intr(struct phy_device *phydev)
120 {
121 	u16 val;
122 
123 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
124 		val = RTL8211F_INER_LINK_STATUS;
125 	else
126 		val = 0;
127 
128 	return phy_write_paged(phydev, 0xa42, RTL821x_INER, val);
129 }
130 
131 static int rtl8211_config_aneg(struct phy_device *phydev)
132 {
133 	int ret;
134 
135 	ret = genphy_config_aneg(phydev);
136 	if (ret < 0)
137 		return ret;
138 
139 	/* Quirk was copied from vendor driver. Unfortunately it includes no
140 	 * description of the magic numbers.
141 	 */
142 	if (phydev->speed == SPEED_100 && phydev->autoneg == AUTONEG_DISABLE) {
143 		phy_write(phydev, 0x17, 0x2138);
144 		phy_write(phydev, 0x0e, 0x0260);
145 	} else {
146 		phy_write(phydev, 0x17, 0x2108);
147 		phy_write(phydev, 0x0e, 0x0000);
148 	}
149 
150 	return 0;
151 }
152 
153 static int rtl8211c_config_init(struct phy_device *phydev)
154 {
155 	/* RTL8211C has an issue when operating in Gigabit slave mode */
156 	phy_set_bits(phydev, MII_CTRL1000,
157 		     CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER);
158 
159 	return genphy_config_init(phydev);
160 }
161 
162 static int rtl8211f_config_init(struct phy_device *phydev)
163 {
164 	int ret;
165 	u16 val = 0;
166 
167 	ret = genphy_config_init(phydev);
168 	if (ret < 0)
169 		return ret;
170 
171 	/* enable TX-delay for rgmii-id and rgmii-txid, otherwise disable it */
172 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
173 	    phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
174 		val = RTL8211F_TX_DELAY;
175 
176 	return phy_modify_paged(phydev, 0xd08, 0x11, RTL8211F_TX_DELAY, val);
177 }
178 
179 static int rtl8211b_suspend(struct phy_device *phydev)
180 {
181 	phy_write(phydev, MII_MMD_DATA, BIT(9));
182 
183 	return genphy_suspend(phydev);
184 }
185 
186 static int rtl8211b_resume(struct phy_device *phydev)
187 {
188 	phy_write(phydev, MII_MMD_DATA, 0);
189 
190 	return genphy_resume(phydev);
191 }
192 
193 static struct phy_driver realtek_drvs[] = {
194 	{
195 		.phy_id         = 0x00008201,
196 		.name           = "RTL8201CP Ethernet",
197 		.phy_id_mask    = 0x0000ffff,
198 		.features       = PHY_BASIC_FEATURES,
199 		.flags          = PHY_HAS_INTERRUPT,
200 	}, {
201 		.phy_id		= 0x001cc816,
202 		.name		= "RTL8201F 10/100Mbps Ethernet",
203 		.phy_id_mask	= 0x001fffff,
204 		.features	= PHY_BASIC_FEATURES,
205 		.flags		= PHY_HAS_INTERRUPT,
206 		.ack_interrupt	= &rtl8201_ack_interrupt,
207 		.config_intr	= &rtl8201_config_intr,
208 		.suspend	= genphy_suspend,
209 		.resume		= genphy_resume,
210 		.read_page	= rtl821x_read_page,
211 		.write_page	= rtl821x_write_page,
212 	}, {
213 		.phy_id		= 0x001cc910,
214 		.name		= "RTL8211 Gigabit Ethernet",
215 		.phy_id_mask	= 0x001fffff,
216 		.features	= PHY_GBIT_FEATURES,
217 		.config_aneg	= rtl8211_config_aneg,
218 		.read_mmd	= &genphy_read_mmd_unsupported,
219 		.write_mmd	= &genphy_write_mmd_unsupported,
220 	}, {
221 		.phy_id		= 0x001cc912,
222 		.name		= "RTL8211B Gigabit Ethernet",
223 		.phy_id_mask	= 0x001fffff,
224 		.features	= PHY_GBIT_FEATURES,
225 		.flags		= PHY_HAS_INTERRUPT,
226 		.ack_interrupt	= &rtl821x_ack_interrupt,
227 		.config_intr	= &rtl8211b_config_intr,
228 		.read_mmd	= &genphy_read_mmd_unsupported,
229 		.write_mmd	= &genphy_write_mmd_unsupported,
230 		.suspend	= rtl8211b_suspend,
231 		.resume		= rtl8211b_resume,
232 	}, {
233 		.phy_id		= 0x001cc913,
234 		.name		= "RTL8211C Gigabit Ethernet",
235 		.phy_id_mask	= 0x001fffff,
236 		.features	= PHY_GBIT_FEATURES,
237 		.config_init	= rtl8211c_config_init,
238 		.read_mmd	= &genphy_read_mmd_unsupported,
239 		.write_mmd	= &genphy_write_mmd_unsupported,
240 	}, {
241 		.phy_id		= 0x001cc914,
242 		.name		= "RTL8211DN Gigabit Ethernet",
243 		.phy_id_mask	= 0x001fffff,
244 		.features	= PHY_GBIT_FEATURES,
245 		.flags		= PHY_HAS_INTERRUPT,
246 		.ack_interrupt	= rtl821x_ack_interrupt,
247 		.config_intr	= rtl8211e_config_intr,
248 		.suspend	= genphy_suspend,
249 		.resume		= genphy_resume,
250 	}, {
251 		.phy_id		= 0x001cc915,
252 		.name		= "RTL8211E Gigabit Ethernet",
253 		.phy_id_mask	= 0x001fffff,
254 		.features	= PHY_GBIT_FEATURES,
255 		.flags		= PHY_HAS_INTERRUPT,
256 		.ack_interrupt	= &rtl821x_ack_interrupt,
257 		.config_intr	= &rtl8211e_config_intr,
258 		.suspend	= genphy_suspend,
259 		.resume		= genphy_resume,
260 	}, {
261 		.phy_id		= 0x001cc916,
262 		.name		= "RTL8211F Gigabit Ethernet",
263 		.phy_id_mask	= 0x001fffff,
264 		.features	= PHY_GBIT_FEATURES,
265 		.flags		= PHY_HAS_INTERRUPT,
266 		.config_init	= &rtl8211f_config_init,
267 		.ack_interrupt	= &rtl8211f_ack_interrupt,
268 		.config_intr	= &rtl8211f_config_intr,
269 		.suspend	= genphy_suspend,
270 		.resume		= genphy_resume,
271 		.read_page	= rtl821x_read_page,
272 		.write_page	= rtl821x_write_page,
273 	},
274 };
275 
276 module_phy_driver(realtek_drvs);
277 
278 static struct mdio_device_id __maybe_unused realtek_tbl[] = {
279 	{ 0x001cc816, 0x001fffff },
280 	{ 0x001cc910, 0x001fffff },
281 	{ 0x001cc912, 0x001fffff },
282 	{ 0x001cc914, 0x001fffff },
283 	{ 0x001cc915, 0x001fffff },
284 	{ 0x001cc916, 0x001fffff },
285 	{ }
286 };
287 
288 MODULE_DEVICE_TABLE(mdio, realtek_tbl);
289