18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Atheros AR71XX/9XXX USB PHY driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015-2018 Alban Bedel <albeu@free.fr> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 108c2ecf20Sopenharmony_ci#include <linux/phy/phy.h> 118c2ecf20Sopenharmony_ci#include <linux/reset.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_cistruct ath79_usb_phy { 148c2ecf20Sopenharmony_ci struct reset_control *reset; 158c2ecf20Sopenharmony_ci /* The suspend override logic is inverted, hence the no prefix 168c2ecf20Sopenharmony_ci * to make the code a bit easier to understand. 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci struct reset_control *no_suspend_override; 198c2ecf20Sopenharmony_ci}; 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic int ath79_usb_phy_power_on(struct phy *phy) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci struct ath79_usb_phy *priv = phy_get_drvdata(phy); 248c2ecf20Sopenharmony_ci int err = 0; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci if (priv->no_suspend_override) { 278c2ecf20Sopenharmony_ci err = reset_control_assert(priv->no_suspend_override); 288c2ecf20Sopenharmony_ci if (err) 298c2ecf20Sopenharmony_ci return err; 308c2ecf20Sopenharmony_ci } 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci err = reset_control_deassert(priv->reset); 338c2ecf20Sopenharmony_ci if (err && priv->no_suspend_override) 348c2ecf20Sopenharmony_ci reset_control_deassert(priv->no_suspend_override); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci return err; 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic int ath79_usb_phy_power_off(struct phy *phy) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci struct ath79_usb_phy *priv = phy_get_drvdata(phy); 428c2ecf20Sopenharmony_ci int err = 0; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci err = reset_control_assert(priv->reset); 458c2ecf20Sopenharmony_ci if (err) 468c2ecf20Sopenharmony_ci return err; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci if (priv->no_suspend_override) { 498c2ecf20Sopenharmony_ci err = reset_control_deassert(priv->no_suspend_override); 508c2ecf20Sopenharmony_ci if (err) 518c2ecf20Sopenharmony_ci reset_control_deassert(priv->reset); 528c2ecf20Sopenharmony_ci } 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci return err; 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic const struct phy_ops ath79_usb_phy_ops = { 588c2ecf20Sopenharmony_ci .power_on = ath79_usb_phy_power_on, 598c2ecf20Sopenharmony_ci .power_off = ath79_usb_phy_power_off, 608c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int ath79_usb_phy_probe(struct platform_device *pdev) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct ath79_usb_phy *priv; 668c2ecf20Sopenharmony_ci struct phy *phy; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 698c2ecf20Sopenharmony_ci if (!priv) 708c2ecf20Sopenharmony_ci return -ENOMEM; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci priv->reset = devm_reset_control_get(&pdev->dev, "phy"); 738c2ecf20Sopenharmony_ci if (IS_ERR(priv->reset)) 748c2ecf20Sopenharmony_ci return PTR_ERR(priv->reset); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci priv->no_suspend_override = devm_reset_control_get_optional( 778c2ecf20Sopenharmony_ci &pdev->dev, "usb-suspend-override"); 788c2ecf20Sopenharmony_ci if (IS_ERR(priv->no_suspend_override)) 798c2ecf20Sopenharmony_ci return PTR_ERR(priv->no_suspend_override); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci phy = devm_phy_create(&pdev->dev, NULL, &ath79_usb_phy_ops); 828c2ecf20Sopenharmony_ci if (IS_ERR(phy)) 838c2ecf20Sopenharmony_ci return PTR_ERR(phy); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci phy_set_drvdata(phy, priv); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(devm_of_phy_provider_register( 888c2ecf20Sopenharmony_ci &pdev->dev, of_phy_simple_xlate)); 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic const struct of_device_id ath79_usb_phy_of_match[] = { 928c2ecf20Sopenharmony_ci { .compatible = "qca,ar7100-usb-phy" }, 938c2ecf20Sopenharmony_ci {} 948c2ecf20Sopenharmony_ci}; 958c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ath79_usb_phy_of_match); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic struct platform_driver ath79_usb_phy_driver = { 988c2ecf20Sopenharmony_ci .probe = ath79_usb_phy_probe, 998c2ecf20Sopenharmony_ci .driver = { 1008c2ecf20Sopenharmony_ci .of_match_table = ath79_usb_phy_of_match, 1018c2ecf20Sopenharmony_ci .name = "ath79-usb-phy", 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci}; 1048c2ecf20Sopenharmony_cimodule_platform_driver(ath79_usb_phy_driver); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ATH79 USB PHY driver"); 1078c2ecf20Sopenharmony_ciMODULE_AUTHOR("Alban Bedel <albeu@free.fr>"); 1088c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 109