sdhci-esdhc-imx.c 12.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * Freescale eSDHC i.MX controller driver for the platform bus.
 *
 * derived from the OF-version.
 *
 * Copyright (c) 2010 Pengutronix e.K.
 *   Author: Wolfram Sang <w.sang@pengutronix.de>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License.
 */

#include <linux/io.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/clk.h>
18
#include <linux/gpio.h>
19
#include <linux/slab.h>
20
#include <linux/mmc/host.h>
21 22
#include <linux/mmc/mmc.h>
#include <linux/mmc/sdio.h>
23
#include <mach/esdhc.h>
24 25 26
#include "sdhci-pltfm.h"
#include "sdhci-esdhc.h"

27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
/* VENDOR SPEC register */
#define SDHCI_VENDOR_SPEC		0xC0
#define  SDHCI_VENDOR_SPEC_SDIO_QUIRK	0x00000002

/*
 * The CMDTYPE of the CMD register (offset 0xE) should be set to
 * "11" when the STOP CMD12 is issued on imx53 to abort one
 * open ended multi-blk IO. Otherwise the TC INT wouldn't
 * be generated.
 * In exact block transfer, the controller doesn't complete the
 * operations automatically as required at the end of the
 * transfer and remains on hold if the abort command is not sent.
 * As a result, the TC flag is not asserted and SW  received timeout
 * exeception. Bit1 of Vendor Spec registor is used to fix it.
 */
#define ESDHC_FLAG_MULTIBLK_NO_INT	(1 << 1)
43

44 45 46 47 48 49 50
enum imx_esdhc_type {
	IMX25_ESDHC,
	IMX35_ESDHC,
	IMX51_ESDHC,
	IMX53_ESDHC,
};

51 52 53
struct pltfm_imx_data {
	int flags;
	u32 scratchpad;
54
	enum imx_esdhc_type devtype;
55
	struct esdhc_platform_data boarddata;
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 88 89 90 91 92 93 94 95 96
static struct platform_device_id imx_esdhc_devtype[] = {
	{
		.name = "sdhci-esdhc-imx25",
		.driver_data = IMX25_ESDHC,
	}, {
		.name = "sdhci-esdhc-imx35",
		.driver_data = IMX35_ESDHC,
	}, {
		.name = "sdhci-esdhc-imx51",
		.driver_data = IMX51_ESDHC,
	}, {
		.name = "sdhci-esdhc-imx53",
		.driver_data = IMX53_ESDHC,
	}, {
		/* sentinel */
	}
};
MODULE_DEVICE_TABLE(platform, imx_esdhc_devtype);

static inline int is_imx25_esdhc(struct pltfm_imx_data *data)
{
	return data->devtype == IMX25_ESDHC;
}

static inline int is_imx35_esdhc(struct pltfm_imx_data *data)
{
	return data->devtype == IMX35_ESDHC;
}

static inline int is_imx51_esdhc(struct pltfm_imx_data *data)
{
	return data->devtype == IMX51_ESDHC;
}

static inline int is_imx53_esdhc(struct pltfm_imx_data *data)
{
	return data->devtype == IMX53_ESDHC;
}

97 98 99 100 101 102 103 104
static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg)
{
	void __iomem *base = host->ioaddr + (reg & ~0x3);
	u32 shift = (reg & 0x3) * 8;

	writel(((readl(base) & ~(mask << shift)) | (val << shift)), base);
}

105 106
static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
{
107 108 109
	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
	struct pltfm_imx_data *imx_data = pltfm_host->priv;
	struct esdhc_platform_data *boarddata = &imx_data->boarddata;
110

111
	/* fake CARD_PRESENT flag */
112 113
	u32 val = readl(host->ioaddr + reg);

114
	if (unlikely((reg == SDHCI_PRESENT_STATE)
115 116
			&& gpio_is_valid(boarddata->cd_gpio))) {
		if (gpio_get_value(boarddata->cd_gpio))
117
			/* no card, if a valid gpio says so... */
118
			val &= ~SDHCI_CARD_PRESENT;
119 120 121 122 123 124 125 126 127 128
		else
			/* ... in all other cases assume card is present */
			val |= SDHCI_CARD_PRESENT;
	}

	return val;
}

