diff mbox

[LEDE-DEV,2/2] ipq806x: Add support for ipq40xx subtarget

Message ID 1492096230-20720-1-git-send-email-rjangir@codeaurora.org
State Rejected
Headers show

Commit Message

Ram Chandra Jangir April 13, 2017, 3:10 p.m. UTC
This change adds QCA IPQ40xx SoC based board support

Supported IPQ40xx boards:
 - AP-DK01.1-C1 and AP-DK04.1-C1 with nor flash boot.

Network Test:
 - Tested IPv4 and IPv6 connectivity
 - Router can ping device through WAN interface
 - Device on LAN can ping through router.
 - WiFi connectivity AP as well as STA

Signed-off-by: Ram Chandra Jangir <rjangir@codeaurora.org>
---
 target/linux/ipq806x/Makefile                      |    4 +-
 .../ipq806x/base-files/etc/board.d/02_network      |    6 +
 .../etc/hotplug.d/firmware/11-ath10k-caldata       |    8 +
 target/linux/ipq806x/base-files/lib/ipq806x.sh     |    6 +
 target/linux/ipq806x/config-4.9                    |   10 +-
 target/linux/ipq806x/image/Makefile                |   30 +-
 target/linux/ipq806x/ipq40xx/config-default        |    5 +
 target/linux/ipq806x/ipq40xx/target.mk             |    7 +
 target/linux/ipq806x/modules.mk                    |   32 +
 ...-Add-support-for-spi-nor-32-MB-flash-and-.patch |  111 ++
 ...9-pinctrl-Updated-various-Pin-definitions.patch | 1332 ++++++++++++++
 ...m-Fix-edma-driver-for-GMAC-avaiable-on-IP.patch | 1937 ++++++++++++++++++++
 .../854-net-phy-Export-phy-statemachine-API.patch  |   36 +
 .../855-clk-qcom-ipq4019-add-ess-reset.patch       |   58 +
 ...-Fix-mdio-driver-to-work-with-IPQ40xx-SoC.patch |  100 +
 ...arm-dts-Add-ess-switch-device-for-IPQ4019.patch |  486 +++++
 target/linux/ipq806x/profiles/00-default.mk        |    3 +-
 17 files changed, 4161 insertions(+), 10 deletions(-)
 create mode 100644 target/linux/ipq806x/ipq40xx/config-default
 create mode 100644 target/linux/ipq806x/ipq40xx/target.mk
 create mode 100644 target/linux/ipq806x/patches-4.9/851-dts-ipq40xx-Add-support-for-spi-nor-32-MB-flash-and-.patch
 create mode 100644 target/linux/ipq806x/patches-4.9/852-ipq4019-pinctrl-Updated-various-Pin-definitions.patch
 create mode 100644 target/linux/ipq806x/patches-4.9/853-net-qualcomm-Fix-edma-driver-for-GMAC-avaiable-on-IP.patch
 create mode 100644 target/linux/ipq806x/patches-4.9/854-net-phy-Export-phy-statemachine-API.patch
 create mode 100644 target/linux/ipq806x/patches-4.9/855-clk-qcom-ipq4019-add-ess-reset.patch
 create mode 100644 target/linux/ipq806x/patches-4.9/857-ipq40xx-Fix-mdio-driver-to-work-with-IPQ40xx-SoC.patch
 create mode 100644 target/linux/ipq806x/patches-4.9/858-arm-dts-Add-ess-switch-device-for-IPQ4019.patch

Comments

Felix Fietkau April 14, 2017, 9:28 a.m. UTC | #1
On 2017-04-13 17:10, Ram Chandra Jangir wrote:
> This change adds QCA IPQ40xx SoC based board support
> 
> Supported IPQ40xx boards:
>  - AP-DK01.1-C1 and AP-DK04.1-C1 with nor flash boot.
> 
> Network Test:
>  - Tested IPv4 and IPv6 connectivity
>  - Router can ping device through WAN interface
>  - Device on LAN can ping through router.
>  - WiFi connectivity AP as well as STA
> 
> Signed-off-by: Ram Chandra Jangir <rjangir@codeaurora.org>
What's the reason for making it a separate subtarget?
I think it would be better to be able to run ipq806x and ipq40xx from
the same build. It also saves resources for our snapshot builds.

- Felix
Michael Yartys via Lede-dev April 14, 2017, 9:35 p.m. UTC | #2
The sender domain has a DMARC Reject/Quarantine policy which disallows
sending mailing list messages using the original "From" header.

To mitigate this problem, the original message has been wrapped
automatically by the mailing list software.
On Thursday, April 13, 2017 8:40:30 PM CEST Ram Chandra Jangir wrote:
> This change adds QCA IPQ40xx SoC based board support
> 
> Supported IPQ40xx boards:
>  - AP-DK01.1-C1 and AP-DK04.1-C1 with nor flash boot.
This is Great. There are several users which are waiting for DK04.

On is the Compex WPJ428. There are several remaining issues with it:
- The USB 3.0 does not work on his board and is downgraded to USB 2.0.
It's working on the FB4040 and RT-AC58U though.

This is what the user sees on the WPJ428:
[   30.614478] usb 1-1: new high-speed USB device number 3 using 
xhci-hcd
[   31.356496] xhci-hcd xhci-hcd.0.auto: Cannot set link state.
[   31.357151] usb usb2-port1: cannot disable (err = -32)

Can you please check the usb port functionality on your board too?

- the board fails to reboot / restart.
The WPJ428 LEDE-Image will just freeze. The FB4040 and RT-AC58U doesn't
have no issue though and neither does the Stock Image. Can you please
check, if this is working with your image?

> ---
> diff --git a/target/linux/ipq806x/Makefile b/target/linux/ipq806x/Makefile
> index 5a5551c..b5b36e1 100644
> --- a/target/linux/ipq806x/Makefile
> +++ b/target/linux/ipq806x/Makefile
> @@ -21,7 +21,7 @@ DEFAULT_PACKAGES += \
>  	kmod-ata-core kmod-ata-ahci kmod-ata-ahci-platform \
>  	kmod-usb-core kmod-usb-ohci kmod-usb2 kmod-usb-ledtrig-usbport \
>  	kmod-usb3 kmod-usb-dwc3-of-simple kmod-usb-phy-qcom-dwc3 \
> -	kmod-ath10k wpad-mini \
> +	kmod-ath10k wpad-mini kmod-switch-ar40xx kmod-ipq40xx-edma \
the switch and edma driver are already part of the default image.
Why do you want to have them as separate packages?

> diff --git a/target/linux/ipq806x/base-files/etc/board.d/02_network b/target/linux/ipq806x/base-files/etc/board.d/02_network
> index 36e0fb3..082105a 100755
> --- a/target/linux/ipq806x/base-files/etc/board.d/02_network
> +++ b/target/linux/ipq806x/base-files/etc/board.d/02_network
> @@ -48,6 +48,12 @@ nbg6817)
>  	ucidef_set_interface_macaddr "lan" "$hw_mac_addr"
>  	ucidef_set_interface_macaddr "wan" "$(macaddr_add $hw_mac_addr 1)"
>  	;;
> +ap-dk01.1-c1|\
> +ap-dk04.1-c1)
> +	ucidef_set_interfaces_lan_wan "eth1" "eth0"
> +	ucidef_add_switch "switch0" \
> +		"0t@eth1" "1:lan" "2:lan" "3:lan" "4:lan"
> +	;;

For the record: eth1 IS NOT a separate MAC. From what I can tell, the
essedma driver just maps different VLANs to multiple ethX instances. 
So both eth0 and eth1 share the same PSGMII link to the QCA8075. 
Therefore I suggest to let the kernel handle the VLAN and switch to:

        ucidef_add_switch "switch0" \
                "0@eth0" "1:lan" "2:lan" "3:lan" "4:lan" "5:wan"

		(the ucidef_set_interfaces_lan_wan gets removed)

