Merge branch 'dsa_eee'

Florian Fainelli says:

====================
net: dsa: EEE and other PM features

This patch set allows DSA switch drivers to enable/disable/query EEE on a
per-port level, as well as control precisely which switch ports are
enable/disabled.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2014-09-28 17:14:15 -04:00
commit fe2c5fb1ef
5 changed files with 205 additions and 20 deletions

View File

@ -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,26 +218,28 @@ 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 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)
{
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 */
reg = core_readl(priv, CORE_MEM_PSM_VDD_CTRL);
reg &= ~P_TXQ_PSM_VDD(port);
@ -236,9 +257,18 @@ 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);
/* If EEE was enabled, restore it */
if (priv->port_sts[port].eee.eee_enabled)
bcm_sf2_eee_enable_set(ds, port, true);
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 +276,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
@ -261,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;
@ -363,11 +452,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
@ -506,6 +595,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;
}
@ -629,7 +727,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;
@ -685,7 +783,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);
}
@ -763,6 +861,10 @@ 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,
.get_eee = bcm_sf2_sw_get_eee,
.set_eee = bcm_sf2_sw_set_eee,
};
static int __init bcm_sf2_init(void)

View File

@ -18,6 +18,7 @@
#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <net/dsa.h>
@ -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 {

View File

@ -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 */

View File

@ -224,6 +224,23 @@ struct dsa_switch_driver {
*/
int (*suspend)(struct dsa_switch *ds);
int (*resume)(struct dsa_switch *ds);
/*
* Port enable/disable
*/
int (*port_enable)(struct dsa_switch *ds, int port,
struct phy_device *phy);
void (*port_disable)(struct dsa_switch *ds, int port,
struct phy_device *phy);
/*
* EEE setttings
*/
int (*set_eee)(struct dsa_switch *ds, int port,
struct phy_device *phydev,
struct ethtool_eee *e);
int (*get_eee)(struct dsa_switch *ds, int port,
struct ethtool_eee *e);
};
void register_switch_driver(struct dsa_switch_driver *type);

View File

@ -62,6 +62,7 @@ static int dsa_slave_open(struct net_device *dev)
{
struct dsa_slave_priv *p = netdev_priv(dev);
struct net_device *master = p->parent->dst->master_netdev;
struct dsa_switch *ds = p->parent;
int err;
if (!(master->flags & IFF_UP))
@ -84,8 +85,20 @@ static int dsa_slave_open(struct net_device *dev)
goto clear_allmulti;
}
if (ds->drv->port_enable) {
err = ds->drv->port_enable(ds, p->port, p->phy);
if (err)
goto clear_promisc;
}
if (p->phy)
phy_start(p->phy);
return 0;
clear_promisc:
if (dev->flags & IFF_PROMISC)
dev_set_promiscuity(master, 0);
clear_allmulti:
if (dev->flags & IFF_ALLMULTI)
dev_set_allmulti(master, -1);
@ -100,6 +113,10 @@ static int dsa_slave_close(struct net_device *dev)
{
struct dsa_slave_priv *p = netdev_priv(dev);
struct net_device *master = p->parent->dst->master_netdev;
struct dsa_switch *ds = p->parent;
if (p->phy)
phy_stop(p->phy);
dev_mc_unsync(master, dev);
dev_uc_unsync(master, dev);
@ -111,6 +128,9 @@ static int dsa_slave_close(struct net_device *dev)
if (!ether_addr_equal(dev->dev_addr, master->dev_addr))
dev_uc_del(master, dev->dev_addr);
if (ds->drv->port_disable)
ds->drv->port_disable(ds, p->port, p->phy);
return 0;
}
@ -322,6 +342,44 @@ static int dsa_slave_set_wol(struct net_device *dev, struct ethtool_wolinfo *w)
return ret;
}
static int dsa_slave_set_eee(struct net_device *dev, struct ethtool_eee *e)
{
struct dsa_slave_priv *p = netdev_priv(dev);
struct dsa_switch *ds = p->parent;
int ret;
if (!ds->drv->set_eee)
return -EOPNOTSUPP;
ret = ds->drv->set_eee(ds, p->port, p->phy, e);
if (ret)
return ret;
if (p->phy)
ret = phy_ethtool_set_eee(p->phy, e);
return ret;
}
static int dsa_slave_get_eee(struct net_device *dev, struct ethtool_eee *e)
{
struct dsa_slave_priv *p = netdev_priv(dev);
struct dsa_switch *ds = p->parent;
int ret;
if (!ds->drv->get_eee)
return -EOPNOTSUPP;
ret = ds->drv->get_eee(ds, p->port, e);
if (ret)
return ret;
if (p->phy)
ret = phy_ethtool_get_eee(p->phy, e);
return ret;
}
static const struct ethtool_ops dsa_slave_ethtool_ops = {
.get_settings = dsa_slave_get_settings,
.set_settings = dsa_slave_set_settings,
@ -333,6 +391,8 @@ static const struct ethtool_ops dsa_slave_ethtool_ops = {
.get_sset_count = dsa_slave_get_sset_count,
.set_wol = dsa_slave_set_wol,
.get_wol = dsa_slave_get_wol,
.set_eee = dsa_slave_set_eee,
.get_eee = dsa_slave_get_eee,
};
static const struct net_device_ops dsa_slave_netdev_ops = {