162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci// Copyright (C) 2014 Broadcom Corporation
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci/*
562306a36Sopenharmony_ci * iProc SDHCI platform driver
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/acpi.h>
962306a36Sopenharmony_ci#include <linux/delay.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/mmc/host.h>
1262306a36Sopenharmony_ci#include <linux/of.h>
1362306a36Sopenharmony_ci#include <linux/platform_device.h>
1462306a36Sopenharmony_ci#include "sdhci-pltfm.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistruct sdhci_iproc_data {
1762306a36Sopenharmony_ci	const struct sdhci_pltfm_data *pdata;
1862306a36Sopenharmony_ci	u32 caps;
1962306a36Sopenharmony_ci	u32 caps1;
2062306a36Sopenharmony_ci	u32 mmc_caps;
2162306a36Sopenharmony_ci	bool missing_caps;
2262306a36Sopenharmony_ci};
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistruct sdhci_iproc_host {
2562306a36Sopenharmony_ci	const struct sdhci_iproc_data *data;
2662306a36Sopenharmony_ci	u32 shadow_cmd;
2762306a36Sopenharmony_ci	u32 shadow_blk;
2862306a36Sopenharmony_ci	bool is_cmd_shadowed;
2962306a36Sopenharmony_ci	bool is_blk_shadowed;
3062306a36Sopenharmony_ci};
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define REG_OFFSET_IN_BITS(reg) ((reg) << 3 & 0x18)
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic inline u32 sdhci_iproc_readl(struct sdhci_host *host, int reg)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	u32 val = readl(host->ioaddr + reg);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	pr_debug("%s: readl [0x%02x] 0x%08x\n",
3962306a36Sopenharmony_ci		 mmc_hostname(host->mmc), reg, val);
4062306a36Sopenharmony_ci	return val;
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic u16 sdhci_iproc_readw(struct sdhci_host *host, int reg)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
4662306a36Sopenharmony_ci	struct sdhci_iproc_host *iproc_host = sdhci_pltfm_priv(pltfm_host);
4762306a36Sopenharmony_ci	u32 val;
4862306a36Sopenharmony_ci	u16 word;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	if ((reg == SDHCI_TRANSFER_MODE) && iproc_host->is_cmd_shadowed) {
5162306a36Sopenharmony_ci		/* Get the saved transfer mode */
5262306a36Sopenharmony_ci		val = iproc_host->shadow_cmd;
5362306a36Sopenharmony_ci	} else if ((reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) &&
5462306a36Sopenharmony_ci		   iproc_host->is_blk_shadowed) {
5562306a36Sopenharmony_ci		/* Get the saved block info */
5662306a36Sopenharmony_ci		val = iproc_host->shadow_blk;
5762306a36Sopenharmony_ci	} else {
5862306a36Sopenharmony_ci		val = sdhci_iproc_readl(host, (reg & ~3));
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci	word = val >> REG_OFFSET_IN_BITS(reg) & 0xffff;
6162306a36Sopenharmony_ci	return word;
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic u8 sdhci_iproc_readb(struct sdhci_host *host, int reg)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	u32 val = sdhci_iproc_readl(host, (reg & ~3));
6762306a36Sopenharmony_ci	u8 byte = val >> REG_OFFSET_IN_BITS(reg) & 0xff;
6862306a36Sopenharmony_ci	return byte;
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic inline void sdhci_iproc_writel(struct sdhci_host *host, u32 val, int reg)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	pr_debug("%s: writel [0x%02x] 0x%08x\n",
7462306a36Sopenharmony_ci		 mmc_hostname(host->mmc), reg, val);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	writel(val, host->ioaddr + reg);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (host->clock <= 400000) {
7962306a36Sopenharmony_ci		/* Round up to micro-second four SD clock delay */
8062306a36Sopenharmony_ci		if (host->clock)
8162306a36Sopenharmony_ci			udelay((4 * 1000000 + host->clock - 1) / host->clock);
8262306a36Sopenharmony_ci		else
8362306a36Sopenharmony_ci			udelay(10);
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/*
8862306a36Sopenharmony_ci * The Arasan has a bugette whereby it may lose the content of successive
8962306a36Sopenharmony_ci * writes to the same register that are within two SD-card clock cycles of
9062306a36Sopenharmony_ci * each other (a clock domain crossing problem). The data
9162306a36Sopenharmony_ci * register does not have this problem, which is just as well - otherwise we'd
9262306a36Sopenharmony_ci * have to nobble the DMA engine too.
9362306a36Sopenharmony_ci *
9462306a36Sopenharmony_ci * This wouldn't be a problem with the code except that we can only write the
9562306a36Sopenharmony_ci * controller with 32-bit writes.  So two different 16-bit registers are
9662306a36Sopenharmony_ci * written back to back creates the problem.
9762306a36Sopenharmony_ci *
9862306a36Sopenharmony_ci * In reality, this only happens when SDHCI_BLOCK_SIZE and SDHCI_BLOCK_COUNT
9962306a36Sopenharmony_ci * are written followed by SDHCI_TRANSFER_MODE and SDHCI_COMMAND.
10062306a36Sopenharmony_ci * The BLOCK_SIZE and BLOCK_COUNT are meaningless until a command issued so
10162306a36Sopenharmony_ci * the work around can be further optimized. We can keep shadow values of
10262306a36Sopenharmony_ci * BLOCK_SIZE, BLOCK_COUNT, and TRANSFER_MODE until a COMMAND is issued.
10362306a36Sopenharmony_ci * Then, write the BLOCK_SIZE+BLOCK_COUNT in a single 32-bit write followed
10462306a36Sopenharmony_ci * by the TRANSFER+COMMAND in another 32-bit write.
10562306a36Sopenharmony_ci */
10662306a36Sopenharmony_cistatic void sdhci_iproc_writew(struct sdhci_host *host, u16 val, int reg)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
10962306a36Sopenharmony_ci	struct sdhci_iproc_host *iproc_host = sdhci_pltfm_priv(pltfm_host);
11062306a36Sopenharmony_ci	u32 word_shift = REG_OFFSET_IN_BITS(reg);
11162306a36Sopenharmony_ci	u32 mask = 0xffff << word_shift;
11262306a36Sopenharmony_ci	u32 oldval, newval;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	if (reg == SDHCI_COMMAND) {
11562306a36Sopenharmony_ci		/* Write the block now as we are issuing a command */
11662306a36Sopenharmony_ci		if (iproc_host->is_blk_shadowed) {
11762306a36Sopenharmony_ci			sdhci_iproc_writel(host, iproc_host->shadow_blk,
11862306a36Sopenharmony_ci				SDHCI_BLOCK_SIZE);
11962306a36Sopenharmony_ci			iproc_host->is_blk_shadowed = false;
12062306a36Sopenharmony_ci		}
12162306a36Sopenharmony_ci		oldval = iproc_host->shadow_cmd;
12262306a36Sopenharmony_ci		iproc_host->is_cmd_shadowed = false;
12362306a36Sopenharmony_ci	} else if ((reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) &&
12462306a36Sopenharmony_ci		   iproc_host->is_blk_shadowed) {
12562306a36Sopenharmony_ci		/* Block size and count are stored in shadow reg */
12662306a36Sopenharmony_ci		oldval = iproc_host->shadow_blk;
12762306a36Sopenharmony_ci	} else {
12862306a36Sopenharmony_ci		/* Read reg, all other registers are not shadowed */
12962306a36Sopenharmony_ci		oldval = sdhci_iproc_readl(host, (reg & ~3));
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci	newval = (oldval & ~mask) | (val << word_shift);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (reg == SDHCI_TRANSFER_MODE) {
13462306a36Sopenharmony_ci		/* Save the transfer mode until the command is issued */
13562306a36Sopenharmony_ci		iproc_host->shadow_cmd = newval;
13662306a36Sopenharmony_ci		iproc_host->is_cmd_shadowed = true;
13762306a36Sopenharmony_ci	} else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) {
13862306a36Sopenharmony_ci		/* Save the block info until the command is issued */
13962306a36Sopenharmony_ci		iproc_host->shadow_blk = newval;
14062306a36Sopenharmony_ci		iproc_host->is_blk_shadowed = true;
14162306a36Sopenharmony_ci	} else {
14262306a36Sopenharmony_ci		/* Command or other regular 32-bit write */
14362306a36Sopenharmony_ci		sdhci_iproc_writel(host, newval, reg & ~3);
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic void sdhci_iproc_writeb(struct sdhci_host *host, u8 val, int reg)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	u32 oldval = sdhci_iproc_readl(host, (reg & ~3));
15062306a36Sopenharmony_ci	u32 byte_shift = REG_OFFSET_IN_BITS(reg);
15162306a36Sopenharmony_ci	u32 mask = 0xff << byte_shift;
15262306a36Sopenharmony_ci	u32 newval = (oldval & ~mask) | (val << byte_shift);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	sdhci_iproc_writel(host, newval, reg & ~3);
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic unsigned int sdhci_iproc_get_max_clock(struct sdhci_host *host)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	if (pltfm_host->clk)
16262306a36Sopenharmony_ci		return sdhci_pltfm_clk_get_max_clock(host);
16362306a36Sopenharmony_ci	else
16462306a36Sopenharmony_ci		return pltfm_host->clock;
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci/*
16862306a36Sopenharmony_ci * There is a known bug on BCM2711's SDHCI core integration where the
16962306a36Sopenharmony_ci * controller will hang when the difference between the core clock and the bus
17062306a36Sopenharmony_ci * clock is too great. Specifically this can be reproduced under the following
17162306a36Sopenharmony_ci * conditions:
17262306a36Sopenharmony_ci *
17362306a36Sopenharmony_ci *  - No SD card plugged in, polling thread is running, probing cards at
17462306a36Sopenharmony_ci *    100 kHz.
17562306a36Sopenharmony_ci *  - BCM2711's core clock configured at 500MHz or more
17662306a36Sopenharmony_ci *
17762306a36Sopenharmony_ci * So we set 200kHz as the minimum clock frequency available for that SoC.
17862306a36Sopenharmony_ci */
17962306a36Sopenharmony_cistatic unsigned int sdhci_iproc_bcm2711_get_min_clock(struct sdhci_host *host)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	return 200000;
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic const struct sdhci_ops sdhci_iproc_ops = {
18562306a36Sopenharmony_ci	.set_clock = sdhci_set_clock,
18662306a36Sopenharmony_ci	.get_max_clock = sdhci_iproc_get_max_clock,
18762306a36Sopenharmony_ci	.set_bus_width = sdhci_set_bus_width,
18862306a36Sopenharmony_ci	.reset = sdhci_reset,
18962306a36Sopenharmony_ci	.set_uhs_signaling = sdhci_set_uhs_signaling,
19062306a36Sopenharmony_ci};
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic const struct sdhci_ops sdhci_iproc_32only_ops = {
19362306a36Sopenharmony_ci	.read_l = sdhci_iproc_readl,
19462306a36Sopenharmony_ci	.read_w = sdhci_iproc_readw,
19562306a36Sopenharmony_ci	.read_b = sdhci_iproc_readb,
19662306a36Sopenharmony_ci	.write_l = sdhci_iproc_writel,
19762306a36Sopenharmony_ci	.write_w = sdhci_iproc_writew,
19862306a36Sopenharmony_ci	.write_b = sdhci_iproc_writeb,
19962306a36Sopenharmony_ci	.set_clock = sdhci_set_clock,
20062306a36Sopenharmony_ci	.get_max_clock = sdhci_iproc_get_max_clock,
20162306a36Sopenharmony_ci	.set_bus_width = sdhci_set_bus_width,
20262306a36Sopenharmony_ci	.reset = sdhci_reset,
20362306a36Sopenharmony_ci	.set_uhs_signaling = sdhci_set_uhs_signaling,
20462306a36Sopenharmony_ci};
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic const struct sdhci_pltfm_data sdhci_iproc_cygnus_pltfm_data = {
20762306a36Sopenharmony_ci	.quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
20862306a36Sopenharmony_ci		  SDHCI_QUIRK_NO_HISPD_BIT,
20962306a36Sopenharmony_ci	.quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN | SDHCI_QUIRK2_HOST_OFF_CARD_ON,
21062306a36Sopenharmony_ci	.ops = &sdhci_iproc_32only_ops,
21162306a36Sopenharmony_ci};
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic const struct sdhci_iproc_data iproc_cygnus_data = {
21462306a36Sopenharmony_ci	.pdata = &sdhci_iproc_cygnus_pltfm_data,
21562306a36Sopenharmony_ci	.caps = ((0x1 << SDHCI_MAX_BLOCK_SHIFT)
21662306a36Sopenharmony_ci			& SDHCI_MAX_BLOCK_MASK) |
21762306a36Sopenharmony_ci		SDHCI_CAN_VDD_330 |
21862306a36Sopenharmony_ci		SDHCI_CAN_VDD_180 |
21962306a36Sopenharmony_ci		SDHCI_CAN_DO_SUSPEND |
22062306a36Sopenharmony_ci		SDHCI_CAN_DO_HISPD |
22162306a36Sopenharmony_ci		SDHCI_CAN_DO_ADMA2 |
22262306a36Sopenharmony_ci		SDHCI_CAN_DO_SDMA,
22362306a36Sopenharmony_ci	.caps1 = SDHCI_DRIVER_TYPE_C |
22462306a36Sopenharmony_ci		 SDHCI_DRIVER_TYPE_D |
22562306a36Sopenharmony_ci		 SDHCI_SUPPORT_DDR50,
22662306a36Sopenharmony_ci	.mmc_caps = MMC_CAP_1_8V_DDR,
22762306a36Sopenharmony_ci};
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic const struct sdhci_pltfm_data sdhci_iproc_pltfm_data = {
23062306a36Sopenharmony_ci	.quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
23162306a36Sopenharmony_ci		  SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 |
23262306a36Sopenharmony_ci		  SDHCI_QUIRK_NO_HISPD_BIT,
23362306a36Sopenharmony_ci	.quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN,
23462306a36Sopenharmony_ci	.ops = &sdhci_iproc_ops,
23562306a36Sopenharmony_ci};
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic const struct sdhci_iproc_data iproc_data = {
23862306a36Sopenharmony_ci	.pdata = &sdhci_iproc_pltfm_data,
23962306a36Sopenharmony_ci	.caps = ((0x1 << SDHCI_MAX_BLOCK_SHIFT)
24062306a36Sopenharmony_ci			& SDHCI_MAX_BLOCK_MASK) |
24162306a36Sopenharmony_ci		SDHCI_CAN_VDD_330 |
24262306a36Sopenharmony_ci		SDHCI_CAN_VDD_180 |
24362306a36Sopenharmony_ci		SDHCI_CAN_DO_SUSPEND |
24462306a36Sopenharmony_ci		SDHCI_CAN_DO_HISPD |
24562306a36Sopenharmony_ci		SDHCI_CAN_DO_ADMA2 |
24662306a36Sopenharmony_ci		SDHCI_CAN_DO_SDMA,
24762306a36Sopenharmony_ci	.caps1 = SDHCI_DRIVER_TYPE_C |
24862306a36Sopenharmony_ci		 SDHCI_DRIVER_TYPE_D |
24962306a36Sopenharmony_ci		 SDHCI_SUPPORT_DDR50,
25062306a36Sopenharmony_ci};
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic const struct sdhci_pltfm_data sdhci_bcm2835_pltfm_data = {
25362306a36Sopenharmony_ci	.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
25462306a36Sopenharmony_ci		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
25562306a36Sopenharmony_ci		  SDHCI_QUIRK_NO_HISPD_BIT,
25662306a36Sopenharmony_ci	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
25762306a36Sopenharmony_ci	.ops = &sdhci_iproc_32only_ops,
25862306a36Sopenharmony_ci};
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic const struct sdhci_iproc_data bcm2835_data = {
26162306a36Sopenharmony_ci	.pdata = &sdhci_bcm2835_pltfm_data,
26262306a36Sopenharmony_ci	.caps = ((0x1 << SDHCI_MAX_BLOCK_SHIFT)
26362306a36Sopenharmony_ci			& SDHCI_MAX_BLOCK_MASK) |
26462306a36Sopenharmony_ci		SDHCI_CAN_VDD_330 |
26562306a36Sopenharmony_ci		SDHCI_CAN_DO_HISPD,
26662306a36Sopenharmony_ci	.caps1 = SDHCI_DRIVER_TYPE_A |
26762306a36Sopenharmony_ci		 SDHCI_DRIVER_TYPE_C,
26862306a36Sopenharmony_ci	.mmc_caps = 0x00000000,
26962306a36Sopenharmony_ci	.missing_caps = true,
27062306a36Sopenharmony_ci};
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic const struct sdhci_ops sdhci_iproc_bcm2711_ops = {
27362306a36Sopenharmony_ci	.read_l = sdhci_iproc_readl,
27462306a36Sopenharmony_ci	.read_w = sdhci_iproc_readw,
27562306a36Sopenharmony_ci	.read_b = sdhci_iproc_readb,
27662306a36Sopenharmony_ci	.write_l = sdhci_iproc_writel,
27762306a36Sopenharmony_ci	.write_w = sdhci_iproc_writew,
27862306a36Sopenharmony_ci	.write_b = sdhci_iproc_writeb,
27962306a36Sopenharmony_ci	.set_clock = sdhci_set_clock,
28062306a36Sopenharmony_ci	.set_power = sdhci_set_power_and_bus_voltage,
28162306a36Sopenharmony_ci	.get_max_clock = sdhci_iproc_get_max_clock,
28262306a36Sopenharmony_ci	.get_min_clock = sdhci_iproc_bcm2711_get_min_clock,
28362306a36Sopenharmony_ci	.set_bus_width = sdhci_set_bus_width,
28462306a36Sopenharmony_ci	.reset = sdhci_reset,
28562306a36Sopenharmony_ci	.set_uhs_signaling = sdhci_set_uhs_signaling,
28662306a36Sopenharmony_ci};
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic const struct sdhci_pltfm_data sdhci_bcm2711_pltfm_data = {
28962306a36Sopenharmony_ci	.quirks = SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12,
29062306a36Sopenharmony_ci	.ops = &sdhci_iproc_bcm2711_ops,
29162306a36Sopenharmony_ci};
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic const struct sdhci_iproc_data bcm2711_data = {
29462306a36Sopenharmony_ci	.pdata = &sdhci_bcm2711_pltfm_data,
29562306a36Sopenharmony_ci	.mmc_caps = MMC_CAP_3_3V_DDR,
29662306a36Sopenharmony_ci};
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic const struct sdhci_pltfm_data sdhci_bcm7211a0_pltfm_data = {
29962306a36Sopenharmony_ci	.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
30062306a36Sopenharmony_ci		SDHCI_QUIRK_BROKEN_DMA |
30162306a36Sopenharmony_ci		SDHCI_QUIRK_BROKEN_ADMA,
30262306a36Sopenharmony_ci	.ops = &sdhci_iproc_ops,
30362306a36Sopenharmony_ci};
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci#define BCM7211A0_BASE_CLK_MHZ 100
30662306a36Sopenharmony_cistatic const struct sdhci_iproc_data bcm7211a0_data = {
30762306a36Sopenharmony_ci	.pdata = &sdhci_bcm7211a0_pltfm_data,
30862306a36Sopenharmony_ci	.caps = ((BCM7211A0_BASE_CLK_MHZ / 2) << SDHCI_TIMEOUT_CLK_SHIFT) |
30962306a36Sopenharmony_ci		(BCM7211A0_BASE_CLK_MHZ << SDHCI_CLOCK_BASE_SHIFT) |
31062306a36Sopenharmony_ci		((0x2 << SDHCI_MAX_BLOCK_SHIFT)
31162306a36Sopenharmony_ci			& SDHCI_MAX_BLOCK_MASK) |
31262306a36Sopenharmony_ci		SDHCI_CAN_VDD_330 |
31362306a36Sopenharmony_ci		SDHCI_CAN_VDD_180 |
31462306a36Sopenharmony_ci		SDHCI_CAN_DO_SUSPEND |
31562306a36Sopenharmony_ci		SDHCI_CAN_DO_HISPD,
31662306a36Sopenharmony_ci	.caps1 = SDHCI_DRIVER_TYPE_C |
31762306a36Sopenharmony_ci		 SDHCI_DRIVER_TYPE_D,
31862306a36Sopenharmony_ci	.missing_caps = true,
31962306a36Sopenharmony_ci};
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic const struct of_device_id sdhci_iproc_of_match[] = {
32262306a36Sopenharmony_ci	{ .compatible = "brcm,bcm2835-sdhci", .data = &bcm2835_data },
32362306a36Sopenharmony_ci	{ .compatible = "brcm,bcm2711-emmc2", .data = &bcm2711_data },
32462306a36Sopenharmony_ci	{ .compatible = "brcm,sdhci-iproc-cygnus", .data = &iproc_cygnus_data},
32562306a36Sopenharmony_ci	{ .compatible = "brcm,sdhci-iproc", .data = &iproc_data },
32662306a36Sopenharmony_ci	{ .compatible = "brcm,bcm7211a0-sdhci", .data = &bcm7211a0_data },
32762306a36Sopenharmony_ci	{ }
32862306a36Sopenharmony_ci};
32962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, sdhci_iproc_of_match);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci#ifdef CONFIG_ACPI
33262306a36Sopenharmony_ci/*
33362306a36Sopenharmony_ci * This is a duplicate of bcm2835_(pltfrm_)data without caps quirks
33462306a36Sopenharmony_ci * which are provided by the ACPI table.
33562306a36Sopenharmony_ci */
33662306a36Sopenharmony_cistatic const struct sdhci_pltfm_data sdhci_bcm_arasan_data = {
33762306a36Sopenharmony_ci	.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
33862306a36Sopenharmony_ci		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
33962306a36Sopenharmony_ci		  SDHCI_QUIRK_NO_HISPD_BIT,
34062306a36Sopenharmony_ci	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
34162306a36Sopenharmony_ci	.ops = &sdhci_iproc_32only_ops,
34262306a36Sopenharmony_ci};
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_cistatic const struct sdhci_iproc_data bcm_arasan_data = {
34562306a36Sopenharmony_ci	.pdata = &sdhci_bcm_arasan_data,
34662306a36Sopenharmony_ci};
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic const struct acpi_device_id sdhci_iproc_acpi_ids[] = {
34962306a36Sopenharmony_ci	{ .id = "BRCM5871", .driver_data = (kernel_ulong_t)&iproc_cygnus_data },
35062306a36Sopenharmony_ci	{ .id = "BRCM5872", .driver_data = (kernel_ulong_t)&iproc_data },
35162306a36Sopenharmony_ci	{ .id = "BCM2847",  .driver_data = (kernel_ulong_t)&bcm_arasan_data },
35262306a36Sopenharmony_ci	{ .id = "BRCME88C", .driver_data = (kernel_ulong_t)&bcm2711_data },
35362306a36Sopenharmony_ci	{ /* sentinel */ }
35462306a36Sopenharmony_ci};
35562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, sdhci_iproc_acpi_ids);
35662306a36Sopenharmony_ci#endif
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic int sdhci_iproc_probe(struct platform_device *pdev)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
36162306a36Sopenharmony_ci	const struct sdhci_iproc_data *iproc_data = NULL;
36262306a36Sopenharmony_ci	struct sdhci_host *host;
36362306a36Sopenharmony_ci	struct sdhci_iproc_host *iproc_host;
36462306a36Sopenharmony_ci	struct sdhci_pltfm_host *pltfm_host;
36562306a36Sopenharmony_ci	int ret;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	iproc_data = device_get_match_data(dev);
36862306a36Sopenharmony_ci	if (!iproc_data)
36962306a36Sopenharmony_ci		return -ENODEV;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	host = sdhci_pltfm_init(pdev, iproc_data->pdata, sizeof(*iproc_host));
37262306a36Sopenharmony_ci	if (IS_ERR(host))
37362306a36Sopenharmony_ci		return PTR_ERR(host);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	pltfm_host = sdhci_priv(host);
37662306a36Sopenharmony_ci	iproc_host = sdhci_pltfm_priv(pltfm_host);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	iproc_host->data = iproc_data;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	ret = mmc_of_parse(host->mmc);
38162306a36Sopenharmony_ci	if (ret)
38262306a36Sopenharmony_ci		goto err;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	sdhci_get_property(pdev);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	host->mmc->caps |= iproc_host->data->mmc_caps;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	if (dev->of_node) {
38962306a36Sopenharmony_ci		pltfm_host->clk = devm_clk_get_enabled(dev, NULL);
39062306a36Sopenharmony_ci		if (IS_ERR(pltfm_host->clk)) {
39162306a36Sopenharmony_ci			ret = PTR_ERR(pltfm_host->clk);
39262306a36Sopenharmony_ci			goto err;
39362306a36Sopenharmony_ci		}
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	if (iproc_host->data->missing_caps) {
39762306a36Sopenharmony_ci		__sdhci_read_caps(host, NULL,
39862306a36Sopenharmony_ci				  &iproc_host->data->caps,
39962306a36Sopenharmony_ci				  &iproc_host->data->caps1);
40062306a36Sopenharmony_ci	}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	ret = sdhci_add_host(host);
40362306a36Sopenharmony_ci	if (ret)
40462306a36Sopenharmony_ci		goto err;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	return 0;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_cierr:
40962306a36Sopenharmony_ci	sdhci_pltfm_free(pdev);
41062306a36Sopenharmony_ci	return ret;
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic void sdhci_iproc_shutdown(struct platform_device *pdev)
41462306a36Sopenharmony_ci{
41562306a36Sopenharmony_ci	sdhci_pltfm_suspend(&pdev->dev);
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cistatic struct platform_driver sdhci_iproc_driver = {
41962306a36Sopenharmony_ci	.driver = {
42062306a36Sopenharmony_ci		.name = "sdhci-iproc",
42162306a36Sopenharmony_ci		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
42262306a36Sopenharmony_ci		.of_match_table = sdhci_iproc_of_match,
42362306a36Sopenharmony_ci		.acpi_match_table = ACPI_PTR(sdhci_iproc_acpi_ids),
42462306a36Sopenharmony_ci		.pm = &sdhci_pltfm_pmops,
42562306a36Sopenharmony_ci	},
42662306a36Sopenharmony_ci	.probe = sdhci_iproc_probe,
42762306a36Sopenharmony_ci	.remove_new = sdhci_pltfm_remove,
42862306a36Sopenharmony_ci	.shutdown = sdhci_iproc_shutdown,
42962306a36Sopenharmony_ci};
43062306a36Sopenharmony_cimodule_platform_driver(sdhci_iproc_driver);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ciMODULE_AUTHOR("Broadcom");
43362306a36Sopenharmony_ciMODULE_DESCRIPTION("IPROC SDHCI driver");
43462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
435