1/* ehci-msm.c - HSUSB Host Controller Driver Implementation 2 * 3 * Copyright (c) 2008-2011, 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#include <linux/pm_runtime.h> 29 30#include <linux/usb/otg.h> 31#include <linux/usb/msm_hsusb_hw.h> 32 33#define MSM_USB_BASE (hcd->regs) 34 35static struct otg_transceiver *otg; 36 37static int ehci_msm_reset(struct usb_hcd *hcd) 38{ 39 struct ehci_hcd *ehci = hcd_to_ehci(hcd); 40 int retval; 41 42 ehci->caps = USB_CAPLENGTH; 43 hcd->has_tt = 1; 44 45 retval = ehci_setup(hcd); 46 if (retval) 47 return retval; 48 49 /* bursts of unspecified length. */ 50 writel(0, USB_AHBBURST); 51 /* Use the AHB transactor */ 52 writel(0, USB_AHBMODE); 53 /* Disable streaming mode and select host mode */ 54 writel(0x13, USB_USBMODE); 55 56 ehci_port_power(ehci, 1); 57 return 0; 58} 59 60static struct hc_driver msm_hc_driver = { 61 .description = hcd_name, 62 .product_desc = "Qualcomm On-Chip EHCI Host Controller", 63 .hcd_priv_size = sizeof(struct ehci_hcd), 64 65 /* 66 * generic hardware linkage 67 */ 68 .irq = ehci_irq, 69 .flags = HCD_USB2 | HCD_MEMORY, 70 71 .reset = ehci_msm_reset, 72 .start = ehci_run, 73 74 .stop = ehci_stop, 75 .shutdown = ehci_shutdown, 76 77 /* 78 * managing i/o requests and associated device resources 79 */ 80 .urb_enqueue = ehci_urb_enqueue, 81 .urb_dequeue = ehci_urb_dequeue, 82 .endpoint_disable = ehci_endpoint_disable, 83 .endpoint_reset = ehci_endpoint_reset, 84 .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, 85 86 /* 87 * scheduling support 88 */ 89 .get_frame_number = ehci_get_frame, 90 91 /* 92 * root hub support 93 */ 94 .hub_status_data = ehci_hub_status_data, 95 .hub_control = ehci_hub_control, 96 .relinquish_port = ehci_relinquish_port, 97 .port_handed_over = ehci_port_handed_over, 98 99 /* 100 * PM support 101 */ 102 .bus_suspend = ehci_bus_suspend, 103 .bus_resume = ehci_bus_resume, 104}; 105 106static int ehci_msm_probe(struct platform_device *pdev) 107{ 108 struct usb_hcd *hcd; 109 struct resource *res; 110 int ret; 111 112 dev_dbg(&pdev->dev, "ehci_msm proble\n"); 113 114 hcd = usb_create_hcd(&msm_hc_driver, &pdev->dev, dev_name(&pdev->dev)); 115 if (!hcd) { 116 dev_err(&pdev->dev, "Unable to create HCD\n"); 117 return -ENOMEM; 118 } 119 120 hcd->irq = platform_get_irq(pdev, 0); 121 if (hcd->irq < 0) { 122 dev_err(&pdev->dev, "Unable to get IRQ resource\n"); 123 ret = hcd->irq; 124 goto put_hcd; 125 } 126 127 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 128 if (!res) { 129 dev_err(&pdev->dev, "Unable to get memory resource\n"); 130 ret = -ENODEV; 131 goto put_hcd; 132 } 133 134 hcd->rsrc_start = res->start; 135 hcd->rsrc_len = resource_size(res); 136 hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); 137 if (!hcd->regs) { 138 dev_err(&pdev->dev, "ioremap failed\n"); 139 ret = -ENOMEM; 140 goto put_hcd; 141 } 142 143 /* 144 * OTG driver takes care of PHY initialization, clock management, 145 * powering up VBUS, mapping of registers address space and power 146 * management. 147 */ 148 otg = otg_get_transceiver(); 149 if (!otg) { 150 dev_err(&pdev->dev, "unable to find transceiver\n"); 151 ret = -ENODEV; 152 goto unmap; 153 } 154 155 ret = otg_set_host(otg, &hcd->self); 156 if (ret < 0) { 157 dev_err(&pdev->dev, "unable to register with transceiver\n"); 158 goto put_transceiver; 159 } 160 161 device_init_wakeup(&pdev->dev, 1); 162 /* 163 * OTG device parent of HCD takes care of putting 164 * hardware into low power mode. 165 */ 166 pm_runtime_no_callbacks(&pdev->dev); 167 pm_runtime_enable(&pdev->dev); 168 169 return 0; 170 171put_transceiver: 172 otg_put_transceiver(otg); 173unmap: 174 iounmap(hcd->regs); 175put_hcd: 176 usb_put_hcd(hcd); 177 178 return ret; 179} 180 181static int __devexit ehci_msm_remove(struct platform_device *pdev) 182{ 183 struct usb_hcd *hcd = platform_get_drvdata(pdev); 184 185 device_init_wakeup(&pdev->dev, 0); 186 pm_runtime_disable(&pdev->dev); 187 pm_runtime_set_suspended(&pdev->dev); 188 189 otg_set_host(otg, NULL); 190 otg_put_transceiver(otg); 191 192 usb_put_hcd(hcd); 193 194 return 0; 195} 196 197#ifdef CONFIG_PM 198static int ehci_msm_pm_suspend(struct device *dev) 199{ 200 struct usb_hcd *hcd = dev_get_drvdata(dev); 201 bool wakeup = device_may_wakeup(dev); 202 203 dev_dbg(dev, "ehci-msm PM suspend\n"); 204 205 /* 206 * EHCI helper function has also the same check before manipulating 207 * port wakeup flags. We do check here the same condition before 208 * calling the same helper function to avoid bringing hardware 209 * from Low power mode when there is no need for adjusting port 210 * wakeup flags. 211 */ 212 if (hcd->self.root_hub->do_remote_wakeup && !wakeup) { 213 pm_runtime_resume(dev); 214 ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd), 215 wakeup); 216 } 217 218 return 0; 219} 220 221static int ehci_msm_pm_resume(struct device *dev) 222{ 223 struct usb_hcd *hcd = dev_get_drvdata(dev); 224 225 dev_dbg(dev, "ehci-msm PM resume\n"); 226 ehci_prepare_ports_for_controller_resume(hcd_to_ehci(hcd)); 227 228 return 0; 229} 230#else 231#define ehci_msm_pm_suspend NULL 232#define ehci_msm_pm_resume NULL 233#endif 234 235static const struct dev_pm_ops ehci_msm_dev_pm_ops = { 236 .suspend = ehci_msm_pm_suspend, 237 .resume = ehci_msm_pm_resume, 238}; 239 240static struct platform_driver ehci_msm_driver = { 241 .probe = ehci_msm_probe, 242 .remove = __devexit_p(ehci_msm_remove), 243 .driver = { 244 .name = "msm_hsusb_host", 245 .pm = &ehci_msm_dev_pm_ops, 246 }, 247}; 248