This patch adds swconfig support for ramips_esw:

So far I've developed and tested the patch mostly on the D-LINK DIR-300 B1.
I've made sure that in the enable_vlan=0 case it behaves like a dumb switch,
so external switches should work fine with vlans.

Also I've taken some more care than before to make sure it doesn't change the
pre-existing behaviour in case swconfig is not available or an external switch
is used.

The current state shown by swconfig is always read directly from HW registers,
new settings only show after 'swconfig dev rt305x set apply'.

Note that I have renamed RT305X_ESW_REG_POC[123] to RT305X_ESW_REG_POC[012] to
better match the datasheet (which has a typo on POC0, which probably lead to
the confusing POC[123] naming).

Default on D-LINK DIR-300 B1 after first boot:
|root@OpenWrt:/# swconfig dev rt305x show
|Global attributes:
|        enable_vlan: 1
|Port 0:
|        disable: 0
|        doubletag: 0
|        untag: 1
|        led: 0
|        lan: 1
|        pvid: 1
|        link: port:0 link:down
|Port 1:
|        disable: 0
|        doubletag: 0
|        untag: 1
|        led: 0
|        lan: 1
|        pvid: 1
|        link: port:1 link:down
|Port 2:
|        disable: 0
|        doubletag: 0
|        untag: 1
|        led: 0
|        lan: 1
|        pvid: 1
|        link: port:2 link:down
|Port 3:
|        disable: 0
|        doubletag: 0
|        untag: 1
|        led: 0
|        lan: 1
|        pvid: 1
|        link: port:3 link:up speed:100baseT full-duplex 
|Port 4:
|        disable: 0
|        doubletag: 0
|        untag: 1
|        led: 0
|        lan: 0
|        pvid: 2
|        link: port:4 link:up speed:100baseT full-duplex 
|Port 5:
|        disable: 1
|        doubletag: 0
|        untag: 1
|        led: -1
|        lan: 1
|        pvid: 1
|        link: port:5 link:down
|Port 6:
|        disable: 0
|        doubletag: 0
|        untag: 0
|        led: -1
|        lan: -1
|        pvid: 1
|        link: port:6 link:up speed:1000baseT full-duplex 
|VLAN 1:
|        ports: 0 1 2 3 6t 
|VLAN 2:
|        ports: 4 6t 

With enable_vlan=0:
|Global attributes:
|        enable_vlan: 0
|Port 0:
|        disable: 0
|        doubletag: 0
|        untag: 1
|        led: 0
|        lan: 1
|        pvid: 0
|        link: port:0 link:down
|Port 1:
|        disable: 0
|        doubletag: 0
|        untag: 1
|        led: 0
|        lan: 1
|        pvid: 0
|        link: port:1 link:down
|Port 2:
|        disable: 0
|        doubletag: 0
|        untag: 1
|        led: 0
|        lan: 1
|        pvid: 0
|        link: port:2 link:down
|Port 3:
|        disable: 0
|        doubletag: 0
|        untag: 1
|        led: 0
|        lan: 1
|        pvid: 0
|        link: port:3 link:up speed:100baseT full-duplex 
|Port 4:
|        disable: 0
|        doubletag: 0
|        untag: 1
|        led: 0
|        lan: 0
|        pvid: 0
|        link: port:4 link:up speed:100baseT full-duplex 
|Port 5:
|        disable: 0
|        doubletag: 0
|        untag: 1
|        led: -1
|        lan: 1
|        pvid: 0
|        link: port:5 link:down
|Port 6:
|        disable: 0
|        doubletag: 0
|        untag: 1
|        led: -1
|        lan: -1
|        pvid: 0
|        link: port:6 link:up speed:1000baseT full-duplex 
|VLAN 0:
|        ports: 0 1 2 3 4 5 6 

Closes: https://dev.openwrt.org/ticket/11327

Signed-off-by: Tobias Diedrich <ranma+open...@tdiedrich.de>

Index: target/linux/ramips/files/arch/mips/ralink/rt305x/mach-wl351.c
===================================================================
--- target/linux/ramips/files/arch/mips/ralink/rt305x/mach-wl351.c      
(revision 31538)
+++ target/linux/ramips/files/arch/mips/ralink/rt305x/mach-wl351.c      
(working copy)
@@ -98,7 +98,7 @@
                                     ARRAY_SIZE(wl351_gpio_buttons),
                                     wl351_gpio_buttons);
        // external rtl8366rb
-       rt305x_esw_data.vlan_config = RT305X_ESW_VLAN_CONFIG_BYPASS;
+       rt305x_esw_data.vlan_config = RT305X_ESW_VLAN_CONFIG_NONE;
        rt305x_esw_data.reg_initval_fct2 = 0x0002500c;
        rt305x_esw_data.reg_initval_fpa2 = 0x1f003fff;
        rt305x_register_ethernet();
