Commit | Line | Data |
---|---|---|
a2443fd1 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
097c2aa8 JL |
2 | /* |
3 | * drivers/net/phy/realtek.c | |
4 | * | |
5 | * Driver for Realtek PHYs | |
6 | * | |
7 | * Author: Johnson Leung <r58129@freescale.com> | |
8 | * | |
9 | * Copyright (c) 2004 Freescale Semiconductor, Inc. | |
097c2aa8 | 10 | */ |
8cc5baef | 11 | #include <linux/bitops.h> |
097c2aa8 | 12 | #include <linux/phy.h> |
9d9779e7 | 13 | #include <linux/module.h> |
097c2aa8 | 14 | |
f609ab0e MB |
15 | #define RTL821x_PHYSR 0x11 |
16 | #define RTL821x_PHYSR_DUPLEX BIT(13) | |
17 | #define RTL821x_PHYSR_SPEED GENMASK(15, 14) | |
a82f266d | 18 | |
f609ab0e MB |
19 | #define RTL821x_INER 0x12 |
20 | #define RTL8211B_INER_INIT 0x6400 | |
21 | #define RTL8211E_INER_LINK_STATUS BIT(10) | |
22 | #define RTL8211F_INER_LINK_STATUS BIT(4) | |
a82f266d | 23 | |
f609ab0e | 24 | #define RTL821x_INSR 0x13 |
a82f266d | 25 | |
f81dadbc | 26 | #define RTL821x_EXT_PAGE_SELECT 0x1e |
f609ab0e | 27 | #define RTL821x_PAGE_SELECT 0x1f |
097c2aa8 | 28 | |
f609ab0e | 29 | #define RTL8211F_INSR 0x1d |
ef3d9049 | 30 | |
f609ab0e | 31 | #define RTL8211F_TX_DELAY BIT(8) |
f81dadbc SS |
32 | #define RTL8211E_TX_DELAY BIT(1) |
33 | #define RTL8211E_RX_DELAY BIT(2) | |
34 | #define RTL8211E_MODE_MII_GMII BIT(3) | |
f609ab0e MB |
35 | |
36 | #define RTL8201F_ISR 0x1e | |
37 | #define RTL8201F_IER 0x13 | |
513588dd | 38 | |
d8545825 LW |
39 | #define RTL8366RB_POWER_SAVE 0x15 |
40 | #define RTL8366RB_POWER_SAVE_ON BIT(12) | |
41 | ||
097c2aa8 JL |
42 | MODULE_DESCRIPTION("Realtek PHY driver"); |
43 | MODULE_AUTHOR("Johnson Leung"); | |
44 | MODULE_LICENSE("GPL"); | |
45 | ||
d98c8ccd | 46 | static int rtl821x_read_page(struct phy_device *phydev) |
136819a6 | 47 | { |
d98c8ccd | 48 | return __phy_read(phydev, RTL821x_PAGE_SELECT); |
136819a6 MB |
49 | } |
50 | ||
d98c8ccd | 51 | static int rtl821x_write_page(struct phy_device *phydev, int page) |
136819a6 | 52 | { |
d98c8ccd | 53 | return __phy_write(phydev, RTL821x_PAGE_SELECT, page); |
136819a6 MB |
54 | } |
55 | ||
513588dd JB |
56 | static int rtl8201_ack_interrupt(struct phy_device *phydev) |
57 | { | |
58 | int err; | |
59 | ||
60 | err = phy_read(phydev, RTL8201F_ISR); | |
61 | ||
62 | return (err < 0) ? err : 0; | |
63 | } | |
64 | ||
097c2aa8 JL |
65 | static int rtl821x_ack_interrupt(struct phy_device *phydev) |
66 | { | |
67 | int err; | |
68 | ||
69 | err = phy_read(phydev, RTL821x_INSR); | |
70 | ||
71 | return (err < 0) ? err : 0; | |
72 | } | |
73 | ||
3447cf2e SL |
74 | static int rtl8211f_ack_interrupt(struct phy_device *phydev) |
75 | { | |
76 | int err; | |
77 | ||
d98c8ccd | 78 | err = phy_read_paged(phydev, 0xa43, RTL8211F_INSR); |
3447cf2e SL |
79 | |
80 | return (err < 0) ? err : 0; | |
81 | } | |
82 | ||
513588dd JB |
83 | static int rtl8201_config_intr(struct phy_device *phydev) |
84 | { | |
136819a6 | 85 | u16 val; |
513588dd JB |
86 | |
87 | if (phydev->interrupts == PHY_INTERRUPT_ENABLED) | |
136819a6 | 88 | val = BIT(13) | BIT(12) | BIT(11); |
513588dd | 89 | else |
136819a6 | 90 | val = 0; |
513588dd | 91 | |
d98c8ccd | 92 | return phy_write_paged(phydev, 0x7, RTL8201F_IER, val); |
513588dd JB |
93 | } |
94 | ||
ef3d9049 | 95 | static int rtl8211b_config_intr(struct phy_device *phydev) |
097c2aa8 JL |
96 | { |
97 | int err; | |
98 | ||
99 | if (phydev->interrupts == PHY_INTERRUPT_ENABLED) | |
100 | err = phy_write(phydev, RTL821x_INER, | |
69021e32 | 101 | RTL8211B_INER_INIT); |
097c2aa8 JL |
102 | else |
103 | err = phy_write(phydev, RTL821x_INER, 0); | |
104 | ||
105 | return err; | |
106 | } | |
107 | ||
ef3d9049 GC |
108 | static int rtl8211e_config_intr(struct phy_device *phydev) |
109 | { | |
110 | int err; | |
111 | ||
112 | if (phydev->interrupts == PHY_INTERRUPT_ENABLED) | |
113 | err = phy_write(phydev, RTL821x_INER, | |
8b64fd61 | 114 | RTL8211E_INER_LINK_STATUS); |
ef3d9049 GC |
115 | else |
116 | err = phy_write(phydev, RTL821x_INER, 0); | |
117 | ||
118 | return err; | |
119 | } | |
120 | ||
3447cf2e SL |
121 | static int rtl8211f_config_intr(struct phy_device *phydev) |
122 | { | |
136819a6 | 123 | u16 val; |
3447cf2e SL |
124 | |
125 | if (phydev->interrupts == PHY_INTERRUPT_ENABLED) | |
136819a6 | 126 | val = RTL8211F_INER_LINK_STATUS; |
3447cf2e | 127 | else |
136819a6 | 128 | val = 0; |
3447cf2e | 129 | |
d98c8ccd | 130 | return phy_write_paged(phydev, 0xa42, RTL821x_INER, val); |
3447cf2e SL |
131 | } |
132 | ||
d241d4aa HK |
133 | static int rtl8211_config_aneg(struct phy_device *phydev) |
134 | { | |
135 | int ret; | |
136 | ||
137 | ret = genphy_config_aneg(phydev); | |
138 | if (ret < 0) | |
139 | return ret; | |
140 | ||
141 | /* Quirk was copied from vendor driver. Unfortunately it includes no | |
142 | * description of the magic numbers. | |
143 | */ | |
144 | if (phydev->speed == SPEED_100 && phydev->autoneg == AUTONEG_DISABLE) { | |
145 | phy_write(phydev, 0x17, 0x2138); | |
146 | phy_write(phydev, 0x0e, 0x0260); | |
147 | } else { | |
148 | phy_write(phydev, 0x17, 0x2108); | |
149 | phy_write(phydev, 0x0e, 0x0000); | |
150 | } | |
151 | ||
152 | return 0; | |
153 | } | |
154 | ||
cf87915c HK |
155 | static int rtl8211c_config_init(struct phy_device *phydev) |
156 | { | |
157 | /* RTL8211C has an issue when operating in Gigabit slave mode */ | |
48e4adf9 HK |
158 | return phy_set_bits(phydev, MII_CTRL1000, |
159 | CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER); | |
cf87915c HK |
160 | } |
161 | ||
3447cf2e SL |
162 | static int rtl8211f_config_init(struct phy_device *phydev) |
163 | { | |
d98c8ccd | 164 | u16 val = 0; |
3447cf2e | 165 | |
e3230494 MB |
166 | /* enable TX-delay for rgmii-id and rgmii-txid, otherwise disable it */ |
167 | if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || | |
168 | phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) | |
d98c8ccd | 169 | val = RTL8211F_TX_DELAY; |
3447cf2e | 170 | |
d98c8ccd | 171 | return phy_modify_paged(phydev, 0xd08, 0x11, RTL8211F_TX_DELAY, val); |
3447cf2e SL |
172 | } |
173 | ||
f81dadbc SS |
174 | static int rtl8211e_config_init(struct phy_device *phydev) |
175 | { | |
176 | int ret = 0, oldpage; | |
177 | u16 val; | |
178 | ||
179 | /* enable TX/RX delay for rgmii-* modes, and disable them for rgmii. */ | |
180 | switch (phydev->interface) { | |
181 | case PHY_INTERFACE_MODE_RGMII: | |
182 | val = 0; | |
183 | break; | |
184 | case PHY_INTERFACE_MODE_RGMII_ID: | |
185 | val = RTL8211E_TX_DELAY | RTL8211E_RX_DELAY; | |
186 | break; | |
187 | case PHY_INTERFACE_MODE_RGMII_RXID: | |
188 | val = RTL8211E_RX_DELAY; | |
189 | break; | |
190 | case PHY_INTERFACE_MODE_RGMII_TXID: | |
191 | val = RTL8211E_TX_DELAY; | |
192 | break; | |
193 | default: /* the rest of the modes imply leaving delays as is. */ | |
194 | return 0; | |
195 | } | |
196 | ||
197 | /* According to a sample driver there is a 0x1c config register on the | |
198 | * 0xa4 extension page (0x7) layout. It can be used to disable/enable | |
199 | * the RX/TX delays otherwise controlled by RXDLY/TXDLY pins. It can | |
200 | * also be used to customize the whole configuration register: | |
201 | * 8:6 = PHY Address, 5:4 = Auto-Negotiation, 3 = Interface Mode Select, | |
202 | * 2 = RX Delay, 1 = TX Delay, 0 = SELRGV (see original PHY datasheet | |
203 | * for details). | |
204 | */ | |
205 | oldpage = phy_select_page(phydev, 0x7); | |
206 | if (oldpage < 0) | |
207 | goto err_restore_page; | |
208 | ||
209 | ret = phy_write(phydev, RTL821x_EXT_PAGE_SELECT, 0xa4); | |
210 | if (ret) | |
211 | goto err_restore_page; | |
212 | ||
213 | ret = phy_modify(phydev, 0x1c, RTL8211E_TX_DELAY | RTL8211E_RX_DELAY, | |
214 | val); | |
215 | ||
216 | err_restore_page: | |
217 | return phy_restore_page(phydev, oldpage, ret); | |
218 | } | |
219 | ||
049ff57a HK |
220 | static int rtl8211b_suspend(struct phy_device *phydev) |
221 | { | |
222 | phy_write(phydev, MII_MMD_DATA, BIT(9)); | |
223 | ||
224 | return genphy_suspend(phydev); | |
225 | } | |
226 | ||
227 | static int rtl8211b_resume(struct phy_device *phydev) | |
228 | { | |
229 | phy_write(phydev, MII_MMD_DATA, 0); | |
230 | ||
231 | return genphy_resume(phydev); | |
232 | } | |
233 | ||
d8545825 LW |
234 | static int rtl8366rb_config_init(struct phy_device *phydev) |
235 | { | |
236 | int ret; | |
237 | ||
d8545825 LW |
238 | ret = phy_set_bits(phydev, RTL8366RB_POWER_SAVE, |
239 | RTL8366RB_POWER_SAVE_ON); | |
240 | if (ret) { | |
241 | dev_err(&phydev->mdio.dev, | |
242 | "error enabling power management\n"); | |
243 | } | |
244 | ||
245 | return ret; | |
246 | } | |
247 | ||
71b9c4a8 JK |
248 | static struct phy_driver realtek_drvs[] = { |
249 | { | |
ca494936 | 250 | PHY_ID_MATCH_EXACT(0x00008201), |
71b9c4a8 | 251 | .name = "RTL8201CP Ethernet", |
513588dd | 252 | }, { |
ca494936 | 253 | PHY_ID_MATCH_EXACT(0x001cc816), |
0432e833 | 254 | .name = "RTL8201F Fast Ethernet", |
513588dd JB |
255 | .ack_interrupt = &rtl8201_ack_interrupt, |
256 | .config_intr = &rtl8201_config_intr, | |
257 | .suspend = genphy_suspend, | |
258 | .resume = genphy_resume, | |
d98c8ccd HK |
259 | .read_page = rtl821x_read_page, |
260 | .write_page = rtl821x_write_page, | |
d241d4aa | 261 | }, { |
ca494936 | 262 | PHY_ID_MATCH_EXACT(0x001cc910), |
d241d4aa | 263 | .name = "RTL8211 Gigabit Ethernet", |
d241d4aa HK |
264 | .config_aneg = rtl8211_config_aneg, |
265 | .read_mmd = &genphy_read_mmd_unsupported, | |
266 | .write_mmd = &genphy_write_mmd_unsupported, | |
71b9c4a8 | 267 | }, { |
ca494936 | 268 | PHY_ID_MATCH_EXACT(0x001cc912), |
71b9c4a8 | 269 | .name = "RTL8211B Gigabit Ethernet", |
71b9c4a8 JK |
270 | .ack_interrupt = &rtl821x_ack_interrupt, |
271 | .config_intr = &rtl8211b_config_intr, | |
0231b1a0 KH |
272 | .read_mmd = &genphy_read_mmd_unsupported, |
273 | .write_mmd = &genphy_write_mmd_unsupported, | |
049ff57a HK |
274 | .suspend = rtl8211b_suspend, |
275 | .resume = rtl8211b_resume, | |
cf87915c | 276 | }, { |
ca494936 | 277 | PHY_ID_MATCH_EXACT(0x001cc913), |
cf87915c | 278 | .name = "RTL8211C Gigabit Ethernet", |
cf87915c HK |
279 | .config_init = rtl8211c_config_init, |
280 | .read_mmd = &genphy_read_mmd_unsupported, | |
281 | .write_mmd = &genphy_write_mmd_unsupported, | |
0024f892 | 282 | }, { |
ca494936 | 283 | PHY_ID_MATCH_EXACT(0x001cc914), |
0024f892 | 284 | .name = "RTL8211DN Gigabit Ethernet", |
0024f892 SX |
285 | .ack_interrupt = rtl821x_ack_interrupt, |
286 | .config_intr = rtl8211e_config_intr, | |
287 | .suspend = genphy_suspend, | |
288 | .resume = genphy_resume, | |
71b9c4a8 | 289 | }, { |
ca494936 | 290 | PHY_ID_MATCH_EXACT(0x001cc915), |
71b9c4a8 | 291 | .name = "RTL8211E Gigabit Ethernet", |
f81dadbc | 292 | .config_init = &rtl8211e_config_init, |
71b9c4a8 JK |
293 | .ack_interrupt = &rtl821x_ack_interrupt, |
294 | .config_intr = &rtl8211e_config_intr, | |
295 | .suspend = genphy_suspend, | |
296 | .resume = genphy_resume, | |
3447cf2e | 297 | }, { |
ca494936 | 298 | PHY_ID_MATCH_EXACT(0x001cc916), |
3447cf2e | 299 | .name = "RTL8211F Gigabit Ethernet", |
3447cf2e | 300 | .config_init = &rtl8211f_config_init, |
3447cf2e SL |
301 | .ack_interrupt = &rtl8211f_ack_interrupt, |
302 | .config_intr = &rtl8211f_config_intr, | |
303 | .suspend = genphy_suspend, | |
304 | .resume = genphy_resume, | |
d98c8ccd HK |
305 | .read_page = rtl821x_read_page, |
306 | .write_page = rtl821x_write_page, | |
f66ebd14 HK |
307 | }, { |
308 | PHY_ID_MATCH_EXACT(0x001cc800), | |
309 | .name = "Generic Realtek PHY", | |
f66ebd14 HK |
310 | .suspend = genphy_suspend, |
311 | .resume = genphy_resume, | |
312 | .read_page = rtl821x_read_page, | |
313 | .write_page = rtl821x_write_page, | |
d8545825 | 314 | }, { |
ca494936 | 315 | PHY_ID_MATCH_EXACT(0x001cc961), |
d8545825 | 316 | .name = "RTL8366RB Gigabit Ethernet", |
d8545825 | 317 | .config_init = &rtl8366rb_config_init, |
4c8e0459 LW |
318 | /* These interrupts are handled by the irq controller |
319 | * embedded inside the RTL8366RB, they get unmasked when the | |
320 | * irq is requested and ACKed by reading the status register, | |
321 | * which is done by the irqchip code. | |
322 | */ | |
323 | .ack_interrupt = genphy_no_ack_interrupt, | |
324 | .config_intr = genphy_no_config_intr, | |
d8545825 LW |
325 | .suspend = genphy_suspend, |
326 | .resume = genphy_resume, | |
71b9c4a8 | 327 | }, |
097c2aa8 JL |
328 | }; |
329 | ||
50fd7150 | 330 | module_phy_driver(realtek_drvs); |
4e4f10f6 | 331 | |
3b73e842 | 332 | static const struct mdio_device_id __maybe_unused realtek_tbl[] = { |
ca494936 | 333 | { PHY_ID_MATCH_VENDOR(0x001cc800) }, |
4e4f10f6 DW |
334 | { } |
335 | }; | |
336 | ||
337 | MODULE_DEVICE_TABLE(mdio, realtek_tbl); |