2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2016-05-11 14:21:30 +03:00
/*
* UFS Host driver for Synopsys Designware Core
*
* Copyright ( C ) 2015 - 2016 Synopsys , Inc . ( www . synopsys . com )
*
* Authors : Joao Pinto < jpinto @ synopsys . com >
*/
2022-04-20 01:58:08 +03:00
# include <linux/module.h>
2022-05-12 00:25:52 +03:00
# include <ufs/ufshcd.h>
# include <ufs/unipro.h>
2016-05-11 14:21:30 +03:00
# include "ufshcd-dwc.h"
# include "ufshci-dwc.h"
int ufshcd_dwc_dme_set_attrs ( struct ufs_hba * hba ,
const struct ufshcd_dme_attr_val * v , int n )
{
int ret = 0 ;
int attr_node = 0 ;
for ( attr_node = 0 ; attr_node < n ; attr_node + + ) {
ret = ufshcd_dme_set_attr ( hba , v [ attr_node ] . attr_sel ,
ATTR_SET_NOR , v [ attr_node ] . mib_val , v [ attr_node ] . peer ) ;
if ( ret )
return ret ;
}
return 0 ;
}
EXPORT_SYMBOL ( ufshcd_dwc_dme_set_attrs ) ;
/**
* ufshcd_dwc_program_clk_div ( )
* This function programs the clk divider value . This value is needed to
* provide 1 microsecond tick to unipro layer .
* @ hba : Private Structure pointer
* @ divider_val : clock divider value to be programmed
*
*/
static void ufshcd_dwc_program_clk_div ( struct ufs_hba * hba , u32 divider_val )
{
ufshcd_writel ( hba , divider_val , DWC_UFS_REG_HCLKDIV ) ;
}
/**
* ufshcd_dwc_link_is_up ( )
* Check if link is up
2019-02-10 18:20:45 +03:00
* @ hba : private structure pointer
2016-05-11 14:21:30 +03:00
*
* Returns 0 on success , non - zero value on failure
*/
static int ufshcd_dwc_link_is_up ( struct ufs_hba * hba )
{
int dme_result = 0 ;
ufshcd_dme_get ( hba , UIC_ARG_MIB ( VS_POWERSTATE ) , & dme_result ) ;
if ( dme_result = = UFSHCD_LINK_IS_UP ) {
ufshcd_set_link_active ( hba ) ;
return 0 ;
}
return 1 ;
}
/**
* ufshcd_dwc_connection_setup ( )
* This function configures both the local side ( host ) and the peer side
* ( device ) unipro attributes to establish the connection to application /
* cport .
* This function is not required if the hardware is properly configured to
* have this connection setup on reset . But invoking this function does no
* harm and should be fine even working with any ufs device .
*
* @ hba : pointer to drivers private data
*
* Returns 0 on success non - zero value on failure
*/
static int ufshcd_dwc_connection_setup ( struct ufs_hba * hba )
{
2019-09-06 20:01:04 +03:00
static const struct ufshcd_dme_attr_val setup_attrs [ ] = {
2016-05-11 14:21:30 +03:00
{ UIC_ARG_MIB ( T_CONNECTIONSTATE ) , 0 , DME_LOCAL } ,
{ UIC_ARG_MIB ( N_DEVICEID ) , 0 , DME_LOCAL } ,
{ UIC_ARG_MIB ( N_DEVICEID_VALID ) , 0 , DME_LOCAL } ,
{ UIC_ARG_MIB ( T_PEERDEVICEID ) , 1 , DME_LOCAL } ,
{ UIC_ARG_MIB ( T_PEERCPORTID ) , 0 , DME_LOCAL } ,
{ UIC_ARG_MIB ( T_TRAFFICCLASS ) , 0 , DME_LOCAL } ,
{ UIC_ARG_MIB ( T_CPORTFLAGS ) , 0x6 , DME_LOCAL } ,
{ UIC_ARG_MIB ( T_CPORTMODE ) , 1 , DME_LOCAL } ,
{ UIC_ARG_MIB ( T_CONNECTIONSTATE ) , 1 , DME_LOCAL } ,
{ UIC_ARG_MIB ( T_CONNECTIONSTATE ) , 0 , DME_PEER } ,
{ UIC_ARG_MIB ( N_DEVICEID ) , 1 , DME_PEER } ,
{ UIC_ARG_MIB ( N_DEVICEID_VALID ) , 1 , DME_PEER } ,
{ UIC_ARG_MIB ( T_PEERDEVICEID ) , 1 , DME_PEER } ,
{ UIC_ARG_MIB ( T_PEERCPORTID ) , 0 , DME_PEER } ,
{ UIC_ARG_MIB ( T_TRAFFICCLASS ) , 0 , DME_PEER } ,
{ UIC_ARG_MIB ( T_CPORTFLAGS ) , 0x6 , DME_PEER } ,
{ UIC_ARG_MIB ( T_CPORTMODE ) , 1 , DME_PEER } ,
{ UIC_ARG_MIB ( T_CONNECTIONSTATE ) , 1 , DME_PEER }
} ;
return ufshcd_dwc_dme_set_attrs ( hba , setup_attrs , ARRAY_SIZE ( setup_attrs ) ) ;
}
/**
* ufshcd_dwc_link_startup_notify ( )
* UFS Host DWC specific link startup sequence
2019-02-10 18:20:45 +03:00
* @ hba : private structure pointer
2016-05-11 14:21:30 +03:00
* @ status : Callback notify status
*
* Returns 0 on success , non - zero value on failure
*/
int ufshcd_dwc_link_startup_notify ( struct ufs_hba * hba ,
enum ufs_notify_change_status status )
{
int err = 0 ;
if ( status = = PRE_CHANGE ) {
ufshcd_dwc_program_clk_div ( hba , DWC_UFS_REG_HCLKDIV_DIV_125 ) ;
2020-12-05 15:00:41 +03:00
err = ufshcd_vops_phy_initialization ( hba ) ;
if ( err ) {
dev_err ( hba - > dev , " Phy setup failed (%d) \n " , err ) ;
goto out ;
2016-05-11 14:21:30 +03:00
}
} else { /* POST_CHANGE */
err = ufshcd_dwc_link_is_up ( hba ) ;
if ( err ) {
dev_err ( hba - > dev , " Link is not up \n " ) ;
goto out ;
}
err = ufshcd_dwc_connection_setup ( hba ) ;
if ( err )
dev_err ( hba - > dev , " Connection setup failed (%d) \n " ,
err ) ;
}
out :
return err ;
}
EXPORT_SYMBOL ( ufshcd_dwc_link_startup_notify ) ;
2016-05-23 12:18:07 +03:00
MODULE_AUTHOR ( " Joao Pinto <Joao.Pinto@synopsys.com> " ) ;
MODULE_DESCRIPTION ( " UFS Host driver for Synopsys Designware Core " ) ;
MODULE_LICENSE ( " Dual BSD/GPL " ) ;