[source] ipq806x: sync with latest patches sent by QCA

LEDE Commits lede-commits at lists.infradead.org
Thu Aug 18 00:49:30 PDT 2016


blogic pushed a commit to source.git, branch master:
https://git.lede-project.org/?p=source.git;a=commitdiff;h=d7e4b9babb6ce8bf66c4c2e721b78c30d09afdda

commit d7e4b9babb6ce8bf66c4c2e721b78c30d09afdda
Author: John Crispin <john at phrozen.org>
AuthorDate: Thu Aug 11 16:34:08 2016 +0200

    ipq806x: sync with latest patches sent by QCA
    
    Signed-off-by: John Crispin <john at phrozen.org>
---
 target/linux/ipq806x/modules.mk                    |  18 +-
 .../097-usb-dwc3-add-generic-OF-glue-layer.patch   | 244 ++++++++++++++++
 ...sb-dwc3-of-simple-fix-build-warning-on-PM.patch |  41 +++
 ...move-impossible-check-for-of_clk_get_pare.patch |  52 ++++
 ...b-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch | 186 ++++++------
 ...up-Make-sure-mode-is-only-determined-once.patch | 225 +++++++++++++++
 ...11-spi-qup-Fix-transaction-done-signaling.patch |  35 +++
 ...12-spi-qup-Fix-DMA-mode-to-work-correctly.patch | 226 +++++++++++++++
 ...-spi-qup-Fix-block-mode-to-work-correctly.patch | 317 +++++++++++++++++++++
 ...-spi-qup-properly-detect-extra-interrupts.patch |  67 +++++
 ...-t-re-read-opflags-to-see-if-transaction-.patch |  32 +++
 11 files changed, 1350 insertions(+), 93 deletions(-)

diff --git a/target/linux/ipq806x/modules.mk b/target/linux/ipq806x/modules.mk
index 04a2d4f..38dd195 100644
--- a/target/linux/ipq806x/modules.mk
+++ b/target/linux/ipq806x/modules.mk
@@ -1,6 +1,22 @@
+define KernelPackage/usb-dwc3-of-simple
+  TITLE:=DWC3 USB simple OF driver
+  DEPENDS:=+kmod-usb-dwc3
+  KCONFIG:= CONFIG_USB_DWC3_OF_SIMPLE
+  FILES:= $(LINUX_DIR)/drivers/usb/dwc3/dwc3-of-simple.ko
+  AUTOLOAD:=$(call AutoLoad,53,dwc3-of-simple,1)
+  $(call AddDepends/usb)
+endef
+
+define KernelPackage/usb-dwc3-of-simple/description
+ This driver provides generic platform glue for the integrated DesignWare
+ USB3 IP Core.
+endef
+
+$(eval $(call KernelPackage,usb-dwc3-of-simple))
+
 define KernelPackage/usb-phy-qcom-dwc3
   TITLE:=DWC3 USB QCOM PHY driver
-  DEPENDS:=@TARGET_ipq806x
+  DEPENDS:=@TARGET_ipq806x +kmod-usb-dwc3-of-simple
   KCONFIG:= CONFIG_PHY_QCOM_DWC3
   FILES:= $(LINUX_DIR)/drivers/phy/phy-qcom-dwc3.ko
   AUTOLOAD:=$(call AutoLoad,45,phy-qcom-dwc3,1)
