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