xref: /linux/drivers/phy/phy-google-usb.c (revision c17ee635fd3a482b2ad2bf5e269755c2eae5f25e)
1*cbce6666SRoy Luo // SPDX-License-Identifier: GPL-2.0
2*cbce6666SRoy Luo /*
3*cbce6666SRoy Luo  * phy-google-usb.c - Google USB PHY driver
4*cbce6666SRoy Luo  *
5*cbce6666SRoy Luo  * Copyright (C) 2025, Google LLC
6*cbce6666SRoy Luo  */
7*cbce6666SRoy Luo 
8*cbce6666SRoy Luo #include <linux/bitfield.h>
9*cbce6666SRoy Luo #include <linux/cleanup.h>
10*cbce6666SRoy Luo #include <linux/clk.h>
11*cbce6666SRoy Luo #include <linux/io.h>
12*cbce6666SRoy Luo #include <linux/kernel.h>
13*cbce6666SRoy Luo #include <linux/mfd/syscon.h>
14*cbce6666SRoy Luo #include <linux/module.h>
15*cbce6666SRoy Luo #include <linux/mutex.h>
16*cbce6666SRoy Luo #include <linux/of.h>
17*cbce6666SRoy Luo #include <linux/phy/phy.h>
18*cbce6666SRoy Luo #include <linux/platform_device.h>
19*cbce6666SRoy Luo #include <linux/regmap.h>
20*cbce6666SRoy Luo #include <linux/reset.h>
21*cbce6666SRoy Luo #include <linux/usb/typec_mux.h>
22*cbce6666SRoy Luo 
23*cbce6666SRoy Luo #define USBCS_USB2PHY_CFG19_OFFSET 0x0
24*cbce6666SRoy Luo #define USBCS_USB2PHY_CFG19_PHY_CFG_PLL_FB_DIV GENMASK(19, 8)
25*cbce6666SRoy Luo 
26*cbce6666SRoy Luo #define USBCS_USB2PHY_CFG21_OFFSET 0x8
27*cbce6666SRoy Luo #define USBCS_USB2PHY_CFG21_PHY_ENABLE BIT(12)
28*cbce6666SRoy Luo #define USBCS_USB2PHY_CFG21_REF_FREQ_SEL GENMASK(15, 13)
29*cbce6666SRoy Luo #define USBCS_USB2PHY_CFG21_PHY_TX_DIG_BYPASS_SEL BIT(19)
30*cbce6666SRoy Luo 
31*cbce6666SRoy Luo #define USBCS_PHY_CFG1_OFFSET 0x28
32*cbce6666SRoy Luo #define USBCS_PHY_CFG1_SYS_VBUSVALID BIT(17)
33*cbce6666SRoy Luo 
34*cbce6666SRoy Luo enum google_usb_phy_id {
35*cbce6666SRoy Luo 	GOOGLE_USB2_PHY,
36*cbce6666SRoy Luo 	GOOGLE_USB_PHY_NUM,
37*cbce6666SRoy Luo };
38*cbce6666SRoy Luo 
39*cbce6666SRoy Luo struct google_usb_phy_instance {
40*cbce6666SRoy Luo 	struct google_usb_phy *parent;
41*cbce6666SRoy Luo 	unsigned int index;
42*cbce6666SRoy Luo 	struct phy *phy;
43*cbce6666SRoy Luo 	unsigned int num_clks;
44*cbce6666SRoy Luo 	struct clk_bulk_data *clks;
45*cbce6666SRoy Luo 	unsigned int num_rsts;
46*cbce6666SRoy Luo 	struct reset_control_bulk_data *rsts;
47*cbce6666SRoy Luo };
48*cbce6666SRoy Luo 
49*cbce6666SRoy Luo struct google_usb_phy {
50*cbce6666SRoy Luo 	struct device *dev;
51*cbce6666SRoy Luo 	struct regmap *usb_cfg_regmap;
52*cbce6666SRoy Luo 	unsigned int usb2_cfg_offset;
53*cbce6666SRoy Luo 	void __iomem *usbdp_top_base;
54*cbce6666SRoy Luo 	struct google_usb_phy_instance *insts;
55*cbce6666SRoy Luo 	/*
56*cbce6666SRoy Luo 	 * Protect phy registers from concurrent access, specifically via
57*cbce6666SRoy Luo 	 * google_usb_set_orientation callback.
58*cbce6666SRoy Luo 	 */
59*cbce6666SRoy Luo 	struct mutex phy_mutex;
60*cbce6666SRoy Luo 	struct typec_switch_dev *sw;
61*cbce6666SRoy Luo 	enum typec_orientation orientation;
62*cbce6666SRoy Luo };
63*cbce6666SRoy Luo 
64*cbce6666SRoy Luo static void set_vbus_valid(struct google_usb_phy *gphy)
65*cbce6666SRoy Luo {
66*cbce6666SRoy Luo 	u32 reg;
67*cbce6666SRoy Luo 
68*cbce6666SRoy Luo 	if (gphy->orientation == TYPEC_ORIENTATION_NONE) {
69*cbce6666SRoy Luo 		reg = readl(gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET);
70*cbce6666SRoy Luo 		reg &= ~USBCS_PHY_CFG1_SYS_VBUSVALID;
71*cbce6666SRoy Luo 		writel(reg, gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET);
72*cbce6666SRoy Luo 	} else {
73*cbce6666SRoy Luo 		reg = readl(gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET);
74*cbce6666SRoy Luo 		reg |= USBCS_PHY_CFG1_SYS_VBUSVALID;
75*cbce6666SRoy Luo 		writel(reg, gphy->usbdp_top_base + USBCS_PHY_CFG1_OFFSET);
76*cbce6666SRoy Luo 	}
77*cbce6666SRoy Luo }
78*cbce6666SRoy Luo 
79*cbce6666SRoy Luo static int google_usb_set_orientation(struct typec_switch_dev *sw,
80*cbce6666SRoy Luo 				      enum typec_orientation orientation)
81*cbce6666SRoy Luo {
82*cbce6666SRoy Luo 	struct google_usb_phy *gphy = typec_switch_get_drvdata(sw);
83*cbce6666SRoy Luo 
84*cbce6666SRoy Luo 	dev_dbg(gphy->dev, "set orientation %d\n", orientation);
85*cbce6666SRoy Luo 
86*cbce6666SRoy Luo 	gphy->orientation = orientation;
87*cbce6666SRoy Luo 
88*cbce6666SRoy Luo 	if (pm_runtime_suspended(gphy->dev))
89*cbce6666SRoy Luo 		return 0;
90*cbce6666SRoy Luo 
91*cbce6666SRoy Luo 	guard(mutex)(&gphy->phy_mutex);
92*cbce6666SRoy Luo 
93*cbce6666SRoy Luo 	set_vbus_valid(gphy);
94*cbce6666SRoy Luo 
95*cbce6666SRoy Luo 	return 0;
96*cbce6666SRoy Luo }
97*cbce6666SRoy Luo 
98*cbce6666SRoy Luo static int google_usb2_phy_init(struct phy *_phy)
99*cbce6666SRoy Luo {
100*cbce6666SRoy Luo 	struct google_usb_phy_instance *inst = phy_get_drvdata(_phy);
101*cbce6666SRoy Luo 	struct google_usb_phy *gphy = inst->parent;
102*cbce6666SRoy Luo 	u32 reg;
103*cbce6666SRoy Luo 	int ret;
104*cbce6666SRoy Luo 
105*cbce6666SRoy Luo 	dev_dbg(gphy->dev, "initializing usb2 phy\n");
106*cbce6666SRoy Luo 
107*cbce6666SRoy Luo 	guard(mutex)(&gphy->phy_mutex);
108*cbce6666SRoy Luo 
109*cbce6666SRoy Luo 	regmap_read(gphy->usb_cfg_regmap, gphy->usb2_cfg_offset + USBCS_USB2PHY_CFG21_OFFSET, &reg);
110*cbce6666SRoy Luo 	reg &= ~USBCS_USB2PHY_CFG21_PHY_TX_DIG_BYPASS_SEL;
111*cbce6666SRoy Luo 	reg &= ~USBCS_USB2PHY_CFG21_REF_FREQ_SEL;
112*cbce6666SRoy Luo 	reg |= FIELD_PREP(USBCS_USB2PHY_CFG21_REF_FREQ_SEL, 0);
113*cbce6666SRoy Luo 	regmap_write(gphy->usb_cfg_regmap, gphy->usb2_cfg_offset + USBCS_USB2PHY_CFG21_OFFSET, reg);
114*cbce6666SRoy Luo 
115*cbce6666SRoy Luo 	regmap_read(gphy->usb_cfg_regmap, gphy->usb2_cfg_offset + USBCS_USB2PHY_CFG19_OFFSET, &reg);
116*cbce6666SRoy Luo 	reg &= ~USBCS_USB2PHY_CFG19_PHY_CFG_PLL_FB_DIV;
117*cbce6666SRoy Luo 	reg |= FIELD_PREP(USBCS_USB2PHY_CFG19_PHY_CFG_PLL_FB_DIV, 368);
118*cbce6666SRoy Luo 	regmap_write(gphy->usb_cfg_regmap, gphy->usb2_cfg_offset + USBCS_USB2PHY_CFG19_OFFSET, reg);
119*cbce6666SRoy Luo 
120*cbce6666SRoy Luo 	set_vbus_valid(gphy);
121*cbce6666SRoy Luo 
122*cbce6666SRoy Luo 	ret = clk_bulk_prepare_enable(inst->num_clks, inst->clks);
123*cbce6666SRoy Luo 	if (ret)
124*cbce6666SRoy Luo 		return ret;
125*cbce6666SRoy Luo 
126*cbce6666SRoy Luo 	ret = reset_control_bulk_deassert(inst->num_rsts, inst->rsts);
127*cbce6666SRoy Luo 	if (ret) {
128*cbce6666SRoy Luo 		clk_bulk_disable_unprepare(inst->num_clks, inst->clks);
129*cbce6666SRoy Luo 		return ret;
130*cbce6666SRoy Luo 	}
131*cbce6666SRoy Luo 
132*cbce6666SRoy Luo 	regmap_read(gphy->usb_cfg_regmap, gphy->usb2_cfg_offset + USBCS_USB2PHY_CFG21_OFFSET, &reg);
133*cbce6666SRoy Luo 	reg |= USBCS_USB2PHY_CFG21_PHY_ENABLE;
134*cbce6666SRoy Luo 	regmap_write(gphy->usb_cfg_regmap, gphy->usb2_cfg_offset + USBCS_USB2PHY_CFG21_OFFSET, reg);
135*cbce6666SRoy Luo 
136*cbce6666SRoy Luo 	return 0;
137*cbce6666SRoy Luo }
138*cbce6666SRoy Luo 
139*cbce6666SRoy Luo static int google_usb2_phy_exit(struct phy *_phy)
140*cbce6666SRoy Luo {
141*cbce6666SRoy Luo 	struct google_usb_phy_instance *inst = phy_get_drvdata(_phy);
142*cbce6666SRoy Luo 	struct google_usb_phy *gphy = inst->parent;
143*cbce6666SRoy Luo 	u32 reg;
144*cbce6666SRoy Luo 
145*cbce6666SRoy Luo 	dev_dbg(gphy->dev, "exiting usb2 phy\n");
146*cbce6666SRoy Luo 
147*cbce6666SRoy Luo 	guard(mutex)(&gphy->phy_mutex);
148*cbce6666SRoy Luo 
149*cbce6666SRoy Luo 	regmap_read(gphy->usb_cfg_regmap, gphy->usb2_cfg_offset + USBCS_USB2PHY_CFG21_OFFSET, &reg);
150*cbce6666SRoy Luo 	reg &= ~USBCS_USB2PHY_CFG21_PHY_ENABLE;
151*cbce6666SRoy Luo 	regmap_write(gphy->usb_cfg_regmap, gphy->usb2_cfg_offset + USBCS_USB2PHY_CFG21_OFFSET, reg);
152*cbce6666SRoy Luo 
153*cbce6666SRoy Luo 	reset_control_bulk_assert(inst->num_rsts, inst->rsts);
154*cbce6666SRoy Luo 	clk_bulk_disable_unprepare(inst->num_clks, inst->clks);
155*cbce6666SRoy Luo 
156*cbce6666SRoy Luo 	return 0;
157*cbce6666SRoy Luo }
158*cbce6666SRoy Luo 
159*cbce6666SRoy Luo static const struct phy_ops google_usb2_phy_ops = {
160*cbce6666SRoy Luo 	.init		= google_usb2_phy_init,
161*cbce6666SRoy Luo 	.exit		= google_usb2_phy_exit,
162*cbce6666SRoy Luo };
163*cbce6666SRoy Luo 
164*cbce6666SRoy Luo static struct phy *google_usb_phy_xlate(struct device *dev,
165*cbce6666SRoy Luo 					const struct of_phandle_args *args)
166*cbce6666SRoy Luo {
167*cbce6666SRoy Luo 	struct google_usb_phy *gphy = dev_get_drvdata(dev);
168*cbce6666SRoy Luo 
169*cbce6666SRoy Luo 	if (args->args[0] >= GOOGLE_USB_PHY_NUM) {
170*cbce6666SRoy Luo 		dev_err(dev, "invalid PHY index requested from DT\n");
171*cbce6666SRoy Luo 		return ERR_PTR(-ENODEV);
172*cbce6666SRoy Luo 	}
173*cbce6666SRoy Luo 	return gphy->insts[args->args[0]].phy;
174*cbce6666SRoy Luo }
175*cbce6666SRoy Luo 
176*cbce6666SRoy Luo static int google_usb_phy_probe(struct platform_device *pdev)
177*cbce6666SRoy Luo {
178*cbce6666SRoy Luo 	struct typec_switch_desc sw_desc = { };
179*cbce6666SRoy Luo 	struct google_usb_phy_instance *inst;
180*cbce6666SRoy Luo 	struct phy_provider *phy_provider;
181*cbce6666SRoy Luo 	struct device *dev = &pdev->dev;
182*cbce6666SRoy Luo 	struct google_usb_phy *gphy;
183*cbce6666SRoy Luo 	struct phy *phy;
184*cbce6666SRoy Luo 	u32 args[1];
185*cbce6666SRoy Luo 	int ret;
186*cbce6666SRoy Luo 
187*cbce6666SRoy Luo 	gphy = devm_kzalloc(dev, sizeof(*gphy), GFP_KERNEL);
188*cbce6666SRoy Luo 	if (!gphy)
189*cbce6666SRoy Luo 		return -ENOMEM;
190*cbce6666SRoy Luo 
191*cbce6666SRoy Luo 	dev_set_drvdata(dev, gphy);
192*cbce6666SRoy Luo 	gphy->dev = dev;
193*cbce6666SRoy Luo 
194*cbce6666SRoy Luo 	ret = devm_mutex_init(dev, &gphy->phy_mutex);
195*cbce6666SRoy Luo 	if (ret)
196*cbce6666SRoy Luo 		return ret;
197*cbce6666SRoy Luo 
198*cbce6666SRoy Luo 	gphy->usb_cfg_regmap =
199*cbce6666SRoy Luo 		syscon_regmap_lookup_by_phandle_args(dev->of_node,
200*cbce6666SRoy Luo 						     "google,usb-cfg-csr",
201*cbce6666SRoy Luo 						     ARRAY_SIZE(args), args);
202*cbce6666SRoy Luo 	if (IS_ERR(gphy->usb_cfg_regmap)) {
203*cbce6666SRoy Luo 		return dev_err_probe(dev, PTR_ERR(gphy->usb_cfg_regmap),
204*cbce6666SRoy Luo 				     "invalid usb cfg csr\n");
205*cbce6666SRoy Luo 	}
206*cbce6666SRoy Luo 
207*cbce6666SRoy Luo 	gphy->usb2_cfg_offset = args[0];
208*cbce6666SRoy Luo 
209*cbce6666SRoy Luo 	gphy->usbdp_top_base = devm_platform_ioremap_resource_byname(pdev,
210*cbce6666SRoy Luo 								     "usbdp_top");
211*cbce6666SRoy Luo 	if (IS_ERR(gphy->usbdp_top_base))
212*cbce6666SRoy Luo 		return dev_err_probe(dev, PTR_ERR(gphy->usbdp_top_base),
213*cbce6666SRoy Luo 				    "invalid usbdp top\n");
214*cbce6666SRoy Luo 
215*cbce6666SRoy Luo 	gphy->insts = devm_kcalloc(dev, GOOGLE_USB_PHY_NUM, sizeof(*gphy->insts), GFP_KERNEL);
216*cbce6666SRoy Luo 	if (!gphy->insts)
217*cbce6666SRoy Luo 		return -ENOMEM;
218*cbce6666SRoy Luo 
219*cbce6666SRoy Luo 	inst = &gphy->insts[GOOGLE_USB2_PHY];
220*cbce6666SRoy Luo 	inst->parent = gphy;
221*cbce6666SRoy Luo 	inst->index = GOOGLE_USB2_PHY;
222*cbce6666SRoy Luo 	phy = devm_phy_create(dev, NULL, &google_usb2_phy_ops);
223*cbce6666SRoy Luo 	if (IS_ERR(phy))
224*cbce6666SRoy Luo 		return dev_err_probe(dev, PTR_ERR(phy),
225*cbce6666SRoy Luo 				     "failed to create usb2 phy instance\n");
226*cbce6666SRoy Luo 	inst->phy = phy;
227*cbce6666SRoy Luo 	phy_set_drvdata(phy, inst);
228*cbce6666SRoy Luo 
229*cbce6666SRoy Luo 	inst->num_clks = 2;
230*cbce6666SRoy Luo 	inst->clks = devm_kcalloc(dev, inst->num_clks, sizeof(*inst->clks), GFP_KERNEL);
231*cbce6666SRoy Luo 	if (!inst->clks)
232*cbce6666SRoy Luo 		return -ENOMEM;
233*cbce6666SRoy Luo 	inst->clks[0].id = "usb2";
234*cbce6666SRoy Luo 	inst->clks[1].id = "usb2_apb";
235*cbce6666SRoy Luo 	ret = devm_clk_bulk_get(dev, inst->num_clks, inst->clks);
236*cbce6666SRoy Luo 	if (ret)
237*cbce6666SRoy Luo 		return dev_err_probe(dev, ret, "failed to get u2 phy clks\n");
238*cbce6666SRoy Luo 
239*cbce6666SRoy Luo 	inst->num_rsts = 2;
240*cbce6666SRoy Luo 	inst->rsts = devm_kcalloc(dev, inst->num_rsts, sizeof(*inst->rsts), GFP_KERNEL);
241*cbce6666SRoy Luo 	if (!inst->rsts)
242*cbce6666SRoy Luo 		return -ENOMEM;
243*cbce6666SRoy Luo 	inst->rsts[0].id = "usb2";
244*cbce6666SRoy Luo 	inst->rsts[1].id = "usb2_apb";
245*cbce6666SRoy Luo 	ret = devm_reset_control_bulk_get_exclusive(dev, inst->num_rsts, inst->rsts);
246*cbce6666SRoy Luo 	if (ret)
247*cbce6666SRoy Luo 		return dev_err_probe(dev, ret, "failed to get u2 phy resets\n");
248*cbce6666SRoy Luo 
249*cbce6666SRoy Luo 	phy_provider = devm_of_phy_provider_register(dev, google_usb_phy_xlate);
250*cbce6666SRoy Luo 	if (IS_ERR(phy_provider))
251*cbce6666SRoy Luo 		return dev_err_probe(dev, PTR_ERR(phy_provider),
252*cbce6666SRoy Luo 				     "failed to register phy provider\n");
253*cbce6666SRoy Luo 
254*cbce6666SRoy Luo 	pm_runtime_enable(dev);
255*cbce6666SRoy Luo 
256*cbce6666SRoy Luo 	sw_desc.fwnode = dev_fwnode(dev);
257*cbce6666SRoy Luo 	sw_desc.drvdata = gphy;
258*cbce6666SRoy Luo 	sw_desc.name = fwnode_get_name(dev_fwnode(dev));
259*cbce6666SRoy Luo 	sw_desc.set = google_usb_set_orientation;
260*cbce6666SRoy Luo 
261*cbce6666SRoy Luo 	gphy->sw = typec_switch_register(dev, &sw_desc);
262*cbce6666SRoy Luo 	if (IS_ERR(gphy->sw))
263*cbce6666SRoy Luo 		return dev_err_probe(dev, PTR_ERR(gphy->sw),
264*cbce6666SRoy Luo 				     "failed to register typec switch\n");
265*cbce6666SRoy Luo 
266*cbce6666SRoy Luo 	return 0;
267*cbce6666SRoy Luo }
268*cbce6666SRoy Luo 
269*cbce6666SRoy Luo static void google_usb_phy_remove(struct platform_device *pdev)
270*cbce6666SRoy Luo {
271*cbce6666SRoy Luo 	struct google_usb_phy *gphy = dev_get_drvdata(&pdev->dev);
272*cbce6666SRoy Luo 
273*cbce6666SRoy Luo 	typec_switch_unregister(gphy->sw);
274*cbce6666SRoy Luo 	pm_runtime_disable(&pdev->dev);
275*cbce6666SRoy Luo }
276*cbce6666SRoy Luo 
277*cbce6666SRoy Luo static const struct of_device_id google_usb_phy_of_match[] = {
278*cbce6666SRoy Luo 	{
279*cbce6666SRoy Luo 		.compatible = "google,lga-usb-phy",
280*cbce6666SRoy Luo 	},
281*cbce6666SRoy Luo 	{ }
282*cbce6666SRoy Luo };
283*cbce6666SRoy Luo MODULE_DEVICE_TABLE(of, google_usb_phy_of_match);
284*cbce6666SRoy Luo 
285*cbce6666SRoy Luo static struct platform_driver google_usb_phy = {
286*cbce6666SRoy Luo 	.probe	= google_usb_phy_probe,
287*cbce6666SRoy Luo 	.remove = google_usb_phy_remove,
288*cbce6666SRoy Luo 	.driver = {
289*cbce6666SRoy Luo 		.name		= "google-usb-phy",
290*cbce6666SRoy Luo 		.of_match_table	= google_usb_phy_of_match,
291*cbce6666SRoy Luo 	}
292*cbce6666SRoy Luo };
293*cbce6666SRoy Luo 
294*cbce6666SRoy Luo module_platform_driver(google_usb_phy);
295*cbce6666SRoy Luo MODULE_LICENSE("GPL");
296*cbce6666SRoy Luo MODULE_DESCRIPTION("Google USB phy driver");
297