summaryrefslogtreecommitdiff
path: root/target/linux/mediatek/patches-4.14/0190-usb-xhci-mtk-supports-remote-wakeup-for-mt2712-with-.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/mediatek/patches-4.14/0190-usb-xhci-mtk-supports-remote-wakeup-for-mt2712-with-.patch')
-rw-r--r--target/linux/mediatek/patches-4.14/0190-usb-xhci-mtk-supports-remote-wakeup-for-mt2712-with-.patch273
1 files changed, 273 insertions, 0 deletions
diff --git a/target/linux/mediatek/patches-4.14/0190-usb-xhci-mtk-supports-remote-wakeup-for-mt2712-with-.patch b/target/linux/mediatek/patches-4.14/0190-usb-xhci-mtk-supports-remote-wakeup-for-mt2712-with-.patch
new file mode 100644
index 0000000..ba7b834
--- /dev/null
+++ b/target/linux/mediatek/patches-4.14/0190-usb-xhci-mtk-supports-remote-wakeup-for-mt2712-with-.patch
@@ -0,0 +1,273 @@
+From e6fe50ef22071fe87ce48f79ab4fe21cbec2081b Mon Sep 17 00:00:00 2001
+From: Chunfeng Yun <chunfeng.yun@mediatek.com>
+Date: Wed, 3 Jan 2018 16:53:20 +0800
+Subject: [PATCH 190/224] usb: xhci-mtk: supports remote wakeup for mt2712 with
+ two xHCI IPs
+
+The old way of usb wakeup only supports platform with single xHCI IP,
+such as mt8173, but mt2712 has two xHCI IPs, so rebuild its flow and
+supports the new glue layer of usb wakeup on mt2712 which is different
+from mt8173.
+Due to there is a hardware bug with the LINE STATE wakeup mode on
+mt8173 which causes wakeup failure by low speed devices, and also
+because IP SLEEP mode can cover all functions of LINE STATE mode,
+it is unused in fact, and will not support it later, so remove it at
+the same time.
+
+Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/host/xhci-mtk.c | 177 +++++++++++++++-----------------------------
+ drivers/usb/host/xhci-mtk.h | 6 +-
+ 2 files changed, 65 insertions(+), 118 deletions(-)
+
+diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c
+index e5caabe7eebe..8e51b3fec386 100644
+--- a/drivers/usb/host/xhci-mtk.c
++++ b/drivers/usb/host/xhci-mtk.c
+@@ -66,26 +66,21 @@
+ /* u2_phy_pll register */
+ #define CTRL_U2_FORCE_PLL_STB BIT(28)
+
+-#define PERI_WK_CTRL0 0x400
+-#define UWK_CTR0_0P_LS_PE BIT(8) /* posedge */
+-#define UWK_CTR0_0P_LS_NE BIT(7) /* negedge for 0p linestate*/
+-#define UWK_CTL1_1P_LS_C(x) (((x) & 0xf) << 1)
+-#define UWK_CTL1_1P_LS_E BIT(0)
+-
+-#define PERI_WK_CTRL1 0x404
+-#define UWK_CTL1_IS_C(x) (((x) & 0xf) << 26)
+-#define UWK_CTL1_IS_E BIT(25)
+-#define UWK_CTL1_0P_LS_C(x) (((x) & 0xf) << 21)
+-#define UWK_CTL1_0P_LS_E BIT(20)
+-#define UWK_CTL1_IDDIG_C(x) (((x) & 0xf) << 11) /* cycle debounce */
+-#define UWK_CTL1_IDDIG_E BIT(10) /* enable debounce */
+-#define UWK_CTL1_IDDIG_P BIT(9) /* polarity */
+-#define UWK_CTL1_0P_LS_P BIT(7)
+-#define UWK_CTL1_IS_P BIT(6) /* polarity for ip sleep */
+-
+-enum ssusb_wakeup_src {
+- SSUSB_WK_IP_SLEEP = 1,
+- SSUSB_WK_LINE_STATE = 2,
++/* usb remote wakeup registers in syscon */
++/* mt8173 etc */
++#define PERI_WK_CTRL1 0x4
++#define WC1_IS_C(x) (((x) & 0xf) << 26) /* cycle debounce */
++#define WC1_IS_EN BIT(25)
++#define WC1_IS_P BIT(6) /* polarity for ip sleep */
++
++/* mt2712 etc */
++#define PERI_SSUSB_SPM_CTRL 0x0
++#define SSC_IP_SLEEP_EN BIT(4)
++#define SSC_SPM_INT_EN BIT(1)
++
++enum ssusb_uwk_vers {
++ SSUSB_UWK_V1 = 1,
++ SSUSB_UWK_V2,
+ };
+
+ static int xhci_mtk_host_enable(struct xhci_hcd_mtk *mtk)
+@@ -308,112 +303,58 @@ static void xhci_mtk_clks_disable(struct xhci_hcd_mtk *mtk)
+ }
+
+ /* only clocks can be turn off for ip-sleep wakeup mode */
+-static void usb_wakeup_ip_sleep_en(struct xhci_hcd_mtk *mtk)
++static void usb_wakeup_ip_sleep_set(struct xhci_hcd_mtk *mtk, bool enable)
+ {
+- u32 tmp;
+- struct regmap *pericfg = mtk->pericfg;
+-
+- regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
+- tmp &= ~UWK_CTL1_IS_P;
+- tmp &= ~(UWK_CTL1_IS_C(0xf));
+- tmp |= UWK_CTL1_IS_C(0x8);
+- regmap_write(pericfg, PERI_WK_CTRL1, tmp);
+- regmap_write(pericfg, PERI_WK_CTRL1, tmp | UWK_CTL1_IS_E);
+-
+- regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
+- dev_dbg(mtk->dev, "%s(): WK_CTRL1[P6,E25,C26:29]=%#x\n",
+- __func__, tmp);
++ u32 reg, msk, val;
++
++ switch (mtk->uwk_vers) {
++ case SSUSB_UWK_V1:
++ reg = mtk->uwk_reg_base + PERI_WK_CTRL1;
++ msk = WC1_IS_EN | WC1_IS_C(0xf) | WC1_IS_P;
++ val = enable ? (WC1_IS_EN | WC1_IS_C(0x8)) : 0;
++ break;
++ case SSUSB_UWK_V2:
++ reg = mtk->uwk_reg_base + PERI_SSUSB_SPM_CTRL;
++ msk = SSC_IP_SLEEP_EN | SSC_SPM_INT_EN;
++ val = enable ? msk : 0;
++ break;
++ default:
++ return;
++ };
++ regmap_update_bits(mtk->uwk, reg, msk, val);
+ }
+
+-static void usb_wakeup_ip_sleep_dis(struct xhci_hcd_mtk *mtk)
++static int usb_wakeup_of_property_parse(struct xhci_hcd_mtk *mtk,
++ struct device_node *dn)
+ {
+- u32 tmp;
++ struct of_phandle_args args;
++ int ret;
+
+- regmap_read(mtk->pericfg, PERI_WK_CTRL1, &tmp);
+- tmp &= ~UWK_CTL1_IS_E;
+- regmap_write(mtk->pericfg, PERI_WK_CTRL1, tmp);
+-}
++ /* Wakeup function is optional */
++ mtk->uwk_en = of_property_read_bool(dn, "wakeup-source");
++ if (!mtk->uwk_en)
++ return 0;
+
+-/*
+-* for line-state wakeup mode, phy's power should not power-down
+-* and only support cable plug in/out
+-*/
+-static void usb_wakeup_line_state_en(struct xhci_hcd_mtk *mtk)
+-{
+- u32 tmp;
+- struct regmap *pericfg = mtk->pericfg;
+-
+- /* line-state of u2-port0 */
+- regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
+- tmp &= ~UWK_CTL1_0P_LS_P;
+- tmp &= ~(UWK_CTL1_0P_LS_C(0xf));
+- tmp |= UWK_CTL1_0P_LS_C(0x8);
+- regmap_write(pericfg, PERI_WK_CTRL1, tmp);
+- regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
+- regmap_write(pericfg, PERI_WK_CTRL1, tmp | UWK_CTL1_0P_LS_E);
+-
+- /* line-state of u2-port1 */
+- regmap_read(pericfg, PERI_WK_CTRL0, &tmp);
+- tmp &= ~(UWK_CTL1_1P_LS_C(0xf));
+- tmp |= UWK_CTL1_1P_LS_C(0x8);
+- regmap_write(pericfg, PERI_WK_CTRL0, tmp);
+- regmap_write(pericfg, PERI_WK_CTRL0, tmp | UWK_CTL1_1P_LS_E);
+-}
++ ret = of_parse_phandle_with_fixed_args(dn,
++ "mediatek,syscon-wakeup", 2, 0, &args);
++ if (ret)
++ return ret;
+
+-static void usb_wakeup_line_state_dis(struct xhci_hcd_mtk *mtk)
+-{
+- u32 tmp;
+- struct regmap *pericfg = mtk->pericfg;
+-
+- /* line-state of u2-port0 */
+- regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
+- tmp &= ~UWK_CTL1_0P_LS_E;
+- regmap_write(pericfg, PERI_WK_CTRL1, tmp);
+-
+- /* line-state of u2-port1 */
+- regmap_read(pericfg, PERI_WK_CTRL0, &tmp);
+- tmp &= ~UWK_CTL1_1P_LS_E;
+- regmap_write(pericfg, PERI_WK_CTRL0, tmp);
+-}
++ mtk->uwk_reg_base = args.args[0];
++ mtk->uwk_vers = args.args[1];
++ mtk->uwk = syscon_node_to_regmap(args.np);
++ of_node_put(args.np);
++ dev_info(mtk->dev, "uwk - reg:0x%x, version:%d\n",
++ mtk->uwk_reg_base, mtk->uwk_vers);
+
+-static void usb_wakeup_enable(struct xhci_hcd_mtk *mtk)
+-{
+- if (mtk->wakeup_src == SSUSB_WK_IP_SLEEP)
+- usb_wakeup_ip_sleep_en(mtk);
+- else if (mtk->wakeup_src == SSUSB_WK_LINE_STATE)
+- usb_wakeup_line_state_en(mtk);
+-}
++ return PTR_ERR_OR_ZERO(mtk->uwk);
+
+-static void usb_wakeup_disable(struct xhci_hcd_mtk *mtk)
+-{
+- if (mtk->wakeup_src == SSUSB_WK_IP_SLEEP)
+- usb_wakeup_ip_sleep_dis(mtk);
+- else if (mtk->wakeup_src == SSUSB_WK_LINE_STATE)
+- usb_wakeup_line_state_dis(mtk);
+ }
+
+-static int usb_wakeup_of_property_parse(struct xhci_hcd_mtk *mtk,
+- struct device_node *dn)
++static void usb_wakeup_set(struct xhci_hcd_mtk *mtk, bool enable)
+ {
+- struct device *dev = mtk->dev;
+-
+- /*
+- * wakeup function is optional, so it is not an error if this property
+- * does not exist, and in such case, no need to get relative
+- * properties anymore.
+- */
+- of_property_read_u32(dn, "mediatek,wakeup-src", &mtk->wakeup_src);
+- if (!mtk->wakeup_src)
+- return 0;
+-
+- mtk->pericfg = syscon_regmap_lookup_by_phandle(dn,
+- "mediatek,syscon-wakeup");
+- if (IS_ERR(mtk->pericfg)) {
+- dev_err(dev, "fail to get pericfg regs\n");
+- return PTR_ERR(mtk->pericfg);
+- }
+-
+- return 0;
++ if (mtk->uwk_en)
++ usb_wakeup_ip_sleep_set(mtk, enable);
+ }
+
+ static int xhci_mtk_setup(struct usb_hcd *hcd);
+@@ -595,8 +536,10 @@ static int xhci_mtk_probe(struct platform_device *pdev)
+ &mtk->u3p_dis_msk);
+
+ ret = usb_wakeup_of_property_parse(mtk, node);
+- if (ret)
++ if (ret) {
++ dev_err(dev, "failed to parse uwk property\n");
+ return ret;
++ }
+
+ mtk->num_phys = of_count_phandle_with_args(node,
+ "phys", "#phy-cells");
+@@ -780,7 +723,7 @@ static int __maybe_unused xhci_mtk_suspend(struct device *dev)
+ xhci_mtk_host_disable(mtk);
+ xhci_mtk_phy_power_off(mtk);
+ xhci_mtk_clks_disable(mtk);
+- usb_wakeup_enable(mtk);
++ usb_wakeup_set(mtk, true);
+ return 0;
+ }
+
+@@ -790,7 +733,7 @@ static int __maybe_unused xhci_mtk_resume(struct device *dev)
+ struct usb_hcd *hcd = mtk->hcd;
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+
+- usb_wakeup_disable(mtk);
++ usb_wakeup_set(mtk, false);
+ xhci_mtk_clks_enable(mtk);
+ xhci_mtk_phy_power_on(mtk);
+ xhci_mtk_host_enable(mtk);
+diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h
+index 45ff5c67efb5..85c007ee1f52 100644
+--- a/drivers/usb/host/xhci-mtk.h
++++ b/drivers/usb/host/xhci-mtk.h
+@@ -131,8 +131,12 @@ struct xhci_hcd_mtk {
+ struct regmap *pericfg;
+ struct phy **phys;
+ int num_phys;
+- int wakeup_src;
+ bool lpm_support;
++ /* usb remote wakeup */
++ bool uwk_en;
++ struct regmap *uwk;
++ u32 uwk_reg_base;
++ u32 uwk_vers;
+ };
+
+ static inline struct xhci_hcd_mtk *hcd_to_mtk(struct usb_hcd *hcd)
+--
+2.11.0
+