diff options
Diffstat (limited to 'drivers/usb/dwc2/core.c')
-rw-r--r-- | drivers/usb/dwc2/core.c | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index 55d5ae2a7ec7..8b499d643461 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c | |||
@@ -1020,6 +1020,205 @@ 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 | |||
1156 | /* Set turnaround time */ | ||
1157 | if (dwc2_is_device_mode(hsotg)) { | ||
1158 | usbcfg &= ~GUSBCFG_USBTRDTIM_MASK; | ||
1159 | if (hsotg->params.phy_utmi_width == 16) | ||
1160 | usbcfg |= 5 << GUSBCFG_USBTRDTIM_SHIFT; | ||
1161 | else | ||
1162 | usbcfg |= 9 << GUSBCFG_USBTRDTIM_SHIFT; | ||
1163 | } | ||
1164 | break; | ||
1165 | default: | ||
1166 | dev_err(hsotg->dev, "FS PHY selected at HS!\n"); | ||
1167 | break; | ||
1168 | } | ||
1169 | |||
1170 | if (usbcfg != usbcfg_old) { | ||
1171 | dwc2_writel(hsotg, usbcfg, GUSBCFG); | ||
1172 | |||
1173 | /* Reset after setting the PHY parameters */ | ||
1174 | retval = dwc2_core_reset(hsotg, false); | ||
1175 | if (retval) { | ||
1176 | dev_err(hsotg->dev, | ||
1177 | "%s: Reset failed, aborting", __func__); | ||
1178 | return retval; | ||
1179 | } | ||
1180 | } | ||
1181 | |||
1182 | return retval; | ||
1183 | } | ||
1184 | |||
1185 | int dwc2_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) | ||
1186 | { | ||
1187 | u32 usbcfg; | ||
1188 | int retval = 0; | ||
1189 | |||
1190 | if ((hsotg->params.speed == DWC2_SPEED_PARAM_FULL || | ||
1191 | hsotg->params.speed == DWC2_SPEED_PARAM_LOW) && | ||
1192 | hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) { | ||
1193 | /* If FS/LS mode with FS/LS PHY */ | ||
1194 | retval = dwc2_fs_phy_init(hsotg, select_phy); | ||
1195 | if (retval) | ||
1196 | return retval; | ||
1197 | } else { | ||
1198 | /* High speed PHY */ | ||
1199 | retval = dwc2_hs_phy_init(hsotg, select_phy); | ||
1200 | if (retval) | ||
1201 | return retval; | ||
1202 | } | ||
1203 | |||
1204 | if (hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI && | ||
1205 | hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED && | ||
1206 | hsotg->params.ulpi_fs_ls) { | ||
1207 | dev_dbg(hsotg->dev, "Setting ULPI FSLS\n"); | ||
1208 | usbcfg = dwc2_readl(hsotg, GUSBCFG); | ||
1209 | usbcfg |= GUSBCFG_ULPI_FS_LS; | ||
1210 | usbcfg |= GUSBCFG_ULPI_CLK_SUSP_M; | ||
1211 | dwc2_writel(hsotg, usbcfg, GUSBCFG); | ||
1212 | } else { | ||
1213 | usbcfg = dwc2_readl(hsotg, GUSBCFG); | ||
1214 | usbcfg &= ~GUSBCFG_ULPI_FS_LS; | ||
1215 | usbcfg &= ~GUSBCFG_ULPI_CLK_SUSP_M; | ||
1216 | dwc2_writel(hsotg, usbcfg, GUSBCFG); | ||
1217 | } | ||
1218 | |||
1219 | return retval; | ||
1220 | } | ||
1221 | |||
1023 | MODULE_DESCRIPTION("DESIGNWARE HS OTG Core"); | 1222 | MODULE_DESCRIPTION("DESIGNWARE HS OTG Core"); |
1024 | MODULE_AUTHOR("Synopsys, Inc."); | 1223 | MODULE_AUTHOR("Synopsys, Inc."); |
1025 | MODULE_LICENSE("Dual BSD/GPL"); | 1224 | MODULE_LICENSE("Dual BSD/GPL"); |