[PATCH] Add support for Fritzbox 4040

Christian Dresel fff at chrisi01.de
Mi Nov 28 14:41:15 CET 2018


hi

Am 28.11.18 um 14:35 schrieb Adrian Schmutzler:
> Hallo Christian,
> 
>> -----Original Message-----
>> From: franken-dev [mailto:franken-dev-bounces at freifunk.net] On Behalf Of
>> Christian Dresel
>> Sent: Sonntag, 25. November 2018 10:11
>> To: franken-dev at freifunk.net
>> Subject: [PATCH] Add support for Fritzbox 4040
>>
>> Add a new target ipq40xx
>> We need the following patches to use VLANs
>> https://github.com/chunkeey/LEDE-
>> IPQ40XX/commit/465fd7c8e131ec49563d8d7e0502fb5e13dc2124
>> https://github.com/chunkeey/LEDE-
>> IPQ40XX/commit/f28a3fbebe976414aa62650c25f2298e58d3c306
> 
> das kann OpenWRT nicht selber?

nein, OpenWRT erzeugt ein eth0 (war glaub ich alle 4 gelben Ports,
konnte man nicht auftrennen) und eth1 (war glaub ich WAN vllt. auch
genau andersrum, da bin ich mir eben unsicher). VLANs sind mit dem
ipq40xx target ohne diesen Treiber nicht möglich (hab ich getestet, ging
wirklich nicht). Dieser Treiber führt dann ganz normal VLANs (wie man es
vom ar71xx target kennt) ein so das OpenWRT sein normales eth0.1
(Client) und eth0.2 (WAN) erzeugt und auch unser configurenetwork ganz
normal mit klar kommt und das eth0.3 (Batman) erzeugen kann.

mfg

Christian

> 
> Grüße
> 
> Adrian
> 
>>
>> Signed-off-by: Christian Dresel <fff at chrisi01.de>
>> ---
>>  bsp/ipq40xx.bsp                                    |    6 +
>>  bsp/ipq40xx/.config                                |   22 +
>>  .../0006-ipq40xx-ipqess-ethernet-driver.patch      | 2178
>> ++++++++++++++++++++
>>  .../openwrt/0007-ipqess-integration.patch          |  207 ++
>>  .../openwrt/0008-Enable-QinQ-on-the-switch.patch   |  106 +
>>  build_patches/openwrt/0009-enable-IPQ_ESS.patch    |   23 +
>>  buildscript                                        |    6 +-
>>  .../files/etc/uci-defaults/50-fff-boardname        |    3 +
>>  .../fff-network/ipq40xx/network.avm_fritzbox-4040  |    8 +
>>  .../fff/fff-sysupgrade/files/etc/sysupgrade.sh     |    3 +
>>  10 files changed, 2559 insertions(+), 3 deletions(-)
>>  create mode 100644 bsp/ipq40xx.bsp
>>  create mode 100644 bsp/ipq40xx/.config
>>  create mode 100644 build_patches/openwrt/0006-ipq40xx-ipqess-ethernet-
>> driver.patch
>>  create mode 100644 build_patches/openwrt/0007-ipqess-integration.patch
>>  create mode 100644 build_patches/openwrt/0008-Enable-QinQ-on-the-
>> switch.patch
>>  create mode 100644 build_patches/openwrt/0009-enable-IPQ_ESS.patch
>>  create mode 100644 src/packages/fff/fff-
>> network/ipq40xx/network.avm_fritzbox-4040
>>
>> diff --git a/bsp/ipq40xx.bsp b/bsp/ipq40xx.bsp
>> new file mode 100644
>> index 0000000..eac5759
>> --- /dev/null
>> +++ b/bsp/ipq40xx.bsp
>> @@ -0,0 +1,6 @@
>> +machine=ipq40xx
>> +chipset=ipq40xx
>> +subtarget=generic
>> +target=$builddir/$machine
>> +images=("openwrt-${chipset}-avm_fritzbox-4040-squashfs-sysupgrade.bin"
>> +        )
>> diff --git a/bsp/ipq40xx/.config b/bsp/ipq40xx/.config
>> new file mode 100644
>> index 0000000..b923e3d
>> --- /dev/null
>> +++ b/bsp/ipq40xx/.config
>> @@ -0,0 +1,22 @@
>> +# Generated using "./buildscript config openwrt".
>> +# Do no edit manually
>> +#
>> +CONFIG_TARGET_ipq40xx=y
>> +CONFIG_TARGET_MULTI_PROFILE=y
>> +CONFIG_TARGET_DEVICE_ipq40xx_DEVICE_avm_fritzbox-4040=y
>> +CONFIG_TARGET_DEVICE_ipq40xx_DEVICE_avm_fritzbox-4040=y
>> +CONFIG_BUSYBOX_CUSTOM=y
>> +# CONFIG_TARGET_ipq806x_DEVICE_compex_wpq864 is not set
>> +# CONFIG_TARGET_ipq806x_DEVICE_linksys_ea8500 is not set
>> +# CONFIG_TARGET_ipq806x_DEVICE_nec_wg2600hp is not set
>> +# CONFIG_TARGET_ipq806x_DEVICE_netgear_d7800 is not set
>> +# CONFIG_TARGET_ipq806x_DEVICE_netgear_r7500 is not set
>> +# CONFIG_TARGET_ipq806x_DEVICE_netgear_r7500v2 is not set
>> +# CONFIG_TARGET_ipq806x_DEVICE_netgear_r7800 is not set
>> +# CONFIG_TARGET_ipq806x_DEVICE_qcom_ipq8064-ap148 is not set
>> +# CONFIG_TARGET_ipq806x_DEVICE_qcom_ipq8064-ap148-legacy is not set
>> +# CONFIG_TARGET_ipq806x_DEVICE_qcom_ipq8064-db149 is not set
>> +# CONFIG_TARGET_ipq806x_DEVICE_tplink_c2600 is not set
>> +# CONFIG_TARGET_ipq806x_DEVICE_tplink_vr2600v is not set
>> +# CONFIG_TARGET_ipq806x_DEVICE_zyxel_nbg6817 is not set
>> +# CONFIG_TARGET_ipq806x_Default is not set
>> \ No newline at end of file
>> diff --git a/build_patches/openwrt/0006-ipq40xx-ipqess-ethernet-driver.patch
>> b/build_patches/openwrt/0006-ipq40xx-ipqess-ethernet-driver.patch
>> new file mode 100644
>> index 0000000..41a0f66
>> --- /dev/null
>> +++ b/build_patches/openwrt/0006-ipq40xx-ipqess-ethernet-driver.patch
>> @@ -0,0 +1,2178 @@
>> +From 465fd7c8e131ec49563d8d7e0502fb5e13dc2124 Mon Sep 17 00:00:00 2001
>> +From: John Crispin <john at phrozen.org>
>> +Date: Sun, 18 Nov 2018 20:30:48 +0100
>> +Subject: [PATCH 1/2] ipq40xx: ipqess ethernet driver
>> +
>> +modified John Crispin's essedma replacement driver.
>> +
>> +Fouled-up-by: Christian Lamparter <chunkeey at gmail.com>
>> +---
>> + .../drivers/net/ethernet/qualcomm/ipqess.c         | 1206
>> ++++++++++++++++++++
>> + .../drivers/net/ethernet/qualcomm/ipqess.h         |  568 +++++++++
>> + .../drivers/net/ethernet/qualcomm/ipqess_ethtool.c |  191 ++++
>> + .../linux/ipq40xx/patches-4.14/999-0-ipqess.patch  |  167 +++
>> + 4 files changed, 2132 insertions(+)
>> + create mode 100644 target/linux/ipq40xx/files-
>> 4.14/drivers/net/ethernet/qualcomm/ipqess.c
>> + create mode 100644 target/linux/ipq40xx/files-
>> 4.14/drivers/net/ethernet/qualcomm/ipqess.h
>> + create mode 100644 target/linux/ipq40xx/files-
>> 4.14/drivers/net/ethernet/qualcomm/ipqess_ethtool.c
>> + create mode 100644 target/linux/ipq40xx/patches-4.14/999-0-ipqess.patch
>> +
>> +diff --git a/target/linux/ipq40xx/files-
>> 4.14/drivers/net/ethernet/qualcomm/ipqess.c b/target/linux/ipq40xx/files-
>> 4.14/drivers/net/ethernet/qualcomm/ipqess.c
>> +new file mode 100644
>> +index 0000000000..affe403fea
>> +--- /dev/null
>> ++++ b/target/linux/ipq40xx/files-
>> 4.14/drivers/net/ethernet/qualcomm/ipqess.c
>> +@@ -0,0 +1,1206 @@
>> ++/*
>> ++ * Copyright (c) 2014 - 2017, The Linux Foundation. All rights reserved.
>> ++ *
>> ++ * Permission to use, copy, modify, and/or distribute this software for
>> ++ * any purpose with or without fee is hereby granted, provided that the
>> ++ * above copyright notice and this permission notice appear in all copies.
>> ++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
>> WARRANTIES
>> ++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
>> OF
>> ++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE
>> LIABLE FOR
>> ++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
>> DAMAGES
>> ++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
>> WHETHER IN AN
>> ++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
>> ARISING OUT
>> ++ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
>> SOFTWARE.
>> ++ */
>> ++
>> ++#include <linux/if_vlan.h>
>> ++#include <linux/interrupt.h>
>> ++#include <linux/module.h>
>> ++#include <linux/of.h>
>> ++#include <linux/of_net.h>
>> ++#include <linux/of_mdio.h>
>> ++#include <linux/of_device.h>
>> ++#include <linux/platform_device.h>
>> ++#include <linux/phy.h>
>> ++#include <linux/skbuff.h>
>> ++#include <linux/vmalloc.h>
>> ++
>> ++#include <net/checksum.h>
>> ++#include <net/ip6_checksum.h>
>> ++
>> ++#include "ipqess.h"
>> ++
>> ++#define IPQESS_RRD_SIZE		16
>> ++#define IPQESS_NEXT_IDX(X, Y)  (((X) + 1) & ((Y) - 1))
>> ++#define IPQESS_TX_DMA_BUF_LEN	0x3fff
>> ++
>> ++static void ipqess_w32(struct ipqess *ess, u32 reg, u32 val)
>> ++{
>> ++	__raw_writel(val, ess->hw_addr + reg);
>> ++}
>> ++
>> ++static u32 ipqess_r32(struct ipqess *ess, u16 reg)
>> ++{
>> ++	return __raw_readl(ess->hw_addr + reg);
>> ++}
>> ++
>> ++static void ipqess_m32(struct ipqess *ess, u32 mask, u32 val, u16 reg)
>> ++{
>> ++	u32 _val = ipqess_r32(ess, reg);
>> ++	_val &= ~mask;
>> ++	_val |= val;
>> ++	ipqess_w32(ess, reg, _val);
>> ++}
>> ++
>> ++static int ipqess_tx_ring_alloc(struct ipqess *ess)
>> ++{
>> ++	int i;
>> ++
>> ++	for (i = 0; i < IPQESS_NETDEV_QUEUES; i++) {
>> ++		u16 idx;
>> ++
>> ++		ess->tx_ring[i].ess = ess;
>> ++		ess->tx_ring[i].idx = i * 4;
>> ++		ess->tx_ring[i].count = IPQESS_TX_RING_SIZE;
>> ++		ess->tx_ring[i].nq = netdev_get_tx_queue(ess->netdev, i);
>> ++
>> ++		ess->tx_ring[i].buf = devm_kzalloc(&ess->pdev->dev,
>> ++			sizeof(struct ipqess_buf) * IPQESS_TX_RING_SIZE,
>> ++			GFP_KERNEL);
>> ++		if (!ess->tx_ring[i].buf) {
>> ++			netdev_err(ess->netdev, "buffer alloc of tx ring failed");
>> ++			return -ENOMEM;
>> ++		}
>> ++
>> ++		ess->tx_ring[i].hw_desc = dmam_alloc_coherent(&ess->pdev-
>>> dev,
>> ++			sizeof(struct ipqess_tx_desc) * IPQESS_TX_RING_SIZE,
>> ++			&ess->tx_ring[i].dma, GFP_KERNEL);
>> ++		if (!ess->tx_ring[i].hw_desc) {
>> ++			netdev_err(ess->netdev, "descriptor allocation for tx
>> ring failed");
>> ++			return -ENOMEM;
>> ++		}
>> ++
>> ++		ipqess_w32(ess, IPQESS_REG_TPD_BASE_ADDR_Q(ess-
>>> tx_ring[i].idx),
>> ++			 (u32)ess->tx_ring[i].dma);
>> ++
>> ++		idx = ipqess_r32(ess, IPQESS_REG_TPD_IDX_Q(ess-
>>> tx_ring[i].idx));
>> ++		idx >>= IPQESS_TPD_CONS_IDX_SHIFT;
>> ++		idx &= 0xffff;
>> ++		ess->tx_ring[i].head = ess->tx_ring[i].tail = idx;
>> ++
>> ++		ipqess_m32(ess, IPQESS_TPD_PROD_IDX_MASK <<
>> IPQESS_TPD_PROD_IDX_SHIFT,
>> ++			 idx, IPQESS_REG_TPD_IDX_Q(ess->tx_ring[i].idx));
>> ++		ipqess_w32(ess, IPQESS_REG_TX_SW_CONS_IDX_Q(ess-
>>> tx_ring[i].idx), idx);
>> ++		ipqess_w32(ess, IPQESS_REG_TPD_RING_SIZE,
>> IPQESS_TX_RING_SIZE);
>> ++	}
>> ++
>> ++	ipqess_m32(ess, 0, BIT(IPQESS_LOAD_PTR_SHIFT),
>> ++		 IPQESS_REG_TX_SRAM_PART);
>> ++
>> ++	return 0;
>> ++}
>> ++
>> ++static int ipqess_tx_unmap_and_free(struct device *dev, struct ipqess_buf
>> *buf)
>> ++{
>> ++	int len = 0;
>> ++
>> ++	if (buf->flags & IPQESS_DESC_SINGLE)
>> ++		dma_unmap_single(dev, buf->dma,	buf->length,
>> DMA_TO_DEVICE);
>> ++	else if (buf->flags & IPQESS_DESC_PAGE)
>> ++		dma_unmap_page(dev, buf->dma, buf->length,
>> DMA_TO_DEVICE);
>> ++
>> ++	if (buf->flags & IPQESS_DESC_LAST) {
>> ++		len = buf->skb->len;
>> ++		dev_kfree_skb_any(buf->skb);
>> ++	}
>> ++
>> ++	buf->flags = 0;
>> ++
>> ++	return len;
>> ++}
>> ++
>> ++static void ipqess_tx_ring_free(struct ipqess *ess)
>> ++{
>> ++	int i;
>> ++
>> ++	for (i = 0; i < IPQESS_NETDEV_QUEUES; i++) {
>> ++		int j;
>> ++
>> ++		if (ess->tx_ring[i].hw_desc)
>> ++			continue;
>> ++
>> ++		for (j = 0; j < IPQESS_TX_RING_SIZE; j++) {
>> ++			struct ipqess_buf *buf = &ess->tx_ring[i].buf[j];
>> ++
>> ++			ipqess_tx_unmap_and_free(&ess->pdev->dev, buf);
>> ++		}
>> ++
>> ++		ess->tx_ring[i].buf = NULL;
>> ++	}
>> ++}
>> ++
>> ++static int ipqess_rx_buf_prepare(struct ipqess_buf *buf,
>> ++	struct ipqess_rx_ring *rx_ring)
>> ++{
>> ++	buf->dma = dma_map_single(&rx_ring->ess->pdev->dev, buf->skb-
>>> data,
>> ++				  IPQESS_RX_HEAD_BUFF_SIZE,
>> DMA_FROM_DEVICE);
>> ++	if (dma_mapping_error(&rx_ring->ess->pdev->dev, buf->dma)) {
>> ++		WARN_ONCE(0, "IPQESS DMA mapping failed for linear address
>> %x", buf->dma);
>> ++		dev_kfree_skb_any(buf->skb);
>> ++		buf->skb = NULL;
>> ++		/* TODO: which return code */
>> ++		return -EINVAL;
>> ++	}
>> ++
>> ++	buf->length = IPQESS_RX_HEAD_BUFF_SIZE;
>> ++	rx_ring->hw_desc[rx_ring->head] = (void *)buf->dma;
>> ++	rx_ring->head = (rx_ring->head + 1) % IPQESS_RX_RING_SIZE;
>> ++
>> ++	ipqess_m32(rx_ring->ess, IPQESS_RFD_PROD_IDX_BITS,
>> ++		 (rx_ring->head + IPQESS_RX_RING_SIZE - 1) %
>> IPQESS_RX_RING_SIZE,
>> ++		 IPQESS_REG_RFD_IDX_Q(rx_ring->idx));
>> ++
>> ++	return 0;
>> ++}
>> ++
>> ++static int ipqess_rx_buf_alloc_napi(struct ipqess_rx_ring *rx_ring)
>> ++{
>> ++	struct ipqess_buf *buf = &rx_ring->buf[rx_ring->head];
>> ++
>> ++	buf->skb = napi_alloc_skb(&rx_ring->napi_rx,
>> ++		IPQESS_RX_HEAD_BUFF_SIZE);
>> ++	if (!buf->skb)
>> ++		return -ENOMEM;
>> ++	return ipqess_rx_buf_prepare(buf, rx_ring);
>> ++}
>> ++
>> ++static int ipqess_rx_buf_alloc(struct ipqess_rx_ring *rx_ring)
>> ++{
>> ++	struct ipqess_buf *buf = &rx_ring->buf[rx_ring->head];
>> ++
>> ++	buf->skb = netdev_alloc_skb_ip_align(rx_ring->ess->netdev,
>> ++		IPQESS_RX_HEAD_BUFF_SIZE);
>> ++	if (!buf->skb)
>> ++		return -ENOMEM;
>> ++	return ipqess_rx_buf_prepare(buf, rx_ring);
>> ++}
>> ++
>> ++static void ipqess_refill_work(struct work_struct *work)
>> ++{
>> ++	struct ipqess_rx_ring *rx_ring = container_of(work,
>> ++		struct ipqess_rx_ring, refill_work);
>> ++	int refill = 0;
>> ++
>> ++	/* don't let this loop by accident. */
>> ++	while (atomic_dec_and_test(&rx_ring->refill_count)) {
>> ++		if (ipqess_rx_buf_alloc(rx_ring)) {
>> ++			refill++;
>> ++			dev_dbg(&rx_ring->ess->pdev->dev,
>> ++         			"Not all buffers was reallocated");
>> ++		}
>> ++	}
>> ++
>> ++	if (refill || atomic_read(&rx_ring->refill_count)) {
>> ++		atomic_add(refill, &rx_ring->refill_count);
>> ++		schedule_work(&rx_ring->refill_work);
>> ++	}
>> ++}
>> ++
>> ++static int ipqess_rx_ring_alloc(struct ipqess *ess)
>> ++{
>> ++	int i;
>> ++
>> ++	for (i = 0; i < IPQESS_NETDEV_QUEUES; i++) {
>> ++		int j;
>> ++
>> ++		ess->rx_ring[i].ess = ess;
>> ++		ess->rx_ring[i].idx = i;
>> ++
>> ++		ess->rx_ring[i].buf = devm_kzalloc(&ess->pdev->dev,
>> ++			sizeof(struct ipqess_buf) * IPQESS_RX_RING_SIZE,
>> ++			GFP_KERNEL);
>> ++		if (!ess->rx_ring[i].buf)
>> ++			return -ENOMEM;
>> ++
>> ++		ess->rx_ring[i].hw_desc = dmam_alloc_coherent(&ess->pdev-
>>> dev,
>> ++			sizeof(struct ipqess_rx_desc) * IPQESS_RX_RING_SIZE,
>> ++			&ess->rx_ring[i].dma, GFP_KERNEL);
>> ++		if (!ess->rx_ring[i].hw_desc)
>> ++			return -ENOMEM;
>> ++
>> ++		for (j = 0; j < IPQESS_RX_RING_SIZE; j++)
>> ++			if (ipqess_rx_buf_alloc(&ess->rx_ring[i]) < 0)
>> ++				return -ENOMEM;
>> ++
>> ++		INIT_WORK(&ess->rx_ring[i].refill_work, ipqess_refill_work);
>> ++
>> ++		ipqess_w32(ess, IPQESS_REG_RFD_BASE_ADDR_Q(i),
>> ++			 (u32)(ess->rx_ring[i].dma));
>> ++	}
>> ++
>> ++	ipqess_w32(ess, IPQESS_REG_RX_DESC0,
>> ++		 (IPQESS_RX_HEAD_BUFF_SIZE << IPQESS_RX_BUF_SIZE_SHIFT)
>> |
>> ++		 (IPQESS_RX_RING_SIZE << IPQESS_RFD_RING_SIZE_SHIFT));
>> ++
>> ++	return 0;
>> ++}
>> ++
>> ++static void ipqess_rx_ring_free(struct ipqess *ess)
>> ++{
>> ++	int i;
>> ++
>> ++	for (i = 0, i = 0; i < IPQESS_NETDEV_QUEUES; i++) {
>> ++		int j;
>> ++
>> ++		for (j = 0; j < IPQESS_RX_RING_SIZE; j++) {
>> ++			dma_unmap_single(&ess->pdev->dev,
>> ++					 ess->rx_ring[i].buf[j].dma,
>> ++					 ess->rx_ring[i].buf[j].length,
>> ++					 DMA_FROM_DEVICE);
>> ++			dev_kfree_skb_any(ess->rx_ring[i].buf[j].skb);
>> ++		}
>> ++
>> ++		atomic_set(&ess->rx_ring[i].refill_count, 0);
>> ++		cancel_work_sync(&ess->rx_ring[i].refill_work);
>> ++	}
>> ++}
>> ++
>> ++static struct net_device_stats *ipqess_get_stats(struct net_device *netdev)
>> ++{
>> ++	struct ipqess *ess = netdev_priv(netdev);
>> ++	uint32_t *p;
>> ++	int i;
>> ++	u32 stat;
>> ++
>> ++	spin_lock(&ess->stats_lock);
>> ++	p = (uint32_t *)&(ess->ipqessstats);
>> ++
>> ++	for (i = 0; i < IPQESS_MAX_TX_QUEUE; i++) {
>> ++		stat = ipqess_r32(ess, IPQESS_REG_TX_STAT_PKT_Q(i));
>> ++		*p += stat;
>> ++		p++;
>> ++	}
>> ++
>> ++	for (i = 0; i < IPQESS_MAX_TX_QUEUE; i++) {
>> ++		stat = ipqess_r32(ess, IPQESS_REG_TX_STAT_BYTE_Q(i));
>> ++		*p += stat;
>> ++		p++;
>> ++	}
>> ++
>> ++	for (i = 0; i < IPQESS_MAX_RX_QUEUE; i++) {
>> ++		stat = ipqess_r32(ess, IPQESS_REG_RX_STAT_PKT_Q(i));
>> ++		*p += stat;
>> ++		p++;
>> ++	}
>> ++
>> ++	for (i = 0; i < IPQESS_MAX_RX_QUEUE; i++) {
>> ++		stat = ipqess_r32(ess, IPQESS_REG_RX_STAT_BYTE_Q(i));
>> ++		*p += stat;
>> ++		p++;
>> ++	}
>> ++
>> ++	spin_unlock(&ess->stats_lock);
>> ++	return &ess->stats;
>> ++}
>> ++
>> ++static void ipqess_adjust_link(struct net_device *netdev)
>> ++{
>> ++	if (netdev->phydev->link)
>> ++		netif_carrier_on(netdev);
>> ++	else
>> ++		netif_carrier_off(netdev);
>> ++}
>> ++
>> ++static int ipqess_phy_connect_node(struct ipqess *ess,
>> ++				 struct device_node *phy_node)
>> ++{
>> ++	struct phy_device *phydev;
>> ++	int phy_mode;
>> ++
>> ++	phy_mode = of_get_phy_mode(phy_node);
>> ++	if (phy_mode < 0) {
>> ++		dev_err(&ess->netdev->dev, "incorrect phy-mode %d\n",
>> phy_mode);
>> ++		return -EINVAL;
>> ++	}
>> ++
>> ++	phydev = of_phy_connect(ess->netdev, phy_node,
>> ++				ipqess_adjust_link, 0, phy_mode);
>> ++	if (!phydev) {
>> ++		dev_err(&ess->netdev->dev, "could not connect to PHY\n");
>> ++		return -ENODEV;
>> ++	}
>> ++
>> ++	dev_info(&ess->netdev->dev,
>> ++		 "connected to PHY at %s [uid=%08x, driver=%s]\n",
>> ++		 phydev_name(phydev), phydev->phy_id,
>> ++		 phydev->drv->name);
>> ++
>> ++	return 0;
>> ++}
>> ++
>> ++static int ipqess_phy_connect(struct net_device *netdev)
>> ++{
>> ++	struct ipqess *ess = netdev_priv(netdev);
>> ++	struct device_node *np;
>> ++
>> ++	np = of_parse_phandle(ess->of_node, "phy-handle", 0);
>> ++	if (!np && of_phy_is_fixed_link(ess->of_node))
>> ++		if (!of_phy_register_fixed_link(ess->of_node))
>> ++			np = of_node_get(ess->of_node);
>> ++	if (!np)
>> ++		return -ENODEV;
>> ++
>> ++	switch (of_get_phy_mode(np)) {
>> ++	case PHY_INTERFACE_MODE_SGMII:
>> ++		break;
>> ++	default:
>> ++		goto err_phy;
>> ++	}
>> ++
>> ++	if (ipqess_phy_connect_node(ess, np))
>> ++		goto err_phy;
>> ++
>> ++	netdev->phydev->autoneg = AUTONEG_ENABLE;
>> ++	netdev->phydev->speed = 0;
>> ++	netdev->phydev->duplex = 0;
>> ++
>> ++	if (of_phy_is_fixed_link(ess->of_node))
>> ++		netdev->phydev->supported |=
>> ++			SUPPORTED_Pause | SUPPORTED_Asym_Pause;
>> ++
>> ++	netdev->phydev->supported &= PHY_GBIT_FEATURES |
>> SUPPORTED_Pause |
>> ++			SUPPORTED_Asym_Pause;
>> ++	netdev->phydev->advertising = netdev->phydev->supported |
>> ++		ADVERTISED_Autoneg;
>> ++	phy_start_aneg(netdev->phydev);
>> ++	of_node_put(np);
>> ++
>> ++	return 0;
>> ++
>> ++err_phy:
>> ++	if (of_phy_is_fixed_link(ess->of_node))
>> ++		of_phy_deregister_fixed_link(ess->of_node);
>> ++	of_node_put(np);
>> ++	dev_err(&netdev->dev, "%s: invalid phy\n", __func__);
>> ++
>> ++	return -EINVAL;
>> ++}
>> ++
>> ++static int ipqess_rx_poll(struct ipqess_rx_ring *rx_ring, int budget)
>> ++{
>> ++	u32 length = 0, num_desc, tail;
>> ++	int done = 0;
>> ++
>> ++	tail = ipqess_r32(rx_ring->ess, IPQESS_REG_RFD_IDX_Q(rx_ring->idx));
>> ++	tail >>= IPQESS_RFD_CONS_IDX_SHIFT;
>> ++	tail &= IPQESS_RFD_CONS_IDX_MASK;
>> ++
>> ++	while (done < budget) {
>> ++		struct sk_buff *skb;
>> ++		struct ipqess_rx_desc *rd;
>> ++
>> ++		if (rx_ring->tail == tail)
>> ++			break;
>> ++
>> ++		skb = rx_ring->buf[rx_ring->tail].skb;
>> ++		dma_unmap_single(&rx_ring->ess->pdev->dev,
>> ++				 rx_ring->buf[rx_ring->tail].dma,
>> ++				 rx_ring->buf[rx_ring->tail].length,
>> ++				 DMA_FROM_DEVICE);
>> ++		rd = (struct ipqess_rx_desc *)skb->data;
>> ++		num_desc = rd->rrd1 & IPQESS_RRD_NUM_RFD_MASK;
>> ++		length = rd->rrd6 & IPQESS_RRD_PKT_SIZE_MASK;
>> ++		rx_ring->tail = IPQESS_NEXT_IDX(rx_ring->tail,
>> IPQESS_RX_RING_SIZE);
>> ++
>> ++		skb_reserve(skb, IPQESS_RRD_SIZE);
>> ++		if (num_desc > 1) {
>> ++			/* can we use build_skb here ? */
>> ++			struct sk_buff *skb_prev = NULL;
>> ++			int size_remaining;
>> ++			int i;
>> ++
>> ++			skb->data_len = 0;
>> ++			skb->tail += (IPQESS_RX_HEAD_BUFF_SIZE -
>> IPQESS_RRD_SIZE);
>> ++			skb->len = skb->truesize = length;
>> ++			size_remaining = length - (IPQESS_RX_HEAD_BUFF_SIZE
>> - IPQESS_RRD_SIZE);
>> ++
>> ++			for (i = 1; i < num_desc; i++) {
>> ++				/* TODO: use build_skb ? */
>> ++				struct sk_buff *skb_temp = rx_ring-
>>> buf[rx_ring->tail].skb;
>> ++
>> ++				dma_unmap_single(&rx_ring->ess->pdev->dev,
>> ++						 rx_ring->buf[rx_ring->tail].dma,
>> ++						 rx_ring->buf[rx_ring-
>>> tail].length,
>> ++						 DMA_FROM_DEVICE);
>> ++
>> ++				skb_put(skb_temp, min(size_remaining,
>> IPQESS_RX_HEAD_BUFF_SIZE));
>> ++				if (skb_prev)
>> ++					skb_prev->next = rx_ring->buf[rx_ring-
>>> tail].skb;
>> ++				else
>> ++					skb_shinfo(skb)->frag_list = rx_ring-
>>> buf[rx_ring->tail].skb;
>> ++				skb_prev = rx_ring->buf[rx_ring->tail].skb;
>> ++				rx_ring->buf[rx_ring->tail].skb->next = NULL;
>> ++
>> ++				skb->data_len += rx_ring->buf[rx_ring->tail].skb-
>>> len;
>> ++				size_remaining -= rx_ring->buf[rx_ring-
>>> tail].skb->len;
>> ++
>> ++				rx_ring->tail = IPQESS_NEXT_IDX(rx_ring->tail,
>> IPQESS_RX_RING_SIZE);
>> ++			}
>> ++
>> ++		} else {
>> ++			skb_put(skb, length);
>> ++		}
>> ++
>> ++		skb->dev = rx_ring->ess->netdev;
>> ++		skb->protocol = eth_type_trans(skb, rx_ring->ess->netdev);
>> ++		skb_record_rx_queue(skb, rx_ring->idx);
>> ++
>> ++		if (rd->rrd6 & IPQESS_RRD_CSUM_FAIL_MASK)
>> ++			skb_checksum_none_assert(skb);
>> ++		else
>> ++			skb->ip_summed = CHECKSUM_UNNECESSARY;
>> ++
>> ++		if (rd->rrd7 & IPQESS_RRD_CVLAN) {
>> ++			__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), rd-
>>> rrd4);
>> ++		} else if (rd->rrd1 & IPQESS_RRD_SVLAN) {
>> ++			__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021AD),
>> rd->rrd4);
>> ++		}
>> ++		napi_gro_receive(&rx_ring->napi_rx, skb);
>> ++
>> ++		/* TODO: do we need to have these here ? */
>> ++		rx_ring->ess->stats.rx_packets++;
>> ++		rx_ring->ess->stats.rx_bytes += length;
>> ++
>> ++		done++;
>> ++		while (num_desc) {
>> ++			if (ipqess_rx_buf_alloc_napi(rx_ring)) {
>> ++				schedule_work(&rx_ring->refill_work);
>> ++				atomic_add(num_desc, &rx_ring->refill_count);
>> ++				break;
>> ++			}
>> ++			num_desc--;
>> ++		}
>> ++
>> ++	}
>> ++
>> ++	ipqess_w32(rx_ring->ess, IPQESS_REG_RX_SW_CONS_IDX_Q(rx_ring-
>>> idx),
>> ++		 rx_ring->tail);
>> ++
>> ++	return done;
>> ++}
>> ++
>> ++static int ipqess_tx_complete(struct ipqess_tx_ring *tx_ring, int budget)
>> ++{
>> ++	u32 tail;
>> ++	int done = 0;
>> ++	int total = 0, ret;
>> ++
>> ++	tail = ipqess_r32(tx_ring->ess, IPQESS_REG_TPD_IDX_Q(tx_ring->idx));
>> ++	tail >>= IPQESS_TPD_CONS_IDX_SHIFT;
>> ++	tail &= IPQESS_TPD_CONS_IDX_MASK;
>> ++
>> ++	while ((tx_ring->tail != tail) && (done < budget)) {
>> ++		//pr_info("freeing txq:%d tail:%d tailbuf:%p\n", tx_ring->idx,
>> tx_ring->tail, &tx_ring->buf[tx_ring->tail]);
>> ++		ret = ipqess_tx_unmap_and_free(&tx_ring->ess->pdev->dev,
>> ++				       &tx_ring->buf[tx_ring->tail]);
>> ++		tx_ring->tail = IPQESS_NEXT_IDX(tx_ring->tail, tx_ring->count);
>> ++		if (ret) {
>> ++			total += ret;
>> ++			done++;
>> ++		}
>> ++	}
>> ++
>> ++	ipqess_w32(tx_ring->ess,
>> ++		 IPQESS_REG_TX_SW_CONS_IDX_Q(tx_ring->idx),
>> ++		 tx_ring->tail);
>> ++
>> ++	if (netif_tx_queue_stopped(tx_ring->nq)) {
>> ++		printk("S %d\n", tx_ring->idx);
>> ++		netif_tx_wake_queue(tx_ring->nq);
>> ++	}
>> ++
>> ++	netdev_tx_completed_queue(tx_ring->nq, done, total);
>> ++
>> ++	return done;
>> ++}
>> ++
>> ++static int ipqess_tx_napi(struct napi_struct *napi, int budget)
>> ++{
>> ++	struct ipqess_tx_ring *tx_ring = container_of(napi, struct
>> ipqess_tx_ring,
>> ++						    napi_tx);
>> ++	u32 reg_data;
>> ++	u32 shadow_tx_status;
>> ++	int work_done = 0;
>> ++	struct queue *queue = &tx_ring->ess->queue[tx_ring->idx / 4];
>> ++
>> ++	reg_data = ipqess_r32(tx_ring->ess, IPQESS_REG_TX_ISR);
>> ++	queue->tx_status |= reg_data & BIT(tx_ring->idx);
>> ++	shadow_tx_status = queue->tx_status;
>> ++
>> ++	work_done = ipqess_tx_complete(tx_ring, budget);
>> ++
>> ++	ipqess_w32(tx_ring->ess, IPQESS_REG_TX_ISR, shadow_tx_status);
>> ++
>> ++	if (likely(work_done < budget)) {
>> ++		napi_complete(napi);
>> ++		ipqess_w32(tx_ring->ess,
>> IPQESS_REG_TX_INT_MASK_Q(tx_ring->idx), 0x1);
>> ++	}
>> ++
>> ++	return work_done;
>> ++}
>> ++
>> ++static int ipqess_rx_napi(struct napi_struct *napi, int budget)
>> ++{
>> ++	struct ipqess_rx_ring *rx_ring = container_of(napi, struct
>> ipqess_rx_ring,
>> ++						    napi_rx);
>> ++	struct ipqess *ess = rx_ring->ess;
>> ++	int remain_budget = budget;
>> ++	int rx_done;
>> ++	u32 rx_mask = BIT(rx_ring->idx << IPQESS_RX_PER_CPU_MASK_SHIFT);
>> ++	u32 status;
>> ++
>> ++poll_again:
>> ++	ipqess_w32(ess, IPQESS_REG_RX_ISR, rx_mask);
>> ++	rx_done = ipqess_rx_poll(rx_ring, remain_budget);
>> ++
>> ++	if (rx_done == remain_budget)
>> ++		return budget;
>> ++
>> ++	status = ipqess_r32(ess, IPQESS_REG_RX_ISR);
>> ++	if (status & rx_mask) {
>> ++		remain_budget -= rx_done;
>> ++		goto poll_again;
>> ++	}
>> ++
>> ++	napi_complete(napi);
>> ++	ipqess_w32(ess, IPQESS_REG_RX_INT_MASK_Q(rx_ring->idx), 0x1);
>> ++
>> ++	return rx_done + budget - remain_budget;
>> ++}
>> ++
>> ++static irqreturn_t ipqess_interrupt_tx(int irq, void *priv)
>> ++{
>> ++	struct ipqess_tx_ring *tx_ring = (struct ipqess_tx_ring *) priv;
>> ++
>> ++	if (likely(napi_schedule_prep(&tx_ring->napi_tx))) {
>> ++		__napi_schedule(&tx_ring->napi_tx);
>> ++		ipqess_w32(tx_ring->ess,
>> ++			 IPQESS_REG_TX_INT_MASK_Q(tx_ring->idx),
>> ++			 0x0);
>> ++	}
>> ++
>> ++	return IRQ_HANDLED;
>> ++}
>> ++
>> ++static irqreturn_t ipqess_interrupt_rx(int irq, void *priv)
>> ++{
>> ++	struct ipqess_rx_ring *rx_ring = (struct ipqess_rx_ring *) priv;
>> ++
>> ++	if (likely(napi_schedule_prep(&rx_ring->napi_rx))) {
>> ++		__napi_schedule(&rx_ring->napi_rx);
>> ++		ipqess_w32(rx_ring->ess,
>> ++			 IPQESS_REG_RX_INT_MASK_Q(rx_ring->idx),
>> ++			 0x0);
>> ++	}
>> ++
>> ++	return IRQ_HANDLED;
>> ++}
>> ++
>> ++static void ipqess_irq_enable(struct ipqess *ess)
>> ++{
>> ++	int i;
>> ++
>> ++	ipqess_w32(ess, IPQESS_REG_RX_ISR, 0xff);
>> ++	ipqess_w32(ess, IPQESS_REG_TX_ISR, 0xffff);
>> ++	for (i = 0; i < IPQESS_NETDEV_QUEUES; i++) {
>> ++		ipqess_w32(ess, IPQESS_REG_RX_INT_MASK_Q(i), 1);
>> ++		ipqess_w32(ess, IPQESS_REG_TX_INT_MASK_Q(i), 1);
>> ++	}
>> ++}
>> ++
>> ++static void ipqess_irq_disable(struct ipqess *ess)
>> ++{
>> ++	int i;
>> ++
>> ++	for (i = 0; i < IPQESS_NETDEV_QUEUES; i++) {
>> ++		ipqess_w32(ess, IPQESS_REG_RX_INT_MASK_Q(i), 0);
>> ++		ipqess_w32(ess, IPQESS_REG_TX_INT_MASK_Q(i), 0);
>> ++	}
>> ++}
>> ++
>> ++static int __init ipqess_init(struct net_device *netdev)
>> ++{
>> ++	struct ipqess *ess = netdev_priv(netdev);
>> ++	const char *mac_addr;
>> ++
>> ++	mac_addr = of_get_mac_address(ess->of_node);
>> ++	if (mac_addr)
>> ++		ether_addr_copy(netdev->dev_addr, mac_addr);
>> ++	if (!is_valid_ether_addr(netdev->dev_addr)) {
>> ++		random_ether_addr(netdev->dev_addr);
>> ++		dev_err(&netdev->dev, "generated random MAC address
>> %pM\n",
>> ++			netdev->dev_addr);
>> ++		netdev->addr_assign_type = NET_ADDR_RANDOM;
>> ++	}
>> ++
>> ++	return ipqess_phy_connect(netdev);
>> ++}
>> ++
>> ++static void ipqess_uninit(struct net_device *netdev)
>> ++{
>> ++	struct ipqess *ess = netdev_priv(netdev);
>> ++
>> ++	phy_disconnect(netdev->phydev);
>> ++	if (of_phy_is_fixed_link(ess->of_node))
>> ++		of_phy_deregister_fixed_link(ess->of_node);
>> ++}
>> ++
>> ++static int ipqess_open(struct net_device *netdev)
>> ++{
>> ++	struct ipqess *ess = netdev_priv(netdev);
>> ++	int i;
>> ++
>> ++	for (i = 0; i < IPQESS_NETDEV_QUEUES; i++) {
>> ++		napi_enable(&ess->tx_ring[i].napi_tx);
>> ++		napi_enable(&ess->rx_ring[i].napi_rx);
>> ++	}
>> ++	ipqess_irq_enable(ess);
>> ++	phy_start(ess->netdev->phydev);
>> ++	netif_tx_start_all_queues(netdev);
>> ++
>> ++	return 0;
>> ++}
>> ++
>> ++static int ipqess_stop(struct net_device *netdev)
>> ++{
>> ++	struct ipqess *ess = netdev_priv(netdev);
>> ++	int i;
>> ++
>> ++	netif_tx_stop_all_queues(netdev);
>> ++	phy_stop(netdev->phydev);
>> ++	ipqess_irq_disable(ess);
>> ++	for (i = 0; i < IPQESS_NETDEV_QUEUES; i++) {
>> ++		napi_disable(&ess->tx_ring[i].napi_tx);
>> ++		napi_disable(&ess->rx_ring[i].napi_rx);
>> ++	}
>> ++
>> ++	return 0;
>> ++}
>> ++
>> ++static int ipqess_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
>> ++{
>> ++	switch (cmd) {
>> ++	case SIOCGMIIPHY:
>> ++	case SIOCGMIIREG:
>> ++	case SIOCSMIIREG:
>> ++		return phy_mii_ioctl(netdev->phydev, ifr, cmd);
>> ++	default:
>> ++		break;
>> ++	}
>> ++
>> ++	return -EOPNOTSUPP;
>> ++}
>> ++
>> ++
>> ++static inline u16 ipqess_tx_desc_available(struct ipqess_tx_ring *tx_ring)
>> ++{
>> ++	u16 count = 0;
>> ++
>> ++	if (tx_ring->tail <= tx_ring->head)
>> ++		count = IPQESS_TX_RING_SIZE;
>> ++
>> ++	count += tx_ring->tail - tx_ring->head - 1;
>> ++
>> ++	return count;
>> ++}
>> ++
>> ++static inline int ipqess_cal_txd_req(struct sk_buff *skb)
>> ++{
>> ++	int i, nfrags;
>> ++	struct skb_frag_struct *frag;
>> ++
>> ++	nfrags = 1;
>> ++	if (skb_is_gso(skb)) {
>> ++		for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
>> ++			frag = &skb_shinfo(skb)->frags[i];
>> ++			nfrags += DIV_ROUND_UP(frag->size,
>> IPQESS_TX_DMA_BUF_LEN);
>> ++		}
>> ++	} else {
>> ++		nfrags += skb_shinfo(skb)->nr_frags;
>> ++	}
>> ++
>> ++	return nfrags; // DIV_ROUND_UP(nfrags, 2);
>> ++}
>> ++
>> ++static struct ipqess_buf *ipqess_get_tx_buffer(struct ipqess_tx_ring
>> *tx_ring,
>> ++					       struct ipqess_tx_desc *desc)
>> ++{
>> ++	return &tx_ring->buf[desc - (struct ipqess_tx_desc *)tx_ring-
>>> hw_desc];
>> ++}
>> ++
>> ++static struct ipqess_tx_desc *ipqess_tx_desc_next(struct ipqess_tx_ring
>> *tx_ring)
>> ++{
>> ++	struct ipqess_tx_desc *desc;
>> ++
>> ++	desc = (&((struct ipqess_tx_desc *)(tx_ring->hw_desc))[tx_ring-
>>> head]);
>> ++	tx_ring->head = IPQESS_NEXT_IDX(tx_ring->head, tx_ring->count);
>> ++
>> ++	return desc;
>> ++}
>> ++
>> ++static void ipqess_rollback_tx(struct ipqess *eth,
>> ++			    struct ipqess_tx_desc *first_desc, int queue_id)
>> ++{
>> ++	struct ipqess_tx_ring *tx_ring = &eth->tx_ring[queue_id / 4];
>> ++	struct ipqess_buf *buf;
>> ++	struct ipqess_tx_desc *desc = NULL;
>> ++	u16 start_index, index;
>> ++
>> ++	start_index = first_desc - (struct ipqess_tx_desc *)(tx_ring->hw_desc);
>> ++
>> ++	index = start_index;
>> ++	while (index != tx_ring->head) {
>> ++		desc = (&((struct ipqess_tx_desc *)(tx_ring->hw_desc))[index]);
>> ++		buf = &tx_ring->buf[index];
>> ++		ipqess_tx_unmap_and_free(&eth->pdev->dev, buf);
>> ++		memset(desc, 0, sizeof(struct ipqess_tx_desc));
>> ++		if (++index == tx_ring->count)
>> ++			index = 0;
>> ++	}
>> ++	tx_ring->head = start_index;
>> ++}
>> ++
>> ++static int ipqess_tx_map_and_fill(struct ipqess_tx_ring *tx_ring, struct
>> sk_buff *skb)
>> ++{
>> ++	struct ipqess_buf *buf = NULL;
>> ++	struct platform_device *pdev = tx_ring->ess->pdev;
>> ++	struct ipqess_tx_desc *desc = NULL, *first_desc = NULL;
>> ++	u32 word1 = 0, word3 = 0, lso_word1 = 0, svlan_tag = 0;
>> ++	u16 len, lso_len = 0;
>> ++	int i = 0;
>> ++
>> ++	if (skb_is_gso(skb)) {
>> ++		if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) {
>> ++			lso_word1 |= IPQESS_TPD_IPV4_EN;
>> ++			ip_hdr(skb)->check = 0;
>> ++			tcp_hdr(skb)->check =
>> ~csum_tcpudp_magic(ip_hdr(skb)->saddr,
>> ++				ip_hdr(skb)->daddr, 0, IPPROTO_TCP, 0);
>> ++		} else if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) {
>> ++			lso_word1 |= IPQESS_TPD_LSO_V2_EN;
>> ++			ipv6_hdr(skb)->payload_len = 0;
>> ++			tcp_hdr(skb)->check =
>> ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
>> ++				&ipv6_hdr(skb)->daddr, 0, IPPROTO_TCP, 0);
>> ++		}
>> ++
>> ++		lso_word1 |= IPQESS_TPD_LSO_EN |
>> ++			     ((skb_shinfo(skb)->gso_size &
>> IPQESS_TPD_MSS_MASK) << IPQESS_TPD_MSS_SHIFT) |
>> ++			     (skb_transport_offset(skb) <<
>> IPQESS_TPD_HDR_SHIFT);
>> ++	} else if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) {
>> ++			u8 css, cso;
>> ++			cso = skb_checksum_start_offset(skb);
>> ++			css = cso + skb->csum_offset;
>> ++
>> ++			word1 |= (IPQESS_TPD_CUSTOM_CSUM_EN);
>> ++			word1 |= (cso >> 1) << IPQESS_TPD_HDR_SHIFT;
>> ++			word1 |= ((css >> 1) <<
>> IPQESS_TPD_CUSTOM_CSUM_SHIFT);
>> ++	}
>> ++
>> ++	if (skb_vlan_tag_present(skb)) {
>> ++		switch (skb->vlan_proto) {
>> ++		case htons(ETH_P_8021Q):
>> ++			word3 |= BIT(IPQESS_TX_INS_CVLAN);
>> ++			word3 |= skb_vlan_tag_get(skb) <<
>> IPQESS_TX_CVLAN_TAG_SHIFT;
>> ++			break;
>> ++		case htons(ETH_P_8021AD):
>> ++			word1 |= BIT(IPQESS_TX_INS_SVLAN);
>> ++			svlan_tag = skb_vlan_tag_get(skb) <<
>> IPQESS_TX_SVLAN_TAG_SHIFT;
>> ++			break;
>> ++		default:
>> ++			dev_err(&pdev->dev, "no ctag or stag present\n");
>> ++			goto vlan_tag_error;
>> ++		}
>> ++	}
>> ++
>> ++        if (skb->protocol == htons(ETH_P_PPP_SES))
>> ++                word1 |= IPQESS_TPD_PPPOE_EN;
>> ++
>> ++	word3 |= 0x3e << IPQESS_TPD_PORT_BITMAP_SHIFT;
>> ++	len = skb_headlen(skb);
>> ++
>> ++	first_desc = desc = ipqess_tx_desc_next(tx_ring);
>> ++	if (lso_word1 & IPQESS_TPD_LSO_V2_EN) {
>> ++		desc->addr = cpu_to_le16(skb->len);
>> ++		desc->word1 = word1 | lso_word1;
>> ++		desc->svlan_tag = svlan_tag;
>> ++		desc->word3 = word3;
>> ++		desc = ipqess_tx_desc_next(tx_ring);
>> ++	}
>> ++
>> ++	buf = ipqess_get_tx_buffer(tx_ring, desc);
>> ++	if (lso_word1)
>> ++		buf->length = lso_len;
>> ++	else
>> ++		buf->length = len;
>> ++	buf->dma = dma_map_single(&pdev->dev,
>> ++				skb->data, len, DMA_TO_DEVICE);
>> ++	if (dma_mapping_error(&pdev->dev, buf->dma))
>> ++		goto dma_error;
>> ++
>> ++	desc->addr = cpu_to_le32(buf->dma);
>> ++	desc->len  = cpu_to_le16(len);
>> ++
>> ++	buf->flags |= IPQESS_DESC_SINGLE;
>> ++	desc->word1 = word1 | lso_word1;
>> ++	desc->svlan_tag = svlan_tag;
>> ++	desc->word3 = word3;
>> ++
>> ++	while (i < skb_shinfo(skb)->nr_frags) {
>> ++		skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
>> ++		len = skb_frag_size(frag);
>> ++		desc = ipqess_tx_desc_next(tx_ring);
>> ++		buf = ipqess_get_tx_buffer(tx_ring, desc);
>> ++		buf->length = len;
>> ++		buf->flags |= IPQESS_DESC_PAGE;
>> ++		buf->dma = skb_frag_dma_map(&pdev->dev, frag, 0, len,
>> DMA_TO_DEVICE);
>> ++		if (dma_mapping_error(NULL, buf->dma))
>> ++			goto dma_error;
>> ++
>> ++		desc->addr = cpu_to_le32(buf->dma);
>> ++		desc->len  = cpu_to_le16(len);
>> ++		desc->svlan_tag = svlan_tag;
>> ++		desc->word1 = word1 | lso_word1;
>> ++		desc->word3 = word3;
>> ++		i++;
>> ++	}
>> ++	desc->word1 |= 1 << IPQESS_TPD_EOP_SHIFT;
>> ++	buf->skb = skb;
>> ++	buf->flags |= IPQESS_DESC_LAST;
>> ++
>> ++	return 0;
>> ++
>> ++dma_error:
>> ++	ipqess_rollback_tx(tx_ring->ess, first_desc, tx_ring->idx);
>> ++	dev_err(&pdev->dev, "TX DMA map failed\n");
>> ++vlan_tag_error:
>> ++	return -ENOMEM;
>> ++}
>> ++
>> ++static netdev_tx_t ipqess_xmit(struct sk_buff *skb,
>> ++			     struct net_device *netdev)
>> ++{
>> ++	struct ipqess *ess = netdev_priv(netdev);
>> ++	struct ipqess_tx_ring *tx_ring;
>> ++	int tx_num;
>> ++	int ret;
>> ++
>> ++	tx_ring = &ess->tx_ring[skb_get_queue_mapping(skb)];
>> ++	tx_num = ipqess_cal_txd_req(skb);
>> ++	if (ipqess_tx_desc_available(tx_ring) <= tx_num) {
>> ++		printk("s %d %x\n", tx_ring->idx, ipqess_r32(tx_ring->ess,
>> IPQESS_REG_TX_INT_MASK_Q(tx_ring->idx)));
>> ++		netif_tx_stop_queue(tx_ring->nq);
>> ++		ipqess_w32(tx_ring->ess,
>> IPQESS_REG_TX_INT_MASK_Q(tx_ring->idx), 0x1);
>> ++		return NETDEV_TX_BUSY;
>> ++	}
>> ++
>> ++	ret = ipqess_tx_map_and_fill(tx_ring, skb);
>> ++	if (ret) {
>> ++		dev_kfree_skb_any(skb);
>> ++		ess->stats.tx_errors++;
>> ++		goto err_out;
>> ++	}
>> ++
>> ++	ess->stats.tx_packets++;
>> ++	ess->stats.tx_bytes += skb->len;
>> ++	netdev_tx_sent_queue(tx_ring->nq, skb->len);
>> ++
>> ++	if (!skb->xmit_more || netif_xmit_stopped(tx_ring->nq))
>> ++		ipqess_m32(ess,
>> ++			 IPQESS_TPD_PROD_IDX_BITS,
>> ++			 tx_ring->head,
>> ++			 IPQESS_REG_TPD_IDX_Q(tx_ring->idx));
>> ++
>> ++err_out:
>> ++	return NETDEV_TX_OK;
>> ++}
>> ++
>> ++static int ipqess_set_mac_address(struct net_device *netdev, void *p)
>> ++{
>> ++	int ret = eth_mac_addr(netdev, p);
>> ++	struct ipqess *ess = netdev_priv(netdev);
>> ++	const char *macaddr = netdev->dev_addr;
>> ++
>> ++	if (ret)
>> ++		return ret;
>> ++
>> ++//	spin_lock_bh(&mac->hw->page_lock);
>> ++	ipqess_w32(ess, IPQESS_REG_MAC_CTRL1,
>> ++		 (macaddr[0] << 8) | macaddr[1]);
>> ++	ipqess_w32(ess, IPQESS_REG_MAC_CTRL0,
>> ++		 (macaddr[2] << 24) | (macaddr[3] << 16) |
>> ++		 (macaddr[4] << 8) | macaddr[5]);
>> ++//	spin_unlock_bh(&mac->hw->page_lock);
>> ++
>> ++	return 0;
>> ++}
>> ++
>> ++static const struct net_device_ops ipqess_axi_netdev_ops = {
>> ++	.ndo_init		= ipqess_init,
>> ++	.ndo_uninit		= ipqess_uninit,
>> ++	.ndo_open		= ipqess_open,
>> ++	.ndo_stop		= ipqess_stop,
>> ++	.ndo_do_ioctl		= ipqess_do_ioctl,
>> ++	.ndo_start_xmit		= ipqess_xmit,
>> ++	.ndo_get_stats		= ipqess_get_stats,
>> ++	.ndo_set_mac_address	= ipqess_set_mac_address,
>> ++};
>> ++
>> ++static void ipqess_reset(struct ipqess *ess)
>> ++{
>> ++	int i;
>> ++
>> ++	/* disable all IRQs */
>> ++	for (i = 0; i < IPQESS_NETDEV_QUEUES; i++) {
>> ++		ipqess_w32(ess, IPQESS_REG_RX_INT_MASK_Q(i), 0x0);
>> ++		ipqess_w32(ess, IPQESS_REG_TX_INT_MASK_Q(i), 0x0);
>> ++	}
>> ++
>> ++	ipqess_w32(ess, IPQESS_REG_MISC_IMR, 0);
>> ++	ipqess_w32(ess, IPQESS_REG_WOL_IMR, 0);
>> ++
>> ++	/* clear the IRQ status registers */
>> ++	ipqess_w32(ess, IPQESS_REG_RX_ISR, 0xff);
>> ++	ipqess_w32(ess, IPQESS_REG_TX_ISR, 0xffff);
>> ++	ipqess_w32(ess, IPQESS_REG_MISC_ISR, 0x1fff);
>> ++	ipqess_w32(ess, IPQESS_REG_WOL_ISR, 0x1);
>> ++	ipqess_w32(ess, IPQESS_REG_WOL_CTRL, 0);
>> ++
>> ++	/* disable RX and TX queues */
>> ++	ipqess_m32(ess, IPQESS_RXQ_CTRL_EN, 0, IPQESS_REG_RXQ_CTRL);
>> ++	ipqess_m32(ess, IPQESS_TXQ_CTRL_TXQ_EN, 0,
>> IPQESS_REG_TXQ_CTRL);
>> ++}
>> ++
>> ++static int ipqess_hw_init(struct ipqess *ess)
>> ++{
>> ++	int i, err;
>> ++
>> ++	ipqess_reset(ess);
>> ++
>> ++	ipqess_m32(ess, BIT(IPQESS_INTR_SW_IDX_W_TYP_SHIFT),
>> ++		 IPQESS_INTR_SW_IDX_W_TYPE <<
>> IPQESS_INTR_SW_IDX_W_TYP_SHIFT,
>> ++		 IPQESS_REG_INTR_CTRL);
>> ++
>> ++	ipqess_w32(ess, IPQESS_REG_RX_ISR, 0xff);
>> ++	ipqess_w32(ess, IPQESS_REG_TX_ISR, 0xffff);
>> ++
>> ++	/* enable IRQ delay slot */
>> ++	ipqess_w32(ess, IPQESS_REG_IRQ_MODRT_TIMER_INIT,
>> ++		 (IPQESS_TX_IMT << IPQESS_IRQ_MODRT_TX_TIMER_SHIFT) |
>> ++		 (IPQESS_RX_IMT << IPQESS_IRQ_MODRT_RX_TIMER_SHIFT));
>> ++
>> ++	/* Configure the TX Queue bursting */
>> ++	ipqess_w32(ess, IPQESS_REG_TXQ_CTRL,
>> ++		 (IPQESS_TPD_BURST <<
>> IPQESS_TXQ_NUM_TPD_BURST_SHIFT) |
>> ++		 (IPQESS_TXF_BURST << IPQESS_TXQ_TXF_BURST_NUM_SHIFT)
>> |
>> ++		 IPQESS_TXQ_CTRL_TPD_BURST_EN);
>> ++
>> ++	/* Set RSS type */
>> ++	ipqess_w32(ess, IPQESS_REG_RSS_TYPE,
>> ++		 IPQESS_RSS_TYPE_IPV4TCP | IPQESS_RSS_TYPE_IPV6_TCP |
>> ++		 IPQESS_RSS_TYPE_IPV4_UDP | IPQESS_RSS_TYPE_IPV6UDP |
>> ++		 IPQESS_RSS_TYPE_IPV4 | IPQESS_RSS_TYPE_IPV6);
>> ++
>> ++	/* Set RFD ring burst and threshold */
>> ++	ipqess_w32(ess, IPQESS_REG_RX_DESC1,
>> ++		(IPQESS_RFD_BURST << IPQESS_RXQ_RFD_BURST_NUM_SHIFT)
>> |
>> ++		(IPQESS_RFD_THR << IPQESS_RXQ_RFD_PF_THRESH_SHIFT) |
>> ++		(IPQESS_RFD_LTHR <<
>> IPQESS_RXQ_RFD_LOW_THRESH_SHIFT));
>> ++
>> ++	/* Set Rx FIFO
>> ++	 * - threshold to start to DMA data to host
>> ++	 * - remove vlan bit
>> ++	 */
>> ++	ipqess_w32(ess, IPQESS_REG_RXQ_CTRL,
>> ++		 IPQESS_FIFO_THRESH_128_BYTE |
>> IPQESS_RXQ_CTRL_RMV_VLAN);
>> ++
>> ++	err = ipqess_rx_ring_alloc(ess);
>> ++	if (err)
>> ++		return err;
>> ++
>> ++	err = ipqess_tx_ring_alloc(ess);
>> ++	if (err)
>> ++		return err;
>> ++
>> ++	/* Disable TX FIFO low watermark and high watermark */
>> ++	ipqess_w32(ess, IPQESS_REG_TXF_WATER_MARK, 0);
>> ++
>> ++	/* Configure RSS indirection table.
>> ++	 * 128 hash will be configured in the following
>> ++	 * pattern: hash{0,1,2,3} = {Q0,Q2,Q4,Q6} respectively
>> ++	 * and so on
>> ++	 */
>> ++	for (i = 0; i < IPQESS_NUM_IDT; i++)
>> ++		ipqess_w32(ess, IPQESS_REG_RSS_IDT(i),
>> IPQESS_RSS_IDT_VALUE);
>> ++
>> ++	/* Configure load balance mapping table.
>> ++	 * 4 table entry will be configured according to the
>> ++	 * following pattern: load_balance{0,1,2,3} = {Q0,Q1,Q3,Q4}
>> ++	 * respectively.
>> ++	 */
>> ++	ipqess_w32(ess, IPQESS_REG_LB_RING, IPQESS_LB_REG_VALUE);
>> ++
>> ++	/* Configure Virtual queue for Tx rings
>> ++	 * User can also change this value runtime through
>> ++	 * a sysctl
>> ++	 */
>> ++	ipqess_w32(ess, IPQESS_REG_VQ_CTRL0, IPQESS_VQ_REG_VALUE);
>> ++	ipqess_w32(ess, IPQESS_REG_VQ_CTRL1, IPQESS_VQ_REG_VALUE);
>> ++
>> ++	/* Configure Max AXI Burst write size to 128 bytes*/
>> ++	ipqess_w32(ess, IPQESS_REG_AXIW_CTRL_MAXWRSIZE,
>> ++		 IPQESS_AXIW_MAXWRSIZE_VALUE);
>> ++
>> ++	/* Enable All 16 tx and 8 rx irq mask */
>> ++	ipqess_m32(ess, 0, IPQESS_TXQ_CTRL_TXQ_EN,
>> IPQESS_REG_TXQ_CTRL);
>> ++	ipqess_m32(ess, 0, IPQESS_RXQ_CTRL_EN, IPQESS_REG_RXQ_CTRL);
>> ++
>> ++	return 0;
>> ++}
>> ++
>> ++static void ipqess_cleanup(struct ipqess *ess)
>> ++{
>> ++	ipqess_reset(ess);
>> ++	unregister_netdev(ess->netdev);
>> ++
>> ++	ipqess_tx_ring_free(ess);
>> ++	ipqess_rx_ring_free(ess);
>> ++
>> ++	free_netdev(ess->netdev);
>> ++}
>> ++
>> ++static int ipqess_axi_probe(struct platform_device *pdev)
>> ++{
>> ++	struct ipqess *ess;
>> ++	struct net_device *netdev;
>> ++	struct resource *res;
>> ++	int i, err = 0;
>> ++
>> ++	netdev = alloc_etherdev_mqs(sizeof(struct ipqess),
>> ++				    IPQESS_NETDEV_QUEUES,
>> ++				    IPQESS_NETDEV_QUEUES);
>> ++	if (!netdev)
>> ++		return -ENODEV;
>> ++
>> ++	ess = netdev_priv(netdev);
>> ++	memset(ess, 0, sizeof(struct ipqess));
>> ++	ess->netdev = netdev;
>> ++	ess->pdev = pdev;
>> ++	ess->of_node = pdev->dev.of_node;
>> ++	spin_lock_init(&ess->stats_lock);
>> ++	SET_NETDEV_DEV(netdev, &pdev->dev);
>> ++	platform_set_drvdata(pdev, netdev);
>> ++
>> ++	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> ++	ess->hw_addr = devm_ioremap_resource(&pdev->dev, res);
>> ++	if (IS_ERR(ess->hw_addr)) {
>> ++		err = PTR_ERR(ess->hw_addr);
>> ++		goto err_out;
>> ++	}
>> ++
>> ++	for (i = 0; i < IPQESS_MAX_TX_QUEUE; i++)
>> ++		ess->tx_irq[i] = platform_get_irq(pdev, i);
>> ++	for (i = 0; i < IPQESS_MAX_RX_QUEUE; i++)
>> ++		ess->rx_irq[i] = platform_get_irq(pdev, i +
>> IPQESS_MAX_TX_QUEUE);
>> ++
>> ++	netdev->netdev_ops = &ipqess_axi_netdev_ops;
>> ++	netdev->features = NETIF_F_HW_CSUM | NETIF_F_RXCSUM |
>> ++			   NETIF_F_HW_VLAN_CTAG_RX |
>> ++			   NETIF_F_HW_VLAN_CTAG_TX |
>> ++			   NETIF_F_TSO | NETIF_F_TSO6 |
>> ++			   NETIF_F_GRO | NETIF_F_SG;
>> ++	netdev->hw_features = NETIF_F_HW_CSUM | NETIF_F_RXCSUM |
>> ++			      NETIF_F_HW_VLAN_CTAG_RX |
>> ++			      NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_SG |
>> ++			      NETIF_F_TSO | NETIF_F_TSO6 |
>> ++			      NETIF_F_GRO;
>> ++	netdev->vlan_features = NETIF_F_HW_CSUM | NETIF_F_SG |
>> ++				NETIF_F_TSO | NETIF_F_TSO6 |
>> ++				NETIF_F_GRO;
>> ++	netdev->wanted_features = NETIF_F_HW_CSUM | NETIF_F_SG |
>> ++				  NETIF_F_TSO | NETIF_F_TSO6 |
>> ++				  NETIF_F_GRO;
>> ++	netdev->watchdog_timeo = 5 * HZ;
>> ++	netdev->base_addr = (u32) ess->hw_addr;
>> ++	netdev->max_mtu = 9000;
>> ++	netdev->gso_max_segs = IPQESS_TX_RING_SIZE / 2;
>> ++
>> ++	ipqess_set_ethtool_ops(netdev);
>> ++
>> ++	err = register_netdev(netdev);
>> ++	if (err)
>> ++		goto err_out;
>> ++
>> ++	err = ipqess_hw_init(ess);
>> ++	if (err)
>> ++		goto err_out;
>> ++
>> ++	for (i = 0; i < IPQESS_NETDEV_QUEUES; i++) {
>> ++		netif_napi_add(netdev,
>> ++			       &ess->tx_ring[i].napi_tx,
>> ++			       ipqess_tx_napi, 64);
>> ++		netif_napi_add(netdev,
>> ++			       &ess->rx_ring[i].napi_rx,
>> ++			       ipqess_rx_napi, 64);
>> ++
>> ++		ess->queue[i].ess = ess;
>> ++		ess->queue[i].idx = i;
>> ++		err = devm_request_irq(&ess->netdev->dev,
>> ++			ess->tx_irq[i << IPQESS_TX_CPU_START_SHIFT],
>> ++			ipqess_interrupt_tx, 0, "ipqess TX", &ess->tx_ring[i]);
>> ++		if (err)
>> ++			goto err_out;
>> ++
>> ++		err = devm_request_irq(&ess->netdev->dev,
>> ++			ess->rx_irq[i << IPQESS_RX_CPU_START_SHIFT],
>> ++			ipqess_interrupt_rx, 0, "ipqess RX", &ess->rx_ring[i]);
>> ++		if (err)
>> ++			goto err_out;
>> ++	}
>> ++
>> ++
>> ++	return 0;
>> ++
>> ++err_out:
>> ++	ipqess_cleanup(ess);
>> ++	return err;
>> ++}
>> ++
>> ++static int ipqess_axi_remove(struct platform_device *pdev)
>> ++{
>> ++	const struct net_device *netdev = platform_get_drvdata(pdev);
>> ++	struct ipqess *ess = netdev_priv(netdev);
>> ++
>> ++	ipqess_cleanup(ess);
>> ++
>> ++	return 0;
>> ++}
>> ++
>> ++static const struct of_device_id ipqess_of_mtable[] = {
>> ++	{.compatible = "qcom,ess-edma" },
>> ++	{}
>> ++};
>> ++MODULE_DEVICE_TABLE(of, ipqess_of_mtable);
>> ++
>> ++static struct platform_driver ipqess_axi_driver = {
>> ++	.driver = {
>> ++		.name    = "ess-edma",
>> ++		.of_match_table = ipqess_of_mtable,
>> ++	},
>> ++	.probe    = ipqess_axi_probe,
>> ++	.remove   = ipqess_axi_remove,
>> ++};
>> ++
>> ++module_platform_driver(ipqess_axi_driver);
>> ++
>> ++MODULE_AUTHOR("Qualcomm Atheros Inc");
>> ++MODULE_AUTHOR("John Crispin <john at phrozen.org>");
>> ++MODULE_LICENSE("GPL");
>> +diff --git a/target/linux/ipq40xx/files-
>> 4.14/drivers/net/ethernet/qualcomm/ipqess.h b/target/linux/ipq40xx/files-
>> 4.14/drivers/net/ethernet/qualcomm/ipqess.h
>> +new file mode 100644
>> +index 0000000000..a988bbca07
>> +--- /dev/null
>> ++++ b/target/linux/ipq40xx/files-
>> 4.14/drivers/net/ethernet/qualcomm/ipqess.h
>> +@@ -0,0 +1,568 @@
>> ++/*
>> ++ * Copyright (c) 2014 - 2016, The Linux Foundation. All rights reserved.
>> ++ *
>> ++ * Permission to use, copy, modify, and/or distribute this software for
>> ++ * any purpose with or without fee is hereby granted, provided that the
>> ++ * above copyright notice and this permission notice appear in all copies.
>> ++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
>> WARRANTIES
>> ++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
>> OF
>> ++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE
>> LIABLE FOR
>> ++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
>> DAMAGES
>> ++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
>> WHETHER IN AN
>> ++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
>> ARISING OUT
>> ++ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
>> SOFTWARE.
>> ++ */
>> ++
>> ++#ifndef _IPQESS_H_
>> ++#define _IPQESS_H_
>> ++
>> ++#define IPQESS_CPU_CORES_SUPPORTED 4
>> ++#define IPQESS_MAX_PORTID_SUPPORTED 5
>> ++#define IPQESS_MAX_VLAN_SUPPORTED
>> IPQESS_MAX_PORTID_SUPPORTED
>> ++#define IPQESS_MAX_PORTID_BITMAP_INDEX
>> (IPQESS_MAX_PORTID_SUPPORTED + 1)
>> ++#define IPQESS_MAX_PORTID_BITMAP_SUPPORTED 0x1f	/* 0001_1111 =
>> 0x1f */
>> ++#define IPQESS_MAX_NETDEV_PER_QUEUE 4 /* 3 Netdev per queue, 1
>> space for indexing */
>> ++
>> ++#define IPQESS_NETDEV_QUEUES	4
>> ++
>> ++#define IPQESS_TPD_EOP_SHIFT 31
>> ++
>> ++#define IPQESS_PORT_ID_SHIFT 12
>> ++#define IPQESS_PORT_ID_MASK 0x7
>> ++
>> ++/* tpd word 3 bit 18-28 */
>> ++#define IPQESS_TPD_PORT_BITMAP_SHIFT 18
>> ++
>> ++#define IPQESS_TPD_FROM_CPU_SHIFT 25
>> ++
>> ++#define IPQESS_RX_RING_SIZE 128
>> ++#define IPQESS_RX_HEAD_BUFF_SIZE 1540
>> ++#define IPQESS_TX_RING_SIZE 128
>> ++#define IPQESS_MAX_RX_QUEUE 8
>> ++#define IPQESS_MAX_TX_QUEUE 16
>> ++
>> ++
>> ++/* Configurations */
>> ++#define IPQESS_INTR_CLEAR_TYPE 0
>> ++#define IPQESS_INTR_SW_IDX_W_TYPE 0
>> ++#define IPQESS_FIFO_THRESH_TYPE 0
>> ++#define IPQESS_RSS_TYPE 0
>> ++#define IPQESS_RX_IMT 0x0020
>> ++#define IPQESS_TX_IMT 0x0050
>> ++#define IPQESS_TPD_BURST 5
>> ++#define IPQESS_TXF_BURST 0x100
>> ++#define IPQESS_RFD_BURST 8
>> ++#define IPQESS_RFD_THR 16
>> ++#define IPQESS_RFD_LTHR 0
>> ++
>> ++/* RX/TX per CPU based mask/shift */
>> ++#define IPQESS_TX_PER_CPU_MASK 0xF
>> ++#define IPQESS_RX_PER_CPU_MASK 0x3
>> ++#define IPQESS_TX_PER_CPU_MASK_SHIFT 0x2
>> ++#define IPQESS_RX_PER_CPU_MASK_SHIFT 0x1
>> ++#define IPQESS_TX_CPU_START_SHIFT 0x2
>> ++#define IPQESS_RX_CPU_START_SHIFT 0x1
>> ++
>> ++/* Flags used in transmit direction */
>> ++#define IPQESS_HW_CHECKSUM 0x00000001
>> ++#define IPQESS_VLAN_TX_TAG_INSERT_FLAG 0x00000002
>> ++#define IPQESS_VLAN_TX_TAG_INSERT_DEFAULT_FLAG 0x00000004
>> ++
>> ++#define IPQESS_DESC_LAST 0x1
>> ++#define IPQESS_DESC_SINGLE 0x2
>> ++#define IPQESS_DESC_PAGE 0x4
>> ++#define IPQESS_DESC_PAGELIST 0x8
>> ++#define IPQESS_DESC_SKB_NONE 0x10
>> ++#define IPQESS_DESC_SKB_REUSE 0x20
>> ++
>> ++
>> ++#define IPQESS_MAX_SKB_FRAGS (MAX_SKB_FRAGS + 1)
>> ++
>> ++/* Ethtool specific list of IPQESS supported features */
>> ++#define IPQESS_SUPPORTED_FEATURES (SUPPORTED_10baseT_Half \
>> ++					| SUPPORTED_10baseT_Full \
>> ++					| SUPPORTED_100baseT_Half \
>> ++					| SUPPORTED_100baseT_Full \
>> ++					| SUPPORTED_1000baseT_Full)
>> ++
>> ++/* Receive side Atheros Header */
>> ++#define IPQESS_RX_ATH_HDR_VERSION 0x2
>> ++#define IPQESS_RX_ATH_HDR_VERSION_SHIFT 14
>> ++#define IPQESS_RX_ATH_HDR_PRIORITY_SHIFT 11
>> ++#define IPQESS_RX_ATH_PORT_TYPE_SHIFT 6
>> ++#define IPQESS_RX_ATH_HDR_RSTP_PORT_TYPE 0x4
>> ++
>> ++/* Transmit side Atheros Header */
>> ++#define IPQESS_TX_ATH_HDR_PORT_BITMAP_MASK 0x7F
>> ++#define IPQESS_TX_ATH_HDR_FROM_CPU_MASK 0x80
>> ++#define IPQESS_TX_ATH_HDR_FROM_CPU_SHIFT 7
>> ++
>> ++#define IPQESS_TXQ_START_CORE0 8
>> ++#define IPQESS_TXQ_START_CORE1 12
>> ++#define IPQESS_TXQ_START_CORE2 0
>> ++#define IPQESS_TXQ_START_CORE3 4
>> ++
>> ++#define IPQESS_TXQ_IRQ_MASK_CORE0 0x0F00
>> ++#define IPQESS_TXQ_IRQ_MASK_CORE1 0xF000
>> ++#define IPQESS_TXQ_IRQ_MASK_CORE2 0x000F
>> ++#define IPQESS_TXQ_IRQ_MASK_CORE3 0x00F0
>> ++
>> ++#define IPQESS_ETH_HDR_LEN 12
>> ++#define IPQESS_ETH_TYPE_MASK 0xFFFF
>> ++
>> ++#define IPQESS_RX_BUFFER_WRITE 16
>> ++#define IPQESS_RFD_AVAIL_THR 80
>> ++
>> ++#define IPQESS_GMAC_NO_MDIO_PHY	PHY_MAX_ADDR
>> ++
>> ++extern int ssdk_rfs_ipct_rule_set(__be32 ip_src, __be32 ip_dst,
>> ++				  __be16 sport, __be16 dport,
>> ++				  uint8_t proto, u16 loadbalance, bool action);
>> ++struct ipqesstool_statistics {
>> ++	u32 tx_q0_pkt;
>> ++	u32 tx_q1_pkt;
>> ++	u32 tx_q2_pkt;
>> ++	u32 tx_q3_pkt;
>> ++	u32 tx_q4_pkt;
>> ++	u32 tx_q5_pkt;
>> ++	u32 tx_q6_pkt;
>> ++	u32 tx_q7_pkt;
>> ++	u32 tx_q8_pkt;
>> ++	u32 tx_q9_pkt;
>> ++	u32 tx_q10_pkt;
>> ++	u32 tx_q11_pkt;
>> ++	u32 tx_q12_pkt;
>> ++	u32 tx_q13_pkt;
>> ++	u32 tx_q14_pkt;
>> ++	u32 tx_q15_pkt;
>> ++	u32 tx_q0_byte;
>> ++	u32 tx_q1_byte;
>> ++	u32 tx_q2_byte;
>> ++	u32 tx_q3_byte;
>> ++	u32 tx_q4_byte;
>> ++	u32 tx_q5_byte;
>> ++	u32 tx_q6_byte;
>> ++	u32 tx_q7_byte;
>> ++	u32 tx_q8_byte;
>> ++	u32 tx_q9_byte;
>> ++	u32 tx_q10_byte;
>> ++	u32 tx_q11_byte;
>> ++	u32 tx_q12_byte;
>> ++	u32 tx_q13_byte;
>> ++	u32 tx_q14_byte;
>> ++	u32 tx_q15_byte;
>> ++	u32 rx_q0_pkt;
>> ++	u32 rx_q1_pkt;
>> ++	u32 rx_q2_pkt;
>> ++	u32 rx_q3_pkt;
>> ++	u32 rx_q4_pkt;
>> ++	u32 rx_q5_pkt;
>> ++	u32 rx_q6_pkt;
>> ++	u32 rx_q7_pkt;
>> ++	u32 rx_q0_byte;
>> ++	u32 rx_q1_byte;
>> ++	u32 rx_q2_byte;
>> ++	u32 rx_q3_byte;
>> ++	u32 rx_q4_byte;
>> ++	u32 rx_q5_byte;
>> ++	u32 rx_q6_byte;
>> ++	u32 rx_q7_byte;
>> ++	u32 tx_desc_error;
>> ++};
>> ++
>> ++struct ipqess_tx_desc {
>> ++	__le16  len;
>> ++	__le16  svlan_tag;
>> ++	__le32  word1;
>> ++	__le32  addr;
>> ++	__le32  word3;
>> ++};
>> ++
>> ++struct ipqess_rx_desc {
>> ++	u16 rrd0;
>> ++	u16 rrd1;
>> ++	u16 rrd2;
>> ++	u16 rrd3;
>> ++	u16 rrd4;
>> ++	u16 rrd5;
>> ++	u16 rrd6;
>> ++	u16 rrd7;
>> ++};
>> ++
>> ++struct ipqess_buf {
>> ++	struct sk_buff *skb;
>> ++	dma_addr_t dma;
>> ++	u16 length;
>> ++	u32 flags;
>> ++};
>> ++
>> ++struct queue {
>> ++	u32 idx;
>> ++	u32 tx_mask;
>> ++	u32 tx_status;
>> ++	u32 tx_start;
>> ++	struct ipqess *ess;
>> ++};
>> ++
>> ++struct ipqess_tx_ring {
>> ++	u32 idx;
>> ++	struct ipqess *ess;
>> ++	struct napi_struct napi_tx;
>> ++	struct netdev_queue *nq;
>> ++	void *hw_desc;
>> ++	struct ipqess_buf *buf;
>> ++	int netdev_bmp;
>> ++	u16 count;
>> ++	dma_addr_t dma;
>> ++	u16 head;
>> ++	u16 tail;
>> ++};
>> ++
>> ++struct ipqess_rx_ring {
>> ++	u32 idx;
>> ++	struct ipqess *ess;
>> ++	struct napi_struct napi_rx;
>> ++	void **hw_desc;
>> ++	struct ipqess_buf *buf;
>> ++	struct work_struct refill_work;
>> ++	atomic_t refill_count;
>> ++	dma_addr_t dma;
>> ++	u16 head;
>> ++	u16 tail;
>> ++};
>> ++
>> ++struct ipqess {
>> ++	struct net_device *netdev;
>> ++	void __iomem *hw_addr;
>> ++
>> ++	struct device_node *of_node;
>> ++
>> ++	struct ipqess_tx_ring tx_ring[IPQESS_NETDEV_QUEUES];
>> ++	struct ipqess_rx_ring rx_ring[IPQESS_NETDEV_QUEUES];
>> ++	struct platform_device *pdev;
>> ++	struct ipqesstool_statistics ipqessstats;
>> ++	u32 tx_irq[16];
>> ++	u32 rx_irq[8];
>> ++	u32 from_cpu;
>> ++	struct queue queue[CONFIG_NR_CPUS];
>> ++	spinlock_t stats_lock;
>> ++
>> ++	struct net_device_stats stats;
>> ++	u32 flags;
>> ++	unsigned long state_flags;
>> ++};
>> ++
>> ++void ipqess_set_ethtool_ops(struct net_device *netdev);
>> ++
>> ++/* register definition */
>> ++#define IPQESS_REG_MAS_CTRL 0x0
>> ++#define IPQESS_REG_TIMEOUT_CTRL 0x004
>> ++#define IPQESS_REG_DBG0 0x008
>> ++#define IPQESS_REG_DBG1 0x00C
>> ++#define IPQESS_REG_SW_CTRL0 0x100
>> ++#define IPQESS_REG_SW_CTRL1 0x104
>> ++
>> ++/* Interrupt Status Register */
>> ++#define IPQESS_REG_RX_ISR 0x200
>> ++#define IPQESS_REG_TX_ISR 0x208
>> ++#define IPQESS_REG_MISC_ISR 0x210
>> ++#define IPQESS_REG_WOL_ISR 0x218
>> ++
>> ++#define IPQESS_MISC_ISR_RX_URG_Q(x) (1 << x)
>> ++
>> ++#define IPQESS_MISC_ISR_AXIR_TIMEOUT 0x00000100
>> ++#define IPQESS_MISC_ISR_AXIR_ERR 0x00000200
>> ++#define IPQESS_MISC_ISR_TXF_DEAD 0x00000400
>> ++#define IPQESS_MISC_ISR_AXIW_ERR 0x00000800
>> ++#define IPQESS_MISC_ISR_AXIW_TIMEOUT 0x00001000
>> ++
>> ++#define IPQESS_WOL_ISR 0x00000001
>> ++
>> ++/* Interrupt Mask Register */
>> ++#define IPQESS_REG_MISC_IMR 0x214
>> ++#define IPQESS_REG_WOL_IMR 0x218
>> ++
>> ++#define IPQESS_RX_IMR_NORMAL_MASK 0x1
>> ++#define IPQESS_TX_IMR_NORMAL_MASK 0x1
>> ++#define IPQESS_MISC_IMR_NORMAL_MASK 0x80001FFF
>> ++#define IPQESS_WOL_IMR_NORMAL_MASK 0x1
>> ++
>> ++/* Edma receive consumer index */
>> ++#define IPQESS_REG_RX_SW_CONS_IDX_Q(x) (0x220 + ((x) << 3)) /* x is the
>> queue id */
>> ++/* Edma transmit consumer index */
>> ++#define IPQESS_REG_TX_SW_CONS_IDX_Q(x) (0x240 + ((x) << 2)) /* x is the
>> queue id */
>> ++
>> ++/* IRQ Moderator Initial Timer Register */
>> ++#define IPQESS_REG_IRQ_MODRT_TIMER_INIT 0x280
>> ++#define IPQESS_IRQ_MODRT_TIMER_MASK 0xFFFF
>> ++#define IPQESS_IRQ_MODRT_RX_TIMER_SHIFT 0
>> ++#define IPQESS_IRQ_MODRT_TX_TIMER_SHIFT 16
>> ++
>> ++/* Interrupt Control Register */
>> ++#define IPQESS_REG_INTR_CTRL 0x284
>> ++#define IPQESS_INTR_CLR_TYP_SHIFT 0
>> ++#define IPQESS_INTR_SW_IDX_W_TYP_SHIFT 1
>> ++#define IPQESS_INTR_CLEAR_TYPE_W1 0
>> ++#define IPQESS_INTR_CLEAR_TYPE_R 1
>> ++
>> ++/* RX Interrupt Mask Register */
>> ++#define IPQESS_REG_RX_INT_MASK_Q(x) (0x300 + ((x) << 3)) /* x = queue id
>> */
>> ++
>> ++/* TX Interrupt mask register */
>> ++#define IPQESS_REG_TX_INT_MASK_Q(x) (0x340 + ((x) << 2)) /* x = queue id
>> */
>> ++
>> ++/* Load Ptr Register
>> ++ * Software sets this bit after the initialization of the head and tail
>> ++ */
>> ++#define IPQESS_REG_TX_SRAM_PART 0x400
>> ++#define IPQESS_LOAD_PTR_SHIFT 16
>> ++
>> ++/* TXQ Control Register */
>> ++#define IPQESS_REG_TXQ_CTRL 0x404
>> ++#define IPQESS_TXQ_CTRL_IP_OPTION_EN 0x10
>> ++#define IPQESS_TXQ_CTRL_TXQ_EN 0x20
>> ++#define IPQESS_TXQ_CTRL_ENH_MODE 0x40
>> ++#define IPQESS_TXQ_CTRL_LS_8023_EN 0x80
>> ++#define IPQESS_TXQ_CTRL_TPD_BURST_EN 0x100
>> ++#define IPQESS_TXQ_CTRL_LSO_BREAK_EN 0x200
>> ++#define IPQESS_TXQ_NUM_TPD_BURST_MASK 0xF
>> ++#define IPQESS_TXQ_TXF_BURST_NUM_MASK 0xFFFF
>> ++#define IPQESS_TXQ_NUM_TPD_BURST_SHIFT 0
>> ++#define IPQESS_TXQ_TXF_BURST_NUM_SHIFT 16
>> ++
>> ++#define	IPQESS_REG_TXF_WATER_MARK 0x408 /* In 8-bytes */
>> ++#define IPQESS_TXF_WATER_MARK_MASK 0x0FFF
>> ++#define IPQESS_TXF_LOW_WATER_MARK_SHIFT 0
>> ++#define IPQESS_TXF_HIGH_WATER_MARK_SHIFT 16
>> ++#define IPQESS_TXQ_CTRL_BURST_MODE_EN 0x80000000
>> ++
>> ++/* WRR Control Register */
>> ++#define IPQESS_REG_WRR_CTRL_Q0_Q3 0x40c
>> ++#define IPQESS_REG_WRR_CTRL_Q4_Q7 0x410
>> ++#define IPQESS_REG_WRR_CTRL_Q8_Q11 0x414
>> ++#define IPQESS_REG_WRR_CTRL_Q12_Q15 0x418
>> ++
>> ++/* Weight round robin(WRR), it takes queue as input, and computes
>> ++ * starting bits where we need to write the weight for a particular
>> ++ * queue
>> ++ */
>> ++#define IPQESS_WRR_SHIFT(x) (((x) * 5) % 20)
>> ++
>> ++/* Tx Descriptor Control Register */
>> ++#define IPQESS_REG_TPD_RING_SIZE 0x41C
>> ++#define IPQESS_TPD_RING_SIZE_SHIFT 0
>> ++#define IPQESS_TPD_RING_SIZE_MASK 0xFFFF
>> ++
>> ++/* Transmit descriptor base address */
>> ++#define IPQESS_REG_TPD_BASE_ADDR_Q(x) (0x420 + ((x) << 2)) /* x =
>> queue id */
>> ++
>> ++/* TPD Index Register */
>> ++#define IPQESS_REG_TPD_IDX_Q(x) (0x460 + ((x) << 2)) /* x = queue id */
>> ++
>> ++#define IPQESS_TPD_PROD_IDX_BITS 0x0000FFFF
>> ++#define IPQESS_TPD_CONS_IDX_BITS 0xFFFF0000
>> ++#define IPQESS_TPD_PROD_IDX_MASK 0xFFFF
>> ++#define IPQESS_TPD_CONS_IDX_MASK 0xFFFF
>> ++#define IPQESS_TPD_PROD_IDX_SHIFT 0
>> ++#define IPQESS_TPD_CONS_IDX_SHIFT 16
>> ++
>> ++/* TX Virtual Queue Mapping Control Register */
>> ++#define IPQESS_REG_VQ_CTRL0 0x4A0
>> ++#define IPQESS_REG_VQ_CTRL1 0x4A4
>> ++
>> ++/* Virtual QID shift, it takes queue as input, and computes
>> ++ * Virtual QID position in virtual qid control register
>> ++ */
>> ++#define IPQESS_VQ_ID_SHIFT(i) (((i) * 3) % 24)
>> ++
>> ++/* Virtual Queue Default Value */
>> ++#define IPQESS_VQ_REG_VALUE 0x240240
>> ++
>> ++/* Tx side Port Interface Control Register */
>> ++#define IPQESS_REG_PORT_CTRL 0x4A8
>> ++#define IPQESS_PAD_EN_SHIFT 15
>> ++
>> ++/* Tx side VLAN Configuration Register */
>> ++#define IPQESS_REG_VLAN_CFG 0x4AC
>> ++
>> ++#define IPQESS_TX_CVLAN 16
>> ++#define IPQESS_TX_INS_CVLAN 17
>> ++#define IPQESS_TX_CVLAN_TAG_SHIFT 0
>> ++
>> ++#define IPQESS_TX_SVLAN 14
>> ++#define IPQESS_TX_INS_SVLAN 15
>> ++#define IPQESS_TX_SVLAN_TAG_SHIFT 16
>> ++
>> ++/* Tx Queue Packet Statistic Register */
>> ++#define IPQESS_REG_TX_STAT_PKT_Q(x) (0x700 + ((x) << 3)) /* x = queue id
>> */
>> ++
>> ++#define IPQESS_TX_STAT_PKT_MASK 0xFFFFFF
>> ++
>> ++/* Tx Queue Byte Statistic Register */
>> ++#define IPQESS_REG_TX_STAT_BYTE_Q(x) (0x704 + ((x) << 3)) /* x = queue id
>> */
>> ++
>> ++/* Load Balance Based Ring Offset Register */
>> ++#define IPQESS_REG_LB_RING 0x800
>> ++#define IPQESS_LB_RING_ENTRY_MASK 0xff
>> ++#define IPQESS_LB_RING_ID_MASK 0x7
>> ++#define IPQESS_LB_RING_PROFILE_ID_MASK 0x3
>> ++#define IPQESS_LB_RING_ENTRY_BIT_OFFSET 8
>> ++#define IPQESS_LB_RING_ID_OFFSET 0
>> ++#define IPQESS_LB_RING_PROFILE_ID_OFFSET 3
>> ++#define IPQESS_LB_REG_VALUE 0x6040200
>> ++
>> ++/* Load Balance Priority Mapping Register */
>> ++#define IPQESS_REG_LB_PRI_START 0x804
>> ++#define IPQESS_REG_LB_PRI_END 0x810
>> ++#define IPQESS_LB_PRI_REG_INC 4
>> ++#define IPQESS_LB_PRI_ENTRY_BIT_OFFSET 4
>> ++#define IPQESS_LB_PRI_ENTRY_MASK 0xf
>> ++
>> ++/* RSS Priority Mapping Register */
>> ++#define IPQESS_REG_RSS_PRI 0x820
>> ++#define IPQESS_RSS_PRI_ENTRY_MASK 0xf
>> ++#define IPQESS_RSS_RING_ID_MASK 0x7
>> ++#define IPQESS_RSS_PRI_ENTRY_BIT_OFFSET 4
>> ++
>> ++/* RSS Indirection Register */
>> ++#define IPQESS_REG_RSS_IDT(x) (0x840 + ((x) << 2)) /* x = No. of indirection
>> table */
>> ++#define IPQESS_NUM_IDT 16
>> ++#define IPQESS_RSS_IDT_VALUE 0x64206420
>> ++
>> ++/* Default RSS Ring Register */
>> ++#define IPQESS_REG_DEF_RSS 0x890
>> ++#define IPQESS_DEF_RSS_MASK 0x7
>> ++
>> ++/* RSS Hash Function Type Register */
>> ++#define IPQESS_REG_RSS_TYPE 0x894
>> ++#define IPQESS_RSS_TYPE_NONE 0x01
>> ++#define IPQESS_RSS_TYPE_IPV4TCP 0x02
>> ++#define IPQESS_RSS_TYPE_IPV6_TCP 0x04
>> ++#define IPQESS_RSS_TYPE_IPV4_UDP 0x08
>> ++#define IPQESS_RSS_TYPE_IPV6UDP 0x10
>> ++#define IPQESS_RSS_TYPE_IPV4 0x20
>> ++#define IPQESS_RSS_TYPE_IPV6 0x40
>> ++#define IPQESS_RSS_HASH_MODE_MASK 0x7f
>> ++
>> ++#define IPQESS_REG_RSS_HASH_VALUE 0x8C0
>> ++
>> ++#define IPQESS_REG_RSS_TYPE_RESULT 0x8C4
>> ++
>> ++#define IPQESS_HASH_TYPE_START 0
>> ++#define IPQESS_HASH_TYPE_END 5
>> ++#define IPQESS_HASH_TYPE_SHIFT 12
>> ++
>> ++#define IPQESS_RFS_FLOW_ENTRIES 1024
>> ++#define IPQESS_RFS_FLOW_ENTRIES_MASK (IPQESS_RFS_FLOW_ENTRIES -
>> 1)
>> ++#define IPQESS_RFS_EXPIRE_COUNT_PER_CALL 128
>> ++
>> ++/* RFD Base Address Register */
>> ++#define IPQESS_REG_RFD_BASE_ADDR_Q(x) (0x950 + ((x) << 3)) /* x =
>> queue id */
>> ++
>> ++/* RFD Index Register */
>> ++#define IPQESS_REG_RFD_IDX_Q(x) (0x9B0 + ((x) << 3))
>> ++
>> ++#define IPQESS_RFD_PROD_IDX_BITS 0x00000FFF
>> ++#define IPQESS_RFD_CONS_IDX_BITS 0x0FFF0000
>> ++#define IPQESS_RFD_PROD_IDX_MASK 0xFFF
>> ++#define IPQESS_RFD_CONS_IDX_MASK 0xFFF
>> ++#define IPQESS_RFD_PROD_IDX_SHIFT 0
>> ++#define IPQESS_RFD_CONS_IDX_SHIFT 16
>> ++
>> ++/* Rx Descriptor Control Register */
>> ++#define IPQESS_REG_RX_DESC0 0xA10
>> ++#define IPQESS_RFD_RING_SIZE_MASK 0xFFF
>> ++#define IPQESS_RX_BUF_SIZE_MASK 0xFFFF
>> ++#define IPQESS_RFD_RING_SIZE_SHIFT 0
>> ++#define IPQESS_RX_BUF_SIZE_SHIFT 16
>> ++
>> ++#define IPQESS_REG_RX_DESC1 0xA14
>> ++#define IPQESS_RXQ_RFD_BURST_NUM_MASK 0x3F
>> ++#define IPQESS_RXQ_RFD_PF_THRESH_MASK 0x1F
>> ++#define IPQESS_RXQ_RFD_LOW_THRESH_MASK 0xFFF
>> ++#define IPQESS_RXQ_RFD_BURST_NUM_SHIFT 0
>> ++#define IPQESS_RXQ_RFD_PF_THRESH_SHIFT 8
>> ++#define IPQESS_RXQ_RFD_LOW_THRESH_SHIFT 16
>> ++
>> ++/* RXQ Control Register */
>> ++#define IPQESS_REG_RXQ_CTRL 0xA18
>> ++#define IPQESS_FIFO_THRESH_TYPE_SHIF 0
>> ++#define IPQESS_FIFO_THRESH_128_BYTE 0x0
>> ++#define IPQESS_FIFO_THRESH_64_BYTE 0x1
>> ++#define IPQESS_RXQ_CTRL_RMV_VLAN 0x00000002
>> ++#define IPQESS_RXQ_CTRL_EN 0x0000FF00
>> ++
>> ++/* AXI Burst Size Config */
>> ++#define IPQESS_REG_AXIW_CTRL_MAXWRSIZE 0xA1C
>> ++#define IPQESS_AXIW_MAXWRSIZE_VALUE 0x0
>> ++
>> ++/* Rx Statistics Register */
>> ++#define IPQESS_REG_RX_STAT_BYTE_Q(x) (0xA30 + ((x) << 2)) /* x = queue
>> id */
>> ++#define IPQESS_REG_RX_STAT_PKT_Q(x) (0xA50 + ((x) << 2)) /* x = queue id
>> */
>> ++
>> ++/* WoL Pattern Length Register */
>> ++#define IPQESS_REG_WOL_PATTERN_LEN0 0xC00
>> ++#define IPQESS_WOL_PT_LEN_MASK 0xFF
>> ++#define IPQESS_WOL_PT0_LEN_SHIFT 0
>> ++#define IPQESS_WOL_PT1_LEN_SHIFT 8
>> ++#define IPQESS_WOL_PT2_LEN_SHIFT 16
>> ++#define IPQESS_WOL_PT3_LEN_SHIFT 24
>> ++
>> ++#define IPQESS_REG_WOL_PATTERN_LEN1 0xC04
>> ++#define IPQESS_WOL_PT4_LEN_SHIFT 0
>> ++#define IPQESS_WOL_PT5_LEN_SHIFT 8
>> ++#define IPQESS_WOL_PT6_LEN_SHIFT 16
>> ++
>> ++/* WoL Control Register */
>> ++#define IPQESS_REG_WOL_CTRL 0xC08
>> ++#define IPQESS_WOL_WK_EN 0x00000001
>> ++#define IPQESS_WOL_MG_EN 0x00000002
>> ++#define IPQESS_WOL_PT0_EN 0x00000004
>> ++#define IPQESS_WOL_PT1_EN 0x00000008
>> ++#define IPQESS_WOL_PT2_EN 0x00000010
>> ++#define IPQESS_WOL_PT3_EN 0x00000020
>> ++#define IPQESS_WOL_PT4_EN 0x00000040
>> ++#define IPQESS_WOL_PT5_EN 0x00000080
>> ++#define IPQESS_WOL_PT6_EN 0x00000100
>> ++
>> ++/* MAC Control Register */
>> ++#define IPQESS_REG_MAC_CTRL0 0xC20
>> ++#define IPQESS_REG_MAC_CTRL1 0xC24
>> ++
>> ++/* WoL Pattern Register */
>> ++#define IPQESS_REG_WOL_PATTERN_START 0x5000
>> ++#define IPQESS_PATTERN_PART_REG_OFFSET 0x40
>> ++
>> ++
>> ++/* TX descriptor fields */
>> ++#define IPQESS_TPD_HDR_SHIFT 0
>> ++#define IPQESS_TPD_PPPOE_EN 0x00000100
>> ++#define IPQESS_TPD_IP_CSUM_EN 0x00000200
>> ++#define IPQESS_TPD_TCP_CSUM_EN 0x0000400
>> ++#define IPQESS_TPD_UDP_CSUM_EN 0x00000800
>> ++#define IPQESS_TPD_CUSTOM_CSUM_EN 0x00000C00
>> ++#define IPQESS_TPD_LSO_EN 0x00001000
>> ++#define IPQESS_TPD_LSO_V2_EN 0x00002000
>> ++#define IPQESS_TPD_IPV4_EN 0x00010000
>> ++#define IPQESS_TPD_MSS_MASK 0x1FFF
>> ++#define IPQESS_TPD_MSS_SHIFT 18
>> ++#define IPQESS_TPD_CUSTOM_CSUM_SHIFT 18
>> ++
>> ++/* RRD descriptor fields */
>> ++#define IPQESS_RRD_NUM_RFD_MASK 0x000F
>> ++#define IPQESS_RRD_PKT_SIZE_MASK 0x3FFF
>> ++#define IPQESS_RRD_SRC_PORT_NUM_MASK 0x4000
>> ++#define IPQESS_RRD_SVLAN 0x8000
>> ++#define IPQESS_RRD_FLOW_COOKIE_MASK 0x07FF;
>> ++
>> ++#define IPQESS_RRD_PKT_SIZE_MASK 0x3FFF
>> ++#define IPQESS_RRD_CSUM_FAIL_MASK 0xC000
>> ++#define IPQESS_RRD_CVLAN 0x0001
>> ++#define IPQESS_RRD_DESC_VALID 0x8000
>> ++
>> ++#define IPQESS_RRD_PRIORITY_SHIFT 4
>> ++#define IPQESS_RRD_PRIORITY_MASK 0x7
>> ++#define IPQESS_RRD_PORT_TYPE_SHIFT 7
>> ++#define IPQESS_RRD_PORT_TYPE_MASK 0x1F
>> ++
>> ++#endif
>> +diff --git a/target/linux/ipq40xx/files-
>> 4.14/drivers/net/ethernet/qualcomm/ipqess_ethtool.c
>> b/target/linux/ipq40xx/files-
>> 4.14/drivers/net/ethernet/qualcomm/ipqess_ethtool.c
>> +new file mode 100644
>> +index 0000000000..0b75904d48
>> +--- /dev/null
>> ++++ b/target/linux/ipq40xx/files-
>> 4.14/drivers/net/ethernet/qualcomm/ipqess_ethtool.c
>> +@@ -0,0 +1,191 @@
>> ++/*
>> ++ * Copyright (c) 2015 - 2016, The Linux Foundation. All rights reserved.
>> ++ *
>> ++ * Permission to use, copy, modify, and/or distribute this software for
>> ++ * any purpose with or without fee is hereby granted, provided that the
>> ++ * above copyright notice and this permission notice appear in all copies.
>> ++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
>> WARRANTIES
>> ++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
>> OF
>> ++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE
>> LIABLE FOR
>> ++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
>> DAMAGES
>> ++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
>> WHETHER IN AN
>> ++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
>> ARISING OUT
>> ++ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
>> SOFTWARE.
>> ++ */
>> ++
>> ++#include <linux/ethtool.h>
>> ++#include <linux/netdevice.h>
>> ++#include <linux/string.h>
>> ++#include <linux/phy.h>
>> ++#include "ipqess.h"
>> ++
>> ++struct ipqesstool_stats {
>> ++	uint8_t string[ETH_GSTRING_LEN];
>> ++	uint32_t offset;
>> ++};
>> ++
>> ++#define IPQESS_STAT(m)    offsetof(struct ipqesstool_statistics, m)
>> ++#define DRVINFO_LEN	32
>> ++
>> ++static const struct ipqesstool_stats ipqess_stats[] = {
>> ++	{"tx_q0_pkt", IPQESS_STAT(tx_q0_pkt)},
>> ++	{"tx_q1_pkt", IPQESS_STAT(tx_q1_pkt)},
>> ++	{"tx_q2_pkt", IPQESS_STAT(tx_q2_pkt)},
>> ++	{"tx_q3_pkt", IPQESS_STAT(tx_q3_pkt)},
>> ++	{"tx_q4_pkt", IPQESS_STAT(tx_q4_pkt)},
>> ++	{"tx_q5_pkt", IPQESS_STAT(tx_q5_pkt)},
>> ++	{"tx_q6_pkt", IPQESS_STAT(tx_q6_pkt)},
>> ++	{"tx_q7_pkt", IPQESS_STAT(tx_q7_pkt)},
>> ++	{"tx_q8_pkt", IPQESS_STAT(tx_q8_pkt)},
>> ++	{"tx_q9_pkt", IPQESS_STAT(tx_q9_pkt)},
>> ++	{"tx_q10_pkt", IPQESS_STAT(tx_q10_pkt)},
>> ++	{"tx_q11_pkt", IPQESS_STAT(tx_q11_pkt)},
>> ++	{"tx_q12_pkt", IPQESS_STAT(tx_q12_pkt)},
>> ++	{"tx_q13_pkt", IPQESS_STAT(tx_q13_pkt)},
>> ++	{"tx_q14_pkt", IPQESS_STAT(tx_q14_pkt)},
>> ++	{"tx_q15_pkt", IPQESS_STAT(tx_q15_pkt)},
>> ++	{"tx_q0_byte", IPQESS_STAT(tx_q0_byte)},
>> ++	{"tx_q1_byte", IPQESS_STAT(tx_q1_byte)},
>> ++	{"tx_q2_byte", IPQESS_STAT(tx_q2_byte)},
>> ++	{"tx_q3_byte", IPQESS_STAT(tx_q3_byte)},
>> ++	{"tx_q4_byte", IPQESS_STAT(tx_q4_byte)},
>> ++	{"tx_q5_byte", IPQESS_STAT(tx_q5_byte)},
>> ++	{"tx_q6_byte", IPQESS_STAT(tx_q6_byte)},
>> ++	{"tx_q7_byte", IPQESS_STAT(tx_q7_byte)},
>> ++	{"tx_q8_byte", IPQESS_STAT(tx_q8_byte)},
>> ++	{"tx_q9_byte", IPQESS_STAT(tx_q9_byte)},
>> ++	{"tx_q10_byte", IPQESS_STAT(tx_q10_byte)},
>> ++	{"tx_q11_byte", IPQESS_STAT(tx_q11_byte)},
>> ++	{"tx_q12_byte", IPQESS_STAT(tx_q12_byte)},
>> ++	{"tx_q13_byte", IPQESS_STAT(tx_q13_byte)},
>> ++	{"tx_q14_byte", IPQESS_STAT(tx_q14_byte)},
>> ++	{"tx_q15_byte", IPQESS_STAT(tx_q15_byte)},
>> ++	{"rx_q0_pkt", IPQESS_STAT(rx_q0_pkt)},
>> ++	{"rx_q1_pkt", IPQESS_STAT(rx_q1_pkt)},
>> ++	{"rx_q2_pkt", IPQESS_STAT(rx_q2_pkt)},
>> ++	{"rx_q3_pkt", IPQESS_STAT(rx_q3_pkt)},
>> ++	{"rx_q4_pkt", IPQESS_STAT(rx_q4_pkt)},
>> ++	{"rx_q5_pkt", IPQESS_STAT(rx_q5_pkt)},
>> ++	{"rx_q6_pkt", IPQESS_STAT(rx_q6_pkt)},
>> ++	{"rx_q7_pkt", IPQESS_STAT(rx_q7_pkt)},
>> ++	{"rx_q0_byte", IPQESS_STAT(rx_q0_byte)},
>> ++	{"rx_q1_byte", IPQESS_STAT(rx_q1_byte)},
>> ++	{"rx_q2_byte", IPQESS_STAT(rx_q2_byte)},
>> ++	{"rx_q3_byte", IPQESS_STAT(rx_q3_byte)},
>> ++	{"rx_q4_byte", IPQESS_STAT(rx_q4_byte)},
>> ++	{"rx_q5_byte", IPQESS_STAT(rx_q5_byte)},
>> ++	{"rx_q6_byte", IPQESS_STAT(rx_q6_byte)},
>> ++	{"rx_q7_byte", IPQESS_STAT(rx_q7_byte)},
>> ++	{"tx_desc_error", IPQESS_STAT(tx_desc_error)},
>> ++};
>> ++
>> ++static int ipqess_get_strset_count(struct net_device *netdev, int sset)
>> ++{
>> ++	switch (sset) {
>> ++	case ETH_SS_STATS:
>> ++		return ARRAY_SIZE(ipqess_stats);
>> ++	default:
>> ++		netdev_dbg(netdev, "%s: Invalid string set", __func__);
>> ++		return -EOPNOTSUPP;
>> ++	}
>> ++}
>> ++
>> ++static void ipqess_get_strings(struct net_device *netdev, uint32_t stringset,
>> ++			       uint8_t *data)
>> ++{
>> ++	uint8_t *p = data;
>> ++	uint32_t i;
>> ++
>> ++	switch (stringset) {
>> ++	case ETH_SS_STATS:
>> ++		for (i = 0; i < ARRAY_SIZE(ipqess_stats); i++) {
>> ++			memcpy(p, ipqess_stats[i].string,
>> ++			       min((size_t)ETH_GSTRING_LEN,
>> ++			       strlen(ipqess_stats[i].string) + 1));
>> ++			p += ETH_GSTRING_LEN;
>> ++		}
>> ++		break;
>> ++	}
>> ++}
>> ++
>> ++static void ipqess_get_drvinfo(struct net_device *dev,
>> ++			       struct ethtool_drvinfo *info)
>> ++{
>> ++	strlcpy(info->driver, "qca_ipqess", DRVINFO_LEN);
>> ++	strlcpy(info->bus_info, "axi", ETHTOOL_BUSINFO_LEN);
>> ++}
>> ++
>> ++static int ipqess_get_settings(struct net_device *netdev,
>> ++			       struct ethtool_cmd *ecmd)
>> ++{
>> ++	struct phy_device *phydev = NULL;
>> ++	uint16_t phyreg;
>> ++
>> ++	phydev = netdev->phydev;
>> ++
>> ++	ecmd->advertising = phydev->advertising;
>> ++	ecmd->autoneg = phydev->autoneg;
>> ++	ecmd->speed = phydev->speed;
>> ++	ecmd->duplex = phydev->duplex;
>> ++	ecmd->phy_address = phydev->mdio.addr;
>> ++
>> ++	phyreg = (uint16_t)phy_read(netdev->phydev, MII_LPA);
>> ++	if (phyreg & LPA_10HALF)
>> ++		ecmd->lp_advertising |= ADVERTISED_10baseT_Half;
>> ++
>> ++	if (phyreg & LPA_10FULL)
>> ++		ecmd->lp_advertising |= ADVERTISED_10baseT_Full;
>> ++
>> ++	if (phyreg & LPA_100HALF)
>> ++		ecmd->lp_advertising |= ADVERTISED_100baseT_Half;
>> ++
>> ++	if (phyreg & LPA_100FULL)
>> ++		ecmd->lp_advertising |= ADVERTISED_100baseT_Full;
>> ++
>> ++	phyreg = (uint16_t)phy_read(netdev->phydev, MII_STAT1000);
>> ++	if (phyreg & LPA_1000HALF)
>> ++		ecmd->lp_advertising |= ADVERTISED_1000baseT_Half;
>> ++
>> ++	if (phyreg & LPA_1000FULL)
>> ++		ecmd->lp_advertising |= ADVERTISED_1000baseT_Full;
>> ++
>> ++	return 0;
>> ++}
>> ++
>> ++static int ipqess_set_settings(struct net_device *netdev,
>> ++			     struct ethtool_cmd *ecmd)
>> ++{
>> ++	struct phy_device *phydev = NULL;
>> ++
>> ++	phydev = netdev->phydev;
>> ++	phydev->advertising = ecmd->advertising;
>> ++	phydev->autoneg = ecmd->autoneg;
>> ++	phydev->speed = ethtool_cmd_speed(ecmd);
>> ++	phydev->duplex = ecmd->duplex;
>> ++
>> ++	genphy_config_aneg(phydev);
>> ++
>> ++	return 0;
>> ++}
>> ++
>> ++static void ipqess_get_ringparam(struct net_device *netdev,
>> ++			       struct ethtool_ringparam *ring)
>> ++{
>> ++	ring->tx_max_pending = IPQESS_TX_RING_SIZE;
>> ++	ring->rx_max_pending = IPQESS_RX_RING_SIZE;
>> ++}
>> ++
>> ++static const struct ethtool_ops ipqesstool_ops = {
>> ++	.get_drvinfo = &ipqess_get_drvinfo,
>> ++	.get_link = &ethtool_op_get_link,
>> ++	.get_settings = &ipqess_get_settings,
>> ++	.set_settings = &ipqess_set_settings,
>> ++	.get_strings = &ipqess_get_strings,
>> ++	.get_sset_count = &ipqess_get_strset_count,
>> ++	.get_ringparam = ipqess_get_ringparam,
>> ++};
>> ++
>> ++void ipqess_set_ethtool_ops(struct net_device *netdev)
>> ++{
>> ++	netdev->ethtool_ops = &ipqesstool_ops;
>> ++}
>> +diff --git a/target/linux/ipq40xx/patches-4.14/999-0-ipqess.patch
>> b/target/linux/ipq40xx/patches-4.14/999-0-ipqess.patch
>> +new file mode 100644
>> +index 0000000000..c0ffe6f052
>> +--- /dev/null
>> ++++ b/target/linux/ipq40xx/patches-4.14/999-0-ipqess.patch
>> +@@ -0,0 +1,167 @@
>> ++Index: linux-4.9.34/drivers/net/ethernet/qualcomm/Kconfig
>> ++===========================================================
>> ========
>> ++--- linux-4.9.34.orig/drivers/net/ethernet/qualcomm/Kconfig
>> +++++ linux-4.9.34/drivers/net/ethernet/qualcomm/Kconfig
>> ++@@ -15,6 +15,15 @@ config NET_VENDOR_QUALCOMM
>> ++
>> ++ if NET_VENDOR_QUALCOMM
>> ++
>> +++config IPQ_ESS
>> +++	tristate "Qualcomm Atheros IPQ ESS support"
>> +++	depends on OF
>> +++	---help---
>> +++	  This SPI protocol driver supports the Qualcomm Atheros QCA7000.
>> +++
>> +++	  To compile this driver as a module, choose M here. The module
>> +++	  will be called qcaspi.
>> +++
>> ++ config QCA7000
>> ++ 	tristate "Qualcomm Atheros QCA7000 support"
>> ++ 	depends on SPI_MASTER && OF
>> ++Index: linux-4.9.34/drivers/net/ethernet/qualcomm/Makefile
>> ++===========================================================
>> ========
>> ++--- linux-4.9.34.orig/drivers/net/ethernet/qualcomm/Makefile
>> +++++ linux-4.9.34/drivers/net/ethernet/qualcomm/Makefile
>> ++@@ -2,6 +2,9 @@
>> ++ # Makefile for the Qualcomm network device drivers.
>> ++ #
>> ++
>> +++obj-$(CONFIG_IPQ_ESS) += ipq_ess.o
>> +++ipq_ess-objs := ipqess.o ipqess_ethtool.o
>> +++
>> ++ obj-$(CONFIG_QCA7000) += qca_7k_common.o
>> ++ obj-$(CONFIG_QCA7000_SPI) += qcaspi.o
>> ++ qcaspi-objs := qca_7k.o qca_debug.o qca_spi.o
>> ++Index: linux-4.9.34/arch/arm/boot/dts/qcom-ipq4019.dtsi
>> ++===========================================================
>> ========
>> ++--- linux-4.9.34.orig/arch/arm/boot/dts/qcom-ipq4019.dtsi
>> +++++ linux-4.9.34/arch/arm/boot/dts/qcom-ipq4019.dtsi
>> ++@@ -44,8 +44,7 @@
>> ++ 		spi1 = &spi_1;
>> ++ 		i2c0 = &i2c_0;
>> ++ 		i2c1 = &i2c_1;
>> ++-		ethernet0 = &gmac0;
>> ++-		ethernet1 = &gmac1;
>> +++		ethernet0 = &gmac;
>> ++ 	};
>> ++
>> ++ 	cpus {
>> ++@@ -389,6 +388,53 @@
>> ++ 			status = "disabled";
>> ++ 		};
>> ++
>> +++		gmac: edma at c080000 {
>> +++			compatible = "qcom,ess-edma";
>> +++			reg = <0xc080000 0x8000>;
>> +++			interrupts = <GIC_SPI  65 IRQ_TYPE_EDGE_RISING>,
>> +++				     <GIC_SPI  66 IRQ_TYPE_EDGE_RISING>,
>> +++				     <GIC_SPI  67 IRQ_TYPE_EDGE_RISING>,
>> +++				     <GIC_SPI  68 IRQ_TYPE_EDGE_RISING>,
>> +++				     <GIC_SPI  69 IRQ_TYPE_EDGE_RISING>,
>> +++				     <GIC_SPI  79 IRQ_TYPE_EDGE_RISING>,
>> +++				     <GIC_SPI  71 IRQ_TYPE_EDGE_RISING>,
>> +++				     <GIC_SPI  72 IRQ_TYPE_EDGE_RISING>,
>> +++				     <GIC_SPI  73 IRQ_TYPE_EDGE_RISING>,
>> +++				     <GIC_SPI  74 IRQ_TYPE_EDGE_RISING>,
>> +++				     <GIC_SPI  75 IRQ_TYPE_EDGE_RISING>,
>> +++				     <GIC_SPI  76 IRQ_TYPE_EDGE_RISING>,
>> +++				     <GIC_SPI  77 IRQ_TYPE_EDGE_RISING>,
>> +++				     <GIC_SPI  78 IRQ_TYPE_EDGE_RISING>,
>> +++				     <GIC_SPI  79 IRQ_TYPE_EDGE_RISING>,
>> +++				     <GIC_SPI  80 IRQ_TYPE_EDGE_RISING>,
>> +++				     <GIC_SPI 240 IRQ_TYPE_EDGE_RISING>,
>> +++				     <GIC_SPI 241 IRQ_TYPE_EDGE_RISING>,
>> +++				     <GIC_SPI 242 IRQ_TYPE_EDGE_RISING>,
>> +++				     <GIC_SPI 243 IRQ_TYPE_EDGE_RISING>,
>> +++				     <GIC_SPI 244 IRQ_TYPE_EDGE_RISING>,
>> +++				     <GIC_SPI 245 IRQ_TYPE_EDGE_RISING>,
>> +++				     <GIC_SPI 246 IRQ_TYPE_EDGE_RISING>,
>> +++				     <GIC_SPI 247 IRQ_TYPE_EDGE_RISING>,
>> +++				     <GIC_SPI 248 IRQ_TYPE_EDGE_RISING>,
>> +++				     <GIC_SPI 249 IRQ_TYPE_EDGE_RISING>,
>> +++				     <GIC_SPI 250 IRQ_TYPE_EDGE_RISING>,
>> +++				     <GIC_SPI 251 IRQ_TYPE_EDGE_RISING>,
>> +++				     <GIC_SPI 252 IRQ_TYPE_EDGE_RISING>,
>> +++				     <GIC_SPI 253 IRQ_TYPE_EDGE_RISING>,
>> +++				     <GIC_SPI 254 IRQ_TYPE_EDGE_RISING>,
>> +++				     <GIC_SPI 255 IRQ_TYPE_EDGE_RISING>;
>> +++
>> +++			status = "disabled";
>> +++
>> +++			phy-mode = "sgmii";
>> +++
>> +++			fixed-link {
>> +++				speed = <1000>;
>> +++				full-duplex;
>> +++				pause;
>> +++			};
>> +++		};
>> +++
>> ++ 		pcie0: pci at 40000000 {
>> ++ 			compatible = "qcom,pcie-ipq4019";
>> ++ 			reg =  <0x40000000 0xf1d
>> ++@@ -462,64 +508,6 @@
>> ++ 			status = "disabled";
>> ++ 		};
>> ++
>> ++-		edma at c080000 {
>> ++-			compatible = "qcom,ess-edma";
>> ++-			reg = <0xc080000 0x8000>;
>> ++-			qcom,page-mode = <0>;
>> ++-			qcom,rx_head_buf_size = <1540>;
>> ++-			qcom,mdio_supported;
>> ++-			qcom,poll_required = <1>;
>> ++-			qcom,num_gmac = <2>;
>> ++-			interrupts = <0  65 IRQ_TYPE_EDGE_RISING
>> ++-				      0  66 IRQ_TYPE_EDGE_RISING
>> ++-				      0  67 IRQ_TYPE_EDGE_RISING
>> ++-				      0  68 IRQ_TYPE_EDGE_RISING
>> ++-				      0  69 IRQ_TYPE_EDGE_RISING
>> ++-				      0  70 IRQ_TYPE_EDGE_RISING
>> ++-				      0  71 IRQ_TYPE_EDGE_RISING
>> ++-				      0  72 IRQ_TYPE_EDGE_RISING
>> ++-				      0  73 IRQ_TYPE_EDGE_RISING
>> ++-				      0  74 IRQ_TYPE_EDGE_RISING
>> ++-				      0  75 IRQ_TYPE_EDGE_RISING
>> ++-				      0  76 IRQ_TYPE_EDGE_RISING
>> ++-				      0  77 IRQ_TYPE_EDGE_RISING
>> ++-				      0  78 IRQ_TYPE_EDGE_RISING
>> ++-				      0  79 IRQ_TYPE_EDGE_RISING
>> ++-				      0  80 IRQ_TYPE_EDGE_RISING
>> ++-				      0 240 IRQ_TYPE_EDGE_RISING
>> ++-				      0 241 IRQ_TYPE_EDGE_RISING
>> ++-				      0 242 IRQ_TYPE_EDGE_RISING
>> ++-				      0 243 IRQ_TYPE_EDGE_RISING
>> ++-				      0 244 IRQ_TYPE_EDGE_RISING
>> ++-				      0 245 IRQ_TYPE_EDGE_RISING
>> ++-				      0 246 IRQ_TYPE_EDGE_RISING
>> ++-				      0 247 IRQ_TYPE_EDGE_RISING
>> ++-				      0 248 IRQ_TYPE_EDGE_RISING
>> ++-				      0 249 IRQ_TYPE_EDGE_RISING
>> ++-				      0 250 IRQ_TYPE_EDGE_RISING
>> ++-				      0 251 IRQ_TYPE_EDGE_RISING
>> ++-				      0 252 IRQ_TYPE_EDGE_RISING
>> ++-				      0 253 IRQ_TYPE_EDGE_RISING
>> ++-				      0 254 IRQ_TYPE_EDGE_RISING
>> ++-				      0 255 IRQ_TYPE_EDGE_RISING>;
>> ++-
>> ++-			status = "disabled";
>> ++-
>> ++-			gmac0: gmac0 {
>> ++-				local-mac-address = [00 00 00 00 00 00];
>> ++-				vlan_tag = <1 0x1f>;
>> ++-			};
>> ++-
>> ++-			gmac1: gmac1 {
>> ++-				local-mac-address = [00 00 00 00 00 00];
>> ++-				qcom,phy_mdio_addr = <4>;
>> ++-				qcom,poll_required = <1>;
>> ++-				qcom,forced_speed = <1000>;
>> ++-				qcom,forced_duplex = <1>;
>> ++-				vlan_tag = <2 0x20>;
>> ++-			};
>> ++-		};
>> ++-
>> ++ 		usb3_ss_phy: ssphy at 9a000 {
>> ++ 			compatible = "qca,uni-ssphy";
>> ++ 			reg = <0x9a000 0x800>;
>> +--
>> +2.11.0
>> +
>> diff --git a/build_patches/openwrt/0007-ipqess-integration.patch
>> b/build_patches/openwrt/0007-ipqess-integration.patch
>> new file mode 100644
>> index 0000000..3a6b91f
>> --- /dev/null
>> +++ b/build_patches/openwrt/0007-ipqess-integration.patch
>> @@ -0,0 +1,207 @@
>> +From 63fcc7024ae7cb989d1817dc1eeb128f702548bf Mon Sep 17 00:00:00 2001
>> +From: Christian Dresel <fff at chrisi01.de>
>> +Date: Sat, 24 Nov 2018 17:22:23 +0100
>> +Subject: [PATCH] ipqess integration
>> +
>> +Signed-off-by: Christian Dresel <fff at chrisi01.de>
>> +
>> +rebase
>> +
>> +Signed-off-by: Christian Dresel <fff at chrisi01.de>
>> +---
>> + target/linux/ipq40xx/base-files/etc/board.d/01_leds   |  2 +-
>> + .../linux/ipq40xx/base-files/etc/board.d/02_network   | 13 +++++++++++--
>> + .../files-4.14/arch/arm/boot/dts/qcom-ipq4018-a42.dts | 16 ----------------
>> + .../arch/arm/boot/dts/qcom-ipq4018-jalapeno.dts       | 14 --------------
>> + .../files-4.14/arch/arm/boot/dts/qcom-ipq4019-a62.dts |  8 --------
>> + .../arch/arm/boot/dts/qcom-ipq4028-wpj428.dts         | 16 ----------------
>> + .../arch/arm/boot/dts/qcom-ipq4029-mr33.dts           | 19 +++++++++----------
>> + 7 files changed, 21 insertions(+), 67 deletions(-)
>> +
>> +diff --git a/target/linux/ipq40xx/base-files/etc/board.d/01_leds
>> b/target/linux/ipq40xx/base-files/etc/board.d/01_leds
>> +index fcba2aea54..ceec20d99a 100755
>> +--- a/target/linux/ipq40xx/base-files/etc/board.d/01_leds
>> ++++ b/target/linux/ipq40xx/base-files/etc/board.d/01_leds
>> +@@ -20,7 +20,7 @@ asus,rt-ac58u)
>> + 	;;
>> + avm,fritzbox-4040)
>> + 	ucidef_set_led_wlan "wlan" "WLAN" "fritz4040:green:wlan" "phy0tpt"
>> "phy1tpt"
>> +-	ucidef_set_led_netdev "wan" "WAN" "fritz4040:green:wan" "eth1"
>> ++	ucidef_set_led_switch "wan" "WAN" "fritz4040:green:wan" "switch0"
>> "0x20"
>> + 	ucidef_set_led_switch "lan" "LAN" "fritz4040:green:lan" "switch0"
>> "0x1e"
>> + 	;;
>> + glinet,gl-b1300)
>> +diff --git a/target/linux/ipq40xx/base-files/etc/board.d/02_network
>> b/target/linux/ipq40xx/base-files/etc/board.d/02_network
>> +index 03e0c0e16c..7c84139d11 100755
>> +--- a/target/linux/ipq40xx/base-files/etc/board.d/02_network
>> ++++ b/target/linux/ipq40xx/base-files/etc/board.d/02_network
>> +@@ -26,9 +26,8 @@ asus,rt-ac58u)
>> + 	ucidef_set_interface_macaddr "wan" "$wan_mac_addr"
>> + 	;;
>> + avm,fritzbox-4040)
>> +-	ucidef_set_interfaces_lan_wan "eth0" "eth1"
>> + 	ucidef_add_switch "switch0" \
>> +-		"0u at eth0" "1:lan" "2:lan" "3:lan" "4:lan"
>> ++		"0 at eth0" "1:lan:4" "2:lan:3" "3:lan:2" "4:lan:1" "5:wan"
>> + 	;;
>> + compex,wpj428)
>> + 	ucidef_set_interface_lan "eth0 eth1"
>> +@@ -53,6 +52,16 @@ zyxel,wre6606)
>> + 	;;
>> + esac
>> +
>> ++case "$board" in
>> ++avm,fritzbox-4040)
>> ++	lan_mac=$(fritz_tffs -b -n maca -i $(find_mtd_part "tffs1"))
>> ++	wan_mac=$(fritz_tffs -b -n macb -i $(find_mtd_part "tffs1"))
>> ++	;;
>> ++esac
>> ++[ -n "$lan_mac" ] && ucidef_set_interface_macaddr "lan" "$lan_mac"
>> ++[ -n "$wan_mac" ] && ucidef_set_interface_macaddr "wan" "$wan_mac"
>> ++
>> ++
>> + board_config_flush
>> +
>> + exit 0
>> +diff --git a/target/linux/ipq40xx/files-4.14/arch/arm/boot/dts/qcom-ipq4018-
>> a42.dts b/target/linux/ipq40xx/files-4.14/arch/arm/boot/dts/qcom-ipq4018-
>> a42.dts
>> +index 20330afc66..f0fb05a801 100644
>> +--- a/target/linux/ipq40xx/files-4.14/arch/arm/boot/dts/qcom-ipq4018-a42.dts
>> ++++ b/target/linux/ipq40xx/files-4.14/arch/arm/boot/dts/qcom-ipq4018-
>> a42.dts
>> +@@ -185,22 +185,6 @@
>> + 	status = "okay";
>> + };
>> +
>> +-&gmac0 {
>> +-	qcom,phy_mdio_addr = <4>;
>> +-	qcom,poll_required = <1>;
>> +-	qcom,forced_speed = <1000>;
>> +-	qcom,forced_duplex = <1>;
>> +-	vlan_tag = <2 0x20>;
>> +-};
>> +-
>> +-&gmac1 {
>> +-	qcom,phy_mdio_addr = <3>;
>> +-	qcom,poll_required = <1>;
>> +-	qcom,forced_speed = <1000>;
>> +-	qcom,forced_duplex = <1>;
>> +-	vlan_tag = <1 0x10>;
>> +-};
>> +-
>> + &usb2_hs_phy {
>> + 	status = "okay";
>> + };
>> +diff --git a/target/linux/ipq40xx/files-4.14/arch/arm/boot/dts/qcom-ipq4018-
>> jalapeno.dts b/target/linux/ipq40xx/files-4.14/arch/arm/boot/dts/qcom-
>> ipq4018-jalapeno.dts
>> +index ee203b0f1e..fd871f2985 100644
>> +--- a/target/linux/ipq40xx/files-4.14/arch/arm/boot/dts/qcom-ipq4018-
>> jalapeno.dts
>> ++++ b/target/linux/ipq40xx/files-4.14/arch/arm/boot/dts/qcom-ipq4018-
>> jalapeno.dts
>> +@@ -230,20 +230,6 @@
>> + 	status = "okay";
>> + };
>> +
>> +-&gmac0 {
>> +-	qcom,poll_required = <1>;
>> +-	qcom,poll_required_dynamic = <1>;
>> +-	qcom,phy_mdio_addr = <3>;
>> +-	vlan_tag = <1 0x10>;
>> +-};
>> +-
>> +-&gmac1 {
>> +-	qcom,poll_required = <1>;
>> +-	qcom,poll_required_dynamic = <1>;
>> +-	qcom,phy_mdio_addr = <4>;
>> +-	vlan_tag = <2 0x20>;
>> +-};
>> +-
>> + &wifi0 {
>> + 	status = "okay";
>> + 	qcom,ath10k-calibration-variant = "8devices-Jalapeno";
>> +diff --git a/target/linux/ipq40xx/files-4.14/arch/arm/boot/dts/qcom-ipq4019-
>> a62.dts b/target/linux/ipq40xx/files-4.14/arch/arm/boot/dts/qcom-ipq4019-
>> a62.dts
>> +index dd6d6bd775..8e0f6f4e0c 100644
>> +--- a/target/linux/ipq40xx/files-4.14/arch/arm/boot/dts/qcom-ipq4019-a62.dts
>> ++++ b/target/linux/ipq40xx/files-4.14/arch/arm/boot/dts/qcom-ipq4019-
>> a62.dts
>> +@@ -195,14 +195,6 @@
>> + 	status = "okay";
>> + };
>> +
>> +-&gmac0 {
>> +-	qcom,phy_mdio_addr = <3>;
>> +-	qcom,poll_required = <1>;
>> +-	qcom,forced_speed = <1000>;
>> +-	qcom,forced_duplex = <1>;
>> +-	vlan_tag = <1 0x10>;
>> +-};
>> +-
>> + &usb2_hs_phy {
>> + 	status = "okay";
>> + };
>> +diff --git a/target/linux/ipq40xx/files-4.14/arch/arm/boot/dts/qcom-ipq4028-
>> wpj428.dts b/target/linux/ipq40xx/files-4.14/arch/arm/boot/dts/qcom-ipq4028-
>> wpj428.dts
>> +index f9f0f96ae9..79d0d22ab7 100644
>> +--- a/target/linux/ipq40xx/files-4.14/arch/arm/boot/dts/qcom-ipq4028-
>> wpj428.dts
>> ++++ b/target/linux/ipq40xx/files-4.14/arch/arm/boot/dts/qcom-ipq4028-
>> wpj428.dts
>> +@@ -233,22 +233,6 @@
>> + 	status = "okay";
>> + };
>> +
>> +-&gmac0 {
>> +-	qcom,phy_mdio_addr = <4>;
>> +-	qcom,poll_required = <1>;
>> +-	qcom,forced_speed = <1000>;
>> +-	qcom,forced_duplex = <1>;
>> +-	vlan_tag = <2 0x20>;
>> +-};
>> +-
>> +-&gmac1 {
>> +-	qcom,phy_mdio_addr = <3>;
>> +-	qcom,poll_required = <1>;
>> +-	qcom,forced_speed = <1000>;
>> +-	qcom,forced_duplex = <1>;
>> +-	vlan_tag = <1 0x10>;
>> +-};
>> +-
>> + &usb3_ss_phy {
>> + 	status = "okay";
>> + };
>> +diff --git a/target/linux/ipq40xx/files-4.14/arch/arm/boot/dts/qcom-ipq4029-
>> mr33.dts b/target/linux/ipq40xx/files-4.14/arch/arm/boot/dts/qcom-ipq4029-
>> mr33.dts
>> +index 0be1960655..c913b3c354 100644
>> +--- a/target/linux/ipq40xx/files-4.14/arch/arm/boot/dts/qcom-ipq4029-
>> mr33.dts
>> ++++ b/target/linux/ipq40xx/files-4.14/arch/arm/boot/dts/qcom-ipq4029-
>> mr33.dts
>> +@@ -97,14 +97,19 @@
>> +
>> + 		ess-switch at c000000 {
>> + 			switch_mac_mode = <0x3>; /* mac mode for RGMII
>> RMII */
>> +-			switch_lan_bmp = <0x0>; /* lan port bitmap */
>> +-			switch_wan_bmp = <0x10>; /* wan port bitmap */
>> ++			mdio {
>> ++				#address-cells = <1>;
>> ++				#size-cells = <0>;
>> ++
>> ++				phy: phy4 at 4 {
>> ++					reg = <4>;
>> ++				};
>> ++			};
>> + 		};
>> +
>> + 		edma at c080000 {
>> +-			qcom,single-phy;
>> +-			qcom,num_gmac = <1>;
>> + 			phy-mode = "rgmii-rxid";
>> ++			phy-handle = <&phy>;
>> + 			status = "okay";
>> + 		};
>> + 	};
>> +@@ -138,12 +143,6 @@
>> + 	status = "okay";
>> + };
>> +
>> +-&gmac0 {
>> +-	qcom,phy_mdio_addr = <1>;
>> +-	qcom,poll_required = <1>;
>> +-	vlan_tag = <0 0x20>;
>> +-};
>> +-
>> + &i2c_0 {
>> + 	pinctrl-0 = <&i2c_0_pins>;
>> + 	pinctrl-names = "default";
>> +--
>> +2.11.0
>> +
>> diff --git a/build_patches/openwrt/0008-Enable-QinQ-on-the-switch.patch
>> b/build_patches/openwrt/0008-Enable-QinQ-on-the-switch.patch
>> new file mode 100644
>> index 0000000..8e9ce77
>> --- /dev/null
>> +++ b/build_patches/openwrt/0008-Enable-QinQ-on-the-switch.patch
>> @@ -0,0 +1,106 @@
>> +From 57f78fa2d1d5175ce3e55c3e786b7a768eb0e56f Mon Sep 17 00:00:00 2001
>> +From: Christian Dresel <fff at chrisi01.de>
>> +Date: Sat, 24 Nov 2018 00:28:58 +0100
>> +Subject: [PATCH] Enable-QinQ-on-the-switch
>> +
>> +Signed-off-by: Christian Dresel <fff at chrisi01.de>
>> +---
>> + .../715-ar40xx-Enable-QinQ-on-the-switch.patch     | 86
>> ++++++++++++++++++++++
>> + 1 file changed, 86 insertions(+)
>> + create mode 100644 target/linux/ipq40xx/patches-4.14/715-ar40xx-Enable-
>> QinQ-on-the-switch.patch
>> +
>> +diff --git a/target/linux/ipq40xx/patches-4.14/715-ar40xx-Enable-QinQ-on-the-
>> switch.patch b/target/linux/ipq40xx/patches-4.14/715-ar40xx-Enable-QinQ-on-
>> the-switch.patch
>> +new file mode 100644
>> +index 0000000000..c8edcbf5bc
>> +--- /dev/null
>> ++++ b/target/linux/ipq40xx/patches-4.14/715-ar40xx-Enable-QinQ-on-the-
>> switch.patch
>> +@@ -0,0 +1,86 @@
>> ++From: Sven Eckelmann <sven at narfation.org>
>> ++Date: Fri, 17 Mar 2017 11:04:50 +0100
>> ++Subject: [PATCH] ar40xx: Enable QinQ on the switch
>> ++
>> ++The switch used in by IPQ40xx is using VLANs by default to select the
>> ++outgoing port. It was therefore not possible to sent or receive 802.1q
>> ++tagged frames over the CPU port. This can be allowed by changing the port
>> ++configuration and lookup configuration.
>> ++
>> ++The resulting VLAN-tagged frames send or received by the CPU will therefore
>> ++look like QinQ frames. The outer VLAN tag is the port-VLAN of the port from
>> ++which the data was received or towards which the data has to be sent. The
>> ++inner VLAN tag (when it exists) is the VLAN which was configrued on top of
>> ++the ethernet device.
>> ++
>> ++---
>> ++
>> ++diff --git a/drivers/net/phy/ar40xx.c b/drivers/net/phy/ar40xx.c
>> ++--- a/drivers/net/phy/ar40xx.c
>> +++++ b/drivers/net/phy/ar40xx.c
>> ++@@ -1246,6 +1246,10 @@ ar40xx_init_globals(struct ar40xx_priv *priv)
>> ++ 	t = (AR40XX_PORT0_FC_THRESH_ON_DFLT << 16) |
>> ++ 	      AR40XX_PORT0_FC_THRESH_OFF_DFLT;
>> ++ 	ar40xx_write(priv, AR40XX_REG_PORT_FLOWCTRL_THRESH(0), t);
>> +++
>> +++	/* set service tag to 802.1q */
>> +++	//t = ETH_P_8021Q | AR40XX_ESS_SERVICE_TAG_STAG;
>> +++	//ar40xx_write(priv, AR40XX_ESS_SERVICE_TAG, t);
>> ++ }
>> ++
>> ++ static void
>> ++@@ -1571,7 +1575,11 @@ ar40xx_setup_port(struct ar40xx_priv *priv, int
>> port, u32 members)
>> ++ 	u32 pvid = priv->vlan_id[priv->pvid[port]];
>> ++
>> ++ 	if (priv->vlan) {
>> ++-		egress = AR40XX_PORT_VLAN1_OUT_MODE_UNMOD;
>> +++		//if (priv->vlan_tagged & BIT(port))
>> +++		//	egress = AR40XX_PORT_VLAN1_OUT_MODE_TAG;
>> +++		//else
>> +++			egress = AR40XX_PORT_VLAN1_OUT_MODE_UNMOD;
>> +++
>> ++ 		ingress = AR40XX_IN_SECURE;
>> ++ 	} else {
>> ++ 		egress = AR40XX_PORT_VLAN1_OUT_MODE_UNTOUCH;
>> ++@@ -1582,8 +1590,17 @@ ar40xx_setup_port(struct ar40xx_priv *priv, int
>> port, u32 members)
>> ++ 	t |= pvid << AR40XX_PORT_VLAN0_DEF_CVID_S;
>> ++ 	ar40xx_write(priv, AR40XX_REG_PORT_VLAN0(port), t);
>> ++
>> ++-	t = AR40XX_PORT_VLAN1_PORT_VLAN_PROP;
>> ++-	t |= egress << AR40XX_PORT_VLAN1_OUT_MODE_S;
>> +++	t = egress << AR40XX_PORT_VLAN1_OUT_MODE_S;
>> +++
>> +++	///* set CPU port to core port */
>> +++	//if (port == 0)
>> +++	//	t |= AR40XX_PORT_VLAN1_CORE_PORT;
>> +++
>> +++	//if (priv->vlan_tagged & BIT(port))
>> +++		t |= AR40XX_PORT_VLAN1_PORT_VLAN_PROP;
>> +++	//else
>> +++	//	t |= AR40XX_PORT_VLAN1_PORT_TLS_MODE;
>> +++
>> ++ 	ar40xx_write(priv, AR40XX_REG_PORT_VLAN1(port), t);
>> ++
>> ++ 	t = members;
>> ++diff --git a/drivers/net/phy/ar40xx.h b/drivers/net/phy/ar40xx.h
>> ++--- a/drivers/net/phy/ar40xx.h
>> +++++ b/drivers/net/phy/ar40xx.h
>> ++@@ -151,6 +151,9 @@ struct ar40xx_mib_desc {
>> ++ #define   AR40XX_MIB_FUNC_NO_OP		0x0
>> ++ #define   AR40XX_MIB_FUNC_FLUSH		0x1
>> ++
>> +++#define AR40XX_ESS_SERVICE_TAG		0x48
>> +++#define AR40XX_ESS_SERVICE_TAG_STAG	BIT(17)
>> +++
>> ++ #define AR40XX_REG_PORT_STATUS(_i)		(0x07c + (_i) * 4)
>> ++ #define   AR40XX_PORT_SPEED			BITS(0, 2)
>> ++ #define   AR40XX_PORT_STATUS_SPEED_S	0
>> ++@@ -179,6 +182,8 @@ struct ar40xx_mib_desc {
>> ++ #define   AR40XX_PORT_VLAN0_DEF_CVID_S		16
>> ++
>> ++ #define AR40XX_REG_PORT_VLAN1(_i)		(0x424 + (_i) * 0x8)
>> +++#define   AR40XX_PORT_VLAN1_CORE_PORT		BIT(9)
>> +++#define   AR40XX_PORT_VLAN1_PORT_TLS_MODE	BIT(7)
>> ++ #define   AR40XX_PORT_VLAN1_PORT_VLAN_PROP	BIT(6)
>> ++ #define   AR40XX_PORT_VLAN1_OUT_MODE		BITS(12, 2)
>> ++ #define   AR40XX_PORT_VLAN1_OUT_MODE_S		12
>> +--
>> +2.11.0
>> +
>> diff --git a/build_patches/openwrt/0009-enable-IPQ_ESS.patch
>> b/build_patches/openwrt/0009-enable-IPQ_ESS.patch
>> new file mode 100644
>> index 0000000..8471930
>> --- /dev/null
>> +++ b/build_patches/openwrt/0009-enable-IPQ_ESS.patch
>> @@ -0,0 +1,23 @@
>> +From a9373adae4503b84c73d1f095b26683ac5e7063b Mon Sep 17 00:00:00 2001
>> +From: Christian Dresel <fff at chrisi01.de>
>> +Date: Sat, 24 Nov 2018 23:34:58 +0100
>> +Subject: [PATCH] enable IPQ_ESS
>> +
>> +Signed-off-by: Christian Dresel <fff at chrisi01.de>
>> +---
>> + target/linux/ipq40xx/config-4.14 | 1 +
>> + 1 file changed, 1 insertion(+)
>> +
>> +diff --git a/target/linux/ipq40xx/config-4.14 b/target/linux/ipq40xx/config-4.14
>> +index fa52d58bed..184e8fd5fb 100644
>> +--- a/target/linux/ipq40xx/config-4.14
>> ++++ b/target/linux/ipq40xx/config-4.14
>> +@@ -482,3 +482,4 @@ CONFIG_ZBOOT_ROM_BSS=0
>> + CONFIG_ZBOOT_ROM_TEXT=0
>> + CONFIG_ZLIB_DEFLATE=y
>> + CONFIG_ZLIB_INFLATE=y
>> ++CONFIG_IPQ_ESS=y
>> +\ No newline at end of file
>> +--
>> +2.11.0
>> +
>> diff --git a/buildscript b/buildscript
>> index f8d435c..df82a36 100755
>> --- a/buildscript
>> +++ b/buildscript
>> @@ -279,10 +279,10 @@ cp_firmware() {
>>          filename_build=${filename_build//tiny/t}
>>          cp "$target/bin/targets/${chipset}/${subtarget}/$image"
>> "./bin/$filename_build"
>>
>> -        for region in "" "-eu" "-us"; do
>> -            image_factory=${image/sysupgrade/factory$region}
>> +        for factory in "factory" "factory-eu" "factory-us" "eva"; do
>> +            image_factory=${image/sysupgrade/$factory}
>>              if [[ -f "$target/bin/targets/${chipset}/${subtarget}/$image_factory" ]];
>> then
>> -              filename_build_factory=${filename_build/sysupgrade/factory$region}
>> +              filename_build_factory=${filename_build/sysupgrade/$factory}
>>                if [ ${#image_factory} -lt ${#filename_build_factory} ]; then
>>                  echo "Warning: The factory image file name
>> (${filename_build_factory}) is longer than the OpenWrt one, which might lead
>> to incompatibility with the stock firmware."
>>                fi
>> diff --git a/src/packages/fff/fff-boardname/files/etc/uci-defaults/50-fff-
>> boardname b/src/packages/fff/fff-boardname/files/etc/uci-defaults/50-fff-
>> boardname
>> index ee9c3d3..75f8ee8 100644
>> --- a/src/packages/fff/fff-boardname/files/etc/uci-defaults/50-fff-boardname
>> +++ b/src/packages/fff/fff-boardname/files/etc/uci-defaults/50-fff-boardname
>> @@ -70,6 +70,9 @@ case "$BOARD" in
>>      archer-c7)
>>          BOARD=archer-c7-v2
>>          ;;
>> +    avm,fritzbox-4040)
>> +        BOARD=avm_fritzbox-4040
>> +        ;;
>>  esac
>>
>>  uci set board.model.name=$BOARD
>> diff --git a/src/packages/fff/fff-network/ipq40xx/network.avm_fritzbox-4040
>> b/src/packages/fff/fff-network/ipq40xx/network.avm_fritzbox-4040
>> new file mode 100644
>> index 0000000..80ec186
>> --- /dev/null
>> +++ b/src/packages/fff/fff-network/ipq40xx/network.avm_fritzbox-4040
>> @@ -0,0 +1,8 @@
>> +WANDEV=eth0
>> +SWITCHDEV=eth0
>> +CLIENT_PORTS="3 4 0t"
>> +WAN_PORTS="5 0t"
>> +BATMAN_PORTS="1 2 0t"
>> +
>> +ROUTERMAC=$(cat /sys/class/net/eth0/address)
>> +
>> diff --git a/src/packages/fff/fff-sysupgrade/files/etc/sysupgrade.sh
>> b/src/packages/fff/fff-sysupgrade/files/etc/sysupgrade.sh
>> index 87ac48a..6241514 100755
>> --- a/src/packages/fff/fff-sysupgrade/files/etc/sysupgrade.sh
>> +++ b/src/packages/fff/fff-sysupgrade/files/etc/sysupgrade.sh
>> @@ -19,6 +19,9 @@ case $BOARD in
>>      tl-wdr4900-v1 )
>>          SOC="mpc85xx-g"
>>          ;;
>> +    avm_fritzbox-4040 )
>> +        SOC="ipq40xx"
>> +        ;;
>>      * )
>>          SOC="ar71xx-t"
>>          ;;
>> --
>> 2.11.0

-------------- nächster Teil --------------
Ein Dateianhang mit Binärdaten wurde abgetrennt...
Dateiname   : signature.asc
Dateityp    : application/pgp-signature
Dateigröße  : 833 bytes
Beschreibung: OpenPGP digital signature
URL         : <http://lists.freifunk.net/pipermail/franken-dev-freifunk.net/attachments/20181128/6ec8d3f6/attachment.sig>


Mehr Informationen über die Mailingliste franken-dev