2017-11-03 11:28:30 +01:00
// SPDX-License-Identifier: GPL-2.0
2012-03-13 16:57:41 +02:00
/*
* xhci - plat . c - xHCI host controller driver platform Bus Glue .
*
2020-07-11 15:58:13 +02:00
* Copyright ( C ) 2012 Texas Instruments Incorporated - https : //www.ti.com
2012-03-13 16:57:41 +02:00
* Author : Sebastian Andrzej Siewior < bigeasy @ linutronix . de >
*
* A lot of code borrowed from the Linux xHCI driver .
*/
2014-05-15 12:17:32 +02:00
# include <linux/clk.h>
2014-05-15 12:17:31 +02:00
# include <linux/dma-mapping.h>
2012-03-13 16:57:41 +02:00
# include <linux/module.h>
2017-03-13 10:18:44 +08:00
# include <linux/pci.h>
2013-07-25 19:04:44 -04:00
# include <linux/of.h>
2017-10-05 11:21:49 +03:00
# include <linux/of_device.h>
2014-05-15 12:17:31 +02:00
# include <linux/platform_device.h>
2015-03-17 18:32:22 +02:00
# include <linux/usb/phy.h>
2014-05-15 12:17:31 +02:00
# include <linux/slab.h>
2015-10-09 13:30:12 +03:00
# include <linux/acpi.h>
2018-09-20 19:13:36 +03:00
# include <linux/usb/of.h>
2012-03-13 16:57:41 +02:00
# include "xhci.h"
2015-11-24 13:09:49 +02:00
# include "xhci-plat.h"
2014-05-15 12:17:33 +02:00
# include "xhci-mvebu.h"
2014-07-09 10:08:52 +09:00
# include "xhci-rcar.h"
2012-03-13 16:57:41 +02:00
2014-10-03 11:35:26 +03:00
static struct hc_driver __read_mostly xhci_plat_hc_driver ;
2015-05-29 17:01:46 +03: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 13:09:49 +02:00
. extra_priv_size = sizeof ( struct xhci_plat_priv ) ,
2015-05-29 17:01:46 +03:00
. reset = xhci_plat_setup ,
. start = xhci_plat_start ,
} ;
2016-04-22 13:17:16 +03:00
static void xhci_priv_plat_start ( struct usb_hcd * hcd )
2012-03-13 16:57:41 +02:00
{
2016-04-22 13:17:16 +03: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 16:25:08 +03:00
2020-09-18 16:17:44 +03:00
static int xhci_priv_suspend_quirk ( struct usb_hcd * hcd )
{
struct xhci_plat_priv * priv = hcd_to_xhci_priv ( hcd ) ;
if ( ! priv - > suspend_quirk )
return 0 ;
return priv - > suspend_quirk ( hcd ) ;
}
2017-04-19 16:55:47 +03:00
static int xhci_priv_resume_quirk ( struct usb_hcd * hcd )
{
struct xhci_plat_priv * priv = hcd_to_xhci_priv ( hcd ) ;
if ( ! priv - > resume_quirk )
return 0 ;
return priv - > resume_quirk ( hcd ) ;
}
2016-04-22 13:17:16 +03:00
static void xhci_plat_quirks ( struct device * dev , struct xhci_hcd * xhci )
{
2019-09-02 21:01:36 +09:00
struct xhci_plat_priv * priv = xhci_to_priv ( xhci ) ;
2012-03-13 16:57:41 +02: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
*/
2019-09-02 21:01:36 +09:00
xhci - > quirks | = XHCI_PLAT | priv - > quirks ;
2012-03-13 16:57:41 +02:00
}
/* called during probe() after chip reset completes */
static int xhci_plat_setup ( struct usb_hcd * hcd )
{
2014-07-09 10:08:52 +09:00
int ret ;
2016-04-22 13:17:16 +03:00
ret = xhci_priv_init_quirk ( hcd ) ;
if ( ret )
return ret ;
2016-04-22 13:17:14 +03:00
2012-03-13 16:57:41 +02:00
return xhci_gen_setup ( hcd , xhci_plat_quirks ) ;
}
2014-05-28 20:22:58 +09:00
static int xhci_plat_start ( struct usb_hcd * hcd )
{
2016-04-22 13:17:16 +03:00
xhci_priv_plat_start ( hcd ) ;
2014-05-28 20:22:58 +09:00
return xhci_run ( hcd ) ;
}
2015-11-24 13:09:49 +02:00
# ifdef CONFIG_OF
static const struct xhci_plat_priv xhci_plat_marvell_armada = {
2016-04-22 13:17:16 +03:00
. init_quirk = xhci_mvebu_mbus_init_quirk ,
2015-11-24 13:09:49 +02:00
} ;
2019-01-29 10:23:41 +01:00
static const struct xhci_plat_priv xhci_plat_marvell_armada3700 = {
. init_quirk = xhci_mvebu_a3700_init_quirk ,
} ;
2015-11-24 13:09:49 +02:00
static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen2 = {
2019-09-02 21:01:37 +09:00
SET_XHCI_PLAT_PRIV_FOR_RCAR ( XHCI_RCAR_FIRMWARE_NAME_V1 )
2015-11-24 13:09:49 +02:00
} ;
2015-11-24 13:09:53 +02:00
static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen3 = {
2019-09-02 21:01:37 +09:00
SET_XHCI_PLAT_PRIV_FOR_RCAR ( XHCI_RCAR_FIRMWARE_NAME_V3 )
2016-11-11 15:13:27 +02:00
} ;
2020-05-12 11:00:17 -04:00
static const struct xhci_plat_priv xhci_plat_brcm = {
2022-08-10 15:27:35 -07:00
. quirks = XHCI_RESET_ON_RESUME | XHCI_SUSPEND_RESUME_CLKS ,
2020-05-12 11:00:17 -04:00
} ;
2015-11-24 13:09:49 +02: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 ,
2019-01-29 10:23:41 +01:00
} , {
. compatible = " marvell,armada3700-xhci " ,
. data = & xhci_plat_marvell_armada3700 ,
2015-11-24 13:09:49 +02:00
} , {
. compatible = " renesas,xhci-r8a7790 " ,
. data = & xhci_plat_renesas_rcar_gen2 ,
} , {
. compatible = " renesas,xhci-r8a7791 " ,
. data = & xhci_plat_renesas_rcar_gen2 ,
2015-11-24 13:09:52 +02:00
} , {
. compatible = " renesas,xhci-r8a7793 " ,
. data = & xhci_plat_renesas_rcar_gen2 ,
2015-11-24 13:09:53 +02:00
} , {
. compatible = " renesas,xhci-r8a7795 " ,
. data = & xhci_plat_renesas_rcar_gen3 ,
2016-11-11 15:13:27 +02:00
} , {
. compatible = " renesas,xhci-r8a7796 " ,
2017-08-16 14:23:19 +03:00
. data = & xhci_plat_renesas_rcar_gen3 ,
2015-11-24 13:09:49 +02:00
} , {
2016-02-18 17:55:49 +02:00
. compatible = " renesas,rcar-gen2-xhci " ,
. data = & xhci_plat_renesas_rcar_gen2 ,
} , {
. compatible = " renesas,rcar-gen3-xhci " ,
. data = & xhci_plat_renesas_rcar_gen3 ,
2020-05-12 11:00:17 -04:00
} , {
. compatible = " brcm,xhci-brcm-v2 " ,
. data = & xhci_plat_brcm ,
} , {
. compatible = " brcm,bcm7445-xhci " ,
. data = & xhci_plat_brcm ,
2015-11-24 13:09:49 +02:00
} ,
2016-02-18 17:55:50 +02:00
{ } ,
2015-11-24 13:09:49 +02:00
} ;
MODULE_DEVICE_TABLE ( of , usb_xhci_of_match ) ;
# endif
2012-03-13 16:57:41 +02:00
static int xhci_plat_probe ( struct platform_device * pdev )
{
2017-10-05 11:21:49 +03:00
const struct xhci_plat_priv * priv_match ;
2012-03-13 16:57:41 +02:00
const struct hc_driver * driver ;
2018-08-31 17:24:42 +03:00
struct device * sysdev , * tmpdev ;
2012-03-13 16:57:41 +02:00
struct xhci_hcd * xhci ;
struct resource * res ;
2022-05-12 01:04:45 +03:00
struct usb_hcd * hcd , * usb3_hcd ;
2012-03-13 16:57:41 +02:00
int ret ;
int irq ;
2020-09-18 16:17:46 +03:00
struct xhci_plat_priv * priv = NULL ;
2012-03-13 16:57:41 +02:00
if ( usb_disabled ( ) )
return - ENODEV ;
2014-10-03 11:35:26 +03:00
driver = & xhci_plat_hc_driver ;
2012-03-13 16:57:41 +02:00
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 )
2017-05-17 18:32:06 +03:00
return irq ;
2012-03-13 16:57:41 +02:00
2017-03-13 10:18:44 +08:00
/*
* sysdev must point to a device that is known to the system firmware
* or PCI hardware . We handle these three cases here :
* 1. xhci_plat comes from firmware
* 2. xhci_plat is child of a device from firmware ( dwc3 - plat )
* 3. xhci_plat is grandchild of a pci device ( dwc3 - pci )
*/
2017-09-18 17:39:16 +03:00
for ( sysdev = & pdev - > dev ; sysdev ; sysdev = sysdev - > parent ) {
if ( is_of_node ( sysdev - > fwnode ) | |
is_acpi_device_node ( sysdev - > fwnode ) )
break ;
2017-03-13 10:18:44 +08:00
# ifdef CONFIG_PCI
2017-09-18 17:39:16 +03:00
else if ( sysdev - > bus = = & pci_bus_type )
break ;
2017-03-13 10:18:44 +08:00
# endif
2017-09-18 17:39:16 +03:00
}
if ( ! sysdev )
sysdev = & pdev - > dev ;
2017-03-13 10:18:44 +08:00
if ( WARN_ON ( ! sysdev - > dma_mask ) )
2015-10-09 13:30:11 +03:00
/* Platform did not initialize dma_mask */
2022-01-15 09:06:31 +01:00
ret = dma_coerce_mask_and_coherent ( sysdev , 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 05:55:19 +03:00
else
2017-03-13 10:18:44 +08:00
ret = dma_set_mask_and_coherent ( sysdev , DMA_BIT_MASK ( 64 ) ) ;
2022-01-15 09:06:31 +01:00
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 05:55:19 +03:00
2017-04-19 16:55:45 +03:00
pm_runtime_set_active ( & pdev - > dev ) ;
pm_runtime_enable ( & pdev - > dev ) ;
pm_runtime_get_noresume ( & pdev - > dev ) ;
2017-03-13 10:18:44 +08:00
hcd = __usb_create_hcd ( driver , sysdev , & pdev - > dev ,
dev_name ( & pdev - > dev ) , NULL ) ;
2017-04-19 16:55:45 +03:00
if ( ! hcd ) {
ret = - ENOMEM ;
goto disable_runtime ;
}
2012-03-13 16:57:41 +02:00
2020-03-24 00:06:09 +08:00
hcd - > regs = devm_platform_get_and_ioremap_resource ( pdev , 0 , & res ) ;
2014-06-20 23:11:23 +05:30
if ( IS_ERR ( hcd - > regs ) ) {
ret = PTR_ERR ( hcd - > regs ) ;
2012-03-13 16:57:41 +02:00
goto put_hcd ;
}
2014-11-04 07:51:27 +05:30
hcd - > rsrc_start = res - > start ;
hcd - > rsrc_len = resource_size ( res ) ;
2019-04-17 16:28:17 +08:00
xhci = hcd_to_xhci ( hcd ) ;
2022-05-12 01:04:46 +03:00
xhci - > allow_single_roothub = 1 ;
2014-05-15 12:17:32 +02:00
/*
2018-04-20 16:52:52 +03:00
* Not all platforms have clks so it is not an error if the
* clock do not exist .
2014-05-15 12:17:32 +02:00
*/
2019-04-17 16:28:17 +08:00
xhci - > reg_clk = devm_clk_get_optional ( & pdev - > dev , " reg " ) ;
if ( IS_ERR ( xhci - > reg_clk ) ) {
ret = PTR_ERR ( xhci - > reg_clk ) ;
2018-04-20 16:52:52 +03:00
goto put_hcd ;
}
2019-04-17 16:28:17 +08:00
ret = clk_prepare_enable ( xhci - > reg_clk ) ;
if ( ret )
goto put_hcd ;
xhci - > clk = devm_clk_get_optional ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( xhci - > clk ) ) {
ret = PTR_ERR ( xhci - > clk ) ;
2018-04-20 16:52:52 +03:00
goto disable_reg_clk ;
2014-05-15 12:17:32 +02:00
}
2019-04-17 16:28:17 +08:00
ret = clk_prepare_enable ( xhci - > clk ) ;
if ( ret )
goto disable_reg_clk ;
2020-09-18 16:17:43 +03:00
if ( pdev - > dev . of_node )
priv_match = of_device_get_match_data ( & pdev - > dev ) ;
else
priv_match = dev_get_platdata ( & pdev - > dev ) ;
2017-10-05 11:21:49 +03:00
if ( priv_match ) {
2020-09-18 16:17:46 +03:00
priv = hcd_to_xhci_priv ( hcd ) ;
2015-11-24 13:09:49 +02:00
/* Just copy data for now */
2020-09-18 16:17:45 +03:00
* priv = * priv_match ;
2015-11-24 13:09:49 +02:00
}
2020-09-18 16:17:47 +03:00
device_set_wakeup_capable ( & pdev - > dev , true ) ;
2012-03-13 16:57:41 +02:00
2015-05-29 17:01:50 +03:00
xhci - > main_hcd = hcd ;
2012-03-13 16:57:41 +02:00
2018-08-31 17:24:42 +03:00
/* imod_interval is the interrupt moderation value in nanoseconds. */
xhci - > imod_interval = 40000 ;
2017-10-05 11:21:37 +03:00
2018-08-31 17:24:42 +03:00
/* Iterate over all parent nodes for finding quirks */
for ( tmpdev = & pdev - > dev ; tmpdev ; tmpdev = tmpdev - > parent ) {
2012-03-13 16:57:41 +02:00
2018-08-31 17:24:42 +03:00
if ( device_property_read_bool ( tmpdev , " usb2-lpm-disable " ) )
xhci - > quirks | = XHCI_HW_LPM_DISABLE ;
2017-01-23 14:19:59 +02:00
2018-08-31 17:24:42 +03:00
if ( device_property_read_bool ( tmpdev , " usb3-lpm-capable " ) )
xhci - > quirks | = XHCI_LPM_SUPPORT ;
if ( device_property_read_bool ( tmpdev , " quirk-broken-port-ped " ) )
xhci - > quirks | = XHCI_BROKEN_PORT_PED ;
device_property_read_u32 ( tmpdev , " imod-interval-ns " ,
& xhci - > imod_interval ) ;
}
2017-12-08 17:59:13 +02:00
2017-03-13 10:18:44 +08:00
hcd - > usb_phy = devm_usb_get_phy_by_phandle ( sysdev , " usb-phy " , 0 ) ;
2015-03-17 18:32:22 +02:00
if ( IS_ERR ( hcd - > usb_phy ) ) {
ret = PTR_ERR ( hcd - > usb_phy ) ;
if ( ret = = - EPROBE_DEFER )
2022-05-12 01:04:44 +03:00
goto disable_clk ;
2015-03-17 18:32:22 +02:00
hcd - > usb_phy = NULL ;
} else {
ret = usb_phy_init ( hcd - > usb_phy ) ;
if ( ret )
2022-05-12 01:04:44 +03:00
goto disable_clk ;
2015-03-17 18:32:22 +02:00
}
2018-09-20 19:13:36 +03:00
hcd - > tpl_support = of_usb_host_tpl_support ( sysdev - > of_node ) ;
2022-05-12 01:04:44 +03:00
2022-02-03 22:44:43 +01:00
if ( priv & & ( priv - > quirks & XHCI_SKIP_PHY_INIT ) )
2020-09-18 16:17:46 +03:00
hcd - > skip_phy_initialization = 1 ;
2020-12-08 11:29:08 +02:00
if ( priv & & ( priv - > quirks & XHCI_SG_TRB_CACHE_SIZE_QUIRK ) )
xhci - > quirks | = XHCI_SG_TRB_CACHE_SIZE_QUIRK ;
2015-05-29 17:01:47 +03:00
ret = usb_add_hcd ( hcd , irq , IRQF_SHARED ) ;
2012-03-13 16:57:41 +02:00
if ( ret )
2015-03-17 18:32:22 +02:00
goto disable_usb_phy ;
2012-03-13 16:57:41 +02:00
2022-05-12 01:04:45 +03:00
if ( ! xhci_has_one_roothub ( xhci ) ) {
xhci - > shared_hcd = __usb_create_hcd ( driver , sysdev , & pdev - > dev ,
dev_name ( & pdev - > dev ) , hcd ) ;
if ( ! xhci - > shared_hcd ) {
ret = - ENOMEM ;
goto dealloc_usb2_hcd ;
}
2022-05-12 01:04:44 +03:00
2022-05-12 01:04:45 +03:00
xhci - > shared_hcd - > tpl_support = hcd - > tpl_support ;
}
2022-05-12 01:04:44 +03:00
2022-05-12 01:04:45 +03:00
usb3_hcd = xhci_get_usb3_hcd ( xhci ) ;
if ( usb3_hcd & & HCC_MAX_PSA ( xhci - > hcc_params ) > = 4 )
usb3_hcd - > can_do_streams = 1 ;
2017-01-17 15:32:07 +08:00
2022-05-12 01:04:45 +03:00
if ( xhci - > shared_hcd ) {
ret = usb_add_hcd ( xhci - > shared_hcd , irq , IRQF_SHARED ) ;
if ( ret )
goto put_usb3_hcd ;
}
2015-05-29 17:01:47 +03:00
2017-04-07 17:56:49 +03:00
device_enable_async_suspend ( & pdev - > dev ) ;
2017-04-19 16:55:45 +03:00
pm_runtime_put_noidle ( & pdev - > dev ) ;
/*
* Prevent runtime pm from being on as default , users should enable
* runtime pm using power / control in sysfs .
*/
pm_runtime_forbid ( & pdev - > dev ) ;
2017-04-07 17:56:49 +03:00
2012-03-13 16:57:41 +02:00
return 0 ;
2015-05-29 17:01:47 +03:00
2022-05-12 01:04:44 +03:00
put_usb3_hcd :
usb_put_hcd ( xhci - > shared_hcd ) ;
2015-05-29 17:01:47 +03:00
dealloc_usb2_hcd :
usb_remove_hcd ( hcd ) ;
2015-03-17 18:32:22 +02:00
disable_usb_phy :
usb_phy_shutdown ( hcd - > usb_phy ) ;
2014-05-15 12:17:32 +02:00
disable_clk :
2019-04-17 16:28:17 +08:00
clk_disable_unprepare ( xhci - > clk ) ;
2014-05-15 12:17:32 +02:00
2018-04-20 16:52:52 +03:00
disable_reg_clk :
2019-04-17 16:28:17 +08:00
clk_disable_unprepare ( xhci - > reg_clk ) ;
2018-04-20 16:52:52 +03:00
2012-03-13 16:57:41 +02:00
put_hcd :
usb_put_hcd ( hcd ) ;
2017-04-19 16:55:45 +03:00
disable_runtime :
pm_runtime_put_noidle ( & pdev - > dev ) ;
pm_runtime_disable ( & pdev - > dev ) ;
2012-03-13 16:57:41 +02:00
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 12:17:32 +02:00
struct clk * clk = xhci - > clk ;
2018-04-20 16:52:52 +03:00
struct clk * reg_clk = xhci - > reg_clk ;
2018-11-09 17:21:17 +02:00
struct usb_hcd * shared_hcd = xhci - > shared_hcd ;
2012-03-13 16:57:41 +02:00
2020-05-14 14:04:32 +03:00
pm_runtime_get_sync ( & dev - > dev ) ;
2017-03-09 15:39:37 +02:00
xhci - > xhc_state | = XHCI_STATE_REMOVING ;
2022-08-25 18:08:38 +03:00
if ( shared_hcd ) {
usb_remove_hcd ( shared_hcd ) ;
xhci - > shared_hcd = NULL ;
}
2015-03-17 18:32:22 +02:00
usb_phy_shutdown ( hcd - > usb_phy ) ;
2012-03-13 16:57:41 +02:00
usb_remove_hcd ( hcd ) ;
2022-08-25 18:08:38 +03:00
if ( shared_hcd )
usb_put_hcd ( shared_hcd ) ;
2015-05-29 17:01:47 +03:00
2018-04-20 16:52:51 +03:00
clk_disable_unprepare ( clk ) ;
2018-04-20 16:52:52 +03:00
clk_disable_unprepare ( reg_clk ) ;
2012-03-13 16:57:41 +02:00
usb_put_hcd ( hcd ) ;
2017-04-19 16:55:45 +03:00
pm_runtime_disable ( & dev - > dev ) ;
2020-05-14 14:04:32 +03:00
pm_runtime_put_noidle ( & dev - > dev ) ;
pm_runtime_set_suspended ( & dev - > dev ) ;
2017-04-19 16:55:45 +03:00
2012-03-13 16:57:41 +02:00
return 0 ;
}
2017-04-21 23:42:54 +02:00
static int __maybe_unused xhci_plat_suspend ( struct device * dev )
2013-02-11 12:58:00 +02:00
{
struct usb_hcd * hcd = dev_get_drvdata ( dev ) ;
struct xhci_hcd * xhci = hcd_to_xhci ( hcd ) ;
2020-09-18 16:17:44 +03:00
int ret ;
2013-02-11 12:58:00 +02:00
2022-01-10 11:27:38 -06:00
if ( pm_runtime_suspended ( dev ) )
pm_runtime_resume ( dev ) ;
2020-09-18 16:17:44 +03:00
ret = xhci_priv_suspend_quirk ( hcd ) ;
if ( ret )
return ret ;
2014-11-18 11:27:14 +02:00
/*
* xhci_suspend ( ) needs ` do_wakeup ` to know whether host is allowed
2020-09-18 16:17:48 +03:00
* to do wakeup during suspend .
2014-11-18 11:27:14 +02:00
*/
2022-08-10 15:27:34 -07:00
ret = xhci_suspend ( xhci , device_may_wakeup ( dev ) ) ;
if ( ret )
return ret ;
if ( ! device_may_wakeup ( dev ) & & ( xhci - > quirks & XHCI_SUSPEND_RESUME_CLKS ) ) {
clk_disable_unprepare ( xhci - > clk ) ;
clk_disable_unprepare ( xhci - > reg_clk ) ;
}
return 0 ;
2013-02-11 12:58:00 +02:00
}
2017-04-21 23:42:54 +02:00
static int __maybe_unused xhci_plat_resume ( struct device * dev )
2013-02-11 12:58:00 +02:00
{
struct usb_hcd * hcd = dev_get_drvdata ( dev ) ;
struct xhci_hcd * xhci = hcd_to_xhci ( hcd ) ;
2017-04-19 16:55:47 +03:00
int ret ;
2013-02-11 12:58:00 +02:00
2022-08-10 15:27:34 -07:00
if ( ! device_may_wakeup ( dev ) & & ( xhci - > quirks & XHCI_SUSPEND_RESUME_CLKS ) ) {
clk_prepare_enable ( xhci - > clk ) ;
clk_prepare_enable ( xhci - > reg_clk ) ;
}
2017-04-19 16:55:47 +03:00
ret = xhci_priv_resume_quirk ( hcd ) ;
if ( ret )
return ret ;
2020-05-18 16:49:30 +01:00
ret = xhci_resume ( xhci , 0 ) ;
if ( ret )
return ret ;
pm_runtime_disable ( dev ) ;
pm_runtime_set_active ( dev ) ;
pm_runtime_enable ( dev ) ;
return 0 ;
2013-02-11 12:58:00 +02:00
}
2017-04-19 16:55:45 +03:00
2017-04-21 23:42:54 +02:00
static int __maybe_unused xhci_plat_runtime_suspend ( struct device * dev )
2017-04-19 16:55:45 +03:00
{
struct usb_hcd * hcd = dev_get_drvdata ( dev ) ;
struct xhci_hcd * xhci = hcd_to_xhci ( hcd ) ;
2020-09-18 16:17:44 +03:00
int ret ;
ret = xhci_priv_suspend_quirk ( hcd ) ;
if ( ret )
return ret ;
2017-04-19 16:55:45 +03:00
return xhci_suspend ( xhci , true ) ;
}
2017-04-21 23:42:54 +02:00
static int __maybe_unused xhci_plat_runtime_resume ( struct device * dev )
2017-04-19 16:55:45 +03:00
{
struct usb_hcd * hcd = dev_get_drvdata ( dev ) ;
struct xhci_hcd * xhci = hcd_to_xhci ( hcd ) ;
return xhci_resume ( xhci , 0 ) ;
}
2013-02-11 12:58:00 +02:00
static const struct dev_pm_ops xhci_plat_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( xhci_plat_suspend , xhci_plat_resume )
2017-04-19 16:55:45 +03:00
SET_RUNTIME_PM_OPS ( xhci_plat_runtime_suspend ,
xhci_plat_runtime_resume ,
NULL )
2013-02-11 12:58:00 +02:00
} ;
2020-07-06 14:33:41 +01:00
# ifdef CONFIG_ACPI
2015-10-09 13:30:12 +03:00
static const struct acpi_device_id usb_xhci_acpi_match [ ] = {
/* XHCI-compliant USB Controller */
{ " PNP0D10 " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( acpi , usb_xhci_acpi_match ) ;
2020-07-06 14:33:41 +01:00
# endif
2015-10-09 13:30:12 +03:00
2012-03-13 16:57:41 +02:00
static struct platform_driver usb_xhci_driver = {
. probe = xhci_plat_probe ,
. remove = xhci_plat_remove ,
2020-03-06 17:23:28 +08:00
. shutdown = usb_hcd_platform_shutdown ,
2012-03-13 16:57:41 +02:00
. driver = {
. name = " xhci-hcd " ,
2017-04-19 16:55:45 +03:00
. pm = & xhci_plat_pm_ops ,
2013-07-25 19:04:44 -04:00
. of_match_table = of_match_ptr ( usb_xhci_of_match ) ,
2015-10-09 13:30:12 +03:00
. acpi_match_table = ACPI_PTR ( usb_xhci_acpi_match ) ,
2012-03-13 16:57:41 +02:00
} ,
} ;
MODULE_ALIAS ( " platform:xhci-hcd " ) ;
2014-10-03 11:35:29 +03:00
static int __init xhci_plat_init ( void )
2012-03-13 16:57:41 +02:00
{
2015-05-29 17:01:46 +03:00
xhci_init_driver ( & xhci_plat_hc_driver , & xhci_plat_overrides ) ;
2012-03-13 16:57:41 +02:00
return platform_driver_register ( & usb_xhci_driver ) ;
}
2014-10-03 11:35:29 +03:00
module_init ( xhci_plat_init ) ;
2012-03-13 16:57:41 +02:00
2014-10-03 11:35:29 +03:00
static void __exit xhci_plat_exit ( void )
2012-03-13 16:57:41 +02:00
{
platform_driver_unregister ( & usb_xhci_driver ) ;
}
2014-10-03 11:35:29 +03:00
module_exit ( xhci_plat_exit ) ;
MODULE_DESCRIPTION ( " xHCI Platform Host Controller Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;