[U-Boot] [PATCH 2/6] net: mvpp2.c: Add Marvell mvpp2 network driver for Armada 375

Joe Hershberger joe.hershberger at gmail.com
Tue Mar 22 20:10:53 CET 2016


Hit Stefan,

Sorry for the delay.

On Tue, Mar 15, 2016 at 11:35 AM, Stefan Roese <sr at denx.de> wrote:
> This patch adds support for the mvpp2 ethernet controller which is integrated
> in the Marvell Armada 375 SoC. This port is based on the Linux driver (v4.4),
> which has been stripped of the in U-Boot unused portions.
>
> Tested on the Marvell Armada 375 eval board db-88f6720.
>
> Signed-off-by: Stefan Roese <sr at denx.de>
> Cc: Luka Perkov <luka.perkov at sartura.hr>
> Cc: Joe Hershberger <joe.hershberger at gmail.com>
> ---
>  drivers/net/Kconfig  |    8 +
>  drivers/net/Makefile |    1 +
>  drivers/net/mvpp2.c  | 4222 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 4231 insertions(+)
>  create mode 100644 drivers/net/mvpp2.c
>
> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> index bc2f51d..bcb4c96 100644
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -94,6 +94,14 @@ config ETH_DESIGNWARE
>           100Mbit and 1 Gbit operation. You must enable CONFIG_PHYLIB to
>           provide the PHY (physical media interface).
>
> +config MVPP2
> +       bool "Marvell Armada 375 network interface support"
> +       depends on ARMADA_375
> +       select PHYLIB
> +       help
> +         This driver supports the network interface units in the
> +         Marvell ARMADA 375 SoC.
> +
>  config PCH_GBE
>         bool "Intel Platform Controller Hub EG20T GMAC driver"
>         depends on DM_ETH && DM_PCI
> diff --git a/drivers/net/Makefile b/drivers/net/Makefile
> index 33a81ee..fbedd04 100644
> --- a/drivers/net/Makefile
> +++ b/drivers/net/Makefile
> @@ -42,6 +42,7 @@ obj-$(CONFIG_MPC5xxx_FEC) += mpc5xxx_fec.o
>  obj-$(CONFIG_MPC512x_FEC) += mpc512x_fec.o
>  obj-$(CONFIG_MVGBE) += mvgbe.o
>  obj-$(CONFIG_MVNETA) += mvneta.o
> +obj-$(CONFIG_MVPP2) += mvpp2.o
>  obj-$(CONFIG_NATSEMI) += natsemi.o
>  obj-$(CONFIG_DRIVER_NE2000) += ne2000.o ne2000_base.o
>  obj-$(CONFIG_DRIVER_AX88796L) += ax88796.o ne2000_base.o
> diff --git a/drivers/net/mvpp2.c b/drivers/net/mvpp2.c
> new file mode 100644
> index 0000000..09dfc7e
> --- /dev/null
> +++ b/drivers/net/mvpp2.c
> @@ -0,0 +1,4222 @@
> +/*
> + * Driver for Marvell PPv2 network controller for Armada 375 SoC.
> + *
> + * Copyright (C) 2014 Marvell
> + *
> + * Marcin Wojtas <mw at semihalf.com>
> + *
> + * U-Boot version:
> + * Copyright (C) 2016 Stefan Roese <sr at denx.de>
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <common.h>
> +#include <dm.h>
> +#include <dm/device-internal.h>
> +#include <dm/lists.h>
> +#include <net.h>
> +#include <netdev.h>
> +#include <config.h>
> +#include <malloc.h>
> +#include <asm/io.h>
> +#include <asm/errno.h>
> +#include <phy.h>
> +#include <miiphy.h>
> +#include <watchdog.h>
> +#include <asm/arch/cpu.h>
> +#include <asm/arch/soc.h>
> +#include <linux/compat.h>
> +#include <linux/mbus.h>
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +#if !defined(CONFIG_PHYLIB)
> +# error Marvell mvpp2 requires PHYLIB
> +#endif

This isn't needed since you "select" PHYLIB in the Kconfig.

> +
> +/* Some linux -> U-Boot compatibility stuff */
> +#define netdev_err(dev, fmt, args...)          \
> +       printf(fmt, ##args)
> +#define netdev_warn(dev, fmt, args...)         \
> +       printf(fmt, ##args)
> +#define netdev_info(dev, fmt, args...)         \
> +       printf(fmt, ##args)
> +#define netdev_dbg(dev, fmt, args...)          \
> +       printf(fmt, ##args)
> +
> +#define ETH_ALEN       6               /* Octets in one ethernet addr  */
> +
> +#define ETH_P_IP       0x0800          /* Internet Protocol packet     */

Already available in include/net.h as PROT_IP

> +#define ETH_P_PPP_SES  0x8864          /* PPPoE session messages       */

Add this to include/net.h as PROT_PPP_SES

> +#define ETH_P_ARP      0x0806          /* Address Resolution packet    */

Already available in include/net.h as PROT_ARP

> +#define ETH_P_IPV6     0x86DD          /* IPv6 over bluebook           */

Add this to include/net.h as PROT_IPV6

> +
> +#define __verify_pcpu_ptr(ptr)                                         \
> +do {                                                                   \
> +       const void __percpu *__vpp_verify = (typeof((ptr) + 0))NULL;    \
> +       (void)__vpp_verify;                                             \
> +} while (0)
> +
> +#define VERIFY_PERCPU_PTR(__p)                                         \
> +({                                                                     \
> +       __verify_pcpu_ptr(__p);                                         \
> +       (typeof(*(__p)) __kernel __force *)(__p);                       \
> +})
> +
> +#define per_cpu_ptr(ptr, cpu)  ({ (void)(cpu); VERIFY_PERCPU_PTR(ptr); })
> +#define smp_processor_id()     0
> +#define num_present_cpus()     1
> +#define for_each_present_cpu(cpu)                      \
> +       for ((cpu) = 0; (cpu) < 1; (cpu)++)
> +
> +#define NET_SKB_PAD    max(32, MVPP2_CPU_D_CACHE_LINE_SIZE)
> +
> +#define CONFIG_NR_CPUS         1
> +#define ETH_HLEN               14      /* Total octets in header */

Already available in include/net.h as ETHER_HDR_SIZE.

> +
> +/* 2(HW hdr) 14(MAC hdr) 4(CRC) 32(extra for cache prefetch) */
> +#define WRAP                   (2 + ETH_HLEN + 4 + 32)
> +#define MTU                    1500
> +#define RX_BUFFER_SIZE         (ALIGN(MTU + WRAP, ARCH_DMA_MINALIGN))

<snip - constants>

> +/*
> + * Page table entries are set to 1MB, or multiples of 1MB
> + * (not < 1MB). driver uses less bd's so use 1MB bdspace.
> + */
> +#define BD_SPACE       (1 << 20)

It would be great if this long list of constants lived in a header
file instead of the top of the source.

> +
> +/* Utility/helper methods */
> +
> +static void mvpp2_write(struct mvpp2 *priv, u32 offset, u32 data)
> +{
> +       writel(data, priv->base + offset);
> +}
> +
> +static u32 mvpp2_read(struct mvpp2 *priv, u32 offset)
> +{
> +       return readl(priv->base + offset);
> +}
> +

<snip>

> +
> +/* Set hw internals when starting port */
> +static void mvpp2_start_dev(struct mvpp2_port *port)
> +{
> +       mvpp2_gmac_max_rx_size_set(port);
> +       mvpp2_txp_max_tx_size_set(port);
> +
> +       mvpp2_port_enable(port);
> +}
> +
> +/* Set hw internals when stopping port */
> +static void mvpp2_stop_dev(struct mvpp2_port *port)
> +{
> +       /* Stop new packets from arriving to RXQs */
> +       mvpp2_ingress_disable(port);
> +
> +       mvpp2_egress_disable(port);
> +       mvpp2_port_disable(port);
> +}
> +
> +static int mvpp2_phy_connect(struct udevice *dev, struct mvpp2_port *port)
> +{
> +       struct phy_device *phy_dev;
> +
> +       if (!port->init || port->link == 0) {
> +               phy_dev = phy_connect(port->priv->bus, port->phyaddr, dev,
> +                                     port->phy_interface);
> +               port->phy_dev = phy_dev;
> +               if (!phy_dev) {
> +                       netdev_err(port->dev, "cannot connect to phy\n");
> +                       return -ENODEV;
> +               }
> +               phy_dev->supported &= PHY_GBIT_FEATURES;
> +               phy_dev->advertising = phy_dev->supported;
> +
> +               port->phy_dev = phy_dev;
> +               port->link    = 0;
> +               port->duplex  = 0;
> +               port->speed   = 0;
> +
> +               phy_config(phy_dev);
> +               phy_startup(phy_dev);
> +               if (!phy_dev->link) {
> +                       printf("%s: No link\n", phy_dev->dev->name);
> +                       return -1;
> +               }
> +
> +               port->init = 1;
> +       } else {
> +               mvpp2_egress_enable(port);
> +               mvpp2_ingress_enable(port);
> +       }
> +
> +       return 0;
> +}
> +
> +static int mvpp2_open(struct udevice *dev, struct mvpp2_port *port)
> +{
> +       unsigned char mac_bcast[ETH_ALEN] = {
> +                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
> +       int err;
> +
> +       err = mvpp2_prs_mac_da_accept(port->priv, port->id, mac_bcast, true);
> +       if (err) {
> +               netdev_err(dev, "mvpp2_prs_mac_da_accept BC failed\n");
> +               return err;
> +       }
> +       err = mvpp2_prs_mac_da_accept(port->priv, port->id,
> +                                     port->dev_addr, true);
> +       if (err) {
> +               netdev_err(dev, "mvpp2_prs_mac_da_accept MC failed\n");
> +               return err;
> +       }
> +       err = mvpp2_prs_def_flow(port);
> +       if (err) {
> +               netdev_err(dev, "mvpp2_prs_def_flow failed\n");
> +               return err;
> +       }
> +
> +       /* Allocate the Rx/Tx queues */
> +       err = mvpp2_setup_rxqs(port);
> +       if (err) {
> +               netdev_err(port->dev, "cannot allocate Rx queues\n");
> +               return err;
> +       }
> +
> +       err = mvpp2_setup_txqs(port);
> +       if (err) {
> +               netdev_err(port->dev, "cannot allocate Tx queues\n");
> +               return err;
> +       }
> +
> +       err = mvpp2_phy_connect(dev, port);
> +       if (err < 0)
> +               return err;
> +
> +       mvpp2_link_event(port);
> +
> +       mvpp2_start_dev(port);
> +
> +       return 0;
> +}
> +
> +/* No Device ops here in U-Boot */
> +
> +/* Driver initialization */
> +
> +static void mvpp2_port_power_up(struct mvpp2_port *port)
> +{
> +       mvpp2_port_mii_set(port);
> +       mvpp2_port_periodic_xon_disable(port);
> +       mvpp2_port_fc_adv_enable(port);
> +       mvpp2_port_reset(port);
> +}
> +
> +/* Initialize port HW */
> +static int mvpp2_port_init(struct udevice *dev, struct mvpp2_port *port)
> +{
> +       struct mvpp2 *priv = port->priv;
> +       struct mvpp2_txq_pcpu *txq_pcpu;
> +       int queue, cpu, err;
> +
> +       if (port->first_rxq + rxq_number > MVPP2_RXQ_TOTAL_NUM)
> +               return -EINVAL;
> +
> +       /* Disable port */
> +       mvpp2_egress_disable(port);
> +       mvpp2_port_disable(port);
> +
> +       port->txqs = devm_kcalloc(dev, txq_number, sizeof(*port->txqs),
> +                                 GFP_KERNEL);
> +       if (!port->txqs)
> +               return -ENOMEM;
> +
> +       /* Associate physical Tx queues to this port and initialize.
> +        * The mapping is predefined.
> +        */
> +       for (queue = 0; queue < txq_number; queue++) {
> +               int queue_phy_id = mvpp2_txq_phys(port->id, queue);
> +               struct mvpp2_tx_queue *txq;
> +
> +               txq = devm_kzalloc(dev, sizeof(*txq), GFP_KERNEL);
> +               if (!txq)
> +                       return -ENOMEM;
> +
> +               txq->pcpu = devm_kzalloc(dev, sizeof(struct mvpp2_txq_pcpu),
> +                                        GFP_KERNEL);
> +               if (!txq->pcpu)
> +                       return -ENOMEM;
> +
> +               txq->id = queue_phy_id;
> +               txq->log_id = queue;
> +               txq->done_pkts_coal = MVPP2_TXDONE_COAL_PKTS_THRESH;
> +               for_each_present_cpu(cpu) {
> +                       txq_pcpu = per_cpu_ptr(txq->pcpu, cpu);
> +                       txq_pcpu->cpu = cpu;
> +               }
> +
> +               port->txqs[queue] = txq;
> +       }
> +
> +       port->rxqs = devm_kcalloc(dev, rxq_number, sizeof(*port->rxqs),
> +                                 GFP_KERNEL);
> +       if (!port->rxqs)
> +               return -ENOMEM;
> +
> +       /* Allocate and initialize Rx queue for this port */
> +       for (queue = 0; queue < rxq_number; queue++) {
> +               struct mvpp2_rx_queue *rxq;
> +
> +               /* Map physical Rx queue to port's logical Rx queue */
> +               rxq = devm_kzalloc(dev, sizeof(*rxq), GFP_KERNEL);
> +               if (!rxq)
> +                       return -ENOMEM;
> +               /* Map this Rx queue to a physical queue */
> +               rxq->id = port->first_rxq + queue;
> +               rxq->port = port->id;
> +               rxq->logic_rxq = queue;
> +
> +               port->rxqs[queue] = rxq;
> +       }
> +
> +       /* Configure Rx queue group interrupt for this port */
> +       mvpp2_write(priv, MVPP2_ISR_RXQ_GROUP_REG(port->id), CONFIG_MV_ETH_RXQ);
> +
> +       /* Create Rx descriptor rings */
> +       for (queue = 0; queue < rxq_number; queue++) {
> +               struct mvpp2_rx_queue *rxq = port->rxqs[queue];
> +
> +               rxq->size = port->rx_ring_size;
> +               rxq->pkts_coal = MVPP2_RX_COAL_PKTS;
> +               rxq->time_coal = MVPP2_RX_COAL_USEC;
> +       }
> +
> +       mvpp2_ingress_disable(port);
> +
> +       /* Port default configuration */
> +       mvpp2_defaults_set(port);
> +
> +       /* Port's classifier configuration */
> +       mvpp2_cls_oversize_rxq_set(port);
> +       mvpp2_cls_port_config(port);
> +
> +       /* Provide an initial Rx packet size */
> +       port->pkt_size = MVPP2_RX_PKT_SIZE(PKTSIZE_ALIGN);
> +
> +       /* Initialize pools for swf */
> +       err = mvpp2_swf_bm_pool_init(port);
> +       if (err)
> +               return err;
> +
> +       return 0;
> +}
> +
> +/* Ports initialization */
> +static int mvpp2_port_probe(struct udevice *dev,
> +                           struct mvpp2_port *port,
> +                           int port_node,
> +                           struct mvpp2 *priv,
> +                           int *next_first_rxq)
> +{
> +       int phy_node;
> +       u32 id;
> +       u32 phyaddr;
> +       const char *phy_mode_str;
> +       int phy_mode = -1;
> +       int priv_common_regs_num = 2;
> +       int err;
> +
> +       phy_node = fdtdec_lookup_phandle(gd->fdt_blob, port_node, "phy");
> +       if (phy_node < 0) {
> +               dev_err(&pdev->dev, "missing phy\n");
> +               return -ENODEV;
> +       }
> +
> +       phy_mode_str = fdt_getprop(gd->fdt_blob, port_node, "phy-mode", NULL);
> +       if (phy_mode_str)
> +               phy_mode = phy_get_interface_by_name(phy_mode_str);
> +       if (phy_mode == -1) {
> +               dev_err(&pdev->dev, "incorrect phy mode\n");
> +               return -EINVAL;
> +       }
> +
> +       id = fdtdec_get_int(gd->fdt_blob, port_node, "port-id", -1);
> +       if (id == -1) {
> +               dev_err(&pdev->dev, "missing port-id value\n");
> +               return -EINVAL;
> +       }
> +
> +       phyaddr = fdtdec_get_int(gd->fdt_blob, phy_node, "reg", 0);
> +
> +       port->priv = priv;
> +       port->id = id;
> +       port->first_rxq = *next_first_rxq;
> +       port->phy_node = phy_node;
> +       port->phy_interface = phy_mode;
> +       port->phyaddr = phyaddr;
> +
> +       port->base = (void __iomem *)dev_get_addr_index(dev->parent,
> +                                                       priv_common_regs_num
> +                                                       + id);
> +       if (IS_ERR(port->base))
> +               return PTR_ERR(port->base);
> +
> +       port->tx_ring_size = MVPP2_MAX_TXD;
> +       port->rx_ring_size = MVPP2_MAX_RXD;
> +
> +       err = mvpp2_port_init(dev, port);
> +       if (err < 0) {
> +               dev_err(&pdev->dev, "failed to init port %d\n", id);
> +               return err;
> +       }
> +       mvpp2_port_power_up(port);
> +
> +       /* Increment the first Rx queue number to be used by the next port */
> +       *next_first_rxq += CONFIG_MV_ETH_RXQ;
> +       priv->port_list[id] = port;
> +       return 0;
> +}
> +
> +/* Initialize decoding windows */
> +static void mvpp2_conf_mbus_windows(const struct mbus_dram_target_info *dram,
> +                                   struct mvpp2 *priv)
> +{
> +       u32 win_enable;
> +       int i;
> +
> +       for (i = 0; i < 6; i++) {
> +               mvpp2_write(priv, MVPP2_WIN_BASE(i), 0);
> +               mvpp2_write(priv, MVPP2_WIN_SIZE(i), 0);
> +
> +               if (i < 4)
> +                       mvpp2_write(priv, MVPP2_WIN_REMAP(i), 0);
> +       }
> +
> +       win_enable = 0;
> +
> +       for (i = 0; i < dram->num_cs; i++) {
> +               const struct mbus_dram_window *cs = dram->cs + i;
> +
> +               mvpp2_write(priv, MVPP2_WIN_BASE(i),
> +                           (cs->base & 0xffff0000) | (cs->mbus_attr << 8) |
> +                           dram->mbus_dram_target_id);
> +
> +               mvpp2_write(priv, MVPP2_WIN_SIZE(i),
> +                           (cs->size - 1) & 0xffff0000);
> +
> +               win_enable |= (1 << i);
> +       }
> +
> +       mvpp2_write(priv, MVPP2_BASE_ADDR_ENABLE, win_enable);
> +}
> +
> +/* Initialize Rx FIFO's */
> +static void mvpp2_rx_fifo_init(struct mvpp2 *priv)
> +{
> +       int port;
> +
> +       for (port = 0; port < MVPP2_MAX_PORTS; port++) {
> +               mvpp2_write(priv, MVPP2_RX_DATA_FIFO_SIZE_REG(port),
> +                           MVPP2_RX_FIFO_PORT_DATA_SIZE);
> +               mvpp2_write(priv, MVPP2_RX_ATTR_FIFO_SIZE_REG(port),
> +                           MVPP2_RX_FIFO_PORT_ATTR_SIZE);
> +       }
> +
> +       mvpp2_write(priv, MVPP2_RX_MIN_PKT_SIZE_REG,
> +                   MVPP2_RX_FIFO_PORT_MIN_PKT);
> +       mvpp2_write(priv, MVPP2_RX_FIFO_INIT_REG, 0x1);
> +}
> +
> +/* Initialize network controller common part HW */
> +static int mvpp2_init(struct udevice *dev, struct mvpp2 *priv)
> +{
> +       const struct mbus_dram_target_info *dram_target_info;
> +       int err, i;
> +       u32 val;
> +
> +       /* Checks for hardware constraints (U-Boot uses only one rxq) */
> +       if ((rxq_number > MVPP2_MAX_RXQ) || (txq_number > MVPP2_MAX_TXQ)) {
> +               dev_err(&pdev->dev, "invalid queue size parameter\n");
> +               return -EINVAL;
> +       }
> +
> +       /* MBUS windows configuration */
> +       dram_target_info = mvebu_mbus_dram_info();
> +       if (dram_target_info)
> +               mvpp2_conf_mbus_windows(dram_target_info, priv);
> +
> +       /* Disable HW PHY polling */
> +       val = readl(priv->lms_base + MVPP2_PHY_AN_CFG0_REG);
> +       val |= MVPP2_PHY_AN_STOP_SMI0_MASK;
> +       writel(val, priv->lms_base + MVPP2_PHY_AN_CFG0_REG);
> +
> +       /* Allocate and initialize aggregated TXQs */
> +       priv->aggr_txqs = devm_kcalloc(dev, num_present_cpus(),
> +                                      sizeof(struct mvpp2_tx_queue),
> +                                      GFP_KERNEL);
> +       if (!priv->aggr_txqs)
> +               return -ENOMEM;
> +
> +       for_each_present_cpu(i) {
> +               priv->aggr_txqs[i].id = i;
> +               priv->aggr_txqs[i].size = MVPP2_AGGR_TXQ_SIZE;
> +               err = mvpp2_aggr_txq_init(dev, &priv->aggr_txqs[i],
> +                                         MVPP2_AGGR_TXQ_SIZE, i, priv);
> +               if (err < 0)
> +                       return err;
> +       }
> +
> +       /* Rx Fifo Init */
> +       mvpp2_rx_fifo_init(priv);
> +
> +       /* Reset Rx queue group interrupt configuration */
> +       for (i = 0; i < MVPP2_MAX_PORTS; i++)
> +               mvpp2_write(priv, MVPP2_ISR_RXQ_GROUP_REG(i),
> +                           CONFIG_MV_ETH_RXQ);
> +
> +       writel(MVPP2_EXT_GLOBAL_CTRL_DEFAULT,
> +              priv->lms_base + MVPP2_MNG_EXTENDED_GLOBAL_CTRL_REG);
> +
> +       /* Allow cache snoop when transmiting packets */
> +       mvpp2_write(priv, MVPP2_TX_SNOOP_REG, 0x1);
> +
> +       /* Buffer Manager initialization */
> +       err = mvpp2_bm_init(dev, priv);
> +       if (err < 0)
> +               return err;
> +
> +       /* Parser default initialization */
> +       err = mvpp2_prs_default_init(dev, priv);
> +       if (err < 0)
> +               return err;
> +
> +       /* Classifier default initialization */
> +       mvpp2_cls_init(priv);
> +
> +       return 0;
> +}
> +
> +/* SMI / MDIO functions */
> +
> +static int smi_wait_ready(struct mvpp2 *priv)
> +{
> +       u32 timeout = MVPP2_SMI_TIMEOUT;
> +       u32 smi_reg;
> +
> +       /* wait till the SMI is not busy */
> +       do {
> +               /* read smi register */
> +               smi_reg = readl(priv->lms_base + MVPP2_SMI);
> +               if (timeout-- == 0) {
> +                       printf("Error: SMI busy timeout\n");
> +                       return -EFAULT;
> +               }
> +       } while (smi_reg & MVPP2_SMI_BUSY);
> +
> +       return 0;
> +}
> +
> +/*
> + * mpp2_mdio_read - miiphy_read callback function.
> + *
> + * Returns 16bit phy register value, or 0xffff on error
> + */
> +static int mpp2_mdio_read(struct mii_dev *bus, int addr, int devad, int reg)
> +{
> +       struct mvpp2 *priv = bus->priv;
> +       u32 smi_reg;
> +       u32 timeout;
> +
> +       /* check parameters */
> +       if (addr > MVPP2_PHY_ADDR_MASK) {
> +               printf("Error: Invalid PHY address %d\n", addr);
> +               return -EFAULT;
> +       }
> +
> +       if (reg > MVPP2_PHY_REG_MASK) {
> +               printf("Err: Invalid register offset %d\n", reg);
> +               return -EFAULT;
> +       }
> +
> +       /* wait till the SMI is not busy */
> +       if (smi_wait_ready(priv) < 0)
> +               return -EFAULT;
> +
> +       /* fill the phy address and regiser offset and read opcode */
> +       smi_reg = (addr << MVPP2_SMI_DEV_ADDR_OFFS)
> +               | (reg << MVPP2_SMI_REG_ADDR_OFFS)
> +               | MVPP2_SMI_OPCODE_READ;
> +
> +       /* write the smi register */
> +       writel(smi_reg, priv->lms_base + MVPP2_SMI);
> +
> +       /* wait till read value is ready */
> +       timeout = MVPP2_SMI_TIMEOUT;
> +
> +       do {
> +               /* read smi register */
> +               smi_reg = readl(priv->lms_base + MVPP2_SMI);
> +               if (timeout-- == 0) {
> +                       printf("Err: SMI read ready timeout\n");
> +                       return -EFAULT;
> +               }
> +       } while (!(smi_reg & MVPP2_SMI_READ_VALID));
> +
> +       /* Wait for the data to update in the SMI register */
> +       for (timeout = 0; timeout < MVPP2_SMI_TIMEOUT; timeout++)
> +               ;
> +
> +       return readl(priv->lms_base + MVPP2_SMI) & MVPP2_SMI_DATA_MASK;
> +}
> +
> +/*
> + * mpp2_mdio_write - miiphy_write callback function.
> + *
> + * Returns 0 if write succeed, -EINVAL on bad parameters
> + * -ETIME on timeout
> + */
> +static int mpp2_mdio_write(struct mii_dev *bus, int addr, int devad, int reg,
> +                          u16 value)
> +{
> +       struct mvpp2 *priv = bus->priv;
> +       u32 smi_reg;
> +
> +       /* check parameters */
> +       if (addr > MVPP2_PHY_ADDR_MASK) {
> +               printf("Error: Invalid PHY address %d\n", addr);
> +               return -EFAULT;
> +       }
> +
> +       if (reg > MVPP2_PHY_REG_MASK) {
> +               printf("Err: Invalid register offset %d\n", reg);
> +               return -EFAULT;
> +       }
> +
> +       /* wait till the SMI is not busy */
> +       if (smi_wait_ready(priv) < 0)
> +               return -EFAULT;
> +
> +       /* fill the phy addr and reg offset and write opcode and data */
> +       smi_reg = value << MVPP2_SMI_DATA_OFFS;
> +       smi_reg |= (addr << MVPP2_SMI_DEV_ADDR_OFFS)
> +               | (reg << MVPP2_SMI_REG_ADDR_OFFS);
> +       smi_reg &= ~MVPP2_SMI_OPCODE_READ;
> +
> +       /* write the smi register */
> +       writel(smi_reg, priv->lms_base + MVPP2_SMI);
> +
> +       return 0;
> +}
> +
> +static int mvpp2_probe(struct udevice *dev)
> +{
> +       struct mvpp2_port *port = dev_get_priv(dev);
> +       struct mvpp2 *priv;
> +       struct mii_dev *bus;
> +       void *bd_space;
> +       int err;
> +       u32 size = 0;
> +       int i;
> +
> +       /*
> +        * This function is called multiple times. For each controller.
> +        * But we only need to do the main controller probing once. So
> +        * lets only do the port probing here, if called not for the
> +        * first time.
> +        */
> +       if (buffer_loc.tx_descs) {
> +               priv = buffer_loc.priv;
> +               port->priv = priv;
> +
> +               err = mvpp2_port_probe(dev, port, dev->of_offset, priv,
> +                                      &buffer_loc.first_rxq);
> +
> +               return 0;
> +       }
> +
> +       /*
> +        * U-Boot special buffer handling:
> +        *
> +        * Allocate buffer area for descs and rx_buffers. This is only
> +        * done once for all interfaces. As only one interface can
> +        * be active. Make this area DMA save by disabling the D-cache

DMA save -> DMA-safe

> +        */
> +
> +       /* Align buffer area for descs and rx_buffers to 1MiB */
> +       bd_space = memalign(1 << MMU_SECTION_SHIFT, BD_SPACE);
> +       mmu_set_region_dcache_behaviour((u32)bd_space, BD_SPACE, DCACHE_OFF);
> +
> +       buffer_loc.aggr_tx_descs = (struct mvpp2_tx_desc *)bd_space;
> +       size += MVPP2_AGGR_TXQ_SIZE * MVPP2_DESC_ALIGNED_SIZE;
> +
> +       buffer_loc.tx_descs = (struct mvpp2_tx_desc *)((u32)bd_space + size);
> +       size += MVPP2_MAX_TXD * MVPP2_DESC_ALIGNED_SIZE;
> +
> +       buffer_loc.rx_descs = (struct mvpp2_rx_desc *)((u32)bd_space + size);
> +       size += MVPP2_MAX_RXD * MVPP2_DESC_ALIGNED_SIZE;
> +
> +       for (i = 0; i < MVPP2_BM_POOLS_NUM; i++) {
> +               buffer_loc.bm_pool[i] = (u32 *)((u32)bd_space + size);
> +               size += MVPP2_BM_POOL_SIZE_MAX * sizeof(u32);
> +       }
> +
> +       for (i = 0; i < MVPP2_BM_LONG_BUF_NUM; i++) {
> +               buffer_loc.rx_buffer[i] = (u32 *)((u32)bd_space + size);
> +               size += RX_BUFFER_SIZE;
> +       }
> +
> +       /*
> +        * This is done only once, for the first controller that is
> +        * probed. Here the common structures are initialized.
> +        */

Any reason this isn't in the probe function for the MISC class parent driver?

> +       priv = calloc(1, sizeof(struct mvpp2));
> +       if (!priv)
> +               return -ENOMEM;
> +
> +       /* And save pointer to main mvpp2 struct */
> +       port->priv = priv;
> +       buffer_loc.priv = priv;
> +
> +       priv->base = (void *)dev_get_addr_index(dev->parent, 0);
> +       if (IS_ERR(priv->base))
> +               return PTR_ERR(priv->base);
> +
> +       priv->lms_base = (void *)dev_get_addr_index(dev->parent, 1);
> +       if (IS_ERR(priv->lms_base))
> +               return PTR_ERR(priv->lms_base);
> +
> +       /* Initialize network controller */
> +       err = mvpp2_init(dev, priv);
> +       if (err < 0) {
> +               dev_err(&pdev->dev, "failed to initialize controller\n");
> +               return err;
> +       }
> +
> +       err = mvpp2_port_probe(dev, port, dev->of_offset, priv,
> +                              &buffer_loc.first_rxq);
> +
> +       /*
> +        * Finally create and register the MDIO bus driver
> +        */
> +       bus = mdio_alloc();
> +       if (!bus) {
> +               printf("Failed to allocate MDIO bus\n");
> +               return -ENOMEM;
> +       }
> +
> +       bus->read = mpp2_mdio_read;
> +       bus->write = mpp2_mdio_write;
> +       snprintf(bus->name, sizeof(bus->name), dev->name);
> +       bus->priv = (void *)priv;
> +       priv->bus = bus;
> +
> +       return mdio_register(bus);
> +}
> +
> +static int mvpp2_recv(struct udevice *dev, int flags, uchar **packetp)
> +{
> +       struct mvpp2_port *port = dev_get_priv(dev);
> +       struct mvpp2_rx_desc *rx_desc;
> +       struct mvpp2_bm_pool *bm_pool;
> +       dma_addr_t phys_addr;
> +       u32 bm, rx_status;
> +       int pool, rx_bytes, err;
> +       int rx_received;
> +       struct mvpp2_rx_queue *rxq;
> +       u32 cause_rx_tx, cause_rx, cause_misc;
> +       u8 *data;
> +
> +       cause_rx_tx = mvpp2_read(port->priv,
> +                                MVPP2_ISR_RX_TX_CAUSE_REG(port->id));
> +       cause_rx_tx &= ~MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK;
> +       cause_misc = cause_rx_tx & MVPP2_CAUSE_MISC_SUM_MASK;
> +       if (!cause_rx_tx && !cause_misc)
> +               return 0;
> +
> +       cause_rx = cause_rx_tx & MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK;
> +
> +       /* Process RX packets */
> +       cause_rx |= port->pending_cause_rx;
> +       rxq = mvpp2_get_rx_queue(port, cause_rx);
> +
> +       /* Get number of received packets and clamp the to-do */
> +       rx_received = mvpp2_rxq_received(port, rxq->id);
> +
> +       /* Return if no packets are received */
> +       if (!rx_received)
> +               return 0;
> +
> +       rx_desc = mvpp2_rxq_next_desc_get(rxq);
> +       rx_status = rx_desc->status;
> +       rx_bytes = rx_desc->data_size - MVPP2_MH_SIZE;
> +       phys_addr = rx_desc->buf_phys_addr;
> +
> +       bm = mvpp2_bm_cookie_build(rx_desc);
> +       pool = mvpp2_bm_cookie_pool_get(bm);
> +       bm_pool = &port->priv->bm_pools[pool];
> +
> +       /* Check if buffer header is used */
> +       if (rx_status & MVPP2_RXD_BUF_HDR)
> +               return 0;
> +
> +       /* In case of an error, release the requested buffer pointer
> +        * to the Buffer Manager. This request process is controlled
> +        * by the hardware, and the information about the buffer is
> +        * comprised by the RX descriptor.
> +        */
> +       if (rx_status & MVPP2_RXD_ERR_SUMMARY) {
> +               mvpp2_rx_error(port, rx_desc);
> +               /* Return the buffer to the pool */
> +               mvpp2_pool_refill(port, bm, rx_desc->buf_phys_addr,
> +                                 rx_desc->buf_cookie);
> +               return 0;
> +       }
> +
> +       err = mvpp2_rx_refill(port, bm_pool, bm, phys_addr);
> +       if (err) {
> +               netdev_err(port->dev, "failed to refill BM pools\n");
> +               return 0;
> +       }
> +
> +       /* Update Rx queue management counters */
> +       mb();
> +       mvpp2_rxq_status_update(port, rxq->id, 1, 1);
> +
> +       /* give packet to stack - skip on first n bytes */
> +       data = (u8 *)phys_addr + 2 + 32;
> +
> +       if (rx_bytes <= 0)
> +               return 0;
> +
> +       /*
> +        * No cache invalidation needed here, since the rx_buffer's are
> +        * located in a uncached memory region
> +        */
> +       *packetp = data;
> +
> +       return rx_bytes;
> +}
> +
> +/* Drain Txq */
> +static void mvpp2_txq_drain(struct mvpp2_port *port, struct mvpp2_tx_queue *txq,
> +                           int enable)
> +{
> +       u32 val;
> +
> +       mvpp2_write(port->priv, MVPP2_TXQ_NUM_REG, txq->id);
> +       val = mvpp2_read(port->priv, MVPP2_TXQ_PREF_BUF_REG);
> +       if (enable)
> +               val |= MVPP2_TXQ_DRAIN_EN_MASK;
> +       else
> +               val &= ~MVPP2_TXQ_DRAIN_EN_MASK;
> +       mvpp2_write(port->priv, MVPP2_TXQ_PREF_BUF_REG, val);
> +}
> +
> +static int mvpp2_send(struct udevice *dev, void *packet, int length)
> +{
> +       struct mvpp2_port *port = dev_get_priv(dev);
> +       struct mvpp2_tx_queue *txq, *aggr_txq;
> +       struct mvpp2_tx_desc *tx_desc;
> +       int tx_done;
> +       int timeout;
> +
> +       txq = port->txqs[0];
> +       aggr_txq = &port->priv->aggr_txqs[smp_processor_id()];
> +
> +       /* Get a descriptor for the first part of the packet */
> +       tx_desc = mvpp2_txq_next_desc_get(aggr_txq);
> +       tx_desc->phys_txq = txq->id;
> +       tx_desc->data_size = length;
> +       tx_desc->packet_offset = (u32)packet & MVPP2_TX_DESC_ALIGN;
> +       tx_desc->buf_phys_addr = (u32)packet & ~MVPP2_TX_DESC_ALIGN;
> +       /* First and Last descriptor */
> +       tx_desc->command = MVPP2_TXD_L4_CSUM_NOT | MVPP2_TXD_IP_CSUM_DISABLE
> +               | MVPP2_TXD_F_DESC | MVPP2_TXD_L_DESC;
> +
> +       /* Flush tx data */
> +       flush_dcache_range((u32)packet, (u32)packet + length);
> +
> +       /* Enable transmit */
> +       mb();
> +       mvpp2_aggr_txq_pend_desc_add(port, 1);
> +
> +       mvpp2_write(port->priv, MVPP2_TXQ_NUM_REG, txq->id);
> +
> +       timeout = 0;
> +       do {
> +               if (timeout++ > 10000) {
> +                       printf("timeout: packet not sent from aggregated to phys TXQ\n");
> +                       return 0;
> +               }
> +               tx_done = mvpp2_txq_pend_desc_num_get(port, txq);
> +       } while (tx_done);
> +
> +       /* Enable TXQ drain */
> +       mvpp2_txq_drain(port, txq, 1);
> +
> +       timeout = 0;
> +       do {
> +               if (timeout++ > 10000) {
> +                       printf("timeout: packet not sent\n");
> +                       return 0;
> +               }
> +               tx_done = mvpp2_txq_sent_desc_proc(port, txq);
> +       } while (!tx_done);
> +
> +       /* Disable TXQ drain */
> +       mvpp2_txq_drain(port, txq, 0);
> +
> +       return 0;
> +}
> +
> +static int mvpp2_start(struct udevice *dev)
> +{
> +       struct eth_pdata *pdata = dev_get_platdata(dev);
> +       struct mvpp2_port *port = dev_get_priv(dev);
> +
> +       /* Load current MAC address */
> +       memcpy(port->dev_addr, pdata->enetaddr, ETH_ALEN);
> +
> +       /* Reconfigure parser accept the original MAC address */
> +       mvpp2_prs_update_mac_da(port, port->dev_addr);
> +
> +       mvpp2_port_power_up(port);
> +
> +       mvpp2_open(dev, port);
> +
> +       return 0;
> +}
> +
> +static void mvpp2_stop(struct udevice *dev)
> +{
> +       struct mvpp2_port *port = dev_get_priv(dev);
> +
> +       mvpp2_stop_dev(port);
> +       mvpp2_cleanup_rxqs(port);
> +       mvpp2_cleanup_txqs(port);
> +}
> +
> +static const struct eth_ops mvpp2_ops = {
> +       .start          = mvpp2_start,
> +       .send           = mvpp2_send,
> +       .recv           = mvpp2_recv,
> +       .stop           = mvpp2_stop,
> +};
> +
> +static struct driver mvpp2_driver = {
> +       .name   = "mvpp2",
> +       .id     = UCLASS_ETH,
> +       .probe  = mvpp2_probe,
> +       .ops    = &mvpp2_ops,
> +       .priv_auto_alloc_size = sizeof(struct mvpp2_port),
> +       .platdata_auto_alloc_size = sizeof(struct eth_pdata),
> +};
> +
> +/*
> + * Use a MISC device to bind the n instances (child nodes) of the
> + * network base controller in UCLASS_ETH.
> + */
> +static int mvpp2_base_bind(struct udevice *parent)
> +{
> +       const void *blob = gd->fdt_blob;
> +       int node = parent->of_offset;
> +       struct uclass_driver *drv;
> +       struct udevice *dev;
> +       struct eth_pdata *plat;
> +       char *name;
> +       int subnode;
> +       u32 id;
> +
> +       /* Lookup eth driver */
> +       drv = lists_uclass_lookup(UCLASS_ETH);
> +       if (!drv) {
> +               puts("Cannot find eth driver\n");
> +               return -ENOENT;
> +       }
> +
> +       fdt_for_each_subnode(blob, subnode, node) {
> +               /* Skip disabled ports */
> +               if (!fdtdec_get_is_enabled(blob, subnode))
> +                       continue;
> +
> +               plat = calloc(1, sizeof(*plat));
> +               if (!plat)
> +                       return -ENOMEM;
> +
> +               id = fdtdec_get_int(blob, subnode, "port-id", -1);
> +
> +               name = calloc(1, 16);
> +               sprintf(name, "mvpp2-%d", id);
> +
> +               /* Create child device UCLASS_ETH and bind it */
> +               device_bind(parent, &mvpp2_driver, name, plat, subnode, &dev);
> +               dev->of_offset = subnode;
> +       }
> +
> +       return 0;
> +}
> +
> +static const struct udevice_id mvpp2_ids[] = {
> +       { .compatible = "marvell,armada-375-pp2" },
> +       { }
> +};
> +
> +U_BOOT_DRIVER(mvpp2_base) = {
> +       .name   = "mvpp2_base",
> +       .id     = UCLASS_MISC,
> +       .of_match = mvpp2_ids,
> +       .bind   = mvpp2_base_bind,
> +};
> --
> 2.7.3
>

Mostly looks good to me.

-Joe


More information about the U-Boot mailing list