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