xref: /openbmc/linux/drivers/usb/chipidea/host.c (revision ed8f8318)
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;
3609f6ffdeSAlan Stern 
378e22978cSAlexander Shishkin static irqreturn_t host_irq(struct ci_hdrc *ci)
38eb70e5abSAlexander Shishkin {
39eb70e5abSAlexander Shishkin 	return usb_hcd_irq(ci->irq, ci->hcd);
40eb70e5abSAlexander Shishkin }
41eb70e5abSAlexander Shishkin 
428e22978cSAlexander Shishkin static int host_start(struct ci_hdrc *ci)
43eb70e5abSAlexander Shishkin {
44eb70e5abSAlexander Shishkin 	struct usb_hcd *hcd;
45eb70e5abSAlexander Shishkin 	struct ehci_hcd *ehci;
46eb70e5abSAlexander Shishkin 	int ret;
47eb70e5abSAlexander Shishkin 
48eb70e5abSAlexander Shishkin 	if (usb_disabled())
49eb70e5abSAlexander Shishkin 		return -ENODEV;
50eb70e5abSAlexander Shishkin 
51eb70e5abSAlexander Shishkin 	hcd = usb_create_hcd(&ci_ehci_hc_driver, ci->dev, dev_name(ci->dev));
52eb70e5abSAlexander Shishkin 	if (!hcd)
53eb70e5abSAlexander Shishkin 		return -ENOMEM;
54eb70e5abSAlexander Shishkin 
55eb70e5abSAlexander Shishkin 	dev_set_drvdata(ci->dev, ci);
56eb70e5abSAlexander Shishkin 	hcd->rsrc_start = ci->hw_bank.phys;
57eb70e5abSAlexander Shishkin 	hcd->rsrc_len = ci->hw_bank.size;
58eb70e5abSAlexander Shishkin 	hcd->regs = ci->hw_bank.abs;
59eb70e5abSAlexander Shishkin 	hcd->has_tt = 1;
60eb70e5abSAlexander Shishkin 
6177c4400fSRichard Zhao 	hcd->power_budget = ci->platdata->power_budget;
62a2c3d690SRichard Zhao 	hcd->phy = ci->transceiver;
63bd841986SAlexander Shishkin 
64eb70e5abSAlexander Shishkin 	ehci = hcd_to_ehci(hcd);
65eb70e5abSAlexander Shishkin 	ehci->caps = ci->hw_bank.cap;
66eb70e5abSAlexander Shishkin 	ehci->has_hostpc = ci->hw_bank.lpm;
672cdcec4fSTuomas Tynkkynen 	ehci->has_tdi_phy_lpm = ci->hw_bank.lpm;
68ed8f8318SPeter Chen 	ehci->imx28_write_fix = ci->imx28_write_fix;
69eb70e5abSAlexander Shishkin 
7040ed51a4SPeter Chen 	if (ci->platdata->reg_vbus) {
7140ed51a4SPeter Chen 		ret = regulator_enable(ci->platdata->reg_vbus);
7240ed51a4SPeter Chen 		if (ret) {
7340ed51a4SPeter Chen 			dev_err(ci->dev,
7440ed51a4SPeter Chen 				"Failed to enable vbus regulator, ret=%d\n",
7540ed51a4SPeter Chen 				ret);
7640ed51a4SPeter Chen 			goto put_hcd;
7740ed51a4SPeter Chen 		}
7840ed51a4SPeter Chen 	}
7940ed51a4SPeter Chen 
80eb70e5abSAlexander Shishkin 	ret = usb_add_hcd(hcd, 0, 0);
81eb70e5abSAlexander Shishkin 	if (ret)
8240ed51a4SPeter Chen 		goto disable_reg;
83eb70e5abSAlexander Shishkin 	else
84eb70e5abSAlexander Shishkin 		ci->hcd = hcd;
85eb70e5abSAlexander Shishkin 
868e22978cSAlexander Shishkin 	if (ci->platdata->flags & CI_HDRC_DISABLE_STREAMING)
87929473eaSFabio Estevam 		hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS);
88929473eaSFabio Estevam 
89eb70e5abSAlexander Shishkin 	return ret;
9040ed51a4SPeter Chen 
9140ed51a4SPeter Chen disable_reg:
9246adcf3dSFabio Estevam 	if (ci->platdata->reg_vbus)
9340ed51a4SPeter Chen 		regulator_disable(ci->platdata->reg_vbus);
9440ed51a4SPeter Chen 
9540ed51a4SPeter Chen put_hcd:
9640ed51a4SPeter Chen 	usb_put_hcd(hcd);
9740ed51a4SPeter Chen 
9840ed51a4SPeter Chen 	return ret;
99eb70e5abSAlexander Shishkin }
100eb70e5abSAlexander Shishkin 
1018e22978cSAlexander Shishkin static void host_stop(struct ci_hdrc *ci)
102eb70e5abSAlexander Shishkin {
103eb70e5abSAlexander Shishkin 	struct usb_hcd *hcd = ci->hcd;
104eb70e5abSAlexander Shishkin 
10541314feaSRussell King - ARM Linux 	if (hcd) {
106eb70e5abSAlexander Shishkin 		usb_remove_hcd(hcd);
107eb70e5abSAlexander Shishkin 		usb_put_hcd(hcd);
10840ed51a4SPeter Chen 		if (ci->platdata->reg_vbus)
10940ed51a4SPeter Chen 			regulator_disable(ci->platdata->reg_vbus);
110eb70e5abSAlexander Shishkin 	}
111df101c53SPeter Chen }
112eb70e5abSAlexander Shishkin 
1133f124d23SPeter Chen 
1143f124d23SPeter Chen void ci_hdrc_host_destroy(struct ci_hdrc *ci)
1153f124d23SPeter Chen {
116df101c53SPeter Chen 	if (ci->role == CI_ROLE_HOST && ci->hcd)
1173f124d23SPeter Chen 		host_stop(ci);
1183f124d23SPeter Chen }
1193f124d23SPeter Chen 
1208e22978cSAlexander Shishkin int ci_hdrc_host_init(struct ci_hdrc *ci)
121eb70e5abSAlexander Shishkin {
122eb70e5abSAlexander Shishkin 	struct ci_role_driver *rdrv;
123eb70e5abSAlexander Shishkin 
124eb70e5abSAlexander Shishkin 	if (!hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_HC))
125eb70e5abSAlexander Shishkin 		return -ENXIO;
126eb70e5abSAlexander Shishkin 
127eb70e5abSAlexander Shishkin 	rdrv = devm_kzalloc(ci->dev, sizeof(struct ci_role_driver), GFP_KERNEL);
128eb70e5abSAlexander Shishkin 	if (!rdrv)
129eb70e5abSAlexander Shishkin 		return -ENOMEM;
130eb70e5abSAlexander Shishkin 
131eb70e5abSAlexander Shishkin 	rdrv->start	= host_start;
132eb70e5abSAlexander Shishkin 	rdrv->stop	= host_stop;
133eb70e5abSAlexander Shishkin 	rdrv->irq	= host_irq;
134eb70e5abSAlexander Shishkin 	rdrv->name	= "host";
135eb70e5abSAlexander Shishkin 	ci->roles[CI_ROLE_HOST] = rdrv;
136eb70e5abSAlexander Shishkin 
1371b36810eSAlan Stern 	ehci_init_driver(&ci_ehci_hc_driver, NULL);
13809f6ffdeSAlan Stern 
139eb70e5abSAlexander Shishkin 	return 0;
140eb70e5abSAlexander Shishkin }
141