Merge tag 'pci-v5.15-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaa...
[linux-2.6-microblaze.git] / drivers / net / phy / bcm-cygnus.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2015 Broadcom Corporation
4  */
5
6 /* Broadcom Cygnus SoC internal transceivers support. */
7 #include "bcm-phy-lib.h"
8 #include <linux/brcmphy.h>
9 #include <linux/module.h>
10 #include <linux/netdevice.h>
11 #include <linux/phy.h>
12
13 struct bcm_omega_phy_priv {
14         u64     *stats;
15 };
16
17 /* Broadcom Cygnus Phy specific registers */
18 #define MII_BCM_CYGNUS_AFE_VDAC_ICTRL_0  0x91E5 /* VDAL Control register */
19
20 static int bcm_cygnus_afe_config(struct phy_device *phydev)
21 {
22         int rc;
23
24         /* ensure smdspclk is enabled */
25         rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, 0x0c30);
26         if (rc < 0)
27                 return rc;
28
29         /* AFE_VDAC_ICTRL_0 bit 7:4 Iq=1100 for 1g 10bt, normal modes */
30         rc = bcm_phy_write_misc(phydev, 0x39, 0x01, 0xA7C8);
31         if (rc < 0)
32                 return rc;
33
34         /* AFE_HPF_TRIM_OTHERS bit11=1, short cascode enable for all modes*/
35         rc = bcm_phy_write_misc(phydev, 0x3A, 0x00, 0x0803);
36         if (rc < 0)
37                 return rc;
38
39         /* AFE_TX_CONFIG_1 bit 7:4 Iq=1100 for test modes */
40         rc = bcm_phy_write_misc(phydev, 0x3A, 0x01, 0xA740);
41         if (rc < 0)
42                 return rc;
43
44         /* AFE TEMPSEN_OTHERS rcal_HT, rcal_LT 10000 */
45         rc = bcm_phy_write_misc(phydev, 0x3A, 0x03, 0x8400);
46         if (rc < 0)
47                 return rc;
48
49         /* AFE_FUTURE_RSV bit 2:0 rccal <2:0>=100 */
50         rc = bcm_phy_write_misc(phydev, 0x3B, 0x00, 0x0004);
51         if (rc < 0)
52                 return rc;
53
54         /* Adjust bias current trim to overcome digital offSet */
55         rc = phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x02);
56         if (rc < 0)
57                 return rc;
58
59         /* make rcal=100, since rdb default is 000 */
60         rc = bcm_phy_write_exp_sel(phydev, MII_BRCM_CORE_EXPB1, 0x10);
61         if (rc < 0)
62                 return rc;
63
64         /* CORE_EXPB0, Reset R_CAL/RC_CAL Engine */
65         rc = bcm_phy_write_exp_sel(phydev, MII_BRCM_CORE_EXPB0, 0x10);
66         if (rc < 0)
67                 return rc;
68
69         /* CORE_EXPB0, Disable Reset R_CAL/RC_CAL Engine */
70         rc = bcm_phy_write_exp_sel(phydev, MII_BRCM_CORE_EXPB0, 0x00);
71
72         return 0;
73 }
74
75 static int bcm_cygnus_config_init(struct phy_device *phydev)
76 {
77         int reg, rc;
78
79         reg = phy_read(phydev, MII_BCM54XX_ECR);
80         if (reg < 0)
81                 return reg;
82
83         /* Mask interrupts globally. */
84         reg |= MII_BCM54XX_ECR_IM;
85         rc = phy_write(phydev, MII_BCM54XX_ECR, reg);
86         if (rc)
87                 return rc;
88
89         /* Unmask events of interest */
90         reg = ~(MII_BCM54XX_INT_DUPLEX |
91                 MII_BCM54XX_INT_SPEED |
92                 MII_BCM54XX_INT_LINK);
93         rc = phy_write(phydev, MII_BCM54XX_IMR, reg);
94         if (rc)
95                 return rc;
96
97         /* Apply AFE settings for the PHY */
98         rc = bcm_cygnus_afe_config(phydev);
99         if (rc)
100                 return rc;
101
102         /* Advertise EEE */
103         rc = bcm_phy_set_eee(phydev, true);
104         if (rc)
105                 return rc;
106
107         /* Enable APD */
108         return bcm_phy_enable_apd(phydev, false);
109 }
110
111 static int bcm_cygnus_resume(struct phy_device *phydev)
112 {
113         int rc;
114
115         genphy_resume(phydev);
116
117         /* Re-initialize the PHY to apply AFE work-arounds and
118          * configurations when coming out of suspend.
119          */
120         rc = bcm_cygnus_config_init(phydev);
121         if (rc)
122                 return rc;
123
124         /* restart auto negotiation with the new settings */
125         return genphy_config_aneg(phydev);
126 }
127
128 static int bcm_omega_config_init(struct phy_device *phydev)
129 {
130         u8 count, rev;
131         int ret = 0;
132
133         rev = phydev->phy_id & ~phydev->drv->phy_id_mask;
134
135         pr_info_once("%s: %s PHY revision: 0x%02x\n",
136                      phydev_name(phydev), phydev->drv->name, rev);
137
138         /* Dummy read to a register to workaround an issue upon reset where the
139          * internal inverter may not allow the first MDIO transaction to pass
140          * the MDIO management controller and make us return 0xffff for such
141          * reads.
142          */
143         phy_read(phydev, MII_BMSR);
144
145         switch (rev) {
146         case 0x00:
147                 ret = bcm_phy_28nm_a0b0_afe_config_init(phydev);
148                 break;
149         default:
150                 break;
151         }
152
153         if (ret)
154                 return ret;
155
156         ret = bcm_phy_downshift_get(phydev, &count);
157         if (ret)
158                 return ret;
159
160         /* Only enable EEE if Wirespeed/downshift is disabled */
161         ret = bcm_phy_set_eee(phydev, count == DOWNSHIFT_DEV_DISABLE);
162         if (ret)
163                 return ret;
164
165         return bcm_phy_enable_apd(phydev, true);
166 }
167
168 static int bcm_omega_resume(struct phy_device *phydev)
169 {
170         int ret;
171
172         /* Re-apply workarounds coming out suspend/resume */
173         ret = bcm_omega_config_init(phydev);
174         if (ret)
175                 return ret;
176
177         /* 28nm Gigabit PHYs come out of reset without any half-duplex
178          * or "hub" compliant advertised mode, fix that. This does not
179          * cause any problems with the PHY library since genphy_config_aneg()
180          * gracefully handles auto-negotiated and forced modes.
181          */
182         return genphy_config_aneg(phydev);
183 }
184
185 static int bcm_omega_get_tunable(struct phy_device *phydev,
186                                  struct ethtool_tunable *tuna, void *data)
187 {
188         switch (tuna->id) {
189         case ETHTOOL_PHY_DOWNSHIFT:
190                 return bcm_phy_downshift_get(phydev, (u8 *)data);
191         default:
192                 return -EOPNOTSUPP;
193         }
194 }
195
196 static int bcm_omega_set_tunable(struct phy_device *phydev,
197                                  struct ethtool_tunable *tuna,
198                                  const void *data)
199 {
200         u8 count = *(u8 *)data;
201         int ret;
202
203         switch (tuna->id) {
204         case ETHTOOL_PHY_DOWNSHIFT:
205                 ret = bcm_phy_downshift_set(phydev, count);
206                 break;
207         default:
208                 return -EOPNOTSUPP;
209         }
210
211         if (ret)
212                 return ret;
213
214         /* Disable EEE advertisement since this prevents the PHY
215          * from successfully linking up, trigger auto-negotiation restart
216          * to let the MAC decide what to do.
217          */
218         ret = bcm_phy_set_eee(phydev, count == DOWNSHIFT_DEV_DISABLE);
219         if (ret)
220                 return ret;
221
222         return genphy_restart_aneg(phydev);
223 }
224
225 static void bcm_omega_get_phy_stats(struct phy_device *phydev,
226                                     struct ethtool_stats *stats, u64 *data)
227 {
228         struct bcm_omega_phy_priv *priv = phydev->priv;
229
230         bcm_phy_get_stats(phydev, priv->stats, stats, data);
231 }
232
233 static int bcm_omega_probe(struct phy_device *phydev)
234 {
235         struct bcm_omega_phy_priv *priv;
236
237         priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
238         if (!priv)
239                 return -ENOMEM;
240
241         phydev->priv = priv;
242
243         priv->stats = devm_kcalloc(&phydev->mdio.dev,
244                                    bcm_phy_get_sset_count(phydev), sizeof(u64),
245                                    GFP_KERNEL);
246         if (!priv->stats)
247                 return -ENOMEM;
248
249         return 0;
250 }
251
252 static struct phy_driver bcm_cygnus_phy_driver[] = {
253 {
254         .phy_id        = PHY_ID_BCM_CYGNUS,
255         .phy_id_mask   = 0xfffffff0,
256         .name          = "Broadcom Cygnus PHY",
257         /* PHY_GBIT_FEATURES */
258         .config_init   = bcm_cygnus_config_init,
259         .config_intr   = bcm_phy_config_intr,
260         .handle_interrupt = bcm_phy_handle_interrupt,
261         .suspend       = genphy_suspend,
262         .resume        = bcm_cygnus_resume,
263 }, {
264         .phy_id         = PHY_ID_BCM_OMEGA,
265         .phy_id_mask    = 0xfffffff0,
266         .name           = "Broadcom Omega Combo GPHY",
267         /* PHY_GBIT_FEATURES */
268         .flags          = PHY_IS_INTERNAL,
269         .config_init    = bcm_omega_config_init,
270         .suspend        = genphy_suspend,
271         .resume         = bcm_omega_resume,
272         .get_tunable    = bcm_omega_get_tunable,
273         .set_tunable    = bcm_omega_set_tunable,
274         .get_sset_count = bcm_phy_get_sset_count,
275         .get_strings    = bcm_phy_get_strings,
276         .get_stats      = bcm_omega_get_phy_stats,
277         .probe          = bcm_omega_probe,
278 }
279 };
280
281 static struct mdio_device_id __maybe_unused bcm_cygnus_phy_tbl[] = {
282         { PHY_ID_BCM_CYGNUS, 0xfffffff0, },
283         { PHY_ID_BCM_OMEGA, 0xfffffff0, },
284         { }
285 };
286 MODULE_DEVICE_TABLE(mdio, bcm_cygnus_phy_tbl);
287
288 module_phy_driver(bcm_cygnus_phy_driver);
289
290 MODULE_DESCRIPTION("Broadcom Cygnus internal PHY driver");
291 MODULE_LICENSE("GPL v2");
292 MODULE_AUTHOR("Broadcom Corporation");