summaryrefslogtreecommitdiffstats
path: root/drivers/net/dsa/bcm_sf2.c
diff options
context:
space:
mode:
authorFlorian Fainelli2014-09-25 02:05:22 +0200
committerDavid S. Miller2014-09-28 23:14:09 +0200
commit450b05c15f9c776996f9627c7b4f1d38b6e6f4a0 (patch)
tree8e447cdbf775bb00d40e69b09cf89e77f333e07a /drivers/net/dsa/bcm_sf2.c
parentnet: dsa: allow switches driver to implement get/set EEE (diff)
downloadkernel-qcow2-linux-450b05c15f9c776996f9627c7b4f1d38b6e6f4a0.tar.gz
kernel-qcow2-linux-450b05c15f9c776996f9627c7b4f1d38b6e6f4a0.tar.xz
kernel-qcow2-linux-450b05c15f9c776996f9627c7b4f1d38b6e6f4a0.zip
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 <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/dsa/bcm_sf2.c')
-rw-r--r--drivers/net/dsa/bcm_sf2.c73
1 files changed, 73 insertions, 0 deletions
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)