Hi Antoine,

A quick look...

On 29 Aug 03:50 PM, Antoine Tenart wrote:
> This patch introduces the Marvell Berlin network unit driver, which uses
> the MVMDIO interface to communicate to the PHY. This is a fast Ethernet
> driver.
> 
> This driver is highly based on the mv643xx_eth driver, and reuse some of
> its functions. But lots of differences are there:
> - They do not have the same registers.
> - The mvberlin_eth driver only supports fast Ethernet.
> - The rx/tx handling functions logic is different.
> - The mv643xx_eth driver supports TSO.

TSO is just a software feature which needs just some basic hardware features
to work. I guess there's no point in implementing it for a fast Ethernet
controller?

> - The mvberlin_eth driver uses a hash table to filter incoming packets.
> 
> No packet is dropped during a ping flood (ping -f) and an iperf test
> shows:
> ------------------------------------------------------------
> Client connecting to 192.168.0.11, TCP port 5001
> TCP window size: 85.0 KByte (default)
> ------------------------------------------------------------
> [  3] local 192.168.0.20 port 44183 connected with 192.168.0.11 port 5001
> [ ID] Interval       Transfer     Bandwidth
> [  3]  0.0-10.0 sec   113 MBytes  94.8 Mbits/sec
> 
> Signed-off-by: Antoine Tenart <antoine.ten...@free-electrons.com>
> ---
>  drivers/net/ethernet/marvell/Kconfig        |    9 +
>  drivers/net/ethernet/marvell/Makefile       |    1 +
>  drivers/net/ethernet/marvell/mvberlin_eth.c | 2081 
> +++++++++++++++++++++++++++
>  3 files changed, 2091 insertions(+)
>  create mode 100644 drivers/net/ethernet/marvell/mvberlin_eth.c
> 
> diff --git a/drivers/net/ethernet/marvell/Kconfig 
> b/drivers/net/ethernet/marvell/Kconfig
> index 1b4fc7c639e6..a4f12257d099 100644
> --- a/drivers/net/ethernet/marvell/Kconfig
> +++ b/drivers/net/ethernet/marvell/Kconfig
> @@ -18,6 +18,15 @@ config NET_VENDOR_MARVELL
>  
>  if NET_VENDOR_MARVELL
>  
> +config MVBERLIN_ETH
> +     tristate "Marvell Berlin ethernet support"
> +     depends on ARCH_BERLIN && INET
> +     select PHYLIB
> +     select MVMDIO
> +     ---help---
> +       This driver supports the ethernet interface of the Marvell
> +       Berlin SoCs.
> +
>  config MV643XX_ETH
>       tristate "Marvell Discovery (643XX) and Orion ethernet support"
>       depends on (MV64X60 || PPC32 || PLAT_ORION) && INET
> diff --git a/drivers/net/ethernet/marvell/Makefile 
> b/drivers/net/ethernet/marvell/Makefile
> index f6425bd2884b..a802dba2503e 100644
> --- a/drivers/net/ethernet/marvell/Makefile
> +++ b/drivers/net/ethernet/marvell/Makefile
> @@ -2,6 +2,7 @@
>  # Makefile for the Marvell device drivers.
>  #
>  
> +obj-$(CONFIG_MVBERLIN_ETH) += mvberlin_eth.o
>  obj-$(CONFIG_MVMDIO) += mvmdio.o
>  obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
>  obj-$(CONFIG_MVNETA) += mvneta.o
> diff --git a/drivers/net/ethernet/marvell/mvberlin_eth.c 
> b/drivers/net/ethernet/marvell/mvberlin_eth.c
> new file mode 100644
> index 000000000000..5f1874b58017
> --- /dev/null
> +++ b/drivers/net/ethernet/marvell/mvberlin_eth.c
> @@ -0,0 +1,2081 @@
> +/*
> + * Copyright (C) 2014 Marvell Technology Group Ltd.
> + *
> + * Antoine Tenart <antoine.ten...@free-electrons.com>
> + * Jisheng Zhang <jszh...@marvell.com>
> + *
> + * Based on the driver for Marvell Discovery (MV643XX) and Marvell Orion
> + * ethernet ports
> + * Copyright (C) 2002 Matthew Dharm <mdh...@momenco.com>
> + *
> + * Based on the 64360 driver from:
> + * Copyright (C) 2002 Rabeeh Khoury <rab...@galileo.co.il>
> + *                 Rabeeh Khoury <rab...@marvell.com>
> + *
> + * Copyright (C) 2003 PMC-Sierra, Inc.,
> + *   written by Manish Lachwani
> + *
> + * Copyright (C) 2003 Ralf Baechle <r...@linux-mips.org>
> + *
> + * Copyright (C) 2004-2006 MontaVista Software, Inc.
> + *                      Dale Farnsworth <d...@farnsworth.org>
> + *
> + * Copyright (C) 2004 Steven J. Hill <sjhi...@rockwellcollins.com>
> + *                                <sjh...@realitydiluted.com>
> + *
> + * Copyright (C) 2007-2008 Marvell Semiconductor
> + *                      Lennert Buytenhek <buyt...@marvell.com>
> + *
> + * Copyright (C) 2013 Michael Stapelberg <mich...@stapelberg.de>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/etherdevice.h>
> +#include <linux/ethtool.h>
> +#include <linux/in.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/ip.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/mv643xx_eth.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_net.h>
> +#include <linux/of_mdio.h>
> +#include <linux/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/tcp.h>
> +#include <linux/types.h>
> +#include <linux/udp.h>
> +#include <linux/workqueue.h>
> +
> +static const char mvberlin_eth_driver_name[] = "mvberlin_eth";
> +static const char mvberlin_eth_driver_version[] = "1.0";
> +
> +/* Main per-port registers. These live at offset 0x0400 */
> +#define PORT_CONFIG                  0x0000
> +#define  PROMISCUOUS_MODE            0x00000001

I think that using BIT() makes the code more readable.

> +
> +/* SDMA configuration register default value */
> +#if defined(__BIG_ENDIAN)
> +#define PORT_SDMA_CONFIG_DEFAULT_VALUE               \
> +             (BURST_SIZE_8_64BIT     |       \
> +              0x3c                   |       \
> +              RX_FRAME_INTERRUPT)
> +#elif defined(__LITTLE_ENDIAN)
> +#define PORT_SDMA_CONFIG_DEFAULT_VALUE               \
> +             (BURST_SIZE_8_64BIT     |       \
> +              BLM_RX_LE              |       \
> +              BLM_TX_LE              |       \
> +              0x3c                   |       \
> +              RX_FRAME_INTERRUPT)
> +#else
> +#error One of __BIG_ENDIAN or __LITTLE_ENDIAN must be defined
> +#endif
> +
> +/* Misc definitions */
> +#define DEFAULT_RX_QUEUE_SIZE        128
> +#define DEFAULT_TX_QUEUE_SIZE        512
> +#define SKB_DMA_REALIGN              ((PAGE_SIZE - NET_SKB_PAD) % 
> SMP_CACHE_BYTES)
> +
> +/* RX/TX descriptors */
> +#if defined(__BIG_ENDIAN)

Maybe you can pack this inside the above ifdef and squash all the
endian-dependent stuff?

> +struct rx_desc {
> +     u16 buf_size;           /* Buffer size                          */
> +     u16 byte_cnt;           /* Descriptor buffer byte count         */
> +     u32 cmd_sts;            /* Command/status field                 */
> +     u32 next_desc_ptr;      /* Next descriptor pointer              */
> +     u32 buf_ptr;            /* Descriptor buffer pointer            */
> +};
> +
> +struct tx_desc {
> +     u16 byte_cnt;           /* buffer byte count                    */
> +     u16 l4i_chk;            /* CPU provided TCP checksum            */
> +     u32 cmd_sts;            /* Command/status field                 */
> +     u32 next_desc_ptr;      /* Pointer to next descriptor           */
> +     u32 buf_ptr;            /* pointer to buffer for this descriptor*/
> +};
> +#elif defined(__LITTLE_ENDIAN)
> +struct rx_desc {
> +     u32 cmd_sts;            /* Descriptor command status            */
> +     u16 byte_cnt;           /* Descriptor buffer byte count         */
> +     u16 buf_size;           /* Buffer size                          */
> +     u32 buf_ptr;            /* Descriptor buffer pointer            */
> +     u32 next_desc_ptr;      /* Next descriptor pointer              */
> +};
> +
> +struct tx_desc {
> +     u32 cmd_sts;            /* Command/status field                 */
> +     u16 l4i_chk;            /* CPU provided TCP checksum            */
> +     u16 byte_cnt;           /* buffer byte count                    */
> +     u32 buf_ptr;            /* pointer to buffer for this descriptor*/
> +     u32 next_desc_ptr;      /* Pointer to next descriptor           */
> +};
> +#else
> +#error One of __BIG_ENDIAN or __LITTLE_ENDIAN must be defined
> +#endif
> +
> +/* RX & TX descriptor command */
> +#define BUFFER_OWNED_BY_DMA          0x80000000
> +

