Hit Stefan, Sorry for the delay.
On Tue, Mar 15, 2016 at 11:35 AM, Stefan Roese <s...@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 <s...@denx.de> > Cc: Luka Perkov <luka.per...@sartura.hr> > Cc: Joe Hershberger <joe.hershber...@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 <m...@semihalf.com> > + * > + * U-Boot version: > + * Copyright (C) 2016 Stefan Roese <s...@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 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot