2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2017-01-06 07:24:46 +03:00
/*
* ZX Specific Extensions for Synopsys DW Multimedia Card Interface driver
*
* Copyright ( C ) 2016 , Linaro Ltd .
* Copyright ( C ) 2016 , ZTE Corp .
*/
# include <linux/clk.h>
# include <linux/mfd/syscon.h>
# include <linux/mmc/host.h>
# include <linux/mmc/mmc.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/platform_device.h>
# include <linux/pm_runtime.h>
# include <linux/regmap.h>
# include <linux/slab.h>
# include "dw_mmc.h"
# include "dw_mmc-pltfm.h"
# include "dw_mmc-zx.h"
struct dw_mci_zx_priv_data {
struct regmap * sysc_base ;
} ;
enum delay_type {
DELAY_TYPE_READ , /* read dqs delay */
DELAY_TYPE_CLK , /* clk sample delay */
} ;
static int dw_mci_zx_emmc_set_delay ( struct dw_mci * host , unsigned int delay ,
enum delay_type dflag )
{
struct dw_mci_zx_priv_data * priv = host - > priv ;
struct regmap * sysc_base = priv - > sysc_base ;
unsigned int clksel ;
unsigned int loop = 1000 ;
int ret ;
if ( ! sysc_base )
return - EINVAL ;
ret = regmap_update_bits ( sysc_base , LB_AON_EMMC_CFG_REG0 ,
PARA_HALF_CLK_MODE | PARA_DLL_BYPASS_MODE |
PARA_PHASE_DET_SEL_MASK |
PARA_DLL_LOCK_NUM_MASK |
DLL_REG_SET | PARA_DLL_START_MASK ,
PARA_DLL_START ( 4 ) | PARA_DLL_LOCK_NUM ( 4 ) ) ;
if ( ret )
return ret ;
ret = regmap_read ( sysc_base , LB_AON_EMMC_CFG_REG1 , & clksel ) ;
if ( ret )
return ret ;
if ( dflag = = DELAY_TYPE_CLK ) {
clksel & = ~ CLK_SAMP_DELAY_MASK ;
clksel | = CLK_SAMP_DELAY ( delay ) ;
} else {
clksel & = ~ READ_DQS_DELAY_MASK ;
clksel | = READ_DQS_DELAY ( delay ) ;
}
regmap_write ( sysc_base , LB_AON_EMMC_CFG_REG1 , clksel ) ;
regmap_update_bits ( sysc_base , LB_AON_EMMC_CFG_REG0 ,
PARA_DLL_START_MASK | PARA_DLL_LOCK_NUM_MASK |
DLL_REG_SET ,
PARA_DLL_START ( 4 ) | PARA_DLL_LOCK_NUM ( 4 ) |
DLL_REG_SET ) ;
do {
ret = regmap_read ( sysc_base , LB_AON_EMMC_CFG_REG2 , & clksel ) ;
if ( ret )
return ret ;
} while ( - - loop & & ! ( clksel & ZX_DLL_LOCKED ) ) ;
if ( ! loop ) {
dev_err ( host - > dev , " Error: %s dll lock fail \n " , __func__ ) ;
return - EIO ;
}
return 0 ;
}
static int dw_mci_zx_emmc_execute_tuning ( struct dw_mci_slot * slot , u32 opcode )
{
struct dw_mci * host = slot - > host ;
struct mmc_host * mmc = slot - > mmc ;
int ret , len = 0 , start = 0 , end = 0 , delay , best = 0 ;
for ( delay = 1 ; delay < 128 ; delay + + ) {
ret = dw_mci_zx_emmc_set_delay ( host , delay , DELAY_TYPE_CLK ) ;
if ( ! ret & & mmc_send_tuning ( mmc , opcode , NULL ) ) {
if ( start > = 0 ) {
end = delay - 1 ;
/* check and update longest good range */
if ( ( end - start ) > len ) {
best = ( start + end ) > > 1 ;
len = end - start ;
}
}
start = - 1 ;
end = 0 ;
continue ;
}
if ( start < 0 )
start = delay ;
}
if ( start > = 0 ) {
end = delay - 1 ;
if ( ( end - start ) > len ) {
best = ( start + end ) > > 1 ;
len = end - start ;
}
}
if ( best < 0 )
return - EIO ;
dev_info ( host - > dev , " %s best range: start %d end %d \n " , __func__ ,
start , end ) ;
return dw_mci_zx_emmc_set_delay ( host , best , DELAY_TYPE_CLK ) ;
}
static int dw_mci_zx_prepare_hs400_tuning ( struct dw_mci * host ,
struct mmc_ios * ios )
{
int ret ;
/* config phase shift as 90 degree */
ret = dw_mci_zx_emmc_set_delay ( host , 32 , DELAY_TYPE_READ ) ;
if ( ret < 0 )
return - EIO ;
return 0 ;
}
static int dw_mci_zx_execute_tuning ( struct dw_mci_slot * slot , u32 opcode )
{
struct dw_mci * host = slot - > host ;
if ( host - > verid = = 0x290a ) /* only for emmc */
return dw_mci_zx_emmc_execute_tuning ( slot , opcode ) ;
/* TODO: Add 0x210a dedicated tuning for sd/sdio */
return 0 ;
}
static int dw_mci_zx_parse_dt ( struct dw_mci * host )
{
struct device_node * np = host - > dev - > of_node ;
struct device_node * node ;
struct dw_mci_zx_priv_data * priv ;
struct regmap * sysc_base ;
/* syscon is needed only by emmc */
node = of_parse_phandle ( np , " zte,aon-syscon " , 0 ) ;
if ( node ) {
sysc_base = syscon_node_to_regmap ( node ) ;
of_node_put ( node ) ;
2020-09-02 22:36:50 +03:00
if ( IS_ERR ( sysc_base ) )
return dev_err_probe ( host - > dev , PTR_ERR ( sysc_base ) ,
" Can't get syscon \n " ) ;
2017-01-06 07:24:46 +03:00
} else {
return 0 ;
}
priv = devm_kzalloc ( host - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
priv - > sysc_base = sysc_base ;
host - > priv = priv ;
return 0 ;
}
static unsigned long zx_dwmmc_caps [ 3 ] = {
MMC_CAP_CMD23 ,
MMC_CAP_CMD23 ,
MMC_CAP_CMD23 ,
} ;
static const struct dw_mci_drv_data zx_drv_data = {
. caps = zx_dwmmc_caps ,
2018-02-24 09:17:23 +03:00
. num_caps = ARRAY_SIZE ( zx_dwmmc_caps ) ,
2017-01-06 07:24:46 +03:00
. execute_tuning = dw_mci_zx_execute_tuning ,
. prepare_hs400_tuning = dw_mci_zx_prepare_hs400_tuning ,
. parse_dt = dw_mci_zx_parse_dt ,
} ;
static const struct of_device_id dw_mci_zx_match [ ] = {
{ . compatible = " zte,zx296718-dw-mshc " , . data = & zx_drv_data } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , dw_mci_zx_match ) ;
static int dw_mci_zx_probe ( struct platform_device * pdev )
{
const struct dw_mci_drv_data * drv_data ;
const struct of_device_id * match ;
match = of_match_node ( dw_mci_zx_match , pdev - > dev . of_node ) ;
drv_data = match - > data ;
return dw_mci_pltfm_register ( pdev , drv_data ) ;
}
static const struct dev_pm_ops dw_mci_zx_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( pm_runtime_force_suspend ,
pm_runtime_force_resume )
SET_RUNTIME_PM_OPS ( dw_mci_runtime_suspend ,
dw_mci_runtime_resume ,
NULL )
} ;
static struct platform_driver dw_mci_zx_pltfm_driver = {
. probe = dw_mci_zx_probe ,
. remove = dw_mci_pltfm_remove ,
. driver = {
. name = " dwmmc_zx " ,
2020-09-04 02:24:38 +03:00
. probe_type = PROBE_PREFER_ASYNCHRONOUS ,
2017-01-06 07:24:46 +03:00
. of_match_table = dw_mci_zx_match ,
. pm = & dw_mci_zx_dev_pm_ops ,
} ,
} ;
module_platform_driver ( dw_mci_zx_pltfm_driver ) ;
MODULE_DESCRIPTION ( " ZTE emmc/sd driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;