Commit | Line | Data |
---|---|---|
a2443fd1 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
b560a58c FF |
2 | /* |
3 | * Broadcom BCM7xxx internal transceivers support. | |
4 | * | |
83ee102a | 5 | * Copyright (C) 2014-2017 Broadcom |
b560a58c FF |
6 | */ |
7 | ||
8 | #include <linux/module.h> | |
9 | #include <linux/phy.h> | |
10 | #include <linux/delay.h> | |
a1cba561 | 11 | #include "bcm-phy-lib.h" |
b560a58c FF |
12 | #include <linux/bitops.h> |
13 | #include <linux/brcmphy.h> | |
b8f9a029 | 14 | #include <linux/mdio.h> |
b560a58c FF |
15 | |
16 | /* Broadcom BCM7xxx internal PHY registers */ | |
b560a58c | 17 | |
83ee102a | 18 | /* EPHY only register definitions */ |
b560a58c FF |
19 | #define MII_BCM7XXX_100TX_AUX_CTL 0x10 |
20 | #define MII_BCM7XXX_100TX_FALSE_CAR 0x13 | |
21 | #define MII_BCM7XXX_100TX_DISC 0x14 | |
22 | #define MII_BCM7XXX_AUX_MODE 0x1d | |
3ccc3055 | 23 | #define MII_BCM7XXX_64CLK_MDIO BIT(12) |
b560a58c FF |
24 | #define MII_BCM7XXX_TEST 0x1f |
25 | #define MII_BCM7XXX_SHD_MODE_2 BIT(2) | |
83ee102a DB |
26 | #define MII_BCM7XXX_SHD_2_ADDR_CTRL 0xe |
27 | #define MII_BCM7XXX_SHD_2_CTRL_STAT 0xf | |
28 | #define MII_BCM7XXX_SHD_2_BIAS_TRIM 0x1a | |
29 | #define MII_BCM7XXX_SHD_3_AN_EEE_ADV 0x3 | |
30 | #define MII_BCM7XXX_SHD_3_PCS_CTRL_2 0x6 | |
31 | #define MII_BCM7XXX_PCS_CTRL_2_DEF 0x4400 | |
32 | #define MII_BCM7XXX_SHD_3_AN_STAT 0xb | |
33 | #define MII_BCM7XXX_AN_NULL_MSG_EN BIT(0) | |
34 | #define MII_BCM7XXX_AN_EEE_EN BIT(1) | |
35 | #define MII_BCM7XXX_SHD_3_EEE_THRESH 0xe | |
36 | #define MII_BCM7XXX_EEE_THRESH_DEF 0x50 | |
37 | #define MII_BCM7XXX_SHD_3_TL4 0x23 | |
38 | #define MII_BCM7XXX_TL4_RST_MSK (BIT(2) | BIT(1)) | |
b560a58c | 39 | |
a3622f2c FF |
40 | /* 28nm only register definitions */ |
41 | #define MISC_ADDR(base, channel) base, channel | |
42 | ||
43 | #define DSP_TAP10 MISC_ADDR(0x0a, 0) | |
44 | #define PLL_PLLCTRL_1 MISC_ADDR(0x32, 1) | |
45 | #define PLL_PLLCTRL_2 MISC_ADDR(0x32, 2) | |
46 | #define PLL_PLLCTRL_4 MISC_ADDR(0x33, 0) | |
47 | ||
48 | #define AFE_RXCONFIG_0 MISC_ADDR(0x38, 0) | |
49 | #define AFE_RXCONFIG_1 MISC_ADDR(0x38, 1) | |
a490631f | 50 | #define AFE_RXCONFIG_2 MISC_ADDR(0x38, 2) |
a3622f2c FF |
51 | #define AFE_RX_LP_COUNTER MISC_ADDR(0x38, 3) |
52 | #define AFE_TX_CONFIG MISC_ADDR(0x39, 0) | |
a490631f FF |
53 | #define AFE_VDCA_ICTRL_0 MISC_ADDR(0x39, 1) |
54 | #define AFE_VDAC_OTHERS_0 MISC_ADDR(0x39, 3) | |
a3622f2c FF |
55 | #define AFE_HPF_TRIM_OTHERS MISC_ADDR(0x3a, 0) |
56 | ||
b23ce9e8 FF |
57 | struct bcm7xxx_phy_priv { |
58 | u64 *stats; | |
59 | }; | |
60 | ||
9c41f2ba FF |
61 | static void r_rc_cal_reset(struct phy_device *phydev) |
62 | { | |
63 | /* Reset R_CAL/RC_CAL Engine */ | |
79fb218d | 64 | bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0010); |
9c41f2ba FF |
65 | |
66 | /* Disable Reset R_AL/RC_CAL Engine */ | |
79fb218d | 67 | bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0000); |
9c41f2ba FF |
68 | } |
69 | ||
2a9df742 | 70 | static int bcm7xxx_28nm_b0_afe_config_init(struct phy_device *phydev) |
b560a58c | 71 | { |
b560a58c FF |
72 | /* Increase VCO range to prevent unlocking problem of PLL at low |
73 | * temp | |
74 | */ | |
a1cba561 | 75 | bcm_phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048); |
b560a58c FF |
76 | |
77 | /* Change Ki to 011 */ | |
a1cba561 | 78 | bcm_phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b); |
b560a58c FF |
79 | |
80 | /* Disable loading of TVCO buffer to bandgap, set bandgap trim | |
81 | * to 111 | |
82 | */ | |
a1cba561 | 83 | bcm_phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20); |
b560a58c FF |
84 | |
85 | /* Adjust bias current trim by -3 */ | |
a1cba561 | 86 | bcm_phy_write_misc(phydev, DSP_TAP10, 0x690b); |
b560a58c FF |
87 | |
88 | /* Switch to CORE_BASE1E */ | |
9200c27a | 89 | phy_write(phydev, MII_BRCM_CORE_BASE1E, 0xd); |
b560a58c | 90 | |
9c41f2ba | 91 | r_rc_cal_reset(phydev); |
b560a58c | 92 | |
9918542e | 93 | /* write AFE_RXCONFIG_0 */ |
a1cba561 | 94 | bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19); |
9918542e FF |
95 | |
96 | /* write AFE_RXCONFIG_1 */ | |
a1cba561 | 97 | bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f); |
9918542e FF |
98 | |
99 | /* write AFE_RX_LP_COUNTER */ | |
a1cba561 | 100 | bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0); |
9918542e FF |
101 | |
102 | /* write AFE_HPF_TRIM_OTHERS */ | |
a1cba561 | 103 | bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b); |
9918542e FF |
104 | |
105 | /* write AFTE_TX_CONFIG */ | |
a1cba561 | 106 | bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800); |
9918542e | 107 | |
b560a58c FF |
108 | return 0; |
109 | } | |
110 | ||
a490631f FF |
111 | static int bcm7xxx_28nm_d0_afe_config_init(struct phy_device *phydev) |
112 | { | |
113 | /* AFE_RXCONFIG_0 */ | |
a1cba561 | 114 | bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb15); |
a490631f FF |
115 | |
116 | /* AFE_RXCONFIG_1 */ | |
a1cba561 | 117 | bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f); |
a490631f FF |
118 | |
119 | /* AFE_RXCONFIG_2, set rCal offset for HT=0 code and LT=-2 code */ | |
a1cba561 | 120 | bcm_phy_write_misc(phydev, AFE_RXCONFIG_2, 0x2003); |
a490631f FF |
121 | |
122 | /* AFE_RX_LP_COUNTER, set RX bandwidth to maximum */ | |
a1cba561 | 123 | bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0); |
a490631f | 124 | |
6da8253b | 125 | /* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */ |
a1cba561 | 126 | bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x431); |
a490631f FF |
127 | |
128 | /* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */ | |
a1cba561 | 129 | bcm_phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da); |
a490631f FF |
130 | |
131 | /* AFE_VDAC_OTHERS_0, set 1000BT Cidac=010 for all ports */ | |
a1cba561 | 132 | bcm_phy_write_misc(phydev, AFE_VDAC_OTHERS_0, 0xa020); |
a490631f FF |
133 | |
134 | /* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal | |
135 | * offset for HT=0 code | |
136 | */ | |
a1cba561 | 137 | bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3); |
a490631f FF |
138 | |
139 | /* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */ | |
9200c27a | 140 | phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x0010); |
a490631f FF |
141 | |
142 | /* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */ | |
a1cba561 | 143 | bcm_phy_write_misc(phydev, DSP_TAP10, 0x011b); |
a490631f FF |
144 | |
145 | /* Reset R_CAL/RC_CAL engine */ | |
146 | r_rc_cal_reset(phydev); | |
147 | ||
148 | return 0; | |
149 | } | |
150 | ||
0c2fdc25 FF |
151 | static int bcm7xxx_28nm_e0_plus_afe_config_init(struct phy_device *phydev) |
152 | { | |
153 | /* AFE_RXCONFIG_1, provide more margin for INL/DNL measurement */ | |
a1cba561 | 154 | bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f); |
0c2fdc25 | 155 | |
6da8253b | 156 | /* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */ |
a1cba561 | 157 | bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x431); |
6da8253b | 158 | |
0c2fdc25 | 159 | /* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */ |
a1cba561 | 160 | bcm_phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da); |
0c2fdc25 FF |
161 | |
162 | /* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal | |
163 | * offset for HT=0 code | |
164 | */ | |
a1cba561 | 165 | bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3); |
0c2fdc25 FF |
166 | |
167 | /* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */ | |
9200c27a | 168 | phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x0010); |
0c2fdc25 FF |
169 | |
170 | /* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */ | |
a1cba561 | 171 | bcm_phy_write_misc(phydev, DSP_TAP10, 0x011b); |
0c2fdc25 FF |
172 | |
173 | /* Reset R_CAL/RC_CAL engine */ | |
174 | r_rc_cal_reset(phydev); | |
175 | ||
176 | return 0; | |
177 | } | |
178 | ||
039a7b85 FF |
179 | static int bcm7xxx_28nm_a0_patch_afe_config_init(struct phy_device *phydev) |
180 | { | |
181 | /* +1 RC_CAL codes for RL centering for both LT and HT conditions */ | |
182 | bcm_phy_write_misc(phydev, AFE_RXCONFIG_2, 0xd003); | |
183 | ||
184 | /* Cut master bias current by 2% to compensate for RC_CAL offset */ | |
185 | bcm_phy_write_misc(phydev, DSP_TAP10, 0x791b); | |
186 | ||
187 | /* Improve hybrid leakage */ | |
188 | bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x10e3); | |
189 | ||
190 | /* Change rx_on_tune 8 to 0xf */ | |
191 | bcm_phy_write_misc(phydev, 0x21, 0x2, 0x87f6); | |
192 | ||
193 | /* Change 100Tx EEE bandwidth */ | |
194 | bcm_phy_write_misc(phydev, 0x22, 0x2, 0x017d); | |
195 | ||
196 | /* Enable ffe zero detection for Vitesse interoperability */ | |
197 | bcm_phy_write_misc(phydev, 0x26, 0x2, 0x0015); | |
198 | ||
199 | r_rc_cal_reset(phydev); | |
200 | ||
201 | return 0; | |
202 | } | |
203 | ||
b560a58c FF |
204 | static int bcm7xxx_28nm_config_init(struct phy_device *phydev) |
205 | { | |
d8ebfed3 FF |
206 | u8 rev = PHY_BRCM_7XXX_REV(phydev->dev_flags); |
207 | u8 patch = PHY_BRCM_7XXX_PATCH(phydev->dev_flags); | |
db88816b | 208 | u8 count; |
d8ebfed3 FF |
209 | int ret = 0; |
210 | ||
039a7b85 FF |
211 | /* Newer devices have moved the revision information back into a |
212 | * standard location in MII_PHYS_ID[23] | |
213 | */ | |
214 | if (rev == 0) | |
215 | rev = phydev->phy_id & ~phydev->drv->phy_id_mask; | |
216 | ||
6ec259c1 | 217 | pr_info_once("%s: %s PHY revision: 0x%02x, patch: %d\n", |
84eff6d1 | 218 | phydev_name(phydev), phydev->drv->name, rev, patch); |
d8ebfed3 | 219 | |
8e346e15 FF |
220 | /* Dummy read to a register to workaround an issue upon reset where the |
221 | * internal inverter may not allow the first MDIO transaction to pass | |
222 | * the MDIO management controller and make us return 0xffff for such | |
223 | * reads. | |
224 | */ | |
225 | phy_read(phydev, MII_BMSR); | |
226 | ||
d8ebfed3 | 227 | switch (rev) { |
6fdecfe3 | 228 | case 0xa0: |
d8ebfed3 | 229 | case 0xb0: |
2a9df742 | 230 | ret = bcm7xxx_28nm_b0_afe_config_init(phydev); |
d8ebfed3 | 231 | break; |
a490631f FF |
232 | case 0xd0: |
233 | ret = bcm7xxx_28nm_d0_afe_config_init(phydev); | |
234 | break; | |
0c2fdc25 FF |
235 | case 0xe0: |
236 | case 0xf0: | |
60efff0c FF |
237 | /* Rev G0 introduces a roll over */ |
238 | case 0x10: | |
0c2fdc25 FF |
239 | ret = bcm7xxx_28nm_e0_plus_afe_config_init(phydev); |
240 | break; | |
039a7b85 FF |
241 | case 0x01: |
242 | ret = bcm7xxx_28nm_a0_patch_afe_config_init(phydev); | |
243 | break; | |
d8ebfed3 | 244 | default: |
d8ebfed3 FF |
245 | break; |
246 | } | |
b560a58c | 247 | |
9df54dda FF |
248 | if (ret) |
249 | return ret; | |
250 | ||
db88816b FF |
251 | ret = bcm_phy_downshift_get(phydev, &count); |
252 | if (ret) | |
253 | return ret; | |
254 | ||
255 | /* Only enable EEE if Wirespeed/downshift is disabled */ | |
256 | ret = bcm_phy_set_eee(phydev, count == DOWNSHIFT_DEV_DISABLE); | |
b8f9a029 FF |
257 | if (ret) |
258 | return ret; | |
259 | ||
a1cba561 | 260 | return bcm_phy_enable_apd(phydev, true); |
b560a58c FF |
261 | } |
262 | ||
4fd14e0b FF |
263 | static int bcm7xxx_28nm_resume(struct phy_device *phydev) |
264 | { | |
265 | int ret; | |
266 | ||
267 | /* Re-apply workarounds coming out suspend/resume */ | |
268 | ret = bcm7xxx_28nm_config_init(phydev); | |
269 | if (ret) | |
270 | return ret; | |
271 | ||
272 | /* 28nm Gigabit PHYs come out of reset without any half-duplex | |
273 | * or "hub" compliant advertised mode, fix that. This does not | |
274 | * cause any problems with the PHY library since genphy_config_aneg() | |
275 | * gracefully handles auto-negotiated and forced modes. | |
276 | */ | |
277 | return genphy_config_aneg(phydev); | |
278 | } | |
279 | ||
b560a58c FF |
280 | static int phy_set_clr_bits(struct phy_device *dev, int location, |
281 | int set_mask, int clr_mask) | |
282 | { | |
283 | int v, ret; | |
284 | ||
285 | v = phy_read(dev, location); | |
286 | if (v < 0) | |
287 | return v; | |
288 | ||
289 | v &= ~clr_mask; | |
290 | v |= set_mask; | |
291 | ||
292 | ret = phy_write(dev, location, v); | |
293 | if (ret < 0) | |
294 | return ret; | |
295 | ||
296 | return v; | |
297 | } | |
298 | ||
83ee102a DB |
299 | static int bcm7xxx_28nm_ephy_01_afe_config_init(struct phy_device *phydev) |
300 | { | |
301 | int ret; | |
302 | ||
303 | /* set shadow mode 2 */ | |
304 | ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, | |
305 | MII_BCM7XXX_SHD_MODE_2, 0); | |
306 | if (ret < 0) | |
307 | return ret; | |
308 | ||
309 | /* Set current trim values INT_trim = -1, Ext_trim =0 */ | |
310 | ret = phy_write(phydev, MII_BCM7XXX_SHD_2_BIAS_TRIM, 0x3BE0); | |
311 | if (ret < 0) | |
312 | goto reset_shadow_mode; | |
313 | ||
314 | /* Cal reset */ | |
315 | ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL, | |
316 | MII_BCM7XXX_SHD_3_TL4); | |
317 | if (ret < 0) | |
318 | goto reset_shadow_mode; | |
319 | ret = phy_set_clr_bits(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT, | |
320 | MII_BCM7XXX_TL4_RST_MSK, 0); | |
321 | if (ret < 0) | |
322 | goto reset_shadow_mode; | |
323 | ||
324 | /* Cal reset disable */ | |
325 | ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL, | |
326 | MII_BCM7XXX_SHD_3_TL4); | |
327 | if (ret < 0) | |
328 | goto reset_shadow_mode; | |
329 | ret = phy_set_clr_bits(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT, | |
330 | 0, MII_BCM7XXX_TL4_RST_MSK); | |
331 | if (ret < 0) | |
332 | goto reset_shadow_mode; | |
333 | ||
334 | reset_shadow_mode: | |
335 | /* reset shadow mode 2 */ | |
336 | ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, 0, | |
337 | MII_BCM7XXX_SHD_MODE_2); | |
338 | if (ret < 0) | |
339 | return ret; | |
340 | ||
341 | return 0; | |
342 | } | |
343 | ||
344 | /* The 28nm EPHY does not support Clause 45 (MMD) used by bcm-phy-lib */ | |
345 | static int bcm7xxx_28nm_ephy_apd_enable(struct phy_device *phydev) | |
346 | { | |
347 | int ret; | |
348 | ||
349 | /* set shadow mode 1 */ | |
350 | ret = phy_set_clr_bits(phydev, MII_BRCM_FET_BRCMTEST, | |
351 | MII_BRCM_FET_BT_SRE, 0); | |
352 | if (ret < 0) | |
353 | return ret; | |
354 | ||
355 | /* Enable auto-power down */ | |
356 | ret = phy_set_clr_bits(phydev, MII_BRCM_FET_SHDW_AUXSTAT2, | |
357 | MII_BRCM_FET_SHDW_AS2_APDE, 0); | |
358 | if (ret < 0) | |
359 | return ret; | |
360 | ||
361 | /* reset shadow mode 1 */ | |
362 | ret = phy_set_clr_bits(phydev, MII_BRCM_FET_BRCMTEST, 0, | |
363 | MII_BRCM_FET_BT_SRE); | |
364 | if (ret < 0) | |
365 | return ret; | |
366 | ||
367 | return 0; | |
368 | } | |
369 | ||
370 | static int bcm7xxx_28nm_ephy_eee_enable(struct phy_device *phydev) | |
371 | { | |
372 | int ret; | |
373 | ||
374 | /* set shadow mode 2 */ | |
375 | ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, | |
376 | MII_BCM7XXX_SHD_MODE_2, 0); | |
377 | if (ret < 0) | |
378 | return ret; | |
379 | ||
380 | /* Advertise supported modes */ | |
381 | ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL, | |
382 | MII_BCM7XXX_SHD_3_AN_EEE_ADV); | |
383 | if (ret < 0) | |
384 | goto reset_shadow_mode; | |
385 | ret = phy_write(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT, | |
386 | MDIO_EEE_100TX); | |
387 | if (ret < 0) | |
388 | goto reset_shadow_mode; | |
389 | ||
390 | /* Restore Defaults */ | |
391 | ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL, | |
392 | MII_BCM7XXX_SHD_3_PCS_CTRL_2); | |
393 | if (ret < 0) | |
394 | goto reset_shadow_mode; | |
395 | ret = phy_write(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT, | |
396 | MII_BCM7XXX_PCS_CTRL_2_DEF); | |
397 | if (ret < 0) | |
398 | goto reset_shadow_mode; | |
399 | ||
400 | ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL, | |
401 | MII_BCM7XXX_SHD_3_EEE_THRESH); | |
402 | if (ret < 0) | |
403 | goto reset_shadow_mode; | |
404 | ret = phy_write(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT, | |
405 | MII_BCM7XXX_EEE_THRESH_DEF); | |
406 | if (ret < 0) | |
407 | goto reset_shadow_mode; | |
408 | ||
409 | /* Enable EEE autonegotiation */ | |
410 | ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL, | |
411 | MII_BCM7XXX_SHD_3_AN_STAT); | |
412 | if (ret < 0) | |
413 | goto reset_shadow_mode; | |
414 | ret = phy_write(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT, | |
415 | (MII_BCM7XXX_AN_NULL_MSG_EN | MII_BCM7XXX_AN_EEE_EN)); | |
416 | if (ret < 0) | |
417 | goto reset_shadow_mode; | |
418 | ||
419 | reset_shadow_mode: | |
420 | /* reset shadow mode 2 */ | |
421 | ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, 0, | |
422 | MII_BCM7XXX_SHD_MODE_2); | |
423 | if (ret < 0) | |
424 | return ret; | |
425 | ||
426 | /* Restart autoneg */ | |
427 | phy_write(phydev, MII_BMCR, | |
428 | (BMCR_SPEED100 | BMCR_ANENABLE | BMCR_ANRESTART)); | |
429 | ||
430 | return 0; | |
431 | } | |
432 | ||
433 | static int bcm7xxx_28nm_ephy_config_init(struct phy_device *phydev) | |
434 | { | |
435 | u8 rev = phydev->phy_id & ~phydev->drv->phy_id_mask; | |
436 | int ret = 0; | |
437 | ||
438 | pr_info_once("%s: %s PHY revision: 0x%02x\n", | |
439 | phydev_name(phydev), phydev->drv->name, rev); | |
440 | ||
441 | /* Dummy read to a register to workaround a possible issue upon reset | |
442 | * where the internal inverter may not allow the first MDIO transaction | |
443 | * to pass the MDIO management controller and make us return 0xffff for | |
444 | * such reads. | |
445 | */ | |
446 | phy_read(phydev, MII_BMSR); | |
447 | ||
448 | /* Apply AFE software work-around if necessary */ | |
449 | if (rev == 0x01) { | |
450 | ret = bcm7xxx_28nm_ephy_01_afe_config_init(phydev); | |
451 | if (ret) | |
452 | return ret; | |
453 | } | |
454 | ||
455 | ret = bcm7xxx_28nm_ephy_eee_enable(phydev); | |
456 | if (ret) | |
457 | return ret; | |
458 | ||
459 | return bcm7xxx_28nm_ephy_apd_enable(phydev); | |
460 | } | |
461 | ||
462 | static int bcm7xxx_28nm_ephy_resume(struct phy_device *phydev) | |
463 | { | |
464 | int ret; | |
465 | ||
466 | /* Re-apply workarounds coming out suspend/resume */ | |
467 | ret = bcm7xxx_28nm_ephy_config_init(phydev); | |
468 | if (ret) | |
469 | return ret; | |
470 | ||
471 | return genphy_config_aneg(phydev); | |
472 | } | |
473 | ||
b560a58c FF |
474 | static int bcm7xxx_config_init(struct phy_device *phydev) |
475 | { | |
476 | int ret; | |
477 | ||
478 | /* Enable 64 clock MDIO */ | |
3ccc3055 | 479 | phy_write(phydev, MII_BCM7XXX_AUX_MODE, MII_BCM7XXX_64CLK_MDIO); |
b560a58c FF |
480 | phy_read(phydev, MII_BCM7XXX_AUX_MODE); |
481 | ||
b560a58c FF |
482 | /* set shadow mode 2 */ |
483 | ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, | |
484 | MII_BCM7XXX_SHD_MODE_2, MII_BCM7XXX_SHD_MODE_2); | |
485 | if (ret < 0) | |
486 | return ret; | |
487 | ||
488 | /* set iddq_clkbias */ | |
489 | phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0F00); | |
490 | udelay(10); | |
491 | ||
492 | /* reset iddq_clkbias */ | |
493 | phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0C00); | |
494 | ||
495 | phy_write(phydev, MII_BCM7XXX_100TX_FALSE_CAR, 0x7555); | |
496 | ||
497 | /* reset shadow mode 2 */ | |
50d89980 | 498 | ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, 0, MII_BCM7XXX_SHD_MODE_2); |
b560a58c FF |
499 | if (ret < 0) |
500 | return ret; | |
501 | ||
502 | return 0; | |
503 | } | |
504 | ||
505 | /* Workaround for putting the PHY in IDDQ mode, required | |
82c084f5 | 506 | * for all BCM7XXX 40nm and 65nm PHYs |
b560a58c FF |
507 | */ |
508 | static int bcm7xxx_suspend(struct phy_device *phydev) | |
509 | { | |
510 | int ret; | |
33c81821 | 511 | static const struct bcm7xxx_regs { |
b560a58c FF |
512 | int reg; |
513 | u16 value; | |
514 | } bcm7xxx_suspend_cfg[] = { | |
515 | { MII_BCM7XXX_TEST, 0x008b }, | |
516 | { MII_BCM7XXX_100TX_AUX_CTL, 0x01c0 }, | |
517 | { MII_BCM7XXX_100TX_DISC, 0x7000 }, | |
518 | { MII_BCM7XXX_TEST, 0x000f }, | |
519 | { MII_BCM7XXX_100TX_AUX_CTL, 0x20d0 }, | |
520 | { MII_BCM7XXX_TEST, 0x000b }, | |
521 | }; | |
522 | unsigned int i; | |
523 | ||
524 | for (i = 0; i < ARRAY_SIZE(bcm7xxx_suspend_cfg); i++) { | |
525 | ret = phy_write(phydev, | |
526 | bcm7xxx_suspend_cfg[i].reg, | |
527 | bcm7xxx_suspend_cfg[i].value); | |
528 | if (ret) | |
529 | return ret; | |
530 | } | |
531 | ||
532 | return 0; | |
533 | } | |
534 | ||
db88816b FF |
535 | static int bcm7xxx_28nm_get_tunable(struct phy_device *phydev, |
536 | struct ethtool_tunable *tuna, | |
537 | void *data) | |
538 | { | |
539 | switch (tuna->id) { | |
540 | case ETHTOOL_PHY_DOWNSHIFT: | |
541 | return bcm_phy_downshift_get(phydev, (u8 *)data); | |
542 | default: | |
543 | return -EOPNOTSUPP; | |
544 | } | |
545 | } | |
546 | ||
547 | static int bcm7xxx_28nm_set_tunable(struct phy_device *phydev, | |
548 | struct ethtool_tunable *tuna, | |
549 | const void *data) | |
550 | { | |
551 | u8 count = *(u8 *)data; | |
552 | int ret; | |
553 | ||
554 | switch (tuna->id) { | |
555 | case ETHTOOL_PHY_DOWNSHIFT: | |
556 | ret = bcm_phy_downshift_set(phydev, count); | |
557 | break; | |
558 | default: | |
559 | return -EOPNOTSUPP; | |
560 | } | |
561 | ||
562 | if (ret) | |
563 | return ret; | |
564 | ||
cc1122b0 | 565 | /* Disable EEE advertisement since this prevents the PHY |
db88816b FF |
566 | * from successfully linking up, trigger auto-negotiation restart |
567 | * to let the MAC decide what to do. | |
568 | */ | |
569 | ret = bcm_phy_set_eee(phydev, count == DOWNSHIFT_DEV_DISABLE); | |
570 | if (ret) | |
571 | return ret; | |
572 | ||
573 | return genphy_restart_aneg(phydev); | |
574 | } | |
575 | ||
b23ce9e8 FF |
576 | static void bcm7xxx_28nm_get_phy_stats(struct phy_device *phydev, |
577 | struct ethtool_stats *stats, u64 *data) | |
578 | { | |
579 | struct bcm7xxx_phy_priv *priv = phydev->priv; | |
580 | ||
581 | bcm_phy_get_stats(phydev, priv->stats, stats, data); | |
582 | } | |
583 | ||
584 | static int bcm7xxx_28nm_probe(struct phy_device *phydev) | |
585 | { | |
586 | struct bcm7xxx_phy_priv *priv; | |
587 | ||
588 | priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); | |
589 | if (!priv) | |
590 | return -ENOMEM; | |
591 | ||
592 | phydev->priv = priv; | |
593 | ||
594 | priv->stats = devm_kcalloc(&phydev->mdio.dev, | |
595 | bcm_phy_get_sset_count(phydev), sizeof(u64), | |
596 | GFP_KERNEL); | |
597 | if (!priv->stats) | |
598 | return -ENOMEM; | |
599 | ||
600 | return 0; | |
601 | } | |
602 | ||
153df3c7 FF |
603 | #define BCM7XXX_28NM_GPHY(_oui, _name) \ |
604 | { \ | |
605 | .phy_id = (_oui), \ | |
606 | .phy_id_mask = 0xfffffff0, \ | |
607 | .name = _name, \ | |
529ed127 | 608 | .features = PHY_GBIT_FEATURES, \ |
153df3c7 | 609 | .flags = PHY_IS_INTERNAL, \ |
2a9df742 | 610 | .config_init = bcm7xxx_28nm_config_init, \ |
153df3c7 | 611 | .resume = bcm7xxx_28nm_resume, \ |
db88816b FF |
612 | .get_tunable = bcm7xxx_28nm_get_tunable, \ |
613 | .set_tunable = bcm7xxx_28nm_set_tunable, \ | |
b23ce9e8 FF |
614 | .get_sset_count = bcm_phy_get_sset_count, \ |
615 | .get_strings = bcm_phy_get_strings, \ | |
616 | .get_stats = bcm7xxx_28nm_get_phy_stats, \ | |
617 | .probe = bcm7xxx_28nm_probe, \ | |
153df3c7 FF |
618 | } |
619 | ||
83ee102a DB |
620 | #define BCM7XXX_28NM_EPHY(_oui, _name) \ |
621 | { \ | |
622 | .phy_id = (_oui), \ | |
623 | .phy_id_mask = 0xfffffff0, \ | |
624 | .name = _name, \ | |
625 | .features = PHY_BASIC_FEATURES, \ | |
626 | .flags = PHY_IS_INTERNAL, \ | |
627 | .config_init = bcm7xxx_28nm_ephy_config_init, \ | |
83ee102a DB |
628 | .resume = bcm7xxx_28nm_ephy_resume, \ |
629 | .get_sset_count = bcm_phy_get_sset_count, \ | |
630 | .get_strings = bcm_phy_get_strings, \ | |
631 | .get_stats = bcm7xxx_28nm_get_phy_stats, \ | |
632 | .probe = bcm7xxx_28nm_probe, \ | |
633 | } | |
634 | ||
3125c081 FF |
635 | #define BCM7XXX_40NM_EPHY(_oui, _name) \ |
636 | { \ | |
637 | .phy_id = (_oui), \ | |
638 | .phy_id_mask = 0xfffffff0, \ | |
639 | .name = _name, \ | |
529ed127 | 640 | .features = PHY_BASIC_FEATURES, \ |
3125c081 FF |
641 | .flags = PHY_IS_INTERNAL, \ |
642 | .config_init = bcm7xxx_config_init, \ | |
3125c081 FF |
643 | .suspend = bcm7xxx_suspend, \ |
644 | .resume = bcm7xxx_config_init, \ | |
645 | } | |
646 | ||
b560a58c | 647 | static struct phy_driver bcm7xxx_driver[] = { |
430ad68f | 648 | BCM7XXX_28NM_GPHY(PHY_ID_BCM7250, "Broadcom BCM7250"), |
8572a1b4 | 649 | BCM7XXX_28NM_EPHY(PHY_ID_BCM7255, "Broadcom BCM7255"), |
83ee102a DB |
650 | BCM7XXX_28NM_EPHY(PHY_ID_BCM7260, "Broadcom BCM7260"), |
651 | BCM7XXX_28NM_EPHY(PHY_ID_BCM7268, "Broadcom BCM7268"), | |
652 | BCM7XXX_28NM_EPHY(PHY_ID_BCM7271, "Broadcom BCM7271"), | |
582d0ac3 | 653 | BCM7XXX_28NM_GPHY(PHY_ID_BCM7278, "Broadcom BCM7278"), |
430ad68f | 654 | BCM7XXX_28NM_GPHY(PHY_ID_BCM7364, "Broadcom BCM7364"), |
153df3c7 | 655 | BCM7XXX_28NM_GPHY(PHY_ID_BCM7366, "Broadcom BCM7366"), |
b08d46b0 | 656 | BCM7XXX_28NM_GPHY(PHY_ID_BCM74371, "Broadcom BCM74371"), |
153df3c7 | 657 | BCM7XXX_28NM_GPHY(PHY_ID_BCM7439, "Broadcom BCM7439"), |
59e33c2b | 658 | BCM7XXX_28NM_GPHY(PHY_ID_BCM7439_2, "Broadcom BCM7439 (2)"), |
153df3c7 | 659 | BCM7XXX_28NM_GPHY(PHY_ID_BCM7445, "Broadcom BCM7445"), |
6fdecfe3 | 660 | BCM7XXX_28NM_GPHY(PHY_ID_BCM_OMEGA, "Broadcom Omega Combo GPHY"), |
4cef191d JS |
661 | BCM7XXX_40NM_EPHY(PHY_ID_BCM7346, "Broadcom BCM7346"), |
662 | BCM7XXX_40NM_EPHY(PHY_ID_BCM7362, "Broadcom BCM7362"), | |
3125c081 FF |
663 | BCM7XXX_40NM_EPHY(PHY_ID_BCM7425, "Broadcom BCM7425"), |
664 | BCM7XXX_40NM_EPHY(PHY_ID_BCM7429, "Broadcom BCM7429"), | |
665 | BCM7XXX_40NM_EPHY(PHY_ID_BCM7435, "Broadcom BCM7435"), | |
b6333531 | 666 | }; |
b560a58c FF |
667 | |
668 | static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = { | |
430ad68f | 669 | { PHY_ID_BCM7250, 0xfffffff0, }, |
8572a1b4 | 670 | { PHY_ID_BCM7255, 0xfffffff0, }, |
83ee102a DB |
671 | { PHY_ID_BCM7260, 0xfffffff0, }, |
672 | { PHY_ID_BCM7268, 0xfffffff0, }, | |
673 | { PHY_ID_BCM7271, 0xfffffff0, }, | |
582d0ac3 | 674 | { PHY_ID_BCM7278, 0xfffffff0, }, |
430ad68f | 675 | { PHY_ID_BCM7364, 0xfffffff0, }, |
b560a58c | 676 | { PHY_ID_BCM7366, 0xfffffff0, }, |
4cef191d JS |
677 | { PHY_ID_BCM7346, 0xfffffff0, }, |
678 | { PHY_ID_BCM7362, 0xfffffff0, }, | |
d068b02c PG |
679 | { PHY_ID_BCM7425, 0xfffffff0, }, |
680 | { PHY_ID_BCM7429, 0xfffffff0, }, | |
b08d46b0 | 681 | { PHY_ID_BCM74371, 0xfffffff0, }, |
b560a58c | 682 | { PHY_ID_BCM7439, 0xfffffff0, }, |
9458ceab | 683 | { PHY_ID_BCM7435, 0xfffffff0, }, |
b560a58c | 684 | { PHY_ID_BCM7445, 0xfffffff0, }, |
b560a58c FF |
685 | { } |
686 | }; | |
687 | ||
50fd7150 | 688 | module_phy_driver(bcm7xxx_driver); |
b560a58c FF |
689 | |
690 | MODULE_DEVICE_TABLE(mdio, bcm7xxx_tbl); | |
691 | ||
692 | MODULE_DESCRIPTION("Broadcom BCM7xxx internal PHY driver"); | |
693 | MODULE_LICENSE("GPL"); | |
694 | MODULE_AUTHOR("Broadcom Corporation"); |