This will create eth0.1 and eth0.2 instead of eth0 and eth1.
I've already tested this and made a patch:
<https://github.com/chunkeey/LEDE-IPQ40XX/commit/f4c1345f73bb63d7c5b9acc7e766c18b071c7885>
[@John, I think this should fix the weird VLAN issues you had with your box]
(I'm waiting for AP devices with a AR8036 to see how they behave here)
> diff --git a/target/linux/ipq806x/patches-4.9/851-dts-ipq40xx-Add-support-for-spi-nor-32-MB-flash-and-.patch b/target/linux/ipq806x/patches-4.9/851-dts-ipq40xx-Add-support-for-spi-nor-32-MB-flash-and-.patch
> new file mode 100644
> index 0000000..5753f10
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.9/851-dts-ipq40xx-Add-support-for-spi-nor-32-MB-flash-and-.patch
> @@ -0,0 +1,111 @@
> +From 5f07811771ad92a3413248a240eaedfee09ace93 Mon Sep 17 00:00:00 2001
> +From: Ram Chandra Jangir <rjangir@codeaurora.org>
> +Date: Fri, 24 Mar 2017 14:00:00 +0530
> +Subject: [PATCH] dts: ipq40xx: Add support for spi nor 32 MB flash and enable
> + wifi support
> +
> +- Add micron n25q128a11 spi nor 32MB flash and fixes spi nodes
> +  to work on DK01 and DK04 boards.
> +- Add support for default SPI configuration
> +- Enable and fixed WiFi nodes to work with DK boards
> +
> +Signed-off-by: Ram Chandra Jangir <rjangir@codeaurora.org>
> +---
> +[...]
> +diff --git a/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi b/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi
> +index 768a2a4..2a5cc5e 100644
> +--- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi
> ++++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi
> +@@ -81,13 +81,16 @@
> +			pinctrl-names = "default";
> +			status = "ok";
> +			cs-gpios = <&tlmm 54 0>;
> ++			num-cs = <1>;
> +
> +-			mx25l25635e@0 {
> ++			m25p80@0 {
There are already boards that use the mx25l25635e property.
Please, just add a separate property for the generic "m25p80" and set
its status to disabled (this is a dtsi, which will probably be used 
by most other devices!).

> +				#address-cells = <1>;
> +				#size-cells = <1>;
> +				reg = <0>;
> +-				compatible = "mx25l25635e";
> ++				compatible = "n25q128a11";
we should really add "jedec,spi-nor" at the end.
See: <http://lxr.free-electrons.com/source/Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt>
"Must also include "jedec,spi-nor" for any SPI NOR flash that can
 be identified by the JEDEC READ ID opcode (0x9F)."

> ++				linux,modalias = "m25p80", "n25q128a11";
> +				spi-max-frequency = <24000000>;
> ++				use-default-sizes;
> +			};
> +		};
> +
> +diff --git a/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi b/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi
> +index e817432..b68fc1a 100644
> +--- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi
> ++++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi
> +@@ -94,18 +94,20 @@
> +			status = "ok";
> +		};
> +
> +-		spi_0: spi@78b5000 {
> ++		spi_0: spi@78b5000 { /* BLSP1 QUP1 */
> +			pinctrl-0 = <&spi_0_pins>;
> +			pinctrl-names = "default";
> +			status = "ok";
> +			cs-gpios = <&tlmm 12 0>;
> +
> +-			mx25l25635e@0 {
> ++			m25p80@0 {
Same as above. Please make a separate m25p80@0 node there. If you want
to have it in the .dtsi. Alternatively we could move the nor/spinand
chip definitions into a .dts and just keep the spi-driver there.
This is probably the saner way to do it.

> +				#address-cells = <1>;
> +				#size-cells = <1>;
> +				reg = <0>;
> +-				compatible = "mx25l25635e";
> ++				compatible = "n25q128a11";
add "jedec,spi-nor"; (Yes, I'll need to fix my stuff as well).

> ++				linux,modalias = "m25p80", "n25q128a11";
> +				spi-max-frequency = <24000000>;
> ++				use-default-sizes;
> +			};
> +		};
> +
> +diff --git a/arch/arm/boot/dts/qcom-ipq4019.dtsi b/arch/arm/boot/dts/qcom-ipq4019.dtsi
> +index 7013c85..be5b6f7 100644
> +--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
> ++++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
> +@@ -506,6 +506,7 @@
> +		wifi0: wifi@a000000 {
> +			compatible = "qcom,ipq4019-wifi";
> +			reg = <0xa000000 0x200000>;
> ++			core-id = <0x0>;
Where does this property come from? It's not part of the ath10k binding:
<http://lxr.free-electrons.com/source/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.txt>
And I can't find a of_ parser for it in the ath10k driver.

> +@@ -542,12 +543,15 @@
> +					   "msi8",  "msi9", "msi10", "msi11",
> +					  "msi12", "msi13", "msi14", "msi15",
> +					  "legacy";
> +-			status = "disabled";
> ++			status = "ok";
> ++			qca,msi_addr = <0x0b006040>;
> ++			qca,msi_base = <0x40>;
Yes, I hope that the ath10k ahb driver will support this one day.

[...]
> diff --git a/target/linux/ipq806x/patches-4.9/852-ipq4019-pinctrl-Updated-various-Pin-definitions.patch b/target/linux/ipq806x/patches-4.9/852-ipq4019-pinctrl-Updated-various-Pin-definitions.patch
> new file mode 100644
> index 0000000..4267d47
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.9/852-ipq4019-pinctrl-Updated-various-Pin-definitions.patch
> @@ -0,0 +1,1332 @@
> +From fc6cf61517b8b4ab4678659936fc7572f699d6e7 Mon Sep 17 00:00:00 2001
> +From: Ram Chandra Jangir <rjangir@codeaurora.org>
> +Date: Tue, 28 Mar 2017 14:00:00 +0530
> +Subject: [PATCH] ipq4019: pinctrl: Updated various Pin definitions
> +
> +Populate default values for various GPIO functions
> +
> +Signed-off-by: Ram Chandra Jangir <rjangir@codeaurora.org>
Please send this upstream to linux-gpio@vger.kernel.org.
I'm saying this because it has become nearly impossible to
merge this stuff into the kernel without the right domain
in the email-address.
[...]

> diff --git a/target/linux/ipq806x/patches-4.9/854-net-phy-Export-phy-statemachine-API.patch b/target/linux/ipq806x/patches-4.9/854-net-phy-Export-phy-statemachine-API.patch
> new file mode 100644
> index 0000000..2bb213d
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.9/854-net-phy-Export-phy-statemachine-API.patch
> @@ -0,0 +1,36 @@
> +From 69f50b2694e9e9a3e927c21b900112d7adb581c9 Mon Sep 17 00:00:00 2001
> +From: Ram Chandra Jangir <rjangir@codeaurora.org>
> +Date: Tue, 28 Mar 2017 14:00:00 +0530
> +Subject: [PATCH 2/5] net/phy: Export phy statemachine API
> +
> +When any port with link-detection enabled changes group
> +we need to start/stop phy detection with phy_start_machine()
> +and phy_stop_machine() at runtime for those specific ports
> +
> +Signed-off-by :Ram Chandra Jangir <rjangir@codeaurora.org>
> +---
> + drivers/net/phy/phy.c | 2 ++
> + 1 file changed, 2 insertions(+)
> +
> +diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
> +index 00fe2ab..4058d5c 100644
> +--- a/drivers/net/phy/phy.c
> ++++ b/drivers/net/phy/phy.c
> +@@ -650,6 +650,7 @@ void phy_start_machine(struct phy_device *phydev)
> + {
> +	queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, HZ);
> + }
> ++EXPORT_SYMBOL(phy_start_machine);
> +
> + /**
> +  * phy_trigger_machine - trigger the state machine to run
> +@@ -687,6 +688,7 @@ void phy_stop_machine(struct phy_device *phydev)
> +		phydev->state = PHY_UP;
> +	mutex_unlock(&phydev->lock);
> + }
> ++EXPORT_SYMBOL(phy_stop_machine);
Hm, accessing the phy code internals is bound to break.
It would be much better to remove the VLAN code from the
essedma and ar40xx phy code and just let the kernel deal
with it.

> diff --git a/target/linux/ipq806x/patches-4.9/855-clk-qcom-ipq4019-add-ess-reset.patch b/target/linux/ipq806x/patches-4.9/855-clk-qcom-ipq4019-add-ess-reset.patch
> new file mode 100644
> index 0000000..d33b8ab
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.9/855-clk-qcom-ipq4019-add-ess-reset.patch
> @@ -0,0 +1,58 @@
> +From 7efb48a343ca368f83359d3a7087dd5ab25a283a Mon Sep 17 00:00:00 2001
> +From: Ram Chandra Jangir <rjangir@codeaurora.org>
> +Date: Tue, 28 Mar 2017 22:35:33 +0530
> +Subject: [PATCH 5/8] clk: qcom: ipq4019: add ess reset
> +
> +Added the ESS reset in IPQ4019 GCC.
> +
> +Signed-off-by: Ram Chandra Jangir <rjangir@codeaurora.org>
> +---
Please send this upstream.

> diff --git a/target/linux/ipq806x/patches-4.9/857-ipq40xx-Fix-mdio-driver-to-work-with-IPQ40xx-SoC.patch b/target/linux/ipq806x/patches-4.9/857-ipq40xx-Fix-mdio-driver-to-work-with-IPQ40xx-SoC.patch
> new file mode 100644
> index 0000000..bb72169
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.9/857-ipq40xx-Fix-mdio-driver-to-work-with-IPQ40xx-SoC.patch
> @@ -0,0 +1,100 @@
> +From 1d8433be4af4a5862b8805a5668d404bd6fde945 Mon Sep 17 00:00:00 2001
> +From: Ram Chandra Jangir <rjangir@codeaurora.org>
> +Date: Tue, 28 Mar 2017 22:32:07 +0530
> +Subject: [PATCH] ipq40xx: Fix mdio driver to work with IPQ40xx SoC
> +
> +- Add phy-reset-gpio support in probe function
> +- Add proper assignment of mii_bus read/write operations
> +
> +Signed-off-by: Ram Chandra Jangir <rjangir@codeaurora.org>
> +---
> +diff --git a/drivers/net/phy/mdio-ipq40xx.c b/drivers/net/phy/mdio-ipq40xx.c
> +index 335d531..e969f06 100644
> +--- a/drivers/net/phy/mdio-ipq40xx.c
> ++++ b/drivers/net/phy/mdio-ipq40xx.c
> +@@ -22,6 +22,7 @@
> + #include <linux/of_mdio.h>
> + #include <linux/phy.h>
> + #include <linux/platform_device.h>
> ++#include <linux/of_gpio.h>
> +
> + #define MDIO_CTRL_0_REG		0x40
> + #define MDIO_CTRL_1_REG		0x44
> +@@ -122,11 +123,60 @@ static int ipq40xx_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
> +	return 0;
> + }
> +
> ++static int ipq40xx_phy_reset(struct platform_device *pdev)
> ++{
> ++	struct device_node *mdio_node;
> ++	int phy_reset_gpio_number;
> ++	int ret;
> ++
> ++	mdio_node = of_find_node_by_name(NULL, "mdio");
> ++	if (!mdio_node) {
> ++		dev_err(&pdev->dev, "Could not find mdio node\n");
> ++		return -ENOENT;
> ++	}
> ++
> ++	ret = of_get_named_gpio(mdio_node, "phy-reset-gpio", 0);
> ++	if (ret < 0) {
> ++		dev_err(&pdev->dev, "Could not find phy-reset-gpio\n");
> ++		return ret;
This could break existing configurations. Please add a check that
would skip the error in case the "phy-reset-gpio" property isn't
available.
[...]
> + static int ipq40xx_mdio_probe(struct platform_device *pdev)
> + {
> +	struct ipq40xx_mdio_data *am;
> +	struct resource *res;
> +-	int i;
> ++	int i, ret;
> ++
> ++	ret = ipq40xx_phy_reset(pdev);
> ++	if (ret) {
> ++		dev_err(&pdev->dev, "Could not find qca8075 reset gpio\n");
> ++		return ret;
> ++	}
> diff --git a/target/linux/ipq806x/patches-4.9/858-arm-dts-Add-ess-switch-device-for-IPQ4019.patch b/target/linux/ipq806x/patches-4.9/858-arm-dts-Add-ess-switch-device-for-IPQ4019.patch
> new file mode 100644
> index 0000000..4598451
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.9/858-arm-dts-Add-ess-switch-device-for-IPQ4019.patch
> @@ -0,0 +1,486 @@
> +From 4842020af3b39ce8c7c9a92de106d8fffd92b7c0 Mon Sep 17 00:00:00 2001
> +From: Ram Chandra Jangir <rjangir@codeaurora.org>
> +Date: Tue, 28 Mar 2017 14:00:00 +0530
> +Subject: [PATCH] arm: dts: Add ess switch device for IPQ4019
> +
> +- Update ipq4019 dts nodes for mdio interface, ess-switch
> +   and edma driver.
> +- Update dt documentation for qca-ess ethernet subsystem
> +
> +Signed-off-by: xiaofeis <xiaofeis@codeaurora.org>
> +Signed-off-by: Ram Chandra Jangir <rjangir@codeaurora.org>
> +---
> +diff --git a/Documentation/devicetree/bindings/net/qca-ess.txt b/Documentation/devicetree/bindings/net/qca-ess.txt
> +new file mode 100644
> +index 0000000..c192774
> +--- /dev/null
> ++++ b/Documentation/devicetree/bindings/net/qca-ess.txt
> +@@ -0,0 +1,107 @@
> ++QCA Ethernet Subsystem
> ++----------------------
> ++
> ++This driver adds support for the Ethernet subsystem
> ++
> ++1. QCA Ethernet DMA
> ++----------------------
> ++
> ++Required properties:
> ++  - compatible = "qcom,ess-edma";
> ++
> ++Optional properties:
> ++
> ++Example:
> ++    edma@c080000 {
> ++	compatible = "qcom,ess-edma";
> ++	reg = <0xc080000 0x8000>;
> ++	qcom,page-mode = <0>;
> ++	qcom,rx_head_buf_size = <1540>;
rx-head-buf-size?
> ++	qcom,port_id_wan = <0x5>;
port_id_wan? port-id-wan ?
That said, edma's custom VLAN code stuff should go...
> ++	interrupts = <0 65 1>,
> ++		   <0 66 1>,
> ++		   <0 67 1>,
[...]
> ++   };
> ++
> ++2. QCA Ethernet Switch
> ++----------------------
> ++
> ++Required properties:
> ++  - compatible = "qcom,ess-switch";
> ++
> ++Optional properties:
> ++
> ++Example:
> ++
> ++	ess-switch@c000000 {
> ++		compatible = "qcom,ess-switch";
> ++		reg = <0xc000000 0x80000>; /* 512KB */
> ++		switch_access_mode = "local bus";
switch-access-mode?
> ++		resets = <&gcc ESS_RESET>;
> ++		switch_cpu_bmp = <0x1>;  /* cpu port bitmap */
> ++		switch_lan_bmp = <0x1e>; /* lan port bitmap */
> ++		switch_wan_bmp = <0x20>; /* wan port bitmap */
switch_cpu_bmp, switch_lan_bmp, switch_wan_bmp can be removed.
Userspace should take care of setting up the VLAN.

> ++	};
> ++
> ++3. QCA Ethernet PHY mode
> ++-------------------------
> ++
> ++Required properties:
> ++  - compatible = "qcom,ess-psgmii";
> ++
> ++Optional properties:
> ++
> ++Example:
> ++
> ++	ess-psgmii@98000 {
> ++		compatible = "qcom,ess-psgmii";
> ++		reg = <0x98000 0x800>; /* 2k */
> ++		psgmii_access_mode = "local bus";
psgmii-access-mode?
> ++	};
> ++
> ++4. MDIO Interface
> ++----------------------
> ++
> ++Required properties:
> ++  - compatible = "qcom,ipq4019-mdio";
> ++
> ++Optional properties:
> ++
> ++Example:
> ++
> ++	mdio@90000 {
> ++		#address-cells = <1>;
> ++		#size-cells = <1>;
size-cells = <0>;  

> ++		compatible = "qcom,ipq4019-mdio";
> ++		reg = <0x90000 0x64>;
> ++	};
> ++
> +diff --git a/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1-c1.dts b/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1-c1.dts
> +index 0d92f1b..ec75396 100644
> +--- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1-c1.dts
> ++++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1-c1.dts
> +@@ -18,5 +18,13 @@
> +
> + / {
> +	model = "Qualcomm Technologies, Inc. IPQ40xx/AP-DK01.1-C1";
> +-
> ++		soc {
> ++			mdio@90000 {
> ++			status = "ok";
> ++			pinctrl-0 = <&mdio_pins>;
> ++			pinctrl-names = "default";
> ++			bias-disable;
> ++			phy-reset-gpio = <&tlmm 59 0>;
> ++		};
> ++        };
This looks like a whitespace issue in the patch?

> +diff --git a/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi b/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi
> +index b68fc1a..1aee3b1 100644
> +--- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi
> ++++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi
> +@@ -161,5 +182,56 @@
> +		watchdog@b017000 {
> +			status = "ok";
> +		};
> ++
> ++		ess-switch@c000000 {
> ++			status = "ok";
> ++			switch_mac_mode = <0x0>; /* mac mode for RGMII RMII */
> ++			switch_initvlas = <
> ++				0x0007c 0x54 /* PORT0_STATUS */
> ++			>;
> ++			led_source@0 {
> ++				led = <0x3>;     /*led number */
> ++				source = <0x1>;  /*source id 1 */
> ++				mode = "normal"; /*on,off,blink,normal */
> ++				speed = "all";   /*10M,100M,1000M,all */
> ++				freq = "auto";   /*2Hz,4Hz,8Hz,auto*/
> ++			};
[...]
Just a heads up: I had to add a "gpio-controller" to the ar40xx driver.
This was necessary, because the AVM FritzBox 4040 uses one of the switch
LEDs to enable the power of both USB ports.
(Off-topic: Yes, the usb power will "blink" for a few seconds during
boot :-D ) 

> +	};
> + };

> +diff --git a/arch/arm/boot/dts/qcom-ipq4019.dtsi b/arch/arm/boot/dts/qcom-ipq4019.dtsi
> +index 7013c85..9793611 100644
> +--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
> ++++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
> +@@ -26,8 +26,8 @@
> +	aliases {
> +		spi0 = &spi_0;
> +		i2c0 = &i2c_0;
> +-		ethernet0 = &gmac0;
> +-		ethernet1 = &gmac1;
> ++		ethernet0 = "/soc/edma/gmac0";
> ++		ethernet1 = "/soc/edma/gmac1";
> +	};
Any reason why you deleted the labels? The FB4040 will be doing 
a lookup based on the labels, so I hate to see them go.

> +	cpus {
> +@@ -325,43 +325,47 @@
> +
> +		mdio@90000 {
> +			#address-cells = <1>;
> +-			#size-cells = <0>;
> ++			#size-cells = <1>;
#size-cells = <0>;
you don't have any size cells in the subnodes.
> +			compatible = "qcom,ipq4019-mdio";
> +			reg = <0x90000 0x64>;
> +			status = "disabled";
> +
> +-			ethernet-phy@0 {
> ++			phy0: ethernet-phy@0 {
> +				reg = <0>;
There's only the address parameter. no size.
> +			};
[...]
> +@@ -378,57 +384,58 @@
> +			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 255 IRQ_TYPE_EDGE_RISING>;
> ++			qcom,rx-head-buf-size = <1540>;
> ++			qcom,num-gmac = <2>;
Technically, the essedma has only one. See the VLAN
comment above.

> ++			qcom,mdio-supported;
> ++			interrupts = <0  65 IRQ_TYPE_EDGE_RISING>,
> ++				      <0  66 IRQ_TYPE_EDGE_RISING>,
[...]
> ++				      <0 255 IRQ_TYPE_EDGE_RISING>;
> +
> +			status = "disabled";
> +
> +-			gmac0: gmac0 {
> ++			gmac0 {
please keep the label.
> +				local-mac-address = [00 00 00 00 00 00];
> +-				vlan_tag = <1 0x1f>;
> ++				qcom,phy-mdio-addr = <4>;
> ++				qcom,poll-required = <1>;
> ++				qcom,poll-required-dynamic = <1>;
> ++				qcom,forced-speed = <1000>;
> ++				qcom,forced-duplex = <1>;
> ++				vlan-tag = <2 0x20>;
Ideally, fixed-link should be used instead of custom "force-speed".
The custom VLAN stuff ( phy-mdio-addr, vlan_tag) should be removed.
The userspace will deal with setting up the switch.
[...]
> +--
> +2.7.2
> diff --git a/target/linux/ipq806x/profiles/00-default.mk b/target/linux/ipq806x/profiles/00-default.mk
> index 9baa24f..c4b58a2 100644
> --- a/target/linux/ipq806x/profiles/00-default.mk
> +++ b/target/linux/ipq806x/profiles/00-default.mk
> @@ -1,7 +1,8 @@
>  define Profile/Default
>    NAME:=Default Profile
>    PRIORITY:=1
> -  PACKAGES:=ath10k-firmware-qca99x0 ath10k-firmware-qca988x ath10k-firmware-qca9984
> +  PACKAGES:=ath10k-firmware-qca99x0 ath10k-firmware-qca988x ath10k-firmware-qca9984 \
> +	    ath10k-firmware-qca4019

Currently, ath10k-firmware-qca4019 gets added automatically by the ipq-wifi 
board package: 
<https://github.com/lede-project/source/blob/master/package/firmware/ipq-wifi/Makefile>

As this is supposed to be the reference board. the data should be part of
ath10k-firmware's board-2.bin. However, other platforms aren't that lucky.

This issue has been reported to ath10k-devel and linux-wireless:
<https://www.mail-archive.com/ath10k@lists.infradead.org/msg06154.html>

And it would be nice to have a official solution for this.

Best Regards,
Christian
Ram Chandra Jangir April 17, 2017, 11:26 a.m. UTC | #3
Hi Felix,

> What's the reason for making it a separate subtarget?

We would like to add ipq40xx as subtarget for following reasons.
1. ipq40xx is different SoC than existing ipq806x
2. Would like to have SoC specific images, so that kernel configs can be tweaked per SoC
3. SoC specific images will be smaller in size & faster boot-time
4. Newer SoC may have exclusive FWs which needs compile time differentiation.
5. There is new SoC(ipq807x) under development, which is based on ARMv8. Hence we will need subtarget approach to accommodate new SoC.

Thanks,
Ram

-----Original Message-----
From: Felix Fietkau [mailto:nbd@nbd.name] 
Sent: Friday, April 14, 2017 2:59 PM
To: Ram Chandra Jangir <rjangir@codeaurora.org>; lede-dev@lists.infradead.org
Subject: Re: [LEDE-DEV] [PATCH 2/2] ipq806x: Add support for ipq40xx subtarget

On 2017-04-13 17:10, Ram Chandra Jangir wrote:
> This change adds QCA IPQ40xx SoC based board support
> 
> Supported IPQ40xx boards:
>  - AP-DK01.1-C1 and AP-DK04.1-C1 with nor flash boot.
> 
> Network Test:
>  - Tested IPv4 and IPv6 connectivity
>  - Router can ping device through WAN interface
>  - Device on LAN can ping through router.
>  - WiFi connectivity AP as well as STA
> 
> Signed-off-by: Ram Chandra Jangir <rjangir@codeaurora.org>
What's the reason for making it a separate subtarget?
I think it would be better to be able to run ipq806x and ipq40xx from the same build. It also saves resources for our snapshot builds.

- Felix
Felix Fietkau April 17, 2017, 12:54 p.m. UTC | #4
On 2017-04-17 13:26, Ram Chandra Jangir wrote:
> Hi Felix,
> 
>> What's the reason for making it a separate subtarget?
> 
> We would like to add ipq40xx as subtarget for following reasons.
> 1. ipq40xx is different SoC than existing ipq806x
> 2. Would like to have SoC specific images, so that kernel configs can be tweaked per SoC
> 3. SoC specific images will be smaller in size & faster boot-time
While some drivers are indeed SoC specific, I think ipq40xx and ipq806x
do have a lot of overlap. I don't think that images will be much smaller
with SoC specific subtargets, at least not enough to make any
*practical* difference.

> 4. Newer SoC may have exclusive FWs which needs compile time differentiation.
Firmware should be packaged anyway, and can thus be handled via device
profiles.

> 5. There is new SoC(ipq807x) under development, which is based on
> ARMv8. Hence we will need subtarget approach to accommodate new SoC.
I agree that there should be a separate subtarget for ipq807x, but I
don't think it makes sense for ipq40xx.

Please keep in mind that each extra build target has a visible cost in
buildbot and download server resources, so I would really like to avoid
adding more except where there are strong reasons to do so.

- Felix
Ram Chandra Jangir April 17, 2017, 5:43 p.m. UTC | #5
Hi Christian,

Thanks for the review and time. Please see my inline comments. I will update
these in  new version of this patch .

>This is what the user sees on the WPJ428:
> [   30.614478] usb 1-1: new high-speed USB device number 3 using 
>xhci-hcd
> [   31.356496] xhci-hcd xhci-hcd.0.auto: Cannot set link state.
> [   31.357151] usb usb2-port1: cannot disable (err = -32)

>Can you please check the usb port functionality on your board too?

>- the board fails to reboot / restart.
>The WPJ428 LEDE-Image will just freeze. The FB4040 and RT-AC58U doesn't
have no issue though and neither does the Stock Image. Can you please check,
if this is working with your image?
[Ram]: I faced lots of network issues/panics and fixed them. Basic board
boot up is fine with usb and checked only basic USB 3.0  functionality. I
have not done thoroughly testing with usb. That's why I did not mention usb
in commit message except network. Will take it on next.
              
> ---
> diff --git a/target/linux/ipq806x/Makefile 
> b/target/linux/ipq806x/Makefile index 5a5551c..b5b36e1 100644
> --- a/target/linux/ipq806x/Makefile
> +++ b/target/linux/ipq806x/Makefile
> @@ -21,7 +21,7 @@ DEFAULT_PACKAGES += \
>  	kmod-ata-core kmod-ata-ahci kmod-ata-ahci-platform \
>  	kmod-usb-core kmod-usb-ohci kmod-usb2 kmod-usb-ledtrig-usbport \
>  	kmod-usb3 kmod-usb-dwc3-of-simple kmod-usb-phy-qcom-dwc3 \
> -	kmod-ath10k wpad-mini \
> +	kmod-ath10k wpad-mini kmod-switch-ar40xx kmod-ipq40xx-edma \

>>the switch and edma driver are already part of the default image.
>>Why do you want to have them as separate packages?
[Ram]: It is good to have enabled them as module instead of inbuilt,
Theoretically ar40xx switch depends on swconfig module which is enabled as
kmod only.

> diff --git a/target/linux/ipq806x/base-files/etc/board.d/02_network 
> b/target/linux/ipq806x/base-files/etc/board.d/02_network
> index 36e0fb3..082105a 100755
> --- a/target/linux/ipq806x/base-files/etc/board.d/02_network
> +++ b/target/linux/ipq806x/base-files/etc/board.d/02_network
> @@ -48,6 +48,12 @@ nbg6817)
>  	ucidef_set_interface_macaddr "lan" "$hw_mac_addr"
>  	ucidef_set_interface_macaddr "wan" "$(macaddr_add $hw_mac_addr 1)"
>  	;;
> +ap-dk01.1-c1|\
> +ap-dk04.1-c1)
> +	ucidef_set_interfaces_lan_wan "eth1" "eth0"
> +	ucidef_add_switch "switch0" \
> +		"0t@eth1" "1:lan" "2:lan" "3:lan" "4:lan"
> +	;;

>For the record: eth1 IS NOT a separate MAC. From what I can tell, the
essedma driver just maps different VLANs to multiple ethX instances. 
>So both eth0 and eth1 share the same PSGMII link to the QCA8075. 
>Therefore I suggest to let the kernel handle the VLAN and switch to:

  >     ucidef_add_switch "switch0" \
>                "0@eth0" "1:lan" "2:lan" "3:lan" "4:lan" "5:wan"
>
>		(the ucidef_set_interfaces_lan_wan gets removed)

>>This will create eth0.1 and eth0.2 instead of eth0 and eth1.

[Ram] : I added eth0 as WAN and eth1 along with eth1.1  as lan interfaces.  
               verified IPv4 and IPv6 connectivity, Router can ping device
through WAN interface
               Device on LAN can ping through router.  I will look your
patch after this.

>I've already tested this and made a patch:
<https://github.com/chunkeey/LEDE-IPQ40XX/commit/f4c1345f73bb63d7c5b9acc7e76
6c18b071c7885>
>[@John, I think this should fix the weird VLAN issues you had with your
box] (I'm waiting for AP devices with a AR8036 to see how they behave here)
> diff --git 
> a/target/linux/ipq806x/patches-4.9/851-dts-ipq40xx-Add-support-for-spi
> -nor-32-MB-flash-and-.patch 
> b/target/linux/ipq806x/patches-4.9/851-dts-ipq40xx-Add-support-for-spi
> -nor-32-MB-flash-and-.patch
> new file mode 100644
> index 0000000..5753f10
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.9/851-dts-ipq40xx-Add-support-for
> +++ -spi-nor-32-MB-flash-and-.patch
> @@ -0,0 +1,111 @@
> +From 5f07811771ad92a3413248a240eaedfee09ace93 Mon Sep 17 00:00:00 
> +2001
> +From: Ram Chandra Jangir <rjangir@codeaurora.org>
> +Date: Fri, 24 Mar 2017 14:00:00 +0530
> +Subject: [PATCH] dts: ipq40xx: Add support for spi nor 32 MB flash 
> +and enable  wifi support
> +
> +- Add micron n25q128a11 spi nor 32MB flash and fixes spi nodes
> +  to work on DK01 and DK04 boards.
> +- Add support for default SPI configuration
> +- Enable and fixed WiFi nodes to work with DK boards
> +
> +Signed-off-by: Ram Chandra Jangir <rjangir@codeaurora.org>
> +---
> +[...]
> +diff --git a/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi 
> +b/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi
> +index 768a2a4..2a5cc5e 100644
> +--- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi
> ++++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi
> +@@ -81,13 +81,16 @@
> +			pinctrl-names = "default";
> +			status = "ok";
> +			cs-gpios = <&tlmm 54 0>;
> ++			num-cs = <1>;
> +
> +-			mx25l25635e@0 {
> ++			m25p80@0 {
>>There are already boards that use the mx25l25635e property.
>>Please, just add a separate property for the generic "m25p80" and set its
status to disabled (this is a dtsi, which will probably be used by most
other devices!).
[Ram]: Will check/test the board which uses mx25l25635e flash and will
revise the patch if needed.

> +				#address-cells = <1>;
> +				#size-cells = <1>;
> +				reg = <0>;
> +-				compatible = "mx25l25635e";
> ++				compatible = "n25q128a11";
>we should really add "jedec,spi-nor" at the end.
>See:
<http://lxr.free-electrons.com/source/Documentation/devicetree/bindings/mtd/
jedec,spi-nor.txt>
>"Must also include "jedec,spi-nor" for any SPI NOR flash that can  be
identified by the JEDEC READ ID opcode (0x9F)."
[Ram]: yeah it can be added if possible.

> ++				linux,modalias = "m25p80", "n25q128a11";
> +				spi-max-frequency = <24000000>;
> ++				use-default-sizes;
> +			};
> +		};
> +
> +diff --git a/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi 
> +b/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi
> +index e817432..b68fc1a 100644
> +--- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi
> ++++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi
> +@@ -94,18 +94,20 @@
> +			status = "ok";
> +		};
> +
> +-		spi_0: spi@78b5000 {
> ++		spi_0: spi@78b5000 { /* BLSP1 QUP1 */
> +			pinctrl-0 = <&spi_0_pins>;
> +			pinctrl-names = "default";
> +			status = "ok";
> +			cs-gpios = <&tlmm 12 0>;
> +
> +-			mx25l25635e@0 {
> ++			m25p80@0 {
>Same as above. Please make a separate m25p80@0 node there. If you want to
have it in the .dtsi. Alternatively we could move the nor/spinand chip
definitions into a .dts and just keep the spi-driver there.
>This is probably the saner way to do it.
[Ram]: commented on top.

> +				#address-cells = <1>;
> +				#size-cells = <1>;
> +				reg = <0>;
> +-				compatible = "mx25l25635e";
> ++				compatible = "n25q128a11";
>add "jedec,spi-nor"; (Yes, I'll need to fix my stuff as well).

> ++				linux,modalias = "m25p80", "n25q128a11";
> +				spi-max-frequency = <24000000>;
> ++				use-default-sizes;
> +			};
> +		};
> +
> +diff --git a/arch/arm/boot/dts/qcom-ipq4019.dtsi 
> +b/arch/arm/boot/dts/qcom-ipq4019.dtsi
> +index 7013c85..be5b6f7 100644
> +--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
> ++++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
> +@@ -506,6 +506,7 @@
> +		wifi0: wifi@a000000 {
> +			compatible = "qcom,ipq4019-wifi";
> +			reg = <0xa000000 0x200000>;
> ++			core-id = <0x0>;
>Where does this property come from? It's not part of the ath10k binding:
<http://lxr.free-electrons.com/source/Documentation/devicetree/bindings/net/
wireless/qcom,ath10k.txt>
>And I can't find a of_ parser for it in the ath10k driver.
[Ram]: yeah right,  it is old now, will clean it out.

> +@@ -542,12 +543,15 @@
> +					   "msi8",  "msi9", "msi10",
"msi11",
> +					  "msi12", "msi13", "msi14",
"msi15",
> +					  "legacy";
> +-			status = "disabled";
> ++			status = "ok";
> ++			qca,msi_addr = <0x0b006040>;
> ++			qca,msi_base = <0x40>;
>Yes, I hope that the ath10k ahb driver will support this one day.
[...]
> diff --git 
> a/target/linux/ipq806x/patches-4.9/852-ipq4019-pinctrl-Updated-various
> -Pin-definitions.patch 
> b/target/linux/ipq806x/patches-4.9/852-ipq4019-pinctrl-Updated-various
> -Pin-definitions.patch
> new file mode 100644
> index 0000000..4267d47
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.9/852-ipq4019-pinctrl-Updated-var
> +++ ious-Pin-definitions.patch
> @@ -0,0 +1,1332 @@
> +From fc6cf61517b8b4ab4678659936fc7572f699d6e7 Mon Sep 17 00:00:00 
> +2001
> +From: Ram Chandra Jangir <rjangir@codeaurora.org>
> +Date: Tue, 28 Mar 2017 14:00:00 +0530
> +Subject: [PATCH] ipq4019: pinctrl: Updated various Pin definitions
> +
> +Populate default values for various GPIO functions
> +
> +Signed-off-by: Ram Chandra Jangir <rjangir@codeaurora.org>
>Please send this upstream to linux-gpio@vger.kernel.org.
>I'm saying this because it has become nearly impossible to merge this stuff
into the kernel without the right domain in the email-address.
 [Ram]: it will be upstreamed soon.

> diff --git 
> a/target/linux/ipq806x/patches-4.9/854-net-phy-Export-phy-statemachine
> -API.patch 
> b/target/linux/ipq806x/patches-4.9/854-net-phy-Export-phy-statemachine
> -API.patch
> new file mode 100644
> index 0000000..2bb213d
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.9/854-net-phy-Export-phy-statemac
> +++ hine-API.patch
> @@ -0,0 +1,36 @@
> +From 69f50b2694e9e9a3e927c21b900112d7adb581c9 Mon Sep 17 00:00:00 
> +2001
> +From: Ram Chandra Jangir <rjangir@codeaurora.org>
> +Date: Tue, 28 Mar 2017 14:00:00 +0530
> +Subject: [PATCH 2/5] net/phy: Export phy statemachine API
> +
> +When any port with link-detection enabled changes group we need to 
> +start/stop phy detection with phy_start_machine() and 
> +phy_stop_machine() at runtime for those specific ports
> +
> +Signed-off-by :Ram Chandra Jangir <rjangir@codeaurora.org>
> +---
> + drivers/net/phy/phy.c | 2 ++
> + 1 file changed, 2 insertions(+)
> +
> +diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 
> +00fe2ab..4058d5c 100644
> +--- a/drivers/net/phy/phy.c
> ++++ b/drivers/net/phy/phy.c
> +@@ -650,6 +650,7 @@ void phy_start_machine(struct phy_device *phydev)  
> +{
> +	queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, 
> +HZ);  }
> ++EXPORT_SYMBOL(phy_start_machine);
> +
> + /**
> +  * phy_trigger_machine - trigger the state machine to run @@ -687,6 
> ++688,7 @@ void phy_stop_machine(struct phy_device *phydev)
> +		phydev->state = PHY_UP;
> +	mutex_unlock(&phydev->lock);
> + }
> ++EXPORT_SYMBOL(phy_stop_machine);
>Hm, accessing the phy code internals is bound to break.
>It would be much better to remove the VLAN code from the essedma and ar40xx
phy code and just let the kernel deal with it.
[Ram]: I needed to export this, since I enabled edma as module, and will
drop this in next patch.

> diff --git 
> a/target/linux/ipq806x/patches-4.9/855-clk-qcom-ipq4019-add-ess-reset.
> patch 
> b/target/linux/ipq806x/patches-4.9/855-clk-qcom-ipq4019-add-ess-reset.
> patch
> new file mode 100644
> index 0000000..d33b8ab
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.9/855-clk-qcom-ipq4019-add-ess-re
> +++ set.patch
> @@ -0,0 +1,58 @@
> +From 7efb48a343ca368f83359d3a7087dd5ab25a283a Mon Sep 17 00:00:00 
> +2001
> +From: Ram Chandra Jangir <rjangir@codeaurora.org>
> +Date: Tue, 28 Mar 2017 22:35:33 +0530
> +Subject: [PATCH 5/8] clk: qcom: ipq4019: add ess reset
> +
> +Added the ESS reset in IPQ4019 GCC.
> +
> +Signed-off-by: Ram Chandra Jangir <rjangir@codeaurora.org>
> +---
>Please send this upstream.
[Ram]: it will be upstreamed soon.

> diff --git 
> a/target/linux/ipq806x/patches-4.9/857-ipq40xx-Fix-mdio-driver-to-work
> -with-IPQ40xx-SoC.patch 
> b/target/linux/ipq806x/patches-4.9/857-ipq40xx-Fix-mdio-driver-to-work
> -with-IPQ40xx-SoC.patch
> new file mode 100644
> index 0000000..bb72169
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.9/857-ipq40xx-Fix-mdio-driver-to-
> +++ work-with-IPQ40xx-SoC.patch
> @@ -0,0 +1,100 @@
> +From 1d8433be4af4a5862b8805a5668d404bd6fde945 Mon Sep 17 00:00:00 
> +2001
> +From: Ram Chandra Jangir <rjangir@codeaurora.org>
> +Date: Tue, 28 Mar 2017 22:32:07 +0530
> +Subject: [PATCH] ipq40xx: Fix mdio driver to work with IPQ40xx SoC
> +
> +- Add phy-reset-gpio support in probe function
> +- Add proper assignment of mii_bus read/write operations
> +
> +Signed-off-by: Ram Chandra Jangir <rjangir@codeaurora.org>
> +---
> +diff --git a/drivers/net/phy/mdio-ipq40xx.c 
> +b/drivers/net/phy/mdio-ipq40xx.c index 335d531..e969f06 100644
> +--- a/drivers/net/phy/mdio-ipq40xx.c
> ++++ b/drivers/net/phy/mdio-ipq40xx.c
> +@@ -22,6 +22,7 @@
> + #include <linux/of_mdio.h>
> + #include <linux/phy.h>
> + #include <linux/platform_device.h>
> ++#include <linux/of_gpio.h>
> +
> + #define MDIO_CTRL_0_REG		0x40
> + #define MDIO_CTRL_1_REG		0x44
> +@@ -122,11 +123,60 @@ static int ipq40xx_mdio_write(struct mii_bus *bus,
int mii_id, int regnum,
> +	return 0;
> + }
> +
> ++static int ipq40xx_phy_reset(struct platform_device *pdev) {
> ++	struct device_node *mdio_node;
> ++	int phy_reset_gpio_number;
> ++	int ret;
> ++
> ++	mdio_node = of_find_node_by_name(NULL, "mdio");
> ++	if (!mdio_node) {
> ++		dev_err(&pdev->dev, "Could not find mdio node\n");
> ++		return -ENOENT;
> ++	}
> ++
> ++	ret = of_get_named_gpio(mdio_node, "phy-reset-gpio", 0);
> ++	if (ret < 0) {
> ++		dev_err(&pdev->dev, "Could not find phy-reset-gpio\n");
> ++		return ret;
>This could break existing configurations. Please add a check that would
skip the error in case the "phy-reset-gpio" property isn't available.
[Ram]: This is required to reset via gpio, will skip if this property is not
available/defined in next patch version.

[...]
> + static int ipq40xx_mdio_probe(struct platform_device *pdev)  {
> +	struct ipq40xx_mdio_data *am;
> +	struct resource *res;
> +-	int i;
> ++	int i, ret;
> ++
> ++	ret = ipq40xx_phy_reset(pdev);
> ++	if (ret) {
> ++		dev_err(&pdev->dev, "Could not find qca8075 reset gpio\n");
> ++		return ret;
> ++	}
> diff --git 
> a/target/linux/ipq806x/patches-4.9/858-arm-dts-Add-ess-switch-device-f
> or-IPQ4019.patch 
> b/target/linux/ipq806x/patches-4.9/858-arm-dts-Add-ess-switch-device-f
> or-IPQ4019.patch
> new file mode 100644
> index 0000000..4598451
> --- /dev/null
> +++ b/target/linux/ipq806x/patches-4.9/858-arm-dts-Add-ess-switch-devi
> +++ ce-for-IPQ4019.patch
> @@ -0,0 +1,486 @@
> +From 4842020af3b39ce8c7c9a92de106d8fffd92b7c0 Mon Sep 17 00:00:00 
> +2001
> +From: Ram Chandra Jangir <rjangir@codeaurora.org>
> +Date: Tue, 28 Mar 2017 14:00:00 +0530
> +Subject: [PATCH] arm: dts: Add ess switch device for IPQ4019
> +
> +- Update ipq4019 dts nodes for mdio interface, ess-switch
> +   and edma driver.
> +- Update dt documentation for qca-ess ethernet subsystem
> +
> +Signed-off-by: xiaofeis <xiaofeis@codeaurora.org>
> +Signed-off-by: Ram Chandra Jangir <rjangir@codeaurora.org>
> +---
> +diff --git a/Documentation/devicetree/bindings/net/qca-ess.txt 
> +b/Documentation/devicetree/bindings/net/qca-ess.txt
> +new file mode 100644
> +index 0000000..c192774
> +--- /dev/null
> ++++ b/Documentation/devicetree/bindings/net/qca-ess.txt
> +@@ -0,0 +1,107 @@
> ++QCA Ethernet Subsystem
> ++----------------------
> ++
> ++This driver adds support for the Ethernet subsystem
> ++
> ++1. QCA Ethernet DMA
> ++----------------------
> ++
> ++Required properties:
> ++  - compatible = "qcom,ess-edma";
> ++
> ++Optional properties:
> ++
> ++Example:
> ++    edma@c080000 {
> ++	compatible = "qcom,ess-edma";
> ++	reg = <0xc080000 0x8000>;
> ++	qcom,page-mode = <0>;
> ++	qcom,rx_head_buf_size = <1540>;
rx-head-buf-size?
> ++	qcom,port_id_wan = <0x5>;
>port_id_wan? port-id-wan ?
[Ram]: This is port id for wan port, example: here for dakota, port 5 is the
WAN port.

>That said, edma's custom VLAN code stuff should go...
> ++	interrupts = <0 65 1>,
> ++		   <0 66 1>,
> ++		   <0 67 1>,
[...]
> ++   };
> ++
> ++2. QCA Ethernet Switch
> ++----------------------
> ++
> ++Required properties:
> ++  - compatible = "qcom,ess-switch";
> ++
> ++Optional properties:
> ++
> ++Example:
> ++
> ++	ess-switch@c000000 {
> ++		compatible = "qcom,ess-switch";
> ++		reg = <0xc000000 0x80000>; /* 512KB */
> ++		switch_access_mode = "local bus";
>switch-access-mode?
[Ram]: there are some boards which uses different switch, we use mdio bus so
here to identify whether to use mdio bus or not.

> ++		resets = <&gcc ESS_RESET>;
> ++		switch_cpu_bmp = <0x1>;  /* cpu port bitmap */
> ++		switch_lan_bmp = <0x1e>; /* lan port bitmap */
> ++		switch_wan_bmp = <0x20>; /* wan port bitmap */
>switch_cpu_bmp, switch_lan_bmp, switch_wan_bmp can be removed.
>Userspace should take care of setting up the VLAN.
[Ram]: Userspace can always change the VLANs, but these are default values ,
It is not good to declare these default values as macros inside driver code
instead 
                it is good to add them in dts file, it will also help to
configure the different default values based on the different boards.
> ++	};
> ++
> ++3. QCA Ethernet PHY mode
> ++-------------------------
> ++
> ++Required properties:
> ++  - compatible = "qcom,ess-psgmii";
> ++
> ++Optional properties:
> ++
> ++Example:
> ++
> ++	ess-psgmii@98000 {
> ++		compatible = "qcom,ess-psgmii";
> ++		reg = <0x98000 0x800>; /* 2k */
> ++		psgmii_access_mode = "local bus";
>psgmii-access-mode?
[Ram]: same as switch-access-mode.

> ++	};
> ++
> ++4. MDIO Interface
> ++----------------------
> ++
> ++Required properties:
> ++  - compatible = "qcom,ipq4019-mdio";
> ++
> ++Optional properties:
> ++
> ++Example:
> ++
> ++	mdio@90000 {
> ++		#address-cells = <1>;
> ++		#size-cells = <1>;
>size-cells = <0>;  

> ++		compatible = "qcom,ipq4019-mdio";
> ++		reg = <0x90000 0x64>;
> ++	};
> ++
> +diff --git a/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1-c1.dts 
> +b/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1-c1.dts
> +index 0d92f1b..ec75396 100644
> +--- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1-c1.dts
> ++++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1-c1.dts
> +@@ -18,5 +18,13 @@
> +
> + / {
> +	model = "Qualcomm Technologies, Inc. IPQ40xx/AP-DK01.1-C1";
> +-
> ++		soc {
> ++			mdio@90000 {
> ++			status = "ok";
> ++			pinctrl-0 = <&mdio_pins>;
> ++			pinctrl-names = "default";
> ++			bias-disable;
> ++			phy-reset-gpio = <&tlmm 59 0>;
> ++		};
> ++        };
>This looks like a whitespace issue in the patch?
[Ram]: Thanks, will fix in next patch.

> +diff --git a/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi 
> +b/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi
> +index b68fc1a..1aee3b1 100644
> +--- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi
> ++++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi
> +@@ -161,5 +182,56 @@
> +		watchdog@b017000 {
> +			status = "ok";
> +		};
> ++
> ++		ess-switch@c000000 {
> ++			status = "ok";
> ++			switch_mac_mode = <0x0>; /* mac mode for RGMII RMII
*/
> ++			switch_initvlas = <
> ++				0x0007c 0x54 /* PORT0_STATUS */
> ++			>;
> ++			led_source@0 {
> ++				led = <0x3>;     /*led number */
> ++				source = <0x1>;  /*source id 1 */
> ++				mode = "normal"; /*on,off,blink,normal */
> ++				speed = "all";   /*10M,100M,1000M,all */
> ++				freq = "auto";   /*2Hz,4Hz,8Hz,auto*/
> ++			};
[...]
>Just a heads up: I had to add a "gpio-controller" to the ar40xx driver.
>This was necessary, because the AVM FritzBox 4040 uses one of the switch
LEDs to enable the power of both USB ports.
>(Off-topic: Yes, the usb power will "blink" for a few seconds during boot
:-D ) 

> +	};
> + };

> +diff --git a/arch/arm/boot/dts/qcom-ipq4019.dtsi 
> +b/arch/arm/boot/dts/qcom-ipq4019.dtsi
> +index 7013c85..9793611 100644
> +--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
> ++++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
> +@@ -26,8 +26,8 @@
> +	aliases {
> +		spi0 = &spi_0;
> +		i2c0 = &i2c_0;
> +-		ethernet0 = &gmac0;
> +-		ethernet1 = &gmac1;
> ++		ethernet0 = "/soc/edma/gmac0";
> ++		ethernet1 = "/soc/edma/gmac1";
> +	};
>Any reason why you deleted the labels? The FB4040 will be doing a lookup
based on the labels, so I hate to see them go.
[Ram]: It is same thing in different way, will update  back the labels in
next patch.

> +	cpus {
> +@@ -325,43 +325,47 @@
> +
> +		mdio@90000 {
> +			#address-cells = <1>;
> +-			#size-cells = <0>;
> ++			#size-cells = <1>;
#size-cells = <0>;
>you don't have any size cells in the subnodes.
[Ram]: Yes, you are right. It seems 0 is better. will add it in next patch. 

> +			compatible = "qcom,ipq4019-mdio";
> +			reg = <0x90000 0x64>;
> +			status = "disabled";
> +
> +-			ethernet-phy@0 {
> ++			phy0: ethernet-phy@0 {
> +				reg = <0>;
>There's only the address parameter. no size.
[Ram]:  These are five phy devices with phy address from 0 to 4. These are
added into children leaf under mdio bus leaf.

> +			};
[...]
> +@@ -378,57 +384,58 @@
> +			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 255 IRQ_TYPE_EDGE_RISING>;
> ++			qcom,rx-head-buf-size = <1540>;
> ++			qcom,num-gmac = <2>;
>Technically, the essedma has only one. See the VLAN comment above.
[Ram]:  There are two groups in dakota boards, one is LAN group and second
is WAN group.
                num-gmac tells number of groups to be created, which is
currently set to 2.

> ++			qcom,mdio-supported;
> ++			interrupts = <0  65 IRQ_TYPE_EDGE_RISING>,
> ++				      <0  66 IRQ_TYPE_EDGE_RISING>,
[...]
> ++				      <0 255 IRQ_TYPE_EDGE_RISING>;
> +
> +			status = "disabled";
> +
> +-			gmac0: gmac0 {
> ++			gmac0 {
>please keep the label.
[Ram]: ok.
> +				local-mac-address = [00 00 00 00 00 00];
> +-				vlan_tag = <1 0x1f>;
> ++				qcom,phy-mdio-addr = <4>;
> ++				qcom,poll-required = <1>;
> ++				qcom,poll-required-dynamic = <1>;
> ++				qcom,forced-speed = <1000>;
> ++				qcom,forced-duplex = <1>;
> ++				vlan-tag = <2 0x20>;
>Ideally, fixed-link should be used instead of custom "force-speed".
[Ram]: I think both should be same, just different way to define it.
>The custom VLAN stuff ( phy-mdio-addr, vlan_tag) should be removed.
>The userspace will deal with setting up the switch.
[Ram]:  User can setup the switch but here are some default values
populated.
                Vlan tag for WAN group is 2, for LAN group is 1. This is
pre-determined and populated with default values here. 
           This can be changed from user space too. phy-mdio-addr is for
enabling WAN link detection. 

[...]
> +--
> +2.7.2
> diff --git a/target/linux/ipq806x/profiles/00-default.mk 
> b/target/linux/ipq806x/profiles/00-default.mk
> index 9baa24f..c4b58a2 100644
> --- a/target/linux/ipq806x/profiles/00-default.mk
> +++ b/target/linux/ipq806x/profiles/00-default.mk
> @@ -1,7 +1,8 @@
>  define Profile/Default
>    NAME:=Default Profile
>    PRIORITY:=1
> -  PACKAGES:=ath10k-firmware-qca99x0 ath10k-firmware-qca988x 
> ath10k-firmware-qca9984
> +  PACKAGES:=ath10k-firmware-qca99x0 ath10k-firmware-qca988x
ath10k-firmware-qca9984 \
> +	    ath10k-firmware-qca4019

>Currently, ath10k-firmware-qca4019 gets added automatically by the ipq-wifi
board package: 
<https://github.com/lede-project/source/blob/master/package/firmware/ipq-wif
i/Makefile>

>As this is supposed to be the reference board. the data should be part of
ath10k-firmware's board-2.bin. However, other platforms aren't that lucky.

>This issue has been reported to ath10k-devel and linux-wireless:
<https://www.mail-archive.com/ath10k@lists.infradead.org/msg06154.html>

>And it would be nice to have a official solution for this.
[Ram]: I did not use ipq-wifi during validation. 
                 Currently ath10k needs calibration data which are stored in
ART partition. We cannot have common board-2.bin for all boards as
calibration data will differ per board.
                 Hence use board-2.bin from Kvalo's tree & use caldata from
ART partition.
                 This way we will be able to use the latest firmware
releases from kvalo's tree without re-genrating the new board-2.bin file and
checking in to git.

Once again Thanks for your review comments and time.

Thanks,
Ram
John Crispin April 17, 2017, 6:08 p.m. UTC | #6
On 17/04/17 14:54, Felix Fietkau wrote:
> On 2017-04-17 13:26, Ram Chandra Jangir wrote:
>> Hi Felix,
>>
>>> What's the reason for making it a separate subtarget?
>> We would like to add ipq40xx as subtarget for following reasons.
>> 1. ipq40xx is different SoC than existing ipq806x
>> 2. Would like to have SoC specific images, so that kernel configs can be tweaked per SoC
>> 3. SoC specific images will be smaller in size & faster boot-time
> While some drivers are indeed SoC specific, I think ipq40xx and ipq806x
> do have a lot of overlap. I don't think that images will be much smaller
> with SoC specific subtargets, at least not enough to make any
> *practical* difference.
>
>> 4. Newer SoC may have exclusive FWs which needs compile time differentiation.
> Firmware should be packaged anyway, and can thus be handled via device
> profiles.
>
>> 5. There is new SoC(ipq807x) under development, which is based on
>> ARMv8. Hence we will need subtarget approach to accommodate new SoC.
> I agree that there should be a separate subtarget for ipq807x, but I
> don't think it makes sense for ipq40xx.
>
> Please keep in mind that each extra build target has a visible cost in
> buildbot and download server resources, so I would really like to avoid
> adding more except where there are strong reasons to do so.
I agree on this one. there is no technical need to split ipq4xxx into a 
sub target

     John

> - Felix
>
> _______________________________________________
> Lede-dev mailing list
> Lede-dev@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/lede-dev
Michael Yartys via Lede-dev April 19, 2017, 3:23 p.m. UTC | #7
The sender domain has a DMARC Reject/Quarantine policy which disallows
sending mailing list messages using the original "From" header.

To mitigate this problem, the original message has been wrapped
automatically by the mailing list software.
Hello Ram,

On Monday, April 17, 2017 11:13:11 PM CEST Ram Chandra Jangir wrote:
> > diff --git a/target/linux/ipq806x/Makefile 
> > b/target/linux/ipq806x/Makefile index 5a5551c..b5b36e1 100644
> > --- a/target/linux/ipq806x/Makefile
> > +++ b/target/linux/ipq806x/Makefile
> > @@ -21,7 +21,7 @@ DEFAULT_PACKAGES += \
> >  	kmod-ata-core kmod-ata-ahci kmod-ata-ahci-platform \
> >  	kmod-usb-core kmod-usb-ohci kmod-usb2 kmod-usb-ledtrig-usbport \
> >  	kmod-usb3 kmod-usb-dwc3-of-simple kmod-usb-phy-qcom-dwc3 \
> > -	kmod-ath10k wpad-mini \
> > +	kmod-ath10k wpad-mini kmod-switch-ar40xx kmod-ipq40xx-edma \
> 
> >>the switch and edma driver are already part of the default image.
> >>Why do you want to have them as separate packages?
> [Ram]: It is good to have enabled them as module instead of inbuilt,
> Theoretically ar40xx switch depends on swconfig module which is 
> enabled as kmod only.
I fail to see why this would be good idea. Can you please explain in !detail!
why having this as an external module would be advantageous. As for why not:
The kmod-swconfig module does not set SWCONFIG_LEDS. This will break the LAN
LED on the Fritz!Box 4040.

> > diff --git 
> > a/target/linux/ipq806x/patches-4.9/852-ipq4019-pinctrl-Updated-various
> > -Pin-definitions.patch 
> > b/target/linux/ipq806x/patches-4.9/852-ipq4019-pinctrl-Updated-various
> > -Pin-definitions.patch
> > new file mode 100644
> > index 0000000..4267d47
> > --- /dev/null
> > +++ b/target/linux/ipq806x/patches-4.9/852-ipq4019-pinctrl-Updated-var
> > +++ ious-Pin-definitions.patch
> > @@ -0,0 +1,1332 @@
> > +From fc6cf61517b8b4ab4678659936fc7572f699d6e7 Mon Sep 17 00:00:00 
> > +2001
> > +From: Ram Chandra Jangir <rjangir@codeaurora.org>
> > +Date: Tue, 28 Mar 2017 14:00:00 +0530
> > +Subject: [PATCH] ipq4019: pinctrl: Updated various Pin definitions
> > +
> > +Populate default values for various GPIO functions
> > +
> > +Signed-off-by: Ram Chandra Jangir <rjangir@codeaurora.org>
> >Please send this upstream to linux-gpio@vger.kernel.org.
> >I'm saying this because it has become nearly impossible to merge this stuff
> into the kernel without the right domain in the email-address.
>  [Ram]: it will be upstreamed soon.

There were a bunch of comments for the original submission to linux-gpio.
Please check them out: <https://patchwork.kernel.org/patch/7662241/>
before you submit this again. Because from what I can tell, the original
issues in v2 did not get addressed in this version. 
(i.e.: owner parameter. see Bjorn's comment about the platform driver.)

> > +@@ -122,11 +123,60 @@ static int ipq40xx_mdio_write(struct mii_bus *bus,
> int mii_id, int regnum,
> > +	return 0;
> > + }
> > +
> > ++static int ipq40xx_phy_reset(struct platform_device *pdev) {
> > ++	struct device_node *mdio_node;
> > ++	int phy_reset_gpio_number;
> > ++	int ret;
> > ++
> > ++	mdio_node = of_find_node_by_name(NULL, "mdio");
> > ++	if (!mdio_node) {
> > ++		dev_err(&pdev->dev, "Could not find mdio node\n");
> > ++		return -ENOENT;
> > ++	}
> > ++
> > ++	ret = of_get_named_gpio(mdio_node, "phy-reset-gpio", 0);
> > ++	if (ret < 0) {
> > ++		dev_err(&pdev->dev, "Could not find phy-reset-gpio\n");
> > ++		return ret;
> >This could break existing configurations. Please add a check that would
> skip the error in case the "phy-reset-gpio" property isn't available.
> [Ram]: This is required to reset via gpio, will skip if this property is not
> available/defined in next patch version.
One thing, can you ask around what would happen if the PHY is resetted on a
device powered by PoE? I know that this is an issue with a Meraki MR18 
(AR71XX with AR8035) when it is powered by PoE Alternative B. In this
case, the MR18 will simply fail to boot, because once the PHY reset GPIO
is asserted it just restarts due to the "sudden" loss of power.

> > diff --git 
> > a/target/linux/ipq806x/patches-4.9/858-arm-dts-Add-ess-switch-device-f
> > or-IPQ4019.patch 
> > b/target/linux/ipq806x/patches-4.9/858-arm-dts-Add-ess-switch-device-f
> > or-IPQ4019.patch
> > new file mode 100644
> > index 0000000..4598451
> > --- /dev/null
> > +++ b/target/linux/ipq806x/patches-4.9/858-arm-dts-Add-ess-switch-devi
> > +++ ce-for-IPQ4019.patch
> > @@ -0,0 +1,486 @@
> > +From 4842020af3b39ce8c7c9a92de106d8fffd92b7c0 Mon Sep 17 00:00:00 
> > +2001
> > +From: Ram Chandra Jangir <rjangir@codeaurora.org>
> > +Date: Tue, 28 Mar 2017 14:00:00 +0530
> > +Subject: [PATCH] arm: dts: Add ess switch device for IPQ4019
> > +
> > +- Update ipq4019 dts nodes for mdio interface, ess-switch
> > +   and edma driver.
> > +- Update dt documentation for qca-ess ethernet subsystem
> > +
> > +Signed-off-by: xiaofeis <xiaofeis@codeaurora.org>
> > +Signed-off-by: Ram Chandra Jangir <rjangir@codeaurora.org>
> > +---
> > +diff --git a/Documentation/devicetree/bindings/net/qca-ess.txt 
> > +b/Documentation/devicetree/bindings/net/qca-ess.txt
> > +new file mode 100644
> > +index 0000000..c192774
> > +--- /dev/null
> > ++++ b/Documentation/devicetree/bindings/net/qca-ess.txt
> > +@@ -0,0 +1,107 @@
> > ++QCA Ethernet Subsystem
> > ++----------------------
> > ++
> > ++This driver adds support for the Ethernet subsystem
> > ++
> > ++1. QCA Ethernet DMA
> > ++----------------------
> > ++
> > ++Required properties:
> > ++  - compatible = "qcom,ess-edma";
> > ++
> > ++Optional properties:
> > ++
> > ++Example:
> > ++    edma@c080000 {
> > ++	compatible = "qcom,ess-edma";
> > ++	reg = <0xc080000 0x8000>;
> > ++	qcom,page-mode = <0>;
> > ++	qcom,rx_head_buf_size = <1540>;
> rx-head-buf-size?
> > ++	qcom,port_id_wan = <0x5>;
> >port_id_wan? port-id-wan ?
> [Ram]: This is port id for wan port, example: here for dakota, port 5 is the
> WAN port.

No, check again. the device-tree description is out of date. Your code looks
for "qcom,rx-head-buf-size" and not "qcom,rx_head_buf_size". The same is true
for the other properties.

> > ++2. QCA Ethernet Switch
> > ++----------------------
> > ++
> > ++Required properties:
> > ++  - compatible = "qcom,ess-switch";
> > ++
> > ++Optional properties:
> > ++
> > ++Example:
> > ++
> > ++	ess-switch@c000000 {
> > ++		compatible = "qcom,ess-switch";
> > ++		reg = <0xc000000 0x80000>; /* 512KB */
> > ++		switch_access_mode = "local bus";
> >switch-access-mode?
> [Ram]: there are some boards which uses different switch, we use mdio bus so
> here to identify whether to use mdio bus or not.
Same problem. Please update the description.
 
> > ++		resets = <&gcc ESS_RESET>;
> > ++		switch_cpu_bmp = <0x1>;  /* cpu port bitmap */
> > ++		switch_lan_bmp = <0x1e>; /* lan port bitmap */
> > ++		switch_wan_bmp = <0x20>; /* wan port bitmap */
> >switch_cpu_bmp, switch_lan_bmp, switch_wan_bmp can be removed.
> >Userspace should take care of setting up the VLAN.
> [Ram]: Userspace can always change the VLANs, but these are default values ,
> It is not good to declare these default values as macros inside driver code
> instead it is good to add them in dts file, it will also help to
> configure the different default values based on the different boards.
I say: It's not a good idea to dublicate what userspace is doing later.
See: target/linux/ipq806x/base-files/etc/board.d/02_network:

+       ucidef_set_interfaces_lan_wan "eth1" "eth0"
+       ucidef_add_switch "switch0" \
+               "0t@eth1" "1:lan" "2:lan" "3:lan" "4:lan"

So what exactly is the point of doing this twice then? Please keep the
switch and ethernet configuration in userspace. Or alternatively
convert essedma and ar40xx to use the Distributed Switch Architecture.

> > ++	};
> > ++
> > ++3. QCA Ethernet PHY mode
> > ++-------------------------
> > ++
> > ++Required properties:
> > ++  - compatible = "qcom,ess-psgmii";
> > ++
> > ++Optional properties:
> > ++
> > ++Example:
> > ++
> > ++	ess-psgmii@98000 {
> > ++		compatible = "qcom,ess-psgmii";
> > ++		reg = <0x98000 0x800>; /* 2k */
> > ++		psgmii_access_mode = "local bus";
> >psgmii-access-mode?
> [Ram]: same as switch-access-mode.
Please replace _ with -

> > ++		compatible = "qcom,ipq4019-mdio";
> > ++		reg = <0x90000 0x64>;
> > ++	};
> > ++ 
> > +diff --git a/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi 
> > +b/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi
> > +index b68fc1a..1aee3b1 100644
> > +--- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi
> > ++++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi
> > +@@ -161,5 +182,56 @@
> > +		watchdog@b017000 {
> > +			status = "ok";
> > +		};
> > ++
> > ++		ess-switch@c000000 {
> > ++			status = "ok";
> > ++			switch_mac_mode = <0x0>; /* mac mode for RGMII RMII */
> > ++			switch_initvlas = <
> > ++				0x0007c 0x54 /* PORT0_STATUS */
> > ++			>;
> > ++			led_source@0 {
> > ++				led = <0x3>;     /*led number */
> > ++				source = <0x1>;  /*source id 1 */
> > ++				mode = "normal"; /*on,off,blink,normal */
> > ++				speed = "all";   /*10M,100M,1000M,all */
> > ++				freq = "auto";   /*2Hz,4Hz,8Hz,auto*/
> > ++			};
> [...]
> >Just a heads up: I had to add a "gpio-controller" to the ar40xx driver.
> >This was necessary, because the AVM FritzBox 4040 uses one of the switch
> LEDs to enable the power of both USB ports.
> >(Off-topic: Yes, the usb power will "blink" for a few seconds during boot
> :-D ) 

Also, where does this led_source node get parsed?

> > +diff --git a/arch/arm/boot/dts/qcom-ipq4019.dtsi 
> > +b/arch/arm/boot/dts/qcom-ipq4019.dtsi
> > +index 7013c85..9793611 100644
> > +--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
> > ++++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
> > +@@ -325,43 +325,47 @@
> > +
> > +		mdio@90000 {
> > +			#address-cells = <1>;
> > +-			#size-cells = <0>;
> > ++			#size-cells = <1>;
> #size-cells = <0>;
> >you don't have any size cells in the subnodes.
> [Ram]: Yes, you are right. It seems 0 is better. will add it in next patch. 
Don't forget to update the device tree binding documentation as well.
 
> > +@@ -378,57 +384,58 @@
> > +			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 255 IRQ_TYPE_EDGE_RISING>;
> > ++			qcom,rx-head-buf-size = <1540>;
> > ++			qcom,num-gmac = <2>;
> >Technically, the essedma has only one. See the VLAN comment above.
> [Ram]:  There are two groups in dakota boards, one is LAN group and second
> is WAN group. num-gmac tells number of groups to be created, which is
> currently set to 2.
VLAN configuration is done by userspace. The secondary (tertiary, ...) MACs
are just a virtual MACs for VLAN and nothing else. The kernel already does VLAN
for you. Please, do not waste resources for this. The essedma driver allocates
a full rx receive ring for these virtual MACs too and this is problem for 128 MiB
RAM devices like the Asus RT-AC58U.

> > +				local-mac-address = [00 00 00 00 00 00];
> > +-				vlan_tag = <1 0x1f>;
> > ++				qcom,phy-mdio-addr = <4>;
> > ++				qcom,poll-required = <1>;
> > ++				qcom,poll-required-dynamic = <1>;
> > ++				qcom,forced-speed = <1000>;
> > ++				qcom,forced-duplex = <1>;
> > ++				vlan-tag = <2 0x20>;
> >Ideally, fixed-link should be used instead of custom "force-speed".
> [Ram]: I think both should be same, just different way to define it.
Well, you could try to mark them (qcom,forced-speed, qcom,forced-duplex) 
as deprecated. Altough, Rob or Mark will probably tell you to remove
those, since the essedma node isn't present in the upstream kernel.

> >The custom VLAN stuff ( phy-mdio-addr, vlan_tag) should be removed.
> >The userspace will deal with setting up the switch.
> [Ram]:  User can setup the switch but here are some default values
> populated. Vlan tag for WAN group is 2, for LAN group is 1. This is
> pre-determined and populated with default values here. This can be 
> changed from user space too. phy-mdio-addr is for enabling WAN link
> detection. 
VLAN configuration is performed by userspace. Of course, if you want,
you can do some of this with DSA as well.

> > +--
> > +2.7.2
> > diff --git a/target/linux/ipq806x/profiles/00-default.mk 
> > b/target/linux/ipq806x/profiles/00-default.mk
> > index 9baa24f..c4b58a2 100644
> > --- a/target/linux/ipq806x/profiles/00-default.mk
> > +++ b/target/linux/ipq806x/profiles/00-default.mk
> > @@ -1,7 +1,8 @@
> >  define Profile/Default
> >    NAME:=Default Profile
> >    PRIORITY:=1
> > -  PACKAGES:=ath10k-firmware-qca99x0 ath10k-firmware-qca988x 
> > ath10k-firmware-qca9984
> > +  PACKAGES:=ath10k-firmware-qca99x0 ath10k-firmware-qca988x
> ath10k-firmware-qca9984 \
> > +	    ath10k-firmware-qca4019
> > Currently, ath10k-firmware-qca4019 gets added automatically by the ipq-wifi
> > board package: <https://github.com/lede-project/source/blob/master/package/firmware/ipq-wifi/Makefile>
> >
> > As this is supposed to be the reference board. the data should be part of
> > ath10k-firmware's board-2.bin. However, other platforms aren't that lucky.
> >
> >This issue has been reported to ath10k-devel and linux-wireless:
> ><https://www.mail-archive.com/ath10k@lists.infradead.org/msg06154.html>
> >
> >And it would be nice to have a official solution for this.

> [Ram]: I did not use ipq-wifi during validation. Currently ath10k needs calibration
> data which are stored in ART partition. We cannot have common board-2.bin for all
> boards as calibration data will differ per board.
Please read Kalle Valo's reply in the thread linked above:
https://www.mail-archive.com/ath10k@lists.infradead.org/msg06563.html

"I haven't followed the discussion very closely, so I might be way off,
but for laptop SMBIOS implementations Waldemar added a variant field to
board-2.bin so that we can have multiple images for the same subsystem
id. Could it help here also?"

The ath10k has already introduced a way to make this work. I would suggest
you bring this to the attention of your managers and let them work this out
together with the ODM that decided to go for different PAs/RF Layouts.

The provided board-2.bin (from ath10k-firmware repository) simply does not
work for most of the released customer devices: Asus RT-AC58U, Zyxel NBG6617,
Meraki MR33, Netgear Orbi and the TP-Link Deco (M5?).

Again: There needs to be a working solution for all devices... and not just
the reference boards.

Regards,
Christian
diff mbox

Patch

diff --git a/target/linux/ipq806x/Makefile b/target/linux/ipq806x/Makefile
index 5a5551c..b5b36e1 100644
--- a/target/linux/ipq806x/Makefile
+++ b/target/linux/ipq806x/Makefile
@@ -8,7 +8,7 @@  BOARDNAME:=Qualcomm Atheros IPQ806X
 FEATURES:=squashfs nand ubifs fpu
 CPU_TYPE:=cortex-a15
 CPU_SUBTYPE:=neon-vfpv4
-SUBTARGETS:=ipq806x
+SUBTARGETS:=ipq806x ipq40xx
 MAINTAINER:=John Crispin <john@phrozen.org>
 
 KERNEL_PATCHVER:=4.9
@@ -21,7 +21,7 @@  DEFAULT_PACKAGES += \
 	kmod-ata-core kmod-ata-ahci kmod-ata-ahci-platform \
 	kmod-usb-core kmod-usb-ohci kmod-usb2 kmod-usb-ledtrig-usbport \
 	kmod-usb3 kmod-usb-dwc3-of-simple kmod-usb-phy-qcom-dwc3 \
-	kmod-ath10k wpad-mini \
+	kmod-ath10k wpad-mini kmod-switch-ar40xx kmod-ipq40xx-edma \
 	uboot-envtools
 
 $(eval $(call BuildTarget))
diff --git a/target/linux/ipq806x/base-files/etc/board.d/02_network b/target/linux/ipq806x/base-files/etc/board.d/02_network
index 36e0fb3..082105a 100755
--- a/target/linux/ipq806x/base-files/etc/board.d/02_network
+++ b/target/linux/ipq806x/base-files/etc/board.d/02_network
@@ -48,6 +48,12 @@  nbg6817)
 	ucidef_set_interface_macaddr "lan" "$hw_mac_addr"
 	ucidef_set_interface_macaddr "wan" "$(macaddr_add $hw_mac_addr 1)"
 	;;
+ap-dk01.1-c1|\
+ap-dk04.1-c1)
+	ucidef_set_interfaces_lan_wan "eth1" "eth0"
+	ucidef_add_switch "switch0" \
+		"0t@eth1" "1:lan" "2:lan" "3:lan" "4:lan"
+	;;
 *)
 	echo "Unsupported hardware. Network interfaces not intialized"
 	;;
diff --git a/target/linux/ipq806x/base-files/etc/hotplug.d/firmware/11-ath10k-caldata b/target/linux/ipq806x/base-files/etc/hotplug.d/firmware/11-ath10k-caldata
index 6526212..46f3f92 100644
--- a/target/linux/ipq806x/base-files/etc/hotplug.d/firmware/11-ath10k-caldata
+++ b/target/linux/ipq806x/base-files/etc/hotplug.d/firmware/11-ath10k-caldata
@@ -51,6 +51,10 @@  case "$FIRMWARE" in
 	fritz4040)
 		/usr/bin/fritz_cal_extract -i 1 -s 0x400 -e 0x207 -l 12064 -o /lib/firmware/$FIRMWARE $(find_mtd_chardev "urlader_config")
 		;;
+	ap-dk01.1-c1 |\
+	ap-dk04.1-c1)
+		ath10kcal_extract "0:ART" 4096 12064
+		;;
 	esac
 	;;
 "ath10k/pre-cal-ahb-a800000.wifi.bin")
@@ -58,6 +62,10 @@  case "$FIRMWARE" in
 	fritz4040)
 		/usr/bin/fritz_cal_extract -i 1 -s 0x400 -e 0x208 -l 12064 -o /lib/firmware/$FIRMWARE $(find_mtd_chardev "urlader_config")
 		;;
+	ap-dk01.1-c1 |\
+	ap-dk04.1-c1)
+		ath10kcal_extract "0:ART" 20480 12064
+		;;
 	esac
 	;;
 
diff --git a/target/linux/ipq806x/base-files/lib/ipq806x.sh b/target/linux/ipq806x/base-files/lib/ipq806x.sh
index 348a3a8..e2bebf5 100644
--- a/target/linux/ipq806x/base-files/lib/ipq806x.sh
+++ b/target/linux/ipq806x/base-files/lib/ipq806x.sh
@@ -47,6 +47,12 @@  ipq806x_board_detect() {
 	*"VR2600v")
 		name="vr2600v"
 		;;
+	*"AP-DK01.1-C1")
+		name="ap-dk01.1-c1"
+		;;
+	*"AP-DK04.1-C1")
+		name="ap-dk04.1-c1"
+		;;
 	esac
 
 	[ -z "$name" ] && name="unknown"
diff --git a/target/linux/ipq806x/config-4.9 b/target/linux/ipq806x/config-4.9
index bb1f141..07e7e69 100644
--- a/target/linux/ipq806x/config-4.9
+++ b/target/linux/ipq806x/config-4.9
@@ -2,7 +2,6 @@  CONFIG_ALIGNMENT_TRAP=y
 # CONFIG_AMBA_PL08X is not set
 CONFIG_APQ_GCC_8084=y
 CONFIG_APQ_MMCC_8084=y
-CONFIG_AR40XX_PHY=y
 CONFIG_AR8216_PHY=y
 CONFIG_ARCH_CLOCKSOURCE_DATA=y
 CONFIG_ARCH_HAS_ELF_RANDOMIZE=y
@@ -11,7 +10,7 @@  CONFIG_ARCH_HAS_SG_CHAIN=y
 CONFIG_ARCH_HAS_TICK_BROADCAST=y
 CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y
 CONFIG_ARCH_HIBERNATION_POSSIBLE=y
-CONFIG_ARCH_IPQ40XX=y
+# CONFIG_ARCH_IPQ40XX is not set
 # CONFIG_ARCH_MDM9615 is not set
 CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y
 CONFIG_ARCH_MSM8960=y
@@ -164,7 +163,6 @@  CONFIG_DYNAMIC_DEBUG=y
 CONFIG_EARLY_PRINTK=y
 CONFIG_EDAC_ATOMIC_SCRUB=y
 CONFIG_EDAC_SUPPORT=y
-CONFIG_ESSEDMA=y
 CONFIG_ETHERNET_PACKET_MANGLE=y
 CONFIG_FIXED_PHY=y
 CONFIG_FIX_EARLYCON_MEM=y
@@ -255,7 +253,7 @@  CONFIG_IOMMU_HELPER=y
 # CONFIG_IOMMU_IO_PGTABLE_ARMV7S is not set
 # CONFIG_IOMMU_IO_PGTABLE_LPAE is not set
 CONFIG_IOMMU_SUPPORT=y
-CONFIG_IPQ_GCC_4019=y
+# CONFIG_IPQ_GCC_4019 is not set
 # CONFIG_IPQ_GCC_806X is not set
 # CONFIG_IPQ_LCC_806X is not set
 CONFIG_IRQCHIP=y
@@ -275,7 +273,7 @@  CONFIG_LZO_DECOMPRESS=y
 CONFIG_MDIO_BITBANG=y
 CONFIG_MDIO_BOARDINFO=y
 CONFIG_MDIO_GPIO=y
-CONFIG_MDIO_IPQ40XX=y
+# CONFIG_MDIO_IPQ40XX is not set
 # CONFIG_MDM_GCC_9615 is not set
 # CONFIG_MDM_LCC_9615 is not set
 # CONFIG_MFD_MAX77620 is not set
@@ -376,7 +374,7 @@  CONFIG_PHYLIB=y
 CONFIG_PINCTRL=y
 CONFIG_PINCTRL_APQ8064=y
 # CONFIG_PINCTRL_APQ8084 is not set
-CONFIG_PINCTRL_IPQ4019=y
+# CONFIG_PINCTRL_IPQ4019 is not set
 # CONFIG_PINCTRL_IPQ8064 is not set
 # CONFIG_PINCTRL_MDM9615 is not set
 CONFIG_PINCTRL_MSM=y
diff --git a/target/linux/ipq806x/image/Makefile b/target/linux/ipq806x/image/Makefile
index 9af2e5a..4d6fd10 100644
--- a/target/linux/ipq806x/image/Makefile
+++ b/target/linux/ipq806x/image/Makefile
@@ -250,9 +250,37 @@  define Device/VR2600v
 	IMAGE/sysupgrade.bin := pad-extra 512 | append-kernel | pad-to $$$${KERNEL_SIZE} | append-rootfs | pad-rootfs | append-metadata
 endef
 
+define Device/AP-DK01.1-C1
+	$(call Device/FitImage)
+	DEVICE_DTS := qcom-ipq4019-ap.dk01.1-c1
+	KERNEL_LOADADDR := 0x80208000
+	KERNEL_INSTALL := 1
+	KERNEL_SIZE := 4048k
+	BLOCKSIZE := 128k
+	PAGESIZE := 2048
+	BOARD_NAME := ap-dk01.1-c1
+	DEVICE_TITLE := QCA AP-DK01.1-C1
+endef
+
+define Device/AP-DK04.1-C1
+	$(call Device/FitImage)
+	DEVICE_DTS := qcom-ipq4019-ap.dk04.1-c1
+	KERNEL_LOADADDR := 0x80208000
+	KERNEL_INSTALL := 1
+	KERNEL_SIZE := 4048k
+	BLOCKSIZE := 128k
+	PAGESIZE := 2048
+	BOARD_NAME := ap-dk04.1-c1
+	DEVICE_TITLE := QCA AP-DK04.1-C1
+endef
+
 ifeq ($(SUBTARGET),ipq806x)
-TARGET_DEVICES += AP148 AP148-legacy C2600 D7800 DB149 EA8500 FRITZ4040 R7500 \
+TARGET_DEVICES += AP148 AP148-legacy C2600 D7800 DB149 EA8500 R7500 \
 		  R7500v2 R7800 NBG6817 VR2600v
 endif
 
+ifeq ($(SUBTARGET),ipq40xx)
+  TARGET_DEVICES += FRITZ4040 AP-DK01.1-C1 AP-DK04.1-C1
+endif
+
 $(eval $(call BuildImage))
diff --git a/target/linux/ipq806x/ipq40xx/config-default b/target/linux/ipq806x/ipq40xx/config-default
new file mode 100644
index 0000000..1ee3991
--- /dev/null
+++ b/target/linux/ipq806x/ipq40xx/config-default
@@ -0,0 +1,5 @@ 
+CONFIG_ARCH_IPQ40XX=y
+CONFIG_IPQ_GCC_4019=y
+CONFIG_MDIO_IPQ40XX=y
+CONFIG_PINCTRL_IPQ4019=y
+CONFIG_USB_IPQ4019_PHY=y
diff --git a/target/linux/ipq806x/ipq40xx/target.mk b/target/linux/ipq806x/ipq40xx/target.mk
new file mode 100644
index 0000000..362001a
--- /dev/null
+++ b/target/linux/ipq806x/ipq40xx/target.mk
@@ -0,0 +1,7 @@ 
+
+SUBTARGET:=ipq40xx
+BOARDNAME:=QCA IPQ40xx based boards
+
+define Target/Description
+	Build firmware image for IPQ40xx SoC devices.
+endef
diff --git a/target/linux/ipq806x/modules.mk b/target/linux/ipq806x/modules.mk
index 6f1ca2d..e6edf63 100644
--- a/target/linux/ipq806x/modules.mk
+++ b/target/linux/ipq806x/modules.mk
@@ -29,3 +29,35 @@  define KernelPackage/usb-phy-qcom-dwc3/description
 endef
 
 $(eval $(call KernelPackage,usb-phy-qcom-dwc3))
+
+define KernelPackage/switch-ar40xx
+  SUBMENU:=Network Devices
+  TITLE:=QCA AR40XX switch driver
+  DEPENDS:=@TARGET_ipq806x_ipq40xx +kmod-swconfig
+  KCONFIG:=CONFIG_AR40XX_PHY
+  FILES:=$(LINUX_DIR)/drivers/net/phy/ar40xx.ko
+  AUTOLOAD:=$(call AutoLoad,30,ar40xx)
+endef
+
+define KernelPackage/switch-ar40xx/description
+ Qualcomm atheros AR40XX switch support
+endef
+
+$(eval $(call KernelPackage,switch-ar40xx))
+
+define KernelPackage/ipq40xx-edma
+  SUBMENU:=Network Devices
+  TITLE:=QCA IPQ40xx ethernet driver
+  DEPENDS:=@TARGET_ipq806x_ipq40xx
+  KCONFIG:= \
+	CONFIG_NET_VENDOR_QUALCOMM=y \
+	CONFIG_ESSEDMA
+  FILES:=$(LINUX_DIR)/drivers/net/ethernet/qualcomm/essedma/essedma.ko
+  AUTOLOAD:=$(call AutoLoad,45,essedma)
+endef
+
+define KernelPackage/ipq40xx-edma/description
+ Kernel modules for IPQ40xx integrated ethernet adapater.
+endef
+
+$(eval $(call KernelPackage,ipq40xx-edma))
diff --git a/target/linux/ipq806x/patches-4.9/851-dts-ipq40xx-Add-support-for-spi-nor-32-MB-flash-and-.patch b/target/linux/ipq806x/patches-4.9/851-dts-ipq40xx-Add-support-for-spi-nor-32-MB-flash-and-.patch
new file mode 100644
index 0000000..5753f10
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/851-dts-ipq40xx-Add-support-for-spi-nor-32-MB-flash-and-.patch
@@ -0,0 +1,111 @@ 
+From 5f07811771ad92a3413248a240eaedfee09ace93 Mon Sep 17 00:00:00 2001
+From: Ram Chandra Jangir <rjangir@codeaurora.org>
+Date: Fri, 24 Mar 2017 14:00:00 +0530
+Subject: [PATCH] dts: ipq40xx: Add support for spi nor 32 MB flash and enable
+ wifi support
+
+- Add micron n25q128a11 spi nor 32MB flash and fixes spi nodes
+  to work on DK01 and DK04 boards.
+- Add support for default SPI configuration
+- Enable and fixed WiFi nodes to work with DK boards
+
+Signed-off-by: Ram Chandra Jangir <rjangir@codeaurora.org>
+---
+ arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi |  7 +++++--
+ arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi |  8 +++++---
+ arch/arm/boot/dts/qcom-ipq4019.dtsi           | 10 ++++++++--
+ 3 files changed, 18 insertions(+), 7 deletions(-)
+
+diff --git a/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi b/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi
+index 768a2a4..2a5cc5e 100644
+--- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi
+@@ -81,13 +81,16 @@
+			pinctrl-names = "default";
+			status = "ok";
+			cs-gpios = <&tlmm 54 0>;
++			num-cs = <1>;
+
+-			mx25l25635e@0 {
++			m25p80@0 {
+				#address-cells = <1>;
+				#size-cells = <1>;
+				reg = <0>;
+-				compatible = "mx25l25635e";
++				compatible = "n25q128a11";
++				linux,modalias = "m25p80", "n25q128a11";
+				spi-max-frequency = <24000000>;
++				use-default-sizes;
+			};
+		};
+
+diff --git a/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi b/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi
+index e817432..b68fc1a 100644
+--- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi
+@@ -94,18 +94,20 @@
+			status = "ok";
+		};
+
+-		spi_0: spi@78b5000 {
++		spi_0: spi@78b5000 { /* BLSP1 QUP1 */
+			pinctrl-0 = <&spi_0_pins>;
+			pinctrl-names = "default";
+			status = "ok";
+			cs-gpios = <&tlmm 12 0>;
+
+-			mx25l25635e@0 {
++			m25p80@0 {
+				#address-cells = <1>;
+				#size-cells = <1>;
+				reg = <0>;
+-				compatible = "mx25l25635e";
++				compatible = "n25q128a11";
++				linux,modalias = "m25p80", "n25q128a11";
+				spi-max-frequency = <24000000>;
++				use-default-sizes;
+			};
+		};
+
+diff --git a/arch/arm/boot/dts/qcom-ipq4019.dtsi b/arch/arm/boot/dts/qcom-ipq4019.dtsi
+index 7013c85..be5b6f7 100644
+--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
+@@ -506,6 +506,7 @@
+		wifi0: wifi@a000000 {
+			compatible = "qcom,ipq4019-wifi";
+			reg = <0xa000000 0x200000>;
++			core-id = <0x0>;
+			resets = <&gcc WIFI0_CPU_INIT_RESET
+				  &gcc WIFI0_RADIO_SRIF_RESET
+				  &gcc WIFI0_RADIO_WARM_RESET
+@@ -542,12 +543,15 @@
+					   "msi8",  "msi9", "msi10", "msi11",
+					  "msi12", "msi13", "msi14", "msi15",
+					  "legacy";
+-			status = "disabled";
++			status = "ok";
++			qca,msi_addr = <0x0b006040>;
++			qca,msi_base = <0x40>;
+		};
+
+		wifi1: wifi@a800000 {
+			compatible = "qcom,ipq4019-wifi";
+			reg = <0xa800000 0x200000>;
++			core-id = <0x1>;
+			resets = <&gcc WIFI1_CPU_INIT_RESET
+				  &gcc WIFI1_RADIO_SRIF_RESET
+				  &gcc WIFI1_RADIO_WARM_RESET
+@@ -584,7 +588,9 @@
+					   "msi8",  "msi9", "msi10", "msi11",
+					  "msi12", "msi13", "msi14", "msi15",
+					  "legacy";
+-			status = "disabled";
++			status = "ok";
++			qca,msi_addr = <0x0b006040>;
++			qca,msi_base = <0x50>;
+		};
+	};
+ };
+--
+2.7.2
diff --git a/target/linux/ipq806x/patches-4.9/852-ipq4019-pinctrl-Updated-various-Pin-definitions.patch b/target/linux/ipq806x/patches-4.9/852-ipq4019-pinctrl-Updated-various-Pin-definitions.patch
new file mode 100644
index 0000000..4267d47
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/852-ipq4019-pinctrl-Updated-various-Pin-definitions.patch
@@ -0,0 +1,1332 @@ 
+From fc6cf61517b8b4ab4678659936fc7572f699d6e7 Mon Sep 17 00:00:00 2001
+From: Ram Chandra Jangir <rjangir@codeaurora.org>
+Date: Tue, 28 Mar 2017 14:00:00 +0530
+Subject: [PATCH] ipq4019: pinctrl: Updated various Pin definitions
+
+Populate default values for various GPIO functions
+
+Signed-off-by: Ram Chandra Jangir <rjangir@codeaurora.org>
+---
+ drivers/pinctrl/qcom/pinctrl-ipq4019.c | 1189 +++++++++++++++++++++++++++++---
+ 1 file changed, 1111 insertions(+), 78 deletions(-)
+
+diff --git a/drivers/pinctrl/qcom/pinctrl-ipq4019.c b/drivers/pinctrl/qcom/pinctrl-ipq4019.c
+index 743d1f4..571eb51 100644
+--- a/drivers/pinctrl/qcom/pinctrl-ipq4019.c
++++ b/drivers/pinctrl/qcom/pinctrl-ipq4019.c
+@@ -276,16 +276,531 @@ DECLARE_QCA_GPIO_PINS(99);
+
+
+ enum ipq4019_functions {
++	qca_mux_rmii0_refclk,
++	qca_mux_wifi0_rfsilient0,
++	qca_mux_wifi1_rfsilient0,
++	qca_mux_smart2,
++	qca_mux_led4,
++	qca_mux_wifi0_cal,
++	qca_mux_wifi1_cal,
++	qca_mux_wifi_wci0,
++	qca_mux_rmii0_dv,
++	qca_mux_wifi_wci1,
++	qca_mux_rmii1_refclk,
++	qca_mux_blsp_spi1,
++	qca_mux_led5,
++	qca_mux_rmii10,
++	qca_mux_led6,
++	qca_mux_rmii11,
++	qca_mux_led7,
++	qca_mux_rmii1_dv,
++	qca_mux_led8,
++	qca_mux_rmii1_tx,
++	qca_mux_aud_pin,
++	qca_mux_led9,
++	qca_mux_rmii1_rx,
++	qca_mux_led10,
++	qca_mux_wifi0_rfsilient1,
++	qca_mux_wifi1_rfsilient1,
++	qca_mux_led11,
++	qca_mux_boot7,
++	qca_mux_qpic_pad,
++	qca_mux_pcie_clk,
++	qca_mux_tm_clk0,
++	qca_mux_wifi00,
++	qca_mux_wifi10,
++	qca_mux_mdio1,
++	qca_mux_prng_rosc,
++	qca_mux_dbg_out,
++	qca_mux_tm0,
++	qca_mux_wifi01,
++	qca_mux_wifi11,
++	qca_mux_atest_char3,
++	qca_mux_pmu0,
++	qca_mux_boot8,
++	qca_mux_tm1,
++	qca_mux_atest_char2,
++	qca_mux_pmu1,
++	qca_mux_boot9,
++	qca_mux_tm2,
++	qca_mux_atest_char1,
++	qca_mux_tm_ack,
++	qca_mux_wifi03,
++	qca_mux_wifi13,
++	qca_mux_qpic_pad4,
++	qca_mux_atest_char0,
++	qca_mux_tm3,
++	qca_mux_wifi02,
++	qca_mux_wifi12,
++	qca_mux_qpic_pad5,
++	qca_mux_smart3,
++	qca_mux_wcss0_dbg14,
++	qca_mux_tm4,
++	qca_mux_wifi04,
++	qca_mux_wifi14,
++	qca_mux_qpic_pad6,
++	qca_mux_wcss0_dbg15,
++	qca_mux_qdss_tracectl_a,
++	qca_mux_boot18,
++	qca_mux_tm5,
++	qca_mux_qpic_pad7,
++	qca_mux_atest_char,
++	qca_mux_wcss0_dbg4,
++	qca_mux_qdss_traceclk_a,
++	qca_mux_boot19,
++	qca_mux_tm6,
++	qca_mux_wcss0_dbg5,
++	qca_mux_qdss_cti_trig_out_a0,
++	qca_mux_boot14,
++	qca_mux_tm7,
++	qca_mux_chip_rst,
++	qca_mux_wcss0_dbg6,
++	qca_mux_qdss_cti_trig_out_b0,
++	qca_mux_boot11,
++	qca_mux_tm8,
++	qca_mux_wcss0_dbg7,
++	qca_mux_wcss1_dbg7,
++	qca_mux_boot20,
++	qca_mux_tm9,
++	qca_mux_qpic_pad1,
++	qca_mux_wcss0_dbg8,
++	qca_mux_wcss1_dbg8,
++	qca_mux_qpic_pad2,
++	qca_mux_wcss0_dbg9,
++	qca_mux_wcss1_dbg9,
++	qca_mux_qpic_pad3,
++	qca_mux_wcss0_dbg10,
++	qca_mux_wcss1_dbg10,
++	qca_mux_qpic_pad0,
++	qca_mux_wcss0_dbg11,
++	qca_mux_wcss1_dbg11,
++	qca_mux_qpic_pad8,
++	qca_mux_wcss0_dbg12,
++	qca_mux_wcss1_dbg12,
++	qca_mux_wifi034,
++	qca_mux_wifi134,
++	qca_mux_jtag_tdi,
+	qca_mux_gpio,
++	qca_mux_i2s_rx_bclk,
++	qca_mux_jtag_tck,
++	qca_mux_i2s_rx_fsync,
++	qca_mux_jtag_tms,
++	qca_mux_i2s_rxd,
++	qca_mux_smart0,
++	qca_mux_jtag_tdo,
++	qca_mux_jtag_rst,
++	qca_mux_jtag_trst,
++	qca_mux_mdio0,
++	qca_mux_wcss0_dbg18,
++	qca_mux_wcss1_dbg18,
++	qca_mux_qdss_tracedata_a,
++	qca_mux_mdc,
++	qca_mux_wcss0_dbg19,
++	qca_mux_wcss1_dbg19,
+	qca_mux_blsp_uart1,
++	qca_mux_wifi0_uart,
++	qca_mux_wifi1_uart,
++	qca_mux_smart1,
++	qca_mux_wcss0_dbg20,
++	qca_mux_wcss1_dbg20,
++	qca_mux_wifi0_uart0,
++	qca_mux_wifi1_uart0,
++	qca_mux_wcss0_dbg21,
++	qca_mux_wcss1_dbg21,
+	qca_mux_blsp_i2c0,
++	qca_mux_wcss0_dbg22,
++	qca_mux_wcss1_dbg22,
++	qca_mux_wcss0_dbg23,
++	qca_mux_wcss1_dbg23,
++	qca_mux_blsp_spi0,
+	qca_mux_blsp_i2c1,
++	qca_mux_wcss0_dbg24,
++	qca_mux_wcss1_dbg24,
++	qca_mux_wcss0_dbg25,
++	qca_mux_wcss1_dbg25,
++	qca_mux_wcss0_dbg26,
++	qca_mux_wcss1_dbg26,
++	qca_mux_wcss0_dbg,
++	qca_mux_wcss1_dbg,
+	qca_mux_blsp_uart0,
+-	qca_mux_blsp_spi1,
+-	qca_mux_blsp_spi0,
++	qca_mux_led0,
++	qca_mux_wcss0_dbg28,
++	qca_mux_wcss1_dbg28,
++	qca_mux_led1,
++	qca_mux_wcss0_dbg29,
++	qca_mux_wcss1_dbg29,
++	qca_mux_wifi0_uart1,
++	qca_mux_wifi1_uart1,
++	qca_mux_wcss0_dbg30,
++	qca_mux_wcss1_dbg30,
++	qca_mux_wcss0_dbg31,
++	qca_mux_wcss1_dbg31,
++	qca_mux_i2s_rx_mclk,
++	qca_mux_wcss0_dbg16,
++	qca_mux_wcss1_dbg16,
++	qca_mux_wcss0_dbg17,
++	qca_mux_wcss1_dbg17,
++	qca_mux_rgmii0,
++	qca_mux_sdio0,
++	qca_mux_rgmii1,
++	qca_mux_sdio1,
++	qca_mux_rgmii2,
++	qca_mux_i2s_tx_mclk,
++	qca_mux_sdio2,
++	qca_mux_rgmii3,
++	qca_mux_i2s_tx_bclk,
++	qca_mux_sdio3,
++	qca_mux_rgmii_rx,
++	qca_mux_i2s_tx_fsync,
++	qca_mux_sdio_clk,
++	qca_mux_rgmii_txc,
++	qca_mux_i2s_td1,
++	qca_mux_sdio_cmd,
++	qca_mux_i2s_td2,
++	qca_mux_sdio4,
++	qca_mux_i2s_td3,
++	qca_mux_sdio5,
++	qca_mux_audio_pwm0,
++	qca_mux_sdio6,
++	qca_mux_audio_pwm1,
++	qca_mux_wcss0_dbg27,
++	qca_mux_wcss1_dbg27,
++	qca_mux_sdio7,
++	qca_mux_rgmii_rxc,
++	qca_mux_audio_pwm2,
++	qca_mux_rgmii_tx,
++	qca_mux_audio_pwm3,
++	qca_mux_boot2,
++	qca_mux_i2s_spdif_in,
++	qca_mux_i2s_spdif_out,
++	qca_mux_rmii00,
++	qca_mux_led2,
++	qca_mux_rmii01,
++	qca_mux_wifi0_wci,
++	qca_mux_wifi1_wci,
++	qca_mux_boot4,
++	qca_mux_rmii0_tx,
++	qca_mux_boot5,
++	qca_mux_rmii0_rx,
++	qca_mux_pcie_clk1,
++	qca_mux_led3,
++	qca_mux_sdio_cd,
+	qca_mux_NA,
+ };
+
++static const char * const rmii0_refclk_groups[] = {
++	"gpio40",
++};
++static const char * const wifi0_rfsilient0_groups[] = {
++	"gpio40",
++};
++static const char * const wifi1_rfsilient0_groups[] = {
++	"gpio40",
++};
++static const char * const smart2_groups[] = {
++	"gpio40", "gpio41", "gpio48", "gpio49",
++};
++static const char * const led4_groups[] = {
++	"gpio40",
++};
++static const char * const wifi0_cal_groups[] = {
++	"gpio41", "gpio51",
++};
++static const char * const wifi1_cal_groups[] = {
++	"gpio41", "gpio51",
++};
++static const char * const wifi_wci0_groups[] = {
++	"gpio42",
++};
++static const char * const rmii0_dv_groups[] = {
++	"gpio43",
++};
++static const char * const wifi_wci1_groups[] = {
++	"gpio43",
++};
++static const char * const rmii1_refclk_groups[] = {
++	"gpio44",
++};
++static const char * const blsp_spi1_groups[] = {
++	"gpio44", "gpio45", "gpio46", "gpio47",
++};
++static const char * const led5_groups[] = {
++	"gpio44",
++};
++static const char * const rmii10_groups[] = {
++	"gpio45", "gpio50",
++};
++static const char * const led6_groups[] = {
++	"gpio45",
++};
++static const char * const rmii11_groups[] = {
++	"gpio46", "gpio51",
++};
++static const char * const led7_groups[] = {
++	"gpio46",
++};
++static const char * const rmii1_dv_groups[] = {
++	"gpio47",
++};
++static const char * const led8_groups[] = {
++	"gpio47",
++};
++static const char * const rmii1_tx_groups[] = {
++	"gpio48",
++};
++static const char * const aud_pin_groups[] = {
++	"gpio48", "gpio49", "gpio50", "gpio51",
++};
++static const char * const led9_groups[] = {
++	"gpio48",
++};
++static const char * const rmii1_rx_groups[] = {
++	"gpio49",
++};
++static const char * const led10_groups[] = {
++	"gpio49",
++};
++static const char * const wifi0_rfsilient1_groups[] = {
++	"gpio50",
++};
++static const char * const wifi1_rfsilient1_groups[] = {
++	"gpio50",
++};
++static const char * const led11_groups[] = {
++	"gpio50",
++};
++static const char * const boot7_groups[] = {
++	"gpio51",
++};
++static const char * const qpic_pad_groups[] = {
++	"gpio52", "gpio53", "gpio54", "gpio55", "gpio56", "gpio61", "gpio62",
++	"gpio63", "gpio69",
++};
++static const char * const pcie_clk_groups[] = {
++	"gpio52",
++};
++static const char * const tm_clk0_groups[] = {
++	"gpio52",
++};
++static const char * const wifi00_groups[] = {
++	"gpio52",
++};
++static const char * const wifi10_groups[] = {
++	"gpio52",
++};
++static const char * const mdio1_groups[] = {
++	"gpio53",
++};
++static const char * const prng_rosc_groups[] = {
++	"gpio53",
++};
++static const char * const dbg_out_groups[] = {
++	"gpio53",
++};
++static const char * const tm0_groups[] = {
++	"gpio53",
++};
++static const char * const wifi01_groups[] = {
++	"gpio53",
++};
++static const char * const wifi11_groups[] = {
++	"gpio53",
++};
++static const char * const atest_char3_groups[] = {
++	"gpio54",
++};
++static const char * const pmu0_groups[] = {
++	"gpio54",
++};
++static const char * const boot8_groups[] = {
++	"gpio54",
++};
++static const char * const tm1_groups[] = {
++	"gpio54",
++};
++static const char * const atest_char2_groups[] = {
++	"gpio55",
++};
++static const char * const pmu1_groups[] = {
++	"gpio55",
++};
++static const char * const boot9_groups[] = {
++	"gpio55",
++};
++static const char * const tm2_groups[] = {
++	"gpio55",
++};
++static const char * const atest_char1_groups[] = {
++	"gpio56",
++};
++static const char * const tm_ack_groups[] = {
++	"gpio56",
++};
++static const char * const wifi03_groups[] = {
++	"gpio56",
++};
++static const char * const wifi13_groups[] = {
++	"gpio56",
++};
++static const char * const qpic_pad4_groups[] = {
++	"gpio57",
++};
++static const char * const atest_char0_groups[] = {
++	"gpio57",
++};
++static const char * const tm3_groups[] = {
++	"gpio57",
++};
++static const char * const wifi02_groups[] = {
++	"gpio57",
++};
++static const char * const wifi12_groups[] = {
++	"gpio57",
++};
++static const char * const qpic_pad5_groups[] = {
++	"gpio58",
++};
++static const char * const smart3_groups[] = {
++	"gpio58", "gpio59", "gpio60", "gpio61",
++};
++static const char * const wcss0_dbg14_groups[] = {
++	"gpio58",
++};
++static const char * const tm4_groups[] = {
++	"gpio58",
++};
++static const char * const wifi04_groups[] = {
++	"gpio58",
++};
++static const char * const wifi14_groups[] = {
++	"gpio58",
++};
++static const char * const qpic_pad6_groups[] = {
++	"gpio59",
++};
++static const char * const wcss0_dbg15_groups[] = {
++	"gpio59",
++};
++static const char * const qdss_tracectl_a_groups[] = {
++	"gpio59",
++};
++static const char * const boot18_groups[] = {
++	"gpio59",
++};
++static const char * const tm5_groups[] = {
++	"gpio59",
++};
++static const char * const qpic_pad7_groups[] = {
++	"gpio60",
++};
++static const char * const atest_char_groups[] = {
++	"gpio60",
++};
++static const char * const wcss0_dbg4_groups[] = {
++	"gpio60",
++};
++static const char * const qdss_traceclk_a_groups[] = {
++	"gpio60",
++};
++static const char * const boot19_groups[] = {
++	"gpio60",
++};
++static const char * const tm6_groups[] = {
++	"gpio60",
++};
++static const char * const wcss0_dbg5_groups[] = {
++	"gpio61",
++};
++static const char * const qdss_cti_trig_out_a0_groups[] = {
++	"gpio61",
++};
++static const char * const boot14_groups[] = {
++	"gpio61",
++};
++static const char * const tm7_groups[] = {
++	"gpio61",
++};
++static const char * const chip_rst_groups[] = {
++	"gpio62",
++};
++static const char * const wcss0_dbg6_groups[] = {
++	"gpio62",
++};
++static const char * const qdss_cti_trig_out_b0_groups[] = {
++	"gpio62",
++};
++static const char * const boot11_groups[] = {
++	"gpio62",
++};
++static const char * const tm8_groups[] = {
++	"gpio62",
++};
++static const char * const wcss0_dbg7_groups[] = {
++	"gpio63",
++};
++static const char * const wcss1_dbg7_groups[] = {
++	"gpio63",
++};
++static const char * const boot20_groups[] = {
++	"gpio63",
++};
++static const char * const tm9_groups[] = {
++	"gpio63",
++};
++static const char * const qpic_pad1_groups[] = {
++	"gpio64",
++};
++static const char * const wcss0_dbg8_groups[] = {
++	"gpio64",
++};
++static const char * const wcss1_dbg8_groups[] = {
++	"gpio64",
++};
++static const char * const qpic_pad2_groups[] = {
++	"gpio65",
++};
++static const char * const wcss0_dbg9_groups[] = {
++	"gpio65",
++};
++static const char * const wcss1_dbg9_groups[] = {
++	"gpio65",
++};
++static const char * const qpic_pad3_groups[] = {
++	"gpio66",
++};
++static const char * const wcss0_dbg10_groups[] = {
++	"gpio66",
++};
++static const char * const wcss1_dbg10_groups[] = {
++	"gpio66",
++};
++static const char * const qpic_pad0_groups[] = {
++	"gpio67",
++};
++static const char * const wcss0_dbg11_groups[] = {
++	"gpio67",
++};
++static const char * const wcss1_dbg11_groups[] = {
++	"gpio67",
++};
++static const char * const qpic_pad8_groups[] = {
++	"gpio68",
++};
++static const char * const wcss0_dbg12_groups[] = {
++	"gpio68",
++};
++static const char * const wcss1_dbg12_groups[] = {
++	"gpio68",
++};
++static const char * const wifi034_groups[] = {
++	"gpio98",
++};
++static const char * const wifi134_groups[] = {
++	"gpio98",
++};
++static const char * const jtag_tdi_groups[] = {
++	"gpio0",
++};
+ static const char * const gpio_groups[] = {
+	"gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7",
+	"gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14",
+@@ -303,13 +818,103 @@ static const char * const gpio_groups[] = {
+	"gpio92", "gpio93", "gpio94", "gpio95", "gpio96", "gpio97", "gpio98",
+	"gpio99",
+ };
+-
++static const char * const i2s_rx_bclk_groups[] = {
++	"gpio0", "gpio21", "gpio60",
++};
++static const char * const jtag_tck_groups[] = {
++	"gpio1",
++};
++static const char * const i2s_rx_fsync_groups[] = {
++	"gpio1", "gpio22", "gpio61",
++};
++static const char * const jtag_tms_groups[] = {
++	"gpio2",
++};
++static const char * const i2s_rxd_groups[] = {
++	"gpio2", "gpio23", "gpio63",
++};
++static const char * const smart0_groups[] = {
++	"gpio0", "gpio1", "gpio2", "gpio5", "gpio44", "gpio45", "gpio46",
++	"gpio47",
++};
++static const char * const jtag_tdo_groups[] = {
++	"gpio3",
++};
++static const char * const jtag_rst_groups[] = {
++	"gpio4",
++};
++static const char * const jtag_trst_groups[] = {
++	"gpio5",
++};
++static const char * const mdio0_groups[] = {
++	"gpio6",
++};
++static const char * const wcss0_dbg18_groups[] = {
++	"gpio6", "gpio22", "gpio39",
++};
++static const char * const wcss1_dbg18_groups[] = {
++	"gpio6", "gpio22", "gpio39",
++};
++static const char * const qdss_tracedata_a_groups[] = {
++	"gpio6", "gpio7", "gpio8", "gpio9", "gpio10", "gpio11", "gpio16",
++	"gpio17", "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", "gpio42",
++	"gpio43",
++};
++static const char * const mdc_groups[] = {
++	"gpio7", "gpio52",
++};
++static const char * const wcss0_dbg19_groups[] = {
++	"gpio7", "gpio23", "gpio40",
++};
++static const char * const wcss1_dbg19_groups[] = {
++	"gpio7", "gpio23", "gpio40",
++};
+ static const char * const blsp_uart1_groups[] = {
+	"gpio8", "gpio9", "gpio10", "gpio11",
+ };
++static const char * const wifi0_uart_groups[] = {
++	"gpio8", "gpio9", "gpio11", "gpio19", "gpio62",
++};
++static const char * const wifi1_uart_groups[] = {
++	"gpio8", "gpio11", "gpio19", "gpio62", "gpio63",
++};
++static const char * const smart1_groups[] = {
++	"gpio8", "gpio9", "gpio16", "gpio17", "gpio58", "gpio59", "gpio60",
++	"gpio61",
++};
++static const char * const wcss0_dbg20_groups[] = {
++	"gpio8", "gpio24", "gpio41",
++};
++static const char * const wcss1_dbg20_groups[] = {
++	"gpio8", "gpio24", "gpio41",
++};
++static const char * const wifi0_uart0_groups[] = {
++	"gpio9", "gpio10",
++};
++static const char * const wifi1_uart0_groups[] = {
++	"gpio9", "gpio10",
++};
++static const char * const wcss0_dbg21_groups[] = {
++	"gpio9", "gpio25", "gpio42",
++};
++static const char * const wcss1_dbg21_groups[] = {
++	"gpio9", "gpio25", "gpio42",
++};
+ static const char * const blsp_i2c0_groups[] = {
+	"gpio10", "gpio11", "gpio20", "gpio21", "gpio58", "gpio59",
+ };
++static const char * const wcss0_dbg22_groups[] = {
++	"gpio10", "gpio26", "gpio43",
++};
++static const char * const wcss1_dbg22_groups[] = {
++	"gpio10", "gpio26", "gpio43",
++};
++static const char * const wcss0_dbg23_groups[] = {
++	"gpio11", "gpio27", "gpio44",
++};
++static const char * const wcss1_dbg23_groups[] = {
++	"gpio11", "gpio27", "gpio44",
++};
+ static const char * const blsp_spi0_groups[] = {
+	"gpio12", "gpio13", "gpio14", "gpio15", "gpio45",
+	"gpio54", "gpio55", "gpio56", "gpio57",
+@@ -317,94 +922,582 @@ static const char * const blsp_spi0_groups[] = {
+ static const char * const blsp_i2c1_groups[] = {
+	"gpio12", "gpio13", "gpio34", "gpio35",
+ };
++static const char * const wcss0_dbg24_groups[] = {
++	"gpio12", "gpio28", "gpio45",
++};
++static const char * const wcss1_dbg24_groups[] = {
++	"gpio12", "gpio28", "gpio45",
++};
++static const char * const wcss0_dbg25_groups[] = {
++	"gpio13", "gpio29", "gpio46",
++};
++static const char * const wcss1_dbg25_groups[] = {
++	"gpio13", "gpio29", "gpio46",
++};
++static const char * const wcss0_dbg26_groups[] = {
++	"gpio14", "gpio30", "gpio47",
++};
++static const char * const wcss1_dbg26_groups[] = {
++	"gpio14", "gpio30", "gpio47",
++};
++static const char * const wcss0_dbg_groups[] = {
++	"gpio15", "gpio69",
++};
++static const char * const wcss1_dbg_groups[] = {
++	"gpio15",
++};
+ static const char * const blsp_uart0_groups[] = {
+	"gpio16", "gpio17", "gpio60", "gpio61",
+ };
+-static const char * const blsp_spi1_groups[] = {
+-	"gpio44", "gpio45", "gpio46", "gpio47",
++static const char * const led0_groups[] = {
++	"gpio16", "gpio36", "gpio60",
++};
++static const char * const wcss0_dbg28_groups[] = {
++	"gpio16", "gpio32", "gpio49",
++};
++static const char * const wcss1_dbg28_groups[] = {
++	"gpio16", "gpio32", "gpio49",
++};
++static const char * const led1_groups[] = {
++	"gpio17", "gpio37", "gpio61",
++};
++static const char * const wcss0_dbg29_groups[] = {
++	"gpio17", "gpio33", "gpio50",
++};
++static const char * const wcss1_dbg29_groups[] = {
++	"gpio17", "gpio33", "gpio50",
++};
++static const char * const wifi0_uart1_groups[] = {
++	"gpio18", "gpio63",
++};
++static const char * const wifi1_uart1_groups[] = {
++	"gpio18", "gpio63",
++};
++static const char * const wcss0_dbg30_groups[] = {
++	"gpio18", "gpio34", "gpio51",
++};
++static const char * const wcss1_dbg30_groups[] = {
++	"gpio18", "gpio34", "gpio51",
++};
++static const char * const wcss0_dbg31_groups[] = {
++	"gpio19", "gpio35", "gpio52",
++};
++static const char * const wcss1_dbg31_groups[] = {
++	"gpio19", "gpio35",
++};
++static const char * const i2s_rx_mclk_groups[] = {
++	"gpio20", "gpio58",
++};
++static const char * const wcss0_dbg16_groups[] = {
++	"gpio20", "gpio37",
++};
++static const char * const wcss1_dbg16_groups[] = {
++	"gpio20", "gpio37",
++};
++static const char * const wcss0_dbg17_groups[] = {
++	"gpio21", "gpio38",
++};
++static const char * const wcss1_dbg17_groups[] = {
++	"gpio21", "gpio38",
++};
++static const char * const rgmii0_groups[] = {
++	"gpio22", "gpio28",
++};
++static const char * const sdio0_groups[] = {
++	"gpio23",
++};
++static const char * const rgmii1_groups[] = {
++	"gpio23", "gpio29",
++};
++static const char * const sdio1_groups[] = {
++	"gpio24",
++};
++static const char * const rgmii2_groups[] = {
++	"gpio24", "gpio30",
++};
++static const char * const i2s_tx_mclk_groups[] = {
++	"gpio24", "gpio52",
++};
++static const char * const sdio2_groups[] = {
++	"gpio25",
++};
++static const char * const rgmii3_groups[] = {
++	"gpio25", "gpio31",
++};
++static const char * const i2s_tx_bclk_groups[] = {
++	"gpio25", "gpio53", "gpio60",
++};
++static const char * const sdio3_groups[] = {
++	"gpio26",
++};
++static const char * const rgmii_rx_groups[] = {
++	"gpio26",
++};
++static const char * const i2s_tx_fsync_groups[] = {
++	"gpio26", "gpio57", "gpio61",
++};
++static const char * const sdio_clk_groups[] = {
++	"gpio27",
++};
++static const char * const rgmii_txc_groups[] = {
++	"gpio27",
++};
++static const char * const i2s_td1_groups[] = {
++	"gpio27", "gpio54", "gpio63",
++};
++static const char * const sdio_cmd_groups[] = {
++	"gpio28",
++};
++static const char * const i2s_td2_groups[] = {
++	"gpio28", "gpio55",
++};
++static const char * const sdio4_groups[] = {
++	"gpio29",
++};
++static const char * const i2s_td3_groups[] = {
++	"gpio29", "gpio56",
++};
++static const char * const sdio5_groups[] = {
++	"gpio30",
++};
++static const char * const audio_pwm0_groups[] = {
++	"gpio30", "gpio64",
++};
++static const char * const sdio6_groups[] = {
++	"gpio31",
++};
++static const char * const audio_pwm1_groups[] = {
++	"gpio31", "gpio65",
++};
++static const char * const wcss0_dbg27_groups[] = {
++	"gpio31", "gpio48",
++};
++static const char * const wcss1_dbg27_groups[] = {
++	"gpio31", "gpio48",
++};
++static const char * const sdio7_groups[] = {
++	"gpio32",
++};
++static const char * const rgmii_rxc_groups[] = {
++	"gpio32",
++};
++static const char * const audio_pwm2_groups[] = {
++	"gpio32", "gpio66",
++};
++static const char * const rgmii_tx_groups[] = {
++	"gpio33",
++};
++static const char * const audio_pwm3_groups[] = {
++	"gpio33", "gpio67",
++};
++static const char * const boot2_groups[] = {
++	"gpio33",
++};
++static const char * const i2s_spdif_in_groups[] = {
++	"gpio34", "gpio59", "gpio63",
++};
++static const char * const i2s_spdif_out_groups[] = {
++	"gpio35", "gpio62", "gpio63",
++};
++static const char * const rmii00_groups[] = {
++	"gpio36", "gpio41",
++};
++static const char * const led2_groups[] = {
++	"gpio36", "gpio38", "gpio58",
++};
++static const char * const rmii01_groups[] = {
++	"gpio37", "gpio42",
++};
++static const char * const wifi0_wci_groups[] = {
++	"gpio37",
++};
++static const char * const wifi1_wci_groups[] = {
++	"gpio37",
++};
++static const char * const boot4_groups[] = {
++	"gpio37",
++};
++static const char * const rmii0_tx_groups[] = {
++	"gpio38",
++};
++static const char * const boot5_groups[] = {
++	"gpio38",
++};
++static const char * const rmii0_rx_groups[] = {
++	"gpio39",
++};
++static const char * const pcie_clk1_groups[] = {
++	"gpio39",
++};
++static const char * const led3_groups[] = {
++	"gpio39",
++};
++static const char * const sdio_cd_groups[] = {
++	"gpio22",
+ };
+
+ static const struct msm_function ipq4019_functions[] = {
++	FUNCTION(rmii0_refclk),
++	FUNCTION(wifi0_rfsilient0),
++	FUNCTION(wifi1_rfsilient0),
++	FUNCTION(smart2),
++	FUNCTION(led4),
++	FUNCTION(wifi0_cal),
++	FUNCTION(wifi1_cal),
++	FUNCTION(wifi_wci0),
++	FUNCTION(rmii0_dv),
++	FUNCTION(wifi_wci1),
++	FUNCTION(rmii1_refclk),
++	FUNCTION(blsp_spi1),
++	FUNCTION(led5),
++	FUNCTION(rmii10),
++	FUNCTION(led6),
++	FUNCTION(rmii11),
++	FUNCTION(led7),
++	FUNCTION(rmii1_dv),
++	FUNCTION(led8),
++	FUNCTION(rmii1_tx),
++	FUNCTION(aud_pin),
++	FUNCTION(led9),
++	FUNCTION(rmii1_rx),
++	FUNCTION(led10),
++	FUNCTION(wifi0_rfsilient1),
++	FUNCTION(wifi1_rfsilient1),
++	FUNCTION(led11),
++	FUNCTION(boot7),
++	FUNCTION(qpic_pad),
++	FUNCTION(pcie_clk),
++	FUNCTION(tm_clk0),
++	FUNCTION(wifi00),
++	FUNCTION(wifi10),
++	FUNCTION(mdio1),
++	FUNCTION(prng_rosc),
++	FUNCTION(dbg_out),
++	FUNCTION(tm0),
++	FUNCTION(wifi01),
++	FUNCTION(wifi11),
++	FUNCTION(atest_char3),
++	FUNCTION(pmu0),
++	FUNCTION(boot8),
++	FUNCTION(tm1),
++	FUNCTION(atest_char2),
++	FUNCTION(pmu1),
++	FUNCTION(boot9),
++	FUNCTION(tm2),
++	FUNCTION(atest_char1),
++	FUNCTION(tm_ack),
++	FUNCTION(wifi03),
++	FUNCTION(wifi13),
++	FUNCTION(qpic_pad4),
++	FUNCTION(atest_char0),
++	FUNCTION(tm3),
++	FUNCTION(wifi02),
++	FUNCTION(wifi12),
++	FUNCTION(qpic_pad5),
++	FUNCTION(smart3),
++	FUNCTION(wcss0_dbg14),
++	FUNCTION(tm4),
++	FUNCTION(wifi04),
++	FUNCTION(wifi14),
++	FUNCTION(qpic_pad6),
++	FUNCTION(wcss0_dbg15),
++	FUNCTION(qdss_tracectl_a),
++	FUNCTION(boot18),
++	FUNCTION(tm5),
++	FUNCTION(qpic_pad7),
++	FUNCTION(atest_char),
++	FUNCTION(wcss0_dbg4),
++	FUNCTION(qdss_traceclk_a),
++	FUNCTION(boot19),
++	FUNCTION(tm6),
++	FUNCTION(wcss0_dbg5),
++	FUNCTION(qdss_cti_trig_out_a0),
++	FUNCTION(boot14),
++	FUNCTION(tm7),
++	FUNCTION(chip_rst),
++	FUNCTION(wcss0_dbg6),
++	FUNCTION(qdss_cti_trig_out_b0),
++	FUNCTION(boot11),
++	FUNCTION(tm8),
++	FUNCTION(wcss0_dbg7),
++	FUNCTION(wcss1_dbg7),
++	FUNCTION(boot20),
++	FUNCTION(tm9),
++	FUNCTION(qpic_pad1),
++	FUNCTION(wcss0_dbg8),
++	FUNCTION(wcss1_dbg8),
++	FUNCTION(qpic_pad2),
++	FUNCTION(wcss0_dbg9),
++	FUNCTION(wcss1_dbg9),
++	FUNCTION(qpic_pad3),
++	FUNCTION(wcss0_dbg10),
++	FUNCTION(wcss1_dbg10),
++	FUNCTION(qpic_pad0),
++	FUNCTION(wcss0_dbg11),
++	FUNCTION(wcss1_dbg11),
++	FUNCTION(qpic_pad8),
++	FUNCTION(wcss0_dbg12),
++	FUNCTION(wcss1_dbg12),
++	FUNCTION(wifi034),
++	FUNCTION(wifi134),
++	FUNCTION(jtag_tdi),
+	FUNCTION(gpio),
++	FUNCTION(i2s_rx_bclk),
++	FUNCTION(jtag_tck),
++	FUNCTION(i2s_rx_fsync),
++	FUNCTION(jtag_tms),
++	FUNCTION(i2s_rxd),
++	FUNCTION(smart0),
++	FUNCTION(jtag_tdo),
++	FUNCTION(jtag_rst),
++	FUNCTION(jtag_trst),
++	FUNCTION(mdio0),
++	FUNCTION(wcss0_dbg18),
++	FUNCTION(wcss1_dbg18),
++	FUNCTION(qdss_tracedata_a),
++	FUNCTION(mdc),
++	FUNCTION(wcss0_dbg19),
++	FUNCTION(wcss1_dbg19),
+	FUNCTION(blsp_uart1),
++	FUNCTION(wifi0_uart),
++	FUNCTION(wifi1_uart),
++	FUNCTION(smart1),
++	FUNCTION(wcss0_dbg20),
++	FUNCTION(wcss1_dbg20),
++	FUNCTION(wifi0_uart0),
++	FUNCTION(wifi1_uart0),
++	FUNCTION(wcss0_dbg21),
++	FUNCTION(wcss1_dbg21),
+	FUNCTION(blsp_i2c0),
++	FUNCTION(wcss0_dbg22),
++	FUNCTION(wcss1_dbg22),
++	FUNCTION(wcss0_dbg23),
++	FUNCTION(wcss1_dbg23),
++	FUNCTION(blsp_spi0),
+	FUNCTION(blsp_i2c1),
++	FUNCTION(wcss0_dbg24),
++	FUNCTION(wcss1_dbg24),
++	FUNCTION(wcss0_dbg25),
++	FUNCTION(wcss1_dbg25),
++	FUNCTION(wcss0_dbg26),
++	FUNCTION(wcss1_dbg26),
++	FUNCTION(wcss0_dbg),
++	FUNCTION(wcss1_dbg),
+	FUNCTION(blsp_uart0),
+-	FUNCTION(blsp_spi1),
+-	FUNCTION(blsp_spi0),
++	FUNCTION(led0),
++	FUNCTION(wcss0_dbg28),
++	FUNCTION(wcss1_dbg28),
++	FUNCTION(led1),
++	FUNCTION(wcss0_dbg29),
++	FUNCTION(wcss1_dbg29),
++	FUNCTION(wifi0_uart1),
++	FUNCTION(wifi1_uart1),
++	FUNCTION(wcss0_dbg30),
++	FUNCTION(wcss1_dbg30),
++	FUNCTION(wcss0_dbg31),
++	FUNCTION(wcss1_dbg31),
++	FUNCTION(i2s_rx_mclk),
++	FUNCTION(wcss0_dbg16),
++	FUNCTION(wcss1_dbg16),
++	FUNCTION(wcss0_dbg17),
++	FUNCTION(wcss1_dbg17),
++	FUNCTION(rgmii0),
++	FUNCTION(sdio0),
++	FUNCTION(rgmii1),
++	FUNCTION(sdio1),
++	FUNCTION(rgmii2),
++	FUNCTION(i2s_tx_mclk),
++	FUNCTION(sdio2),
++	FUNCTION(rgmii3),
++	FUNCTION(i2s_tx_bclk),
++	FUNCTION(sdio3),
++	FUNCTION(rgmii_rx),
++	FUNCTION(i2s_tx_fsync),
++	FUNCTION(sdio_clk),
++	FUNCTION(rgmii_txc),
++	FUNCTION(i2s_td1),
++	FUNCTION(sdio_cmd),
++	FUNCTION(i2s_td2),
++	FUNCTION(sdio4),
++	FUNCTION(i2s_td3),
++	FUNCTION(sdio5),
++	FUNCTION(audio_pwm0),
++	FUNCTION(sdio6),
++	FUNCTION(audio_pwm1),
++	FUNCTION(wcss0_dbg27),
++	FUNCTION(wcss1_dbg27),
++	FUNCTION(sdio7),
++	FUNCTION(rgmii_rxc),
++	FUNCTION(audio_pwm2),
++	FUNCTION(rgmii_tx),
++	FUNCTION(audio_pwm3),
++	FUNCTION(boot2),
++	FUNCTION(i2s_spdif_in),
++	FUNCTION(i2s_spdif_out),
++	FUNCTION(rmii00),
++	FUNCTION(led2),
++	FUNCTION(rmii01),
++	FUNCTION(wifi0_wci),
++	FUNCTION(wifi1_wci),
++	FUNCTION(boot4),
++	FUNCTION(rmii0_tx),
++	FUNCTION(boot5),
++	FUNCTION(rmii0_rx),
++	FUNCTION(pcie_clk1),
++	FUNCTION(led3),
++	FUNCTION(sdio_cd),
+ };
+
+ static const struct msm_pingroup ipq4019_groups[] = {
+-	PINGROUP(0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(1, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(2, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(3, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(4, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(5, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(6, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(7, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(8, blsp_uart1, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(9, blsp_uart1, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(10, blsp_uart1, NA, NA, blsp_i2c0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(11, blsp_uart1, NA, NA, blsp_i2c0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(12, blsp_spi0, blsp_i2c1, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(13, blsp_spi0, blsp_i2c1, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(14, blsp_spi0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(15, blsp_spi0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(16, blsp_uart0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(17, blsp_uart0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(18, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(19, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(20, blsp_i2c0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(21, blsp_i2c0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(22, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(23, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(24, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(25, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(26, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(27, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(28, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(29, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(30, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(31, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(32, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(33, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(34, blsp_i2c1, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(35, blsp_i2c1, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(36, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(37, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(38, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(39, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(40, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(41, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(42, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(43, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(44, NA, blsp_spi1, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(45, NA, blsp_spi1, blsp_spi0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(46, NA, blsp_spi1, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(47, NA, blsp_spi1, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(48, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(49, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(50, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(51, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(52, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(53, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(54, NA, blsp_spi0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(55, NA, blsp_spi0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(56, NA, blsp_spi0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(57, NA, blsp_spi0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(58, NA, NA, blsp_i2c0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(59, NA, blsp_i2c0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(60, NA, blsp_uart0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(61, NA, blsp_uart0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(62, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(63, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(64, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(65, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(66, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(67, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(68, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(69, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++	PINGROUP(0, jtag_tdi, smart0, i2s_rx_bclk, NA, NA, NA, NA, NA, NA, NA,
++		 NA, NA, NA, NA),
++	PINGROUP(1, jtag_tck, smart0, i2s_rx_fsync, NA, NA, NA, NA, NA, NA, NA,
++		 NA, NA, NA, NA),
++	PINGROUP(2, jtag_tms, smart0, i2s_rxd, NA, NA, NA, NA, NA, NA, NA, NA,
++		 NA, NA, NA),
++	PINGROUP(3, jtag_tdo, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,
++		 NA),
++	PINGROUP(4, jtag_rst, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,
++		 NA),
++	PINGROUP(5, jtag_trst, smart0, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,
++		 NA, NA),
++	PINGROUP(6, mdio0, NA, wcss0_dbg18, wcss1_dbg18, NA, qdss_tracedata_a,
++		 NA, NA, NA, NA, NA, NA, NA, NA),
++	PINGROUP(7, mdc, NA, wcss0_dbg19, wcss1_dbg19, NA, qdss_tracedata_a,
++		 NA, NA, NA, NA, NA, NA, NA, NA),
++	PINGROUP(8, blsp_uart1, wifi0_uart, wifi1_uart, smart1, NA,
++		 wcss0_dbg20, wcss1_dbg20, NA, qdss_tracedata_a, NA, NA, NA,
++		 NA, NA),
++	PINGROUP(9, blsp_uart1, wifi0_uart0, wifi1_uart0, smart1, wifi0_uart,
++		 NA, wcss0_dbg21, wcss1_dbg21, NA, qdss_tracedata_a, NA, NA,
++		 NA, NA),
++	PINGROUP(10, blsp_uart1, wifi0_uart0, wifi1_uart0, blsp_i2c0, NA,
++		 wcss0_dbg22, wcss1_dbg22, NA, qdss_tracedata_a, NA, NA, NA,
++		 NA, NA),
++	PINGROUP(11, blsp_uart1, wifi0_uart, wifi1_uart, blsp_i2c0, NA,
++		 wcss0_dbg23, wcss1_dbg23, NA, qdss_tracedata_a, NA, NA, NA,
++		 NA, NA),
++	PINGROUP(12, blsp_spi0, blsp_i2c1, NA, wcss0_dbg24, wcss1_dbg24, NA,
++		 NA, NA, NA, NA, NA, NA, NA, NA),
++	PINGROUP(13, blsp_spi0, blsp_i2c1, NA, wcss0_dbg25, wcss1_dbg25, NA,
++		 NA, NA, NA, NA, NA, NA, NA, NA),
++	PINGROUP(14, blsp_spi0, NA, wcss0_dbg26, wcss1_dbg26, NA, NA, NA, NA,
++		 NA, NA, NA, NA, NA, NA),
++	PINGROUP(15, blsp_spi0, NA, wcss0_dbg, wcss1_dbg, NA, NA, NA, NA, NA,
++		 NA, NA, NA, NA, NA),
++	PINGROUP(16, blsp_uart0, led0, smart1, NA, wcss0_dbg28, wcss1_dbg28,
++		 NA, qdss_tracedata_a, NA, NA, NA, NA, NA, NA),
++	PINGROUP(17, blsp_uart0, led1, smart1, NA, wcss0_dbg29, wcss1_dbg29,
++		 NA, qdss_tracedata_a, NA, NA, NA, NA, NA, NA),
++	PINGROUP(18, wifi0_uart1, wifi1_uart1, NA, wcss0_dbg30, wcss1_dbg30,
++		 NA, NA, NA, NA, NA, NA, NA, NA, NA),
++	PINGROUP(19, wifi0_uart, wifi1_uart, NA, wcss0_dbg31, wcss1_dbg31, NA,
++		 NA, NA, NA, NA, NA, NA, NA, NA),
++	PINGROUP(20, blsp_i2c0, i2s_rx_mclk, NA, wcss0_dbg16, wcss1_dbg16, NA,
++		 NA, NA, NA, NA, NA, NA, NA, NA),
++	PINGROUP(21, blsp_i2c0, i2s_rx_bclk, NA, wcss0_dbg17, wcss1_dbg17, NA,
++		 NA, NA, NA, NA, NA, NA, NA, NA),
++	PINGROUP(22, rgmii0, i2s_rx_fsync, NA, wcss0_dbg18, wcss1_dbg18, NA,
++		 NA, NA, NA, NA, NA, NA, NA, NA),
++	PINGROUP(23, sdio0, rgmii1, i2s_rxd, NA, wcss0_dbg19, wcss1_dbg19, NA,
++		 NA, NA, NA, NA, NA, NA, NA),
++	PINGROUP(24, sdio1, rgmii2, i2s_tx_mclk, NA, wcss0_dbg20, wcss1_dbg20,
++		 NA, NA, NA, NA, NA, NA, NA, NA),
++	PINGROUP(25, sdio2, rgmii3, i2s_tx_bclk, NA, wcss0_dbg21, wcss1_dbg21,
++		 NA, NA, NA, NA, NA, NA, NA, NA),
++	PINGROUP(26, sdio3, rgmii_rx, i2s_tx_fsync, NA, wcss0_dbg22,
++		 wcss1_dbg22, NA, NA, NA, NA, NA, NA, NA, NA),
++	PINGROUP(27, sdio_clk, rgmii_txc, i2s_td1, NA, wcss0_dbg23,
++		 wcss1_dbg23, NA, NA, NA, NA, NA, NA, NA, NA),
++	PINGROUP(28, sdio_cmd, rgmii0, i2s_td2, NA, wcss0_dbg24, wcss1_dbg24,
++		 NA, NA, NA, NA, NA, NA, NA, NA),
++	PINGROUP(29, sdio4, rgmii1, i2s_td3, NA, wcss0_dbg25, wcss1_dbg25, NA,
++		 NA, NA, NA, NA, NA, NA, NA),
++	PINGROUP(30, sdio5, rgmii2, audio_pwm0, NA, wcss0_dbg26, wcss1_dbg26,
++		 NA, NA, NA, NA, NA, NA, NA, NA),
++	PINGROUP(31, sdio6, rgmii3, audio_pwm1, NA, wcss0_dbg27, wcss1_dbg27,
++		 NA, NA, NA, NA, NA, NA, NA, NA),
++	PINGROUP(32, sdio7, rgmii_rxc, audio_pwm2, NA, wcss0_dbg28,
++		 wcss1_dbg28, NA, NA, NA, NA, NA, NA, NA, NA),
++	PINGROUP(33, rgmii_tx, audio_pwm3, NA, wcss0_dbg29, wcss1_dbg29, NA,
++		 boot2, NA, NA, NA, NA, NA, NA, NA),
++	PINGROUP(34, blsp_i2c1, i2s_spdif_in, NA, wcss0_dbg30, wcss1_dbg30, NA,
++		 NA, NA, NA, NA, NA, NA, NA, NA),
++	PINGROUP(35, blsp_i2c1, i2s_spdif_out, NA, wcss0_dbg31, wcss1_dbg31,
++		 NA, NA, NA, NA, NA, NA, NA, NA, NA),
++	PINGROUP(36, rmii00, led2, led0, NA, NA, NA, NA, NA, NA, NA, NA, NA,
++		 NA, NA),
++	PINGROUP(37, rmii01, wifi0_wci, wifi1_wci, led1, NA, NA, wcss0_dbg16,
++		 wcss1_dbg16, NA, qdss_tracedata_a, boot4, NA, NA, NA),
++	PINGROUP(38, rmii0_tx, led2, NA, NA, wcss0_dbg17, wcss1_dbg17, NA,
++		 qdss_tracedata_a, boot5, NA, NA, NA, NA, NA),
++	PINGROUP(39, rmii0_rx, pcie_clk1, led3, NA, NA, wcss0_dbg18,
++		 wcss1_dbg18, NA, NA, qdss_tracedata_a, NA, NA, NA, NA),
++	PINGROUP(40, rmii0_refclk, wifi0_rfsilient0, wifi1_rfsilient0, smart2,
++		 led4, NA, NA, wcss0_dbg19, wcss1_dbg19, NA, NA,
++		 qdss_tracedata_a, NA, NA),
++	PINGROUP(41, rmii00, wifi0_cal, wifi1_cal, smart2, NA, NA, wcss0_dbg20,
++		 wcss1_dbg20, NA, NA, qdss_tracedata_a, NA, NA, NA),
++	PINGROUP(42, rmii01, wifi_wci0, NA, NA, wcss0_dbg21, wcss1_dbg21, NA,
++		 NA, qdss_tracedata_a, NA, NA, NA, NA, NA),
++	PINGROUP(43, rmii0_dv, wifi_wci1, NA, NA, wcss0_dbg22, wcss1_dbg22, NA,
++		 NA, qdss_tracedata_a, NA, NA, NA, NA, NA),
++	PINGROUP(44, rmii1_refclk, blsp_spi1, smart0, led5, NA, NA,
++		 wcss0_dbg23, wcss1_dbg23, NA, NA, NA, NA, NA, NA),
++	PINGROUP(45, rmii10, blsp_spi1, blsp_spi0, smart0, led6, NA, NA,
++		 wcss0_dbg24, wcss1_dbg24, NA, NA, NA, NA, NA),
++	PINGROUP(46, rmii11, blsp_spi1, smart0, led7, NA, NA, wcss0_dbg25,
++		 wcss1_dbg25, NA, NA, NA, NA, NA, NA),
++	PINGROUP(47, rmii1_dv, blsp_spi1, smart0, led8, NA, NA, wcss0_dbg26,
++		 wcss1_dbg26, NA, NA, NA, NA, NA, NA),
++	PINGROUP(48, rmii1_tx, aud_pin, smart2, led9, NA, NA, wcss0_dbg27,
++		 wcss1_dbg27, NA, NA, NA, NA, NA, NA),
++	PINGROUP(49, rmii1_rx, aud_pin, smart2, led10, NA, NA, wcss0_dbg28,
++		 wcss1_dbg28, NA, NA, NA, NA, NA, NA),
++	PINGROUP(50, rmii10, aud_pin, wifi0_rfsilient1, wifi1_rfsilient1,
++		 led11, NA, NA, wcss0_dbg29, wcss1_dbg29, NA, NA, NA, NA, NA),
++	PINGROUP(51, rmii11, aud_pin, wifi0_cal, wifi1_cal, NA, NA,
++		 wcss0_dbg30, wcss1_dbg30, NA, boot7, NA, NA, NA, NA),
++	PINGROUP(52, qpic_pad, mdc, pcie_clk, i2s_tx_mclk, NA, NA, wcss0_dbg31,
++		 tm_clk0, wifi00, wifi10, NA, NA, NA, NA),
++	PINGROUP(53, qpic_pad, mdio1, i2s_tx_bclk, prng_rosc, dbg_out, tm0,
++		 wifi01, wifi11, NA, NA, NA, NA, NA, NA),
++	PINGROUP(54, qpic_pad, blsp_spi0, i2s_td1, atest_char3, pmu0, NA, NA,
++		 boot8, tm1, NA, NA, NA, NA, NA),
++	PINGROUP(55, qpic_pad, blsp_spi0, i2s_td2, atest_char2, pmu1, NA, NA,
++		 boot9, tm2, NA, NA, NA, NA, NA),
++	PINGROUP(56, qpic_pad, blsp_spi0, i2s_td3, atest_char1, NA, tm_ack,
++		 wifi03, wifi13, NA, NA, NA, NA, NA, NA),
++	PINGROUP(57, qpic_pad4, blsp_spi0, i2s_tx_fsync, atest_char0, NA, tm3,
++		 wifi02, wifi12, NA, NA, NA, NA, NA, NA),
++	PINGROUP(58, qpic_pad5, led2, blsp_i2c0, smart3, smart1, i2s_rx_mclk,
++		 NA, wcss0_dbg14, tm4, wifi04, wifi14, NA, NA, NA),
++	PINGROUP(59, qpic_pad6, blsp_i2c0, smart3, smart1, i2s_spdif_in, NA,
++		 NA, wcss0_dbg15, qdss_tracectl_a, boot18, tm5, NA, NA, NA),
++	PINGROUP(60, qpic_pad7, blsp_uart0, smart1, smart3, led0, i2s_tx_bclk,
++		 i2s_rx_bclk, atest_char, NA, wcss0_dbg4, qdss_traceclk_a,
++		 boot19, tm6, NA),
++	PINGROUP(61, qpic_pad, blsp_uart0, smart1, smart3, led1, i2s_tx_fsync,
++		 i2s_rx_fsync, NA, NA, wcss0_dbg5, qdss_cti_trig_out_a0,
++		 boot14, tm7, NA),
++	PINGROUP(62, qpic_pad, chip_rst, wifi0_uart, wifi1_uart, i2s_spdif_out,
++		 NA, NA, wcss0_dbg6, qdss_cti_trig_out_b0, boot11, tm8, NA, NA,
++		 NA),
++	PINGROUP(63, qpic_pad, wifi0_uart1, wifi1_uart1, wifi1_uart, i2s_td1,
++		 i2s_rxd, i2s_spdif_out, i2s_spdif_in, NA, wcss0_dbg7,
++		 wcss1_dbg7, boot20, tm9, NA),
++	PINGROUP(64, qpic_pad1, audio_pwm0, NA, wcss0_dbg8, wcss1_dbg8, NA, NA,
++		 NA, NA, NA, NA, NA, NA, NA),
++	PINGROUP(65, qpic_pad2, audio_pwm1, NA, wcss0_dbg9, wcss1_dbg9, NA, NA,
++		 NA, NA, NA, NA, NA, NA, NA),
++	PINGROUP(66, qpic_pad3, audio_pwm2, NA, wcss0_dbg10, wcss1_dbg10, NA,
++		 NA, NA, NA, NA, NA, NA, NA, NA),
++	PINGROUP(67, qpic_pad0, audio_pwm3, NA, wcss0_dbg11, wcss1_dbg11, NA,
++		 NA, NA, NA, NA, NA, NA, NA, NA),
++	PINGROUP(68, qpic_pad8, NA, wcss0_dbg12, wcss1_dbg12, NA, NA, NA, NA,
++		 NA, NA, NA, NA, NA, NA),
++	PINGROUP(69, qpic_pad, NA, wcss0_dbg, NA, NA, NA, NA, NA, NA, NA, NA,
++		 NA, NA, NA),
+	PINGROUP(70, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(71, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(72, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+@@ -433,7 +1526,8 @@ static const struct msm_pingroup ipq4019_groups[] = {
+	PINGROUP(95, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(96, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(97, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+-	PINGROUP(98, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
++	PINGROUP(98, wifi034, wifi134, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,
++		 NA, NA),
+	PINGROUP(99, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ };
+
+@@ -460,6 +1554,7 @@ static const struct of_device_id ipq4019_pinctrl_of_match[] = {
+ static struct platform_driver ipq4019_pinctrl_driver = {
+	.driver = {
+		.name = "ipq4019-pinctrl",
++		.owner = THIS_MODULE,
+		.of_match_table = ipq4019_pinctrl_of_match,
+	},
+	.probe = ipq4019_pinctrl_probe,
+--
+2.7.2
diff --git a/target/linux/ipq806x/patches-4.9/853-net-qualcomm-Fix-edma-driver-for-GMAC-avaiable-on-IP.patch b/target/linux/ipq806x/patches-4.9/853-net-qualcomm-Fix-edma-driver-for-GMAC-avaiable-on-IP.patch
new file mode 100644
index 0000000..64c6d30
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/853-net-qualcomm-Fix-edma-driver-for-GMAC-avaiable-on-IP.patch
@@ -0,0 +1,1937 @@ 
+From 68b3b707d094d73d87f7eead47e470a458ef8b30 Mon Sep 17 00:00:00 2001
+From: Ram Chandra Jangir <rjangir@codeaurora.org>
+Date: Tue, 28 Mar 2017 14:00:00 +0530
+Subject: [PATCH 1/5] net: qualcomm: Fix edma driver for GMAC avaiable on
+ IPQ40xx
+
+Existing edma driver is not fully functional, this change
+fixes the edma driver and it add new features on top of it.
+
+- Support Dynamic WAN/LAN port group composition for single
+  netdevice platforms
+- Support for enable/disable of TX Vlan offloading through
+    ethtool
+
+Signed-off-by: Ram Chandra Jangir <rjangir@codeaurora.org>
+---
+ drivers/net/ethernet/qualcomm/essedma/edma.c       | 396 ++++++++-----
+ drivers/net/ethernet/qualcomm/essedma/edma.h       |  26 +-
+ drivers/net/ethernet/qualcomm/essedma/edma_axi.c   | 647 +++++++++++++++++----
+ .../net/ethernet/qualcomm/essedma/edma_ethtool.c   |  50 +-
+ drivers/net/ethernet/qualcomm/essedma/ess_edma.h   |   2 +
+ 5 files changed, 823 insertions(+), 298 deletions(-)
+
+diff --git a/drivers/net/ethernet/qualcomm/essedma/edma.c b/drivers/net/ethernet/qualcomm/essedma/edma.c
+index 671c2c0..410d40a 100644
+--- a/drivers/net/ethernet/qualcomm/essedma/edma.c
++++ b/drivers/net/ethernet/qualcomm/essedma/edma.c
+@@ -23,7 +23,7 @@ bool edma_stp_rstp;
+ u16 edma_ath_eth_type;
+
+ /* edma_skb_priority_offset()
+- * 	get edma skb priority
++ *	get edma skb priority
+  */
+ static unsigned int edma_skb_priority_offset(struct sk_buff *skb)
+ {
+@@ -37,14 +37,15 @@ static int edma_alloc_tx_ring(struct edma_common_info *edma_cinfo,
+			      struct edma_tx_desc_ring *etdr)
+ {
+	struct platform_device *pdev = edma_cinfo->pdev;
++	u16 sw_size = sizeof(struct edma_sw_desc) * etdr->count;
+
+	/* Initialize ring */
+-	etdr->size = sizeof(struct edma_sw_desc) * etdr->count;
++	etdr->size = sizeof(struct edma_tx_desc) * etdr->count;
+	etdr->sw_next_to_fill = 0;
+	etdr->sw_next_to_clean = 0;
+
+	/* Allocate SW descriptors */
+-	etdr->sw_desc = vzalloc(etdr->size);
++	etdr->sw_desc = vzalloc(sw_size);
+	if (!etdr->sw_desc) {
+		dev_err(&pdev->dev, "buffer alloc of tx ring failed=%p", etdr);
+		return -ENOMEM;
+@@ -56,6 +57,7 @@ static int edma_alloc_tx_ring(struct edma_common_info *edma_cinfo,
+	if (!etdr->hw_desc) {
+		dev_err(&pdev->dev, "descriptor allocation for tx ring failed");
+		vfree(etdr->sw_desc);
++		etdr->sw_desc = NULL;
+		return -ENOMEM;
+	}
+
+@@ -70,12 +72,13 @@ static void edma_free_tx_ring(struct edma_common_info *edma_cinfo,
+ {
+	struct platform_device *pdev = edma_cinfo->pdev;
+
+-	if (likely(etdr->dma))
++	if (likely(etdr->hw_desc)) {
+		dma_free_coherent(&pdev->dev, etdr->size, etdr->hw_desc,
+				 etdr->dma);
+
+-	vfree(etdr->sw_desc);
+-	etdr->sw_desc = NULL;
++		vfree(etdr->sw_desc);
++		etdr->sw_desc = NULL;
++	}
+ }
+
+ /* edma_alloc_rx_ring()
+@@ -85,13 +88,14 @@ static int edma_alloc_rx_ring(struct edma_common_info *edma_cinfo,
+			     struct edma_rfd_desc_ring *erxd)
+ {
+	struct platform_device *pdev = edma_cinfo->pdev;
++	u16 sw_size = sizeof(struct edma_sw_desc) * erxd->count;
+
+	erxd->size = sizeof(struct edma_sw_desc) * erxd->count;
+	erxd->sw_next_to_fill = 0;
+	erxd->sw_next_to_clean = 0;
+
+	/* Allocate SW descriptors */
+-	erxd->sw_desc = vzalloc(erxd->size);
++	erxd->sw_desc = vzalloc(sw_size);
+	if (!erxd->sw_desc)
+		return -ENOMEM;
+
+@@ -100,6 +104,7 @@ static int edma_alloc_rx_ring(struct edma_common_info *edma_cinfo,
+			GFP_KERNEL);
+	if (!erxd->hw_desc) {
+		vfree(erxd->sw_desc);
++		erxd->sw_desc = NULL;
+		return -ENOMEM;
+	}
+
+@@ -110,16 +115,17 @@ static int edma_alloc_rx_ring(struct edma_common_info *edma_cinfo,
+  *	Free rx ring allocated by alloc_rx_ring
+  */
+ static void edma_free_rx_ring(struct edma_common_info *edma_cinfo,
+-			     struct edma_rfd_desc_ring *rxdr)
++			     struct edma_rfd_desc_ring *erxd)
+ {
+	struct platform_device *pdev = edma_cinfo->pdev;
+
+-	if (likely(rxdr->dma))
+-		dma_free_coherent(&pdev->dev, rxdr->size, rxdr->hw_desc,
+-				 rxdr->dma);
++	if (likely(erxd->hw_desc)) {
++		dma_free_coherent(&pdev->dev, erxd->size, erxd->hw_desc,
++				 erxd->dma);
+
+-	vfree(rxdr->sw_desc);
+-	rxdr->sw_desc = NULL;
++		vfree(erxd->sw_desc);
++		erxd->sw_desc = NULL;
++	}
+ }
+
+ /* edma_configure_tx()
+@@ -135,7 +141,6 @@ static void edma_configure_tx(struct edma_common_info *edma_cinfo)
+	edma_write_reg(EDMA_REG_TXQ_CTRL, txq_ctrl_data);
+ }
+
+-
+ /* edma_configure_rx()
+  *	configure reception control data
+  */
+@@ -197,19 +202,43 @@ static int edma_alloc_rx_buf(struct edma_common_info
+
+		if (sw_desc->flags & EDMA_SW_DESC_FLAG_SKB_REUSE) {
+			skb = sw_desc->skb;
++
++			/* Clear REUSE flag */
++			sw_desc->flags &= ~EDMA_SW_DESC_FLAG_SKB_REUSE;
+		} else {
+			/* alloc skb */
+			skb = netdev_alloc_skb(edma_netdev[0], length);
+			if (!skb) {
+				/* Better luck next round */
++				sw_desc->flags = 0;
+				break;
+			}
+		}
+
+-		if (edma_cinfo->page_mode) {
++		if (!edma_cinfo->page_mode) {
++			sw_desc->dma = dma_map_single(&pdev->dev, skb->data,
++						     length, DMA_FROM_DEVICE);
++				if (dma_mapping_error(&pdev->dev, sw_desc->dma)) {
++					WARN_ONCE(0, "EDMA DMA mapping failed for linear address %x", sw_desc->dma);
++					sw_desc->flags = 0;
++					sw_desc->skb = NULL;
++					dev_kfree_skb_any(skb);
++					break;
++				}
++
++				/*
++				 * We should not exit from here with REUSE flag set
++				 * This is to avoid re-using same sk_buff for next
++				 * time around
++				 */
++				sw_desc->flags = EDMA_SW_DESC_FLAG_SKB_HEAD;
++				sw_desc->length = length;
++		} else {
+			struct page *pg = alloc_page(GFP_ATOMIC);
+
+			if (!pg) {
++				sw_desc->flags = 0;
++				sw_desc->skb = NULL;
+				dev_kfree_skb_any(skb);
+				break;
+			}
+@@ -217,8 +246,10 @@ static int edma_alloc_rx_buf(struct edma_common_info
+			sw_desc->dma = dma_map_page(&pdev->dev, pg, 0,
+						   edma_cinfo->rx_page_buffer_len,
+						   DMA_FROM_DEVICE);
+-			if (dma_mapping_error(&pdev->dev,
+-				    sw_desc->dma)) {
++			if (dma_mapping_error(&pdev->dev, sw_desc->dma)) {
++				WARN_ONCE(0, "EDMA DMA mapping failed for page address %x", sw_desc->dma);
++				sw_desc->flags = 0;
++				sw_desc->skb = NULL;
+				__free_page(pg);
+				dev_kfree_skb_any(skb);
+				break;
+@@ -228,22 +259,11 @@ static int edma_alloc_rx_buf(struct edma_common_info
+					   edma_cinfo->rx_page_buffer_len);
+			sw_desc->flags = EDMA_SW_DESC_FLAG_SKB_FRAG;
+			sw_desc->length = edma_cinfo->rx_page_buffer_len;
+-		} else {
+-			sw_desc->dma = dma_map_single(&pdev->dev, skb->data,
+-						     length, DMA_FROM_DEVICE);
+-			if (dma_mapping_error(&pdev->dev,
+-			   sw_desc->dma)) {
+-				dev_kfree_skb_any(skb);
+-				break;
+-			}
+-
+-			sw_desc->flags = EDMA_SW_DESC_FLAG_SKB_HEAD;
+-			sw_desc->length = length;
+		}
+
+		/* Update the buffer info */
+		sw_desc->skb = skb;
+-		rx_desc = (&((struct edma_rx_free_desc *)(erdr->hw_desc))[i]);
++		rx_desc = (&(erdr->hw_desc)[i]);
+		rx_desc->buffer_addr = cpu_to_le64(sw_desc->dma);
+		if (++i == erdr->count)
+			i = 0;
+@@ -307,7 +327,7 @@ static void edma_init_desc(struct edma_common_info *edma_cinfo)
+		rfd_ring = edma_cinfo->rfd_ring[j];
+		/* Update Receive Free descriptor ring base address */
+		edma_write_reg(EDMA_REG_RFD_BASE_ADDR_Q(j),
+-			(u32)(rfd_ring->dma));
++			      (u32)(rfd_ring->dma));
+		j += ((edma_cinfo->num_rx_queues == 4) ? 2 : 1);
+	}
+
+@@ -337,7 +357,7 @@ static void edma_init_desc(struct edma_common_info *edma_cinfo)
+  *	Api to check checksum on receive packets
+  */
+ static void edma_receive_checksum(struct edma_rx_return_desc *rd,
+-						 struct sk_buff *skb)
++				 struct sk_buff *skb)
+ {
+	skb_checksum_none_assert(skb);
+
+@@ -354,23 +374,36 @@ static void edma_receive_checksum(struct edma_rx_return_desc *rd,
+ /* edma_clean_rfd()
+  *	clean up rx resourcers on error
+  */
+-static void edma_clean_rfd(struct edma_rfd_desc_ring *erdr, u16 index)
++static void edma_clean_rfd(struct platform_device *pdev,
++			  struct edma_rfd_desc_ring *erdr,
++			  u16 index,
++			  int pos)
+ {
+-	struct edma_rx_free_desc *rx_desc;
+-	struct edma_sw_desc *sw_desc;
++	struct edma_rx_free_desc *rx_desc = &(erdr->hw_desc[index]);
++	struct edma_sw_desc *sw_desc = &erdr->sw_desc[index];
++
++	/* Unmap non-first RFD positions in packet */
++	if (pos) {
++		if (likely(sw_desc->flags & EDMA_SW_DESC_FLAG_SKB_HEAD))
++			dma_unmap_single(&pdev->dev, sw_desc->dma,
++					sw_desc->length, DMA_FROM_DEVICE);
++		else
++			dma_unmap_page(&pdev->dev, sw_desc->dma,
++				      sw_desc->length, DMA_FROM_DEVICE);
++	}
+
+-	rx_desc = (&((struct edma_rx_free_desc *)(erdr->hw_desc))[index]);
+-	sw_desc = &erdr->sw_desc[index];
+	if (sw_desc->skb) {
+		dev_kfree_skb_any(sw_desc->skb);
+		sw_desc->skb = NULL;
+	}
+
++	sw_desc->flags = 0;
+	memset(rx_desc, 0, sizeof(struct edma_rx_free_desc));
+ }
+
+-/* edma_rx_complete_fraglist()
+- *	Complete Rx processing for fraglist skbs
++/*
++ * edma_rx_complete_stp_rstp()
++ *	Complete Rx processing for STP RSTP packets
+  */
+ static void edma_rx_complete_stp_rstp(struct sk_buff *skb, int port_id, struct edma_rx_return_desc *rd)
+ {
+@@ -380,7 +413,7 @@ static void edma_rx_complete_stp_rstp(struct sk_buff *skb, int port_id, struct e
+	u8 mac_addr[EDMA_ETH_HDR_LEN];
+
+	port_type = (rd->rrd1 >> EDMA_RRD_PORT_TYPE_SHIFT)
+-		                & EDMA_RRD_PORT_TYPE_MASK;
++				& EDMA_RRD_PORT_TYPE_MASK;
+	/* if port type is 0x4, then only proceed with
+	 * other stp/rstp calculation
+	 */
+@@ -416,7 +449,7 @@ static void edma_rx_complete_stp_rstp(struct sk_buff *skb, int port_id, struct e
+  *	Complete Rx processing for fraglist skbs
+  */
+ static int edma_rx_complete_fraglist(struct sk_buff *skb, u16 num_rfds, u16 length, u32 sw_next_to_clean,
+-					u16 *cleaned_count, struct edma_rfd_desc_ring *erdr, struct edma_common_info *edma_cinfo)
++				    struct edma_rfd_desc_ring *erdr, struct edma_common_info *edma_cinfo)
+ {
+	struct platform_device *pdev = edma_cinfo->pdev;
+	struct edma_hw *hw = &edma_cinfo->hw;
+@@ -433,6 +466,7 @@ static int edma_rx_complete_fraglist(struct sk_buff *skb, u16 num_rfds, u16 leng
+	/* clean-up all related sw_descs */
+	for (i = 1; i < num_rfds; i++) {
+		struct sk_buff *skb_prev;
++
+		sw_desc = &erdr->sw_desc[sw_next_to_clean];
+		skb_temp = sw_desc->skb;
+
+@@ -461,7 +495,6 @@ static int edma_rx_complete_fraglist(struct sk_buff *skb, u16 num_rfds, u16 leng
+
+		/* Increment SW index */
+		sw_next_to_clean = (sw_next_to_clean + 1) & (erdr->count - 1);
+-		(*cleaned_count)++;
+	}
+
+	return sw_next_to_clean;
+@@ -470,8 +503,10 @@ static int edma_rx_complete_fraglist(struct sk_buff *skb, u16 num_rfds, u16 leng
+ /* edma_rx_complete_paged()
+  *	Complete Rx processing for paged skbs
+  */
+-static int edma_rx_complete_paged(struct sk_buff *skb, u16 num_rfds, u16 length, u32 sw_next_to_clean,
+-					u16 *cleaned_count, struct edma_rfd_desc_ring *erdr, struct edma_common_info *edma_cinfo)
++static int edma_rx_complete_paged(struct sk_buff *skb, u16 num_rfds,
++				 u16 length, u32 sw_next_to_clean,
++				struct edma_rfd_desc_ring *erdr,
++				struct edma_common_info *edma_cinfo)
+ {
+	struct platform_device *pdev = edma_cinfo->pdev;
+	struct sk_buff *skb_temp;
+@@ -504,14 +539,15 @@ static int edma_rx_complete_paged(struct sk_buff *skb, u16 num_rfds, u16 length,
+			skb_temp = sw_desc->skb;
+			frag = &skb_shinfo(skb_temp)->frags[0];
+			dma_unmap_page(&pdev->dev, sw_desc->dma,
+-				sw_desc->length, DMA_FROM_DEVICE);
++				      sw_desc->length, DMA_FROM_DEVICE);
+
+			if (size_remaining < edma_cinfo->rx_page_buffer_len)
+				frag->size = size_remaining;
+
+			skb_fill_page_desc(skb, i, skb_frag_page(frag),
+-					0, frag->size);
++					  0, frag->size);
+
++			/* We used frag pages from skb_temp in skb */
+			skb_shinfo(skb_temp)->nr_frags = 0;
+			dev_kfree_skb_any(skb_temp);
+
+@@ -521,7 +557,6 @@ static int edma_rx_complete_paged(struct sk_buff *skb, u16 num_rfds, u16 length,
+
+			/* Increment SW index */
+			sw_next_to_clean = (sw_next_to_clean + 1) & (erdr->count - 1);
+-			(*cleaned_count)++;
+		}
+	}
+
+@@ -538,17 +573,9 @@ static void edma_rx_complete(struct edma_common_info *edma_cinfo,
+ {
+	struct platform_device *pdev = edma_cinfo->pdev;
+	struct edma_rfd_desc_ring *erdr = edma_cinfo->rfd_ring[queue_id];
+-	struct net_device *netdev;
+-	struct edma_adapter *adapter;
+-	struct edma_sw_desc *sw_desc;
+-	struct sk_buff *skb;
+-	struct edma_rx_return_desc *rd;
+	u16 hash_type, rrd[8], cleaned_count = 0, length = 0, num_rfds = 1,
+	    sw_next_to_clean, hw_next_to_clean = 0, vlan = 0, ret_count = 0;
+	u32 data = 0;
+-	u8 *vaddr;
+-	int port_id, i, drop_count = 0;
+-	u32 priority;
+	u16 count = erdr->count, rfd_avail;
+	u8 queue_to_rxid[8] = {0, 0, 1, 1, 2, 2, 3, 3};
+
+@@ -556,37 +583,44 @@ static void edma_rx_complete(struct edma_common_info *edma_cinfo,
+
+	edma_read_reg(EDMA_REG_RFD_IDX_Q(queue_id), &data);
+	hw_next_to_clean = (data >> EDMA_RFD_CONS_IDX_SHIFT) &
+-			   EDMA_RFD_CONS_IDX_MASK;
++			EDMA_RFD_CONS_IDX_MASK;
+
+	do {
+		while (sw_next_to_clean != hw_next_to_clean) {
++			struct net_device *netdev;
++			struct edma_adapter *adapter;
++			struct edma_sw_desc *sw_desc;
++			struct sk_buff *skb;
++			struct edma_rx_return_desc *rd;
++			u8 *vaddr;
++			int port_id, i, drop_count = 0;
++			u32 priority;
++
+			if (!work_to_do)
+				break;
+
+			sw_desc = &erdr->sw_desc[sw_next_to_clean];
+			skb = sw_desc->skb;
+
+-			/* Unmap the allocated buffer */
+-			if (likely(sw_desc->flags & EDMA_SW_DESC_FLAG_SKB_HEAD))
++			/* Get RRD */
++			if (!edma_cinfo->page_mode) {
+				dma_unmap_single(&pdev->dev, sw_desc->dma,
+-					        sw_desc->length, DMA_FROM_DEVICE);
+-			else
++						sw_desc->length, DMA_FROM_DEVICE);
++				rd = (struct edma_rx_return_desc *)skb->data;
++
++			} else {
+				dma_unmap_page(&pdev->dev, sw_desc->dma,
+					      sw_desc->length, DMA_FROM_DEVICE);
+-
+-			/* Get RRD */
+-			if (edma_cinfo->page_mode) {
+				vaddr = kmap_atomic(skb_frag_page(&skb_shinfo(skb)->frags[0]));
+				memcpy((uint8_t *)&rrd[0], vaddr, 16);
+				rd = (struct edma_rx_return_desc *)rrd;
+				kunmap_atomic(vaddr);
+-			} else {
+-				rd = (struct edma_rx_return_desc *)skb->data;
+			}
+
+			/* Check if RRD is valid */
+			if (!(rd->rrd7 & EDMA_RRD_DESC_VALID)) {
+-				edma_clean_rfd(erdr, sw_next_to_clean);
++				dev_err(&pdev->dev, "Incorrect RRD DESC valid bit set");
++				edma_clean_rfd(pdev, erdr, sw_next_to_clean, 0);
+				sw_next_to_clean = (sw_next_to_clean + 1) &
+						   (erdr->count - 1);
+				cleaned_count++;
+@@ -599,25 +633,39 @@ static void edma_rx_complete(struct edma_common_info *edma_cinfo,
+			/* Get Rx port ID from switch */
+			port_id = (rd->rrd1 >> EDMA_PORT_ID_SHIFT) & EDMA_PORT_ID_MASK;
+			if ((!port_id) || (port_id > EDMA_MAX_PORTID_SUPPORTED)) {
+-				dev_err(&pdev->dev, "Invalid RRD source port bit set");
++				if (net_ratelimit()) {
++					dev_err(&pdev->dev,
++						"Incorrect RRD sport bit set");
++					dev_err(&pdev->dev, "RRD Dump\n rrd0:%x rrd1: %x rrd2:%x rrd3: %x rrd4: %x rrd5: %x rrd6: %x rrd7: %x",
++						rd->rrd0, rd->rrd1, rd->rrd2,
++						rd->rrd3, rd->rrd4, rd->rrd5,
++						rd->rrd6, rd->rrd7);
++					dev_err(&pdev->dev, "Num_rfds: %d src_port: %d, pkt_size: %d cvlan_tag: %d\n",
++						num_rfds,
++						rd->rrd1 &
++						EDMA_RRD_SRC_PORT_NUM_MASK,
++						rd->rrd6 &
++						EDMA_RRD_PKT_SIZE_MASK,
++						rd->rrd7 & EDMA_RRD_CVLAN);
++				}
+				for (i = 0; i < num_rfds; i++) {
+-					edma_clean_rfd(erdr, sw_next_to_clean);
++					edma_clean_rfd(pdev, erdr, sw_next_to_clean, i);
+					sw_next_to_clean = (sw_next_to_clean + 1) & (erdr->count - 1);
+-					cleaned_count++;
+				}
++
++				cleaned_count += num_rfds;
+				continue;
+			}
+
+-			/* check if we have a sink for the data we receive.
+-			 * If the interface isn't setup, we have to drop the
+-			 * incoming data for now.
+-			 */
+			netdev = edma_cinfo->portid_netdev_lookup_tbl[port_id];
+			if (!netdev) {
+-				edma_clean_rfd(erdr, sw_next_to_clean);
+-				sw_next_to_clean = (sw_next_to_clean + 1) &
+-						   (erdr->count - 1);
+-				cleaned_count++;
++				dev_err(&pdev->dev, "Invalid netdev");
++				for (i = 0; i < num_rfds; i++) {
++					edma_clean_rfd(pdev, erdr, sw_next_to_clean, i);
++					sw_next_to_clean = (sw_next_to_clean + 1) & (erdr->count - 1);
++				}
++
++				cleaned_count += num_rfds;
+				continue;
+			}
+			adapter = netdev_priv(netdev);
+@@ -640,7 +688,7 @@ static void edma_rx_complete(struct edma_common_info *edma_cinfo,
+			if (likely(!priority && !edma_cinfo->page_mode && (num_rfds <= 1))) {
+				rfd_avail = (count + sw_next_to_clean - hw_next_to_clean - 1) & (count - 1);
+				if (rfd_avail < EDMA_RFD_AVAIL_THR) {
+-					sw_desc->flags = EDMA_SW_DESC_FLAG_SKB_REUSE;
++					sw_desc->flags |= EDMA_SW_DESC_FLAG_SKB_REUSE;
+					sw_next_to_clean = (sw_next_to_clean + 1) & (erdr->count - 1);
+					adapter->stats.rx_dropped++;
+					cleaned_count++;
+@@ -668,15 +716,16 @@ static void edma_rx_complete(struct edma_common_info *edma_cinfo,
+			sw_next_to_clean = (sw_next_to_clean + 1) &
+					   (erdr->count - 1);
+
+-			cleaned_count++;
+-
+			/* Get the packet size and allocate buffer */
+			length = rd->rrd6 & EDMA_RRD_PKT_SIZE_MASK;
+
+			if (edma_cinfo->page_mode) {
+				/* paged skb */
+-				sw_next_to_clean = edma_rx_complete_paged(skb, num_rfds, length, sw_next_to_clean, &cleaned_count, erdr, edma_cinfo);
++				sw_next_to_clean = edma_rx_complete_paged(skb, num_rfds, length,
++									 sw_next_to_clean,
++									 erdr, edma_cinfo);
+				if (!pskb_may_pull(skb, ETH_HLEN)) {
++					cleaned_count += num_rfds;
+					dev_kfree_skb_any(skb);
+					continue;
+				}
+@@ -688,16 +737,18 @@ static void edma_rx_complete(struct edma_common_info *edma_cinfo,
+				 * starts from an offset of 16.
+				 */
+				skb_reserve(skb, 16);
+-				if (likely((num_rfds <= 1) || !edma_cinfo->fraglist_mode)) {
++				if (likely((num_rfds <= 1) || !edma_cinfo->fraglist_mode))
+					skb_put(skb, length);
+-				} else {
+-					sw_next_to_clean = edma_rx_complete_fraglist(skb, num_rfds, length, sw_next_to_clean, &cleaned_count, erdr, edma_cinfo);
+-				}
++				else
++					sw_next_to_clean = edma_rx_complete_fraglist(skb, num_rfds, length,
++										    sw_next_to_clean,
++										    erdr, edma_cinfo);
+			}
+
+-			if (edma_stp_rstp) {
++			cleaned_count += num_rfds;
++
++			if (edma_stp_rstp)
+				edma_rx_complete_stp_rstp(skb, port_id, rd);
+-			}
+
+			skb->protocol = eth_type_trans(skb, netdev);
+
+@@ -715,7 +766,7 @@ static void edma_rx_complete(struct edma_common_info *edma_cinfo,
+			edma_receive_checksum(rd, skb);
+
+			/* Process VLAN HW acceleration indication provided by HW */
+-			if (unlikely(adapter->default_vlan_tag != rd->rrd4)) {
++			if (adapter->default_vlan_tag != rd->rrd4) {
+				vlan = rd->rrd4;
+				if (likely(rd->rrd7 & EDMA_RRD_CVLAN))
+					__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan);
+@@ -769,13 +820,14 @@ static int edma_delete_rfs_filter(struct edma_adapter *adapter,
+ {
+	int res = -1;
+
+-	struct flow_keys *keys = &filter_node->keys;
+-
+	if (likely(adapter->set_rfs_rule))
+-		res = (*adapter->set_rfs_rule)(adapter->netdev,
+-			flow_get_u32_src(keys), flow_get_u32_dst(keys),
+-			keys->ports.src, keys->ports.dst,
+-			keys->basic.ip_proto, filter_node->rq_id, 0);
++		res  =  (*adapter->set_rfs_rule)(adapter->netdev,
++				filter_node->keys.addrs.v4addrs.src,
++				filter_node->keys.addrs.v4addrs.dst, filter_node->keys.ports.src,
++				filter_node->keys.ports.dst,
++				filter_node->keys.basic.ip_proto,
++				filter_node->rq_id,
++				0);
+
+	return res;
+ }
+@@ -784,25 +836,20 @@ static int edma_delete_rfs_filter(struct edma_adapter *adapter,
+  *	Add RFS filter to switch
+  */
+ static int edma_add_rfs_filter(struct edma_adapter *adapter,
+-			       struct flow_keys *keys, u16 rq,
+-			       struct edma_rfs_filter_node *filter_node)
++			      struct flow_keys *keys, u16 rq,
++			      struct edma_rfs_filter_node *filter_node)
+ {
+	int res = -1;
+
+-	struct flow_keys *dest_keys = &filter_node->keys;
++	filter_node->keys.addrs.v4addrs.src = keys->addrs.v4addrs.src;
++	filter_node->keys.addrs.v4addrs.dst = keys->addrs.v4addrs.dst;
++	filter_node->keys.ports.ports = keys->ports.ports;
++	filter_node->keys.basic.ip_proto = keys->basic.ip_proto;
+
+-	memcpy(dest_keys, &filter_node->keys, sizeof(*dest_keys));
+-/*
+-	dest_keys->control = keys->control;
+-	dest_keys->basic = keys->basic;
+-	dest_keys->addrs = keys->addrs;
+-	dest_keys->ports = keys->ports;
+-	dest_keys.ip_proto = keys->ip_proto;
+-*/
+	/* Call callback registered by ESS driver */
+	if (likely(adapter->set_rfs_rule))
+-		res = (*adapter->set_rfs_rule)(adapter->netdev, flow_get_u32_src(keys),
+-		      flow_get_u32_dst(keys), keys->ports.src, keys->ports.dst,
++		res = (*adapter->set_rfs_rule)(adapter->netdev, keys->addrs.v4addrs.src,
++		      keys->addrs.v4addrs.dst, keys->ports.src, keys->ports.dst,
+		      keys->basic.ip_proto, rq, 1);
+
+	return res;
+@@ -817,17 +864,16 @@ static struct edma_rfs_filter_node *edma_rfs_key_search(struct hlist_head *h,
+	struct edma_rfs_filter_node *p;
+
+	hlist_for_each_entry(p, h, node)
+-		if (flow_get_u32_src(&p->keys) == flow_get_u32_src(key) &&
+-		    flow_get_u32_dst(&p->keys) == flow_get_u32_dst(key) &&
+-		    p->keys.ports.src == key->ports.src &&
+-		    p->keys.ports.dst == key->ports.dst &&
+-		    p->keys.basic.ip_proto == key->basic.ip_proto)
++		if (p->keys.addrs.v4addrs.src == key->addrs.v4addrs.src &&
++		   p->keys.addrs.v4addrs.dst == key->addrs.v4addrs.dst &&
++		   p->keys.ports.ports == key->ports.ports &&
++		   p->keys.basic.ip_proto == key->basic.ip_proto)
+			return p;
+	return NULL;
+ }
+
+ /* edma_initialise_rfs_flow_table()
+- * 	Initialise EDMA RFS flow table
++ *	Initialise EDMA RFS flow table
+  */
+ static void edma_initialise_rfs_flow_table(struct edma_adapter *adapter)
+ {
+@@ -851,7 +897,7 @@ static void edma_initialise_rfs_flow_table(struct edma_adapter *adapter)
+ }
+
+ /* edma_free_rfs_flow_table()
+- * 	Free EDMA RFS flow table
++ *	Free EDMA RFS flow table
+  */
+ static void edma_free_rfs_flow_table(struct edma_adapter *adapter)
+ {
+@@ -873,11 +919,11 @@ static void edma_free_rfs_flow_table(struct edma_adapter *adapter)
+
+		hhead = &adapter->rfs.hlist_head[i];
+		hlist_for_each_entry_safe(filter_node, tmp, hhead, node) {
+-			res  = edma_delete_rfs_filter(adapter, filter_node);
++			res = edma_delete_rfs_filter(adapter, filter_node);
+			if (res < 0)
+				dev_warn(&adapter->netdev->dev,
+					"EDMA going down but RFS entry %d not allowed to be flushed by Switch",
+-				        filter_node->flow_id);
++					filter_node->flow_id);
+			hlist_del(&filter_node->node);
+			kfree(filter_node);
+		}
+@@ -894,14 +940,14 @@ static inline void edma_tx_unmap_and_free(struct platform_device *pdev,
+	struct sk_buff *skb = sw_desc->skb;
+
+	if (likely((sw_desc->flags & EDMA_SW_DESC_FLAG_SKB_HEAD) ||
+-			(sw_desc->flags & EDMA_SW_DESC_FLAG_SKB_FRAGLIST)))
++		  (sw_desc->flags & EDMA_SW_DESC_FLAG_SKB_FRAGLIST)))
+		/* unmap_single for skb head area */
+		dma_unmap_single(&pdev->dev, sw_desc->dma,
+				sw_desc->length, DMA_TO_DEVICE);
+	else if (sw_desc->flags & EDMA_SW_DESC_FLAG_SKB_FRAG)
+		/* unmap page for paged fragments */
+		dma_unmap_page(&pdev->dev, sw_desc->dma,
+-		  	      sw_desc->length, DMA_TO_DEVICE);
++				sw_desc->length, DMA_TO_DEVICE);
+
+	if (likely(sw_desc->flags & EDMA_SW_DESC_FLAG_LAST))
+		dev_kfree_skb_any(skb);
+@@ -954,6 +1000,7 @@ static struct edma_sw_desc *edma_get_tx_buffer(struct edma_common_info *edma_cin
+					       struct edma_tx_desc *tpd, int queue_id)
+ {
+	struct edma_tx_desc_ring *etdr = edma_cinfo->tpd_ring[queue_id];
++
+	return &etdr->sw_desc[tpd - (struct edma_tx_desc *)etdr->hw_desc];
+ }
+
+@@ -1010,7 +1057,7 @@ static inline int edma_tx_queue_get(struct edma_adapter *adapter,
+  *	update the producer index for the ring transmitted
+  */
+ static void edma_tx_update_hw_idx(struct edma_common_info *edma_cinfo,
+-			         struct sk_buff *skb, int queue_id)
++				struct sk_buff *skb, int queue_id)
+ {
+	struct edma_tx_desc_ring *etdr = edma_cinfo->tpd_ring[queue_id];
+	u32 tpd_idx_data;
+@@ -1056,9 +1103,11 @@ static void edma_rollback_tx(struct edma_adapter *adapter,
+  * gets mapped
+  */
+ static int edma_tx_map_and_fill(struct edma_common_info *edma_cinfo,
+-			       struct edma_adapter *adapter, struct sk_buff *skb, int queue_id,
+-			       unsigned int flags_transmit, u16 from_cpu, u16 dp_bitmap,
+-			       bool packet_is_rstp, int nr_frags)
++				struct edma_adapter *adapter,
++				struct sk_buff *skb, int queue_id,
++				unsigned int flags_transmit,
++				u16 from_cpu, u16 dp_bitmap,
++				bool packet_is_rstp, int nr_frags)
+ {
+	struct edma_sw_desc *sw_desc = NULL;
+	struct platform_device *pdev = edma_cinfo->pdev;
+@@ -1068,9 +1117,6 @@ static int edma_tx_map_and_fill(struct edma_common_info *edma_cinfo,
+	u32 word1 = 0, word3 = 0, lso_word1 = 0, svlan_tag = 0;
+	u16 buf_len, lso_desc_len = 0;
+
+-	/* It should either be a nr_frags skb or fraglist skb but not both */
+-	BUG_ON(nr_frags && skb_has_frag_list(skb));
+-
+	if (skb_is_gso(skb)) {
+		/* TODO: What additional checks need to be performed here */
+		if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) {
+@@ -1091,7 +1137,7 @@ static int edma_tx_map_and_fill(struct edma_common_info *edma_cinfo,
+	} else if (flags_transmit & EDMA_HW_CHECKSUM) {
+			u8 css, cso;
+			cso = skb_checksum_start_offset(skb);
+-			css = cso  + skb->csum_offset;
++			css = cso + skb->csum_offset;
+
+			word1 |= (EDMA_TPD_CUSTOM_CSUM_EN);
+			word1 |= (cso >> 1) << EDMA_TPD_HDR_SHIFT;
+@@ -1102,7 +1148,7 @@ static int edma_tx_map_and_fill(struct edma_common_info *edma_cinfo,
+		word1 |= EDMA_TPD_PPPOE_EN;
+
+	if (flags_transmit & EDMA_VLAN_TX_TAG_INSERT_FLAG) {
+-		switch(skb->vlan_proto) {
++		switch (skb->vlan_proto) {
+		case htons(ETH_P_8021Q):
+			word3 |= (1 << EDMA_TX_INS_CVLAN);
+			word3 |= skb_vlan_tag_get(skb) << EDMA_TX_CVLAN_TAG_SHIFT;
+@@ -1204,6 +1250,8 @@ static int edma_tx_map_and_fill(struct edma_common_info *edma_cinfo,
+		tpd->word3 = word3;
+	}
+
++	i = 0;
++
+	/* Walk through all paged fragments */
+	while (nr_frags--) {
+		skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+@@ -1245,10 +1293,39 @@ static int edma_tx_map_and_fill(struct edma_common_info *edma_cinfo,
+		tpd->word1 = word1 | lso_word1;
+		tpd->word3 = word3;
+		sw_desc->flags |= EDMA_SW_DESC_FLAG_SKB_FRAGLIST;
++
++		i = 0;
++
++		nr_frags = skb_shinfo(iter_skb)->nr_frags;
++
++		/* Walk through paged frags for this fraglist skb */
++		while (nr_frags--) {
++			skb_frag_t *frag = &skb_shinfo(iter_skb)->frags[i];
++			buf_len = skb_frag_size(frag);
++			tpd = edma_get_next_tpd(edma_cinfo, queue_id);
++			sw_desc = edma_get_tx_buffer(edma_cinfo, tpd, queue_id);
++			sw_desc->length = buf_len;
++			sw_desc->flags |= EDMA_SW_DESC_FLAG_SKB_FRAG;
++
++			sw_desc->dma = skb_frag_dma_map(&pdev->dev, frag,
++					0, buf_len, DMA_TO_DEVICE);
++			if (dma_mapping_error(NULL, sw_desc->dma))
++				goto dma_error;
++
++			tpd->addr = cpu_to_le32(sw_desc->dma);
++			tpd->len  = cpu_to_le16(buf_len);
++			tpd->svlan_tag = svlan_tag;
++			tpd->word1 = word1 | lso_word1;
++			tpd->word3 = word3;
++			i++;
++		}
+	}
+
+-	if (tpd)
+-		tpd->word1 |= 1 << EDMA_TPD_EOP_SHIFT;
++	/* If tpd or sw_desc is still unitiialized then we need to return */
++	if ((!tpd) || (!sw_desc))
++		return -EINVAL;
++
++	tpd->word1 |= 1 << EDMA_TPD_EOP_SHIFT;
+
+	sw_desc->skb = skb;
+	sw_desc->flags |= EDMA_SW_DESC_FLAG_LAST;
+@@ -1325,20 +1402,25 @@ netdev_tx_t edma_xmit(struct sk_buff *skb,
+	struct edma_adapter *adapter = netdev_priv(net_dev);
+	struct edma_common_info *edma_cinfo = adapter->edma_cinfo;
+	struct edma_tx_desc_ring *etdr;
+-	u16 from_cpu, dp_bitmap, txq_id;
+-	int ret, nr_frags = 0, num_tpds_needed = 1, queue_id;
++	u16 from_cpu = 0, dp_bitmap = 0, txq_id;
++	int ret, nr_frags_first = 0, num_tpds_needed = 1, queue_id = 0;
+	unsigned int flags_transmit = 0;
+	bool packet_is_rstp = false;
+	struct netdev_queue *nq = NULL;
+
+	if (skb_shinfo(skb)->nr_frags) {
+-		nr_frags = skb_shinfo(skb)->nr_frags;
+-		num_tpds_needed += nr_frags;
+-	} else if (skb_has_frag_list(skb)) {
++		nr_frags_first = skb_shinfo(skb)->nr_frags;
++		num_tpds_needed += nr_frags_first;
++	}
++
++	if (skb_has_frag_list(skb)) {
+		struct sk_buff *iter_skb;
+
+-		skb_walk_frags(skb, iter_skb)
+-			num_tpds_needed++;
++		/* Walk through fraglist skbs making a note of nr_frags */
++		skb_walk_frags(skb, iter_skb) {
++			/* One TPD for skb->data and more for nr_frags */
++			num_tpds_needed += (1 + skb_shinfo(iter_skb)->nr_frags);
++		}
+	}
+
+	if (num_tpds_needed > EDMA_MAX_SKB_FRAGS) {
+@@ -1399,7 +1481,8 @@ netdev_tx_t edma_xmit(struct sk_buff *skb,
+
+	/* Map and fill descriptor for Tx */
+	ret = edma_tx_map_and_fill(edma_cinfo, adapter, skb, queue_id,
+-		flags_transmit, from_cpu, dp_bitmap, packet_is_rstp, nr_frags);
++		flags_transmit, from_cpu, dp_bitmap,
++		packet_is_rstp, nr_frags_first);
+	if (ret) {
+		dev_kfree_skb_any(skb);
+		adapter->stats.tx_errors++;
+@@ -1439,9 +1522,8 @@ void edma_flow_may_expire(unsigned long data)
+			res = rps_may_expire_flow(adapter->netdev, n->rq_id,
+					n->flow_id, n->filter_id);
+			if (res) {
+-				int ret;
+-				ret = edma_delete_rfs_filter(adapter, n);
+-				if (ret < 0)
++				res = edma_delete_rfs_filter(adapter, n);
++				if (res < 0)
+					dev_dbg(&adapter->netdev->dev,
+							"RFS entry %d not allowed to be flushed by Switch",
+							n->flow_id);
+@@ -1463,7 +1545,7 @@ void edma_flow_may_expire(unsigned long data)
+  *	Called by core to to steer the flow to CPU
+  */
+ int edma_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb,
+-		       u16 rxq, u32 flow_id)
++		      u16 rxq, u32 flow_id)
+ {
+	struct flow_keys keys;
+	struct edma_rfs_filter_node *filter_node;
+@@ -1472,7 +1554,8 @@ int edma_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb,
+	int res;
+
+	if (skb->protocol == htons(ETH_P_IPV6)) {
+-		dev_err(&adapter->pdev->dev, "IPv6 not supported\n");
++		if (net_ratelimit())
++			dev_err(&adapter->pdev->dev, "IPv6 not supported\n");
+		res = -EINVAL;
+		goto no_protocol_err;
+	}
+@@ -1549,6 +1632,7 @@ int edma_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb,
+	return res;
+ }
+
++#ifdef CONFIG_RFS_ACCEL
+ /* edma_register_rfs_filter()
+  *	Add RFS filter callback
+  */
+@@ -1569,6 +1653,19 @@ int edma_register_rfs_filter(struct net_device *netdev,
+
+	return 0;
+ }
++#endif
++
++/* edma_select_xps_queue()
++ *	Called by Linux TX stack to populate Linux TX queue
++ */
++u16 edma_select_xps_queue(struct net_device *dev, struct sk_buff *skb,
++				void *accel_priv, select_queue_fallback_t fallback)
++{
++	int cpu = get_cpu();
++	put_cpu();
++
++	return cpu;
++}
+
+ /* edma_alloc_tx_rings()
+  *	Allocate rx rings
+@@ -1685,24 +1782,15 @@ void edma_free_queues(struct edma_common_info *edma_cinfo)
+  */
+ void edma_free_rx_resources(struct edma_common_info *edma_cinfo)
+ {
+-        struct edma_rfd_desc_ring *erdr;
+-	struct edma_sw_desc *sw_desc;
++	struct edma_rfd_desc_ring *erdr;
+	struct platform_device *pdev = edma_cinfo->pdev;
+	int i, j, k;
+
+	for (i = 0, k = 0; i < edma_cinfo->num_rx_queues; i++) {
+		erdr = edma_cinfo->rfd_ring[k];
+		for (j = 0; j < EDMA_RX_RING_SIZE; j++) {
+-			sw_desc = &erdr->sw_desc[j];
+-			if (likely(sw_desc->flags & EDMA_SW_DESC_FLAG_SKB_HEAD)) {
+-				dma_unmap_single(&pdev->dev, sw_desc->dma,
+-					sw_desc->length, DMA_FROM_DEVICE);
+-				edma_clean_rfd(erdr, j);
+-			} else if ((sw_desc->flags & EDMA_SW_DESC_FLAG_SKB_FRAG)) {
+-				dma_unmap_page(&pdev->dev, sw_desc->dma,
+-					sw_desc->length, DMA_FROM_DEVICE);
+-				edma_clean_rfd(erdr, j);
+-			}
++			/* unmap all descriptors while cleaning */
++			edma_clean_rfd(pdev, erdr, j, 1);
+		}
+		k += ((edma_cinfo->num_rx_queues == 4) ? 2 : 1);
+
+@@ -2115,7 +2203,7 @@ int edma_poll(struct napi_struct *napi, int budget)
+	while (edma_percpu_info->rx_status) {
+		queue_id = ffs(edma_percpu_info->rx_status) - 1;
+		edma_rx_complete(edma_cinfo, &work_done,
+-			        budget, queue_id, napi);
++				budget, queue_id, napi);
+
+		if (likely(work_done < budget))
+			edma_percpu_info->rx_status &= ~(1 << queue_id);
+diff --git a/drivers/net/ethernet/qualcomm/essedma/edma.h b/drivers/net/ethernet/qualcomm/essedma/edma.h
+index 5b0695b..7be5aab 100644
+--- a/drivers/net/ethernet/qualcomm/essedma/edma.h
++++ b/drivers/net/ethernet/qualcomm/essedma/edma.h
+@@ -59,12 +59,19 @@
+ #define EDMA_LAN_DEFAULT_VLAN 1
+ #define EDMA_WAN_DEFAULT_VLAN 2
+
+-#define EDMA_DEFAULT_GROUP1_VLAN 1
+-#define EDMA_DEFAULT_GROUP2_VLAN 2
++#define EDMA_DEFAULT_GROUP1_VLAN 2
++#define EDMA_DEFAULT_GROUP2_VLAN 1
+ #define EDMA_DEFAULT_GROUP3_VLAN 3
+ #define EDMA_DEFAULT_GROUP4_VLAN 4
+ #define EDMA_DEFAULT_GROUP5_VLAN 5
+
++#define EDMA_DEFAULT_GROUP1_BMP 0x20
++#define EDMA_DEFAULT_GROUP2_BMP 0x1e
++
++#define EDMA_DEFAULT_DISABLE_RSS 0
++#define EDMA_RSS_DISABLE 1
++#define EDMA_RSS_ENABLE 0
++
+ /* Queues exposed to linux kernel */
+ #define EDMA_NETDEV_TX_QUEUE 4
+ #define EDMA_NETDEV_RX_QUEUE 4
+@@ -88,8 +95,8 @@
+
+ /* TX/RX descriptor ring count */
+ /* should be a power of 2 */
+-#define EDMA_RX_RING_SIZE 128
+-#define EDMA_TX_RING_SIZE 128
++#define EDMA_RX_RING_SIZE 512
++#define EDMA_TX_RING_SIZE 512
+
+ /* Flags used in paged/non paged mode */
+ #define EDMA_RX_HEAD_BUFF_SIZE_JUMBO 256
+@@ -315,7 +322,7 @@ struct edma_common_info {
+	struct ctl_table_header *edma_ctl_table_hdr;
+	int num_gmac;
+	struct edma_ethtool_statistics edma_ethstats; /* ethtool stats */
+-	int num_rx_queues; /* number of rx queue */
++	u32 num_rx_queues; /* number of rx queue */
+	u32 num_tx_queues; /* number of tx queue */
+	u32 tx_irq[16]; /* number of tx irq */
+	u32 rx_irq[8]; /* number of rx irq */
+@@ -350,7 +357,7 @@ struct edma_tx_desc_ring {
+
+ /* receive free descriptor (rfd) ring */
+ struct edma_rfd_desc_ring {
+-	void *hw_desc; /* descriptor ring virtual address */
++	struct edma_rx_free_desc *hw_desc; /* descriptor ring virtual address */
+	struct edma_sw_desc *sw_desc; /* buffer associated with ring */
+	u16 size; /* bytes allocated to sw_desc */
+	u16 count; /* number of descriptors in the ring */
+@@ -386,7 +393,9 @@ struct edma_adapter {
+	struct phy_device *phydev; /* Phy device */
+	struct edma_rfs_flow_table rfs; /* edma rfs flow table */
+	struct net_device_stats stats; /* netdev statistics */
++#ifdef CONFIG_RFS_ACCEL
+	set_rfs_filter_callback_t set_rfs_rule;
++#endif
+	u32 flags;/* status flags */
+	unsigned long state_flags; /* GMAC up/down flags */
+	u32 forced_speed; /* link force speed */
+@@ -394,6 +403,7 @@ struct edma_adapter {
+	u32 link_state; /* phy link state */
+	u32 phy_mdio_addr; /* PHY device address on MII interface */
+	u32 poll_required; /* check if link polling is required */
++	u32 poll_required_dynamic; /* dynamic polling flag */
+	u32 tx_start_offset[CONFIG_NR_CPUS]; /* tx queue start */
+	u32 default_vlan_tag; /* vlan tag */
+	u32 dp_bitmap;
+@@ -429,8 +439,10 @@ struct net_device_stats *edma_get_stats(struct net_device *netdev);
+ int edma_set_mac_addr(struct net_device *netdev, void *p);
+ int edma_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb,
+		u16 rxq, u32 flow_id);
++#ifdef CONFIG_RFS_ACCEL
+ int edma_register_rfs_filter(struct net_device *netdev,
+		set_rfs_filter_callback_t set_filter);
++#endif
+ void edma_flow_may_expire(unsigned long data);
+ void edma_set_ethtool_ops(struct net_device *netdev);
+ int edma_change_mtu(struct net_device *netdev, int new_mtu);
+@@ -439,6 +451,8 @@ void edma_assign_ath_hdr_type(int tag);
+ int edma_get_default_vlan_tag(struct net_device *netdev);
+ void edma_adjust_link(struct net_device *netdev);
+ int edma_fill_netdev(struct edma_common_info *edma_cinfo, int qid, int num, int txq_id);
++u16 edma_select_xps_queue(struct net_device *dev, struct sk_buff *skb,
++	void *accel_priv, select_queue_fallback_t fallback);
+ void edma_read_append_stats(struct edma_common_info *edma_cinfo);
+ void edma_change_tx_coalesce(int usecs);
+ void edma_change_rx_coalesce(int usecs);
+diff --git a/drivers/net/ethernet/qualcomm/essedma/edma_axi.c b/drivers/net/ethernet/qualcomm/essedma/edma_axi.c
+index 94ff340..6fe9019 100644
+--- a/drivers/net/ethernet/qualcomm/essedma/edma_axi.c
++++ b/drivers/net/ethernet/qualcomm/essedma/edma_axi.c
+@@ -17,6 +17,7 @@
+ #include <linux/of.h>
+ #include <linux/of_net.h>
+ #include <linux/timer.h>
++#include <linux/bitmap.h>
+ #include "edma.h"
+ #include "ess_edma.h"
+
+@@ -33,10 +34,14 @@ static const u32 default_msg = NETIF_MSG_DRV | NETIF_MSG_PROBE |
+ static u32 edma_hw_addr;
+
+ struct timer_list edma_stats_timer;
++static struct mii_bus *miibus_gb;
+
+ char edma_tx_irq[16][64];
+ char edma_rx_irq[8][64];
+ struct net_device *edma_netdev[EDMA_MAX_PORTID_SUPPORTED];
++static struct phy_device *edma_phydev[EDMA_MAX_PORTID_SUPPORTED];
++static int edma_link_detect_bmp;
++static int phy_dev_state[EDMA_MAX_PORTID_SUPPORTED];
+ static u16 tx_start[4] = {EDMA_TXQ_START_CORE0, EDMA_TXQ_START_CORE1,
+			EDMA_TXQ_START_CORE2, EDMA_TXQ_START_CORE3};
+ static u32 tx_mask[4] = {EDMA_TXQ_IRQ_MASK_CORE0, EDMA_TXQ_IRQ_MASK_CORE1,
+@@ -49,8 +54,10 @@ static u32 edma_default_group2_vtag  __read_mostly = EDMA_DEFAULT_GROUP2_VLAN;
+ static u32 edma_default_group3_vtag  __read_mostly = EDMA_DEFAULT_GROUP3_VLAN;
+ static u32 edma_default_group4_vtag  __read_mostly = EDMA_DEFAULT_GROUP4_VLAN;
+ static u32 edma_default_group5_vtag  __read_mostly = EDMA_DEFAULT_GROUP5_VLAN;
+-static u32 edma_rss_idt_val = EDMA_RSS_IDT_VALUE;
+-static u32 edma_rss_idt_idx;
++
++static u32 edma_default_group1_bmp  __read_mostly = EDMA_DEFAULT_GROUP1_BMP;
++static u32 edma_default_group2_bmp  __read_mostly = EDMA_DEFAULT_GROUP2_BMP;
++static u32 edma_disable_rss __read_mostly = EDMA_DEFAULT_DISABLE_RSS;
+
+ static int edma_weight_assigned_to_q __read_mostly;
+ static int edma_queue_to_virtual_q __read_mostly;
+@@ -194,6 +201,36 @@ static int edma_ath_hdr_eth_type(struct ctl_table *table, int write,
+	return ret;
+ }
+
++static int edma_get_port_from_phydev(struct phy_device *phydev)
++{
++	int i;
++
++	for (i = 0; i < EDMA_MAX_PORTID_SUPPORTED; i++) {
++		if (phydev == edma_phydev[i])
++			return i;
++	}
++
++	pr_err("Invalid PHY devive\n");
++	return -1;
++}
++
++static int edma_is_port_used(int portid)
++{
++	int used_portid_bmp;
++
++	used_portid_bmp = edma_link_detect_bmp >> 1;
++
++	while (used_portid_bmp) {
++		int port_bit_set = ffs(used_portid_bmp);
++
++		if (port_bit_set == portid)
++			return 1;
++		used_portid_bmp &= ~(1 << (port_bit_set - 1));
++	}
++
++	return 0;
++}
++
+ static int edma_change_default_lan_vlan(struct ctl_table *table, int write,
+					void __user *buffer, size_t *lenp,
+					loff_t *ppos)
+@@ -358,36 +395,400 @@ static int edma_change_group5_vtag(struct ctl_table *table, int write,
+	return ret;
+ }
+
+-static int edma_set_rss_idt_value(struct ctl_table *table, int write,
+-				  void __user *buffer, size_t *lenp,
+-				  loff_t *ppos)
++static int edma_change_group1_bmp(struct ctl_table *table, int write,
++		void __user *buffer, size_t *lenp, loff_t *ppos)
+ {
++	struct edma_adapter *adapter;
++	struct edma_common_info *edma_cinfo;
++	struct net_device *ndev;
++	struct phy_device *phydev;
++	int ret, num_ports_enabled;
++	u32 portid_bmp, port_bit, prev_bmp, port_id;
++
++	ndev = edma_netdev[0];
++	if (!ndev) {
++		pr_err("Netdevice for Group 0 does not exist\n");
++		return -1;
++	}
++
++	prev_bmp = edma_default_group1_bmp;
++
++	ret = proc_dointvec(table, write, buffer, lenp, ppos);
++	if ((!write) || (prev_bmp == edma_default_group1_bmp))
++		return ret;
++
++	adapter = netdev_priv(ndev);
++	edma_cinfo = adapter->edma_cinfo;
++
++	/* We ignore the bit for CPU Port */
++	portid_bmp = edma_default_group1_bmp >> 1;
++	port_bit = ffs(portid_bmp);
++	if (port_bit > EDMA_MAX_PORTID_SUPPORTED)
++		return -1;
++
++	/* If this group has no ports,
++	 * we disable polling for the adapter, stop the queues and return
++	 */
++	if (!port_bit) {
++		adapter->dp_bitmap = edma_default_group1_bmp;
++		if (adapter->poll_required) {
++			adapter->poll_required = 0;
++			adapter->link_state = __EDMA_LINKDOWN;
++			netif_carrier_off(ndev);
++			netif_tx_stop_all_queues(ndev);
++		}
++		return 0;
++	}
++
++	/* Our array indexes are for 5 ports (0 - 4) */
++	port_bit--;
++	edma_link_detect_bmp = 0;
++
++	/* Do we have more ports in this group */
++	num_ports_enabled = bitmap_weight((unsigned long int *)&portid_bmp, 32);
++
++	/* If this group has more then one port,
++	 * we disable polling for the adapter as link detection
++	 * should be disabled, stop the phy state machine of previous
++	 * phy adapter attached to group and start the queues
++	 */
++	if (num_ports_enabled > 1) {
++		if (adapter->poll_required) {
++			adapter->poll_required = 0;
++			if (adapter->phydev) {
++				port_id = edma_get_port_from_phydev(
++						adapter->phydev);
++
++				/* We check if phydev attached to this group is
++				 * already started and if yes, we stop
++				 * the state machine for the phy
++				 */
++				if (phy_dev_state[port_id]) {
++					phy_stop_machine(adapter->phydev);
++					phy_dev_state[port_id] = 0;
++				}
++
++				adapter->phydev = NULL;
++			}
++
++			/* Start the tx queues for this netdev
++			 * with link detection disabled
++			 */
++			if (adapter->link_state == __EDMA_LINKDOWN) {
++				adapter->link_state = __EDMA_LINKUP;
++				netif_tx_start_all_queues(ndev);
++				netif_carrier_on(ndev);
++			}
++		}
++		goto set_bitmap;
++	}
++
++	adapter->poll_required = adapter->poll_required_dynamic;
++
++	if (!adapter->poll_required)
++		goto set_bitmap;
++
++	phydev = adapter->phydev;
++
++	/* If this group has only one port,
++	 * if phydev exists we start the phy state machine
++	 * and if it doesn't we create a phydev and start it.
++	 */
++	if (edma_phydev[port_bit]) {
++		adapter->phydev = edma_phydev[port_bit];
++		set_bit(port_bit, (unsigned long int *)&edma_link_detect_bmp);
++
++		/* If the Phy device has changed group,
++		 * we need to reassign the netdev
++		 */
++		if (adapter->phydev->attached_dev != ndev)
++			adapter->phydev->attached_dev = ndev;
++
++		if (!phy_dev_state[port_bit]) {
++			phy_start_machine(adapter->phydev);
++			phy_dev_state[port_bit] = 1;
++		}
++	} else {
++		snprintf(adapter->phy_id,
++			MII_BUS_ID_SIZE + 3, PHY_ID_FMT,
++			miibus_gb->id,
++			port_bit);
++
++		adapter->phydev = phy_connect(ndev,
++					(const char *)adapter->phy_id,
++					&edma_adjust_link,
++					PHY_INTERFACE_MODE_SGMII);
++
++		if (IS_ERR(adapter->phydev)) {
++			adapter->phydev = phydev;
++			pr_err("PHY attach FAIL for port %d", port_bit);
++			return -1;
++		}
++
++		if (adapter->phydev->attached_dev != ndev)
++			adapter->phydev->attached_dev = ndev;
++
++		edma_phydev[port_bit] = adapter->phydev;
++		phy_dev_state[port_bit] = 1;
++		set_bit(port_bit, (unsigned long int *)&edma_link_detect_bmp);
++		adapter->phydev->advertising |=
++			(ADVERTISED_Pause |
++			ADVERTISED_Asym_Pause);
++		adapter->phydev->supported |=
++			(SUPPORTED_Pause |
++			SUPPORTED_Asym_Pause);
++		phy_start(adapter->phydev);
++		phy_start_aneg(adapter->phydev);
++	}
++
++	/* We check if this phydev is in use by other Groups
++	 * and stop phy machine only if it is not stopped
++	 */
++	if (phydev) {
++		port_id = edma_get_port_from_phydev(phydev);
++		if (phy_dev_state[port_id]) {
++			phy_stop_machine(phydev);
++			phy_dev_state[port_id] = 0;
++		}
++	}
++
++	adapter->poll_required = 1;
++	adapter->link_state = __EDMA_LINKDOWN;
++
++set_bitmap:
++	while (portid_bmp) {
++		int port_bit_set = ffs(portid_bmp);
++
++		edma_cinfo->portid_netdev_lookup_tbl[port_bit_set] = ndev;
++		portid_bmp &= ~(1 << (port_bit_set - 1));
++	}
++
++	adapter->dp_bitmap = edma_default_group1_bmp;
++
++	return 0;
++}
++
++static int edma_change_group2_bmp(struct ctl_table *table, int write,
++		void __user *buffer, size_t *lenp, loff_t *ppos)
++{
++	struct edma_adapter *adapter;
++	struct edma_common_info *edma_cinfo;
++	struct net_device *ndev;
++	struct phy_device *phydev;
+	int ret;
++	u32 prev_bmp, portid_bmp, port_bit, num_ports_enabled, port_id;
++
++	ndev = edma_netdev[1];
++	if (!ndev) {
++		pr_err("Netdevice for Group 1 does not exist\n");
++		return -1;
++	}
++
++	prev_bmp = edma_default_group2_bmp;
+
+	ret = proc_dointvec(table, write, buffer, lenp, ppos);
+-	if (write && !ret)
+-		edma_write_reg(EDMA_REG_RSS_IDT(edma_rss_idt_idx),
+-			       edma_rss_idt_val);
+-	return ret;
++	if ((!write) || (prev_bmp == edma_default_group2_bmp))
++		return ret;
++
++	adapter = netdev_priv(ndev);
++	edma_cinfo = adapter->edma_cinfo;
++
++	/* We ignore the bit for CPU Port */
++	portid_bmp = edma_default_group2_bmp >> 1;
++	port_bit = ffs(portid_bmp);
++	if (port_bit > EDMA_MAX_PORTID_SUPPORTED)
++		return -1;
++
++	/* If this group has no ports,
++	 * we disable polling for the adapter, stop the queues and return
++	 */
++	if (!port_bit) {
++		adapter->dp_bitmap = edma_default_group2_bmp;
++		if (adapter->poll_required) {
++			adapter->poll_required = 0;
++			adapter->link_state = __EDMA_LINKDOWN;
++			netif_carrier_off(ndev);
++			netif_tx_stop_all_queues(ndev);
++		}
++		return 0;
++	}
++
++	/* Our array indexes are for 5 ports (0 - 4) */
++	port_bit--;
++
++	/* Do we have more ports in this group */
++	num_ports_enabled = bitmap_weight((unsigned long int *)&portid_bmp, 32);
++
++	/* If this group has more then one port,
++	 * we disable polling for the adapter as link detection
++	 * should be disabled, stop the phy state machine of previous
++	 * phy adapter attached to group and start the queues
++	 */
++	if (num_ports_enabled > 1) {
++		if (adapter->poll_required) {
++			adapter->poll_required = 0;
++			if (adapter->phydev) {
++				port_id = edma_get_port_from_phydev(
++							adapter->phydev);
++
++				/* We check if this phydev is in use by
++				 * other Groups and stop phy machine only
++				 * if that is NOT the case
++				 */
++				if (!edma_is_port_used(port_id)) {
++					if (phy_dev_state[port_id]) {
++						phy_stop_machine(
++							adapter->phydev);
++						phy_dev_state[port_id] = 0;
++					}
++				}
++
++				adapter->phydev = NULL;
++			}
++
++			/* Start the tx queues for this netdev
++			 * with link detection disabled
++			 */
++			if (adapter->link_state == __EDMA_LINKDOWN) {
++				adapter->link_state = __EDMA_LINKUP;
++				netif_carrier_on(ndev);
++				netif_tx_start_all_queues(ndev);
++			}
++		}
++		goto set_bitmap;
++	}
++
++	adapter->poll_required = adapter->poll_required_dynamic;
++
++	if (!adapter->poll_required)
++		goto set_bitmap;
++
++	phydev = adapter->phydev;
++
++	/* If this group has only one port,
++	 * if phydev exists we start the phy state machine
++	 * and if it doesn't we create a phydev and start it.
++	 */
++	if (edma_phydev[port_bit]) {
++		adapter->phydev = edma_phydev[port_bit];
++
++		/* If the Phy device has changed group,
++		 * we need to reassign the netdev
++		 */
++		if (adapter->phydev->attached_dev != ndev)
++			adapter->phydev->attached_dev = ndev;
++
++		if (!phy_dev_state[port_bit]) {
++			phy_start_machine(adapter->phydev);
++			phy_dev_state[port_bit] = 1;
++			set_bit(port_bit,
++				(unsigned long int *)&edma_link_detect_bmp);
++		}
++	} else {
++		snprintf(adapter->phy_id,
++			 MII_BUS_ID_SIZE + 3, PHY_ID_FMT,
++			 miibus_gb->id,
++			 port_bit);
++
++		adapter->phydev = phy_connect(ndev,
++					      (const char *)adapter->phy_id,
++					      &edma_adjust_link,
++					      PHY_INTERFACE_MODE_SGMII);
++
++		if (IS_ERR(adapter->phydev)) {
++			adapter->phydev = phydev;
++			pr_err("PHY attach FAIL for port %d", port_bit);
++			return -1;
++		}
++
++		if (adapter->phydev->attached_dev != ndev)
++			adapter->phydev->attached_dev = ndev;
++
++		edma_phydev[port_bit] = adapter->phydev;
++		phy_dev_state[port_bit] = 1;
++		set_bit(port_bit, (unsigned long int *)&edma_link_detect_bmp);
++		adapter->phydev->advertising |=
++			(ADVERTISED_Pause |
++			ADVERTISED_Asym_Pause);
++		adapter->phydev->supported |=
++			(SUPPORTED_Pause |
++			SUPPORTED_Asym_Pause);
++		phy_start(adapter->phydev);
++		phy_start_aneg(adapter->phydev);
++	}
++
++	/* We check if this phydev is in use by other Groups
++	 * and stop phy machine only if that is NOT the case
++	 */
++	if (phydev) {
++		port_id = edma_get_port_from_phydev(phydev);
++		if (!edma_is_port_used(port_id)) {
++			if (phy_dev_state[port_id]) {
++				phy_stop_machine(phydev);
++				phy_dev_state[port_id] = 0;
++			}
++		}
++	}
++
++	adapter->poll_required = 1;
++	adapter->link_state = __EDMA_LINKDOWN;
++
++set_bitmap:
++	while (portid_bmp) {
++		int port_bit_set = ffs(portid_bmp);
++
++		edma_cinfo->portid_netdev_lookup_tbl[port_bit_set] = ndev;
++		portid_bmp &= ~(1 << (port_bit_set - 1));
++	}
++
++	adapter->dp_bitmap = edma_default_group2_bmp;
++
++	return 0;
+ }
+
+-static int edma_set_rss_idt_idx(struct ctl_table *table, int write,
+-				void __user *buffer, size_t *lenp,
+-				loff_t *ppos)
++static int edma_disable_rss_func(struct ctl_table *table, int write,
++				 void __user *buffer, size_t *lenp,
++				 loff_t *ppos)
+ {
++	struct edma_adapter *adapter;
++	struct edma_common_info *edma_cinfo;
++	struct edma_hw *hw;
+	int ret;
+-	u32 old_value = edma_rss_idt_idx;
++
++	if (!edma_netdev[0]) {
++		pr_err("Invalid Netdevice\n");
++		return -1;
++	}
++
++	adapter = netdev_priv(edma_netdev[0]);
++	edma_cinfo = adapter->edma_cinfo;
++	hw = &edma_cinfo->hw;
+
+	ret = proc_dointvec(table, write, buffer, lenp, ppos);
+-	if (!write || ret)
++
++	if ((!write) || (ret))
+		return ret;
+
+-	if (edma_rss_idt_idx >= EDMA_NUM_IDT) {
+-		pr_err("Invalid RSS indirection table index %d\n",
+-		       edma_rss_idt_idx);
+-		edma_rss_idt_idx = old_value;
+-		return -EINVAL;
++	switch (edma_disable_rss) {
++	case EDMA_RSS_DISABLE:
++		hw->rss_type = 0;
++		edma_write_reg(EDMA_REG_RSS_TYPE, hw->rss_type);
++		break;
++	case EDMA_RSS_ENABLE:
++		hw->rss_type = EDMA_RSS_TYPE_IPV4TCP |
++				EDMA_RSS_TYPE_IPV6_TCP |
++				EDMA_RSS_TYPE_IPV4_UDP |
++				EDMA_RSS_TYPE_IPV6UDP |
++				EDMA_RSS_TYPE_IPV4 |
++				EDMA_RSS_TYPE_IPV6;
++		edma_write_reg(EDMA_REG_RSS_TYPE, hw->rss_type);
++		break;
++	default:
++		pr_err("Invalid input\n");
++		ret = -1;
++		break;
+	}
++
+	return ret;
+ }
+
+@@ -458,95 +859,102 @@ static int edma_queue_to_virtual_queue_map(struct ctl_table *table, int write,
+
+ static struct ctl_table edma_table[] = {
+	{
+-		.procname       = "default_lan_tag",
+-		.data           = &edma_default_ltag,
+-		.maxlen         = sizeof(int),
+-		.mode           = 0644,
+-		.proc_handler   = edma_change_default_lan_vlan
++		.procname	= "default_lan_tag",
++		.data		= &edma_default_ltag,
++		.maxlen		= sizeof(int),
++		.mode		= 0644,
++		.proc_handler	= edma_change_default_lan_vlan
+	},
+	{
+-		.procname       = "default_wan_tag",
+-		.data           = &edma_default_wtag,
+-		.maxlen         = sizeof(int),
+-		.mode           = 0644,
+-		.proc_handler   = edma_change_default_wan_vlan
++		.procname	= "default_wan_tag",
++		.data		= &edma_default_wtag,
++		.maxlen		= sizeof(int),
++		.mode		= 0644,
++		.proc_handler	= edma_change_default_wan_vlan
+	},
+	{
+-		.procname       = "weight_assigned_to_queues",
+-		.data           = &edma_weight_assigned_to_q,
+-		.maxlen         = sizeof(int),
+-		.mode           = 0644,
+-		.proc_handler   = edma_weight_assigned_to_queues
++		.procname	= "weight_assigned_to_queues",
++		.data		= &edma_weight_assigned_to_q,
++		.maxlen		= sizeof(int),
++		.mode		= 0644,
++		.proc_handler	= edma_weight_assigned_to_queues
+	},
+	{
+-		.procname       = "queue_to_virtual_queue_map",
+-		.data           = &edma_queue_to_virtual_q,
+-		.maxlen         = sizeof(int),
+-		.mode           = 0644,
+-		.proc_handler   = edma_queue_to_virtual_queue_map
++		.procname	= "queue_to_virtual_queue_map",
++		.data		= &edma_queue_to_virtual_q,
++		.maxlen		= sizeof(int),
++		.mode		= 0644,
++		.proc_handler	= edma_queue_to_virtual_queue_map
+	},
+	{
+-		.procname       = "enable_stp_rstp",
+-		.data           = &edma_enable_rstp,
+-		.maxlen         = sizeof(int),
+-		.mode           = 0644,
+-		.proc_handler   = edma_enable_stp_rstp
++		.procname	= "enable_stp_rstp",
++		.data		= &edma_enable_rstp,
++		.maxlen		= sizeof(int),
++		.mode		= 0644,
++		.proc_handler	= edma_enable_stp_rstp
+	},
+	{
+-		.procname       = "athr_hdr_eth_type",
+-		.data           = &edma_athr_hdr_eth_type,
+-		.maxlen         = sizeof(int),
+-		.mode           = 0644,
+-		.proc_handler   = edma_ath_hdr_eth_type
++		.procname	= "athr_hdr_eth_type",
++		.data		= &edma_athr_hdr_eth_type,
++		.maxlen		= sizeof(int),
++		.mode		= 0644,
++		.proc_handler	= edma_ath_hdr_eth_type
+	},
+	{
+-		.procname       = "default_group1_vlan_tag",
+-		.data           = &edma_default_group1_vtag,
+-		.maxlen         = sizeof(int),
+-		.mode           = 0644,
+-		.proc_handler   = edma_change_group1_vtag
++		.procname	= "default_group1_vlan_tag",
++		.data		= &edma_default_group1_vtag,
++		.maxlen		= sizeof(int),
++		.mode		= 0644,
++		.proc_handler	= edma_change_group1_vtag
+	},
+	{
+-		.procname       = "default_group2_vlan_tag",
+-		.data           = &edma_default_group2_vtag,
+-		.maxlen         = sizeof(int),
+-		.mode           = 0644,
+-		.proc_handler   = edma_change_group2_vtag
++		.procname	= "default_group2_vlan_tag",
++		.data		= &edma_default_group2_vtag,
++		.maxlen		= sizeof(int),
++		.mode		= 0644,
++		.proc_handler	= edma_change_group2_vtag
+	},
+	{
+-		.procname       = "default_group3_vlan_tag",
+-		.data           = &edma_default_group3_vtag,
+-		.maxlen         = sizeof(int),
+-		.mode           = 0644,
+-		.proc_handler   = edma_change_group3_vtag
++		.procname	= "default_group3_vlan_tag",
++		.data		= &edma_default_group3_vtag,
++		.maxlen		= sizeof(int),
++		.mode		= 0644,
++		.proc_handler	= edma_change_group3_vtag
+	},
+	{
+-		.procname       = "default_group4_vlan_tag",
+-		.data           = &edma_default_group4_vtag,
+-		.maxlen         = sizeof(int),
+-		.mode           = 0644,
+-		.proc_handler   = edma_change_group4_vtag
++		.procname	= "default_group4_vlan_tag",
++		.data		= &edma_default_group4_vtag,
++		.maxlen		= sizeof(int),
++		.mode		= 0644,
++		.proc_handler	= edma_change_group4_vtag
+	},
+	{
+-		.procname       = "default_group5_vlan_tag",
+-		.data           = &edma_default_group5_vtag,
++		.procname	= "default_group5_vlan_tag",
++		.data		= &edma_default_group5_vtag,
++		.maxlen		= sizeof(int),
++		.mode		= 0644,
++		.proc_handler	= edma_change_group5_vtag
++	},
++	{
++		.procname       = "default_group1_bmp",
++		.data           = &edma_default_group1_bmp,
+		.maxlen         = sizeof(int),
+		.mode           = 0644,
+-		.proc_handler   = edma_change_group5_vtag
++		.proc_handler   = edma_change_group1_bmp
+	},
+	{
+-		.procname       = "edma_rss_idt_value",
+-		.data           = &edma_rss_idt_val,
++		.procname       = "default_group2_bmp",
++		.data           = &edma_default_group2_bmp,
+		.maxlen         = sizeof(int),
+		.mode           = 0644,
+-		.proc_handler   = edma_set_rss_idt_value
++		.proc_handler   = edma_change_group2_bmp
+	},
+	{
+-		.procname       = "edma_rss_idt_idx",
+-		.data           = &edma_rss_idt_idx,
++		.procname       = "edma_disable_rss",
++		.data           = &edma_disable_rss,
+		.maxlen         = sizeof(int),
+		.mode           = 0644,
+-		.proc_handler   = edma_set_rss_idt_idx
++		.proc_handler   = edma_disable_rss_func
+	},
+	{}
+ };
+@@ -562,16 +970,17 @@ static struct ctl_table edma_table[] = {
+  * }
+  */
+ static const struct net_device_ops edma_axi_netdev_ops = {
+-	.ndo_open               = edma_open,
+-	.ndo_stop               = edma_close,
+-	.ndo_start_xmit         = edma_xmit,
+-	.ndo_set_mac_address    = edma_set_mac_addr,
++	.ndo_open		= edma_open,
++	.ndo_stop		= edma_close,
++	.ndo_start_xmit		= edma_xmit,
++	.ndo_set_mac_address	= edma_set_mac_addr,
++	.ndo_select_queue	= edma_select_xps_queue,
+ #ifdef CONFIG_RFS_ACCEL
+-	.ndo_rx_flow_steer      = edma_rx_flow_steer,
++	.ndo_rx_flow_steer	= edma_rx_flow_steer,
+	.ndo_register_rfs_filter = edma_register_rfs_filter,
+	.ndo_get_default_vlan_tag = edma_get_default_vlan_tag,
+ #endif
+-	.ndo_get_stats          = edma_get_stats,
++	.ndo_get_stats		= edma_get_stats,
+	.ndo_change_mtu		= edma_change_mtu,
+ };
+
+@@ -594,7 +1003,7 @@ static int edma_axi_probe(struct platform_device *pdev)
+	struct mii_bus *miibus = NULL;
+	struct edma_mdio_data *mdio_data = NULL;
+	int i, j, k, err = 0;
+-	int portid_bmp;
++	u32 portid_bmp;
+	int idx = 0, idx_mac = 0;
+
+	if (CONFIG_NR_CPUS != EDMA_CPU_CORES_SUPPORTED) {
+@@ -614,7 +1023,7 @@ static int edma_axi_probe(struct platform_device *pdev)
+
+	edma_cinfo->pdev = pdev;
+
+-	of_property_read_u32(np, "qcom,num_gmac", &edma_cinfo->num_gmac);
++	of_property_read_u32(np, "qcom,num-gmac", &edma_cinfo->num_gmac);
+	if (edma_cinfo->num_gmac > EDMA_MAX_PORTID_SUPPORTED) {
+		pr_err("Invalid DTSI Entry for qcom,num_gmac\n");
+		err = -EINVAL;
+@@ -661,7 +1070,7 @@ static int edma_axi_probe(struct platform_device *pdev)
+	hw->rx_intr_mask = EDMA_RX_IMR_NORMAL_MASK;
+
+	of_property_read_u32(np, "qcom,page-mode", &edma_cinfo->page_mode);
+-	of_property_read_u32(np, "qcom,rx_head_buf_size",
++	of_property_read_u32(np, "qcom,rx-head-buf-size",
+			     &hw->rx_head_buff_size);
+
+	if (overwrite_mode) {
+@@ -757,7 +1166,7 @@ static int edma_axi_probe(struct platform_device *pdev)
+		}
+	}
+
+-	if (of_property_read_bool(np, "qcom,mdio_supported")) {
++	if (of_property_read_bool(np, "qcom,mdio-supported")) {
+		mdio_node = of_find_compatible_node(NULL, NULL,
+						    "qcom,ipq4019-mdio");
+		if (!mdio_node) {
+@@ -785,6 +1194,7 @@ static int edma_axi_probe(struct platform_device *pdev)
+		}
+
+		miibus = mdio_data->mii_bus;
++		miibus_gb = mdio_data->mii_bus;
+	}
+
+	for_each_available_child_of_node(np, pnp) {
+@@ -833,19 +1243,19 @@ static int edma_axi_probe(struct platform_device *pdev)
+		adapter[i]->edma_cinfo = edma_cinfo;
+		edma_netdev[i]->netdev_ops = &edma_axi_netdev_ops;
+		edma_netdev[i]->features = NETIF_F_HW_CSUM | NETIF_F_RXCSUM
+-				      | NETIF_F_HW_VLAN_CTAG_TX
+-				      | NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_SG |
+-				      NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_GRO;
++				| NETIF_F_HW_VLAN_CTAG_TX
++				| NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_SG |
++				NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_GRO;
+		edma_netdev[i]->hw_features = NETIF_F_HW_CSUM | NETIF_F_RXCSUM |
+-				NETIF_F_HW_VLAN_CTAG_RX
++				NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_TX
+				| NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6 |
+				NETIF_F_GRO;
+		edma_netdev[i]->vlan_features = NETIF_F_HW_CSUM | NETIF_F_SG |
+-					   NETIF_F_TSO | NETIF_F_TSO6 |
+-					   NETIF_F_GRO;
++					NETIF_F_TSO | NETIF_F_TSO6 |
++					NETIF_F_GRO;
+		edma_netdev[i]->wanted_features = NETIF_F_HW_CSUM | NETIF_F_SG |
+-					     NETIF_F_TSO | NETIF_F_TSO6 |
+-					     NETIF_F_GRO;
++					NETIF_F_TSO | NETIF_F_TSO6 |
++					NETIF_F_GRO;
+
+ #ifdef CONFIG_RFS_ACCEL
+		edma_netdev[i]->features |=  NETIF_F_RXHASH | NETIF_F_NTUPLE;
+@@ -853,6 +1263,13 @@ static int edma_axi_probe(struct platform_device *pdev)
+		edma_netdev[i]->vlan_features |= NETIF_F_RXHASH | NETIF_F_NTUPLE;
+		edma_netdev[i]->wanted_features |= NETIF_F_RXHASH | NETIF_F_NTUPLE;
+ #endif
++		if (edma_cinfo->fraglist_mode) {
++			edma_netdev[i]->features |= NETIF_F_FRAGLIST;
++			edma_netdev[i]->hw_features |= NETIF_F_FRAGLIST;
++			edma_netdev[i]->vlan_features |= NETIF_F_FRAGLIST;
++			edma_netdev[i]->wanted_features |= NETIF_F_FRAGLIST;
++		}
++
+		edma_set_ethtool_ops(edma_netdev[i]);
+
+		/* This just fill in some default MAC address
+@@ -905,7 +1322,7 @@ static int edma_axi_probe(struct platform_device *pdev)
+			break;
+
+		/* Populate port-id to netdev lookup table */
+-		vlan_tag = of_get_property(pnp, "vlan_tag", &len);
++		vlan_tag = of_get_property(pnp, "vlan-tag", &len);
+		if (!vlan_tag) {
+			pr_err("Vlan tag parsing Failed.\n");
+			goto err_rmap_alloc_fail;
+@@ -927,14 +1344,18 @@ static int edma_axi_probe(struct platform_device *pdev)
+			portid_bmp &= ~(1 << (port_bit - 1));
+		}
+
+-		if (!of_property_read_u32(pnp, "qcom,poll_required",
++		if (of_property_read_u32(pnp, "qcom,poll-required-dynamic",
++					 &adapter[idx]->poll_required_dynamic))
++			adapter[idx]->poll_required_dynamic = 0;
++
++		if (!of_property_read_u32(pnp, "qcom,poll-required",
+					  &adapter[idx]->poll_required)) {
+			if (adapter[idx]->poll_required) {
+-				of_property_read_u32(pnp, "qcom,phy_mdio_addr",
++				of_property_read_u32(pnp, "qcom,phy-mdio-addr",
+						     &adapter[idx]->phy_mdio_addr);
+-				of_property_read_u32(pnp, "qcom,forced_speed",
++				of_property_read_u32(pnp, "qcom,forced-speed",
+						     &adapter[idx]->forced_speed);
+-				of_property_read_u32(pnp, "qcom,forced_duplex",
++				of_property_read_u32(pnp, "qcom,forced-duplex",
+						     &adapter[idx]->forced_duplex);
+
+				/* create a phyid using MDIO bus id
+@@ -1073,7 +1494,10 @@ static int edma_axi_probe(struct platform_device *pdev)
+	edma_enable_rx_ctrl(&edma_cinfo->hw);
+
+	for (i = 0; i < edma_cinfo->num_gmac; i++) {
+-		if (adapter[i]->poll_required) {
++		u32 port_id;
++		if (!(adapter[i]->poll_required)) {
++			adapter[i]->phydev = NULL;
++		} else {
+			adapter[i]->phydev =
+				phy_connect(edma_netdev[i],
+					    (const char *)adapter[i]->phy_id,
+@@ -1090,9 +1514,11 @@ static int edma_axi_probe(struct platform_device *pdev)
+				adapter[i]->phydev->supported |=
+					SUPPORTED_Pause |
+					SUPPORTED_Asym_Pause;
++				portid_bmp = adapter[i]->dp_bitmap >> 1;
++				port_id = ffs(portid_bmp);
++				edma_phydev[port_id - 1] = adapter[i]->phydev;
++				phy_dev_state[port_id - 1] = 1;
+			}
+-		} else {
+-			adapter[i]->phydev = NULL;
+		}
+	}
+
+@@ -1170,16 +1596,14 @@ static int edma_axi_remove(struct platform_device *pdev)
+	edma_write_reg(EDMA_REG_TX_ISR, 0xffff);
+ #ifdef CONFIG_RFS_ACCEL
+	for (i = 0; i < edma_cinfo->num_gmac; i++) {
+-		free_irq_cpu_rmap(edma_netdev[i]->rx_cpu_rmap);
+-		edma_netdev[i]->rx_cpu_rmap = NULL;
++		free_irq_cpu_rmap(edma_netdev[0]->rx_cpu_rmap);
++		edma_netdev[0]->rx_cpu_rmap = NULL;
+	}
+ #endif
+
+-	for (i = 0; i < edma_cinfo->num_gmac; i++) {
+-		struct edma_adapter *adapter = netdev_priv(edma_netdev[i]);
+-
+-		if (adapter->phydev)
+-			phy_disconnect(adapter->phydev);
++	for (i = 0; i < EDMA_MAX_PORTID_SUPPORTED; i++) {
++		if (edma_phydev[i])
++			phy_disconnect(edma_phydev[i]);
+	}
+
+	del_timer_sync(&edma_stats_timer);
+@@ -1216,5 +1640,4 @@ static struct platform_driver edma_axi_driver = {
+ module_platform_driver(edma_axi_driver);
+
+ MODULE_AUTHOR("Qualcomm Atheros Inc");
+-MODULE_DESCRIPTION("QCA ESS EDMA driver");
+ MODULE_LICENSE("GPL");
+diff --git a/drivers/net/ethernet/qualcomm/essedma/edma_ethtool.c b/drivers/net/ethernet/qualcomm/essedma/edma_ethtool.c
+index 377dc3e..423f5f0 100644
+--- a/drivers/net/ethernet/qualcomm/essedma/edma_ethtool.c
++++ b/drivers/net/ethernet/qualcomm/essedma/edma_ethtool.c
+@@ -184,7 +184,30 @@ static int edma_get_settings(struct net_device *netdev,
+ {
+	struct edma_adapter *adapter = netdev_priv(netdev);
+
+-	if (adapter->poll_required) {
++	if (!(adapter->poll_required)) {
++		/* If the speed/duplex for this GMAC is forced and we
++		 * are not polling for link state changes, return the
++		 * values as specified by platform. This will be true
++		 * for GMACs connected to switch, and interfaces that
++		 * do not use a PHY.
++		 */
++		if (adapter->forced_speed != SPEED_UNKNOWN) {
++			/* set speed and duplex */
++			ethtool_cmd_speed_set(ecmd, SPEED_1000);
++			ecmd->duplex = DUPLEX_FULL;
++
++			/* Populate capabilities advertised by self */
++			ecmd->advertising = 0;
++			ecmd->autoneg = 0;
++			ecmd->port = PORT_TP;
++			ecmd->transceiver = XCVR_EXTERNAL;
++		} else {
++			/* non link polled and non
++			 * forced speed/duplex interface
++			 */
++			return -EIO;
++		}
++	} else {
+		struct phy_device *phydev = NULL;
+		uint16_t phyreg;
+
+@@ -226,31 +249,6 @@ static int edma_get_settings(struct net_device *netdev,
+
+		if (phyreg & LPA_1000FULL)
+			ecmd->lp_advertising |= ADVERTISED_1000baseT_Full;
+-	} else {
+-		/* If the speed/duplex for this GMAC is forced and we
+-		 * are not polling for link state changes, return the
+-		 * values as specified by platform. This will be true
+-		 * for GMACs connected to switch, and interfaces that
+-		 * do not use a PHY.
+-		 */
+-		if (!(adapter->poll_required)) {
+-			if (adapter->forced_speed != SPEED_UNKNOWN) {
+-				/* set speed and duplex */
+-				ethtool_cmd_speed_set(ecmd, SPEED_1000);
+-				ecmd->duplex = DUPLEX_FULL;
+-
+-				/* Populate capabilities advertised by self */
+-				ecmd->advertising = 0;
+-				ecmd->autoneg = 0;
+-				ecmd->port = PORT_TP;
+-				ecmd->transceiver = XCVR_EXTERNAL;
+-			} else {
+-				/* non link polled and non
+-				 * forced speed/duplex interface
+-				 */
+-				return -EIO;
+-			}
+-		}
+	}
+
+	return 0;
+diff --git a/drivers/net/ethernet/qualcomm/essedma/ess_edma.h b/drivers/net/ethernet/qualcomm/essedma/ess_edma.h
+index 81a5ddd..dd76322 100644
+--- a/drivers/net/ethernet/qualcomm/essedma/ess_edma.h
++++ b/drivers/net/ethernet/qualcomm/essedma/ess_edma.h
+@@ -317,6 +317,8 @@ struct edma_hw;
+
+ /* RRD descriptor fields */
+ #define EDMA_RRD_NUM_RFD_MASK 0x000F
++#define EDMA_RRD_PKT_SIZE_MASK 0x3FFF
++#define EDMA_RRD_SRC_PORT_NUM_MASK 0x4000
+ #define EDMA_RRD_SVLAN 0x8000
+ #define EDMA_RRD_FLOW_COOKIE_MASK 0x07FF;
+
+--
+2.7.2
diff --git a/target/linux/ipq806x/patches-4.9/854-net-phy-Export-phy-statemachine-API.patch b/target/linux/ipq806x/patches-4.9/854-net-phy-Export-phy-statemachine-API.patch
new file mode 100644
index 0000000..2bb213d
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/854-net-phy-Export-phy-statemachine-API.patch
@@ -0,0 +1,36 @@ 
+From 69f50b2694e9e9a3e927c21b900112d7adb581c9 Mon Sep 17 00:00:00 2001
+From: Ram Chandra Jangir <rjangir@codeaurora.org>
+Date: Tue, 28 Mar 2017 14:00:00 +0530
+Subject: [PATCH 2/5] net/phy: Export phy statemachine API
+
+When any port with link-detection enabled changes group
+we need to start/stop phy detection with phy_start_machine()
+and phy_stop_machine() at runtime for those specific ports
+
+Signed-off-by :Ram Chandra Jangir <rjangir@codeaurora.org>
+---
+ drivers/net/phy/phy.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
+index 00fe2ab..4058d5c 100644
+--- a/drivers/net/phy/phy.c
++++ b/drivers/net/phy/phy.c
+@@ -650,6 +650,7 @@ void phy_start_machine(struct phy_device *phydev)
+ {
+	queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, HZ);
+ }
++EXPORT_SYMBOL(phy_start_machine);
+
+ /**
+  * phy_trigger_machine - trigger the state machine to run
+@@ -687,6 +688,7 @@ void phy_stop_machine(struct phy_device *phydev)
+		phydev->state = PHY_UP;
+	mutex_unlock(&phydev->lock);
+ }
++EXPORT_SYMBOL(phy_stop_machine);
+
+ /**
+  * phy_error - enter HALTED state for this PHY device
+--
+2.7.2
diff --git a/target/linux/ipq806x/patches-4.9/855-clk-qcom-ipq4019-add-ess-reset.patch b/target/linux/ipq806x/patches-4.9/855-clk-qcom-ipq4019-add-ess-reset.patch
new file mode 100644
index 0000000..d33b8ab
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/855-clk-qcom-ipq4019-add-ess-reset.patch
@@ -0,0 +1,58 @@ 
+From 7efb48a343ca368f83359d3a7087dd5ab25a283a Mon Sep 17 00:00:00 2001
+From: Ram Chandra Jangir <rjangir@codeaurora.org>
+Date: Tue, 28 Mar 2017 22:35:33 +0530
+Subject: [PATCH 5/8] clk: qcom: ipq4019: add ess reset
+
+Added the ESS reset in IPQ4019 GCC.
+
+Signed-off-by: Ram Chandra Jangir <rjangir@codeaurora.org>
+---
+ drivers/clk/qcom/gcc-ipq4019.c               | 11 +++++++++++
+ include/dt-bindings/clock/qcom,gcc-ipq4019.h | 11 +++++++++++
+ 2 files changed, 22 insertions(+)
+
+diff --git a/drivers/clk/qcom/gcc-ipq4019.c b/drivers/clk/qcom/gcc-ipq4019.c
+index d0a5d0e..99a7bdb 100644
+--- a/drivers/clk/qcom/gcc-ipq4019.c
++++ b/drivers/clk/qcom/gcc-ipq4019.c
+@@ -1289,6 +1289,17 @@ static const struct qcom_reset_map gcc_ipq4019_resets[] = {
+	[GCC_TCSR_BCR] = {0x22000, 0},
+	[GCC_MPM_BCR] = {0x24000, 0},
+	[GCC_SPDM_BCR] = {0x25000, 0},
++	[ESS_MAC1_ARES] = {0x1200C, 0},
++	[ESS_MAC2_ARES] = {0x1200C, 1},
++	[ESS_MAC3_ARES] = {0x1200C, 2},
++	[ESS_MAC4_ARES] = {0x1200C, 3},
++	[ESS_MAC5_ARES] = {0x1200C, 4},
++	[ESS_PSGMII_ARES] = {0x1200C, 5},
++	[ESS_MAC1_CLK_DIS] = {0x1200C, 8},
++	[ESS_MAC2_CLK_DIS] = {0x1200C, 9},
++	[ESS_MAC3_CLK_DIS] = {0x1200C, 10},
++	[ESS_MAC4_CLK_DIS] = {0x1200C, 11},
++	[ESS_MAC5_CLK_DIS] = {0x1200C, 12},
+ };
+
+ static const struct regmap_config gcc_ipq4019_regmap_config = {
+diff --git a/include/dt-bindings/clock/qcom,gcc-ipq4019.h b/include/dt-bindings/clock/qcom,gcc-ipq4019.h
+index 6240e5b..9a299e7 100644
+--- a/include/dt-bindings/clock/qcom,gcc-ipq4019.h
++++ b/include/dt-bindings/clock/qcom,gcc-ipq4019.h
+@@ -154,5 +154,16 @@
+ #define GCC_QDSS_BCR					69
+ #define GCC_MPM_BCR					70
+ #define GCC_SPDM_BCR					71
++#define ESS_MAC1_ARES					72
++#define ESS_MAC2_ARES					73
++#define ESS_MAC3_ARES					74
++#define ESS_MAC4_ARES					75
++#define ESS_MAC5_ARES					76
++#define ESS_PSGMII_ARES					77
++#define ESS_MAC1_CLK_DIS				78
++#define ESS_MAC2_CLK_DIS				79
++#define ESS_MAC3_CLK_DIS				80
++#define ESS_MAC4_CLK_DIS				81
++#define ESS_MAC5_CLK_DIS				82
+
+ #endif
+--
+2.7.2
diff --git a/target/linux/ipq806x/patches-4.9/857-ipq40xx-Fix-mdio-driver-to-work-with-IPQ40xx-SoC.patch b/target/linux/ipq806x/patches-4.9/857-ipq40xx-Fix-mdio-driver-to-work-with-IPQ40xx-SoC.patch
new file mode 100644
index 0000000..bb72169
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/857-ipq40xx-Fix-mdio-driver-to-work-with-IPQ40xx-SoC.patch
@@ -0,0 +1,100 @@ 
+From 1d8433be4af4a5862b8805a5668d404bd6fde945 Mon Sep 17 00:00:00 2001
+From: Ram Chandra Jangir <rjangir@codeaurora.org>
+Date: Tue, 28 Mar 2017 22:32:07 +0530
+Subject: [PATCH] ipq40xx: Fix mdio driver to work with IPQ40xx SoC
+
+- Add phy-reset-gpio support in probe function
+- Add proper assignment of mii_bus read/write operations
+
+Signed-off-by: Ram Chandra Jangir <rjangir@codeaurora.org>
+---
+ drivers/net/phy/mdio-ipq40xx.c | 56 +++++++++++++++++++++++++++++++++++++++---
+ 1 file changed, 53 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/net/phy/mdio-ipq40xx.c b/drivers/net/phy/mdio-ipq40xx.c
+index 335d531..e969f06 100644
+--- a/drivers/net/phy/mdio-ipq40xx.c
++++ b/drivers/net/phy/mdio-ipq40xx.c
+@@ -22,6 +22,7 @@
+ #include <linux/of_mdio.h>
+ #include <linux/phy.h>
+ #include <linux/platform_device.h>
++#include <linux/of_gpio.h>
+
+ #define MDIO_CTRL_0_REG		0x40
+ #define MDIO_CTRL_1_REG		0x44
+@@ -122,11 +123,60 @@ static int ipq40xx_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
+	return 0;
+ }
+
++static int ipq40xx_phy_reset(struct platform_device *pdev)
++{
++	struct device_node *mdio_node;
++	int phy_reset_gpio_number;
++	int ret;
++
++	mdio_node = of_find_node_by_name(NULL, "mdio");
++	if (!mdio_node) {
++		dev_err(&pdev->dev, "Could not find mdio node\n");
++		return -ENOENT;
++	}
++
++	ret = of_get_named_gpio(mdio_node, "phy-reset-gpio", 0);
++	if (ret < 0) {
++		dev_err(&pdev->dev, "Could not find phy-reset-gpio\n");
++		return ret;
++	}
++
++	phy_reset_gpio_number = ret;
++
++	ret = gpio_request(phy_reset_gpio_number, "phy-reset-gpio");
++	if (ret) {
++		dev_err(&pdev->dev, "Can't get phy-reset-gpio %d\n", ret);
++		return ret;
++	}
++
++	ret = gpio_direction_output(phy_reset_gpio_number, 0x0);
++	if (ret) {
++		dev_err(&pdev->dev,
++			"Can't set direction for phy-reset-gpio %d\n", ret);
++		goto phy_reset_out;
++	}
++
++	usleep_range(1000, 10005);
++
++	gpio_set_value(phy_reset_gpio_number, 0x01);
++
++phy_reset_out:
++	gpio_free(phy_reset_gpio_number);
++
++	return ret;
++}
++
+ static int ipq40xx_mdio_probe(struct platform_device *pdev)
+ {
+	struct ipq40xx_mdio_data *am;
+	struct resource *res;
+-	int i;
++	int i, ret;
++
++	ret = ipq40xx_phy_reset(pdev);
++	if (ret) {
++		dev_err(&pdev->dev, "Could not find qca8075 reset gpio\n");
++		return ret;
++	}
+
+	am = devm_kzalloc(&pdev->dev, sizeof(*am), GFP_KERNEL);
+	if (!am)
+@@ -151,8 +201,8 @@ static int ipq40xx_mdio_probe(struct platform_device *pdev)
+	writel(CTRL_0_REG_DEFAULT_VALUE, am->membase + MDIO_CTRL_0_REG);
+
+	am->mii_bus->name = "ipq40xx_mdio";
+-	am->mii_bus->read = ipq40xx_mdio_read;
+-	am->mii_bus->write = ipq40xx_mdio_write;
++	am->mii_bus->read = &ipq40xx_mdio_read;
++	am->mii_bus->write = &ipq40xx_mdio_write;
+	memcpy(am->mii_bus->irq, am->phy_irq, sizeof(am->phy_irq));
+	am->mii_bus->priv = am;
+	am->mii_bus->parent = &pdev->dev;
+--
+2.7.2
diff --git a/target/linux/ipq806x/patches-4.9/858-arm-dts-Add-ess-switch-device-for-IPQ4019.patch b/target/linux/ipq806x/patches-4.9/858-arm-dts-Add-ess-switch-device-for-IPQ4019.patch
new file mode 100644
index 0000000..4598451
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.9/858-arm-dts-Add-ess-switch-device-for-IPQ4019.patch
@@ -0,0 +1,486 @@ 
+From 4842020af3b39ce8c7c9a92de106d8fffd92b7c0 Mon Sep 17 00:00:00 2001
+From: Ram Chandra Jangir <rjangir@codeaurora.org>
+Date: Tue, 28 Mar 2017 14:00:00 +0530
+Subject: [PATCH] arm: dts: Add ess switch device for IPQ4019
+
+- Update ipq4019 dts nodes for mdio interface, ess-switch
+   and edma driver.
+- Update dt documentation for qca-ess ethernet subsystem
+
+Signed-off-by: xiaofeis <xiaofeis@codeaurora.org>
+Signed-off-by: Ram Chandra Jangir <rjangir@codeaurora.org>
+---
+ Documentation/devicetree/bindings/net/qca-ess.txt | 107 ++++++++++++++++++
+ arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1-c1.dts   |  10 +-
+ arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi     |  29 +++++
+ arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi     |  72 +++++++++++++
+ arch/arm/boot/dts/qcom-ipq4019.dtsi               | 125 ++++++++++++----------
+ 5 files changed, 283 insertions(+), 60 deletions(-)
+ create mode 100644 Documentation/devicetree/bindings/net/qca-ess.txt
+
+diff --git a/Documentation/devicetree/bindings/net/qca-ess.txt b/Documentation/devicetree/bindings/net/qca-ess.txt
+new file mode 100644
+index 0000000..c192774
+--- /dev/null
++++ b/Documentation/devicetree/bindings/net/qca-ess.txt
+@@ -0,0 +1,107 @@
++QCA Ethernet Subsystem
++----------------------
++
++This driver adds support for the Ethernet subsystem
++
++1. QCA Ethernet DMA
++----------------------
++
++Required properties:
++  - compatible = "qcom,ess-edma";
++
++Optional properties:
++
++Example:
++    edma@c080000 {
++	compatible = "qcom,ess-edma";
++	reg = <0xc080000 0x8000>;
++	qcom,page-mode = <0>;
++	qcom,rx_head_buf_size = <1540>;
++	qcom,port_id_wan = <0x5>;
++	interrupts = <0 65 1>,
++		   <0 66 1>,
++		   <0 67 1>,
++		   <0 68 1>,
++		   <0 69 1>,
++		   <0 70 1>,
++		   <0 71 1>,
++		   <0 72 1>,
++		   <0 73 1>,
++		   <0 74 1>,
++		   <0 75 1>,
++		   <0 76 1>,
++		   <0 77 1>,
++		   <0 78 1>,
++		   <0 79 1>,
++		   <0 80 1>,
++		   <0 240 1>,
++		   <0 241 1>,
++		   <0 242 1>,
++		   <0 243 1>,
++		   <0 244 1>,
++		   <0 245 1>,
++		   <0 246 1>,
++		   <0 247 1>,
++		   <0 248 1>,
++		   <0 249 1>,
++		   <0 250 1>,
++		   <0 251 1>,
++		   <0 252 1>,
++		   <0 253 1>,
++		   <0 254 1>,
++		   <0 255 1>;
++   };
++
++2. QCA Ethernet Switch
++----------------------
++
++Required properties:
++  - compatible = "qcom,ess-switch";
++
++Optional properties:
++
++Example:
++
++	ess-switch@c000000 {
++		compatible = "qcom,ess-switch";
++		reg = <0xc000000 0x80000>; /* 512KB */
++		switch_access_mode = "local bus";
++		resets = <&gcc ESS_RESET>;
++		switch_cpu_bmp = <0x1>;  /* cpu port bitmap */
++		switch_lan_bmp = <0x1e>; /* lan port bitmap */
++		switch_wan_bmp = <0x20>; /* wan port bitmap */
++	};
++
++3. QCA Ethernet PHY mode
++-------------------------
++
++Required properties:
++  - compatible = "qcom,ess-psgmii";
++
++Optional properties:
++
++Example:
++
++	ess-psgmii@98000 {
++		compatible = "qcom,ess-psgmii";
++		reg = <0x98000 0x800>; /* 2k */
++		psgmii_access_mode = "local bus";
++	};
++
++4. MDIO Interface
++----------------------
++
++Required properties:
++  - compatible = "qcom,ipq4019-mdio";
++
++Optional properties:
++
++Example:
++
++	mdio@90000 {
++		#address-cells = <1>;
++		#size-cells = <1>;
++		compatible = "qcom,ipq4019-mdio";
++		reg = <0x90000 0x64>;
++	};
++
+diff --git a/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1-c1.dts b/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1-c1.dts
+index 0d92f1b..ec75396 100644
+--- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1-c1.dts
++++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1-c1.dts
+@@ -18,5 +18,13 @@
+
+ / {
+	model = "Qualcomm Technologies, Inc. IPQ40xx/AP-DK01.1-C1";
+-
++		soc {
++			mdio@90000 {
++			status = "ok";
++			pinctrl-0 = <&mdio_pins>;
++			pinctrl-names = "default";
++			bias-disable;
++			phy-reset-gpio = <&tlmm 59 0>;
++		};
++        };
+ };
+diff --git a/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi b/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi
+index 2a5cc5e..bf4fde6 100644
+--- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk01.1.dtsi
+@@ -41,6 +41,19 @@
+		};
+
+		pinctrl@0x01000000 {
++			mdio_pins: mdio_pinmux {
++				mux_1 {
++					pins = "gpio53";
++					function = "mdio1";
++					bias-bus-hold;
++				};
++				mux_2 {
++					pins = "gpio52";
++					function = "mdc";
++					bias-bus-hold;
++				};
++			};
++
+			serial_pins: serial_pinmux {
+				mux {
+					pins = "gpio60", "gpio61";
+@@ -131,5 +144,21 @@
+		usb2: usb2@60f8800 {
+			status = "ok";
+		};
++
++		ess-switch@c000000 {
++			status = "ok";
++			switch_mac_mode = <0x0>; /* mac mode for RGMII RMII */
++			switch_initvlas = <
++				0x0007c 0x54 /* PORT0_STATUS */
++			>;
++		};
++
++		ess-psgmii@98000 {
++			status = "ok";
++		};
++
++		edma@c080000 {
++			status = "ok";
++		};
+	};
+ };
+diff --git a/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi b/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi
+index b68fc1a..1aee3b1 100644
+--- a/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi
+@@ -88,6 +88,19 @@
+					bias-disable;
+				};
+			};
++
++			mdio_pins: mdio_pinmux {
++				mux_1 {
++					pins = "gpio6";
++					function = "mdio0";
++					bias-bus-hold;
++				};
++				mux_2 {
++					pins = "gpio7";
++					function = "mdc";
++					bias-bus-hold;
++				};
++			};
+		};
+
+		blsp_dma: dma@7884000 {
+@@ -118,6 +131,14 @@
+			status = "ok";
+		};
+
++		mdio@90000 {
++			status = "ok";
++			pinctrl-0 = <&mdio_pins>;
++			pinctrl-names = "default";
++			phy-reset-gpio = <&tlmm 47 0>;
++			bias-disable;
++		};
++
+		serial@78af000 {
+			pinctrl-0 = <&serial_0_pins>;
+			pinctrl-names = "default";
+@@ -161,5 +182,56 @@
+		watchdog@b017000 {
+			status = "ok";
+		};
++
++		ess-switch@c000000 {
++			status = "ok";
++			switch_mac_mode = <0x0>; /* mac mode for RGMII RMII */
++			switch_initvlas = <
++				0x0007c 0x54 /* PORT0_STATUS */
++			>;
++			led_source@0 {
++				led = <0x3>;     /*led number */
++				source = <0x1>;  /*source id 1 */
++				mode = "normal"; /*on,off,blink,normal */
++				speed = "all";   /*10M,100M,1000M,all */
++				freq = "auto";   /*2Hz,4Hz,8Hz,auto*/
++			};
++			led_source@1 {
++				led = <0x4>;     /*led number */
++				source = <0x4>;  /*source id 4 */
++				mode = "normal"; /*on,off,blink,normal */
++				speed = "all";   /*10M,100M,1000M,all */
++				freq = "auto";   /*2Hz,4Hz,8Hz,auto*/
++			};
++			led_source@2 {
++				led = <0x5>;     /*led number */
++				source = <0x7>;  /*source id 7 */
++				mode = "normal"; /*on,off,blink,normal */
++				speed = "all";   /*10M,100M,1000M,all */
++				freq = "auto";   /*2Hz,4Hz,8Hz,auto*/
++			};
++			led_source@3 {
++				led = <0x6>;     /*led number */
++				source = <0xa>;  /*source id 10 */
++				mode = "normal"; /*on,off,blink,normal */
++				speed = "all";   /*10M,100M,1000M,all */
++				freq = "auto";   /*2Hz,4Hz,8Hz,auto*/
++			};
++			led_source@4 {
++				led = <0x7>;     /*led number */
++				source = <0xd>;  /*source id 13 */
++				mode = "normal"; /*on,off,blink,normal */
++				speed = "all";   /*10M,100M,1000M,all */
++				freq = "auto";   /*2Hz,4Hz,8Hz,auto*/
++			};
++		};
++
++		ess-psgmii@98000 {
++			status = "ok";
++		};
++
++		edma@c080000 {
++			status = "ok";
++		};
+	};
+ };
+diff --git a/arch/arm/boot/dts/qcom-ipq4019.dtsi b/arch/arm/boot/dts/qcom-ipq4019.dtsi
+index 7013c85..9793611 100644
+--- a/arch/arm/boot/dts/qcom-ipq4019.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi
+@@ -26,8 +26,8 @@
+	aliases {
+		spi0 = &spi_0;
+		i2c0 = &i2c_0;
+-		ethernet0 = &gmac0;
+-		ethernet1 = &gmac1;
++		ethernet0 = "/soc/edma/gmac0";
++		ethernet1 = "/soc/edma/gmac1";
+	};
+
+	cpus {
+@@ -325,43 +325,47 @@
+
+		mdio@90000 {
+			#address-cells = <1>;
+-			#size-cells = <0>;
++			#size-cells = <1>;
+			compatible = "qcom,ipq4019-mdio";
+			reg = <0x90000 0x64>;
+			status = "disabled";
+
+-			ethernet-phy@0 {
++			phy0: ethernet-phy@0 {
+				reg = <0>;
+			};
+
+-			ethernet-phy@1 {
++			phy1: ethernet-phy@1 {
+				reg = <1>;
+			};
+
+-			ethernet-phy@2 {
++			phy2: ethernet-phy@2 {
+				reg = <2>;
+			};
+
+-			ethernet-phy@3 {
++			phy3: ethernet-phy@3 {
+				reg = <3>;
+			};
+
+-			ethernet-phy@4 {
++			phy4: ethernet-phy@4 {
+				reg = <4>;
+			};
+		};
+
+		ess-switch@c000000 {
+			compatible = "qcom,ess-switch";
+-			reg = <0xc000000 0x80000>;
++			reg = <0xc000000 0x80000>; /* 512KB */
+			switch_access_mode = "local bus";
+-			resets = <&gcc ESS_RESET>;
+-			reset-names = "ess_rst";
++			resets = <&gcc ESS_RESET>, <&gcc ESS_MAC1_CLK_DIS>, \
++				<&gcc ESS_MAC2_CLK_DIS>, <&gcc ESS_MAC3_CLK_DIS>, \
++				<&gcc ESS_MAC4_CLK_DIS>, <&gcc ESS_MAC5_CLK_DIS>;
++			reset-names = "ess_rst","ess_mac1_clk_dis", \
++				"ess_mac2_clk_dis","ess_mac3_clk_dis", \
++				"ess_mac4_clk_dis", "ess_mac5_clk_dis";
+			clocks = <&gcc GCC_ESS_CLK>;
+			clock-names = "ess_clk";
+-			switch_cpu_bmp = <0x1>;
+-			switch_lan_bmp = <0x1e>;
+-			switch_wan_bmp = <0x20>;
++			switch_cpu_bmp = <0x1>;  /* cpu port bitmap */
++			switch_lan_bmp = <0x1e>; /* lan port bitmap */
++			switch_wan_bmp = <0x20>; /* wan port bitmap */
+			switch_mac_mode = <0>; /* PORT_WRAPPER_PSGMII */
+			switch_initvlas = <0x7c 0x54>;
+			status = "disabled";
+@@ -369,8 +373,10 @@
+
+		ess-psgmii@98000 {
+			compatible = "qcom,ess-psgmii";
+-			reg = <0x98000 0x800>;
++			reg = <0x98000 0x800>; /* 2k */
+			psgmii_access_mode = "local bus";
++			resets = <&gcc ESS_PSGMII_ARES>;
++			reset-names = "psgmii_rst";
+			status = "disabled";
+		};
+
+@@ -378,57 +384,58 @@
+			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>;
++			qcom,rx-head-buf-size = <1540>;
++			qcom,num-gmac = <2>;
++			qcom,mdio-supported;
++			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 {
++			gmac0 {
+				local-mac-address = [00 00 00 00 00 00];
+-				vlan_tag = <1 0x1f>;
++				qcom,phy-mdio-addr = <4>;
++				qcom,poll-required = <1>;
++				qcom,poll-required-dynamic = <1>;
++				qcom,forced-speed = <1000>;
++				qcom,forced-duplex = <1>;
++				vlan-tag = <2 0x20>;
+			};
+
+-			gmac1: 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>;
++				qcom,poll-required-dynamic = <1>;
++				vlan-tag = <1 0x1e>;
+			};
+		};
+
+--
+2.7.2
diff --git a/target/linux/ipq806x/profiles/00-default.mk b/target/linux/ipq806x/profiles/00-default.mk
index 9baa24f..c4b58a2 100644
--- a/target/linux/ipq806x/profiles/00-default.mk
+++ b/target/linux/ipq806x/profiles/00-default.mk
@@ -1,7 +1,8 @@ 
 define Profile/Default
   NAME:=Default Profile
   PRIORITY:=1
-  PACKAGES:=ath10k-firmware-qca99x0 ath10k-firmware-qca988x ath10k-firmware-qca9984
+  PACKAGES:=ath10k-firmware-qca99x0 ath10k-firmware-qca988x ath10k-firmware-qca9984 \
+	    ath10k-firmware-qca4019
 endef
 
 define Profile/Default/Description