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 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 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 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