162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) Sunplus Inc.
462306a36Sopenharmony_ci * Author: Tony Huang <tonyhuang.sunplus@gmail.com>
562306a36Sopenharmony_ci * Author: Li-hao Kuo <lhjeff911@gmail.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/bitfield.h>
962306a36Sopenharmony_ci#include <linux/clk.h>
1062306a36Sopenharmony_ci#include <linux/delay.h>
1162306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/iopoll.h>
1462306a36Sopenharmony_ci#include <linux/mmc/core.h>
1562306a36Sopenharmony_ci#include <linux/mmc/host.h>
1662306a36Sopenharmony_ci#include <linux/mmc/mmc.h>
1762306a36Sopenharmony_ci#include <linux/mmc/sdio.h>
1862306a36Sopenharmony_ci#include <linux/mmc/slot-gpio.h>
1962306a36Sopenharmony_ci#include <linux/module.h>
2062306a36Sopenharmony_ci#include <linux/of.h>
2162306a36Sopenharmony_ci#include <linux/platform_device.h>
2262306a36Sopenharmony_ci#include <linux/pm.h>
2362306a36Sopenharmony_ci#include <linux/pm_runtime.h>
2462306a36Sopenharmony_ci#include <linux/reset.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define SPMMC_MIN_CLK			400000
2762306a36Sopenharmony_ci#define SPMMC_MAX_CLK			52000000
2862306a36Sopenharmony_ci#define SPMMC_MAX_BLK_COUNT		65536
2962306a36Sopenharmony_ci#define SPMMC_MAX_TUNABLE_DLY	7
3062306a36Sopenharmony_ci#define SPMMC_TIMEOUT_US		500000
3162306a36Sopenharmony_ci#define SPMMC_POLL_DELAY_US		10
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define SPMMC_CARD_MEDIATYPE_SRCDST_REG 0x0000
3462306a36Sopenharmony_ci#define SPMMC_MEDIA_TYPE		GENMASK(2, 0)
3562306a36Sopenharmony_ci#define SPMMC_DMA_SOURCE		GENMASK(6, 4)
3662306a36Sopenharmony_ci#define SPMMC_DMA_DESTINATION		GENMASK(10, 8)
3762306a36Sopenharmony_ci#define SPMMC_MEDIA_NONE	0
3862306a36Sopenharmony_ci#define SPMMC_MEDIA_SD		6
3962306a36Sopenharmony_ci#define SPMMC_MEDIA_MS		7
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define SPMMC_SDRAM_SECTOR_0_SIZE_REG	0x0008
4262306a36Sopenharmony_ci#define SPMMC_DMA_BASE_ADDR_REG		0x000C
4362306a36Sopenharmony_ci#define SPMMC_HW_DMA_CTRL_REG		0x0010
4462306a36Sopenharmony_ci#define SPMMC_HW_DMA_RST	BIT(9)
4562306a36Sopenharmony_ci#define SPMMC_DMAIDLE		BIT(10)
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define SPMMC_MAX_DMA_MEMORY_SECTORS	8
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#define SPMMC_SDRAM_SECTOR_1_ADDR_REG 0x0018
5062306a36Sopenharmony_ci#define SPMMC_SDRAM_SECTOR_1_LENG_REG 0x001C
5162306a36Sopenharmony_ci#define SPMMC_SDRAM_SECTOR_2_ADDR_REG 0x0020
5262306a36Sopenharmony_ci#define SPMMC_SDRAM_SECTOR_2_LENG_REG 0x0024
5362306a36Sopenharmony_ci#define SPMMC_SDRAM_SECTOR_3_ADDR_REG 0x0028
5462306a36Sopenharmony_ci#define SPMMC_SDRAM_SECTOR_3_LENG_REG 0x002C
5562306a36Sopenharmony_ci#define SPMMC_SDRAM_SECTOR_4_ADDR_REG 0x0030
5662306a36Sopenharmony_ci#define SPMMC_SDRAM_SECTOR_4_LENG_REG 0x0034
5762306a36Sopenharmony_ci#define SPMMC_SDRAM_SECTOR_5_ADDR_REG 0x0038
5862306a36Sopenharmony_ci#define SPMMC_SDRAM_SECTOR_5_LENG_REG 0x003C
5962306a36Sopenharmony_ci#define SPMMC_SDRAM_SECTOR_6_ADDR_REG 0x0040
6062306a36Sopenharmony_ci#define SPMMC_SDRAM_SECTOR_6_LENG_REG 0x0044
6162306a36Sopenharmony_ci#define SPMMC_SDRAM_SECTOR_7_ADDR_REG 0x0048
6262306a36Sopenharmony_ci#define SPMMC_SDRAM_SECTOR_7_LENG_REG 0x004C
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci#define SPMMC_SD_INT_REG	0x0088
6562306a36Sopenharmony_ci#define SPMMC_SDINT_SDCMPEN	BIT(0)
6662306a36Sopenharmony_ci#define SPMMC_SDINT_SDCMP	BIT(1)
6762306a36Sopenharmony_ci#define SPMMC_SDINT_SDCMPCLR	BIT(2)
6862306a36Sopenharmony_ci#define SPMMC_SDINT_SDIOEN	BIT(3)
6962306a36Sopenharmony_ci#define SPMMC_SDINT_SDIO	BIT(4)
7062306a36Sopenharmony_ci#define SPMMC_SDINT_SDIOCLR	BIT(5)
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci#define SPMMC_SD_PAGE_NUM_REG	0x008C
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci#define SPMMC_SD_CONFIG0_REG	0x0090
7562306a36Sopenharmony_ci#define SPMMC_SD_PIO_MODE	BIT(0)
7662306a36Sopenharmony_ci#define SPMMC_SD_DDR_MODE	BIT(1)
7762306a36Sopenharmony_ci#define SPMMC_SD_LEN_MODE	BIT(2)
7862306a36Sopenharmony_ci#define SPMMC_SD_TRANS_MODE	GENMASK(5, 4)
7962306a36Sopenharmony_ci#define SPMMC_SD_AUTO_RESPONSE	BIT(6)
8062306a36Sopenharmony_ci#define SPMMC_SD_CMD_DUMMY	BIT(7)
8162306a36Sopenharmony_ci#define SPMMC_SD_RSP_CHK_EN	BIT(8)
8262306a36Sopenharmony_ci#define SPMMC_SDIO_MODE		BIT(9)
8362306a36Sopenharmony_ci#define SPMMC_SD_MMC_MODE	BIT(10)
8462306a36Sopenharmony_ci#define SPMMC_SD_DATA_WD	BIT(11)
8562306a36Sopenharmony_ci#define SPMMC_RX4_EN		BIT(14)
8662306a36Sopenharmony_ci#define SPMMC_SD_RSP_TYPE	BIT(15)
8762306a36Sopenharmony_ci#define SPMMC_MMC8_EN		BIT(18)
8862306a36Sopenharmony_ci#define SPMMC_CLOCK_DIVISION	GENMASK(31, 20)
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci#define SPMMC_SDIO_CTRL_REG		0x0094
9162306a36Sopenharmony_ci#define SPMMC_INT_MULTI_TRIG		BIT(6)
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci#define SPMMC_SD_RST_REG		0x0098
9462306a36Sopenharmony_ci#define SPMMC_SD_CTRL_REG		0x009C
9562306a36Sopenharmony_ci#define SPMMC_NEW_COMMAND_TRIGGER	BIT(0)
9662306a36Sopenharmony_ci#define SPMMC_DUMMY_CLOCK_TRIGGER	BIT(1)
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci#define SPMMC_SD_STATUS_REG						0x00A0
9962306a36Sopenharmony_ci#define SPMMC_SDSTATUS_DUMMY_READY				BIT(0)
10062306a36Sopenharmony_ci#define SPMMC_SDSTATUS_RSP_BUF_FULL				BIT(1)
10162306a36Sopenharmony_ci#define SPMMC_SDSTATUS_TX_DATA_BUF_EMPTY		BIT(2)
10262306a36Sopenharmony_ci#define SPMMC_SDSTATUS_RX_DATA_BUF_FULL			BIT(3)
10362306a36Sopenharmony_ci#define SPMMC_SDSTATUS_CMD_PIN_STATUS			BIT(4)
10462306a36Sopenharmony_ci#define SPMMC_SDSTATUS_DAT0_PIN_STATUS			BIT(5)
10562306a36Sopenharmony_ci#define SPMMC_SDSTATUS_RSP_TIMEOUT				BIT(6)
10662306a36Sopenharmony_ci#define SPMMC_SDSTATUS_CARD_CRC_CHECK_TIMEOUT	BIT(7)
10762306a36Sopenharmony_ci#define SPMMC_SDSTATUS_STB_TIMEOUT				BIT(8)
10862306a36Sopenharmony_ci#define SPMMC_SDSTATUS_RSP_CRC7_ERROR			BIT(9)
10962306a36Sopenharmony_ci#define SPMMC_SDSTATUS_CRC_TOKEN_CHECK_ERROR	BIT(10)
11062306a36Sopenharmony_ci#define SPMMC_SDSTATUS_RDATA_CRC16_ERROR		BIT(11)
11162306a36Sopenharmony_ci#define SPMMC_SDSTATUS_SUSPEND_STATE_READY		BIT(12)
11262306a36Sopenharmony_ci#define SPMMC_SDSTATUS_BUSY_CYCLE				BIT(13)
11362306a36Sopenharmony_ci#define SPMMC_SDSTATUS_DAT1_PIN_STATUS			BIT(14)
11462306a36Sopenharmony_ci#define SPMMC_SDSTATUS_SD_SENSE_STATUS			BIT(15)
11562306a36Sopenharmony_ci#define SPMMC_SDSTATUS_BOOT_ACK_TIMEOUT			BIT(16)
11662306a36Sopenharmony_ci#define SPMMC_SDSTATUS_BOOT_DATA_TIMEOUT		BIT(17)
11762306a36Sopenharmony_ci#define SPMMC_SDSTATUS_BOOT_ACK_ERROR			BIT(18)
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci#define SPMMC_SD_STATE_REG		0x00A4
12062306a36Sopenharmony_ci#define SPMMC_CRCTOKEN_CHECK_RESULT	GENMASK(6, 4)
12162306a36Sopenharmony_ci#define SPMMC_SDSTATE_ERROR		BIT(13)
12262306a36Sopenharmony_ci#define SPMMC_SDSTATE_FINISH	BIT(14)
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci#define SPMMC_SD_HW_STATE_REG		0x00A8
12562306a36Sopenharmony_ci#define SPMMC_SD_BLOCKSIZE_REG		0x00AC
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci#define SPMMC_SD_CONFIG1_REG		0x00B0
12862306a36Sopenharmony_ci#define SPMMC_TX_DUMMY_NUM		GENMASK(8, 0)
12962306a36Sopenharmony_ci#define SPMMC_SD_HIGH_SPEED_EN		BIT(31)
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci#define SPMMC_SD_TIMING_CONFIG0_REG 0x00B4
13262306a36Sopenharmony_ci#define SPMMC_SD_CLOCK_DELAY	GENMASK(2, 0)
13362306a36Sopenharmony_ci#define SPMMC_SD_WRITE_DATA_DELAY	GENMASK(6, 4)
13462306a36Sopenharmony_ci#define SPMMC_SD_WRITE_COMMAND_DELAY	GENMASK(10, 8)
13562306a36Sopenharmony_ci#define SPMMC_SD_READ_RESPONSE_DELAY	GENMASK(14, 12)
13662306a36Sopenharmony_ci#define SPMMC_SD_READ_DATA_DELAY	GENMASK(18, 16)
13762306a36Sopenharmony_ci#define SPMMC_SD_READ_CRC_DELAY	GENMASK(22, 20)
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci#define SPMMC_SD_PIODATATX_REG		0x00BC
14062306a36Sopenharmony_ci#define SPMMC_SD_PIODATARX_REG		0x00C0
14162306a36Sopenharmony_ci#define SPMMC_SD_CMDBUF0_3_REG		0x00C4
14262306a36Sopenharmony_ci#define SPMMC_SD_CMDBUF4_REG		0x00C8
14362306a36Sopenharmony_ci#define SPMMC_SD_RSPBUF0_3_REG		0x00CC
14462306a36Sopenharmony_ci#define SPMMC_SD_RSPBUF4_5_REG		0x00D0
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci#define SPMMC_MAX_RETRIES (8 * 8)
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistruct spmmc_tuning_info {
14962306a36Sopenharmony_ci	int enable_tuning;
15062306a36Sopenharmony_ci	int need_tuning;
15162306a36Sopenharmony_ci	int retried; /* how many times has been retried */
15262306a36Sopenharmony_ci	u32 rd_crc_dly:3;
15362306a36Sopenharmony_ci	u32 rd_dat_dly:3;
15462306a36Sopenharmony_ci	u32 rd_rsp_dly:3;
15562306a36Sopenharmony_ci	u32 wr_cmd_dly:3;
15662306a36Sopenharmony_ci	u32 wr_dat_dly:3;
15762306a36Sopenharmony_ci	u32 clk_dly:3;
15862306a36Sopenharmony_ci};
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci#define SPMMC_DMA_MODE 0
16162306a36Sopenharmony_ci#define	SPMMC_PIO_MODE 1
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistruct spmmc_host {
16462306a36Sopenharmony_ci	void __iomem *base;
16562306a36Sopenharmony_ci	struct clk *clk;
16662306a36Sopenharmony_ci	struct reset_control *rstc;
16762306a36Sopenharmony_ci	struct mmc_host *mmc;
16862306a36Sopenharmony_ci	struct mmc_request *mrq; /* current mrq */
16962306a36Sopenharmony_ci	int irq;
17062306a36Sopenharmony_ci	int dmapio_mode;
17162306a36Sopenharmony_ci	struct spmmc_tuning_info tuning_info;
17262306a36Sopenharmony_ci	int dma_int_threshold;
17362306a36Sopenharmony_ci	int dma_use_int;
17462306a36Sopenharmony_ci};
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic inline int spmmc_wait_finish(struct spmmc_host *host)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	u32 state;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	return readl_poll_timeout(host->base + SPMMC_SD_STATE_REG, state,
18162306a36Sopenharmony_ci					(state & SPMMC_SDSTATE_FINISH),
18262306a36Sopenharmony_ci					SPMMC_POLL_DELAY_US, SPMMC_TIMEOUT_US);
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic inline int spmmc_wait_sdstatus(struct spmmc_host *host, unsigned int status_bit)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	u32 status;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	return readl_poll_timeout(host->base + SPMMC_SD_STATUS_REG, status,
19062306a36Sopenharmony_ci					(status & status_bit),
19162306a36Sopenharmony_ci					SPMMC_POLL_DELAY_US, SPMMC_TIMEOUT_US);
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci#define spmmc_wait_rspbuf_full(host) spmmc_wait_sdstatus(host, SPMMC_SDSTATUS_RSP_BUF_FULL)
19562306a36Sopenharmony_ci#define spmmc_wait_rxbuf_full(host) spmmc_wait_sdstatus(host, SPMMC_SDSTATUS_RX_DATA_BUF_FULL)
19662306a36Sopenharmony_ci#define spmmc_wait_txbuf_empty(host) spmmc_wait_sdstatus(host, SPMMC_SDSTATUS_TX_DATA_BUF_EMPTY)
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic void spmmc_get_rsp(struct spmmc_host *host, struct mmc_command *cmd)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	u32 value0_3, value4_5;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	if (!(cmd->flags & MMC_RSP_PRESENT))
20362306a36Sopenharmony_ci		return;
20462306a36Sopenharmony_ci	if (cmd->flags & MMC_RSP_136) {
20562306a36Sopenharmony_ci		if (spmmc_wait_rspbuf_full(host))
20662306a36Sopenharmony_ci			return;
20762306a36Sopenharmony_ci		value0_3 = readl(host->base + SPMMC_SD_RSPBUF0_3_REG);
20862306a36Sopenharmony_ci		value4_5 = readl(host->base + SPMMC_SD_RSPBUF4_5_REG) & 0xffff;
20962306a36Sopenharmony_ci		cmd->resp[0] = (value0_3 << 8) | (value4_5 >> 8);
21062306a36Sopenharmony_ci		cmd->resp[1] = value4_5 << 24;
21162306a36Sopenharmony_ci		value0_3 = readl(host->base + SPMMC_SD_RSPBUF0_3_REG);
21262306a36Sopenharmony_ci		value4_5 = readl(host->base + SPMMC_SD_RSPBUF4_5_REG) & 0xffff;
21362306a36Sopenharmony_ci		cmd->resp[1] |= value0_3 >> 8;
21462306a36Sopenharmony_ci		cmd->resp[2] = value0_3 << 24;
21562306a36Sopenharmony_ci		cmd->resp[2] |= value4_5 << 8;
21662306a36Sopenharmony_ci		value0_3 = readl(host->base + SPMMC_SD_RSPBUF0_3_REG);
21762306a36Sopenharmony_ci		value4_5 = readl(host->base + SPMMC_SD_RSPBUF4_5_REG) & 0xffff;
21862306a36Sopenharmony_ci		cmd->resp[2] |= value0_3 >> 24;
21962306a36Sopenharmony_ci		cmd->resp[3] = value0_3 << 8;
22062306a36Sopenharmony_ci		cmd->resp[3] |= value4_5 >> 8;
22162306a36Sopenharmony_ci	} else {
22262306a36Sopenharmony_ci		if (spmmc_wait_rspbuf_full(host))
22362306a36Sopenharmony_ci			return;
22462306a36Sopenharmony_ci		value0_3 = readl(host->base + SPMMC_SD_RSPBUF0_3_REG);
22562306a36Sopenharmony_ci		value4_5 = readl(host->base + SPMMC_SD_RSPBUF4_5_REG) & 0xffff;
22662306a36Sopenharmony_ci		cmd->resp[0] = (value0_3 << 8) | (value4_5 >> 8);
22762306a36Sopenharmony_ci		cmd->resp[1] = value4_5 << 24;
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic void spmmc_set_bus_clk(struct spmmc_host *host, int clk)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	unsigned int clkdiv;
23462306a36Sopenharmony_ci	int f_min = host->mmc->f_min;
23562306a36Sopenharmony_ci	int f_max = host->mmc->f_max;
23662306a36Sopenharmony_ci	u32 value = readl(host->base + SPMMC_SD_CONFIG0_REG);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	if (clk < f_min)
23962306a36Sopenharmony_ci		clk = f_min;
24062306a36Sopenharmony_ci	if (clk > f_max)
24162306a36Sopenharmony_ci		clk = f_max;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	clkdiv = (clk_get_rate(host->clk) + clk) / clk - 1;
24462306a36Sopenharmony_ci	if (clkdiv > 0xfff)
24562306a36Sopenharmony_ci		clkdiv = 0xfff;
24662306a36Sopenharmony_ci	value &= ~SPMMC_CLOCK_DIVISION;
24762306a36Sopenharmony_ci	value |= FIELD_PREP(SPMMC_CLOCK_DIVISION, clkdiv);
24862306a36Sopenharmony_ci	writel(value, host->base + SPMMC_SD_CONFIG0_REG);
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic void spmmc_set_bus_timing(struct spmmc_host *host, unsigned int timing)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	u32 value = readl(host->base + SPMMC_SD_CONFIG1_REG);
25462306a36Sopenharmony_ci	int clkdiv = FIELD_GET(SPMMC_CLOCK_DIVISION, readl(host->base + SPMMC_SD_CONFIG0_REG));
25562306a36Sopenharmony_ci	int delay = clkdiv / 2 < 7 ? clkdiv / 2 : 7;
25662306a36Sopenharmony_ci	int hs_en = 1, ddr_enabled = 0;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	switch (timing) {
25962306a36Sopenharmony_ci	case MMC_TIMING_LEGACY:
26062306a36Sopenharmony_ci		hs_en = 0;
26162306a36Sopenharmony_ci		break;
26262306a36Sopenharmony_ci	case MMC_TIMING_MMC_HS:
26362306a36Sopenharmony_ci	case MMC_TIMING_SD_HS:
26462306a36Sopenharmony_ci	case MMC_TIMING_UHS_SDR50:
26562306a36Sopenharmony_ci	case MMC_TIMING_UHS_SDR104:
26662306a36Sopenharmony_ci	case MMC_TIMING_MMC_HS200:
26762306a36Sopenharmony_ci		hs_en = 1;
26862306a36Sopenharmony_ci		break;
26962306a36Sopenharmony_ci	case MMC_TIMING_UHS_DDR50:
27062306a36Sopenharmony_ci		ddr_enabled = 1;
27162306a36Sopenharmony_ci		break;
27262306a36Sopenharmony_ci	case MMC_TIMING_MMC_DDR52:
27362306a36Sopenharmony_ci		ddr_enabled = 1;
27462306a36Sopenharmony_ci		break;
27562306a36Sopenharmony_ci	default:
27662306a36Sopenharmony_ci		hs_en = 0;
27762306a36Sopenharmony_ci		break;
27862306a36Sopenharmony_ci	}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	if (hs_en) {
28162306a36Sopenharmony_ci		value |= SPMMC_SD_HIGH_SPEED_EN;
28262306a36Sopenharmony_ci		writel(value, host->base + SPMMC_SD_CONFIG1_REG);
28362306a36Sopenharmony_ci		value = readl(host->base + SPMMC_SD_TIMING_CONFIG0_REG);
28462306a36Sopenharmony_ci		value &= ~SPMMC_SD_WRITE_DATA_DELAY;
28562306a36Sopenharmony_ci		value |= FIELD_PREP(SPMMC_SD_WRITE_DATA_DELAY, delay);
28662306a36Sopenharmony_ci		value &= ~SPMMC_SD_WRITE_COMMAND_DELAY;
28762306a36Sopenharmony_ci		value |= FIELD_PREP(SPMMC_SD_WRITE_COMMAND_DELAY, delay);
28862306a36Sopenharmony_ci		writel(value, host->base + SPMMC_SD_TIMING_CONFIG0_REG);
28962306a36Sopenharmony_ci	} else {
29062306a36Sopenharmony_ci		value &= ~SPMMC_SD_HIGH_SPEED_EN;
29162306a36Sopenharmony_ci		writel(value, host->base + SPMMC_SD_CONFIG1_REG);
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci	if (ddr_enabled) {
29462306a36Sopenharmony_ci		value = readl(host->base + SPMMC_SD_CONFIG0_REG);
29562306a36Sopenharmony_ci		value |= SPMMC_SD_DDR_MODE;
29662306a36Sopenharmony_ci		writel(value, host->base + SPMMC_SD_CONFIG0_REG);
29762306a36Sopenharmony_ci	} else {
29862306a36Sopenharmony_ci		value = readl(host->base + SPMMC_SD_CONFIG0_REG);
29962306a36Sopenharmony_ci		value &= ~SPMMC_SD_DDR_MODE;
30062306a36Sopenharmony_ci		writel(value, host->base + SPMMC_SD_CONFIG0_REG);
30162306a36Sopenharmony_ci	}
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic void spmmc_set_bus_width(struct spmmc_host *host, int width)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	u32 value = readl(host->base + SPMMC_SD_CONFIG0_REG);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	switch (width) {
30962306a36Sopenharmony_ci	case MMC_BUS_WIDTH_8:
31062306a36Sopenharmony_ci		value &= ~SPMMC_SD_DATA_WD;
31162306a36Sopenharmony_ci		value |= SPMMC_MMC8_EN;
31262306a36Sopenharmony_ci		break;
31362306a36Sopenharmony_ci	case MMC_BUS_WIDTH_4:
31462306a36Sopenharmony_ci		value |= SPMMC_SD_DATA_WD;
31562306a36Sopenharmony_ci		value &= ~SPMMC_MMC8_EN;
31662306a36Sopenharmony_ci		break;
31762306a36Sopenharmony_ci	default:
31862306a36Sopenharmony_ci		value &= ~SPMMC_SD_DATA_WD;
31962306a36Sopenharmony_ci		value &= ~SPMMC_MMC8_EN;
32062306a36Sopenharmony_ci		break;
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci	writel(value, host->base + SPMMC_SD_CONFIG0_REG);
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci/*
32662306a36Sopenharmony_ci * select the working mode of controller: sd/sdio/emmc
32762306a36Sopenharmony_ci */
32862306a36Sopenharmony_cistatic void spmmc_set_sdmmc_mode(struct spmmc_host *host)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	u32 value = readl(host->base + SPMMC_SD_CONFIG0_REG);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	value |= SPMMC_SD_MMC_MODE;
33362306a36Sopenharmony_ci	value &= ~SPMMC_SDIO_MODE;
33462306a36Sopenharmony_ci	writel(value, host->base + SPMMC_SD_CONFIG0_REG);
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic void spmmc_sw_reset(struct spmmc_host *host)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	u32 value;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	/*
34262306a36Sopenharmony_ci	 * Must reset dma operation first, or it will
34362306a36Sopenharmony_ci	 * be stuck on sd_state == 0x1c00 because of
34462306a36Sopenharmony_ci	 * a controller software reset bug
34562306a36Sopenharmony_ci	 */
34662306a36Sopenharmony_ci	value = readl(host->base + SPMMC_HW_DMA_CTRL_REG);
34762306a36Sopenharmony_ci	value |= SPMMC_DMAIDLE;
34862306a36Sopenharmony_ci	writel(value, host->base + SPMMC_HW_DMA_CTRL_REG);
34962306a36Sopenharmony_ci	value &= ~SPMMC_DMAIDLE;
35062306a36Sopenharmony_ci	writel(value, host->base + SPMMC_HW_DMA_CTRL_REG);
35162306a36Sopenharmony_ci	value = readl(host->base + SPMMC_HW_DMA_CTRL_REG);
35262306a36Sopenharmony_ci	value |= SPMMC_HW_DMA_RST;
35362306a36Sopenharmony_ci	writel(value, host->base + SPMMC_HW_DMA_CTRL_REG);
35462306a36Sopenharmony_ci	writel(0x7, host->base + SPMMC_SD_RST_REG);
35562306a36Sopenharmony_ci	readl_poll_timeout_atomic(host->base + SPMMC_SD_HW_STATE_REG, value,
35662306a36Sopenharmony_ci				  !(value & BIT(6)), 1, SPMMC_TIMEOUT_US);
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic void spmmc_prepare_cmd(struct spmmc_host *host, struct mmc_command *cmd)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	u32 value;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	/* add start bit, according to spec, command format */
36462306a36Sopenharmony_ci	value = ((cmd->opcode | 0x40) << 24) | (cmd->arg >> 8);
36562306a36Sopenharmony_ci	writel(value, host->base + SPMMC_SD_CMDBUF0_3_REG);
36662306a36Sopenharmony_ci	writeb(cmd->arg & 0xff, host->base + SPMMC_SD_CMDBUF4_REG);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	/* disable interrupt if needed */
36962306a36Sopenharmony_ci	value = readl(host->base + SPMMC_SD_INT_REG);
37062306a36Sopenharmony_ci	value |= SPMMC_SDINT_SDCMPCLR;
37162306a36Sopenharmony_ci	value &= ~SPMMC_SDINT_SDCMPEN;
37262306a36Sopenharmony_ci	writel(value, host->base + SPMMC_SD_INT_REG);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	value = readl(host->base + SPMMC_SD_CONFIG0_REG);
37562306a36Sopenharmony_ci	value &= ~SPMMC_SD_TRANS_MODE;
37662306a36Sopenharmony_ci	value |= SPMMC_SD_CMD_DUMMY;
37762306a36Sopenharmony_ci	if (cmd->flags & MMC_RSP_PRESENT) {
37862306a36Sopenharmony_ci		value |= SPMMC_SD_AUTO_RESPONSE;
37962306a36Sopenharmony_ci	} else {
38062306a36Sopenharmony_ci		value &= ~SPMMC_SD_AUTO_RESPONSE;
38162306a36Sopenharmony_ci		writel(value, host->base + SPMMC_SD_CONFIG0_REG);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci		return;
38462306a36Sopenharmony_ci	}
38562306a36Sopenharmony_ci	/*
38662306a36Sopenharmony_ci	 * Currently, host is not capable of checking R2's CRC7,
38762306a36Sopenharmony_ci	 * thus, enable crc7 check only for 48 bit response commands
38862306a36Sopenharmony_ci	 */
38962306a36Sopenharmony_ci	if (cmd->flags & MMC_RSP_CRC && !(cmd->flags & MMC_RSP_136))
39062306a36Sopenharmony_ci		value |= SPMMC_SD_RSP_CHK_EN;
39162306a36Sopenharmony_ci	else
39262306a36Sopenharmony_ci		value &= ~SPMMC_SD_RSP_CHK_EN;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	if (cmd->flags & MMC_RSP_136)
39562306a36Sopenharmony_ci		value |= SPMMC_SD_RSP_TYPE;
39662306a36Sopenharmony_ci	else
39762306a36Sopenharmony_ci		value &= ~SPMMC_SD_RSP_TYPE;
39862306a36Sopenharmony_ci	writel(value, host->base + SPMMC_SD_CONFIG0_REG);
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cistatic void spmmc_prepare_data(struct spmmc_host *host, struct mmc_data *data)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	u32 value, srcdst;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	writel(data->blocks - 1, host->base + SPMMC_SD_PAGE_NUM_REG);
40662306a36Sopenharmony_ci	writel(data->blksz - 1, host->base + SPMMC_SD_BLOCKSIZE_REG);
40762306a36Sopenharmony_ci	value = readl(host->base + SPMMC_SD_CONFIG0_REG);
40862306a36Sopenharmony_ci	if (data->flags & MMC_DATA_READ) {
40962306a36Sopenharmony_ci		value &= ~SPMMC_SD_TRANS_MODE;
41062306a36Sopenharmony_ci		value |= FIELD_PREP(SPMMC_SD_TRANS_MODE, 2);
41162306a36Sopenharmony_ci		value &= ~SPMMC_SD_AUTO_RESPONSE;
41262306a36Sopenharmony_ci		value &= ~SPMMC_SD_CMD_DUMMY;
41362306a36Sopenharmony_ci		srcdst = readl(host->base + SPMMC_CARD_MEDIATYPE_SRCDST_REG);
41462306a36Sopenharmony_ci		srcdst &= ~SPMMC_DMA_SOURCE;
41562306a36Sopenharmony_ci		srcdst |= FIELD_PREP(SPMMC_DMA_SOURCE, 0x2);
41662306a36Sopenharmony_ci		srcdst &= ~SPMMC_DMA_DESTINATION;
41762306a36Sopenharmony_ci		srcdst |= FIELD_PREP(SPMMC_DMA_DESTINATION, 0x1);
41862306a36Sopenharmony_ci		writel(srcdst, host->base + SPMMC_CARD_MEDIATYPE_SRCDST_REG);
41962306a36Sopenharmony_ci	} else {
42062306a36Sopenharmony_ci		value &= ~SPMMC_SD_TRANS_MODE;
42162306a36Sopenharmony_ci		value |= FIELD_PREP(SPMMC_SD_TRANS_MODE, 1);
42262306a36Sopenharmony_ci		srcdst = readl(host->base + SPMMC_CARD_MEDIATYPE_SRCDST_REG);
42362306a36Sopenharmony_ci		srcdst &= ~SPMMC_DMA_SOURCE;
42462306a36Sopenharmony_ci		srcdst |= FIELD_PREP(SPMMC_DMA_SOURCE, 0x1);
42562306a36Sopenharmony_ci		srcdst &= ~SPMMC_DMA_DESTINATION;
42662306a36Sopenharmony_ci		srcdst |= FIELD_PREP(SPMMC_DMA_DESTINATION, 0x2);
42762306a36Sopenharmony_ci		writel(srcdst, host->base + SPMMC_CARD_MEDIATYPE_SRCDST_REG);
42862306a36Sopenharmony_ci	}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	value |= SPMMC_SD_LEN_MODE;
43162306a36Sopenharmony_ci	if (host->dmapio_mode == SPMMC_DMA_MODE) {
43262306a36Sopenharmony_ci		struct scatterlist *sg;
43362306a36Sopenharmony_ci		dma_addr_t dma_addr;
43462306a36Sopenharmony_ci		unsigned int dma_size;
43562306a36Sopenharmony_ci		int i, count = 1;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci		count = dma_map_sg(host->mmc->parent, data->sg, data->sg_len,
43862306a36Sopenharmony_ci				   mmc_get_dma_dir(data));
43962306a36Sopenharmony_ci		if (!count || count > SPMMC_MAX_DMA_MEMORY_SECTORS) {
44062306a36Sopenharmony_ci			data->error = -EINVAL;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci			return;
44362306a36Sopenharmony_ci		}
44462306a36Sopenharmony_ci		for_each_sg(data->sg, sg, count, i) {
44562306a36Sopenharmony_ci			dma_addr = sg_dma_address(sg);
44662306a36Sopenharmony_ci			dma_size = sg_dma_len(sg) / data->blksz - 1;
44762306a36Sopenharmony_ci			if (i == 0) {
44862306a36Sopenharmony_ci				writel(dma_addr, host->base + SPMMC_DMA_BASE_ADDR_REG);
44962306a36Sopenharmony_ci				writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_0_SIZE_REG);
45062306a36Sopenharmony_ci			} else if (i == 1) {
45162306a36Sopenharmony_ci				writel(dma_addr, host->base + SPMMC_SDRAM_SECTOR_1_ADDR_REG);
45262306a36Sopenharmony_ci				writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_1_LENG_REG);
45362306a36Sopenharmony_ci			} else if (i == 2) {
45462306a36Sopenharmony_ci				writel(dma_addr, host->base + SPMMC_SDRAM_SECTOR_2_ADDR_REG);
45562306a36Sopenharmony_ci				writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_2_LENG_REG);
45662306a36Sopenharmony_ci			} else if (i == 3) {
45762306a36Sopenharmony_ci				writel(dma_addr, host->base + SPMMC_SDRAM_SECTOR_3_ADDR_REG);
45862306a36Sopenharmony_ci				writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_3_LENG_REG);
45962306a36Sopenharmony_ci			} else if (i == 4) {
46062306a36Sopenharmony_ci				writel(dma_addr, host->base + SPMMC_SDRAM_SECTOR_4_ADDR_REG);
46162306a36Sopenharmony_ci				writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_4_LENG_REG);
46262306a36Sopenharmony_ci			} else if (i == 5) {
46362306a36Sopenharmony_ci				writel(dma_addr, host->base + SPMMC_SDRAM_SECTOR_5_ADDR_REG);
46462306a36Sopenharmony_ci				writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_5_LENG_REG);
46562306a36Sopenharmony_ci			} else if (i == 6) {
46662306a36Sopenharmony_ci				writel(dma_addr, host->base + SPMMC_SDRAM_SECTOR_6_ADDR_REG);
46762306a36Sopenharmony_ci				writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_6_LENG_REG);
46862306a36Sopenharmony_ci			} else if (i == 7) {
46962306a36Sopenharmony_ci				writel(dma_addr, host->base + SPMMC_SDRAM_SECTOR_7_ADDR_REG);
47062306a36Sopenharmony_ci				writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_7_LENG_REG);
47162306a36Sopenharmony_ci			}
47262306a36Sopenharmony_ci		}
47362306a36Sopenharmony_ci		value &= ~SPMMC_SD_PIO_MODE;
47462306a36Sopenharmony_ci		writel(value, host->base + SPMMC_SD_CONFIG0_REG);
47562306a36Sopenharmony_ci		/* enable interrupt if needed */
47662306a36Sopenharmony_ci		if (data->blksz * data->blocks > host->dma_int_threshold) {
47762306a36Sopenharmony_ci			host->dma_use_int = 1;
47862306a36Sopenharmony_ci			value = readl(host->base + SPMMC_SD_INT_REG);
47962306a36Sopenharmony_ci			value &= ~SPMMC_SDINT_SDCMPEN;
48062306a36Sopenharmony_ci			value |= FIELD_PREP(SPMMC_SDINT_SDCMPEN, 1); /* sdcmpen */
48162306a36Sopenharmony_ci			writel(value, host->base + SPMMC_SD_INT_REG);
48262306a36Sopenharmony_ci		}
48362306a36Sopenharmony_ci	} else {
48462306a36Sopenharmony_ci		value |= SPMMC_SD_PIO_MODE;
48562306a36Sopenharmony_ci		value |= SPMMC_RX4_EN;
48662306a36Sopenharmony_ci		writel(value, host->base + SPMMC_SD_CONFIG0_REG);
48762306a36Sopenharmony_ci	}
48862306a36Sopenharmony_ci}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_cistatic inline void spmmc_trigger_transaction(struct spmmc_host *host)
49162306a36Sopenharmony_ci{
49262306a36Sopenharmony_ci	u32 value = readl(host->base + SPMMC_SD_CTRL_REG);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	value |= SPMMC_NEW_COMMAND_TRIGGER;
49562306a36Sopenharmony_ci	writel(value, host->base + SPMMC_SD_CTRL_REG);
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_cistatic void spmmc_send_stop_cmd(struct spmmc_host *host)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	struct mmc_command stop = {};
50162306a36Sopenharmony_ci	u32 value;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	stop.opcode = MMC_STOP_TRANSMISSION;
50462306a36Sopenharmony_ci	stop.arg = 0;
50562306a36Sopenharmony_ci	stop.flags = MMC_RSP_R1B;
50662306a36Sopenharmony_ci	spmmc_prepare_cmd(host, &stop);
50762306a36Sopenharmony_ci	value = readl(host->base + SPMMC_SD_INT_REG);
50862306a36Sopenharmony_ci	value &= ~SPMMC_SDINT_SDCMPEN;
50962306a36Sopenharmony_ci	value |= FIELD_PREP(SPMMC_SDINT_SDCMPEN, 0);
51062306a36Sopenharmony_ci	writel(value, host->base + SPMMC_SD_INT_REG);
51162306a36Sopenharmony_ci	spmmc_trigger_transaction(host);
51262306a36Sopenharmony_ci	readl_poll_timeout(host->base + SPMMC_SD_STATE_REG, value,
51362306a36Sopenharmony_ci			   (value & SPMMC_SDSTATE_FINISH), 1, SPMMC_TIMEOUT_US);
51462306a36Sopenharmony_ci}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_cistatic int spmmc_check_error(struct spmmc_host *host, struct mmc_request *mrq)
51762306a36Sopenharmony_ci{
51862306a36Sopenharmony_ci	int ret = 0;
51962306a36Sopenharmony_ci	struct mmc_command *cmd = mrq->cmd;
52062306a36Sopenharmony_ci	struct mmc_data *data = mrq->data;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	u32 value = readl(host->base + SPMMC_SD_STATE_REG);
52362306a36Sopenharmony_ci	u32 crc_token = FIELD_GET(SPMMC_CRCTOKEN_CHECK_RESULT, value);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	if (value & SPMMC_SDSTATE_ERROR) {
52662306a36Sopenharmony_ci		u32 timing_cfg0 = 0;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci		value = readl(host->base + SPMMC_SD_STATUS_REG);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci		if (host->tuning_info.enable_tuning) {
53162306a36Sopenharmony_ci			timing_cfg0 = readl(host->base + SPMMC_SD_TIMING_CONFIG0_REG);
53262306a36Sopenharmony_ci			host->tuning_info.rd_crc_dly = FIELD_GET(SPMMC_SD_READ_CRC_DELAY,
53362306a36Sopenharmony_ci								 timing_cfg0);
53462306a36Sopenharmony_ci			host->tuning_info.rd_dat_dly = FIELD_GET(SPMMC_SD_READ_DATA_DELAY,
53562306a36Sopenharmony_ci								 timing_cfg0);
53662306a36Sopenharmony_ci			host->tuning_info.rd_rsp_dly = FIELD_GET(SPMMC_SD_READ_RESPONSE_DELAY,
53762306a36Sopenharmony_ci								 timing_cfg0);
53862306a36Sopenharmony_ci			host->tuning_info.wr_cmd_dly = FIELD_GET(SPMMC_SD_WRITE_COMMAND_DELAY,
53962306a36Sopenharmony_ci								 timing_cfg0);
54062306a36Sopenharmony_ci			host->tuning_info.wr_dat_dly = FIELD_GET(SPMMC_SD_WRITE_DATA_DELAY,
54162306a36Sopenharmony_ci								 timing_cfg0);
54262306a36Sopenharmony_ci		}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci		if (value & SPMMC_SDSTATUS_RSP_TIMEOUT) {
54562306a36Sopenharmony_ci			ret = -ETIMEDOUT;
54662306a36Sopenharmony_ci			host->tuning_info.wr_cmd_dly++;
54762306a36Sopenharmony_ci		} else if (value & SPMMC_SDSTATUS_RSP_CRC7_ERROR) {
54862306a36Sopenharmony_ci			ret = -EILSEQ;
54962306a36Sopenharmony_ci			host->tuning_info.rd_rsp_dly++;
55062306a36Sopenharmony_ci		} else if (data) {
55162306a36Sopenharmony_ci			if ((value & SPMMC_SDSTATUS_STB_TIMEOUT)) {
55262306a36Sopenharmony_ci				ret = -ETIMEDOUT;
55362306a36Sopenharmony_ci				host->tuning_info.rd_dat_dly++;
55462306a36Sopenharmony_ci			} else if (value & SPMMC_SDSTATUS_RDATA_CRC16_ERROR) {
55562306a36Sopenharmony_ci				ret = -EILSEQ;
55662306a36Sopenharmony_ci				host->tuning_info.rd_dat_dly++;
55762306a36Sopenharmony_ci			} else if (value & SPMMC_SDSTATUS_CARD_CRC_CHECK_TIMEOUT) {
55862306a36Sopenharmony_ci				ret = -ETIMEDOUT;
55962306a36Sopenharmony_ci				host->tuning_info.rd_crc_dly++;
56062306a36Sopenharmony_ci			} else if (value & SPMMC_SDSTATUS_CRC_TOKEN_CHECK_ERROR) {
56162306a36Sopenharmony_ci				ret = -EILSEQ;
56262306a36Sopenharmony_ci				if (crc_token == 0x5)
56362306a36Sopenharmony_ci					host->tuning_info.wr_dat_dly++;
56462306a36Sopenharmony_ci				else
56562306a36Sopenharmony_ci					host->tuning_info.rd_crc_dly++;
56662306a36Sopenharmony_ci			}
56762306a36Sopenharmony_ci		}
56862306a36Sopenharmony_ci		cmd->error = ret;
56962306a36Sopenharmony_ci		if (data) {
57062306a36Sopenharmony_ci			data->error = ret;
57162306a36Sopenharmony_ci			data->bytes_xfered = 0;
57262306a36Sopenharmony_ci		}
57362306a36Sopenharmony_ci		if (!host->tuning_info.need_tuning && host->tuning_info.enable_tuning)
57462306a36Sopenharmony_ci			cmd->retries = SPMMC_MAX_RETRIES;
57562306a36Sopenharmony_ci		spmmc_sw_reset(host);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci		if (host->tuning_info.enable_tuning) {
57862306a36Sopenharmony_ci			timing_cfg0 &= ~SPMMC_SD_READ_CRC_DELAY;
57962306a36Sopenharmony_ci			timing_cfg0 |= FIELD_PREP(SPMMC_SD_READ_CRC_DELAY,
58062306a36Sopenharmony_ci						       host->tuning_info.rd_crc_dly);
58162306a36Sopenharmony_ci			timing_cfg0 &= ~SPMMC_SD_READ_DATA_DELAY;
58262306a36Sopenharmony_ci			timing_cfg0 |= FIELD_PREP(SPMMC_SD_READ_DATA_DELAY,
58362306a36Sopenharmony_ci						       host->tuning_info.rd_dat_dly);
58462306a36Sopenharmony_ci			timing_cfg0 &= ~SPMMC_SD_READ_RESPONSE_DELAY;
58562306a36Sopenharmony_ci			timing_cfg0 |= FIELD_PREP(SPMMC_SD_READ_RESPONSE_DELAY,
58662306a36Sopenharmony_ci						       host->tuning_info.rd_rsp_dly);
58762306a36Sopenharmony_ci			timing_cfg0 &= ~SPMMC_SD_WRITE_COMMAND_DELAY;
58862306a36Sopenharmony_ci			timing_cfg0 |= FIELD_PREP(SPMMC_SD_WRITE_COMMAND_DELAY,
58962306a36Sopenharmony_ci						       host->tuning_info.wr_cmd_dly);
59062306a36Sopenharmony_ci			timing_cfg0 &= ~SPMMC_SD_WRITE_DATA_DELAY;
59162306a36Sopenharmony_ci			timing_cfg0 |= FIELD_PREP(SPMMC_SD_WRITE_DATA_DELAY,
59262306a36Sopenharmony_ci						       host->tuning_info.wr_dat_dly);
59362306a36Sopenharmony_ci			writel(timing_cfg0, host->base + SPMMC_SD_TIMING_CONFIG0_REG);
59462306a36Sopenharmony_ci		}
59562306a36Sopenharmony_ci	} else if (data) {
59662306a36Sopenharmony_ci		data->error = 0;
59762306a36Sopenharmony_ci		data->bytes_xfered = data->blocks * data->blksz;
59862306a36Sopenharmony_ci	}
59962306a36Sopenharmony_ci	host->tuning_info.need_tuning = ret;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	return ret;
60262306a36Sopenharmony_ci}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci/*
60562306a36Sopenharmony_ci * the strategy is:
60662306a36Sopenharmony_ci * 1. if several continuous delays are acceptable, we choose a middle one;
60762306a36Sopenharmony_ci * 2. otherwise, we choose the first one.
60862306a36Sopenharmony_ci */
60962306a36Sopenharmony_cistatic inline int spmmc_find_best_delay(u8 candidate_dly)
61062306a36Sopenharmony_ci{
61162306a36Sopenharmony_ci	int f, w, value;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	if (!candidate_dly)
61462306a36Sopenharmony_ci		return 0;
61562306a36Sopenharmony_ci	f = ffs(candidate_dly) - 1;
61662306a36Sopenharmony_ci	w = hweight8(candidate_dly);
61762306a36Sopenharmony_ci	value = ((1 << w) - 1) << f;
61862306a36Sopenharmony_ci	if (0xff == (value & ~candidate_dly))
61962306a36Sopenharmony_ci		return (f + w / 2);
62062306a36Sopenharmony_ci	else
62162306a36Sopenharmony_ci		return (f);
62262306a36Sopenharmony_ci}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_cistatic void spmmc_xfer_data_pio(struct spmmc_host *host, struct mmc_data *data)
62562306a36Sopenharmony_ci{
62662306a36Sopenharmony_ci	u32 *buf;
62762306a36Sopenharmony_ci	int data_left = data->blocks * data->blksz;
62862306a36Sopenharmony_ci	int consumed, remain;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	struct sg_mapping_iter sg_miter;
63162306a36Sopenharmony_ci	unsigned int flags = 0;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	if (data->flags & MMC_DATA_WRITE)
63462306a36Sopenharmony_ci		flags |= SG_MITER_FROM_SG;
63562306a36Sopenharmony_ci	else
63662306a36Sopenharmony_ci		flags |= SG_MITER_TO_SG;
63762306a36Sopenharmony_ci	sg_miter_start(&sg_miter, data->sg, data->sg_len, flags);
63862306a36Sopenharmony_ci	while (data_left > 0) {
63962306a36Sopenharmony_ci		consumed = 0;
64062306a36Sopenharmony_ci		if (!sg_miter_next(&sg_miter))
64162306a36Sopenharmony_ci			break;
64262306a36Sopenharmony_ci		buf = sg_miter.addr;
64362306a36Sopenharmony_ci		remain = sg_miter.length;
64462306a36Sopenharmony_ci		do {
64562306a36Sopenharmony_ci			if (data->flags & MMC_DATA_WRITE) {
64662306a36Sopenharmony_ci				if (spmmc_wait_txbuf_empty(host))
64762306a36Sopenharmony_ci					goto done;
64862306a36Sopenharmony_ci				writel(*buf, host->base + SPMMC_SD_PIODATATX_REG);
64962306a36Sopenharmony_ci			} else {
65062306a36Sopenharmony_ci				if (spmmc_wait_rxbuf_full(host))
65162306a36Sopenharmony_ci					goto done;
65262306a36Sopenharmony_ci				*buf = readl(host->base + SPMMC_SD_PIODATARX_REG);
65362306a36Sopenharmony_ci			}
65462306a36Sopenharmony_ci			buf++;
65562306a36Sopenharmony_ci			/* tx/rx 4 bytes one time in pio mode */
65662306a36Sopenharmony_ci			consumed += 4;
65762306a36Sopenharmony_ci			remain -= 4;
65862306a36Sopenharmony_ci		} while (remain);
65962306a36Sopenharmony_ci		sg_miter.consumed = consumed;
66062306a36Sopenharmony_ci		data_left -= consumed;
66162306a36Sopenharmony_ci	}
66262306a36Sopenharmony_cidone:
66362306a36Sopenharmony_ci	sg_miter_stop(&sg_miter);
66462306a36Sopenharmony_ci}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_cistatic void spmmc_controller_init(struct spmmc_host *host)
66762306a36Sopenharmony_ci{
66862306a36Sopenharmony_ci	u32 value;
66962306a36Sopenharmony_ci	int ret = reset_control_assert(host->rstc);
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	if (!ret) {
67262306a36Sopenharmony_ci		usleep_range(1000, 1250);
67362306a36Sopenharmony_ci		ret = reset_control_deassert(host->rstc);
67462306a36Sopenharmony_ci	}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	value = readl(host->base + SPMMC_CARD_MEDIATYPE_SRCDST_REG);
67762306a36Sopenharmony_ci	value &= ~SPMMC_MEDIA_TYPE;
67862306a36Sopenharmony_ci	value |= FIELD_PREP(SPMMC_MEDIA_TYPE, SPMMC_MEDIA_SD);
67962306a36Sopenharmony_ci	writel(value, host->base + SPMMC_CARD_MEDIATYPE_SRCDST_REG);
68062306a36Sopenharmony_ci}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci/*
68362306a36Sopenharmony_ci * 1. unmap scatterlist if needed;
68462306a36Sopenharmony_ci * 2. get response & check error conditions;
68562306a36Sopenharmony_ci * 3. notify mmc layer the request is done
68662306a36Sopenharmony_ci */
68762306a36Sopenharmony_cistatic void spmmc_finish_request(struct spmmc_host *host, struct mmc_request *mrq)
68862306a36Sopenharmony_ci{
68962306a36Sopenharmony_ci	struct mmc_command *cmd;
69062306a36Sopenharmony_ci	struct mmc_data *data;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	if (!mrq)
69362306a36Sopenharmony_ci		return;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	cmd = mrq->cmd;
69662306a36Sopenharmony_ci	data = mrq->data;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	if (data && SPMMC_DMA_MODE == host->dmapio_mode) {
69962306a36Sopenharmony_ci		dma_unmap_sg(host->mmc->parent, data->sg, data->sg_len, mmc_get_dma_dir(data));
70062306a36Sopenharmony_ci		host->dma_use_int = 0;
70162306a36Sopenharmony_ci	}
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	spmmc_get_rsp(host, cmd);
70462306a36Sopenharmony_ci	spmmc_check_error(host, mrq);
70562306a36Sopenharmony_ci	if (mrq->stop)
70662306a36Sopenharmony_ci		spmmc_send_stop_cmd(host);
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	host->mrq = NULL;
70962306a36Sopenharmony_ci	mmc_request_done(host->mmc, mrq);
71062306a36Sopenharmony_ci}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci/* Interrupt Service Routine */
71362306a36Sopenharmony_cistatic irqreturn_t spmmc_irq(int irq, void *dev_id)
71462306a36Sopenharmony_ci{
71562306a36Sopenharmony_ci	struct spmmc_host *host = dev_id;
71662306a36Sopenharmony_ci	u32 value = readl(host->base + SPMMC_SD_INT_REG);
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	if ((value & SPMMC_SDINT_SDCMP) && (value & SPMMC_SDINT_SDCMPEN)) {
71962306a36Sopenharmony_ci		value &= ~SPMMC_SDINT_SDCMPEN;
72062306a36Sopenharmony_ci		value |= SPMMC_SDINT_SDCMPCLR;
72162306a36Sopenharmony_ci		writel(value, host->base + SPMMC_SD_INT_REG);
72262306a36Sopenharmony_ci		return IRQ_WAKE_THREAD;
72362306a36Sopenharmony_ci	}
72462306a36Sopenharmony_ci	return IRQ_HANDLED;
72562306a36Sopenharmony_ci}
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_cistatic void spmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
72862306a36Sopenharmony_ci{
72962306a36Sopenharmony_ci	struct spmmc_host *host = mmc_priv(mmc);
73062306a36Sopenharmony_ci	struct mmc_data *data;
73162306a36Sopenharmony_ci	struct mmc_command *cmd;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	host->mrq = mrq;
73462306a36Sopenharmony_ci	data = mrq->data;
73562306a36Sopenharmony_ci	cmd = mrq->cmd;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	spmmc_prepare_cmd(host, cmd);
73862306a36Sopenharmony_ci	/* we need manually read response R2. */
73962306a36Sopenharmony_ci	if (cmd->flags & MMC_RSP_136) {
74062306a36Sopenharmony_ci		spmmc_trigger_transaction(host);
74162306a36Sopenharmony_ci		spmmc_get_rsp(host, cmd);
74262306a36Sopenharmony_ci		spmmc_wait_finish(host);
74362306a36Sopenharmony_ci		spmmc_check_error(host, mrq);
74462306a36Sopenharmony_ci		host->mrq = NULL;
74562306a36Sopenharmony_ci		mmc_request_done(host->mmc, mrq);
74662306a36Sopenharmony_ci	} else {
74762306a36Sopenharmony_ci		if (data)
74862306a36Sopenharmony_ci			spmmc_prepare_data(host, data);
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci		if (host->dmapio_mode == SPMMC_PIO_MODE && data) {
75162306a36Sopenharmony_ci			u32 value;
75262306a36Sopenharmony_ci			/* pio data transfer do not use interrupt */
75362306a36Sopenharmony_ci			value = readl(host->base + SPMMC_SD_INT_REG);
75462306a36Sopenharmony_ci			value &= ~SPMMC_SDINT_SDCMPEN;
75562306a36Sopenharmony_ci			writel(value, host->base + SPMMC_SD_INT_REG);
75662306a36Sopenharmony_ci			spmmc_trigger_transaction(host);
75762306a36Sopenharmony_ci			spmmc_xfer_data_pio(host, data);
75862306a36Sopenharmony_ci			spmmc_wait_finish(host);
75962306a36Sopenharmony_ci			spmmc_finish_request(host, mrq);
76062306a36Sopenharmony_ci		} else {
76162306a36Sopenharmony_ci			if (host->dma_use_int) {
76262306a36Sopenharmony_ci				spmmc_trigger_transaction(host);
76362306a36Sopenharmony_ci			} else {
76462306a36Sopenharmony_ci				spmmc_trigger_transaction(host);
76562306a36Sopenharmony_ci				spmmc_wait_finish(host);
76662306a36Sopenharmony_ci				spmmc_finish_request(host, mrq);
76762306a36Sopenharmony_ci			}
76862306a36Sopenharmony_ci		}
76962306a36Sopenharmony_ci	}
77062306a36Sopenharmony_ci}
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_cistatic void spmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
77362306a36Sopenharmony_ci{
77462306a36Sopenharmony_ci	struct spmmc_host *host = (struct spmmc_host *)mmc_priv(mmc);
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	spmmc_set_bus_clk(host, ios->clock);
77762306a36Sopenharmony_ci	spmmc_set_bus_timing(host, ios->timing);
77862306a36Sopenharmony_ci	spmmc_set_bus_width(host, ios->bus_width);
77962306a36Sopenharmony_ci	/* ensure mode is correct, because we might have hw reset the controller */
78062306a36Sopenharmony_ci	spmmc_set_sdmmc_mode(host);
78162306a36Sopenharmony_ci}
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci/*
78462306a36Sopenharmony_ci * Return values for the get_cd callback should be:
78562306a36Sopenharmony_ci *   0 for a absent card
78662306a36Sopenharmony_ci *   1 for a present card
78762306a36Sopenharmony_ci *   -ENOSYS when not supported (equal to NULL callback)
78862306a36Sopenharmony_ci *   or a negative errno value when something bad happened
78962306a36Sopenharmony_ci */
79062306a36Sopenharmony_cistatic int spmmc_get_cd(struct mmc_host *mmc)
79162306a36Sopenharmony_ci{
79262306a36Sopenharmony_ci	int ret = 0;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	if (mmc_can_gpio_cd(mmc))
79562306a36Sopenharmony_ci		ret = mmc_gpio_get_cd(mmc);
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	if (ret < 0)
79862306a36Sopenharmony_ci		ret = 0;
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	return ret;
80162306a36Sopenharmony_ci}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_cistatic int spmmc_execute_tuning(struct mmc_host *mmc, u32 opcode)
80462306a36Sopenharmony_ci{
80562306a36Sopenharmony_ci	struct spmmc_host *host = mmc_priv(mmc);
80662306a36Sopenharmony_ci	u8 smpl_dly = 0, candidate_dly = 0;
80762306a36Sopenharmony_ci	u32 value;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	host->tuning_info.enable_tuning = 0;
81062306a36Sopenharmony_ci	do {
81162306a36Sopenharmony_ci		value = readl(host->base + SPMMC_SD_TIMING_CONFIG0_REG);
81262306a36Sopenharmony_ci		value &= ~SPMMC_SD_READ_RESPONSE_DELAY;
81362306a36Sopenharmony_ci		value |= FIELD_PREP(SPMMC_SD_READ_RESPONSE_DELAY, smpl_dly);
81462306a36Sopenharmony_ci		value &= ~SPMMC_SD_READ_DATA_DELAY;
81562306a36Sopenharmony_ci		value |= FIELD_PREP(SPMMC_SD_READ_DATA_DELAY, smpl_dly);
81662306a36Sopenharmony_ci		value &= ~SPMMC_SD_READ_CRC_DELAY;
81762306a36Sopenharmony_ci		value |= FIELD_PREP(SPMMC_SD_READ_CRC_DELAY, smpl_dly);
81862306a36Sopenharmony_ci		writel(value, host->base + SPMMC_SD_TIMING_CONFIG0_REG);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci		if (!mmc_send_tuning(mmc, opcode, NULL)) {
82162306a36Sopenharmony_ci			candidate_dly |= (1 << smpl_dly);
82262306a36Sopenharmony_ci			break;
82362306a36Sopenharmony_ci		}
82462306a36Sopenharmony_ci	} while (smpl_dly++ <= SPMMC_MAX_TUNABLE_DLY);
82562306a36Sopenharmony_ci	host->tuning_info.enable_tuning = 1;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	if (candidate_dly) {
82862306a36Sopenharmony_ci		smpl_dly = spmmc_find_best_delay(candidate_dly);
82962306a36Sopenharmony_ci		value = readl(host->base + SPMMC_SD_TIMING_CONFIG0_REG);
83062306a36Sopenharmony_ci		value &= ~SPMMC_SD_READ_RESPONSE_DELAY;
83162306a36Sopenharmony_ci		value |= FIELD_PREP(SPMMC_SD_READ_RESPONSE_DELAY, smpl_dly);
83262306a36Sopenharmony_ci		value &= ~SPMMC_SD_READ_DATA_DELAY;
83362306a36Sopenharmony_ci		value |= FIELD_PREP(SPMMC_SD_READ_DATA_DELAY, smpl_dly);
83462306a36Sopenharmony_ci		value &= ~SPMMC_SD_READ_CRC_DELAY;
83562306a36Sopenharmony_ci		value |= FIELD_PREP(SPMMC_SD_READ_CRC_DELAY, smpl_dly);
83662306a36Sopenharmony_ci		writel(value, host->base + SPMMC_SD_TIMING_CONFIG0_REG);
83762306a36Sopenharmony_ci		return 0;
83862306a36Sopenharmony_ci	}
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	return -EIO;
84162306a36Sopenharmony_ci}
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_cistatic const struct mmc_host_ops spmmc_ops = {
84462306a36Sopenharmony_ci	.request = spmmc_request,
84562306a36Sopenharmony_ci	.set_ios = spmmc_set_ios,
84662306a36Sopenharmony_ci	.get_cd = spmmc_get_cd,
84762306a36Sopenharmony_ci	.execute_tuning = spmmc_execute_tuning,
84862306a36Sopenharmony_ci};
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_cistatic irqreturn_t spmmc_func_finish_req(int irq, void *dev_id)
85162306a36Sopenharmony_ci{
85262306a36Sopenharmony_ci	struct spmmc_host *host = dev_id;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	spmmc_finish_request(host, host->mrq);
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	return IRQ_HANDLED;
85762306a36Sopenharmony_ci}
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_cistatic int spmmc_drv_probe(struct platform_device *pdev)
86062306a36Sopenharmony_ci{
86162306a36Sopenharmony_ci	struct mmc_host *mmc;
86262306a36Sopenharmony_ci	struct resource *res;
86362306a36Sopenharmony_ci	struct spmmc_host *host;
86462306a36Sopenharmony_ci	int ret = 0;
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(struct spmmc_host));
86762306a36Sopenharmony_ci	if (!mmc)
86862306a36Sopenharmony_ci		return -ENOMEM;
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	host = mmc_priv(mmc);
87162306a36Sopenharmony_ci	host->mmc = mmc;
87262306a36Sopenharmony_ci	host->dmapio_mode = SPMMC_DMA_MODE;
87362306a36Sopenharmony_ci	host->dma_int_threshold = 1024;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	host->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
87662306a36Sopenharmony_ci	if (IS_ERR(host->base))
87762306a36Sopenharmony_ci		return PTR_ERR(host->base);
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	host->clk = devm_clk_get(&pdev->dev, NULL);
88062306a36Sopenharmony_ci	if (IS_ERR(host->clk))
88162306a36Sopenharmony_ci		return dev_err_probe(&pdev->dev, PTR_ERR(host->clk), "clk get fail\n");
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	host->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
88462306a36Sopenharmony_ci	if (IS_ERR(host->rstc))
88562306a36Sopenharmony_ci		return dev_err_probe(&pdev->dev, PTR_ERR(host->rstc), "rst get fail\n");
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	host->irq = platform_get_irq(pdev, 0);
88862306a36Sopenharmony_ci	if (host->irq < 0)
88962306a36Sopenharmony_ci		return host->irq;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	ret = devm_request_threaded_irq(&pdev->dev, host->irq,
89262306a36Sopenharmony_ci					spmmc_irq, spmmc_func_finish_req, IRQF_SHARED,
89362306a36Sopenharmony_ci			NULL, host);
89462306a36Sopenharmony_ci	if (ret)
89562306a36Sopenharmony_ci		return ret;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	ret = clk_prepare_enable(host->clk);
89862306a36Sopenharmony_ci	if (ret)
89962306a36Sopenharmony_ci		return dev_err_probe(&pdev->dev, ret, "failed to enable clk\n");
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	ret = mmc_of_parse(mmc);
90262306a36Sopenharmony_ci	if (ret)
90362306a36Sopenharmony_ci		goto clk_disable;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	mmc->ops = &spmmc_ops;
90662306a36Sopenharmony_ci	mmc->f_min = SPMMC_MIN_CLK;
90762306a36Sopenharmony_ci	if (mmc->f_max > SPMMC_MAX_CLK)
90862306a36Sopenharmony_ci		mmc->f_max = SPMMC_MAX_CLK;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	ret = mmc_regulator_get_supply(mmc);
91162306a36Sopenharmony_ci	if (ret)
91262306a36Sopenharmony_ci		goto clk_disable;
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	if (!mmc->ocr_avail)
91562306a36Sopenharmony_ci		mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
91662306a36Sopenharmony_ci	mmc->max_seg_size = SPMMC_MAX_BLK_COUNT * 512;
91762306a36Sopenharmony_ci	mmc->max_segs = SPMMC_MAX_DMA_MEMORY_SECTORS;
91862306a36Sopenharmony_ci	mmc->max_req_size = SPMMC_MAX_BLK_COUNT * 512;
91962306a36Sopenharmony_ci	mmc->max_blk_size = 512;
92062306a36Sopenharmony_ci	mmc->max_blk_count = SPMMC_MAX_BLK_COUNT;
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	dev_set_drvdata(&pdev->dev, host);
92362306a36Sopenharmony_ci	spmmc_controller_init(host);
92462306a36Sopenharmony_ci	spmmc_set_sdmmc_mode(host);
92562306a36Sopenharmony_ci	host->tuning_info.enable_tuning = 1;
92662306a36Sopenharmony_ci	pm_runtime_set_active(&pdev->dev);
92762306a36Sopenharmony_ci	pm_runtime_enable(&pdev->dev);
92862306a36Sopenharmony_ci	ret = mmc_add_host(mmc);
92962306a36Sopenharmony_ci	if (ret)
93062306a36Sopenharmony_ci		goto pm_disable;
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	return 0;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_cipm_disable:
93562306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ciclk_disable:
93862306a36Sopenharmony_ci	clk_disable_unprepare(host->clk);
93962306a36Sopenharmony_ci	return ret;
94062306a36Sopenharmony_ci}
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_cistatic void spmmc_drv_remove(struct platform_device *dev)
94362306a36Sopenharmony_ci{
94462306a36Sopenharmony_ci	struct spmmc_host *host = platform_get_drvdata(dev);
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	mmc_remove_host(host->mmc);
94762306a36Sopenharmony_ci	pm_runtime_get_sync(&dev->dev);
94862306a36Sopenharmony_ci	clk_disable_unprepare(host->clk);
94962306a36Sopenharmony_ci	pm_runtime_put_noidle(&dev->dev);
95062306a36Sopenharmony_ci	pm_runtime_disable(&dev->dev);
95162306a36Sopenharmony_ci}
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_cistatic int spmmc_pm_runtime_suspend(struct device *dev)
95462306a36Sopenharmony_ci{
95562306a36Sopenharmony_ci	struct spmmc_host *host;
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	host = dev_get_drvdata(dev);
95862306a36Sopenharmony_ci	clk_disable_unprepare(host->clk);
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	return 0;
96162306a36Sopenharmony_ci}
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_cistatic int spmmc_pm_runtime_resume(struct device *dev)
96462306a36Sopenharmony_ci{
96562306a36Sopenharmony_ci	struct spmmc_host *host;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	host = dev_get_drvdata(dev);
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	return clk_prepare_enable(host->clk);
97062306a36Sopenharmony_ci}
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_cistatic DEFINE_RUNTIME_DEV_PM_OPS(spmmc_pm_ops, spmmc_pm_runtime_suspend,
97362306a36Sopenharmony_ci							spmmc_pm_runtime_resume, NULL);
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_cistatic const struct of_device_id spmmc_of_table[] = {
97662306a36Sopenharmony_ci	{
97762306a36Sopenharmony_ci		.compatible = "sunplus,sp7021-mmc",
97862306a36Sopenharmony_ci	},
97962306a36Sopenharmony_ci	{/* sentinel */}
98062306a36Sopenharmony_ci};
98162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, spmmc_of_table);
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_cistatic struct platform_driver spmmc_driver = {
98462306a36Sopenharmony_ci	.probe = spmmc_drv_probe,
98562306a36Sopenharmony_ci	.remove_new = spmmc_drv_remove,
98662306a36Sopenharmony_ci	.driver = {
98762306a36Sopenharmony_ci		.name = "spmmc",
98862306a36Sopenharmony_ci		.pm = pm_ptr(&spmmc_pm_ops),
98962306a36Sopenharmony_ci		.of_match_table = spmmc_of_table,
99062306a36Sopenharmony_ci	},
99162306a36Sopenharmony_ci};
99262306a36Sopenharmony_cimodule_platform_driver(spmmc_driver);
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ciMODULE_AUTHOR("Tony Huang <tonyhuang.sunplus@gmail.com>");
99562306a36Sopenharmony_ciMODULE_AUTHOR("Li-hao Kuo <lhjeff911@gmail.com>");
99662306a36Sopenharmony_ciMODULE_DESCRIPTION("Sunplus MMC controller driver");
99762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
998