diff options
Diffstat (limited to 'drivers/usb/dwc2/core.c')
-rw-r--r-- | drivers/usb/dwc2/core.c | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index 55d5ae2a7ec7..01ac4a064feb 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c | |||
@@ -1020,6 +1020,196 @@ int dwc2_hsotg_wait_bit_clear(struct dwc2_hsotg *hsotg, u32 offset, u32 mask, | |||
1020 | return -ETIMEDOUT; | 1020 | return -ETIMEDOUT; |
1021 | } | 1021 | } |
1022 | 1022 | ||
1023 | /* | ||
1024 | * Initializes the FSLSPClkSel field of the HCFG register depending on the | ||
1025 | * PHY type | ||
1026 | */ | ||
1027 | void dwc2_init_fs_ls_pclk_sel(struct dwc2_hsotg *hsotg) | ||
1028 | { | ||
1029 | u32 hcfg, val; | ||
1030 | |||
1031 | if ((hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI && | ||
1032 | hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED && | ||
1033 | hsotg->params.ulpi_fs_ls) || | ||
1034 | hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) { | ||
1035 | /* Full speed PHY */ | ||
1036 | val = HCFG_FSLSPCLKSEL_48_MHZ; | ||
1037 | } else { | ||
1038 | /* High speed PHY running at full speed or high speed */ | ||
1039 | val = HCFG_FSLSPCLKSEL_30_60_MHZ; | ||
1040 | } | ||
1041 | |||
1042 | dev_dbg(hsotg->dev, "Initializing HCFG.FSLSPClkSel to %08x\n", val); | ||
1043 | hcfg = dwc2_readl(hsotg, HCFG); | ||
1044 | hcfg &= ~HCFG_FSLSPCLKSEL_MASK; | ||
1045 | hcfg |= val << HCFG_FSLSPCLKSEL_SHIFT; | ||
1046 | dwc2_writel(hsotg, hcfg, HCFG); | ||
1047 | } | ||
1048 | |||
1049 | static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) | ||
1050 | { | ||
1051 | u32 usbcfg, ggpio, i2cctl; | ||
1052 | int retval = 0; | ||
1053 | |||
1054 | /* | ||
1055 | * core_init() is now called on every switch so only call the | ||
1056 | * following for the first time through | ||
1057 | */ | ||
1058 | if (select_phy) { | ||
1059 | dev_dbg(hsotg->dev, "FS PHY selected\n"); | ||
1060 | |||
1061 | usbcfg = dwc2_readl(hsotg, GUSBCFG); | ||
1062 | if (!(usbcfg & GUSBCFG_PHYSEL)) { | ||
1063 | usbcfg |= GUSBCFG_PHYSEL; | ||
1064 | dwc2_writel(hsotg, usbcfg, GUSBCFG); | ||
1065 | |||
1066 | /* Reset after a PHY select */ | ||
1067 | retval = dwc2_core_reset(hsotg, false); | ||
1068 | |||
1069 | if (retval) { | ||
1070 | dev_err(hsotg->dev, | ||
1071 | "%s: Reset failed, aborting", __func__); | ||
1072 | return retval; | ||
1073 | } | ||
1074 | } | ||
1075 | |||
1076 | if (hsotg->params.activate_stm_fs_transceiver) { | ||
1077 | ggpio = dwc2_readl(hsotg, GGPIO); | ||
1078 | if (!(ggpio & GGPIO_STM32_OTG_GCCFG_PWRDWN)) { | ||
1079 | dev_dbg(hsotg->dev, "Activating transceiver\n"); | ||
1080 | /* | ||
1081 | * STM32F4x9 uses the GGPIO register as general | ||
1082 | * core configuration register. | ||
1083 | */ | ||
1084 | ggpio |= GGPIO_STM32_OTG_GCCFG_PWRDWN; | ||
1085 | dwc2_writel(hsotg, ggpio, GGPIO); | ||
1086 | } | ||
1087 | } | ||
1088 | } | ||
1089 | |||
1090 | /* | ||
1091 | * Program DCFG.DevSpd or HCFG.FSLSPclkSel to 48Mhz in FS. Also | ||
1092 | * do this on HNP Dev/Host mode switches (done in dev_init and | ||
1093 | * host_init). | ||
1094 | */ | ||
1095 | if (dwc2_is_host_mode(hsotg)) | ||
1096 | dwc2_init_fs_ls_pclk_sel(hsotg); | ||
1097 | |||
1098 | if (hsotg->params.i2c_enable) { | ||
1099 | dev_dbg(hsotg->dev, "FS PHY enabling I2C\n"); | ||
1100 | |||
1101 | /* Program GUSBCFG.OtgUtmiFsSel to I2C */ | ||
1102 | usbcfg = dwc2_readl(hsotg, GUSBCFG); | ||
1103 | usbcfg |= GUSBCFG_OTG_UTMI_FS_SEL; | ||
1104 | dwc2_writel(hsotg, usbcfg, GUSBCFG); | ||
1105 | |||
1106 | /* Program GI2CCTL.I2CEn */ | ||
1107 | i2cctl = dwc2_readl(hsotg, GI2CCTL); | ||
1108 | i2cctl &= ~GI2CCTL_I2CDEVADDR_MASK; | ||
1109 | i2cctl |= 1 << GI2CCTL_I2CDEVADDR_SHIFT; | ||
1110 | i2cctl &= ~GI2CCTL_I2CEN; | ||
1111 | dwc2_writel(hsotg, i2cctl, GI2CCTL); | ||
1112 | i2cctl |= GI2CCTL_I2CEN; | ||
1113 | dwc2_writel(hsotg, i2cctl, GI2CCTL); | ||
1114 | } | ||
1115 | |||
1116 | return retval; | ||
1117 | } | ||
1118 | |||
1119 | static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) | ||
1120 | { | ||
1121 | u32 usbcfg, usbcfg_old; | ||
1122 | int retval = 0; | ||
1123 | |||
1124 | if (!select_phy) | ||
1125 | return 0; | ||
1126 | |||
1127 | usbcfg = dwc2_readl(hsotg, GUSBCFG); | ||
1128 | usbcfg_old = usbcfg; | ||
1129 | |||
1130 | /* | ||
1131 | * HS PHY parameters. These parameters are preserved during soft reset | ||
1132 | * so only program the first time. Do a soft reset immediately after | ||
1133 | * setting phyif. | ||
1134 | */ | ||
1135 | switch (hsotg->params.phy_type) { | ||
1136 | case DWC2_PHY_TYPE_PARAM_ULPI: | ||
1137 | /* ULPI interface */ | ||
1138 | dev_dbg(hsotg->dev, "HS ULPI PHY selected\n"); | ||
1139 | usbcfg |= GUSBCFG_ULPI_UTMI_SEL; | ||
1140 | usbcfg &= ~(GUSBCFG_PHYIF16 | GUSBCFG_DDRSEL); | ||
1141 | if (hsotg->params.phy_ulpi_ddr) | ||
1142 | usbcfg |= GUSBCFG_DDRSEL; | ||
1143 | |||
1144 | /* Set external VBUS indicator as needed. */ | ||
1145 | if (hsotg->params.oc_disable) | ||
1146 | usbcfg |= (GUSBCFG_ULPI_INT_VBUS_IND | | ||
1147 | GUSBCFG_INDICATORPASSTHROUGH); | ||
1148 | break; | ||
1149 | case DWC2_PHY_TYPE_PARAM_UTMI: | ||
1150 | /* UTMI+ interface */ | ||
1151 | dev_dbg(hsotg->dev, "HS UTMI+ PHY selected\n"); | ||
1152 | usbcfg &= ~(GUSBCFG_ULPI_UTMI_SEL | GUSBCFG_PHYIF16); | ||
1153 | if (hsotg->params.phy_utmi_width == 16) | ||
1154 | usbcfg |= GUSBCFG_PHYIF16; | ||
1155 | break; | ||
1156 | default: | ||
1157 | dev_err(hsotg->dev, "FS PHY selected at HS!\n"); | ||
1158 | break; | ||
1159 | } | ||
1160 | |||
1161 | if (usbcfg != usbcfg_old) { | ||
1162 | dwc2_writel(hsotg, usbcfg, GUSBCFG); | ||
1163 | |||
1164 | /* Reset after setting the PHY parameters */ | ||
1165 | retval = dwc2_core_reset(hsotg, false); | ||
1166 | if (retval) { | ||
1167 | dev_err(hsotg->dev, | ||
1168 | "%s: Reset failed, aborting", __func__); | ||
1169 | return retval; | ||
1170 | } | ||
1171 | } | ||
1172 | |||
1173 | return retval; | ||
1174 | } | ||
1175 | |||
1176 | int dwc2_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) | ||
1177 | { | ||
1178 | u32 usbcfg; | ||
1179 | int retval = 0; | ||
1180 | |||
1181 | if ((hsotg->params.speed == DWC2_SPEED_PARAM_FULL || | ||
1182 | hsotg->params.speed == DWC2_SPEED_PARAM_LOW) && | ||
1183 | hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) { | ||
1184 | /* If FS/LS mode with FS/LS PHY */ | ||
1185 | retval = dwc2_fs_phy_init(hsotg, select_phy); | ||
1186 | if (retval) | ||
1187 | return retval; | ||
1188 | } else { | ||
1189 | /* High speed PHY */ | ||
1190 | retval = dwc2_hs_phy_init(hsotg, select_phy); | ||
1191 | if (retval) | ||
1192 | return retval; | ||
1193 | } | ||
1194 | |||
1195 | if (hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI && | ||
1196 | hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED && | ||
1197 | hsotg->params.ulpi_fs_ls) { | ||
1198 | dev_dbg(hsotg->dev, "Setting ULPI FSLS\n"); | ||
1199 | usbcfg = dwc2_readl(hsotg, GUSBCFG); | ||
1200 | usbcfg |= GUSBCFG_ULPI_FS_LS; | ||
1201 | usbcfg |= GUSBCFG_ULPI_CLK_SUSP_M; | ||
1202 | dwc2_writel(hsotg, usbcfg, GUSBCFG); | ||
1203 | } else { | ||
1204 | usbcfg = dwc2_readl(hsotg, GUSBCFG); | ||
1205 | usbcfg &= ~GUSBCFG_ULPI_FS_LS; | ||
1206 | usbcfg &= ~GUSBCFG_ULPI_CLK_SUSP_M; | ||
1207 | dwc2_writel(hsotg, usbcfg, GUSBCFG); | ||
1208 | } | ||
1209 | |||
1210 | return retval; | ||
1211 | } | ||
1212 | |||
1023 | MODULE_DESCRIPTION("DESIGNWARE HS OTG Core"); | 1213 | MODULE_DESCRIPTION("DESIGNWARE HS OTG Core"); |
1024 | MODULE_AUTHOR("Synopsys, Inc."); | 1214 | MODULE_AUTHOR("Synopsys, Inc."); |
1025 | MODULE_LICENSE("Dual BSD/GPL"); | 1215 | MODULE_LICENSE("Dual BSD/GPL"); |