soc/qualcomm/x1p42100: Add USB Type-C support

Add USB DWC3 controller, VBUS support and Integrate Clock and PHY
for Type C ports C0, C1.
The register details are part of HRD-X1P42100-S1 document.
https://docs.qualcomm.com/bundle/resource/topics/HRD-X1P42100-S1/

BUG=b:448107633
TEST=Create an image.serial.bin, ensure it boots on X1P42100
and check USB port link status.

firmware-shell: md 0x0a800420 8
0a800420: 000002a0 00000000 00000000 00000000    ................
0a800430: 00001203 00000000 00000000 00000000    ................

firmware-shell: md 0x0a600420 8
0a600420: 00000e03 00000000 00000000 00000000    ................
0a600430: 00001203 00000000 00000000 00000000    ..........

Console logs:
[INFO ]  Setting up USB HOST controller.
[DEBUG]  USB HS PHY initialized for index 0
[DEBUG]  USB HS PHY initialized for index 1
[DEBUG]  QMP PHY MP0 init
[DEBUG]  QMP PHY MP0 initialized and locked in 1674us
[DEBUG]  QMP PHY MP1 init
[DEBUG]  QMP PHY MP1 initialized and locked in 1674us
[DEBUG]  USB HS PHY initialized for index 2
[DEBUG]  QMP-1x16 USB4 DP PHY SS0 init
[DEBUG]  QMP PHY SS0 initialized and locked in 1671us
[INFO ]  Enabling SMB1 VBUS SuperSpeed
[DEBUG]  SMB1 OTG Status: 0x00, State: 0x00
[ERROR]  SMB1 OTG enable timeout after 100 ms, final state: 0x00
[ERROR]  SMB1 OTG enable failed
[INFO ]  SMB1 Type-C Status:
[INFO ]    Misc Status (0x2B0B): 0x3b
[INFO ]    Src Status (0x2B08): 0x00
[INFO ]    Mode Config (0x2B44): 0x00
[DEBUG]  USB HS PHY initialized for index 3
[DEBUG]  QMP-1x16 USB4 DP PHY SS1 init
[DEBUG]  QMP PHY SS1 initialized and locked in 1672us
[INFO ]  Enabling SMB2 VBUS SuperSpeed
[DEBUG]  SMB2 OTG Status: 0x03, State: 0x03
[DEBUG]  SMB2 OTG Status: 0x02, State: 0x02
[INFO ]  SMB2 OTG block enabled successfully
[INFO ]  SMB2 Type-C Status:
[INFO ]    Misc Status (0x2B0B): 0x49, VBUS Status (bit 5): 0
[INFO ]    Src Status (0x2B08): 0x08
[INFO ]    Mode Config (0x2B44): 0x00
[SPEW ]  Configure USB in Host mode
[SPEW ]  Configure USB Primary in Host mode
[SPEW ]  Configure USB Secondary in Host mode
[INFO ]  DWC3 and PHY setup finished

Change-Id: I9f5e9ef910844358308a16b31de6da58d5da7f3a
Signed-off-by: Hari L <haril@qualcomm.corp-partner.google.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/89447
Reviewed-by: Subrata Banik <subratabanik@google.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Kapil Porwal <kapilporwal@google.com>
Reviewed-by: Paul Menzel <paulepanter@mailbox.org>
This commit is contained in:
Hari L 2025-10-07 19:20:26 +05:30 committed by Subrata Banik
commit 8ffa58723a
4 changed files with 489 additions and 38 deletions

View file

@ -6,6 +6,9 @@
#include <stddef.h>
#include <stdint.h>
/* Macro to construct SPMI address from slave ID and register offset */
#define SPMI_ADDR(slave, reg) ((slave << 16) | reg)
int spmi_read8(uint32_t addr);
int spmi_write8(uint32_t addr, uint8_t data);

View file

