Skip to content

Commit cc52a69

Browse files
ivoszbgvinodkoul
authored andcommitted
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 <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Vinod Koul <[email protected]>
1 parent c4098f3 commit cc52a69

File tree

2 files changed

+215
-15
lines changed

2 files changed

+215
-15
lines changed

drivers/phy/samsung/phy-exynos5-usbdrd.c

Lines changed: 212 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,21 @@
3636
#define EXYNOS5_FSEL_26MHZ 0x6
3737
#define EXYNOS5_FSEL_50MHZ 0x7
3838

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+
3954
/* Exynos5: USB 3.0 DRD PHY registers */
4055
#define EXYNOS5_DRD_LINKSYSTEM 0x04
4156
#define LINKSYSTEM_XHCI_VERSION_CONTROL BIT(27)
@@ -431,6 +446,7 @@ struct exynos5_usbdrd_phy_drvdata {
431446
* @clks: clocks for register access
432447
* @core_clks: core clocks for phy (ref, pipe3, utmi+, ITP, etc. as required)
433448
* @drv_data: pointer to SoC level driver data structure
449+
* @hs_phy: pointer to non-Samsung IP high-speed phy controller
434450
* @phy_mutex: mutex protecting phy_init/exit & TCPC callbacks
435451
* @phys: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY
436452
* instances each with its 'phy' and 'phy_cfg'.
@@ -448,6 +464,7 @@ struct exynos5_usbdrd_phy {
448464
struct clk_bulk_data *clks;
449465
struct clk_bulk_data *core_clks;
450466
const struct exynos5_usbdrd_phy_drvdata *drv_data;
467+
struct phy *hs_phy;
451468
struct mutex phy_mutex;
452469
struct phy_usb_instance {
453470
struct phy *phy;
@@ -1285,6 +1302,149 @@ static const struct phy_ops exynos7870_usbdrd_phy_ops = {
12851302
.owner = THIS_MODULE,
12861303
};
12871304

1305+
static void exynos2200_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
1306+
{
1307+
/* Configure non-Samsung IP PHY, responsible for UTMI */
1308+
phy_init(phy_drd->hs_phy);
1309+
}
1310+
1311+
static void exynos2200_usbdrd_link_init(struct exynos5_usbdrd_phy *phy_drd)
1312+
{
1313+
void __iomem *regs_base = phy_drd->reg_phy;
1314+
u32 reg;
1315+
1316+
/*
1317+
* Disable HWACG (hardware auto clock gating control). This will force
1318+
* QACTIVE signal in Q-Channel interface to HIGH level, to make sure
1319+
* the PHY clock is not gated by the hardware.
1320+
*/
1321+
reg = readl(regs_base + EXYNOS850_DRD_LINKCTRL);
1322+
reg |= LINKCTRL_FORCE_QACT;
1323+
writel(reg, regs_base + EXYNOS850_DRD_LINKCTRL);
1324+
1325+
/* De-assert link reset */
1326+
reg = readl(regs_base + EXYNOS2200_DRD_CLKRST);
1327+
reg &= ~CLKRST_LINK_SW_RST;
1328+
writel(reg, regs_base + EXYNOS2200_DRD_CLKRST);
1329+
1330+
/* Set link VBUS Valid */
1331+
reg = readl(regs_base + EXYNOS2200_DRD_UTMI);
1332+
reg |= EXYNOS2200_UTMI_FORCE_BVALID | EXYNOS2200_UTMI_FORCE_VBUSVALID;
1333+
writel(reg, regs_base + EXYNOS2200_DRD_UTMI);
1334+
}
1335+
1336+
static void
1337+
exynos2200_usbdrd_link_attach_detach_pipe3_phy(struct phy_usb_instance *inst)
1338+
{
1339+
struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
1340+
void __iomem *regs_base = phy_drd->reg_phy;
1341+
u32 reg;
1342+
1343+
reg = readl(regs_base + EXYNOS850_DRD_LINKCTRL);
1344+
if (inst->phy_cfg->id == EXYNOS5_DRDPHY_UTMI) {
1345+
/* force pipe3 signal for link */
1346+
reg &= ~LINKCTRL_FORCE_PHYSTATUS;
1347+
reg |= LINKCTRL_FORCE_PIPE_EN | LINKCTRL_FORCE_RXELECIDLE;
1348+
} else {
1349+
/* disable forcing pipe interface */
1350+
reg &= ~LINKCTRL_FORCE_PIPE_EN;
1351+
}
1352+
writel(reg, regs_base + EXYNOS850_DRD_LINKCTRL);
1353+
1354+
reg = readl(regs_base + EXYNOS2200_DRD_HSP_MISC);
1355+
if (inst->phy_cfg->id == EXYNOS5_DRDPHY_UTMI) {
1356+
/* calibrate only eUSB phy */
1357+
reg |= FIELD_PREP(HSP_MISC_RES_TUNE, RES_TUNE_PHY1);
1358+
reg |= HSP_MISC_SET_REQ_IN2;
1359+
} else {
1360+
/* calibrate for dual phy */
1361+
reg |= FIELD_PREP(HSP_MISC_RES_TUNE, RES_TUNE_PHY1_PHY2);
1362+
reg &= ~HSP_MISC_SET_REQ_IN2;
1363+
}
1364+
writel(reg, regs_base + EXYNOS2200_DRD_HSP_MISC);
1365+
1366+
reg = readl(regs_base + EXYNOS2200_DRD_CLKRST);
1367+
if (inst->phy_cfg->id == EXYNOS5_DRDPHY_UTMI)
1368+
reg &= ~EXYNOS2200_CLKRST_LINK_PCLK_SEL;
1369+
else
1370+
reg |= EXYNOS2200_CLKRST_LINK_PCLK_SEL;
1371+
1372+
writel(reg, regs_base + EXYNOS2200_DRD_CLKRST);
1373+
}
1374+
1375+
static int exynos2200_usbdrd_phy_init(struct phy *phy)
1376+
{
1377+
struct phy_usb_instance *inst = phy_get_drvdata(phy);
1378+
struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
1379+
int ret;
1380+
1381+
if (inst->phy_cfg->id == EXYNOS5_DRDPHY_UTMI) {
1382+
/* Power-on PHY ... */
1383+
ret = regulator_bulk_enable(phy_drd->drv_data->n_regulators,
1384+
phy_drd->regulators);
1385+
if (ret) {
1386+
dev_err(phy_drd->dev,
1387+
"Failed to enable PHY regulator(s)\n");
1388+
return ret;
1389+
}
1390+
}
1391+
/*
1392+
* ... and ungate power via PMU. Without this here, we get an SError
1393+
* trying to access PMA registers
1394+
*/
1395+
exynos5_usbdrd_phy_isol(inst, false);
1396+
1397+
ret = clk_bulk_prepare_enable(phy_drd->drv_data->n_clks, phy_drd->clks);
1398+
if (ret)
1399+
return ret;
1400+
1401+
/* Set up the link controller */
1402+
exynos2200_usbdrd_link_init(phy_drd);
1403+
1404+
/* UTMI or PIPE3 link preparation */
1405+
exynos2200_usbdrd_link_attach_detach_pipe3_phy(inst);
1406+
1407+
/* UTMI or PIPE3 specific init */
1408+
inst->phy_cfg->phy_init(phy_drd);
1409+
1410+
clk_bulk_disable_unprepare(phy_drd->drv_data->n_clks, phy_drd->clks);
1411+
1412+
return 0;
1413+
}
1414+
1415+
static int exynos2200_usbdrd_phy_exit(struct phy *phy)
1416+
{
1417+
struct phy_usb_instance *inst = phy_get_drvdata(phy);
1418+
struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
1419+
void __iomem *regs_base = phy_drd->reg_phy;
1420+
u32 reg;
1421+
int ret;
1422+
1423+
ret = clk_bulk_prepare_enable(phy_drd->drv_data->n_clks, phy_drd->clks);
1424+
if (ret)
1425+
return ret;
1426+
1427+
reg = readl(regs_base + EXYNOS2200_DRD_UTMI);
1428+
reg &= ~(EXYNOS2200_UTMI_FORCE_BVALID | EXYNOS2200_UTMI_FORCE_VBUSVALID);
1429+
writel(reg, regs_base + EXYNOS2200_DRD_UTMI);
1430+
1431+
reg = readl(regs_base + EXYNOS2200_DRD_CLKRST);
1432+
reg |= CLKRST_LINK_SW_RST;
1433+
writel(reg, regs_base + EXYNOS2200_DRD_CLKRST);
1434+
1435+
clk_bulk_disable_unprepare(phy_drd->drv_data->n_clks, phy_drd->clks);
1436+
1437+
exynos5_usbdrd_phy_isol(inst, true);
1438+
return regulator_bulk_disable(phy_drd->drv_data->n_regulators,
1439+
phy_drd->regulators);
1440+
}
1441+
1442+
static const struct phy_ops exynos2200_usbdrd_phy_ops = {
1443+
.init = exynos2200_usbdrd_phy_init,
1444+
.exit = exynos2200_usbdrd_phy_exit,
1445+
.owner = THIS_MODULE,
1446+
};
1447+
12881448
static void
12891449
exynos5_usbdrd_usb_v3p1_pipe_override(struct exynos5_usbdrd_phy *phy_drd)
12901450
{
@@ -1594,27 +1754,37 @@ static int exynos5_usbdrd_phy_clk_handle(struct exynos5_usbdrd_phy *phy_drd)
15941754
return dev_err_probe(phy_drd->dev, ret,
15951755
"failed to get phy core clock(s)\n");
15961756

1597-
ref_clk = NULL;
1598-
for (int i = 0; i < phy_drd->drv_data->n_core_clks; ++i) {
1599-
if (!strcmp(phy_drd->core_clks[i].id, "ref")) {
1600-
ref_clk = phy_drd->core_clks[i].clk;
1601-
break;
1757+
if (phy_drd->drv_data->n_core_clks) {
1758+
ref_clk = NULL;
1759+
for (int i = 0; i < phy_drd->drv_data->n_core_clks; ++i) {
1760+
if (!strcmp(phy_drd->core_clks[i].id, "ref")) {
1761+
ref_clk = phy_drd->core_clks[i].clk;
1762+
break;
1763+
}
16021764
}
1603-
}
1604-
if (!ref_clk)
1605-
return dev_err_probe(phy_drd->dev, -ENODEV,
1606-
"failed to find phy reference clock\n");
1765+
if (!ref_clk)
1766+
return dev_err_probe(phy_drd->dev, -ENODEV,
1767+
"failed to find phy reference clock\n");
16071768

1608-
ref_rate = clk_get_rate(ref_clk);
1609-
ret = exynos5_rate_to_clk(ref_rate, &phy_drd->extrefclk);
1610-
if (ret)
1611-
return dev_err_probe(phy_drd->dev, ret,
1612-
"clock rate (%ld) not supported\n",
1613-
ref_rate);
1769+
ref_rate = clk_get_rate(ref_clk);
1770+
ret = exynos5_rate_to_clk(ref_rate, &phy_drd->extrefclk);
1771+
if (ret)
1772+
return dev_err_probe(phy_drd->dev, ret,
1773+
"clock rate (%ld) not supported\n",
1774+
ref_rate);
1775+
}
16141776

16151777
return 0;
16161778
}
16171779

1780+
static const struct exynos5_usbdrd_phy_config phy_cfg_exynos2200[] = {
1781+
{
1782+
.id = EXYNOS5_DRDPHY_UTMI,
1783+
.phy_isol = exynos5_usbdrd_phy_isol,
1784+
.phy_init = exynos2200_usbdrd_utmi_init,
1785+
},
1786+
};
1787+
16181788
static int exynos5_usbdrd_orien_sw_set(struct typec_switch_dev *sw,
16191789
enum typec_orientation orientation)
16201790
{
@@ -1767,6 +1937,19 @@ static const char * const exynos5_regulator_names[] = {
17671937
"vbus", "vbus-boost",
17681938
};
17691939

1940+
static const struct exynos5_usbdrd_phy_drvdata exynos2200_usb32drd_phy = {
1941+
.phy_cfg = phy_cfg_exynos2200,
1942+
.phy_ops = &exynos2200_usbdrd_phy_ops,
1943+
.pmu_offset_usbdrd0_phy = EXYNOS2200_PHY_CTRL_USB20,
1944+
.clk_names = exynos5_clk_names,
1945+
.n_clks = ARRAY_SIZE(exynos5_clk_names),
1946+
/* clocks and regulators are specific to the underlying PHY blocks */
1947+
.core_clk_names = NULL,
1948+
.n_core_clks = 0,
1949+
.regulator_names = NULL,
1950+
.n_regulators = 0,
1951+
};
1952+
17701953
static const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = {
17711954
.phy_cfg = phy_cfg_exynos5,
17721955
.phy_ops = &exynos5_usbdrd_phy_ops,
@@ -2024,6 +2207,9 @@ static const struct of_device_id exynos5_usbdrd_phy_of_match[] = {
20242207
{
20252208
.compatible = "google,gs101-usb31drd-phy",
20262209
.data = &gs101_usbd31rd_phy
2210+
}, {
2211+
.compatible = "samsung,exynos2200-usb32drd-phy",
2212+
.data = &exynos2200_usb32drd_phy,
20272213
}, {
20282214
.compatible = "samsung,exynos5250-usbdrd-phy",
20292215
.data = &exynos5250_usbdrd_phy
@@ -2099,6 +2285,17 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev)
20992285
return PTR_ERR(phy_drd->reg_phy);
21002286
}
21012287

2288+
/*
2289+
* USB32DRD 4nm controller implements Synopsys eUSB2.0 PHY
2290+
* and Synopsys SS/USBDP COMBOPHY, managed by external code.
2291+
*/
2292+
if (of_property_present(dev->of_node, "phy-names")) {
2293+
phy_drd->hs_phy = devm_of_phy_get(dev, dev->of_node, "hs");
2294+
if (IS_ERR(phy_drd->hs_phy))
2295+
return dev_err_probe(dev, PTR_ERR(phy_drd->hs_phy),
2296+
"failed to get hs_phy\n");
2297+
}
2298+
21022299
ret = exynos5_usbdrd_phy_clk_handle(phy_drd);
21032300
if (ret)
21042301
return ret;

include/linux/soc/samsung/exynos-regs-pmu.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,9 @@
187187
/* Only for S5Pv210 */
188188
#define S5PV210_EINT_WAKEUP_MASK 0xC004
189189

190+
/* Only for Exynos2200 */
191+
#define EXYNOS2200_PHY_CTRL_USB20 0x72C
192+
190193
/* Only for Exynos4210 */
191194
#define S5P_CMU_CLKSTOP_LCD1_LOWPWR 0x1154
192195
#define S5P_CMU_RESET_LCD1_LOWPWR 0x1174

0 commit comments

Comments
 (0)