.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright (C) 2015 Broadcom Corporation |
---|
3 | | - * |
---|
4 | | - * This program is free software; you can redistribute it and/or |
---|
5 | | - * modify it under the terms of the GNU General Public License as |
---|
6 | | - * published by the Free Software Foundation version 2. |
---|
7 | | - * |
---|
8 | | - * This program is distributed "as is" WITHOUT ANY WARRANTY of any |
---|
9 | | - * kind, whether express or implied; without even the implied warranty |
---|
10 | | - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
11 | | - * GNU General Public License for more details. |
---|
12 | 4 | */ |
---|
13 | 5 | |
---|
14 | 6 | /* Broadcom Cygnus SoC internal transceivers support. */ |
---|
.. | .. |
---|
17 | 9 | #include <linux/module.h> |
---|
18 | 10 | #include <linux/netdevice.h> |
---|
19 | 11 | #include <linux/phy.h> |
---|
| 12 | + |
---|
| 13 | +struct bcm_omega_phy_priv { |
---|
| 14 | + u64 *stats; |
---|
| 15 | +}; |
---|
20 | 16 | |
---|
21 | 17 | /* Broadcom Cygnus Phy specific registers */ |
---|
22 | 18 | #define MII_BCM_CYGNUS_AFE_VDAC_ICTRL_0 0x91E5 /* VDAL Control register */ |
---|
.. | .. |
---|
129 | 125 | return genphy_config_aneg(phydev); |
---|
130 | 126 | } |
---|
131 | 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 | + |
---|
132 | 252 | static struct phy_driver bcm_cygnus_phy_driver[] = { |
---|
133 | 253 | { |
---|
134 | 254 | .phy_id = PHY_ID_BCM_CYGNUS, |
---|
135 | 255 | .phy_id_mask = 0xfffffff0, |
---|
136 | 256 | .name = "Broadcom Cygnus PHY", |
---|
137 | | - .features = PHY_GBIT_FEATURES, |
---|
| 257 | + /* PHY_GBIT_FEATURES */ |
---|
138 | 258 | .config_init = bcm_cygnus_config_init, |
---|
139 | 259 | .ack_interrupt = bcm_phy_ack_intr, |
---|
140 | 260 | .config_intr = bcm_phy_config_intr, |
---|
141 | 261 | .suspend = genphy_suspend, |
---|
142 | 262 | .resume = bcm_cygnus_resume, |
---|
143 | | -} }; |
---|
| 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 | +}; |
---|
144 | 280 | |
---|
145 | 281 | static struct mdio_device_id __maybe_unused bcm_cygnus_phy_tbl[] = { |
---|
146 | 282 | { PHY_ID_BCM_CYGNUS, 0xfffffff0, }, |
---|
| 283 | + { PHY_ID_BCM_OMEGA, 0xfffffff0, }, |
---|
147 | 284 | { } |
---|
148 | 285 | }; |
---|
149 | 286 | MODULE_DEVICE_TABLE(mdio, bcm_cygnus_phy_tbl); |
---|