162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Generic platform ehci driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2007 Steven Brown <sbrown@cortland.com> 662306a36Sopenharmony_ci * Copyright 2010-2012 Hauke Mehrtens <hauke@hauke-m.de> 762306a36Sopenharmony_ci * Copyright 2014 Hans de Goede <hdegoede@redhat.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Derived from the ohci-ssb driver 1062306a36Sopenharmony_ci * Copyright 2007 Michael Buesch <m@bues.ch> 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Derived from the EHCI-PCI driver 1362306a36Sopenharmony_ci * Copyright (c) 2000-2004 by David Brownell 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * Derived from the ohci-pci driver 1662306a36Sopenharmony_ci * Copyright 1999 Roman Weissgaerber 1762306a36Sopenharmony_ci * Copyright 2000-2002 David Brownell 1862306a36Sopenharmony_ci * Copyright 1999 Linus Torvalds 1962306a36Sopenharmony_ci * Copyright 1999 Gregory P. Smith 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_ci#include <linux/acpi.h> 2262306a36Sopenharmony_ci#include <linux/clk.h> 2362306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2462306a36Sopenharmony_ci#include <linux/err.h> 2562306a36Sopenharmony_ci#include <linux/kernel.h> 2662306a36Sopenharmony_ci#include <linux/hrtimer.h> 2762306a36Sopenharmony_ci#include <linux/io.h> 2862306a36Sopenharmony_ci#include <linux/module.h> 2962306a36Sopenharmony_ci#include <linux/of.h> 3062306a36Sopenharmony_ci#include <linux/platform_device.h> 3162306a36Sopenharmony_ci#include <linux/reset.h> 3262306a36Sopenharmony_ci#include <linux/sys_soc.h> 3362306a36Sopenharmony_ci#include <linux/timer.h> 3462306a36Sopenharmony_ci#include <linux/usb.h> 3562306a36Sopenharmony_ci#include <linux/usb/hcd.h> 3662306a36Sopenharmony_ci#include <linux/usb/ehci_pdriver.h> 3762306a36Sopenharmony_ci#include <linux/usb/of.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#include "ehci.h" 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define DRIVER_DESC "EHCI generic platform driver" 4262306a36Sopenharmony_ci#define EHCI_MAX_CLKS 4 4362306a36Sopenharmony_ci#define hcd_to_ehci_priv(h) ((struct ehci_platform_priv *)hcd_to_ehci(h)->priv) 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define BCM_USB_FIFO_THRESHOLD 0x00800040 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistruct ehci_platform_priv { 4862306a36Sopenharmony_ci struct clk *clks[EHCI_MAX_CLKS]; 4962306a36Sopenharmony_ci struct reset_control *rsts; 5062306a36Sopenharmony_ci bool reset_on_resume; 5162306a36Sopenharmony_ci bool quirk_poll; 5262306a36Sopenharmony_ci struct timer_list poll_timer; 5362306a36Sopenharmony_ci struct delayed_work poll_work; 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic int ehci_platform_reset(struct usb_hcd *hcd) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(hcd->self.controller); 5962306a36Sopenharmony_ci struct usb_ehci_pdata *pdata = dev_get_platdata(&pdev->dev); 6062306a36Sopenharmony_ci struct ehci_hcd *ehci = hcd_to_ehci(hcd); 6162306a36Sopenharmony_ci int retval; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci ehci->has_synopsys_hc_bug = pdata->has_synopsys_hc_bug; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (pdata->pre_setup) { 6662306a36Sopenharmony_ci retval = pdata->pre_setup(hcd); 6762306a36Sopenharmony_ci if (retval < 0) 6862306a36Sopenharmony_ci return retval; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci ehci->caps = hcd->regs + pdata->caps_offset; 7262306a36Sopenharmony_ci retval = ehci_setup(hcd); 7362306a36Sopenharmony_ci if (retval) 7462306a36Sopenharmony_ci return retval; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (pdata->no_io_watchdog) 7762306a36Sopenharmony_ci ehci->need_io_watchdog = 0; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (of_device_is_compatible(pdev->dev.of_node, "brcm,xgs-iproc-ehci")) 8062306a36Sopenharmony_ci ehci_writel(ehci, BCM_USB_FIFO_THRESHOLD, 8162306a36Sopenharmony_ci &ehci->regs->brcm_insnreg[1]); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci return 0; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic int ehci_platform_power_on(struct platform_device *dev) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct usb_hcd *hcd = platform_get_drvdata(dev); 8962306a36Sopenharmony_ci struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); 9062306a36Sopenharmony_ci int clk, ret; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++) { 9362306a36Sopenharmony_ci ret = clk_prepare_enable(priv->clks[clk]); 9462306a36Sopenharmony_ci if (ret) 9562306a36Sopenharmony_ci goto err_disable_clks; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci return 0; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cierr_disable_clks: 10162306a36Sopenharmony_ci while (--clk >= 0) 10262306a36Sopenharmony_ci clk_disable_unprepare(priv->clks[clk]); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci return ret; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic void ehci_platform_power_off(struct platform_device *dev) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct usb_hcd *hcd = platform_get_drvdata(dev); 11062306a36Sopenharmony_ci struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); 11162306a36Sopenharmony_ci int clk; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--) 11462306a36Sopenharmony_ci if (priv->clks[clk]) 11562306a36Sopenharmony_ci clk_disable_unprepare(priv->clks[clk]); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic struct hc_driver __read_mostly ehci_platform_hc_driver; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic const struct ehci_driver_overrides platform_overrides __initconst = { 12162306a36Sopenharmony_ci .reset = ehci_platform_reset, 12262306a36Sopenharmony_ci .extra_priv_size = sizeof(struct ehci_platform_priv), 12362306a36Sopenharmony_ci}; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic struct usb_ehci_pdata ehci_platform_defaults = { 12662306a36Sopenharmony_ci .power_on = ehci_platform_power_on, 12762306a36Sopenharmony_ci .power_suspend = ehci_platform_power_off, 12862306a36Sopenharmony_ci .power_off = ehci_platform_power_off, 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci/** 13262306a36Sopenharmony_ci * quirk_poll_check_port_status - Poll port_status if the device sticks 13362306a36Sopenharmony_ci * @ehci: the ehci hcd pointer 13462306a36Sopenharmony_ci * 13562306a36Sopenharmony_ci * Since EHCI/OHCI controllers on R-Car Gen3 SoCs are possible to be getting 13662306a36Sopenharmony_ci * stuck very rarely after a full/low usb device was disconnected. To 13762306a36Sopenharmony_ci * detect such a situation, the controllers require a special way which poll 13862306a36Sopenharmony_ci * the EHCI PORTSC register. 13962306a36Sopenharmony_ci * 14062306a36Sopenharmony_ci * Return: true if the controller's port_status indicated getting stuck 14162306a36Sopenharmony_ci */ 14262306a36Sopenharmony_cistatic bool quirk_poll_check_port_status(struct ehci_hcd *ehci) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci u32 port_status = ehci_readl(ehci, &ehci->regs->port_status[0]); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (!(port_status & PORT_OWNER) && 14762306a36Sopenharmony_ci (port_status & PORT_POWER) && 14862306a36Sopenharmony_ci !(port_status & PORT_CONNECT) && 14962306a36Sopenharmony_ci (port_status & PORT_LS_MASK)) 15062306a36Sopenharmony_ci return true; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return false; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci/** 15662306a36Sopenharmony_ci * quirk_poll_rebind_companion - rebind comanion device to recover 15762306a36Sopenharmony_ci * @ehci: the ehci hcd pointer 15862306a36Sopenharmony_ci * 15962306a36Sopenharmony_ci * Since EHCI/OHCI controllers on R-Car Gen3 SoCs are possible to be getting 16062306a36Sopenharmony_ci * stuck very rarely after a full/low usb device was disconnected. To 16162306a36Sopenharmony_ci * recover from such a situation, the controllers require changing the OHCI 16262306a36Sopenharmony_ci * functional state. 16362306a36Sopenharmony_ci */ 16462306a36Sopenharmony_cistatic void quirk_poll_rebind_companion(struct ehci_hcd *ehci) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct device *companion_dev; 16762306a36Sopenharmony_ci struct usb_hcd *hcd = ehci_to_hcd(ehci); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci companion_dev = usb_of_get_companion_dev(hcd->self.controller); 17062306a36Sopenharmony_ci if (!companion_dev) 17162306a36Sopenharmony_ci return; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci device_release_driver(companion_dev); 17462306a36Sopenharmony_ci if (device_attach(companion_dev) < 0) 17562306a36Sopenharmony_ci ehci_err(ehci, "%s: failed\n", __func__); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci put_device(companion_dev); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic void quirk_poll_work(struct work_struct *work) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci struct ehci_platform_priv *priv = 18362306a36Sopenharmony_ci container_of(to_delayed_work(work), struct ehci_platform_priv, 18462306a36Sopenharmony_ci poll_work); 18562306a36Sopenharmony_ci struct ehci_hcd *ehci = container_of((void *)priv, struct ehci_hcd, 18662306a36Sopenharmony_ci priv); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* check the status twice to reduce misdetection rate */ 18962306a36Sopenharmony_ci if (!quirk_poll_check_port_status(ehci)) 19062306a36Sopenharmony_ci return; 19162306a36Sopenharmony_ci udelay(10); 19262306a36Sopenharmony_ci if (!quirk_poll_check_port_status(ehci)) 19362306a36Sopenharmony_ci return; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci ehci_dbg(ehci, "%s: detected getting stuck. rebind now!\n", __func__); 19662306a36Sopenharmony_ci quirk_poll_rebind_companion(ehci); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic void quirk_poll_timer(struct timer_list *t) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci struct ehci_platform_priv *priv = from_timer(priv, t, poll_timer); 20262306a36Sopenharmony_ci struct ehci_hcd *ehci = container_of((void *)priv, struct ehci_hcd, 20362306a36Sopenharmony_ci priv); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (quirk_poll_check_port_status(ehci)) { 20662306a36Sopenharmony_ci /* 20762306a36Sopenharmony_ci * Now scheduling the work for testing the port more. Note that 20862306a36Sopenharmony_ci * updating the status is possible to be delayed when 20962306a36Sopenharmony_ci * reconnection. So, this uses delayed work with 5 ms delay 21062306a36Sopenharmony_ci * to avoid misdetection. 21162306a36Sopenharmony_ci */ 21262306a36Sopenharmony_ci schedule_delayed_work(&priv->poll_work, msecs_to_jiffies(5)); 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci mod_timer(&priv->poll_timer, jiffies + HZ); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic void quirk_poll_init(struct ehci_platform_priv *priv) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci INIT_DELAYED_WORK(&priv->poll_work, quirk_poll_work); 22162306a36Sopenharmony_ci timer_setup(&priv->poll_timer, quirk_poll_timer, 0); 22262306a36Sopenharmony_ci mod_timer(&priv->poll_timer, jiffies + HZ); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic void quirk_poll_end(struct ehci_platform_priv *priv) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci del_timer_sync(&priv->poll_timer); 22862306a36Sopenharmony_ci cancel_delayed_work(&priv->poll_work); 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic const struct soc_device_attribute quirk_poll_match[] = { 23262306a36Sopenharmony_ci { .family = "R-Car Gen3" }, 23362306a36Sopenharmony_ci { /* sentinel*/ } 23462306a36Sopenharmony_ci}; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic int ehci_platform_probe(struct platform_device *dev) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct usb_hcd *hcd; 23962306a36Sopenharmony_ci struct resource *res_mem; 24062306a36Sopenharmony_ci struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); 24162306a36Sopenharmony_ci struct ehci_platform_priv *priv; 24262306a36Sopenharmony_ci struct ehci_hcd *ehci; 24362306a36Sopenharmony_ci int err, irq, clk = 0; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (usb_disabled()) 24662306a36Sopenharmony_ci return -ENODEV; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* 24962306a36Sopenharmony_ci * Use reasonable defaults so platforms don't have to provide these 25062306a36Sopenharmony_ci * with DT probing on ARM. 25162306a36Sopenharmony_ci */ 25262306a36Sopenharmony_ci if (!pdata) 25362306a36Sopenharmony_ci pdata = &ehci_platform_defaults; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci err = dma_coerce_mask_and_coherent(&dev->dev, 25662306a36Sopenharmony_ci pdata->dma_mask_64 ? DMA_BIT_MASK(64) : DMA_BIT_MASK(32)); 25762306a36Sopenharmony_ci if (err) { 25862306a36Sopenharmony_ci dev_err(&dev->dev, "Error: DMA mask configuration failed\n"); 25962306a36Sopenharmony_ci return err; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci irq = platform_get_irq(dev, 0); 26362306a36Sopenharmony_ci if (irq < 0) 26462306a36Sopenharmony_ci return irq; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev, 26762306a36Sopenharmony_ci dev_name(&dev->dev)); 26862306a36Sopenharmony_ci if (!hcd) 26962306a36Sopenharmony_ci return -ENOMEM; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci platform_set_drvdata(dev, hcd); 27262306a36Sopenharmony_ci dev->dev.platform_data = pdata; 27362306a36Sopenharmony_ci priv = hcd_to_ehci_priv(hcd); 27462306a36Sopenharmony_ci ehci = hcd_to_ehci(hcd); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (pdata == &ehci_platform_defaults && dev->dev.of_node) { 27762306a36Sopenharmony_ci if (of_property_read_bool(dev->dev.of_node, "big-endian-regs")) 27862306a36Sopenharmony_ci ehci->big_endian_mmio = 1; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if (of_property_read_bool(dev->dev.of_node, "big-endian-desc")) 28162306a36Sopenharmony_ci ehci->big_endian_desc = 1; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (of_property_read_bool(dev->dev.of_node, "big-endian")) 28462306a36Sopenharmony_ci ehci->big_endian_mmio = ehci->big_endian_desc = 1; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (of_property_read_bool(dev->dev.of_node, "spurious-oc")) 28762306a36Sopenharmony_ci ehci->spurious_oc = 1; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (of_property_read_bool(dev->dev.of_node, 29062306a36Sopenharmony_ci "needs-reset-on-resume")) 29162306a36Sopenharmony_ci priv->reset_on_resume = true; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (of_property_read_bool(dev->dev.of_node, 29462306a36Sopenharmony_ci "has-transaction-translator")) 29562306a36Sopenharmony_ci hcd->has_tt = 1; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (of_device_is_compatible(dev->dev.of_node, 29862306a36Sopenharmony_ci "aspeed,ast2500-ehci") || 29962306a36Sopenharmony_ci of_device_is_compatible(dev->dev.of_node, 30062306a36Sopenharmony_ci "aspeed,ast2600-ehci")) 30162306a36Sopenharmony_ci ehci->is_aspeed = 1; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (soc_device_match(quirk_poll_match)) 30462306a36Sopenharmony_ci priv->quirk_poll = true; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci for (clk = 0; clk < EHCI_MAX_CLKS; clk++) { 30762306a36Sopenharmony_ci priv->clks[clk] = of_clk_get(dev->dev.of_node, clk); 30862306a36Sopenharmony_ci if (IS_ERR(priv->clks[clk])) { 30962306a36Sopenharmony_ci err = PTR_ERR(priv->clks[clk]); 31062306a36Sopenharmony_ci if (err == -EPROBE_DEFER) 31162306a36Sopenharmony_ci goto err_put_clks; 31262306a36Sopenharmony_ci priv->clks[clk] = NULL; 31362306a36Sopenharmony_ci break; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci priv->rsts = devm_reset_control_array_get_optional_shared(&dev->dev); 31962306a36Sopenharmony_ci if (IS_ERR(priv->rsts)) { 32062306a36Sopenharmony_ci err = PTR_ERR(priv->rsts); 32162306a36Sopenharmony_ci goto err_put_clks; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci err = reset_control_deassert(priv->rsts); 32562306a36Sopenharmony_ci if (err) 32662306a36Sopenharmony_ci goto err_put_clks; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (pdata->big_endian_desc) 32962306a36Sopenharmony_ci ehci->big_endian_desc = 1; 33062306a36Sopenharmony_ci if (pdata->big_endian_mmio) 33162306a36Sopenharmony_ci ehci->big_endian_mmio = 1; 33262306a36Sopenharmony_ci if (pdata->has_tt) 33362306a36Sopenharmony_ci hcd->has_tt = 1; 33462306a36Sopenharmony_ci if (pdata->reset_on_resume) 33562306a36Sopenharmony_ci priv->reset_on_resume = true; 33662306a36Sopenharmony_ci if (pdata->spurious_oc) 33762306a36Sopenharmony_ci ehci->spurious_oc = 1; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci#ifndef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO 34062306a36Sopenharmony_ci if (ehci->big_endian_mmio) { 34162306a36Sopenharmony_ci dev_err(&dev->dev, 34262306a36Sopenharmony_ci "Error: CONFIG_USB_EHCI_BIG_ENDIAN_MMIO not set\n"); 34362306a36Sopenharmony_ci err = -EINVAL; 34462306a36Sopenharmony_ci goto err_reset; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci#endif 34762306a36Sopenharmony_ci#ifndef CONFIG_USB_EHCI_BIG_ENDIAN_DESC 34862306a36Sopenharmony_ci if (ehci->big_endian_desc) { 34962306a36Sopenharmony_ci dev_err(&dev->dev, 35062306a36Sopenharmony_ci "Error: CONFIG_USB_EHCI_BIG_ENDIAN_DESC not set\n"); 35162306a36Sopenharmony_ci err = -EINVAL; 35262306a36Sopenharmony_ci goto err_reset; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci#endif 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (pdata->power_on) { 35762306a36Sopenharmony_ci err = pdata->power_on(dev); 35862306a36Sopenharmony_ci if (err < 0) 35962306a36Sopenharmony_ci goto err_reset; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci hcd->regs = devm_platform_get_and_ioremap_resource(dev, 0, &res_mem); 36362306a36Sopenharmony_ci if (IS_ERR(hcd->regs)) { 36462306a36Sopenharmony_ci err = PTR_ERR(hcd->regs); 36562306a36Sopenharmony_ci goto err_power; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci hcd->rsrc_start = res_mem->start; 36862306a36Sopenharmony_ci hcd->rsrc_len = resource_size(res_mem); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci hcd->tpl_support = of_usb_host_tpl_support(dev->dev.of_node); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci err = usb_add_hcd(hcd, irq, IRQF_SHARED); 37362306a36Sopenharmony_ci if (err) 37462306a36Sopenharmony_ci goto err_power; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci device_wakeup_enable(hcd->self.controller); 37762306a36Sopenharmony_ci device_enable_async_suspend(hcd->self.controller); 37862306a36Sopenharmony_ci platform_set_drvdata(dev, hcd); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (priv->quirk_poll) 38162306a36Sopenharmony_ci quirk_poll_init(priv); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci return err; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cierr_power: 38662306a36Sopenharmony_ci if (pdata->power_off) 38762306a36Sopenharmony_ci pdata->power_off(dev); 38862306a36Sopenharmony_cierr_reset: 38962306a36Sopenharmony_ci reset_control_assert(priv->rsts); 39062306a36Sopenharmony_cierr_put_clks: 39162306a36Sopenharmony_ci while (--clk >= 0) 39262306a36Sopenharmony_ci clk_put(priv->clks[clk]); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (pdata == &ehci_platform_defaults) 39562306a36Sopenharmony_ci dev->dev.platform_data = NULL; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci usb_put_hcd(hcd); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci return err; 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic void ehci_platform_remove(struct platform_device *dev) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci struct usb_hcd *hcd = platform_get_drvdata(dev); 40562306a36Sopenharmony_ci struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); 40662306a36Sopenharmony_ci struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); 40762306a36Sopenharmony_ci int clk; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (priv->quirk_poll) 41062306a36Sopenharmony_ci quirk_poll_end(priv); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci usb_remove_hcd(hcd); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if (pdata->power_off) 41562306a36Sopenharmony_ci pdata->power_off(dev); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci reset_control_assert(priv->rsts); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++) 42062306a36Sopenharmony_ci clk_put(priv->clks[clk]); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci usb_put_hcd(hcd); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (pdata == &ehci_platform_defaults) 42562306a36Sopenharmony_ci dev->dev.platform_data = NULL; 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cistatic int __maybe_unused ehci_platform_suspend(struct device *dev) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci struct usb_hcd *hcd = dev_get_drvdata(dev); 43162306a36Sopenharmony_ci struct usb_ehci_pdata *pdata = dev_get_platdata(dev); 43262306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 43362306a36Sopenharmony_ci struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); 43462306a36Sopenharmony_ci bool do_wakeup = device_may_wakeup(dev); 43562306a36Sopenharmony_ci int ret; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (priv->quirk_poll) 43862306a36Sopenharmony_ci quirk_poll_end(priv); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci ret = ehci_suspend(hcd, do_wakeup); 44162306a36Sopenharmony_ci if (ret) 44262306a36Sopenharmony_ci return ret; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci if (pdata->power_suspend) 44562306a36Sopenharmony_ci pdata->power_suspend(pdev); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci return ret; 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cistatic int __maybe_unused ehci_platform_resume(struct device *dev) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci struct usb_hcd *hcd = dev_get_drvdata(dev); 45362306a36Sopenharmony_ci struct usb_ehci_pdata *pdata = dev_get_platdata(dev); 45462306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 45562306a36Sopenharmony_ci struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); 45662306a36Sopenharmony_ci struct device *companion_dev; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci if (pdata->power_on) { 45962306a36Sopenharmony_ci int err = pdata->power_on(pdev); 46062306a36Sopenharmony_ci if (err < 0) 46162306a36Sopenharmony_ci return err; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci companion_dev = usb_of_get_companion_dev(hcd->self.controller); 46562306a36Sopenharmony_ci if (companion_dev) { 46662306a36Sopenharmony_ci device_pm_wait_for_dev(hcd->self.controller, companion_dev); 46762306a36Sopenharmony_ci put_device(companion_dev); 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci ehci_resume(hcd, priv->reset_on_resume); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci pm_runtime_disable(dev); 47362306a36Sopenharmony_ci pm_runtime_set_active(dev); 47462306a36Sopenharmony_ci pm_runtime_enable(dev); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci if (priv->quirk_poll) 47762306a36Sopenharmony_ci quirk_poll_init(priv); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci return 0; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic const struct of_device_id vt8500_ehci_ids[] = { 48362306a36Sopenharmony_ci { .compatible = "via,vt8500-ehci", }, 48462306a36Sopenharmony_ci { .compatible = "wm,prizm-ehci", }, 48562306a36Sopenharmony_ci { .compatible = "generic-ehci", }, 48662306a36Sopenharmony_ci { .compatible = "cavium,octeon-6335-ehci", }, 48762306a36Sopenharmony_ci {} 48862306a36Sopenharmony_ci}; 48962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, vt8500_ehci_ids); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci#ifdef CONFIG_ACPI 49262306a36Sopenharmony_cistatic const struct acpi_device_id ehci_acpi_match[] = { 49362306a36Sopenharmony_ci { "PNP0D20", 0 }, /* EHCI controller without debug */ 49462306a36Sopenharmony_ci { } 49562306a36Sopenharmony_ci}; 49662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, ehci_acpi_match); 49762306a36Sopenharmony_ci#endif 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistatic const struct platform_device_id ehci_platform_table[] = { 50062306a36Sopenharmony_ci { "ehci-platform", 0 }, 50162306a36Sopenharmony_ci { } 50262306a36Sopenharmony_ci}; 50362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(platform, ehci_platform_table); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(ehci_platform_pm_ops, ehci_platform_suspend, 50662306a36Sopenharmony_ci ehci_platform_resume); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_cistatic struct platform_driver ehci_platform_driver = { 50962306a36Sopenharmony_ci .id_table = ehci_platform_table, 51062306a36Sopenharmony_ci .probe = ehci_platform_probe, 51162306a36Sopenharmony_ci .remove_new = ehci_platform_remove, 51262306a36Sopenharmony_ci .shutdown = usb_hcd_platform_shutdown, 51362306a36Sopenharmony_ci .driver = { 51462306a36Sopenharmony_ci .name = "ehci-platform", 51562306a36Sopenharmony_ci .pm = pm_ptr(&ehci_platform_pm_ops), 51662306a36Sopenharmony_ci .of_match_table = vt8500_ehci_ids, 51762306a36Sopenharmony_ci .acpi_match_table = ACPI_PTR(ehci_acpi_match), 51862306a36Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci}; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic int __init ehci_platform_init(void) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci if (usb_disabled()) 52562306a36Sopenharmony_ci return -ENODEV; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci ehci_init_driver(&ehci_platform_hc_driver, &platform_overrides); 52862306a36Sopenharmony_ci return platform_driver_register(&ehci_platform_driver); 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_cimodule_init(ehci_platform_init); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistatic void __exit ehci_platform_cleanup(void) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci platform_driver_unregister(&ehci_platform_driver); 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_cimodule_exit(ehci_platform_cleanup); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 53962306a36Sopenharmony_ciMODULE_AUTHOR("Hauke Mehrtens"); 54062306a36Sopenharmony_ciMODULE_AUTHOR("Alan Stern"); 54162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 542