add feature gpio
[lede.git] / target / linux / ramips / files / drivers / net / ethernet / ramips / ramips_esw.c
index 19fc3b4b2d8ea878f63929f3973f48604f3a5477..d3150d7a6fc898e42fdfe8b9b555372d2ce255ad 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
@@ -326,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);
 
@@ -333,6 +385,7 @@ static void
 rt305x_esw_hw_init(struct rt305x_esw *esw)
 {
        int i;
+       u8 port_disable = 0;
        u8 port_map = RT305X_ESW_PMAP_LLLLLL;
 
        /* vodoo from original driver */
@@ -345,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);
 
@@ -384,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 */
@@ -434,6 +498,10 @@ 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);
 
+       /* make the switch leds blink */
+       for (i = 0; i < RT305X_ESW_NUM_LEDS; i++)
+               esw->ports[i].led = 0x05;
+
        /* Apply the empty config. */
        rt305x_esw_apply_config(&esw->swdev);
 }
@@ -470,7 +538,7 @@ rt305x_esw_apply_config(struct switch_dev *dev)
                        untag     |= esw->ports[i].untag     << i;
                        pvid       = esw->ports[i].pvid;
                } else {
-                       int x = esw->alt_vlan_disable ? 1 : 0;
+                       int x = esw->alt_vlan_disable ? 0 : 1;
                        doubletag |= x << i;
                        en_vlan   |= x << i;
                        untag     |= x << i;
@@ -482,9 +550,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),
@@ -492,9 +558,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) {
                /*
@@ -624,16 +690,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_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;
@@ -688,11 +754,13 @@ rt305x_esw_get_port_recv_badgood(struct switch_dev *dev,
        struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
        int idx = val->port_vlan;
        int shift = attr->id == RT305X_ESW_ATTR_PORT_RECV_GOOD ? 0 : 16;
+       u32 reg;
 
        if (idx < 0 || idx >= RT305X_ESW_NUM_LANWAN)
                return -EINVAL;
 
-       val->value.i = rt305x_esw_rr(esw, RT305X_ESW_REG_P0PC + 4*idx) >> shift;
+       reg = rt305x_esw_rr(esw, RT305X_ESW_REG_P0PC + 4*idx);
+       val->value.i = (reg >> shift) & 0xffff;
 
        return 0;
 }
@@ -760,7 +828,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;
 
@@ -782,7 +850,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;
@@ -793,7 +861,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;