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