ipq806x: sync with latest patches sent by QCA
Signed-off-by: John Crispin <john@phrozen.org>
This commit is contained in:
parent
5e563262f1
commit
d7e4b9babb
11 changed files with 1350 additions and 93 deletions
|
@ -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)
|
||||
|
|
|
@ -0,0 +1,244 @@
|
|||
From 41c2b5280cd2fa3e198c422cdf223ba6e48f857a Mon Sep 17 00:00:00 2001
|
||||
From: Felipe Balbi <balbi@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@xilinx.com>
|
||||
Signed-off-by: Felipe Balbi <balbi@ti.com>
|
||||
(cherry picked from commit 16adc674d0d68a50dfc725574738d7ae11cf5d7e)
|
||||
|
||||
Change-Id: I6fd260442997b198dc12ca726814b7a9518e6353
|
||||
Signed-off-by: Nitheesh Sekar <nsekar@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@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@mm-sol.com> and the original patch adding support for Xilinx' SoC
|
||||
+ * by Subbaraya Sundeep Bhatta <subbaraya.sundeep.bhatta@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@ti.com>");
|
||||
--
|
||||
2.7.2
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
From 131386d63ca3177d471aa93808c69b85fdac520d Mon Sep 17 00:00:00 2001
|
||||
From: Felipe Balbi <balbi@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@ti.com>
|
||||
(cherry picked from commit 5072cfc40a80cea3749fd3413b3896630d8c787e)
|
||||
|
||||
Change-Id: I088186c33aa917ec8da2985372ceefc289b24242
|
||||
Signed-off-by: Nitheesh Sekar <nsekar@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
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
From 07c8b15688055d81ac8e1c8c964b9e4c302287f1 Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Boyd <sboyd@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@kernel.org>
|
||||
Cc: <linux-usb@vger.kernel.org>
|
||||
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
|
||||
(cherry picked from commit 3d755dcc20dd452b52532eca17da40ebbd12aee9)
|
||||
|
||||
Change-Id: Iaa38e064d801fb36c855fea51c0443840368e0d3
|
||||
Signed-off-by: Nitheesh Sekar <nsekar@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
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
--- 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"
|
||||
|
@ -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);
|
||||
+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,
|
||||
+};
|
||||
+
|
||||
+ 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 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);
|
||||
|
|
|
@ -0,0 +1,225 @@
|
|||
From 93f99afbc534e00d72d58336061823055ee820f1 Mon Sep 17 00:00:00 2001
|
||||
From: Andy Gross <andy.gross@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@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
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
From 8e830bd17e945e74964a5b61353d74e34c0791cd Mon Sep 17 00:00:00 2001
|
||||
From: Andy Gross <andy.gross@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@chromium.org>
|
||||
CC: Sarthak Kukreti <skukreti@codeaurora.org>
|
||||
Signed-off-by: Andy Gross <andy.gross@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
|
||||
|
|
@ -0,0 +1,226 @@
|
|||
From ed56e6322b067a898a25bda1774eb1180a832246 Mon Sep 17 00:00:00 2001
|
||||
From: Andy Gross <andy.gross@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@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
|
||||
|
|
@ -0,0 +1,317 @@
|
|||
From 148f77310a9ddf4db5036066458d7aed92cea9ae Mon Sep 17 00:00:00 2001
|
||||
From: Andy Gross <andy.gross@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@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
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
From b69e5e855aaae2dd9f7fc6f4a40af8e6e0cf98ee Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@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@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
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
From f57ff801665b868d8607c9e872466b54982564bc Mon Sep 17 00:00:00 2001
|
||||
From: Matthew McClintock <mmcclint@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@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
|
||||
|
Loading…
Reference in a new issue