18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * host.c - ChipIdea USB host controller driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2012 Intel Corporation
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Alexander Shishkin
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/io.h>
128c2ecf20Sopenharmony_ci#include <linux/usb.h>
138c2ecf20Sopenharmony_ci#include <linux/usb/hcd.h>
148c2ecf20Sopenharmony_ci#include <linux/usb/chipidea.h>
158c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h>
168c2ecf20Sopenharmony_ci#include <linux/pinctrl/consumer.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include "../host/ehci.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include "ci.h"
218c2ecf20Sopenharmony_ci#include "bits.h"
228c2ecf20Sopenharmony_ci#include "host.h"
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic struct hc_driver __read_mostly ci_ehci_hc_driver;
258c2ecf20Sopenharmony_cistatic int (*orig_bus_suspend)(struct usb_hcd *hcd);
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistruct ehci_ci_priv {
288c2ecf20Sopenharmony_ci	struct regulator *reg_vbus;
298c2ecf20Sopenharmony_ci	bool enabled;
308c2ecf20Sopenharmony_ci};
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool enable)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
358c2ecf20Sopenharmony_ci	struct ehci_ci_priv *priv = (struct ehci_ci_priv *)ehci->priv;
368c2ecf20Sopenharmony_ci	struct device *dev = hcd->self.controller;
378c2ecf20Sopenharmony_ci	struct ci_hdrc *ci = dev_get_drvdata(dev);
388c2ecf20Sopenharmony_ci	int ret = 0;
398c2ecf20Sopenharmony_ci	int port = HCS_N_PORTS(ehci->hcs_params);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	if (priv->reg_vbus && enable != priv->enabled) {
428c2ecf20Sopenharmony_ci		if (port > 1) {
438c2ecf20Sopenharmony_ci			dev_warn(dev,
448c2ecf20Sopenharmony_ci				"Not support multi-port regulator control\n");
458c2ecf20Sopenharmony_ci			return 0;
468c2ecf20Sopenharmony_ci		}
478c2ecf20Sopenharmony_ci		if (enable)
488c2ecf20Sopenharmony_ci			ret = regulator_enable(priv->reg_vbus);
498c2ecf20Sopenharmony_ci		else
508c2ecf20Sopenharmony_ci			ret = regulator_disable(priv->reg_vbus);
518c2ecf20Sopenharmony_ci		if (ret) {
528c2ecf20Sopenharmony_ci			dev_err(dev,
538c2ecf20Sopenharmony_ci				"Failed to %s vbus regulator, ret=%d\n",
548c2ecf20Sopenharmony_ci				enable ? "enable" : "disable", ret);
558c2ecf20Sopenharmony_ci			return ret;
568c2ecf20Sopenharmony_ci		}
578c2ecf20Sopenharmony_ci		priv->enabled = enable;
588c2ecf20Sopenharmony_ci	}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	if (enable && (ci->platdata->phy_mode == USBPHY_INTERFACE_MODE_HSIC)) {
618c2ecf20Sopenharmony_ci		/*
628c2ecf20Sopenharmony_ci		 * Marvell 28nm HSIC PHY requires forcing the port to HS mode.
638c2ecf20Sopenharmony_ci		 * As HSIC is always HS, this should be safe for others.
648c2ecf20Sopenharmony_ci		 */
658c2ecf20Sopenharmony_ci		hw_port_test_set(ci, 5);
668c2ecf20Sopenharmony_ci		hw_port_test_set(ci, 0);
678c2ecf20Sopenharmony_ci	}
688c2ecf20Sopenharmony_ci	return 0;
698c2ecf20Sopenharmony_ci};
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic int ehci_ci_reset(struct usb_hcd *hcd)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	struct device *dev = hcd->self.controller;
748c2ecf20Sopenharmony_ci	struct ci_hdrc *ci = dev_get_drvdata(dev);
758c2ecf20Sopenharmony_ci	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
768c2ecf20Sopenharmony_ci	int ret;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	ret = ehci_setup(hcd);
798c2ecf20Sopenharmony_ci	if (ret)
808c2ecf20Sopenharmony_ci		return ret;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	ehci->need_io_watchdog = 0;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	if (ci->platdata->notify_event) {
858c2ecf20Sopenharmony_ci		ret = ci->platdata->notify_event(ci,
868c2ecf20Sopenharmony_ci				CI_HDRC_CONTROLLER_RESET_EVENT);
878c2ecf20Sopenharmony_ci		if (ret)
888c2ecf20Sopenharmony_ci			return ret;
898c2ecf20Sopenharmony_ci	}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	ci_platform_configure(ci);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	return ret;
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic const struct ehci_driver_overrides ehci_ci_overrides = {
978c2ecf20Sopenharmony_ci	.extra_priv_size = sizeof(struct ehci_ci_priv),
988c2ecf20Sopenharmony_ci	.port_power	 = ehci_ci_portpower,
998c2ecf20Sopenharmony_ci	.reset		 = ehci_ci_reset,
1008c2ecf20Sopenharmony_ci};
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic irqreturn_t host_irq(struct ci_hdrc *ci)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	return usb_hcd_irq(ci->irq, ci->hcd);
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic int host_start(struct ci_hdrc *ci)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	struct usb_hcd *hcd;
1108c2ecf20Sopenharmony_ci	struct ehci_hcd *ehci;
1118c2ecf20Sopenharmony_ci	struct ehci_ci_priv *priv;
1128c2ecf20Sopenharmony_ci	int ret;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	if (usb_disabled())
1158c2ecf20Sopenharmony_ci		return -ENODEV;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	hcd = __usb_create_hcd(&ci_ehci_hc_driver, ci->dev->parent,
1188c2ecf20Sopenharmony_ci			       ci->dev, dev_name(ci->dev), NULL);
1198c2ecf20Sopenharmony_ci	if (!hcd)
1208c2ecf20Sopenharmony_ci		return -ENOMEM;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	dev_set_drvdata(ci->dev, ci);
1238c2ecf20Sopenharmony_ci	hcd->rsrc_start = ci->hw_bank.phys;
1248c2ecf20Sopenharmony_ci	hcd->rsrc_len = ci->hw_bank.size;
1258c2ecf20Sopenharmony_ci	hcd->regs = ci->hw_bank.abs;
1268c2ecf20Sopenharmony_ci	hcd->has_tt = 1;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	hcd->power_budget = ci->platdata->power_budget;
1298c2ecf20Sopenharmony_ci	hcd->tpl_support = ci->platdata->tpl_support;
1308c2ecf20Sopenharmony_ci	if (ci->phy || ci->usb_phy) {
1318c2ecf20Sopenharmony_ci		hcd->skip_phy_initialization = 1;
1328c2ecf20Sopenharmony_ci		if (ci->usb_phy)
1338c2ecf20Sopenharmony_ci			hcd->usb_phy = ci->usb_phy;
1348c2ecf20Sopenharmony_ci	}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	ehci = hcd_to_ehci(hcd);
1378c2ecf20Sopenharmony_ci	ehci->caps = ci->hw_bank.cap;
1388c2ecf20Sopenharmony_ci	ehci->has_hostpc = ci->hw_bank.lpm;
1398c2ecf20Sopenharmony_ci	ehci->has_tdi_phy_lpm = ci->hw_bank.lpm;
1408c2ecf20Sopenharmony_ci	ehci->imx28_write_fix = ci->imx28_write_fix;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	priv = (struct ehci_ci_priv *)ehci->priv;
1438c2ecf20Sopenharmony_ci	priv->reg_vbus = NULL;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci)) {
1468c2ecf20Sopenharmony_ci		if (ci->platdata->flags & CI_HDRC_TURN_VBUS_EARLY_ON) {
1478c2ecf20Sopenharmony_ci			ret = regulator_enable(ci->platdata->reg_vbus);
1488c2ecf20Sopenharmony_ci			if (ret) {
1498c2ecf20Sopenharmony_ci				dev_err(ci->dev,
1508c2ecf20Sopenharmony_ci				"Failed to enable vbus regulator, ret=%d\n",
1518c2ecf20Sopenharmony_ci									ret);
1528c2ecf20Sopenharmony_ci				goto put_hcd;
1538c2ecf20Sopenharmony_ci			}
1548c2ecf20Sopenharmony_ci		} else {
1558c2ecf20Sopenharmony_ci			priv->reg_vbus = ci->platdata->reg_vbus;
1568c2ecf20Sopenharmony_ci		}
1578c2ecf20Sopenharmony_ci	}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if (ci->platdata->pins_host)
1608c2ecf20Sopenharmony_ci		pinctrl_select_state(ci->platdata->pctl,
1618c2ecf20Sopenharmony_ci				     ci->platdata->pins_host);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	ret = usb_add_hcd(hcd, 0, 0);
1648c2ecf20Sopenharmony_ci	if (ret) {
1658c2ecf20Sopenharmony_ci		goto disable_reg;
1668c2ecf20Sopenharmony_ci	} else {
1678c2ecf20Sopenharmony_ci		struct usb_otg *otg = &ci->otg;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci		ci->hcd = hcd;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci		if (ci_otg_is_fsm_mode(ci)) {
1728c2ecf20Sopenharmony_ci			otg->host = &hcd->self;
1738c2ecf20Sopenharmony_ci			hcd->self.otg_port = 1;
1748c2ecf20Sopenharmony_ci		}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci		if (ci->platdata->notify_event &&
1778c2ecf20Sopenharmony_ci			(ci->platdata->flags & CI_HDRC_IMX_IS_HSIC))
1788c2ecf20Sopenharmony_ci			ci->platdata->notify_event
1798c2ecf20Sopenharmony_ci				(ci, CI_HDRC_IMX_HSIC_ACTIVE_EVENT);
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	return ret;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cidisable_reg:
1858c2ecf20Sopenharmony_ci	if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci) &&
1868c2ecf20Sopenharmony_ci			(ci->platdata->flags & CI_HDRC_TURN_VBUS_EARLY_ON))
1878c2ecf20Sopenharmony_ci		regulator_disable(ci->platdata->reg_vbus);
1888c2ecf20Sopenharmony_ciput_hcd:
1898c2ecf20Sopenharmony_ci	usb_put_hcd(hcd);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	return ret;
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic void host_stop(struct ci_hdrc *ci)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	struct usb_hcd *hcd = ci->hcd;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if (hcd) {
1998c2ecf20Sopenharmony_ci		if (ci->platdata->notify_event)
2008c2ecf20Sopenharmony_ci			ci->platdata->notify_event(ci,
2018c2ecf20Sopenharmony_ci				CI_HDRC_CONTROLLER_STOPPED_EVENT);
2028c2ecf20Sopenharmony_ci		usb_remove_hcd(hcd);
2038c2ecf20Sopenharmony_ci		ci->role = CI_ROLE_END;
2048c2ecf20Sopenharmony_ci		synchronize_irq(ci->irq);
2058c2ecf20Sopenharmony_ci		usb_put_hcd(hcd);
2068c2ecf20Sopenharmony_ci		if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci) &&
2078c2ecf20Sopenharmony_ci			(ci->platdata->flags & CI_HDRC_TURN_VBUS_EARLY_ON))
2088c2ecf20Sopenharmony_ci				regulator_disable(ci->platdata->reg_vbus);
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci	ci->hcd = NULL;
2118c2ecf20Sopenharmony_ci	ci->otg.host = NULL;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	if (ci->platdata->pins_host && ci->platdata->pins_default)
2148c2ecf20Sopenharmony_ci		pinctrl_select_state(ci->platdata->pctl,
2158c2ecf20Sopenharmony_ci				     ci->platdata->pins_default);
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_civoid ci_hdrc_host_destroy(struct ci_hdrc *ci)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	if (ci->role == CI_ROLE_HOST && ci->hcd)
2228c2ecf20Sopenharmony_ci		host_stop(ci);
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci/* The below code is based on tegra ehci driver */
2268c2ecf20Sopenharmony_cistatic int ci_ehci_hub_control(
2278c2ecf20Sopenharmony_ci	struct usb_hcd	*hcd,
2288c2ecf20Sopenharmony_ci	u16		typeReq,
2298c2ecf20Sopenharmony_ci	u16		wValue,
2308c2ecf20Sopenharmony_ci	u16		wIndex,
2318c2ecf20Sopenharmony_ci	char		*buf,
2328c2ecf20Sopenharmony_ci	u16		wLength
2338c2ecf20Sopenharmony_ci)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	struct ehci_hcd	*ehci = hcd_to_ehci(hcd);
2368c2ecf20Sopenharmony_ci	unsigned int	ports = HCS_N_PORTS(ehci->hcs_params);
2378c2ecf20Sopenharmony_ci	u32 __iomem	*status_reg;
2388c2ecf20Sopenharmony_ci	u32		temp, port_index;
2398c2ecf20Sopenharmony_ci	unsigned long	flags;
2408c2ecf20Sopenharmony_ci	int		retval = 0;
2418c2ecf20Sopenharmony_ci	struct device *dev = hcd->self.controller;
2428c2ecf20Sopenharmony_ci	struct ci_hdrc *ci = dev_get_drvdata(dev);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	port_index = wIndex & 0xff;
2458c2ecf20Sopenharmony_ci	port_index -= (port_index > 0);
2468c2ecf20Sopenharmony_ci	status_reg = &ehci->regs->port_status[port_index];
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ehci->lock, flags);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	if (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_SUSPEND) {
2518c2ecf20Sopenharmony_ci		if (!wIndex || wIndex > ports) {
2528c2ecf20Sopenharmony_ci			retval = -EPIPE;
2538c2ecf20Sopenharmony_ci			goto done;
2548c2ecf20Sopenharmony_ci		}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci		temp = ehci_readl(ehci, status_reg);
2578c2ecf20Sopenharmony_ci		if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) {
2588c2ecf20Sopenharmony_ci			retval = -EPIPE;
2598c2ecf20Sopenharmony_ci			goto done;
2608c2ecf20Sopenharmony_ci		}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci		temp &= ~(PORT_RWC_BITS | PORT_WKCONN_E);
2638c2ecf20Sopenharmony_ci		temp |= PORT_WKDISC_E | PORT_WKOC_E;
2648c2ecf20Sopenharmony_ci		ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci		/*
2678c2ecf20Sopenharmony_ci		 * If a transaction is in progress, there may be a delay in
2688c2ecf20Sopenharmony_ci		 * suspending the port. Poll until the port is suspended.
2698c2ecf20Sopenharmony_ci		 */
2708c2ecf20Sopenharmony_ci		if (ehci_handshake(ehci, status_reg, PORT_SUSPEND,
2718c2ecf20Sopenharmony_ci			PORT_SUSPEND, 5000))
2728c2ecf20Sopenharmony_ci			ehci_err(ehci, "timeout waiting for SUSPEND\n");
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci		if (ci->platdata->flags & CI_HDRC_IMX_IS_HSIC) {
2758c2ecf20Sopenharmony_ci			if (ci->platdata->notify_event)
2768c2ecf20Sopenharmony_ci				ci->platdata->notify_event(ci,
2778c2ecf20Sopenharmony_ci					CI_HDRC_IMX_HSIC_SUSPEND_EVENT);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci			temp = ehci_readl(ehci, status_reg);
2808c2ecf20Sopenharmony_ci			temp &= ~(PORT_WKDISC_E | PORT_WKCONN_E);
2818c2ecf20Sopenharmony_ci			ehci_writel(ehci, temp, status_reg);
2828c2ecf20Sopenharmony_ci		}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci		set_bit(port_index, &ehci->suspended_ports);
2858c2ecf20Sopenharmony_ci		goto done;
2868c2ecf20Sopenharmony_ci	}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	/*
2898c2ecf20Sopenharmony_ci	 * After resume has finished, it needs do some post resume
2908c2ecf20Sopenharmony_ci	 * operation for some SoCs.
2918c2ecf20Sopenharmony_ci	 */
2928c2ecf20Sopenharmony_ci	else if (typeReq == ClearPortFeature &&
2938c2ecf20Sopenharmony_ci		wValue == USB_PORT_FEAT_C_SUSPEND) {
2948c2ecf20Sopenharmony_ci		/* Make sure the resume has finished, it should be finished */
2958c2ecf20Sopenharmony_ci		if (ehci_handshake(ehci, status_reg, PORT_RESUME, 0, 25000))
2968c2ecf20Sopenharmony_ci			ehci_err(ehci, "timeout waiting for resume\n");
2978c2ecf20Sopenharmony_ci	}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ehci->lock, flags);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	/* Handle the hub control events here */
3028c2ecf20Sopenharmony_ci	return ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
3038c2ecf20Sopenharmony_cidone:
3048c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ehci->lock, flags);
3058c2ecf20Sopenharmony_ci	return retval;
3068c2ecf20Sopenharmony_ci}
3078c2ecf20Sopenharmony_cistatic int ci_ehci_bus_suspend(struct usb_hcd *hcd)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
3108c2ecf20Sopenharmony_ci	struct device *dev = hcd->self.controller;
3118c2ecf20Sopenharmony_ci	struct ci_hdrc *ci = dev_get_drvdata(dev);
3128c2ecf20Sopenharmony_ci	int port;
3138c2ecf20Sopenharmony_ci	u32 tmp;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	int ret = orig_bus_suspend(hcd);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	if (ret)
3188c2ecf20Sopenharmony_ci		return ret;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	port = HCS_N_PORTS(ehci->hcs_params);
3218c2ecf20Sopenharmony_ci	while (port--) {
3228c2ecf20Sopenharmony_ci		u32 __iomem *reg = &ehci->regs->port_status[port];
3238c2ecf20Sopenharmony_ci		u32 portsc = ehci_readl(ehci, reg);
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci		if (portsc & PORT_CONNECT) {
3268c2ecf20Sopenharmony_ci			/*
3278c2ecf20Sopenharmony_ci			 * For chipidea, the resume signal will be ended
3288c2ecf20Sopenharmony_ci			 * automatically, so for remote wakeup case, the
3298c2ecf20Sopenharmony_ci			 * usbcmd.rs may not be set before the resume has
3308c2ecf20Sopenharmony_ci			 * ended if other resume paths consumes too much
3318c2ecf20Sopenharmony_ci			 * time (~24ms), in that case, the SOF will not
3328c2ecf20Sopenharmony_ci			 * send out within 3ms after resume ends, then the
3338c2ecf20Sopenharmony_ci			 * high speed device will enter full speed mode.
3348c2ecf20Sopenharmony_ci			 */
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci			tmp = ehci_readl(ehci, &ehci->regs->command);
3378c2ecf20Sopenharmony_ci			tmp |= CMD_RUN;
3388c2ecf20Sopenharmony_ci			ehci_writel(ehci, tmp, &ehci->regs->command);
3398c2ecf20Sopenharmony_ci			/*
3408c2ecf20Sopenharmony_ci			 * It needs a short delay between set RS bit and PHCD.
3418c2ecf20Sopenharmony_ci			 */
3428c2ecf20Sopenharmony_ci			usleep_range(150, 200);
3438c2ecf20Sopenharmony_ci			/*
3448c2ecf20Sopenharmony_ci			 * Need to clear WKCN and WKOC for imx HSIC,
3458c2ecf20Sopenharmony_ci			 * otherwise, there will be wakeup event.
3468c2ecf20Sopenharmony_ci			 */
3478c2ecf20Sopenharmony_ci			if (ci->platdata->flags & CI_HDRC_IMX_IS_HSIC) {
3488c2ecf20Sopenharmony_ci				tmp = ehci_readl(ehci, reg);
3498c2ecf20Sopenharmony_ci				tmp &= ~(PORT_WKDISC_E | PORT_WKCONN_E);
3508c2ecf20Sopenharmony_ci				ehci_writel(ehci, tmp, reg);
3518c2ecf20Sopenharmony_ci			}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci			break;
3548c2ecf20Sopenharmony_ci		}
3558c2ecf20Sopenharmony_ci	}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	return 0;
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ciint ci_hdrc_host_init(struct ci_hdrc *ci)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	struct ci_role_driver *rdrv;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	if (!hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_HC))
3658c2ecf20Sopenharmony_ci		return -ENXIO;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	rdrv = devm_kzalloc(ci->dev, sizeof(struct ci_role_driver), GFP_KERNEL);
3688c2ecf20Sopenharmony_ci	if (!rdrv)
3698c2ecf20Sopenharmony_ci		return -ENOMEM;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	rdrv->start	= host_start;
3728c2ecf20Sopenharmony_ci	rdrv->stop	= host_stop;
3738c2ecf20Sopenharmony_ci	rdrv->irq	= host_irq;
3748c2ecf20Sopenharmony_ci	rdrv->name	= "host";
3758c2ecf20Sopenharmony_ci	ci->roles[CI_ROLE_HOST] = rdrv;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	return 0;
3788c2ecf20Sopenharmony_ci}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_civoid ci_hdrc_host_driver_init(void)
3818c2ecf20Sopenharmony_ci{
3828c2ecf20Sopenharmony_ci	ehci_init_driver(&ci_ehci_hc_driver, &ehci_ci_overrides);
3838c2ecf20Sopenharmony_ci	orig_bus_suspend = ci_ehci_hc_driver.bus_suspend;
3848c2ecf20Sopenharmony_ci	ci_ehci_hc_driver.bus_suspend = ci_ehci_bus_suspend;
3858c2ecf20Sopenharmony_ci	ci_ehci_hc_driver.hub_control = ci_ehci_hub_control;
3868c2ecf20Sopenharmony_ci}
387