[PATCH 2/8] mmc: k1: add sdhci platform driver

Peng Fan peng.fan at oss.nxp.com
Wed Jun 24 10:15:57 CEST 2026


On Fri, Jun 12, 2026 at 04:18:55PM -0400, Raymond Mao wrote:
>From: Guodong Xu <guodong at riscstar.com>
>
>Add SDHCI platform driver support for SpacemiT K1 SoC. This driver
>implements the necessary platform-specific operations for the SDHCI
>controller, enabling MMC/SD card functionality on K1-based platforms.
>
>Signed-off-by: Guodong Xu <guodong at riscstar.com>
>Signed-off-by: Raymond Mao <raymond.mao at riscstar.com>
>---
>+
>+#define LOG_CATEGORY UCLASS_MMC
>+
>+#include <clk.h>
>+#include <dm.h>
>+#include <dm/pinctrl.h>
>+#include <fdtdec.h>
>+#include <linux/libfdt.h>
>+#include <linux/delay.h>
>+#include <log.h>
>+#include <malloc.h>
>+#include <sdhci.h>
>+#include <reset-uclass.h>
>+#include <power/regulator.h>
>+#include <mapmem.h>

See doc/develop/codingstyle.rst regarding "Include files"

>+
>+/* SDH register definitions */
>+#define SDHC_OP_EXT_REG			0x108
>+#define OVRRD_CLK_OEN			0x0800
>+#define FORCE_CLK_ON			0x1000
>+
>+#define SDHC_LEGACY_CTRL_REG		0x10C

Not used.

>+#define GEN_PAD_CLK_ON			0x0040
>+
>+#define SDHC_MMC_CTRL_REG		0x114
>+#define MISC_INT_EN			0x0002
>+#define MISC_INT			0x0004
>+#define ENHANCE_STROBE_EN		0x0100
>+#define MMC_HS400			0x0200
>+#define MMC_HS200			0x0400
>+#define MMC_CARD_MODE			0x1000

Use BIT(x) and GENMASK for register bits, and other places.

>+
>+#define SDHC_RX_CFG_REG			0x118
>+#define RX_SDCLK_SEL0_MASK		0x03
>+#define RX_SDCLK_SEL0_SHIFT		0x00
>+#define RX_SDCLK_SEL0			0x02
>+#define RX_SDCLK_SEL1_MASK		0x03
>+#define RX_SDCLK_SEL1_SHIFT		0x02
>+#define RX_SDCLK_SEL1			0x01
>+
>+#define SDHC_TX_CFG_REG			0x11C
>+#define TX_INT_CLK_SEL			0x40000000
>+#define TX_MUX_SEL			0x80000000
>+
>+#define SDHC_DLINE_CTRL_REG		0x130
>+#define DLINE_PU			0x01
>+#define RX_DLINE_CODE_MASK		0xFF
>+#define RX_DLINE_CODE_SHIFT		0x10
>+#define TX_DLINE_CODE_MASK		0xFF
>+#define TX_DLINE_CODE_SHIFT		0x18
>+
>+#define SDHC_DLINE_CFG_REG		0x134
>+#define RX_DLINE_REG_MASK		0xFF
>+#define RX_DLINE_REG_SHIFT		0x00
>+#define RX_DLINE_GAIN_MASK		0x1
>+#define RX_DLINE_GAIN_SHIFT		0x8
>+#define RX_DLINE_GAIN			0x1
>+#define TX_DLINE_REG_MASK		0xFF
>+#define TX_DLINE_REG_SHIFT		0x10
>+
>+#define SDHC_PHY_CTRL_REG		0x160
>+#define PHY_FUNC_EN			0x0001
>+#define PHY_PLL_LOCK			0x0002
>+#define HOST_LEGACY_MODE		0x80000000
>+
>+#define SDHC_PHY_FUNC_REG		0x164
>+#define PHY_TEST_EN			0x0080
>+#define HS200_USE_RFIFO			0x8000
>+
>+#define SDHC_PHY_DLLCFG			0x168
>+#define DLL_PREDLY_NUM			0x04
>+#define DLL_FULLDLY_RANGE		0x10
>+#define DLL_VREG_CTRL			0x40
>+#define DLL_ENABLE			0x80000000
>+#define DLL_REFRESH_SWEN_SHIFT		0x1C
>+#define DLL_REFRESH_SW_SHIFT		0x1D
>+
>+#define SDHC_PHY_DLLCFG1		0x16C
>+#define DLL_REG2_CTRL			0x0C
>+#define DLL_REG3_CTRL_MASK		0xFF
>+#define DLL_REG3_CTRL_SHIFT		0x10
>+#define DLL_REG2_CTRL_MASK		0xFF
>+#define DLL_REG2_CTRL_SHIFT		0x08
>+#define DLL_REG1_CTRL			0x92
>+#define DLL_REG1_CTRL_MASK		0xFF
>+#define DLL_REG1_CTRL_SHIFT		0x00
>+
>+#define SDHC_PHY_DLLSTS			0x170
>+#define DLL_LOCK_STATE			0x01
>+
>+#define SDHC_PHY_DLLSTS1		0x174
>+#define DLL_MASTER_DELAY_MASK		0xFF
>+#define DLL_MASTER_DELAY_SHIFT		0x10
>+
>+#define SDHC_PHY_PADCFG_REG		0x178
>+#define RX_BIAS_CTRL_SHIFT		0x5
>+#define PHY_DRIVE_SEL_SHIFT		0x0
>+#define PHY_DRIVE_SEL_MASK		0x7
>+#define PHY_DRIVE_SEL_DEFAULT		0x4
>+
>+#define MMC1_IO_V18EN			0x04
>+#define AKEY_ASFAR			0xBABA
>+#define AKEY_ASSAR			0xEB10
>+
>+#define SDHC_RX_TUNE_DELAY_MIN		0x0
>+#define SDHC_RX_TUNE_DELAY_MAX		0xFF
>+#define SDHC_RX_TUNE_DELAY_STEP		0x1
>+
>+#define CANDIDATE_WIN_NUM		3
>+#define SELECT_DELAY_NUM		9
>+#define WINDOW_1ST			0
>+#define WINDOW_2ND			1
>+#define WINDOW_3RD			2
>+
>+#define RX_TUNING_WINDOW_THRESHOLD	80
>+#define RX_TUNING_DLINE_REG		0x09
>+#define TX_TUNING_DLINE_REG		0x00
>+#define TX_TUNING_DELAYCODE		127
...
>+
>+static int spacemit_sdhci_send_tuning_cmd(struct sdhci_host *host, u32 opcode)
>+{
>+	int err = 0;
>+
>+	err = spacemit_sdhci_send_tuning(host, opcode, NULL);
>+	if (err) {
>+		log_warning("%s: send tuning err:%d\n", host->name, err);
>+		return err;
>+	}
>+
>+	err = spacemit_sdhci_tuning_pattern_check(host);
>+	return err;

return spacemit_sdhci_tuning_pattern_check(host);

>+}
>+
>+static void spacemit_sdhci_clear_tuned_clk(struct sdhci_host *host)
>+{
>+	u16 ctrl;
>+
>+	ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
>+	ctrl &= ~(SDHCI_CTRL_TUNED_CLK | SDHCI_CTRL_EXEC_TUNING);
>+	sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
>+}
>+
...
>+
>+
>+	/* Enable DLL */
>+	reg = sdhci_readl(host, SDHC_PHY_DLLCFG);
>+	reg |= DLL_ENABLE;
>+	sdhci_writel(host, reg, SDHC_PHY_DLLCFG);
>+
>+	/* Wait for DLL lock */
>+	i = 0;
>+	while (i++ < 100) {
>+		if (sdhci_readl(host, SDHC_PHY_DLLSTS) & DLL_LOCK_STATE)
>+			break;
>+		udelay(10);
>+	}

