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

Configure Feed

Select the types of activity you want to include in your feed.

phy: exynos5-usbdrd: support Exynos USBDRD 3.2 4nm controller

Add support for the Exynos USB 3.2 DRD 4nm controller. It's used in
recent 4nm SoCs like Exynos2200 and Exynos2400.

This device consists of 3 underlying and independent phys: SEC link
control phy, Synopsys eUSB 2.0 and Synopsys USBDP/SS combophy. Unlike
older device designs, where the internal phy blocks were all IP of
Samsung, Synopsys phys are present. This means that the link controller
is now mapped differently to account for missing bits and registers.
The Synopsys phys also have separate register bases.

As there are non-SEC PHYs present now, it doesn't make much sense to
implement them in this driver. They are expected to be configured
by external drivers, so pass phandles to them. USBDRD3.2 link controller
set up is still required beforehand.

This commit adds the necessary changes for USB HS to work. USB SS and
DisplayPort are out of scope in this commit and will be introduced
in the future.

Signed-off-by: Ivaylo Ivanov <ivo.ivanov.ivanov1@gmail.com>
Link: https://lore.kernel.org/r/20250504144527.1723980-11-ivo.ivanov.ivanov1@gmail.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>

authored by

Ivaylo Ivanov and committed by
Vinod Koul
cc52a697 c4098f3e

