1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * OHCI HCD (Host Controller Driver) for USB. 4 * 5 * Copyright (C) 2010 ST Microelectronics. 6 * Deepak Sikri<deepak.sikri@st.com> 7 * 8 * Based on various ohci-*.c drivers 9 */ 10 11 #include <linux/clk.h> 12 #include <linux/dma-mapping.h> 13 #include <linux/io.h> 14 #include <linux/kernel.h> 15 #include <linux/module.h> 16 #include <linux/of.h> 17 #include <linux/platform_device.h> 18 #include <linux/signal.h> 19 #include <linux/usb.h> 20 #include <linux/usb/hcd.h> 21 22 #include "ohci.h" 23 24 #define DRIVER_DESC "OHCI SPEAr driver" 25 26 static const char hcd_name[] = "SPEAr-ohci"; 27 struct spear_ohci { 28 struct clk *clk; 29 }; 30 31 #define to_spear_ohci(hcd) (struct spear_ohci *)(hcd_to_ohci(hcd)->priv) 32 33 static struct hc_driver __read_mostly ohci_spear_hc_driver; 34 35 static int spear_ohci_hcd_drv_probe(struct platform_device *pdev) 36 { 37 const struct hc_driver *driver = &ohci_spear_hc_driver; 38 struct ohci_hcd *ohci; 39 struct usb_hcd *hcd = NULL; 40 struct clk *usbh_clk; 41 struct spear_ohci *sohci_p; 42 struct resource *res; 43 int retval, irq; 44 45 irq = platform_get_irq(pdev, 0); 46 if (irq < 0) { 47 retval = irq; 48 goto fail; 49 } 50 51 /* 52 * Right now device-tree probed devices don't get dma_mask set. 53 * Since shared usb code relies on it, set it here for now. 54 * Once we have dma capability bindings this can go away. 55 */ 56 retval = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 57 if (retval) 58 goto fail; 59 60 usbh_clk = devm_clk_get(&pdev->dev, NULL); 61 if (IS_ERR(usbh_clk)) { 62 dev_err(&pdev->dev, "Error getting interface clock\n"); 63 retval = PTR_ERR(usbh_clk); 64 goto fail; 65 } 66 67 hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); 68 if (!hcd) { 69 retval = -ENOMEM; 70 goto fail; 71 } 72 73 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 74 hcd->regs = devm_ioremap_resource(&pdev->dev, res); 75 if (IS_ERR(hcd->regs)) { 76 retval = PTR_ERR(hcd->regs); 77 goto err_put_hcd; 78 } 79 80 hcd->rsrc_start = pdev->resource[0].start; 81 hcd->rsrc_len = resource_size(res); 82 83 sohci_p = to_spear_ohci(hcd); 84 sohci_p->clk = usbh_clk; 85 86 clk_prepare_enable(sohci_p->clk); 87 88 ohci = hcd_to_ohci(hcd); 89 90 retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), 0); 91 if (retval == 0) { 92 device_wakeup_enable(hcd->self.controller); 93 return retval; 94 } 95 96 clk_disable_unprepare(sohci_p->clk); 97 err_put_hcd: 98 usb_put_hcd(hcd); 99 fail: 100 dev_err(&pdev->dev, "init fail, %d\n", retval); 101 102 return retval; 103 } 104 105 static int spear_ohci_hcd_drv_remove(struct platform_device *pdev) 106 { 107 struct usb_hcd *hcd = platform_get_drvdata(pdev); 108 struct spear_ohci *sohci_p = to_spear_ohci(hcd); 109 110 usb_remove_hcd(hcd); 111 if (sohci_p->clk) 112 clk_disable_unprepare(sohci_p->clk); 113 114 usb_put_hcd(hcd); 115 return 0; 116 } 117 118 #if defined(CONFIG_PM) 119 static int spear_ohci_hcd_drv_suspend(struct platform_device *pdev, 120 pm_message_t message) 121 { 122 struct usb_hcd *hcd = platform_get_drvdata(pdev); 123 struct ohci_hcd *ohci = hcd_to_ohci(hcd); 124 struct spear_ohci *sohci_p = to_spear_ohci(hcd); 125 bool do_wakeup = device_may_wakeup(&pdev->dev); 126 int ret; 127 128 if (time_before(jiffies, ohci->next_statechange)) 129 msleep(5); 130 ohci->next_statechange = jiffies; 131 132 ret = ohci_suspend(hcd, do_wakeup); 133 if (ret) 134 return ret; 135 136 clk_disable_unprepare(sohci_p->clk); 137 138 return ret; 139 } 140 141 static int spear_ohci_hcd_drv_resume(struct platform_device *dev) 142 { 143 struct usb_hcd *hcd = platform_get_drvdata(dev); 144 struct ohci_hcd *ohci = hcd_to_ohci(hcd); 145 struct spear_ohci *sohci_p = to_spear_ohci(hcd); 146 147 if (time_before(jiffies, ohci->next_statechange)) 148 msleep(5); 149 ohci->next_statechange = jiffies; 150 151 clk_prepare_enable(sohci_p->clk); 152 ohci_resume(hcd, false); 153 return 0; 154 } 155 #endif 156 157 static const struct of_device_id spear_ohci_id_table[] = { 158 { .compatible = "st,spear600-ohci", }, 159 { }, 160 }; 161 MODULE_DEVICE_TABLE(of, spear_ohci_id_table); 162 163 /* Driver definition to register with the platform bus */ 164 static struct platform_driver spear_ohci_hcd_driver = { 165 .probe = spear_ohci_hcd_drv_probe, 166 .remove = spear_ohci_hcd_drv_remove, 167 #ifdef CONFIG_PM 168 .suspend = spear_ohci_hcd_drv_suspend, 169 .resume = spear_ohci_hcd_drv_resume, 170 #endif 171 .driver = { 172 .name = "spear-ohci", 173 .of_match_table = spear_ohci_id_table, 174 }, 175 }; 176 177 static const struct ohci_driver_overrides spear_overrides __initconst = { 178 .extra_priv_size = sizeof(struct spear_ohci), 179 }; 180 static int __init ohci_spear_init(void) 181 { 182 if (usb_disabled()) 183 return -ENODEV; 184 185 pr_info("%s: " DRIVER_DESC "\n", hcd_name); 186 187 ohci_init_driver(&ohci_spear_hc_driver, &spear_overrides); 188 return platform_driver_register(&spear_ohci_hcd_driver); 189 } 190 module_init(ohci_spear_init); 191 192 static void __exit ohci_spear_cleanup(void) 193 { 194 platform_driver_unregister(&spear_ohci_hcd_driver); 195 } 196 module_exit(ohci_spear_cleanup); 197 198 MODULE_DESCRIPTION(DRIVER_DESC); 199 MODULE_AUTHOR("Deepak Sikri"); 200 MODULE_LICENSE("GPL v2"); 201 MODULE_ALIAS("platform:spear-ohci"); 202