2018-08-30 16:21:44 +08:00
// SPDX-License-Identifier: GPL-2.0
//
// Secure Digital Host Controller
//
// Copyright (C) 2018 Spreadtrum, Inc.
// Author: Chunyan Zhang <chunyan.zhang@unisoc.com>
# include <linux/delay.h>
# include <linux/dma-mapping.h>
# include <linux/highmem.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/of_gpio.h>
2019-06-21 14:12:33 +08:00
# include <linux/pinctrl/consumer.h>
2018-08-30 16:21:44 +08:00
# include <linux/platform_device.h>
# include <linux/pm_runtime.h>
# include <linux/regulator/consumer.h>
# include <linux/slab.h>
# include "sdhci-pltfm.h"
/* SDHCI_ARGUMENT2 register high 16bit */
# define SDHCI_SPRD_ARG2_STUFF GENMASK(31, 16)
2019-06-04 16:14:26 +08:00
# define SDHCI_SPRD_REG_32_DLL_CFG 0x200
# define SDHCI_SPRD_DLL_ALL_CPST_EN (BIT(18) | BIT(24) | BIT(25) | BIT(26) | BIT(27))
# define SDHCI_SPRD_DLL_EN BIT(21)
# define SDHCI_SPRD_DLL_SEARCH_MODE BIT(16)
# define SDHCI_SPRD_DLL_INIT_COUNT 0xc00
# define SDHCI_SPRD_DLL_PHASE_INTERNAL 0x3
2019-06-04 16:14:28 +08:00
# define SDHCI_SPRD_REG_32_DLL_DLY 0x204
2018-08-30 16:21:44 +08:00
# define SDHCI_SPRD_REG_32_DLL_DLY_OFFSET 0x208
# define SDHCIBSPRD_IT_WR_DLY_INV BIT(5)
# define SDHCI_SPRD_BIT_CMD_DLY_INV BIT(13)
# define SDHCI_SPRD_BIT_POSRD_DLY_INV BIT(21)
# define SDHCI_SPRD_BIT_NEGRD_DLY_INV BIT(29)
# define SDHCI_SPRD_REG_32_BUSY_POSI 0x250
# define SDHCI_SPRD_BIT_OUTR_CLK_AUTO_EN BIT(25)
# define SDHCI_SPRD_BIT_INNR_CLK_AUTO_EN BIT(24)
# define SDHCI_SPRD_REG_DEBOUNCE 0x28C
# define SDHCI_SPRD_BIT_DLL_BAK BIT(0)
# define SDHCI_SPRD_BIT_DLL_VAL BIT(1)
# define SDHCI_SPRD_INT_SIGNAL_MASK 0x1B7F410B
/* SDHCI_HOST_CONTROL2 */
# define SDHCI_SPRD_CTRL_HS200 0x0005
# define SDHCI_SPRD_CTRL_HS400 0x0006
2019-06-04 16:14:25 +08:00
# define SDHCI_SPRD_CTRL_HS400ES 0x0007
2018-08-30 16:21:44 +08:00
/*
* According to the standard specification , BIT ( 3 ) of SDHCI_SOFTWARE_RESET is
* reserved , and only used on Spreadtrum ' s design , the hardware cannot work
* if this bit is cleared .
* 1 : normal work
* 0 : hardware reset
*/
# define SDHCI_HW_RESET_CARD BIT(3)
# define SDHCI_SPRD_MAX_CUR 0xFFFFFF
# define SDHCI_SPRD_CLK_MAX_DIV 1023
# define SDHCI_SPRD_CLK_DEF_RATE 26000000
2019-06-04 16:14:26 +08:00
# define SDHCI_SPRD_PHY_DLL_CLK 52000000
2018-08-30 16:21:44 +08:00
struct sdhci_sprd_host {
u32 version ;
struct clk * clk_sdio ;
struct clk * clk_enable ;
2019-06-04 16:14:23 +08:00
struct clk * clk_2x_enable ;
2019-06-21 14:12:33 +08:00
struct pinctrl * pinctrl ;
struct pinctrl_state * pins_uhs ;
struct pinctrl_state * pins_default ;
2018-08-30 16:21:44 +08:00
u32 base_rate ;
int flags ; /* backup of host attribute */
2019-06-04 16:14:28 +08:00
u32 phy_delay [ MMC_TIMING_MMC_HS400 + 2 ] ;
} ;
struct sdhci_sprd_phy_cfg {
const char * property ;
u8 timing ;
} ;
static const struct sdhci_sprd_phy_cfg sdhci_sprd_phy_cfgs [ ] = {
{ " sprd,phy-delay-legacy " , MMC_TIMING_LEGACY , } ,
{ " sprd,phy-delay-sd-highspeed " , MMC_TIMING_SD_HS , } ,
{ " sprd,phy-delay-sd-uhs-sdr50 " , MMC_TIMING_UHS_SDR50 , } ,
{ " sprd,phy-delay-sd-uhs-sdr104 " , MMC_TIMING_UHS_SDR104 , } ,
{ " sprd,phy-delay-mmc-highspeed " , MMC_TIMING_MMC_HS , } ,
{ " sprd,phy-delay-mmc-ddr52 " , MMC_TIMING_MMC_DDR52 , } ,
{ " sprd,phy-delay-mmc-hs200 " , MMC_TIMING_MMC_HS200 , } ,
{ " sprd,phy-delay-mmc-hs400 " , MMC_TIMING_MMC_HS400 , } ,
{ " sprd,phy-delay-mmc-hs400es " , MMC_TIMING_MMC_HS400 + 1 , } ,
2018-08-30 16:21:44 +08:00
} ;
# define TO_SPRD_HOST(host) sdhci_pltfm_priv(sdhci_priv(host))
static void sdhci_sprd_init_config ( struct sdhci_host * host )
{
u16 val ;
/* set dll backup mode */
val = sdhci_readl ( host , SDHCI_SPRD_REG_DEBOUNCE ) ;
val | = SDHCI_SPRD_BIT_DLL_BAK | SDHCI_SPRD_BIT_DLL_VAL ;
sdhci_writel ( host , val , SDHCI_SPRD_REG_DEBOUNCE ) ;
}
static inline u32 sdhci_sprd_readl ( struct sdhci_host * host , int reg )
{
if ( unlikely ( reg = = SDHCI_MAX_CURRENT ) )
return SDHCI_SPRD_MAX_CUR ;
return readl_relaxed ( host - > ioaddr + reg ) ;
}
static inline void sdhci_sprd_writel ( struct sdhci_host * host , u32 val , int reg )
{
/* SDHCI_MAX_CURRENT is reserved on Spreadtrum's platform */
if ( unlikely ( reg = = SDHCI_MAX_CURRENT ) )
return ;
if ( unlikely ( reg = = SDHCI_SIGNAL_ENABLE | | reg = = SDHCI_INT_ENABLE ) )
val = val & SDHCI_SPRD_INT_SIGNAL_MASK ;
writel_relaxed ( val , host - > ioaddr + reg ) ;
}
static inline void sdhci_sprd_writew ( struct sdhci_host * host , u16 val , int reg )
{
/* SDHCI_BLOCK_COUNT is Read Only on Spreadtrum's platform */
if ( unlikely ( reg = = SDHCI_BLOCK_COUNT ) )
return ;
writew_relaxed ( val , host - > ioaddr + reg ) ;
}
static inline void sdhci_sprd_writeb ( struct sdhci_host * host , u8 val , int reg )
{
/*
* Since BIT ( 3 ) of SDHCI_SOFTWARE_RESET is reserved according to the
* standard specification , sdhci_reset ( ) write this register directly
* without checking other reserved bits , that will clear BIT ( 3 ) which
* is defined as hardware reset on Spreadtrum ' s platform and clearing
* it by mistake will lead the card not work . So here we need to work
* around it .
*/
if ( unlikely ( reg = = SDHCI_SOFTWARE_RESET ) ) {
if ( readb_relaxed ( host - > ioaddr + reg ) & SDHCI_HW_RESET_CARD )
val | = SDHCI_HW_RESET_CARD ;
}
writeb_relaxed ( val , host - > ioaddr + reg ) ;
}
static inline void sdhci_sprd_sd_clk_off ( struct sdhci_host * host )
{
u16 ctrl = sdhci_readw ( host , SDHCI_CLOCK_CONTROL ) ;
ctrl & = ~ SDHCI_CLOCK_CARD_EN ;
sdhci_writew ( host , ctrl , SDHCI_CLOCK_CONTROL ) ;
}
2019-06-04 16:14:25 +08:00
static inline void sdhci_sprd_sd_clk_on ( struct sdhci_host * host )
{
u16 ctrl ;
ctrl = sdhci_readw ( host , SDHCI_CLOCK_CONTROL ) ;
ctrl | = SDHCI_CLOCK_CARD_EN ;
sdhci_writew ( host , ctrl , SDHCI_CLOCK_CONTROL ) ;
}
2018-08-30 16:21:44 +08:00
static inline void
sdhci_sprd_set_dll_invert ( struct sdhci_host * host , u32 mask , bool en )
{
u32 dll_dly_offset ;
dll_dly_offset = sdhci_readl ( host , SDHCI_SPRD_REG_32_DLL_DLY_OFFSET ) ;
if ( en )
dll_dly_offset | = mask ;
else
dll_dly_offset & = ~ mask ;
sdhci_writel ( host , dll_dly_offset , SDHCI_SPRD_REG_32_DLL_DLY_OFFSET ) ;
}
static inline u32 sdhci_sprd_calc_div ( u32 base_clk , u32 clk )
{
u32 div ;
/* select 2x clock source */
if ( base_clk < = clk * 2 )
return 0 ;
div = ( u32 ) ( base_clk / ( clk * 2 ) ) ;
if ( ( base_clk / div ) > ( clk * 2 ) )
div + + ;
if ( div > SDHCI_SPRD_CLK_MAX_DIV )
div = SDHCI_SPRD_CLK_MAX_DIV ;
if ( div % 2 )
div = ( div + 1 ) / 2 ;
else
div = div / 2 ;
return div ;
}
static inline void _sdhci_sprd_set_clock ( struct sdhci_host * host ,
unsigned int clk )
{
struct sdhci_sprd_host * sprd_host = TO_SPRD_HOST ( host ) ;
u32 div , val , mask ;
div = sdhci_sprd_calc_div ( sprd_host - > base_rate , clk ) ;
clk | = ( ( div & 0x300 ) > > 2 ) | ( ( div & 0xFF ) < < 8 ) ;
sdhci_enable_clk ( host , clk ) ;
/* enable auto gate sdhc_enable_auto_gate */
val = sdhci_readl ( host , SDHCI_SPRD_REG_32_BUSY_POSI ) ;
mask = SDHCI_SPRD_BIT_OUTR_CLK_AUTO_EN |
SDHCI_SPRD_BIT_INNR_CLK_AUTO_EN ;
if ( mask ! = ( val & mask ) ) {
val | = mask ;
sdhci_writel ( host , val , SDHCI_SPRD_REG_32_BUSY_POSI ) ;
}
}
2019-06-04 16:14:26 +08:00
static void sdhci_sprd_enable_phy_dll ( struct sdhci_host * host )
{
u32 tmp ;
tmp = sdhci_readl ( host , SDHCI_SPRD_REG_32_DLL_CFG ) ;
tmp & = ~ ( SDHCI_SPRD_DLL_EN | SDHCI_SPRD_DLL_ALL_CPST_EN ) ;
sdhci_writel ( host , tmp , SDHCI_SPRD_REG_32_DLL_CFG ) ;
/* wait 1ms */
usleep_range ( 1000 , 1250 ) ;
tmp = sdhci_readl ( host , SDHCI_SPRD_REG_32_DLL_CFG ) ;
tmp | = SDHCI_SPRD_DLL_ALL_CPST_EN | SDHCI_SPRD_DLL_SEARCH_MODE |
SDHCI_SPRD_DLL_INIT_COUNT | SDHCI_SPRD_DLL_PHASE_INTERNAL ;
sdhci_writel ( host , tmp , SDHCI_SPRD_REG_32_DLL_CFG ) ;
/* wait 1ms */
usleep_range ( 1000 , 1250 ) ;
tmp = sdhci_readl ( host , SDHCI_SPRD_REG_32_DLL_CFG ) ;
tmp | = SDHCI_SPRD_DLL_EN ;
sdhci_writel ( host , tmp , SDHCI_SPRD_REG_32_DLL_CFG ) ;
/* wait 1ms */
usleep_range ( 1000 , 1250 ) ;
}
2018-08-30 16:21:44 +08:00
static void sdhci_sprd_set_clock ( struct sdhci_host * host , unsigned int clock )
{
2019-06-04 16:14:26 +08:00
bool en = false , clk_changed = false ;
2018-08-30 16:21:44 +08:00
if ( clock = = 0 ) {
sdhci_writew ( host , 0 , SDHCI_CLOCK_CONTROL ) ;
} else if ( clock ! = host - > clock ) {
sdhci_sprd_sd_clk_off ( host ) ;
_sdhci_sprd_set_clock ( host , clock ) ;
if ( clock < = 400000 )
en = true ;
sdhci_sprd_set_dll_invert ( host , SDHCI_SPRD_BIT_CMD_DLY_INV |
SDHCI_SPRD_BIT_POSRD_DLY_INV , en ) ;
2019-06-04 16:14:26 +08:00
clk_changed = true ;
2018-08-30 16:21:44 +08:00
} else {
_sdhci_sprd_set_clock ( host , clock ) ;
}
2019-06-04 16:14:26 +08:00
/*
* According to the Spreadtrum SD host specification , when we changed
* the clock to be more than 52 M , we should enable the PHY DLL which
* is used to track the clock frequency to make the clock work more
* stable . Otherwise deviation may occur of the higher clock .
*/
if ( clk_changed & & clock > SDHCI_SPRD_PHY_DLL_CLK )
sdhci_sprd_enable_phy_dll ( host ) ;
2018-08-30 16:21:44 +08:00
}
static unsigned int sdhci_sprd_get_max_clock ( struct sdhci_host * host )
{
struct sdhci_sprd_host * sprd_host = TO_SPRD_HOST ( host ) ;
return clk_round_rate ( sprd_host - > clk_sdio , ULONG_MAX ) ;
}
static unsigned int sdhci_sprd_get_min_clock ( struct sdhci_host * host )
{
return 400000 ;
}
static void sdhci_sprd_set_uhs_signaling ( struct sdhci_host * host ,
unsigned int timing )
{
2019-06-04 16:14:28 +08:00
struct sdhci_sprd_host * sprd_host = TO_SPRD_HOST ( host ) ;
struct mmc_host * mmc = host - > mmc ;
u32 * p = sprd_host - > phy_delay ;
2018-08-30 16:21:44 +08:00
u16 ctrl_2 ;
if ( timing = = host - > timing )
return ;
ctrl_2 = sdhci_readw ( host , SDHCI_HOST_CONTROL2 ) ;
/* Select Bus Speed Mode for host */
ctrl_2 & = ~ SDHCI_CTRL_UHS_MASK ;
switch ( timing ) {
case MMC_TIMING_UHS_SDR12 :
ctrl_2 | = SDHCI_CTRL_UHS_SDR12 ;
break ;
case MMC_TIMING_MMC_HS :
case MMC_TIMING_SD_HS :
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_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 ;
case MMC_TIMING_MMC_HS200 :
ctrl_2 | = SDHCI_SPRD_CTRL_HS200 ;
break ;
case MMC_TIMING_MMC_HS400 :
ctrl_2 | = SDHCI_SPRD_CTRL_HS400 ;
break ;
default :
break ;
}
sdhci_writew ( host , ctrl_2 , SDHCI_HOST_CONTROL2 ) ;
2019-06-04 16:14:28 +08:00
if ( ! mmc - > ios . enhanced_strobe )
sdhci_writel ( host , p [ timing ] , SDHCI_SPRD_REG_32_DLL_DLY ) ;
2018-08-30 16:21:44 +08:00
}
static void sdhci_sprd_hw_reset ( struct sdhci_host * host )
{
int val ;
/*
* Note : don ' t use sdhci_writeb ( ) API here since it is redirected to
* sdhci_sprd_writeb ( ) in which we have a workaround for
* SDHCI_SOFTWARE_RESET which would make bit SDHCI_HW_RESET_CARD can
* not be cleared .
*/
val = readb_relaxed ( host - > ioaddr + SDHCI_SOFTWARE_RESET ) ;
val & = ~ SDHCI_HW_RESET_CARD ;
writeb_relaxed ( val , host - > ioaddr + SDHCI_SOFTWARE_RESET ) ;
/* wait for 10 us */
usleep_range ( 10 , 20 ) ;
val | = SDHCI_HW_RESET_CARD ;
writeb_relaxed ( val , host - > ioaddr + SDHCI_SOFTWARE_RESET ) ;
usleep_range ( 300 , 500 ) ;
}
2019-06-04 16:14:24 +08:00
static unsigned int sdhci_sprd_get_max_timeout_count ( struct sdhci_host * host )
{
/* The Spredtrum controller actual maximum timeout count is 1 << 31 */
return 1 < < 31 ;
}
2018-08-30 16:21:44 +08:00
static struct sdhci_ops sdhci_sprd_ops = {
. read_l = sdhci_sprd_readl ,
. write_l = sdhci_sprd_writel ,
. write_b = sdhci_sprd_writeb ,
. set_clock = sdhci_sprd_set_clock ,
. get_max_clock = sdhci_sprd_get_max_clock ,
. get_min_clock = sdhci_sprd_get_min_clock ,
. set_bus_width = sdhci_set_bus_width ,
. reset = sdhci_reset ,
. set_uhs_signaling = sdhci_sprd_set_uhs_signaling ,
. hw_reset = sdhci_sprd_hw_reset ,
2019-06-04 16:14:24 +08:00
. get_max_timeout_count = sdhci_sprd_get_max_timeout_count ,
2018-08-30 16:21:44 +08:00
} ;
static void sdhci_sprd_request ( struct mmc_host * mmc , struct mmc_request * mrq )
{
struct sdhci_host * host = mmc_priv ( mmc ) ;
struct sdhci_sprd_host * sprd_host = TO_SPRD_HOST ( host ) ;
host - > flags | = sprd_host - > flags & SDHCI_AUTO_CMD23 ;
/*
* From version 4.10 onward , ARGUMENT2 register is also as 32 - bit
* block count register which doesn ' t support stuff bits of
* CMD23 argument on Spreadtrum ' s sd host controller .
*/
if ( host - > version > = SDHCI_SPEC_410 & &
mrq - > sbc & & ( mrq - > sbc - > arg & SDHCI_SPRD_ARG2_STUFF ) & &
( host - > flags & SDHCI_AUTO_CMD23 ) )
host - > flags & = ~ SDHCI_AUTO_CMD23 ;
sdhci_request ( mmc , mrq ) ;
}
2019-06-21 14:12:31 +08:00
static int sdhci_sprd_voltage_switch ( struct mmc_host * mmc , struct mmc_ios * ios )
{
2019-06-21 14:12:33 +08:00
struct sdhci_host * host = mmc_priv ( mmc ) ;
struct sdhci_sprd_host * sprd_host = TO_SPRD_HOST ( host ) ;
2019-06-21 14:12:31 +08:00
int ret ;
if ( ! IS_ERR ( mmc - > supply . vqmmc ) ) {
ret = mmc_regulator_set_vqmmc ( mmc , ios ) ;
if ( ret ) {
pr_err ( " %s: Switching signalling voltage failed \n " ,
mmc_hostname ( mmc ) ) ;
return ret ;
}
}
2019-06-21 14:12:33 +08:00
if ( IS_ERR ( sprd_host - > pinctrl ) )
return 0 ;
switch ( ios - > signal_voltage ) {
case MMC_SIGNAL_VOLTAGE_180 :
ret = pinctrl_select_state ( sprd_host - > pinctrl ,
sprd_host - > pins_uhs ) ;
if ( ret ) {
pr_err ( " %s: failed to select uhs pin state \n " ,
mmc_hostname ( mmc ) ) ;
return ret ;
}
break ;
default :
/* fall-through */
case MMC_SIGNAL_VOLTAGE_330 :
ret = pinctrl_select_state ( sprd_host - > pinctrl ,
sprd_host - > pins_default ) ;
if ( ret ) {
pr_err ( " %s: failed to select default pin state \n " ,
mmc_hostname ( mmc ) ) ;
return ret ;
}
break ;
}
/* Wait for 300 ~ 500 us for pin state stable */
usleep_range ( 300 , 500 ) ;
sdhci_reset ( host , SDHCI_RESET_CMD | SDHCI_RESET_DATA ) ;
2019-06-21 14:12:31 +08:00
return 0 ;
}
2019-06-04 16:14:25 +08:00
static void sdhci_sprd_hs400_enhanced_strobe ( struct mmc_host * mmc ,
struct mmc_ios * ios )
{
struct sdhci_host * host = mmc_priv ( mmc ) ;
2019-06-04 16:14:28 +08:00
struct sdhci_sprd_host * sprd_host = TO_SPRD_HOST ( host ) ;
u32 * p = sprd_host - > phy_delay ;
2019-06-04 16:14:25 +08:00
u16 ctrl_2 ;
if ( ! ios - > enhanced_strobe )
return ;
sdhci_sprd_sd_clk_off ( host ) ;
/* Set HS400 enhanced strobe mode */
ctrl_2 = sdhci_readw ( host , SDHCI_HOST_CONTROL2 ) ;
ctrl_2 & = ~ SDHCI_CTRL_UHS_MASK ;
ctrl_2 | = SDHCI_SPRD_CTRL_HS400ES ;
sdhci_writew ( host , ctrl_2 , SDHCI_HOST_CONTROL2 ) ;
sdhci_sprd_sd_clk_on ( host ) ;
2019-06-04 16:14:28 +08:00
/* Set the PHY DLL delay value for HS400 enhanced strobe mode */
sdhci_writel ( host , p [ MMC_TIMING_MMC_HS400 + 1 ] ,
SDHCI_SPRD_REG_32_DLL_DLY ) ;
}
static void sdhci_sprd_phy_param_parse ( struct sdhci_sprd_host * sprd_host ,
struct device_node * np )
{
u32 * p = sprd_host - > phy_delay ;
int ret , i , index ;
u32 val [ 4 ] ;
for ( i = 0 ; i < ARRAY_SIZE ( sdhci_sprd_phy_cfgs ) ; i + + ) {
ret = of_property_read_u32_array ( np ,
sdhci_sprd_phy_cfgs [ i ] . property , val , 4 ) ;
if ( ret )
continue ;
index = sdhci_sprd_phy_cfgs [ i ] . timing ;
p [ index ] = val [ 0 ] | ( val [ 1 ] < < 8 ) | ( val [ 2 ] < < 16 ) | ( val [ 3 ] < < 24 ) ;
}
2019-06-04 16:14:25 +08:00
}
2018-08-30 16:21:44 +08:00
static const struct sdhci_pltfm_data sdhci_sprd_pdata = {
. quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK ,
. quirks2 = SDHCI_QUIRK2_BROKEN_HS200 |
SDHCI_QUIRK2_USE_32BIT_BLK_CNT ,
. ops = & sdhci_sprd_ops ,
} ;
static int sdhci_sprd_probe ( struct platform_device * pdev )
{
struct sdhci_host * host ;
struct sdhci_sprd_host * sprd_host ;
struct clk * clk ;
int ret = 0 ;
host = sdhci_pltfm_init ( pdev , & sdhci_sprd_pdata , sizeof ( * sprd_host ) ) ;
if ( IS_ERR ( host ) )
return PTR_ERR ( host ) ;
host - > dma_mask = DMA_BIT_MASK ( 64 ) ;
pdev - > dev . dma_mask = & host - > dma_mask ;
host - > mmc_host_ops . request = sdhci_sprd_request ;
2019-06-04 16:14:25 +08:00
host - > mmc_host_ops . hs400_enhanced_strobe =
sdhci_sprd_hs400_enhanced_strobe ;
2019-06-21 14:12:31 +08:00
/*
* We can not use the standard ops to change and detect the voltage
* signal for Spreadtrum SD host controller , since our voltage regulator
* for I / O is fixed in hardware , that means we do not need control
* the standard SD host controller to change the I / O voltage .
*/
host - > mmc_host_ops . start_signal_voltage_switch =
sdhci_sprd_voltage_switch ;
2018-08-30 16:21:44 +08:00
host - > mmc - > caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
MMC_CAP_ERASE | MMC_CAP_CMD23 ;
ret = mmc_of_parse ( host - > mmc ) ;
if ( ret )
goto pltfm_free ;
sprd_host = TO_SPRD_HOST ( host ) ;
2019-06-04 16:14:28 +08:00
sdhci_sprd_phy_param_parse ( sprd_host , pdev - > dev . of_node ) ;
2018-08-30 16:21:44 +08:00
2019-06-21 14:12:33 +08:00
sprd_host - > pinctrl = devm_pinctrl_get ( & pdev - > dev ) ;
if ( ! IS_ERR ( sprd_host - > pinctrl ) ) {
sprd_host - > pins_uhs =
pinctrl_lookup_state ( sprd_host - > pinctrl , " state_uhs " ) ;
if ( IS_ERR ( sprd_host - > pins_uhs ) ) {
ret = PTR_ERR ( sprd_host - > pins_uhs ) ;
goto pltfm_free ;
}
sprd_host - > pins_default =
pinctrl_lookup_state ( sprd_host - > pinctrl , " default " ) ;
if ( IS_ERR ( sprd_host - > pins_default ) ) {
ret = PTR_ERR ( sprd_host - > pins_default ) ;
goto pltfm_free ;
}
}
2018-08-30 16:21:44 +08:00
clk = devm_clk_get ( & pdev - > dev , " sdio " ) ;
if ( IS_ERR ( clk ) ) {
ret = PTR_ERR ( clk ) ;
goto pltfm_free ;
}
sprd_host - > clk_sdio = clk ;
sprd_host - > base_rate = clk_get_rate ( sprd_host - > clk_sdio ) ;
if ( ! sprd_host - > base_rate )
sprd_host - > base_rate = SDHCI_SPRD_CLK_DEF_RATE ;
clk = devm_clk_get ( & pdev - > dev , " enable " ) ;
if ( IS_ERR ( clk ) ) {
ret = PTR_ERR ( clk ) ;
goto pltfm_free ;
}
sprd_host - > clk_enable = clk ;
2019-06-04 16:14:23 +08:00
clk = devm_clk_get ( & pdev - > dev , " 2x_enable " ) ;
if ( ! IS_ERR ( clk ) )
sprd_host - > clk_2x_enable = clk ;
2018-08-30 16:21:44 +08:00
ret = clk_prepare_enable ( sprd_host - > clk_sdio ) ;
if ( ret )
goto pltfm_free ;
2019-06-04 16:14:21 +08:00
ret = clk_prepare_enable ( sprd_host - > clk_enable ) ;
2018-08-30 16:21:44 +08:00
if ( ret )
goto clk_disable ;
2019-06-04 16:14:23 +08:00
ret = clk_prepare_enable ( sprd_host - > clk_2x_enable ) ;
if ( ret )
goto clk_disable2 ;
2018-08-30 16:21:44 +08:00
sdhci_sprd_init_config ( host ) ;
host - > version = sdhci_readw ( host , SDHCI_HOST_VERSION ) ;
sprd_host - > version = ( ( host - > version & SDHCI_VENDOR_VER_MASK ) > >
SDHCI_VENDOR_VER_SHIFT ) ;
pm_runtime_get_noresume ( & pdev - > dev ) ;
pm_runtime_set_active ( & pdev - > dev ) ;
pm_runtime_enable ( & pdev - > dev ) ;
pm_runtime_set_autosuspend_delay ( & pdev - > dev , 50 ) ;
pm_runtime_use_autosuspend ( & pdev - > dev ) ;
pm_suspend_ignore_children ( & pdev - > dev , 1 ) ;
sdhci_enable_v4_mode ( host ) ;
ret = sdhci_setup_host ( host ) ;
if ( ret )
goto pm_runtime_disable ;
sprd_host - > flags = host - > flags ;
ret = __sdhci_add_host ( host ) ;
if ( ret )
goto err_cleanup_host ;
pm_runtime_mark_last_busy ( & pdev - > dev ) ;
pm_runtime_put_autosuspend ( & pdev - > dev ) ;
return 0 ;
err_cleanup_host :
sdhci_cleanup_host ( host ) ;
pm_runtime_disable :
2019-07-15 18:00:14 +08:00
pm_runtime_put_noidle ( & pdev - > dev ) ;
2018-08-30 16:21:44 +08:00
pm_runtime_disable ( & pdev - > dev ) ;
pm_runtime_set_suspended ( & pdev - > dev ) ;
2019-06-04 16:14:23 +08:00
clk_disable_unprepare ( sprd_host - > clk_2x_enable ) ;
clk_disable2 :
2018-08-30 16:21:44 +08:00
clk_disable_unprepare ( sprd_host - > clk_enable ) ;
clk_disable :
clk_disable_unprepare ( sprd_host - > clk_sdio ) ;
pltfm_free :
sdhci_pltfm_free ( pdev ) ;
return ret ;
}
static int sdhci_sprd_remove ( struct platform_device * pdev )
{
struct sdhci_host * host = platform_get_drvdata ( pdev ) ;
struct sdhci_sprd_host * sprd_host = TO_SPRD_HOST ( host ) ;
struct mmc_host * mmc = host - > mmc ;
mmc_remove_host ( mmc ) ;
clk_disable_unprepare ( sprd_host - > clk_sdio ) ;
clk_disable_unprepare ( sprd_host - > clk_enable ) ;
2019-06-04 16:14:23 +08:00
clk_disable_unprepare ( sprd_host - > clk_2x_enable ) ;
2018-08-30 16:21:44 +08:00
mmc_free_host ( mmc ) ;
return 0 ;
}
static const struct of_device_id sdhci_sprd_of_match [ ] = {
{ . compatible = " sprd,sdhci-r11 " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , sdhci_sprd_of_match ) ;
# ifdef CONFIG_PM
static int sdhci_sprd_runtime_suspend ( struct device * dev )
{
struct sdhci_host * host = dev_get_drvdata ( dev ) ;
struct sdhci_sprd_host * sprd_host = TO_SPRD_HOST ( host ) ;
sdhci_runtime_suspend_host ( host ) ;
clk_disable_unprepare ( sprd_host - > clk_sdio ) ;
clk_disable_unprepare ( sprd_host - > clk_enable ) ;
2019-06-04 16:14:23 +08:00
clk_disable_unprepare ( sprd_host - > clk_2x_enable ) ;
2018-08-30 16:21:44 +08:00
return 0 ;
}
static int sdhci_sprd_runtime_resume ( struct device * dev )
{
struct sdhci_host * host = dev_get_drvdata ( dev ) ;
struct sdhci_sprd_host * sprd_host = TO_SPRD_HOST ( host ) ;
int ret ;
2019-06-04 16:14:23 +08:00
ret = clk_prepare_enable ( sprd_host - > clk_2x_enable ) ;
2018-08-30 16:21:44 +08:00
if ( ret )
return ret ;
2019-06-04 16:14:23 +08:00
ret = clk_prepare_enable ( sprd_host - > clk_enable ) ;
if ( ret )
goto clk_2x_disable ;
2018-08-30 16:21:44 +08:00
ret = clk_prepare_enable ( sprd_host - > clk_sdio ) ;
2019-06-04 16:14:23 +08:00
if ( ret )
goto clk_disable ;
2018-08-30 16:21:44 +08:00
2019-07-25 11:14:22 +08:00
sdhci_runtime_resume_host ( host , 1 ) ;
2018-08-30 16:21:44 +08:00
return 0 ;
2019-06-04 16:14:23 +08:00
clk_disable :
clk_disable_unprepare ( sprd_host - > clk_enable ) ;
clk_2x_disable :
clk_disable_unprepare ( sprd_host - > clk_2x_enable ) ;
return ret ;
2018-08-30 16:21:44 +08:00
}
# endif
static const struct dev_pm_ops sdhci_sprd_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( pm_runtime_force_suspend ,
pm_runtime_force_resume )
SET_RUNTIME_PM_OPS ( sdhci_sprd_runtime_suspend ,
sdhci_sprd_runtime_resume , NULL )
} ;
static struct platform_driver sdhci_sprd_driver = {
. probe = sdhci_sprd_probe ,
. remove = sdhci_sprd_remove ,
. driver = {
. name = " sdhci_sprd_r11 " ,
. of_match_table = of_match_ptr ( sdhci_sprd_of_match ) ,
. pm = & sdhci_sprd_pm_ops ,
} ,
} ;
module_platform_driver ( sdhci_sprd_driver ) ;
MODULE_DESCRIPTION ( " Spreadtrum sdio host controller r11 driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_ALIAS ( " platform:sdhci-sprd-r11 " ) ;