sh_mobile_sdhi.c 10.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
 * SuperH Mobile SDHI
 *
 * Copyright (C) 2009 Magnus Damm
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * Based on "Compaq ASIC3 support":
 *
 * Copyright 2001 Compaq Computer Corporation.
 * Copyright 2004-2005 Phil Blundell
 * Copyright 2007-2008 OpenedHand Ltd.
 *
 * Authors: Phil Blundell <pb@handhelds.org>,
 *	    Samuel Ortiz <sameo@openedhand.com>
 *
 */

#include <linux/kernel.h>
#include <linux/clk.h>
23
#include <linux/slab.h>
24
#include <linux/mod_devicetable.h>
25
#include <linux/module.h>
26
#include <linux/of_device.h>
27
#include <linux/platform_device.h>
28
#include <linux/mmc/host.h>
29
#include <linux/mmc/sh_mobile_sdhi.h>
30
#include <linux/mfd/tmio.h>
31
#include <linux/sh_dma.h>
S
Simon Horman 已提交
32
#include <linux/delay.h>
33

34 35
#include "tmio_mmc.h"

36 37
#define EXT_ACC           0xe4

38 39
struct sh_mobile_sdhi_of_data {
	unsigned long tmio_flags;
40
	unsigned long capabilities;
41
	unsigned long capabilities2;
42
	dma_addr_t dma_rx_offset;
43 44 45 46 47 48 49 50
};

static const struct sh_mobile_sdhi_of_data sh_mobile_sdhi_of_cfg[] = {
	{
		.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT,
	},
};

51
static const struct sh_mobile_sdhi_of_data of_rcar_gen1_compatible = {
52 53
	.tmio_flags	= TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
			  TMIO_MMC_CLK_ACTUAL,
54 55 56
	.capabilities	= MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
};

57
static const struct sh_mobile_sdhi_of_data of_rcar_gen2_compatible = {
58 59
	.tmio_flags	= TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
			  TMIO_MMC_CLK_ACTUAL,
60
	.capabilities	= MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
61
	.dma_rx_offset	= 0x2000,
62 63
};

64 65 66 67 68 69
static const struct of_device_id sh_mobile_sdhi_of_match[] = {
	{ .compatible = "renesas,sdhi-shmobile" },
	{ .compatible = "renesas,sdhi-sh7372" },
	{ .compatible = "renesas,sdhi-sh73a0", .data = &sh_mobile_sdhi_of_cfg[0], },
	{ .compatible = "renesas,sdhi-r8a73a4", .data = &sh_mobile_sdhi_of_cfg[0], },
	{ .compatible = "renesas,sdhi-r8a7740", .data = &sh_mobile_sdhi_of_cfg[0], },
70
	{ .compatible = "renesas,sdhi-r8a7778", .data = &of_rcar_gen1_compatible, },
71
	{ .compatible = "renesas,sdhi-r8a7779", .data = &of_rcar_gen1_compatible, },
72
	{ .compatible = "renesas,sdhi-r8a7790", .data = &of_rcar_gen2_compatible, },
73
	{ .compatible = "renesas,sdhi-r8a7791", .data = &of_rcar_gen2_compatible, },
74 75 76
	{ .compatible = "renesas,sdhi-r8a7792", .data = &of_rcar_gen2_compatible, },
	{ .compatible = "renesas,sdhi-r8a7793", .data = &of_rcar_gen2_compatible, },
	{ .compatible = "renesas,sdhi-r8a7794", .data = &of_rcar_gen2_compatible, },
77 78 79 80
	{},
};
MODULE_DEVICE_TABLE(of, sh_mobile_sdhi_of_match);

81 82 83
struct sh_mobile_sdhi {
	struct clk *clk;
	struct tmio_mmc_data mmc_data;
84
	struct tmio_mmc_dma dma_priv;
85 86
};

