>From TI am335x Reference Manual: 14.3.2.10.2 Dual Mac Mode When operating in dual mac mode the intention is to transfer packets between ports 0 and 1 and ports 0 and 2, but not between ports 1 and 2. Each CPGMAC_SL appears as a single MAC with no bridging between MAC’s. Each CPGMAC_SL has at least one unique (not the same) mac address. Dual mac mode is configured as described below: - Set the ale_vlan_aware bit in the ALE_Control register. This bit configures the ALE to process in vlan aware mode. With X can be Port 1 or Port 2: - Configure the Port X to Port 0 VLAN. Add a VLAN Table Entry with ports 0 and X as members (clear the flood masks). Add a VLAN/Unicast Address Table Entry with the PortX/0 VLAN and a port number of 0. - Select the dual mac mode on the port 0 FIFO by setting tx_in_sel[1:0] = 01 in P0_Tx_In_Ctl. Signed-off-by: Michael Grzeschik <m.grzeschik@xxxxxxxxxxxxxx> --- drivers/net/cpsw.c | 99 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 89 insertions(+), 10 deletions(-) diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c index aabfc76ad6..7cb530570f 100644 --- a/drivers/net/cpsw.c +++ b/drivers/net/cpsw.c @@ -164,10 +164,12 @@ enum cpsw_ale_port_state { /* ALE unicast entry flags - passed into cpsw_ale_add_ucast() */ #define ALE_SECURE 1 #define ALE_BLOCKED 2 +#define ALE_VLAN 4 struct cpsw_slave { struct cpsw_slave_regs *regs; struct cpsw_sliver_regs *sliver; + int port_vlan; int slave_num; int phy_id; phy_interface_t phy_if; @@ -261,6 +263,7 @@ static inline void cpsw_ale_set_##name(u32 *ale_entry, u32 value) \ } DEFINE_ALE_FIELD(entry_type, 60, 2) +DEFINE_ALE_FIELD(vlan_id, 48, 12) DEFINE_ALE_FIELD(mcast_state, 62, 2) DEFINE_ALE_FIELD(port_mask, 66, 3) DEFINE_ALE_FIELD(ucast_type, 62, 2) @@ -268,6 +271,10 @@ DEFINE_ALE_FIELD(port_num, 66, 2) DEFINE_ALE_FIELD(blocked, 65, 1) DEFINE_ALE_FIELD(secure, 64, 1) DEFINE_ALE_FIELD(mcast, 40, 1) +DEFINE_ALE_FIELD(vlan_untag, 24, 3) +DEFINE_ALE_FIELD(vlan_reg_mcast, 16, 3) +DEFINE_ALE_FIELD(vlan_unreg_mcast, 8, 3) +DEFINE_ALE_FIELD(vlan_member_list, 0, 3) static char ethbdaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; @@ -312,6 +319,23 @@ static int cpsw_ale_write(struct cpsw_priv *priv, int idx, u32 *ale_entry) return idx; } +static int cpsw_ale_match_vlan(struct cpsw_priv *priv, u16 vid) +{ + u32 ale_entry[ALE_ENTRY_WORDS]; + int type, idx; + + for (idx = 0; idx < priv->ale_entries; idx++) { + cpsw_ale_read(priv, idx, ale_entry); + type = cpsw_ale_get_entry_type(ale_entry); + if (type != ALE_TYPE_VLAN) + continue; + if (cpsw_ale_get_vlan_id(ale_entry) == vid) + return idx; + } + + return -ENOENT; +} + static int cpsw_ale_match_addr(struct cpsw_priv *priv, u8* addr) { u32 ale_entry[ALE_ENTRY_WORDS]; @@ -376,13 +400,47 @@ static int cpsw_ale_find_ageable(struct cpsw_priv *priv) return -ENOENT; } +static int cpsw_ale_add_vlan(struct cpsw_priv *priv, u16 vid, int port_mask, + int untag, int reg_mcast, int unreg_mcast) +{ + u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; + int idx; + + idx = cpsw_ale_match_vlan(priv, vid); + if (idx >= 0) + cpsw_ale_read(priv, idx, ale_entry); + + cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_VLAN); + cpsw_ale_set_vlan_id(ale_entry, vid); + cpsw_ale_set_vlan_untag(ale_entry, untag); + cpsw_ale_set_vlan_reg_mcast(ale_entry, reg_mcast); + cpsw_ale_set_vlan_unreg_mcast(ale_entry, unreg_mcast); + cpsw_ale_set_vlan_member_list(ale_entry, port_mask); + + if (idx < 0) + idx = cpsw_ale_match_free(priv); + if (idx < 0) + idx = cpsw_ale_find_ageable(priv); + if (idx < 0) + return -ENOMEM; + + cpsw_ale_write(priv, idx, ale_entry); + return 0; +} + static int cpsw_ale_add_ucast(struct cpsw_priv *priv, u8 *addr, - int port, int flags) + int port, int flags, u16 vid) { u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; int idx; - cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR); + if (flags & ALE_VLAN) { + cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_VLAN_ADDR); + cpsw_ale_set_vlan_id(ale_entry, vid); + } else { + cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR); + } + cpsw_ale_set_addr(ale_entry, addr); cpsw_ale_set_ucast_type(ale_entry, ALE_UCAST_PERSISTANT); cpsw_ale_set_secure(ale_entry, (flags & ALE_SECURE) ? 1 : 0); @@ -401,7 +459,8 @@ static int cpsw_ale_add_ucast(struct cpsw_priv *priv, u8 *addr, return 0; } -static int cpsw_ale_add_mcast(struct cpsw_priv *priv, u8 *addr, int port_mask) +static int cpsw_ale_add_mcast(struct cpsw_priv *priv, u8 *addr, int port_mask, + int flags, u16 vid, int mcast_state) { u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; int idx, mask; @@ -410,9 +469,14 @@ static int cpsw_ale_add_mcast(struct cpsw_priv *priv, u8 *addr, int port_mask) if (idx >= 0) cpsw_ale_read(priv, idx, ale_entry); - cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR); + if (flags & ALE_VLAN) { + cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_VLAN_ADDR); + cpsw_ale_set_vlan_id(ale_entry, vid); + } else { + cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR); + } cpsw_ale_set_addr(ale_entry, addr); - cpsw_ale_set_mcast_state(ale_entry, ALE_MCAST_FWD_2); + cpsw_ale_set_mcast_state(ale_entry, mcast_state); mask = cpsw_ale_get_port_mask(ale_entry); port_mask |= mask; @@ -676,6 +740,7 @@ static inline u32 cpsw_get_slave_port(struct cpsw_priv *priv, u32 slave_num) static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv) { u32 slave_port; + u32 port_mask; dev_dbg(&slave->dev, "* %s\n", __func__); @@ -692,8 +757,22 @@ static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv) slave_port = cpsw_get_slave_port(priv, slave->slave_num); cpsw_ale_port_state(priv, slave_port, ALE_PORT_STATE_FORWARD); + port_mask = BIT(slave_port) | BIT(priv->host_port); + + /* set port_vlan to host_vlan */ + writel(BIT(slave->slave_num), &slave->regs->port_vlan); + slave->port_vlan = readl(&slave->regs->port_vlan); + slave->port_vlan &= 0xfff; + + /* add dual emac default entries */ + cpsw_ale_add_vlan(priv, slave->port_vlan, port_mask, + port_mask, port_mask, 0); /* add broadcast address */ - cpsw_ale_add_mcast(priv, ethbdaddr, 1 << slave_port); + cpsw_ale_add_mcast(priv, ethbdaddr, BIT(priv->host_port), ALE_VLAN, + slave->port_vlan, 0); + cpsw_ale_add_ucast(priv, priv->mac_addr, priv->host_port, + ALE_SECURE | ALE_VLAN, + slave->port_vlan); } static struct cpdma_desc *cpdma_desc_alloc(struct cpsw_priv *priv) @@ -839,7 +918,10 @@ static int cpsw_setup(struct device_d *dev) cpsw_ale_enable(priv, 1); cpsw_ale_clear(priv, 1); cpsw_ale_bypass(priv, 0); - cpsw_ale_vlan_aware(priv, 0); /* vlan unaware mode */ + cpsw_ale_vlan_aware(priv, 1); /* vlan aware mode */ + + /* dual mac mode in fifo */ + writel(BIT(16), &priv->host_port_regs->flow_thresh); /* setup host port priority mapping */ writel(0x76543210, &priv->host_port_regs->cpdma_tx_pri_map); @@ -853,9 +935,6 @@ static int cpsw_setup(struct device_d *dev) cpsw_ale_port_state(priv, priv->host_port, ALE_PORT_STATE_FORWARD); - cpsw_ale_add_ucast(priv, priv->mac_addr, priv->host_port, - ALE_SECURE); - cpsw_ale_add_mcast(priv, ethbdaddr, 1 << priv->host_port); /* init descriptor pool */ for (i = 0; i < NUM_DESCS; i++) { u32 val = (i == (NUM_DESCS - 1)) ? 0 : (u32)&priv->descs[i + 1]; -- 2.26.2 _______________________________________________ barebox mailing list barebox@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/barebox