diff --git a/Documentation/devicetree/bindings/net/airoha,en7581-eth.yaml b/Documentation/devicetree/bindings/net/airoha,en7581-eth.yaml index fbe2ddcdd909..17fe2edf4886 100644 --- a/Documentation/devicetree/bindings/net/airoha,en7581-eth.yaml +++ b/Documentation/devicetree/bindings/net/airoha,en7581-eth.yaml @@ -130,6 +130,42 @@ patternProperties: maximum: 4 description: GMAC port identifier + allOf: + - if: + properties: + reg: + contains: + items: + - enum: + - 3 + - 4 + then: + properties: + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + patternProperties: + "^ethernet@[0-5]$": + type: object + unevaluatedProperties: false + $ref: ethernet-controller.yaml# + description: External ethernet port ID available on the GDM port + + properties: + compatible: + const: airoha,eth-port + + reg: + maximum: 5 + description: External ethernet port identifier + + required: + - reg + - compatible + required: - reg - compatible @@ -191,9 +227,27 @@ examples: #address-cells = <1>; #size-cells = <0>; - mac: ethernet@1 { + ethernet@1 { compatible = "airoha,eth-mac"; reg = <1>; }; + + ethernet@4 { + compatible = "airoha,eth-mac"; + reg = <4>; + + #address-cells = <1>; + #size-cells = <0>; + + ethernet@0 { + compatible = "airoha,eth-port"; + reg = <0>; + }; + + ethernet@1 { + compatible = "airoha,eth-port"; + reg = <1>; + }; + }; }; }; diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index 584c4f1a09dc..5a8e84fa9918 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -71,20 +71,76 @@ static void airoha_qdma_irq_disable(struct airoha_irq_bank *irq_bank, airoha_qdma_set_irqmask(irq_bank, index, mask, 0); } -static void airoha_set_macaddr(struct airoha_gdm_dev *dev, const u8 *addr) +static int airoha_set_macaddr(struct airoha_gdm_dev *dev, const u8 *addr) { + u8 ref_addr[ETH_ALEN] __aligned(2); struct airoha_eth *eth = dev->eth; - u32 val, reg; + u32 reg, val, lmin, lmax; + int i; + + eth_zero_addr(ref_addr); + lmin = (addr[3] << 16) | (addr[4] << 8) | addr[5]; + lmax = lmin; + + for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { + struct airoha_gdm_port *port = eth->ports[i]; + int j; + + if (!port) + continue; + + for (j = 0; j < ARRAY_SIZE(port->devs); j++) { + struct airoha_gdm_dev *iter_dev; + struct net_device *netdev; + + iter_dev = port->devs[j]; + if (!iter_dev || iter_dev == dev) + continue; + + if (airoha_is_lan_gdm_dev(iter_dev) != + airoha_is_lan_gdm_dev(dev)) + continue; + + netdev = netdev_from_priv(iter_dev); + if (netdev->reg_state != NETREG_REGISTERED) + continue; + + ether_addr_copy(ref_addr, netdev->dev_addr); + val = (netdev->dev_addr[3] << 16) | + (netdev->dev_addr[4] << 8) | netdev->dev_addr[5]; + if (val < lmin) + lmin = val; + if (val > lmax) + lmax = val; + } + } + + if (!is_zero_ether_addr(ref_addr) && memcmp(ref_addr, addr, 3)) { + /* According to the HW design, hw mac address MSBs must be + * the same for each net_device with the same LAN/WAN + * configuration. + */ + struct net_device *netdev = netdev_from_priv(dev); + + dev_warn(eth->dev, + "%s: wrong mac addr, MSBs must be %02x:%02x:%02x\n", + netdev->name, ref_addr[0], ref_addr[1], + ref_addr[2]); + dev_warn(eth->dev, "FE hw forwarding won't work properly\n"); + + return -EINVAL; + } reg = airoha_is_lan_gdm_dev(dev) ? REG_FE_LAN_MAC_H : REG_FE_WAN_MAC_H; val = (addr[0] << 16) | (addr[1] << 8) | addr[2]; airoha_fe_wr(eth, reg, val); - val = (addr[3] << 16) | (addr[4] << 8) | addr[5]; - airoha_fe_wr(eth, REG_FE_MAC_LMIN(reg), val); - airoha_fe_wr(eth, REG_FE_MAC_LMAX(reg), val); + airoha_fe_wr(eth, REG_FE_MAC_LMIN(reg), lmin); + airoha_fe_wr(eth, REG_FE_MAC_LMAX(reg), lmax); - airoha_ppe_init_upd_mem(dev); + airoha_ppe_init_upd_mem(dev, addr); + + return 0; } static void airoha_set_gdm_port_fwd_cfg(struct airoha_eth *eth, u32 addr, @@ -106,7 +162,7 @@ static int airoha_set_vip_for_gdm_port(struct airoha_gdm_dev *dev, bool enable) struct airoha_eth *eth = dev->eth; u32 vip_port; - vip_port = eth->soc->ops.get_vip_port(port, port->nbq); + vip_port = eth->soc->ops.get_vip_port(port, dev->nbq); if (enable) { airoha_fe_set(eth, REG_FE_VIP_PORT_EN, vip_port); airoha_fe_set(eth, REG_FE_IFC_PORT_EN, vip_port); @@ -566,24 +622,26 @@ static int airoha_qdma_fill_rx_queue(struct airoha_queue *q) return nframes; } -static int airoha_qdma_get_gdm_port(struct airoha_eth *eth, - struct airoha_qdma_desc *desc) +static struct airoha_gdm_dev * +airoha_qdma_get_gdm_dev(struct airoha_eth *eth, struct airoha_qdma_desc *desc) { - u32 port, sport, msg1 = le32_to_cpu(READ_ONCE(desc->msg1)); + struct airoha_gdm_port *port; + u16 p, d; - sport = FIELD_GET(QDMA_ETH_RXMSG_SPORT_MASK, msg1); - switch (sport) { - case 0x10 ... 0x14: - port = 0; - break; - case 0x2 ... 0x4: - port = sport - 1; - break; - default: - return -EINVAL; - } + if (eth->soc->ops.get_dev_from_sport(desc, &p, &d)) + return ERR_PTR(-ENODEV); - return port >= ARRAY_SIZE(eth->ports) ? -EINVAL : port; + if (p >= ARRAY_SIZE(eth->ports)) + return ERR_PTR(-ENODEV); + + port = eth->ports[p]; + if (!port) + return ERR_PTR(-ENODEV); + + if (d >= ARRAY_SIZE(port->devs)) + return ERR_PTR(-ENODEV); + + return port->devs[d] ? port->devs[d] : ERR_PTR(-ENODEV); } static int airoha_qdma_rx_process(struct airoha_queue *q, int budget) @@ -598,9 +656,9 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget) struct airoha_queue_entry *e = &q->entry[q->tail]; struct airoha_qdma_desc *desc = &q->desc[q->tail]; u32 hash, reason, msg1, desc_ctrl; - struct airoha_gdm_port *port; + struct airoha_gdm_dev *dev; struct net_device *netdev; - int data_len, len, p; + int data_len, len; struct page *page; desc_ctrl = le32_to_cpu(READ_ONCE(desc->ctrl)); @@ -621,15 +679,11 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget) if (!len || data_len < len) goto free_frag; - p = airoha_qdma_get_gdm_port(eth, desc); - if (p < 0 || !eth->ports[p]) + dev = airoha_qdma_get_gdm_dev(eth, desc); + if (IS_ERR(dev)) goto free_frag; - port = eth->ports[p]; - if (!port->dev) - goto free_frag; - - netdev = port->dev->dev; + netdev = netdev_from_priv(dev); if (!q->skb) { /* first buffer */ q->skb = napi_build_skb(e->buf - AIROHA_RX_HEADROOM, q->buf_size); @@ -659,6 +713,8 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget) continue; if (netdev_uses_dsa(netdev)) { + struct airoha_gdm_port *port = dev->port; + /* PPE module requires untagged packets to work * properly and it provides DSA port index via the * DMA descriptor. Report DSA tag to the DSA stack @@ -852,24 +908,29 @@ static void airoha_qdma_wake_netdev_txqs(struct airoha_queue *q) for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { struct airoha_gdm_port *port = eth->ports[i]; - struct airoha_gdm_dev *dev; - int j; + int d; if (!port) continue; - dev = port->dev; - if (!dev) - continue; + for (d = 0; d < ARRAY_SIZE(port->devs); d++) { + struct airoha_gdm_dev *dev = port->devs[d]; + struct net_device *netdev; + int j; - if (dev->qdma != qdma) - continue; - - for (j = 0; j < dev->dev->num_tx_queues; j++) { - if (airoha_qdma_get_txq(qdma, j) != qid) + if (!dev) continue; - netif_wake_subqueue(dev->dev, j); + if (dev->qdma != qdma) + continue; + + netdev = netdev_from_priv(dev); + for (j = 0; j < netdev->num_tx_queues; j++) { + if (airoha_qdma_get_txq(qdma, j) != qid) + continue; + + netif_wake_subqueue(netdev, j); + } } } q->txq_stopped = false; @@ -1715,8 +1776,8 @@ static int airoha_dev_open(struct net_device *netdev) int err, len = ETH_HLEN + netdev->mtu + ETH_FCS_LEN; struct airoha_gdm_dev *dev = netdev_priv(netdev); struct airoha_gdm_port *port = dev->port; + u32 cur_len, pse_port = FE_PSE_PORT_PPE1; struct airoha_qdma *qdma = dev->qdma; - u32 pse_port = FE_PSE_PORT_PPE1; netif_tx_start_all_queues(netdev); err = airoha_set_vip_for_gdm_port(dev, true); @@ -1730,27 +1791,59 @@ static int airoha_dev_open(struct net_device *netdev) airoha_fe_clear(qdma->eth, REG_GDM_INGRESS_CFG(port->id), GDM_STAG_EN_MASK); - airoha_fe_rmw(qdma->eth, REG_GDM_LEN_CFG(port->id), - GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK, - FIELD_PREP(GDM_SHORT_LEN_MASK, 60) | - FIELD_PREP(GDM_LONG_LEN_MASK, len)); + cur_len = airoha_fe_get(qdma->eth, REG_GDM_LEN_CFG(port->id), + GDM_LONG_LEN_MASK); + if (!port->users || len > cur_len) { + /* Opening a sibling net_device with a larger MTU updates the + * MTU of already running devices. This is required to allow + * multiple net_devices with different MTUs to share the same + * GDM port. + */ + airoha_fe_rmw(qdma->eth, REG_GDM_LEN_CFG(port->id), + GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK, + FIELD_PREP(GDM_SHORT_LEN_MASK, 60) | + FIELD_PREP(GDM_LONG_LEN_MASK, len)); + } + port->users++; airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG, GLOBAL_CFG_TX_DMA_EN_MASK | GLOBAL_CFG_RX_DMA_EN_MASK); atomic_inc(&qdma->users); - if (port->id == AIROHA_GDM2_IDX && - airoha_ppe_is_enabled(qdma->eth, 1)) { - /* For PPE2 always use secondary cpu port. */ + if (!airoha_is_lan_gdm_dev(dev) && + airoha_ppe_is_enabled(qdma->eth, 1)) pse_port = FE_PSE_PORT_PPE2; - } airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id), pse_port); return 0; } +static void airoha_set_port_mtu(struct airoha_eth *eth, + struct airoha_gdm_port *port) +{ + u32 len = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(port->devs); i++) { + struct airoha_gdm_dev *dev = port->devs[i]; + struct net_device *netdev; + + if (!dev) + continue; + + netdev = netdev_from_priv(dev); + if (netif_running(netdev)) + len = max_t(u32, len, netdev->mtu); + } + len += ETH_HLEN + ETH_FCS_LEN; + + airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id), + GDM_LONG_LEN_MASK, + FIELD_PREP(GDM_LONG_LEN_MASK, len)); +} + static int airoha_dev_stop(struct net_device *netdev) { struct airoha_gdm_dev *dev = netdev_priv(netdev); @@ -1763,8 +1856,12 @@ static int airoha_dev_stop(struct net_device *netdev) for (i = 0; i < netdev->num_tx_queues; i++) netdev_tx_reset_subqueue(netdev, i); - airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id), - FE_PSE_PORT_DROP); + if (--port->users) + airoha_set_port_mtu(dev->eth, port); + else + airoha_set_gdm_port_fwd_cfg(qdma->eth, + REG_GDM_FWD_CFG(port->id), + FE_PSE_PORT_DROP); if (atomic_dec_and_test(&qdma->users)) { airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG, @@ -1785,13 +1882,18 @@ static int airoha_dev_stop(struct net_device *netdev) static int airoha_dev_set_macaddr(struct net_device *netdev, void *p) { struct airoha_gdm_dev *dev = netdev_priv(netdev); + struct sockaddr *addr = p; int err; - err = eth_mac_addr(netdev, p); + err = eth_prepare_mac_addr_change(netdev, p); if (err) return err; - airoha_set_macaddr(dev, netdev->dev_addr); + err = airoha_set_macaddr(dev, addr->sa_data); + if (err) + return err; + + eth_commit_mac_addr_change(netdev, p); return 0; } @@ -1832,7 +1934,7 @@ static int airoha_enable_gdm2_loopback(struct airoha_gdm_dev *dev) airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX)); airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, BIT(AIROHA_GDM2_IDX)); - src_port = eth->soc->ops.get_sport(port, port->nbq); + src_port = eth->soc->ops.get_sport(port, dev->nbq); if (src_port < 0) return src_port; @@ -1849,7 +1951,7 @@ static int airoha_enable_gdm2_loopback(struct airoha_gdm_dev *dev) airoha_ppe_set_cpu_port(dev, i, AIROHA_GDM2_IDX); if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) { - u32 mask = FC_ID_OF_SRC_PORT_MASK(port->nbq); + u32 mask = FC_ID_OF_SRC_PORT_MASK(dev->nbq); airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6, mask, __field_prep(mask, AIROHA_GDM2_IDX)); @@ -1858,36 +1960,81 @@ static int airoha_enable_gdm2_loopback(struct airoha_gdm_dev *dev) return 0; } +static struct airoha_gdm_dev * +airoha_get_wan_gdm_dev(struct airoha_eth *eth) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { + struct airoha_gdm_port *port = eth->ports[i]; + int j; + + if (!port) + continue; + + for (j = 0; j < ARRAY_SIZE(port->devs); j++) { + struct airoha_gdm_dev *dev = port->devs[j]; + + if (dev && !airoha_is_lan_gdm_dev(dev)) + return dev; + } + } + + return NULL; +} + +static void airoha_dev_set_qdma(struct airoha_gdm_dev *dev) +{ + struct net_device *netdev = netdev_from_priv(dev); + struct airoha_eth *eth = dev->eth; + int ppe_id; + + /* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */ + dev->qdma = ð->qdma[!airoha_is_lan_gdm_dev(dev)]; + netdev->irq = dev->qdma->irq_banks[0].irq; + + ppe_id = !airoha_is_lan_gdm_dev(dev) && airoha_ppe_is_enabled(eth, 1); + airoha_ppe_set_cpu_port(dev, ppe_id, airoha_get_fe_port(dev)); +} + static int airoha_dev_init(struct net_device *netdev) { struct airoha_gdm_dev *dev = netdev_priv(netdev); struct airoha_gdm_port *port = dev->port; - struct airoha_eth *eth = dev->eth; - int i; - - /* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */ - dev->qdma = ð->qdma[!airoha_is_lan_gdm_dev(dev)]; - dev->dev->irq = dev->qdma->irq_banks[0].irq; - airoha_set_macaddr(dev, netdev->dev_addr); switch (port->id) { case AIROHA_GDM3_IDX: - case AIROHA_GDM4_IDX: - /* If GDM2 is active we can't enable loopback */ - if (!eth->ports[1]) { - int err; + case AIROHA_GDM4_IDX: { + struct airoha_eth *eth = dev->eth; - err = airoha_enable_gdm2_loopback(dev); - if (err) - return err; - } + /* GDM2 supports a single net_device */ + if (eth->ports[1] && eth->ports[1]->devs[0]) + break; + + if (airoha_get_wan_gdm_dev(eth)) + break; + + fallthrough; + } + case AIROHA_GDM2_IDX: + /* GDM2 is always used as wan */ + dev->flags |= AIROHA_PRIV_F_WAN; break; default: break; } - for (i = 0; i < eth->soc->num_ppe; i++) - airoha_ppe_set_cpu_port(dev, i, airoha_get_fe_port(dev)); + airoha_dev_set_qdma(dev); + airoha_set_macaddr(dev, netdev->dev_addr); + + if (!airoha_is_lan_gdm_dev(dev) && + (port->id == AIROHA_GDM3_IDX || port->id == AIROHA_GDM4_IDX)) { + int err; + + err = airoha_enable_gdm2_loopback(dev); + if (err) + return err; + } return 0; } @@ -1919,13 +2066,10 @@ static int airoha_dev_change_mtu(struct net_device *netdev, int mtu) { struct airoha_gdm_dev *dev = netdev_priv(netdev); struct airoha_gdm_port *port = dev->port; - u32 len = ETH_HLEN + mtu + ETH_FCS_LEN; - struct airoha_eth *eth = dev->eth; - airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id), - GDM_LONG_LEN_MASK, - FIELD_PREP(GDM_LONG_LEN_MASK, len)); WRITE_ONCE(netdev->mtu, mtu); + if (port->users) + airoha_set_port_mtu(dev->eth, port); return 0; } @@ -2053,7 +2197,8 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, } fport = airoha_get_fe_port(dev); - msg1 = FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) | + msg1 = FIELD_PREP(QDMA_ETH_TXMSG_NBOQ_MASK, dev->nbq) | + FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) | FIELD_PREP(QDMA_ETH_TXMSG_METER_MASK, 0x7f); q = &qdma->q_tx[qid]; @@ -2989,12 +3134,15 @@ bool airoha_is_valid_gdm_dev(struct airoha_eth *eth, for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { struct airoha_gdm_port *port = eth->ports[i]; + int j; if (!port) continue; - if (port->dev == dev) - return true; + for (j = 0; j < ARRAY_SIZE(port->devs); j++) { + if (port->devs[j] == dev) + return true; + } } return false; @@ -3002,10 +3150,11 @@ bool airoha_is_valid_gdm_dev(struct airoha_eth *eth, static int airoha_alloc_gdm_device(struct airoha_eth *eth, struct airoha_gdm_port *port, - struct device_node *np) + int nbq, struct device_node *np) { - struct airoha_gdm_dev *dev; struct net_device *netdev; + struct airoha_gdm_dev *dev; + u8 index; int err; netdev = devm_alloc_etherdev_mqs(eth->dev, sizeof(*dev), @@ -3025,7 +3174,6 @@ static int airoha_alloc_gdm_device(struct airoha_eth *eth, NETIF_F_HW_TC; netdev->features |= netdev->hw_features; netdev->vlan_features = netdev->hw_features; - netdev->dev.of_node = np; SET_NETDEV_DEV(netdev, eth->dev); /* reserve hw queues for HTB offloading */ @@ -3043,11 +3191,24 @@ static int airoha_alloc_gdm_device(struct airoha_eth *eth, netdev->dev_addr); } + /* Allowed nbq for EN7581 on GDM3 port are 4 and 5 for PCIE0 + * and PCIE1 respectively. + */ + index = nbq; + if (index && airoha_is_7581(eth) && port->id == AIROHA_GDM3_IDX) + index -= 4; + + if (index >= ARRAY_SIZE(port->devs) || port->devs[index]) { + dev_err(eth->dev, "invalid nbq id: %d\n", nbq); + return -EINVAL; + } + + netdev->dev.of_node = of_node_get(np); dev = netdev_priv(netdev); - dev->dev = netdev; dev->port = port; - port->dev = dev; dev->eth = eth; + dev->nbq = nbq; + port->devs[index] = dev; return 0; } @@ -3057,7 +3218,8 @@ static int airoha_alloc_gdm_port(struct airoha_eth *eth, { const __be32 *id_ptr = of_get_property(np, "reg", NULL); struct airoha_gdm_port *port; - int err, p; + struct device_node *node; + int err, nbq, p, d = 0; u32 id; if (!id_ptr) { @@ -3085,15 +3247,51 @@ static int airoha_alloc_gdm_port(struct airoha_eth *eth, u64_stats_init(&port->stats.syncp); spin_lock_init(&port->stats.lock); port->id = id; - /* XXX: Read nbq from DTS */ - port->nbq = id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0; eth->ports[p] = port; err = airoha_metadata_dst_alloc(port); if (err) return err; - return airoha_alloc_gdm_device(eth, port, np); + /* Default nbq value to ensure backward compatibility */ + nbq = id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0; + + for_each_child_of_node(np, node) { + /* Multiple external serdes connected to the FE GDM port via an + * external arbiter. + */ + const __be32 *nbq_ptr; + + if (!of_device_is_compatible(node, "airoha,eth-port")) + continue; + + d++; + if (!of_device_is_available(node)) + continue; + + nbq_ptr = of_get_property(node, "reg", NULL); + if (!nbq_ptr) { + dev_err(eth->dev, "missing nbq id\n"); + of_node_put(node); + return -EINVAL; + } + + /* Verify the provided nbq parameter is valid */ + nbq = be32_to_cpup(nbq_ptr); + err = eth->soc->ops.get_sport(port, nbq); + if (err < 0) { + of_node_put(node); + return err; + } + + err = airoha_alloc_gdm_device(eth, port, nbq, node); + if (err) { + of_node_put(node); + return err; + } + } + + return !d ? airoha_alloc_gdm_device(eth, port, nbq, np) : 0; } static int airoha_register_gdm_devices(struct airoha_eth *eth) @@ -3102,14 +3300,22 @@ static int airoha_register_gdm_devices(struct airoha_eth *eth) for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { struct airoha_gdm_port *port = eth->ports[i]; - int err; + int j; if (!port) continue; - err = register_netdev(port->dev->dev); - if (err) - return err; + for (j = 0; j < ARRAY_SIZE(port->devs); j++) { + struct airoha_gdm_dev *dev = port->devs[j]; + int err; + + if (!dev) + continue; + + err = register_netdev(netdev_from_priv(dev)); + if (err) + return err; + } } set_bit(DEV_STATE_REGISTERED, ð->state); @@ -3216,14 +3422,23 @@ static int airoha_probe(struct platform_device *pdev) for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { struct airoha_gdm_port *port = eth->ports[i]; - struct airoha_gdm_dev *dev; + int j; if (!port) continue; - dev = port->dev; - if (dev && dev->dev->reg_state == NETREG_REGISTERED) - unregister_netdev(dev->dev); + for (j = 0; j < ARRAY_SIZE(port->devs); j++) { + struct airoha_gdm_dev *dev = port->devs[j]; + struct net_device *netdev; + + if (!dev) + continue; + + netdev = netdev_from_priv(dev); + if (netdev->reg_state == NETREG_REGISTERED) + unregister_netdev(netdev); + of_node_put(netdev->dev.of_node); + } airoha_metadata_dst_free(port); } airoha_hw_cleanup(eth); @@ -3244,14 +3459,22 @@ static void airoha_remove(struct platform_device *pdev) for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { struct airoha_gdm_port *port = eth->ports[i]; - struct airoha_gdm_dev *dev; + int j; if (!port) continue; - dev = port->dev; - if (dev) - unregister_netdev(dev->dev); + for (j = 0; j < ARRAY_SIZE(port->devs); j++) { + struct airoha_gdm_dev *dev = port->devs[j]; + struct net_device *netdev; + + if (!dev) + continue; + + netdev = netdev_from_priv(dev); + unregister_netdev(netdev); + of_node_put(netdev->dev.of_node); + } airoha_metadata_dst_free(port); } airoha_hw_cleanup(eth); @@ -3314,6 +3537,39 @@ static u32 airoha_en7581_get_vip_port(struct airoha_gdm_port *port, int nbq) return 0; } +static int airoha_en7581_get_dev_from_sport(struct airoha_qdma_desc *desc, + u16 *port, u16 *dev) +{ + u32 sport = FIELD_GET(QDMA_ETH_RXMSG_SPORT_MASK, + le32_to_cpu(READ_ONCE(desc->msg1))); + + *dev = 0; + switch (sport) { + case 0x10 ... 0x14: + *port = 0; /* GDM1 */ + break; + case 0x2 ... 0x4: + *port = sport - 1; + break; + case HSGMII_LAN_7581_PCIE1_SRCPORT: + *dev = 1; + fallthrough; + case HSGMII_LAN_7581_PCIE0_SRCPORT: + *port = 2; /* GDM3 */ + break; + case HSGMII_LAN_7581_USB_SRCPORT: + *dev = 1; + fallthrough; + case HSGMII_LAN_7581_ETH_SRCPORT: + *port = 3; /* GDM4 */ + break; + default: + return -EINVAL; + } + + return 0; +} + static const char * const an7583_xsi_rsts_names[] = { "xsi-mac", "hsi0-mac", @@ -3363,6 +3619,36 @@ static u32 airoha_an7583_get_vip_port(struct airoha_gdm_port *port, int nbq) return 0; } +static int airoha_an7583_get_dev_from_sport(struct airoha_qdma_desc *desc, + u16 *port, u16 *dev) +{ + u32 sport = FIELD_GET(QDMA_ETH_RXMSG_SPORT_MASK, + le32_to_cpu(READ_ONCE(desc->msg1))); + + *dev = 0; + switch (sport) { + case 0x10 ... 0x14: + *port = 0; /* GDM1 */ + break; + case 0x2 ... 0x4: + *port = sport - 1; + break; + case HSGMII_LAN_7583_ETH_SRCPORT: + *port = 2; /* GDM3 */ + break; + case HSGMII_LAN_7583_USB_SRCPORT: + *dev = 1; + fallthrough; + case HSGMII_LAN_7583_PCIE_SRCPORT: + *port = 3; /* GDM4 */ + break; + default: + return -EINVAL; + } + + return 0; +} + static const struct airoha_eth_soc_data en7581_soc_data = { .version = 0x7581, .xsi_rsts_names = en7581_xsi_rsts_names, @@ -3371,6 +3657,7 @@ static const struct airoha_eth_soc_data en7581_soc_data = { .ops = { .get_sport = airoha_en7581_get_sport, .get_vip_port = airoha_en7581_get_vip_port, + .get_dev_from_sport = airoha_en7581_get_dev_from_sport, }, }; @@ -3382,6 +3669,7 @@ static const struct airoha_eth_soc_data an7583_soc_data = { .ops = { .get_sport = airoha_an7583_get_sport, .get_vip_port = airoha_an7583_get_vip_port, + .get_dev_from_sport = airoha_an7583_get_dev_from_sport, }, }; diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h index fbb50dc73af8..8f42973f9cf5 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.h +++ b/drivers/net/ethernet/airoha/airoha_eth.h @@ -17,6 +17,7 @@ #include #define AIROHA_MAX_NUM_GDM_PORTS 4 +#define AIROHA_MAX_NUM_GDM_DEVS 2 #define AIROHA_MAX_NUM_QDMA 2 #define AIROHA_MAX_NUM_IRQ_BANKS 4 #define AIROHA_MAX_DSA_PORTS 7 @@ -537,22 +538,28 @@ struct airoha_qdma { DECLARE_BITMAP(qos_channel_map, AIROHA_NUM_QOS_CHANNELS); }; +enum airoha_priv_flags { + AIROHA_PRIV_F_WAN = BIT(0), +}; + struct airoha_gdm_dev { struct airoha_gdm_port *port; struct airoha_qdma *qdma; - struct net_device *dev; struct airoha_eth *eth; DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS); /* qos stats counters */ u64 cpu_tx_packets; u64 fwd_tx_packets; + + u32 flags; + int nbq; }; struct airoha_gdm_port { - struct airoha_gdm_dev *dev; + struct airoha_gdm_dev *devs[AIROHA_MAX_NUM_GDM_DEVS]; int id; - int nbq; + int users; struct airoha_hw_stats stats; @@ -588,6 +595,8 @@ struct airoha_eth_soc_data { struct { int (*get_sport)(struct airoha_gdm_port *port, int nbq); u32 (*get_vip_port)(struct airoha_gdm_port *port, int nbq); + int (*get_dev_from_sport)(struct airoha_qdma_desc *desc, + u16 *port, u16 *dev); } ops; }; @@ -650,13 +659,7 @@ static inline u16 airoha_qdma_get_txq(struct airoha_qdma *qdma, u16 qid) static inline bool airoha_is_lan_gdm_dev(struct airoha_gdm_dev *dev) { - struct airoha_gdm_port *port = dev->port; - - /* GDM1 port on EN7581 SoC is connected to the lan dsa switch. - * GDM{2,3,4} can be used as wan port connected to an external - * phy module. - */ - return port->id == 1; + return !(dev->flags & AIROHA_PRIV_F_WAN); } static inline bool airoha_is_7581(struct airoha_eth *eth) @@ -680,7 +683,7 @@ void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb, int airoha_ppe_setup_tc_block_cb(struct airoha_ppe_dev *dev, void *type_data); int airoha_ppe_init(struct airoha_eth *eth); void airoha_ppe_deinit(struct airoha_eth *eth); -void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev); +void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev, const u8 *addr); u32 airoha_ppe_get_total_num_entries(struct airoha_ppe *ppe); struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe, u32 hash); diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c index 047141b2d6d8..91bcc55a6ac6 100644 --- a/drivers/net/ethernet/airoha/airoha_ppe.c +++ b/drivers/net/ethernet/airoha/airoha_ppe.c @@ -167,9 +167,7 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe) airoha_fe_clear(eth, REG_PPE_PPE_FLOW_CFG(i), PPE_FLOW_CFG_IP6_6RD_MASK); - for (p = 0; p < ARRAY_SIZE(eth->ports); p++) { - struct airoha_gdm_port *port = eth->ports[p]; - + for (p = 0; p < ARRAY_SIZE(eth->ports); p++) airoha_fe_rmw(eth, REG_PPE_MTU(i, p), FP0_EGRESS_MTU_MASK | FP1_EGRESS_MTU_MASK, @@ -177,11 +175,27 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe) AIROHA_MAX_MTU) | FIELD_PREP(FP1_EGRESS_MTU_MASK, AIROHA_MAX_MTU)); - if (!port) + } + + for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { + struct airoha_gdm_port *port = eth->ports[i]; + int j; + + if (!port) + continue; + + for (j = 0; j < ARRAY_SIZE(port->devs); j++) { + struct airoha_gdm_dev *dev = port->devs[j]; + int ppe_id; + u8 fport; + + if (!dev) continue; - airoha_ppe_set_cpu_port(port->dev, i, - airoha_get_fe_port(port->dev)); + ppe_id = !airoha_is_lan_gdm_dev(dev) && + airoha_ppe_is_enabled(eth, 1); + fport = airoha_get_fe_port(dev); + airoha_ppe_set_cpu_port(dev, ppe_id, fport); } } } @@ -341,7 +355,7 @@ static int airoha_ppe_foe_entry_prepare(struct airoha_eth *eth, return -EINVAL; port = dev->port; - if (dsa_port >= 0 || eth->ports[1]) + if (dsa_port >= 0 || airoha_is_lan_gdm_dev(dev)) pse_port = port->id == 4 ? FE_PSE_PORT_GDM4 : port->id; else @@ -1473,12 +1487,10 @@ void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb, airoha_ppe_foe_insert_entry(ppe, skb, hash, rx_wlan); } -void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev) +void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev, const u8 *addr) { struct airoha_gdm_port *port = dev->port; - struct net_device *netdev = dev->dev; struct airoha_eth *eth = dev->eth; - const u8 *addr = netdev->dev_addr; u32 val; val = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) | addr[5];