2018-12-10 21:35:07 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* sdhci_am654 . c - SDHCI driver for TI ' s AM654 SOCs
*
* Copyright ( C ) 2018 Texas Instruments Incorporated - http : //www.ti.com
*
*/
# include <linux/clk.h>
2019-06-04 09:09:12 +03:00
# include <linux/of.h>
2018-12-10 21:35:07 +03:00
# include <linux/module.h>
# include <linux/pm_runtime.h>
# include <linux/property.h>
# include <linux/regmap.h>
2020-06-19 15:57:57 +03:00
# include <linux/sys_soc.h>
2018-12-10 21:35:07 +03:00
2019-11-18 10:36:09 +03:00
# include "cqhci.h"
2018-12-10 21:35:07 +03:00
# include "sdhci-pltfm.h"
/* CTL_CFG Registers */
# define CTL_CFG_2 0x14
# define SLOTTYPE_MASK GENMASK(31, 30)
# define SLOTTYPE_EMBEDDED BIT(30)
/* PHY Registers */
# define PHY_CTRL1 0x100
# define PHY_CTRL2 0x104
# define PHY_CTRL3 0x108
# define PHY_CTRL4 0x10C
# define PHY_CTRL5 0x110
# define PHY_CTRL6 0x114
# define PHY_STAT1 0x130
# define PHY_STAT2 0x134
# define IOMUX_ENABLE_SHIFT 31
# define IOMUX_ENABLE_MASK BIT(IOMUX_ENABLE_SHIFT)
# define OTAPDLYENA_SHIFT 20
# define OTAPDLYENA_MASK BIT(OTAPDLYENA_SHIFT)
# define OTAPDLYSEL_SHIFT 12
# define OTAPDLYSEL_MASK GENMASK(15, 12)
# define STRBSEL_SHIFT 24
2019-06-04 09:09:12 +03:00
# define STRBSEL_4BIT_MASK GENMASK(27, 24)
# define STRBSEL_8BIT_MASK GENMASK(31, 24)
2018-12-10 21:35:07 +03:00
# define SEL50_SHIFT 8
# define SEL50_MASK BIT(SEL50_SHIFT)
# define SEL100_SHIFT 9
# define SEL100_MASK BIT(SEL100_SHIFT)
2019-06-04 09:09:12 +03:00
# define FREQSEL_SHIFT 8
# define FREQSEL_MASK GENMASK(10, 8)
2020-06-19 15:58:00 +03:00
# define CLKBUFSEL_SHIFT 0
# define CLKBUFSEL_MASK GENMASK(2, 0)
2018-12-10 21:35:07 +03:00
# define DLL_TRIM_ICP_SHIFT 4
# define DLL_TRIM_ICP_MASK GENMASK(7, 4)
# define DR_TY_SHIFT 20
# define DR_TY_MASK GENMASK(22, 20)
# define ENDLL_SHIFT 1
# define ENDLL_MASK BIT(ENDLL_SHIFT)
# define DLLRDY_SHIFT 0
# define DLLRDY_MASK BIT(DLLRDY_SHIFT)
# define PDB_SHIFT 0
# define PDB_MASK BIT(PDB_SHIFT)
# define CALDONE_SHIFT 1
# define CALDONE_MASK BIT(CALDONE_SHIFT)
# define RETRIM_SHIFT 17
# define RETRIM_MASK BIT(RETRIM_SHIFT)
2020-06-19 15:57:59 +03:00
# define SELDLYTXCLK_SHIFT 17
# define SELDLYTXCLK_MASK BIT(SELDLYTXCLK_SHIFT)
2018-12-10 21:35:07 +03:00
# define DRIVER_STRENGTH_50_OHM 0x0
# define DRIVER_STRENGTH_33_OHM 0x1
# define DRIVER_STRENGTH_66_OHM 0x2
# define DRIVER_STRENGTH_100_OHM 0x3
# define DRIVER_STRENGTH_40_OHM 0x4
# define CLOCK_TOO_SLOW_HZ 400000
2019-11-18 10:36:09 +03:00
/* Command Queue Host Controller Interface Base address */
# define SDHCI_AM654_CQE_BASE_ADDR 0x200
2018-12-10 21:35:07 +03:00
static struct regmap_config sdhci_am654_regmap_config = {
. reg_bits = 32 ,
. val_bits = 32 ,
. reg_stride = 4 ,
. fast_io = true ,
} ;
struct sdhci_am654_data {
struct regmap * base ;
2020-01-08 18:09:19 +03:00
bool legacy_otapdly ;
int otap_del_sel [ 11 ] ;
2020-06-19 15:58:00 +03:00
int clkbuf_sel ;
2018-12-10 21:35:07 +03:00
int trm_icp ;
int drv_strength ;
bool dll_on ;
2019-06-04 09:09:12 +03:00
int strb_sel ;
u32 flags ;
} ;
struct sdhci_am654_driver_data {
const struct sdhci_pltfm_data * pdata ;
u32 flags ;
# define IOMUX_PRESENT (1 << 0)
# define FREQSEL_2_BIT (1 << 1)
# define STRBSEL_4_BIT (1 << 2)
2019-06-04 09:09:13 +03:00
# define DLL_PRESENT (1 << 3)
2020-06-19 15:57:56 +03:00
# define DLL_CALIB (1 << 4)
2018-12-10 21:35:07 +03:00
} ;
2020-01-08 18:09:19 +03:00
struct timing_data {
const char * binding ;
u32 capability ;
} ;
static const struct timing_data td [ ] = {
[ MMC_TIMING_LEGACY ] = { " ti,otap-del-sel-legacy " , 0 } ,
[ MMC_TIMING_MMC_HS ] = { " ti,otap-del-sel-mmc-hs " , MMC_CAP_MMC_HIGHSPEED } ,
[ MMC_TIMING_SD_HS ] = { " ti,otap-del-sel-sd-hs " , MMC_CAP_SD_HIGHSPEED } ,
[ MMC_TIMING_UHS_SDR12 ] = { " ti,otap-del-sel-sdr12 " , MMC_CAP_UHS_SDR12 } ,
[ MMC_TIMING_UHS_SDR25 ] = { " ti,otap-del-sel-sdr25 " , MMC_CAP_UHS_SDR25 } ,
[ MMC_TIMING_UHS_SDR50 ] = { " ti,otap-del-sel-sdr50 " , MMC_CAP_UHS_SDR50 } ,
[ MMC_TIMING_UHS_SDR104 ] = { " ti,otap-del-sel-sdr104 " ,
MMC_CAP_UHS_SDR104 } ,
[ MMC_TIMING_UHS_DDR50 ] = { " ti,otap-del-sel-ddr50 " , MMC_CAP_UHS_DDR50 } ,
[ MMC_TIMING_MMC_DDR52 ] = { " ti,otap-del-sel-ddr52 " , MMC_CAP_DDR } ,
[ MMC_TIMING_MMC_HS200 ] = { " ti,otap-del-sel-hs200 " , MMC_CAP2_HS200 } ,
[ MMC_TIMING_MMC_HS400 ] = { " ti,otap-del-sel-hs400 " , MMC_CAP2_HS400 } ,
} ;
2020-01-08 18:09:20 +03:00
static void sdhci_am654_setup_dll ( struct sdhci_host * host , unsigned int clock )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_am654_data * sdhci_am654 = sdhci_pltfm_priv ( pltfm_host ) ;
int sel50 , sel100 , freqsel ;
u32 mask , val ;
int ret ;
if ( sdhci_am654 - > flags & FREQSEL_2_BIT ) {
switch ( clock ) {
case 200000000 :
sel50 = 0 ;
sel100 = 0 ;
break ;
case 100000000 :
sel50 = 0 ;
sel100 = 1 ;
break ;
default :
sel50 = 1 ;
sel100 = 0 ;
}
/* Configure PHY DLL frequency */
mask = SEL50_MASK | SEL100_MASK ;
val = ( sel50 < < SEL50_SHIFT ) | ( sel100 < < SEL100_SHIFT ) ;
regmap_update_bits ( sdhci_am654 - > base , PHY_CTRL5 , mask , val ) ;
} else {
switch ( clock ) {
case 200000000 :
freqsel = 0x0 ;
break ;
default :
freqsel = 0x4 ;
}
regmap_update_bits ( sdhci_am654 - > base , PHY_CTRL5 , FREQSEL_MASK ,
freqsel < < FREQSEL_SHIFT ) ;
}
/* Configure DLL TRIM */
mask = DLL_TRIM_ICP_MASK ;
val = sdhci_am654 - > trm_icp < < DLL_TRIM_ICP_SHIFT ;
/* Configure DLL driver strength */
mask | = DR_TY_MASK ;
val | = sdhci_am654 - > drv_strength < < DR_TY_SHIFT ;
regmap_update_bits ( sdhci_am654 - > base , PHY_CTRL1 , mask , val ) ;
/* Enable DLL */
regmap_update_bits ( sdhci_am654 - > base , PHY_CTRL1 , ENDLL_MASK ,
0x1 < < ENDLL_SHIFT ) ;
/*
* Poll for DLL ready . Use a one second timeout .
* Works in all experiments done so far
*/
ret = regmap_read_poll_timeout ( sdhci_am654 - > base , PHY_STAT1 , val ,
val & DLLRDY_MASK , 1000 , 1000000 ) ;
if ( ret ) {
dev_err ( mmc_dev ( host - > mmc ) , " DLL failed to relock \n " ) ;
return ;
}
sdhci_am654 - > dll_on = true ;
}
2018-12-10 21:35:07 +03:00
static void sdhci_am654_set_clock ( struct sdhci_host * host , unsigned int clock )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_am654_data * sdhci_am654 = sdhci_pltfm_priv ( pltfm_host ) ;
2020-01-08 18:09:19 +03:00
unsigned char timing = host - > mmc - > ios . timing ;
u32 otap_del_sel ;
u32 otap_del_ena ;
2018-12-10 21:35:07 +03:00
u32 mask , val ;
if ( sdhci_am654 - > dll_on ) {
2019-05-28 12:59:27 +03:00
regmap_update_bits ( sdhci_am654 - > base , PHY_CTRL1 , ENDLL_MASK , 0 ) ;
2018-12-10 21:35:07 +03:00
sdhci_am654 - > dll_on = false ;
}
sdhci_set_clock ( host , clock ) ;
2020-06-19 15:57:58 +03:00
/* Setup DLL Output TAP delay */
if ( sdhci_am654 - > legacy_otapdly )
otap_del_sel = sdhci_am654 - > otap_del_sel [ 0 ] ;
else
otap_del_sel = sdhci_am654 - > otap_del_sel [ timing ] ;
2020-01-08 18:09:19 +03:00
2020-06-19 15:57:58 +03:00
otap_del_ena = ( timing > MMC_TIMING_UHS_SDR25 ) ? 1 : 0 ;
2020-01-08 18:09:19 +03:00
2020-06-19 15:57:58 +03:00
mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK ;
val = ( otap_del_ena < < OTAPDLYENA_SHIFT ) |
( otap_del_sel < < OTAPDLYSEL_SHIFT ) ;
2020-01-08 18:09:19 +03:00
2020-06-19 15:57:58 +03:00
/* Write to STRBSEL for HS400 speed mode */
if ( timing = = MMC_TIMING_MMC_HS400 ) {
if ( sdhci_am654 - > flags & STRBSEL_4_BIT )
mask | = STRBSEL_4BIT_MASK ;
else
mask | = STRBSEL_8BIT_MASK ;
2019-06-04 09:09:12 +03:00
2020-06-19 15:57:58 +03:00
val | = sdhci_am654 - > strb_sel < < STRBSEL_SHIFT ;
}
2019-06-04 09:09:12 +03:00
2020-06-19 15:57:58 +03:00
regmap_update_bits ( sdhci_am654 - > base , PHY_CTRL4 , mask , val ) ;
2020-01-08 18:09:19 +03:00
2020-06-19 15:57:59 +03:00
if ( timing > MMC_TIMING_UHS_SDR25 & & clock > CLOCK_TOO_SLOW_HZ ) {
regmap_update_bits ( sdhci_am654 - > base , PHY_CTRL5 ,
SELDLYTXCLK_MASK , 0 ) ;
2020-06-19 15:57:58 +03:00
sdhci_am654_setup_dll ( host , clock ) ;
2020-06-19 15:57:59 +03:00
} else {
regmap_update_bits ( sdhci_am654 - > base , PHY_CTRL5 ,
SELDLYTXCLK_MASK , 1 < < SELDLYTXCLK_SHIFT ) ;
}
2020-06-19 15:58:00 +03:00
regmap_update_bits ( sdhci_am654 - > base , PHY_CTRL5 , CLKBUFSEL_MASK ,
sdhci_am654 - > clkbuf_sel ) ;
2018-12-10 21:35:07 +03:00
}
2019-06-28 07:07:51 +03:00
static void sdhci_j721e_4bit_set_clock ( struct sdhci_host * host ,
unsigned int clock )
2019-06-04 09:09:13 +03:00
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_am654_data * sdhci_am654 = sdhci_pltfm_priv ( pltfm_host ) ;
2020-01-08 18:09:19 +03:00
unsigned char timing = host - > mmc - > ios . timing ;
u32 otap_del_sel ;
u32 mask , val ;
/* Setup DLL Output TAP delay */
if ( sdhci_am654 - > legacy_otapdly )
otap_del_sel = sdhci_am654 - > otap_del_sel [ 0 ] ;
else
otap_del_sel = sdhci_am654 - > otap_del_sel [ timing ] ;
2019-06-04 09:09:13 +03:00
mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK ;
2020-01-08 18:09:19 +03:00
val = ( 0x1 < < OTAPDLYENA_SHIFT ) |
( otap_del_sel < < OTAPDLYSEL_SHIFT ) ;
2019-06-04 09:09:13 +03:00
regmap_update_bits ( sdhci_am654 - > base , PHY_CTRL4 , mask , val ) ;
2020-06-19 15:58:00 +03:00
regmap_update_bits ( sdhci_am654 - > base , PHY_CTRL5 , CLKBUFSEL_MASK ,
sdhci_am654 - > clkbuf_sel ) ;
2019-06-04 09:09:13 +03:00
sdhci_set_clock ( host , clock ) ;
}
2019-04-01 15:58:04 +03:00
static void sdhci_am654_write_b ( struct sdhci_host * host , u8 val , int reg )
{
unsigned char timing = host - > mmc - > ios . timing ;
if ( reg = = SDHCI_HOST_CONTROL ) {
switch ( timing ) {
/*
* According to the data manual , HISPD bit
* should not be set in these speed modes .
*/
case MMC_TIMING_SD_HS :
case MMC_TIMING_MMC_HS :
case MMC_TIMING_UHS_SDR12 :
case MMC_TIMING_UHS_SDR25 :
val & = ~ SDHCI_CTRL_HISPD ;
}
}
writeb ( val , host - > ioaddr + reg ) ;
}
2020-01-08 17:33:00 +03:00
static int sdhci_am654_execute_tuning ( struct mmc_host * mmc , u32 opcode )
{
struct sdhci_host * host = mmc_priv ( mmc ) ;
int err = sdhci_execute_tuning ( mmc , opcode ) ;
if ( err )
return err ;
/*
* Tuning data remains in the buffer after tuning .
* Do a command and data reset to get rid of it
*/
sdhci_reset ( host , SDHCI_RESET_CMD | SDHCI_RESET_DATA ) ;
return 0 ;
}
2020-01-08 17:33:01 +03:00
static u32 sdhci_am654_cqhci_irq ( struct sdhci_host * host , u32 intmask )
{
int cmd_error = 0 ;
int data_error = 0 ;
if ( ! sdhci_cqe_irq ( host , intmask , & cmd_error , & data_error ) )
return intmask ;
cqhci_irq ( host - > mmc , intmask , cmd_error , data_error ) ;
return 0 ;
}
2018-12-18 10:15:41 +03:00
static struct sdhci_ops sdhci_am654_ops = {
2018-12-10 21:35:07 +03:00
. get_max_clock = sdhci_pltfm_clk_get_max_clock ,
. get_timeout_clock = sdhci_pltfm_clk_get_max_clock ,
. set_uhs_signaling = sdhci_set_uhs_signaling ,
. set_bus_width = sdhci_set_bus_width ,
2020-03-06 20:44:09 +03:00
. set_power = sdhci_set_power_and_bus_voltage ,
2018-12-10 21:35:07 +03:00
. set_clock = sdhci_am654_set_clock ,
2019-04-01 15:58:04 +03:00
. write_b = sdhci_am654_write_b ,
2020-01-08 17:33:01 +03:00
. irq = sdhci_am654_cqhci_irq ,
2018-12-10 21:35:07 +03:00
. reset = sdhci_reset ,
} ;
static const struct sdhci_pltfm_data sdhci_am654_pdata = {
. ops = & sdhci_am654_ops ,
2020-01-08 17:32:59 +03:00
. quirks = SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 ,
2018-12-10 21:35:07 +03:00
. quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN ,
} ;
2020-06-19 15:57:57 +03:00
static const struct sdhci_am654_driver_data sdhci_am654_sr1_drvdata = {
2019-06-04 09:09:12 +03:00
. pdata = & sdhci_am654_pdata ,
2020-06-19 15:57:56 +03:00
. flags = IOMUX_PRESENT | FREQSEL_2_BIT | STRBSEL_4_BIT | DLL_PRESENT |
DLL_CALIB ,
2019-06-04 09:09:12 +03:00
} ;
2020-06-19 15:57:57 +03:00
static const struct sdhci_am654_driver_data sdhci_am654_drvdata = {
. pdata = & sdhci_am654_pdata ,
. flags = IOMUX_PRESENT | FREQSEL_2_BIT | STRBSEL_4_BIT | DLL_PRESENT ,
} ;
2019-06-28 07:07:51 +03:00
static struct sdhci_ops sdhci_j721e_8bit_ops = {
2019-06-04 09:09:12 +03:00
. get_max_clock = sdhci_pltfm_clk_get_max_clock ,
. get_timeout_clock = sdhci_pltfm_clk_get_max_clock ,
. set_uhs_signaling = sdhci_set_uhs_signaling ,
. set_bus_width = sdhci_set_bus_width ,
2020-03-06 20:44:09 +03:00
. set_power = sdhci_set_power_and_bus_voltage ,
2019-06-04 09:09:12 +03:00
. set_clock = sdhci_am654_set_clock ,
. write_b = sdhci_am654_write_b ,
2019-11-18 10:36:09 +03:00
. irq = sdhci_am654_cqhci_irq ,
2019-06-04 09:09:12 +03:00
. reset = sdhci_reset ,
} ;
static const struct sdhci_pltfm_data sdhci_j721e_8bit_pdata = {
. ops = & sdhci_j721e_8bit_ops ,
2020-01-08 17:32:59 +03:00
. quirks = SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 ,
2019-06-04 09:09:12 +03:00
. quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN ,
} ;
static const struct sdhci_am654_driver_data sdhci_j721e_8bit_drvdata = {
. pdata = & sdhci_j721e_8bit_pdata ,
2020-06-19 15:57:56 +03:00
. flags = DLL_PRESENT | DLL_CALIB ,
2019-06-04 09:09:13 +03:00
} ;
2019-06-28 07:07:51 +03:00
static struct sdhci_ops sdhci_j721e_4bit_ops = {
2019-06-04 09:09:13 +03:00
. get_max_clock = sdhci_pltfm_clk_get_max_clock ,
. get_timeout_clock = sdhci_pltfm_clk_get_max_clock ,
. set_uhs_signaling = sdhci_set_uhs_signaling ,
. set_bus_width = sdhci_set_bus_width ,
2020-03-06 20:44:09 +03:00
. set_power = sdhci_set_power_and_bus_voltage ,
2019-06-04 09:09:13 +03:00
. set_clock = sdhci_j721e_4bit_set_clock ,
. write_b = sdhci_am654_write_b ,
2019-11-18 10:36:09 +03:00
. irq = sdhci_am654_cqhci_irq ,
2019-06-04 09:09:13 +03:00
. reset = sdhci_reset ,
} ;
static const struct sdhci_pltfm_data sdhci_j721e_4bit_pdata = {
. ops = & sdhci_j721e_4bit_ops ,
2020-01-08 17:32:59 +03:00
. quirks = SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 ,
2019-06-04 09:09:13 +03:00
. quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN ,
2019-06-04 09:09:12 +03:00
} ;
2019-06-04 09:09:13 +03:00
static const struct sdhci_am654_driver_data sdhci_j721e_4bit_drvdata = {
. pdata = & sdhci_j721e_4bit_pdata ,
. flags = IOMUX_PRESENT ,
} ;
2019-11-18 10:36:09 +03:00
2020-06-19 15:57:57 +03:00
static const struct soc_device_attribute sdhci_am654_devices [ ] = {
{ . family = " AM65X " ,
. revision = " SR1.0 " ,
. data = & sdhci_am654_sr1_drvdata
} ,
{ /* sentinel */ }
} ;
2019-11-18 10:36:09 +03:00
static void sdhci_am654_dumpregs ( struct mmc_host * mmc )
{
sdhci_dumpregs ( mmc_priv ( mmc ) ) ;
}
static const struct cqhci_host_ops sdhci_am654_cqhci_ops = {
. enable = sdhci_cqe_enable ,
. disable = sdhci_cqe_disable ,
. dumpregs = sdhci_am654_dumpregs ,
} ;
static int sdhci_am654_cqe_add_host ( struct sdhci_host * host )
{
struct cqhci_host * cq_host ;
int ret ;
cq_host = devm_kzalloc ( host - > mmc - > parent , sizeof ( struct cqhci_host ) ,
GFP_KERNEL ) ;
if ( ! cq_host )
return - ENOMEM ;
cq_host - > mmio = host - > ioaddr + SDHCI_AM654_CQE_BASE_ADDR ;
cq_host - > quirks | = CQHCI_QUIRK_SHORT_TXFR_DESC_SZ ;
cq_host - > caps | = CQHCI_TASK_DESC_SZ_128 ;
cq_host - > ops = & sdhci_am654_cqhci_ops ;
host - > mmc - > caps2 | = MMC_CAP2_CQE ;
ret = cqhci_init ( cq_host , host - > mmc , 1 ) ;
return ret ;
}
2020-01-08 18:09:19 +03:00
static int sdhci_am654_get_otap_delay ( struct sdhci_host * host ,
struct sdhci_am654_data * sdhci_am654 )
{
struct device * dev = mmc_dev ( host - > mmc ) ;
int i ;
int ret ;
ret = device_property_read_u32 ( dev , td [ MMC_TIMING_LEGACY ] . binding ,
& sdhci_am654 - > otap_del_sel [ MMC_TIMING_LEGACY ] ) ;
if ( ret ) {
/*
* ti , otap - del - sel - legacy is mandatory , look for old binding
* if not found .
*/
ret = device_property_read_u32 ( dev , " ti,otap-del-sel " ,
& sdhci_am654 - > otap_del_sel [ 0 ] ) ;
if ( ret ) {
dev_err ( dev , " Couldn't find otap-del-sel \n " ) ;
return ret ;
}
dev_info ( dev , " Using legacy binding ti,otap-del-sel \n " ) ;
sdhci_am654 - > legacy_otapdly = true ;
return 0 ;
}
for ( i = MMC_TIMING_MMC_HS ; i < = MMC_TIMING_MMC_HS400 ; i + + ) {
ret = device_property_read_u32 ( dev , td [ i ] . binding ,
& sdhci_am654 - > otap_del_sel [ i ] ) ;
if ( ret ) {
dev_dbg ( dev , " Couldn't find %s \n " ,
td [ i ] . binding ) ;
/*
* Remove the corresponding capability
* if an otap - del - sel value is not found
*/
if ( i < = MMC_TIMING_MMC_DDR52 )
host - > mmc - > caps & = ~ td [ i ] . capability ;
else
host - > mmc - > caps2 & = ~ td [ i ] . capability ;
}
}
return 0 ;
}
2018-12-10 21:35:07 +03:00
static int sdhci_am654_init ( struct sdhci_host * host )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_am654_data * sdhci_am654 = sdhci_pltfm_priv ( pltfm_host ) ;
u32 ctl_cfg_2 = 0 ;
u32 mask ;
u32 val ;
int ret ;
/* Reset OTAP to default value */
mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK ;
2019-05-28 12:59:27 +03:00
regmap_update_bits ( sdhci_am654 - > base , PHY_CTRL4 , mask , 0x0 ) ;
2018-12-10 21:35:07 +03:00
2020-06-19 15:57:56 +03:00
if ( sdhci_am654 - > flags & DLL_CALIB ) {
2019-06-04 09:09:13 +03:00
regmap_read ( sdhci_am654 - > base , PHY_STAT1 , & val ) ;
if ( ~ val & CALDONE_MASK ) {
/* Calibrate IO lines */
regmap_update_bits ( sdhci_am654 - > base , PHY_CTRL1 ,
PDB_MASK , PDB_MASK ) ;
ret = regmap_read_poll_timeout ( sdhci_am654 - > base ,
PHY_STAT1 , val ,
val & CALDONE_MASK ,
1 , 20 ) ;
if ( ret )
return ret ;
}
2018-12-10 21:35:07 +03:00
}
/* Enable pins by setting IO mux to 0 */
2019-06-04 09:09:12 +03:00
if ( sdhci_am654 - > flags & IOMUX_PRESENT )
regmap_update_bits ( sdhci_am654 - > base , PHY_CTRL1 ,
IOMUX_ENABLE_MASK , 0 ) ;
2018-12-10 21:35:07 +03:00
/* Set slot type based on SD or eMMC */
if ( host - > mmc - > caps & MMC_CAP_NONREMOVABLE )
ctl_cfg_2 = SLOTTYPE_EMBEDDED ;
2019-05-28 12:59:27 +03:00
regmap_update_bits ( sdhci_am654 - > base , CTL_CFG_2 , SLOTTYPE_MASK ,
ctl_cfg_2 ) ;
2018-12-10 21:35:07 +03:00
2019-11-18 10:36:09 +03:00
ret = sdhci_setup_host ( host ) ;
if ( ret )
return ret ;
ret = sdhci_am654_cqe_add_host ( host ) ;
if ( ret )
goto err_cleanup_host ;
2020-01-08 18:09:19 +03:00
ret = sdhci_am654_get_otap_delay ( host , sdhci_am654 ) ;
if ( ret )
goto err_cleanup_host ;
2019-11-18 10:36:09 +03:00
ret = __sdhci_add_host ( host ) ;
if ( ret )
goto err_cleanup_host ;
return 0 ;
err_cleanup_host :
sdhci_cleanup_host ( host ) ;
return ret ;
2018-12-10 21:35:07 +03:00
}
static int sdhci_am654_get_of_property ( struct platform_device * pdev ,
struct sdhci_am654_data * sdhci_am654 )
{
struct device * dev = & pdev - > dev ;
int drv_strength ;
int ret ;
2019-06-04 09:09:13 +03:00
if ( sdhci_am654 - > flags & DLL_PRESENT ) {
ret = device_property_read_u32 ( dev , " ti,trm-icp " ,
& sdhci_am654 - > trm_icp ) ;
if ( ret )
return ret ;
ret = device_property_read_u32 ( dev , " ti,driver-strength-ohm " ,
& drv_strength ) ;
if ( ret )
return ret ;
2018-12-10 21:35:07 +03:00
2019-06-04 09:09:13 +03:00
switch ( drv_strength ) {
case 50 :
sdhci_am654 - > drv_strength = DRIVER_STRENGTH_50_OHM ;
break ;
case 33 :
sdhci_am654 - > drv_strength = DRIVER_STRENGTH_33_OHM ;
break ;
case 66 :
sdhci_am654 - > drv_strength = DRIVER_STRENGTH_66_OHM ;
break ;
case 100 :
sdhci_am654 - > drv_strength = DRIVER_STRENGTH_100_OHM ;
break ;
case 40 :
sdhci_am654 - > drv_strength = DRIVER_STRENGTH_40_OHM ;
break ;
default :
dev_err ( dev , " Invalid driver strength \n " ) ;
return - EINVAL ;
}
2018-12-10 21:35:07 +03:00
}
2019-06-04 09:09:12 +03:00
device_property_read_u32 ( dev , " ti,strobe-sel " , & sdhci_am654 - > strb_sel ) ;
2020-06-19 15:58:00 +03:00
device_property_read_u32 ( dev , " ti,clkbuf-sel " ,
& sdhci_am654 - > clkbuf_sel ) ;
2019-06-04 09:09:12 +03:00
2018-12-10 21:35:07 +03:00
sdhci_get_of_property ( pdev ) ;
return 0 ;
}
2019-06-04 09:09:12 +03:00
static const struct of_device_id sdhci_am654_of_match [ ] = {
{
. compatible = " ti,am654-sdhci-5.1 " ,
. data = & sdhci_am654_drvdata ,
} ,
{
. compatible = " ti,j721e-sdhci-8bit " ,
. data = & sdhci_j721e_8bit_drvdata ,
} ,
2019-06-04 09:09:13 +03:00
{
. compatible = " ti,j721e-sdhci-4bit " ,
. data = & sdhci_j721e_4bit_drvdata ,
} ,
2019-06-04 09:09:12 +03:00
{ /* sentinel */ }
} ;
2018-12-10 21:35:07 +03:00
static int sdhci_am654_probe ( struct platform_device * pdev )
{
2019-06-04 09:09:12 +03:00
const struct sdhci_am654_driver_data * drvdata ;
2020-06-19 15:57:57 +03:00
const struct soc_device_attribute * soc ;
2018-12-10 21:35:07 +03:00
struct sdhci_pltfm_host * pltfm_host ;
struct sdhci_am654_data * sdhci_am654 ;
2019-06-04 09:09:12 +03:00
const struct of_device_id * match ;
2018-12-10 21:35:07 +03:00
struct sdhci_host * host ;
struct clk * clk_xin ;
struct device * dev = & pdev - > dev ;
void __iomem * base ;
int ret ;
2019-06-04 09:09:12 +03:00
match = of_match_node ( sdhci_am654_of_match , pdev - > dev . of_node ) ;
drvdata = match - > data ;
2020-06-19 15:57:57 +03:00
/* Update drvdata based on SoC revision */
soc = soc_device_match ( sdhci_am654_devices ) ;
if ( soc & & soc - > data )
drvdata = soc - > data ;
2019-06-04 09:09:12 +03:00
host = sdhci_pltfm_init ( pdev , drvdata - > pdata , sizeof ( * sdhci_am654 ) ) ;
2018-12-10 21:35:07 +03:00
if ( IS_ERR ( host ) )
return PTR_ERR ( host ) ;
pltfm_host = sdhci_priv ( host ) ;
sdhci_am654 = sdhci_pltfm_priv ( pltfm_host ) ;
2019-06-04 09:09:12 +03:00
sdhci_am654 - > flags = drvdata - > flags ;
2018-12-10 21:35:07 +03:00
clk_xin = devm_clk_get ( dev , " clk_xin " ) ;
if ( IS_ERR ( clk_xin ) ) {
dev_err ( dev , " clk_xin clock not found. \n " ) ;
ret = PTR_ERR ( clk_xin ) ;
goto err_pltfm_free ;
}
pltfm_host - > clk = clk_xin ;
/* Clocks are enabled using pm_runtime */
pm_runtime_enable ( dev ) ;
ret = pm_runtime_get_sync ( dev ) ;
if ( ret < 0 ) {
pm_runtime_put_noidle ( dev ) ;
goto pm_runtime_disable ;
}
2019-12-15 20:51:15 +03:00
base = devm_platform_ioremap_resource ( pdev , 1 ) ;
2018-12-10 21:35:07 +03:00
if ( IS_ERR ( base ) ) {
ret = PTR_ERR ( base ) ;
goto pm_runtime_put ;
}
sdhci_am654 - > base = devm_regmap_init_mmio ( dev , base ,
& sdhci_am654_regmap_config ) ;
if ( IS_ERR ( sdhci_am654 - > base ) ) {
dev_err ( dev , " Failed to initialize regmap \n " ) ;
ret = PTR_ERR ( sdhci_am654 - > base ) ;
goto pm_runtime_put ;
}
ret = sdhci_am654_get_of_property ( pdev , sdhci_am654 ) ;
if ( ret )
goto pm_runtime_put ;
ret = mmc_of_parse ( host - > mmc ) ;
if ( ret ) {
dev_err ( dev , " parsing dt failed (%d) \n " , ret ) ;
goto pm_runtime_put ;
}
2020-01-08 17:33:00 +03:00
host - > mmc_host_ops . execute_tuning = sdhci_am654_execute_tuning ;
2018-12-10 21:35:07 +03:00
ret = sdhci_am654_init ( host ) ;
if ( ret )
goto pm_runtime_put ;
return 0 ;
pm_runtime_put :
pm_runtime_put_sync ( dev ) ;
pm_runtime_disable :
pm_runtime_disable ( dev ) ;
err_pltfm_free :
sdhci_pltfm_free ( pdev ) ;
return ret ;
}
static int sdhci_am654_remove ( struct platform_device * pdev )
{
struct sdhci_host * host = platform_get_drvdata ( pdev ) ;
int ret ;
sdhci_remove_host ( host , true ) ;
ret = pm_runtime_put_sync ( & pdev - > dev ) ;
if ( ret < 0 )
return ret ;
pm_runtime_disable ( & pdev - > dev ) ;
sdhci_pltfm_free ( pdev ) ;
return 0 ;
}
static struct platform_driver sdhci_am654_driver = {
. driver = {
. name = " sdhci-am654 " ,
. of_match_table = sdhci_am654_of_match ,
} ,
. probe = sdhci_am654_probe ,
. remove = sdhci_am654_remove ,
} ;
module_platform_driver ( sdhci_am654_driver ) ;
MODULE_DESCRIPTION ( " Driver for SDHCI Controller on TI's AM654 devices " ) ;
MODULE_AUTHOR ( " Faiz Abbas <faiz_abbas@ti.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;