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 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 int spear_ohci_hcd_drv_probe(struct platform_device *pdev) 95 { 96 const struct hc_driver *driver = &ohci_spear_hc_driver; 97 struct usb_hcd *hcd = NULL; 98 struct clk *usbh_clk; 99 struct spear_ohci *ohci_p; 100 struct resource *res; 101 int retval, irq; 102 103 irq = platform_get_irq(pdev, 0); 104 if (irq < 0) { 105 retval = irq; 106 goto fail; 107 } 108 109 /* 110 * Right now device-tree probed devices don't get dma_mask set. 111 * Since shared usb code relies on it, set it here for now. 112 * Once we have dma capability bindings this can go away. 113 */ 114 if (!pdev->dev.dma_mask) 115 pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; 116 retval = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); 117 if (retval) 118 goto fail; 119 120 usbh_clk = devm_clk_get(&pdev->dev, NULL); 121 if (IS_ERR(usbh_clk)) { 122 dev_err(&pdev->dev, "Error getting interface clock\n"); 123 retval = PTR_ERR(usbh_clk); 124 goto fail; 125 } 126 127 hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); 128 if (!hcd) { 129 retval = -ENOMEM; 130 goto fail; 131 } 132 133 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 134 if (!res) { 135 retval = -ENODEV; 136 goto err_put_hcd; 137 } 138 139 hcd->rsrc_start = pdev->resource[0].start; 140 hcd->rsrc_len = resource_size(res); 141 if (!devm_request_mem_region(&pdev->dev, hcd->rsrc_start, hcd->rsrc_len, 142 hcd_name)) { 143 dev_dbg(&pdev->dev, "request_mem_region failed\n"); 144 retval = -EBUSY; 145 goto err_put_hcd; 146 } 147 148 hcd->regs = devm_ioremap(&pdev->dev, hcd->rsrc_start, hcd->rsrc_len); 149 if (!hcd->regs) { 150 dev_dbg(&pdev->dev, "ioremap failed\n"); 151 retval = -ENOMEM; 152 goto err_put_hcd; 153 } 154 155 ohci_p = (struct spear_ohci *)hcd_to_ohci(hcd); 156 ohci_p->clk = usbh_clk; 157 spear_start_ohci(ohci_p); 158 ohci_hcd_init(hcd_to_ohci(hcd)); 159 160 retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), 0); 161 if (retval == 0) 162 return retval; 163 164 spear_stop_ohci(ohci_p); 165 err_put_hcd: 166 usb_put_hcd(hcd); 167 fail: 168 dev_err(&pdev->dev, "init fail, %d\n", retval); 169 170 return retval; 171 } 172 173 static int spear_ohci_hcd_drv_remove(struct platform_device *pdev) 174 { 175 struct usb_hcd *hcd = platform_get_drvdata(pdev); 176 struct spear_ohci *ohci_p = to_spear_ohci(hcd); 177 178 usb_remove_hcd(hcd); 179 if (ohci_p->clk) 180 spear_stop_ohci(ohci_p); 181 182 usb_put_hcd(hcd); 183 return 0; 184 } 185 186 #if defined(CONFIG_PM) 187 static int spear_ohci_hcd_drv_suspend(struct platform_device *dev, 188 pm_message_t message) 189 { 190 struct usb_hcd *hcd = platform_get_drvdata(dev); 191 struct ohci_hcd *ohci = hcd_to_ohci(hcd); 192 struct spear_ohci *ohci_p = to_spear_ohci(hcd); 193 194 if (time_before(jiffies, ohci->next_statechange)) 195 msleep(5); 196 ohci->next_statechange = jiffies; 197 198 spear_stop_ohci(ohci_p); 199 return 0; 200 } 201 202 static int spear_ohci_hcd_drv_resume(struct platform_device *dev) 203 { 204 struct usb_hcd *hcd = platform_get_drvdata(dev); 205 struct ohci_hcd *ohci = hcd_to_ohci(hcd); 206 struct spear_ohci *ohci_p = to_spear_ohci(hcd); 207 208 if (time_before(jiffies, ohci->next_statechange)) 209 msleep(5); 210 ohci->next_statechange = jiffies; 211 212 spear_start_ohci(ohci_p); 213 ohci_resume(hcd, false); 214 return 0; 215 } 216 #endif 217 218 static struct of_device_id spear_ohci_id_table[] = { 219 { .compatible = "st,spear600-ohci", }, 220 { }, 221 }; 222 223 /* Driver definition to register with the platform bus */ 224 static struct platform_driver spear_ohci_hcd_driver = { 225 .probe = spear_ohci_hcd_drv_probe, 226 .remove = spear_ohci_hcd_drv_remove, 227 #ifdef CONFIG_PM 228 .suspend = spear_ohci_hcd_drv_suspend, 229 .resume = spear_ohci_hcd_drv_resume, 230 #endif 231 .driver = { 232 .owner = THIS_MODULE, 233 .name = "spear-ohci", 234 .of_match_table = spear_ohci_id_table, 235 }, 236 }; 237 238 MODULE_ALIAS("platform:spear-ohci"); 239