OSDN Git Service

net: dsa: bcm_sf2: Unhardcode port numbers
[sagit-ice-cold/kernel_xiaomi_msm8998.git] / drivers / net / dsa / bcm_sf2.c
index 4f32b8a..6f946fe 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/of.h>
 #include <linux/of_irq.h>
 #include <linux/of_address.h>
+#include <linux/of_net.h>
 #include <net/dsa.h>
 #include <linux/ethtool.h>
 #include <linux/if_bridge.h>
@@ -266,6 +267,50 @@ static void bcm_sf2_gphy_enable_set(struct dsa_switch *ds, bool enable)
        }
 }
 
+static inline void bcm_sf2_port_intr_enable(struct bcm_sf2_priv *priv,
+                                           int port)
+{
+       unsigned int off;
+
+       switch (port) {
+       case 7:
+               off = P7_IRQ_OFF;
+               break;
+       case 0:
+               /* Port 0 interrupts are located on the first bank */
+               intrl2_0_mask_clear(priv, P_IRQ_MASK(P0_IRQ_OFF));
+               return;
+       default:
+               off = P_IRQ_OFF(port);
+               break;
+       }
+
+       intrl2_1_mask_clear(priv, P_IRQ_MASK(off));
+}
+
+static inline void bcm_sf2_port_intr_disable(struct bcm_sf2_priv *priv,
+                                            int port)
+{
+       unsigned int off;
+
+       switch (port) {
+       case 7:
+               off = P7_IRQ_OFF;
+               break;
+       case 0:
+               /* Port 0 interrupts are located on the first bank */
+               intrl2_0_mask_set(priv, P_IRQ_MASK(P0_IRQ_OFF));
+               intrl2_0_writel(priv, P_IRQ_MASK(P0_IRQ_OFF), INTRL2_CPU_CLEAR);
+               return;
+       default:
+               off = P_IRQ_OFF(port);
+               break;
+       }
+
+       intrl2_1_mask_set(priv, P_IRQ_MASK(off));
+       intrl2_1_writel(priv, P_IRQ_MASK(off), INTRL2_CPU_CLEAR);
+}
+
 static int bcm_sf2_port_setup(struct dsa_switch *ds, int port,
                              struct phy_device *phy)
 {
@@ -282,7 +327,7 @@ static int bcm_sf2_port_setup(struct dsa_switch *ds, int port,
        core_writel(priv, 0, CORE_G_PCTL_PORT(port));
 
        /* Re-enable the GPHY and re-apply workarounds */
-       if (port == 0 && priv->hw_params.num_gphy == 1) {
+       if (priv->int_phy_mask & 1 << port && priv->hw_params.num_gphy == 1) {
                bcm_sf2_gphy_enable_set(ds, true);
                if (phy) {
                        /* if phy_stop() has been called before, phy
@@ -299,9 +344,9 @@ static int bcm_sf2_port_setup(struct dsa_switch *ds, int port,
                }
        }
 
-       /* Enable port 7 interrupts to get notified */
-       if (port == 7)
-               intrl2_1_mask_clear(priv, P_IRQ_MASK(P7_IRQ_OFF));
+       /* Enable MoCA port interrupts to get notified */
+       if (port == priv->moca_port)
+               bcm_sf2_port_intr_enable(priv, port);
 
        /* Set this port, and only this one to be in the default VLAN,
         * if member of a bridge, restore its membership prior to
@@ -331,12 +376,10 @@ 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 (port == priv->moca_port)
+               bcm_sf2_port_intr_disable(priv, port);
 
-       if (port == 0 && priv->hw_params.num_gphy == 1)
+       if (priv->int_phy_mask & 1 << port && priv->hw_params.num_gphy == 1)
                bcm_sf2_gphy_enable_set(ds, false);
 
        if (dsa_is_cpu_port(ds, port))
@@ -847,6 +890,42 @@ static void bcm_sf2_intr_disable(struct bcm_sf2_priv *priv)
        intrl2_1_writel(priv, 0, INTRL2_CPU_MASK_CLEAR);
 }
 
+static void bcm_sf2_identify_ports(struct bcm_sf2_priv *priv,
+                                  struct device_node *dn)
+{
+       struct device_node *port;
+       const char *phy_mode_str;
+       int mode;
+       unsigned int port_num;
+       int ret;
+
+       priv->moca_port = -1;
+
+       for_each_available_child_of_node(dn, port) {
+               if (of_property_read_u32(port, "reg", &port_num))
+                       continue;
+
+               /* Internal PHYs get assigned a specific 'phy-mode' property
+                * value: "internal" to help flag them before MDIO probing
+                * has completed, since they might be turned off at that
+                * time
+                */
+               mode = of_get_phy_mode(port);
+               if (mode < 0) {
+                       ret = of_property_read_string(port, "phy-mode",
+                                                     &phy_mode_str);
+                       if (ret < 0)
+                               continue;
+
+                       if (!strcasecmp(phy_mode_str, "internal"))
+                               priv->int_phy_mask |= 1 << port_num;
+               }
+
+               if (mode == PHY_INTERFACE_MODE_MOCA)
+                       priv->moca_port = port_num;
+       }
+}
+
 static int bcm_sf2_sw_setup(struct dsa_switch *ds)
 {
        const char *reg_names[BCM_SF2_REGS_NUM] = BCM_SF2_REGS_NAME;
@@ -865,6 +944,7 @@ static int bcm_sf2_sw_setup(struct dsa_switch *ds)
         * level
         */
        dn = ds->pd->of_node->parent;
+       bcm_sf2_identify_ports(priv, ds->pd->of_node);
 
        priv->irq0 = irq_of_parse_and_map(dn, 0);
        priv->irq1 = irq_of_parse_and_map(dn, 1);
@@ -1145,7 +1225,7 @@ static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port,
 
        status->link = 0;
 
-       /* Port 7 is special as we do not get link status from CORE_LNKSTS,
+       /* MoCA port is special as we do not get link status from CORE_LNKSTS,
         * which means that we need to force the link at the port override
         * level to get the data to flow. We do use what the interrupt handler
         * did determine before.
@@ -1153,7 +1233,7 @@ static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port,
         * For the other ports, we just force the link status, since this is
         * a fixed PHY device.
         */
-       if (port == 7) {
+       if (port == priv->moca_port) {
                status->link = priv->port_sts[port].link;
                /* For MoCA interfaces, also force a link down notification
                 * since some version of the user-space daemon (mocad) use