msm_sdcc.c revision c7fc9370df1433486dfa9460a833fae664e8be6c
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.
656a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat *  Copyright (C) 2009, Code Aurora Forum. All Rights Reserved.
79d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat *
89d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat * This program is free software; you can redistribute it and/or modify
99d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat * it under the terms of the GNU General Public License version 2 as
109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat * published by the Free Software Foundation.
119d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat *
129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat * Based on mmci.c
139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat *
149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat * Author: San Mehat (san@android.com)
159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat *
169d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat */
179d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/module.h>
199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/moduleparam.h>
209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/init.h>
219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/ioport.h>
229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/device.h>
239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/interrupt.h>
249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/delay.h>
259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/err.h>
269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/highmem.h>
279d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/log2.h>
289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/mmc/host.h>
299d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/mmc/card.h>
30b3fa579118b239e218e690f5ef76870aff6fe738San Mehat#include <linux/mmc/sdio.h>
319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/clk.h>
329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/scatterlist.h>
339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/platform_device.h>
349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/dma-mapping.h>
359d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/debugfs.h>
369d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/io.h>
379d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <linux/memory.h>
389d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <asm/cacheflush.h>
409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <asm/div64.h>
419d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <asm/sizes.h>
429d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
433989d17847071fa94c93299805a9cca27cf65d26Pavel Machek#include <mach/mmc.h>
449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <mach/msm_iomap.h>
459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include <mach/dma.h>
469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#include "msm_sdcc.h"
489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#define DRIVER_NAME "msm-sdcc"
509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
51c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat#define BUSCLK_TIMEOUT (HZ)
529d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic unsigned int msmsdcc_fmin = 144000;
539d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic unsigned int msmsdcc_fmax = 50000000;
549d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic unsigned int msmsdcc_4bit = 1;
559d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic unsigned int msmsdcc_pwrsave = 1;
569d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic unsigned int msmsdcc_piopoll = 1;
579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic unsigned int msmsdcc_sdioirq;
589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#define PIO_SPINMAX 30
609d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#define CMD_SPINMAX 20
619d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
62865c8064a2fb07100525097983966b8e789bde1aSan Mehat
63c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehatstatic inline void
64c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehatmsmsdcc_disable_clocks(struct msmsdcc_host *host, int deferr)
65865c8064a2fb07100525097983966b8e789bde1aSan Mehat{
66c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat	WARN_ON(!host->clks_on);
678b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat
68c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat	if (deferr) {
69c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat		mod_timer(&host->busclk_timer, jiffies + BUSCLK_TIMEOUT);
70865c8064a2fb07100525097983966b8e789bde1aSan Mehat	} else {
71c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat		del_timer_sync(&host->busclk_timer);
72c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat//		dev_info(mmc_dev(host->mmc), "Immediate clock shutdown\n");
73865c8064a2fb07100525097983966b8e789bde1aSan Mehat		clk_disable(host->clk);
74865c8064a2fb07100525097983966b8e789bde1aSan Mehat		clk_disable(host->pclk);
75865c8064a2fb07100525097983966b8e789bde1aSan Mehat		host->clks_on = 0;
76865c8064a2fb07100525097983966b8e789bde1aSan Mehat	}
77c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat}
78c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat
79c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehatstatic inline int
80c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehatmsmsdcc_enable_clocks(struct msmsdcc_host *host)
81c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat{
82c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat	int rc;
83c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat
84c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat	WARN_ON(host->clks_on);
85c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat
86c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat	del_timer_sync(&host->busclk_timer);
87c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat
88c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat	rc = clk_enable(host->pclk);
89c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat	if (rc)
90c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat		return rc;
91c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat	rc = clk_enable(host->clk);
92c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat	if (rc) {
93c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat		clk_disable(host->pclk);
94c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat		return rc;
95c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat	}
96c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat	udelay(1 + ((3 * USEC_PER_SEC) /
97c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat	       (host->clk_rate ? host->clk_rate : msmsdcc_fmin)));
98c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat	host->clks_on = 1;
99865c8064a2fb07100525097983966b8e789bde1aSan Mehat	return 0;
100865c8064a2fb07100525097983966b8e789bde1aSan Mehat}
101865c8064a2fb07100525097983966b8e789bde1aSan Mehat
1028b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehatstatic inline unsigned int
1038b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehatmsmsdcc_readl(struct msmsdcc_host *host, unsigned int reg)
1048b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat{
1058b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	return readl(host->base + reg);
1068b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat}
1078b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat
1088b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehatstatic inline void
1098b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehatmsmsdcc_writel(struct msmsdcc_host *host, u32 data, unsigned int reg)
1108b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat{
1118b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	writel(data, host->base + reg);
1128b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	/* 3 clk delay required! */
1138b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	udelay(1 + ((3 * USEC_PER_SEC) /
1148b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	       (host->clk_rate ? host->clk_rate : msmsdcc_fmin)));
1158b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat}
116865c8064a2fb07100525097983966b8e789bde1aSan Mehat
1179d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
1189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_start_command(struct msmsdcc_host *host, struct mmc_command *cmd,
1199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		      u32 c);
1209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
1229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_request_end(struct msmsdcc_host *host, struct mmc_request *mrq)
1239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
1249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	BUG_ON(host->curr.data);
1259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.mrq = NULL;
1279d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.cmd = NULL;
1289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1299d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (mrq->data)
1309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		mrq->data->bytes_xfered = host->curr.data_xfered;
1319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (mrq->cmd->error == -ETIMEDOUT)
1329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		mdelay(5);
1339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
134c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat	msmsdcc_disable_clocks(host, 1);
1359d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	/*
1369d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 * Need to drop the host lock here; mmc_request_done may call
1379d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 * back into the driver...
1389d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 */
1399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_unlock(&host->lock);
1409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc_request_done(host->mmc, mrq);
1419d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_lock(&host->lock);
1429d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
1439d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
1459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_stop_data(struct msmsdcc_host *host)
1469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
1479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.data = NULL;
1489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.got_dataend = host->curr.got_datablkend = 0;
1499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
1509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1519d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatuint32_t msmsdcc_fifo_addr(struct msmsdcc_host *host)
1529d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
15375d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	switch (host->pdev_id) {
15475d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	case 1:
1559d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return MSM_SDC1_PHYS + MMCIFIFO;
15675d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	case 2:
1579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return MSM_SDC2_PHYS + MMCIFIFO;
15875d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	case 3:
1599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return MSM_SDC3_PHYS + MMCIFIFO;
16075d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	case 4:
1619d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return MSM_SDC4_PHYS + MMCIFIFO;
16275d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	}
16375d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	BUG();
1649d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return 0;
1659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
1669d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
16756a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehatstatic inline void
16856a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehatmsmsdcc_start_command_exec(struct msmsdcc_host *host, u32 arg, u32 c) {
16956a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat       msmsdcc_writel(host, arg, MMCIARGUMENT);
17056a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat       msmsdcc_writel(host, c, MMCICOMMAND);
17156a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat}
17256a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat
17356a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehatstatic void
17456a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehatmsmsdcc_dma_exec_func(struct msm_dmov_cmd *cmd)
17556a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat{
17656a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat       struct msmsdcc_host *host = (struct msmsdcc_host *)cmd->data;
17756a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat
17856a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat       writel(host->cmd_timeout, host->base + MMCIDATATIMER);
17956a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat       writel((unsigned int)host->curr.xfer_size, host->base + MMCIDATALENGTH);
18056a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat       writel(host->cmd_pio_irqmask, host->base + MMCIMASK1);
18156a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat       writel(host->cmd_datactrl, host->base + MMCIDATACTRL);
18256a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat
18356a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat       if (host->cmd_cmd) {
18456a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat               msmsdcc_start_command_exec(host,
18556a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat                       (u32)host->cmd_cmd->arg, (u32)host->cmd_c);
18656a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat       }
18756a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	host->dma.active = 1;
18856a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat}
18956a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat
1909d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
1919d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_dma_complete_func(struct msm_dmov_cmd *cmd,
1929d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			  unsigned int result,
1939d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			  struct msm_dmov_errdata *err)
1949d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
1959d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_dma_data	*dma_data =
1969d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		container_of(cmd, struct msmsdcc_dma_data, hdr);
1979d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host	*host = dma_data->host;
1989d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	unsigned long		flags;
1999d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct mmc_request	*mrq;
2009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2019d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_lock_irqsave(&host->lock, flags);
20256a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	host->dma.active = 0;
20356a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat
2049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mrq = host->curr.mrq;
2059d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	BUG_ON(!mrq);
2069d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2079d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!(result & DMOV_RSLT_VALID)) {
2080a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("msmsdcc: Invalid DataMover result\n");
2099d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		goto out;
2109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
2119d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (result & DMOV_RSLT_DONE) {
2139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->curr.data_xfered = host->curr.xfer_size;
2149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else {
2159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		/* Error or flush  */
2169d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (result & DMOV_RSLT_ERROR)
2170a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches			pr_err("%s: DMA error (0x%.8x)\n",
2189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			       mmc_hostname(host->mmc), result);
2199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (result & DMOV_RSLT_FLUSH)
2200a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches			pr_err("%s: DMA channel flushed (0x%.8x)\n",
2219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			       mmc_hostname(host->mmc), result);
2229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (err)
2230a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches			pr_err("Flush data: %.8x %.8x %.8x %.8x %.8x %.8x\n",
2249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			       err->flush[0], err->flush[1], err->flush[2],
2259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			       err->flush[3], err->flush[4], err->flush[5]);
2269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (!mrq->data->error)
2279d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			mrq->data->error = -EIO;
2289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
2299d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	dma_unmap_sg(mmc_dev(host->mmc), host->dma.sg, host->dma.num_ents,
2309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		     host->dma.dir);
2319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->curr.user_pages) {
2339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		struct scatterlist *sg = host->dma.sg;
2349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		int i;
2359d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
23675d145283b2e42619d7ee1e00b78466bacd51808Joe Perches		for (i = 0; i < host->dma.num_ents; i++)
23775d145283b2e42619d7ee1e00b78466bacd51808Joe Perches			flush_dcache_page(sg_page(sg++));
2389d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
2399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.sg = NULL;
24156a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	host->dma.busy = 0;
2429d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2439d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if ((host->curr.got_dataend && host->curr.got_datablkend)
2449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	     || mrq->data->error) {
2459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		/*
2479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		 * If we've already gotten our DATAEND / DATABLKEND
2489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		 * for this request, then complete it through here.
2499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		 */
2509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		msmsdcc_stop_data(host);
2519d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2529d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (!mrq->data->error)
2539d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			host->curr.data_xfered = host->curr.xfer_size;
2549d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (!mrq->data->stop || mrq->cmd->error) {
2559d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			host->curr.mrq = NULL;
2569d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			host->curr.cmd = NULL;
2579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			mrq->data->bytes_xfered = host->curr.data_xfered;
2589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			spin_unlock_irqrestore(&host->lock, flags);
260c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat			msmsdcc_disable_clocks(host, 1);
2619d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			mmc_request_done(host->mmc, mrq);
2629d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			return;
2639d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		} else
2649d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			msmsdcc_start_command(host, mrq->data->stop, 0);
2659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
2669d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2679d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatout:
2689d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_unlock_irqrestore(&host->lock, flags);
2699d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return;
2709d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
2719d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2729d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int validate_dma(struct msmsdcc_host *host, struct mmc_data *data)
2739d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
2749d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->dma.channel == -1)
2759d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return -ENOENT;
2769d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2779d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if ((data->blksz * data->blocks) < MCI_FIFOSIZE)
2789d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return -EINVAL;
2799d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if ((data->blksz * data->blocks) % MCI_FIFOSIZE)
2809d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return -EINVAL;
2819d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return 0;
2829d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
2839d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2849d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int msmsdcc_config_dma(struct msmsdcc_host *host, struct mmc_data *data)
2859d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
2869d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_nc_dmadata *nc;
2879d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	dmov_box *box;
2889d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	uint32_t rows;
2899d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	uint32_t crci;
2909d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	unsigned int n;
2919d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	int i, rc;
2929d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct scatterlist *sg = data->sg;
2939d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2949d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	rc = validate_dma(host, data);
2959d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (rc)
2969d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return rc;
2979d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
2989d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.sg = data->sg;
2999d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.num_ents = data->sg_len;
3009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
30156a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat       BUG_ON(host->dma.num_ents > NR_SG); /* Prevent memory corruption */
30256a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat
3039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	nc = host->dma.nc;
3049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
30575d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	switch (host->pdev_id) {
30675d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	case 1:
3079d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		crci = MSMSDCC_CRCI_SDC1;
30875d145283b2e42619d7ee1e00b78466bacd51808Joe Perches		break;
30975d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	case 2:
3109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		crci = MSMSDCC_CRCI_SDC2;
31175d145283b2e42619d7ee1e00b78466bacd51808Joe Perches		break;
31275d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	case 3:
3139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		crci = MSMSDCC_CRCI_SDC3;
31475d145283b2e42619d7ee1e00b78466bacd51808Joe Perches		break;
31575d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	case 4:
3169d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		crci = MSMSDCC_CRCI_SDC4;
31775d145283b2e42619d7ee1e00b78466bacd51808Joe Perches		break;
31875d145283b2e42619d7ee1e00b78466bacd51808Joe Perches	default:
3199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->dma.sg = NULL;
3209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->dma.num_ents = 0;
3219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return -ENOENT;
3229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
3239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (data->flags & MMC_DATA_READ)
3259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->dma.dir = DMA_FROM_DEVICE;
3269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	else
3279d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->dma.dir = DMA_TO_DEVICE;
3289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3299d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.user_pages = 0;
3309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	box = &nc->cmd[0];
3329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	for (i = 0; i < host->dma.num_ents; i++) {
3339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		box->cmd = CMD_MODE_BOX;
3349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
33556a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	/* Initialize sg dma address */
33656a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	sg->dma_address = page_to_dma(mmc_dev(host->mmc), sg_page(sg))
33756a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat				+ sg->offset;
33856a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat
33956a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	if (i == (host->dma.num_ents - 1))
3409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->cmd |= CMD_LC;
3419d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		rows = (sg_dma_len(sg) % MCI_FIFOSIZE) ?
3429d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			(sg_dma_len(sg) / MCI_FIFOSIZE) + 1 :
3439d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			(sg_dma_len(sg) / MCI_FIFOSIZE) ;
3449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (data->flags & MMC_DATA_READ) {
3469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->src_row_addr = msmsdcc_fifo_addr(host);
3479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->dst_row_addr = sg_dma_address(sg);
3489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->src_dst_len = (MCI_FIFOSIZE << 16) |
3509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat					   (MCI_FIFOSIZE);
3519d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->row_offset = MCI_FIFOSIZE;
3529d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3539d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->num_rows = rows * ((1 << 16) + 1);
3549d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->cmd |= CMD_SRC_CRCI(crci);
3559d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		} else {
3569d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->src_row_addr = sg_dma_address(sg);
3579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->dst_row_addr = msmsdcc_fifo_addr(host);
3589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->src_dst_len = (MCI_FIFOSIZE << 16) |
3609d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat					   (MCI_FIFOSIZE);
3619d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->row_offset = (MCI_FIFOSIZE << 16);
3629d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3639d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->num_rows = rows * ((1 << 16) + 1);
3649d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			box->cmd |= CMD_DST_CRCI(crci);
3659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		}
3669d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		box++;
3679d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		sg++;
3689d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
3699d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3709d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	/* location of command block must be 64 bit aligned */
3719d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	BUG_ON(host->dma.cmd_busaddr & 0x07);
3729d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
3739d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	nc->cmdptr = (host->dma.cmd_busaddr >> 3) | CMD_PTR_LP;
3749d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.hdr.cmdptr = DMOV_CMD_PTR_LIST |
3759d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			       DMOV_CMD_ADDR(host->dma.cmdptr_busaddr);
3769d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.hdr.complete_func = msmsdcc_dma_complete_func;
3779d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
37856a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	n = dma_map_sg(mmc_dev(host->mmc), host->dma.sg,
37956a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat			host->dma.num_ents, host->dma.dir);
38056a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat/* dsb inside dma_map_sg will write nc out to mem as well */
38156a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat
38256a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	if (n != host->dma.num_ents) {
38356a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		printk(KERN_ERR "%s: Unable to map in all sg elements\n",
38456a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat			mmc_hostname(host->mmc));
38556a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		host->dma.sg = NULL;
38656a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		host->dma.num_ents = 0;
38756a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		return -ENOMEM;
38856a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	}
38956a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat
39056a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	return 0;
39156a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat}
39256a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat
39356a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehatstatic int
39456a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehatsnoop_cccr_abort(struct mmc_command *cmd)
39556a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat{
39656a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	if ((cmd->opcode == 52) &&
39756a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	    (cmd->arg & 0x80000000) &&
39856a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	    (((cmd->arg >> 9) & 0x1ffff) == SDIO_CCCR_ABORT))
39956a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		return 1;
4009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return 0;
4019d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
4029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
40456a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehatmsmsdcc_start_command_deferred(struct msmsdcc_host *host,
40556a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat				struct mmc_command *cmd, u32 *c)
40656a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat{
40756a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	*c |= (cmd->opcode | MCI_CPSM_ENABLE);
40856a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat
40956a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	if (cmd->flags & MMC_RSP_PRESENT) {
41056a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		if (cmd->flags & MMC_RSP_136)
41156a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat			*c |= MCI_CPSM_LONGRSP;
41256a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		*c |= MCI_CPSM_RESPONSE;
41356a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	}
41456a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat
41556a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	if (/*interrupt*/0)
41656a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		*c |= MCI_CPSM_INTERRUPT;
41756a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat
41856a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	if ((((cmd->opcode == 17) || (cmd->opcode == 18))  ||
41956a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	     ((cmd->opcode == 24) || (cmd->opcode == 25))) ||
42056a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	      (cmd->opcode == 53))
42156a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		*c |= MCI_CSPM_DATCMD;
42256a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat
42356a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	if (cmd == cmd->mrq->stop)
42456a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		*c |= MCI_CSPM_MCIABORT;
42556a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat
42656a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	if (snoop_cccr_abort(cmd))
42756a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		*c |= MCI_CSPM_MCIABORT;
42856a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat
42956a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	if (host->curr.cmd != NULL) {
43056a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		printk(KERN_ERR "%s: Overlapping command requests\n",
43156a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat			mmc_hostname(host->mmc));
43256a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	}
43356a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	host->curr.cmd = cmd;
43456a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat}
43556a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat
43656a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehatstatic void
43756a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehatmsmsdcc_start_data(struct msmsdcc_host *host, struct mmc_data *data,
43856a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat			struct mmc_command *cmd, u32 c)
4399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
4409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	unsigned int datactrl, timeout;
4419d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	unsigned long long clks;
4429d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	unsigned int pio_irqmask = 0;
4439d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.data = data;
4459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.xfer_size = data->blksz * data->blocks;
4469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.xfer_remain = host->curr.xfer_size;
4479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.data_xfered = 0;
4489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.got_dataend = 0;
4499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.got_datablkend = 0;
4509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4519d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	memset(&host->pio, 0, sizeof(host->pio));
4529d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4539d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	datactrl = MCI_DPSM_ENABLE | (data->blksz << 4);
4549d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4559d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!msmsdcc_config_dma(host, data))
4569d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		datactrl |= MCI_DPSM_DMAENABLE;
4579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	else {
4589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->pio.sg = data->sg;
4599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->pio.sg_len = data->sg_len;
4609d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->pio.sg_off = 0;
4619d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4629d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (data->flags & MMC_DATA_READ) {
4639d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			pio_irqmask = MCI_RXFIFOHALFFULLMASK;
4649d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			if (host->curr.xfer_remain < MCI_FIFOSIZE)
4659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				pio_irqmask |= MCI_RXDATAAVLBLMASK;
4669d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		} else
4679d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			pio_irqmask = MCI_TXFIFOHALFEMPTYMASK;
4689d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
4699d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4709d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (data->flags & MMC_DATA_READ)
4719d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		datactrl |= MCI_DPSM_DIRECTION;
4729d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
47356a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	clks = (unsigned long long)data->timeout_ns * host->clk_rate;
47456a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	do_div(clks, NSEC_PER_SEC);
47556a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	timeout = data->timeout_clks + (unsigned int)clks*2 ;
4769d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
4779d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (datactrl & MCI_DPSM_DMAENABLE) {
47856a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		/* Save parameters for the exec function */
47956a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		host->cmd_timeout = timeout;
48056a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		host->cmd_pio_irqmask = pio_irqmask;
48156a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		host->cmd_datactrl = datactrl;
48256a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		host->cmd_cmd = cmd;
48356a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat
48456a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		host->dma.hdr.execute_func = msmsdcc_dma_exec_func;
48556a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		host->dma.hdr.data = (void *)host;
4869d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->dma.busy = 1;
48756a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat
48856a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		if (cmd) {
48956a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat			msmsdcc_start_command_deferred(host, cmd, &c);
49056a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat			host->cmd_c = c;
49156a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		}
4929d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		msm_dmov_enqueue_cmd(host->dma.channel, &host->dma.hdr);
49356a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	} else {
49456a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		msmsdcc_writel(host, timeout, MMCIDATATIMER);
4959d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
49656a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		msmsdcc_writel(host, host->curr.xfer_size, MMCIDATALENGTH);
49756a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat
49856a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		msmsdcc_writel(host, pio_irqmask, MMCIMASK1);
49956a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		msmsdcc_writel(host, datactrl, MMCIDATACTRL);
50056a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat
50156a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		if (cmd) {
50256a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat			/* Daisy-chain the command if requested */
50356a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat			msmsdcc_start_command(host, cmd, c);
50456a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		}
50556a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	}
506b3fa579118b239e218e690f5ef76870aff6fe738San Mehat}
507b3fa579118b239e218e690f5ef76870aff6fe738San Mehat
5089d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
5099d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_start_command(struct msmsdcc_host *host, struct mmc_command *cmd, u32 c)
5109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
5119d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (cmd == cmd->mrq->stop)
5129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		c |= MCI_CSPM_MCIABORT;
5139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->stats.cmds++;
5159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
51656a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	msmsdcc_start_command_deferred(host, cmd, &c);
51756a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	msmsdcc_start_command_exec(host, cmd->arg, c);
5189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
5199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
5219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_data_err(struct msmsdcc_host *host, struct mmc_data *data,
5229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		 unsigned int status)
5239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
5249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (status & MCI_DATACRCFAIL) {
5250a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: Data CRC error\n", mmc_hostname(host->mmc));
5260a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: opcode 0x%.8x\n", __func__,
5279d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		       data->mrq->cmd->opcode);
5280a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: blksz %d, blocks %d\n", __func__,
5299d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		       data->blksz, data->blocks);
5309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		data->error = -EILSEQ;
5319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else if (status & MCI_DATATIMEOUT) {
5320a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: Data timeout\n", mmc_hostname(host->mmc));
5339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		data->error = -ETIMEDOUT;
5349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else if (status & MCI_RXOVERRUN) {
5350a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: RX overrun\n", mmc_hostname(host->mmc));
5369d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		data->error = -EIO;
5379d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else if (status & MCI_TXUNDERRUN) {
5380a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: TX underrun\n", mmc_hostname(host->mmc));
5399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		data->error = -EIO;
5409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else {
5410a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: Unknown error (0x%.8x)\n",
5420a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		       mmc_hostname(host->mmc), status);
5439d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		data->error = -EIO;
5449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
5459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
5469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int
5499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_pio_read(struct msmsdcc_host *host, char *buffer, unsigned int remain)
5509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
5519d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	uint32_t	*ptr = (uint32_t *) buffer;
5529d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	int		count = 0;
5539d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5548b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	while (msmsdcc_readl(host, MMCISTATUS) & MCI_RXDATAAVLBL) {
5558b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		*ptr = msmsdcc_readl(host, MMCIFIFO + (count % MCI_FIFOSIZE));
5569d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		ptr++;
5579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		count += sizeof(uint32_t);
5589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		remain -=  sizeof(uint32_t);
5609d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (remain == 0)
5619d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			break;
5629d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
5639d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return count;
5649d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
5659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5669d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int
5679d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_pio_write(struct msmsdcc_host *host, char *buffer,
5689d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		  unsigned int remain, u32 status)
5699d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
5709d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	void __iomem *base = host->base;
5719d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	char *ptr = buffer;
5729d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5739d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	do {
5749d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		unsigned int count, maxcnt;
5759d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5769d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		maxcnt = status & MCI_TXFIFOEMPTY ? MCI_FIFOSIZE :
5779d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat						    MCI_FIFOHALFSIZE;
5789d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		count = min(remain, maxcnt);
5799d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5809d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		writesl(base + MMCIFIFO, ptr, count >> 2);
5819d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		ptr += count;
5829d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		remain -= count;
5839d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5849d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (remain == 0)
5859d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			break;
5869d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5878b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		status = msmsdcc_readl(host, MMCISTATUS);
5889d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} while (status & MCI_TXFIFOHALFEMPTY);
5899d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5909d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return ptr - buffer;
5919d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
5929d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
5939d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int
5949d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_spin_on_status(struct msmsdcc_host *host, uint32_t mask, int maxspin)
5959d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
5969d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	while (maxspin) {
5978b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		if ((msmsdcc_readl(host, MMCISTATUS) & mask))
5989d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			return 0;
5999d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		udelay(1);
6009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		--maxspin;
6019d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
6029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return -ETIMEDOUT;
6039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
6049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
6059d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int
6069d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_pio_irq(int irq, void *dev_id)
6079d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
6089d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host	*host = dev_id;
6099d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	uint32_t		status;
6109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
6118b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	status = msmsdcc_readl(host, MMCISTATUS);
6129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
6139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	do {
6149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		unsigned long flags;
6159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		unsigned int remain, len;
6169d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		char *buffer;
6179d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
6189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (!(status & (MCI_TXFIFOHALFEMPTY | MCI_RXDATAAVLBL))) {
6199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			if (host->curr.xfer_remain == 0 || !msmsdcc_piopoll)
6209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				break;
6219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
6229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			if (msmsdcc_spin_on_status(host,
6239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat						   (MCI_TXFIFOHALFEMPTY |
6249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat						   MCI_RXDATAAVLBL),
6259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat						   PIO_SPINMAX)) {
6269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				break;
6279d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			}
6289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		}
6299d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
6309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		/* Map the current scatter buffer */
6319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		local_irq_save(flags);
6329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		buffer = kmap_atomic(sg_page(host->pio.sg),
6339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				     KM_BIO_SRC_IRQ) + host->pio.sg->offset;
6349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		buffer += host->pio.sg_off;
6359d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		remain = host->pio.sg->length - host->pio.sg_off;
6369d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		len = 0;
6379d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (status & MCI_RXACTIVE)
6389d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			len = msmsdcc_pio_read(host, buffer, remain);
6399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (status & MCI_TXACTIVE)
6409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			len = msmsdcc_pio_write(host, buffer, remain, status);
6419d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
6429d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		/* Unmap the buffer */
6439d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
6449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		local_irq_restore(flags);
6459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
6469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->pio.sg_off += len;
6479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->curr.xfer_remain -= len;
6489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->curr.data_xfered += len;
6499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		remain -= len;
6509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
6519d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (remain == 0) {
6529d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			/* This sg page is full - do some housekeeping */
6539d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			if (status & MCI_RXACTIVE && host->curr.user_pages)
6549d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				flush_dcache_page(sg_page(host->pio.sg));
6559d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
6569d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			if (!--host->pio.sg_len) {
6579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				memset(&host->pio, 0, sizeof(host->pio));
6589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				break;
6599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			}
6609d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
6619d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			/* Advance to next sg */
6629d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			host->pio.sg++;
6639d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			host->pio.sg_off = 0;
6649d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		}
6659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
6668b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		status = msmsdcc_readl(host, MMCISTATUS);
6679d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} while (1);
6689d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
6699d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (status & MCI_RXACTIVE && host->curr.xfer_remain < MCI_FIFOSIZE)
6708b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		msmsdcc_writel(host, MCI_RXDATAAVLBLMASK, MMCIMASK1);
6719d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
6729d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!host->curr.xfer_remain)
6738b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		msmsdcc_writel(host, 0, MMCIMASK1);
6749d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
6759d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return IRQ_HANDLED;
6769d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
6779d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
6789d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void msmsdcc_do_cmdirq(struct msmsdcc_host *host, uint32_t status)
6799d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
6809d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct mmc_command *cmd = host->curr.cmd;
6819d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
6829d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.cmd = NULL;
6838b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	cmd->resp[0] = msmsdcc_readl(host, MMCIRESPONSE0);
6848b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	cmd->resp[1] = msmsdcc_readl(host, MMCIRESPONSE1);
6858b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	cmd->resp[2] = msmsdcc_readl(host, MMCIRESPONSE2);
6868b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	cmd->resp[3] = msmsdcc_readl(host, MMCIRESPONSE3);
6879d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
6889d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (status & MCI_CMDTIMEOUT) {
6899d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		cmd->error = -ETIMEDOUT;
6909d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else if (status & MCI_CMDCRCFAIL &&
6919d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		   cmd->flags & MMC_RSP_CRC) {
6920a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: Command CRC error\n", mmc_hostname(host->mmc));
6939d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		cmd->error = -EILSEQ;
6949d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
6959d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
6969d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!cmd->data || cmd->error) {
6979d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (host->curr.data && host->dma.sg)
6989d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			msm_dmov_stop_cmd(host->dma.channel,
6999d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat					  &host->dma.hdr, 0);
7009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		else if (host->curr.data) { /* Non DMA */
7019d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			msmsdcc_stop_data(host);
7029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			msmsdcc_request_end(host, cmd->mrq);
7039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		} else /* host->data == NULL */
7049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			msmsdcc_request_end(host, cmd->mrq);
70556a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	} else if (cmd->data)
70656a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		if (!(cmd->data->flags & MMC_DATA_READ))
70756a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat			msmsdcc_start_data(host, cmd->data,
70856a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat						NULL, 0);
7099d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
7109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
711b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perchesstatic void
712b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perchesmsmsdcc_handle_irq_data(struct msmsdcc_host *host, u32 status,
713b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches			void __iomem *base)
714b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches{
71556a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	struct mmc_data *data;
716b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches
71756a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	if (status & (MCI_CMDSENT | MCI_CMDRESPEND | MCI_CMDCRCFAIL |
71856a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	              MCI_CMDTIMEOUT) && host->curr.cmd) {
71956a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		msmsdcc_do_cmdirq(host, status);
72056a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	}
72156a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat
72256a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	data = host->curr.data;
723b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	if (!data)
724b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		return;
725b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches
726b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	/* Check for data errors */
727b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	if (status & (MCI_DATACRCFAIL | MCI_DATATIMEOUT |
728b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		      MCI_TXUNDERRUN | MCI_RXOVERRUN)) {
729b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		msmsdcc_data_err(host, data, status);
730b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		host->curr.data_xfered = 0;
731b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		if (host->dma.sg)
732b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches			msm_dmov_stop_cmd(host->dma.channel,
733b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches					  &host->dma.hdr, 0);
734b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		else {
735b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches			msmsdcc_stop_data(host);
736b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches			if (!data->stop)
737b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches				msmsdcc_request_end(host, data->mrq);
738b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches			else
739b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches				msmsdcc_start_command(host, data->stop, 0);
740b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		}
741b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	}
742b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches
743b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	/* Check for data done */
744b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	if (!host->curr.got_dataend && (status & MCI_DATAEND))
745b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		host->curr.got_dataend = 1;
746b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches
747b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	if (!host->curr.got_datablkend && (status & MCI_DATABLOCKEND))
748b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		host->curr.got_datablkend = 1;
749b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches
750b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	/*
751b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	 * If DMA is still in progress, we complete via the completion handler
752b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	 */
753b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	if (host->curr.got_dataend && host->curr.got_datablkend &&
754b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	    !host->dma.busy) {
755b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		/*
756b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		 * There appears to be an issue in the controller where
757b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		 * if you request a small block transfer (< fifo size),
758b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		 * you may get your DATAEND/DATABLKEND irq without the
759b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		 * PIO data irq.
760b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		 *
761b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		 * Check to see if there is still data to be read,
762b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		 * and simulate a PIO irq.
763b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		 */
764b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		if (readl(base + MMCISTATUS) & MCI_RXDATAAVLBL)
765b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches			msmsdcc_pio_irq(1, host);
766b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches
767b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		msmsdcc_stop_data(host);
768b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		if (!data->error)
769b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches			host->curr.data_xfered = host->curr.xfer_size;
770b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches
771b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		if (!data->stop)
772b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches			msmsdcc_request_end(host, data->mrq);
773b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		else
774b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches			msmsdcc_start_command(host, data->stop, 0);
775b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches	}
776b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches}
777b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches
7789d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic irqreturn_t
7799d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_irq(int irq, void *dev_id)
7809d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
7819d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host	*host = dev_id;
7829d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	void __iomem		*base = host->base;
7839d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	u32			status;
7849d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	int			ret = 0;
7859d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	int			cardint = 0;
7869d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7879d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_lock(&host->lock);
7889d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
7899d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	do {
7908b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		struct mmc_data *data;
7918b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		status = msmsdcc_readl(host, MMCISTATUS);
7928b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		status &= (msmsdcc_readl(host, MMCIMASK0) |
7938b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat					      MCI_DATABLOCKENDMASK);
7948b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		msmsdcc_writel(host, status, MMCICLEAR);
7959d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
796865c8064a2fb07100525097983966b8e789bde1aSan Mehat		if (status & MCI_SDIOINTR)
797865c8064a2fb07100525097983966b8e789bde1aSan Mehat			status &= ~MCI_SDIOINTR;
798865c8064a2fb07100525097983966b8e789bde1aSan Mehat
799865c8064a2fb07100525097983966b8e789bde1aSan Mehat		if (!status)
800865c8064a2fb07100525097983966b8e789bde1aSan Mehat			break;
801865c8064a2fb07100525097983966b8e789bde1aSan Mehat
802b5a74d6058e86546868242bb5283e16fb10fd90aJoe Perches		msmsdcc_handle_irq_data(host, status, base);
8039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (status & MCI_SDIOINTOPER) {
8059d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			cardint = 1;
8069d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			status &= ~MCI_SDIOINTOPER;
8079d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		}
8089d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		ret = 1;
8099d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} while (status);
8109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8119d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_unlock(&host->lock);
8129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	/*
8149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 * We have to delay handling the card interrupt as it calls
8159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 * back into the driver.
8169d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 */
8179d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (cardint)
8189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		mmc_signal_sdio_irq(host->mmc);
8199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return IRQ_RETVAL(ret);
8219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
8229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
8249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_request(struct mmc_host *mmc, struct mmc_request *mrq)
8259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
8269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host *host = mmc_priv(mmc);
8279d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	unsigned long flags;
8289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8299d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	WARN_ON(host->curr.mrq != NULL);
8309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	WARN_ON(host->pwr == 0);
8319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_lock_irqsave(&host->lock, flags);
8339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->stats.reqs++;
8359d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8369d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->eject) {
8379d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (mrq->data && !(mrq->data->flags & MMC_DATA_READ)) {
8389d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			mrq->cmd->error = 0;
8399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			mrq->data->bytes_xfered = mrq->data->blksz *
8409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat						  mrq->data->blocks;
8419d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		} else
8429d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			mrq->cmd->error = -ENOMEDIUM;
8439d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		spin_unlock_irqrestore(&host->lock, flags);
8459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		mmc_request_done(mmc, mrq);
8469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return;
8479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
8489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->curr.mrq = mrq;
850c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat
851c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat	/* Need to drop the host lock here in case
852c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat	 * the busclk wd fires
853c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat	 */
854c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat	spin_unlock_irqrestore(&host->lock, flags);
855865c8064a2fb07100525097983966b8e789bde1aSan Mehat	if (!host->clks_on)
856c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat		msmsdcc_enable_clocks(host);
857c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat	spin_lock_irqsave(&host->lock, flags);
8589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (mrq->data && mrq->data->flags & MMC_DATA_READ)
86056a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		/* Queue/read data, daisy-chain command when data starts */
86156a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		msmsdcc_start_data(host, mrq->data, mrq->cmd, 0);
86256a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	else
86356a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		msmsdcc_start_command(host, mrq->cmd, 0);
8649d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->cmdpoll && !msmsdcc_spin_on_status(host,
8669d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				MCI_CMDRESPEND|MCI_CMDCRCFAIL|MCI_CMDTIMEOUT,
8679d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				CMD_SPINMAX)) {
8688b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		uint32_t status = msmsdcc_readl(host, MMCISTATUS);
8699d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		msmsdcc_do_cmdirq(host, status);
8708b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		msmsdcc_writel(host,
8718b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat			       MCI_CMDRESPEND | MCI_CMDCRCFAIL | MCI_CMDTIMEOUT,
8728b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat			       MMCICLEAR);
8739d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->stats.cmdpoll_hits++;
8749d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else {
8759d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->stats.cmdpoll_misses++;
8769d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
8779d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_unlock_irqrestore(&host->lock, flags);
8789d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
8799d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
8809d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
8819d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
8829d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
8839d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host *host = mmc_priv(mmc);
8849d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	u32 clk = 0, pwr = 0;
8859d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	int rc;
8864adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat	unsigned long flags;
8879d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
888865c8064a2fb07100525097983966b8e789bde1aSan Mehat	if (!host->clks_on)
889c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat		msmsdcc_enable_clocks(host);
890c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat
891c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat	spin_lock_irqsave(&host->lock, flags);
8929d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
893865c8064a2fb07100525097983966b8e789bde1aSan Mehat	if (ios->clock) {
8949d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (ios->clock != host->clk_rate) {
8959d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			rc = clk_set_rate(host->clk, ios->clock);
8969d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			if (rc < 0)
8970a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches				pr_err("%s: Error setting clock rate (%d)\n",
8980a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches				       mmc_hostname(host->mmc), rc);
8999d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			else
9009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				host->clk_rate = ios->clock;
9019d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		}
9029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		clk |= MCI_CLK_ENABLE;
9039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
9049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9059d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (ios->bus_width == MMC_BUS_WIDTH_4)
9069d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		clk |= (2 << 10); /* Set WIDEBUS */
9079d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9089d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (ios->clock > 400000 && msmsdcc_pwrsave)
9099d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		clk |= (1 << 9); /* PWRSAVE */
9109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9119d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	clk |= (1 << 12); /* FLOW_ENA */
9129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	clk |= (1 << 15); /* feedback clock */
9139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->plat->translate_vdd)
9159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		pwr |= host->plat->translate_vdd(mmc_dev(mmc), ios->vdd);
9169d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9179d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	switch (ios->power_mode) {
9189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	case MMC_POWER_OFF:
9199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		break;
9209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	case MMC_POWER_UP:
9219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		pwr |= MCI_PWR_UP;
9229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		break;
9239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	case MMC_POWER_ON:
9249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		pwr |= MCI_PWR_ON;
9259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		break;
9269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
9279d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
9299d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		pwr |= MCI_OD;
9309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9318b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	msmsdcc_writel(host, clk, MMCICLOCK);
9329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->pwr != pwr) {
9349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->pwr = pwr;
9358b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		msmsdcc_writel(host, pwr, MMCIPOWER);
9369d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
937c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat	msmsdcc_disable_clocks(host, 1);
9384adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat	spin_unlock_irqrestore(&host->lock, flags);
9399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
9409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9419d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void msmsdcc_enable_sdio_irq(struct mmc_host *mmc, int enable)
9429d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
9439d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host *host = mmc_priv(mmc);
9449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	unsigned long flags;
9459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	u32 status;
9469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_lock_irqsave(&host->lock, flags);
9489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (msmsdcc_sdioirq == 1) {
9498b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		status = msmsdcc_readl(host, MMCIMASK0);
9509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (enable)
9519d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			status |= MCI_SDIOINTOPERMASK;
9529d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		else
9539d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			status &= ~MCI_SDIOINTOPERMASK;
9549d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->saved_irq0mask = status;
9558b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		msmsdcc_writel(host, status, MMCIMASK0);
9569d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
9579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_unlock_irqrestore(&host->lock, flags);
9589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
9599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9609d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic const struct mmc_host_ops msmsdcc_ops = {
9619d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	.request	= msmsdcc_request,
9629d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	.set_ios	= msmsdcc_set_ios,
9639d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	.enable_sdio_irq = msmsdcc_enable_sdio_irq,
9649d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat};
9659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9669d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
9679d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_check_status(unsigned long data)
9689d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
9699d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host *host = (struct msmsdcc_host *)data;
9709d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	unsigned int status;
9719d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9729d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!host->plat->status) {
9739d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		mmc_detect_change(host->mmc, 0);
9749d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		goto out;
9759d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
9769d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9779d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	status = host->plat->status(mmc_dev(host->mmc));
9789d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->eject = !status;
9799d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (status ^ host->oldstat) {
9800a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_info("%s: Slot status change detected (%d -> %d)\n",
9810a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches			mmc_hostname(host->mmc), host->oldstat, status);
9829d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (status)
9839d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			mmc_detect_change(host->mmc, (5 * HZ) / 2);
9849d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		else
9859d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			mmc_detect_change(host->mmc, 0);
9869d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
9879d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9889d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->oldstat = status;
9899d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9909d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatout:
9919d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->timer.function)
9929d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		mod_timer(&host->timer, jiffies + HZ);
9939d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
9949d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
9959d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic irqreturn_t
9969d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_platform_status_irq(int irq, void *dev_id)
9979d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
9989d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host *host = dev_id;
9999d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	printk(KERN_DEBUG "%s: %d\n", __func__, irq);
10019d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	msmsdcc_check_status((unsigned long) host);
10029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return IRQ_HANDLED;
10039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
10049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10059d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
10069d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_status_notify_cb(int card_present, void *dev_id)
10079d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
10089d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host *host = dev_id;
10099d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	printk(KERN_DEBUG "%s: card_present %d\n", mmc_hostname(host->mmc),
10119d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	       card_present);
10129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	msmsdcc_check_status((unsigned long) host);
10139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
10149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1015865c8064a2fb07100525097983966b8e789bde1aSan Mehatstatic void
1016865c8064a2fb07100525097983966b8e789bde1aSan Mehatmsmsdcc_busclk_expired(unsigned long _data)
1017865c8064a2fb07100525097983966b8e789bde1aSan Mehat{
1018865c8064a2fb07100525097983966b8e789bde1aSan Mehat	struct msmsdcc_host	*host = (struct msmsdcc_host *) _data;
1019865c8064a2fb07100525097983966b8e789bde1aSan Mehat	unsigned long 		flags;
1020865c8064a2fb07100525097983966b8e789bde1aSan Mehat
1021865c8064a2fb07100525097983966b8e789bde1aSan Mehat	spin_lock_irqsave(&host->lock, flags);
1022c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat	dev_info(mmc_dev(host->mmc), "Bus clock timer expired\n");
1023865c8064a2fb07100525097983966b8e789bde1aSan Mehat	if (host->clks_on)
1024c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat		msmsdcc_disable_clocks(host, 0);
1025865c8064a2fb07100525097983966b8e789bde1aSan Mehat	spin_unlock_irqrestore(&host->lock, flags);
1026865c8064a2fb07100525097983966b8e789bde1aSan Mehat}
1027865c8064a2fb07100525097983966b8e789bde1aSan Mehat
10289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int
10299d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_init_dma(struct msmsdcc_host *host)
10309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
10319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	memset(&host->dma, 0, sizeof(struct msmsdcc_dma_data));
10329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.host = host;
10339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.channel = -1;
10349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10359d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!host->dmares)
10369d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return -ENODEV;
10379d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10389d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.nc = dma_alloc_coherent(NULL,
10399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat					  sizeof(struct msmsdcc_nc_dmadata),
10409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat					  &host->dma.nc_busaddr,
10419d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat					  GFP_KERNEL);
10429d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->dma.nc == NULL) {
10430a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("Unable to allocate DMA buffer\n");
10449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return -ENOMEM;
10459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
10469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	memset(host->dma.nc, 0x00, sizeof(struct msmsdcc_nc_dmadata));
10479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.cmd_busaddr = host->dma.nc_busaddr;
10489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.cmdptr_busaddr = host->dma.nc_busaddr +
10499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				offsetof(struct msmsdcc_nc_dmadata, cmdptr);
10509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dma.channel = host->dmares->start;
10519d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10529d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return 0;
10539d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
10549d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10559d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#ifdef CONFIG_MMC_MSM7X00A_RESUME_IN_WQ
10569d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void
10579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatdo_resume_work(struct work_struct *work)
10589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
10599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host *host =
10609d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		container_of(work, struct msmsdcc_host, resume_task);
10619d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct mmc_host	*mmc = host->mmc;
10629d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10639d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (mmc) {
10649d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		mmc_resume_host(mmc);
10659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (host->stat_irq)
10669d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			enable_irq(host->stat_irq);
10679d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
10689d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
10699d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat#endif
10709d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10719d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int
10729d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_probe(struct platform_device *pdev)
10739d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
10749d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct mmc_platform_data *plat = pdev->dev.platform_data;
10759d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct msmsdcc_host *host;
10769d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct mmc_host *mmc;
10779d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct resource *cmd_irqres = NULL;
10789d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct resource *pio_irqres = NULL;
10799d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct resource *stat_irqres = NULL;
10809d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct resource *memres = NULL;
10819d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct resource *dmares = NULL;
10829d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	int ret;
10839d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10849d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	/* must have platform data */
10859d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!plat) {
10860a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: Platform data not available\n", __func__);
10879d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		ret = -EINVAL;
10889d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		goto out;
10899d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
10909d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10919d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (pdev->id < 1 || pdev->id > 4)
10929d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return -EINVAL;
10939d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10949d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (pdev->resource == NULL || pdev->num_resources < 2) {
10950a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: Invalid resource\n", __func__);
10969d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return -ENXIO;
10979d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
10989d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
10999d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	memres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
11009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
11019d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	cmd_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
11029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat						  "cmd_irq");
11039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	pio_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
11049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat						  "pio_irq");
11059d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	stat_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
11069d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat						   "status_irq");
11079d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11089d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!cmd_irqres || !pio_irqres || !memres) {
11090a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: Invalid resource\n", __func__);
11109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		return -ENXIO;
11119d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
11129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	/*
11149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 * Setup our host structure
11159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 */
11169d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11179d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc = mmc_alloc_host(sizeof(struct msmsdcc_host), &pdev->dev);
11189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!mmc) {
11199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		ret = -ENOMEM;
11209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		goto out;
11219d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
11229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host = mmc_priv(mmc);
11249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->pdev_id = pdev->id;
11259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->plat = plat;
11269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->mmc = mmc;
112756a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	host->curr.cmd = NULL;
11289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11299d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->cmdpoll = 1;
11309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->base = ioremap(memres->start, PAGE_SIZE);
11329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (!host->base) {
11339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		ret = -ENOMEM;
11349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		goto out;
11359d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
11369d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11379d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->cmd_irqres = cmd_irqres;
11389d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->pio_irqres = pio_irqres;
11399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->memres = memres;
11409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->dmares = dmares;
11419d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	spin_lock_init(&host->lock);
11429d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11439d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	/*
11449d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 * Setup DMA
11459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 */
11469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	msmsdcc_init_dma(host);
11479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11484adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat	/* Get our clocks */
11499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->pclk = clk_get(&pdev->dev, "sdc_pclk");
11509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (IS_ERR(host->pclk)) {
11519d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		ret = PTR_ERR(host->pclk);
11529d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		goto host_free;
11539d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
11549d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11559d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->clk = clk_get(&pdev->dev, "sdc_clk");
11569d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (IS_ERR(host->clk)) {
11579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		ret = PTR_ERR(host->clk);
11584adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat		goto pclk_put;
11599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
11609d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11614adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat	/* Enable clocks */
1162c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat	ret = msmsdcc_enable_clocks(host);
11639d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (ret)
11649d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		goto clk_put;
11659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11669d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	ret = clk_set_rate(host->clk, msmsdcc_fmin);
11679d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (ret) {
11680a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: Clock rate set failed (%d)\n", __func__, ret);
11699d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		goto clk_disable;
11709d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
11719d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11724adbbcc7b6cfb3dcf5ab49b06edb7752391b0e80San Mehat	host->pclk_rate = clk_get_rate(host->pclk);
11739d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->clk_rate = clk_get_rate(host->clk);
11749d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11759d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	/*
11769d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 * Setup MMC host structure
11779d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 */
11789d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->ops = &msmsdcc_ops;
11799d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->f_min = msmsdcc_fmin;
11809d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->f_max = msmsdcc_fmax;
11819d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->ocr_avail = plat->ocr_mask;
11829d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11839d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (msmsdcc_4bit)
11849d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		mmc->caps |= MMC_CAP_4_BIT_DATA;
11859d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (msmsdcc_sdioirq)
11869d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		mmc->caps |= MMC_CAP_SDIO_IRQ;
11879d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
11889d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11899d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->max_phys_segs = NR_SG;
11909d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->max_hw_segs = NR_SG;
11919d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->max_blk_size = 4096;	/* MCI_DATA_CTL BLOCKSIZE up to 4096 */
11929d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->max_blk_count = 65536;
11939d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11949d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->max_req_size = 33554432;	/* MCI_DATA_LENGTH is 25 bits */
11959d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc->max_seg_size = mmc->max_req_size;
11969d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
11978b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	msmsdcc_writel(host, 0, MMCIMASK0);
11988b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	msmsdcc_writel(host, 0x5e007ff, MMCICLEAR);
11999d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12008b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat	msmsdcc_writel(host, MCI_IRQENABLE, MMCIMASK0);
12019d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	host->saved_irq0mask = MCI_IRQENABLE;
12029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	/*
12049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 * Setup card detect change
12059d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	 */
12069d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12079d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	memset(&host->timer, 0, sizeof(host->timer));
12089d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12099d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (stat_irqres && !(stat_irqres->flags & IORESOURCE_DISABLED)) {
12109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		unsigned long irqflags = IRQF_SHARED |
12119d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			(stat_irqres->flags & IRQF_TRIGGER_MASK);
12129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->stat_irq = stat_irqres->start;
12149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		ret = request_irq(host->stat_irq,
12159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				  msmsdcc_platform_status_irq,
12169d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				  irqflags,
12179d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				  DRIVER_NAME " (slot)",
12189d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat				  host);
12199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (ret) {
12200a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches			pr_err("%s: Unable to get slot IRQ %d (%d)\n",
12210a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches			       mmc_hostname(mmc), host->stat_irq, ret);
12229d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			goto clk_disable;
12239d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		}
12249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else if (plat->register_status_notify) {
12259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		plat->register_status_notify(msmsdcc_status_notify_cb, host);
12269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else if (!plat->status)
12270a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_err("%s: No card detect facilities available\n",
12289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		       mmc_hostname(mmc));
12299d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	else {
12309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		init_timer(&host->timer);
12319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->timer.data = (unsigned long)host;
12329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->timer.function = msmsdcc_check_status;
12339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->timer.expires = jiffies + HZ;
12349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		add_timer(&host->timer);
12359d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
12369d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12379d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (plat->status) {
12389d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->oldstat = host->plat->status(mmc_dev(host->mmc));
12399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		host->eject = !host->oldstat;
12409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
12419d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1242865c8064a2fb07100525097983966b8e789bde1aSan Mehat	init_timer(&host->busclk_timer);
1243865c8064a2fb07100525097983966b8e789bde1aSan Mehat	host->busclk_timer.data = (unsigned long) host;
1244865c8064a2fb07100525097983966b8e789bde1aSan Mehat	host->busclk_timer.function = msmsdcc_busclk_expired;
1245865c8064a2fb07100525097983966b8e789bde1aSan Mehat
12469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	ret = request_irq(cmd_irqres->start, msmsdcc_irq, IRQF_SHARED,
12479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			  DRIVER_NAME " (cmd)", host);
12489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (ret)
12499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		goto stat_irq_free;
12509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12519d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	ret = request_irq(pio_irqres->start, msmsdcc_pio_irq, IRQF_SHARED,
12529d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			  DRIVER_NAME " (pio)", host);
12539d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (ret)
12549d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		goto cmd_irq_free;
12559d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12569d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc_set_drvdata(pdev, mmc);
12579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc_add_host(mmc);
12589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12590a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches	pr_info("%s: Qualcomm MSM SDCC at 0x%016llx irq %d,%d dma %d\n",
12600a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		mmc_hostname(mmc), (unsigned long long)memres->start,
12610a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		(unsigned int) cmd_irqres->start,
12620a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		(unsigned int) host->stat_irq, host->dma.channel);
12630a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches	pr_info("%s: 4 bit data mode %s\n", mmc_hostname(mmc),
12640a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		(mmc->caps & MMC_CAP_4_BIT_DATA ? "enabled" : "disabled"));
12650a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches	pr_info("%s: MMC clock %u -> %u Hz, PCLK %u Hz\n",
12660a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		mmc_hostname(mmc), msmsdcc_fmin, msmsdcc_fmax, host->pclk_rate);
12670a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches	pr_info("%s: Slot eject status = %d\n", mmc_hostname(mmc), host->eject);
12680a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches	pr_info("%s: Power save feature enable = %d\n",
12690a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		mmc_hostname(mmc), msmsdcc_pwrsave);
12709d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
12719d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->dma.channel != -1) {
12720a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_info("%s: DM non-cached buffer at %p, dma_addr 0x%.8x\n",
12730a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches			mmc_hostname(mmc), host->dma.nc, host->dma.nc_busaddr);
12740a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_info("%s: DM cmd busaddr 0x%.8x, cmdptr busaddr 0x%.8x\n",
12750a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches			mmc_hostname(mmc), host->dma.cmd_busaddr,
12760a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches			host->dma.cmdptr_busaddr);
12779d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	} else
12780a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_info("%s: PIO transfer enabled\n", mmc_hostname(mmc));
12799d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->timer.function)
12800a7ff7c7573011ec1f52052a8baeae68f4066ddeJoe Perches		pr_info("%s: Polling status mode enabled\n", mmc_hostname(mmc));
12819d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1282c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat	msmsdcc_disable_clocks(host, 1);
12839d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return 0;
12849d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat cmd_irq_free:
12859d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	free_irq(cmd_irqres->start, host);
12869d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat stat_irq_free:
12879d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (host->stat_irq)
12889d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		free_irq(host->stat_irq, host);
12899d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat clk_disable:
1290c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat	msmsdcc_disable_clocks(host, 0);
12919d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat clk_put:
12929d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	clk_put(host->clk);
12939d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat pclk_put:
12949d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	clk_put(host->pclk);
12959d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat host_free:
12969d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	mmc_free_host(mmc);
12979d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat out:
12989d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return ret;
12999d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
13009d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
13019d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int
13029d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_suspend(struct platform_device *dev, pm_message_t state)
13039d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
13049d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct mmc_host *mmc = mmc_get_drvdata(dev);
13059d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	int rc = 0;
130656a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat	unsigned long flags;
13079d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
13089d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (mmc) {
13099d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		struct msmsdcc_host *host = mmc_priv(mmc);
13109d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
131156a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		spin_lock_irqsave(&host->lock, flags);
13129d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (host->stat_irq)
13139d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			disable_irq(host->stat_irq);
13149d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
13159d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (mmc->card && mmc->card->type != MMC_TYPE_SDIO)
13169d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			rc = mmc_suspend_host(mmc, state);
13179d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (!rc) {
13188b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat			msmsdcc_writel(host, 0, MMCIMASK0);
13199d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
13209d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		}
132156a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat		spin_unlock_irqrestore(&host->lock, flags);
1322c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat		if (host->clks_on)
1323c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat			msmsdcc_disable_clocks(host, 0);
13249d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
13259d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return rc;
13269d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
13279d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
13289d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int
13299d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmsmsdcc_resume(struct platform_device *dev)
13309d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
13319d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	struct mmc_host *mmc = mmc_get_drvdata(dev);
13329d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
13339d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	if (mmc) {
13349d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		struct msmsdcc_host *host = mmc_priv(mmc);
13359d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
1336c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat		msmsdcc_enable_clocks(host);
133756a8b5b8ae81bd766e527a0e5274a087c3c1109dSan Mehat
13388b1c2ba274c8416afb7eab3bd788f98a917efe06San Mehat		msmsdcc_writel(host, host->saved_irq0mask, MMCIMASK0);
13399d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
13409d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		if (mmc->card && mmc->card->type != MMC_TYPE_SDIO)
13419d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			mmc_resume_host(mmc);
13425b8a2fb34f5670b1f07483bfa40de9ce539dbdb2Roel Kluin		if (host->stat_irq)
13439d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat			enable_irq(host->stat_irq);
1344c7fc9370df1433486dfa9460a833fae664e8be6cSan Mehat		msmsdcc_disable_clocks(host, 1);
13459d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	}
13469d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return 0;
13479d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
13489d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
13499d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic struct platform_driver msmsdcc_driver = {
13509d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	.probe		= msmsdcc_probe,
13519d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	.suspend	= msmsdcc_suspend,
13529d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	.resume		= msmsdcc_resume,
13539d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	.driver		= {
13549d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat		.name	= "msm_sdcc",
13559d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	},
13569d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat};
13579d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
13589d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic int __init msmsdcc_init(void)
13599d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
13609d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	return platform_driver_register(&msmsdcc_driver);
13619d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
13629d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
13639d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatstatic void __exit msmsdcc_exit(void)
13649d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat{
13659d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat	platform_driver_unregister(&msmsdcc_driver);
13669d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat}
13679d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
13689d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmodule_init(msmsdcc_init);
13699d2bd7383c71d38c60328a3dc8a946eda2013826San Mehatmodule_exit(msmsdcc_exit);
13709d2bd7383c71d38c60328a3dc8a946eda2013826San Mehat
13719d2bd7383c71d38c60328a3dc8a946eda2013826San MehatMODULE_DESCRIPTION("Qualcomm MSM 7X00A Multimedia Card Interface driver");
13729d2bd7383c71d38c60328a3dc8a946eda2013826San MehatMODULE_LICENSE("GPL");
1373