static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
{
129 130
	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
	struct pltfm_imx_data *imx_data = pltfm_host->priv;
131
	struct esdhc_platform_data *boarddata = &imx_data->boarddata;
132 133

	if (unlikely((reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)
134
			&& (boarddata->cd_type == ESDHC_CD_GPIO)))
135 136 137 138 139
		/*
		 * these interrupts won't work with a custom card_detect gpio
		 */
		val &= ~(SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT);

140 141 142 143 144 145 146 147 148
	if (unlikely((imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
				&& (reg == SDHCI_INT_STATUS)
				&& (val & SDHCI_INT_DATA_END))) {
			u32 v;
			v = readl(host->ioaddr + SDHCI_VENDOR_SPEC);
			v &= ~SDHCI_VENDOR_SPEC_SDIO_QUIRK;
			writel(v, host->ioaddr + SDHCI_VENDOR_SPEC);
	}

149 150 151
	writel(val, host->ioaddr + reg);
}

152 153 154 155 156 157 158 159 160 161 162
static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
{
	if (unlikely(reg == SDHCI_HOST_VERSION))
		reg ^= 2;

	return readw(host->ioaddr + reg);
}

static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
{
	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
163
	struct pltfm_imx_data *imx_data = pltfm_host->priv;
164 165 166 167 168 169 170

	switch (reg) {
	case SDHCI_TRANSFER_MODE:
		/*
		 * Postpone this write, we must do it together with a
		 * command write that is down below.
		 */
171 172 173 174 175 176 177 178 179
		if ((imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
				&& (host->cmd->opcode == SD_IO_RW_EXTENDED)
				&& (host->cmd->data->blocks > 1)
				&& (host->cmd->data->flags & MMC_DATA_READ)) {
			u32 v;
			v = readl(host->ioaddr + SDHCI_VENDOR_SPEC);
			v |= SDHCI_VENDOR_SPEC_SDIO_QUIRK;
			writel(v, host->ioaddr + SDHCI_VENDOR_SPEC);
		}
180
		imx_data->scratchpad = val;
181 182
		return;
	case SDHCI_COMMAND:
183 184 185
		if ((host->cmd->opcode == MMC_STOP_TRANSMISSION)
			&& (imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT))
			val |= SDHCI_CMD_ABORTCMD;
186
		writel(val << 16 | imx_data->scratchpad,
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
			host->ioaddr + SDHCI_TRANSFER_MODE);
		return;
	case SDHCI_BLOCK_SIZE:
		val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);
		break;
	}
	esdhc_clrset_le(host, 0xffff, val, reg);
}

static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
{
	u32 new_val;

	switch (reg) {
	case SDHCI_POWER_CONTROL:
		/*
		 * FSL put some DMA bits here
		 * If your board has a regulator, code should be here
		 */
		return;
	case SDHCI_HOST_CONTROL:
		/* FSL messed up here, so we can just keep those two */
		new_val = val & (SDHCI_CTRL_LED | SDHCI_CTRL_4BITBUS);
		/* ensure the endianess */
		new_val |= ESDHC_HOST_CONTROL_LE;
		/* DMA mode bits are shifted */
		new_val |= (val & SDHCI_CTRL_DMA_MASK) << 5;

		esdhc_clrset_le(host, 0xffff, new_val, reg);
		return;
	}
	esdhc_clrset_le(host, 0xff, val, reg);
219 220 221 222 223 224 225 226 227 228 229

	/*
	 * The esdhc has a design violation to SDHC spec which tells
	 * that software reset should not affect card detection circuit.
	 * But esdhc clears its SYSCTL register bits [0..2] during the
	 * software reset.  This will stop those clocks that card detection
	 * circuit relies on.  To work around it, we turn the clocks on back
	 * to keep card detection circuit functional.
	 */
	if ((reg == SDHCI_SOFTWARE_RESET) && (val & 1))
		esdhc_clrset_le(host, 0x7, 0x7, ESDHC_SYSTEM_CONTROL);
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
}

static unsigned int esdhc_pltfm_get_max_clock(struct sdhci_host *host)
{
	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);

	return clk_get_rate(pltfm_host->clk);
}

static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host)
{
	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);

	return clk_get_rate(pltfm_host->clk) / 256 / 16;
}

