xref: /linux/drivers/phy/ti/phy-da8xx-usb.c (revision c771600c6af14749609b49565ffb4cac2959710d)
18e8e69d6SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f2e60041SDavid Lechner /*
3f2e60041SDavid Lechner  * phy-da8xx-usb - TI DaVinci DA8xx USB PHY driver
4f2e60041SDavid Lechner  *
5f2e60041SDavid Lechner  * Copyright (C) 2016 David Lechner <david@lechnology.com>
6f2e60041SDavid Lechner  */
7f2e60041SDavid Lechner 
8f2e60041SDavid Lechner #include <linux/clk.h>
9f2e60041SDavid Lechner #include <linux/io.h>
10f2e60041SDavid Lechner #include <linux/of.h>
11f2e60041SDavid Lechner #include <linux/mfd/da8xx-cfgchip.h>
12f2e60041SDavid Lechner #include <linux/mfd/syscon.h>
13f2e60041SDavid Lechner #include <linux/module.h>
14f2e60041SDavid Lechner #include <linux/phy/phy.h>
15bdec5a6bSDavid Lechner #include <linux/platform_data/phy-da8xx-usb.h>
16f2e60041SDavid Lechner #include <linux/platform_device.h>
17ee8e41b5SBastien Curutchet #include <linux/pm_runtime.h>
18f2e60041SDavid Lechner #include <linux/regmap.h>
19f2e60041SDavid Lechner 
2023e9b38dSAlexandre Bailon #define PHY_INIT_BITS	(CFGCHIP2_SESENDEN | CFGCHIP2_VBDTCTEN)
2123e9b38dSAlexandre Bailon 
22f2e60041SDavid Lechner struct da8xx_usb_phy {
23ee8e41b5SBastien Curutchet 	struct device		*dev;
24f2e60041SDavid Lechner 	struct phy_provider	*phy_provider;
25f2e60041SDavid Lechner 	struct phy		*usb11_phy;
26f2e60041SDavid Lechner 	struct phy		*usb20_phy;
27f2e60041SDavid Lechner 	struct clk		*usb11_clk;
28f2e60041SDavid Lechner 	struct clk		*usb20_clk;
29f2e60041SDavid Lechner 	struct regmap		*regmap;
30f2e60041SDavid Lechner };
31f2e60041SDavid Lechner 
da8xx_usb11_phy_power_on(struct phy * phy)32f2e60041SDavid Lechner static int da8xx_usb11_phy_power_on(struct phy *phy)
33f2e60041SDavid Lechner {
34f2e60041SDavid Lechner 	struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);
35f2e60041SDavid Lechner 	int ret;
36f2e60041SDavid Lechner 
37f2e60041SDavid Lechner 	ret = clk_prepare_enable(d_phy->usb11_clk);
38f2e60041SDavid Lechner 	if (ret)
39f2e60041SDavid Lechner 		return ret;
40f2e60041SDavid Lechner 
41f2e60041SDavid Lechner 	regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_USB1SUSPENDM,
42f2e60041SDavid Lechner 			  CFGCHIP2_USB1SUSPENDM);
43f2e60041SDavid Lechner 
44ee8e41b5SBastien Curutchet 	/*
45ee8e41b5SBastien Curutchet 	 * USB1.1 can used USB2.0 output clock as reference clock so this is here to prevent USB2.0
46ee8e41b5SBastien Curutchet 	 * from shutting PHY's power when USB1.1 might use it
47ee8e41b5SBastien Curutchet 	 */
48ee8e41b5SBastien Curutchet 	pm_runtime_get_sync(d_phy->dev);
49ee8e41b5SBastien Curutchet 
50f2e60041SDavid Lechner 	return 0;
51f2e60041SDavid Lechner }
52f2e60041SDavid Lechner 
da8xx_usb11_phy_power_off(struct phy * phy)53f2e60041SDavid Lechner static int da8xx_usb11_phy_power_off(struct phy *phy)
54f2e60041SDavid Lechner {
55f2e60041SDavid Lechner 	struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);
56f2e60041SDavid Lechner 
57f2e60041SDavid Lechner 	regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_USB1SUSPENDM, 0);
58f2e60041SDavid Lechner 
59f2e60041SDavid Lechner 	clk_disable_unprepare(d_phy->usb11_clk);
60ee8e41b5SBastien Curutchet 	pm_runtime_put_sync(d_phy->dev);
61f2e60041SDavid Lechner 
62f2e60041SDavid Lechner 	return 0;
63f2e60041SDavid Lechner }
64f2e60041SDavid Lechner 
65f2e60041SDavid Lechner static const struct phy_ops da8xx_usb11_phy_ops = {
66f2e60041SDavid Lechner 	.power_on	= da8xx_usb11_phy_power_on,
67f2e60041SDavid Lechner 	.power_off	= da8xx_usb11_phy_power_off,
68f2e60041SDavid Lechner 	.owner		= THIS_MODULE,
69f2e60041SDavid Lechner };
70f2e60041SDavid Lechner 
da8xx_usb20_phy_power_on(struct phy * phy)71f2e60041SDavid Lechner static int da8xx_usb20_phy_power_on(struct phy *phy)
72f2e60041SDavid Lechner {
73f2e60041SDavid Lechner 	struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);
74f2e60041SDavid Lechner 	int ret;
75f2e60041SDavid Lechner 
76f2e60041SDavid Lechner 	ret = clk_prepare_enable(d_phy->usb20_clk);
77f2e60041SDavid Lechner 	if (ret)
78f2e60041SDavid Lechner 		return ret;
79f2e60041SDavid Lechner 
80f2e60041SDavid Lechner 	regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGPWRDN, 0);
81f2e60041SDavid Lechner 
82f2e60041SDavid Lechner 	return 0;
83f2e60041SDavid Lechner }
84f2e60041SDavid Lechner 
da8xx_usb20_phy_power_off(struct phy * phy)85f2e60041SDavid Lechner static int da8xx_usb20_phy_power_off(struct phy *phy)
86f2e60041SDavid Lechner {
87f2e60041SDavid Lechner 	struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);
88f2e60041SDavid Lechner 
89f2e60041SDavid Lechner 	regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGPWRDN,
90f2e60041SDavid Lechner 			  CFGCHIP2_OTGPWRDN);
91f2e60041SDavid Lechner 
92f2e60041SDavid Lechner 	clk_disable_unprepare(d_phy->usb20_clk);
93f2e60041SDavid Lechner 
94f2e60041SDavid Lechner 	return 0;
95f2e60041SDavid Lechner }
96f2e60041SDavid Lechner 
da8xx_usb20_phy_set_mode(struct phy * phy,enum phy_mode mode,int submode)9779a5a18aSGrygorii Strashko static int da8xx_usb20_phy_set_mode(struct phy *phy,
9879a5a18aSGrygorii Strashko 				    enum phy_mode mode, int submode)
99f2e60041SDavid Lechner {
100f2e60041SDavid Lechner 	struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy);
101f2e60041SDavid Lechner 	u32 val;
102f2e60041SDavid Lechner 
103f2e60041SDavid Lechner 	switch (mode) {
104f2e60041SDavid Lechner 	case PHY_MODE_USB_HOST:		/* Force VBUS valid, ID = 0 */
105f2e60041SDavid Lechner 		val = CFGCHIP2_OTGMODE_FORCE_HOST;
106f2e60041SDavid Lechner 		break;
107f2e60041SDavid Lechner 	case PHY_MODE_USB_DEVICE:	/* Force VBUS valid, ID = 1 */
108f2e60041SDavid Lechner 		val = CFGCHIP2_OTGMODE_FORCE_DEVICE;
109f2e60041SDavid Lechner 		break;
110f2e60041SDavid Lechner 	case PHY_MODE_USB_OTG:	/* Don't override the VBUS/ID comparators */
111f2e60041SDavid Lechner 		val = CFGCHIP2_OTGMODE_NO_OVERRIDE;
112f2e60041SDavid Lechner 		break;
113f2e60041SDavid Lechner 	default:
114f2e60041SDavid Lechner 		return -EINVAL;
115f2e60041SDavid Lechner 	}
116f2e60041SDavid Lechner 
117f2e60041SDavid Lechner 	regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGMODE_MASK,
118f2e60041SDavid Lechner 			  val);
119f2e60041SDavid Lechner 
120f2e60041SDavid Lechner 	return 0;
121f2e60041SDavid Lechner }
122f2e60041SDavid Lechner 
123f2e60041SDavid Lechner static const struct phy_ops da8xx_usb20_phy_ops = {
124f2e60041SDavid Lechner 	.power_on	= da8xx_usb20_phy_power_on,
125f2e60041SDavid Lechner 	.power_off	= da8xx_usb20_phy_power_off,
126f2e60041SDavid Lechner 	.set_mode	= da8xx_usb20_phy_set_mode,
127f2e60041SDavid Lechner 	.owner		= THIS_MODULE,
128f2e60041SDavid Lechner };
129f2e60041SDavid Lechner 
da8xx_runtime_suspend(struct device * dev)130ee8e41b5SBastien Curutchet static int __maybe_unused da8xx_runtime_suspend(struct device *dev)
131ee8e41b5SBastien Curutchet {
132ee8e41b5SBastien Curutchet 	struct da8xx_usb_phy *d_phy = dev_get_drvdata(dev);
133ee8e41b5SBastien Curutchet 
134ee8e41b5SBastien Curutchet 	dev_dbg(dev, "Suspending ...\n");
135ee8e41b5SBastien Curutchet 
136ee8e41b5SBastien Curutchet 	regmap_set_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_PHYPWRDN | CFGCHIP2_OTGPWRDN);
137ee8e41b5SBastien Curutchet 
138ee8e41b5SBastien Curutchet 	return 0;
139ee8e41b5SBastien Curutchet }
140ee8e41b5SBastien Curutchet 
da8xx_runtime_resume(struct device * dev)141ee8e41b5SBastien Curutchet static int __maybe_unused da8xx_runtime_resume(struct device *dev)
142ee8e41b5SBastien Curutchet {
143ee8e41b5SBastien Curutchet 	u32 mask = CFGCHIP2_RESET | CFGCHIP2_PHYPWRDN | CFGCHIP2_OTGPWRDN | CFGCHIP2_PHY_PLLON;
144ee8e41b5SBastien Curutchet 	struct da8xx_usb_phy *d_phy = dev_get_drvdata(dev);
145ee8e41b5SBastien Curutchet 	u32 pll_status;
146ee8e41b5SBastien Curutchet 
147ee8e41b5SBastien Curutchet 	regmap_update_bits(d_phy->regmap, CFGCHIP(2), mask, CFGCHIP2_PHY_PLLON);
148ee8e41b5SBastien Curutchet 
149ee8e41b5SBastien Curutchet 	dev_dbg(dev, "Resuming ...\n");
150ee8e41b5SBastien Curutchet 
151ee8e41b5SBastien Curutchet 	return regmap_read_poll_timeout(d_phy->regmap, CFGCHIP(2), pll_status,
152ee8e41b5SBastien Curutchet 					pll_status & CFGCHIP2_PHYCLKGD, 1000, 500000);
153ee8e41b5SBastien Curutchet }
154ee8e41b5SBastien Curutchet 
155ee8e41b5SBastien Curutchet static const struct dev_pm_ops da8xx_usb_phy_pm_ops = {
156ee8e41b5SBastien Curutchet 	SET_RUNTIME_PM_OPS(da8xx_runtime_suspend, da8xx_runtime_resume, NULL)
157ee8e41b5SBastien Curutchet };
158ee8e41b5SBastien Curutchet 
da8xx_usb_phy_of_xlate(struct device * dev,const struct of_phandle_args * args)159f2e60041SDavid Lechner static struct phy *da8xx_usb_phy_of_xlate(struct device *dev,
16000ca8a15SKrzysztof Kozlowski 					 const struct of_phandle_args *args)
161f2e60041SDavid Lechner {
162f2e60041SDavid Lechner 	struct da8xx_usb_phy *d_phy = dev_get_drvdata(dev);
163f2e60041SDavid Lechner 
164f2e60041SDavid Lechner 	if (!d_phy)
165f2e60041SDavid Lechner 		return ERR_PTR(-ENODEV);
166f2e60041SDavid Lechner 
167f2e60041SDavid Lechner 	switch (args->args[0]) {
168f2e60041SDavid Lechner 	case 0:
169f2e60041SDavid Lechner 		return d_phy->usb20_phy;
170f2e60041SDavid Lechner 	case 1:
171f2e60041SDavid Lechner 		return d_phy->usb11_phy;
172f2e60041SDavid Lechner 	default:
173f2e60041SDavid Lechner 		return ERR_PTR(-EINVAL);
174f2e60041SDavid Lechner 	}
175f2e60041SDavid Lechner }
176f2e60041SDavid Lechner 
da8xx_usb_phy_probe(struct platform_device * pdev)177f2e60041SDavid Lechner static int da8xx_usb_phy_probe(struct platform_device *pdev)
178f2e60041SDavid Lechner {
179f2e60041SDavid Lechner 	struct device		*dev = &pdev->dev;
180bdec5a6bSDavid Lechner 	struct da8xx_usb_phy_platform_data *pdata = dev->platform_data;
181f2e60041SDavid Lechner 	struct device_node	*node = dev->of_node;
182f2e60041SDavid Lechner 	struct da8xx_usb_phy	*d_phy;
183f2e60041SDavid Lechner 
184f2e60041SDavid Lechner 	d_phy = devm_kzalloc(dev, sizeof(*d_phy), GFP_KERNEL);
185f2e60041SDavid Lechner 	if (!d_phy)
186f2e60041SDavid Lechner 		return -ENOMEM;
187f2e60041SDavid Lechner 
188ee8e41b5SBastien Curutchet 	d_phy->dev = dev;
189ee8e41b5SBastien Curutchet 
190bdec5a6bSDavid Lechner 	if (pdata)
191bdec5a6bSDavid Lechner 		d_phy->regmap = pdata->cfgchip;
192bdec5a6bSDavid Lechner 	else
193f2e60041SDavid Lechner 		d_phy->regmap = syscon_regmap_lookup_by_compatible(
194f2e60041SDavid Lechner 							"ti,da830-cfgchip");
195f2e60041SDavid Lechner 	if (IS_ERR(d_phy->regmap)) {
196f2e60041SDavid Lechner 		dev_err(dev, "Failed to get syscon\n");
197f2e60041SDavid Lechner 		return PTR_ERR(d_phy->regmap);
198f2e60041SDavid Lechner 	}
199f2e60041SDavid Lechner 
200e98bbbf3SDavid Lechner 	d_phy->usb11_clk = devm_clk_get(dev, "usb1_clk48");
201f2e60041SDavid Lechner 	if (IS_ERR(d_phy->usb11_clk)) {
202e98bbbf3SDavid Lechner 		dev_err(dev, "Failed to get usb1_clk48\n");
203f2e60041SDavid Lechner 		return PTR_ERR(d_phy->usb11_clk);
204f2e60041SDavid Lechner 	}
205f2e60041SDavid Lechner 
206e98bbbf3SDavid Lechner 	d_phy->usb20_clk = devm_clk_get(dev, "usb0_clk48");
207f2e60041SDavid Lechner 	if (IS_ERR(d_phy->usb20_clk)) {
208e98bbbf3SDavid Lechner 		dev_err(dev, "Failed to get usb0_clk48\n");
209f2e60041SDavid Lechner 		return PTR_ERR(d_phy->usb20_clk);
210f2e60041SDavid Lechner 	}
211f2e60041SDavid Lechner 
212f2e60041SDavid Lechner 	d_phy->usb11_phy = devm_phy_create(dev, node, &da8xx_usb11_phy_ops);
213f2e60041SDavid Lechner 	if (IS_ERR(d_phy->usb11_phy)) {
214f2e60041SDavid Lechner 		dev_err(dev, "Failed to create usb11 phy\n");
215f2e60041SDavid Lechner 		return PTR_ERR(d_phy->usb11_phy);
216f2e60041SDavid Lechner 	}
217f2e60041SDavid Lechner 
218f2e60041SDavid Lechner 	d_phy->usb20_phy = devm_phy_create(dev, node, &da8xx_usb20_phy_ops);
219f2e60041SDavid Lechner 	if (IS_ERR(d_phy->usb20_phy)) {
220f2e60041SDavid Lechner 		dev_err(dev, "Failed to create usb20 phy\n");
221f2e60041SDavid Lechner 		return PTR_ERR(d_phy->usb20_phy);
222f2e60041SDavid Lechner 	}
223f2e60041SDavid Lechner 
224f2e60041SDavid Lechner 	platform_set_drvdata(pdev, d_phy);
225f2e60041SDavid Lechner 	phy_set_drvdata(d_phy->usb11_phy, d_phy);
226f2e60041SDavid Lechner 	phy_set_drvdata(d_phy->usb20_phy, d_phy);
227f2e60041SDavid Lechner 
228f2e60041SDavid Lechner 	if (node) {
229f2e60041SDavid Lechner 		d_phy->phy_provider = devm_of_phy_provider_register(dev,
230f2e60041SDavid Lechner 							da8xx_usb_phy_of_xlate);
231f2e60041SDavid Lechner 		if (IS_ERR(d_phy->phy_provider)) {
232f2e60041SDavid Lechner 			dev_err(dev, "Failed to create phy provider\n");
233f2e60041SDavid Lechner 			return PTR_ERR(d_phy->phy_provider);
234f2e60041SDavid Lechner 		}
235f2e60041SDavid Lechner 	} else {
236f2e60041SDavid Lechner 		int ret;
237f2e60041SDavid Lechner 
23876632542SAxel Haslam 		ret = phy_create_lookup(d_phy->usb11_phy, "usb-phy",
23976632542SAxel Haslam 					"ohci-da8xx");
240f2e60041SDavid Lechner 		if (ret)
241f2e60041SDavid Lechner 			dev_warn(dev, "Failed to create usb11 phy lookup\n");
242f2e60041SDavid Lechner 		ret = phy_create_lookup(d_phy->usb20_phy, "usb-phy",
243f2e60041SDavid Lechner 					"musb-da8xx");
244f2e60041SDavid Lechner 		if (ret)
245f2e60041SDavid Lechner 			dev_warn(dev, "Failed to create usb20 phy lookup\n");
246f2e60041SDavid Lechner 	}
247f2e60041SDavid Lechner 
24823e9b38dSAlexandre Bailon 	regmap_write_bits(d_phy->regmap, CFGCHIP(2),
24923e9b38dSAlexandre Bailon 			  PHY_INIT_BITS, PHY_INIT_BITS);
25023e9b38dSAlexandre Bailon 
251ee8e41b5SBastien Curutchet 	pm_runtime_set_active(dev);
252ee8e41b5SBastien Curutchet 	devm_pm_runtime_enable(dev);
253ee8e41b5SBastien Curutchet 	/*
254ee8e41b5SBastien Curutchet 	 * Prevent runtime pm from being ON by default. Users can enable
255ee8e41b5SBastien Curutchet 	 * it using power/control in sysfs.
256ee8e41b5SBastien Curutchet 	 */
257ee8e41b5SBastien Curutchet 	pm_runtime_forbid(dev);
258ee8e41b5SBastien Curutchet 
259f2e60041SDavid Lechner 	return 0;
260f2e60041SDavid Lechner }
261f2e60041SDavid Lechner 
da8xx_usb_phy_remove(struct platform_device * pdev)2620196c847SUwe Kleine-König static void da8xx_usb_phy_remove(struct platform_device *pdev)
263f2e60041SDavid Lechner {
264f2e60041SDavid Lechner 	struct da8xx_usb_phy *d_phy = platform_get_drvdata(pdev);
265f2e60041SDavid Lechner 
266f2e60041SDavid Lechner 	if (!pdev->dev.of_node) {
267f2e60041SDavid Lechner 		phy_remove_lookup(d_phy->usb20_phy, "usb-phy", "musb-da8xx");
26876632542SAxel Haslam 		phy_remove_lookup(d_phy->usb11_phy, "usb-phy", "ohci-da8xx");
269f2e60041SDavid Lechner 	}
270f2e60041SDavid Lechner }
271f2e60041SDavid Lechner 
272f2e60041SDavid Lechner static const struct of_device_id da8xx_usb_phy_ids[] = {
273f2e60041SDavid Lechner 	{ .compatible = "ti,da830-usb-phy" },
274f2e60041SDavid Lechner 	{ }
275f2e60041SDavid Lechner };
276f2e60041SDavid Lechner MODULE_DEVICE_TABLE(of, da8xx_usb_phy_ids);
277f2e60041SDavid Lechner 
278f2e60041SDavid Lechner static struct platform_driver da8xx_usb_phy_driver = {
279f2e60041SDavid Lechner 	.probe	= da8xx_usb_phy_probe,
280*54234e3aSUwe Kleine-König 	.remove	= da8xx_usb_phy_remove,
281f2e60041SDavid Lechner 	.driver	= {
282f2e60041SDavid Lechner 		.name	= "da8xx-usb-phy",
283ee8e41b5SBastien Curutchet 		.pm	= &da8xx_usb_phy_pm_ops,
284f2e60041SDavid Lechner 		.of_match_table	= da8xx_usb_phy_ids,
285f2e60041SDavid Lechner 	},
286f2e60041SDavid Lechner };
287f2e60041SDavid Lechner 
288f2e60041SDavid Lechner module_platform_driver(da8xx_usb_phy_driver);
289f2e60041SDavid Lechner 
290f2e60041SDavid Lechner MODULE_ALIAS("platform:da8xx-usb-phy");
291f2e60041SDavid Lechner MODULE_AUTHOR("David Lechner <david@lechnology.com>");
292f2e60041SDavid Lechner MODULE_DESCRIPTION("TI DA8xx USB PHY driver");
293f2e60041SDavid Lechner MODULE_LICENSE("GPL v2");
294