xref: /openbmc/u-boot/drivers/mmc/ftsdc010_mci.c (revision 1221ce45)
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 
15f6c3b346SKuo-Jung Su #include <asm/io.h>
16*1221ce45SMasahiro Yamada #include <linux/errno.h>
17f6c3b346SKuo-Jung Su #include <asm/byteorder.h>
18f6c3b346SKuo-Jung Su #include <faraday/ftsdc010.h>
19f6c3b346SKuo-Jung Su 
20f6c3b346SKuo-Jung Su #define CFG_CMD_TIMEOUT (CONFIG_SYS_HZ >> 4) /* 250 ms */
21f6c3b346SKuo-Jung Su #define CFG_RST_TIMEOUT CONFIG_SYS_HZ /* 1 sec reset timeout */
22f6c3b346SKuo-Jung Su 
23f6c3b346SKuo-Jung Su struct ftsdc010_chip {
24f6c3b346SKuo-Jung Su 	void __iomem *regs;
25f6c3b346SKuo-Jung Su 	uint32_t wprot;   /* write protected (locked) */
26f6c3b346SKuo-Jung Su 	uint32_t rate;    /* actual SD clock in Hz */
27f6c3b346SKuo-Jung Su 	uint32_t sclk;    /* FTSDC010 source clock in Hz */
28f6c3b346SKuo-Jung Su 	uint32_t fifo;    /* fifo depth in bytes */
29f6c3b346SKuo-Jung Su 	uint32_t acmd;
3093bfd616SPantelis Antoniou 	struct mmc_config cfg;	/* mmc configuration */
31f6c3b346SKuo-Jung Su };
32f6c3b346SKuo-Jung Su 
33f6c3b346SKuo-Jung Su static inline int ftsdc010_send_cmd(struct mmc *mmc, struct mmc_cmd *mmc_cmd)
34f6c3b346SKuo-Jung Su {
35f6c3b346SKuo-Jung Su 	struct ftsdc010_chip *chip = mmc->priv;
36f6c3b346SKuo-Jung Su 	struct ftsdc010_mmc __iomem *regs = chip->regs;
37915ffa52SJaehoon Chung 	int ret = -ETIMEDOUT;
38f6c3b346SKuo-Jung Su 	uint32_t ts, st;
39f6c3b346SKuo-Jung Su 	uint32_t cmd   = FTSDC010_CMD_IDX(mmc_cmd->cmdidx);
40f6c3b346SKuo-Jung Su 	uint32_t arg   = mmc_cmd->cmdarg;
41f6c3b346SKuo-Jung Su 	uint32_t flags = mmc_cmd->resp_type;
42f6c3b346SKuo-Jung Su 
43f6c3b346SKuo-Jung Su 	cmd |= FTSDC010_CMD_CMD_EN;
44f6c3b346SKuo-Jung Su 
45f6c3b346SKuo-Jung Su 	if (chip->acmd) {
46f6c3b346SKuo-Jung Su 		cmd |= FTSDC010_CMD_APP_CMD;
47f6c3b346SKuo-Jung Su 		chip->acmd = 0;
48f6c3b346SKuo-Jung Su 	}
49f6c3b346SKuo-Jung Su 
50f6c3b346SKuo-Jung Su 	if (flags & MMC_RSP_PRESENT)
51f6c3b346SKuo-Jung Su 		cmd |= FTSDC010_CMD_NEED_RSP;
52f6c3b346SKuo-Jung Su 
53f6c3b346SKuo-Jung Su 	if (flags & MMC_RSP_136)
54f6c3b346SKuo-Jung Su 		cmd |= FTSDC010_CMD_LONG_RSP;
55f6c3b346SKuo-Jung Su 
56f6c3b346SKuo-Jung Su 	writel(FTSDC010_STATUS_RSP_MASK | FTSDC010_STATUS_CMD_SEND,
57f6c3b346SKuo-Jung Su 		&regs->clr);
58f6c3b346SKuo-Jung Su 	writel(arg, &regs->argu);
59f6c3b346SKuo-Jung Su 	writel(cmd, &regs->cmd);
60f6c3b346SKuo-Jung Su 
61f6c3b346SKuo-Jung Su 	if (!(flags & (MMC_RSP_PRESENT | MMC_RSP_136))) {
62f6c3b346SKuo-Jung Su 		for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) {
63f6c3b346SKuo-Jung Su 			if (readl(&regs->status) & FTSDC010_STATUS_CMD_SEND) {
64f6c3b346SKuo-Jung Su 				writel(FTSDC010_STATUS_CMD_SEND, &regs->clr);
65f6c3b346SKuo-Jung Su 				ret = 0;
66f6c3b346SKuo-Jung Su 				break;
67f6c3b346SKuo-Jung Su 			}
68f6c3b346SKuo-Jung Su 		}
69f6c3b346SKuo-Jung Su 	} else {
70f6c3b346SKuo-Jung Su 		st = 0;
71f6c3b346SKuo-Jung Su 		for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) {
72f6c3b346SKuo-Jung Su 			st = readl(&regs->status);
73f6c3b346SKuo-Jung Su 			writel(st & FTSDC010_STATUS_RSP_MASK, &regs->clr);
74f6c3b346SKuo-Jung Su 			if (st & FTSDC010_STATUS_RSP_MASK)
75f6c3b346SKuo-Jung Su 				break;
76f6c3b346SKuo-Jung Su 		}
77f6c3b346SKuo-Jung Su 		if (st & FTSDC010_STATUS_RSP_CRC_OK) {
78f6c3b346SKuo-Jung Su 			if (flags & MMC_RSP_136) {
79f6c3b346SKuo-Jung Su 				mmc_cmd->response[0] = readl(&regs->rsp3);
80f6c3b346SKuo-Jung Su 				mmc_cmd->response[1] = readl(&regs->rsp2);
81f6c3b346SKuo-Jung Su 				mmc_cmd->response[2] = readl(&regs->rsp1);
82f6c3b346SKuo-Jung Su 				mmc_cmd->response[3] = readl(&regs->rsp0);
83f6c3b346SKuo-Jung Su 			} else {
84f6c3b346SKuo-Jung Su 				mmc_cmd->response[0] = readl(&regs->rsp0);
85f6c3b346SKuo-Jung Su 			}
86f6c3b346SKuo-Jung Su 			ret = 0;
87f6c3b346SKuo-Jung Su 		} else {
88f6c3b346SKuo-Jung Su 			debug("ftsdc010: rsp err (cmd=%d, st=0x%x)\n",
89f6c3b346SKuo-Jung Su 				mmc_cmd->cmdidx, st);
90f6c3b346SKuo-Jung Su 		}
91f6c3b346SKuo-Jung Su 	}
92f6c3b346SKuo-Jung Su 
93f6c3b346SKuo-Jung Su 	if (ret) {
94f6c3b346SKuo-Jung Su 		debug("ftsdc010: cmd timeout (op code=%d)\n",
95f6c3b346SKuo-Jung Su 			mmc_cmd->cmdidx);
96f6c3b346SKuo-Jung Su 	} else if (mmc_cmd->cmdidx == MMC_CMD_APP_CMD) {
97f6c3b346SKuo-Jung Su 		chip->acmd = 1;
98f6c3b346SKuo-Jung Su 	}
99f6c3b346SKuo-Jung Su 
100f6c3b346SKuo-Jung Su 	return ret;
101f6c3b346SKuo-Jung Su }
102f6c3b346SKuo-Jung Su 
103f6c3b346SKuo-Jung Su static void ftsdc010_clkset(struct mmc *mmc, uint32_t rate)
104f6c3b346SKuo-Jung Su {
105f6c3b346SKuo-Jung Su 	struct ftsdc010_chip *chip = mmc->priv;
106f6c3b346SKuo-Jung Su 	struct ftsdc010_mmc __iomem *regs = chip->regs;
107f6c3b346SKuo-Jung Su 	uint32_t div;
108f6c3b346SKuo-Jung Su 
109f6c3b346SKuo-Jung Su 	for (div = 0; div < 0x7f; ++div) {
110f6c3b346SKuo-Jung Su 		if (rate >= chip->sclk / (2 * (div + 1)))
111f6c3b346SKuo-Jung Su 			break;
112f6c3b346SKuo-Jung Su 	}
113f6c3b346SKuo-Jung Su 	chip->rate = chip->sclk / (2 * (div + 1));
114f6c3b346SKuo-Jung Su 
115f6c3b346SKuo-Jung Su 	writel(FTSDC010_CCR_CLK_DIV(div), &regs->ccr);
116f6c3b346SKuo-Jung Su 
117f6c3b346SKuo-Jung Su 	if (IS_SD(mmc)) {
118f6c3b346SKuo-Jung Su 		setbits_le32(&regs->ccr, FTSDC010_CCR_CLK_SD);
119f6c3b346SKuo-Jung Su 
120f6c3b346SKuo-Jung Su 		if (chip->rate > 25000000)
121f6c3b346SKuo-Jung Su 			setbits_le32(&regs->ccr, FTSDC010_CCR_CLK_HISPD);
122f6c3b346SKuo-Jung Su 		else
123f6c3b346SKuo-Jung Su 			clrbits_le32(&regs->ccr, FTSDC010_CCR_CLK_HISPD);
124f6c3b346SKuo-Jung Su 	}
125f6c3b346SKuo-Jung Su }
126f6c3b346SKuo-Jung Su 
127f6c3b346SKuo-Jung Su static int ftsdc010_wait(struct ftsdc010_mmc __iomem *regs, uint32_t mask)
128f6c3b346SKuo-Jung Su {
129915ffa52SJaehoon Chung 	int ret = -ETIMEDOUT;
130f6c3b346SKuo-Jung Su 	uint32_t st, ts;
131f6c3b346SKuo-Jung Su 
132f6c3b346SKuo-Jung Su 	for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) {
133f6c3b346SKuo-Jung Su 		st = readl(&regs->status);
134f6c3b346SKuo-Jung Su 		if (!(st & mask))
135f6c3b346SKuo-Jung Su 			continue;
136f6c3b346SKuo-Jung Su 		writel(st & mask, &regs->clr);
137f6c3b346SKuo-Jung Su 		ret = 0;
138f6c3b346SKuo-Jung Su 		break;
139f6c3b346SKuo-Jung Su 	}
140f6c3b346SKuo-Jung Su 
141f6c3b346SKuo-Jung Su 	if (ret)
142f6c3b346SKuo-Jung Su 		debug("ftsdc010: wait st(0x%x) timeout\n", mask);
143f6c3b346SKuo-Jung Su 
144f6c3b346SKuo-Jung Su 	return ret;
145f6c3b346SKuo-Jung Su }
146f6c3b346SKuo-Jung Su 
147f6c3b346SKuo-Jung Su /*
148f6c3b346SKuo-Jung Su  * u-boot mmc api
149f6c3b346SKuo-Jung Su  */
150f6c3b346SKuo-Jung Su 
151f6c3b346SKuo-Jung Su static int ftsdc010_request(struct mmc *mmc, struct mmc_cmd *cmd,
152f6c3b346SKuo-Jung Su 	struct mmc_data *data)
153f6c3b346SKuo-Jung Su {
154915ffa52SJaehoon Chung 	int ret = -EOPNOTSUPP;
155f6c3b346SKuo-Jung Su 	uint32_t len = 0;
156f6c3b346SKuo-Jung Su 	struct ftsdc010_chip *chip = mmc->priv;
157f6c3b346SKuo-Jung Su 	struct ftsdc010_mmc __iomem *regs = chip->regs;
158f6c3b346SKuo-Jung Su 
159f6c3b346SKuo-Jung Su 	if (data && (data->flags & MMC_DATA_WRITE) && chip->wprot) {
160f6c3b346SKuo-Jung Su 		printf("ftsdc010: the card is write protected!\n");
161f6c3b346SKuo-Jung Su 		return ret;
162f6c3b346SKuo-Jung Su 	}
163f6c3b346SKuo-Jung Su 
164f6c3b346SKuo-Jung Su 	if (data) {
165f6c3b346SKuo-Jung Su 		uint32_t dcr;
166f6c3b346SKuo-Jung Su 
167f6c3b346SKuo-Jung Su 		len = data->blocksize * data->blocks;
168f6c3b346SKuo-Jung Su 
169f6c3b346SKuo-Jung Su 		/* 1. data disable + fifo reset */
170dbb713baSGabor Juhos 		dcr = 0;
171dbb713baSGabor Juhos #ifdef CONFIG_FTSDC010_SDIO
172dbb713baSGabor Juhos 		dcr |= FTSDC010_DCR_FIFO_RST;
173dbb713baSGabor Juhos #endif
174dbb713baSGabor Juhos 		writel(dcr, &regs->dcr);
175f6c3b346SKuo-Jung Su 
176f6c3b346SKuo-Jung Su 		/* 2. clear status register */
177f6c3b346SKuo-Jung Su 		writel(FTSDC010_STATUS_DATA_MASK | FTSDC010_STATUS_FIFO_URUN
178f6c3b346SKuo-Jung Su 			| FTSDC010_STATUS_FIFO_ORUN, &regs->clr);
179f6c3b346SKuo-Jung Su 
180f6c3b346SKuo-Jung Su 		/* 3. data timeout (1 sec) */
181f6c3b346SKuo-Jung Su 		writel(chip->rate, &regs->dtr);
182f6c3b346SKuo-Jung Su 
183f6c3b346SKuo-Jung Su 		/* 4. data length (bytes) */
184f6c3b346SKuo-Jung Su 		writel(len, &regs->dlr);
185f6c3b346SKuo-Jung Su 
186f6c3b346SKuo-Jung Su 		/* 5. data enable */
187f6c3b346SKuo-Jung Su 		dcr = (ffs(data->blocksize) - 1) | FTSDC010_DCR_DATA_EN;
188f6c3b346SKuo-Jung Su 		if (data->flags & MMC_DATA_WRITE)
189f6c3b346SKuo-Jung Su 			dcr |= FTSDC010_DCR_DATA_WRITE;
190f6c3b346SKuo-Jung Su 		writel(dcr, &regs->dcr);
191f6c3b346SKuo-Jung Su 	}
192f6c3b346SKuo-Jung Su 
193f6c3b346SKuo-Jung Su 	ret = ftsdc010_send_cmd(mmc, cmd);
194f6c3b346SKuo-Jung Su 	if (ret) {
195f6c3b346SKuo-Jung Su 		printf("ftsdc010: CMD%d failed\n", cmd->cmdidx);
196f6c3b346SKuo-Jung Su 		return ret;
197f6c3b346SKuo-Jung Su 	}
198f6c3b346SKuo-Jung Su 
199f6c3b346SKuo-Jung Su 	if (!data)
200f6c3b346SKuo-Jung Su 		return ret;
201f6c3b346SKuo-Jung Su 
202f6c3b346SKuo-Jung Su 	if (data->flags & MMC_DATA_WRITE) {
203f6c3b346SKuo-Jung Su 		const uint8_t *buf = (const uint8_t *)data->src;
204f6c3b346SKuo-Jung Su 
205f6c3b346SKuo-Jung Su 		while (len > 0) {
206f6c3b346SKuo-Jung Su 			int wlen;
207f6c3b346SKuo-Jung Su 
208f6c3b346SKuo-Jung Su 			/* wait for tx ready */
209f6c3b346SKuo-Jung Su 			ret = ftsdc010_wait(regs, FTSDC010_STATUS_FIFO_URUN);
210f6c3b346SKuo-Jung Su 			if (ret)
211f6c3b346SKuo-Jung Su 				break;
212f6c3b346SKuo-Jung Su 
213f6c3b346SKuo-Jung Su 			/* write bytes to ftsdc010 */
214f6c3b346SKuo-Jung Su 			for (wlen = 0; wlen < len && wlen < chip->fifo; ) {
215f6c3b346SKuo-Jung Su 				writel(*(uint32_t *)buf, &regs->dwr);
216f6c3b346SKuo-Jung Su 				buf  += 4;
217f6c3b346SKuo-Jung Su 				wlen += 4;
218f6c3b346SKuo-Jung Su 			}
219f6c3b346SKuo-Jung Su 
220f6c3b346SKuo-Jung Su 			len -= wlen;
221f6c3b346SKuo-Jung Su 		}
222f6c3b346SKuo-Jung Su 
223f6c3b346SKuo-Jung Su 	} else {
224f6c3b346SKuo-Jung Su 		uint8_t *buf = (uint8_t *)data->dest;
225f6c3b346SKuo-Jung Su 
226f6c3b346SKuo-Jung Su 		while (len > 0) {
227f6c3b346SKuo-Jung Su 			int rlen;
228f6c3b346SKuo-Jung Su 
229f6c3b346SKuo-Jung Su 			/* wait for rx ready */
230f6c3b346SKuo-Jung Su 			ret = ftsdc010_wait(regs, FTSDC010_STATUS_FIFO_ORUN);
231f6c3b346SKuo-Jung Su 			if (ret)
232f6c3b346SKuo-Jung Su 				break;
233f6c3b346SKuo-Jung Su 
234f6c3b346SKuo-Jung Su 			/* fetch bytes from ftsdc010 */
235f6c3b346SKuo-Jung Su 			for (rlen = 0; rlen < len && rlen < chip->fifo; ) {
236f6c3b346SKuo-Jung Su 				*(uint32_t *)buf = readl(&regs->dwr);
237f6c3b346SKuo-Jung Su 				buf  += 4;
238f6c3b346SKuo-Jung Su 				rlen += 4;
239f6c3b346SKuo-Jung Su 			}
240f6c3b346SKuo-Jung Su 
241f6c3b346SKuo-Jung Su 			len -= rlen;
242f6c3b346SKuo-Jung Su 		}
243f6c3b346SKuo-Jung Su 
244f6c3b346SKuo-Jung Su 	}
245f6c3b346SKuo-Jung Su 
246f6c3b346SKuo-Jung Su 	if (!ret) {
247f6c3b346SKuo-Jung Su 		ret = ftsdc010_wait(regs,
248f6c3b346SKuo-Jung Su 			FTSDC010_STATUS_DATA_END | FTSDC010_STATUS_DATA_ERROR);
249f6c3b346SKuo-Jung Su 	}
250f6c3b346SKuo-Jung Su 
251f6c3b346SKuo-Jung Su 	return ret;
252f6c3b346SKuo-Jung Su }
253f6c3b346SKuo-Jung Su 
254f6c3b346SKuo-Jung Su static void ftsdc010_set_ios(struct mmc *mmc)
255f6c3b346SKuo-Jung Su {
256f6c3b346SKuo-Jung Su 	struct ftsdc010_chip *chip = mmc->priv;
257f6c3b346SKuo-Jung Su 	struct ftsdc010_mmc __iomem *regs = chip->regs;
258f6c3b346SKuo-Jung Su 
259f6c3b346SKuo-Jung Su 	ftsdc010_clkset(mmc, mmc->clock);
260f6c3b346SKuo-Jung Su 
261f6c3b346SKuo-Jung Su 	clrbits_le32(&regs->bwr, FTSDC010_BWR_MODE_MASK);
262f6c3b346SKuo-Jung Su 	switch (mmc->bus_width) {
263f6c3b346SKuo-Jung Su 	case 4:
264f6c3b346SKuo-Jung Su 		setbits_le32(&regs->bwr, FTSDC010_BWR_MODE_4BIT);
265f6c3b346SKuo-Jung Su 		break;
266f6c3b346SKuo-Jung Su 	case 8:
267f6c3b346SKuo-Jung Su 		setbits_le32(&regs->bwr, FTSDC010_BWR_MODE_8BIT);
268f6c3b346SKuo-Jung Su 		break;
269f6c3b346SKuo-Jung Su 	default:
270f6c3b346SKuo-Jung Su 		setbits_le32(&regs->bwr, FTSDC010_BWR_MODE_1BIT);
271f6c3b346SKuo-Jung Su 		break;
272f6c3b346SKuo-Jung Su 	}
273f6c3b346SKuo-Jung Su }
274f6c3b346SKuo-Jung Su 
275f6c3b346SKuo-Jung Su static int ftsdc010_init(struct mmc *mmc)
276f6c3b346SKuo-Jung Su {
277f6c3b346SKuo-Jung Su 	struct ftsdc010_chip *chip = mmc->priv;
278f6c3b346SKuo-Jung Su 	struct ftsdc010_mmc __iomem *regs = chip->regs;
279f6c3b346SKuo-Jung Su 	uint32_t ts;
280f6c3b346SKuo-Jung Su 
281f6c3b346SKuo-Jung Su 	if (readl(&regs->status) & FTSDC010_STATUS_CARD_DETECT)
282915ffa52SJaehoon Chung 		return -ENOMEDIUM;
283f6c3b346SKuo-Jung Su 
284f6c3b346SKuo-Jung Su 	if (readl(&regs->status) & FTSDC010_STATUS_WRITE_PROT) {
285f6c3b346SKuo-Jung Su 		printf("ftsdc010: write protected\n");
286f6c3b346SKuo-Jung Su 		chip->wprot = 1;
287f6c3b346SKuo-Jung Su 	}
288f6c3b346SKuo-Jung Su 
289f6c3b346SKuo-Jung Su 	chip->fifo = (readl(&regs->feature) & 0xff) << 2;
290f6c3b346SKuo-Jung Su 
291f6c3b346SKuo-Jung Su 	/* 1. chip reset */
292f6c3b346SKuo-Jung Su 	writel(FTSDC010_CMD_SDC_RST, &regs->cmd);
293f6c3b346SKuo-Jung Su 	for (ts = get_timer(0); get_timer(ts) < CFG_RST_TIMEOUT; ) {
294f6c3b346SKuo-Jung Su 		if (readl(&regs->cmd) & FTSDC010_CMD_SDC_RST)
295f6c3b346SKuo-Jung Su 			continue;
296f6c3b346SKuo-Jung Su 		break;
297f6c3b346SKuo-Jung Su 	}
298f6c3b346SKuo-Jung Su 	if (readl(&regs->cmd) & FTSDC010_CMD_SDC_RST) {
299f6c3b346SKuo-Jung Su 		printf("ftsdc010: reset failed\n");
300915ffa52SJaehoon Chung 		return -EOPNOTSUPP;
301f6c3b346SKuo-Jung Su 	}
302f6c3b346SKuo-Jung Su 
303f6c3b346SKuo-Jung Su 	/* 2. enter low speed mode (400k card detection) */
304f6c3b346SKuo-Jung Su 	ftsdc010_clkset(mmc, 400000);
305f6c3b346SKuo-Jung Su 
306f6c3b346SKuo-Jung Su 	/* 3. interrupt disabled */
307f6c3b346SKuo-Jung Su 	writel(0, &regs->int_mask);
308f6c3b346SKuo-Jung Su 
309f6c3b346SKuo-Jung Su 	return 0;
310f6c3b346SKuo-Jung Su }
311f6c3b346SKuo-Jung Su 
312ab769f22SPantelis Antoniou static const struct mmc_ops ftsdc010_ops = {
313ab769f22SPantelis Antoniou 	.send_cmd	= ftsdc010_request,
314ab769f22SPantelis Antoniou 	.set_ios	= ftsdc010_set_ios,
315ab769f22SPantelis Antoniou 	.init		= ftsdc010_init,
316ab769f22SPantelis Antoniou };
317ab769f22SPantelis Antoniou 
318f6c3b346SKuo-Jung Su int ftsdc010_mmc_init(int devid)
319f6c3b346SKuo-Jung Su {
320f6c3b346SKuo-Jung Su 	struct mmc *mmc;
321f6c3b346SKuo-Jung Su 	struct ftsdc010_chip *chip;
322f6c3b346SKuo-Jung Su 	struct ftsdc010_mmc __iomem *regs;
323f6c3b346SKuo-Jung Su #ifdef CONFIG_FTSDC010_BASE_LIST
324f6c3b346SKuo-Jung Su 	uint32_t base_list[] = CONFIG_FTSDC010_BASE_LIST;
325f6c3b346SKuo-Jung Su 
326f6c3b346SKuo-Jung Su 	if (devid < 0 || devid >= ARRAY_SIZE(base_list))
327f6c3b346SKuo-Jung Su 		return -1;
328f6c3b346SKuo-Jung Su 	regs = (void __iomem *)base_list[devid];
329f6c3b346SKuo-Jung Su #else
330f6c3b346SKuo-Jung Su 	regs = (void __iomem *)(CONFIG_FTSDC010_BASE + (devid << 20));
331f6c3b346SKuo-Jung Su #endif
332f6c3b346SKuo-Jung Su 
333f6c3b346SKuo-Jung Su 	chip = malloc(sizeof(struct ftsdc010_chip));
33493bfd616SPantelis Antoniou 	if (!chip)
335f6c3b346SKuo-Jung Su 		return -ENOMEM;
336f6c3b346SKuo-Jung Su 	memset(chip, 0, sizeof(struct ftsdc010_chip));
337f6c3b346SKuo-Jung Su 
338f6c3b346SKuo-Jung Su 	chip->regs = regs;
339f6c3b346SKuo-Jung Su #ifdef CONFIG_SYS_CLK_FREQ
340f6c3b346SKuo-Jung Su 	chip->sclk = CONFIG_SYS_CLK_FREQ;
341f6c3b346SKuo-Jung Su #else
342f6c3b346SKuo-Jung Su 	chip->sclk = clk_get_rate("SDC");
343f6c3b346SKuo-Jung Su #endif
344f6c3b346SKuo-Jung Su 
34593bfd616SPantelis Antoniou 	chip->cfg.name = "ftsdc010";
34693bfd616SPantelis Antoniou 	chip->cfg.ops = &ftsdc010_ops;
34793bfd616SPantelis Antoniou 	chip->cfg.host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz;
34893bfd616SPantelis Antoniou 	switch (readl(&regs->bwr) & FTSDC010_BWR_CAPS_MASK) {
34993bfd616SPantelis Antoniou 	case FTSDC010_BWR_CAPS_4BIT:
35093bfd616SPantelis Antoniou 		chip->cfg.host_caps |= MMC_MODE_4BIT;
35193bfd616SPantelis Antoniou 		break;
35293bfd616SPantelis Antoniou 	case FTSDC010_BWR_CAPS_8BIT:
35393bfd616SPantelis Antoniou 		chip->cfg.host_caps |= MMC_MODE_4BIT | MMC_MODE_8BIT;
35493bfd616SPantelis Antoniou 		break;
35593bfd616SPantelis Antoniou 	default:
35693bfd616SPantelis Antoniou 		break;
35793bfd616SPantelis Antoniou 	}
358f6c3b346SKuo-Jung Su 
35993bfd616SPantelis Antoniou 	chip->cfg.voltages  = MMC_VDD_32_33 | MMC_VDD_33_34;
36093bfd616SPantelis Antoniou 	chip->cfg.f_max     = chip->sclk / 2;
36193bfd616SPantelis Antoniou 	chip->cfg.f_min     = chip->sclk / 0x100;
36293bfd616SPantelis Antoniou 
36393bfd616SPantelis Antoniou 	chip->cfg.part_type = PART_TYPE_DOS;
36493bfd616SPantelis Antoniou 	chip->cfg.b_max	    = CONFIG_SYS_MMC_MAX_BLK_COUNT;
36593bfd616SPantelis Antoniou 
36693bfd616SPantelis Antoniou 	mmc = mmc_create(&chip->cfg, chip);
36793bfd616SPantelis Antoniou 	if (mmc == NULL) {
36893bfd616SPantelis Antoniou 		free(chip);
36993bfd616SPantelis Antoniou 		return -ENOMEM;
37093bfd616SPantelis Antoniou 	}
371f6c3b346SKuo-Jung Su 
372f6c3b346SKuo-Jung Su 	return 0;
373f6c3b346SKuo-Jung Su }
374