xref: /openbmc/linux/drivers/usb/chipidea/host.c (revision 568c30c4)
15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2eb70e5abSAlexander Shishkin /*
3eb70e5abSAlexander Shishkin  * host.c - ChipIdea USB host controller driver
4eb70e5abSAlexander Shishkin  *
5eb70e5abSAlexander Shishkin  * Copyright (c) 2012 Intel Corporation
6eb70e5abSAlexander Shishkin  *
7eb70e5abSAlexander Shishkin  * Author: Alexander Shishkin
8eb70e5abSAlexander Shishkin  */
9eb70e5abSAlexander Shishkin 
10eb70e5abSAlexander Shishkin #include <linux/kernel.h>
11cdb2fac7SAlan Stern #include <linux/io.h>
12eb70e5abSAlexander Shishkin #include <linux/usb.h>
13eb70e5abSAlexander Shishkin #include <linux/usb/hcd.h>
14eb70e5abSAlexander Shishkin #include <linux/usb/chipidea.h>
1540ed51a4SPeter Chen #include <linux/regulator/consumer.h>
1616caf1faSLoic Poulain #include <linux/pinctrl/consumer.h>
17eb70e5abSAlexander Shishkin 
1809f6ffdeSAlan Stern #include "../host/ehci.h"
19eb70e5abSAlexander Shishkin 
20eb70e5abSAlexander Shishkin #include "ci.h"
21eb70e5abSAlexander Shishkin #include "bits.h"
22eb70e5abSAlexander Shishkin #include "host.h"
23eb70e5abSAlexander Shishkin 
2409f6ffdeSAlan Stern static struct hc_driver __read_mostly ci_ehci_hc_driver;
2578f0357eSPeter Chen static int (*orig_bus_suspend)(struct usb_hcd *hcd);
2609f6ffdeSAlan Stern 
27c8679a2fSMichael Grzeschik struct ehci_ci_priv {
28c8679a2fSMichael Grzeschik 	struct regulator *reg_vbus;
29c1ffba30SGuenter Roeck 	bool enabled;
30c8679a2fSMichael Grzeschik };
31c8679a2fSMichael Grzeschik 
32fc53d527SPeter Geis struct ci_hdrc_dma_aligned_buffer {
33*568c30c4SMichał Mirosław 	void *original_buffer;
344294a8c2Skernel test robot 	u8 data[];
35fc53d527SPeter Geis };
36fc53d527SPeter Geis 
ehci_ci_portpower(struct usb_hcd * hcd,int portnum,bool enable)37c8679a2fSMichael Grzeschik static int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool enable)
38c8679a2fSMichael Grzeschik {
39c8679a2fSMichael Grzeschik 	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
40c8679a2fSMichael Grzeschik 	struct ehci_ci_priv *priv = (struct ehci_ci_priv *)ehci->priv;
41c8679a2fSMichael Grzeschik 	struct device *dev = hcd->self.controller;
421311d6e3SPeter Chen 	struct ci_hdrc *ci = dev_get_drvdata(dev);
43c8679a2fSMichael Grzeschik 	int ret = 0;
44c8679a2fSMichael Grzeschik 	int port = HCS_N_PORTS(ehci->hcs_params);
45c8679a2fSMichael Grzeschik 
46c1ffba30SGuenter Roeck 	if (priv->reg_vbus && enable != priv->enabled) {
47c8679a2fSMichael Grzeschik 		if (port > 1) {
48c8679a2fSMichael Grzeschik 			dev_warn(dev,
49c8679a2fSMichael Grzeschik 				"Not support multi-port regulator control\n");
50c8679a2fSMichael Grzeschik 			return 0;
51c8679a2fSMichael Grzeschik 		}
52c8679a2fSMichael Grzeschik 		if (enable)
53c8679a2fSMichael Grzeschik 			ret = regulator_enable(priv->reg_vbus);
54c8679a2fSMichael Grzeschik 		else
55c8679a2fSMichael Grzeschik 			ret = regulator_disable(priv->reg_vbus);
56c8679a2fSMichael Grzeschik 		if (ret) {
57c8679a2fSMichael Grzeschik 			dev_err(dev,
58c8679a2fSMichael Grzeschik 				"Failed to %s vbus regulator, ret=%d\n",
59c8679a2fSMichael Grzeschik 				enable ? "enable" : "disable", ret);
60c8679a2fSMichael Grzeschik 			return ret;
61c8679a2fSMichael Grzeschik 		}
62c1ffba30SGuenter Roeck 		priv->enabled = enable;
63c8679a2fSMichael Grzeschik 	}
64fc6b68baSRob Herring 
6566d1c802SPiyush Mehta 	if (ci->platdata->flags & CI_HDRC_PHY_VBUS_CONTROL) {
6666d1c802SPiyush Mehta 		if (enable)
6766d1c802SPiyush Mehta 			usb_phy_vbus_on(ci->usb_phy);
6866d1c802SPiyush Mehta 		else
6966d1c802SPiyush Mehta 			usb_phy_vbus_off(ci->usb_phy);
7066d1c802SPiyush Mehta 	}
7166d1c802SPiyush Mehta 
72fc6b68baSRob Herring 	if (enable && (ci->platdata->phy_mode == USBPHY_INTERFACE_MODE_HSIC)) {
73fc6b68baSRob Herring 		/*
74fc6b68baSRob Herring 		 * Marvell 28nm HSIC PHY requires forcing the port to HS mode.
75fc6b68baSRob Herring 		 * As HSIC is always HS, this should be safe for others.
76fc6b68baSRob Herring 		 */
77fc6b68baSRob Herring 		hw_port_test_set(ci, 5);
78fc6b68baSRob Herring 		hw_port_test_set(ci, 0);
79fc6b68baSRob Herring 	}
80c8679a2fSMichael Grzeschik 	return 0;
81c8679a2fSMichael Grzeschik };
82c8679a2fSMichael Grzeschik 
ehci_ci_reset(struct usb_hcd * hcd)8311a27098SPeter Chen static int ehci_ci_reset(struct usb_hcd *hcd)
8411a27098SPeter Chen {
8511a27098SPeter Chen 	struct device *dev = hcd->self.controller;
8611a27098SPeter Chen 	struct ci_hdrc *ci = dev_get_drvdata(dev);
87c744a0dbSLucas Stach 	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
8811a27098SPeter Chen 	int ret;
8911a27098SPeter Chen 
9011a27098SPeter Chen 	ret = ehci_setup(hcd);
9111a27098SPeter Chen 	if (ret)
9211a27098SPeter Chen 		return ret;
9311a27098SPeter Chen 
94c744a0dbSLucas Stach 	ehci->need_io_watchdog = 0;
95c744a0dbSLucas Stach 
9611893daeSStephen Boyd 	if (ci->platdata->notify_event) {
9711893daeSStephen Boyd 		ret = ci->platdata->notify_event(ci,
9811893daeSStephen Boyd 				CI_HDRC_CONTROLLER_RESET_EVENT);
9911893daeSStephen Boyd 		if (ret)
10011893daeSStephen Boyd 			return ret;
10111893daeSStephen Boyd 	}
102b90a17c5SStephen Boyd 
10311a27098SPeter Chen 	ci_platform_configure(ci);
10411a27098SPeter Chen 
10511a27098SPeter Chen 	return ret;
10611a27098SPeter Chen }
10711a27098SPeter Chen 
108c8679a2fSMichael Grzeschik static const struct ehci_driver_overrides ehci_ci_overrides = {
109c8679a2fSMichael Grzeschik 	.extra_priv_size = sizeof(struct ehci_ci_priv),
110c8679a2fSMichael Grzeschik 	.port_power	 = ehci_ci_portpower,
11111a27098SPeter Chen 	.reset		 = ehci_ci_reset,
112c8679a2fSMichael Grzeschik };
113c8679a2fSMichael Grzeschik 
host_irq(struct ci_hdrc * ci)1148e22978cSAlexander Shishkin static irqreturn_t host_irq(struct ci_hdrc *ci)
115eb70e5abSAlexander Shishkin {
116eb70e5abSAlexander Shishkin 	return usb_hcd_irq(ci->irq, ci->hcd);
117eb70e5abSAlexander Shishkin }
118eb70e5abSAlexander Shishkin 
host_start(struct ci_hdrc * ci)1198e22978cSAlexander Shishkin static int host_start(struct ci_hdrc *ci)
120eb70e5abSAlexander Shishkin {
121eb70e5abSAlexander Shishkin 	struct usb_hcd *hcd;
122eb70e5abSAlexander Shishkin 	struct ehci_hcd *ehci;
123c8679a2fSMichael Grzeschik 	struct ehci_ci_priv *priv;
124eb70e5abSAlexander Shishkin 	int ret;
125eb70e5abSAlexander Shishkin 
126eb70e5abSAlexander Shishkin 	if (usb_disabled())
127eb70e5abSAlexander Shishkin 		return -ENODEV;
128eb70e5abSAlexander Shishkin 
129aeb78cdaSArnd Bergmann 	hcd = __usb_create_hcd(&ci_ehci_hc_driver, ci->dev->parent,
130aeb78cdaSArnd Bergmann 			       ci->dev, dev_name(ci->dev), NULL);
131eb70e5abSAlexander Shishkin 	if (!hcd)
132eb70e5abSAlexander Shishkin 		return -ENOMEM;
133eb70e5abSAlexander Shishkin 
13424c498dfSPeter Chen 	dev_set_drvdata(ci->dev, ci);
135eb70e5abSAlexander Shishkin 	hcd->rsrc_start = ci->hw_bank.phys;
136eb70e5abSAlexander Shishkin 	hcd->rsrc_len = ci->hw_bank.size;
137eb70e5abSAlexander Shishkin 	hcd->regs = ci->hw_bank.abs;
138eb70e5abSAlexander Shishkin 	hcd->has_tt = 1;
139eb70e5abSAlexander Shishkin 
14077c4400fSRichard Zhao 	hcd->power_budget = ci->platdata->power_budget;
141f6a9ff07SPeter Chen 	hcd->tpl_support = ci->platdata->tpl_support;
14290f26cc6SPeter Chen 	if (ci->phy || ci->usb_phy) {
1434e88d4c0SMartin Blumenstingl 		hcd->skip_phy_initialization = 1;
14490f26cc6SPeter Chen 		if (ci->usb_phy)
14590f26cc6SPeter Chen 			hcd->usb_phy = ci->usb_phy;
14690f26cc6SPeter Chen 	}
147bd841986SAlexander Shishkin 
148eb70e5abSAlexander Shishkin 	ehci = hcd_to_ehci(hcd);
149eb70e5abSAlexander Shishkin 	ehci->caps = ci->hw_bank.cap;
150eb70e5abSAlexander Shishkin 	ehci->has_hostpc = ci->hw_bank.lpm;
1512cdcec4fSTuomas Tynkkynen 	ehci->has_tdi_phy_lpm = ci->hw_bank.lpm;
152ed8f8318SPeter Chen 	ehci->imx28_write_fix = ci->imx28_write_fix;
15312e6ac69SXu Yang 	ehci->has_ci_pec_bug = ci->has_portsc_pec_bug;
154eb70e5abSAlexander Shishkin 
155c8679a2fSMichael Grzeschik 	priv = (struct ehci_ci_priv *)ehci->priv;
156c8679a2fSMichael Grzeschik 	priv->reg_vbus = NULL;
157c8679a2fSMichael Grzeschik 
15865945917SLi Jun 	if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci)) {
15965945917SLi Jun 		if (ci->platdata->flags & CI_HDRC_TURN_VBUS_EARLY_ON) {
16065945917SLi Jun 			ret = regulator_enable(ci->platdata->reg_vbus);
16165945917SLi Jun 			if (ret) {
16265945917SLi Jun 				dev_err(ci->dev,
16365945917SLi Jun 				"Failed to enable vbus regulator, ret=%d\n",
16465945917SLi Jun 									ret);
16565945917SLi Jun 				goto put_hcd;
16665945917SLi Jun 			}
16765945917SLi Jun 		} else {
168c8679a2fSMichael Grzeschik 			priv->reg_vbus = ci->platdata->reg_vbus;
16965945917SLi Jun 		}
17065945917SLi Jun 	}
17140ed51a4SPeter Chen 
17216caf1faSLoic Poulain 	if (ci->platdata->pins_host)
17316caf1faSLoic Poulain 		pinctrl_select_state(ci->platdata->pctl,
17416caf1faSLoic Poulain 				     ci->platdata->pins_host);
17516caf1faSLoic Poulain 
176fc53d527SPeter Geis 	ci->hcd = hcd;
177fc53d527SPeter Geis 
178eb70e5abSAlexander Shishkin 	ret = usb_add_hcd(hcd, 0, 0);
1790698b9b3SLi Jun 	if (ret) {
180fc53d527SPeter Geis 		ci->hcd = NULL;
18165945917SLi Jun 		goto disable_reg;
1820698b9b3SLi Jun 	} else {
183ef44cb42SAntoine Tenart 		struct usb_otg *otg = &ci->otg;
1840698b9b3SLi Jun 
185ef44cb42SAntoine Tenart 		if (ci_otg_is_fsm_mode(ci)) {
1860698b9b3SLi Jun 			otg->host = &hcd->self;
1870698b9b3SLi Jun 			hcd->self.otg_port = 1;
1880698b9b3SLi Jun 		}
189014abe34SPeter Chen 
190014abe34SPeter Chen 		if (ci->platdata->notify_event &&
191014abe34SPeter Chen 			(ci->platdata->flags & CI_HDRC_IMX_IS_HSIC))
192014abe34SPeter Chen 			ci->platdata->notify_event
193014abe34SPeter Chen 				(ci, CI_HDRC_IMX_HSIC_ACTIVE_EVENT);
1940698b9b3SLi Jun 	}
195eb70e5abSAlexander Shishkin 
196eb70e5abSAlexander Shishkin 	return ret;
19740ed51a4SPeter Chen 
19865945917SLi Jun disable_reg:
19965945917SLi Jun 	if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci) &&
20065945917SLi Jun 			(ci->platdata->flags & CI_HDRC_TURN_VBUS_EARLY_ON))
20165945917SLi Jun 		regulator_disable(ci->platdata->reg_vbus);
20240ed51a4SPeter Chen put_hcd:
20340ed51a4SPeter Chen 	usb_put_hcd(hcd);
20440ed51a4SPeter Chen 
20540ed51a4SPeter Chen 	return ret;
206eb70e5abSAlexander Shishkin }
207eb70e5abSAlexander Shishkin 
host_stop(struct ci_hdrc * ci)2088e22978cSAlexander Shishkin static void host_stop(struct ci_hdrc *ci)
209eb70e5abSAlexander Shishkin {
210eb70e5abSAlexander Shishkin 	struct usb_hcd *hcd = ci->hcd;
211eb70e5abSAlexander Shishkin 
21241314feaSRussell King - ARM Linux 	if (hcd) {
213b90a17c5SStephen Boyd 		if (ci->platdata->notify_event)
214b90a17c5SStephen Boyd 			ci->platdata->notify_event(ci,
215b90a17c5SStephen Boyd 				CI_HDRC_CONTROLLER_STOPPED_EVENT);
216eb70e5abSAlexander Shishkin 		usb_remove_hcd(hcd);
217991d5addSStefan Wahren 		ci->role = CI_ROLE_END;
218991d5addSStefan Wahren 		synchronize_irq(ci->irq);
219eb70e5abSAlexander Shishkin 		usb_put_hcd(hcd);
22065945917SLi Jun 		if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci) &&
22165945917SLi Jun 			(ci->platdata->flags & CI_HDRC_TURN_VBUS_EARLY_ON))
22265945917SLi Jun 				regulator_disable(ci->platdata->reg_vbus);
223eb70e5abSAlexander Shishkin 	}
22443a40457SLi Jun 	ci->hcd = NULL;
22543a40457SLi Jun 	ci->otg.host = NULL;
22616caf1faSLoic Poulain 
22716caf1faSLoic Poulain 	if (ci->platdata->pins_host && ci->platdata->pins_default)
22816caf1faSLoic Poulain 		pinctrl_select_state(ci->platdata->pctl,
22916caf1faSLoic Poulain 				     ci->platdata->pins_default);
230df101c53SPeter Chen }
231eb70e5abSAlexander Shishkin 
2323f124d23SPeter Chen 
ci_hdrc_host_destroy(struct ci_hdrc * ci)2333f124d23SPeter Chen void ci_hdrc_host_destroy(struct ci_hdrc *ci)
2343f124d23SPeter Chen {
235df101c53SPeter Chen 	if (ci->role == CI_ROLE_HOST && ci->hcd)
2363f124d23SPeter Chen 		host_stop(ci);
2373f124d23SPeter Chen }
2383f124d23SPeter Chen 
2392c4593ecSPeter Chen /* The below code is based on tegra ehci driver */
ci_ehci_hub_control(struct usb_hcd * hcd,u16 typeReq,u16 wValue,u16 wIndex,char * buf,u16 wLength)2402c4593ecSPeter Chen static int ci_ehci_hub_control(
2412c4593ecSPeter Chen 	struct usb_hcd	*hcd,
2422c4593ecSPeter Chen 	u16		typeReq,
2432c4593ecSPeter Chen 	u16		wValue,
2442c4593ecSPeter Chen 	u16		wIndex,
2452c4593ecSPeter Chen 	char		*buf,
2462c4593ecSPeter Chen 	u16		wLength
2472c4593ecSPeter Chen )
2482c4593ecSPeter Chen {
2492c4593ecSPeter Chen 	struct ehci_hcd	*ehci = hcd_to_ehci(hcd);
250e5d6a7c6SLi Jun 	unsigned int	ports = HCS_N_PORTS(ehci->hcs_params);
2512c4593ecSPeter Chen 	u32 __iomem	*status_reg;
252e5d6a7c6SLi Jun 	u32		temp, port_index;
2532c4593ecSPeter Chen 	unsigned long	flags;
2542c4593ecSPeter Chen 	int		retval = 0;
255fc53d527SPeter Geis 	bool		done = false;
2562c4593ecSPeter Chen 	struct device *dev = hcd->self.controller;
2572c4593ecSPeter Chen 	struct ci_hdrc *ci = dev_get_drvdata(dev);
2582c4593ecSPeter Chen 
259e5d6a7c6SLi Jun 	port_index = wIndex & 0xff;
260e5d6a7c6SLi Jun 	port_index -= (port_index > 0);
261e5d6a7c6SLi Jun 	status_reg = &ehci->regs->port_status[port_index];
2622c4593ecSPeter Chen 
2632c4593ecSPeter Chen 	spin_lock_irqsave(&ehci->lock, flags);
2642c4593ecSPeter Chen 
265fc53d527SPeter Geis 	if (ci->platdata->hub_control) {
266fc53d527SPeter Geis 		retval = ci->platdata->hub_control(ci, typeReq, wValue, wIndex,
267fc53d527SPeter Geis 						   buf, wLength, &done, &flags);
268fc53d527SPeter Geis 		if (done)
269fc53d527SPeter Geis 			goto done;
270fc53d527SPeter Geis 	}
271fc53d527SPeter Geis 
2722c4593ecSPeter Chen 	if (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_SUSPEND) {
273e5d6a7c6SLi Jun 		if (!wIndex || wIndex > ports) {
274e5d6a7c6SLi Jun 			retval = -EPIPE;
275e5d6a7c6SLi Jun 			goto done;
276e5d6a7c6SLi Jun 		}
277e5d6a7c6SLi Jun 
2782c4593ecSPeter Chen 		temp = ehci_readl(ehci, status_reg);
2792c4593ecSPeter Chen 		if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) {
2802c4593ecSPeter Chen 			retval = -EPIPE;
2812c4593ecSPeter Chen 			goto done;
2822c4593ecSPeter Chen 		}
2832c4593ecSPeter Chen 
2842c4593ecSPeter Chen 		temp &= ~(PORT_RWC_BITS | PORT_WKCONN_E);
2852c4593ecSPeter Chen 		temp |= PORT_WKDISC_E | PORT_WKOC_E;
2862c4593ecSPeter Chen 		ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
2872c4593ecSPeter Chen 
2882c4593ecSPeter Chen 		/*
2892c4593ecSPeter Chen 		 * If a transaction is in progress, there may be a delay in
2902c4593ecSPeter Chen 		 * suspending the port. Poll until the port is suspended.
2912c4593ecSPeter Chen 		 */
2922c4593ecSPeter Chen 		if (ehci_handshake(ehci, status_reg, PORT_SUSPEND,
2932c4593ecSPeter Chen 			PORT_SUSPEND, 5000))
2942c4593ecSPeter Chen 			ehci_err(ehci, "timeout waiting for SUSPEND\n");
2952c4593ecSPeter Chen 
2962c4593ecSPeter Chen 		if (ci->platdata->flags & CI_HDRC_IMX_IS_HSIC) {
2972c4593ecSPeter Chen 			if (ci->platdata->notify_event)
2982c4593ecSPeter Chen 				ci->platdata->notify_event(ci,
2992c4593ecSPeter Chen 					CI_HDRC_IMX_HSIC_SUSPEND_EVENT);
3002c4593ecSPeter Chen 
3012c4593ecSPeter Chen 			temp = ehci_readl(ehci, status_reg);
3022c4593ecSPeter Chen 			temp &= ~(PORT_WKDISC_E | PORT_WKCONN_E);
3032c4593ecSPeter Chen 			ehci_writel(ehci, temp, status_reg);
3042c4593ecSPeter Chen 		}
3052c4593ecSPeter Chen 
306e5d6a7c6SLi Jun 		set_bit(port_index, &ehci->suspended_ports);
3072c4593ecSPeter Chen 		goto done;
3082c4593ecSPeter Chen 	}
3092c4593ecSPeter Chen 
3102c4593ecSPeter Chen 	/*
3112c4593ecSPeter Chen 	 * After resume has finished, it needs do some post resume
3122c4593ecSPeter Chen 	 * operation for some SoCs.
3132c4593ecSPeter Chen 	 */
3142c4593ecSPeter Chen 	else if (typeReq == ClearPortFeature &&
3152c4593ecSPeter Chen 		wValue == USB_PORT_FEAT_C_SUSPEND) {
3162c4593ecSPeter Chen 		/* Make sure the resume has finished, it should be finished */
3172c4593ecSPeter Chen 		if (ehci_handshake(ehci, status_reg, PORT_RESUME, 0, 25000))
3182c4593ecSPeter Chen 			ehci_err(ehci, "timeout waiting for resume\n");
3192c4593ecSPeter Chen 	}
3202c4593ecSPeter Chen 
3212c4593ecSPeter Chen 	spin_unlock_irqrestore(&ehci->lock, flags);
3222c4593ecSPeter Chen 
3232c4593ecSPeter Chen 	/* Handle the hub control events here */
3242c4593ecSPeter Chen 	return ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
3252c4593ecSPeter Chen done:
3262c4593ecSPeter Chen 	spin_unlock_irqrestore(&ehci->lock, flags);
3272c4593ecSPeter Chen 	return retval;
3282c4593ecSPeter Chen }
ci_ehci_bus_suspend(struct usb_hcd * hcd)32978f0357eSPeter Chen static int ci_ehci_bus_suspend(struct usb_hcd *hcd)
33078f0357eSPeter Chen {
33178f0357eSPeter Chen 	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
332014abe34SPeter Chen 	struct device *dev = hcd->self.controller;
333014abe34SPeter Chen 	struct ci_hdrc *ci = dev_get_drvdata(dev);
33478f0357eSPeter Chen 	int port;
33578f0357eSPeter Chen 	u32 tmp;
33678f0357eSPeter Chen 
33778f0357eSPeter Chen 	int ret = orig_bus_suspend(hcd);
33878f0357eSPeter Chen 
33978f0357eSPeter Chen 	if (ret)
34078f0357eSPeter Chen 		return ret;
34178f0357eSPeter Chen 
34278f0357eSPeter Chen 	port = HCS_N_PORTS(ehci->hcs_params);
34378f0357eSPeter Chen 	while (port--) {
34478f0357eSPeter Chen 		u32 __iomem *reg = &ehci->regs->port_status[port];
34578f0357eSPeter Chen 		u32 portsc = ehci_readl(ehci, reg);
34678f0357eSPeter Chen 
34778f0357eSPeter Chen 		if (portsc & PORT_CONNECT) {
34878f0357eSPeter Chen 			/*
34978f0357eSPeter Chen 			 * For chipidea, the resume signal will be ended
35078f0357eSPeter Chen 			 * automatically, so for remote wakeup case, the
35178f0357eSPeter Chen 			 * usbcmd.rs may not be set before the resume has
35278f0357eSPeter Chen 			 * ended if other resume paths consumes too much
35378f0357eSPeter Chen 			 * time (~24ms), in that case, the SOF will not
35478f0357eSPeter Chen 			 * send out within 3ms after resume ends, then the
35578f0357eSPeter Chen 			 * high speed device will enter full speed mode.
35678f0357eSPeter Chen 			 */
35778f0357eSPeter Chen 
35878f0357eSPeter Chen 			tmp = ehci_readl(ehci, &ehci->regs->command);
35978f0357eSPeter Chen 			tmp |= CMD_RUN;
36078f0357eSPeter Chen 			ehci_writel(ehci, tmp, &ehci->regs->command);
36178f0357eSPeter Chen 			/*
36278f0357eSPeter Chen 			 * It needs a short delay between set RS bit and PHCD.
36378f0357eSPeter Chen 			 */
36478f0357eSPeter Chen 			usleep_range(150, 200);
365014abe34SPeter Chen 			/*
366014abe34SPeter Chen 			 * Need to clear WKCN and WKOC for imx HSIC,
367014abe34SPeter Chen 			 * otherwise, there will be wakeup event.
368014abe34SPeter Chen 			 */
369014abe34SPeter Chen 			if (ci->platdata->flags & CI_HDRC_IMX_IS_HSIC) {
370014abe34SPeter Chen 				tmp = ehci_readl(ehci, reg);
371014abe34SPeter Chen 				tmp &= ~(PORT_WKDISC_E | PORT_WKCONN_E);
372014abe34SPeter Chen 				ehci_writel(ehci, tmp, reg);
373014abe34SPeter Chen 			}
374014abe34SPeter Chen 
37578f0357eSPeter Chen 			break;
37678f0357eSPeter Chen 		}
37778f0357eSPeter Chen 	}
37878f0357eSPeter Chen 
37978f0357eSPeter Chen 	return 0;
38078f0357eSPeter Chen }
38178f0357eSPeter Chen 
ci_hdrc_free_dma_aligned_buffer(struct urb * urb,bool copy_back)382*568c30c4SMichał Mirosław static void ci_hdrc_free_dma_aligned_buffer(struct urb *urb, bool copy_back)
383fc53d527SPeter Geis {
384fc53d527SPeter Geis 	struct ci_hdrc_dma_aligned_buffer *temp;
385fc53d527SPeter Geis 
386fc53d527SPeter Geis 	if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER))
387fc53d527SPeter Geis 		return;
388*568c30c4SMichał Mirosław 	urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER;
389fc53d527SPeter Geis 
390fc53d527SPeter Geis 	temp = container_of(urb->transfer_buffer,
391fc53d527SPeter Geis 			    struct ci_hdrc_dma_aligned_buffer, data);
392*568c30c4SMichał Mirosław 	urb->transfer_buffer = temp->original_buffer;
393fc53d527SPeter Geis 
394*568c30c4SMichał Mirosław 	if (copy_back && usb_urb_dir_in(urb)) {
395*568c30c4SMichał Mirosław 		size_t length;
396*568c30c4SMichał Mirosław 
397fc53d527SPeter Geis 		if (usb_pipeisoc(urb->pipe))
398fc53d527SPeter Geis 			length = urb->transfer_buffer_length;
399fc53d527SPeter Geis 		else
400fc53d527SPeter Geis 			length = urb->actual_length;
401fc53d527SPeter Geis 
402*568c30c4SMichał Mirosław 		memcpy(temp->original_buffer, temp->data, length);
403fc53d527SPeter Geis 	}
404fc53d527SPeter Geis 
405*568c30c4SMichał Mirosław 	kfree(temp);
406fc53d527SPeter Geis }
407fc53d527SPeter Geis 
ci_hdrc_alloc_dma_aligned_buffer(struct urb * urb,gfp_t mem_flags)408fc53d527SPeter Geis static int ci_hdrc_alloc_dma_aligned_buffer(struct urb *urb, gfp_t mem_flags)
409fc53d527SPeter Geis {
410*568c30c4SMichał Mirosław 	struct ci_hdrc_dma_aligned_buffer *temp;
411fc53d527SPeter Geis 
41238a41a0cSMichał Mirosław 	if (urb->num_sgs || urb->sg || urb->transfer_buffer_length == 0)
41338a41a0cSMichał Mirosław 		return 0;
414*568c30c4SMichał Mirosław 	if (IS_ALIGNED((uintptr_t)urb->transfer_buffer, 4)
415*568c30c4SMichał Mirosław 	    && IS_ALIGNED(urb->transfer_buffer_length, 4))
416fc53d527SPeter Geis 		return 0;
417fc53d527SPeter Geis 
418*568c30c4SMichał Mirosław 	temp = kmalloc(sizeof(*temp) + ALIGN(urb->transfer_buffer_length, 4), mem_flags);
419*568c30c4SMichał Mirosław 	if (!temp)
420fc53d527SPeter Geis 		return -ENOMEM;
421fc53d527SPeter Geis 
422fc53d527SPeter Geis 	if (usb_urb_dir_out(urb))
423fc53d527SPeter Geis 		memcpy(temp->data, urb->transfer_buffer,
424fc53d527SPeter Geis 		       urb->transfer_buffer_length);
425fc53d527SPeter Geis 
426*568c30c4SMichał Mirosław 	temp->original_buffer = urb->transfer_buffer;
427*568c30c4SMichał Mirosław 	urb->transfer_buffer = temp->data;
428fc53d527SPeter Geis 	urb->transfer_flags |= URB_ALIGNED_TEMP_BUFFER;
429fc53d527SPeter Geis 
430fc53d527SPeter Geis 	return 0;
431fc53d527SPeter Geis }
432fc53d527SPeter Geis 
ci_hdrc_map_urb_for_dma(struct usb_hcd * hcd,struct urb * urb,gfp_t mem_flags)433fc53d527SPeter Geis static int ci_hdrc_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
434fc53d527SPeter Geis 				   gfp_t mem_flags)
435fc53d527SPeter Geis {
436fc53d527SPeter Geis 	int ret;
437fc53d527SPeter Geis 
438fc53d527SPeter Geis 	ret = ci_hdrc_alloc_dma_aligned_buffer(urb, mem_flags);
439fc53d527SPeter Geis 	if (ret)
440fc53d527SPeter Geis 		return ret;
441fc53d527SPeter Geis 
442fc53d527SPeter Geis 	ret = usb_hcd_map_urb_for_dma(hcd, urb, mem_flags);
443fc53d527SPeter Geis 	if (ret)
444*568c30c4SMichał Mirosław 		ci_hdrc_free_dma_aligned_buffer(urb, false);
445fc53d527SPeter Geis 
446fc53d527SPeter Geis 	return ret;
447fc53d527SPeter Geis }
448fc53d527SPeter Geis 
ci_hdrc_unmap_urb_for_dma(struct usb_hcd * hcd,struct urb * urb)449fc53d527SPeter Geis static void ci_hdrc_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
450fc53d527SPeter Geis {
451fc53d527SPeter Geis 	usb_hcd_unmap_urb_for_dma(hcd, urb);
452*568c30c4SMichał Mirosław 	ci_hdrc_free_dma_aligned_buffer(urb, true);
453fc53d527SPeter Geis }
454fc53d527SPeter Geis 
4552f64d6a6SXu Yang #ifdef CONFIG_PM_SLEEP
ci_hdrc_host_suspend(struct ci_hdrc * ci)4562f64d6a6SXu Yang static void ci_hdrc_host_suspend(struct ci_hdrc *ci)
4572f64d6a6SXu Yang {
4582f64d6a6SXu Yang 	ehci_suspend(ci->hcd, device_may_wakeup(ci->dev));
4592f64d6a6SXu Yang }
4602f64d6a6SXu Yang 
ci_hdrc_host_resume(struct ci_hdrc * ci,bool power_lost)4612f64d6a6SXu Yang static void ci_hdrc_host_resume(struct ci_hdrc *ci, bool power_lost)
4622f64d6a6SXu Yang {
4632f64d6a6SXu Yang 	ehci_resume(ci->hcd, power_lost);
4642f64d6a6SXu Yang }
4652f64d6a6SXu Yang #endif
4662f64d6a6SXu Yang 
ci_hdrc_host_init(struct ci_hdrc * ci)4678e22978cSAlexander Shishkin int ci_hdrc_host_init(struct ci_hdrc *ci)
468eb70e5abSAlexander Shishkin {
469eb70e5abSAlexander Shishkin 	struct ci_role_driver *rdrv;
470eb70e5abSAlexander Shishkin 
471eb70e5abSAlexander Shishkin 	if (!hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_HC))
472eb70e5abSAlexander Shishkin 		return -ENXIO;
473eb70e5abSAlexander Shishkin 
474eb70e5abSAlexander Shishkin 	rdrv = devm_kzalloc(ci->dev, sizeof(struct ci_role_driver), GFP_KERNEL);
475eb70e5abSAlexander Shishkin 	if (!rdrv)
476eb70e5abSAlexander Shishkin 		return -ENOMEM;
477eb70e5abSAlexander Shishkin 
478eb70e5abSAlexander Shishkin 	rdrv->start	= host_start;
479eb70e5abSAlexander Shishkin 	rdrv->stop	= host_stop;
4802f64d6a6SXu Yang #ifdef CONFIG_PM_SLEEP
4812f64d6a6SXu Yang 	rdrv->suspend	= ci_hdrc_host_suspend;
4822f64d6a6SXu Yang 	rdrv->resume	= ci_hdrc_host_resume;
4832f64d6a6SXu Yang #endif
484eb70e5abSAlexander Shishkin 	rdrv->irq	= host_irq;
485eb70e5abSAlexander Shishkin 	rdrv->name	= "host";
486eb70e5abSAlexander Shishkin 	ci->roles[CI_ROLE_HOST] = rdrv;
487eb70e5abSAlexander Shishkin 
488fc53d527SPeter Geis 	if (ci->platdata->flags & CI_HDRC_REQUIRES_ALIGNED_DMA) {
489fc53d527SPeter Geis 		ci_ehci_hc_driver.map_urb_for_dma = ci_hdrc_map_urb_for_dma;
490fc53d527SPeter Geis 		ci_ehci_hc_driver.unmap_urb_for_dma = ci_hdrc_unmap_urb_for_dma;
491fc53d527SPeter Geis 	}
492fc53d527SPeter Geis 
4932f01a33bSPeter Chen 	return 0;
4942f01a33bSPeter Chen }
4952f01a33bSPeter Chen 
ci_hdrc_host_driver_init(void)4962f01a33bSPeter Chen void ci_hdrc_host_driver_init(void)
4972f01a33bSPeter Chen {
498c8679a2fSMichael Grzeschik 	ehci_init_driver(&ci_ehci_hc_driver, &ehci_ci_overrides);
49978f0357eSPeter Chen 	orig_bus_suspend = ci_ehci_hc_driver.bus_suspend;
50078f0357eSPeter Chen 	ci_ehci_hc_driver.bus_suspend = ci_ehci_bus_suspend;
5012c4593ecSPeter Chen 	ci_ehci_hc_driver.hub_control = ci_ehci_hub_control;
502eb70e5abSAlexander Shishkin }
503