zynq_sdhci.c 7.0 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0+
2
/*
M
Michal Simek 已提交
3
 * (C) Copyright 2013 - 2015 Xilinx, Inc.
4 5 6 7
 *
 * Xilinx Zynq SD Host Controller Interface
 */

8
#include <clk.h>
9
#include <common.h>
M
Michal Simek 已提交
10
#include <dm.h>
11
#include <fdtdec.h>
12
#include <linux/delay.h>
13
#include "mmc_private.h"
14
#include <log.h>
15
#include <dm/device_compat.h>
16
#include <linux/err.h>
17
#include <linux/libfdt.h>
18 19
#include <malloc.h>
#include <sdhci.h>
20
#include <zynqmp_tap_delay.h>
21

22 23 24 25 26
struct arasan_sdhci_plat {
	struct mmc_config cfg;
	struct mmc mmc;
};

27 28 29 30 31 32 33
struct arasan_sdhci_priv {
	struct sdhci_host *host;
	u8 deviceid;
	u8 bank;
};

#if defined(CONFIG_ARCH_ZYNQMP)
34 35
#define MMC_HS200_BUS_SPEED	5

36
static const u8 mode2timing[] = {
37 38 39 40 41 42 43 44 45 46 47
	[MMC_LEGACY] = UHS_SDR12_BUS_SPEED,
	[MMC_HS] = HIGH_SPEED_BUS_SPEED,
	[SD_HS] = HIGH_SPEED_BUS_SPEED,
	[MMC_HS_52] = HIGH_SPEED_BUS_SPEED,
	[MMC_DDR_52] = HIGH_SPEED_BUS_SPEED,
	[UHS_SDR12] = UHS_SDR12_BUS_SPEED,
	[UHS_SDR25] = UHS_SDR25_BUS_SPEED,
	[UHS_SDR50] = UHS_SDR50_BUS_SPEED,
	[UHS_DDR50] = UHS_DDR50_BUS_SPEED,
	[UHS_SDR104] = UHS_SDR104_BUS_SPEED,
	[MMC_HS_200] = MMC_HS200_BUS_SPEED,
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
};

#define SDHCI_TUNING_LOOP_COUNT	40

static void arasan_zynqmp_dll_reset(struct sdhci_host *host, u8 deviceid)
{
	u16 clk;
	unsigned long timeout;

	clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
	clk &= ~(SDHCI_CLOCK_CARD_EN);
	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);

	/* Issue DLL Reset */
	zynqmp_dll_reset(deviceid);

	/* Wait max 20 ms */
	timeout = 100;
	while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
				& SDHCI_CLOCK_INT_STABLE)) {
		if (timeout == 0) {
			dev_err(mmc_dev(host->mmc),
				": Internal clock never stabilised.\n");
			return;
		}
		timeout--;
		udelay(1000);
	}

	clk |= SDHCI_CLOCK_CARD_EN;
	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
}

static int arasan_sdhci_execute_tuning(struct mmc *mmc, u8 opcode)
{
	struct mmc_cmd cmd;
	struct mmc_data data;
	u32 ctrl;
	struct sdhci_host *host;
	struct arasan_sdhci_priv *priv = dev_get_priv(mmc->dev);
88
	char tuning_loop_counter = SDHCI_TUNING_LOOP_COUNT;
89 90 91 92 93 94 95
	u8 deviceid;

	debug("%s\n", __func__);

	host = priv->host;
	deviceid = priv->deviceid;

96
	ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
97
	ctrl |= SDHCI_CTRL_EXEC_TUNING;
98
	sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129

	mdelay(1);

	arasan_zynqmp_dll_reset(host, deviceid);

	sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_INT_ENABLE);
	sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_SIGNAL_ENABLE);

	do {
		cmd.cmdidx = opcode;
		cmd.resp_type = MMC_RSP_R1;
		cmd.cmdarg = 0;

		data.blocksize = 64;
		data.blocks = 1;
		data.flags = MMC_DATA_READ;

		if (tuning_loop_counter-- == 0)
			break;

		if (cmd.cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200 &&
		    mmc->bus_width == 8)
			data.blocksize = 128;

		sdhci_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG,
						    data.blocksize),
			     SDHCI_BLOCK_SIZE);
		sdhci_writew(host, data.blocks, SDHCI_BLOCK_COUNT);
		sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE);

		mmc_send_cmd(mmc, &cmd, NULL);
130
		ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
131 132 133 134 135 136 137 138

		if (cmd.cmdidx == MMC_CMD_SEND_TUNING_BLOCK)
			udelay(1);

	} while (ctrl & SDHCI_CTRL_EXEC_TUNING);

	if (tuning_loop_counter < 0) {
		ctrl &= ~SDHCI_CTRL_TUNED_CLK;
139
		sdhci_writel(host, ctrl, SDHCI_HOST_CONTROL2);
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
	}

	if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) {
		printf("%s:Tuning failed\n", __func__);
		return -1;
	}

	udelay(1);
	arasan_zynqmp_dll_reset(host, deviceid);

	/* Enable only interrupts served by the SD controller */
	sdhci_writel(host, SDHCI_INT_DATA_MASK | SDHCI_INT_CMD_MASK,
		     SDHCI_INT_ENABLE);
	/* Mask all sdhci interrupt sources */
	sdhci_writel(host, 0x0, SDHCI_SIGNAL_ENABLE);

	return 0;
}

static void arasan_sdhci_set_tapdelay(struct sdhci_host *host)
{
	struct arasan_sdhci_priv *priv = dev_get_priv(host->mmc->dev);
	struct mmc *mmc = (struct mmc *)host->mmc;
	u8 uhsmode;

	uhsmode = mode2timing[mmc->selected_mode];

	if (uhsmode >= UHS_SDR25_BUS_SPEED)
		arasan_zynqmp_set_tapdelay(priv->deviceid, uhsmode,
					   priv->bank);
}