+215 -15
+212 -15
drivers/phy/samsung/phy-exynos5-usbdrd.c
··· 36 36 #define EXYNOS5_FSEL_26MHZ 0x6 37 37 #define EXYNOS5_FSEL_50MHZ 0x7 38 38 39 + /* USB 3.2 DRD 4nm PHY link controller registers */ 40 + #define EXYNOS2200_DRD_CLKRST 0x0c 41 + #define EXYNOS2200_CLKRST_LINK_PCLK_SEL BIT(1) 42 + 43 + #define EXYNOS2200_DRD_UTMI 0x10 44 + #define EXYNOS2200_UTMI_FORCE_VBUSVALID BIT(1) 45 + #define EXYNOS2200_UTMI_FORCE_BVALID BIT(0) 46 + 47 + #define EXYNOS2200_DRD_HSP_MISC 0x114 48 + #define HSP_MISC_SET_REQ_IN2 BIT(4) 49 + #define HSP_MISC_RES_TUNE GENMASK(1, 0) 50 + #define RES_TUNE_PHY1_PHY2 0x1 51 + #define RES_TUNE_PHY1 0x2 52 + #define RES_TUNE_PHY2 0x3 53 + 39 54 /* Exynos5: USB 3.0 DRD PHY registers */ 40 55 #define EXYNOS5_DRD_LINKSYSTEM 0x04 41 56 #define LINKSYSTEM_XHCI_VERSION_CONTROL BIT(27) ··· 446 431 * @clks: clocks for register access 447 432 * @core_clks: core clocks for phy (ref, pipe3, utmi+, ITP, etc. as required) 448 433 * @drv_data: pointer to SoC level driver data structure 434 + * @hs_phy: pointer to non-Samsung IP high-speed phy controller 449 435 * @phy_mutex: mutex protecting phy_init/exit & TCPC callbacks 450 436 * @phys: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY 451 437 * instances each with its 'phy' and 'phy_cfg'. ··· 464 448 struct clk_bulk_data *clks; 465 449 struct clk_bulk_data *core_clks; 466 450 const struct exynos5_usbdrd_phy_drvdata *drv_data; 451 + struct phy *hs_phy; 467 452 struct mutex phy_mutex; 468 453 struct phy_usb_instance { 469 454 struct phy *phy; ··· 1302 1285 .owner = THIS_MODULE, 1303 1286 }; 1304 1287 1288 + static void exynos2200_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd) 1289 + { 1290 + /* Configure non-Samsung IP PHY, responsible for UTMI */ 1291 + phy_init(phy_drd->hs_phy); 1292 + } 1293 + 1294 + static void exynos2200_usbdrd_link_init(struct exynos5_usbdrd_phy *phy_drd) 1295 + { 1296 + void __iomem *regs_base = phy_drd->reg_phy; 1297 + u32 reg; 1298 + 1299 + /* 1300 + * Disable HWACG (hardware auto clock gating control). This will force 1301 + * QACTIVE signal in Q-Channel interface to HIGH level, to make sure 1302 + * the PHY clock is not gated by the hardware. 1303 + */ 1304 + reg = readl(regs_base + EXYNOS850_DRD_LINKCTRL); 1305 + reg |= LINKCTRL_FORCE_QACT; 1306 + writel(reg, regs_base + EXYNOS850_DRD_LINKCTRL); 1307 + 1308 + /* De-assert link reset */ 1309 + reg = readl(regs_base + EXYNOS2200_DRD_CLKRST); 1310 + reg &= ~CLKRST_LINK_SW_RST; 1311 + writel(reg, regs_base + EXYNOS2200_DRD_CLKRST); 1312 + 1313 + /* Set link VBUS Valid */ 1314 + reg = readl(regs_base + EXYNOS2200_DRD_UTMI); 1315 + reg |= EXYNOS2200_UTMI_FORCE_BVALID | EXYNOS2200_UTMI_FORCE_VBUSVALID; 1316 + writel(reg, regs_base + EXYNOS2200_DRD_UTMI); 1317 + } 1318 + 1319 + static void 1320 + exynos2200_usbdrd_link_attach_detach_pipe3_phy(struct phy_usb_instance *inst) 1321 + { 1322 + struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); 1323 + void __iomem *regs_base = phy_drd->reg_phy; 1324 + u32 reg; 1325 + 1326 + reg = readl(regs_base + EXYNOS850_DRD_LINKCTRL); 1327 + if (inst->phy_cfg->id == EXYNOS5_DRDPHY_UTMI) { 1328 + /* force pipe3 signal for link */ 1329 + reg &= ~LINKCTRL_FORCE_PHYSTATUS; 1330 + reg |= LINKCTRL_FORCE_PIPE_EN | LINKCTRL_FORCE_RXELECIDLE; 1331 + } else { 1332 + /* disable forcing pipe interface */ 1333 + reg &= ~LINKCTRL_FORCE_PIPE_EN; 1334 + } 1335 + writel(reg, regs_base + EXYNOS850_DRD_LINKCTRL); 1336 + 1337 + reg = readl(regs_base + EXYNOS2200_DRD_HSP_MISC); 1338 + if (inst->phy_cfg->id == EXYNOS5_DRDPHY_UTMI) { 1339 + /* calibrate only eUSB phy */ 1340 + reg |= FIELD_PREP(HSP_MISC_RES_TUNE, RES_TUNE_PHY1); 1341 + reg |= HSP_MISC_SET_REQ_IN2; 1342 + } else { 1343 + /* calibrate for dual phy */ 1344 + reg |= FIELD_PREP(HSP_MISC_RES_TUNE, RES_TUNE_PHY1_PHY2); 1345 + reg &= ~HSP_MISC_SET_REQ_IN2; 1346 + } 1347 + writel(reg, regs_base + EXYNOS2200_DRD_HSP_MISC); 1348 + 1349 + reg = readl(regs_base + EXYNOS2200_DRD_CLKRST); 1350 + if (inst->phy_cfg->id == EXYNOS5_DRDPHY_UTMI) 1351 + reg &= ~EXYNOS2200_CLKRST_LINK_PCLK_SEL; 1352 + else 1353 + reg |= EXYNOS2200_CLKRST_LINK_PCLK_SEL; 1354 + 1355 + writel(reg, regs_base + EXYNOS2200_DRD_CLKRST); 1356 + } 1357 + 1358 + static int exynos2200_usbdrd_phy_init(struct phy *phy) 1359 + { 1360 + struct phy_usb_instance *inst = phy_get_drvdata(phy); 1361 + struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); 1362 + int ret; 1363 + 1364 + if (inst->phy_cfg->id == EXYNOS5_DRDPHY_UTMI) { 1365 + /* Power-on PHY ... */ 1366 + ret = regulator_bulk_enable(phy_drd->drv_data->n_regulators, 1367 + phy_drd->regulators); 1368 + if (ret) { 1369 + dev_err(phy_drd->dev, 1370 + "Failed to enable PHY regulator(s)\n"); 1371 + return ret; 1372 + } 1373 + } 1374 + /* 1375 + * ... and ungate power via PMU. Without this here, we get an SError 1376 + * trying to access PMA registers 1377 + */ 1378 + exynos5_usbdrd_phy_isol(inst, false); 1379 + 1380 + ret = clk_bulk_prepare_enable(phy_drd->drv_data->n_clks, phy_drd->clks); 1381 + if (ret) 1382 + return ret; 1383 + 1384 + /* Set up the link controller */ 1385 + exynos2200_usbdrd_link_init(phy_drd); 1386 + 1387 + /* UTMI or PIPE3 link preparation */ 1388 + exynos2200_usbdrd_link_attach_detach_pipe3_phy(inst); 1389 + 1390 + /* UTMI or PIPE3 specific init */ 1391 + inst->phy_cfg->phy_init(phy_drd); 1392 + 1393 + clk_bulk_disable_unprepare(phy_drd->drv_data->n_clks, phy_drd->clks); 1394 + 1395 + return 0; 1396 + } 1397 + 1398 + static int exynos2200_usbdrd_phy_exit(struct phy *phy) 1399 + { 1400 + struct phy_usb_instance *inst = phy_get_drvdata(phy); 1401 + struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); 1402 + void __iomem *regs_base = phy_drd->reg_phy; 1403 + u32 reg; 1404 + int ret; 1405 + 1406 + ret = clk_bulk_prepare_enable(phy_drd->drv_data->n_clks, phy_drd->clks); 1407 + if (ret) 1408 + return ret; 1409 + 1410 + reg = readl(regs_base + EXYNOS2200_DRD_UTMI); 1411 + reg &= ~(EXYNOS2200_UTMI_FORCE_BVALID | EXYNOS2200_UTMI_FORCE_VBUSVALID); 1412 + writel(reg, regs_base + EXYNOS2200_DRD_UTMI); 1413 + 1414 + reg = readl(regs_base + EXYNOS2200_DRD_CLKRST); 1415 + reg |= CLKRST_LINK_SW_RST; 1416 + writel(reg, regs_base + EXYNOS2200_DRD_CLKRST); 1417 + 1418 + clk_bulk_disable_unprepare(phy_drd->drv_data->n_clks, phy_drd->clks); 1419 + 1420 + exynos5_usbdrd_phy_isol(inst, true); 1421 + return regulator_bulk_disable(phy_drd->drv_data->n_regulators, 1422 + phy_drd->regulators); 1423 + } 1424 + 1425 + static const struct phy_ops exynos2200_usbdrd_phy_ops = { 1426 + .init = exynos2200_usbdrd_phy_init, 1427 + .exit = exynos2200_usbdrd_phy_exit, 1428 + .owner = THIS_MODULE, 1429 + }; 1430 + 1305 1431 static void 1306 1432 exynos5_usbdrd_usb_v3p1_pipe_override(struct exynos5_usbdrd_phy *phy_drd) 1307 1433 { ··· 1754 1594 return dev_err_probe(phy_drd->dev, ret, 1755 1595 "failed to get phy core clock(s)\n"); 1756 1596 1757 - ref_clk = NULL; 1758 - for (int i = 0; i < phy_drd->drv_data->n_core_clks; ++i) { 1759 - if (!strcmp(phy_drd->core_clks[i].id, "ref")) { 1760 - ref_clk = phy_drd->core_clks[i].clk; 1761 - break; 1597 + if (phy_drd->drv_data->n_core_clks) { 1598 + ref_clk = NULL; 1599 + for (int i = 0; i < phy_drd->drv_data->n_core_clks; ++i) { 1600 + if (!strcmp(phy_drd->core_clks[i].id, "ref")) { 1601 + ref_clk = phy_drd->core_clks[i].clk; 1602 + break; 1603 + } 1762 1604 } 1763 - } 1764 - if (!ref_clk) 1765 - return dev_err_probe(phy_drd->dev, -ENODEV, 1766 - "failed to find phy reference clock\n"); 1605 + if (!ref_clk) 1606 + return dev_err_probe(phy_drd->dev, -ENODEV, 1607 + "failed to find phy reference clock\n"); 1767 1608 1768 - ref_rate = clk_get_rate(ref_clk); 1769 - ret = exynos5_rate_to_clk(ref_rate, &phy_drd->extrefclk); 1770 - if (ret) 1771 - return dev_err_probe(phy_drd->dev, ret, 1772 - "clock rate (%ld) not supported\n", 1773 - ref_rate); 1609 + ref_rate = clk_get_rate(ref_clk); 1610 + ret = exynos5_rate_to_clk(ref_rate, &phy_drd->extrefclk); 1611 + if (ret) 1612 + return dev_err_probe(phy_drd->dev, ret, 1613 + "clock rate (%ld) not supported\n", 1614 + ref_rate); 1615 + } 1774 1616 1775 1617 return 0; 1776 1618 } 1619 + 1620 + static const struct exynos5_usbdrd_phy_config phy_cfg_exynos2200[] = { 1621 + { 1622 + .id = EXYNOS5_DRDPHY_UTMI, 1623 + .phy_isol = exynos5_usbdrd_phy_isol, 1624 + .phy_init = exynos2200_usbdrd_utmi_init, 1625 + }, 1626 + }; 1777 1627 1778 1628 static int exynos5_usbdrd_orien_sw_set(struct typec_switch_dev *sw, 1779 1629 enum typec_orientation orientation) ··· 1935 1765 1936 1766 static const char * const exynos5_regulator_names[] = { 1937 1767 "vbus", "vbus-boost", 1768 + }; 1769 + 1770 + static const struct exynos5_usbdrd_phy_drvdata exynos2200_usb32drd_phy = { 1771 + .phy_cfg = phy_cfg_exynos2200, 1772 + .phy_ops = &exynos2200_usbdrd_phy_ops, 1773 + .pmu_offset_usbdrd0_phy = EXYNOS2200_PHY_CTRL_USB20, 1774 + .clk_names = exynos5_clk_names, 1775 + .n_clks = ARRAY_SIZE(exynos5_clk_names), 1776 + /* clocks and regulators are specific to the underlying PHY blocks */ 1777 + .core_clk_names = NULL, 1778 + .n_core_clks = 0, 1779 + .regulator_names = NULL, 1780 + .n_regulators = 0, 1938 1781 }; 1939 1782 1940 1783 static const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = { ··· 2208 2025 .compatible = "google,gs101-usb31drd-phy", 2209 2026 .data = &gs101_usbd31rd_phy 2210 2027 }, { 2028 + .compatible = "samsung,exynos2200-usb32drd-phy", 2029 + .data = &exynos2200_usb32drd_phy, 2030 + }, { 2211 2031 .compatible = "samsung,exynos5250-usbdrd-phy", 2212 2032 .data = &exynos5250_usbdrd_phy 2213 2033 }, { ··· 2283 2097 phy_drd->reg_phy = devm_platform_ioremap_resource(pdev, 0); 2284 2098 if (IS_ERR(phy_drd->reg_phy)) 2285 2099 return PTR_ERR(phy_drd->reg_phy); 2100 + } 2101 + 2102 + /* 2103 + * USB32DRD 4nm controller implements Synopsys eUSB2.0 PHY 2104 + * and Synopsys SS/USBDP COMBOPHY, managed by external code. 2105 + */ 2106 + if (of_property_present(dev->of_node, "phy-names")) { 2107 + phy_drd->hs_phy = devm_of_phy_get(dev, dev->of_node, "hs"); 2108 + if (IS_ERR(phy_drd->hs_phy)) 2109 + return dev_err_probe(dev, PTR_ERR(phy_drd->hs_phy), 2110 + "failed to get hs_phy\n"); 2286 2111 } 2287 2112 2288 2113 ret = exynos5_usbdrd_phy_clk_handle(phy_drd);
+3
include/linux/soc/samsung/exynos-regs-pmu.h
··· 187 187 /* Only for S5Pv210 */ 188 188 #define S5PV210_EINT_WAKEUP_MASK 0xC004 189 189 190 + /* Only for Exynos2200 */ 191 + #define EXYNOS2200_PHY_CTRL_USB20 0x72C 192 + 190 193 /* Only for Exynos4210 */ 191 194 #define S5P_CMU_CLKSTOP_LCD1_LOWPWR 0x1154 192 195 #define S5P_CMU_RESET_LCD1_LOWPWR 0x1174