1cd3bf368SAlban Bedel // SPDX-License-Identifier: GPL-2.0+
2cd3bf368SAlban Bedel /*
3cd3bf368SAlban Bedel * Atheros AR71XX/9XXX USB PHY driver
4cd3bf368SAlban Bedel *
5cd3bf368SAlban Bedel * Copyright (C) 2015-2018 Alban Bedel <albeu@free.fr>
6cd3bf368SAlban Bedel */
7cd3bf368SAlban Bedel
8*7559e757SRob Herring #include <linux/mod_devicetable.h>
9cd3bf368SAlban Bedel #include <linux/module.h>
10cd3bf368SAlban Bedel #include <linux/platform_device.h>
11cd3bf368SAlban Bedel #include <linux/phy/phy.h>
12cd3bf368SAlban Bedel #include <linux/reset.h>
13cd3bf368SAlban Bedel
14cd3bf368SAlban Bedel struct ath79_usb_phy {
15cd3bf368SAlban Bedel struct reset_control *reset;
16cd3bf368SAlban Bedel /* The suspend override logic is inverted, hence the no prefix
17cd3bf368SAlban Bedel * to make the code a bit easier to understand.
18cd3bf368SAlban Bedel */
19cd3bf368SAlban Bedel struct reset_control *no_suspend_override;
20cd3bf368SAlban Bedel };
21cd3bf368SAlban Bedel
ath79_usb_phy_power_on(struct phy * phy)22cd3bf368SAlban Bedel static int ath79_usb_phy_power_on(struct phy *phy)
23cd3bf368SAlban Bedel {
24cd3bf368SAlban Bedel struct ath79_usb_phy *priv = phy_get_drvdata(phy);
25cd3bf368SAlban Bedel int err = 0;
26cd3bf368SAlban Bedel
27cd3bf368SAlban Bedel if (priv->no_suspend_override) {
28cd3bf368SAlban Bedel err = reset_control_assert(priv->no_suspend_override);
29cd3bf368SAlban Bedel if (err)
30cd3bf368SAlban Bedel return err;
31cd3bf368SAlban Bedel }
32cd3bf368SAlban Bedel
33cd3bf368SAlban Bedel err = reset_control_deassert(priv->reset);
34cd3bf368SAlban Bedel if (err && priv->no_suspend_override)
3500980815SAlban Bedel reset_control_deassert(priv->no_suspend_override);
36cd3bf368SAlban Bedel
37cd3bf368SAlban Bedel return err;
38cd3bf368SAlban Bedel }
39cd3bf368SAlban Bedel
ath79_usb_phy_power_off(struct phy * phy)40cd3bf368SAlban Bedel static int ath79_usb_phy_power_off(struct phy *phy)
41cd3bf368SAlban Bedel {
42cd3bf368SAlban Bedel struct ath79_usb_phy *priv = phy_get_drvdata(phy);
43cd3bf368SAlban Bedel int err = 0;
44cd3bf368SAlban Bedel
45cd3bf368SAlban Bedel err = reset_control_assert(priv->reset);
46cd3bf368SAlban Bedel if (err)
47cd3bf368SAlban Bedel return err;
48cd3bf368SAlban Bedel
49cd3bf368SAlban Bedel if (priv->no_suspend_override) {
50cd3bf368SAlban Bedel err = reset_control_deassert(priv->no_suspend_override);
51cd3bf368SAlban Bedel if (err)
52cd3bf368SAlban Bedel reset_control_deassert(priv->reset);
53cd3bf368SAlban Bedel }
54cd3bf368SAlban Bedel
55cd3bf368SAlban Bedel return err;
56cd3bf368SAlban Bedel }
57cd3bf368SAlban Bedel
58cd3bf368SAlban Bedel static const struct phy_ops ath79_usb_phy_ops = {
59cd3bf368SAlban Bedel .power_on = ath79_usb_phy_power_on,
60cd3bf368SAlban Bedel .power_off = ath79_usb_phy_power_off,
61cd3bf368SAlban Bedel .owner = THIS_MODULE,
62cd3bf368SAlban Bedel };
63cd3bf368SAlban Bedel
ath79_usb_phy_probe(struct platform_device * pdev)64cd3bf368SAlban Bedel static int ath79_usb_phy_probe(struct platform_device *pdev)
65cd3bf368SAlban Bedel {
66cd3bf368SAlban Bedel struct ath79_usb_phy *priv;
67cd3bf368SAlban Bedel struct phy *phy;
68cd3bf368SAlban Bedel
69cd3bf368SAlban Bedel priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
70cd3bf368SAlban Bedel if (!priv)
71cd3bf368SAlban Bedel return -ENOMEM;
72cd3bf368SAlban Bedel
73827cb032SAlban Bedel priv->reset = devm_reset_control_get(&pdev->dev, "phy");
74cd3bf368SAlban Bedel if (IS_ERR(priv->reset))
75cd3bf368SAlban Bedel return PTR_ERR(priv->reset);
76cd3bf368SAlban Bedel
77cd3bf368SAlban Bedel priv->no_suspend_override = devm_reset_control_get_optional(
78cd3bf368SAlban Bedel &pdev->dev, "usb-suspend-override");
79cd3bf368SAlban Bedel if (IS_ERR(priv->no_suspend_override))
80cd3bf368SAlban Bedel return PTR_ERR(priv->no_suspend_override);
81cd3bf368SAlban Bedel
82cd3bf368SAlban Bedel phy = devm_phy_create(&pdev->dev, NULL, &ath79_usb_phy_ops);
83cd3bf368SAlban Bedel if (IS_ERR(phy))
84cd3bf368SAlban Bedel return PTR_ERR(phy);
85cd3bf368SAlban Bedel
86cd3bf368SAlban Bedel phy_set_drvdata(phy, priv);
87cd3bf368SAlban Bedel
88cd3bf368SAlban Bedel return PTR_ERR_OR_ZERO(devm_of_phy_provider_register(
89cd3bf368SAlban Bedel &pdev->dev, of_phy_simple_xlate));
90cd3bf368SAlban Bedel }
91cd3bf368SAlban Bedel
92cd3bf368SAlban Bedel static const struct of_device_id ath79_usb_phy_of_match[] = {
93cd3bf368SAlban Bedel { .compatible = "qca,ar7100-usb-phy" },
94cd3bf368SAlban Bedel {}
95cd3bf368SAlban Bedel };
96cd3bf368SAlban Bedel MODULE_DEVICE_TABLE(of, ath79_usb_phy_of_match);
97cd3bf368SAlban Bedel
98cd3bf368SAlban Bedel static struct platform_driver ath79_usb_phy_driver = {
99cd3bf368SAlban Bedel .probe = ath79_usb_phy_probe,
100cd3bf368SAlban Bedel .driver = {
101cd3bf368SAlban Bedel .of_match_table = ath79_usb_phy_of_match,
102cd3bf368SAlban Bedel .name = "ath79-usb-phy",
103cd3bf368SAlban Bedel }
104cd3bf368SAlban Bedel };
105cd3bf368SAlban Bedel module_platform_driver(ath79_usb_phy_driver);
106cd3bf368SAlban Bedel
107cd3bf368SAlban Bedel MODULE_DESCRIPTION("ATH79 USB PHY driver");
108cd3bf368SAlban Bedel MODULE_AUTHOR("Alban Bedel <albeu@free.fr>");
109cd3bf368SAlban Bedel MODULE_LICENSE("GPL");
110