diff options
author | Pavankumar Kondeti <pkondeti@codeaurora.org> | 2010-12-07 07:23:56 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-12-10 17:23:32 -0500 |
commit | b0848aea10da186372582f33152efdda43944f26 (patch) | |
tree | 681c7e880b3d5cd0d279b3eb72b7123a890dd0e8 /drivers/usb/host | |
parent | e0c201f339fe7fc38d1b0f6f4755ff627686c7e0 (diff) |
USB: EHCI: Add MSM Host Controller driver
This patch adds support for EHCI compliant HSUSB Host controller found
on MSM chips. The root hub has a single port and TT is built into it.
This driver depends on OTG driver for PHY initialization, clock
management and powering up VBUS.
Signed-off-by: Pavankumar Kondeti <pkondeti@codeaurora.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host')
-rw-r--r-- | drivers/usb/host/Kconfig | 11 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 5 | ||||
-rw-r--r-- | drivers/usb/host/ehci-msm.c | 290 |
3 files changed, 306 insertions, 0 deletions
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 6a7c688b4781..d8665ec0565b 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig | |||
@@ -141,6 +141,17 @@ config USB_EHCI_HCD_OMAP | |||
141 | Enables support for the on-chip EHCI controller on | 141 | Enables support for the on-chip EHCI controller on |
142 | OMAP3 and later chips. | 142 | OMAP3 and later chips. |
143 | 143 | ||
144 | config USB_EHCI_MSM | ||
145 | bool "Support for MSM on-chip EHCI USB controller" | ||
146 | depends on USB_EHCI_HCD && ARCH_MSM | ||
147 | select USB_EHCI_ROOT_HUB_TT | ||
148 | select USB_MSM_OTG_72K | ||
149 | ---help--- | ||
150 | Enables support for the USB Host controller present on the | ||
151 | Qualcomm chipsets. Root Hub has inbuilt TT. | ||
152 | This driver depends on OTG driver for PHY initialization, | ||
153 | clock management, powering up VBUS. | ||
154 | |||
144 | config USB_EHCI_HCD_PPC_OF | 155 | config USB_EHCI_HCD_PPC_OF |
145 | bool "EHCI support for PPC USB controller on OF platform bus" | 156 | bool "EHCI support for PPC USB controller on OF platform bus" |
146 | depends on USB_EHCI_HCD && PPC_OF | 157 | depends on USB_EHCI_HCD && PPC_OF |
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 6778fbcf2416..48021d26024c 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c | |||
@@ -1239,6 +1239,11 @@ MODULE_LICENSE ("GPL"); | |||
1239 | #define PLATFORM_DRIVER spear_ehci_hcd_driver | 1239 | #define PLATFORM_DRIVER spear_ehci_hcd_driver |
1240 | #endif | 1240 | #endif |
1241 | 1241 | ||
1242 | #ifdef CONFIG_USB_EHCI_MSM | ||
1243 | #include "ehci-msm.c" | ||
1244 | #define PLATFORM_DRIVER ehci_msm_driver | ||
1245 | #endif | ||
1246 | |||
1242 | #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ | 1247 | #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ |
1243 | !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \ | 1248 | !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \ |
1244 | !defined(XILINX_OF_PLATFORM_DRIVER) | 1249 | !defined(XILINX_OF_PLATFORM_DRIVER) |
diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c new file mode 100644 index 000000000000..9ed855986599 --- /dev/null +++ b/drivers/usb/host/ehci-msm.c | |||
@@ -0,0 +1,290 @@ | |||
1 | /* ehci-msm.c - HSUSB Host Controller Driver Implementation | ||
2 | * | ||
3 | * Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved. | ||
4 | * | ||
5 | * Partly derived from ehci-fsl.c and ehci-hcd.c | ||
6 | * Copyright (c) 2000-2004 by David Brownell | ||
7 | * Copyright (c) 2005 MontaVista Software | ||
8 | * | ||
9 | * All source code in this file is licensed under the following license except | ||
10 | * where indicated. | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify it | ||
13 | * under the terms of the GNU General Public License version 2 as published | ||
14 | * by the Free Software Foundation. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
19 | * | ||
20 | * See the GNU General Public License for more details. | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, you can find it at http://www.fsf.org | ||
23 | */ | ||
24 | |||
25 | #include <linux/platform_device.h> | ||
26 | #include <linux/clk.h> | ||
27 | #include <linux/err.h> | ||
28 | |||
29 | #include <linux/usb/otg.h> | ||
30 | #include <linux/usb/msm_hsusb_hw.h> | ||
31 | |||
32 | #define MSM_USB_BASE (hcd->regs) | ||
33 | |||
34 | static struct otg_transceiver *otg; | ||
35 | |||
36 | /* | ||
37 | * ehci_run defined in drivers/usb/host/ehci-hcd.c reset the controller and | ||
38 | * the configuration settings in ehci_msm_reset vanish after controller is | ||
39 | * reset. Resetting the controler in ehci_run seems to be un-necessary | ||
40 | * provided HCD reset the controller before calling ehci_run. Most of the HCD | ||
41 | * do but some are not. So this function is same as ehci_run but we don't | ||
42 | * reset the controller here. | ||
43 | */ | ||
44 | static int ehci_msm_run(struct usb_hcd *hcd) | ||
45 | { | ||
46 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); | ||
47 | u32 temp; | ||
48 | u32 hcc_params; | ||
49 | |||
50 | hcd->uses_new_polling = 1; | ||
51 | |||
52 | ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list); | ||
53 | ehci_writel(ehci, (u32)ehci->async->qh_dma, &ehci->regs->async_next); | ||
54 | |||
55 | /* | ||
56 | * hcc_params controls whether ehci->regs->segment must (!!!) | ||
57 | * be used; it constrains QH/ITD/SITD and QTD locations. | ||
58 | * pci_pool consistent memory always uses segment zero. | ||
59 | * streaming mappings for I/O buffers, like pci_map_single(), | ||
60 | * can return segments above 4GB, if the device allows. | ||
61 | * | ||
62 | * NOTE: the dma mask is visible through dma_supported(), so | ||
63 | * drivers can pass this info along ... like NETIF_F_HIGHDMA, | ||
64 | * Scsi_Host.highmem_io, and so forth. It's readonly to all | ||
65 | * host side drivers though. | ||
66 | */ | ||
67 | hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params); | ||
68 | if (HCC_64BIT_ADDR(hcc_params)) | ||
69 | ehci_writel(ehci, 0, &ehci->regs->segment); | ||
70 | |||
71 | /* | ||
72 | * Philips, Intel, and maybe others need CMD_RUN before the | ||
73 | * root hub will detect new devices (why?); NEC doesn't | ||
74 | */ | ||
75 | ehci->command &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); | ||
76 | ehci->command |= CMD_RUN; | ||
77 | ehci_writel(ehci, ehci->command, &ehci->regs->command); | ||
78 | dbg_cmd(ehci, "init", ehci->command); | ||
79 | |||
80 | /* | ||
81 | * Start, enabling full USB 2.0 functionality ... usb 1.1 devices | ||
82 | * are explicitly handed to companion controller(s), so no TT is | ||
83 | * involved with the root hub. (Except where one is integrated, | ||
84 | * and there's no companion controller unless maybe for USB OTG.) | ||
85 | * | ||
86 | * Turning on the CF flag will transfer ownership of all ports | ||
87 | * from the companions to the EHCI controller. If any of the | ||
88 | * companions are in the middle of a port reset at the time, it | ||
89 | * could cause trouble. Write-locking ehci_cf_port_reset_rwsem | ||
90 | * guarantees that no resets are in progress. After we set CF, | ||
91 | * a short delay lets the hardware catch up; new resets shouldn't | ||
92 | * be started before the port switching actions could complete. | ||
93 | */ | ||
94 | down_write(&ehci_cf_port_reset_rwsem); | ||
95 | hcd->state = HC_STATE_RUNNING; | ||
96 | ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); | ||
97 | ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ | ||
98 | usleep_range(5000, 5500); | ||
99 | up_write(&ehci_cf_port_reset_rwsem); | ||
100 | ehci->last_periodic_enable = ktime_get_real(); | ||
101 | |||
102 | temp = HC_VERSION(ehci_readl(ehci, &ehci->caps->hc_capbase)); | ||
103 | ehci_info(ehci, | ||
104 | "USB %x.%x started, EHCI %x.%02x%s\n", | ||
105 | ((ehci->sbrn & 0xf0)>>4), (ehci->sbrn & 0x0f), | ||
106 | temp >> 8, temp & 0xff, | ||
107 | ignore_oc ? ", overcurrent ignored" : ""); | ||
108 | |||
109 | ehci_writel(ehci, INTR_MASK, | ||
110 | &ehci->regs->intr_enable); /* Turn On Interrupts */ | ||
111 | |||
112 | /* GRR this is run-once init(), being done every time the HC starts. | ||
113 | * So long as they're part of class devices, we can't do it init() | ||
114 | * since the class device isn't created that early. | ||
115 | */ | ||
116 | create_debug_files(ehci); | ||
117 | create_companion_file(ehci); | ||
118 | |||
119 | return 0; | ||
120 | } | ||
121 | |||
122 | static int ehci_msm_reset(struct usb_hcd *hcd) | ||
123 | { | ||
124 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); | ||
125 | int retval; | ||
126 | |||
127 | ehci->caps = USB_CAPLENGTH; | ||
128 | ehci->regs = USB_CAPLENGTH + | ||
129 | HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); | ||
130 | |||
131 | /* cache the data to minimize the chip reads*/ | ||
132 | ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); | ||
133 | |||
134 | hcd->has_tt = 1; | ||
135 | ehci->sbrn = HCD_USB2; | ||
136 | |||
137 | /* data structure init */ | ||
138 | retval = ehci_init(hcd); | ||
139 | if (retval) | ||
140 | return retval; | ||
141 | |||
142 | retval = ehci_reset(ehci); | ||
143 | if (retval) | ||
144 | return retval; | ||
145 | |||
146 | /* bursts of unspecified length. */ | ||
147 | writel(0, USB_AHBBURST); | ||
148 | /* Use the AHB transactor */ | ||
149 | writel(0, USB_AHBMODE); | ||
150 | /* Disable streaming mode and select host mode */ | ||
151 | writel(0x13, USB_USBMODE); | ||
152 | |||
153 | ehci_port_power(ehci, 1); | ||
154 | return 0; | ||
155 | } | ||
156 | |||
157 | static struct hc_driver msm_hc_driver = { | ||
158 | .description = hcd_name, | ||
159 | .product_desc = "Qualcomm On-Chip EHCI Host Controller", | ||
160 | .hcd_priv_size = sizeof(struct ehci_hcd), | ||
161 | |||
162 | /* | ||
163 | * generic hardware linkage | ||
164 | */ | ||
165 | .irq = ehci_irq, | ||
166 | .flags = HCD_USB2 | HCD_MEMORY, | ||
167 | |||
168 | .reset = ehci_msm_reset, | ||
169 | .start = ehci_msm_run, | ||
170 | |||
171 | .stop = ehci_stop, | ||
172 | .shutdown = ehci_shutdown, | ||
173 | |||
174 | /* | ||
175 | * managing i/o requests and associated device resources | ||
176 | */ | ||
177 | .urb_enqueue = ehci_urb_enqueue, | ||
178 | .urb_dequeue = ehci_urb_dequeue, | ||
179 | .endpoint_disable = ehci_endpoint_disable, | ||
180 | .endpoint_reset = ehci_endpoint_reset, | ||
181 | .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, | ||
182 | |||
183 | /* | ||
184 | * scheduling support | ||
185 | */ | ||
186 | .get_frame_number = ehci_get_frame, | ||
187 | |||
188 | /* | ||
189 | * root hub support | ||
190 | */ | ||
191 | .hub_status_data = ehci_hub_status_data, | ||
192 | .hub_control = ehci_hub_control, | ||
193 | .relinquish_port = ehci_relinquish_port, | ||
194 | .port_handed_over = ehci_port_handed_over, | ||
195 | |||
196 | /* | ||
197 | * PM support | ||
198 | */ | ||
199 | .bus_suspend = ehci_bus_suspend, | ||
200 | .bus_resume = ehci_bus_resume, | ||
201 | }; | ||
202 | |||
203 | static int ehci_msm_probe(struct platform_device *pdev) | ||
204 | { | ||
205 | struct usb_hcd *hcd; | ||
206 | struct resource *res; | ||
207 | int ret; | ||
208 | |||
209 | dev_dbg(&pdev->dev, "ehci_msm proble\n"); | ||
210 | |||
211 | hcd = usb_create_hcd(&msm_hc_driver, &pdev->dev, dev_name(&pdev->dev)); | ||
212 | if (!hcd) { | ||
213 | dev_err(&pdev->dev, "Unable to create HCD\n"); | ||
214 | return -ENOMEM; | ||
215 | } | ||
216 | |||
217 | hcd->irq = platform_get_irq(pdev, 0); | ||
218 | if (hcd->irq < 0) { | ||
219 | dev_err(&pdev->dev, "Unable to get IRQ resource\n"); | ||
220 | ret = hcd->irq; | ||
221 | goto put_hcd; | ||
222 | } | ||
223 | |||
224 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
225 | if (!res) { | ||
226 | dev_err(&pdev->dev, "Unable to get memory resource\n"); | ||
227 | ret = -ENODEV; | ||
228 | goto put_hcd; | ||
229 | } | ||
230 | |||
231 | hcd->rsrc_start = res->start; | ||
232 | hcd->rsrc_len = resource_size(res); | ||
233 | hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); | ||
234 | if (!hcd->regs) { | ||
235 | dev_err(&pdev->dev, "ioremap failed\n"); | ||
236 | ret = -ENOMEM; | ||
237 | goto put_hcd; | ||
238 | } | ||
239 | |||
240 | /* | ||
241 | * OTG driver takes care of PHY initialization, clock management, | ||
242 | * powering up VBUS and mapping of registers address space. | ||
243 | */ | ||
244 | otg = otg_get_transceiver(); | ||
245 | if (!otg) { | ||
246 | dev_err(&pdev->dev, "unable to find transceiver\n"); | ||
247 | ret = -ENODEV; | ||
248 | goto unmap; | ||
249 | } | ||
250 | |||
251 | ret = otg_set_host(otg, &hcd->self); | ||
252 | if (ret < 0) { | ||
253 | dev_err(&pdev->dev, "unable to register with transceiver\n"); | ||
254 | goto put_transceiver; | ||
255 | } | ||
256 | |||
257 | device_init_wakeup(&pdev->dev, 1); | ||
258 | return 0; | ||
259 | |||
260 | put_transceiver: | ||
261 | otg_put_transceiver(otg); | ||
262 | unmap: | ||
263 | iounmap(hcd->regs); | ||
264 | put_hcd: | ||
265 | usb_put_hcd(hcd); | ||
266 | |||
267 | return ret; | ||
268 | } | ||
269 | |||
270 | static int __devexit ehci_msm_remove(struct platform_device *pdev) | ||
271 | { | ||
272 | struct usb_hcd *hcd = platform_get_drvdata(pdev); | ||
273 | |||
274 | device_init_wakeup(&pdev->dev, 0); | ||
275 | |||
276 | otg_set_host(otg, NULL); | ||
277 | otg_put_transceiver(otg); | ||
278 | |||
279 | usb_put_hcd(hcd); | ||
280 | |||
281 | return 0; | ||
282 | } | ||
283 | |||
284 | static struct platform_driver ehci_msm_driver = { | ||
285 | .probe = ehci_msm_probe, | ||
286 | .remove = __devexit_p(ehci_msm_remove), | ||
287 | .driver = { | ||
288 | .name = "msm_hsusb_host", | ||
289 | }, | ||
290 | }; | ||