Use readx_poll_sleep_timeout?

>+	if (i == 100) {
>+		log_err("%s: phy dll lock timeout\n", host->name);
>+		return -ETIMEDOUT;
>+	}
>+
>+	return 0;
>+}
>+
>+static int spacemit_sdhci_hs400_enhanced_strobe(struct sdhci_host *host)
>+{
>+	u32 reg;
>+
>+	reg = sdhci_readl(host, SDHC_MMC_CTRL_REG);
>+	reg |= ENHANCE_STROBE_EN;
>+	sdhci_writel(host, reg, SDHC_MMC_CTRL_REG);
>+
>+	return spacemit_sdhci_phy_dll_init(host);
>+}
>+#endif
>+
>+const struct sdhci_ops spacemit_sdhci_ops = {
>+	.set_control_reg		= spacemit_sdhci_set_control_reg,
>+	.platform_execute_tuning	= spacemit_sdhci_execute_tuning,
>+#if CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT)
>+	.set_enhanced_strobe		= spacemit_sdhci_hs400_enhanced_strobe,
>+#endif
>+};
>+
>+static struct dm_mmc_ops spacemit_mmc_ops;
>+
>+static int spacemit_sdhci_probe(struct udevice *dev)
>+{
>+	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
>+	struct spacemit_sdhci_priv *priv = dev_get_priv(dev);
>+	struct spacemit_sdhci_plat *plat = dev_get_plat(dev);
>+	struct sdhci_host *host = &priv->host;
>+	struct clk clk;
>+	int ret = 0;
>+
>+	host->mmc = &plat->mmc;
>+	host->mmc->priv = host;
>+	host->mmc->dev = dev;
>+	upriv->mmc = host->mmc;
>+
>+	spacemit_mmc_ops = sdhci_ops;
>+	spacemit_mmc_ops.wait_dat0 = spacemit_sdhci_wait_dat0;
>+
>+	ret = clk_get_bulk(dev, &plat->clks);
>+	if (ret) {
>+		log_err("Can't get clk: %d\n", ret);
>+		return ret;
>+	}
>+
>+	ret = clk_enable_bulk(&plat->clks);
>+	if (ret) {
>+		log_err("Failed to enable clk: %d\n", ret);
>+		return ret;
>+	}
>+
>+	ret = reset_get_bulk(dev, &plat->resets);
>+	if (ret) {
>+		log_err("Can't get reset: %d\n", ret);
>+		return ret;
>+	}
>+
>+	ret = reset_deassert_bulk(&plat->resets);
>+	if (ret) {
>+		log_err("Failed to reset: %d\n", ret);
>+		return ret;
>+	}
>+
>+	ret = clk_get_by_index(dev, 1, &clk);
>+	if (ret) {
>+		log_err("Can't get io clk: %d\n", ret);
>+		return ret;
>+	}
>+
>+	ret = clk_set_rate(&clk, plat->cfg.f_max);
>+	if (ret) {
>+		log_err("Failed to set io clk: %d\n", ret);
>+		return ret;
>+	}
>+
>+	/* Set quirks */
>+	if (IS_ENABLED(CONFIG_SPL_BUILD))

CONFIG_XPL_BUILD

>+		host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD;
>+	else
>+		host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD |
>+				SDHCI_QUIRK_32BIT_DMA_ADDR;

Regards
Peng


More information about the U-Boot mailing list