xref: /linux/drivers/mmc/host/omap.c (revision 6e0ee714fdab0568c3487455951dea2673e9557f)
11c6a0718SPierre Ossman /*
270f10482SPierre Ossman  *  linux/drivers/mmc/host/omap.c
31c6a0718SPierre Ossman  *
41c6a0718SPierre Ossman  *  Copyright (C) 2004 Nokia Corporation
5d36b6910SAl Viro  *  Written by Tuukka Tikkanen and Juha Yrjölä<juha.yrjola@nokia.com>
61c6a0718SPierre Ossman  *  Misc hacks here and there by Tony Lindgren <tony@atomide.com>
71c6a0718SPierre Ossman  *  Other hacks (DMA, SD, etc) by David Brownell
81c6a0718SPierre Ossman  *
91c6a0718SPierre Ossman  * This program is free software; you can redistribute it and/or modify
101c6a0718SPierre Ossman  * it under the terms of the GNU General Public License version 2 as
111c6a0718SPierre Ossman  * published by the Free Software Foundation.
121c6a0718SPierre Ossman  */
131c6a0718SPierre Ossman 
141c6a0718SPierre Ossman #include <linux/module.h>
151c6a0718SPierre Ossman #include <linux/moduleparam.h>
161c6a0718SPierre Ossman #include <linux/init.h>
171c6a0718SPierre Ossman #include <linux/ioport.h>
181c6a0718SPierre Ossman #include <linux/platform_device.h>
191c6a0718SPierre Ossman #include <linux/interrupt.h>
203451c067SRussell King #include <linux/dmaengine.h>
211c6a0718SPierre Ossman #include <linux/dma-mapping.h>
221c6a0718SPierre Ossman #include <linux/delay.h>
231c6a0718SPierre Ossman #include <linux/spinlock.h>
241c6a0718SPierre Ossman #include <linux/timer.h>
253451c067SRussell King #include <linux/omap-dma.h>
261c6a0718SPierre Ossman #include <linux/mmc/host.h>
271c6a0718SPierre Ossman #include <linux/mmc/card.h>
281c6a0718SPierre Ossman #include <linux/clk.h>
2945711f1aSJens Axboe #include <linux/scatterlist.h>
305a0e3ad6STejun Heo #include <linux/slab.h>
311c6a0718SPierre Ossman 
32ce491cf8STony Lindgren #include <plat/mmc.h>
33ce491cf8STony Lindgren #include <plat/dma.h>
341c6a0718SPierre Ossman 
351c6a0718SPierre Ossman #define	OMAP_MMC_REG_CMD	0x00
360e950fa6SMarek Belisko #define	OMAP_MMC_REG_ARGL	0x01
370e950fa6SMarek Belisko #define	OMAP_MMC_REG_ARGH	0x02
380e950fa6SMarek Belisko #define	OMAP_MMC_REG_CON	0x03
390e950fa6SMarek Belisko #define	OMAP_MMC_REG_STAT	0x04
400e950fa6SMarek Belisko #define	OMAP_MMC_REG_IE		0x05
410e950fa6SMarek Belisko #define	OMAP_MMC_REG_CTO	0x06
420e950fa6SMarek Belisko #define	OMAP_MMC_REG_DTO	0x07
430e950fa6SMarek Belisko #define	OMAP_MMC_REG_DATA	0x08
440e950fa6SMarek Belisko #define	OMAP_MMC_REG_BLEN	0x09
450e950fa6SMarek Belisko #define	OMAP_MMC_REG_NBLK	0x0a
460e950fa6SMarek Belisko #define	OMAP_MMC_REG_BUF	0x0b
470e950fa6SMarek Belisko #define	OMAP_MMC_REG_SDIO	0x0d
480e950fa6SMarek Belisko #define	OMAP_MMC_REG_REV	0x0f
490e950fa6SMarek Belisko #define	OMAP_MMC_REG_RSP0	0x10
500e950fa6SMarek Belisko #define	OMAP_MMC_REG_RSP1	0x11
510e950fa6SMarek Belisko #define	OMAP_MMC_REG_RSP2	0x12
520e950fa6SMarek Belisko #define	OMAP_MMC_REG_RSP3	0x13
530e950fa6SMarek Belisko #define	OMAP_MMC_REG_RSP4	0x14
540e950fa6SMarek Belisko #define	OMAP_MMC_REG_RSP5	0x15
550e950fa6SMarek Belisko #define	OMAP_MMC_REG_RSP6	0x16
560e950fa6SMarek Belisko #define	OMAP_MMC_REG_RSP7	0x17
570e950fa6SMarek Belisko #define	OMAP_MMC_REG_IOSR	0x18
580e950fa6SMarek Belisko #define	OMAP_MMC_REG_SYSC	0x19
590e950fa6SMarek Belisko #define	OMAP_MMC_REG_SYSS	0x1a
601c6a0718SPierre Ossman 
611c6a0718SPierre Ossman #define	OMAP_MMC_STAT_CARD_ERR		(1 << 14)
621c6a0718SPierre Ossman #define	OMAP_MMC_STAT_CARD_IRQ		(1 << 13)
631c6a0718SPierre Ossman #define	OMAP_MMC_STAT_OCR_BUSY		(1 << 12)
641c6a0718SPierre Ossman #define	OMAP_MMC_STAT_A_EMPTY		(1 << 11)
651c6a0718SPierre Ossman #define	OMAP_MMC_STAT_A_FULL		(1 << 10)
661c6a0718SPierre Ossman #define	OMAP_MMC_STAT_CMD_CRC		(1 <<  8)
671c6a0718SPierre Ossman #define	OMAP_MMC_STAT_CMD_TOUT		(1 <<  7)
681c6a0718SPierre Ossman #define	OMAP_MMC_STAT_DATA_CRC		(1 <<  6)
691c6a0718SPierre Ossman #define	OMAP_MMC_STAT_DATA_TOUT		(1 <<  5)
701c6a0718SPierre Ossman #define	OMAP_MMC_STAT_END_BUSY		(1 <<  4)
711c6a0718SPierre Ossman #define	OMAP_MMC_STAT_END_OF_DATA	(1 <<  3)
721c6a0718SPierre Ossman #define	OMAP_MMC_STAT_CARD_BUSY		(1 <<  2)
731c6a0718SPierre Ossman #define	OMAP_MMC_STAT_END_OF_CMD	(1 <<  0)
741c6a0718SPierre Ossman 
750e950fa6SMarek Belisko #define OMAP_MMC_REG(host, reg)		(OMAP_MMC_REG_##reg << (host)->reg_shift)
760e950fa6SMarek Belisko #define OMAP_MMC_READ(host, reg)	__raw_readw((host)->virt_base + OMAP_MMC_REG(host, reg))
770e950fa6SMarek Belisko #define OMAP_MMC_WRITE(host, reg, val)	__raw_writew((val), (host)->virt_base + OMAP_MMC_REG(host, reg))
781c6a0718SPierre Ossman 
791c6a0718SPierre Ossman /*
801c6a0718SPierre Ossman  * Command types
811c6a0718SPierre Ossman  */
821c6a0718SPierre Ossman #define OMAP_MMC_CMDTYPE_BC	0
831c6a0718SPierre Ossman #define OMAP_MMC_CMDTYPE_BCR	1
841c6a0718SPierre Ossman #define OMAP_MMC_CMDTYPE_AC	2
851c6a0718SPierre Ossman #define OMAP_MMC_CMDTYPE_ADTC	3
861c6a0718SPierre Ossman 
871c6a0718SPierre Ossman 
881c6a0718SPierre Ossman #define DRIVER_NAME "mmci-omap"
891c6a0718SPierre Ossman 
901c6a0718SPierre Ossman /* Specifies how often in millisecs to poll for card status changes
911c6a0718SPierre Ossman  * when the cover switch is open */
927584d276SJarkko Lavinen #define OMAP_MMC_COVER_POLL_DELAY	500
931c6a0718SPierre Ossman 
94abfbe5f7SJuha Yrjola struct mmc_omap_host;
95abfbe5f7SJuha Yrjola 
96abfbe5f7SJuha Yrjola struct mmc_omap_slot {
97abfbe5f7SJuha Yrjola 	int			id;
98abfbe5f7SJuha Yrjola 	unsigned int		vdd;
99abfbe5f7SJuha Yrjola 	u16			saved_con;
100abfbe5f7SJuha Yrjola 	u16			bus_mode;
101abfbe5f7SJuha Yrjola 	unsigned int		fclk_freq;
102abfbe5f7SJuha Yrjola 
1037584d276SJarkko Lavinen 	struct tasklet_struct	cover_tasklet;
1047584d276SJarkko Lavinen 	struct timer_list       cover_timer;
1055a0f3f1fSJuha Yrjola 	unsigned		cover_open;
1065a0f3f1fSJuha Yrjola 
107abfbe5f7SJuha Yrjola 	struct mmc_request      *mrq;
108abfbe5f7SJuha Yrjola 	struct mmc_omap_host    *host;
109abfbe5f7SJuha Yrjola 	struct mmc_host		*mmc;
110abfbe5f7SJuha Yrjola 	struct omap_mmc_slot_data *pdata;
111abfbe5f7SJuha Yrjola };
112abfbe5f7SJuha Yrjola 
1131c6a0718SPierre Ossman struct mmc_omap_host {
1141c6a0718SPierre Ossman 	int			initialized;
1151c6a0718SPierre Ossman 	int			suspended;
1161c6a0718SPierre Ossman 	struct mmc_request *	mrq;
1171c6a0718SPierre Ossman 	struct mmc_command *	cmd;
1181c6a0718SPierre Ossman 	struct mmc_data *	data;
1191c6a0718SPierre Ossman 	struct mmc_host *	mmc;
1201c6a0718SPierre Ossman 	struct device *		dev;
1211c6a0718SPierre Ossman 	unsigned char		id; /* 16xx chips have 2 MMC blocks */
1221c6a0718SPierre Ossman 	struct clk *		iclk;
1231c6a0718SPierre Ossman 	struct clk *		fclk;
1243451c067SRussell King 	struct dma_chan		*dma_rx;
1253451c067SRussell King 	u32			dma_rx_burst;
1263451c067SRussell King 	struct dma_chan		*dma_tx;
1273451c067SRussell King 	u32			dma_tx_burst;
1281c6a0718SPierre Ossman 	struct resource		*mem_res;
1291c6a0718SPierre Ossman 	void __iomem		*virt_base;
1301c6a0718SPierre Ossman 	unsigned int		phys_base;
1311c6a0718SPierre Ossman 	int			irq;
1321c6a0718SPierre Ossman 	unsigned char		bus_mode;
1330e950fa6SMarek Belisko 	unsigned int		reg_shift;
1341c6a0718SPierre Ossman 
1350fb4723dSJarkko Lavinen 	struct work_struct	cmd_abort_work;
1360fb4723dSJarkko Lavinen 	unsigned		abort:1;
1370fb4723dSJarkko Lavinen 	struct timer_list	cmd_abort_timer;
138eb1860bcSJarkko Lavinen 
1390f602ec7SJarkko Lavinen 	struct work_struct      slot_release_work;
1400f602ec7SJarkko Lavinen 	struct mmc_omap_slot    *next_slot;
1410f602ec7SJarkko Lavinen 	struct work_struct      send_stop_work;
1420f602ec7SJarkko Lavinen 	struct mmc_data		*stop_data;
1430f602ec7SJarkko Lavinen 
1441c6a0718SPierre Ossman 	unsigned int		sg_len;
1451c6a0718SPierre Ossman 	int			sg_idx;
1461c6a0718SPierre Ossman 	u16 *			buffer;
1471c6a0718SPierre Ossman 	u32			buffer_bytes_left;
1481c6a0718SPierre Ossman 	u32			total_bytes_left;
1491c6a0718SPierre Ossman 
1501c6a0718SPierre Ossman 	unsigned		use_dma:1;
1511c6a0718SPierre Ossman 	unsigned		brs_received:1, dma_done:1;
1521c6a0718SPierre Ossman 	unsigned		dma_in_use:1;
1533451c067SRussell King 	spinlock_t		dma_lock;
1541c6a0718SPierre Ossman 
155abfbe5f7SJuha Yrjola 	struct mmc_omap_slot    *slots[OMAP_MMC_MAX_SLOTS];
156abfbe5f7SJuha Yrjola 	struct mmc_omap_slot    *current_slot;
157abfbe5f7SJuha Yrjola 	spinlock_t              slot_lock;
158abfbe5f7SJuha Yrjola 	wait_queue_head_t       slot_wq;
159abfbe5f7SJuha Yrjola 	int                     nr_slots;
160abfbe5f7SJuha Yrjola 
1610807a9b5SJarkko Lavinen 	struct timer_list       clk_timer;
1620807a9b5SJarkko Lavinen 	spinlock_t		clk_lock;     /* for changing enabled state */
1630807a9b5SJarkko Lavinen 	unsigned int            fclk_enabled:1;
164b01a4f1cSVenkatraman S 	struct workqueue_struct *mmc_omap_wq;
1650807a9b5SJarkko Lavinen 
166abfbe5f7SJuha Yrjola 	struct omap_mmc_platform_data *pdata;
1671c6a0718SPierre Ossman };
1681c6a0718SPierre Ossman 
1690d9ee5b2STejun Heo 
1707c8ad982SRussell King static void mmc_omap_fclk_offdelay(struct mmc_omap_slot *slot)
1710807a9b5SJarkko Lavinen {
1720807a9b5SJarkko Lavinen 	unsigned long tick_ns;
1730807a9b5SJarkko Lavinen 
1740807a9b5SJarkko Lavinen 	if (slot != NULL && slot->host->fclk_enabled && slot->fclk_freq > 0) {
1750807a9b5SJarkko Lavinen 		tick_ns = (1000000000 + slot->fclk_freq - 1) / slot->fclk_freq;
1760807a9b5SJarkko Lavinen 		ndelay(8 * tick_ns);
1770807a9b5SJarkko Lavinen 	}
1780807a9b5SJarkko Lavinen }
1790807a9b5SJarkko Lavinen 
1807c8ad982SRussell King static void mmc_omap_fclk_enable(struct mmc_omap_host *host, unsigned int enable)
1810807a9b5SJarkko Lavinen {
1820807a9b5SJarkko Lavinen 	unsigned long flags;
1830807a9b5SJarkko Lavinen 
1840807a9b5SJarkko Lavinen 	spin_lock_irqsave(&host->clk_lock, flags);
1850807a9b5SJarkko Lavinen 	if (host->fclk_enabled != enable) {
1860807a9b5SJarkko Lavinen 		host->fclk_enabled = enable;
1870807a9b5SJarkko Lavinen 		if (enable)
1880807a9b5SJarkko Lavinen 			clk_enable(host->fclk);
1890807a9b5SJarkko Lavinen 		else
1900807a9b5SJarkko Lavinen 			clk_disable(host->fclk);
1910807a9b5SJarkko Lavinen 	}
1920807a9b5SJarkko Lavinen 	spin_unlock_irqrestore(&host->clk_lock, flags);
1930807a9b5SJarkko Lavinen }
1940807a9b5SJarkko Lavinen 
195abfbe5f7SJuha Yrjola static void mmc_omap_select_slot(struct mmc_omap_slot *slot, int claimed)
196abfbe5f7SJuha Yrjola {
197abfbe5f7SJuha Yrjola 	struct mmc_omap_host *host = slot->host;
198abfbe5f7SJuha Yrjola 	unsigned long flags;
199abfbe5f7SJuha Yrjola 
200abfbe5f7SJuha Yrjola 	if (claimed)
201abfbe5f7SJuha Yrjola 		goto no_claim;
202abfbe5f7SJuha Yrjola 	spin_lock_irqsave(&host->slot_lock, flags);
203abfbe5f7SJuha Yrjola 	while (host->mmc != NULL) {
204abfbe5f7SJuha Yrjola 		spin_unlock_irqrestore(&host->slot_lock, flags);
205abfbe5f7SJuha Yrjola 		wait_event(host->slot_wq, host->mmc == NULL);
206abfbe5f7SJuha Yrjola 		spin_lock_irqsave(&host->slot_lock, flags);
207abfbe5f7SJuha Yrjola 	}
208abfbe5f7SJuha Yrjola 	host->mmc = slot->mmc;
209abfbe5f7SJuha Yrjola 	spin_unlock_irqrestore(&host->slot_lock, flags);
210abfbe5f7SJuha Yrjola no_claim:
2110807a9b5SJarkko Lavinen 	del_timer(&host->clk_timer);
2120807a9b5SJarkko Lavinen 	if (host->current_slot != slot || !claimed)
2130807a9b5SJarkko Lavinen 		mmc_omap_fclk_offdelay(host->current_slot);
2140807a9b5SJarkko Lavinen 
215abfbe5f7SJuha Yrjola 	if (host->current_slot != slot) {
2160807a9b5SJarkko Lavinen 		OMAP_MMC_WRITE(host, CON, slot->saved_con & 0xFC00);
217abfbe5f7SJuha Yrjola 		if (host->pdata->switch_slot != NULL)
218abfbe5f7SJuha Yrjola 			host->pdata->switch_slot(mmc_dev(slot->mmc), slot->id);
219abfbe5f7SJuha Yrjola 		host->current_slot = slot;
220abfbe5f7SJuha Yrjola 	}
221abfbe5f7SJuha Yrjola 
2220807a9b5SJarkko Lavinen 	if (claimed) {
2230807a9b5SJarkko Lavinen 		mmc_omap_fclk_enable(host, 1);
2240807a9b5SJarkko Lavinen 
225abfbe5f7SJuha Yrjola 		/* Doing the dummy read here seems to work around some bug
226abfbe5f7SJuha Yrjola 		 * at least in OMAP24xx silicon where the command would not
227abfbe5f7SJuha Yrjola 		 * start after writing the CMD register. Sigh. */
228abfbe5f7SJuha Yrjola 		OMAP_MMC_READ(host, CON);
229abfbe5f7SJuha Yrjola 
230abfbe5f7SJuha Yrjola 		OMAP_MMC_WRITE(host, CON, slot->saved_con);
2310807a9b5SJarkko Lavinen 	} else
2320807a9b5SJarkko Lavinen 		mmc_omap_fclk_enable(host, 0);
233abfbe5f7SJuha Yrjola }
234abfbe5f7SJuha Yrjola 
235abfbe5f7SJuha Yrjola static void mmc_omap_start_request(struct mmc_omap_host *host,
236abfbe5f7SJuha Yrjola 				   struct mmc_request *req);
237abfbe5f7SJuha Yrjola 
2380f602ec7SJarkko Lavinen static void mmc_omap_slot_release_work(struct work_struct *work)
2390f602ec7SJarkko Lavinen {
2400f602ec7SJarkko Lavinen 	struct mmc_omap_host *host = container_of(work, struct mmc_omap_host,
2410f602ec7SJarkko Lavinen 						  slot_release_work);
2420f602ec7SJarkko Lavinen 	struct mmc_omap_slot *next_slot = host->next_slot;
2430f602ec7SJarkko Lavinen 	struct mmc_request *rq;
2440f602ec7SJarkko Lavinen 
2450f602ec7SJarkko Lavinen 	host->next_slot = NULL;
2460f602ec7SJarkko Lavinen 	mmc_omap_select_slot(next_slot, 1);
2470f602ec7SJarkko Lavinen 
2480f602ec7SJarkko Lavinen 	rq = next_slot->mrq;
2490f602ec7SJarkko Lavinen 	next_slot->mrq = NULL;
2500f602ec7SJarkko Lavinen 	mmc_omap_start_request(host, rq);
2510f602ec7SJarkko Lavinen }
2520f602ec7SJarkko Lavinen 
2530807a9b5SJarkko Lavinen static void mmc_omap_release_slot(struct mmc_omap_slot *slot, int clk_enabled)
254abfbe5f7SJuha Yrjola {
255abfbe5f7SJuha Yrjola 	struct mmc_omap_host *host = slot->host;
256abfbe5f7SJuha Yrjola 	unsigned long flags;
257abfbe5f7SJuha Yrjola 	int i;
258abfbe5f7SJuha Yrjola 
259abfbe5f7SJuha Yrjola 	BUG_ON(slot == NULL || host->mmc == NULL);
2600807a9b5SJarkko Lavinen 
2610807a9b5SJarkko Lavinen 	if (clk_enabled)
2620807a9b5SJarkko Lavinen 		/* Keeps clock running for at least 8 cycles on valid freq */
2630807a9b5SJarkko Lavinen 		mod_timer(&host->clk_timer, jiffies  + HZ/10);
2640807a9b5SJarkko Lavinen 	else {
2650807a9b5SJarkko Lavinen 		del_timer(&host->clk_timer);
2660807a9b5SJarkko Lavinen 		mmc_omap_fclk_offdelay(slot);
2670807a9b5SJarkko Lavinen 		mmc_omap_fclk_enable(host, 0);
2680807a9b5SJarkko Lavinen 	}
269abfbe5f7SJuha Yrjola 
270abfbe5f7SJuha Yrjola 	spin_lock_irqsave(&host->slot_lock, flags);
271abfbe5f7SJuha Yrjola 	/* Check for any pending requests */
272abfbe5f7SJuha Yrjola 	for (i = 0; i < host->nr_slots; i++) {
273abfbe5f7SJuha Yrjola 		struct mmc_omap_slot *new_slot;
274abfbe5f7SJuha Yrjola 
275abfbe5f7SJuha Yrjola 		if (host->slots[i] == NULL || host->slots[i]->mrq == NULL)
276abfbe5f7SJuha Yrjola 			continue;
277abfbe5f7SJuha Yrjola 
2780f602ec7SJarkko Lavinen 		BUG_ON(host->next_slot != NULL);
279abfbe5f7SJuha Yrjola 		new_slot = host->slots[i];
280abfbe5f7SJuha Yrjola 		/* The current slot should not have a request in queue */
281abfbe5f7SJuha Yrjola 		BUG_ON(new_slot == host->current_slot);
282abfbe5f7SJuha Yrjola 
2830f602ec7SJarkko Lavinen 		host->next_slot = new_slot;
284abfbe5f7SJuha Yrjola 		host->mmc = new_slot->mmc;
285abfbe5f7SJuha Yrjola 		spin_unlock_irqrestore(&host->slot_lock, flags);
286b01a4f1cSVenkatraman S 		queue_work(host->mmc_omap_wq, &host->slot_release_work);
287abfbe5f7SJuha Yrjola 		return;
288abfbe5f7SJuha Yrjola 	}
289abfbe5f7SJuha Yrjola 
290abfbe5f7SJuha Yrjola 	host->mmc = NULL;
291abfbe5f7SJuha Yrjola 	wake_up(&host->slot_wq);
292abfbe5f7SJuha Yrjola 	spin_unlock_irqrestore(&host->slot_lock, flags);
293abfbe5f7SJuha Yrjola }
294abfbe5f7SJuha Yrjola 
2955a0f3f1fSJuha Yrjola static inline
2965a0f3f1fSJuha Yrjola int mmc_omap_cover_is_open(struct mmc_omap_slot *slot)
2975a0f3f1fSJuha Yrjola {
2988348f002SKyungmin Park 	if (slot->pdata->get_cover_state)
2998348f002SKyungmin Park 		return slot->pdata->get_cover_state(mmc_dev(slot->mmc),
3008348f002SKyungmin Park 						    slot->id);
3018348f002SKyungmin Park 	return 0;
3025a0f3f1fSJuha Yrjola }
3035a0f3f1fSJuha Yrjola 
3045a0f3f1fSJuha Yrjola static ssize_t
3055a0f3f1fSJuha Yrjola mmc_omap_show_cover_switch(struct device *dev, struct device_attribute *attr,
3065a0f3f1fSJuha Yrjola 			   char *buf)
3075a0f3f1fSJuha Yrjola {
3085a0f3f1fSJuha Yrjola 	struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev);
3095a0f3f1fSJuha Yrjola 	struct mmc_omap_slot *slot = mmc_priv(mmc);
3105a0f3f1fSJuha Yrjola 
3115a0f3f1fSJuha Yrjola 	return sprintf(buf, "%s\n", mmc_omap_cover_is_open(slot) ? "open" :
3125a0f3f1fSJuha Yrjola 		       "closed");
3135a0f3f1fSJuha Yrjola }
3145a0f3f1fSJuha Yrjola 
3155a0f3f1fSJuha Yrjola static DEVICE_ATTR(cover_switch, S_IRUGO, mmc_omap_show_cover_switch, NULL);
3165a0f3f1fSJuha Yrjola 
317abfbe5f7SJuha Yrjola static ssize_t
318abfbe5f7SJuha Yrjola mmc_omap_show_slot_name(struct device *dev, struct device_attribute *attr,
319abfbe5f7SJuha Yrjola 			char *buf)
320abfbe5f7SJuha Yrjola {
321abfbe5f7SJuha Yrjola 	struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev);
322abfbe5f7SJuha Yrjola 	struct mmc_omap_slot *slot = mmc_priv(mmc);
323abfbe5f7SJuha Yrjola 
324abfbe5f7SJuha Yrjola 	return sprintf(buf, "%s\n", slot->pdata->name);
325abfbe5f7SJuha Yrjola }
326abfbe5f7SJuha Yrjola 
327abfbe5f7SJuha Yrjola static DEVICE_ATTR(slot_name, S_IRUGO, mmc_omap_show_slot_name, NULL);
328abfbe5f7SJuha Yrjola 
3291c6a0718SPierre Ossman static void
3301c6a0718SPierre Ossman mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd)
3311c6a0718SPierre Ossman {
3321c6a0718SPierre Ossman 	u32 cmdreg;
3331c6a0718SPierre Ossman 	u32 resptype;
3341c6a0718SPierre Ossman 	u32 cmdtype;
3351c6a0718SPierre Ossman 
3361c6a0718SPierre Ossman 	host->cmd = cmd;
3371c6a0718SPierre Ossman 
3381c6a0718SPierre Ossman 	resptype = 0;
3391c6a0718SPierre Ossman 	cmdtype = 0;
3401c6a0718SPierre Ossman 
3411c6a0718SPierre Ossman 	/* Our hardware needs to know exact type */
3421c6a0718SPierre Ossman 	switch (mmc_resp_type(cmd)) {
3431c6a0718SPierre Ossman 	case MMC_RSP_NONE:
3441c6a0718SPierre Ossman 		break;
3451c6a0718SPierre Ossman 	case MMC_RSP_R1:
3461c6a0718SPierre Ossman 	case MMC_RSP_R1B:
3471c6a0718SPierre Ossman 		/* resp 1, 1b, 6, 7 */
3481c6a0718SPierre Ossman 		resptype = 1;
3491c6a0718SPierre Ossman 		break;
3501c6a0718SPierre Ossman 	case MMC_RSP_R2:
3511c6a0718SPierre Ossman 		resptype = 2;
3521c6a0718SPierre Ossman 		break;
3531c6a0718SPierre Ossman 	case MMC_RSP_R3:
3541c6a0718SPierre Ossman 		resptype = 3;
3551c6a0718SPierre Ossman 		break;
3561c6a0718SPierre Ossman 	default:
3571c6a0718SPierre Ossman 		dev_err(mmc_dev(host->mmc), "Invalid response type: %04x\n", mmc_resp_type(cmd));
3581c6a0718SPierre Ossman 		break;
3591c6a0718SPierre Ossman 	}
3601c6a0718SPierre Ossman 
3611c6a0718SPierre Ossman 	if (mmc_cmd_type(cmd) == MMC_CMD_ADTC) {
3621c6a0718SPierre Ossman 		cmdtype = OMAP_MMC_CMDTYPE_ADTC;
3631c6a0718SPierre Ossman 	} else if (mmc_cmd_type(cmd) == MMC_CMD_BC) {
3641c6a0718SPierre Ossman 		cmdtype = OMAP_MMC_CMDTYPE_BC;
3651c6a0718SPierre Ossman 	} else if (mmc_cmd_type(cmd) == MMC_CMD_BCR) {
3661c6a0718SPierre Ossman 		cmdtype = OMAP_MMC_CMDTYPE_BCR;
3671c6a0718SPierre Ossman 	} else {
3681c6a0718SPierre Ossman 		cmdtype = OMAP_MMC_CMDTYPE_AC;
3691c6a0718SPierre Ossman 	}
3701c6a0718SPierre Ossman 
3711c6a0718SPierre Ossman 	cmdreg = cmd->opcode | (resptype << 8) | (cmdtype << 12);
3721c6a0718SPierre Ossman 
373abfbe5f7SJuha Yrjola 	if (host->current_slot->bus_mode == MMC_BUSMODE_OPENDRAIN)
3741c6a0718SPierre Ossman 		cmdreg |= 1 << 6;
3751c6a0718SPierre Ossman 
3761c6a0718SPierre Ossman 	if (cmd->flags & MMC_RSP_BUSY)
3771c6a0718SPierre Ossman 		cmdreg |= 1 << 11;
3781c6a0718SPierre Ossman 
3791c6a0718SPierre Ossman 	if (host->data && !(host->data->flags & MMC_DATA_WRITE))
3801c6a0718SPierre Ossman 		cmdreg |= 1 << 15;
3811c6a0718SPierre Ossman 
3820fb4723dSJarkko Lavinen 	mod_timer(&host->cmd_abort_timer, jiffies + HZ/2);
383eb1860bcSJarkko Lavinen 
3841c6a0718SPierre Ossman 	OMAP_MMC_WRITE(host, CTO, 200);
3851c6a0718SPierre Ossman 	OMAP_MMC_WRITE(host, ARGL, cmd->arg & 0xffff);
3861c6a0718SPierre Ossman 	OMAP_MMC_WRITE(host, ARGH, cmd->arg >> 16);
3871c6a0718SPierre Ossman 	OMAP_MMC_WRITE(host, IE,
3881c6a0718SPierre Ossman 		       OMAP_MMC_STAT_A_EMPTY    | OMAP_MMC_STAT_A_FULL    |
3891c6a0718SPierre Ossman 		       OMAP_MMC_STAT_CMD_CRC    | OMAP_MMC_STAT_CMD_TOUT  |
3901c6a0718SPierre Ossman 		       OMAP_MMC_STAT_DATA_CRC   | OMAP_MMC_STAT_DATA_TOUT |
3911c6a0718SPierre Ossman 		       OMAP_MMC_STAT_END_OF_CMD | OMAP_MMC_STAT_CARD_ERR  |
3921c6a0718SPierre Ossman 		       OMAP_MMC_STAT_END_OF_DATA);
3931c6a0718SPierre Ossman 	OMAP_MMC_WRITE(host, CMD, cmdreg);
3941c6a0718SPierre Ossman }
3951c6a0718SPierre Ossman 
3961c6a0718SPierre Ossman static void
397a914ded2SJuha Yrjola mmc_omap_release_dma(struct mmc_omap_host *host, struct mmc_data *data,
398a914ded2SJuha Yrjola 		     int abort)
3991c6a0718SPierre Ossman {
4001c6a0718SPierre Ossman 	enum dma_data_direction dma_data_dir;
4013451c067SRussell King 	struct device *dev = mmc_dev(host->mmc);
4023451c067SRussell King 	struct dma_chan *c;
4031c6a0718SPierre Ossman 
4043451c067SRussell King 	if (data->flags & MMC_DATA_WRITE) {
4051c6a0718SPierre Ossman 		dma_data_dir = DMA_TO_DEVICE;
4063451c067SRussell King 		c = host->dma_tx;
4073451c067SRussell King 	} else {
4081c6a0718SPierre Ossman 		dma_data_dir = DMA_FROM_DEVICE;
4093451c067SRussell King 		c = host->dma_rx;
4103451c067SRussell King 	}
4113451c067SRussell King 	if (c) {
4123451c067SRussell King 		if (data->error) {
4133451c067SRussell King 			dmaengine_terminate_all(c);
4143451c067SRussell King 			/* Claim nothing transferred on error... */
4153451c067SRussell King 			data->bytes_xfered = 0;
4163451c067SRussell King 		}
4173451c067SRussell King 		dev = c->device->dev;
4183451c067SRussell King 	}
4193451c067SRussell King 	dma_unmap_sg(dev, data->sg, host->sg_len, dma_data_dir);
4201c6a0718SPierre Ossman }
421a914ded2SJuha Yrjola 
4220f602ec7SJarkko Lavinen static void mmc_omap_send_stop_work(struct work_struct *work)
4230f602ec7SJarkko Lavinen {
4240f602ec7SJarkko Lavinen 	struct mmc_omap_host *host = container_of(work, struct mmc_omap_host,
4250f602ec7SJarkko Lavinen 						  send_stop_work);
4260f602ec7SJarkko Lavinen 	struct mmc_omap_slot *slot = host->current_slot;
4270f602ec7SJarkko Lavinen 	struct mmc_data *data = host->stop_data;
4280f602ec7SJarkko Lavinen 	unsigned long tick_ns;
4290f602ec7SJarkko Lavinen 
4300f602ec7SJarkko Lavinen 	tick_ns = (1000000000 + slot->fclk_freq - 1)/slot->fclk_freq;
4310f602ec7SJarkko Lavinen 	ndelay(8*tick_ns);
4320f602ec7SJarkko Lavinen 
4330f602ec7SJarkko Lavinen 	mmc_omap_start_command(host, data->stop);
4340f602ec7SJarkko Lavinen }
4350f602ec7SJarkko Lavinen 
436a914ded2SJuha Yrjola static void
437a914ded2SJuha Yrjola mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
438a914ded2SJuha Yrjola {
439a914ded2SJuha Yrjola 	if (host->dma_in_use)
440a914ded2SJuha Yrjola 		mmc_omap_release_dma(host, data, data->error);
441a914ded2SJuha Yrjola 
4421c6a0718SPierre Ossman 	host->data = NULL;
4431c6a0718SPierre Ossman 	host->sg_len = 0;
4441c6a0718SPierre Ossman 
4451c6a0718SPierre Ossman 	/* NOTE:  MMC layer will sometimes poll-wait CMD13 next, issuing
4461c6a0718SPierre Ossman 	 * dozens of requests until the card finishes writing data.
4471c6a0718SPierre Ossman 	 * It'd be cheaper to just wait till an EOFB interrupt arrives...
4481c6a0718SPierre Ossman 	 */
4491c6a0718SPierre Ossman 
4501c6a0718SPierre Ossman 	if (!data->stop) {
451a914ded2SJuha Yrjola 		struct mmc_host *mmc;
452a914ded2SJuha Yrjola 
4531c6a0718SPierre Ossman 		host->mrq = NULL;
454a914ded2SJuha Yrjola 		mmc = host->mmc;
4550807a9b5SJarkko Lavinen 		mmc_omap_release_slot(host->current_slot, 1);
456a914ded2SJuha Yrjola 		mmc_request_done(mmc, data->mrq);
4571c6a0718SPierre Ossman 		return;
4581c6a0718SPierre Ossman 	}
4591c6a0718SPierre Ossman 
4600f602ec7SJarkko Lavinen 	host->stop_data = data;
461b01a4f1cSVenkatraman S 	queue_work(host->mmc_omap_wq, &host->send_stop_work);
4621c6a0718SPierre Ossman }
4631c6a0718SPierre Ossman 
4641c6a0718SPierre Ossman static void
4650fb4723dSJarkko Lavinen mmc_omap_send_abort(struct mmc_omap_host *host, int maxloops)
466eb1860bcSJarkko Lavinen {
467eb1860bcSJarkko Lavinen 	struct mmc_omap_slot *slot = host->current_slot;
468eb1860bcSJarkko Lavinen 	unsigned int restarts, passes, timeout;
469eb1860bcSJarkko Lavinen 	u16 stat = 0;
470eb1860bcSJarkko Lavinen 
471eb1860bcSJarkko Lavinen 	/* Sending abort takes 80 clocks. Have some extra and round up */
472eb1860bcSJarkko Lavinen 	timeout = (120*1000000 + slot->fclk_freq - 1)/slot->fclk_freq;
473eb1860bcSJarkko Lavinen 	restarts = 0;
4740fb4723dSJarkko Lavinen 	while (restarts < maxloops) {
475eb1860bcSJarkko Lavinen 		OMAP_MMC_WRITE(host, STAT, 0xFFFF);
476eb1860bcSJarkko Lavinen 		OMAP_MMC_WRITE(host, CMD, (3 << 12) | (1 << 7));
477eb1860bcSJarkko Lavinen 
478eb1860bcSJarkko Lavinen 		passes = 0;
479eb1860bcSJarkko Lavinen 		while (passes < timeout) {
480eb1860bcSJarkko Lavinen 			stat = OMAP_MMC_READ(host, STAT);
481eb1860bcSJarkko Lavinen 			if (stat & OMAP_MMC_STAT_END_OF_CMD)
482eb1860bcSJarkko Lavinen 				goto out;
483eb1860bcSJarkko Lavinen 			udelay(1);
484eb1860bcSJarkko Lavinen 			passes++;
485eb1860bcSJarkko Lavinen 		}
486eb1860bcSJarkko Lavinen 
487eb1860bcSJarkko Lavinen 		restarts++;
488eb1860bcSJarkko Lavinen 	}
489eb1860bcSJarkko Lavinen out:
490eb1860bcSJarkko Lavinen 	OMAP_MMC_WRITE(host, STAT, stat);
491eb1860bcSJarkko Lavinen }
492eb1860bcSJarkko Lavinen 
493eb1860bcSJarkko Lavinen static void
494a914ded2SJuha Yrjola mmc_omap_abort_xfer(struct mmc_omap_host *host, struct mmc_data *data)
495a914ded2SJuha Yrjola {
496a914ded2SJuha Yrjola 	if (host->dma_in_use)
497a914ded2SJuha Yrjola 		mmc_omap_release_dma(host, data, 1);
498a914ded2SJuha Yrjola 
499a914ded2SJuha Yrjola 	host->data = NULL;
500a914ded2SJuha Yrjola 	host->sg_len = 0;
501a914ded2SJuha Yrjola 
5020fb4723dSJarkko Lavinen 	mmc_omap_send_abort(host, 10000);
503a914ded2SJuha Yrjola }
504a914ded2SJuha Yrjola 
505a914ded2SJuha Yrjola static void
5061c6a0718SPierre Ossman mmc_omap_end_of_data(struct mmc_omap_host *host, struct mmc_data *data)
5071c6a0718SPierre Ossman {
5081c6a0718SPierre Ossman 	unsigned long flags;
5091c6a0718SPierre Ossman 	int done;
5101c6a0718SPierre Ossman 
5111c6a0718SPierre Ossman 	if (!host->dma_in_use) {
5121c6a0718SPierre Ossman 		mmc_omap_xfer_done(host, data);
5131c6a0718SPierre Ossman 		return;
5141c6a0718SPierre Ossman 	}
5151c6a0718SPierre Ossman 	done = 0;
5161c6a0718SPierre Ossman 	spin_lock_irqsave(&host->dma_lock, flags);
5171c6a0718SPierre Ossman 	if (host->dma_done)
5181c6a0718SPierre Ossman 		done = 1;
5191c6a0718SPierre Ossman 	else
5201c6a0718SPierre Ossman 		host->brs_received = 1;
5211c6a0718SPierre Ossman 	spin_unlock_irqrestore(&host->dma_lock, flags);
5221c6a0718SPierre Ossman 	if (done)
5231c6a0718SPierre Ossman 		mmc_omap_xfer_done(host, data);
5241c6a0718SPierre Ossman }
5251c6a0718SPierre Ossman 
5261c6a0718SPierre Ossman static void
5271c6a0718SPierre Ossman mmc_omap_dma_done(struct mmc_omap_host *host, struct mmc_data *data)
5281c6a0718SPierre Ossman {
5291c6a0718SPierre Ossman 	unsigned long flags;
5301c6a0718SPierre Ossman 	int done;
5311c6a0718SPierre Ossman 
5321c6a0718SPierre Ossman 	done = 0;
5331c6a0718SPierre Ossman 	spin_lock_irqsave(&host->dma_lock, flags);
5341c6a0718SPierre Ossman 	if (host->brs_received)
5351c6a0718SPierre Ossman 		done = 1;
5361c6a0718SPierre Ossman 	else
5371c6a0718SPierre Ossman 		host->dma_done = 1;
5381c6a0718SPierre Ossman 	spin_unlock_irqrestore(&host->dma_lock, flags);
5391c6a0718SPierre Ossman 	if (done)
5401c6a0718SPierre Ossman 		mmc_omap_xfer_done(host, data);
5411c6a0718SPierre Ossman }
5421c6a0718SPierre Ossman 
5431c6a0718SPierre Ossman static void
5441c6a0718SPierre Ossman mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
5451c6a0718SPierre Ossman {
5461c6a0718SPierre Ossman 	host->cmd = NULL;
5471c6a0718SPierre Ossman 
5480fb4723dSJarkko Lavinen 	del_timer(&host->cmd_abort_timer);
549eb1860bcSJarkko Lavinen 
5501c6a0718SPierre Ossman 	if (cmd->flags & MMC_RSP_PRESENT) {
5511c6a0718SPierre Ossman 		if (cmd->flags & MMC_RSP_136) {
5521c6a0718SPierre Ossman 			/* response type 2 */
5531c6a0718SPierre Ossman 			cmd->resp[3] =
5541c6a0718SPierre Ossman 				OMAP_MMC_READ(host, RSP0) |
5551c6a0718SPierre Ossman 				(OMAP_MMC_READ(host, RSP1) << 16);
5561c6a0718SPierre Ossman 			cmd->resp[2] =
5571c6a0718SPierre Ossman 				OMAP_MMC_READ(host, RSP2) |
5581c6a0718SPierre Ossman 				(OMAP_MMC_READ(host, RSP3) << 16);
5591c6a0718SPierre Ossman 			cmd->resp[1] =
5601c6a0718SPierre Ossman 				OMAP_MMC_READ(host, RSP4) |
5611c6a0718SPierre Ossman 				(OMAP_MMC_READ(host, RSP5) << 16);
5621c6a0718SPierre Ossman 			cmd->resp[0] =
5631c6a0718SPierre Ossman 				OMAP_MMC_READ(host, RSP6) |
5641c6a0718SPierre Ossman 				(OMAP_MMC_READ(host, RSP7) << 16);
5651c6a0718SPierre Ossman 		} else {
5661c6a0718SPierre Ossman 			/* response types 1, 1b, 3, 4, 5, 6 */
5671c6a0718SPierre Ossman 			cmd->resp[0] =
5681c6a0718SPierre Ossman 				OMAP_MMC_READ(host, RSP6) |
5691c6a0718SPierre Ossman 				(OMAP_MMC_READ(host, RSP7) << 16);
5701c6a0718SPierre Ossman 		}
5711c6a0718SPierre Ossman 	}
5721c6a0718SPierre Ossman 
57317b0429dSPierre Ossman 	if (host->data == NULL || cmd->error) {
574a914ded2SJuha Yrjola 		struct mmc_host *mmc;
575a914ded2SJuha Yrjola 
576a914ded2SJuha Yrjola 		if (host->data != NULL)
577a914ded2SJuha Yrjola 			mmc_omap_abort_xfer(host, host->data);
5781c6a0718SPierre Ossman 		host->mrq = NULL;
579a914ded2SJuha Yrjola 		mmc = host->mmc;
5800807a9b5SJarkko Lavinen 		mmc_omap_release_slot(host->current_slot, 1);
581a914ded2SJuha Yrjola 		mmc_request_done(mmc, cmd->mrq);
5821c6a0718SPierre Ossman 	}
5831c6a0718SPierre Ossman }
5841c6a0718SPierre Ossman 
585eb1860bcSJarkko Lavinen /*
586eb1860bcSJarkko Lavinen  * Abort stuck command. Can occur when card is removed while it is being
587eb1860bcSJarkko Lavinen  * read.
588eb1860bcSJarkko Lavinen  */
589eb1860bcSJarkko Lavinen static void mmc_omap_abort_command(struct work_struct *work)
590eb1860bcSJarkko Lavinen {
591eb1860bcSJarkko Lavinen 	struct mmc_omap_host *host = container_of(work, struct mmc_omap_host,
5920fb4723dSJarkko Lavinen 						  cmd_abort_work);
5930fb4723dSJarkko Lavinen 	BUG_ON(!host->cmd);
594eb1860bcSJarkko Lavinen 
595eb1860bcSJarkko Lavinen 	dev_dbg(mmc_dev(host->mmc), "Aborting stuck command CMD%d\n",
596eb1860bcSJarkko Lavinen 		host->cmd->opcode);
597eb1860bcSJarkko Lavinen 
5980fb4723dSJarkko Lavinen 	if (host->cmd->error == 0)
599eb1860bcSJarkko Lavinen 		host->cmd->error = -ETIMEDOUT;
6000fb4723dSJarkko Lavinen 
6010fb4723dSJarkko Lavinen 	if (host->data == NULL) {
6020fb4723dSJarkko Lavinen 		struct mmc_command *cmd;
6030fb4723dSJarkko Lavinen 		struct mmc_host    *mmc;
6040fb4723dSJarkko Lavinen 
6050fb4723dSJarkko Lavinen 		cmd = host->cmd;
6060fb4723dSJarkko Lavinen 		host->cmd = NULL;
6070fb4723dSJarkko Lavinen 		mmc_omap_send_abort(host, 10000);
6080fb4723dSJarkko Lavinen 
6090fb4723dSJarkko Lavinen 		host->mrq = NULL;
6100fb4723dSJarkko Lavinen 		mmc = host->mmc;
6110807a9b5SJarkko Lavinen 		mmc_omap_release_slot(host->current_slot, 1);
6120fb4723dSJarkko Lavinen 		mmc_request_done(mmc, cmd->mrq);
6130fb4723dSJarkko Lavinen 	} else
614eb1860bcSJarkko Lavinen 		mmc_omap_cmd_done(host, host->cmd);
6150fb4723dSJarkko Lavinen 
6160fb4723dSJarkko Lavinen 	host->abort = 0;
6170fb4723dSJarkko Lavinen 	enable_irq(host->irq);
618eb1860bcSJarkko Lavinen }
619eb1860bcSJarkko Lavinen 
620eb1860bcSJarkko Lavinen static void
621eb1860bcSJarkko Lavinen mmc_omap_cmd_timer(unsigned long data)
622eb1860bcSJarkko Lavinen {
623eb1860bcSJarkko Lavinen 	struct mmc_omap_host *host = (struct mmc_omap_host *) data;
6240fb4723dSJarkko Lavinen 	unsigned long flags;
625eb1860bcSJarkko Lavinen 
6260fb4723dSJarkko Lavinen 	spin_lock_irqsave(&host->slot_lock, flags);
6270fb4723dSJarkko Lavinen 	if (host->cmd != NULL && !host->abort) {
6280fb4723dSJarkko Lavinen 		OMAP_MMC_WRITE(host, IE, 0);
6290fb4723dSJarkko Lavinen 		disable_irq(host->irq);
6300fb4723dSJarkko Lavinen 		host->abort = 1;
631b01a4f1cSVenkatraman S 		queue_work(host->mmc_omap_wq, &host->cmd_abort_work);
6320fb4723dSJarkko Lavinen 	}
6330fb4723dSJarkko Lavinen 	spin_unlock_irqrestore(&host->slot_lock, flags);
634eb1860bcSJarkko Lavinen }
635eb1860bcSJarkko Lavinen 
6361c6a0718SPierre Ossman /* PIO only */
6371c6a0718SPierre Ossman static void
6381c6a0718SPierre Ossman mmc_omap_sg_to_buf(struct mmc_omap_host *host)
6391c6a0718SPierre Ossman {
6401c6a0718SPierre Ossman 	struct scatterlist *sg;
6411c6a0718SPierre Ossman 
6421c6a0718SPierre Ossman 	sg = host->data->sg + host->sg_idx;
6431c6a0718SPierre Ossman 	host->buffer_bytes_left = sg->length;
64445711f1aSJens Axboe 	host->buffer = sg_virt(sg);
6451c6a0718SPierre Ossman 	if (host->buffer_bytes_left > host->total_bytes_left)
6461c6a0718SPierre Ossman 		host->buffer_bytes_left = host->total_bytes_left;
6471c6a0718SPierre Ossman }
6481c6a0718SPierre Ossman 
6490807a9b5SJarkko Lavinen static void
6500807a9b5SJarkko Lavinen mmc_omap_clk_timer(unsigned long data)
6510807a9b5SJarkko Lavinen {
6520807a9b5SJarkko Lavinen 	struct mmc_omap_host *host = (struct mmc_omap_host *) data;
6530807a9b5SJarkko Lavinen 
6540807a9b5SJarkko Lavinen 	mmc_omap_fclk_enable(host, 0);
6550807a9b5SJarkko Lavinen }
6560807a9b5SJarkko Lavinen 
6571c6a0718SPierre Ossman /* PIO only */
6581c6a0718SPierre Ossman static void
6591c6a0718SPierre Ossman mmc_omap_xfer_data(struct mmc_omap_host *host, int write)
6601c6a0718SPierre Ossman {
66175b53aeeSPaul Walmsley 	int n, nwords;
6621c6a0718SPierre Ossman 
6631c6a0718SPierre Ossman 	if (host->buffer_bytes_left == 0) {
6641c6a0718SPierre Ossman 		host->sg_idx++;
6651c6a0718SPierre Ossman 		BUG_ON(host->sg_idx == host->sg_len);
6661c6a0718SPierre Ossman 		mmc_omap_sg_to_buf(host);
6671c6a0718SPierre Ossman 	}
6681c6a0718SPierre Ossman 	n = 64;
6691c6a0718SPierre Ossman 	if (n > host->buffer_bytes_left)
6701c6a0718SPierre Ossman 		n = host->buffer_bytes_left;
67175b53aeeSPaul Walmsley 
67275b53aeeSPaul Walmsley 	nwords = n / 2;
67375b53aeeSPaul Walmsley 	nwords += n & 1; /* handle odd number of bytes to transfer */
67475b53aeeSPaul Walmsley 
6751c6a0718SPierre Ossman 	host->buffer_bytes_left -= n;
6761c6a0718SPierre Ossman 	host->total_bytes_left -= n;
6771c6a0718SPierre Ossman 	host->data->bytes_xfered += n;
6781c6a0718SPierre Ossman 
6791c6a0718SPierre Ossman 	if (write) {
68075b53aeeSPaul Walmsley 		__raw_writesw(host->virt_base + OMAP_MMC_REG(host, DATA),
68175b53aeeSPaul Walmsley 			      host->buffer, nwords);
6821c6a0718SPierre Ossman 	} else {
68375b53aeeSPaul Walmsley 		__raw_readsw(host->virt_base + OMAP_MMC_REG(host, DATA),
68475b53aeeSPaul Walmsley 			     host->buffer, nwords);
6851c6a0718SPierre Ossman 	}
68675b53aeeSPaul Walmsley 
68775b53aeeSPaul Walmsley 	host->buffer += nwords;
6881c6a0718SPierre Ossman }
6891c6a0718SPierre Ossman 
69075d569d3SVenkatraman S #ifdef CONFIG_MMC_DEBUG
69175d569d3SVenkatraman S static void mmc_omap_report_irq(struct mmc_omap_host *host, u16 status)
6921c6a0718SPierre Ossman {
6931c6a0718SPierre Ossman 	static const char *mmc_omap_status_bits[] = {
6941c6a0718SPierre Ossman 		"EOC", "CD", "CB", "BRS", "EOFB", "DTO", "DCRC", "CTO",
6951c6a0718SPierre Ossman 		"CCRC", "CRW", "AF", "AE", "OCRB", "CIRQ", "CERR"
6961c6a0718SPierre Ossman 	};
69775d569d3SVenkatraman S 	int i;
69875d569d3SVenkatraman S 	char res[64], *buf = res;
69975d569d3SVenkatraman S 
70075d569d3SVenkatraman S 	buf += sprintf(buf, "MMC IRQ 0x%x:", status);
7011c6a0718SPierre Ossman 
7021c6a0718SPierre Ossman 	for (i = 0; i < ARRAY_SIZE(mmc_omap_status_bits); i++)
70375d569d3SVenkatraman S 		if (status & (1 << i))
70475d569d3SVenkatraman S 			buf += sprintf(buf, " %s", mmc_omap_status_bits[i]);
70575d569d3SVenkatraman S 	dev_vdbg(mmc_dev(host->mmc), "%s\n", res);
7061c6a0718SPierre Ossman }
70775d569d3SVenkatraman S #else
70875d569d3SVenkatraman S static void mmc_omap_report_irq(struct mmc_omap_host *host, u16 status)
70975d569d3SVenkatraman S {
7101c6a0718SPierre Ossman }
71175d569d3SVenkatraman S #endif
71275d569d3SVenkatraman S 
7131c6a0718SPierre Ossman 
7141c6a0718SPierre Ossman static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
7151c6a0718SPierre Ossman {
7161c6a0718SPierre Ossman 	struct mmc_omap_host * host = (struct mmc_omap_host *)dev_id;
7171c6a0718SPierre Ossman 	u16 status;
7181c6a0718SPierre Ossman 	int end_command;
7191c6a0718SPierre Ossman 	int end_transfer;
7202a50b888SJuha Yrjola 	int transfer_error, cmd_error;
7211c6a0718SPierre Ossman 
7221c6a0718SPierre Ossman 	if (host->cmd == NULL && host->data == NULL) {
7231c6a0718SPierre Ossman 		status = OMAP_MMC_READ(host, STAT);
7242a50b888SJuha Yrjola 		dev_info(mmc_dev(host->slots[0]->mmc),
7252a50b888SJuha Yrjola 			 "Spurious IRQ 0x%04x\n", status);
7261c6a0718SPierre Ossman 		if (status != 0) {
7271c6a0718SPierre Ossman 			OMAP_MMC_WRITE(host, STAT, status);
7281c6a0718SPierre Ossman 			OMAP_MMC_WRITE(host, IE, 0);
7291c6a0718SPierre Ossman 		}
7301c6a0718SPierre Ossman 		return IRQ_HANDLED;
7311c6a0718SPierre Ossman 	}
7321c6a0718SPierre Ossman 
7331c6a0718SPierre Ossman 	end_command = 0;
7341c6a0718SPierre Ossman 	end_transfer = 0;
7351c6a0718SPierre Ossman 	transfer_error = 0;
7362a50b888SJuha Yrjola 	cmd_error = 0;
7371c6a0718SPierre Ossman 
7381c6a0718SPierre Ossman 	while ((status = OMAP_MMC_READ(host, STAT)) != 0) {
7392a50b888SJuha Yrjola 		int cmd;
7402a50b888SJuha Yrjola 
7411c6a0718SPierre Ossman 		OMAP_MMC_WRITE(host, STAT, status);
7422a50b888SJuha Yrjola 		if (host->cmd != NULL)
7432a50b888SJuha Yrjola 			cmd = host->cmd->opcode;
7442a50b888SJuha Yrjola 		else
7452a50b888SJuha Yrjola 			cmd = -1;
7461c6a0718SPierre Ossman 		dev_dbg(mmc_dev(host->mmc), "MMC IRQ %04x (CMD %d): ",
7472a50b888SJuha Yrjola 			status, cmd);
74875d569d3SVenkatraman S 		mmc_omap_report_irq(host, status);
74975d569d3SVenkatraman S 
7501c6a0718SPierre Ossman 		if (host->total_bytes_left) {
7511c6a0718SPierre Ossman 			if ((status & OMAP_MMC_STAT_A_FULL) ||
7521c6a0718SPierre Ossman 			    (status & OMAP_MMC_STAT_END_OF_DATA))
7531c6a0718SPierre Ossman 				mmc_omap_xfer_data(host, 0);
7541c6a0718SPierre Ossman 			if (status & OMAP_MMC_STAT_A_EMPTY)
7551c6a0718SPierre Ossman 				mmc_omap_xfer_data(host, 1);
7561c6a0718SPierre Ossman 		}
7571c6a0718SPierre Ossman 
7582a50b888SJuha Yrjola 		if (status & OMAP_MMC_STAT_END_OF_DATA)
7591c6a0718SPierre Ossman 			end_transfer = 1;
7601c6a0718SPierre Ossman 
7611c6a0718SPierre Ossman 		if (status & OMAP_MMC_STAT_DATA_TOUT) {
7622a50b888SJuha Yrjola 			dev_dbg(mmc_dev(host->mmc), "data timeout (CMD%d)\n",
7632a50b888SJuha Yrjola 				cmd);
7641c6a0718SPierre Ossman 			if (host->data) {
76517b0429dSPierre Ossman 				host->data->error = -ETIMEDOUT;
7661c6a0718SPierre Ossman 				transfer_error = 1;
7671c6a0718SPierre Ossman 			}
7681c6a0718SPierre Ossman 		}
7691c6a0718SPierre Ossman 
7701c6a0718SPierre Ossman 		if (status & OMAP_MMC_STAT_DATA_CRC) {
7711c6a0718SPierre Ossman 			if (host->data) {
77217b0429dSPierre Ossman 				host->data->error = -EILSEQ;
7731c6a0718SPierre Ossman 				dev_dbg(mmc_dev(host->mmc),
7741c6a0718SPierre Ossman 					 "data CRC error, bytes left %d\n",
7751c6a0718SPierre Ossman 					host->total_bytes_left);
7761c6a0718SPierre Ossman 				transfer_error = 1;
7771c6a0718SPierre Ossman 			} else {
7781c6a0718SPierre Ossman 				dev_dbg(mmc_dev(host->mmc), "data CRC error\n");
7791c6a0718SPierre Ossman 			}
7801c6a0718SPierre Ossman 		}
7811c6a0718SPierre Ossman 
7821c6a0718SPierre Ossman 		if (status & OMAP_MMC_STAT_CMD_TOUT) {
7831c6a0718SPierre Ossman 			/* Timeouts are routine with some commands */
7841c6a0718SPierre Ossman 			if (host->cmd) {
785abfbe5f7SJuha Yrjola 				struct mmc_omap_slot *slot =
786abfbe5f7SJuha Yrjola 					host->current_slot;
7872a50b888SJuha Yrjola 				if (slot == NULL ||
7882a50b888SJuha Yrjola 				    !mmc_omap_cover_is_open(slot))
7891c6a0718SPierre Ossman 					dev_err(mmc_dev(host->mmc),
7902a50b888SJuha Yrjola 						"command timeout (CMD%d)\n",
7912a50b888SJuha Yrjola 						cmd);
79217b0429dSPierre Ossman 				host->cmd->error = -ETIMEDOUT;
7931c6a0718SPierre Ossman 				end_command = 1;
7942a50b888SJuha Yrjola 				cmd_error = 1;
7951c6a0718SPierre Ossman 			}
7961c6a0718SPierre Ossman 		}
7971c6a0718SPierre Ossman 
7981c6a0718SPierre Ossman 		if (status & OMAP_MMC_STAT_CMD_CRC) {
7991c6a0718SPierre Ossman 			if (host->cmd) {
8001c6a0718SPierre Ossman 				dev_err(mmc_dev(host->mmc),
8011c6a0718SPierre Ossman 					"command CRC error (CMD%d, arg 0x%08x)\n",
8022a50b888SJuha Yrjola 					cmd, host->cmd->arg);
80317b0429dSPierre Ossman 				host->cmd->error = -EILSEQ;
8041c6a0718SPierre Ossman 				end_command = 1;
8052a50b888SJuha Yrjola 				cmd_error = 1;
8061c6a0718SPierre Ossman 			} else
8071c6a0718SPierre Ossman 				dev_err(mmc_dev(host->mmc),
8081c6a0718SPierre Ossman 					"command CRC error without cmd?\n");
8091c6a0718SPierre Ossman 		}
8101c6a0718SPierre Ossman 
8111c6a0718SPierre Ossman 		if (status & OMAP_MMC_STAT_CARD_ERR) {
8120107a4b3SRagner Magalhaes 			dev_dbg(mmc_dev(host->mmc),
8130107a4b3SRagner Magalhaes 				"ignoring card status error (CMD%d)\n",
8142a50b888SJuha Yrjola 				cmd);
8151c6a0718SPierre Ossman 			end_command = 1;
8161c6a0718SPierre Ossman 		}
8171c6a0718SPierre Ossman 
8181c6a0718SPierre Ossman 		/*
8191c6a0718SPierre Ossman 		 * NOTE: On 1610 the END_OF_CMD may come too early when
8201c6a0718SPierre Ossman 		 * starting a write
8211c6a0718SPierre Ossman 		 */
8221c6a0718SPierre Ossman 		if ((status & OMAP_MMC_STAT_END_OF_CMD) &&
8231c6a0718SPierre Ossman 		    (!(status & OMAP_MMC_STAT_A_EMPTY))) {
8241c6a0718SPierre Ossman 			end_command = 1;
8251c6a0718SPierre Ossman 		}
8261c6a0718SPierre Ossman 	}
8271c6a0718SPierre Ossman 
8280fb4723dSJarkko Lavinen 	if (cmd_error && host->data) {
8290fb4723dSJarkko Lavinen 		del_timer(&host->cmd_abort_timer);
8300fb4723dSJarkko Lavinen 		host->abort = 1;
8310fb4723dSJarkko Lavinen 		OMAP_MMC_WRITE(host, IE, 0);
832e749c6f2SBen Nizette 		disable_irq_nosync(host->irq);
833b01a4f1cSVenkatraman S 		queue_work(host->mmc_omap_wq, &host->cmd_abort_work);
8340fb4723dSJarkko Lavinen 		return IRQ_HANDLED;
8350fb4723dSJarkko Lavinen 	}
8360fb4723dSJarkko Lavinen 
837f6947514SMichael Buesch 	if (end_command && host->cmd)
8381c6a0718SPierre Ossman 		mmc_omap_cmd_done(host, host->cmd);
8392a50b888SJuha Yrjola 	if (host->data != NULL) {
8401c6a0718SPierre Ossman 		if (transfer_error)
8411c6a0718SPierre Ossman 			mmc_omap_xfer_done(host, host->data);
8421c6a0718SPierre Ossman 		else if (end_transfer)
8431c6a0718SPierre Ossman 			mmc_omap_end_of_data(host, host->data);
8442a50b888SJuha Yrjola 	}
8451c6a0718SPierre Ossman 
8461c6a0718SPierre Ossman 	return IRQ_HANDLED;
8471c6a0718SPierre Ossman }
8481c6a0718SPierre Ossman 
8497584d276SJarkko Lavinen void omap_mmc_notify_cover_event(struct device *dev, int num, int is_closed)
8505a0f3f1fSJuha Yrjola {
8517584d276SJarkko Lavinen 	int cover_open;
8525a0f3f1fSJuha Yrjola 	struct mmc_omap_host *host = dev_get_drvdata(dev);
8537584d276SJarkko Lavinen 	struct mmc_omap_slot *slot = host->slots[num];
8545a0f3f1fSJuha Yrjola 
8557584d276SJarkko Lavinen 	BUG_ON(num >= host->nr_slots);
8565a0f3f1fSJuha Yrjola 
8575a0f3f1fSJuha Yrjola 	/* Other subsystems can call in here before we're initialised. */
8587584d276SJarkko Lavinen 	if (host->nr_slots == 0 || !host->slots[num])
8595a0f3f1fSJuha Yrjola 		return;
8605a0f3f1fSJuha Yrjola 
8615a0f3f1fSJuha Yrjola 	cover_open = mmc_omap_cover_is_open(slot);
8625a0f3f1fSJuha Yrjola 	if (cover_open != slot->cover_open) {
8635a0f3f1fSJuha Yrjola 		slot->cover_open = cover_open;
8647584d276SJarkko Lavinen 		sysfs_notify(&slot->mmc->class_dev.kobj, NULL, "cover_switch");
8655a0f3f1fSJuha Yrjola 	}
8667584d276SJarkko Lavinen 
8677584d276SJarkko Lavinen 	tasklet_hi_schedule(&slot->cover_tasklet);
8687584d276SJarkko Lavinen }
8697584d276SJarkko Lavinen 
8707584d276SJarkko Lavinen static void mmc_omap_cover_timer(unsigned long arg)
8717584d276SJarkko Lavinen {
8727584d276SJarkko Lavinen 	struct mmc_omap_slot *slot = (struct mmc_omap_slot *) arg;
8737584d276SJarkko Lavinen 	tasklet_schedule(&slot->cover_tasklet);
8747584d276SJarkko Lavinen }
8757584d276SJarkko Lavinen 
8767584d276SJarkko Lavinen static void mmc_omap_cover_handler(unsigned long param)
8777584d276SJarkko Lavinen {
8787584d276SJarkko Lavinen 	struct mmc_omap_slot *slot = (struct mmc_omap_slot *)param;
8797584d276SJarkko Lavinen 	int cover_open = mmc_omap_cover_is_open(slot);
8807584d276SJarkko Lavinen 
8817584d276SJarkko Lavinen 	mmc_detect_change(slot->mmc, 0);
8827584d276SJarkko Lavinen 	if (!cover_open)
8837584d276SJarkko Lavinen 		return;
8847584d276SJarkko Lavinen 
8857584d276SJarkko Lavinen 	/*
8867584d276SJarkko Lavinen 	 * If no card is inserted, we postpone polling until
8877584d276SJarkko Lavinen 	 * the cover has been closed.
8887584d276SJarkko Lavinen 	 */
8897584d276SJarkko Lavinen 	if (slot->mmc->card == NULL || !mmc_card_present(slot->mmc->card))
8907584d276SJarkko Lavinen 		return;
8917584d276SJarkko Lavinen 
8927584d276SJarkko Lavinen 	mod_timer(&slot->cover_timer,
8937584d276SJarkko Lavinen 		  jiffies + msecs_to_jiffies(OMAP_MMC_COVER_POLL_DELAY));
8945a0f3f1fSJuha Yrjola }
8955a0f3f1fSJuha Yrjola 
8963451c067SRussell King static void mmc_omap_dma_callback(void *priv)
8973451c067SRussell King {
8983451c067SRussell King 	struct mmc_omap_host *host = priv;
8993451c067SRussell King 	struct mmc_data *data = host->data;
9003451c067SRussell King 
9013451c067SRussell King 	/* If we got to the end of DMA, assume everything went well */
9023451c067SRussell King 	data->bytes_xfered += data->blocks * data->blksz;
9033451c067SRussell King 
9043451c067SRussell King 	mmc_omap_dma_done(host, data);
9053451c067SRussell King }
9063451c067SRussell King 
9071c6a0718SPierre Ossman static inline void set_cmd_timeout(struct mmc_omap_host *host, struct mmc_request *req)
9081c6a0718SPierre Ossman {
9091c6a0718SPierre Ossman 	u16 reg;
9101c6a0718SPierre Ossman 
9111c6a0718SPierre Ossman 	reg = OMAP_MMC_READ(host, SDIO);
9121c6a0718SPierre Ossman 	reg &= ~(1 << 5);
9131c6a0718SPierre Ossman 	OMAP_MMC_WRITE(host, SDIO, reg);
9141c6a0718SPierre Ossman 	/* Set maximum timeout */
9151c6a0718SPierre Ossman 	OMAP_MMC_WRITE(host, CTO, 0xff);
9161c6a0718SPierre Ossman }
9171c6a0718SPierre Ossman 
9181c6a0718SPierre Ossman static inline void set_data_timeout(struct mmc_omap_host *host, struct mmc_request *req)
9191c6a0718SPierre Ossman {
920b8f9f0e9SJuha Yrjola 	unsigned int timeout, cycle_ns;
9211c6a0718SPierre Ossman 	u16 reg;
9221c6a0718SPierre Ossman 
923b8f9f0e9SJuha Yrjola 	cycle_ns = 1000000000 / host->current_slot->fclk_freq;
924b8f9f0e9SJuha Yrjola 	timeout = req->data->timeout_ns / cycle_ns;
925b8f9f0e9SJuha Yrjola 	timeout += req->data->timeout_clks;
9261c6a0718SPierre Ossman 
9271c6a0718SPierre Ossman 	/* Check if we need to use timeout multiplier register */
9281c6a0718SPierre Ossman 	reg = OMAP_MMC_READ(host, SDIO);
9291c6a0718SPierre Ossman 	if (timeout > 0xffff) {
9301c6a0718SPierre Ossman 		reg |= (1 << 5);
9311c6a0718SPierre Ossman 		timeout /= 1024;
9321c6a0718SPierre Ossman 	} else
9331c6a0718SPierre Ossman 		reg &= ~(1 << 5);
9341c6a0718SPierre Ossman 	OMAP_MMC_WRITE(host, SDIO, reg);
9351c6a0718SPierre Ossman 	OMAP_MMC_WRITE(host, DTO, timeout);
9361c6a0718SPierre Ossman }
9371c6a0718SPierre Ossman 
9381c6a0718SPierre Ossman static void
9391c6a0718SPierre Ossman mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
9401c6a0718SPierre Ossman {
9411c6a0718SPierre Ossman 	struct mmc_data *data = req->data;
9421c6a0718SPierre Ossman 	int i, use_dma, block_size;
9431c6a0718SPierre Ossman 	unsigned sg_len;
9441c6a0718SPierre Ossman 
9451c6a0718SPierre Ossman 	host->data = data;
9461c6a0718SPierre Ossman 	if (data == NULL) {
9471c6a0718SPierre Ossman 		OMAP_MMC_WRITE(host, BLEN, 0);
9481c6a0718SPierre Ossman 		OMAP_MMC_WRITE(host, NBLK, 0);
9491c6a0718SPierre Ossman 		OMAP_MMC_WRITE(host, BUF, 0);
9501c6a0718SPierre Ossman 		host->dma_in_use = 0;
9511c6a0718SPierre Ossman 		set_cmd_timeout(host, req);
9521c6a0718SPierre Ossman 		return;
9531c6a0718SPierre Ossman 	}
9541c6a0718SPierre Ossman 
9551c6a0718SPierre Ossman 	block_size = data->blksz;
9561c6a0718SPierre Ossman 
9571c6a0718SPierre Ossman 	OMAP_MMC_WRITE(host, NBLK, data->blocks - 1);
9581c6a0718SPierre Ossman 	OMAP_MMC_WRITE(host, BLEN, block_size - 1);
9591c6a0718SPierre Ossman 	set_data_timeout(host, req);
9601c6a0718SPierre Ossman 
9611c6a0718SPierre Ossman 	/* cope with calling layer confusion; it issues "single
9621c6a0718SPierre Ossman 	 * block" writes using multi-block scatterlists.
9631c6a0718SPierre Ossman 	 */
9641c6a0718SPierre Ossman 	sg_len = (data->blocks == 1) ? 1 : data->sg_len;
9651c6a0718SPierre Ossman 
9661c6a0718SPierre Ossman 	/* Only do DMA for entire blocks */
9671c6a0718SPierre Ossman 	use_dma = host->use_dma;
9681c6a0718SPierre Ossman 	if (use_dma) {
9691c6a0718SPierre Ossman 		for (i = 0; i < sg_len; i++) {
9701c6a0718SPierre Ossman 			if ((data->sg[i].length % block_size) != 0) {
9711c6a0718SPierre Ossman 				use_dma = 0;
9721c6a0718SPierre Ossman 				break;
9731c6a0718SPierre Ossman 			}
9741c6a0718SPierre Ossman 		}
9751c6a0718SPierre Ossman 	}
9761c6a0718SPierre Ossman 
9771c6a0718SPierre Ossman 	host->sg_idx = 0;
9781c6a0718SPierre Ossman 	if (use_dma) {
9793451c067SRussell King 		enum dma_data_direction dma_data_dir;
9803451c067SRussell King 		struct dma_async_tx_descriptor *tx;
9813451c067SRussell King 		struct dma_chan *c;
9823451c067SRussell King 		u32 burst, *bp;
9833451c067SRussell King 		u16 buf;
9843451c067SRussell King 
9853451c067SRussell King 		/*
9863451c067SRussell King 		 * FIFO is 16x2 bytes on 15xx, and 32x2 bytes on 16xx
9873451c067SRussell King 		 * and 24xx. Use 16 or 32 word frames when the
9883451c067SRussell King 		 * blocksize is at least that large. Blocksize is
9893451c067SRussell King 		 * usually 512 bytes; but not for some SD reads.
9903451c067SRussell King 		 */
9913451c067SRussell King 		burst = cpu_is_omap15xx() ? 32 : 64;
9923451c067SRussell King 		if (burst > data->blksz)
9933451c067SRussell King 			burst = data->blksz;
9943451c067SRussell King 
9953451c067SRussell King 		burst >>= 1;
9963451c067SRussell King 
9973451c067SRussell King 		if (data->flags & MMC_DATA_WRITE) {
9983451c067SRussell King 			c = host->dma_tx;
9993451c067SRussell King 			bp = &host->dma_tx_burst;
10003451c067SRussell King 			buf = 0x0f80 | (burst - 1) << 0;
10013451c067SRussell King 			dma_data_dir = DMA_TO_DEVICE;
10023451c067SRussell King 		} else {
10033451c067SRussell King 			c = host->dma_rx;
10043451c067SRussell King 			bp = &host->dma_rx_burst;
10053451c067SRussell King 			buf = 0x800f | (burst - 1) << 8;
10063451c067SRussell King 			dma_data_dir = DMA_FROM_DEVICE;
10073451c067SRussell King 		}
10083451c067SRussell King 
10093451c067SRussell King 		if (!c)
10103451c067SRussell King 			goto use_pio;
10113451c067SRussell King 
10123451c067SRussell King 		/* Only reconfigure if we have a different burst size */
10133451c067SRussell King 		if (*bp != burst) {
10143451c067SRussell King 			struct dma_slave_config cfg;
10153451c067SRussell King 
10163451c067SRussell King 			cfg.src_addr = host->phys_base + OMAP_MMC_REG(host, DATA);
10173451c067SRussell King 			cfg.dst_addr = host->phys_base + OMAP_MMC_REG(host, DATA);
10183451c067SRussell King 			cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
10193451c067SRussell King 			cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
10203451c067SRussell King 			cfg.src_maxburst = burst;
10213451c067SRussell King 			cfg.dst_maxburst = burst;
10223451c067SRussell King 
10233451c067SRussell King 			if (dmaengine_slave_config(c, &cfg))
10243451c067SRussell King 				goto use_pio;
10253451c067SRussell King 
10263451c067SRussell King 			*bp = burst;
10273451c067SRussell King 		}
10283451c067SRussell King 
10293451c067SRussell King 		host->sg_len = dma_map_sg(c->device->dev, data->sg, sg_len,
10303451c067SRussell King 					  dma_data_dir);
10313451c067SRussell King 		if (host->sg_len == 0)
10323451c067SRussell King 			goto use_pio;
10333451c067SRussell King 
10343451c067SRussell King 		tx = dmaengine_prep_slave_sg(c, data->sg, host->sg_len,
10353451c067SRussell King 			data->flags & MMC_DATA_WRITE ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
10363451c067SRussell King 			DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
10373451c067SRussell King 		if (!tx)
10383451c067SRussell King 			goto use_pio;
10393451c067SRussell King 
10403451c067SRussell King 		OMAP_MMC_WRITE(host, BUF, buf);
10413451c067SRussell King 
10423451c067SRussell King 		tx->callback = mmc_omap_dma_callback;
10433451c067SRussell King 		tx->callback_param = host;
10443451c067SRussell King 		dmaengine_submit(tx);
10453451c067SRussell King 		host->brs_received = 0;
10463451c067SRussell King 		host->dma_done = 0;
10473451c067SRussell King 		host->dma_in_use = 1;
10483451c067SRussell King 		return;
10493451c067SRussell King 	}
10503451c067SRussell King  use_pio:
10511c6a0718SPierre Ossman 
10521c6a0718SPierre Ossman 	/* Revert to PIO? */
10531c6a0718SPierre Ossman 	OMAP_MMC_WRITE(host, BUF, 0x1f1f);
10541c6a0718SPierre Ossman 	host->total_bytes_left = data->blocks * block_size;
10551c6a0718SPierre Ossman 	host->sg_len = sg_len;
10561c6a0718SPierre Ossman 	mmc_omap_sg_to_buf(host);
10571c6a0718SPierre Ossman 	host->dma_in_use = 0;
10581c6a0718SPierre Ossman }
10591c6a0718SPierre Ossman 
1060abfbe5f7SJuha Yrjola static void mmc_omap_start_request(struct mmc_omap_host *host,
1061abfbe5f7SJuha Yrjola 				   struct mmc_request *req)
10621c6a0718SPierre Ossman {
1063abfbe5f7SJuha Yrjola 	BUG_ON(host->mrq != NULL);
10641c6a0718SPierre Ossman 
10651c6a0718SPierre Ossman 	host->mrq = req;
10661c6a0718SPierre Ossman 
10671c6a0718SPierre Ossman 	/* only touch fifo AFTER the controller readies it */
10681c6a0718SPierre Ossman 	mmc_omap_prepare_data(host, req);
10691c6a0718SPierre Ossman 	mmc_omap_start_command(host, req->cmd);
10703451c067SRussell King 	if (host->dma_in_use) {
10713451c067SRussell King 		struct dma_chan *c = host->data->flags & MMC_DATA_WRITE ?
10723451c067SRussell King 				host->dma_tx : host->dma_rx;
10733451c067SRussell King 
10743451c067SRussell King 		dma_async_issue_pending(c);
10753451c067SRussell King 	}
1076abfbe5f7SJuha Yrjola }
1077abfbe5f7SJuha Yrjola 
1078abfbe5f7SJuha Yrjola static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req)
1079abfbe5f7SJuha Yrjola {
1080abfbe5f7SJuha Yrjola 	struct mmc_omap_slot *slot = mmc_priv(mmc);
1081abfbe5f7SJuha Yrjola 	struct mmc_omap_host *host = slot->host;
1082abfbe5f7SJuha Yrjola 	unsigned long flags;
1083abfbe5f7SJuha Yrjola 
1084abfbe5f7SJuha Yrjola 	spin_lock_irqsave(&host->slot_lock, flags);
1085abfbe5f7SJuha Yrjola 	if (host->mmc != NULL) {
1086abfbe5f7SJuha Yrjola 		BUG_ON(slot->mrq != NULL);
1087abfbe5f7SJuha Yrjola 		slot->mrq = req;
1088abfbe5f7SJuha Yrjola 		spin_unlock_irqrestore(&host->slot_lock, flags);
1089abfbe5f7SJuha Yrjola 		return;
1090abfbe5f7SJuha Yrjola 	} else
1091abfbe5f7SJuha Yrjola 		host->mmc = mmc;
1092abfbe5f7SJuha Yrjola 	spin_unlock_irqrestore(&host->slot_lock, flags);
1093abfbe5f7SJuha Yrjola 	mmc_omap_select_slot(slot, 1);
1094abfbe5f7SJuha Yrjola 	mmc_omap_start_request(host, req);
10951c6a0718SPierre Ossman }
10961c6a0718SPierre Ossman 
109765b5b6e5SJuha Yrjola static void mmc_omap_set_power(struct mmc_omap_slot *slot, int power_on,
109865b5b6e5SJuha Yrjola 				int vdd)
10991c6a0718SPierre Ossman {
110065b5b6e5SJuha Yrjola 	struct mmc_omap_host *host;
11011c6a0718SPierre Ossman 
110265b5b6e5SJuha Yrjola 	host = slot->host;
110365b5b6e5SJuha Yrjola 
110465b5b6e5SJuha Yrjola 	if (slot->pdata->set_power != NULL)
110565b5b6e5SJuha Yrjola 		slot->pdata->set_power(mmc_dev(slot->mmc), slot->id, power_on,
110665b5b6e5SJuha Yrjola 					vdd);
110765b5b6e5SJuha Yrjola 
110865b5b6e5SJuha Yrjola 	if (cpu_is_omap24xx()) {
110965b5b6e5SJuha Yrjola 		u16 w;
111065b5b6e5SJuha Yrjola 
111165b5b6e5SJuha Yrjola 		if (power_on) {
111265b5b6e5SJuha Yrjola 			w = OMAP_MMC_READ(host, CON);
111365b5b6e5SJuha Yrjola 			OMAP_MMC_WRITE(host, CON, w | (1 << 11));
11141c6a0718SPierre Ossman 		} else {
111565b5b6e5SJuha Yrjola 			w = OMAP_MMC_READ(host, CON);
111665b5b6e5SJuha Yrjola 			OMAP_MMC_WRITE(host, CON, w & ~(1 << 11));
111765b5b6e5SJuha Yrjola 		}
11181c6a0718SPierre Ossman 	}
11191c6a0718SPierre Ossman }
11201c6a0718SPierre Ossman 
1121d3af5abeSTony Lindgren static int mmc_omap_calc_divisor(struct mmc_host *mmc, struct mmc_ios *ios)
11221c6a0718SPierre Ossman {
1123abfbe5f7SJuha Yrjola 	struct mmc_omap_slot *slot = mmc_priv(mmc);
1124abfbe5f7SJuha Yrjola 	struct mmc_omap_host *host = slot->host;
1125d3af5abeSTony Lindgren 	int func_clk_rate = clk_get_rate(host->fclk);
11261c6a0718SPierre Ossman 	int dsor;
11271c6a0718SPierre Ossman 
11281c6a0718SPierre Ossman 	if (ios->clock == 0)
1129d3af5abeSTony Lindgren 		return 0;
11301c6a0718SPierre Ossman 
1131d3af5abeSTony Lindgren 	dsor = func_clk_rate / ios->clock;
11321c6a0718SPierre Ossman 	if (dsor < 1)
11331c6a0718SPierre Ossman 		dsor = 1;
11341c6a0718SPierre Ossman 
1135d3af5abeSTony Lindgren 	if (func_clk_rate / dsor > ios->clock)
11361c6a0718SPierre Ossman 		dsor++;
11371c6a0718SPierre Ossman 
11381c6a0718SPierre Ossman 	if (dsor > 250)
11391c6a0718SPierre Ossman 		dsor = 250;
11401c6a0718SPierre Ossman 
1141abfbe5f7SJuha Yrjola 	slot->fclk_freq = func_clk_rate / dsor;
1142abfbe5f7SJuha Yrjola 
11431c6a0718SPierre Ossman 	if (ios->bus_width == MMC_BUS_WIDTH_4)
11441c6a0718SPierre Ossman 		dsor |= 1 << 15;
1145d3af5abeSTony Lindgren 
1146d3af5abeSTony Lindgren 	return dsor;
11471c6a0718SPierre Ossman }
11481c6a0718SPierre Ossman 
1149d3af5abeSTony Lindgren static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
1150d3af5abeSTony Lindgren {
1151abfbe5f7SJuha Yrjola 	struct mmc_omap_slot *slot = mmc_priv(mmc);
1152abfbe5f7SJuha Yrjola 	struct mmc_omap_host *host = slot->host;
1153abfbe5f7SJuha Yrjola 	int i, dsor;
11540807a9b5SJarkko Lavinen 	int clk_enabled;
115565b5b6e5SJuha Yrjola 
115665b5b6e5SJuha Yrjola 	mmc_omap_select_slot(slot, 0);
115765b5b6e5SJuha Yrjola 
11580807a9b5SJarkko Lavinen 	dsor = mmc_omap_calc_divisor(mmc, ios);
11590807a9b5SJarkko Lavinen 
116065b5b6e5SJuha Yrjola 	if (ios->vdd != slot->vdd)
116165b5b6e5SJuha Yrjola 		slot->vdd = ios->vdd;
1162d3af5abeSTony Lindgren 
11630807a9b5SJarkko Lavinen 	clk_enabled = 0;
11641c6a0718SPierre Ossman 	switch (ios->power_mode) {
11651c6a0718SPierre Ossman 	case MMC_POWER_OFF:
116665b5b6e5SJuha Yrjola 		mmc_omap_set_power(slot, 0, ios->vdd);
11671c6a0718SPierre Ossman 		break;
11681c6a0718SPierre Ossman 	case MMC_POWER_UP:
116946a6730eSTony Lindgren 		/* Cannot touch dsor yet, just power up MMC */
117065b5b6e5SJuha Yrjola 		mmc_omap_set_power(slot, 1, ios->vdd);
117165b5b6e5SJuha Yrjola 		goto exit;
117246a6730eSTony Lindgren 	case MMC_POWER_ON:
11730807a9b5SJarkko Lavinen 		mmc_omap_fclk_enable(host, 1);
11740807a9b5SJarkko Lavinen 		clk_enabled = 1;
11751c6a0718SPierre Ossman 		dsor |= 1 << 11;
11761c6a0718SPierre Ossman 		break;
11771c6a0718SPierre Ossman 	}
11781c6a0718SPierre Ossman 
117965b5b6e5SJuha Yrjola 	if (slot->bus_mode != ios->bus_mode) {
118065b5b6e5SJuha Yrjola 		if (slot->pdata->set_bus_mode != NULL)
118165b5b6e5SJuha Yrjola 			slot->pdata->set_bus_mode(mmc_dev(mmc), slot->id,
118265b5b6e5SJuha Yrjola 						  ios->bus_mode);
118365b5b6e5SJuha Yrjola 		slot->bus_mode = ios->bus_mode;
118465b5b6e5SJuha Yrjola 	}
11851c6a0718SPierre Ossman 
11861c6a0718SPierre Ossman 	/* On insanely high arm_per frequencies something sometimes
11871c6a0718SPierre Ossman 	 * goes somehow out of sync, and the POW bit is not being set,
11881c6a0718SPierre Ossman 	 * which results in the while loop below getting stuck.
11891c6a0718SPierre Ossman 	 * Writing to the CON register twice seems to do the trick. */
11901c6a0718SPierre Ossman 	for (i = 0; i < 2; i++)
11911c6a0718SPierre Ossman 		OMAP_MMC_WRITE(host, CON, dsor);
119265b5b6e5SJuha Yrjola 	slot->saved_con = dsor;
119346a6730eSTony Lindgren 	if (ios->power_mode == MMC_POWER_ON) {
11949d7c6eeeSJarkko Lavinen 		/* worst case at 400kHz, 80 cycles makes 200 microsecs */
11959d7c6eeeSJarkko Lavinen 		int usecs = 250;
11969d7c6eeeSJarkko Lavinen 
11971c6a0718SPierre Ossman 		/* Send clock cycles, poll completion */
11981c6a0718SPierre Ossman 		OMAP_MMC_WRITE(host, IE, 0);
11991c6a0718SPierre Ossman 		OMAP_MMC_WRITE(host, STAT, 0xffff);
12001c6a0718SPierre Ossman 		OMAP_MMC_WRITE(host, CMD, 1 << 7);
12019d7c6eeeSJarkko Lavinen 		while (usecs > 0 && (OMAP_MMC_READ(host, STAT) & 1) == 0) {
12029d7c6eeeSJarkko Lavinen 			udelay(1);
12039d7c6eeeSJarkko Lavinen 			usecs--;
12049d7c6eeeSJarkko Lavinen 		}
12051c6a0718SPierre Ossman 		OMAP_MMC_WRITE(host, STAT, 1);
12061c6a0718SPierre Ossman 	}
120765b5b6e5SJuha Yrjola 
120865b5b6e5SJuha Yrjola exit:
12090807a9b5SJarkko Lavinen 	mmc_omap_release_slot(slot, clk_enabled);
12101c6a0718SPierre Ossman }
12111c6a0718SPierre Ossman 
12121c6a0718SPierre Ossman static const struct mmc_host_ops mmc_omap_ops = {
12131c6a0718SPierre Ossman 	.request	= mmc_omap_request,
12141c6a0718SPierre Ossman 	.set_ios	= mmc_omap_set_ios,
12151c6a0718SPierre Ossman };
12161c6a0718SPierre Ossman 
1217c3be1efdSBill Pemberton static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)
1218abfbe5f7SJuha Yrjola {
1219abfbe5f7SJuha Yrjola 	struct mmc_omap_slot *slot = NULL;
1220abfbe5f7SJuha Yrjola 	struct mmc_host *mmc;
1221abfbe5f7SJuha Yrjola 	int r;
1222abfbe5f7SJuha Yrjola 
1223abfbe5f7SJuha Yrjola 	mmc = mmc_alloc_host(sizeof(struct mmc_omap_slot), host->dev);
1224abfbe5f7SJuha Yrjola 	if (mmc == NULL)
1225abfbe5f7SJuha Yrjola 		return -ENOMEM;
1226abfbe5f7SJuha Yrjola 
1227abfbe5f7SJuha Yrjola 	slot = mmc_priv(mmc);
1228abfbe5f7SJuha Yrjola 	slot->host = host;
1229abfbe5f7SJuha Yrjola 	slot->mmc = mmc;
1230abfbe5f7SJuha Yrjola 	slot->id = id;
1231abfbe5f7SJuha Yrjola 	slot->pdata = &host->pdata->slots[id];
1232abfbe5f7SJuha Yrjola 
1233abfbe5f7SJuha Yrjola 	host->slots[id] = slot;
1234abfbe5f7SJuha Yrjola 
123523af6039SPierre Ossman 	mmc->caps = 0;
123690c62bf0STony Lindgren 	if (host->pdata->slots[id].wires >= 4)
1237abfbe5f7SJuha Yrjola 		mmc->caps |= MMC_CAP_4_BIT_DATA;
1238abfbe5f7SJuha Yrjola 
1239abfbe5f7SJuha Yrjola 	mmc->ops = &mmc_omap_ops;
1240abfbe5f7SJuha Yrjola 	mmc->f_min = 400000;
1241abfbe5f7SJuha Yrjola 
1242abfbe5f7SJuha Yrjola 	if (cpu_class_is_omap2())
1243abfbe5f7SJuha Yrjola 		mmc->f_max = 48000000;
1244abfbe5f7SJuha Yrjola 	else
1245abfbe5f7SJuha Yrjola 		mmc->f_max = 24000000;
1246abfbe5f7SJuha Yrjola 	if (host->pdata->max_freq)
1247abfbe5f7SJuha Yrjola 		mmc->f_max = min(host->pdata->max_freq, mmc->f_max);
1248abfbe5f7SJuha Yrjola 	mmc->ocr_avail = slot->pdata->ocr_mask;
1249abfbe5f7SJuha Yrjola 
1250abfbe5f7SJuha Yrjola 	/* Use scatterlist DMA to reduce per-transfer costs.
1251abfbe5f7SJuha Yrjola 	 * NOTE max_seg_size assumption that small blocks aren't
1252abfbe5f7SJuha Yrjola 	 * normally used (except e.g. for reading SD registers).
1253abfbe5f7SJuha Yrjola 	 */
1254a36274e0SMartin K. Petersen 	mmc->max_segs = 32;
1255abfbe5f7SJuha Yrjola 	mmc->max_blk_size = 2048;	/* BLEN is 11 bits (+1) */
1256abfbe5f7SJuha Yrjola 	mmc->max_blk_count = 2048;	/* NBLK is 11 bits (+1) */
1257abfbe5f7SJuha Yrjola 	mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
1258abfbe5f7SJuha Yrjola 	mmc->max_seg_size = mmc->max_req_size;
1259abfbe5f7SJuha Yrjola 
1260abfbe5f7SJuha Yrjola 	r = mmc_add_host(mmc);
1261abfbe5f7SJuha Yrjola 	if (r < 0)
1262abfbe5f7SJuha Yrjola 		goto err_remove_host;
1263abfbe5f7SJuha Yrjola 
1264abfbe5f7SJuha Yrjola 	if (slot->pdata->name != NULL) {
1265abfbe5f7SJuha Yrjola 		r = device_create_file(&mmc->class_dev,
1266abfbe5f7SJuha Yrjola 					&dev_attr_slot_name);
1267abfbe5f7SJuha Yrjola 		if (r < 0)
1268abfbe5f7SJuha Yrjola 			goto err_remove_host;
1269abfbe5f7SJuha Yrjola 	}
1270abfbe5f7SJuha Yrjola 
12715a0f3f1fSJuha Yrjola 	if (slot->pdata->get_cover_state != NULL) {
12725a0f3f1fSJuha Yrjola 		r = device_create_file(&mmc->class_dev,
12735a0f3f1fSJuha Yrjola 					&dev_attr_cover_switch);
12745a0f3f1fSJuha Yrjola 		if (r < 0)
12755a0f3f1fSJuha Yrjola 			goto err_remove_slot_name;
12765a0f3f1fSJuha Yrjola 
12777584d276SJarkko Lavinen 		setup_timer(&slot->cover_timer, mmc_omap_cover_timer,
127801e77e13SCarlos Eduardo Aguiar 			    (unsigned long)slot);
12797584d276SJarkko Lavinen 		tasklet_init(&slot->cover_tasklet, mmc_omap_cover_handler,
12807584d276SJarkko Lavinen 			     (unsigned long)slot);
12817584d276SJarkko Lavinen 		tasklet_schedule(&slot->cover_tasklet);
12825a0f3f1fSJuha Yrjola 	}
12835a0f3f1fSJuha Yrjola 
1284abfbe5f7SJuha Yrjola 	return 0;
1285abfbe5f7SJuha Yrjola 
12865a0f3f1fSJuha Yrjola err_remove_slot_name:
12875a0f3f1fSJuha Yrjola 	if (slot->pdata->name != NULL)
12885a0f3f1fSJuha Yrjola 		device_remove_file(&mmc->class_dev, &dev_attr_slot_name);
1289abfbe5f7SJuha Yrjola err_remove_host:
1290abfbe5f7SJuha Yrjola 	mmc_remove_host(mmc);
1291abfbe5f7SJuha Yrjola 	mmc_free_host(mmc);
1292abfbe5f7SJuha Yrjola 	return r;
1293abfbe5f7SJuha Yrjola }
1294abfbe5f7SJuha Yrjola 
1295abfbe5f7SJuha Yrjola static void mmc_omap_remove_slot(struct mmc_omap_slot *slot)
1296abfbe5f7SJuha Yrjola {
1297abfbe5f7SJuha Yrjola 	struct mmc_host *mmc = slot->mmc;
1298abfbe5f7SJuha Yrjola 
1299abfbe5f7SJuha Yrjola 	if (slot->pdata->name != NULL)
1300abfbe5f7SJuha Yrjola 		device_remove_file(&mmc->class_dev, &dev_attr_slot_name);
13015a0f3f1fSJuha Yrjola 	if (slot->pdata->get_cover_state != NULL)
13025a0f3f1fSJuha Yrjola 		device_remove_file(&mmc->class_dev, &dev_attr_cover_switch);
13035a0f3f1fSJuha Yrjola 
13047584d276SJarkko Lavinen 	tasklet_kill(&slot->cover_tasklet);
13057584d276SJarkko Lavinen 	del_timer_sync(&slot->cover_timer);
1306b01a4f1cSVenkatraman S 	flush_workqueue(slot->host->mmc_omap_wq);
1307abfbe5f7SJuha Yrjola 
1308abfbe5f7SJuha Yrjola 	mmc_remove_host(mmc);
1309abfbe5f7SJuha Yrjola 	mmc_free_host(mmc);
1310abfbe5f7SJuha Yrjola }
1311abfbe5f7SJuha Yrjola 
1312c3be1efdSBill Pemberton static int mmc_omap_probe(struct platform_device *pdev)
13131c6a0718SPierre Ossman {
1314abfbe5f7SJuha Yrjola 	struct omap_mmc_platform_data *pdata = pdev->dev.platform_data;
13151c6a0718SPierre Ossman 	struct mmc_omap_host *host = NULL;
13161c6a0718SPierre Ossman 	struct resource *res;
13173451c067SRussell King 	dma_cap_mask_t mask;
13183451c067SRussell King 	unsigned sig;
1319abfbe5f7SJuha Yrjola 	int i, ret = 0;
13201c6a0718SPierre Ossman 	int irq;
13211c6a0718SPierre Ossman 
1322abfbe5f7SJuha Yrjola 	if (pdata == NULL) {
13231c6a0718SPierre Ossman 		dev_err(&pdev->dev, "platform data missing\n");
13241c6a0718SPierre Ossman 		return -ENXIO;
13251c6a0718SPierre Ossman 	}
1326abfbe5f7SJuha Yrjola 	if (pdata->nr_slots == 0) {
1327abfbe5f7SJuha Yrjola 		dev_err(&pdev->dev, "no slots\n");
1328abfbe5f7SJuha Yrjola 		return -ENXIO;
1329abfbe5f7SJuha Yrjola 	}
13301c6a0718SPierre Ossman 
13311c6a0718SPierre Ossman 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
13321c6a0718SPierre Ossman 	irq = platform_get_irq(pdev, 0);
13331c6a0718SPierre Ossman 	if (res == NULL || irq < 0)
13341c6a0718SPierre Ossman 		return -ENXIO;
13351c6a0718SPierre Ossman 
13362092014dSChris Ball 	res = request_mem_region(res->start, resource_size(res),
13371c6a0718SPierre Ossman 				 pdev->name);
13381c6a0718SPierre Ossman 	if (res == NULL)
13391c6a0718SPierre Ossman 		return -EBUSY;
13401c6a0718SPierre Ossman 
1341abfbe5f7SJuha Yrjola 	host = kzalloc(sizeof(struct mmc_omap_host), GFP_KERNEL);
1342abfbe5f7SJuha Yrjola 	if (host == NULL) {
13431c6a0718SPierre Ossman 		ret = -ENOMEM;
13441c6a0718SPierre Ossman 		goto err_free_mem_region;
13451c6a0718SPierre Ossman 	}
13461c6a0718SPierre Ossman 
13470f602ec7SJarkko Lavinen 	INIT_WORK(&host->slot_release_work, mmc_omap_slot_release_work);
13480f602ec7SJarkko Lavinen 	INIT_WORK(&host->send_stop_work, mmc_omap_send_stop_work);
13490f602ec7SJarkko Lavinen 
13500fb4723dSJarkko Lavinen 	INIT_WORK(&host->cmd_abort_work, mmc_omap_abort_command);
13510fb4723dSJarkko Lavinen 	setup_timer(&host->cmd_abort_timer, mmc_omap_cmd_timer,
13520fb4723dSJarkko Lavinen 		    (unsigned long) host);
1353eb1860bcSJarkko Lavinen 
13540807a9b5SJarkko Lavinen 	spin_lock_init(&host->clk_lock);
13550807a9b5SJarkko Lavinen 	setup_timer(&host->clk_timer, mmc_omap_clk_timer, (unsigned long) host);
13560807a9b5SJarkko Lavinen 
13571c6a0718SPierre Ossman 	spin_lock_init(&host->dma_lock);
1358abfbe5f7SJuha Yrjola 	spin_lock_init(&host->slot_lock);
1359abfbe5f7SJuha Yrjola 	init_waitqueue_head(&host->slot_wq);
1360abfbe5f7SJuha Yrjola 
1361abfbe5f7SJuha Yrjola 	host->pdata = pdata;
1362abfbe5f7SJuha Yrjola 	host->dev = &pdev->dev;
1363abfbe5f7SJuha Yrjola 	platform_set_drvdata(pdev, host);
1364abfbe5f7SJuha Yrjola 
13651c6a0718SPierre Ossman 	host->id = pdev->id;
13661c6a0718SPierre Ossman 	host->mem_res = res;
13671c6a0718SPierre Ossman 	host->irq = irq;
1368abfbe5f7SJuha Yrjola 	host->use_dma = 1;
1369abfbe5f7SJuha Yrjola 	host->irq = irq;
1370abfbe5f7SJuha Yrjola 	host->phys_base = host->mem_res->start;
13712092014dSChris Ball 	host->virt_base = ioremap(res->start, resource_size(res));
137255c381e4SRussell King 	if (!host->virt_base)
137355c381e4SRussell King 		goto err_ioremap;
1374abfbe5f7SJuha Yrjola 
13755c9e02b1SRussell King 	host->iclk = clk_get(&pdev->dev, "ick");
1376e799acb2SLadislav Michl 	if (IS_ERR(host->iclk)) {
1377e799acb2SLadislav Michl 		ret = PTR_ERR(host->iclk);
13781c6a0718SPierre Ossman 		goto err_free_mmc_host;
1379e799acb2SLadislav Michl 	}
13801c6a0718SPierre Ossman 	clk_enable(host->iclk);
13811c6a0718SPierre Ossman 
13825c9e02b1SRussell King 	host->fclk = clk_get(&pdev->dev, "fck");
13831c6a0718SPierre Ossman 	if (IS_ERR(host->fclk)) {
13841c6a0718SPierre Ossman 		ret = PTR_ERR(host->fclk);
13851c6a0718SPierre Ossman 		goto err_free_iclk;
13861c6a0718SPierre Ossman 	}
13871c6a0718SPierre Ossman 
13883451c067SRussell King 	dma_cap_zero(mask);
13893451c067SRussell King 	dma_cap_set(DMA_SLAVE, mask);
13903451c067SRussell King 
13913451c067SRussell King 	host->dma_tx_burst = -1;
13923451c067SRussell King 	host->dma_rx_burst = -1;
13933451c067SRussell King 
13943451c067SRussell King 	if (cpu_is_omap24xx())
13953451c067SRussell King 		sig = host->id == 0 ? OMAP24XX_DMA_MMC1_TX : OMAP24XX_DMA_MMC2_TX;
13963451c067SRussell King 	else
13973451c067SRussell King 		sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX;
13983451c067SRussell King 	host->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
13993451c067SRussell King #if 0
14003451c067SRussell King 	if (!host->dma_tx) {
14013451c067SRussell King 		dev_err(host->dev, "unable to obtain TX DMA engine channel %u\n",
14023451c067SRussell King 			sig);
14033451c067SRussell King 		goto err_dma;
14043451c067SRussell King 	}
14053451c067SRussell King #else
14063451c067SRussell King 	if (!host->dma_tx)
14073451c067SRussell King 		dev_warn(host->dev, "unable to obtain TX DMA engine channel %u\n",
14083451c067SRussell King 			sig);
14093451c067SRussell King #endif
14103451c067SRussell King 	if (cpu_is_omap24xx())
14113451c067SRussell King 		sig = host->id == 0 ? OMAP24XX_DMA_MMC1_RX : OMAP24XX_DMA_MMC2_RX;
14123451c067SRussell King 	else
14133451c067SRussell King 		sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX;
14143451c067SRussell King 	host->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
14153451c067SRussell King #if 0
14163451c067SRussell King 	if (!host->dma_rx) {
14173451c067SRussell King 		dev_err(host->dev, "unable to obtain RX DMA engine channel %u\n",
14183451c067SRussell King 			sig);
14193451c067SRussell King 		goto err_dma;
14203451c067SRussell King 	}
14213451c067SRussell King #else
14223451c067SRussell King 	if (!host->dma_rx)
14233451c067SRussell King 		dev_warn(host->dev, "unable to obtain RX DMA engine channel %u\n",
14243451c067SRussell King 			sig);
14253451c067SRussell King #endif
14263451c067SRussell King 
14271c6a0718SPierre Ossman 	ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host);
14281c6a0718SPierre Ossman 	if (ret)
14293451c067SRussell King 		goto err_free_dma;
14301c6a0718SPierre Ossman 
1431abfbe5f7SJuha Yrjola 	if (pdata->init != NULL) {
1432abfbe5f7SJuha Yrjola 		ret = pdata->init(&pdev->dev);
1433abfbe5f7SJuha Yrjola 		if (ret < 0)
1434abfbe5f7SJuha Yrjola 			goto err_free_irq;
1435abfbe5f7SJuha Yrjola 	}
14361c6a0718SPierre Ossman 
1437abfbe5f7SJuha Yrjola 	host->nr_slots = pdata->nr_slots;
1438ebbe6f88STony Lindgren 	host->reg_shift = (cpu_is_omap7xx() ? 1 : 2);
14393caf4140STony Lindgren 
14403caf4140STony Lindgren 	host->mmc_omap_wq = alloc_workqueue("mmc_omap", 0, 0);
14413caf4140STony Lindgren 	if (!host->mmc_omap_wq)
14423caf4140STony Lindgren 		goto err_plat_cleanup;
14433caf4140STony Lindgren 
1444abfbe5f7SJuha Yrjola 	for (i = 0; i < pdata->nr_slots; i++) {
1445abfbe5f7SJuha Yrjola 		ret = mmc_omap_new_slot(host, i);
1446abfbe5f7SJuha Yrjola 		if (ret < 0) {
1447abfbe5f7SJuha Yrjola 			while (--i >= 0)
1448abfbe5f7SJuha Yrjola 				mmc_omap_remove_slot(host->slots[i]);
1449abfbe5f7SJuha Yrjola 
14503caf4140STony Lindgren 			goto err_destroy_wq;
1451abfbe5f7SJuha Yrjola 		}
1452abfbe5f7SJuha Yrjola 	}
14531c6a0718SPierre Ossman 
14541c6a0718SPierre Ossman 	return 0;
14551c6a0718SPierre Ossman 
14563caf4140STony Lindgren err_destroy_wq:
14573caf4140STony Lindgren 	destroy_workqueue(host->mmc_omap_wq);
1458abfbe5f7SJuha Yrjola err_plat_cleanup:
1459abfbe5f7SJuha Yrjola 	if (pdata->cleanup)
1460abfbe5f7SJuha Yrjola 		pdata->cleanup(&pdev->dev);
1461abfbe5f7SJuha Yrjola err_free_irq:
1462abfbe5f7SJuha Yrjola 	free_irq(host->irq, host);
14633451c067SRussell King err_free_dma:
14643451c067SRussell King 	if (host->dma_tx)
14653451c067SRussell King 		dma_release_channel(host->dma_tx);
14663451c067SRussell King 	if (host->dma_rx)
14673451c067SRussell King 		dma_release_channel(host->dma_rx);
14681c6a0718SPierre Ossman 	clk_put(host->fclk);
14691c6a0718SPierre Ossman err_free_iclk:
14701c6a0718SPierre Ossman 	clk_disable(host->iclk);
14711c6a0718SPierre Ossman 	clk_put(host->iclk);
14721c6a0718SPierre Ossman err_free_mmc_host:
147355c381e4SRussell King 	iounmap(host->virt_base);
147455c381e4SRussell King err_ioremap:
1475abfbe5f7SJuha Yrjola 	kfree(host);
14761c6a0718SPierre Ossman err_free_mem_region:
14772092014dSChris Ball 	release_mem_region(res->start, resource_size(res));
14781c6a0718SPierre Ossman 	return ret;
14791c6a0718SPierre Ossman }
14801c6a0718SPierre Ossman 
1481*6e0ee714SBill Pemberton static int mmc_omap_remove(struct platform_device *pdev)
14821c6a0718SPierre Ossman {
14831c6a0718SPierre Ossman 	struct mmc_omap_host *host = platform_get_drvdata(pdev);
1484abfbe5f7SJuha Yrjola 	int i;
14851c6a0718SPierre Ossman 
14861c6a0718SPierre Ossman 	platform_set_drvdata(pdev, NULL);
14871c6a0718SPierre Ossman 
14881c6a0718SPierre Ossman 	BUG_ON(host == NULL);
14891c6a0718SPierre Ossman 
1490abfbe5f7SJuha Yrjola 	for (i = 0; i < host->nr_slots; i++)
1491abfbe5f7SJuha Yrjola 		mmc_omap_remove_slot(host->slots[i]);
14921c6a0718SPierre Ossman 
1493abfbe5f7SJuha Yrjola 	if (host->pdata->cleanup)
1494abfbe5f7SJuha Yrjola 		host->pdata->cleanup(&pdev->dev);
1495abfbe5f7SJuha Yrjola 
1496d4a36645SRussell King 	mmc_omap_fclk_enable(host, 0);
149749c1d9daSLadislav Michl 	free_irq(host->irq, host);
14981c6a0718SPierre Ossman 	clk_put(host->fclk);
1499d4a36645SRussell King 	clk_disable(host->iclk);
1500d4a36645SRussell King 	clk_put(host->iclk);
15011c6a0718SPierre Ossman 
15023451c067SRussell King 	if (host->dma_tx)
15033451c067SRussell King 		dma_release_channel(host->dma_tx);
15043451c067SRussell King 	if (host->dma_rx)
15053451c067SRussell King 		dma_release_channel(host->dma_rx);
15063451c067SRussell King 
150755c381e4SRussell King 	iounmap(host->virt_base);
15081c6a0718SPierre Ossman 	release_mem_region(pdev->resource[0].start,
15091c6a0718SPierre Ossman 			   pdev->resource[0].end - pdev->resource[0].start + 1);
1510b01a4f1cSVenkatraman S 	destroy_workqueue(host->mmc_omap_wq);
15111c6a0718SPierre Ossman 
1512abfbe5f7SJuha Yrjola 	kfree(host);
15131c6a0718SPierre Ossman 
15141c6a0718SPierre Ossman 	return 0;
15151c6a0718SPierre Ossman }
15161c6a0718SPierre Ossman 
15171c6a0718SPierre Ossman #ifdef CONFIG_PM
15181c6a0718SPierre Ossman static int mmc_omap_suspend(struct platform_device *pdev, pm_message_t mesg)
15191c6a0718SPierre Ossman {
1520abfbe5f7SJuha Yrjola 	int i, ret = 0;
15211c6a0718SPierre Ossman 	struct mmc_omap_host *host = platform_get_drvdata(pdev);
15221c6a0718SPierre Ossman 
1523abfbe5f7SJuha Yrjola 	if (host == NULL || host->suspended)
15241c6a0718SPierre Ossman 		return 0;
15251c6a0718SPierre Ossman 
1526abfbe5f7SJuha Yrjola 	for (i = 0; i < host->nr_slots; i++) {
1527abfbe5f7SJuha Yrjola 		struct mmc_omap_slot *slot;
1528abfbe5f7SJuha Yrjola 
1529abfbe5f7SJuha Yrjola 		slot = host->slots[i];
15301a13f8faSMatt Fleming 		ret = mmc_suspend_host(slot->mmc);
1531abfbe5f7SJuha Yrjola 		if (ret < 0) {
1532abfbe5f7SJuha Yrjola 			while (--i >= 0) {
1533abfbe5f7SJuha Yrjola 				slot = host->slots[i];
1534abfbe5f7SJuha Yrjola 				mmc_resume_host(slot->mmc);
15351c6a0718SPierre Ossman 			}
15361c6a0718SPierre Ossman 			return ret;
15371c6a0718SPierre Ossman 		}
1538abfbe5f7SJuha Yrjola 	}
1539abfbe5f7SJuha Yrjola 	host->suspended = 1;
1540abfbe5f7SJuha Yrjola 	return 0;
1541abfbe5f7SJuha Yrjola }
15421c6a0718SPierre Ossman 
15431c6a0718SPierre Ossman static int mmc_omap_resume(struct platform_device *pdev)
15441c6a0718SPierre Ossman {
1545abfbe5f7SJuha Yrjola 	int i, ret = 0;
15461c6a0718SPierre Ossman 	struct mmc_omap_host *host = platform_get_drvdata(pdev);
15471c6a0718SPierre Ossman 
1548abfbe5f7SJuha Yrjola 	if (host == NULL || !host->suspended)
15491c6a0718SPierre Ossman 		return 0;
15501c6a0718SPierre Ossman 
1551abfbe5f7SJuha Yrjola 	for (i = 0; i < host->nr_slots; i++) {
1552abfbe5f7SJuha Yrjola 		struct mmc_omap_slot *slot;
1553abfbe5f7SJuha Yrjola 		slot = host->slots[i];
1554abfbe5f7SJuha Yrjola 		ret = mmc_resume_host(slot->mmc);
1555abfbe5f7SJuha Yrjola 		if (ret < 0)
1556abfbe5f7SJuha Yrjola 			return ret;
1557abfbe5f7SJuha Yrjola 
15581c6a0718SPierre Ossman 		host->suspended = 0;
15591c6a0718SPierre Ossman 	}
1560abfbe5f7SJuha Yrjola 	return 0;
15611c6a0718SPierre Ossman }
15621c6a0718SPierre Ossman #else
15631c6a0718SPierre Ossman #define mmc_omap_suspend	NULL
15641c6a0718SPierre Ossman #define mmc_omap_resume		NULL
15651c6a0718SPierre Ossman #endif
15661c6a0718SPierre Ossman 
15671c6a0718SPierre Ossman static struct platform_driver mmc_omap_driver = {
1568b6e0703bSVenkatraman S 	.probe		= mmc_omap_probe,
15690433c143SBill Pemberton 	.remove		= mmc_omap_remove,
15701c6a0718SPierre Ossman 	.suspend	= mmc_omap_suspend,
15711c6a0718SPierre Ossman 	.resume		= mmc_omap_resume,
15721c6a0718SPierre Ossman 	.driver		= {
15731c6a0718SPierre Ossman 		.name	= DRIVER_NAME,
1574bc65c724SKay Sievers 		.owner	= THIS_MODULE,
15751c6a0718SPierre Ossman 	},
15761c6a0718SPierre Ossman };
15771c6a0718SPierre Ossman 
1578680f1b5bSVenkatraman S module_platform_driver(mmc_omap_driver);
15791c6a0718SPierre Ossman MODULE_DESCRIPTION("OMAP Multimedia Card driver");
15801c6a0718SPierre Ossman MODULE_LICENSE("GPL");
1581bc65c724SKay Sievers MODULE_ALIAS("platform:" DRIVER_NAME);
1582d36b6910SAl Viro MODULE_AUTHOR("Juha Yrjölä");
1583