162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  linux/drivers/mmc/host/mxcmmc.c - Freescale i.MX MMCI driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  This is a driver for the SDHC controller found in Freescale MX2/MX3
662306a36Sopenharmony_ci *  SoCs. It is basically the same hardware as found on MX1 (imxmmc.c).
762306a36Sopenharmony_ci *  Unlike the hardware found on MX1, this hardware just works and does
862306a36Sopenharmony_ci *  not need all the quirks found in imxmmc.c, hence the separate driver.
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci *  Copyright (C) 2008 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
1162306a36Sopenharmony_ci *  Copyright (C) 2006 Pavel Pisa, PiKRON <ppisa@pikron.com>
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci *  derived from pxamci.c by Russell King
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/init.h>
1862306a36Sopenharmony_ci#include <linux/ioport.h>
1962306a36Sopenharmony_ci#include <linux/platform_device.h>
2062306a36Sopenharmony_ci#include <linux/highmem.h>
2162306a36Sopenharmony_ci#include <linux/interrupt.h>
2262306a36Sopenharmony_ci#include <linux/irq.h>
2362306a36Sopenharmony_ci#include <linux/blkdev.h>
2462306a36Sopenharmony_ci#include <linux/dma-mapping.h>
2562306a36Sopenharmony_ci#include <linux/mmc/host.h>
2662306a36Sopenharmony_ci#include <linux/mmc/card.h>
2762306a36Sopenharmony_ci#include <linux/delay.h>
2862306a36Sopenharmony_ci#include <linux/clk.h>
2962306a36Sopenharmony_ci#include <linux/io.h>
3062306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
3162306a36Sopenharmony_ci#include <linux/dmaengine.h>
3262306a36Sopenharmony_ci#include <linux/types.h>
3362306a36Sopenharmony_ci#include <linux/of.h>
3462306a36Sopenharmony_ci#include <linux/of_dma.h>
3562306a36Sopenharmony_ci#include <linux/mmc/slot-gpio.h>
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#include <asm/dma.h>
3862306a36Sopenharmony_ci#include <asm/irq.h>
3962306a36Sopenharmony_ci#include <linux/platform_data/mmc-mxcmmc.h>
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#include <linux/dma/imx-dma.h>
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#define DRIVER_NAME "mxc-mmc"
4462306a36Sopenharmony_ci#define MXCMCI_TIMEOUT_MS 10000
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#define MMC_REG_STR_STP_CLK		0x00
4762306a36Sopenharmony_ci#define MMC_REG_STATUS			0x04
4862306a36Sopenharmony_ci#define MMC_REG_CLK_RATE		0x08
4962306a36Sopenharmony_ci#define MMC_REG_CMD_DAT_CONT		0x0C
5062306a36Sopenharmony_ci#define MMC_REG_RES_TO			0x10
5162306a36Sopenharmony_ci#define MMC_REG_READ_TO			0x14
5262306a36Sopenharmony_ci#define MMC_REG_BLK_LEN			0x18
5362306a36Sopenharmony_ci#define MMC_REG_NOB			0x1C
5462306a36Sopenharmony_ci#define MMC_REG_REV_NO			0x20
5562306a36Sopenharmony_ci#define MMC_REG_INT_CNTR		0x24
5662306a36Sopenharmony_ci#define MMC_REG_CMD			0x28
5762306a36Sopenharmony_ci#define MMC_REG_ARG			0x2C
5862306a36Sopenharmony_ci#define MMC_REG_RES_FIFO		0x34
5962306a36Sopenharmony_ci#define MMC_REG_BUFFER_ACCESS		0x38
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#define STR_STP_CLK_RESET               (1 << 3)
6262306a36Sopenharmony_ci#define STR_STP_CLK_START_CLK           (1 << 1)
6362306a36Sopenharmony_ci#define STR_STP_CLK_STOP_CLK            (1 << 0)
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci#define STATUS_CARD_INSERTION		(1 << 31)
6662306a36Sopenharmony_ci#define STATUS_CARD_REMOVAL		(1 << 30)
6762306a36Sopenharmony_ci#define STATUS_YBUF_EMPTY		(1 << 29)
6862306a36Sopenharmony_ci#define STATUS_XBUF_EMPTY		(1 << 28)
6962306a36Sopenharmony_ci#define STATUS_YBUF_FULL		(1 << 27)
7062306a36Sopenharmony_ci#define STATUS_XBUF_FULL		(1 << 26)
7162306a36Sopenharmony_ci#define STATUS_BUF_UND_RUN		(1 << 25)
7262306a36Sopenharmony_ci#define STATUS_BUF_OVFL			(1 << 24)
7362306a36Sopenharmony_ci#define STATUS_SDIO_INT_ACTIVE		(1 << 14)
7462306a36Sopenharmony_ci#define STATUS_END_CMD_RESP		(1 << 13)
7562306a36Sopenharmony_ci#define STATUS_WRITE_OP_DONE		(1 << 12)
7662306a36Sopenharmony_ci#define STATUS_DATA_TRANS_DONE		(1 << 11)
7762306a36Sopenharmony_ci#define STATUS_READ_OP_DONE		(1 << 11)
7862306a36Sopenharmony_ci#define STATUS_WR_CRC_ERROR_CODE_MASK	(3 << 10)
7962306a36Sopenharmony_ci#define STATUS_CARD_BUS_CLK_RUN		(1 << 8)
8062306a36Sopenharmony_ci#define STATUS_BUF_READ_RDY		(1 << 7)
8162306a36Sopenharmony_ci#define STATUS_BUF_WRITE_RDY		(1 << 6)
8262306a36Sopenharmony_ci#define STATUS_RESP_CRC_ERR		(1 << 5)
8362306a36Sopenharmony_ci#define STATUS_CRC_READ_ERR		(1 << 3)
8462306a36Sopenharmony_ci#define STATUS_CRC_WRITE_ERR		(1 << 2)
8562306a36Sopenharmony_ci#define STATUS_TIME_OUT_RESP		(1 << 1)
8662306a36Sopenharmony_ci#define STATUS_TIME_OUT_READ		(1 << 0)
8762306a36Sopenharmony_ci#define STATUS_ERR_MASK			0x2f
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci#define CMD_DAT_CONT_CMD_RESP_LONG_OFF	(1 << 12)
9062306a36Sopenharmony_ci#define CMD_DAT_CONT_STOP_READWAIT	(1 << 11)
9162306a36Sopenharmony_ci#define CMD_DAT_CONT_START_READWAIT	(1 << 10)
9262306a36Sopenharmony_ci#define CMD_DAT_CONT_BUS_WIDTH_4	(2 << 8)
9362306a36Sopenharmony_ci#define CMD_DAT_CONT_INIT		(1 << 7)
9462306a36Sopenharmony_ci#define CMD_DAT_CONT_WRITE		(1 << 4)
9562306a36Sopenharmony_ci#define CMD_DAT_CONT_DATA_ENABLE	(1 << 3)
9662306a36Sopenharmony_ci#define CMD_DAT_CONT_RESPONSE_48BIT_CRC	(1 << 0)
9762306a36Sopenharmony_ci#define CMD_DAT_CONT_RESPONSE_136BIT	(2 << 0)
9862306a36Sopenharmony_ci#define CMD_DAT_CONT_RESPONSE_48BIT	(3 << 0)
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci#define INT_SDIO_INT_WKP_EN		(1 << 18)
10162306a36Sopenharmony_ci#define INT_CARD_INSERTION_WKP_EN	(1 << 17)
10262306a36Sopenharmony_ci#define INT_CARD_REMOVAL_WKP_EN		(1 << 16)
10362306a36Sopenharmony_ci#define INT_CARD_INSERTION_EN		(1 << 15)
10462306a36Sopenharmony_ci#define INT_CARD_REMOVAL_EN		(1 << 14)
10562306a36Sopenharmony_ci#define INT_SDIO_IRQ_EN			(1 << 13)
10662306a36Sopenharmony_ci#define INT_DAT0_EN			(1 << 12)
10762306a36Sopenharmony_ci#define INT_BUF_READ_EN			(1 << 4)
10862306a36Sopenharmony_ci#define INT_BUF_WRITE_EN		(1 << 3)
10962306a36Sopenharmony_ci#define INT_END_CMD_RES_EN		(1 << 2)
11062306a36Sopenharmony_ci#define INT_WRITE_OP_DONE_EN		(1 << 1)
11162306a36Sopenharmony_ci#define INT_READ_OP_EN			(1 << 0)
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cienum mxcmci_type {
11462306a36Sopenharmony_ci	IMX21_MMC,
11562306a36Sopenharmony_ci	IMX31_MMC,
11662306a36Sopenharmony_ci	MPC512X_MMC,
11762306a36Sopenharmony_ci};
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistruct mxcmci_host {
12062306a36Sopenharmony_ci	struct mmc_host		*mmc;
12162306a36Sopenharmony_ci	void __iomem		*base;
12262306a36Sopenharmony_ci	dma_addr_t		phys_base;
12362306a36Sopenharmony_ci	int			detect_irq;
12462306a36Sopenharmony_ci	struct dma_chan		*dma;
12562306a36Sopenharmony_ci	struct dma_async_tx_descriptor *desc;
12662306a36Sopenharmony_ci	int			do_dma;
12762306a36Sopenharmony_ci	int			default_irq_mask;
12862306a36Sopenharmony_ci	int			use_sdio;
12962306a36Sopenharmony_ci	unsigned int		power_mode;
13062306a36Sopenharmony_ci	struct imxmmc_platform_data *pdata;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	struct mmc_request	*req;
13362306a36Sopenharmony_ci	struct mmc_command	*cmd;
13462306a36Sopenharmony_ci	struct mmc_data		*data;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	unsigned int		datasize;
13762306a36Sopenharmony_ci	unsigned int		dma_dir;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	u16			rev_no;
14062306a36Sopenharmony_ci	unsigned int		cmdat;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	struct clk		*clk_ipg;
14362306a36Sopenharmony_ci	struct clk		*clk_per;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	int			clock;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	struct work_struct	datawork;
14862306a36Sopenharmony_ci	spinlock_t		lock;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	int			burstlen;
15162306a36Sopenharmony_ci	int			dmareq;
15262306a36Sopenharmony_ci	struct dma_slave_config dma_slave_config;
15362306a36Sopenharmony_ci	struct imx_dma_data	dma_data;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	struct timer_list	watchdog;
15662306a36Sopenharmony_ci	enum mxcmci_type	devtype;
15762306a36Sopenharmony_ci};
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic const struct of_device_id mxcmci_of_match[] = {
16062306a36Sopenharmony_ci	{
16162306a36Sopenharmony_ci		.compatible = "fsl,imx21-mmc",
16262306a36Sopenharmony_ci		.data = (void *) IMX21_MMC,
16362306a36Sopenharmony_ci	}, {
16462306a36Sopenharmony_ci		.compatible = "fsl,imx31-mmc",
16562306a36Sopenharmony_ci		.data = (void *) IMX31_MMC,
16662306a36Sopenharmony_ci	}, {
16762306a36Sopenharmony_ci		.compatible = "fsl,mpc5121-sdhc",
16862306a36Sopenharmony_ci		.data = (void *) MPC512X_MMC,
16962306a36Sopenharmony_ci	}, {
17062306a36Sopenharmony_ci		/* sentinel */
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci};
17362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mxcmci_of_match);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic inline int is_imx31_mmc(struct mxcmci_host *host)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	return host->devtype == IMX31_MMC;
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic inline int is_mpc512x_mmc(struct mxcmci_host *host)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	return host->devtype == MPC512X_MMC;
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic inline u32 mxcmci_readl(struct mxcmci_host *host, int reg)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_PPC_MPC512x))
18862306a36Sopenharmony_ci		return ioread32be(host->base + reg);
18962306a36Sopenharmony_ci	else
19062306a36Sopenharmony_ci		return readl(host->base + reg);
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic inline void mxcmci_writel(struct mxcmci_host *host, u32 val, int reg)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_PPC_MPC512x))
19662306a36Sopenharmony_ci		iowrite32be(val, host->base + reg);
19762306a36Sopenharmony_ci	else
19862306a36Sopenharmony_ci		writel(val, host->base + reg);
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic inline u16 mxcmci_readw(struct mxcmci_host *host, int reg)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_PPC_MPC512x))
20462306a36Sopenharmony_ci		return ioread32be(host->base + reg);
20562306a36Sopenharmony_ci	else
20662306a36Sopenharmony_ci		return readw(host->base + reg);
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistatic inline void mxcmci_writew(struct mxcmci_host *host, u16 val, int reg)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_PPC_MPC512x))
21262306a36Sopenharmony_ci		iowrite32be(val, host->base + reg);
21362306a36Sopenharmony_ci	else
21462306a36Sopenharmony_ci		writew(val, host->base + reg);
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic void mxcmci_set_power(struct mxcmci_host *host, unsigned int vdd)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	if (!IS_ERR(host->mmc->supply.vmmc)) {
22262306a36Sopenharmony_ci		if (host->power_mode == MMC_POWER_UP)
22362306a36Sopenharmony_ci			mmc_regulator_set_ocr(host->mmc,
22462306a36Sopenharmony_ci					      host->mmc->supply.vmmc, vdd);
22562306a36Sopenharmony_ci		else if (host->power_mode == MMC_POWER_OFF)
22662306a36Sopenharmony_ci			mmc_regulator_set_ocr(host->mmc,
22762306a36Sopenharmony_ci					      host->mmc->supply.vmmc, 0);
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	if (host->pdata && host->pdata->setpower)
23162306a36Sopenharmony_ci		host->pdata->setpower(mmc_dev(host->mmc), vdd);
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistatic inline int mxcmci_use_dma(struct mxcmci_host *host)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	return host->do_dma;
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cistatic void mxcmci_softreset(struct mxcmci_host *host)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	int i;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	dev_dbg(mmc_dev(host->mmc), "mxcmci_softreset\n");
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	/* reset sequence */
24662306a36Sopenharmony_ci	mxcmci_writew(host, STR_STP_CLK_RESET, MMC_REG_STR_STP_CLK);
24762306a36Sopenharmony_ci	mxcmci_writew(host, STR_STP_CLK_RESET | STR_STP_CLK_START_CLK,
24862306a36Sopenharmony_ci			MMC_REG_STR_STP_CLK);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	for (i = 0; i < 8; i++)
25162306a36Sopenharmony_ci		mxcmci_writew(host, STR_STP_CLK_START_CLK, MMC_REG_STR_STP_CLK);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	mxcmci_writew(host, 0xff, MMC_REG_RES_TO);
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_PPC_MPC512x)
25762306a36Sopenharmony_cistatic inline void buffer_swap32(u32 *buf, int len)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	int i;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	for (i = 0; i < ((len + 3) / 4); i++) {
26262306a36Sopenharmony_ci		*buf = swab32(*buf);
26362306a36Sopenharmony_ci		buf++;
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistatic void mxcmci_swap_buffers(struct mmc_data *data)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	struct scatterlist *sg;
27062306a36Sopenharmony_ci	int i;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	for_each_sg(data->sg, sg, data->sg_len, i)
27362306a36Sopenharmony_ci		buffer_swap32(sg_virt(sg), sg->length);
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci#else
27662306a36Sopenharmony_cistatic inline void mxcmci_swap_buffers(struct mmc_data *data) {}
27762306a36Sopenharmony_ci#endif
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
28062306a36Sopenharmony_ci{
28162306a36Sopenharmony_ci	unsigned int nob = data->blocks;
28262306a36Sopenharmony_ci	unsigned int blksz = data->blksz;
28362306a36Sopenharmony_ci	unsigned int datasize = nob * blksz;
28462306a36Sopenharmony_ci	struct scatterlist *sg;
28562306a36Sopenharmony_ci	enum dma_transfer_direction slave_dirn;
28662306a36Sopenharmony_ci	int i, nents;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	host->data = data;
28962306a36Sopenharmony_ci	data->bytes_xfered = 0;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	mxcmci_writew(host, nob, MMC_REG_NOB);
29262306a36Sopenharmony_ci	mxcmci_writew(host, blksz, MMC_REG_BLK_LEN);
29362306a36Sopenharmony_ci	host->datasize = datasize;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	if (!mxcmci_use_dma(host))
29662306a36Sopenharmony_ci		return 0;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	for_each_sg(data->sg, sg, data->sg_len, i) {
29962306a36Sopenharmony_ci		if (sg->offset & 3 || sg->length & 3 || sg->length < 512) {
30062306a36Sopenharmony_ci			host->do_dma = 0;
30162306a36Sopenharmony_ci			return 0;
30262306a36Sopenharmony_ci		}
30362306a36Sopenharmony_ci	}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	if (data->flags & MMC_DATA_READ) {
30662306a36Sopenharmony_ci		host->dma_dir = DMA_FROM_DEVICE;
30762306a36Sopenharmony_ci		slave_dirn = DMA_DEV_TO_MEM;
30862306a36Sopenharmony_ci	} else {
30962306a36Sopenharmony_ci		host->dma_dir = DMA_TO_DEVICE;
31062306a36Sopenharmony_ci		slave_dirn = DMA_MEM_TO_DEV;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci		mxcmci_swap_buffers(data);
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	nents = dma_map_sg(host->dma->device->dev, data->sg,
31662306a36Sopenharmony_ci				     data->sg_len,  host->dma_dir);
31762306a36Sopenharmony_ci	if (nents != data->sg_len)
31862306a36Sopenharmony_ci		return -EINVAL;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	host->desc = dmaengine_prep_slave_sg(host->dma,
32162306a36Sopenharmony_ci		data->sg, data->sg_len, slave_dirn,
32262306a36Sopenharmony_ci		DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	if (!host->desc) {
32562306a36Sopenharmony_ci		dma_unmap_sg(host->dma->device->dev, data->sg, data->sg_len,
32662306a36Sopenharmony_ci				host->dma_dir);
32762306a36Sopenharmony_ci		host->do_dma = 0;
32862306a36Sopenharmony_ci		return 0; /* Fall back to PIO */
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci	wmb();
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	dmaengine_submit(host->desc);
33362306a36Sopenharmony_ci	dma_async_issue_pending(host->dma);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	mod_timer(&host->watchdog, jiffies + msecs_to_jiffies(MXCMCI_TIMEOUT_MS));
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	return 0;
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_cistatic void mxcmci_cmd_done(struct mxcmci_host *host, unsigned int stat);
34162306a36Sopenharmony_cistatic void mxcmci_data_done(struct mxcmci_host *host, unsigned int stat);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic void mxcmci_dma_callback(void *data)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	struct mxcmci_host *host = data;
34662306a36Sopenharmony_ci	u32 stat;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	del_timer(&host->watchdog);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	stat = mxcmci_readl(host, MMC_REG_STATUS);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	dev_dbg(mmc_dev(host->mmc), "%s: 0x%08x\n", __func__, stat);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	mxcmci_data_done(host, stat);
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic int mxcmci_start_cmd(struct mxcmci_host *host, struct mmc_command *cmd,
35862306a36Sopenharmony_ci		unsigned int cmdat)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	u32 int_cntr = host->default_irq_mask;
36162306a36Sopenharmony_ci	unsigned long flags;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	WARN_ON(host->cmd != NULL);
36462306a36Sopenharmony_ci	host->cmd = cmd;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	switch (mmc_resp_type(cmd)) {
36762306a36Sopenharmony_ci	case MMC_RSP_R1: /* short CRC, OPCODE */
36862306a36Sopenharmony_ci	case MMC_RSP_R1B:/* short CRC, OPCODE, BUSY */
36962306a36Sopenharmony_ci		cmdat |= CMD_DAT_CONT_RESPONSE_48BIT_CRC;
37062306a36Sopenharmony_ci		break;
37162306a36Sopenharmony_ci	case MMC_RSP_R2: /* long 136 bit + CRC */
37262306a36Sopenharmony_ci		cmdat |= CMD_DAT_CONT_RESPONSE_136BIT;
37362306a36Sopenharmony_ci		break;
37462306a36Sopenharmony_ci	case MMC_RSP_R3: /* short */
37562306a36Sopenharmony_ci		cmdat |= CMD_DAT_CONT_RESPONSE_48BIT;
37662306a36Sopenharmony_ci		break;
37762306a36Sopenharmony_ci	case MMC_RSP_NONE:
37862306a36Sopenharmony_ci		break;
37962306a36Sopenharmony_ci	default:
38062306a36Sopenharmony_ci		dev_err(mmc_dev(host->mmc), "unhandled response type 0x%x\n",
38162306a36Sopenharmony_ci				mmc_resp_type(cmd));
38262306a36Sopenharmony_ci		cmd->error = -EINVAL;
38362306a36Sopenharmony_ci		return -EINVAL;
38462306a36Sopenharmony_ci	}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	int_cntr = INT_END_CMD_RES_EN;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	if (mxcmci_use_dma(host)) {
38962306a36Sopenharmony_ci		if (host->dma_dir == DMA_FROM_DEVICE) {
39062306a36Sopenharmony_ci			host->desc->callback = mxcmci_dma_callback;
39162306a36Sopenharmony_ci			host->desc->callback_param = host;
39262306a36Sopenharmony_ci		} else {
39362306a36Sopenharmony_ci			int_cntr |= INT_WRITE_OP_DONE_EN;
39462306a36Sopenharmony_ci		}
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	spin_lock_irqsave(&host->lock, flags);
39862306a36Sopenharmony_ci	if (host->use_sdio)
39962306a36Sopenharmony_ci		int_cntr |= INT_SDIO_IRQ_EN;
40062306a36Sopenharmony_ci	mxcmci_writel(host, int_cntr, MMC_REG_INT_CNTR);
40162306a36Sopenharmony_ci	spin_unlock_irqrestore(&host->lock, flags);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	mxcmci_writew(host, cmd->opcode, MMC_REG_CMD);
40462306a36Sopenharmony_ci	mxcmci_writel(host, cmd->arg, MMC_REG_ARG);
40562306a36Sopenharmony_ci	mxcmci_writew(host, cmdat, MMC_REG_CMD_DAT_CONT);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	return 0;
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_cistatic void mxcmci_finish_request(struct mxcmci_host *host,
41162306a36Sopenharmony_ci		struct mmc_request *req)
41262306a36Sopenharmony_ci{
41362306a36Sopenharmony_ci	u32 int_cntr = host->default_irq_mask;
41462306a36Sopenharmony_ci	unsigned long flags;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	spin_lock_irqsave(&host->lock, flags);
41762306a36Sopenharmony_ci	if (host->use_sdio)
41862306a36Sopenharmony_ci		int_cntr |= INT_SDIO_IRQ_EN;
41962306a36Sopenharmony_ci	mxcmci_writel(host, int_cntr, MMC_REG_INT_CNTR);
42062306a36Sopenharmony_ci	spin_unlock_irqrestore(&host->lock, flags);
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	host->req = NULL;
42362306a36Sopenharmony_ci	host->cmd = NULL;
42462306a36Sopenharmony_ci	host->data = NULL;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	mmc_request_done(host->mmc, req);
42762306a36Sopenharmony_ci}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_cistatic int mxcmci_finish_data(struct mxcmci_host *host, unsigned int stat)
43062306a36Sopenharmony_ci{
43162306a36Sopenharmony_ci	struct mmc_data *data = host->data;
43262306a36Sopenharmony_ci	int data_error;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	if (mxcmci_use_dma(host)) {
43562306a36Sopenharmony_ci		dma_unmap_sg(host->dma->device->dev, data->sg, data->sg_len,
43662306a36Sopenharmony_ci				host->dma_dir);
43762306a36Sopenharmony_ci		mxcmci_swap_buffers(data);
43862306a36Sopenharmony_ci	}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	if (stat & STATUS_ERR_MASK) {
44162306a36Sopenharmony_ci		dev_dbg(mmc_dev(host->mmc), "request failed. status: 0x%08x\n",
44262306a36Sopenharmony_ci				stat);
44362306a36Sopenharmony_ci		if (stat & STATUS_CRC_READ_ERR) {
44462306a36Sopenharmony_ci			dev_err(mmc_dev(host->mmc), "%s: -EILSEQ\n", __func__);
44562306a36Sopenharmony_ci			data->error = -EILSEQ;
44662306a36Sopenharmony_ci		} else if (stat & STATUS_CRC_WRITE_ERR) {
44762306a36Sopenharmony_ci			u32 err_code = (stat >> 9) & 0x3;
44862306a36Sopenharmony_ci			if (err_code == 2) { /* No CRC response */
44962306a36Sopenharmony_ci				dev_err(mmc_dev(host->mmc),
45062306a36Sopenharmony_ci					"%s: No CRC -ETIMEDOUT\n", __func__);
45162306a36Sopenharmony_ci				data->error = -ETIMEDOUT;
45262306a36Sopenharmony_ci			} else {
45362306a36Sopenharmony_ci				dev_err(mmc_dev(host->mmc),
45462306a36Sopenharmony_ci					"%s: -EILSEQ\n", __func__);
45562306a36Sopenharmony_ci				data->error = -EILSEQ;
45662306a36Sopenharmony_ci			}
45762306a36Sopenharmony_ci		} else if (stat & STATUS_TIME_OUT_READ) {
45862306a36Sopenharmony_ci			dev_err(mmc_dev(host->mmc),
45962306a36Sopenharmony_ci				"%s: read -ETIMEDOUT\n", __func__);
46062306a36Sopenharmony_ci			data->error = -ETIMEDOUT;
46162306a36Sopenharmony_ci		} else {
46262306a36Sopenharmony_ci			dev_err(mmc_dev(host->mmc), "%s: -EIO\n", __func__);
46362306a36Sopenharmony_ci			data->error = -EIO;
46462306a36Sopenharmony_ci		}
46562306a36Sopenharmony_ci	} else {
46662306a36Sopenharmony_ci		data->bytes_xfered = host->datasize;
46762306a36Sopenharmony_ci	}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	data_error = data->error;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	host->data = NULL;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	return data_error;
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_cistatic void mxcmci_read_response(struct mxcmci_host *host, unsigned int stat)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	struct mmc_command *cmd = host->cmd;
47962306a36Sopenharmony_ci	int i;
48062306a36Sopenharmony_ci	u32 a, b, c;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	if (!cmd)
48362306a36Sopenharmony_ci		return;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	if (stat & STATUS_TIME_OUT_RESP) {
48662306a36Sopenharmony_ci		dev_dbg(mmc_dev(host->mmc), "CMD TIMEOUT\n");
48762306a36Sopenharmony_ci		cmd->error = -ETIMEDOUT;
48862306a36Sopenharmony_ci	} else if (stat & STATUS_RESP_CRC_ERR && cmd->flags & MMC_RSP_CRC) {
48962306a36Sopenharmony_ci		dev_dbg(mmc_dev(host->mmc), "cmd crc error\n");
49062306a36Sopenharmony_ci		cmd->error = -EILSEQ;
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	if (cmd->flags & MMC_RSP_PRESENT) {
49462306a36Sopenharmony_ci		if (cmd->flags & MMC_RSP_136) {
49562306a36Sopenharmony_ci			for (i = 0; i < 4; i++) {
49662306a36Sopenharmony_ci				a = mxcmci_readw(host, MMC_REG_RES_FIFO);
49762306a36Sopenharmony_ci				b = mxcmci_readw(host, MMC_REG_RES_FIFO);
49862306a36Sopenharmony_ci				cmd->resp[i] = a << 16 | b;
49962306a36Sopenharmony_ci			}
50062306a36Sopenharmony_ci		} else {
50162306a36Sopenharmony_ci			a = mxcmci_readw(host, MMC_REG_RES_FIFO);
50262306a36Sopenharmony_ci			b = mxcmci_readw(host, MMC_REG_RES_FIFO);
50362306a36Sopenharmony_ci			c = mxcmci_readw(host, MMC_REG_RES_FIFO);
50462306a36Sopenharmony_ci			cmd->resp[0] = a << 24 | b << 8 | c >> 8;
50562306a36Sopenharmony_ci		}
50662306a36Sopenharmony_ci	}
50762306a36Sopenharmony_ci}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_cistatic int mxcmci_poll_status(struct mxcmci_host *host, u32 mask)
51062306a36Sopenharmony_ci{
51162306a36Sopenharmony_ci	u32 stat;
51262306a36Sopenharmony_ci	unsigned long timeout = jiffies + HZ;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	do {
51562306a36Sopenharmony_ci		stat = mxcmci_readl(host, MMC_REG_STATUS);
51662306a36Sopenharmony_ci		if (stat & STATUS_ERR_MASK)
51762306a36Sopenharmony_ci			return stat;
51862306a36Sopenharmony_ci		if (time_after(jiffies, timeout)) {
51962306a36Sopenharmony_ci			mxcmci_softreset(host);
52062306a36Sopenharmony_ci			mxcmci_set_clk_rate(host, host->clock);
52162306a36Sopenharmony_ci			return STATUS_TIME_OUT_READ;
52262306a36Sopenharmony_ci		}
52362306a36Sopenharmony_ci		if (stat & mask)
52462306a36Sopenharmony_ci			return 0;
52562306a36Sopenharmony_ci		cpu_relax();
52662306a36Sopenharmony_ci	} while (1);
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_cistatic int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes)
53062306a36Sopenharmony_ci{
53162306a36Sopenharmony_ci	unsigned int stat;
53262306a36Sopenharmony_ci	u32 *buf = _buf;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	while (bytes > 3) {
53562306a36Sopenharmony_ci		stat = mxcmci_poll_status(host,
53662306a36Sopenharmony_ci				STATUS_BUF_READ_RDY | STATUS_READ_OP_DONE);
53762306a36Sopenharmony_ci		if (stat)
53862306a36Sopenharmony_ci			return stat;
53962306a36Sopenharmony_ci		*buf++ = cpu_to_le32(mxcmci_readl(host, MMC_REG_BUFFER_ACCESS));
54062306a36Sopenharmony_ci		bytes -= 4;
54162306a36Sopenharmony_ci	}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	if (bytes) {
54462306a36Sopenharmony_ci		u8 *b = (u8 *)buf;
54562306a36Sopenharmony_ci		u32 tmp;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci		stat = mxcmci_poll_status(host,
54862306a36Sopenharmony_ci				STATUS_BUF_READ_RDY | STATUS_READ_OP_DONE);
54962306a36Sopenharmony_ci		if (stat)
55062306a36Sopenharmony_ci			return stat;
55162306a36Sopenharmony_ci		tmp = cpu_to_le32(mxcmci_readl(host, MMC_REG_BUFFER_ACCESS));
55262306a36Sopenharmony_ci		memcpy(b, &tmp, bytes);
55362306a36Sopenharmony_ci	}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	return 0;
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_cistatic int mxcmci_push(struct mxcmci_host *host, void *_buf, int bytes)
55962306a36Sopenharmony_ci{
56062306a36Sopenharmony_ci	unsigned int stat;
56162306a36Sopenharmony_ci	u32 *buf = _buf;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	while (bytes > 3) {
56462306a36Sopenharmony_ci		stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
56562306a36Sopenharmony_ci		if (stat)
56662306a36Sopenharmony_ci			return stat;
56762306a36Sopenharmony_ci		mxcmci_writel(host, cpu_to_le32(*buf++), MMC_REG_BUFFER_ACCESS);
56862306a36Sopenharmony_ci		bytes -= 4;
56962306a36Sopenharmony_ci	}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	if (bytes) {
57262306a36Sopenharmony_ci		u8 *b = (u8 *)buf;
57362306a36Sopenharmony_ci		u32 tmp;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci		stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
57662306a36Sopenharmony_ci		if (stat)
57762306a36Sopenharmony_ci			return stat;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci		memcpy(&tmp, b, bytes);
58062306a36Sopenharmony_ci		mxcmci_writel(host, cpu_to_le32(tmp), MMC_REG_BUFFER_ACCESS);
58162306a36Sopenharmony_ci	}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	return mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
58462306a36Sopenharmony_ci}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_cistatic int mxcmci_transfer_data(struct mxcmci_host *host)
58762306a36Sopenharmony_ci{
58862306a36Sopenharmony_ci	struct mmc_data *data = host->req->data;
58962306a36Sopenharmony_ci	struct scatterlist *sg;
59062306a36Sopenharmony_ci	int stat, i;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	host->data = data;
59362306a36Sopenharmony_ci	host->datasize = 0;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	if (data->flags & MMC_DATA_READ) {
59662306a36Sopenharmony_ci		for_each_sg(data->sg, sg, data->sg_len, i) {
59762306a36Sopenharmony_ci			stat = mxcmci_pull(host, sg_virt(sg), sg->length);
59862306a36Sopenharmony_ci			if (stat)
59962306a36Sopenharmony_ci				return stat;
60062306a36Sopenharmony_ci			host->datasize += sg->length;
60162306a36Sopenharmony_ci		}
60262306a36Sopenharmony_ci	} else {
60362306a36Sopenharmony_ci		for_each_sg(data->sg, sg, data->sg_len, i) {
60462306a36Sopenharmony_ci			stat = mxcmci_push(host, sg_virt(sg), sg->length);
60562306a36Sopenharmony_ci			if (stat)
60662306a36Sopenharmony_ci				return stat;
60762306a36Sopenharmony_ci			host->datasize += sg->length;
60862306a36Sopenharmony_ci		}
60962306a36Sopenharmony_ci		stat = mxcmci_poll_status(host, STATUS_WRITE_OP_DONE);
61062306a36Sopenharmony_ci		if (stat)
61162306a36Sopenharmony_ci			return stat;
61262306a36Sopenharmony_ci	}
61362306a36Sopenharmony_ci	return 0;
61462306a36Sopenharmony_ci}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_cistatic void mxcmci_datawork(struct work_struct *work)
61762306a36Sopenharmony_ci{
61862306a36Sopenharmony_ci	struct mxcmci_host *host = container_of(work, struct mxcmci_host,
61962306a36Sopenharmony_ci						  datawork);
62062306a36Sopenharmony_ci	int datastat = mxcmci_transfer_data(host);
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	mxcmci_writel(host, STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE,
62362306a36Sopenharmony_ci		MMC_REG_STATUS);
62462306a36Sopenharmony_ci	mxcmci_finish_data(host, datastat);
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	if (host->req->stop) {
62762306a36Sopenharmony_ci		if (mxcmci_start_cmd(host, host->req->stop, 0)) {
62862306a36Sopenharmony_ci			mxcmci_finish_request(host, host->req);
62962306a36Sopenharmony_ci			return;
63062306a36Sopenharmony_ci		}
63162306a36Sopenharmony_ci	} else {
63262306a36Sopenharmony_ci		mxcmci_finish_request(host, host->req);
63362306a36Sopenharmony_ci	}
63462306a36Sopenharmony_ci}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_cistatic void mxcmci_data_done(struct mxcmci_host *host, unsigned int stat)
63762306a36Sopenharmony_ci{
63862306a36Sopenharmony_ci	struct mmc_request *req;
63962306a36Sopenharmony_ci	int data_error;
64062306a36Sopenharmony_ci	unsigned long flags;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	spin_lock_irqsave(&host->lock, flags);
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	if (!host->data) {
64562306a36Sopenharmony_ci		spin_unlock_irqrestore(&host->lock, flags);
64662306a36Sopenharmony_ci		return;
64762306a36Sopenharmony_ci	}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	if (!host->req) {
65062306a36Sopenharmony_ci		spin_unlock_irqrestore(&host->lock, flags);
65162306a36Sopenharmony_ci		return;
65262306a36Sopenharmony_ci	}
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	req = host->req;
65562306a36Sopenharmony_ci	if (!req->stop)
65662306a36Sopenharmony_ci		host->req = NULL; /* we will handle finish req below */
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	data_error = mxcmci_finish_data(host, stat);
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	spin_unlock_irqrestore(&host->lock, flags);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	if (data_error)
66362306a36Sopenharmony_ci		return;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	mxcmci_read_response(host, stat);
66662306a36Sopenharmony_ci	host->cmd = NULL;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	if (req->stop) {
66962306a36Sopenharmony_ci		if (mxcmci_start_cmd(host, req->stop, 0)) {
67062306a36Sopenharmony_ci			mxcmci_finish_request(host, req);
67162306a36Sopenharmony_ci			return;
67262306a36Sopenharmony_ci		}
67362306a36Sopenharmony_ci	} else {
67462306a36Sopenharmony_ci		mxcmci_finish_request(host, req);
67562306a36Sopenharmony_ci	}
67662306a36Sopenharmony_ci}
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_cistatic void mxcmci_cmd_done(struct mxcmci_host *host, unsigned int stat)
67962306a36Sopenharmony_ci{
68062306a36Sopenharmony_ci	mxcmci_read_response(host, stat);
68162306a36Sopenharmony_ci	host->cmd = NULL;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	if (!host->data && host->req) {
68462306a36Sopenharmony_ci		mxcmci_finish_request(host, host->req);
68562306a36Sopenharmony_ci		return;
68662306a36Sopenharmony_ci	}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	/* For the DMA case the DMA engine handles the data transfer
68962306a36Sopenharmony_ci	 * automatically. For non DMA we have to do it ourselves.
69062306a36Sopenharmony_ci	 * Don't do it in interrupt context though.
69162306a36Sopenharmony_ci	 */
69262306a36Sopenharmony_ci	if (!mxcmci_use_dma(host) && host->data)
69362306a36Sopenharmony_ci		schedule_work(&host->datawork);
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_cistatic irqreturn_t mxcmci_irq(int irq, void *devid)
69862306a36Sopenharmony_ci{
69962306a36Sopenharmony_ci	struct mxcmci_host *host = devid;
70062306a36Sopenharmony_ci	bool sdio_irq;
70162306a36Sopenharmony_ci	u32 stat;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	stat = mxcmci_readl(host, MMC_REG_STATUS);
70462306a36Sopenharmony_ci	mxcmci_writel(host,
70562306a36Sopenharmony_ci		stat & ~(STATUS_SDIO_INT_ACTIVE | STATUS_DATA_TRANS_DONE |
70662306a36Sopenharmony_ci			 STATUS_WRITE_OP_DONE),
70762306a36Sopenharmony_ci		MMC_REG_STATUS);
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	dev_dbg(mmc_dev(host->mmc), "%s: 0x%08x\n", __func__, stat);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	spin_lock(&host->lock);
71262306a36Sopenharmony_ci	sdio_irq = (stat & STATUS_SDIO_INT_ACTIVE) && host->use_sdio;
71362306a36Sopenharmony_ci	spin_unlock(&host->lock);
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	if (mxcmci_use_dma(host) && (stat & (STATUS_WRITE_OP_DONE)))
71662306a36Sopenharmony_ci		mxcmci_writel(host, STATUS_WRITE_OP_DONE, MMC_REG_STATUS);
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	if (sdio_irq) {
71962306a36Sopenharmony_ci		mxcmci_writel(host, STATUS_SDIO_INT_ACTIVE, MMC_REG_STATUS);
72062306a36Sopenharmony_ci		mmc_signal_sdio_irq(host->mmc);
72162306a36Sopenharmony_ci	}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	if (stat & STATUS_END_CMD_RESP)
72462306a36Sopenharmony_ci		mxcmci_cmd_done(host, stat);
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	if (mxcmci_use_dma(host) && (stat & STATUS_WRITE_OP_DONE)) {
72762306a36Sopenharmony_ci		del_timer(&host->watchdog);
72862306a36Sopenharmony_ci		mxcmci_data_done(host, stat);
72962306a36Sopenharmony_ci	}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	if (host->default_irq_mask &&
73262306a36Sopenharmony_ci		  (stat & (STATUS_CARD_INSERTION | STATUS_CARD_REMOVAL)))
73362306a36Sopenharmony_ci		mmc_detect_change(host->mmc, msecs_to_jiffies(200));
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	return IRQ_HANDLED;
73662306a36Sopenharmony_ci}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_cistatic void mxcmci_request(struct mmc_host *mmc, struct mmc_request *req)
73962306a36Sopenharmony_ci{
74062306a36Sopenharmony_ci	struct mxcmci_host *host = mmc_priv(mmc);
74162306a36Sopenharmony_ci	unsigned int cmdat = host->cmdat;
74262306a36Sopenharmony_ci	int error;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	WARN_ON(host->req != NULL);
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	host->req = req;
74762306a36Sopenharmony_ci	host->cmdat &= ~CMD_DAT_CONT_INIT;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	if (host->dma)
75062306a36Sopenharmony_ci		host->do_dma = 1;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	if (req->data) {
75362306a36Sopenharmony_ci		error = mxcmci_setup_data(host, req->data);
75462306a36Sopenharmony_ci		if (error) {
75562306a36Sopenharmony_ci			req->cmd->error = error;
75662306a36Sopenharmony_ci			goto out;
75762306a36Sopenharmony_ci		}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci		cmdat |= CMD_DAT_CONT_DATA_ENABLE;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci		if (req->data->flags & MMC_DATA_WRITE)
76362306a36Sopenharmony_ci			cmdat |= CMD_DAT_CONT_WRITE;
76462306a36Sopenharmony_ci	}
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	error = mxcmci_start_cmd(host, req->cmd, cmdat);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ciout:
76962306a36Sopenharmony_ci	if (error)
77062306a36Sopenharmony_ci		mxcmci_finish_request(host, req);
77162306a36Sopenharmony_ci}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_cistatic void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios)
77462306a36Sopenharmony_ci{
77562306a36Sopenharmony_ci	unsigned int divider;
77662306a36Sopenharmony_ci	int prescaler = 0;
77762306a36Sopenharmony_ci	unsigned int clk_in = clk_get_rate(host->clk_per);
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	while (prescaler <= 0x800) {
78062306a36Sopenharmony_ci		for (divider = 1; divider <= 0xF; divider++) {
78162306a36Sopenharmony_ci			int x;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci			x = (clk_in / (divider + 1));
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci			if (prescaler)
78662306a36Sopenharmony_ci				x /= (prescaler * 2);
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci			if (x <= clk_ios)
78962306a36Sopenharmony_ci				break;
79062306a36Sopenharmony_ci		}
79162306a36Sopenharmony_ci		if (divider < 0x10)
79262306a36Sopenharmony_ci			break;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci		if (prescaler == 0)
79562306a36Sopenharmony_ci			prescaler = 1;
79662306a36Sopenharmony_ci		else
79762306a36Sopenharmony_ci			prescaler <<= 1;
79862306a36Sopenharmony_ci	}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	mxcmci_writew(host, (prescaler << 4) | divider, MMC_REG_CLK_RATE);
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	dev_dbg(mmc_dev(host->mmc), "scaler: %d divider: %d in: %d out: %d\n",
80362306a36Sopenharmony_ci			prescaler, divider, clk_in, clk_ios);
80462306a36Sopenharmony_ci}
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_cistatic int mxcmci_setup_dma(struct mmc_host *mmc)
80762306a36Sopenharmony_ci{
80862306a36Sopenharmony_ci	struct mxcmci_host *host = mmc_priv(mmc);
80962306a36Sopenharmony_ci	struct dma_slave_config *config = &host->dma_slave_config;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	config->dst_addr = host->phys_base + MMC_REG_BUFFER_ACCESS;
81262306a36Sopenharmony_ci	config->src_addr = host->phys_base + MMC_REG_BUFFER_ACCESS;
81362306a36Sopenharmony_ci	config->dst_addr_width = 4;
81462306a36Sopenharmony_ci	config->src_addr_width = 4;
81562306a36Sopenharmony_ci	config->dst_maxburst = host->burstlen;
81662306a36Sopenharmony_ci	config->src_maxburst = host->burstlen;
81762306a36Sopenharmony_ci	config->device_fc = false;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	return dmaengine_slave_config(host->dma, config);
82062306a36Sopenharmony_ci}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_cistatic void mxcmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
82362306a36Sopenharmony_ci{
82462306a36Sopenharmony_ci	struct mxcmci_host *host = mmc_priv(mmc);
82562306a36Sopenharmony_ci	int burstlen, ret;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	/*
82862306a36Sopenharmony_ci	 * use burstlen of 64 (16 words) in 4 bit mode (--> reg value  0)
82962306a36Sopenharmony_ci	 * use burstlen of 16 (4 words) in 1 bit mode (--> reg value 16)
83062306a36Sopenharmony_ci	 */
83162306a36Sopenharmony_ci	if (ios->bus_width == MMC_BUS_WIDTH_4)
83262306a36Sopenharmony_ci		burstlen = 16;
83362306a36Sopenharmony_ci	else
83462306a36Sopenharmony_ci		burstlen = 4;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	if (mxcmci_use_dma(host) && burstlen != host->burstlen) {
83762306a36Sopenharmony_ci		host->burstlen = burstlen;
83862306a36Sopenharmony_ci		ret = mxcmci_setup_dma(mmc);
83962306a36Sopenharmony_ci		if (ret) {
84062306a36Sopenharmony_ci			dev_err(mmc_dev(host->mmc),
84162306a36Sopenharmony_ci				"failed to config DMA channel. Falling back to PIO\n");
84262306a36Sopenharmony_ci			dma_release_channel(host->dma);
84362306a36Sopenharmony_ci			host->do_dma = 0;
84462306a36Sopenharmony_ci			host->dma = NULL;
84562306a36Sopenharmony_ci		}
84662306a36Sopenharmony_ci	}
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	if (ios->bus_width == MMC_BUS_WIDTH_4)
84962306a36Sopenharmony_ci		host->cmdat |= CMD_DAT_CONT_BUS_WIDTH_4;
85062306a36Sopenharmony_ci	else
85162306a36Sopenharmony_ci		host->cmdat &= ~CMD_DAT_CONT_BUS_WIDTH_4;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	if (host->power_mode != ios->power_mode) {
85462306a36Sopenharmony_ci		host->power_mode = ios->power_mode;
85562306a36Sopenharmony_ci		mxcmci_set_power(host, ios->vdd);
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci		if (ios->power_mode == MMC_POWER_ON)
85862306a36Sopenharmony_ci			host->cmdat |= CMD_DAT_CONT_INIT;
85962306a36Sopenharmony_ci	}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	if (ios->clock) {
86262306a36Sopenharmony_ci		mxcmci_set_clk_rate(host, ios->clock);
86362306a36Sopenharmony_ci		mxcmci_writew(host, STR_STP_CLK_START_CLK, MMC_REG_STR_STP_CLK);
86462306a36Sopenharmony_ci	} else {
86562306a36Sopenharmony_ci		mxcmci_writew(host, STR_STP_CLK_STOP_CLK, MMC_REG_STR_STP_CLK);
86662306a36Sopenharmony_ci	}
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	host->clock = ios->clock;
86962306a36Sopenharmony_ci}
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_cistatic irqreturn_t mxcmci_detect_irq(int irq, void *data)
87262306a36Sopenharmony_ci{
87362306a36Sopenharmony_ci	struct mmc_host *mmc = data;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	dev_dbg(mmc_dev(mmc), "%s\n", __func__);
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	mmc_detect_change(mmc, msecs_to_jiffies(250));
87862306a36Sopenharmony_ci	return IRQ_HANDLED;
87962306a36Sopenharmony_ci}
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_cistatic int mxcmci_get_ro(struct mmc_host *mmc)
88262306a36Sopenharmony_ci{
88362306a36Sopenharmony_ci	struct mxcmci_host *host = mmc_priv(mmc);
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	if (host->pdata && host->pdata->get_ro)
88662306a36Sopenharmony_ci		return !!host->pdata->get_ro(mmc_dev(mmc));
88762306a36Sopenharmony_ci	/*
88862306a36Sopenharmony_ci	 * If board doesn't support read only detection (no mmc_gpio
88962306a36Sopenharmony_ci	 * context or gpio is invalid), then let the mmc core decide
89062306a36Sopenharmony_ci	 * what to do.
89162306a36Sopenharmony_ci	 */
89262306a36Sopenharmony_ci	return mmc_gpio_get_ro(mmc);
89362306a36Sopenharmony_ci}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_cistatic void mxcmci_enable_sdio_irq(struct mmc_host *mmc, int enable)
89662306a36Sopenharmony_ci{
89762306a36Sopenharmony_ci	struct mxcmci_host *host = mmc_priv(mmc);
89862306a36Sopenharmony_ci	unsigned long flags;
89962306a36Sopenharmony_ci	u32 int_cntr;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	spin_lock_irqsave(&host->lock, flags);
90262306a36Sopenharmony_ci	host->use_sdio = enable;
90362306a36Sopenharmony_ci	int_cntr = mxcmci_readl(host, MMC_REG_INT_CNTR);
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	if (enable)
90662306a36Sopenharmony_ci		int_cntr |= INT_SDIO_IRQ_EN;
90762306a36Sopenharmony_ci	else
90862306a36Sopenharmony_ci		int_cntr &= ~INT_SDIO_IRQ_EN;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	mxcmci_writel(host, int_cntr, MMC_REG_INT_CNTR);
91162306a36Sopenharmony_ci	spin_unlock_irqrestore(&host->lock, flags);
91262306a36Sopenharmony_ci}
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_cistatic void mxcmci_init_card(struct mmc_host *host, struct mmc_card *card)
91562306a36Sopenharmony_ci{
91662306a36Sopenharmony_ci	struct mxcmci_host *mxcmci = mmc_priv(host);
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	/*
91962306a36Sopenharmony_ci	 * MX3 SoCs have a silicon bug which corrupts CRC calculation of
92062306a36Sopenharmony_ci	 * multi-block transfers when connected SDIO peripheral doesn't
92162306a36Sopenharmony_ci	 * drive the BUSY line as required by the specs.
92262306a36Sopenharmony_ci	 * One way to prevent this is to only allow 1-bit transfers.
92362306a36Sopenharmony_ci	 */
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	if (is_imx31_mmc(mxcmci) && mmc_card_sdio(card))
92662306a36Sopenharmony_ci		host->caps &= ~MMC_CAP_4_BIT_DATA;
92762306a36Sopenharmony_ci	else
92862306a36Sopenharmony_ci		host->caps |= MMC_CAP_4_BIT_DATA;
92962306a36Sopenharmony_ci}
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_cistatic bool filter(struct dma_chan *chan, void *param)
93262306a36Sopenharmony_ci{
93362306a36Sopenharmony_ci	struct mxcmci_host *host = param;
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	if (!imx_dma_is_general_purpose(chan))
93662306a36Sopenharmony_ci		return false;
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	chan->private = &host->dma_data;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	return true;
94162306a36Sopenharmony_ci}
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_cistatic void mxcmci_watchdog(struct timer_list *t)
94462306a36Sopenharmony_ci{
94562306a36Sopenharmony_ci	struct mxcmci_host *host = from_timer(host, t, watchdog);
94662306a36Sopenharmony_ci	struct mmc_request *req = host->req;
94762306a36Sopenharmony_ci	unsigned int stat = mxcmci_readl(host, MMC_REG_STATUS);
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	if (host->dma_dir == DMA_FROM_DEVICE) {
95062306a36Sopenharmony_ci		dmaengine_terminate_all(host->dma);
95162306a36Sopenharmony_ci		dev_err(mmc_dev(host->mmc),
95262306a36Sopenharmony_ci			"%s: read time out (status = 0x%08x)\n",
95362306a36Sopenharmony_ci			__func__, stat);
95462306a36Sopenharmony_ci	} else {
95562306a36Sopenharmony_ci		dev_err(mmc_dev(host->mmc),
95662306a36Sopenharmony_ci			"%s: write time out (status = 0x%08x)\n",
95762306a36Sopenharmony_ci			__func__, stat);
95862306a36Sopenharmony_ci		mxcmci_softreset(host);
95962306a36Sopenharmony_ci	}
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	/* Mark transfer as erroneus and inform the upper layers */
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	if (host->data)
96462306a36Sopenharmony_ci		host->data->error = -ETIMEDOUT;
96562306a36Sopenharmony_ci	host->req = NULL;
96662306a36Sopenharmony_ci	host->cmd = NULL;
96762306a36Sopenharmony_ci	host->data = NULL;
96862306a36Sopenharmony_ci	mmc_request_done(host->mmc, req);
96962306a36Sopenharmony_ci}
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_cistatic const struct mmc_host_ops mxcmci_ops = {
97262306a36Sopenharmony_ci	.request		= mxcmci_request,
97362306a36Sopenharmony_ci	.set_ios		= mxcmci_set_ios,
97462306a36Sopenharmony_ci	.get_ro			= mxcmci_get_ro,
97562306a36Sopenharmony_ci	.enable_sdio_irq	= mxcmci_enable_sdio_irq,
97662306a36Sopenharmony_ci	.init_card		= mxcmci_init_card,
97762306a36Sopenharmony_ci};
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_cistatic int mxcmci_probe(struct platform_device *pdev)
98062306a36Sopenharmony_ci{
98162306a36Sopenharmony_ci	struct mmc_host *mmc;
98262306a36Sopenharmony_ci	struct mxcmci_host *host;
98362306a36Sopenharmony_ci	struct resource *res;
98462306a36Sopenharmony_ci	int ret = 0, irq;
98562306a36Sopenharmony_ci	bool dat3_card_detect = false;
98662306a36Sopenharmony_ci	dma_cap_mask_t mask;
98762306a36Sopenharmony_ci	struct imxmmc_platform_data *pdata = pdev->dev.platform_data;
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	pr_info("i.MX/MPC512x SDHC driver\n");
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
99262306a36Sopenharmony_ci	if (irq < 0)
99362306a36Sopenharmony_ci		return irq;
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	mmc = mmc_alloc_host(sizeof(*host), &pdev->dev);
99662306a36Sopenharmony_ci	if (!mmc)
99762306a36Sopenharmony_ci		return -ENOMEM;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	host = mmc_priv(mmc);
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	host->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
100262306a36Sopenharmony_ci	if (IS_ERR(host->base)) {
100362306a36Sopenharmony_ci		ret = PTR_ERR(host->base);
100462306a36Sopenharmony_ci		goto out_free;
100562306a36Sopenharmony_ci	}
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	host->phys_base = res->start;
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	ret = mmc_of_parse(mmc);
101062306a36Sopenharmony_ci	if (ret)
101162306a36Sopenharmony_ci		goto out_free;
101262306a36Sopenharmony_ci	mmc->ops = &mxcmci_ops;
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	/* For devicetree parsing, the bus width is read from devicetree */
101562306a36Sopenharmony_ci	if (pdata)
101662306a36Sopenharmony_ci		mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
101762306a36Sopenharmony_ci	else
101862306a36Sopenharmony_ci		mmc->caps |= MMC_CAP_SDIO_IRQ;
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	/* MMC core transfer sizes tunable parameters */
102162306a36Sopenharmony_ci	mmc->max_blk_size = 2048;
102262306a36Sopenharmony_ci	mmc->max_blk_count = 65535;
102362306a36Sopenharmony_ci	mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
102462306a36Sopenharmony_ci	mmc->max_seg_size = mmc->max_req_size;
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	host->devtype = (uintptr_t)of_device_get_match_data(&pdev->dev);
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	/* adjust max_segs after devtype detection */
102962306a36Sopenharmony_ci	if (!is_mpc512x_mmc(host))
103062306a36Sopenharmony_ci		mmc->max_segs = 64;
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	host->mmc = mmc;
103362306a36Sopenharmony_ci	host->pdata = pdata;
103462306a36Sopenharmony_ci	spin_lock_init(&host->lock);
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	if (pdata)
103762306a36Sopenharmony_ci		dat3_card_detect = pdata->dat3_card_detect;
103862306a36Sopenharmony_ci	else if (mmc_card_is_removable(mmc)
103962306a36Sopenharmony_ci			&& !of_property_read_bool(pdev->dev.of_node, "cd-gpios"))
104062306a36Sopenharmony_ci		dat3_card_detect = true;
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	ret = mmc_regulator_get_supply(mmc);
104362306a36Sopenharmony_ci	if (ret)
104462306a36Sopenharmony_ci		goto out_free;
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	if (!mmc->ocr_avail) {
104762306a36Sopenharmony_ci		if (pdata && pdata->ocr_avail)
104862306a36Sopenharmony_ci			mmc->ocr_avail = pdata->ocr_avail;
104962306a36Sopenharmony_ci		else
105062306a36Sopenharmony_ci			mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
105162306a36Sopenharmony_ci	}
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	if (dat3_card_detect)
105462306a36Sopenharmony_ci		host->default_irq_mask =
105562306a36Sopenharmony_ci			INT_CARD_INSERTION_EN | INT_CARD_REMOVAL_EN;
105662306a36Sopenharmony_ci	else
105762306a36Sopenharmony_ci		host->default_irq_mask = 0;
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	host->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
106062306a36Sopenharmony_ci	if (IS_ERR(host->clk_ipg)) {
106162306a36Sopenharmony_ci		ret = PTR_ERR(host->clk_ipg);
106262306a36Sopenharmony_ci		goto out_free;
106362306a36Sopenharmony_ci	}
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	host->clk_per = devm_clk_get(&pdev->dev, "per");
106662306a36Sopenharmony_ci	if (IS_ERR(host->clk_per)) {
106762306a36Sopenharmony_ci		ret = PTR_ERR(host->clk_per);
106862306a36Sopenharmony_ci		goto out_free;
106962306a36Sopenharmony_ci	}
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	ret = clk_prepare_enable(host->clk_per);
107262306a36Sopenharmony_ci	if (ret)
107362306a36Sopenharmony_ci		goto out_free;
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	ret = clk_prepare_enable(host->clk_ipg);
107662306a36Sopenharmony_ci	if (ret)
107762306a36Sopenharmony_ci		goto out_clk_per_put;
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	mxcmci_softreset(host);
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	host->rev_no = mxcmci_readw(host, MMC_REG_REV_NO);
108262306a36Sopenharmony_ci	if (host->rev_no != 0x400) {
108362306a36Sopenharmony_ci		ret = -ENODEV;
108462306a36Sopenharmony_ci		dev_err(mmc_dev(host->mmc), "wrong rev.no. 0x%08x. aborting.\n",
108562306a36Sopenharmony_ci			host->rev_no);
108662306a36Sopenharmony_ci		goto out_clk_put;
108762306a36Sopenharmony_ci	}
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	mmc->f_min = clk_get_rate(host->clk_per) >> 16;
109062306a36Sopenharmony_ci	mmc->f_max = clk_get_rate(host->clk_per) >> 1;
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	/* recommended in data sheet */
109362306a36Sopenharmony_ci	mxcmci_writew(host, 0x2db4, MMC_REG_READ_TO);
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	mxcmci_writel(host, host->default_irq_mask, MMC_REG_INT_CNTR);
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	if (!host->pdata) {
109862306a36Sopenharmony_ci		host->dma = dma_request_chan(&pdev->dev, "rx-tx");
109962306a36Sopenharmony_ci		if (IS_ERR(host->dma)) {
110062306a36Sopenharmony_ci			if (PTR_ERR(host->dma) == -EPROBE_DEFER) {
110162306a36Sopenharmony_ci				ret = -EPROBE_DEFER;
110262306a36Sopenharmony_ci				goto out_clk_put;
110362306a36Sopenharmony_ci			}
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci			/* Ignore errors to fall back to PIO mode */
110662306a36Sopenharmony_ci			host->dma = NULL;
110762306a36Sopenharmony_ci		}
110862306a36Sopenharmony_ci	} else {
110962306a36Sopenharmony_ci		res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
111062306a36Sopenharmony_ci		if (res) {
111162306a36Sopenharmony_ci			host->dmareq = res->start;
111262306a36Sopenharmony_ci			host->dma_data.peripheral_type = IMX_DMATYPE_SDHC;
111362306a36Sopenharmony_ci			host->dma_data.priority = DMA_PRIO_LOW;
111462306a36Sopenharmony_ci			host->dma_data.dma_request = host->dmareq;
111562306a36Sopenharmony_ci			dma_cap_zero(mask);
111662306a36Sopenharmony_ci			dma_cap_set(DMA_SLAVE, mask);
111762306a36Sopenharmony_ci			host->dma = dma_request_channel(mask, filter, host);
111862306a36Sopenharmony_ci		}
111962306a36Sopenharmony_ci	}
112062306a36Sopenharmony_ci	if (host->dma)
112162306a36Sopenharmony_ci		mmc->max_seg_size = dma_get_max_seg_size(
112262306a36Sopenharmony_ci				host->dma->device->dev);
112362306a36Sopenharmony_ci	else
112462306a36Sopenharmony_ci		dev_info(mmc_dev(host->mmc), "dma not available. Using PIO\n");
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	INIT_WORK(&host->datawork, mxcmci_datawork);
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	ret = devm_request_irq(&pdev->dev, irq, mxcmci_irq, 0,
112962306a36Sopenharmony_ci			       dev_name(&pdev->dev), host);
113062306a36Sopenharmony_ci	if (ret)
113162306a36Sopenharmony_ci		goto out_free_dma;
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	platform_set_drvdata(pdev, mmc);
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	if (host->pdata && host->pdata->init) {
113662306a36Sopenharmony_ci		ret = host->pdata->init(&pdev->dev, mxcmci_detect_irq,
113762306a36Sopenharmony_ci				host->mmc);
113862306a36Sopenharmony_ci		if (ret)
113962306a36Sopenharmony_ci			goto out_free_dma;
114062306a36Sopenharmony_ci	}
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	timer_setup(&host->watchdog, mxcmci_watchdog, 0);
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	ret = mmc_add_host(mmc);
114562306a36Sopenharmony_ci	if (ret)
114662306a36Sopenharmony_ci		goto out_free_dma;
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	return 0;
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ciout_free_dma:
115162306a36Sopenharmony_ci	if (host->dma)
115262306a36Sopenharmony_ci		dma_release_channel(host->dma);
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ciout_clk_put:
115562306a36Sopenharmony_ci	clk_disable_unprepare(host->clk_ipg);
115662306a36Sopenharmony_ciout_clk_per_put:
115762306a36Sopenharmony_ci	clk_disable_unprepare(host->clk_per);
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ciout_free:
116062306a36Sopenharmony_ci	mmc_free_host(mmc);
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	return ret;
116362306a36Sopenharmony_ci}
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_cistatic void mxcmci_remove(struct platform_device *pdev)
116662306a36Sopenharmony_ci{
116762306a36Sopenharmony_ci	struct mmc_host *mmc = platform_get_drvdata(pdev);
116862306a36Sopenharmony_ci	struct mxcmci_host *host = mmc_priv(mmc);
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	mmc_remove_host(mmc);
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	if (host->pdata && host->pdata->exit)
117362306a36Sopenharmony_ci		host->pdata->exit(&pdev->dev, mmc);
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	if (host->dma)
117662306a36Sopenharmony_ci		dma_release_channel(host->dma);
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	clk_disable_unprepare(host->clk_per);
117962306a36Sopenharmony_ci	clk_disable_unprepare(host->clk_ipg);
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	mmc_free_host(mmc);
118262306a36Sopenharmony_ci}
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_cistatic int mxcmci_suspend(struct device *dev)
118562306a36Sopenharmony_ci{
118662306a36Sopenharmony_ci	struct mmc_host *mmc = dev_get_drvdata(dev);
118762306a36Sopenharmony_ci	struct mxcmci_host *host = mmc_priv(mmc);
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	clk_disable_unprepare(host->clk_per);
119062306a36Sopenharmony_ci	clk_disable_unprepare(host->clk_ipg);
119162306a36Sopenharmony_ci	return 0;
119262306a36Sopenharmony_ci}
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_cistatic int mxcmci_resume(struct device *dev)
119562306a36Sopenharmony_ci{
119662306a36Sopenharmony_ci	struct mmc_host *mmc = dev_get_drvdata(dev);
119762306a36Sopenharmony_ci	struct mxcmci_host *host = mmc_priv(mmc);
119862306a36Sopenharmony_ci	int ret;
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	ret = clk_prepare_enable(host->clk_per);
120162306a36Sopenharmony_ci	if (ret)
120262306a36Sopenharmony_ci		return ret;
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	ret = clk_prepare_enable(host->clk_ipg);
120562306a36Sopenharmony_ci	if (ret)
120662306a36Sopenharmony_ci		clk_disable_unprepare(host->clk_per);
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	return ret;
120962306a36Sopenharmony_ci}
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(mxcmci_pm_ops, mxcmci_suspend, mxcmci_resume);
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_cistatic struct platform_driver mxcmci_driver = {
121462306a36Sopenharmony_ci	.probe		= mxcmci_probe,
121562306a36Sopenharmony_ci	.remove_new	= mxcmci_remove,
121662306a36Sopenharmony_ci	.driver		= {
121762306a36Sopenharmony_ci		.name		= DRIVER_NAME,
121862306a36Sopenharmony_ci		.probe_type	= PROBE_PREFER_ASYNCHRONOUS,
121962306a36Sopenharmony_ci		.pm	= pm_sleep_ptr(&mxcmci_pm_ops),
122062306a36Sopenharmony_ci		.of_match_table	= mxcmci_of_match,
122162306a36Sopenharmony_ci	}
122262306a36Sopenharmony_ci};
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_cimodule_platform_driver(mxcmci_driver);
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ciMODULE_DESCRIPTION("i.MX Multimedia Card Interface Driver");
122762306a36Sopenharmony_ciMODULE_AUTHOR("Sascha Hauer, Pengutronix");
122862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
122962306a36Sopenharmony_ciMODULE_ALIAS("platform:mxc-mmc");
1230