@ -18,8 +18,8 @@
/* CPUCP */
#define APSS_HM_BASE 0x17000000
#define APSS_CPUCP_LPM_BFSM_REG_BASE (APSS_HM_BASE + 0x019d0000)
#define HWIO_APSS_CPUCP_CPUCP_LPM_SEQ_WAIT_EVT_CTRL_MASK_ADDR (APSS_CPUCP_LPM_BFSM_REG_BASE + 0x161c)
#define APSS_CPUCP_LPM_BFSM_REG_BASE (APSS_HM_BASE + 0x019D0000)
#define HWIO_APSS_CPUCP_CPUCP_LPM_SEQ_WAIT_EVT_CTRL_MASK_ADDR (APSS_CPUCP_LPM_BFSM_REG_BASE + 0x161C)
#define HWIO_APSS_CPUCP_CPUCP_SW_WAKEUP_REQ_ADDR (APSS_CPUCP_LPM_BFSM_REG_BASE + 0x1688)
/* X1P42100 QSPI GPIO PINS */
@ -74,40 +74,42 @@
#define HS_USB_SS1_PHY_BASE 0x00FD9000
#define HS_USB_SS2_PHY_BASE 0x00FDE000
#define HS_USB_MP0_PHY_BASE 0x088e1000
#define HS_USB_MP0_PHY_BASE 0x088E1000
#define HS_USB_MP1_PHY_BASE 0x088E2000
#define QMP_PHY_MP0_QSERDES_COM_REG_BASE 0x088E3000
#define QMP_PHY_MP0_QSERDES_TX_REG_BASE 0x088E3E00
#define QMP_PHY_MP0_QSERDES_RX_REG_BASE 0x088E4000
#define QMP_PHY_MP0_PCS_REG_BASE 0x088E3200
#define QMP_PHY_MP0_PCS_REG_BASE 0x088E3200
#define QMP_PHY_MP0_PCS_USB3_REG_BASE 0x088E4200
#define USB_HOST_DWC3_BASE 0x0a40c100
#define USB_HOST_DWC3_MP_BASE 0x0A40C100
#define QMP_PHY_MP1_QSERDES_COM_REG_BASE 0x088E5000
#define QMP_PHY_MP1_QSERDES_TX_REG_BASE 0x088E5E00
#define QMP_PHY_MP1_QSERDES_RX_REG_BASE 0x088E6000
#define QMP_PHY_MP1_PCS_REG_BASE 0x088E5200
#define QMP_PHY_MP1_PCS_REG_BASE 0x088E5200
#define QMP_PHY_MP1_PCS_USB3_REG_BASE 0x088E6200
/* USB SS0 PHY BASE ADDRESSES - AHB2PHY_1_QUSB4PHY_SS_0 */
#define QMP_PHY_SS0_COM_REG_BASE 0xFD5000 // USB43DP_COM
#define QMP_PHY_SS0_TXA_REG_BASE 0xFD5400 // USB43DP_QSERDES_TXA
#define QMP_PHY_SS0_TXB_REG_BASE 0xFD5A00 // USB43DP_QSERDES_TXB
#define QMP_PHY_SS0_RXA_REG_BASE 0xFD5600 // USB43DP_QSERDES_RXA
#define QMP_PHY_SS0_RXB_REG_BASE 0xFD5C00 // USB43DP_QSERDES_RXB
#define QMP_PHY_SS0_QSERDES_PLL_REG_BASE 0x00FD6000 // USB3_QSERDES_PLL
#define QMP_PHY_SS0_PCS_REG_BASE 0xFD6400 // USB3_PCS
#define QMP_PHY_SS0_PCS_USB3_REG_BASE 0xFD6700 // USB3_PCS_USB3
#define QMP_PHY_SS0_COM_REG_BASE 0x00FD5000 /* USB43DP_COM */
#define QMP_PHY_SS0_TXA_REG_BASE 0x00FD5400 /* USB43DP_QSERDES_TXA */
#define QMP_PHY_SS0_TXB_REG_BASE 0x00FD5A00 /* USB43DP_QSERDES_TXB */
#define QMP_PHY_SS0_RXA_REG_BASE 0x00FD5600 /* USB43DP_QSERDES_RXA */
#define QMP_PHY_SS0_RXB_REG_BASE 0x00FD5C00 /* USB43DP_QSERDES_RXB */
#define QMP_PHY_SS0_QSERDES_PLL_REG_BASE 0x00FD6000 /* USB3_QSERDES_PLL */
#define QMP_PHY_SS0_PCS_REG_BASE 0x00FD6400 /* USB3_PCS */
#define QMP_PHY_SS0_PCS_USB3_REG_BASE 0x00FD6700 /* USB3_PCS_USB3 */
#define USB_HOST_DWC3_PRIM_BASE 0x0A60C100
/* USB SS1 PHY BASE ADDRESSES - AHB2PHY_1_QUSB4PHY_SS_1 */
#define QMP_PHY_SS1_COM_REG_BASE 0xFDA000 // USB43DP_COM
#define QMP_PHY_SS1_TXA_REG_BASE 0xFDA400 // USB43DP_QSERDES_TXA
#define QMP_PHY_SS1_TXB_REG_BASE 0xFDAA00 // USB43DP_QSERDES_TXB
#define QMP_PHY_SS1_RXA_REG_BASE 0xFDA600 // USB43DP_QSERDES_RXA
#define QMP_PHY_SS1_RXB_REG_BASE 0xFDAC00 // USB43DP_QSERDES_RXB
#define QMP_PHY_SS1_QSERDES_PLL_REG_BASE 0xFDB000 // USB3_QSERDES_PLL
#define QMP_PHY_SS1_PCS_REG_BASE 0xFDB400 // USB3_PCS
#define QMP_PHY_SS1_PCS_USB3_REG_BASE 0xFDB700 // USB3_PCS_USB3
#define QMP_PHY_SS1_COM_REG_BASE 0x00FDA000 /* USB43DP_COM */
#define QMP_PHY_SS1_TXA_REG_BASE 0x00FDA400 /* USB43DP_QSERDES_TXA */
#define QMP_PHY_SS1_TXB_REG_BASE 0x00FDAA00 /* USB43DP_QSERDES_TXB */
#define QMP_PHY_SS1_RXA_REG_BASE 0x00FDA600 /* USB43DP_QSERDES_RXA */
#define QMP_PHY_SS1_RXB_REG_BASE 0x00FDAC00 /* USB43DP_QSERDES_RXB */
#define QMP_PHY_SS1_QSERDES_PLL_REG_BASE 0xFDB000 /* USB3_QSERDES_PLL */
#define QMP_PHY_SS1_PCS_REG_BASE 0xFDB400 /* USB3_PCS */
#define QMP_PHY_SS1_PCS_USB3_REG_BASE 0xFDB700 /* USB3_PCS_USB3 */
#define USB_HOST_DWC3_SEC_BASE 0x0A80C100
/* PCIE 6A */
#define PCIE6A_PCIE_PARF 0x01BF8000
@ -166,8 +168,10 @@
#define TCSR_GCC_USB3_MP0_CLKREF_EN_ADDR ((void *)0x1FD510C)
#define TCSR_GCC_USB3_MP1_CLKREF_EN_ADDR ((void *)0x1FD5110)
#define TCSR_GCC_USB4_1_CLKREF_EN_ADDR ((void *)0x1FD5120)
#define TCSR_GCC_USB2_1_CLKREF_EN_ADDR ((void *)0x1FD5114)
#define TCSR_GCC_USB4_2_CLKREF_EN_ADDR ((void *)0x1FD5124)
#define USB3_CLKREF_ENABLE_VALUE 0x1
#define TCSR_GCC_USB2_2_CLKREF_EN_ADDR ((void *)0x1FD5118)
#define USB_CLKREF_ENABLE_VALUE 0x1
/* SPMI PMIC ARB */
#define SPMI_PMIC_ARB_CORE_BASE 0x0C400000

