aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorFlorian Fainelli <f.fainelli@gmail.com>2014-09-18 17:31:23 -0700
committerDavid S. Miller <davem@davemloft.net>2014-09-22 14:41:23 -0400
commit8cfa94984c9cfdd5f5af2af4f161176cb7256d44 (patch)
tree8a41b591a41f652361fde48ca0916da8fd466497 /drivers
parentnet: dsa: allow switch drivers to implement suspend/resume hooks (diff)
downloadlinux-dev-8cfa94984c9cfdd5f5af2af4f161176cb7256d44.tar.xz
linux-dev-8cfa94984c9cfdd5f5af2af4f161176cb7256d44.zip
net: dsa: bcm_sf2: add suspend/resume callbacks
Implement the suspend/resume callbacks for the Broadcom Starfighter 2 switch driver. Suspending the switch requires masking interrupts and shutting down ports. Resuming the switch requires a software reset since we do not know which power-sate we might be coming from, and re-enabling the physical ports that are used. Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/dsa/bcm_sf2.c85
1 files changed, 85 insertions, 0 deletions
diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c
index a97ba2548ea5..29ceee366e44 100644
--- a/drivers/net/dsa/bcm_sf2.c
+++ b/drivers/net/dsa/bcm_sf2.c
@@ -606,6 +606,89 @@ static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port,
status->pause = 1;
}
+static int bcm_sf2_sw_suspend(struct dsa_switch *ds)
+{
+ struct bcm_sf2_priv *priv = ds_to_priv(ds);
+ unsigned int port;
+
+ intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_MASK_SET);
+ intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR);
+ intrl2_0_writel(priv, 0, INTRL2_CPU_MASK_CLEAR);
+ intrl2_1_writel(priv, 0xffffffff, INTRL2_CPU_MASK_SET);
+ intrl2_1_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR);
+ intrl2_1_writel(priv, 0, INTRL2_CPU_MASK_CLEAR);
+
+ /* Disable all ports physically present including the IMP
+ * port, the other ones have already been disabled during
+ * bcm_sf2_sw_setup
+ */
+ 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);
+ }
+
+ return 0;
+}
+
+static int bcm_sf2_sw_rst(struct bcm_sf2_priv *priv)
+{
+ unsigned int timeout = 1000;
+ u32 reg;
+
+ reg = core_readl(priv, CORE_WATCHDOG_CTRL);
+ reg |= SOFTWARE_RESET | EN_CHIP_RST | EN_SW_RESET;
+ core_writel(priv, reg, CORE_WATCHDOG_CTRL);
+
+ do {
+ reg = core_readl(priv, CORE_WATCHDOG_CTRL);
+ if (!(reg & SOFTWARE_RESET))
+ break;
+
+ usleep_range(1000, 2000);
+ } while (timeout-- > 0);
+
+ if (timeout == 0)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int bcm_sf2_sw_resume(struct dsa_switch *ds)
+{
+ struct bcm_sf2_priv *priv = ds_to_priv(ds);
+ unsigned int port;
+ u32 reg;
+ int ret;
+
+ ret = bcm_sf2_sw_rst(priv);
+ if (ret) {
+ pr_err("%s: failed to software reset switch\n", __func__);
+ return ret;
+ }
+
+ /* Reinitialize the single GPHY */
+ if (priv->hw_params.num_gphy == 1) {
+ reg = reg_readl(priv, REG_SPHY_CNTRL);
+ reg |= PHY_RESET;
+ reg &= ~(EXT_PWR_DOWN | IDDQ_BIAS);
+ reg_writel(priv, reg, REG_SPHY_CNTRL);
+ udelay(21);
+ reg = reg_readl(priv, REG_SPHY_CNTRL);
+ reg &= ~PHY_RESET;
+ reg_writel(priv, reg, REG_SPHY_CNTRL);
+ }
+
+ for (port = 0; port < DSA_MAX_PORTS; port++) {
+ if ((1 << port) & ds->phys_port_mask)
+ bcm_sf2_port_setup(ds, port);
+ else if (dsa_is_cpu_port(ds, port))
+ bcm_sf2_imp_setup(ds, port);
+ }
+
+ return 0;
+}
+
static struct dsa_switch_driver bcm_sf2_switch_driver = {
.tag_protocol = DSA_TAG_PROTO_BRCM,
.priv_size = sizeof(struct bcm_sf2_priv),
@@ -620,6 +703,8 @@ static struct dsa_switch_driver bcm_sf2_switch_driver = {
.get_sset_count = bcm_sf2_sw_get_sset_count,
.adjust_link = bcm_sf2_sw_adjust_link,
.fixed_link_update = bcm_sf2_sw_fixed_link_update,
+ .suspend = bcm_sf2_sw_suspend,
+ .resume = bcm_sf2_sw_resume,
};
static int __init bcm_sf2_init(void)