Hi Stefan, On Wed, Mar 23, 2016 at 1:39 AM, Stefan Roese <s...@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 <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. > > > 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 > _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot