ramips: Power down phy on disabled switch ports
[openwrt/svn-archive/archive.git] / target / linux / ramips / files / drivers / net / ethernet / ramips / ramips_esw.c
index 8a70795e7e0d1b5e899c9e1e9eea16abee170089..309efdeb78dbcafba8c2f8626b522e336c241590 100644 (file)
@@ -1,5 +1,6 @@
 #include <linux/ioport.h>
 #include <linux/switch.h>
+#include <linux/mii.h>
 
 #include <rt305x_regs.h>
 #include <rt305x_esw_platform.h>
@@ -23,9 +24,9 @@
 #define RT305X_ESW_REG_POA             0x80
 #define RT305X_ESW_REG_FPA             0x84
 #define RT305X_ESW_REG_SOCPC           0x8c
-#define RT305X_ESW_REG_POC1            0x90
-#define RT305X_ESW_REG_POC2            0x94
-#define RT305X_ESW_REG_POC3            0x98
+#define RT305X_ESW_REG_POC0            0x90
+#define RT305X_ESW_REG_POC1            0x94
+#define RT305X_ESW_REG_POC2            0x98
 #define RT305X_ESW_REG_SGC             0x9c
 #define RT305X_ESW_REG_STRT            0xa0
 #define RT305X_ESW_REG_PCR0            0xc0
 #define RT305X_ESW_SOCPC_DISBC2CPU_S   16
 #define RT305X_ESW_SOCPC_CRC_PADDING   BIT(25)
 
-#define RT305X_ESW_POC1_EN_BP_S                0
-#define RT305X_ESW_POC1_EN_FC_S                8
-#define RT305X_ESW_POC1_DIS_RMC2CPU_S  16
-#define RT305X_ESW_POC1_DIS_PORT_M     0x7f
-#define RT305X_ESW_POC1_DIS_PORT_S     23
+#define RT305X_ESW_POC0_EN_BP_S                0
+#define RT305X_ESW_POC0_EN_FC_S                8
+#define RT305X_ESW_POC0_DIS_RMC2CPU_S  16
+#define RT305X_ESW_POC0_DIS_PORT_M     0x7f
+#define RT305X_ESW_POC0_DIS_PORT_S     23
 
-#define RT305X_ESW_POC3_UNTAG_EN_M     0xff
-#define RT305X_ESW_POC3_UNTAG_EN_S     0
-#define RT305X_ESW_POC3_ENAGING_S      8
-#define RT305X_ESW_POC3_DIS_UC_PAUSE_S 16
+#define RT305X_ESW_POC2_UNTAG_EN_M     0xff
+#define RT305X_ESW_POC2_UNTAG_EN_S     0
+#define RT305X_ESW_POC2_ENAGING_S      8
+#define RT305X_ESW_POC2_DIS_UC_PAUSE_S 16
 
 #define RT305X_ESW_SGC2_DOUBLE_TAG_M   0x7f
 #define RT305X_ESW_SGC2_DOUBLE_TAG_S   0
@@ -147,7 +148,6 @@ enum {
        /* Port attributes. */
        RT305X_ESW_ATTR_PORT_DISABLE,
        RT305X_ESW_ATTR_PORT_DOUBLETAG,
-       RT305X_ESW_ATTR_PORT_EN_VLAN,
        RT305X_ESW_ATTR_PORT_UNTAG,
        RT305X_ESW_ATTR_PORT_LED,
        RT305X_ESW_ATTR_PORT_LAN,
@@ -159,7 +159,6 @@ struct rt305x_esw_port {
        bool    disable;
        bool    doubletag;
        bool    untag;
-       bool    en_vlan;
        u8      led;
        u16     pvid;
 };
@@ -328,6 +327,57 @@ rt305x_esw_set_vmsc(struct rt305x_esw *esw, unsigned vlan, unsigned msc)
                       (msc & RT305X_ESW_VMSC_MSC_M) << s);
 }
 
