[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