Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * xhci-plat.c - xHCI host controller driver platform Bus Glue.
0004  *
0005  * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com
0006  * Author: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
0007  *
0008  * A lot of code borrowed from the Linux xHCI driver.
0009  */
0010 
0011 #include <linux/clk.h>
0012 #include <linux/dma-mapping.h>
0013 #include <linux/module.h>
0014 #include <linux/pci.h>
0015 #include <linux/of.h>
0016 #include <linux/of_device.h>
0017 #include <linux/platform_device.h>
0018 #include <linux/usb/phy.h>
0019 #include <linux/slab.h>
0020 #include <linux/acpi.h>
0021 #include <linux/usb/of.h>
0022 
0023 #include "xhci.h"
0024 #include "xhci-plat.h"
0025 #include "xhci-mvebu.h"
0026 #include "xhci-rcar.h"
0027 
0028 static struct hc_driver __read_mostly xhci_plat_hc_driver;
0029 
0030 static int xhci_plat_setup(struct usb_hcd *hcd);
0031 static int xhci_plat_start(struct usb_hcd *hcd);
0032 
0033 static const struct xhci_driver_overrides xhci_plat_overrides __initconst = {
0034     .extra_priv_size = sizeof(struct xhci_plat_priv),
0035     .reset = xhci_plat_setup,
0036     .start = xhci_plat_start,
0037 };
0038 
0039 static void xhci_priv_plat_start(struct usb_hcd *hcd)
0040 {
0041     struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
0042 
0043     if (priv->plat_start)
0044         priv->plat_start(hcd);
0045 }
0046 
0047 static int xhci_priv_init_quirk(struct usb_hcd *hcd)
0048 {
0049     struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
0050 
0051     if (!priv->init_quirk)
0052         return 0;
0053 
0054     return priv->init_quirk(hcd);
0055 }
0056 
0057 static int xhci_priv_suspend_quirk(struct usb_hcd *hcd)
0058 {
0059     struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
0060 
0061     if (!priv->suspend_quirk)
0062         return 0;
0063 
0064     return priv->suspend_quirk(hcd);
0065 }
0066 
0067 static int xhci_priv_resume_quirk(struct usb_hcd *hcd)
0068 {
0069     struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
0070 
0071     if (!priv->resume_quirk)
0072         return 0;
0073 
0074     return priv->resume_quirk(hcd);
0075 }
0076 
0077 static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci)
0078 {
0079     struct xhci_plat_priv *priv = xhci_to_priv(xhci);
0080 
0081     /*
0082      * As of now platform drivers don't provide MSI support so we ensure
0083      * here that the generic code does not try to make a pci_dev from our
0084      * dev struct in order to setup MSI
0085      */
0086     xhci->quirks |= XHCI_PLAT | priv->quirks;
0087 }
0088 
0089 /* called during probe() after chip reset completes */
0090 static int xhci_plat_setup(struct usb_hcd *hcd)
0091 {
0092     int ret;
0093 
0094 
0095     ret = xhci_priv_init_quirk(hcd);
0096     if (ret)
0097         return ret;
0098 
0099     return xhci_gen_setup(hcd, xhci_plat_quirks);
0100 }
0101 
0102 static int xhci_plat_start(struct usb_hcd *hcd)
0103 {
0104     xhci_priv_plat_start(hcd);
0105     return xhci_run(hcd);
0106 }
0107 
0108 #ifdef CONFIG_OF
0109 static const struct xhci_plat_priv xhci_plat_marvell_armada = {
0110     .init_quirk = xhci_mvebu_mbus_init_quirk,
0111 };
0112 
0113 static const struct xhci_plat_priv xhci_plat_marvell_armada3700 = {
0114     .init_quirk = xhci_mvebu_a3700_init_quirk,
0115 };
0116 
0117 static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen2 = {
0118     SET_XHCI_PLAT_PRIV_FOR_RCAR(XHCI_RCAR_FIRMWARE_NAME_V1)
0119 };
0120 
0121 static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen3 = {
0122     SET_XHCI_PLAT_PRIV_FOR_RCAR(XHCI_RCAR_FIRMWARE_NAME_V3)
0123 };
0124 
0125 static const struct xhci_plat_priv xhci_plat_brcm = {
0126     .quirks = XHCI_RESET_ON_RESUME,
0127 };
0128 
0129 static const struct of_device_id usb_xhci_of_match[] = {
0130     {
0131         .compatible = "generic-xhci",
0132     }, {
0133         .compatible = "xhci-platform",
0134     }, {
0135         .compatible = "marvell,armada-375-xhci",
0136         .data = &xhci_plat_marvell_armada,
0137     }, {
0138         .compatible = "marvell,armada-380-xhci",
0139         .data = &xhci_plat_marvell_armada,
0140     }, {
0141         .compatible = "marvell,armada3700-xhci",
0142         .data = &xhci_plat_marvell_armada3700,
0143     }, {
0144         .compatible = "renesas,xhci-r8a7790",
0145         .data = &xhci_plat_renesas_rcar_gen2,
0146     }, {
0147         .compatible = "renesas,xhci-r8a7791",
0148         .data = &xhci_plat_renesas_rcar_gen2,
0149     }, {
0150         .compatible = "renesas,xhci-r8a7793",
0151         .data = &xhci_plat_renesas_rcar_gen2,
0152     }, {
0153         .compatible = "renesas,xhci-r8a7795",
0154         .data = &xhci_plat_renesas_rcar_gen3,
0155     }, {
0156         .compatible = "renesas,xhci-r8a7796",
0157         .data = &xhci_plat_renesas_rcar_gen3,
0158     }, {
0159         .compatible = "renesas,rcar-gen2-xhci",
0160         .data = &xhci_plat_renesas_rcar_gen2,
0161     }, {
0162         .compatible = "renesas,rcar-gen3-xhci",
0163         .data = &xhci_plat_renesas_rcar_gen3,
0164     }, {
0165         .compatible = "brcm,xhci-brcm-v2",
0166         .data = &xhci_plat_brcm,
0167     }, {
0168         .compatible = "brcm,bcm7445-xhci",
0169         .data = &xhci_plat_brcm,
0170     },
0171     {},
0172 };
0173 MODULE_DEVICE_TABLE(of, usb_xhci_of_match);
0174 #endif
0175 
0176 static int xhci_plat_probe(struct platform_device *pdev)
0177 {
0178     const struct xhci_plat_priv *priv_match;
0179     const struct hc_driver  *driver;
0180     struct device       *sysdev, *tmpdev;
0181     struct xhci_hcd     *xhci;
0182     struct resource         *res;
0183     struct usb_hcd      *hcd, *usb3_hcd;
0184     int         ret;
0185     int         irq;
0186     struct xhci_plat_priv   *priv = NULL;
0187 
0188 
0189     if (usb_disabled())
0190         return -ENODEV;
0191 
0192     driver = &xhci_plat_hc_driver;
0193 
0194     irq = platform_get_irq(pdev, 0);
0195     if (irq < 0)
0196         return irq;
0197 
0198     /*
0199      * sysdev must point to a device that is known to the system firmware
0200      * or PCI hardware. We handle these three cases here:
0201      * 1. xhci_plat comes from firmware
0202      * 2. xhci_plat is child of a device from firmware (dwc3-plat)
0203      * 3. xhci_plat is grandchild of a pci device (dwc3-pci)
0204      */
0205     for (sysdev = &pdev->dev; sysdev; sysdev = sysdev->parent) {
0206         if (is_of_node(sysdev->fwnode) ||
0207             is_acpi_device_node(sysdev->fwnode))
0208             break;
0209 #ifdef CONFIG_PCI
0210         else if (sysdev->bus == &pci_bus_type)
0211             break;
0212 #endif
0213     }
0214 
0215     if (!sysdev)
0216         sysdev = &pdev->dev;
0217 
0218     if (WARN_ON(!sysdev->dma_mask))
0219         /* Platform did not initialize dma_mask */
0220         ret = dma_coerce_mask_and_coherent(sysdev, DMA_BIT_MASK(64));
0221     else
0222         ret = dma_set_mask_and_coherent(sysdev, DMA_BIT_MASK(64));
0223     if (ret)
0224         return ret;
0225 
0226     pm_runtime_set_active(&pdev->dev);
0227     pm_runtime_enable(&pdev->dev);
0228     pm_runtime_get_noresume(&pdev->dev);
0229 
0230     hcd = __usb_create_hcd(driver, sysdev, &pdev->dev,
0231                    dev_name(&pdev->dev), NULL);
0232     if (!hcd) {
0233         ret = -ENOMEM;
0234         goto disable_runtime;
0235     }
0236 
0237     hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
0238     if (IS_ERR(hcd->regs)) {
0239         ret = PTR_ERR(hcd->regs);
0240         goto put_hcd;
0241     }
0242 
0243     hcd->rsrc_start = res->start;
0244     hcd->rsrc_len = resource_size(res);
0245 
0246     xhci = hcd_to_xhci(hcd);
0247 
0248     xhci->allow_single_roothub = 1;
0249 
0250     /*
0251      * Not all platforms have clks so it is not an error if the
0252      * clock do not exist.
0253      */
0254     xhci->reg_clk = devm_clk_get_optional(&pdev->dev, "reg");
0255     if (IS_ERR(xhci->reg_clk)) {
0256         ret = PTR_ERR(xhci->reg_clk);
0257         goto put_hcd;
0258     }
0259 
0260     ret = clk_prepare_enable(xhci->reg_clk);
0261     if (ret)
0262         goto put_hcd;
0263 
0264     xhci->clk = devm_clk_get_optional(&pdev->dev, NULL);
0265     if (IS_ERR(xhci->clk)) {
0266         ret = PTR_ERR(xhci->clk);
0267         goto disable_reg_clk;
0268     }
0269 
0270     ret = clk_prepare_enable(xhci->clk);
0271     if (ret)
0272         goto disable_reg_clk;
0273 
0274     if (pdev->dev.of_node)
0275         priv_match = of_device_get_match_data(&pdev->dev);
0276     else
0277         priv_match = dev_get_platdata(&pdev->dev);
0278 
0279     if (priv_match) {
0280         priv = hcd_to_xhci_priv(hcd);
0281         /* Just copy data for now */
0282         *priv = *priv_match;
0283     }
0284 
0285     device_set_wakeup_capable(&pdev->dev, true);
0286 
0287     xhci->main_hcd = hcd;
0288 
0289     /* imod_interval is the interrupt moderation value in nanoseconds. */
0290     xhci->imod_interval = 40000;
0291 
0292     /* Iterate over all parent nodes for finding quirks */
0293     for (tmpdev = &pdev->dev; tmpdev; tmpdev = tmpdev->parent) {
0294 
0295         if (device_property_read_bool(tmpdev, "usb2-lpm-disable"))
0296             xhci->quirks |= XHCI_HW_LPM_DISABLE;
0297 
0298         if (device_property_read_bool(tmpdev, "usb3-lpm-capable"))
0299             xhci->quirks |= XHCI_LPM_SUPPORT;
0300 
0301         if (device_property_read_bool(tmpdev, "quirk-broken-port-ped"))
0302             xhci->quirks |= XHCI_BROKEN_PORT_PED;
0303 
0304         device_property_read_u32(tmpdev, "imod-interval-ns",
0305                      &xhci->imod_interval);
0306     }
0307 
0308     hcd->usb_phy = devm_usb_get_phy_by_phandle(sysdev, "usb-phy", 0);
0309     if (IS_ERR(hcd->usb_phy)) {
0310         ret = PTR_ERR(hcd->usb_phy);
0311         if (ret == -EPROBE_DEFER)
0312             goto disable_clk;
0313         hcd->usb_phy = NULL;
0314     } else {
0315         ret = usb_phy_init(hcd->usb_phy);
0316         if (ret)
0317             goto disable_clk;
0318     }
0319 
0320     hcd->tpl_support = of_usb_host_tpl_support(sysdev->of_node);
0321 
0322     if (priv && (priv->quirks & XHCI_SKIP_PHY_INIT))
0323         hcd->skip_phy_initialization = 1;
0324 
0325     if (priv && (priv->quirks & XHCI_SG_TRB_CACHE_SIZE_QUIRK))
0326         xhci->quirks |= XHCI_SG_TRB_CACHE_SIZE_QUIRK;
0327 
0328     ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
0329     if (ret)
0330         goto disable_usb_phy;
0331 
0332     if (!xhci_has_one_roothub(xhci)) {
0333         xhci->shared_hcd = __usb_create_hcd(driver, sysdev, &pdev->dev,
0334                             dev_name(&pdev->dev), hcd);
0335         if (!xhci->shared_hcd) {
0336             ret = -ENOMEM;
0337             goto dealloc_usb2_hcd;
0338         }
0339 
0340         xhci->shared_hcd->tpl_support = hcd->tpl_support;
0341     }
0342 
0343     usb3_hcd = xhci_get_usb3_hcd(xhci);
0344     if (usb3_hcd && HCC_MAX_PSA(xhci->hcc_params) >= 4)
0345         usb3_hcd->can_do_streams = 1;
0346 
0347     if (xhci->shared_hcd) {
0348         ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
0349         if (ret)
0350             goto put_usb3_hcd;
0351     }
0352 
0353     device_enable_async_suspend(&pdev->dev);
0354     pm_runtime_put_noidle(&pdev->dev);
0355 
0356     /*
0357      * Prevent runtime pm from being on as default, users should enable
0358      * runtime pm using power/control in sysfs.
0359      */
0360     pm_runtime_forbid(&pdev->dev);
0361 
0362     return 0;
0363 
0364 
0365 put_usb3_hcd:
0366     usb_put_hcd(xhci->shared_hcd);
0367 
0368 dealloc_usb2_hcd:
0369     usb_remove_hcd(hcd);
0370 
0371 disable_usb_phy:
0372     usb_phy_shutdown(hcd->usb_phy);
0373 
0374 disable_clk:
0375     clk_disable_unprepare(xhci->clk);
0376 
0377 disable_reg_clk:
0378     clk_disable_unprepare(xhci->reg_clk);
0379 
0380 put_hcd:
0381     usb_put_hcd(hcd);
0382 
0383 disable_runtime:
0384     pm_runtime_put_noidle(&pdev->dev);
0385     pm_runtime_disable(&pdev->dev);
0386 
0387     return ret;
0388 }
0389 
0390 static int xhci_plat_remove(struct platform_device *dev)
0391 {
0392     struct usb_hcd  *hcd = platform_get_drvdata(dev);
0393     struct xhci_hcd *xhci = hcd_to_xhci(hcd);
0394     struct clk *clk = xhci->clk;
0395     struct clk *reg_clk = xhci->reg_clk;
0396     struct usb_hcd *shared_hcd = xhci->shared_hcd;
0397 
0398     pm_runtime_get_sync(&dev->dev);
0399     xhci->xhc_state |= XHCI_STATE_REMOVING;
0400 
0401     if (shared_hcd) {
0402         usb_remove_hcd(shared_hcd);
0403         xhci->shared_hcd = NULL;
0404     }
0405 
0406     usb_phy_shutdown(hcd->usb_phy);
0407 
0408     usb_remove_hcd(hcd);
0409 
0410     if (shared_hcd)
0411         usb_put_hcd(shared_hcd);
0412 
0413     clk_disable_unprepare(clk);
0414     clk_disable_unprepare(reg_clk);
0415     usb_put_hcd(hcd);
0416 
0417     pm_runtime_disable(&dev->dev);
0418     pm_runtime_put_noidle(&dev->dev);
0419     pm_runtime_set_suspended(&dev->dev);
0420 
0421     return 0;
0422 }
0423 
0424 static int __maybe_unused xhci_plat_suspend(struct device *dev)
0425 {
0426     struct usb_hcd  *hcd = dev_get_drvdata(dev);
0427     struct xhci_hcd *xhci = hcd_to_xhci(hcd);
0428     int ret;
0429 
0430     if (pm_runtime_suspended(dev))
0431         pm_runtime_resume(dev);
0432 
0433     ret = xhci_priv_suspend_quirk(hcd);
0434     if (ret)
0435         return ret;
0436     /*
0437      * xhci_suspend() needs `do_wakeup` to know whether host is allowed
0438      * to do wakeup during suspend.
0439      */
0440     return xhci_suspend(xhci, device_may_wakeup(dev));
0441 }
0442 
0443 static int __maybe_unused xhci_plat_resume(struct device *dev)
0444 {
0445     struct usb_hcd  *hcd = dev_get_drvdata(dev);
0446     struct xhci_hcd *xhci = hcd_to_xhci(hcd);
0447     int ret;
0448 
0449     ret = xhci_priv_resume_quirk(hcd);
0450     if (ret)
0451         return ret;
0452 
0453     ret = xhci_resume(xhci, 0);
0454     if (ret)
0455         return ret;
0456 
0457     pm_runtime_disable(dev);
0458     pm_runtime_set_active(dev);
0459     pm_runtime_enable(dev);
0460 
0461     return 0;
0462 }
0463 
0464 static int __maybe_unused xhci_plat_runtime_suspend(struct device *dev)
0465 {
0466     struct usb_hcd  *hcd = dev_get_drvdata(dev);
0467     struct xhci_hcd *xhci = hcd_to_xhci(hcd);
0468     int ret;
0469 
0470     ret = xhci_priv_suspend_quirk(hcd);
0471     if (ret)
0472         return ret;
0473 
0474     return xhci_suspend(xhci, true);
0475 }
0476 
0477 static int __maybe_unused xhci_plat_runtime_resume(struct device *dev)
0478 {
0479     struct usb_hcd  *hcd = dev_get_drvdata(dev);
0480     struct xhci_hcd *xhci = hcd_to_xhci(hcd);
0481 
0482     return xhci_resume(xhci, 0);
0483 }
0484 
0485 static const struct dev_pm_ops xhci_plat_pm_ops = {
0486     SET_SYSTEM_SLEEP_PM_OPS(xhci_plat_suspend, xhci_plat_resume)
0487 
0488     SET_RUNTIME_PM_OPS(xhci_plat_runtime_suspend,
0489                xhci_plat_runtime_resume,
0490                NULL)
0491 };
0492 
0493 #ifdef CONFIG_ACPI
0494 static const struct acpi_device_id usb_xhci_acpi_match[] = {
0495     /* XHCI-compliant USB Controller */
0496     { "PNP0D10", },
0497     { }
0498 };
0499 MODULE_DEVICE_TABLE(acpi, usb_xhci_acpi_match);
0500 #endif
0501 
0502 static struct platform_driver usb_xhci_driver = {
0503     .probe  = xhci_plat_probe,
0504     .remove = xhci_plat_remove,
0505     .shutdown = usb_hcd_platform_shutdown,
0506     .driver = {
0507         .name = "xhci-hcd",
0508         .pm = &xhci_plat_pm_ops,
0509         .of_match_table = of_match_ptr(usb_xhci_of_match),
0510         .acpi_match_table = ACPI_PTR(usb_xhci_acpi_match),
0511     },
0512 };
0513 MODULE_ALIAS("platform:xhci-hcd");
0514 
0515 static int __init xhci_plat_init(void)
0516 {
0517     xhci_init_driver(&xhci_plat_hc_driver, &xhci_plat_overrides);
0518     return platform_driver_register(&usb_xhci_driver);
0519 }
0520 module_init(xhci_plat_init);
0521 
0522 static void __exit xhci_plat_exit(void)
0523 {
0524     platform_driver_unregister(&usb_xhci_driver);
0525 }
0526 module_exit(xhci_plat_exit);
0527 
0528 MODULE_DESCRIPTION("xHCI Platform Host Controller Driver");
0529 MODULE_LICENSE("GPL");