246 247
static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)
{
248 249 250
	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
	struct pltfm_imx_data *imx_data = pltfm_host->priv;
	struct esdhc_platform_data *boarddata = &imx_data->boarddata;
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265

	switch (boarddata->wp_type) {
	case ESDHC_WP_GPIO:
		if (gpio_is_valid(boarddata->wp_gpio))
			return gpio_get_value(boarddata->wp_gpio);
	case ESDHC_WP_CONTROLLER:
		return !(readl(host->ioaddr + SDHCI_PRESENT_STATE) &
			       SDHCI_WRITE_PROTECT);
	case ESDHC_WP_NONE:
		break;
	}

	return -ENOSYS;
}

266
static struct sdhci_ops sdhci_esdhc_ops = {
267
	.read_l = esdhc_readl_le,
268
	.read_w = esdhc_readw_le,
269
	.write_l = esdhc_writel_le,
270 271 272 273 274
	.write_w = esdhc_writew_le,
	.write_b = esdhc_writeb_le,
	.set_clock = esdhc_set_clock,
	.get_max_clock = esdhc_pltfm_get_max_clock,
	.get_min_clock = esdhc_pltfm_get_min_clock,
275
	.get_ro = esdhc_pltfm_get_ro,
276 277
};

278 279 280 281 282 283 284
static struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
	.quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_ADMA
			| SDHCI_QUIRK_BROKEN_CARD_DETECTION,
	/* ADMA has issues. Might be fixable */
	.ops = &sdhci_esdhc_ops,
};

285 286 287 288 289 290 291 292
static irqreturn_t cd_irq(int irq, void *data)
{
	struct sdhci_host *sdhost = (struct sdhci_host *)data;

	tasklet_schedule(&sdhost->card_tasklet);
	return IRQ_HANDLED;
};

293
static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev)
294
{
295 296 297
	struct sdhci_pltfm_host *pltfm_host;
	struct sdhci_host *host;
	struct esdhc_platform_data *boarddata;
298
	struct clk *clk;
299
	int err;
300
	struct pltfm_imx_data *imx_data;
301

302 303 304 305 306 307 308 309 310
	host = sdhci_pltfm_init(pdev, &sdhci_esdhc_imx_pdata);
	if (IS_ERR(host))
		return PTR_ERR(host);

	pltfm_host = sdhci_priv(host);

	imx_data = kzalloc(sizeof(struct pltfm_imx_data), GFP_KERNEL);
	if (!imx_data)
		return -ENOMEM;
311 312

	imx_data->devtype = pdev->id_entry->driver_data;
313 314
	pltfm_host->priv = imx_data;

315 316 317
	clk = clk_get(mmc_dev(host->mmc), NULL);
	if (IS_ERR(clk)) {
		dev_err(mmc_dev(host->mmc), "clk err\n");
318 319
		err = PTR_ERR(clk);
		goto err_clk_get;
320 321 322 323
	}
	clk_enable(clk);
	pltfm_host->clk = clk;

324
	if (!is_imx25_esdhc(imx_data))
325 326
		host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;

327
	if (is_imx25_esdhc(imx_data) || is_imx35_esdhc(imx_data))
328
		/* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */
329
		host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK;
330

331
	if (is_imx53_esdhc(imx_data))
332 333
		imx_data->flags |= ESDHC_FLAG_MULTIBLK_NO_INT;

334
	if (!host->mmc->parent->platform_data) {
335 336 337 338
		dev_err(mmc_dev(host->mmc), "no board data!\n");
		err = -EINVAL;
		goto no_board_data;
	}
339 340 341
	imx_data->boarddata = *((struct esdhc_platform_data *)
				host->mmc->parent->platform_data);
	boarddata = &imx_data->boarddata;
342 343 344

	/* write_protect */
	if (boarddata->wp_type == ESDHC_WP_GPIO) {
345 346 347
		err = gpio_request_one(boarddata->wp_gpio, GPIOF_IN, "ESDHC_WP");
		if (err) {
			dev_warn(mmc_dev(host->mmc),
348 349
				 "no write-protect pin available!\n");
			boarddata->wp_gpio = -EINVAL;
350
		}
351 352 353 354 355 356 357
	} else {
		boarddata->wp_gpio = -EINVAL;
	}

	/* card_detect */
	if (boarddata->cd_type != ESDHC_CD_GPIO)
		boarddata->cd_gpio = -EINVAL;
358

359 360
	switch (boarddata->cd_type) {
	case ESDHC_CD_GPIO:
361 362
		err = gpio_request_one(boarddata->cd_gpio, GPIOF_IN, "ESDHC_CD");
		if (err) {
363
			dev_err(mmc_dev(host->mmc),
364 365 366 367 368 369 370 371
				"no card-detect pin available!\n");
			goto no_card_detect_pin;
		}

		err = request_irq(gpio_to_irq(boarddata->cd_gpio), cd_irq,
				 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
				 mmc_hostname(host->mmc), host);
		if (err) {
372
			dev_err(mmc_dev(host->mmc), "request irq error\n");
373 374
			goto no_card_detect_irq;
		}
375
		/* fall through */
376

377 378
	case ESDHC_CD_CONTROLLER:
		/* we have a working card_detect back */
379
		host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
380 381 382 383 384 385 386 387
		break;

	case ESDHC_CD_PERMANENT:
		host->mmc->caps = MMC_CAP_NONREMOVABLE;
		break;

	case ESDHC_CD_NONE:
		break;
388
	}
389

390 391 392 393
	err = sdhci_add_host(host);
	if (err)
		goto err_add_host;

394
	return 0;
395

396 397 398 399 400 401 402 403 404 405
err_add_host:
	if (gpio_is_valid(boarddata->cd_gpio))
		free_irq(gpio_to_irq(boarddata->cd_gpio), host);
no_card_detect_irq:
	if (gpio_is_valid(boarddata->cd_gpio))
		gpio_free(boarddata->cd_gpio);
	if (gpio_is_valid(boarddata->wp_gpio))
		gpio_free(boarddata->wp_gpio);
no_card_detect_pin:
no_board_data:
406 407
	clk_disable(pltfm_host->clk);
	clk_put(pltfm_host->clk);
408 409
err_clk_get:
	kfree(imx_data);
410 411
	sdhci_pltfm_free(pdev);
	return err;
412 413
}

