msm_sdcc.c revision 8b1c2ba274c8416afb7eab3bd788f98a917efe06
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;
658b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat
66865c8064a2fb07100525097983966b8e789bde1aSan Mehat	WARN_ON(enable == host->clks_on);
67865c8064a2fb07100525097983966b8e789bde1aSan Mehat	if (enable) {
68865c8064a2fb07100525097983966b8e789bde1aSan Mehat		rc = clk_enable(host->pclk);
69865c8064a2fb07100525097983966b8e789bde1aSan Mehat		if (rc)
70865c8064a2fb07100525097983966b8e789bde1aSan Mehat			return rc;
71865c8064a2fb07100525097983966b8e789bde1aSan Mehat		rc = clk_enable(host->clk);
72865c8064a2fb07100525097983966b8e789bde1aSan Mehat		if (rc) {
73865c8064a2fb07100525097983966b8e789bde1aSan Mehat			clk_disable(host->pclk);
74865c8064a2fb07100525097983966b8e789bde1aSan Mehat			return rc;
75865c8064a2fb07100525097983966b8e789bde1aSan Mehat		}
768b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		udelay(1 + ((3 * USEC_PER_SEC) /
778b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		       (host->clk_rate ? host->clk_rate : msmsdcc_fmin)));
78865c8064a2fb07100525097983966b8e789bde1aSan Mehat		host->clks_on = 1;
79865c8064a2fb07100525097983966b8e789bde1aSan Mehat	} else {
80865c8064a2fb07100525097983966b8e789bde1aSan Mehat		clk_disable(host->clk);
81865c8064a2fb07100525097983966b8e789bde1aSan Mehat		clk_disable(host->pclk);
82865c8064a2fb07100525097983966b8e789bde1aSan Mehat		host->clks_on = 0;
83865c8064a2fb07100525097983966b8e789bde1aSan Mehat	}
84865c8064a2fb07100525097983966b8e789bde1aSan Mehat	return 0;
85865c8064a2fb07100525097983966b8e789bde1aSan Mehat}
86865c8064a2fb07100525097983966b8e789bde1aSan Mehat
878b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehatstatic inline unsigned int
888b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehatmsmsdcc_readl(struct msmsdcc_host *host, unsigned int reg)
898b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat{
908b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	return readl(host->base + reg);
918b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat}
928b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat
938b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehatstatic inline void
948b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehatmsmsdcc_writel(struct msmsdcc_host *host, u32 data, unsigned int reg)
958b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat{
968b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	writel(data, host->base + reg);
978b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	/* 3 clk delay required! */
988b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	udelay(1 + ((3 * USEC_PER_SEC) /
998b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	       (host->clk_rate ? host->clk_rate : msmsdcc_fmin)));
1008b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat}
101865c8064a2fb07100525097983966b8e789bde1aSan Mehat
1029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
1039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_start_command(struct msmsdcc_host *host, struct mmc_command *cmd,
1049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		      u32 c);
1059d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1069d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
1079d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_request_end(struct msmsdcc_host *host, struct mmc_request *mrq)
1089d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
1098b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	msmsdcc_writel(host, 0, MMCICOMMAND);
1109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1119d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	BUG_ON(host->curr.data);
1129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.mrq = NULL;
1149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.cmd = NULL;
1159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1169d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (mrq->data)
1179d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		mrq->data->bytes_xfered = host->curr.data_xfered;
1189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (mrq->cmd->error == -ETIMEDOUT)
1199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		mdelay(5);
1209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
121865c8064a2fb07100525097983966b8e789bde1aSan Mehat	if (host->use_bustimer)
122865c8064a2fb07100525097983966b8e789bde1aSan Mehat		mod_timer(&host->busclk_timer, jiffies + HZ);
1239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	/*
1249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 * Need to drop the host lock here; mmc_request_done may call
1259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 * back into the driver...
1269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 */
1279d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_unlock(&host->lock);
1289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc_request_done(host->mmc, mrq);
1299d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_lock(&host->lock);
1309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
1319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
1339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_stop_data(struct msmsdcc_host *host)
1349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
1358b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	msmsdcc_writel(host, 0, MMCIDATACTRL);
1369d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.data = NULL;
1379d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.got_dataend = host->curr.got_datablkend = 0;
1389d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
1399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatuint32_t msmsdcc_fifo_addr(struct msmsdcc_host *host)
1419d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
14275d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	switch (host->pdev_id) {
14375d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	case 1:
1449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return MSM_SDC1_PHYS + MMCIFIFO;
14575d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	case 2:
1469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return MSM_SDC2_PHYS + MMCIFIFO;
14775d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	case 3:
1489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return MSM_SDC3_PHYS + MMCIFIFO;
14975d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	case 4:
1509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return MSM_SDC4_PHYS + MMCIFIFO;
15175d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	}
15275d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	BUG();
1539d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return 0;
1549d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
1559d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1569d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
1579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_dma_complete_func(struct msm_dmov_cmd *cmd,
1589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			  unsigned int result,
1599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			  struct msm_dmov_errdata *err)
1609d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
1619d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_dma_data	*dma_data =
1629d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		container_of(cmd, struct msmsdcc_dma_data, hdr);
1639d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host	*host = dma_data->host;
1649d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	unsigned long		flags;
1659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct mmc_request	*mrq;
1669d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1679d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_lock_irqsave(&host->lock, flags);
1689d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mrq = host->curr.mrq;
1699d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	BUG_ON(!mrq);
1709d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1719d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!(result & DMOV_RSLT_VALID)) {
1720a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("msmsdcc: Invalid DataMover result\n");
1739d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		goto out;
1749d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
1759d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1769d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (result & DMOV_RSLT_DONE) {
1779d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->curr.data_xfered = host->curr.xfer_size;
1789d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else {
1799d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		/* Error or flush  */
1809d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (result & DMOV_RSLT_ERROR)
1810a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches			pr_err("%s: DMA error (0x%.8x)\n",
1829d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			       mmc_hostname(host->mmc), result);
1839d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (result & DMOV_RSLT_FLUSH)
1840a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches			pr_err("%s: DMA channel flushed (0x%.8x)\n",
1859d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			       mmc_hostname(host->mmc), result);
1869d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (err)
1870a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches			pr_err("Flush data: %.8x %.8x %.8x %.8x %.8x %.8x\n",
1889d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			       err->flush[0], err->flush[1], err->flush[2],
1899d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			       err->flush[3], err->flush[4], err->flush[5]);
1909d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (!mrq->data->error)
1919d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			mrq->data->error = -EIO;
1929d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
1939d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.busy = 0;
1949d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	dma_unmap_sg(mmc_dev(host->mmc), host->dma.sg, host->dma.num_ents,
1959d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		     host->dma.dir);
1969d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1979d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->curr.user_pages) {
1989d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		struct scatterlist *sg = host->dma.sg;
1999d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		int i;
2009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
20175d145283b2e42619d7ee1e00b78466bacd51808Joe Perches		for (i = 0; i < host->dma.num_ents; i++)
20275d145283b2e42619d7ee1e00b78466bacd51808Joe Perches			flush_dcache_page(sg_page(sg++));
2039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
2049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2059d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.sg = NULL;
2069d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2079d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if ((host->curr.got_dataend && host->curr.got_datablkend)
2089d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	     || mrq->data->error) {
2099d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		/*
2119d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		 * If we've already gotten our DATAEND / DATABLKEND
2129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		 * for this request, then complete it through here.
2139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		 */
2149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		msmsdcc_stop_data(host);
2159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2169d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (!mrq->data->error)
2179d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			host->curr.data_xfered = host->curr.xfer_size;
2189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (!mrq->data->stop || mrq->cmd->error) {
2198b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat			msmsdcc_writel(host, 0, MMCICOMMAND);
2209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			host->curr.mrq = NULL;
2219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			host->curr.cmd = NULL;
2229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			mrq->data->bytes_xfered = host->curr.data_xfered;
2239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			spin_unlock_irqrestore(&host->lock, flags);
2259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			mmc_request_done(host->mmc, mrq);
2269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			return;
2279d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		} else
2289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			msmsdcc_start_command(host, mrq->data->stop, 0);
2299d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
2309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatout:
2329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_unlock_irqrestore(&host->lock, flags);
2339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return;
2349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
2359d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2369d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int validate_dma(struct msmsdcc_host *host, struct mmc_data *data)
2379d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
2389d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->dma.channel == -1)
2399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return -ENOENT;
2409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2419d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if ((data->blksz * data->blocks) < MCI_FIFOSIZE)
2429d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return -EINVAL;
2439d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if ((data->blksz * data->blocks) % MCI_FIFOSIZE)
2449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return -EINVAL;
2459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return 0;
2469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
2479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int msmsdcc_config_dma(struct msmsdcc_host *host, struct mmc_data *data)
2499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
2509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_nc_dmadata *nc;
2519d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	dmov_box *box;
2529d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	uint32_t rows;
2539d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	uint32_t crci;
2549d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	unsigned int n;
2559d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	int i, rc;
2569d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct scatterlist *sg = data->sg;
2579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	rc = validate_dma(host, data);
2599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (rc)
2609d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return rc;
2619d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2629d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.sg = data->sg;
2639d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.num_ents = data->sg_len;
2649d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	nc = host->dma.nc;
2669d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
26775d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	switch (host->pdev_id) {
26875d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	case 1:
2699d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		crci = MSMSDCC_CRCI_SDC1;
27075d145283b2e42619d7ee1e00b78466bacd51808Joe Perches		break;
27175d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	case 2:
2729d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		crci = MSMSDCC_CRCI_SDC2;
27375d145283b2e42619d7ee1e00b78466bacd51808Joe Perches		break;
27475d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	case 3:
2759d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		crci = MSMSDCC_CRCI_SDC3;
27675d145283b2e42619d7ee1e00b78466bacd51808Joe Perches		break;
27775d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	case 4:
2789d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		crci = MSMSDCC_CRCI_SDC4;
27975d145283b2e42619d7ee1e00b78466bacd51808Joe Perches		break;
28075d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	default:
2819d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->dma.sg = NULL;
2829d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->dma.num_ents = 0;
2839d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return -ENOENT;
2849d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
2859d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2869d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (data->flags & MMC_DATA_READ)
2879d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->dma.dir = DMA_FROM_DEVICE;
2889d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	else
2899d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->dma.dir = DMA_TO_DEVICE;
2909d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2919d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.user_pages = 0;
2929d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2939d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	n = dma_map_sg(mmc_dev(host->mmc), host->dma.sg,
29475d145283b2e42619d7ee1e00b78466bacd51808Joe Perches		       host->dma.num_ents, host->dma.dir);
2959d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2969d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (n != host->dma.num_ents) {
2970a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: Unable to map in all sg elements\n",
2989d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		       mmc_hostname(host->mmc));
2999d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->dma.sg = NULL;
3009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->dma.num_ents = 0;
3019d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return -ENOMEM;
3029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
3039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	box = &nc->cmd[0];
3059d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	for (i = 0; i < host->dma.num_ents; i++) {
3069d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		box->cmd = CMD_MODE_BOX;
3079d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3089d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (i == (host->dma.num_ents - 1))
3099d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->cmd |= CMD_LC;
3109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		rows = (sg_dma_len(sg) % MCI_FIFOSIZE) ?
3119d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			(sg_dma_len(sg) / MCI_FIFOSIZE) + 1 :
3129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			(sg_dma_len(sg) / MCI_FIFOSIZE) ;
3139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (data->flags & MMC_DATA_READ) {
3159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->src_row_addr = msmsdcc_fifo_addr(host);
3169d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->dst_row_addr = sg_dma_address(sg);
3179d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->src_dst_len = (MCI_FIFOSIZE << 16) |
3199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat					   (MCI_FIFOSIZE);
3209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->row_offset = MCI_FIFOSIZE;
3219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->num_rows = rows * ((1 << 16) + 1);
3239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->cmd |= CMD_SRC_CRCI(crci);
3249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		} else {
3259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->src_row_addr = sg_dma_address(sg);
3269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->dst_row_addr = msmsdcc_fifo_addr(host);
3279d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->src_dst_len = (MCI_FIFOSIZE << 16) |
3299d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat					   (MCI_FIFOSIZE);
3309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->row_offset = (MCI_FIFOSIZE << 16);
3319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->num_rows = rows * ((1 << 16) + 1);
3339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->cmd |= CMD_DST_CRCI(crci);
3349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		}
3359d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		box++;
3369d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		sg++;
3379d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
3389d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	/* location of command block must be 64 bit aligned */
3409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	BUG_ON(host->dma.cmd_busaddr & 0x07);
3419d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3429d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	nc->cmdptr = (host->dma.cmd_busaddr >> 3) | CMD_PTR_LP;
3439d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.hdr.cmdptr = DMOV_CMD_PTR_LIST |
3449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			       DMOV_CMD_ADDR(host->dma.cmdptr_busaddr);
3459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.hdr.complete_func = msmsdcc_dma_complete_func;
3465b00f40f90e7b17c11cf388680f43e8466b3666dSan Mehat	host->dma.hdr.execute_func = NULL;
3479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return 0;
3499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
3509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3519d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
3529d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_start_data(struct msmsdcc_host *host, struct mmc_data *data)
3539d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
3549d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	unsigned int datactrl, timeout;
3559d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	unsigned long long clks;
3569d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	unsigned int pio_irqmask = 0;
3579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.data = data;
3599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.xfer_size = data->blksz * data->blocks;
3609d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.xfer_remain = host->curr.xfer_size;
3619d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.data_xfered = 0;
3629d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.got_dataend = 0;
3639d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.got_datablkend = 0;
3649d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	memset(&host->pio, 0, sizeof(host->pio));
3669d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3679d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	clks = (unsigned long long)data->timeout_ns * host->clk_rate;
36875d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	do_div(clks, NSEC_PER_SEC);
3699d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	timeout = data->timeout_clks + (unsigned int)clks;
3708b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	msmsdcc_writel(host, timeout, MMCIDATATIMER);
3719d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3728b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	msmsdcc_writel(host, host->curr.xfer_size, MMCIDATALENGTH);
3739d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3749d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	datactrl = MCI_DPSM_ENABLE | (data->blksz << 4);
3759d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3769d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!msmsdcc_config_dma(host, data))
3779d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		datactrl |= MCI_DPSM_DMAENABLE;
3789d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	else {
3799d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->pio.sg = data->sg;
3809d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->pio.sg_len = data->sg_len;
3819d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->pio.sg_off = 0;
3829d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3839d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (data->flags & MMC_DATA_READ) {
3849d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			pio_irqmask = MCI_RXFIFOHALFFULLMASK;
3859d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			if (host->curr.xfer_remain < MCI_FIFOSIZE)
3869d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				pio_irqmask |= MCI_RXDATAAVLBLMASK;
3879d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		} else
3889d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			pio_irqmask = MCI_TXFIFOHALFEMPTYMASK;
3899d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
3909d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3919d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (data->flags & MMC_DATA_READ)
3929d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		datactrl |= MCI_DPSM_DIRECTION;
3939d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3948b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	msmsdcc_writel(host, pio_irqmask, MMCIMASK1);
3958b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	msmsdcc_writel(host, datactrl, MMCIDATACTRL);
3969d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3979d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (datactrl & MCI_DPSM_DMAENABLE) {
3989d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->dma.busy = 1;
3999d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		msm_dmov_enqueue_cmd(host->dma.channel, &host->dma.hdr);
4009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
4019d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
4029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
403b3fa579118b239e218e690f5ef76870aff6fe738San Mehatstatic int
404b3fa579118b239e218e690f5ef76870aff6fe738San Mehatsnoop_cccr_abort(struct mmc_command *cmd)
405b3fa579118b239e218e690f5ef76870aff6fe738San Mehat{
406b3fa579118b239e218e690f5ef76870aff6fe738San Mehat	if ((cmd->opcode == 52) &&
407b3fa579118b239e218e690f5ef76870aff6fe738San Mehat	    (cmd->arg & 0x80000000) &&
408b3fa579118b239e218e690f5ef76870aff6fe738San Mehat	    (((cmd->arg >> 9) & 0x1ffff) == SDIO_CCCR_ABORT))
409b3fa579118b239e218e690f5ef76870aff6fe738San Mehat		return 1;
410b3fa579118b239e218e690f5ef76870aff6fe738San Mehat	return 0;
411b3fa579118b239e218e690f5ef76870aff6fe738San Mehat}
412b3fa579118b239e218e690f5ef76870aff6fe738San Mehat
4139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
4149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_start_command(struct msmsdcc_host *host, struct mmc_command *cmd, u32 c)
4159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
4168b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	if (msmsdcc_readl(host, MMCICOMMAND) & MCI_CPSM_ENABLE)
4178b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		msmsdcc_writel(host, 0, MMCICOMMAND);
4189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	c |= cmd->opcode | MCI_CPSM_ENABLE;
4209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (cmd->flags & MMC_RSP_PRESENT) {
4229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (cmd->flags & MMC_RSP_136)
4239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			c |= MCI_CPSM_LONGRSP;
4249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		c |= MCI_CPSM_RESPONSE;
4259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
4269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
42775d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	if (cmd->opcode == 17 || cmd->opcode == 18 ||
42875d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	    cmd->opcode == 24 || cmd->opcode == 25 ||
42975d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	    cmd->opcode == 53)
4309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		c |= MCI_CSPM_DATCMD;
4319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (cmd == cmd->mrq->stop)
4339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		c |= MCI_CSPM_MCIABORT;
4349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
435b3fa579118b239e218e690f5ef76870aff6fe738San Mehat	if (snoop_cccr_abort(cmd))
436b3fa579118b239e218e690f5ef76870aff6fe738San Mehat		c |= MCI_CSPM_MCIABORT;
437b3fa579118b239e218e690f5ef76870aff6fe738San Mehat
4389d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.cmd = cmd;
4399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->stats.cmds++;
4419d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4428b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	msmsdcc_writel(host, cmd->arg, MMCIARGUMENT);
4438b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	msmsdcc_writel(host, c, MMCICOMMAND);
4449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
4459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
4479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_data_err(struct msmsdcc_host *host, struct mmc_data *data,
4489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		 unsigned int status)
4499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
4509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (status & MCI_DATACRCFAIL) {
4510a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: Data CRC error\n", mmc_hostname(host->mmc));
4520a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: opcode 0x%.8x\n", __func__,
4539d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		       data->mrq->cmd->opcode);
4540a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: blksz %d, blocks %d\n", __func__,
4559d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		       data->blksz, data->blocks);
4569d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		data->error = -EILSEQ;
4579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else if (status & MCI_DATATIMEOUT) {
4580a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: Data timeout\n", mmc_hostname(host->mmc));
4599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		data->error = -ETIMEDOUT;
4609d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else if (status & MCI_RXOVERRUN) {
4610a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: RX overrun\n", mmc_hostname(host->mmc));
4629d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		data->error = -EIO;
4639d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else if (status & MCI_TXUNDERRUN) {
4640a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: TX underrun\n", mmc_hostname(host->mmc));
4659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		data->error = -EIO;
4669d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else {
4670a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: Unknown error (0x%.8x)\n",
4680a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		       mmc_hostname(host->mmc), status);
4699d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		data->error = -EIO;
4709d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
4719d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
4729d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4739d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4749d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int
4759d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_pio_read(struct msmsdcc_host *host, char *buffer, unsigned int remain)
4769d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
4779d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	uint32_t	*ptr = (uint32_t *) buffer;
4789d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	int		count = 0;
4799d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4808b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	while (msmsdcc_readl(host, MMCISTATUS) & MCI_RXDATAAVLBL) {
4818b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		*ptr = msmsdcc_readl(host, MMCIFIFO + (count % MCI_FIFOSIZE));
4829d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		ptr++;
4839d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		count += sizeof(uint32_t);
4849d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4859d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		remain -=  sizeof(uint32_t);
4869d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (remain == 0)
4879d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			break;
4889d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
4899d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return count;
4909d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
4919d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4929d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int
4939d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_pio_write(struct msmsdcc_host *host, char *buffer,
4949d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		  unsigned int remain, u32 status)
4959d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
4969d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	void __iomem *base = host->base;
4979d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	char *ptr = buffer;
4989d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4999d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	do {
5009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		unsigned int count, maxcnt;
5019d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		maxcnt = status & MCI_TXFIFOEMPTY ? MCI_FIFOSIZE :
5039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat						    MCI_FIFOHALFSIZE;
5049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		count = min(remain, maxcnt);
5059d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5069d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		writesl(base + MMCIFIFO, ptr, count >> 2);
5079d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		ptr += count;
5089d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		remain -= count;
5099d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (remain == 0)
5119d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			break;
5129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5138b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		status = msmsdcc_readl(host, MMCISTATUS);
5149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} while (status & MCI_TXFIFOHALFEMPTY);
5159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5169d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return ptr - buffer;
5179d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
5189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int
5209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_spin_on_status(struct msmsdcc_host *host, uint32_t mask, int maxspin)
5219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
5229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	while (maxspin) {
5238b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		if ((msmsdcc_readl(host, MMCISTATUS) & mask))
5249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			return 0;
5259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		udelay(1);
5269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		--maxspin;
5279d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
5289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return -ETIMEDOUT;
5299d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
5309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int
5329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_pio_irq(int irq, void *dev_id)
5339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
5349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host	*host = dev_id;
5359d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	uint32_t		status;
5369d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5378b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	status = msmsdcc_readl(host, MMCISTATUS);
5389d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	do {
5409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		unsigned long flags;
5419d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		unsigned int remain, len;
5429d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		char *buffer;
5439d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (!(status & (MCI_TXFIFOHALFEMPTY | MCI_RXDATAAVLBL))) {
5459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			if (host->curr.xfer_remain == 0 || !msmsdcc_piopoll)
5469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				break;
5479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			if (msmsdcc_spin_on_status(host,
5499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat						   (MCI_TXFIFOHALFEMPTY |
5509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat						   MCI_RXDATAAVLBL),
5519d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat						   PIO_SPINMAX)) {
5529d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				break;
5539d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			}
5549d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		}
5559d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5569d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		/* Map the current scatter buffer */
5579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		local_irq_save(flags);
5589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		buffer = kmap_atomic(sg_page(host->pio.sg),
5599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				     KM_BIO_SRC_IRQ) + host->pio.sg->offset;
5609d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		buffer += host->pio.sg_off;
5619d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		remain = host->pio.sg->length - host->pio.sg_off;
5629d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		len = 0;
5639d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (status & MCI_RXACTIVE)
5649d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			len = msmsdcc_pio_read(host, buffer, remain);
5659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (status & MCI_TXACTIVE)
5669d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			len = msmsdcc_pio_write(host, buffer, remain, status);
5679d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5689d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		/* Unmap the buffer */
5699d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
5709d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		local_irq_restore(flags);
5719d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5729d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->pio.sg_off += len;
5739d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->curr.xfer_remain -= len;
5749d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->curr.data_xfered += len;
5759d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		remain -= len;
5769d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5779d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (remain == 0) {
5789d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			/* This sg page is full - do some housekeeping */
5799d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			if (status & MCI_RXACTIVE && host->curr.user_pages)
5809d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				flush_dcache_page(sg_page(host->pio.sg));
5819d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5829d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			if (!--host->pio.sg_len) {
5839d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				memset(&host->pio, 0, sizeof(host->pio));
5849d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				break;
5859d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			}
5869d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5879d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			/* Advance to next sg */
5889d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			host->pio.sg++;
5899d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			host->pio.sg_off = 0;
5909d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		}
5919d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5928b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		status = msmsdcc_readl(host, MMCISTATUS);
5939d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} while (1);
5949d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5959d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (status & MCI_RXACTIVE && host->curr.xfer_remain < MCI_FIFOSIZE)
5968b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		msmsdcc_writel(host, MCI_RXDATAAVLBLMASK, MMCIMASK1);
5979d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5989d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!host->curr.xfer_remain)
5998b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		msmsdcc_writel(host, 0, MMCIMASK1);
6009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
6019d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return IRQ_HANDLED;
6029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
6039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
6049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void msmsdcc_do_cmdirq(struct msmsdcc_host *host, uint32_t status)
6059d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
6069d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct mmc_command *cmd = host->curr.cmd;
6079d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
6089d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.cmd = NULL;
6098b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	cmd->resp[0] = msmsdcc_readl(host, MMCIRESPONSE0);
6108b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	cmd->resp[1] = msmsdcc_readl(host, MMCIRESPONSE1);
6118b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	cmd->resp[2] = msmsdcc_readl(host, MMCIRESPONSE2);
6128b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	cmd->resp[3] = msmsdcc_readl(host, MMCIRESPONSE3);
6139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
6149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	del_timer(&host->command_timer);
6159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (status & MCI_CMDTIMEOUT) {
6169d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		cmd->error = -ETIMEDOUT;
6179d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else if (status & MCI_CMDCRCFAIL &&
6189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		   cmd->flags & MMC_RSP_CRC) {
6190a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: Command CRC error\n", mmc_hostname(host->mmc));
6209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		cmd->error = -EILSEQ;
6219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
6229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
6239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!cmd->data || cmd->error) {
6249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (host->curr.data && host->dma.sg)
6259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			msm_dmov_stop_cmd(host->dma.channel,
6269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat					  &host->dma.hdr, 0);
6279d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		else if (host->curr.data) { /* Non DMA */
6289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			msmsdcc_stop_data(host);
6299d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			msmsdcc_request_end(host, cmd->mrq);
6309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		} else /* host->data == NULL */
6319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			msmsdcc_request_end(host, cmd->mrq);
6329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else if (!(cmd->data->flags & MMC_DATA_READ))
6339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		msmsdcc_start_data(host, cmd->data);
6349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
6359d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
636b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perchesstatic void
637b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perchesmsmsdcc_handle_irq_data(struct msmsdcc_host *host, u32 status,
638b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches			void __iomem *base)
639b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches{
640b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	struct mmc_data *data = host->curr.data;
641b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches
642b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	if (!data)
643b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		return;
644b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches
645b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	/* Check for data errors */
646b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	if (status & (MCI_DATACRCFAIL | MCI_DATATIMEOUT |
647b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		      MCI_TXUNDERRUN | MCI_RXOVERRUN)) {
648b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		msmsdcc_data_err(host, data, status);
649b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		host->curr.data_xfered = 0;
650b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		if (host->dma.sg)
651b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches			msm_dmov_stop_cmd(host->dma.channel,
652b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches					  &host->dma.hdr, 0);
653b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		else {
654b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches			msmsdcc_stop_data(host);
655b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches			if (!data->stop)
656b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches				msmsdcc_request_end(host, data->mrq);
657b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches			else
658b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches				msmsdcc_start_command(host, data->stop, 0);
659b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		}
660b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	}
661b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches
662b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	/* Check for data done */
663b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	if (!host->curr.got_dataend && (status & MCI_DATAEND))
664b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		host->curr.got_dataend = 1;
665b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches
666b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	if (!host->curr.got_datablkend && (status & MCI_DATABLOCKEND))
667b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		host->curr.got_datablkend = 1;
668b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches
669b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	/*
670b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	 * If DMA is still in progress, we complete via the completion handler
671b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	 */
672b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	if (host->curr.got_dataend && host->curr.got_datablkend &&
673b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	    !host->dma.busy) {
674b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		/*
675b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		 * There appears to be an issue in the controller where
676b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		 * if you request a small block transfer (< fifo size),
677b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		 * you may get your DATAEND/DATABLKEND irq without the
678b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		 * PIO data irq.
679b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		 *
680b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		 * Check to see if there is still data to be read,
681b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		 * and simulate a PIO irq.
682b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		 */
683b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		if (readl(base + MMCISTATUS) & MCI_RXDATAAVLBL)
684b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches			msmsdcc_pio_irq(1, host);
685b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches
686b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		msmsdcc_stop_data(host);
687b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		if (!data->error)
688b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches			host->curr.data_xfered = host->curr.xfer_size;
689b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches
690b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		if (!data->stop)
691b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches			msmsdcc_request_end(host, data->mrq);
692b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		else
693b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches			msmsdcc_start_command(host, data->stop, 0);
694b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	}
695b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches}
696b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches
6979d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic irqreturn_t
6989d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_irq(int irq, void *dev_id)
6999d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
7009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host	*host = dev_id;
7019d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	void __iomem		*base = host->base;
7029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	u32			status;
7039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	int			ret = 0;
7049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	int			cardint = 0;
7059d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7069d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_lock(&host->lock);
7079d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7089d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	do {
7098b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		struct mmc_data *data;
7108b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		status = msmsdcc_readl(host, MMCISTATUS);
7118b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		status &= (msmsdcc_readl(host, MMCIMASK0) |
7128b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat					      MCI_DATABLOCKENDMASK);
7138b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		msmsdcc_writel(host, status, MMCICLEAR);
7149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
715865c8064a2fb07100525097983966b8e789bde1aSan Mehat		if (status & MCI_SDIOINTR)
716865c8064a2fb07100525097983966b8e789bde1aSan Mehat			status &= ~MCI_SDIOINTR;
717865c8064a2fb07100525097983966b8e789bde1aSan Mehat
718865c8064a2fb07100525097983966b8e789bde1aSan Mehat		if (!status)
719865c8064a2fb07100525097983966b8e789bde1aSan Mehat			break;
720865c8064a2fb07100525097983966b8e789bde1aSan Mehat
721b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		msmsdcc_handle_irq_data(host, status, base);
7229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (status & (MCI_CMDSENT | MCI_CMDRESPEND | MCI_CMDCRCFAIL |
7249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			      MCI_CMDTIMEOUT) && host->curr.cmd) {
7259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			msmsdcc_do_cmdirq(host, status);
7269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		}
7279d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (status & MCI_SDIOINTOPER) {
7299d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			cardint = 1;
7309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			status &= ~MCI_SDIOINTOPER;
7319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		}
7329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		ret = 1;
7339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} while (status);
7349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7359d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_unlock(&host->lock);
7369d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7379d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	/*
7389d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 * We have to delay handling the card interrupt as it calls
7399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 * back into the driver.
7409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 */
7419d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (cardint)
7429d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		mmc_signal_sdio_irq(host->mmc);
7439d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return IRQ_RETVAL(ret);
7459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
7469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
7489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_request(struct mmc_host *mmc, struct mmc_request *mrq)
7499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
7509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host *host = mmc_priv(mmc);
7519d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	unsigned long flags;
7529d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7539d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	WARN_ON(host->curr.mrq != NULL);
7549d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	WARN_ON(host->pwr == 0);
7559d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7569d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_lock_irqsave(&host->lock, flags);
7579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->stats.reqs++;
7599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7609d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->eject) {
7619d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (mrq->data && !(mrq->data->flags & MMC_DATA_READ)) {
7629d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			mrq->cmd->error = 0;
7639d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			mrq->data->bytes_xfered = mrq->data->blksz *
7649d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat						  mrq->data->blocks;
7659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		} else
7669d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			mrq->cmd->error = -ENOMEDIUM;
7679d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7689d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		spin_unlock_irqrestore(&host->lock, flags);
7699d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		mmc_request_done(mmc, mrq);
7709d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return;
7719d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
7729d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7739d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.mrq = mrq;
774865c8064a2fb07100525097983966b8e789bde1aSan Mehat	if (!host->clks_on)
775865c8064a2fb07100525097983966b8e789bde1aSan Mehat		msmsdcc_enable_clocks(host, 1);
7769d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7779d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (mrq->data && mrq->data->flags & MMC_DATA_READ)
7789d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		msmsdcc_start_data(host, mrq->data);
7799d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7809d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	msmsdcc_start_command(host, mrq->cmd, 0);
7819d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7829d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->cmdpoll && !msmsdcc_spin_on_status(host,
7839d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				MCI_CMDRESPEND|MCI_CMDCRCFAIL|MCI_CMDTIMEOUT,
7849d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				CMD_SPINMAX)) {
7858b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		uint32_t status = msmsdcc_readl(host, MMCISTATUS);
7869d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		msmsdcc_do_cmdirq(host, status);
7878b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		msmsdcc_writel(host,
7888b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat			       MCI_CMDRESPEND | MCI_CMDCRCFAIL | MCI_CMDTIMEOUT,
7898b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat			       MMCICLEAR);
7909d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->stats.cmdpoll_hits++;
7919d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else {
7929d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->stats.cmdpoll_misses++;
7939d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		mod_timer(&host->command_timer, jiffies + HZ);
7949d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
7959d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_unlock_irqrestore(&host->lock, flags);
7969d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
7979d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7989d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
7999d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
8009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
8019d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host *host = mmc_priv(mmc);
8029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	u32 clk = 0, pwr = 0;
8039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	int rc;
8044adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat	unsigned long flags;
8059d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8064adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat	spin_lock_irqsave(&host->lock, flags);
807865c8064a2fb07100525097983966b8e789bde1aSan Mehat	if (!host->clks_on)
808865c8064a2fb07100525097983966b8e789bde1aSan Mehat		msmsdcc_enable_clocks(host, 1);
8099d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
810865c8064a2fb07100525097983966b8e789bde1aSan Mehat	if (ios->clock) {
8119d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (ios->clock != host->clk_rate) {
8129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			rc = clk_set_rate(host->clk, ios->clock);
8139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			if (rc < 0)
8140a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches				pr_err("%s: Error setting clock rate (%d)\n",
8150a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches				       mmc_hostname(host->mmc), rc);
8169d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			else
8179d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				host->clk_rate = ios->clock;
8189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		}
8199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		clk |= MCI_CLK_ENABLE;
8209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
8219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (ios->bus_width == MMC_BUS_WIDTH_4)
8239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		clk |= (2 << 10); /* Set WIDEBUS */
8249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (ios->clock > 400000 && msmsdcc_pwrsave)
8269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		clk |= (1 << 9); /* PWRSAVE */
8279d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	clk |= (1 << 12); /* FLOW_ENA */
8299d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	clk |= (1 << 15); /* feedback clock */
8309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->plat->translate_vdd)
8329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		pwr |= host->plat->translate_vdd(mmc_dev(mmc), ios->vdd);
8339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	switch (ios->power_mode) {
8359d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	case MMC_POWER_OFF:
8369d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		break;
8379d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	case MMC_POWER_UP:
8389d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		pwr |= MCI_PWR_UP;
8399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		break;
8409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	case MMC_POWER_ON:
8419d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		pwr |= MCI_PWR_ON;
8429d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		break;
8439d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
8449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
8469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		pwr |= MCI_OD;
8479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8488b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	msmsdcc_writel(host, clk, MMCICLOCK);
8499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->pwr != pwr) {
8519d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->pwr = pwr;
8528b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		msmsdcc_writel(host, pwr, MMCIPOWER);
8539d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
854865c8064a2fb07100525097983966b8e789bde1aSan Mehat	if (host->clks_on)
8554adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat		msmsdcc_enable_clocks(host, 0);
8564adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat	spin_unlock_irqrestore(&host->lock, flags);
8579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
8589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void msmsdcc_enable_sdio_irq(struct mmc_host *mmc, int enable)
8609d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
8619d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host *host = mmc_priv(mmc);
8629d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	unsigned long flags;
8639d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	u32 status;
8649d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_lock_irqsave(&host->lock, flags);
8669d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (msmsdcc_sdioirq == 1) {
8678b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		status = msmsdcc_readl(host, MMCIMASK0);
8689d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (enable)
8699d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			status |= MCI_SDIOINTOPERMASK;
8709d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		else
8719d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			status &= ~MCI_SDIOINTOPERMASK;
8729d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->saved_irq0mask = status;
8738b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		msmsdcc_writel(host, status, MMCIMASK0);
8749d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
8759d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_unlock_irqrestore(&host->lock, flags);
8769d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
8779d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8789d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic const struct mmc_host_ops msmsdcc_ops = {
8799d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	.request	= msmsdcc_request,
8809d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	.set_ios	= msmsdcc_set_ios,
8819d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	.enable_sdio_irq = msmsdcc_enable_sdio_irq,
8829d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat};
8839d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8849d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
8859d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_check_status(unsigned long data)
8869d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
8879d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host *host = (struct msmsdcc_host *)data;
8889d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	unsigned int status;
8899d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8909d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!host->plat->status) {
8919d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		mmc_detect_change(host->mmc, 0);
8929d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		goto out;
8939d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
8949d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8959d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	status = host->plat->status(mmc_dev(host->mmc));
8969d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->eject = !status;
8979d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (status ^ host->oldstat) {
8980a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_info("%s: Slot status change detected (%d -> %d)\n",
8990a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches			mmc_hostname(host->mmc), host->oldstat, status);
9009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (status)
9019d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			mmc_detect_change(host->mmc, (5 * HZ) / 2);
9029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		else
9039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			mmc_detect_change(host->mmc, 0);
9049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
9059d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9069d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->oldstat = status;
9079d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9089d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatout:
9099d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->timer.function)
9109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		mod_timer(&host->timer, jiffies + HZ);
9119d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
9129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic irqreturn_t
9149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_platform_status_irq(int irq, void *dev_id)
9159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
9169d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host *host = dev_id;
9179d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	printk(KERN_DEBUG "%s: %d\n", __func__, irq);
9199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	msmsdcc_check_status((unsigned long) host);
9209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return IRQ_HANDLED;
9219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
9229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
9249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_status_notify_cb(int card_present, void *dev_id)
9259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
9269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host *host = dev_id;
9279d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	printk(KERN_DEBUG "%s: card_present %d\n", mmc_hostname(host->mmc),
9299d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	       card_present);
9309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	msmsdcc_check_status((unsigned long) host);
9319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
9329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
933865c8064a2fb07100525097983966b8e789bde1aSan Mehatstatic void
934865c8064a2fb07100525097983966b8e789bde1aSan Mehatmsmsdcc_busclk_expired(unsigned long _data)
935865c8064a2fb07100525097983966b8e789bde1aSan Mehat{
936865c8064a2fb07100525097983966b8e789bde1aSan Mehat	struct msmsdcc_host	*host = (struct msmsdcc_host *) _data;
937865c8064a2fb07100525097983966b8e789bde1aSan Mehat	unsigned long 		flags;
938865c8064a2fb07100525097983966b8e789bde1aSan Mehat
939865c8064a2fb07100525097983966b8e789bde1aSan Mehat	spin_lock_irqsave(&host->lock, flags);
940865c8064a2fb07100525097983966b8e789bde1aSan Mehat	if (host->clks_on)
941865c8064a2fb07100525097983966b8e789bde1aSan Mehat		msmsdcc_enable_clocks(host, 0);
942865c8064a2fb07100525097983966b8e789bde1aSan Mehat
943865c8064a2fb07100525097983966b8e789bde1aSan Mehat	spin_unlock_irqrestore(&host->lock, flags);
944865c8064a2fb07100525097983966b8e789bde1aSan Mehat}
945865c8064a2fb07100525097983966b8e789bde1aSan Mehat
9469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat/*
9479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat * called when a command expires.
9489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat * Dump some debugging, and then error
9499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat * out the transaction.
9509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat */
9519d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
9529d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_command_expired(unsigned long _data)
9539d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
9549d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host	*host = (struct msmsdcc_host *) _data;
9559d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct mmc_request	*mrq;
9569d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	unsigned long		flags;
9579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_lock_irqsave(&host->lock, flags);
9599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mrq = host->curr.mrq;
9609d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9619d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!mrq) {
9629d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		spin_unlock_irqrestore(&host->lock, flags);
9639d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return;
9649d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
9659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9668b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	pr_err("%s: Controller lockup detected\n",
9678b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	       mmc_hostname(host->mmc));
9689d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mrq->cmd->error = -ETIMEDOUT;
9699d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	msmsdcc_stop_data(host);
9709d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9718b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	msmsdcc_writel(host, 0, MMCICOMMAND);
9729d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9739d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.mrq = NULL;
9749d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.cmd = NULL;
9759d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
976865c8064a2fb07100525097983966b8e789bde1aSan Mehat	if (host->clks_on)
977865c8064a2fb07100525097983966b8e789bde1aSan Mehat		msmsdcc_enable_clocks(host, 0);
9789d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_unlock_irqrestore(&host->lock, flags);
9799d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc_request_done(host->mmc, mrq);
9809d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
9819d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9829d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int
9839d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_init_dma(struct msmsdcc_host *host)
9849d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
9859d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	memset(&host->dma, 0, sizeof(struct msmsdcc_dma_data));
9869d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.host = host;
9879d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.channel = -1;
9889d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9899d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!host->dmares)
9909d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return -ENODEV;
9919d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9929d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.nc = dma_alloc_coherent(NULL,
9939d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat					  sizeof(struct msmsdcc_nc_dmadata),
9949d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat					  &host->dma.nc_busaddr,
9959d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat					  GFP_KERNEL);
9969d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->dma.nc == NULL) {
9970a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("Unable to allocate DMA buffer\n");
9989d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return -ENOMEM;
9999d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
10009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	memset(host->dma.nc, 0x00, sizeof(struct msmsdcc_nc_dmadata));
10019d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.cmd_busaddr = host->dma.nc_busaddr;
10029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.cmdptr_busaddr = host->dma.nc_busaddr +
10039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				offsetof(struct msmsdcc_nc_dmadata, cmdptr);
10049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.channel = host->dmares->start;
10059d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10069d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return 0;
10079d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
10089d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10099d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#ifdef CONFIG_MMC_MSM7X00A_RESUME_IN_WQ
10109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
10119d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatdo_resume_work(struct work_struct *work)
10129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
10139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host *host =
10149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		container_of(work, struct msmsdcc_host, resume_task);
10159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct mmc_host	*mmc = host->mmc;
10169d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10179d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (mmc) {
10189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		mmc_resume_host(mmc);
10199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (host->stat_irq)
10209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			enable_irq(host->stat_irq);
10219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
10229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
10239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#endif
10249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int
10269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_probe(struct platform_device *pdev)
10279d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
10289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct mmc_platform_data *plat = pdev->dev.platform_data;
10299d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host *host;
10309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct mmc_host *mmc;
10319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct resource *cmd_irqres = NULL;
10329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct resource *pio_irqres = NULL;
10339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct resource *stat_irqres = NULL;
10349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct resource *memres = NULL;
10359d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct resource *dmares = NULL;
10369d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	int ret;
10379d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10389d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	/* must have platform data */
10399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!plat) {
10400a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: Platform data not available\n", __func__);
10419d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		ret = -EINVAL;
10429d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		goto out;
10439d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
10449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (pdev->id < 1 || pdev->id > 4)
10469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return -EINVAL;
10479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (pdev->resource == NULL || pdev->num_resources < 2) {
10490a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: Invalid resource\n", __func__);
10509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return -ENXIO;
10519d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
10529d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10539d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	memres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
10549d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
10559d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	cmd_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
10569d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat						  "cmd_irq");
10579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	pio_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
10589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat						  "pio_irq");
10599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	stat_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
10609d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat						   "status_irq");
10619d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10629d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!cmd_irqres || !pio_irqres || !memres) {
10630a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: Invalid resource\n", __func__);
10649d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return -ENXIO;
10659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
10669d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10679d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	/*
10689d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 * Setup our host structure
10699d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 */
10709d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10719d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc = mmc_alloc_host(sizeof(struct msmsdcc_host), &pdev->dev);
10729d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!mmc) {
10739d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		ret = -ENOMEM;
10749d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		goto out;
10759d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
10769d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10779d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host = mmc_priv(mmc);
10789d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->pdev_id = pdev->id;
10799d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->plat = plat;
10809d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->mmc = mmc;
10819d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10829d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->cmdpoll = 1;
10839d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1084865c8064a2fb07100525097983966b8e789bde1aSan Mehat	host->use_bustimer = 1;
1085865c8064a2fb07100525097983966b8e789bde1aSan Mehat
10869d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->base = ioremap(memres->start, PAGE_SIZE);
10879d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!host->base) {
10889d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		ret = -ENOMEM;
10899d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		goto out;
10909d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
10919d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10929d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->cmd_irqres = cmd_irqres;
10939d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->pio_irqres = pio_irqres;
10949d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->memres = memres;
10959d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dmares = dmares;
10969d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_lock_init(&host->lock);
10979d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10989d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	/*
10999d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 * Setup DMA
11009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 */
11019d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	msmsdcc_init_dma(host);
11029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11034adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat	/* Get our clocks */
11049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->pclk = clk_get(&pdev->dev, "sdc_pclk");
11059d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (IS_ERR(host->pclk)) {
11069d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		ret = PTR_ERR(host->pclk);
11079d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		goto host_free;
11089d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
11099d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->clk = clk_get(&pdev->dev, "sdc_clk");
11119d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (IS_ERR(host->clk)) {
11129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		ret = PTR_ERR(host->clk);
11134adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat		goto pclk_put;
11149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
11159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11164adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat	/* Enable clocks */
11174adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat	ret = msmsdcc_enable_clocks(host, 1);
11189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (ret)
11199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		goto clk_put;
11209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	ret = clk_set_rate(host->clk, msmsdcc_fmin);
11229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (ret) {
11230a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: Clock rate set failed (%d)\n", __func__, ret);
11249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		goto clk_disable;
11259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
11269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11274adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat	host->pclk_rate = clk_get_rate(host->pclk);
11289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->clk_rate = clk_get_rate(host->clk);
11299d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	/*
11319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 * Setup MMC host structure
11329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 */
11339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->ops = &msmsdcc_ops;
11349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->f_min = msmsdcc_fmin;
11359d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->f_max = msmsdcc_fmax;
11369d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->ocr_avail = plat->ocr_mask;
11379d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11389d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (msmsdcc_4bit)
11399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		mmc->caps |= MMC_CAP_4_BIT_DATA;
11409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (msmsdcc_sdioirq)
11419d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		mmc->caps |= MMC_CAP_SDIO_IRQ;
11429d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
11439d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->max_phys_segs = NR_SG;
11459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->max_hw_segs = NR_SG;
11469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->max_blk_size = 4096;	/* MCI_DATA_CTL BLOCKSIZE up to 4096 */
11479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->max_blk_count = 65536;
11489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->max_req_size = 33554432;	/* MCI_DATA_LENGTH is 25 bits */
11509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->max_seg_size = mmc->max_req_size;
11519d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11528b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	msmsdcc_writel(host, 0, MMCIMASK0);
11538b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	msmsdcc_writel(host, 0x5e007ff, MMCICLEAR);
11549d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11558b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	msmsdcc_writel(host, MCI_IRQENABLE, MMCIMASK0);
11569d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->saved_irq0mask = MCI_IRQENABLE;
11579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	/*
11599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 * Setup card detect change
11609d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 */
11619d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11629d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	memset(&host->timer, 0, sizeof(host->timer));
11639d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11649d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (stat_irqres && !(stat_irqres->flags & IORESOURCE_DISABLED)) {
11659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		unsigned long irqflags = IRQF_SHARED |
11669d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			(stat_irqres->flags & IRQF_TRIGGER_MASK);
11679d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11689d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->stat_irq = stat_irqres->start;
11699d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		ret = request_irq(host->stat_irq,
11709d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				  msmsdcc_platform_status_irq,
11719d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				  irqflags,
11729d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				  DRIVER_NAME " (slot)",
11739d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				  host);
11749d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (ret) {
11750a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches			pr_err("%s: Unable to get slot IRQ %d (%d)\n",
11760a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches			       mmc_hostname(mmc), host->stat_irq, ret);
11779d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			goto clk_disable;
11789d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		}
11799d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else if (plat->register_status_notify) {
11809d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		plat->register_status_notify(msmsdcc_status_notify_cb, host);
11819d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else if (!plat->status)
11820a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: No card detect facilities available\n",
11839d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		       mmc_hostname(mmc));
11849d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	else {
11859d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		init_timer(&host->timer);
11869d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->timer.data = (unsigned long)host;
11879d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->timer.function = msmsdcc_check_status;
11889d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->timer.expires = jiffies + HZ;
11899d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		add_timer(&host->timer);
11909d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
11919d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11929d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (plat->status) {
11939d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->oldstat = host->plat->status(mmc_dev(host->mmc));
11949d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->eject = !host->oldstat;
11959d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
11969d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11979d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	/*
11989d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 * Setup a command timer. We currently need this due to
11999d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 * some 'strange' timeout / error handling situations.
12009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 */
12019d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	init_timer(&host->command_timer);
12029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->command_timer.data = (unsigned long) host;
12039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->command_timer.function = msmsdcc_command_expired;
12049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1205865c8064a2fb07100525097983966b8e789bde1aSan Mehat	init_timer(&host->busclk_timer);
1206865c8064a2fb07100525097983966b8e789bde1aSan Mehat	host->busclk_timer.data = (unsigned long) host;
1207865c8064a2fb07100525097983966b8e789bde1aSan Mehat	host->busclk_timer.function = msmsdcc_busclk_expired;
1208865c8064a2fb07100525097983966b8e789bde1aSan Mehat
12099d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	ret = request_irq(cmd_irqres->start, msmsdcc_irq, IRQF_SHARED,
12109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			  DRIVER_NAME " (cmd)", host);
12119d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (ret)
12129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		goto stat_irq_free;
12139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	ret = request_irq(pio_irqres->start, msmsdcc_pio_irq, IRQF_SHARED,
12159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			  DRIVER_NAME " (pio)", host);
12169d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (ret)
12179d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		goto cmd_irq_free;
12189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc_set_drvdata(pdev, mmc);
12209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc_add_host(mmc);
12219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12220a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches	pr_info("%s: Qualcomm MSM SDCC at 0x%016llx irq %d,%d dma %d\n",
12230a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		mmc_hostname(mmc), (unsigned long long)memres->start,
12240a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		(unsigned int) cmd_irqres->start,
12250a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		(unsigned int) host->stat_irq, host->dma.channel);
12260a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches	pr_info("%s: 4 bit data mode %s\n", mmc_hostname(mmc),
12270a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		(mmc->caps & MMC_CAP_4_BIT_DATA ? "enabled" : "disabled"));
12280a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches	pr_info("%s: MMC clock %u -> %u Hz, PCLK %u Hz\n",
12290a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		mmc_hostname(mmc), msmsdcc_fmin, msmsdcc_fmax, host->pclk_rate);
12300a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches	pr_info("%s: Slot eject status = %d\n", mmc_hostname(mmc), host->eject);
12310a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches	pr_info("%s: Power save feature enable = %d\n",
12320a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		mmc_hostname(mmc), msmsdcc_pwrsave);
12339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->dma.channel != -1) {
12350a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_info("%s: DM non-cached buffer at %p, dma_addr 0x%.8x\n",
12360a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches			mmc_hostname(mmc), host->dma.nc, host->dma.nc_busaddr);
12370a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_info("%s: DM cmd busaddr 0x%.8x, cmdptr busaddr 0x%.8x\n",
12380a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches			mmc_hostname(mmc), host->dma.cmd_busaddr,
12390a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches			host->dma.cmdptr_busaddr);
12409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else
12410a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_info("%s: PIO transfer enabled\n", mmc_hostname(mmc));
12429d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->timer.function)
12430a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_info("%s: Polling status mode enabled\n", mmc_hostname(mmc));
12449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return 0;
12469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat cmd_irq_free:
12479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	free_irq(cmd_irqres->start, host);
12489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat stat_irq_free:
12499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->stat_irq)
12509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		free_irq(host->stat_irq, host);
12519d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat clk_disable:
12524adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat	msmsdcc_enable_clocks(host, 0);
12539d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat clk_put:
12549d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	clk_put(host->clk);
12559d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat pclk_put:
12569d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	clk_put(host->pclk);
12579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat host_free:
12589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc_free_host(mmc);
12599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat out:
12609d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return ret;
12619d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
12629d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12639d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int
12649d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_suspend(struct platform_device *dev, pm_message_t state)
12659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
12669d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct mmc_host *mmc = mmc_get_drvdata(dev);
12679d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	int rc = 0;
12689d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12699d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (mmc) {
12709d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		struct msmsdcc_host *host = mmc_priv(mmc);
12719d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12729d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (host->stat_irq)
12739d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			disable_irq(host->stat_irq);
12749d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12759d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (mmc->card && mmc->card->type != MMC_TYPE_SDIO)
12769d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			rc = mmc_suspend_host(mmc, state);
12779d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (!rc) {
12788b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat			msmsdcc_writel(host, 0, MMCIMASK0);
12799d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12804adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat			if (host->clks_on)
12814adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat				msmsdcc_enable_clocks(host, 0);
12829d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		}
12839d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
12849d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return rc;
12859d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
12869d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12879d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int
12889d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_resume(struct platform_device *dev)
12899d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
12909d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct mmc_host *mmc = mmc_get_drvdata(dev);
12919d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	unsigned long flags;
12929d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12939d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (mmc) {
12949d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		struct msmsdcc_host *host = mmc_priv(mmc);
12959d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12969d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		spin_lock_irqsave(&host->lock, flags);
12979d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12984adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat		if (!host->clks_on)
12994adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat			msmsdcc_enable_clocks(host, 1);
13009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
13018b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		msmsdcc_writel(host, host->saved_irq0mask, MMCIMASK0);
13029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
13039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		spin_unlock_irqrestore(&host->lock, flags);
13049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
13059d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (mmc->card && mmc->card->type != MMC_TYPE_SDIO)
13069d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			mmc_resume_host(mmc);
13075b8a2fb34f5670b1f07483bfa40de9ce539dbdb2Roel Kluin		if (host->stat_irq)
13089d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			enable_irq(host->stat_irq);
13099d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
13109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return 0;
13119d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
13129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
13139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic struct platform_driver msmsdcc_driver = {
13149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	.probe		= msmsdcc_probe,
13159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	.suspend	= msmsdcc_suspend,
13169d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	.resume		= msmsdcc_resume,
13179d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	.driver		= {
13189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		.name	= "msm_sdcc",
13199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	},
13209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat};
13219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
13229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int __init msmsdcc_init(void)
13239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
13249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return platform_driver_register(&msmsdcc_driver);
13259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
13269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
13279d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void __exit msmsdcc_exit(void)
13289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
13299d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	platform_driver_unregister(&msmsdcc_driver);
13309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
13319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
13329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmodule_init(msmsdcc_init);
13339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmodule_exit(msmsdcc_exit);
13349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
13359d2bd7383c71d38c60328a3dc8a946eda2013826San MehatMODULE_DESCRIPTION("Qualcomm MSM 7X00A Multimedia Card Interface driver");
13369d2bd7383c71d38c60328a3dc8a946eda2013826San MehatMODULE_LICENSE("GPL");
1337