87 88
static int sh_mobile_sdhi_clk_enable(struct platform_device *pdev, unsigned int *f)
{
89
	struct mmc_host *mmc = platform_get_drvdata(pdev);
90 91
	struct tmio_mmc_host *host = mmc_priv(mmc);
	struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
92
	int ret = clk_prepare_enable(priv->clk);
93 94 95 96 97 98 99 100 101
	if (ret < 0)
		return ret;

	*f = clk_get_rate(priv->clk);
	return 0;
}

static void sh_mobile_sdhi_clk_disable(struct platform_device *pdev)
{
102
	struct mmc_host *mmc = platform_get_drvdata(pdev);
103 104
	struct tmio_mmc_host *host = mmc_priv(mmc);
	struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
105
	clk_disable_unprepare(priv->clk);
106 107
}

S
Simon Horman 已提交
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
static int sh_mobile_sdhi_wait_idle(struct tmio_mmc_host *host)
{
	int timeout = 1000;

	while (--timeout && !(sd_ctrl_read16(host, CTL_STATUS2) & (1 << 13)))
		udelay(1);

	if (!timeout) {
		dev_warn(host->pdata->dev, "timeout waiting for SD bus idle\n");
		return -EBUSY;
	}

	return 0;
}

static int sh_mobile_sdhi_write16_hook(struct tmio_mmc_host *host, int addr)
{
	switch (addr)
	{
	case CTL_SD_CMD:
	case CTL_STOP_INTERNAL_ACTION:
	case CTL_XFER_BLK_COUNT:
	case CTL_SD_CARD_CLK_CTL:
	case CTL_SD_XFER_LEN:
	case CTL_SD_MEM_CARD_OPT:
	case CTL_TRANSACTION_CTL:
	case CTL_DMA_ENABLE:
		return sh_mobile_sdhi_wait_idle(host);
	}

	return 0;
}

141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
static int sh_mobile_sdhi_multi_io_quirk(struct mmc_card *card,
					 unsigned int direction, int blk_size)
{
	/*
	 * In Renesas controllers, when performing a
	 * multiple block read of one or two blocks,
	 * depending on the timing with which the
	 * response register is read, the response
	 * value may not be read properly.
	 * Use single block read for this HW bug
	 */
	if ((direction == MMC_DATA_READ) &&
	    blk_size == 2)
		return 1;

	return blk_size;
}

159 160
static void sh_mobile_sdhi_cd_wakeup(const struct platform_device *pdev)
{
161
	mmc_detect_change(platform_get_drvdata(pdev), msecs_to_jiffies(100));
162 163 164 165 166 167
}

static const struct sh_mobile_sdhi_ops sdhi_ops = {
	.cd_wakeup = sh_mobile_sdhi_cd_wakeup,
};

B
Bill Pemberton 已提交
168
static int sh_mobile_sdhi_probe(struct platform_device *pdev)
169
{
170 171
	const struct of_device_id *of_id =
		of_match_device(sh_mobile_sdhi_of_match, &pdev->dev);
172
	struct sh_mobile_sdhi *priv;
173 174
	struct tmio_mmc_data *mmc_data;
	struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
175
	struct tmio_mmc_host *host;
176
	struct resource *res;
177 178
	int irq, ret, i = 0;
	bool multiplexed_isr = true;
179
	struct tmio_mmc_dma *dma_priv;
180
	u16 ver;
181

182 183 184 185
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!res)
		return -EINVAL;

186
	priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_mobile_sdhi), GFP_KERNEL);
187 188 189 190 191
	if (priv == NULL) {
		dev_err(&pdev->dev, "kzalloc failed\n");
		return -ENOMEM;
	}

192
	mmc_data = &priv->mmc_data;
193
	dma_priv = &priv->dma_priv;
194

195 196 197 198
	if (p) {
		if (p->init) {
			ret = p->init(pdev, &sdhi_ops);
			if (ret)
199
				return ret;
200
		}
201 202
	}

