1 /*
2 * Copyright (c) 2006-2018, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2020-04-15 Jonne first version for s3c2440 mmc controller
9 */
10 #include <rthw.h>
11 #include <rtthread.h>
12 #include <rtdevice.h>
13 #include <drivers/dev_mmcsd_core.h>
14 #include <s3c24x0.h>
15
16 #define S3C_PCLK 50000000
17
18
s3c_mmc_set_clk(struct rt_mmcsd_host * host,rt_uint32_t clock)19 static void s3c_mmc_set_clk(struct rt_mmcsd_host *host, rt_uint32_t clock)
20 {
21 rt_uint32_t prescale;
22 rt_uint32_t realClk;
23
24 for(prescale = 0; prescale < 256; ++prescale)
25 {
26 realClk = S3C_PCLK / (1 + prescale);
27 if(realClk <= clock)
28 {
29 break;
30 }
31 }
32
33 SDIPRE = prescale;
34 host->io_cfg.clock = realClk;
35 }
36
s3c_mmc_send_cmd(struct rt_mmcsd_host * host,struct rt_mmcsd_cmd * cmd)37 static rt_uint32_t s3c_mmc_send_cmd(struct rt_mmcsd_host *host, struct rt_mmcsd_cmd *cmd)
38 {
39 rt_uint32_t ccon;
40 rt_uint32_t cmdSta;
41
42 SDICARG = cmd->arg;
43
44 ccon = cmd->cmd_code & 0x3f;
45 ccon |= (0 << 7) | (1 << 6); /* two start bits*/
46 ccon |= (1 << 8);/* command start*/
47
48 if(cmd->flags & 0xF)
49 {
50 // Need response
51 ccon |= (1 << 9);
52 }
53
54 if((cmd->flags & 0xF) == RESP_R2)
55 {
56 // R2 need 136bit response
57 ccon |= (1 << 10);
58 }
59
60 SDICCON = ccon; /* start cmd */
61
62 if(cmd->flags & 0xF)
63 {
64 cmdSta = SDICSTA;
65 while((cmdSta & 0x200) != 0x200 && (cmdSta & 0x400) != 0x400)
66 {
67 cmdSta = SDICSTA;
68 }
69
70 if((cmdSta & 0x1000) == 0x1000 && (cmd->flags & 0xF) != RESP_R3 && (cmd->flags & 0xF) != RESP_R4)
71 {
72 // crc error, but R3 R4 ignore it
73 SDICSTA = cmdSta;
74 return -RT_ERROR;
75 }
76
77 if((cmdSta & 0xF00) != 0xa00)
78 {
79 SDICSTA = cmdSta;
80 return -RT_ERROR;
81 }
82
83 cmd->resp[0] = SDIRSP0;
84 if((cmd->flags & 0xF) == RESP_R2)
85 {
86 cmd->resp[1] = SDIRSP1;
87 cmd->resp[2] = SDIRSP2;
88 cmd->resp[3] = SDIRSP3;
89 }
90 }
91 else
92 {
93 cmdSta = SDICSTA;
94 while((cmdSta & 0x800) != 0x800)
95 {
96 cmdSta = SDICSTA;
97 }
98 }
99
100 SDICSTA = cmdSta; // clear current status
101
102 return RT_EOK;
103
104 }
105
s3c_mmc_xfer_data(struct rt_mmcsd_data * data)106 static rt_uint32_t s3c_mmc_xfer_data(struct rt_mmcsd_data *data)
107 {
108 rt_uint32_t status;
109 rt_uint32_t xfer_size;
110 rt_uint32_t handled_size = 0;
111 rt_uint32_t *pBuf = RT_NULL;
112
113
114 if(data == RT_NULL)
115 {
116 return -RT_ERROR;
117 }
118
119 xfer_size = data->blks * data->blksize;
120
121 pBuf = data->buf;
122 if(data->flags & DATA_DIR_READ)
123 {
124 while(handled_size < xfer_size)
125 {
126 if ((SDIDSTA & 0x20) == 0x20)
127 {
128 SDIDSTA = (0x1 << 0x5);
129 break;
130 }
131
132 status = SDIFSTA;
133 if ((status & 0x1000) == 0x1000)
134 {
135 *pBuf++ = SDIDAT;
136 handled_size += 4;
137 }
138 }
139 }
140 else
141 {
142 while(handled_size < xfer_size)
143 {
144 status = SDIFSTA;
145 if ((status & 0x2000) == 0x2000)
146 {
147 SDIDAT = *pBuf++;
148 handled_size += 4;
149 }
150 }
151 }
152
153 // wait for end
154 status = SDIDSTA;
155 while((status & 0x30) == 0)
156 {
157 status = SDIDSTA;
158 }
159 SDIDSTA = status;
160
161 if ((status & 0xfc) != 0x10)
162 {
163 return -RT_ERROR;
164 }
165
166 SDIDCON = SDIDCON & ~(7<<12);
167 SDIFSTA = SDIFSTA & 0x200;
168 SDIDSTA = 0x10;
169
170 return RT_EOK;
171 }
mmc_request(struct rt_mmcsd_host * host,struct rt_mmcsd_req * req)172 static void mmc_request(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req)
173 {
174 rt_uint32_t ret;
175 struct rt_mmcsd_cmd *cmd;
176 struct rt_mmcsd_data *data;
177 rt_uint32_t val;
178 rt_uint32_t tryCnt = 0;
179
180 if(req->cmd == RT_NULL)
181 {
182 goto out;
183 }
184 cmd = req->cmd;
185
186 /* prepare for data transfer*/
187 if(req->data != RT_NULL)
188 {
189 SDIFSTA = SDIFSTA | (1<<16); // reset fifo
190
191 while(SDIDSTA & 0x03)
192 {
193 if(tryCnt++ > 500)
194 {
195 break;
196 SDIDSTA = SDIDSTA;
197 }
198 }
199
200 data = req->data;
201
202 if((data->blksize & 0x3) != 0)
203 {
204 goto out;
205 }
206
207 val = (2 << 22) //word transfer
208 | (1 << 20) // transmet after response
209 | (1 << 19) // reciveve after command sent
210 | (1 << 17) // block data transfer
211 | (1 << 14); // data start
212
213 if(host->io_cfg.bus_width == MMCSD_BUS_WIDTH_4)
214 {
215 val |= (1 << 16); // wide bus mode(4bit data)
216 }
217
218 if(data->flags & DATA_DIR_READ)
219 {
220 // for data read
221 val |= (2 << 12);
222 }
223 else
224 {
225 val |= (3 << 12);
226 }
227
228 val |= (data->blks & 0xFFF);
229
230 SDIDCON = val;
231
232 SDIBSIZE = data->blksize;
233 SDIDTIMER = 0x7fffff;
234 }
235
236 ret = s3c_mmc_send_cmd(host,req->cmd);
237 if(ret != RT_EOK) {
238 cmd->err = ret;
239 goto out;
240 }
241
242 if(req->data != RT_NULL)
243 {
244 /*do transfer data*/
245 ret = s3c_mmc_xfer_data(data);
246 if(ret != RT_EOK)
247 {
248 data->err = ret;
249 goto out;
250 }
251 }
252
253 out:
254 mmcsd_req_complete(host);
255 }
mmc_set_iocfg(struct rt_mmcsd_host * host,struct rt_mmcsd_io_cfg * io_cfg)256 static void mmc_set_iocfg(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *io_cfg)
257 {
258 switch (io_cfg->power_mode) {
259 case MMCSD_POWER_ON:
260 case MMCSD_POWER_UP:
261 /* Enable PCLK into SDI Block */
262 CLKCON |= 1 << 9;
263
264 /* Setup GPIO as SD and SDCMD, SDDAT[3:0] Pull up En */
265 GPEUP = GPEUP & (~(0x3f << 5)) | (0x01 << 5);
266 GPECON = GPECON & (~(0xfff << 10)) | (0xaaa << 10);
267 break;
268
269 case MMCSD_POWER_OFF:
270 default:
271 break;
272 }
273
274 s3c_mmc_set_clk(host, io_cfg->clock);
275
276 SDICON = 1;
277 }
278
mmc_get_card_status(struct rt_mmcsd_host * host)279 static rt_int32_t mmc_get_card_status(struct rt_mmcsd_host *host)
280 {
281 return RT_EOK;
282 }
mmc_enable_sdio_irq(struct rt_mmcsd_host * host,rt_int32_t en)283 static void mmc_enable_sdio_irq(struct rt_mmcsd_host *host, rt_int32_t en)
284 {
285 }
286
287 static const struct rt_mmcsd_host_ops ops =
288 {
289 mmc_request,
290 mmc_set_iocfg,
291 mmc_get_card_status,
292 mmc_enable_sdio_irq
293 };
294
s3c_sdio_init(void)295 int s3c_sdio_init(void)
296 {
297 struct rt_mmcsd_host * host = RT_NULL;
298
299
300 host = mmcsd_alloc_host();
301 if (!host)
302 {
303 goto err;
304 }
305
306 host->ops = &ops;
307 host->freq_min = 300000;
308 host->freq_max = 50000000;
309 host->valid_ocr = VDD_32_33 | VDD_33_34;
310 host->flags = MMCSD_MUTBLKWRITE | MMCSD_SUP_HIGHSPEED | MMCSD_SUP_SDIO_IRQ | MMCSD_BUSWIDTH_4;
311 host->max_seg_size = 2048;
312 host->max_dma_segs = 10;
313 host->max_blk_size = 512;
314 host->max_blk_count = 4096;
315
316
317 mmcsd_change(host);
318
319 return RT_EOK;
320
321 err:
322 if(host) rt_free(host);
323
324 return -RT_EIO;
325 }
326
327 INIT_DEVICE_EXPORT(s3c_sdio_init);
328