+static unsigned
+rt305x_esw_get_port_disable(struct rt305x_esw *esw)
+{
+       unsigned reg;
+       reg = rt305x_esw_rr(esw, RT305X_ESW_REG_POC0);
+       return (reg >> RT305X_ESW_POC0_DIS_PORT_S) &
+              RT305X_ESW_POC0_DIS_PORT_M;
+}
+
+static void
+rt305x_esw_set_port_disable(struct rt305x_esw *esw, unsigned disable_mask)
+{
+       unsigned old_mask;
+       unsigned enable_mask;
+       unsigned changed;
+       int i;
+
+       old_mask = rt305x_esw_get_port_disable(esw);
+       changed = old_mask ^ disable_mask;
+       enable_mask = old_mask & disable_mask;
+
+       /* enable before writing to MII */
+       rt305x_esw_rmw(esw, RT305X_ESW_REG_POC0,
+                      (RT305X_ESW_POC0_DIS_PORT_M <<
+                       RT305X_ESW_POC0_DIS_PORT_S),
+                      enable_mask << RT305X_ESW_POC0_DIS_PORT_S);
+
+       for (i = 0; i < RT305X_ESW_NUM_LEDS; i++) {
+               if (!(changed & (1 << i)))
+                       continue;
+               if (disable_mask & (1 << i)) {
+                       /* disable */
+                       rt305x_mii_write(esw, i, MII_BMCR,
+                                        BMCR_PDOWN);
+               } else {
+                       /* enable */
+                       rt305x_mii_write(esw, i, MII_BMCR,
+                                        BMCR_FULLDPLX |
+                                        BMCR_ANENABLE |
+                                        BMCR_ANRESTART |
+                                        BMCR_SPEED100);
+               }
+       }
+
+       /* disable after writing to MII */
+       rt305x_esw_rmw(esw, RT305X_ESW_REG_POC0,
+                      (RT305X_ESW_POC0_DIS_PORT_M <<
+                       RT305X_ESW_POC0_DIS_PORT_S),
+                      disable_mask << RT305X_ESW_POC0_DIS_PORT_S);
+}
+
 static int
 rt305x_esw_apply_config(struct switch_dev *dev);
 
@@ -335,7 +385,8 @@ static void
 rt305x_esw_hw_init(struct rt305x_esw *esw)
 {
        int i;
-       u8 port_map = 0;
+       u8 port_disable = 0;
+       u8 port_map = RT305X_ESW_PMAP_LLLLLL;
 
        /* vodoo from original driver */
        rt305x_esw_wr(esw, 0xC8A07850, RT305X_ESW_REG_FCT0);
@@ -347,15 +398,15 @@ rt305x_esw_hw_init(struct rt305x_esw *esw)
 
        /* Enable Back Pressure, and Flow Control */
        rt305x_esw_wr(esw,
-                     ((RT305X_ESW_PORTS_ALL << RT305X_ESW_POC1_EN_BP_S) |
-                      (RT305X_ESW_PORTS_ALL << RT305X_ESW_POC1_EN_FC_S)),
-                     RT305X_ESW_REG_POC1);
+                     ((RT305X_ESW_PORTS_ALL << RT305X_ESW_POC0_EN_BP_S) |
+                      (RT305X_ESW_PORTS_ALL << RT305X_ESW_POC0_EN_FC_S)),
+                     RT305X_ESW_REG_POC0);
 
        /* Enable Aging, and VLAN TAG removal */
        rt305x_esw_wr(esw,
-                     ((RT305X_ESW_PORTS_ALL << RT305X_ESW_POC3_ENAGING_S) |
-                      (RT305X_ESW_PORTS_NOCPU << RT305X_ESW_POC3_UNTAG_EN_S)),
-                     RT305X_ESW_REG_POC3);
+                     ((RT305X_ESW_PORTS_ALL << RT305X_ESW_POC2_ENAGING_S) |
+                      (RT305X_ESW_PORTS_NOCPU << RT305X_ESW_POC2_UNTAG_EN_S)),
+                     RT305X_ESW_REG_POC2);
 
        rt305x_esw_wr(esw, esw->pdata->reg_initval_fct2, RT305X_ESW_REG_FCT2);
 
@@ -386,10 +437,21 @@ rt305x_esw_hw_init(struct rt305x_esw *esw)
        rt305x_esw_wr(esw, 0x00000005, RT305X_ESW_REG_P3LED);
        rt305x_esw_wr(esw, 0x00000005, RT305X_ESW_REG_P4LED);
 
