162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// Secure Digital Host Controller
462306a36Sopenharmony_ci//
562306a36Sopenharmony_ci// Copyright (C) 2018 Spreadtrum, Inc.
662306a36Sopenharmony_ci// Author: Chunyan Zhang <chunyan.zhang@unisoc.com>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/delay.h>
962306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1062306a36Sopenharmony_ci#include <linux/highmem.h>
1162306a36Sopenharmony_ci#include <linux/iopoll.h>
1262306a36Sopenharmony_ci#include <linux/mmc/host.h>
1362306a36Sopenharmony_ci#include <linux/mmc/mmc.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/of.h>
1662306a36Sopenharmony_ci#include <linux/of_gpio.h>
1762306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h>
1862306a36Sopenharmony_ci#include <linux/platform_device.h>
1962306a36Sopenharmony_ci#include <linux/pm_runtime.h>
2062306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
2162306a36Sopenharmony_ci#include <linux/slab.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include "sdhci-pltfm.h"
2462306a36Sopenharmony_ci#include "mmc_hsq.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/* SDHCI_ARGUMENT2 register high 16bit */
2762306a36Sopenharmony_ci#define SDHCI_SPRD_ARG2_STUFF		GENMASK(31, 16)
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define SDHCI_SPRD_REG_32_DLL_CFG	0x200
3062306a36Sopenharmony_ci#define  SDHCI_SPRD_DLL_ALL_CPST_EN	(BIT(18) | BIT(24) | BIT(25) | BIT(26) | BIT(27))
3162306a36Sopenharmony_ci#define  SDHCI_SPRD_DLL_EN		BIT(21)
3262306a36Sopenharmony_ci#define  SDHCI_SPRD_DLL_SEARCH_MODE	BIT(16)
3362306a36Sopenharmony_ci#define  SDHCI_SPRD_DLL_INIT_COUNT	0xc00
3462306a36Sopenharmony_ci#define  SDHCI_SPRD_DLL_PHASE_INTERNAL	0x3
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define SDHCI_SPRD_REG_32_DLL_DLY	0x204
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define SDHCI_SPRD_REG_32_DLL_DLY_OFFSET	0x208
3962306a36Sopenharmony_ci#define  SDHCIBSPRD_IT_WR_DLY_INV		BIT(5)
4062306a36Sopenharmony_ci#define  SDHCI_SPRD_BIT_CMD_DLY_INV		BIT(13)
4162306a36Sopenharmony_ci#define  SDHCI_SPRD_BIT_POSRD_DLY_INV		BIT(21)
4262306a36Sopenharmony_ci#define  SDHCI_SPRD_BIT_NEGRD_DLY_INV		BIT(29)
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#define SDHCI_SPRD_REG_32_DLL_STS0	0x210
4562306a36Sopenharmony_ci#define SDHCI_SPRD_DLL_LOCKED		BIT(18)
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define SDHCI_SPRD_REG_32_BUSY_POSI		0x250
4862306a36Sopenharmony_ci#define  SDHCI_SPRD_BIT_OUTR_CLK_AUTO_EN	BIT(25)
4962306a36Sopenharmony_ci#define  SDHCI_SPRD_BIT_INNR_CLK_AUTO_EN	BIT(24)
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#define SDHCI_SPRD_REG_DEBOUNCE		0x28C
5262306a36Sopenharmony_ci#define  SDHCI_SPRD_BIT_DLL_BAK		BIT(0)
5362306a36Sopenharmony_ci#define  SDHCI_SPRD_BIT_DLL_VAL		BIT(1)
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#define  SDHCI_SPRD_INT_SIGNAL_MASK	0x1B7F410B
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci/* SDHCI_HOST_CONTROL2 */
5862306a36Sopenharmony_ci#define  SDHCI_SPRD_CTRL_HS200		0x0005
5962306a36Sopenharmony_ci#define  SDHCI_SPRD_CTRL_HS400		0x0006
6062306a36Sopenharmony_ci#define  SDHCI_SPRD_CTRL_HS400ES	0x0007
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/*
6362306a36Sopenharmony_ci * According to the standard specification, BIT(3) of SDHCI_SOFTWARE_RESET is
6462306a36Sopenharmony_ci * reserved, and only used on Spreadtrum's design, the hardware cannot work
6562306a36Sopenharmony_ci * if this bit is cleared.
6662306a36Sopenharmony_ci * 1 : normal work
6762306a36Sopenharmony_ci * 0 : hardware reset
6862306a36Sopenharmony_ci */
6962306a36Sopenharmony_ci#define  SDHCI_HW_RESET_CARD		BIT(3)
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci#define SDHCI_SPRD_MAX_CUR		0xFFFFFF
7262306a36Sopenharmony_ci#define SDHCI_SPRD_CLK_MAX_DIV		1023
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci#define SDHCI_SPRD_CLK_DEF_RATE		26000000
7562306a36Sopenharmony_ci#define SDHCI_SPRD_PHY_DLL_CLK		52000000
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci#define SDHCI_SPRD_MAX_RANGE		0xff
7862306a36Sopenharmony_ci#define SDHCI_SPRD_CMD_DLY_MASK		GENMASK(15, 8)
7962306a36Sopenharmony_ci#define SDHCI_SPRD_POSRD_DLY_MASK	GENMASK(23, 16)
8062306a36Sopenharmony_ci#define SDHCI_SPRD_CPST_EN		GENMASK(27, 24)
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistruct sdhci_sprd_host {
8362306a36Sopenharmony_ci	u32 version;
8462306a36Sopenharmony_ci	struct clk *clk_sdio;
8562306a36Sopenharmony_ci	struct clk *clk_enable;
8662306a36Sopenharmony_ci	struct clk *clk_2x_enable;
8762306a36Sopenharmony_ci	struct pinctrl *pinctrl;
8862306a36Sopenharmony_ci	struct pinctrl_state *pins_uhs;
8962306a36Sopenharmony_ci	struct pinctrl_state *pins_default;
9062306a36Sopenharmony_ci	u32 base_rate;
9162306a36Sopenharmony_ci	int flags; /* backup of host attribute */
9262306a36Sopenharmony_ci	u32 phy_delay[MMC_TIMING_MMC_HS400 + 2];
9362306a36Sopenharmony_ci};
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cienum sdhci_sprd_tuning_type {
9662306a36Sopenharmony_ci	SDHCI_SPRD_TUNING_SD_HS_CMD,
9762306a36Sopenharmony_ci	SDHCI_SPRD_TUNING_SD_HS_DATA,
9862306a36Sopenharmony_ci};
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistruct sdhci_sprd_phy_cfg {
10162306a36Sopenharmony_ci	const char *property;
10262306a36Sopenharmony_ci	u8 timing;
10362306a36Sopenharmony_ci};
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic const struct sdhci_sprd_phy_cfg sdhci_sprd_phy_cfgs[] = {
10662306a36Sopenharmony_ci	{ "sprd,phy-delay-legacy", MMC_TIMING_LEGACY, },
10762306a36Sopenharmony_ci	{ "sprd,phy-delay-sd-highspeed", MMC_TIMING_SD_HS, },
10862306a36Sopenharmony_ci	{ "sprd,phy-delay-sd-uhs-sdr50", MMC_TIMING_UHS_SDR50, },
10962306a36Sopenharmony_ci	{ "sprd,phy-delay-sd-uhs-sdr104", MMC_TIMING_UHS_SDR104, },
11062306a36Sopenharmony_ci	{ "sprd,phy-delay-mmc-highspeed", MMC_TIMING_MMC_HS, },
11162306a36Sopenharmony_ci	{ "sprd,phy-delay-mmc-ddr52", MMC_TIMING_MMC_DDR52, },
11262306a36Sopenharmony_ci	{ "sprd,phy-delay-mmc-hs200", MMC_TIMING_MMC_HS200, },
11362306a36Sopenharmony_ci	{ "sprd,phy-delay-mmc-hs400", MMC_TIMING_MMC_HS400, },
11462306a36Sopenharmony_ci	{ "sprd,phy-delay-mmc-hs400es", MMC_TIMING_MMC_HS400 + 1, },
11562306a36Sopenharmony_ci};
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci#define TO_SPRD_HOST(host) sdhci_pltfm_priv(sdhci_priv(host))
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic void sdhci_sprd_init_config(struct sdhci_host *host)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	u16 val;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	/* set dll backup mode */
12462306a36Sopenharmony_ci	val = sdhci_readl(host, SDHCI_SPRD_REG_DEBOUNCE);
12562306a36Sopenharmony_ci	val |= SDHCI_SPRD_BIT_DLL_BAK | SDHCI_SPRD_BIT_DLL_VAL;
12662306a36Sopenharmony_ci	sdhci_writel(host, val, SDHCI_SPRD_REG_DEBOUNCE);
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic inline u32 sdhci_sprd_readl(struct sdhci_host *host, int reg)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	if (unlikely(reg == SDHCI_MAX_CURRENT))
13262306a36Sopenharmony_ci		return SDHCI_SPRD_MAX_CUR;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	return readl_relaxed(host->ioaddr + reg);
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic inline void sdhci_sprd_writel(struct sdhci_host *host, u32 val, int reg)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	/* SDHCI_MAX_CURRENT is reserved on Spreadtrum's platform */
14062306a36Sopenharmony_ci	if (unlikely(reg == SDHCI_MAX_CURRENT))
14162306a36Sopenharmony_ci		return;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	if (unlikely(reg == SDHCI_SIGNAL_ENABLE || reg == SDHCI_INT_ENABLE))
14462306a36Sopenharmony_ci		val = val & SDHCI_SPRD_INT_SIGNAL_MASK;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	writel_relaxed(val, host->ioaddr + reg);
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistatic inline void sdhci_sprd_writew(struct sdhci_host *host, u16 val, int reg)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	/* SDHCI_BLOCK_COUNT is Read Only on Spreadtrum's platform */
15262306a36Sopenharmony_ci	if (unlikely(reg == SDHCI_BLOCK_COUNT))
15362306a36Sopenharmony_ci		return;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	writew_relaxed(val, host->ioaddr + reg);
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic inline void sdhci_sprd_writeb(struct sdhci_host *host, u8 val, int reg)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	/*
16162306a36Sopenharmony_ci	 * Since BIT(3) of SDHCI_SOFTWARE_RESET is reserved according to the
16262306a36Sopenharmony_ci	 * standard specification, sdhci_reset() write this register directly
16362306a36Sopenharmony_ci	 * without checking other reserved bits, that will clear BIT(3) which
16462306a36Sopenharmony_ci	 * is defined as hardware reset on Spreadtrum's platform and clearing
16562306a36Sopenharmony_ci	 * it by mistake will lead the card not work. So here we need to work
16662306a36Sopenharmony_ci	 * around it.
16762306a36Sopenharmony_ci	 */
16862306a36Sopenharmony_ci	if (unlikely(reg == SDHCI_SOFTWARE_RESET)) {
16962306a36Sopenharmony_ci		if (readb_relaxed(host->ioaddr + reg) & SDHCI_HW_RESET_CARD)
17062306a36Sopenharmony_ci			val |= SDHCI_HW_RESET_CARD;
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	writeb_relaxed(val, host->ioaddr + reg);
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic inline void sdhci_sprd_sd_clk_off(struct sdhci_host *host)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	u16 ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	ctrl &= ~SDHCI_CLOCK_CARD_EN;
18162306a36Sopenharmony_ci	sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL);
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic inline void sdhci_sprd_sd_clk_on(struct sdhci_host *host)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	u16 ctrl;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
18962306a36Sopenharmony_ci	ctrl |= SDHCI_CLOCK_CARD_EN;
19062306a36Sopenharmony_ci	sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL);
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic inline void
19462306a36Sopenharmony_cisdhci_sprd_set_dll_invert(struct sdhci_host *host, u32 mask, bool en)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	u32 dll_dly_offset;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	dll_dly_offset = sdhci_readl(host, SDHCI_SPRD_REG_32_DLL_DLY_OFFSET);
19962306a36Sopenharmony_ci	if (en)
20062306a36Sopenharmony_ci		dll_dly_offset |= mask;
20162306a36Sopenharmony_ci	else
20262306a36Sopenharmony_ci		dll_dly_offset &= ~mask;
20362306a36Sopenharmony_ci	sdhci_writel(host, dll_dly_offset, SDHCI_SPRD_REG_32_DLL_DLY_OFFSET);
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic inline u32 sdhci_sprd_calc_div(u32 base_clk, u32 clk)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	u32 div;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	/* select 2x clock source */
21162306a36Sopenharmony_ci	if (base_clk <= clk * 2)
21262306a36Sopenharmony_ci		return 0;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	div = (u32) (base_clk / (clk * 2));
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	if ((base_clk / div) > (clk * 2))
21762306a36Sopenharmony_ci		div++;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	if (div % 2)
22062306a36Sopenharmony_ci		div = (div + 1) / 2;
22162306a36Sopenharmony_ci	else
22262306a36Sopenharmony_ci		div = div / 2;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	if (div > SDHCI_SPRD_CLK_MAX_DIV)
22562306a36Sopenharmony_ci		div = SDHCI_SPRD_CLK_MAX_DIV;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	return div;
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic inline void _sdhci_sprd_set_clock(struct sdhci_host *host,
23162306a36Sopenharmony_ci					unsigned int clk)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
23462306a36Sopenharmony_ci	u32 div, val, mask;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	div = sdhci_sprd_calc_div(sprd_host->base_rate, clk);
23962306a36Sopenharmony_ci	div = ((div & 0x300) >> 2) | ((div & 0xFF) << 8);
24062306a36Sopenharmony_ci	sdhci_enable_clk(host, div);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	val = sdhci_readl(host, SDHCI_SPRD_REG_32_BUSY_POSI);
24362306a36Sopenharmony_ci	mask = SDHCI_SPRD_BIT_OUTR_CLK_AUTO_EN | SDHCI_SPRD_BIT_INNR_CLK_AUTO_EN;
24462306a36Sopenharmony_ci	/* Enable CLK_AUTO when the clock is greater than 400K. */
24562306a36Sopenharmony_ci	if (clk > 400000) {
24662306a36Sopenharmony_ci		if (mask != (val & mask)) {
24762306a36Sopenharmony_ci			val |= mask;
24862306a36Sopenharmony_ci			sdhci_writel(host, val, SDHCI_SPRD_REG_32_BUSY_POSI);
24962306a36Sopenharmony_ci		}
25062306a36Sopenharmony_ci	} else {
25162306a36Sopenharmony_ci		if (val & mask) {
25262306a36Sopenharmony_ci			val &= ~mask;
25362306a36Sopenharmony_ci			sdhci_writel(host, val, SDHCI_SPRD_REG_32_BUSY_POSI);
25462306a36Sopenharmony_ci		}
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cistatic void sdhci_sprd_enable_phy_dll(struct sdhci_host *host)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	u32 tmp;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	tmp = sdhci_readl(host, SDHCI_SPRD_REG_32_DLL_CFG);
26362306a36Sopenharmony_ci	tmp &= ~(SDHCI_SPRD_DLL_EN | SDHCI_SPRD_DLL_ALL_CPST_EN);
26462306a36Sopenharmony_ci	sdhci_writel(host, tmp, SDHCI_SPRD_REG_32_DLL_CFG);
26562306a36Sopenharmony_ci	/* wait 1ms */
26662306a36Sopenharmony_ci	usleep_range(1000, 1250);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	tmp = sdhci_readl(host, SDHCI_SPRD_REG_32_DLL_CFG);
26962306a36Sopenharmony_ci	tmp |= SDHCI_SPRD_DLL_ALL_CPST_EN | SDHCI_SPRD_DLL_SEARCH_MODE |
27062306a36Sopenharmony_ci		SDHCI_SPRD_DLL_INIT_COUNT | SDHCI_SPRD_DLL_PHASE_INTERNAL;
27162306a36Sopenharmony_ci	sdhci_writel(host, tmp, SDHCI_SPRD_REG_32_DLL_CFG);
27262306a36Sopenharmony_ci	/* wait 1ms */
27362306a36Sopenharmony_ci	usleep_range(1000, 1250);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	tmp = sdhci_readl(host, SDHCI_SPRD_REG_32_DLL_CFG);
27662306a36Sopenharmony_ci	tmp |= SDHCI_SPRD_DLL_EN;
27762306a36Sopenharmony_ci	sdhci_writel(host, tmp, SDHCI_SPRD_REG_32_DLL_CFG);
27862306a36Sopenharmony_ci	/* wait 1ms */
27962306a36Sopenharmony_ci	usleep_range(1000, 1250);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	if (read_poll_timeout(sdhci_readl, tmp, (tmp & SDHCI_SPRD_DLL_LOCKED),
28262306a36Sopenharmony_ci		2000, USEC_PER_SEC, false, host, SDHCI_SPRD_REG_32_DLL_STS0)) {
28362306a36Sopenharmony_ci		pr_err("%s: DLL locked fail!\n", mmc_hostname(host->mmc));
28462306a36Sopenharmony_ci		pr_info("%s: DLL_STS0 : 0x%x, DLL_CFG : 0x%x\n",
28562306a36Sopenharmony_ci			 mmc_hostname(host->mmc),
28662306a36Sopenharmony_ci			 sdhci_readl(host, SDHCI_SPRD_REG_32_DLL_STS0),
28762306a36Sopenharmony_ci			 sdhci_readl(host, SDHCI_SPRD_REG_32_DLL_CFG));
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cistatic void sdhci_sprd_set_clock(struct sdhci_host *host, unsigned int clock)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	bool en = false, clk_changed = false;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	if (clock == 0) {
29662306a36Sopenharmony_ci		sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
29762306a36Sopenharmony_ci	} else if (clock != host->clock) {
29862306a36Sopenharmony_ci		sdhci_sprd_sd_clk_off(host);
29962306a36Sopenharmony_ci		_sdhci_sprd_set_clock(host, clock);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci		if (clock <= 400000)
30262306a36Sopenharmony_ci			en = true;
30362306a36Sopenharmony_ci		sdhci_sprd_set_dll_invert(host, SDHCI_SPRD_BIT_CMD_DLY_INV |
30462306a36Sopenharmony_ci					  SDHCI_SPRD_BIT_POSRD_DLY_INV, en);
30562306a36Sopenharmony_ci		clk_changed = true;
30662306a36Sopenharmony_ci	} else {
30762306a36Sopenharmony_ci		_sdhci_sprd_set_clock(host, clock);
30862306a36Sopenharmony_ci	}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	/*
31162306a36Sopenharmony_ci	 * According to the Spreadtrum SD host specification, when we changed
31262306a36Sopenharmony_ci	 * the clock to be more than 52M, we should enable the PHY DLL which
31362306a36Sopenharmony_ci	 * is used to track the clock frequency to make the clock work more
31462306a36Sopenharmony_ci	 * stable. Otherwise deviation may occur of the higher clock.
31562306a36Sopenharmony_ci	 */
31662306a36Sopenharmony_ci	if (clk_changed && clock > SDHCI_SPRD_PHY_DLL_CLK)
31762306a36Sopenharmony_ci		sdhci_sprd_enable_phy_dll(host);
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic unsigned int sdhci_sprd_get_max_clock(struct sdhci_host *host)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	return clk_round_rate(sprd_host->clk_sdio, ULONG_MAX);
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_cistatic unsigned int sdhci_sprd_get_min_clock(struct sdhci_host *host)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	return 100000;
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic void sdhci_sprd_set_uhs_signaling(struct sdhci_host *host,
33362306a36Sopenharmony_ci					 unsigned int timing)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
33662306a36Sopenharmony_ci	struct mmc_host *mmc = host->mmc;
33762306a36Sopenharmony_ci	u32 *p = sprd_host->phy_delay;
33862306a36Sopenharmony_ci	u16 ctrl_2;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	if (timing == host->timing)
34162306a36Sopenharmony_ci		return;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
34462306a36Sopenharmony_ci	/* Select Bus Speed Mode for host */
34562306a36Sopenharmony_ci	ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
34662306a36Sopenharmony_ci	switch (timing) {
34762306a36Sopenharmony_ci	case MMC_TIMING_UHS_SDR12:
34862306a36Sopenharmony_ci		ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
34962306a36Sopenharmony_ci		break;
35062306a36Sopenharmony_ci	case MMC_TIMING_MMC_HS:
35162306a36Sopenharmony_ci	case MMC_TIMING_SD_HS:
35262306a36Sopenharmony_ci	case MMC_TIMING_UHS_SDR25:
35362306a36Sopenharmony_ci		ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
35462306a36Sopenharmony_ci		break;
35562306a36Sopenharmony_ci	case MMC_TIMING_UHS_SDR50:
35662306a36Sopenharmony_ci		ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
35762306a36Sopenharmony_ci		break;
35862306a36Sopenharmony_ci	case MMC_TIMING_UHS_SDR104:
35962306a36Sopenharmony_ci		ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
36062306a36Sopenharmony_ci		break;
36162306a36Sopenharmony_ci	case MMC_TIMING_UHS_DDR50:
36262306a36Sopenharmony_ci	case MMC_TIMING_MMC_DDR52:
36362306a36Sopenharmony_ci		ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
36462306a36Sopenharmony_ci		break;
36562306a36Sopenharmony_ci	case MMC_TIMING_MMC_HS200:
36662306a36Sopenharmony_ci		ctrl_2 |= SDHCI_SPRD_CTRL_HS200;
36762306a36Sopenharmony_ci		break;
36862306a36Sopenharmony_ci	case MMC_TIMING_MMC_HS400:
36962306a36Sopenharmony_ci		ctrl_2 |= SDHCI_SPRD_CTRL_HS400;
37062306a36Sopenharmony_ci		break;
37162306a36Sopenharmony_ci	default:
37262306a36Sopenharmony_ci		break;
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	if (!mmc->ios.enhanced_strobe)
37862306a36Sopenharmony_ci		sdhci_writel(host, p[timing], SDHCI_SPRD_REG_32_DLL_DLY);
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cistatic void sdhci_sprd_hw_reset(struct sdhci_host *host)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	int val;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	/*
38662306a36Sopenharmony_ci	 * Note: don't use sdhci_writeb() API here since it is redirected to
38762306a36Sopenharmony_ci	 * sdhci_sprd_writeb() in which we have a workaround for
38862306a36Sopenharmony_ci	 * SDHCI_SOFTWARE_RESET which would make bit SDHCI_HW_RESET_CARD can
38962306a36Sopenharmony_ci	 * not be cleared.
39062306a36Sopenharmony_ci	 */
39162306a36Sopenharmony_ci	val = readb_relaxed(host->ioaddr + SDHCI_SOFTWARE_RESET);
39262306a36Sopenharmony_ci	val &= ~SDHCI_HW_RESET_CARD;
39362306a36Sopenharmony_ci	writeb_relaxed(val, host->ioaddr + SDHCI_SOFTWARE_RESET);
39462306a36Sopenharmony_ci	/* wait for 10 us */
39562306a36Sopenharmony_ci	usleep_range(10, 20);
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	val |= SDHCI_HW_RESET_CARD;
39862306a36Sopenharmony_ci	writeb_relaxed(val, host->ioaddr + SDHCI_SOFTWARE_RESET);
39962306a36Sopenharmony_ci	usleep_range(300, 500);
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_cistatic unsigned int sdhci_sprd_get_max_timeout_count(struct sdhci_host *host)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	/* The Spredtrum controller actual maximum timeout count is 1 << 31 */
40562306a36Sopenharmony_ci	return 1 << 31;
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_cistatic unsigned int sdhci_sprd_get_ro(struct sdhci_host *host)
40962306a36Sopenharmony_ci{
41062306a36Sopenharmony_ci	return 0;
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic void sdhci_sprd_request_done(struct sdhci_host *host,
41462306a36Sopenharmony_ci				    struct mmc_request *mrq)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	/* Validate if the request was from software queue firstly. */
41762306a36Sopenharmony_ci	if (mmc_hsq_finalize_request(host->mmc, mrq))
41862306a36Sopenharmony_ci		return;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	mmc_request_done(host->mmc, mrq);
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_cistatic void sdhci_sprd_set_power(struct sdhci_host *host, unsigned char mode,
42462306a36Sopenharmony_ci				 unsigned short vdd)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	struct mmc_host *mmc = host->mmc;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	switch (mode) {
42962306a36Sopenharmony_ci	case MMC_POWER_OFF:
43062306a36Sopenharmony_ci		mmc_regulator_set_ocr(host->mmc, mmc->supply.vmmc, 0);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci		mmc_regulator_disable_vqmmc(mmc);
43362306a36Sopenharmony_ci		break;
43462306a36Sopenharmony_ci	case MMC_POWER_ON:
43562306a36Sopenharmony_ci		mmc_regulator_enable_vqmmc(mmc);
43662306a36Sopenharmony_ci		break;
43762306a36Sopenharmony_ci	case MMC_POWER_UP:
43862306a36Sopenharmony_ci		mmc_regulator_set_ocr(host->mmc, mmc->supply.vmmc, vdd);
43962306a36Sopenharmony_ci		break;
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_cistatic struct sdhci_ops sdhci_sprd_ops = {
44462306a36Sopenharmony_ci	.read_l = sdhci_sprd_readl,
44562306a36Sopenharmony_ci	.write_l = sdhci_sprd_writel,
44662306a36Sopenharmony_ci	.write_w = sdhci_sprd_writew,
44762306a36Sopenharmony_ci	.write_b = sdhci_sprd_writeb,
44862306a36Sopenharmony_ci	.set_clock = sdhci_sprd_set_clock,
44962306a36Sopenharmony_ci	.set_power = sdhci_sprd_set_power,
45062306a36Sopenharmony_ci	.get_max_clock = sdhci_sprd_get_max_clock,
45162306a36Sopenharmony_ci	.get_min_clock = sdhci_sprd_get_min_clock,
45262306a36Sopenharmony_ci	.set_bus_width = sdhci_set_bus_width,
45362306a36Sopenharmony_ci	.reset = sdhci_reset,
45462306a36Sopenharmony_ci	.set_uhs_signaling = sdhci_sprd_set_uhs_signaling,
45562306a36Sopenharmony_ci	.hw_reset = sdhci_sprd_hw_reset,
45662306a36Sopenharmony_ci	.get_max_timeout_count = sdhci_sprd_get_max_timeout_count,
45762306a36Sopenharmony_ci	.get_ro = sdhci_sprd_get_ro,
45862306a36Sopenharmony_ci	.request_done = sdhci_sprd_request_done,
45962306a36Sopenharmony_ci};
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_cistatic void sdhci_sprd_check_auto_cmd23(struct mmc_host *mmc,
46262306a36Sopenharmony_ci					struct mmc_request *mrq)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	struct sdhci_host *host = mmc_priv(mmc);
46562306a36Sopenharmony_ci	struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	host->flags |= sprd_host->flags & SDHCI_AUTO_CMD23;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	/*
47062306a36Sopenharmony_ci	 * From version 4.10 onward, ARGUMENT2 register is also as 32-bit
47162306a36Sopenharmony_ci	 * block count register which doesn't support stuff bits of
47262306a36Sopenharmony_ci	 * CMD23 argument on Spreadtrum's sd host controller.
47362306a36Sopenharmony_ci	 */
47462306a36Sopenharmony_ci	if (host->version >= SDHCI_SPEC_410 &&
47562306a36Sopenharmony_ci	    mrq->sbc && (mrq->sbc->arg & SDHCI_SPRD_ARG2_STUFF) &&
47662306a36Sopenharmony_ci	    (host->flags & SDHCI_AUTO_CMD23))
47762306a36Sopenharmony_ci		host->flags &= ~SDHCI_AUTO_CMD23;
47862306a36Sopenharmony_ci}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_cistatic void sdhci_sprd_request(struct mmc_host *mmc, struct mmc_request *mrq)
48162306a36Sopenharmony_ci{
48262306a36Sopenharmony_ci	sdhci_sprd_check_auto_cmd23(mmc, mrq);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	sdhci_request(mmc, mrq);
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_cistatic int sdhci_sprd_request_atomic(struct mmc_host *mmc,
48862306a36Sopenharmony_ci				     struct mmc_request *mrq)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	sdhci_sprd_check_auto_cmd23(mmc, mrq);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	return sdhci_request_atomic(mmc, mrq);
49362306a36Sopenharmony_ci}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_cistatic int sdhci_sprd_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios)
49662306a36Sopenharmony_ci{
49762306a36Sopenharmony_ci	struct sdhci_host *host = mmc_priv(mmc);
49862306a36Sopenharmony_ci	struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
49962306a36Sopenharmony_ci	int ret;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	if (!IS_ERR(mmc->supply.vqmmc)) {
50262306a36Sopenharmony_ci		ret = mmc_regulator_set_vqmmc(mmc, ios);
50362306a36Sopenharmony_ci		if (ret < 0) {
50462306a36Sopenharmony_ci			pr_err("%s: Switching signalling voltage failed\n",
50562306a36Sopenharmony_ci			       mmc_hostname(mmc));
50662306a36Sopenharmony_ci			return ret;
50762306a36Sopenharmony_ci		}
50862306a36Sopenharmony_ci	}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	if (IS_ERR(sprd_host->pinctrl))
51162306a36Sopenharmony_ci		goto reset;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	switch (ios->signal_voltage) {
51462306a36Sopenharmony_ci	case MMC_SIGNAL_VOLTAGE_180:
51562306a36Sopenharmony_ci		ret = pinctrl_select_state(sprd_host->pinctrl,
51662306a36Sopenharmony_ci					   sprd_host->pins_uhs);
51762306a36Sopenharmony_ci		if (ret) {
51862306a36Sopenharmony_ci			pr_err("%s: failed to select uhs pin state\n",
51962306a36Sopenharmony_ci			       mmc_hostname(mmc));
52062306a36Sopenharmony_ci			return ret;
52162306a36Sopenharmony_ci		}
52262306a36Sopenharmony_ci		break;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	default:
52562306a36Sopenharmony_ci		fallthrough;
52662306a36Sopenharmony_ci	case MMC_SIGNAL_VOLTAGE_330:
52762306a36Sopenharmony_ci		ret = pinctrl_select_state(sprd_host->pinctrl,
52862306a36Sopenharmony_ci					   sprd_host->pins_default);
52962306a36Sopenharmony_ci		if (ret) {
53062306a36Sopenharmony_ci			pr_err("%s: failed to select default pin state\n",
53162306a36Sopenharmony_ci			       mmc_hostname(mmc));
53262306a36Sopenharmony_ci			return ret;
53362306a36Sopenharmony_ci		}
53462306a36Sopenharmony_ci		break;
53562306a36Sopenharmony_ci	}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	/* Wait for 300 ~ 500 us for pin state stable */
53862306a36Sopenharmony_ci	usleep_range(300, 500);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_cireset:
54162306a36Sopenharmony_ci	sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	return 0;
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_cistatic void sdhci_sprd_hs400_enhanced_strobe(struct mmc_host *mmc,
54762306a36Sopenharmony_ci					     struct mmc_ios *ios)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	struct sdhci_host *host = mmc_priv(mmc);
55062306a36Sopenharmony_ci	struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
55162306a36Sopenharmony_ci	u32 *p = sprd_host->phy_delay;
55262306a36Sopenharmony_ci	u16 ctrl_2;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	if (!ios->enhanced_strobe)
55562306a36Sopenharmony_ci		return;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	sdhci_sprd_sd_clk_off(host);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	/* Set HS400 enhanced strobe mode */
56062306a36Sopenharmony_ci	ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
56162306a36Sopenharmony_ci	ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
56262306a36Sopenharmony_ci	ctrl_2 |= SDHCI_SPRD_CTRL_HS400ES;
56362306a36Sopenharmony_ci	sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	sdhci_sprd_sd_clk_on(host);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	/* Set the PHY DLL delay value for HS400 enhanced strobe mode */
56862306a36Sopenharmony_ci	sdhci_writel(host, p[MMC_TIMING_MMC_HS400 + 1],
56962306a36Sopenharmony_ci		     SDHCI_SPRD_REG_32_DLL_DLY);
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_cistatic int mmc_send_tuning_cmd(struct mmc_card *card)
57362306a36Sopenharmony_ci{
57462306a36Sopenharmony_ci	return mmc_send_status(card, NULL);
57562306a36Sopenharmony_ci}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_cistatic int mmc_send_tuning_data(struct mmc_card *card)
57862306a36Sopenharmony_ci{
57962306a36Sopenharmony_ci	u8 *status;
58062306a36Sopenharmony_ci	int ret;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	status = kmalloc(64, GFP_KERNEL);
58362306a36Sopenharmony_ci	if (!status)
58462306a36Sopenharmony_ci		return -ENOMEM;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	ret = mmc_sd_switch(card, 0, 0, 0, status);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	kfree(status);
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	return ret;
59162306a36Sopenharmony_ci}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_cistatic int sdhci_sprd_get_best_clk_sample(struct mmc_host *mmc, u8 *value)
59462306a36Sopenharmony_ci{
59562306a36Sopenharmony_ci	int range_end = SDHCI_SPRD_MAX_RANGE;
59662306a36Sopenharmony_ci	int range_length = 0;
59762306a36Sopenharmony_ci	int middle_range = 0;
59862306a36Sopenharmony_ci	int count = 0;
59962306a36Sopenharmony_ci	int i;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	for (i = 0; i <= SDHCI_SPRD_MAX_RANGE; i++) {
60262306a36Sopenharmony_ci		if (value[i]) {
60362306a36Sopenharmony_ci			pr_debug("%s: tuning ok: %d\n", mmc_hostname(mmc), i);
60462306a36Sopenharmony_ci			count++;
60562306a36Sopenharmony_ci		} else {
60662306a36Sopenharmony_ci			pr_debug("%s: tuning fail: %d\n", mmc_hostname(mmc), i);
60762306a36Sopenharmony_ci			if (range_length < count) {
60862306a36Sopenharmony_ci				range_length = count;
60962306a36Sopenharmony_ci				range_end = i - 1;
61062306a36Sopenharmony_ci				count = 0;
61162306a36Sopenharmony_ci			}
61262306a36Sopenharmony_ci		}
61362306a36Sopenharmony_ci	}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	if (!count)
61662306a36Sopenharmony_ci		return -EIO;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	if (count > range_length) {
61962306a36Sopenharmony_ci		range_length = count;
62062306a36Sopenharmony_ci		range_end = i - 1;
62162306a36Sopenharmony_ci	}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	middle_range = range_end - (range_length - 1) / 2;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	return middle_range;
62662306a36Sopenharmony_ci}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_cistatic int sdhci_sprd_tuning(struct mmc_host *mmc, struct mmc_card *card,
62962306a36Sopenharmony_ci			enum sdhci_sprd_tuning_type type)
63062306a36Sopenharmony_ci{
63162306a36Sopenharmony_ci	struct sdhci_host *host = mmc_priv(mmc);
63262306a36Sopenharmony_ci	struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
63362306a36Sopenharmony_ci	u32 *p = sprd_host->phy_delay;
63462306a36Sopenharmony_ci	u32 dll_cfg, dll_dly;
63562306a36Sopenharmony_ci	int best_clk_sample;
63662306a36Sopenharmony_ci	int err = 0;
63762306a36Sopenharmony_ci	u8 *value;
63862306a36Sopenharmony_ci	int i;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	value = kmalloc(SDHCI_SPRD_MAX_RANGE + 1, GFP_KERNEL);
64162306a36Sopenharmony_ci	if (!value)
64262306a36Sopenharmony_ci		return -ENOMEM;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	dll_cfg = sdhci_readl(host, SDHCI_SPRD_REG_32_DLL_CFG);
64762306a36Sopenharmony_ci	dll_cfg &= ~SDHCI_SPRD_CPST_EN;
64862306a36Sopenharmony_ci	sdhci_writel(host, dll_cfg, SDHCI_SPRD_REG_32_DLL_CFG);
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	dll_dly = p[mmc->ios.timing];
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	for (i = 0; i <= SDHCI_SPRD_MAX_RANGE; i++) {
65362306a36Sopenharmony_ci		if (type == SDHCI_SPRD_TUNING_SD_HS_CMD) {
65462306a36Sopenharmony_ci			dll_dly &= ~SDHCI_SPRD_CMD_DLY_MASK;
65562306a36Sopenharmony_ci			dll_dly |= ((i << 8) & SDHCI_SPRD_CMD_DLY_MASK);
65662306a36Sopenharmony_ci		} else {
65762306a36Sopenharmony_ci			dll_dly &= ~SDHCI_SPRD_POSRD_DLY_MASK;
65862306a36Sopenharmony_ci			dll_dly |= ((i << 16) & SDHCI_SPRD_POSRD_DLY_MASK);
65962306a36Sopenharmony_ci		}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci		sdhci_writel(host, dll_dly, SDHCI_SPRD_REG_32_DLL_DLY);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci		if (type == SDHCI_SPRD_TUNING_SD_HS_CMD)
66462306a36Sopenharmony_ci			value[i] = !mmc_send_tuning_cmd(card);
66562306a36Sopenharmony_ci		else
66662306a36Sopenharmony_ci			value[i] = !mmc_send_tuning_data(card);
66762306a36Sopenharmony_ci	}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	best_clk_sample = sdhci_sprd_get_best_clk_sample(mmc, value);
67062306a36Sopenharmony_ci	if (best_clk_sample < 0) {
67162306a36Sopenharmony_ci		dev_err(mmc_dev(host->mmc), "all tuning phase fail!\n");
67262306a36Sopenharmony_ci		err = best_clk_sample;
67362306a36Sopenharmony_ci		goto out;
67462306a36Sopenharmony_ci	}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	if (type == SDHCI_SPRD_TUNING_SD_HS_CMD) {
67762306a36Sopenharmony_ci		p[mmc->ios.timing] &= ~SDHCI_SPRD_CMD_DLY_MASK;
67862306a36Sopenharmony_ci		p[mmc->ios.timing] |= ((best_clk_sample << 8) & SDHCI_SPRD_CMD_DLY_MASK);
67962306a36Sopenharmony_ci	} else {
68062306a36Sopenharmony_ci		p[mmc->ios.timing] &= ~(SDHCI_SPRD_POSRD_DLY_MASK);
68162306a36Sopenharmony_ci		p[mmc->ios.timing] |= ((best_clk_sample << 16) & SDHCI_SPRD_POSRD_DLY_MASK);
68262306a36Sopenharmony_ci	}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	pr_debug("%s: the best clk sample %d, delay value 0x%08x\n",
68562306a36Sopenharmony_ci			mmc_hostname(host->mmc), best_clk_sample, p[mmc->ios.timing]);
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ciout:
68862306a36Sopenharmony_ci	sdhci_writel(host, p[mmc->ios.timing], SDHCI_SPRD_REG_32_DLL_DLY);
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	kfree(value);
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	return err;
69362306a36Sopenharmony_ci}
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_cistatic int sdhci_sprd_prepare_sd_hs_cmd_tuning(struct mmc_host *mmc, struct mmc_card *card)
69662306a36Sopenharmony_ci{
69762306a36Sopenharmony_ci	return sdhci_sprd_tuning(mmc, card, SDHCI_SPRD_TUNING_SD_HS_CMD);
69862306a36Sopenharmony_ci}
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_cistatic int sdhci_sprd_execute_sd_hs_data_tuning(struct mmc_host *mmc, struct mmc_card *card)
70162306a36Sopenharmony_ci{
70262306a36Sopenharmony_ci	return sdhci_sprd_tuning(mmc, card, SDHCI_SPRD_TUNING_SD_HS_DATA);
70362306a36Sopenharmony_ci}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_cistatic void sdhci_sprd_phy_param_parse(struct sdhci_sprd_host *sprd_host,
70662306a36Sopenharmony_ci				       struct device_node *np)
70762306a36Sopenharmony_ci{
70862306a36Sopenharmony_ci	u32 *p = sprd_host->phy_delay;
70962306a36Sopenharmony_ci	int ret, i, index;
71062306a36Sopenharmony_ci	u32 val[4];
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(sdhci_sprd_phy_cfgs); i++) {
71362306a36Sopenharmony_ci		ret = of_property_read_u32_array(np,
71462306a36Sopenharmony_ci				sdhci_sprd_phy_cfgs[i].property, val, 4);
71562306a36Sopenharmony_ci		if (ret)
71662306a36Sopenharmony_ci			continue;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci		index = sdhci_sprd_phy_cfgs[i].timing;
71962306a36Sopenharmony_ci		p[index] = val[0] | (val[1] << 8) | (val[2] << 16) | (val[3] << 24);
72062306a36Sopenharmony_ci	}
72162306a36Sopenharmony_ci}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_cistatic const struct sdhci_pltfm_data sdhci_sprd_pdata = {
72462306a36Sopenharmony_ci	.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
72562306a36Sopenharmony_ci		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK,
72662306a36Sopenharmony_ci	.quirks2 = SDHCI_QUIRK2_BROKEN_HS200 |
72762306a36Sopenharmony_ci		   SDHCI_QUIRK2_USE_32BIT_BLK_CNT |
72862306a36Sopenharmony_ci		   SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
72962306a36Sopenharmony_ci	.ops = &sdhci_sprd_ops,
73062306a36Sopenharmony_ci};
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_cistatic int sdhci_sprd_probe(struct platform_device *pdev)
73362306a36Sopenharmony_ci{
73462306a36Sopenharmony_ci	struct sdhci_host *host;
73562306a36Sopenharmony_ci	struct sdhci_sprd_host *sprd_host;
73662306a36Sopenharmony_ci	struct mmc_hsq *hsq;
73762306a36Sopenharmony_ci	struct clk *clk;
73862306a36Sopenharmony_ci	int ret = 0;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	host = sdhci_pltfm_init(pdev, &sdhci_sprd_pdata, sizeof(*sprd_host));
74162306a36Sopenharmony_ci	if (IS_ERR(host))
74262306a36Sopenharmony_ci		return PTR_ERR(host);
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	host->dma_mask = DMA_BIT_MASK(64);
74562306a36Sopenharmony_ci	pdev->dev.dma_mask = &host->dma_mask;
74662306a36Sopenharmony_ci	host->mmc_host_ops.request = sdhci_sprd_request;
74762306a36Sopenharmony_ci	host->mmc_host_ops.hs400_enhanced_strobe =
74862306a36Sopenharmony_ci		sdhci_sprd_hs400_enhanced_strobe;
74962306a36Sopenharmony_ci	host->mmc_host_ops.prepare_sd_hs_tuning =
75062306a36Sopenharmony_ci		sdhci_sprd_prepare_sd_hs_cmd_tuning;
75162306a36Sopenharmony_ci	host->mmc_host_ops.execute_sd_hs_tuning =
75262306a36Sopenharmony_ci		sdhci_sprd_execute_sd_hs_data_tuning;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	/*
75562306a36Sopenharmony_ci	 * We can not use the standard ops to change and detect the voltage
75662306a36Sopenharmony_ci	 * signal for Spreadtrum SD host controller, since our voltage regulator
75762306a36Sopenharmony_ci	 * for I/O is fixed in hardware, that means we do not need control
75862306a36Sopenharmony_ci	 * the standard SD host controller to change the I/O voltage.
75962306a36Sopenharmony_ci	 */
76062306a36Sopenharmony_ci	host->mmc_host_ops.start_signal_voltage_switch =
76162306a36Sopenharmony_ci		sdhci_sprd_voltage_switch;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	host->mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
76462306a36Sopenharmony_ci		MMC_CAP_WAIT_WHILE_BUSY;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	ret = mmc_of_parse(host->mmc);
76762306a36Sopenharmony_ci	if (ret)
76862306a36Sopenharmony_ci		goto pltfm_free;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	if (!mmc_card_is_removable(host->mmc))
77162306a36Sopenharmony_ci		host->mmc_host_ops.request_atomic = sdhci_sprd_request_atomic;
77262306a36Sopenharmony_ci	else
77362306a36Sopenharmony_ci		host->always_defer_done = true;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	sprd_host = TO_SPRD_HOST(host);
77662306a36Sopenharmony_ci	sdhci_sprd_phy_param_parse(sprd_host, pdev->dev.of_node);
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	sprd_host->pinctrl = devm_pinctrl_get(&pdev->dev);
77962306a36Sopenharmony_ci	if (!IS_ERR(sprd_host->pinctrl)) {
78062306a36Sopenharmony_ci		sprd_host->pins_uhs =
78162306a36Sopenharmony_ci			pinctrl_lookup_state(sprd_host->pinctrl, "state_uhs");
78262306a36Sopenharmony_ci		if (IS_ERR(sprd_host->pins_uhs)) {
78362306a36Sopenharmony_ci			ret = PTR_ERR(sprd_host->pins_uhs);
78462306a36Sopenharmony_ci			goto pltfm_free;
78562306a36Sopenharmony_ci		}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci		sprd_host->pins_default =
78862306a36Sopenharmony_ci			pinctrl_lookup_state(sprd_host->pinctrl, "default");
78962306a36Sopenharmony_ci		if (IS_ERR(sprd_host->pins_default)) {
79062306a36Sopenharmony_ci			ret = PTR_ERR(sprd_host->pins_default);
79162306a36Sopenharmony_ci			goto pltfm_free;
79262306a36Sopenharmony_ci		}
79362306a36Sopenharmony_ci	}
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	clk = devm_clk_get(&pdev->dev, "sdio");
79662306a36Sopenharmony_ci	if (IS_ERR(clk)) {
79762306a36Sopenharmony_ci		ret = PTR_ERR(clk);
79862306a36Sopenharmony_ci		goto pltfm_free;
79962306a36Sopenharmony_ci	}
80062306a36Sopenharmony_ci	sprd_host->clk_sdio = clk;
80162306a36Sopenharmony_ci	sprd_host->base_rate = clk_get_rate(sprd_host->clk_sdio);
80262306a36Sopenharmony_ci	if (!sprd_host->base_rate)
80362306a36Sopenharmony_ci		sprd_host->base_rate = SDHCI_SPRD_CLK_DEF_RATE;
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	clk = devm_clk_get(&pdev->dev, "enable");
80662306a36Sopenharmony_ci	if (IS_ERR(clk)) {
80762306a36Sopenharmony_ci		ret = PTR_ERR(clk);
80862306a36Sopenharmony_ci		goto pltfm_free;
80962306a36Sopenharmony_ci	}
81062306a36Sopenharmony_ci	sprd_host->clk_enable = clk;
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	clk = devm_clk_get(&pdev->dev, "2x_enable");
81362306a36Sopenharmony_ci	if (!IS_ERR(clk))
81462306a36Sopenharmony_ci		sprd_host->clk_2x_enable = clk;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	ret = clk_prepare_enable(sprd_host->clk_sdio);
81762306a36Sopenharmony_ci	if (ret)
81862306a36Sopenharmony_ci		goto pltfm_free;
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	ret = clk_prepare_enable(sprd_host->clk_enable);
82162306a36Sopenharmony_ci	if (ret)
82262306a36Sopenharmony_ci		goto clk_disable;
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	ret = clk_prepare_enable(sprd_host->clk_2x_enable);
82562306a36Sopenharmony_ci	if (ret)
82662306a36Sopenharmony_ci		goto clk_disable2;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	sdhci_sprd_init_config(host);
82962306a36Sopenharmony_ci	host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
83062306a36Sopenharmony_ci	sprd_host->version = ((host->version & SDHCI_VENDOR_VER_MASK) >>
83162306a36Sopenharmony_ci			       SDHCI_VENDOR_VER_SHIFT);
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	pm_runtime_get_noresume(&pdev->dev);
83462306a36Sopenharmony_ci	pm_runtime_set_active(&pdev->dev);
83562306a36Sopenharmony_ci	pm_runtime_enable(&pdev->dev);
83662306a36Sopenharmony_ci	pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
83762306a36Sopenharmony_ci	pm_runtime_use_autosuspend(&pdev->dev);
83862306a36Sopenharmony_ci	pm_suspend_ignore_children(&pdev->dev, 1);
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	sdhci_enable_v4_mode(host);
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	/*
84362306a36Sopenharmony_ci	 * Supply the existing CAPS, but clear the UHS-I modes. This
84462306a36Sopenharmony_ci	 * will allow these modes to be specified only by device
84562306a36Sopenharmony_ci	 * tree properties through mmc_of_parse().
84662306a36Sopenharmony_ci	 */
84762306a36Sopenharmony_ci	sdhci_read_caps(host);
84862306a36Sopenharmony_ci	host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_SDR104 |
84962306a36Sopenharmony_ci			 SDHCI_SUPPORT_DDR50);
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	ret = mmc_regulator_get_supply(host->mmc);
85262306a36Sopenharmony_ci	if (ret)
85362306a36Sopenharmony_ci		goto pm_runtime_disable;
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	ret = sdhci_setup_host(host);
85662306a36Sopenharmony_ci	if (ret)
85762306a36Sopenharmony_ci		goto pm_runtime_disable;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	sprd_host->flags = host->flags;
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	hsq = devm_kzalloc(&pdev->dev, sizeof(*hsq), GFP_KERNEL);
86262306a36Sopenharmony_ci	if (!hsq) {
86362306a36Sopenharmony_ci		ret = -ENOMEM;
86462306a36Sopenharmony_ci		goto err_cleanup_host;
86562306a36Sopenharmony_ci	}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	ret = mmc_hsq_init(hsq, host->mmc);
86862306a36Sopenharmony_ci	if (ret)
86962306a36Sopenharmony_ci		goto err_cleanup_host;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	ret = __sdhci_add_host(host);
87262306a36Sopenharmony_ci	if (ret)
87362306a36Sopenharmony_ci		goto err_cleanup_host;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	pm_runtime_mark_last_busy(&pdev->dev);
87662306a36Sopenharmony_ci	pm_runtime_put_autosuspend(&pdev->dev);
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	return 0;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_cierr_cleanup_host:
88162306a36Sopenharmony_ci	sdhci_cleanup_host(host);
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_cipm_runtime_disable:
88462306a36Sopenharmony_ci	pm_runtime_put_noidle(&pdev->dev);
88562306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
88662306a36Sopenharmony_ci	pm_runtime_set_suspended(&pdev->dev);
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	clk_disable_unprepare(sprd_host->clk_2x_enable);
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ciclk_disable2:
89162306a36Sopenharmony_ci	clk_disable_unprepare(sprd_host->clk_enable);
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ciclk_disable:
89462306a36Sopenharmony_ci	clk_disable_unprepare(sprd_host->clk_sdio);
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_cipltfm_free:
89762306a36Sopenharmony_ci	sdhci_pltfm_free(pdev);
89862306a36Sopenharmony_ci	return ret;
89962306a36Sopenharmony_ci}
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_cistatic void sdhci_sprd_remove(struct platform_device *pdev)
90262306a36Sopenharmony_ci{
90362306a36Sopenharmony_ci	struct sdhci_host *host = platform_get_drvdata(pdev);
90462306a36Sopenharmony_ci	struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	sdhci_remove_host(host, 0);
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	clk_disable_unprepare(sprd_host->clk_sdio);
90962306a36Sopenharmony_ci	clk_disable_unprepare(sprd_host->clk_enable);
91062306a36Sopenharmony_ci	clk_disable_unprepare(sprd_host->clk_2x_enable);
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	sdhci_pltfm_free(pdev);
91362306a36Sopenharmony_ci}
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_cistatic const struct of_device_id sdhci_sprd_of_match[] = {
91662306a36Sopenharmony_ci	{ .compatible = "sprd,sdhci-r11", },
91762306a36Sopenharmony_ci	{ }
91862306a36Sopenharmony_ci};
91962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, sdhci_sprd_of_match);
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci#ifdef CONFIG_PM
92262306a36Sopenharmony_cistatic int sdhci_sprd_runtime_suspend(struct device *dev)
92362306a36Sopenharmony_ci{
92462306a36Sopenharmony_ci	struct sdhci_host *host = dev_get_drvdata(dev);
92562306a36Sopenharmony_ci	struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	mmc_hsq_suspend(host->mmc);
92862306a36Sopenharmony_ci	sdhci_runtime_suspend_host(host);
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	clk_disable_unprepare(sprd_host->clk_sdio);
93162306a36Sopenharmony_ci	clk_disable_unprepare(sprd_host->clk_enable);
93262306a36Sopenharmony_ci	clk_disable_unprepare(sprd_host->clk_2x_enable);
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	return 0;
93562306a36Sopenharmony_ci}
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_cistatic int sdhci_sprd_runtime_resume(struct device *dev)
93862306a36Sopenharmony_ci{
93962306a36Sopenharmony_ci	struct sdhci_host *host = dev_get_drvdata(dev);
94062306a36Sopenharmony_ci	struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
94162306a36Sopenharmony_ci	int ret;
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	ret = clk_prepare_enable(sprd_host->clk_2x_enable);
94462306a36Sopenharmony_ci	if (ret)
94562306a36Sopenharmony_ci		return ret;
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	ret = clk_prepare_enable(sprd_host->clk_enable);
94862306a36Sopenharmony_ci	if (ret)
94962306a36Sopenharmony_ci		goto clk_2x_disable;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	ret = clk_prepare_enable(sprd_host->clk_sdio);
95262306a36Sopenharmony_ci	if (ret)
95362306a36Sopenharmony_ci		goto clk_disable;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	sdhci_runtime_resume_host(host, 1);
95662306a36Sopenharmony_ci	mmc_hsq_resume(host->mmc);
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	return 0;
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ciclk_disable:
96162306a36Sopenharmony_ci	clk_disable_unprepare(sprd_host->clk_enable);
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ciclk_2x_disable:
96462306a36Sopenharmony_ci	clk_disable_unprepare(sprd_host->clk_2x_enable);
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	return ret;
96762306a36Sopenharmony_ci}
96862306a36Sopenharmony_ci#endif
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_cistatic const struct dev_pm_ops sdhci_sprd_pm_ops = {
97162306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
97262306a36Sopenharmony_ci				pm_runtime_force_resume)
97362306a36Sopenharmony_ci	SET_RUNTIME_PM_OPS(sdhci_sprd_runtime_suspend,
97462306a36Sopenharmony_ci			   sdhci_sprd_runtime_resume, NULL)
97562306a36Sopenharmony_ci};
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_cistatic struct platform_driver sdhci_sprd_driver = {
97862306a36Sopenharmony_ci	.probe = sdhci_sprd_probe,
97962306a36Sopenharmony_ci	.remove_new = sdhci_sprd_remove,
98062306a36Sopenharmony_ci	.driver = {
98162306a36Sopenharmony_ci		.name = "sdhci_sprd_r11",
98262306a36Sopenharmony_ci		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
98362306a36Sopenharmony_ci		.of_match_table = sdhci_sprd_of_match,
98462306a36Sopenharmony_ci		.pm = &sdhci_sprd_pm_ops,
98562306a36Sopenharmony_ci	},
98662306a36Sopenharmony_ci};
98762306a36Sopenharmony_cimodule_platform_driver(sdhci_sprd_driver);
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ciMODULE_DESCRIPTION("Spreadtrum sdio host controller r11 driver");
99062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
99162306a36Sopenharmony_ciMODULE_ALIAS("platform:sdhci-sprd-r11");
992