diff options
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-.patch | 273 |
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 + |