Merge tag 'char-misc-4.3-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh...
[firefly-linux-kernel-4.4.55.git] / 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 100BT Cfeed=011 to improve rise/fall time */
140         phy_write_misc(phydev, AFE_TX_CONFIG, 0x431);
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_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */
171         phy_write_misc(phydev, AFE_TX_CONFIG, 0x431);
172
173         /* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */
174         phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da);
175
176         /* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal
177          * offset for HT=0 code
178          */
179         phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3);
180
181         /* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */
182         phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0x0010);
183
184         /* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */
185         phy_write_misc(phydev, DSP_TAP10, 0x011b);
186
187         /* Reset R_CAL/RC_CAL engine */
188         r_rc_cal_reset(phydev);
189
190         return 0;
191 }
192
193 static int bcm7xxx_apd_enable(struct phy_device *phydev)
194 {
195         int val;
196
197         /* Enable powering down of the DLL during auto-power down */
198         val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_SCR3);
199         if (val < 0)
200                 return val;
201
202         val |= BCM54XX_SHD_SCR3_DLLAPD_DIS;
203         bcm54xx_shadow_write(phydev, BCM54XX_SHD_SCR3, val);
204
205         /* Enable auto-power down */
206         val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_APD);
207         if (val < 0)
208                 return val;
209
210         val |= BCM54XX_SHD_APD_EN;
211         return bcm54xx_shadow_write(phydev, BCM54XX_SHD_APD, val);
212 }
213
214 static int bcm7xxx_eee_enable(struct phy_device *phydev)
215 {
216         int val;
217
218         val = phy_read_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL,
219                                     MDIO_MMD_AN, phydev->addr);
220         if (val < 0)
221                 return val;
222
223         /* Enable general EEE feature at the PHY level */
224         val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X;
225
226         phy_write_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL,
227                                MDIO_MMD_AN, phydev->addr, val);
228
229         /* Advertise supported modes */
230         val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV,
231                                     MDIO_MMD_AN, phydev->addr);
232
233         val |= (MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T);
234         phy_write_mmd_indirect(phydev, MDIO_AN_EEE_ADV,
235                                MDIO_MMD_AN, phydev->addr, val);
236
237         return 0;
238 }
239
240 static int bcm7xxx_28nm_config_init(struct phy_device *phydev)
241 {
242         u8 rev = PHY_BRCM_7XXX_REV(phydev->dev_flags);
243         u8 patch = PHY_BRCM_7XXX_PATCH(phydev->dev_flags);
244         int ret = 0;
245
246         pr_info_once("%s: %s PHY revision: 0x%02x, patch: %d\n",
247                      dev_name(&phydev->dev), phydev->drv->name, rev, patch);
248
249         /* Dummy read to a register to workaround an issue upon reset where the
250          * internal inverter may not allow the first MDIO transaction to pass
251          * the MDIO management controller and make us return 0xffff for such
252          * reads.
253          */
254         phy_read(phydev, MII_BMSR);
255
256         switch (rev) {
257         case 0xb0:
258                 ret = bcm7xxx_28nm_b0_afe_config_init(phydev);
259                 break;
260         case 0xd0:
261                 ret = bcm7xxx_28nm_d0_afe_config_init(phydev);
262                 break;
263         case 0xe0:
264         case 0xf0:
265         /* Rev G0 introduces a roll over */
266         case 0x10:
267                 ret = bcm7xxx_28nm_e0_plus_afe_config_init(phydev);
268                 break;
269         default:
270                 break;
271         }
272
273         if (ret)
274                 return ret;
275
276         ret = bcm7xxx_eee_enable(phydev);
277         if (ret)
278                 return ret;
279
280         return bcm7xxx_apd_enable(phydev);
281 }
282
283 static int bcm7xxx_28nm_resume(struct phy_device *phydev)
284 {
285         int ret;
286
287         /* Re-apply workarounds coming out suspend/resume */
288         ret = bcm7xxx_28nm_config_init(phydev);
289         if (ret)
290                 return ret;
291
292         /* 28nm Gigabit PHYs come out of reset without any half-duplex
293          * or "hub" compliant advertised mode, fix that. This does not
294          * cause any problems with the PHY library since genphy_config_aneg()
295          * gracefully handles auto-negotiated and forced modes.
296          */
297         return genphy_config_aneg(phydev);
298 }
299
300 static int phy_set_clr_bits(struct phy_device *dev, int location,
301                                         int set_mask, int clr_mask)
302 {
303         int v, ret;
304
305         v = phy_read(dev, location);
306         if (v < 0)
307                 return v;
308
309         v &= ~clr_mask;
310         v |= set_mask;
311
312         ret = phy_write(dev, location, v);
313         if (ret < 0)
314                 return ret;
315
316         return v;
317 }
318
319 static int bcm7xxx_config_init(struct phy_device *phydev)
320 {
321         int ret;
322
323         /* Enable 64 clock MDIO */
324         phy_write(phydev, MII_BCM7XXX_AUX_MODE, MII_BCM7XX_64CLK_MDIO);
325         phy_read(phydev, MII_BCM7XXX_AUX_MODE);
326
327         /* Workaround only required for 100Mbits/sec capable PHYs */
328         if (phydev->supported & PHY_GBIT_FEATURES)
329                 return 0;
330
331         /* set shadow mode 2 */
332         ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST,
333                         MII_BCM7XXX_SHD_MODE_2, MII_BCM7XXX_SHD_MODE_2);
334         if (ret < 0)
335                 return ret;
336
337         /* set iddq_clkbias */
338         phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0F00);
339         udelay(10);
340
341         /* reset iddq_clkbias */
342         phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0C00);
343
344         phy_write(phydev, MII_BCM7XXX_100TX_FALSE_CAR, 0x7555);
345
346         /* reset shadow mode 2 */
347         ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, MII_BCM7XXX_SHD_MODE_2, 0);
348         if (ret < 0)
349                 return ret;
350
351         return 0;
352 }
353
354 /* Workaround for putting the PHY in IDDQ mode, required
355  * for all BCM7XXX 40nm and 65nm PHYs
356  */
357 static int bcm7xxx_suspend(struct phy_device *phydev)
358 {
359         int ret;
360         const struct bcm7xxx_regs {
361                 int reg;
362                 u16 value;
363         } bcm7xxx_suspend_cfg[] = {
364                 { MII_BCM7XXX_TEST, 0x008b },
365                 { MII_BCM7XXX_100TX_AUX_CTL, 0x01c0 },
366                 { MII_BCM7XXX_100TX_DISC, 0x7000 },
367                 { MII_BCM7XXX_TEST, 0x000f },
368                 { MII_BCM7XXX_100TX_AUX_CTL, 0x20d0 },
369                 { MII_BCM7XXX_TEST, 0x000b },
370         };
371         unsigned int i;
372
373         for (i = 0; i < ARRAY_SIZE(bcm7xxx_suspend_cfg); i++) {
374                 ret = phy_write(phydev,
375                                 bcm7xxx_suspend_cfg[i].reg,
376                                 bcm7xxx_suspend_cfg[i].value);
377                 if (ret)
378                         return ret;
379         }
380
381         return 0;
382 }
383
384 static int bcm7xxx_dummy_config_init(struct phy_device *phydev)
385 {
386         return 0;
387 }
388
389 #define BCM7XXX_28NM_GPHY(_oui, _name)                                  \
390 {                                                                       \
391         .phy_id         = (_oui),                                       \
392         .phy_id_mask    = 0xfffffff0,                                   \
393         .name           = _name,                                        \
394         .features       = PHY_GBIT_FEATURES |                           \
395                           SUPPORTED_Pause | SUPPORTED_Asym_Pause,       \
396         .flags          = PHY_IS_INTERNAL,                              \
397         .config_init    = bcm7xxx_28nm_config_init,                     \
398         .config_aneg    = genphy_config_aneg,                           \
399         .read_status    = genphy_read_status,                           \
400         .resume         = bcm7xxx_28nm_resume,                          \
401         .driver         = { .owner = THIS_MODULE },                     \
402 }
403
404 static struct phy_driver bcm7xxx_driver[] = {
405         BCM7XXX_28NM_GPHY(PHY_ID_BCM7250, "Broadcom BCM7250"),
406         BCM7XXX_28NM_GPHY(PHY_ID_BCM7364, "Broadcom BCM7364"),
407         BCM7XXX_28NM_GPHY(PHY_ID_BCM7366, "Broadcom BCM7366"),
408         BCM7XXX_28NM_GPHY(PHY_ID_BCM7439, "Broadcom BCM7439"),
409         BCM7XXX_28NM_GPHY(PHY_ID_BCM7439_2, "Broadcom BCM7439 (2)"),
410         BCM7XXX_28NM_GPHY(PHY_ID_BCM7445, "Broadcom BCM7445"),
411 {
412         .phy_id         = PHY_ID_BCM7425,
413         .phy_id_mask    = 0xfffffff0,
414         .name           = "Broadcom BCM7425",
415         .features       = PHY_GBIT_FEATURES |
416                           SUPPORTED_Pause | SUPPORTED_Asym_Pause,
417         .flags          = PHY_IS_INTERNAL,
418         .config_init    = bcm7xxx_config_init,
419         .config_aneg    = genphy_config_aneg,
420         .read_status    = genphy_read_status,
421         .suspend        = bcm7xxx_suspend,
422         .resume         = bcm7xxx_config_init,
423         .driver         = { .owner = THIS_MODULE },
424 }, {
425         .phy_id         = PHY_ID_BCM7429,
426         .phy_id_mask    = 0xfffffff0,
427         .name           = "Broadcom BCM7429",
428         .features       = PHY_GBIT_FEATURES |
429                           SUPPORTED_Pause | SUPPORTED_Asym_Pause,
430         .flags          = PHY_IS_INTERNAL,
431         .config_init    = bcm7xxx_config_init,
432         .config_aneg    = genphy_config_aneg,
433         .read_status    = genphy_read_status,
434         .suspend        = bcm7xxx_suspend,
435         .resume         = bcm7xxx_config_init,
436         .driver         = { .owner = THIS_MODULE },
437 }, {
438         .phy_id         = PHY_BCM_OUI_4,
439         .phy_id_mask    = 0xffff0000,
440         .name           = "Broadcom BCM7XXX 40nm",
441         .features       = PHY_GBIT_FEATURES |
442                           SUPPORTED_Pause | SUPPORTED_Asym_Pause,
443         .flags          = PHY_IS_INTERNAL,
444         .config_init    = bcm7xxx_config_init,
445         .config_aneg    = genphy_config_aneg,
446         .read_status    = genphy_read_status,
447         .suspend        = bcm7xxx_suspend,
448         .resume         = bcm7xxx_config_init,
449         .driver         = { .owner = THIS_MODULE },
450 }, {
451         .phy_id         = PHY_BCM_OUI_5,
452         .phy_id_mask    = 0xffffff00,
453         .name           = "Broadcom BCM7XXX 65nm",
454         .features       = PHY_BASIC_FEATURES |
455                           SUPPORTED_Pause | SUPPORTED_Asym_Pause,
456         .flags          = PHY_IS_INTERNAL,
457         .config_init    = bcm7xxx_dummy_config_init,
458         .config_aneg    = genphy_config_aneg,
459         .read_status    = genphy_read_status,
460         .suspend        = bcm7xxx_suspend,
461         .resume         = bcm7xxx_config_init,
462         .driver         = { .owner = THIS_MODULE },
463 } };
464
465 static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = {
466         { PHY_ID_BCM7250, 0xfffffff0, },
467         { PHY_ID_BCM7364, 0xfffffff0, },
468         { PHY_ID_BCM7366, 0xfffffff0, },
469         { PHY_ID_BCM7425, 0xfffffff0, },
470         { PHY_ID_BCM7429, 0xfffffff0, },
471         { PHY_ID_BCM7439, 0xfffffff0, },
472         { PHY_ID_BCM7445, 0xfffffff0, },
473         { PHY_BCM_OUI_4, 0xffff0000 },
474         { PHY_BCM_OUI_5, 0xffffff00 },
475         { }
476 };
477
478 module_phy_driver(bcm7xxx_driver);
479
480 MODULE_DEVICE_TABLE(mdio, bcm7xxx_tbl);
481
482 MODULE_DESCRIPTION("Broadcom BCM7xxx internal PHY driver");
483 MODULE_LICENSE("GPL");
484 MODULE_AUTHOR("Broadcom Corporation");