mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-13 22:56:27 +08:00
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:
commit
fe2c5fb1ef
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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 = {
|
||||
|
Loading…
Reference in New Issue
Block a user