diff options
Diffstat (limited to 'lib/chibios/os/hal/src/hal_sdc.c')
-rw-r--r-- | lib/chibios/os/hal/src/hal_sdc.c | 999 |
1 files changed, 999 insertions, 0 deletions
diff --git a/lib/chibios/os/hal/src/hal_sdc.c b/lib/chibios/os/hal/src/hal_sdc.c new file mode 100644 index 000000000..0d93ce636 --- /dev/null +++ b/lib/chibios/os/hal/src/hal_sdc.c | |||
@@ -0,0 +1,999 @@ | |||
1 | /* | ||
2 | ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio | ||
3 | |||
4 | Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | you may not use this file except in compliance with the License. | ||
6 | You may obtain a copy of the License at | ||
7 | |||
8 | http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | |||
10 | Unless required by applicable law or agreed to in writing, software | ||
11 | distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | See the License for the specific language governing permissions and | ||
14 | limitations under the License. | ||
15 | */ | ||
16 | |||
17 | /** | ||
18 | * @file hal_sdc.c | ||
19 | * @brief SDC Driver code. | ||
20 | * | ||
21 | * @addtogroup SDC | ||
22 | * @{ | ||
23 | */ | ||
24 | |||
25 | #include <string.h> | ||
26 | |||
27 | #include "hal.h" | ||
28 | |||
29 | #if (HAL_USE_SDC == TRUE) || defined(__DOXYGEN__) | ||
30 | |||
31 | /*===========================================================================*/ | ||
32 | /* Driver local definitions. */ | ||
33 | /*===========================================================================*/ | ||
34 | |||
35 | /** | ||
36 | * @brief MMC switch mode. | ||
37 | */ | ||
38 | typedef enum { | ||
39 | MMC_SWITCH_COMMAND_SET = 0, | ||
40 | MMC_SWITCH_SET_BITS = 1, | ||
41 | MMC_SWITCH_CLEAR_BITS = 2, | ||
42 | MMC_SWITCH_WRITE_BYTE = 3 | ||
43 | } mmc_switch_t; | ||
44 | |||
45 | /** | ||
46 | * @brief SDC switch mode. | ||
47 | */ | ||
48 | typedef enum { | ||
49 | SD_SWITCH_CHECK = 0, | ||
50 | SD_SWITCH_SET = 1 | ||
51 | } sd_switch_t; | ||
52 | |||
53 | /** | ||
54 | * @brief SDC switch function. | ||
55 | */ | ||
56 | typedef enum { | ||
57 | SD_SWITCH_FUNCTION_SPEED = 0, | ||
58 | SD_SWITCH_FUNCTION_CMD_SYSTEM = 1, | ||
59 | SD_SWITCH_FUNCTION_DRIVER_STRENGTH = 2, | ||
60 | SD_SWITCH_FUNCTION_CURRENT_LIMIT = 3 | ||
61 | } sd_switch_function_t; | ||
62 | |||
63 | /*===========================================================================*/ | ||
64 | /* Driver exported variables. */ | ||
65 | /*===========================================================================*/ | ||
66 | |||
67 | /*===========================================================================*/ | ||
68 | /* Driver local variables and types. */ | ||
69 | /*===========================================================================*/ | ||
70 | |||
71 | /** | ||
72 | * @brief Virtual methods table. | ||
73 | */ | ||
74 | static const struct SDCDriverVMT sdc_vmt = { | ||
75 | (size_t)0, | ||
76 | (bool (*)(void *))sdc_lld_is_card_inserted, | ||
77 | (bool (*)(void *))sdc_lld_is_write_protected, | ||
78 | (bool (*)(void *))sdcConnect, | ||
79 | (bool (*)(void *))sdcDisconnect, | ||
80 | (bool (*)(void *, uint32_t, uint8_t *, uint32_t))sdcRead, | ||
81 | (bool (*)(void *, uint32_t, const uint8_t *, uint32_t))sdcWrite, | ||
82 | (bool (*)(void *))sdcSync, | ||
83 | (bool (*)(void *, BlockDeviceInfo *))sdcGetInfo | ||
84 | }; | ||
85 | |||
86 | /*===========================================================================*/ | ||
87 | /* Driver local functions. */ | ||
88 | /*===========================================================================*/ | ||
89 | /** | ||
90 | * @brief Detects card mode. | ||
91 | * | ||
92 | * @param[in] sdcp pointer to the @p SDCDriver object | ||
93 | * | ||
94 | * @return The operation status. | ||
95 | * @retval HAL_SUCCESS operation succeeded. | ||
96 | * @retval HAL_FAILED operation failed. | ||
97 | * | ||
98 | * @notapi | ||
99 | */ | ||
100 | static bool mode_detect(SDCDriver *sdcp) { | ||
101 | uint32_t resp[1]; | ||
102 | |||
103 | /* V2.0 cards detection.*/ | ||
104 | if (!sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SEND_IF_COND, | ||
105 | MMCSD_CMD8_PATTERN, resp)) { | ||
106 | sdcp->cardmode = SDC_MODE_CARDTYPE_SDV20; | ||
107 | /* Voltage verification.*/ | ||
108 | if (((resp[0] >> 8U) & 0xFU) != 1U) { | ||
109 | return HAL_FAILED; | ||
110 | } | ||
111 | if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_APP_CMD, 0, resp) || | ||
112 | MMCSD_R1_ERROR(resp[0])) { | ||
113 | return HAL_FAILED; | ||
114 | } | ||
115 | } | ||
116 | else { | ||
117 | /* MMC or SD V1.1 detection.*/ | ||
118 | if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_APP_CMD, 0, resp) || | ||
119 | MMCSD_R1_ERROR(resp[0])) { | ||
120 | sdcp->cardmode = SDC_MODE_CARDTYPE_MMC; | ||
121 | } | ||
122 | else { | ||
123 | sdcp->cardmode = SDC_MODE_CARDTYPE_SDV11; | ||
124 | |||
125 | /* Reset error flag illegal command.*/ | ||
126 | sdc_lld_send_cmd_none(sdcp, MMCSD_CMD_GO_IDLE_STATE, 0); | ||
127 | } | ||
128 | } | ||
129 | |||
130 | return HAL_SUCCESS; | ||
131 | } | ||
132 | |||
133 | /** | ||
134 | * @brief Init procedure for MMC. | ||
135 | * | ||
136 | * @param[in] sdcp pointer to the @p SDCDriver object | ||
137 | * | ||
138 | * @return The operation status. | ||
139 | * @retval HAL_SUCCESS operation succeeded. | ||
140 | * @retval HAL_FAILED operation failed. | ||
141 | * | ||
142 | * @notapi | ||
143 | */ | ||
144 | static bool mmc_init(SDCDriver *sdcp) { | ||
145 | uint32_t ocr; | ||
146 | unsigned i; | ||
147 | uint32_t resp[1]; | ||
148 | |||
149 | ocr = 0xC0FF8000U; | ||
150 | i = 0; | ||
151 | while (true) { | ||
152 | if (sdc_lld_send_cmd_short(sdcp, MMCSD_CMD_INIT, ocr, resp)) { | ||
153 | return HAL_FAILED; | ||
154 | } | ||
155 | if ((resp[0] & 0x80000000U) != 0U) { | ||
156 | if ((resp[0] & 0x40000000U) != 0U) { | ||
157 | sdcp->cardmode |= SDC_MODE_HIGH_CAPACITY; | ||
158 | } | ||
159 | break; | ||
160 | } | ||
161 | if (++i >= (unsigned)SDC_INIT_RETRY) { | ||
162 | return HAL_FAILED; | ||
163 | } | ||
164 | osalThreadSleepMilliseconds(10); | ||
165 | } | ||
166 | |||
167 | return HAL_SUCCESS; | ||
168 | } | ||
169 | |||
170 | /** | ||
171 | * @brief Init procedure for SDC. | ||
172 | * | ||
173 | * @param[in] sdcp pointer to the @p SDCDriver object | ||
174 | * | ||
175 | * @return The operation status. | ||
176 | * @retval HAL_SUCCESS operation succeeded. | ||
177 | * @retval HAL_FAILED operation failed. | ||
178 | * | ||
179 | * @notapi | ||
180 | */ | ||
181 | static bool sdc_init(SDCDriver *sdcp) { | ||
182 | unsigned i; | ||
183 | uint32_t ocr; | ||
184 | uint32_t resp[1]; | ||
185 | |||
186 | if ((sdcp->cardmode & SDC_MODE_CARDTYPE_MASK) == SDC_MODE_CARDTYPE_SDV20) { | ||
187 | ocr = SDC_INIT_OCR_V20; | ||
188 | } | ||
189 | else { | ||
190 | ocr = SDC_INIT_OCR; | ||
191 | } | ||
192 | |||
193 | i = 0; | ||
194 | while (true) { | ||
195 | if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_APP_CMD, 0, resp) || | ||
196 | MMCSD_R1_ERROR(resp[0])) { | ||
197 | return HAL_FAILED; | ||
198 | } | ||
199 | if (sdc_lld_send_cmd_short(sdcp, MMCSD_CMD_APP_OP_COND, ocr, resp)) { | ||
200 | return HAL_FAILED; | ||
201 | } | ||
202 | if ((resp[0] & 0x80000000U) != 0U) { | ||
203 | if ((resp[0] & 0x40000000U) != 0U) { | ||
204 | sdcp->cardmode |= SDC_MODE_HIGH_CAPACITY; | ||
205 | } | ||
206 | break; | ||
207 | } | ||
208 | if (++i >= (unsigned)SDC_INIT_RETRY) { | ||
209 | return HAL_FAILED; | ||
210 | } | ||
211 | osalThreadSleepMilliseconds(10); | ||
212 | } | ||
213 | |||
214 | return HAL_SUCCESS; | ||
215 | } | ||
216 | |||
217 | /** | ||
218 | * @brief Constructs CMD6 argument for MMC. | ||
219 | * | ||
220 | * @param[in] access EXT_CSD access mode | ||
221 | * @param[in] idx EXT_CSD byte number | ||
222 | * @param[in] value value to be written in target field | ||
223 | * @param[in] cmd_set switch current command set | ||
224 | * | ||
225 | * @return CMD6 argument. | ||
226 | * | ||
227 | * @notapi | ||
228 | */ | ||
229 | static uint32_t mmc_cmd6_construct(mmc_switch_t access, uint32_t idx, | ||
230 | uint32_t value, uint32_t cmd_set) { | ||
231 | |||
232 | osalDbgAssert(idx <= 191U, "This field is not writable"); | ||
233 | osalDbgAssert(cmd_set < 8U, "This field has only 3 bits"); | ||
234 | |||
235 | return ((uint32_t)access << 24U) | (idx << 16U) | (value << 8U) | cmd_set; | ||
236 | } | ||
237 | |||
238 | /** | ||
239 | * @brief Constructs CMD6 argument for SDC. | ||
240 | * | ||
241 | * @param[in] mode switch/test mode | ||
242 | * @param[in] function function number to be switched | ||
243 | * @param[in] value value to be written in target function | ||
244 | * | ||
245 | * @return CMD6 argument. | ||
246 | * | ||
247 | * @notapi | ||
248 | */ | ||
249 | static uint32_t sdc_cmd6_construct(sd_switch_t mode, | ||
250 | sd_switch_function_t function, | ||
251 | uint32_t value) { | ||
252 | uint32_t ret = 0xFFFFFF; | ||
253 | |||
254 | osalDbgAssert((value < 16U), "This field has only 4 bits"); | ||
255 | |||
256 | ret &= ~((uint32_t)0xFU << ((uint32_t)function * 4U)); | ||
257 | ret |= value << ((uint32_t)function * 4U); | ||
258 | return ret | ((uint32_t)mode << 31U); | ||
259 | } | ||
260 | |||
261 | /** | ||
262 | * @brief Extracts information from CMD6 answer. | ||
263 | * | ||
264 | * @param[in] function function number to be switched | ||
265 | * @param[in] buf buffer with answer | ||
266 | * | ||
267 | * @return extracted answer. | ||
268 | * | ||
269 | * @notapi | ||
270 | */ | ||
271 | static uint16_t sdc_cmd6_extract_info(sd_switch_function_t function, | ||
272 | const uint8_t *buf) { | ||
273 | |||
274 | unsigned start = 12U - ((unsigned)function * 2U); | ||
275 | |||
276 | return ((uint16_t)buf[start] << 8U) | (uint16_t)buf[start + 1U]; | ||
277 | } | ||
278 | |||
279 | /** | ||
280 | * @brief Checks status after switching using CMD6. | ||
281 | * | ||
282 | * @param[in] function function number to be switched | ||
283 | * @param[in] buf buffer with answer | ||
284 | * | ||
285 | * @return The operation status. | ||
286 | * @retval HAL_SUCCESS operation succeeded. | ||
287 | * @retval HAL_FAILED operation failed. | ||
288 | * | ||
289 | * @notapi | ||
290 | */ | ||
291 | static bool sdc_cmd6_check_status(sd_switch_function_t function, | ||
292 | const uint8_t *buf) { | ||
293 | |||
294 | uint32_t tmp; | ||
295 | uint32_t status; | ||
296 | |||
297 | tmp = ((uint32_t)buf[14] << 16U) | | ||
298 | ((uint32_t)buf[15] << 8U) | | ||
299 | (uint32_t)buf[16]; | ||
300 | status = (tmp >> ((uint32_t)function * 4U)) & 0xFU; | ||
301 | if (0xFU != status) { | ||
302 | return HAL_SUCCESS; | ||
303 | } | ||
304 | return HAL_FAILED; | ||
305 | } | ||
306 | |||
307 | /** | ||
308 | * @brief Reads supported bus clock and switch SDC to appropriate mode. | ||
309 | * | ||
310 | * @param[in] sdcp pointer to the @p SDCDriver object | ||
311 | * @param[out] clk pointer to clock enum | ||
312 | * | ||
313 | * @return The operation status. | ||
314 | * @retval HAL_SUCCESS operation succeeded. | ||
315 | * @retval HAL_FAILED operation failed. | ||
316 | * | ||
317 | * @notapi | ||
318 | */ | ||
319 | static bool sdc_detect_bus_clk(SDCDriver *sdcp, sdcbusclk_t *clk) { | ||
320 | uint32_t cmdarg; | ||
321 | const size_t N = 64; | ||
322 | uint8_t tmp[N]; | ||
323 | |||
324 | /* Safe default.*/ | ||
325 | *clk = SDC_CLK_25MHz; | ||
326 | |||
327 | /* Looks like only "high capacity" cards produce meaningful results during | ||
328 | this clock detection procedure.*/ | ||
329 | if (0U == _mmcsd_get_slice(sdcp->csd, MMCSD_CSD_10_CSD_STRUCTURE_SLICE)) { | ||
330 | *clk = SDC_CLK_25MHz; | ||
331 | return HAL_SUCCESS; | ||
332 | } | ||
333 | |||
334 | /* Read switch functions' register.*/ | ||
335 | if (sdc_lld_read_special(sdcp, tmp, N, MMCSD_CMD_SWITCH, 0)) { | ||
336 | return HAL_FAILED; | ||
337 | } | ||
338 | |||
339 | /* Check card capabilities parsing acquired data.*/ | ||
340 | if ((sdc_cmd6_extract_info(SD_SWITCH_FUNCTION_SPEED, tmp) & 2U) == 2U) { | ||
341 | /* Construct command to set the bus speed.*/ | ||
342 | cmdarg = sdc_cmd6_construct(SD_SWITCH_SET, SD_SWITCH_FUNCTION_SPEED, 1); | ||
343 | |||
344 | /* Write constructed command and read operation status in single call.*/ | ||
345 | if (sdc_lld_read_special(sdcp, tmp, N, MMCSD_CMD_SWITCH, cmdarg)) { | ||
346 | return HAL_FAILED; | ||
347 | } | ||
348 | |||
349 | /* Check card answer for success status bits.*/ | ||
350 | if (HAL_SUCCESS == sdc_cmd6_check_status(SD_SWITCH_FUNCTION_SPEED, tmp)) { | ||
351 | *clk = SDC_CLK_50MHz; | ||
352 | } | ||
353 | else { | ||
354 | *clk = SDC_CLK_25MHz; | ||
355 | } | ||
356 | } | ||
357 | |||
358 | return HAL_SUCCESS; | ||
359 | } | ||
360 | |||
361 | /** | ||
362 | * @brief Reads supported bus clock and switch MMC to appropriate mode. | ||
363 | * | ||
364 | * @param[in] sdcp pointer to the @p SDCDriver object | ||
365 | * @param[out] clk pointer to clock enum | ||
366 | * | ||
367 | * @return The operation status. | ||
368 | * @retval HAL_SUCCESS operation succeeded. | ||
369 | * @retval HAL_FAILED operation failed. | ||
370 | * | ||
371 | * @notapi | ||
372 | */ | ||
373 | static bool mmc_detect_bus_clk(SDCDriver *sdcp, sdcbusclk_t *clk) { | ||
374 | uint32_t cmdarg; | ||
375 | uint32_t resp[1]; | ||
376 | |||
377 | /* Safe default.*/ | ||
378 | *clk = SDC_CLK_25MHz; | ||
379 | |||
380 | cmdarg = mmc_cmd6_construct(MMC_SWITCH_WRITE_BYTE, 185, 1, 0); | ||
381 | if (!(sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SWITCH, cmdarg, resp) || | ||
382 | MMCSD_R1_ERROR(resp[0]))) { | ||
383 | *clk = SDC_CLK_50MHz; | ||
384 | } | ||
385 | |||
386 | return HAL_SUCCESS; | ||
387 | } | ||
388 | |||
389 | /** | ||
390 | * @brief Reads supported bus clock and switch card to appropriate mode. | ||
391 | * | ||
392 | * @param[in] sdcp pointer to the @p SDCDriver object | ||
393 | * @param[out] clk pointer to clock enum | ||
394 | * | ||
395 | * @return The operation status. | ||
396 | * @retval HAL_SUCCESS operation succeeded. | ||
397 | * @retval HAL_FAILED operation failed. | ||
398 | * | ||
399 | * @notapi | ||
400 | */ | ||
401 | static bool detect_bus_clk(SDCDriver *sdcp, sdcbusclk_t *clk) { | ||
402 | |||
403 | if (SDC_MODE_CARDTYPE_MMC == (sdcp->cardmode & SDC_MODE_CARDTYPE_MASK)) { | ||
404 | return mmc_detect_bus_clk(sdcp, clk); | ||
405 | } | ||
406 | return sdc_detect_bus_clk(sdcp, clk); | ||
407 | } | ||
408 | |||
409 | /** | ||
410 | * @brief Sets bus width for SDC. | ||
411 | * | ||
412 | * @param[in] sdcp pointer to the @p SDCDriver object | ||
413 | * | ||
414 | * @return The operation status. | ||
415 | * @retval HAL_SUCCESS operation succeeded. | ||
416 | * @retval HAL_FAILED operation failed. | ||
417 | * | ||
418 | * @notapi | ||
419 | */ | ||
420 | static bool sdc_set_bus_width(SDCDriver *sdcp) { | ||
421 | uint32_t resp[1]; | ||
422 | |||
423 | if (SDC_MODE_1BIT == sdcp->config->bus_width) { | ||
424 | /* Nothing to do. Bus is already in 1bit mode.*/ | ||
425 | return HAL_SUCCESS; | ||
426 | } | ||
427 | else if (SDC_MODE_4BIT == sdcp->config->bus_width) { | ||
428 | sdc_lld_set_bus_mode(sdcp, SDC_MODE_4BIT); | ||
429 | if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_APP_CMD, sdcp->rca, resp) || | ||
430 | MMCSD_R1_ERROR(resp[0])) { | ||
431 | return HAL_FAILED; | ||
432 | } | ||
433 | |||
434 | if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SET_BUS_WIDTH, 2, resp) || | ||
435 | MMCSD_R1_ERROR(resp[0])) { | ||
436 | return HAL_FAILED; | ||
437 | } | ||
438 | } | ||
439 | else { | ||
440 | /* SD card does not support 8bit bus.*/ | ||
441 | return HAL_FAILED; | ||
442 | } | ||
443 | |||
444 | return HAL_SUCCESS; | ||
445 | } | ||
446 | |||
447 | /** | ||
448 | * @brief Sets bus width for MMC. | ||
449 | * | ||
450 | * @param[in] sdcp pointer to the @p SDCDriver object | ||
451 | * | ||
452 | * @return The operation status. | ||
453 | * @retval HAL_SUCCESS operation succeeded. | ||
454 | * @retval HAL_FAILED operation failed. | ||
455 | * | ||
456 | * @notapi | ||
457 | */ | ||
458 | static bool mmc_set_bus_width(SDCDriver *sdcp) { | ||
459 | uint32_t resp[1]; | ||
460 | uint32_t cmdarg = mmc_cmd6_construct(MMC_SWITCH_WRITE_BYTE, 183, 0, 0); | ||
461 | |||
462 | switch (sdcp->config->bus_width) { | ||
463 | case SDC_MODE_1BIT: | ||
464 | /* Nothing to do. Bus is already in 1bit mode.*/ | ||
465 | return HAL_SUCCESS; | ||
466 | case SDC_MODE_4BIT: | ||
467 | cmdarg = mmc_cmd6_construct(MMC_SWITCH_WRITE_BYTE, 183, 1, 0); | ||
468 | break; | ||
469 | case SDC_MODE_8BIT: | ||
470 | cmdarg = mmc_cmd6_construct(MMC_SWITCH_WRITE_BYTE, 183, 2, 0); | ||
471 | break; | ||
472 | default: | ||
473 | osalDbgAssert(false, "unexpected case"); | ||
474 | break; | ||
475 | } | ||
476 | |||
477 | sdc_lld_set_bus_mode(sdcp, sdcp->config->bus_width); | ||
478 | if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SWITCH, cmdarg, resp) || | ||
479 | MMCSD_R1_ERROR(resp[0])) { | ||
480 | return HAL_FAILED; | ||
481 | } | ||
482 | |||
483 | return HAL_SUCCESS; | ||
484 | } | ||
485 | |||
486 | /** | ||
487 | * @brief Wait for the card to complete pending operations. | ||
488 | * | ||
489 | * @param[in] sdcp pointer to the @p SDCDriver object | ||
490 | * | ||
491 | * @return The operation status. | ||
492 | * @retval HAL_SUCCESS operation succeeded. | ||
493 | * @retval HAL_FAILED operation failed. | ||
494 | * | ||
495 | * @notapi | ||
496 | */ | ||
497 | bool _sdc_wait_for_transfer_state(SDCDriver *sdcp) { | ||
498 | uint32_t resp[1]; | ||
499 | |||
500 | while (true) { | ||
501 | if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SEND_STATUS, | ||
502 | sdcp->rca, resp) || | ||
503 | MMCSD_R1_ERROR(resp[0])) { | ||
504 | return HAL_FAILED; | ||
505 | } | ||
506 | |||
507 | switch (MMCSD_R1_STS(resp[0])) { | ||
508 | case MMCSD_STS_TRAN: | ||
509 | return HAL_SUCCESS; | ||
510 | case MMCSD_STS_DATA: | ||
511 | case MMCSD_STS_RCV: | ||
512 | case MMCSD_STS_PRG: | ||
513 | #if SDC_NICE_WAITING == TRUE | ||
514 | osalThreadSleepMilliseconds(1); | ||
515 | #endif | ||
516 | continue; | ||
517 | default: | ||
518 | /* The card should have been initialized so any other state is not | ||
519 | valid and is reported as an error.*/ | ||
520 | return HAL_FAILED; | ||
521 | } | ||
522 | } | ||
523 | } | ||
524 | |||
525 | /*===========================================================================*/ | ||
526 | /* Driver exported functions. */ | ||
527 | /*===========================================================================*/ | ||
528 | |||
529 | /** | ||
530 | * @brief SDC Driver initialization. | ||
531 | * @note This function is implicitly invoked by @p halInit(), there is | ||
532 | * no need to explicitly initialize the driver. | ||
533 | * | ||
534 | * @init | ||
535 | */ | ||
536 | void sdcInit(void) { | ||
537 | |||
538 | sdc_lld_init(); | ||
539 | } | ||
540 | |||
541 | /** | ||
542 | * @brief Initializes the standard part of a @p SDCDriver structure. | ||
543 | * | ||
544 | * @param[out] sdcp pointer to the @p SDCDriver object | ||
545 | * | ||
546 | * @init | ||
547 | */ | ||
548 | void sdcObjectInit(SDCDriver *sdcp) { | ||
549 | |||
550 | sdcp->vmt = &sdc_vmt; | ||
551 | sdcp->state = BLK_STOP; | ||
552 | sdcp->errors = SDC_NO_ERROR; | ||
553 | sdcp->config = NULL; | ||
554 | sdcp->capacity = 0; | ||
555 | } | ||
556 | |||
557 | /** | ||
558 | * @brief Configures and activates the SDC peripheral. | ||
559 | * | ||
560 | * @param[in] sdcp pointer to the @p SDCDriver object | ||
561 | * @param[in] config pointer to the @p SDCConfig object, can be @p NULL if | ||
562 | * the driver supports a default configuration or | ||
563 | * requires no configuration | ||
564 | * | ||
565 | * @api | ||
566 | */ | ||
567 | void sdcStart(SDCDriver *sdcp, const SDCConfig *config) { | ||
568 | |||
569 | osalDbgCheck(sdcp != NULL); | ||
570 | |||
571 | osalSysLock(); | ||
572 | osalDbgAssert((sdcp->state == BLK_STOP) || (sdcp->state == BLK_ACTIVE), | ||
573 | "invalid state"); | ||
574 | sdcp->config = config; | ||
575 | sdc_lld_start(sdcp); | ||
576 | sdcp->state = BLK_ACTIVE; | ||
577 | osalSysUnlock(); | ||
578 | } | ||
579 | |||
580 | /** | ||
581 | * @brief Deactivates the SDC peripheral. | ||
582 | * | ||
583 | * @param[in] sdcp pointer to the @p SDCDriver object | ||
584 | * | ||
585 | * @api | ||
586 | */ | ||
587 | void sdcStop(SDCDriver *sdcp) { | ||
588 | |||
589 | osalDbgCheck(sdcp != NULL); | ||
590 | |||
591 | osalSysLock(); | ||
592 | |||
593 | osalDbgAssert((sdcp->state == BLK_STOP) || (sdcp->state == BLK_ACTIVE), | ||
594 | "invalid state"); | ||
595 | |||
596 | sdc_lld_stop(sdcp); | ||
597 | sdcp->config = NULL; | ||
598 | sdcp->state = BLK_STOP; | ||
599 | |||
600 | osalSysUnlock(); | ||
601 | } | ||
602 | |||
603 | /** | ||
604 | * @brief Performs the initialization procedure on the inserted card. | ||
605 | * @details This function should be invoked when a card is inserted and | ||
606 | * brings the driver in the @p BLK_READY state where it is possible | ||
607 | * to perform read and write operations. | ||
608 | * | ||
609 | * @param[in] sdcp pointer to the @p SDCDriver object | ||
610 | * | ||
611 | * @return The operation status. | ||
612 | * @retval HAL_SUCCESS operation succeeded. | ||
613 | * @retval HAL_FAILED operation failed. | ||
614 | * | ||
615 | * @api | ||
616 | */ | ||
617 | bool sdcConnect(SDCDriver *sdcp) { | ||
618 | uint32_t resp[1]; | ||
619 | sdcbusclk_t clk = SDC_CLK_25MHz; | ||
620 | |||
621 | osalDbgCheck(sdcp != NULL); | ||
622 | osalDbgAssert((sdcp->state == BLK_ACTIVE) || (sdcp->state == BLK_READY), | ||
623 | "invalid state"); | ||
624 | |||
625 | /* Connection procedure in progress.*/ | ||
626 | sdcp->state = BLK_CONNECTING; | ||
627 | |||
628 | /* Card clock initialization.*/ | ||
629 | sdc_lld_start_clk(sdcp); | ||
630 | |||
631 | /* Enforces the initial card state.*/ | ||
632 | sdc_lld_send_cmd_none(sdcp, MMCSD_CMD_GO_IDLE_STATE, 0); | ||
633 | |||
634 | /* Detect card type.*/ | ||
635 | if (HAL_FAILED == mode_detect(sdcp)) { | ||
636 | goto failed; | ||
637 | } | ||
638 | |||
639 | /* Perform specific initialization procedure.*/ | ||
640 | if ((sdcp->cardmode & SDC_MODE_CARDTYPE_MASK) == SDC_MODE_CARDTYPE_MMC) { | ||
641 | if (HAL_FAILED == mmc_init(sdcp)) { | ||
642 | goto failed; | ||
643 | } | ||
644 | } | ||
645 | else { | ||
646 | if (HAL_FAILED == sdc_init(sdcp)) { | ||
647 | goto failed; | ||
648 | } | ||
649 | } | ||
650 | |||
651 | /* Reads CID.*/ | ||
652 | if (sdc_lld_send_cmd_long_crc(sdcp, MMCSD_CMD_ALL_SEND_CID, 0, sdcp->cid)) { | ||
653 | goto failed; | ||
654 | } | ||
655 | |||
656 | /* Asks for the RCA.*/ | ||
657 | if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SEND_RELATIVE_ADDR, | ||
658 | 0, &sdcp->rca)) { | ||
659 | goto failed; | ||
660 | } | ||
661 | |||
662 | /* Reads CSD.*/ | ||
663 | if (sdc_lld_send_cmd_long_crc(sdcp, MMCSD_CMD_SEND_CSD, | ||
664 | sdcp->rca, sdcp->csd)) { | ||
665 | goto failed; | ||
666 | } | ||
667 | |||
668 | /* Selects the card for operations.*/ | ||
669 | if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SEL_DESEL_CARD, | ||
670 | sdcp->rca, resp)) { | ||
671 | goto failed; | ||
672 | } | ||
673 | |||
674 | /* Switches to high speed.*/ | ||
675 | if (HAL_SUCCESS != detect_bus_clk(sdcp, &clk)) { | ||
676 | goto failed; | ||
677 | } | ||
678 | sdc_lld_set_data_clk(sdcp, clk); | ||
679 | |||
680 | /* Reads extended CSD if needed and possible.*/ | ||
681 | if (SDC_MODE_CARDTYPE_MMC == (sdcp->cardmode & SDC_MODE_CARDTYPE_MASK)) { | ||
682 | |||
683 | /* The card is a MMC, checking if it is a large device.*/ | ||
684 | if (_mmcsd_get_slice(sdcp->csd, MMCSD_CSD_MMC_CSD_STRUCTURE_SLICE) > 1U) { | ||
685 | uint8_t *ext_csd = sdcp->buf; | ||
686 | |||
687 | if (sdc_lld_read_special(sdcp, ext_csd, 512, MMCSD_CMD_SEND_EXT_CSD, 0)) { | ||
688 | goto failed; | ||
689 | } | ||
690 | |||
691 | /* Capacity from the EXT_CSD.*/ | ||
692 | sdcp->capacity = _mmcsd_get_capacity_ext(ext_csd); | ||
693 | } | ||
694 | else { | ||
695 | /* Capacity from the normal CSD.*/ | ||
696 | sdcp->capacity = _mmcsd_get_capacity(sdcp->csd); | ||
697 | } | ||
698 | } | ||
699 | else { | ||
700 | /* The card is an SDC, capacity from the normal CSD.*/ | ||
701 | sdcp->capacity = _mmcsd_get_capacity(sdcp->csd); | ||
702 | } | ||
703 | |||
704 | /* Block length fixed at 512 bytes.*/ | ||
705 | if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SET_BLOCKLEN, | ||
706 | MMCSD_BLOCK_SIZE, resp) || | ||
707 | MMCSD_R1_ERROR(resp[0])) { | ||
708 | goto failed; | ||
709 | } | ||
710 | |||
711 | /* Switches to wide bus mode.*/ | ||
712 | switch (sdcp->cardmode & SDC_MODE_CARDTYPE_MASK) { | ||
713 | case SDC_MODE_CARDTYPE_SDV11: | ||
714 | case SDC_MODE_CARDTYPE_SDV20: | ||
715 | if (HAL_FAILED == sdc_set_bus_width(sdcp)) { | ||
716 | goto failed; | ||
717 | } | ||
718 | break; | ||
719 | case SDC_MODE_CARDTYPE_MMC: | ||
720 | if (HAL_FAILED == mmc_set_bus_width(sdcp)) { | ||
721 | goto failed; | ||
722 | } | ||
723 | break; | ||
724 | default: | ||
725 | /* Unknown type.*/ | ||
726 | goto failed; | ||
727 | } | ||
728 | |||
729 | /* Initialization complete.*/ | ||
730 | sdcp->state = BLK_READY; | ||
731 | return HAL_SUCCESS; | ||
732 | |||
733 | /* Connection failed, state reset to BLK_ACTIVE.*/ | ||
734 | failed: | ||
735 | sdc_lld_stop_clk(sdcp); | ||
736 | sdcp->state = BLK_ACTIVE; | ||
737 | return HAL_FAILED; | ||
738 | } | ||
739 | |||
740 | /** | ||
741 | * @brief Brings the driver in a state safe for card removal. | ||
742 | * | ||
743 | * @param[in] sdcp pointer to the @p SDCDriver object | ||
744 | * | ||
745 | * @return The operation status. | ||
746 | * @retval HAL_SUCCESS operation succeeded. | ||
747 | * @retval HAL_FAILED operation failed. | ||
748 | * | ||
749 | * @api | ||
750 | */ | ||
751 | bool sdcDisconnect(SDCDriver *sdcp) { | ||
752 | |||
753 | osalDbgCheck(sdcp != NULL); | ||
754 | |||
755 | osalSysLock(); | ||
756 | osalDbgAssert((sdcp->state == BLK_ACTIVE) || (sdcp->state == BLK_READY), | ||
757 | "invalid state"); | ||
758 | if (sdcp->state == BLK_ACTIVE) { | ||
759 | osalSysUnlock(); | ||
760 | return HAL_SUCCESS; | ||
761 | } | ||
762 | sdcp->state = BLK_DISCONNECTING; | ||
763 | osalSysUnlock(); | ||
764 | |||
765 | /* Waits for eventual pending operations completion.*/ | ||
766 | if (_sdc_wait_for_transfer_state(sdcp)) { | ||
767 | sdc_lld_stop_clk(sdcp); | ||
768 | sdcp->state = BLK_ACTIVE; | ||
769 | return HAL_FAILED; | ||
770 | } | ||
771 | |||
772 | /* Card clock stopped.*/ | ||
773 | sdc_lld_stop_clk(sdcp); | ||
774 | sdcp->state = BLK_ACTIVE; | ||
775 | return HAL_SUCCESS; | ||
776 | } | ||
777 | |||
778 | /** | ||
779 | * @brief Reads one or more blocks. | ||
780 | * @pre The driver must be in the @p BLK_READY state after a successful | ||
781 | * sdcConnect() invocation. | ||
782 | * | ||
783 | * @param[in] sdcp pointer to the @p SDCDriver object | ||
784 | * @param[in] startblk first block to read | ||
785 | * @param[out] buf pointer to the read buffer | ||
786 | * @param[in] n number of blocks to read | ||
787 | * | ||
788 | * @return The operation status. | ||
789 | * @retval HAL_SUCCESS operation succeeded. | ||
790 | * @retval HAL_FAILED operation failed. | ||
791 | * | ||
792 | * @api | ||
793 | */ | ||
794 | bool sdcRead(SDCDriver *sdcp, uint32_t startblk, uint8_t *buf, uint32_t n) { | ||
795 | bool status; | ||
796 | |||
797 | osalDbgCheck((sdcp != NULL) && (buf != NULL) && (n > 0U)); | ||
798 | osalDbgAssert(sdcp->state == BLK_READY, "invalid state"); | ||
799 | |||
800 | if ((startblk + n - 1U) > sdcp->capacity) { | ||
801 | sdcp->errors |= SDC_OVERFLOW_ERROR; | ||
802 | return HAL_FAILED; | ||
803 | } | ||
804 | |||
805 | /* Read operation in progress.*/ | ||
806 | sdcp->state = BLK_READING; | ||
807 | |||
808 | status = sdc_lld_read(sdcp, startblk, buf, n); | ||
809 | |||
810 | /* Read operation finished.*/ | ||
811 | sdcp->state = BLK_READY; | ||
812 | return status; | ||
813 | } | ||
814 | |||
815 | /** | ||
816 | * @brief Writes one or more blocks. | ||
817 | * @pre The driver must be in the @p BLK_READY state after a successful | ||
818 | * sdcConnect() invocation. | ||
819 | * | ||
820 | * @param[in] sdcp pointer to the @p SDCDriver object | ||
821 | * @param[in] startblk first block to write | ||
822 | * @param[out] buf pointer to the write buffer | ||
823 | * @param[in] n number of blocks to write | ||
824 | * | ||
825 | * @return The operation status. | ||
826 | * @retval HAL_SUCCESS operation succeeded. | ||
827 | * @retval HAL_FAILED operation failed. | ||
828 | * | ||
829 | * @api | ||
830 | */ | ||
831 | bool sdcWrite(SDCDriver *sdcp, uint32_t startblk, | ||
832 | const uint8_t *buf, uint32_t n) { | ||
833 | bool status; | ||
834 | |||
835 | osalDbgCheck((sdcp != NULL) && (buf != NULL) && (n > 0U)); | ||
836 | osalDbgAssert(sdcp->state == BLK_READY, "invalid state"); | ||
837 | |||
838 | if ((startblk + n - 1U) > sdcp->capacity) { | ||
839 | sdcp->errors |= SDC_OVERFLOW_ERROR; | ||
840 | return HAL_FAILED; | ||
841 | } | ||
842 | |||
843 | /* Write operation in progress.*/ | ||
844 | sdcp->state = BLK_WRITING; | ||
845 | |||
846 | status = sdc_lld_write(sdcp, startblk, buf, n); | ||
847 | |||
848 | /* Write operation finished.*/ | ||
849 | sdcp->state = BLK_READY; | ||
850 | return status; | ||
851 | } | ||
852 | |||
853 | /** | ||
854 | * @brief Returns the errors mask associated to the previous operation. | ||
855 | * | ||
856 | * @param[in] sdcp pointer to the @p SDCDriver object | ||
857 | * @return The errors mask. | ||
858 | * | ||
859 | * @api | ||
860 | */ | ||
861 | sdcflags_t sdcGetAndClearErrors(SDCDriver *sdcp) { | ||
862 | sdcflags_t flags; | ||
863 | |||
864 | osalDbgCheck(sdcp != NULL); | ||
865 | osalDbgAssert(sdcp->state == BLK_READY, "invalid state"); | ||
866 | |||
867 | osalSysLock(); | ||
868 | flags = sdcp->errors; | ||
869 | sdcp->errors = SDC_NO_ERROR; | ||
870 | osalSysUnlock(); | ||
871 | return flags; | ||
872 | } | ||
873 | |||
874 | /** | ||
875 | * @brief Waits for card idle condition. | ||
876 | * | ||
877 | * @param[in] sdcp pointer to the @p SDCDriver object | ||
878 | * | ||
879 | * @return The operation status. | ||
880 | * @retval HAL_SUCCESS the operation succeeded. | ||
881 | * @retval HAL_FAILED the operation failed. | ||
882 | * | ||
883 | * @api | ||
884 | */ | ||
885 | bool sdcSync(SDCDriver *sdcp) { | ||
886 | bool result; | ||
887 | |||
888 | osalDbgCheck(sdcp != NULL); | ||
889 | |||
890 | if (sdcp->state != BLK_READY) { | ||
891 | return HAL_FAILED; | ||
892 | } | ||
893 | |||
894 | /* Synchronization operation in progress.*/ | ||
895 | sdcp->state = BLK_SYNCING; | ||
896 | |||
897 | result = sdc_lld_sync(sdcp); | ||
898 | |||
899 | /* Synchronization operation finished.*/ | ||
900 | sdcp->state = BLK_READY; | ||
901 | return result; | ||
902 | } | ||
903 | |||
904 | /** | ||
905 | * @brief Returns the media info. | ||
906 | * | ||
907 | * @param[in] sdcp pointer to the @p SDCDriver object | ||
908 | * @param[out] bdip pointer to a @p BlockDeviceInfo structure | ||
909 | * | ||
910 | * @return The operation status. | ||
911 | * @retval HAL_SUCCESS the operation succeeded. | ||
912 | * @retval HAL_FAILED the operation failed. | ||
913 | * | ||
914 | * @api | ||
915 | */ | ||
916 | bool sdcGetInfo(SDCDriver *sdcp, BlockDeviceInfo *bdip) { | ||
917 | |||
918 | osalDbgCheck((sdcp != NULL) && (bdip != NULL)); | ||
919 | |||
920 | if (sdcp->state != BLK_READY) { | ||
921 | return HAL_FAILED; | ||
922 | } | ||
923 | |||
924 | bdip->blk_num = sdcp->capacity; | ||
925 | bdip->blk_size = MMCSD_BLOCK_SIZE; | ||
926 | |||
927 | return HAL_SUCCESS; | ||
928 | } | ||
929 | |||
930 | /** | ||
931 | * @brief Erases the supplied blocks. | ||
932 | * | ||
933 | * @param[in] sdcp pointer to the @p SDCDriver object | ||
934 | * @param[in] startblk starting block number | ||
935 | * @param[in] endblk ending block number | ||
936 | * | ||
937 | * @return The operation status. | ||
938 | * @retval HAL_SUCCESS the operation succeeded. | ||
939 | * @retval HAL_FAILED the operation failed. | ||
940 | * | ||
941 | * @api | ||
942 | */ | ||
943 | bool sdcErase(SDCDriver *sdcp, uint32_t startblk, uint32_t endblk) { | ||
944 | uint32_t resp[1]; | ||
945 | |||
946 | osalDbgCheck((sdcp != NULL)); | ||
947 | osalDbgAssert(sdcp->state == BLK_READY, "invalid state"); | ||
948 | |||
949 | /* Erase operation in progress.*/ | ||
950 | sdcp->state = BLK_WRITING; | ||
951 | |||
952 | /* Handling command differences between HC and normal cards.*/ | ||
953 | if ((sdcp->cardmode & SDC_MODE_HIGH_CAPACITY) != 0U) { | ||
954 | startblk *= MMCSD_BLOCK_SIZE; | ||
955 | endblk *= MMCSD_BLOCK_SIZE; | ||
956 | } | ||
957 | |||
958 | if (_sdc_wait_for_transfer_state(sdcp)) { | ||
959 | goto failed; | ||
960 | } | ||
961 | |||
962 | if ((sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_ERASE_RW_BLK_START, | ||
963 | startblk, resp) != HAL_SUCCESS) || | ||
964 | MMCSD_R1_ERROR(resp[0])) { | ||
965 | goto failed; | ||
966 | } | ||
967 | |||
968 | if ((sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_ERASE_RW_BLK_END, | ||
969 | endblk, resp) != HAL_SUCCESS) || | ||
970 | MMCSD_R1_ERROR(resp[0])) { | ||
971 | goto failed; | ||
972 | } | ||
973 | |||
974 | if ((sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_ERASE, | ||
975 | 0, resp) != HAL_SUCCESS) || | ||
976 | MMCSD_R1_ERROR(resp[0])) { | ||
977 | goto failed; | ||
978 | } | ||
979 | |||
980 | /* Quick sleep to allow it to transition to programming or receiving state */ | ||
981 | /* CHTODO: ??????????????????????????? */ | ||
982 | |||
983 | /* Wait for it to return to transfer state to indicate it has finished erasing */ | ||
984 | if (_sdc_wait_for_transfer_state(sdcp)) { | ||
985 | goto failed; | ||
986 | } | ||
987 | |||
988 | sdcp->state = BLK_READY; | ||
989 | return HAL_SUCCESS; | ||
990 | |||
991 | failed: | ||
992 | sdcp->state = BLK_READY; | ||
993 | return HAL_FAILED; | ||
994 | } | ||
995 | |||
996 | #endif /* HAL_USE_SDC == TRUE */ | ||
997 | |||
998 | /** @} */ | ||
999 | |||