Index: 
target/linux/ramips/files/arch/mips/include/asm/mach-ralink/rt305x_esw_platform.h
===================================================================
--- 
target/linux/ramips/files/arch/mips/include/asm/mach-ralink/rt305x_esw_platform.h
   (revision 31538)
+++ 
target/linux/ramips/files/arch/mips/include/asm/mach-ralink/rt305x_esw_platform.h
   (working copy)
@@ -13,7 +13,6 @@
 
 enum {
        RT305X_ESW_VLAN_CONFIG_NONE = 0,
-       RT305X_ESW_VLAN_CONFIG_BYPASS,
        RT305X_ESW_VLAN_CONFIG_LLLLW,
        RT305X_ESW_VLAN_CONFIG_WLLLL,
 };
Index: target/linux/ramips/files/drivers/net/ethernet/ramips/Kconfig
===================================================================
--- target/linux/ramips/files/drivers/net/ethernet/ramips/Kconfig       
(revision 31538)
+++ target/linux/ramips/files/drivers/net/ethernet/ramips/Kconfig       
(working copy)
@@ -2,6 +2,7 @@
        tristate "Ralink RT288X/RT3X5X/RT3662/RT3883 ethernet driver"
        depends on MIPS_RALINK
        select PHYLIB if (SOC_RT288X || SOC_RT3883)
+       select SWCONFIG
        help
          This driver supports the etehrnet mac inside the ralink wisocs
 
Index: target/linux/ramips/files/drivers/net/ethernet/ramips/ramips_esw.c
===================================================================
--- target/linux/ramips/files/drivers/net/ethernet/ramips/ramips_esw.c  
(revision 31538)
+++ target/linux/ramips/files/drivers/net/ethernet/ramips/ramips_esw.c  
(working copy)
@@ -1,4 +1,5 @@
 #include <linux/ioport.h>
+#include <linux/switch.h>
 
 #include <rt305x_regs.h>
 #include <rt305x_esw_platform.h>
@@ -8,12 +9,14 @@
 #define RT305X_ESW_REG_PVIDC(_n)       (0x40 + 4 * (_n))
 #define RT305X_ESW_REG_VLANI(_n)       (0x50 + 4 * (_n))
 #define RT305X_ESW_REG_VMSC(_n)                (0x70 + 4 * (_n))
+#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_REG_PCR1            0xc4
 #define RT305X_ESW_REG_FPA2            0xc8
@@ -25,6 +28,23 @@
 #define RT305X_ESW_REG_P3LED   0xb0
 #define RT305X_ESW_REG_P4LED   0xb4
 
+#define RT305X_ESW_LED_LINK            0
+#define RT305X_ESW_LED_100M            1
+#define RT305X_ESW_LED_DUPLEX          2
+#define RT305X_ESW_LED_ACTIVITY                3
+#define RT305X_ESW_LED_COLLISION       4
+#define RT305X_ESW_LED_LINKACT         5
+#define RT305X_ESW_LED_DUPLCOLL                6
+#define RT305X_ESW_LED_10MACT          7
+#define RT305X_ESW_LED_100MACT         8
+// Additional led states not in datasheet:
+#define RT305X_ESW_LED_BLINK           10
+#define RT305X_ESW_LED_ON              12
+
+#define RT305X_ESW_LINK_S      25
+#define RT305X_ESW_DUPLEX_S    9
+#define RT305X_ESW_SPD_S       0
+
 #define RT305X_ESW_PCR0_WT_NWAY_DATA_S 16
 #define RT305X_ESW_PCR0_WT_PHY_CMD     BIT(13)
 #define RT305X_ESW_PCR0_CPU_PHY_REG_S  8
@@ -47,15 +67,27 @@
 #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_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_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
+#define RT305X_ESW_SGC2_LAN_PMAP_M     0x3f
+#define RT305X_ESW_SGC2_LAN_PMAP_S     24
+
+#define RT305X_ESW_PFC1_EN_VLAN_S      16
+#define RT305X_ESW_PFC1_EN_TOS_S       24
+
+#define RT305X_ESW_VLAN_NONE           0xfff
+
 #define RT305X_ESW_PORT0               0
 #define RT305X_ESW_PORT1               1
 #define RT305X_ESW_PORT2               2
@@ -64,6 +96,12 @@
 #define RT305X_ESW_PORT5               5
 #define RT305X_ESW_PORT6               6
 