View file

@ -29,12 +29,29 @@
#define DWC3_GCTL_DISSCRAMBLE (1 << 3)
#define DWC3_GCTL_U2EXIT_LFPS (1 << 2)
#define DWC3_GCTL_DSBLCLKGTNG (1 << 0)
/* Global RX Threshold Configuration Register */
#define DWC3_GRXTHRCFG_USBMAXRXBURSTSIZE_SHFT 19
#define DWC3_GRXTHRCFG_USBRXPKTCNT_SHFT 24
#define DWC3_GRXTHRCFG_USBRXPKTCNTSEL BIT(29)
#define DWC3_GRXTHRCFG_USBMAXRXBURSTSIZE(n) ((n) << DWC3_GRXTHRCFG_USBMAXRXBURSTSIZE_SHFT)
#define DWC3_GRXTHRCFG_USBRXPKTCNT(n) ((n) << DWC3_GRXTHRCFG_USBRXPKTCNT_SHFT)
/* Global SBus Configuration 1 Register */
#define DWC3_GSBUSCFG1_PIPETRANSLIMIT_SHFT 8
#define DWC3_GSBUSCFG1_EN1KPAGE BIT(12)
#define DWC3_GSBUSCFG1_PIPETRANSLIMIT(n) ((n) << DWC3_GSBUSCFG1_PIPETRANSLIMIT_SHFT)
/* Global User Control Register */
#define DWC3_GUCTL_SPRSCTRLTRANSEN BIT(17)
#define UTMI_CLK_DIS_0 (1 << 8)
#define UTMI_CLK_SEL_0 (1 << 0)
#define PIPE3_PHYSTATUS_SW_0 (1 << 3)
#define PIPE3_SET_PHYSTATUS_SW_0 (1 << 9)
#define USB3_MP_CGCTL_REG_ADDR ((void *)0x0A4F8828)
#define USB3_MP_DBM_FSM_EN_BIT (1 << 1)
#define USB3_CGCTL_DBM_FSM_EN_BIT (1 << 1)
#define USB3_MP_LINK_REGS_1_LU3LFPSRXTIM_ADDR ((void *)0X0A40D090)
#define USB3_MP_LINK_REGS_0_LU3LFPSRXTIM_ADDR ((void *)0X0A40D010)
@ -56,7 +73,7 @@
DWC3_GUCTL1_IP_GAP_ADD_ON(0x3) | \
DWC3_GUCTL1_DEV_DECOUPLE_L1L2_EVT)
#define USB_HOST_DWC3_GENERAL_CFG_ADDR 0X0A4F8808
#define USB_HOST_DWC3_MP_GENERAL_CFG_ADDR 0X0A4F8808
#define USB3_MP_GUSB2PHYCFG_REGS_1_ADDR ((void *)0x0A40C204)
#define GUSB2PHYCFG_ENBLSLPM_BIT BIT(8)
#define USB3_MP_GUSB2PHYCFG_REGS_0_ADDR ((void *)0x0A40C200)
@ -66,6 +83,51 @@
#define USB3_MP_PORTSC_30_REGS_1_ADDR ((void *)0x0A400450)
#define USB3_PORTSC_WCE_BIT BIT(25)
/* USB4 SS USB3 DRD SP 0 (Primary) Register Addresses */
#define USB4_SS_USB3_DRD_SP_0_USB31_PRIMCGCTL_REG ((void *)0x0A6F8828)
#define USB4_SS_USB3_DRD_SP_0_USB31_PRIMLINK_REGS_0_LU3LFPSRXTIM_ADDR ((void *)0x0A61D010)
#define USB4_SS_USB3_DRD_SP_0_USB31_PRIMGUSB2PHYCFG_REGS_0_GUSB2PHYCFG_ADDR ((void *)0x0A60C200)
#define USB4_SS_USB3_DRD_SP_0_USB31_PRIMPORTSC_20_REGS_0_PORTSC_20_ADDR ((void *)0x0A600420)
#define USB4_SS_USB3_DRD_SP_0_USB31_PRIMPORTSC_30_REGS_0_PORTSC_30_ADDR ((void *)0x0A600430)
#define USB4_SS_USB3_DRD_SP_0_USB31_PRIMGENERAL_CFG 0xA6F8808
/* USB4 SS USB3 DRD SP 1 (Secondary) Register Addresses */
#define USB4_SS_USB3_DRD_SP_1_USB31_SECCGCTL_REG ((void *)0x0A8F8828)
#define USB4_SS_USB3_DRD_SP_1_USB31_SECLINK_REGS_0_LU3LFPSRXTIM_ADDR ((void *)0x0A81D010)
#define USB4_SS_USB3_DRD_SP_1_USB31_SECGUSB2PHYCFG_REGS_0_GUSB2PHYCFG_ADDR ((void *)0x0A80C200)
#define USB4_SS_USB3_DRD_SP_1_USB31_SECPORTSC_20_REGS_0_PORTSC_20_ADDR ((void *)0x0A800420)
#define USB4_SS_USB3_DRD_SP_1_USB31_SECPORTSC_30_REGS_0_PORTSC_30_ADDR ((void *)0x0A800430)
#define USB4_SS_USB3_DRD_SP_1_USB31_SECGENERAL_CFG 0xA8F8808
/* VBUS SS Enable definitions - using same slave IDs as battery charging */
#define SMB1_SLAVE_ID 0x07
#define SMB2_SLAVE_ID 0x0A
/* SCHG DCDC register offsets */
#define SCHG_DCDC_CMD_OTG 0x2740
#define SCHG_DCDC_OTG_CFG 0x2753
#define SCHG_DCDC_OTG_STATUS 0x270D
/* OTG Status register bit definitions */
#define OTG_STATE_MASK 0x07
#define OTG_STATE_DISABLED 0x00
#define OTG_STATE_ENABLING 0x01
#define OTG_STATE_ENABLED 0x02
#define OTG_STATE_DISABLING 0x03
#define OTG_STATE_ERROR 0x04
/* OTG Status check timeout and polling interval */
#define OTG_STATUS_TIMEOUT_MS 100
#define OTG_STATUS_POLL_INTERVAL_MS 2
/* Type-C register offsets */
#define SCHG_TYPE_C_TYPE_C_MISC_STATUS 0x2B0B
#define SCHG_TYPE_C_TYPE_C_SRC_STATUS 0x2B08
#define SCHG_TYPE_C_TYPE_C_MODE_CFG 0x2B44
#define TYPEC_VBUS_STATUS_MASK BIT(5)
/* Forward declaration */
struct dwc3_controller_config;
/* Initializes a specific HS PHY instance */
void hs_usb_phy_init(int index);
/* Initializes and configures the USB HOST0 controller */
@ -74,3 +136,9 @@ void setup_usb_host0(void);
int qcom_enable_usb_clk(void);
/* Enable TCSR REFGEN */
void enable_clock_tcsr(void);
/* Enable TCSR CLKREF */
void usb_update_refclk_for_core(u32 core_num, bool enable);
/* Enables VBUS SuperSpeed for specified USB core */
void enable_vbus_ss(const struct dwc3_controller_config *config);
/* Reads comprehensive Type-C status from PMIC */
void usb_typec_status_check(const struct dwc3_controller_config *config);

