2011-01-02 07:52:56 +03:00
/*
* Copyright ( C ) 2010 Google , Inc .
*
* This software is licensed under the terms of the GNU General Public
* License version 2 , as published by the Free Software Foundation , and
* may be copied , distributed , and modified under those terms .
*
* 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 .
*
*/
2016-02-29 23:56:25 +03:00
# include <linux/delay.h>
2011-01-02 07:52:56 +03:00
# include <linux/err.h>
2011-07-03 23:15:51 +04:00
# include <linux/module.h>
2011-01-02 07:52:56 +03:00
# include <linux/init.h>
2018-08-30 18:06:13 +03:00
# include <linux/iopoll.h>
2011-01-02 07:52:56 +03:00
# include <linux/platform_device.h>
# include <linux/clk.h>
# include <linux/io.h>
2011-08-30 23:17:16 +04:00
# include <linux/of.h>
2012-02-02 03:30:55 +04:00
# include <linux/of_device.h>
2018-08-30 18:06:12 +03:00
# include <linux/pinctrl/consumer.h>
# include <linux/regulator/consumer.h>
2017-03-08 22:00:39 +03:00
# include <linux/reset.h>
2011-01-02 07:52:56 +03:00
# include <linux/mmc/card.h>
# include <linux/mmc/host.h>
2015-12-22 21:41:02 +03:00
# include <linux/mmc/mmc.h>
2013-03-12 00:44:11 +04:00
# include <linux/mmc/slot-gpio.h>
2015-03-31 00:39:25 +03:00
# include <linux/gpio/consumer.h>
2011-01-02 07:52:56 +03:00
# include "sdhci-pltfm.h"
2012-04-18 17:18:02 +04:00
/* Tegra SDHOST controller vendor register definitions */
2015-12-22 21:41:01 +03:00
# define SDHCI_TEGRA_VENDOR_CLOCK_CTRL 0x100
2015-12-22 21:41:02 +03:00
# define SDHCI_CLOCK_CTRL_TAP_MASK 0x00ff0000
# define SDHCI_CLOCK_CTRL_TAP_SHIFT 16
2018-08-30 18:06:23 +03:00
# define SDHCI_CLOCK_CTRL_TRIM_MASK 0x1f000000
# define SDHCI_CLOCK_CTRL_TRIM_SHIFT 24
2015-12-22 21:41:02 +03:00
# define SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE BIT(5)
2015-12-22 21:41:01 +03:00
# define SDHCI_CLOCK_CTRL_PADPIPE_CLKEN_OVERRIDE BIT(3)
# define SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE BIT(2)
2018-08-30 18:06:14 +03:00
# define SDHCI_TEGRA_VENDOR_MISC_CTRL 0x120
# define SDHCI_MISC_CTRL_ENABLE_SDR104 0x8
# define SDHCI_MISC_CTRL_ENABLE_SDR50 0x10
# define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 0x20
# define SDHCI_MISC_CTRL_ENABLE_DDR50 0x200
2018-08-30 18:06:20 +03:00
# define SDHCI_VNDR_TUN_CTRL0_0 0x1c0
# define SDHCI_VNDR_TUN_CTRL0_TUN_HW_TAP 0x20000
2018-08-30 18:06:14 +03:00
# define SDHCI_TEGRA_AUTO_CAL_CONFIG 0x1e4
# define SDHCI_AUTO_CAL_START BIT(31)
# define SDHCI_AUTO_CAL_ENABLE BIT(29)
2018-08-30 18:06:17 +03:00
# define SDHCI_AUTO_CAL_PDPU_OFFSET_MASK 0x0000ffff
2018-08-30 18:06:14 +03:00
# define SDHCI_TEGRA_SDMEM_COMP_PADCTRL 0x1e0
# define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK 0x0000000f
# define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL 0x7
2018-08-30 18:06:15 +03:00
# define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD BIT(31)
2018-08-30 18:06:14 +03:00
# define SDHCI_TEGRA_AUTO_CAL_STATUS 0x1ec
# define SDHCI_TEGRA_AUTO_CAL_ACTIVE BIT(31)
# define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0)
# define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1)
# define NVQUIRK_ENABLE_SDHCI_SPEC_300 BIT(2)
# define NVQUIRK_ENABLE_SDR50 BIT(3)
# define NVQUIRK_ENABLE_SDR104 BIT(4)
# define NVQUIRK_ENABLE_DDR50 BIT(5)
# define NVQUIRK_HAS_PADCALIB BIT(6)
# define NVQUIRK_NEEDS_PAD_CONTROL BIT(7)
2018-08-30 18:06:20 +03:00
# define NVQUIRK_DIS_CARD_CLK_CONFIG_TAP BIT(8)
2012-02-02 03:30:55 +04:00
struct sdhci_tegra_soc_data {
2013-03-13 22:26:03 +04:00
const struct sdhci_pltfm_data * pdata ;
2012-02-02 03:30:55 +04:00
u32 nvquirks ;
} ;
2018-08-30 18:06:17 +03:00
/* Magic pull up and pull down pad calibration offsets */
struct sdhci_tegra_autocal_offsets {
u32 pull_up_3v3 ;
u32 pull_down_3v3 ;
u32 pull_up_3v3_timeout ;
u32 pull_down_3v3_timeout ;
u32 pull_up_1v8 ;
u32 pull_down_1v8 ;
u32 pull_up_1v8_timeout ;
u32 pull_down_1v8_timeout ;
u32 pull_up_sdr104 ;
u32 pull_down_sdr104 ;
u32 pull_up_hs400 ;
u32 pull_down_hs400 ;
} ;
2012-02-02 03:30:55 +04:00
struct sdhci_tegra {
const struct sdhci_tegra_soc_data * soc_data ;
2015-03-31 00:39:25 +03:00
struct gpio_desc * power_gpio ;
2015-12-22 21:41:00 +03:00
bool ddr_signaling ;
2016-02-29 23:56:25 +03:00
bool pad_calib_required ;
2018-08-30 18:06:12 +03:00
bool pad_control_available ;
2017-03-08 22:00:39 +03:00
struct reset_control * rst ;
2018-08-30 18:06:12 +03:00
struct pinctrl * pinctrl_sdmmc ;
struct pinctrl_state * pinctrl_state_3v3 ;
struct pinctrl_state * pinctrl_state_1v8 ;
2018-08-30 18:06:17 +03:00
struct sdhci_tegra_autocal_offsets autocal_offsets ;
2018-08-30 18:06:21 +03:00
u32 default_tap ;
u32 default_trim ;
2012-02-02 03:30:55 +04:00
} ;
2011-01-02 07:52:56 +03:00
static u16 tegra_sdhci_readw ( struct sdhci_host * host , int reg )
{
2012-02-02 03:30:55 +04:00
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
2016-02-16 16:08:29 +03:00
struct sdhci_tegra * tegra_host = sdhci_pltfm_priv ( pltfm_host ) ;
2012-02-02 03:30:55 +04:00
const struct sdhci_tegra_soc_data * soc_data = tegra_host - > soc_data ;
if ( unlikely ( ( soc_data - > nvquirks & NVQUIRK_FORCE_SDHCI_SPEC_200 ) & &
( reg = = SDHCI_HOST_VERSION ) ) ) {
2011-01-02 07:52:56 +03:00
/* Erratum: Version register is invalid in HW. */
return SDHCI_SPEC_200 ;
}
return readw ( host - > ioaddr + reg ) ;
}
2015-01-28 19:45:16 +03:00
static void tegra_sdhci_writew ( struct sdhci_host * host , u16 val , int reg )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
2015-02-11 20:55:51 +03:00
switch ( reg ) {
case SDHCI_TRANSFER_MODE :
/*
* Postpone this write , we must do it together with a
* command write that is down below .
*/
pltfm_host - > xfer_mode_shadow = val ;
return ;
case SDHCI_COMMAND :
writel ( ( val < < 16 ) | pltfm_host - > xfer_mode_shadow ,
host - > ioaddr + SDHCI_TRANSFER_MODE ) ;
return ;
2015-01-28 19:45:16 +03:00
}
writew ( val , host - > ioaddr + reg ) ;
}
2011-01-02 07:52:56 +03:00
static void tegra_sdhci_writel ( struct sdhci_host * host , u32 val , int reg )
{
2012-02-02 03:30:55 +04:00
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
2016-02-16 16:08:29 +03:00
struct sdhci_tegra * tegra_host = sdhci_pltfm_priv ( pltfm_host ) ;
2012-02-02 03:30:55 +04:00
const struct sdhci_tegra_soc_data * soc_data = tegra_host - > soc_data ;
2011-01-02 07:52:56 +03:00
/* Seems like we're getting spurious timeout and crc errors, so
* disable signalling of them . In case of real errors software
* timers should take care of eventually detecting them .
*/
if ( unlikely ( reg = = SDHCI_SIGNAL_ENABLE ) )
val & = ~ ( SDHCI_INT_TIMEOUT | SDHCI_INT_CRC ) ;
writel ( val , host - > ioaddr + reg ) ;
2012-02-02 03:30:55 +04:00
if ( unlikely ( ( soc_data - > nvquirks & NVQUIRK_ENABLE_BLOCK_GAP_DET ) & &
( reg = = SDHCI_INT_ENABLE ) ) ) {
2011-01-02 07:52:56 +03:00
/* Erratum: Must enable block gap interrupt detection */
u8 gap_ctrl = readb ( host - > ioaddr + SDHCI_BLOCK_GAP_CONTROL ) ;
if ( val & SDHCI_INT_CARD_INT )
gap_ctrl | = 0x8 ;
else
gap_ctrl & = ~ 0x8 ;
writeb ( gap_ctrl , host - > ioaddr + SDHCI_BLOCK_GAP_CONTROL ) ;
}
}
2012-02-02 03:30:55 +04:00
static unsigned int tegra_sdhci_get_ro ( struct sdhci_host * host )
2011-01-02 07:52:56 +03:00
{
2013-03-12 00:44:11 +04:00
return mmc_gpio_get_ro ( host - > mmc ) ;
2011-01-02 07:52:56 +03:00
}
2018-08-30 18:06:12 +03:00
static bool tegra_sdhci_is_pad_and_regulator_valid ( struct sdhci_host * host )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_tegra * tegra_host = sdhci_pltfm_priv ( pltfm_host ) ;
int has_1v8 , has_3v3 ;
/*
* The SoCs which have NVQUIRK_NEEDS_PAD_CONTROL require software pad
* voltage configuration in order to perform voltage switching . This
* means that valid pinctrl info is required on SDHCI instances capable
* of performing voltage switching . Whether or not an SDHCI instance is
* capable of voltage switching is determined based on the regulator .
*/
if ( ! ( tegra_host - > soc_data - > nvquirks & NVQUIRK_NEEDS_PAD_CONTROL ) )
return true ;
if ( IS_ERR ( host - > mmc - > supply . vqmmc ) )
return false ;
has_1v8 = regulator_is_supported_voltage ( host - > mmc - > supply . vqmmc ,
1700000 , 1950000 ) ;
has_3v3 = regulator_is_supported_voltage ( host - > mmc - > supply . vqmmc ,
2700000 , 3600000 ) ;
if ( has_1v8 = = 1 & & has_3v3 = = 1 )
return tegra_host - > pad_control_available ;
/* Fixed voltage, no pad control required. */
return true ;
}
2018-08-30 18:06:22 +03:00
static bool tegra_sdhci_configure_card_clk ( struct sdhci_host * host , bool enable )
{
bool status ;
u32 reg ;
reg = sdhci_readw ( host , SDHCI_CLOCK_CONTROL ) ;
status = ! ! ( reg & SDHCI_CLOCK_CARD_EN ) ;
if ( status = = enable )
return status ;
if ( enable )
reg | = SDHCI_CLOCK_CARD_EN ;
else
reg & = ~ SDHCI_CLOCK_CARD_EN ;
sdhci_writew ( host , reg , SDHCI_CLOCK_CONTROL ) ;
return status ;
}
static void tegra_sdhci_set_tap ( struct sdhci_host * host , unsigned int tap )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_tegra * tegra_host = sdhci_pltfm_priv ( pltfm_host ) ;
const struct sdhci_tegra_soc_data * soc_data = tegra_host - > soc_data ;
bool card_clk_enabled = false ;
u32 reg ;
/*
* Touching the tap values is a bit tricky on some SoC generations .
* The quirk enables a workaround for a glitch that sometimes occurs if
* the tap values are changed .
*/
if ( soc_data - > nvquirks & NVQUIRK_DIS_CARD_CLK_CONFIG_TAP )
card_clk_enabled = tegra_sdhci_configure_card_clk ( host , false ) ;
reg = sdhci_readl ( host , SDHCI_TEGRA_VENDOR_CLOCK_CTRL ) ;
reg & = ~ SDHCI_CLOCK_CTRL_TAP_MASK ;
reg | = tap < < SDHCI_CLOCK_CTRL_TAP_SHIFT ;
sdhci_writel ( host , reg , SDHCI_TEGRA_VENDOR_CLOCK_CTRL ) ;
if ( soc_data - > nvquirks & NVQUIRK_DIS_CARD_CLK_CONFIG_TAP & &
card_clk_enabled ) {
udelay ( 1 ) ;
sdhci_reset ( host , SDHCI_RESET_CMD | SDHCI_RESET_DATA ) ;
tegra_sdhci_configure_card_clk ( host , card_clk_enabled ) ;
}
}
2014-04-25 15:57:12 +04:00
static void tegra_sdhci_reset ( struct sdhci_host * host , u8 mask )
2012-04-18 17:18:02 +04:00
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
2016-02-16 16:08:29 +03:00
struct sdhci_tegra * tegra_host = sdhci_pltfm_priv ( pltfm_host ) ;
2012-04-18 17:18:02 +04:00
const struct sdhci_tegra_soc_data * soc_data = tegra_host - > soc_data ;
2018-08-30 18:06:14 +03:00
u32 misc_ctrl , clk_ctrl , pad_ctrl ;
2012-04-18 17:18:02 +04:00
2014-04-25 15:57:12 +04:00
sdhci_reset ( host , mask ) ;
2012-04-18 17:18:02 +04:00
if ( ! ( mask & SDHCI_RESET_ALL ) )
return ;
2018-08-30 18:06:22 +03:00
tegra_sdhci_set_tap ( host , tegra_host - > default_tap ) ;
2015-12-22 21:41:04 +03:00
misc_ctrl = sdhci_readl ( host , SDHCI_TEGRA_VENDOR_MISC_CTRL ) ;
2015-12-22 21:41:01 +03:00
clk_ctrl = sdhci_readl ( host , SDHCI_TEGRA_VENDOR_CLOCK_CTRL ) ;
2016-07-12 16:53:37 +03:00
misc_ctrl & = ~ ( SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 |
SDHCI_MISC_CTRL_ENABLE_SDR50 |
SDHCI_MISC_CTRL_ENABLE_DDR50 |
SDHCI_MISC_CTRL_ENABLE_SDR104 ) ;
2018-08-30 18:06:23 +03:00
clk_ctrl & = ~ ( SDHCI_CLOCK_CTRL_TRIM_MASK |
SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE ) ;
2016-07-12 16:53:37 +03:00
2018-08-30 18:06:12 +03:00
if ( tegra_sdhci_is_pad_and_regulator_valid ( host ) ) {
2016-07-12 16:53:37 +03:00
/* Erratum: Enable SDHCI spec v3.00 support */
if ( soc_data - > nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300 )
misc_ctrl | = SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 ;
/* Advertise UHS modes as supported by host */
if ( soc_data - > nvquirks & NVQUIRK_ENABLE_SDR50 )
misc_ctrl | = SDHCI_MISC_CTRL_ENABLE_SDR50 ;
if ( soc_data - > nvquirks & NVQUIRK_ENABLE_DDR50 )
misc_ctrl | = SDHCI_MISC_CTRL_ENABLE_DDR50 ;
if ( soc_data - > nvquirks & NVQUIRK_ENABLE_SDR104 )
misc_ctrl | = SDHCI_MISC_CTRL_ENABLE_SDR104 ;
if ( soc_data - > nvquirks & SDHCI_MISC_CTRL_ENABLE_SDR50 )
clk_ctrl | = SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE ;
}
2018-08-30 18:06:23 +03:00
clk_ctrl | = tegra_host - > default_trim < < SDHCI_CLOCK_CTRL_TRIM_SHIFT ;
2016-07-12 16:53:37 +03:00
sdhci_writel ( host , misc_ctrl , SDHCI_TEGRA_VENDOR_MISC_CTRL ) ;
2015-12-22 21:41:01 +03:00
sdhci_writel ( host , clk_ctrl , SDHCI_TEGRA_VENDOR_CLOCK_CTRL ) ;
2018-08-30 18:06:14 +03:00
if ( soc_data - > nvquirks & NVQUIRK_HAS_PADCALIB ) {
pad_ctrl = sdhci_readl ( host , SDHCI_TEGRA_SDMEM_COMP_PADCTRL ) ;
pad_ctrl & = ~ SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK ;
pad_ctrl | = SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL ;
sdhci_writel ( host , pad_ctrl , SDHCI_TEGRA_SDMEM_COMP_PADCTRL ) ;
2016-02-29 23:56:25 +03:00
tegra_host - > pad_calib_required = true ;
2018-08-30 18:06:14 +03:00
}
2016-02-29 23:56:25 +03:00
2015-12-22 21:41:00 +03:00
tegra_host - > ddr_signaling = false ;
2012-04-18 17:18:02 +04:00
}
2018-08-30 18:06:15 +03:00
static void tegra_sdhci_configure_cal_pad ( struct sdhci_host * host , bool enable )
{
u32 val ;
/*
* Enable or disable the additional I / O pad used by the drive strength
* calibration process .
*/
val = sdhci_readl ( host , SDHCI_TEGRA_SDMEM_COMP_PADCTRL ) ;
if ( enable )
val | = SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD ;
else
val & = ~ SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD ;
sdhci_writel ( host , val , SDHCI_TEGRA_SDMEM_COMP_PADCTRL ) ;
if ( enable )
usleep_range ( 1 , 2 ) ;
}
2018-08-30 18:06:17 +03:00
static void tegra_sdhci_set_pad_autocal_offset ( struct sdhci_host * host ,
u16 pdpu )
{
u32 reg ;
reg = sdhci_readl ( host , SDHCI_TEGRA_AUTO_CAL_CONFIG ) ;
reg & = ~ SDHCI_AUTO_CAL_PDPU_OFFSET_MASK ;
reg | = pdpu ;
sdhci_writel ( host , reg , SDHCI_TEGRA_AUTO_CAL_CONFIG ) ;
}
2016-02-29 23:56:25 +03:00
static void tegra_sdhci_pad_autocalib ( struct sdhci_host * host )
{
2018-08-30 18:06:17 +03:00
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_tegra * tegra_host = sdhci_pltfm_priv ( pltfm_host ) ;
struct sdhci_tegra_autocal_offsets offsets =
tegra_host - > autocal_offsets ;
struct mmc_ios * ios = & host - > mmc - > ios ;
2018-08-30 18:06:16 +03:00
bool card_clk_enabled ;
2018-08-30 18:06:17 +03:00
u16 pdpu ;
2018-08-30 18:06:13 +03:00
u32 reg ;
int ret ;
2018-08-30 18:06:17 +03:00
switch ( ios - > timing ) {
case MMC_TIMING_UHS_SDR104 :
pdpu = offsets . pull_down_sdr104 < < 8 | offsets . pull_up_sdr104 ;
break ;
case MMC_TIMING_MMC_HS400 :
pdpu = offsets . pull_down_hs400 < < 8 | offsets . pull_up_hs400 ;
break ;
default :
if ( ios - > signal_voltage = = MMC_SIGNAL_VOLTAGE_180 )
pdpu = offsets . pull_down_1v8 < < 8 | offsets . pull_up_1v8 ;
else
pdpu = offsets . pull_down_3v3 < < 8 | offsets . pull_up_3v3 ;
}
tegra_sdhci_set_pad_autocal_offset ( host , pdpu ) ;
2018-08-30 18:06:16 +03:00
card_clk_enabled = tegra_sdhci_configure_card_clk ( host , false ) ;
2018-08-30 18:06:15 +03:00
tegra_sdhci_configure_cal_pad ( host , true ) ;
2018-08-30 18:06:13 +03:00
reg = sdhci_readl ( host , SDHCI_TEGRA_AUTO_CAL_CONFIG ) ;
reg | = SDHCI_AUTO_CAL_ENABLE | SDHCI_AUTO_CAL_START ;
sdhci_writel ( host , reg , SDHCI_TEGRA_AUTO_CAL_CONFIG ) ;
2016-02-29 23:56:25 +03:00
2018-08-30 18:06:13 +03:00
usleep_range ( 1 , 2 ) ;
/* 10 ms timeout */
ret = readl_poll_timeout ( host - > ioaddr + SDHCI_TEGRA_AUTO_CAL_STATUS ,
reg , ! ( reg & SDHCI_TEGRA_AUTO_CAL_ACTIVE ) ,
1000 , 10000 ) ;
2016-02-29 23:56:25 +03:00
2018-08-30 18:06:15 +03:00
tegra_sdhci_configure_cal_pad ( host , false ) ;
2018-08-30 18:06:16 +03:00
tegra_sdhci_configure_card_clk ( host , card_clk_enabled ) ;
2018-08-30 18:06:17 +03:00
if ( ret ) {
2018-08-30 18:06:13 +03:00
dev_err ( mmc_dev ( host - > mmc ) , " Pad autocal timed out \n " ) ;
2018-08-30 18:06:17 +03:00
if ( ios - > signal_voltage = = MMC_SIGNAL_VOLTAGE_180 )
pdpu = offsets . pull_down_1v8_timeout < < 8 |
offsets . pull_up_1v8_timeout ;
else
pdpu = offsets . pull_down_3v3_timeout < < 8 |
offsets . pull_up_3v3_timeout ;
/* Disable automatic calibration and use fixed offsets */
reg = sdhci_readl ( host , SDHCI_TEGRA_AUTO_CAL_CONFIG ) ;
reg & = ~ SDHCI_AUTO_CAL_ENABLE ;
sdhci_writel ( host , reg , SDHCI_TEGRA_AUTO_CAL_CONFIG ) ;
tegra_sdhci_set_pad_autocal_offset ( host , pdpu ) ;
}
}
static void tegra_sdhci_parse_pad_autocal_dt ( struct sdhci_host * host )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_tegra * tegra_host = sdhci_pltfm_priv ( pltfm_host ) ;
struct sdhci_tegra_autocal_offsets * autocal =
& tegra_host - > autocal_offsets ;
int err ;
err = device_property_read_u32 ( host - > mmc - > parent ,
" nvidia,pad-autocal-pull-up-offset-3v3 " ,
& autocal - > pull_up_3v3 ) ;
if ( err )
autocal - > pull_up_3v3 = 0 ;
err = device_property_read_u32 ( host - > mmc - > parent ,
" nvidia,pad-autocal-pull-down-offset-3v3 " ,
& autocal - > pull_down_3v3 ) ;
if ( err )
autocal - > pull_down_3v3 = 0 ;
err = device_property_read_u32 ( host - > mmc - > parent ,
" nvidia,pad-autocal-pull-up-offset-1v8 " ,
& autocal - > pull_up_1v8 ) ;
if ( err )
autocal - > pull_up_1v8 = 0 ;
err = device_property_read_u32 ( host - > mmc - > parent ,
" nvidia,pad-autocal-pull-down-offset-1v8 " ,
& autocal - > pull_down_1v8 ) ;
if ( err )
autocal - > pull_down_1v8 = 0 ;
err = device_property_read_u32 ( host - > mmc - > parent ,
" nvidia,pad-autocal-pull-up-offset-3v3-timeout " ,
& autocal - > pull_up_3v3 ) ;
if ( err )
autocal - > pull_up_3v3_timeout = 0 ;
err = device_property_read_u32 ( host - > mmc - > parent ,
" nvidia,pad-autocal-pull-down-offset-3v3-timeout " ,
& autocal - > pull_down_3v3 ) ;
if ( err )
autocal - > pull_down_3v3_timeout = 0 ;
err = device_property_read_u32 ( host - > mmc - > parent ,
" nvidia,pad-autocal-pull-up-offset-1v8-timeout " ,
& autocal - > pull_up_1v8 ) ;
if ( err )
autocal - > pull_up_1v8_timeout = 0 ;
err = device_property_read_u32 ( host - > mmc - > parent ,
" nvidia,pad-autocal-pull-down-offset-1v8-timeout " ,
& autocal - > pull_down_1v8 ) ;
if ( err )
autocal - > pull_down_1v8_timeout = 0 ;
err = device_property_read_u32 ( host - > mmc - > parent ,
" nvidia,pad-autocal-pull-up-offset-sdr104 " ,
& autocal - > pull_up_sdr104 ) ;
if ( err )
autocal - > pull_up_sdr104 = autocal - > pull_up_1v8 ;
err = device_property_read_u32 ( host - > mmc - > parent ,
" nvidia,pad-autocal-pull-down-offset-sdr104 " ,
& autocal - > pull_down_sdr104 ) ;
if ( err )
autocal - > pull_down_sdr104 = autocal - > pull_down_1v8 ;
err = device_property_read_u32 ( host - > mmc - > parent ,
" nvidia,pad-autocal-pull-up-offset-hs400 " ,
& autocal - > pull_up_hs400 ) ;
if ( err )
autocal - > pull_up_hs400 = autocal - > pull_up_1v8 ;
err = device_property_read_u32 ( host - > mmc - > parent ,
" nvidia,pad-autocal-pull-down-offset-hs400 " ,
& autocal - > pull_down_hs400 ) ;
if ( err )
autocal - > pull_down_hs400 = autocal - > pull_down_1v8 ;
2016-02-29 23:56:25 +03:00
}
2018-08-30 18:06:21 +03:00
static void tegra_sdhci_parse_default_tap_and_trim ( struct sdhci_host * host )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_tegra * tegra_host = sdhci_pltfm_priv ( pltfm_host ) ;
int err ;
err = device_property_read_u32 ( host - > mmc - > parent , " nvidia,default-tap " ,
& tegra_host - > default_tap ) ;
if ( err )
tegra_host - > default_tap = 0 ;
err = device_property_read_u32 ( host - > mmc - > parent , " nvidia,default-trim " ,
& tegra_host - > default_trim ) ;
if ( err )
tegra_host - > default_trim = 0 ;
}
2015-12-22 21:41:00 +03:00
static void tegra_sdhci_set_clock ( struct sdhci_host * host , unsigned int clock )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
2016-02-16 16:08:29 +03:00
struct sdhci_tegra * tegra_host = sdhci_pltfm_priv ( pltfm_host ) ;
2015-12-22 21:41:00 +03:00
unsigned long host_clk ;
if ( ! clock )
2016-02-29 23:56:24 +03:00
return sdhci_set_clock ( host , clock ) ;
2015-12-22 21:41:00 +03:00
2018-07-16 17:34:29 +03:00
/*
* In DDR50 / 52 modes the Tegra SDHCI controllers require the SDHCI
* divider to be configured to divided the host clock by two . The SDHCI
* clock divider is calculated as part of sdhci_set_clock ( ) by
* sdhci_calc_clk ( ) . The divider is calculated from host - > max_clk and
* the requested clock rate .
*
* By setting the host - > max_clk to clock * 2 the divider calculation
* will always result in the correct value for DDR50 / 52 modes ,
* regardless of clock rate rounding , which may happen if the value
* from clk_get_rate ( ) is used .
*/
2015-12-22 21:41:00 +03:00
host_clk = tegra_host - > ddr_signaling ? clock * 2 : clock ;
clk_set_rate ( pltfm_host - > clk , host_clk ) ;
2018-07-16 17:34:29 +03:00
if ( tegra_host - > ddr_signaling )
host - > max_clk = host_clk ;
else
host - > max_clk = clk_get_rate ( pltfm_host - > clk ) ;
2015-12-22 21:41:00 +03:00
2016-02-29 23:56:25 +03:00
sdhci_set_clock ( host , clock ) ;
if ( tegra_host - > pad_calib_required ) {
tegra_sdhci_pad_autocalib ( host ) ;
tegra_host - > pad_calib_required = false ;
}
2015-12-22 21:41:00 +03:00
}
2018-07-13 16:17:45 +03:00
static unsigned int tegra_sdhci_get_max_clock ( struct sdhci_host * host )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
return clk_round_rate ( pltfm_host - > clk , UINT_MAX ) ;
}
2018-08-30 18:06:22 +03:00
static void tegra_sdhci_set_uhs_signaling ( struct sdhci_host * host ,
unsigned timing )
2015-12-22 21:41:02 +03:00
{
2018-08-30 18:06:20 +03:00
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_tegra * tegra_host = sdhci_pltfm_priv ( pltfm_host ) ;
2018-08-30 18:06:22 +03:00
bool set_default_tap = false ;
2015-12-22 21:41:02 +03:00
2018-08-30 18:06:22 +03:00
switch ( timing ) {
case MMC_TIMING_UHS_SDR50 :
case MMC_TIMING_UHS_SDR104 :
case MMC_TIMING_MMC_HS200 :
case MMC_TIMING_MMC_HS400 :
/* Don't set default tap on tunable modes. */
break ;
case MMC_TIMING_MMC_DDR52 :
case MMC_TIMING_UHS_DDR50 :
tegra_host - > ddr_signaling = true ;
set_default_tap = true ;
break ;
default :
set_default_tap = true ;
break ;
}
2018-08-30 18:06:20 +03:00
2018-08-30 18:06:22 +03:00
sdhci_set_uhs_signaling ( host , timing ) ;
2018-08-30 18:06:20 +03:00
2018-08-30 18:06:22 +03:00
tegra_sdhci_pad_autocalib ( host ) ;
2018-08-30 18:06:20 +03:00
2018-08-30 18:06:22 +03:00
if ( set_default_tap )
tegra_sdhci_set_tap ( host , tegra_host - > default_tap ) ;
2015-12-22 21:41:02 +03:00
}
static int tegra_sdhci_execute_tuning ( struct sdhci_host * host , u32 opcode )
{
unsigned int min , max ;
/*
* Start search for minimum tap value at 10 , as smaller values are
* may wrongly be reported as working but fail at higher speeds ,
* according to the TRM .
*/
min = 10 ;
while ( min < 255 ) {
tegra_sdhci_set_tap ( host , min ) ;
if ( ! mmc_send_tuning ( host - > mmc , opcode , NULL ) )
break ;
min + + ;
}
/* Find the maximum tap value that still passes. */
max = min + 1 ;
while ( max < 255 ) {
tegra_sdhci_set_tap ( host , max ) ;
if ( mmc_send_tuning ( host - > mmc , opcode , NULL ) ) {
max - - ;
break ;
}
max + + ;
}
/* The TRM states the ideal tap value is at 75% in the passing range. */
tegra_sdhci_set_tap ( host , min + ( ( max - min ) * 3 / 4 ) ) ;
return mmc_send_tuning ( host - > mmc , opcode , NULL ) ;
}
2018-08-30 18:06:12 +03:00
static int tegra_sdhci_set_padctrl ( struct sdhci_host * host , int voltage )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_tegra * tegra_host = sdhci_pltfm_priv ( pltfm_host ) ;
int ret ;
if ( ! tegra_host - > pad_control_available )
return 0 ;
if ( voltage = = MMC_SIGNAL_VOLTAGE_180 ) {
ret = pinctrl_select_state ( tegra_host - > pinctrl_sdmmc ,
tegra_host - > pinctrl_state_1v8 ) ;
if ( ret < 0 )
dev_err ( mmc_dev ( host - > mmc ) ,
" setting 1.8V failed, ret: %d \n " , ret ) ;
} else {
ret = pinctrl_select_state ( tegra_host - > pinctrl_sdmmc ,
tegra_host - > pinctrl_state_3v3 ) ;
if ( ret < 0 )
dev_err ( mmc_dev ( host - > mmc ) ,
" setting 3.3V failed, ret: %d \n " , ret ) ;
}
return ret ;
}
static int sdhci_tegra_start_signal_voltage_switch ( struct mmc_host * mmc ,
struct mmc_ios * ios )
{
struct sdhci_host * host = mmc_priv ( mmc ) ;
2018-08-30 18:06:18 +03:00
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_tegra * tegra_host = sdhci_pltfm_priv ( pltfm_host ) ;
2018-08-30 18:06:12 +03:00
int ret = 0 ;
if ( ios - > signal_voltage = = MMC_SIGNAL_VOLTAGE_330 ) {
ret = tegra_sdhci_set_padctrl ( host , ios - > signal_voltage ) ;
if ( ret < 0 )
return ret ;
ret = sdhci_start_signal_voltage_switch ( mmc , ios ) ;
} else if ( ios - > signal_voltage = = MMC_SIGNAL_VOLTAGE_180 ) {
ret = sdhci_start_signal_voltage_switch ( mmc , ios ) ;
if ( ret < 0 )
return ret ;
ret = tegra_sdhci_set_padctrl ( host , ios - > signal_voltage ) ;
}
2018-08-30 18:06:18 +03:00
if ( tegra_host - > pad_calib_required )
tegra_sdhci_pad_autocalib ( host ) ;
2018-08-30 18:06:12 +03:00
return ret ;
}
static int tegra_sdhci_init_pinctrl_info ( struct device * dev ,
struct sdhci_tegra * tegra_host )
{
tegra_host - > pinctrl_sdmmc = devm_pinctrl_get ( dev ) ;
if ( IS_ERR ( tegra_host - > pinctrl_sdmmc ) ) {
dev_dbg ( dev , " No pinctrl info, err: %ld \n " ,
PTR_ERR ( tegra_host - > pinctrl_sdmmc ) ) ;
return - 1 ;
}
tegra_host - > pinctrl_state_3v3 =
pinctrl_lookup_state ( tegra_host - > pinctrl_sdmmc , " sdmmc-3v3 " ) ;
if ( IS_ERR ( tegra_host - > pinctrl_state_3v3 ) ) {
dev_warn ( dev , " Missing 3.3V pad state, err: %ld \n " ,
PTR_ERR ( tegra_host - > pinctrl_state_3v3 ) ) ;
return - 1 ;
}
tegra_host - > pinctrl_state_1v8 =
pinctrl_lookup_state ( tegra_host - > pinctrl_sdmmc , " sdmmc-1v8 " ) ;
if ( IS_ERR ( tegra_host - > pinctrl_state_1v8 ) ) {
dev_warn ( dev , " Missing 1.8V pad state, err: %ld \n " ,
PTR_ERR ( tegra_host - > pinctrl_state_3v3 ) ) ;
return - 1 ;
}
tegra_host - > pad_control_available = true ;
return 0 ;
}
2016-02-29 23:56:25 +03:00
static void tegra_sdhci_voltage_switch ( struct sdhci_host * host )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_tegra * tegra_host = sdhci_pltfm_priv ( pltfm_host ) ;
const struct sdhci_tegra_soc_data * soc_data = tegra_host - > soc_data ;
if ( soc_data - > nvquirks & NVQUIRK_HAS_PADCALIB )
tegra_host - > pad_calib_required = true ;
}
2013-03-13 22:26:05 +04:00
static const struct sdhci_ops tegra_sdhci_ops = {
2011-05-27 19:48:12 +04:00
. get_ro = tegra_sdhci_get_ro ,
. read_w = tegra_sdhci_readw ,
. write_l = tegra_sdhci_writel ,
2015-12-22 21:41:00 +03:00
. set_clock = tegra_sdhci_set_clock ,
2017-08-14 23:00:24 +03:00
. set_bus_width = sdhci_set_bus_width ,
2014-04-25 15:57:12 +04:00
. reset = tegra_sdhci_reset ,
2015-12-22 21:41:02 +03:00
. platform_execute_tuning = tegra_sdhci_execute_tuning ,
2015-12-22 21:41:00 +03:00
. set_uhs_signaling = tegra_sdhci_set_uhs_signaling ,
2016-02-29 23:56:25 +03:00
. voltage_switch = tegra_sdhci_voltage_switch ,
2018-07-13 16:17:45 +03:00
. get_max_clock = tegra_sdhci_get_max_clock ,
2011-05-27 19:48:12 +04:00
} ;
2013-03-13 22:26:03 +04:00
static const struct sdhci_pltfm_data sdhci_tegra20_pdata = {
2012-02-02 03:30:55 +04:00
. quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
SDHCI_QUIRK_SINGLE_POWER_WRITE |
SDHCI_QUIRK_NO_HISPD_BIT |
2014-05-22 19:55:36 +04:00
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN ,
2012-02-02 03:30:55 +04:00
. ops = & tegra_sdhci_ops ,
} ;
2015-11-16 12:27:14 +03:00
static const struct sdhci_tegra_soc_data soc_data_tegra20 = {
2012-02-02 03:30:55 +04:00
. pdata = & sdhci_tegra20_pdata ,
. nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 |
NVQUIRK_ENABLE_BLOCK_GAP_DET ,
} ;
2013-03-13 22:26:03 +04:00
static const struct sdhci_pltfm_data sdhci_tegra30_pdata = {
2011-05-27 19:48:12 +04:00
. quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
2012-02-02 03:30:55 +04:00
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
2011-05-27 19:48:12 +04:00
SDHCI_QUIRK_SINGLE_POWER_WRITE |
SDHCI_QUIRK_NO_HISPD_BIT |
2014-05-22 19:55:36 +04:00
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN ,
2018-07-12 10:39:02 +03:00
. quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
2018-07-12 10:39:04 +03:00
SDHCI_QUIRK2_BROKEN_HS200 |
/*
* Auto - CMD23 leads to " Got command interrupt 0x00010000 even
* though no command operation was in progress . "
*
* The exact reason is unknown , as the same hardware seems
* to support Auto CMD23 on a downstream 3.1 kernel .
*/
SDHCI_QUIRK2_ACMD23_BROKEN ,
2011-05-27 19:48:12 +04:00
. ops = & tegra_sdhci_ops ,
} ;
2011-01-02 07:52:56 +03:00
2015-11-16 12:27:14 +03:00
static const struct sdhci_tegra_soc_data soc_data_tegra30 = {
2012-02-02 03:30:55 +04:00
. pdata = & sdhci_tegra30_pdata ,
2014-05-22 19:55:35 +04:00
. nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300 |
2015-12-22 21:41:03 +03:00
NVQUIRK_ENABLE_SDR50 |
2016-02-29 23:56:25 +03:00
NVQUIRK_ENABLE_SDR104 |
NVQUIRK_HAS_PADCALIB ,
2012-02-02 03:30:55 +04:00
} ;
2015-02-11 20:55:51 +03:00
static const struct sdhci_ops tegra114_sdhci_ops = {
. get_ro = tegra_sdhci_get_ro ,
. read_w = tegra_sdhci_readw ,
. write_w = tegra_sdhci_writew ,
. write_l = tegra_sdhci_writel ,
2015-12-22 21:41:00 +03:00
. set_clock = tegra_sdhci_set_clock ,
2017-08-14 23:00:24 +03:00
. set_bus_width = sdhci_set_bus_width ,
2015-02-11 20:55:51 +03:00
. reset = tegra_sdhci_reset ,
2015-12-22 21:41:02 +03:00
. platform_execute_tuning = tegra_sdhci_execute_tuning ,
2015-12-22 21:41:00 +03:00
. set_uhs_signaling = tegra_sdhci_set_uhs_signaling ,
2016-02-29 23:56:25 +03:00
. voltage_switch = tegra_sdhci_voltage_switch ,
2018-07-13 16:17:45 +03:00
. get_max_clock = tegra_sdhci_get_max_clock ,
2015-02-11 20:55:51 +03:00
} ;
2013-03-13 22:26:03 +04:00
static const struct sdhci_pltfm_data sdhci_tegra114_pdata = {
2013-02-20 22:35:17 +04:00
. quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
SDHCI_QUIRK_SINGLE_POWER_WRITE |
SDHCI_QUIRK_NO_HISPD_BIT |
2014-05-22 19:55:36 +04:00
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN ,
2015-12-22 21:41:00 +03:00
. quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN ,
2015-02-11 20:55:51 +03:00
. ops = & tegra114_sdhci_ops ,
2013-02-20 22:35:17 +04:00
} ;
2015-11-16 12:27:14 +03:00
static const struct sdhci_tegra_soc_data soc_data_tegra114 = {
2013-02-20 22:35:17 +04:00
. pdata = & sdhci_tegra114_pdata ,
2016-02-26 12:34:17 +03:00
} ;
2016-09-01 14:46:17 +03:00
static const struct sdhci_pltfm_data sdhci_tegra124_pdata = {
. quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
SDHCI_QUIRK_SINGLE_POWER_WRITE |
SDHCI_QUIRK_NO_HISPD_BIT |
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN ,
. quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
/*
* The TRM states that the SD / MMC controller found on
* Tegra124 can address 34 bits ( the maximum supported by
* the Tegra memory controller ) , but tests show that DMA
* to or from above 4 GiB doesn ' t work . This is possibly
* caused by missing programming , though it ' s not obvious
* what sequence is required . Mark 64 - bit DMA broken for
* now to fix this for existing users ( e . g . Nyan boards ) .
*/
SDHCI_QUIRK2_BROKEN_64_BIT_DMA ,
. ops = & tegra114_sdhci_ops ,
} ;
static const struct sdhci_tegra_soc_data soc_data_tegra124 = {
. pdata = & sdhci_tegra124_pdata ,
} ;
2018-08-30 18:06:24 +03:00
static const struct sdhci_ops tegra210_sdhci_ops = {
. get_ro = tegra_sdhci_get_ro ,
. read_w = tegra_sdhci_readw ,
. write_l = tegra_sdhci_writel ,
. set_clock = tegra_sdhci_set_clock ,
. set_bus_width = sdhci_set_bus_width ,
. reset = tegra_sdhci_reset ,
. set_uhs_signaling = tegra_sdhci_set_uhs_signaling ,
. voltage_switch = tegra_sdhci_voltage_switch ,
. get_max_clock = tegra_sdhci_get_max_clock ,
} ;
2015-11-16 12:27:15 +03:00
static const struct sdhci_pltfm_data sdhci_tegra210_pdata = {
. quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
SDHCI_QUIRK_SINGLE_POWER_WRITE |
SDHCI_QUIRK_NO_HISPD_BIT |
2015-12-22 21:41:00 +03:00
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN ,
. quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN ,
2018-08-30 18:06:24 +03:00
. ops = & tegra210_sdhci_ops ,
2015-11-16 12:27:15 +03:00
} ;
static const struct sdhci_tegra_soc_data soc_data_tegra210 = {
. pdata = & sdhci_tegra210_pdata ,
2018-08-30 18:06:19 +03:00
. nvquirks = NVQUIRK_NEEDS_PAD_CONTROL |
2018-08-30 18:06:20 +03:00
NVQUIRK_HAS_PADCALIB |
NVQUIRK_DIS_CARD_CLK_CONFIG_TAP ,
2015-11-16 12:27:15 +03:00
} ;
2017-03-08 22:00:40 +03:00
static const struct sdhci_pltfm_data sdhci_tegra186_pdata = {
. quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
SDHCI_QUIRK_SINGLE_POWER_WRITE |
SDHCI_QUIRK_NO_HISPD_BIT |
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN ,
2017-09-08 22:48:33 +03:00
. quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
/* SDHCI controllers on Tegra186 support 40-bit addressing.
* IOVA addresses are 48 - bit wide on Tegra186 .
* With 64 - bit dma mask used for SDHCI , accesses can
* be broken . Disable 64 - bit dma , which would fall back
* to 32 - bit dma mask . Ideally 40 - bit dma mask would work ,
* But it is not supported as of now .
*/
SDHCI_QUIRK2_BROKEN_64_BIT_DMA ,
2018-08-30 18:06:24 +03:00
. ops = & tegra210_sdhci_ops ,
2017-03-08 22:00:40 +03:00
} ;
static const struct sdhci_tegra_soc_data soc_data_tegra186 = {
. pdata = & sdhci_tegra186_pdata ,
2018-08-30 18:06:19 +03:00
. nvquirks = NVQUIRK_NEEDS_PAD_CONTROL |
2018-08-30 18:06:20 +03:00
NVQUIRK_HAS_PADCALIB |
NVQUIRK_DIS_CARD_CLK_CONFIG_TAP ,
2017-03-08 22:00:40 +03:00
} ;
2012-11-19 22:24:22 +04:00
static const struct of_device_id sdhci_tegra_dt_match [ ] = {
2017-03-08 22:00:40 +03:00
{ . compatible = " nvidia,tegra186-sdhci " , . data = & soc_data_tegra186 } ,
2015-11-16 12:27:15 +03:00
{ . compatible = " nvidia,tegra210-sdhci " , . data = & soc_data_tegra210 } ,
2016-09-01 14:46:17 +03:00
{ . compatible = " nvidia,tegra124-sdhci " , . data = & soc_data_tegra124 } ,
2013-02-20 22:35:17 +04:00
{ . compatible = " nvidia,tegra114-sdhci " , . data = & soc_data_tegra114 } ,
2012-02-02 03:30:55 +04:00
{ . compatible = " nvidia,tegra30-sdhci " , . data = & soc_data_tegra30 } ,
{ . compatible = " nvidia,tegra20-sdhci " , . data = & soc_data_tegra20 } ,
2011-08-23 22:15:33 +04:00
{ }
} ;
2013-04-23 23:05:57 +04:00
MODULE_DEVICE_TABLE ( of , sdhci_tegra_dt_match ) ;
2011-08-23 22:15:33 +04:00
2012-11-19 22:23:06 +04:00
static int sdhci_tegra_probe ( struct platform_device * pdev )
2011-01-02 07:52:56 +03:00
{
2012-02-02 03:30:55 +04:00
const struct of_device_id * match ;
const struct sdhci_tegra_soc_data * soc_data ;
struct sdhci_host * host ;
2011-05-27 19:48:12 +04:00
struct sdhci_pltfm_host * pltfm_host ;
2012-02-02 03:30:55 +04:00
struct sdhci_tegra * tegra_host ;
2011-01-02 07:52:56 +03:00
struct clk * clk ;
int rc ;
2012-02-02 03:30:55 +04:00
match = of_match_device ( sdhci_tegra_dt_match , & pdev - > dev ) ;
2012-08-17 11:04:31 +04:00
if ( ! match )
return - EINVAL ;
soc_data = match - > data ;
2012-02-02 03:30:55 +04:00
2016-02-16 16:08:29 +03:00
host = sdhci_pltfm_init ( pdev , soc_data - > pdata , sizeof ( * tegra_host ) ) ;
2011-05-27 19:48:12 +04:00
if ( IS_ERR ( host ) )
return PTR_ERR ( host ) ;
pltfm_host = sdhci_priv ( host ) ;
2016-02-16 16:08:29 +03:00
tegra_host = sdhci_pltfm_priv ( pltfm_host ) ;
2015-12-22 21:41:00 +03:00
tegra_host - > ddr_signaling = false ;
2016-02-29 23:56:25 +03:00
tegra_host - > pad_calib_required = false ;
2018-08-30 18:06:12 +03:00
tegra_host - > pad_control_available = false ;
2012-02-02 03:30:55 +04:00
tegra_host - > soc_data = soc_data ;
2011-08-23 22:15:33 +04:00
2018-08-30 18:06:12 +03:00
if ( soc_data - > nvquirks & NVQUIRK_NEEDS_PAD_CONTROL ) {
rc = tegra_sdhci_init_pinctrl_info ( & pdev - > dev , tegra_host ) ;
if ( rc = = 0 )
host - > mmc_host_ops . start_signal_voltage_switch =
sdhci_tegra_start_signal_voltage_switch ;
}
2015-03-31 00:39:25 +03:00
rc = mmc_of_parse ( host - > mmc ) ;
2013-06-10 00:14:16 +04:00
if ( rc )
goto err_parse_dt ;
2013-02-16 02:07:19 +04:00
2015-12-22 21:41:03 +03:00
if ( tegra_host - > soc_data - > nvquirks & NVQUIRK_ENABLE_DDR50 )
2015-12-22 21:41:02 +03:00
host - > mmc - > caps | = MMC_CAP_1_8V_DDR ;
2018-08-30 18:06:17 +03:00
tegra_sdhci_parse_pad_autocal_dt ( host ) ;
2018-08-30 18:06:21 +03:00
tegra_sdhci_parse_default_tap_and_trim ( host ) ;
2015-03-31 00:39:25 +03:00
tegra_host - > power_gpio = devm_gpiod_get_optional ( & pdev - > dev , " power " ,
GPIOD_OUT_HIGH ) ;
if ( IS_ERR ( tegra_host - > power_gpio ) ) {
rc = PTR_ERR ( tegra_host - > power_gpio ) ;
goto err_power_req ;
2011-01-02 07:52:56 +03:00
}
2015-02-27 10:47:27 +03:00
clk = devm_clk_get ( mmc_dev ( host - > mmc ) , NULL ) ;
2011-01-02 07:52:56 +03:00
if ( IS_ERR ( clk ) ) {
dev_err ( mmc_dev ( host - > mmc ) , " clk err \n " ) ;
rc = PTR_ERR ( clk ) ;
2011-05-27 19:48:12 +04:00
goto err_clk_get ;
2011-01-02 07:52:56 +03:00
}
2012-06-05 08:29:37 +04:00
clk_prepare_enable ( clk ) ;
2011-01-02 07:52:56 +03:00
pltfm_host - > clk = clk ;
2017-07-19 18:25:45 +03:00
tegra_host - > rst = devm_reset_control_get_exclusive ( & pdev - > dev ,
" sdhci " ) ;
2017-03-08 22:00:39 +03:00
if ( IS_ERR ( tegra_host - > rst ) ) {
rc = PTR_ERR ( tegra_host - > rst ) ;
dev_err ( & pdev - > dev , " failed to get reset control: %d \n " , rc ) ;
goto err_rst_get ;
}
rc = reset_control_assert ( tegra_host - > rst ) ;
if ( rc )
goto err_rst_get ;
usleep_range ( 2000 , 4000 ) ;
rc = reset_control_deassert ( tegra_host - > rst ) ;
if ( rc )
goto err_rst_get ;
usleep_range ( 2000 , 4000 ) ;
2011-05-27 19:48:12 +04:00
rc = sdhci_add_host ( host ) ;
if ( rc )
goto err_add_host ;
2011-01-02 07:52:56 +03:00
return 0 ;
2011-05-27 19:48:12 +04:00
err_add_host :
2017-03-08 22:00:39 +03:00
reset_control_assert ( tegra_host - > rst ) ;
err_rst_get :
2012-06-05 08:29:37 +04:00
clk_disable_unprepare ( pltfm_host - > clk ) ;
2011-05-27 19:48:12 +04:00
err_clk_get :
err_power_req :
2013-06-10 00:14:16 +04:00
err_parse_dt :
2011-05-27 19:48:12 +04:00
sdhci_pltfm_free ( pdev ) ;
2011-01-02 07:52:56 +03:00
return rc ;
}
2017-03-08 22:00:39 +03:00
static int sdhci_tegra_remove ( struct platform_device * pdev )
{
struct sdhci_host * host = platform_get_drvdata ( pdev ) ;
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_tegra * tegra_host = sdhci_pltfm_priv ( pltfm_host ) ;
sdhci_remove_host ( host , 0 ) ;
reset_control_assert ( tegra_host - > rst ) ;
usleep_range ( 2000 , 4000 ) ;
clk_disable_unprepare ( pltfm_host - > clk ) ;
sdhci_pltfm_free ( pdev ) ;
return 0 ;
}
2011-05-27 19:48:12 +04:00
static struct platform_driver sdhci_tegra_driver = {
. driver = {
. name = " sdhci-tegra " ,
2011-08-23 22:15:33 +04:00
. of_match_table = sdhci_tegra_dt_match ,
2016-07-27 14:07:21 +03:00
. pm = & sdhci_pltfm_pmops ,
2011-05-27 19:48:12 +04:00
} ,
. probe = sdhci_tegra_probe ,
2017-03-08 22:00:39 +03:00
. remove = sdhci_tegra_remove ,
2011-01-02 07:52:56 +03:00
} ;
2011-11-26 08:55:43 +04:00
module_platform_driver ( sdhci_tegra_driver ) ;
2011-05-27 19:48:12 +04:00
MODULE_DESCRIPTION ( " SDHCI driver for Tegra " ) ;
2012-02-02 03:30:55 +04:00
MODULE_AUTHOR ( " Google, Inc. " ) ;
2011-05-27 19:48:12 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;