+#define RT305X_ESW_PORTS_NONE          0
+
+#define RT305X_ESW_PMAP_LLLLL          0x00
+#define RT305X_ESW_PMAP_LLLLW          0x10
+#define RT305X_ESW_PMAP_WLLLL          0x01
+
 #define RT305X_ESW_PORTS_INTERNAL                                      \
                (BIT(RT305X_ESW_PORT0) | BIT(RT305X_ESW_PORT1) |        \
                 BIT(RT305X_ESW_PORT2) | BIT(RT305X_ESW_PORT3) |        \
@@ -78,12 +116,50 @@
                (RT305X_ESW_PORTS_NOCPU | RT305X_ESW_PORTS_CPU)
 
 #define RT305X_ESW_NUM_VLANS   16
+#define RT305X_ESW_NUM_VIDS    4096
 #define RT305X_ESW_NUM_PORTS   7
+#define RT305X_ESW_NUM_LANWAN  6
+#define RT305X_ESW_NUM_LEDS    5
 
+enum {
+       /* global attributes */
+       RT305X_ESW_ATTR_ENABLE_VLAN,
+       /* port attributes */
+       RT305X_ESW_ATTR_PORT_DISABLE,
+       RT305X_ESW_ATTR_PORT_DOUBLETAG,
+       RT305X_ESW_ATTR_PORT_UNTAG,
+       RT305X_ESW_ATTR_PORT_LED,
+       RT305X_ESW_ATTR_PORT_LAN,
+};
+
+struct rt305x_esw_bools {
+       u16     reg;
+       u16     shift;
+};
+
+struct rt305x_esw_port {
+       bool    disable;
+       bool    doubletag;
+       bool    untag;
+       u8      led;
+       u16     pvid;
+};
+
+struct rt305x_esw_vlan {
+       u8      ports;
+       u16     vid;
+};
+
 struct rt305x_esw {
+       struct switch_dev swdev;
        void __iomem *base;
        struct rt305x_esw_platform_data *pdata;
        spinlock_t reg_rw_lock;
+
+       bool enable_vlan;
+       u8 pmap;
+       struct rt305x_esw_vlan vlans[RT305X_ESW_NUM_VLANS];
+       struct rt305x_esw_port ports[RT305X_ESW_NUM_PORTS];
 };
 
 static inline void
@@ -160,6 +236,19 @@
        return ret;
 }
 
+static unsigned
+rt305x_esw_get_vlan_id(struct rt305x_esw *esw, unsigned vlan)
+{
+       unsigned s;
+       unsigned val;
+
+       s = RT305X_ESW_VLANI_VID_S * (vlan % 2);
+       val = rt305x_esw_rr(esw, RT305X_ESW_REG_VLANI(vlan / 2));
+       val = (val >> s) & RT305X_ESW_VLANI_VID_M;
+
+       return val;
+}
+
 static void
 rt305x_esw_set_vlan_id(struct rt305x_esw *esw, unsigned vlan, unsigned vid)
 {
@@ -172,6 +261,17 @@
                       (vid & RT305X_ESW_VLANI_VID_M) << s);
 }
 
+static unsigned
+rt305x_esw_get_pvid(struct rt305x_esw *esw, unsigned port)
+{
+       unsigned s, val;
+
+       s = RT305X_ESW_PVIDC_PVID_S * (port % 2);
+       val = rt305x_esw_rr(esw,
+                      RT305X_ESW_REG_PVIDC(port / 2));
+       return (val >> s) & RT305X_ESW_PVIDC_PVID_M; 
+}
+
 static void
 rt305x_esw_set_pvid(struct rt305x_esw *esw, unsigned port, unsigned pvid)
 {
@@ -184,6 +284,18 @@
                       (pvid & RT305X_ESW_PVIDC_PVID_M) << s);
 }
 
+static unsigned
+rt305x_esw_get_vmsc(struct rt305x_esw *esw, unsigned vlan)
+{
+       unsigned s, val;
+
+       s = RT305X_ESW_VMSC_MSC_S * (vlan % 4);
+       val = rt305x_esw_rr(esw, RT305X_ESW_REG_VMSC(vlan / 4));
+       val = (val >> s) & RT305X_ESW_VMSC_MSC_M;
+
+       return val;
+}
+
 static void
 rt305x_esw_set_vmsc(struct rt305x_esw *esw, unsigned vlan, unsigned msc)
 {
@@ -196,29 +308,39 @@
                       (msc & RT305X_ESW_VMSC_MSC_M) << s);
 }
 