203
	priv->clk = devm_clk_get(&pdev->dev, NULL);
204 205
	if (IS_ERR(priv->clk)) {
		ret = PTR_ERR(priv->clk);
206
		dev_err(&pdev->dev, "cannot get clock: %d\n", ret);
207
		goto eclkget;
208 209
	}

210 211
	mmc_data->clk_enable = sh_mobile_sdhi_clk_enable;
	mmc_data->clk_disable = sh_mobile_sdhi_clk_disable;
212
	mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED;
213
	mmc_data->write16_hook = sh_mobile_sdhi_write16_hook;
214
	mmc_data->multi_io_quirk = sh_mobile_sdhi_multi_io_quirk;
215
	if (p) {
216
		mmc_data->flags = p->tmio_flags;
217
		mmc_data->ocr_mask = p->tmio_ocr_mask;
218
		mmc_data->capabilities |= p->tmio_caps;
219
		mmc_data->capabilities2 |= p->tmio_caps2;
220
		mmc_data->cd_gpio = p->cd_gpio;
221

222
		if (p->dma_slave_tx > 0 && p->dma_slave_rx > 0) {
223 224 225 226 227 228 229 230 231
			/*
			 * Yes, we have to provide slave IDs twice to TMIO:
			 * once as a filter parameter and once for channel
			 * configuration as an explicit slave ID
			 */
			dma_priv->chan_priv_tx = (void *)p->dma_slave_tx;
			dma_priv->chan_priv_rx = (void *)p->dma_slave_rx;
			dma_priv->slave_id_tx = p->dma_slave_tx;
			dma_priv->slave_id_rx = p->dma_slave_rx;
232
		}
233
	}
234

235 236 237 238 239
	dma_priv->alignment_shift = 1; /* 2-byte alignment */
	dma_priv->filter = shdma_chan_filter;

	mmc_data->dma = dma_priv;

240 241 242 243 244 245
	/*
	 * All SDHI blocks support 2-byte and larger block sizes in 4-bit
	 * bus width mode.
	 */
	mmc_data->flags |= TMIO_MMC_BLKSZ_2BYTES;

246 247 248 249 250
	/*
	 * All SDHI blocks support SDIO IRQ signalling.
	 */
	mmc_data->flags |= TMIO_MMC_SDIO_IRQ;

251 252 253 254 255
	/*
	 * All SDHI have CMD12 controll bit
	 */
	mmc_data->flags |= TMIO_MMC_HAVE_CMD12_CTRL;

256 257 258 259 260
	/*
	 * All SDHI need SDIO_INFO1 reserved bit
	 */
	mmc_data->flags |= TMIO_MMC_SDIO_STATUS_QUIRK;

261 262 263 264 265
	/*
	 * All SDHI have DMA control register
	 */
	mmc_data->flags |= TMIO_MMC_HAVE_CTL_DMA_REG;

266 267 268
	if (of_id && of_id->data) {
		const struct sh_mobile_sdhi_of_data *of_data = of_id->data;
		mmc_data->flags |= of_data->tmio_flags;
269
		mmc_data->capabilities |= of_data->capabilities;
270
		mmc_data->capabilities2 |= of_data->capabilities2;
271
		dma_priv->dma_rx_offset = of_data->dma_rx_offset;
272 273
	}

274 275 276
	/* SD control register space size is 0x100, 0x200 for bus_shift=1 */
	mmc_data->bus_shift = resource_size(res) >> 9;

277 278 279
	ret = tmio_mmc_host_probe(&host, pdev, mmc_data);
	if (ret < 0)
		goto eprobe;
280

281 282 283 284 285 286 287 288
	/*
	 * FIXME:
	 * this Workaround can be more clever method
	 */
	ver = sd_ctrl_read16(host, CTL_VERSION);
	if (ver == 0xCB0D)
		sd_ctrl_write16(host, EXT_ACC, 1);

