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
14struct 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
22static 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
40static 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
58static 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
64static 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
92static const struct of_device_id ath79_usb_phy_of_match[] = {
93	{ .compatible = "qca,ar7100-usb-phy" },
94	{}
95};
96MODULE_DEVICE_TABLE(of, ath79_usb_phy_of_match);
97
98static 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};
105module_platform_driver(ath79_usb_phy_driver);
106
107MODULE_DESCRIPTION("ATH79 USB PHY driver");
108MODULE_AUTHOR("Alban Bedel <albeu@free.fr>");
109MODULE_LICENSE("GPL");
110