+static int
+rt305x_esw_apply_config(struct switch_dev *dev);
+
 static void
 rt305x_esw_hw_init(struct rt305x_esw *esw)
 {
        int i;
+       u32 sgc2 = 0;
 
        /* vodoo from original driver */
        rt305x_esw_wr(esw, 0xC8A07850, RT305X_ESW_REG_FCT0);
-       rt305x_esw_wr(esw, 0x00000000, RT305X_ESW_REG_SGC2);
-       rt305x_esw_wr(esw, 0x00405555, RT305X_ESW_REG_PFC1);
+       /* port priority 1 for all ports, vlan enabled */
+       rt305x_esw_wr(esw, 0x00005555 |
+                     (RT305X_ESW_PORTS_ALL << RT305X_ESW_PFC1_EN_VLAN_S),
+                     RT305X_ESW_REG_PFC1);
+       /* Enable Aging, and VLAN TAG removal */
+       rt305x_esw_wr(esw,
+                     ((RT305X_ESW_PORTS_ALL << RT305X_ESW_POC2_ENAGING_S) |
+                      (RT305X_ESW_PORTS_NOCPU << RT305X_ESW_POC2_UNTAG_EN_S)),
+                     RT305X_ESW_REG_POC2);
 
        /* 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_wr(esw, esw->pdata->reg_initval_fct2, RT305X_ESW_REG_FCT2);
 
-       rt305x_esw_wr(esw, esw->pdata->reg_initval_fct2, RT305X_ESW_REG_FCT2);
+       /* 300s aging timer, max packet len 1536, broadcast storm prevention 
disabled,
+        * disable collision abort, mac xor48 hash, 10 packet back pressure jam,
+        * GMII disable was_transmit, back pressure disabled, 30ms led flash,
+        * unmatched IGMP as broadcast, rmc tb fault to all ports */
        rt305x_esw_wr(esw, 0x0008a301, RT305X_ESW_REG_SGC);
 
        /* Setup SoC Port control register */
@@ -265,66 +387,525 @@
        /* 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++) {
-               rt305x_esw_set_vlan_id(esw, i, 0);
-               rt305x_esw_set_vmsc(esw, i, 0);
+               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++)
-               rt305x_esw_set_pvid(esw, i, 1);
+       for (i = 0; i < RT305X_ESW_NUM_PORTS; i++) {
+               esw->ports[i].pvid = 1;
+               esw->ports[i].untag = i != RT305X_ESW_PORT6;
+       }
 
        switch (esw->pdata->vlan_config) {
        case RT305X_ESW_VLAN_CONFIG_NONE:
-               break;
-
        case RT305X_ESW_VLAN_CONFIG_BYPASS:
-               /* Pass all vlan tags to all ports */
-               for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) {
-                       rt305x_esw_set_vlan_id(esw, i, i+1);
-                       rt305x_esw_set_vmsc(esw, i, RT305X_ESW_PORTS_ALL);
-               }
-               /* Disable VLAN TAG removal, keep aging on. */
-               rt305x_esw_wr(esw,
-                             RT305X_ESW_PORTS_ALL << RT305X_ESW_POC3_ENAGING_S,
-                             RT305X_ESW_REG_POC3);
+               esw->enable_vlan = 0;
+               esw->pmap = RT305X_ESW_PMAP_LLLLL;
                break;
 
        case RT305X_ESW_VLAN_CONFIG_LLLLW:
-               rt305x_esw_set_vlan_id(esw, 0, 1);
-               rt305x_esw_set_vlan_id(esw, 1, 2);
-               rt305x_esw_set_pvid(esw, RT305X_ESW_PORT4, 2);
-
-               rt305x_esw_set_vmsc(esw, 0,
+               esw->enable_vlan = 1;
+               esw->pmap = RT305X_ESW_PMAP_LLLLW;
+               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));
-               rt305x_esw_set_vmsc(esw, 1,
-                               BIT(RT305X_ESW_PORT4) | BIT(RT305X_ESW_PORT6));
+                               BIT(RT305X_ESW_PORT6);
+               esw->vlans[1].ports =
+                               BIT(RT305X_ESW_PORT4) | BIT(RT305X_ESW_PORT6);
                break;
 
        case RT305X_ESW_VLAN_CONFIG_WLLLL:
-               rt305x_esw_set_vlan_id(esw, 0, 1);
-               rt305x_esw_set_vlan_id(esw, 1, 2);
-               rt305x_esw_set_pvid(esw, RT305X_ESW_PORT0, 2);
-
-               rt305x_esw_set_vmsc(esw, 0,
-                               BIT(RT305X_ESW_PORT1) | BIT(RT305X_ESW_PORT2) |
-                               BIT(RT305X_ESW_PORT3) | BIT(RT305X_ESW_PORT4) |
-                               BIT(RT305X_ESW_PORT6));
-               rt305x_esw_set_vmsc(esw, 1,
-                               BIT(RT305X_ESW_PORT0) | BIT(RT305X_ESW_PORT6));
+               esw->enable_vlan = 1;
+               esw->pmap = RT305X_ESW_PMAP_WLLLL;
+               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();
        }
+
+       rt305x_esw_apply_config(&esw->swdev);
 }
 
 static int
