[PATCH 02/10] dpaa_eth: add support for DPAA Ethernet
Joakim Tjernlund
joakim.tjernlund at transmode.se
Thu Jul 30 00:15:48 AEST 2015
On Wed, 2015-07-22 at 19:16 +0300, Madalin Bucur wrote:
> This introduces the Freescale Data Path Acceleration Architecture
> (DPAA) Ethernet driver (dpaa_eth) that builds upon the DPAA QMan,
> BMan, PAMU and FMan drivers to deliver Ethernet connectivity on
> the Freescale DPAA QorIQ platforms.
>
> Signed-off-by: Madalin Bucur <madalin.bucur at freescale.com>
> ---
> drivers/net/ethernet/freescale/Kconfig | 2 +
> drivers/net/ethernet/freescale/Makefile | 1 +
> drivers/net/ethernet/freescale/dpaa/Kconfig | 46 +
> drivers/net/ethernet/freescale/dpaa/Makefile | 13 +
> drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 827 +++++++++++++
> drivers/net/ethernet/freescale/dpaa/dpaa_eth.h | 447 +++++++
> .../net/ethernet/freescale/dpaa/dpaa_eth_common.c | 1254 ++++++++++++++++++++
> .../net/ethernet/freescale/dpaa/dpaa_eth_common.h | 119 ++
> drivers/net/ethernet/freescale/dpaa/dpaa_eth_sg.c | 406 +++++++
> 9 files changed, 3115 insertions(+)
> create mode 100644 drivers/net/ethernet/freescale/dpaa/Kconfig
> create mode 100644 drivers/net/ethernet/freescale/dpaa/Makefile
> create mode 100644 drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
> create mode 100644 drivers/net/ethernet/freescale/dpaa/dpaa_eth.h
> create mode 100644 drivers/net/ethernet/freescale/dpaa/dpaa_eth_common.c
> create mode 100644 drivers/net/ethernet/freescale/dpaa/dpaa_eth_common.h
> create mode 100644 drivers/net/ethernet/freescale/dpaa/dpaa_eth_sg.c
>
> diff --git a/drivers/net/ethernet/freescale/Kconfig b/drivers/net/ethernet/freescale/Kconfig
> index f3f89cc..92198be 100644
> --- a/drivers/net/ethernet/freescale/Kconfig
> +++ b/drivers/net/ethernet/freescale/Kconfig
> @@ -92,4 +92,6 @@ config GIANFAR
> and MPC86xx family of chips, the eTSEC on LS1021A and the FEC
> on the 8540.
>
> +source "drivers/net/ethernet/freescale/dpaa/Kconfig"
> +
> endif # NET_VENDOR_FREESCALE
> diff --git a/drivers/net/ethernet/freescale/Makefile b/drivers/net/ethernet/freescale/Makefile
> index 4097c58..ae13dc5 100644
> --- a/drivers/net/ethernet/freescale/Makefile
> +++ b/drivers/net/ethernet/freescale/Makefile
> @@ -12,6 +12,7 @@ obj-$(CONFIG_FS_ENET) += fs_enet/
> obj-$(CONFIG_FSL_PQ_MDIO) += fsl_pq_mdio.o
> obj-$(CONFIG_FSL_XGMAC_MDIO) += xgmac_mdio.o
> obj-$(CONFIG_GIANFAR) += gianfar_driver.o
> +obj-$(CONFIG_FSL_DPAA_ETH) += dpaa/
> obj-$(CONFIG_PTP_1588_CLOCK_GIANFAR) += gianfar_ptp.o
> gianfar_driver-objs := gianfar.o \
> gianfar_ethtool.o
> diff --git a/drivers/net/ethernet/freescale/dpaa/Kconfig b/drivers/net/ethernet/freescale/dpaa/Kconfig
> new file mode 100644
> index 0000000..1f3a203
> --- /dev/null
> +++ b/drivers/net/ethernet/freescale/dpaa/Kconfig
> @@ -0,0 +1,46 @@
> +menuconfig FSL_DPAA_ETH
> + tristate "DPAA Ethernet"
> + depends on FSL_SOC && FSL_BMAN && FSL_QMAN && FSL_FMAN
> + select PHYLIB
> + select FSL_FMAN_MAC
> + ---help---
> + Data Path Acceleration Architecture Ethernet driver,
> + supporting the Freescale QorIQ chips.
> + Depends on Freescale Buffer Manager and Queue Manager
> + driver and Frame Manager Driver.
> +
> +if FSL_DPAA_ETH
> +
> +config FSL_DPAA_CS_THRESHOLD_1G
> + hex "Egress congestion threshold on 1G ports"
> + range 0x1000 0x10000000
> + default "0x06000000"
> + ---help---
> + The size in bytes of the egress Congestion State notification threshold on 1G ports.
> + The 1G dTSECs can quite easily be flooded by cores doing Tx in a tight loop
> + (e.g. by sending UDP datagrams at "while(1) speed"),
> + and the larger the frame size, the more acute the problem.
> + So we have to find a balance between these factors:
> + - avoiding the device staying congested for a prolonged time (risking
> + the netdev watchdog to fire - see also the tx_timeout module param);
> + - affecting performance of protocols such as TCP, which otherwise
> + behave well under the congestion notification mechanism;
> + - preventing the Tx cores from tightly-looping (as if the congestion
> + threshold was too low to be effective);
> + - running out of memory if the CS threshold is set too high.
> +
> +config FSL_DPAA_CS_THRESHOLD_10G
> + hex "Egress congestion threshold on 10G ports"
> + range 0x1000 0x20000000
> + default "0x10000000"
> + ---help ---
> + The size in bytes of the egress Congestion State notification threshold on 10G ports.
> +
> +config FSL_DPAA_INGRESS_CS_THRESHOLD
> + hex "Ingress congestion threshold on FMan ports"
> + default "0x10000000"
> + ---help---
> + The size in bytes of the ingress tail-drop threshold on FMan ports.
> + Traffic piling up above this value will be rejected by QMan and discarded by FMan.
> +
> +endif # FSL_DPAA_ETH
> diff --git a/drivers/net/ethernet/freescale/dpaa/Makefile b/drivers/net/ethernet/freescale/dpaa/Makefile
> new file mode 100644
> index 0000000..cf126dd
> --- /dev/null
> +++ b/drivers/net/ethernet/freescale/dpaa/Makefile
> @@ -0,0 +1,13 @@
> +#
> +# Makefile for the Freescale DPAA Ethernet controllers
> +#
> +
> +# Include FMan headers
> +FMAN = $(srctree)/drivers/net/ethernet/freescale/fman
> +ccflags-y += -I$(FMAN)
> +ccflags-y += -I$(FMAN)/inc
> +ccflags-y += -I$(FMAN)/flib
> +
> +obj-$(CONFIG_FSL_DPAA_ETH) += fsl_dpa.o
> +
> +fsl_dpa-objs += dpaa_eth.o dpaa_eth_sg.o dpaa_eth_common.o
> diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
> new file mode 100644
> index 0000000..500d0e3
> --- /dev/null
> +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
> @@ -0,0 +1,827 @@
> +/* Copyright 2008 - 2015 Freescale Semiconductor Inc.
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions are met:
> + * * Redistributions of source code must retain the above copyright
> + * notice, this list of conditions and the following disclaimer.
> + * * Redistributions in binary form must reproduce the above copyright
> + * notice, this list of conditions and the following disclaimer in the
> + * documentation and/or other materials provided with the distribution.
> + * * Neither the name of Freescale Semiconductor nor the
> + * names of its contributors may be used to endorse or promote products
> + * derived from this software without specific prior written permission.
> + *
> + * ALTERNATIVELY, this software may be distributed under the terms of the
> + * GNU General Public License ("GPL") as published by the Free Software
> + * Foundation, either version 2 of that License or (at your option) any
> + * later version.
> + *
> + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
> + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
> + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
> + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
> + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
> + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
> + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
> + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
> + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/of_mdio.h>
> +#include <linux/of_net.h>
> +#include <linux/kthread.h>
> +#include <linux/io.h>
> +#include <linux/if_arp.h>
> +#include <linux/if_vlan.h>
> +#include <linux/icmp.h>
> +#include <linux/ip.h>
> +#include <linux/ipv6.h>
> +#include <linux/udp.h>
> +#include <linux/tcp.h>
> +#include <linux/net.h>
> +#include <linux/if_ether.h>
> +#include <linux/highmem.h>
> +#include <linux/percpu.h>
> +#include <linux/dma-mapping.h>
> +#include <soc/fsl/bman.h>
> +
> +#include "fsl_fman.h"
> +#include "fm_ext.h"
> +#include "fm_port_ext.h"
> +
> +#include "mac.h"
> +#include "dpaa_eth.h"
> +#include "dpaa_eth_common.h"
> +
> +#define DPA_NAPI_WEIGHT 64
> +
> +/* Valid checksum indication */
> +#define DPA_CSUM_VALID 0xFFFF
> +
> +#define DPA_DESCRIPTION "FSL DPAA Ethernet driver"
> +
> +static u8 debug = -1;
> +module_param(debug, byte, S_IRUGO);
> +MODULE_PARM_DESC(debug, "Module/Driver verbosity level");
> +
> +/* This has to work in tandem with the DPA_CS_THRESHOLD_xxx values. */
> +static u16 tx_timeout = 1000;
> +module_param(tx_timeout, ushort, S_IRUGO);
> +MODULE_PARM_DESC(tx_timeout, "The Tx timeout in ms");
> +
> +/* BM */
> +
> +#define DPAA_ETH_MAX_PAD (L1_CACHE_BYTES * 8)
> +
> +static u8 dpa_priv_common_bpid;
> +
> +static void _dpa_rx_error(struct net_device *net_dev,
> + const struct dpa_priv_s *priv,
> + struct dpa_percpu_priv_s *percpu_priv,
> + const struct qm_fd *fd,
> + u32 fqid)
> +{
> + /* limit common, possibly innocuous Rx FIFO Overflow errors'
> + * interference with zero-loss convergence benchmark results.
> + */
> + if (likely(fd->status & FM_FD_STAT_ERR_PHYSICAL))
> + pr_warn_once("non-zero error counters in fman statistics (sysfs)\n");
> + else
> + if (net_ratelimit())
> + netif_err(priv, hw, net_dev, "Err FD status = 0x%08x\n",
> + fd->status & FM_FD_STAT_RX_ERRORS);
> +
> + percpu_priv->stats.rx_errors++;
> +
> + dpa_fd_release(net_dev, fd);
> +}
> +
> +static void _dpa_tx_error(struct net_device *net_dev,
> + const struct dpa_priv_s *priv,
> + struct dpa_percpu_priv_s *percpu_priv,
> + const struct qm_fd *fd,
> + u32 fqid)
> +{
> + struct sk_buff *skb;
> +
> + if (net_ratelimit())
> + netif_warn(priv, hw, net_dev, "FD status = 0x%08x\n",
> + fd->status & FM_FD_STAT_TX_ERRORS);
> +
> + percpu_priv->stats.tx_errors++;
> +
> + /* If we intended the buffers from this frame to go into the bpools
> + * when the FMan transmit was done, we need to put it in manually.
> + */
> + if (fd->bpid != 0xff) {
> + dpa_fd_release(net_dev, fd);
> + return;
> + }
> +
> + skb = _dpa_cleanup_tx_fd(priv, fd);
> + dev_kfree_skb(skb);
> +}
> +
> +static int dpaa_eth_poll(struct napi_struct *napi, int budget)
> +{
> + struct dpa_napi_portal *np =
> + container_of(napi, struct dpa_napi_portal, napi);
> +
> + int cleaned = qman_p_poll_dqrr(np->p, budget);
> +
> + if (cleaned < budget) {
> + int tmp;
> +
> + napi_complete(napi);
> + tmp = qman_p_irqsource_add(np->p, QM_PIRQ_DQRI);
> + DPA_ERR_ON(tmp);
> + }
> +
> + return cleaned;
> +}
> +
> +static void __hot _dpa_tx_conf(struct net_device *net_dev,
> + const struct dpa_priv_s *priv,
> + struct dpa_percpu_priv_s *percpu_priv,
> + const struct qm_fd *fd,
> + u32 fqid)
> +{
> + struct sk_buff *skb;
> +
> + if (unlikely(fd->status & FM_FD_STAT_TX_ERRORS) != 0) {
> + if (net_ratelimit())
> + netif_warn(priv, hw, net_dev, "FD status = 0x%08x\n",
> + fd->status & FM_FD_STAT_TX_ERRORS);
> +
> + percpu_priv->stats.tx_errors++;
> + }
> +
> + skb = _dpa_cleanup_tx_fd(priv, fd);
> +
> + dev_kfree_skb(skb);
> +}
> +
> +static enum qman_cb_dqrr_result
> +priv_rx_error_dqrr(struct qman_portal *portal,
> + struct qman_fq *fq,
> + const struct qm_dqrr_entry *dq)
> +{
> + struct net_device *net_dev;
> + struct dpa_priv_s *priv;
> + struct dpa_percpu_priv_s *percpu_priv;
> + int *count_ptr;
> +
> + net_dev = ((struct dpa_fq *)fq)->net_dev;
> + priv = netdev_priv(net_dev);
> +
> + percpu_priv = raw_cpu_ptr(priv->percpu_priv);
> + count_ptr = raw_cpu_ptr(priv->dpa_bp->percpu_count);
> +
> + if (dpaa_eth_napi_schedule(percpu_priv, portal))
> + return qman_cb_dqrr_stop;
> +
> + if (unlikely(dpaa_eth_refill_bpools(priv->dpa_bp, count_ptr)))
> + /* Unable to refill the buffer pool due to insufficient
> + * system memory. Just release the frame back into the pool,
> + * otherwise we'll soon end up with an empty buffer pool.
> + */
> + dpa_fd_release(net_dev, &dq->fd);
> + else
> + _dpa_rx_error(net_dev, priv, percpu_priv, &dq->fd, fq->fqid);
> +
> + return qman_cb_dqrr_consume;
> +}
> +
> +static enum qman_cb_dqrr_result __hot
> +priv_rx_default_dqrr(struct qman_portal *portal,
> + struct qman_fq *fq,
> + const struct qm_dqrr_entry *dq)
> +{
> + struct net_device *net_dev;
> + struct dpa_priv_s *priv;
> + struct dpa_percpu_priv_s *percpu_priv;
> + int *count_ptr;
> + struct dpa_bp *dpa_bp;
> +
> + net_dev = ((struct dpa_fq *)fq)->net_dev;
> + priv = netdev_priv(net_dev);
> + dpa_bp = priv->dpa_bp;
> +
> + /* IRQ handler, non-migratable; safe to use raw_cpu_ptr here */
> + percpu_priv = raw_cpu_ptr(priv->percpu_priv);
> + count_ptr = raw_cpu_ptr(dpa_bp->percpu_count);
> +
> + if (unlikely(dpaa_eth_napi_schedule(percpu_priv, portal)))
> + return qman_cb_dqrr_stop;
> +
> + /* Vale of plenty: make sure we didn't run out of buffers */
> +
> + if (unlikely(dpaa_eth_refill_bpools(dpa_bp, count_ptr)))
> + /* Unable to refill the buffer pool due to insufficient
> + * system memory. Just release the frame back into the pool,
> + * otherwise we'll soon end up with an empty buffer pool.
> + */
> + dpa_fd_release(net_dev, &dq->fd);
> + else
> + _dpa_rx(net_dev, portal, priv, percpu_priv, &dq->fd, fq->fqid,
> + count_ptr);
> +
> + return qman_cb_dqrr_consume;
> +}
> +
> +static enum qman_cb_dqrr_result
> +priv_tx_conf_error_dqrr(struct qman_portal *portal,
> + struct qman_fq *fq,
> + const struct qm_dqrr_entry *dq)
> +{
> + struct net_device *net_dev;
> + struct dpa_priv_s *priv;
> + struct dpa_percpu_priv_s *percpu_priv;
> +
> + net_dev = ((struct dpa_fq *)fq)->net_dev;
> + priv = netdev_priv(net_dev);
> +
> + percpu_priv = raw_cpu_ptr(priv->percpu_priv);
> +
> + if (dpaa_eth_napi_schedule(percpu_priv, portal))
> + return qman_cb_dqrr_stop;
> +
> + _dpa_tx_error(net_dev, priv, percpu_priv, &dq->fd, fq->fqid);
> +
> + return qman_cb_dqrr_consume;
> +}
> +
> +static enum qman_cb_dqrr_result __hot
> +priv_tx_conf_default_dqrr(struct qman_portal *portal,
> + struct qman_fq *fq,
> + const struct qm_dqrr_entry *dq)
> +{
> + struct net_device *net_dev;
> + struct dpa_priv_s *priv;
> + struct dpa_percpu_priv_s *percpu_priv;
> +
> + net_dev = ((struct dpa_fq *)fq)->net_dev;
> + priv = netdev_priv(net_dev);
> +
> + /* Non-migratable context, safe to use raw_cpu_ptr */
> + percpu_priv = raw_cpu_ptr(priv->percpu_priv);
> +
> + if (dpaa_eth_napi_schedule(percpu_priv, portal))
> + return qman_cb_dqrr_stop;
> +
> + _dpa_tx_conf(net_dev, priv, percpu_priv, &dq->fd, fq->fqid);
> +
> + return qman_cb_dqrr_consume;
> +}
> +
> +static void priv_ern(struct qman_portal *portal,
> + struct qman_fq *fq,
> + const struct qm_mr_entry *msg)
> +{
> + struct net_device *net_dev;
> + const struct dpa_priv_s *priv;
> + struct sk_buff *skb;
> + struct dpa_percpu_priv_s *percpu_priv;
> + const struct qm_fd *fd = &msg->ern.fd;
> +
> + net_dev = ((struct dpa_fq *)fq)->net_dev;
> + priv = netdev_priv(net_dev);
> + /* Non-migratable context, safe to use raw_cpu_ptr */
> + percpu_priv = raw_cpu_ptr(priv->percpu_priv);
> +
> + percpu_priv->stats.tx_dropped++;
> + percpu_priv->stats.tx_fifo_errors++;
> +
> + /* If we intended this buffer to go into the pool
> + * when the FM was done, we need to put it in
> + * manually.
> + */
> + if (msg->ern.fd.bpid != 0xff) {
> + dpa_fd_release(net_dev, fd);
> + return;
> + }
> +
> + skb = _dpa_cleanup_tx_fd(priv, fd);
> + dev_kfree_skb_any(skb);
> +}
> +
> +static const struct dpa_fq_cbs_t private_fq_cbs = {
> + .rx_defq = { .cb = { .dqrr = priv_rx_default_dqrr } },
> + .tx_defq = { .cb = { .dqrr = priv_tx_conf_default_dqrr } },
> + .rx_errq = { .cb = { .dqrr = priv_rx_error_dqrr } },
> + .tx_errq = { .cb = { .dqrr = priv_tx_conf_error_dqrr } },
> + .egress_ern = { .cb = { .ern = priv_ern } }
> +};
> +
> +static void dpaa_eth_napi_enable(struct dpa_priv_s *priv)
> +{
> + struct dpa_percpu_priv_s *percpu_priv;
> + int i, j;
> +
> + for_each_possible_cpu(i) {
> + percpu_priv = per_cpu_ptr(priv->percpu_priv, i);
> +
> + for (j = 0; j < qman_portal_max; j++)
> + napi_enable(&percpu_priv->np[j].napi);
> + }
> +}
> +
> +static void dpaa_eth_napi_disable(struct dpa_priv_s *priv)
> +{
> + struct dpa_percpu_priv_s *percpu_priv;
> + int i, j;
> +
> + for_each_possible_cpu(i) {
> + percpu_priv = per_cpu_ptr(priv->percpu_priv, i);
> +
> + for (j = 0; j < qman_portal_max; j++)
> + napi_disable(&percpu_priv->np[j].napi);
> + }
> +}
> +
> +static int dpa_eth_priv_start(struct net_device *net_dev)
> +{
> + int err;
> + struct dpa_priv_s *priv;
> +
> + priv = netdev_priv(net_dev);
> +
> + dpaa_eth_napi_enable(priv);
> +
> + err = dpa_start(net_dev);
> + if (err < 0)
> + dpaa_eth_napi_disable(priv);
> +
> + return err;
> +}
> +
> +static int dpa_eth_priv_stop(struct net_device *net_dev)
> +{
> + int err;
> + struct dpa_priv_s *priv;
> +
> + err = dpa_stop(net_dev);
> + /* Allow NAPI to consume any frame still in the Rx/TxConfirm
> + * ingress queues. This is to avoid a race between the current
> + * context and ksoftirqd which could leave NAPI disabled while
> + * in fact there's still Rx traffic to be processed.
> + */
> + usleep_range(5000, 10000);
> +
> + priv = netdev_priv(net_dev);
> + dpaa_eth_napi_disable(priv);
> +
> + return err;
> +}
> +
> +static const struct net_device_ops dpa_private_ops = {
> + .ndo_open = dpa_eth_priv_start,
> + .ndo_start_xmit = dpa_tx,
> + .ndo_stop = dpa_eth_priv_stop,
> + .ndo_tx_timeout = dpa_timeout,
> + .ndo_get_stats64 = dpa_get_stats64,
> + .ndo_set_mac_address = dpa_set_mac_address,
> + .ndo_validate_addr = eth_validate_addr,
> + .ndo_change_mtu = dpa_change_mtu,
> + .ndo_set_rx_mode = dpa_set_rx_mode,
> + .ndo_init = dpa_ndo_init,
> + .ndo_set_features = dpa_set_features,
> + .ndo_fix_features = dpa_fix_features,
> +};
> +
> +static int dpa_private_napi_add(struct net_device *net_dev)
> +{
> + struct dpa_priv_s *priv = netdev_priv(net_dev);
> + struct dpa_percpu_priv_s *percpu_priv;
> + int i, cpu;
> +
> + for_each_possible_cpu(cpu) {
> + percpu_priv = per_cpu_ptr(priv->percpu_priv, cpu);
> +
> + percpu_priv->np = devm_kzalloc(net_dev->dev.parent,
> + qman_portal_max * sizeof(struct dpa_napi_portal),
> + GFP_KERNEL);
> +
> + if (unlikely(!percpu_priv->np))
> + return -ENOMEM;
> +
> + for (i = 0; i < qman_portal_max; i++)
> + netif_napi_add(net_dev, &percpu_priv->np[i].napi,
> + dpaa_eth_poll, DPA_NAPI_WEIGHT);
> + }
> +
> + return 0;
> +}
> +
> +void dpa_private_napi_del(struct net_device *net_dev)
> +{
> + struct dpa_priv_s *priv = netdev_priv(net_dev);
> + struct dpa_percpu_priv_s *percpu_priv;
> + int i, cpu;
> +
> + for_each_possible_cpu(cpu) {
> + percpu_priv = per_cpu_ptr(priv->percpu_priv, cpu);
> +
> + if (percpu_priv->np) {
> + for (i = 0; i < qman_portal_max; i++)
> + netif_napi_del(&percpu_priv->np[i].napi);
> +
> + devm_kfree(net_dev->dev.parent, percpu_priv->np);
> + }
> + }
> +}
> +
> +static int dpa_private_netdev_init(struct net_device *net_dev)
> +{
> + int i;
> + struct dpa_priv_s *priv = netdev_priv(net_dev);
> + struct dpa_percpu_priv_s *percpu_priv;
> + const u8 *mac_addr;
> +
> + /* Although we access another CPU's private data here
> + * we do it at initialization so it is safe
> + */
> + for_each_possible_cpu(i) {
> + percpu_priv = per_cpu_ptr(priv->percpu_priv, i);
> + percpu_priv->net_dev = net_dev;
> + }
> +
> + net_dev->netdev_ops = &dpa_private_ops;
> + mac_addr = priv->mac_dev->addr;
> +
> + net_dev->mem_start = priv->mac_dev->res->start;
> + net_dev->mem_end = priv->mac_dev->res->end;
> +
> + net_dev->hw_features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
> + NETIF_F_LLTX);
> +
> + net_dev->features |= NETIF_F_GSO;
> +
> + return dpa_netdev_init(net_dev, mac_addr, tx_timeout);
> +}
> +
> +static struct dpa_bp * __cold
> +dpa_priv_bp_probe(struct device *dev)
> +{
> + struct dpa_bp *dpa_bp;
> +
> + dpa_bp = devm_kzalloc(dev, sizeof(*dpa_bp), GFP_KERNEL);
> + if (unlikely(!dpa_bp))
> + return ERR_PTR(-ENOMEM);
> +
> + dpa_bp->percpu_count = devm_alloc_percpu(dev, *dpa_bp->percpu_count);
> + dpa_bp->target_count = FSL_DPAA_ETH_MAX_BUF_COUNT;
> +
> + dpa_bp->seed_cb = dpa_bp_priv_seed;
> + dpa_bp->free_buf_cb = _dpa_bp_free_pf;
> +
> + return dpa_bp;
> +}
> +
> +/* Place all ingress FQs (Rx Default, Rx Error) in a dedicated CGR.
> + * We won't be sending congestion notifications to FMan; for now, we just use
> + * this CGR to generate enqueue rejections to FMan in order to drop the frames
> + * before they reach our ingress queues and eat up memory.
> + */
> +static int dpaa_eth_priv_ingress_cgr_init(struct dpa_priv_s *priv)
> +{
> + struct qm_mcc_initcgr initcgr;
> + u32 cs_th;
> + int err;
> +
> + err = qman_alloc_cgrid(&priv->ingress_cgr.cgrid);
> + if (err < 0) {
> + pr_err("Error %d allocating CGR ID\n", err);
> + goto out_error;
> + }
> +
> + /* Enable CS TD, but disable Congestion State Change Notifications. */
> + initcgr.we_mask = QM_CGR_WE_CS_THRES;
> + initcgr.cgr.cscn_en = QM_CGR_EN;
> + cs_th = CONFIG_FSL_DPAA_INGRESS_CS_THRESHOLD;
> + qm_cgr_cs_thres_set64(&initcgr.cgr.cs_thres, cs_th, 1);
> +
> + initcgr.we_mask |= QM_CGR_WE_CSTD_EN;
> + initcgr.cgr.cstd_en = QM_CGR_EN;
> +
> + /* This is actually a hack, because this CGR will be associated with
> + * our affine SWP. However, we'll place our ingress FQs in it.
> + */
> + err = qman_create_cgr(&priv->ingress_cgr, QMAN_CGR_FLAG_USE_INIT,
> + &initcgr);
> + if (err < 0) {
> + pr_err("Error %d creating ingress CGR with ID %d\n", err,
> + priv->ingress_cgr.cgrid);
> + qman_release_cgrid(priv->ingress_cgr.cgrid);
> + goto out_error;
> + }
> + pr_debug("Created ingress CGR %d for netdev with hwaddr %pM\n",
> + priv->ingress_cgr.cgrid, priv->mac_dev->addr);
> +
> + /* struct qman_cgr allows special cgrid values (i.e. outside the 0..255
> + * range), but we have no common initialization path between the
> + * different variants of the DPAA Eth driver, so we do it here rather
> + * than modifying every other variant than "private Eth".
> + */
> + priv->use_ingress_cgr = true;
> +
> +out_error:
> + return err;
> +}
> +
> +static int dpa_priv_bp_create(struct net_device *net_dev, struct dpa_bp *dpa_bp,
> + size_t count)
> +{
> + struct dpa_priv_s *priv = netdev_priv(net_dev);
> + int i;
> +
> + netif_dbg(priv, probe, net_dev,
> + "Using private BM buffer pools\n");
> +
> + priv->bp_count = count;
> +
> + for (i = 0; i < count; i++) {
> + int err;
> +
> + err = dpa_bp_alloc(&dpa_bp[i]);
> + if (err < 0) {
> + dpa_bp_free(priv);
> + priv->dpa_bp = NULL;
> + return err;
> + }
> +
> + priv->dpa_bp = &dpa_bp[i];
> + }
> +
> + dpa_priv_common_bpid = priv->dpa_bp->bpid;
> + return 0;
> +}
> +
> +static const struct of_device_id dpa_match[];
> +
> +static int
> +dpaa_eth_priv_probe(struct platform_device *pdev)
> +{
> + int err = 0, i, channel;
> + struct device *dev;
> + struct dpa_bp *dpa_bp;
> + struct dpa_fq *dpa_fq, *tmp;
> + size_t count = 1;
> + struct net_device *net_dev = NULL;
> + struct dpa_priv_s *priv = NULL;
> + struct dpa_percpu_priv_s *percpu_priv;
> + struct fm_port_fqs port_fqs;
> + struct dpa_buffer_layout_s *buf_layout = NULL;
> + struct mac_device *mac_dev;
> + struct task_struct *kth;
> +
> + dev = &pdev->dev;
> +
> + /* Get the buffer pool assigned to this interface;
> + * run only once the default pool probing code
> + */
> + dpa_bp = (dpa_bpid2pool(dpa_priv_common_bpid)) ? :
> + dpa_priv_bp_probe(dev);
> + if (IS_ERR(dpa_bp))
> + return PTR_ERR(dpa_bp);
> +
> + /* Allocate this early, so we can store relevant information in
> + * the private area
> + */
> + net_dev = alloc_etherdev_mq(sizeof(*priv), DPAA_ETH_TX_QUEUES);
> + if (!net_dev) {
> + dev_err(dev, "alloc_etherdev_mq() failed\n");
> + goto alloc_etherdev_mq_failed;
> + }
> +
> + snprintf(net_dev->name, IFNAMSIZ, "fm%d-mac%d",
> + dpa_mac_fman_index_get(pdev),
> + dpa_mac_hw_index_get(pdev));
Still think the driver should not set I/F name, this is best left to
udev or similar.
Jocke
More information about the Linuxppc-dev
mailing list