Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003 * Driver for EHCI HCD on SPEAr SOC
0004 *
0005 * Copyright (C) 2010 ST Micro Electronics,
0006 * Deepak Sikri <deepak.sikri@st.com>
0007 *
0008 * Based on various ehci-*.c drivers
0009 */
0010 
0011 #include <linux/clk.h>
0012 #include <linux/dma-mapping.h>
0013 #include <linux/io.h>
0014 #include <linux/jiffies.h>
0015 #include <linux/kernel.h>
0016 #include <linux/module.h>
0017 #include <linux/of.h>
0018 #include <linux/platform_device.h>
0019 #include <linux/pm.h>
0020 #include <linux/usb.h>
0021 #include <linux/usb/hcd.h>
0022 
0023 #include "ehci.h"
0024 
0025 #define DRIVER_DESC "EHCI SPEAr driver"
0026 
0027 static const char hcd_name[] = "SPEAr-ehci";
0028 
0029 struct spear_ehci {
0030     struct clk *clk;
0031 };
0032 
0033 #define to_spear_ehci(hcd)  (struct spear_ehci *)(hcd_to_ehci(hcd)->priv)
0034 
0035 static struct hc_driver __read_mostly ehci_spear_hc_driver;
0036 
0037 static int __maybe_unused ehci_spear_drv_suspend(struct device *dev)
0038 {
0039     struct usb_hcd *hcd = dev_get_drvdata(dev);
0040     bool do_wakeup = device_may_wakeup(dev);
0041 
0042     return ehci_suspend(hcd, do_wakeup);
0043 }
0044 
0045 static int __maybe_unused ehci_spear_drv_resume(struct device *dev)
0046 {
0047     struct usb_hcd *hcd = dev_get_drvdata(dev);
0048 
0049     ehci_resume(hcd, false);
0050     return 0;
0051 }
0052 
0053 static SIMPLE_DEV_PM_OPS(ehci_spear_pm_ops, ehci_spear_drv_suspend,
0054         ehci_spear_drv_resume);
0055 
0056 static int spear_ehci_hcd_drv_probe(struct platform_device *pdev)
0057 {
0058     struct usb_hcd *hcd ;
0059     struct spear_ehci *sehci;
0060     struct resource *res;
0061     struct clk *usbh_clk;
0062     const struct hc_driver *driver = &ehci_spear_hc_driver;
0063     int irq, retval;
0064 
0065     if (usb_disabled())
0066         return -ENODEV;
0067 
0068     irq = platform_get_irq(pdev, 0);
0069     if (irq < 0) {
0070         retval = irq;
0071         goto fail;
0072     }
0073 
0074     /*
0075      * Right now device-tree probed devices don't get dma_mask set.
0076      * Since shared usb code relies on it, set it here for now.
0077      * Once we have dma capability bindings this can go away.
0078      */
0079     retval = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
0080     if (retval)
0081         goto fail;
0082 
0083     usbh_clk = devm_clk_get(&pdev->dev, NULL);
0084     if (IS_ERR(usbh_clk)) {
0085         dev_err(&pdev->dev, "Error getting interface clock\n");
0086         retval = PTR_ERR(usbh_clk);
0087         goto fail;
0088     }
0089 
0090     hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
0091     if (!hcd) {
0092         retval = -ENOMEM;
0093         goto fail;
0094     }
0095 
0096     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0097     hcd->regs = devm_ioremap_resource(&pdev->dev, res);
0098     if (IS_ERR(hcd->regs)) {
0099         retval = PTR_ERR(hcd->regs);
0100         goto err_put_hcd;
0101     }
0102     hcd->rsrc_start = res->start;
0103     hcd->rsrc_len = resource_size(res);
0104 
0105     sehci = to_spear_ehci(hcd);
0106     sehci->clk = usbh_clk;
0107 
0108     /* registers start at offset 0x0 */
0109     hcd_to_ehci(hcd)->caps = hcd->regs;
0110 
0111     clk_prepare_enable(sehci->clk);
0112     retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
0113     if (retval)
0114         goto err_stop_ehci;
0115 
0116     device_wakeup_enable(hcd->self.controller);
0117     return retval;
0118 
0119 err_stop_ehci:
0120     clk_disable_unprepare(sehci->clk);
0121 err_put_hcd:
0122     usb_put_hcd(hcd);
0123 fail:
0124     dev_err(&pdev->dev, "init fail, %d\n", retval);
0125 
0126     return retval ;
0127 }
0128 
0129 static int spear_ehci_hcd_drv_remove(struct platform_device *pdev)
0130 {
0131     struct usb_hcd *hcd = platform_get_drvdata(pdev);
0132     struct spear_ehci *sehci = to_spear_ehci(hcd);
0133 
0134     usb_remove_hcd(hcd);
0135 
0136     if (sehci->clk)
0137         clk_disable_unprepare(sehci->clk);
0138     usb_put_hcd(hcd);
0139 
0140     return 0;
0141 }
0142 
0143 static const struct of_device_id spear_ehci_id_table[] = {
0144     { .compatible = "st,spear600-ehci", },
0145     { },
0146 };
0147 MODULE_DEVICE_TABLE(of, spear_ehci_id_table);
0148 
0149 static struct platform_driver spear_ehci_hcd_driver = {
0150     .probe      = spear_ehci_hcd_drv_probe,
0151     .remove     = spear_ehci_hcd_drv_remove,
0152     .shutdown   = usb_hcd_platform_shutdown,
0153     .driver     = {
0154         .name = "spear-ehci",
0155         .bus = &platform_bus_type,
0156         .pm = pm_ptr(&ehci_spear_pm_ops),
0157         .of_match_table = spear_ehci_id_table,
0158     }
0159 };
0160 
0161 static const struct ehci_driver_overrides spear_overrides __initconst = {
0162     .extra_priv_size = sizeof(struct spear_ehci),
0163 };
0164 
0165 static int __init ehci_spear_init(void)
0166 {
0167     if (usb_disabled())
0168         return -ENODEV;
0169 
0170     pr_info("%s: " DRIVER_DESC "\n", hcd_name);
0171 
0172     ehci_init_driver(&ehci_spear_hc_driver, &spear_overrides);
0173     return platform_driver_register(&spear_ehci_hcd_driver);
0174 }
0175 module_init(ehci_spear_init);
0176 
0177 static void __exit ehci_spear_cleanup(void)
0178 {
0179     platform_driver_unregister(&spear_ehci_hcd_driver);
0180 }
0181 module_exit(ehci_spear_cleanup);
0182 
0183 MODULE_DESCRIPTION(DRIVER_DESC);
0184 MODULE_ALIAS("platform:spear-ehci");
0185 MODULE_AUTHOR("Deepak Sikri");
0186 MODULE_LICENSE("GPL");