[U-Boot] [PATCH 2/6] net: mvpp2.c: Add Marvell mvpp2 network driver for Armada 375
Stefan Roese
sr at denx.de
Wed Mar 23 07:39:22 CET 2016
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?
>> +
>> +#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.
>> +
>> +/* 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