static void arasan_sdhci_set_control_reg(struct sdhci_host *host)
{
	struct mmc *mmc = (struct mmc *)host->mmc;
	u32 reg;

177 178 179
	if (!IS_SD(mmc))
		return;

180
	if (mmc->signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
181 182 183
		reg = sdhci_readw(host, SDHCI_HOST_CONTROL2);
		reg |= SDHCI_CTRL_VDD_180;
		sdhci_writew(host, reg, SDHCI_HOST_CONTROL2);
184 185 186
	}

	if (mmc->selected_mode > SD_HS &&
187 188
	    mmc->selected_mode <= UHS_DDR50)
		sdhci_set_uhs_timing(host);
189 190 191
}
#endif

192
#if defined(CONFIG_ARCH_ZYNQMP)
193 194 195 196 197 198 199
const struct sdhci_ops arasan_ops = {
	.platform_execute_tuning	= &arasan_sdhci_execute_tuning,
	.set_delay = &arasan_sdhci_set_tapdelay,
	.set_control_reg = &arasan_sdhci_set_control_reg,
};
#endif

M
Michal Simek 已提交
200
static int arasan_sdhci_probe(struct udevice *dev)
201
{
202
	struct arasan_sdhci_plat *plat = dev_get_platdata(dev);
M
Michal Simek 已提交
203
	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
204 205
	struct arasan_sdhci_priv *priv = dev_get_priv(dev);
	struct sdhci_host *host;
206 207
	struct clk clk;
	unsigned long clock;
208
	int ret;
209

210 211
	host = priv->host;

212 213 214 215 216 217 218 219 220 221 222
	ret = clk_get_by_index(dev, 0, &clk);
	if (ret < 0) {
		dev_err(dev, "failed to get clock\n");
		return ret;
	}

	clock = clk_get_rate(&clk);
	if (IS_ERR_VALUE(clock)) {
		dev_err(dev, "failed to get rate\n");
		return clock;
	}
223

224 225 226 227 228 229 230 231
	debug("%s: CLK %ld\n", __func__, clock);

	ret = clk_enable(&clk);
	if (ret && ret != -ENOSYS) {
		dev_err(dev, "failed to enable clock\n");
		return ret;
	}

232
	host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD |
233
		       SDHCI_QUIRK_BROKEN_R1B;
234 235

#ifdef CONFIG_ZYNQ_HISPD_BROKEN
236
	host->quirks |= SDHCI_QUIRK_BROKEN_HISPD_MODE;
237 238
#endif

239 240 241 242 243
	plat->cfg.f_max = CONFIG_ZYNQ_SDHCI_MAX_FREQ;

	ret = mmc_of_parse(dev, &plat->cfg);
	if (ret)
		return ret;
244

245
	host->max_clk = clock;
246

247 248 249 250
	host->mmc = &plat->mmc;
	host->mmc->dev = dev;
	host->mmc->priv = host;

251
	ret = sdhci_setup_cfg(&plat->cfg, host, plat->cfg.f_max,
252
			      CONFIG_ZYNQ_SDHCI_MIN_FREQ);
253 254 255
	if (ret)
		return ret;
	upriv->mmc = host->mmc;
M
Michal Simek 已提交
256

257
	return sdhci_probe(dev);
258
}
M
Michal Simek 已提交
259 260 261

static int arasan_sdhci_ofdata_to_platdata(struct udevice *dev)
{
262 263 264 265 266
	struct arasan_sdhci_priv *priv = dev_get_priv(dev);

	priv->host = calloc(1, sizeof(struct sdhci_host));
	if (!priv->host)
		return -1;
M
Michal Simek 已提交
267

268 269
	priv->host->name = dev->name;

270
#if defined(CONFIG_ARCH_ZYNQMP)
271 272
	priv->host->ops = &arasan_ops;
#endif
M
Michal Simek 已提交
273

274 275 276
	priv->host->ioaddr = (void *)dev_read_addr(dev);
	if (IS_ERR(priv->host->ioaddr))
		return PTR_ERR(priv->host->ioaddr);
277

278
	priv->deviceid = dev_read_u32_default(dev, "xlnx,device_id", -1);
279
	priv->bank = dev_read_u32_default(dev, "xlnx,mio-bank", -1);
280

M
Michal Simek 已提交
281 282 283
	return 0;
}

284 285 286 287
static int arasan_sdhci_bind(struct udevice *dev)
{
	struct arasan_sdhci_plat *plat = dev_get_platdata(dev);

288
	return sdhci_bind(dev, &plat->mmc, &plat->cfg);
289 290
}

M
Michal Simek 已提交
291 292 293 294 295 296 297 298 299 300
static const struct udevice_id arasan_sdhci_ids[] = {
	{ .compatible = "arasan,sdhci-8.9a" },
	{ }
};

U_BOOT_DRIVER(arasan_sdhci_drv) = {
	.name		= "arasan_sdhci",
	.id		= UCLASS_MMC,
	.of_match	= arasan_sdhci_ids,
	.ofdata_to_platdata = arasan_sdhci_ofdata_to_platdata,
301 302
	.ops		= &sdhci_ops,
	.bind		= arasan_sdhci_bind,
M
Michal Simek 已提交
303
	.probe		= arasan_sdhci_probe,
304
	.priv_auto_alloc_size = sizeof(struct arasan_sdhci_priv),
305
	.platdata_auto_alloc_size = sizeof(struct arasan_sdhci_plat),
M
Michal Simek 已提交
306
};