msm_sdcc.c revision 865c8064a2fb07100525097983966b8e789bde1a
19d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat/*
29d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat *  linux/drivers/mmc/host/msm_sdcc.c - Qualcomm MSM 7X00A SDCC Driver
39d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat *
49d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat *  Copyright (C) 2007 Google Inc,
59d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat *  Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved.
69d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat *
79d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat * This program is free software; you can redistribute it and/or modify
89d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat * it under the terms of the GNU General Public License version 2 as
99d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat * published by the Free Software Foundation.
109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat *
119d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat * Based on mmci.c
129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat *
139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat * Author: San Mehat (san@android.com)
149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat *
159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat */
169d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
179d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/module.h>
189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/moduleparam.h>
199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/init.h>
209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/ioport.h>
219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/device.h>
229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/interrupt.h>
239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/delay.h>
249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/err.h>
259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/highmem.h>
269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/log2.h>
279d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/mmc/host.h>
289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/mmc/card.h>
29b3fa579118b239e218e690f5ef76870aff6fe738San Mehat#include <linux/mmc/sdio.h>
309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/clk.h>
319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/scatterlist.h>
329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/platform_device.h>
339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/dma-mapping.h>
349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/debugfs.h>
359d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/io.h>
369d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/memory.h>
379d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
389d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <asm/cacheflush.h>
399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <asm/div64.h>
409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <asm/sizes.h>
419d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
423989d17847071fa94c93299805a9cca27cf65d26Pavel Machek#include <mach/mmc.h>
439d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <mach/msm_iomap.h>
449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <mach/dma.h>
459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include "msm_sdcc.h"
479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#define DRIVER_NAME "msm-sdcc"
499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic unsigned int msmsdcc_fmin = 144000;
519d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic unsigned int msmsdcc_fmax = 50000000;
529d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic unsigned int msmsdcc_4bit = 1;
539d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic unsigned int msmsdcc_pwrsave = 1;
549d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic unsigned int msmsdcc_piopoll = 1;
559d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic unsigned int msmsdcc_sdioirq;
569d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#define PIO_SPINMAX 30
589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#define CMD_SPINMAX 20
599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
60865c8064a2fb07100525097983966b8e789bde1aSan Mehat
61865c8064a2fb07100525097983966b8e789bde1aSan Mehatstatic inline int
62865c8064a2fb07100525097983966b8e789bde1aSan Mehatmsmsdcc_enable_clocks(struct msmsdcc_host *host, int enable)
63865c8064a2fb07100525097983966b8e789bde1aSan Mehat{
64865c8064a2fb07100525097983966b8e789bde1aSan Mehat	int rc;
65865c8064a2fb07100525097983966b8e789bde1aSan Mehat	WARN_ON(enable == host->clks_on);
66865c8064a2fb07100525097983966b8e789bde1aSan Mehat	if (enable) {
67865c8064a2fb07100525097983966b8e789bde1aSan Mehat		rc = clk_enable(host->pclk);
68865c8064a2fb07100525097983966b8e789bde1aSan Mehat		if (rc)
69865c8064a2fb07100525097983966b8e789bde1aSan Mehat			return rc;
70865c8064a2fb07100525097983966b8e789bde1aSan Mehat		rc = clk_enable(host->clk);
71865c8064a2fb07100525097983966b8e789bde1aSan Mehat		if (rc) {
72865c8064a2fb07100525097983966b8e789bde1aSan Mehat			clk_disable(host->pclk);
73865c8064a2fb07100525097983966b8e789bde1aSan Mehat			return rc;
74865c8064a2fb07100525097983966b8e789bde1aSan Mehat		}
75865c8064a2fb07100525097983966b8e789bde1aSan Mehat		udelay(30);
76865c8064a2fb07100525097983966b8e789bde1aSan Mehat		host->clks_on = 1;
77865c8064a2fb07100525097983966b8e789bde1aSan Mehat	} else {
78865c8064a2fb07100525097983966b8e789bde1aSan Mehat		clk_disable(host->clk);
79865c8064a2fb07100525097983966b8e789bde1aSan Mehat		clk_disable(host->pclk);
80865c8064a2fb07100525097983966b8e789bde1aSan Mehat		host->clks_on = 0;
81865c8064a2fb07100525097983966b8e789bde1aSan Mehat	}
82865c8064a2fb07100525097983966b8e789bde1aSan Mehat	return 0;
83865c8064a2fb07100525097983966b8e789bde1aSan Mehat}
84865c8064a2fb07100525097983966b8e789bde1aSan Mehat
85865c8064a2fb07100525097983966b8e789bde1aSan Mehat
869d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
879d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_start_command(struct msmsdcc_host *host, struct mmc_command *cmd,
889d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		      u32 c);
899d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
909d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
919d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_request_end(struct msmsdcc_host *host, struct mmc_request *mrq)
929d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
939d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	writel(0, host->base + MMCICOMMAND);
949d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
959d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	BUG_ON(host->curr.data);
969d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
979d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.mrq = NULL;
989d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.cmd = NULL;
999d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (mrq->data)
1019d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		mrq->data->bytes_xfered = host->curr.data_xfered;
1029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (mrq->cmd->error == -ETIMEDOUT)
1039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		mdelay(5);
1049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
105865c8064a2fb07100525097983966b8e789bde1aSan Mehat	if (host->use_bustimer)
106865c8064a2fb07100525097983966b8e789bde1aSan Mehat		mod_timer(&host->busclk_timer, jiffies + HZ);
1079d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	/*
1089d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 * Need to drop the host lock here; mmc_request_done may call
1099d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 * back into the driver...
1109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 */
1119d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_unlock(&host->lock);
1129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc_request_done(host->mmc, mrq);
1139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_lock(&host->lock);
1149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
1159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1169d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
1179d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_stop_data(struct msmsdcc_host *host)
1189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
1199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	writel(0, host->base + MMCIDATACTRL);
1209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.data = NULL;
1219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.got_dataend = host->curr.got_datablkend = 0;
1229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
1239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatuint32_t msmsdcc_fifo_addr(struct msmsdcc_host *host)
1259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
12675d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	switch (host->pdev_id) {
12775d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	case 1:
1289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return MSM_SDC1_PHYS + MMCIFIFO;
12975d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	case 2:
1309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return MSM_SDC2_PHYS + MMCIFIFO;
13175d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	case 3:
1329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return MSM_SDC3_PHYS + MMCIFIFO;
13375d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	case 4:
1349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return MSM_SDC4_PHYS + MMCIFIFO;
13575d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	}
13675d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	BUG();
1379d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return 0;
1389d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
1399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
1419d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_dma_complete_func(struct msm_dmov_cmd *cmd,
1429d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			  unsigned int result,
1439d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			  struct msm_dmov_errdata *err)
1449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
1459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_dma_data	*dma_data =
1469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		container_of(cmd, struct msmsdcc_dma_data, hdr);
1479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host	*host = dma_data->host;
1489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	unsigned long		flags;
1499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct mmc_request	*mrq;
1509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1519d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_lock_irqsave(&host->lock, flags);
1529d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mrq = host->curr.mrq;
1539d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	BUG_ON(!mrq);
1549d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1559d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!(result & DMOV_RSLT_VALID)) {
1560a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("msmsdcc: Invalid DataMover result\n");
1579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		goto out;
1589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
1599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1609d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (result & DMOV_RSLT_DONE) {
1619d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->curr.data_xfered = host->curr.xfer_size;
1629d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else {
1639d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		/* Error or flush  */
1649d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (result & DMOV_RSLT_ERROR)
1650a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches			pr_err("%s: DMA error (0x%.8x)\n",
1669d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			       mmc_hostname(host->mmc), result);
1679d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (result & DMOV_RSLT_FLUSH)
1680a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches			pr_err("%s: DMA channel flushed (0x%.8x)\n",
1699d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			       mmc_hostname(host->mmc), result);
1709d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (err)
1710a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches			pr_err("Flush data: %.8x %.8x %.8x %.8x %.8x %.8x\n",
1729d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			       err->flush[0], err->flush[1], err->flush[2],
1739d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			       err->flush[3], err->flush[4], err->flush[5]);
1749d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (!mrq->data->error)
1759d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			mrq->data->error = -EIO;
1769d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
1779d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.busy = 0;
1789d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	dma_unmap_sg(mmc_dev(host->mmc), host->dma.sg, host->dma.num_ents,
1799d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		     host->dma.dir);
1809d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1819d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->curr.user_pages) {
1829d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		struct scatterlist *sg = host->dma.sg;
1839d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		int i;
1849d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
18575d145283b2e42619d7ee1e00b78466bacd51808Joe Perches		for (i = 0; i < host->dma.num_ents; i++)
18675d145283b2e42619d7ee1e00b78466bacd51808Joe Perches			flush_dcache_page(sg_page(sg++));
1879d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
1889d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1899d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.sg = NULL;
1909d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1919d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if ((host->curr.got_dataend && host->curr.got_datablkend)
1929d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	     || mrq->data->error) {
1939d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1949d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		/*
1959d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		 * If we've already gotten our DATAEND / DATABLKEND
1969d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		 * for this request, then complete it through here.
1979d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		 */
1989d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		msmsdcc_stop_data(host);
1999d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (!mrq->data->error)
2019d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			host->curr.data_xfered = host->curr.xfer_size;
2029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (!mrq->data->stop || mrq->cmd->error) {
2039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			writel(0, host->base + MMCICOMMAND);
2049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			host->curr.mrq = NULL;
2059d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			host->curr.cmd = NULL;
2069d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			mrq->data->bytes_xfered = host->curr.data_xfered;
2079d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2089d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			spin_unlock_irqrestore(&host->lock, flags);
2099d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			mmc_request_done(host->mmc, mrq);
2109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			return;
2119d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		} else
2129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			msmsdcc_start_command(host, mrq->data->stop, 0);
2139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
2149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatout:
2169d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_unlock_irqrestore(&host->lock, flags);
2179d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return;
2189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
2199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int validate_dma(struct msmsdcc_host *host, struct mmc_data *data)
2219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
2229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->dma.channel == -1)
2239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return -ENOENT;
2249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if ((data->blksz * data->blocks) < MCI_FIFOSIZE)
2269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return -EINVAL;
2279d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if ((data->blksz * data->blocks) % MCI_FIFOSIZE)
2289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return -EINVAL;
2299d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return 0;
2309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
2319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int msmsdcc_config_dma(struct msmsdcc_host *host, struct mmc_data *data)
2339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
2349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_nc_dmadata *nc;
2359d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	dmov_box *box;
2369d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	uint32_t rows;
2379d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	uint32_t crci;
2389d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	unsigned int n;
2399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	int i, rc;
2409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct scatterlist *sg = data->sg;
2419d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2429d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	rc = validate_dma(host, data);
2439d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (rc)
2449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return rc;
2459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.sg = data->sg;
2479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.num_ents = data->sg_len;
2489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	nc = host->dma.nc;
2509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
25175d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	switch (host->pdev_id) {
25275d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	case 1:
2539d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		crci = MSMSDCC_CRCI_SDC1;
25475d145283b2e42619d7ee1e00b78466bacd51808Joe Perches		break;
25575d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	case 2:
2569d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		crci = MSMSDCC_CRCI_SDC2;
25775d145283b2e42619d7ee1e00b78466bacd51808Joe Perches		break;
25875d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	case 3:
2599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		crci = MSMSDCC_CRCI_SDC3;
26075d145283b2e42619d7ee1e00b78466bacd51808Joe Perches		break;
26175d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	case 4:
2629d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		crci = MSMSDCC_CRCI_SDC4;
26375d145283b2e42619d7ee1e00b78466bacd51808Joe Perches		break;
26475d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	default:
2659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->dma.sg = NULL;
2669d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->dma.num_ents = 0;
2679d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return -ENOENT;
2689d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
2699d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2709d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (data->flags & MMC_DATA_READ)
2719d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->dma.dir = DMA_FROM_DEVICE;
2729d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	else
2739d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->dma.dir = DMA_TO_DEVICE;
2749d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2759d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.user_pages = 0;
2769d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2779d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	n = dma_map_sg(mmc_dev(host->mmc), host->dma.sg,
27875d145283b2e42619d7ee1e00b78466bacd51808Joe Perches		       host->dma.num_ents, host->dma.dir);
2799d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2809d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (n != host->dma.num_ents) {
2810a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: Unable to map in all sg elements\n",
2829d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		       mmc_hostname(host->mmc));
2839d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->dma.sg = NULL;
2849d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->dma.num_ents = 0;
2859d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return -ENOMEM;
2869d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
2879d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2889d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	box = &nc->cmd[0];
2899d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	for (i = 0; i < host->dma.num_ents; i++) {
2909d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		box->cmd = CMD_MODE_BOX;
2919d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2929d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (i == (host->dma.num_ents - 1))
2939d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->cmd |= CMD_LC;
2949d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		rows = (sg_dma_len(sg) % MCI_FIFOSIZE) ?
2959d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			(sg_dma_len(sg) / MCI_FIFOSIZE) + 1 :
2969d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			(sg_dma_len(sg) / MCI_FIFOSIZE) ;
2979d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2989d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (data->flags & MMC_DATA_READ) {
2999d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->src_row_addr = msmsdcc_fifo_addr(host);
3009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->dst_row_addr = sg_dma_address(sg);
3019d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->src_dst_len = (MCI_FIFOSIZE << 16) |
3039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat					   (MCI_FIFOSIZE);
3049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->row_offset = MCI_FIFOSIZE;
3059d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3069d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->num_rows = rows * ((1 << 16) + 1);
3079d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->cmd |= CMD_SRC_CRCI(crci);
3089d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		} else {
3099d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->src_row_addr = sg_dma_address(sg);
3109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->dst_row_addr = msmsdcc_fifo_addr(host);
3119d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->src_dst_len = (MCI_FIFOSIZE << 16) |
3139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat					   (MCI_FIFOSIZE);
3149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->row_offset = (MCI_FIFOSIZE << 16);
3159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3169d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->num_rows = rows * ((1 << 16) + 1);
3179d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->cmd |= CMD_DST_CRCI(crci);
3189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		}
3199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		box++;
3209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		sg++;
3219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
3229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	/* location of command block must be 64 bit aligned */
3249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	BUG_ON(host->dma.cmd_busaddr & 0x07);
3259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	nc->cmdptr = (host->dma.cmd_busaddr >> 3) | CMD_PTR_LP;
3279d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.hdr.cmdptr = DMOV_CMD_PTR_LIST |
3289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			       DMOV_CMD_ADDR(host->dma.cmdptr_busaddr);
3299d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.hdr.complete_func = msmsdcc_dma_complete_func;
3305b00f40f90e7b17c11cf388680f43e8466b3666dSan Mehat	host->dma.hdr.execute_func = NULL;
3319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return 0;
3339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
3349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3359d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
3369d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_start_data(struct msmsdcc_host *host, struct mmc_data *data)
3379d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
3389d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	unsigned int datactrl, timeout;
3399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	unsigned long long clks;
3409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	void __iomem *base = host->base;
3419d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	unsigned int pio_irqmask = 0;
3429d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3439d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.data = data;
3449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.xfer_size = data->blksz * data->blocks;
3459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.xfer_remain = host->curr.xfer_size;
3469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.data_xfered = 0;
3479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.got_dataend = 0;
3489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.got_datablkend = 0;
3499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	memset(&host->pio, 0, sizeof(host->pio));
3519d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3529d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	clks = (unsigned long long)data->timeout_ns * host->clk_rate;
35375d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	do_div(clks, NSEC_PER_SEC);
3549d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	timeout = data->timeout_clks + (unsigned int)clks;
3559d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	writel(timeout, base + MMCIDATATIMER);
3569d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	writel(host->curr.xfer_size, base + MMCIDATALENGTH);
3589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	datactrl = MCI_DPSM_ENABLE | (data->blksz << 4);
3609d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3619d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!msmsdcc_config_dma(host, data))
3629d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		datactrl |= MCI_DPSM_DMAENABLE;
3639d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	else {
3649d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->pio.sg = data->sg;
3659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->pio.sg_len = data->sg_len;
3669d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->pio.sg_off = 0;
3679d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3689d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (data->flags & MMC_DATA_READ) {
3699d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			pio_irqmask = MCI_RXFIFOHALFFULLMASK;
3709d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			if (host->curr.xfer_remain < MCI_FIFOSIZE)
3719d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				pio_irqmask |= MCI_RXDATAAVLBLMASK;
3729d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		} else
3739d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			pio_irqmask = MCI_TXFIFOHALFEMPTYMASK;
3749d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
3759d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3769d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (data->flags & MMC_DATA_READ)
3779d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		datactrl |= MCI_DPSM_DIRECTION;
3789d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3799d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	writel(pio_irqmask, base + MMCIMASK1);
3809d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	writel(datactrl, base + MMCIDATACTRL);
3819d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3829d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (datactrl & MCI_DPSM_DMAENABLE) {
3839d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->dma.busy = 1;
3849d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		msm_dmov_enqueue_cmd(host->dma.channel, &host->dma.hdr);
3859d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
3869d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
3879d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
388b3fa579118b239e218e690f5ef76870aff6fe738San Mehatstatic int
389b3fa579118b239e218e690f5ef76870aff6fe738San Mehatsnoop_cccr_abort(struct mmc_command *cmd)
390b3fa579118b239e218e690f5ef76870aff6fe738San Mehat{
391b3fa579118b239e218e690f5ef76870aff6fe738San Mehat	if ((cmd->opcode == 52) &&
392b3fa579118b239e218e690f5ef76870aff6fe738San Mehat	    (cmd->arg & 0x80000000) &&
393b3fa579118b239e218e690f5ef76870aff6fe738San Mehat	    (((cmd->arg >> 9) & 0x1ffff) == SDIO_CCCR_ABORT))
394b3fa579118b239e218e690f5ef76870aff6fe738San Mehat		return 1;
395b3fa579118b239e218e690f5ef76870aff6fe738San Mehat	return 0;
396b3fa579118b239e218e690f5ef76870aff6fe738San Mehat}
397b3fa579118b239e218e690f5ef76870aff6fe738San Mehat
3989d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
3999d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_start_command(struct msmsdcc_host *host, struct mmc_command *cmd, u32 c)
4009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
4019d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	void __iomem *base = host->base;
4029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (readl(base + MMCICOMMAND) & MCI_CPSM_ENABLE) {
4049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		writel(0, base + MMCICOMMAND);
4059d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		udelay(2 + ((5 * 1000000) / host->clk_rate));
4069d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
4079d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4089d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	c |= cmd->opcode | MCI_CPSM_ENABLE;
4099d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (cmd->flags & MMC_RSP_PRESENT) {
4119d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (cmd->flags & MMC_RSP_136)
4129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			c |= MCI_CPSM_LONGRSP;
4139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		c |= MCI_CPSM_RESPONSE;
4149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
4159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
41675d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	if (cmd->opcode == 17 || cmd->opcode == 18 ||
41775d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	    cmd->opcode == 24 || cmd->opcode == 25 ||
41875d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	    cmd->opcode == 53)
4199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		c |= MCI_CSPM_DATCMD;
4209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (cmd == cmd->mrq->stop)
4229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		c |= MCI_CSPM_MCIABORT;
4239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
424b3fa579118b239e218e690f5ef76870aff6fe738San Mehat	if (snoop_cccr_abort(cmd))
425b3fa579118b239e218e690f5ef76870aff6fe738San Mehat		c |= MCI_CSPM_MCIABORT;
426b3fa579118b239e218e690f5ef76870aff6fe738San Mehat
4279d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.cmd = cmd;
4289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4299d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->stats.cmds++;
4309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	writel(cmd->arg, base + MMCIARGUMENT);
4329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	writel(c, base + MMCICOMMAND);
4339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
4349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4359d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
4369d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_data_err(struct msmsdcc_host *host, struct mmc_data *data,
4379d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		 unsigned int status)
4389d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
4399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (status & MCI_DATACRCFAIL) {
4400a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: Data CRC error\n", mmc_hostname(host->mmc));
4410a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: opcode 0x%.8x\n", __func__,
4429d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		       data->mrq->cmd->opcode);
4430a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: blksz %d, blocks %d\n", __func__,
4449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		       data->blksz, data->blocks);
4459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		data->error = -EILSEQ;
4469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else if (status & MCI_DATATIMEOUT) {
4470a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: Data timeout\n", mmc_hostname(host->mmc));
4489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		data->error = -ETIMEDOUT;
4499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else if (status & MCI_RXOVERRUN) {
4500a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: RX overrun\n", mmc_hostname(host->mmc));
4519d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		data->error = -EIO;
4529d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else if (status & MCI_TXUNDERRUN) {
4530a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: TX underrun\n", mmc_hostname(host->mmc));
4549d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		data->error = -EIO;
4559d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else {
4560a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: Unknown error (0x%.8x)\n",
4570a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		       mmc_hostname(host->mmc), status);
4589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		data->error = -EIO;
4599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
4609d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
4619d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4629d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4639d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int
4649d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_pio_read(struct msmsdcc_host *host, char *buffer, unsigned int remain)
4659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
4669d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	void __iomem	*base = host->base;
4679d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	uint32_t	*ptr = (uint32_t *) buffer;
4689d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	int		count = 0;
4699d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4709d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	while (readl(base + MMCISTATUS) & MCI_RXDATAAVLBL) {
4719d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4729d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		*ptr = readl(base + MMCIFIFO + (count % MCI_FIFOSIZE));
4739d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		ptr++;
4749d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		count += sizeof(uint32_t);
4759d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4769d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		remain -=  sizeof(uint32_t);
4779d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (remain == 0)
4789d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			break;
4799d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
4809d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return count;
4819d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
4829d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4839d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int
4849d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_pio_write(struct msmsdcc_host *host, char *buffer,
4859d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		  unsigned int remain, u32 status)
4869d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
4879d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	void __iomem *base = host->base;
4889d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	char *ptr = buffer;
4899d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4909d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	do {
4919d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		unsigned int count, maxcnt;
4929d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4939d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		maxcnt = status & MCI_TXFIFOEMPTY ? MCI_FIFOSIZE :
4949d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat						    MCI_FIFOHALFSIZE;
4959d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		count = min(remain, maxcnt);
4969d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4979d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		writesl(base + MMCIFIFO, ptr, count >> 2);
4989d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		ptr += count;
4999d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		remain -= count;
5009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5019d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (remain == 0)
5029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			break;
5039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		status = readl(base + MMCISTATUS);
5059d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} while (status & MCI_TXFIFOHALFEMPTY);
5069d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5079d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return ptr - buffer;
5089d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
5099d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int
5119d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_spin_on_status(struct msmsdcc_host *host, uint32_t mask, int maxspin)
5129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
5139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	while (maxspin) {
5149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if ((readl(host->base + MMCISTATUS) & mask))
5159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			return 0;
5169d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		udelay(1);
5179d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		--maxspin;
5189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
5199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return -ETIMEDOUT;
5209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
5219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int
5239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_pio_irq(int irq, void *dev_id)
5249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
5259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host	*host = dev_id;
5269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	void __iomem		*base = host->base;
5279d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	uint32_t		status;
5289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5299d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	status = readl(base + MMCISTATUS);
5309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	do {
5329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		unsigned long flags;
5339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		unsigned int remain, len;
5349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		char *buffer;
5359d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5369d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (!(status & (MCI_TXFIFOHALFEMPTY | MCI_RXDATAAVLBL))) {
5379d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			if (host->curr.xfer_remain == 0 || !msmsdcc_piopoll)
5389d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				break;
5399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			if (msmsdcc_spin_on_status(host,
5419d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat						   (MCI_TXFIFOHALFEMPTY |
5429d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat						   MCI_RXDATAAVLBL),
5439d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat						   PIO_SPINMAX)) {
5449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				break;
5459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			}
5469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		}
5479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		/* Map the current scatter buffer */
5499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		local_irq_save(flags);
5509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		buffer = kmap_atomic(sg_page(host->pio.sg),
5519d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				     KM_BIO_SRC_IRQ) + host->pio.sg->offset;
5529d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		buffer += host->pio.sg_off;
5539d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		remain = host->pio.sg->length - host->pio.sg_off;
5549d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		len = 0;
5559d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (status & MCI_RXACTIVE)
5569d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			len = msmsdcc_pio_read(host, buffer, remain);
5579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (status & MCI_TXACTIVE)
5589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			len = msmsdcc_pio_write(host, buffer, remain, status);
5599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5609d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		/* Unmap the buffer */
5619d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
5629d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		local_irq_restore(flags);
5639d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5649d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->pio.sg_off += len;
5659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->curr.xfer_remain -= len;
5669d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->curr.data_xfered += len;
5679d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		remain -= len;
5689d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5699d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (remain == 0) {
5709d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			/* This sg page is full - do some housekeeping */
5719d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			if (status & MCI_RXACTIVE && host->curr.user_pages)
5729d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				flush_dcache_page(sg_page(host->pio.sg));
5739d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5749d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			if (!--host->pio.sg_len) {
5759d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				memset(&host->pio, 0, sizeof(host->pio));
5769d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				break;
5779d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			}
5789d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5799d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			/* Advance to next sg */
5809d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			host->pio.sg++;
5819d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			host->pio.sg_off = 0;
5829d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		}
5839d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5849d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		status = readl(base + MMCISTATUS);
5859d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} while (1);
5869d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5879d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (status & MCI_RXACTIVE && host->curr.xfer_remain < MCI_FIFOSIZE)
5889d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		writel(MCI_RXDATAAVLBLMASK, base + MMCIMASK1);
5899d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5909d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!host->curr.xfer_remain)
5919d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		writel(0, base + MMCIMASK1);
5929d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5939d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return IRQ_HANDLED;
5949d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
5959d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5969d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void msmsdcc_do_cmdirq(struct msmsdcc_host *host, uint32_t status)
5979d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
5989d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct mmc_command *cmd = host->curr.cmd;
5999d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	void __iomem	   *base = host->base;
6009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
6019d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.cmd = NULL;
6029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	cmd->resp[0] = readl(base + MMCIRESPONSE0);
6039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	cmd->resp[1] = readl(base + MMCIRESPONSE1);
6049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	cmd->resp[2] = readl(base + MMCIRESPONSE2);
6059d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	cmd->resp[3] = readl(base + MMCIRESPONSE3);
6069d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
6079d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	del_timer(&host->command_timer);
6089d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (status & MCI_CMDTIMEOUT) {
6099d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		cmd->error = -ETIMEDOUT;
6109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else if (status & MCI_CMDCRCFAIL &&
6119d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		   cmd->flags & MMC_RSP_CRC) {
6120a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: Command CRC error\n", mmc_hostname(host->mmc));
6139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		cmd->error = -EILSEQ;
6149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
6159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
6169d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!cmd->data || cmd->error) {
6179d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (host->curr.data && host->dma.sg)
6189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			msm_dmov_stop_cmd(host->dma.channel,
6199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat					  &host->dma.hdr, 0);
6209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		else if (host->curr.data) { /* Non DMA */
6219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			msmsdcc_stop_data(host);
6229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			msmsdcc_request_end(host, cmd->mrq);
6239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		} else /* host->data == NULL */
6249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			msmsdcc_request_end(host, cmd->mrq);
6259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else if (!(cmd->data->flags & MMC_DATA_READ))
6269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		msmsdcc_start_data(host, cmd->data);
6279d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
6289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
629b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perchesstatic void
630b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perchesmsmsdcc_handle_irq_data(struct msmsdcc_host *host, u32 status,
631b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches			void __iomem *base)
632b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches{
633b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	struct mmc_data *data = host->curr.data;
634b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches
635b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	if (!data)
636b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		return;
637b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches
638b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	/* Check for data errors */
639b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	if (status & (MCI_DATACRCFAIL | MCI_DATATIMEOUT |
640b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		      MCI_TXUNDERRUN | MCI_RXOVERRUN)) {
641b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		msmsdcc_data_err(host, data, status);
642b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		host->curr.data_xfered = 0;
643b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		if (host->dma.sg)
644b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches			msm_dmov_stop_cmd(host->dma.channel,
645b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches					  &host->dma.hdr, 0);
646b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		else {
647b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches			msmsdcc_stop_data(host);
648b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches			if (!data->stop)
649b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches				msmsdcc_request_end(host, data->mrq);
650b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches			else
651b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches				msmsdcc_start_command(host, data->stop, 0);
652b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		}
653b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	}
654b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches
655b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	/* Check for data done */
656b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	if (!host->curr.got_dataend && (status & MCI_DATAEND))
657b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		host->curr.got_dataend = 1;
658b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches
659b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	if (!host->curr.got_datablkend && (status & MCI_DATABLOCKEND))
660b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		host->curr.got_datablkend = 1;
661b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches
662b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	/*
663b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	 * If DMA is still in progress, we complete via the completion handler
664b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	 */
665b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	if (host->curr.got_dataend && host->curr.got_datablkend &&
666b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	    !host->dma.busy) {
667b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		/*
668b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		 * There appears to be an issue in the controller where
669b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		 * if you request a small block transfer (< fifo size),
670b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		 * you may get your DATAEND/DATABLKEND irq without the
671b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		 * PIO data irq.
672b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		 *
673b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		 * Check to see if there is still data to be read,
674b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		 * and simulate a PIO irq.
675b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		 */
676b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		if (readl(base + MMCISTATUS) & MCI_RXDATAAVLBL)
677b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches			msmsdcc_pio_irq(1, host);
678b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches
679b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		msmsdcc_stop_data(host);
680b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		if (!data->error)
681b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches			host->curr.data_xfered = host->curr.xfer_size;
682b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches
683b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		if (!data->stop)
684b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches			msmsdcc_request_end(host, data->mrq);
685b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		else
686b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches			msmsdcc_start_command(host, data->stop, 0);
687b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	}
688b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches}
689b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches
6909d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic irqreturn_t
6919d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_irq(int irq, void *dev_id)
6929d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
6939d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host	*host = dev_id;
6949d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	void __iomem		*base = host->base;
6959d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	u32			status;
6969d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	int			ret = 0;
6979d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	int			cardint = 0;
6989d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
6999d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_lock(&host->lock);
7009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7019d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	do {
7029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		status = readl(base + MMCISTATUS);
7039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
704b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		status &= (readl(base + MMCIMASK0) | MCI_DATABLOCKENDMASK);
7059d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		writel(status, base + MMCICLEAR);
7069d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
707865c8064a2fb07100525097983966b8e789bde1aSan Mehat		if (status & MCI_SDIOINTR)
708865c8064a2fb07100525097983966b8e789bde1aSan Mehat			status &= ~MCI_SDIOINTR;
709865c8064a2fb07100525097983966b8e789bde1aSan Mehat
710865c8064a2fb07100525097983966b8e789bde1aSan Mehat		if (!status)
711865c8064a2fb07100525097983966b8e789bde1aSan Mehat			break;
712865c8064a2fb07100525097983966b8e789bde1aSan Mehat
713b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		msmsdcc_handle_irq_data(host, status, base);
7149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (status & (MCI_CMDSENT | MCI_CMDRESPEND | MCI_CMDCRCFAIL |
7169d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			      MCI_CMDTIMEOUT) && host->curr.cmd) {
7179d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			msmsdcc_do_cmdirq(host, status);
7189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		}
7199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (status & MCI_SDIOINTOPER) {
7219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			cardint = 1;
7229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			status &= ~MCI_SDIOINTOPER;
7239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		}
7249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		ret = 1;
7259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} while (status);
7269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7279d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_unlock(&host->lock);
7289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7299d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	/*
7309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 * We have to delay handling the card interrupt as it calls
7319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 * back into the driver.
7329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 */
7339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (cardint)
7349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		mmc_signal_sdio_irq(host->mmc);
7359d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7369d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return IRQ_RETVAL(ret);
7379d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
7389d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
7409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_request(struct mmc_host *mmc, struct mmc_request *mrq)
7419d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
7429d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host *host = mmc_priv(mmc);
7439d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	unsigned long flags;
7449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	WARN_ON(host->curr.mrq != NULL);
7469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	WARN_ON(host->pwr == 0);
7479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_lock_irqsave(&host->lock, flags);
7499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->stats.reqs++;
7519d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7529d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->eject) {
7539d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (mrq->data && !(mrq->data->flags & MMC_DATA_READ)) {
7549d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			mrq->cmd->error = 0;
7559d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			mrq->data->bytes_xfered = mrq->data->blksz *
7569d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat						  mrq->data->blocks;
7579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		} else
7589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			mrq->cmd->error = -ENOMEDIUM;
7599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7609d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		spin_unlock_irqrestore(&host->lock, flags);
7619d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		mmc_request_done(mmc, mrq);
7629d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return;
7639d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
7649d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.mrq = mrq;
766865c8064a2fb07100525097983966b8e789bde1aSan Mehat	if (!host->clks_on)
767865c8064a2fb07100525097983966b8e789bde1aSan Mehat		msmsdcc_enable_clocks(host, 1);
7689d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7699d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (mrq->data && mrq->data->flags & MMC_DATA_READ)
7709d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		msmsdcc_start_data(host, mrq->data);
7719d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7729d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	msmsdcc_start_command(host, mrq->cmd, 0);
7739d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7749d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->cmdpoll && !msmsdcc_spin_on_status(host,
7759d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				MCI_CMDRESPEND|MCI_CMDCRCFAIL|MCI_CMDTIMEOUT,
7769d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				CMD_SPINMAX)) {
7779d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		uint32_t status = readl(host->base + MMCISTATUS);
7789d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		msmsdcc_do_cmdirq(host, status);
7799d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		writel(MCI_CMDRESPEND | MCI_CMDCRCFAIL | MCI_CMDTIMEOUT,
7809d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		       host->base + MMCICLEAR);
7819d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->stats.cmdpoll_hits++;
7829d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else {
7839d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->stats.cmdpoll_misses++;
7849d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		mod_timer(&host->command_timer, jiffies + HZ);
7859d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
7869d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_unlock_irqrestore(&host->lock, flags);
7879d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
7889d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7899d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
7909d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
7919d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
7929d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host *host = mmc_priv(mmc);
7939d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	u32 clk = 0, pwr = 0;
7949d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	int rc;
7954adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat	unsigned long flags;
7969d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7974adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat	spin_lock_irqsave(&host->lock, flags);
798865c8064a2fb07100525097983966b8e789bde1aSan Mehat	if (!host->clks_on)
799865c8064a2fb07100525097983966b8e789bde1aSan Mehat		msmsdcc_enable_clocks(host, 1);
8009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
801865c8064a2fb07100525097983966b8e789bde1aSan Mehat	if (ios->clock) {
8029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (ios->clock != host->clk_rate) {
8039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			rc = clk_set_rate(host->clk, ios->clock);
8049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			if (rc < 0)
8050a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches				pr_err("%s: Error setting clock rate (%d)\n",
8060a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches				       mmc_hostname(host->mmc), rc);
8079d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			else
8089d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				host->clk_rate = ios->clock;
8099d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		}
8109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		clk |= MCI_CLK_ENABLE;
8119d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
8129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (ios->bus_width == MMC_BUS_WIDTH_4)
8149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		clk |= (2 << 10); /* Set WIDEBUS */
8159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8169d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (ios->clock > 400000 && msmsdcc_pwrsave)
8179d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		clk |= (1 << 9); /* PWRSAVE */
8189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	clk |= (1 << 12); /* FLOW_ENA */
8209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	clk |= (1 << 15); /* feedback clock */
8219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->plat->translate_vdd)
8239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		pwr |= host->plat->translate_vdd(mmc_dev(mmc), ios->vdd);
8249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	switch (ios->power_mode) {
8269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	case MMC_POWER_OFF:
8279d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		break;
8289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	case MMC_POWER_UP:
8299d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		pwr |= MCI_PWR_UP;
8309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		break;
8319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	case MMC_POWER_ON:
8329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		pwr |= MCI_PWR_ON;
8339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		break;
8349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
8359d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8369d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
8379d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		pwr |= MCI_OD;
8389d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	writel(clk, host->base + MMCICLOCK);
8409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8419d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->pwr != pwr) {
8429d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->pwr = pwr;
8439d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		writel(pwr, host->base + MMCIPOWER);
8449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
845865c8064a2fb07100525097983966b8e789bde1aSan Mehat	if (host->clks_on)
8464adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat		msmsdcc_enable_clocks(host, 0);
8474adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat	spin_unlock_irqrestore(&host->lock, flags);
8489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
8499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void msmsdcc_enable_sdio_irq(struct mmc_host *mmc, int enable)
8519d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
8529d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host *host = mmc_priv(mmc);
8539d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	unsigned long flags;
8549d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	u32 status;
8559d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8569d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_lock_irqsave(&host->lock, flags);
8579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (msmsdcc_sdioirq == 1) {
8589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		status = readl(host->base + MMCIMASK0);
8599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (enable)
8609d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			status |= MCI_SDIOINTOPERMASK;
8619d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		else
8629d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			status &= ~MCI_SDIOINTOPERMASK;
8639d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->saved_irq0mask = status;
8649d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		writel(status, host->base + MMCIMASK0);
8659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
8669d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_unlock_irqrestore(&host->lock, flags);
8679d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
8689d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8699d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic const struct mmc_host_ops msmsdcc_ops = {
8709d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	.request	= msmsdcc_request,
8719d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	.set_ios	= msmsdcc_set_ios,
8729d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	.enable_sdio_irq = msmsdcc_enable_sdio_irq,
8739d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat};
8749d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8759d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
8769d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_check_status(unsigned long data)
8779d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
8789d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host *host = (struct msmsdcc_host *)data;
8799d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	unsigned int status;
8809d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8819d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!host->plat->status) {
8829d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		mmc_detect_change(host->mmc, 0);
8839d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		goto out;
8849d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
8859d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8869d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	status = host->plat->status(mmc_dev(host->mmc));
8879d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->eject = !status;
8889d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (status ^ host->oldstat) {
8890a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_info("%s: Slot status change detected (%d -> %d)\n",
8900a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches			mmc_hostname(host->mmc), host->oldstat, status);
8919d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (status)
8929d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			mmc_detect_change(host->mmc, (5 * HZ) / 2);
8939d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		else
8949d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			mmc_detect_change(host->mmc, 0);
8959d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
8969d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8979d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->oldstat = status;
8989d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8999d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatout:
9009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->timer.function)
9019d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		mod_timer(&host->timer, jiffies + HZ);
9029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
9039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic irqreturn_t
9059d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_platform_status_irq(int irq, void *dev_id)
9069d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
9079d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host *host = dev_id;
9089d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9099d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	printk(KERN_DEBUG "%s: %d\n", __func__, irq);
9109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	msmsdcc_check_status((unsigned long) host);
9119d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return IRQ_HANDLED;
9129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
9139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
9159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_status_notify_cb(int card_present, void *dev_id)
9169d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
9179d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host *host = dev_id;
9189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	printk(KERN_DEBUG "%s: card_present %d\n", mmc_hostname(host->mmc),
9209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	       card_present);
9219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	msmsdcc_check_status((unsigned long) host);
9229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
9239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
924865c8064a2fb07100525097983966b8e789bde1aSan Mehatstatic void
925865c8064a2fb07100525097983966b8e789bde1aSan Mehatmsmsdcc_busclk_expired(unsigned long _data)
926865c8064a2fb07100525097983966b8e789bde1aSan Mehat{
927865c8064a2fb07100525097983966b8e789bde1aSan Mehat	struct msmsdcc_host	*host = (struct msmsdcc_host *) _data;
928865c8064a2fb07100525097983966b8e789bde1aSan Mehat	unsigned long 		flags;
929865c8064a2fb07100525097983966b8e789bde1aSan Mehat
930865c8064a2fb07100525097983966b8e789bde1aSan Mehat	spin_lock_irqsave(&host->lock, flags);
931865c8064a2fb07100525097983966b8e789bde1aSan Mehat	if (host->clks_on)
932865c8064a2fb07100525097983966b8e789bde1aSan Mehat		msmsdcc_enable_clocks(host, 0);
933865c8064a2fb07100525097983966b8e789bde1aSan Mehat
934865c8064a2fb07100525097983966b8e789bde1aSan Mehat	spin_unlock_irqrestore(&host->lock, flags);
935865c8064a2fb07100525097983966b8e789bde1aSan Mehat}
936865c8064a2fb07100525097983966b8e789bde1aSan Mehat
9379d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat/*
9389d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat * called when a command expires.
9399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat * Dump some debugging, and then error
9409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat * out the transaction.
9419d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat */
9429d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
9439d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_command_expired(unsigned long _data)
9449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
9459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host	*host = (struct msmsdcc_host *) _data;
9469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct mmc_request	*mrq;
9479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	unsigned long		flags;
9489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_lock_irqsave(&host->lock, flags);
9509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mrq = host->curr.mrq;
9519d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9529d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!mrq) {
9530a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_info("%s: Command expiry misfire\n",
9540a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches			mmc_hostname(host->mmc));
9559d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		spin_unlock_irqrestore(&host->lock, flags);
9569d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return;
9579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
9589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9590a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches	pr_err("%s: Command timeout (%p %p %p %p)\n",
9609d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	       mmc_hostname(host->mmc), mrq, mrq->cmd,
9619d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	       mrq->data, host->dma.sg);
9629d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mrq->cmd->error = -ETIMEDOUT;
9639d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	msmsdcc_stop_data(host);
9649d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	writel(0, host->base + MMCICOMMAND);
9669d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9679d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.mrq = NULL;
9689d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.cmd = NULL;
9699d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
970865c8064a2fb07100525097983966b8e789bde1aSan Mehat	if (host->clks_on)
971865c8064a2fb07100525097983966b8e789bde1aSan Mehat		msmsdcc_enable_clocks(host, 0);
9729d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_unlock_irqrestore(&host->lock, flags);
9739d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc_request_done(host->mmc, mrq);
9749d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
9759d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9769d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int
9779d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_init_dma(struct msmsdcc_host *host)
9789d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
9799d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	memset(&host->dma, 0, sizeof(struct msmsdcc_dma_data));
9809d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.host = host;
9819d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.channel = -1;
9829d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9839d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!host->dmares)
9849d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return -ENODEV;
9859d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9869d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.nc = dma_alloc_coherent(NULL,
9879d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat					  sizeof(struct msmsdcc_nc_dmadata),
9889d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat					  &host->dma.nc_busaddr,
9899d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat					  GFP_KERNEL);
9909d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->dma.nc == NULL) {
9910a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("Unable to allocate DMA buffer\n");
9929d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return -ENOMEM;
9939d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
9949d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	memset(host->dma.nc, 0x00, sizeof(struct msmsdcc_nc_dmadata));
9959d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.cmd_busaddr = host->dma.nc_busaddr;
9969d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.cmdptr_busaddr = host->dma.nc_busaddr +
9979d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				offsetof(struct msmsdcc_nc_dmadata, cmdptr);
9989d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.channel = host->dmares->start;
9999d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return 0;
10019d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
10029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#ifdef CONFIG_MMC_MSM7X00A_RESUME_IN_WQ
10049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
10059d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatdo_resume_work(struct work_struct *work)
10069d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
10079d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host *host =
10089d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		container_of(work, struct msmsdcc_host, resume_task);
10099d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct mmc_host	*mmc = host->mmc;
10109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10119d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (mmc) {
10129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		mmc_resume_host(mmc);
10139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (host->stat_irq)
10149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			enable_irq(host->stat_irq);
10159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
10169d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
10179d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#endif
10189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int
10209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_probe(struct platform_device *pdev)
10219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
10229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct mmc_platform_data *plat = pdev->dev.platform_data;
10239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host *host;
10249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct mmc_host *mmc;
10259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct resource *cmd_irqres = NULL;
10269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct resource *pio_irqres = NULL;
10279d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct resource *stat_irqres = NULL;
10289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct resource *memres = NULL;
10299d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct resource *dmares = NULL;
10309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	int ret;
10319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	/* must have platform data */
10339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!plat) {
10340a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: Platform data not available\n", __func__);
10359d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		ret = -EINVAL;
10369d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		goto out;
10379d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
10389d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (pdev->id < 1 || pdev->id > 4)
10409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return -EINVAL;
10419d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10429d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (pdev->resource == NULL || pdev->num_resources < 2) {
10430a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: Invalid resource\n", __func__);
10449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return -ENXIO;
10459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
10469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	memres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
10489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
10499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	cmd_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
10509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat						  "cmd_irq");
10519d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	pio_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
10529d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat						  "pio_irq");
10539d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	stat_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
10549d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat						   "status_irq");
10559d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10569d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!cmd_irqres || !pio_irqres || !memres) {
10570a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: Invalid resource\n", __func__);
10589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return -ENXIO;
10599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
10609d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10619d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	/*
10629d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 * Setup our host structure
10639d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 */
10649d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc = mmc_alloc_host(sizeof(struct msmsdcc_host), &pdev->dev);
10669d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!mmc) {
10679d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		ret = -ENOMEM;
10689d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		goto out;
10699d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
10709d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10719d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host = mmc_priv(mmc);
10729d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->pdev_id = pdev->id;
10739d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->plat = plat;
10749d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->mmc = mmc;
10759d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10769d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->cmdpoll = 1;
10779d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1078865c8064a2fb07100525097983966b8e789bde1aSan Mehat	host->use_bustimer = 1;
1079865c8064a2fb07100525097983966b8e789bde1aSan Mehat
10809d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->base = ioremap(memres->start, PAGE_SIZE);
10819d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!host->base) {
10829d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		ret = -ENOMEM;
10839d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		goto out;
10849d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
10859d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10869d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->cmd_irqres = cmd_irqres;
10879d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->pio_irqres = pio_irqres;
10889d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->memres = memres;
10899d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dmares = dmares;
10909d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_lock_init(&host->lock);
10919d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10929d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	/*
10939d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 * Setup DMA
10949d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 */
10959d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	msmsdcc_init_dma(host);
10969d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10974adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat	/* Get our clocks */
10989d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->pclk = clk_get(&pdev->dev, "sdc_pclk");
10999d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (IS_ERR(host->pclk)) {
11009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		ret = PTR_ERR(host->pclk);
11019d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		goto host_free;
11029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
11039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->clk = clk_get(&pdev->dev, "sdc_clk");
11059d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (IS_ERR(host->clk)) {
11069d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		ret = PTR_ERR(host->clk);
11074adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat		goto pclk_put;
11089d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
11099d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11104adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat	/* Enable clocks */
11114adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat	ret = msmsdcc_enable_clocks(host, 1);
11129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (ret)
11139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		goto clk_put;
11149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	ret = clk_set_rate(host->clk, msmsdcc_fmin);
11169d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (ret) {
11170a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: Clock rate set failed (%d)\n", __func__, ret);
11189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		goto clk_disable;
11199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
11209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11214adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat	host->pclk_rate = clk_get_rate(host->pclk);
11229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->clk_rate = clk_get_rate(host->clk);
11239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	/*
11259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 * Setup MMC host structure
11269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 */
11279d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->ops = &msmsdcc_ops;
11289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->f_min = msmsdcc_fmin;
11299d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->f_max = msmsdcc_fmax;
11309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->ocr_avail = plat->ocr_mask;
11319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (msmsdcc_4bit)
11339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		mmc->caps |= MMC_CAP_4_BIT_DATA;
11349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (msmsdcc_sdioirq)
11359d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		mmc->caps |= MMC_CAP_SDIO_IRQ;
11369d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
11379d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11389d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->max_phys_segs = NR_SG;
11399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->max_hw_segs = NR_SG;
11409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->max_blk_size = 4096;	/* MCI_DATA_CTL BLOCKSIZE up to 4096 */
11419d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->max_blk_count = 65536;
11429d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11439d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->max_req_size = 33554432;	/* MCI_DATA_LENGTH is 25 bits */
11449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->max_seg_size = mmc->max_req_size;
11459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	writel(0, host->base + MMCIMASK0);
11479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	writel(0x5e007ff, host->base + MMCICLEAR); /* Add: 1 << 25 */
11489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	writel(MCI_IRQENABLE, host->base + MMCIMASK0);
11509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->saved_irq0mask = MCI_IRQENABLE;
11519d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11529d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	/*
11539d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 * Setup card detect change
11549d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 */
11559d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11569d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	memset(&host->timer, 0, sizeof(host->timer));
11579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (stat_irqres && !(stat_irqres->flags & IORESOURCE_DISABLED)) {
11599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		unsigned long irqflags = IRQF_SHARED |
11609d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			(stat_irqres->flags & IRQF_TRIGGER_MASK);
11619d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11629d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->stat_irq = stat_irqres->start;
11639d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		ret = request_irq(host->stat_irq,
11649d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				  msmsdcc_platform_status_irq,
11659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				  irqflags,
11669d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				  DRIVER_NAME " (slot)",
11679d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				  host);
11689d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (ret) {
11690a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches			pr_err("%s: Unable to get slot IRQ %d (%d)\n",
11700a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches			       mmc_hostname(mmc), host->stat_irq, ret);
11719d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			goto clk_disable;
11729d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		}
11739d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else if (plat->register_status_notify) {
11749d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		plat->register_status_notify(msmsdcc_status_notify_cb, host);
11759d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else if (!plat->status)
11760a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: No card detect facilities available\n",
11779d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		       mmc_hostname(mmc));
11789d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	else {
11799d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		init_timer(&host->timer);
11809d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->timer.data = (unsigned long)host;
11819d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->timer.function = msmsdcc_check_status;
11829d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->timer.expires = jiffies + HZ;
11839d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		add_timer(&host->timer);
11849d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
11859d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11869d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (plat->status) {
11879d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->oldstat = host->plat->status(mmc_dev(host->mmc));
11889d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->eject = !host->oldstat;
11899d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
11909d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11919d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	/*
11929d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 * Setup a command timer. We currently need this due to
11939d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 * some 'strange' timeout / error handling situations.
11949d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 */
11959d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	init_timer(&host->command_timer);
11969d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->command_timer.data = (unsigned long) host;
11979d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->command_timer.function = msmsdcc_command_expired;
11989d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1199865c8064a2fb07100525097983966b8e789bde1aSan Mehat	init_timer(&host->busclk_timer);
1200865c8064a2fb07100525097983966b8e789bde1aSan Mehat	host->busclk_timer.data = (unsigned long) host;
1201865c8064a2fb07100525097983966b8e789bde1aSan Mehat	host->busclk_timer.function = msmsdcc_busclk_expired;
1202865c8064a2fb07100525097983966b8e789bde1aSan Mehat
12039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	ret = request_irq(cmd_irqres->start, msmsdcc_irq, IRQF_SHARED,
12049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			  DRIVER_NAME " (cmd)", host);
12059d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (ret)
12069d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		goto stat_irq_free;
12079d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12089d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	ret = request_irq(pio_irqres->start, msmsdcc_pio_irq, IRQF_SHARED,
12099d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			  DRIVER_NAME " (pio)", host);
12109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (ret)
12119d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		goto cmd_irq_free;
12129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc_set_drvdata(pdev, mmc);
12149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc_add_host(mmc);
12159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12160a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches	pr_info("%s: Qualcomm MSM SDCC at 0x%016llx irq %d,%d dma %d\n",
12170a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		mmc_hostname(mmc), (unsigned long long)memres->start,
12180a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		(unsigned int) cmd_irqres->start,
12190a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		(unsigned int) host->stat_irq, host->dma.channel);
12200a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches	pr_info("%s: 4 bit data mode %s\n", mmc_hostname(mmc),
12210a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		(mmc->caps & MMC_CAP_4_BIT_DATA ? "enabled" : "disabled"));
12220a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches	pr_info("%s: MMC clock %u -> %u Hz, PCLK %u Hz\n",
12230a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		mmc_hostname(mmc), msmsdcc_fmin, msmsdcc_fmax, host->pclk_rate);
12240a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches	pr_info("%s: Slot eject status = %d\n", mmc_hostname(mmc), host->eject);
12250a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches	pr_info("%s: Power save feature enable = %d\n",
12260a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		mmc_hostname(mmc), msmsdcc_pwrsave);
12279d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->dma.channel != -1) {
12290a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_info("%s: DM non-cached buffer at %p, dma_addr 0x%.8x\n",
12300a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches			mmc_hostname(mmc), host->dma.nc, host->dma.nc_busaddr);
12310a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_info("%s: DM cmd busaddr 0x%.8x, cmdptr busaddr 0x%.8x\n",
12320a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches			mmc_hostname(mmc), host->dma.cmd_busaddr,
12330a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches			host->dma.cmdptr_busaddr);
12349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else
12350a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_info("%s: PIO transfer enabled\n", mmc_hostname(mmc));
12369d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->timer.function)
12370a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_info("%s: Polling status mode enabled\n", mmc_hostname(mmc));
12389d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return 0;
12409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat cmd_irq_free:
12419d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	free_irq(cmd_irqres->start, host);
12429d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat stat_irq_free:
12439d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->stat_irq)
12449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		free_irq(host->stat_irq, host);
12459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat clk_disable:
12464adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat	msmsdcc_enable_clocks(host, 0);
12479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat clk_put:
12489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	clk_put(host->clk);
12499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat pclk_put:
12509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	clk_put(host->pclk);
12519d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat host_free:
12529d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc_free_host(mmc);
12539d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat out:
12549d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return ret;
12559d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
12569d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int
12589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_suspend(struct platform_device *dev, pm_message_t state)
12599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
12609d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct mmc_host *mmc = mmc_get_drvdata(dev);
12619d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	int rc = 0;
12629d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12639d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (mmc) {
12649d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		struct msmsdcc_host *host = mmc_priv(mmc);
12659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12669d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (host->stat_irq)
12679d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			disable_irq(host->stat_irq);
12689d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12699d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (mmc->card && mmc->card->type != MMC_TYPE_SDIO)
12709d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			rc = mmc_suspend_host(mmc, state);
12719d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (!rc) {
12729d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			writel(0, host->base + MMCIMASK0);
12739d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12744adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat			if (host->clks_on)
12754adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat				msmsdcc_enable_clocks(host, 0);
12769d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		}
12779d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
12789d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return rc;
12799d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
12809d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12819d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int
12829d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_resume(struct platform_device *dev)
12839d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
12849d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct mmc_host *mmc = mmc_get_drvdata(dev);
12859d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	unsigned long flags;
12869d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12879d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (mmc) {
12889d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		struct msmsdcc_host *host = mmc_priv(mmc);
12899d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12909d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		spin_lock_irqsave(&host->lock, flags);
12919d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12924adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat		if (!host->clks_on)
12934adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat			msmsdcc_enable_clocks(host, 1);
12949d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12959d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		writel(host->saved_irq0mask, host->base + MMCIMASK0);
12969d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12979d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		spin_unlock_irqrestore(&host->lock, flags);
12989d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12999d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (mmc->card && mmc->card->type != MMC_TYPE_SDIO)
13009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			mmc_resume_host(mmc);
13015b8a2fb34f5670b1f07483bfa40de9ce539dbdb2Roel Kluin		if (host->stat_irq)
13029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			enable_irq(host->stat_irq);
13039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
13049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return 0;
13059d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
13069d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
13079d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic struct platform_driver msmsdcc_driver = {
13089d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	.probe		= msmsdcc_probe,
13099d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	.suspend	= msmsdcc_suspend,
13109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	.resume		= msmsdcc_resume,
13119d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	.driver		= {
13129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		.name	= "msm_sdcc",
13139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	},
13149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat};
13159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
13169d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int __init msmsdcc_init(void)
13179d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
13189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return platform_driver_register(&msmsdcc_driver);
13199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
13209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
13219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void __exit msmsdcc_exit(void)
13229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
13239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	platform_driver_unregister(&msmsdcc_driver);
13249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
13259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
13269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmodule_init(msmsdcc_init);
13279d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmodule_exit(msmsdcc_exit);
13289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
13299d2bd7383c71d38c60328a3dc8a946eda2013826San MehatMODULE_DESCRIPTION("Qualcomm MSM 7X00A Multimedia Card Interface driver");
13309d2bd7383c71d38c60328a3dc8a946eda2013826San MehatMODULE_LICENSE("GPL");
1331