diff --git a/target/linux/ipq806x/patches-4.4/097-usb-dwc3-add-generic-OF-glue-layer.patch b/target/linux/ipq806x/patches-4.4/097-usb-dwc3-add-generic-OF-glue-layer.patch
new file mode 100644
index 0000000..96e9859
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/097-usb-dwc3-add-generic-OF-glue-layer.patch
@@ -0,0 +1,244 @@
+From 41c2b5280cd2fa3e198c422cdf223ba6e48f857a Mon Sep 17 00:00:00 2001
+From: Felipe Balbi <balbi at ti.com>
+Date: Wed, 18 Nov 2015 13:15:20 -0600
+Subject: [PATCH] usb: dwc3: add generic OF glue layer
+
+For simple platforms which merely enable some clocks
+and populate its children, we can use this generic
+glue layer to avoid boilerplate code duplication.
+
+For now this supports Qcom and Xilinx, but if we
+find a way to add generic handling of regulators and
+optional PHYs, we can absorb exynos as well.
+
+Tested-by: Subbaraya Sundeep Bhatta <subbaraya.sundeep.bhatta at xilinx.com>
+Signed-off-by: Felipe Balbi <balbi at ti.com>
+(cherry picked from commit 16adc674d0d68a50dfc725574738d7ae11cf5d7e)
+
+Change-Id: I6fd260442997b198dc12ca726814b7a9518e6353
+Signed-off-by: Nitheesh Sekar <nsekar at codeaurora.org>
+---
+ drivers/usb/dwc3/Kconfig          |   9 ++
+ drivers/usb/dwc3/Makefile         |   1 +
+ drivers/usb/dwc3/dwc3-of-simple.c | 178 ++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 188 insertions(+)
+ create mode 100644 drivers/usb/dwc3/dwc3-of-simple.c
+
+diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
+index 5a42c45..070e704 100644
+--- a/drivers/usb/dwc3/Kconfig
++++ b/drivers/usb/dwc3/Kconfig
+@@ -87,6 +87,15 @@ config USB_DWC3_KEYSTONE
+ 	  Support of USB2/3 functionality in TI Keystone2 platforms.
+ 	  Say 'Y' or 'M' here if you have one such device
+
++config USB_DWC3_OF_SIMPLE
++       tristate "Generic OF Simple Glue Layer"
++       depends on OF && COMMON_CLK
++       default USB_DWC3
++       help
++         Support USB2/3 functionality in simple SoC integrations.
++	 Currently supports Xilinx and Qualcomm DWC USB3 IP.
++	 Say 'Y' or 'M' if you have one such device.
++
+ config USB_DWC3_ST
+ 	tristate "STMicroelectronics Platforms"
+ 	depends on ARCH_STI && OF
+diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
+index acc951d..6491f9b 100644
+--- a/drivers/usb/dwc3/Makefile
++++ b/drivers/usb/dwc3/Makefile
+@@ -37,5 +37,6 @@ obj-$(CONFIG_USB_DWC3_OMAP)		+= dwc3-omap.o
+ obj-$(CONFIG_USB_DWC3_EXYNOS)		+= dwc3-exynos.o
+ obj-$(CONFIG_USB_DWC3_PCI)		+= dwc3-pci.o
+ obj-$(CONFIG_USB_DWC3_KEYSTONE)		+= dwc3-keystone.o
++obj-$(CONFIG_USB_DWC3_OF_SIMPLE)	+= dwc3-of-simple.o
+ obj-$(CONFIG_USB_DWC3_QCOM)		+= dwc3-qcom.o
+ obj-$(CONFIG_USB_DWC3_ST)		+= dwc3-st.o
+diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c
+new file mode 100644
+index 0000000..60c4c5a
+--- /dev/null
++++ b/drivers/usb/dwc3/dwc3-of-simple.c
+@@ -0,0 +1,178 @@
++/**
++ * dwc3-of-simple.c - OF glue layer for simple integrations
++ *
++ * Copyright (c) 2015 Texas Instruments Incorporated - http://www.ti.com
++ *
++ * Author: Felipe Balbi <balbi at ti.com>
++ *
++ * This program is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2  of
++ * the License as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * This is a combination of the old dwc3-qcom.c by Ivan T. Ivanov
++ * <iivanov at mm-sol.com> and the original patch adding support for Xilinx' SoC
++ * by Subbaraya Sundeep Bhatta <subbaraya.sundeep.bhatta at xilinx.com>
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/platform_device.h>
++#include <linux/dma-mapping.h>
++#include <linux/clk.h>
++#include <linux/clk-provider.h>
++#include <linux/of.h>
++#include <linux/of_platform.h>
++#include <linux/pm_runtime.h>
++
++struct dwc3_of_simple {
++	struct device		*dev;
++	struct clk		**clks;
++	int			num_clocks;
++};
++
++static int dwc3_of_simple_probe(struct platform_device *pdev)
++{
++	struct dwc3_of_simple	*simple;
++	struct device		*dev = &pdev->dev;
++	struct device_node	*np = dev->of_node;
++
++	int			ret;
++	int			i;
++
++	simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL);
++	if (!simple)
++		return -ENOMEM;
++
++	ret = of_clk_get_parent_count(np);
++	if (ret < 0)
++		return ret;
++
++	simple->num_clocks = ret;
++
++	simple->clks = devm_kcalloc(dev, simple->num_clocks,
++			sizeof(struct clk *), GFP_KERNEL);
++	if (!simple->clks)
++		return -ENOMEM;
++
++	simple->dev = dev;
++
++	for (i = 0; i < simple->num_clocks; i++) {
++		struct clk	*clk;
++
++		clk = of_clk_get(np, i);
++		if (IS_ERR(clk)) {
++			while (--i >= 0)
++				clk_put(simple->clks[i]);
++			return PTR_ERR(clk);
++		}
++
++		ret = clk_prepare_enable(clk);
++		if (ret < 0) {
++			while (--i >= 0) {
++				clk_disable_unprepare(simple->clks[i]);
++				clk_put(simple->clks[i]);
++			}
++			clk_put(clk);
++
++			return ret;
++		}
++
++		simple->clks[i] = clk;
++	}
++
++	ret = of_platform_populate(np, NULL, NULL, dev);
++	if (ret) {
++		for (i = 0; i < simple->num_clocks; i++) {
++			clk_disable_unprepare(simple->clks[i]);
++			clk_put(simple->clks[i]);
++		}
++
++		return ret;
++	}
++
++	pm_runtime_set_active(dev);
++	pm_runtime_enable(dev);
++	pm_runtime_get_sync(dev);
++
++	return 0;
++}
++
++static int dwc3_of_simple_remove(struct platform_device *pdev)
++{
++	struct dwc3_of_simple	*simple = platform_get_drvdata(pdev);
++	struct device		*dev = &pdev->dev;
++	int			i;
++
++	for (i = 0; i < simple->num_clocks; i++) {
++		clk_unprepare(simple->clks[i]);
++		clk_put(simple->clks[i]);
++	}
++
++	of_platform_depopulate(dev);
++
++	pm_runtime_put_sync(dev);
++	pm_runtime_disable(dev);
++
++	return 0;
++}
++
++static int dwc3_of_simple_runtime_suspend(struct device *dev)
++{
++	struct dwc3_of_simple	*simple = dev_get_drvdata(dev);
++	int			i;
++
++	for (i = 0; i < simple->num_clocks; i++)
++		clk_disable(simple->clks[i]);
++
++	return 0;
++}
++
++static int dwc3_of_simple_runtime_resume(struct device *dev)
++{
++	struct dwc3_of_simple	*simple = dev_get_drvdata(dev);
++	int			ret;
++	int			i;
++
++	for (i = 0; i < simple->num_clocks; i++) {
++		ret = clk_enable(simple->clks[i]);
++		if (ret < 0) {
++			while (--i >= 0)
++				clk_disable(simple->clks[i]);
++			return ret;
++		}
++	}
++
++	return 0;
++}
++
++static const struct dev_pm_ops dwc3_of_simple_dev_pm_ops = {
++	SET_RUNTIME_PM_OPS(dwc3_of_simple_runtime_suspend,
++			dwc3_of_simple_runtime_resume, NULL)
++};
++
++static const struct of_device_id of_dwc3_simple_match[] = {
++	{ .compatible = "qcom,dwc3" },
++	{ .compatible = "xlnx,zynqmp-dwc3" },
++	{ /* Sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, of_dwc3_simple_match);
++
++static struct platform_driver dwc3_of_simple_driver = {
++	.probe		= dwc3_of_simple_probe,
++	.remove		= dwc3_of_simple_remove,
++	.driver		= {
++		.name	= "dwc3-of-simple",
++		.of_match_table = of_dwc3_simple_match,
++	},
++};
++
++module_platform_driver(dwc3_of_simple_driver);
++MODULE_LICENSE("GPL v2");
++MODULE_DESCRIPTION("DesignWare USB3 OF Simple Glue Layer");
++MODULE_AUTHOR("Felipe Balbi <balbi at ti.com>");
+--
+2.7.2
+
diff --git a/target/linux/ipq806x/patches-4.4/098-usb-dwc3-of-simple-fix-build-warning-on-PM.patch b/target/linux/ipq806x/patches-4.4/098-usb-dwc3-of-simple-fix-build-warning-on-PM.patch
new file mode 100644
index 0000000..2fbdd1b
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/098-usb-dwc3-of-simple-fix-build-warning-on-PM.patch
@@ -0,0 +1,41 @@
+From 131386d63ca3177d471aa93808c69b85fdac520d Mon Sep 17 00:00:00 2001
+From: Felipe Balbi <balbi at ti.com>
+Date: Tue, 22 Dec 2015 21:56:10 -0600
+Subject: [PATCH] usb: dwc3: of-simple: fix build warning on !PM
+
+if we have a !PM kernel build, our runtime
+suspend/resume callbacks will be left defined but
+unused. Add a ifdef CONFIG_PM guard.
+
+Signed-off-by: Felipe Balbi <balbi at ti.com>
+(cherry picked from commit 5072cfc40a80cea3749fd3413b3896630d8c787e)
+
+Change-Id: I088186c33aa917ec8da2985372ceefc289b24242
+Signed-off-by: Nitheesh Sekar <nsekar at codeaurora.org>
+---
+ drivers/usb/dwc3/dwc3-of-simple.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c
+index 60c4c5a..9c9f741 100644
+--- a/drivers/usb/dwc3/dwc3-of-simple.c
++++ b/drivers/usb/dwc3/dwc3-of-simple.c
+@@ -122,6 +122,7 @@ static int dwc3_of_simple_remove(struct platform_device *pdev)
+ 	return 0;
+ }
+
++#ifdef CONFIG_PM
+ static int dwc3_of_simple_runtime_suspend(struct device *dev)
+ {
+ 	struct dwc3_of_simple	*simple = dev_get_drvdata(dev);
+@@ -150,6 +151,7 @@ static int dwc3_of_simple_runtime_resume(struct device *dev)
+
+ 	return 0;
+ }
++#endif
+
+ static const struct dev_pm_ops dwc3_of_simple_dev_pm_ops = {
+ 	SET_RUNTIME_PM_OPS(dwc3_of_simple_runtime_suspend,
+--
+2.7.2
+
diff --git a/target/linux/ipq806x/patches-4.4/099-usb-dwc3-Remove-impossible-check-for-of_clk_get_pare.patch b/target/linux/ipq806x/patches-4.4/099-usb-dwc3-Remove-impossible-check-for-of_clk_get_pare.patch
new file mode 100644
index 0000000..44506c1
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/099-usb-dwc3-Remove-impossible-check-for-of_clk_get_pare.patch
@@ -0,0 +1,52 @@
+From 07c8b15688055d81ac8e1c8c964b9e4c302287f1 Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd at codeaurora.org>
+Date: Mon, 22 Feb 2016 11:12:47 -0800
+Subject: [PATCH] usb: dwc3: Remove impossible check for
+ of_clk_get_parent_count() < 0
+
+The check for < 0 is impossible now that
+of_clk_get_parent_count() returns an unsigned int. Simplify the
+code and update the types.
+
+Acked-by: Felipe Balbi <balbi at kernel.org>
+Cc: <linux-usb at vger.kernel.org>
+Signed-off-by: Stephen Boyd <sboyd at codeaurora.org>
+(cherry picked from commit 3d755dcc20dd452b52532eca17da40ebbd12aee9)
+
+Change-Id: Iaa38e064d801fb36c855fea51c0443840368e0d3
+Signed-off-by: Nitheesh Sekar <nsekar at codeaurora.org>
+---
+ drivers/usb/dwc3/dwc3-of-simple.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c
+index 9c9f741..9743353 100644
+--- a/drivers/usb/dwc3/dwc3-of-simple.c
++++ b/drivers/usb/dwc3/dwc3-of-simple.c
+@@ -42,6 +42,7 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
+ 	struct device		*dev = &pdev->dev;
+ 	struct device_node	*np = dev->of_node;
+
++	unsigned int		count;
+ 	int			ret;
+ 	int			i;
+
+@@ -49,11 +50,11 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
+ 	if (!simple)
+ 		return -ENOMEM;
+
+-	ret = of_clk_get_parent_count(np);
+-	if (ret < 0)
+-		return ret;
++	count = of_clk_get_parent_count(np);
++	if (!count)
++		return -ENOENT;
+
+-	simple->num_clocks = ret;
++	simple->num_clocks = count;
+
+ 	simple->clks = devm_kcalloc(dev, simple->num_clocks,
+ 			sizeof(struct clk *), GFP_KERNEL);
+--
+2.7.2
+
diff --git a/target/linux/ipq806x/patches-4.4/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch b/target/linux/ipq806x/patches-4.4/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch
index 4bba0ac..d554919 100644
--- a/target/linux/ipq806x/patches-4.4/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch
+++ b/target/linux/ipq806x/patches-4.4/100-usb-phy-Add-Qualcomm-DWC3-HS-SS-PHY-drivers.patch
@@ -1,9 +1,9 @@
 --- a/drivers/phy/Kconfig
 +++ b/drivers/phy/Kconfig
-@@ -390,4 +390,15 @@ config PHY_CYGNUS_PCIE
- 	  Enable this to support the Broadcom Cygnus PCIe PHY.
- 	  If unsure, say N.
- 
+@@ -390,4 +390,15 @@
+	  Enable this to support the Broadcom Cygnus PCIe PHY.
+	  If unsure, say N.
+
 +config PHY_QCOM_DWC3
 +	tristate "QCOM DWC3 USB PHY support"
 +	depends on ARCH_QCOM
@@ -18,25 +18,25 @@
  endmenu
 --- a/drivers/phy/Makefile
 +++ b/drivers/phy/Makefile
-@@ -48,3 +48,4 @@ obj-$(CONFIG_PHY_TUSB1210)		+= phy-tusb1
+@@ -48,3 +48,4 @@ obj-$(CONFIG_PHY_TUSB1210)	+=
  obj-$(CONFIG_PHY_BRCMSTB_SATA)		+= phy-brcmstb-sata.o
  obj-$(CONFIG_PHY_PISTACHIO_USB)		+= phy-pistachio-usb.o
  obj-$(CONFIG_PHY_CYGNUS_PCIE)		+= phy-bcm-cygnus-pcie.o
 +obj-$(CONFIG_PHY_QCOM_DWC3)		+= phy-qcom-dwc3.o
 --- /dev/null
 +++ b/drivers/phy/phy-qcom-dwc3.c
-@@ -0,0 +1,482 @@
-+/* Copyright (c) 2013-2014, Code Aurora Forum. All rights reserved.
+@@ -0,0 +1,484 @@
++/* Copyright (c) 2014-2015, Code Aurora Forum. All rights reserved.
 + *
 + * This program is free software; you can redistribute it and/or modify
 + * it under the terms of the GNU General Public License version 2 and
 + * only version 2 as published by the Free Software Foundation.
 + *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ */
