xref: /linux/drivers/phy/spacemit/phy-k1-usb2.c (revision 9b725d5959a1d9bb670ccdb23add45f0eaafefb5)
1*fe4bc1a0SZe Huang // SPDX-License-Identifier: GPL-2.0-only
2*fe4bc1a0SZe Huang /*
3*fe4bc1a0SZe Huang  * SpacemiT K1 USB 2.0 PHY driver
4*fe4bc1a0SZe Huang  *
5*fe4bc1a0SZe Huang  * Copyright (C) 2025 SpacemiT (Hangzhou) Technology Co. Ltd
6*fe4bc1a0SZe Huang  * Copyright (C) 2025 Ze Huang <huang.ze@linux.dev>
7*fe4bc1a0SZe Huang  */
8*fe4bc1a0SZe Huang 
9*fe4bc1a0SZe Huang #include <linux/bitfield.h>
10*fe4bc1a0SZe Huang #include <linux/clk.h>
11*fe4bc1a0SZe Huang #include <linux/iopoll.h>
12*fe4bc1a0SZe Huang #include <linux/platform_device.h>
13*fe4bc1a0SZe Huang #include <linux/regmap.h>
14*fe4bc1a0SZe Huang #include <linux/usb/of.h>
15*fe4bc1a0SZe Huang 
16*fe4bc1a0SZe Huang #define PHY_RST_MODE_CTRL		0x04
17*fe4bc1a0SZe Huang #define  PHY_PLL_RDY			BIT(0)
18*fe4bc1a0SZe Huang #define  PHY_CLK_CDR_EN			BIT(1)
19*fe4bc1a0SZe Huang #define  PHY_CLK_PLL_EN			BIT(2)
20*fe4bc1a0SZe Huang #define  PHY_CLK_MAC_EN			BIT(3)
21*fe4bc1a0SZe Huang #define  PHY_MAC_RSTN			BIT(5)
22*fe4bc1a0SZe Huang #define  PHY_CDR_RSTN			BIT(6)
23*fe4bc1a0SZe Huang #define  PHY_PLL_RSTN			BIT(7)
24*fe4bc1a0SZe Huang /*
25*fe4bc1a0SZe Huang  * hs line state sel (Bit 13):
26*fe4bc1a0SZe Huang  * - 1 (Default): Internal HS line state is set to 01 when usb_hs_tx_en is valid.
27*fe4bc1a0SZe Huang  * - 0: Internal HS line state is always driven by usb_hs_lstate.
28*fe4bc1a0SZe Huang  *
29*fe4bc1a0SZe Huang  * fs line state sel (Bit 14):
30*fe4bc1a0SZe Huang  * - 1 (Default): FS line state is determined by the output data
31*fe4bc1a0SZe Huang  * (usb_fs_datain/b).
32*fe4bc1a0SZe Huang  * - 0: FS line state is always determined by the input data (dmo/dpo).
33*fe4bc1a0SZe Huang  */
34*fe4bc1a0SZe Huang #define  PHY_HS_LINE_TX_MODE		BIT(13)
35*fe4bc1a0SZe Huang #define  PHY_FS_LINE_TX_MODE		BIT(14)
36*fe4bc1a0SZe Huang 
37*fe4bc1a0SZe Huang #define  PHY_INIT_MODE_BITS		(PHY_FS_LINE_TX_MODE | PHY_HS_LINE_TX_MODE)
38*fe4bc1a0SZe Huang #define  PHY_CLK_ENABLE_BITS		(PHY_CLK_PLL_EN | PHY_CLK_CDR_EN | \
39*fe4bc1a0SZe Huang 					 PHY_CLK_MAC_EN)
40*fe4bc1a0SZe Huang #define  PHY_DEASSERT_RST_BITS		(PHY_PLL_RSTN | PHY_CDR_RSTN | \
41*fe4bc1a0SZe Huang 					 PHY_MAC_RSTN)
42*fe4bc1a0SZe Huang 
43*fe4bc1a0SZe Huang #define PHY_TX_HOST_CTRL		0x10
44*fe4bc1a0SZe Huang #define  PHY_HST_DISC_AUTO_CLR		BIT(2)		/* autoclear hs host disc when re-connect */
45*fe4bc1a0SZe Huang 
46*fe4bc1a0SZe Huang #define PHY_HSTXP_HW_CTRL		0x34
47*fe4bc1a0SZe Huang #define  PHY_HSTXP_RSTN			BIT(2)		/* generate reset for clock hstxp */
48*fe4bc1a0SZe Huang #define  PHY_CLK_HSTXP_EN		BIT(3)		/* clock hstxp enable */
49*fe4bc1a0SZe Huang #define  PHY_HSTXP_MODE			BIT(4)		/* 0: force en_txp to be 1; 1: no force */
50*fe4bc1a0SZe Huang 
51*fe4bc1a0SZe Huang #define PHY_PLL_DIV_CFG			0x98
52*fe4bc1a0SZe Huang #define  PHY_FDIV_FRACT_8_15		GENMASK(7, 0)
53*fe4bc1a0SZe Huang #define  PHY_FDIV_FRACT_16_19		GENMASK(11, 8)
54*fe4bc1a0SZe Huang #define  PHY_FDIV_FRACT_20_21		BIT(12)		/* fdiv_reg<21>, <20>, bit21 == bit20 */
55*fe4bc1a0SZe Huang /*
56*fe4bc1a0SZe Huang  * freq_sel<1:0>
57*fe4bc1a0SZe Huang  * if ref clk freq=24.0MHz-->freq_sel<2:0> == 3b'001, then internal divider value == 80
58*fe4bc1a0SZe Huang  */
59*fe4bc1a0SZe Huang #define  PHY_FDIV_FRACT_0_1		GENMASK(14, 13)
60*fe4bc1a0SZe Huang /*
61*fe4bc1a0SZe Huang  * pll divider value selection
62*fe4bc1a0SZe Huang  * 1: divider value will choose internal default value ,dependent on freq_sel<1:0>
63*fe4bc1a0SZe Huang  * 0: divider value will be over ride by fdiv_reg<21:0>
64*fe4bc1a0SZe Huang  */
65*fe4bc1a0SZe Huang #define  PHY_DIV_LOCAL_EN		BIT(15)
66*fe4bc1a0SZe Huang 
67*fe4bc1a0SZe Huang #define  PHY_SEL_FREQ_24MHZ		0x01
68*fe4bc1a0SZe Huang #define  FDIV_REG_MASK			(PHY_FDIV_FRACT_20_21 | PHY_FDIV_FRACT_16_19 | \
69*fe4bc1a0SZe Huang 					 PHY_FDIV_FRACT_8_15)
70*fe4bc1a0SZe Huang #define  FDIV_REG_VAL			0x1ec4		/* 0x100 selects 24MHz, rest are default */
71*fe4bc1a0SZe Huang 
72*fe4bc1a0SZe Huang #define K1_USB2PHY_RESET_TIME_MS	50
73*fe4bc1a0SZe Huang 
74*fe4bc1a0SZe Huang struct spacemit_usb2phy {
75*fe4bc1a0SZe Huang 	struct phy *phy;
76*fe4bc1a0SZe Huang 	struct clk *clk;
77*fe4bc1a0SZe Huang 	struct regmap *regmap_base;
78*fe4bc1a0SZe Huang };
79*fe4bc1a0SZe Huang 
80*fe4bc1a0SZe Huang static const struct regmap_config phy_regmap_config = {
81*fe4bc1a0SZe Huang 	.reg_bits = 32,
82*fe4bc1a0SZe Huang 	.val_bits = 32,
83*fe4bc1a0SZe Huang 	.reg_stride = 4,
84*fe4bc1a0SZe Huang 	.max_register = 0x200,
85*fe4bc1a0SZe Huang };
86*fe4bc1a0SZe Huang 
87*fe4bc1a0SZe Huang static int spacemit_usb2phy_init(struct phy *phy)
88*fe4bc1a0SZe Huang {
89*fe4bc1a0SZe Huang 	struct spacemit_usb2phy *sphy = phy_get_drvdata(phy);
90*fe4bc1a0SZe Huang 	struct regmap *map = sphy->regmap_base;
91*fe4bc1a0SZe Huang 	u32 val;
92*fe4bc1a0SZe Huang 	int ret;
93*fe4bc1a0SZe Huang 
94*fe4bc1a0SZe Huang 	ret = clk_enable(sphy->clk);
95*fe4bc1a0SZe Huang 	if (ret) {
96*fe4bc1a0SZe Huang 		dev_err(&phy->dev, "failed to enable clock\n");
97*fe4bc1a0SZe Huang 		clk_disable(sphy->clk);
98*fe4bc1a0SZe Huang 		return ret;
99*fe4bc1a0SZe Huang 	}
100*fe4bc1a0SZe Huang 
101*fe4bc1a0SZe Huang 	/*
102*fe4bc1a0SZe Huang 	 * make sure the usb controller is not under reset process before
103*fe4bc1a0SZe Huang 	 * any configuration
104*fe4bc1a0SZe Huang 	 */
105*fe4bc1a0SZe Huang 	usleep_range(150, 200);
106*fe4bc1a0SZe Huang 
107*fe4bc1a0SZe Huang 	/* 24M ref clk */
108*fe4bc1a0SZe Huang 	val = FIELD_PREP(FDIV_REG_MASK, FDIV_REG_VAL) |
109*fe4bc1a0SZe Huang 	      FIELD_PREP(PHY_FDIV_FRACT_0_1, PHY_SEL_FREQ_24MHZ) |
110*fe4bc1a0SZe Huang 	      PHY_DIV_LOCAL_EN;
111*fe4bc1a0SZe Huang 	regmap_write(map, PHY_PLL_DIV_CFG, val);
112*fe4bc1a0SZe Huang 
113*fe4bc1a0SZe Huang 	ret = regmap_read_poll_timeout(map, PHY_RST_MODE_CTRL, val,
114*fe4bc1a0SZe Huang 				       (val & PHY_PLL_RDY),
115*fe4bc1a0SZe Huang 				       500, K1_USB2PHY_RESET_TIME_MS * 1000);
116*fe4bc1a0SZe Huang 	if (ret) {
117*fe4bc1a0SZe Huang 		dev_err(&phy->dev, "wait PLLREADY timeout\n");
118*fe4bc1a0SZe Huang 		clk_disable(sphy->clk);
119*fe4bc1a0SZe Huang 		return ret;
120*fe4bc1a0SZe Huang 	}
121*fe4bc1a0SZe Huang 
122*fe4bc1a0SZe Huang 	/* release usb2 phy internal reset and enable clock gating */
123*fe4bc1a0SZe Huang 	val = (PHY_INIT_MODE_BITS | PHY_CLK_ENABLE_BITS | PHY_DEASSERT_RST_BITS);
124*fe4bc1a0SZe Huang 	regmap_write(map, PHY_RST_MODE_CTRL, val);
125*fe4bc1a0SZe Huang 
126*fe4bc1a0SZe Huang 	val = (PHY_HSTXP_RSTN | PHY_CLK_HSTXP_EN | PHY_HSTXP_MODE);
127*fe4bc1a0SZe Huang 	regmap_write(map, PHY_HSTXP_HW_CTRL, val);
128*fe4bc1a0SZe Huang 
129*fe4bc1a0SZe Huang 	/* auto clear host disc */
130*fe4bc1a0SZe Huang 	regmap_update_bits(map, PHY_TX_HOST_CTRL, PHY_HST_DISC_AUTO_CLR,
131*fe4bc1a0SZe Huang 			   PHY_HST_DISC_AUTO_CLR);
132*fe4bc1a0SZe Huang 
133*fe4bc1a0SZe Huang 	return 0;
134*fe4bc1a0SZe Huang }
135*fe4bc1a0SZe Huang 
136*fe4bc1a0SZe Huang static int spacemit_usb2phy_exit(struct phy *phy)
137*fe4bc1a0SZe Huang {
138*fe4bc1a0SZe Huang 	struct spacemit_usb2phy *sphy = phy_get_drvdata(phy);
139*fe4bc1a0SZe Huang 
140*fe4bc1a0SZe Huang 	clk_disable(sphy->clk);
141*fe4bc1a0SZe Huang 
142*fe4bc1a0SZe Huang 	return 0;
143*fe4bc1a0SZe Huang }
144*fe4bc1a0SZe Huang 
145*fe4bc1a0SZe Huang static const struct phy_ops spacemit_usb2phy_ops = {
146*fe4bc1a0SZe Huang 	.init = spacemit_usb2phy_init,
147*fe4bc1a0SZe Huang 	.exit = spacemit_usb2phy_exit,
148*fe4bc1a0SZe Huang 	.owner = THIS_MODULE,
149*fe4bc1a0SZe Huang };
150*fe4bc1a0SZe Huang 
151*fe4bc1a0SZe Huang static int spacemit_usb2phy_probe(struct platform_device *pdev)
152*fe4bc1a0SZe Huang {
153*fe4bc1a0SZe Huang 	struct phy_provider *phy_provider;
154*fe4bc1a0SZe Huang 	struct device *dev = &pdev->dev;
155*fe4bc1a0SZe Huang 	struct spacemit_usb2phy *sphy;
156*fe4bc1a0SZe Huang 	void __iomem *base;
157*fe4bc1a0SZe Huang 
158*fe4bc1a0SZe Huang 	sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL);
159*fe4bc1a0SZe Huang 	if (!sphy)
160*fe4bc1a0SZe Huang 		return -ENOMEM;
161*fe4bc1a0SZe Huang 
162*fe4bc1a0SZe Huang 	sphy->clk = devm_clk_get_prepared(&pdev->dev, NULL);
163*fe4bc1a0SZe Huang 	if (IS_ERR(sphy->clk))
164*fe4bc1a0SZe Huang 		return dev_err_probe(dev, PTR_ERR(sphy->clk), "Failed to get clock\n");
165*fe4bc1a0SZe Huang 
166*fe4bc1a0SZe Huang 	base = devm_platform_ioremap_resource(pdev, 0);
167*fe4bc1a0SZe Huang 	if (IS_ERR(base))
168*fe4bc1a0SZe Huang 		return PTR_ERR(base);
169*fe4bc1a0SZe Huang 
170*fe4bc1a0SZe Huang 	sphy->regmap_base = devm_regmap_init_mmio(dev, base, &phy_regmap_config);
171*fe4bc1a0SZe Huang 	if (IS_ERR(sphy->regmap_base))
172*fe4bc1a0SZe Huang 		return dev_err_probe(dev, PTR_ERR(sphy->regmap_base), "Failed to init regmap\n");
173*fe4bc1a0SZe Huang 
174*fe4bc1a0SZe Huang 	sphy->phy = devm_phy_create(dev, NULL, &spacemit_usb2phy_ops);
175*fe4bc1a0SZe Huang 	if (IS_ERR(sphy->phy))
176*fe4bc1a0SZe Huang 		return dev_err_probe(dev, PTR_ERR(sphy->phy), "Failed to create phy\n");
177*fe4bc1a0SZe Huang 
178*fe4bc1a0SZe Huang 	phy_set_drvdata(sphy->phy, sphy);
179*fe4bc1a0SZe Huang 	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
180*fe4bc1a0SZe Huang 
181*fe4bc1a0SZe Huang 	return PTR_ERR_OR_ZERO(phy_provider);
182*fe4bc1a0SZe Huang }
183*fe4bc1a0SZe Huang 
184*fe4bc1a0SZe Huang static const struct of_device_id spacemit_usb2phy_dt_match[] = {
185*fe4bc1a0SZe Huang 	{ .compatible = "spacemit,k1-usb2-phy", },
186*fe4bc1a0SZe Huang 	{ /* sentinel */ }
187*fe4bc1a0SZe Huang };
188*fe4bc1a0SZe Huang MODULE_DEVICE_TABLE(of, spacemit_usb2phy_dt_match);
189*fe4bc1a0SZe Huang 
190*fe4bc1a0SZe Huang static struct platform_driver spacemit_usb2_phy_driver = {
191*fe4bc1a0SZe Huang 	.probe	= spacemit_usb2phy_probe,
192*fe4bc1a0SZe Huang 	.driver = {
193*fe4bc1a0SZe Huang 		.name   = "spacemit-usb2-phy",
194*fe4bc1a0SZe Huang 		.of_match_table = spacemit_usb2phy_dt_match,
195*fe4bc1a0SZe Huang 	},
196*fe4bc1a0SZe Huang };
197*fe4bc1a0SZe Huang module_platform_driver(spacemit_usb2_phy_driver);
198*fe4bc1a0SZe Huang 
199*fe4bc1a0SZe Huang MODULE_DESCRIPTION("Spacemit USB 2.0 PHY driver");
200*fe4bc1a0SZe Huang MODULE_LICENSE("GPL");
201