289 290 291 292 293 294 295 296
	/*
	 * Allow one or more specific (named) ISRs or
	 * one or more multiplexed (un-named) ISRs.
	 */

	irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_CARD_DETECT);
	if (irq >= 0) {
		multiplexed_isr = false;
297
		ret = devm_request_irq(&pdev->dev, irq, tmio_mmc_card_detect_irq, 0,
298 299
				  dev_name(&pdev->dev), host);
		if (ret)
300
			goto eirq;
301 302 303 304 305
	}

	irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_SDIO);
	if (irq >= 0) {
		multiplexed_isr = false;
306
		ret = devm_request_irq(&pdev->dev, irq, tmio_mmc_sdio_irq, 0,
307 308
				  dev_name(&pdev->dev), host);
		if (ret)
309
			goto eirq;
310 311 312 313 314
	}

	irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_SDCARD);
	if (irq >= 0) {
		multiplexed_isr = false;
315
		ret = devm_request_irq(&pdev->dev, irq, tmio_mmc_sdcard_irq, 0,
316
				  dev_name(&pdev->dev), host);
317
		if (ret)
318
			goto eirq;
319 320 321 322
	} else if (!multiplexed_isr) {
		dev_err(&pdev->dev,
			"Principal SD-card IRQ is missing among named interrupts\n");
		ret = irq;
323
		goto eirq;
324 325 326 327 328 329 330 331
	}

	if (multiplexed_isr) {
		while (1) {
			irq = platform_get_irq(pdev, i);
			if (irq < 0)
				break;
			i++;
332
			ret = devm_request_irq(&pdev->dev, irq, tmio_mmc_irq, 0,
333 334
					  dev_name(&pdev->dev), host);
			if (ret)
335
				goto eirq;
336
		}
337 338

		/* There must be at least one IRQ source */
339 340
		if (!i) {
			ret = irq;
341
			goto eirq;
342
		}
343
	}
344

345 346
	dev_info(&pdev->dev, "%s base at 0x%08lx clock rate %u MHz\n",
		 mmc_hostname(host->mmc), (unsigned long)
347
		 (platform_get_resource(pdev, IORESOURCE_MEM, 0)->start),
348
		 host->mmc->f_max / 1000000);
349

350
	return ret;
351

352
eirq:
353
	tmio_mmc_host_remove(host);
354 355
eprobe:
eclkget:
356
	if (p && p->cleanup)
357
		p->cleanup(pdev);
358 359 360 361 362
	return ret;
}

static int sh_mobile_sdhi_remove(struct platform_device *pdev)
{
363 364
	struct mmc_host *mmc = platform_get_drvdata(pdev);
	struct tmio_mmc_host *host = mmc_priv(mmc);
365
	struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
366

367 368
	tmio_mmc_host_remove(host);

369
	if (p && p->cleanup)
370 371
		p->cleanup(pdev);

372 373 374
	return 0;
}

375
static const struct dev_pm_ops tmio_mmc_dev_pm_ops = {
376 377
	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
			pm_runtime_force_resume)
378
	SET_PM_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend,
379 380
			tmio_mmc_host_runtime_resume,
			NULL)
381 382
};

383 384 385
static struct platform_driver sh_mobile_sdhi_driver = {
	.driver		= {
		.name	= "sh_mobile_sdhi",
386
		.pm	= &tmio_mmc_dev_pm_ops,
387
		.of_match_table = sh_mobile_sdhi_of_match,
388 389
	},
	.probe		= sh_mobile_sdhi_probe,
B
Bill Pemberton 已提交
390
	.remove		= sh_mobile_sdhi_remove,
391 392
};

393
module_platform_driver(sh_mobile_sdhi_driver);
394 395 396 397

MODULE_DESCRIPTION("SuperH Mobile SDHI driver");
MODULE_AUTHOR("Magnus Damm");
MODULE_LICENSE("GPL v2");
398
MODULE_ALIAS("platform:sh_mobile_sdhi");