2014-03-10 19:37:12 +04:00
/*
* drivers / mmc / host / sdhci - msm . c - Qualcomm SDHCI Platform driver
*
* Copyright ( c ) 2013 - 2014 , The Linux Foundation . All rights reserved .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
*/
# include <linux/module.h>
# include <linux/of_device.h>
# include <linux/delay.h>
2014-03-10 19:37:13 +04:00
# include <linux/mmc/mmc.h>
# include <linux/slab.h>
2014-03-10 19:37:12 +04:00
# include "sdhci-pltfm.h"
2015-03-23 19:47:29 +03:00
# define CORE_MCI_VERSION 0x50
# define CORE_VERSION_MAJOR_SHIFT 28
# define CORE_VERSION_MAJOR_MASK (0xf << CORE_VERSION_MAJOR_SHIFT)
# define CORE_VERSION_MINOR_MASK 0xff
2014-03-10 19:37:12 +04:00
# define CORE_HC_MODE 0x78
# define HC_MODE_EN 0x1
# define CORE_POWER 0x0
# define CORE_SW_RST BIT(7)
2014-03-10 19:37:13 +04:00
# define MAX_PHASES 16
# define CORE_DLL_LOCK BIT(7)
# define CORE_DLL_EN BIT(16)
# define CORE_CDR_EN BIT(17)
# define CORE_CK_OUT_EN BIT(18)
# define CORE_CDR_EXT_EN BIT(19)
# define CORE_DLL_PDN BIT(29)
# define CORE_DLL_RST BIT(30)
# define CORE_DLL_CONFIG 0x100
# define CORE_DLL_STATUS 0x108
# define CORE_VENDOR_SPEC 0x10c
# define CORE_CLK_PWRSAVE BIT(1)
2015-03-23 19:47:29 +03:00
# define CORE_VENDOR_SPEC_CAPABILITIES0 0x11c
2014-03-10 19:37:13 +04:00
# define CDR_SELEXT_SHIFT 20
# define CDR_SELEXT_MASK (0xf << CDR_SELEXT_SHIFT)
# define CMUX_SHIFT_PHASE_SHIFT 24
# define CMUX_SHIFT_PHASE_MASK (7 << CMUX_SHIFT_PHASE_SHIFT)
2014-03-10 19:37:12 +04:00
struct sdhci_msm_host {
struct platform_device * pdev ;
void __iomem * core_mem ; /* MSM SDCC mapped address */
struct clk * clk ; /* main SD/MMC bus clock */
struct clk * pclk ; /* SDHC peripheral bus clock */
struct clk * bus_clk ; /* SDHC bus voter clock */
struct mmc_host * mmc ;
} ;
/* Platform specific tuning */
2014-03-10 19:37:13 +04:00
static inline int msm_dll_poll_ck_out_en ( struct sdhci_host * host , u8 poll )
{
u32 wait_cnt = 50 ;
u8 ck_out_en ;
struct mmc_host * mmc = host - > mmc ;
/* Poll for CK_OUT_EN bit. max. poll time = 50us */
ck_out_en = ! ! ( readl_relaxed ( host - > ioaddr + CORE_DLL_CONFIG ) &
CORE_CK_OUT_EN ) ;
while ( ck_out_en ! = poll ) {
if ( - - wait_cnt = = 0 ) {
dev_err ( mmc_dev ( mmc ) , " %s: CK_OUT_EN bit is not %d \n " ,
mmc_hostname ( mmc ) , poll ) ;
return - ETIMEDOUT ;
}
udelay ( 1 ) ;
ck_out_en = ! ! ( readl_relaxed ( host - > ioaddr + CORE_DLL_CONFIG ) &
CORE_CK_OUT_EN ) ;
}
return 0 ;
}
static int msm_config_cm_dll_phase ( struct sdhci_host * host , u8 phase )
{
int rc ;
static const u8 grey_coded_phase_table [ ] = {
0x0 , 0x1 , 0x3 , 0x2 , 0x6 , 0x7 , 0x5 , 0x4 ,
0xc , 0xd , 0xf , 0xe , 0xa , 0xb , 0x9 , 0x8
} ;
unsigned long flags ;
u32 config ;
struct mmc_host * mmc = host - > mmc ;
spin_lock_irqsave ( & host - > lock , flags ) ;
config = readl_relaxed ( host - > ioaddr + CORE_DLL_CONFIG ) ;
config & = ~ ( CORE_CDR_EN | CORE_CK_OUT_EN ) ;
config | = ( CORE_CDR_EXT_EN | CORE_DLL_EN ) ;
writel_relaxed ( config , host - > ioaddr + CORE_DLL_CONFIG ) ;
/* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '0' */
rc = msm_dll_poll_ck_out_en ( host , 0 ) ;
if ( rc )
goto err_out ;
/*
* Write the selected DLL clock output phase ( 0 . . . 15 )
* to CDR_SELEXT bit field of DLL_CONFIG register .
*/
config = readl_relaxed ( host - > ioaddr + CORE_DLL_CONFIG ) ;
config & = ~ CDR_SELEXT_MASK ;
config | = grey_coded_phase_table [ phase ] < < CDR_SELEXT_SHIFT ;
writel_relaxed ( config , host - > ioaddr + CORE_DLL_CONFIG ) ;
/* Set CK_OUT_EN bit of DLL_CONFIG register to 1. */
writel_relaxed ( ( readl_relaxed ( host - > ioaddr + CORE_DLL_CONFIG )
| CORE_CK_OUT_EN ) , host - > ioaddr + CORE_DLL_CONFIG ) ;
/* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '1' */
rc = msm_dll_poll_ck_out_en ( host , 1 ) ;
if ( rc )
goto err_out ;
config = readl_relaxed ( host - > ioaddr + CORE_DLL_CONFIG ) ;
config | = CORE_CDR_EN ;
config & = ~ CORE_CDR_EXT_EN ;
writel_relaxed ( config , host - > ioaddr + CORE_DLL_CONFIG ) ;
goto out ;
err_out :
dev_err ( mmc_dev ( mmc ) , " %s: Failed to set DLL phase: %d \n " ,
mmc_hostname ( mmc ) , phase ) ;
out :
spin_unlock_irqrestore ( & host - > lock , flags ) ;
return rc ;
}
/*
* Find out the greatest range of consecuitive selected
* DLL clock output phases that can be used as sampling
* setting for SD3 .0 UHS - I card read operation ( in SDR104
* timing mode ) or for eMMC4 .5 card read operation ( in HS200
* timing mode ) .
* Select the 3 / 4 of the range and configure the DLL with the
* selected DLL clock output phase .
*/
static int msm_find_most_appropriate_phase ( struct sdhci_host * host ,
u8 * phase_table , u8 total_phases )
{
int ret ;
u8 ranges [ MAX_PHASES ] [ MAX_PHASES ] = { { 0 } , { 0 } } ;
u8 phases_per_row [ MAX_PHASES ] = { 0 } ;
int row_index = 0 , col_index = 0 , selected_row_index = 0 , curr_max = 0 ;
int i , cnt , phase_0_raw_index = 0 , phase_15_raw_index = 0 ;
bool phase_0_found = false , phase_15_found = false ;
struct mmc_host * mmc = host - > mmc ;
if ( ! total_phases | | ( total_phases > MAX_PHASES ) ) {
dev_err ( mmc_dev ( mmc ) , " %s: Invalid argument: total_phases=%d \n " ,
mmc_hostname ( mmc ) , total_phases ) ;
return - EINVAL ;
}
for ( cnt = 0 ; cnt < total_phases ; cnt + + ) {
ranges [ row_index ] [ col_index ] = phase_table [ cnt ] ;
phases_per_row [ row_index ] + = 1 ;
col_index + + ;
if ( ( cnt + 1 ) = = total_phases ) {
continue ;
/* check if next phase in phase_table is consecutive or not */
} else if ( ( phase_table [ cnt ] + 1 ) ! = phase_table [ cnt + 1 ] ) {
row_index + + ;
col_index = 0 ;
}
}
if ( row_index > = MAX_PHASES )
return - EINVAL ;
/* Check if phase-0 is present in first valid window? */
if ( ! ranges [ 0 ] [ 0 ] ) {
phase_0_found = true ;
phase_0_raw_index = 0 ;
/* Check if cycle exist between 2 valid windows */
for ( cnt = 1 ; cnt < = row_index ; cnt + + ) {
if ( phases_per_row [ cnt ] ) {
for ( i = 0 ; i < phases_per_row [ cnt ] ; i + + ) {
if ( ranges [ cnt ] [ i ] = = 15 ) {
phase_15_found = true ;
phase_15_raw_index = cnt ;
break ;
}
}
}
}
}
/* If 2 valid windows form cycle then merge them as single window */
if ( phase_0_found & & phase_15_found ) {
/* number of phases in raw where phase 0 is present */
u8 phases_0 = phases_per_row [ phase_0_raw_index ] ;
/* number of phases in raw where phase 15 is present */
u8 phases_15 = phases_per_row [ phase_15_raw_index ] ;
if ( phases_0 + phases_15 > = MAX_PHASES )
/*
* If there are more than 1 phase windows then total
* number of phases in both the windows should not be
* more than or equal to MAX_PHASES .
*/
return - EINVAL ;
/* Merge 2 cyclic windows */
i = phases_15 ;
for ( cnt = 0 ; cnt < phases_0 ; cnt + + ) {
ranges [ phase_15_raw_index ] [ i ] =
ranges [ phase_0_raw_index ] [ cnt ] ;
if ( + + i > = MAX_PHASES )
break ;
}
phases_per_row [ phase_0_raw_index ] = 0 ;
phases_per_row [ phase_15_raw_index ] = phases_15 + phases_0 ;
}
for ( cnt = 0 ; cnt < = row_index ; cnt + + ) {
if ( phases_per_row [ cnt ] > curr_max ) {
curr_max = phases_per_row [ cnt ] ;
selected_row_index = cnt ;
}
}
i = ( curr_max * 3 ) / 4 ;
if ( i )
i - - ;
ret = ranges [ selected_row_index ] [ i ] ;
if ( ret > = MAX_PHASES ) {
ret = - EINVAL ;
dev_err ( mmc_dev ( mmc ) , " %s: Invalid phase selected=%d \n " ,
mmc_hostname ( mmc ) , ret ) ;
}
return ret ;
}
static inline void msm_cm_dll_set_freq ( struct sdhci_host * host )
2014-03-10 19:37:12 +04:00
{
2014-03-10 19:37:13 +04:00
u32 mclk_freq = 0 , config ;
/* Program the MCLK value to MCLK_FREQ bit field */
if ( host - > clock < = 112000000 )
mclk_freq = 0 ;
else if ( host - > clock < = 125000000 )
mclk_freq = 1 ;
else if ( host - > clock < = 137000000 )
mclk_freq = 2 ;
else if ( host - > clock < = 150000000 )
mclk_freq = 3 ;
else if ( host - > clock < = 162000000 )
mclk_freq = 4 ;
else if ( host - > clock < = 175000000 )
mclk_freq = 5 ;
else if ( host - > clock < = 187000000 )
mclk_freq = 6 ;
else if ( host - > clock < = 200000000 )
mclk_freq = 7 ;
config = readl_relaxed ( host - > ioaddr + CORE_DLL_CONFIG ) ;
config & = ~ CMUX_SHIFT_PHASE_MASK ;
config | = mclk_freq < < CMUX_SHIFT_PHASE_SHIFT ;
writel_relaxed ( config , host - > ioaddr + CORE_DLL_CONFIG ) ;
}
/* Initialize the DLL (Programmable Delay Line) */
static int msm_init_cm_dll ( struct sdhci_host * host )
{
struct mmc_host * mmc = host - > mmc ;
int wait_cnt = 50 ;
unsigned long flags ;
spin_lock_irqsave ( & host - > lock , flags ) ;
2014-03-10 19:37:12 +04:00
/*
2014-03-10 19:37:13 +04:00
* Make sure that clock is always enabled when DLL
* tuning is in progress . Keeping PWRSAVE ON may
* turn off the clock .
2014-03-10 19:37:12 +04:00
*/
2014-03-10 19:37:13 +04:00
writel_relaxed ( ( readl_relaxed ( host - > ioaddr + CORE_VENDOR_SPEC )
& ~ CORE_CLK_PWRSAVE ) , host - > ioaddr + CORE_VENDOR_SPEC ) ;
/* Write 1 to DLL_RST bit of DLL_CONFIG register */
writel_relaxed ( ( readl_relaxed ( host - > ioaddr + CORE_DLL_CONFIG )
| CORE_DLL_RST ) , host - > ioaddr + CORE_DLL_CONFIG ) ;
/* Write 1 to DLL_PDN bit of DLL_CONFIG register */
writel_relaxed ( ( readl_relaxed ( host - > ioaddr + CORE_DLL_CONFIG )
| CORE_DLL_PDN ) , host - > ioaddr + CORE_DLL_CONFIG ) ;
msm_cm_dll_set_freq ( host ) ;
/* Write 0 to DLL_RST bit of DLL_CONFIG register */
writel_relaxed ( ( readl_relaxed ( host - > ioaddr + CORE_DLL_CONFIG )
& ~ CORE_DLL_RST ) , host - > ioaddr + CORE_DLL_CONFIG ) ;
/* Write 0 to DLL_PDN bit of DLL_CONFIG register */
writel_relaxed ( ( readl_relaxed ( host - > ioaddr + CORE_DLL_CONFIG )
& ~ CORE_DLL_PDN ) , host - > ioaddr + CORE_DLL_CONFIG ) ;
/* Set DLL_EN bit to 1. */
writel_relaxed ( ( readl_relaxed ( host - > ioaddr + CORE_DLL_CONFIG )
| CORE_DLL_EN ) , host - > ioaddr + CORE_DLL_CONFIG ) ;
/* Set CK_OUT_EN bit to 1. */
writel_relaxed ( ( readl_relaxed ( host - > ioaddr + CORE_DLL_CONFIG )
| CORE_CK_OUT_EN ) , host - > ioaddr + CORE_DLL_CONFIG ) ;
/* Wait until DLL_LOCK bit of DLL_STATUS register becomes '1' */
while ( ! ( readl_relaxed ( host - > ioaddr + CORE_DLL_STATUS ) &
CORE_DLL_LOCK ) ) {
/* max. wait for 50us sec for LOCK bit to be set */
if ( - - wait_cnt = = 0 ) {
dev_err ( mmc_dev ( mmc ) , " %s: DLL failed to LOCK \n " ,
mmc_hostname ( mmc ) ) ;
spin_unlock_irqrestore ( & host - > lock , flags ) ;
return - ETIMEDOUT ;
}
udelay ( 1 ) ;
}
spin_unlock_irqrestore ( & host - > lock , flags ) ;
2014-03-10 19:37:12 +04:00
return 0 ;
}
2014-03-10 19:37:13 +04:00
static int sdhci_msm_execute_tuning ( struct sdhci_host * host , u32 opcode )
{
int tuning_seq_cnt = 3 ;
2014-12-05 14:59:41 +03:00
u8 phase , tuned_phases [ 16 ] , tuned_phase_cnt = 0 ;
2014-03-10 19:37:13 +04:00
int rc ;
struct mmc_host * mmc = host - > mmc ;
struct mmc_ios ios = host - > mmc - > ios ;
/*
* Tuning is required for SDR104 , HS200 and HS400 cards and
* if clock frequency is greater than 100 MHz in these modes .
*/
if ( host - > clock < = 100 * 1000 * 1000 | |
! ( ( ios . timing = = MMC_TIMING_MMC_HS200 ) | |
( ios . timing = = MMC_TIMING_UHS_SDR104 ) ) )
return 0 ;
retry :
/* First of all reset the tuning block */
rc = msm_init_cm_dll ( host ) ;
if ( rc )
2014-12-05 14:59:41 +03:00
return rc ;
2014-03-10 19:37:13 +04:00
phase = 0 ;
do {
/* Set the phase in delay line hw block */
rc = msm_config_cm_dll_phase ( host , phase ) ;
if ( rc )
2014-12-05 14:59:41 +03:00
return rc ;
2014-03-10 19:37:13 +04:00
2015-10-27 09:24:28 +03:00
rc = mmc_send_tuning ( mmc , opcode , NULL ) ;
2014-12-05 14:59:41 +03:00
if ( ! rc ) {
2014-03-10 19:37:13 +04:00
/* Tuning is successful at this tuning point */
tuned_phases [ tuned_phase_cnt + + ] = phase ;
dev_dbg ( mmc_dev ( mmc ) , " %s: Found good phase = %d \n " ,
mmc_hostname ( mmc ) , phase ) ;
}
} while ( + + phase < ARRAY_SIZE ( tuned_phases ) ) ;
if ( tuned_phase_cnt ) {
rc = msm_find_most_appropriate_phase ( host , tuned_phases ,
tuned_phase_cnt ) ;
if ( rc < 0 )
2014-12-05 14:59:41 +03:00
return rc ;
2014-03-10 19:37:13 +04:00
else
phase = rc ;
/*
* Finally set the selected phase in delay
* line hw block .
*/
rc = msm_config_cm_dll_phase ( host , phase ) ;
if ( rc )
2014-12-05 14:59:41 +03:00
return rc ;
2014-03-10 19:37:13 +04:00
dev_dbg ( mmc_dev ( mmc ) , " %s: Setting the tuning phase to %d \n " ,
mmc_hostname ( mmc ) , phase ) ;
} else {
if ( - - tuning_seq_cnt )
goto retry ;
/* Tuning failed */
dev_dbg ( mmc_dev ( mmc ) , " %s: No tuning point found \n " ,
mmc_hostname ( mmc ) ) ;
rc = - EIO ;
}
return rc ;
}
2016-07-19 17:52:25 +03:00
static void sdhci_msm_set_uhs_signaling ( struct sdhci_host * host ,
unsigned int uhs )
{
struct mmc_host * mmc = host - > mmc ;
u16 ctrl_2 ;
ctrl_2 = sdhci_readw ( host , SDHCI_HOST_CONTROL2 ) ;
/* Select Bus Speed Mode for host */
ctrl_2 & = ~ SDHCI_CTRL_UHS_MASK ;
switch ( uhs ) {
case MMC_TIMING_UHS_SDR12 :
ctrl_2 | = SDHCI_CTRL_UHS_SDR12 ;
break ;
case MMC_TIMING_UHS_SDR25 :
ctrl_2 | = SDHCI_CTRL_UHS_SDR25 ;
break ;
case MMC_TIMING_UHS_SDR50 :
ctrl_2 | = SDHCI_CTRL_UHS_SDR50 ;
break ;
case MMC_TIMING_MMC_HS200 :
case MMC_TIMING_UHS_SDR104 :
ctrl_2 | = SDHCI_CTRL_UHS_SDR104 ;
break ;
case MMC_TIMING_UHS_DDR50 :
case MMC_TIMING_MMC_DDR52 :
ctrl_2 | = SDHCI_CTRL_UHS_DDR50 ;
break ;
}
/*
* When clock frequency is less than 100 MHz , the feedback clock must be
* provided and DLL must not be used so that tuning can be skipped . To
* provide feedback clock , the mode selection can be any value less
* than 3 ' b011 in bits [ 2 : 0 ] of HOST CONTROL2 register .
*/
if ( host - > clock < = 100000000 & &
( uhs = = MMC_TIMING_MMC_HS400 | |
uhs = = MMC_TIMING_MMC_HS200 | |
uhs = = MMC_TIMING_UHS_SDR104 ) )
ctrl_2 & = ~ SDHCI_CTRL_UHS_MASK ;
dev_dbg ( mmc_dev ( mmc ) , " %s: clock=%u uhs=%u ctrl_2=0x%x \n " ,
mmc_hostname ( host - > mmc ) , host - > clock , uhs , ctrl_2 ) ;
sdhci_writew ( host , ctrl_2 , SDHCI_HOST_CONTROL2 ) ;
}
2014-03-10 19:37:12 +04:00
static const struct of_device_id sdhci_msm_dt_match [ ] = {
{ . compatible = " qcom,sdhci-msm-v4 " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , sdhci_msm_dt_match ) ;
2016-02-16 16:08:21 +03:00
static const struct sdhci_ops sdhci_msm_ops = {
2014-03-10 19:37:12 +04:00
. platform_execute_tuning = sdhci_msm_execute_tuning ,
2014-06-10 22:27:19 +04:00
. reset = sdhci_reset ,
. set_clock = sdhci_set_clock ,
. set_bus_width = sdhci_set_bus_width ,
2016-07-19 17:52:25 +03:00
. set_uhs_signaling = sdhci_msm_set_uhs_signaling ,
2014-03-10 19:37:12 +04:00
} ;
2016-02-16 16:08:21 +03:00
static const struct sdhci_pltfm_data sdhci_msm_pdata = {
. quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
2016-06-24 19:24:59 +03:00
SDHCI_QUIRK_NO_CARD_NO_RESET |
2016-02-16 16:08:21 +03:00
SDHCI_QUIRK_SINGLE_POWER_WRITE ,
. ops = & sdhci_msm_ops ,
} ;
2014-03-10 19:37:12 +04:00
static int sdhci_msm_probe ( struct platform_device * pdev )
{
struct sdhci_host * host ;
struct sdhci_pltfm_host * pltfm_host ;
struct sdhci_msm_host * msm_host ;
struct resource * core_memres ;
int ret ;
2015-03-23 19:47:29 +03:00
u16 host_version , core_minor ;
u32 core_version , caps ;
u8 core_major ;
2014-03-10 19:37:12 +04:00
2016-02-16 16:08:22 +03:00
host = sdhci_pltfm_init ( pdev , & sdhci_msm_pdata , sizeof ( * msm_host ) ) ;
2014-03-10 19:37:12 +04:00
if ( IS_ERR ( host ) )
return PTR_ERR ( host ) ;
pltfm_host = sdhci_priv ( host ) ;
2016-02-16 16:08:22 +03:00
msm_host = sdhci_pltfm_priv ( pltfm_host ) ;
2014-03-10 19:37:12 +04:00
msm_host - > mmc = host - > mmc ;
msm_host - > pdev = pdev ;
ret = mmc_of_parse ( host - > mmc ) ;
if ( ret )
goto pltfm_free ;
sdhci_get_of_property ( pdev ) ;
/* Setup SDCC bus voter clock. */
msm_host - > bus_clk = devm_clk_get ( & pdev - > dev , " bus " ) ;
if ( ! IS_ERR ( msm_host - > bus_clk ) ) {
/* Vote for max. clk rate for max. performance */
ret = clk_set_rate ( msm_host - > bus_clk , INT_MAX ) ;
if ( ret )
goto pltfm_free ;
ret = clk_prepare_enable ( msm_host - > bus_clk ) ;
if ( ret )
goto pltfm_free ;
}
/* Setup main peripheral bus clock */
msm_host - > pclk = devm_clk_get ( & pdev - > dev , " iface " ) ;
if ( IS_ERR ( msm_host - > pclk ) ) {
ret = PTR_ERR ( msm_host - > pclk ) ;
2016-06-23 12:52:05 +03:00
dev_err ( & pdev - > dev , " Peripheral clk setup failed (%d) \n " , ret ) ;
2014-03-10 19:37:12 +04:00
goto bus_clk_disable ;
}
ret = clk_prepare_enable ( msm_host - > pclk ) ;
if ( ret )
goto bus_clk_disable ;
/* Setup SDC MMC clock */
msm_host - > clk = devm_clk_get ( & pdev - > dev , " core " ) ;
if ( IS_ERR ( msm_host - > clk ) ) {
ret = PTR_ERR ( msm_host - > clk ) ;
dev_err ( & pdev - > dev , " SDC MMC clk setup failed (%d) \n " , ret ) ;
goto pclk_disable ;
}
2015-07-06 14:53:38 +03:00
/* Vote for maximum clock rate for maximum performance */
ret = clk_set_rate ( msm_host - > clk , INT_MAX ) ;
if ( ret )
dev_warn ( & pdev - > dev , " core clock boost failed \n " ) ;
2014-03-10 19:37:12 +04:00
ret = clk_prepare_enable ( msm_host - > clk ) ;
if ( ret )
goto pclk_disable ;
core_memres = platform_get_resource ( pdev , IORESOURCE_MEM , 1 ) ;
msm_host - > core_mem = devm_ioremap_resource ( & pdev - > dev , core_memres ) ;
if ( IS_ERR ( msm_host - > core_mem ) ) {
dev_err ( & pdev - > dev , " Failed to remap registers \n " ) ;
ret = PTR_ERR ( msm_host - > core_mem ) ;
goto clk_disable ;
}
/* Reset the core and Enable SDHC mode */
writel_relaxed ( readl_relaxed ( msm_host - > core_mem + CORE_POWER ) |
CORE_SW_RST , msm_host - > core_mem + CORE_POWER ) ;
/* SW reset can take upto 10HCLK + 15MCLK cycles. (min 40us) */
usleep_range ( 1000 , 5000 ) ;
if ( readl ( msm_host - > core_mem + CORE_POWER ) & CORE_SW_RST ) {
dev_err ( & pdev - > dev , " Stuck in reset \n " ) ;
ret = - ETIMEDOUT ;
goto clk_disable ;
}
/* Set HC_MODE_EN bit in HC_MODE register */
writel_relaxed ( HC_MODE_EN , ( msm_host - > core_mem + CORE_HC_MODE ) ) ;
host_version = readw_relaxed ( ( host - > ioaddr + SDHCI_HOST_VERSION ) ) ;
dev_dbg ( & pdev - > dev , " Host Version: 0x%x Vendor Version 0x%x \n " ,
host_version , ( ( host_version & SDHCI_VENDOR_VER_MASK ) > >
SDHCI_VENDOR_VER_SHIFT ) ) ;
2015-03-23 19:47:29 +03:00
core_version = readl_relaxed ( msm_host - > core_mem + CORE_MCI_VERSION ) ;
core_major = ( core_version & CORE_VERSION_MAJOR_MASK ) > >
CORE_VERSION_MAJOR_SHIFT ;
core_minor = core_version & CORE_VERSION_MINOR_MASK ;
dev_dbg ( & pdev - > dev , " MCI Version: 0x%08x, major: 0x%04x, minor: 0x%02x \n " ,
core_version , core_major , core_minor ) ;
/*
* Support for some capabilities is not advertised by newer
* controller versions and must be explicitly enabled .
*/
if ( core_major > = 1 & & core_minor ! = 0x11 & & core_minor ! = 0x12 ) {
caps = readl_relaxed ( host - > ioaddr + SDHCI_CAPABILITIES ) ;
caps | = SDHCI_CAN_VDD_300 | SDHCI_CAN_DO_8BIT ;
writel_relaxed ( caps , host - > ioaddr +
CORE_VENDOR_SPEC_CAPABILITIES0 ) ;
}
2014-03-10 19:37:12 +04:00
ret = sdhci_add_host ( host ) ;
if ( ret )
goto clk_disable ;
return 0 ;
clk_disable :
clk_disable_unprepare ( msm_host - > clk ) ;
pclk_disable :
clk_disable_unprepare ( msm_host - > pclk ) ;
bus_clk_disable :
if ( ! IS_ERR ( msm_host - > bus_clk ) )
clk_disable_unprepare ( msm_host - > bus_clk ) ;
pltfm_free :
sdhci_pltfm_free ( pdev ) ;
return ret ;
}
static int sdhci_msm_remove ( struct platform_device * pdev )
{
struct sdhci_host * host = platform_get_drvdata ( pdev ) ;
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
2016-02-16 16:08:22 +03:00
struct sdhci_msm_host * msm_host = sdhci_pltfm_priv ( pltfm_host ) ;
2014-03-10 19:37:12 +04:00
int dead = ( readl_relaxed ( host - > ioaddr + SDHCI_INT_STATUS ) = =
0xffffffff ) ;
sdhci_remove_host ( host , dead ) ;
clk_disable_unprepare ( msm_host - > clk ) ;
clk_disable_unprepare ( msm_host - > pclk ) ;
if ( ! IS_ERR ( msm_host - > bus_clk ) )
clk_disable_unprepare ( msm_host - > bus_clk ) ;
2016-02-16 16:08:22 +03:00
sdhci_pltfm_free ( pdev ) ;
2014-03-10 19:37:12 +04:00
return 0 ;
}
static struct platform_driver sdhci_msm_driver = {
. probe = sdhci_msm_probe ,
. remove = sdhci_msm_remove ,
. driver = {
. name = " sdhci_msm " ,
. of_match_table = sdhci_msm_dt_match ,
} ,
} ;
module_platform_driver ( sdhci_msm_driver ) ;
MODULE_DESCRIPTION ( " Qualcomm Secure Digital Host Controller Interface driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;