Commit dae0f5c644c0f76e67306bd49c09d95373b7357a
Committed by
Pantelis Antoniou
1 parent
7eca6b6327
Exists in
v2017.01-smarct4x
and in
37 other branches
mmc: s3c: Add SD driver
Implement SD driver for the S3C24xx family. This implementation is currently only capable of using the PIO transfers, DMA is not supported. Signed-off-by: Marek Vasut <marex@denx.de> Cc: Kyungmin Park <kyungmin.park@samsung.com> Cc: Lukasz Majewski <l.majewski@samsung.com> Cc: Minkyu Kang <mk7.kang@samsung.com> Cc: Pantelis Antoniou <panto@antoniou-consulting.com> Cc: Vladimir Zapolskiy <vz@mleia.com> Acked-by: Pantelis Antoniou <panto@antoniou-consulting.com>
Showing 3 changed files with 328 additions and 0 deletions Side-by-side Diff
arch/arm/include/asm/arch-s3c24x0/s3c24x0.h
drivers/mmc/Makefile
... | ... | @@ -22,6 +22,7 @@ |
22 | 22 | obj-$(CONFIG_SDHCI) += sdhci.o |
23 | 23 | obj-$(CONFIG_BCM2835_SDHCI) += bcm2835_sdhci.o |
24 | 24 | obj-$(CONFIG_KONA_SDHCI) += kona_sdhci.o |
25 | +obj-$(CONFIG_S3C_SDI) += s3c_sdi.o | |
25 | 26 | obj-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o |
26 | 27 | obj-$(CONFIG_SH_MMCIF) += sh_mmcif.o |
27 | 28 | obj-$(CONFIG_SPEAR_SDHCI) += spear_sdhci.o |
drivers/mmc/s3c_sdi.c
1 | +/* | |
2 | + * S3C24xx SD/MMC driver | |
3 | + * | |
4 | + * Based on OpenMoko S3C24xx driver by Harald Welte <laforge@openmoko.org> | |
5 | + * | |
6 | + * Copyright (C) 2014 Marek Vasut <marex@denx.de> | |
7 | + * | |
8 | + * SPDX-License-Identifier: GPL-2.0+ | |
9 | + */ | |
10 | + | |
11 | +#include <common.h> | |
12 | +#include <malloc.h> | |
13 | +#include <mmc.h> | |
14 | +#include <errno.h> | |
15 | +#include <asm/arch/s3c24x0_cpu.h> | |
16 | +#include <asm/io.h> | |
17 | +#include <asm/unaligned.h> | |
18 | + | |
19 | +#define S3C2440_SDICON_SDRESET (1 << 8) | |
20 | +#define S3C2410_SDICON_FIFORESET (1 << 1) | |
21 | +#define S3C2410_SDICON_CLOCKTYPE (1 << 0) | |
22 | + | |
23 | +#define S3C2410_SDICMDCON_LONGRSP (1 << 10) | |
24 | +#define S3C2410_SDICMDCON_WAITRSP (1 << 9) | |
25 | +#define S3C2410_SDICMDCON_CMDSTART (1 << 8) | |
26 | +#define S3C2410_SDICMDCON_SENDERHOST (1 << 6) | |
27 | +#define S3C2410_SDICMDCON_INDEX 0x3f | |
28 | + | |
29 | +#define S3C2410_SDICMDSTAT_CRCFAIL (1 << 12) | |
30 | +#define S3C2410_SDICMDSTAT_CMDSENT (1 << 11) | |
31 | +#define S3C2410_SDICMDSTAT_CMDTIMEOUT (1 << 10) | |
32 | +#define S3C2410_SDICMDSTAT_RSPFIN (1 << 9) | |
33 | + | |
34 | +#define S3C2440_SDIDCON_DS_WORD (2 << 22) | |
35 | +#define S3C2410_SDIDCON_TXAFTERRESP (1 << 20) | |
36 | +#define S3C2410_SDIDCON_RXAFTERCMD (1 << 19) | |
37 | +#define S3C2410_SDIDCON_BLOCKMODE (1 << 17) | |
38 | +#define S3C2410_SDIDCON_WIDEBUS (1 << 16) | |
39 | +#define S3C2440_SDIDCON_DATSTART (1 << 14) | |
40 | +#define S3C2410_SDIDCON_XFER_RXSTART (2 << 12) | |
41 | +#define S3C2410_SDIDCON_XFER_TXSTART (3 << 12) | |
42 | +#define S3C2410_SDIDCON_BLKNUM 0x7ff | |
43 | + | |
44 | +#define S3C2410_SDIDSTA_FIFOFAIL (1 << 8) | |
45 | +#define S3C2410_SDIDSTA_CRCFAIL (1 << 7) | |
46 | +#define S3C2410_SDIDSTA_RXCRCFAIL (1 << 6) | |
47 | +#define S3C2410_SDIDSTA_DATATIMEOUT (1 << 5) | |
48 | +#define S3C2410_SDIDSTA_XFERFINISH (1 << 4) | |
49 | + | |
50 | +#define S3C2410_SDIFSTA_TFHALF (1 << 11) | |
51 | +#define S3C2410_SDIFSTA_COUNTMASK 0x7f | |
52 | + | |
53 | +/* | |
54 | + * WARNING: We only support one SD IP block. | |
55 | + * NOTE: It's not likely there will ever exist an S3C24xx with two, | |
56 | + * at least not in this universe all right. | |
57 | + */ | |
58 | +static int wide_bus; | |
59 | + | |
60 | +static int | |
61 | +s3cmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) | |
62 | +{ | |
63 | + struct s3c24x0_sdi *sdi_regs = s3c24x0_get_base_sdi(); | |
64 | + uint32_t sdiccon, sdicsta, sdidcon, sdidsta, sdidat, sdifsta; | |
65 | + uint32_t sdicsta_wait_bit = S3C2410_SDICMDSTAT_CMDSENT; | |
66 | + unsigned int timeout = 100000; | |
67 | + int ret = 0, xfer_len, data_offset = 0; | |
68 | + const uint32_t sdidsta_err_mask = S3C2410_SDIDSTA_FIFOFAIL | | |
69 | + S3C2410_SDIDSTA_CRCFAIL | S3C2410_SDIDSTA_RXCRCFAIL | | |
70 | + S3C2410_SDIDSTA_DATATIMEOUT; | |
71 | + | |
72 | + | |
73 | + writel(0xffffffff, &sdi_regs->sdicsta); | |
74 | + writel(0xffffffff, &sdi_regs->sdidsta); | |
75 | + writel(0xffffffff, &sdi_regs->sdifsta); | |
76 | + | |
77 | + /* Set up data transfer (if applicable). */ | |
78 | + if (data) { | |
79 | + writel(data->blocksize, &sdi_regs->sdibsize); | |
80 | + | |
81 | + sdidcon = data->blocks & S3C2410_SDIDCON_BLKNUM; | |
82 | + sdidcon |= S3C2410_SDIDCON_BLOCKMODE; | |
83 | +#if defined(CONFIG_S3C2440) | |
84 | + sdidcon |= S3C2440_SDIDCON_DS_WORD | S3C2440_SDIDCON_DATSTART; | |
85 | +#endif | |
86 | + if (wide_bus) | |
87 | + sdidcon |= S3C2410_SDIDCON_WIDEBUS; | |
88 | + | |
89 | + if (data->flags & MMC_DATA_READ) { | |
90 | + sdidcon |= S3C2410_SDIDCON_RXAFTERCMD; | |
91 | + sdidcon |= S3C2410_SDIDCON_XFER_RXSTART; | |
92 | + } else { | |
93 | + sdidcon |= S3C2410_SDIDCON_TXAFTERRESP; | |
94 | + sdidcon |= S3C2410_SDIDCON_XFER_TXSTART; | |
95 | + } | |
96 | + | |
97 | + writel(sdidcon, &sdi_regs->sdidcon); | |
98 | + } | |
99 | + | |
100 | + /* Write CMD arg. */ | |
101 | + writel(cmd->cmdarg, &sdi_regs->sdicarg); | |
102 | + | |
103 | + /* Write CMD index. */ | |
104 | + sdiccon = cmd->cmdidx & S3C2410_SDICMDCON_INDEX; | |
105 | + sdiccon |= S3C2410_SDICMDCON_SENDERHOST; | |
106 | + sdiccon |= S3C2410_SDICMDCON_CMDSTART; | |
107 | + | |
108 | + /* Command with short response. */ | |
109 | + if (cmd->resp_type & MMC_RSP_PRESENT) { | |
110 | + sdiccon |= S3C2410_SDICMDCON_WAITRSP; | |
111 | + sdicsta_wait_bit = S3C2410_SDICMDSTAT_RSPFIN; | |
112 | + } | |
113 | + | |
114 | + /* Command with long response. */ | |
115 | + if (cmd->resp_type & MMC_RSP_136) | |
116 | + sdiccon |= S3C2410_SDICMDCON_LONGRSP; | |
117 | + | |
118 | + /* Start the command. */ | |
119 | + writel(sdiccon, &sdi_regs->sdiccon); | |
120 | + | |
121 | + /* Wait for the command to complete or for response. */ | |
122 | + for (timeout = 100000; timeout; timeout--) { | |
123 | + sdicsta = readl(&sdi_regs->sdicsta); | |
124 | + if (sdicsta & sdicsta_wait_bit) | |
125 | + break; | |
126 | + | |
127 | + if (sdicsta & S3C2410_SDICMDSTAT_CMDTIMEOUT) | |
128 | + timeout = 1; | |
129 | + } | |
130 | + | |
131 | + /* Clean the status bits. */ | |
132 | + setbits_le32(&sdi_regs->sdicsta, 0xf << 9); | |
133 | + | |
134 | + if (!timeout) { | |
135 | + puts("S3C SDI: Command timed out!\n"); | |
136 | + ret = TIMEOUT; | |
137 | + goto error; | |
138 | + } | |
139 | + | |
140 | + /* Read out the response. */ | |
141 | + if (cmd->resp_type & MMC_RSP_136) { | |
142 | + cmd->response[0] = readl(&sdi_regs->sdirsp0); | |
143 | + cmd->response[1] = readl(&sdi_regs->sdirsp1); | |
144 | + cmd->response[2] = readl(&sdi_regs->sdirsp2); | |
145 | + cmd->response[3] = readl(&sdi_regs->sdirsp3); | |
146 | + } else { | |
147 | + cmd->response[0] = readl(&sdi_regs->sdirsp0); | |
148 | + } | |
149 | + | |
150 | + /* If there are no data, we're done. */ | |
151 | + if (!data) | |
152 | + return 0; | |
153 | + | |
154 | + xfer_len = data->blocksize * data->blocks; | |
155 | + | |
156 | + while (xfer_len > 0) { | |
157 | + sdidsta = readl(&sdi_regs->sdidsta); | |
158 | + sdifsta = readl(&sdi_regs->sdifsta); | |
159 | + | |
160 | + if (sdidsta & sdidsta_err_mask) { | |
161 | + printf("S3C SDI: Data error (sdta=0x%08x)\n", sdidsta); | |
162 | + ret = -EIO; | |
163 | + goto error; | |
164 | + } | |
165 | + | |
166 | + if (data->flags & MMC_DATA_READ) { | |
167 | + if ((sdifsta & S3C2410_SDIFSTA_COUNTMASK) < 4) | |
168 | + continue; | |
169 | + sdidat = readl(&sdi_regs->sdidat); | |
170 | + put_unaligned_le32(sdidat, data->dest + data_offset); | |
171 | + } else { /* Write */ | |
172 | + /* TX FIFO half full. */ | |
173 | + if (!(sdifsta & S3C2410_SDIFSTA_TFHALF)) | |
174 | + continue; | |
175 | + | |
176 | + /* TX FIFO is below 32b full, write. */ | |
177 | + sdidat = get_unaligned_le32(data->src + data_offset); | |
178 | + writel(sdidat, &sdi_regs->sdidat); | |
179 | + } | |
180 | + data_offset += 4; | |
181 | + xfer_len -= 4; | |
182 | + } | |
183 | + | |
184 | + /* Wait for the command to complete or for response. */ | |
185 | + for (timeout = 100000; timeout; timeout--) { | |
186 | + sdidsta = readl(&sdi_regs->sdidsta); | |
187 | + if (sdidsta & S3C2410_SDIDSTA_XFERFINISH) | |
188 | + break; | |
189 | + | |
190 | + if (sdidsta & S3C2410_SDIDSTA_DATATIMEOUT) | |
191 | + timeout = 1; | |
192 | + } | |
193 | + | |
194 | + /* Clear status bits. */ | |
195 | + writel(0x6f8, &sdi_regs->sdidsta); | |
196 | + | |
197 | + if (!timeout) { | |
198 | + puts("S3C SDI: Command timed out!\n"); | |
199 | + ret = TIMEOUT; | |
200 | + goto error; | |
201 | + } | |
202 | + | |
203 | + writel(0, &sdi_regs->sdidcon); | |
204 | + | |
205 | + return 0; | |
206 | +error: | |
207 | + return ret; | |
208 | +} | |
209 | + | |
210 | +static void s3cmmc_set_ios(struct mmc *mmc) | |
211 | +{ | |
212 | + struct s3c24x0_sdi *sdi_regs = s3c24x0_get_base_sdi(); | |
213 | + uint32_t divider = 0; | |
214 | + | |
215 | + wide_bus = (mmc->bus_width == 4); | |
216 | + | |
217 | + if (!mmc->clock) | |
218 | + return; | |
219 | + | |
220 | + divider = DIV_ROUND_UP(get_PCLK(), mmc->clock); | |
221 | + if (divider) | |
222 | + divider--; | |
223 | + | |
224 | + writel(divider, &sdi_regs->sdipre); | |
225 | + mdelay(125); | |
226 | +} | |
227 | + | |
228 | +static int s3cmmc_init(struct mmc *mmc) | |
229 | +{ | |
230 | + struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power(); | |
231 | + struct s3c24x0_sdi *sdi_regs = s3c24x0_get_base_sdi(); | |
232 | + | |
233 | + /* Start the clock. */ | |
234 | + setbits_le32(&clk_power->clkcon, 1 << 9); | |
235 | + | |
236 | +#if defined(CONFIG_S3C2440) | |
237 | + writel(S3C2440_SDICON_SDRESET, &sdi_regs->sdicon); | |
238 | + mdelay(10); | |
239 | + writel(0x7fffff, &sdi_regs->sdidtimer); | |
240 | +#else | |
241 | + writel(0xffff, &sdi_regs->sdidtimer); | |
242 | +#endif | |
243 | + writel(MMC_MAX_BLOCK_LEN, &sdi_regs->sdibsize); | |
244 | + writel(0x0, &sdi_regs->sdiimsk); | |
245 | + | |
246 | + writel(S3C2410_SDICON_FIFORESET | S3C2410_SDICON_CLOCKTYPE, | |
247 | + &sdi_regs->sdicon); | |
248 | + | |
249 | + mdelay(125); | |
250 | + | |
251 | + return 0; | |
252 | +} | |
253 | + | |
254 | +struct s3cmmc_priv { | |
255 | + struct mmc_config cfg; | |
256 | + int (*getcd)(struct mmc *); | |
257 | + int (*getwp)(struct mmc *); | |
258 | +}; | |
259 | + | |
260 | +static int s3cmmc_getcd(struct mmc *mmc) | |
261 | +{ | |
262 | + struct s3cmmc_priv *priv = mmc->priv; | |
263 | + if (priv->getcd) | |
264 | + return priv->getcd(mmc); | |
265 | + else | |
266 | + return 0; | |
267 | +} | |
268 | + | |
269 | +static int s3cmmc_getwp(struct mmc *mmc) | |
270 | +{ | |
271 | + struct s3cmmc_priv *priv = mmc->priv; | |
272 | + if (priv->getwp) | |
273 | + return priv->getwp(mmc); | |
274 | + else | |
275 | + return 0; | |
276 | +} | |
277 | + | |
278 | +static const struct mmc_ops s3cmmc_ops = { | |
279 | + .send_cmd = s3cmmc_send_cmd, | |
280 | + .set_ios = s3cmmc_set_ios, | |
281 | + .init = s3cmmc_init, | |
282 | + .getcd = s3cmmc_getcd, | |
283 | + .getwp = s3cmmc_getwp, | |
284 | +}; | |
285 | + | |
286 | +int s3cmmc_initialize(bd_t *bis, int (*getcd)(struct mmc *), | |
287 | + int (*getwp)(struct mmc *)) | |
288 | +{ | |
289 | + struct s3cmmc_priv *priv; | |
290 | + struct mmc *mmc; | |
291 | + struct mmc_config *cfg; | |
292 | + | |
293 | + priv = calloc(1, sizeof(*priv)); | |
294 | + if (!priv) | |
295 | + return -ENOMEM; | |
296 | + cfg = &priv->cfg; | |
297 | + | |
298 | + cfg->name = "S3C MMC"; | |
299 | + cfg->ops = &s3cmmc_ops; | |
300 | + cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; | |
301 | + cfg->host_caps = MMC_MODE_4BIT | MMC_MODE_HC | MMC_MODE_HS; | |
302 | + cfg->f_min = 400000; | |
303 | + cfg->f_max = get_PCLK() / 2; | |
304 | + cfg->b_max = 0x80; | |
305 | + | |
306 | +#if defined(CONFIG_S3C2410) | |
307 | + /* | |
308 | + * S3C2410 has some bug that prevents reliable | |
309 | + * operation at higher speed | |
310 | + */ | |
311 | + cfg->f_max /= 2; | |
312 | +#endif | |
313 | + | |
314 | + mmc = mmc_create(cfg, priv); | |
315 | + if (!mmc) { | |
316 | + free(priv); | |
317 | + return -ENOMEM; | |
318 | + } | |
319 | + | |
320 | + return 0; | |
321 | +} |