net: dsa: bcm_sf2: add suspend/resume callbacks
authorFlorian Fainelli <f.fainelli@gmail.com>
Fri, 19 Sep 2014 00:31:23 +0000 (17:31 -0700)
committerDavid S. Miller <davem@davemloft.net>
Mon, 22 Sep 2014 18:41:23 +0000 (14:41 -0400)
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>
drivers/net/dsa/bcm_sf2.c

index a97ba2548ea59ff0d70176def67ed0e97b34bd4f..29ceee366e44d7b1e86eab77adbfdcabba660ae1 100644 (file)
@@ -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)