| @@ -135,10 +135,29 @@ static char *bcm_sf2_sw_probe(struct device *host_dev, int sw_addr) · 134 unmodified lines |
| 135 | 135 | return "Broadcom Starfighter 2"; |
| 136 | 136 | } |
| 137 | 137 | |
| 138 | | −static void bcm_sf2_imp_setup(struct dsa_switch *ds, int port) |
| 138 | +static void bcm_sf2_imp_vlan_setup(struct dsa_switch *ds, int cpu_port) |
| 139 | 139 | { |
| 140 | 140 | struct bcm_sf2_priv *priv = ds_to_priv(ds); |
| 141 | 141 | unsigned int i; |
| 142 | + u32 reg; |
| 143 | + |
| 144 | + /* Enable the IMP Port to be in the same VLAN as the other ports |
| 145 | + * on a per-port basis such that we only have Port i and IMP in |
| 146 | + * the same VLAN. |
| 147 | + */ |
| 148 | + for (i = 0; i < priv->hw_params.num_ports; i++) { |
| 149 | + if (!((1 << i) & ds->phys_port_mask)) |
| 150 | + continue; |
| 151 | + |
| 152 | + reg = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(i)); |
| 153 | + reg |= (1 << cpu_port); |
| 154 | + core_writel(priv, reg, CORE_PORT_VLAN_CTL_PORT(i)); |
| 155 | + } |
| 156 | +} |
| 157 | + |
| 158 | +static void bcm_sf2_imp_setup(struct dsa_switch *ds, int port) |
| 159 | +{ |
| 160 | + struct bcm_sf2_priv *priv = ds_to_priv(ds); |
| 142 | 161 | u32 reg, val; |
| 143 | 162 | |
| 144 | 163 | /* Enable the port memories */ |
| @@ -199,24 +218,26 @@ static void bcm_sf2_imp_setup(struct dsa_switch *ds, int port) · 54 unmodified lines |
| 199 | 218 | reg = core_readl(priv, CORE_STS_OVERRIDE_IMP); |
| 200 | 219 | reg |= (MII_SW_OR | LINK_STS); |
| 201 | 220 | core_writel(priv, reg, CORE_STS_OVERRIDE_IMP); |
| 221 | +} |
| 202 | 222 | |
| 203 | | − /* Enable the IMP Port to be in the same VLAN as the other ports |
| 204 | | − * on a per-port basis such that we only have Port i and IMP in |
| 205 | | − * the same VLAN. |
| 206 | | − */ |
| 207 | | − for (i = 0; i < priv->hw_params.num_ports; i++) { |
| 208 | | − if (!((1 << i) & ds->phys_port_mask)) |
| 209 | | − continue; |
| 223 | +static void bcm_sf2_eee_enable_set(struct dsa_switch *ds, int port, bool enable) |
| 224 | +{ |
| 225 | + struct bcm_sf2_priv *priv = ds_to_priv(ds); |
| 226 | + u32 reg; |
| 210 | 227 | |
| 211 | | − reg = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(i)); |
| 212 | | − reg |= (1 << port); |
| 213 | | − core_writel(priv, reg, CORE_PORT_VLAN_CTL_PORT(i)); |
| 214 | | − } |
| 228 | + reg = core_readl(priv, CORE_EEE_EN_CTRL); |
| 229 | + if (enable) |
| 230 | + reg |= 1 << port; |
| 231 | + else |
| 232 | + reg &= ~(1 << port); |
| 233 | + core_writel(priv, reg, CORE_EEE_EN_CTRL); |
| 215 | 234 | } |
| 216 | 235 | |
| 217 | | −static void bcm_sf2_port_setup(struct dsa_switch *ds, int port) |
| 236 | +static int bcm_sf2_port_setup(struct dsa_switch *ds, int port, |
| 237 | + struct phy_device *phy) |
| 218 | 238 | { |
| 219 | 239 | struct bcm_sf2_priv *priv = ds_to_priv(ds); |
| 240 | + s8 cpu_port = ds->dst[ds->index].cpu_port; |
| 220 | 241 | u32 reg; |
| 221 | 242 | |
| 222 | 243 | /* Clear the memory power down */ |
| @@ -236,9 +257,18 @@ static void bcm_sf2_port_setup(struct dsa_switch *ds, int port) · 13 unmodified lines |
| 236 | 257 | reg &= ~PORT_VLAN_CTRL_MASK; |
| 237 | 258 | reg |= (1 << port); |
| 238 | 259 | core_writel(priv, reg, CORE_PORT_VLAN_CTL_PORT(port)); |
| 260 | + |
| 261 | + bcm_sf2_imp_vlan_setup(ds, cpu_port); |
| 262 | + |
| 263 | + /* If EEE was enabled, restore it */ |
| 264 | + if (priv->port_sts[port].eee.eee_enabled) |
| 265 | + bcm_sf2_eee_enable_set(ds, port, true); |
| 266 | + |
| 267 | + return 0; |
| 239 | 268 | } |
| 240 | 269 | |
| 241 | | −static void bcm_sf2_port_disable(struct dsa_switch *ds, int port) |
| 270 | +static void bcm_sf2_port_disable(struct dsa_switch *ds, int port, |
| 271 | + struct phy_device *phy) |
| 242 | 272 | { |
| 243 | 273 | struct bcm_sf2_priv *priv = ds_to_priv(ds); |
| 244 | 274 | u32 off, reg; |
| @@ -246,6 +276,11 @@ static void bcm_sf2_port_disable(struct dsa_switch *ds, int port) · 1 unmodified line |
| 246 | 276 | if (priv->wol_ports_mask & (1 << port)) |
| 247 | 277 | return; |
| 248 | 278 | |
| 279 | + if (port == 7) { |
| 280 | + intrl2_1_mask_set(priv, P_IRQ_MASK(P7_IRQ_OFF)); |
| 281 | + intrl2_1_writel(priv, P_IRQ_MASK(P7_IRQ_OFF), INTRL2_CPU_CLEAR); |
| 282 | + } |
| 283 | + |
| 249 | 284 | if (dsa_is_cpu_port(ds, port)) |
| 250 | 285 | off = CORE_IMP_CTL; |
| 251 | 286 | else |
| @@ -261,6 +296,60 @@ static void bcm_sf2_port_disable(struct dsa_switch *ds, int port) · 9 unmodified lines |
| 261 | 296 | core_writel(priv, reg, CORE_MEM_PSM_VDD_CTRL); |
| 262 | 297 | } |
| 263 | 298 | |
| 299 | +/* Returns 0 if EEE was not enabled, or 1 otherwise |
| 300 | + */ |
| 301 | +static int bcm_sf2_eee_init(struct dsa_switch *ds, int port, |
| 302 | + struct phy_device *phy) |
| 303 | +{ |
| 304 | + struct bcm_sf2_priv *priv = ds_to_priv(ds); |
| 305 | + struct ethtool_eee *p = &priv->port_sts[port].eee; |
| 306 | + int ret; |
| 307 | + |
| 308 | + p->supported = (SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full); |
| 309 | + |
| 310 | + ret = phy_init_eee(phy, 0); |
| 311 | + if (ret) |
| 312 | + return 0; |
| 313 | + |
| 314 | + bcm_sf2_eee_enable_set(ds, port, true); |
| 315 | + |
| 316 | + return 1; |
| 317 | +} |
| 318 | + |
| 319 | +static int bcm_sf2_sw_get_eee(struct dsa_switch *ds, int port, |
| 320 | + struct ethtool_eee *e) |
| 321 | +{ |
| 322 | + struct bcm_sf2_priv *priv = ds_to_priv(ds); |
| 323 | + struct ethtool_eee *p = &priv->port_sts[port].eee; |
| 324 | + u32 reg; |
| 325 | + |
| 326 | + reg = core_readl(priv, CORE_EEE_LPI_INDICATE); |
| 327 | + e->eee_enabled = p->eee_enabled; |
| 328 | + e->eee_active = !!(reg & (1 << port)); |
| 329 | + |
| 330 | + return 0; |
| 331 | +} |
| 332 | + |
| 333 | +static int bcm_sf2_sw_set_eee(struct dsa_switch *ds, int port, |
| 334 | + struct phy_device *phydev, |
| 335 | + struct ethtool_eee *e) |
| 336 | +{ |
| 337 | + struct bcm_sf2_priv *priv = ds_to_priv(ds); |
| 338 | + struct ethtool_eee *p = &priv->port_sts[port].eee; |
| 339 | + |
| 340 | + p->eee_enabled = e->eee_enabled; |
| 341 | + |
| 342 | + if (!p->eee_enabled) { |
| 343 | + bcm_sf2_eee_enable_set(ds, port, false); |
| 344 | + } else { |
| 345 | + p->eee_enabled = bcm_sf2_eee_init(ds, port, phydev); |
| 346 | + if (!p->eee_enabled) |
| 347 | + return -EOPNOTSUPP; |
| 348 | + } |
| 349 | + |
| 350 | + return 0; |
| 351 | +} |
| 352 | + |
| 264 | 353 | static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id) |
| 265 | 354 | { |
| 266 | 355 | struct bcm_sf2_priv *priv = dev_id; |
| @@ -363,11 +452,11 @@ static int bcm_sf2_sw_setup(struct dsa_switch *ds) · 96 unmodified lines |
| 363 | 452 | for (port = 0; port < priv->hw_params.num_ports; port++) { |
| 364 | 453 | /* IMP port receives special treatment */ |
| 365 | 454 | if ((1 << port) & ds->phys_port_mask) |
| 366 | | − bcm_sf2_port_setup(ds, port); |
| 455 | + bcm_sf2_port_setup(ds, port, NULL); |
| 367 | 456 | else if (dsa_is_cpu_port(ds, port)) |
| 368 | 457 | bcm_sf2_imp_setup(ds, port); |
| 369 | 458 | else |
| 370 | | − bcm_sf2_port_disable(ds, port); |
| 459 | + bcm_sf2_port_disable(ds, port, NULL); |
| 371 | 460 | } |
| 372 | 461 | |
| 373 | 462 | /* Include the pseudo-PHY address and the broadcast PHY address to |
| @@ -506,6 +595,15 @@ static void bcm_sf2_sw_adjust_link(struct dsa_switch *ds, int port, · 132 unmodified lines |
| 506 | 595 | port_mode = EXT_REVMII; |
| 507 | 596 | break; |
| 508 | 597 | default: |
| 598 | + /* All other PHYs: internal and MoCA */ |
| 599 | + goto force_link; |
| 600 | + } |
| 601 | + |
| 602 | + /* If the link is down, just disable the interface to conserve power */ |
| 603 | + if (!phydev->link) { |
| 604 | + reg = reg_readl(priv, REG_RGMII_CNTRL_P(port)); |
| 605 | + reg &= ~RGMII_MODE_EN; |
| 606 | + reg_writel(priv, reg, REG_RGMII_CNTRL_P(port)); |
| 509 | 607 | goto force_link; |
| 510 | 608 | } |
| 511 | 609 | |
| @@ -629,7 +727,7 @@ static int bcm_sf2_sw_suspend(struct dsa_switch *ds) · 117 unmodified lines |
| 629 | 727 | for (port = 0; port < DSA_MAX_PORTS; port++) { |
| 630 | 728 | if ((1 << port) & ds->phys_port_mask || |
| 631 | 729 | dsa_is_cpu_port(ds, port)) |
| 632 | | − bcm_sf2_port_disable(ds, port); |
| 730 | + bcm_sf2_port_disable(ds, port, NULL); |
| 633 | 731 | } |
| 634 | 732 | |
| 635 | 733 | return 0; |
| @@ -685,7 +783,7 @@ static int bcm_sf2_sw_resume(struct dsa_switch *ds) · 49 unmodified lines |
| 685 | 783 | |
| 686 | 784 | for (port = 0; port < DSA_MAX_PORTS; port++) { |
| 687 | 785 | if ((1 << port) & ds->phys_port_mask) |
| 688 | | − bcm_sf2_port_setup(ds, port); |
| 786 | + bcm_sf2_port_setup(ds, port, NULL); |
| 689 | 787 | else if (dsa_is_cpu_port(ds, port)) |
| 690 | 788 | bcm_sf2_imp_setup(ds, port); |
| 691 | 789 | } |
| @@ -763,6 +861,10 @@ static struct dsa_switch_driver bcm_sf2_switch_driver = { · 71 unmodified lines |
| 763 | 861 | .resume = bcm_sf2_sw_resume, |
| 764 | 862 | .get_wol = bcm_sf2_sw_get_wol, |
| 765 | 863 | .set_wol = bcm_sf2_sw_set_wol, |
| 864 | + .port_enable = bcm_sf2_port_setup, |
| 865 | + .port_disable = bcm_sf2_port_disable, |
| 866 | + .get_eee = bcm_sf2_sw_get_eee, |
| 867 | + .set_eee = bcm_sf2_sw_set_eee, |
| 766 | 868 | }; |
| 767 | 869 | |
| 768 | 870 | static int __init bcm_sf2_init(void) |