From 7de1557ce7521e756974d5c28794c2375d28e3cc Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Wed, 24 Sep 2014 17:05:19 -0700 Subject: net: dsa: bcm_sf2: disable RGMII interface(s) when link is down When the link is down, disable the RGMII interface to conserve as much power as possible. We re-enable the RGMII interface whenever the link is detected. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/bcm_sf2.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/net/dsa/bcm_sf2.c') diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index d9b7da545063..58b8fef25b96 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -506,6 +506,15 @@ static void bcm_sf2_sw_adjust_link(struct dsa_switch *ds, int port, port_mode = EXT_REVMII; break; default: + /* All other PHYs: internal and MoCA */ + goto force_link; + } + + /* If the link is down, just disable the interface to conserve power */ + if (!phydev->link) { + reg = reg_readl(priv, REG_RGMII_CNTRL_P(port)); + reg &= ~RGMII_MODE_EN; + reg_writel(priv, reg, REG_RGMII_CNTRL_P(port)); goto force_link; } -- cgit v1.2.3 From b6d045db59210476323caef042c5b50884e4675f Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Wed, 24 Sep 2014 17:05:20 -0700 Subject: net: dsa: bcm_sf2: add port_enable/disable callbacks The SF2 switch driver is already architected around per-port enable/disable callbacks, so we just need a slight update to our existing bcm_sf2_port_setup() resp. bcm_sf2_port_disable() functions to be suitable as callbacks for port_enable/port_disable. We need to shuffle a little the code that does the per-port VLAN configuration/isolation since ports can now be brought up/down separately, so we need to make sure that IMP (CPU, management) port is always included in that specific port setup. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/bcm_sf2.c | 60 +++++++++++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 20 deletions(-) (limited to 'drivers/net/dsa/bcm_sf2.c') diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 58b8fef25b96..634e44ee8d0d 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -135,10 +135,29 @@ static char *bcm_sf2_sw_probe(struct device *host_dev, int sw_addr) return "Broadcom Starfighter 2"; } -static void bcm_sf2_imp_setup(struct dsa_switch *ds, int port) +static void bcm_sf2_imp_vlan_setup(struct dsa_switch *ds, int cpu_port) { struct bcm_sf2_priv *priv = ds_to_priv(ds); unsigned int i; + u32 reg; + + /* Enable the IMP Port to be in the same VLAN as the other ports + * on a per-port basis such that we only have Port i and IMP in + * the same VLAN. + */ + for (i = 0; i < priv->hw_params.num_ports; i++) { + if (!((1 << i) & ds->phys_port_mask)) + continue; + + reg = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(i)); + reg |= (1 << cpu_port); + core_writel(priv, reg, CORE_PORT_VLAN_CTL_PORT(i)); + } +} + +static void bcm_sf2_imp_setup(struct dsa_switch *ds, int port) +{ + struct bcm_sf2_priv *priv = ds_to_priv(ds); u32 reg, val; /* Enable the port memories */ @@ -199,24 +218,13 @@ static void bcm_sf2_imp_setup(struct dsa_switch *ds, int port) reg = core_readl(priv, CORE_STS_OVERRIDE_IMP); reg |= (MII_SW_OR | LINK_STS); core_writel(priv, reg, CORE_STS_OVERRIDE_IMP); - - /* Enable the IMP Port to be in the same VLAN as the other ports - * on a per-port basis such that we only have Port i and IMP in - * the same VLAN. - */ - for (i = 0; i < priv->hw_params.num_ports; i++) { - if (!((1 << i) & ds->phys_port_mask)) - continue; - - reg = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(i)); - reg |= (1 << port); - core_writel(priv, reg, CORE_PORT_VLAN_CTL_PORT(i)); - } } -static void bcm_sf2_port_setup(struct dsa_switch *ds, int port) +static int bcm_sf2_port_setup(struct dsa_switch *ds, int port, + struct phy_device *phy) { struct bcm_sf2_priv *priv = ds_to_priv(ds); + s8 cpu_port = ds->dst[ds->index].cpu_port; u32 reg; /* Clear the memory power down */ @@ -236,9 +244,14 @@ static void bcm_sf2_port_setup(struct dsa_switch *ds, int port) reg &= ~PORT_VLAN_CTRL_MASK; reg |= (1 << port); core_writel(priv, reg, CORE_PORT_VLAN_CTL_PORT(port)); + + bcm_sf2_imp_vlan_setup(ds, cpu_port); + + return 0; } -static void bcm_sf2_port_disable(struct dsa_switch *ds, int port) +static void bcm_sf2_port_disable(struct dsa_switch *ds, int port, + struct phy_device *phy) { struct bcm_sf2_priv *priv = ds_to_priv(ds); u32 off, reg; @@ -246,6 +259,11 @@ static void bcm_sf2_port_disable(struct dsa_switch *ds, int port) if (priv->wol_ports_mask & (1 << port)) return; + if (port == 7) { + intrl2_1_mask_set(priv, P_IRQ_MASK(P7_IRQ_OFF)); + intrl2_1_writel(priv, P_IRQ_MASK(P7_IRQ_OFF), INTRL2_CPU_CLEAR); + } + if (dsa_is_cpu_port(ds, port)) off = CORE_IMP_CTL; else @@ -363,11 +381,11 @@ static int bcm_sf2_sw_setup(struct dsa_switch *ds) for (port = 0; port < priv->hw_params.num_ports; port++) { /* IMP port receives special treatment */ if ((1 << port) & ds->phys_port_mask) - bcm_sf2_port_setup(ds, port); + bcm_sf2_port_setup(ds, port, NULL); else if (dsa_is_cpu_port(ds, port)) bcm_sf2_imp_setup(ds, port); else - bcm_sf2_port_disable(ds, port); + bcm_sf2_port_disable(ds, port, NULL); } /* Include the pseudo-PHY address and the broadcast PHY address to @@ -638,7 +656,7 @@ static int bcm_sf2_sw_suspend(struct dsa_switch *ds) for (port = 0; port < DSA_MAX_PORTS; port++) { if ((1 << port) & ds->phys_port_mask || dsa_is_cpu_port(ds, port)) - bcm_sf2_port_disable(ds, port); + bcm_sf2_port_disable(ds, port, NULL); } return 0; @@ -694,7 +712,7 @@ static int bcm_sf2_sw_resume(struct dsa_switch *ds) for (port = 0; port < DSA_MAX_PORTS; port++) { if ((1 << port) & ds->phys_port_mask) - bcm_sf2_port_setup(ds, port); + bcm_sf2_port_setup(ds, port, NULL); else if (dsa_is_cpu_port(ds, port)) bcm_sf2_imp_setup(ds, port); } @@ -772,6 +790,8 @@ static struct dsa_switch_driver bcm_sf2_switch_driver = { .resume = bcm_sf2_sw_resume, .get_wol = bcm_sf2_sw_get_wol, .set_wol = bcm_sf2_sw_set_wol, + .port_enable = bcm_sf2_port_setup, + .port_disable = bcm_sf2_port_disable, }; static int __init bcm_sf2_init(void) -- cgit v1.2.3 From 450b05c15f9c776996f9627c7b4f1d38b6e6f4a0 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Wed, 24 Sep 2014 17:05:22 -0700 Subject: net: dsa: bcm_sf2: add support for controlling EEE When EEE is enabled, negotiate this feature with the PHY and make sure that the capability checking, local EEE advertisement, link partner EEE advertisement and auto-negotiation resolution returned by phy_init_eee() is positive, and enable EEE at the switch level. While querying the current EEE settings, verify the low-power indication and indicate its status. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/bcm_sf2.c | 73 ++++++++++++++++++++++++++++++++++++++++++ drivers/net/dsa/bcm_sf2.h | 3 ++ drivers/net/dsa/bcm_sf2_regs.h | 3 ++ 3 files changed, 79 insertions(+) (limited to 'drivers/net/dsa/bcm_sf2.c') diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 634e44ee8d0d..b9625968daac 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -220,6 +220,19 @@ static void bcm_sf2_imp_setup(struct dsa_switch *ds, int port) core_writel(priv, reg, CORE_STS_OVERRIDE_IMP); } +static void bcm_sf2_eee_enable_set(struct dsa_switch *ds, int port, bool enable) +{ + struct bcm_sf2_priv *priv = ds_to_priv(ds); + u32 reg; + + reg = core_readl(priv, CORE_EEE_EN_CTRL); + if (enable) + reg |= 1 << port; + else + reg &= ~(1 << port); + core_writel(priv, reg, CORE_EEE_EN_CTRL); +} + static int bcm_sf2_port_setup(struct dsa_switch *ds, int port, struct phy_device *phy) { @@ -247,6 +260,10 @@ static int bcm_sf2_port_setup(struct dsa_switch *ds, int port, bcm_sf2_imp_vlan_setup(ds, cpu_port); + /* If EEE was enabled, restore it */ + if (priv->port_sts[port].eee.eee_enabled) + bcm_sf2_eee_enable_set(ds, port, true); + return 0; } @@ -279,6 +296,60 @@ static void bcm_sf2_port_disable(struct dsa_switch *ds, int port, core_writel(priv, reg, CORE_MEM_PSM_VDD_CTRL); } +/* Returns 0 if EEE was not enabled, or 1 otherwise + */ +static int bcm_sf2_eee_init(struct dsa_switch *ds, int port, + struct phy_device *phy) +{ + struct bcm_sf2_priv *priv = ds_to_priv(ds); + struct ethtool_eee *p = &priv->port_sts[port].eee; + int ret; + + p->supported = (SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full); + + ret = phy_init_eee(phy, 0); + if (ret) + return 0; + + bcm_sf2_eee_enable_set(ds, port, true); + + return 1; +} + +static int bcm_sf2_sw_get_eee(struct dsa_switch *ds, int port, + struct ethtool_eee *e) +{ + struct bcm_sf2_priv *priv = ds_to_priv(ds); + struct ethtool_eee *p = &priv->port_sts[port].eee; + u32 reg; + + reg = core_readl(priv, CORE_EEE_LPI_INDICATE); + e->eee_enabled = p->eee_enabled; + e->eee_active = !!(reg & (1 << port)); + + return 0; +} + +static int bcm_sf2_sw_set_eee(struct dsa_switch *ds, int port, + struct phy_device *phydev, + struct ethtool_eee *e) +{ + struct bcm_sf2_priv *priv = ds_to_priv(ds); + struct ethtool_eee *p = &priv->port_sts[port].eee; + + p->eee_enabled = e->eee_enabled; + + if (!p->eee_enabled) { + bcm_sf2_eee_enable_set(ds, port, false); + } else { + p->eee_enabled = bcm_sf2_eee_init(ds, port, phydev); + if (!p->eee_enabled) + return -EOPNOTSUPP; + } + + return 0; +} + static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id) { struct bcm_sf2_priv *priv = dev_id; @@ -792,6 +863,8 @@ static struct dsa_switch_driver bcm_sf2_switch_driver = { .set_wol = bcm_sf2_sw_set_wol, .port_enable = bcm_sf2_port_setup, .port_disable = bcm_sf2_port_disable, + .get_eee = bcm_sf2_sw_get_eee, + .set_eee = bcm_sf2_sw_set_eee, }; static int __init bcm_sf2_init(void) diff --git a/drivers/net/dsa/bcm_sf2.h b/drivers/net/dsa/bcm_sf2.h index 8fd6c1451a84..ee9f650d5026 100644 --- a/drivers/net/dsa/bcm_sf2.h +++ b/drivers/net/dsa/bcm_sf2.h @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -43,6 +44,8 @@ struct bcm_sf2_hw_params { struct bcm_sf2_port_status { unsigned int link; + + struct ethtool_eee eee; }; struct bcm_sf2_priv { diff --git a/drivers/net/dsa/bcm_sf2_regs.h b/drivers/net/dsa/bcm_sf2_regs.h index c65f138c777f..1bb49cb699ab 100644 --- a/drivers/net/dsa/bcm_sf2_regs.h +++ b/drivers/net/dsa/bcm_sf2_regs.h @@ -225,4 +225,7 @@ #define CORE_PORT_VLAN_CTL_PORT(x) (0xc400 + ((x) * 0x8)) #define PORT_VLAN_CTRL_MASK 0x1ff +#define CORE_EEE_EN_CTRL 0x24800 +#define CORE_EEE_LPI_INDICATE 0x24810 + #endif /* __BCM_SF2_REGS_H */ -- cgit v1.2.3