Skip to content

Commit b48baf6

Browse files
Hui-Ping Chenvinodkoul
authored andcommitted
phy: nuvoton: add new driver for the Nuvoton MA35 SoC USB 2.0 PHY
Nuvoton MA35 SoCs support DWC2 USB controller. Add the driver to drive the USB 2.0 PHY transceivers. Signed-off-by: Hui-Ping Chen <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Vinod Koul <[email protected]>
1 parent c174f1c commit b48baf6

File tree

5 files changed

+160
-0
lines changed

5 files changed

+160
-0
lines changed

drivers/phy/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ source "drivers/phy/mediatek/Kconfig"
9595
source "drivers/phy/microchip/Kconfig"
9696
source "drivers/phy/motorola/Kconfig"
9797
source "drivers/phy/mscc/Kconfig"
98+
source "drivers/phy/nuvoton/Kconfig"
9899
source "drivers/phy/qualcomm/Kconfig"
99100
source "drivers/phy/ralink/Kconfig"
100101
source "drivers/phy/realtek/Kconfig"

drivers/phy/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ obj-y += allwinner/ \
2525
microchip/ \
2626
motorola/ \
2727
mscc/ \
28+
nuvoton/ \
2829
qualcomm/ \
2930
ralink/ \
3031
realtek/ \

drivers/phy/nuvoton/Kconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# SPDX-License-Identifier: GPL-2.0-only
2+
#
3+
# PHY drivers for Nuvoton MA35 platforms
4+
#
5+
config PHY_MA35_USB
6+
tristate "Nuvoton MA35 USB2.0 PHY driver"
7+
depends on ARCH_MA35 || COMPILE_TEST
8+
depends on OF
9+
select GENERIC_PHY
10+
help
11+
Enable this to support the USB2.0 PHY on the Nuvoton MA35
12+
series SoCs.

drivers/phy/nuvoton/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# SPDX-License-Identifier: GPL-2.0
2+
3+
obj-$(CONFIG_PHY_MA35_USB) += phy-ma35d1-usb2.o

