Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003 * OHCI HCD (Host Controller Driver) for USB.
0004 *
0005 * Copyright (C) 2010 ST Microelectronics.
0006 * Deepak Sikri<deepak.sikri@st.com>
0007 *
0008 * Based on various ohci-*.c drivers
0009 */
0010 
0011 #include <linux/clk.h>
0012 #include <linux/dma-mapping.h>
0013 #include <linux/io.h>
0014 #include <linux/kernel.h>
0015 #include <linux/module.h>
0016 #include <linux/of.h>
0017 #include <linux/platform_device.h>
0018 #include <linux/signal.h>
0019 #include <linux/usb.h>
0020 #include <linux/usb/hcd.h>
0021 
0022 #include "ohci.h"
0023 
0024 #define DRIVER_DESC "OHCI SPEAr driver"
0025 
0026 static const char hcd_name[] = "SPEAr-ohci";
0027 struct spear_ohci {
0028     struct clk *clk;
0029 };
0030 
0031 #define to_spear_ohci(hcd)     (struct spear_ohci *)(hcd_to_ohci(hcd)->priv)
0032 
0033 static struct hc_driver __read_mostly ohci_spear_hc_driver;
0034 
0035 static int spear_ohci_hcd_drv_probe(struct platform_device *pdev)
0036 {
0037     const struct hc_driver *driver = &ohci_spear_hc_driver;
0038     struct usb_hcd *hcd = NULL;
0039     struct clk *usbh_clk;
0040     struct spear_ohci *sohci_p;
0041     struct resource *res;
0042     int retval, irq;
0043 
0044     irq = platform_get_irq(pdev, 0);
0045     if (irq < 0) {
0046         retval = irq;
0047         goto fail;
0048     }
0049 
0050     /*
0051      * Right now device-tree probed devices don't get dma_mask set.
0052      * Since shared usb code relies on it, set it here for now.
0053      * Once we have dma capability bindings this can go away.
0054      */
0055     retval = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
0056     if (retval)
0057         goto fail;
0058 
0059     usbh_clk = devm_clk_get(&pdev->dev, NULL);
0060     if (IS_ERR(usbh_clk)) {
0061         dev_err(&pdev->dev, "Error getting interface clock\n");
0062         retval = PTR_ERR(usbh_clk);
0063         goto fail;
0064     }
0065 
0066     hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
0067     if (!hcd) {
0068         retval = -ENOMEM;
0069         goto fail;
0070     }
0071 
0072     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0073     hcd->regs = devm_ioremap_resource(&pdev->dev, res);
0074     if (IS_ERR(hcd->regs)) {
0075         retval = PTR_ERR(hcd->regs);
0076         goto err_put_hcd;
0077     }
0078 
0079     hcd->rsrc_start = res->start;
0080     hcd->rsrc_len = resource_size(res);
0081 
0082     sohci_p = to_spear_ohci(hcd);
0083     sohci_p->clk = usbh_clk;
0084 
0085     clk_prepare_enable(sohci_p->clk);
0086 
0087     retval = usb_add_hcd(hcd, irq, 0);
0088     if (retval == 0) {
0089         device_wakeup_enable(hcd->self.controller);
0090         return retval;
0091     }
0092 
0093     clk_disable_unprepare(sohci_p->clk);
0094 err_put_hcd:
0095     usb_put_hcd(hcd);
0096 fail:
0097     dev_err(&pdev->dev, "init fail, %d\n", retval);
0098 
0099     return retval;
0100 }
0101 
0102 static int spear_ohci_hcd_drv_remove(struct platform_device *pdev)
0103 {
0104     struct usb_hcd *hcd = platform_get_drvdata(pdev);
0105     struct spear_ohci *sohci_p = to_spear_ohci(hcd);
0106 
0107     usb_remove_hcd(hcd);
0108     if (sohci_p->clk)
0109         clk_disable_unprepare(sohci_p->clk);
0110 
0111     usb_put_hcd(hcd);
0112     return 0;
0113 }
0114 
0115 #if defined(CONFIG_PM)
0116 static int spear_ohci_hcd_drv_suspend(struct platform_device *pdev,
0117         pm_message_t message)
0118 {
0119     struct usb_hcd *hcd = platform_get_drvdata(pdev);
0120     struct ohci_hcd *ohci = hcd_to_ohci(hcd);
0121     struct spear_ohci *sohci_p = to_spear_ohci(hcd);
0122     bool do_wakeup = device_may_wakeup(&pdev->dev);
0123     int ret;
0124 
0125     if (time_before(jiffies, ohci->next_statechange))
0126         msleep(5);
0127     ohci->next_statechange = jiffies;
0128 
0129     ret = ohci_suspend(hcd, do_wakeup);
0130     if (ret)
0131         return ret;
0132 
0133     clk_disable_unprepare(sohci_p->clk);
0134 
0135     return ret;
0136 }
0137 
0138 static int spear_ohci_hcd_drv_resume(struct platform_device *dev)
0139 {
0140     struct usb_hcd *hcd = platform_get_drvdata(dev);
0141     struct ohci_hcd *ohci = hcd_to_ohci(hcd);
0142     struct spear_ohci *sohci_p = to_spear_ohci(hcd);
0143 
0144     if (time_before(jiffies, ohci->next_statechange))
0145         msleep(5);
0146     ohci->next_statechange = jiffies;
0147 
0148     clk_prepare_enable(sohci_p->clk);
0149     ohci_resume(hcd, false);
0150     return 0;
0151 }
0152 #endif
0153 
0154 static const struct of_device_id spear_ohci_id_table[] = {
0155     { .compatible = "st,spear600-ohci", },
0156     { },
0157 };
0158 MODULE_DEVICE_TABLE(of, spear_ohci_id_table);
0159 
0160 /* Driver definition to register with the platform bus */
0161 static struct platform_driver spear_ohci_hcd_driver = {
0162     .probe =    spear_ohci_hcd_drv_probe,
0163     .remove =   spear_ohci_hcd_drv_remove,
0164 #ifdef CONFIG_PM
0165     .suspend =  spear_ohci_hcd_drv_suspend,
0166     .resume =   spear_ohci_hcd_drv_resume,
0167 #endif
0168     .driver = {
0169         .name = "spear-ohci",
0170         .of_match_table = spear_ohci_id_table,
0171     },
0172 };
0173 
0174 static const struct ohci_driver_overrides spear_overrides __initconst = {
0175     .extra_priv_size = sizeof(struct spear_ohci),
0176 };
0177 static int __init ohci_spear_init(void)
0178 {
0179     if (usb_disabled())
0180         return -ENODEV;
0181 
0182     pr_info("%s: " DRIVER_DESC "\n", hcd_name);
0183 
0184     ohci_init_driver(&ohci_spear_hc_driver, &spear_overrides);
0185     return platform_driver_register(&spear_ohci_hcd_driver);
0186 }
0187 module_init(ohci_spear_init);
0188 
0189 static void __exit ohci_spear_cleanup(void)
0190 {
0191     platform_driver_unregister(&spear_ohci_hcd_driver);
0192 }
0193 module_exit(ohci_spear_cleanup);
0194 
0195 MODULE_DESCRIPTION(DRIVER_DESC);
0196 MODULE_AUTHOR("Deepak Sikri");
0197 MODULE_LICENSE("GPL v2");
0198 MODULE_ALIAS("platform:spear-ohci");