162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ST OHCI driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014 STMicroelectronics – All Rights Reserved 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Peter Griffin <peter.griffin@linaro.org> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Derived from ohci-platform.c 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/clk.h> 1362306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1462306a36Sopenharmony_ci#include <linux/hrtimer.h> 1562306a36Sopenharmony_ci#include <linux/io.h> 1662306a36Sopenharmony_ci#include <linux/kernel.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/err.h> 1962306a36Sopenharmony_ci#include <linux/phy/phy.h> 2062306a36Sopenharmony_ci#include <linux/platform_device.h> 2162306a36Sopenharmony_ci#include <linux/reset.h> 2262306a36Sopenharmony_ci#include <linux/usb/ohci_pdriver.h> 2362306a36Sopenharmony_ci#include <linux/usb.h> 2462306a36Sopenharmony_ci#include <linux/usb/hcd.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "ohci.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define USB_MAX_CLKS 3 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistruct st_ohci_platform_priv { 3162306a36Sopenharmony_ci struct clk *clks[USB_MAX_CLKS]; 3262306a36Sopenharmony_ci struct clk *clk48; 3362306a36Sopenharmony_ci struct reset_control *rst; 3462306a36Sopenharmony_ci struct reset_control *pwr; 3562306a36Sopenharmony_ci struct phy *phy; 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define DRIVER_DESC "OHCI STMicroelectronics driver" 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define hcd_to_ohci_priv(h) \ 4162306a36Sopenharmony_ci ((struct st_ohci_platform_priv *)hcd_to_ohci(h)->priv) 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic int st_ohci_platform_power_on(struct platform_device *dev) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci struct usb_hcd *hcd = platform_get_drvdata(dev); 4662306a36Sopenharmony_ci struct st_ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); 4762306a36Sopenharmony_ci int clk, ret; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci ret = reset_control_deassert(priv->pwr); 5062306a36Sopenharmony_ci if (ret) 5162306a36Sopenharmony_ci return ret; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci ret = reset_control_deassert(priv->rst); 5462306a36Sopenharmony_ci if (ret) 5562306a36Sopenharmony_ci goto err_assert_power; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci /* some SoCs don't have a dedicated 48Mhz clock, but those that do 5862306a36Sopenharmony_ci need the rate to be explicitly set */ 5962306a36Sopenharmony_ci if (priv->clk48) { 6062306a36Sopenharmony_ci ret = clk_set_rate(priv->clk48, 48000000); 6162306a36Sopenharmony_ci if (ret) 6262306a36Sopenharmony_ci goto err_assert_reset; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci for (clk = 0; clk < USB_MAX_CLKS && priv->clks[clk]; clk++) { 6662306a36Sopenharmony_ci ret = clk_prepare_enable(priv->clks[clk]); 6762306a36Sopenharmony_ci if (ret) 6862306a36Sopenharmony_ci goto err_disable_clks; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci ret = phy_init(priv->phy); 7262306a36Sopenharmony_ci if (ret) 7362306a36Sopenharmony_ci goto err_disable_clks; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci ret = phy_power_on(priv->phy); 7662306a36Sopenharmony_ci if (ret) 7762306a36Sopenharmony_ci goto err_exit_phy; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci return 0; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cierr_exit_phy: 8262306a36Sopenharmony_ci phy_exit(priv->phy); 8362306a36Sopenharmony_cierr_disable_clks: 8462306a36Sopenharmony_ci while (--clk >= 0) 8562306a36Sopenharmony_ci clk_disable_unprepare(priv->clks[clk]); 8662306a36Sopenharmony_cierr_assert_reset: 8762306a36Sopenharmony_ci reset_control_assert(priv->rst); 8862306a36Sopenharmony_cierr_assert_power: 8962306a36Sopenharmony_ci reset_control_assert(priv->pwr); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci return ret; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic void st_ohci_platform_power_off(struct platform_device *dev) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci struct usb_hcd *hcd = platform_get_drvdata(dev); 9762306a36Sopenharmony_ci struct st_ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci int clk; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci reset_control_assert(priv->pwr); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci reset_control_assert(priv->rst); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci phy_power_off(priv->phy); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci phy_exit(priv->phy); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci for (clk = USB_MAX_CLKS - 1; clk >= 0; clk--) 11062306a36Sopenharmony_ci if (priv->clks[clk]) 11162306a36Sopenharmony_ci clk_disable_unprepare(priv->clks[clk]); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic struct hc_driver __read_mostly ohci_platform_hc_driver; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic const struct ohci_driver_overrides platform_overrides __initconst = { 11762306a36Sopenharmony_ci .product_desc = "ST OHCI controller", 11862306a36Sopenharmony_ci .extra_priv_size = sizeof(struct st_ohci_platform_priv), 11962306a36Sopenharmony_ci}; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic struct usb_ohci_pdata ohci_platform_defaults = { 12262306a36Sopenharmony_ci .power_on = st_ohci_platform_power_on, 12362306a36Sopenharmony_ci .power_suspend = st_ohci_platform_power_off, 12462306a36Sopenharmony_ci .power_off = st_ohci_platform_power_off, 12562306a36Sopenharmony_ci}; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic int st_ohci_platform_probe(struct platform_device *dev) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci struct usb_hcd *hcd; 13062306a36Sopenharmony_ci struct resource *res_mem; 13162306a36Sopenharmony_ci struct usb_ohci_pdata *pdata = &ohci_platform_defaults; 13262306a36Sopenharmony_ci struct st_ohci_platform_priv *priv; 13362306a36Sopenharmony_ci int err, irq, clk = 0; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (usb_disabled()) 13662306a36Sopenharmony_ci return -ENODEV; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci irq = platform_get_irq(dev, 0); 13962306a36Sopenharmony_ci if (irq < 0) 14062306a36Sopenharmony_ci return irq; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev, 14362306a36Sopenharmony_ci dev_name(&dev->dev)); 14462306a36Sopenharmony_ci if (!hcd) 14562306a36Sopenharmony_ci return -ENOMEM; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci platform_set_drvdata(dev, hcd); 14862306a36Sopenharmony_ci dev->dev.platform_data = pdata; 14962306a36Sopenharmony_ci priv = hcd_to_ohci_priv(hcd); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci priv->phy = devm_phy_get(&dev->dev, "usb"); 15262306a36Sopenharmony_ci if (IS_ERR(priv->phy)) { 15362306a36Sopenharmony_ci err = PTR_ERR(priv->phy); 15462306a36Sopenharmony_ci goto err_put_hcd; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci for (clk = 0; clk < USB_MAX_CLKS; clk++) { 15862306a36Sopenharmony_ci priv->clks[clk] = of_clk_get(dev->dev.of_node, clk); 15962306a36Sopenharmony_ci if (IS_ERR(priv->clks[clk])) { 16062306a36Sopenharmony_ci err = PTR_ERR(priv->clks[clk]); 16162306a36Sopenharmony_ci if (err == -EPROBE_DEFER) 16262306a36Sopenharmony_ci goto err_put_clks; 16362306a36Sopenharmony_ci priv->clks[clk] = NULL; 16462306a36Sopenharmony_ci break; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* some SoCs don't have a dedicated 48Mhz clock, but those that 16962306a36Sopenharmony_ci do need the rate to be explicitly set */ 17062306a36Sopenharmony_ci priv->clk48 = devm_clk_get(&dev->dev, "clk48"); 17162306a36Sopenharmony_ci if (IS_ERR(priv->clk48)) { 17262306a36Sopenharmony_ci dev_info(&dev->dev, "48MHz clk not found\n"); 17362306a36Sopenharmony_ci priv->clk48 = NULL; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci priv->pwr = 17762306a36Sopenharmony_ci devm_reset_control_get_optional_shared(&dev->dev, "power"); 17862306a36Sopenharmony_ci if (IS_ERR(priv->pwr)) { 17962306a36Sopenharmony_ci err = PTR_ERR(priv->pwr); 18062306a36Sopenharmony_ci goto err_put_clks; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci priv->rst = 18462306a36Sopenharmony_ci devm_reset_control_get_optional_shared(&dev->dev, "softreset"); 18562306a36Sopenharmony_ci if (IS_ERR(priv->rst)) { 18662306a36Sopenharmony_ci err = PTR_ERR(priv->rst); 18762306a36Sopenharmony_ci goto err_put_clks; 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (pdata->power_on) { 19162306a36Sopenharmony_ci err = pdata->power_on(dev); 19262306a36Sopenharmony_ci if (err < 0) 19362306a36Sopenharmony_ci goto err_power; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci hcd->regs = devm_platform_get_and_ioremap_resource(dev, 0, &res_mem); 19762306a36Sopenharmony_ci if (IS_ERR(hcd->regs)) { 19862306a36Sopenharmony_ci err = PTR_ERR(hcd->regs); 19962306a36Sopenharmony_ci goto err_power; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci hcd->rsrc_start = res_mem->start; 20262306a36Sopenharmony_ci hcd->rsrc_len = resource_size(res_mem); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci err = usb_add_hcd(hcd, irq, IRQF_SHARED); 20562306a36Sopenharmony_ci if (err) 20662306a36Sopenharmony_ci goto err_power; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci device_wakeup_enable(hcd->self.controller); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci platform_set_drvdata(dev, hcd); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci return err; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cierr_power: 21562306a36Sopenharmony_ci if (pdata->power_off) 21662306a36Sopenharmony_ci pdata->power_off(dev); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cierr_put_clks: 21962306a36Sopenharmony_ci while (--clk >= 0) 22062306a36Sopenharmony_ci clk_put(priv->clks[clk]); 22162306a36Sopenharmony_cierr_put_hcd: 22262306a36Sopenharmony_ci if (pdata == &ohci_platform_defaults) 22362306a36Sopenharmony_ci dev->dev.platform_data = NULL; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci usb_put_hcd(hcd); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci return err; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic void st_ohci_platform_remove(struct platform_device *dev) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci struct usb_hcd *hcd = platform_get_drvdata(dev); 23362306a36Sopenharmony_ci struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev); 23462306a36Sopenharmony_ci struct st_ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); 23562306a36Sopenharmony_ci int clk; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci usb_remove_hcd(hcd); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (pdata->power_off) 24062306a36Sopenharmony_ci pdata->power_off(dev); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci for (clk = 0; clk < USB_MAX_CLKS && priv->clks[clk]; clk++) 24462306a36Sopenharmony_ci clk_put(priv->clks[clk]); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci usb_put_hcd(hcd); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (pdata == &ohci_platform_defaults) 24962306a36Sopenharmony_ci dev->dev.platform_data = NULL; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic int st_ohci_suspend(struct device *dev) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci struct usb_hcd *hcd = dev_get_drvdata(dev); 25762306a36Sopenharmony_ci struct usb_ohci_pdata *pdata = dev->platform_data; 25862306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 25962306a36Sopenharmony_ci bool do_wakeup = device_may_wakeup(dev); 26062306a36Sopenharmony_ci int ret; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci ret = ohci_suspend(hcd, do_wakeup); 26362306a36Sopenharmony_ci if (ret) 26462306a36Sopenharmony_ci return ret; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (pdata->power_suspend) 26762306a36Sopenharmony_ci pdata->power_suspend(pdev); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci return ret; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic int st_ohci_resume(struct device *dev) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct usb_hcd *hcd = dev_get_drvdata(dev); 27562306a36Sopenharmony_ci struct usb_ohci_pdata *pdata = dev_get_platdata(dev); 27662306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 27762306a36Sopenharmony_ci int err; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (pdata->power_on) { 28062306a36Sopenharmony_ci err = pdata->power_on(pdev); 28162306a36Sopenharmony_ci if (err < 0) 28262306a36Sopenharmony_ci return err; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci ohci_resume(hcd, false); 28662306a36Sopenharmony_ci return 0; 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(st_ohci_pm_ops, st_ohci_suspend, st_ohci_resume); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic const struct of_device_id st_ohci_platform_ids[] = { 29462306a36Sopenharmony_ci { .compatible = "st,st-ohci-300x", }, 29562306a36Sopenharmony_ci { /* sentinel */ } 29662306a36Sopenharmony_ci}; 29762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, st_ohci_platform_ids); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic struct platform_driver ohci_platform_driver = { 30062306a36Sopenharmony_ci .probe = st_ohci_platform_probe, 30162306a36Sopenharmony_ci .remove_new = st_ohci_platform_remove, 30262306a36Sopenharmony_ci .shutdown = usb_hcd_platform_shutdown, 30362306a36Sopenharmony_ci .driver = { 30462306a36Sopenharmony_ci .name = "st-ohci", 30562306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 30662306a36Sopenharmony_ci .pm = &st_ohci_pm_ops, 30762306a36Sopenharmony_ci#endif 30862306a36Sopenharmony_ci .of_match_table = st_ohci_platform_ids, 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci}; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic int __init ohci_platform_init(void) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci if (usb_disabled()) 31562306a36Sopenharmony_ci return -ENODEV; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci ohci_init_driver(&ohci_platform_hc_driver, &platform_overrides); 31862306a36Sopenharmony_ci return platform_driver_register(&ohci_platform_driver); 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_cimodule_init(ohci_platform_init); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic void __exit ohci_platform_cleanup(void) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci platform_driver_unregister(&ohci_platform_driver); 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_cimodule_exit(ohci_platform_cleanup); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 32962306a36Sopenharmony_ciMODULE_AUTHOR("Peter Griffin <peter.griffin@linaro.org>"); 33062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 331