xref: /openbmc/linux/drivers/mmc/host/moxart-mmc.c (revision 8105c2ab)
11b66e94eSJonas Jensen /*
21b66e94eSJonas Jensen  * MOXA ART MMC host driver.
31b66e94eSJonas Jensen  *
41b66e94eSJonas Jensen  * Copyright (C) 2014 Jonas Jensen
51b66e94eSJonas Jensen  *
61b66e94eSJonas Jensen  * Jonas Jensen <jonas.jensen@gmail.com>
71b66e94eSJonas Jensen  *
81b66e94eSJonas Jensen  * Based on code from
91b66e94eSJonas Jensen  * Moxa Technologies Co., Ltd. <www.moxa.com>
101b66e94eSJonas Jensen  *
111b66e94eSJonas Jensen  * This file is licensed under the terms of the GNU General Public
121b66e94eSJonas Jensen  * License version 2.  This program is licensed "as is" without any
131b66e94eSJonas Jensen  * warranty of any kind, whether express or implied.
141b66e94eSJonas Jensen  */
151b66e94eSJonas Jensen 
161b66e94eSJonas Jensen #include <linux/module.h>
171b66e94eSJonas Jensen #include <linux/init.h>
181b66e94eSJonas Jensen #include <linux/platform_device.h>
191b66e94eSJonas Jensen #include <linux/delay.h>
203981c516SArnd Bergmann #include <linux/errno.h>
211b66e94eSJonas Jensen #include <linux/interrupt.h>
221b66e94eSJonas Jensen #include <linux/blkdev.h>
231b66e94eSJonas Jensen #include <linux/dma-mapping.h>
241b66e94eSJonas Jensen #include <linux/dmaengine.h>
251b66e94eSJonas Jensen #include <linux/mmc/host.h>
261b66e94eSJonas Jensen #include <linux/mmc/sd.h>
271b66e94eSJonas Jensen #include <linux/sched.h>
281b66e94eSJonas Jensen #include <linux/io.h>
291b66e94eSJonas Jensen #include <linux/of_address.h>
301b66e94eSJonas Jensen #include <linux/of_irq.h>
311b66e94eSJonas Jensen #include <linux/clk.h>
321b66e94eSJonas Jensen #include <linux/bitops.h>
331b66e94eSJonas Jensen #include <linux/of_dma.h>
341b66e94eSJonas Jensen #include <linux/spinlock.h>
351b66e94eSJonas Jensen 
361b66e94eSJonas Jensen #define REG_COMMAND		0
371b66e94eSJonas Jensen #define REG_ARGUMENT		4
381b66e94eSJonas Jensen #define REG_RESPONSE0		8
391b66e94eSJonas Jensen #define REG_RESPONSE1		12
401b66e94eSJonas Jensen #define REG_RESPONSE2		16
411b66e94eSJonas Jensen #define REG_RESPONSE3		20
421b66e94eSJonas Jensen #define REG_RESPONSE_COMMAND	24
431b66e94eSJonas Jensen #define REG_DATA_CONTROL	28
441b66e94eSJonas Jensen #define REG_DATA_TIMER		32
451b66e94eSJonas Jensen #define REG_DATA_LENGTH		36
461b66e94eSJonas Jensen #define REG_STATUS		40
471b66e94eSJonas Jensen #define REG_CLEAR		44
481b66e94eSJonas Jensen #define REG_INTERRUPT_MASK	48
491b66e94eSJonas Jensen #define REG_POWER_CONTROL	52
501b66e94eSJonas Jensen #define REG_CLOCK_CONTROL	56
511b66e94eSJonas Jensen #define REG_BUS_WIDTH		60
521b66e94eSJonas Jensen #define REG_DATA_WINDOW		64
531b66e94eSJonas Jensen #define REG_FEATURE		68
541b66e94eSJonas Jensen #define REG_REVISION		72
551b66e94eSJonas Jensen 
561b66e94eSJonas Jensen /* REG_COMMAND */
571b66e94eSJonas Jensen #define CMD_SDC_RESET		BIT(10)
581b66e94eSJonas Jensen #define CMD_EN			BIT(9)
591b66e94eSJonas Jensen #define CMD_APP_CMD		BIT(8)
601b66e94eSJonas Jensen #define CMD_LONG_RSP		BIT(7)
611b66e94eSJonas Jensen #define CMD_NEED_RSP		BIT(6)
621b66e94eSJonas Jensen #define CMD_IDX_MASK		0x3f
631b66e94eSJonas Jensen 
641b66e94eSJonas Jensen /* REG_RESPONSE_COMMAND */
651b66e94eSJonas Jensen #define RSP_CMD_APP		BIT(6)
661b66e94eSJonas Jensen #define RSP_CMD_IDX_MASK	0x3f
671b66e94eSJonas Jensen 
681b66e94eSJonas Jensen /* REG_DATA_CONTROL */
691b66e94eSJonas Jensen #define DCR_DATA_FIFO_RESET     BIT(8)
701b66e94eSJonas Jensen #define DCR_DATA_THRES          BIT(7)
711b66e94eSJonas Jensen #define DCR_DATA_EN		BIT(6)
721b66e94eSJonas Jensen #define DCR_DMA_EN		BIT(5)
731b66e94eSJonas Jensen #define DCR_DATA_WRITE		BIT(4)
741b66e94eSJonas Jensen #define DCR_BLK_SIZE		0x0f
751b66e94eSJonas Jensen 
761b66e94eSJonas Jensen /* REG_DATA_LENGTH */
771b66e94eSJonas Jensen #define DATA_LEN_MASK		0xffffff
781b66e94eSJonas Jensen 
791b66e94eSJonas Jensen /* REG_STATUS */
801b66e94eSJonas Jensen #define WRITE_PROT		BIT(12)
811b66e94eSJonas Jensen #define CARD_DETECT		BIT(11)
821b66e94eSJonas Jensen /* 1-10 below can be sent to either registers, interrupt or clear. */
831b66e94eSJonas Jensen #define CARD_CHANGE		BIT(10)
841b66e94eSJonas Jensen #define FIFO_ORUN		BIT(9)
851b66e94eSJonas Jensen #define FIFO_URUN		BIT(8)
861b66e94eSJonas Jensen #define DATA_END		BIT(7)
871b66e94eSJonas Jensen #define CMD_SENT		BIT(6)
881b66e94eSJonas Jensen #define DATA_CRC_OK		BIT(5)
891b66e94eSJonas Jensen #define RSP_CRC_OK		BIT(4)
901b66e94eSJonas Jensen #define DATA_TIMEOUT		BIT(3)
911b66e94eSJonas Jensen #define RSP_TIMEOUT		BIT(2)
921b66e94eSJonas Jensen #define DATA_CRC_FAIL		BIT(1)
931b66e94eSJonas Jensen #define RSP_CRC_FAIL		BIT(0)
941b66e94eSJonas Jensen 
951b66e94eSJonas Jensen #define MASK_RSP		(RSP_TIMEOUT | RSP_CRC_FAIL | \
961b66e94eSJonas Jensen 				 RSP_CRC_OK  | CARD_DETECT  | CMD_SENT)
971b66e94eSJonas Jensen 
981b66e94eSJonas Jensen #define MASK_DATA		(DATA_CRC_OK   | DATA_END | \
991b66e94eSJonas Jensen 				 DATA_CRC_FAIL | DATA_TIMEOUT)
1001b66e94eSJonas Jensen 
1011b66e94eSJonas Jensen #define MASK_INTR_PIO		(FIFO_URUN | FIFO_ORUN | CARD_CHANGE)
1021b66e94eSJonas Jensen 
1031b66e94eSJonas Jensen /* REG_POWER_CONTROL */
1041b66e94eSJonas Jensen #define SD_POWER_ON		BIT(4)
1051b66e94eSJonas Jensen #define SD_POWER_MASK		0x0f
1061b66e94eSJonas Jensen 
1071b66e94eSJonas Jensen /* REG_CLOCK_CONTROL */
1081b66e94eSJonas Jensen #define CLK_HISPD		BIT(9)
1091b66e94eSJonas Jensen #define CLK_OFF			BIT(8)
1101b66e94eSJonas Jensen #define CLK_SD			BIT(7)
1111b66e94eSJonas Jensen #define CLK_DIV_MASK		0x7f
1121b66e94eSJonas Jensen 
1131b66e94eSJonas Jensen /* REG_BUS_WIDTH */
1141b66e94eSJonas Jensen #define BUS_WIDTH_8		BIT(2)
1151b66e94eSJonas Jensen #define BUS_WIDTH_4		BIT(1)
1161b66e94eSJonas Jensen #define BUS_WIDTH_1		BIT(0)
1171b66e94eSJonas Jensen 
1181b66e94eSJonas Jensen #define MMC_VDD_360		23
1191b66e94eSJonas Jensen #define MIN_POWER		(MMC_VDD_360 - SD_POWER_MASK)
1201b66e94eSJonas Jensen #define MAX_RETRIES		500000
1211b66e94eSJonas Jensen 
1221b66e94eSJonas Jensen struct moxart_host {
1231b66e94eSJonas Jensen 	spinlock_t			lock;
1241b66e94eSJonas Jensen 
1251b66e94eSJonas Jensen 	void __iomem			*base;
1261b66e94eSJonas Jensen 
1271b66e94eSJonas Jensen 	phys_addr_t			reg_phys;
1281b66e94eSJonas Jensen 
1291b66e94eSJonas Jensen 	struct dma_chan			*dma_chan_tx;
1301b66e94eSJonas Jensen 	struct dma_chan                 *dma_chan_rx;
1311b66e94eSJonas Jensen 	struct dma_async_tx_descriptor	*tx_desc;
1321b66e94eSJonas Jensen 	struct mmc_host			*mmc;
1331b66e94eSJonas Jensen 	struct mmc_request		*mrq;
1341b66e94eSJonas Jensen 	struct scatterlist		*cur_sg;
1351b66e94eSJonas Jensen 	struct completion		dma_complete;
1361b66e94eSJonas Jensen 	struct completion		pio_complete;
1371b66e94eSJonas Jensen 
1381b66e94eSJonas Jensen 	u32				num_sg;
1391b66e94eSJonas Jensen 	u32				data_remain;
1401b66e94eSJonas Jensen 	u32				data_len;
1411b66e94eSJonas Jensen 	u32				fifo_width;
1421b66e94eSJonas Jensen 	u32				timeout;
1431b66e94eSJonas Jensen 	u32				rate;
1441b66e94eSJonas Jensen 
1451b66e94eSJonas Jensen 	long				sysclk;
1461b66e94eSJonas Jensen 
1471b66e94eSJonas Jensen 	bool				have_dma;
1481b66e94eSJonas Jensen 	bool				is_removed;
1491b66e94eSJonas Jensen };
1501b66e94eSJonas Jensen 
1511b66e94eSJonas Jensen static inline void moxart_init_sg(struct moxart_host *host,
1521b66e94eSJonas Jensen 				  struct mmc_data *data)
1531b66e94eSJonas Jensen {
1541b66e94eSJonas Jensen 	host->cur_sg = data->sg;
1551b66e94eSJonas Jensen 	host->num_sg = data->sg_len;
1561b66e94eSJonas Jensen 	host->data_remain = host->cur_sg->length;
1571b66e94eSJonas Jensen 
1581b66e94eSJonas Jensen 	if (host->data_remain > host->data_len)
1591b66e94eSJonas Jensen 		host->data_remain = host->data_len;
1601b66e94eSJonas Jensen }
1611b66e94eSJonas Jensen 
1621b66e94eSJonas Jensen static inline int moxart_next_sg(struct moxart_host *host)
1631b66e94eSJonas Jensen {
1641b66e94eSJonas Jensen 	int remain;
1651b66e94eSJonas Jensen 	struct mmc_data *data = host->mrq->cmd->data;
1661b66e94eSJonas Jensen 
1671b66e94eSJonas Jensen 	host->cur_sg++;
1681b66e94eSJonas Jensen 	host->num_sg--;
1691b66e94eSJonas Jensen 
1701b66e94eSJonas Jensen 	if (host->num_sg > 0) {
1711b66e94eSJonas Jensen 		host->data_remain = host->cur_sg->length;
1721b66e94eSJonas Jensen 		remain = host->data_len - data->bytes_xfered;
1731b66e94eSJonas Jensen 		if (remain > 0 && remain < host->data_remain)
1741b66e94eSJonas Jensen 			host->data_remain = remain;
1751b66e94eSJonas Jensen 	}
1761b66e94eSJonas Jensen 
1771b66e94eSJonas Jensen 	return host->num_sg;
1781b66e94eSJonas Jensen }
1791b66e94eSJonas Jensen 
1801b66e94eSJonas Jensen static int moxart_wait_for_status(struct moxart_host *host,
1811b66e94eSJonas Jensen 				  u32 mask, u32 *status)
1821b66e94eSJonas Jensen {
1831b66e94eSJonas Jensen 	int ret = -ETIMEDOUT;
1841b66e94eSJonas Jensen 	u32 i;
1851b66e94eSJonas Jensen 
1861b66e94eSJonas Jensen 	for (i = 0; i < MAX_RETRIES; i++) {
1871b66e94eSJonas Jensen 		*status = readl(host->base + REG_STATUS);
1881b66e94eSJonas Jensen 		if (!(*status & mask)) {
1891b66e94eSJonas Jensen 			udelay(5);
1901b66e94eSJonas Jensen 			continue;
1911b66e94eSJonas Jensen 		}
1921b66e94eSJonas Jensen 		writel(*status & mask, host->base + REG_CLEAR);
1931b66e94eSJonas Jensen 		ret = 0;
1941b66e94eSJonas Jensen 		break;
1951b66e94eSJonas Jensen 	}
1961b66e94eSJonas Jensen 
1971b66e94eSJonas Jensen 	if (ret)
1981b66e94eSJonas Jensen 		dev_err(mmc_dev(host->mmc), "timed out waiting for status\n");
1991b66e94eSJonas Jensen 
2001b66e94eSJonas Jensen 	return ret;
2011b66e94eSJonas Jensen }
2021b66e94eSJonas Jensen 
2031b66e94eSJonas Jensen 
2041b66e94eSJonas Jensen static void moxart_send_command(struct moxart_host *host,
2051b66e94eSJonas Jensen 	struct mmc_command *cmd)
2061b66e94eSJonas Jensen {
2071b66e94eSJonas Jensen 	u32 status, cmdctrl;
2081b66e94eSJonas Jensen 
2091b66e94eSJonas Jensen 	writel(RSP_TIMEOUT  | RSP_CRC_OK |
2101b66e94eSJonas Jensen 	       RSP_CRC_FAIL | CMD_SENT, host->base + REG_CLEAR);
2111b66e94eSJonas Jensen 	writel(cmd->arg, host->base + REG_ARGUMENT);
2121b66e94eSJonas Jensen 
2131b66e94eSJonas Jensen 	cmdctrl = cmd->opcode & CMD_IDX_MASK;
2141b66e94eSJonas Jensen 	if (cmdctrl == SD_APP_SET_BUS_WIDTH    || cmdctrl == SD_APP_OP_COND   ||
2151b66e94eSJonas Jensen 	    cmdctrl == SD_APP_SEND_SCR         || cmdctrl == SD_APP_SD_STATUS ||
2161b66e94eSJonas Jensen 	    cmdctrl == SD_APP_SEND_NUM_WR_BLKS)
2171b66e94eSJonas Jensen 		cmdctrl |= CMD_APP_CMD;
2181b66e94eSJonas Jensen 
2191b66e94eSJonas Jensen 	if (cmd->flags & MMC_RSP_PRESENT)
2201b66e94eSJonas Jensen 		cmdctrl |= CMD_NEED_RSP;
2211b66e94eSJonas Jensen 
2221b66e94eSJonas Jensen 	if (cmd->flags & MMC_RSP_136)
2231b66e94eSJonas Jensen 		cmdctrl |= CMD_LONG_RSP;
2241b66e94eSJonas Jensen 
2251b66e94eSJonas Jensen 	writel(cmdctrl | CMD_EN, host->base + REG_COMMAND);
2261b66e94eSJonas Jensen 
2271b66e94eSJonas Jensen 	if (moxart_wait_for_status(host, MASK_RSP, &status) == -ETIMEDOUT)
2281b66e94eSJonas Jensen 		cmd->error = -ETIMEDOUT;
2291b66e94eSJonas Jensen 
2301b66e94eSJonas Jensen 	if (status & RSP_TIMEOUT) {
2311b66e94eSJonas Jensen 		cmd->error = -ETIMEDOUT;
2321b66e94eSJonas Jensen 		return;
2331b66e94eSJonas Jensen 	}
2341b66e94eSJonas Jensen 	if (status & RSP_CRC_FAIL) {
2351b66e94eSJonas Jensen 		cmd->error = -EIO;
2361b66e94eSJonas Jensen 		return;
2371b66e94eSJonas Jensen 	}
2381b66e94eSJonas Jensen 	if (status & RSP_CRC_OK) {
2391b66e94eSJonas Jensen 		if (cmd->flags & MMC_RSP_136) {
2401b66e94eSJonas Jensen 			cmd->resp[3] = readl(host->base + REG_RESPONSE0);
2411b66e94eSJonas Jensen 			cmd->resp[2] = readl(host->base + REG_RESPONSE1);
2421b66e94eSJonas Jensen 			cmd->resp[1] = readl(host->base + REG_RESPONSE2);
2431b66e94eSJonas Jensen 			cmd->resp[0] = readl(host->base + REG_RESPONSE3);
2441b66e94eSJonas Jensen 		} else {
2451b66e94eSJonas Jensen 			cmd->resp[0] = readl(host->base + REG_RESPONSE0);
2461b66e94eSJonas Jensen 		}
2471b66e94eSJonas Jensen 	}
2481b66e94eSJonas Jensen }
2491b66e94eSJonas Jensen 
2501b66e94eSJonas Jensen static void moxart_dma_complete(void *param)
2511b66e94eSJonas Jensen {
2521b66e94eSJonas Jensen 	struct moxart_host *host = param;
2531b66e94eSJonas Jensen 
2541b66e94eSJonas Jensen 	complete(&host->dma_complete);
2551b66e94eSJonas Jensen }
2561b66e94eSJonas Jensen 
2571b66e94eSJonas Jensen static void moxart_transfer_dma(struct mmc_data *data, struct moxart_host *host)
2581b66e94eSJonas Jensen {
259feeef096SHeiner Kallweit 	u32 len, dir_slave;
2601b66e94eSJonas Jensen 	struct dma_async_tx_descriptor *desc = NULL;
2611b66e94eSJonas Jensen 	struct dma_chan *dma_chan;
2621b66e94eSJonas Jensen 
2631b66e94eSJonas Jensen 	if (host->data_len == data->bytes_xfered)
2641b66e94eSJonas Jensen 		return;
2651b66e94eSJonas Jensen 
2661b66e94eSJonas Jensen 	if (data->flags & MMC_DATA_WRITE) {
2671b66e94eSJonas Jensen 		dma_chan = host->dma_chan_tx;
2681b66e94eSJonas Jensen 		dir_slave = DMA_MEM_TO_DEV;
2691b66e94eSJonas Jensen 	} else {
2701b66e94eSJonas Jensen 		dma_chan = host->dma_chan_rx;
2711b66e94eSJonas Jensen 		dir_slave = DMA_DEV_TO_MEM;
2721b66e94eSJonas Jensen 	}
2731b66e94eSJonas Jensen 
2741b66e94eSJonas Jensen 	len = dma_map_sg(dma_chan->device->dev, data->sg,
275feeef096SHeiner Kallweit 			 data->sg_len, mmc_get_dma_dir(data));
2761b66e94eSJonas Jensen 
2771b66e94eSJonas Jensen 	if (len > 0) {
2781b66e94eSJonas Jensen 		desc = dmaengine_prep_slave_sg(dma_chan, data->sg,
2791b66e94eSJonas Jensen 					       len, dir_slave,
2801b66e94eSJonas Jensen 					       DMA_PREP_INTERRUPT |
2811b66e94eSJonas Jensen 					       DMA_CTRL_ACK);
2821b66e94eSJonas Jensen 	} else {
2831b66e94eSJonas Jensen 		dev_err(mmc_dev(host->mmc), "dma_map_sg returned zero length\n");
2841b66e94eSJonas Jensen 	}
2851b66e94eSJonas Jensen 
2861b66e94eSJonas Jensen 	if (desc) {
2871b66e94eSJonas Jensen 		host->tx_desc = desc;
2881b66e94eSJonas Jensen 		desc->callback = moxart_dma_complete;
2891b66e94eSJonas Jensen 		desc->callback_param = host;
2901b66e94eSJonas Jensen 		dmaengine_submit(desc);
2911b66e94eSJonas Jensen 		dma_async_issue_pending(dma_chan);
2921b66e94eSJonas Jensen 	}
2931b66e94eSJonas Jensen 
2941b66e94eSJonas Jensen 	data->bytes_xfered += host->data_remain;
2951b66e94eSJonas Jensen 
296ae3519b6SYang Li 	wait_for_completion_interruptible_timeout(&host->dma_complete,
297ae3519b6SYang Li 						  host->timeout);
2981b66e94eSJonas Jensen 
2991b66e94eSJonas Jensen 	dma_unmap_sg(dma_chan->device->dev,
3001b66e94eSJonas Jensen 		     data->sg, data->sg_len,
301feeef096SHeiner Kallweit 		     mmc_get_dma_dir(data));
3021b66e94eSJonas Jensen }
3031b66e94eSJonas Jensen 
3041b66e94eSJonas Jensen 
3051b66e94eSJonas Jensen static void moxart_transfer_pio(struct moxart_host *host)
3061b66e94eSJonas Jensen {
3071b66e94eSJonas Jensen 	struct mmc_data *data = host->mrq->cmd->data;
3081b66e94eSJonas Jensen 	u32 *sgp, len = 0, remain, status;
3091b66e94eSJonas Jensen 
3101b66e94eSJonas Jensen 	if (host->data_len == data->bytes_xfered)
3111b66e94eSJonas Jensen 		return;
3121b66e94eSJonas Jensen 
3131b66e94eSJonas Jensen 	sgp = sg_virt(host->cur_sg);
3141b66e94eSJonas Jensen 	remain = host->data_remain;
3151b66e94eSJonas Jensen 
3161b66e94eSJonas Jensen 	if (data->flags & MMC_DATA_WRITE) {
3171b66e94eSJonas Jensen 		while (remain > 0) {
3181b66e94eSJonas Jensen 			if (moxart_wait_for_status(host, FIFO_URUN, &status)
3191b66e94eSJonas Jensen 			     == -ETIMEDOUT) {
3201b66e94eSJonas Jensen 				data->error = -ETIMEDOUT;
3211b66e94eSJonas Jensen 				complete(&host->pio_complete);
3221b66e94eSJonas Jensen 				return;
3231b66e94eSJonas Jensen 			}
3241b66e94eSJonas Jensen 			for (len = 0; len < remain && len < host->fifo_width;) {
3251b66e94eSJonas Jensen 				iowrite32(*sgp, host->base + REG_DATA_WINDOW);
3261b66e94eSJonas Jensen 				sgp++;
3271b66e94eSJonas Jensen 				len += 4;
3281b66e94eSJonas Jensen 			}
3291b66e94eSJonas Jensen 			remain -= len;
3301b66e94eSJonas Jensen 		}
3311b66e94eSJonas Jensen 
3321b66e94eSJonas Jensen 	} else {
3331b66e94eSJonas Jensen 		while (remain > 0) {
3341b66e94eSJonas Jensen 			if (moxart_wait_for_status(host, FIFO_ORUN, &status)
3351b66e94eSJonas Jensen 			    == -ETIMEDOUT) {
3361b66e94eSJonas Jensen 				data->error = -ETIMEDOUT;
3371b66e94eSJonas Jensen 				complete(&host->pio_complete);
3381b66e94eSJonas Jensen 				return;
3391b66e94eSJonas Jensen 			}
3401b66e94eSJonas Jensen 			for (len = 0; len < remain && len < host->fifo_width;) {
3411b66e94eSJonas Jensen 				/* SCR data must be read in big endian. */
3421b66e94eSJonas Jensen 				if (data->mrq->cmd->opcode == SD_APP_SEND_SCR)
3431b66e94eSJonas Jensen 					*sgp = ioread32be(host->base +
3441b66e94eSJonas Jensen 							  REG_DATA_WINDOW);
3451b66e94eSJonas Jensen 				else
3461b66e94eSJonas Jensen 					*sgp = ioread32(host->base +
3471b66e94eSJonas Jensen 							REG_DATA_WINDOW);
3481b66e94eSJonas Jensen 				sgp++;
3491b66e94eSJonas Jensen 				len += 4;
3501b66e94eSJonas Jensen 			}
3511b66e94eSJonas Jensen 			remain -= len;
3521b66e94eSJonas Jensen 		}
3531b66e94eSJonas Jensen 	}
3541b66e94eSJonas Jensen 
3551b66e94eSJonas Jensen 	data->bytes_xfered += host->data_remain - remain;
3561b66e94eSJonas Jensen 	host->data_remain = remain;
3571b66e94eSJonas Jensen 
3581b66e94eSJonas Jensen 	if (host->data_len != data->bytes_xfered)
3591b66e94eSJonas Jensen 		moxart_next_sg(host);
3601b66e94eSJonas Jensen 	else
3611b66e94eSJonas Jensen 		complete(&host->pio_complete);
3621b66e94eSJonas Jensen }
3631b66e94eSJonas Jensen 
3641b66e94eSJonas Jensen static void moxart_prepare_data(struct moxart_host *host)
3651b66e94eSJonas Jensen {
3661b66e94eSJonas Jensen 	struct mmc_data *data = host->mrq->cmd->data;
3671b66e94eSJonas Jensen 	u32 datactrl;
3681b66e94eSJonas Jensen 	int blksz_bits;
3691b66e94eSJonas Jensen 
3701b66e94eSJonas Jensen 	if (!data)
3711b66e94eSJonas Jensen 		return;
3721b66e94eSJonas Jensen 
3731b66e94eSJonas Jensen 	host->data_len = data->blocks * data->blksz;
3741b66e94eSJonas Jensen 	blksz_bits = ffs(data->blksz) - 1;
3751b66e94eSJonas Jensen 	BUG_ON(1 << blksz_bits != data->blksz);
3761b66e94eSJonas Jensen 
3771b66e94eSJonas Jensen 	moxart_init_sg(host, data);
3781b66e94eSJonas Jensen 
3791b66e94eSJonas Jensen 	datactrl = DCR_DATA_EN | (blksz_bits & DCR_BLK_SIZE);
3801b66e94eSJonas Jensen 
3811b66e94eSJonas Jensen 	if (data->flags & MMC_DATA_WRITE)
3821b66e94eSJonas Jensen 		datactrl |= DCR_DATA_WRITE;
3831b66e94eSJonas Jensen 
3841b66e94eSJonas Jensen 	if ((host->data_len > host->fifo_width) && host->have_dma)
3851b66e94eSJonas Jensen 		datactrl |= DCR_DMA_EN;
3861b66e94eSJonas Jensen 
3871b66e94eSJonas Jensen 	writel(DCR_DATA_FIFO_RESET, host->base + REG_DATA_CONTROL);
3881b66e94eSJonas Jensen 	writel(MASK_DATA | FIFO_URUN | FIFO_ORUN, host->base + REG_CLEAR);
3891b66e94eSJonas Jensen 	writel(host->rate, host->base + REG_DATA_TIMER);
3901b66e94eSJonas Jensen 	writel(host->data_len, host->base + REG_DATA_LENGTH);
3911b66e94eSJonas Jensen 	writel(datactrl, host->base + REG_DATA_CONTROL);
3921b66e94eSJonas Jensen }
3931b66e94eSJonas Jensen 
3941b66e94eSJonas Jensen static void moxart_request(struct mmc_host *mmc, struct mmc_request *mrq)
3951b66e94eSJonas Jensen {
3961b66e94eSJonas Jensen 	struct moxart_host *host = mmc_priv(mmc);
39741f469caSNicholas Mc Guire 	unsigned long flags;
3981b66e94eSJonas Jensen 	u32 status;
3991b66e94eSJonas Jensen 
4001b66e94eSJonas Jensen 	spin_lock_irqsave(&host->lock, flags);
4011b66e94eSJonas Jensen 
4021b66e94eSJonas Jensen 	init_completion(&host->dma_complete);
4031b66e94eSJonas Jensen 	init_completion(&host->pio_complete);
4041b66e94eSJonas Jensen 
4051b66e94eSJonas Jensen 	host->mrq = mrq;
4061b66e94eSJonas Jensen 
4071b66e94eSJonas Jensen 	if (readl(host->base + REG_STATUS) & CARD_DETECT) {
4081b66e94eSJonas Jensen 		mrq->cmd->error = -ETIMEDOUT;
4091b66e94eSJonas Jensen 		goto request_done;
4101b66e94eSJonas Jensen 	}
4111b66e94eSJonas Jensen 
4121b66e94eSJonas Jensen 	moxart_prepare_data(host);
4131b66e94eSJonas Jensen 	moxart_send_command(host, host->mrq->cmd);
4141b66e94eSJonas Jensen 
4151b66e94eSJonas Jensen 	if (mrq->cmd->data) {
4161b66e94eSJonas Jensen 		if ((host->data_len > host->fifo_width) && host->have_dma) {
4171b66e94eSJonas Jensen 
4181b66e94eSJonas Jensen 			writel(CARD_CHANGE, host->base + REG_INTERRUPT_MASK);
4191b66e94eSJonas Jensen 
4201b66e94eSJonas Jensen 			spin_unlock_irqrestore(&host->lock, flags);
4211b66e94eSJonas Jensen 
4221b66e94eSJonas Jensen 			moxart_transfer_dma(mrq->cmd->data, host);
4231b66e94eSJonas Jensen 
4241b66e94eSJonas Jensen 			spin_lock_irqsave(&host->lock, flags);
4251b66e94eSJonas Jensen 		} else {
4261b66e94eSJonas Jensen 
4271b66e94eSJonas Jensen 			writel(MASK_INTR_PIO, host->base + REG_INTERRUPT_MASK);
4281b66e94eSJonas Jensen 
4291b66e94eSJonas Jensen 			spin_unlock_irqrestore(&host->lock, flags);
4301b66e94eSJonas Jensen 
4311b66e94eSJonas Jensen 			/* PIO transfers start from interrupt. */
432ae3519b6SYang Li 			wait_for_completion_interruptible_timeout(&host->pio_complete,
433ae3519b6SYang Li 								  host->timeout);
4341b66e94eSJonas Jensen 
4351b66e94eSJonas Jensen 			spin_lock_irqsave(&host->lock, flags);
4361b66e94eSJonas Jensen 		}
4371b66e94eSJonas Jensen 
4381b66e94eSJonas Jensen 		if (host->is_removed) {
4391b66e94eSJonas Jensen 			dev_err(mmc_dev(host->mmc), "card removed\n");
4401b66e94eSJonas Jensen 			mrq->cmd->error = -ETIMEDOUT;
4411b66e94eSJonas Jensen 			goto request_done;
4421b66e94eSJonas Jensen 		}
4431b66e94eSJonas Jensen 
4441b66e94eSJonas Jensen 		if (moxart_wait_for_status(host, MASK_DATA, &status)
4451b66e94eSJonas Jensen 		    == -ETIMEDOUT) {
4461b66e94eSJonas Jensen 			mrq->cmd->data->error = -ETIMEDOUT;
4471b66e94eSJonas Jensen 			goto request_done;
4481b66e94eSJonas Jensen 		}
4491b66e94eSJonas Jensen 
4501b66e94eSJonas Jensen 		if (status & DATA_CRC_FAIL)
4511b66e94eSJonas Jensen 			mrq->cmd->data->error = -ETIMEDOUT;
4521b66e94eSJonas Jensen 
4531b66e94eSJonas Jensen 		if (mrq->cmd->data->stop)
4541b66e94eSJonas Jensen 			moxart_send_command(host, mrq->cmd->data->stop);
4551b66e94eSJonas Jensen 	}
4561b66e94eSJonas Jensen 
4571b66e94eSJonas Jensen request_done:
4581b66e94eSJonas Jensen 	spin_unlock_irqrestore(&host->lock, flags);
4591b66e94eSJonas Jensen 	mmc_request_done(host->mmc, mrq);
4601b66e94eSJonas Jensen }
4611b66e94eSJonas Jensen 
4621b66e94eSJonas Jensen static irqreturn_t moxart_irq(int irq, void *devid)
4631b66e94eSJonas Jensen {
4641b66e94eSJonas Jensen 	struct moxart_host *host = (struct moxart_host *)devid;
4651b66e94eSJonas Jensen 	u32 status;
4661b66e94eSJonas Jensen 
467120ae805STian Tao 	spin_lock(&host->lock);
4681b66e94eSJonas Jensen 
4691b66e94eSJonas Jensen 	status = readl(host->base + REG_STATUS);
4701b66e94eSJonas Jensen 	if (status & CARD_CHANGE) {
4711b66e94eSJonas Jensen 		host->is_removed = status & CARD_DETECT;
4721b66e94eSJonas Jensen 		if (host->is_removed && host->have_dma) {
4731b66e94eSJonas Jensen 			dmaengine_terminate_all(host->dma_chan_tx);
4741b66e94eSJonas Jensen 			dmaengine_terminate_all(host->dma_chan_rx);
4751b66e94eSJonas Jensen 		}
4761b66e94eSJonas Jensen 		host->mrq = NULL;
4771b66e94eSJonas Jensen 		writel(MASK_INTR_PIO, host->base + REG_CLEAR);
4781b66e94eSJonas Jensen 		writel(CARD_CHANGE, host->base + REG_INTERRUPT_MASK);
4791b66e94eSJonas Jensen 		mmc_detect_change(host->mmc, 0);
4801b66e94eSJonas Jensen 	}
4811b66e94eSJonas Jensen 	if (status & (FIFO_ORUN | FIFO_URUN) && host->mrq)
4821b66e94eSJonas Jensen 		moxart_transfer_pio(host);
4831b66e94eSJonas Jensen 
484120ae805STian Tao 	spin_unlock(&host->lock);
4851b66e94eSJonas Jensen 
4861b66e94eSJonas Jensen 	return IRQ_HANDLED;
4871b66e94eSJonas Jensen }
4881b66e94eSJonas Jensen 
4891b66e94eSJonas Jensen static void moxart_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
4901b66e94eSJonas Jensen {
4911b66e94eSJonas Jensen 	struct moxart_host *host = mmc_priv(mmc);
4921b66e94eSJonas Jensen 	unsigned long flags;
4931b66e94eSJonas Jensen 	u8 power, div;
4941b66e94eSJonas Jensen 	u32 ctrl;
4951b66e94eSJonas Jensen 
4961b66e94eSJonas Jensen 	spin_lock_irqsave(&host->lock, flags);
4971b66e94eSJonas Jensen 
4981b66e94eSJonas Jensen 	if (ios->clock) {
4991b66e94eSJonas Jensen 		for (div = 0; div < CLK_DIV_MASK; ++div) {
5001b66e94eSJonas Jensen 			if (ios->clock >= host->sysclk / (2 * (div + 1)))
5011b66e94eSJonas Jensen 				break;
5021b66e94eSJonas Jensen 		}
5031b66e94eSJonas Jensen 		ctrl = CLK_SD | div;
5041b66e94eSJonas Jensen 		host->rate = host->sysclk / (2 * (div + 1));
5051b66e94eSJonas Jensen 		if (host->rate > host->sysclk)
5061b66e94eSJonas Jensen 			ctrl |= CLK_HISPD;
5071b66e94eSJonas Jensen 		writel(ctrl, host->base + REG_CLOCK_CONTROL);
5081b66e94eSJonas Jensen 	}
5091b66e94eSJonas Jensen 
5101b66e94eSJonas Jensen 	if (ios->power_mode == MMC_POWER_OFF) {
5111b66e94eSJonas Jensen 		writel(readl(host->base + REG_POWER_CONTROL) & ~SD_POWER_ON,
5121b66e94eSJonas Jensen 		       host->base + REG_POWER_CONTROL);
5131b66e94eSJonas Jensen 	} else {
5141b66e94eSJonas Jensen 		if (ios->vdd < MIN_POWER)
5151b66e94eSJonas Jensen 			power = 0;
5161b66e94eSJonas Jensen 		else
5171b66e94eSJonas Jensen 			power = ios->vdd - MIN_POWER;
5181b66e94eSJonas Jensen 
5191b66e94eSJonas Jensen 		writel(SD_POWER_ON | (u32) power,
5201b66e94eSJonas Jensen 		       host->base + REG_POWER_CONTROL);
5211b66e94eSJonas Jensen 	}
5221b66e94eSJonas Jensen 
5231b66e94eSJonas Jensen 	switch (ios->bus_width) {
5241b66e94eSJonas Jensen 	case MMC_BUS_WIDTH_4:
5251b66e94eSJonas Jensen 		writel(BUS_WIDTH_4, host->base + REG_BUS_WIDTH);
5261b66e94eSJonas Jensen 		break;
5271b66e94eSJonas Jensen 	case MMC_BUS_WIDTH_8:
5281b66e94eSJonas Jensen 		writel(BUS_WIDTH_8, host->base + REG_BUS_WIDTH);
5291b66e94eSJonas Jensen 		break;
5301b66e94eSJonas Jensen 	default:
5311b66e94eSJonas Jensen 		writel(BUS_WIDTH_1, host->base + REG_BUS_WIDTH);
5321b66e94eSJonas Jensen 		break;
5331b66e94eSJonas Jensen 	}
5341b66e94eSJonas Jensen 
5351b66e94eSJonas Jensen 	spin_unlock_irqrestore(&host->lock, flags);
5361b66e94eSJonas Jensen }
5371b66e94eSJonas Jensen 
5381b66e94eSJonas Jensen 
5391b66e94eSJonas Jensen static int moxart_get_ro(struct mmc_host *mmc)
5401b66e94eSJonas Jensen {
5411b66e94eSJonas Jensen 	struct moxart_host *host = mmc_priv(mmc);
5421b66e94eSJonas Jensen 
5431b66e94eSJonas Jensen 	return !!(readl(host->base + REG_STATUS) & WRITE_PROT);
5441b66e94eSJonas Jensen }
5451b66e94eSJonas Jensen 
546bc860c20SJulia Lawall static const struct mmc_host_ops moxart_ops = {
5471b66e94eSJonas Jensen 	.request = moxart_request,
5481b66e94eSJonas Jensen 	.set_ios = moxart_set_ios,
5491b66e94eSJonas Jensen 	.get_ro = moxart_get_ro,
5501b66e94eSJonas Jensen };
5511b66e94eSJonas Jensen 
5521b66e94eSJonas Jensen static int moxart_probe(struct platform_device *pdev)
5531b66e94eSJonas Jensen {
5541b66e94eSJonas Jensen 	struct device *dev = &pdev->dev;
5551b66e94eSJonas Jensen 	struct device_node *node = dev->of_node;
5561b66e94eSJonas Jensen 	struct resource res_mmc;
5571b66e94eSJonas Jensen 	struct mmc_host *mmc;
5581b66e94eSJonas Jensen 	struct moxart_host *host = NULL;
5591b66e94eSJonas Jensen 	struct dma_slave_config cfg;
5601b66e94eSJonas Jensen 	struct clk *clk;
5611b66e94eSJonas Jensen 	void __iomem *reg_mmc;
5621b66e94eSJonas Jensen 	int irq, ret;
5631b66e94eSJonas Jensen 	u32 i;
5641b66e94eSJonas Jensen 
5651b66e94eSJonas Jensen 	mmc = mmc_alloc_host(sizeof(struct moxart_host), dev);
5661b66e94eSJonas Jensen 	if (!mmc) {
5671b66e94eSJonas Jensen 		dev_err(dev, "mmc_alloc_host failed\n");
5681b66e94eSJonas Jensen 		ret = -ENOMEM;
5691b66e94eSJonas Jensen 		goto out;
5701b66e94eSJonas Jensen 	}
5711b66e94eSJonas Jensen 
5721b66e94eSJonas Jensen 	ret = of_address_to_resource(node, 0, &res_mmc);
5731b66e94eSJonas Jensen 	if (ret) {
5741b66e94eSJonas Jensen 		dev_err(dev, "of_address_to_resource failed\n");
5751b66e94eSJonas Jensen 		goto out;
5761b66e94eSJonas Jensen 	}
5771b66e94eSJonas Jensen 
5781b66e94eSJonas Jensen 	irq = irq_of_parse_and_map(node, 0);
5791b66e94eSJonas Jensen 	if (irq <= 0) {
5801b66e94eSJonas Jensen 		dev_err(dev, "irq_of_parse_and_map failed\n");
5811b66e94eSJonas Jensen 		ret = -EINVAL;
5821b66e94eSJonas Jensen 		goto out;
5831b66e94eSJonas Jensen 	}
5841b66e94eSJonas Jensen 
5853981c516SArnd Bergmann 	clk = devm_clk_get(dev, NULL);
5861b66e94eSJonas Jensen 	if (IS_ERR(clk)) {
5871b66e94eSJonas Jensen 		ret = PTR_ERR(clk);
5881b66e94eSJonas Jensen 		goto out;
5891b66e94eSJonas Jensen 	}
5901b66e94eSJonas Jensen 
5911b66e94eSJonas Jensen 	reg_mmc = devm_ioremap_resource(dev, &res_mmc);
5921b66e94eSJonas Jensen 	if (IS_ERR(reg_mmc)) {
5931b66e94eSJonas Jensen 		ret = PTR_ERR(reg_mmc);
5941b66e94eSJonas Jensen 		goto out;
5951b66e94eSJonas Jensen 	}
5961b66e94eSJonas Jensen 
5976d2b4218SUlf Hansson 	ret = mmc_of_parse(mmc);
5986d2b4218SUlf Hansson 	if (ret)
5996d2b4218SUlf Hansson 		goto out;
6001b66e94eSJonas Jensen 
6011b66e94eSJonas Jensen 	host = mmc_priv(mmc);
6021b66e94eSJonas Jensen 	host->mmc = mmc;
6031b66e94eSJonas Jensen 	host->base = reg_mmc;
6041b66e94eSJonas Jensen 	host->reg_phys = res_mmc.start;
6051b66e94eSJonas Jensen 	host->timeout = msecs_to_jiffies(1000);
6061b66e94eSJonas Jensen 	host->sysclk = clk_get_rate(clk);
6071b66e94eSJonas Jensen 	host->fifo_width = readl(host->base + REG_FEATURE) << 2;
608c2a93d75SPeter Ujfalusi 	host->dma_chan_tx = dma_request_chan(dev, "tx");
609c2a93d75SPeter Ujfalusi 	host->dma_chan_rx = dma_request_chan(dev, "rx");
6101b66e94eSJonas Jensen 
6111b66e94eSJonas Jensen 	spin_lock_init(&host->lock);
6121b66e94eSJonas Jensen 
6131b66e94eSJonas Jensen 	mmc->ops = &moxart_ops;
6141b66e94eSJonas Jensen 	mmc->f_max = DIV_ROUND_CLOSEST(host->sysclk, 2);
6151b66e94eSJonas Jensen 	mmc->f_min = DIV_ROUND_CLOSEST(host->sysclk, CLK_DIV_MASK * 2);
6161b66e94eSJonas Jensen 	mmc->ocr_avail = 0xffff00;	/* Support 2.0v - 3.6v power. */
6171b66e94eSJonas Jensen 
6181b66e94eSJonas Jensen 	if (IS_ERR(host->dma_chan_tx) || IS_ERR(host->dma_chan_rx)) {
6193981c516SArnd Bergmann 		if (PTR_ERR(host->dma_chan_tx) == -EPROBE_DEFER ||
6203981c516SArnd Bergmann 		    PTR_ERR(host->dma_chan_rx) == -EPROBE_DEFER) {
6213981c516SArnd Bergmann 			ret = -EPROBE_DEFER;
6223981c516SArnd Bergmann 			goto out;
6233981c516SArnd Bergmann 		}
624*8105c2abSXin Xiong 		if (!IS_ERR(host->dma_chan_tx)) {
625*8105c2abSXin Xiong 			dma_release_channel(host->dma_chan_tx);
626*8105c2abSXin Xiong 			host->dma_chan_tx = NULL;
627*8105c2abSXin Xiong 		}
628*8105c2abSXin Xiong 		if (!IS_ERR(host->dma_chan_rx)) {
629*8105c2abSXin Xiong 			dma_release_channel(host->dma_chan_rx);
630*8105c2abSXin Xiong 			host->dma_chan_rx = NULL;
631*8105c2abSXin Xiong 		}
6321b66e94eSJonas Jensen 		dev_dbg(dev, "PIO mode transfer enabled\n");
6331b66e94eSJonas Jensen 		host->have_dma = false;
6341b66e94eSJonas Jensen 	} else {
6351b66e94eSJonas Jensen 		dev_dbg(dev, "DMA channels found (%p,%p)\n",
6361b66e94eSJonas Jensen 			 host->dma_chan_tx, host->dma_chan_rx);
6371b66e94eSJonas Jensen 		host->have_dma = true;
6381b66e94eSJonas Jensen 
639ee516535STony Lindgren 		memset(&cfg, 0, sizeof(cfg));
6401b66e94eSJonas Jensen 		cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
6411b66e94eSJonas Jensen 		cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
6421b66e94eSJonas Jensen 
6431b66e94eSJonas Jensen 		cfg.direction = DMA_MEM_TO_DEV;
6441b66e94eSJonas Jensen 		cfg.src_addr = 0;
6451b66e94eSJonas Jensen 		cfg.dst_addr = host->reg_phys + REG_DATA_WINDOW;
6461b66e94eSJonas Jensen 		dmaengine_slave_config(host->dma_chan_tx, &cfg);
6471b66e94eSJonas Jensen 
6481b66e94eSJonas Jensen 		cfg.direction = DMA_DEV_TO_MEM;
6491b66e94eSJonas Jensen 		cfg.src_addr = host->reg_phys + REG_DATA_WINDOW;
6501b66e94eSJonas Jensen 		cfg.dst_addr = 0;
6511b66e94eSJonas Jensen 		dmaengine_slave_config(host->dma_chan_rx, &cfg);
6521b66e94eSJonas Jensen 	}
6531b66e94eSJonas Jensen 
6541b66e94eSJonas Jensen 	switch ((readl(host->base + REG_BUS_WIDTH) >> 3) & 3) {
6551b66e94eSJonas Jensen 	case 1:
6561b66e94eSJonas Jensen 		mmc->caps |= MMC_CAP_4_BIT_DATA;
6571b66e94eSJonas Jensen 		break;
6581b66e94eSJonas Jensen 	case 2:
6591b66e94eSJonas Jensen 		mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA;
6601b66e94eSJonas Jensen 		break;
6611b66e94eSJonas Jensen 	default:
6621b66e94eSJonas Jensen 		break;
6631b66e94eSJonas Jensen 	}
6641b66e94eSJonas Jensen 
6651b66e94eSJonas Jensen 	writel(0, host->base + REG_INTERRUPT_MASK);
6661b66e94eSJonas Jensen 
6671b66e94eSJonas Jensen 	writel(CMD_SDC_RESET, host->base + REG_COMMAND);
6681b66e94eSJonas Jensen 	for (i = 0; i < MAX_RETRIES; i++) {
6691b66e94eSJonas Jensen 		if (!(readl(host->base + REG_COMMAND) & CMD_SDC_RESET))
6701b66e94eSJonas Jensen 			break;
6711b66e94eSJonas Jensen 		udelay(5);
6721b66e94eSJonas Jensen 	}
6731b66e94eSJonas Jensen 
6741b66e94eSJonas Jensen 	ret = devm_request_irq(dev, irq, moxart_irq, 0, "moxart-mmc", host);
6751b66e94eSJonas Jensen 	if (ret)
6761b66e94eSJonas Jensen 		goto out;
6771b66e94eSJonas Jensen 
6781b66e94eSJonas Jensen 	dev_set_drvdata(dev, mmc);
6791b66e94eSJonas Jensen 	mmc_add_host(mmc);
6801b66e94eSJonas Jensen 
6811b66e94eSJonas Jensen 	dev_dbg(dev, "IRQ=%d, FIFO is %d bytes\n", irq, host->fifo_width);
6821b66e94eSJonas Jensen 
6831b66e94eSJonas Jensen 	return 0;
6841b66e94eSJonas Jensen 
6851b66e94eSJonas Jensen out:
686*8105c2abSXin Xiong 	if (!IS_ERR_OR_NULL(host->dma_chan_tx))
687*8105c2abSXin Xiong 		dma_release_channel(host->dma_chan_tx);
688*8105c2abSXin Xiong 	if (!IS_ERR_OR_NULL(host->dma_chan_rx))
689*8105c2abSXin Xiong 		dma_release_channel(host->dma_chan_rx);
6901b66e94eSJonas Jensen 	if (mmc)
6911b66e94eSJonas Jensen 		mmc_free_host(mmc);
6921b66e94eSJonas Jensen 	return ret;
6931b66e94eSJonas Jensen }
6941b66e94eSJonas Jensen 
6951b66e94eSJonas Jensen static int moxart_remove(struct platform_device *pdev)
6961b66e94eSJonas Jensen {
6971b66e94eSJonas Jensen 	struct mmc_host *mmc = dev_get_drvdata(&pdev->dev);
6981b66e94eSJonas Jensen 	struct moxart_host *host = mmc_priv(mmc);
6991b66e94eSJonas Jensen 
7001b66e94eSJonas Jensen 	dev_set_drvdata(&pdev->dev, NULL);
7011b66e94eSJonas Jensen 
702*8105c2abSXin Xiong 	if (!IS_ERR_OR_NULL(host->dma_chan_tx))
7031b66e94eSJonas Jensen 		dma_release_channel(host->dma_chan_tx);
704*8105c2abSXin Xiong 	if (!IS_ERR_OR_NULL(host->dma_chan_rx))
7051b66e94eSJonas Jensen 		dma_release_channel(host->dma_chan_rx);
7061b66e94eSJonas Jensen 	mmc_remove_host(mmc);
7071b66e94eSJonas Jensen 	mmc_free_host(mmc);
7081b66e94eSJonas Jensen 
7091b66e94eSJonas Jensen 	writel(0, host->base + REG_INTERRUPT_MASK);
7101b66e94eSJonas Jensen 	writel(0, host->base + REG_POWER_CONTROL);
7111b66e94eSJonas Jensen 	writel(readl(host->base + REG_CLOCK_CONTROL) | CLK_OFF,
7121b66e94eSJonas Jensen 	       host->base + REG_CLOCK_CONTROL);
7136b28f2c4SKrzysztof Kozlowski 
7141b66e94eSJonas Jensen 	return 0;
7151b66e94eSJonas Jensen }
7161b66e94eSJonas Jensen 
7171b66e94eSJonas Jensen static const struct of_device_id moxart_mmc_match[] = {
7181b66e94eSJonas Jensen 	{ .compatible = "moxa,moxart-mmc" },
7191b66e94eSJonas Jensen 	{ .compatible = "faraday,ftsdc010" },
7201b66e94eSJonas Jensen 	{ }
7211b66e94eSJonas Jensen };
72277452353SLuis de Bethencourt MODULE_DEVICE_TABLE(of, moxart_mmc_match);
7231b66e94eSJonas Jensen 
7241b66e94eSJonas Jensen static struct platform_driver moxart_mmc_driver = {
7251b66e94eSJonas Jensen 	.probe      = moxart_probe,
7261b66e94eSJonas Jensen 	.remove     = moxart_remove,
7271b66e94eSJonas Jensen 	.driver     = {
7281b66e94eSJonas Jensen 		.name		= "mmc-moxart",
72921b2cec6SDouglas Anderson 		.probe_type	= PROBE_PREFER_ASYNCHRONOUS,
7301b66e94eSJonas Jensen 		.of_match_table	= moxart_mmc_match,
7311b66e94eSJonas Jensen 	},
7321b66e94eSJonas Jensen };
7331b66e94eSJonas Jensen module_platform_driver(moxart_mmc_driver);
7341b66e94eSJonas Jensen 
7351b66e94eSJonas Jensen MODULE_ALIAS("platform:mmc-moxart");
7361b66e94eSJonas Jensen MODULE_DESCRIPTION("MOXA ART MMC driver");
7371b66e94eSJonas Jensen MODULE_LICENSE("GPL v2");
7381b66e94eSJonas Jensen MODULE_AUTHOR("Jonas Jensen <jonas.jensen@gmail.com>");
739