View file

@ -5,6 +5,7 @@
#include <soc/usb/usb.h>
#include <soc/addressmap.h>
#include <soc/clock.h>
#include <soc/qcom_spmi.h>
#include <delay.h>
#include <gpio.h>
@ -49,20 +50,29 @@ check_member(usb_dwc3, usb3pipectl_mp1, 0x1c4);
struct usb_dwc3_cfg {
struct usb_dwc3 *usb_host_dwc3;
struct usb_dwc3 *usb_host_dwc3_prim;
struct usb_dwc3 *usb_host_dwc3_sec;
u32 *usb3_bcr;
u32 *qusb2phy_bcr;
u32 *qusb2phy1_bcr;
u32 *gcc_qusb2phy_prim_bcr;
u32 *gcc_qusb2phy_sec_bcr;
u32 *gcc_usb3phy_bcr_reg;
u32 *gcc_qmpphy_bcr_reg;
u32 *gcc_usb3phy1_bcr_reg;
u32 *gcc_qmpphy1_bcr_reg;
u32 *qusb2secphy_bcr;
u32 *gcc_usb3secphy_bcr_reg;
u32 *gcc_qmpsecphy_bcr_reg;
u32 *gcc_usb4_0_dp0_phy_prim_bcr;
u32 *gcc_usb4_1_dp0_phy_sec_bcr;
u32 *gcc_usb3_phy_prim_bcr;
u32 *gcc_usb3_phy_sec_bcr;
u32 *gcc_usb3phy_phy_prim_bcr;
u32 *gcc_usb3phy_phy_sec_bcr;
};
static struct usb_dwc3_cfg usb_port0 = {
.usb_host_dwc3 = (void *)USB_HOST_DWC3_BASE,
static struct usb_dwc3_cfg usb_ports = {
.usb_host_dwc3 = (void *)USB_HOST_DWC3_MP_BASE,
.usb_host_dwc3_prim = (void *)USB_HOST_DWC3_PRIM_BASE,
.usb_host_dwc3_sec = (void *)USB_HOST_DWC3_SEC_BASE,
.usb3_bcr = &gcc->gcc_usb30_mp_bcr,
.qusb2phy_bcr = &gcc->qusb2phy_hs0_mp_bcr,
.gcc_usb3phy_bcr_reg = &gcc->usb3uniphy_phy_mp0_bcr,
@ -70,10 +80,18 @@ static struct usb_dwc3_cfg usb_port0 = {
.qusb2phy1_bcr = &gcc->qusb2phy_hs1_mp_bcr,
.gcc_usb3phy1_bcr_reg = &gcc->usb3uniphy_phy_mp1_bcr,
.gcc_qmpphy1_bcr_reg = &gcc->usb3_uniphy_mp1_bcr,
.gcc_qusb2phy_prim_bcr = &gcc->gcc_qusb2phy_prim_bcr,
.gcc_qusb2phy_sec_bcr = &gcc->gcc_qusb2phy_sec_bcr,
.gcc_usb4_0_dp0_phy_prim_bcr = &gcc->gcc_usb4_0_dp0_phy_prim_bcr,
.gcc_usb4_1_dp0_phy_sec_bcr = &gcc->gcc_usb4_1_dp0_phy_sec_bcr,
.gcc_usb3_phy_prim_bcr = &gcc->gcc_usb3_phy_prim_bcr,
.gcc_usb3_phy_sec_bcr = &gcc->gcc_usb3_phy_sec_bcr,
.gcc_usb3phy_phy_prim_bcr = &gcc->gcc_usb3phy_phy_prim_bcr,
.gcc_usb3phy_phy_sec_bcr = &gcc->gcc_usb3phy_phy_sec_bcr,
};
bool hs_speed_only = false;
u32 *usb3_general_cfg_addr = (void *)USB_HOST_DWC3_GENERAL_CFG_ADDR;
static bool hs_speed_only;
static u32 *usb3_general_cfg_addr = (void *)USB_HOST_DWC3_MP_GENERAL_CFG_ADDR;
/* Enables Repeaters /Refgen blocks for USB */
void enable_clock_tcsr(void)
@ -107,7 +125,7 @@ int32_t qcom_enable_usb_clk(void)
}
clock_configure_usb();
/* Set USB3 PHY PIPE 0 clock source to X0 */
if (usb_clock_configure_mux(USB3_PHY_PIPE_0, USB_PHY_XO_SRC_SEL)) {
printk(BIOS_ERR, "%s(): USB3 PHY PIPE 0 clock enable failed\n", __func__);
return -1;
@ -125,8 +143,7 @@ int32_t qcom_enable_usb_clk(void)
}
}
write32(TCSR_GCC_USB3_MP0_CLKREF_EN_ADDR, USB3_CLKREF_ENABLE_VALUE);
write32(TCSR_GCC_USB3_MP1_CLKREF_EN_ADDR, USB3_CLKREF_ENABLE_VALUE);
usb_update_refclk_for_core(3, true);
/* Set USB3 PHY PIPE 1 clock source to USB PHY */
if (usb_clock_configure_mux(USB3_PHY_PIPE_0, USB_PHY_PIPE_SRC_SEL)) {
@ -139,6 +156,48 @@ int32_t qcom_enable_usb_clk(void)
return -1;
}
if (usb_clock_configure_mux(USB3_PRIM_PHY_PIPE, USB_PHY_XO_SRC_SEL)) {
printk(BIOS_ERR, "%s(): USB3 PRIM PHY PIPE clock XO config failed\n", __func__);
return -1;
}
/* Enable USB0 PRIM clocks */
for (clk = USB_PRIM_SYS_NOC_USB_AXI_CBCR; clk < USB_PRIM_CLK_COUNT; clk++) {
ret = usb_prim_clock_enable(clk);
if (ret) {
printk(BIOS_ERR, "Failed to enable USB0 clock %d\n", clk);
return ret;
}
}
if (usb_clock_configure_mux(USB3_PRIM_PHY_PIPE, USB_PHY_PIPE_SRC_SEL)) {
printk(BIOS_ERR, "%s(): USB3 PRIM PHY PIPE clock PHY config failed\n", __func__);
return -1;
}
usb_update_refclk_for_core(0, true);
if (usb_clock_configure_mux(USB3_SEC_PHY_PIPE, USB_PHY_XO_SRC_SEL)) {
printk(BIOS_ERR, "%s(): USB3 SEC PHY PIPE clock XO config failed\n", __func__);
return -1;
}
/* Enable USB SEC clocks */
for (clk = USB_SEC_CFG_NOC_USB3_SEC_AXI_CBCR; clk < USB_SEC_CLK_COUNT; clk++) {
ret = usb_sec_clock_enable(clk);
if (ret) {
printk(BIOS_ERR, "Failed to enable USB SEC clock %d\n", clk);
return ret;
}
}
if (usb_clock_configure_mux(USB3_SEC_PHY_PIPE, USB_PHY_PIPE_SRC_SEL)) {
printk(BIOS_ERR, "%s(): USB3 SEC PHY PIPE clock PHY config failed\n", __func__);
return -1;
}
usb_update_refclk_for_core(1, true);
return ret;
}
@ -201,7 +260,7 @@ static void setup_dwc3(struct usb_dwc3 *dwc3)
To save power, enable the hardware-based clock gating (not relevant for PBL):
a. usb30_reg_cgctl[DBM_FSM_EN] = 0x1
*/
setbits32(USB3_MP_CGCTL_REG_ADDR, USB3_MP_DBM_FSM_EN_BIT);
setbits32(USB3_MP_CGCTL_REG_ADDR, USB3_CGCTL_DBM_FSM_EN_BIT);
//Disable clock gating: DWC_USB3_GCTL.DSBLCLKGTNG = 1
clrsetbits32(&dwc3->ctl, (DWC3_GCTL_SCALEDOWN_MASK |
DWC3_GCTL_DISSCRAMBLE),
@ -246,9 +305,151 @@ static void setup_dwc3(struct usb_dwc3 *dwc3)
}
/*
* setup_dwc3 - Configures the DWC3 USB controller.
* @dwc3: Pointer to the USB DWC3 configuration structure.
* Handles high-speed operation, power management, and sets to host mode.
*/
/* DWC3 Controller Configuration Structure */
struct dwc3_controller_config {
const char *name;
u32 *general_cfg_addr;
u32 *cgctl_reg_addr;
u32 *link_regs_lu3lfpsrxtim_addr;
u32 *gusb2phycfg_regs_addr;
u32 *portsc_20_regs_addr;
u32 *portsc_30_regs_addr;
u8 smb_slave_addr;
};
/*
* setup_dwc3_controller - Configure DWC3 USB controller
* @dwc3: Pointer to the USB DWC3 structure
* @config: Controller-specific configuration
* @hs_only: true for high-speed only mode, false for super-speed mode
*
* Handles both high-speed and super-speed operation, power management,
* and sets controller to host mode.
*/
static void setup_dwc3_controller(struct usb_dwc3 *dwc3,
const struct dwc3_controller_config *config,
bool hs_only)
{
u32 *reg = config->general_cfg_addr;
/* Configure speed mode */
if (hs_only) {
/* High-speed only mode configuration */
setbits32(reg, UTMI_CLK_DIS_0);
udelay(10);
setbits32(reg, UTMI_CLK_SEL_0);
setbits32(reg, PIPE3_PHYSTATUS_SW_0);
clrbits32(reg, PIPE3_SET_PHYSTATUS_SW_0);
udelay(10);
clrbits32(reg, UTMI_CLK_DIS_0);
} else {
/* Super-speed mode: core exits U1/U2/U3 only in PHY power state P1/P2/P3 */
clrsetbits32(&dwc3->usb3pipectl,
DWC3_GUSB3PIPECTL_DELAYP1TRANS,
DWC3_GUSB3PIPECTL_UX_EXIT_IN_PX);
}
/* Configure USB2 PHY interface: Select UTMI+ PHY with 8-bit interface */
clrsetbits32(&dwc3->usb2phycfg,
(DWC3_GUSB2PHYCFG_USB2TRDTIM_MASK |
DWC3_GUSB2PHYCFG_PHYIF_MASK |
DWC3_GUSB2PHYCFG_ENBLSLPM_MASK),
(DWC3_GUSB2PHYCFG_PHYIF(UTMI_PHYIF_8_BIT) |
DWC3_GUSB2PHYCFG_USBTRDTIM(USBTRDTIM_UTMI_8_BIT)));
/* Enable hardware-based clock gating */
setbits32(config->cgctl_reg_addr, USB3_CGCTL_DBM_FSM_EN_BIT);
/* Disable clock gating: DWC_USB3_GCTL.DSBLCLKGTNG = 1 */
clrsetbits32(&dwc3->ctl,
(DWC3_GCTL_SCALEDOWN_MASK | DWC3_GCTL_DISSCRAMBLE),
DWC3_GCTL_U2EXIT_LFPS | DWC3_GCTL_DSBLCLKGTNG);
/* Allow PHY to transition to P2 from suspend (P3) state */
setbits32(&dwc3->usb3pipectl,
(DWC3_GUSB3PIPECTL_P3EXSIGP2 |
DWC3_GUSB3PIPECTL_UX_EXIT_IN_PX));
/* Reduce U3 exit handshake timer to 300ns */
clrsetbits32(config->link_regs_lu3lfpsrxtim_addr,
LFPS_RSP_RX_CLK_CLR_MASK, LFPS_RSP_RX_CLK_SET_MASK);
/* Configure L1 exit and IP gap settings */
clrsetbits32(&dwc3->uctl1,
DWC3_GUCTL1_CLR_MASK,
DWC3_GUCTL1_SET_MASK);
/* Set GRXTHRCFG based on case 8000615753 values */
setbits32(&dwc3->rxthrcfg,
DWC3_GRXTHRCFG_USBMAXRXBURSTSIZE(3) |
DWC3_GRXTHRCFG_USBRXPKTCNT(3) |
DWC3_GRXTHRCFG_USBRXPKTCNTSEL);
/* Set the bus configuration 1K page pipe limit */
setbits32(&dwc3->sbuscfg1,
DWC3_GSBUSCFG1_PIPETRANSLIMIT(0xE) |
DWC3_GSBUSCFG1_EN1KPAGE);
/* Set Sparse Control Transaction Enable */
setbits32(&dwc3->uctl, DWC3_GUCTL_SPRSCTRLTRANSEN);
/* Disable sleep mode */
clrbits32(config->gusb2phycfg_regs_addr, GUSB2PHYCFG_ENBLSLPM_BIT);
/* Configure controller in Host mode */
clrsetbits32(&dwc3->ctl,
DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG),
DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_HOST));
printk(BIOS_SPEW, "Configure USB %s in Host mode\n", config->name);
/* Enable wake on connect/disconnect/overcurrent */
setbits32(config->portsc_20_regs_addr, USB3_PORTSC_WCE_BIT);
setbits32(config->portsc_30_regs_addr, USB3_PORTSC_WCE_BIT);
}
static const struct dwc3_controller_config prim_config = {
.name = "Primary",
.general_cfg_addr = (u32 *)USB4_SS_USB3_DRD_SP_0_USB31_PRIMGENERAL_CFG,
.cgctl_reg_addr = USB4_SS_USB3_DRD_SP_0_USB31_PRIMCGCTL_REG,
.link_regs_lu3lfpsrxtim_addr =
USB4_SS_USB3_DRD_SP_0_USB31_PRIMLINK_REGS_0_LU3LFPSRXTIM_ADDR,
.gusb2phycfg_regs_addr =
USB4_SS_USB3_DRD_SP_0_USB31_PRIMGUSB2PHYCFG_REGS_0_GUSB2PHYCFG_ADDR,
.portsc_20_regs_addr =
USB4_SS_USB3_DRD_SP_0_USB31_PRIMPORTSC_20_REGS_0_PORTSC_20_ADDR,
.portsc_30_regs_addr =
USB4_SS_USB3_DRD_SP_0_USB31_PRIMPORTSC_30_REGS_0_PORTSC_30_ADDR,
.smb_slave_addr = SMB1_SLAVE_ID,
};
static const struct dwc3_controller_config sec_config = {
.name = "Secondary",
.general_cfg_addr = (u32 *)USB4_SS_USB3_DRD_SP_1_USB31_SECGENERAL_CFG,
.cgctl_reg_addr = USB4_SS_USB3_DRD_SP_1_USB31_SECCGCTL_REG,
.link_regs_lu3lfpsrxtim_addr =
USB4_SS_USB3_DRD_SP_1_USB31_SECLINK_REGS_0_LU3LFPSRXTIM_ADDR,
.gusb2phycfg_regs_addr =
USB4_SS_USB3_DRD_SP_1_USB31_SECGUSB2PHYCFG_REGS_0_GUSB2PHYCFG_ADDR,
.portsc_20_regs_addr =
USB4_SS_USB3_DRD_SP_1_USB31_SECPORTSC_20_REGS_0_PORTSC_20_ADDR,
.portsc_30_regs_addr =
USB4_SS_USB3_DRD_SP_1_USB31_SECPORTSC_30_REGS_0_PORTSC_30_ADDR,
.smb_slave_addr = SMB2_SLAVE_ID,
};
/* Initialization of DWC3 Core and PHY */
static void setup_usb_host(struct usb_dwc3_cfg *dwc3)
{
bool high_speed_only_primary = false;
bool high_speed_only_secondary = false;
/* Call Clock Enable */
qcom_enable_usb_clk();
@ -311,7 +512,69 @@ static void setup_usb_host(struct usb_dwc3_cfg *dwc3)
if (!ret0 || !ret1)
hs_speed_only = true;
/* Type C port 0 - C0 */
clock_reset_bcr(dwc3->gcc_qusb2phy_prim_bcr, 1);
udelay(10);
clock_reset_bcr(dwc3->gcc_qusb2phy_prim_bcr, 0);
/* TBD:usb_shared_repeater_reset,usb_shared_repeater_init */
/* Initialize secondary HS PHY */
hs_usb_phy_init(2);
/* Reset USB4.0 DP0 PHY, USB3 PHY, and USB3PHY PHY PRIM BCRs */
clock_reset_bcr(dwc3->gcc_usb4_0_dp0_phy_prim_bcr, 1);
clock_reset_bcr(dwc3->gcc_usb3_phy_prim_bcr, 1);
clock_reset_bcr(dwc3->gcc_usb3phy_phy_prim_bcr, 1);
udelay(10);
clock_reset_bcr(dwc3->gcc_usb3phy_phy_prim_bcr, 0);
clock_reset_bcr(dwc3->gcc_usb3_phy_prim_bcr, 0);
clock_reset_bcr(dwc3->gcc_usb4_0_dp0_phy_prim_bcr, 0);
udelay(10);
/* Initialize USB4/USB3 EDP_DP_Con PHY Configuration */
int ss0_ret = qmp_usb4_dp_phy_ss_init(0);
if (ss0_ret != CB_SUCCESS) {
printk(BIOS_ERR, "SS0 QMP PHY initialization failed\n");
high_speed_only_primary = true;
}
/* Enable VBUS for C0 */
enable_vbus_ss(&prim_config);
udelay(50);
usb_typec_status_check(&prim_config);
/* Type C port 1 - C1 */
/* Reset USB secondary[C1] PHY BCRs */
clock_reset_bcr(dwc3->gcc_qusb2phy_sec_bcr, 1);
udelay(10);
clock_reset_bcr(dwc3->gcc_qusb2phy_sec_bcr, 0);
/* TBD:usb_shared_repeater_reset,usb_shared_repeater_init for secondary */
/* Initialize secondary HS PHY */
hs_usb_phy_init(3);
/* Reset USB4.1 DP0 PHY, USB3 PHY, and USB3PHY PHY SEC BCRs */
clock_reset_bcr(dwc3->gcc_usb4_1_dp0_phy_sec_bcr, 1);
clock_reset_bcr(dwc3->gcc_usb3_phy_sec_bcr, 1);
clock_reset_bcr(dwc3->gcc_usb3phy_phy_sec_bcr, 1);
udelay(10);
clock_reset_bcr(dwc3->gcc_usb3phy_phy_sec_bcr, 0);
clock_reset_bcr(dwc3->gcc_usb3_phy_sec_bcr, 0);
clock_reset_bcr(dwc3->gcc_usb4_1_dp0_phy_sec_bcr, 0);
udelay(10);
/* Initialize USB4/USB3 EDP_DP_Con PHY Configuration (secondary) */
int ss1_ret = qmp_usb4_dp_phy_ss_init(1);
if (ss1_ret != CB_SUCCESS) {
printk(BIOS_ERR, "SS1 QMP PHY initialization failed\n");
high_speed_only_secondary = true;
}
/* Enable VBUS for C1 */
enable_vbus_ss(&sec_config);
udelay(50);
usb_typec_status_check(&sec_config);
/* Initialize USB Controller for Type A */
setup_dwc3(dwc3->usb_host_dwc3);
/* Initialize USB Controller for Type C port 0(C0) */
setup_dwc3_controller(dwc3->usb_host_dwc3_prim, &prim_config, high_speed_only_primary);
/* Initialize USB Controller for Type C port 1(C1) */
setup_dwc3_controller(dwc3->usb_host_dwc3_sec, &sec_config, high_speed_only_secondary);
printk(BIOS_INFO, "DWC3 and PHY setup finished\n");
}
@ -322,6 +585,119 @@ static void setup_usb_host(struct usb_dwc3_cfg *dwc3)
*/
void setup_usb_host0(void)
{
printk(BIOS_INFO, "Setting up USB HOST0 controller.\n");
setup_usb_host(&usb_port0);
printk(BIOS_INFO, "Setting up USB HOST controller.\n");
setup_usb_host(&usb_ports);
}
/*
* usb_update_refclk_for_core - Updates USB reference clock for specified core
* @core_num: USB core number (0-4)
* @enable: true to enable, false to disable reference clock
*/
void usb_update_refclk_for_core(u32 core_num, bool enable)
{
u32 value = enable ? USB_CLKREF_ENABLE_VALUE : 0;
switch (core_num) {
case 1:
clrsetbits32(TCSR_GCC_USB4_1_CLKREF_EN_ADDR, 0x1, value);
clrsetbits32(TCSR_GCC_USB2_1_CLKREF_EN_ADDR, 0x1, value);
break;
case 2:
clrsetbits32(TCSR_GCC_USB4_2_CLKREF_EN_ADDR, 0x1, value);
clrsetbits32(TCSR_GCC_USB2_2_CLKREF_EN_ADDR, 0x1, value);
break;
case 3:
clrsetbits32(TCSR_GCC_USB3_MP0_CLKREF_EN_ADDR, 0x1, value);
clrsetbits32(TCSR_GCC_USB3_MP1_CLKREF_EN_ADDR, 0x1, value);
break;
default:
/* No clkref */
break;
}
}
/*
* wait_for_otg_enabled - Waits for OTG block to reach enabled state
* @status_reg_addr: SPMI address of the OTG status register
* @core_name: Name of the core for logging (e.g., "SMB1", "SMB2")
* @return: true if OTG enabled successfully, false if timeout or error
*/
static bool wait_for_otg_enabled(u32 status_reg_addr, const char *core_name)
{
u32 timeout_ms = 0;
u8 otg_status;
u8 otg_state;
while (timeout_ms < OTG_STATUS_TIMEOUT_MS) {
otg_status = spmi_read8(status_reg_addr);
otg_state = otg_status & OTG_STATE_MASK;
printk(BIOS_DEBUG, "%s OTG Status: 0x%02x, State: 0x%02x\n",
core_name, otg_status, otg_state);
switch (otg_state) {
case OTG_STATE_ENABLED:
printk(BIOS_INFO, "%s OTG block enabled successfully\n", core_name);
return true;
case OTG_STATE_ERROR:
printk(BIOS_ERR, "%s OTG block in ERROR state (0x%02x)\n",
core_name, otg_status);
return false;
case OTG_STATE_DISABLED:
case OTG_STATE_ENABLING:
case OTG_STATE_DISABLING:
/* Continue polling */
break;
default:
printk(BIOS_WARNING, "%s OTG block in unknown state: 0x%02x\n",
core_name, otg_state);
break;
}
mdelay(OTG_STATUS_POLL_INTERVAL_MS);
timeout_ms += OTG_STATUS_POLL_INTERVAL_MS;
}
printk(BIOS_ERR, "%s OTG enable timeout after %d ms, final state: 0x%02x\n",
core_name, timeout_ms, otg_state);
return false;
}
/*
* enable_vbus_ss - Enables VBUS SuperSpeed for specified USB core
* @config: Controller configuration containing SMB slave address
*/
void enable_vbus_ss(const struct dwc3_controller_config *config)
{
u8 slave = config->smb_slave_addr;
printk(BIOS_INFO, "Enabling %s VBUS SuperSpeed\n", config->name);
spmi_write8(SPMI_ADDR(slave, SCHG_DCDC_OTG_CFG), 0x20);
spmi_write8(SPMI_ADDR(slave, SCHG_DCDC_CMD_OTG), 0x1);
/* Wait for OTG block to reach enabled state */
if (!wait_for_otg_enabled(SPMI_ADDR(slave, SCHG_DCDC_OTG_STATUS), config->name))
printk(BIOS_ERR, "%s OTG enable failed\n", config->name);
}
/*
* usb_typec_status_check - Reads comprehensive Type-C status from PMIC
* @config: Controller configuration containing SMB slave address
*/
void usb_typec_status_check(const struct dwc3_controller_config *config)
{
u8 slave = config->smb_slave_addr;
u8 misc_status, src_status, mode_cfg;
misc_status = spmi_read8(SPMI_ADDR(slave, SCHG_TYPE_C_TYPE_C_MISC_STATUS));
src_status = spmi_read8(SPMI_ADDR(slave, SCHG_TYPE_C_TYPE_C_SRC_STATUS));
mode_cfg = spmi_read8(SPMI_ADDR(slave, SCHG_TYPE_C_TYPE_C_MODE_CFG));
printk(BIOS_INFO, "%s Type-C Status:\n", config->name);
printk(BIOS_INFO, " Misc Status (0x2B0B): 0x%02x, VBUS Status (bit 5): %d\n",
misc_status, (misc_status & TYPEC_VBUS_STATUS_MASK) ? 1 : 0);
printk(BIOS_INFO, " Src Status (0x2B08): 0x%02x\n", src_status);
printk(BIOS_INFO, " Mode Config (0x2B44): 0x%02x\n", mode_cfg);
}