2017-11-03 11:28:30 +01:00
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
2013-04-22 14:00:19 -07:00
/*
* platform . c - DesignWare HS OTG Controller platform driver
*
* Copyright ( C ) Matthijs Kooijman < matthijs @ stdin . nl >
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions , and the following disclaimer ,
* without modification .
* 2. Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
* 3. The names of the above - listed copyright holders may not be used
* to endorse or promote products derived from this software without
* specific prior written permission .
*
* ALTERNATIVELY , this software may be distributed under the terms of the
* GNU General Public License ( " GPL " ) as published by the Free Software
* Foundation ; either version 2 of the License , or ( at your option ) any
* later version .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS
* IS " AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL ,
* EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED TO ,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR
* PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING
* NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/slab.h>
2015-10-14 08:52:29 +02:00
# include <linux/clk.h>
2013-04-22 14:00:19 -07:00
# include <linux/device.h>
# include <linux/dma-mapping.h>
2013-11-26 18:58:01 -07:00
# include <linux/of_device.h>
2014-11-21 15:14:48 +01:00
# include <linux/mutex.h>
2013-04-22 14:00:19 -07:00
# include <linux/platform_device.h>
2015-10-14 08:52:29 +02:00
# include <linux/phy/phy.h>
# include <linux/platform_data/s3c-hsotg.h>
2016-08-10 08:53:34 -05:00
# include <linux/reset.h>
2013-04-22 14:00:19 -07:00
2014-08-06 09:01:50 +08:00
# include <linux/usb/of.h>
2013-04-22 14:00:19 -07:00
# include "core.h"
# include "hcd.h"
2015-04-29 22:08:59 +02:00
# include "debug.h"
2013-04-22 14:00:19 -07:00
static const char dwc2_driver_name [ ] = " dwc2 " ;
2015-12-17 11:16:31 -08:00
/*
* Check the dr_mode against the module configuration and hardware
* capabilities .
*
* The hardware , module , and dr_mode , can each be set to host , device ,
* or otg . Check that all these values are compatible and adjust the
* value of dr_mode if possible .
*
* actual
* HW MOD dr_mode dr_mode
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* HST HST any : HST
* HST DEV any : - - -
* HST OTG any : HST
*
* DEV HST any : - - -
* DEV DEV any : DEV
* DEV OTG any : DEV
*
* OTG HST any : HST
* OTG DEV any : DEV
* OTG OTG any : dr_mode
*/
static int dwc2_get_dr_mode ( struct dwc2_hsotg * hsotg )
{
enum usb_dr_mode mode ;
hsotg - > dr_mode = usb_get_dr_mode ( hsotg - > dev ) ;
if ( hsotg - > dr_mode = = USB_DR_MODE_UNKNOWN )
hsotg - > dr_mode = USB_DR_MODE_OTG ;
mode = hsotg - > dr_mode ;
if ( dwc2_hw_is_device ( hsotg ) ) {
if ( IS_ENABLED ( CONFIG_USB_DWC2_HOST ) ) {
dev_err ( hsotg - > dev ,
" Controller does not support host mode. \n " ) ;
return - EINVAL ;
}
mode = USB_DR_MODE_PERIPHERAL ;
} else if ( dwc2_hw_is_host ( hsotg ) ) {
if ( IS_ENABLED ( CONFIG_USB_DWC2_PERIPHERAL ) ) {
dev_err ( hsotg - > dev ,
" Controller does not support device mode. \n " ) ;
return - EINVAL ;
}
mode = USB_DR_MODE_HOST ;
} else {
if ( IS_ENABLED ( CONFIG_USB_DWC2_HOST ) )
mode = USB_DR_MODE_HOST ;
else if ( IS_ENABLED ( CONFIG_USB_DWC2_PERIPHERAL ) )
mode = USB_DR_MODE_PERIPHERAL ;
}
if ( mode ! = hsotg - > dr_mode ) {
dev_warn ( hsotg - > dev ,
2017-01-17 20:30:27 -08:00
" Configuration mismatch. dr_mode forced to %s \n " ,
2015-12-17 11:16:31 -08:00
mode = = USB_DR_MODE_HOST ? " host " : " device " ) ;
hsotg - > dr_mode = mode ;
}
return 0 ;
}
2020-09-14 14:06:34 +01:00
static void __dwc2_disable_regulators ( void * data )
{
struct dwc2_hsotg * hsotg = data ;
regulator_bulk_disable ( ARRAY_SIZE ( hsotg - > supplies ) , hsotg - > supplies ) ;
}
2015-10-14 08:52:29 +02:00
static int __dwc2_lowlevel_hw_enable ( struct dwc2_hsotg * hsotg )
{
struct platform_device * pdev = to_platform_device ( hsotg - > dev ) ;
int ret ;
ret = regulator_bulk_enable ( ARRAY_SIZE ( hsotg - > supplies ) ,
hsotg - > supplies ) ;
if ( ret )
return ret ;
2020-09-14 14:06:34 +01:00
ret = devm_add_action_or_reset ( & pdev - > dev ,
__dwc2_disable_regulators , hsotg ) ;
if ( ret )
return ret ;
2015-11-13 17:02:12 +00:00
if ( hsotg - > clk ) {
ret = clk_prepare_enable ( hsotg - > clk ) ;
if ( ret )
return ret ;
}
2015-10-14 08:52:29 +02:00
2017-01-17 20:31:43 -08:00
if ( hsotg - > uphy ) {
2015-10-14 08:52:29 +02:00
ret = usb_phy_init ( hsotg - > uphy ) ;
2017-01-17 20:31:43 -08:00
} else if ( hsotg - > plat & & hsotg - > plat - > phy_init ) {
2015-10-14 08:52:29 +02:00
ret = hsotg - > plat - > phy_init ( pdev , hsotg - > plat - > phy_type ) ;
2017-01-17 20:31:43 -08:00
} else {
2015-10-14 08:52:29 +02:00
ret = phy_power_on ( hsotg - > phy ) ;
if ( ret = = 0 )
ret = phy_init ( hsotg - > phy ) ;
}
return ret ;
}
/**
* dwc2_lowlevel_hw_enable - enable platform lowlevel hw resources
* @ hsotg : The driver state
*
* A wrapper for platform code responsible for controlling
* low - level USB platform resources ( phy , clock , regulators )
*/
int dwc2_lowlevel_hw_enable ( struct dwc2_hsotg * hsotg )
{
int ret = __dwc2_lowlevel_hw_enable ( hsotg ) ;
if ( ret = = 0 )
hsotg - > ll_hw_enabled = true ;
return ret ;
}
static int __dwc2_lowlevel_hw_disable ( struct dwc2_hsotg * hsotg )
{
struct platform_device * pdev = to_platform_device ( hsotg - > dev ) ;
int ret = 0 ;
2017-01-17 20:31:43 -08:00
if ( hsotg - > uphy ) {
2015-10-14 08:52:29 +02:00
usb_phy_shutdown ( hsotg - > uphy ) ;
2017-01-17 20:31:43 -08:00
} else if ( hsotg - > plat & & hsotg - > plat - > phy_exit ) {
2015-10-14 08:52:29 +02:00
ret = hsotg - > plat - > phy_exit ( pdev , hsotg - > plat - > phy_type ) ;
2017-01-17 20:31:43 -08:00
} else {
2015-10-14 08:52:29 +02:00
ret = phy_exit ( hsotg - > phy ) ;
if ( ret = = 0 )
ret = phy_power_off ( hsotg - > phy ) ;
}
if ( ret )
return ret ;
2015-11-13 17:02:12 +00:00
if ( hsotg - > clk )
clk_disable_unprepare ( hsotg - > clk ) ;
2015-10-14 08:52:29 +02:00
2020-09-14 14:06:34 +01:00
return 0 ;
2015-10-14 08:52:29 +02:00
}
/**
* dwc2_lowlevel_hw_disable - disable platform lowlevel hw resources
* @ hsotg : The driver state
*
* A wrapper for platform code responsible for controlling
* low - level USB platform resources ( phy , clock , regulators )
*/
int dwc2_lowlevel_hw_disable ( struct dwc2_hsotg * hsotg )
{
int ret = __dwc2_lowlevel_hw_disable ( hsotg ) ;
if ( ret = = 0 )
hsotg - > ll_hw_enabled = false ;
return ret ;
}
static int dwc2_lowlevel_hw_init ( struct dwc2_hsotg * hsotg )
{
int i , ret ;
2016-08-10 08:53:34 -05:00
hsotg - > reset = devm_reset_control_get_optional ( hsotg - > dev , " dwc2 " ) ;
if ( IS_ERR ( hsotg - > reset ) ) {
ret = PTR_ERR ( hsotg - > reset ) ;
2017-03-15 12:31:53 +01:00
dev_err ( hsotg - > dev , " error getting reset control %d \n " , ret ) ;
return ret ;
2016-08-10 08:53:34 -05:00
}
2017-03-15 12:31:53 +01:00
reset_control_deassert ( hsotg - > reset ) ;
2016-08-10 08:53:34 -05:00
2017-11-01 10:34:53 -05:00
hsotg - > reset_ecc = devm_reset_control_get_optional ( hsotg - > dev , " dwc2-ecc " ) ;
if ( IS_ERR ( hsotg - > reset_ecc ) ) {
ret = PTR_ERR ( hsotg - > reset_ecc ) ;
dev_err ( hsotg - > dev , " error getting reset control for ecc %d \n " , ret ) ;
return ret ;
}
reset_control_deassert ( hsotg - > reset_ecc ) ;
2015-10-14 08:52:29 +02:00
/*
* Attempt to find a generic PHY , then look for an old style
* USB PHY and then fall back to pdata
*/
hsotg - > phy = devm_phy_get ( hsotg - > dev , " usb2-phy " ) ;
if ( IS_ERR ( hsotg - > phy ) ) {
2015-11-13 17:02:11 +00:00
ret = PTR_ERR ( hsotg - > phy ) ;
switch ( ret ) {
case - ENODEV :
case - ENOSYS :
hsotg - > phy = NULL ;
break ;
case - EPROBE_DEFER :
return ret ;
default :
dev_err ( hsotg - > dev , " error getting phy %d \n " , ret ) ;
return ret ;
}
}
if ( ! hsotg - > phy ) {
2015-10-14 08:52:29 +02:00
hsotg - > uphy = devm_usb_get_phy ( hsotg - > dev , USB_PHY_TYPE_USB2 ) ;
2015-11-13 17:02:11 +00:00
if ( IS_ERR ( hsotg - > uphy ) ) {
ret = PTR_ERR ( hsotg - > uphy ) ;
switch ( ret ) {
case - ENODEV :
case - ENXIO :
hsotg - > uphy = NULL ;
break ;
case - EPROBE_DEFER :
return ret ;
default :
dev_err ( hsotg - > dev , " error getting usb phy %d \n " ,
ret ) ;
return ret ;
}
}
2015-10-14 08:52:29 +02:00
}
2015-11-13 17:02:11 +00:00
hsotg - > plat = dev_get_platdata ( hsotg - > dev ) ;
2015-10-14 08:52:29 +02:00
/* Clock */
2019-04-17 16:28:19 +08:00
hsotg - > clk = devm_clk_get_optional ( hsotg - > dev , " otg " ) ;
2015-10-14 08:52:29 +02:00
if ( IS_ERR ( hsotg - > clk ) ) {
2019-04-17 16:28:19 +08:00
dev_err ( hsotg - > dev , " cannot get otg clock \n " ) ;
return PTR_ERR ( hsotg - > clk ) ;
2015-10-14 08:52:29 +02:00
}
/* Regulators */
for ( i = 0 ; i < ARRAY_SIZE ( hsotg - > supplies ) ; i + + )
hsotg - > supplies [ i ] . supply = dwc2_hsotg_supply_names [ i ] ;
ret = devm_regulator_bulk_get ( hsotg - > dev , ARRAY_SIZE ( hsotg - > supplies ) ,
hsotg - > supplies ) ;
if ( ret ) {
2020-02-28 10:25:57 +01:00
if ( ret ! = - EPROBE_DEFER )
dev_err ( hsotg - > dev , " failed to request supplies: %d \n " ,
ret ) ;
2015-10-14 08:52:29 +02:00
return ret ;
}
return 0 ;
}
2013-04-22 14:00:19 -07:00
/**
* dwc2_driver_remove ( ) - Called when the DWC_otg core is unregistered with the
* DWC_otg driver
*
* @ dev : Platform device
*
* This routine is called , for example , when the rmmod command is executed . The
* device may or may not be electrically present . If it is present , the driver
* stops device processing . Any resources used on behalf of this device are
* freed .
*/
static int dwc2_driver_remove ( struct platform_device * dev )
{
struct dwc2_hsotg * hsotg = platform_get_drvdata ( dev ) ;
2021-04-08 13:46:14 +04:00
int ret = 0 ;
/* Exit Partial Power Down when driver is removed. */
if ( hsotg - > in_ppd ) {
ret = dwc2_exit_partial_power_down ( hsotg , 0 , true ) ;
if ( ret )
dev_err ( hsotg - > dev ,
" exit partial_power_down failed \n " ) ;
}
2013-04-22 14:00:19 -07:00
2015-04-29 22:08:59 +02:00
dwc2_debugfs_exit ( hsotg ) ;
2015-03-10 13:41:10 +01:00
if ( hsotg - > hcd_enabled )
dwc2_hcd_remove ( hsotg ) ;
if ( hsotg - > gadget_enabled )
2015-08-06 18:11:54 -05:00
dwc2_hsotg_remove ( hsotg ) ;
2013-04-22 14:00:19 -07:00
2020-09-09 11:35:10 +02:00
dwc2_drd_exit ( hsotg ) ;
2020-01-24 09:41:31 +01:00
if ( hsotg - > params . activate_stm_id_vb_detection )
regulator_disable ( hsotg - > usb33d ) ;
2015-10-14 08:52:29 +02:00
if ( hsotg - > ll_hw_enabled )
dwc2_lowlevel_hw_disable ( hsotg ) ;
2017-03-15 12:31:53 +01:00
reset_control_assert ( hsotg - > reset ) ;
2017-11-01 10:34:53 -05:00
reset_control_assert ( hsotg - > reset_ecc ) ;
2016-08-10 08:53:34 -05:00
2021-04-08 13:46:14 +04:00
return ret ;
2013-04-22 14:00:19 -07:00
}
2015-12-18 19:30:59 +01:00
/**
* dwc2_driver_shutdown ( ) - Called on device shutdown
*
* @ dev : Platform device
*
* In specific conditions ( involving usb hubs ) dwc2 devices can create a
* lot of interrupts , even to the point of overwhelming devices running
* at low frequencies . Some devices need to do special clock handling
* at shutdown - time which may bring the system clock below the threshold
* of being able to handle the dwc2 interrupts . Disabling dwc2 - irqs
* prevents reboots / poweroffs from getting stuck in such cases .
*/
static void dwc2_driver_shutdown ( struct platform_device * dev )
{
struct dwc2_hsotg * hsotg = platform_get_drvdata ( dev ) ;
2020-05-30 11:41:50 +04:00
dwc2_disable_global_interrupts ( hsotg ) ;
synchronize_irq ( hsotg - > irq ) ;
2015-12-18 19:30:59 +01:00
}
2018-07-27 12:26:29 +04:00
/**
* dwc2_check_core_endianness ( ) - Returns true if core and AHB have
* opposite endianness .
* @ hsotg : Programming view of the DWC_otg controller .
*/
static bool dwc2_check_core_endianness ( struct dwc2_hsotg * hsotg )
{
u32 snpsid ;
snpsid = ioread32 ( hsotg - > regs + GSNPSID ) ;
if ( ( snpsid & GSNPSID_ID_MASK ) = = DWC2_OTG_ID | |
( snpsid & GSNPSID_ID_MASK ) = = DWC2_FS_IOT_ID | |
( snpsid & GSNPSID_ID_MASK ) = = DWC2_HS_IOT_ID )
return false ;
return true ;
}
2020-05-21 10:05:44 +04:00
/**
* Check core version
*
* @ hsotg : Programming view of the DWC_otg controller
*
*/
int dwc2_check_core_version ( struct dwc2_hsotg * hsotg )
{
struct dwc2_hw_params * hw = & hsotg - > hw_params ;
/*
* Attempt to ensure this device is really a DWC_otg Controller .
* Read and verify the GSNPSID register contents . The value should be
* 0x45f4 xxxx , 0x5531 xxxx or 0x5532 xxxx
*/
hw - > snpsid = dwc2_readl ( hsotg , GSNPSID ) ;
if ( ( hw - > snpsid & GSNPSID_ID_MASK ) ! = DWC2_OTG_ID & &
( hw - > snpsid & GSNPSID_ID_MASK ) ! = DWC2_FS_IOT_ID & &
( hw - > snpsid & GSNPSID_ID_MASK ) ! = DWC2_HS_IOT_ID ) {
dev_err ( hsotg - > dev , " Bad value for GSNPSID: 0x%08x \n " ,
hw - > snpsid ) ;
return - ENODEV ;
}
dev_dbg ( hsotg - > dev , " Core Release: %1x.%1x%1x%1x (snpsid=%x) \n " ,
hw - > snpsid > > 12 & 0xf , hw - > snpsid > > 8 & 0xf ,
hw - > snpsid > > 4 & 0xf , hw - > snpsid & 0xf , hw - > snpsid ) ;
return 0 ;
}
2013-04-22 14:00:19 -07:00
/**
* dwc2_driver_probe ( ) - Called when the DWC_otg core is bound to the DWC_otg
* driver
*
* @ dev : Platform device
*
* This routine creates the driver components required to control the device
* ( core , HCD , and PCD ) and initializes the device . The driver components are
* stored in a dwc2_hsotg structure . A reference to the dwc2_hsotg is saved
* in the device private data . This allows the driver to access the dwc2_hsotg
* structure on subsequent calls to driver methods for this device .
*/
static int dwc2_driver_probe ( struct platform_device * dev )
{
struct dwc2_hsotg * hsotg ;
struct resource * res ;
int retval ;
hsotg = devm_kzalloc ( & dev - > dev , sizeof ( * hsotg ) , GFP_KERNEL ) ;
if ( ! hsotg )
return - ENOMEM ;
hsotg - > dev = & dev - > dev ;
2013-05-17 10:52:55 +02:00
/*
* Use reasonable defaults so platforms don ' t have to provide these .
*/
if ( ! dev - > dev . dma_mask )
dev - > dev . dma_mask = & dev - > dev . coherent_dma_mask ;
2013-06-10 16:56:16 +01:00
retval = dma_set_coherent_mask ( & dev - > dev , DMA_BIT_MASK ( 32 ) ) ;
2018-02-12 21:20:08 +01:00
if ( retval ) {
dev_err ( & dev - > dev , " can't set coherent DMA mask: %d \n " , retval ) ;
2013-06-10 16:56:16 +01:00
return retval ;
2018-02-12 21:20:08 +01:00
}
2013-05-17 10:52:55 +02:00
2020-03-24 00:06:11 +08:00
hsotg - > regs = devm_platform_get_and_ioremap_resource ( dev , 0 , & res ) ;
2013-04-22 14:00:19 -07:00
if ( IS_ERR ( hsotg - > regs ) )
return PTR_ERR ( hsotg - > regs ) ;
dev_dbg ( & dev - > dev , " mapped PA %08lx to VA %p \n " ,
( unsigned long ) res - > start , hsotg - > regs ) ;
2015-10-14 08:52:29 +02:00
retval = dwc2_lowlevel_hw_init ( hsotg ) ;
2015-04-29 22:09:05 +02:00
if ( retval )
return retval ;
2015-10-14 08:52:29 +02:00
spin_lock_init ( & hsotg - > lock ) ;
2015-12-18 19:30:59 +01:00
hsotg - > irq = platform_get_irq ( dev , 0 ) ;
2019-07-30 11:15:46 -07:00
if ( hsotg - > irq < 0 )
2015-12-18 19:30:59 +01:00
return hsotg - > irq ;
2015-11-12 21:08:34 +00:00
dev_dbg ( hsotg - > dev , " registering common handler for irq%d \n " ,
2015-12-18 19:30:59 +01:00
hsotg - > irq ) ;
retval = devm_request_irq ( hsotg - > dev , hsotg - > irq ,
2015-11-12 21:08:34 +00:00
dwc2_handle_common_intr , IRQF_SHARED ,
dev_name ( hsotg - > dev ) , hsotg ) ;
if ( retval )
return retval ;
2018-09-05 13:40:02 +02:00
hsotg - > vbus_supply = devm_regulator_get_optional ( hsotg - > dev , " vbus " ) ;
if ( IS_ERR ( hsotg - > vbus_supply ) ) {
retval = PTR_ERR ( hsotg - > vbus_supply ) ;
hsotg - > vbus_supply = NULL ;
if ( retval ! = - ENODEV )
return retval ;
}
2015-10-14 08:52:29 +02:00
retval = dwc2_lowlevel_hw_enable ( hsotg ) ;
if ( retval )
return retval ;
2018-08-27 18:36:38 -03:00
hsotg - > needs_byte_swap = dwc2_check_core_endianness ( hsotg ) ;
2015-12-17 11:16:31 -08:00
retval = dwc2_get_dr_mode ( hsotg ) ;
if ( retval )
2016-04-27 20:20:56 -07:00
goto error ;
2015-12-17 11:16:31 -08:00
2019-05-20 10:56:04 -07:00
hsotg - > need_phy_for_wake =
of_property_read_bool ( dev - > dev . of_node ,
" snps,need-phy-for-wake " ) ;
2020-05-21 10:05:44 +04:00
/*
* Before performing any core related operations
* check core version .
*/
retval = dwc2_check_core_version ( hsotg ) ;
if ( retval )
goto error ;
2016-01-11 16:32:14 -08:00
/*
* Reset before dwc2_get_hwparams ( ) then it could get power - on real
* reset value form registers .
*/
2018-02-16 12:56:03 +04:00
retval = dwc2_core_reset ( hsotg , false ) ;
if ( retval )
goto error ;
2016-01-11 16:32:14 -08:00
/* Detect config values from hardware */
2015-10-14 08:52:29 +02:00
retval = dwc2_get_hwparams ( hsotg ) ;
if ( retval )
goto error ;
2018-02-16 12:56:03 +04:00
/*
* For OTG cores , set the force mode bits to reflect the value
* of dr_mode . Force mode bits should not be touched at any
* other time after this .
*/
2015-12-17 11:18:27 -08:00
dwc2_force_dr_mode ( hsotg ) ;
2015-12-17 11:16:58 -08:00
2016-11-03 17:55:55 -07:00
retval = dwc2_init_params ( hsotg ) ;
if ( retval )
goto error ;
2020-01-24 09:41:31 +01:00
if ( hsotg - > params . activate_stm_id_vb_detection ) {
u32 ggpio ;
hsotg - > usb33d = devm_regulator_get ( hsotg - > dev , " usb33d " ) ;
if ( IS_ERR ( hsotg - > usb33d ) ) {
retval = PTR_ERR ( hsotg - > usb33d ) ;
if ( retval ! = - EPROBE_DEFER )
dev_err ( hsotg - > dev ,
" failed to request usb33d supply: %d \n " ,
retval ) ;
goto error ;
}
retval = regulator_enable ( hsotg - > usb33d ) ;
if ( retval ) {
dev_err ( hsotg - > dev ,
" failed to enable usb33d supply: %d \n " , retval ) ;
goto error ;
}
ggpio = dwc2_readl ( hsotg , GGPIO ) ;
ggpio | = GGPIO_STM32_OTG_GCCFG_IDEN ;
ggpio | = GGPIO_STM32_OTG_GCCFG_VBDEN ;
dwc2_writel ( hsotg , ggpio , GGPIO ) ;
}
2020-09-09 11:35:10 +02:00
retval = dwc2_drd_init ( hsotg ) ;
if ( retval ) {
if ( retval ! = - EPROBE_DEFER )
dev_err ( hsotg - > dev , " failed to initialize dual-role \n " ) ;
goto error_init ;
}
2015-03-10 13:41:10 +01:00
if ( hsotg - > dr_mode ! = USB_DR_MODE_HOST ) {
2017-12-25 15:17:45 +04:00
retval = dwc2_gadget_init ( hsotg ) ;
2015-03-10 13:41:10 +01:00
if ( retval )
2020-09-09 11:35:10 +02:00
goto error_drd ;
2015-03-10 13:41:10 +01:00
hsotg - > gadget_enabled = 1 ;
}
2019-05-20 10:56:04 -07:00
/*
* If we need PHY for wakeup we must be wakeup capable .
* When we have a device that can wake without the PHY we
* can adjust this condition .
*/
if ( hsotg - > need_phy_for_wake )
device_set_wakeup_capable ( & dev - > dev , true ) ;
2019-04-16 14:53:49 -07:00
hsotg - > reset_phy_on_wake =
of_property_read_bool ( dev - > dev . of_node ,
" snps,reset-phy-on-wake " ) ;
if ( hsotg - > reset_phy_on_wake & & ! hsotg - > phy ) {
dev_warn ( hsotg - > dev ,
" Quirk reset-phy-on-wake only supports generic PHYs \n " ) ;
hsotg - > reset_phy_on_wake = false ;
}
2015-03-10 13:41:10 +01:00
if ( hsotg - > dr_mode ! = USB_DR_MODE_PERIPHERAL ) {
2017-01-25 23:13:37 +01:00
retval = dwc2_hcd_init ( hsotg ) ;
2015-03-10 13:41:10 +01:00
if ( retval ) {
if ( hsotg - > gadget_enabled )
2015-08-06 18:11:54 -05:00
dwc2_hsotg_remove ( hsotg ) ;
2020-09-09 11:35:10 +02:00
goto error_drd ;
2015-03-10 13:41:10 +01:00
}
hsotg - > hcd_enabled = 1 ;
}
2013-04-22 14:00:19 -07:00
platform_set_drvdata ( dev , hsotg ) ;
2018-02-16 14:08:27 +04:00
hsotg - > hibernated = 0 ;
2013-04-22 14:00:19 -07:00
2015-04-29 22:08:59 +02:00
dwc2_debugfs_init ( hsotg ) ;
2015-10-14 08:52:29 +02:00
/* Gadget code manages lowlevel hw on its own */
if ( hsotg - > dr_mode = = USB_DR_MODE_PERIPHERAL )
dwc2_lowlevel_hw_disable ( hsotg ) ;
2020-06-09 12:28:11 +04:00
# if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
IS_ENABLED ( CONFIG_USB_DWC2_DUAL_ROLE )
/* Postponed adding a new gadget to the udc class driver list */
if ( hsotg - > gadget_enabled ) {
retval = usb_add_gadget_udc ( hsotg - > dev , & hsotg - > gadget ) ;
if ( retval ) {
2020-07-16 14:09:48 +02:00
hsotg - > gadget . udc = NULL ;
2020-06-09 12:28:11 +04:00
dwc2_hsotg_remove ( hsotg ) ;
2020-07-04 00:50:43 +02:00
goto error_debugfs ;
2020-06-09 12:28:11 +04:00
}
}
# endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */
2015-10-14 08:52:29 +02:00
return 0 ;
2020-10-17 18:50:12 +02:00
# if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
IS_ENABLED ( CONFIG_USB_DWC2_DUAL_ROLE )
2020-07-04 00:50:43 +02:00
error_debugfs :
dwc2_debugfs_exit ( hsotg ) ;
if ( hsotg - > hcd_enabled )
dwc2_hcd_remove ( hsotg ) ;
2020-10-17 18:50:12 +02:00
# endif
2020-09-09 11:35:10 +02:00
error_drd :
dwc2_drd_exit ( hsotg ) ;
2020-01-24 09:41:31 +01:00
error_init :
if ( hsotg - > params . activate_stm_id_vb_detection )
regulator_disable ( hsotg - > usb33d ) ;
2015-10-14 08:52:29 +02:00
error :
2020-07-16 14:09:48 +02:00
if ( hsotg - > dr_mode ! = USB_DR_MODE_PERIPHERAL )
dwc2_lowlevel_hw_disable ( hsotg ) ;
2013-04-22 14:00:19 -07:00
return retval ;
}
2014-11-19 12:37:53 -02:00
static int __maybe_unused dwc2_suspend ( struct device * dev )
2014-11-11 11:13:34 -06:00
{
2014-11-11 11:13:35 -06:00
struct dwc2_hsotg * dwc2 = dev_get_drvdata ( dev ) ;
2019-05-20 10:56:04 -07:00
bool is_device_mode = dwc2_is_device_mode ( dwc2 ) ;
2014-11-11 11:13:34 -06:00
int ret = 0 ;
2019-05-20 10:56:04 -07:00
if ( is_device_mode )
2015-10-14 08:52:29 +02:00
dwc2_hsotg_suspend ( dwc2 ) ;
2020-09-09 11:35:10 +02:00
dwc2_drd_suspend ( dwc2 ) ;
2020-01-24 09:41:31 +01:00
if ( dwc2 - > params . activate_stm_id_vb_detection ) {
unsigned long flags ;
u32 ggpio , gotgctl ;
/*
* Need to force the mode to the current mode to avoid Mode
* Mismatch Interrupt when ID detection will be disabled .
*/
dwc2_force_mode ( dwc2 , ! is_device_mode ) ;
spin_lock_irqsave ( & dwc2 - > lock , flags ) ;
gotgctl = dwc2_readl ( dwc2 , GOTGCTL ) ;
/* bypass debounce filter, enable overrides */
gotgctl | = GOTGCTL_DBNCE_FLTR_BYPASS ;
gotgctl | = GOTGCTL_BVALOEN | GOTGCTL_AVALOEN ;
/* Force A / B session if needed */
if ( gotgctl & GOTGCTL_ASESVLD )
gotgctl | = GOTGCTL_AVALOVAL ;
if ( gotgctl & GOTGCTL_BSESVLD )
gotgctl | = GOTGCTL_BVALOVAL ;
dwc2_writel ( dwc2 , gotgctl , GOTGCTL ) ;
spin_unlock_irqrestore ( & dwc2 - > lock , flags ) ;
ggpio = dwc2_readl ( dwc2 , GGPIO ) ;
ggpio & = ~ GGPIO_STM32_OTG_GCCFG_IDEN ;
ggpio & = ~ GGPIO_STM32_OTG_GCCFG_VBDEN ;
dwc2_writel ( dwc2 , ggpio , GGPIO ) ;
regulator_disable ( dwc2 - > usb33d ) ;
}
2019-05-20 10:56:04 -07:00
if ( dwc2 - > ll_hw_enabled & &
( is_device_mode | | dwc2_host_can_poweroff_phy ( dwc2 ) ) ) {
2015-10-14 08:52:29 +02:00
ret = __dwc2_lowlevel_hw_disable ( dwc2 ) ;
2019-05-20 10:56:04 -07:00
dwc2 - > phy_off_for_suspend = true ;
}
2014-12-08 17:46:26 +08:00
2014-11-11 11:13:34 -06:00
return ret ;
}
2014-11-19 12:37:53 -02:00
static int __maybe_unused dwc2_resume ( struct device * dev )
2014-11-11 11:13:34 -06:00
{
2014-11-11 11:13:35 -06:00
struct dwc2_hsotg * dwc2 = dev_get_drvdata ( dev ) ;
2014-11-11 11:13:34 -06:00
int ret = 0 ;
2019-05-20 10:56:04 -07:00
if ( dwc2 - > phy_off_for_suspend & & dwc2 - > ll_hw_enabled ) {
2015-10-14 08:52:29 +02:00
ret = __dwc2_lowlevel_hw_enable ( dwc2 ) ;
if ( ret )
return ret ;
}
2019-05-20 10:56:04 -07:00
dwc2 - > phy_off_for_suspend = false ;
2015-10-14 08:52:29 +02:00
2020-01-24 09:41:31 +01:00
if ( dwc2 - > params . activate_stm_id_vb_detection ) {
unsigned long flags ;
u32 ggpio , gotgctl ;
ret = regulator_enable ( dwc2 - > usb33d ) ;
if ( ret )
return ret ;
ggpio = dwc2_readl ( dwc2 , GGPIO ) ;
ggpio | = GGPIO_STM32_OTG_GCCFG_IDEN ;
ggpio | = GGPIO_STM32_OTG_GCCFG_VBDEN ;
dwc2_writel ( dwc2 , ggpio , GGPIO ) ;
/* ID/VBUS detection startup time */
usleep_range ( 5000 , 7000 ) ;
spin_lock_irqsave ( & dwc2 - > lock , flags ) ;
gotgctl = dwc2_readl ( dwc2 , GOTGCTL ) ;
gotgctl & = ~ GOTGCTL_DBNCE_FLTR_BYPASS ;
gotgctl & = ~ ( GOTGCTL_BVALOEN | GOTGCTL_AVALOEN |
GOTGCTL_BVALOVAL | GOTGCTL_AVALOVAL ) ;
dwc2_writel ( dwc2 , gotgctl , GOTGCTL ) ;
spin_unlock_irqrestore ( & dwc2 - > lock , flags ) ;
}
/* Need to restore FORCEDEVMODE/FORCEHOSTMODE */
dwc2_force_dr_mode ( dwc2 ) ;
2020-09-09 11:35:10 +02:00
dwc2_drd_resume ( dwc2 ) ;
2015-10-14 08:52:29 +02:00
if ( dwc2_is_device_mode ( dwc2 ) )
2015-08-06 18:11:54 -05:00
ret = dwc2_hsotg_resume ( dwc2 ) ;
2014-12-08 17:46:26 +08:00
2014-11-11 11:13:34 -06:00
return ret ;
}
2014-11-11 11:13:35 -06:00
static const struct dev_pm_ops dwc2_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( dwc2_suspend , dwc2_resume )
} ;
2013-04-22 14:00:19 -07:00
static struct platform_driver dwc2_platform_driver = {
. driver = {
2013-11-12 20:07:19 +01:00
. name = dwc2_driver_name ,
2013-04-22 14:00:19 -07:00
. of_match_table = dwc2_of_match_table ,
2014-11-11 11:13:35 -06:00
. pm = & dwc2_dev_pm_ops ,
2013-04-22 14:00:19 -07:00
} ,
. probe = dwc2_driver_probe ,
. remove = dwc2_driver_remove ,
2015-12-18 19:30:59 +01:00
. shutdown = dwc2_driver_shutdown ,
2013-04-22 14:00:19 -07:00
} ;
module_platform_driver ( dwc2_platform_driver ) ;
MODULE_DESCRIPTION ( " DESIGNWARE HS OTG Platform Glue " ) ;
MODULE_AUTHOR ( " Matthijs Kooijman <matthijs@stdin.nl> " ) ;
MODULE_LICENSE ( " Dual BSD/GPL " ) ;