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