++* This program is distributed in the hope that it will be useful,
++* but WITHOUT ANY WARRANTY; without even the implied warranty of
++* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++* GNU General Public License for more details.
++*/
 +
 +#include <linux/clk.h>
 +#include <linux/err.h>
@@ -57,7 +57,7 @@
 +#define HSUSB_CTRL_DMSEHV_CLAMP			BIT(24)
 +#define HSUSB_CTRL_USB2_SUSPEND			BIT(23)
 +#define HSUSB_CTRL_UTMI_CLK_EN			BIT(21)
-+#define	HSUSB_CTRL_UTMI_OTG_VBUS_VALID		BIT(20)
++#define HSUSB_CTRL_UTMI_OTG_VBUS_VALID		BIT(20)
 +#define HSUSB_CTRL_USE_CLKCORE			BIT(18)
 +#define HSUSB_CTRL_DPSEHV_CLAMP			BIT(17)
 +#define HSUSB_CTRL_COMMONONN			BIT(11)
@@ -113,18 +113,24 @@
 +#define TX_OVRD_DRV_LO_PREEMPH_SHIFT	7
 +#define TX_OVRD_DRV_LO_EN		BIT(14)
 +
++/* SS CAP register bits */
++#define SS_CR_CAP_ADDR_REG		BIT(0)
++#define SS_CR_CAP_DATA_REG		BIT(0)
++#define SS_CR_READ_REG			BIT(0)
++#define SS_CR_WRITE_REG			BIT(0)
++
 +struct qcom_dwc3_usb_phy {
 +	void __iomem		*base;
 +	struct device		*dev;
-+	struct phy *phy;
-+
-+	int (*phy_init)(struct qcom_dwc3_usb_phy *phy_dwc3);
-+	int (*phy_exit)(struct qcom_dwc3_usb_phy *phy_dwc3);
-+
 +	struct clk		*xo_clk;
 +	struct clk		*ref_clk;
 +};
 +
++struct qcom_dwc3_phy_drvdata {
++	struct phy_ops	ops;
++	u32		clk_rate;
++};
++
 +/**
 + * Write register and read back masked value to confirm it is written
 + *
@@ -177,29 +183,32 @@
 + * @addr - SSPHY address to write.
 + * @val - value to write.
 + */
-+static int qcom_dwc3_ss_write_phycreg(void __iomem *base, u32 addr, u32 val)
++static int qcom_dwc3_ss_write_phycreg(struct qcom_dwc3_usb_phy *phy_dwc3,
++					u32 addr, u32 val)
 +{
 +	int ret;
 +
-+	writel(addr, base + CR_PROTOCOL_DATA_IN_REG);
-+	writel(0x1, base + CR_PROTOCOL_CAP_ADDR_REG);
++	writel(addr, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG);
++	writel(SS_CR_CAP_ADDR_REG, phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG);
 +
-+	ret = wait_for_latch(base + CR_PROTOCOL_CAP_ADDR_REG);
++	ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG);
 +	if (ret)
 +		goto err_wait;
 +
-+	writel(val, base + CR_PROTOCOL_DATA_IN_REG);
-+	writel(0x1, base + CR_PROTOCOL_CAP_DATA_REG);
++	writel(val, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG);
++	writel(SS_CR_CAP_DATA_REG, phy_dwc3->base + CR_PROTOCOL_CAP_DATA_REG);
 +
-+	ret = wait_for_latch(base + CR_PROTOCOL_CAP_DATA_REG);
++	ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_DATA_REG);
 +	if (ret)
 +		goto err_wait;
 +
-+	writel(0x1, base + CR_PROTOCOL_WRITE_REG);
++	writel(SS_CR_WRITE_REG, phy_dwc3->base + CR_PROTOCOL_WRITE_REG);
 +
-+	ret = wait_for_latch(base + CR_PROTOCOL_WRITE_REG);
++	ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_WRITE_REG);
 +
 +err_wait:
++	if (ret)
++		dev_err(phy_dwc3->dev, "timeout waiting for latch\n");
 +	return ret;
 +}
 +
@@ -212,10 +221,9 @@
 +static int qcom_dwc3_ss_read_phycreg(void __iomem *base, u32 addr, u32 *val)
 +{
 +	int ret;
-+	bool first_read = true;
 +
 +	writel(addr, base + CR_PROTOCOL_DATA_IN_REG);
-+	writel(0x1, base + CR_PROTOCOL_CAP_ADDR_REG);
++	writel(SS_CR_CAP_ADDR_REG, base + CR_PROTOCOL_CAP_ADDR_REG);
 +
 +	ret = wait_for_latch(base + CR_PROTOCOL_CAP_ADDR_REG);
 +	if (ret)
@@ -226,18 +234,20 @@
 +	 * incorrect. Hence as workaround, SW should perform SSPHY register
 +	 * read twice, but use only second read and ignore first read.
 +	 */
-+retry:
-+	writel(0x1, base + CR_PROTOCOL_READ_REG);
++	writel(SS_CR_READ_REG, base + CR_PROTOCOL_READ_REG);
 +
 +	ret = wait_for_latch(base + CR_PROTOCOL_READ_REG);
 +	if (ret)
 +		goto err_wait;
 +
-+	if (first_read) {
-+		readl(base + CR_PROTOCOL_DATA_OUT_REG);
-+		first_read = false;
-+		goto retry;
-+	}
++	/* throwaway read */
++	readl(base + CR_PROTOCOL_DATA_OUT_REG);
++
++	writel(SS_CR_READ_REG, base + CR_PROTOCOL_READ_REG);
++
++	ret = wait_for_latch(base + CR_PROTOCOL_READ_REG);
++	if (ret)
++		goto err_wait;
 +
 +	*val = readl(base + CR_PROTOCOL_DATA_OUT_REG);
 +
@@ -271,8 +281,9 @@
 +	return 0;
 +}
 +
-+static int qcom_dwc3_hs_phy_init(struct qcom_dwc3_usb_phy *phy_dwc3)
++static int qcom_dwc3_hs_phy_init(struct phy *phy)
 +{
++	struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
 +	u32 val;
 +
 +	/*
@@ -298,17 +309,18 @@
 +	return 0;
 +}
 +
-+static int qcom_dwc3_ss_phy_init(struct qcom_dwc3_usb_phy *phy_dwc3)
++static int qcom_dwc3_ss_phy_init(struct phy *phy)
 +{
++	struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
 +	int ret;
 +	u32 data = 0;
 +
 +	/* reset phy */
-+	data = readl_relaxed(phy_dwc3->base + SSUSB_PHY_CTRL_REG);
-+	writel_relaxed(data | SSUSB_CTRL_SS_PHY_RESET,
++	data = readl(phy_dwc3->base + SSUSB_PHY_CTRL_REG);
++	writel(data | SSUSB_CTRL_SS_PHY_RESET,
 +		phy_dwc3->base + SSUSB_PHY_CTRL_REG);
 +	usleep_range(2000, 2200);
-+	writel_relaxed(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
++	writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
 +
 +	/* clear REF_PAD if we don't have XO clk */
 +	if (!phy_dwc3->xo_clk)
@@ -316,11 +328,13 @@
 +	else
 +		data |= SSUSB_CTRL_REF_USE_PAD;
 +
-+	writel_relaxed(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
++	writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
++
++	/* wait for ref clk to become stable, this can take up to 30ms */
 +	msleep(30);
 +
 +	data |= SSUSB_CTRL_SS_PHY_EN | SSUSB_CTRL_LANE0_PWR_PRESENT;
-+	writel_relaxed(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
++	writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG);
 +
 +	/*
 +	 * Fix RX Equalization setting as follows
@@ -339,7 +353,7 @@
 +	data &= ~RX_OVRD_IN_HI_RX_EQ_MASK;
 +	data |= 0x3 << RX_OVRD_IN_HI_RX_EQ_SHIFT;
 +	data |= RX_OVRD_IN_HI_RX_EQ_OVRD;
-+	ret = qcom_dwc3_ss_write_phycreg(phy_dwc3->base,
++	ret = qcom_dwc3_ss_write_phycreg(phy_dwc3,
 +		SSPHY_CTRL_RX_OVRD_IN_HI(0), data);
 +	if (ret)
 +		goto err_phy_trans;
@@ -360,7 +374,7 @@
 +	data &= ~TX_OVRD_DRV_LO_AMPLITUDE_MASK;
 +	data |= 0x7f;
 +	data |= TX_OVRD_DRV_LO_EN;
-+	ret = qcom_dwc3_ss_write_phycreg(phy_dwc3->base,
++	ret = qcom_dwc3_ss_write_phycreg(phy_dwc3,
 +		SSPHY_CTRL_TX_OVRD_DRV_LO(0), data);
 +	if (ret)
 +		goto err_phy_trans;
@@ -378,13 +392,14 @@
 +	return ret;
 +}
 +
-+static int qcom_dwc3_ss_phy_exit(struct qcom_dwc3_usb_phy *phy_dwc3)
++static int qcom_dwc3_ss_phy_exit(struct phy *phy)
 +{
++	struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
++
 +	/* Sequence to put SSPHY in low power state:
 +	 * 1. Clear REF_PHY_EN in PHY_CTRL_REG
 +	 * 2. Clear REF_USE_PAD in PHY_CTRL_REG
 +	 * 3. Set TEST_POWERED_DOWN in PHY_CTRL_REG to enable PHY retention
-+	 * 4. Disable SSPHY ref clk
 +	 */
 +	qcom_dwc3_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG,
 +		SSUSB_CTRL_SS_PHY_EN, 0x0);
@@ -396,37 +411,30 @@
 +	return 0;
 +}
 +
-+static int qcom_dwc3_phy_init(struct phy *phy)
-+{
-+	struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
-+
-+	if (phy_dwc3->phy_init)
-+		return phy_dwc3->phy_init(phy_dwc3);
-+
-+	return 0;
-+}
-+
-+static int qcom_dwc3_phy_exit(struct phy *phy)
-+{
-+	struct qcom_dwc3_usb_phy *phy_dwc3 = phy_get_drvdata(phy);
-+
-+	if (phy_dwc3->phy_exit)
-+		return qcom_dwc3_ss_phy_exit(phy_dwc3);
-+
-+	return 0;
-+}
++static const struct qcom_dwc3_phy_drvdata qcom_dwc3_hs_drvdata = {
++	.ops = {
++		.init		= qcom_dwc3_hs_phy_init,
++		.power_on	= qcom_dwc3_phy_power_on,
++		.power_off	= qcom_dwc3_phy_power_off,
++		.owner		= THIS_MODULE,
++	},
++	.clk_rate = 60000000,
++};
 +
-+static struct phy_ops qcom_dwc3_phy_ops = {
-+	.init		= qcom_dwc3_phy_init,
-+	.exit		= qcom_dwc3_phy_exit,
-+	.power_on	= qcom_dwc3_phy_power_on,
-+	.power_off	= qcom_dwc3_phy_power_off,
-+	.owner		= THIS_MODULE,
++static const struct qcom_dwc3_phy_drvdata qcom_dwc3_ss_drvdata = {
++	.ops = {
++		.init		= qcom_dwc3_ss_phy_init,
++		.exit		= qcom_dwc3_ss_phy_exit,
++		.power_on	= qcom_dwc3_phy_power_on,
++		.power_off	= qcom_dwc3_phy_power_off,
++		.owner		= THIS_MODULE,
++	},
++	.clk_rate = 125000000,
 +};
 +
 +static const struct of_device_id qcom_dwc3_phy_table[] = {
-+	{ .compatible = "qcom,dwc3-hs-usb-phy", },
-+	{ .compatible = "qcom,dwc3-ss-usb-phy", },
++	{ .compatible = "qcom,dwc3-hs-usb-phy", .data = &qcom_dwc3_hs_drvdata },
++	{ .compatible = "qcom,dwc3-ss-usb-phy", .data = &qcom_dwc3_ss_drvdata },
 +	{ /* Sentinel */ }
 +};
 +MODULE_DEVICE_TABLE(of, qcom_dwc3_phy_table);
@@ -435,13 +443,17 @@
 +{
 +	struct qcom_dwc3_usb_phy	*phy_dwc3;
 +	struct phy_provider		*phy_provider;
++	struct phy			*generic_phy;
 +	struct resource			*res;
++	const struct of_device_id *match;
++	const struct qcom_dwc3_phy_drvdata *data;
 +
 +	phy_dwc3 = devm_kzalloc(&pdev->dev, sizeof(*phy_dwc3), GFP_KERNEL);
 +	if (!phy_dwc3)
 +		return -ENOMEM;
 +
-+	platform_set_drvdata(pdev, phy_dwc3);
++	match = of_match_node(qcom_dwc3_phy_table, pdev->dev.of_node);
++	data = match->data;
 +
 +	phy_dwc3->dev = &pdev->dev;
 +
@@ -456,19 +468,7 @@
 +		return PTR_ERR(phy_dwc3->ref_clk);
 +	}
 +
-+	if (of_device_is_compatible(pdev->dev.of_node,
-+			"qcom,dwc3-hs-usb-phy")) {
-+		clk_set_rate(phy_dwc3->ref_clk, 60000000);
-+		phy_dwc3->phy_init = qcom_dwc3_hs_phy_init;
-+	} else if (of_device_is_compatible(pdev->dev.of_node,
-+			"qcom,dwc3-ss-usb-phy")) {
-+		phy_dwc3->phy_init = qcom_dwc3_ss_phy_init;
-+		phy_dwc3->phy_exit = qcom_dwc3_ss_phy_exit;
-+		clk_set_rate(phy_dwc3->ref_clk, 125000000);
-+	} else {
-+		dev_err(phy_dwc3->dev, "Unknown phy\n");
-+		return -EINVAL;
-+	}
++	clk_set_rate(phy_dwc3->ref_clk, data->clk_rate);
 +
 +	phy_dwc3->xo_clk = devm_clk_get(phy_dwc3->dev, "xo");
 +	if (IS_ERR(phy_dwc3->xo_clk)) {
@@ -476,12 +476,14 @@
 +		phy_dwc3->xo_clk = NULL;
 +	}
 +
-+	phy_dwc3->phy = devm_phy_create(phy_dwc3->dev, NULL, &qcom_dwc3_phy_ops);
++	generic_phy = devm_phy_create(phy_dwc3->dev, pdev->dev.of_node,
++				      &data->ops);
 +
-+	if (IS_ERR(phy_dwc3->phy))
-+		return PTR_ERR(phy_dwc3->phy);
++	if (IS_ERR(generic_phy))
++		return PTR_ERR(generic_phy);
 +
-+	phy_set_drvdata(phy_dwc3->phy, phy_dwc3);
++	phy_set_drvdata(generic_phy, phy_dwc3);
++	platform_set_drvdata(pdev, phy_dwc3);
 +
 +	phy_provider = devm_of_phy_provider_register(phy_dwc3->dev,
 +			of_phy_simple_xlate);
diff --git a/target/linux/ipq806x/patches-4.4/710-spi-qup-Make-sure-mode-is-only-determined-once.patch b/target/linux/ipq806x/patches-4.4/710-spi-qup-Make-sure-mode-is-only-determined-once.patch
new file mode 100644
index 0000000..e5dafd7
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/710-spi-qup-Make-sure-mode-is-only-determined-once.patch
@@ -0,0 +1,225 @@
+From 93f99afbc534e00d72d58336061823055ee820f1 Mon Sep 17 00:00:00 2001
+From: Andy Gross <andy.gross at linaro.org>
+Date: Tue, 12 Apr 2016 09:11:47 -0500
+Subject: [PATCH] spi: qup: Make sure mode is only determined once
+
+This patch calculates the mode once.  All decisions on the current
+transaction
+is made using the mode instead of use_dma
+
+Signed-off-by: Andy Gross <andy.gross at linaro.org>
+
+Change-Id: If3cdd924355e037d77dc8201a72895fac0461aa5
+---
+ drivers/spi/spi-qup.c | 96 +++++++++++++++++++--------------------------------
+ 1 file changed, 36 insertions(+), 60 deletions(-)
+
+diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
+index eb2cb8c..714fd4e 100644
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -150,13 +150,20 @@ struct spi_qup {
+ 	int			rx_bytes;
+ 	int			qup_v1;
+
+-	int			use_dma;
++	int			mode;
+ 	struct dma_slave_config	rx_conf;
+ 	struct dma_slave_config	tx_conf;
+-	int mode;
+ };
+
+
++static inline bool spi_qup_is_dma_xfer(int mode)
++{
++	if (mode == QUP_IO_M_MODE_DMOV || mode == QUP_IO_M_MODE_BAM)
++		return true;
++
++	return false;
++}
++
+ static inline bool spi_qup_is_valid_state(struct spi_qup *controller)
+ {
+ 	u32 opstate = readl_relaxed(controller->base + QUP_STATE);
+@@ -427,7 +434,7 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
+ 		error = -EIO;
+ 	}
+
+-	if (!controller->use_dma) {
++	if (!spi_qup_is_dma_xfer(controller->mode)) {
+ 		if (opflags & QUP_OP_IN_SERVICE_FLAG)
+ 			spi_qup_fifo_read(controller, xfer);
+
+@@ -446,43 +453,11 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
+ 	return IRQ_HANDLED;
+ }
+
+-static u32
+-spi_qup_get_mode(struct spi_master *master, struct spi_transfer *xfer)
+-{
+-	struct spi_qup *qup = spi_master_get_devdata(master);
+-	u32 mode;
+-	size_t dma_align = dma_get_cache_alignment();
+-
+-	qup->w_size = 4;
+-
+-	if (xfer->bits_per_word <= 8)
+-		qup->w_size = 1;
+-	else if (xfer->bits_per_word <= 16)
+-		qup->w_size = 2;
+-
+-	qup->n_words = xfer->len / qup->w_size;
+-
+-	if (!IS_ERR_OR_NULL(master->dma_rx) &&
+-			IS_ALIGNED((size_t)xfer->tx_buf, dma_align) &&
+-			IS_ALIGNED((size_t)xfer->rx_buf, dma_align) &&
+-			!is_vmalloc_addr(xfer->tx_buf) &&
+-			!is_vmalloc_addr(xfer->rx_buf) &&
+-			(xfer->len > 3*qup->in_blk_sz))
+-		qup->use_dma = 1;
+-
+-	if (qup->n_words <= (qup->in_fifo_sz / sizeof(u32)))
+-		mode = QUP_IO_M_MODE_FIFO;
+-	else
+-		mode = QUP_IO_M_MODE_BLOCK;
+-
+-	return mode;
+-}
+-
+ /* set clock freq ... bits per word */
+ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
+ {
+ 	struct spi_qup *controller = spi_master_get_devdata(spi->master);
+-	u32 config, iomode, mode, control;
++	u32 config, iomode, control;
+ 	int ret, n_words;
+
+ 	if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) {
+@@ -503,24 +478,22 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
+ 		return -EIO;
+ 	}
+
+-	controller->mode = mode = spi_qup_get_mode(spi->master, xfer);
++	controller->w_size = DIV_ROUND_UP(xfer->bits_per_word, 8);
++	controller->n_words = xfer->len / controller->w_size;
+ 	n_words = controller->n_words;
+
+-	if (mode == QUP_IO_M_MODE_FIFO) {
++	if (n_words <= (controller->in_fifo_sz / sizeof(u32))) {
++		controller->mode = QUP_IO_M_MODE_FIFO;
+ 		writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT);
+ 		writel_relaxed(n_words, controller->base + QUP_MX_WRITE_CNT);
+ 		/* must be zero for FIFO */
+ 		writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
+ 		writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
+ 		controller->use_dma = 0;
+-	} else if (!controller->use_dma) {
+-		writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
+-		writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
+-		/* must be zero for BLOCK and BAM */
+-		writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
+-		writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
+-	} else {
+-		mode = QUP_IO_M_MODE_BAM;
++	} else if (spi->master->can_dma &&
++	    spi->master->can_dma(spi->master, spi, xfer) &&
++	    spi->master->cur_msg_mapped) {
++		controller->mode = QUP_IO_M_MODE_BAM;
+ 		writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
+ 		writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
+
+@@ -541,19 +514,26 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
+
+ 			writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
+ 		}
++	} else {
++		controller->mode = QUP_IO_M_MODE_BLOCK;
++		writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
++		writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
++		/* must be zero for BLOCK and BAM */
++		writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
++		writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
+ 	}
+
+ 	iomode = readl_relaxed(controller->base + QUP_IO_M_MODES);
+ 	/* Set input and output transfer mode */
+ 	iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK);
+
+-	if (!controller->use_dma)
++	if (!spi_qup_is_dma_xfer(controller->mode))
+ 		iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN);
+ 	else
+ 		iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN;
+
+-	iomode |= (mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT);
+-	iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
++	iomode |= (controller->mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT);
++	iomode |= (controller->mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
+
+ 	writel_relaxed(iomode, controller->base + QUP_IO_M_MODES);
+
+@@ -594,7 +574,7 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
+ 	config |= xfer->bits_per_word - 1;
+ 	config |= QUP_CONFIG_SPI_MODE;
+
+-	if (controller->use_dma) {
++	if (spi_qup_is_dma_xfer(controller->mode)) {
+ 		if (!xfer->tx_buf)
+ 			config |= QUP_CONFIG_NO_OUTPUT;
+ 		if (!xfer->rx_buf)
+@@ -612,7 +592,7 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
+ 		 * status change in BAM mode
+ 		 */
+
+-		if (mode == QUP_IO_M_MODE_BAM)
++		if (spi_qup_is_dma_xfer(controller->mode))
+ 			mask = QUP_OP_IN_SERVICE_FLAG | QUP_OP_OUT_SERVICE_FLAG;
+
+ 		writel_relaxed(mask, controller->base + QUP_OPERATIONAL_MASK);
+@@ -646,7 +626,7 @@ static int spi_qup_transfer_one(struct spi_master *master,
+ 	controller->tx_bytes = 0;
+ 	spin_unlock_irqrestore(&controller->lock, flags);
+
+-	if (controller->use_dma)
++	if (spi_qup_is_dma_xfer(controller->mode))
+ 		ret = spi_qup_do_dma(master, xfer);
+ 	else
+ 		ret = spi_qup_do_pio(master, xfer);
+@@ -670,7 +650,7 @@ exit:
+ 		ret = controller->error;
+ 	spin_unlock_irqrestore(&controller->lock, flags);
+
+-	if (ret && controller->use_dma)
++	if (ret && spi_qup_is_dma_xfer(controller->mode))
+ 		spi_qup_dma_terminate(master, xfer);
+
+ 	return ret;
+@@ -681,9 +661,7 @@ static bool spi_qup_can_dma(struct spi_master *master, struct spi_device *spi,
+ {
+ 	struct spi_qup *qup = spi_master_get_devdata(master);
+ 	size_t dma_align = dma_get_cache_alignment();
+-	u32 mode;
+-
+-	qup->use_dma = 0;
++	int n_words;
+
+ 	if (xfer->rx_buf && (xfer->len % qup->in_blk_sz ||
+ 	    IS_ERR_OR_NULL(master->dma_rx) ||
+@@ -695,12 +673,10 @@ static bool spi_qup_can_dma(struct spi_master *master, struct spi_device *spi,
+ 	    !IS_ALIGNED((size_t)xfer->tx_buf, dma_align)))
+ 		return false;
+
+-	mode = spi_qup_get_mode(master, xfer);
+-	if (mode == QUP_IO_M_MODE_FIFO)
++	n_words = xfer->len / DIV_ROUND_UP(xfer->bits_per_word, 8);
++	if (n_words <= (qup->in_fifo_sz / sizeof(u32)))
+ 		return false;
+
+-	qup->use_dma = 1;
+-
+ 	return true;
+ }
+
+--
+2.7.2
+
diff --git a/target/linux/ipq806x/patches-4.4/711-spi-qup-Fix-transaction-done-signaling.patch b/target/linux/ipq806x/patches-4.4/711-spi-qup-Fix-transaction-done-signaling.patch
new file mode 100644
index 0000000..dd4bad3
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/711-spi-qup-Fix-transaction-done-signaling.patch
@@ -0,0 +1,35 @@
+From 8e830bd17e945e74964a5b61353d74e34c0791cd Mon Sep 17 00:00:00 2001
+From: Andy Gross <andy.gross at linaro.org>
+Date: Fri, 29 Jan 2016 22:06:50 -0600
+Subject: [PATCH] spi: qup: Fix transaction done signaling
+
+Wait to signal done until we get all of the interrupts we are expecting
+to get for a transaction.  If we don't wait for the input done flag, we
+can be inbetween transactions when the done flag comes in and this can
+mess up the next transaction.
+
+Change-Id: I08d78376e71590663158d6434a3fb7c0623264c9
+CC: Grant Grundler <grundler at chromium.org>
+CC: Sarthak Kukreti <skukreti at codeaurora.org>
+Signed-off-by: Andy Gross <andy.gross at linaro.org>
+---
+ drivers/spi/spi-qup.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
+index 714fd4e..fe629f2 100644
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -447,7 +447,8 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
+ 	controller->xfer = xfer;
+ 	spin_unlock_irqrestore(&controller->lock, flags);
+
+-	if (controller->rx_bytes == xfer->len || error)
++	if ((controller->rx_bytes == xfer->len &&
++		(opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) ||  error)
+ 		complete(&controller->done);
+
+ 	return IRQ_HANDLED;
+--
+2.7.2
+
diff --git a/target/linux/ipq806x/patches-4.4/712-spi-qup-Fix-DMA-mode-to-work-correctly.patch b/target/linux/ipq806x/patches-4.4/712-spi-qup-Fix-DMA-mode-to-work-correctly.patch
new file mode 100644
index 0000000..d9abc65
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/712-spi-qup-Fix-DMA-mode-to-work-correctly.patch
@@ -0,0 +1,226 @@
+From ed56e6322b067a898a25bda1774eb1180a832246 Mon Sep 17 00:00:00 2001
+From: Andy Gross <andy.gross at linaro.org>
+Date: Tue, 2 Feb 2016 17:00:53 -0600
+Subject: [PATCH] spi: qup: Fix DMA mode to work correctly
+
+This patch fixes a few issues with the DMA mode.  The QUP needs to be
+placed in the run mode before the DMA transactions are executed.  The
+conditions for being able to DMA vary between revisions of the QUP.
+This is due to v1.1.1 using ADM DMA and later revisions using BAM.
+
+Change-Id: Ib1f876eaa05d079e0bca4358d2b25b2940986089
+Signed-off-by: Andy Gross <andy.gross at linaro.org>
+---
+ drivers/spi/spi-qup.c | 95 ++++++++++++++++++++++++++++++++++-----------------
+ 1 file changed, 63 insertions(+), 32 deletions(-)
+
+diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
+index fe629f2..089c5e8 100644
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -143,6 +143,7 @@ struct spi_qup {
+
+ 	struct spi_transfer	*xfer;
+ 	struct completion	done;
++	struct completion	dma_tx_done;
+ 	int			error;
+ 	int			w_size;	/* bytes per SPI word */
+ 	int			n_words;
+@@ -285,16 +286,16 @@ static void spi_qup_fifo_write(struct spi_qup *controller,
+
+ static void spi_qup_dma_done(void *data)
+ {
+-	struct spi_qup *qup = data;
++	struct completion *done = data;
+
+-	complete(&qup->done);
++	complete(done);
+ }
+
+ static int spi_qup_prep_sg(struct spi_master *master, struct spi_transfer *xfer,
+ 			   enum dma_transfer_direction dir,
+-			   dma_async_tx_callback callback)
++			   dma_async_tx_callback callback,
++			   void *data)
+ {
+-	struct spi_qup *qup = spi_master_get_devdata(master);
+ 	unsigned long flags = DMA_PREP_INTERRUPT | DMA_PREP_FENCE;
+ 	struct dma_async_tx_descriptor *desc;
+ 	struct scatterlist *sgl;
+@@ -313,11 +314,11 @@ static int spi_qup_prep_sg(struct spi_master *master, struct spi_transfer *xfer,
+ 	}
+
+ 	desc = dmaengine_prep_slave_sg(chan, sgl, nents, dir, flags);
+-	if (!desc)
+-		return -EINVAL;
++	if (IS_ERR_OR_NULL(desc))
++		return desc ? PTR_ERR(desc) : -EINVAL;
+
+ 	desc->callback = callback;
+-	desc->callback_param = qup;
++	desc->callback_param = data;
+
+ 	cookie = dmaengine_submit(desc);
+
+@@ -333,18 +334,29 @@ static void spi_qup_dma_terminate(struct spi_master *master,
+ 		dmaengine_terminate_all(master->dma_rx);
+ }
+
+-static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer)
++static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer,
++unsigned long timeout)
+ {
++	struct spi_qup *qup = spi_master_get_devdata(master);
+ 	dma_async_tx_callback rx_done = NULL, tx_done = NULL;
+ 	int ret;
+
++	/* before issuing the descriptors, set the QUP to run */
++	ret = spi_qup_set_state(qup, QUP_STATE_RUN);
++	if (ret) {
++		dev_warn(qup->dev, "cannot set RUN state\n");
++		return ret;
++	}
++
+ 	if (xfer->rx_buf)
+ 		rx_done = spi_qup_dma_done;
+-	else if (xfer->tx_buf)
++
++	if (xfer->tx_buf)
+ 		tx_done = spi_qup_dma_done;
+
+ 	if (xfer->rx_buf) {
+-		ret = spi_qup_prep_sg(master, xfer, DMA_DEV_TO_MEM, rx_done);
++		ret = spi_qup_prep_sg(master, xfer, DMA_DEV_TO_MEM, rx_done,
++				      &qup->done);
+ 		if (ret)
+ 			return ret;
+
+@@ -352,17 +364,26 @@ static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer)
+ 	}
+
+ 	if (xfer->tx_buf) {
+-		ret = spi_qup_prep_sg(master, xfer, DMA_MEM_TO_DEV, tx_done);
++		ret = spi_qup_prep_sg(master, xfer, DMA_MEM_TO_DEV, tx_done,
++				      &qup->dma_tx_done);
+ 		if (ret)
+ 			return ret;
+
+ 		dma_async_issue_pending(master->dma_tx);
+ 	}
+
+-	return 0;
++	if (xfer->rx_buf && !wait_for_completion_timeout(&qup->done, timeout))
++		return -ETIMEDOUT;
++
++	if (xfer->tx_buf &&
++	    !wait_for_completion_timeout(&qup->dma_tx_done, timeout))
++		ret = -ETIMEDOUT;
++
++	return ret;
+ }
+
+-static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer)
++static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer,
++			  unsigned long timeout)
+ {
+ 	struct spi_qup *qup = spi_master_get_devdata(master);
+ 	int ret;
+@@ -382,6 +403,15 @@ static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer)
+ 	if (qup->mode == QUP_IO_M_MODE_FIFO)
+ 		spi_qup_fifo_write(qup, xfer);
+
++	ret = spi_qup_set_state(qup, QUP_STATE_RUN);
++	if (ret) {
++		dev_warn(qup->dev, "cannot set RUN state\n");
++		return ret;
++	}
++
++	if (!wait_for_completion_timeout(&qup->done, timeout))
++		return -ETIMEDOUT;
++
+ 	return 0;
+ }
+
+@@ -430,7 +460,6 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
+ 			dev_warn(controller->dev, "CLK_OVER_RUN\n");
+ 		if (spi_err & SPI_ERROR_CLK_UNDER_RUN)
+ 			dev_warn(controller->dev, "CLK_UNDER_RUN\n");
+-
+ 		error = -EIO;
+ 	}
+
+@@ -619,6 +648,7 @@ static int spi_qup_transfer_one(struct spi_master *master,
+ 	timeout = 100 * msecs_to_jiffies(timeout);
+
+ 	reinit_completion(&controller->done);
++	reinit_completion(&controller->dma_tx_done);
+
+ 	spin_lock_irqsave(&controller->lock, flags);
+ 	controller->xfer     = xfer;
+@@ -628,21 +658,13 @@ static int spi_qup_transfer_one(struct spi_master *master,
+ 	spin_unlock_irqrestore(&controller->lock, flags);
+
+ 	if (spi_qup_is_dma_xfer(controller->mode))
+-		ret = spi_qup_do_dma(master, xfer);
++		ret = spi_qup_do_dma(master, xfer, timeout);
+ 	else
+-		ret = spi_qup_do_pio(master, xfer);
++		ret = spi_qup_do_pio(master, xfer, timeout);
+
+ 	if (ret)
+ 		goto exit;
+
+-	if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
+-		dev_warn(controller->dev, "cannot set EXECUTE state\n");
+-		goto exit;
+-	}
+-
+-	if (!wait_for_completion_timeout(&controller->done, timeout))
+-		ret = -ETIMEDOUT;
+-
+ exit:
+ 	spi_qup_set_state(controller, QUP_STATE_RESET);
+ 	spin_lock_irqsave(&controller->lock, flags);
+@@ -664,15 +686,23 @@ static bool spi_qup_can_dma(struct spi_master *master, struct spi_device *spi,
+ 	size_t dma_align = dma_get_cache_alignment();
+ 	int n_words;
+
+-	if (xfer->rx_buf && (xfer->len % qup->in_blk_sz ||
+-	    IS_ERR_OR_NULL(master->dma_rx) ||
+-	    !IS_ALIGNED((size_t)xfer->rx_buf, dma_align)))
+-		return false;
++	if (xfer->rx_buf) {
++		if (!IS_ALIGNED((size_t)xfer->rx_buf, dma_align) ||
++		    IS_ERR_OR_NULL(master->dma_rx))
++			return false;
+
+-	if (xfer->tx_buf && (xfer->len % qup->out_blk_sz ||
+-	    IS_ERR_OR_NULL(master->dma_tx) ||
+-	    !IS_ALIGNED((size_t)xfer->tx_buf, dma_align)))
+-		return false;
++		if (qup->qup_v1 && (xfer->len % qup->in_blk_sz))
++			return false;
++	}
++
++	if (xfer->tx_buf) {
++		if (!IS_ALIGNED((size_t)xfer->tx_buf, dma_align) ||
++		    IS_ERR_OR_NULL(master->dma_tx))
++			return false;
++
++		if (qup->qup_v1 && (xfer->len % qup->out_blk_sz))
++			return false;
++	}
+
+ 	n_words = xfer->len / DIV_ROUND_UP(xfer->bits_per_word, 8);
+ 	if (n_words <= (qup->in_fifo_sz / sizeof(u32)))
+@@ -875,6 +905,7 @@ static int spi_qup_probe(struct platform_device *pdev)
+
+ 	spin_lock_init(&controller->lock);
+ 	init_completion(&controller->done);
++	init_completion(&controller->dma_tx_done);
+
+ 	iomode = readl_relaxed(base + QUP_IO_M_MODES);
+
+--
+2.7.2
+
diff --git a/target/linux/ipq806x/patches-4.4/713-spi-qup-Fix-block-mode-to-work-correctly.patch b/target/linux/ipq806x/patches-4.4/713-spi-qup-Fix-block-mode-to-work-correctly.patch
new file mode 100644
index 0000000..7a05ecd
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/713-spi-qup-Fix-block-mode-to-work-correctly.patch
@@ -0,0 +1,317 @@
+From 148f77310a9ddf4db5036066458d7aed92cea9ae Mon Sep 17 00:00:00 2001
+From: Andy Gross <andy.gross at linaro.org>
+Date: Sun, 31 Jan 2016 21:28:13 -0600
+Subject: [PATCH] spi: qup: Fix block mode to work correctly
+
+This patch corrects the behavior of the BLOCK
+transactions.  During block transactions, the controller
+must be read/written to in block size transactions.
+
+Signed-off-by: Andy Gross <andy.gross at linaro.org>
+
+Change-Id: I4b4f4d25be57e6e8148f6f0d24bed376eb287ecf
+---
+ drivers/spi/spi-qup.c | 181 +++++++++++++++++++++++++++++++++++++++-----------
+ 1 file changed, 141 insertions(+), 40 deletions(-)
+
+diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
+index 089c5e8..e487416 100644
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -83,6 +83,8 @@
+ #define QUP_IO_M_MODE_BAM		3
+
+ /* QUP_OPERATIONAL fields */
++#define QUP_OP_IN_BLOCK_READ_REQ	BIT(13)
++#define QUP_OP_OUT_BLOCK_WRITE_REQ	BIT(12)
+ #define QUP_OP_MAX_INPUT_DONE_FLAG	BIT(11)
+ #define QUP_OP_MAX_OUTPUT_DONE_FLAG	BIT(10)
+ #define QUP_OP_IN_SERVICE_FLAG		BIT(9)
+@@ -156,6 +158,12 @@ struct spi_qup {
+ 	struct dma_slave_config	tx_conf;
+ };
+
++static inline bool spi_qup_is_flag_set(struct spi_qup *controller, u32 flag)
++{
++	u32 opflag = readl_relaxed(controller->base + QUP_OPERATIONAL);
++
++	return opflag & flag;
++}
+
+ static inline bool spi_qup_is_dma_xfer(int mode)
+ {
+@@ -217,29 +225,26 @@ static int spi_qup_set_state(struct spi_qup *controller, u32 state)
+ 	return 0;
+ }
+
+-static void spi_qup_fifo_read(struct spi_qup *controller,
+-			    struct spi_transfer *xfer)
++static void spi_qup_read_from_fifo(struct spi_qup *controller,
++	struct spi_transfer *xfer, u32 num_words)
+ {
+ 	u8 *rx_buf = xfer->rx_buf;
+-	u32 word, state;
+-	int idx, shift, w_size;
+-
+-	w_size = controller->w_size;
+-
+-	while (controller->rx_bytes < xfer->len) {
++	int i, shift, num_bytes;
++	u32 word;
+
+-		state = readl_relaxed(controller->base + QUP_OPERATIONAL);
+-		if (0 == (state & QUP_OP_IN_FIFO_NOT_EMPTY))
+-			break;
++	for (; num_words; num_words--) {
+
+ 		word = readl_relaxed(controller->base + QUP_INPUT_FIFO);
+
++		num_bytes = min_t(int, xfer->len - controller->rx_bytes,
++					controller->w_size);
++
+ 		if (!rx_buf) {
+-			controller->rx_bytes += w_size;
++			controller->rx_bytes += num_bytes;
+ 			continue;
+ 		}
+
+-		for (idx = 0; idx < w_size; idx++, controller->rx_bytes++) {
++		for (i = 0; i < num_bytes; i++, controller->rx_bytes++) {
+ 			/*
+ 			 * The data format depends on bytes per SPI word:
+ 			 *  4 bytes: 0x12345678
+@@ -247,38 +252,80 @@ static void spi_qup_fifo_read(struct spi_qup *controller,
+ 			 *  1 byte : 0x00000012
+ 			 */
+ 			shift = BITS_PER_BYTE;
+-			shift *= (w_size - idx - 1);
++			shift *= (controller->w_size - i - 1);
+ 			rx_buf[controller->rx_bytes] = word >> shift;
+ 		}
+ 	}
+ }
+
+-static void spi_qup_fifo_write(struct spi_qup *controller,
++static void spi_qup_read(struct spi_qup *controller,
+ 			    struct spi_transfer *xfer)
+ {
+-	const u8 *tx_buf = xfer->tx_buf;
+-	u32 word, state, data;
+-	int idx, w_size;
++	u32 remainder, words_per_block, num_words;
++	bool is_block_mode = controller->mode == QUP_IO_M_MODE_BLOCK;
++
++	remainder = DIV_ROUND_UP(xfer->len - controller->rx_bytes,
++				 controller->w_size);
++	words_per_block = controller->in_blk_sz >> 2;
++
++	do {
++		/* ACK by clearing service flag */
++		writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
++			       controller->base + QUP_OPERATIONAL);
++
++		if (is_block_mode) {
++			num_words = (remainder > words_per_block) ?
++					words_per_block : remainder;
++		} else {
++			if (!spi_qup_is_flag_set(controller,
++						 QUP_OP_IN_FIFO_NOT_EMPTY))
++				break;
++
++			num_words = 1;
++		}
+
+-	w_size = controller->w_size;
++		/* read up to the maximum transfer size available */
++		spi_qup_read_from_fifo(controller, xfer, num_words);
+
+-	while (controller->tx_bytes < xfer->len) {
++		remainder -= num_words;
+
+-		state = readl_relaxed(controller->base + QUP_OPERATIONAL);
+-		if (state & QUP_OP_OUT_FIFO_FULL)
++		/* if block mode, check to see if next block is available */
++		if (is_block_mode && !spi_qup_is_flag_set(controller,
++					QUP_OP_IN_BLOCK_READ_REQ))
+ 			break;
+
++	} while (remainder);
++
++	/*
++	 * Due to extra stickiness of the QUP_OP_IN_SERVICE_FLAG during block
++	 * mode reads, it has to be cleared again at the very end
++	 */
++	if (is_block_mode && spi_qup_is_flag_set(controller,
++				QUP_OP_MAX_INPUT_DONE_FLAG))
++		writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
++			       controller->base + QUP_OPERATIONAL);
++
++}
++
++static void spi_qup_write_to_fifo(struct spi_qup *controller,
++	struct spi_transfer *xfer, u32 num_words)
++{
++	const u8 *tx_buf = xfer->tx_buf;
++	int i, num_bytes;
++	u32 word, data;
++
++	for (; num_words; num_words--) {
+ 		word = 0;
+-		for (idx = 0; idx < w_size; idx++, controller->tx_bytes++) {
+
+-			if (!tx_buf) {
+-				controller->tx_bytes += w_size;
+-				break;
++		num_bytes = min_t(int, xfer->len - controller->tx_bytes,
++				    controller->w_size);
++		if (tx_buf)
++			for (i = 0; i < num_bytes; i++) {
++				data = tx_buf[controller->tx_bytes + i];
++				word |= data << (BITS_PER_BYTE * (3 - i));
+ 			}
+
+-			data = tx_buf[controller->tx_bytes];
+-			word |= data << (BITS_PER_BYTE * (3 - idx));
+-		}
++		controller->tx_bytes += num_bytes;
+
+ 		writel_relaxed(word, controller->base + QUP_OUTPUT_FIFO);
+ 	}
+@@ -291,6 +338,44 @@ static void spi_qup_dma_done(void *data)
+ 	complete(done);
+ }
+
++static void spi_qup_write(struct spi_qup *controller,
++			    struct spi_transfer *xfer)
++{
++	bool is_block_mode = controller->mode == QUP_IO_M_MODE_BLOCK;
++	u32 remainder, words_per_block, num_words;
++
++	remainder = DIV_ROUND_UP(xfer->len - controller->tx_bytes,
++				 controller->w_size);
++	words_per_block = controller->out_blk_sz >> 2;
++
++	do {
++		/* ACK by clearing service flag */
++		writel_relaxed(QUP_OP_OUT_SERVICE_FLAG,
++			       controller->base + QUP_OPERATIONAL);
++
++		if (is_block_mode) {
++			num_words = (remainder > words_per_block) ?
++				words_per_block : remainder;
++		} else {
++			if (spi_qup_is_flag_set(controller,
++						QUP_OP_OUT_FIFO_FULL))
++				break;
++
++			num_words = 1;
++		}
++
++		spi_qup_write_to_fifo(controller, xfer, num_words);
++
++		remainder -= num_words;
++
++		/* if block mode, check to see if next block is available */
++		if (is_block_mode && !spi_qup_is_flag_set(controller,
++					QUP_OP_OUT_BLOCK_WRITE_REQ))
++			break;
++
++	} while (remainder);
++}
++
+ static int spi_qup_prep_sg(struct spi_master *master, struct spi_transfer *xfer,
+ 			   enum dma_transfer_direction dir,
+ 			   dma_async_tx_callback callback,
+@@ -348,11 +433,13 @@ unsigned long timeout)
+ 		return ret;
+ 	}
+
+-	if (xfer->rx_buf)
+-		rx_done = spi_qup_dma_done;
++	if (!qup->qup_v1) {
++		if (xfer->rx_buf)
++			rx_done = spi_qup_dma_done;
+
+-	if (xfer->tx_buf)
+-		tx_done = spi_qup_dma_done;
++		if (xfer->tx_buf)
++			tx_done = spi_qup_dma_done;
++	}
+
+ 	if (xfer->rx_buf) {
+ 		ret = spi_qup_prep_sg(master, xfer, DMA_DEV_TO_MEM, rx_done,
+@@ -401,7 +488,7 @@ static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer,
+ 	}
+
+ 	if (qup->mode == QUP_IO_M_MODE_FIFO)
+-		spi_qup_fifo_write(qup, xfer);
++		spi_qup_write(qup, xfer);
+
+ 	ret = spi_qup_set_state(qup, QUP_STATE_RUN);
+ 	if (ret) {
+@@ -434,10 +521,11 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
+
+ 	writel_relaxed(qup_err, controller->base + QUP_ERROR_FLAGS);
+ 	writel_relaxed(spi_err, controller->base + SPI_ERROR_FLAGS);
+-	writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
+
+ 	if (!xfer) {
+-		dev_err_ratelimited(controller->dev, "unexpected irq %08x %08x %08x\n",
++		writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
++		dev_err_ratelimited(controller->dev,
++				    "unexpected irq %08x %08x %08x\n",
+ 				    qup_err, spi_err, opflags);
+ 		return IRQ_HANDLED;
+ 	}
+@@ -463,12 +551,20 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
+ 		error = -EIO;
+ 	}
+
+-	if (!spi_qup_is_dma_xfer(controller->mode)) {
++	if (spi_qup_is_dma_xfer(controller->mode)) {
++		writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
++		if (opflags & QUP_OP_IN_SERVICE_FLAG &&
++		    opflags & QUP_OP_MAX_INPUT_DONE_FLAG)
++			complete(&controller->done);
++		if (opflags & QUP_OP_OUT_SERVICE_FLAG &&
++		    opflags & QUP_OP_MAX_OUTPUT_DONE_FLAG)
++			complete(&controller->dma_tx_done);
++	} else {
+ 		if (opflags & QUP_OP_IN_SERVICE_FLAG)
+-			spi_qup_fifo_read(controller, xfer);
++			spi_qup_read(controller, xfer);
+
+ 		if (opflags & QUP_OP_OUT_SERVICE_FLAG)
+-			spi_qup_fifo_write(controller, xfer);
++			spi_qup_write(controller, xfer);
+ 	}
+
+ 	spin_lock_irqsave(&controller->lock, flags);
+@@ -476,6 +572,9 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
+ 	controller->xfer = xfer;
+ 	spin_unlock_irqrestore(&controller->lock, flags);
+
++	/* re-read opflags as flags may have changed due to actions above */
++	opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
++
+ 	if ((controller->rx_bytes == xfer->len &&
+ 		(opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) ||  error)
+ 		complete(&controller->done);
+@@ -519,11 +618,13 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
+ 		/* must be zero for FIFO */
+ 		writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
+ 		writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
+-		controller->use_dma = 0;
+ 	} else if (spi->master->can_dma &&
+ 	    spi->master->can_dma(spi->master, spi, xfer) &&
+ 	    spi->master->cur_msg_mapped) {
+ 		controller->mode = QUP_IO_M_MODE_BAM;
++		writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
++		writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
++		/* must be zero for BLOCK and BAM */
+ 		writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
+ 		writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
+
+--
+2.7.2
+
diff --git a/target/linux/ipq806x/patches-4.4/714-spi-qup-properly-detect-extra-interrupts.patch b/target/linux/ipq806x/patches-4.4/714-spi-qup-properly-detect-extra-interrupts.patch
new file mode 100644
index 0000000..d8a9b31
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/714-spi-qup-properly-detect-extra-interrupts.patch
@@ -0,0 +1,67 @@
+From b69e5e855aaae2dd9f7fc6f4a40af8e6e0cf98ee Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint at codeaurora.org>
+Date: Thu, 10 Mar 2016 16:44:55 -0600
+Subject: [PATCH] spi: qup: properly detect extra interrupts
+
+It's possible for a SPI transaction to complete and get another
+interrupt and have it processed on the same spi_transfer before the
+transfer_one can set it to NULL.
+
+This masks unexpected interrupts, so let's set the spi_transfer to
+NULL in the interrupt once the transaction is done. So we can
+properly detect these bad interrupts and print warning messages.
+
+Change-Id: I0e70ed59fb50e5c48a72a38f74bd178b17c9f24d
+Signed-off-by: Matthew McClintock <mmcclint at codeaurora.org>
+---
+ drivers/spi/spi-qup.c | 15 +++++++++------
+ 1 file changed, 9 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
+index e487416..45e30c7 100644
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -509,6 +509,7 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
+ 	u32 opflags, qup_err, spi_err;
+ 	unsigned long flags;
+ 	int error = 0;
++	bool done = 0;
+
+ 	spin_lock_irqsave(&controller->lock, flags);
+ 	xfer = controller->xfer;
+@@ -567,16 +568,19 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
+ 			spi_qup_write(controller, xfer);
+ 	}
+
+-	spin_lock_irqsave(&controller->lock, flags);
+-	controller->error = error;
+-	controller->xfer = xfer;
+-	spin_unlock_irqrestore(&controller->lock, flags);
+-
+ 	/* re-read opflags as flags may have changed due to actions above */
+ 	opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
+
+ 	if ((controller->rx_bytes == xfer->len &&
+ 		(opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) ||  error)
++		done = true;
++
++	spin_lock_irqsave(&controller->lock, flags);
++	controller->error = error;
++	controller->xfer = done ? NULL : xfer;
++	spin_unlock_irqrestore(&controller->lock, flags);
++
++	if (done)
+ 		complete(&controller->done);
+
+ 	return IRQ_HANDLED;
+@@ -769,7 +773,6 @@ static int spi_qup_transfer_one(struct spi_master *master,
+ exit:
+ 	spi_qup_set_state(controller, QUP_STATE_RESET);
+ 	spin_lock_irqsave(&controller->lock, flags);
+-	controller->xfer = NULL;
+ 	if (!ret)
+ 		ret = controller->error;
+ 	spin_unlock_irqrestore(&controller->lock, flags);
+--
+2.7.2
+
diff --git a/target/linux/ipq806x/patches-4.4/715-spi-qup-don-t-re-read-opflags-to-see-if-transaction-.patch b/target/linux/ipq806x/patches-4.4/715-spi-qup-don-t-re-read-opflags-to-see-if-transaction-.patch
new file mode 100644
index 0000000..54711a1
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/715-spi-qup-don-t-re-read-opflags-to-see-if-transaction-.patch
@@ -0,0 +1,32 @@
+From f57ff801665b868d8607c9e872466b54982564bc Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint at codeaurora.org>
+Date: Thu, 10 Mar 2016 16:48:27 -0600
+Subject: [PATCH] spi: qup: don't re-read opflags to see if transaction is done
+ for reads
+
+For reads, we will get another interrupt so we need to handle things
+then. For writes, we can finish up now.
+
+Change-Id: I4fa95ae7bb9d78f8ba54c613b981b37d4ea81d7e
+Signed-off-by: Matthew McClintock <mmcclint at codeaurora.org>
+---
+ drivers/spi/spi-qup.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
+index 45e30c7..59bc37c 100644
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -569,7 +569,8 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
+ 	}
+
+ 	/* re-read opflags as flags may have changed due to actions above */
+-	opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
++	if (opflags & QUP_OP_OUT_SERVICE_FLAG)
++		opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
+
+ 	if ((controller->rx_bytes == xfer->len &&
+ 		(opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) ||  error)
+--
+2.7.2
+



More information about the lede-commits mailing list