162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/module.h> 362306a36Sopenharmony_ci#include <linux/platform_device.h> 462306a36Sopenharmony_ci#include <linux/dma-mapping.h> 562306a36Sopenharmony_ci#include <linux/usb/otg.h> 662306a36Sopenharmony_ci#include <linux/usb/usb_phy_generic.h> 762306a36Sopenharmony_ci#include <linux/slab.h> 862306a36Sopenharmony_ci#include <linux/clk.h> 962306a36Sopenharmony_ci#include <linux/of.h> 1062306a36Sopenharmony_ci#include <linux/of_address.h> 1162306a36Sopenharmony_ci#include <linux/usb/of.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "phy-am335x-control.h" 1462306a36Sopenharmony_ci#include "phy-generic.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistruct am335x_phy { 1762306a36Sopenharmony_ci struct usb_phy_generic usb_phy_gen; 1862306a36Sopenharmony_ci struct phy_control *phy_ctrl; 1962306a36Sopenharmony_ci int id; 2062306a36Sopenharmony_ci enum usb_dr_mode dr_mode; 2162306a36Sopenharmony_ci}; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic int am335x_init(struct usb_phy *phy) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci struct am335x_phy *am_phy = dev_get_drvdata(phy->dev); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, am_phy->dr_mode, true); 2862306a36Sopenharmony_ci return 0; 2962306a36Sopenharmony_ci} 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic void am335x_shutdown(struct usb_phy *phy) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci struct am335x_phy *am_phy = dev_get_drvdata(phy->dev); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, am_phy->dr_mode, false); 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic int am335x_phy_probe(struct platform_device *pdev) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci struct am335x_phy *am_phy; 4162306a36Sopenharmony_ci struct device *dev = &pdev->dev; 4262306a36Sopenharmony_ci int ret; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci am_phy = devm_kzalloc(dev, sizeof(*am_phy), GFP_KERNEL); 4562306a36Sopenharmony_ci if (!am_phy) 4662306a36Sopenharmony_ci return -ENOMEM; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci am_phy->phy_ctrl = am335x_get_phy_control(dev); 4962306a36Sopenharmony_ci if (!am_phy->phy_ctrl) 5062306a36Sopenharmony_ci return -EPROBE_DEFER; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci am_phy->id = of_alias_get_id(pdev->dev.of_node, "phy"); 5362306a36Sopenharmony_ci if (am_phy->id < 0) { 5462306a36Sopenharmony_ci dev_err(&pdev->dev, "Missing PHY id: %d\n", am_phy->id); 5562306a36Sopenharmony_ci return am_phy->id; 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci am_phy->dr_mode = of_usb_get_dr_mode_by_phy(pdev->dev.of_node, -1); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci ret = usb_phy_gen_create_phy(dev, &am_phy->usb_phy_gen); 6162306a36Sopenharmony_ci if (ret) 6262306a36Sopenharmony_ci return ret; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci am_phy->usb_phy_gen.phy.init = am335x_init; 6562306a36Sopenharmony_ci am_phy->usb_phy_gen.phy.shutdown = am335x_shutdown; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci platform_set_drvdata(pdev, am_phy); 6862306a36Sopenharmony_ci device_init_wakeup(dev, true); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci /* 7162306a36Sopenharmony_ci * If we leave PHY wakeup enabled then AM33XX wakes up 7262306a36Sopenharmony_ci * immediately from DS0. To avoid this we mark dev->power.can_wakeup 7362306a36Sopenharmony_ci * to false. The same is checked in suspend routine to decide 7462306a36Sopenharmony_ci * on whether to enable PHY wakeup or not. 7562306a36Sopenharmony_ci * PHY wakeup works fine in standby mode, there by allowing us to 7662306a36Sopenharmony_ci * handle remote wakeup, wakeup on disconnect and connect. 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci device_set_wakeup_enable(dev, false); 8062306a36Sopenharmony_ci phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, am_phy->dr_mode, false); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci return usb_add_phy_dev(&am_phy->usb_phy_gen.phy); 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic void am335x_phy_remove(struct platform_device *pdev) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct am335x_phy *am_phy = platform_get_drvdata(pdev); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci usb_remove_phy(&am_phy->usb_phy_gen.phy); 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 9362306a36Sopenharmony_cistatic int am335x_phy_suspend(struct device *dev) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci struct am335x_phy *am_phy = dev_get_drvdata(dev); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci /* 9862306a36Sopenharmony_ci * Enable phy wakeup only if dev->power.can_wakeup is true. 9962306a36Sopenharmony_ci * Make sure to enable wakeup to support remote wakeup in 10062306a36Sopenharmony_ci * standby mode ( same is not supported in OFF(DS0) mode). 10162306a36Sopenharmony_ci * Enable it by doing 10262306a36Sopenharmony_ci * echo enabled > /sys/bus/platform/devices/<usb-phy-id>/power/wakeup 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (device_may_wakeup(dev)) 10662306a36Sopenharmony_ci phy_ctrl_wkup(am_phy->phy_ctrl, am_phy->id, true); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, am_phy->dr_mode, false); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci return 0; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic int am335x_phy_resume(struct device *dev) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci struct am335x_phy *am_phy = dev_get_drvdata(dev); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, am_phy->dr_mode, true); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (device_may_wakeup(dev)) 12062306a36Sopenharmony_ci phy_ctrl_wkup(am_phy->phy_ctrl, am_phy->id, false); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return 0; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci#endif 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(am335x_pm_ops, am335x_phy_suspend, am335x_phy_resume); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic const struct of_device_id am335x_phy_ids[] = { 12962306a36Sopenharmony_ci { .compatible = "ti,am335x-usb-phy" }, 13062306a36Sopenharmony_ci { } 13162306a36Sopenharmony_ci}; 13262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, am335x_phy_ids); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic struct platform_driver am335x_phy_driver = { 13562306a36Sopenharmony_ci .probe = am335x_phy_probe, 13662306a36Sopenharmony_ci .remove_new = am335x_phy_remove, 13762306a36Sopenharmony_ci .driver = { 13862306a36Sopenharmony_ci .name = "am335x-phy-driver", 13962306a36Sopenharmony_ci .pm = &am335x_pm_ops, 14062306a36Sopenharmony_ci .of_match_table = am335x_phy_ids, 14162306a36Sopenharmony_ci }, 14262306a36Sopenharmony_ci}; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cimodule_platform_driver(am335x_phy_driver); 14562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 146