1 /*
2  * Copyright (c) 2006-2021, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2017-08-08     Yang        the first version
9  */
10 
11 #include <string.h>
12 #include <board.h>
13 
14 #include "drv_sd.h"
15 
16 
17 static struct mci_device *_mci_device;
18 static uint8_t sdio_buffer[1024];
19 
rt_mci_init(rt_device_t dev)20 static rt_err_t rt_mci_init(rt_device_t dev)
21 {
22     rt_err_t result = RT_EOK;
23 
24     return result;
25 }
26 
rt_mci_open(rt_device_t dev,rt_uint16_t oflag)27 static rt_err_t rt_mci_open(rt_device_t dev, rt_uint16_t oflag)
28 {
29     return RT_EOK;
30 }
31 
rt_mci_close(rt_device_t dev)32 static rt_err_t rt_mci_close(rt_device_t dev)
33 {
34     return RT_EOK;
35 }
36 
rt_mci_read(rt_device_t dev,rt_off_t pos,void * buffer,rt_size_t size)37 static rt_ssize_t rt_mci_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
38 {
39     rt_uint8_t status = kStatus_Success;
40     struct mci_device *mci = (struct mci_device *)dev;
41 
42     rt_mutex_take(&mci->lock, RT_WAITING_FOREVER);
43 
44     {
45         /* non-aligned. */
46         uint32_t i;
47         rt_size_t sector_adr;
48         uint8_t* copy_buffer;
49 
50         sector_adr = pos;
51         copy_buffer = (uint8_t*)buffer;
52 
53         for(i=0; i<size; i++)
54         {
55             status=SD_ReadBlocks(&mci->card, sdio_buffer, sector_adr, 1);
56 
57             memcpy(copy_buffer, sdio_buffer, mci->card.blockSize);
58             sector_adr ++;
59             copy_buffer += mci->card.blockSize;
60         }
61     }
62 
63     rt_mutex_release(&_mci_device->lock);
64 
65     if (status == kStatus_Success) return size;
66 
67     return 0;
68 }
69 
rt_mci_write(rt_device_t dev,rt_off_t pos,const void * buffer,rt_size_t size)70 static rt_ssize_t rt_mci_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
71 {
72     rt_uint8_t status = kStatus_Success;
73     struct mci_device *mci = (struct mci_device *)dev;
74 
75     rt_mutex_take(&mci->lock, RT_WAITING_FOREVER);
76 
77     {
78         /* non-aligned. */
79         uint32_t i;
80         rt_size_t sector_adr;
81         uint8_t* copy_buffer;
82 
83         sector_adr = pos;
84         copy_buffer = (uint8_t*)buffer;
85 
86         for(i = 0; i < size; i++)
87         {
88             memcpy(sdio_buffer, copy_buffer, mci->card.blockSize);
89 
90             status = SD_WriteBlocks(&mci->card, sdio_buffer, sector_adr, 1);
91 
92             sector_adr ++;
93             copy_buffer += mci->card.blockSize;
94 
95         }
96     }
97 
98     /* release and exit */
99     rt_mutex_release(&_mci_device->lock);
100 
101     if (status == kStatus_Success) return size;
102 
103     return 0;
104 }
105 
rt_mci_control(rt_device_t dev,int cmd,void * args)106 static rt_err_t rt_mci_control(rt_device_t dev, int cmd, void *args)
107 {
108     struct mci_device *mci = (struct mci_device *)dev;
109 
110     RT_ASSERT(dev != RT_NULL);
111 
112     if (cmd == RT_DEVICE_CTRL_BLK_GETGEOME)
113     {
114         struct rt_device_blk_geometry *geometry;
115 
116         geometry = (struct rt_device_blk_geometry *)args;
117         if (geometry == RT_NULL) return -RT_ERROR;
118 
119         geometry->bytes_per_sector = mci->card.blockSize;
120         geometry->block_size = mci->card.csd.eraseSectorSize;
121         geometry->sector_count = mci->card.blockCount;
122     }
123 
124     return RT_EOK;
125 }
126 
sdio_init_pins(void)127 void sdio_init_pins(void)
128 {
129     const uint32_t port2_pin10_config = (
130                                             IOCON_PIO_FUNC2 |                                        /* Pin is configured as SD_CARD_DET_N */
131                                             IOCON_PIO_MODE_INACT |                                   /* No addition pin function */
132                                             IOCON_PIO_INV_DI |                                       /* Input function is not inverted */
133                                             IOCON_PIO_DIGITAL_EN |                                   /* Enables digital function */
134                                             IOCON_PIO_INPFILT_OFF |                                  /* Input filter disabled */
135                                             IOCON_PIO_SLEW_STANDARD |                                /* Standard mode, output slew rate control is enabled */
136                                             IOCON_PIO_OPENDRAIN_DI                                   /* Open drain is disabled */
137                                         );
138     IOCON_PinMuxSet(IOCON, PORT2_IDX, PIN10_IDX, port2_pin10_config); /* PORT2 PIN10 (coords: P1) is configured as SD_CARD_DET_N */
139     const uint32_t port2_pin3_config = (
140                                            IOCON_PIO_FUNC2 |                                        /* Pin is configured as SD_CLK */
141                                            IOCON_PIO_MODE_INACT |                                   /* No addition pin function */
142                                            IOCON_PIO_INV_DI |                                       /* Input function is not inverted */
143                                            IOCON_PIO_DIGITAL_EN |                                   /* Enables digital function */
144                                            IOCON_PIO_INPFILT_OFF |                                  /* Input filter disabled */
145                                            IOCON_PIO_SLEW_FAST |                                    /* Fast mode, slew rate control is disabled */
146                                            IOCON_PIO_OPENDRAIN_DI                                   /* Open drain is disabled */
147                                        );
148     IOCON_PinMuxSet(IOCON, PORT2_IDX, PIN3_IDX, port2_pin3_config); /* PORT2 PIN3 (coords: B1) is configured as SD_CLK */
149     const uint32_t port2_pin4_config = (
150                                            IOCON_PIO_FUNC2 |                                        /* Pin is configured as SD_CMD */
151                                            IOCON_PIO_MODE_INACT |                                   /* No addition pin function */
152                                            IOCON_PIO_INV_DI |                                       /* Input function is not inverted */
153                                            IOCON_PIO_DIGITAL_EN |                                   /* Enables digital function */
154                                            IOCON_PIO_INPFILT_OFF |                                  /* Input filter disabled */
155                                            IOCON_PIO_SLEW_FAST |                                    /* Fast mode, slew rate control is disabled */
156                                            IOCON_PIO_OPENDRAIN_DI                                   /* Open drain is disabled */
157                                        );
158     IOCON_PinMuxSet(IOCON, PORT2_IDX, PIN4_IDX, port2_pin4_config); /* PORT2 PIN4 (coords: D3) is configured as SD_CMD */
159     const uint32_t port2_pin5_config = (
160                                            IOCON_PIO_FUNC2 |                                        /* Pin is configured as SD_POW_EN */
161                                            IOCON_PIO_MODE_INACT |                                   /* No addition pin function */
162                                            IOCON_PIO_INV_DI |                                       /* Input function is not inverted */
163                                            IOCON_PIO_DIGITAL_EN |                                   /* Enables digital function */
164                                            IOCON_PIO_INPFILT_OFF |                                  /* Input filter disabled */
165                                            IOCON_PIO_SLEW_STANDARD |                                /* Standard mode, output slew rate control is enabled */
166                                            IOCON_PIO_OPENDRAIN_DI                                   /* Open drain is disabled */
167                                        );
168     IOCON_PinMuxSet(IOCON, PORT2_IDX, PIN5_IDX, port2_pin5_config); /* PORT2 PIN5 (coords: C1) is configured as SD_POW_EN */
169     const uint32_t port2_pin6_config = (
170                                            IOCON_PIO_FUNC2 |                                        /* Pin is configured as SD_D(0) */
171                                            IOCON_PIO_MODE_INACT |                                   /* No addition pin function */
172                                            IOCON_PIO_INV_DI |                                       /* Input function is not inverted */
173                                            IOCON_PIO_DIGITAL_EN |                                   /* Enables digital function */
174                                            IOCON_PIO_INPFILT_OFF |                                  /* Input filter disabled */
175                                            IOCON_PIO_SLEW_FAST |                                    /* Fast mode, slew rate control is disabled */
176                                            IOCON_PIO_OPENDRAIN_DI                                   /* Open drain is disabled */
177                                        );
178     IOCON_PinMuxSet(IOCON, PORT2_IDX, PIN6_IDX, port2_pin6_config); /* PORT2 PIN6 (coords: F3) is configured as SD_D(0) */
179     const uint32_t port2_pin7_config = (
180                                            IOCON_PIO_FUNC2 |                                        /* Pin is configured as SD_D(1) */
181                                            IOCON_PIO_MODE_INACT |                                   /* No addition pin function */
182                                            IOCON_PIO_INV_DI |                                       /* Input function is not inverted */
183                                            IOCON_PIO_DIGITAL_EN |                                   /* Enables digital function */
184                                            IOCON_PIO_INPFILT_OFF |                                  /* Input filter disabled */
185                                            IOCON_PIO_SLEW_FAST |                                    /* Fast mode, slew rate control is disabled */
186                                            IOCON_PIO_OPENDRAIN_DI                                   /* Open drain is disabled */
187                                        );
188     IOCON_PinMuxSet(IOCON, PORT2_IDX, PIN7_IDX, port2_pin7_config); /* PORT2 PIN7 (coords: J2) is configured as SD_D(1) */
189     const uint32_t port2_pin8_config = (
190                                            IOCON_PIO_FUNC2 |                                        /* Pin is configured as SD_D(2) */
191                                            IOCON_PIO_MODE_INACT |                                   /* No addition pin function */
192                                            IOCON_PIO_INV_DI |                                       /* Input function is not inverted */
193                                            IOCON_PIO_DIGITAL_EN |                                   /* Enables digital function */
194                                            IOCON_PIO_INPFILT_OFF |                                  /* Input filter disabled */
195                                            IOCON_PIO_SLEW_FAST |                                    /* Fast mode, slew rate control is disabled */
196                                            IOCON_PIO_OPENDRAIN_DI                                   /* Open drain is disabled */
197                                        );
198     IOCON_PinMuxSet(IOCON, PORT2_IDX, PIN8_IDX, port2_pin8_config); /* PORT2 PIN8 (coords: F4) is configured as SD_D(2) */
199     const uint32_t port2_pin9_config = (
200                                            IOCON_PIO_FUNC2 |                                        /* Pin is configured as SD_D(3) */
201                                            IOCON_PIO_MODE_INACT |                                   /* No addition pin function */
202                                            IOCON_PIO_INV_DI |                                       /* Input function is not inverted */
203                                            IOCON_PIO_DIGITAL_EN |                                   /* Enables digital function */
204                                            IOCON_PIO_INPFILT_OFF |                                  /* Input filter disabled */
205                                            IOCON_PIO_SLEW_FAST |                                    /* Fast mode, slew rate control is disabled */
206                                            IOCON_PIO_OPENDRAIN_DI                                   /* Open drain is disabled */
207                                        );
208     IOCON_PinMuxSet(IOCON, PORT2_IDX, PIN9_IDX, port2_pin9_config); /* PORT2 PIN9 (coords: K2) is configured as SD_D(3) */
209     const uint32_t port3_pin15_config = (
210                                             IOCON_PIO_FUNC2 |                                        /* Pin is configured as SD_WR_PRT */
211                                             IOCON_PIO_MODE_INACT |                                   /* No addition pin function */
212                                             IOCON_PIO_INV_DI |                                       /* Input function is not inverted */
213                                             IOCON_PIO_DIGITAL_EN |                                   /* Enables digital function */
214                                             IOCON_PIO_INPFILT_OFF |                                  /* Input filter disabled */
215                                             IOCON_PIO_SLEW_STANDARD |                                /* Standard mode, output slew rate control is enabled */
216                                             IOCON_PIO_OPENDRAIN_DI                                   /* Open drain is disabled */
217                                         );
218     IOCON_PinMuxSet(IOCON, PORT3_IDX, PIN15_IDX, port3_pin15_config); /* PORT3 PIN15 (coords: D2) is configured as SD_WR_PRT */
219 }
220 
221 sd_card_t g_sd;
222 
223 /*! @brief Data written to the card */
224 uint8_t g_dataWrite[FSL_SDMMC_DEFAULT_BLOCK_SIZE * 5U];
225 /*! @brief Data read from the card */
226 uint8_t g_dataRead[FSL_SDMMC_DEFAULT_BLOCK_SIZE * 5U];
227 
mci_hw_init(const char * device_name)228 rt_err_t mci_hw_init(const char *device_name)
229 #if 0
230 {
231     sd_card_t *card = &g_sd;
232     bool isReadOnly;
233     bool failedFlag = false;
234     char ch = '0';
235 
236     /* attach main clock to SDIF */
237     CLOCK_AttachClk(kMCLK_to_SDIO_CLK);
238     /* need call this function to clear the halt bit in clock divider register */
239     CLOCK_SetClkDiv(kCLOCK_DivSdioClk, 1U, true);
240 
241     sdio_init_pins();
242 
243     card->host.base = SDIF;
244     card->host.sourceClock_Hz = CLOCK_GetFreq(kCLOCK_SDio);
245 
246     /* Init card. */
247     if (SD_Init(card))
248     {
249         rt_kprintf("\r\nSD card init failed.\r\n");
250         return -1;
251     }
252 
253     rt_kprintf("\r\nRead/Write/Erase the card continuously until encounter error......\r\n");
254     /* Check if card is readonly. */
255     isReadOnly = SD_CheckReadOnly(card);
256     if (isReadOnly)
257     {
258         //while (true)
259         {
260             /*if (failedFlag || (ch == 'q'))
261             {
262                 break;
263             }*/
264 
265             rt_kprintf("\r\nRead one data block......\r\n");
266             if (kStatus_Success != SD_ReadBlocks(card, g_dataRead, 2U, 1U))
267             {
268                 rt_kprintf("Read one data block failed.\r\n");
269                 failedFlag = true;
270                 //continue;
271             }
272 
273             rt_kprintf("Read multiple data blocks......\r\n");
274             if (kStatus_Success != SD_ReadBlocks(card, g_dataRead, 2U, 5U))
275             {
276                 rt_kprintf("Read multiple data blocks failed.\r\n");
277                 failedFlag = true;
278                 //continue;
279             }
280 
281             rt_kprintf(
282                 "\r\nInput 'q' to quit read process.\
283                 \r\nInput other char to read data blocks again.\r\n");
284             //ch = GETCHAR();
285             //PUTCHAR(ch);
286         }
287     }
288     else
289     {
290         memset(g_dataWrite, 0x67U, sizeof(g_dataWrite));
291 
292         //while (true)
293         {
294             /*if (failedFlag || (ch == 'q'))
295             {
296                 break;
297             }*/
298 
299             rt_kprintf("\r\nWrite/read one data block......\r\n");
300             if (kStatus_Success != SD_WriteBlocks(card, g_dataWrite, 2U, 1U))
301             {
302                 rt_kprintf("Write one data block failed.\r\n");
303                 failedFlag = true;
304                 //continue;
305             }
306 
307             memset(g_dataRead, 0U, sizeof(g_dataRead));
308             if (kStatus_Success != SD_ReadBlocks(card, g_dataRead, 2U, 1U))
309             {
310                 rt_kprintf("Read one data block failed.\r\n");
311                 failedFlag = true;
312                 //continue;
313             }
314 
315             rt_kprintf("Compare the read/write content......\r\n");
316             if (memcmp(g_dataRead, g_dataWrite, FSL_SDMMC_DEFAULT_BLOCK_SIZE))
317             {
318                 rt_kprintf("The read/write content isn't consistent.\r\n");
319                 failedFlag = true;
320                 //continue;
321             }
322             rt_kprintf("The read/write content is consistent.\r\n");
323 
324             rt_kprintf("Write/read multiple data blocks......\r\n");
325             if (kStatus_Success != SD_WriteBlocks(card, g_dataWrite, 2U, 5U))
326             {
327                 rt_kprintf("Write multiple data blocks failed.\r\n");
328                 failedFlag = true;
329                 //continue;
330             }
331 
332             memset(g_dataRead, 0U, sizeof(g_dataRead));
333 
334             if (kStatus_Success != SD_ReadBlocks(card, g_dataRead, 2U, 5U))
335             {
336                 rt_kprintf("Read multiple data blocks failed.\r\n");
337                 failedFlag = true;
338                 //continue;
339             }
340 
341             rt_kprintf("Compare the read/write content......\r\n");
342             if (memcmp(g_dataRead, g_dataWrite, FSL_SDMMC_DEFAULT_BLOCK_SIZE))
343             {
344                 rt_kprintf("The read/write content isn't consistent.\r\n");
345                 failedFlag = true;
346                 //continue;
347             }
348             rt_kprintf("The read/write content is consistent.\r\n");
349 
350             rt_kprintf("Erase multiple data blocks......\r\n");
351             if (kStatus_Success != SD_EraseBlocks(card, 2U, 5U))
352             {
353                 rt_kprintf("Erase multiple data blocks failed.\r\n");
354                 failedFlag = true;
355                 //continue;
356             }
357 
358             rt_kprintf(
359                 "\r\nInput 'q' to quit read/write/erase process.\
360                 \r\nInput other char to read/write/erase data blocks again.\r\n");
361             //ch = GETCHAR();
362             //PUTCHAR(ch);
363         }
364     }
365     rt_kprintf("\r\nThe example will not read/write data blocks again.\r\n");
366 
367     SD_Deinit(card);
368 }
369 #else
370 {
371     _mci_device = (struct mci_device *)rt_malloc(sizeof(struct mci_device));
372     if (_mci_device == RT_NULL)
373     {
374         rt_kprintf("mci_hw_init _mci_device rt_malloc failed!\n");
375         return -RT_ERROR;
376     }
377     rt_memset(_mci_device, 0, sizeof(struct mci_device));
378 
379     /* attach main clock to SDIF */
380     CLOCK_AttachClk(kMCLK_to_SDIO_CLK);
381     /* need call this function to clear the halt bit in clock divider register */
382     CLOCK_SetClkDiv(kCLOCK_DivSdioClk, 1U, true);
383 
384     sdio_init_pins();
385 
386     /* Save host information. */
387     _mci_device->card.host.base = SDIF;
388     _mci_device->card.host.sourceClock_Hz = CLOCK_GetFreq(kCLOCK_SDio);
389 
390     if (kStatus_Success != SD_Init(&_mci_device->card))
391     {
392         SD_Deinit(&_mci_device->card);
393         memset(&_mci_device->card, 0U, sizeof(_mci_device->card));
394         rt_kprintf("SD_Init failed!\n");
395         return -RT_ERROR;
396     }
397 
398     /*
399     follow the page: https://community.nxp.com/thread/454769
400 
401     The issue concerns sdmmc library bug (I finally solved) in SD_Init() in the file sdmmc/src/fsl_sd.c:SD_SelectBusTiming()
402     calls SD_SwitchFunction() which sets block size to 64bytes (512bits).Therefore SD_SetBlockSize(card, FSL_SDMMC_DEFAULT_BLOCK_SIZE)
403     should be called again before SD_Init() exits.
404     */
405 
406     if (kStatus_Success != SDMMC_SetBlockSize(_mci_device->card.host.base, _mci_device->card.host.transfer, FSL_SDMMC_DEFAULT_BLOCK_SIZE))
407     {
408         SD_Deinit(&_mci_device->card);
409         memset(&_mci_device->card, 0U, sizeof(_mci_device->card));
410         rt_kprintf("SD_Init failed!\n");
411         return -RT_ERROR;
412     }
413 
414     /* initialize mutex lock */
415     rt_mutex_init(&_mci_device->lock, device_name, RT_IPC_FLAG_PRIO);
416     /* create finish event */
417     _mci_device->finish_event = rt_event_create(device_name, RT_IPC_FLAG_FIFO);
418 
419     /* register sdcard device */
420     _mci_device->parent.type    = RT_Device_Class_Block;
421 
422     _mci_device->geometry.bytes_per_sector = 0;
423     _mci_device->geometry.sector_count = 0;
424     _mci_device->geometry.block_size = 0;
425 
426     _mci_device->parent.init    = rt_mci_init;
427     _mci_device->parent.open    = rt_mci_open;
428     _mci_device->parent.close   = rt_mci_close;
429     _mci_device->parent.read    = rt_mci_read;
430     _mci_device->parent.write   = rt_mci_write;
431     _mci_device->parent.control = rt_mci_control;
432 
433     /* no private, no callback */
434     _mci_device->parent.user_data = RT_NULL;
435     _mci_device->parent.rx_indicate = RT_NULL;
436     _mci_device->parent.tx_complete = RT_NULL;
437 
438     rt_device_register(&_mci_device->parent, device_name,
439                        RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE );
440     return RT_EOK;
441 }
442 #endif
443