162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-1.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * OHCI HCD (Host Controller Driver) for USB.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
662306a36Sopenharmony_ci * (C) Copyright 2000-2005 David Brownell
762306a36Sopenharmony_ci * (C) Copyright 2002 Hewlett-Packard Company
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * OMAP Bus Glue
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Modified for OMAP by Tony Lindgren <tony@atomide.com>
1262306a36Sopenharmony_ci * Based on the 2.4 OMAP OHCI driver originally done by MontaVista Software Inc.
1362306a36Sopenharmony_ci * and on ohci-sa1111.c by Christopher Hoover <ch@hpl.hp.com>
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * This file is licenced under the GPL.
1662306a36Sopenharmony_ci */
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <linux/clk.h>
1962306a36Sopenharmony_ci#include <linux/dma-mapping.h>
2062306a36Sopenharmony_ci#include <linux/err.h>
2162306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
2262306a36Sopenharmony_ci#include <linux/io.h>
2362306a36Sopenharmony_ci#include <linux/jiffies.h>
2462306a36Sopenharmony_ci#include <linux/kernel.h>
2562306a36Sopenharmony_ci#include <linux/module.h>
2662306a36Sopenharmony_ci#include <linux/usb/otg.h>
2762306a36Sopenharmony_ci#include <linux/platform_device.h>
2862306a36Sopenharmony_ci#include <linux/platform_data/usb-omap1.h>
2962306a36Sopenharmony_ci#include <linux/soc/ti/omap1-usb.h>
3062306a36Sopenharmony_ci#include <linux/soc/ti/omap1-mux.h>
3162306a36Sopenharmony_ci#include <linux/soc/ti/omap1-soc.h>
3262306a36Sopenharmony_ci#include <linux/soc/ti/omap1-io.h>
3362306a36Sopenharmony_ci#include <linux/signal.h>
3462306a36Sopenharmony_ci#include <linux/usb.h>
3562306a36Sopenharmony_ci#include <linux/usb/hcd.h>
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#include "ohci.h"
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#include <asm/io.h>
4062306a36Sopenharmony_ci#include <asm/mach-types.h>
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define DRIVER_DESC "OHCI OMAP driver"
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistruct ohci_omap_priv {
4562306a36Sopenharmony_ci	struct clk *usb_host_ck;
4662306a36Sopenharmony_ci	struct clk *usb_dc_ck;
4762306a36Sopenharmony_ci	struct gpio_desc *power;
4862306a36Sopenharmony_ci	struct gpio_desc *overcurrent;
4962306a36Sopenharmony_ci};
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic const char hcd_name[] = "ohci-omap";
5262306a36Sopenharmony_cistatic struct hc_driver __read_mostly ohci_omap_hc_driver;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#define hcd_to_ohci_omap_priv(h) \
5562306a36Sopenharmony_ci	((struct ohci_omap_priv *)hcd_to_ohci(h)->priv)
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic void omap_ohci_clock_power(struct ohci_omap_priv *priv, int on)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	if (on) {
6062306a36Sopenharmony_ci		clk_enable(priv->usb_dc_ck);
6162306a36Sopenharmony_ci		clk_enable(priv->usb_host_ck);
6262306a36Sopenharmony_ci		/* guesstimate for T5 == 1x 32K clock + APLL lock time */
6362306a36Sopenharmony_ci		udelay(100);
6462306a36Sopenharmony_ci	} else {
6562306a36Sopenharmony_ci		clk_disable(priv->usb_host_ck);
6662306a36Sopenharmony_ci		clk_disable(priv->usb_dc_ck);
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic void start_hnp(struct ohci_hcd *ohci)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	struct usb_hcd *hcd = ohci_to_hcd(ohci);
7362306a36Sopenharmony_ci	const unsigned	port = hcd->self.otg_port - 1;
7462306a36Sopenharmony_ci	unsigned long	flags;
7562306a36Sopenharmony_ci	u32 l;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	otg_start_hnp(hcd->usb_phy->otg);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	local_irq_save(flags);
8062306a36Sopenharmony_ci	hcd->usb_phy->otg->state = OTG_STATE_A_SUSPEND;
8162306a36Sopenharmony_ci	writel (RH_PS_PSS, &ohci->regs->roothub.portstatus [port]);
8262306a36Sopenharmony_ci	l = omap_readl(OTG_CTRL);
8362306a36Sopenharmony_ci	l &= ~OTG_A_BUSREQ;
8462306a36Sopenharmony_ci	omap_writel(l, OTG_CTRL);
8562306a36Sopenharmony_ci	local_irq_restore(flags);
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic int ohci_omap_reset(struct usb_hcd *hcd)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	struct ohci_hcd		*ohci = hcd_to_ohci(hcd);
9362306a36Sopenharmony_ci	struct omap_usb_config	*config = dev_get_platdata(hcd->self.controller);
9462306a36Sopenharmony_ci	struct ohci_omap_priv	*priv = hcd_to_ohci_omap_priv(hcd);
9562306a36Sopenharmony_ci	int			need_transceiver = (config->otg != 0);
9662306a36Sopenharmony_ci	int			ret;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	dev_dbg(hcd->self.controller, "starting USB Controller\n");
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	if (config->otg) {
10162306a36Sopenharmony_ci		hcd->self.otg_port = config->otg;
10262306a36Sopenharmony_ci		/* default/minimum OTG power budget:  8 mA */
10362306a36Sopenharmony_ci		hcd->power_budget = 8;
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	/* XXX OMAP16xx only */
10762306a36Sopenharmony_ci	if (config->ocpi_enable)
10862306a36Sopenharmony_ci		config->ocpi_enable();
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_USB_OTG) && need_transceiver) {
11162306a36Sopenharmony_ci		hcd->usb_phy = usb_get_phy(USB_PHY_TYPE_USB2);
11262306a36Sopenharmony_ci		if (!IS_ERR_OR_NULL(hcd->usb_phy)) {
11362306a36Sopenharmony_ci			int	status = otg_set_host(hcd->usb_phy->otg,
11462306a36Sopenharmony_ci						&ohci_to_hcd(ohci)->self);
11562306a36Sopenharmony_ci			dev_dbg(hcd->self.controller, "init %s phy, status %d\n",
11662306a36Sopenharmony_ci					hcd->usb_phy->label, status);
11762306a36Sopenharmony_ci			if (status) {
11862306a36Sopenharmony_ci				usb_put_phy(hcd->usb_phy);
11962306a36Sopenharmony_ci				return status;
12062306a36Sopenharmony_ci			}
12162306a36Sopenharmony_ci		} else {
12262306a36Sopenharmony_ci			return -EPROBE_DEFER;
12362306a36Sopenharmony_ci		}
12462306a36Sopenharmony_ci		hcd->skip_phy_initialization = 1;
12562306a36Sopenharmony_ci		ohci->start_hnp = start_hnp;
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	omap_ohci_clock_power(priv, 1);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	if (config->lb_reset)
13162306a36Sopenharmony_ci		config->lb_reset();
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	ret = ohci_setup(hcd);
13462306a36Sopenharmony_ci	if (ret < 0)
13562306a36Sopenharmony_ci		return ret;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	if (config->otg || config->rwc) {
13862306a36Sopenharmony_ci		ohci->hc_control = OHCI_CTRL_RWC;
13962306a36Sopenharmony_ci		writel(OHCI_CTRL_RWC, &ohci->regs->control);
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	/* board-specific power switching and overcurrent support */
14362306a36Sopenharmony_ci	if (machine_is_omap_osk()) {
14462306a36Sopenharmony_ci		u32	rh = roothub_a (ohci);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci		/* power switching (ganged by default) */
14762306a36Sopenharmony_ci		rh &= ~RH_A_NPS;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci		/* TPS2045 switch for internal transceiver (port 1) */
15062306a36Sopenharmony_ci		if (machine_is_omap_osk()) {
15162306a36Sopenharmony_ci			ohci_to_hcd(ohci)->power_budget = 250;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci			rh &= ~RH_A_NOCP;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci			/* gpio9 for overcurrent detction */
15662306a36Sopenharmony_ci			omap_cfg_reg(W8_1610_GPIO9);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci			/* for paranoia's sake:  disable USB.PUEN */
15962306a36Sopenharmony_ci			omap_cfg_reg(W4_USB_HIGHZ);
16062306a36Sopenharmony_ci		}
16162306a36Sopenharmony_ci		ohci_writel(ohci, rh, &ohci->regs->roothub.a);
16262306a36Sopenharmony_ci		ohci->flags &= ~OHCI_QUIRK_HUB_POWER;
16362306a36Sopenharmony_ci	} else if (machine_is_nokia770()) {
16462306a36Sopenharmony_ci		/* We require a self-powered hub, which should have
16562306a36Sopenharmony_ci		 * plenty of power. */
16662306a36Sopenharmony_ci		ohci_to_hcd(ohci)->power_budget = 0;
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	/* FIXME hub_wq hub requests should manage power switching */
17062306a36Sopenharmony_ci	if (config->transceiver_power)
17162306a36Sopenharmony_ci		return config->transceiver_power(1);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	if (priv->power)
17462306a36Sopenharmony_ci		gpiod_set_value_cansleep(priv->power, 0);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	/* board init will have already handled HMC and mux setup.
17762306a36Sopenharmony_ci	 * any external transceiver should already be initialized
17862306a36Sopenharmony_ci	 * too, so all configured ports use the right signaling now.
17962306a36Sopenharmony_ci	 */
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	return 0;
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci/**
18762306a36Sopenharmony_ci * ohci_hcd_omap_probe - initialize OMAP-based HCDs
18862306a36Sopenharmony_ci * @pdev:	USB controller to probe
18962306a36Sopenharmony_ci *
19062306a36Sopenharmony_ci * Context: task context, might sleep
19162306a36Sopenharmony_ci *
19262306a36Sopenharmony_ci * Allocates basic resources for this USB host controller, and
19362306a36Sopenharmony_ci * then invokes the start() method for the HCD associated with it
19462306a36Sopenharmony_ci * through the hotplug entry's driver_data.
19562306a36Sopenharmony_ci */
19662306a36Sopenharmony_cistatic int ohci_hcd_omap_probe(struct platform_device *pdev)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	int retval, irq;
19962306a36Sopenharmony_ci	struct usb_hcd *hcd = 0;
20062306a36Sopenharmony_ci	struct ohci_omap_priv *priv;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	if (pdev->num_resources != 2) {
20362306a36Sopenharmony_ci		dev_err(&pdev->dev, "invalid num_resources: %i\n",
20462306a36Sopenharmony_ci		       pdev->num_resources);
20562306a36Sopenharmony_ci		return -ENODEV;
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (pdev->resource[0].flags != IORESOURCE_MEM
20962306a36Sopenharmony_ci			|| pdev->resource[1].flags != IORESOURCE_IRQ) {
21062306a36Sopenharmony_ci		dev_err(&pdev->dev, "invalid resource type\n");
21162306a36Sopenharmony_ci		return -ENODEV;
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	hcd = usb_create_hcd(&ohci_omap_hc_driver, &pdev->dev,
21562306a36Sopenharmony_ci			dev_name(&pdev->dev));
21662306a36Sopenharmony_ci	if (!hcd)
21762306a36Sopenharmony_ci		return -ENOMEM;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	hcd->rsrc_start = pdev->resource[0].start;
22062306a36Sopenharmony_ci	hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1;
22162306a36Sopenharmony_ci	priv = hcd_to_ohci_omap_priv(hcd);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	/* Obtain two optional GPIO lines */
22462306a36Sopenharmony_ci	priv->power = devm_gpiod_get_optional(&pdev->dev, "power", GPIOD_ASIS);
22562306a36Sopenharmony_ci	if (IS_ERR(priv->power)) {
22662306a36Sopenharmony_ci		retval = PTR_ERR(priv->power);
22762306a36Sopenharmony_ci		goto err_put_hcd;
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci	if (priv->power)
23062306a36Sopenharmony_ci		gpiod_set_consumer_name(priv->power, "OHCI power");
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	/*
23362306a36Sopenharmony_ci	 * This "overcurrent" GPIO line isn't really used in the code,
23462306a36Sopenharmony_ci	 * but has a designated hardware function.
23562306a36Sopenharmony_ci	 * TODO: implement proper overcurrent handling.
23662306a36Sopenharmony_ci	 */
23762306a36Sopenharmony_ci	priv->overcurrent = devm_gpiod_get_optional(&pdev->dev, "overcurrent",
23862306a36Sopenharmony_ci						    GPIOD_IN);
23962306a36Sopenharmony_ci	if (IS_ERR(priv->overcurrent)) {
24062306a36Sopenharmony_ci		retval = PTR_ERR(priv->overcurrent);
24162306a36Sopenharmony_ci		goto err_put_hcd;
24262306a36Sopenharmony_ci	}
24362306a36Sopenharmony_ci	if (priv->overcurrent)
24462306a36Sopenharmony_ci		gpiod_set_consumer_name(priv->overcurrent, "OHCI overcurrent");
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	priv->usb_host_ck = clk_get(&pdev->dev, "usb_hhc_ck");
24762306a36Sopenharmony_ci	if (IS_ERR(priv->usb_host_ck)) {
24862306a36Sopenharmony_ci		retval = PTR_ERR(priv->usb_host_ck);
24962306a36Sopenharmony_ci		goto err_put_hcd;
25062306a36Sopenharmony_ci	}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	retval = clk_prepare(priv->usb_host_ck);
25362306a36Sopenharmony_ci	if (retval)
25462306a36Sopenharmony_ci		goto err_put_host_ck;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	if (!cpu_is_omap15xx())
25762306a36Sopenharmony_ci		priv->usb_dc_ck = clk_get(&pdev->dev, "usb_dc_ck");
25862306a36Sopenharmony_ci	else
25962306a36Sopenharmony_ci		priv->usb_dc_ck = clk_get(&pdev->dev, "lb_ck");
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	if (IS_ERR(priv->usb_dc_ck)) {
26262306a36Sopenharmony_ci		retval = PTR_ERR(priv->usb_dc_ck);
26362306a36Sopenharmony_ci		goto err_unprepare_host_ck;
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	retval = clk_prepare(priv->usb_dc_ck);
26762306a36Sopenharmony_ci	if (retval)
26862306a36Sopenharmony_ci		goto err_put_dc_ck;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
27162306a36Sopenharmony_ci		dev_dbg(&pdev->dev, "request_mem_region failed\n");
27262306a36Sopenharmony_ci		retval = -EBUSY;
27362306a36Sopenharmony_ci		goto err_unprepare_dc_ck;
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
27762306a36Sopenharmony_ci	if (!hcd->regs) {
27862306a36Sopenharmony_ci		dev_err(&pdev->dev, "can't ioremap OHCI HCD\n");
27962306a36Sopenharmony_ci		retval = -ENOMEM;
28062306a36Sopenharmony_ci		goto err2;
28162306a36Sopenharmony_ci	}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
28462306a36Sopenharmony_ci	if (irq < 0) {
28562306a36Sopenharmony_ci		retval = irq;
28662306a36Sopenharmony_ci		goto err3;
28762306a36Sopenharmony_ci	}
28862306a36Sopenharmony_ci	retval = usb_add_hcd(hcd, irq, 0);
28962306a36Sopenharmony_ci	if (retval)
29062306a36Sopenharmony_ci		goto err3;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	device_wakeup_enable(hcd->self.controller);
29362306a36Sopenharmony_ci	return 0;
29462306a36Sopenharmony_cierr3:
29562306a36Sopenharmony_ci	iounmap(hcd->regs);
29662306a36Sopenharmony_cierr2:
29762306a36Sopenharmony_ci	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
29862306a36Sopenharmony_cierr_unprepare_dc_ck:
29962306a36Sopenharmony_ci	clk_unprepare(priv->usb_dc_ck);
30062306a36Sopenharmony_cierr_put_dc_ck:
30162306a36Sopenharmony_ci	clk_put(priv->usb_dc_ck);
30262306a36Sopenharmony_cierr_unprepare_host_ck:
30362306a36Sopenharmony_ci	clk_unprepare(priv->usb_host_ck);
30462306a36Sopenharmony_cierr_put_host_ck:
30562306a36Sopenharmony_ci	clk_put(priv->usb_host_ck);
30662306a36Sopenharmony_cierr_put_hcd:
30762306a36Sopenharmony_ci	usb_put_hcd(hcd);
30862306a36Sopenharmony_ci	return retval;
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci/* may be called with controller, bus, and devices active */
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci/**
31562306a36Sopenharmony_ci * ohci_hcd_omap_remove - shutdown processing for OMAP-based HCDs
31662306a36Sopenharmony_ci * @pdev: USB Host Controller being removed
31762306a36Sopenharmony_ci *
31862306a36Sopenharmony_ci * Context: task context, might sleep
31962306a36Sopenharmony_ci *
32062306a36Sopenharmony_ci * Reverses the effect of ohci_hcd_omap_probe(), first invoking
32162306a36Sopenharmony_ci * the HCD's stop() method.  It is always called from a thread
32262306a36Sopenharmony_ci * context, normally "rmmod", "apmd", or something similar.
32362306a36Sopenharmony_ci */
32462306a36Sopenharmony_cistatic void ohci_hcd_omap_remove(struct platform_device *pdev)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	struct usb_hcd	*hcd = platform_get_drvdata(pdev);
32762306a36Sopenharmony_ci	struct ohci_omap_priv *priv = hcd_to_ohci_omap_priv(hcd);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	dev_dbg(hcd->self.controller, "stopping USB Controller\n");
33062306a36Sopenharmony_ci	usb_remove_hcd(hcd);
33162306a36Sopenharmony_ci	omap_ohci_clock_power(priv, 0);
33262306a36Sopenharmony_ci	if (!IS_ERR_OR_NULL(hcd->usb_phy)) {
33362306a36Sopenharmony_ci		(void) otg_set_host(hcd->usb_phy->otg, 0);
33462306a36Sopenharmony_ci		usb_put_phy(hcd->usb_phy);
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci	iounmap(hcd->regs);
33762306a36Sopenharmony_ci	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
33862306a36Sopenharmony_ci	clk_unprepare(priv->usb_dc_ck);
33962306a36Sopenharmony_ci	clk_put(priv->usb_dc_ck);
34062306a36Sopenharmony_ci	clk_unprepare(priv->usb_host_ck);
34162306a36Sopenharmony_ci	clk_put(priv->usb_host_ck);
34262306a36Sopenharmony_ci	usb_put_hcd(hcd);
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci#ifdef	CONFIG_PM
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cistatic int ohci_omap_suspend(struct platform_device *pdev, pm_message_t message)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	struct usb_hcd *hcd = platform_get_drvdata(pdev);
35262306a36Sopenharmony_ci	struct ohci_hcd *ohci = hcd_to_ohci(hcd);
35362306a36Sopenharmony_ci	struct ohci_omap_priv *priv = hcd_to_ohci_omap_priv(hcd);
35462306a36Sopenharmony_ci	bool do_wakeup = device_may_wakeup(&pdev->dev);
35562306a36Sopenharmony_ci	int ret;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	if (time_before(jiffies, ohci->next_statechange))
35862306a36Sopenharmony_ci		msleep(5);
35962306a36Sopenharmony_ci	ohci->next_statechange = jiffies;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	ret = ohci_suspend(hcd, do_wakeup);
36262306a36Sopenharmony_ci	if (ret)
36362306a36Sopenharmony_ci		return ret;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	omap_ohci_clock_power(priv, 0);
36662306a36Sopenharmony_ci	return ret;
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic int ohci_omap_resume(struct platform_device *dev)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	struct usb_hcd	*hcd = platform_get_drvdata(dev);
37262306a36Sopenharmony_ci	struct ohci_hcd	*ohci = hcd_to_ohci(hcd);
37362306a36Sopenharmony_ci	struct ohci_omap_priv *priv = hcd_to_ohci_omap_priv(hcd);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	if (time_before(jiffies, ohci->next_statechange))
37662306a36Sopenharmony_ci		msleep(5);
37762306a36Sopenharmony_ci	ohci->next_statechange = jiffies;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	omap_ohci_clock_power(priv, 1);
38062306a36Sopenharmony_ci	ohci_resume(hcd, false);
38162306a36Sopenharmony_ci	return 0;
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci#endif
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci/*
38962306a36Sopenharmony_ci * Driver definition to register with the OMAP bus
39062306a36Sopenharmony_ci */
39162306a36Sopenharmony_cistatic struct platform_driver ohci_hcd_omap_driver = {
39262306a36Sopenharmony_ci	.probe		= ohci_hcd_omap_probe,
39362306a36Sopenharmony_ci	.remove_new	= ohci_hcd_omap_remove,
39462306a36Sopenharmony_ci	.shutdown	= usb_hcd_platform_shutdown,
39562306a36Sopenharmony_ci#ifdef	CONFIG_PM
39662306a36Sopenharmony_ci	.suspend	= ohci_omap_suspend,
39762306a36Sopenharmony_ci	.resume		= ohci_omap_resume,
39862306a36Sopenharmony_ci#endif
39962306a36Sopenharmony_ci	.driver		= {
40062306a36Sopenharmony_ci		.name	= "ohci",
40162306a36Sopenharmony_ci	},
40262306a36Sopenharmony_ci};
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistatic const struct ohci_driver_overrides omap_overrides __initconst = {
40562306a36Sopenharmony_ci	.product_desc	= "OMAP OHCI",
40662306a36Sopenharmony_ci	.reset		= ohci_omap_reset,
40762306a36Sopenharmony_ci	.extra_priv_size = sizeof(struct ohci_omap_priv),
40862306a36Sopenharmony_ci};
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_cistatic int __init ohci_omap_init(void)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	if (usb_disabled())
41362306a36Sopenharmony_ci		return -ENODEV;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	ohci_init_driver(&ohci_omap_hc_driver, &omap_overrides);
41662306a36Sopenharmony_ci	return platform_driver_register(&ohci_hcd_omap_driver);
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_cimodule_init(ohci_omap_init);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_cistatic void __exit ohci_omap_cleanup(void)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	platform_driver_unregister(&ohci_hcd_omap_driver);
42362306a36Sopenharmony_ci}
42462306a36Sopenharmony_cimodule_exit(ohci_omap_cleanup);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
42762306a36Sopenharmony_ciMODULE_ALIAS("platform:ohci");
42862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
429