xref: /openbmc/u-boot/drivers/mmc/ftsdc010_mci.c (revision 252185f2)
1f6c3b346SKuo-Jung Su /*
2f6c3b346SKuo-Jung Su  * Faraday MMC/SD Host Controller
3f6c3b346SKuo-Jung Su  *
4f6c3b346SKuo-Jung Su  * (C) Copyright 2010 Faraday Technology
5f6c3b346SKuo-Jung Su  * Dante Su <dantesu@faraday-tech.com>
6f6c3b346SKuo-Jung Su  *
71a459660SWolfgang Denk  * SPDX-License-Identifier:	GPL-2.0+
8f6c3b346SKuo-Jung Su  */
9f6c3b346SKuo-Jung Su 
10f6c3b346SKuo-Jung Su #include <common.h>
11f6c3b346SKuo-Jung Su #include <malloc.h>
12f6c3b346SKuo-Jung Su #include <part.h>
13f6c3b346SKuo-Jung Su #include <mmc.h>
14f6c3b346SKuo-Jung Su 
15*252185f2SRick Chen #include <linux/io.h>
161221ce45SMasahiro Yamada #include <linux/errno.h>
17f6c3b346SKuo-Jung Su #include <asm/byteorder.h>
18f6c3b346SKuo-Jung Su #include <faraday/ftsdc010.h>
19*252185f2SRick Chen #include "ftsdc010_mci.h"
20f6c3b346SKuo-Jung Su 
21f6c3b346SKuo-Jung Su #define CFG_CMD_TIMEOUT (CONFIG_SYS_HZ >> 4) /* 250 ms */
22f6c3b346SKuo-Jung Su #define CFG_RST_TIMEOUT CONFIG_SYS_HZ /* 1 sec reset timeout */
23f6c3b346SKuo-Jung Su 
24f6c3b346SKuo-Jung Su static inline int ftsdc010_send_cmd(struct mmc *mmc, struct mmc_cmd *mmc_cmd)
25f6c3b346SKuo-Jung Su {
26f6c3b346SKuo-Jung Su 	struct ftsdc010_chip *chip = mmc->priv;
27f6c3b346SKuo-Jung Su 	struct ftsdc010_mmc __iomem *regs = chip->regs;
28915ffa52SJaehoon Chung 	int ret = -ETIMEDOUT;
29f6c3b346SKuo-Jung Su 	uint32_t ts, st;
30f6c3b346SKuo-Jung Su 	uint32_t cmd   = FTSDC010_CMD_IDX(mmc_cmd->cmdidx);
31f6c3b346SKuo-Jung Su 	uint32_t arg   = mmc_cmd->cmdarg;
32f6c3b346SKuo-Jung Su 	uint32_t flags = mmc_cmd->resp_type;
33f6c3b346SKuo-Jung Su 
34f6c3b346SKuo-Jung Su 	cmd |= FTSDC010_CMD_CMD_EN;
35f6c3b346SKuo-Jung Su 
36f6c3b346SKuo-Jung Su 	if (chip->acmd) {
37f6c3b346SKuo-Jung Su 		cmd |= FTSDC010_CMD_APP_CMD;
38f6c3b346SKuo-Jung Su 		chip->acmd = 0;
39f6c3b346SKuo-Jung Su 	}
40f6c3b346SKuo-Jung Su 
41f6c3b346SKuo-Jung Su 	if (flags & MMC_RSP_PRESENT)
42f6c3b346SKuo-Jung Su 		cmd |= FTSDC010_CMD_NEED_RSP;
43f6c3b346SKuo-Jung Su 
44f6c3b346SKuo-Jung Su 	if (flags & MMC_RSP_136)
45f6c3b346SKuo-Jung Su 		cmd |= FTSDC010_CMD_LONG_RSP;
46f6c3b346SKuo-Jung Su 
47f6c3b346SKuo-Jung Su 	writel(FTSDC010_STATUS_RSP_MASK | FTSDC010_STATUS_CMD_SEND,
48f6c3b346SKuo-Jung Su 		&regs->clr);
49f6c3b346SKuo-Jung Su 	writel(arg, &regs->argu);
50f6c3b346SKuo-Jung Su 	writel(cmd, &regs->cmd);
51f6c3b346SKuo-Jung Su 
52f6c3b346SKuo-Jung Su 	if (!(flags & (MMC_RSP_PRESENT | MMC_RSP_136))) {
53f6c3b346SKuo-Jung Su 		for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) {
54f6c3b346SKuo-Jung Su 			if (readl(&regs->status) & FTSDC010_STATUS_CMD_SEND) {
55f6c3b346SKuo-Jung Su 				writel(FTSDC010_STATUS_CMD_SEND, &regs->clr);
56f6c3b346SKuo-Jung Su 				ret = 0;
57f6c3b346SKuo-Jung Su 				break;
58f6c3b346SKuo-Jung Su 			}
59f6c3b346SKuo-Jung Su 		}
60f6c3b346SKuo-Jung Su 	} else {
61f6c3b346SKuo-Jung Su 		st = 0;
62f6c3b346SKuo-Jung Su 		for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) {
63f6c3b346SKuo-Jung Su 			st = readl(&regs->status);
64f6c3b346SKuo-Jung Su 			writel(st & FTSDC010_STATUS_RSP_MASK, &regs->clr);
65f6c3b346SKuo-Jung Su 			if (st & FTSDC010_STATUS_RSP_MASK)
66f6c3b346SKuo-Jung Su 				break;
67f6c3b346SKuo-Jung Su 		}
68f6c3b346SKuo-Jung Su 		if (st & FTSDC010_STATUS_RSP_CRC_OK) {
69f6c3b346SKuo-Jung Su 			if (flags & MMC_RSP_136) {
70f6c3b346SKuo-Jung Su 				mmc_cmd->response[0] = readl(&regs->rsp3);
71f6c3b346SKuo-Jung Su 				mmc_cmd->response[1] = readl(&regs->rsp2);
72f6c3b346SKuo-Jung Su 				mmc_cmd->response[2] = readl(&regs->rsp1);
73f6c3b346SKuo-Jung Su 				mmc_cmd->response[3] = readl(&regs->rsp0);
74f6c3b346SKuo-Jung Su 			} else {
75f6c3b346SKuo-Jung Su 				mmc_cmd->response[0] = readl(&regs->rsp0);
76f6c3b346SKuo-Jung Su 			}
77f6c3b346SKuo-Jung Su 			ret = 0;
78f6c3b346SKuo-Jung Su 		} else {
79f6c3b346SKuo-Jung Su 			debug("ftsdc010: rsp err (cmd=%d, st=0x%x)\n",
80f6c3b346SKuo-Jung Su 				mmc_cmd->cmdidx, st);
81f6c3b346SKuo-Jung Su 		}
82f6c3b346SKuo-Jung Su 	}
83f6c3b346SKuo-Jung Su 
84f6c3b346SKuo-Jung Su 	if (ret) {
85f6c3b346SKuo-Jung Su 		debug("ftsdc010: cmd timeout (op code=%d)\n",
86f6c3b346SKuo-Jung Su 			mmc_cmd->cmdidx);
87f6c3b346SKuo-Jung Su 	} else if (mmc_cmd->cmdidx == MMC_CMD_APP_CMD) {
88f6c3b346SKuo-Jung Su 		chip->acmd = 1;
89f6c3b346SKuo-Jung Su 	}
90f6c3b346SKuo-Jung Su 
91f6c3b346SKuo-Jung Su 	return ret;
92f6c3b346SKuo-Jung Su }
93f6c3b346SKuo-Jung Su 
94f6c3b346SKuo-Jung Su static void ftsdc010_clkset(struct mmc *mmc, uint32_t rate)
95f6c3b346SKuo-Jung Su {
96f6c3b346SKuo-Jung Su 	struct ftsdc010_chip *chip = mmc->priv;
97f6c3b346SKuo-Jung Su 	struct ftsdc010_mmc __iomem *regs = chip->regs;
98f6c3b346SKuo-Jung Su 	uint32_t div;
99f6c3b346SKuo-Jung Su 
100f6c3b346SKuo-Jung Su 	for (div = 0; div < 0x7f; ++div) {
101f6c3b346SKuo-Jung Su 		if (rate >= chip->sclk / (2 * (div + 1)))
102f6c3b346SKuo-Jung Su 			break;
103f6c3b346SKuo-Jung Su 	}
104f6c3b346SKuo-Jung Su 	chip->rate = chip->sclk / (2 * (div + 1));
105f6c3b346SKuo-Jung Su 
106f6c3b346SKuo-Jung Su 	writel(FTSDC010_CCR_CLK_DIV(div), &regs->ccr);
107f6c3b346SKuo-Jung Su 
108f6c3b346SKuo-Jung Su 	if (IS_SD(mmc)) {
109f6c3b346SKuo-Jung Su 		setbits_le32(&regs->ccr, FTSDC010_CCR_CLK_SD);
110f6c3b346SKuo-Jung Su 
111f6c3b346SKuo-Jung Su 		if (chip->rate > 25000000)
112f6c3b346SKuo-Jung Su 			setbits_le32(&regs->ccr, FTSDC010_CCR_CLK_HISPD);
113f6c3b346SKuo-Jung Su 		else
114f6c3b346SKuo-Jung Su 			clrbits_le32(&regs->ccr, FTSDC010_CCR_CLK_HISPD);
115f6c3b346SKuo-Jung Su 	}
116f6c3b346SKuo-Jung Su }
117f6c3b346SKuo-Jung Su 
118f6c3b346SKuo-Jung Su static int ftsdc010_wait(struct ftsdc010_mmc __iomem *regs, uint32_t mask)
119f6c3b346SKuo-Jung Su {
120915ffa52SJaehoon Chung 	int ret = -ETIMEDOUT;
121*252185f2SRick Chen 	uint32_t st, timeout = 10000000;
122*252185f2SRick Chen 	while (timeout--) {
123f6c3b346SKuo-Jung Su 		st = readl(&regs->status);
124f6c3b346SKuo-Jung Su 		if (!(st & mask))
125f6c3b346SKuo-Jung Su 			continue;
126f6c3b346SKuo-Jung Su 		writel(st & mask, &regs->clr);
127f6c3b346SKuo-Jung Su 		ret = 0;
128f6c3b346SKuo-Jung Su 		break;
129f6c3b346SKuo-Jung Su 	}
130f6c3b346SKuo-Jung Su 
131f6c3b346SKuo-Jung Su 	if (ret)
132f6c3b346SKuo-Jung Su 		debug("ftsdc010: wait st(0x%x) timeout\n", mask);
133f6c3b346SKuo-Jung Su 
134f6c3b346SKuo-Jung Su 	return ret;
135f6c3b346SKuo-Jung Su }
136f6c3b346SKuo-Jung Su 
137f6c3b346SKuo-Jung Su /*
138f6c3b346SKuo-Jung Su  * u-boot mmc api
139f6c3b346SKuo-Jung Su  */
140*252185f2SRick Chen #ifdef CONFIG_DM_MMC
141*252185f2SRick Chen static int ftsdc010_request(struct udevice *dev, struct mmc_cmd *cmd,
142*252185f2SRick Chen 	struct mmc_data *data)
143*252185f2SRick Chen {
144*252185f2SRick Chen 	struct mmc *mmc = mmc_get_mmc_dev(dev);
145*252185f2SRick Chen #else
146f6c3b346SKuo-Jung Su static int ftsdc010_request(struct mmc *mmc, struct mmc_cmd *cmd,
147f6c3b346SKuo-Jung Su 	struct mmc_data *data)
148f6c3b346SKuo-Jung Su {
149*252185f2SRick Chen #endif
150915ffa52SJaehoon Chung 	int ret = -EOPNOTSUPP;
151f6c3b346SKuo-Jung Su 	uint32_t len = 0;
152f6c3b346SKuo-Jung Su 	struct ftsdc010_chip *chip = mmc->priv;
153f6c3b346SKuo-Jung Su 	struct ftsdc010_mmc __iomem *regs = chip->regs;
154f6c3b346SKuo-Jung Su 
155f6c3b346SKuo-Jung Su 	if (data && (data->flags & MMC_DATA_WRITE) && chip->wprot) {
156f6c3b346SKuo-Jung Su 		printf("ftsdc010: the card is write protected!\n");
157f6c3b346SKuo-Jung Su 		return ret;
158f6c3b346SKuo-Jung Su 	}
159f6c3b346SKuo-Jung Su 
160f6c3b346SKuo-Jung Su 	if (data) {
161f6c3b346SKuo-Jung Su 		uint32_t dcr;
162f6c3b346SKuo-Jung Su 
163f6c3b346SKuo-Jung Su 		len = data->blocksize * data->blocks;
164f6c3b346SKuo-Jung Su 
165f6c3b346SKuo-Jung Su 		/* 1. data disable + fifo reset */
166dbb713baSGabor Juhos 		dcr = 0;
167dbb713baSGabor Juhos #ifdef CONFIG_FTSDC010_SDIO
168dbb713baSGabor Juhos 		dcr |= FTSDC010_DCR_FIFO_RST;
169dbb713baSGabor Juhos #endif
170dbb713baSGabor Juhos 		writel(dcr, &regs->dcr);
171f6c3b346SKuo-Jung Su 
172f6c3b346SKuo-Jung Su 		/* 2. clear status register */
173f6c3b346SKuo-Jung Su 		writel(FTSDC010_STATUS_DATA_MASK | FTSDC010_STATUS_FIFO_URUN
174f6c3b346SKuo-Jung Su 			| FTSDC010_STATUS_FIFO_ORUN, &regs->clr);
175f6c3b346SKuo-Jung Su 
176f6c3b346SKuo-Jung Su 		/* 3. data timeout (1 sec) */
177f6c3b346SKuo-Jung Su 		writel(chip->rate, &regs->dtr);
178f6c3b346SKuo-Jung Su 
179f6c3b346SKuo-Jung Su 		/* 4. data length (bytes) */
180f6c3b346SKuo-Jung Su 		writel(len, &regs->dlr);
181f6c3b346SKuo-Jung Su 
182f6c3b346SKuo-Jung Su 		/* 5. data enable */
183f6c3b346SKuo-Jung Su 		dcr = (ffs(data->blocksize) - 1) | FTSDC010_DCR_DATA_EN;
184f6c3b346SKuo-Jung Su 		if (data->flags & MMC_DATA_WRITE)
185f6c3b346SKuo-Jung Su 			dcr |= FTSDC010_DCR_DATA_WRITE;
186f6c3b346SKuo-Jung Su 		writel(dcr, &regs->dcr);
187f6c3b346SKuo-Jung Su 	}
188f6c3b346SKuo-Jung Su 
189f6c3b346SKuo-Jung Su 	ret = ftsdc010_send_cmd(mmc, cmd);
190f6c3b346SKuo-Jung Su 	if (ret) {
191f6c3b346SKuo-Jung Su 		printf("ftsdc010: CMD%d failed\n", cmd->cmdidx);
192f6c3b346SKuo-Jung Su 		return ret;
193f6c3b346SKuo-Jung Su 	}
194f6c3b346SKuo-Jung Su 
195f6c3b346SKuo-Jung Su 	if (!data)
196f6c3b346SKuo-Jung Su 		return ret;
197f6c3b346SKuo-Jung Su 
198f6c3b346SKuo-Jung Su 	if (data->flags & MMC_DATA_WRITE) {
199f6c3b346SKuo-Jung Su 		const uint8_t *buf = (const uint8_t *)data->src;
200f6c3b346SKuo-Jung Su 
201f6c3b346SKuo-Jung Su 		while (len > 0) {
202f6c3b346SKuo-Jung Su 			int wlen;
203f6c3b346SKuo-Jung Su 
204f6c3b346SKuo-Jung Su 			/* wait for tx ready */
205f6c3b346SKuo-Jung Su 			ret = ftsdc010_wait(regs, FTSDC010_STATUS_FIFO_URUN);
206f6c3b346SKuo-Jung Su 			if (ret)
207f6c3b346SKuo-Jung Su 				break;
208f6c3b346SKuo-Jung Su 
209f6c3b346SKuo-Jung Su 			/* write bytes to ftsdc010 */
210f6c3b346SKuo-Jung Su 			for (wlen = 0; wlen < len && wlen < chip->fifo; ) {
211f6c3b346SKuo-Jung Su 				writel(*(uint32_t *)buf, &regs->dwr);
212f6c3b346SKuo-Jung Su 				buf  += 4;
213f6c3b346SKuo-Jung Su 				wlen += 4;
214f6c3b346SKuo-Jung Su 			}
215f6c3b346SKuo-Jung Su 
216f6c3b346SKuo-Jung Su 			len -= wlen;
217f6c3b346SKuo-Jung Su 		}
218f6c3b346SKuo-Jung Su 
219f6c3b346SKuo-Jung Su 	} else {
220f6c3b346SKuo-Jung Su 		uint8_t *buf = (uint8_t *)data->dest;
221f6c3b346SKuo-Jung Su 
222f6c3b346SKuo-Jung Su 		while (len > 0) {
223f6c3b346SKuo-Jung Su 			int rlen;
224f6c3b346SKuo-Jung Su 
225f6c3b346SKuo-Jung Su 			/* wait for rx ready */
226f6c3b346SKuo-Jung Su 			ret = ftsdc010_wait(regs, FTSDC010_STATUS_FIFO_ORUN);
227f6c3b346SKuo-Jung Su 			if (ret)
228f6c3b346SKuo-Jung Su 				break;
229f6c3b346SKuo-Jung Su 
230f6c3b346SKuo-Jung Su 			/* fetch bytes from ftsdc010 */
231f6c3b346SKuo-Jung Su 			for (rlen = 0; rlen < len && rlen < chip->fifo; ) {
232f6c3b346SKuo-Jung Su 				*(uint32_t *)buf = readl(&regs->dwr);
233f6c3b346SKuo-Jung Su 				buf  += 4;
234f6c3b346SKuo-Jung Su 				rlen += 4;
235f6c3b346SKuo-Jung Su 			}
236f6c3b346SKuo-Jung Su 
237f6c3b346SKuo-Jung Su 			len -= rlen;
238f6c3b346SKuo-Jung Su 		}
239f6c3b346SKuo-Jung Su 
240f6c3b346SKuo-Jung Su 	}
241f6c3b346SKuo-Jung Su 
242f6c3b346SKuo-Jung Su 	if (!ret) {
243f6c3b346SKuo-Jung Su 		ret = ftsdc010_wait(regs,
244f6c3b346SKuo-Jung Su 			FTSDC010_STATUS_DATA_END | FTSDC010_STATUS_DATA_ERROR);
245f6c3b346SKuo-Jung Su 	}
246f6c3b346SKuo-Jung Su 
247f6c3b346SKuo-Jung Su 	return ret;
248f6c3b346SKuo-Jung Su }
249f6c3b346SKuo-Jung Su 
250*252185f2SRick Chen #ifdef CONFIG_DM_MMC
251*252185f2SRick Chen static int ftsdc010_set_ios(struct udevice *dev)
252*252185f2SRick Chen {
253*252185f2SRick Chen 	struct mmc *mmc = mmc_get_mmc_dev(dev);
254*252185f2SRick Chen #else
25507b0b9c0SJaehoon Chung static int ftsdc010_set_ios(struct mmc *mmc)
256f6c3b346SKuo-Jung Su {
257*252185f2SRick Chen #endif
258f6c3b346SKuo-Jung Su 	struct ftsdc010_chip *chip = mmc->priv;
259f6c3b346SKuo-Jung Su 	struct ftsdc010_mmc __iomem *regs = chip->regs;
260f6c3b346SKuo-Jung Su 
261f6c3b346SKuo-Jung Su 	ftsdc010_clkset(mmc, mmc->clock);
262f6c3b346SKuo-Jung Su 
263f6c3b346SKuo-Jung Su 	clrbits_le32(&regs->bwr, FTSDC010_BWR_MODE_MASK);
264f6c3b346SKuo-Jung Su 	switch (mmc->bus_width) {
265f6c3b346SKuo-Jung Su 	case 4:
266f6c3b346SKuo-Jung Su 		setbits_le32(&regs->bwr, FTSDC010_BWR_MODE_4BIT);
267f6c3b346SKuo-Jung Su 		break;
268f6c3b346SKuo-Jung Su 	case 8:
269f6c3b346SKuo-Jung Su 		setbits_le32(&regs->bwr, FTSDC010_BWR_MODE_8BIT);
270f6c3b346SKuo-Jung Su 		break;
271f6c3b346SKuo-Jung Su 	default:
272f6c3b346SKuo-Jung Su 		setbits_le32(&regs->bwr, FTSDC010_BWR_MODE_1BIT);
273f6c3b346SKuo-Jung Su 		break;
274f6c3b346SKuo-Jung Su 	}
27507b0b9c0SJaehoon Chung 
27607b0b9c0SJaehoon Chung 	return 0;
277f6c3b346SKuo-Jung Su }
278f6c3b346SKuo-Jung Su 
279*252185f2SRick Chen #ifdef CONFIG_DM_MMC
280*252185f2SRick Chen static int ftsdc010_get_cd(struct udevice *dev)
281*252185f2SRick Chen {
282*252185f2SRick Chen 	struct mmc *mmc = mmc_get_mmc_dev(dev);
283*252185f2SRick Chen #else
284*252185f2SRick Chen static int ftsdc010_get_cd(struct mmc *mmc)
285*252185f2SRick Chen {
286*252185f2SRick Chen #endif
287*252185f2SRick Chen 	struct ftsdc010_chip *chip = mmc->priv;
288*252185f2SRick Chen 	struct ftsdc010_mmc __iomem *regs = chip->regs;
289*252185f2SRick Chen 	return !(readl(&regs->status) & FTSDC010_STATUS_CARD_DETECT);
290*252185f2SRick Chen }
291*252185f2SRick Chen 
292*252185f2SRick Chen #ifdef CONFIG_DM_MMC
293*252185f2SRick Chen static int ftsdc010_get_wp(struct udevice *dev)
294*252185f2SRick Chen {
295*252185f2SRick Chen 	struct mmc *mmc = mmc_get_mmc_dev(dev);
296*252185f2SRick Chen #else
297*252185f2SRick Chen static int ftsdc010_get_wp(struct mmc *mmc)
298*252185f2SRick Chen {
299*252185f2SRick Chen #endif
300*252185f2SRick Chen 	struct ftsdc010_chip *chip = mmc->priv;
301*252185f2SRick Chen 	struct ftsdc010_mmc __iomem *regs = chip->regs;
302*252185f2SRick Chen 	if (readl(&regs->status) & FTSDC010_STATUS_WRITE_PROT) {
303*252185f2SRick Chen 		printf("ftsdc010: write protected\n");
304*252185f2SRick Chen 		chip->wprot = 1;
305*252185f2SRick Chen 	}
306*252185f2SRick Chen 
307*252185f2SRick Chen 	return 0;
308*252185f2SRick Chen }
309*252185f2SRick Chen 
310f6c3b346SKuo-Jung Su static int ftsdc010_init(struct mmc *mmc)
311f6c3b346SKuo-Jung Su {
312f6c3b346SKuo-Jung Su 	struct ftsdc010_chip *chip = mmc->priv;
313f6c3b346SKuo-Jung Su 	struct ftsdc010_mmc __iomem *regs = chip->regs;
314f6c3b346SKuo-Jung Su 	uint32_t ts;
315f6c3b346SKuo-Jung Su 
316f6c3b346SKuo-Jung Su 	chip->fifo = (readl(&regs->feature) & 0xff) << 2;
317f6c3b346SKuo-Jung Su 
318f6c3b346SKuo-Jung Su 	/* 1. chip reset */
319f6c3b346SKuo-Jung Su 	writel(FTSDC010_CMD_SDC_RST, &regs->cmd);
320f6c3b346SKuo-Jung Su 	for (ts = get_timer(0); get_timer(ts) < CFG_RST_TIMEOUT; ) {
321f6c3b346SKuo-Jung Su 		if (readl(&regs->cmd) & FTSDC010_CMD_SDC_RST)
322f6c3b346SKuo-Jung Su 			continue;
323f6c3b346SKuo-Jung Su 		break;
324f6c3b346SKuo-Jung Su 	}
325f6c3b346SKuo-Jung Su 	if (readl(&regs->cmd) & FTSDC010_CMD_SDC_RST) {
326f6c3b346SKuo-Jung Su 		printf("ftsdc010: reset failed\n");
327915ffa52SJaehoon Chung 		return -EOPNOTSUPP;
328f6c3b346SKuo-Jung Su 	}
329f6c3b346SKuo-Jung Su 
330f6c3b346SKuo-Jung Su 	/* 2. enter low speed mode (400k card detection) */
331f6c3b346SKuo-Jung Su 	ftsdc010_clkset(mmc, 400000);
332f6c3b346SKuo-Jung Su 
333f6c3b346SKuo-Jung Su 	/* 3. interrupt disabled */
334f6c3b346SKuo-Jung Su 	writel(0, &regs->int_mask);
335f6c3b346SKuo-Jung Su 
336f6c3b346SKuo-Jung Su 	return 0;
337f6c3b346SKuo-Jung Su }
338f6c3b346SKuo-Jung Su 
339*252185f2SRick Chen #ifdef CONFIG_DM_MMC
340*252185f2SRick Chen int ftsdc010_probe(struct udevice *dev)
341*252185f2SRick Chen {
342*252185f2SRick Chen 	struct mmc *mmc = mmc_get_mmc_dev(dev);
343*252185f2SRick Chen 	return ftsdc010_init(mmc);
344*252185f2SRick Chen }
345*252185f2SRick Chen 
346*252185f2SRick Chen const struct dm_mmc_ops dm_ftsdc010_ops = {
347*252185f2SRick Chen 	.send_cmd	= ftsdc010_request,
348*252185f2SRick Chen 	.set_ios	= ftsdc010_set_ios,
349*252185f2SRick Chen 	.get_cd		= ftsdc010_get_cd,
350*252185f2SRick Chen 	.get_wp		= ftsdc010_get_wp,
351*252185f2SRick Chen };
352*252185f2SRick Chen 
353*252185f2SRick Chen #else
354ab769f22SPantelis Antoniou static const struct mmc_ops ftsdc010_ops = {
355ab769f22SPantelis Antoniou 	.send_cmd	= ftsdc010_request,
356ab769f22SPantelis Antoniou 	.set_ios	= ftsdc010_set_ios,
357*252185f2SRick Chen 	.getcd		= ftsdc010_get_cd,
358*252185f2SRick Chen 	.getwp		= ftsdc010_get_wp,
359ab769f22SPantelis Antoniou 	.init		= ftsdc010_init,
360ab769f22SPantelis Antoniou };
361*252185f2SRick Chen #endif
362*252185f2SRick Chen 
363*252185f2SRick Chen void ftsdc_setup_cfg(struct mmc_config *cfg, const char *name, int buswidth,
364*252185f2SRick Chen 		     uint caps, u32 max_clk, u32 min_clk)
365*252185f2SRick Chen {
366*252185f2SRick Chen 	cfg->name = name;
367*252185f2SRick Chen 	cfg->f_min = min_clk;
368*252185f2SRick Chen 	cfg->f_max = max_clk;
369*252185f2SRick Chen 	cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
370*252185f2SRick Chen 	cfg->host_caps = caps;
371*252185f2SRick Chen 	if (buswidth == 8) {
372*252185f2SRick Chen 		cfg->host_caps |= MMC_MODE_8BIT;
373*252185f2SRick Chen 		cfg->host_caps &= ~MMC_MODE_4BIT;
374*252185f2SRick Chen 	} else {
375*252185f2SRick Chen 		cfg->host_caps |= MMC_MODE_4BIT;
376*252185f2SRick Chen 		cfg->host_caps &= ~MMC_MODE_8BIT;
377*252185f2SRick Chen 	}
378*252185f2SRick Chen 	cfg->host_caps |= MMC_MODE_HS | MMC_MODE_HS_52MHz;
379*252185f2SRick Chen 	cfg->part_type = PART_TYPE_DOS;
380*252185f2SRick Chen 	cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
381*252185f2SRick Chen }
382*252185f2SRick Chen 
383*252185f2SRick Chen void set_bus_width(struct ftsdc010_mmc __iomem *regs, struct mmc_config *cfg)
384*252185f2SRick Chen {
385*252185f2SRick Chen 	switch (readl(&regs->bwr) & FTSDC010_BWR_CAPS_MASK) {
386*252185f2SRick Chen 	case FTSDC010_BWR_CAPS_4BIT:
387*252185f2SRick Chen 		cfg->host_caps |= MMC_MODE_4BIT;
388*252185f2SRick Chen 		break;
389*252185f2SRick Chen 	case FTSDC010_BWR_CAPS_8BIT:
390*252185f2SRick Chen 		cfg->host_caps |= MMC_MODE_4BIT | MMC_MODE_8BIT;
391*252185f2SRick Chen 		break;
392*252185f2SRick Chen 	default:
393*252185f2SRick Chen 		break;
394*252185f2SRick Chen 	}
395*252185f2SRick Chen }
396*252185f2SRick Chen 
397*252185f2SRick Chen #ifdef CONFIG_BLK
398*252185f2SRick Chen int ftsdc010_bind(struct udevice *dev, struct mmc *mmc, struct mmc_config *cfg)
399*252185f2SRick Chen {
400*252185f2SRick Chen 	return mmc_bind(dev, mmc, cfg);
401*252185f2SRick Chen }
402*252185f2SRick Chen #else
403ab769f22SPantelis Antoniou 
404f6c3b346SKuo-Jung Su int ftsdc010_mmc_init(int devid)
405f6c3b346SKuo-Jung Su {
406f6c3b346SKuo-Jung Su 	struct mmc *mmc;
407f6c3b346SKuo-Jung Su 	struct ftsdc010_chip *chip;
408f6c3b346SKuo-Jung Su 	struct ftsdc010_mmc __iomem *regs;
409f6c3b346SKuo-Jung Su #ifdef CONFIG_FTSDC010_BASE_LIST
410f6c3b346SKuo-Jung Su 	uint32_t base_list[] = CONFIG_FTSDC010_BASE_LIST;
411f6c3b346SKuo-Jung Su 
412f6c3b346SKuo-Jung Su 	if (devid < 0 || devid >= ARRAY_SIZE(base_list))
413f6c3b346SKuo-Jung Su 		return -1;
414f6c3b346SKuo-Jung Su 	regs = (void __iomem *)base_list[devid];
415f6c3b346SKuo-Jung Su #else
416f6c3b346SKuo-Jung Su 	regs = (void __iomem *)(CONFIG_FTSDC010_BASE + (devid << 20));
417f6c3b346SKuo-Jung Su #endif
418f6c3b346SKuo-Jung Su 
419f6c3b346SKuo-Jung Su 	chip = malloc(sizeof(struct ftsdc010_chip));
42093bfd616SPantelis Antoniou 	if (!chip)
421f6c3b346SKuo-Jung Su 		return -ENOMEM;
422f6c3b346SKuo-Jung Su 	memset(chip, 0, sizeof(struct ftsdc010_chip));
423f6c3b346SKuo-Jung Su 
424f6c3b346SKuo-Jung Su 	chip->regs = regs;
425f6c3b346SKuo-Jung Su #ifdef CONFIG_SYS_CLK_FREQ
426f6c3b346SKuo-Jung Su 	chip->sclk = CONFIG_SYS_CLK_FREQ;
427f6c3b346SKuo-Jung Su #else
428f6c3b346SKuo-Jung Su 	chip->sclk = clk_get_rate("SDC");
429f6c3b346SKuo-Jung Su #endif
430f6c3b346SKuo-Jung Su 
43193bfd616SPantelis Antoniou 	chip->cfg.name = "ftsdc010";
432*252185f2SRick Chen #ifndef CONFIG_DM_MMC
43393bfd616SPantelis Antoniou 	chip->cfg.ops = &ftsdc010_ops;
434*252185f2SRick Chen #endif
43593bfd616SPantelis Antoniou 	chip->cfg.host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz;
436*252185f2SRick Chen 	set_bus_width(regs , &chip->cfg);
43793bfd616SPantelis Antoniou 	chip->cfg.voltages  = MMC_VDD_32_33 | MMC_VDD_33_34;
43893bfd616SPantelis Antoniou 	chip->cfg.f_max     = chip->sclk / 2;
43993bfd616SPantelis Antoniou 	chip->cfg.f_min     = chip->sclk / 0x100;
44093bfd616SPantelis Antoniou 
44193bfd616SPantelis Antoniou 	chip->cfg.part_type = PART_TYPE_DOS;
44293bfd616SPantelis Antoniou 	chip->cfg.b_max	    = CONFIG_SYS_MMC_MAX_BLK_COUNT;
44393bfd616SPantelis Antoniou 
44493bfd616SPantelis Antoniou 	mmc = mmc_create(&chip->cfg, chip);
44593bfd616SPantelis Antoniou 	if (mmc == NULL) {
44693bfd616SPantelis Antoniou 		free(chip);
44793bfd616SPantelis Antoniou 		return -ENOMEM;
44893bfd616SPantelis Antoniou 	}
449f6c3b346SKuo-Jung Su 
450f6c3b346SKuo-Jung Su 	return 0;
451f6c3b346SKuo-Jung Su }
452*252185f2SRick Chen #endif
453