Ditto about BIT() usage.

> +static netdev_tx_t mvberlin_eth_xmit(struct sk_buff *skb,
> +                                  struct net_device *dev)
> +{
> +     struct mvberlin_eth_private *mp = netdev_priv(dev);
> +     int length, queue;
> +     struct tx_queue *txq;
> +     struct netdev_queue *nq;
> +
> +     queue = skb_get_queue_mapping(skb);
> +     txq = mp->txq + queue;
> +     nq = netdev_get_tx_queue(dev, queue);
> +
> +     if (has_tiny_unaligned_frags(skb) && __skb_linearize(skb)) {
> +             netdev_printk(KERN_DEBUG, dev,
> +                           "failed to linearize skb with tiny unaligned 
> fragment\n");

netdev_dbg?

> +             return NETDEV_TX_BUSY;
> +     }
> +
> +     length = skb->len;
> +
> +     if (!txq_submit_skb(txq, skb, dev)) {
> +             txq->tx_bytes += length;
> +             txq->tx_packets++;
> +
> +             if (txq->tx_desc_count >= txq->tx_stop_threshold)
> +                     netif_tx_stop_queue(nq);
> +     } else {
> +             txq->tx_dropped++;
> +             dev_kfree_skb_any(skb);
> +     }
> +
> +     return NETDEV_TX_OK;
> +}
> +
> +
> +static void handle_link_event(struct mvberlin_eth_private *mp)
> +{
> +     struct net_device *dev = mp->dev;
> +     u32 port_status;
> +     int speed;
> +     int duplex;
> +     int fc;
> +
> +     port_status = rdlp(mp, PORT_STATUS);
> +     if (!(port_status & LINK_UP)) {
> +             if (netif_carrier_ok(dev)) {
> +                     int i;
> +
> +                     netdev_info(dev, "link down\n");
> +
> +                     netif_carrier_off(dev);
> +
> +                     for (i = 0; i < mp->txq_count; i++) {
> +                             struct tx_queue *txq = mp->txq + i;
> +
> +                             txq_reclaim(txq, txq->tx_ring_size, 1);
> +                             txq_reset_hw_ptr(txq);
> +                     }
> +             }
> +             return;
> +     }
> +
> +     switch (port_status & PORT_SPEED_MASK) {
> +     case PORT_SPEED_10:
> +             speed = 10;
> +             break;
> +     case PORT_SPEED_100:
> +             speed = 100;
> +             break;
> +     default:
> +             speed = -1;
> +             break;
> +     }
> +
> +     duplex = (port_status & FULL_DUPLEX) ? 1 : 0;
> +     fc = (port_status & FLOW_CONTROL_ENABLED) ? 1 : 0;
> +
> +     netdev_info(dev, "link up, %d Mb/s, %s duplex, flow control %sabled\n",
> +                 speed, duplex ? "full" : "half", fc ? "en" : "dis");

Maybe you can use phy_print_status() instead of rolling your own?

> +
> +     if (!netif_carrier_ok(dev))
> +             netif_carrier_on(dev);
> +}
> +
> +static irqreturn_t mvberlin_eth_irq(int irq, void *dev_id)
> +{
> +     struct net_device *dev = (struct net_device *)dev_id;
> +     struct mvberlin_eth_private *mp = netdev_priv(dev);
> +     u32 int_cause, txstatus;
> +     int i;
> +
> +     int_cause = rdlp(mp, INT_CAUSE) & mp->int_mask;
> +
> +     if (int_cause == 0)
> +             return IRQ_NONE;
> +     wrlp(mp, INT_CAUSE, ~int_cause);
> +
> +     if (int_cause & INT_RX) {
> +             wrlp(mp, INT_MASK, mp->int_mask & ~INT_RX);
> +             napi_schedule(&mp->napi);
> +     }
> +
> +     if (int_cause & INT_EXT)
> +             handle_link_event(mp);
> +
> +     txstatus = int_cause & INT_TX;
> +     for (i = 0; i < mp->txq_count; ++i) {
> +             if (txstatus & INT_TX_0 << i) {
> +                     txq_reclaim(mp->txq + i, 16, 0);
> +                     txq_maybe_wake(mp->txq + i);
> +             }
> +     }
> +
> +     txstatus = ((int_cause & INT_TX_END) >> 6) &
> +                ~((rdlp(mp, SDMA_COMMAND) >> 16) & 0x3);
> +     for (i = 0; i < mp->txq_count; ++i) {
> +             if (txstatus & 1 << i)
> +                     txq_kick(mp->txq + i);
> +     }
> +
> +     return IRQ_HANDLED;
> +}

> +static void set_params(struct mvberlin_eth_private *mp,
> +                    struct mv643xx_eth_platform_data *pd)
> +{
> +     struct net_device *dev = mp->dev;
> +
> +     if (is_valid_ether_addr(pd->mac_addr))
> +             memcpy(dev->dev_addr, pd->mac_addr, ETH_ALEN);
> +     else
> +             uc_addr_get(mp, dev->dev_addr);
> +
> +     mp->rx_ring_size = DEFAULT_RX_QUEUE_SIZE;
> +     if (pd->rx_queue_size)
> +             mp->rx_ring_size = pd->rx_queue_size;
> +     mp->rx_desc_sram_addr = pd->rx_sram_addr;
> +     mp->rx_desc_sram_size = pd->rx_sram_size;
> +
> +     mp->rxq_count = pd->rx_queue_count ? : 1;
> +
> +     mp->tx_ring_size = DEFAULT_TX_QUEUE_SIZE;
> +     if (pd->tx_queue_size)
> +             mp->tx_ring_size = pd->tx_queue_size;
> +
> +     mp->tx_desc_sram_addr = pd->tx_sram_addr;
> +     mp->tx_desc_sram_size = pd->tx_sram_size;
> +
> +     mp->txq_count = pd->tx_queue_count ? : 1;
> +}
> +
> +static const struct net_device_ops mvberlin_eth_netdev_ops = {
> +     .ndo_open               = mvberlin_eth_open,
> +     .ndo_stop               = mvberlin_eth_stop,
> +     .ndo_start_xmit         = mvberlin_eth_xmit,
> +     .ndo_set_rx_mode        = mvberlin_eth_set_multicast_list,
> +     .ndo_set_mac_address    = mvberlin_eth_set_mac_address,
> +     .ndo_validate_addr      = eth_validate_addr,
> +     .ndo_do_ioctl           = mvberlin_eth_ioctl,
> +     .ndo_change_mtu         = mvberlin_eth_change_mtu,
> +     .ndo_tx_timeout         = mvberlin_eth_tx_timeout,
> +     .ndo_get_stats          = mvberlin_eth_get_stats,
> +#ifdef CONFIG_NET_POLL_CONTROLLER
> +     .ndo_poll_controller    = mvberlin_eth_netpoll,
> +#endif
> +};
> +
> +static int mvberlin_eth_probe(struct platform_device *pdev)
> +{
> +     struct mv643xx_eth_platform_data *pd;

mv643xx_eth_platform_data? You really lost me here :) I'm having a hard
time figuring out who will set this platform data. I guess I'm overlooking
something?

> +     struct mvberlin_eth_private *mp;
> +     struct net_device *dev;
> +     struct resource *res;
> +     int ret;
> +
> +     dev = alloc_etherdev_mq(sizeof(struct mvberlin_eth_private), 8);
> +     if (!dev)
> +             return -ENOMEM;
> +
> +     pd = devm_kzalloc(&pdev->dev, sizeof(*pd), GFP_KERNEL);
> +     if (!pd)
> +             return -ENOMEM;
> +
> +     mp = netdev_priv(dev);
> +     platform_set_drvdata(pdev, mp);
> +     mp->dev = dev;
> +
> +     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +     if (!res)
> +             return -ENOMEM;
> +
> +     mp->shared = devm_kzalloc(&pdev->dev,
> +                               sizeof(struct mvberlin_eth_shared_private),
> +                               GFP_KERNEL);
> +     if (!mp->shared)
> +             return -ENOMEM;
> +
> +     mp->shared->base = devm_ioremap_resource(&pdev->dev, res);
> +     if (IS_ERR(mp->shared->base))
> +             return PTR_ERR(mp->shared->base);
> +     mp->base = mp->shared->base + 0x400;
> +
> +     mp->clk = devm_clk_get(&pdev->dev, NULL);
> +     if (!IS_ERR(mp->clk)) {
> +             clk_prepare_enable(mp->clk);
> +             mp->t_clk = clk_get_rate(mp->clk);
> +     }
> +

The binding doesn't declare the clock as optional, I'd say you should enforce
the requirement here.

> +     set_params(mp, pd);
> +     netif_set_real_num_tx_queues(dev, mp->txq_count);
> +     netif_set_real_num_rx_queues(dev, mp->rxq_count);
> +
> +     pd->phy_node = of_parse_phandle(pdev->dev.of_node, "phy-handle", 0);
> +     if (!pd->phy_node) {
> +             ret = -EINVAL;
> +             goto out;
> +     }
> +
> +     mp->phy = of_phy_connect(dev, pd->phy_node,
> +                              mvberlin_eth_adjust_link, 0,
> +                              PHY_INTERFACE_MODE_RGMII);
> +     if (!mp->phy) {
> +             ret = -EPROBE_DEFER;
> +             goto out;
> +     }
> +
> +     dev->ethtool_ops = &mvberlin_eth_ethtool_ops;
> +
> +     init_pscr(mp);
> +
> +     init_hash_table(mp);
> +     mvberlin_eth_program_unicast_filter(mp, NULL, dev->dev_addr);
> +
> +     mib_counters_clear(mp);
> +
> +     INIT_WORK(&mp->tx_timeout_task, tx_timeout_task);
> +
> +     netif_napi_add(dev, &mp->napi, mvberlin_eth_poll, NAPI_POLL_WEIGHT);
> +
> +     init_timer(&mp->rx_oom);
> +     mp->rx_oom.data = (unsigned long)mp;
> +     mp->rx_oom.function = oom_timer_wrapper;
> +
> +     res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> +     BUG_ON(!res);

Hm... BUG_ON!?!? Are you sure you want to kill the entire system?

There's another BUG_ON above, and since this is just network driver,
I think it can be relaxed.

> +     dev->irq = res->start;
> +
> +     dev->netdev_ops = &mvberlin_eth_netdev_ops;
> +
> +     dev->watchdog_timeo = 2 * HZ;
> +     dev->base_addr = 0;
> +
> +     SET_NETDEV_DEV(dev, &pdev->dev);
> +
> +     wrlp(mp, SDMA_CONFIG, PORT_SDMA_CONFIG_DEFAULT_VALUE);
> +
> +     ret = register_netdev(dev);
> +     if (ret)
> +             goto out;
> +
> +     netif_carrier_off(dev);
> +
> +     return 0;
> +
> +out:
> +     if (!IS_ERR(mp->clk))
> +             clk_disable_unprepare(mp->clk);
> +     free_netdev(dev);
> +
> +     return ret;
> +}
> +
> +static int mvberlin_eth_remove(struct platform_device *pdev)
> +{
> +     struct mvberlin_eth_private *mp = platform_get_drvdata(pdev);
> +
> +     unregister_netdev(mp->dev);
> +     if (mp->phy != NULL)
> +             phy_disconnect(mp->phy);
> +     cancel_work_sync(&mp->tx_timeout_task);
> +
> +     if (!IS_ERR(mp->clk))
> +             clk_disable_unprepare(mp->clk);
> +

Ditto for the optional clock.

-- 
Ezequiel GarcĂ­a, Free Electrons
Embedded Linux, Kernel and Android Engineering
http://free-electrons.com
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to