Hi, On 6/5/23 22:58, Piyush Malgujar wrote: > From: Dhananjay Kangude <dkang...@cadence.com> > > Add support for SD6 controller along with: > - HS200, HS400 and HS400ES support > - Host side Slew and drive configuration > > These changes to support SD6 cadence IP are isolated from the > existing support of SD4 controller. > > Signed-off-by: Dhananjay Kangude <dkang...@cadence.com> > Co-developed-by: Jayanthi Annadurai <jannadu...@marvell.com> > Signed-off-by: Jayanthi Annadurai <jannadu...@marvell.com> > Signed-off-by: Piyush Malgujar <pmalgu...@marvell.com> > --- > drivers/mmc/Kconfig | 1 + > drivers/mmc/sdhci-cadence.c | 1317 ++++++++++++++++++++++++++++++++++- > 2 files changed, 1301 insertions(+), 17 deletions(-) > > diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig > index > de01b9687bad28f3b493208c145f5c5dd2f59f78..6b724f1d1ed050b49e31a6cbe5f898b4b636c330 > 100644 > --- a/drivers/mmc/Kconfig > +++ b/drivers/mmc/Kconfig > @@ -566,6 +566,7 @@ config MMC_SDHCI_CADENCE > depends on BLK && DM_MMC > depends on MMC_SDHCI > depends on OF_CONTROL > + select MMC_SDHCI_IO_ACCESSORS > help > This selects the Cadence SD/SDIO/eMMC driver. > > diff --git a/drivers/mmc/sdhci-cadence.c b/drivers/mmc/sdhci-cadence.c > index > 0bb258da63e442232310d9433b7b6882992bd45d..1d8e8e177eeeb6464ba9ade5fd63c1cca4554bb6 > 100644 > --- a/drivers/mmc/sdhci-cadence.c > +++ b/drivers/mmc/sdhci-cadence.c > @@ -6,6 +6,7 @@ > > #include <common.h> > #include <dm.h> > +#include <div64.h> > #include <asm/global_data.h> > #include <dm/device_compat.h> > #include <linux/bitfield.h> > @@ -18,8 +19,19 @@ > #include <mmc.h> > #include <sdhci.h> > > +#define SDHCI_CDNS_SD6_MAXCLK 200000000 > + > +#define DEFAULT_CMD_DELAY 16 > +#define SDHCI_CDNS_TUNE_START 16 > +#define SDHCI_CDNS_TUNE_STEP 6 > +#define SDHCI_CDNS_TUNE_ITERATIONS 40 > + > +#define SDHCI_CDNS_HRS00 0x00 > +#define SDHCI_CDNS_HRS00_SWR BIT(0) > + > +#define SDHCI_CDNS_HRS02 0x08 /* PHY access > port */ > +#define SDHCI_CDNS_HRS04 0x10 /* PHY access > port */ > /* SD 4.0 Controller HRS - Host Register Set (specific to Cadence) */ > -#define SDHCI_CDNS_HRS04 0x10 /* PHY access port */ > #define SDHCI_CDNS_SD4_HRS04_ACK BIT(26) > #define SDHCI_CDNS_SD4_HRS04_RD BIT(25) > #define SDHCI_CDNS_SD4_HRS04_WR BIT(24) > @@ -32,12 +44,93 @@ > #define SDHCI_CDNS_HRS06_TUNE GENMASK(13, 8) > #define SDHCI_CDNS_HRS06_MODE GENMASK(2, 0) > #define SDHCI_CDNS_HRS06_MODE_SD 0x0 > +#define SDHCI_CDNS_HRS06_MODE_LEGACY 0x1 > #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 > #define SDHCI_CDNS_HRS06_MODE_MMC_HS400ES 0x6 > > +/* SD 6.0 Controller HRS - Host Register Set (Specific to Cadence) */ > +#define SDHCI_CDNS_SD6_HRS04_ADDR GENMASK(15, 0) > + > +#define SDHCI_CDNS_HRS05 0x14 > + > +#define SDHCI_CDNS_HRS07 0x1C > +#define SDHCI_CDNS_HRS07_RW_COMPENSATE GENMASK(20, 16) > +#define SDHCI_CDNS_HRS07_IDELAY_VAL GENMASK(4, 0) > + > +#define SDHCI_CDNS_HRS09 0x24 > +#define SDHCI_CDNS_HRS09_RDDATA_EN BIT(16) > +#define SDHCI_CDNS_HRS09_RDCMD_EN BIT(15) > +#define SDHCI_CDNS_HRS09_EXTENDED_WR_MODE BIT(3) > +#define SDHCI_CDNS_HRS09_EXTENDED_RD_MODE BIT(2) > +#define SDHCI_CDNS_HRS09_PHY_INIT_COMPLETE BIT(1) > +#define SDHCI_CDNS_HRS09_PHY_SW_RESET BIT(0) > + > +#define SDHCI_CDNS_HRS10 0x28 > +#define SDHCI_CDNS_HRS10_HCSDCLKADJ GENMASK(19, 16) > + > +#define SDHCI_CDNS_SRS11 0x2c > +#define SDHCI_CDNS_SRS11_SW_RESET_ALL BIT(24) > +#define SDHCI_CDNS_SRS11_SW_RESET_CMD BIT(25) > +#define SDHCI_CDNS_SRS11_SW_RESET_DAT BIT(26) > + > +#define SDHCI_CDNS_HRS16 0x40 > +#define SDHCI_CDNS_HRS16_WRDATA1_SDCLK_DLY GENMASK(31, 28) > +#define SDHCI_CDNS_HRS16_WRDATA0_SDCLK_DLY GENMASK(27, 24) > +#define SDHCI_CDNS_HRS16_WRCMD1_SDCLK_DLY GENMASK(23, 20) > +#define SDHCI_CDNS_HRS16_WRCMD0_SDCLK_DLY GENMASK(19, 16) > +#define SDHCI_CDNS_HRS16_WRDATA1_DLY GENMASK(15, 12) > +#define SDHCI_CDNS_HRS16_WRDATA0_DLY GENMASK(11, 8) > +#define SDHCI_CDNS_HRS16_WRCMD1_DLY GENMASK(7, 4) > +#define SDHCI_CDNS_HRS16_WRCMD0_DLY GENMASK(3, 0) > + > +/* PHY registers for SD6 controller */ > +#define SDHCI_CDNS_SD6_PHY_DQ_TIMING 0x2000 > +#define SDHCI_CDNS_SD6_PHY_DQ_TIMING_IO_MASK_ALWAYS_ON BIT(31) > +#define SDHCI_CDNS_SD6_PHY_DQ_TIMING_IO_MASK_END > GENMASK(29, 27) > +#define SDHCI_CDNS_SD6_PHY_DQ_TIMING_IO_MASK_START > GENMASK(26, 24) > +#define SDHCI_CDNS_SD6_PHY_DQ_TIMING_DATA_SELECT_OE_END > GENMASK(2, 0) > + > +#define SDHCI_CDNS_SD6_PHY_DQS_TIMING 0x2004 > +#define SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_EXT_LPBK_DQS BIT(22) > +#define SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_LPBK_DQS BIT(21) > +#define SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_PHONY_DQS BIT(20) > +#define SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_PHONY_DQS_CMD BIT(19) > + > +#define SDHCI_CDNS_SD6_PHY_GATE_LPBK 0x2008 > +#define SDHCI_CDNS_SD6_PHY_GATE_LPBK_SYNC_METHOD BIT(31) > +#define SDHCI_CDNS_SD6_PHY_GATE_LPBK_SW_HALF_CYCLE_SHIFT BIT(28) > +#define SDHCI_CDNS_SD6_PHY_GATE_LPBK_RD_DEL_SEL > GENMASK(24, 19) > +#define SDHCI_CDNS_SD6_PHY_GATE_LPBK_UNDERRUN_SUPPRESS BIT(18) > +#define SDHCI_CDNS_SD6_PHY_GATE_LPBK_GATE_CFG_ALWAYS_ON BIT(6) > + > +#define SDHCI_CDNS_SD6_PHY_DLL_MASTER 0x200C > +#define SDHCI_CDNS_SD6_PHY_DLL_MASTER_BYPASS_MODE BIT(23) > +#define SDHCI_CDNS_SD6_PHY_DLL_MASTER_PHASE_DETECT_SEL > GENMASK(22, 20) > +#define SDHCI_CDNS_SD6_PHY_DLL_MASTER_DLL_LOCK_NUM > GENMASK(18, 16) > +#define SDHCI_CDNS_SD6_PHY_DLL_MASTER_DLL_START_POINT > GENMASK(7, 0) > + > +#define SDHCI_CDNS_SD6_PHY_DLL_SLAVE 0x2010 > +#define SDHCI_CDNS_SD6_PHY_DLL_SLAVE_READ_DQS_CMD_DELAY > GENMASK(31, 24) > +#define SDHCI_CDNS_SD6_PHY_DLL_SLAVE_CLK_WRDQS_DELAY > GENMASK(23, 16) > +#define SDHCI_CDNS_SD6_PHY_DLL_SLAVE_CLK_WR_DELAY > GENMASK(15, 8) > +#define SDHCI_CDNS_SD6_PHY_DLL_SLAVE_READ_DQS_DELAY > GENMASK(7, 0) > + > +#define SDHCI_CDNS_SD6_PHY_DLL_OBS_REG0 0x201C > +#define SDHCI_CDNS_SD6_PHY_DLL_OBS_REG0_DLL_LOCK_MODE > GENMASK(2, 1) > + > +#define SDHCI_CDNS_SD6_PHY_CTRL 0x2080 > +#define SDHCI_CDNS_SD6_PHY_CTRL_PHONY_DQS_TIMING > GENMASK(9, 4) > + > +#define SDHCI_CDNS_SD6_PHY_GPIO_CTRL0 0x2088 > +#define SDHCI_CDNS_SD6_PHY_GPIO_CTRL0_CLK_OVR_EN BIT(7) > +#define SDHCI_CDNS_SD6_PHY_GPIO_CTRL0_DRV GENMASK(6, 5) > +#define SDHCI_CDNS_SD6_PHY_GPIO_CTRL0_DRV_OVR_EN BIT(4) > +#define SDHCI_CDNS_SD6_PHY_GPIO_CTRL0_SLEW GENMASK(2, 1) > +#define SDHCI_CDNS_SD6_PHY_GPIO_CTRL0_SLEW_OVR_EN BIT(0) > + > /* SRS - Slot Register Set (SDHCI-compatible) */ > #define SDHCI_CDNS_SRS_BASE 0x200 > > @@ -62,15 +155,269 @@ > */ > #define SDHCI_CDNS_MAX_TUNING_LOOP 40 > > +struct sdhci_cdns_phy_cfg { > + const char *property; > + u8 addr; > +}; > + > +enum sdhci_cdns_sd6_phy_lock_mode { > + SDHCI_CDNS_SD6_PHY_LOCK_MODE_FULL_CLK = 0, > + SDHCI_CDNS_SD6_PHY_LOCK_MODE_HALF_CLK = 2, > + SDHCI_CDNS_SD6_PHY_LOCK_MODE_SATURATION = 3, > +}; > + > +struct sdhci_cdns_sd6_phy_timings { > + u32 t_cmd_output_min; > + u32 t_cmd_output_max; > + u32 t_dat_output_min; > + u32 t_dat_output_max; > + u32 t_cmd_input_min; > + u32 t_cmd_input_max; > + u32 t_dat_input_min; > + u32 t_dat_input_max; > + u32 t_sdclk_min; > + u32 t_sdclk_max; > +}; > + > +struct sdhci_cdns_sd6_phy_delays { > + u32 phy_sdclk_delay; > + u32 phy_cmd_o_delay; > + u32 phy_dat_o_delay; > + u32 iocell_input_delay; > + u32 iocell_output_delay; > + u32 delay_element_org; > + u32 delay_element; > +}; > + > +struct sdhci_cdns_sd6_phy_settings { > + /* SDHCI_CDNS_SD6_PHY_DLL_SLAVE */ > + u32 cp_read_dqs_cmd_delay; > + u32 cp_read_dqs_delay; > + u32 cp_clk_wr_delay; > + u32 cp_clk_wrdqs_delay; > + > + /* SDHCI_CDNS_SD6_PHY_DLL_MASTER */ > + u32 cp_dll_bypass_mode; > + u32 cp_dll_start_point; > + > + /* SDHCI_CDNS_SD6_PHY_DLL_OBS_REG0 */ > + u32 cp_dll_locked_mode; > + > + /* SDHCI_CDNS_SD6_PHY_GATE_LPBK */ > + u32 cp_gate_cfg_always_on; > + u32 cp_sync_method; > + u32 cp_rd_del_sel; > + u32 cp_sw_half_cycle_shift; > + u32 cp_underrun_suppress; > + > + /* SDHCI_CDNS_SD6_PHY_DQ_TIMING */ > + u32 cp_io_mask_always_on; > + u32 cp_io_mask_end; > + u32 cp_io_mask_start; > + u32 cp_data_select_oe_end; > + > + /* SDHCI_CDNS_SD6_PHY_DQS_TIMING */ > + u32 cp_use_ext_lpbk_dqs; > + u32 cp_use_lpbk_dqs; > + u8 cp_use_phony_dqs; > + u8 cp_use_phony_dqs_cmd; > + > + /* HRS 09 */ > + u8 sdhc_extended_rd_mode; > + u8 sdhc_extended_wr_mode; > + u32 sdhc_rdcmd_en; > + u32 sdhc_rddata_en; > + > + /* HRS10 */ > + u32 sdhc_hcsdclkadj; > + > + /* HRS 07 */ > + u32 sdhc_idelay_val; > + u32 sdhc_rw_compensate; > + > + /* SRS 11 */ > + u32 sdhc_sdcfsh; > + u32 sdhc_sdcfsl; > + > + /* HRS 16 */ > + u32 sdhc_wrcmd0_dly; > + u32 sdhc_wrcmd0_sdclk_dly; > + u32 sdhc_wrcmd1_dly; > + u32 sdhc_wrcmd1_sdclk_dly; > + u32 sdhc_wrdata0_dly; > + u32 sdhc_wrdata0_sdclk_dly; > + u32 sdhc_wrdata1_dly; > + u32 sdhc_wrdata1_sdclk_dly; > + > + u32 hs200_tune_val; > + u32 drive; > + u32 slew; > +}; > + > +struct sdhci_cdns_sd6_phy_intermediate_results { > + u32 t_sdmclk_calc; > + u32 dll_max_value; > +}; > + > +struct sdhci_cdns_sd6_phy { > + struct sdhci_cdns_sd6_phy_timings t; > + struct sdhci_cdns_sd6_phy_delays d; > + u32 t_sdmclk; > + struct sdhci_cdns_sd6_phy_settings settings; > + struct sdhci_cdns_sd6_phy_intermediate_results vars; > + bool ddr; > + bool tune_cmd; > + bool tune_dat; > + bool strobe_cmd; > + bool strobe_dat; > + int mode; > + int t_sdclk; > +}; > + > struct sdhci_cdns_plat { > struct mmc_config cfg; > struct mmc mmc; > void __iomem *hrs_addr; > + bool enhanced_strobe; > + void *priv; > }; > > -struct sdhci_cdns_phy_cfg { > - const char *property; > - u8 addr; > +static u32 tune_val_start = SDHCI_CDNS_TUNE_START; > +static u32 tune_val_step = SDHCI_CDNS_TUNE_STEP; > +static u32 max_tune_iter = SDHCI_CDNS_TUNE_ITERATIONS; > +static u32 read_dqs_cmd_delay; > +static struct sdhci_cdns_sd6_phy sd6_phy_config; > + > +/* Flag for SD6 controller */ > +static bool sd6_ctrl; > + > +static void init_hs(struct sdhci_cdns_sd6_phy_timings *t, int t_sdclk) > +{ > + *t = (struct sdhci_cdns_sd6_phy_timings){ > + .t_cmd_output_min = 2000, .t_cmd_output_max = t_sdclk - 6000, > + .t_dat_output_min = 2000, .t_dat_output_max = t_sdclk - 6000, > + .t_cmd_input_min = 14000, .t_cmd_input_max = t_sdclk + 2500, > + .t_dat_input_min = 14000, .t_dat_input_max = t_sdclk + 2500, > + .t_sdclk_min = 1000000 / 50, .t_sdclk_max = 1000000 / 0.4 > + }; > +} > + > +static void init_uhs_sdr12(struct sdhci_cdns_sd6_phy_timings *t, int t_sdclk) > +{ > + *t = (struct sdhci_cdns_sd6_phy_timings){ > + .t_cmd_output_min = 800, .t_cmd_output_max = t_sdclk - 3000, > + .t_dat_output_min = 800, .t_dat_output_max = t_sdclk - 3000, > + .t_cmd_input_min = 14000, .t_cmd_input_max = t_sdclk + 1500, > + .t_dat_input_min = 14000, .t_dat_input_max = t_sdclk + 1500, > + .t_sdclk_min = 1000000 / 25, .t_sdclk_max = 1000000 / 0.4 > + }; > +} > + > +static void init_uhs_sdr25(struct sdhci_cdns_sd6_phy_timings *t, int t_sdclk) > +{ > + *t = (struct sdhci_cdns_sd6_phy_timings){ > + .t_cmd_output_min = 800, .t_cmd_output_max = t_sdclk - 3000, > + .t_dat_output_min = 800, .t_dat_output_max = t_sdclk - 3000, > + .t_cmd_input_min = 14000, .t_cmd_input_max = t_sdclk + 1500, > + .t_dat_input_min = 14000, .t_dat_input_max = t_sdclk + 1500, > + .t_sdclk_min = 1000000 / 50, .t_sdclk_max = 1000000 / 0.4 > + }; > +} > + > +static void init_uhs_sdr50(struct sdhci_cdns_sd6_phy_timings *t, int t_sdclk) > +{ > + *t = (struct sdhci_cdns_sd6_phy_timings){ > + .t_cmd_output_min = 800, .t_cmd_output_max = t_sdclk - 3000, > + .t_dat_output_min = 800, .t_dat_output_max = t_sdclk - 3000, > + .t_cmd_input_min = 7500, .t_cmd_input_max = t_sdclk + 1500, > + .t_dat_input_min = 7500, .t_dat_input_max = t_sdclk + 1500, > + .t_sdclk_min = 1000000 / 100, .t_sdclk_max = 1000000 / 0.4 > + }; > +} > + > +static void init_uhs_sdr104(struct sdhci_cdns_sd6_phy_timings *t, int > t_sdclk) > +{ > + *t = (struct sdhci_cdns_sd6_phy_timings){ > + .t_cmd_output_min = 800, .t_cmd_output_max = t_sdclk - 1400, > + .t_dat_output_min = 800, .t_dat_output_max = t_sdclk - 1400, > + .t_cmd_input_min = 1000, .t_cmd_input_max = t_sdclk + 1000, > + .t_dat_input_min = 1000, .t_dat_input_max = t_sdclk + 1000, > + .t_sdclk_min = 1000000 / 200, .t_sdclk_max = 1000000 / 100 > + }; > +} > + > +static void init_uhs_ddr50(struct sdhci_cdns_sd6_phy_timings *t, int t_sdclk) > +{ > + *t = (struct sdhci_cdns_sd6_phy_timings){ > + .t_cmd_output_min = 800, .t_cmd_output_max = t_sdclk - 3000, > + .t_dat_output_min = 800, .t_dat_output_max = t_sdclk - 3000, > + .t_cmd_input_min = 13700, .t_cmd_input_max = t_sdclk + 1500, > + .t_dat_input_min = 7000, .t_dat_input_max = t_sdclk + 1500, > + .t_sdclk_min = 1000000 / 50, .t_sdclk_max = 1000000 / 0.4 > + }; > +} > + > +static void init_emmc_legacy(struct sdhci_cdns_sd6_phy_timings *t, int > t_sdclk) > +{ > + *t = (struct sdhci_cdns_sd6_phy_timings){ > + .t_cmd_output_min = 3000, .t_cmd_output_max = t_sdclk - 3000, > + .t_dat_output_min = 3000, .t_dat_output_max = t_sdclk - 3000, > + .t_cmd_input_min = 11700, .t_cmd_input_max = t_sdclk + 8300, > + .t_dat_input_min = 11700, .t_dat_input_max = t_sdclk + 8300, > + .t_sdclk_min = 1000000 / 25, .t_sdclk_max = 1000000 / 0.4 > + }; > +} > + > +static void init_emmc_sdr(struct sdhci_cdns_sd6_phy_timings *t, int t_sdclk) > +{ > + *t = (struct sdhci_cdns_sd6_phy_timings){ > + .t_cmd_output_min = 3000, .t_cmd_output_max = t_sdclk - 3000, > + .t_dat_output_min = 3000, .t_dat_output_max = t_sdclk - 3000, > + .t_cmd_input_min = 13700, .t_cmd_input_max = t_sdclk + 2500, > + .t_dat_input_min = 13700, .t_dat_input_max = t_sdclk + 2500, > + .t_sdclk_min = 1000000 / 50, .t_sdclk_max = 1000000 / 0.4 > + }; > +} > + > +static void init_emmc_ddr(struct sdhci_cdns_sd6_phy_timings *t, int t_sdclk) > +{ > + *t = (struct sdhci_cdns_sd6_phy_timings){ > + .t_cmd_output_min = 3000, .t_cmd_output_max = t_sdclk - 3000, > + .t_dat_output_min = 2500, .t_dat_output_max = t_sdclk - 2500, > + .t_cmd_input_min = 13700, .t_cmd_input_max = t_sdclk + 2500, > + .t_dat_input_min = 7000, .t_dat_input_max = t_sdclk + 1500, > + .t_sdclk_min = 1000000 / 50, .t_sdclk_max = 1000000 / 0.4 > + }; > +} > + > +static void init_emmc_hs200(struct sdhci_cdns_sd6_phy_timings *t, int > t_sdclk) > +{ > + *t = (struct sdhci_cdns_sd6_phy_timings){ > + .t_cmd_output_min = 800, .t_cmd_output_max = t_sdclk - 1400, > + .t_dat_output_min = 800, .t_dat_output_max = t_sdclk - 1400, > + .t_cmd_input_min = 1000, .t_cmd_input_max = t_sdclk + 1000, > + .t_dat_input_min = 1000, .t_dat_input_max = t_sdclk + 1000, > + .t_sdclk_min = 1000000 / 200, .t_sdclk_max = 1000000 / 100 > + }; > +} > + > +/* HS400 and HS400ES */ > +static void init_emmc_hs400(struct sdhci_cdns_sd6_phy_timings *t, int > t_sdclk) > +{ > + *t = (struct sdhci_cdns_sd6_phy_timings){ > + .t_cmd_output_min = 800, .t_cmd_output_max = t_sdclk - 1400, > + .t_dat_output_min = 400, .t_dat_output_max = t_sdclk - 400, > + .t_cmd_input_min = 1000, .t_cmd_input_max = t_sdclk + 1000, > + .t_dat_input_min = 1000, .t_dat_input_max = t_sdclk + 1000, > + .t_sdclk_min = 1000000 / 200, .t_sdclk_max = 1000000 / 100 > + }; > +} > + > +static void (*init_timings[])(struct sdhci_cdns_sd6_phy_timings*, int) = { > + &init_emmc_legacy, &init_emmc_legacy, &init_hs, &init_emmc_sdr, > + &init_emmc_ddr, &init_uhs_sdr12, &init_uhs_sdr25, &init_uhs_sdr50, > + &init_uhs_ddr50, &init_uhs_sdr104, &init_emmc_hs200, &init_emmc_hs400, > + &init_emmc_hs400, > }; > > static const struct sdhci_cdns_phy_cfg sdhci_cdns_sd4_phy_cfgs[] = { > @@ -133,10 +480,405 @@ static int sdhci_cdns_sd4_phy_init(struct > sdhci_cdns_plat *plat, > return 0; > } > > +static u32 sdhci_cdns_sd6_readl(struct sdhci_host *host, int reg) > +{ > + return readl(host->ioaddr + reg); > +} > + > +static u32 sdhci_cdns_sd6_read_phy_reg(struct sdhci_cdns_plat *plat, > + u32 addr) > +{ > + writel(addr, plat->hrs_addr + SDHCI_CDNS_HRS04); > + return readl(plat->hrs_addr + SDHCI_CDNS_HRS05); > +} > + > +static void sdhci_cdns_sd6_writel(struct sdhci_host *host, u32 val, int reg) > +{ > + writel(val, host->ioaddr + reg); > +} > + > +static u16 sdhci_cdns_sd6_readw(struct sdhci_host *host, int reg) > +{ > + u32 val, regoff; > + > + regoff = reg & ~3;
How about using macro? I don't know what 0x3 is meaning. #define XXX(x) (x & ~0x3) > + > + val = readl(host->ioaddr + regoff); > + if ((reg & 0x3) == 0) > + return (val & 0xFFFF); Use define macro instead of 0xFFFF. > + else > + return ((val >> 16) & 0xFFFF); > +} > + > +static void sdhci_cdns_sd6_writew(struct sdhci_host *host, u16 val, int reg) > +{ > + writew(val, host->ioaddr + reg); > +} > + > +static u8 sdhci_cdns_sd6_readb(struct sdhci_host *host, int reg) > +{ > + u32 val, regoff; > + > + regoff = reg & ~3; ditto. If using macro, it can be replaced. > + > + val = readl(host->ioaddr + regoff); > + switch (reg & 3) { Use 0x3 and meaningful macro. > + case 0: > + return (val & 0xFF); > + case 1: > + return ((val >> 8) & 0xFF); > + case 2: > + return ((val >> 16) & 0xFF); > + case 3: > + return ((val >> 24) & 0xFF); > + } > + return 0; Never come to here. reg & 0x3 should be 0,1,2 or 3. > +} > + > +static void sdhci_cdns_sd6_writeb(struct sdhci_host *host, u8 val, int reg) > +{ > + writeb(val, host->ioaddr + reg); > +} > + > +static int sdhci_cdns_sd6_get_delay_params(struct udevice *dev, struct > sdhci_cdns_plat *plat) Not need to use int type? it can be void. > +{ > + struct sdhci_cdns_sd6_phy *phy = plat->priv; > + int ret; > + > + ret = dev_read_u32(dev, "cdns,read_dqs_cmd_delay", > + &phy->settings.cp_read_dqs_cmd_delay); > + if (ret) > + phy->settings.cp_read_dqs_cmd_delay = DEFAULT_CMD_DELAY; > + > + ret = dev_read_u32(dev, "cdns,tune_val_start", &tune_val_start); > + if (ret) > + tune_val_start = SDHCI_CDNS_TUNE_START; > + > + ret = dev_read_u32(dev, "cdns,tune_val_step", &tune_val_step); > + if (ret) > + tune_val_step = SDHCI_CDNS_TUNE_STEP; > + > + ret = dev_read_u32(dev, "cdns,max_tune_iter", &max_tune_iter); > + if (ret) > + max_tune_iter = SDHCI_CDNS_TUNE_ITERATIONS; > + > + read_dqs_cmd_delay = phy->settings.cp_read_dqs_cmd_delay; > + return 0; > +} > + > +#if CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT) > +static void sdhci_cdns_set_emmc_mode(struct sdhci_cdns_plat *priv, u32 mode) > +{ > + u32 tmp; > + > + /* The speed mode for eMMC is selected by HRS06 register */ > + tmp = readl(priv->hrs_addr + SDHCI_CDNS_HRS06); > + tmp &= ~SDHCI_CDNS_HRS06_MODE; > + tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_MODE, mode); > + writel(tmp, priv->hrs_addr + SDHCI_CDNS_HRS06); > +} > +#endif > + > +void sdhci_cdns_sd6_fullsw_reset(struct sdhci_cdns_plat *plat) > +{ > + u32 regval; > + > + regval = readl(plat->hrs_addr + SDHCI_CDNS_HRS00); > + regval |= SDHCI_CDNS_HRS00_SWR; > + writel(regval, plat->hrs_addr + SDHCI_CDNS_HRS00); > + > + do { > + regval = readl(plat->hrs_addr + SDHCI_CDNS_HRS00); > + } while (regval & SDHCI_CDNS_HRS00_SWR); readl_poll_timeout()? > +} > + > +void sdhci_cdns_sd6_stop_clock(struct sdhci_cdns_plat *plat) > +{ > + u32 reg_srs11 = 0; > + > + reg_srs11 = readl(plat->hrs_addr + SDHCI_CDNS_SRS_BASE + > SDHCI_CDNS_SRS11); > + reg_srs11 &= ~5; Use macro instead of 5. (otherwise 0x5) > + writel(reg_srs11, plat->hrs_addr + SDHCI_CDNS_SRS_BASE + > SDHCI_CDNS_SRS11); > +} > + > +void sdhci_cdns_sd6_set_volt(struct sdhci_cdns_plat *plat) > +{ > + u32 controller_setting = 0; > + > + controller_setting = readl(plat->hrs_addr + SDHCI_CDNS_SRS_BASE + > SDHCI_CDNS_HRS10); > + controller_setting &= ~(7 << 9); > + > + controller_setting |= (7 << 9); > + controller_setting |= (1 << 8); Ditto. Use macro about (7 << 9) and (1 << 8) > + > + writel(controller_setting, plat->hrs_addr + SDHCI_CDNS_SRS_BASE + > SDHCI_CDNS_HRS10); > + controller_setting = readl(plat->hrs_addr + SDHCI_CDNS_SRS_BASE + > SDHCI_CDNS_HRS10); > +} > + > +static int sdhci_cdns_sd6_get_fdt_params(struct udevice *dev, struct > sdhci_cdns_plat *plat) > +{ > + struct sdhci_cdns_sd6_phy *phy = plat->priv; > + const char *mode_name; > + int ret; > + > + dev_read_u32(dev, "cdns,iocell_input_delay", > &phy->d.iocell_input_delay); > + > + dev_read_u32(dev, "cdns,iocell_output_delay", > &phy->d.iocell_output_delay); > + > + dev_read_u32(dev, "cdns,delay_element", &phy->d.delay_element); > + > + ret = dev_read_u32(dev, "cdns,host_slew", &phy->settings.slew); dev_read_u32_default()? > + if (ret) > + phy->settings.slew = 3; > + > + ret = dev_read_u32(dev, "cdns,host_drive", &phy->settings.drive); > + if (ret) > + phy->settings.drive = 2; > + > + mode_name = dev_read_string(dev, "cdns,mode"); > + > + if (mode_name != NULL) { It cna be "if (mode_name)" > + if (!strcmp("emmc_sdr", mode_name)) > + phy->mode = MMC_HS_52; > + else if (!strcmp("emmc_ddr", mode_name)) > + phy->mode = MMC_DDR_52; > + else if (!strcmp("emmc_hs200", mode_name)) > + phy->mode = MMC_HS_200; > + else if (!strcmp("emmc_hs400", mode_name)) > + phy->mode = MMC_HS_400; > + else if (!strcmp("emmc_hs400_es", mode_name)) > + phy->mode = MMC_HS_400_ES; > + else if (!strcmp("sd_hs", mode_name)) > + phy->mode = SD_HS; > + else > + phy->mode = MMC_HS; > + } else { > + phy->mode = MMC_HS; > + } > + > + sdhci_cdns_sd6_get_delay_params(dev, plat); > + return 0; > +} > + > +static void sdhci_cdns_sd6_write_phy_reg(struct sdhci_cdns_plat *plat, > + u32 addr, u32 data) > +{ > + writel(addr, plat->hrs_addr + SDHCI_CDNS_HRS04); > + writel(data, plat->hrs_addr + SDHCI_CDNS_HRS05); > +} > + > +static int sdhci_cdns_sd6_dll_reset(struct sdhci_cdns_plat *plat, bool > doreset) > +{ > + u32 reg; > + int ret = 0; > + > + reg = readl(plat->hrs_addr + SDHCI_CDNS_HRS09); > + if (doreset) > + reg &= ~SDHCI_CDNS_HRS09_PHY_SW_RESET; > + else > + reg |= SDHCI_CDNS_HRS09_PHY_SW_RESET; > + writel(reg, plat->hrs_addr + SDHCI_CDNS_HRS09); > + > + if (!doreset) { > + do { > + reg = readl(plat->hrs_addr + SDHCI_CDNS_HRS09); > + } while ((reg & SDHCI_CDNS_HRS09_PHY_INIT_COMPLETE) == 0); > + } > + > + return ret; > +} > + > +static int sdhci_cdns_sd6_phy_init(struct udevice *dev, struct > sdhci_cdns_plat *plat) > +{ > + int ret; > + u32 reg; > + struct sdhci_cdns_sd6_phy *phy = plat->priv; > + > + if ((phy->mode == -1) || (phy->t_sdclk == -1)) > + return 0; > + > + sdhci_cdns_sd6_dll_reset(plat, true); > + > + /* cp_use_phony_dqs SDHCI_CDNS_SD6_PHY_DQS_TIMING */ > + reg = sdhci_cdns_sd6_read_phy_reg(plat, SDHCI_CDNS_SD6_PHY_DQS_TIMING); > + reg &= ~SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_EXT_LPBK_DQS; > + reg &= ~SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_LPBK_DQS; > + reg &= ~SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_PHONY_DQS; > + reg &= ~SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_PHONY_DQS_CMD; > + reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_EXT_LPBK_DQS, > + phy->settings.cp_use_ext_lpbk_dqs); > + reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_LPBK_DQS, > + phy->settings.cp_use_lpbk_dqs); > + reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_PHONY_DQS, > + phy->settings.cp_use_phony_dqs); > + reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DQS_TIMING_USE_PHONY_DQS_CMD, > + phy->settings.cp_use_phony_dqs_cmd); > + > + sdhci_cdns_sd6_write_phy_reg(plat, SDHCI_CDNS_SD6_PHY_DQS_TIMING, reg); > + > + /* SDHCI_CDNS_SD6_PHY_GATE_LPBK */ > + reg = sdhci_cdns_sd6_read_phy_reg(plat, SDHCI_CDNS_SD6_PHY_GATE_LPBK); > + reg &= ~SDHCI_CDNS_SD6_PHY_GATE_LPBK_SYNC_METHOD; > + reg &= ~SDHCI_CDNS_SD6_PHY_GATE_LPBK_SW_HALF_CYCLE_SHIFT; > + reg &= ~SDHCI_CDNS_SD6_PHY_GATE_LPBK_RD_DEL_SEL; > + reg &= ~SDHCI_CDNS_SD6_PHY_GATE_LPBK_GATE_CFG_ALWAYS_ON; > + reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_GATE_LPBK_SYNC_METHOD, > + phy->settings.cp_sync_method); > + reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_GATE_LPBK_SW_HALF_CYCLE_SHIFT, > + phy->settings.cp_sw_half_cycle_shift); > + reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_GATE_LPBK_RD_DEL_SEL, > + phy->settings.cp_rd_del_sel); > + reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_GATE_LPBK_GATE_CFG_ALWAYS_ON, > + phy->settings.cp_gate_cfg_always_on); > + reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_GATE_LPBK_UNDERRUN_SUPPRESS, > + phy->settings.cp_underrun_suppress); > + > + sdhci_cdns_sd6_write_phy_reg(plat, SDHCI_CDNS_SD6_PHY_GATE_LPBK, reg); > + > + /* SDHCI_CDNS_SD6_PHY_DLL_MASTER */ > + reg = 0x0; > + reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DLL_MASTER_BYPASS_MODE, > + phy->settings.cp_dll_bypass_mode); > + reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DLL_MASTER_PHASE_DETECT_SEL, 2); > + reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DLL_MASTER_DLL_START_POINT, > + phy->settings.cp_dll_start_point); > + > + sdhci_cdns_sd6_write_phy_reg(plat, SDHCI_CDNS_SD6_PHY_DLL_MASTER, reg); > + > + /* SDHCI_CDNS_SD6_PHY_DLL_SLAVE */ > + reg = 0x0; > + reg = FIELD_PREP(SDHCI_CDNS_SD6_PHY_DLL_SLAVE_READ_DQS_CMD_DELAY, > + phy->settings.cp_read_dqs_cmd_delay); > + reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DLL_SLAVE_CLK_WRDQS_DELAY, > + phy->settings.cp_clk_wrdqs_delay); > + reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DLL_SLAVE_CLK_WR_DELAY, > + phy->settings.cp_clk_wr_delay); > + reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DLL_SLAVE_READ_DQS_DELAY, > + phy->settings.cp_read_dqs_delay); > + > + sdhci_cdns_sd6_write_phy_reg(plat, SDHCI_CDNS_SD6_PHY_DLL_SLAVE, reg); > + > + /* SDHCI_CDNS_SD6_PHY_CTRL */ > + reg = sdhci_cdns_sd6_read_phy_reg(plat, SDHCI_CDNS_SD6_PHY_CTRL); > + reg &= ~SDHCI_CDNS_SD6_PHY_CTRL_PHONY_DQS_TIMING; > + sdhci_cdns_sd6_write_phy_reg(plat, SDHCI_CDNS_SD6_PHY_CTRL, reg); > + > + reg = sdhci_cdns_sd6_read_phy_reg(plat, SDHCI_CDNS_SD6_PHY_GPIO_CTRL0); > + reg |= SDHCI_CDNS_SD6_PHY_GPIO_CTRL0_CLK_OVR_EN; > + sdhci_cdns_sd6_write_phy_reg(plat, SDHCI_CDNS_SD6_PHY_GPIO_CTRL0, reg); > + > + reg = sdhci_cdns_sd6_read_phy_reg(plat, SDHCI_CDNS_SD6_PHY_GPIO_CTRL0); > + /* Clear the drive and slew fields */ > + reg &= ~0x77; Not use magicode as 0x77. > + /* Use the drive and slew from settings */ > + reg |= SDHCI_CDNS_SD6_PHY_GPIO_CTRL0_DRV_OVR_EN | > + SDHCI_CDNS_SD6_PHY_GPIO_CTRL0_SLEW_OVR_EN; > + reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_GPIO_CTRL0_DRV, > + phy->settings.drive); > + reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_GPIO_CTRL0_SLEW, > + phy->settings.slew); > + > + sdhci_cdns_sd6_write_phy_reg(plat, SDHCI_CDNS_SD6_PHY_GPIO_CTRL0, reg); > + > + ret = sdhci_cdns_sd6_dll_reset(plat, false); > + if (ret) > + return ret; > + > + reg = sdhci_cdns_sd6_read_phy_reg(plat, SDHCI_CDNS_SD6_PHY_DQ_TIMING); > + reg &= ~SDHCI_CDNS_SD6_PHY_DQ_TIMING_IO_MASK_ALWAYS_ON; > + reg &= ~SDHCI_CDNS_SD6_PHY_DQ_TIMING_IO_MASK_END; > + reg &= ~SDHCI_CDNS_SD6_PHY_DQ_TIMING_IO_MASK_START; > + reg &= ~SDHCI_CDNS_SD6_PHY_DQ_TIMING_DATA_SELECT_OE_END; > + reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DQ_TIMING_IO_MASK_ALWAYS_ON, > + phy->settings.cp_io_mask_always_on); > + reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DQ_TIMING_IO_MASK_END, > + phy->settings.cp_io_mask_end); > + reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DQ_TIMING_IO_MASK_START, > + phy->settings.cp_io_mask_start); > + reg |= FIELD_PREP(SDHCI_CDNS_SD6_PHY_DQ_TIMING_DATA_SELECT_OE_END, > + phy->settings.cp_data_select_oe_end); > + > + sdhci_cdns_sd6_write_phy_reg(plat, SDHCI_CDNS_SD6_PHY_DQ_TIMING, reg); > + > + reg = readl(plat->hrs_addr + SDHCI_CDNS_HRS09); > + if (phy->settings.sdhc_extended_wr_mode) > + reg |= SDHCI_CDNS_HRS09_EXTENDED_WR_MODE; > + else > + reg &= ~SDHCI_CDNS_HRS09_EXTENDED_WR_MODE; > + > + if (phy->settings.sdhc_extended_rd_mode) > + reg |= SDHCI_CDNS_HRS09_EXTENDED_RD_MODE; > + else > + reg &= ~SDHCI_CDNS_HRS09_EXTENDED_RD_MODE; > + > + if (phy->settings.sdhc_rddata_en) > + reg |= SDHCI_CDNS_HRS09_RDDATA_EN; > + else > + reg &= ~SDHCI_CDNS_HRS09_RDDATA_EN; > + > + if (phy->settings.sdhc_rdcmd_en) > + reg |= SDHCI_CDNS_HRS09_RDCMD_EN; > + else > + reg &= ~SDHCI_CDNS_HRS09_RDCMD_EN; > + > + writel(reg, plat->hrs_addr + SDHCI_CDNS_HRS09); > + > + writel(0x30004, plat->hrs_addr + SDHCI_CDNS_HRS02); don't like the using magicode. Could you use macro instead of 0x30004? > + > + reg = 0x0; > + reg = FIELD_PREP(SDHCI_CDNS_HRS10_HCSDCLKADJ, > phy->settings.sdhc_hcsdclkadj); > + writel(reg, plat->hrs_addr + SDHCI_CDNS_HRS10); > + > + if (phy->mode == MMC_HS_52 || phy->mode == MMC_DDR_52) { > + reg = 0x202; Ditto. > + } else { > + reg = 0x0; > + reg = FIELD_PREP(SDHCI_CDNS_HRS16_WRDATA1_SDCLK_DLY, > + phy->settings.sdhc_wrdata1_sdclk_dly); > + reg |= FIELD_PREP(SDHCI_CDNS_HRS16_WRDATA0_SDCLK_DLY, > + phy->settings.sdhc_wrdata0_sdclk_dly); > + reg |= FIELD_PREP(SDHCI_CDNS_HRS16_WRCMD1_SDCLK_DLY, > + phy->settings.sdhc_wrcmd1_sdclk_dly); > + reg |= FIELD_PREP(SDHCI_CDNS_HRS16_WRCMD0_SDCLK_DLY, > + phy->settings.sdhc_wrcmd0_sdclk_dly); > + reg |= FIELD_PREP(SDHCI_CDNS_HRS16_WRDATA1_DLY, > + phy->settings.sdhc_wrdata1_dly); > + reg |= FIELD_PREP(SDHCI_CDNS_HRS16_WRDATA0_DLY, > + phy->settings.sdhc_wrdata0_dly); > + reg |= FIELD_PREP(SDHCI_CDNS_HRS16_WRCMD1_DLY, > + phy->settings.sdhc_wrcmd1_dly); > + reg |= FIELD_PREP(SDHCI_CDNS_HRS16_WRCMD0_DLY, > + phy->settings.sdhc_wrcmd0_dly); > + } > + > + writel(reg, plat->hrs_addr + SDHCI_CDNS_HRS16); > + > + reg = 0x0; > + reg = FIELD_PREP(SDHCI_CDNS_HRS07_RW_COMPENSATE, > + phy->settings.sdhc_rw_compensate); > + reg |= FIELD_PREP(SDHCI_CDNS_HRS07_IDELAY_VAL, > + phy->settings.sdhc_idelay_val); > + writel(reg, plat->hrs_addr + SDHCI_CDNS_HRS07); > + return 0; > +} > + > +static int sdhci_cdns_sd6_set_tune_val(struct sdhci_cdns_plat *plat, > + unsigned int val) > +{ > + struct sdhci_cdns_sd6_phy *phy = plat->priv; > + > + phy->settings.hs200_tune_val = val; > + phy->settings.cp_read_dqs_cmd_delay = val; > + phy->settings.cp_read_dqs_delay = val; > + > + return sdhci_cdns_sd6_phy_init(NULL, plat); > +} > + > static void sdhci_cdns_set_control_reg(struct sdhci_host *host) > { > struct mmc *mmc = host->mmc; > struct sdhci_cdns_plat *plat = dev_get_plat(mmc->dev); > + struct sdhci_cdns_sd6_phy *phy = plat->priv; > unsigned int clock = mmc->clock; > u32 mode, tmp; > > @@ -146,17 +888,43 @@ static void sdhci_cdns_set_control_reg(struct > sdhci_host *host) > * U-Boot does not support timing. Use the clock frequency instead. > */ > if (clock <= 26000000) { > - mode = SDHCI_CDNS_HRS06_MODE_SD; /* use this for Legacy */ > + phy->mode = MMC_LEGACY; /* use this for Legacy */ > } else if (clock <= 52000000) { > if (mmc->ddr_mode) > - mode = SDHCI_CDNS_HRS06_MODE_MMC_DDR; > + phy->mode = MMC_DDR_52; > else > - mode = SDHCI_CDNS_HRS06_MODE_MMC_SDR; > + phy->mode = MMC_HS_52; > } else { > if (mmc->ddr_mode) > - mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400; > + phy->mode = MMC_HS_400; > else > - mode = SDHCI_CDNS_HRS06_MODE_MMC_HS200; > + phy->mode = MMC_HS_200; > + } > + > + switch (phy->mode) { > + case MMC_LEGACY: > + case MMC_HS: > + mode = SDHCI_CDNS_HRS06_MODE_LEGACY; > + break; > + case MMC_HS_52: > + mode = SDHCI_CDNS_HRS06_MODE_MMC_SDR; > + break; > + case MMC_DDR_52: > + mode = SDHCI_CDNS_HRS06_MODE_MMC_DDR; > + break; > + case MMC_HS_200: > + mode = SDHCI_CDNS_HRS06_MODE_MMC_HS200; > + break; > + case MMC_HS_400: > + mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400; > + break; > + case MMC_HS_400_ES: > + mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400ES; > + break; > + default: > + /* All other modes treated as SD */ > + mode = SDHCI_CDNS_HRS06_MODE_SD; > + break; > } > > tmp = readl(plat->hrs_addr + SDHCI_CDNS_HRS06); > @@ -165,10 +933,6 @@ static void sdhci_cdns_set_control_reg(struct sdhci_host > *host) > writel(tmp, plat->hrs_addr + SDHCI_CDNS_HRS06); > } > > -static const struct sdhci_ops sdhci_cdns_ops = { > - .set_control_reg = sdhci_cdns_set_control_reg, > -}; > - > static int sdhci_cdns_set_tune_val(struct sdhci_cdns_plat *plat, > unsigned int val) > { > @@ -201,6 +965,443 @@ static int sdhci_cdns_set_tune_val(struct > sdhci_cdns_plat *plat, > return 0; > } > > +static int sdhci_cdns_sd6_phy_lock_dll(struct sdhci_cdns_sd6_phy *phy) > +{ > + u32 delay_element = phy->d.delay_element_org; > + u32 delay_elements_in_sdmclk; > + enum sdhci_cdns_sd6_phy_lock_mode mode; > + > + delay_elements_in_sdmclk = DIV_ROUND_UP(phy->t_sdmclk, delay_element); > + if (delay_elements_in_sdmclk > 256) { > + delay_element *= 2; > + delay_elements_in_sdmclk = DIV_ROUND_UP(phy->t_sdmclk, > + delay_element); > + > + if (delay_elements_in_sdmclk > 256) { > + return -1; > + } else { not need to use "else" > + mode = SDHCI_CDNS_SD6_PHY_LOCK_MODE_HALF_CLK; > + phy->vars.dll_max_value = 127; > + } > + } else { > + mode = SDHCI_CDNS_SD6_PHY_LOCK_MODE_FULL_CLK; > + phy->vars.dll_max_value = 255; > + } > + > + phy->vars.t_sdmclk_calc = delay_element * delay_elements_in_sdmclk; > + phy->d.delay_element = delay_element; > + phy->settings.cp_dll_locked_mode = mode; > + phy->settings.cp_dll_bypass_mode = 0; > + > + return 0; > +} > + > +static void sdhci_cdns_sd6_phy_dll_bypass(struct sdhci_cdns_sd6_phy *phy) > +{ > + phy->vars.dll_max_value = 256; > + phy->settings.cp_dll_bypass_mode = 1; > + phy->settings.cp_dll_locked_mode = > + SDHCI_CDNS_SD6_PHY_LOCK_MODE_SATURATION; > +} > + > +static void sdhci_cdns_sd6_phy_configure_dll(struct sdhci_cdns_sd6_phy *phy) > +{ > + if (phy->settings.sdhc_extended_wr_mode == 0) { > + if (sdhci_cdns_sd6_phy_lock_dll(phy) == 0) if (... || ...) return; > + return; > + } > + sdhci_cdns_sd6_phy_dll_bypass(phy); > +} > + > +static void sdhci_cdns_sd6_phy_calc_out(struct sdhci_cdns_sd6_phy *phy, > + bool cmd_not_dat) > +{ > + u32 wr0_dly = 0, wr1_dly = 0, output_min, output_max, phy_o_delay; > + u32 clk_wr_delay = 0, wr0_sdclk_dly = 0, wr1_sdclk_dly = 0; > + bool data_ddr = phy->ddr && !cmd_not_dat; > + int t; > + > + if (cmd_not_dat) { > + output_min = phy->t.t_cmd_output_min; > + output_max = phy->t.t_cmd_output_max; > + phy_o_delay = phy->d.phy_cmd_o_delay; > + } else { > + output_min = phy->t.t_dat_output_min; > + output_max = phy->t.t_dat_output_max; > + phy_o_delay = phy->d.phy_dat_o_delay; > + } > + > + clk_wr_delay = 0; > + if (data_ddr) > + wr0_sdclk_dly = wr1_sdclk_dly = 1; CHECK: multiple assignments should be avoided #972: FILE: drivers/mmc/sdhci-cadence.c:1036: + wr0_sdclk_dly = wr1_sdclk_dly = 1; > + > + t = phy_o_delay - phy->d.phy_sdclk_delay - output_min; > + if (t < 0 && phy->settings.sdhc_extended_wr_mode == 1) { > + u32 n_half_cycle = DIV_ROUND_UP(-t * 2, phy->t_sdmclk); > + > + wr0_dly = (n_half_cycle + 1) / 2; > + if (data_ddr) > + wr1_dly = (n_half_cycle + 1) / 2; > + else > + wr1_dly = (n_half_cycle + 1) % 2 + wr0_dly - 1; > + } > + > + if (phy->settings.sdhc_extended_wr_mode == 0) { > + u32 out_hold, out_setup, out_hold_margin; > + u32 n; > + > + if (!data_ddr) > + wr0_dly = 1; > + > + out_setup = output_max; > + out_hold = output_min; > + out_hold_margin = DIV_ROUND_UP(out_setup - out_hold, 4); > + out_hold += out_hold_margin; > + > + if (phy->settings.cp_dll_bypass_mode == 0) > + n = DIV_ROUND_UP(256 * out_hold, > phy->vars.t_sdmclk_calc); > + else > + n = DIV_ROUND_UP(out_hold, phy->d.delay_element) - 1; > + > + if (n <= phy->vars.dll_max_value) { > + clk_wr_delay = n; > + } else { > + clk_wr_delay = 255; > + /* no dll setting*/ Remove comment. Is it necessary? > + } > + } else { > + /* sdhc_extended_wr_mode = 1 - PHY IO cell work in SDR mode */ > + clk_wr_delay = 0; > + } > + > + if (cmd_not_dat) { > + phy->settings.sdhc_wrcmd0_dly = wr0_dly; > + phy->settings.sdhc_wrcmd1_dly = wr1_dly; > + phy->settings.cp_clk_wrdqs_delay = clk_wr_delay; > + phy->settings.sdhc_wrcmd0_sdclk_dly = wr0_sdclk_dly; > + phy->settings.sdhc_wrcmd1_sdclk_dly = wr1_sdclk_dly; > + } else { > + phy->settings.sdhc_wrdata0_dly = wr0_dly; > + phy->settings.sdhc_wrdata1_dly = wr1_dly; > + phy->settings.cp_clk_wr_delay = clk_wr_delay; > + phy->settings.sdhc_wrdata0_sdclk_dly = wr0_sdclk_dly; > + phy->settings.sdhc_wrdata1_sdclk_dly = wr1_sdclk_dly; > + } > +} > + > +static void sdhci_cdns_sd6_phy_calc_cmd_out(struct sdhci_cdns_sd6_phy *phy) > +{ > + sdhci_cdns_sd6_phy_calc_out(phy, true); > +} > + > +static void sdhci_cdns_sd6_phy_calc_cmd_in(struct sdhci_cdns_sd6_phy *phy) > +{ > + phy->settings.cp_io_mask_end = > + ((phy->d.iocell_output_delay + phy->d.iocell_input_delay) * 2) > + / phy->t_sdmclk; > + > + if (phy->settings.cp_io_mask_end >= 8) > + phy->settings.cp_io_mask_end = 7; > + > + if (phy->strobe_cmd && phy->settings.cp_io_mask_end > 0) > + phy->settings.cp_io_mask_end--; > + > + if (phy->strobe_cmd) { > + phy->settings.cp_use_phony_dqs_cmd = 0; > + phy->settings.cp_read_dqs_cmd_delay = 64; > + } else { > + phy->settings.cp_use_phony_dqs_cmd = 1; > + phy->settings.cp_read_dqs_cmd_delay = 0; > + } > + > + if ((phy->mode == MMC_HS_400 && !phy->strobe_cmd) || > + phy->mode == MMC_HS_200) > + phy->settings.cp_read_dqs_cmd_delay = > + phy->settings.hs200_tune_val; > +} > + > +static void sdhci_cdns_sd6_phy_calc_dat_in(struct sdhci_cdns_sd6_phy *phy) > +{ > + u32 hcsdclkadj = 0; Is there any meaning about hcsdclkadj? > + > + if (phy->strobe_dat) { > + phy->settings.cp_use_phony_dqs = 0; > + phy->settings.cp_read_dqs_delay = 64; > + } else { > + phy->settings.cp_use_phony_dqs = 1; > + phy->settings.cp_read_dqs_delay = 0; > + } > + > + if (phy->mode == MMC_HS_200) > + phy->settings.cp_read_dqs_delay = > + phy->settings.hs200_tune_val; > + > + if (phy->strobe_dat) { > + /* dqs loopback input via IO cell */ > + hcsdclkadj += phy->d.iocell_input_delay; > + /* dfi_dqs_in: mem_dqs -> clean_dqs_mod; delay of > hic_dll_dqs_nand2 */ > + hcsdclkadj += phy->d.delay_element / 2; > + /* delay line */ > + hcsdclkadj += phy->t_sdclk / 2; > + /* PHY FIFO write pointer */ > + hcsdclkadj += phy->t_sdclk / 2 + phy->d.delay_element; > + /* 1st synchronizer */ > + hcsdclkadj += DIV_ROUND_UP(hcsdclkadj, phy->t_sdmclk) > + * phy->t_sdmclk - hcsdclkadj; > + /* > + * 2nd synchronizer + PHY FIFO read pointer + PHY rddata > + * + PHY rddata registered, + FIFO 1st ciu_en > + */ > + hcsdclkadj += 5 * phy->t_sdmclk; > + /* FIFO 2st ciu_en */ > + hcsdclkadj += phy->t_sdclk; > + > + hcsdclkadj /= phy->t_sdclk; > + } else { > + u32 n; > + > + /* rebar PHY delay */ > + hcsdclkadj += 2 * phy->t_sdmclk; > + /* rebar output via IO cell */ > + hcsdclkadj += phy->d.iocell_output_delay; > + /* dqs loopback input via IO cell */ > + hcsdclkadj += phy->d.iocell_input_delay; > + /* dfi_dqs_in: mem_dqs -> clean_dqs_mod delay of > hic_dll_dqs_nand2 */ > + hcsdclkadj += phy->d.delay_element / 2; > + /* dll: one delay element between SIGI_0 and SIGO_0 */ > + hcsdclkadj += phy->d.delay_element; > + /* dfi_dqs_in: mem_dqs_delayed -> clk_dqs delay of > hic_dll_dqs_nand2 */ > + hcsdclkadj += phy->d.delay_element / 2; > + /* deskew DLL: clk_dqs -> clk_dqN: one delay element */ > + hcsdclkadj += phy->d.delay_element; > + > + if (phy->t_sdclk == phy->t_sdmclk) > + n = (hcsdclkadj - 2 * phy->t_sdmclk) / phy->t_sdclk; > + else > + n = hcsdclkadj / phy->t_sdclk; > + > + /* phase shift within one t_sdclk clock cycle caused by rebar - > lbk dqs delay */ > + hcsdclkadj = hcsdclkadj % phy->t_sdclk; > + /* PHY FIFO write pointer */ > + hcsdclkadj += phy->t_sdclk / 2; > + /* 1st synchronizer */ > + hcsdclkadj += DIV_ROUND_UP(hcsdclkadj, phy->t_sdmclk) > + * phy->t_sdmclk - hcsdclkadj; > + /* > + * 2nd synchronizer + PHY FIFO read pointer + PHY rddata > + * + PHY rddata registered > + */ > + hcsdclkadj += 4 * phy->t_sdmclk; > + > + if ((phy->t_sdclk / phy->t_sdmclk) > 1) { > + u32 tmp1, tmp2; > + > + tmp1 = hcsdclkadj; > + tmp2 = (hcsdclkadj / phy->t_sdclk) * phy->t_sdclk > + + phy->t_sdclk - phy->t_sdmclk; > + if (tmp1 == tmp2) > + tmp2 += phy->t_sdclk; > + > + /* FIFO aligns to clock cycle before ciu_en */ > + hcsdclkadj += tmp2 - tmp1; > + } > + > + /* FIFO 1st ciu_en */ > + hcsdclkadj += phy->t_sdmclk; > + /* FIFO 2nd ciu_en */ > + hcsdclkadj += phy->t_sdclk; > + > + hcsdclkadj /= phy->t_sdclk; > + > + hcsdclkadj += n; > + > + if ((phy->t_sdclk / phy->t_sdmclk) >= 2) { > + if (phy->mode == UHS_DDR50 || phy->mode == MMC_DDR_52) > + hcsdclkadj -= 2; > + else > + hcsdclkadj -= 1; > + } else if ((phy->t_sdclk / phy->t_sdmclk) == 1) { > + hcsdclkadj += 2; > + } > + > + if (phy->tune_dat) > + hcsdclkadj -= 1; > + } > + > + if (hcsdclkadj > 15) > + hcsdclkadj = 15; > + > + phy->settings.sdhc_hcsdclkadj = hcsdclkadj; > +} > + > +static void sdhci_cdns_sd6_phy_calc_dat_out(struct sdhci_cdns_sd6_phy *phy) > +{ > + sdhci_cdns_sd6_phy_calc_out(phy, false); > +} > + > +static void sdhci_cdns_sd6_phy_calc_io(struct sdhci_cdns_sd6_phy *phy) > +{ > + u32 rw_compensate; > + > + rw_compensate = (phy->d.iocell_input_delay + phy->d.iocell_output_delay) > + / phy->t_sdmclk + phy->settings.sdhc_wrdata0_dly + 5 + 3; > + > + phy->settings.sdhc_idelay_val = (2 * phy->d.iocell_input_delay) > + / phy->t_sdmclk; > + > + phy->settings.cp_io_mask_start = 0; > + if (phy->t_sdclk == phy->t_sdmclk && rw_compensate > 10) > + phy->settings.cp_io_mask_start = 2 * (rw_compensate - 10); > + > + if (phy->mode == UHS_SDR104) > + phy->settings.cp_io_mask_start++; > + > + if (phy->t_sdclk == phy->t_sdmclk && phy->mode == UHS_SDR50) > + phy->settings.cp_io_mask_start++; > + > + phy->settings.sdhc_rw_compensate = rw_compensate; > +} > + > +static void sdhci_cdns_sd6_phy_calc_settings(struct sdhci_cdns_sd6_phy *phy) > +{ > + sdhci_cdns_sd6_phy_calc_cmd_out(phy); > + sdhci_cdns_sd6_phy_calc_cmd_in(phy); > + sdhci_cdns_sd6_phy_calc_dat_out(phy); > + sdhci_cdns_sd6_phy_calc_dat_in(phy); > + sdhci_cdns_sd6_phy_calc_io(phy); > +} > + > +static int sdhci_cdns_sd6_phy_update_timings(struct sdhci_cdns_plat *plat) > +{ > + struct sdhci_cdns_sd6_phy *phy = plat->priv; > + int t_sdmclk = phy->t_sdmclk; > + > + /* initialize input */ > + init_timings[phy->mode](&phy->t, phy->t_sdclk); > + > + /* Reset the following setting before the switch statement to make sure > + * they are correct if we change speeds like after a mmc rescan > + */ > + phy->tune_cmd = false; > + phy->ddr = false; > + phy->strobe_dat = false; > + phy->tune_dat = false; > + > + switch (phy->mode) { > + case UHS_SDR104: > + phy->tune_cmd = true; > + phy->tune_dat = true; > + break; > + case UHS_DDR50: > + phy->ddr = true; > + break; > + case MMC_DDR_52: > + phy->ddr = true; > + break; > + case MMC_HS_200: > + phy->tune_dat = true; > + phy->tune_cmd = true; > + break; > + case MMC_HS_400: > + case MMC_HS_400_ES: > + phy->tune_cmd = true; > + phy->ddr = true; > + phy->strobe_dat = true; > + break; > + } > + > + if (plat->enhanced_strobe) > + phy->strobe_cmd = true; > + > + phy->d.phy_sdclk_delay = 2 * t_sdmclk; > + phy->d.phy_cmd_o_delay = 2 * t_sdmclk + t_sdmclk / 2; > + phy->d.phy_dat_o_delay = 2 * t_sdmclk + t_sdmclk / 2; > + > + if (phy->t_sdclk == phy->t_sdmclk) { > + phy->settings.sdhc_extended_wr_mode = 0; > + phy->settings.sdhc_extended_rd_mode = 0; > + } else { > + phy->settings.sdhc_extended_wr_mode = 1; > + phy->settings.sdhc_extended_rd_mode = 1; > + } > + > + phy->settings.cp_gate_cfg_always_on = 1; > + > + sdhci_cdns_sd6_phy_configure_dll(phy); > + > + sdhci_cdns_sd6_phy_calc_settings(phy); > + > + return 0; > +} > + > +static void sdhci_cdns_sd6_set_clock(struct sdhci_host *host, unsigned int > div) > +{ > + struct udevice *dev = host->mmc->dev; > + struct sdhci_cdns_plat *plat = dev_get_plat(dev); > + struct sdhci_cdns_sd6_phy *phy = plat->priv; > + unsigned int clock; > + > + if (!div) > + div = 1; > + else > + div <<= 1; > + > + clock = DIV_ROUND_DOWN_ULL(SDHCI_CDNS_SD6_MAXCLK, div); > + > + sdhci_cdns_set_control_reg(host); > + > + phy->t_sdclk = DIV_ROUND_DOWN_ULL(1e12, clock); > + > + if (sdhci_cdns_sd6_phy_update_timings(plat)) > + debug("%s: update timings failed\n", __func__); > + else > + host->clock = clock; > + > + if (sdhci_cdns_sd6_phy_init(dev, plat)) > + debug("%s: phy init failed\n", __func__); > +} > + > +static int sdhci_cdns_sd6_plat_init(struct udevice *dev, struct > sdhci_cdns_plat *plat) > +{ > + struct sdhci_cdns_sd6_phy *phy = plat->priv; > + > + memset(phy, 0, sizeof(struct sdhci_cdns_sd6_phy)); > + > + sdhci_cdns_sd6_fullsw_reset(plat); > + sdhci_cdns_sd6_set_volt(plat); > + > + sdhci_cdns_sd6_get_fdt_params(dev, plat); > + > + phy->t_sdmclk = 5000; > + > + phy->d.delay_element_org = phy->d.delay_element; > + phy->settings.cp_sync_method = 1; > + phy->settings.cp_rd_del_sel = 52; > + phy->settings.cp_use_ext_lpbk_dqs = 1; > + phy->settings.cp_use_lpbk_dqs = 1; > + phy->settings.cp_data_select_oe_end = 1; > + phy->settings.cp_dll_start_point = 4; > + > + phy->settings.cp_use_phony_dqs = 1; > + phy->settings.cp_use_phony_dqs_cmd = 1; > + phy->settings.cp_dll_bypass_mode = 1; > + > + phy->settings.cp_read_dqs_cmd_delay = 0; > + phy->settings.cp_clk_wrdqs_delay = 0; > + phy->settings.cp_clk_wr_delay = 0; > + phy->settings.cp_read_dqs_delay = 0; > + phy->settings.cp_io_mask_end = 5; > + phy->settings.cp_io_mask_start = 0; > + phy->settings.sdhc_extended_rd_mode = 1; > + phy->settings.sdhc_extended_wr_mode = 1; > + phy->settings.sdhc_hcsdclkadj = 6; > + phy->settings.sdhc_rw_compensate = 10; > + phy->settings.sdhc_idelay_val = 0; > + > + sdhci_cdns_sd6_phy_update_timings(plat); > + return 0; > +} > + > static int __maybe_unused sdhci_cdns_execute_tuning(struct udevice *dev, > unsigned int opcode) > { > @@ -243,8 +1444,64 @@ static int __maybe_unused > sdhci_cdns_execute_tuning(struct udevice *dev, > return sdhci_cdns_set_tune_val(plat, end_of_streak - max_streak / 2); > } > > +#if CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT) > +static int sdhci_cdns_hs400_enhanced_strobe(struct udevice *dev) > +{ > + struct sdhci_cdns_plat *plat = dev_get_plat(dev); > + > + plat->enhanced_strobe = 1; > + sdhci_cdns_set_emmc_mode(plat, SDHCI_CDNS_HRS06_MODE_MMC_HS400ES); > + > + return 0; > +} > +#endif > + > +static int __maybe_unused sdhci_cdns_sd6_execute_tuning(struct mmc *mmc, > unsigned char opcode) > +{ > + struct udevice *dev = mmc->dev; > + struct sdhci_cdns_plat *plat = dev_get_plat(dev); > + int cur_streak = 0; > + int max_streak = 0; > + int end_of_streak = 0; > + int cnt = 0, midpoint, iter = 0; > + > + for (cnt = tune_val_start; iter < max_tune_iter; iter++, cnt += > tune_val_step) { > + if (sdhci_cdns_sd6_set_tune_val(plat, cnt) || > + mmc_send_tuning(mmc, opcode, NULL)) { /* bad */ > + cur_streak = 0; > + } else { /* good */ > + cur_streak++; > + if (cur_streak > max_streak) { > + max_streak = cur_streak; > + end_of_streak = cnt; > + } > + } > + } > + > + if (!max_streak) > + return -EIO; > + > + midpoint = end_of_streak - (((max_streak - 1) * tune_val_step) / 2); > + > + return sdhci_cdns_sd6_set_tune_val(plat, midpoint); > +} > + > static struct dm_mmc_ops sdhci_cdns_mmc_ops; > > +static const struct sdhci_ops sdhci_cdns_ops = { > + .set_control_reg = sdhci_cdns_set_control_reg, > + .write_l = sdhci_cdns_sd6_writel, > + .read_l = sdhci_cdns_sd6_readl, > + .write_w = sdhci_cdns_sd6_writew, > + .read_w = sdhci_cdns_sd6_readw, > + .write_b = sdhci_cdns_sd6_writeb, > + .read_b = sdhci_cdns_sd6_readb, > + .set_clock = sdhci_cdns_sd6_set_clock, > +#ifdef MMC_SUPPORTS_TUNING > + .platform_execute_tuning = sdhci_cdns_sd6_execute_tuning, > +#endif > +}; > + > static int sdhci_cdns_bind(struct udevice *dev) > { > struct sdhci_cdns_plat *plat = dev_get_plat(dev); > @@ -255,11 +1512,18 @@ static int sdhci_cdns_bind(struct udevice *dev) > static int sdhci_cdns_probe(struct udevice *dev) > { > DECLARE_GLOBAL_DATA_PTR; > + > + ofnode node = dev_ofnode(dev); > + > + if (ofnode_device_is_compatible(node, "cdns,sd6hc")) > + sd6_ctrl = 1; > + > struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); > struct sdhci_cdns_plat *plat = dev_get_plat(dev); > struct sdhci_host *host = dev_get_priv(dev); > fdt_addr_t base; > int ret; > + struct sdhci_cdns_sd6_phy *phy; > > base = dev_read_addr(dev); > if (base == FDT_ADDR_T_NONE) > @@ -269,25 +1533,43 @@ static int sdhci_cdns_probe(struct udevice *dev) > if (!plat->hrs_addr) > return -ENOMEM; > > + plat->priv = &sd6_phy_config; > + > host->name = dev->name; > host->ioaddr = plat->hrs_addr + SDHCI_CDNS_SRS_BASE; > host->ops = &sdhci_cdns_ops; > host->quirks |= SDHCI_QUIRK_WAIT_SEND_CMD; > sdhci_cdns_mmc_ops = sdhci_ops; > #ifdef MMC_SUPPORTS_TUNING > - sdhci_cdns_mmc_ops.execute_tuning = sdhci_cdns_execute_tuning; > + if (!sd6_ctrl) > + sdhci_cdns_mmc_ops.execute_tuning = sdhci_cdns_execute_tuning; > #endif > > ret = mmc_of_parse(dev, &plat->cfg); > if (ret) > return ret; > > - ret = sdhci_cdns_sd4_phy_init(plat, gd->fdt_blob, dev_of_offset(dev)); > - if (ret) > - return ret; > +#if CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT) > + sdhci_cdns_mmc_ops.set_enhanced_strobe = > sdhci_cdns_hs400_enhanced_strobe; > +#endif > + > + phy = plat->priv; > + > + if (sd6_ctrl) { > + sdhci_cdns_sd6_plat_init(dev, plat); > + > + ret = sdhci_cdns_sd6_phy_init(dev, plat); > + if (ret) > + return ret; > + } else { > + ret = sdhci_cdns_sd4_phy_init(plat, gd->fdt_blob, > dev_of_offset(dev)); > + if (ret) > + return ret; > + } > > host->mmc = &plat->mmc; > host->mmc->dev = dev; > + > ret = sdhci_setup_cfg(&plat->cfg, host, 0, 0); > if (ret) > return ret; > @@ -301,6 +1583,7 @@ static int sdhci_cdns_probe(struct udevice *dev) > static const struct udevice_id sdhci_cdns_match[] = { > { .compatible = "socionext,uniphier-sd4hc" }, > { .compatible = "cdns,sd4hc" }, > + { .compatible = "cdns,sd6hc" }, > { /* sentinel */ } > }; >