+rt305x_esw_apply_config(struct switch_dev *dev)
+{
+       struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+       int i;
+       u32 sgc2;
+       u8 doubletag = 0;
+       u8 disable = 0;
+       u8 untag = 0;
+
+       for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) {
+               u32 vid, vmsc;
+               if (esw->enable_vlan) {
+                       vid = esw->vlans[i].vid;
+                       vmsc = esw->vlans[i].ports;
+               } else {
+                       vid = RT305X_ESW_VLAN_NONE;
+                       vmsc = RT305X_ESW_PORTS_NONE;
+               }
+               rt305x_esw_set_vlan_id(esw, i, vid);
+               rt305x_esw_set_vmsc(esw, i, vmsc);
+       }
+
+       for (i = 0; i < RT305X_ESW_NUM_PORTS; i++) {
+               u32 pvid;
+               if (esw->enable_vlan) {
+                       doubletag |= esw->ports[i].doubletag << i;
+                       untag     |= esw->ports[i].untag     << i;
+                       disable   |= esw->ports[i].disable   << i;
+                       pvid       = esw->ports[i].pvid;
+               } else {
+                       doubletag |= 1 << i;
+                       untag     |= 1 << i;
+                       pvid       = 0;
+               }
+               rt305x_esw_set_pvid(esw, i, pvid);
+               if (i < RT305X_ESW_NUM_LEDS)
+                       rt305x_esw_wr(esw, esw->ports[i].led,
+                                     RT305X_ESW_REG_P0LED + 4*i);
+       }
+
+       rt305x_esw_rmw(esw, RT305X_ESW_REG_POC0,
+                      RT305X_ESW_POC0_DIS_PORT_M << RT305X_ESW_POC0_DIS_PORT_S,
+                      disable << RT305X_ESW_POC0_DIS_PORT_S);
+       rt305x_esw_rmw(esw, RT305X_ESW_REG_SGC2,
+                      RT305X_ESW_SGC2_DOUBLE_TAG_M << 
RT305X_ESW_SGC2_DOUBLE_TAG_S,
+                      doubletag << RT305X_ESW_SGC2_DOUBLE_TAG_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);
+       /* unused feature, but still nice to be consistent here... */
+       sgc2 = (~esw->pmap & RT305X_ESW_SGC2_LAN_PMAP_M) << 
RT305X_ESW_SGC2_LAN_PMAP_S;
+       rt305x_esw_wr(esw, sgc2, RT305X_ESW_REG_SGC2);
+
+       if (!esw->enable_vlan) {
+               /* Still need to put all ports into vlan 0 or they'll be 
isolated */
+               /* NOTE: vlan 0 is special: No vlan tag is prepended */
+               rt305x_esw_set_vlan_id(esw, 0, 0);
+               rt305x_esw_set_vmsc(esw, 0, RT305X_ESW_PORTS_ALL);
+       }
+
+       return 0;
+}
+
+static int
+rt305x_esw_reset_switch(struct switch_dev *dev)
+{
+       struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+       esw->enable_vlan = 0;
+       memset(esw->ports, 0, sizeof(esw->ports));
+       memset(esw->vlans, 0, sizeof(esw->vlans));
+       rt305x_esw_hw_init(esw);
+
+       return 0;
+}
+
+static int
+rt305x_esw_get_vlan_enable(struct switch_dev *dev,
+                           const struct switch_attr *attr,
+                           struct switch_val *val)
+{
+       struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+
+       val->value.i = esw->enable_vlan;
+
+       return 0;
+}
+
+static int
+rt305x_esw_set_vlan_enable(struct switch_dev *dev,
+                           const struct switch_attr *attr,
+                           struct switch_val *val)
+{
+       struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+
+       esw->enable_vlan = val->value.i != 0;
+
+       return 0;
+}
+
+static int
+rt305x_esw_get_port_link(struct switch_dev *dev,
+                         int port,
+                         struct switch_port_link *link)
+{
+       struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+       u32 speed, poa;
+
+       if (port < 0 || port >= RT305X_ESW_NUM_PORTS)
+               return -EINVAL;
+
+       poa = rt305x_esw_rr(esw, RT305X_ESW_REG_POA) >> port;
+
+       link->link = (poa >> RT305X_ESW_LINK_S) & 1;
+       link->duplex = (poa >> RT305X_ESW_DUPLEX_S) & 1;
+       if (port < RT305X_ESW_NUM_LEDS) {
+               speed = (poa >> RT305X_ESW_SPD_S) & 1;
+       } else {
+               if(port == RT305X_ESW_NUM_PORTS - 1)
+                       poa >>= 1;
+               speed = (poa >> RT305X_ESW_SPD_S) & 3;
+       }
+       switch (speed) {
+       case 0:
+               link->speed = SWITCH_PORT_SPEED_10;
+               break;
+       case 1:
+               link->speed = SWITCH_PORT_SPEED_100;
+               break;
+       case 2:
+       case 3: /* forced gige speed can be 2 or 3 */
+               link->speed = SWITCH_PORT_SPEED_1000;
+               break;
+       default:
+               link->speed = SWITCH_PORT_SPEED_UNKNOWN;
+               break;
+       }
+
+       return 0;
+}
+
+static int
+rt305x_esw_get_port_bool(struct switch_dev *dev,
+                         const struct switch_attr *attr,
+                         struct switch_val *val)
+{
+       struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+       int idx = val->port_vlan;
+       u32 x, reg, shift;
+
+       if (idx < 0 || idx >= RT305X_ESW_NUM_PORTS)
+               return -EINVAL;
+
+       switch (attr->id) {
+       case RT305X_ESW_ATTR_PORT_DISABLE:
+               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_POC2;
+               shift = RT305X_ESW_POC2_UNTAG_EN_S;
+               break;
+       case RT305X_ESW_ATTR_PORT_LAN:
+               reg = RT305X_ESW_REG_SGC2;
+               shift = RT305X_ESW_SGC2_LAN_PMAP_S;
+               if (idx >= RT305X_ESW_NUM_LANWAN) {
+                       val->value.i = -1;
+                       return 0;
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       x = rt305x_esw_rr(esw, reg);
+       val->value.i = (x >> (idx + shift)) & 1;
+
+       return 0;
+}
+
+static int
+rt305x_esw_set_port_bool(struct switch_dev *dev,
+                         const struct switch_attr *attr,
+                         struct switch_val *val)
+{
+       struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+       int idx = val->port_vlan;
+
+       if (idx < 0 || idx >= RT305X_ESW_NUM_PORTS ||
+           val->value.i < 0 || val->value.i > 1)
+               return -EINVAL;
+
+       switch (attr->id) {
+       case RT305X_ESW_ATTR_PORT_DISABLE:
+               esw->ports[idx].disable = val->value.i;
+               break;
+       case RT305X_ESW_ATTR_PORT_DOUBLETAG:
+               esw->ports[idx].doubletag = val->value.i;
+               break;
+       case RT305X_ESW_ATTR_PORT_UNTAG:
+               esw->ports[idx].untag = val->value.i;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int
+rt305x_esw_get_port_led(struct switch_dev *dev,
+                        const struct switch_attr *attr,
+                        struct switch_val *val)
+{
+       struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+       int idx = val->port_vlan;
+
+       if (idx < 0 || idx >= RT305X_ESW_NUM_PORTS)
+               return -EINVAL;
+
+       if (idx >= RT305X_ESW_NUM_LEDS)
+               val->value.i = -1;
+       else
+               val->value.i = rt305x_esw_rr(esw, RT305X_ESW_REG_P0LED + 4*idx);
+
+       return 0;
+}
+
+static int
+rt305x_esw_set_port_led(struct switch_dev *dev,
+                        const struct switch_attr *attr,
+                        struct switch_val *val)
+{
+       struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+       int idx = val->port_vlan;
+
+       if (idx < 0 || idx >= RT305X_ESW_NUM_LEDS)
+               return -EINVAL;
+
+       esw->ports[idx].led = val->value.i;
+
+       return 0;
+}
+
+static int
+rt305x_esw_get_port_pvid(struct switch_dev *dev, int port, int *val)
+{
+       struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+
+       if (port >= RT305X_ESW_NUM_PORTS)
+               return -EINVAL;
+
+       *val = rt305x_esw_get_pvid(esw, port);
+
+       return 0;
+}
+
+static int
+rt305x_esw_set_port_pvid(struct switch_dev *dev, int port, int val)
+{
+       struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+
+       if (port >= RT305X_ESW_NUM_PORTS)
+               return -EINVAL;
+
+       esw->ports[port].pvid = val;
+
+       return 0;
+}
+
+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, poc2;
+       int vlan_idx = -1;
+       int i;
+
+       val->len = 0;
+
+       if (val->port_vlan < 0 || val->port_vlan >= RT305X_ESW_NUM_VIDS)
+               return -EINVAL;
+
+       // valid vlan?
+       for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) {
+               if (rt305x_esw_get_vlan_id(esw, i) == val->port_vlan &&
+                   rt305x_esw_get_vmsc(esw, i) != RT305X_ESW_PORTS_NONE) {
+                       vlan_idx = i;
+                       break;
+               }
+       }
+
+       if (vlan_idx == -1)
+               return -EINVAL;
+
+       vmsc = rt305x_esw_get_vmsc(esw, vlan_idx);
+       poc2 = rt305x_esw_rr(esw, RT305X_ESW_REG_POC2);
+
+       for (i = 0; i < RT305X_ESW_NUM_PORTS; i++) {
+               struct switch_port *p;
+               int port_mask = 1 << i;
+
+               if (!(vmsc & port_mask))
+                       continue;
+
+               p = &val->value.ports[val->len++];
+               p->id = i;
+               if (poc2 & (port_mask << RT305X_ESW_POC2_UNTAG_EN_S))
+                       p->flags = 0;
+               else
+                       p->flags = 1 << SWITCH_PORT_FLAG_TAGGED;
+       }
+
+       return 0;
+}
+
+static int
+rt305x_esw_set_vlan_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+       int ports;
+       int vlan_idx = -1;
+       int i;
+
+       if (val->port_vlan < 0 || val->port_vlan >= RT305X_ESW_NUM_VIDS ||
+           val->len > RT305X_ESW_NUM_PORTS)
+               return -EINVAL;
+
+       // one of the already defined vlans?
+       for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) {
+               if (rt305x_esw_get_vlan_id(esw, i) == val->port_vlan &&
+                   rt305x_esw_get_vmsc(esw, i) != RT305X_ESW_PORTS_NONE) {
+                       vlan_idx = i;
+                       break;
+               }
+       }
+
+       // select a free slot
+       for (i = 0; vlan_idx == -1 && i < RT305X_ESW_NUM_VLANS; i++) {
+               if (rt305x_esw_get_vmsc(esw, i) == RT305X_ESW_PORTS_NONE)
+                       vlan_idx = i;
+       }
+
+       // bail if all slots are in use
+       if (vlan_idx == -1)
+               return -EINVAL;
+
+       ports = RT305X_ESW_PORTS_NONE;
+       for (i = 0; i < val->len; i++) {
+               struct switch_port *p = &val->value.ports[i];
+               int port_mask = 1 << p->id;
+               bool untagged = !(p->flags & (1 << SWITCH_PORT_FLAG_TAGGED));
+
+               if (p->id >= RT305X_ESW_NUM_PORTS)
+                       return -EINVAL;
+
+               ports |= port_mask;
+               esw->ports[p->id].untag = untagged;
+       }
+       esw->vlans[vlan_idx].ports = ports;
+       if (ports == RT305X_ESW_PORTS_NONE)
+               esw->vlans[vlan_idx].vid = RT305X_ESW_VLAN_NONE;
+       else
+               esw->vlans[vlan_idx].vid = val->port_vlan;
+
+       return 0;
+}
+
+static const struct switch_attr rt305x_esw_global[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "VLAN mode (1:enabled)",
+               .max = 1,
+               .id = RT305X_ESW_ATTR_ENABLE_VLAN,
+               .get = rt305x_esw_get_vlan_enable,
+               .set = rt305x_esw_set_vlan_enable,
+       },
+};
+
+static const struct switch_attr rt305x_esw_port[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "disable",
+               .description = "Port state (1:disabled)",
+               .max = 1,
+               .id = RT305X_ESW_ATTR_PORT_DISABLE,
+               .get = rt305x_esw_get_port_bool,
+               .set = rt305x_esw_set_port_bool,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "doubletag",
+               .description = "Double tagging for incoming vlan packets 
(1:enabled)",
+               .max = 1,
+               .id = RT305X_ESW_ATTR_PORT_DOUBLETAG,
+               .get = rt305x_esw_get_port_bool,
+               .set = rt305x_esw_set_port_bool,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "untag",
+               .description = "Untag (1:strip outgoing vlan tag)",
+               .max = 1,
+               .id = RT305X_ESW_ATTR_PORT_UNTAG,
+               .get = rt305x_esw_get_port_bool,
+               .set = rt305x_esw_set_port_bool,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "led",
+               .description = "LED mode (0:link, 1:100m, 2:duplex, 3:activity, 
"
+                              "4:collision, 5:linkact, 6:duplcoll, 7:10mact, "
+                              "8:100mact, 10:blink, 12:on)",
+               .max = 15,
+               .id = RT305X_ESW_ATTR_PORT_LED,
+               .get = rt305x_esw_get_port_led,
+               .set = rt305x_esw_set_port_led,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "lan",
+               .description = "HW port group (0:wan, 1:lan)",
+               .max = 1,
+               .id = RT305X_ESW_ATTR_PORT_LAN,
+               .get = rt305x_esw_get_port_bool,
+       },
+};
+
+static const struct switch_attr rt305x_esw_vlan[] = {
+};
+
+static const struct switch_dev_ops rt305x_esw_ops = {
+       .attr_global = {
+               .attr = rt305x_esw_global,
+               .n_attr = ARRAY_SIZE(rt305x_esw_global),
+       },
+       .attr_port = {
+               .attr = rt305x_esw_port,
+               .n_attr = ARRAY_SIZE(rt305x_esw_port),
+       },
+       .attr_vlan = {
+               .attr = rt305x_esw_vlan,
+               .n_attr = ARRAY_SIZE(rt305x_esw_vlan),
+       },
+       .get_vlan_ports = rt305x_esw_get_vlan_ports,
+       .set_vlan_ports = rt305x_esw_set_vlan_ports,
+       .get_port_pvid = rt305x_esw_get_port_pvid,
+       .set_port_pvid = rt305x_esw_set_port_pvid,
+       .get_port_link = rt305x_esw_get_port_link,
+       .apply_config = rt305x_esw_apply_config,
+       .reset_switch = rt305x_esw_reset_switch,
+};
+
+static int
 rt305x_esw_probe(struct platform_device *pdev)
 {
        struct rt305x_esw_platform_data *pdata;
        struct rt305x_esw *esw;
+       struct switch_dev *swdev;
        struct resource *res;
        int err;
 
@@ -351,6 +932,20 @@
                goto free_esw;
        }
 
