diff --git a/src/soc/qualcomm/qcs405/Makefile.inc b/src/soc/qualcomm/qcs405/Makefile.inc index 89a7354557..4df5246e3c 100644 --- a/src/soc/qualcomm/qcs405/Makefile.inc +++ b/src/soc/qualcomm/qcs405/Makefile.inc @@ -21,6 +21,7 @@ romstage-y += spi.c romstage-y += cbmem.c romstage-y += gpio.c romstage-y += clock.c +romstage-y += usb.c ################################################################################ ramstage-y += soc.c @@ -29,6 +30,7 @@ ramstage-y += spi.c ramstage-y += cbmem.c ramstage-y += gpio.c ramstage-y += clock.c +ramstage-y += usb.c ################################################################################ diff --git a/src/soc/qualcomm/qcs405/include/soc/clock.h b/src/soc/qualcomm/qcs405/include/soc/clock.h index 2766f0975b..d0f307b308 100644 --- a/src/soc/qualcomm/qcs405/include/soc/clock.h +++ b/src/soc/qualcomm/qcs405/include/soc/clock.h @@ -26,10 +26,10 @@ /** * USB BCR registers */ -#define GCC_USB_HS_PHY_CFG_AHB_BCR 0x1841038 +#define GCC_USB_HS_PHY_CFG_AHB_BCR 0x180000C #define GCC_USB_HS_BCR 0x1841000 #define GCC_USB_30_BCR 0x1839000 -#define GCC_USB2A_PHY_BCR 0x1841028 +#define GCC_USB2A_PHY_BCR 0x180000C #define GCC_USB2_HS_PHY_ONLY_BCR 0x1841034 #define GCC_QUSB2_PHY_BCR 0x184103C diff --git a/src/soc/qualcomm/qcs405/include/soc/usb.h b/src/soc/qualcomm/qcs405/include/soc/usb.h new file mode 100644 index 0000000000..121be4dfe8 --- /dev/null +++ b/src/soc/qualcomm/qcs405/include/soc/usb.h @@ -0,0 +1,78 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (c) 2018 Qualcomm Technologies + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include + +#ifndef _QCS405_USB_H_ +#define _QCS405_USB_H_ + +/* QSCRATCH_GENERAL_CFG register bit offset */ +#define PIPE_UTMI_CLK_SEL BIT(0) +#define PIPE3_PHYSTATUS_SW BIT(3) +#define PIPE_UTMI_CLK_DIS BIT(8) + +/* Global USB3 Control Registers */ +#define DWC3_GUSB3PIPECTL_DELAYP1TRANS BIT(18) +#define DWC3_GUSB3PIPECTL_UX_EXIT_IN_PX BIT(27) +#define DWC3_GCTL_PRTCAPDIR(n) ((n) << 12) +#define DWC3_GCTL_PRTCAP_OTG 3 +#define DWC3_GCTL_PRTCAP_HOST 1 + +/* Global USB2 PHY Configuration Register */ +#define DWC3_GUSB2PHYCFG_USBTRDTIM(n) ((n) << 10) +#define DWC3_GUSB2PHYCFG_USB2TRDTIM_MASK DWC3_GUSB2PHYCFG_USBTRDTIM(0xf) +#define DWC3_GUSB2PHYCFG_PHYIF(n) ((n) << 3) +#define DWC3_GUSB2PHYCFG_PHYIF_MASK DWC3_GUSB2PHYCFG_PHYIF(1) +#define USBTRDTIM_UTMI_8_BIT 9 +#define UTMI_PHYIF_8_BIT 0 + +#define DWC3_GCTL_SCALEDOWN(n) ((n) << 4) +#define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3) +#define DWC3_GCTL_DISSCRAMBLE (1 << 3) +#define DWC3_GCTL_U2EXIT_LFPS (1 << 2) +#define DWC3_GCTL_DSBLCLKGTNG (1 << 0) + +/* USB2 PHY register values */ +#define USB2PHY_TCSR_CTRL 0x01 +#define USB2PHY_REFCLK_CTRL 0x0d +#define USB2PHY_UTMI_CTRL5 0x12 +#define USB2PHY_PARAMETER_OVERRIDE_X0 0x63 +#define USB2PHY_PARAMETER_OVERRIDE_X1 0x03 +#define USB2PHY_PARAMETER_OVERRIDE_X2 0x1d +#define USB2PHY_PARAMETER_OVERRIDE_X3 0x03 +#define USB2PHY_HS_PHY_CTRL1 0x23 +#define QUSB2PHY_HS_PHY_CTRL_COMMON0 0x08 +#define QUSB2PHY_HS_PHY_CTRL_COMMON1 0xdc +#define USB2PHY_HS_PHY_CTRL2 0xe0 +#define USB2PHY_UTMI_CTRL5_POR_CLEAR 0x10 +#define USB2PHY_HS_PHY_CTRL2_SUSPEND_N_SEL 0x60 + +struct usb_board_data { + /* Register values going to override from the boardfile */ + u8 parameter_override_x0; + u8 parameter_override_x1; + u8 parameter_override_x2; + u8 parameter_override_x3; +}; + +enum usb_port { + HSUSB_SS_PORT_0, + HSUSB_HS_PORT_1, +}; + +void setup_usb_host(enum usb_port port, struct usb_board_data *data); +/* Call reset_ before setup_ */ +void reset_usb(enum usb_port port); + +#endif /* _QCS405_USB_H_ */ diff --git a/src/soc/qualcomm/qcs405/usb.c b/src/soc/qualcomm/qcs405/usb.c new file mode 100644 index 0000000000..b727248952 --- /dev/null +++ b/src/soc/qualcomm/qcs405/usb.c @@ -0,0 +1,269 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (c) 2018 Qualcomm Technologies + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* USB BASE ADDRESS */ +#define USB_HOST0_DWC3_BASE 0x758C100 +#define USB3_USB30_QSCRATCH_BASE 0x7678800 +#define USB2_FEMTO_PHY_PRI_BASE 0x007A000 +#define USB_HOST1_DWC3_BASE 0x78CC100 +#define USB2_USB30_QSCRATCH_BASE 0x79B8800 +#define USB2_FEMTO_PHY_SEC_BASE 0x007C000 + +struct usb_qscratch { + u8 rsvd0[8]; + u32 *qscratch_cfg_reg; + +}; +check_member(usb_qscratch, qscratch_cfg_reg, 0x08); + +struct usb_usb2_phy_dig { + u8 rsvd1[116]; + u32 utmi_ctrl5; + u32 ctrl_common0; + u32 ctrl_common1; + u8 rsvd2[12]; + u32 phy_ctrl1; + u32 phy_ctrl2; + u8 rsvd3; + u32 override_x0; + u32 override_x1; + u32 override_x2; + u32 override_x3; + u8 rsvd4[24]; + u32 tcsr_ctrl; + u8 rsvd5[36]; + u32 refclk_ctrl; +}; +check_member(usb_usb2_phy_dig, utmi_ctrl5, 0x74); +check_member(usb_usb2_phy_dig, phy_ctrl1, 0x8C); +check_member(usb_usb2_phy_dig, override_x0, 0x98); +check_member(usb_usb2_phy_dig, tcsr_ctrl, 0xC0); +check_member(usb_usb2_phy_dig, refclk_ctrl, 0xE8); + +struct usb_dwc3 { + u32 sbuscfg0; + u32 sbuscfg1; + u32 txthrcfg; + u32 rxthrcfg; + u32 ctl; + u32 pmsts; + u32 sts; + u32 uctl1; + u32 snpsid; + u32 gpio; + u32 uid; + u32 uctl; + u64 buserraddr; + u64 prtbimap; + u8 reserved1[32]; + u32 dbgfifospace; + u32 dbgltssm; + u32 dbglnmcc; + u32 dbgbmu; + u32 dbglspmux; + u32 dbglsp; + u32 dbgepinfo0; + u32 dbgepinfo1; + u64 prtbimap_hs; + u64 prtbimap_fs; + u8 reserved2[112]; + u32 usb2phycfg; + u8 reserved3[60]; + u32 usb2i2cctl; + u8 reserved4[60]; + u32 usb2phyacc; + u8 reserved5[60]; + u32 usb3pipectl; + u8 reserved6[60]; +}; +check_member(usb_dwc3, usb3pipectl, 0x1c0); + +struct usb_dwc3_cfg { + struct usb_dwc3 *usb_host_dwc3; + struct usb_usb2_phy_dig *usb2_phy_dig; + struct usb_qscratch *usb_qscratch_reg; + u32 *usb2_phy_bcr; + u32 *usb2_phy_por_bcr; + u32 *usb3_bcr; + struct usb_board_data *board_data; +}; + +static struct usb_dwc3_cfg usb_host_base[2] = { + [HSUSB_SS_PORT_0] = { + .usb_host_dwc3 = (void *)USB_HOST0_DWC3_BASE, + .usb2_phy_dig = (void *)USB2_FEMTO_PHY_PRI_BASE, + .usb2_phy_bcr = (void *)GCC_USB_HS_PHY_CFG_AHB_BCR, + .usb2_phy_por_bcr = (void *)GCC_USB2A_PHY_BCR, + .usb3_bcr = (void *)GCC_USB_30_BCR, + .usb_qscratch_reg = (void *)USB3_USB30_QSCRATCH_BASE, + }, + [HSUSB_HS_PORT_1] = { + .usb_host_dwc3 = (void *)USB_HOST1_DWC3_BASE, + .usb2_phy_dig = (void *)USB2_FEMTO_PHY_SEC_BASE, + .usb2_phy_bcr = (void *)GCC_QUSB2_PHY_BCR, + .usb2_phy_por_bcr = (void *)GCC_USB2_HS_PHY_ONLY_BCR, + .usb3_bcr = (void *)GCC_USB_HS_BCR, + .usb_qscratch_reg = (void *)USB2_USB30_QSCRATCH_BASE, + }, +}; + +void reset_usb(enum usb_port port) +{ + struct usb_dwc3_cfg *dwc3 = &usb_host_base[port]; + + /* Put Core in Reset */ + printk(BIOS_INFO, "Starting DWC3 reset for USB%d\n", port); + + /* Assert Core reset */ + clock_reset_bcr(dwc3->usb3_bcr, 1); +} + +static void usb2_phy_override_phy_params(struct usb_dwc3_cfg *dwc3) +{ + /* Override disconnect & squelch threshold values */ + write8(&dwc3->usb2_phy_dig->override_x0, + dwc3->board_data->parameter_override_x0); + + /* Override HS transmitter Pre-emphasis values */ + write8(&dwc3->usb2_phy_dig->override_x1, + dwc3->board_data->parameter_override_x1); + + /* Override HS transmitter Rise/Fall time values */ + write8(&dwc3->usb2_phy_dig->override_x2, + dwc3->board_data->parameter_override_x2); + + /* Override FS/LS Source impedance values */ + write8(&dwc3->usb2_phy_dig->override_x3, + dwc3->board_data->parameter_override_x3); +} + +static void hs_usb_phy_init(struct usb_dwc3_cfg *dwc3) +{ + write8(&dwc3->usb2_phy_dig->tcsr_ctrl, USB2PHY_TCSR_CTRL); + write8(&dwc3->usb2_phy_dig->refclk_ctrl, USB2PHY_REFCLK_CTRL); + write8(&dwc3->usb2_phy_dig->utmi_ctrl5, USB2PHY_UTMI_CTRL5); + write8(&dwc3->usb2_phy_dig->override_x0, USB2PHY_PARAMETER_OVERRIDE_X0); + write8(&dwc3->usb2_phy_dig->override_x1, USB2PHY_PARAMETER_OVERRIDE_X1); + write8(&dwc3->usb2_phy_dig->override_x2, USB2PHY_PARAMETER_OVERRIDE_X2); + write8(&dwc3->usb2_phy_dig->override_x3, USB2PHY_PARAMETER_OVERRIDE_X3); + + if (dwc3->board_data) + /* Override board specific PHY tuning values */ + usb2_phy_override_phy_params(dwc3); + + write8(&dwc3->usb2_phy_dig->phy_ctrl1, USB2PHY_HS_PHY_CTRL1); + write8(&dwc3->usb2_phy_dig->ctrl_common0, QUSB2PHY_HS_PHY_CTRL_COMMON0); + write8(&dwc3->usb2_phy_dig->ctrl_common1, QUSB2PHY_HS_PHY_CTRL_COMMON1); + write8(&dwc3->usb2_phy_dig->phy_ctrl2, USB2PHY_HS_PHY_CTRL2); + udelay(20); + write8(&dwc3->usb2_phy_dig->utmi_ctrl5, USB2PHY_UTMI_CTRL5_POR_CLEAR); + write8(&dwc3->usb2_phy_dig->phy_ctrl2, + USB2PHY_HS_PHY_CTRL2_SUSPEND_N_SEL); +} + +static void setup_dwc3(struct usb_dwc3 *dwc3) +{ + /* core exits U1/U2/U3 only in PHY power state P1/P2/P3 respectively */ + clrsetbits_le32(&dwc3->usb3pipectl, + DWC3_GUSB3PIPECTL_DELAYP1TRANS, + DWC3_GUSB3PIPECTL_UX_EXIT_IN_PX); + + clrsetbits_le32(&dwc3->ctl, (DWC3_GCTL_SCALEDOWN_MASK | + DWC3_GCTL_DISSCRAMBLE), + DWC3_GCTL_U2EXIT_LFPS | DWC3_GCTL_DSBLCLKGTNG); + + /* configure controller in Host mode */ + clrsetbits_le32(&dwc3->ctl, (DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG)), + DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_HOST)); + printk(BIOS_INFO, "Configure USB in Host mode\n"); +} + +/* Initialization of DWC3 Core and PHY */ +void setup_usb_host(enum usb_port port, struct usb_board_data *board_data) +{ + struct usb_dwc3_cfg *dwc3 = &usb_host_base[port]; + u32 val; + + printk(BIOS_INFO, "Setting up USB HOST%d controller.\n", port); + + dwc3->board_data = board_data; + + /* Clear core reset. */ + clock_reset_bcr(dwc3->usb3_bcr, 0); + + + if (port == HSUSB_SS_PORT_0) { + /* Set PHY reset. */ + setbits_le32(&dwc3->usb2_phy_bcr, BIT(1)); + udelay(15); + /* Clear PHY reset. */ + clrbits_le32(&dwc3->usb2_phy_bcr, BIT(1)); + } else { + clock_reset_bcr(dwc3->usb2_phy_bcr, 1); + udelay(15); + clock_reset_bcr(dwc3->usb2_phy_bcr, 0); + } + udelay(100); + + /* Initialize PHYs */ + hs_usb_phy_init(dwc3); + + if (port == HSUSB_SS_PORT_0) { + /* Set PHY POR reset. */ + setbits_le32(&dwc3->usb2_phy_por_bcr, BIT(0)); + val = read8(&dwc3->usb2_phy_dig->ctrl_common0); + val &= ~(0x4); + write8(&dwc3->usb2_phy_dig->ctrl_common0, val); + udelay(20); + /* Clear PHY POR reset. */ + clrbits_le32(&dwc3->usb2_phy_por_bcr, BIT(0)); + } else { + clock_reset_bcr(dwc3->usb2_phy_por_bcr, 1); + val = read8(&dwc3->usb2_phy_dig->ctrl_common0); + val &= ~(0x4); + write8(&dwc3->usb2_phy_dig->ctrl_common0, val); + udelay(20); + clock_reset_bcr(dwc3->usb2_phy_por_bcr, 0); + } + udelay(100); + + setup_dwc3(dwc3->usb_host_dwc3); + + /* + * Below sequence is used when dwc3 operates without + * SSPHY and only HS/FS/LS modes are supported. + */ + + /* Configure dwc3 to use UTMI clock as PIPE clock not present */ + setbits_le32(&dwc3->usb_qscratch_reg->qscratch_cfg_reg, + PIPE_UTMI_CLK_DIS); + udelay(2); + setbits_le32(&dwc3->usb_qscratch_reg->qscratch_cfg_reg, + PIPE_UTMI_CLK_SEL | PIPE3_PHYSTATUS_SW); + udelay(3); + clrbits_le32(&dwc3->usb_qscratch_reg->qscratch_cfg_reg, + PIPE_UTMI_CLK_DIS); + + printk(BIOS_INFO, "DWC3 and PHY setup finished\n"); +}