+       /* Copy disabled port configuration from bootloader setup */
+       port_disable = rt305x_esw_get_port_disable(esw);
+       for (i = 0; i < 6; i++)
+               esw->ports[i].disable = (port_disable & (1 << i)) != 0;
+
        rt305x_mii_write(esw, 0, 31, 0x8000);
        for (i = 0; i < 5; i++) {
-               /* TX10 waveform coefficient */
-               rt305x_mii_write(esw, i, 0, 0x3100);
+               if (esw->ports[i].disable) {
+                       rt305x_mii_write(esw, i, MII_BMCR, BMCR_PDOWN);
+               } else {
+                       rt305x_mii_write(esw, i, MII_BMCR,
+                                        BMCR_FULLDPLX |
+                                        BMCR_ANENABLE |
+                                        BMCR_SPEED100);
+               }
                /* TX10 waveform coefficient */
                rt305x_mii_write(esw, i, 26, 0x1601);
                /* TX100/TX10 AD/DA current bias */
@@ -412,55 +474,16 @@ rt305x_esw_hw_init(struct rt305x_esw *esw)
        /* select local register */
        rt305x_mii_write(esw, 0, 31, 0x8000);
 
-       /* Set up logical config and apply. */
-       for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) {
-               esw->vlans[i].vid = RT305X_ESW_VLAN_NONE;
-               esw->vlans[i].ports = RT305X_ESW_PORTS_NONE;
-       }
-
-       for (i = 0; i < RT305X_ESW_NUM_PORTS; i++) {
-               esw->ports[i].pvid = 1;
-               esw->ports[i].en_vlan = 1;
-               esw->ports[i].untag = i != RT305X_ESW_PORT6;
-       }
-
        switch (esw->pdata->vlan_config) {
-       case RT305X_ESW_VLAN_CONFIG_BYPASS:
        case RT305X_ESW_VLAN_CONFIG_NONE:
                port_map = RT305X_ESW_PMAP_LLLLLL;
-               esw->global_vlan_enable = 0;
                break;
-
        case RT305X_ESW_VLAN_CONFIG_LLLLW:
                port_map = RT305X_ESW_PMAP_LLLLWL;
-               esw->global_vlan_enable = 1;
-               esw->vlans[0].vid = 1;
-               esw->vlans[1].vid = 2;
-               esw->ports[4].pvid = 2;
-               esw->ports[5].disable = 1;
-               esw->vlans[0].ports =
-                               BIT(RT305X_ESW_PORT0) | BIT(RT305X_ESW_PORT1) |
-                               BIT(RT305X_ESW_PORT2) | BIT(RT305X_ESW_PORT3) |
-                               BIT(RT305X_ESW_PORT6);
-               esw->vlans[1].ports =
-                               BIT(RT305X_ESW_PORT4) | BIT(RT305X_ESW_PORT6);
                break;
-
        case RT305X_ESW_VLAN_CONFIG_WLLLL:
                port_map = RT305X_ESW_PMAP_WLLLLL;
-               esw->global_vlan_enable = 1;
-               esw->vlans[0].vid = 1;
-               esw->vlans[1].vid = 2;
-               esw->ports[0].pvid = 2;
-               esw->ports[5].disable = 1;
-               esw->vlans[0].ports =
-                       BIT(RT305X_ESW_PORT1) | BIT(RT305X_ESW_PORT2) |
-                       BIT(RT305X_ESW_PORT3) | BIT(RT305X_ESW_PORT4) |
-                       BIT(RT305X_ESW_PORT6);
-               esw->vlans[1].ports =
-                               BIT(RT305X_ESW_PORT0) | BIT(RT305X_ESW_PORT6);
                break;
-
        default:
                BUG();
        }
@@ -475,6 +498,7 @@ rt305x_esw_hw_init(struct rt305x_esw *esw)
                       RT305X_ESW_SGC2_LAN_PMAP_M << RT305X_ESW_SGC2_LAN_PMAP_S,
                       port_map << RT305X_ESW_SGC2_LAN_PMAP_S);
 
+       /* Apply the empty config. */
        rt305x_esw_apply_config(&esw->swdev);
 }
 
@@ -506,7 +530,7 @@ rt305x_esw_apply_config(struct switch_dev *dev)
                disable |= esw->ports[i].disable << i;
                if (esw->global_vlan_enable) {
                        doubletag |= esw->ports[i].doubletag << i;
-                       en_vlan   |= esw->ports[i].en_vlan   << i;
+                       en_vlan   |= 1                       << i;
                        untag     |= esw->ports[i].untag     << i;
                        pvid       = esw->ports[i].pvid;
                } else {
@@ -522,9 +546,7 @@ rt305x_esw_apply_config(struct switch_dev *dev)
                                      RT305X_ESW_REG_P0LED + 4*i);
        }
 
