/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2019 MediaTek Inc. */ #ifdef CONFIG_MTK_CLKMGR #include #else #include #endif #include #include #include #include #include #include #include "usb20.h" #include "mtk_devinfo.h" #ifdef CONFIG_OF #include #endif #include #include #define FRA (48) #define PARA (28) #ifdef CONFIG_FPGA_EARLY_PORTING bool usb_enable_clock(bool enable) { return true; } bool usb_prepare_clock(bool enable) { return true; } void usb_prepare_enable_clock(bool enable) { } void usb_phy_poweron(void) { } void usb_phy_savecurrent(void) { } void usb_phy_recover(void) { } /* BC1.2 */ void Charger_Detect_Init(void) { } void Charger_Detect_Release(void) { } void usb_phy_context_save(void) { } void usb_phy_context_restore(void) { } #ifdef CONFIG_MTK_UART_USB_SWITCH bool usb_phy_check_in_uart_mode(void) { return false; } void usb_phy_switch_to_uart(void) { } void usb_phy_switch_to_usb(void) { } #endif #else #ifdef CONFIG_MTK_USB2JTAG_SUPPORT int usb2jtag_usb_init(void) { struct device_node *node = NULL; void __iomem *usb_phy_base; u32 temp; node = of_find_compatible_node(NULL, NULL, "mediatek,mt6739-usb20"); if (!node) { pr_notice("[USB2JTAG] map node @ mediatek,USB0 failed\n"); return -1; } usb_phy_base = of_iomap(node, 1); if (!usb_phy_base) { pr_notice("[USB2JTAG] iomap usb_phy_base failed\n"); return -1; } /* rg_usb20_gpio_ctl: bit[9] = 1 */ temp = readl(usb_phy_base + 0x820); writel(temp | (1 << 9), usb_phy_base + 0x820); /* RG_USB20_BGR_EN: bit[0] = 1 */ temp = readl(usb_phy_base + 0x800); writel(temp | (1 << 0), usb_phy_base + 0x800); /* RG_USB20_BC11_SW_EN: bit[23] = 0 */ temp = readl(usb_phy_base + 0x818); writel(temp & ~(1 << 23), usb_phy_base + 0x818); /* wait stable */ mdelay(1); iounmap(usb_phy_base); return 0; } #endif #ifdef CONFIG_MTK_UART_USB_SWITCH bool in_uart_mode; #endif void usb_prepare_enable_clock(bool enable) { if (enable) { usb_prepare_clock(true); usb_enable_clock(true); } else { usb_enable_clock(false); usb_prepare_clock(false); } } DEFINE_MUTEX(prepare_lock); static atomic_t clk_prepare_cnt = ATOMIC_INIT(0); bool usb_prepare_clock(bool enable) { int before_cnt = atomic_read(&clk_prepare_cnt); mutex_lock(&prepare_lock); if (IS_ERR_OR_NULL(musb_clk) || IS_ERR_OR_NULL(musb_clk_top_sel) || IS_ERR_OR_NULL(musb_clk_univpll3_d4)) { DBG(0, "clk not ready\n"); mutex_unlock(&prepare_lock); return 0; } if (enable) { if (clk_prepare(musb_clk_top_sel)) { DBG(0, "musb_clk_top_sel prepare fail\n"); } else { if (clk_set_parent(musb_clk_top_sel, musb_clk_univpll3_d4)) DBG(0, "musb_clk_top_sel set_parent fail\n"); } if (clk_prepare(musb_clk)) DBG(0, "musb_clk prepare fail\n"); atomic_inc(&clk_prepare_cnt); } else { atomic_dec(&clk_prepare_cnt); clk_unprepare(musb_clk_top_sel); clk_unprepare(musb_clk); } mutex_unlock(&prepare_lock); DBG(1, "enable(%d), usb prepare_cnt, before(%d), after(%d)\n", enable, before_cnt, atomic_read(&clk_prepare_cnt)); if (atomic_read(&clk_prepare_cnt) < 0) aee_kernel_warning("usb20", "usb clock prepare_cnt error\n"); return 1; } static DEFINE_SPINLOCK(musb_reg_clock_lock); bool usb_enable_clock(bool enable) { static int count; static int real_enable = 0, real_disable; static int virt_enable = 0, virt_disable; unsigned long flags; DBG(1, "enable(%d),count(%d),<%d,%d,%d,%d>\n", enable, count, virt_enable, virt_disable, real_enable, real_disable); spin_lock_irqsave(&musb_reg_clock_lock, flags); if (unlikely(atomic_read(&clk_prepare_cnt) <= 0)) { DBG_LIMIT(1, "clock not prepare"); goto exit; } if (enable && count == 0) { if (clk_enable(musb_clk_top_sel)) { DBG(0, "musb_clk_top_sel enable fail\n"); goto exit; } if (clk_enable(musb_clk)) { DBG(0, "musb_clk enable fail\n"); clk_disable(musb_clk_top_sel); goto exit; } usb_hal_dpidle_request(USB_DPIDLE_FORBIDDEN); real_enable++; } else if (!enable && count == 1) { clk_disable(musb_clk); clk_disable(musb_clk_top_sel); usb_hal_dpidle_request(USB_DPIDLE_ALLOWED); real_disable++; } if (enable) count++; else count = (count == 0) ? 0 : (count - 1); exit: if (enable) virt_enable++; else virt_disable++; spin_unlock_irqrestore(&musb_reg_clock_lock, flags); DBG(1, "enable(%d),count(%d), <%d,%d,%d,%d>\n", enable, count, virt_enable, virt_disable, real_enable, real_disable); return 1; } static void hs_slew_rate_cal(void) { unsigned long data; unsigned long x; unsigned char value; unsigned long start_time, timeout; unsigned int timeout_flag = 0; /* enable usb ring oscillator. */ USBPHY_SET32(0x14, (0x1 << 15)); /* wait 1us. */ udelay(1); /* enable free run clock */ USBPHY_SET32(0xF10 - 0x800, (0x01 << 8)); /* setting cyclecnt. */ USBPHY_SET32(0xF00 - 0x800, (0x04 << 8)); /* enable frequency meter */ USBPHY_SET32(0xF00 - 0x800, (0x01 << 24)); /* wait for frequency valid. */ start_time = jiffies; timeout = jiffies + 3 * HZ; while (!((USBPHY_READ32(0xF10 - 0x800) & 0xFF) == 0x1)) { if (time_after(jiffies, timeout)) { timeout_flag = 1; break; } } /* read result. */ if (timeout_flag) { DBG(0, "[USBPHY] Slew Rate Calibration: Timeout\n"); value = 0x4; } else { data = USBPHY_READ32(0xF0C - 0x800); x = ((1024 * FRA * PARA) / data); value = (unsigned char)(x / 1000); if ((x - value * 1000) / 100 >= 5) value += 1; DBG(0, "[USBPHY]slew calibration:FM_OUT =%lu,x=%lu,value=%d\n", data, x, value); } /* disable Frequency and disable free run clock. */ USBPHY_CLR32(0xF00 - 0x800, (0x01 << 24)); USBPHY_CLR32(0xF10 - 0x800, (0x01 << 8)); #define MSK_RG_USB20_HSTX_SRCTRL 0x7 /* all clr first then set */ USBPHY_CLR32(0x14, (MSK_RG_USB20_HSTX_SRCTRL << 12)); USBPHY_SET32(0x14, ((value & MSK_RG_USB20_HSTX_SRCTRL) << 12)); /* disable usb ring oscillator. */ USBPHY_CLR32(0x14, (0x1 << 15)); } #ifdef CONFIG_MTK_UART_USB_SWITCH bool usb_phy_check_in_uart_mode(void) { u32 usb_port_mode; usb_port_mode = USBPHY_READ32(0x68); if (((usb_port_mode >> 30) & 0x3) == 1) { DBG(0, "%s:%d - IN UART MODE : 0x%x\n", __func__, __LINE__, usb_port_mode); in_uart_mode = true; } else { DBG(0, "%s:%d - NOT IN UART MODE : 0x%x\n", __func__, __LINE__, usb_port_mode); in_uart_mode = false; } return in_uart_mode; } void usb_phy_switch_to_uart(void) { unsigned int val = 0; if (usb_phy_check_in_uart_mode()) { DBG(0, "Already in UART mode.\n"); return; } udelay(50); /* RG_USB20_BC11_SW_EN 0x11F4_0818[23] = 1'b0 */ USBPHY_CLR32(0x18, (0x1 << 23)); /* Set RG_SUSPENDM 0x11F4_0868[3] to 1 */ USBPHY_SET32(0x68, (0x1 << 3)); /* force suspendm 0x11F4_0868[18] = 1 */ USBPHY_SET32(0x68, (0x1 << 18)); /* Set rg_uart_mode 0x11F4_0868[31:30] to 2'b01 */ USBPHY_CLR32(0x68, (0x3 << 30)); USBPHY_SET32(0x68, (0x1 << 30)); /* force_uart_i 0x11F4_0868[29] = 0*/ USBPHY_CLR32(0x68, (0x1 << 29)); /* force_uart_bias_en 0x11F4_0868[28] = 1 */ USBPHY_SET32(0x68, (0x1 << 28)); /* force_uart_tx_oe 0x11F4_0868[27] = 1 */ USBPHY_SET32(0x68, (0x1 << 27)); /* force_uart_en 0x11F4_0868[26] = 1 */ USBPHY_SET32(0x68, (0x1 << 26)); /* RG_UART_BIAS_EN 0x11F4_086c[18] = 1 */ USBPHY_SET32(0x6C, (0x1 << 18)); /* RG_UART_TX_OE 0x11F4_086c[17] = 1 */ USBPHY_SET32(0x6C, (0x1 << 17)); /* Set RG_UART_EN to 1 */ USBPHY_SET32(0x6C, (0x1 << 16)); /* Set RG_USB20_DM_100K_EN to 1 */ USBPHY_SET32(0x20, (0x1 << 17)); /* GPIO Selection */ val = readl(ap_gpio_base); writel(val & (~(GPIO_SEL_MASK)), ap_gpio_base); val = readl(ap_gpio_base); writel(val | (GPIO_SEL_UART0), ap_gpio_base); in_uart_mode = true; } void usb_phy_switch_to_usb(void) { unsigned int val = 0; /* GPIO Selection */ val = readl(ap_gpio_base); writel(val & (~(GPIO_SEL_MASK)), ap_gpio_base); /* clear force_uart_en */ USBPHY_CLR32(0x68, (0x1 << 26)); /* Set rg_uart_mode 0x11F4_0868[31:30] to 2'b00 */ USBPHY_CLR32(0x68, (0x3 << 30)); in_uart_mode = false; usb_phy_poweron(); } #endif void set_usb_phy_mode(int mode) { switch (mode) { case PHY_MODE_USB_DEVICE: /* VBUSVALID=1, AVALID=1, BVALID=1, SESSEND=0, IDDIG=1, IDPULLUP=1 */ USBPHY_CLR32(0x6C, (0x10<<0)); USBPHY_SET32(0x6C, (0x2F<<0)); USBPHY_SET32(0x6C, (0x3F<<8)); break; case PHY_MODE_USB_HOST: /* VBUSVALID=1, AVALID=1, BVALID=1, SESSEND=0, IDDIG=0, IDPULLUP=1 */ USBPHY_CLR32(0x6c, (0x12<<0)); USBPHY_SET32(0x6c, (0x2d<<0)); USBPHY_SET32(0x6c, (0x3f<<8)); break; case PHY_MODE_INVALID: /* VBUSVALID=0, AVALID=0, BVALID=0, SESSEND=1, IDDIG=0, IDPULLUP=1 */ USBPHY_SET32(0x6c, (0x11<<0)); USBPHY_CLR32(0x6c, (0x2e<<0)); USBPHY_SET32(0x6c, (0x3f<<8)); break; default: DBG(0, "mode error %d\n", mode); } DBG(0, "force PHY to mode %d, 0x6c=%x\n", mode, USBPHY_READ32(0x6c)); } void usb_rev6_setting(int value) { static int direct_return; if (direct_return) return; /* RG_USB20_PHY_REV[7:0] = 8'b01000000 */ USBPHY_CLR32(0x18, (0xFF << 24)); if (value) USBPHY_SET32(0x18, (value << 24)); else direct_return = 1; } /* M17_USB_PWR Sequence 20160603.xls */ void usb_phy_poweron(void) { #ifdef CONFIG_MTK_UART_USB_SWITCH if (in_uart_mode) { DBG(0, "At UART mode. No usb_phy_poweron\n"); return; } #endif /* wait 50 usec for PHY3.3v/1.8v stable. */ udelay(50); /* * force_uart_en 1'b0 0x68 26 * RG_UART_EN 1'b0 0x6c 16 * rg_usb20_gpio_ctl 1'b0 0x20 09 * usb20_gpio_mode 1'b0 0x20 08 * RG_USB20_BC11_SW_EN 1'b0 0x18 23 * rg_usb20_dp_100k_mode 1'b1 0x20 18 * USB20_DP_100K_EN 1'b0 0x20 16 * RG_USB20_DM_100K_EN 1'b0 0x20 17 * RG_USB20_OTG_VBUSCMP_EN 1'b1 0x18 20 * force_suspendm 1'b0 0x68 18 */ /* force_uart_en, 1'b0 */ USBPHY_CLR32(0x68, (0x1 << 26)); /* RG_UART_EN, 1'b0 */ USBPHY_CLR32(0x6c, (0x1 << 16)); /* rg_usb20_gpio_ctl, 1'b0, usb20_gpio_mode, 1'b0 */ USBPHY_CLR32(0x20, ((0x1 << 9) | (0x1 << 8))); /* RG_USB20_BC11_SW_EN, 1'b0 */ USBPHY_CLR32(0x18, (0x1 << 23)); /* rg_usb20_dp_100k_mode, 1'b1 */ USBPHY_SET32(0x20, (0x1 << 18)); /* USB20_DP_100K_EN 1'b0, RG_USB20_DM_100K_EN, 1'b0 */ USBPHY_CLR32(0x20, ((0x1 << 16) | (0x1 << 17))); /* For saving 1V8 & 3V3 power*/ /* RG_USB20_OTG_VBUSCMP_EN, 1'b1 */ /* USBPHY_SET32(0x18, (0x1 << 20)); */ /* force_suspendm, 1'b0 */ USBPHY_CLR32(0x68, (0x1 << 18)); /* wait for 800 usec. */ udelay(800); DBG(0, "usb power on success\n"); } /* M17_USB_PWR Sequence 20160603.xls */ static void usb_phy_savecurrent_internal(void) { #ifdef CONFIG_MTK_UART_USB_SWITCH if (in_uart_mode) { DBG(0, "At UART mode. No usb_phy_savecurrent_internal\n"); return; } #endif /* * force_uart_en 1'b0 0x68 26 * RG_UART_EN 1'b0 0x6c 16 * rg_usb20_gpio_ctl 1'b0 0x20 09 * usb20_gpio_mode 1'b0 0x20 08 * RG_USB20_BC11_SW_EN 1'b0 0x18 23 * RG_USB20_OTG_VBUSCMP_EN 1'b0 0x18 20 * RG_SUSPENDM 1'b1 0x68 03 * force_suspendm 1'b1 0x68 18 * RG_DPPULLDOWN 1'b1 0x68 06 * RG_DMPULLDOWN 1'b1 0x68 07 * RG_XCVRSEL[1:0] 2'b01 0x68 [04-05] * RG_TERMSEL 1'b1 0x68 02 * RG_DATAIN[3:0] 4'b0000 0x68 [10-13] * force_dp_pulldown 1'b1 0x68 20 * force_dm_pulldown 1'b1 0x68 21 * force_xcversel 1'b1 0x68 19 * force_termsel 1'b1 0x68 17 * force_datain 1'b1 0x68 23 * RG_SUSPENDM 1'b0 0x68 03 */ /* force_uart_en, 1'b0 */ USBPHY_CLR32(0x68, (0x1 << 26)); /* RG_UART_EN, 1'b0 */ USBPHY_CLR32(0x6c, (0x1 << 16)); /* rg_usb20_gpio_ctl, 1'b0, usb20_gpio_mode, 1'b0 */ USBPHY_CLR32(0x20, (0x1 << 9)); USBPHY_CLR32(0x20, (0x1 << 8)); /* RG_USB20_BC11_SW_EN, 1'b0 */ USBPHY_CLR32(0x18, (0x1 << 23)); /* RG_USB20_OTG_VBUSCMP_EN, 1'b0 */ USBPHY_CLR32(0x18, (0x1 << 20)); /* RG_SUSPENDM, 1'b1 */ USBPHY_SET32(0x68, (0x1 << 3)); /* force_suspendm, 1'b1 */ USBPHY_SET32(0x68, (0x1 << 18)); /* RG_DPPULLDOWN, 1'b1, RG_DMPULLDOWN, 1'b1 */ USBPHY_SET32(0x68, ((0x1 << 6) | (0x1 << 7))); /* RG_XCVRSEL[1:0], 2'b01. */ USBPHY_CLR32(0x68, (0x3 << 4)); USBPHY_SET32(0x68, (0x1 << 4)); /* RG_TERMSEL, 1'b1 */ USBPHY_SET32(0x68, (0x1 << 2)); /* RG_DATAIN[3:0], 4'b0000 */ USBPHY_CLR32(0x68, (0xF << 10)); /* force_dp_pulldown, 1'b1, force_dm_pulldown, 1'b1, * force_xcversel, 1'b1, force_termsel, 1'b1, force_datain, 1'b1 */ USBPHY_SET32(0x68, ((0x1 << 20) | (0x1 << 21) | (0x1 << 19) | (0x1 << 17) | (0x1 << 23))); /* wait 800 usec. */ udelay(800); /* RG_SUSPENDM, 1'b0 */ USBPHY_CLR32(0x68, (0x1 << 3)); udelay(1); set_usb_phy_mode(PHY_MODE_INVALID); } void usb_phy_savecurrent(void) { usb_phy_savecurrent_internal(); DBG(0, "usb save current success\n"); } /* M17_USB_PWR Sequence 20160603.xls */ void usb_phy_recover(void) { unsigned int efuse_val = 0; #ifdef CONFIG_MTK_UART_USB_SWITCH if (in_uart_mode) { DBG(0, "At UART mode. No usb_phy_recover\n"); return; } #endif /* wait 50 usec. */ udelay(50); /* * 04.force_uart_en 1'b0 0x68 26 * 04.RG_UART_EN 1'b0 0x6C 16 * 04.rg_usb20_gpio_ctl 1'b0 0x20 09 * 04.usb20_gpio_mode 1'b0 0x20 08 * 05.force_suspendm 1'b0 0x68 18 * 06.RG_DPPULLDOWN 1'b0 0x68 06 * 07.RG_DMPULLDOWN 1'b0 0x68 07 * 08.RG_XCVRSEL[1:0] 2'b00 0x68 [04:05] * 09.RG_TERMSEL 1'b0 0x68 02 * 10.RG_DATAIN[3:0] 4'b0000 0x68 [10:13] * 11.force_dp_pulldown 1'b0 0x68 20 * 12.force_dm_pulldown 1'b0 0x68 21 * 13.force_xcversel 1'b0 0x68 19 * 14.force_termsel 1'b0 0x68 17 * 15.force_datain 1'b0 0x68 23 * 16.RG_USB20_BC11_SW_EN 1'b0 0x18 23 * 17.RG_USB20_OTG_VBUSCMP_EN 1'b1 0x18 20 */ /* clean PUPD_BIST_EN */ /* PUPD_BIST_EN = 1'b0 */ /* PMIC will use it to detect charger type */ /* NEED?? USBPHY_CLR8(0x1d, 0x10);*/ USBPHY_CLR32(0x1c, (0x1 << 12)); /* force_uart_en, 1'b0 */ USBPHY_CLR32(0x68, (0x1 << 26)); /* RG_UART_EN, 1'b0 */ USBPHY_CLR32(0x6C, (0x1 << 16)); /* rg_usb20_gpio_ctl, 1'b0, usb20_gpio_mode, 1'b0 */ USBPHY_CLR32(0x20, (0x1 << 9)); USBPHY_CLR32(0x20, (0x1 << 8)); /* force_suspendm, 1'b0 */ USBPHY_CLR32(0x68, (0x1 << 18)); /* RG_DPPULLDOWN, 1'b0, RG_DMPULLDOWN, 1'b0 */ USBPHY_CLR32(0x68, ((0x1 << 6) | (0x1 << 7))); /* RG_XCVRSEL[1:0], 2'b00. */ USBPHY_CLR32(0x68, (0x3 << 4)); /* RG_TERMSEL, 1'b0 */ USBPHY_CLR32(0x68, (0x1 << 2)); /* RG_DATAIN[3:0], 4'b0000 */ USBPHY_CLR32(0x68, (0xF << 10)); /* force_dp_pulldown, 1'b0, force_dm_pulldown, 1'b0, * force_xcversel, 1'b0, force_termsel, 1'b0, force_datain, 1'b0 */ USBPHY_CLR32(0x68, ((0x1 << 20) | (0x1 << 21) | (0x1 << 19) | (0x1 << 17) | (0x1 << 23))); /* RG_USB20_BC11_SW_EN, 1'b0 */ USBPHY_CLR32(0x18, (0x1 << 23)); /* For saving 1V8 & 3V3 power*/ /* RG_USB20_OTG_VBUSCMP_EN, 1'b1 */ /* USBPHY_SET32(0x18, (0x1 << 20)); */ /* RG_USB20_PHY_REV[7:0] = 8'b01000000 */ usb_rev6_setting(0x40); /* wait 800 usec. */ udelay(800); /* force enter device mode */ set_usb_phy_mode(PHY_MODE_USB_DEVICE); hs_slew_rate_cal(); /* M_ANALOG8[4:0] => RG_USB20_INTR_CAL[4:0] */ efuse_val = (get_devinfo_with_index(108) & (0x1f<<0)) >> 0; if (efuse_val) { DBG(0, "skip efuse setting temporary, RG_USB20_INTR_CAL=0x%x\n", efuse_val); /* DBG(0, "apply efuse setting, RG_USB20_INTR_CAL=0x%x\n", efuse_val); */ /* USBPHY_CLR32(0x04, (0x1F<<19)); */ /* USBPHY_SET32(0x04, (efuse_val<<19)); */ } /* disc threshold to max, RG_USB20_DISCTH[7:4], dft:1000, MAX:1111 */ USBPHY_SET32(0x18, (0xf0<<0)); DBG(0, "usb recovery success\n"); } /* BC1.2 */ void Charger_Detect_Init(void) { usb_prepare_enable_clock(true); /* wait 50 usec. */ udelay(50); /* RG_USB20_BC11_SW_EN = 1'b1 */ USBPHY_SET32(0x18, (0x1 << 23)); usb_prepare_enable_clock(false); DBG(0, "Charger_Detect_Init\n"); } EXPORT_SYMBOL(Charger_Detect_Init); void Charger_Detect_Release(void) { usb_prepare_enable_clock(true); /* RG_USB20_BC11_SW_EN = 1'b0 */ USBPHY_CLR32(0x18, (0x1 << 23)); udelay(1); usb_prepare_enable_clock(false); DBG(0, "Charger_Detect_Release\n"); } EXPORT_SYMBOL(Charger_Detect_Release); void usb_phy_context_save(void) { #ifdef CONFIG_MTK_UART_USB_SWITCH in_uart_mode = usb_phy_check_in_uart_mode(); #endif } void usb_phy_context_restore(void) { #ifdef CONFIG_MTK_UART_USB_SWITCH if (in_uart_mode) usb_phy_switch_to_uart(); #endif } #endif