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