Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * SuperH EHCI host controller driver
0004  *
0005  * Copyright (C) 2010  Paul Mundt
0006  *
0007  * Based on ohci-sh.c and ehci-atmel.c.
0008  */
0009 #include <linux/platform_device.h>
0010 #include <linux/clk.h>
0011 
0012 struct ehci_sh_priv {
0013     struct clk *iclk, *fclk;
0014     struct usb_hcd *hcd;
0015 };
0016 
0017 static int ehci_sh_reset(struct usb_hcd *hcd)
0018 {
0019     struct ehci_hcd *ehci = hcd_to_ehci(hcd);
0020 
0021     ehci->caps = hcd->regs;
0022 
0023     return ehci_setup(hcd);
0024 }
0025 
0026 static const struct hc_driver ehci_sh_hc_driver = {
0027     .description            = hcd_name,
0028     .product_desc           = "SuperH EHCI",
0029     .hcd_priv_size          = sizeof(struct ehci_hcd),
0030 
0031     /*
0032      * generic hardware linkage
0033      */
0034     .irq                = ehci_irq,
0035     .flags              = HCD_USB2 | HCD_DMA | HCD_MEMORY | HCD_BH,
0036 
0037     /*
0038      * basic lifecycle operations
0039      */
0040     .reset              = ehci_sh_reset,
0041     .start              = ehci_run,
0042     .stop               = ehci_stop,
0043     .shutdown           = ehci_shutdown,
0044 
0045     /*
0046      * managing i/o requests and associated device resources
0047      */
0048     .urb_enqueue            = ehci_urb_enqueue,
0049     .urb_dequeue            = ehci_urb_dequeue,
0050     .endpoint_disable       = ehci_endpoint_disable,
0051     .endpoint_reset         = ehci_endpoint_reset,
0052 
0053     /*
0054      * scheduling support
0055      */
0056     .get_frame_number       = ehci_get_frame,
0057 
0058     /*
0059      * root hub support
0060      */
0061     .hub_status_data        = ehci_hub_status_data,
0062     .hub_control            = ehci_hub_control,
0063 
0064 #ifdef CONFIG_PM
0065     .bus_suspend            = ehci_bus_suspend,
0066     .bus_resume         = ehci_bus_resume,
0067 #endif
0068 
0069     .relinquish_port        = ehci_relinquish_port,
0070     .port_handed_over       = ehci_port_handed_over,
0071     .clear_tt_buffer_complete   = ehci_clear_tt_buffer_complete,
0072 };
0073 
0074 static int ehci_hcd_sh_probe(struct platform_device *pdev)
0075 {
0076     struct resource *res;
0077     struct ehci_sh_priv *priv;
0078     struct usb_hcd *hcd;
0079     int irq, ret;
0080 
0081     if (usb_disabled())
0082         return -ENODEV;
0083 
0084     irq = platform_get_irq(pdev, 0);
0085     if (irq <= 0) {
0086         ret = -ENODEV;
0087         goto fail_create_hcd;
0088     }
0089 
0090     /* initialize hcd */
0091     hcd = usb_create_hcd(&ehci_sh_hc_driver, &pdev->dev,
0092                  dev_name(&pdev->dev));
0093     if (!hcd) {
0094         ret = -ENOMEM;
0095         goto fail_create_hcd;
0096     }
0097 
0098     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0099     hcd->regs = devm_ioremap_resource(&pdev->dev, res);
0100     if (IS_ERR(hcd->regs)) {
0101         ret = PTR_ERR(hcd->regs);
0102         goto fail_request_resource;
0103     }
0104     hcd->rsrc_start = res->start;
0105     hcd->rsrc_len = resource_size(res);
0106 
0107     priv = devm_kzalloc(&pdev->dev, sizeof(struct ehci_sh_priv),
0108                 GFP_KERNEL);
0109     if (!priv) {
0110         ret = -ENOMEM;
0111         goto fail_request_resource;
0112     }
0113 
0114     /* These are optional, we don't care if they fail */
0115     priv->fclk = devm_clk_get(&pdev->dev, "usb_fck");
0116     if (IS_ERR(priv->fclk))
0117         priv->fclk = NULL;
0118 
0119     priv->iclk = devm_clk_get(&pdev->dev, "usb_ick");
0120     if (IS_ERR(priv->iclk))
0121         priv->iclk = NULL;
0122 
0123     clk_enable(priv->fclk);
0124     clk_enable(priv->iclk);
0125 
0126     ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
0127     if (ret != 0) {
0128         dev_err(&pdev->dev, "Failed to add hcd");
0129         goto fail_add_hcd;
0130     }
0131     device_wakeup_enable(hcd->self.controller);
0132 
0133     priv->hcd = hcd;
0134     platform_set_drvdata(pdev, priv);
0135 
0136     return ret;
0137 
0138 fail_add_hcd:
0139     clk_disable(priv->iclk);
0140     clk_disable(priv->fclk);
0141 
0142 fail_request_resource:
0143     usb_put_hcd(hcd);
0144 fail_create_hcd:
0145     dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), ret);
0146 
0147     return ret;
0148 }
0149 
0150 static int ehci_hcd_sh_remove(struct platform_device *pdev)
0151 {
0152     struct ehci_sh_priv *priv = platform_get_drvdata(pdev);
0153     struct usb_hcd *hcd = priv->hcd;
0154 
0155     usb_remove_hcd(hcd);
0156     usb_put_hcd(hcd);
0157 
0158     clk_disable(priv->fclk);
0159     clk_disable(priv->iclk);
0160 
0161     return 0;
0162 }
0163 
0164 static void ehci_hcd_sh_shutdown(struct platform_device *pdev)
0165 {
0166     struct ehci_sh_priv *priv = platform_get_drvdata(pdev);
0167     struct usb_hcd *hcd = priv->hcd;
0168 
0169     if (hcd->driver->shutdown)
0170         hcd->driver->shutdown(hcd);
0171 }
0172 
0173 static struct platform_driver ehci_hcd_sh_driver = {
0174     .probe      = ehci_hcd_sh_probe,
0175     .remove     = ehci_hcd_sh_remove,
0176     .shutdown   = ehci_hcd_sh_shutdown,
0177     .driver     = {
0178         .name   = "sh_ehci",
0179     },
0180 };
0181 
0182 MODULE_ALIAS("platform:sh_ehci");