#include <linux/platform_data/b53.h>
 #include <linux/phy.h>
 #include <linux/etherdevice.h>
+#include <linux/if_bridge.h>
 #include <net/dsa.h>
 #include <net/switchdev.h>
 
        return b53_write16(dev, B53_JUMBO_PAGE, dev->jumbo_size_reg, max_size);
 }
 
-static int b53_flush_arl(struct b53_device *dev)
+static int b53_flush_arl(struct b53_device *dev, u8 mask)
 {
        unsigned int i;
 
        b53_write8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL,
-                  FAST_AGE_DONE | FAST_AGE_DYNAMIC | FAST_AGE_STATIC);
+                  FAST_AGE_DONE | FAST_AGE_DYNAMIC | mask);
 
        for (i = 0; i < 10; i++) {
                u8 fast_age_ctrl;
        return 0;
 }
 
+static int b53_fast_age_port(struct b53_device *dev, int port)
+{
+       b53_write8(dev, B53_CTRL_PAGE, B53_FAST_AGE_PORT_CTRL, port);
+
+       return b53_flush_arl(dev, FAST_AGE_PORT);
+}
+
+static void b53_imp_vlan_setup(struct dsa_switch *ds, int cpu_port)
+{
+       struct b53_device *dev = ds_to_priv(ds);
+       unsigned int i;
+       u16 pvlan;
+
+       /* Enable the IMP port to be in the same VLAN as the other ports
+        * on a per-port basis such that we only have Port i and IMP in
+        * the same VLAN.
+        */
+       b53_for_each_port(dev, i) {
+               b53_read16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(i), &pvlan);
+               pvlan |= BIT(cpu_port);
+               b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(i), pvlan);
+       }
+}
+
 static int b53_enable_port(struct dsa_switch *ds, int port,
                           struct phy_device *phy)
 {
        struct b53_device *dev = ds_to_priv(ds);
+       unsigned int cpu_port = dev->cpu_port;
+       u16 pvlan;
 
        /* Clear the Rx and Tx disable bits and set to no spanning tree */
        b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(port), 0);
 
+       /* Set this port, and only this one to be in the default VLAN,
+        * if member of a bridge, restore its membership prior to
+        * bringing down this port.
+        */
+       b53_read16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port), &pvlan);
+       pvlan &= ~0x1ff;
+       pvlan |= BIT(port);
+       pvlan |= dev->ports[port].vlan_ctl_mask;
+       b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port), pvlan);
+
+       b53_imp_vlan_setup(ds, cpu_port);
+
        return 0;
 }
 
 
        b53_enable_mib(dev);
 
-       return b53_flush_arl(dev);
+       return b53_flush_arl(dev, FAST_AGE_STATIC);
 }
 
 static int b53_phy_read16(struct dsa_switch *ds, int addr, int reg)
        return 0;
 }
 
+static int b53_br_join(struct dsa_switch *ds, int port,
+                      struct net_device *bridge)
+{
+       struct b53_device *dev = ds_to_priv(ds);
+       u16 pvlan, reg;
+       unsigned int i;
+
+       dev->ports[port].bridge_dev = bridge;
+       b53_read16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port), &pvlan);
+
+       b53_for_each_port(dev, i) {
+               if (dev->ports[i].bridge_dev != bridge)
+                       continue;
+
+               /* Add this local port to the remote port VLAN control
+                * membership and update the remote port bitmask
+                */
+               b53_read16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(i), ®);
+               reg |= BIT(port);
+               b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(i), reg);
+               dev->ports[i].vlan_ctl_mask = reg;
+
+               pvlan |= BIT(i);
+       }
+
+       /* Configure the local port VLAN control membership to include
+        * remote ports and update the local port bitmask
+        */
+       b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port), pvlan);
+       dev->ports[port].vlan_ctl_mask = pvlan;
+
+       return 0;
+}
+
+static void b53_br_leave(struct dsa_switch *ds, int port)
+{
+       struct b53_device *dev = ds_to_priv(ds);
+       struct net_device *bridge = dev->ports[port].bridge_dev;
+       unsigned int i;
+       u16 pvlan, reg;
+
+       b53_read16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port), &pvlan);
+
+       b53_for_each_port(dev, i) {
+               /* Don't touch the remaining ports */
+               if (dev->ports[i].bridge_dev != bridge)
+                       continue;
+
+               b53_read16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(i), ®);
+               reg &= ~BIT(port);
+               b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(i), reg);
+               dev->ports[port].vlan_ctl_mask = reg;
+
+               /* Prevent self removal to preserve isolation */
+               if (port != i)
+                       pvlan &= ~BIT(i);
+       }
+
+       b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port), pvlan);
+       dev->ports[port].vlan_ctl_mask = pvlan;
+       dev->ports[port].bridge_dev = NULL;
+}
+
+static void b53_br_set_stp_state(struct dsa_switch *ds, int port,
+                                u8 state)
+{
+       struct b53_device *dev = ds_to_priv(ds);
+       u8 hw_state, cur_hw_state;
+       u8 reg;
+
+       b53_read8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(port), ®);
+       cur_hw_state = reg & PORT_CTRL_STP_STATE_MASK;
+
+       switch (state) {
+       case BR_STATE_DISABLED:
+               hw_state = PORT_CTRL_DIS_STATE;
+               break;
+       case BR_STATE_LISTENING:
+               hw_state = PORT_CTRL_LISTEN_STATE;
+               break;
+       case BR_STATE_LEARNING:
+               hw_state = PORT_CTRL_LEARN_STATE;
+               break;
+       case BR_STATE_FORWARDING:
+               hw_state = PORT_CTRL_FWD_STATE;
+               break;
+       case BR_STATE_BLOCKING:
+               hw_state = PORT_CTRL_BLOCK_STATE;
+               break;
+       default:
+               dev_err(ds->dev, "invalid STP state: %d\n", state);
+               return;
+       }
+
+       /* Fast-age ARL entries if we are moving a port from Learning or
+        * Forwarding (cur_hw_state) state to Disabled, Blocking or Listening
+        * state (hw_state)
+        */
+       if (cur_hw_state != hw_state) {
+               if (cur_hw_state >= PORT_CTRL_LEARN_STATE &&
+                   hw_state <= PORT_CTRL_LISTEN_STATE) {
+                       if (b53_fast_age_port(dev, port)) {
+                               dev_err(ds->dev, "fast ageing failed\n");
+                               return;
+                       }
+               }
+       }
+
+       b53_read8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(port), ®);
+       reg &= ~PORT_CTRL_STP_STATE_MASK;
+       reg |= hw_state;
+       b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(port), reg);
+}
+
 static struct dsa_switch_driver b53_switch_ops = {
        .tag_protocol           = DSA_TAG_PROTO_NONE,
        .setup                  = b53_setup,
        .adjust_link            = b53_adjust_link,
        .port_enable            = b53_enable_port,
        .port_disable           = b53_disable_port,
+       .port_bridge_join       = b53_br_join,
+       .port_bridge_leave      = b53_br_leave,
+       .port_stp_state_set     = b53_br_set_stp_state,
        .port_fdb_prepare       = b53_fdb_prepare,
        .port_fdb_dump          = b53_fdb_dump,
        .port_fdb_add           = b53_fdb_add,