xref: /openbmc/linux/drivers/mmc/host/moxart-mmc.c (revision 19d38f77)
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 */
11435ca91d1SSergei Antonov #define BUS_WIDTH_4_SUPPORT	BIT(3)
11535ca91d1SSergei Antonov #define BUS_WIDTH_4		BIT(2)
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 
moxart_init_sg(struct moxart_host * host,struct mmc_data * data)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 
moxart_next_sg(struct moxart_host * host)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 
moxart_wait_for_status(struct moxart_host * host,u32 mask,u32 * status)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 
moxart_send_command(struct moxart_host * host,struct mmc_command * cmd)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 
moxart_dma_complete(void * param)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 
moxart_transfer_dma(struct mmc_data * data,struct moxart_host * host)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 
moxart_transfer_pio(struct moxart_host * host)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;) {
341d4426322SSergei Antonov 				*sgp = ioread32(host->base + REG_DATA_WINDOW);
3421b66e94eSJonas Jensen 				sgp++;
3431b66e94eSJonas Jensen 				len += 4;
3441b66e94eSJonas Jensen 			}
3451b66e94eSJonas Jensen 			remain -= len;
3461b66e94eSJonas Jensen 		}
3471b66e94eSJonas Jensen 	}
3481b66e94eSJonas Jensen 
3491b66e94eSJonas Jensen 	data->bytes_xfered += host->data_remain - remain;
3501b66e94eSJonas Jensen 	host->data_remain = remain;
3511b66e94eSJonas Jensen 
3521b66e94eSJonas Jensen 	if (host->data_len != data->bytes_xfered)
3531b66e94eSJonas Jensen 		moxart_next_sg(host);
3541b66e94eSJonas Jensen 	else
3551b66e94eSJonas Jensen 		complete(&host->pio_complete);
3561b66e94eSJonas Jensen }
3571b66e94eSJonas Jensen 
moxart_prepare_data(struct moxart_host * host)3581b66e94eSJonas Jensen static void moxart_prepare_data(struct moxart_host *host)
3591b66e94eSJonas Jensen {
3601b66e94eSJonas Jensen 	struct mmc_data *data = host->mrq->cmd->data;
3611b66e94eSJonas Jensen 	u32 datactrl;
3621b66e94eSJonas Jensen 	int blksz_bits;
3631b66e94eSJonas Jensen 
3641b66e94eSJonas Jensen 	if (!data)
3651b66e94eSJonas Jensen 		return;
3661b66e94eSJonas Jensen 
3671b66e94eSJonas Jensen 	host->data_len = data->blocks * data->blksz;
3681b66e94eSJonas Jensen 	blksz_bits = ffs(data->blksz) - 1;
3691b66e94eSJonas Jensen 	BUG_ON(1 << blksz_bits != data->blksz);
3701b66e94eSJonas Jensen 
3711b66e94eSJonas Jensen 	moxart_init_sg(host, data);
3721b66e94eSJonas Jensen 
3731b66e94eSJonas Jensen 	datactrl = DCR_DATA_EN | (blksz_bits & DCR_BLK_SIZE);
3741b66e94eSJonas Jensen 
3751b66e94eSJonas Jensen 	if (data->flags & MMC_DATA_WRITE)
3761b66e94eSJonas Jensen 		datactrl |= DCR_DATA_WRITE;
3771b66e94eSJonas Jensen 
3781b66e94eSJonas Jensen 	if ((host->data_len > host->fifo_width) && host->have_dma)
3791b66e94eSJonas Jensen 		datactrl |= DCR_DMA_EN;
3801b66e94eSJonas Jensen 
3811b66e94eSJonas Jensen 	writel(DCR_DATA_FIFO_RESET, host->base + REG_DATA_CONTROL);
3821b66e94eSJonas Jensen 	writel(MASK_DATA | FIFO_URUN | FIFO_ORUN, host->base + REG_CLEAR);
3831b66e94eSJonas Jensen 	writel(host->rate, host->base + REG_DATA_TIMER);
3841b66e94eSJonas Jensen 	writel(host->data_len, host->base + REG_DATA_LENGTH);
3851b66e94eSJonas Jensen 	writel(datactrl, host->base + REG_DATA_CONTROL);
3861b66e94eSJonas Jensen }
3871b66e94eSJonas Jensen 
moxart_request(struct mmc_host * mmc,struct mmc_request * mrq)3881b66e94eSJonas Jensen static void moxart_request(struct mmc_host *mmc, struct mmc_request *mrq)
3891b66e94eSJonas Jensen {
3901b66e94eSJonas Jensen 	struct moxart_host *host = mmc_priv(mmc);
39141f469caSNicholas Mc Guire 	unsigned long flags;
3921b66e94eSJonas Jensen 	u32 status;
3931b66e94eSJonas Jensen 
3941b66e94eSJonas Jensen 	spin_lock_irqsave(&host->lock, flags);
3951b66e94eSJonas Jensen 
3961b66e94eSJonas Jensen 	init_completion(&host->dma_complete);
3971b66e94eSJonas Jensen 	init_completion(&host->pio_complete);
3981b66e94eSJonas Jensen 
3991b66e94eSJonas Jensen 	host->mrq = mrq;
4001b66e94eSJonas Jensen 
4011b66e94eSJonas Jensen 	if (readl(host->base + REG_STATUS) & CARD_DETECT) {
4021b66e94eSJonas Jensen 		mrq->cmd->error = -ETIMEDOUT;
4031b66e94eSJonas Jensen 		goto request_done;
4041b66e94eSJonas Jensen 	}
4051b66e94eSJonas Jensen 
4061b66e94eSJonas Jensen 	moxart_prepare_data(host);
4071b66e94eSJonas Jensen 	moxart_send_command(host, host->mrq->cmd);
4081b66e94eSJonas Jensen 
4091b66e94eSJonas Jensen 	if (mrq->cmd->data) {
4101b66e94eSJonas Jensen 		if ((host->data_len > host->fifo_width) && host->have_dma) {
4111b66e94eSJonas Jensen 
4121b66e94eSJonas Jensen 			writel(CARD_CHANGE, host->base + REG_INTERRUPT_MASK);
4131b66e94eSJonas Jensen 
4141b66e94eSJonas Jensen 			spin_unlock_irqrestore(&host->lock, flags);
4151b66e94eSJonas Jensen 
4161b66e94eSJonas Jensen 			moxart_transfer_dma(mrq->cmd->data, host);
4171b66e94eSJonas Jensen 
4181b66e94eSJonas Jensen 			spin_lock_irqsave(&host->lock, flags);
4191b66e94eSJonas Jensen 		} else {
4201b66e94eSJonas Jensen 
4211b66e94eSJonas Jensen 			writel(MASK_INTR_PIO, host->base + REG_INTERRUPT_MASK);
4221b66e94eSJonas Jensen 
4231b66e94eSJonas Jensen 			spin_unlock_irqrestore(&host->lock, flags);
4241b66e94eSJonas Jensen 
4251b66e94eSJonas Jensen 			/* PIO transfers start from interrupt. */
426ae3519b6SYang Li 			wait_for_completion_interruptible_timeout(&host->pio_complete,
427ae3519b6SYang Li 								  host->timeout);
4281b66e94eSJonas Jensen 
4291b66e94eSJonas Jensen 			spin_lock_irqsave(&host->lock, flags);
4301b66e94eSJonas Jensen 		}
4311b66e94eSJonas Jensen 
4321b66e94eSJonas Jensen 		if (host->is_removed) {
4331b66e94eSJonas Jensen 			dev_err(mmc_dev(host->mmc), "card removed\n");
4341b66e94eSJonas Jensen 			mrq->cmd->error = -ETIMEDOUT;
4351b66e94eSJonas Jensen 			goto request_done;
4361b66e94eSJonas Jensen 		}
4371b66e94eSJonas Jensen 
4381b66e94eSJonas Jensen 		if (moxart_wait_for_status(host, MASK_DATA, &status)
4391b66e94eSJonas Jensen 		    == -ETIMEDOUT) {
4401b66e94eSJonas Jensen 			mrq->cmd->data->error = -ETIMEDOUT;
4411b66e94eSJonas Jensen 			goto request_done;
4421b66e94eSJonas Jensen 		}
4431b66e94eSJonas Jensen 
4441b66e94eSJonas Jensen 		if (status & DATA_CRC_FAIL)
4451b66e94eSJonas Jensen 			mrq->cmd->data->error = -ETIMEDOUT;
4461b66e94eSJonas Jensen 
4471b66e94eSJonas Jensen 		if (mrq->cmd->data->stop)
4481b66e94eSJonas Jensen 			moxart_send_command(host, mrq->cmd->data->stop);
4491b66e94eSJonas Jensen 	}
4501b66e94eSJonas Jensen 
4511b66e94eSJonas Jensen request_done:
4521b66e94eSJonas Jensen 	spin_unlock_irqrestore(&host->lock, flags);
4531b66e94eSJonas Jensen 	mmc_request_done(host->mmc, mrq);
4541b66e94eSJonas Jensen }
4551b66e94eSJonas Jensen 
moxart_irq(int irq,void * devid)4561b66e94eSJonas Jensen static irqreturn_t moxart_irq(int irq, void *devid)
4571b66e94eSJonas Jensen {
4581b66e94eSJonas Jensen 	struct moxart_host *host = (struct moxart_host *)devid;
4591b66e94eSJonas Jensen 	u32 status;
4601b66e94eSJonas Jensen 
461120ae805STian Tao 	spin_lock(&host->lock);
4621b66e94eSJonas Jensen 
4631b66e94eSJonas Jensen 	status = readl(host->base + REG_STATUS);
4641b66e94eSJonas Jensen 	if (status & CARD_CHANGE) {
4651b66e94eSJonas Jensen 		host->is_removed = status & CARD_DETECT;
4661b66e94eSJonas Jensen 		if (host->is_removed && host->have_dma) {
4671b66e94eSJonas Jensen 			dmaengine_terminate_all(host->dma_chan_tx);
4681b66e94eSJonas Jensen 			dmaengine_terminate_all(host->dma_chan_rx);
4691b66e94eSJonas Jensen 		}
4701b66e94eSJonas Jensen 		host->mrq = NULL;
4711b66e94eSJonas Jensen 		writel(MASK_INTR_PIO, host->base + REG_CLEAR);
4721b66e94eSJonas Jensen 		writel(CARD_CHANGE, host->base + REG_INTERRUPT_MASK);
4731b66e94eSJonas Jensen 		mmc_detect_change(host->mmc, 0);
4741b66e94eSJonas Jensen 	}
4751b66e94eSJonas Jensen 	if (status & (FIFO_ORUN | FIFO_URUN) && host->mrq)
4761b66e94eSJonas Jensen 		moxart_transfer_pio(host);
4771b66e94eSJonas Jensen 
478120ae805STian Tao 	spin_unlock(&host->lock);
4791b66e94eSJonas Jensen 
4801b66e94eSJonas Jensen 	return IRQ_HANDLED;
4811b66e94eSJonas Jensen }
4821b66e94eSJonas Jensen 
moxart_set_ios(struct mmc_host * mmc,struct mmc_ios * ios)4831b66e94eSJonas Jensen static void moxart_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
4841b66e94eSJonas Jensen {
4851b66e94eSJonas Jensen 	struct moxart_host *host = mmc_priv(mmc);
4861b66e94eSJonas Jensen 	unsigned long flags;
4871b66e94eSJonas Jensen 	u8 power, div;
4881b66e94eSJonas Jensen 	u32 ctrl;
4891b66e94eSJonas Jensen 
4901b66e94eSJonas Jensen 	spin_lock_irqsave(&host->lock, flags);
4911b66e94eSJonas Jensen 
4921b66e94eSJonas Jensen 	if (ios->clock) {
4931b66e94eSJonas Jensen 		for (div = 0; div < CLK_DIV_MASK; ++div) {
4941b66e94eSJonas Jensen 			if (ios->clock >= host->sysclk / (2 * (div + 1)))
4951b66e94eSJonas Jensen 				break;
4961b66e94eSJonas Jensen 		}
4971b66e94eSJonas Jensen 		ctrl = CLK_SD | div;
4981b66e94eSJonas Jensen 		host->rate = host->sysclk / (2 * (div + 1));
4991b66e94eSJonas Jensen 		if (host->rate > host->sysclk)
5001b66e94eSJonas Jensen 			ctrl |= CLK_HISPD;
5011b66e94eSJonas Jensen 		writel(ctrl, host->base + REG_CLOCK_CONTROL);
5021b66e94eSJonas Jensen 	}
5031b66e94eSJonas Jensen 
5041b66e94eSJonas Jensen 	if (ios->power_mode == MMC_POWER_OFF) {
5051b66e94eSJonas Jensen 		writel(readl(host->base + REG_POWER_CONTROL) & ~SD_POWER_ON,
5061b66e94eSJonas Jensen 		       host->base + REG_POWER_CONTROL);
5071b66e94eSJonas Jensen 	} else {
5081b66e94eSJonas Jensen 		if (ios->vdd < MIN_POWER)
5091b66e94eSJonas Jensen 			power = 0;
5101b66e94eSJonas Jensen 		else
5111b66e94eSJonas Jensen 			power = ios->vdd - MIN_POWER;
5121b66e94eSJonas Jensen 
5131b66e94eSJonas Jensen 		writel(SD_POWER_ON | (u32) power,
5141b66e94eSJonas Jensen 		       host->base + REG_POWER_CONTROL);
5151b66e94eSJonas Jensen 	}
5161b66e94eSJonas Jensen 
5171b66e94eSJonas Jensen 	switch (ios->bus_width) {
5181b66e94eSJonas Jensen 	case MMC_BUS_WIDTH_4:
5191b66e94eSJonas Jensen 		writel(BUS_WIDTH_4, host->base + REG_BUS_WIDTH);
5201b66e94eSJonas Jensen 		break;
5211b66e94eSJonas Jensen 	default:
5221b66e94eSJonas Jensen 		writel(BUS_WIDTH_1, host->base + REG_BUS_WIDTH);
5231b66e94eSJonas Jensen 		break;
5241b66e94eSJonas Jensen 	}
5251b66e94eSJonas Jensen 
5261b66e94eSJonas Jensen 	spin_unlock_irqrestore(&host->lock, flags);
5271b66e94eSJonas Jensen }
5281b66e94eSJonas Jensen 
5291b66e94eSJonas Jensen 
moxart_get_ro(struct mmc_host * mmc)5301b66e94eSJonas Jensen static int moxart_get_ro(struct mmc_host *mmc)
5311b66e94eSJonas Jensen {
5321b66e94eSJonas Jensen 	struct moxart_host *host = mmc_priv(mmc);
5331b66e94eSJonas Jensen 
5341b66e94eSJonas Jensen 	return !!(readl(host->base + REG_STATUS) & WRITE_PROT);
5351b66e94eSJonas Jensen }
5361b66e94eSJonas Jensen 
537bc860c20SJulia Lawall static const struct mmc_host_ops moxart_ops = {
5381b66e94eSJonas Jensen 	.request = moxart_request,
5391b66e94eSJonas Jensen 	.set_ios = moxart_set_ios,
5401b66e94eSJonas Jensen 	.get_ro = moxart_get_ro,
5411b66e94eSJonas Jensen };
5421b66e94eSJonas Jensen 
moxart_probe(struct platform_device * pdev)5431b66e94eSJonas Jensen static int moxart_probe(struct platform_device *pdev)
5441b66e94eSJonas Jensen {
5451b66e94eSJonas Jensen 	struct device *dev = &pdev->dev;
5461b66e94eSJonas Jensen 	struct device_node *node = dev->of_node;
5471b66e94eSJonas Jensen 	struct resource res_mmc;
5481b66e94eSJonas Jensen 	struct mmc_host *mmc;
5491b66e94eSJonas Jensen 	struct moxart_host *host = NULL;
5501b66e94eSJonas Jensen 	struct dma_slave_config cfg;
5511b66e94eSJonas Jensen 	struct clk *clk;
5521b66e94eSJonas Jensen 	void __iomem *reg_mmc;
5531b66e94eSJonas Jensen 	int irq, ret;
5541b66e94eSJonas Jensen 	u32 i;
5551b66e94eSJonas Jensen 
5561b66e94eSJonas Jensen 	mmc = mmc_alloc_host(sizeof(struct moxart_host), dev);
5571b66e94eSJonas Jensen 	if (!mmc) {
5581b66e94eSJonas Jensen 		dev_err(dev, "mmc_alloc_host failed\n");
5591b66e94eSJonas Jensen 		ret = -ENOMEM;
5600eab756fSColin Ian King 		goto out_mmc;
5611b66e94eSJonas Jensen 	}
5621b66e94eSJonas Jensen 
5631b66e94eSJonas Jensen 	ret = of_address_to_resource(node, 0, &res_mmc);
5641b66e94eSJonas Jensen 	if (ret) {
5651b66e94eSJonas Jensen 		dev_err(dev, "of_address_to_resource failed\n");
5660eab756fSColin Ian King 		goto out_mmc;
5671b66e94eSJonas Jensen 	}
5681b66e94eSJonas Jensen 
5691b66e94eSJonas Jensen 	irq = irq_of_parse_and_map(node, 0);
5701b66e94eSJonas Jensen 	if (irq <= 0) {
5711b66e94eSJonas Jensen 		dev_err(dev, "irq_of_parse_and_map failed\n");
5721b66e94eSJonas Jensen 		ret = -EINVAL;
5730eab756fSColin Ian King 		goto out_mmc;
5741b66e94eSJonas Jensen 	}
5751b66e94eSJonas Jensen 
5763981c516SArnd Bergmann 	clk = devm_clk_get(dev, NULL);
5771b66e94eSJonas Jensen 	if (IS_ERR(clk)) {
5781b66e94eSJonas Jensen 		ret = PTR_ERR(clk);
5790eab756fSColin Ian King 		goto out_mmc;
5801b66e94eSJonas Jensen 	}
5811b66e94eSJonas Jensen 
5821b66e94eSJonas Jensen 	reg_mmc = devm_ioremap_resource(dev, &res_mmc);
5831b66e94eSJonas Jensen 	if (IS_ERR(reg_mmc)) {
5841b66e94eSJonas Jensen 		ret = PTR_ERR(reg_mmc);
5850eab756fSColin Ian King 		goto out_mmc;
5861b66e94eSJonas Jensen 	}
5871b66e94eSJonas Jensen 
5886d2b4218SUlf Hansson 	ret = mmc_of_parse(mmc);
5896d2b4218SUlf Hansson 	if (ret)
5900eab756fSColin Ian King 		goto out_mmc;
5911b66e94eSJonas Jensen 
5921b66e94eSJonas Jensen 	host = mmc_priv(mmc);
5931b66e94eSJonas Jensen 	host->mmc = mmc;
5941b66e94eSJonas Jensen 	host->base = reg_mmc;
5951b66e94eSJonas Jensen 	host->reg_phys = res_mmc.start;
5961b66e94eSJonas Jensen 	host->timeout = msecs_to_jiffies(1000);
5971b66e94eSJonas Jensen 	host->sysclk = clk_get_rate(clk);
5981b66e94eSJonas Jensen 	host->fifo_width = readl(host->base + REG_FEATURE) << 2;
599c2a93d75SPeter Ujfalusi 	host->dma_chan_tx = dma_request_chan(dev, "tx");
600c2a93d75SPeter Ujfalusi 	host->dma_chan_rx = dma_request_chan(dev, "rx");
6011b66e94eSJonas Jensen 
6021b66e94eSJonas Jensen 	spin_lock_init(&host->lock);
6031b66e94eSJonas Jensen 
6041b66e94eSJonas Jensen 	mmc->ops = &moxart_ops;
6051b66e94eSJonas Jensen 	mmc->f_max = DIV_ROUND_CLOSEST(host->sysclk, 2);
6061b66e94eSJonas Jensen 	mmc->f_min = DIV_ROUND_CLOSEST(host->sysclk, CLK_DIV_MASK * 2);
6071b66e94eSJonas Jensen 	mmc->ocr_avail = 0xffff00;	/* Support 2.0v - 3.6v power. */
60816b492ceSSergei Antonov 	mmc->max_blk_size = 2048; /* Max. block length in REG_DATA_CONTROL */
60916b492ceSSergei Antonov 	mmc->max_req_size = DATA_LEN_MASK; /* bits 0-23 in REG_DATA_LENGTH */
61016b492ceSSergei Antonov 	mmc->max_blk_count = mmc->max_req_size / 512;
6111b66e94eSJonas Jensen 
6121b66e94eSJonas Jensen 	if (IS_ERR(host->dma_chan_tx) || IS_ERR(host->dma_chan_rx)) {
6133981c516SArnd Bergmann 		if (PTR_ERR(host->dma_chan_tx) == -EPROBE_DEFER ||
6143981c516SArnd Bergmann 		    PTR_ERR(host->dma_chan_rx) == -EPROBE_DEFER) {
6153981c516SArnd Bergmann 			ret = -EPROBE_DEFER;
6163981c516SArnd Bergmann 			goto out;
6173981c516SArnd Bergmann 		}
6188105c2abSXin Xiong 		if (!IS_ERR(host->dma_chan_tx)) {
6198105c2abSXin Xiong 			dma_release_channel(host->dma_chan_tx);
6208105c2abSXin Xiong 			host->dma_chan_tx = NULL;
6218105c2abSXin Xiong 		}
6228105c2abSXin Xiong 		if (!IS_ERR(host->dma_chan_rx)) {
6238105c2abSXin Xiong 			dma_release_channel(host->dma_chan_rx);
6248105c2abSXin Xiong 			host->dma_chan_rx = NULL;
6258105c2abSXin Xiong 		}
6261b66e94eSJonas Jensen 		dev_dbg(dev, "PIO mode transfer enabled\n");
6271b66e94eSJonas Jensen 		host->have_dma = false;
62816b492ceSSergei Antonov 
62916b492ceSSergei Antonov 		mmc->max_seg_size = mmc->max_req_size;
6301b66e94eSJonas Jensen 	} else {
6311b66e94eSJonas Jensen 		dev_dbg(dev, "DMA channels found (%p,%p)\n",
6321b66e94eSJonas Jensen 			 host->dma_chan_tx, host->dma_chan_rx);
6331b66e94eSJonas Jensen 		host->have_dma = true;
6341b66e94eSJonas Jensen 
635ee516535STony Lindgren 		memset(&cfg, 0, sizeof(cfg));
6361b66e94eSJonas Jensen 		cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
6371b66e94eSJonas Jensen 		cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
6381b66e94eSJonas Jensen 
6391b66e94eSJonas Jensen 		cfg.direction = DMA_MEM_TO_DEV;
6401b66e94eSJonas Jensen 		cfg.src_addr = 0;
6411b66e94eSJonas Jensen 		cfg.dst_addr = host->reg_phys + REG_DATA_WINDOW;
6421b66e94eSJonas Jensen 		dmaengine_slave_config(host->dma_chan_tx, &cfg);
6431b66e94eSJonas Jensen 
6441b66e94eSJonas Jensen 		cfg.direction = DMA_DEV_TO_MEM;
6451b66e94eSJonas Jensen 		cfg.src_addr = host->reg_phys + REG_DATA_WINDOW;
6461b66e94eSJonas Jensen 		cfg.dst_addr = 0;
6471b66e94eSJonas Jensen 		dmaengine_slave_config(host->dma_chan_rx, &cfg);
64816b492ceSSergei Antonov 
64916b492ceSSergei Antonov 		mmc->max_seg_size = min3(mmc->max_req_size,
65016b492ceSSergei Antonov 			dma_get_max_seg_size(host->dma_chan_rx->device->dev),
65116b492ceSSergei Antonov 			dma_get_max_seg_size(host->dma_chan_tx->device->dev));
6521b66e94eSJonas Jensen 	}
6531b66e94eSJonas Jensen 
65435ca91d1SSergei Antonov 	if (readl(host->base + REG_BUS_WIDTH) & BUS_WIDTH_4_SUPPORT)
6551b66e94eSJonas Jensen 		mmc->caps |= MMC_CAP_4_BIT_DATA;
6561b66e94eSJonas Jensen 
6571b66e94eSJonas Jensen 	writel(0, host->base + REG_INTERRUPT_MASK);
6581b66e94eSJonas Jensen 
6591b66e94eSJonas Jensen 	writel(CMD_SDC_RESET, host->base + REG_COMMAND);
6601b66e94eSJonas Jensen 	for (i = 0; i < MAX_RETRIES; i++) {
6611b66e94eSJonas Jensen 		if (!(readl(host->base + REG_COMMAND) & CMD_SDC_RESET))
6621b66e94eSJonas Jensen 			break;
6631b66e94eSJonas Jensen 		udelay(5);
6641b66e94eSJonas Jensen 	}
6651b66e94eSJonas Jensen 
6661b66e94eSJonas Jensen 	ret = devm_request_irq(dev, irq, moxart_irq, 0, "moxart-mmc", host);
6671b66e94eSJonas Jensen 	if (ret)
6681b66e94eSJonas Jensen 		goto out;
6691b66e94eSJonas Jensen 
6701b66e94eSJonas Jensen 	dev_set_drvdata(dev, mmc);
6710ca18d09SYang Yingliang 	ret = mmc_add_host(mmc);
6720ca18d09SYang Yingliang 	if (ret)
6730ca18d09SYang Yingliang 		goto out;
6741b66e94eSJonas Jensen 
6751b66e94eSJonas Jensen 	dev_dbg(dev, "IRQ=%d, FIFO is %d bytes\n", irq, host->fifo_width);
6761b66e94eSJonas Jensen 
6771b66e94eSJonas Jensen 	return 0;
6781b66e94eSJonas Jensen 
6791b66e94eSJonas Jensen out:
6808105c2abSXin Xiong 	if (!IS_ERR_OR_NULL(host->dma_chan_tx))
6818105c2abSXin Xiong 		dma_release_channel(host->dma_chan_tx);
6828105c2abSXin Xiong 	if (!IS_ERR_OR_NULL(host->dma_chan_rx))
6838105c2abSXin Xiong 		dma_release_channel(host->dma_chan_rx);
6840eab756fSColin Ian King out_mmc:
6851b66e94eSJonas Jensen 	if (mmc)
6861b66e94eSJonas Jensen 		mmc_free_host(mmc);
6871b66e94eSJonas Jensen 	return ret;
6881b66e94eSJonas Jensen }
6891b66e94eSJonas Jensen 
moxart_remove(struct platform_device * pdev)690*19d38f77SYangtao Li static void moxart_remove(struct platform_device *pdev)
6911b66e94eSJonas Jensen {
6921b66e94eSJonas Jensen 	struct mmc_host *mmc = dev_get_drvdata(&pdev->dev);
6931b66e94eSJonas Jensen 	struct moxart_host *host = mmc_priv(mmc);
6941b66e94eSJonas Jensen 
6958105c2abSXin Xiong 	if (!IS_ERR_OR_NULL(host->dma_chan_tx))
6961b66e94eSJonas Jensen 		dma_release_channel(host->dma_chan_tx);
6978105c2abSXin Xiong 	if (!IS_ERR_OR_NULL(host->dma_chan_rx))
6981b66e94eSJonas Jensen 		dma_release_channel(host->dma_chan_rx);
6991b66e94eSJonas Jensen 	mmc_remove_host(mmc);
7001b66e94eSJonas Jensen 
7011b66e94eSJonas Jensen 	writel(0, host->base + REG_INTERRUPT_MASK);
7021b66e94eSJonas Jensen 	writel(0, host->base + REG_POWER_CONTROL);
7031b66e94eSJonas Jensen 	writel(readl(host->base + REG_CLOCK_CONTROL) | CLK_OFF,
7041b66e94eSJonas Jensen 	       host->base + REG_CLOCK_CONTROL);
705bd2db32eSGreg Kroah-Hartman 	mmc_free_host(mmc);
7061b66e94eSJonas Jensen }
7071b66e94eSJonas Jensen 
7081b66e94eSJonas Jensen static const struct of_device_id moxart_mmc_match[] = {
7091b66e94eSJonas Jensen 	{ .compatible = "moxa,moxart-mmc" },
7101b66e94eSJonas Jensen 	{ .compatible = "faraday,ftsdc010" },
7111b66e94eSJonas Jensen 	{ }
7121b66e94eSJonas Jensen };
71377452353SLuis de Bethencourt MODULE_DEVICE_TABLE(of, moxart_mmc_match);
7141b66e94eSJonas Jensen 
7151b66e94eSJonas Jensen static struct platform_driver moxart_mmc_driver = {
7161b66e94eSJonas Jensen 	.probe      = moxart_probe,
717*19d38f77SYangtao Li 	.remove_new = moxart_remove,
7181b66e94eSJonas Jensen 	.driver     = {
7191b66e94eSJonas Jensen 		.name		= "mmc-moxart",
72021b2cec6SDouglas Anderson 		.probe_type	= PROBE_PREFER_ASYNCHRONOUS,
7211b66e94eSJonas Jensen 		.of_match_table	= moxart_mmc_match,
7221b66e94eSJonas Jensen 	},
7231b66e94eSJonas Jensen };
7241b66e94eSJonas Jensen module_platform_driver(moxart_mmc_driver);
7251b66e94eSJonas Jensen 
7261b66e94eSJonas Jensen MODULE_ALIAS("platform:mmc-moxart");
7271b66e94eSJonas Jensen MODULE_DESCRIPTION("MOXA ART MMC driver");
7281b66e94eSJonas Jensen MODULE_LICENSE("GPL v2");
7291b66e94eSJonas Jensen MODULE_AUTHOR("Jonas Jensen <jonas.jensen@gmail.com>");
730