Skip to content

Commit f0c6d77

Browse files
inochisavinodkoul
authored andcommitted
phy: sophgo: Add USB 2.0 PHY driver for Sophgo CV18XX/SG200X
Add USB 2.0 PHY driver for Sophgo CV18XX/SG200X. Currently this driver does not support OTG mode as lack of document. Signed-off-by: Inochi Amaoto <inochiama@gmail.com> Tested-by: Alexander Sverdlin <alexander.sverdlin@gmail.com> Link: https://lore.kernel.org/r/20250708063038.497473-3-inochiama@gmail.com Signed-off-by: Vinod Koul <vkoul@kernel.org>
1 parent cdb2511 commit f0c6d77

File tree

5 files changed

+193
-0
lines changed

5 files changed

+193
-0
lines changed

drivers/phy/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ source "drivers/phy/renesas/Kconfig"
122122
source "drivers/phy/rockchip/Kconfig"
123123
source "drivers/phy/samsung/Kconfig"
124124
source "drivers/phy/socionext/Kconfig"
125+
source "drivers/phy/sophgo/Kconfig"
125126
source "drivers/phy/st/Kconfig"
126127
source "drivers/phy/starfive/Kconfig"
127128
source "drivers/phy/sunplus/Kconfig"

drivers/phy/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ obj-y += allwinner/ \
3535
rockchip/ \
3636
samsung/ \
3737
socionext/ \
38+
sophgo/ \
3839
st/ \
3940
starfive/ \
4041
sunplus/ \

drivers/phy/sophgo/Kconfig

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# SPDX-License-Identifier: GPL-2.0-only
2+
#
3+
# Phy drivers for Sophgo platforms
4+
#
5+
6+
if ARCH_SOPHGO || COMPILE_TEST
7+
8+
config PHY_SOPHGO_CV1800_USB2
9+
tristate "Sophgo CV18XX/SG200X USB 2.0 PHY support"
10+
depends on MFD_SYSCON
11+
depends on USB_SUPPORT
12+
select GENERIC_PHY
13+
help
14+
Enable this to support the USB 2.0 PHY used with
15+
the DWC2 USB controller in Sophgo CV18XX/SG200X
16+
series SoC.
17+
If unsure, say N.
18+
19+
endif # ARCH_SOPHGO || COMPILE_TEST

