Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / net / phy / bcm7xxx.c
1 /*
2  * Broadcom BCM7xxx internal transceivers support.
3  *
4  * Copyright (C) 2014, Broadcom Corporation
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version
9  * 2 of the License, or (at your option) any later version.
10  */
11
12 #include <linux/module.h>
13 #include <linux/phy.h>
14 #include <linux/delay.h>
15 #include <linux/bitops.h>
16 #include <linux/brcmphy.h>
17 #include <linux/mdio.h>
18
19 /* Broadcom BCM7xxx internal PHY registers */
20 #define MII_BCM7XXX_CHANNEL_WIDTH       0x2000
21
22 /* 40nm only register definitions */
23 #define MII_BCM7XXX_100TX_AUX_CTL       0x10
24 #define MII_BCM7XXX_100TX_FALSE_CAR     0x13
25 #define MII_BCM7XXX_100TX_DISC          0x14
26 #define MII_BCM7XXX_AUX_MODE            0x1d
27 #define  MII_BCM7XX_64CLK_MDIO          BIT(12)
28 #define MII_BCM7XXX_CORE_BASE1E         0x1e
29 #define MII_BCM7XXX_TEST                0x1f
30 #define  MII_BCM7XXX_SHD_MODE_2         BIT(2)
31
32 /* 28nm only register definitions */
33 #define MISC_ADDR(base, channel)        base, channel
34
35 #define DSP_TAP10                       MISC_ADDR(0x0a, 0)
36 #define PLL_PLLCTRL_1                   MISC_ADDR(0x32, 1)
37 #define PLL_PLLCTRL_2                   MISC_ADDR(0x32, 2)
38 #define PLL_PLLCTRL_4                   MISC_ADDR(0x33, 0)
39
40 #define AFE_RXCONFIG_0                  MISC_ADDR(0x38, 0)
41 #define AFE_RXCONFIG_1                  MISC_ADDR(0x38, 1)
42 #define AFE_RXCONFIG_2                  MISC_ADDR(0x38, 2)
43 #define AFE_RX_LP_COUNTER               MISC_ADDR(0x38, 3)
44 #define AFE_TX_CONFIG                   MISC_ADDR(0x39, 0)
45 #define AFE_VDCA_ICTRL_0                MISC_ADDR(0x39, 1)
46 #define AFE_VDAC_OTHERS_0               MISC_ADDR(0x39, 3)
47 #define AFE_HPF_TRIM_OTHERS             MISC_ADDR(0x3a, 0)
48
49 #define CORE_EXPB0                      0xb0
50
51 static void phy_write_exp(struct phy_device *phydev,
52                                         u16 reg, u16 value)
53 {
54         phy_write(phydev, MII_BCM54XX_EXP_SEL, MII_BCM54XX_EXP_SEL_ER | reg);
55         phy_write(phydev, MII_BCM54XX_EXP_DATA, value);
56 }
57
58 static void phy_write_misc(struct phy_device *phydev,
59                                         u16 reg, u16 chl, u16 value)
60 {
61         int tmp;
62
63         phy_write(phydev, MII_BCM54XX_AUX_CTL, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
64
65         tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL);
66         tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA;
67         phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp);
68
69         tmp = (chl * MII_BCM7XXX_CHANNEL_WIDTH) | reg;
70         phy_write(phydev, MII_BCM54XX_EXP_SEL, tmp);
71
72         phy_write(phydev, MII_BCM54XX_EXP_DATA, value);
73 }
74
75 static void r_rc_cal_reset(struct phy_device *phydev)
76 {
77         /* Reset R_CAL/RC_CAL Engine */
78         phy_write_exp(phydev, 0x00b0, 0x0010);
79
80         /* Disable Reset R_AL/RC_CAL Engine */
81         phy_write_exp(phydev, 0x00b0, 0x0000);
82 }
83
84 static int bcm7xxx_28nm_b0_afe_config_init(struct phy_device *phydev)
85 {
86         /* Increase VCO range to prevent unlocking problem of PLL at low
87          * temp
88          */
89         phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048);
90
91         /* Change Ki to 011 */
92         phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b);
93
94         /* Disable loading of TVCO buffer to bandgap, set bandgap trim
95          * to 111
96          */
97         phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20);
98
99         /* Adjust bias current trim by -3 */
100         phy_write_misc(phydev, DSP_TAP10, 0x690b);
101
102         /* Switch to CORE_BASE1E */
103         phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0xd);
104
105         r_rc_cal_reset(phydev);
106
107         /* write AFE_RXCONFIG_0 */
108         phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19);
109
110         /* write AFE_RXCONFIG_1 */
111         phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f);
112
113         /* write AFE_RX_LP_COUNTER */
114         phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);
115
116         /* write AFE_HPF_TRIM_OTHERS */
117         phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b);
118
119         /* write AFTE_TX_CONFIG */
120         phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800);
121
122         return 0;
123 }
124
125 static int bcm7xxx_28nm_d0_afe_config_init(struct phy_device *phydev)
126 {
127         /* AFE_RXCONFIG_0 */
128         phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb15);
129
130         /* AFE_RXCONFIG_1 */
131         phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f);
132
133         /* AFE_RXCONFIG_2, set rCal offset for HT=0 code and LT=-2 code */
134         phy_write_misc(phydev, AFE_RXCONFIG_2, 0x2003);
135
136         /* AFE_RX_LP_COUNTER, set RX bandwidth to maximum */
137         phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);
138
139         /* AFE_TX_CONFIG, set 1000BT Cfeed=110 for all ports */
140         phy_write_misc(phydev, AFE_TX_CONFIG, 0x0061);
141
142         /* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */
143         phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da);
144
145         /* AFE_VDAC_OTHERS_0, set 1000BT Cidac=010 for all ports */
146         phy_write_misc(phydev, AFE_VDAC_OTHERS_0, 0xa020);
147
148         /* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal
149          * offset for HT=0 code
150          */
151         phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3);
152
153         /* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */
154         phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0x0010);
155
156         /* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */
157         phy_write_misc(phydev, DSP_TAP10, 0x011b);
158
159         /* Reset R_CAL/RC_CAL engine */
160         r_rc_cal_reset(phydev);
161
162         return 0;
163 }
164
165 static int bcm7xxx_28nm_e0_plus_afe_config_init(struct phy_device *phydev)
166 {
167         /* AFE_RXCONFIG_1, provide more margin for INL/DNL measurement */
168         phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f);
169
170         /* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */
171         phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da);
172
173         /* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal
174          * offset for HT=0 code
175          */
176         phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3);
177
178         /* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */
179         phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0x0010);
180
181         /* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */
182         phy_write_misc(phydev, DSP_TAP10, 0x011b);
183
184         /* Reset R_CAL/RC_CAL engine */
185         r_rc_cal_reset(phydev);
186
187         return 0;
188 }
189
190 static int bcm7xxx_apd_enable(struct phy_device *phydev)
191 {
192         int val;
193
194         /* Enable powering down of the DLL during auto-power down */
195         val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_SCR3);
196         if (val < 0)
197                 return val;
198
199         val |= BCM54XX_SHD_SCR3_DLLAPD_DIS;
200         bcm54xx_shadow_write(phydev, BCM54XX_SHD_SCR3, val);
201
202         /* Enable auto-power down */
203         val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_APD);
204         if (val < 0)
205                 return val;
206
207         val |= BCM54XX_SHD_APD_EN;
208         return bcm54xx_shadow_write(phydev, BCM54XX_SHD_APD, val);
209 }
210
211 static int bcm7xxx_eee_enable(struct phy_device *phydev)
212 {
213         int val;
214
215         val = phy_read_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL,
216                                     MDIO_MMD_AN, phydev->addr);
217         if (val < 0)
218                 return val;
219
220         /* Enable general EEE feature at the PHY level */
221         val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X;
222
223         phy_write_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL,
224                                MDIO_MMD_AN, phydev->addr, val);
225
226         /* Advertise supported modes */
227         val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV,
228                                     MDIO_MMD_AN, phydev->addr);
229
230         val |= (MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T);
231         phy_write_mmd_indirect(phydev, MDIO_AN_EEE_ADV,
232                                MDIO_MMD_AN, phydev->addr, val);
233
234         return 0;
235 }
236
237 static int bcm7xxx_28nm_config_init(struct phy_device *phydev)
238 {
239         u8 rev = PHY_BRCM_7XXX_REV(phydev->dev_flags);
240         u8 patch = PHY_BRCM_7XXX_PATCH(phydev->dev_flags);
241         int ret = 0;
242
243         pr_info_once("%s: %s PHY revision: 0x%02x, patch: %d\n",
244                      dev_name(&phydev->dev), phydev->drv->name, rev, patch);
245
246         switch (rev) {
247         case 0xb0:
248                 ret = bcm7xxx_28nm_b0_afe_config_init(phydev);
249                 break;
250         case 0xd0:
251                 ret = bcm7xxx_28nm_d0_afe_config_init(phydev);
252                 break;
253         case 0xe0:
254         case 0xf0:
255         /* Rev G0 introduces a roll over */
256         case 0x10:
257                 ret = bcm7xxx_28nm_e0_plus_afe_config_init(phydev);
258                 break;
259         default:
260                 break;
261         }
262
263         if (ret)
264                 return ret;
265
266         ret = bcm7xxx_eee_enable(phydev);
267         if (ret)
268                 return ret;
269
270         return bcm7xxx_apd_enable(phydev);
271 }
272
273 static int bcm7xxx_28nm_resume(struct phy_device *phydev)
274 {
275         int ret;
276
277         /* Re-apply workarounds coming out suspend/resume */
278         ret = bcm7xxx_28nm_config_init(phydev);
279         if (ret)
280                 return ret;
281
282         /* 28nm Gigabit PHYs come out of reset without any half-duplex
283          * or "hub" compliant advertised mode, fix that. This does not
284          * cause any problems with the PHY library since genphy_config_aneg()
285          * gracefully handles auto-negotiated and forced modes.
286          */
287         return genphy_config_aneg(phydev);
288 }
289
290 static int phy_set_clr_bits(struct phy_device *dev, int location,
291                                         int set_mask, int clr_mask)
292 {
293         int v, ret;
294
295         v = phy_read(dev, location);
296         if (v < 0)
297                 return v;
298
299         v &= ~clr_mask;
300         v |= set_mask;
301
302         ret = phy_write(dev, location, v);
303         if (ret < 0)
304                 return ret;
305
306         return v;
307 }
308
309 static int bcm7xxx_config_init(struct phy_device *phydev)
310 {
311         int ret;
312
313         /* Enable 64 clock MDIO */
314         phy_write(phydev, MII_BCM7XXX_AUX_MODE, MII_BCM7XX_64CLK_MDIO);
315         phy_read(phydev, MII_BCM7XXX_AUX_MODE);
316
317         /* Workaround only required for 100Mbits/sec capable PHYs */
318         if (phydev->supported & PHY_GBIT_FEATURES)
319                 return 0;
320
321         /* set shadow mode 2 */
322         ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST,
323                         MII_BCM7XXX_SHD_MODE_2, MII_BCM7XXX_SHD_MODE_2);
324         if (ret < 0)
325                 return ret;
326
327         /* set iddq_clkbias */
328         phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0F00);
329         udelay(10);
330
331         /* reset iddq_clkbias */
332         phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0C00);
333
334         phy_write(phydev, MII_BCM7XXX_100TX_FALSE_CAR, 0x7555);
335
336         /* reset shadow mode 2 */
337         ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, MII_BCM7XXX_SHD_MODE_2, 0);
338         if (ret < 0)
339                 return ret;
340
341         return 0;
342 }
343
344 /* Workaround for putting the PHY in IDDQ mode, required
345  * for all BCM7XXX 40nm and 65nm PHYs
346  */
347 static int bcm7xxx_suspend(struct phy_device *phydev)
348 {
349         int ret;
350         const struct bcm7xxx_regs {
351                 int reg;
352                 u16 value;
353         } bcm7xxx_suspend_cfg[] = {
354                 { MII_BCM7XXX_TEST, 0x008b },
355                 { MII_BCM7XXX_100TX_AUX_CTL, 0x01c0 },
356                 { MII_BCM7XXX_100TX_DISC, 0x7000 },
357                 { MII_BCM7XXX_TEST, 0x000f },
358                 { MII_BCM7XXX_100TX_AUX_CTL, 0x20d0 },
359                 { MII_BCM7XXX_TEST, 0x000b },
360         };
361         unsigned int i;
362
363         for (i = 0; i < ARRAY_SIZE(bcm7xxx_suspend_cfg); i++) {
364                 ret = phy_write(phydev,
365                                 bcm7xxx_suspend_cfg[i].reg,
366                                 bcm7xxx_suspend_cfg[i].value);
367                 if (ret)
368                         return ret;
369         }
370
371         return 0;
372 }
373
374 static int bcm7xxx_dummy_config_init(struct phy_device *phydev)
375 {
376         return 0;
377 }
378
379 #define BCM7XXX_28NM_GPHY(_oui, _name)                                  \
380 {                                                                       \
381         .phy_id         = (_oui),                                       \
382         .phy_id_mask    = 0xfffffff0,                                   \
383         .name           = _name,                                        \
384         .features       = PHY_GBIT_FEATURES |                           \
385                           SUPPORTED_Pause | SUPPORTED_Asym_Pause,       \
386         .flags          = PHY_IS_INTERNAL,                              \
387         .config_init    = bcm7xxx_28nm_config_init,                     \
388         .config_aneg    = genphy_config_aneg,                           \
389         .read_status    = genphy_read_status,                           \
390         .resume         = bcm7xxx_28nm_resume,                          \
391         .driver         = { .owner = THIS_MODULE },                     \
392 }
393
394 static struct phy_driver bcm7xxx_driver[] = {
395         BCM7XXX_28NM_GPHY(PHY_ID_BCM7250, "Broadcom BCM7250"),
396         BCM7XXX_28NM_GPHY(PHY_ID_BCM7364, "Broadcom BCM7364"),
397         BCM7XXX_28NM_GPHY(PHY_ID_BCM7366, "Broadcom BCM7366"),
398         BCM7XXX_28NM_GPHY(PHY_ID_BCM7439, "Broadcom BCM7439"),
399         BCM7XXX_28NM_GPHY(PHY_ID_BCM7439_2, "Broadcom BCM7439 (2)"),
400         BCM7XXX_28NM_GPHY(PHY_ID_BCM7445, "Broadcom BCM7445"),
401 {
402         .phy_id         = PHY_ID_BCM7425,
403         .phy_id_mask    = 0xfffffff0,
404         .name           = "Broadcom BCM7425",
405         .features       = PHY_GBIT_FEATURES |
406                           SUPPORTED_Pause | SUPPORTED_Asym_Pause,
407         .flags          = PHY_IS_INTERNAL,
408         .config_init    = bcm7xxx_config_init,
409         .config_aneg    = genphy_config_aneg,
410         .read_status    = genphy_read_status,
411         .suspend        = bcm7xxx_suspend,
412         .resume         = bcm7xxx_config_init,
413         .driver         = { .owner = THIS_MODULE },
414 }, {
415         .phy_id         = PHY_ID_BCM7429,
416         .phy_id_mask    = 0xfffffff0,
417         .name           = "Broadcom BCM7429",
418         .features       = PHY_GBIT_FEATURES |
419                           SUPPORTED_Pause | SUPPORTED_Asym_Pause,
420         .flags          = PHY_IS_INTERNAL,
421         .config_init    = bcm7xxx_config_init,
422         .config_aneg    = genphy_config_aneg,
423         .read_status    = genphy_read_status,
424         .suspend        = bcm7xxx_suspend,
425         .resume         = bcm7xxx_config_init,
426         .driver         = { .owner = THIS_MODULE },
427 }, {
428         .phy_id         = PHY_BCM_OUI_4,
429         .phy_id_mask    = 0xffff0000,
430         .name           = "Broadcom BCM7XXX 40nm",
431         .features       = PHY_GBIT_FEATURES |
432                           SUPPORTED_Pause | SUPPORTED_Asym_Pause,
433         .flags          = PHY_IS_INTERNAL,
434         .config_init    = bcm7xxx_config_init,
435         .config_aneg    = genphy_config_aneg,
436         .read_status    = genphy_read_status,
437         .suspend        = bcm7xxx_suspend,
438         .resume         = bcm7xxx_config_init,
439         .driver         = { .owner = THIS_MODULE },
440 }, {
441         .phy_id         = PHY_BCM_OUI_5,
442         .phy_id_mask    = 0xffffff00,
443         .name           = "Broadcom BCM7XXX 65nm",
444         .features       = PHY_BASIC_FEATURES |
445                           SUPPORTED_Pause | SUPPORTED_Asym_Pause,
446         .flags          = PHY_IS_INTERNAL,
447         .config_init    = bcm7xxx_dummy_config_init,
448         .config_aneg    = genphy_config_aneg,
449         .read_status    = genphy_read_status,
450         .suspend        = bcm7xxx_suspend,
451         .resume         = bcm7xxx_config_init,
452         .driver         = { .owner = THIS_MODULE },
453 } };
454
455 static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = {
456         { PHY_ID_BCM7250, 0xfffffff0, },
457         { PHY_ID_BCM7364, 0xfffffff0, },
458         { PHY_ID_BCM7366, 0xfffffff0, },
459         { PHY_ID_BCM7425, 0xfffffff0, },
460         { PHY_ID_BCM7429, 0xfffffff0, },
461         { PHY_ID_BCM7439, 0xfffffff0, },
462         { PHY_ID_BCM7445, 0xfffffff0, },
463         { PHY_BCM_OUI_4, 0xffff0000 },
464         { PHY_BCM_OUI_5, 0xffffff00 },
465         { }
466 };
467
468 module_phy_driver(bcm7xxx_driver);
469
470 MODULE_DEVICE_TABLE(mdio, bcm7xxx_tbl);
471
472 MODULE_DESCRIPTION("Broadcom BCM7xxx internal PHY driver");
473 MODULE_LICENSE("GPL");
474 MODULE_AUTHOR("Broadcom Corporation");