* [PATCH 0/3] mmc: sdhci-cadence: SD6 controller support
@ 2023-06-05 13:58 Piyush Malgujar
2023-06-05 13:58 ` [PATCH 1/3] mmc: sdhci-cadence: Rename functions to SD4 specific Piyush Malgujar
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Piyush Malgujar @ 2023-06-05 13:58 UTC (permalink / raw)
To: peng.fan, jh80.chung, u-boot; +Cc: jannadurai, cchavva, Piyush Malgujar
Added changes to support SD6 controller
- Renamed the function which will be SD4 specific
- Added SD6 controller support
- Added debug option
Dhananjay Kangude (3):
mmc: sdhci-cadence: Rename functions to SD4 specific
mmc: sdhci-cadence: SD6 Controller support
mmc: sdhci-cadence: Add debug option in sdhci-cadence driver
drivers/mmc/Kconfig | 9 +
drivers/mmc/sdhci-cadence.c | 1460 ++++++++++++++++++++++++++++++++++-
2 files changed, 1429 insertions(+), 40 deletions(-)
--
2.17.1
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 1/3] mmc: sdhci-cadence: Rename functions to SD4 specific
2023-06-05 13:58 [PATCH 0/3] mmc: sdhci-cadence: SD6 controller support Piyush Malgujar
@ 2023-06-05 13:58 ` Piyush Malgujar
2023-07-20 2:56 ` Jaehoon Chung
2023-06-05 13:58 ` [PATCH 2/3] mmc: sdhci-cadence: SD6 Controller support Piyush Malgujar
2023-06-05 13:58 ` [PATCH 3/3] mmc: sdhci-cadence: Add debug option in sdhci-cadence driver Piyush Malgujar
2 siblings, 1 reply; 6+ messages in thread
From: Piyush Malgujar @ 2023-06-05 13:58 UTC (permalink / raw)
To: peng.fan, jh80.chung, u-boot
Cc: jannadurai, cchavva, Dhananjay Kangude, Piyush Malgujar
From: Dhananjay Kangude <dkangude@cadence.com>
Renaming the functions and structures specific to SD4 so
that it can be separated from upcoming SD6 related
functionality.
Signed-off-by: Dhananjay Kangude <dkangude@cadence.com>
Co-developed-by: Jayanthi Annadurai <jannadurai@marvell.com>
Signed-off-by: Jayanthi Annadurai <jannadurai@marvell.com>
Signed-off-by: Piyush Malgujar <pmalgujar@marvell.com>
---
drivers/mmc/sdhci-cadence.c | 48 ++++++++++++++++++-------------------
1 file changed, 24 insertions(+), 24 deletions(-)
diff --git a/drivers/mmc/sdhci-cadence.c b/drivers/mmc/sdhci-cadence.c
index 327a05ad11d00fad415bd18b93d83f08e2b6ed5a..0bb258da63e442232310d9433b7b6882992bd45d 100644
--- a/drivers/mmc/sdhci-cadence.c
+++ b/drivers/mmc/sdhci-cadence.c
@@ -18,14 +18,14 @@
#include <mmc.h>
#include <sdhci.h>
-/* HRS - Host Register Set (specific to Cadence) */
+/* SD 4.0 Controller 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)
-#define SDHCI_CDNS_HRS04_RDATA GENMASK(23, 16)
-#define SDHCI_CDNS_HRS04_WDATA GENMASK(15, 8)
-#define SDHCI_CDNS_HRS04_ADDR GENMASK(5, 0)
+#define SDHCI_CDNS_SD4_HRS04_ACK BIT(26)
+#define SDHCI_CDNS_SD4_HRS04_RD BIT(25)
+#define SDHCI_CDNS_SD4_HRS04_WR BIT(24)
+#define SDHCI_CDNS_SD4_HRS04_RDATA GENMASK(23, 16)
+#define SDHCI_CDNS_SD4_HRS04_WDATA GENMASK(15, 8)
+#define SDHCI_CDNS_SD4_HRS04_ADDR GENMASK(5, 0)
#define SDHCI_CDNS_HRS06 0x18 /* eMMC control */
#define SDHCI_CDNS_HRS06_TUNE_UP BIT(15)
@@ -41,7 +41,7 @@
/* SRS - Slot Register Set (SDHCI-compatible) */
#define SDHCI_CDNS_SRS_BASE 0x200
-/* PHY */
+/* PHY registers for SD4 controller */
#define SDHCI_CDNS_PHY_DLY_SD_HS 0x00
#define SDHCI_CDNS_PHY_DLY_SD_DEFAULT 0x01
#define SDHCI_CDNS_PHY_DLY_UHS_SDR12 0x02
@@ -73,7 +73,7 @@ struct sdhci_cdns_phy_cfg {
u8 addr;
};
-static const struct sdhci_cdns_phy_cfg sdhci_cdns_phy_cfgs[] = {
+static const struct sdhci_cdns_phy_cfg sdhci_cdns_sd4_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, },
@@ -87,45 +87,45 @@ static const struct sdhci_cdns_phy_cfg sdhci_cdns_phy_cfgs[] = {
{ "cdns,phy-dll-delay-strobe", SDHCI_CDNS_PHY_DLY_STROBE, },
};
-static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_plat *plat,
- u8 addr, u8 data)
+static int sdhci_cdns_sd4_write_phy_reg(struct sdhci_cdns_plat *plat,
+ u8 addr, u8 data)
{
void __iomem *reg = plat->hrs_addr + SDHCI_CDNS_HRS04;
u32 tmp;
int ret;
- tmp = FIELD_PREP(SDHCI_CDNS_HRS04_WDATA, data) |
- FIELD_PREP(SDHCI_CDNS_HRS04_ADDR, addr);
+ tmp = FIELD_PREP(SDHCI_CDNS_SD4_HRS04_WDATA, data) |
+ FIELD_PREP(SDHCI_CDNS_SD4_HRS04_ADDR, addr);
writel(tmp, reg);
- tmp |= SDHCI_CDNS_HRS04_WR;
+ tmp |= SDHCI_CDNS_SD4_HRS04_WR;
writel(tmp, reg);
- ret = readl_poll_timeout(reg, tmp, tmp & SDHCI_CDNS_HRS04_ACK, 10);
+ ret = readl_poll_timeout(reg, tmp, tmp & SDHCI_CDNS_SD4_HRS04_ACK, 10);
if (ret)
return ret;
- tmp &= ~SDHCI_CDNS_HRS04_WR;
+ tmp &= ~SDHCI_CDNS_SD4_HRS04_WR;
writel(tmp, reg);
return 0;
}
-static int sdhci_cdns_phy_init(struct sdhci_cdns_plat *plat,
- const void *fdt, int nodeoffset)
+static int sdhci_cdns_sd4_phy_init(struct sdhci_cdns_plat *plat,
+ const void *fdt, int nodeoffset)
{
const fdt32_t *prop;
int ret, i;
- for (i = 0; i < ARRAY_SIZE(sdhci_cdns_phy_cfgs); i++) {
+ for (i = 0; i < ARRAY_SIZE(sdhci_cdns_sd4_phy_cfgs); i++) {
prop = fdt_getprop(fdt, nodeoffset,
- sdhci_cdns_phy_cfgs[i].property, NULL);
+ sdhci_cdns_sd4_phy_cfgs[i].property, NULL);
if (!prop)
continue;
- ret = sdhci_cdns_write_phy_reg(plat,
- sdhci_cdns_phy_cfgs[i].addr,
- fdt32_to_cpu(*prop));
+ ret = sdhci_cdns_sd4_write_phy_reg(plat,
+ sdhci_cdns_sd4_phy_cfgs[i].addr,
+ fdt32_to_cpu(*prop));
if (ret)
return ret;
}
@@ -282,7 +282,7 @@ static int sdhci_cdns_probe(struct udevice *dev)
if (ret)
return ret;
- ret = sdhci_cdns_phy_init(plat, gd->fdt_blob, dev_of_offset(dev));
+ ret = sdhci_cdns_sd4_phy_init(plat, gd->fdt_blob, dev_of_offset(dev));
if (ret)
return ret;
--
2.17.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 2/3] mmc: sdhci-cadence: SD6 Controller support
2023-06-05 13:58 [PATCH 0/3] mmc: sdhci-cadence: SD6 controller support Piyush Malgujar
2023-06-05 13:58 ` [PATCH 1/3] mmc: sdhci-cadence: Rename functions to SD4 specific Piyush Malgujar
@ 2023-06-05 13:58 ` Piyush Malgujar
2023-07-03 8:58 ` Jaehoon Chung
2023-06-05 13:58 ` [PATCH 3/3] mmc: sdhci-cadence: Add debug option in sdhci-cadence driver Piyush Malgujar
2 siblings, 1 reply; 6+ messages in thread
From: Piyush Malgujar @ 2023-06-05 13:58 UTC (permalink / raw)
To: peng.fan, jh80.chung, u-boot
Cc: jannadurai, cchavva, Dhananjay Kangude, Piyush Malgujar
From: Dhananjay Kangude <dkangude@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 <dkangude@cadence.com>
Co-developed-by: Jayanthi Annadurai <jannadurai@marvell.com>
Signed-off-by: Jayanthi Annadurai <jannadurai@marvell.com>
Signed-off-by: Piyush Malgujar <pmalgujar@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;
+
+ val = readl(host->ioaddr + regoff);
+ if ((reg & 0x3) == 0)
+ return (val & 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;
+
+ val = readl(host->ioaddr + regoff);
+ switch (reg & 3) {
+ 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;
+}
+
+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)
+{
+ 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);
+}
+
+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;
+ 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);
+
+ 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);
+ 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) {
+ 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;
+ /* 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);
+
+ 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;
+ } 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 {
+ 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)
+ 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;
+
+ 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*/
+ }
+ } 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;
+
+ 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 */ }
};
--
2.17.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 3/3] mmc: sdhci-cadence: Add debug option in sdhci-cadence driver
2023-06-05 13:58 [PATCH 0/3] mmc: sdhci-cadence: SD6 controller support Piyush Malgujar
2023-06-05 13:58 ` [PATCH 1/3] mmc: sdhci-cadence: Rename functions to SD4 specific Piyush Malgujar
2023-06-05 13:58 ` [PATCH 2/3] mmc: sdhci-cadence: SD6 Controller support Piyush Malgujar
@ 2023-06-05 13:58 ` Piyush Malgujar
2 siblings, 0 replies; 6+ messages in thread
From: Piyush Malgujar @ 2023-06-05 13:58 UTC (permalink / raw)
To: peng.fan, jh80.chung, u-boot
Cc: jannadurai, cchavva, Dhananjay Kangude, Piyush Malgujar
From: Dhananjay Kangude <dkangude@cadence.com>
Additional debug information is printed if MMC_SDHCI_CADENCE_DEBUG
is enabled.
Signed-off-by: Dhananjay Kangude <dkangude@cadence.com>
Co-developed-by: Jayanthi Annadurai <jannadurai@marvell.com>
Signed-off-by: Jayanthi Annadurai <jannadurai@marvell.com>
Signed-off-by: Piyush Malgujar <pmalgujar@marvell.com>
---
drivers/mmc/Kconfig | 8 +++
drivers/mmc/sdhci-cadence.c | 97 +++++++++++++++++++++++++++++++++++++
2 files changed, 105 insertions(+)
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index 6b724f1d1ed050b49e31a6cbe5f898b4b636c330..8177a83e492087f5e30844f0290a1ee85ae02511 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -574,6 +574,14 @@ config MMC_SDHCI_CADENCE
If unsure, say N.
+config MMC_SDHCI_CADENCE_DEBUG
+ bool "Debug support for the Cadence SD/SDIO/eMMC controller"
+ depends on MMC_SDHCI_CADENCE
+ default n
+ help
+ This enables additional debug messages from cadence SDHCI
+ driver.
+
config MMC_SDHCI_AM654
bool "SDHCI Controller on TI's Am654 devices"
depends on ARCH_K3
diff --git a/drivers/mmc/sdhci-cadence.c b/drivers/mmc/sdhci-cadence.c
index 1d8e8e177eeeb6464ba9ade5fd63c1cca4554bb6..90607d4d802d07be3b1c840143141931f10f9126 100644
--- a/drivers/mmc/sdhci-cadence.c
+++ b/drivers/mmc/sdhci-cadence.c
@@ -18,6 +18,11 @@
#include <linux/libfdt.h>
#include <mmc.h>
#include <sdhci.h>
+#ifdef CONFIG_MMC_SDHCI_CADENCE_DEBUG
+#define DEBUG_DRV(fmt, ...) \
+ if (1) \
+ printf(fmt, ##__VA_ARGS__)
+#endif
#define SDHCI_CDNS_SD6_MAXCLK 200000000
@@ -492,6 +497,72 @@ static u32 sdhci_cdns_sd6_read_phy_reg(struct sdhci_cdns_plat *plat,
return readl(plat->hrs_addr + SDHCI_CDNS_HRS05);
}
+#ifdef CONFIG_MMC_SDHCI_CADENCE_DEBUG
+void sdhci_cdns_sd6_dump(struct sdhci_cdns_plat *plat)
+{
+ int i;
+
+ for (i = 0; i < 14; i++)
+ DEBUG_DRV("HRS%d 0x%x\n", i, readl(plat->hrs_addr + (i * 4)));
+
+ for (i = 0; i < 27; i++)
+ DEBUG_DRV("SRS%d 0x%x\n", i, readl(plat->hrs_addr + 0x200 + (i * 4)));
+
+ DEBUG_DRV("SDHCI_CDNS_SD6_PHY_DLL_SLAVE 0x%x\n",
+ sdhci_cdns_sd6_read_phy_reg(plat, SDHCI_CDNS_SD6_PHY_DLL_SLAVE));
+ DEBUG_DRV("SDHCI_CDNS_SD6_PHY_CTRL 0x%x\n",
+ sdhci_cdns_sd6_read_phy_reg(plat, SDHCI_CDNS_SD6_PHY_CTRL));
+ DEBUG_DRV("SDHCI_CDNS_SD6_PHY_DLL_MASTER 0x%x\n",
+ sdhci_cdns_sd6_read_phy_reg(plat, SDHCI_CDNS_SD6_PHY_DLL_MASTER));
+ DEBUG_DRV("SDHCI_CDNS_SD6_PHY_GATE_LPBK 0x%x\n",
+ sdhci_cdns_sd6_read_phy_reg(plat, SDHCI_CDNS_SD6_PHY_GATE_LPBK));
+ DEBUG_DRV("SDHCI_CDNS_SD6_PHY_DQS_TIMING 0x%x\n",
+ sdhci_cdns_sd6_read_phy_reg(plat, SDHCI_CDNS_SD6_PHY_DQS_TIMING));
+}
+
+static void sdhci_cdns_sd6_phy_dump(struct sdhci_cdns_sd6_phy *phy)
+{
+ DEBUG_DRV("sdhci_cdns_sd6_phy_init mode %d t_sdclk %d\n", phy->mode, phy->t_sdclk);
+ DEBUG_DRV("cp_clk_wr_delay %d\n", phy->settings.cp_clk_wr_delay);
+ DEBUG_DRV("cp_clk_wrdqs_delay %d\n", phy->settings.cp_clk_wrdqs_delay);
+ DEBUG_DRV("cp_data_select_oe_end %d\n", phy->settings.cp_data_select_oe_end);
+ DEBUG_DRV("cp_dll_bypass_mode %d\n", phy->settings.cp_dll_bypass_mode);
+ DEBUG_DRV("cp_dll_locked_mode %d\n", phy->settings.cp_dll_locked_mode);
+ DEBUG_DRV("cp_dll_start_point %d\n", phy->settings.cp_dll_start_point);
+ DEBUG_DRV("cp_io_mask_always_on %d\n", phy->settings.cp_io_mask_always_on);
+ DEBUG_DRV("cp_io_mask_end %d\n", phy->settings.cp_io_mask_end);
+ DEBUG_DRV("cp_io_mask_start %d\n", phy->settings.cp_io_mask_start);
+ DEBUG_DRV("cp_rd_del_sel %d\n", phy->settings.cp_rd_del_sel);
+ DEBUG_DRV("cp_read_dqs_cmd_delay %d\n", phy->settings.cp_read_dqs_cmd_delay);
+ DEBUG_DRV("cp_read_dqs_delay %d\n", phy->settings.cp_read_dqs_delay);
+ DEBUG_DRV("cp_sw_half_cycle_shift %d\n", phy->settings.cp_sw_half_cycle_shift);
+ DEBUG_DRV("cp_sync_method %d\n", phy->settings.cp_sync_method);
+ DEBUG_DRV("cp_use_ext_lpbk_dqs %d\n", phy->settings.cp_use_ext_lpbk_dqs);
+ DEBUG_DRV("cp_use_lpbk_dqs %d\n", phy->settings.cp_use_lpbk_dqs);
+ DEBUG_DRV("cp_use_phony_dqs %d\n", phy->settings.cp_use_phony_dqs);
+ DEBUG_DRV("cp_use_phony_dqs_cmd %d\n", phy->settings.cp_use_phony_dqs_cmd);
+ DEBUG_DRV("sdhc_extended_rd_mode %d\n", phy->settings.sdhc_extended_rd_mode);
+ DEBUG_DRV("sdhc_extended_wr_mode %d\n", phy->settings.sdhc_extended_wr_mode);
+ DEBUG_DRV("sdhc_hcsdclkadj %d\n", phy->settings.sdhc_hcsdclkadj);
+ DEBUG_DRV("sdhc_idelay_val %d\n", phy->settings.sdhc_idelay_val);
+ DEBUG_DRV("sdhc_rdcmd_en %d\n", phy->settings.sdhc_rdcmd_en);
+ DEBUG_DRV("sdhc_rddata_en %d\n", phy->settings.sdhc_rddata_en);
+ DEBUG_DRV("sdhc_rw_compensate %d\n", phy->settings.sdhc_rw_compensate);
+ DEBUG_DRV("sdhc_sdcfsh %d\n", phy->settings.sdhc_sdcfsh);
+ DEBUG_DRV("sdhc_sdcfsl %d\n", phy->settings.sdhc_sdcfsl);
+ DEBUG_DRV("sdhc_wrcmd0_dly %d %d\n", phy->settings.sdhc_wrcmd0_dly,
+ phy->settings.sdhc_wrcmd0_sdclk_dly);
+ DEBUG_DRV("sdhc_wrcmd1_dly %d %d\n", phy->settings.sdhc_wrcmd1_dly,
+ phy->settings.sdhc_wrcmd1_sdclk_dly);
+ DEBUG_DRV("sdhc_wrdata0_dly %d %d\n", phy->settings.sdhc_wrdata0_dly,
+ phy->settings.sdhc_wrdata0_sdclk_dly);
+
+ DEBUG_DRV("sdhc_wrdata1_dly %d %d\n", phy->settings.sdhc_wrdata1_dly,
+ phy->settings.sdhc_wrdata1_sdclk_dly);
+ DEBUG_DRV("hs200_tune_val %d\n", phy->settings.hs200_tune_val);
+}
+#endif
+
static void sdhci_cdns_sd6_writel(struct sdhci_host *host, u32 val, int reg)
{
writel(val, host->ioaddr + reg);
@@ -579,6 +650,16 @@ static void sdhci_cdns_set_emmc_mode(struct sdhci_cdns_plat *priv, u32 mode)
}
#endif
+#ifdef CONFIG_MMC_SDHCI_CADENCE_DEBUG
+static u32 sdhci_cdns_get_emmc_mode(struct sdhci_cdns_plat *priv)
+{
+ u32 tmp;
+
+ tmp = readl(priv->hrs_addr + SDHCI_CDNS_HRS06);
+ return FIELD_GET(SDHCI_CDNS_HRS06_MODE, tmp);
+}
+#endif
+
void sdhci_cdns_sd6_fullsw_reset(struct sdhci_cdns_plat *plat)
{
u32 regval;
@@ -1334,6 +1415,18 @@ static int sdhci_cdns_sd6_phy_update_timings(struct sdhci_cdns_plat *plat)
return 0;
}
+#ifdef CONFIG_MMC_SDHCI_CADENCE_DEBUG
+void dump_sdhci_regs(struct sdhci_host *host)
+{
+ struct udevice *dev = host->mmc->dev;
+ struct sdhci_cdns_plat *plat = dev_get_plat(dev);
+ struct sdhci_cdns_sd6_phy *phy = plat->priv;
+
+ sdhci_cdns_sd6_phy_dump(phy);
+ sdhci_cdns_sd6_dump(plat);
+}
+#endif
+
static void sdhci_cdns_sd6_set_clock(struct sdhci_host *host, unsigned int div)
{
struct udevice *dev = host->mmc->dev;
@@ -1359,6 +1452,10 @@ static void sdhci_cdns_sd6_set_clock(struct sdhci_host *host, unsigned int div)
if (sdhci_cdns_sd6_phy_init(dev, plat))
debug("%s: phy init failed\n", __func__);
+#ifdef CONFIG_MMC_SDHCI_CADENCE_DEBUG
+ dump_sdhci_regs(host);
+ DEBUG_DRV("%s sdhci mode %d\n", __func__, sdhci_cdns_get_emmc_mode(plat));
+#endif
}
static int sdhci_cdns_sd6_plat_init(struct udevice *dev, struct sdhci_cdns_plat *plat)
--
2.17.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH 2/3] mmc: sdhci-cadence: SD6 Controller support
2023-06-05 13:58 ` [PATCH 2/3] mmc: sdhci-cadence: SD6 Controller support Piyush Malgujar
@ 2023-07-03 8:58 ` Jaehoon Chung
0 siblings, 0 replies; 6+ messages in thread
From: Jaehoon Chung @ 2023-07-03 8:58 UTC (permalink / raw)
To: Piyush Malgujar, peng.fan, u-boot
Cc: jannadurai, cchavva, Dhananjay Kangude, CPGS
Hi,
On 6/5/23 22:58, Piyush Malgujar wrote:
> From: Dhananjay Kangude <dkangude@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 <dkangude@cadence.com>
> Co-developed-by: Jayanthi Annadurai <jannadurai@marvell.com>
> Signed-off-by: Jayanthi Annadurai <jannadurai@marvell.com>
> Signed-off-by: Piyush Malgujar <pmalgujar@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 */ }
> };
>
^ permalink raw reply [flat|nested] 6+ messages in thread
* RE: [PATCH 1/3] mmc: sdhci-cadence: Rename functions to SD4 specific
2023-06-05 13:58 ` [PATCH 1/3] mmc: sdhci-cadence: Rename functions to SD4 specific Piyush Malgujar
@ 2023-07-20 2:56 ` Jaehoon Chung
0 siblings, 0 replies; 6+ messages in thread
From: Jaehoon Chung @ 2023-07-20 2:56 UTC (permalink / raw)
To: 'Piyush Malgujar', peng.fan, u-boot
Cc: jannadurai, cchavva, 'Dhananjay Kangude'
> -----Original Message-----
> From: Piyush Malgujar <pmalgujar@marvell.com>
> Sent: Monday, June 5, 2023 10:58 PM
> To: peng.fan@nxp.com; jh80.chung@samsung.com; u-boot@lists.denx.de
> Cc: jannadurai@marvell.com; cchavva@marvell.com; Dhananjay Kangude <dkangude@cadence.com>; Piyush
> Malgujar <pmalgujar@marvell.com>
> Subject: [PATCH 1/3] mmc: sdhci-cadence: Rename functions to SD4 specific
>
> From: Dhananjay Kangude <dkangude@cadence.com>
>
> Renaming the functions and structures specific to SD4 so
> that it can be separated from upcoming SD6 related
> functionality.
>
> Signed-off-by: Dhananjay Kangude <dkangude@cadence.com>
> Co-developed-by: Jayanthi Annadurai <jannadurai@marvell.com>
> Signed-off-by: Jayanthi Annadurai <jannadurai@marvell.com>
> Signed-off-by: Piyush Malgujar <pmalgujar@marvell.com>
Reviewed-by: Jaehoon Chung <jh80.chung@samsung.com>
Best Regards,
Jaehoon Chung
> ---
> drivers/mmc/sdhci-cadence.c | 48 ++++++++++++++++++-------------------
> 1 file changed, 24 insertions(+), 24 deletions(-)
>
> diff --git a/drivers/mmc/sdhci-cadence.c b/drivers/mmc/sdhci-cadence.c
> index 327a05ad11d00fad415bd18b93d83f08e2b6ed5a..0bb258da63e442232310d9433b7b6882992bd45d 100644
> --- a/drivers/mmc/sdhci-cadence.c
> +++ b/drivers/mmc/sdhci-cadence.c
> @@ -18,14 +18,14 @@
> #include <mmc.h>
> #include <sdhci.h>
>
> -/* HRS - Host Register Set (specific to Cadence) */
> +/* SD 4.0 Controller 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)
> -#define SDHCI_CDNS_HRS04_RDATA GENMASK(23, 16)
> -#define SDHCI_CDNS_HRS04_WDATA GENMASK(15, 8)
> -#define SDHCI_CDNS_HRS04_ADDR GENMASK(5, 0)
> +#define SDHCI_CDNS_SD4_HRS04_ACK BIT(26)
> +#define SDHCI_CDNS_SD4_HRS04_RD BIT(25)
> +#define SDHCI_CDNS_SD4_HRS04_WR BIT(24)
> +#define SDHCI_CDNS_SD4_HRS04_RDATA GENMASK(23, 16)
> +#define SDHCI_CDNS_SD4_HRS04_WDATA GENMASK(15, 8)
> +#define SDHCI_CDNS_SD4_HRS04_ADDR GENMASK(5, 0)
>
> #define SDHCI_CDNS_HRS06 0x18 /* eMMC control */
> #define SDHCI_CDNS_HRS06_TUNE_UP BIT(15)
> @@ -41,7 +41,7 @@
> /* SRS - Slot Register Set (SDHCI-compatible) */
> #define SDHCI_CDNS_SRS_BASE 0x200
>
> -/* PHY */
> +/* PHY registers for SD4 controller */
> #define SDHCI_CDNS_PHY_DLY_SD_HS 0x00
> #define SDHCI_CDNS_PHY_DLY_SD_DEFAULT 0x01
> #define SDHCI_CDNS_PHY_DLY_UHS_SDR12 0x02
> @@ -73,7 +73,7 @@ struct sdhci_cdns_phy_cfg {
> u8 addr;
> };
>
> -static const struct sdhci_cdns_phy_cfg sdhci_cdns_phy_cfgs[] = {
> +static const struct sdhci_cdns_phy_cfg sdhci_cdns_sd4_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, },
> @@ -87,45 +87,45 @@ static const struct sdhci_cdns_phy_cfg sdhci_cdns_phy_cfgs[] = {
> { "cdns,phy-dll-delay-strobe", SDHCI_CDNS_PHY_DLY_STROBE, },
> };
>
> -static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_plat *plat,
> - u8 addr, u8 data)
> +static int sdhci_cdns_sd4_write_phy_reg(struct sdhci_cdns_plat *plat,
> + u8 addr, u8 data)
> {
> void __iomem *reg = plat->hrs_addr + SDHCI_CDNS_HRS04;
> u32 tmp;
> int ret;
>
> - tmp = FIELD_PREP(SDHCI_CDNS_HRS04_WDATA, data) |
> - FIELD_PREP(SDHCI_CDNS_HRS04_ADDR, addr);
> + tmp = FIELD_PREP(SDHCI_CDNS_SD4_HRS04_WDATA, data) |
> + FIELD_PREP(SDHCI_CDNS_SD4_HRS04_ADDR, addr);
> writel(tmp, reg);
>
> - tmp |= SDHCI_CDNS_HRS04_WR;
> + tmp |= SDHCI_CDNS_SD4_HRS04_WR;
> writel(tmp, reg);
>
> - ret = readl_poll_timeout(reg, tmp, tmp & SDHCI_CDNS_HRS04_ACK, 10);
> + ret = readl_poll_timeout(reg, tmp, tmp & SDHCI_CDNS_SD4_HRS04_ACK, 10);
> if (ret)
> return ret;
>
> - tmp &= ~SDHCI_CDNS_HRS04_WR;
> + tmp &= ~SDHCI_CDNS_SD4_HRS04_WR;
> writel(tmp, reg);
>
> return 0;
> }
>
> -static int sdhci_cdns_phy_init(struct sdhci_cdns_plat *plat,
> - const void *fdt, int nodeoffset)
> +static int sdhci_cdns_sd4_phy_init(struct sdhci_cdns_plat *plat,
> + const void *fdt, int nodeoffset)
> {
> const fdt32_t *prop;
> int ret, i;
>
> - for (i = 0; i < ARRAY_SIZE(sdhci_cdns_phy_cfgs); i++) {
> + for (i = 0; i < ARRAY_SIZE(sdhci_cdns_sd4_phy_cfgs); i++) {
> prop = fdt_getprop(fdt, nodeoffset,
> - sdhci_cdns_phy_cfgs[i].property, NULL);
> + sdhci_cdns_sd4_phy_cfgs[i].property, NULL);
> if (!prop)
> continue;
>
> - ret = sdhci_cdns_write_phy_reg(plat,
> - sdhci_cdns_phy_cfgs[i].addr,
> - fdt32_to_cpu(*prop));
> + ret = sdhci_cdns_sd4_write_phy_reg(plat,
> + sdhci_cdns_sd4_phy_cfgs[i].addr,
> + fdt32_to_cpu(*prop));
> if (ret)
> return ret;
> }
> @@ -282,7 +282,7 @@ static int sdhci_cdns_probe(struct udevice *dev)
> if (ret)
> return ret;
>
> - ret = sdhci_cdns_phy_init(plat, gd->fdt_blob, dev_of_offset(dev));
> + ret = sdhci_cdns_sd4_phy_init(plat, gd->fdt_blob, dev_of_offset(dev));
> if (ret)
> return ret;
>
> --
> 2.17.1
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2023-07-20 2:56 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-06-05 13:58 [PATCH 0/3] mmc: sdhci-cadence: SD6 controller support Piyush Malgujar
2023-06-05 13:58 ` [PATCH 1/3] mmc: sdhci-cadence: Rename functions to SD4 specific Piyush Malgujar
2023-07-20 2:56 ` Jaehoon Chung
2023-06-05 13:58 ` [PATCH 2/3] mmc: sdhci-cadence: SD6 Controller support Piyush Malgujar
2023-07-03 8:58 ` Jaehoon Chung
2023-06-05 13:58 ` [PATCH 3/3] mmc: sdhci-cadence: Add debug option in sdhci-cadence driver Piyush Malgujar
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox