Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

phy: exynos5-usbdrd: add exynos7870 USBDRD support

Implement support for Exynos7870 USB DRD on top of the existing
exynos5-usbdrd driver.

Exynos7870 has a single USB 2.0 DRD PHY controller and no 3.0 PHYs. Thus,
it only supports the UTMI interface.

Moreover, the PMU register offset for enabling the PHY controller is
different for SoCs such as Exynos7870, where BIT(0) is for the 3.0 PHY and
BIT(1) is for the 2.0 PHY. The phy_isol function for Exynos7870 uses the
appropriate register offsets.

Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
Link: https://lore.kernel.org/r/20250410-exynos7870-usbphy-v2-3-2eb005987455@disroot.org
Signed-off-by: Vinod Koul <vkoul@kernel.org>

authored by

Kaustabh Chakraborty and committed by
Vinod Koul
588d5d20 23f79385

+262
+260
drivers/phy/samsung/phy-exynos5-usbdrd.c
··· 39 39 /* Exynos5: USB 3.0 DRD PHY registers */ 40 40 #define EXYNOS5_DRD_LINKSYSTEM 0x04 41 41 #define LINKSYSTEM_XHCI_VERSION_CONTROL BIT(27) 42 + #define LINKSYSTEM_FORCE_VBUSVALID BIT(8) 43 + #define LINKSYSTEM_FORCE_BVALID BIT(7) 42 44 #define LINKSYSTEM_FLADJ GENMASK(6, 1) 43 45 44 46 #define EXYNOS5_DRD_PHYUTMI 0x08 47 + #define PHYUTMI_UTMI_SUSPEND_COM_N BIT(12) 48 + #define PHYUTMI_UTMI_L1_SUSPEND_COM_N BIT(11) 49 + #define PHYUTMI_VBUSVLDEXTSEL BIT(10) 50 + #define PHYUTMI_VBUSVLDEXT BIT(9) 51 + #define PHYUTMI_TXBITSTUFFENH BIT(8) 52 + #define PHYUTMI_TXBITSTUFFEN BIT(7) 45 53 #define PHYUTMI_OTGDISABLE BIT(6) 54 + #define PHYUTMI_IDPULLUP BIT(5) 55 + #define PHYUTMI_DRVVBUS BIT(4) 56 + #define PHYUTMI_DPPULLDOWN BIT(3) 57 + #define PHYUTMI_DMPULLDOWN BIT(2) 46 58 #define PHYUTMI_FORCESUSPEND BIT(1) 47 59 #define PHYUTMI_FORCESLEEP BIT(0) 48 60 ··· 103 91 #define PHYPARAM0_REF_USE_PAD BIT(31) 104 92 #define PHYPARAM0_REF_LOSLEVEL GENMASK(30, 26) 105 93 #define PHYPARAM0_REF_LOSLEVEL_VAL 0x9 94 + #define PHYPARAM0_TXVREFTUNE GENMASK(25, 22) 95 + #define PHYPARAM0_TXRISETUNE GENMASK(21, 20) 96 + #define PHYPARAM0_TXRESTUNE GENMASK(19, 18) 97 + #define PHYPARAM0_TXPREEMPPULSETUNE BIT(17) 98 + #define PHYPARAM0_TXPREEMPAMPTUNE GENMASK(16, 15) 99 + #define PHYPARAM0_TXHSXVTUNE GENMASK(14, 13) 100 + #define PHYPARAM0_TXFSLSTUNE GENMASK(12, 9) 101 + #define PHYPARAM0_SQRXTUNE GENMASK(8, 6) 102 + #define PHYPARAM0_OTGTUNE GENMASK(5, 3) 103 + #define PHYPARAM0_COMPDISTUNE GENMASK(2, 0) 106 104 107 105 #define EXYNOS5_DRD_PHYPARAM1 0x20 108 106 #define PHYPARAM1_PCS_TXDEEMPH GENMASK(4, 0) ··· 132 110 #define EXYNOS5_DRD_PHYRESUME 0x34 133 111 134 112 #define EXYNOS5_DRD_LINKPORT 0x44 113 + #define LINKPORT_HOST_U3_PORT_DISABLE BIT(8) 114 + #define LINKPORT_HOST_U2_PORT_DISABLE BIT(7) 115 + #define LINKPORT_HOST_PORT_OVCR_U3 BIT(5) 116 + #define LINKPORT_HOST_PORT_OVCR_U2 BIT(4) 117 + #define LINKPORT_HOST_PORT_OVCR_U3_SEL BIT(3) 118 + #define LINKPORT_HOST_PORT_OVCR_U2_SEL BIT(2) 135 119 136 120 /* USB 3.0 DRD PHY SS Function Control Reg; accessed by CR_PORT */ 137 121 #define EXYNOS5_DRD_PHYSS_LOSLEVEL_OVRD_IN (0x15) ··· 157 129 #define LANE0_TX_DEBUG_RXDET_MEAS_TIME_48M_50M_52M (0x20 << 4) 158 130 #define LANE0_TX_DEBUG_RXDET_MEAS_TIME_62M5 (0x20 << 4) 159 131 #define LANE0_TX_DEBUG_RXDET_MEAS_TIME_96M_100M (0x40 << 4) 132 + 133 + /* Exynos7870: USB DRD PHY registers */ 134 + #define EXYNOS7870_DRD_PHYPCSVAL 0x3C 135 + #define PHYPCSVAL_PCS_RX_LOS_MASK GENMASK(9, 0) 136 + 137 + #define EXYNOS7870_DRD_PHYPARAM2 0x50 138 + #define PHYPARAM2_TX_VBOOST_LVL GENMASK(6, 4) 139 + #define PHYPARAM2_LOS_BIAS GENMASK(2, 0) 140 + 141 + #define EXYNOS7870_DRD_HSPHYCTRL 0x54 142 + #define HSPHYCTRL_PHYSWRSTALL BIT(31) 143 + #define HSPHYCTRL_SIDDQ BIT(6) 144 + #define HSPHYCTRL_PHYSWRST BIT(0) 145 + 146 + #define EXYNOS7870_DRD_HSPHYPLLTUNE 0x70 147 + #define HSPHYPLLTUNE_PLL_B_TUNE BIT(6) 148 + #define HSPHYPLLTUNE_PLL_I_TUNE GENMASK(5, 4) 149 + #define HSPHYPLLTUNE_PLL_P_TUNE GENMASK(3, 0) 160 150 161 151 /* Exynos850: USB DRD PHY registers */ 162 152 #define EXYNOS850_DRD_LINKCTRL 0x04 ··· 1124 1078 .owner = THIS_MODULE, 1125 1079 }; 1126 1080 1081 + static void exynos7870_usbdrd_phy_isol(struct phy_usb_instance *inst, 1082 + bool isolate) 1083 + { 1084 + unsigned int val; 1085 + 1086 + if (!inst->reg_pmu) 1087 + return; 1088 + 1089 + val = isolate ? 0 : EXYNOS7870_USB2PHY_ENABLE; 1090 + 1091 + regmap_update_bits(inst->reg_pmu, inst->pmu_offset, 1092 + EXYNOS7870_USB2PHY_ENABLE, val); 1093 + } 1094 + 1095 + static void exynos7870_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd) 1096 + { 1097 + u32 reg; 1098 + 1099 + reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); 1100 + /* Use PADREFCLK as ref clock */ 1101 + reg &= ~PHYCLKRST_REFCLKSEL; 1102 + reg |= FIELD_PREP_CONST(PHYCLKRST_REFCLKSEL, 1103 + PHYCLKRST_REFCLKSEL_PAD_REFCLK); 1104 + /* Select ref clock rate */ 1105 + reg &= ~PHYCLKRST_FSEL_UTMI; 1106 + reg &= ~PHYCLKRST_FSEL_PIPE; 1107 + reg |= FIELD_PREP(PHYCLKRST_FSEL_UTMI, phy_drd->extrefclk); 1108 + /* Enable suspend and reset the port */ 1109 + reg |= PHYCLKRST_EN_UTMISUSPEND; 1110 + reg |= PHYCLKRST_COMMONONN; 1111 + reg |= PHYCLKRST_PORTRESET; 1112 + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); 1113 + udelay(10); 1114 + 1115 + /* Clear the port reset bit */ 1116 + reg &= ~PHYCLKRST_PORTRESET; 1117 + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); 1118 + 1119 + /* Change PHY PLL tune value */ 1120 + reg = readl(phy_drd->reg_phy + EXYNOS7870_DRD_HSPHYPLLTUNE); 1121 + if (phy_drd->extrefclk == EXYNOS5_FSEL_24MHZ) 1122 + reg |= HSPHYPLLTUNE_PLL_B_TUNE; 1123 + else 1124 + reg &= ~HSPHYPLLTUNE_PLL_B_TUNE; 1125 + reg &= ~HSPHYPLLTUNE_PLL_P_TUNE; 1126 + reg |= FIELD_PREP_CONST(HSPHYPLLTUNE_PLL_P_TUNE, 14); 1127 + writel(reg, phy_drd->reg_phy + EXYNOS7870_DRD_HSPHYPLLTUNE); 1128 + 1129 + /* High-Speed PHY control */ 1130 + reg = readl(phy_drd->reg_phy + EXYNOS7870_DRD_HSPHYCTRL); 1131 + reg &= ~HSPHYCTRL_SIDDQ; 1132 + reg &= ~HSPHYCTRL_PHYSWRST; 1133 + reg &= ~HSPHYCTRL_PHYSWRSTALL; 1134 + writel(reg, phy_drd->reg_phy + EXYNOS7870_DRD_HSPHYCTRL); 1135 + udelay(500); 1136 + 1137 + reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_LINKSYSTEM); 1138 + /* 1139 + * Setting the Frame length Adj value[6:1] to default 0x20 1140 + * See xHCI 1.0 spec, 5.2.4 1141 + */ 1142 + reg |= LINKSYSTEM_XHCI_VERSION_CONTROL; 1143 + reg |= FIELD_PREP_CONST(LINKSYSTEM_FLADJ, 0x20); 1144 + /* Set VBUSVALID signal as the VBUS pad is not used */ 1145 + reg |= LINKSYSTEM_FORCE_BVALID; 1146 + reg |= LINKSYSTEM_FORCE_VBUSVALID; 1147 + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_LINKSYSTEM); 1148 + 1149 + reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMI); 1150 + /* Release force_sleep & force_suspend */ 1151 + reg &= ~PHYUTMI_FORCESLEEP; 1152 + reg &= ~PHYUTMI_FORCESUSPEND; 1153 + /* DP/DM pull down control */ 1154 + reg &= ~PHYUTMI_DMPULLDOWN; 1155 + reg &= ~PHYUTMI_DPPULLDOWN; 1156 + reg &= ~PHYUTMI_DRVVBUS; 1157 + /* Set DP-pull up as the VBUS pad is not used */ 1158 + reg |= PHYUTMI_VBUSVLDEXTSEL; 1159 + reg |= PHYUTMI_VBUSVLDEXT; 1160 + /* Disable OTG block and VBUS valid comparator */ 1161 + reg |= PHYUTMI_OTGDISABLE; 1162 + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMI); 1163 + 1164 + /* Configure OVC IO usage */ 1165 + reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_LINKPORT); 1166 + reg |= LINKPORT_HOST_PORT_OVCR_U3_SEL | LINKPORT_HOST_PORT_OVCR_U2_SEL; 1167 + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_LINKPORT); 1168 + 1169 + /* High-Speed PHY swrst */ 1170 + reg = readl(phy_drd->reg_phy + EXYNOS7870_DRD_HSPHYCTRL); 1171 + reg |= HSPHYCTRL_PHYSWRST; 1172 + writel(reg, phy_drd->reg_phy + EXYNOS7870_DRD_HSPHYCTRL); 1173 + udelay(20); 1174 + 1175 + /* Clear the PHY swrst bit */ 1176 + reg = readl(phy_drd->reg_phy + EXYNOS7870_DRD_HSPHYCTRL); 1177 + reg &= ~HSPHYCTRL_PHYSWRST; 1178 + writel(reg, phy_drd->reg_phy + EXYNOS7870_DRD_HSPHYCTRL); 1179 + 1180 + if (phy_drd->drv_data->phy_tunes) 1181 + exynos5_usbdrd_apply_phy_tunes(phy_drd, 1182 + PTS_UTMI_POSTINIT); 1183 + } 1184 + 1185 + static int exynos7870_usbdrd_phy_init(struct phy *phy) 1186 + { 1187 + struct phy_usb_instance *inst = phy_get_drvdata(phy); 1188 + struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); 1189 + int ret; 1190 + 1191 + ret = clk_bulk_prepare_enable(phy_drd->drv_data->n_clks, phy_drd->clks); 1192 + if (ret) 1193 + return ret; 1194 + 1195 + /* UTMI or PIPE3 specific init */ 1196 + inst->phy_cfg->phy_init(phy_drd); 1197 + 1198 + clk_bulk_disable_unprepare(phy_drd->drv_data->n_clks, phy_drd->clks); 1199 + 1200 + return 0; 1201 + } 1202 + 1203 + static int exynos7870_usbdrd_phy_exit(struct phy *phy) 1204 + { 1205 + int ret; 1206 + u32 reg; 1207 + struct phy_usb_instance *inst = phy_get_drvdata(phy); 1208 + struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); 1209 + 1210 + ret = clk_bulk_prepare_enable(phy_drd->drv_data->n_clks, phy_drd->clks); 1211 + if (ret) 1212 + return ret; 1213 + 1214 + /* 1215 + * Disable the VBUS signal and the ID pull-up resistor. 1216 + * Enable force-suspend and force-sleep modes. 1217 + */ 1218 + reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMI); 1219 + reg &= ~(PHYUTMI_DRVVBUS | PHYUTMI_VBUSVLDEXT | PHYUTMI_VBUSVLDEXTSEL); 1220 + reg &= ~PHYUTMI_IDPULLUP; 1221 + reg |= PHYUTMI_FORCESUSPEND | PHYUTMI_FORCESLEEP; 1222 + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMI); 1223 + 1224 + /* Power down PHY analog blocks */ 1225 + reg = readl(phy_drd->reg_phy + EXYNOS7870_DRD_HSPHYCTRL); 1226 + reg |= HSPHYCTRL_SIDDQ; 1227 + writel(reg, phy_drd->reg_phy + EXYNOS7870_DRD_HSPHYCTRL); 1228 + 1229 + /* Clear VBUSVALID signal as the VBUS pad is not used */ 1230 + reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_LINKSYSTEM); 1231 + reg &= ~(LINKSYSTEM_FORCE_BVALID | LINKSYSTEM_FORCE_VBUSVALID); 1232 + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_LINKSYSTEM); 1233 + 1234 + clk_bulk_disable_unprepare(phy_drd->drv_data->n_clks, phy_drd->clks); 1235 + 1236 + return 0; 1237 + } 1238 + 1239 + static const struct phy_ops exynos7870_usbdrd_phy_ops = { 1240 + .init = exynos7870_usbdrd_phy_init, 1241 + .exit = exynos7870_usbdrd_phy_exit, 1242 + .power_on = exynos5_usbdrd_phy_power_on, 1243 + .power_off = exynos5_usbdrd_phy_power_off, 1244 + .owner = THIS_MODULE, 1245 + }; 1246 + 1127 1247 static void 1128 1248 exynos5_usbdrd_usb_v3p1_pipe_override(struct exynos5_usbdrd_phy *phy_drd) 1129 1249 { ··· 1716 1504 }, 1717 1505 }; 1718 1506 1507 + static const struct exynos5_usbdrd_phy_config phy_cfg_exynos7870[] = { 1508 + { 1509 + .id = EXYNOS5_DRDPHY_UTMI, 1510 + .phy_isol = exynos7870_usbdrd_phy_isol, 1511 + .phy_init = exynos7870_usbdrd_utmi_init, 1512 + }, 1513 + }; 1514 + 1719 1515 static const struct exynos5_usbdrd_phy_config phy_cfg_exynos850[] = { 1720 1516 { 1721 1517 .id = EXYNOS5_DRDPHY_UTMI, 1722 1518 .phy_isol = exynos5_usbdrd_phy_isol, 1723 1519 .phy_init = exynos850_usbdrd_utmi_init, 1724 1520 }, 1521 + }; 1522 + 1523 + static 1524 + const struct exynos5_usbdrd_phy_tuning exynos7870_tunes_utmi_postinit[] = { 1525 + PHY_TUNING_ENTRY_PHY(EXYNOS5_DRD_PHYPARAM0, 1526 + (PHYPARAM0_TXVREFTUNE | PHYPARAM0_TXRISETUNE | 1527 + PHYPARAM0_TXRESTUNE | PHYPARAM0_TXPREEMPPULSETUNE | 1528 + PHYPARAM0_TXPREEMPAMPTUNE | PHYPARAM0_TXHSXVTUNE | 1529 + PHYPARAM0_TXFSLSTUNE | PHYPARAM0_SQRXTUNE | 1530 + PHYPARAM0_OTGTUNE | PHYPARAM0_COMPDISTUNE), 1531 + (FIELD_PREP_CONST(PHYPARAM0_TXVREFTUNE, 14) | 1532 + FIELD_PREP_CONST(PHYPARAM0_TXRISETUNE, 1) | 1533 + FIELD_PREP_CONST(PHYPARAM0_TXRESTUNE, 3) | 1534 + FIELD_PREP_CONST(PHYPARAM0_TXPREEMPAMPTUNE, 0) | 1535 + FIELD_PREP_CONST(PHYPARAM0_TXHSXVTUNE, 0) | 1536 + FIELD_PREP_CONST(PHYPARAM0_TXFSLSTUNE, 3) | 1537 + FIELD_PREP_CONST(PHYPARAM0_SQRXTUNE, 6) | 1538 + FIELD_PREP_CONST(PHYPARAM0_OTGTUNE, 2) | 1539 + FIELD_PREP_CONST(PHYPARAM0_COMPDISTUNE, 3))), 1540 + PHY_TUNING_ENTRY_LAST 1541 + }; 1542 + 1543 + static const struct exynos5_usbdrd_phy_tuning *exynos7870_tunes[PTS_MAX] = { 1544 + [PTS_UTMI_POSTINIT] = exynos7870_tunes_utmi_postinit, 1725 1545 }; 1726 1546 1727 1547 static const char * const exynos5_clk_names[] = { ··· 1818 1574 .n_clks = ARRAY_SIZE(exynos5_clk_names), 1819 1575 .core_clk_names = exynos5433_core_clk_names, 1820 1576 .n_core_clks = ARRAY_SIZE(exynos5433_core_clk_names), 1577 + .regulator_names = exynos5_regulator_names, 1578 + .n_regulators = ARRAY_SIZE(exynos5_regulator_names), 1579 + }; 1580 + 1581 + static const struct exynos5_usbdrd_phy_drvdata exynos7870_usbdrd_phy = { 1582 + .phy_cfg = phy_cfg_exynos7870, 1583 + .phy_tunes = exynos7870_tunes, 1584 + .phy_ops = &exynos7870_usbdrd_phy_ops, 1585 + .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, 1586 + .clk_names = exynos5_clk_names, 1587 + .n_clks = ARRAY_SIZE(exynos5_clk_names), 1588 + .core_clk_names = exynos5_core_clk_names, 1589 + .n_core_clks = ARRAY_SIZE(exynos5_core_clk_names), 1821 1590 .regulator_names = exynos5_regulator_names, 1822 1591 .n_regulators = ARRAY_SIZE(exynos5_regulator_names), 1823 1592 }; ··· 2041 1784 }, { 2042 1785 .compatible = "samsung,exynos7-usbdrd-phy", 2043 1786 .data = &exynos7_usbdrd_phy 1787 + }, { 1788 + .compatible = "samsung,exynos7870-usbdrd-phy", 1789 + .data = &exynos7870_usbdrd_phy 2044 1790 }, { 2045 1791 .compatible = "samsung,exynos850-usbdrd-phy", 2046 1792 .data = &exynos850_usbdrd_phy
+2
include/linux/soc/samsung/exynos-regs-pmu.h
··· 55 55 #define EXYNOS4_MIPI_PHY_SRESETN (1 << 1) 56 56 #define EXYNOS4_MIPI_PHY_MRESETN (1 << 2) 57 57 #define EXYNOS4_MIPI_PHY_RESET_MASK (3 << 1) 58 + /* USB PHY enable bit, valid for Exynos7870 */ 59 + #define EXYNOS7870_USB2PHY_ENABLE (1 << 1) 58 60 59 61 #define S5P_INFORM0 0x0800 60 62 #define S5P_INFORM1 0x0804