drivers/phy/nuvoton/phy-ma35d1-usb2.c

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Copyright (C) 2024 Nuvoton Technology Corp.
4+
*/
5+
#include <linux/bitfield.h>
6+
#include <linux/clk.h>
7+
#include <linux/delay.h>
8+
#include <linux/io.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/phy/phy.h>
14+
#include <linux/platform_device.h>
15+
#include <linux/regmap.h>
16+
17+
/* USB PHY Miscellaneous Control Register */
18+
#define MA35_SYS_REG_USBPMISCR 0x60
19+
#define PHY0POR BIT(0) /* PHY Power-On Reset Control Bit */
20+
#define PHY0SUSPEND BIT(1) /* PHY Suspend; 0: suspend, 1: operaion */
21+
#define PHY0COMN BIT(2) /* PHY Common Block Power-Down Control */
22+
#define PHY0DEVCKSTB BIT(10) /* PHY 60 MHz UTMI clock stable bit */
23+
24+
struct ma35_usb_phy {
25+
struct clk *clk;
26+
struct device *dev;
27+
struct regmap *sysreg;
28+
};
29+
30+
static int ma35_usb_phy_power_on(struct phy *phy)
31+
{
32+
struct ma35_usb_phy *p_phy = phy_get_drvdata(phy);
33+
unsigned int val;
34+
int ret;
35+
36+
ret = clk_prepare_enable(p_phy->clk);
37+
if (ret < 0) {
38+
dev_err(p_phy->dev, "Failed to enable PHY clock: %d\n", ret);
39+
return ret;
40+
}
41+
42+
regmap_read(p_phy->sysreg, MA35_SYS_REG_USBPMISCR, &val);
43+
if (val & PHY0SUSPEND) {
44+
/*
45+
* USB PHY0 is in operation mode already
46+
* make sure USB PHY 60 MHz UTMI Interface Clock ready
47+
*/
48+
ret = regmap_read_poll_timeout(p_phy->sysreg, MA35_SYS_REG_USBPMISCR, val,
49+
val & PHY0DEVCKSTB, 10, 1000);
50+
if (ret == 0)
51+
return 0;
52+
}
53+
54+
/*
55+
* reset USB PHY0.
56+
* wait until USB PHY0 60 MHz UTMI Interface Clock ready
57+
*/
58+
regmap_update_bits(p_phy->sysreg, MA35_SYS_REG_USBPMISCR, 0x7, (PHY0POR | PHY0SUSPEND));
59+
udelay(20);
60+
61+
/* make USB PHY0 enter operation mode */
62+
regmap_update_bits(p_phy->sysreg, MA35_SYS_REG_USBPMISCR, 0x7, PHY0SUSPEND);
63+
64+
/* make sure USB PHY 60 MHz UTMI Interface Clock ready */
65+
ret = regmap_read_poll_timeout(p_phy->sysreg, MA35_SYS_REG_USBPMISCR, val,
66+
val & PHY0DEVCKSTB, 10, 1000);
67+
if (ret == -ETIMEDOUT) {
68+
dev_err(p_phy->dev, "Check PHY clock, Timeout: %d\n", ret);
69+
clk_disable_unprepare(p_phy->clk);
70+
return ret;
71+
}
72+
73+
return 0;
74+
}
75+
76+
static int ma35_usb_phy_power_off(struct phy *phy)
77+
{
78+
struct ma35_usb_phy *p_phy = phy_get_drvdata(phy);
79+
80+
clk_disable_unprepare(p_phy->clk);
81+
return 0;
82+
}
83+
84+
static const struct phy_ops ma35_usb_phy_ops = {
85+
.power_on = ma35_usb_phy_power_on,
86+
.power_off = ma35_usb_phy_power_off,
87+
.owner = THIS_MODULE,
88+
};
89+
90+
static int ma35_usb_phy_probe(struct platform_device *pdev)
91+
{
92+
struct phy_provider *provider;
93+
struct ma35_usb_phy *p_phy;
94+
struct phy *phy;
95+
96+
p_phy = devm_kzalloc(&pdev->dev, sizeof(*p_phy), GFP_KERNEL);
97+
if (!p_phy)
98+
return -ENOMEM;
99+
100+
p_phy->dev = &pdev->dev;
101+
platform_set_drvdata(pdev, p_phy);
102+
103+
p_phy->sysreg = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "nuvoton,sys");
104+
if (IS_ERR(p_phy->sysreg))
105+
return dev_err_probe(&pdev->dev, PTR_ERR(p_phy->sysreg),
106+
"Failed to get SYS registers\n");
107+
108+
p_phy->clk = of_clk_get(pdev->dev.of_node, 0);
109+
if (IS_ERR(p_phy->clk))
110+
return dev_err_probe(&pdev->dev, PTR_ERR(p_phy->clk),
111+
"failed to find usb_phy clock\n");
112+
113+
phy = devm_phy_create(&pdev->dev, NULL, &ma35_usb_phy_ops);
114+
if (IS_ERR(phy))
115+
return dev_err_probe(&pdev->dev, PTR_ERR(phy), "Failed to create PHY\n");
116+
117+
phy_set_drvdata(phy, p_phy);
118+
119+
provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);
120+
if (IS_ERR(provider))
121+
return dev_err_probe(&pdev->dev, PTR_ERR(provider),
122+
"Failed to register PHY provider\n");
123+
return 0;
124+
}
125+
126+
static const struct of_device_id ma35_usb_phy_of_match[] = {
127+
{ .compatible = "nuvoton,ma35d1-usb2-phy", },
128+
{ },
129+
};
130+
MODULE_DEVICE_TABLE(of, ma35_usb_phy_of_match);
131+
132+
static struct platform_driver ma35_usb_phy_driver = {
133+
.probe = ma35_usb_phy_probe,
134+
.driver = {
135+
.name = "ma35d1-usb2-phy",
136+
.of_match_table = ma35_usb_phy_of_match,
137+
},
138+
};
139+
module_platform_driver(ma35_usb_phy_driver);
140+
141+
MODULE_DESCRIPTION("Nuvoton ma35d1 USB2.0 PHY driver");
142+
MODULE_AUTHOR("Hui-Ping Chen <[email protected]>");
143+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)