Morse Micro IoT SDK  2.9.7
sdio_spi.c
1/*
2 * Copyright 2023 Morse Micro
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7#include "porting_assistant.h"
8#include "sdio_spi.h"
9#include "mmhal.h"
10
11
12/* Series of defines used when writing to the MM chip */
13#define MORSE_REG_ADDRESS_BASE 0x10000
14#define MORSE_REG_ADDRESS_WINDOW_0 MORSE_REG_ADDRESS_BASE
15#define MORSE_REG_ADDRESS_WINDOW_1 (MORSE_REG_ADDRESS_BASE + 1)
16#define MORSE_REG_ADDRESS_CONFIG (MORSE_REG_ADDRESS_BASE + 2)
17
18#define MORSE_CONFIG_ACCESS_1BYTE 0
19#define MORSE_CONFIG_ACCESS_2BYTE 1
20#define MORSE_CONFIG_ACCESS_4BYTE 2
21
23#define PACK_LE16(dst16_data, src8_array) \
24 do { \
25 dst16_data = *src8_array; \
26 dst16_data |= ((uint16_t)*(src8_array+1) << 8); \
27 } while (0)
28
30#define PACK_BE16(dst16_data, src8_array) \
31 do { \
32 dst16_data = *(src8_array+1); \
33 dst16_data |= ((uint16_t)*(src8_array) << 8); \
34 } while (0)
35
37#define UNPACK_LE16(dst8_array, src16_data) \
38 do { \
39 *dst8_array = (uint8_t)(src16_data); \
40 *(dst8_array+1) = (uint8_t)(src16_data >> 8); \
41 } while (0)
42
44#define UNPACK_BE16(dst8_array, src16_data) \
45 do { \
46 *(dst8_array+1) = (uint8_t)(src16_data); \
47 *(dst8_array) = (uint8_t)(src16_data >> 8); \
48 } while (0)
49
51#define PACK_LE32(dst32_data, src8_array) \
52 do { \
53 dst32_data = *src8_array; \
54 dst32_data |= ((uint32_t)*(src8_array+1) << 8); \
55 dst32_data |= ((uint32_t)*(src8_array+2) << 16); \
56 dst32_data |= ((uint32_t)*(src8_array+3) << 24); \
57 } while (0)
58
60#define PACK_BE32(dst32_data, src8_array) \
61 do { \
62 dst32_data = *(src8_array+3); \
63 dst32_data |= ((uint32_t)*(src8_array+2) << 8); \
64 dst32_data |= ((uint32_t)*(src8_array+1) << 16); \
65 dst32_data |= ((uint32_t)*(src8_array) << 24); \
66 } while (0)
67
69#define UNPACK_LE32(dst8_array, src32_data) \
70 do { \
71 *dst8_array = (uint8_t)(src32_data); \
72 *(dst8_array+1) = (uint8_t)(src32_data >> 8); \
73 *(dst8_array+2) = (uint8_t)(src32_data >> 16); \
74 *(dst8_array+3) = (uint8_t)(src32_data >> 24); \
75 } while (0)
76
78#define UNPACK_BE32(dst8_array, src32_data) \
79 do { \
80 *(dst8_array+3) = (uint8_t)(src32_data); \
81 *(dst8_array+2) = (uint8_t)(src32_data >> 8); \
82 *(dst8_array+1) = (uint8_t)(src32_data >> 16); \
83 *(dst8_array) = (uint8_t)(src32_data >> 24); \
84 } while (0)
85
87#define PACK_LE64(dst64_data, src8_array) \
88 do { \
89 dst64_data = ((uint64_t)*(src8_array+0) << 0); \
90 dst64_data |= ((uint64_t)*(src8_array+1) << 8); \
91 dst64_data |= ((uint64_t)*(src8_array+2) << 16); \
92 dst64_data |= ((uint64_t)*(src8_array+3) << 24); \
93 dst64_data |= ((uint64_t)*(src8_array+4) << 32); \
94 dst64_data |= ((uint64_t)*(src8_array+5) << 40); \
95 dst64_data |= ((uint64_t)*(src8_array+6) << 48); \
96 dst64_data |= ((uint64_t)*(src8_array+7) << 56); \
97 } while (0)
98
100#define PACK_BE64(dst64_data, src8_array) \
101 do { \
102 dst64_data = ((uint64_t)*(src8_array+7) << 0); \
103 dst64_data |= ((uint64_t)*(src8_array+6) << 8); \
104 dst64_data |= ((uint64_t)*(src8_array+5) << 16); \
105 dst64_data |= ((uint64_t)*(src8_array+4) << 24); \
106 dst64_data |= ((uint64_t)*(src8_array+3) << 32); \
107 dst64_data |= ((uint64_t)*(src8_array+2) << 40); \
108 dst64_data |= ((uint64_t)*(src8_array+1) << 48); \
109 dst64_data |= ((uint64_t)*(src8_array+0) << 56); \
110 } while (0)
111
113#define UNPACK_LE64(dst8_array, src64_data) \
114 do { \
115 *(dst8_array+0) = (uint8_t)(src64_data >> 0); \
116 *(dst8_array+1) = (uint8_t)(src64_data >> 8); \
117 *(dst8_array+2) = (uint8_t)(src64_data >> 16); \
118 *(dst8_array+3) = (uint8_t)(src64_data >> 24); \
119 *(dst8_array+4) = (uint8_t)(src64_data >> 32); \
120 *(dst8_array+5) = (uint8_t)(src64_data >> 40); \
121 *(dst8_array+6) = (uint8_t)(src64_data >> 48); \
122 *(dst8_array+7) = (uint8_t)(src64_data >> 56); \
123 } while (0)
124
126#define UNPACK_BE64(dst8_array, src64_data) \
127 do { \
128 *(dst8_array+7) = (uint8_t)(src64_data >> 0); \
129 *(dst8_array+6) = (uint8_t)(src64_data >> 8); \
130 *(dst8_array+5) = (uint8_t)(src64_data >> 16); \
131 *(dst8_array+4) = (uint8_t)(src64_data >> 24); \
132 *(dst8_array+3) = (uint8_t)(src64_data >> 32); \
133 *(dst8_array+2) = (uint8_t)(src64_data >> 40); \
134 *(dst8_array+1) = (uint8_t)(src64_data >> 48); \
135 *(dst8_array+0) = (uint8_t)(src64_data >> 56); \
136 } while (0)
137
138
139/* MAX blocks for single CMD53 read/write*/
140#define CMD53_MAX_BLOCKS 128
141
142enum block_size
143{
144 BLOCK_SIZE_FN1 = 8,
145 BLOCK_SIZE_FN1_LOG2 = 3,
146 BLOCK_SIZE_FN2 = 512,
147 BLOCK_SIZE_FN2_LOG2 = 9,
148};
149
150enum max_block_transfer_size
151{
152 MAX_BLOCK_TRANSFER_SIZE_FN1 = BLOCK_SIZE_FN1 * CMD53_MAX_BLOCKS,
153 MAX_BLOCK_TRANSFER_SIZE_FN2 = BLOCK_SIZE_FN2 * CMD53_MAX_BLOCKS,
154};
155
156
157/* MORSE set chip active for CMD62 and CMD63 */
158#define CHIP_ACTIVE_SEQ (0x00000000)
159#define MAX_RETRY 3
160
162enum sdio_direction
163{
164 SDIO_DIR_CARD_TO_HOST = 0,
165 SDIO_DIR_HOST_TO_CARD = 1 << 6,
166};
167
171enum sdio_cmd_index
172{
174 SDIO_CMD0 = 0,
176 SDIO_CMD52 = 52,
179 SDIO_CMD53 = 53,
181 SDIO_CMD63 = 63,
182};
183
184/*
185 * SDIO Card Common Control Register Flags, per SDIO Specification Version 4.10, Part E1,
186 * Section 6.9.
187 */
188
189#define SDIO_CCCR_IEN_ADDR 0x04u
190#define SDIO_CCCR_IEN_IENM (1u)
191#define SDIO_CCCR_IEN_IEN1 (1u << 1)
192
193#define SDIO_CCCR_BIC_ADDR 0x07u
194#define SDIO_CCCR_BIC_ECSI (1u << 5)
195
196
197static inline uint32_t min_u32(uint32_t a, uint32_t b)
198{
199 if (a < b)
200 {
201 return a;
202 }
203 else
204 {
205 return b;
206 }
207}
208
220static int morse_cmd52_write(uint32_t address, uint8_t data, enum mmhal_sdio_function function)
221{
222 uint32_t arg = mmhal_make_cmd52_arg(MMHAL_SDIO_WRITE, function, address, data);
223
225
226 return sdio_spi_send_cmd(SDIO_CMD52, arg, NULL);
227}
228
243static int morse_cmd53_read(enum mmhal_sdio_function function, uint32_t address,
244 uint8_t *data, uint32_t len)
245{
246 int result = -1;
247
248 enum block_size block_size = BLOCK_SIZE_FN2;
249 enum block_size block_size_log2 = BLOCK_SIZE_FN2_LOG2;
250
251 if (function == MMHAL_SDIO_FUNCTION_1)
252 {
253 block_size = BLOCK_SIZE_FN1;
254 block_size_log2 = BLOCK_SIZE_FN1_LOG2;
255 }
256
257 /* Attempt to read as many blocks as possible */
258 uint16_t num_blocks = len >> block_size_log2;
259 if (num_blocks > 0)
260 {
261 struct mmhal_wlan_sdio_cmd53_read_args args = {
263 address & 0x0000ffff, num_blocks),
264 .data = data,
265 .transfer_length = num_blocks,
266 .block_size = BLOCK_SIZE_FN2,
267 };
268
269 result = mmhal_wlan_sdio_cmd53_read(&args);
270 if (result != 0)
271 {
272 goto exit;
273 }
274
275 uint32_t transfer_size = num_blocks * block_size;
276 address += transfer_size;
277 data += transfer_size;
278 len -= transfer_size;
279 }
280
281 /* Now we use byte mode to read anything that was left over. */
282 if (len > 0)
283 {
284 struct mmhal_wlan_sdio_cmd53_read_args args = {
285 .sdio_arg = mmhal_make_cmd53_arg(MMHAL_SDIO_READ, function, MMHAL_SDIO_MODE_BYTE,
286 address & 0x0000ffff, len),
287 .data = data,
288 .transfer_length = len,
289 .block_size = 0,
290 };
291
292 result = mmhal_wlan_sdio_cmd53_read(&args);
293 }
294
295exit:
296 return result;
297}
298
313static int morse_cmd53_write(enum mmhal_sdio_function function, uint32_t address,
314 const uint8_t *data, uint32_t len)
315{
316 int result = MMHAL_SDIO_OTHER_ERROR;
317
318 enum block_size block_size = BLOCK_SIZE_FN2;
319 enum block_size block_size_log2 = BLOCK_SIZE_FN2_LOG2;
320
321 if (function == MMHAL_SDIO_FUNCTION_1)
322 {
323 block_size = BLOCK_SIZE_FN1;
324 block_size_log2 = BLOCK_SIZE_FN1_LOG2;
325 }
326
327 /* Attempt to write as many blocks as possible */
328 uint16_t num_blocks = len >> block_size_log2;
329 if (num_blocks > 0)
330 {
333 address & 0x0000ffff, num_blocks),
334 .data = (uint8_t *)data,
335 .transfer_length = num_blocks,
336 .block_size = BLOCK_SIZE_FN2,
337 };
338
339 result = mmhal_wlan_sdio_cmd53_write(&args);
340 if (result != 0)
341 {
342 goto exit;
343 }
344
345 uint32_t transfer_size = num_blocks * block_size;
346 address += transfer_size;
347 data += transfer_size;
348 len -= transfer_size;
349 }
350
351 /* Now we use byte mode to write anything that was left over. */
352 if (len > 0)
353 {
355 .sdio_arg = mmhal_make_cmd53_arg(MMHAL_SDIO_WRITE, function, MMHAL_SDIO_MODE_BYTE,
356 address & 0x0000ffff, len),
357 .data = (uint8_t *)data,
358 .transfer_length = len,
359 .block_size = 0,
360 };
361
362 result = mmhal_wlan_sdio_cmd53_write(&args);
363 }
364
365exit:
366 return result;
367}
368
379static int morse_address_base_set(uint32_t address, uint8_t access,
380 enum mmhal_sdio_function function)
381{
382 int result;
383
384 address &= 0xFFFF0000;
385
386 MMOSAL_ASSERT(access <= MORSE_CONFIG_ACCESS_4BYTE);
387
388 result = morse_cmd52_write(MORSE_REG_ADDRESS_WINDOW_0, (uint8_t)(address >> 16),
389 function);
390 if (result != 0)
391 {
392 goto exit;
393 }
394
395 result = morse_cmd52_write(MORSE_REG_ADDRESS_WINDOW_1, (uint8_t)(address >> 24),
396 function);
397 if (result != 0)
398 {
399 goto exit;
400 }
401
402 result = morse_cmd52_write(MORSE_REG_ADDRESS_CONFIG, access, function);
403 if (result != 0)
404 {
405 goto exit;
406 }
407
408exit:
409 return result;
410}
411
412int sdio_spi_read_le32(uint32_t address, uint32_t *data)
413{
414 int result = -1;
415 uint8_t receive_data[4];
416
417 if (data == NULL)
418 {
420 }
421
423
424 result = morse_address_base_set(address, MORSE_CONFIG_ACCESS_4BYTE, function);
425 if (result != 0)
426 {
427 goto exit;
428 }
429
430 result = morse_cmd53_read(function, address, receive_data, sizeof(receive_data));
431 if (result != 0)
432 {
433 goto exit;
434 }
435
436 PACK_LE32(*data, receive_data);
437
438exit:
439 return result;
440}
441
442int sdio_spi_read_multi_byte(uint32_t address, uint8_t *data, uint32_t len)
443{
444 int result = -1;
446 enum max_block_transfer_size max_transfer_size = MAX_BLOCK_TRANSFER_SIZE_FN2;
447
448 /* Length must be a non-zero multiple of 4 */
449 if (len == 0 || (len & 0x03) != 0)
450 {
451 printf("Invalid length %lu\n", len);
453 goto exit;
454 }
455
456 /* Reads cannot cross 64K boundaries, so we may need to do several operations
457 * to read the all the data. */
458 while (len > 0)
459 {
460 result = morse_address_base_set(address, MORSE_CONFIG_ACCESS_4BYTE, function);
461 if (result != 0)
462 {
463 goto exit;
464 }
465
466 /* We first calculate the number of bytes to transfer on this iteration of the loop. */
467 uint32_t size = min_u32(len, max_transfer_size); // NOLINT(build/include_what_you_use)
468
469 /* Read operations cannot cross the 64K boundary. We truncate the operation if this is
470 * is the case. */
471 uint32_t next_boundary = (address & 0xFFFF0000) + 0x10000;
472 if ((address + size) > next_boundary)
473 {
474 size = next_boundary - address;
475 }
476
477 morse_cmd53_read(function, address, data, size);
478
479 /*
480 * Observed sometimes that SDIO read repeats the first 4-bytes word twice,
481 * overwriting second word (hence, tail will be overwritten with 'sync' byte). When
482 * this happens, reading will fetch the correct word.
483 * NB: if repeated again, pass it anyway and upper layers will handle it
484 */
485 if ((size >= 8) && !memcmp(data, data+4, 4)) {
486 /* Lets try one more time before passing up */
487 printf("Corrupt Payload. Re-Read first 8 bytes\n");
488 morse_cmd53_read(function, address, data, 8);
489 }
490
491 address += size;
492 data += size;
493 len -= size;
494 }
495
496exit:
497 return result;
498}
499
500int sdio_spi_write_multi_byte(uint32_t address, const uint8_t *data, uint32_t len)
501{
502 int result = -1;
504 enum max_block_transfer_size max_transfer_size = MAX_BLOCK_TRANSFER_SIZE_FN2;
505
506 /* Length must be a non-zero multiple of 4 */
507 if (len == 0 || (len & 0x03) != 0)
508 {
509 printf("Invalid length %lu\n", len);
511 goto exit;
512 }
513
514 /* Writes cannot cross 64K boundaries, so we may need to do several operations
515 * to write the all the given data. */
516 while (len > 0)
517 {
518 result = morse_address_base_set(address, MORSE_CONFIG_ACCESS_4BYTE, function);
519 if (result != 0)
520 {
521 goto exit;
522 }
523
524 /* We first calculate the number of bytes to transfer on this iteration of the loop. */
525 uint32_t size = min_u32(len, max_transfer_size); // NOLINT(build/include_what_you_use)
526
527 /* Write operations cannot cross the 64K boundary. We truncate the operation if this is
528 * is the case. */
529 uint32_t next_boundary = (address & 0xFFFF0000) + 0x10000;
530 if ((address + size) > next_boundary)
531 {
532 size = next_boundary - address;
533 }
534
535 result = morse_cmd53_write(function, address, data, size);
536 if (result != 0)
537 {
538 goto exit;
539 }
540
541 address += size;
542 data += size;
543 len -= size;
544 }
545
546exit:
547 return result;
548}
549
550int sdio_spi_write_le32(uint32_t address, uint32_t data)
551{
552 int result = -1;
554
555 result = morse_address_base_set(address, MORSE_CONFIG_ACCESS_4BYTE, function);
556 if (result != 0)
557 {
558 goto exit;
559 }
560
561 result = morse_cmd53_write(function, address, (uint8_t*)&data, sizeof(data));
562
563exit:
564 return result;
565}
566
567int sdio_spi_send_cmd(uint8_t cmd_idx, uint32_t arg, uint32_t *rsp)
568{
569 return mmhal_wlan_sdio_cmd(cmd_idx, arg, rsp);
570}
571
572int sdio_spi_update_le32(uint32_t address, uint32_t mask, uint32_t value)
573{
574 int result = -1;
575 uint32_t reg_value;
576 result = sdio_spi_read_le32(address, &reg_value);
577 if (result < 0)
578 {
579 return result;
580 }
581 reg_value |= (value & mask);
582 reg_value &= (value | ~mask);
583 result = sdio_spi_write_le32(address, reg_value);
584 if (result < 0)
585 {
586 return result;
587 }
588 return result;
589}
static uint32_t mmhal_make_cmd53_arg(enum mmhal_sdio_rw rw, enum mmhal_sdio_function fn, enum mmhal_sdio_mode mode, uint32_t address, uint16_t count)
Construct an SDIO CMD53 argument based on the given arguments.
Definition: mmhal_wlan.h:684
static uint32_t mmhal_make_cmd52_arg(enum mmhal_sdio_rw rw, enum mmhal_sdio_function fn, uint32_t address, uint8_t write_data)
Construct an SDIO CMD52 argument based on the given arguments.
Definition: mmhal_wlan.h:659
mmhal_sdio_function
SDIO CMD52/CMD53 function number.
Definition: mmhal_wlan.h:613
#define MMHAL_SDIO_ADDRESS_MAX
CMD52/53 Register Address maximum value.
Definition: mmhal_wlan.h:638
@ MMHAL_SDIO_MODE_BLOCK
Byte mode.
Definition: mmhal_wlan.h:623
@ MMHAL_SDIO_READ
Read operation.
Definition: mmhal_wlan.h:607
@ MMHAL_SDIO_WRITE
Write operation.
Definition: mmhal_wlan.h:608
@ MMHAL_SDIO_FUNCTION_1
Function 0.
Definition: mmhal_wlan.h:615
@ MMHAL_SDIO_FUNCTION_2
Function 1.
Definition: mmhal_wlan.h:616
int mmhal_wlan_sdio_cmd53_read(const struct mmhal_wlan_sdio_cmd53_read_args *args)
Execute an SDIO CMD53 read.
int mmhal_wlan_sdio_cmd53_write(const struct mmhal_wlan_sdio_cmd53_write_args *args)
Execute an SDIO CMD53 write.
int mmhal_wlan_sdio_cmd(uint8_t cmd_idx, uint32_t arg, uint32_t *rsp)
Execute an SDIO command without data.
@ MMHAL_SDIO_INVALID_ARGUMENT
Invalid argument given (e.g., incorrect buffer alignment).
Definition: mmhal_wlan.h:490
@ MMHAL_SDIO_OTHER_ERROR
Another error not covered by the above error codes.
Definition: mmhal_wlan.h:506
#define MMOSAL_ASSERT(expr)
Assert that the given expression evaluates to true and abort execution if not.
Definition: mmosal.h:927
Arguments structure for mmhal_wlan_sdio_cmd53_read().
Definition: mmhal_wlan.h:564
uint16_t block_size
If non-zero this indicates that the data should be transferred in block mode with the given block siz...
Definition: mmhal_wlan.h:578
uint32_t sdio_arg
The SDIO argument.
Definition: mmhal_wlan.h:567
uint8_t * data
Pointer to the data buffer to receive the data.
Definition: mmhal_wlan.h:569
Arguments structure for mmhal_wlan_sdio_cmd53_write().
Definition: mmhal_wlan.h:534
const uint8_t * data
Pointer to the data buffer.
Definition: mmhal_wlan.h:539
uint16_t block_size
If non-zero this indicates that the data should be transferred in block mode with the given block siz...
Definition: mmhal_wlan.h:548
uint16_t transfer_length
Transfer length measured in blocks if block_size is non-zero otherwise in bytes.
Definition: mmhal_wlan.h:542
uint32_t sdio_arg
The SDIO argument.
Definition: mmhal_wlan.h:537