diff options
Diffstat (limited to 'drivers/net/ethernet/socionext/netsec.c')
| -rw-r--r-- | drivers/net/ethernet/socionext/netsec.c | 201 |
1 files changed, 115 insertions, 86 deletions
diff --git a/drivers/net/ethernet/socionext/netsec.c b/drivers/net/ethernet/socionext/netsec.c index 328bc38848bb..ee890de69ffe 100644 --- a/drivers/net/ethernet/socionext/netsec.c +++ b/drivers/net/ethernet/socionext/netsec.c @@ -6,6 +6,7 @@ #include <linux/pm_runtime.h> #include <linux/acpi.h> #include <linux/of_mdio.h> +#include <linux/of_net.h> #include <linux/etherdevice.h> #include <linux/interrupt.h> #include <linux/io.h> @@ -14,7 +15,7 @@ #include <linux/bpf_trace.h> #include <net/tcp.h> -#include <net/page_pool.h> +#include <net/page_pool/helpers.h> #include <net/ip6_checksum.h> #define NETSEC_REG_SOFT_RST 0x104 @@ -525,13 +526,15 @@ static int netsec_phy_read(struct mii_bus *bus, int phy_addr, int reg_addr) static void netsec_et_get_drvinfo(struct net_device *net_device, struct ethtool_drvinfo *info) { - strlcpy(info->driver, "netsec", sizeof(info->driver)); - strlcpy(info->bus_info, dev_name(net_device->dev.parent), + strscpy(info->driver, "netsec", sizeof(info->driver)); + strscpy(info->bus_info, dev_name(net_device->dev.parent), sizeof(info->bus_info)); } static int netsec_et_get_coalesce(struct net_device *net_device, - struct ethtool_coalesce *et_coalesce) + struct ethtool_coalesce *et_coalesce, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct netsec_priv *priv = netdev_priv(net_device); @@ -541,7 +544,9 @@ static int netsec_et_get_coalesce(struct net_device *net_device, } static int netsec_et_set_coalesce(struct net_device *net_device, - struct ethtool_coalesce *et_coalesce) + struct ethtool_coalesce *et_coalesce, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct netsec_priv *priv = netdev_priv(net_device); @@ -630,6 +635,7 @@ static void netsec_set_rx_de(struct netsec_priv *priv, static bool netsec_clean_tx_dring(struct netsec_priv *priv) { struct netsec_desc_ring *dring = &priv->desc_ring[NETSEC_RING_TX]; + struct xdp_frame_bulk bq; struct netsec_de *entry; int tail = dring->tail; unsigned int bytes; @@ -638,8 +644,11 @@ static bool netsec_clean_tx_dring(struct netsec_priv *priv) spin_lock(&dring->lock); bytes = 0; + xdp_frame_bulk_init(&bq); entry = dring->vaddr + DESC_SZ * tail; + rcu_read_lock(); /* need for xdp_return_frame_bulk */ + while (!(entry->attr & (1U << NETSEC_TX_SHIFT_OWN_FIELD)) && cnt < DESC_NUM) { struct netsec_desc *desc; @@ -664,7 +673,10 @@ static bool netsec_clean_tx_dring(struct netsec_priv *priv) dev_kfree_skb(desc->skb); } else { bytes += desc->xdpf->len; - xdp_return_frame(desc->xdpf); + if (desc->buf_type == TYPE_NETSEC_XDP_TX) + xdp_return_frame_rx_napi(desc->xdpf); + else + xdp_return_frame_bulk(desc->xdpf, &bq); } next: /* clean up so netsec_uninit_pkt_dring() won't free the skb @@ -683,6 +695,9 @@ next: entry = dring->vaddr + DESC_SZ * tail; cnt++; } + xdp_flush_frame_bulk(&bq); + + rcu_read_unlock(); spin_unlock(&dring->lock); @@ -765,7 +780,7 @@ static void netsec_finalize_xdp_rx(struct netsec_priv *priv, u32 xdp_res, u16 pkts) { if (xdp_res & NETSEC_XDP_REDIR) - xdp_do_flush_map(); + xdp_do_flush(); if (xdp_res & NETSEC_XDP_TX) netsec_xdp_ring_tx_db(priv, pkts); @@ -918,11 +933,11 @@ static u32 netsec_run_xdp(struct netsec_priv *priv, struct bpf_prog *prog, } break; default: - bpf_warn_invalid_xdp_action(act); - /* fall through */ + bpf_warn_invalid_xdp_action(priv->ndev, prog, act); + fallthrough; case XDP_ABORTED: trace_xdp_exception(priv->ndev, prog, act); - /* fall through -- handle aborts by dropping packet */ + fallthrough; /* handle aborts by dropping packet */ case XDP_DROP: ret = NETSEC_XDP_CONSUMED; page = virt_to_head_page(xdp->data); @@ -945,10 +960,8 @@ static int netsec_process_rx(struct netsec_priv *priv, int budget) u32 xdp_act = 0; int done = 0; - xdp.rxq = &dring->xdp_rxq; - xdp.frame_sz = PAGE_SIZE; + xdp_init_buff(&xdp, PAGE_SIZE, &dring->xdp_rxq); - rcu_read_lock(); xdp_prog = READ_ONCE(priv->xdp_prog); dma_dir = page_pool_get_dma_dir(dring->page_pool); @@ -957,7 +970,7 @@ static int netsec_process_rx(struct netsec_priv *priv, int budget) struct netsec_de *de = dring->vaddr + (DESC_SZ * idx); struct netsec_desc *desc = &dring->desc[idx]; struct page *page = virt_to_page(desc->addr); - u32 xdp_result = NETSEC_XDP_PASS; + u32 metasize, xdp_result = NETSEC_XDP_PASS; struct sk_buff *skb = NULL; u16 pkt_len, desc_len; dma_addr_t dma_handle; @@ -1005,10 +1018,8 @@ static int netsec_process_rx(struct netsec_priv *priv, int budget) dma_dir); prefetch(desc->addr); - xdp.data_hard_start = desc->addr; - xdp.data = desc->addr + NETSEC_RXBUF_HEADROOM; - xdp_set_data_meta_invalid(&xdp); - xdp.data_end = xdp.data + pkt_len; + xdp_prepare_buff(&xdp, desc->addr, NETSEC_RXBUF_HEADROOM, + pkt_len, true); if (xdp_prog) { xdp_result = netsec_run_xdp(priv, xdp_prog, &xdp); @@ -1033,10 +1044,13 @@ static int netsec_process_rx(struct netsec_priv *priv, int budget) "rx failed to build skb\n"); break; } - page_pool_release_page(dring->page_pool, page); + skb_mark_for_recycle(skb); skb_reserve(skb, xdp.data - xdp.data_hard_start); skb_put(skb, xdp.data_end - xdp.data); + metasize = xdp.data - xdp.data_meta; + if (metasize) + skb_metadata_set(skb, metasize); skb->protocol = eth_type_trans(skb, priv->ndev); if (priv->rx_cksum_offload_flag && @@ -1044,8 +1058,9 @@ static int netsec_process_rx(struct netsec_priv *priv, int budget) skb->ip_summed = CHECKSUM_UNNECESSARY; next: - if ((skb && napi_gro_receive(&priv->napi, skb) != GRO_DROP) || - xdp_result) { + if (skb) + napi_gro_receive(&priv->napi, skb); + if (skb || xdp_result) { ndev->stats.rx_packets++; ndev->stats.rx_bytes += xdp.data_end - xdp.data; } @@ -1060,8 +1075,6 @@ next: } netsec_finalize_xdp_rx(priv, xdp_act, xdp_xmit); - rcu_read_unlock(); - return done; } @@ -1292,6 +1305,8 @@ static int netsec_setup_rx_dring(struct netsec_priv *priv) .dma_dir = xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE, .offset = NETSEC_RXBUF_HEADROOM, .max_len = NETSEC_RX_BUF_SIZE, + .napi = &priv->napi, + .netdev = priv->ndev, }; int i, err; @@ -1302,7 +1317,7 @@ static int netsec_setup_rx_dring(struct netsec_priv *priv) goto err_out; } - err = xdp_rxq_info_reg(&dring->xdp_rxq, priv->ndev, 0); + err = xdp_rxq_info_reg(&dring->xdp_rxq, priv->ndev, 0, priv->napi.napi_id); if (err) goto err_out; @@ -1538,7 +1553,7 @@ static int netsec_start_gmac(struct netsec_priv *priv) netsec_write(priv, NETSEC_REG_NRM_RX_INTEN_CLR, ~0); netsec_write(priv, NETSEC_REG_NRM_TX_INTEN_CLR, ~0); - netsec_et_set_coalesce(priv->ndev, &priv->et_coalesce); + netsec_et_set_coalesce(priv->ndev, &priv->et_coalesce, NULL, NULL); if (netsec_mac_write(priv, GMAC_REG_OMR, value)) return -ETIMEDOUT; @@ -1706,14 +1721,17 @@ static int netsec_netdev_init(struct net_device *ndev) goto err1; /* set phy power down */ - data = netsec_phy_read(priv->mii_bus, priv->phy_addr, MII_BMCR) | - BMCR_PDOWN; - netsec_phy_write(priv->mii_bus, priv->phy_addr, MII_BMCR, data); + data = netsec_phy_read(priv->mii_bus, priv->phy_addr, MII_BMCR); + netsec_phy_write(priv->mii_bus, priv->phy_addr, MII_BMCR, + data | BMCR_PDOWN); ret = netsec_reset_hardware(priv, true); if (ret) goto err2; + /* Restore phy power state */ + netsec_phy_write(priv->mii_bus, priv->phy_addr, MII_BMCR, data); + spin_lock_init(&priv->desc_ring[NETSEC_RING_TX].lock); spin_lock_init(&priv->desc_ring[NETSEC_RING_RX].lock); @@ -1748,8 +1766,7 @@ static int netsec_xdp_xmit(struct net_device *ndev, int n, { struct netsec_priv *priv = netdev_priv(ndev); struct netsec_desc_ring *tx_ring = &priv->desc_ring[NETSEC_RING_TX]; - int drops = 0; - int i; + int i, nxmit = 0; if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK)) return -EINVAL; @@ -1760,12 +1777,11 @@ static int netsec_xdp_xmit(struct net_device *ndev, int n, int err; err = netsec_xdp_queue_one(priv, xdpf, true); - if (err != NETSEC_XDP_TX) { - xdp_return_frame_rx_napi(xdpf); - drops++; - } else { - tx_ring->xdp_xmit++; - } + if (err != NETSEC_XDP_TX) + break; + + tx_ring->xdp_xmit++; + nxmit++; } spin_unlock(&tx_ring->lock); @@ -1774,7 +1790,7 @@ static int netsec_xdp_xmit(struct net_device *ndev, int n, tx_ring->xdp_xmit = 0; } - return n - drops; + return nxmit; } static int netsec_xdp_setup(struct netsec_priv *priv, struct bpf_prog *prog, @@ -1810,9 +1826,6 @@ static int netsec_xdp(struct net_device *ndev, struct netdev_bpf *xdp) switch (xdp->command) { case XDP_SETUP_PROG: return netsec_xdp_setup(priv, xdp->prog, xdp->extack); - case XDP_QUERY_PROG: - xdp->prog_id = priv->xdp_prog ? priv->xdp_prog->aux->id : 0; - return 0; default: return -EINVAL; } @@ -1827,7 +1840,7 @@ static const struct net_device_ops netsec_netdev_ops = { .ndo_set_features = netsec_netdev_set_features, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, - .ndo_do_ioctl = phy_do_ioctl, + .ndo_eth_ioctl = phy_do_ioctl, .ndo_xdp_xmit = netsec_xdp_xmit, .ndo_bpf = netsec_xdp, }; @@ -1835,6 +1848,25 @@ static const struct net_device_ops netsec_netdev_ops = { static int netsec_of_probe(struct platform_device *pdev, struct netsec_priv *priv, u32 *phy_addr) { + int err; + + err = of_get_phy_mode(pdev->dev.of_node, &priv->phy_interface); + if (err) { + dev_err(&pdev->dev, "missing required property 'phy-mode'\n"); + return err; + } + + /* + * SynQuacer is physically configured with TX and RX delays + * but the standard firmware claimed otherwise for a long + * time, ignore it. + */ + if (of_machine_is_compatible("socionext,developer-box") && + priv->phy_interface != PHY_INTERFACE_MODE_RGMII_ID) { + dev_warn(&pdev->dev, "Outdated firmware reports incorrect PHY mode, overriding\n"); + priv->phy_interface = PHY_INTERFACE_MODE_RGMII_ID; + } + priv->phy_np = of_parse_phandle(pdev->dev.of_node, "phy-handle", 0); if (!priv->phy_np) { dev_err(&pdev->dev, "missing required property 'phy-handle'\n"); @@ -1844,10 +1876,9 @@ static int netsec_of_probe(struct platform_device *pdev, *phy_addr = of_mdio_parse_addr(&pdev->dev, priv->phy_np); priv->clk = devm_clk_get(&pdev->dev, NULL); /* get by 'phy_ref_clk' */ - if (IS_ERR(priv->clk)) { - dev_err(&pdev->dev, "phy_ref_clk not found\n"); - return PTR_ERR(priv->clk); - } + if (IS_ERR(priv->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk), + "phy_ref_clk not found\n"); priv->freq = clk_get_rate(priv->clk); return 0; @@ -1861,20 +1892,26 @@ static int netsec_acpi_probe(struct platform_device *pdev, if (!IS_ENABLED(CONFIG_ACPI)) return -ENODEV; + /* ACPI systems are assumed to configure the PHY in firmware, so + * there is really no need to discover the PHY mode from the DSDT. + * Since firmware is known to exist in the field that configures the + * PHY correctly but passes the wrong mode string in the phy-mode + * device property, we have no choice but to ignore it. + */ + priv->phy_interface = PHY_INTERFACE_MODE_NA; + ret = device_property_read_u32(&pdev->dev, "phy-channel", phy_addr); - if (ret) { - dev_err(&pdev->dev, - "missing required property 'phy-channel'\n"); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "missing required property 'phy-channel'\n"); ret = device_property_read_u32(&pdev->dev, "socionext,phy-clock-frequency", &priv->freq); if (ret) - dev_err(&pdev->dev, - "missing required property 'socionext,phy-clock-frequency'\n"); - return ret; + return dev_err_probe(&pdev->dev, ret, + "missing required property 'socionext,phy-clock-frequency'\n"); + return 0; } static void netsec_unregister_mdio(struct netsec_priv *priv) @@ -1940,11 +1977,13 @@ static int netsec_register_mdio(struct netsec_priv *priv, u32 phy_addr) ret = PTR_ERR(priv->phydev); dev_err(priv->dev, "get_phy_device err(%d)\n", ret); priv->phydev = NULL; + mdiobus_unregister(bus); return -ENODEV; } ret = phy_device_register(priv->phydev); if (ret) { + phy_device_free(priv->phydev); mdiobus_unregister(bus); dev_err(priv->dev, "phy_device_register err(%d)\n", ret); @@ -1956,12 +1995,12 @@ static int netsec_register_mdio(struct netsec_priv *priv, u32 phy_addr) static int netsec_probe(struct platform_device *pdev) { - struct resource *mmio_res, *eeprom_res, *irq_res; - u8 *mac, macbuf[ETH_ALEN]; + struct resource *mmio_res, *eeprom_res; struct netsec_priv *priv; u32 hw_ver, phy_addr = 0; struct net_device *ndev; int ret; + int irq; mmio_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mmio_res) { @@ -1975,11 +2014,9 @@ static int netsec_probe(struct platform_device *pdev) return -ENODEV; } - irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!irq_res) { - dev_err(&pdev->dev, "No IRQ resource found.\n"); - return -ENODEV; - } + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; ndev = alloc_etherdev(sizeof(*priv)); if (!ndev) @@ -1990,20 +2027,13 @@ static int netsec_probe(struct platform_device *pdev) spin_lock_init(&priv->reglock); SET_NETDEV_DEV(ndev, &pdev->dev); platform_set_drvdata(pdev, priv); - ndev->irq = irq_res->start; + ndev->irq = irq; priv->dev = &pdev->dev; priv->ndev = ndev; priv->msg_enable = NETIF_MSG_TX_ERR | NETIF_MSG_HW | NETIF_MSG_DRV | NETIF_MSG_LINK | NETIF_MSG_PROBE; - priv->phy_interface = device_get_phy_mode(&pdev->dev); - if ((int)priv->phy_interface < 0) { - dev_err(&pdev->dev, "missing required property 'phy-mode'\n"); - ret = -ENODEV; - goto free_ndev; - } - priv->ioaddr = devm_ioremap(&pdev->dev, mmio_res->start, resource_size(mmio_res)); if (!priv->ioaddr) { @@ -2020,21 +2050,19 @@ static int netsec_probe(struct platform_device *pdev) goto free_ndev; } - mac = device_get_mac_address(&pdev->dev, macbuf, sizeof(macbuf)); - if (mac) - ether_addr_copy(ndev->dev_addr, mac); - - if (priv->eeprom_base && - (!mac || !is_valid_ether_addr(ndev->dev_addr))) { + ret = device_get_ethdev_address(&pdev->dev, ndev); + if (ret && priv->eeprom_base) { void __iomem *macp = priv->eeprom_base + NETSEC_EEPROM_MAC_ADDRESS; - - ndev->dev_addr[0] = readb(macp + 3); - ndev->dev_addr[1] = readb(macp + 2); - ndev->dev_addr[2] = readb(macp + 1); - ndev->dev_addr[3] = readb(macp + 0); - ndev->dev_addr[4] = readb(macp + 7); - ndev->dev_addr[5] = readb(macp + 6); + u8 addr[ETH_ALEN]; + + addr[0] = readb(macp + 3); + addr[1] = readb(macp + 2); + addr[2] = readb(macp + 1); + addr[3] = readb(macp + 0); + addr[4] = readb(macp + 7); + addr[5] = readb(macp + 6); + eth_hw_addr_set(ndev, addr); } if (!is_valid_ether_addr(ndev->dev_addr)) { @@ -2083,7 +2111,7 @@ static int netsec_probe(struct platform_device *pdev) dev_info(&pdev->dev, "hardware revision %d.%d\n", hw_ver >> 16, hw_ver & 0xffff); - netif_napi_add(ndev, &priv->napi, netsec_napi_poll, NAPI_POLL_WEIGHT); + netif_napi_add(ndev, &priv->napi, netsec_napi_poll); ndev->netdev_ops = &netsec_netdev_ops; ndev->ethtool_ops = &netsec_ethtool_ops; @@ -2092,6 +2120,9 @@ static int netsec_probe(struct platform_device *pdev) NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; ndev->hw_features = ndev->features; + ndev->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT | + NETDEV_XDP_ACT_NDO_XMIT; + priv->rx_cksum_offload_flag = true; ret = netsec_register_mdio(priv, phy_addr); @@ -2124,7 +2155,7 @@ free_ndev: return ret; } -static int netsec_remove(struct platform_device *pdev) +static void netsec_remove(struct platform_device *pdev) { struct netsec_priv *priv = platform_get_drvdata(pdev); @@ -2136,8 +2167,6 @@ static int netsec_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); free_netdev(priv->ndev); - - return 0; } #ifdef CONFIG_PM @@ -2185,7 +2214,7 @@ MODULE_DEVICE_TABLE(acpi, netsec_acpi_ids); static struct platform_driver netsec_driver = { .probe = netsec_probe, - .remove = netsec_remove, + .remove = netsec_remove, .driver = { .name = "netsec", .pm = &netsec_pm_ops, |