drivers/phy/sophgo/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# SPDX-License-Identifier: GPL-2.0
2+
obj-$(CONFIG_PHY_SOPHGO_CV1800_USB2) += phy-cv1800-usb2.o
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Copyright (C) 2025 Inochi Amaoto <inochiama@outlook.com>
4+
*/
5+
6+
#include <linux/clk.h>
7+
#include <linux/bitfield.h>
8+
#include <linux/debugfs.h>
9+
#include <linux/kernel.h>
10+
#include <linux/mfd/syscon.h>
11+
#include <linux/module.h>
12+
#include <linux/of.h>
13+
#include <linux/of_address.h>
14+
#include <linux/of_gpio.h>
15+
#include <linux/platform_device.h>
16+
#include <linux/phy/phy.h>
17+
#include <linux/regmap.h>
18+
#include <linux/spinlock.h>
19+
20+
#define REG_USB_PHY_CTRL 0x048
21+
22+
#define PHY_VBUS_POWER_EN BIT(0)
23+
#define PHY_VBUS_POWER BIT(1)
24+
#define PHY_ID_OVERWRITE_EN BIT(6)
25+
#define PHY_ID_OVERWRITE_MODE BIT(7)
26+
#define PHY_ID_OVERWRITE_MODE_HOST FIELD_PREP(BIT(7), 0)
27+
#define PHY_ID_OVERWRITE_MODE_DEVICE FIELD_PREP(BIT(7), 1)
28+
29+
#define PHY_APP_CLK_RATE 125000000
30+
#define PHY_LPM_CLK_RATE 12000000
31+
#define PHY_STB_CLK_RATE 333334
32+
33+
struct cv1800_usb_phy {
34+
struct phy *phy;
35+
struct regmap *syscon;
36+
spinlock_t lock;
37+
struct clk *usb_app_clk;
38+
struct clk *usb_lpm_clk;
39+
struct clk *usb_stb_clk;
40+
bool support_otg;
41+
};
42+
43+
static int cv1800_usb_phy_set_mode(struct phy *_phy,
44+
enum phy_mode mode, int submode)
45+
{
46+
struct cv1800_usb_phy *phy = phy_get_drvdata(_phy);
47+
unsigned int regval = 0;
48+
int ret;
49+
50+
dev_info(&phy->phy->dev, "set mode %d", (int)mode);
51+
52+
switch (mode) {
53+
case PHY_MODE_USB_DEVICE:
54+
regval = PHY_ID_OVERWRITE_EN | PHY_ID_OVERWRITE_MODE_DEVICE;
55+
regmap_clear_bits(phy->syscon, REG_USB_PHY_CTRL, PHY_VBUS_POWER);
56+
break;
57+
case PHY_MODE_USB_HOST:
58+
regval = PHY_ID_OVERWRITE_EN | PHY_ID_OVERWRITE_MODE_HOST;
59+
regmap_set_bits(phy->syscon, REG_USB_PHY_CTRL, PHY_VBUS_POWER);
60+
break;
61+
case PHY_MODE_USB_OTG:
62+
if (!phy->support_otg)
63+
return 0;
64+
65+
ret = regmap_read(phy->syscon, REG_USB_PHY_CTRL, &regval);
66+
if (ret)
67+
return ret;
68+
69+
regval = FIELD_GET(PHY_ID_OVERWRITE_MODE, regval);
70+
break;
71+
default:
72+
return -EINVAL;
73+
}
74+
75+
return regmap_update_bits(phy->syscon, REG_USB_PHY_CTRL,
76+
PHY_ID_OVERWRITE_EN | PHY_ID_OVERWRITE_MODE,
77+
regval);
78+
}
79+
80+
static int cv1800_usb_phy_set_clock(struct cv1800_usb_phy *phy)
81+
{
82+
int ret;
83+
84+
ret = clk_set_rate(phy->usb_app_clk, PHY_APP_CLK_RATE);
85+
if (ret)
86+
return ret;
87+
88+
ret = clk_set_rate(phy->usb_lpm_clk, PHY_LPM_CLK_RATE);
89+
if (ret)
90+
return ret;
91+
92+
return clk_set_rate(phy->usb_stb_clk, PHY_STB_CLK_RATE);
93+
}
94+
95+
static const struct phy_ops cv1800_usb_phy_ops = {
96+
.set_mode = cv1800_usb_phy_set_mode,
97+
.owner = THIS_MODULE,
98+
};
99+
100+
static int cv1800_usb_phy_probe(struct platform_device *pdev)
101+
{
102+
struct device *dev = &pdev->dev;
103+
struct device *parent = dev->parent;
104+
struct cv1800_usb_phy *phy;
105+
struct phy_provider *phy_provider;
106+
int ret;
107+
108+
if (!parent)
109+
return -ENODEV;
110+
111+
phy = devm_kmalloc(dev, sizeof(*phy), GFP_KERNEL);
112+
if (!phy)
113+
return -ENOMEM;
114+
115+
phy->syscon = syscon_node_to_regmap(parent->of_node);
116+
if (IS_ERR_OR_NULL(phy->syscon))
117+
return -ENODEV;
118+
119+
phy->support_otg = false;
120+
121+
spin_lock_init(&phy->lock);
122+
123+
phy->usb_app_clk = devm_clk_get_enabled(dev, "app");
124+
if (IS_ERR(phy->usb_app_clk))
125+
return dev_err_probe(dev, PTR_ERR(phy->usb_app_clk),
126+
"Failed to get app clock\n");
127+
128+
phy->usb_lpm_clk = devm_clk_get_enabled(dev, "lpm");
129+
if (IS_ERR(phy->usb_lpm_clk))
130+
return dev_err_probe(dev, PTR_ERR(phy->usb_lpm_clk),
131+
"Failed to get lpm clock\n");
132+
133+
phy->usb_stb_clk = devm_clk_get_enabled(dev, "stb");
134+
if (IS_ERR(phy->usb_stb_clk))
135+
return dev_err_probe(dev, PTR_ERR(phy->usb_stb_clk),
136+
"Failed to get stb clock\n");
137+
138+
phy->phy = devm_phy_create(dev, NULL, &cv1800_usb_phy_ops);
139+
if (IS_ERR(phy->phy))
140+
return dev_err_probe(dev, PTR_ERR(phy->phy),
141+
"Failed to create phy\n");
142+
143+
ret = cv1800_usb_phy_set_clock(phy);
144+
if (ret)
145+
return ret;
146+
147+
phy_set_drvdata(phy->phy, phy);
148+
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
149+
150+
return PTR_ERR_OR_ZERO(phy_provider);
151+
}
152+
153+
static const struct of_device_id cv1800_usb_phy_ids[] = {
154+
{ .compatible = "sophgo,cv1800b-usb2-phy" },
155+
{ },
156+
};
157+
MODULE_DEVICE_TABLE(of, cv1800_usb_phy_ids);
158+
159+
static struct platform_driver cv1800_usb_phy_driver = {
160+
.probe = cv1800_usb_phy_probe,
161+
.driver = {
162+
.name = "cv1800-usb2-phy",
163+
.of_match_table = cv1800_usb_phy_ids,
164+
},
165+
};
166+
module_platform_driver(cv1800_usb_phy_driver);
167+
168+
MODULE_AUTHOR("Inochi Amaoto <inochiama@outlook.com>");
169+
MODULE_DESCRIPTION("CV1800/SG2000 SoC USB 2.0 PHY driver");
170+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)