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>
2016-10-21 09:42:04 +03:00
# include <linux/pm_runtime.h>
2014-03-10 19:37:13 +04:00
# include <linux/slab.h>
2016-11-21 09:37:25 +03:00
# include <linux/iopoll.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)
2016-11-21 09:37:23 +03:00
# define FF_CLK_SW_RST_DIS BIT(13)
2014-03-10 19:37:12 +04:00
2016-06-24 18:07:14 +03:00
# define CORE_PWRCTL_STATUS 0xdc
# define CORE_PWRCTL_MASK 0xe0
# define CORE_PWRCTL_CLEAR 0xe4
# define CORE_PWRCTL_CTL 0xe8
# define CORE_PWRCTL_BUS_OFF BIT(0)
# define CORE_PWRCTL_BUS_ON BIT(1)
# define CORE_PWRCTL_IO_LOW BIT(2)
# define CORE_PWRCTL_IO_HIGH BIT(3)
# define CORE_PWRCTL_BUS_SUCCESS BIT(0)
# define CORE_PWRCTL_IO_SUCCESS BIT(2)
# define REQ_BUS_OFF BIT(0)
# define REQ_BUS_ON BIT(1)
# define REQ_IO_LOW BIT(2)
# define REQ_IO_HIGH BIT(3)
# define INT_MASK 0xf
2014-03-10 19:37:13 +04:00
# define MAX_PHASES 16
# define CORE_DLL_LOCK BIT(7)
2016-11-21 09:37:26 +03:00
# define CORE_DDR_DLL_LOCK BIT(11)
2014-03-10 19:37:13 +04:00
# 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
2016-11-21 09:37:25 +03:00
# define CORE_CMD_DAT_TRACK_SEL BIT(0)
2014-03-10 19:37:13 +04:00
# define CORE_DLL_STATUS 0x108
2016-11-21 09:37:16 +03:00
# define CORE_DLL_CONFIG_2 0x1b4
2016-11-21 09:37:26 +03:00
# define CORE_DDR_CAL_EN BIT(0)
2016-11-21 09:37:16 +03:00
# define CORE_FLL_CYCLE_CNT BIT(18)
# define CORE_DLL_CLOCK_DISABLE BIT(21)
2014-03-10 19:37:13 +04:00
# define CORE_VENDOR_SPEC 0x10c
# define CORE_CLK_PWRSAVE BIT(1)
2016-11-21 09:37:23 +03:00
# define CORE_HC_MCLK_SEL_DFLT (2 << 8)
# define CORE_HC_MCLK_SEL_HS400 (3 << 8)
# define CORE_HC_MCLK_SEL_MASK (3 << 8)
# define CORE_HC_SELECT_IN_EN BIT(18)
# define CORE_HC_SELECT_IN_HS400 (6 << 19)
# define CORE_HC_SELECT_IN_MASK (7 << 19)
2014-03-10 19:37:13 +04:00
2016-11-21 09:37:25 +03:00
# define CORE_CSR_CDC_CTLR_CFG0 0x130
# define CORE_SW_TRIG_FULL_CALIB BIT(16)
# define CORE_HW_AUTOCAL_ENA BIT(17)
# define CORE_CSR_CDC_CTLR_CFG1 0x134
# define CORE_CSR_CDC_CAL_TIMER_CFG0 0x138
# define CORE_TIMER_ENA BIT(16)
# define CORE_CSR_CDC_CAL_TIMER_CFG1 0x13C
# define CORE_CSR_CDC_REFCOUNT_CFG 0x140
# define CORE_CSR_CDC_COARSE_CAL_CFG 0x144
# define CORE_CDC_OFFSET_CFG 0x14C
# define CORE_CSR_CDC_DELAY_CFG 0x150
# define CORE_CDC_SLAVE_DDA_CFG 0x160
# define CORE_CSR_CDC_STATUS0 0x164
# define CORE_CALIBRATION_DONE BIT(0)
# define CORE_CDC_ERROR_CODE_MASK 0x7000000
# define CORE_CSR_CDC_GEN_CFG 0x178
# define CORE_CDC_SWITCH_BYPASS_OFF BIT(0)
# define CORE_CDC_SWITCH_RC_EN BIT(1)
# define CORE_DDR_200_CFG 0x184
# define CORE_CDC_T4_DLY_SEL BIT(0)
# define CORE_START_CDC_TRAFFIC BIT(6)
2016-11-21 09:37:26 +03:00
# define CORE_VENDOR_SPEC3 0x1b0
# define CORE_PWRSAVE_DLL BIT(3)
# define CORE_DDR_CONFIG 0x1b8
# define DDR_CONFIG_POR_VAL 0x80040853
2016-11-21 09:37:25 +03:00
2015-03-23 19:47:29 +03:00
# define CORE_VENDOR_SPEC_CAPABILITIES0 0x11c
2016-11-21 09:37:24 +03:00
# define INVALID_TUNING_PHASE -1
2016-11-21 09:37:17 +03:00
# define SDHCI_MSM_MIN_CLOCK 400000
2016-11-21 09:37:23 +03:00
# define CORE_FREQ_100MHZ (100 * 1000 * 1000)
2016-11-21 09:37:17 +03:00
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)
2016-10-21 09:42:04 +03:00
# define MSM_MMC_AUTOSUSPEND_DELAY_MS 50
2014-03-10 19:37:12 +04:00
struct sdhci_msm_host {
struct platform_device * pdev ;
void __iomem * core_mem ; /* MSM SDCC mapped address */
2016-06-24 18:07:14 +03:00
int pwr_irq ; /* power irq */
2014-03-10 19:37:12 +04:00
struct clk * clk ; /* main SD/MMC bus clock */
struct clk * pclk ; /* SDHC peripheral bus clock */
struct clk * bus_clk ; /* SDHC bus voter clock */
2016-11-21 09:37:16 +03:00
struct clk * xo_clk ; /* TCXO clk needed for FLL feature of cm_dll*/
2016-11-21 09:37:20 +03:00
unsigned long clk_rate ;
2014-03-10 19:37:12 +04:00
struct mmc_host * mmc ;
2016-11-21 09:37:16 +03:00
bool use_14lpp_dll_reset ;
2016-11-21 09:37:23 +03:00
bool tuning_done ;
bool calibration_done ;
2016-11-21 09:37:24 +03:00
u8 saved_tuning_phase ;
2016-11-21 09:37:26 +03:00
bool use_cdclp533 ;
2014-03-10 19:37:12 +04:00
} ;
2017-01-10 10:00:46 +03:00
static unsigned int msm_get_clock_rate_for_bus_mode ( struct sdhci_host * host ,
unsigned int clock )
{
struct mmc_ios ios = host - > mmc - > ios ;
/*
* The SDHC requires internal clock frequency to be double the
* actual clock that will be set for DDR mode . The controller
* uses the faster clock ( 100 / 400 MHz ) for some of its parts and
* send the actual required clock ( 50 / 200 MHz ) to the card .
*/
if ( ios . timing = = MMC_TIMING_UHS_DDR50 | |
ios . timing = = MMC_TIMING_MMC_DDR52 | |
ios . timing = = MMC_TIMING_MMC_HS400 )
clock * = 2 ;
return clock ;
}
static void msm_set_clock_rate_for_bus_mode ( struct sdhci_host * host ,
unsigned int clock )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_msm_host * msm_host = sdhci_pltfm_priv ( pltfm_host ) ;
struct mmc_ios curr_ios = host - > mmc - > ios ;
int rc ;
clock = msm_get_clock_rate_for_bus_mode ( host , clock ) ;
rc = clk_set_rate ( msm_host - > clk , clock ) ;
if ( rc ) {
pr_err ( " %s: Failed to set clock at rate %u at timing %d \n " ,
mmc_hostname ( host - > mmc ) , clock ,
curr_ios . timing ) ;
return ;
}
msm_host - > clk_rate = clock ;
pr_debug ( " %s: Setting clock at rate %lu at timing %d \n " ,
mmc_hostname ( host - > mmc ) , clk_get_rate ( msm_host - > clk ) ,
curr_ios . timing ) ;
}
2014-03-10 19:37:12 +04:00
/* 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 ;
2016-11-21 09:37:24 +03:00
if ( phase > 0xf )
return - EINVAL ;
2014-03-10 19:37:13 +04:00
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 ) ;
2016-11-21 09:37:13 +03:00
config = readl_relaxed ( host - > ioaddr + CORE_DLL_CONFIG ) ;
config | = CORE_CK_OUT_EN ;
writel_relaxed ( config , host - > ioaddr + CORE_DLL_CONFIG ) ;
2014-03-10 19:37:13 +04:00
/* 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
2016-11-21 09:37:23 +03:00
* timing mode ) or for eMMC4 .5 card read operation ( in
* HS400 / HS200 timing mode ) .
2014-03-10 19:37:13 +04:00
* 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 ;
2016-11-21 09:37:16 +03:00
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_msm_host * msm_host = sdhci_pltfm_priv ( pltfm_host ) ;
2014-03-10 19:37:13 +04:00
int wait_cnt = 50 ;
unsigned long flags ;
2016-11-21 09:37:13 +03:00
u32 config ;
2014-03-10 19:37:13 +04:00
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
*/
2016-11-21 09:37:13 +03:00
config = readl_relaxed ( host - > ioaddr + CORE_VENDOR_SPEC ) ;
config & = ~ CORE_CLK_PWRSAVE ;
writel_relaxed ( config , host - > ioaddr + CORE_VENDOR_SPEC ) ;
2014-03-10 19:37:13 +04:00
2016-11-21 09:37:16 +03:00
if ( msm_host - > use_14lpp_dll_reset ) {
config = readl_relaxed ( host - > ioaddr + CORE_DLL_CONFIG ) ;
config & = ~ CORE_CK_OUT_EN ;
writel_relaxed ( config , host - > ioaddr + CORE_DLL_CONFIG ) ;
config = readl_relaxed ( host - > ioaddr + CORE_DLL_CONFIG_2 ) ;
config | = CORE_DLL_CLOCK_DISABLE ;
writel_relaxed ( config , host - > ioaddr + CORE_DLL_CONFIG_2 ) ;
}
2016-11-21 09:37:13 +03:00
config = readl_relaxed ( host - > ioaddr + CORE_DLL_CONFIG ) ;
config | = CORE_DLL_RST ;
writel_relaxed ( config , host - > ioaddr + CORE_DLL_CONFIG ) ;
2014-03-10 19:37:13 +04:00
2016-11-21 09:37:13 +03:00
config = readl_relaxed ( host - > ioaddr + CORE_DLL_CONFIG ) ;
config | = CORE_DLL_PDN ;
writel_relaxed ( config , host - > ioaddr + CORE_DLL_CONFIG ) ;
2014-03-10 19:37:13 +04:00
msm_cm_dll_set_freq ( host ) ;
2016-11-21 09:37:16 +03:00
if ( msm_host - > use_14lpp_dll_reset & &
! IS_ERR_OR_NULL ( msm_host - > xo_clk ) ) {
u32 mclk_freq = 0 ;
config = readl_relaxed ( host - > ioaddr + CORE_DLL_CONFIG_2 ) ;
config & = CORE_FLL_CYCLE_CNT ;
if ( config )
mclk_freq = DIV_ROUND_CLOSEST_ULL ( ( host - > clock * 8 ) ,
clk_get_rate ( msm_host - > xo_clk ) ) ;
else
mclk_freq = DIV_ROUND_CLOSEST_ULL ( ( host - > clock * 4 ) ,
clk_get_rate ( msm_host - > xo_clk ) ) ;
config = readl_relaxed ( host - > ioaddr + CORE_DLL_CONFIG_2 ) ;
config & = ~ ( 0xFF < < 10 ) ;
config | = mclk_freq < < 10 ;
writel_relaxed ( config , host - > ioaddr + CORE_DLL_CONFIG_2 ) ;
/* wait for 5us before enabling DLL clock */
udelay ( 5 ) ;
}
2016-11-21 09:37:13 +03:00
config = readl_relaxed ( host - > ioaddr + CORE_DLL_CONFIG ) ;
config & = ~ CORE_DLL_RST ;
writel_relaxed ( config , host - > ioaddr + CORE_DLL_CONFIG ) ;
2014-03-10 19:37:13 +04:00
2016-11-21 09:37:13 +03:00
config = readl_relaxed ( host - > ioaddr + CORE_DLL_CONFIG ) ;
config & = ~ CORE_DLL_PDN ;
writel_relaxed ( config , host - > ioaddr + CORE_DLL_CONFIG ) ;
2014-03-10 19:37:13 +04:00
2016-11-21 09:37:16 +03:00
if ( msm_host - > use_14lpp_dll_reset ) {
msm_cm_dll_set_freq ( host ) ;
config = readl_relaxed ( host - > ioaddr + CORE_DLL_CONFIG_2 ) ;
config & = ~ CORE_DLL_CLOCK_DISABLE ;
writel_relaxed ( config , host - > ioaddr + CORE_DLL_CONFIG_2 ) ;
}
2016-11-21 09:37:13 +03:00
config = readl_relaxed ( host - > ioaddr + CORE_DLL_CONFIG ) ;
config | = CORE_DLL_EN ;
writel_relaxed ( config , host - > ioaddr + CORE_DLL_CONFIG ) ;
2014-03-10 19:37:13 +04:00
2016-11-21 09:37:13 +03:00
config = readl_relaxed ( host - > ioaddr + CORE_DLL_CONFIG ) ;
config | = CORE_CK_OUT_EN ;
writel_relaxed ( config , host - > ioaddr + CORE_DLL_CONFIG ) ;
2014-03-10 19:37:13 +04:00
/* 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 ;
}
2017-01-10 10:00:45 +03:00
static void msm_hc_select_default ( struct sdhci_host * host )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_msm_host * msm_host = sdhci_pltfm_priv ( pltfm_host ) ;
u32 config ;
if ( ! msm_host - > use_cdclp533 ) {
config = readl_relaxed ( host - > ioaddr +
CORE_VENDOR_SPEC3 ) ;
config & = ~ CORE_PWRSAVE_DLL ;
writel_relaxed ( config , host - > ioaddr +
CORE_VENDOR_SPEC3 ) ;
}
config = readl_relaxed ( host - > ioaddr + CORE_VENDOR_SPEC ) ;
config & = ~ CORE_HC_MCLK_SEL_MASK ;
config | = CORE_HC_MCLK_SEL_DFLT ;
writel_relaxed ( config , host - > ioaddr + CORE_VENDOR_SPEC ) ;
/*
* Disable HC_SELECT_IN to be able to use the UHS mode select
* configuration from Host Control2 register for all other
* modes .
* Write 0 to HC_SELECT_IN and HC_SELECT_IN_EN field
* in VENDOR_SPEC_FUNC
*/
config = readl_relaxed ( host - > ioaddr + CORE_VENDOR_SPEC ) ;
config & = ~ CORE_HC_SELECT_IN_EN ;
config & = ~ CORE_HC_SELECT_IN_MASK ;
writel_relaxed ( config , host - > ioaddr + CORE_VENDOR_SPEC ) ;
/*
* Make sure above writes impacting free running MCLK are completed
* before changing the clk_rate at GCC .
*/
wmb ( ) ;
}
static void msm_hc_select_hs400 ( struct sdhci_host * host )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_msm_host * msm_host = sdhci_pltfm_priv ( pltfm_host ) ;
u32 config , dll_lock ;
int rc ;
/* Select the divided clock (free running MCLK/2) */
config = readl_relaxed ( host - > ioaddr + CORE_VENDOR_SPEC ) ;
config & = ~ CORE_HC_MCLK_SEL_MASK ;
config | = CORE_HC_MCLK_SEL_HS400 ;
writel_relaxed ( config , host - > ioaddr + CORE_VENDOR_SPEC ) ;
/*
* Select HS400 mode using the HC_SELECT_IN from VENDOR SPEC
* register
*/
if ( msm_host - > tuning_done & & ! msm_host - > calibration_done ) {
config = readl_relaxed ( host - > ioaddr + CORE_VENDOR_SPEC ) ;
config | = CORE_HC_SELECT_IN_HS400 ;
config | = CORE_HC_SELECT_IN_EN ;
writel_relaxed ( config , host - > ioaddr + CORE_VENDOR_SPEC ) ;
}
if ( ! msm_host - > clk_rate & & ! msm_host - > use_cdclp533 ) {
/*
* Poll on DLL_LOCK or DDR_DLL_LOCK bits in
* CORE_DLL_STATUS to be set . This should get set
* within 15 us at 200 MHz .
*/
rc = readl_relaxed_poll_timeout ( host - > ioaddr +
CORE_DLL_STATUS ,
dll_lock ,
( dll_lock &
( CORE_DLL_LOCK |
CORE_DDR_DLL_LOCK ) ) , 10 ,
1000 ) ;
if ( rc = = - ETIMEDOUT )
pr_err ( " %s: Unable to get DLL_LOCK/DDR_DLL_LOCK, dll_status: 0x%08x \n " ,
mmc_hostname ( host - > mmc ) , dll_lock ) ;
}
/*
* Make sure above writes impacting free running MCLK are completed
* before changing the clk_rate at GCC .
*/
wmb ( ) ;
}
/*
* sdhci_msm_hc_select_mode : - In general all timing modes are
* controlled via UHS mode select in Host Control2 register .
* eMMC specific HS200 / HS400 doesn ' t have their respective modes
* defined here , hence we use these values .
*
* HS200 - SDR104 ( Since they both are equivalent in functionality )
* HS400 - This involves multiple configurations
* Initially SDR104 - when tuning is required as HS200
* Then when switching to DDR @ 400 MHz ( HS400 ) we use
* the vendor specific HC_SELECT_IN to control the mode .
*
* In addition to controlling the modes we also need to select the
* correct input clock for DLL depending on the mode .
*
* HS400 - divided clock ( free running MCLK / 2 )
* All other modes - default ( free running MCLK )
*/
void sdhci_msm_hc_select_mode ( struct sdhci_host * host )
{
struct mmc_ios ios = host - > mmc - > ios ;
if ( ios . timing = = MMC_TIMING_MMC_HS400 )
msm_hc_select_hs400 ( host ) ;
else
msm_hc_select_default ( host ) ;
}
2016-11-21 09:37:25 +03:00
static int sdhci_msm_cdclp533_calibration ( struct sdhci_host * host )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_msm_host * msm_host = sdhci_pltfm_priv ( pltfm_host ) ;
u32 config , calib_done ;
int ret ;
pr_debug ( " %s: %s: Enter \n " , mmc_hostname ( host - > mmc ) , __func__ ) ;
/*
* Retuning in HS400 ( DDR mode ) will fail , just reset the
* tuning block and restore the saved tuning phase .
*/
ret = msm_init_cm_dll ( host ) ;
if ( ret )
goto out ;
/* Set the selected phase in delay line hw block */
ret = msm_config_cm_dll_phase ( host , msm_host - > saved_tuning_phase ) ;
if ( ret )
goto out ;
config = readl_relaxed ( host - > ioaddr + CORE_DLL_CONFIG ) ;
config | = CORE_CMD_DAT_TRACK_SEL ;
writel_relaxed ( config , host - > ioaddr + CORE_DLL_CONFIG ) ;
config = readl_relaxed ( host - > ioaddr + CORE_DDR_200_CFG ) ;
config & = ~ CORE_CDC_T4_DLY_SEL ;
writel_relaxed ( config , host - > ioaddr + CORE_DDR_200_CFG ) ;
config = readl_relaxed ( host - > ioaddr + CORE_CSR_CDC_GEN_CFG ) ;
config & = ~ CORE_CDC_SWITCH_BYPASS_OFF ;
writel_relaxed ( config , host - > ioaddr + CORE_CSR_CDC_GEN_CFG ) ;
config = readl_relaxed ( host - > ioaddr + CORE_CSR_CDC_GEN_CFG ) ;
config | = CORE_CDC_SWITCH_RC_EN ;
writel_relaxed ( config , host - > ioaddr + CORE_CSR_CDC_GEN_CFG ) ;
config = readl_relaxed ( host - > ioaddr + CORE_DDR_200_CFG ) ;
config & = ~ CORE_START_CDC_TRAFFIC ;
writel_relaxed ( config , host - > ioaddr + CORE_DDR_200_CFG ) ;
/*
* Perform CDC Register Initialization Sequence
*
* CORE_CSR_CDC_CTLR_CFG0 0x11800EC
* CORE_CSR_CDC_CTLR_CFG1 0x3011111
* CORE_CSR_CDC_CAL_TIMER_CFG0 0x1201000
* CORE_CSR_CDC_CAL_TIMER_CFG1 0x4
* CORE_CSR_CDC_REFCOUNT_CFG 0xCB732020
* CORE_CSR_CDC_COARSE_CAL_CFG 0xB19
* CORE_CSR_CDC_DELAY_CFG 0x3AC
* CORE_CDC_OFFSET_CFG 0x0
* CORE_CDC_SLAVE_DDA_CFG 0x16334
*/
writel_relaxed ( 0x11800EC , host - > ioaddr + CORE_CSR_CDC_CTLR_CFG0 ) ;
writel_relaxed ( 0x3011111 , host - > ioaddr + CORE_CSR_CDC_CTLR_CFG1 ) ;
writel_relaxed ( 0x1201000 , host - > ioaddr + CORE_CSR_CDC_CAL_TIMER_CFG0 ) ;
writel_relaxed ( 0x4 , host - > ioaddr + CORE_CSR_CDC_CAL_TIMER_CFG1 ) ;
writel_relaxed ( 0xCB732020 , host - > ioaddr + CORE_CSR_CDC_REFCOUNT_CFG ) ;
writel_relaxed ( 0xB19 , host - > ioaddr + CORE_CSR_CDC_COARSE_CAL_CFG ) ;
writel_relaxed ( 0x3AC , host - > ioaddr + CORE_CSR_CDC_DELAY_CFG ) ;
writel_relaxed ( 0x0 , host - > ioaddr + CORE_CDC_OFFSET_CFG ) ;
writel_relaxed ( 0x16334 , host - > ioaddr + CORE_CDC_SLAVE_DDA_CFG ) ;
/* CDC HW Calibration */
config = readl_relaxed ( host - > ioaddr + CORE_CSR_CDC_CTLR_CFG0 ) ;
config | = CORE_SW_TRIG_FULL_CALIB ;
writel_relaxed ( config , host - > ioaddr + CORE_CSR_CDC_CTLR_CFG0 ) ;
config = readl_relaxed ( host - > ioaddr + CORE_CSR_CDC_CTLR_CFG0 ) ;
config & = ~ CORE_SW_TRIG_FULL_CALIB ;
writel_relaxed ( config , host - > ioaddr + CORE_CSR_CDC_CTLR_CFG0 ) ;
config = readl_relaxed ( host - > ioaddr + CORE_CSR_CDC_CTLR_CFG0 ) ;
config | = CORE_HW_AUTOCAL_ENA ;
writel_relaxed ( config , host - > ioaddr + CORE_CSR_CDC_CTLR_CFG0 ) ;
config = readl_relaxed ( host - > ioaddr + CORE_CSR_CDC_CAL_TIMER_CFG0 ) ;
config | = CORE_TIMER_ENA ;
writel_relaxed ( config , host - > ioaddr + CORE_CSR_CDC_CAL_TIMER_CFG0 ) ;
ret = readl_relaxed_poll_timeout ( host - > ioaddr + CORE_CSR_CDC_STATUS0 ,
calib_done ,
( calib_done & CORE_CALIBRATION_DONE ) ,
1 , 50 ) ;
if ( ret = = - ETIMEDOUT ) {
pr_err ( " %s: %s: CDC calibration was not completed \n " ,
mmc_hostname ( host - > mmc ) , __func__ ) ;
goto out ;
}
ret = readl_relaxed ( host - > ioaddr + CORE_CSR_CDC_STATUS0 )
& CORE_CDC_ERROR_CODE_MASK ;
if ( ret ) {
pr_err ( " %s: %s: CDC error code %d \n " ,
mmc_hostname ( host - > mmc ) , __func__ , ret ) ;
ret = - EINVAL ;
goto out ;
}
config = readl_relaxed ( host - > ioaddr + CORE_DDR_200_CFG ) ;
config | = CORE_START_CDC_TRAFFIC ;
writel_relaxed ( config , host - > ioaddr + CORE_DDR_200_CFG ) ;
out :
pr_debug ( " %s: %s: Exit, ret %d \n " , mmc_hostname ( host - > mmc ) ,
__func__ , ret ) ;
return ret ;
}
2016-11-21 09:37:26 +03:00
static int sdhci_msm_cm_dll_sdc4_calibration ( struct sdhci_host * host )
{
u32 dll_status , config ;
int ret ;
pr_debug ( " %s: %s: Enter \n " , mmc_hostname ( host - > mmc ) , __func__ ) ;
/*
* Currently the CORE_DDR_CONFIG register defaults to desired
* configuration on reset . Currently reprogramming the power on
* reset ( POR ) value in case it might have been modified by
* bootloaders . In the future , if this changes , then the desired
* values will need to be programmed appropriately .
*/
writel_relaxed ( DDR_CONFIG_POR_VAL , host - > ioaddr + CORE_DDR_CONFIG ) ;
config = readl_relaxed ( host - > ioaddr + CORE_DLL_CONFIG_2 ) ;
config | = CORE_DDR_CAL_EN ;
writel_relaxed ( config , host - > ioaddr + CORE_DLL_CONFIG_2 ) ;
ret = readl_relaxed_poll_timeout ( host - > ioaddr + CORE_DLL_STATUS ,
dll_status ,
( dll_status & CORE_DDR_DLL_LOCK ) ,
10 , 1000 ) ;
if ( ret = = - ETIMEDOUT ) {
pr_err ( " %s: %s: CM_DLL_SDC4 calibration was not completed \n " ,
mmc_hostname ( host - > mmc ) , __func__ ) ;
goto out ;
}
config = readl_relaxed ( host - > ioaddr + CORE_VENDOR_SPEC3 ) ;
config | = CORE_PWRSAVE_DLL ;
writel_relaxed ( config , host - > ioaddr + CORE_VENDOR_SPEC3 ) ;
/*
* Drain writebuffer to ensure above DLL calibration
* and PWRSAVE DLL is enabled .
*/
wmb ( ) ;
out :
pr_debug ( " %s: %s: Exit, ret %d \n " , mmc_hostname ( host - > mmc ) ,
__func__ , ret ) ;
return ret ;
}
static int sdhci_msm_hs400_dll_calibration ( struct sdhci_host * host )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_msm_host * msm_host = sdhci_pltfm_priv ( pltfm_host ) ;
int ret ;
u32 config ;
pr_debug ( " %s: %s: Enter \n " , mmc_hostname ( host - > mmc ) , __func__ ) ;
/*
* Retuning in HS400 ( DDR mode ) will fail , just reset the
* tuning block and restore the saved tuning phase .
*/
ret = msm_init_cm_dll ( host ) ;
if ( ret )
goto out ;
/* Set the selected phase in delay line hw block */
ret = msm_config_cm_dll_phase ( host , msm_host - > saved_tuning_phase ) ;
if ( ret )
goto out ;
config = readl_relaxed ( host - > ioaddr + CORE_DLL_CONFIG ) ;
config | = CORE_CMD_DAT_TRACK_SEL ;
writel_relaxed ( config , host - > ioaddr + CORE_DLL_CONFIG ) ;
if ( msm_host - > use_cdclp533 )
ret = sdhci_msm_cdclp533_calibration ( host ) ;
else
ret = sdhci_msm_cm_dll_sdc4_calibration ( host ) ;
out :
pr_debug ( " %s: %s: Exit, ret %d \n " , mmc_hostname ( host - > mmc ) ,
__func__ , ret ) ;
return ret ;
}
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 ;
2016-11-21 09:37:24 +03:00
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_msm_host * msm_host = sdhci_pltfm_priv ( pltfm_host ) ;
2014-03-10 19:37:13 +04:00
/*
* Tuning is required for SDR104 , HS200 and HS400 cards and
* if clock frequency is greater than 100 MHz in these modes .
*/
2016-11-21 09:37:23 +03:00
if ( host - > clock < = CORE_FREQ_100MHZ | |
! ( ios . timing = = MMC_TIMING_MMC_HS400 | |
ios . timing = = MMC_TIMING_MMC_HS200 | |
ios . timing = = MMC_TIMING_UHS_SDR104 ) )
2014-03-10 19:37:13 +04:00
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
2016-11-21 09:37:24 +03:00
msm_host - > saved_tuning_phase = phase ;
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 ;
}
2016-11-21 09:37:23 +03:00
if ( ! rc )
msm_host - > tuning_done = true ;
2014-03-10 19:37:13 +04:00
return rc ;
}
2017-01-10 10:00:47 +03:00
/*
* sdhci_msm_hs400 - Calibrate the DLL for HS400 bus speed mode operation .
* DLL operation is only needed for clock > 100 MHz . For clock < = 100 MHz
* fixed feedback clock is used .
*/
static void sdhci_msm_hs400 ( struct sdhci_host * host , struct mmc_ios * ios )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_msm_host * msm_host = sdhci_pltfm_priv ( pltfm_host ) ;
int ret ;
if ( host - > clock > CORE_FREQ_100MHZ & &
msm_host - > tuning_done & & ! msm_host - > calibration_done ) {
ret = sdhci_msm_hs400_dll_calibration ( host ) ;
if ( ! ret )
msm_host - > calibration_done = true ;
else
pr_err ( " %s: Failed to calibrate DLL for hs400 mode (%d) \n " ,
mmc_hostname ( host - > mmc ) , ret ) ;
}
}
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 ;
2016-11-21 09:37:23 +03:00
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_msm_host * msm_host = sdhci_pltfm_priv ( pltfm_host ) ;
2016-07-19 17:52:25 +03:00
u16 ctrl_2 ;
2016-11-21 09:37:23 +03:00
u32 config ;
2016-07-19 17:52:25 +03:00
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 ;
2016-11-21 09:37:23 +03:00
case MMC_TIMING_MMC_HS400 :
2016-07-19 17:52:25 +03:00
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 .
*/
2016-11-21 09:37:23 +03:00
if ( host - > clock < = CORE_FREQ_100MHZ ) {
if ( uhs = = MMC_TIMING_MMC_HS400 | |
uhs = = MMC_TIMING_MMC_HS200 | |
uhs = = MMC_TIMING_UHS_SDR104 )
ctrl_2 & = ~ SDHCI_CTRL_UHS_MASK ;
/*
* DLL is not required for clock < = 100 MHz
* Thus , make sure DLL it is disabled when not required
*/
config = readl_relaxed ( host - > ioaddr + CORE_DLL_CONFIG ) ;
config | = CORE_DLL_RST ;
writel_relaxed ( config , host - > ioaddr + CORE_DLL_CONFIG ) ;
config = readl_relaxed ( host - > ioaddr + CORE_DLL_CONFIG ) ;
config | = CORE_DLL_PDN ;
writel_relaxed ( config , host - > ioaddr + CORE_DLL_CONFIG ) ;
/*
* The DLL needs to be restored and CDCLP533 recalibrated
* when the clock frequency is set back to 400 MHz .
*/
msm_host - > calibration_done = false ;
}
2016-07-19 17:52:25 +03:00
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 ) ;
2016-11-21 09:37:25 +03:00
spin_unlock_irq ( & host - > lock ) ;
2017-01-10 10:00:47 +03:00
if ( mmc - > ios . timing = = MMC_TIMING_MMC_HS400 )
sdhci_msm_hs400 ( host , & mmc - > ios ) ;
2016-11-21 09:37:25 +03:00
spin_lock_irq ( & host - > lock ) ;
2016-07-19 17:52:25 +03:00
}
2016-06-24 18:07:14 +03:00
static void sdhci_msm_voltage_switch ( struct sdhci_host * host )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_msm_host * msm_host = sdhci_pltfm_priv ( pltfm_host ) ;
u32 irq_status , irq_ack = 0 ;
irq_status = readl_relaxed ( msm_host - > core_mem + CORE_PWRCTL_STATUS ) ;
irq_status & = INT_MASK ;
writel_relaxed ( irq_status , msm_host - > core_mem + CORE_PWRCTL_CLEAR ) ;
if ( irq_status & ( CORE_PWRCTL_BUS_ON | CORE_PWRCTL_BUS_OFF ) )
irq_ack | = CORE_PWRCTL_BUS_SUCCESS ;
if ( irq_status & ( CORE_PWRCTL_IO_LOW | CORE_PWRCTL_IO_HIGH ) )
irq_ack | = CORE_PWRCTL_IO_SUCCESS ;
/*
* The driver has to acknowledge the interrupt , switch voltages and
* report back if it succeded or not to this register . The voltage
* switches are handled by the sdhci core , so just report success .
*/
writel_relaxed ( irq_ack , msm_host - > core_mem + CORE_PWRCTL_CTL ) ;
}
static irqreturn_t sdhci_msm_pwr_irq ( int irq , void * data )
{
struct sdhci_host * host = ( struct sdhci_host * ) data ;
sdhci_msm_voltage_switch ( host ) ;
return IRQ_HANDLED ;
}
2016-11-21 09:37:17 +03:00
static unsigned int sdhci_msm_get_max_clock ( struct sdhci_host * host )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_msm_host * msm_host = sdhci_pltfm_priv ( pltfm_host ) ;
return clk_round_rate ( msm_host - > clk , ULONG_MAX ) ;
}
static unsigned int sdhci_msm_get_min_clock ( struct sdhci_host * host )
{
return SDHCI_MSM_MIN_CLOCK ;
}
2016-11-21 09:37:20 +03:00
/**
* __sdhci_msm_set_clock - sdhci_msm clock control .
*
* Description :
* MSM controller does not use internal divider and
* instead directly control the GCC clock as per
* HW recommendation .
* */
void __sdhci_msm_set_clock ( struct sdhci_host * host , unsigned int clock )
{
u16 clk ;
/*
* Keep actual_clock as zero -
* - since there is no divider used so no need of having actual_clock .
* - MSM controller uses SDCLK for data timeout calculation . If
* actual_clock is zero , host - > clock is taken for calculation .
*/
host - > mmc - > actual_clock = 0 ;
sdhci_writew ( host , 0 , SDHCI_CLOCK_CONTROL ) ;
if ( clock = = 0 )
return ;
/*
* MSM controller do not use clock divider .
* Thus read SDHCI_CLOCK_CONTROL and only enable
* clock with no divider value programmed .
*/
clk = sdhci_readw ( host , SDHCI_CLOCK_CONTROL ) ;
sdhci_enable_clk ( host , clk ) ;
}
/* sdhci_msm_set_clock - Called with (host->lock) spinlock held. */
static void sdhci_msm_set_clock ( struct sdhci_host * host , unsigned int clock )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_msm_host * msm_host = sdhci_pltfm_priv ( pltfm_host ) ;
if ( ! clock ) {
msm_host - > clk_rate = clock ;
goto out ;
}
spin_unlock_irq ( & host - > lock ) ;
2016-11-21 09:37:23 +03:00
2017-01-10 10:00:45 +03:00
sdhci_msm_hc_select_mode ( host ) ;
2016-11-21 09:37:20 +03:00
2017-01-10 10:00:46 +03:00
msm_set_clock_rate_for_bus_mode ( host , clock ) ;
2016-11-21 09:37:20 +03:00
spin_lock_irq ( & host - > lock ) ;
out :
__sdhci_msm_set_clock ( host , clock ) ;
}
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 ,
2016-11-21 09:37:20 +03:00
. set_clock = sdhci_msm_set_clock ,
2016-11-21 09:37:17 +03:00
. get_min_clock = sdhci_msm_get_min_clock ,
. get_max_clock = sdhci_msm_get_max_clock ,
2014-06-10 22:27:19 +04:00
. set_bus_width = sdhci_set_bus_width ,
2016-07-19 17:52:25 +03:00
. set_uhs_signaling = sdhci_msm_set_uhs_signaling ,
2016-06-24 18:07:14 +03:00
. voltage_switch = sdhci_msm_voltage_switch ,
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-11-21 09:37:18 +03:00
SDHCI_QUIRK_SINGLE_POWER_WRITE |
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN ,
. quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN ,
2016-02-16 16:08:21 +03:00
. 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 ;
2016-11-21 09:37:13 +03:00
u32 core_version , config ;
2015-03-23 19:47:29 +03:00
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 ) ;
2016-11-21 09:37:24 +03:00
msm_host - > saved_tuning_phase = INVALID_TUNING_PHASE ;
2014-03-10 19:37:12 +04:00
/* 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 ;
}
2016-11-21 09:37:16 +03:00
/*
* xo clock is needed for FLL feature of cm_dll .
* In case if xo clock is not mentioned in DT , warn and proceed .
*/
msm_host - > xo_clk = devm_clk_get ( & pdev - > dev , " xo " ) ;
if ( IS_ERR ( msm_host - > xo_clk ) ) {
ret = PTR_ERR ( msm_host - > xo_clk ) ;
dev_warn ( & pdev - > dev , " TCXO clk not present (%d) \n " , ret ) ;
}
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 ;
}
2016-11-21 09:37:13 +03:00
config = readl_relaxed ( msm_host - > core_mem + CORE_POWER ) ;
config | = CORE_SW_RST ;
writel_relaxed ( config , msm_host - > core_mem + CORE_POWER ) ;
2014-03-10 19:37:12 +04:00
/* 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 ) ) ;
2016-11-21 09:37:23 +03:00
config = readl_relaxed ( msm_host - > core_mem + CORE_HC_MODE ) ;
config | = FF_CLK_SW_RST_DIS ;
writel_relaxed ( config , msm_host - > core_mem + CORE_HC_MODE ) ;
2014-03-10 19:37:12 +04:00
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 ) ;
2016-11-21 09:37:16 +03:00
if ( core_major = = 1 & & core_minor > = 0x42 )
msm_host - > use_14lpp_dll_reset = true ;
2016-11-21 09:37:26 +03:00
/*
* SDCC 5 controller with major version 1 , minor version 0x34 and later
* with HS 400 mode support will use CM DLL instead of CDC LP 533 DLL .
*/
if ( core_major = = 1 & & core_minor < 0x34 )
msm_host - > use_cdclp533 = true ;
2015-03-23 19:47:29 +03:00
/*
* 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 ) {
2016-11-21 09:37:13 +03:00
config = readl_relaxed ( host - > ioaddr + SDHCI_CAPABILITIES ) ;
config | = SDHCI_CAN_VDD_300 | SDHCI_CAN_DO_8BIT ;
writel_relaxed ( config , host - > ioaddr +
2015-03-23 19:47:29 +03:00
CORE_VENDOR_SPEC_CAPABILITIES0 ) ;
}
2016-06-24 18:07:14 +03:00
/* Setup IRQ for handling power/voltage tasks with PMIC */
msm_host - > pwr_irq = platform_get_irq_byname ( pdev , " pwr_irq " ) ;
if ( msm_host - > pwr_irq < 0 ) {
dev_err ( & pdev - > dev , " Get pwr_irq failed (%d) \n " ,
msm_host - > pwr_irq ) ;
2016-10-26 18:04:41 +03:00
ret = msm_host - > pwr_irq ;
2016-06-24 18:07:14 +03:00
goto clk_disable ;
}
ret = devm_request_threaded_irq ( & pdev - > dev , msm_host - > pwr_irq , NULL ,
sdhci_msm_pwr_irq , IRQF_ONESHOT ,
dev_name ( & pdev - > dev ) , host ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " Request IRQ failed (%d) \n " , ret ) ;
goto clk_disable ;
}
2016-10-21 09:42:04 +03:00
pm_runtime_get_noresume ( & pdev - > dev ) ;
pm_runtime_set_active ( & pdev - > dev ) ;
pm_runtime_enable ( & pdev - > dev ) ;
pm_runtime_set_autosuspend_delay ( & pdev - > dev ,
MSM_MMC_AUTOSUSPEND_DELAY_MS ) ;
pm_runtime_use_autosuspend ( & pdev - > dev ) ;
2014-03-10 19:37:12 +04:00
ret = sdhci_add_host ( host ) ;
if ( ret )
2016-10-21 09:42:04 +03:00
goto pm_runtime_disable ;
pm_runtime_mark_last_busy ( & pdev - > dev ) ;
pm_runtime_put_autosuspend ( & pdev - > dev ) ;
2014-03-10 19:37:12 +04:00
return 0 ;
2016-10-21 09:42:04 +03:00
pm_runtime_disable :
pm_runtime_disable ( & pdev - > dev ) ;
pm_runtime_set_suspended ( & pdev - > dev ) ;
pm_runtime_put_noidle ( & pdev - > dev ) ;
2014-03-10 19:37:12 +04:00
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 ) ;
2016-10-21 09:42:04 +03:00
pm_runtime_get_sync ( & pdev - > dev ) ;
pm_runtime_disable ( & pdev - > dev ) ;
pm_runtime_put_noidle ( & pdev - > dev ) ;
2014-03-10 19:37:12 +04:00
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 ;
}
2016-10-21 09:42:04 +03:00
# ifdef CONFIG_PM
static int sdhci_msm_runtime_suspend ( struct device * dev )
{
struct sdhci_host * host = dev_get_drvdata ( dev ) ;
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_msm_host * msm_host = sdhci_pltfm_priv ( pltfm_host ) ;
clk_disable_unprepare ( msm_host - > clk ) ;
clk_disable_unprepare ( msm_host - > pclk ) ;
return 0 ;
}
static int sdhci_msm_runtime_resume ( struct device * dev )
{
struct sdhci_host * host = dev_get_drvdata ( dev ) ;
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_msm_host * msm_host = sdhci_pltfm_priv ( pltfm_host ) ;
int ret ;
ret = clk_prepare_enable ( msm_host - > clk ) ;
if ( ret ) {
dev_err ( dev , " clk_enable failed for core_clk: %d \n " , ret ) ;
return ret ;
}
ret = clk_prepare_enable ( msm_host - > pclk ) ;
if ( ret ) {
dev_err ( dev , " clk_enable failed for iface_clk: %d \n " , ret ) ;
clk_disable_unprepare ( msm_host - > clk ) ;
return ret ;
}
return 0 ;
}
# endif
static const struct dev_pm_ops sdhci_msm_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( pm_runtime_force_suspend ,
pm_runtime_force_resume )
SET_RUNTIME_PM_OPS ( sdhci_msm_runtime_suspend ,
sdhci_msm_runtime_resume ,
NULL )
} ;
2014-03-10 19:37:12 +04:00
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 ,
2016-10-21 09:42:04 +03:00
. pm = & sdhci_msm_pm_ops ,
2014-03-10 19:37:12 +04:00
} ,
} ;
module_platform_driver ( sdhci_msm_driver ) ;
MODULE_DESCRIPTION ( " Qualcomm Secure Digital Host Controller Interface driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;