diff options
Diffstat (limited to 'drivers/net/dsa/bcm_sf2.c')
-rw-r--r-- | drivers/net/dsa/bcm_sf2.c | 88 |
1 files changed, 64 insertions, 24 deletions
diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index feb29c4526f7..4daffb284931 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c | |||
@@ -233,6 +233,35 @@ static void bcm_sf2_eee_enable_set(struct dsa_switch *ds, int port, bool enable) | |||
233 | core_writel(priv, reg, CORE_EEE_EN_CTRL); | 233 | core_writel(priv, reg, CORE_EEE_EN_CTRL); |
234 | } | 234 | } |
235 | 235 | ||
236 | static void bcm_sf2_gphy_enable_set(struct dsa_switch *ds, bool enable) | ||
237 | { | ||
238 | struct bcm_sf2_priv *priv = ds_to_priv(ds); | ||
239 | u32 reg; | ||
240 | |||
241 | reg = reg_readl(priv, REG_SPHY_CNTRL); | ||
242 | if (enable) { | ||
243 | reg |= PHY_RESET; | ||
244 | reg &= ~(EXT_PWR_DOWN | IDDQ_BIAS | CK25_DIS); | ||
245 | reg_writel(priv, reg, REG_SPHY_CNTRL); | ||
246 | udelay(21); | ||
247 | reg = reg_readl(priv, REG_SPHY_CNTRL); | ||
248 | reg &= ~PHY_RESET; | ||
249 | } else { | ||
250 | reg |= EXT_PWR_DOWN | IDDQ_BIAS | PHY_RESET; | ||
251 | reg_writel(priv, reg, REG_SPHY_CNTRL); | ||
252 | mdelay(1); | ||
253 | reg |= CK25_DIS; | ||
254 | } | ||
255 | reg_writel(priv, reg, REG_SPHY_CNTRL); | ||
256 | |||
257 | /* Use PHY-driven LED signaling */ | ||
258 | if (!enable) { | ||
259 | reg = reg_readl(priv, REG_LED_CNTRL(0)); | ||
260 | reg |= SPDLNK_SRC_SEL; | ||
261 | reg_writel(priv, reg, REG_LED_CNTRL(0)); | ||
262 | } | ||
263 | } | ||
264 | |||
236 | static int bcm_sf2_port_setup(struct dsa_switch *ds, int port, | 265 | static int bcm_sf2_port_setup(struct dsa_switch *ds, int port, |
237 | struct phy_device *phy) | 266 | struct phy_device *phy) |
238 | { | 267 | { |
@@ -248,6 +277,24 @@ static int bcm_sf2_port_setup(struct dsa_switch *ds, int port, | |||
248 | /* Clear the Rx and Tx disable bits and set to no spanning tree */ | 277 | /* Clear the Rx and Tx disable bits and set to no spanning tree */ |
249 | core_writel(priv, 0, CORE_G_PCTL_PORT(port)); | 278 | core_writel(priv, 0, CORE_G_PCTL_PORT(port)); |
250 | 279 | ||
280 | /* Re-enable the GPHY and re-apply workarounds */ | ||
281 | if (port == 0 && priv->hw_params.num_gphy == 1) { | ||
282 | bcm_sf2_gphy_enable_set(ds, true); | ||
283 | if (phy) { | ||
284 | /* if phy_stop() has been called before, phy | ||
285 | * will be in halted state, and phy_start() | ||
286 | * will call resume. | ||
287 | * | ||
288 | * the resume path does not configure back | ||
289 | * autoneg settings, and since we hard reset | ||
290 | * the phy manually here, we need to reset the | ||
291 | * state machine also. | ||
292 | */ | ||
293 | phy->state = PHY_READY; | ||
294 | phy_init_hw(phy); | ||
295 | } | ||
296 | } | ||
297 | |||
251 | /* Enable port 7 interrupts to get notified */ | 298 | /* Enable port 7 interrupts to get notified */ |
252 | if (port == 7) | 299 | if (port == 7) |
253 | intrl2_1_mask_clear(priv, P_IRQ_MASK(P7_IRQ_OFF)); | 300 | intrl2_1_mask_clear(priv, P_IRQ_MASK(P7_IRQ_OFF)); |
@@ -281,6 +328,9 @@ static void bcm_sf2_port_disable(struct dsa_switch *ds, int port, | |||
281 | intrl2_1_writel(priv, P_IRQ_MASK(P7_IRQ_OFF), INTRL2_CPU_CLEAR); | 328 | intrl2_1_writel(priv, P_IRQ_MASK(P7_IRQ_OFF), INTRL2_CPU_CLEAR); |
282 | } | 329 | } |
283 | 330 | ||
331 | if (port == 0 && priv->hw_params.num_gphy == 1) | ||
332 | bcm_sf2_gphy_enable_set(ds, false); | ||
333 | |||
284 | if (dsa_is_cpu_port(ds, port)) | 334 | if (dsa_is_cpu_port(ds, port)) |
285 | off = CORE_IMP_CTL; | 335 | off = CORE_IMP_CTL; |
286 | else | 336 | else |
@@ -400,6 +450,16 @@ static int bcm_sf2_sw_rst(struct bcm_sf2_priv *priv) | |||
400 | return 0; | 450 | return 0; |
401 | } | 451 | } |
402 | 452 | ||
453 | static void bcm_sf2_intr_disable(struct bcm_sf2_priv *priv) | ||
454 | { | ||
455 | intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_MASK_SET); | ||
456 | intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR); | ||
457 | intrl2_0_writel(priv, 0, INTRL2_CPU_MASK_CLEAR); | ||
458 | intrl2_1_writel(priv, 0xffffffff, INTRL2_CPU_MASK_SET); | ||
459 | intrl2_1_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR); | ||
460 | intrl2_1_writel(priv, 0, INTRL2_CPU_MASK_CLEAR); | ||
461 | } | ||
462 | |||
403 | static int bcm_sf2_sw_setup(struct dsa_switch *ds) | 463 | static int bcm_sf2_sw_setup(struct dsa_switch *ds) |
404 | { | 464 | { |
405 | const char *reg_names[BCM_SF2_REGS_NUM] = BCM_SF2_REGS_NAME; | 465 | const char *reg_names[BCM_SF2_REGS_NUM] = BCM_SF2_REGS_NAME; |
@@ -440,12 +500,7 @@ static int bcm_sf2_sw_setup(struct dsa_switch *ds) | |||
440 | } | 500 | } |
441 | 501 | ||
442 | /* Disable all interrupts and request them */ | 502 | /* Disable all interrupts and request them */ |
443 | intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_MASK_SET); | 503 | bcm_sf2_intr_disable(priv); |
444 | intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR); | ||
445 | intrl2_0_writel(priv, 0, INTRL2_CPU_MASK_CLEAR); | ||
446 | intrl2_1_writel(priv, 0xffffffff, INTRL2_CPU_MASK_SET); | ||
447 | intrl2_1_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR); | ||
448 | intrl2_1_writel(priv, 0, INTRL2_CPU_MASK_CLEAR); | ||
449 | 504 | ||
450 | ret = request_irq(priv->irq0, bcm_sf2_switch_0_isr, 0, | 505 | ret = request_irq(priv->irq0, bcm_sf2_switch_0_isr, 0, |
451 | "switch_0", priv); | 506 | "switch_0", priv); |
@@ -747,12 +802,7 @@ static int bcm_sf2_sw_suspend(struct dsa_switch *ds) | |||
747 | struct bcm_sf2_priv *priv = ds_to_priv(ds); | 802 | struct bcm_sf2_priv *priv = ds_to_priv(ds); |
748 | unsigned int port; | 803 | unsigned int port; |
749 | 804 | ||
750 | intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_MASK_SET); | 805 | bcm_sf2_intr_disable(priv); |
751 | intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR); | ||
752 | intrl2_0_writel(priv, 0, INTRL2_CPU_MASK_CLEAR); | ||
753 | intrl2_1_writel(priv, 0xffffffff, INTRL2_CPU_MASK_SET); | ||
754 | intrl2_1_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR); | ||
755 | intrl2_1_writel(priv, 0, INTRL2_CPU_MASK_CLEAR); | ||
756 | 806 | ||
757 | /* Disable all ports physically present including the IMP | 807 | /* Disable all ports physically present including the IMP |
758 | * port, the other ones have already been disabled during | 808 | * port, the other ones have already been disabled during |
@@ -771,7 +821,6 @@ static int bcm_sf2_sw_resume(struct dsa_switch *ds) | |||
771 | { | 821 | { |
772 | struct bcm_sf2_priv *priv = ds_to_priv(ds); | 822 | struct bcm_sf2_priv *priv = ds_to_priv(ds); |
773 | unsigned int port; | 823 | unsigned int port; |
774 | u32 reg; | ||
775 | int ret; | 824 | int ret; |
776 | 825 | ||
777 | ret = bcm_sf2_sw_rst(priv); | 826 | ret = bcm_sf2_sw_rst(priv); |
@@ -780,17 +829,8 @@ static int bcm_sf2_sw_resume(struct dsa_switch *ds) | |||
780 | return ret; | 829 | return ret; |
781 | } | 830 | } |
782 | 831 | ||
783 | /* Reinitialize the single GPHY */ | 832 | if (priv->hw_params.num_gphy == 1) |
784 | if (priv->hw_params.num_gphy == 1) { | 833 | bcm_sf2_gphy_enable_set(ds, true); |
785 | reg = reg_readl(priv, REG_SPHY_CNTRL); | ||
786 | reg |= PHY_RESET; | ||
787 | reg &= ~(EXT_PWR_DOWN | IDDQ_BIAS); | ||
788 | reg_writel(priv, reg, REG_SPHY_CNTRL); | ||
789 | udelay(21); | ||
790 | reg = reg_readl(priv, REG_SPHY_CNTRL); | ||
791 | reg &= ~PHY_RESET; | ||
792 | reg_writel(priv, reg, REG_SPHY_CNTRL); | ||
793 | } | ||
794 | 834 | ||
795 | for (port = 0; port < DSA_MAX_PORTS; port++) { | 835 | for (port = 0; port < DSA_MAX_PORTS; port++) { |
796 | if ((1 << port) & ds->phys_port_mask) | 836 | if ((1 << port) & ds->phys_port_mask) |