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

Joe Hershberger joe.hershberger at gmail.com
Fri Mar 25 22:04:00 CET 2016


Hi Stefan,

On Wed, Mar 23, 2016 at 1:39 AM, Stefan Roese <sr at denx.de> wrote:
> Hi Joe,
>
> On 22.03.2016 20:10, Joe Hershberger wrote:
>>
>> Sorry for the delay.
>
>
> No problem. Thanks for the review.
>
>
>> 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.
>
>
> Good catch. Removed in v2.
>
>>> +
>>> +/* 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
>
>
> Changed to use these U-Boot defines instead. Thanks.
>
> Does it perhaps make sense to globally change all those U-Boot defines
> to the Linux ones instead?

That sounds like a good idea to me.

>>> +
>>> +#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.
>
>
> I'm not a big fan of using headers, when the macros / defines etc
> are only referenced by this one file. Additionally, the original
> Linux driver also includes all this in the C file. So it makes
> syncing easier. I would like to keep it in one file.

OK.

>
>>> +
>>> +/* 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
>
>
> Fixed.
>
>
>>> +        */
>>> +
>>> +       /* 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?
>
>
> Not really. I've moved most of this into the new probe function
> of the parent driver. Looks much cleaner this may. Thanks.
>
>
>>> +       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.
>
>
> Thanks for the review. v2 coming shortly...
>
> Thanks,
> Stefan
>



More information about the U-Boot mailing list