From 4c6ae885c5796c94976bc5aaca071fe8f8b47f39 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Mon, 24 Apr 2017 09:21:11 +0200 Subject: [PATCH] mediatek: add DSA multi cpu port support Signed-off-by: John Crispin --- .../patches-4.9/0096-dsa-multi-cpu.patch | 268 ++++++++++++++++++ .../patches-4.9/0097-dsa-mt7530.patch | 83 ++++++ 2 files changed, 351 insertions(+) create mode 100644 target/linux/mediatek/patches-4.9/0096-dsa-multi-cpu.patch create mode 100644 target/linux/mediatek/patches-4.9/0097-dsa-mt7530.patch diff --git a/target/linux/mediatek/patches-4.9/0096-dsa-multi-cpu.patch b/target/linux/mediatek/patches-4.9/0096-dsa-multi-cpu.patch new file mode 100644 index 000000000000..2a562e1d1c09 --- /dev/null +++ b/target/linux/mediatek/patches-4.9/0096-dsa-multi-cpu.patch @@ -0,0 +1,268 @@ +Index: linux-4.9.20/drivers/net/dsa/mt7530.c +=================================================================== +--- linux-4.9.20.orig/drivers/net/dsa/mt7530.c ++++ linux-4.9.20/drivers/net/dsa/mt7530.c +@@ -996,15 +996,7 @@ err: + static enum dsa_tag_protocol + mtk_get_tag_protocol(struct dsa_switch *ds) + { +- struct mt7530_priv *priv = ds->priv; +- +- if (!dsa_is_cpu_port(ds, MT7530_CPU_PORT)) { +- dev_warn(priv->dev, +- "port not matched with tagging CPU port\n"); +- return DSA_TAG_PROTO_NONE; +- } else { +- return DSA_TAG_PROTO_MTK; +- } ++ return DSA_TAG_PROTO_MTK; + } + + static struct dsa_switch_ops mt7530_switch_ops = { +Index: linux-4.9.20/include/net/dsa.h +=================================================================== +--- linux-4.9.20.orig/include/net/dsa.h ++++ linux-4.9.20/include/net/dsa.h +@@ -145,6 +145,8 @@ struct dsa_port { + struct device_node *dn; + unsigned int ageing_time; + u8 stp_state; ++ struct net_device *ethernet; ++ int upstream; + }; + + struct dsa_switch { +@@ -205,7 +207,7 @@ struct dsa_switch { + + static inline bool dsa_is_cpu_port(struct dsa_switch *ds, int p) + { +- return !!(ds->index == ds->dst->cpu_switch && p == ds->dst->cpu_port); ++ return !!(ds->cpu_port_mask & (1 << p)); + } + + static inline bool dsa_is_dsa_port(struct dsa_switch *ds, int p) +@@ -218,6 +220,11 @@ static inline bool dsa_is_port_initializ + return ds->enabled_port_mask & (1 << p) && ds->ports[p].netdev; + } + ++static inline bool dsa_is_upstream_port(struct dsa_switch *ds, int p) ++{ ++ return dsa_is_cpu_port(ds, p) || dsa_is_dsa_port(ds, p); ++} ++ + static inline u8 dsa_upstream_port(struct dsa_switch *ds) + { + struct dsa_switch_tree *dst = ds->dst; +@@ -234,6 +241,18 @@ static inline u8 dsa_upstream_port(struc + return ds->rtable[dst->cpu_switch]; + } + ++static inline u8 dsa_port_upstream_port(struct dsa_switch *ds, int port) ++{ ++ /* ++ * If this port has a specific upstream cpu port, use it, ++ * otherwise use the switch default. ++ */ ++ if (ds->ports[port].upstream) ++ return ds->ports[port].upstream; ++ else ++ return dsa_upstream_port(ds); ++} ++ + struct switchdev_trans; + struct switchdev_obj; + struct switchdev_obj_port_fdb; +Index: linux-4.9.20/net/dsa/dsa2.c +=================================================================== +--- linux-4.9.20.orig/net/dsa/dsa2.c ++++ linux-4.9.20/net/dsa/dsa2.c +@@ -248,8 +248,6 @@ static int dsa_cpu_port_apply(struct dev + return err; + } + +- ds->cpu_port_mask |= BIT(index); +- + return 0; + } + +@@ -259,6 +257,10 @@ static void dsa_cpu_port_unapply(struct + dsa_cpu_dsa_destroy(port); + ds->cpu_port_mask &= ~BIT(index); + ++ if (ds->ports[index].ethernet) { ++ dev_put(ds->ports[index].ethernet); ++ ds->ports[index].ethernet = NULL; ++ } + } + + static int dsa_user_port_apply(struct device_node *port, u32 index, +@@ -479,6 +481,29 @@ static int dsa_cpu_parse(struct device_n + + dst->rcv = dst->tag_ops->rcv; + ++ dev_hold(ethernet_dev); ++ ds->ports[index].ethernet = ethernet_dev; ++ ds->cpu_port_mask |= BIT(index); ++ ++ return 0; ++} ++ ++static int dsa_user_parse(struct device_node *port, u32 index, ++ struct dsa_switch *ds) ++{ ++ struct device_node *cpu_port; ++ const unsigned int *cpu_port_reg; ++ int cpu_port_index; ++ ++ cpu_port = of_parse_phandle(port, "cpu", 0); ++ if (cpu_port) { ++ cpu_port_reg = of_get_property(cpu_port, "reg", NULL); ++ if (!cpu_port_reg) ++ return -EINVAL; ++ cpu_port_index = be32_to_cpup(cpu_port_reg); ++ ds->ports[index].upstream = cpu_port_index; ++ } ++ + return 0; + } + +@@ -486,18 +511,19 @@ static int dsa_ds_parse(struct dsa_switc + { + struct device_node *port; + u32 index; +- int err; ++ int err = 0; + + for (index = 0; index < DSA_MAX_PORTS; index++) { + port = ds->ports[index].dn; + if (!port) + continue; + +- if (dsa_port_is_cpu(port)) { ++ if (dsa_port_is_cpu(port)) + err = dsa_cpu_parse(port, index, dst, ds); +- if (err) +- return err; +- } ++ else if (!dsa_port_is_dsa(port)) ++ err = dsa_user_parse(port, index, ds); ++ if (err) ++ return err; + } + + pr_info("DSA: switch %d %d parsed\n", dst->tree, ds->index); +Index: linux-4.9.20/net/dsa/dsa_priv.h +=================================================================== +--- linux-4.9.20.orig/net/dsa/dsa_priv.h ++++ linux-4.9.20/net/dsa/dsa_priv.h +@@ -43,6 +43,7 @@ struct dsa_slave_priv { + int old_duplex; + + struct net_device *bridge_dev; ++ struct net_device *master; + #ifdef CONFIG_NET_POLL_CONTROLLER + struct netpoll *netpoll; + #endif +Index: linux-4.9.20/net/dsa/slave.c +=================================================================== +--- linux-4.9.20.orig/net/dsa/slave.c ++++ linux-4.9.20/net/dsa/slave.c +@@ -61,7 +61,7 @@ static int dsa_slave_get_iflink(const st + { + struct dsa_slave_priv *p = netdev_priv(dev); + +- return p->parent->dst->master_netdev->ifindex; ++ return p->master->ifindex; + } + + static inline bool dsa_port_is_bridged(struct dsa_slave_priv *p) +@@ -96,7 +96,7 @@ static void dsa_port_set_stp_state(struc + static int dsa_slave_open(struct net_device *dev) + { + struct dsa_slave_priv *p = netdev_priv(dev); +- struct net_device *master = p->parent->dst->master_netdev; ++ struct net_device *master = p->master; + struct dsa_switch *ds = p->parent; + u8 stp_state = dsa_port_is_bridged(p) ? + BR_STATE_BLOCKING : BR_STATE_FORWARDING; +@@ -151,7 +151,7 @@ out: + static int dsa_slave_close(struct net_device *dev) + { + struct dsa_slave_priv *p = netdev_priv(dev); +- struct net_device *master = p->parent->dst->master_netdev; ++ struct net_device *master = p->master; + struct dsa_switch *ds = p->parent; + + if (p->phy) +@@ -178,7 +178,7 @@ static int dsa_slave_close(struct net_de + static void dsa_slave_change_rx_flags(struct net_device *dev, int change) + { + struct dsa_slave_priv *p = netdev_priv(dev); +- struct net_device *master = p->parent->dst->master_netdev; ++ struct net_device *master = p->master; + + if (change & IFF_ALLMULTI) + dev_set_allmulti(master, dev->flags & IFF_ALLMULTI ? 1 : -1); +@@ -189,7 +189,7 @@ static void dsa_slave_change_rx_flags(st + static void dsa_slave_set_rx_mode(struct net_device *dev) + { + struct dsa_slave_priv *p = netdev_priv(dev); +- struct net_device *master = p->parent->dst->master_netdev; ++ struct net_device *master = p->master; + + dev_mc_sync(master, dev); + dev_uc_sync(master, dev); +@@ -198,7 +198,7 @@ static void dsa_slave_set_rx_mode(struct + static int dsa_slave_set_mac_address(struct net_device *dev, void *a) + { + struct dsa_slave_priv *p = netdev_priv(dev); +- struct net_device *master = p->parent->dst->master_netdev; ++ struct net_device *master = p->master; + struct sockaddr *addr = a; + int err; + +@@ -633,7 +633,7 @@ static netdev_tx_t dsa_slave_xmit(struct + /* Queue the SKB for transmission on the parent interface, but + * do not modify its EtherType + */ +- nskb->dev = p->parent->dst->master_netdev; ++ nskb->dev = p->master; + dev_queue_xmit(nskb); + + return NETDEV_TX_OK; +@@ -945,7 +945,7 @@ static int dsa_slave_netpoll_setup(struc + { + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_switch *ds = p->parent; +- struct net_device *master = ds->dst->master_netdev; ++ struct net_device *master = p->master; + struct netpoll *netpoll; + int err = 0; + +@@ -1235,11 +1235,16 @@ int dsa_slave_create(struct dsa_switch * + struct net_device *master; + struct net_device *slave_dev; + struct dsa_slave_priv *p; ++ int port_cpu = ds->ports[port].upstream; + int ret; + +- master = ds->dst->master_netdev; +- if (ds->master_netdev) ++ if (port_cpu && ds->ports[port_cpu].ethernet) ++ master = ds->ports[port_cpu].ethernet; ++ else if (ds->master_netdev) + master = ds->master_netdev; ++ else ++ master = ds->dst->master_netdev; ++ master->dsa_ptr = (void *)ds->dst; + + slave_dev = alloc_netdev(sizeof(struct dsa_slave_priv), name, + NET_NAME_UNKNOWN, ether_setup); +@@ -1265,6 +1270,7 @@ int dsa_slave_create(struct dsa_switch * + p->parent = ds; + p->port = port; + p->xmit = dst->tag_ops->xmit; ++ p->master = master; + + p->old_pause = -1; + p->old_link = -1; diff --git a/target/linux/mediatek/patches-4.9/0097-dsa-mt7530.patch b/target/linux/mediatek/patches-4.9/0097-dsa-mt7530.patch new file mode 100644 index 000000000000..9f785ddca227 --- /dev/null +++ b/target/linux/mediatek/patches-4.9/0097-dsa-mt7530.patch @@ -0,0 +1,83 @@ +Index: linux-4.9.20/drivers/net/dsa/mt7530.c +=================================================================== +--- linux-4.9.20.orig/drivers/net/dsa/mt7530.c ++++ linux-4.9.20/drivers/net/dsa/mt7530.c +@@ -627,7 +627,7 @@ mt7530_setup(struct dsa_switch *ds) + + /* Enable Port 6 only; P5 as GMAC5 which currently is not supported */ + val = mt7530_read(priv, MT7530_MHWTRAP); +- val &= ~MHWTRAP_P6_DIS & ~MHWTRAP_PHY_ACCESS; ++ val &= ~MHWTRAP_P5_DIS & ~MHWTRAP_P6_DIS & ~MHWTRAP_PHY_ACCESS; + val |= MHWTRAP_MANUAL; + if (!dsa_is_cpu_port(ds, 5)) { + val |= MHWTRAP_P5_DIS; +@@ -735,6 +735,9 @@ static int + mt7530_cpu_port_enable(struct mt7530_priv *priv, + int port) + { ++ u8 port_mask = 0; ++ int i; ++ + /* Enable Mediatek header mode on the cpu port */ + mt7530_write(priv, MT7530_PVC_P(port), + PORT_SPEC_TAG); +@@ -751,8 +754,12 @@ mt7530_cpu_port_enable(struct mt7530_pri + /* CPU port gets connected to all user ports of + * the switch + */ ++ for (i = 0; i < MT7530_NUM_PORTS; i++) ++ if ((priv->ds->enabled_port_mask & BIT(i)) && ++ (dsa_port_upstream_port(priv->ds, i) == port)) ++ port_mask |= BIT(i); + mt7530_write(priv, MT7530_PCR_P(port), +- PCR_MATRIX(priv->ds->enabled_port_mask)); ++ PCR_MATRIX(port_mask)); + + return 0; + } +@@ -762,6 +769,7 @@ mt7530_port_enable(struct dsa_switch *ds + struct phy_device *phy) + { + struct mt7530_priv *priv = ds->priv; ++ u8 upstream = dsa_port_upstream_port(ds, port); + + mutex_lock(&priv->reg_mutex); + +@@ -772,7 +780,7 @@ mt7530_port_enable(struct dsa_switch *ds + * restore the port matrix if the port is the member of a certain + * bridge. + */ +- priv->ports[port].pm |= PCR_MATRIX(BIT(MT7530_CPU_PORT)); ++ priv->ports[port].pm |= PCR_MATRIX(BIT(upstream)); + priv->ports[port].enable = true; + mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK, + priv->ports[port].pm); +@@ -835,7 +843,8 @@ mt7530_port_bridge_join(struct dsa_switc + struct net_device *bridge) + { + struct mt7530_priv *priv = ds->priv; +- u32 port_bitmap = BIT(MT7530_CPU_PORT); ++ u8 upstream = dsa_port_upstream_port(ds, port); ++ u32 port_bitmap = BIT(upstream); + int i; + + mutex_lock(&priv->reg_mutex); +@@ -873,6 +882,7 @@ static void + mt7530_port_bridge_leave(struct dsa_switch *ds, int port) + { + struct mt7530_priv *priv = ds->priv; ++ u8 upstream = dsa_port_upstream_port(ds, port); + int i; + + mutex_lock(&priv->reg_mutex); +@@ -898,8 +908,8 @@ mt7530_port_bridge_leave(struct dsa_swit + priv->bridge_dev[port] = NULL; + if (priv->ports[port].enable) + mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK, +- PCR_MATRIX(BIT(MT7530_CPU_PORT))); +- priv->ports[port].pm = PCR_MATRIX(BIT(MT7530_CPU_PORT)); ++ PCR_MATRIX(BIT(upstream))); ++ priv->ports[port].pm = PCR_MATRIX(BIT(upstream)); + + mutex_unlock(&priv->reg_mutex); + } -- 2.30.2