+       swdev = &esw->swdev;
+       swdev->name = "rt305x-esw";
+       swdev->alias = "rt305x";
+       swdev->cpu_port = RT305X_ESW_PORT6;
+       swdev->ports = RT305X_ESW_NUM_PORTS;
+       swdev->vlans = RT305X_ESW_NUM_VIDS;
+       swdev->ops = &rt305x_esw_ops;
+
+       err = register_switch(swdev, NULL);
+       if (err < 0) {
+               dev_err(&pdev->dev, "register_switch failed\n");
+               goto free_esw;
+       }
+
        platform_set_drvdata(pdev, esw);
 
        esw->pdata = pdata;
@@ -371,6 +966,7 @@
 
        esw = platform_get_drvdata(pdev);
        if (esw) {
+               unregister_switch(&esw->swdev);
                platform_set_drvdata(pdev, NULL);
                iounmap(esw->base);
                kfree(esw);
Index: target/linux/ramips/image/Makefile
===================================================================
--- target/linux/ramips/image/Makefile  (revision 31538)
+++ target/linux/ramips/image/Makefile  (working copy)
@@ -158,14 +158,14 @@
 mtd_dir300b1_kernel_part_size=917504
 mtd_dir300b1_rootfs_part_size=2949120
 define BuildFirmware/DIR300B1
-       $(call BuildFirmware/Generic,$(1),$(2),board=$(3) $(call 
mkmtd/phys,$(mtdlayout_dir300b1)),$(mtd_dir300b1_kernel_part_size),$(mtd_dir300b1_rootfs_part_size))
+       $(call BuildFirmware/Generic,$(1),$(2),$(call 
mkcmdline,$(3),ttyS1,57600) $(call 
mkmtd/phys,$(mtdlayout_dir300b1)),$(mtd_dir300b1_kernel_part_size),$(mtd_dir300b1_rootfs_part_size))
        mkwrgimg -s $(4) -d /dev/mtdblock/2 \
                -i $(call sysupname,$(1),$(2)) \
                -o $(call imgname,$(1),$(2))-factory.bin
 endef
 
 define BuildFirmware/DIR300B1/initramfs
-       $(call BuildFirmware/Generic/initramfs,$(1),$(2),board=$(3) $(call 
mkmtd/phys,$(mtdlayout_dir300b1)),$(mtd_dir300b1_kernel_part_size),$(mtd_dir300b1_rootfs_part_size))
+       $(call BuildFirmware/Generic/initramfs,$(1),$(2),$(call 
mkcmdline,$(3),ttyS1,57600) $(call 
mkmtd/phys,$(mtdlayout_dir300b1)),$(mtd_dir300b1_kernel_part_size),$(mtd_dir300b1_rootfs_part_size))
 endef
 
 define BuildFirmware/Edimax
Index: target/linux/ramips/base-files/etc/uci-defaults/network
===================================================================
--- target/linux/ramips/base-files/etc/uci-defaults/network     (revision 31538)
+++ target/linux/ramips/base-files/etc/uci-defaults/network     (working copy)
@@ -9,6 +9,31 @@
        return
 fi
 
+ramips_setup_rt3x5x_vlans()
+{
+       if [ ! -x /sbin/swconfig ]; then
+               # legacy default
+               ucidef_set_interfaces_lan_wan "eth0.1" "eth0.2"
+               return
+       fi
+       local wanports=""
+       local lanports=""
+       for port in 5 4 3 2 1 0; do
+               if [ `swconfig dev rt305x port $port get disable` = "1" ]; then
+                       continue
+               fi
+               if [ `swconfig dev rt305x port $port get lan` = "0" ]; then
+                       wanports="$port $wanports"
+               else
+                       lanports="$port $lanports"
+               fi
+       done
+       ucidef_set_interfaces_lan_wan "eth0.1" "eth0.2"
+       ucidef_add_switch "rt305x" "1" "1"
+       ucidef_add_switch_vlan "rt305x" "1" "$lanports 6t"
+       ucidef_add_switch_vlan "rt305x" "2" "$wanports 6t"
+}
+
 ramips_setup_interfaces()
 {
        local board="$1"
@@ -71,7 +96,7 @@
        *)
                RT3X5X=`cat /proc/cpuinfo | grep RT3.5`
                if [ -n "${RT3X5X}" ]; then
-                       ucidef_set_interfaces_lan_wan "eth0.1" "eth0.2"
+                       ramips_setup_rt3x5x_vlans
                else
                        ucidef_set_interfaces_lan_wan "eth0" "eth1"
                fi

_______________________________________________
openwrt-devel mailing list
openwrt-devel@lists.openwrt.org
https://lists.openwrt.org/mailman/listinfo/openwrt-devel

Reply via email to