xref: /openbmc/linux/drivers/usb/chipidea/host.c (revision 78f0357e)
1eb70e5abSAlexander Shishkin /*
2eb70e5abSAlexander Shishkin  * host.c - ChipIdea USB host controller driver
3eb70e5abSAlexander Shishkin  *
4eb70e5abSAlexander Shishkin  * Copyright (c) 2012 Intel Corporation
5eb70e5abSAlexander Shishkin  *
6eb70e5abSAlexander Shishkin  * Author: Alexander Shishkin
7eb70e5abSAlexander Shishkin  *
8eb70e5abSAlexander Shishkin  * This program is free software; you can redistribute it and/or modify
9eb70e5abSAlexander Shishkin  * it under the terms of the GNU General Public License version 2 as
10eb70e5abSAlexander Shishkin  * published by the Free Software Foundation.
11eb70e5abSAlexander Shishkin  *
12eb70e5abSAlexander Shishkin  * This program is distributed in the hope that it will be useful,
13eb70e5abSAlexander Shishkin  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14eb70e5abSAlexander Shishkin  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15eb70e5abSAlexander Shishkin  * GNU General Public License for more details.
16eb70e5abSAlexander Shishkin  *
17eb70e5abSAlexander Shishkin  * You should have received a copy of the GNU General Public License
18eb70e5abSAlexander Shishkin  * along with this program; if not, write to the Free Software
19eb70e5abSAlexander Shishkin  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20eb70e5abSAlexander Shishkin  */
21eb70e5abSAlexander Shishkin 
22eb70e5abSAlexander Shishkin #include <linux/kernel.h>
23cdb2fac7SAlan Stern #include <linux/io.h>
24eb70e5abSAlexander Shishkin #include <linux/usb.h>
25eb70e5abSAlexander Shishkin #include <linux/usb/hcd.h>
26eb70e5abSAlexander Shishkin #include <linux/usb/chipidea.h>
2740ed51a4SPeter Chen #include <linux/regulator/consumer.h>
28eb70e5abSAlexander Shishkin 
2909f6ffdeSAlan Stern #include "../host/ehci.h"
30eb70e5abSAlexander Shishkin 
31eb70e5abSAlexander Shishkin #include "ci.h"
32eb70e5abSAlexander Shishkin #include "bits.h"
33eb70e5abSAlexander Shishkin #include "host.h"
34eb70e5abSAlexander Shishkin 
3509f6ffdeSAlan Stern static struct hc_driver __read_mostly ci_ehci_hc_driver;
3678f0357eSPeter Chen static int (*orig_bus_suspend)(struct usb_hcd *hcd);
3709f6ffdeSAlan Stern 
38c8679a2fSMichael Grzeschik struct ehci_ci_priv {
39c8679a2fSMichael Grzeschik 	struct regulator *reg_vbus;
40c8679a2fSMichael Grzeschik };
41c8679a2fSMichael Grzeschik 
42c8679a2fSMichael Grzeschik static int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool enable)
43c8679a2fSMichael Grzeschik {
44c8679a2fSMichael Grzeschik 	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
45c8679a2fSMichael Grzeschik 	struct ehci_ci_priv *priv = (struct ehci_ci_priv *)ehci->priv;
46c8679a2fSMichael Grzeschik 	struct device *dev = hcd->self.controller;
47c8679a2fSMichael Grzeschik 	struct ci_hdrc *ci = dev_get_drvdata(dev);
48c8679a2fSMichael Grzeschik 	int ret = 0;
49c8679a2fSMichael Grzeschik 	int port = HCS_N_PORTS(ehci->hcs_params);
50c8679a2fSMichael Grzeschik 
51c8679a2fSMichael Grzeschik 	if (priv->reg_vbus && !ci_otg_is_fsm_mode(ci)) {
52c8679a2fSMichael Grzeschik 		if (port > 1) {
53c8679a2fSMichael Grzeschik 			dev_warn(dev,
54c8679a2fSMichael Grzeschik 				"Not support multi-port regulator control\n");
55c8679a2fSMichael Grzeschik 			return 0;
56c8679a2fSMichael Grzeschik 		}
57c8679a2fSMichael Grzeschik 		if (enable)
58c8679a2fSMichael Grzeschik 			ret = regulator_enable(priv->reg_vbus);
59c8679a2fSMichael Grzeschik 		else
60c8679a2fSMichael Grzeschik 			ret = regulator_disable(priv->reg_vbus);
61c8679a2fSMichael Grzeschik 		if (ret) {
62c8679a2fSMichael Grzeschik 			dev_err(dev,
63c8679a2fSMichael Grzeschik 				"Failed to %s vbus regulator, ret=%d\n",
64c8679a2fSMichael Grzeschik 				enable ? "enable" : "disable", ret);
65c8679a2fSMichael Grzeschik 			return ret;
66c8679a2fSMichael Grzeschik 		}
67c8679a2fSMichael Grzeschik 	}
68c8679a2fSMichael Grzeschik 	return 0;
69c8679a2fSMichael Grzeschik };
70c8679a2fSMichael Grzeschik 
71c8679a2fSMichael Grzeschik static const struct ehci_driver_overrides ehci_ci_overrides = {
72c8679a2fSMichael Grzeschik 	.extra_priv_size = sizeof(struct ehci_ci_priv),
73c8679a2fSMichael Grzeschik 	.port_power	 = ehci_ci_portpower,
74c8679a2fSMichael Grzeschik };
75c8679a2fSMichael Grzeschik 
768e22978cSAlexander Shishkin static irqreturn_t host_irq(struct ci_hdrc *ci)
77eb70e5abSAlexander Shishkin {
78eb70e5abSAlexander Shishkin 	return usb_hcd_irq(ci->irq, ci->hcd);
79eb70e5abSAlexander Shishkin }
80eb70e5abSAlexander Shishkin 
818e22978cSAlexander Shishkin static int host_start(struct ci_hdrc *ci)
82eb70e5abSAlexander Shishkin {
83eb70e5abSAlexander Shishkin 	struct usb_hcd *hcd;
84eb70e5abSAlexander Shishkin 	struct ehci_hcd *ehci;
85c8679a2fSMichael Grzeschik 	struct ehci_ci_priv *priv;
86eb70e5abSAlexander Shishkin 	int ret;
87eb70e5abSAlexander Shishkin 
88eb70e5abSAlexander Shishkin 	if (usb_disabled())
89eb70e5abSAlexander Shishkin 		return -ENODEV;
90eb70e5abSAlexander Shishkin 
91eb70e5abSAlexander Shishkin 	hcd = usb_create_hcd(&ci_ehci_hc_driver, ci->dev, dev_name(ci->dev));
92eb70e5abSAlexander Shishkin 	if (!hcd)
93eb70e5abSAlexander Shishkin 		return -ENOMEM;
94eb70e5abSAlexander Shishkin 
9524c498dfSPeter Chen 	dev_set_drvdata(ci->dev, ci);
96eb70e5abSAlexander Shishkin 	hcd->rsrc_start = ci->hw_bank.phys;
97eb70e5abSAlexander Shishkin 	hcd->rsrc_len = ci->hw_bank.size;
98eb70e5abSAlexander Shishkin 	hcd->regs = ci->hw_bank.abs;
99eb70e5abSAlexander Shishkin 	hcd->has_tt = 1;
100eb70e5abSAlexander Shishkin 
10177c4400fSRichard Zhao 	hcd->power_budget = ci->platdata->power_budget;
102f6a9ff07SPeter Chen 	hcd->tpl_support = ci->platdata->tpl_support;
1031e5e2d3dSAntoine Tenart 	if (ci->phy)
1041e5e2d3dSAntoine Tenart 		hcd->phy = ci->phy;
1051e5e2d3dSAntoine Tenart 	else
1061e5e2d3dSAntoine Tenart 		hcd->usb_phy = ci->usb_phy;
107bd841986SAlexander Shishkin 
108eb70e5abSAlexander Shishkin 	ehci = hcd_to_ehci(hcd);
109eb70e5abSAlexander Shishkin 	ehci->caps = ci->hw_bank.cap;
110eb70e5abSAlexander Shishkin 	ehci->has_hostpc = ci->hw_bank.lpm;
1112cdcec4fSTuomas Tynkkynen 	ehci->has_tdi_phy_lpm = ci->hw_bank.lpm;
112ed8f8318SPeter Chen 	ehci->imx28_write_fix = ci->imx28_write_fix;
113eb70e5abSAlexander Shishkin 
114c8679a2fSMichael Grzeschik 	priv = (struct ehci_ci_priv *)ehci->priv;
115c8679a2fSMichael Grzeschik 	priv->reg_vbus = NULL;
116c8679a2fSMichael Grzeschik 
117c8679a2fSMichael Grzeschik 	if (ci->platdata->reg_vbus)
118c8679a2fSMichael Grzeschik 		priv->reg_vbus = ci->platdata->reg_vbus;
11940ed51a4SPeter Chen 
120eb70e5abSAlexander Shishkin 	ret = usb_add_hcd(hcd, 0, 0);
1210698b9b3SLi Jun 	if (ret) {
122c8679a2fSMichael Grzeschik 		goto put_hcd;
1230698b9b3SLi Jun 	} else {
124ef44cb42SAntoine Tenart 		struct usb_otg *otg = &ci->otg;
1250698b9b3SLi Jun 
126eb70e5abSAlexander Shishkin 		ci->hcd = hcd;
127ef44cb42SAntoine Tenart 
128ef44cb42SAntoine Tenart 		if (ci_otg_is_fsm_mode(ci)) {
1290698b9b3SLi Jun 			otg->host = &hcd->self;
1300698b9b3SLi Jun 			hcd->self.otg_port = 1;
1310698b9b3SLi Jun 		}
1320698b9b3SLi Jun 	}
133eb70e5abSAlexander Shishkin 
1348e22978cSAlexander Shishkin 	if (ci->platdata->flags & CI_HDRC_DISABLE_STREAMING)
135929473eaSFabio Estevam 		hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS);
136929473eaSFabio Estevam 
137905276c4SDaniel Tang 	if (ci->platdata->flags & CI_HDRC_FORCE_FULLSPEED)
138905276c4SDaniel Tang 		hw_write(ci, OP_PORTSC, PORTSC_PFSC, PORTSC_PFSC);
139905276c4SDaniel Tang 
140eb70e5abSAlexander Shishkin 	return ret;
14140ed51a4SPeter Chen 
14240ed51a4SPeter Chen put_hcd:
14340ed51a4SPeter Chen 	usb_put_hcd(hcd);
14440ed51a4SPeter Chen 
14540ed51a4SPeter Chen 	return ret;
146eb70e5abSAlexander Shishkin }
147eb70e5abSAlexander Shishkin 
1488e22978cSAlexander Shishkin static void host_stop(struct ci_hdrc *ci)
149eb70e5abSAlexander Shishkin {
150eb70e5abSAlexander Shishkin 	struct usb_hcd *hcd = ci->hcd;
151eb70e5abSAlexander Shishkin 
15241314feaSRussell King - ARM Linux 	if (hcd) {
153eb70e5abSAlexander Shishkin 		usb_remove_hcd(hcd);
154eb70e5abSAlexander Shishkin 		usb_put_hcd(hcd);
155eb70e5abSAlexander Shishkin 	}
156df101c53SPeter Chen }
157eb70e5abSAlexander Shishkin 
1583f124d23SPeter Chen 
1593f124d23SPeter Chen void ci_hdrc_host_destroy(struct ci_hdrc *ci)
1603f124d23SPeter Chen {
161df101c53SPeter Chen 	if (ci->role == CI_ROLE_HOST && ci->hcd)
1623f124d23SPeter Chen 		host_stop(ci);
1633f124d23SPeter Chen }
1643f124d23SPeter Chen 
16578f0357eSPeter Chen static int ci_ehci_bus_suspend(struct usb_hcd *hcd)
16678f0357eSPeter Chen {
16778f0357eSPeter Chen 	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
16878f0357eSPeter Chen 	int port;
16978f0357eSPeter Chen 	u32 tmp;
17078f0357eSPeter Chen 
17178f0357eSPeter Chen 	int ret = orig_bus_suspend(hcd);
17278f0357eSPeter Chen 
17378f0357eSPeter Chen 	if (ret)
17478f0357eSPeter Chen 		return ret;
17578f0357eSPeter Chen 
17678f0357eSPeter Chen 	port = HCS_N_PORTS(ehci->hcs_params);
17778f0357eSPeter Chen 	while (port--) {
17878f0357eSPeter Chen 		u32 __iomem *reg = &ehci->regs->port_status[port];
17978f0357eSPeter Chen 		u32 portsc = ehci_readl(ehci, reg);
18078f0357eSPeter Chen 
18178f0357eSPeter Chen 		if (portsc & PORT_CONNECT) {
18278f0357eSPeter Chen 			/*
18378f0357eSPeter Chen 			 * For chipidea, the resume signal will be ended
18478f0357eSPeter Chen 			 * automatically, so for remote wakeup case, the
18578f0357eSPeter Chen 			 * usbcmd.rs may not be set before the resume has
18678f0357eSPeter Chen 			 * ended if other resume paths consumes too much
18778f0357eSPeter Chen 			 * time (~24ms), in that case, the SOF will not
18878f0357eSPeter Chen 			 * send out within 3ms after resume ends, then the
18978f0357eSPeter Chen 			 * high speed device will enter full speed mode.
19078f0357eSPeter Chen 			 */
19178f0357eSPeter Chen 
19278f0357eSPeter Chen 			tmp = ehci_readl(ehci, &ehci->regs->command);
19378f0357eSPeter Chen 			tmp |= CMD_RUN;
19478f0357eSPeter Chen 			ehci_writel(ehci, tmp, &ehci->regs->command);
19578f0357eSPeter Chen 			/*
19678f0357eSPeter Chen 			 * It needs a short delay between set RS bit and PHCD.
19778f0357eSPeter Chen 			 */
19878f0357eSPeter Chen 			usleep_range(150, 200);
19978f0357eSPeter Chen 			break;
20078f0357eSPeter Chen 		}
20178f0357eSPeter Chen 	}
20278f0357eSPeter Chen 
20378f0357eSPeter Chen 	return 0;
20478f0357eSPeter Chen }
20578f0357eSPeter Chen 
2068e22978cSAlexander Shishkin int ci_hdrc_host_init(struct ci_hdrc *ci)
207eb70e5abSAlexander Shishkin {
208eb70e5abSAlexander Shishkin 	struct ci_role_driver *rdrv;
209eb70e5abSAlexander Shishkin 
210eb70e5abSAlexander Shishkin 	if (!hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_HC))
211eb70e5abSAlexander Shishkin 		return -ENXIO;
212eb70e5abSAlexander Shishkin 
213eb70e5abSAlexander Shishkin 	rdrv = devm_kzalloc(ci->dev, sizeof(struct ci_role_driver), GFP_KERNEL);
214eb70e5abSAlexander Shishkin 	if (!rdrv)
215eb70e5abSAlexander Shishkin 		return -ENOMEM;
216eb70e5abSAlexander Shishkin 
217eb70e5abSAlexander Shishkin 	rdrv->start	= host_start;
218eb70e5abSAlexander Shishkin 	rdrv->stop	= host_stop;
219eb70e5abSAlexander Shishkin 	rdrv->irq	= host_irq;
220eb70e5abSAlexander Shishkin 	rdrv->name	= "host";
221eb70e5abSAlexander Shishkin 	ci->roles[CI_ROLE_HOST] = rdrv;
222eb70e5abSAlexander Shishkin 
223c8679a2fSMichael Grzeschik 	ehci_init_driver(&ci_ehci_hc_driver, &ehci_ci_overrides);
22478f0357eSPeter Chen 	orig_bus_suspend = ci_ehci_hc_driver.bus_suspend;
22578f0357eSPeter Chen 	ci_ehci_hc_driver.bus_suspend = ci_ehci_bus_suspend;
22609f6ffdeSAlan Stern 
227eb70e5abSAlexander Shishkin 	return 0;
228eb70e5abSAlexander Shishkin }
229