1/* 2* OHCI HCD (Host Controller Driver) for USB. 3* 4* Copyright (C) 2010 ST Microelectronics. 5* Deepak Sikri<deepak.sikri@st.com> 6* 7* Based on various ohci-*.c drivers 8* 9* This file is licensed under the terms of the GNU General Public 10* License version 2. This program is licensed "as is" without any 11* warranty of any kind, whether express or implied. 12*/ 13 14#include <linux/signal.h> 15#include <linux/platform_device.h> 16#include <linux/clk.h> 17 18struct spear_ohci { 19 struct ohci_hcd ohci; 20 struct clk *clk; 21}; 22 23#define to_spear_ohci(hcd) (struct spear_ohci *)hcd_to_ohci(hcd) 24 25static void spear_start_ohci(struct spear_ohci *ohci) 26{ 27 clk_enable(ohci->clk); 28} 29 30static void spear_stop_ohci(struct spear_ohci *ohci) 31{ 32 clk_disable(ohci->clk); 33} 34 35static int __devinit ohci_spear_start(struct usb_hcd *hcd) 36{ 37 struct ohci_hcd *ohci = hcd_to_ohci(hcd); 38 int ret; 39 40 ret = ohci_init(ohci); 41 if (ret < 0) 42 return ret; 43 ohci->regs = hcd->regs; 44 45 ret = ohci_run(ohci); 46 if (ret < 0) { 47 dev_err(hcd->self.controller, "can't start\n"); 48 ohci_stop(hcd); 49 return ret; 50 } 51 52 create_debug_files(ohci); 53 54#ifdef DEBUG 55 ohci_dump(ohci, 1); 56#endif 57 return 0; 58} 59 60static const struct hc_driver ohci_spear_hc_driver = { 61 .description = hcd_name, 62 .product_desc = "SPEAr OHCI", 63 .hcd_priv_size = sizeof(struct spear_ohci), 64 65 /* generic hardware linkage */ 66 .irq = ohci_irq, 67 .flags = HCD_USB11 | HCD_MEMORY, 68 69 /* basic lifecycle operations */ 70 .start = ohci_spear_start, 71 .stop = ohci_stop, 72 .shutdown = ohci_shutdown, 73#ifdef CONFIG_PM 74 .bus_suspend = ohci_bus_suspend, 75 .bus_resume = ohci_bus_resume, 76#endif 77 78 /* managing i/o requests and associated device resources */ 79 .urb_enqueue = ohci_urb_enqueue, 80 .urb_dequeue = ohci_urb_dequeue, 81 .endpoint_disable = ohci_endpoint_disable, 82 83 /* scheduling support */ 84 .get_frame_number = ohci_get_frame, 85 86 /* root hub support */ 87 .hub_status_data = ohci_hub_status_data, 88 .hub_control = ohci_hub_control, 89 90 .start_port_reset = ohci_start_port_reset, 91}; 92 93static int spear_ohci_hcd_drv_probe(struct platform_device *pdev) 94{ 95 const struct hc_driver *driver = &ohci_spear_hc_driver; 96 struct usb_hcd *hcd = NULL; 97 struct clk *usbh_clk; 98 struct spear_ohci *ohci_p; 99 struct resource *res; 100 int retval, irq; 101 int *pdata = pdev->dev.platform_data; 102 char clk_name[20] = "usbh_clk"; 103 104 if (pdata == NULL) 105 return -EFAULT; 106 107 irq = platform_get_irq(pdev, 0); 108 if (irq < 0) { 109 retval = irq; 110 goto fail_irq_get; 111 } 112 113 if (*pdata >= 0) 114 sprintf(clk_name, "usbh.%01d_clk", *pdata); 115 116 usbh_clk = clk_get(NULL, clk_name); 117 if (IS_ERR(usbh_clk)) { 118 dev_err(&pdev->dev, "Error getting interface clock\n"); 119 retval = PTR_ERR(usbh_clk); 120 goto fail_get_usbh_clk; 121 } 122 123 hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); 124 if (!hcd) { 125 retval = -ENOMEM; 126 goto fail_create_hcd; 127 } 128 129 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 130 if (!res) { 131 retval = -ENODEV; 132 goto fail_request_resource; 133 } 134 135 hcd->rsrc_start = pdev->resource[0].start; 136 hcd->rsrc_len = resource_size(res); 137 if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { 138 dev_dbg(&pdev->dev, "request_mem_region failed\n"); 139 retval = -EBUSY; 140 goto fail_request_resource; 141 } 142 143 hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); 144 if (!hcd->regs) { 145 dev_dbg(&pdev->dev, "ioremap failed\n"); 146 retval = -ENOMEM; 147 goto fail_ioremap; 148 } 149 150 ohci_p = (struct spear_ohci *)hcd_to_ohci(hcd); 151 ohci_p->clk = usbh_clk; 152 spear_start_ohci(ohci_p); 153 ohci_hcd_init(hcd_to_ohci(hcd)); 154 155 retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), 0); 156 if (retval == 0) 157 return retval; 158 159 spear_stop_ohci(ohci_p); 160 iounmap(hcd->regs); 161fail_ioremap: 162 release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 163fail_request_resource: 164 usb_put_hcd(hcd); 165fail_create_hcd: 166 clk_put(usbh_clk); 167fail_get_usbh_clk: 168fail_irq_get: 169 dev_err(&pdev->dev, "init fail, %d\n", retval); 170 171 return retval; 172} 173 174static int spear_ohci_hcd_drv_remove(struct platform_device *pdev) 175{ 176 struct usb_hcd *hcd = platform_get_drvdata(pdev); 177 struct spear_ohci *ohci_p = to_spear_ohci(hcd); 178 179 usb_remove_hcd(hcd); 180 if (ohci_p->clk) 181 spear_stop_ohci(ohci_p); 182 183 iounmap(hcd->regs); 184 release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 185 usb_put_hcd(hcd); 186 187 if (ohci_p->clk) 188 clk_put(ohci_p->clk); 189 platform_set_drvdata(pdev, NULL); 190 return 0; 191} 192 193#if defined(CONFIG_PM) 194static int spear_ohci_hcd_drv_suspend(struct platform_device *dev, 195 pm_message_t message) 196{ 197 struct usb_hcd *hcd = platform_get_drvdata(dev); 198 struct ohci_hcd *ohci = hcd_to_ohci(hcd); 199 struct spear_ohci *ohci_p = to_spear_ohci(hcd); 200 201 if (time_before(jiffies, ohci->next_statechange)) 202 msleep(5); 203 ohci->next_statechange = jiffies; 204 205 spear_stop_ohci(ohci_p); 206 return 0; 207} 208 209static int spear_ohci_hcd_drv_resume(struct platform_device *dev) 210{ 211 struct usb_hcd *hcd = platform_get_drvdata(dev); 212 struct ohci_hcd *ohci = hcd_to_ohci(hcd); 213 struct spear_ohci *ohci_p = to_spear_ohci(hcd); 214 215 if (time_before(jiffies, ohci->next_statechange)) 216 msleep(5); 217 ohci->next_statechange = jiffies; 218 219 spear_start_ohci(ohci_p); 220 ohci_finish_controller_resume(hcd); 221 return 0; 222} 223#endif 224 225/* Driver definition to register with the platform bus */ 226static struct platform_driver spear_ohci_hcd_driver = { 227 .probe = spear_ohci_hcd_drv_probe, 228 .remove = spear_ohci_hcd_drv_remove, 229#ifdef CONFIG_PM 230 .suspend = spear_ohci_hcd_drv_suspend, 231 .resume = spear_ohci_hcd_drv_resume, 232#endif 233 .driver = { 234 .owner = THIS_MODULE, 235 .name = "spear-ohci", 236 }, 237}; 238 239MODULE_ALIAS("platform:spear-ohci"); 240