-       rt305x_esw_rmw(esw, RT305X_ESW_REG_POC1,
-                      RT305X_ESW_POC1_DIS_PORT_M << RT305X_ESW_POC1_DIS_PORT_S,
-                      disable << RT305X_ESW_POC1_DIS_PORT_S);
+       rt305x_esw_set_port_disable(esw, disable);
        rt305x_esw_rmw(esw, RT305X_ESW_REG_SGC2,
                       (RT305X_ESW_SGC2_DOUBLE_TAG_M <<
                        RT305X_ESW_SGC2_DOUBLE_TAG_S),
@@ -532,9 +554,9 @@ rt305x_esw_apply_config(struct switch_dev *dev)
        rt305x_esw_rmw(esw, RT305X_ESW_REG_PFC1,
                       RT305X_ESW_PFC1_EN_VLAN_M << RT305X_ESW_PFC1_EN_VLAN_S,
                       en_vlan << RT305X_ESW_PFC1_EN_VLAN_S);
-       rt305x_esw_rmw(esw, RT305X_ESW_REG_POC3,
-                      RT305X_ESW_POC3_UNTAG_EN_M << RT305X_ESW_POC3_UNTAG_EN_S,
-                      untag << RT305X_ESW_POC3_UNTAG_EN_S);
+       rt305x_esw_rmw(esw, RT305X_ESW_REG_POC2,
+                      RT305X_ESW_POC2_UNTAG_EN_M << RT305X_ESW_POC2_UNTAG_EN_S,
+                      untag << RT305X_ESW_POC2_UNTAG_EN_S);
 
        if (!esw->global_vlan_enable) {
                /*
@@ -664,20 +686,16 @@ rt305x_esw_get_port_bool(struct switch_dev *dev,
 
        switch (attr->id) {
        case RT305X_ESW_ATTR_PORT_DISABLE:
-               reg = RT305X_ESW_REG_POC1;
-               shift = RT305X_ESW_POC1_DIS_PORT_S;
+               reg = RT305X_ESW_REG_POC0;
+               shift = RT305X_ESW_POC0_DIS_PORT_S;
                break;
        case RT305X_ESW_ATTR_PORT_DOUBLETAG:
                reg = RT305X_ESW_REG_SGC2;
                shift = RT305X_ESW_SGC2_DOUBLE_TAG_S;
                break;
-       case RT305X_ESW_ATTR_PORT_EN_VLAN:
-               reg = RT305X_ESW_REG_PFC1;
-               shift = RT305X_ESW_PFC1_EN_VLAN_S;
-               break;
        case RT305X_ESW_ATTR_PORT_UNTAG:
-               reg = RT305X_ESW_REG_POC3;
-               shift = RT305X_ESW_POC3_UNTAG_EN_S;
+               reg = RT305X_ESW_REG_POC2;
+               shift = RT305X_ESW_POC2_UNTAG_EN_S;
                break;
        case RT305X_ESW_ATTR_PORT_LAN:
                reg = RT305X_ESW_REG_SGC2;
@@ -714,9 +732,6 @@ rt305x_esw_set_port_bool(struct switch_dev *dev,
        case RT305X_ESW_ATTR_PORT_DOUBLETAG:
                esw->ports[idx].doubletag = val->value.i;
                break;
-       case RT305X_ESW_ATTR_PORT_EN_VLAN:
-               esw->ports[idx].en_vlan = val->value.i;
-               break;
        case RT305X_ESW_ATTR_PORT_UNTAG:
                esw->ports[idx].untag = val->value.i;
                break;
@@ -807,7 +822,7 @@ static int
 rt305x_esw_get_vlan_ports(struct switch_dev *dev, struct switch_val *val)
 {
        struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
-       u32 vmsc, poc3;
+       u32 vmsc, poc2;
        int vlan_idx = -1;
        int i;
 
@@ -829,7 +844,7 @@ rt305x_esw_get_vlan_ports(struct switch_dev *dev, struct switch_val *val)
                return -EINVAL;
 
        vmsc = rt305x_esw_get_vmsc(esw, vlan_idx);
-       poc3 = rt305x_esw_rr(esw, RT305X_ESW_REG_POC3);
+       poc2 = rt305x_esw_rr(esw, RT305X_ESW_REG_POC2);
 
        for (i = 0; i < RT305X_ESW_NUM_PORTS; i++) {
                struct switch_port *p;
@@ -840,7 +855,7 @@ rt305x_esw_get_vlan_ports(struct switch_dev *dev, struct switch_val *val)
 
                p = &val->value.ports[val->len++];
                p->id = i;
-               if (poc3 & (port_mask << RT305X_ESW_POC3_UNTAG_EN_S))
+               if (poc2 & (port_mask << RT305X_ESW_POC2_UNTAG_EN_S))
                        p->flags = 0;
                else
                        p->flags = 1 << SWITCH_PORT_FLAG_TAGGED;
@@ -943,15 +958,6 @@ static const struct switch_attr rt305x_esw_port[] = {
                .get = rt305x_esw_get_port_bool,
                .set = rt305x_esw_set_port_bool,
        },
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "en_vlan",
-               .description = "VLAN enabled (1:enabled)",
-               .max = 1,
-               .id = RT305X_ESW_ATTR_PORT_EN_VLAN,
-               .get = rt305x_esw_get_port_bool,
-               .set = rt305x_esw_set_port_bool,
-       },
        {
                .type = SWITCH_TYPE_INT,
                .name = "untag",