162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* Copyright (C) 2019 ASPEED Technology Inc. */
362306a36Sopenharmony_ci/* Copyright (C) 2019 IBM Corp. */
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <linux/clk.h>
662306a36Sopenharmony_ci#include <linux/delay.h>
762306a36Sopenharmony_ci#include <linux/device.h>
862306a36Sopenharmony_ci#include <linux/io.h>
962306a36Sopenharmony_ci#include <linux/math64.h>
1062306a36Sopenharmony_ci#include <linux/mmc/host.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/of_address.h>
1362306a36Sopenharmony_ci#include <linux/of.h>
1462306a36Sopenharmony_ci#include <linux/of_platform.h>
1562306a36Sopenharmony_ci#include <linux/platform_device.h>
1662306a36Sopenharmony_ci#include <linux/spinlock.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "sdhci-pltfm.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define ASPEED_SDC_INFO			0x00
2162306a36Sopenharmony_ci#define   ASPEED_SDC_S1_MMC8		BIT(25)
2262306a36Sopenharmony_ci#define   ASPEED_SDC_S0_MMC8		BIT(24)
2362306a36Sopenharmony_ci#define ASPEED_SDC_PHASE		0xf4
2462306a36Sopenharmony_ci#define   ASPEED_SDC_S1_PHASE_IN	GENMASK(25, 21)
2562306a36Sopenharmony_ci#define   ASPEED_SDC_S0_PHASE_IN	GENMASK(20, 16)
2662306a36Sopenharmony_ci#define   ASPEED_SDC_S1_PHASE_OUT	GENMASK(15, 11)
2762306a36Sopenharmony_ci#define   ASPEED_SDC_S1_PHASE_IN_EN	BIT(10)
2862306a36Sopenharmony_ci#define   ASPEED_SDC_S1_PHASE_OUT_EN	GENMASK(9, 8)
2962306a36Sopenharmony_ci#define   ASPEED_SDC_S0_PHASE_OUT	GENMASK(7, 3)
3062306a36Sopenharmony_ci#define   ASPEED_SDC_S0_PHASE_IN_EN	BIT(2)
3162306a36Sopenharmony_ci#define   ASPEED_SDC_S0_PHASE_OUT_EN	GENMASK(1, 0)
3262306a36Sopenharmony_ci#define   ASPEED_SDC_PHASE_MAX		31
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/* SDIO{10,20} */
3562306a36Sopenharmony_ci#define ASPEED_SDC_CAP1_1_8V           (0 * 32 + 26)
3662306a36Sopenharmony_ci/* SDIO{14,24} */
3762306a36Sopenharmony_ci#define ASPEED_SDC_CAP2_SDR104         (1 * 32 + 1)
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistruct aspeed_sdc {
4062306a36Sopenharmony_ci	struct clk *clk;
4162306a36Sopenharmony_ci	struct resource *res;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	spinlock_t lock;
4462306a36Sopenharmony_ci	void __iomem *regs;
4562306a36Sopenharmony_ci};
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistruct aspeed_sdhci_tap_param {
4862306a36Sopenharmony_ci	bool valid;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci#define ASPEED_SDHCI_TAP_PARAM_INVERT_CLK	BIT(4)
5162306a36Sopenharmony_ci	u8 in;
5262306a36Sopenharmony_ci	u8 out;
5362306a36Sopenharmony_ci};
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistruct aspeed_sdhci_tap_desc {
5662306a36Sopenharmony_ci	u32 tap_mask;
5762306a36Sopenharmony_ci	u32 enable_mask;
5862306a36Sopenharmony_ci	u8 enable_value;
5962306a36Sopenharmony_ci};
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistruct aspeed_sdhci_phase_desc {
6262306a36Sopenharmony_ci	struct aspeed_sdhci_tap_desc in;
6362306a36Sopenharmony_ci	struct aspeed_sdhci_tap_desc out;
6462306a36Sopenharmony_ci};
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistruct aspeed_sdhci_pdata {
6762306a36Sopenharmony_ci	unsigned int clk_div_start;
6862306a36Sopenharmony_ci	const struct aspeed_sdhci_phase_desc *phase_desc;
6962306a36Sopenharmony_ci	size_t nr_phase_descs;
7062306a36Sopenharmony_ci};
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistruct aspeed_sdhci {
7362306a36Sopenharmony_ci	const struct aspeed_sdhci_pdata *pdata;
7462306a36Sopenharmony_ci	struct aspeed_sdc *parent;
7562306a36Sopenharmony_ci	u32 width_mask;
7662306a36Sopenharmony_ci	struct mmc_clk_phase_map phase_map;
7762306a36Sopenharmony_ci	const struct aspeed_sdhci_phase_desc *phase_desc;
7862306a36Sopenharmony_ci};
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci/*
8162306a36Sopenharmony_ci * The function sets the mirror register for updating
8262306a36Sopenharmony_ci * capbilities of the current slot.
8362306a36Sopenharmony_ci *
8462306a36Sopenharmony_ci *   slot | capability  | caps_reg | mirror_reg
8562306a36Sopenharmony_ci *   -----|-------------|----------|------------
8662306a36Sopenharmony_ci *     0  | CAP1_1_8V   | SDIO140  |   SDIO10
8762306a36Sopenharmony_ci *     0  | CAP2_SDR104 | SDIO144  |   SDIO14
8862306a36Sopenharmony_ci *     1  | CAP1_1_8V   | SDIO240  |   SDIO20
8962306a36Sopenharmony_ci *     1  | CAP2_SDR104 | SDIO244  |   SDIO24
9062306a36Sopenharmony_ci */
9162306a36Sopenharmony_cistatic void aspeed_sdc_set_slot_capability(struct sdhci_host *host, struct aspeed_sdc *sdc,
9262306a36Sopenharmony_ci					   int capability, bool enable, u8 slot)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	u32 mirror_reg_offset;
9562306a36Sopenharmony_ci	u32 cap_val;
9662306a36Sopenharmony_ci	u8 cap_reg;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	if (slot > 1)
9962306a36Sopenharmony_ci		return;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	cap_reg = capability / 32;
10262306a36Sopenharmony_ci	cap_val = sdhci_readl(host, 0x40 + (cap_reg * 4));
10362306a36Sopenharmony_ci	if (enable)
10462306a36Sopenharmony_ci		cap_val |= BIT(capability % 32);
10562306a36Sopenharmony_ci	else
10662306a36Sopenharmony_ci		cap_val &= ~BIT(capability % 32);
10762306a36Sopenharmony_ci	mirror_reg_offset = ((slot + 1) * 0x10) + (cap_reg * 4);
10862306a36Sopenharmony_ci	writel(cap_val, sdc->regs + mirror_reg_offset);
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic void aspeed_sdc_configure_8bit_mode(struct aspeed_sdc *sdc,
11262306a36Sopenharmony_ci					   struct aspeed_sdhci *sdhci,
11362306a36Sopenharmony_ci					   bool bus8)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	u32 info;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	/* Set/clear 8 bit mode */
11862306a36Sopenharmony_ci	spin_lock(&sdc->lock);
11962306a36Sopenharmony_ci	info = readl(sdc->regs + ASPEED_SDC_INFO);
12062306a36Sopenharmony_ci	if (bus8)
12162306a36Sopenharmony_ci		info |= sdhci->width_mask;
12262306a36Sopenharmony_ci	else
12362306a36Sopenharmony_ci		info &= ~sdhci->width_mask;
12462306a36Sopenharmony_ci	writel(info, sdc->regs + ASPEED_SDC_INFO);
12562306a36Sopenharmony_ci	spin_unlock(&sdc->lock);
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic u32
12962306a36Sopenharmony_ciaspeed_sdc_set_phase_tap(const struct aspeed_sdhci_tap_desc *desc,
13062306a36Sopenharmony_ci			 u8 tap, bool enable, u32 reg)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	reg &= ~(desc->enable_mask | desc->tap_mask);
13362306a36Sopenharmony_ci	if (enable) {
13462306a36Sopenharmony_ci		reg |= tap << __ffs(desc->tap_mask);
13562306a36Sopenharmony_ci		reg |= desc->enable_value << __ffs(desc->enable_mask);
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	return reg;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic void
14262306a36Sopenharmony_ciaspeed_sdc_set_phase_taps(struct aspeed_sdc *sdc,
14362306a36Sopenharmony_ci			  const struct aspeed_sdhci_phase_desc *desc,
14462306a36Sopenharmony_ci			  const struct aspeed_sdhci_tap_param *taps)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	u32 reg;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	spin_lock(&sdc->lock);
14962306a36Sopenharmony_ci	reg = readl(sdc->regs + ASPEED_SDC_PHASE);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	reg = aspeed_sdc_set_phase_tap(&desc->in, taps->in, taps->valid, reg);
15262306a36Sopenharmony_ci	reg = aspeed_sdc_set_phase_tap(&desc->out, taps->out, taps->valid, reg);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	writel(reg, sdc->regs + ASPEED_SDC_PHASE);
15562306a36Sopenharmony_ci	spin_unlock(&sdc->lock);
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci#define PICOSECONDS_PER_SECOND		1000000000000ULL
15962306a36Sopenharmony_ci#define ASPEED_SDHCI_NR_TAPS		15
16062306a36Sopenharmony_ci/* Measured value with *handwave* environmentals and static loading */
16162306a36Sopenharmony_ci#define ASPEED_SDHCI_MAX_TAP_DELAY_PS	1253
16262306a36Sopenharmony_cistatic int aspeed_sdhci_phase_to_tap(struct device *dev, unsigned long rate_hz,
16362306a36Sopenharmony_ci				     int phase_deg)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	u64 phase_period_ps;
16662306a36Sopenharmony_ci	u64 prop_delay_ps;
16762306a36Sopenharmony_ci	u64 clk_period_ps;
16862306a36Sopenharmony_ci	unsigned int tap;
16962306a36Sopenharmony_ci	u8 inverted;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	phase_deg %= 360;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	if (phase_deg >= 180) {
17462306a36Sopenharmony_ci		inverted = ASPEED_SDHCI_TAP_PARAM_INVERT_CLK;
17562306a36Sopenharmony_ci		phase_deg -= 180;
17662306a36Sopenharmony_ci		dev_dbg(dev,
17762306a36Sopenharmony_ci			"Inverting clock to reduce phase correction from %d to %d degrees\n",
17862306a36Sopenharmony_ci			phase_deg + 180, phase_deg);
17962306a36Sopenharmony_ci	} else {
18062306a36Sopenharmony_ci		inverted = 0;
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	prop_delay_ps = ASPEED_SDHCI_MAX_TAP_DELAY_PS / ASPEED_SDHCI_NR_TAPS;
18462306a36Sopenharmony_ci	clk_period_ps = div_u64(PICOSECONDS_PER_SECOND, (u64)rate_hz);
18562306a36Sopenharmony_ci	phase_period_ps = div_u64((u64)phase_deg * clk_period_ps, 360ULL);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	tap = div_u64(phase_period_ps, prop_delay_ps);
18862306a36Sopenharmony_ci	if (tap > ASPEED_SDHCI_NR_TAPS) {
18962306a36Sopenharmony_ci		dev_dbg(dev,
19062306a36Sopenharmony_ci			 "Requested out of range phase tap %d for %d degrees of phase compensation at %luHz, clamping to tap %d\n",
19162306a36Sopenharmony_ci			 tap, phase_deg, rate_hz, ASPEED_SDHCI_NR_TAPS);
19262306a36Sopenharmony_ci		tap = ASPEED_SDHCI_NR_TAPS;
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	return inverted | tap;
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic void
19962306a36Sopenharmony_ciaspeed_sdhci_phases_to_taps(struct device *dev, unsigned long rate,
20062306a36Sopenharmony_ci			    const struct mmc_clk_phase *phases,
20162306a36Sopenharmony_ci			    struct aspeed_sdhci_tap_param *taps)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	taps->valid = phases->valid;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	if (!phases->valid)
20662306a36Sopenharmony_ci		return;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	taps->in = aspeed_sdhci_phase_to_tap(dev, rate, phases->in_deg);
20962306a36Sopenharmony_ci	taps->out = aspeed_sdhci_phase_to_tap(dev, rate, phases->out_deg);
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic void
21362306a36Sopenharmony_ciaspeed_sdhci_configure_phase(struct sdhci_host *host, unsigned long rate)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	struct aspeed_sdhci_tap_param _taps = {0}, *taps = &_taps;
21662306a36Sopenharmony_ci	struct mmc_clk_phase *params;
21762306a36Sopenharmony_ci	struct aspeed_sdhci *sdhci;
21862306a36Sopenharmony_ci	struct device *dev;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	dev = mmc_dev(host->mmc);
22162306a36Sopenharmony_ci	sdhci = sdhci_pltfm_priv(sdhci_priv(host));
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	if (!sdhci->phase_desc)
22462306a36Sopenharmony_ci		return;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	params = &sdhci->phase_map.phase[host->timing];
22762306a36Sopenharmony_ci	aspeed_sdhci_phases_to_taps(dev, rate, params, taps);
22862306a36Sopenharmony_ci	aspeed_sdc_set_phase_taps(sdhci->parent, sdhci->phase_desc, taps);
22962306a36Sopenharmony_ci	dev_dbg(dev,
23062306a36Sopenharmony_ci		"Using taps [%d, %d] for [%d, %d] degrees of phase correction at %luHz (%d)\n",
23162306a36Sopenharmony_ci		taps->in & ASPEED_SDHCI_NR_TAPS,
23262306a36Sopenharmony_ci		taps->out & ASPEED_SDHCI_NR_TAPS,
23362306a36Sopenharmony_ci		params->in_deg, params->out_deg, rate, host->timing);
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic void aspeed_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	struct sdhci_pltfm_host *pltfm_host;
23962306a36Sopenharmony_ci	unsigned long parent, bus;
24062306a36Sopenharmony_ci	struct aspeed_sdhci *sdhci;
24162306a36Sopenharmony_ci	int div;
24262306a36Sopenharmony_ci	u16 clk;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	pltfm_host = sdhci_priv(host);
24562306a36Sopenharmony_ci	sdhci = sdhci_pltfm_priv(pltfm_host);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	parent = clk_get_rate(pltfm_host->clk);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if (clock == 0)
25262306a36Sopenharmony_ci		return;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	if (WARN_ON(clock > host->max_clk))
25562306a36Sopenharmony_ci		clock = host->max_clk;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	/*
25862306a36Sopenharmony_ci	 * Regarding the AST2600:
25962306a36Sopenharmony_ci	 *
26062306a36Sopenharmony_ci	 * If (EMMC12C[7:6], EMMC12C[15:8] == 0) then
26162306a36Sopenharmony_ci	 *   period of SDCLK = period of SDMCLK.
26262306a36Sopenharmony_ci	 *
26362306a36Sopenharmony_ci	 * If (EMMC12C[7:6], EMMC12C[15:8] != 0) then
26462306a36Sopenharmony_ci	 *   period of SDCLK = period of SDMCLK * 2 * (EMMC12C[7:6], EMMC[15:8])
26562306a36Sopenharmony_ci	 *
26662306a36Sopenharmony_ci	 * If you keep EMMC12C[7:6] = 0 and EMMC12C[15:8] as one-hot,
26762306a36Sopenharmony_ci	 * 0x1/0x2/0x4/etc, you will find it is compatible to AST2400 or AST2500
26862306a36Sopenharmony_ci	 *
26962306a36Sopenharmony_ci	 * Keep the one-hot behaviour for backwards compatibility except for
27062306a36Sopenharmony_ci	 * supporting the value 0 in (EMMC12C[7:6], EMMC12C[15:8]), and capture
27162306a36Sopenharmony_ci	 * the 0-value capability in clk_div_start.
27262306a36Sopenharmony_ci	 */
27362306a36Sopenharmony_ci	for (div = sdhci->pdata->clk_div_start; div < 256; div *= 2) {
27462306a36Sopenharmony_ci		bus = parent / div;
27562306a36Sopenharmony_ci		if (bus <= clock)
27662306a36Sopenharmony_ci			break;
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	div >>= 1;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	clk = div << SDHCI_DIVIDER_SHIFT;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	aspeed_sdhci_configure_phase(host, bus);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	sdhci_enable_clk(host, clk);
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic unsigned int aspeed_sdhci_get_max_clock(struct sdhci_host *host)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	if (host->mmc->f_max)
29162306a36Sopenharmony_ci		return host->mmc->f_max;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	return sdhci_pltfm_clk_get_max_clock(host);
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic void aspeed_sdhci_set_bus_width(struct sdhci_host *host, int width)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	struct sdhci_pltfm_host *pltfm_priv;
29962306a36Sopenharmony_ci	struct aspeed_sdhci *aspeed_sdhci;
30062306a36Sopenharmony_ci	struct aspeed_sdc *aspeed_sdc;
30162306a36Sopenharmony_ci	u8 ctrl;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	pltfm_priv = sdhci_priv(host);
30462306a36Sopenharmony_ci	aspeed_sdhci = sdhci_pltfm_priv(pltfm_priv);
30562306a36Sopenharmony_ci	aspeed_sdc = aspeed_sdhci->parent;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	/* Set/clear 8-bit mode */
30862306a36Sopenharmony_ci	aspeed_sdc_configure_8bit_mode(aspeed_sdc, aspeed_sdhci,
30962306a36Sopenharmony_ci				       width == MMC_BUS_WIDTH_8);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	/* Set/clear 1 or 4 bit mode */
31262306a36Sopenharmony_ci	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
31362306a36Sopenharmony_ci	if (width == MMC_BUS_WIDTH_4)
31462306a36Sopenharmony_ci		ctrl |= SDHCI_CTRL_4BITBUS;
31562306a36Sopenharmony_ci	else
31662306a36Sopenharmony_ci		ctrl &= ~SDHCI_CTRL_4BITBUS;
31762306a36Sopenharmony_ci	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic u32 aspeed_sdhci_readl(struct sdhci_host *host, int reg)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	u32 val = readl(host->ioaddr + reg);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	if (unlikely(reg == SDHCI_PRESENT_STATE) &&
32562306a36Sopenharmony_ci	    (host->mmc->caps2 & MMC_CAP2_CD_ACTIVE_HIGH))
32662306a36Sopenharmony_ci		val ^= SDHCI_CARD_PRESENT;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	return val;
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_cistatic const struct sdhci_ops aspeed_sdhci_ops = {
33262306a36Sopenharmony_ci	.read_l = aspeed_sdhci_readl,
33362306a36Sopenharmony_ci	.set_clock = aspeed_sdhci_set_clock,
33462306a36Sopenharmony_ci	.get_max_clock = aspeed_sdhci_get_max_clock,
33562306a36Sopenharmony_ci	.set_bus_width = aspeed_sdhci_set_bus_width,
33662306a36Sopenharmony_ci	.get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
33762306a36Sopenharmony_ci	.reset = sdhci_reset,
33862306a36Sopenharmony_ci	.set_uhs_signaling = sdhci_set_uhs_signaling,
33962306a36Sopenharmony_ci};
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistatic const struct sdhci_pltfm_data aspeed_sdhci_pdata = {
34262306a36Sopenharmony_ci	.ops = &aspeed_sdhci_ops,
34362306a36Sopenharmony_ci	.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
34462306a36Sopenharmony_ci};
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_cistatic inline int aspeed_sdhci_calculate_slot(struct aspeed_sdhci *dev,
34762306a36Sopenharmony_ci					      struct resource *res)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	resource_size_t delta;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	if (!res || resource_type(res) != IORESOURCE_MEM)
35262306a36Sopenharmony_ci		return -EINVAL;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	if (res->start < dev->parent->res->start)
35562306a36Sopenharmony_ci		return -EINVAL;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	delta = res->start - dev->parent->res->start;
35862306a36Sopenharmony_ci	if (delta & (0x100 - 1))
35962306a36Sopenharmony_ci		return -EINVAL;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	return (delta / 0x100) - 1;
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_cistatic int aspeed_sdhci_probe(struct platform_device *pdev)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	const struct aspeed_sdhci_pdata *aspeed_pdata;
36762306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
36862306a36Sopenharmony_ci	struct sdhci_pltfm_host *pltfm_host;
36962306a36Sopenharmony_ci	struct aspeed_sdhci *dev;
37062306a36Sopenharmony_ci	struct sdhci_host *host;
37162306a36Sopenharmony_ci	struct resource *res;
37262306a36Sopenharmony_ci	int slot;
37362306a36Sopenharmony_ci	int ret;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	aspeed_pdata = of_device_get_match_data(&pdev->dev);
37662306a36Sopenharmony_ci	if (!aspeed_pdata) {
37762306a36Sopenharmony_ci		dev_err(&pdev->dev, "Missing platform configuration data\n");
37862306a36Sopenharmony_ci		return -EINVAL;
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	host = sdhci_pltfm_init(pdev, &aspeed_sdhci_pdata, sizeof(*dev));
38262306a36Sopenharmony_ci	if (IS_ERR(host))
38362306a36Sopenharmony_ci		return PTR_ERR(host);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	pltfm_host = sdhci_priv(host);
38662306a36Sopenharmony_ci	dev = sdhci_pltfm_priv(pltfm_host);
38762306a36Sopenharmony_ci	dev->pdata = aspeed_pdata;
38862306a36Sopenharmony_ci	dev->parent = dev_get_drvdata(pdev->dev.parent);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
39162306a36Sopenharmony_ci	slot = aspeed_sdhci_calculate_slot(dev, res);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	if (slot < 0)
39462306a36Sopenharmony_ci		return slot;
39562306a36Sopenharmony_ci	else if (slot >= 2)
39662306a36Sopenharmony_ci		return -EINVAL;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	if (slot < dev->pdata->nr_phase_descs) {
39962306a36Sopenharmony_ci		dev->phase_desc = &dev->pdata->phase_desc[slot];
40062306a36Sopenharmony_ci	} else {
40162306a36Sopenharmony_ci		dev_info(&pdev->dev,
40262306a36Sopenharmony_ci			 "Phase control not supported for slot %d\n", slot);
40362306a36Sopenharmony_ci		dev->phase_desc = NULL;
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	dev->width_mask = !slot ? ASPEED_SDC_S0_MMC8 : ASPEED_SDC_S1_MMC8;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	dev_info(&pdev->dev, "Configured for slot %d\n", slot);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	sdhci_get_of_property(pdev);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	if (of_property_read_bool(np, "mmc-hs200-1_8v") ||
41362306a36Sopenharmony_ci	    of_property_read_bool(np, "sd-uhs-sdr104")) {
41462306a36Sopenharmony_ci		aspeed_sdc_set_slot_capability(host, dev->parent, ASPEED_SDC_CAP1_1_8V,
41562306a36Sopenharmony_ci					       true, slot);
41662306a36Sopenharmony_ci	}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	if (of_property_read_bool(np, "sd-uhs-sdr104")) {
41962306a36Sopenharmony_ci		aspeed_sdc_set_slot_capability(host, dev->parent, ASPEED_SDC_CAP2_SDR104,
42062306a36Sopenharmony_ci					       true, slot);
42162306a36Sopenharmony_ci	}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	pltfm_host->clk = devm_clk_get(&pdev->dev, NULL);
42462306a36Sopenharmony_ci	if (IS_ERR(pltfm_host->clk))
42562306a36Sopenharmony_ci		return PTR_ERR(pltfm_host->clk);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	ret = clk_prepare_enable(pltfm_host->clk);
42862306a36Sopenharmony_ci	if (ret) {
42962306a36Sopenharmony_ci		dev_err(&pdev->dev, "Unable to enable SDIO clock\n");
43062306a36Sopenharmony_ci		goto err_pltfm_free;
43162306a36Sopenharmony_ci	}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	ret = mmc_of_parse(host->mmc);
43462306a36Sopenharmony_ci	if (ret)
43562306a36Sopenharmony_ci		goto err_sdhci_add;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	if (dev->phase_desc)
43862306a36Sopenharmony_ci		mmc_of_parse_clk_phase(host->mmc, &dev->phase_map);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	ret = sdhci_add_host(host);
44162306a36Sopenharmony_ci	if (ret)
44262306a36Sopenharmony_ci		goto err_sdhci_add;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	return 0;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_cierr_sdhci_add:
44762306a36Sopenharmony_ci	clk_disable_unprepare(pltfm_host->clk);
44862306a36Sopenharmony_cierr_pltfm_free:
44962306a36Sopenharmony_ci	sdhci_pltfm_free(pdev);
45062306a36Sopenharmony_ci	return ret;
45162306a36Sopenharmony_ci}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_cistatic void aspeed_sdhci_remove(struct platform_device *pdev)
45462306a36Sopenharmony_ci{
45562306a36Sopenharmony_ci	struct sdhci_pltfm_host *pltfm_host;
45662306a36Sopenharmony_ci	struct sdhci_host *host;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	host = platform_get_drvdata(pdev);
45962306a36Sopenharmony_ci	pltfm_host = sdhci_priv(host);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	sdhci_remove_host(host, 0);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	clk_disable_unprepare(pltfm_host->clk);
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	sdhci_pltfm_free(pdev);
46662306a36Sopenharmony_ci}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_cistatic const struct aspeed_sdhci_pdata ast2400_sdhci_pdata = {
46962306a36Sopenharmony_ci	.clk_div_start = 2,
47062306a36Sopenharmony_ci};
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_cistatic const struct aspeed_sdhci_phase_desc ast2600_sdhci_phase[] = {
47362306a36Sopenharmony_ci	/* SDHCI/Slot 0 */
47462306a36Sopenharmony_ci	[0] = {
47562306a36Sopenharmony_ci		.in = {
47662306a36Sopenharmony_ci			.tap_mask = ASPEED_SDC_S0_PHASE_IN,
47762306a36Sopenharmony_ci			.enable_mask = ASPEED_SDC_S0_PHASE_IN_EN,
47862306a36Sopenharmony_ci			.enable_value = 1,
47962306a36Sopenharmony_ci		},
48062306a36Sopenharmony_ci		.out = {
48162306a36Sopenharmony_ci			.tap_mask = ASPEED_SDC_S0_PHASE_OUT,
48262306a36Sopenharmony_ci			.enable_mask = ASPEED_SDC_S0_PHASE_OUT_EN,
48362306a36Sopenharmony_ci			.enable_value = 3,
48462306a36Sopenharmony_ci		},
48562306a36Sopenharmony_ci	},
48662306a36Sopenharmony_ci	/* SDHCI/Slot 1 */
48762306a36Sopenharmony_ci	[1] = {
48862306a36Sopenharmony_ci		.in = {
48962306a36Sopenharmony_ci			.tap_mask = ASPEED_SDC_S1_PHASE_IN,
49062306a36Sopenharmony_ci			.enable_mask = ASPEED_SDC_S1_PHASE_IN_EN,
49162306a36Sopenharmony_ci			.enable_value = 1,
49262306a36Sopenharmony_ci		},
49362306a36Sopenharmony_ci		.out = {
49462306a36Sopenharmony_ci			.tap_mask = ASPEED_SDC_S1_PHASE_OUT,
49562306a36Sopenharmony_ci			.enable_mask = ASPEED_SDC_S1_PHASE_OUT_EN,
49662306a36Sopenharmony_ci			.enable_value = 3,
49762306a36Sopenharmony_ci		},
49862306a36Sopenharmony_ci	},
49962306a36Sopenharmony_ci};
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_cistatic const struct aspeed_sdhci_pdata ast2600_sdhci_pdata = {
50262306a36Sopenharmony_ci	.clk_div_start = 1,
50362306a36Sopenharmony_ci	.phase_desc = ast2600_sdhci_phase,
50462306a36Sopenharmony_ci	.nr_phase_descs = ARRAY_SIZE(ast2600_sdhci_phase),
50562306a36Sopenharmony_ci};
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_cistatic const struct of_device_id aspeed_sdhci_of_match[] = {
50862306a36Sopenharmony_ci	{ .compatible = "aspeed,ast2400-sdhci", .data = &ast2400_sdhci_pdata, },
50962306a36Sopenharmony_ci	{ .compatible = "aspeed,ast2500-sdhci", .data = &ast2400_sdhci_pdata, },
51062306a36Sopenharmony_ci	{ .compatible = "aspeed,ast2600-sdhci", .data = &ast2600_sdhci_pdata, },
51162306a36Sopenharmony_ci	{ }
51262306a36Sopenharmony_ci};
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_cistatic struct platform_driver aspeed_sdhci_driver = {
51562306a36Sopenharmony_ci	.driver		= {
51662306a36Sopenharmony_ci		.name	= "sdhci-aspeed",
51762306a36Sopenharmony_ci		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
51862306a36Sopenharmony_ci		.of_match_table = aspeed_sdhci_of_match,
51962306a36Sopenharmony_ci	},
52062306a36Sopenharmony_ci	.probe		= aspeed_sdhci_probe,
52162306a36Sopenharmony_ci	.remove_new	= aspeed_sdhci_remove,
52262306a36Sopenharmony_ci};
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cistatic int aspeed_sdc_probe(struct platform_device *pdev)
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci{
52762306a36Sopenharmony_ci	struct device_node *parent, *child;
52862306a36Sopenharmony_ci	struct aspeed_sdc *sdc;
52962306a36Sopenharmony_ci	int ret;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	sdc = devm_kzalloc(&pdev->dev, sizeof(*sdc), GFP_KERNEL);
53262306a36Sopenharmony_ci	if (!sdc)
53362306a36Sopenharmony_ci		return -ENOMEM;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	spin_lock_init(&sdc->lock);
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	sdc->clk = devm_clk_get(&pdev->dev, NULL);
53862306a36Sopenharmony_ci	if (IS_ERR(sdc->clk))
53962306a36Sopenharmony_ci		return PTR_ERR(sdc->clk);
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	ret = clk_prepare_enable(sdc->clk);
54262306a36Sopenharmony_ci	if (ret) {
54362306a36Sopenharmony_ci		dev_err(&pdev->dev, "Unable to enable SDCLK\n");
54462306a36Sopenharmony_ci		return ret;
54562306a36Sopenharmony_ci	}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	sdc->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &sdc->res);
54862306a36Sopenharmony_ci	if (IS_ERR(sdc->regs)) {
54962306a36Sopenharmony_ci		ret = PTR_ERR(sdc->regs);
55062306a36Sopenharmony_ci		goto err_clk;
55162306a36Sopenharmony_ci	}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	dev_set_drvdata(&pdev->dev, sdc);
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	parent = pdev->dev.of_node;
55662306a36Sopenharmony_ci	for_each_available_child_of_node(parent, child) {
55762306a36Sopenharmony_ci		struct platform_device *cpdev;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci		cpdev = of_platform_device_create(child, NULL, &pdev->dev);
56062306a36Sopenharmony_ci		if (!cpdev) {
56162306a36Sopenharmony_ci			of_node_put(child);
56262306a36Sopenharmony_ci			ret = -ENODEV;
56362306a36Sopenharmony_ci			goto err_clk;
56462306a36Sopenharmony_ci		}
56562306a36Sopenharmony_ci	}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	return 0;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_cierr_clk:
57062306a36Sopenharmony_ci	clk_disable_unprepare(sdc->clk);
57162306a36Sopenharmony_ci	return ret;
57262306a36Sopenharmony_ci}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_cistatic void aspeed_sdc_remove(struct platform_device *pdev)
57562306a36Sopenharmony_ci{
57662306a36Sopenharmony_ci	struct aspeed_sdc *sdc = dev_get_drvdata(&pdev->dev);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	clk_disable_unprepare(sdc->clk);
57962306a36Sopenharmony_ci}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_cistatic const struct of_device_id aspeed_sdc_of_match[] = {
58262306a36Sopenharmony_ci	{ .compatible = "aspeed,ast2400-sd-controller", },
58362306a36Sopenharmony_ci	{ .compatible = "aspeed,ast2500-sd-controller", },
58462306a36Sopenharmony_ci	{ .compatible = "aspeed,ast2600-sd-controller", },
58562306a36Sopenharmony_ci	{ }
58662306a36Sopenharmony_ci};
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, aspeed_sdc_of_match);
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_cistatic struct platform_driver aspeed_sdc_driver = {
59162306a36Sopenharmony_ci	.driver		= {
59262306a36Sopenharmony_ci		.name	= "sd-controller-aspeed",
59362306a36Sopenharmony_ci		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
59462306a36Sopenharmony_ci		.pm	= &sdhci_pltfm_pmops,
59562306a36Sopenharmony_ci		.of_match_table = aspeed_sdc_of_match,
59662306a36Sopenharmony_ci	},
59762306a36Sopenharmony_ci	.probe		= aspeed_sdc_probe,
59862306a36Sopenharmony_ci	.remove_new	= aspeed_sdc_remove,
59962306a36Sopenharmony_ci};
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci#if defined(CONFIG_MMC_SDHCI_OF_ASPEED_TEST)
60262306a36Sopenharmony_ci#include "sdhci-of-aspeed-test.c"
60362306a36Sopenharmony_ci#endif
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_cistatic int __init aspeed_sdc_init(void)
60662306a36Sopenharmony_ci{
60762306a36Sopenharmony_ci	int rc;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	rc = platform_driver_register(&aspeed_sdhci_driver);
61062306a36Sopenharmony_ci	if (rc < 0)
61162306a36Sopenharmony_ci		return rc;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	rc = platform_driver_register(&aspeed_sdc_driver);
61462306a36Sopenharmony_ci	if (rc < 0)
61562306a36Sopenharmony_ci		platform_driver_unregister(&aspeed_sdhci_driver);
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	return rc;
61862306a36Sopenharmony_ci}
61962306a36Sopenharmony_cimodule_init(aspeed_sdc_init);
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_cistatic void __exit aspeed_sdc_exit(void)
62262306a36Sopenharmony_ci{
62362306a36Sopenharmony_ci	platform_driver_unregister(&aspeed_sdc_driver);
62462306a36Sopenharmony_ci	platform_driver_unregister(&aspeed_sdhci_driver);
62562306a36Sopenharmony_ci}
62662306a36Sopenharmony_cimodule_exit(aspeed_sdc_exit);
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ciMODULE_DESCRIPTION("Driver for the ASPEED SD/SDIO/SDHCI Controllers");
62962306a36Sopenharmony_ciMODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
63062306a36Sopenharmony_ciMODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
63162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
632