2014-01-09 22:35:11 +08:00
/*
* Copyright ( c ) 2013 Linaro Ltd .
* Copyright ( c ) 2013 Hisilicon Limited .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*/
# include <linux/clk.h>
2015-05-14 16:45:19 +08:00
# include <linux/mfd/syscon.h>
2014-01-09 22:35:11 +08:00
# include <linux/mmc/host.h>
2015-05-14 16:45:19 +08:00
# include <linux/module.h>
2014-01-09 22:35:11 +08:00
# include <linux/of_address.h>
2015-05-14 16:45:19 +08:00
# include <linux/platform_device.h>
2016-10-12 10:54:40 +08:00
# include <linux/pm_runtime.h>
2015-05-14 16:45:19 +08:00
# include <linux/regmap.h>
# include <linux/regulator/consumer.h>
2014-01-09 22:35:11 +08:00
# include "dw_mmc.h"
# include "dw_mmc-pltfm.h"
2015-05-14 16:45:19 +08:00
/*
* hi6220 sd only support io voltage 1.8 v and 3 v
* Also need config AO_SCTRL_SEL18 accordingly
*/
# define AO_SCTRL_SEL18 BIT(10)
# define AO_SCTRL_CTRL3 0x40C
struct k3_priv {
struct regmap * reg ;
} ;
2016-06-01 14:39:58 +02:00
static unsigned long dw_mci_hi6220_caps [ ] = {
MMC_CAP_CMD23 ,
MMC_CAP_CMD23 ,
0
} ;
2014-01-09 22:35:11 +08:00
static void dw_mci_k3_set_ios ( struct dw_mci * host , struct mmc_ios * ios )
{
int ret ;
2014-01-13 17:14:29 +08:00
ret = clk_set_rate ( host - > ciu_clk , ios - > clock ) ;
2014-01-09 22:35:11 +08:00
if ( ret )
2014-01-13 17:14:29 +08:00
dev_warn ( host - > dev , " failed to set rate %uHz \n " , ios - > clock ) ;
2014-01-09 22:35:11 +08:00
host - > bus_hz = clk_get_rate ( host - > ciu_clk ) ;
}
static const struct dw_mci_drv_data k3_drv_data = {
. set_ios = dw_mci_k3_set_ios ,
} ;
2015-05-14 16:45:19 +08:00
static int dw_mci_hi6220_parse_dt ( struct dw_mci * host )
{
struct k3_priv * priv ;
priv = devm_kzalloc ( host - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
priv - > reg = syscon_regmap_lookup_by_phandle ( host - > dev - > of_node ,
" hisilicon,peripheral-syscon " ) ;
if ( IS_ERR ( priv - > reg ) )
priv - > reg = NULL ;
host - > priv = priv ;
return 0 ;
}
static int dw_mci_hi6220_switch_voltage ( struct mmc_host * mmc , struct mmc_ios * ios )
{
struct dw_mci_slot * slot = mmc_priv ( mmc ) ;
struct k3_priv * priv ;
struct dw_mci * host ;
int min_uv , max_uv ;
int ret ;
host = slot - > host ;
priv = host - > priv ;
if ( ! priv | | ! priv - > reg )
return 0 ;
if ( ios - > signal_voltage = = MMC_SIGNAL_VOLTAGE_330 ) {
ret = regmap_update_bits ( priv - > reg , AO_SCTRL_CTRL3 ,
AO_SCTRL_SEL18 , 0 ) ;
min_uv = 3000000 ;
max_uv = 3000000 ;
} else if ( ios - > signal_voltage = = MMC_SIGNAL_VOLTAGE_180 ) {
ret = regmap_update_bits ( priv - > reg , AO_SCTRL_CTRL3 ,
AO_SCTRL_SEL18 , AO_SCTRL_SEL18 ) ;
min_uv = 1800000 ;
max_uv = 1800000 ;
} else {
dev_dbg ( host - > dev , " voltage not supported \n " ) ;
return - EINVAL ;
}
if ( ret ) {
dev_dbg ( host - > dev , " switch voltage failed \n " ) ;
return ret ;
}
if ( IS_ERR_OR_NULL ( mmc - > supply . vqmmc ) )
return 0 ;
ret = regulator_set_voltage ( mmc - > supply . vqmmc , min_uv , max_uv ) ;
if ( ret ) {
dev_dbg ( host - > dev , " Regulator set error %d: %d - %d \n " ,
ret , min_uv , max_uv ) ;
return ret ;
}
return 0 ;
}
static void dw_mci_hi6220_set_ios ( struct dw_mci * host , struct mmc_ios * ios )
{
int ret ;
unsigned int clock ;
clock = ( ios - > clock < = 25000000 ) ? 25000000 : ios - > clock ;
ret = clk_set_rate ( host - > biu_clk , clock ) ;
if ( ret )
dev_warn ( host - > dev , " failed to set rate %uHz \n " , clock ) ;
host - > bus_hz = clk_get_rate ( host - > biu_clk ) ;
}
2016-08-04 10:16:13 +08:00
static int dw_mci_hi6220_execute_tuning ( struct dw_mci_slot * slot , u32 opcode )
{
return 0 ;
}
2015-05-14 16:45:19 +08:00
static const struct dw_mci_drv_data hi6220_data = {
2016-06-01 14:39:58 +02:00
. caps = dw_mci_hi6220_caps ,
2015-05-14 16:45:19 +08:00
. switch_voltage = dw_mci_hi6220_switch_voltage ,
. set_ios = dw_mci_hi6220_set_ios ,
. parse_dt = dw_mci_hi6220_parse_dt ,
2016-08-04 10:16:13 +08:00
. execute_tuning = dw_mci_hi6220_execute_tuning ,
2015-05-14 16:45:19 +08:00
} ;
2014-01-09 22:35:11 +08:00
static const struct of_device_id dw_mci_k3_match [ ] = {
{ . compatible = " hisilicon,hi4511-dw-mshc " , . data = & k3_drv_data , } ,
2015-05-14 16:45:19 +08:00
{ . compatible = " hisilicon,hi6220-dw-mshc " , . data = & hi6220_data , } ,
2014-01-09 22:35:11 +08:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , dw_mci_k3_match ) ;
static int dw_mci_k3_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_k3_match , pdev - > dev . of_node ) ;
drv_data = match - > data ;
return dw_mci_pltfm_register ( pdev , drv_data ) ;
}
2016-10-12 10:54:40 +08:00
static const struct dev_pm_ops dw_mci_k3_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 )
} ;
2014-01-09 22:35:11 +08:00
static struct platform_driver dw_mci_k3_pltfm_driver = {
. probe = dw_mci_k3_probe ,
. remove = dw_mci_pltfm_remove ,
. driver = {
. name = " dwmmc_k3 " ,
. of_match_table = dw_mci_k3_match ,
2016-10-12 10:54:40 +08:00
. pm = & dw_mci_k3_dev_pm_ops ,
2014-01-09 22:35:11 +08:00
} ,
} ;
module_platform_driver ( dw_mci_k3_pltfm_driver ) ;
MODULE_DESCRIPTION ( " K3 Specific DW-MSHC Driver Extension " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
2015-05-14 16:59:44 +08:00
MODULE_ALIAS ( " platform:dwmmc_k3 " ) ;