|
@@ -18,6 +18,7 @@
|
|
#include <linux/mutex.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/mii.h>
|
|
#include <linux/mii.h>
|
|
#include <linux/phy.h>
|
|
#include <linux/phy.h>
|
|
|
|
+#include <linux/if_bridge.h>
|
|
|
|
|
|
#include "lan9303.h"
|
|
#include "lan9303.h"
|
|
|
|
|
|
@@ -146,6 +147,7 @@
|
|
# define LAN9303_SWE_PORT_STATE_FORWARDING_PORT0 (0)
|
|
# define LAN9303_SWE_PORT_STATE_FORWARDING_PORT0 (0)
|
|
# define LAN9303_SWE_PORT_STATE_LEARNING_PORT0 BIT(1)
|
|
# define LAN9303_SWE_PORT_STATE_LEARNING_PORT0 BIT(1)
|
|
# define LAN9303_SWE_PORT_STATE_BLOCKING_PORT0 BIT(0)
|
|
# define LAN9303_SWE_PORT_STATE_BLOCKING_PORT0 BIT(0)
|
|
|
|
+# define LAN9303_SWE_PORT_STATE_DISABLED_PORT0 (3)
|
|
#define LAN9303_SWE_PORT_MIRROR 0x1846
|
|
#define LAN9303_SWE_PORT_MIRROR 0x1846
|
|
# define LAN9303_SWE_PORT_MIRROR_SNIFF_ALL BIT(8)
|
|
# define LAN9303_SWE_PORT_MIRROR_SNIFF_ALL BIT(8)
|
|
# define LAN9303_SWE_PORT_MIRROR_SNIFFER_PORT2 BIT(7)
|
|
# define LAN9303_SWE_PORT_MIRROR_SNIFFER_PORT2 BIT(7)
|
|
@@ -156,6 +158,7 @@
|
|
# define LAN9303_SWE_PORT_MIRROR_MIRRORED_PORT0 BIT(2)
|
|
# define LAN9303_SWE_PORT_MIRROR_MIRRORED_PORT0 BIT(2)
|
|
# define LAN9303_SWE_PORT_MIRROR_ENABLE_RX_MIRRORING BIT(1)
|
|
# define LAN9303_SWE_PORT_MIRROR_ENABLE_RX_MIRRORING BIT(1)
|
|
# define LAN9303_SWE_PORT_MIRROR_ENABLE_TX_MIRRORING BIT(0)
|
|
# define LAN9303_SWE_PORT_MIRROR_ENABLE_TX_MIRRORING BIT(0)
|
|
|
|
+# define LAN9303_SWE_PORT_MIRROR_DISABLED 0
|
|
#define LAN9303_SWE_INGRESS_PORT_TYPE 0x1847
|
|
#define LAN9303_SWE_INGRESS_PORT_TYPE 0x1847
|
|
#define LAN9303_SWE_INGRESS_PORT_TYPE_VLAN 3
|
|
#define LAN9303_SWE_INGRESS_PORT_TYPE_VLAN 3
|
|
#define LAN9303_BM_CFG 0x1c00
|
|
#define LAN9303_BM_CFG 0x1c00
|
|
@@ -556,6 +559,16 @@ static int lan9303_separate_ports(struct lan9303 *chip)
|
|
LAN9303_SWE_PORT_STATE_BLOCKING_PORT2);
|
|
LAN9303_SWE_PORT_STATE_BLOCKING_PORT2);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void lan9303_bridge_ports(struct lan9303 *chip)
|
|
|
|
+{
|
|
|
|
+ /* ports bridged: remove mirroring */
|
|
|
|
+ lan9303_write_switch_reg(chip, LAN9303_SWE_PORT_MIRROR,
|
|
|
|
+ LAN9303_SWE_PORT_MIRROR_DISABLED);
|
|
|
|
+
|
|
|
|
+ lan9303_write_switch_reg(chip, LAN9303_SWE_PORT_STATE,
|
|
|
|
+ chip->swe_port_state);
|
|
|
|
+}
|
|
|
|
+
|
|
static int lan9303_handle_reset(struct lan9303 *chip)
|
|
static int lan9303_handle_reset(struct lan9303 *chip)
|
|
{
|
|
{
|
|
if (!chip->reset_gpio)
|
|
if (!chip->reset_gpio)
|
|
@@ -844,6 +857,72 @@ static void lan9303_port_disable(struct dsa_switch *ds, int port,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int lan9303_port_bridge_join(struct dsa_switch *ds, int port,
|
|
|
|
+ struct net_device *br)
|
|
|
|
+{
|
|
|
|
+ struct lan9303 *chip = ds->priv;
|
|
|
|
+
|
|
|
|
+ dev_dbg(chip->dev, "%s(port %d)\n", __func__, port);
|
|
|
|
+ if (ds->ports[1].bridge_dev == ds->ports[2].bridge_dev) {
|
|
|
|
+ lan9303_bridge_ports(chip);
|
|
|
|
+ chip->is_bridged = true; /* unleash stp_state_set() */
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void lan9303_port_bridge_leave(struct dsa_switch *ds, int port,
|
|
|
|
+ struct net_device *br)
|
|
|
|
+{
|
|
|
|
+ struct lan9303 *chip = ds->priv;
|
|
|
|
+
|
|
|
|
+ dev_dbg(chip->dev, "%s(port %d)\n", __func__, port);
|
|
|
|
+ if (chip->is_bridged) {
|
|
|
|
+ lan9303_separate_ports(chip);
|
|
|
|
+ chip->is_bridged = false;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void lan9303_port_stp_state_set(struct dsa_switch *ds, int port,
|
|
|
|
+ u8 state)
|
|
|
|
+{
|
|
|
|
+ int portmask, portstate;
|
|
|
|
+ struct lan9303 *chip = ds->priv;
|
|
|
|
+
|
|
|
|
+ dev_dbg(chip->dev, "%s(port %d, state %d)\n",
|
|
|
|
+ __func__, port, state);
|
|
|
|
+
|
|
|
|
+ switch (state) {
|
|
|
|
+ case BR_STATE_DISABLED:
|
|
|
|
+ portstate = LAN9303_SWE_PORT_STATE_DISABLED_PORT0;
|
|
|
|
+ break;
|
|
|
|
+ case BR_STATE_BLOCKING:
|
|
|
|
+ case BR_STATE_LISTENING:
|
|
|
|
+ portstate = LAN9303_SWE_PORT_STATE_BLOCKING_PORT0;
|
|
|
|
+ break;
|
|
|
|
+ case BR_STATE_LEARNING:
|
|
|
|
+ portstate = LAN9303_SWE_PORT_STATE_LEARNING_PORT0;
|
|
|
|
+ break;
|
|
|
|
+ case BR_STATE_FORWARDING:
|
|
|
|
+ portstate = LAN9303_SWE_PORT_STATE_FORWARDING_PORT0;
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ portstate = LAN9303_SWE_PORT_STATE_DISABLED_PORT0;
|
|
|
|
+ dev_err(chip->dev, "unknown stp state: port %d, state %d\n",
|
|
|
|
+ port, state);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ portmask = 0x3 << (port * 2);
|
|
|
|
+ portstate <<= (port * 2);
|
|
|
|
+
|
|
|
|
+ chip->swe_port_state = (chip->swe_port_state & ~portmask) | portstate;
|
|
|
|
+
|
|
|
|
+ if (chip->is_bridged)
|
|
|
|
+ lan9303_write_switch_reg(chip, LAN9303_SWE_PORT_STATE,
|
|
|
|
+ chip->swe_port_state);
|
|
|
|
+ /* else: touching SWE_PORT_STATE would break port separation */
|
|
|
|
+}
|
|
|
|
+
|
|
static const struct dsa_switch_ops lan9303_switch_ops = {
|
|
static const struct dsa_switch_ops lan9303_switch_ops = {
|
|
.get_tag_protocol = lan9303_get_tag_protocol,
|
|
.get_tag_protocol = lan9303_get_tag_protocol,
|
|
.setup = lan9303_setup,
|
|
.setup = lan9303_setup,
|
|
@@ -855,6 +934,9 @@ static const struct dsa_switch_ops lan9303_switch_ops = {
|
|
.get_sset_count = lan9303_get_sset_count,
|
|
.get_sset_count = lan9303_get_sset_count,
|
|
.port_enable = lan9303_port_enable,
|
|
.port_enable = lan9303_port_enable,
|
|
.port_disable = lan9303_port_disable,
|
|
.port_disable = lan9303_port_disable,
|
|
|
|
+ .port_bridge_join = lan9303_port_bridge_join,
|
|
|
|
+ .port_bridge_leave = lan9303_port_bridge_leave,
|
|
|
|
+ .port_stp_state_set = lan9303_port_stp_state_set,
|
|
};
|
|
};
|
|
|
|
|
|
static int lan9303_register_switch(struct lan9303 *chip)
|
|
static int lan9303_register_switch(struct lan9303 *chip)
|