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 #include <linux/of.h> 18 19 struct spear_ohci { 20 struct ohci_hcd ohci; 21 struct clk *clk; 22 }; 23 24 #define to_spear_ohci(hcd) (struct spear_ohci *)hcd_to_ohci(hcd) 25 26 static void spear_start_ohci(struct spear_ohci *ohci) 27 { 28 clk_prepare_enable(ohci->clk); 29 } 30 31 static void spear_stop_ohci(struct spear_ohci *ohci) 32 { 33 clk_disable_unprepare(ohci->clk); 34 } 35 36 static int __devinit ohci_spear_start(struct usb_hcd *hcd) 37 { 38 struct ohci_hcd *ohci = hcd_to_ohci(hcd); 39 int ret; 40 41 ret = ohci_init(ohci); 42 if (ret < 0) 43 return ret; 44 ohci->regs = hcd->regs; 45 46 ret = ohci_run(ohci); 47 if (ret < 0) { 48 dev_err(hcd->self.controller, "can't start\n"); 49 ohci_stop(hcd); 50 return ret; 51 } 52 53 create_debug_files(ohci); 54 55 #ifdef DEBUG 56 ohci_dump(ohci, 1); 57 #endif 58 return 0; 59 } 60 61 static const struct hc_driver ohci_spear_hc_driver = { 62 .description = hcd_name, 63 .product_desc = "SPEAr OHCI", 64 .hcd_priv_size = sizeof(struct spear_ohci), 65 66 /* generic hardware linkage */ 67 .irq = ohci_irq, 68 .flags = HCD_USB11 | HCD_MEMORY, 69 70 /* basic lifecycle operations */ 71 .start = ohci_spear_start, 72 .stop = ohci_stop, 73 .shutdown = ohci_shutdown, 74 #ifdef CONFIG_PM 75 .bus_suspend = ohci_bus_suspend, 76 .bus_resume = ohci_bus_resume, 77 #endif 78 79 /* managing i/o requests and associated device resources */ 80 .urb_enqueue = ohci_urb_enqueue, 81 .urb_dequeue = ohci_urb_dequeue, 82 .endpoint_disable = ohci_endpoint_disable, 83 84 /* scheduling support */ 85 .get_frame_number = ohci_get_frame, 86 87 /* root hub support */ 88 .hub_status_data = ohci_hub_status_data, 89 .hub_control = ohci_hub_control, 90 91 .start_port_reset = ohci_start_port_reset, 92 }; 93 94 static u64 spear_ohci_dma_mask = DMA_BIT_MASK(32); 95 96 static int spear_ohci_hcd_drv_probe(struct platform_device *pdev) 97 { 98 const struct hc_driver *driver = &ohci_spear_hc_driver; 99 struct usb_hcd *hcd = NULL; 100 struct clk *usbh_clk; 101 struct spear_ohci *ohci_p; 102 struct resource *res; 103 int retval, irq; 104 char clk_name[20] = "usbh_clk"; 105 static int instance = -1; 106 107 irq = platform_get_irq(pdev, 0); 108 if (irq < 0) { 109 retval = irq; 110 goto fail_irq_get; 111 } 112 113 /* 114 * Right now device-tree probed devices don't get dma_mask set. 115 * Since shared usb code relies on it, set it here for now. 116 * Once we have dma capability bindings this can go away. 117 */ 118 if (!pdev->dev.dma_mask) 119 pdev->dev.dma_mask = &spear_ohci_dma_mask; 120 121 /* 122 * Increment the device instance, when probing via device-tree 123 */ 124 if (pdev->id < 0) 125 instance++; 126 else 127 instance = pdev->id; 128 sprintf(clk_name, "usbh.%01d_clk", instance); 129 130 usbh_clk = clk_get(NULL, clk_name); 131 if (IS_ERR(usbh_clk)) { 132 dev_err(&pdev->dev, "Error getting interface clock\n"); 133 retval = PTR_ERR(usbh_clk); 134 goto fail_get_usbh_clk; 135 } 136 137 hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); 138 if (!hcd) { 139 retval = -ENOMEM; 140 goto fail_create_hcd; 141 } 142 143 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 144 if (!res) { 145 retval = -ENODEV; 146 goto fail_request_resource; 147 } 148 149 hcd->rsrc_start = pdev->resource[0].start; 150 hcd->rsrc_len = resource_size(res); 151 if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { 152 dev_dbg(&pdev->dev, "request_mem_region failed\n"); 153 retval = -EBUSY; 154 goto fail_request_resource; 155 } 156 157 hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); 158 if (!hcd->regs) { 159 dev_dbg(&pdev->dev, "ioremap failed\n"); 160 retval = -ENOMEM; 161 goto fail_ioremap; 162 } 163 164 ohci_p = (struct spear_ohci *)hcd_to_ohci(hcd); 165 ohci_p->clk = usbh_clk; 166 spear_start_ohci(ohci_p); 167 ohci_hcd_init(hcd_to_ohci(hcd)); 168 169 retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), 0); 170 if (retval == 0) 171 return retval; 172 173 spear_stop_ohci(ohci_p); 174 iounmap(hcd->regs); 175 fail_ioremap: 176 release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 177 fail_request_resource: 178 usb_put_hcd(hcd); 179 fail_create_hcd: 180 clk_put(usbh_clk); 181 fail_get_usbh_clk: 182 fail_irq_get: 183 dev_err(&pdev->dev, "init fail, %d\n", retval); 184 185 return retval; 186 } 187 188 static int spear_ohci_hcd_drv_remove(struct platform_device *pdev) 189 { 190 struct usb_hcd *hcd = platform_get_drvdata(pdev); 191 struct spear_ohci *ohci_p = to_spear_ohci(hcd); 192 193 usb_remove_hcd(hcd); 194 if (ohci_p->clk) 195 spear_stop_ohci(ohci_p); 196 197 iounmap(hcd->regs); 198 release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 199 usb_put_hcd(hcd); 200 201 if (ohci_p->clk) 202 clk_put(ohci_p->clk); 203 platform_set_drvdata(pdev, NULL); 204 return 0; 205 } 206 207 #if defined(CONFIG_PM) 208 static int spear_ohci_hcd_drv_suspend(struct platform_device *dev, 209 pm_message_t message) 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_stop_ohci(ohci_p); 220 return 0; 221 } 222 223 static int spear_ohci_hcd_drv_resume(struct platform_device *dev) 224 { 225 struct usb_hcd *hcd = platform_get_drvdata(dev); 226 struct ohci_hcd *ohci = hcd_to_ohci(hcd); 227 struct spear_ohci *ohci_p = to_spear_ohci(hcd); 228 229 if (time_before(jiffies, ohci->next_statechange)) 230 msleep(5); 231 ohci->next_statechange = jiffies; 232 233 spear_start_ohci(ohci_p); 234 ohci_finish_controller_resume(hcd); 235 return 0; 236 } 237 #endif 238 239 static struct of_device_id spear_ohci_id_table[] __devinitdata = { 240 { .compatible = "st,spear600-ohci", }, 241 { }, 242 }; 243 244 /* Driver definition to register with the platform bus */ 245 static struct platform_driver spear_ohci_hcd_driver = { 246 .probe = spear_ohci_hcd_drv_probe, 247 .remove = spear_ohci_hcd_drv_remove, 248 #ifdef CONFIG_PM 249 .suspend = spear_ohci_hcd_drv_suspend, 250 .resume = spear_ohci_hcd_drv_resume, 251 #endif 252 .driver = { 253 .owner = THIS_MODULE, 254 .name = "spear-ohci", 255 .of_match_table = of_match_ptr(spear_ohci_id_table), 256 }, 257 }; 258 259 MODULE_ALIAS("platform:spear-ohci"); 260