2 * S3C24xx SD/MMC driver
4 * Based on OpenMoko S3C24xx driver by Harald Welte <laforge@openmoko.org>
6 * Copyright (C) 2014 Marek Vasut <marex@denx.de>
8 * SPDX-License-Identifier: GPL-2.0+
15 #include <asm/arch/s3c24x0_cpu.h>
17 #include <asm/unaligned.h>
19 #define S3C2440_SDICON_SDRESET (1 << 8)
20 #define S3C2410_SDICON_FIFORESET (1 << 1)
21 #define S3C2410_SDICON_CLOCKTYPE (1 << 0)
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
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)
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
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)
50 #define S3C2410_SDIFSTA_TFHALF (1 << 11)
51 #define S3C2410_SDIFSTA_COUNTMASK 0x7f
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.
61 s3cmmc_send_cmd(struct mmc
*mmc
, struct mmc_cmd
*cmd
, struct mmc_data
*data
)
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
;
73 writel(0xffffffff, &sdi_regs
->sdicsta
);
74 writel(0xffffffff, &sdi_regs
->sdidsta
);
75 writel(0xffffffff, &sdi_regs
->sdifsta
);
77 /* Set up data transfer (if applicable). */
79 writel(data
->blocksize
, &sdi_regs
->sdibsize
);
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
;
87 sdidcon
|= S3C2410_SDIDCON_WIDEBUS
;
89 if (data
->flags
& MMC_DATA_READ
) {
90 sdidcon
|= S3C2410_SDIDCON_RXAFTERCMD
;
91 sdidcon
|= S3C2410_SDIDCON_XFER_RXSTART
;
93 sdidcon
|= S3C2410_SDIDCON_TXAFTERRESP
;
94 sdidcon
|= S3C2410_SDIDCON_XFER_TXSTART
;
97 writel(sdidcon
, &sdi_regs
->sdidcon
);
101 writel(cmd
->cmdarg
, &sdi_regs
->sdicarg
);
103 /* Write CMD index. */
104 sdiccon
= cmd
->cmdidx
& S3C2410_SDICMDCON_INDEX
;
105 sdiccon
|= S3C2410_SDICMDCON_SENDERHOST
;
106 sdiccon
|= S3C2410_SDICMDCON_CMDSTART
;
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
;
114 /* Command with long response. */
115 if (cmd
->resp_type
& MMC_RSP_136
)
116 sdiccon
|= S3C2410_SDICMDCON_LONGRSP
;
118 /* Start the command. */
119 writel(sdiccon
, &sdi_regs
->sdiccon
);
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
)
127 if (sdicsta
& S3C2410_SDICMDSTAT_CMDTIMEOUT
)
131 /* Clean the status bits. */
132 setbits_le32(&sdi_regs
->sdicsta
, 0xf << 9);
135 puts("S3C SDI: Command timed out!\n");
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
);
147 cmd
->response
[0] = readl(&sdi_regs
->sdirsp0
);
150 /* If there are no data, we're done. */
154 xfer_len
= data
->blocksize
* data
->blocks
;
156 while (xfer_len
> 0) {
157 sdidsta
= readl(&sdi_regs
->sdidsta
);
158 sdifsta
= readl(&sdi_regs
->sdifsta
);
160 if (sdidsta
& sdidsta_err_mask
) {
161 printf("S3C SDI: Data error (sdta=0x%08x)\n", sdidsta
);
166 if (data
->flags
& MMC_DATA_READ
) {
167 if ((sdifsta
& S3C2410_SDIFSTA_COUNTMASK
) < 4)
169 sdidat
= readl(&sdi_regs
->sdidat
);
170 put_unaligned_le32(sdidat
, data
->dest
+ data_offset
);
172 /* TX FIFO half full. */
173 if (!(sdifsta
& S3C2410_SDIFSTA_TFHALF
))
176 /* TX FIFO is below 32b full, write. */
177 sdidat
= get_unaligned_le32(data
->src
+ data_offset
);
178 writel(sdidat
, &sdi_regs
->sdidat
);
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
)
190 if (sdidsta
& S3C2410_SDIDSTA_DATATIMEOUT
)
194 /* Clear status bits. */
195 writel(0x6f8, &sdi_regs
->sdidsta
);
198 puts("S3C SDI: Command timed out!\n");
203 writel(0, &sdi_regs
->sdidcon
);
210 static void s3cmmc_set_ios(struct mmc
*mmc
)
212 struct s3c24x0_sdi
*sdi_regs
= s3c24x0_get_base_sdi();
213 uint32_t divider
= 0;
215 wide_bus
= (mmc
->bus_width
== 4);
220 divider
= DIV_ROUND_UP(get_PCLK(), mmc
->clock
);
224 writel(divider
, &sdi_regs
->sdipre
);
228 static int s3cmmc_init(struct mmc
*mmc
)
230 struct s3c24x0_clock_power
*clk_power
= s3c24x0_get_base_clock_power();
231 struct s3c24x0_sdi
*sdi_regs
= s3c24x0_get_base_sdi();
233 /* Start the clock. */
234 setbits_le32(&clk_power
->clkcon
, 1 << 9);
236 #if defined(CONFIG_S3C2440)
237 writel(S3C2440_SDICON_SDRESET
, &sdi_regs
->sdicon
);
239 writel(0x7fffff, &sdi_regs
->sdidtimer
);
241 writel(0xffff, &sdi_regs
->sdidtimer
);
243 writel(MMC_MAX_BLOCK_LEN
, &sdi_regs
->sdibsize
);
244 writel(0x0, &sdi_regs
->sdiimsk
);
246 writel(S3C2410_SDICON_FIFORESET
| S3C2410_SDICON_CLOCKTYPE
,
255 struct mmc_config cfg
;
256 int (*getcd
)(struct mmc
*);
257 int (*getwp
)(struct mmc
*);
260 static int s3cmmc_getcd(struct mmc
*mmc
)
262 struct s3cmmc_priv
*priv
= mmc
->priv
;
264 return priv
->getcd(mmc
);
269 static int s3cmmc_getwp(struct mmc
*mmc
)
271 struct s3cmmc_priv
*priv
= mmc
->priv
;
273 return priv
->getwp(mmc
);
278 static const struct mmc_ops s3cmmc_ops
= {
279 .send_cmd
= s3cmmc_send_cmd
,
280 .set_ios
= s3cmmc_set_ios
,
282 .getcd
= s3cmmc_getcd
,
283 .getwp
= s3cmmc_getwp
,
286 int s3cmmc_initialize(bd_t
*bis
, int (*getcd
)(struct mmc
*),
287 int (*getwp
)(struct mmc
*))
289 struct s3cmmc_priv
*priv
;
291 struct mmc_config
*cfg
;
293 priv
= calloc(1, sizeof(*priv
));
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_HS
;
303 cfg
->f_max
= get_PCLK() / 2;
306 #if defined(CONFIG_S3C2410)
308 * S3C2410 has some bug that prevents reliable
309 * operation at higher speed
314 mmc
= mmc_create(cfg
, priv
);