Commit 06d3e196 authored by David S. Miller's avatar David S. Miller

Merge branch 'lan9303-Add-basic-offloading-of-unicast-traffic'

Egil Hjelmeland says:

====================
lan9303: Add basic offloading of unicast traffic

This series add basic offloading of unicast traffic to the lan9303
DSA driver.

Review welcome!

Changes v1 -> v2:
 - Patch 1: Codestyle linting.
 - Patch 2: Remember SWE_PORT_STATE while not bridged.
            Added constant LAN9303_SWE_PORT_MIRROR_DISABLED.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 4a269818 d99a86ae
......@@ -18,6 +18,7 @@
#include <linux/mutex.h>
#include <linux/mii.h>
#include <linux/phy.h>
#include <linux/if_bridge.h>
#include "lan9303.h"
......@@ -146,6 +147,7 @@
# define LAN9303_SWE_PORT_STATE_FORWARDING_PORT0 (0)
# define LAN9303_SWE_PORT_STATE_LEARNING_PORT0 BIT(1)
# 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_SNIFF_ALL BIT(8)
# define LAN9303_SWE_PORT_MIRROR_SNIFFER_PORT2 BIT(7)
......@@ -156,7 +158,9 @@
# 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_TX_MIRRORING BIT(0)
# define LAN9303_SWE_PORT_MIRROR_DISABLED 0
#define LAN9303_SWE_INGRESS_PORT_TYPE 0x1847
#define LAN9303_SWE_INGRESS_PORT_TYPE_VLAN 3
#define LAN9303_BM_CFG 0x1c00
#define LAN9303_BM_EGRSS_PORT_TYPE 0x1c0c
# define LAN9303_BM_EGRSS_PORT_TYPE_SPECIAL_TAG_PORT2 (BIT(17) | BIT(16))
......@@ -510,11 +514,30 @@ static int lan9303_enable_processing_port(struct lan9303 *chip,
LAN9303_MAC_TX_CFG_X_TX_ENABLE);
}
/* forward special tagged packets from port 0 to port 1 *or* port 2 */
static int lan9303_setup_tagging(struct lan9303 *chip)
{
int ret;
u32 val;
/* enable defining the destination port via special VLAN tagging
* for port 0
*/
ret = lan9303_write_switch_reg(chip, LAN9303_SWE_INGRESS_PORT_TYPE,
LAN9303_SWE_INGRESS_PORT_TYPE_VLAN);
if (ret)
return ret;
/* tag incoming packets at port 1 and 2 on their way to port 0 to be
* able to discover their source port
*/
val = LAN9303_BM_EGRSS_PORT_TYPE_SPECIAL_TAG_PORT0;
return lan9303_write_switch_reg(chip, LAN9303_BM_EGRSS_PORT_TYPE, val);
}
/* We want a special working switch:
* - do not forward packets between port 1 and 2
* - forward everything from port 1 to port 0
* - forward everything from port 2 to port 0
* - forward special tagged packets from port 0 to port 1 *or* port 2
*/
static int lan9303_separate_ports(struct lan9303 *chip)
{
......@@ -529,22 +552,6 @@ static int lan9303_separate_ports(struct lan9303 *chip)
if (ret)
return ret;
/* enable defining the destination port via special VLAN tagging
* for port 0
*/
ret = lan9303_write_switch_reg(chip, LAN9303_SWE_INGRESS_PORT_TYPE,
0x03);
if (ret)
return ret;
/* tag incoming packets at port 1 and 2 on their way to port 0 to be
* able to discover their source port
*/
ret = lan9303_write_switch_reg(chip, LAN9303_BM_EGRSS_PORT_TYPE,
LAN9303_BM_EGRSS_PORT_TYPE_SPECIAL_TAG_PORT0);
if (ret)
return ret;
/* prevent port 1 and 2 from forwarding packets by their own */
return lan9303_write_switch_reg(chip, LAN9303_SWE_PORT_STATE,
LAN9303_SWE_PORT_STATE_FORWARDING_PORT0 |
......@@ -552,6 +559,16 @@ static int lan9303_separate_ports(struct lan9303 *chip)
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)
{
if (!chip->reset_gpio)
......@@ -644,6 +661,10 @@ static int lan9303_setup(struct dsa_switch *ds)
return -EINVAL;
}
ret = lan9303_setup_tagging(chip);
if (ret)
dev_err(chip->dev, "failed to setup port tagging %d\n", ret);
ret = lan9303_separate_ports(chip);
if (ret)
dev_err(chip->dev, "failed to separate ports %d\n", ret);
......@@ -836,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 = {
.get_tag_protocol = lan9303_get_tag_protocol,
.setup = lan9303_setup,
......@@ -847,6 +934,9 @@ static const struct dsa_switch_ops lan9303_switch_ops = {
.get_sset_count = lan9303_get_sset_count,
.port_enable = lan9303_port_enable,
.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)
......
......@@ -21,6 +21,8 @@ struct lan9303 {
struct dsa_switch *ds;
struct mutex indirect_mutex; /* protect indexed register access */
const struct lan9303_phy_ops *ops;
bool is_bridged; /* true if port 1 and 2 are bridged */
u32 swe_port_state; /* remember SWE_PORT_STATE while not bridged */
};
extern const struct regmap_access_table lan9303_register_set;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment