ipq806x: sync with latest patches sent by QCA

Signed-off-by: John Crispin <john@phrozen.org>
This commit is contained in:
John Crispin 2016-08-11 16:34:08 +02:00
parent 5e563262f1
commit d7e4b9babb
11 changed files with 1350 additions and 93 deletions

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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);
+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);

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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