diff options
author | David S. Miller <davem@davemloft.net> | 2015-02-08 01:38:45 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-02-08 01:38:45 -0500 |
commit | 2150f984258e7909327cb90115c8ff41b4e9acb5 (patch) | |
tree | 95f757336beda2400a803524c21a5ea2e2207119 | |
parent | 3c09e92fb6b94b7597442c1f232a260c35b30698 (diff) | |
parent | 9af197a8f6ecab8c22e4ad0167616151fce5a4cb (diff) |
Merge branch 'dsa-next'
Florian Fainelli says:
====================
net: dsa: bcm_sf2: GPHY power down
This patch series implement GPHY power up and down in the SF2 switch
driver in order to conserve power whenever possible (e.g: port is brought
down or unused during Wake-on-LAN).
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/dsa/bcm_sf2.c | 64 | ||||
-rw-r--r-- | drivers/net/dsa/bcm_sf2_regs.h | 4 |
2 files changed, 56 insertions, 12 deletions
diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 09f6b3cc1f66..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 |
@@ -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) |
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 |