summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/socionext/netsec.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ethernet/socionext/netsec.c')
-rw-r--r--drivers/net/ethernet/socionext/netsec.c201
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,