1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Broadcom BCM7xxx internal transceivers support. |
4 | * |
5 | * Copyright (C) 2014-2017 Broadcom |
6 | */ |
7 | |
8 | #include <linux/module.h> |
9 | #include <linux/phy.h> |
10 | #include <linux/delay.h> |
11 | #include "bcm-phy-lib.h" |
12 | #include <linux/bitops.h> |
13 | #include <linux/brcmphy.h> |
14 | #include <linux/clk.h> |
15 | #include <linux/mdio.h> |
16 | |
17 | /* Broadcom BCM7xxx internal PHY registers */ |
18 | |
19 | /* EPHY only register definitions */ |
20 | #define MII_BCM7XXX_100TX_AUX_CTL 0x10 |
21 | #define MII_BCM7XXX_100TX_FALSE_CAR 0x13 |
22 | #define MII_BCM7XXX_100TX_DISC 0x14 |
23 | #define MII_BCM7XXX_AUX_MODE 0x1d |
24 | #define MII_BCM7XXX_64CLK_MDIO BIT(12) |
25 | #define MII_BCM7XXX_TEST 0x1f |
26 | #define MII_BCM7XXX_SHD_MODE_2 BIT(2) |
27 | #define MII_BCM7XXX_SHD_2_ADDR_CTRL 0xe |
28 | #define MII_BCM7XXX_SHD_2_CTRL_STAT 0xf |
29 | #define MII_BCM7XXX_SHD_2_BIAS_TRIM 0x1a |
30 | #define MII_BCM7XXX_SHD_3_PCS_CTRL 0x0 |
31 | #define MII_BCM7XXX_SHD_3_PCS_STATUS 0x1 |
32 | #define MII_BCM7XXX_SHD_3_EEE_CAP 0x2 |
33 | #define MII_BCM7XXX_SHD_3_AN_EEE_ADV 0x3 |
34 | #define MII_BCM7XXX_SHD_3_EEE_LP 0x4 |
35 | #define MII_BCM7XXX_SHD_3_EEE_WK_ERR 0x5 |
36 | #define MII_BCM7XXX_SHD_3_PCS_CTRL_2 0x6 |
37 | #define MII_BCM7XXX_PCS_CTRL_2_DEF 0x4400 |
38 | #define MII_BCM7XXX_SHD_3_AN_STAT 0xb |
39 | #define MII_BCM7XXX_AN_NULL_MSG_EN BIT(0) |
40 | #define MII_BCM7XXX_AN_EEE_EN BIT(1) |
41 | #define MII_BCM7XXX_SHD_3_EEE_THRESH 0xe |
42 | #define MII_BCM7XXX_EEE_THRESH_DEF 0x50 |
43 | #define MII_BCM7XXX_SHD_3_TL4 0x23 |
44 | #define MII_BCM7XXX_TL4_RST_MSK (BIT(2) | BIT(1)) |
45 | |
46 | struct bcm7xxx_phy_priv { |
47 | u64 *stats; |
48 | }; |
49 | |
50 | static int bcm7xxx_28nm_d0_afe_config_init(struct phy_device *phydev) |
51 | { |
52 | /* AFE_RXCONFIG_0 */ |
53 | bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, value: 0xeb15); |
54 | |
55 | /* AFE_RXCONFIG_1 */ |
56 | bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, value: 0x9b2f); |
57 | |
58 | /* AFE_RXCONFIG_2, set rCal offset for HT=0 code and LT=-2 code */ |
59 | bcm_phy_write_misc(phydev, AFE_RXCONFIG_2, value: 0x2003); |
60 | |
61 | /* AFE_RX_LP_COUNTER, set RX bandwidth to maximum */ |
62 | bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, value: 0x7fc0); |
63 | |
64 | /* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */ |
65 | bcm_phy_write_misc(phydev, AFE_TX_CONFIG, value: 0x431); |
66 | |
67 | /* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */ |
68 | bcm_phy_write_misc(phydev, AFE_VDCA_ICTRL_0, value: 0xa7da); |
69 | |
70 | /* AFE_VDAC_OTHERS_0, set 1000BT Cidac=010 for all ports */ |
71 | bcm_phy_write_misc(phydev, AFE_VDAC_OTHERS_0, value: 0xa020); |
72 | |
73 | /* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal |
74 | * offset for HT=0 code |
75 | */ |
76 | bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, value: 0x00e3); |
77 | |
78 | /* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */ |
79 | phy_write(phydev, MII_BRCM_CORE_BASE1E, val: 0x0010); |
80 | |
81 | /* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */ |
82 | bcm_phy_write_misc(phydev, DSP_TAP10, value: 0x011b); |
83 | |
84 | /* Reset R_CAL/RC_CAL engine */ |
85 | bcm_phy_r_rc_cal_reset(phydev); |
86 | |
87 | return 0; |
88 | } |
89 | |
90 | static int bcm7xxx_28nm_e0_plus_afe_config_init(struct phy_device *phydev) |
91 | { |
92 | /* AFE_RXCONFIG_1, provide more margin for INL/DNL measurement */ |
93 | bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, value: 0x9b2f); |
94 | |
95 | /* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */ |
96 | bcm_phy_write_misc(phydev, AFE_TX_CONFIG, value: 0x431); |
97 | |
98 | /* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */ |
99 | bcm_phy_write_misc(phydev, AFE_VDCA_ICTRL_0, value: 0xa7da); |
100 | |
101 | /* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal |
102 | * offset for HT=0 code |
103 | */ |
104 | bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, value: 0x00e3); |
105 | |
106 | /* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */ |
107 | phy_write(phydev, MII_BRCM_CORE_BASE1E, val: 0x0010); |
108 | |
109 | /* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */ |
110 | bcm_phy_write_misc(phydev, DSP_TAP10, value: 0x011b); |
111 | |
112 | /* Reset R_CAL/RC_CAL engine */ |
113 | bcm_phy_r_rc_cal_reset(phydev); |
114 | |
115 | return 0; |
116 | } |
117 | |
118 | static int bcm7xxx_28nm_a0_patch_afe_config_init(struct phy_device *phydev) |
119 | { |
120 | /* +1 RC_CAL codes for RL centering for both LT and HT conditions */ |
121 | bcm_phy_write_misc(phydev, AFE_RXCONFIG_2, value: 0xd003); |
122 | |
123 | /* Cut master bias current by 2% to compensate for RC_CAL offset */ |
124 | bcm_phy_write_misc(phydev, DSP_TAP10, value: 0x791b); |
125 | |
126 | /* Improve hybrid leakage */ |
127 | bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, value: 0x10e3); |
128 | |
129 | /* Change rx_on_tune 8 to 0xf */ |
130 | bcm_phy_write_misc(phydev, reg: 0x21, chl: 0x2, value: 0x87f6); |
131 | |
132 | /* Change 100Tx EEE bandwidth */ |
133 | bcm_phy_write_misc(phydev, reg: 0x22, chl: 0x2, value: 0x017d); |
134 | |
135 | /* Enable ffe zero detection for Vitesse interoperability */ |
136 | bcm_phy_write_misc(phydev, reg: 0x26, chl: 0x2, value: 0x0015); |
137 | |
138 | bcm_phy_r_rc_cal_reset(phydev); |
139 | |
140 | return 0; |
141 | } |
142 | |
143 | static int bcm7xxx_28nm_config_init(struct phy_device *phydev) |
144 | { |
145 | u8 rev = PHY_BRCM_7XXX_REV(phydev->dev_flags); |
146 | u8 patch = PHY_BRCM_7XXX_PATCH(phydev->dev_flags); |
147 | u8 count; |
148 | int ret = 0; |
149 | |
150 | /* Newer devices have moved the revision information back into a |
151 | * standard location in MII_PHYS_ID[23] |
152 | */ |
153 | if (rev == 0) |
154 | rev = phydev->phy_id & ~phydev->drv->phy_id_mask; |
155 | |
156 | pr_info_once("%s: %s PHY revision: 0x%02x, patch: %d\n" , |
157 | phydev_name(phydev), phydev->drv->name, rev, patch); |
158 | |
159 | /* Dummy read to a register to workaround an issue upon reset where the |
160 | * internal inverter may not allow the first MDIO transaction to pass |
161 | * the MDIO management controller and make us return 0xffff for such |
162 | * reads. |
163 | */ |
164 | phy_read(phydev, MII_BMSR); |
165 | |
166 | switch (rev) { |
167 | case 0xa0: |
168 | case 0xb0: |
169 | ret = bcm_phy_28nm_a0b0_afe_config_init(phydev); |
170 | break; |
171 | case 0xd0: |
172 | ret = bcm7xxx_28nm_d0_afe_config_init(phydev); |
173 | break; |
174 | case 0xe0: |
175 | case 0xf0: |
176 | /* Rev G0 introduces a roll over */ |
177 | case 0x10: |
178 | ret = bcm7xxx_28nm_e0_plus_afe_config_init(phydev); |
179 | break; |
180 | case 0x01: |
181 | ret = bcm7xxx_28nm_a0_patch_afe_config_init(phydev); |
182 | break; |
183 | default: |
184 | break; |
185 | } |
186 | |
187 | if (ret) |
188 | return ret; |
189 | |
190 | ret = bcm_phy_enable_jumbo(phydev); |
191 | if (ret) |
192 | return ret; |
193 | |
194 | ret = bcm_phy_downshift_get(phydev, count: &count); |
195 | if (ret) |
196 | return ret; |
197 | |
198 | /* Only enable EEE if Wirespeed/downshift is disabled */ |
199 | ret = bcm_phy_set_eee(phydev, enable: count == DOWNSHIFT_DEV_DISABLE); |
200 | if (ret) |
201 | return ret; |
202 | |
203 | return bcm_phy_enable_apd(phydev, dll_pwr_down: true); |
204 | } |
205 | |
206 | static int bcm7xxx_28nm_resume(struct phy_device *phydev) |
207 | { |
208 | int ret; |
209 | |
210 | /* Re-apply workarounds coming out suspend/resume */ |
211 | ret = bcm7xxx_28nm_config_init(phydev); |
212 | if (ret) |
213 | return ret; |
214 | |
215 | /* 28nm Gigabit PHYs come out of reset without any half-duplex |
216 | * or "hub" compliant advertised mode, fix that. This does not |
217 | * cause any problems with the PHY library since genphy_config_aneg() |
218 | * gracefully handles auto-negotiated and forced modes. |
219 | */ |
220 | return genphy_config_aneg(phydev); |
221 | } |
222 | |
223 | static int __phy_set_clr_bits(struct phy_device *dev, int location, |
224 | int set_mask, int clr_mask) |
225 | { |
226 | int v, ret; |
227 | |
228 | v = __phy_read(phydev: dev, regnum: location); |
229 | if (v < 0) |
230 | return v; |
231 | |
232 | v &= ~clr_mask; |
233 | v |= set_mask; |
234 | |
235 | ret = __phy_write(phydev: dev, regnum: location, val: v); |
236 | if (ret < 0) |
237 | return ret; |
238 | |
239 | return v; |
240 | } |
241 | |
242 | static int phy_set_clr_bits(struct phy_device *dev, int location, |
243 | int set_mask, int clr_mask) |
244 | { |
245 | int ret; |
246 | |
247 | mutex_lock(&dev->mdio.bus->mdio_lock); |
248 | ret = __phy_set_clr_bits(dev, location, set_mask, clr_mask); |
249 | mutex_unlock(lock: &dev->mdio.bus->mdio_lock); |
250 | |
251 | return ret; |
252 | } |
253 | |
254 | static int bcm7xxx_28nm_ephy_01_afe_config_init(struct phy_device *phydev) |
255 | { |
256 | int ret; |
257 | |
258 | /* set shadow mode 2 */ |
259 | ret = phy_set_clr_bits(dev: phydev, MII_BCM7XXX_TEST, |
260 | MII_BCM7XXX_SHD_MODE_2, clr_mask: 0); |
261 | if (ret < 0) |
262 | return ret; |
263 | |
264 | /* Set current trim values INT_trim = -1, Ext_trim =0 */ |
265 | ret = phy_write(phydev, MII_BCM7XXX_SHD_2_BIAS_TRIM, val: 0x3BE0); |
266 | if (ret < 0) |
267 | goto reset_shadow_mode; |
268 | |
269 | /* Cal reset */ |
270 | ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL, |
271 | MII_BCM7XXX_SHD_3_TL4); |
272 | if (ret < 0) |
273 | goto reset_shadow_mode; |
274 | ret = phy_set_clr_bits(dev: phydev, MII_BCM7XXX_SHD_2_CTRL_STAT, |
275 | MII_BCM7XXX_TL4_RST_MSK, clr_mask: 0); |
276 | if (ret < 0) |
277 | goto reset_shadow_mode; |
278 | |
279 | /* Cal reset disable */ |
280 | ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL, |
281 | MII_BCM7XXX_SHD_3_TL4); |
282 | if (ret < 0) |
283 | goto reset_shadow_mode; |
284 | ret = phy_set_clr_bits(dev: phydev, MII_BCM7XXX_SHD_2_CTRL_STAT, |
285 | set_mask: 0, MII_BCM7XXX_TL4_RST_MSK); |
286 | if (ret < 0) |
287 | goto reset_shadow_mode; |
288 | |
289 | reset_shadow_mode: |
290 | /* reset shadow mode 2 */ |
291 | ret = phy_set_clr_bits(dev: phydev, MII_BCM7XXX_TEST, set_mask: 0, |
292 | MII_BCM7XXX_SHD_MODE_2); |
293 | if (ret < 0) |
294 | return ret; |
295 | |
296 | return 0; |
297 | } |
298 | |
299 | /* The 28nm EPHY does not support Clause 45 (MMD) used by bcm-phy-lib */ |
300 | static int bcm7xxx_28nm_ephy_apd_enable(struct phy_device *phydev) |
301 | { |
302 | int ret; |
303 | |
304 | /* set shadow mode 1 */ |
305 | ret = phy_set_clr_bits(dev: phydev, MII_BRCM_FET_BRCMTEST, |
306 | MII_BRCM_FET_BT_SRE, clr_mask: 0); |
307 | if (ret < 0) |
308 | return ret; |
309 | |
310 | /* Enable auto-power down */ |
311 | ret = phy_set_clr_bits(dev: phydev, MII_BRCM_FET_SHDW_AUXSTAT2, |
312 | MII_BRCM_FET_SHDW_AS2_APDE, clr_mask: 0); |
313 | if (ret < 0) |
314 | return ret; |
315 | |
316 | /* reset shadow mode 1 */ |
317 | ret = phy_set_clr_bits(dev: phydev, MII_BRCM_FET_BRCMTEST, set_mask: 0, |
318 | MII_BRCM_FET_BT_SRE); |
319 | if (ret < 0) |
320 | return ret; |
321 | |
322 | return 0; |
323 | } |
324 | |
325 | static int bcm7xxx_28nm_ephy_eee_enable(struct phy_device *phydev) |
326 | { |
327 | int ret; |
328 | |
329 | /* set shadow mode 2 */ |
330 | ret = phy_set_clr_bits(dev: phydev, MII_BCM7XXX_TEST, |
331 | MII_BCM7XXX_SHD_MODE_2, clr_mask: 0); |
332 | if (ret < 0) |
333 | return ret; |
334 | |
335 | /* Advertise supported modes */ |
336 | ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL, |
337 | MII_BCM7XXX_SHD_3_AN_EEE_ADV); |
338 | if (ret < 0) |
339 | goto reset_shadow_mode; |
340 | ret = phy_write(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT, |
341 | MDIO_EEE_100TX); |
342 | if (ret < 0) |
343 | goto reset_shadow_mode; |
344 | |
345 | /* Restore Defaults */ |
346 | ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL, |
347 | MII_BCM7XXX_SHD_3_PCS_CTRL_2); |
348 | if (ret < 0) |
349 | goto reset_shadow_mode; |
350 | ret = phy_write(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT, |
351 | MII_BCM7XXX_PCS_CTRL_2_DEF); |
352 | if (ret < 0) |
353 | goto reset_shadow_mode; |
354 | |
355 | ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL, |
356 | MII_BCM7XXX_SHD_3_EEE_THRESH); |
357 | if (ret < 0) |
358 | goto reset_shadow_mode; |
359 | ret = phy_write(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT, |
360 | MII_BCM7XXX_EEE_THRESH_DEF); |
361 | if (ret < 0) |
362 | goto reset_shadow_mode; |
363 | |
364 | /* Enable EEE autonegotiation */ |
365 | ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL, |
366 | MII_BCM7XXX_SHD_3_AN_STAT); |
367 | if (ret < 0) |
368 | goto reset_shadow_mode; |
369 | ret = phy_write(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT, |
370 | val: (MII_BCM7XXX_AN_NULL_MSG_EN | MII_BCM7XXX_AN_EEE_EN)); |
371 | if (ret < 0) |
372 | goto reset_shadow_mode; |
373 | |
374 | reset_shadow_mode: |
375 | /* reset shadow mode 2 */ |
376 | ret = phy_set_clr_bits(dev: phydev, MII_BCM7XXX_TEST, set_mask: 0, |
377 | MII_BCM7XXX_SHD_MODE_2); |
378 | if (ret < 0) |
379 | return ret; |
380 | |
381 | /* Restart autoneg */ |
382 | phy_write(phydev, MII_BMCR, |
383 | val: (BMCR_SPEED100 | BMCR_ANENABLE | BMCR_ANRESTART)); |
384 | |
385 | return 0; |
386 | } |
387 | |
388 | static int bcm7xxx_28nm_ephy_config_init(struct phy_device *phydev) |
389 | { |
390 | u8 rev = phydev->phy_id & ~phydev->drv->phy_id_mask; |
391 | int ret = 0; |
392 | |
393 | pr_info_once("%s: %s PHY revision: 0x%02x\n" , |
394 | phydev_name(phydev), phydev->drv->name, rev); |
395 | |
396 | /* Dummy read to a register to workaround a possible issue upon reset |
397 | * where the internal inverter may not allow the first MDIO transaction |
398 | * to pass the MDIO management controller and make us return 0xffff for |
399 | * such reads. |
400 | */ |
401 | phy_read(phydev, MII_BMSR); |
402 | |
403 | /* Apply AFE software work-around if necessary */ |
404 | if (rev == 0x01) { |
405 | ret = bcm7xxx_28nm_ephy_01_afe_config_init(phydev); |
406 | if (ret) |
407 | return ret; |
408 | } |
409 | |
410 | ret = bcm7xxx_28nm_ephy_eee_enable(phydev); |
411 | if (ret) |
412 | return ret; |
413 | |
414 | return bcm7xxx_28nm_ephy_apd_enable(phydev); |
415 | } |
416 | |
417 | static int bcm7xxx_16nm_ephy_afe_config(struct phy_device *phydev) |
418 | { |
419 | int tmp, rcalcode, rcalnewcodelp, rcalnewcode11, rcalnewcode11d2; |
420 | |
421 | /* Reset PHY */ |
422 | tmp = genphy_soft_reset(phydev); |
423 | if (tmp) |
424 | return tmp; |
425 | |
426 | /* Reset AFE and PLL */ |
427 | bcm_phy_write_exp_sel(phydev, reg: 0x0003, val: 0x0006); |
428 | /* Clear reset */ |
429 | bcm_phy_write_exp_sel(phydev, reg: 0x0003, val: 0x0000); |
430 | |
431 | /* Write PLL/AFE control register to select 54MHz crystal */ |
432 | bcm_phy_write_misc(phydev, reg: 0x0030, chl: 0x0001, value: 0x0000); |
433 | bcm_phy_write_misc(phydev, reg: 0x0031, chl: 0x0000, value: 0x044a); |
434 | |
435 | /* Change Ka,Kp,Ki to pdiv=1 */ |
436 | bcm_phy_write_misc(phydev, reg: 0x0033, chl: 0x0002, value: 0x71a1); |
437 | /* Configuration override */ |
438 | bcm_phy_write_misc(phydev, reg: 0x0033, chl: 0x0001, value: 0x8000); |
439 | |
440 | /* Change PLL_NDIV and PLL_NUDGE */ |
441 | bcm_phy_write_misc(phydev, reg: 0x0031, chl: 0x0001, value: 0x2f68); |
442 | bcm_phy_write_misc(phydev, reg: 0x0031, chl: 0x0002, value: 0x0000); |
443 | |
444 | /* Reference frequency is 54Mhz, config_mode[15:14] = 3 (low |
445 | * phase) |
446 | */ |
447 | bcm_phy_write_misc(phydev, reg: 0x0030, chl: 0x0003, value: 0xc036); |
448 | |
449 | /* Initialize bypass mode */ |
450 | bcm_phy_write_misc(phydev, reg: 0x0032, chl: 0x0003, value: 0x0000); |
451 | /* Bypass code, default: VCOCLK enabled */ |
452 | bcm_phy_write_misc(phydev, reg: 0x0033, chl: 0x0000, value: 0x0002); |
453 | /* LDOs at default setting */ |
454 | bcm_phy_write_misc(phydev, reg: 0x0030, chl: 0x0002, value: 0x01c0); |
455 | /* Release PLL reset */ |
456 | bcm_phy_write_misc(phydev, reg: 0x0030, chl: 0x0001, value: 0x0001); |
457 | |
458 | /* Bandgap curvature correction to correct default */ |
459 | bcm_phy_write_misc(phydev, reg: 0x0038, chl: 0x0000, value: 0x0010); |
460 | |
461 | /* Run RCAL */ |
462 | bcm_phy_write_misc(phydev, reg: 0x0039, chl: 0x0003, value: 0x0038); |
463 | bcm_phy_write_misc(phydev, reg: 0x0039, chl: 0x0003, value: 0x003b); |
464 | udelay(2); |
465 | bcm_phy_write_misc(phydev, reg: 0x0039, chl: 0x0003, value: 0x003f); |
466 | mdelay(5); |
467 | |
468 | /* AFE_CAL_CONFIG_0, Vref=1000, Target=10, averaging enabled */ |
469 | bcm_phy_write_misc(phydev, reg: 0x0039, chl: 0x0001, value: 0x1c82); |
470 | /* AFE_CAL_CONFIG_0, no reset and analog powerup */ |
471 | bcm_phy_write_misc(phydev, reg: 0x0039, chl: 0x0001, value: 0x9e82); |
472 | udelay(2); |
473 | /* AFE_CAL_CONFIG_0, start calibration */ |
474 | bcm_phy_write_misc(phydev, reg: 0x0039, chl: 0x0001, value: 0x9f82); |
475 | udelay(100); |
476 | /* AFE_CAL_CONFIG_0, clear start calibration, set HiBW */ |
477 | bcm_phy_write_misc(phydev, reg: 0x0039, chl: 0x0001, value: 0x9e86); |
478 | udelay(2); |
479 | /* AFE_CAL_CONFIG_0, start calibration with hi BW mode set */ |
480 | bcm_phy_write_misc(phydev, reg: 0x0039, chl: 0x0001, value: 0x9f86); |
481 | udelay(100); |
482 | |
483 | /* Adjust 10BT amplitude additional +7% and 100BT +2% */ |
484 | bcm_phy_write_misc(phydev, reg: 0x0038, chl: 0x0001, value: 0xe7ea); |
485 | /* Adjust 1G mode amplitude and 1G testmode1 */ |
486 | bcm_phy_write_misc(phydev, reg: 0x0038, chl: 0x0002, value: 0xede0); |
487 | |
488 | /* Read CORE_EXPA9 */ |
489 | tmp = bcm_phy_read_exp_sel(phydev, reg: 0x00a9); |
490 | /* CORE_EXPA9[6:1] is rcalcode[5:0] */ |
491 | rcalcode = (tmp & 0x7e) / 2; |
492 | /* Correct RCAL code + 1 is -1% rprogr, LP: +16 */ |
493 | rcalnewcodelp = rcalcode + 16; |
494 | /* Correct RCAL code + 1 is -15 rprogr, 11: +10 */ |
495 | rcalnewcode11 = rcalcode + 10; |
496 | /* Saturate if necessary */ |
497 | if (rcalnewcodelp > 0x3f) |
498 | rcalnewcodelp = 0x3f; |
499 | if (rcalnewcode11 > 0x3f) |
500 | rcalnewcode11 = 0x3f; |
501 | /* REXT=1 BYP=1 RCAL_st1<5:0>=new rcal code */ |
502 | tmp = 0x00f8 + rcalnewcodelp * 256; |
503 | /* Program into AFE_CAL_CONFIG_2 */ |
504 | bcm_phy_write_misc(phydev, reg: 0x0039, chl: 0x0003, value: tmp); |
505 | /* AFE_BIAS_CONFIG_0 10BT bias code (Bias: E4) */ |
506 | bcm_phy_write_misc(phydev, reg: 0x0038, chl: 0x0001, value: 0xe7e4); |
507 | /* invert adc clock output and 'adc refp ldo current To correct |
508 | * default |
509 | */ |
510 | bcm_phy_write_misc(phydev, reg: 0x003b, chl: 0x0000, value: 0x8002); |
511 | /* 100BT stair case, high BW, 1G stair case, alternate encode */ |
512 | bcm_phy_write_misc(phydev, reg: 0x003c, chl: 0x0003, value: 0xf882); |
513 | /* 1000BT DAC transition method per Erol, bits[32], DAC Shuffle |
514 | * sequence 1 + 10BT imp adjust bits |
515 | */ |
516 | bcm_phy_write_misc(phydev, reg: 0x003d, chl: 0x0000, value: 0x3201); |
517 | /* Non-overlap fix */ |
518 | bcm_phy_write_misc(phydev, reg: 0x003a, chl: 0x0002, value: 0x0c00); |
519 | |
520 | /* pwdb override (rxconfig<5>) to turn on RX LDO indpendent of |
521 | * pwdb controls from DSP_TAP10 |
522 | */ |
523 | bcm_phy_write_misc(phydev, reg: 0x003a, chl: 0x0001, value: 0x0020); |
524 | |
525 | /* Remove references to channel 2 and 3 */ |
526 | bcm_phy_write_misc(phydev, reg: 0x003b, chl: 0x0002, value: 0x0000); |
527 | bcm_phy_write_misc(phydev, reg: 0x003b, chl: 0x0003, value: 0x0000); |
528 | |
529 | /* Set cal_bypassb bit rxconfig<43> */ |
530 | bcm_phy_write_misc(phydev, reg: 0x003a, chl: 0x0003, value: 0x0800); |
531 | udelay(2); |
532 | |
533 | /* Revert pwdb_override (rxconfig<5>) to 0 so that the RX pwr |
534 | * is controlled by DSP. |
535 | */ |
536 | bcm_phy_write_misc(phydev, reg: 0x003a, chl: 0x0001, value: 0x0000); |
537 | |
538 | /* Drop LSB */ |
539 | rcalnewcode11d2 = (rcalnewcode11 & 0xfffe) / 2; |
540 | tmp = bcm_phy_read_misc(phydev, reg: 0x003d, chl: 0x0001); |
541 | /* Clear bits [11:5] */ |
542 | tmp &= ~0xfe0; |
543 | /* set txcfg_ch0<5>=1 (enable + set local rcal) */ |
544 | tmp |= 0x0020 | (rcalnewcode11d2 * 64); |
545 | bcm_phy_write_misc(phydev, reg: 0x003d, chl: 0x0001, value: tmp); |
546 | bcm_phy_write_misc(phydev, reg: 0x003d, chl: 0x0002, value: tmp); |
547 | |
548 | tmp = bcm_phy_read_misc(phydev, reg: 0x003d, chl: 0x0000); |
549 | /* set txcfg<45:44>=11 (enable Rextra + invert fullscaledetect) |
550 | */ |
551 | tmp &= ~0x3000; |
552 | tmp |= 0x3000; |
553 | bcm_phy_write_misc(phydev, reg: 0x003d, chl: 0x0000, value: tmp); |
554 | |
555 | return 0; |
556 | } |
557 | |
558 | static int bcm7xxx_16nm_ephy_config_init(struct phy_device *phydev) |
559 | { |
560 | int ret, val; |
561 | |
562 | ret = bcm7xxx_16nm_ephy_afe_config(phydev); |
563 | if (ret) |
564 | return ret; |
565 | |
566 | ret = bcm_phy_set_eee(phydev, enable: true); |
567 | if (ret) |
568 | return ret; |
569 | |
570 | ret = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3); |
571 | if (ret < 0) |
572 | return ret; |
573 | |
574 | val = ret; |
575 | |
576 | /* Auto power down of DLL enabled, |
577 | * TXC/RXC disabled during auto power down. |
578 | */ |
579 | val &= ~BCM54XX_SHD_SCR3_DLLAPD_DIS; |
580 | val |= BIT(8); |
581 | |
582 | ret = bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val); |
583 | if (ret < 0) |
584 | return ret; |
585 | |
586 | return bcm_phy_enable_apd(phydev, dll_pwr_down: true); |
587 | } |
588 | |
589 | static int bcm7xxx_16nm_ephy_resume(struct phy_device *phydev) |
590 | { |
591 | int ret; |
592 | |
593 | /* Re-apply workarounds coming out suspend/resume */ |
594 | ret = bcm7xxx_16nm_ephy_config_init(phydev); |
595 | if (ret) |
596 | return ret; |
597 | |
598 | return genphy_config_aneg(phydev); |
599 | } |
600 | |
601 | #define MII_BCM7XXX_REG_INVALID 0xff |
602 | |
603 | static u8 bcm7xxx_28nm_ephy_regnum_to_shd(u16 regnum) |
604 | { |
605 | switch (regnum) { |
606 | case MDIO_CTRL1: |
607 | return MII_BCM7XXX_SHD_3_PCS_CTRL; |
608 | case MDIO_STAT1: |
609 | return MII_BCM7XXX_SHD_3_PCS_STATUS; |
610 | case MDIO_PCS_EEE_ABLE: |
611 | return MII_BCM7XXX_SHD_3_EEE_CAP; |
612 | case MDIO_AN_EEE_ADV: |
613 | return MII_BCM7XXX_SHD_3_AN_EEE_ADV; |
614 | case MDIO_AN_EEE_LPABLE: |
615 | return MII_BCM7XXX_SHD_3_EEE_LP; |
616 | case MDIO_PCS_EEE_WK_ERR: |
617 | return MII_BCM7XXX_SHD_3_EEE_WK_ERR; |
618 | default: |
619 | return MII_BCM7XXX_REG_INVALID; |
620 | } |
621 | } |
622 | |
623 | static bool bcm7xxx_28nm_ephy_dev_valid(int devnum) |
624 | { |
625 | return devnum == MDIO_MMD_AN || devnum == MDIO_MMD_PCS; |
626 | } |
627 | |
628 | static int bcm7xxx_28nm_ephy_read_mmd(struct phy_device *phydev, |
629 | int devnum, u16 regnum) |
630 | { |
631 | u8 shd = bcm7xxx_28nm_ephy_regnum_to_shd(regnum); |
632 | int ret; |
633 | |
634 | if (!bcm7xxx_28nm_ephy_dev_valid(devnum) || |
635 | shd == MII_BCM7XXX_REG_INVALID) |
636 | return -EOPNOTSUPP; |
637 | |
638 | /* set shadow mode 2 */ |
639 | ret = __phy_set_clr_bits(dev: phydev, MII_BCM7XXX_TEST, |
640 | MII_BCM7XXX_SHD_MODE_2, clr_mask: 0); |
641 | if (ret < 0) |
642 | return ret; |
643 | |
644 | /* Access the desired shadow register address */ |
645 | ret = __phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL, val: shd); |
646 | if (ret < 0) |
647 | goto reset_shadow_mode; |
648 | |
649 | ret = __phy_read(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT); |
650 | |
651 | reset_shadow_mode: |
652 | /* reset shadow mode 2 */ |
653 | __phy_set_clr_bits(dev: phydev, MII_BCM7XXX_TEST, set_mask: 0, |
654 | MII_BCM7XXX_SHD_MODE_2); |
655 | return ret; |
656 | } |
657 | |
658 | static int bcm7xxx_28nm_ephy_write_mmd(struct phy_device *phydev, |
659 | int devnum, u16 regnum, u16 val) |
660 | { |
661 | u8 shd = bcm7xxx_28nm_ephy_regnum_to_shd(regnum); |
662 | int ret; |
663 | |
664 | if (!bcm7xxx_28nm_ephy_dev_valid(devnum) || |
665 | shd == MII_BCM7XXX_REG_INVALID) |
666 | return -EOPNOTSUPP; |
667 | |
668 | /* set shadow mode 2 */ |
669 | ret = __phy_set_clr_bits(dev: phydev, MII_BCM7XXX_TEST, |
670 | MII_BCM7XXX_SHD_MODE_2, clr_mask: 0); |
671 | if (ret < 0) |
672 | return ret; |
673 | |
674 | /* Access the desired shadow register address */ |
675 | ret = __phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL, val: shd); |
676 | if (ret < 0) |
677 | goto reset_shadow_mode; |
678 | |
679 | /* Write the desired value in the shadow register */ |
680 | __phy_write(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT, val); |
681 | |
682 | reset_shadow_mode: |
683 | /* reset shadow mode 2 */ |
684 | return __phy_set_clr_bits(dev: phydev, MII_BCM7XXX_TEST, set_mask: 0, |
685 | MII_BCM7XXX_SHD_MODE_2); |
686 | } |
687 | |
688 | static int bcm7xxx_28nm_ephy_resume(struct phy_device *phydev) |
689 | { |
690 | int ret; |
691 | |
692 | /* Re-apply workarounds coming out suspend/resume */ |
693 | ret = bcm7xxx_28nm_ephy_config_init(phydev); |
694 | if (ret) |
695 | return ret; |
696 | |
697 | return genphy_config_aneg(phydev); |
698 | } |
699 | |
700 | static int bcm7xxx_config_init(struct phy_device *phydev) |
701 | { |
702 | int ret; |
703 | |
704 | /* Enable 64 clock MDIO */ |
705 | phy_write(phydev, MII_BCM7XXX_AUX_MODE, MII_BCM7XXX_64CLK_MDIO); |
706 | phy_read(phydev, MII_BCM7XXX_AUX_MODE); |
707 | |
708 | /* set shadow mode 2 */ |
709 | ret = phy_set_clr_bits(dev: phydev, MII_BCM7XXX_TEST, |
710 | MII_BCM7XXX_SHD_MODE_2, MII_BCM7XXX_SHD_MODE_2); |
711 | if (ret < 0) |
712 | return ret; |
713 | |
714 | /* set iddq_clkbias */ |
715 | phy_write(phydev, MII_BCM7XXX_100TX_DISC, val: 0x0F00); |
716 | udelay(10); |
717 | |
718 | /* reset iddq_clkbias */ |
719 | phy_write(phydev, MII_BCM7XXX_100TX_DISC, val: 0x0C00); |
720 | |
721 | phy_write(phydev, MII_BCM7XXX_100TX_FALSE_CAR, val: 0x7555); |
722 | |
723 | /* reset shadow mode 2 */ |
724 | ret = phy_set_clr_bits(dev: phydev, MII_BCM7XXX_TEST, set_mask: 0, MII_BCM7XXX_SHD_MODE_2); |
725 | if (ret < 0) |
726 | return ret; |
727 | |
728 | return 0; |
729 | } |
730 | |
731 | /* Workaround for putting the PHY in IDDQ mode, required |
732 | * for all BCM7XXX 40nm and 65nm PHYs |
733 | */ |
734 | static int bcm7xxx_suspend(struct phy_device *phydev) |
735 | { |
736 | int ret; |
737 | static const struct bcm7xxx_regs { |
738 | int reg; |
739 | u16 value; |
740 | } bcm7xxx_suspend_cfg[] = { |
741 | { MII_BCM7XXX_TEST, 0x008b }, |
742 | { MII_BCM7XXX_100TX_AUX_CTL, 0x01c0 }, |
743 | { MII_BCM7XXX_100TX_DISC, 0x7000 }, |
744 | { MII_BCM7XXX_TEST, 0x000f }, |
745 | { MII_BCM7XXX_100TX_AUX_CTL, 0x20d0 }, |
746 | { MII_BCM7XXX_TEST, 0x000b }, |
747 | }; |
748 | unsigned int i; |
749 | |
750 | for (i = 0; i < ARRAY_SIZE(bcm7xxx_suspend_cfg); i++) { |
751 | ret = phy_write(phydev, |
752 | regnum: bcm7xxx_suspend_cfg[i].reg, |
753 | val: bcm7xxx_suspend_cfg[i].value); |
754 | if (ret) |
755 | return ret; |
756 | } |
757 | |
758 | return 0; |
759 | } |
760 | |
761 | static int bcm7xxx_28nm_get_tunable(struct phy_device *phydev, |
762 | struct ethtool_tunable *tuna, |
763 | void *data) |
764 | { |
765 | switch (tuna->id) { |
766 | case ETHTOOL_PHY_DOWNSHIFT: |
767 | return bcm_phy_downshift_get(phydev, count: (u8 *)data); |
768 | default: |
769 | return -EOPNOTSUPP; |
770 | } |
771 | } |
772 | |
773 | static int bcm7xxx_28nm_set_tunable(struct phy_device *phydev, |
774 | struct ethtool_tunable *tuna, |
775 | const void *data) |
776 | { |
777 | u8 count = *(u8 *)data; |
778 | int ret; |
779 | |
780 | switch (tuna->id) { |
781 | case ETHTOOL_PHY_DOWNSHIFT: |
782 | ret = bcm_phy_downshift_set(phydev, count); |
783 | break; |
784 | default: |
785 | return -EOPNOTSUPP; |
786 | } |
787 | |
788 | if (ret) |
789 | return ret; |
790 | |
791 | /* Disable EEE advertisement since this prevents the PHY |
792 | * from successfully linking up, trigger auto-negotiation restart |
793 | * to let the MAC decide what to do. |
794 | */ |
795 | ret = bcm_phy_set_eee(phydev, enable: count == DOWNSHIFT_DEV_DISABLE); |
796 | if (ret) |
797 | return ret; |
798 | |
799 | return genphy_restart_aneg(phydev); |
800 | } |
801 | |
802 | static void bcm7xxx_28nm_get_phy_stats(struct phy_device *phydev, |
803 | struct ethtool_stats *stats, u64 *data) |
804 | { |
805 | struct bcm7xxx_phy_priv *priv = phydev->priv; |
806 | |
807 | bcm_phy_get_stats(phydev, shadow: priv->stats, stats, data); |
808 | } |
809 | |
810 | static int bcm7xxx_28nm_probe(struct phy_device *phydev) |
811 | { |
812 | struct bcm7xxx_phy_priv *priv; |
813 | struct clk *clk; |
814 | int ret = 0; |
815 | |
816 | priv = devm_kzalloc(dev: &phydev->mdio.dev, size: sizeof(*priv), GFP_KERNEL); |
817 | if (!priv) |
818 | return -ENOMEM; |
819 | |
820 | phydev->priv = priv; |
821 | |
822 | priv->stats = devm_kcalloc(dev: &phydev->mdio.dev, |
823 | n: bcm_phy_get_sset_count(phydev), size: sizeof(u64), |
824 | GFP_KERNEL); |
825 | if (!priv->stats) |
826 | return -ENOMEM; |
827 | |
828 | clk = devm_clk_get_optional_enabled(dev: &phydev->mdio.dev, NULL); |
829 | if (IS_ERR(ptr: clk)) |
830 | return PTR_ERR(ptr: clk); |
831 | |
832 | /* Dummy read to a register to workaround an issue upon reset where the |
833 | * internal inverter may not allow the first MDIO transaction to pass |
834 | * the MDIO management controller and make us return 0xffff for such |
835 | * reads. This is needed to ensure that any subsequent reads to the |
836 | * PHY will succeed. |
837 | */ |
838 | phy_read(phydev, MII_BMSR); |
839 | |
840 | return ret; |
841 | } |
842 | |
843 | #define BCM7XXX_28NM_GPHY(_oui, _name) \ |
844 | { \ |
845 | .phy_id = (_oui), \ |
846 | .phy_id_mask = 0xfffffff0, \ |
847 | .name = _name, \ |
848 | /* PHY_GBIT_FEATURES */ \ |
849 | .flags = PHY_IS_INTERNAL, \ |
850 | .config_init = bcm7xxx_28nm_config_init, \ |
851 | .resume = bcm7xxx_28nm_resume, \ |
852 | .get_tunable = bcm7xxx_28nm_get_tunable, \ |
853 | .set_tunable = bcm7xxx_28nm_set_tunable, \ |
854 | .get_sset_count = bcm_phy_get_sset_count, \ |
855 | .get_strings = bcm_phy_get_strings, \ |
856 | .get_stats = bcm7xxx_28nm_get_phy_stats, \ |
857 | .probe = bcm7xxx_28nm_probe, \ |
858 | } |
859 | |
860 | #define BCM7XXX_28NM_EPHY(_oui, _name) \ |
861 | { \ |
862 | .phy_id = (_oui), \ |
863 | .phy_id_mask = 0xfffffff0, \ |
864 | .name = _name, \ |
865 | /* PHY_BASIC_FEATURES */ \ |
866 | .flags = PHY_IS_INTERNAL, \ |
867 | .config_init = bcm7xxx_28nm_ephy_config_init, \ |
868 | .resume = bcm7xxx_28nm_ephy_resume, \ |
869 | .get_sset_count = bcm_phy_get_sset_count, \ |
870 | .get_strings = bcm_phy_get_strings, \ |
871 | .get_stats = bcm7xxx_28nm_get_phy_stats, \ |
872 | .probe = bcm7xxx_28nm_probe, \ |
873 | .read_mmd = bcm7xxx_28nm_ephy_read_mmd, \ |
874 | .write_mmd = bcm7xxx_28nm_ephy_write_mmd, \ |
875 | } |
876 | |
877 | #define BCM7XXX_40NM_EPHY(_oui, _name) \ |
878 | { \ |
879 | .phy_id = (_oui), \ |
880 | .phy_id_mask = 0xfffffff0, \ |
881 | .name = _name, \ |
882 | /* PHY_BASIC_FEATURES */ \ |
883 | .flags = PHY_IS_INTERNAL, \ |
884 | .soft_reset = genphy_soft_reset, \ |
885 | .config_init = bcm7xxx_config_init, \ |
886 | .suspend = bcm7xxx_suspend, \ |
887 | .resume = bcm7xxx_config_init, \ |
888 | } |
889 | |
890 | #define BCM7XXX_16NM_EPHY(_oui, _name) \ |
891 | { \ |
892 | .phy_id = (_oui), \ |
893 | .phy_id_mask = 0xfffffff0, \ |
894 | .name = _name, \ |
895 | /* PHY_BASIC_FEATURES */ \ |
896 | .flags = PHY_IS_INTERNAL, \ |
897 | .get_sset_count = bcm_phy_get_sset_count, \ |
898 | .get_strings = bcm_phy_get_strings, \ |
899 | .get_stats = bcm7xxx_28nm_get_phy_stats, \ |
900 | .probe = bcm7xxx_28nm_probe, \ |
901 | .config_init = bcm7xxx_16nm_ephy_config_init, \ |
902 | .config_aneg = genphy_config_aneg, \ |
903 | .read_status = genphy_read_status, \ |
904 | .resume = bcm7xxx_16nm_ephy_resume, \ |
905 | } |
906 | |
907 | static struct phy_driver bcm7xxx_driver[] = { |
908 | BCM7XXX_28NM_EPHY(PHY_ID_BCM72113, "Broadcom BCM72113" ), |
909 | BCM7XXX_28NM_EPHY(PHY_ID_BCM72116, "Broadcom BCM72116" ), |
910 | BCM7XXX_16NM_EPHY(PHY_ID_BCM72165, "Broadcom BCM72165" ), |
911 | BCM7XXX_28NM_GPHY(PHY_ID_BCM7250, "Broadcom BCM7250" ), |
912 | BCM7XXX_28NM_EPHY(PHY_ID_BCM7255, "Broadcom BCM7255" ), |
913 | BCM7XXX_28NM_EPHY(PHY_ID_BCM7260, "Broadcom BCM7260" ), |
914 | BCM7XXX_28NM_EPHY(PHY_ID_BCM7268, "Broadcom BCM7268" ), |
915 | BCM7XXX_28NM_EPHY(PHY_ID_BCM7271, "Broadcom BCM7271" ), |
916 | BCM7XXX_28NM_GPHY(PHY_ID_BCM7278, "Broadcom BCM7278" ), |
917 | BCM7XXX_28NM_GPHY(PHY_ID_BCM7364, "Broadcom BCM7364" ), |
918 | BCM7XXX_28NM_GPHY(PHY_ID_BCM7366, "Broadcom BCM7366" ), |
919 | BCM7XXX_16NM_EPHY(PHY_ID_BCM74165, "Broadcom BCM74165" ), |
920 | BCM7XXX_28NM_GPHY(PHY_ID_BCM74371, "Broadcom BCM74371" ), |
921 | BCM7XXX_28NM_GPHY(PHY_ID_BCM7439, "Broadcom BCM7439" ), |
922 | BCM7XXX_28NM_GPHY(PHY_ID_BCM7439_2, "Broadcom BCM7439 (2)" ), |
923 | BCM7XXX_28NM_GPHY(PHY_ID_BCM7445, "Broadcom BCM7445" ), |
924 | BCM7XXX_40NM_EPHY(PHY_ID_BCM7346, "Broadcom BCM7346" ), |
925 | BCM7XXX_40NM_EPHY(PHY_ID_BCM7362, "Broadcom BCM7362" ), |
926 | BCM7XXX_40NM_EPHY(PHY_ID_BCM7425, "Broadcom BCM7425" ), |
927 | BCM7XXX_40NM_EPHY(PHY_ID_BCM7429, "Broadcom BCM7429" ), |
928 | BCM7XXX_40NM_EPHY(PHY_ID_BCM7435, "Broadcom BCM7435" ), |
929 | BCM7XXX_16NM_EPHY(PHY_ID_BCM7712, "Broadcom BCM7712" ), |
930 | }; |
931 | |
932 | static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = { |
933 | { PHY_ID_BCM72113, 0xfffffff0 }, |
934 | { PHY_ID_BCM72116, 0xfffffff0, }, |
935 | { PHY_ID_BCM72165, 0xfffffff0, }, |
936 | { PHY_ID_BCM7250, 0xfffffff0, }, |
937 | { PHY_ID_BCM7255, 0xfffffff0, }, |
938 | { PHY_ID_BCM7260, 0xfffffff0, }, |
939 | { PHY_ID_BCM7268, 0xfffffff0, }, |
940 | { PHY_ID_BCM7271, 0xfffffff0, }, |
941 | { PHY_ID_BCM7278, 0xfffffff0, }, |
942 | { PHY_ID_BCM7364, 0xfffffff0, }, |
943 | { PHY_ID_BCM7366, 0xfffffff0, }, |
944 | { PHY_ID_BCM7346, 0xfffffff0, }, |
945 | { PHY_ID_BCM7362, 0xfffffff0, }, |
946 | { PHY_ID_BCM7425, 0xfffffff0, }, |
947 | { PHY_ID_BCM7429, 0xfffffff0, }, |
948 | { PHY_ID_BCM74371, 0xfffffff0, }, |
949 | { PHY_ID_BCM7439, 0xfffffff0, }, |
950 | { PHY_ID_BCM7435, 0xfffffff0, }, |
951 | { PHY_ID_BCM7445, 0xfffffff0, }, |
952 | { PHY_ID_BCM7712, 0xfffffff0, }, |
953 | { } |
954 | }; |
955 | |
956 | module_phy_driver(bcm7xxx_driver); |
957 | |
958 | MODULE_DEVICE_TABLE(mdio, bcm7xxx_tbl); |
959 | |
960 | MODULE_DESCRIPTION("Broadcom BCM7xxx internal PHY driver" ); |
961 | MODULE_LICENSE("GPL" ); |
962 | MODULE_AUTHOR("Broadcom Corporation" ); |
963 | |