1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Portions copyright (C) 2003 Russell King, PXA MMCI Driver
4*4882a593Smuzhiyun * Portions copyright (C) 2004-2005 Pierre Ossman, W83L51xD SD/MMC driver
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * Copyright 2008 Embedded Alley Solutions, Inc.
7*4882a593Smuzhiyun * Copyright 2009-2011 Freescale Semiconductor, Inc.
8*4882a593Smuzhiyun */
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun #include <linux/kernel.h>
11*4882a593Smuzhiyun #include <linux/init.h>
12*4882a593Smuzhiyun #include <linux/ioport.h>
13*4882a593Smuzhiyun #include <linux/of.h>
14*4882a593Smuzhiyun #include <linux/of_device.h>
15*4882a593Smuzhiyun #include <linux/platform_device.h>
16*4882a593Smuzhiyun #include <linux/delay.h>
17*4882a593Smuzhiyun #include <linux/interrupt.h>
18*4882a593Smuzhiyun #include <linux/dma-mapping.h>
19*4882a593Smuzhiyun #include <linux/dmaengine.h>
20*4882a593Smuzhiyun #include <linux/dma/mxs-dma.h>
21*4882a593Smuzhiyun #include <linux/highmem.h>
22*4882a593Smuzhiyun #include <linux/clk.h>
23*4882a593Smuzhiyun #include <linux/err.h>
24*4882a593Smuzhiyun #include <linux/completion.h>
25*4882a593Smuzhiyun #include <linux/mmc/host.h>
26*4882a593Smuzhiyun #include <linux/mmc/mmc.h>
27*4882a593Smuzhiyun #include <linux/mmc/sdio.h>
28*4882a593Smuzhiyun #include <linux/mmc/slot-gpio.h>
29*4882a593Smuzhiyun #include <linux/regulator/consumer.h>
30*4882a593Smuzhiyun #include <linux/module.h>
31*4882a593Smuzhiyun #include <linux/stmp_device.h>
32*4882a593Smuzhiyun #include <linux/spi/mxs-spi.h>
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun #define DRIVER_NAME "mxs-mmc"
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun #define MXS_MMC_IRQ_BITS (BM_SSP_CTRL1_SDIO_IRQ | \
37*4882a593Smuzhiyun BM_SSP_CTRL1_RESP_ERR_IRQ | \
38*4882a593Smuzhiyun BM_SSP_CTRL1_RESP_TIMEOUT_IRQ | \
39*4882a593Smuzhiyun BM_SSP_CTRL1_DATA_TIMEOUT_IRQ | \
40*4882a593Smuzhiyun BM_SSP_CTRL1_DATA_CRC_IRQ | \
41*4882a593Smuzhiyun BM_SSP_CTRL1_FIFO_UNDERRUN_IRQ | \
42*4882a593Smuzhiyun BM_SSP_CTRL1_RECV_TIMEOUT_IRQ | \
43*4882a593Smuzhiyun BM_SSP_CTRL1_FIFO_OVERRUN_IRQ)
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun /* card detect polling timeout */
46*4882a593Smuzhiyun #define MXS_MMC_DETECT_TIMEOUT (HZ/2)
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun struct mxs_mmc_host {
49*4882a593Smuzhiyun struct mxs_ssp ssp;
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun struct mmc_host *mmc;
52*4882a593Smuzhiyun struct mmc_request *mrq;
53*4882a593Smuzhiyun struct mmc_command *cmd;
54*4882a593Smuzhiyun struct mmc_data *data;
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun unsigned char bus_width;
57*4882a593Smuzhiyun spinlock_t lock;
58*4882a593Smuzhiyun int sdio_irq_en;
59*4882a593Smuzhiyun bool broken_cd;
60*4882a593Smuzhiyun };
61*4882a593Smuzhiyun
mxs_mmc_get_cd(struct mmc_host * mmc)62*4882a593Smuzhiyun static int mxs_mmc_get_cd(struct mmc_host *mmc)
63*4882a593Smuzhiyun {
64*4882a593Smuzhiyun struct mxs_mmc_host *host = mmc_priv(mmc);
65*4882a593Smuzhiyun struct mxs_ssp *ssp = &host->ssp;
66*4882a593Smuzhiyun int present, ret;
67*4882a593Smuzhiyun
68*4882a593Smuzhiyun if (host->broken_cd)
69*4882a593Smuzhiyun return -ENOSYS;
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun ret = mmc_gpio_get_cd(mmc);
72*4882a593Smuzhiyun if (ret >= 0)
73*4882a593Smuzhiyun return ret;
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun present = mmc->caps & MMC_CAP_NEEDS_POLL ||
76*4882a593Smuzhiyun !(readl(ssp->base + HW_SSP_STATUS(ssp)) &
77*4882a593Smuzhiyun BM_SSP_STATUS_CARD_DETECT);
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun if (mmc->caps2 & MMC_CAP2_CD_ACTIVE_HIGH)
80*4882a593Smuzhiyun present = !present;
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun return present;
83*4882a593Smuzhiyun }
84*4882a593Smuzhiyun
mxs_mmc_reset(struct mxs_mmc_host * host)85*4882a593Smuzhiyun static int mxs_mmc_reset(struct mxs_mmc_host *host)
86*4882a593Smuzhiyun {
87*4882a593Smuzhiyun struct mxs_ssp *ssp = &host->ssp;
88*4882a593Smuzhiyun u32 ctrl0, ctrl1;
89*4882a593Smuzhiyun int ret;
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun ret = stmp_reset_block(ssp->base);
92*4882a593Smuzhiyun if (ret)
93*4882a593Smuzhiyun return ret;
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun ctrl0 = BM_SSP_CTRL0_IGNORE_CRC;
96*4882a593Smuzhiyun ctrl1 = BF_SSP(0x3, CTRL1_SSP_MODE) |
97*4882a593Smuzhiyun BF_SSP(0x7, CTRL1_WORD_LENGTH) |
98*4882a593Smuzhiyun BM_SSP_CTRL1_DMA_ENABLE |
99*4882a593Smuzhiyun BM_SSP_CTRL1_POLARITY |
100*4882a593Smuzhiyun BM_SSP_CTRL1_RECV_TIMEOUT_IRQ_EN |
101*4882a593Smuzhiyun BM_SSP_CTRL1_DATA_CRC_IRQ_EN |
102*4882a593Smuzhiyun BM_SSP_CTRL1_DATA_TIMEOUT_IRQ_EN |
103*4882a593Smuzhiyun BM_SSP_CTRL1_RESP_TIMEOUT_IRQ_EN |
104*4882a593Smuzhiyun BM_SSP_CTRL1_RESP_ERR_IRQ_EN;
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun writel(BF_SSP(0xffff, TIMING_TIMEOUT) |
107*4882a593Smuzhiyun BF_SSP(2, TIMING_CLOCK_DIVIDE) |
108*4882a593Smuzhiyun BF_SSP(0, TIMING_CLOCK_RATE),
109*4882a593Smuzhiyun ssp->base + HW_SSP_TIMING(ssp));
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun if (host->sdio_irq_en) {
112*4882a593Smuzhiyun ctrl0 |= BM_SSP_CTRL0_SDIO_IRQ_CHECK;
113*4882a593Smuzhiyun ctrl1 |= BM_SSP_CTRL1_SDIO_IRQ_EN;
114*4882a593Smuzhiyun }
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun writel(ctrl0, ssp->base + HW_SSP_CTRL0);
117*4882a593Smuzhiyun writel(ctrl1, ssp->base + HW_SSP_CTRL1(ssp));
118*4882a593Smuzhiyun return 0;
119*4882a593Smuzhiyun }
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun static void mxs_mmc_start_cmd(struct mxs_mmc_host *host,
122*4882a593Smuzhiyun struct mmc_command *cmd);
123*4882a593Smuzhiyun
mxs_mmc_request_done(struct mxs_mmc_host * host)124*4882a593Smuzhiyun static void mxs_mmc_request_done(struct mxs_mmc_host *host)
125*4882a593Smuzhiyun {
126*4882a593Smuzhiyun struct mmc_command *cmd = host->cmd;
127*4882a593Smuzhiyun struct mmc_data *data = host->data;
128*4882a593Smuzhiyun struct mmc_request *mrq = host->mrq;
129*4882a593Smuzhiyun struct mxs_ssp *ssp = &host->ssp;
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun if (mmc_resp_type(cmd) & MMC_RSP_PRESENT) {
132*4882a593Smuzhiyun if (mmc_resp_type(cmd) & MMC_RSP_136) {
133*4882a593Smuzhiyun cmd->resp[3] = readl(ssp->base + HW_SSP_SDRESP0(ssp));
134*4882a593Smuzhiyun cmd->resp[2] = readl(ssp->base + HW_SSP_SDRESP1(ssp));
135*4882a593Smuzhiyun cmd->resp[1] = readl(ssp->base + HW_SSP_SDRESP2(ssp));
136*4882a593Smuzhiyun cmd->resp[0] = readl(ssp->base + HW_SSP_SDRESP3(ssp));
137*4882a593Smuzhiyun } else {
138*4882a593Smuzhiyun cmd->resp[0] = readl(ssp->base + HW_SSP_SDRESP0(ssp));
139*4882a593Smuzhiyun }
140*4882a593Smuzhiyun }
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun if (cmd == mrq->sbc) {
143*4882a593Smuzhiyun /* Finished CMD23, now send actual command. */
144*4882a593Smuzhiyun mxs_mmc_start_cmd(host, mrq->cmd);
145*4882a593Smuzhiyun return;
146*4882a593Smuzhiyun } else if (data) {
147*4882a593Smuzhiyun dma_unmap_sg(mmc_dev(host->mmc), data->sg,
148*4882a593Smuzhiyun data->sg_len, ssp->dma_dir);
149*4882a593Smuzhiyun /*
150*4882a593Smuzhiyun * If there was an error on any block, we mark all
151*4882a593Smuzhiyun * data blocks as being in error.
152*4882a593Smuzhiyun */
153*4882a593Smuzhiyun if (!data->error)
154*4882a593Smuzhiyun data->bytes_xfered = data->blocks * data->blksz;
155*4882a593Smuzhiyun else
156*4882a593Smuzhiyun data->bytes_xfered = 0;
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun host->data = NULL;
159*4882a593Smuzhiyun if (data->stop && (data->error || !mrq->sbc)) {
160*4882a593Smuzhiyun mxs_mmc_start_cmd(host, mrq->stop);
161*4882a593Smuzhiyun return;
162*4882a593Smuzhiyun }
163*4882a593Smuzhiyun }
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun host->mrq = NULL;
166*4882a593Smuzhiyun mmc_request_done(host->mmc, mrq);
167*4882a593Smuzhiyun }
168*4882a593Smuzhiyun
mxs_mmc_dma_irq_callback(void * param)169*4882a593Smuzhiyun static void mxs_mmc_dma_irq_callback(void *param)
170*4882a593Smuzhiyun {
171*4882a593Smuzhiyun struct mxs_mmc_host *host = param;
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun mxs_mmc_request_done(host);
174*4882a593Smuzhiyun }
175*4882a593Smuzhiyun
mxs_mmc_irq_handler(int irq,void * dev_id)176*4882a593Smuzhiyun static irqreturn_t mxs_mmc_irq_handler(int irq, void *dev_id)
177*4882a593Smuzhiyun {
178*4882a593Smuzhiyun struct mxs_mmc_host *host = dev_id;
179*4882a593Smuzhiyun struct mmc_command *cmd = host->cmd;
180*4882a593Smuzhiyun struct mmc_data *data = host->data;
181*4882a593Smuzhiyun struct mxs_ssp *ssp = &host->ssp;
182*4882a593Smuzhiyun u32 stat;
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun spin_lock(&host->lock);
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun stat = readl(ssp->base + HW_SSP_CTRL1(ssp));
187*4882a593Smuzhiyun writel(stat & MXS_MMC_IRQ_BITS,
188*4882a593Smuzhiyun ssp->base + HW_SSP_CTRL1(ssp) + STMP_OFFSET_REG_CLR);
189*4882a593Smuzhiyun
190*4882a593Smuzhiyun spin_unlock(&host->lock);
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun if ((stat & BM_SSP_CTRL1_SDIO_IRQ) && (stat & BM_SSP_CTRL1_SDIO_IRQ_EN))
193*4882a593Smuzhiyun mmc_signal_sdio_irq(host->mmc);
194*4882a593Smuzhiyun
195*4882a593Smuzhiyun if (stat & BM_SSP_CTRL1_RESP_TIMEOUT_IRQ)
196*4882a593Smuzhiyun cmd->error = -ETIMEDOUT;
197*4882a593Smuzhiyun else if (stat & BM_SSP_CTRL1_RESP_ERR_IRQ)
198*4882a593Smuzhiyun cmd->error = -EIO;
199*4882a593Smuzhiyun
200*4882a593Smuzhiyun if (data) {
201*4882a593Smuzhiyun if (stat & (BM_SSP_CTRL1_DATA_TIMEOUT_IRQ |
202*4882a593Smuzhiyun BM_SSP_CTRL1_RECV_TIMEOUT_IRQ))
203*4882a593Smuzhiyun data->error = -ETIMEDOUT;
204*4882a593Smuzhiyun else if (stat & BM_SSP_CTRL1_DATA_CRC_IRQ)
205*4882a593Smuzhiyun data->error = -EILSEQ;
206*4882a593Smuzhiyun else if (stat & (BM_SSP_CTRL1_FIFO_UNDERRUN_IRQ |
207*4882a593Smuzhiyun BM_SSP_CTRL1_FIFO_OVERRUN_IRQ))
208*4882a593Smuzhiyun data->error = -EIO;
209*4882a593Smuzhiyun }
210*4882a593Smuzhiyun
211*4882a593Smuzhiyun return IRQ_HANDLED;
212*4882a593Smuzhiyun }
213*4882a593Smuzhiyun
mxs_mmc_prep_dma(struct mxs_mmc_host * host,unsigned long flags)214*4882a593Smuzhiyun static struct dma_async_tx_descriptor *mxs_mmc_prep_dma(
215*4882a593Smuzhiyun struct mxs_mmc_host *host, unsigned long flags)
216*4882a593Smuzhiyun {
217*4882a593Smuzhiyun struct mxs_ssp *ssp = &host->ssp;
218*4882a593Smuzhiyun struct dma_async_tx_descriptor *desc;
219*4882a593Smuzhiyun struct mmc_data *data = host->data;
220*4882a593Smuzhiyun struct scatterlist * sgl;
221*4882a593Smuzhiyun unsigned int sg_len;
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun if (data) {
224*4882a593Smuzhiyun /* data */
225*4882a593Smuzhiyun dma_map_sg(mmc_dev(host->mmc), data->sg,
226*4882a593Smuzhiyun data->sg_len, ssp->dma_dir);
227*4882a593Smuzhiyun sgl = data->sg;
228*4882a593Smuzhiyun sg_len = data->sg_len;
229*4882a593Smuzhiyun } else {
230*4882a593Smuzhiyun /* pio */
231*4882a593Smuzhiyun sgl = (struct scatterlist *) ssp->ssp_pio_words;
232*4882a593Smuzhiyun sg_len = SSP_PIO_NUM;
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun desc = dmaengine_prep_slave_sg(ssp->dmach,
236*4882a593Smuzhiyun sgl, sg_len, ssp->slave_dirn, flags);
237*4882a593Smuzhiyun if (desc) {
238*4882a593Smuzhiyun desc->callback = mxs_mmc_dma_irq_callback;
239*4882a593Smuzhiyun desc->callback_param = host;
240*4882a593Smuzhiyun } else {
241*4882a593Smuzhiyun if (data)
242*4882a593Smuzhiyun dma_unmap_sg(mmc_dev(host->mmc), data->sg,
243*4882a593Smuzhiyun data->sg_len, ssp->dma_dir);
244*4882a593Smuzhiyun }
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun return desc;
247*4882a593Smuzhiyun }
248*4882a593Smuzhiyun
mxs_mmc_bc(struct mxs_mmc_host * host)249*4882a593Smuzhiyun static void mxs_mmc_bc(struct mxs_mmc_host *host)
250*4882a593Smuzhiyun {
251*4882a593Smuzhiyun struct mxs_ssp *ssp = &host->ssp;
252*4882a593Smuzhiyun struct mmc_command *cmd = host->cmd;
253*4882a593Smuzhiyun struct dma_async_tx_descriptor *desc;
254*4882a593Smuzhiyun u32 ctrl0, cmd0, cmd1;
255*4882a593Smuzhiyun
256*4882a593Smuzhiyun ctrl0 = BM_SSP_CTRL0_ENABLE | BM_SSP_CTRL0_IGNORE_CRC;
257*4882a593Smuzhiyun cmd0 = BF_SSP(cmd->opcode, CMD0_CMD) | BM_SSP_CMD0_APPEND_8CYC;
258*4882a593Smuzhiyun cmd1 = cmd->arg;
259*4882a593Smuzhiyun
260*4882a593Smuzhiyun if (host->sdio_irq_en) {
261*4882a593Smuzhiyun ctrl0 |= BM_SSP_CTRL0_SDIO_IRQ_CHECK;
262*4882a593Smuzhiyun cmd0 |= BM_SSP_CMD0_CONT_CLKING_EN | BM_SSP_CMD0_SLOW_CLKING_EN;
263*4882a593Smuzhiyun }
264*4882a593Smuzhiyun
265*4882a593Smuzhiyun ssp->ssp_pio_words[0] = ctrl0;
266*4882a593Smuzhiyun ssp->ssp_pio_words[1] = cmd0;
267*4882a593Smuzhiyun ssp->ssp_pio_words[2] = cmd1;
268*4882a593Smuzhiyun ssp->dma_dir = DMA_NONE;
269*4882a593Smuzhiyun ssp->slave_dirn = DMA_TRANS_NONE;
270*4882a593Smuzhiyun desc = mxs_mmc_prep_dma(host, MXS_DMA_CTRL_WAIT4END);
271*4882a593Smuzhiyun if (!desc)
272*4882a593Smuzhiyun goto out;
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun dmaengine_submit(desc);
275*4882a593Smuzhiyun dma_async_issue_pending(ssp->dmach);
276*4882a593Smuzhiyun return;
277*4882a593Smuzhiyun
278*4882a593Smuzhiyun out:
279*4882a593Smuzhiyun dev_warn(mmc_dev(host->mmc),
280*4882a593Smuzhiyun "%s: failed to prep dma\n", __func__);
281*4882a593Smuzhiyun }
282*4882a593Smuzhiyun
mxs_mmc_ac(struct mxs_mmc_host * host)283*4882a593Smuzhiyun static void mxs_mmc_ac(struct mxs_mmc_host *host)
284*4882a593Smuzhiyun {
285*4882a593Smuzhiyun struct mxs_ssp *ssp = &host->ssp;
286*4882a593Smuzhiyun struct mmc_command *cmd = host->cmd;
287*4882a593Smuzhiyun struct dma_async_tx_descriptor *desc;
288*4882a593Smuzhiyun u32 ignore_crc, get_resp, long_resp;
289*4882a593Smuzhiyun u32 ctrl0, cmd0, cmd1;
290*4882a593Smuzhiyun
291*4882a593Smuzhiyun ignore_crc = (mmc_resp_type(cmd) & MMC_RSP_CRC) ?
292*4882a593Smuzhiyun 0 : BM_SSP_CTRL0_IGNORE_CRC;
293*4882a593Smuzhiyun get_resp = (mmc_resp_type(cmd) & MMC_RSP_PRESENT) ?
294*4882a593Smuzhiyun BM_SSP_CTRL0_GET_RESP : 0;
295*4882a593Smuzhiyun long_resp = (mmc_resp_type(cmd) & MMC_RSP_136) ?
296*4882a593Smuzhiyun BM_SSP_CTRL0_LONG_RESP : 0;
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun ctrl0 = BM_SSP_CTRL0_ENABLE | ignore_crc | get_resp | long_resp;
299*4882a593Smuzhiyun cmd0 = BF_SSP(cmd->opcode, CMD0_CMD);
300*4882a593Smuzhiyun cmd1 = cmd->arg;
301*4882a593Smuzhiyun
302*4882a593Smuzhiyun if (cmd->opcode == MMC_STOP_TRANSMISSION)
303*4882a593Smuzhiyun cmd0 |= BM_SSP_CMD0_APPEND_8CYC;
304*4882a593Smuzhiyun
305*4882a593Smuzhiyun if (host->sdio_irq_en) {
306*4882a593Smuzhiyun ctrl0 |= BM_SSP_CTRL0_SDIO_IRQ_CHECK;
307*4882a593Smuzhiyun cmd0 |= BM_SSP_CMD0_CONT_CLKING_EN | BM_SSP_CMD0_SLOW_CLKING_EN;
308*4882a593Smuzhiyun }
309*4882a593Smuzhiyun
310*4882a593Smuzhiyun ssp->ssp_pio_words[0] = ctrl0;
311*4882a593Smuzhiyun ssp->ssp_pio_words[1] = cmd0;
312*4882a593Smuzhiyun ssp->ssp_pio_words[2] = cmd1;
313*4882a593Smuzhiyun ssp->dma_dir = DMA_NONE;
314*4882a593Smuzhiyun ssp->slave_dirn = DMA_TRANS_NONE;
315*4882a593Smuzhiyun desc = mxs_mmc_prep_dma(host, MXS_DMA_CTRL_WAIT4END);
316*4882a593Smuzhiyun if (!desc)
317*4882a593Smuzhiyun goto out;
318*4882a593Smuzhiyun
319*4882a593Smuzhiyun dmaengine_submit(desc);
320*4882a593Smuzhiyun dma_async_issue_pending(ssp->dmach);
321*4882a593Smuzhiyun return;
322*4882a593Smuzhiyun
323*4882a593Smuzhiyun out:
324*4882a593Smuzhiyun dev_warn(mmc_dev(host->mmc),
325*4882a593Smuzhiyun "%s: failed to prep dma\n", __func__);
326*4882a593Smuzhiyun }
327*4882a593Smuzhiyun
mxs_ns_to_ssp_ticks(unsigned clock_rate,unsigned ns)328*4882a593Smuzhiyun static unsigned short mxs_ns_to_ssp_ticks(unsigned clock_rate, unsigned ns)
329*4882a593Smuzhiyun {
330*4882a593Smuzhiyun const unsigned int ssp_timeout_mul = 4096;
331*4882a593Smuzhiyun /*
332*4882a593Smuzhiyun * Calculate ticks in ms since ns are large numbers
333*4882a593Smuzhiyun * and might overflow
334*4882a593Smuzhiyun */
335*4882a593Smuzhiyun const unsigned int clock_per_ms = clock_rate / 1000;
336*4882a593Smuzhiyun const unsigned int ms = ns / 1000;
337*4882a593Smuzhiyun const unsigned int ticks = ms * clock_per_ms;
338*4882a593Smuzhiyun const unsigned int ssp_ticks = ticks / ssp_timeout_mul;
339*4882a593Smuzhiyun
340*4882a593Smuzhiyun WARN_ON(ssp_ticks == 0);
341*4882a593Smuzhiyun return ssp_ticks;
342*4882a593Smuzhiyun }
343*4882a593Smuzhiyun
mxs_mmc_adtc(struct mxs_mmc_host * host)344*4882a593Smuzhiyun static void mxs_mmc_adtc(struct mxs_mmc_host *host)
345*4882a593Smuzhiyun {
346*4882a593Smuzhiyun struct mmc_command *cmd = host->cmd;
347*4882a593Smuzhiyun struct mmc_data *data = cmd->data;
348*4882a593Smuzhiyun struct dma_async_tx_descriptor *desc;
349*4882a593Smuzhiyun struct scatterlist *sgl = data->sg, *sg;
350*4882a593Smuzhiyun unsigned int sg_len = data->sg_len;
351*4882a593Smuzhiyun unsigned int i;
352*4882a593Smuzhiyun
353*4882a593Smuzhiyun unsigned short dma_data_dir, timeout;
354*4882a593Smuzhiyun enum dma_transfer_direction slave_dirn;
355*4882a593Smuzhiyun unsigned int data_size = 0, log2_blksz;
356*4882a593Smuzhiyun unsigned int blocks = data->blocks;
357*4882a593Smuzhiyun
358*4882a593Smuzhiyun struct mxs_ssp *ssp = &host->ssp;
359*4882a593Smuzhiyun
360*4882a593Smuzhiyun u32 ignore_crc, get_resp, long_resp, read;
361*4882a593Smuzhiyun u32 ctrl0, cmd0, cmd1, val;
362*4882a593Smuzhiyun
363*4882a593Smuzhiyun ignore_crc = (mmc_resp_type(cmd) & MMC_RSP_CRC) ?
364*4882a593Smuzhiyun 0 : BM_SSP_CTRL0_IGNORE_CRC;
365*4882a593Smuzhiyun get_resp = (mmc_resp_type(cmd) & MMC_RSP_PRESENT) ?
366*4882a593Smuzhiyun BM_SSP_CTRL0_GET_RESP : 0;
367*4882a593Smuzhiyun long_resp = (mmc_resp_type(cmd) & MMC_RSP_136) ?
368*4882a593Smuzhiyun BM_SSP_CTRL0_LONG_RESP : 0;
369*4882a593Smuzhiyun
370*4882a593Smuzhiyun if (data->flags & MMC_DATA_WRITE) {
371*4882a593Smuzhiyun dma_data_dir = DMA_TO_DEVICE;
372*4882a593Smuzhiyun slave_dirn = DMA_MEM_TO_DEV;
373*4882a593Smuzhiyun read = 0;
374*4882a593Smuzhiyun } else {
375*4882a593Smuzhiyun dma_data_dir = DMA_FROM_DEVICE;
376*4882a593Smuzhiyun slave_dirn = DMA_DEV_TO_MEM;
377*4882a593Smuzhiyun read = BM_SSP_CTRL0_READ;
378*4882a593Smuzhiyun }
379*4882a593Smuzhiyun
380*4882a593Smuzhiyun ctrl0 = BF_SSP(host->bus_width, CTRL0_BUS_WIDTH) |
381*4882a593Smuzhiyun ignore_crc | get_resp | long_resp |
382*4882a593Smuzhiyun BM_SSP_CTRL0_DATA_XFER | read |
383*4882a593Smuzhiyun BM_SSP_CTRL0_WAIT_FOR_IRQ |
384*4882a593Smuzhiyun BM_SSP_CTRL0_ENABLE;
385*4882a593Smuzhiyun
386*4882a593Smuzhiyun cmd0 = BF_SSP(cmd->opcode, CMD0_CMD);
387*4882a593Smuzhiyun
388*4882a593Smuzhiyun /* get logarithm to base 2 of block size for setting register */
389*4882a593Smuzhiyun log2_blksz = ilog2(data->blksz);
390*4882a593Smuzhiyun
391*4882a593Smuzhiyun /*
392*4882a593Smuzhiyun * take special care of the case that data size from data->sg
393*4882a593Smuzhiyun * is not equal to blocks x blksz
394*4882a593Smuzhiyun */
395*4882a593Smuzhiyun for_each_sg(sgl, sg, sg_len, i)
396*4882a593Smuzhiyun data_size += sg->length;
397*4882a593Smuzhiyun
398*4882a593Smuzhiyun if (data_size != data->blocks * data->blksz)
399*4882a593Smuzhiyun blocks = 1;
400*4882a593Smuzhiyun
401*4882a593Smuzhiyun /* xfer count, block size and count need to be set differently */
402*4882a593Smuzhiyun if (ssp_is_old(ssp)) {
403*4882a593Smuzhiyun ctrl0 |= BF_SSP(data_size, CTRL0_XFER_COUNT);
404*4882a593Smuzhiyun cmd0 |= BF_SSP(log2_blksz, CMD0_BLOCK_SIZE) |
405*4882a593Smuzhiyun BF_SSP(blocks - 1, CMD0_BLOCK_COUNT);
406*4882a593Smuzhiyun } else {
407*4882a593Smuzhiyun writel(data_size, ssp->base + HW_SSP_XFER_SIZE);
408*4882a593Smuzhiyun writel(BF_SSP(log2_blksz, BLOCK_SIZE_BLOCK_SIZE) |
409*4882a593Smuzhiyun BF_SSP(blocks - 1, BLOCK_SIZE_BLOCK_COUNT),
410*4882a593Smuzhiyun ssp->base + HW_SSP_BLOCK_SIZE);
411*4882a593Smuzhiyun }
412*4882a593Smuzhiyun
413*4882a593Smuzhiyun if (cmd->opcode == SD_IO_RW_EXTENDED)
414*4882a593Smuzhiyun cmd0 |= BM_SSP_CMD0_APPEND_8CYC;
415*4882a593Smuzhiyun
416*4882a593Smuzhiyun cmd1 = cmd->arg;
417*4882a593Smuzhiyun
418*4882a593Smuzhiyun if (host->sdio_irq_en) {
419*4882a593Smuzhiyun ctrl0 |= BM_SSP_CTRL0_SDIO_IRQ_CHECK;
420*4882a593Smuzhiyun cmd0 |= BM_SSP_CMD0_CONT_CLKING_EN | BM_SSP_CMD0_SLOW_CLKING_EN;
421*4882a593Smuzhiyun }
422*4882a593Smuzhiyun
423*4882a593Smuzhiyun /* set the timeout count */
424*4882a593Smuzhiyun timeout = mxs_ns_to_ssp_ticks(ssp->clk_rate, data->timeout_ns);
425*4882a593Smuzhiyun val = readl(ssp->base + HW_SSP_TIMING(ssp));
426*4882a593Smuzhiyun val &= ~(BM_SSP_TIMING_TIMEOUT);
427*4882a593Smuzhiyun val |= BF_SSP(timeout, TIMING_TIMEOUT);
428*4882a593Smuzhiyun writel(val, ssp->base + HW_SSP_TIMING(ssp));
429*4882a593Smuzhiyun
430*4882a593Smuzhiyun /* pio */
431*4882a593Smuzhiyun ssp->ssp_pio_words[0] = ctrl0;
432*4882a593Smuzhiyun ssp->ssp_pio_words[1] = cmd0;
433*4882a593Smuzhiyun ssp->ssp_pio_words[2] = cmd1;
434*4882a593Smuzhiyun ssp->dma_dir = DMA_NONE;
435*4882a593Smuzhiyun ssp->slave_dirn = DMA_TRANS_NONE;
436*4882a593Smuzhiyun desc = mxs_mmc_prep_dma(host, 0);
437*4882a593Smuzhiyun if (!desc)
438*4882a593Smuzhiyun goto out;
439*4882a593Smuzhiyun
440*4882a593Smuzhiyun /* append data sg */
441*4882a593Smuzhiyun WARN_ON(host->data != NULL);
442*4882a593Smuzhiyun host->data = data;
443*4882a593Smuzhiyun ssp->dma_dir = dma_data_dir;
444*4882a593Smuzhiyun ssp->slave_dirn = slave_dirn;
445*4882a593Smuzhiyun desc = mxs_mmc_prep_dma(host, DMA_PREP_INTERRUPT | MXS_DMA_CTRL_WAIT4END);
446*4882a593Smuzhiyun if (!desc)
447*4882a593Smuzhiyun goto out;
448*4882a593Smuzhiyun
449*4882a593Smuzhiyun dmaengine_submit(desc);
450*4882a593Smuzhiyun dma_async_issue_pending(ssp->dmach);
451*4882a593Smuzhiyun return;
452*4882a593Smuzhiyun out:
453*4882a593Smuzhiyun dev_warn(mmc_dev(host->mmc),
454*4882a593Smuzhiyun "%s: failed to prep dma\n", __func__);
455*4882a593Smuzhiyun }
456*4882a593Smuzhiyun
mxs_mmc_start_cmd(struct mxs_mmc_host * host,struct mmc_command * cmd)457*4882a593Smuzhiyun static void mxs_mmc_start_cmd(struct mxs_mmc_host *host,
458*4882a593Smuzhiyun struct mmc_command *cmd)
459*4882a593Smuzhiyun {
460*4882a593Smuzhiyun host->cmd = cmd;
461*4882a593Smuzhiyun
462*4882a593Smuzhiyun switch (mmc_cmd_type(cmd)) {
463*4882a593Smuzhiyun case MMC_CMD_BC:
464*4882a593Smuzhiyun mxs_mmc_bc(host);
465*4882a593Smuzhiyun break;
466*4882a593Smuzhiyun case MMC_CMD_BCR:
467*4882a593Smuzhiyun mxs_mmc_ac(host);
468*4882a593Smuzhiyun break;
469*4882a593Smuzhiyun case MMC_CMD_AC:
470*4882a593Smuzhiyun mxs_mmc_ac(host);
471*4882a593Smuzhiyun break;
472*4882a593Smuzhiyun case MMC_CMD_ADTC:
473*4882a593Smuzhiyun mxs_mmc_adtc(host);
474*4882a593Smuzhiyun break;
475*4882a593Smuzhiyun default:
476*4882a593Smuzhiyun dev_warn(mmc_dev(host->mmc),
477*4882a593Smuzhiyun "%s: unknown MMC command\n", __func__);
478*4882a593Smuzhiyun break;
479*4882a593Smuzhiyun }
480*4882a593Smuzhiyun }
481*4882a593Smuzhiyun
mxs_mmc_request(struct mmc_host * mmc,struct mmc_request * mrq)482*4882a593Smuzhiyun static void mxs_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
483*4882a593Smuzhiyun {
484*4882a593Smuzhiyun struct mxs_mmc_host *host = mmc_priv(mmc);
485*4882a593Smuzhiyun
486*4882a593Smuzhiyun WARN_ON(host->mrq != NULL);
487*4882a593Smuzhiyun host->mrq = mrq;
488*4882a593Smuzhiyun
489*4882a593Smuzhiyun if (mrq->sbc)
490*4882a593Smuzhiyun mxs_mmc_start_cmd(host, mrq->sbc);
491*4882a593Smuzhiyun else
492*4882a593Smuzhiyun mxs_mmc_start_cmd(host, mrq->cmd);
493*4882a593Smuzhiyun }
494*4882a593Smuzhiyun
mxs_mmc_set_ios(struct mmc_host * mmc,struct mmc_ios * ios)495*4882a593Smuzhiyun static void mxs_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
496*4882a593Smuzhiyun {
497*4882a593Smuzhiyun struct mxs_mmc_host *host = mmc_priv(mmc);
498*4882a593Smuzhiyun
499*4882a593Smuzhiyun if (ios->bus_width == MMC_BUS_WIDTH_8)
500*4882a593Smuzhiyun host->bus_width = 2;
501*4882a593Smuzhiyun else if (ios->bus_width == MMC_BUS_WIDTH_4)
502*4882a593Smuzhiyun host->bus_width = 1;
503*4882a593Smuzhiyun else
504*4882a593Smuzhiyun host->bus_width = 0;
505*4882a593Smuzhiyun
506*4882a593Smuzhiyun if (ios->clock)
507*4882a593Smuzhiyun mxs_ssp_set_clk_rate(&host->ssp, ios->clock);
508*4882a593Smuzhiyun }
509*4882a593Smuzhiyun
mxs_mmc_enable_sdio_irq(struct mmc_host * mmc,int enable)510*4882a593Smuzhiyun static void mxs_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
511*4882a593Smuzhiyun {
512*4882a593Smuzhiyun struct mxs_mmc_host *host = mmc_priv(mmc);
513*4882a593Smuzhiyun struct mxs_ssp *ssp = &host->ssp;
514*4882a593Smuzhiyun unsigned long flags;
515*4882a593Smuzhiyun
516*4882a593Smuzhiyun spin_lock_irqsave(&host->lock, flags);
517*4882a593Smuzhiyun
518*4882a593Smuzhiyun host->sdio_irq_en = enable;
519*4882a593Smuzhiyun
520*4882a593Smuzhiyun if (enable) {
521*4882a593Smuzhiyun writel(BM_SSP_CTRL0_SDIO_IRQ_CHECK,
522*4882a593Smuzhiyun ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
523*4882a593Smuzhiyun writel(BM_SSP_CTRL1_SDIO_IRQ_EN,
524*4882a593Smuzhiyun ssp->base + HW_SSP_CTRL1(ssp) + STMP_OFFSET_REG_SET);
525*4882a593Smuzhiyun } else {
526*4882a593Smuzhiyun writel(BM_SSP_CTRL0_SDIO_IRQ_CHECK,
527*4882a593Smuzhiyun ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR);
528*4882a593Smuzhiyun writel(BM_SSP_CTRL1_SDIO_IRQ_EN,
529*4882a593Smuzhiyun ssp->base + HW_SSP_CTRL1(ssp) + STMP_OFFSET_REG_CLR);
530*4882a593Smuzhiyun }
531*4882a593Smuzhiyun
532*4882a593Smuzhiyun spin_unlock_irqrestore(&host->lock, flags);
533*4882a593Smuzhiyun
534*4882a593Smuzhiyun if (enable && readl(ssp->base + HW_SSP_STATUS(ssp)) &
535*4882a593Smuzhiyun BM_SSP_STATUS_SDIO_IRQ)
536*4882a593Smuzhiyun mmc_signal_sdio_irq(host->mmc);
537*4882a593Smuzhiyun
538*4882a593Smuzhiyun }
539*4882a593Smuzhiyun
540*4882a593Smuzhiyun static const struct mmc_host_ops mxs_mmc_ops = {
541*4882a593Smuzhiyun .request = mxs_mmc_request,
542*4882a593Smuzhiyun .get_ro = mmc_gpio_get_ro,
543*4882a593Smuzhiyun .get_cd = mxs_mmc_get_cd,
544*4882a593Smuzhiyun .set_ios = mxs_mmc_set_ios,
545*4882a593Smuzhiyun .enable_sdio_irq = mxs_mmc_enable_sdio_irq,
546*4882a593Smuzhiyun };
547*4882a593Smuzhiyun
548*4882a593Smuzhiyun static const struct platform_device_id mxs_ssp_ids[] = {
549*4882a593Smuzhiyun {
550*4882a593Smuzhiyun .name = "imx23-mmc",
551*4882a593Smuzhiyun .driver_data = IMX23_SSP,
552*4882a593Smuzhiyun }, {
553*4882a593Smuzhiyun .name = "imx28-mmc",
554*4882a593Smuzhiyun .driver_data = IMX28_SSP,
555*4882a593Smuzhiyun }, {
556*4882a593Smuzhiyun /* sentinel */
557*4882a593Smuzhiyun }
558*4882a593Smuzhiyun };
559*4882a593Smuzhiyun MODULE_DEVICE_TABLE(platform, mxs_ssp_ids);
560*4882a593Smuzhiyun
561*4882a593Smuzhiyun static const struct of_device_id mxs_mmc_dt_ids[] = {
562*4882a593Smuzhiyun { .compatible = "fsl,imx23-mmc", .data = (void *) IMX23_SSP, },
563*4882a593Smuzhiyun { .compatible = "fsl,imx28-mmc", .data = (void *) IMX28_SSP, },
564*4882a593Smuzhiyun { /* sentinel */ }
565*4882a593Smuzhiyun };
566*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, mxs_mmc_dt_ids);
567*4882a593Smuzhiyun
mxs_mmc_regulator_disable(void * regulator)568*4882a593Smuzhiyun static void mxs_mmc_regulator_disable(void *regulator)
569*4882a593Smuzhiyun {
570*4882a593Smuzhiyun regulator_disable(regulator);
571*4882a593Smuzhiyun }
572*4882a593Smuzhiyun
mxs_mmc_probe(struct platform_device * pdev)573*4882a593Smuzhiyun static int mxs_mmc_probe(struct platform_device *pdev)
574*4882a593Smuzhiyun {
575*4882a593Smuzhiyun const struct of_device_id *of_id =
576*4882a593Smuzhiyun of_match_device(mxs_mmc_dt_ids, &pdev->dev);
577*4882a593Smuzhiyun struct device_node *np = pdev->dev.of_node;
578*4882a593Smuzhiyun struct mxs_mmc_host *host;
579*4882a593Smuzhiyun struct mmc_host *mmc;
580*4882a593Smuzhiyun int ret = 0, irq_err;
581*4882a593Smuzhiyun struct regulator *reg_vmmc;
582*4882a593Smuzhiyun struct mxs_ssp *ssp;
583*4882a593Smuzhiyun
584*4882a593Smuzhiyun irq_err = platform_get_irq(pdev, 0);
585*4882a593Smuzhiyun if (irq_err < 0)
586*4882a593Smuzhiyun return irq_err;
587*4882a593Smuzhiyun
588*4882a593Smuzhiyun mmc = mmc_alloc_host(sizeof(struct mxs_mmc_host), &pdev->dev);
589*4882a593Smuzhiyun if (!mmc)
590*4882a593Smuzhiyun return -ENOMEM;
591*4882a593Smuzhiyun
592*4882a593Smuzhiyun host = mmc_priv(mmc);
593*4882a593Smuzhiyun ssp = &host->ssp;
594*4882a593Smuzhiyun ssp->dev = &pdev->dev;
595*4882a593Smuzhiyun ssp->base = devm_platform_ioremap_resource(pdev, 0);
596*4882a593Smuzhiyun if (IS_ERR(ssp->base)) {
597*4882a593Smuzhiyun ret = PTR_ERR(ssp->base);
598*4882a593Smuzhiyun goto out_mmc_free;
599*4882a593Smuzhiyun }
600*4882a593Smuzhiyun
601*4882a593Smuzhiyun ssp->devid = (enum mxs_ssp_id) of_id->data;
602*4882a593Smuzhiyun
603*4882a593Smuzhiyun host->mmc = mmc;
604*4882a593Smuzhiyun host->sdio_irq_en = 0;
605*4882a593Smuzhiyun
606*4882a593Smuzhiyun reg_vmmc = devm_regulator_get(&pdev->dev, "vmmc");
607*4882a593Smuzhiyun if (!IS_ERR(reg_vmmc)) {
608*4882a593Smuzhiyun ret = regulator_enable(reg_vmmc);
609*4882a593Smuzhiyun if (ret) {
610*4882a593Smuzhiyun dev_err(&pdev->dev,
611*4882a593Smuzhiyun "Failed to enable vmmc regulator: %d\n", ret);
612*4882a593Smuzhiyun goto out_mmc_free;
613*4882a593Smuzhiyun }
614*4882a593Smuzhiyun
615*4882a593Smuzhiyun ret = devm_add_action_or_reset(&pdev->dev, mxs_mmc_regulator_disable,
616*4882a593Smuzhiyun reg_vmmc);
617*4882a593Smuzhiyun if (ret)
618*4882a593Smuzhiyun goto out_mmc_free;
619*4882a593Smuzhiyun }
620*4882a593Smuzhiyun
621*4882a593Smuzhiyun ssp->clk = devm_clk_get(&pdev->dev, NULL);
622*4882a593Smuzhiyun if (IS_ERR(ssp->clk)) {
623*4882a593Smuzhiyun ret = PTR_ERR(ssp->clk);
624*4882a593Smuzhiyun goto out_mmc_free;
625*4882a593Smuzhiyun }
626*4882a593Smuzhiyun ret = clk_prepare_enable(ssp->clk);
627*4882a593Smuzhiyun if (ret)
628*4882a593Smuzhiyun goto out_mmc_free;
629*4882a593Smuzhiyun
630*4882a593Smuzhiyun ret = mxs_mmc_reset(host);
631*4882a593Smuzhiyun if (ret) {
632*4882a593Smuzhiyun dev_err(&pdev->dev, "Failed to reset mmc: %d\n", ret);
633*4882a593Smuzhiyun goto out_clk_disable;
634*4882a593Smuzhiyun }
635*4882a593Smuzhiyun
636*4882a593Smuzhiyun ssp->dmach = dma_request_chan(&pdev->dev, "rx-tx");
637*4882a593Smuzhiyun if (IS_ERR(ssp->dmach)) {
638*4882a593Smuzhiyun dev_err(mmc_dev(host->mmc),
639*4882a593Smuzhiyun "%s: failed to request dma\n", __func__);
640*4882a593Smuzhiyun ret = PTR_ERR(ssp->dmach);
641*4882a593Smuzhiyun goto out_clk_disable;
642*4882a593Smuzhiyun }
643*4882a593Smuzhiyun
644*4882a593Smuzhiyun /* set mmc core parameters */
645*4882a593Smuzhiyun mmc->ops = &mxs_mmc_ops;
646*4882a593Smuzhiyun mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
647*4882a593Smuzhiyun MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL | MMC_CAP_CMD23;
648*4882a593Smuzhiyun
649*4882a593Smuzhiyun host->broken_cd = of_property_read_bool(np, "broken-cd");
650*4882a593Smuzhiyun
651*4882a593Smuzhiyun mmc->f_min = 400000;
652*4882a593Smuzhiyun mmc->f_max = 288000000;
653*4882a593Smuzhiyun
654*4882a593Smuzhiyun ret = mmc_of_parse(mmc);
655*4882a593Smuzhiyun if (ret)
656*4882a593Smuzhiyun goto out_free_dma;
657*4882a593Smuzhiyun
658*4882a593Smuzhiyun mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
659*4882a593Smuzhiyun
660*4882a593Smuzhiyun mmc->max_segs = 52;
661*4882a593Smuzhiyun mmc->max_blk_size = 1 << 0xf;
662*4882a593Smuzhiyun mmc->max_blk_count = (ssp_is_old(ssp)) ? 0xff : 0xffffff;
663*4882a593Smuzhiyun mmc->max_req_size = (ssp_is_old(ssp)) ? 0xffff : 0xffffffff;
664*4882a593Smuzhiyun mmc->max_seg_size = dma_get_max_seg_size(ssp->dmach->device->dev);
665*4882a593Smuzhiyun
666*4882a593Smuzhiyun platform_set_drvdata(pdev, mmc);
667*4882a593Smuzhiyun
668*4882a593Smuzhiyun spin_lock_init(&host->lock);
669*4882a593Smuzhiyun
670*4882a593Smuzhiyun ret = devm_request_irq(&pdev->dev, irq_err, mxs_mmc_irq_handler, 0,
671*4882a593Smuzhiyun dev_name(&pdev->dev), host);
672*4882a593Smuzhiyun if (ret)
673*4882a593Smuzhiyun goto out_free_dma;
674*4882a593Smuzhiyun
675*4882a593Smuzhiyun ret = mmc_add_host(mmc);
676*4882a593Smuzhiyun if (ret)
677*4882a593Smuzhiyun goto out_free_dma;
678*4882a593Smuzhiyun
679*4882a593Smuzhiyun dev_info(mmc_dev(host->mmc), "initialized\n");
680*4882a593Smuzhiyun
681*4882a593Smuzhiyun return 0;
682*4882a593Smuzhiyun
683*4882a593Smuzhiyun out_free_dma:
684*4882a593Smuzhiyun dma_release_channel(ssp->dmach);
685*4882a593Smuzhiyun out_clk_disable:
686*4882a593Smuzhiyun clk_disable_unprepare(ssp->clk);
687*4882a593Smuzhiyun out_mmc_free:
688*4882a593Smuzhiyun mmc_free_host(mmc);
689*4882a593Smuzhiyun return ret;
690*4882a593Smuzhiyun }
691*4882a593Smuzhiyun
mxs_mmc_remove(struct platform_device * pdev)692*4882a593Smuzhiyun static int mxs_mmc_remove(struct platform_device *pdev)
693*4882a593Smuzhiyun {
694*4882a593Smuzhiyun struct mmc_host *mmc = platform_get_drvdata(pdev);
695*4882a593Smuzhiyun struct mxs_mmc_host *host = mmc_priv(mmc);
696*4882a593Smuzhiyun struct mxs_ssp *ssp = &host->ssp;
697*4882a593Smuzhiyun
698*4882a593Smuzhiyun mmc_remove_host(mmc);
699*4882a593Smuzhiyun
700*4882a593Smuzhiyun if (ssp->dmach)
701*4882a593Smuzhiyun dma_release_channel(ssp->dmach);
702*4882a593Smuzhiyun
703*4882a593Smuzhiyun clk_disable_unprepare(ssp->clk);
704*4882a593Smuzhiyun
705*4882a593Smuzhiyun mmc_free_host(mmc);
706*4882a593Smuzhiyun
707*4882a593Smuzhiyun return 0;
708*4882a593Smuzhiyun }
709*4882a593Smuzhiyun
710*4882a593Smuzhiyun #ifdef CONFIG_PM_SLEEP
mxs_mmc_suspend(struct device * dev)711*4882a593Smuzhiyun static int mxs_mmc_suspend(struct device *dev)
712*4882a593Smuzhiyun {
713*4882a593Smuzhiyun struct mmc_host *mmc = dev_get_drvdata(dev);
714*4882a593Smuzhiyun struct mxs_mmc_host *host = mmc_priv(mmc);
715*4882a593Smuzhiyun struct mxs_ssp *ssp = &host->ssp;
716*4882a593Smuzhiyun
717*4882a593Smuzhiyun clk_disable_unprepare(ssp->clk);
718*4882a593Smuzhiyun return 0;
719*4882a593Smuzhiyun }
720*4882a593Smuzhiyun
mxs_mmc_resume(struct device * dev)721*4882a593Smuzhiyun static int mxs_mmc_resume(struct device *dev)
722*4882a593Smuzhiyun {
723*4882a593Smuzhiyun struct mmc_host *mmc = dev_get_drvdata(dev);
724*4882a593Smuzhiyun struct mxs_mmc_host *host = mmc_priv(mmc);
725*4882a593Smuzhiyun struct mxs_ssp *ssp = &host->ssp;
726*4882a593Smuzhiyun
727*4882a593Smuzhiyun return clk_prepare_enable(ssp->clk);
728*4882a593Smuzhiyun }
729*4882a593Smuzhiyun #endif
730*4882a593Smuzhiyun
731*4882a593Smuzhiyun static SIMPLE_DEV_PM_OPS(mxs_mmc_pm_ops, mxs_mmc_suspend, mxs_mmc_resume);
732*4882a593Smuzhiyun
733*4882a593Smuzhiyun static struct platform_driver mxs_mmc_driver = {
734*4882a593Smuzhiyun .probe = mxs_mmc_probe,
735*4882a593Smuzhiyun .remove = mxs_mmc_remove,
736*4882a593Smuzhiyun .id_table = mxs_ssp_ids,
737*4882a593Smuzhiyun .driver = {
738*4882a593Smuzhiyun .name = DRIVER_NAME,
739*4882a593Smuzhiyun .probe_type = PROBE_PREFER_ASYNCHRONOUS,
740*4882a593Smuzhiyun .pm = &mxs_mmc_pm_ops,
741*4882a593Smuzhiyun .of_match_table = mxs_mmc_dt_ids,
742*4882a593Smuzhiyun },
743*4882a593Smuzhiyun };
744*4882a593Smuzhiyun
745*4882a593Smuzhiyun module_platform_driver(mxs_mmc_driver);
746*4882a593Smuzhiyun
747*4882a593Smuzhiyun MODULE_DESCRIPTION("FREESCALE MXS MMC peripheral");
748*4882a593Smuzhiyun MODULE_AUTHOR("Freescale Semiconductor");
749*4882a593Smuzhiyun MODULE_LICENSE("GPL");
750*4882a593Smuzhiyun MODULE_ALIAS("platform:" DRIVER_NAME);
751