Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Chunfeng Yun and committed by
Greg Kroah-Hartman
a2ecc4df 99ca1f6f

+64 -117
+59 -116
drivers/usb/host/xhci-mtk.c
··· 57 57 /* u2_phy_pll register */ 58 58 #define CTRL_U2_FORCE_PLL_STB BIT(28) 59 59 60 - #define PERI_WK_CTRL0 0x400 61 - #define UWK_CTR0_0P_LS_PE BIT(8) /* posedge */ 62 - #define UWK_CTR0_0P_LS_NE BIT(7) /* negedge for 0p linestate*/ 63 - #define UWK_CTL1_1P_LS_C(x) (((x) & 0xf) << 1) 64 - #define UWK_CTL1_1P_LS_E BIT(0) 60 + /* usb remote wakeup registers in syscon */ 61 + /* mt8173 etc */ 62 + #define PERI_WK_CTRL1 0x4 63 + #define WC1_IS_C(x) (((x) & 0xf) << 26) /* cycle debounce */ 64 + #define WC1_IS_EN BIT(25) 65 + #define WC1_IS_P BIT(6) /* polarity for ip sleep */ 65 66 66 - #define PERI_WK_CTRL1 0x404 67 - #define UWK_CTL1_IS_C(x) (((x) & 0xf) << 26) 68 - #define UWK_CTL1_IS_E BIT(25) 69 - #define UWK_CTL1_0P_LS_C(x) (((x) & 0xf) << 21) 70 - #define UWK_CTL1_0P_LS_E BIT(20) 71 - #define UWK_CTL1_IDDIG_C(x) (((x) & 0xf) << 11) /* cycle debounce */ 72 - #define UWK_CTL1_IDDIG_E BIT(10) /* enable debounce */ 73 - #define UWK_CTL1_IDDIG_P BIT(9) /* polarity */ 74 - #define UWK_CTL1_0P_LS_P BIT(7) 75 - #define UWK_CTL1_IS_P BIT(6) /* polarity for ip sleep */ 67 + /* mt2712 etc */ 68 + #define PERI_SSUSB_SPM_CTRL 0x0 69 + #define SSC_IP_SLEEP_EN BIT(4) 70 + #define SSC_SPM_INT_EN BIT(1) 76 71 77 - enum ssusb_wakeup_src { 78 - SSUSB_WK_IP_SLEEP = 1, 79 - SSUSB_WK_LINE_STATE = 2, 72 + enum ssusb_uwk_vers { 73 + SSUSB_UWK_V1 = 1, 74 + SSUSB_UWK_V2, 80 75 }; 81 76 82 77 static int xhci_mtk_host_enable(struct xhci_hcd_mtk *mtk) ··· 291 296 } 292 297 293 298 /* only clocks can be turn off for ip-sleep wakeup mode */ 294 - static void usb_wakeup_ip_sleep_en(struct xhci_hcd_mtk *mtk) 299 + static void usb_wakeup_ip_sleep_set(struct xhci_hcd_mtk *mtk, bool enable) 295 300 { 296 - u32 tmp; 297 - struct regmap *pericfg = mtk->pericfg; 301 + u32 reg, msk, val; 298 302 299 - regmap_read(pericfg, PERI_WK_CTRL1, &tmp); 300 - tmp &= ~UWK_CTL1_IS_P; 301 - tmp &= ~(UWK_CTL1_IS_C(0xf)); 302 - tmp |= UWK_CTL1_IS_C(0x8); 303 - regmap_write(pericfg, PERI_WK_CTRL1, tmp); 304 - regmap_write(pericfg, PERI_WK_CTRL1, tmp | UWK_CTL1_IS_E); 305 - 306 - regmap_read(pericfg, PERI_WK_CTRL1, &tmp); 307 - dev_dbg(mtk->dev, "%s(): WK_CTRL1[P6,E25,C26:29]=%#x\n", 308 - __func__, tmp); 309 - } 310 - 311 - static void usb_wakeup_ip_sleep_dis(struct xhci_hcd_mtk *mtk) 312 - { 313 - u32 tmp; 314 - 315 - regmap_read(mtk->pericfg, PERI_WK_CTRL1, &tmp); 316 - tmp &= ~UWK_CTL1_IS_E; 317 - regmap_write(mtk->pericfg, PERI_WK_CTRL1, tmp); 318 - } 319 - 320 - /* 321 - * for line-state wakeup mode, phy's power should not power-down 322 - * and only support cable plug in/out 323 - */ 324 - static void usb_wakeup_line_state_en(struct xhci_hcd_mtk *mtk) 325 - { 326 - u32 tmp; 327 - struct regmap *pericfg = mtk->pericfg; 328 - 329 - /* line-state of u2-port0 */ 330 - regmap_read(pericfg, PERI_WK_CTRL1, &tmp); 331 - tmp &= ~UWK_CTL1_0P_LS_P; 332 - tmp &= ~(UWK_CTL1_0P_LS_C(0xf)); 333 - tmp |= UWK_CTL1_0P_LS_C(0x8); 334 - regmap_write(pericfg, PERI_WK_CTRL1, tmp); 335 - regmap_read(pericfg, PERI_WK_CTRL1, &tmp); 336 - regmap_write(pericfg, PERI_WK_CTRL1, tmp | UWK_CTL1_0P_LS_E); 337 - 338 - /* line-state of u2-port1 */ 339 - regmap_read(pericfg, PERI_WK_CTRL0, &tmp); 340 - tmp &= ~(UWK_CTL1_1P_LS_C(0xf)); 341 - tmp |= UWK_CTL1_1P_LS_C(0x8); 342 - regmap_write(pericfg, PERI_WK_CTRL0, tmp); 343 - regmap_write(pericfg, PERI_WK_CTRL0, tmp | UWK_CTL1_1P_LS_E); 344 - } 345 - 346 - static void usb_wakeup_line_state_dis(struct xhci_hcd_mtk *mtk) 347 - { 348 - u32 tmp; 349 - struct regmap *pericfg = mtk->pericfg; 350 - 351 - /* line-state of u2-port0 */ 352 - regmap_read(pericfg, PERI_WK_CTRL1, &tmp); 353 - tmp &= ~UWK_CTL1_0P_LS_E; 354 - regmap_write(pericfg, PERI_WK_CTRL1, tmp); 355 - 356 - /* line-state of u2-port1 */ 357 - regmap_read(pericfg, PERI_WK_CTRL0, &tmp); 358 - tmp &= ~UWK_CTL1_1P_LS_E; 359 - regmap_write(pericfg, PERI_WK_CTRL0, tmp); 360 - } 361 - 362 - static void usb_wakeup_enable(struct xhci_hcd_mtk *mtk) 363 - { 364 - if (mtk->wakeup_src == SSUSB_WK_IP_SLEEP) 365 - usb_wakeup_ip_sleep_en(mtk); 366 - else if (mtk->wakeup_src == SSUSB_WK_LINE_STATE) 367 - usb_wakeup_line_state_en(mtk); 368 - } 369 - 370 - static void usb_wakeup_disable(struct xhci_hcd_mtk *mtk) 371 - { 372 - if (mtk->wakeup_src == SSUSB_WK_IP_SLEEP) 373 - usb_wakeup_ip_sleep_dis(mtk); 374 - else if (mtk->wakeup_src == SSUSB_WK_LINE_STATE) 375 - usb_wakeup_line_state_dis(mtk); 303 + switch (mtk->uwk_vers) { 304 + case SSUSB_UWK_V1: 305 + reg = mtk->uwk_reg_base + PERI_WK_CTRL1; 306 + msk = WC1_IS_EN | WC1_IS_C(0xf) | WC1_IS_P; 307 + val = enable ? (WC1_IS_EN | WC1_IS_C(0x8)) : 0; 308 + break; 309 + case SSUSB_UWK_V2: 310 + reg = mtk->uwk_reg_base + PERI_SSUSB_SPM_CTRL; 311 + msk = SSC_IP_SLEEP_EN | SSC_SPM_INT_EN; 312 + val = enable ? msk : 0; 313 + break; 314 + default: 315 + return; 316 + }; 317 + regmap_update_bits(mtk->uwk, reg, msk, val); 376 318 } 377 319 378 320 static int usb_wakeup_of_property_parse(struct xhci_hcd_mtk *mtk, 379 321 struct device_node *dn) 380 322 { 381 - struct device *dev = mtk->dev; 323 + struct of_phandle_args args; 324 + int ret; 382 325 383 - /* 384 - * wakeup function is optional, so it is not an error if this property 385 - * does not exist, and in such case, no need to get relative 386 - * properties anymore. 387 - */ 388 - of_property_read_u32(dn, "mediatek,wakeup-src", &mtk->wakeup_src); 389 - if (!mtk->wakeup_src) 326 + /* Wakeup function is optional */ 327 + mtk->uwk_en = of_property_read_bool(dn, "wakeup-source"); 328 + if (!mtk->uwk_en) 390 329 return 0; 391 330 392 - mtk->pericfg = syscon_regmap_lookup_by_phandle(dn, 393 - "mediatek,syscon-wakeup"); 394 - if (IS_ERR(mtk->pericfg)) { 395 - dev_err(dev, "fail to get pericfg regs\n"); 396 - return PTR_ERR(mtk->pericfg); 397 - } 331 + ret = of_parse_phandle_with_fixed_args(dn, 332 + "mediatek,syscon-wakeup", 2, 0, &args); 333 + if (ret) 334 + return ret; 398 335 399 - return 0; 336 + mtk->uwk_reg_base = args.args[0]; 337 + mtk->uwk_vers = args.args[1]; 338 + mtk->uwk = syscon_node_to_regmap(args.np); 339 + of_node_put(args.np); 340 + dev_info(mtk->dev, "uwk - reg:0x%x, version:%d\n", 341 + mtk->uwk_reg_base, mtk->uwk_vers); 342 + 343 + return PTR_ERR_OR_ZERO(mtk->uwk); 344 + 345 + } 346 + 347 + static void usb_wakeup_set(struct xhci_hcd_mtk *mtk, bool enable) 348 + { 349 + if (mtk->uwk_en) 350 + usb_wakeup_ip_sleep_set(mtk, enable); 400 351 } 401 352 402 353 static int xhci_mtk_setup(struct usb_hcd *hcd); ··· 524 583 &mtk->u3p_dis_msk); 525 584 526 585 ret = usb_wakeup_of_property_parse(mtk, node); 527 - if (ret) 586 + if (ret) { 587 + dev_err(dev, "failed to parse uwk property\n"); 528 588 return ret; 589 + } 529 590 530 591 mtk->num_phys = of_count_phandle_with_args(node, 531 592 "phys", "#phy-cells"); ··· 720 777 xhci_mtk_host_disable(mtk); 721 778 xhci_mtk_phy_power_off(mtk); 722 779 xhci_mtk_clks_disable(mtk); 723 - usb_wakeup_enable(mtk); 780 + usb_wakeup_set(mtk, true); 724 781 return 0; 725 782 } 726 783 ··· 730 787 struct usb_hcd *hcd = mtk->hcd; 731 788 struct xhci_hcd *xhci = hcd_to_xhci(hcd); 732 789 733 - usb_wakeup_disable(mtk); 790 + usb_wakeup_set(mtk, false); 734 791 xhci_mtk_clks_enable(mtk); 735 792 xhci_mtk_phy_power_on(mtk); 736 793 xhci_mtk_host_enable(mtk);
+5 -1
drivers/usb/host/xhci-mtk.h
··· 122 122 struct regmap *pericfg; 123 123 struct phy **phys; 124 124 int num_phys; 125 - int wakeup_src; 126 125 bool lpm_support; 126 + /* usb remote wakeup */ 127 + bool uwk_en; 128 + struct regmap *uwk; 129 + u32 uwk_reg_base; 130 + u32 uwk_vers; 127 131 }; 128 132 129 133 static inline struct xhci_hcd_mtk *hcd_to_mtk(struct usb_hcd *hcd)