2012-09-17 22:16:43 +04:00
/*
* Exynos Specific Extensions for Synopsys DW Multimedia Card Interface driver
*
* Copyright ( C ) 2012 , Samsung Electronics Co . , Ltd .
*
* 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/module.h>
# include <linux/platform_device.h>
# include <linux/clk.h>
# include <linux/mmc/host.h>
# include <linux/mmc/dw_mmc.h>
2013-08-30 19:12:50 +04:00
# include <linux/mmc/mmc.h>
2012-09-17 22:16:43 +04:00
# include <linux/of.h>
# include <linux/of_gpio.h>
2013-08-30 19:12:50 +04:00
# include <linux/slab.h>
2012-09-17 22:16:43 +04:00
# include "dw_mmc.h"
# include "dw_mmc-pltfm.h"
2014-12-22 15:12:04 +03:00
# include "dw_mmc-exynos.h"
2013-08-30 19:13:03 +04:00
2012-09-17 22:16:43 +04:00
/* Variations in Exynos specific dw-mshc controller */
enum dw_mci_exynos_type {
DW_MCI_TYPE_EXYNOS4210 ,
DW_MCI_TYPE_EXYNOS4412 ,
DW_MCI_TYPE_EXYNOS5250 ,
2013-05-24 14:04:32 +04:00
DW_MCI_TYPE_EXYNOS5420 ,
2013-08-30 19:12:35 +04:00
DW_MCI_TYPE_EXYNOS5420_SMU ,
2014-08-28 17:18:53 +04:00
DW_MCI_TYPE_EXYNOS7 ,
DW_MCI_TYPE_EXYNOS7_SMU ,
2012-09-17 22:16:43 +04:00
} ;
/* Exynos implementation specific driver private data */
struct dw_mci_exynos_priv_data {
enum dw_mci_exynos_type ctrl_type ;
u8 ciu_div ;
u32 sdr_timing ;
u32 ddr_timing ;
2015-01-29 05:41:57 +03:00
u32 hs400_timing ;
u32 tuned_sample ;
2013-08-30 19:13:03 +04:00
u32 cur_speed ;
2015-01-29 05:41:57 +03:00
u32 dqs_delay ;
u32 saved_dqs_en ;
u32 saved_strobe_ctrl ;
2012-09-17 22:16:43 +04:00
} ;
static struct dw_mci_exynos_compatible {
char * compatible ;
enum dw_mci_exynos_type ctrl_type ;
} exynos_compat [ ] = {
{
. compatible = " samsung,exynos4210-dw-mshc " ,
. ctrl_type = DW_MCI_TYPE_EXYNOS4210 ,
} , {
. compatible = " samsung,exynos4412-dw-mshc " ,
. ctrl_type = DW_MCI_TYPE_EXYNOS4412 ,
} , {
. compatible = " samsung,exynos5250-dw-mshc " ,
. ctrl_type = DW_MCI_TYPE_EXYNOS5250 ,
2013-05-24 14:04:32 +04:00
} , {
. compatible = " samsung,exynos5420-dw-mshc " ,
. ctrl_type = DW_MCI_TYPE_EXYNOS5420 ,
2013-08-30 19:12:35 +04:00
} , {
. compatible = " samsung,exynos5420-dw-mshc-smu " ,
. ctrl_type = DW_MCI_TYPE_EXYNOS5420_SMU ,
2014-08-28 17:18:53 +04:00
} , {
. compatible = " samsung,exynos7-dw-mshc " ,
. ctrl_type = DW_MCI_TYPE_EXYNOS7 ,
} , {
. compatible = " samsung,exynos7-dw-mshc-smu " ,
. ctrl_type = DW_MCI_TYPE_EXYNOS7_SMU ,
2012-09-17 22:16:43 +04:00
} ,
} ;
2015-01-29 05:41:57 +03:00
static inline u8 dw_mci_exynos_get_ciu_div ( struct dw_mci * host )
{
struct dw_mci_exynos_priv_data * priv = host - > priv ;
if ( priv - > ctrl_type = = DW_MCI_TYPE_EXYNOS4412 )
return EXYNOS4412_FIXED_CIU_CLK_DIV ;
else if ( priv - > ctrl_type = = DW_MCI_TYPE_EXYNOS4210 )
return EXYNOS4210_FIXED_CIU_CLK_DIV ;
else if ( priv - > ctrl_type = = DW_MCI_TYPE_EXYNOS7 | |
priv - > ctrl_type = = DW_MCI_TYPE_EXYNOS7_SMU )
return SDMMC_CLKSEL_GET_DIV ( mci_readl ( host , CLKSEL64 ) ) + 1 ;
else
return SDMMC_CLKSEL_GET_DIV ( mci_readl ( host , CLKSEL ) ) + 1 ;
}
2016-03-31 08:53:18 +03:00
static void dw_mci_exynos_config_smu ( struct dw_mci * host )
2012-09-17 22:16:43 +04:00
{
2013-08-30 19:11:57 +04:00
struct dw_mci_exynos_priv_data * priv = host - > priv ;
2012-09-17 22:16:43 +04:00
2016-03-31 08:53:18 +03:00
/*
* If Exynos is provided the Security management ,
* set for non - ecryption mode at this time .
*/
2014-08-28 17:18:53 +04:00
if ( priv - > ctrl_type = = DW_MCI_TYPE_EXYNOS5420_SMU | |
priv - > ctrl_type = = DW_MCI_TYPE_EXYNOS7_SMU ) {
2013-08-30 19:12:35 +04:00
mci_writel ( host , MPSBEGIN0 , 0 ) ;
2014-12-22 15:12:04 +03:00
mci_writel ( host , MPSEND0 , SDMMC_ENDING_SEC_NR_MAX ) ;
mci_writel ( host , MPSCTRL0 , SDMMC_MPSCTRL_SECURE_WRITE_BIT |
SDMMC_MPSCTRL_NON_SECURE_READ_BIT |
SDMMC_MPSCTRL_VALID |
SDMMC_MPSCTRL_NON_SECURE_WRITE_BIT ) ;
2013-08-30 19:12:35 +04:00
}
2016-03-31 08:53:18 +03:00
}
static int dw_mci_exynos_priv_init ( struct dw_mci * host )
{
struct dw_mci_exynos_priv_data * priv = host - > priv ;
dw_mci_exynos_config_smu ( host ) ;
2013-08-30 19:12:35 +04:00
2015-01-29 05:41:57 +03:00
if ( priv - > ctrl_type > = DW_MCI_TYPE_EXYNOS5420 ) {
priv - > saved_strobe_ctrl = mci_readl ( host , HS400_DLINE_CTRL ) ;
priv - > saved_dqs_en = mci_readl ( host , HS400_DQS_EN ) ;
priv - > saved_dqs_en | = AXI_NON_BLOCKING_WR ;
mci_writel ( host , HS400_DQS_EN , priv - > saved_dqs_en ) ;
if ( ! priv - > dqs_delay )
priv - > dqs_delay =
DQS_CTRL_GET_RD_DELAY ( priv - > saved_strobe_ctrl ) ;
}
2014-12-22 15:12:03 +03:00
host - > bus_hz / = ( priv - > ciu_div + 1 ) ;
2012-09-17 22:16:43 +04:00
return 0 ;
}
2015-01-29 05:41:57 +03:00
static void dw_mci_exynos_set_clksel_timing ( struct dw_mci * host , u32 timing )
{
struct dw_mci_exynos_priv_data * priv = host - > priv ;
u32 clksel ;
if ( priv - > ctrl_type = = DW_MCI_TYPE_EXYNOS7 | |
priv - > ctrl_type = = DW_MCI_TYPE_EXYNOS7_SMU )
clksel = mci_readl ( host , CLKSEL64 ) ;
else
clksel = mci_readl ( host , CLKSEL ) ;
clksel = ( clksel & ~ SDMMC_CLKSEL_TIMING_MASK ) | timing ;
if ( priv - > ctrl_type = = DW_MCI_TYPE_EXYNOS7 | |
priv - > ctrl_type = = DW_MCI_TYPE_EXYNOS7_SMU )
mci_writel ( host , CLKSEL64 , clksel ) ;
else
mci_writel ( host , CLKSEL , clksel ) ;
2016-01-21 05:01:06 +03:00
/*
* Exynos4412 and Exynos5250 extends the use of CMD register with the
* use of bit 29 ( which is reserved on standard MSHC controllers ) for
* optionally bypassing the HOLD register for command and data . The
* HOLD register should be bypassed in case there is no phase shift
* applied on CMD / DATA that is sent to the card .
*/
2016-06-20 07:16:09 +03:00
if ( ! SDMMC_CLKSEL_GET_DRV_WD3 ( clksel ) & & host - > cur_slot )
2016-01-21 05:01:06 +03:00
set_bit ( DW_MMC_CARD_NO_USE_HOLD , & host - > cur_slot - > flags ) ;
2015-01-29 05:41:57 +03:00
}
2013-08-30 19:11:21 +04:00
# ifdef CONFIG_PM_SLEEP
static int dw_mci_exynos_suspend ( struct device * dev )
{
struct dw_mci * host = dev_get_drvdata ( dev ) ;
return dw_mci_suspend ( host ) ;
}
static int dw_mci_exynos_resume ( struct device * dev )
{
struct dw_mci * host = dev_get_drvdata ( dev ) ;
2016-03-31 08:53:18 +03:00
dw_mci_exynos_config_smu ( host ) ;
2013-08-30 19:11:21 +04:00
return dw_mci_resume ( host ) ;
}
/**
* dw_mci_exynos_resume_noirq - Exynos - specific resume code
*
* On exynos5420 there is a silicon errata that will sometimes leave the
* WAKEUP_INT bit in the CLKSEL register asserted . This bit is 1 to indicate
* that it fired and we can clear it by writing a 1 back . Clear it to prevent
* interrupts from going off constantly .
*
* We run this code on all exynos variants because it doesn ' t hurt .
*/
static int dw_mci_exynos_resume_noirq ( struct device * dev )
{
struct dw_mci * host = dev_get_drvdata ( dev ) ;
2014-08-28 17:18:53 +04:00
struct dw_mci_exynos_priv_data * priv = host - > priv ;
2013-08-30 19:11:21 +04:00
u32 clksel ;
2014-08-28 17:18:53 +04:00
if ( priv - > ctrl_type = = DW_MCI_TYPE_EXYNOS7 | |
priv - > ctrl_type = = DW_MCI_TYPE_EXYNOS7_SMU )
clksel = mci_readl ( host , CLKSEL64 ) ;
else
clksel = mci_readl ( host , CLKSEL ) ;
if ( clksel & SDMMC_CLKSEL_WAKEUP_INT ) {
if ( priv - > ctrl_type = = DW_MCI_TYPE_EXYNOS7 | |
priv - > ctrl_type = = DW_MCI_TYPE_EXYNOS7_SMU )
mci_writel ( host , CLKSEL64 , clksel ) ;
else
mci_writel ( host , CLKSEL , clksel ) ;
}
2013-08-30 19:11:21 +04:00
return 0 ;
}
# else
# define dw_mci_exynos_suspend NULL
# define dw_mci_exynos_resume NULL
# define dw_mci_exynos_resume_noirq NULL
# endif /* CONFIG_PM_SLEEP */
2015-01-29 05:41:57 +03:00
static void dw_mci_exynos_config_hs400 ( struct dw_mci * host , u32 timing )
2012-09-17 22:16:43 +04:00
{
struct dw_mci_exynos_priv_data * priv = host - > priv ;
2015-01-29 05:41:57 +03:00
u32 dqs , strobe ;
2012-09-17 22:16:43 +04:00
2015-01-29 05:41:57 +03:00
/*
* Not supported to configure register
* related to HS400
*/
2016-07-14 16:22:27 +03:00
if ( priv - > ctrl_type < DW_MCI_TYPE_EXYNOS5420 ) {
if ( timing = = MMC_TIMING_MMC_HS400 )
dev_warn ( host - > dev ,
" cannot configure HS400, unsupported chipset \n " ) ;
2015-01-29 05:41:57 +03:00
return ;
2016-07-14 16:22:27 +03:00
}
2015-01-29 05:41:57 +03:00
dqs = priv - > saved_dqs_en ;
strobe = priv - > saved_strobe_ctrl ;
if ( timing = = MMC_TIMING_MMC_HS400 ) {
dqs | = DATA_STROBE_EN ;
strobe = DQS_CTRL_RD_DELAY ( strobe , priv - > dqs_delay ) ;
2013-08-30 19:13:03 +04:00
} else {
2015-01-29 05:41:57 +03:00
dqs & = ~ DATA_STROBE_EN ;
2013-08-30 19:13:03 +04:00
}
2015-01-29 05:41:57 +03:00
mci_writel ( host , HS400_DQS_EN , dqs ) ;
mci_writel ( host , HS400_DLINE_CTRL , strobe ) ;
}
static void dw_mci_exynos_adjust_clock ( struct dw_mci * host , unsigned int wanted )
{
struct dw_mci_exynos_priv_data * priv = host - > priv ;
unsigned long actual ;
u8 div ;
int ret ;
2014-12-22 15:12:03 +03:00
/*
* Don ' t care if wanted clock is zero or
* ciu clock is unavailable
*/
if ( ! wanted | | IS_ERR ( host - > ciu_clk ) )
2013-08-30 19:13:03 +04:00
return ;
/* Guaranteed minimum frequency for cclkin */
if ( wanted < EXYNOS_CCLKIN_MIN )
wanted = EXYNOS_CCLKIN_MIN ;
2015-01-29 05:41:57 +03:00
if ( wanted = = priv - > cur_speed )
return ;
div = dw_mci_exynos_get_ciu_div ( host ) ;
ret = clk_set_rate ( host - > ciu_clk , wanted * div ) ;
if ( ret )
dev_warn ( host - > dev ,
" failed to set clk-rate %u error: %d \n " ,
wanted * div , ret ) ;
actual = clk_get_rate ( host - > ciu_clk ) ;
host - > bus_hz = actual / div ;
priv - > cur_speed = wanted ;
host - > current_speed = 0 ;
}
static void dw_mci_exynos_set_ios ( struct dw_mci * host , struct mmc_ios * ios )
{
struct dw_mci_exynos_priv_data * priv = host - > priv ;
unsigned int wanted = ios - > clock ;
u32 timing = ios - > timing , clksel ;
switch ( timing ) {
case MMC_TIMING_MMC_HS400 :
/* Update tuned sample timing */
clksel = SDMMC_CLKSEL_UP_SAMPLE (
priv - > hs400_timing , priv - > tuned_sample ) ;
wanted < < = 1 ;
break ;
case MMC_TIMING_MMC_DDR52 :
clksel = priv - > ddr_timing ;
/* Should be double rate for DDR mode */
if ( ios - > bus_width = = MMC_BUS_WIDTH_8 )
wanted < < = 1 ;
break ;
default :
clksel = priv - > sdr_timing ;
2013-08-30 19:13:03 +04:00
}
2015-01-29 05:41:57 +03:00
/* Set clock timing for the requested speed mode*/
dw_mci_exynos_set_clksel_timing ( host , clksel ) ;
/* Configure setting for HS400 */
dw_mci_exynos_config_hs400 ( host , timing ) ;
/* Configure clock rate */
dw_mci_exynos_adjust_clock ( host , wanted ) ;
2012-09-17 22:16:43 +04:00
}
static int dw_mci_exynos_parse_dt ( struct dw_mci * host )
{
2013-08-30 19:11:57 +04:00
struct dw_mci_exynos_priv_data * priv ;
2012-09-17 22:16:43 +04:00
struct device_node * np = host - > dev - > of_node ;
u32 timing [ 2 ] ;
u32 div = 0 ;
2013-08-30 19:11:57 +04:00
int idx ;
2012-09-17 22:16:43 +04:00
int ret ;
2013-08-30 19:11:57 +04:00
priv = devm_kzalloc ( host - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
2014-12-23 15:07:33 +03:00
if ( ! priv )
2013-08-30 19:11:57 +04:00
return - ENOMEM ;
for ( idx = 0 ; idx < ARRAY_SIZE ( exynos_compat ) ; idx + + ) {
if ( of_device_is_compatible ( np , exynos_compat [ idx ] . compatible ) )
priv - > ctrl_type = exynos_compat [ idx ] . ctrl_type ;
}
2013-08-30 19:13:03 +04:00
if ( priv - > ctrl_type = = DW_MCI_TYPE_EXYNOS4412 )
priv - > ciu_div = EXYNOS4412_FIXED_CIU_CLK_DIV - 1 ;
else if ( priv - > ctrl_type = = DW_MCI_TYPE_EXYNOS4210 )
priv - > ciu_div = EXYNOS4210_FIXED_CIU_CLK_DIV - 1 ;
else {
of_property_read_u32 ( np , " samsung,dw-mshc-ciu-div " , & div ) ;
priv - > ciu_div = div ;
}
2012-09-17 22:16:43 +04:00
ret = of_property_read_u32_array ( np ,
" samsung,dw-mshc-sdr-timing " , timing , 2 ) ;
if ( ret )
return ret ;
2013-10-22 13:11:56 +04:00
priv - > sdr_timing = SDMMC_CLKSEL_TIMING ( timing [ 0 ] , timing [ 1 ] , div ) ;
2012-09-17 22:16:43 +04:00
ret = of_property_read_u32_array ( np ,
" samsung,dw-mshc-ddr-timing " , timing , 2 ) ;
if ( ret )
return ret ;
priv - > ddr_timing = SDMMC_CLKSEL_TIMING ( timing [ 0 ] , timing [ 1 ] , div ) ;
2015-01-29 05:41:57 +03:00
ret = of_property_read_u32_array ( np ,
" samsung,dw-mshc-hs400-timing " , timing , 2 ) ;
if ( ! ret & & of_property_read_u32 ( np ,
" samsung,read-strobe-delay " , & priv - > dqs_delay ) )
dev_dbg ( host - > dev ,
" read-strobe-delay is not found, assuming usage of default value \n " ) ;
priv - > hs400_timing = SDMMC_CLKSEL_TIMING ( timing [ 0 ] , timing [ 1 ] ,
HS400_FIXED_CIU_CLK_DIV ) ;
2013-08-30 19:11:57 +04:00
host - > priv = priv ;
2012-09-17 22:16:43 +04:00
return 0 ;
}
2013-08-30 19:12:50 +04:00
static inline u8 dw_mci_exynos_get_clksmpl ( struct dw_mci * host )
{
2014-08-28 17:18:53 +04:00
struct dw_mci_exynos_priv_data * priv = host - > priv ;
if ( priv - > ctrl_type = = DW_MCI_TYPE_EXYNOS7 | |
priv - > ctrl_type = = DW_MCI_TYPE_EXYNOS7_SMU )
return SDMMC_CLKSEL_CCLK_SAMPLE ( mci_readl ( host , CLKSEL64 ) ) ;
else
return SDMMC_CLKSEL_CCLK_SAMPLE ( mci_readl ( host , CLKSEL ) ) ;
2013-08-30 19:12:50 +04:00
}
static inline void dw_mci_exynos_set_clksmpl ( struct dw_mci * host , u8 sample )
{
u32 clksel ;
2014-08-28 17:18:53 +04:00
struct dw_mci_exynos_priv_data * priv = host - > priv ;
if ( priv - > ctrl_type = = DW_MCI_TYPE_EXYNOS7 | |
priv - > ctrl_type = = DW_MCI_TYPE_EXYNOS7_SMU )
clksel = mci_readl ( host , CLKSEL64 ) ;
else
clksel = mci_readl ( host , CLKSEL ) ;
2015-01-29 05:41:57 +03:00
clksel = SDMMC_CLKSEL_UP_SAMPLE ( clksel , sample ) ;
2014-08-28 17:18:53 +04:00
if ( priv - > ctrl_type = = DW_MCI_TYPE_EXYNOS7 | |
priv - > ctrl_type = = DW_MCI_TYPE_EXYNOS7_SMU )
mci_writel ( host , CLKSEL64 , clksel ) ;
else
mci_writel ( host , CLKSEL , clksel ) ;
2013-08-30 19:12:50 +04:00
}
static inline u8 dw_mci_exynos_move_next_clksmpl ( struct dw_mci * host )
{
2014-08-28 17:18:53 +04:00
struct dw_mci_exynos_priv_data * priv = host - > priv ;
2013-08-30 19:12:50 +04:00
u32 clksel ;
u8 sample ;
2014-08-28 17:18:53 +04:00
if ( priv - > ctrl_type = = DW_MCI_TYPE_EXYNOS7 | |
priv - > ctrl_type = = DW_MCI_TYPE_EXYNOS7_SMU )
clksel = mci_readl ( host , CLKSEL64 ) ;
else
clksel = mci_readl ( host , CLKSEL ) ;
2015-01-29 05:41:57 +03:00
2013-08-30 19:12:50 +04:00
sample = ( clksel + 1 ) & 0x7 ;
2015-01-29 05:41:57 +03:00
clksel = SDMMC_CLKSEL_UP_SAMPLE ( clksel , sample ) ;
2014-08-28 17:18:53 +04:00
if ( priv - > ctrl_type = = DW_MCI_TYPE_EXYNOS7 | |
priv - > ctrl_type = = DW_MCI_TYPE_EXYNOS7_SMU )
mci_writel ( host , CLKSEL64 , clksel ) ;
else
mci_writel ( host , CLKSEL , clksel ) ;
2015-01-29 05:41:57 +03:00
2013-08-30 19:12:50 +04:00
return sample ;
}
static s8 dw_mci_exynos_get_best_clksmpl ( u8 candiates )
{
const u8 iter = 8 ;
u8 __c ;
s8 i , loc = - 1 ;
for ( i = 0 ; i < iter ; i + + ) {
__c = ror8 ( candiates , i ) ;
if ( ( __c & 0xc7 ) = = 0xc7 ) {
loc = i ;
goto out ;
}
}
for ( i = 0 ; i < iter ; i + + ) {
__c = ror8 ( candiates , i ) ;
if ( ( __c & 0x83 ) = = 0x83 ) {
loc = i ;
goto out ;
}
}
out :
return loc ;
}
2015-10-27 09:24:28 +03:00
static int dw_mci_exynos_execute_tuning ( struct dw_mci_slot * slot , u32 opcode )
2013-08-30 19:12:50 +04:00
{
struct dw_mci * host = slot - > host ;
2015-01-29 05:41:57 +03:00
struct dw_mci_exynos_priv_data * priv = host - > priv ;
2013-08-30 19:12:50 +04:00
struct mmc_host * mmc = slot - > mmc ;
u8 start_smpl , smpl , candiates = 0 ;
s8 found = - 1 ;
int ret = 0 ;
start_smpl = dw_mci_exynos_get_clksmpl ( host ) ;
do {
mci_writel ( host , TMOUT , ~ 0 ) ;
smpl = dw_mci_exynos_move_next_clksmpl ( host ) ;
2015-10-27 09:24:28 +03:00
if ( ! mmc_send_tuning ( mmc , opcode , NULL ) )
2014-12-01 18:13:39 +03:00
candiates | = ( 1 < < smpl ) ;
2013-08-30 19:12:50 +04:00
} while ( start_smpl ! = smpl ) ;
found = dw_mci_exynos_get_best_clksmpl ( candiates ) ;
2015-01-29 05:41:57 +03:00
if ( found > = 0 ) {
2013-08-30 19:12:50 +04:00
dw_mci_exynos_set_clksmpl ( host , found ) ;
2015-01-29 05:41:57 +03:00
priv - > tuned_sample = found ;
} else {
2013-08-30 19:12:50 +04:00
ret = - EIO ;
2015-01-29 05:41:57 +03:00
}
2013-08-30 19:12:50 +04:00
return ret ;
}
2015-03-05 13:02:54 +03:00
static int dw_mci_exynos_prepare_hs400_tuning ( struct dw_mci * host ,
2015-01-29 05:41:57 +03:00
struct mmc_ios * ios )
{
struct dw_mci_exynos_priv_data * priv = host - > priv ;
dw_mci_exynos_set_clksel_timing ( host , priv - > hs400_timing ) ;
dw_mci_exynos_adjust_clock ( host , ( ios - > clock ) < < 1 ) ;
return 0 ;
}
2013-02-22 19:17:45 +04:00
/* Common capabilities of Exynos4/Exynos5 SoC */
static unsigned long exynos_dwmmc_caps [ 4 ] = {
2014-03-14 16:12:43 +04:00
MMC_CAP_1_8V_DDR | MMC_CAP_8_BIT_DATA | MMC_CAP_CMD23 ,
2012-09-17 22:16:43 +04:00
MMC_CAP_CMD23 ,
MMC_CAP_CMD23 ,
MMC_CAP_CMD23 ,
} ;
2013-02-22 19:17:45 +04:00
static const struct dw_mci_drv_data exynos_drv_data = {
. caps = exynos_dwmmc_caps ,
2012-09-17 22:16:43 +04:00
. init = dw_mci_exynos_priv_init ,
. set_ios = dw_mci_exynos_set_ios ,
. parse_dt = dw_mci_exynos_parse_dt ,
2013-08-30 19:12:50 +04:00
. execute_tuning = dw_mci_exynos_execute_tuning ,
2015-01-29 05:41:57 +03:00
. prepare_hs400_tuning = dw_mci_exynos_prepare_hs400_tuning ,
2012-09-17 22:16:43 +04:00
} ;
static const struct of_device_id dw_mci_exynos_match [ ] = {
2013-02-22 19:17:45 +04:00
{ . compatible = " samsung,exynos4412-dw-mshc " ,
. data = & exynos_drv_data , } ,
2012-09-17 22:16:43 +04:00
{ . compatible = " samsung,exynos5250-dw-mshc " ,
2013-02-22 19:17:45 +04:00
. data = & exynos_drv_data , } ,
2013-05-24 14:04:32 +04:00
{ . compatible = " samsung,exynos5420-dw-mshc " ,
. data = & exynos_drv_data , } ,
2013-08-30 19:12:35 +04:00
{ . compatible = " samsung,exynos5420-dw-mshc-smu " ,
. data = & exynos_drv_data , } ,
2014-08-28 17:18:53 +04:00
{ . compatible = " samsung,exynos7-dw-mshc " ,
. data = & exynos_drv_data , } ,
{ . compatible = " samsung,exynos7-dw-mshc-smu " ,
. data = & exynos_drv_data , } ,
2012-09-17 22:16:43 +04:00
{ } ,
} ;
2012-11-07 01:55:30 +04:00
MODULE_DEVICE_TABLE ( of , dw_mci_exynos_match ) ;
2012-09-17 22:16:43 +04:00
2013-02-18 12:53:08 +04:00
static int dw_mci_exynos_probe ( struct platform_device * pdev )
2012-09-17 22:16:43 +04:00
{
2012-11-07 01:55:31 +04:00
const struct dw_mci_drv_data * drv_data ;
2012-09-17 22:16:43 +04:00
const struct of_device_id * match ;
match = of_match_node ( dw_mci_exynos_match , pdev - > dev . of_node ) ;
drv_data = match - > data ;
return dw_mci_pltfm_register ( pdev , drv_data ) ;
}
2014-03-04 09:03:25 +04:00
static const struct dev_pm_ops dw_mci_exynos_pmops = {
2013-08-30 19:11:21 +04:00
SET_SYSTEM_SLEEP_PM_OPS ( dw_mci_exynos_suspend , dw_mci_exynos_resume )
. resume_noirq = dw_mci_exynos_resume_noirq ,
. thaw_noirq = dw_mci_exynos_resume_noirq ,
. restore_noirq = dw_mci_exynos_resume_noirq ,
} ;
2012-09-17 22:16:43 +04:00
static struct platform_driver dw_mci_exynos_pltfm_driver = {
. probe = dw_mci_exynos_probe ,
2015-01-24 03:33:40 +03:00
. remove = dw_mci_pltfm_remove ,
2012-09-17 22:16:43 +04:00
. driver = {
. name = " dwmmc_exynos " ,
2013-02-18 12:53:09 +04:00
. of_match_table = dw_mci_exynos_match ,
2013-08-30 19:11:21 +04:00
. pm = & dw_mci_exynos_pmops ,
2012-09-17 22:16:43 +04:00
} ,
} ;
module_platform_driver ( dw_mci_exynos_pltfm_driver ) ;
MODULE_DESCRIPTION ( " Samsung Specific DW-MSHC Driver Extension " ) ;
MODULE_AUTHOR ( " Thomas Abraham <thomas.ab@samsung.com " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
2015-05-14 11:59:45 +03:00
MODULE_ALIAS ( " platform:dwmmc_exynos " ) ;