diff options
Diffstat (limited to 'drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c')
-rw-r--r-- | drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c | 182 |
1 files changed, 139 insertions, 43 deletions
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index 19379bae0144..7ff147e89426 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* Copyright 2014-2016 Freescale Semiconductor Inc. - * Copyright 2016-2017 NXP + * Copyright 2016-2019 NXP */ #include <linux/init.h> #include <linux/module.h> @@ -221,6 +221,7 @@ static void xdp_release_buf(struct dpaa2_eth_priv *priv, struct dpaa2_eth_channel *ch, dma_addr_t addr) { + int retries = 0; int err; ch->xdp.drop_bufs[ch->xdp.drop_cnt++] = addr; @@ -229,8 +230,11 @@ static void xdp_release_buf(struct dpaa2_eth_priv *priv, while ((err = dpaa2_io_service_release(ch->dpio, priv->bpid, ch->xdp.drop_bufs, - ch->xdp.drop_cnt)) == -EBUSY) + ch->xdp.drop_cnt)) == -EBUSY) { + if (retries++ >= DPAA2_ETH_SWP_BUSY_RETRIES) + break; cpu_relax(); + } if (err) { free_bufs(priv, ch->xdp.drop_bufs, ch->xdp.drop_cnt); @@ -458,7 +462,7 @@ static int consume_frames(struct dpaa2_eth_channel *ch, struct dpaa2_eth_fq *fq = NULL; struct dpaa2_dq *dq; const struct dpaa2_fd *fd; - int cleaned = 0; + int cleaned = 0, retries = 0; int is_last; do { @@ -469,6 +473,11 @@ static int consume_frames(struct dpaa2_eth_channel *ch, * the store until we get some sort of valid response * token (either a valid frame or an "empty dequeue") */ + if (retries++ >= DPAA2_ETH_SWP_BUSY_RETRIES) { + netdev_err_once(priv->net_dev, + "Unable to read a valid dequeue response\n"); + return -ETIMEDOUT; + } continue; } @@ -477,6 +486,7 @@ static int consume_frames(struct dpaa2_eth_channel *ch, fq->consume(priv, ch, fd, fq); cleaned++; + retries = 0; } while (!is_last); if (!cleaned) @@ -949,6 +959,7 @@ static int add_bufs(struct dpaa2_eth_priv *priv, u64 buf_array[DPAA2_ETH_BUFS_PER_CMD]; struct page *page; dma_addr_t addr; + int retries = 0; int i, err; for (i = 0; i < DPAA2_ETH_BUFS_PER_CMD; i++) { @@ -980,8 +991,11 @@ static int add_bufs(struct dpaa2_eth_priv *priv, release_bufs: /* In case the portal is busy, retry until successful */ while ((err = dpaa2_io_service_release(ch->dpio, bpid, - buf_array, i)) == -EBUSY) + buf_array, i)) == -EBUSY) { + if (retries++ >= DPAA2_ETH_SWP_BUSY_RETRIES) + break; cpu_relax(); + } /* If release command failed, clean up and bail out; * not much else we can do about it @@ -1032,16 +1046,21 @@ static int seed_pool(struct dpaa2_eth_priv *priv, u16 bpid) static void drain_bufs(struct dpaa2_eth_priv *priv, int count) { u64 buf_array[DPAA2_ETH_BUFS_PER_CMD]; + int retries = 0; int ret; do { ret = dpaa2_io_service_acquire(NULL, priv->bpid, buf_array, count); if (ret < 0) { + if (ret == -EBUSY && + retries++ >= DPAA2_ETH_SWP_BUSY_RETRIES) + continue; netdev_err(priv->net_dev, "dpaa2_io_service_acquire() failed\n"); return; } free_bufs(priv, buf_array, ret); + retries = 0; } while (ret); } @@ -1094,7 +1113,7 @@ static int pull_channel(struct dpaa2_eth_channel *ch) ch->store); dequeues++; cpu_relax(); - } while (err == -EBUSY); + } while (err == -EBUSY && dequeues < DPAA2_ETH_SWP_BUSY_RETRIES); ch->stats.dequeue_portal_busy += dequeues; if (unlikely(err)) @@ -1118,6 +1137,7 @@ static int dpaa2_eth_poll(struct napi_struct *napi, int budget) struct netdev_queue *nq; int store_cleaned, work_done; struct list_head rx_list; + int retries = 0; int err; ch = container_of(napi, struct dpaa2_eth_channel, napi); @@ -1136,7 +1156,7 @@ static int dpaa2_eth_poll(struct napi_struct *napi, int budget) refill_pool(priv, ch, priv->bpid); store_cleaned = consume_frames(ch, &fq); - if (!store_cleaned) + if (store_cleaned <= 0) break; if (fq->type == DPAA2_RX_FQ) { rx_cleaned += store_cleaned; @@ -1163,7 +1183,7 @@ static int dpaa2_eth_poll(struct napi_struct *napi, int budget) do { err = dpaa2_io_service_rearm(ch->dpio, &ch->nctx); cpu_relax(); - } while (err == -EBUSY); + } while (err == -EBUSY && retries++ < DPAA2_ETH_SWP_BUSY_RETRIES); WARN_ONCE(err, "CDAN notifications rearm failed on core %d", ch->nctx.desired_cpu); @@ -1235,8 +1255,6 @@ static void dpaa2_eth_set_rx_taildrop(struct dpaa2_eth_priv *priv, bool enable) priv->rx_td_enabled = enable; } -static void update_tx_fqids(struct dpaa2_eth_priv *priv); - static int link_state_update(struct dpaa2_eth_priv *priv) { struct dpni_link_state state = {0}; @@ -1258,12 +1276,17 @@ static int link_state_update(struct dpaa2_eth_priv *priv) !!(state.options & DPNI_LINK_OPT_ASYM_PAUSE); dpaa2_eth_set_rx_taildrop(priv, !tx_pause); + /* When we manage the MAC/PHY using phylink there is no need + * to manually update the netif_carrier. + */ + if (priv->mac) + goto out; + /* Chech link state; speed / duplex changes are not treated yet */ if (priv->link_state.up == state.up) goto out; if (state.up) { - update_tx_fqids(priv); netif_carrier_on(priv->net_dev); netif_tx_start_all_queues(priv->net_dev); } else { @@ -1295,17 +1318,21 @@ static int dpaa2_eth_open(struct net_device *net_dev) priv->dpbp_dev->obj_desc.id, priv->bpid); } - /* We'll only start the txqs when the link is actually ready; make sure - * we don't race against the link up notification, which may come - * immediately after dpni_enable(); - */ - netif_tx_stop_all_queues(net_dev); + if (!priv->mac) { + /* We'll only start the txqs when the link is actually ready; + * make sure we don't race against the link up notification, + * which may come immediately after dpni_enable(); + */ + netif_tx_stop_all_queues(net_dev); + + /* Also, explicitly set carrier off, otherwise + * netif_carrier_ok() will return true and cause 'ip link show' + * to report the LOWER_UP flag, even though the link + * notification wasn't even received. + */ + netif_carrier_off(net_dev); + } enable_ch_napi(priv); - /* Also, explicitly set carrier off, otherwise netif_carrier_ok() will - * return true and cause 'ip link show' to report the LOWER_UP flag, - * even though the link notification wasn't even received. - */ - netif_carrier_off(net_dev); err = dpni_enable(priv->mc_io, 0, priv->mc_token); if (err < 0) { @@ -1313,13 +1340,17 @@ static int dpaa2_eth_open(struct net_device *net_dev) goto enable_err; } - /* If the DPMAC object has already processed the link up interrupt, - * we have to learn the link state ourselves. - */ - err = link_state_update(priv); - if (err < 0) { - netdev_err(net_dev, "Can't update link state\n"); - goto link_state_err; + if (!priv->mac) { + /* If the DPMAC object has already processed the link up + * interrupt, we have to learn the link state ourselves. + */ + err = link_state_update(priv); + if (err < 0) { + netdev_err(net_dev, "Can't update link state\n"); + goto link_state_err; + } + } else { + phylink_start(priv->mac->phylink); } return 0; @@ -1394,8 +1425,12 @@ static int dpaa2_eth_stop(struct net_device *net_dev) int dpni_enabled = 0; int retries = 10; - netif_tx_stop_all_queues(net_dev); - netif_carrier_off(net_dev); + if (!priv->mac) { + netif_tx_stop_all_queues(net_dev); + netif_carrier_off(net_dev); + } else { + phylink_stop(priv->mac->phylink); + } /* On dpni_disable(), the MC firmware will: * - stop MAC Rx and wait for all Rx frames to be enqueued to software @@ -1772,11 +1807,8 @@ static int setup_xdp(struct net_device *dev, struct bpf_prog *prog) if (prog && !xdp_mtu_valid(priv, dev->mtu)) return -EINVAL; - if (prog) { - prog = bpf_prog_add(prog, priv->num_channels); - if (IS_ERR(prog)) - return PTR_ERR(prog); - } + if (prog) + bpf_prog_add(prog, priv->num_channels); up = netif_running(dev); need_update = (!!priv->xdp_prog != !!prog); @@ -2046,7 +2078,6 @@ static struct fsl_mc_device *setup_dpcon(struct dpaa2_eth_priv *priv) { struct fsl_mc_device *dpcon; struct device *dev = priv->net_dev->dev.parent; - struct dpcon_attr attrs; int err; err = fsl_mc_object_allocate(to_fsl_mc_device(dev), @@ -2071,12 +2102,6 @@ static struct fsl_mc_device *setup_dpcon(struct dpaa2_eth_priv *priv) goto close; } - err = dpcon_get_attributes(priv->mc_io, 0, dpcon->mc_handle, &attrs); - if (err) { - dev_err(dev, "dpcon_get_attributes() failed\n"); - goto close; - } - err = dpcon_enable(priv->mc_io, 0, dpcon->mc_handle); if (err) { dev_err(dev, "dpcon_enable() failed\n"); @@ -2232,8 +2257,16 @@ err_set_cdan: err_service_reg: free_channel(priv, channel); err_alloc_ch: - if (err == -EPROBE_DEFER) + if (err == -EPROBE_DEFER) { + for (i = 0; i < priv->num_channels; i++) { + channel = priv->channel[i]; + nctx = &channel->nctx; + dpaa2_io_service_deregister(channel->dpio, nctx, dev); + free_channel(priv, channel); + } + priv->num_channels = 0; return err; + } if (cpumask_empty(&priv->dpio_cpumask)) { dev_err(dev, "No cpu with an affine DPIO/DPCON\n"); @@ -3332,12 +3365,56 @@ static int poll_link_state(void *arg) return 0; } +static int dpaa2_eth_connect_mac(struct dpaa2_eth_priv *priv) +{ + struct fsl_mc_device *dpni_dev, *dpmac_dev; + struct dpaa2_mac *mac; + int err; + + dpni_dev = to_fsl_mc_device(priv->net_dev->dev.parent); + dpmac_dev = fsl_mc_get_endpoint(dpni_dev); + if (IS_ERR(dpmac_dev) || dpmac_dev->dev.type != &fsl_mc_bus_dpmac_type) + return 0; + + if (dpaa2_mac_is_type_fixed(dpmac_dev, priv->mc_io)) + return 0; + + mac = kzalloc(sizeof(struct dpaa2_mac), GFP_KERNEL); + if (!mac) + return -ENOMEM; + + mac->mc_dev = dpmac_dev; + mac->mc_io = priv->mc_io; + mac->net_dev = priv->net_dev; + + err = dpaa2_mac_connect(mac); + if (err) { + netdev_err(priv->net_dev, "Error connecting to the MAC endpoint\n"); + kfree(mac); + return err; + } + priv->mac = mac; + + return 0; +} + +static void dpaa2_eth_disconnect_mac(struct dpaa2_eth_priv *priv) +{ + if (!priv->mac) + return; + + dpaa2_mac_disconnect(priv->mac); + kfree(priv->mac); + priv->mac = NULL; +} + static irqreturn_t dpni_irq0_handler_thread(int irq_num, void *arg) { u32 status = ~0; struct device *dev = (struct device *)arg; struct fsl_mc_device *dpni_dev = to_fsl_mc_device(dev); struct net_device *net_dev = dev_get_drvdata(dev); + struct dpaa2_eth_priv *priv = netdev_priv(net_dev); int err; err = dpni_get_irq_status(dpni_dev->mc_io, 0, dpni_dev->mc_handle, @@ -3350,8 +3427,17 @@ static irqreturn_t dpni_irq0_handler_thread(int irq_num, void *arg) if (status & DPNI_IRQ_EVENT_LINK_CHANGED) link_state_update(netdev_priv(net_dev)); - if (status & DPNI_IRQ_EVENT_ENDPOINT_CHANGED) + if (status & DPNI_IRQ_EVENT_ENDPOINT_CHANGED) { set_mac_addr(netdev_priv(net_dev)); + update_tx_fqids(priv); + + rtnl_lock(); + if (priv->mac) + dpaa2_eth_disconnect_mac(priv); + else + dpaa2_eth_connect_mac(priv); + rtnl_unlock(); + } return IRQ_HANDLED; } @@ -3527,6 +3613,10 @@ static int dpaa2_eth_probe(struct fsl_mc_device *dpni_dev) priv->do_link_poll = true; } + err = dpaa2_eth_connect_mac(priv); + if (err) + goto err_connect_mac; + err = register_netdev(net_dev); if (err < 0) { dev_err(dev, "register_netdev() failed\n"); @@ -3541,6 +3631,8 @@ static int dpaa2_eth_probe(struct fsl_mc_device *dpni_dev) return 0; err_netdev_reg: + dpaa2_eth_disconnect_mac(priv); +err_connect_mac: if (priv->do_link_poll) kthread_stop(priv->poll_thread); else @@ -3583,6 +3675,10 @@ static int dpaa2_eth_remove(struct fsl_mc_device *ls_dev) #ifdef CONFIG_DEBUG_FS dpaa2_dbg_remove(priv); #endif + rtnl_lock(); + dpaa2_eth_disconnect_mac(priv); + rtnl_unlock(); + unregister_netdev(net_dev); if (priv->do_link_poll) |