1eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai/*
2eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai * bfin_sdh.c - Analog Devices Blackfin SDH Controller
3eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai *
4eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai * Copyright (C) 2007-2009 Analog Device Inc.
5eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai *
6eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai * Licensed under the GPL-2 or later.
7eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai */
8eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
9eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#define DRIVER_NAME	"bfin-sdh"
10eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
11eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#include <linux/module.h>
12eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#include <linux/init.h>
13eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#include <linux/ioport.h>
14eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#include <linux/platform_device.h>
15eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#include <linux/delay.h>
16eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#include <linux/interrupt.h>
17eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#include <linux/dma-mapping.h>
18eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#include <linux/mmc/host.h>
19eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#include <linux/proc_fs.h>
205a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/gfp.h>
21eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
22eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#include <asm/cacheflush.h>
23eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#include <asm/dma.h>
24eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#include <asm/portmux.h>
25eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#include <asm/bfin_sdh.h>
26eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
27eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#if defined(CONFIG_BF51x)
28eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#define bfin_read_SDH_PWR_CTL		bfin_read_RSI_PWR_CTL
29eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#define bfin_write_SDH_PWR_CTL		bfin_write_RSI_PWR_CTL
30eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#define bfin_read_SDH_CLK_CTL		bfin_read_RSI_CLK_CTL
31eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#define bfin_write_SDH_CLK_CTL		bfin_write_RSI_CLK_CTL
32eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#define bfin_write_SDH_ARGUMENT		bfin_write_RSI_ARGUMENT
33eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#define bfin_write_SDH_COMMAND		bfin_write_RSI_COMMAND
34eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#define bfin_write_SDH_DATA_TIMER	bfin_write_RSI_DATA_TIMER
35eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#define bfin_read_SDH_RESPONSE0		bfin_read_RSI_RESPONSE0
36eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#define bfin_read_SDH_RESPONSE1		bfin_read_RSI_RESPONSE1
37eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#define bfin_read_SDH_RESPONSE2		bfin_read_RSI_RESPONSE2
38eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#define bfin_read_SDH_RESPONSE3		bfin_read_RSI_RESPONSE3
39eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#define bfin_write_SDH_DATA_LGTH	bfin_write_RSI_DATA_LGTH
40eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#define bfin_read_SDH_DATA_CTL		bfin_read_RSI_DATA_CTL
41eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#define bfin_write_SDH_DATA_CTL		bfin_write_RSI_DATA_CTL
42eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#define bfin_read_SDH_DATA_CNT		bfin_read_RSI_DATA_CNT
43eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#define bfin_write_SDH_STATUS_CLR	bfin_write_RSI_STATUS_CLR
44eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#define bfin_read_SDH_E_STATUS		bfin_read_RSI_E_STATUS
45eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#define bfin_write_SDH_E_STATUS		bfin_write_RSI_E_STATUS
46eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#define bfin_read_SDH_STATUS		bfin_read_RSI_STATUS
47eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#define bfin_write_SDH_MASK0		bfin_write_RSI_MASK0
48eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#define bfin_read_SDH_CFG		bfin_read_RSI_CFG
49eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#define bfin_write_SDH_CFG		bfin_write_RSI_CFG
50eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#endif
51eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
52eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Caistruct dma_desc_array {
53eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	unsigned long	start_addr;
54eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	unsigned short	cfg;
55eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	unsigned short	x_count;
56eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	short		x_modify;
57eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai} __packed;
58eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
59eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Caistruct sdh_host {
60eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	struct mmc_host		*mmc;
61eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	spinlock_t		lock;
62eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	struct resource		*res;
63eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	void __iomem		*base;
64eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	int			irq;
65eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	int			stat_irq;
66eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	int			dma_ch;
67eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	int			dma_dir;
68eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	struct dma_desc_array	*sg_cpu;
69eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	dma_addr_t		sg_dma;
70eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	int			dma_len;
71eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
72eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	unsigned int		imask;
73eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	unsigned int		power_mode;
74eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	unsigned int		clk_div;
75eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
76eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	struct mmc_request	*mrq;
77eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	struct mmc_command	*cmd;
78eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	struct mmc_data		*data;
79eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai};
80eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
81eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Caistatic struct bfin_sd_host *get_sdh_data(struct platform_device *pdev)
82eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai{
83eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	return pdev->dev.platform_data;
84eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai}
85eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
86eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Caistatic void sdh_stop_clock(struct sdh_host *host)
87eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai{
88eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	bfin_write_SDH_CLK_CTL(bfin_read_SDH_CLK_CTL() & ~CLK_E);
89eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	SSYNC();
90eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai}
91eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
92eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Caistatic void sdh_enable_stat_irq(struct sdh_host *host, unsigned int mask)
93eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai{
94eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	unsigned long flags;
95eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
96eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	spin_lock_irqsave(&host->lock, flags);
97eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	host->imask |= mask;
98eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	bfin_write_SDH_MASK0(mask);
99eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	SSYNC();
100eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	spin_unlock_irqrestore(&host->lock, flags);
101eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai}
102eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
103eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Caistatic void sdh_disable_stat_irq(struct sdh_host *host, unsigned int mask)
104eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai{
105eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	unsigned long flags;
106eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
107eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	spin_lock_irqsave(&host->lock, flags);
108eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	host->imask &= ~mask;
109eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	bfin_write_SDH_MASK0(host->imask);
110eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	SSYNC();
111eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	spin_unlock_irqrestore(&host->lock, flags);
112eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai}
113eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
114eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Caistatic int sdh_setup_data(struct sdh_host *host, struct mmc_data *data)
115eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai{
116eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	unsigned int length;
117eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	unsigned int data_ctl;
118eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	unsigned int dma_cfg;
119729adf1b5f4562f67fe8bf6c1df97edc1128fac7Cliff Cai	unsigned int cycle_ns, timeout;
120eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
121eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	dev_dbg(mmc_dev(host->mmc), "%s enter flags: 0x%x\n", __func__, data->flags);
122eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	host->data = data;
123eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	data_ctl = 0;
124eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	dma_cfg = 0;
125eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
126eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	length = data->blksz * data->blocks;
127eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	bfin_write_SDH_DATA_LGTH(length);
128eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
129eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (data->flags & MMC_DATA_STREAM)
130eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		data_ctl |= DTX_MODE;
131eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
132eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (data->flags & MMC_DATA_READ)
133eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		data_ctl |= DTX_DIR;
134eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	/* Only supports power-of-2 block size */
135eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (data->blksz & (data->blksz - 1))
136eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		return -EINVAL;
137eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	data_ctl |= ((ffs(data->blksz) - 1) << 4);
138eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
139eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	bfin_write_SDH_DATA_CTL(data_ctl);
140729adf1b5f4562f67fe8bf6c1df97edc1128fac7Cliff Cai	/* the time of a host clock period in ns */
141729adf1b5f4562f67fe8bf6c1df97edc1128fac7Cliff Cai	cycle_ns = 1000000000 / (get_sclk() / (2 * (host->clk_div + 1)));
142729adf1b5f4562f67fe8bf6c1df97edc1128fac7Cliff Cai	timeout = data->timeout_ns / cycle_ns;
143729adf1b5f4562f67fe8bf6c1df97edc1128fac7Cliff Cai	timeout += data->timeout_clks;
144729adf1b5f4562f67fe8bf6c1df97edc1128fac7Cliff Cai	bfin_write_SDH_DATA_TIMER(timeout);
145eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	SSYNC();
146eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
147eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (data->flags & MMC_DATA_READ) {
148eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		host->dma_dir = DMA_FROM_DEVICE;
149eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		dma_cfg |= WNR;
150eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	} else
151eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		host->dma_dir = DMA_TO_DEVICE;
152eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
153eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	sdh_enable_stat_irq(host, (DAT_CRC_FAIL | DAT_TIME_OUT | DAT_END));
154eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, host->dma_dir);
155eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#if defined(CONFIG_BF54x)
156eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	dma_cfg |= DMAFLOW_ARRAY | NDSIZE_5 | RESTART | WDSIZE_32 | DMAEN;
157eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	{
158c744d988729db9be37d1c877d143cfe63941c25cMike Frysinger		struct scatterlist *sg;
159eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		int i;
160eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		for_each_sg(data->sg, sg, host->dma_len, i) {
161eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai			host->sg_cpu[i].start_addr = sg_dma_address(sg);
162eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai			host->sg_cpu[i].cfg = dma_cfg;
163eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai			host->sg_cpu[i].x_count = sg_dma_len(sg) / 4;
164eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai			host->sg_cpu[i].x_modify = 4;
165eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai			dev_dbg(mmc_dev(host->mmc), "%d: start_addr:0x%lx, "
166eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai				"cfg:0x%x, x_count:0x%x, x_modify:0x%x\n",
167eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai				i, host->sg_cpu[i].start_addr,
168eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai				host->sg_cpu[i].cfg, host->sg_cpu[i].x_count,
169eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai				host->sg_cpu[i].x_modify);
170eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		}
171eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	}
172eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	flush_dcache_range((unsigned int)host->sg_cpu,
173eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		(unsigned int)host->sg_cpu +
174eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai			host->dma_len * sizeof(struct dma_desc_array));
175eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	/* Set the last descriptor to stop mode */
176eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	host->sg_cpu[host->dma_len - 1].cfg &= ~(DMAFLOW | NDSIZE);
177eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	host->sg_cpu[host->dma_len - 1].cfg |= DI_EN;
178eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
179eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	set_dma_curr_desc_addr(host->dma_ch, (unsigned long *)host->sg_dma);
180eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	set_dma_x_count(host->dma_ch, 0);
181eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	set_dma_x_modify(host->dma_ch, 0);
182eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	set_dma_config(host->dma_ch, dma_cfg);
183eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#elif defined(CONFIG_BF51x)
184eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	/* RSI DMA doesn't work in array mode */
185eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	dma_cfg |= WDSIZE_32 | DMAEN;
186eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	set_dma_start_addr(host->dma_ch, sg_dma_address(&data->sg[0]));
187eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	set_dma_x_count(host->dma_ch, length / 4);
188eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	set_dma_x_modify(host->dma_ch, 4);
189eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	set_dma_config(host->dma_ch, dma_cfg);
190eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#endif
191eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	bfin_write_SDH_DATA_CTL(bfin_read_SDH_DATA_CTL() | DTX_DMA_E | DTX_E);
192eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
193eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	SSYNC();
194eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
195eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	dev_dbg(mmc_dev(host->mmc), "%s exit\n", __func__);
196eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	return 0;
197eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai}
198eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
199eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Caistatic void sdh_start_cmd(struct sdh_host *host, struct mmc_command *cmd)
200eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai{
201eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	unsigned int sdh_cmd;
202eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	unsigned int stat_mask;
203eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
204eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	dev_dbg(mmc_dev(host->mmc), "%s enter cmd: 0x%p\n", __func__, cmd);
205eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	WARN_ON(host->cmd != NULL);
206eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	host->cmd = cmd;
207eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
208eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	sdh_cmd = 0;
209eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	stat_mask = 0;
210eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
211eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	sdh_cmd |= cmd->opcode;
212eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
213eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (cmd->flags & MMC_RSP_PRESENT) {
214eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		sdh_cmd |= CMD_RSP;
215eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		stat_mask |= CMD_RESP_END;
216eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	} else {
217eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		stat_mask |= CMD_SENT;
218eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	}
219eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
220eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (cmd->flags & MMC_RSP_136)
221eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		sdh_cmd |= CMD_L_RSP;
222eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
223eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	stat_mask |= CMD_CRC_FAIL | CMD_TIME_OUT;
224eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
225eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	sdh_enable_stat_irq(host, stat_mask);
226eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
227eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	bfin_write_SDH_ARGUMENT(cmd->arg);
228eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	bfin_write_SDH_COMMAND(sdh_cmd | CMD_E);
229eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	bfin_write_SDH_CLK_CTL(bfin_read_SDH_CLK_CTL() | CLK_E);
230eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	SSYNC();
231eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai}
232eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
233eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Caistatic void sdh_finish_request(struct sdh_host *host, struct mmc_request *mrq)
234eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai{
235eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	dev_dbg(mmc_dev(host->mmc), "%s enter\n", __func__);
236eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	host->mrq = NULL;
237eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	host->cmd = NULL;
238eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	host->data = NULL;
239eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	mmc_request_done(host->mmc, mrq);
240eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai}
241eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
242eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Caistatic int sdh_cmd_done(struct sdh_host *host, unsigned int stat)
243eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai{
244eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	struct mmc_command *cmd = host->cmd;
245eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	int ret = 0;
246eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
247eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	dev_dbg(mmc_dev(host->mmc), "%s enter cmd: %p\n", __func__, cmd);
248eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (!cmd)
249eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		return 0;
250eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
251eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	host->cmd = NULL;
252eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
253eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (cmd->flags & MMC_RSP_PRESENT) {
254eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		cmd->resp[0] = bfin_read_SDH_RESPONSE0();
255eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		if (cmd->flags & MMC_RSP_136) {
256eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai			cmd->resp[1] = bfin_read_SDH_RESPONSE1();
257eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai			cmd->resp[2] = bfin_read_SDH_RESPONSE2();
258eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai			cmd->resp[3] = bfin_read_SDH_RESPONSE3();
259eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		}
260eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	}
261eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (stat & CMD_TIME_OUT)
262eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		cmd->error = -ETIMEDOUT;
263eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	else if (stat & CMD_CRC_FAIL && cmd->flags & MMC_RSP_CRC)
264eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		cmd->error = -EILSEQ;
265eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
266eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	sdh_disable_stat_irq(host, (CMD_SENT | CMD_RESP_END | CMD_TIME_OUT | CMD_CRC_FAIL));
267eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
268eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (host->data && !cmd->error) {
269eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		if (host->data->flags & MMC_DATA_WRITE) {
270eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai			ret = sdh_setup_data(host, host->data);
271eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai			if (ret)
272eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai				return 0;
273eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		}
274eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
275eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		sdh_enable_stat_irq(host, DAT_END | RX_OVERRUN | TX_UNDERRUN | DAT_TIME_OUT);
276eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	} else
277eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		sdh_finish_request(host, host->mrq);
278eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
279eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	return 1;
280eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai}
281eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
282eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Caistatic int sdh_data_done(struct sdh_host *host, unsigned int stat)
283eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai{
284eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	struct mmc_data *data = host->data;
285eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
286eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	dev_dbg(mmc_dev(host->mmc), "%s enter stat: 0x%x\n", __func__, stat);
287eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (!data)
288eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		return 0;
289eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
290eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	disable_dma(host->dma_ch);
291eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
292eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		     host->dma_dir);
293eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
294eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (stat & DAT_TIME_OUT)
295eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		data->error = -ETIMEDOUT;
296eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	else if (stat & DAT_CRC_FAIL)
297eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		data->error = -EILSEQ;
298eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	else if (stat & (RX_OVERRUN | TX_UNDERRUN))
299eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		data->error = -EIO;
300eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
301eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (!data->error)
302eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		data->bytes_xfered = data->blocks * data->blksz;
303eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	else
304eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		data->bytes_xfered = 0;
305eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
306eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	sdh_disable_stat_irq(host, DAT_END | DAT_TIME_OUT | DAT_CRC_FAIL | RX_OVERRUN | TX_UNDERRUN);
307eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	bfin_write_SDH_STATUS_CLR(DAT_END_STAT | DAT_TIMEOUT_STAT | \
308eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai			DAT_CRC_FAIL_STAT | DAT_BLK_END_STAT | RX_OVERRUN | TX_UNDERRUN);
309eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	bfin_write_SDH_DATA_CTL(0);
310eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	SSYNC();
311eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
312eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	host->data = NULL;
313eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (host->mrq->stop) {
314eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		sdh_stop_clock(host);
315eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		sdh_start_cmd(host, host->mrq->stop);
316eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	} else {
317eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		sdh_finish_request(host, host->mrq);
318eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	}
319eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
320eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	return 1;
321eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai}
322eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
323eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Caistatic void sdh_request(struct mmc_host *mmc, struct mmc_request *mrq)
324eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai{
325eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	struct sdh_host *host = mmc_priv(mmc);
326eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	int ret = 0;
327eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
328eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	dev_dbg(mmc_dev(host->mmc), "%s enter, mrp:%p, cmd:%p\n", __func__, mrq, mrq->cmd);
329eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	WARN_ON(host->mrq != NULL);
330eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
331eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	host->mrq = mrq;
332eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	host->data = mrq->data;
333eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
334eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (mrq->data && mrq->data->flags & MMC_DATA_READ) {
335eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		ret = sdh_setup_data(host, mrq->data);
336eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		if (ret)
337eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai			return;
338eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	}
339eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
340eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	sdh_start_cmd(host, mrq->cmd);
341eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai}
342eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
343eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Caistatic void sdh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
344eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai{
345eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	struct sdh_host *host;
346eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	unsigned long flags;
347eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	u16 clk_ctl = 0;
348eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	u16 pwr_ctl = 0;
349eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	u16 cfg;
350eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	host = mmc_priv(mmc);
351eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
352eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	spin_lock_irqsave(&host->lock, flags);
353eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (ios->clock) {
354eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		unsigned long  sys_clk, ios_clk;
355eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		unsigned char clk_div;
356eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		ios_clk = 2 * ios->clock;
357eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		sys_clk = get_sclk();
358eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		clk_div = sys_clk / ios_clk;
359eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		if (sys_clk % ios_clk == 0)
360eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai			clk_div -= 1;
361eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		clk_div = min_t(unsigned char, clk_div, 0xFF);
362eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		clk_ctl |= clk_div;
363eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		clk_ctl |= CLK_E;
364eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		host->clk_div = clk_div;
365eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	} else
366eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		sdh_stop_clock(host);
367eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
368eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
369eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#ifdef CONFIG_SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND
370eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		pwr_ctl |= ROD_CTL;
371eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#else
372eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		pwr_ctl |= SD_CMD_OD | ROD_CTL;
373eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#endif
374eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
375eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (ios->bus_width == MMC_BUS_WIDTH_4) {
376eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		cfg = bfin_read_SDH_CFG();
377eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		cfg &= ~PD_SDDAT3;
378eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		cfg |= PUP_SDDAT3;
379eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		/* Enable 4 bit SDIO */
380eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		cfg |= (SD4E | MWE);
381eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		bfin_write_SDH_CFG(cfg);
382eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		clk_ctl |= WIDE_BUS;
383eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	} else {
384eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		cfg = bfin_read_SDH_CFG();
385eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		cfg |= MWE;
386eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		bfin_write_SDH_CFG(cfg);
387eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	}
388eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
389eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	bfin_write_SDH_CLK_CTL(clk_ctl);
390eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
391eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	host->power_mode = ios->power_mode;
392eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (ios->power_mode == MMC_POWER_ON)
393eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		pwr_ctl |= PWR_ON;
394eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
395eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	bfin_write_SDH_PWR_CTL(pwr_ctl);
396eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	SSYNC();
397eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
398eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	spin_unlock_irqrestore(&host->lock, flags);
399eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
400eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	dev_dbg(mmc_dev(host->mmc), "SDH: clk_div = 0x%x actual clock:%ld expected clock:%d\n",
401eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		host->clk_div,
402eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		host->clk_div ? get_sclk() / (2 * (host->clk_div + 1)) : 0,
403eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		ios->clock);
404eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai}
405eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
406eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Caistatic const struct mmc_host_ops sdh_ops = {
407eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	.request	= sdh_request,
408eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	.set_ios	= sdh_set_ios,
409eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai};
410eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
411eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Caistatic irqreturn_t sdh_dma_irq(int irq, void *devid)
412eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai{
413eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	struct sdh_host *host = devid;
414eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
415eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	dev_dbg(mmc_dev(host->mmc), "%s enter, irq_stat: 0x%04x\n", __func__,
416eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		get_dma_curr_irqstat(host->dma_ch));
417eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	clear_dma_irqstat(host->dma_ch);
418eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	SSYNC();
419eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
420eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	return IRQ_HANDLED;
421eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai}
422eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
423eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Caistatic irqreturn_t sdh_stat_irq(int irq, void *devid)
424eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai{
425eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	struct sdh_host *host = devid;
426eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	unsigned int status;
427eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	int handled = 0;
428eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
429eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	dev_dbg(mmc_dev(host->mmc), "%s enter\n", __func__);
430eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	status = bfin_read_SDH_E_STATUS();
431eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (status & SD_CARD_DET) {
432eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		mmc_detect_change(host->mmc, 0);
433eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		bfin_write_SDH_E_STATUS(SD_CARD_DET);
434eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	}
435eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	status = bfin_read_SDH_STATUS();
436eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (status & (CMD_SENT | CMD_RESP_END | CMD_TIME_OUT | CMD_CRC_FAIL)) {
437eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		handled |= sdh_cmd_done(host, status);
438eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		bfin_write_SDH_STATUS_CLR(CMD_SENT_STAT | CMD_RESP_END_STAT | \
439eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai				CMD_TIMEOUT_STAT | CMD_CRC_FAIL_STAT);
440eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		SSYNC();
441eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	}
442eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
443eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	status = bfin_read_SDH_STATUS();
444eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (status & (DAT_END | DAT_TIME_OUT | DAT_CRC_FAIL | RX_OVERRUN | TX_UNDERRUN))
445eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		handled |= sdh_data_done(host, status);
446eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
447eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	dev_dbg(mmc_dev(host->mmc), "%s exit\n\n", __func__);
448eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
449eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	return IRQ_RETVAL(handled);
450eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai}
451eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
452eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Caistatic int __devinit sdh_probe(struct platform_device *pdev)
453eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai{
454eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	struct mmc_host *mmc;
455eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	struct sdh_host *host;
456eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	struct bfin_sd_host *drv_data = get_sdh_data(pdev);
457eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	int ret;
458eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
459eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (!drv_data) {
460eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		dev_err(&pdev->dev, "missing platform driver data\n");
461eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		ret = -EINVAL;
462eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		goto out;
463eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	}
464eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
465a34650f0f1ca589cda09c48cb62baf15e680a247Sonic Zhang	mmc = mmc_alloc_host(sizeof(struct sdh_host), &pdev->dev);
466eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (!mmc) {
467eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		ret = -ENOMEM;
468eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		goto out;
469eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	}
470eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
471eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	mmc->ops = &sdh_ops;
472a36274e0184193e393fb82957925c3981a6b0477Martin K. Petersen	mmc->max_segs = 32;
473eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	mmc->max_seg_size = 1 << 16;
474eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	mmc->max_blk_size = 1 << 11;
475eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	mmc->max_blk_count = 1 << 11;
476eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	mmc->max_req_size = PAGE_SIZE;
477eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
478eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	mmc->f_max = get_sclk();
479eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	mmc->f_min = mmc->f_max >> 9;
480eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_NEEDS_POLL;
481eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	host = mmc_priv(mmc);
482eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	host->mmc = mmc;
483eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
484eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	spin_lock_init(&host->lock);
485eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	host->irq = drv_data->irq_int0;
486eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	host->dma_ch = drv_data->dma_chan;
487eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
488eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	ret = request_dma(host->dma_ch, DRIVER_NAME "DMA");
489eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (ret) {
490eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		dev_err(&pdev->dev, "unable to request DMA channel\n");
491eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		goto out1;
492eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	}
493eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
494eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	ret = set_dma_callback(host->dma_ch, sdh_dma_irq, host);
495eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (ret) {
496eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		dev_err(&pdev->dev, "unable to request DMA irq\n");
497eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		goto out2;
498eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	}
499eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
500eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL);
501eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (host->sg_cpu == NULL) {
502eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		ret = -ENOMEM;
503eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		goto out2;
504eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	}
505eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
506eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	platform_set_drvdata(pdev, mmc);
507eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	mmc_add_host(mmc);
508eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
509eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	ret = request_irq(host->irq, sdh_stat_irq, 0, "SDH Status IRQ", host);
510eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (ret) {
511eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		dev_err(&pdev->dev, "unable to request status irq\n");
512eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		goto out3;
513eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	}
514eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
515eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	ret = peripheral_request_list(drv_data->pin_req, DRIVER_NAME);
516eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (ret) {
517eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		dev_err(&pdev->dev, "unable to request peripheral pins\n");
518eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		goto out4;
519eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	}
520eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#if defined(CONFIG_BF54x)
521eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	/* Secure Digital Host shares DMA with Nand controller */
522eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	bfin_write_DMAC1_PERIMUX(bfin_read_DMAC1_PERIMUX() | 0x1);
523eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#endif
524eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
525eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	bfin_write_SDH_CFG(bfin_read_SDH_CFG() | CLKS_EN);
526eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	SSYNC();
527eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
528eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	/* Disable card inserting detection pin. set MMC_CAP_NEES_POLL, and
529eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	 * mmc stack will do the detection.
530eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	 */
531eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	bfin_write_SDH_CFG((bfin_read_SDH_CFG() & 0x1F) | (PUP_SDDAT | PUP_SDDAT3));
532eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	SSYNC();
533eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
534eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	return 0;
535eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
536eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Caiout4:
537eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	free_irq(host->irq, host);
538eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Caiout3:
539eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	mmc_remove_host(mmc);
540eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
541eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Caiout2:
542eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	free_dma(host->dma_ch);
543eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Caiout1:
544eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	mmc_free_host(mmc);
545eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai out:
546eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	return ret;
547eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai}
548eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
549eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Caistatic int __devexit sdh_remove(struct platform_device *pdev)
550eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai{
551eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	struct mmc_host *mmc = platform_get_drvdata(pdev);
552eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
553eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	platform_set_drvdata(pdev, NULL);
554eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
555eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (mmc) {
556eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		struct sdh_host *host = mmc_priv(mmc);
557eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
558eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		mmc_remove_host(mmc);
559eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
560eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		sdh_stop_clock(host);
561eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		free_irq(host->irq, host);
562eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		free_dma(host->dma_ch);
563eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
564eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
565eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		mmc_free_host(mmc);
566eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	}
567eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
568eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	return 0;
569eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai}
570eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
571eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#ifdef CONFIG_PM
572eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Caistatic int sdh_suspend(struct platform_device *dev, pm_message_t state)
573eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai{
574eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	struct mmc_host *mmc = platform_get_drvdata(dev);
575eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	struct bfin_sd_host *drv_data = get_sdh_data(dev);
576eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	int ret = 0;
577eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
578eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (mmc)
5791a13f8fa76c880be41d6b1e6a2b44404bcbfdf9eMatt Fleming		ret = mmc_suspend_host(mmc);
580eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
581eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	bfin_write_SDH_PWR_CTL(bfin_read_SDH_PWR_CTL() & ~PWR_ON);
582eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	peripheral_free_list(drv_data->pin_req);
583eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
584eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	return ret;
585eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai}
586eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
587eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Caistatic int sdh_resume(struct platform_device *dev)
588eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai{
589eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	struct mmc_host *mmc = platform_get_drvdata(dev);
590eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	struct bfin_sd_host *drv_data = get_sdh_data(dev);
591eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	int ret = 0;
592eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
593eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	ret = peripheral_request_list(drv_data->pin_req, DRIVER_NAME);
594eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (ret) {
595eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		dev_err(&dev->dev, "unable to request peripheral pins\n");
596eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		return ret;
597eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	}
598eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
599eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	bfin_write_SDH_PWR_CTL(bfin_read_SDH_PWR_CTL() | PWR_ON);
600eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#if defined(CONFIG_BF54x)
601eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	/* Secure Digital Host shares DMA with Nand controller */
602eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	bfin_write_DMAC1_PERIMUX(bfin_read_DMAC1_PERIMUX() | 0x1);
603eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#endif
604eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	bfin_write_SDH_CFG(bfin_read_SDH_CFG() | CLKS_EN);
605eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	SSYNC();
606eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
607eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	bfin_write_SDH_CFG((bfin_read_SDH_CFG() & 0x1F) | (PUP_SDDAT | PUP_SDDAT3));
608eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	SSYNC();
609eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
610eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	if (mmc)
611eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		ret = mmc_resume_host(mmc);
612eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
613eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	return ret;
614eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai}
615eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#else
616eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai# define sdh_suspend NULL
617eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai# define sdh_resume  NULL
618eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai#endif
619eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
620eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Caistatic struct platform_driver sdh_driver = {
621eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	.probe   = sdh_probe,
622eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	.remove  = __devexit_p(sdh_remove),
623eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	.suspend = sdh_suspend,
624eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	.resume  = sdh_resume,
625eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	.driver  = {
626eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai		.name = DRIVER_NAME,
627eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai	},
628eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai};
629eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
630d1f81a64a4250bdd776978be06ae2b8e13ec7471Axel Linmodule_platform_driver(sdh_driver);
631eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff Cai
632eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff CaiMODULE_DESCRIPTION("Blackfin Secure Digital Host Driver");
633eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff CaiMODULE_AUTHOR("Cliff Cai, Roy Huang");
634eb962d5bb7da4f25e20e5d448ee3aac394144ff6Cliff CaiMODULE_LICENSE("GPL");
635