2012-03-13 22:57:41 +08:00
|
|
|
/*
|
|
|
|
* xhci-plat.c - xHCI host controller driver platform Bus Glue.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com
|
|
|
|
* Author: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
|
|
|
*
|
|
|
|
* A lot of code borrowed from the Linux xHCI driver.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* version 2 as published by the Free Software Foundation.
|
|
|
|
*/
|
|
|
|
|
2014-05-15 18:17:32 +08:00
|
|
|
#include <linux/clk.h>
|
2014-05-15 18:17:31 +08:00
|
|
|
#include <linux/dma-mapping.h>
|
2012-03-13 22:57:41 +08:00
|
|
|
#include <linux/module.h>
|
2013-07-26 07:04:44 +08:00
|
|
|
#include <linux/of.h>
|
2014-05-15 18:17:31 +08:00
|
|
|
#include <linux/platform_device.h>
|
2015-03-18 00:32:22 +08:00
|
|
|
#include <linux/usb/phy.h>
|
2014-05-15 18:17:31 +08:00
|
|
|
#include <linux/slab.h>
|
2015-10-09 18:30:12 +08:00
|
|
|
#include <linux/acpi.h>
|
2012-03-13 22:57:41 +08:00
|
|
|
|
|
|
|
#include "xhci.h"
|
2015-11-24 19:09:49 +08:00
|
|
|
#include "xhci-plat.h"
|
2014-05-15 18:17:33 +08:00
|
|
|
#include "xhci-mvebu.h"
|
2014-07-09 09:08:52 +08:00
|
|
|
#include "xhci-rcar.h"
|
2012-03-13 22:57:41 +08:00
|
|
|
|
2014-10-03 16:35:26 +08:00
|
|
|
static struct hc_driver __read_mostly xhci_plat_hc_driver;
|
|
|
|
|
2015-05-29 22:01:46 +08:00
|
|
|
static int xhci_plat_setup(struct usb_hcd *hcd);
|
|
|
|
static int xhci_plat_start(struct usb_hcd *hcd);
|
|
|
|
|
|
|
|
static const struct xhci_driver_overrides xhci_plat_overrides __initconst = {
|
2015-11-24 19:09:49 +08:00
|
|
|
.extra_priv_size = sizeof(struct xhci_plat_priv),
|
2015-05-29 22:01:46 +08:00
|
|
|
.reset = xhci_plat_setup,
|
|
|
|
.start = xhci_plat_start,
|
|
|
|
};
|
|
|
|
|
2016-04-22 18:17:16 +08:00
|
|
|
static void xhci_priv_plat_start(struct usb_hcd *hcd)
|
2012-03-13 22:57:41 +08:00
|
|
|
{
|
2016-04-22 18:17:16 +08:00
|
|
|
struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
|
|
|
|
|
|
|
|
if (priv->plat_start)
|
|
|
|
priv->plat_start(hcd);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int xhci_priv_init_quirk(struct usb_hcd *hcd)
|
|
|
|
{
|
|
|
|
struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
|
|
|
|
|
|
|
|
if (!priv->init_quirk)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return priv->init_quirk(hcd);
|
|
|
|
}
|
2016-04-08 21:25:08 +08:00
|
|
|
|
2016-04-22 18:17:16 +08:00
|
|
|
static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci)
|
|
|
|
{
|
2012-03-13 22:57:41 +08:00
|
|
|
/*
|
|
|
|
* As of now platform drivers don't provide MSI support so we ensure
|
|
|
|
* here that the generic code does not try to make a pci_dev from our
|
|
|
|
* dev struct in order to setup MSI
|
|
|
|
*/
|
xhci-plat: Don't enable legacy PCI interrupts.
The xHCI platform driver calls into usb_add_hcd to register the irq for
its platform device. It does not want the xHCI generic driver to
register an interrupt for it at all. The original code did that by
setting the XHCI_BROKEN_MSI quirk, which tells the xHCI driver to not
enable MSI or MSI-X for a PCI host.
Unfortunately, if CONFIG_PCI is enabled, and CONFIG_USB_DW3 is enabled,
the xHCI generic driver will attempt to register a legacy PCI interrupt
for the xHCI platform device in xhci_try_enable_msi(). This will result
in a bogus irq being registered, since the underlying device is a
platform_device, not a pci_device, and thus the pci_device->irq pointer
will be bogus.
Add a new quirk, XHCI_PLAT, so that the xHCI generic driver can
distinguish between a PCI device that can't handle MSI or MSI-X, and a
platform device that should not have its interrupts touched at all.
This quirk may be useful in the future, in case other corner cases like
this arise.
This patch should be backported to kernels as old as 3.9, that
contain the commit 00eed9c814cb8f281be6f0f5d8f45025dc0a97eb "USB: xhci:
correctly enable interrupts".
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Reported-by: Yu Y Wang <yu.y.wang@intel.com>
Tested-by: Yu Y Wang <yu.y.wang@intel.com>
Reviewed-by: Felipe Balbi <balbi@ti.com>
Cc: stable@vger.kernel.org
2013-08-09 01:08:34 +08:00
|
|
|
xhci->quirks |= XHCI_PLAT;
|
2012-03-13 22:57:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* called during probe() after chip reset completes */
|
|
|
|
static int xhci_plat_setup(struct usb_hcd *hcd)
|
|
|
|
{
|
2014-07-09 09:08:52 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
2016-04-22 18:17:16 +08:00
|
|
|
ret = xhci_priv_init_quirk(hcd);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2016-04-22 18:17:14 +08:00
|
|
|
|
2012-03-13 22:57:41 +08:00
|
|
|
return xhci_gen_setup(hcd, xhci_plat_quirks);
|
|
|
|
}
|
|
|
|
|
2014-05-28 19:22:58 +08:00
|
|
|
static int xhci_plat_start(struct usb_hcd *hcd)
|
|
|
|
{
|
2016-04-22 18:17:16 +08:00
|
|
|
xhci_priv_plat_start(hcd);
|
2014-05-28 19:22:58 +08:00
|
|
|
return xhci_run(hcd);
|
|
|
|
}
|
|
|
|
|
2015-11-24 19:09:49 +08:00
|
|
|
#ifdef CONFIG_OF
|
|
|
|
static const struct xhci_plat_priv xhci_plat_marvell_armada = {
|
2016-04-22 18:17:16 +08:00
|
|
|
.init_quirk = xhci_mvebu_mbus_init_quirk,
|
2015-11-24 19:09:49 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen2 = {
|
2015-11-24 19:09:51 +08:00
|
|
|
.firmware_name = XHCI_RCAR_FIRMWARE_NAME_V1,
|
2016-04-22 18:17:16 +08:00
|
|
|
.init_quirk = xhci_rcar_init_quirk,
|
|
|
|
.plat_start = xhci_rcar_start,
|
2015-11-24 19:09:49 +08:00
|
|
|
};
|
|
|
|
|
2015-11-24 19:09:53 +08:00
|
|
|
static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen3 = {
|
|
|
|
.firmware_name = XHCI_RCAR_FIRMWARE_NAME_V2,
|
2016-04-22 18:17:16 +08:00
|
|
|
.init_quirk = xhci_rcar_init_quirk,
|
|
|
|
.plat_start = xhci_rcar_start,
|
2015-11-24 19:09:53 +08:00
|
|
|
};
|
|
|
|
|
2015-11-24 19:09:49 +08:00
|
|
|
static const struct of_device_id usb_xhci_of_match[] = {
|
|
|
|
{
|
|
|
|
.compatible = "generic-xhci",
|
|
|
|
}, {
|
|
|
|
.compatible = "xhci-platform",
|
|
|
|
}, {
|
|
|
|
.compatible = "marvell,armada-375-xhci",
|
|
|
|
.data = &xhci_plat_marvell_armada,
|
|
|
|
}, {
|
|
|
|
.compatible = "marvell,armada-380-xhci",
|
|
|
|
.data = &xhci_plat_marvell_armada,
|
|
|
|
}, {
|
|
|
|
.compatible = "renesas,xhci-r8a7790",
|
|
|
|
.data = &xhci_plat_renesas_rcar_gen2,
|
|
|
|
}, {
|
|
|
|
.compatible = "renesas,xhci-r8a7791",
|
|
|
|
.data = &xhci_plat_renesas_rcar_gen2,
|
2015-11-24 19:09:52 +08:00
|
|
|
}, {
|
|
|
|
.compatible = "renesas,xhci-r8a7793",
|
|
|
|
.data = &xhci_plat_renesas_rcar_gen2,
|
2015-11-24 19:09:53 +08:00
|
|
|
}, {
|
|
|
|
.compatible = "renesas,xhci-r8a7795",
|
|
|
|
.data = &xhci_plat_renesas_rcar_gen3,
|
2015-11-24 19:09:49 +08:00
|
|
|
}, {
|
2016-02-18 23:55:49 +08:00
|
|
|
.compatible = "renesas,rcar-gen2-xhci",
|
|
|
|
.data = &xhci_plat_renesas_rcar_gen2,
|
|
|
|
}, {
|
|
|
|
.compatible = "renesas,rcar-gen3-xhci",
|
|
|
|
.data = &xhci_plat_renesas_rcar_gen3,
|
2015-11-24 19:09:49 +08:00
|
|
|
},
|
2016-02-18 23:55:50 +08:00
|
|
|
{},
|
2015-11-24 19:09:49 +08:00
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(of, usb_xhci_of_match);
|
|
|
|
#endif
|
|
|
|
|
2012-03-13 22:57:41 +08:00
|
|
|
static int xhci_plat_probe(struct platform_device *pdev)
|
|
|
|
{
|
2015-11-24 19:09:49 +08:00
|
|
|
const struct of_device_id *match;
|
2012-03-13 22:57:41 +08:00
|
|
|
const struct hc_driver *driver;
|
|
|
|
struct xhci_hcd *xhci;
|
|
|
|
struct resource *res;
|
|
|
|
struct usb_hcd *hcd;
|
2014-05-15 18:17:32 +08:00
|
|
|
struct clk *clk;
|
2012-03-13 22:57:41 +08:00
|
|
|
int ret;
|
|
|
|
int irq;
|
|
|
|
|
|
|
|
if (usb_disabled())
|
|
|
|
return -ENODEV;
|
|
|
|
|
2014-10-03 16:35:26 +08:00
|
|
|
driver = &xhci_plat_hc_driver;
|
2012-03-13 22:57:41 +08:00
|
|
|
|
|
|
|
irq = platform_get_irq(pdev, 0);
|
|
|
|
if (irq < 0)
|
|
|
|
return -ENODEV;
|
|
|
|
|
2015-10-09 18:30:11 +08:00
|
|
|
/* Try to set 64-bit DMA first */
|
|
|
|
if (WARN_ON(!pdev->dev.dma_mask))
|
|
|
|
/* Platform did not initialize dma_mask */
|
|
|
|
ret = dma_coerce_mask_and_coherent(&pdev->dev,
|
|
|
|
DMA_BIT_MASK(64));
|
xhci: fix dma mask setup in xhci.c
The function dma_set_mask() tests internally whether the dma_mask pointer
for the device is initialized and fails if the dma_mask pointer is NULL.
On pci platforms, the device dma_mask pointer is initialized, when pci
devices are enumerated, to point to the pci_dev->dma_mask which is 0xffffffff.
However, for non-pci platforms, the dma_mask pointer may not be initialized
and in that case dma_set_mask() will fail.
This patch initializes the dma_mask and the coherent_dma_mask to 32bits
in xhci_plat_probe(), before the call to usb_create_hcd() that sets the
"uses_dma" flag for the usb bus and the call to usb_add_hcd() that creates
coherent dma pools for the usb hcd.
Moreover, a call to dma_set_mask() does not set the device coherent_dma_mask.
Since the xhci-hcd driver calls dma_alloc_coherent() and dma_pool_alloc()
to allocate consistent DMA memory blocks, the coherent DMA address mask
has to be set explicitly.
This patch sets the coherent_dma_mask to 64bits in xhci_gen_setup() when
the xHC is capable for 64-bit DMA addressing.
If dma_set_mask() succeeds, for a given bitmask, it is guaranteed that
the given bitmask is also supported for consistent DMA mappings.
Other changes introduced in this patch are:
- The return value of dma_set_mask() is checked to ensure that the required
dma bitmask conforms with the host system's addressing capabilities.
- The dma_mask setup code for the non-primary hcd was removed since both
primary and non-primary hcd refer to the same generic device whose
dma_mask and coherent_dma_mask are already set during the setup of
the primary hcd.
- The code for reading the HCCPARAMS register to find out the addressing
capabilities of xHC was removed since its value is already cached in
xhci->hccparams.
- hcd->self.controller was replaced with the dev variable since it is
already available.
Signed-off-by: Xenia Ragiadakou <burzalodowa@gmail.com>
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
2013-08-14 10:55:19 +08:00
|
|
|
else
|
2015-10-09 18:30:11 +08:00
|
|
|
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
|
|
|
|
|
|
|
|
/* If seting 64-bit DMA mask fails, fall back to 32-bit DMA mask */
|
|
|
|
if (ret) {
|
|
|
|
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
xhci: fix dma mask setup in xhci.c
The function dma_set_mask() tests internally whether the dma_mask pointer
for the device is initialized and fails if the dma_mask pointer is NULL.
On pci platforms, the device dma_mask pointer is initialized, when pci
devices are enumerated, to point to the pci_dev->dma_mask which is 0xffffffff.
However, for non-pci platforms, the dma_mask pointer may not be initialized
and in that case dma_set_mask() will fail.
This patch initializes the dma_mask and the coherent_dma_mask to 32bits
in xhci_plat_probe(), before the call to usb_create_hcd() that sets the
"uses_dma" flag for the usb bus and the call to usb_add_hcd() that creates
coherent dma pools for the usb hcd.
Moreover, a call to dma_set_mask() does not set the device coherent_dma_mask.
Since the xhci-hcd driver calls dma_alloc_coherent() and dma_pool_alloc()
to allocate consistent DMA memory blocks, the coherent DMA address mask
has to be set explicitly.
This patch sets the coherent_dma_mask to 64bits in xhci_gen_setup() when
the xHC is capable for 64-bit DMA addressing.
If dma_set_mask() succeeds, for a given bitmask, it is guaranteed that
the given bitmask is also supported for consistent DMA mappings.
Other changes introduced in this patch are:
- The return value of dma_set_mask() is checked to ensure that the required
dma bitmask conforms with the host system's addressing capabilities.
- The dma_mask setup code for the non-primary hcd was removed since both
primary and non-primary hcd refer to the same generic device whose
dma_mask and coherent_dma_mask are already set during the setup of
the primary hcd.
- The code for reading the HCCPARAMS register to find out the addressing
capabilities of xHC was removed since its value is already cached in
xhci->hccparams.
- hcd->self.controller was replaced with the dev variable since it is
already available.
Signed-off-by: Xenia Ragiadakou <burzalodowa@gmail.com>
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
2013-08-14 10:55:19 +08:00
|
|
|
|
2012-03-13 22:57:41 +08:00
|
|
|
hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
|
|
|
|
if (!hcd)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2014-11-04 10:21:27 +08:00
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
2014-06-21 01:41:23 +08:00
|
|
|
hcd->regs = devm_ioremap_resource(&pdev->dev, res);
|
|
|
|
if (IS_ERR(hcd->regs)) {
|
|
|
|
ret = PTR_ERR(hcd->regs);
|
2012-03-13 22:57:41 +08:00
|
|
|
goto put_hcd;
|
|
|
|
}
|
|
|
|
|
2014-11-04 10:21:27 +08:00
|
|
|
hcd->rsrc_start = res->start;
|
|
|
|
hcd->rsrc_len = resource_size(res);
|
|
|
|
|
2014-05-15 18:17:32 +08:00
|
|
|
/*
|
|
|
|
* Not all platforms have a clk so it is not an error if the
|
|
|
|
* clock does not exists.
|
|
|
|
*/
|
|
|
|
clk = devm_clk_get(&pdev->dev, NULL);
|
|
|
|
if (!IS_ERR(clk)) {
|
|
|
|
ret = clk_prepare_enable(clk);
|
|
|
|
if (ret)
|
2014-06-21 01:41:23 +08:00
|
|
|
goto put_hcd;
|
usb: xhci-plat: properly handle probe deferral for devm_clk_get()
On some platforms, the clocks might be registered by a platform
driver. When this is the case, the clock platform driver may very well
be probed after xhci-plat, in which case the first probe() invocation
of xhci-plat will receive -EPROBE_DEFER as the return value of
devm_clk_get().
The current code handles that as a normal error, and simply assumes
that this means that the system doesn't have a clock for the XHCI
controller, and continues probing without calling
clk_prepare_enable(). Unfortunately, this doesn't work on systems
where the XHCI controller does have a clock, but that clock is
provided by another platform driver. In order to fix this situation,
we handle the -EPROBE_DEFER error condition specially, and abort the
XHCI controller probe(). It will be retried later automatically, the
clock will be available, devm_clk_get() will succeed, and the probe()
will continue with the clock prepared and enabled as expected.
In practice, such issue is seen on the ARM64 Marvell 7K/8K platform,
where the clocks are registered by a platform driver.
Cc: <stable@vger.kernel.org>
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-06-01 23:09:09 +08:00
|
|
|
} else if (PTR_ERR(clk) == -EPROBE_DEFER) {
|
|
|
|
ret = -EPROBE_DEFER;
|
|
|
|
goto put_hcd;
|
2014-05-15 18:17:32 +08:00
|
|
|
}
|
|
|
|
|
2015-11-24 19:09:49 +08:00
|
|
|
xhci = hcd_to_xhci(hcd);
|
2016-06-21 15:58:08 +08:00
|
|
|
match = of_match_node(usb_xhci_of_match, pdev->dev.of_node);
|
2015-11-24 19:09:49 +08:00
|
|
|
if (match) {
|
|
|
|
const struct xhci_plat_priv *priv_match = match->data;
|
|
|
|
struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
|
|
|
|
|
|
|
|
/* Just copy data for now */
|
2016-01-26 23:50:11 +08:00
|
|
|
if (priv_match)
|
|
|
|
*priv = *priv_match;
|
2015-11-24 19:09:49 +08:00
|
|
|
}
|
|
|
|
|
2013-11-05 10:46:02 +08:00
|
|
|
device_wakeup_enable(hcd->self.controller);
|
2012-03-13 22:57:41 +08:00
|
|
|
|
2014-05-15 18:17:32 +08:00
|
|
|
xhci->clk = clk;
|
2015-05-29 22:01:50 +08:00
|
|
|
xhci->main_hcd = hcd;
|
2012-03-13 22:57:41 +08:00
|
|
|
xhci->shared_hcd = usb_create_shared_hcd(driver, &pdev->dev,
|
|
|
|
dev_name(&pdev->dev), hcd);
|
|
|
|
if (!xhci->shared_hcd) {
|
|
|
|
ret = -ENOMEM;
|
2015-05-29 22:01:47 +08:00
|
|
|
goto disable_clk;
|
2012-03-13 22:57:41 +08:00
|
|
|
}
|
|
|
|
|
2016-06-21 15:58:10 +08:00
|
|
|
if (device_property_read_bool(&pdev->dev, "usb3-lpm-capable"))
|
2014-07-04 22:01:25 +08:00
|
|
|
xhci->quirks |= XHCI_LPM_SUPPORT;
|
2012-03-13 22:57:41 +08:00
|
|
|
|
2014-02-12 03:36:04 +08:00
|
|
|
if (HCC_MAX_PSA(xhci->hcc_params) >= 4)
|
|
|
|
xhci->shared_hcd->can_do_streams = 1;
|
|
|
|
|
2015-03-18 00:32:22 +08:00
|
|
|
hcd->usb_phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0);
|
|
|
|
if (IS_ERR(hcd->usb_phy)) {
|
|
|
|
ret = PTR_ERR(hcd->usb_phy);
|
|
|
|
if (ret == -EPROBE_DEFER)
|
|
|
|
goto put_usb3_hcd;
|
|
|
|
hcd->usb_phy = NULL;
|
|
|
|
} else {
|
|
|
|
ret = usb_phy_init(hcd->usb_phy);
|
|
|
|
if (ret)
|
|
|
|
goto put_usb3_hcd;
|
|
|
|
}
|
|
|
|
|
2015-05-29 22:01:47 +08:00
|
|
|
ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
|
2012-03-13 22:57:41 +08:00
|
|
|
if (ret)
|
2015-03-18 00:32:22 +08:00
|
|
|
goto disable_usb_phy;
|
2012-03-13 22:57:41 +08:00
|
|
|
|
2015-05-29 22:01:47 +08:00
|
|
|
ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
|
|
|
|
if (ret)
|
|
|
|
goto dealloc_usb2_hcd;
|
|
|
|
|
2012-03-13 22:57:41 +08:00
|
|
|
return 0;
|
|
|
|
|
2015-05-29 22:01:47 +08:00
|
|
|
|
|
|
|
dealloc_usb2_hcd:
|
|
|
|
usb_remove_hcd(hcd);
|
|
|
|
|
2015-03-18 00:32:22 +08:00
|
|
|
disable_usb_phy:
|
|
|
|
usb_phy_shutdown(hcd->usb_phy);
|
|
|
|
|
2012-03-13 22:57:41 +08:00
|
|
|
put_usb3_hcd:
|
|
|
|
usb_put_hcd(xhci->shared_hcd);
|
|
|
|
|
2014-05-15 18:17:32 +08:00
|
|
|
disable_clk:
|
|
|
|
if (!IS_ERR(clk))
|
|
|
|
clk_disable_unprepare(clk);
|
|
|
|
|
2012-03-13 22:57:41 +08:00
|
|
|
put_hcd:
|
|
|
|
usb_put_hcd(hcd);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int xhci_plat_remove(struct platform_device *dev)
|
|
|
|
{
|
|
|
|
struct usb_hcd *hcd = platform_get_drvdata(dev);
|
|
|
|
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
2014-05-15 18:17:32 +08:00
|
|
|
struct clk *clk = xhci->clk;
|
2012-03-13 22:57:41 +08:00
|
|
|
|
|
|
|
usb_remove_hcd(xhci->shared_hcd);
|
2015-03-18 00:32:22 +08:00
|
|
|
usb_phy_shutdown(hcd->usb_phy);
|
2012-03-13 22:57:41 +08:00
|
|
|
|
|
|
|
usb_remove_hcd(hcd);
|
2015-05-29 22:01:47 +08:00
|
|
|
usb_put_hcd(xhci->shared_hcd);
|
|
|
|
|
2014-05-15 18:17:32 +08:00
|
|
|
if (!IS_ERR(clk))
|
|
|
|
clk_disable_unprepare(clk);
|
2012-03-13 22:57:41 +08:00
|
|
|
usb_put_hcd(hcd);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-05-08 21:52:19 +08:00
|
|
|
#ifdef CONFIG_PM_SLEEP
|
2013-02-11 18:58:00 +08:00
|
|
|
static int xhci_plat_suspend(struct device *dev)
|
|
|
|
{
|
|
|
|
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
|
|
|
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
|
|
|
|
2014-11-18 17:27:14 +08:00
|
|
|
/*
|
|
|
|
* xhci_suspend() needs `do_wakeup` to know whether host is allowed
|
|
|
|
* to do wakeup during suspend. Since xhci_plat_suspend is currently
|
|
|
|
* only designed for system suspend, device_may_wakeup() is enough
|
|
|
|
* to dertermine whether host is allowed to do wakeup. Need to
|
|
|
|
* reconsider this when xhci_plat_suspend enlarges its scope, e.g.,
|
|
|
|
* also applies to runtime suspend.
|
|
|
|
*/
|
|
|
|
return xhci_suspend(xhci, device_may_wakeup(dev));
|
2013-02-11 18:58:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int xhci_plat_resume(struct device *dev)
|
|
|
|
{
|
|
|
|
struct usb_hcd *hcd = dev_get_drvdata(dev);
|
|
|
|
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
|
|
|
|
|
|
|
return xhci_resume(xhci, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct dev_pm_ops xhci_plat_pm_ops = {
|
|
|
|
SET_SYSTEM_SLEEP_PM_OPS(xhci_plat_suspend, xhci_plat_resume)
|
|
|
|
};
|
|
|
|
#define DEV_PM_OPS (&xhci_plat_pm_ops)
|
|
|
|
#else
|
|
|
|
#define DEV_PM_OPS NULL
|
|
|
|
#endif /* CONFIG_PM */
|
|
|
|
|
2015-10-09 18:30:12 +08:00
|
|
|
static const struct acpi_device_id usb_xhci_acpi_match[] = {
|
|
|
|
/* XHCI-compliant USB Controller */
|
|
|
|
{ "PNP0D10", },
|
|
|
|
{ }
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(acpi, usb_xhci_acpi_match);
|
|
|
|
|
2012-03-13 22:57:41 +08:00
|
|
|
static struct platform_driver usb_xhci_driver = {
|
|
|
|
.probe = xhci_plat_probe,
|
|
|
|
.remove = xhci_plat_remove,
|
|
|
|
.driver = {
|
|
|
|
.name = "xhci-hcd",
|
2013-02-11 18:58:00 +08:00
|
|
|
.pm = DEV_PM_OPS,
|
2013-07-26 07:04:44 +08:00
|
|
|
.of_match_table = of_match_ptr(usb_xhci_of_match),
|
2015-10-09 18:30:12 +08:00
|
|
|
.acpi_match_table = ACPI_PTR(usb_xhci_acpi_match),
|
2012-03-13 22:57:41 +08:00
|
|
|
},
|
|
|
|
};
|
|
|
|
MODULE_ALIAS("platform:xhci-hcd");
|
|
|
|
|
2014-10-03 16:35:29 +08:00
|
|
|
static int __init xhci_plat_init(void)
|
2012-03-13 22:57:41 +08:00
|
|
|
{
|
2015-05-29 22:01:46 +08:00
|
|
|
xhci_init_driver(&xhci_plat_hc_driver, &xhci_plat_overrides);
|
2012-03-13 22:57:41 +08:00
|
|
|
return platform_driver_register(&usb_xhci_driver);
|
|
|
|
}
|
2014-10-03 16:35:29 +08:00
|
|
|
module_init(xhci_plat_init);
|
2012-03-13 22:57:41 +08:00
|
|
|
|
2014-10-03 16:35:29 +08:00
|
|
|
static void __exit xhci_plat_exit(void)
|
2012-03-13 22:57:41 +08:00
|
|
|
{
|
|
|
|
platform_driver_unregister(&usb_xhci_driver);
|
|
|
|
}
|
2014-10-03 16:35:29 +08:00
|
|
|
module_exit(xhci_plat_exit);
|
|
|
|
|
|
|
|
MODULE_DESCRIPTION("xHCI Platform Host Controller Driver");
|
|
|
|
MODULE_LICENSE("GPL");
|