2019-05-27 08:55:06 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2016-12-08 21:50:55 +09:00
/*
* Copyright ( C ) 2016 Socionext Inc .
* Author : Masahiro Yamada < yamada . masahiro @ socionext . com >
*/
2017-10-13 21:50:31 +09:00
# include <linux/bitfield.h>
2018-10-30 20:05:06 +09:00
# include <linux/bits.h>
2016-12-08 21:50:55 +09:00
# include <linux/iopoll.h>
# include <linux/module.h>
# include <linux/mmc/host.h>
2016-12-30 13:47:21 +01:00
# include <linux/mmc/mmc.h>
2017-03-21 14:33:11 +00:00
# include <linux/of.h>
2020-03-12 19:42:57 +09:00
# include <linux/of_device.h>
2023-04-10 11:45:25 -07:00
# include <linux/reset.h>
2016-12-08 21:50:55 +09:00
# include "sdhci-pltfm.h"
/* HRS - Host Register Set (specific to Cadence) */
# define SDHCI_CDNS_HRS04 0x10 /* PHY access port */
# define SDHCI_CDNS_HRS04_ACK BIT(26)
# define SDHCI_CDNS_HRS04_RD BIT(25)
# define SDHCI_CDNS_HRS04_WR BIT(24)
2017-10-13 21:50:31 +09:00
# define SDHCI_CDNS_HRS04_RDATA GENMASK(23, 16)
# define SDHCI_CDNS_HRS04_WDATA GENMASK(15, 8)
# define SDHCI_CDNS_HRS04_ADDR GENMASK(5, 0)
2016-12-08 21:50:55 +09:00
# define SDHCI_CDNS_HRS06 0x18 /* eMMC control */
# define SDHCI_CDNS_HRS06_TUNE_UP BIT(15)
2017-10-13 21:50:31 +09:00
# define SDHCI_CDNS_HRS06_TUNE GENMASK(13, 8)
# define SDHCI_CDNS_HRS06_MODE GENMASK(2, 0)
2016-12-08 21:50:55 +09:00
# define SDHCI_CDNS_HRS06_MODE_SD 0x0
# define SDHCI_CDNS_HRS06_MODE_MMC_SDR 0x2
# define SDHCI_CDNS_HRS06_MODE_MMC_DDR 0x3
# define SDHCI_CDNS_HRS06_MODE_MMC_HS200 0x4
# define SDHCI_CDNS_HRS06_MODE_MMC_HS400 0x5
2017-03-06 08:28:41 +00:00
# define SDHCI_CDNS_HRS06_MODE_MMC_HS400ES 0x6
2016-12-08 21:50:55 +09:00
/* SRS - Slot Register Set (SDHCI-compatible) */
# define SDHCI_CDNS_SRS_BASE 0x200
/* PHY */
# define SDHCI_CDNS_PHY_DLY_SD_HS 0x00
# define SDHCI_CDNS_PHY_DLY_SD_DEFAULT 0x01
# define SDHCI_CDNS_PHY_DLY_UHS_SDR12 0x02
# define SDHCI_CDNS_PHY_DLY_UHS_SDR25 0x03
# define SDHCI_CDNS_PHY_DLY_UHS_SDR50 0x04
# define SDHCI_CDNS_PHY_DLY_UHS_DDR50 0x05
# define SDHCI_CDNS_PHY_DLY_EMMC_LEGACY 0x06
# define SDHCI_CDNS_PHY_DLY_EMMC_SDR 0x07
# define SDHCI_CDNS_PHY_DLY_EMMC_DDR 0x08
2017-03-21 14:33:11 +00:00
# define SDHCI_CDNS_PHY_DLY_SDCLK 0x0b
# define SDHCI_CDNS_PHY_DLY_HSMMC 0x0c
# define SDHCI_CDNS_PHY_DLY_STROBE 0x0d
2016-12-08 21:50:55 +09:00
/*
* The tuned val register is 6 bit - wide , but not the whole of the range is
* available . The range 0 - 42 seems to be available ( then 43 wraps around to 0 )
* but I am not quite sure if it is official . Use only 0 to 39 for safety .
*/
# define SDHCI_CDNS_MAX_TUNING_LOOP 40
2017-08-23 13:15:00 +09:00
struct sdhci_cdns_phy_param {
u8 addr ;
u8 data ;
} ;
2016-12-08 21:50:55 +09:00
struct sdhci_cdns_priv {
void __iomem * hrs_addr ;
2023-04-10 11:45:24 -07:00
void __iomem * ctl_addr ; /* write control */
spinlock_t wrlock ; /* write lock */
2017-03-06 08:28:41 +00:00
bool enhanced_strobe ;
2023-04-10 11:45:22 -07:00
void ( * priv_writel ) ( struct sdhci_cdns_priv * priv , u32 val , void __iomem * reg ) ;
2023-04-10 11:45:25 -07:00
struct reset_control * rst_hw ;
2017-08-23 13:15:00 +09:00
unsigned int nr_phy_params ;
2020-02-26 16:31:25 -06:00
struct sdhci_cdns_phy_param phy_params [ ] ;
2016-12-08 21:50:55 +09:00
} ;
2017-03-21 14:33:11 +00:00
struct sdhci_cdns_phy_cfg {
const char * property ;
u8 addr ;
} ;
2023-04-10 11:45:23 -07:00
struct sdhci_cdns_drv_data {
int ( * init ) ( struct platform_device * pdev ) ;
const struct sdhci_pltfm_data pltfm_data ;
} ;
2017-03-21 14:33:11 +00:00
static const struct sdhci_cdns_phy_cfg sdhci_cdns_phy_cfgs [ ] = {
{ " cdns,phy-input-delay-sd-highspeed " , SDHCI_CDNS_PHY_DLY_SD_HS , } ,
{ " cdns,phy-input-delay-legacy " , SDHCI_CDNS_PHY_DLY_SD_DEFAULT , } ,
{ " cdns,phy-input-delay-sd-uhs-sdr12 " , SDHCI_CDNS_PHY_DLY_UHS_SDR12 , } ,
{ " cdns,phy-input-delay-sd-uhs-sdr25 " , SDHCI_CDNS_PHY_DLY_UHS_SDR25 , } ,
{ " cdns,phy-input-delay-sd-uhs-sdr50 " , SDHCI_CDNS_PHY_DLY_UHS_SDR50 , } ,
{ " cdns,phy-input-delay-sd-uhs-ddr50 " , SDHCI_CDNS_PHY_DLY_UHS_DDR50 , } ,
{ " cdns,phy-input-delay-mmc-highspeed " , SDHCI_CDNS_PHY_DLY_EMMC_SDR , } ,
{ " cdns,phy-input-delay-mmc-ddr " , SDHCI_CDNS_PHY_DLY_EMMC_DDR , } ,
{ " cdns,phy-dll-delay-sdclk " , SDHCI_CDNS_PHY_DLY_SDCLK , } ,
{ " cdns,phy-dll-delay-sdclk-hsmmc " , SDHCI_CDNS_PHY_DLY_HSMMC , } ,
{ " cdns,phy-dll-delay-strobe " , SDHCI_CDNS_PHY_DLY_STROBE , } ,
} ;
2023-04-10 11:45:22 -07:00
static inline void cdns_writel ( struct sdhci_cdns_priv * priv , u32 val ,
void __iomem * reg )
{
writel ( val , reg ) ;
}
2017-03-21 14:32:16 +00:00
static int sdhci_cdns_write_phy_reg ( struct sdhci_cdns_priv * priv ,
u8 addr , u8 data )
2016-12-08 21:50:55 +09:00
{
void __iomem * reg = priv - > hrs_addr + SDHCI_CDNS_HRS04 ;
u32 tmp ;
2017-03-21 14:32:16 +00:00
int ret ;
2016-12-08 21:50:55 +09:00
2020-05-25 10:40:53 +03:00
ret = readl_poll_timeout ( reg , tmp , ! ( tmp & SDHCI_CDNS_HRS04_ACK ) ,
0 , 10 ) ;
if ( ret )
return ret ;
2017-10-13 21:50:31 +09:00
tmp = FIELD_PREP ( SDHCI_CDNS_HRS04_WDATA , data ) |
FIELD_PREP ( SDHCI_CDNS_HRS04_ADDR , addr ) ;
2023-04-10 11:45:22 -07:00
priv - > priv_writel ( priv , tmp , reg ) ;
2016-12-08 21:50:55 +09:00
tmp | = SDHCI_CDNS_HRS04_WR ;
2023-04-10 11:45:22 -07:00
priv - > priv_writel ( priv , tmp , reg ) ;
2016-12-08 21:50:55 +09:00
2017-03-21 14:32:16 +00:00
ret = readl_poll_timeout ( reg , tmp , tmp & SDHCI_CDNS_HRS04_ACK , 0 , 10 ) ;
if ( ret )
return ret ;
2016-12-08 21:50:55 +09:00
tmp & = ~ SDHCI_CDNS_HRS04_WR ;
2023-04-10 11:45:22 -07:00
priv - > priv_writel ( priv , tmp , reg ) ;
2017-03-21 14:32:16 +00:00
2020-05-25 10:40:53 +03:00
ret = readl_poll_timeout ( reg , tmp , ! ( tmp & SDHCI_CDNS_HRS04_ACK ) ,
0 , 10 ) ;
return ret ;
2016-12-08 21:50:55 +09:00
}
2017-08-23 13:15:00 +09:00
static unsigned int sdhci_cdns_phy_param_count ( struct device_node * np )
2016-12-08 21:50:55 +09:00
{
2017-08-23 13:15:00 +09:00
unsigned int count = 0 ;
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( sdhci_cdns_phy_cfgs ) ; i + + )
if ( of_property_read_bool ( np , sdhci_cdns_phy_cfgs [ i ] . property ) )
count + + ;
return count ;
}
static void sdhci_cdns_phy_param_parse ( struct device_node * np ,
struct sdhci_cdns_priv * priv )
{
struct sdhci_cdns_phy_param * p = priv - > phy_params ;
2017-03-21 14:33:11 +00:00
u32 val ;
int ret , i ;
for ( i = 0 ; i < ARRAY_SIZE ( sdhci_cdns_phy_cfgs ) ; i + + ) {
ret = of_property_read_u32 ( np , sdhci_cdns_phy_cfgs [ i ] . property ,
& val ) ;
if ( ret )
continue ;
2017-08-23 13:15:00 +09:00
p - > addr = sdhci_cdns_phy_cfgs [ i ] . addr ;
p - > data = val ;
p + + ;
}
}
static int sdhci_cdns_phy_init ( struct sdhci_cdns_priv * priv )
{
int ret , i ;
for ( i = 0 ; i < priv - > nr_phy_params ; i + + ) {
ret = sdhci_cdns_write_phy_reg ( priv , priv - > phy_params [ i ] . addr ,
priv - > phy_params [ i ] . data ) ;
2017-03-21 14:33:11 +00:00
if ( ret )
return ret ;
}
return 0 ;
2016-12-08 21:50:55 +09:00
}
2020-01-21 19:58:58 +09:00
static void * sdhci_cdns_priv ( struct sdhci_host * host )
2016-12-08 21:50:55 +09:00
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
return sdhci_pltfm_priv ( pltfm_host ) ;
}
static unsigned int sdhci_cdns_get_timeout_clock ( struct sdhci_host * host )
{
/*
* Cadence ' s spec says the Timeout Clock Frequency is the same as the
2017-03-24 15:50:12 +08:00
* Base Clock Frequency .
2016-12-08 21:50:55 +09:00
*/
2017-03-24 15:50:12 +08:00
return host - > max_clk ;
2016-12-08 21:50:55 +09:00
}
2017-03-06 08:28:41 +00:00
static void sdhci_cdns_set_emmc_mode ( struct sdhci_cdns_priv * priv , u32 mode )
{
u32 tmp ;
/* The speed mode for eMMC is selected by HRS06 register */
tmp = readl ( priv - > hrs_addr + SDHCI_CDNS_HRS06 ) ;
2017-10-13 21:50:31 +09:00
tmp & = ~ SDHCI_CDNS_HRS06_MODE ;
tmp | = FIELD_PREP ( SDHCI_CDNS_HRS06_MODE , mode ) ;
2023-04-10 11:45:22 -07:00
priv - > priv_writel ( priv , tmp , priv - > hrs_addr + SDHCI_CDNS_HRS06 ) ;
2017-03-06 08:28:41 +00:00
}
static u32 sdhci_cdns_get_emmc_mode ( struct sdhci_cdns_priv * priv )
{
u32 tmp ;
tmp = readl ( priv - > hrs_addr + SDHCI_CDNS_HRS06 ) ;
2017-10-13 21:50:31 +09:00
return FIELD_GET ( SDHCI_CDNS_HRS06_MODE , tmp ) ;
2017-03-06 08:28:41 +00:00
}
2016-12-08 21:50:55 +09:00
static int sdhci_cdns_set_tune_val ( struct sdhci_host * host , unsigned int val )
{
struct sdhci_cdns_priv * priv = sdhci_cdns_priv ( host ) ;
void __iomem * reg = priv - > hrs_addr + SDHCI_CDNS_HRS06 ;
u32 tmp ;
2018-03-27 18:29:53 +09:00
int i , ret ;
2016-12-08 21:50:55 +09:00
2017-10-13 21:50:31 +09:00
if ( WARN_ON ( ! FIELD_FIT ( SDHCI_CDNS_HRS06_TUNE , val ) ) )
2016-12-08 21:50:55 +09:00
return - EINVAL ;
tmp = readl ( reg ) ;
2017-10-13 21:50:31 +09:00
tmp & = ~ SDHCI_CDNS_HRS06_TUNE ;
tmp | = FIELD_PREP ( SDHCI_CDNS_HRS06_TUNE , val ) ;
2016-12-08 21:50:55 +09:00
2018-03-27 18:29:53 +09:00
/*
* Workaround for IP errata :
* The IP6116 SD / eMMC PHY design has a timing issue on receive data
* path . Send tune request twice .
*/
for ( i = 0 ; i < 2 ; i + + ) {
tmp | = SDHCI_CDNS_HRS06_TUNE_UP ;
2023-04-10 11:45:22 -07:00
priv - > priv_writel ( priv , tmp , reg ) ;
2018-03-27 18:29:53 +09:00
ret = readl_poll_timeout ( reg , tmp ,
! ( tmp & SDHCI_CDNS_HRS06_TUNE_UP ) ,
0 , 1 ) ;
2018-04-19 10:59:58 -05:00
if ( ret )
return ret ;
2018-03-27 18:29:53 +09:00
}
return 0 ;
2016-12-08 21:50:55 +09:00
}
2020-07-20 15:11:41 +09:00
/*
* In SD mode , software must not use the hardware tuning and instead perform
* an almost identical procedure to eMMC .
*/
static int sdhci_cdns_execute_tuning ( struct sdhci_host * host , u32 opcode )
2016-12-08 21:50:55 +09:00
{
int cur_streak = 0 ;
int max_streak = 0 ;
int end_of_streak = 0 ;
int i ;
/*
2020-07-20 15:11:41 +09:00
* Do not execute tuning for UHS_SDR50 or UHS_DDR50 .
* The delay is set by probe , based on the DT properties .
2016-12-08 21:50:55 +09:00
*/
2020-07-20 15:11:41 +09:00
if ( host - > timing ! = MMC_TIMING_MMC_HS200 & &
host - > timing ! = MMC_TIMING_UHS_SDR104 )
return 0 ;
2016-12-08 21:50:55 +09:00
for ( i = 0 ; i < SDHCI_CDNS_MAX_TUNING_LOOP ; i + + ) {
if ( sdhci_cdns_set_tune_val ( host , i ) | |
mmc_send_tuning ( host - > mmc , opcode , NULL ) ) { /* bad */
cur_streak = 0 ;
} else { /* good */
cur_streak + + ;
if ( cur_streak > max_streak ) {
max_streak = cur_streak ;
end_of_streak = i ;
}
}
}
if ( ! max_streak ) {
dev_err ( mmc_dev ( host - > mmc ) , " no tuning point found \n " ) ;
return - EIO ;
}
return sdhci_cdns_set_tune_val ( host , end_of_streak - max_streak / 2 ) ;
}
2020-07-20 15:11:41 +09:00
static void sdhci_cdns_set_uhs_signaling ( struct sdhci_host * host ,
unsigned int timing )
{
struct sdhci_cdns_priv * priv = sdhci_cdns_priv ( host ) ;
u32 mode ;
switch ( timing ) {
case MMC_TIMING_MMC_HS :
mode = SDHCI_CDNS_HRS06_MODE_MMC_SDR ;
break ;
case MMC_TIMING_MMC_DDR52 :
mode = SDHCI_CDNS_HRS06_MODE_MMC_DDR ;
break ;
case MMC_TIMING_MMC_HS200 :
mode = SDHCI_CDNS_HRS06_MODE_MMC_HS200 ;
break ;
case MMC_TIMING_MMC_HS400 :
if ( priv - > enhanced_strobe )
mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400ES ;
else
mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400 ;
break ;
default :
mode = SDHCI_CDNS_HRS06_MODE_SD ;
break ;
}
sdhci_cdns_set_emmc_mode ( priv , mode ) ;
/* For SD, fall back to the default handler */
if ( mode = = SDHCI_CDNS_HRS06_MODE_SD )
sdhci_set_uhs_signaling ( host , timing ) ;
}
2023-04-10 11:45:24 -07:00
/* Elba control register bits [6:3] are byte-lane enables */
# define ELBA_BYTE_ENABLE_MASK(x) ((x) << 3)
/*
* The Pensando Elba SoC explicitly controls byte - lane enabling on writes
* which includes writes to the HRS registers . The write lock ( wrlock )
* is used to ensure byte - lane enable , using write control ( ctl_addr ) ,
* occurs before the data write .
*/
static void elba_priv_writel ( struct sdhci_cdns_priv * priv , u32 val ,
void __iomem * reg )
{
unsigned long flags ;
spin_lock_irqsave ( & priv - > wrlock , flags ) ;
writel ( GENMASK ( 7 , 3 ) , priv - > ctl_addr ) ;
writel ( val , reg ) ;
spin_unlock_irqrestore ( & priv - > wrlock , flags ) ;
}
static void elba_write_l ( struct sdhci_host * host , u32 val , int reg )
{
elba_priv_writel ( sdhci_cdns_priv ( host ) , val , host - > ioaddr + reg ) ;
}
static void elba_write_w ( struct sdhci_host * host , u16 val , int reg )
{
struct sdhci_cdns_priv * priv = sdhci_cdns_priv ( host ) ;
u32 shift = reg & GENMASK ( 1 , 0 ) ;
unsigned long flags ;
u32 byte_enables ;
byte_enables = GENMASK ( 1 , 0 ) < < shift ;
spin_lock_irqsave ( & priv - > wrlock , flags ) ;
writel ( ELBA_BYTE_ENABLE_MASK ( byte_enables ) , priv - > ctl_addr ) ;
writew ( val , host - > ioaddr + reg ) ;
spin_unlock_irqrestore ( & priv - > wrlock , flags ) ;
}
static void elba_write_b ( struct sdhci_host * host , u8 val , int reg )
{
struct sdhci_cdns_priv * priv = sdhci_cdns_priv ( host ) ;
u32 shift = reg & GENMASK ( 1 , 0 ) ;
unsigned long flags ;
u32 byte_enables ;
byte_enables = BIT ( 0 ) < < shift ;
spin_lock_irqsave ( & priv - > wrlock , flags ) ;
writel ( ELBA_BYTE_ENABLE_MASK ( byte_enables ) , priv - > ctl_addr ) ;
writeb ( val , host - > ioaddr + reg ) ;
spin_unlock_irqrestore ( & priv - > wrlock , flags ) ;
}
static const struct sdhci_ops sdhci_elba_ops = {
. write_l = elba_write_l ,
. write_w = elba_write_w ,
. write_b = elba_write_b ,
. set_clock = sdhci_set_clock ,
. get_timeout_clock = sdhci_cdns_get_timeout_clock ,
. set_bus_width = sdhci_set_bus_width ,
. reset = sdhci_reset ,
. set_uhs_signaling = sdhci_cdns_set_uhs_signaling ,
} ;
static int elba_drv_init ( struct platform_device * pdev )
{
struct sdhci_host * host = platform_get_drvdata ( pdev ) ;
struct sdhci_cdns_priv * priv = sdhci_cdns_priv ( host ) ;
void __iomem * ioaddr ;
host - > mmc - > caps | = MMC_CAP_1_8V_DDR | MMC_CAP_8_BIT_DATA ;
spin_lock_init ( & priv - > wrlock ) ;
/* Byte-lane control register */
ioaddr = devm_platform_ioremap_resource ( pdev , 1 ) ;
if ( IS_ERR ( ioaddr ) )
return PTR_ERR ( ioaddr ) ;
priv - > ctl_addr = ioaddr ;
priv - > priv_writel = elba_priv_writel ;
writel ( ELBA_BYTE_ENABLE_MASK ( 0xf ) , priv - > ctl_addr ) ;
return 0 ;
}
2020-07-20 15:11:41 +09:00
static const struct sdhci_ops sdhci_cdns_ops = {
. set_clock = sdhci_set_clock ,
. get_timeout_clock = sdhci_cdns_get_timeout_clock ,
. set_bus_width = sdhci_set_bus_width ,
. reset = sdhci_reset ,
. platform_execute_tuning = sdhci_cdns_execute_tuning ,
. set_uhs_signaling = sdhci_cdns_set_uhs_signaling ,
} ;
2023-04-10 11:45:23 -07:00
static const struct sdhci_cdns_drv_data sdhci_cdns_uniphier_drv_data = {
. pltfm_data = {
. ops = & sdhci_cdns_ops ,
. quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN ,
} ,
2020-07-20 15:11:41 +09:00
} ;
2023-04-10 11:45:24 -07:00
static const struct sdhci_cdns_drv_data sdhci_elba_drv_data = {
. init = elba_drv_init ,
. pltfm_data = {
. ops = & sdhci_elba_ops ,
} ,
} ;
2023-04-10 11:45:23 -07:00
static const struct sdhci_cdns_drv_data sdhci_cdns_drv_data = {
. pltfm_data = {
. ops = & sdhci_cdns_ops ,
} ,
2020-07-20 15:11:41 +09:00
} ;
2017-03-06 08:28:41 +00:00
static void sdhci_cdns_hs400_enhanced_strobe ( struct mmc_host * mmc ,
struct mmc_ios * ios )
{
struct sdhci_host * host = mmc_priv ( mmc ) ;
struct sdhci_cdns_priv * priv = sdhci_cdns_priv ( host ) ;
u32 mode ;
priv - > enhanced_strobe = ios - > enhanced_strobe ;
mode = sdhci_cdns_get_emmc_mode ( priv ) ;
if ( mode = = SDHCI_CDNS_HRS06_MODE_MMC_HS400 & & ios - > enhanced_strobe )
sdhci_cdns_set_emmc_mode ( priv ,
SDHCI_CDNS_HRS06_MODE_MMC_HS400ES ) ;
if ( mode = = SDHCI_CDNS_HRS06_MODE_MMC_HS400ES & & ! ios - > enhanced_strobe )
sdhci_cdns_set_emmc_mode ( priv ,
SDHCI_CDNS_HRS06_MODE_MMC_HS400 ) ;
}
2023-04-10 11:45:25 -07:00
static void sdhci_cdns_mmc_hw_reset ( struct mmc_host * mmc )
{
struct sdhci_host * host = mmc_priv ( mmc ) ;
struct sdhci_cdns_priv * priv = sdhci_cdns_priv ( host ) ;
dev_dbg ( mmc_dev ( host - > mmc ) , " emmc hardware reset \n " ) ;
reset_control_assert ( priv - > rst_hw ) ;
/* For eMMC, minimum is 1us but give it 3us for good measure */
udelay ( 3 ) ;
reset_control_deassert ( priv - > rst_hw ) ;
/* For eMMC, minimum is 200us but give it 300us for good measure */
usleep_range ( 300 , 1000 ) ;
}
2016-12-08 21:50:55 +09:00
static int sdhci_cdns_probe ( struct platform_device * pdev )
{
struct sdhci_host * host ;
2023-04-10 11:45:23 -07:00
const struct sdhci_cdns_drv_data * data ;
2016-12-08 21:50:55 +09:00
struct sdhci_pltfm_host * pltfm_host ;
struct sdhci_cdns_priv * priv ;
struct clk * clk ;
2017-08-23 13:15:00 +09:00
unsigned int nr_phy_params ;
2016-12-08 21:50:55 +09:00
int ret ;
2017-03-21 14:33:11 +00:00
struct device * dev = & pdev - > dev ;
2019-08-29 19:49:28 +09:00
static const u16 version = SDHCI_SPEC_400 < < SDHCI_SPEC_VER_SHIFT ;
2016-12-08 21:50:55 +09:00
2017-03-21 14:33:26 +00:00
clk = devm_clk_get ( dev , NULL ) ;
2016-12-08 21:50:55 +09:00
if ( IS_ERR ( clk ) )
return PTR_ERR ( clk ) ;
ret = clk_prepare_enable ( clk ) ;
if ( ret )
return ret ;
2020-03-12 19:42:57 +09:00
data = of_device_get_match_data ( dev ) ;
if ( ! data )
2023-04-10 11:45:23 -07:00
data = & sdhci_cdns_drv_data ;
2020-03-12 19:42:57 +09:00
2017-08-23 13:15:00 +09:00
nr_phy_params = sdhci_cdns_phy_param_count ( dev - > of_node ) ;
2023-04-10 11:45:23 -07:00
host = sdhci_pltfm_init ( pdev , & data - > pltfm_data ,
2019-08-08 11:53:01 -05:00
struct_size ( priv , phy_params , nr_phy_params ) ) ;
2016-12-08 21:50:55 +09:00
if ( IS_ERR ( host ) ) {
ret = PTR_ERR ( host ) ;
goto disable_clk ;
}
pltfm_host = sdhci_priv ( host ) ;
pltfm_host - > clk = clk ;
2017-08-23 13:15:00 +09:00
priv = sdhci_pltfm_priv ( pltfm_host ) ;
priv - > nr_phy_params = nr_phy_params ;
2016-12-08 21:50:55 +09:00
priv - > hrs_addr = host - > ioaddr ;
2017-03-06 08:28:41 +00:00
priv - > enhanced_strobe = false ;
2023-04-10 11:45:22 -07:00
priv - > priv_writel = cdns_writel ;
2016-12-08 21:50:55 +09:00
host - > ioaddr + = SDHCI_CDNS_SRS_BASE ;
2017-03-06 08:28:41 +00:00
host - > mmc_host_ops . hs400_enhanced_strobe =
sdhci_cdns_hs400_enhanced_strobe ;
2023-04-10 11:45:23 -07:00
if ( data - > init ) {
ret = data - > init ( pdev ) ;
if ( ret )
goto free ;
}
2019-08-29 19:49:26 +09:00
sdhci_enable_v4_mode ( host ) ;
2019-08-29 19:49:28 +09:00
__sdhci_read_caps ( host , & version , NULL , NULL ) ;
2016-12-08 21:50:55 +09:00
2017-04-11 12:13:38 +01:00
sdhci_get_of_property ( pdev ) ;
2016-12-08 21:50:55 +09:00
ret = mmc_of_parse ( host - > mmc ) ;
if ( ret )
goto free ;
2017-08-23 13:15:00 +09:00
sdhci_cdns_phy_param_parse ( dev - > of_node , priv ) ;
ret = sdhci_cdns_phy_init ( priv ) ;
2017-03-21 14:33:11 +00:00
if ( ret )
goto free ;
2016-12-08 21:50:55 +09:00
2023-04-10 11:45:25 -07:00
if ( host - > mmc - > caps & MMC_CAP_HW_RESET ) {
priv - > rst_hw = devm_reset_control_get_optional_exclusive ( dev , NULL ) ;
2023-04-25 15:41:26 +02:00
if ( IS_ERR ( priv - > rst_hw ) ) {
ret = dev_err_probe ( mmc_dev ( host - > mmc ) , PTR_ERR ( priv - > rst_hw ) ,
" reset controller error \n " ) ;
goto free ;
}
2023-04-10 11:45:25 -07:00
if ( priv - > rst_hw )
host - > mmc_host_ops . card_hw_reset = sdhci_cdns_mmc_hw_reset ;
}
2016-12-08 21:50:55 +09:00
ret = sdhci_add_host ( host ) ;
if ( ret )
goto free ;
return 0 ;
free :
sdhci_pltfm_free ( pdev ) ;
disable_clk :
clk_disable_unprepare ( clk ) ;
return ret ;
}
2017-08-23 13:15:00 +09:00
# ifdef CONFIG_PM_SLEEP
static int sdhci_cdns_resume ( struct device * dev )
{
struct sdhci_host * host = dev_get_drvdata ( dev ) ;
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_cdns_priv * priv = sdhci_pltfm_priv ( pltfm_host ) ;
int ret ;
ret = clk_prepare_enable ( pltfm_host - > clk ) ;
if ( ret )
return ret ;
ret = sdhci_cdns_phy_init ( priv ) ;
if ( ret )
goto disable_clk ;
ret = sdhci_resume_host ( host ) ;
if ( ret )
goto disable_clk ;
return 0 ;
disable_clk :
clk_disable_unprepare ( pltfm_host - > clk ) ;
return ret ;
}
# endif
static const struct dev_pm_ops sdhci_cdns_pm_ops = {
2017-08-23 13:15:03 +09:00
SET_SYSTEM_SLEEP_PM_OPS ( sdhci_pltfm_suspend , sdhci_cdns_resume )
2017-08-23 13:15:00 +09:00
} ;
2016-12-08 21:50:55 +09:00
static const struct of_device_id sdhci_cdns_match [ ] = {
2020-03-12 19:42:57 +09:00
{
. compatible = " socionext,uniphier-sd4hc " ,
2023-04-10 11:45:23 -07:00
. data = & sdhci_cdns_uniphier_drv_data ,
2020-03-12 19:42:57 +09:00
} ,
2023-04-10 11:45:24 -07:00
{
. compatible = " amd,pensando-elba-sd4hc " ,
. data = & sdhci_elba_drv_data ,
} ,
2016-12-08 21:50:55 +09:00
{ . compatible = " cdns,sd4hc " } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , sdhci_cdns_match ) ;
static struct platform_driver sdhci_cdns_driver = {
. driver = {
. name = " sdhci-cdns " ,
2020-09-03 16:24:38 -07:00
. probe_type = PROBE_PREFER_ASYNCHRONOUS ,
2017-08-23 13:15:00 +09:00
. pm = & sdhci_cdns_pm_ops ,
2016-12-08 21:50:55 +09:00
. of_match_table = sdhci_cdns_match ,
} ,
. probe = sdhci_cdns_probe ,
. remove = sdhci_pltfm_unregister ,
} ;
module_platform_driver ( sdhci_cdns_driver ) ;
MODULE_AUTHOR ( " Masahiro Yamada <yamada.masahiro@socionext.com> " ) ;
MODULE_DESCRIPTION ( " Cadence SD/SDIO/eMMC Host Controller Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;