diff options
author | Florian Fainelli <f.fainelli@gmail.com> | 2015-02-05 14:40:42 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-02-08 01:38:40 -0500 |
commit | 9af197a8f6ecab8c22e4ad0167616151fce5a4cb (patch) | |
tree | 95f757336beda2400a803524c21a5ea2e2207119 /drivers/net | |
parent | b083668c93e5bf889e6ec4761540be1accc3f1b1 (diff) |
net: dsa: bcm_sf2: implement GPHY power down
Implement the power on/off recommended procedure for the Single GPHY we
have on our Starfighter 2 switch. In order to make sure we get proper
LED link/activity signaling during suspend, switch the link indication
from the Switch/MAC to the PHY.
Finally, since the GPHY needs to be reset to be put in low power mode,
we will loose any context applied to it: workarounds, EEE etc.. so we
need to call phy_init_hw() to get our fixups re-applied successfully.
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/dsa/bcm_sf2.c | 50 | ||||
-rw-r--r-- | drivers/net/dsa/bcm_sf2_regs.h | 4 |
2 files changed, 45 insertions, 9 deletions
diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 45c0e2b97f5f..4daffb284931 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c | |||
@@ -238,17 +238,28 @@ static void bcm_sf2_gphy_enable_set(struct dsa_switch *ds, bool enable) | |||
238 | struct bcm_sf2_priv *priv = ds_to_priv(ds); | 238 | struct bcm_sf2_priv *priv = ds_to_priv(ds); |
239 | u32 reg; | 239 | u32 reg; |
240 | 240 | ||
241 | if (!enable) | ||
242 | return; | ||
243 | |||
244 | reg = reg_readl(priv, REG_SPHY_CNTRL); | ||
245 | reg |= PHY_RESET; | ||
246 | reg &= ~(EXT_PWR_DOWN | IDDQ_BIAS); | ||
247 | reg_writel(priv, reg, REG_SPHY_CNTRL); | ||
248 | udelay(21); | ||
249 | reg = reg_readl(priv, REG_SPHY_CNTRL); | 241 | reg = reg_readl(priv, REG_SPHY_CNTRL); |
250 | reg &= ~PHY_RESET; | 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 | } | ||
251 | reg_writel(priv, reg, REG_SPHY_CNTRL); | 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 | } | ||
252 | } | 263 | } |
253 | 264 | ||
254 | 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, |
@@ -266,6 +277,24 @@ static int bcm_sf2_port_setup(struct dsa_switch *ds, int port, | |||
266 | /* 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 */ |
267 | core_writel(priv, 0, CORE_G_PCTL_PORT(port)); | 278 | core_writel(priv, 0, CORE_G_PCTL_PORT(port)); |
268 | 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 | |||
269 | /* Enable port 7 interrupts to get notified */ | 298 | /* Enable port 7 interrupts to get notified */ |
270 | if (port == 7) | 299 | if (port == 7) |
271 | intrl2_1_mask_clear(priv, P_IRQ_MASK(P7_IRQ_OFF)); | 300 | intrl2_1_mask_clear(priv, P_IRQ_MASK(P7_IRQ_OFF)); |
@@ -299,6 +328,9 @@ static void bcm_sf2_port_disable(struct dsa_switch *ds, int port, | |||
299 | 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); |
300 | } | 329 | } |
301 | 330 | ||
331 | if (port == 0 && priv->hw_params.num_gphy == 1) | ||
332 | bcm_sf2_gphy_enable_set(ds, false); | ||
333 | |||
302 | if (dsa_is_cpu_port(ds, port)) | 334 | if (dsa_is_cpu_port(ds, port)) |
303 | off = CORE_IMP_CTL; | 335 | off = CORE_IMP_CTL; |
304 | else | 336 | else |
diff --git a/drivers/net/dsa/bcm_sf2_regs.h b/drivers/net/dsa/bcm_sf2_regs.h index 1bb49cb699ab..cabdfa5e217a 100644 --- a/drivers/net/dsa/bcm_sf2_regs.h +++ b/drivers/net/dsa/bcm_sf2_regs.h | |||
@@ -61,6 +61,10 @@ | |||
61 | #define LPI_COUNT_SHIFT 9 | 61 | #define LPI_COUNT_SHIFT 9 |
62 | #define LPI_COUNT_MASK 0x3F | 62 | #define LPI_COUNT_MASK 0x3F |
63 | 63 | ||
64 | #define REG_LED_CNTRL_BASE 0x90 | ||
65 | #define REG_LED_CNTRL(x) (REG_LED_CNTRL_BASE + (x) * 4) | ||
66 | #define SPDLNK_SRC_SEL (1 << 24) | ||
67 | |||
64 | /* Register set relative to 'INTRL2_0' and 'INTRL2_1' */ | 68 | /* Register set relative to 'INTRL2_0' and 'INTRL2_1' */ |
65 | #define INTRL2_CPU_STATUS 0x00 | 69 | #define INTRL2_CPU_STATUS 0x00 |
66 | #define INTRL2_CPU_SET 0x04 | 70 | #define INTRL2_CPU_SET 0x04 |