xref: /openbmc/u-boot/drivers/mmc/ftsdc010_mci.c (revision 3eceff64)
183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
2f6c3b346SKuo-Jung Su /*
3f6c3b346SKuo-Jung Su  * Faraday MMC/SD Host Controller
4f6c3b346SKuo-Jung Su  *
5f6c3b346SKuo-Jung Su  * (C) Copyright 2010 Faraday Technology
6f6c3b346SKuo-Jung Su  * Dante Su <dantesu@faraday-tech.com>
7f6c3b346SKuo-Jung Su  *
8bf9ba4dbSRick Chen  * Copyright 2018 Andes Technology, Inc.
9bf9ba4dbSRick Chen  * Author: Rick Chen (rick@andestech.com)
10f6c3b346SKuo-Jung Su  */
11f6c3b346SKuo-Jung Su 
12f6c3b346SKuo-Jung Su #include <common.h>
13bf9ba4dbSRick Chen #include <clk.h>
14f6c3b346SKuo-Jung Su #include <malloc.h>
15f6c3b346SKuo-Jung Su #include <part.h>
16f6c3b346SKuo-Jung Su #include <mmc.h>
17252185f2SRick Chen #include <linux/io.h>
181221ce45SMasahiro Yamada #include <linux/errno.h>
19f6c3b346SKuo-Jung Su #include <asm/byteorder.h>
20f6c3b346SKuo-Jung Su #include <faraday/ftsdc010.h>
21252185f2SRick Chen #include "ftsdc010_mci.h"
22bf9ba4dbSRick Chen #include <dm.h>
23bf9ba4dbSRick Chen #include <dt-structs.h>
24bf9ba4dbSRick Chen #include <errno.h>
25bf9ba4dbSRick Chen #include <mapmem.h>
26bf9ba4dbSRick Chen #include <pwrseq.h>
27bf9ba4dbSRick Chen #include <syscon.h>
28bf9ba4dbSRick Chen #include <linux/err.h>
29bf9ba4dbSRick Chen 
30bf9ba4dbSRick Chen DECLARE_GLOBAL_DATA_PTR;
31f6c3b346SKuo-Jung Su 
32f6c3b346SKuo-Jung Su #define CFG_CMD_TIMEOUT (CONFIG_SYS_HZ >> 4) /* 250 ms */
33f6c3b346SKuo-Jung Su #define CFG_RST_TIMEOUT CONFIG_SYS_HZ /* 1 sec reset timeout */
34f6c3b346SKuo-Jung Su 
35bf9ba4dbSRick Chen #if CONFIG_IS_ENABLED(OF_PLATDATA)
36bf9ba4dbSRick Chen struct ftsdc010 {
37bf9ba4dbSRick Chen 	fdt32_t		bus_width;
38bf9ba4dbSRick Chen 	bool		cap_mmc_highspeed;
39bf9ba4dbSRick Chen 	bool		cap_sd_highspeed;
40bf9ba4dbSRick Chen 	fdt32_t		clock_freq_min_max[2];
41bf9ba4dbSRick Chen 	struct phandle_2_cell	clocks[4];
42bf9ba4dbSRick Chen 	fdt32_t		fifo_depth;
43bf9ba4dbSRick Chen 	fdt32_t		reg[2];
44bf9ba4dbSRick Chen };
45bf9ba4dbSRick Chen #endif
46bf9ba4dbSRick Chen 
47bf9ba4dbSRick Chen struct ftsdc010_plat {
48bf9ba4dbSRick Chen #if CONFIG_IS_ENABLED(OF_PLATDATA)
49bf9ba4dbSRick Chen 	struct ftsdc010 dtplat;
50bf9ba4dbSRick Chen #endif
51bf9ba4dbSRick Chen 	struct mmc_config cfg;
52bf9ba4dbSRick Chen 	struct mmc mmc;
53bf9ba4dbSRick Chen };
54bf9ba4dbSRick Chen 
55bf9ba4dbSRick Chen struct ftsdc_priv {
56bf9ba4dbSRick Chen 	struct clk clk;
57bf9ba4dbSRick Chen 	struct ftsdc010_chip chip;
58bf9ba4dbSRick Chen 	int fifo_depth;
59bf9ba4dbSRick Chen 	bool fifo_mode;
60bf9ba4dbSRick Chen 	u32 minmax[2];
61bf9ba4dbSRick Chen };
62bf9ba4dbSRick Chen 
ftsdc010_send_cmd(struct mmc * mmc,struct mmc_cmd * mmc_cmd)63f6c3b346SKuo-Jung Su static inline int ftsdc010_send_cmd(struct mmc *mmc, struct mmc_cmd *mmc_cmd)
64f6c3b346SKuo-Jung Su {
65f6c3b346SKuo-Jung Su 	struct ftsdc010_chip *chip = mmc->priv;
66f6c3b346SKuo-Jung Su 	struct ftsdc010_mmc __iomem *regs = chip->regs;
67915ffa52SJaehoon Chung 	int ret = -ETIMEDOUT;
68f6c3b346SKuo-Jung Su 	uint32_t ts, st;
69f6c3b346SKuo-Jung Su 	uint32_t cmd   = FTSDC010_CMD_IDX(mmc_cmd->cmdidx);
70f6c3b346SKuo-Jung Su 	uint32_t arg   = mmc_cmd->cmdarg;
71f6c3b346SKuo-Jung Su 	uint32_t flags = mmc_cmd->resp_type;
72f6c3b346SKuo-Jung Su 
73f6c3b346SKuo-Jung Su 	cmd |= FTSDC010_CMD_CMD_EN;
74f6c3b346SKuo-Jung Su 
75f6c3b346SKuo-Jung Su 	if (chip->acmd) {
76f6c3b346SKuo-Jung Su 		cmd |= FTSDC010_CMD_APP_CMD;
77f6c3b346SKuo-Jung Su 		chip->acmd = 0;
78f6c3b346SKuo-Jung Su 	}
79f6c3b346SKuo-Jung Su 
80f6c3b346SKuo-Jung Su 	if (flags & MMC_RSP_PRESENT)
81f6c3b346SKuo-Jung Su 		cmd |= FTSDC010_CMD_NEED_RSP;
82f6c3b346SKuo-Jung Su 
83f6c3b346SKuo-Jung Su 	if (flags & MMC_RSP_136)
84f6c3b346SKuo-Jung Su 		cmd |= FTSDC010_CMD_LONG_RSP;
85f6c3b346SKuo-Jung Su 
86f6c3b346SKuo-Jung Su 	writel(FTSDC010_STATUS_RSP_MASK | FTSDC010_STATUS_CMD_SEND,
87f6c3b346SKuo-Jung Su 		&regs->clr);
88f6c3b346SKuo-Jung Su 	writel(arg, &regs->argu);
89f6c3b346SKuo-Jung Su 	writel(cmd, &regs->cmd);
90f6c3b346SKuo-Jung Su 
91f6c3b346SKuo-Jung Su 	if (!(flags & (MMC_RSP_PRESENT | MMC_RSP_136))) {
92f6c3b346SKuo-Jung Su 		for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) {
93f6c3b346SKuo-Jung Su 			if (readl(&regs->status) & FTSDC010_STATUS_CMD_SEND) {
94f6c3b346SKuo-Jung Su 				writel(FTSDC010_STATUS_CMD_SEND, &regs->clr);
95f6c3b346SKuo-Jung Su 				ret = 0;
96f6c3b346SKuo-Jung Su 				break;
97f6c3b346SKuo-Jung Su 			}
98f6c3b346SKuo-Jung Su 		}
99f6c3b346SKuo-Jung Su 	} else {
100f6c3b346SKuo-Jung Su 		st = 0;
101f6c3b346SKuo-Jung Su 		for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) {
102f6c3b346SKuo-Jung Su 			st = readl(&regs->status);
103f6c3b346SKuo-Jung Su 			writel(st & FTSDC010_STATUS_RSP_MASK, &regs->clr);
104f6c3b346SKuo-Jung Su 			if (st & FTSDC010_STATUS_RSP_MASK)
105f6c3b346SKuo-Jung Su 				break;
106f6c3b346SKuo-Jung Su 		}
107f6c3b346SKuo-Jung Su 		if (st & FTSDC010_STATUS_RSP_CRC_OK) {
108f6c3b346SKuo-Jung Su 			if (flags & MMC_RSP_136) {
109f6c3b346SKuo-Jung Su 				mmc_cmd->response[0] = readl(&regs->rsp3);
110f6c3b346SKuo-Jung Su 				mmc_cmd->response[1] = readl(&regs->rsp2);
111f6c3b346SKuo-Jung Su 				mmc_cmd->response[2] = readl(&regs->rsp1);
112f6c3b346SKuo-Jung Su 				mmc_cmd->response[3] = readl(&regs->rsp0);
113f6c3b346SKuo-Jung Su 			} else {
114f6c3b346SKuo-Jung Su 				mmc_cmd->response[0] = readl(&regs->rsp0);
115f6c3b346SKuo-Jung Su 			}
116f6c3b346SKuo-Jung Su 			ret = 0;
117f6c3b346SKuo-Jung Su 		} else {
118f6c3b346SKuo-Jung Su 			debug("ftsdc010: rsp err (cmd=%d, st=0x%x)\n",
119f6c3b346SKuo-Jung Su 				mmc_cmd->cmdidx, st);
120f6c3b346SKuo-Jung Su 		}
121f6c3b346SKuo-Jung Su 	}
122f6c3b346SKuo-Jung Su 
123f6c3b346SKuo-Jung Su 	if (ret) {
124f6c3b346SKuo-Jung Su 		debug("ftsdc010: cmd timeout (op code=%d)\n",
125f6c3b346SKuo-Jung Su 			mmc_cmd->cmdidx);
126f6c3b346SKuo-Jung Su 	} else if (mmc_cmd->cmdidx == MMC_CMD_APP_CMD) {
127f6c3b346SKuo-Jung Su 		chip->acmd = 1;
128f6c3b346SKuo-Jung Su 	}
129f6c3b346SKuo-Jung Su 
130f6c3b346SKuo-Jung Su 	return ret;
131f6c3b346SKuo-Jung Su }
132f6c3b346SKuo-Jung Su 
ftsdc010_clkset(struct mmc * mmc,uint32_t rate)133f6c3b346SKuo-Jung Su static void ftsdc010_clkset(struct mmc *mmc, uint32_t rate)
134f6c3b346SKuo-Jung Su {
135f6c3b346SKuo-Jung Su 	struct ftsdc010_chip *chip = mmc->priv;
136f6c3b346SKuo-Jung Su 	struct ftsdc010_mmc __iomem *regs = chip->regs;
137f6c3b346SKuo-Jung Su 	uint32_t div;
138f6c3b346SKuo-Jung Su 
139f6c3b346SKuo-Jung Su 	for (div = 0; div < 0x7f; ++div) {
140f6c3b346SKuo-Jung Su 		if (rate >= chip->sclk / (2 * (div + 1)))
141f6c3b346SKuo-Jung Su 			break;
142f6c3b346SKuo-Jung Su 	}
143f6c3b346SKuo-Jung Su 	chip->rate = chip->sclk / (2 * (div + 1));
144f6c3b346SKuo-Jung Su 
145f6c3b346SKuo-Jung Su 	writel(FTSDC010_CCR_CLK_DIV(div), &regs->ccr);
146f6c3b346SKuo-Jung Su 
147f6c3b346SKuo-Jung Su 	if (IS_SD(mmc)) {
148f6c3b346SKuo-Jung Su 		setbits_le32(&regs->ccr, FTSDC010_CCR_CLK_SD);
149f6c3b346SKuo-Jung Su 
150f6c3b346SKuo-Jung Su 		if (chip->rate > 25000000)
151f6c3b346SKuo-Jung Su 			setbits_le32(&regs->ccr, FTSDC010_CCR_CLK_HISPD);
152f6c3b346SKuo-Jung Su 		else
153f6c3b346SKuo-Jung Su 			clrbits_le32(&regs->ccr, FTSDC010_CCR_CLK_HISPD);
154f6c3b346SKuo-Jung Su 	}
155f6c3b346SKuo-Jung Su }
156f6c3b346SKuo-Jung Su 
ftsdc010_wait(struct ftsdc010_mmc __iomem * regs,uint32_t mask)157f6c3b346SKuo-Jung Su static int ftsdc010_wait(struct ftsdc010_mmc __iomem *regs, uint32_t mask)
158f6c3b346SKuo-Jung Su {
159915ffa52SJaehoon Chung 	int ret = -ETIMEDOUT;
160252185f2SRick Chen 	uint32_t st, timeout = 10000000;
161252185f2SRick Chen 	while (timeout--) {
162f6c3b346SKuo-Jung Su 		st = readl(&regs->status);
163f6c3b346SKuo-Jung Su 		if (!(st & mask))
164f6c3b346SKuo-Jung Su 			continue;
165f6c3b346SKuo-Jung Su 		writel(st & mask, &regs->clr);
166f6c3b346SKuo-Jung Su 		ret = 0;
167f6c3b346SKuo-Jung Su 		break;
168f6c3b346SKuo-Jung Su 	}
169f6c3b346SKuo-Jung Su 
1701a9db640SRick Chen 	if (ret){
171f6c3b346SKuo-Jung Su 		debug("ftsdc010: wait st(0x%x) timeout\n", mask);
1721a9db640SRick Chen 	}
173f6c3b346SKuo-Jung Su 
174f6c3b346SKuo-Jung Su 	return ret;
175f6c3b346SKuo-Jung Su }
176f6c3b346SKuo-Jung Su 
177f6c3b346SKuo-Jung Su /*
178f6c3b346SKuo-Jung Su  * u-boot mmc api
179f6c3b346SKuo-Jung Su  */
ftsdc010_request(struct udevice * dev,struct mmc_cmd * cmd,struct mmc_data * data)180252185f2SRick Chen static int ftsdc010_request(struct udevice *dev, struct mmc_cmd *cmd,
181252185f2SRick Chen 	struct mmc_data *data)
182252185f2SRick Chen {
183252185f2SRick Chen 	struct mmc *mmc = mmc_get_mmc_dev(dev);
184915ffa52SJaehoon Chung 	int ret = -EOPNOTSUPP;
185f6c3b346SKuo-Jung Su 	uint32_t len = 0;
186f6c3b346SKuo-Jung Su 	struct ftsdc010_chip *chip = mmc->priv;
187f6c3b346SKuo-Jung Su 	struct ftsdc010_mmc __iomem *regs = chip->regs;
188f6c3b346SKuo-Jung Su 
189f6c3b346SKuo-Jung Su 	if (data && (data->flags & MMC_DATA_WRITE) && chip->wprot) {
190f6c3b346SKuo-Jung Su 		printf("ftsdc010: the card is write protected!\n");
191f6c3b346SKuo-Jung Su 		return ret;
192f6c3b346SKuo-Jung Su 	}
193f6c3b346SKuo-Jung Su 
194f6c3b346SKuo-Jung Su 	if (data) {
195f6c3b346SKuo-Jung Su 		uint32_t dcr;
196f6c3b346SKuo-Jung Su 
197f6c3b346SKuo-Jung Su 		len = data->blocksize * data->blocks;
198f6c3b346SKuo-Jung Su 
199f6c3b346SKuo-Jung Su 		/* 1. data disable + fifo reset */
200dbb713baSGabor Juhos 		dcr = 0;
201dbb713baSGabor Juhos #ifdef CONFIG_FTSDC010_SDIO
202dbb713baSGabor Juhos 		dcr |= FTSDC010_DCR_FIFO_RST;
203dbb713baSGabor Juhos #endif
204dbb713baSGabor Juhos 		writel(dcr, &regs->dcr);
205f6c3b346SKuo-Jung Su 
206f6c3b346SKuo-Jung Su 		/* 2. clear status register */
207f6c3b346SKuo-Jung Su 		writel(FTSDC010_STATUS_DATA_MASK | FTSDC010_STATUS_FIFO_URUN
208f6c3b346SKuo-Jung Su 			| FTSDC010_STATUS_FIFO_ORUN, &regs->clr);
209f6c3b346SKuo-Jung Su 
210f6c3b346SKuo-Jung Su 		/* 3. data timeout (1 sec) */
211f6c3b346SKuo-Jung Su 		writel(chip->rate, &regs->dtr);
212f6c3b346SKuo-Jung Su 
213f6c3b346SKuo-Jung Su 		/* 4. data length (bytes) */
214f6c3b346SKuo-Jung Su 		writel(len, &regs->dlr);
215f6c3b346SKuo-Jung Su 
216f6c3b346SKuo-Jung Su 		/* 5. data enable */
217f6c3b346SKuo-Jung Su 		dcr = (ffs(data->blocksize) - 1) | FTSDC010_DCR_DATA_EN;
218f6c3b346SKuo-Jung Su 		if (data->flags & MMC_DATA_WRITE)
219f6c3b346SKuo-Jung Su 			dcr |= FTSDC010_DCR_DATA_WRITE;
220f6c3b346SKuo-Jung Su 		writel(dcr, &regs->dcr);
221f6c3b346SKuo-Jung Su 	}
222f6c3b346SKuo-Jung Su 
223f6c3b346SKuo-Jung Su 	ret = ftsdc010_send_cmd(mmc, cmd);
224f6c3b346SKuo-Jung Su 	if (ret) {
225f6c3b346SKuo-Jung Su 		printf("ftsdc010: CMD%d failed\n", cmd->cmdidx);
226f6c3b346SKuo-Jung Su 		return ret;
227f6c3b346SKuo-Jung Su 	}
228f6c3b346SKuo-Jung Su 
229f6c3b346SKuo-Jung Su 	if (!data)
230f6c3b346SKuo-Jung Su 		return ret;
231f6c3b346SKuo-Jung Su 
232f6c3b346SKuo-Jung Su 	if (data->flags & MMC_DATA_WRITE) {
233f6c3b346SKuo-Jung Su 		const uint8_t *buf = (const uint8_t *)data->src;
234f6c3b346SKuo-Jung Su 
235f6c3b346SKuo-Jung Su 		while (len > 0) {
236f6c3b346SKuo-Jung Su 			int wlen;
237f6c3b346SKuo-Jung Su 
238f6c3b346SKuo-Jung Su 			/* wait for tx ready */
239f6c3b346SKuo-Jung Su 			ret = ftsdc010_wait(regs, FTSDC010_STATUS_FIFO_URUN);
240f6c3b346SKuo-Jung Su 			if (ret)
241f6c3b346SKuo-Jung Su 				break;
242f6c3b346SKuo-Jung Su 
243f6c3b346SKuo-Jung Su 			/* write bytes to ftsdc010 */
244f6c3b346SKuo-Jung Su 			for (wlen = 0; wlen < len && wlen < chip->fifo; ) {
245f6c3b346SKuo-Jung Su 				writel(*(uint32_t *)buf, &regs->dwr);
246f6c3b346SKuo-Jung Su 				buf  += 4;
247f6c3b346SKuo-Jung Su 				wlen += 4;
248f6c3b346SKuo-Jung Su 			}
249f6c3b346SKuo-Jung Su 
250f6c3b346SKuo-Jung Su 			len -= wlen;
251f6c3b346SKuo-Jung Su 		}
252f6c3b346SKuo-Jung Su 
253f6c3b346SKuo-Jung Su 	} else {
254f6c3b346SKuo-Jung Su 		uint8_t *buf = (uint8_t *)data->dest;
255f6c3b346SKuo-Jung Su 
256f6c3b346SKuo-Jung Su 		while (len > 0) {
257f6c3b346SKuo-Jung Su 			int rlen;
258f6c3b346SKuo-Jung Su 
259f6c3b346SKuo-Jung Su 			/* wait for rx ready */
260f6c3b346SKuo-Jung Su 			ret = ftsdc010_wait(regs, FTSDC010_STATUS_FIFO_ORUN);
261f6c3b346SKuo-Jung Su 			if (ret)
262f6c3b346SKuo-Jung Su 				break;
263f6c3b346SKuo-Jung Su 
264f6c3b346SKuo-Jung Su 			/* fetch bytes from ftsdc010 */
265f6c3b346SKuo-Jung Su 			for (rlen = 0; rlen < len && rlen < chip->fifo; ) {
266f6c3b346SKuo-Jung Su 				*(uint32_t *)buf = readl(&regs->dwr);
267f6c3b346SKuo-Jung Su 				buf  += 4;
268f6c3b346SKuo-Jung Su 				rlen += 4;
269f6c3b346SKuo-Jung Su 			}
270f6c3b346SKuo-Jung Su 
271f6c3b346SKuo-Jung Su 			len -= rlen;
272f6c3b346SKuo-Jung Su 		}
273f6c3b346SKuo-Jung Su 
274f6c3b346SKuo-Jung Su 	}
275f6c3b346SKuo-Jung Su 
276f6c3b346SKuo-Jung Su 	if (!ret) {
277f6c3b346SKuo-Jung Su 		ret = ftsdc010_wait(regs,
2781a9db640SRick Chen 			FTSDC010_STATUS_DATA_END | FTSDC010_STATUS_DATA_CRC_OK);
279f6c3b346SKuo-Jung Su 	}
280f6c3b346SKuo-Jung Su 
281f6c3b346SKuo-Jung Su 	return ret;
282f6c3b346SKuo-Jung Su }
283f6c3b346SKuo-Jung Su 
ftsdc010_set_ios(struct udevice * dev)284252185f2SRick Chen static int ftsdc010_set_ios(struct udevice *dev)
285252185f2SRick Chen {
286252185f2SRick Chen 	struct mmc *mmc = mmc_get_mmc_dev(dev);
287f6c3b346SKuo-Jung Su 	struct ftsdc010_chip *chip = mmc->priv;
288f6c3b346SKuo-Jung Su 	struct ftsdc010_mmc __iomem *regs = chip->regs;
289f6c3b346SKuo-Jung Su 
290f6c3b346SKuo-Jung Su 	ftsdc010_clkset(mmc, mmc->clock);
291f6c3b346SKuo-Jung Su 
292f6c3b346SKuo-Jung Su 	clrbits_le32(&regs->bwr, FTSDC010_BWR_MODE_MASK);
293f6c3b346SKuo-Jung Su 	switch (mmc->bus_width) {
294f6c3b346SKuo-Jung Su 	case 4:
295f6c3b346SKuo-Jung Su 		setbits_le32(&regs->bwr, FTSDC010_BWR_MODE_4BIT);
296f6c3b346SKuo-Jung Su 		break;
297f6c3b346SKuo-Jung Su 	case 8:
298f6c3b346SKuo-Jung Su 		setbits_le32(&regs->bwr, FTSDC010_BWR_MODE_8BIT);
299f6c3b346SKuo-Jung Su 		break;
300f6c3b346SKuo-Jung Su 	default:
301f6c3b346SKuo-Jung Su 		setbits_le32(&regs->bwr, FTSDC010_BWR_MODE_1BIT);
302f6c3b346SKuo-Jung Su 		break;
303f6c3b346SKuo-Jung Su 	}
30407b0b9c0SJaehoon Chung 
30507b0b9c0SJaehoon Chung 	return 0;
306f6c3b346SKuo-Jung Su }
307f6c3b346SKuo-Jung Su 
ftsdc010_get_cd(struct udevice * dev)308252185f2SRick Chen static int ftsdc010_get_cd(struct udevice *dev)
309252185f2SRick Chen {
310252185f2SRick Chen 	struct mmc *mmc = mmc_get_mmc_dev(dev);
311252185f2SRick Chen 	struct ftsdc010_chip *chip = mmc->priv;
312252185f2SRick Chen 	struct ftsdc010_mmc __iomem *regs = chip->regs;
313252185f2SRick Chen 	return !(readl(&regs->status) & FTSDC010_STATUS_CARD_DETECT);
314252185f2SRick Chen }
315252185f2SRick Chen 
ftsdc010_get_wp(struct udevice * dev)316252185f2SRick Chen static int ftsdc010_get_wp(struct udevice *dev)
317252185f2SRick Chen {
318252185f2SRick Chen 	struct mmc *mmc = mmc_get_mmc_dev(dev);
319252185f2SRick Chen 	struct ftsdc010_chip *chip = mmc->priv;
320252185f2SRick Chen 	struct ftsdc010_mmc __iomem *regs = chip->regs;
321252185f2SRick Chen 	if (readl(&regs->status) & FTSDC010_STATUS_WRITE_PROT) {
322252185f2SRick Chen 		printf("ftsdc010: write protected\n");
323252185f2SRick Chen 		chip->wprot = 1;
324252185f2SRick Chen 	}
325252185f2SRick Chen 
326252185f2SRick Chen 	return 0;
327252185f2SRick Chen }
328252185f2SRick Chen 
ftsdc010_init(struct mmc * mmc)329f6c3b346SKuo-Jung Su static int ftsdc010_init(struct mmc *mmc)
330f6c3b346SKuo-Jung Su {
331f6c3b346SKuo-Jung Su 	struct ftsdc010_chip *chip = mmc->priv;
332f6c3b346SKuo-Jung Su 	struct ftsdc010_mmc __iomem *regs = chip->regs;
333f6c3b346SKuo-Jung Su 	uint32_t ts;
334f6c3b346SKuo-Jung Su 
335f6c3b346SKuo-Jung Su 	chip->fifo = (readl(&regs->feature) & 0xff) << 2;
336f6c3b346SKuo-Jung Su 
337f6c3b346SKuo-Jung Su 	/* 1. chip reset */
338f6c3b346SKuo-Jung Su 	writel(FTSDC010_CMD_SDC_RST, &regs->cmd);
339f6c3b346SKuo-Jung Su 	for (ts = get_timer(0); get_timer(ts) < CFG_RST_TIMEOUT; ) {
340f6c3b346SKuo-Jung Su 		if (readl(&regs->cmd) & FTSDC010_CMD_SDC_RST)
341f6c3b346SKuo-Jung Su 			continue;
342f6c3b346SKuo-Jung Su 		break;
343f6c3b346SKuo-Jung Su 	}
344f6c3b346SKuo-Jung Su 	if (readl(&regs->cmd) & FTSDC010_CMD_SDC_RST) {
345f6c3b346SKuo-Jung Su 		printf("ftsdc010: reset failed\n");
346915ffa52SJaehoon Chung 		return -EOPNOTSUPP;
347f6c3b346SKuo-Jung Su 	}
348f6c3b346SKuo-Jung Su 
349f6c3b346SKuo-Jung Su 	/* 2. enter low speed mode (400k card detection) */
350f6c3b346SKuo-Jung Su 	ftsdc010_clkset(mmc, 400000);
351f6c3b346SKuo-Jung Su 
352f6c3b346SKuo-Jung Su 	/* 3. interrupt disabled */
353f6c3b346SKuo-Jung Su 	writel(0, &regs->int_mask);
354f6c3b346SKuo-Jung Su 
355f6c3b346SKuo-Jung Su 	return 0;
356f6c3b346SKuo-Jung Su }
357f6c3b346SKuo-Jung Su 
ftsdc010_probe(struct udevice * dev)358bf9ba4dbSRick Chen static int ftsdc010_probe(struct udevice *dev)
359252185f2SRick Chen {
360252185f2SRick Chen 	struct mmc *mmc = mmc_get_mmc_dev(dev);
361252185f2SRick Chen 	return ftsdc010_init(mmc);
362252185f2SRick Chen }
363252185f2SRick Chen 
364bf9ba4dbSRick Chen const struct dm_mmc_ops dm_ftsdc010_mmc_ops = {
365252185f2SRick Chen 	.send_cmd	= ftsdc010_request,
366252185f2SRick Chen 	.set_ios	= ftsdc010_set_ios,
367252185f2SRick Chen 	.get_cd		= ftsdc010_get_cd,
368252185f2SRick Chen 	.get_wp		= ftsdc010_get_wp,
369252185f2SRick Chen };
370252185f2SRick Chen 
ftsdc_setup_cfg(struct mmc_config * cfg,const char * name,int buswidth,uint caps,u32 max_clk,u32 min_clk)371bf9ba4dbSRick Chen static void ftsdc_setup_cfg(struct mmc_config *cfg, const char *name, int buswidth,
372252185f2SRick Chen 		     uint caps, u32 max_clk, u32 min_clk)
373252185f2SRick Chen {
374252185f2SRick Chen 	cfg->name = name;
375252185f2SRick Chen 	cfg->f_min = min_clk;
376252185f2SRick Chen 	cfg->f_max = max_clk;
377252185f2SRick Chen 	cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
378252185f2SRick Chen 	cfg->host_caps = caps;
379252185f2SRick Chen 	if (buswidth == 8) {
380252185f2SRick Chen 		cfg->host_caps |= MMC_MODE_8BIT;
381252185f2SRick Chen 		cfg->host_caps &= ~MMC_MODE_4BIT;
382252185f2SRick Chen 	} else {
383252185f2SRick Chen 		cfg->host_caps |= MMC_MODE_4BIT;
384252185f2SRick Chen 		cfg->host_caps &= ~MMC_MODE_8BIT;
385252185f2SRick Chen 	}
386252185f2SRick Chen 	cfg->part_type = PART_TYPE_DOS;
387252185f2SRick Chen 	cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
388252185f2SRick Chen }
389252185f2SRick Chen 
ftsdc010_mmc_ofdata_to_platdata(struct udevice * dev)390bf9ba4dbSRick Chen static int ftsdc010_mmc_ofdata_to_platdata(struct udevice *dev)
391252185f2SRick Chen {
392bf9ba4dbSRick Chen #if !CONFIG_IS_ENABLED(OF_PLATDATA)
393bf9ba4dbSRick Chen 	struct ftsdc_priv *priv = dev_get_priv(dev);
394bf9ba4dbSRick Chen 	struct ftsdc010_chip *chip = &priv->chip;
395bf9ba4dbSRick Chen 	chip->name = dev->name;
396bf9ba4dbSRick Chen 	chip->ioaddr = (void *)devfdt_get_addr(dev);
397bf9ba4dbSRick Chen 	chip->buswidth = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
398bf9ba4dbSRick Chen 					"bus-width", 4);
399bf9ba4dbSRick Chen 	chip->priv = dev;
400bf9ba4dbSRick Chen 	priv->fifo_depth = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
401bf9ba4dbSRick Chen 				    "fifo-depth", 0);
402bf9ba4dbSRick Chen 	priv->fifo_mode = fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
403bf9ba4dbSRick Chen 					  "fifo-mode");
404bf9ba4dbSRick Chen 	if (fdtdec_get_int_array(gd->fdt_blob, dev_of_offset(dev),
405bf9ba4dbSRick Chen 			 "clock-freq-min-max", priv->minmax, 2)) {
406bf9ba4dbSRick Chen 		int val = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
407bf9ba4dbSRick Chen 				  "max-frequency", -EINVAL);
408bf9ba4dbSRick Chen 		if (val < 0)
409bf9ba4dbSRick Chen 			return val;
410bf9ba4dbSRick Chen 
411bf9ba4dbSRick Chen 		priv->minmax[0] = 400000;  /* 400 kHz */
412bf9ba4dbSRick Chen 		priv->minmax[1] = val;
413bf9ba4dbSRick Chen 	} else {
414bf9ba4dbSRick Chen 		debug("%s: 'clock-freq-min-max' property was deprecated.\n",
415bf9ba4dbSRick Chen 		__func__);
416252185f2SRick Chen 	}
417bf9ba4dbSRick Chen #endif
418bf9ba4dbSRick Chen 	chip->sclk = priv->minmax[1];
419bf9ba4dbSRick Chen 	chip->regs = chip->ioaddr;
420bf9ba4dbSRick Chen 	return 0;
421bf9ba4dbSRick Chen }
422bf9ba4dbSRick Chen 
ftsdc010_mmc_probe(struct udevice * dev)423bf9ba4dbSRick Chen static int ftsdc010_mmc_probe(struct udevice *dev)
424bf9ba4dbSRick Chen {
425bf9ba4dbSRick Chen 	struct ftsdc010_plat *plat = dev_get_platdata(dev);
426bf9ba4dbSRick Chen 	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
427bf9ba4dbSRick Chen 	struct ftsdc_priv *priv = dev_get_priv(dev);
428bf9ba4dbSRick Chen 	struct ftsdc010_chip *chip = &priv->chip;
429bf9ba4dbSRick Chen 	struct udevice *pwr_dev __maybe_unused;
430bf9ba4dbSRick Chen 
431bf9ba4dbSRick Chen #if CONFIG_IS_ENABLED(OF_PLATDATA)
432bf9ba4dbSRick Chen 	int ret;
433bf9ba4dbSRick Chen 	struct ftsdc010 *dtplat = &plat->dtplat;
434bf9ba4dbSRick Chen 	chip->name = dev->name;
435bf9ba4dbSRick Chen 	chip->ioaddr = map_sysmem(dtplat->reg[0], dtplat->reg[1]);
436bf9ba4dbSRick Chen 	chip->buswidth = dtplat->bus_width;
437bf9ba4dbSRick Chen 	chip->priv = dev;
438bf9ba4dbSRick Chen 	chip->dev_index = 1;
439bf9ba4dbSRick Chen 	memcpy(priv->minmax, dtplat->clock_freq_min_max, sizeof(priv->minmax));
440bf9ba4dbSRick Chen 	ret = clk_get_by_index_platdata(dev, 0, dtplat->clocks, &priv->clk);
441bf9ba4dbSRick Chen 	if (ret < 0)
442bf9ba4dbSRick Chen 		return ret;
443bf9ba4dbSRick Chen #endif
444bf9ba4dbSRick Chen 
445bf9ba4dbSRick Chen 	if (dev_read_bool(dev, "cap-mmc-highspeed") || \
446bf9ba4dbSRick Chen 		  dev_read_bool(dev, "cap-sd-highspeed"))
447bf9ba4dbSRick Chen 		chip->caps |= MMC_MODE_HS | MMC_MODE_HS_52MHz;
448bf9ba4dbSRick Chen 
449bf9ba4dbSRick Chen 	ftsdc_setup_cfg(&plat->cfg, dev->name, chip->buswidth, chip->caps,
450bf9ba4dbSRick Chen 			priv->minmax[1] , priv->minmax[0]);
451bf9ba4dbSRick Chen 	chip->mmc = &plat->mmc;
452bf9ba4dbSRick Chen 	chip->mmc->priv = &priv->chip;
453bf9ba4dbSRick Chen 	chip->mmc->dev = dev;
454bf9ba4dbSRick Chen 	upriv->mmc = chip->mmc;
455bf9ba4dbSRick Chen 	return ftsdc010_probe(dev);
456bf9ba4dbSRick Chen }
457bf9ba4dbSRick Chen 
ftsdc010_mmc_bind(struct udevice * dev)458bf9ba4dbSRick Chen int ftsdc010_mmc_bind(struct udevice *dev)
459bf9ba4dbSRick Chen {
460bf9ba4dbSRick Chen 	struct ftsdc010_plat *plat = dev_get_platdata(dev);
461bf9ba4dbSRick Chen 
462bf9ba4dbSRick Chen 	return mmc_bind(dev, &plat->mmc, &plat->cfg);
463bf9ba4dbSRick Chen }
464bf9ba4dbSRick Chen 
465bf9ba4dbSRick Chen static const struct udevice_id ftsdc010_mmc_ids[] = {
466*cf3922ddSRick Chen 	{ .compatible = "andestech,atfsdc010" },
467bf9ba4dbSRick Chen 	{ }
468bf9ba4dbSRick Chen };
469bf9ba4dbSRick Chen 
470bf9ba4dbSRick Chen U_BOOT_DRIVER(ftsdc010_mmc) = {
471bf9ba4dbSRick Chen 	.name		= "ftsdc010_mmc",
472bf9ba4dbSRick Chen 	.id		= UCLASS_MMC,
473bf9ba4dbSRick Chen 	.of_match	= ftsdc010_mmc_ids,
474bf9ba4dbSRick Chen 	.ofdata_to_platdata = ftsdc010_mmc_ofdata_to_platdata,
475bf9ba4dbSRick Chen 	.ops		= &dm_ftsdc010_mmc_ops,
476bf9ba4dbSRick Chen 	.bind		= ftsdc010_mmc_bind,
477bf9ba4dbSRick Chen 	.probe		= ftsdc010_mmc_probe,
478bf9ba4dbSRick Chen 	.priv_auto_alloc_size = sizeof(struct ftsdc_priv),
479bf9ba4dbSRick Chen 	.platdata_auto_alloc_size = sizeof(struct ftsdc010_plat),
480bf9ba4dbSRick Chen };
481