diff --git a/src/soc/qualcomm/common/include/soc/qcom_spmi.h b/src/soc/qualcomm/common/include/soc/qcom_spmi.h index 1c0ae6121f..533486cb84 100644 --- a/src/soc/qualcomm/common/include/soc/qcom_spmi.h +++ b/src/soc/qualcomm/common/include/soc/qcom_spmi.h @@ -6,6 +6,9 @@ #include #include +/* 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); diff --git a/src/soc/qualcomm/x1p42100/include/soc/addressmap.h b/src/soc/qualcomm/x1p42100/include/soc/addressmap.h index 8643f49b9b..90c85bff59 100644 --- a/src/soc/qualcomm/x1p42100/include/soc/addressmap.h +++ b/src/soc/qualcomm/x1p42100/include/soc/addressmap.h @@ -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 diff --git a/src/soc/qualcomm/x1p42100/include/soc/usb/usb.h b/src/soc/qualcomm/x1p42100/include/soc/usb/usb.h index 4b62392d2f..360e319939 100644 --- a/src/soc/qualcomm/x1p42100/include/soc/usb/usb.h +++ b/src/soc/qualcomm/x1p42100/include/soc/usb/usb.h @@ -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); diff --git a/src/soc/qualcomm/x1p42100/usb/usb.c b/src/soc/qualcomm/x1p42100/usb/usb.c index c969693c4f..5916bcef81 100644 --- a/src/soc/qualcomm/x1p42100/usb/usb.c +++ b/src/soc/qualcomm/x1p42100/usb/usb.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -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); }