414
static int __devexit sdhci_esdhc_imx_remove(struct platform_device *pdev)
415
{
416
	struct sdhci_host *host = platform_get_drvdata(pdev);
417
	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
418
	struct pltfm_imx_data *imx_data = pltfm_host->priv;
419
	struct esdhc_platform_data *boarddata = &imx_data->boarddata;
420 421 422
	int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);

	sdhci_remove_host(host, dead);
423

424
	if (gpio_is_valid(boarddata->wp_gpio))
425
		gpio_free(boarddata->wp_gpio);
426

427 428
	if (gpio_is_valid(boarddata->cd_gpio)) {
		free_irq(gpio_to_irq(boarddata->cd_gpio), host);
429 430 431
		gpio_free(boarddata->cd_gpio);
	}

432 433
	clk_disable(pltfm_host->clk);
	clk_put(pltfm_host->clk);
434
	kfree(imx_data);
435 436 437 438

	sdhci_pltfm_free(pdev);

	return 0;
439 440
}

441 442 443 444 445
static struct platform_driver sdhci_esdhc_imx_driver = {
	.driver		= {
		.name	= "sdhci-esdhc-imx",
		.owner	= THIS_MODULE,
	},
446
	.id_table	= imx_esdhc_devtype,
447 448 449 450 451 452
	.probe		= sdhci_esdhc_imx_probe,
	.remove		= __devexit_p(sdhci_esdhc_imx_remove),
#ifdef CONFIG_PM
	.suspend	= sdhci_pltfm_suspend,
	.resume		= sdhci_pltfm_resume,
#endif
453
};
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469

static int __init sdhci_esdhc_imx_init(void)
{
	return platform_driver_register(&sdhci_esdhc_imx_driver);
}
module_init(sdhci_esdhc_imx_init);

static void __exit sdhci_esdhc_imx_exit(void)
{
	platform_driver_unregister(&sdhci_esdhc_imx_driver);
}
module_exit(sdhci_esdhc_imx_exit);

MODULE_DESCRIPTION("SDHCI driver for Freescale i.MX eSDHC");
MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>");
MODULE_LICENSE("GPL v2");