| @@ -135,10 +135,29 @@ static char *bcm_sf2_sw_probe(struct device *host_dev, int sw_addr) · 134 unmodified lines |
| 135 | return "Broadcom Starfighter 2"; | 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 | struct bcm_sf2_priv *priv = ds_to_priv(ds); | 140 | struct bcm_sf2_priv *priv = ds_to_priv(ds); |
| 141 | unsigned int i; | 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 | u32 reg, val; | 161 | u32 reg, val; |
| 143 | | 162 | |
| 144 | /* Enable the port memories */ | 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 | reg = core_readl(priv, CORE_STS_OVERRIDE_IMP); | 218 | reg = core_readl(priv, CORE_STS_OVERRIDE_IMP); |
| 200 | reg |= (MII_SW_OR | LINK_STS); | 219 | reg |= (MII_SW_OR | LINK_STS); |
| 201 | core_writel(priv, reg, CORE_STS_OVERRIDE_IMP); | 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 | 223 | static void bcm_sf2_eee_enable_set(struct dsa_switch *ds, int port, bool enable) |
| 204 | * on a per-port basis such that we only have Port i and IMP in | 224 | { |
| 205 | * the same VLAN. | 225 | struct bcm_sf2_priv *priv = ds_to_priv(ds); |
| 206 | */ | 226 | u32 reg; |
| 207 | for (i = 0; i < priv->hw_params.num_ports; i++) { | | |
| 208 | if (!((1 << i) & ds->phys_port_mask)) | | |
| 209 | continue; | | |
| 210 | | 227 | |
| 211 | reg = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(i)); | 228 | reg = core_readl(priv, CORE_EEE_EN_CTRL); |
| 212 | reg |= (1 << port); | 229 | if (enable) |
| 213 | core_writel(priv, reg, CORE_PORT_VLAN_CTL_PORT(i)); | 230 | reg |= 1 << port; |
| 214 | } | 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 | struct bcm_sf2_priv *priv = ds_to_priv(ds); | 239 | struct bcm_sf2_priv *priv = ds_to_priv(ds); |
| | 240 | s8 cpu_port = ds->dst[ds->index].cpu_port; |
| 220 | u32 reg; | 241 | u32 reg; |
| 221 | | 242 | |
| 222 | /* Clear the memory power down */ | 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 | reg &= ~PORT_VLAN_CTRL_MASK; | 257 | reg &= ~PORT_VLAN_CTRL_MASK; |
| 237 | reg |= (1 << port); | 258 | reg |= (1 << port); |
| 238 | core_writel(priv, reg, CORE_PORT_VLAN_CTL_PORT(port)); | 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 | struct bcm_sf2_priv *priv = ds_to_priv(ds); | 273 | struct bcm_sf2_priv *priv = ds_to_priv(ds); |
| 244 | u32 off, reg; | 274 | u32 off, reg; |
| @@ -246,6 +276,11 @@ static void bcm_sf2_port_disable(struct dsa_switch *ds, int port) · 1 unmodified line |
| 246 | if (priv->wol_ports_mask & (1 << port)) | 276 | if (priv->wol_ports_mask & (1 << port)) |
| 247 | return; | 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 | if (dsa_is_cpu_port(ds, port)) | 284 | if (dsa_is_cpu_port(ds, port)) |
| 250 | off = CORE_IMP_CTL; | 285 | off = CORE_IMP_CTL; |
| 251 | else | 286 | else |
| @@ -261,6 +296,60 @@ static void bcm_sf2_port_disable(struct dsa_switch *ds, int port) · 9 unmodified lines |
| 261 | core_writel(priv, reg, CORE_MEM_PSM_VDD_CTRL); | 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 | static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id) | 353 | static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id) |
| 265 | { | 354 | { |
| 266 | struct bcm_sf2_priv *priv = dev_id; | 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 | for (port = 0; port < priv->hw_params.num_ports; port++) { | 452 | for (port = 0; port < priv->hw_params.num_ports; port++) { |
| 364 | /* IMP port receives special treatment */ | 453 | /* IMP port receives special treatment */ |
| 365 | if ((1 << port) & ds->phys_port_mask) | 454 | if ((1 << port) & ds->phys_port_mask) |
| 366 | bcm_sf2_port_setup(ds, port); | 455 | bcm_sf2_port_setup(ds, port, NULL); |
| 367 | else if (dsa_is_cpu_port(ds, port)) | 456 | else if (dsa_is_cpu_port(ds, port)) |
| 368 | bcm_sf2_imp_setup(ds, port); | 457 | bcm_sf2_imp_setup(ds, port); |
| 369 | else | 458 | else |
| 370 | bcm_sf2_port_disable(ds, port); | 459 | bcm_sf2_port_disable(ds, port, NULL); |
| 371 | } | 460 | } |
| 372 | | 461 | |
| 373 | /* Include the pseudo-PHY address and the broadcast PHY address to | 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 | port_mode = EXT_REVMII; | 595 | port_mode = EXT_REVMII; |
| 507 | break; | 596 | break; |
| 508 | default: | 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 | goto force_link; | 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 | for (port = 0; port < DSA_MAX_PORTS; port++) { | 727 | for (port = 0; port < DSA_MAX_PORTS; port++) { |
| 630 | if ((1 << port) & ds->phys_port_mask || | 728 | if ((1 << port) & ds->phys_port_mask || |
| 631 | dsa_is_cpu_port(ds, port)) | 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 | return 0; | 733 | return 0; |
| @@ -685,7 +783,7 @@ static int bcm_sf2_sw_resume(struct dsa_switch *ds) · 49 unmodified lines |
| 685 | | 783 | |
| 686 | for (port = 0; port < DSA_MAX_PORTS; port++) { | 784 | for (port = 0; port < DSA_MAX_PORTS; port++) { |
| 687 | if ((1 << port) & ds->phys_port_mask) | 785 | if ((1 << port) & ds->phys_port_mask) |
| 688 | bcm_sf2_port_setup(ds, port); | 786 | bcm_sf2_port_setup(ds, port, NULL); |
| 689 | else if (dsa_is_cpu_port(ds, port)) | 787 | else if (dsa_is_cpu_port(ds, port)) |
| 690 | bcm_sf2_imp_setup(ds, port); | 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 | .resume = bcm_sf2_sw_resume, | 861 | .resume = bcm_sf2_sw_resume, |
| 764 | .get_wol = bcm_sf2_sw_get_wol, | 862 | .get_wol = bcm_sf2_sw_get_wol, |
| 765 | .set_wol = bcm_sf2_sw_set_wol, | 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 | static int __init bcm_sf2_init(void) | 870 | static int __init bcm_sf2_init(void) |