ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
board_periph_sdhi.c
Go to the documentation of this file.
1
37
38#include <stdint.h>
39#include <stdio.h>
40#include <string.h>
41
42#include "board_console.h"
43#include "board_periph_block.h"
44#include "board_periph_sd.h"
46
48typedef enum : uint32_t {
51
70
72typedef enum : uint32_t {
73 k_sdhi_info1_rspend = 0x00000001U,
74 k_sdhi_info2_bre = 0x00000100U,
75 k_sdhi_info2_bwe = 0x00000200U,
77
86
104
106typedef enum : uint32_t {
107 k_sdhi_cmd_index_mask = 0x0000003FU,
108 k_sdhi_r1_app_cmd = 0x00000020U,
109 k_sdhi_r1_ready = 0x00000900U,
110 k_sdhi_r7_if_cond = 0x000001AAU,
111 k_sdhi_ocr_ready = 0xC0FF8000U,
112 k_sdhi_cid_word = 0x52413844U,
113 k_sdhi_rca_value = 0x00010000U,
114 k_sdhi_csd_v2_struct = 0x40000000U,
115 k_sdhi_csize_lo_mask = 0x0000003FU,
116 k_sdhi_csize_mid_mask = 0x0000FFFFU,
118
129
131typedef enum : uint32_t {
134
136typedef enum : uint8_t {
141
143typedef struct {
144 uint32_t regs[k_sdhi_words];
145 uint32_t rsp[4];
147 uint32_t word_idx;
148 uint32_t lba;
149 uint32_t blocks_left;
150 uint8_t app_cmd;
151 uint8_t xfer;
152 uint32_t reads;
153 uint32_t writes;
155
157
169RA8_INTERNAL static uint32_t internal_sdhi_word(uint64_t off)
170{
171 return (uint32_t)(off / 4U);
172}
173
189
199{
200 uint64_t bytes = 0U;
201 board_sd_info(nullptr, &bytes, nullptr, nullptr);
202 uint32_t cap_blocks = (uint32_t)(bytes / (uint64_t)k_sdhi_block_bytes);
203 if (cap_blocks < (uint32_t)k_sdhi_csize_unit) {
204 cap_blocks = (uint32_t)k_sdhi_csize_unit; /* floor: keep c_size non-negative. */
205 }
206 const uint32_t c_size = (cap_blocks / (uint32_t)k_sdhi_csize_unit) - 1U;
207 s_sdhi.rsp[3] = (uint32_t)k_sdhi_csd_v2_struct;
208 s_sdhi.rsp[2] = (c_size >> (uint32_t)k_sdhi_csize_shift) & (uint32_t)k_sdhi_csize_lo_mask;
209 s_sdhi.rsp[1] = (c_size & (uint32_t)k_sdhi_csize_mid_mask) << (uint32_t)k_sdhi_csize_shift;
210 s_sdhi.rsp[0] = 0U;
211}
212
222{
223 (void)memset(s_sdhi.stage, 0, sizeof(s_sdhi.stage));
224 (void)board_sd_read_block(s_sdhi.lba, s_sdhi.stage);
225 s_sdhi.word_idx = 0U;
227}
228
239{
240 s_sdhi.xfer = (uint8_t)k_sdhi_xfer_read;
241 s_sdhi.blocks_left = multi ? s_sdhi.regs[internal_sdhi_word(k_sdhi_off_sd_seccnt)] : 1U;
242 if (s_sdhi.blocks_left == 0U) {
243 s_sdhi.blocks_left = 1U;
244 }
245 /* Console SD tab: one line per block-read command (CMD17/CMD18). */
247 (void)snprintf(ln,
248 sizeof(ln),
249 "SDHI rd lba=%u x%u",
250 (unsigned)s_sdhi.lba,
251 (unsigned)s_sdhi.blocks_left);
254}
255
266{
267 s_sdhi.xfer = (uint8_t)k_sdhi_xfer_write;
268 s_sdhi.blocks_left = multi ? s_sdhi.regs[internal_sdhi_word(k_sdhi_off_sd_seccnt)] : 1U;
269 if (s_sdhi.blocks_left == 0U) {
270 s_sdhi.blocks_left = 1U;
271 }
272 (void)memset(s_sdhi.stage, 0, sizeof(s_sdhi.stage));
273 s_sdhi.word_idx = 0U;
275 /* Console SD tab: one line per block-write command (CMD24/CMD25). */
277 (void)snprintf(ln,
278 sizeof(ln),
279 "SDHI wr lba=%u x%u",
280 (unsigned)s_sdhi.lba,
281 (unsigned)s_sdhi.blocks_left);
283}
284
295RA8_INTERNAL static void internal_sdhi_exec_ident(uint32_t idx, uint32_t arg)
296{
297 (void)arg;
298 if ((idx == (uint32_t)k_sdhi_acmd41_op_cond) && (s_sdhi.app_cmd != 0U)) {
299 s_sdhi.rsp[0] = (uint32_t)k_sdhi_ocr_ready;
300 return;
301 }
302 switch (idx) {
303 case (uint32_t)k_sdhi_cmd8_if_cond:
304 s_sdhi.rsp[0] = (uint32_t)k_sdhi_r7_if_cond;
305 break;
306 case (uint32_t)k_sdhi_cmd55_app_cmd:
307 s_sdhi.app_cmd = 1U;
308 /* Echo APP_CMD so the ACMD6 SET_BUS_WIDTH that may follow is
309 * honoured by the driver's 4-bit negotiation check. */
310 s_sdhi.rsp[0] = (uint32_t)k_sdhi_r1_ready | (uint32_t)k_sdhi_r1_app_cmd;
311 break;
312 case (uint32_t)k_sdhi_cmd2_send_cid:
313 s_sdhi.rsp[0] = (uint32_t)k_sdhi_cid_word;
314 s_sdhi.rsp[1] = (uint32_t)k_sdhi_cid_word;
315 s_sdhi.rsp[2] = (uint32_t)k_sdhi_cid_word;
316 s_sdhi.rsp[3] = (uint32_t)k_sdhi_cid_word;
317 break;
318 case (uint32_t)k_sdhi_cmd3_send_rca:
319 s_sdhi.rsp[0] = (uint32_t)k_sdhi_rca_value;
320 break;
321 case (uint32_t)k_sdhi_cmd9_send_csd:
323 break;
324 default:
325 s_sdhi.rsp[0] = (uint32_t)k_sdhi_r1_ready;
326 break;
327 }
328}
329
340{
341 const uint32_t idx = cmd & (uint32_t)k_sdhi_cmd_index_mask;
342 const uint32_t arg = s_sdhi.regs[internal_sdhi_word(k_sdhi_off_sd_arg)];
343
344 s_sdhi.rsp[0] = (uint32_t)k_sdhi_r1_ready;
345 s_sdhi.rsp[1] = 0U;
346 s_sdhi.rsp[2] = 0U;
347 s_sdhi.rsp[3] = 0U;
348
349 switch (idx) {
350 case (uint32_t)k_sdhi_cmd17_read1:
351 s_sdhi.lba = arg;
353 break;
354 case (uint32_t)k_sdhi_cmd18_readm:
355 s_sdhi.lba = arg;
357 break;
358 case (uint32_t)k_sdhi_cmd24_write1:
359 s_sdhi.lba = arg;
361 break;
362 case (uint32_t)k_sdhi_cmd25_writem:
363 s_sdhi.lba = arg;
365 break;
366 case (uint32_t)k_sdhi_cmd12_stop:
367 s_sdhi.xfer = (uint8_t)k_sdhi_xfer_none;
368 break;
369 default:
370 internal_sdhi_exec_ident(idx, arg);
371 break;
372 }
373 /* CMD55 sets app_cmd; every other command clears the one-shot prefix. */
374 if (idx != (uint32_t)k_sdhi_cmd55_app_cmd) {
375 s_sdhi.app_cmd = 0U;
376 }
379}
380
392{
393 if (s_sdhi.xfer != (uint8_t)k_sdhi_xfer_read) {
394 return 0U;
395 }
396 const uint32_t b = s_sdhi.word_idx * 4U;
397 const uint32_t word = (uint32_t)s_sdhi.stage[b] |
398 ((uint32_t)s_sdhi.stage[b + 1U] << (uint32_t)k_sdhi_byte_bits) |
399 ((uint32_t)s_sdhi.stage[b + 2U] << (2U * (uint32_t)k_sdhi_byte_bits)) |
400 ((uint32_t)s_sdhi.stage[b + 3U] << (3U * (uint32_t)k_sdhi_byte_bits));
401 s_sdhi.word_idx++;
402 if (s_sdhi.word_idx < (uint32_t)k_sdhi_words_per_blk) {
403 return word;
404 }
405 /* Block drained: advance a multi-block read, else end the phase. */
406 s_sdhi.reads++;
407 if (s_sdhi.blocks_left > 0U) {
408 s_sdhi.blocks_left--;
409 }
410 if (s_sdhi.blocks_left > 0U) {
411 s_sdhi.lba++;
413 } else {
414 s_sdhi.xfer = (uint8_t)k_sdhi_xfer_none;
416 }
417 return word;
418}
419
429RA8_INTERNAL static void internal_sdhi_buf_write(uint32_t word)
430{
431 if (s_sdhi.xfer != (uint8_t)k_sdhi_xfer_write) {
432 return;
433 }
434 const uint32_t b = s_sdhi.word_idx * 4U;
435 s_sdhi.stage[b] = (uint8_t)(word & (uint32_t)k_sdhi_byte_mask);
436 s_sdhi.stage[b + 1U] =
437 (uint8_t)((word >> (uint32_t)k_sdhi_byte_bits) & (uint32_t)k_sdhi_byte_mask);
438 s_sdhi.stage[b + 2U] =
439 (uint8_t)((word >> (2U * (uint32_t)k_sdhi_byte_bits)) & (uint32_t)k_sdhi_byte_mask);
440 s_sdhi.stage[b + 3U] =
441 (uint8_t)((word >> (3U * (uint32_t)k_sdhi_byte_bits)) & (uint32_t)k_sdhi_byte_mask);
442 s_sdhi.word_idx++;
443 if (s_sdhi.word_idx < (uint32_t)k_sdhi_words_per_blk) {
444 return;
445 }
446 /* Block filled: commit it, advance a multi-block write, else end the phase. */
447 (void)board_sd_write_block(s_sdhi.lba, s_sdhi.stage);
448 s_sdhi.writes++;
449 s_sdhi.word_idx = 0U;
450 if (s_sdhi.blocks_left > 0U) {
451 s_sdhi.blocks_left--;
452 }
453 if (s_sdhi.blocks_left > 0U) {
454 s_sdhi.lba++;
455 } else {
456 s_sdhi.xfer = (uint8_t)k_sdhi_xfer_none;
458 }
459}
460
474RA8_INTERNAL static uint64_t internal_sdhi_read(uc_engine* uc, uint64_t addr, unsigned size)
475{
476 (void)uc;
477 (void)size;
478 const uint64_t off = addr - (uint64_t)k_sdhi_base;
479 if (off == (uint64_t)k_sdhi_off_sd_buf0) {
480 return (uint64_t)internal_sdhi_buf_read();
481 }
482 if (internal_sdhi_word(off) >= (uint32_t)k_sdhi_words) {
483 return 0U;
484 }
485 return (uint64_t)s_sdhi.regs[internal_sdhi_word(off)];
486}
487
500RA8_INTERNAL static void
501internal_sdhi_write(uc_engine* uc, uint64_t addr, unsigned size, uint64_t value)
502{
503 (void)uc;
504 (void)size;
505 const uint64_t off = addr - (uint64_t)k_sdhi_base;
506 if (off == (uint64_t)k_sdhi_off_sd_buf0) {
507 internal_sdhi_buf_write((uint32_t)value);
508 return;
509 }
510 if (off == (uint64_t)k_sdhi_off_soft_rst) {
511 /* The driver writes 0 then 1; clear the FIFO/command engine either way. */
512 s_sdhi.xfer = (uint8_t)k_sdhi_xfer_none;
513 s_sdhi.word_idx = 0U;
514 s_sdhi.blocks_left = 0U;
515 s_sdhi.app_cmd = 0U;
516 s_sdhi.regs[internal_sdhi_word(off)] = (uint32_t)value;
519 return;
520 }
521 if (internal_sdhi_word(off) < (uint32_t)k_sdhi_words) {
522 s_sdhi.regs[internal_sdhi_word(off)] = (uint32_t)value;
523 }
524 if (off == (uint64_t)k_sdhi_off_sd_cmd) {
525 internal_sdhi_exec_command((uint32_t)value);
526 }
527}
528
538{
539 (void)memset(&s_sdhi, 0, sizeof(s_sdhi));
540}
541
553{
554 const uint32_t opt = s_sdhi.regs[internal_sdhi_word(k_sdhi_off_sd_option)];
555 if ((opt & (uint32_t)k_sdhi_option_width_bit) != 0U) {
556 return (uint32_t)k_sdhi_lanes_1bit;
557 }
558 if ((opt & (uint32_t)k_sdhi_option_width8_bit) != 0U) {
559 return (uint32_t)k_sdhi_lanes_8bit;
560 }
561 return (uint32_t)k_sdhi_lanes_4bit;
562}
563
573{
574 if ((s_sdhi.reads == 0U) && (s_sdhi.writes == 0U)) {
575 return;
576 }
577 (void)priv_emu_io_errf(" SDHI card : %u block reads %u block writes %u-bit bus\n",
578 s_sdhi.reads,
579 s_sdhi.writes,
580 (unsigned)internal_sdhi_bus_lanes());
581}
582
585 .base = (uint32_t)k_sdhi_base,
586 .span = (uint32_t)k_sdhi_span,
587 .order = (uint32_t)k_sdhi_block_order,
588 .read = internal_sdhi_read,
590 .tick = nullptr,
591 .reset = internal_sdhi_reset,
592 .report = internal_sdhi_report,
593 .name = "SDHI",
594};
595
597[[gnu::constructor]] RA8_INTERNAL static void internal_sdhi_block_register(void)
598{
600}
Multi-channel console log store backing the board view's tabbed console.
void board_console_push(board_console_ch_t ch, const char *line)
Append one completed line to a channel ring and the ALL ring.
@ k_board_console_ch_sd
SD (SDHI + SD-over-SPI) block summaries.
Decentralized peripheral-block registry for the board emulator core.
void board_periph_register_block(const board_periph_block_t *block)
Register a peripheral block's descriptor with the core registry.
SD-card-over-SPI device model for ra8_emulator (attached to SPI_B).
bool board_sd_read_block(uint32_t lba, uint8_t *dst)
Copy one 512-byte block straight out of the backing image.
bool board_sd_write_block(uint32_t lba, const uint8_t *src)
Copy one 512-byte block straight into the backing image.
void board_sd_info(bool *attached, uint64_t *bytes, uint8_t *fat_bits, const char **label)
Read back the attached card's summary (for the status view / report).
static sdhi_state_t s_sdhi
sdhi_option_bits_t
SD_OPTION bus-width selector bits + decoded lane counts.
@ k_sdhi_lanes_1bit
Single data lane.
@ k_sdhi_option_width_bit
SD_OPTION.WIDTH bit 15: 1 -> 1-bit.
@ k_sdhi_lanes_4bit
Four data lanes.
@ k_sdhi_lanes_8bit
Eight data lanes.
@ k_sdhi_option_width8_bit
SD_OPTION.WIDTH8 bit 13: 1 -> 8-bit.
static RA8_INTERNAL uint64_t internal_sdhi_read(uc_engine *uc, uint64_t addr, unsigned size)
MMIO read inside the SDHI window (SD_BUF0 is a live FIFO).
sdhi_resp_const_t
Canned response field values the firmware accepts.
@ k_sdhi_cid_word
Dummy CID fill ("RA8D" packed).
@ k_sdhi_cmd_index_mask
Low 6 bits of SD_CMD = index.
@ k_sdhi_ocr_ready
ACMD41 R3: busy=1, CCS=1, volt win.
@ k_sdhi_r1_ready
R1: TRAN state + ready-for-data.
@ k_sdhi_csd_v2_struct
CSD v2 structure tag in rsp[3].
@ k_sdhi_rca_value
CMD3 R6: RCA = 1 in bits [31:16].
@ k_sdhi_r7_if_cond
CMD8 R7: voltage 0x1 + echo 0xAA.
@ k_sdhi_csize_lo_mask
C_SIZE[21:16] field width.
@ k_sdhi_r1_app_cmd
R1 bit 5 APP_CMD (CMD55 ack).
@ k_sdhi_csize_mid_mask
C_SIZE[15:0] field width.
sdhi_order_t
End-of-run report-order slot (after the XSPI block at 95).
@ k_sdhi_block_order
Report ordering only.
sdhi_status_t
SD_INFO1 / SD_INFO2 status bits the firmware polls.
@ k_sdhi_info2_bre
SD_INFO2.BRE (read buffer ready).
@ k_sdhi_info1_rspend
SD_INFO1.RSPEND (response received).
@ k_sdhi_info2_bwe
SD_INFO2.BWE (write buffer space).
static RA8_INTERNAL void internal_sdhi_exec_command(uint32_t cmd)
Execute the latched command, build its response, raise RSPEND.
static RA8_INTERNAL void internal_sdhi_buf_write(uint32_t word)
Capture one 32-bit FIFO word into the staging buffer (write phase).
static RA8_INTERNAL void internal_sdhi_block_register(void)
Register the SDHI block before main (host constructor).
sdhi_reg_map_t
SDHI0 register-window geometry + the offsets the model owns.
@ k_sdhi_span
Narrow window: SD_CMD..SOFT_RST.
@ k_sdhi_base
SDHI0 register window base.
@ k_sdhi_off_sd_option
SD_OPTION : bus width + timeout.
@ k_sdhi_off_sd_rsp76
SD_RSP76 : response word 3.
@ k_sdhi_off_sd_rsp54
SD_RSP54 : response word 2.
@ k_sdhi_off_sd_buf0
SD_BUF0 : 32-bit FIFO word.
@ k_sdhi_off_sd_stop
SD_STOP : data stop.
@ k_sdhi_off_sd_arg
SD_ARG : command argument.
@ k_sdhi_off_soft_rst
SOFT_RST : software reset.
@ k_sdhi_off_sd_rsp10
SD_RSP10 : response word 0.
@ k_sdhi_off_sd_rsp32
SD_RSP32 : response word 1.
@ k_sdhi_off_sd_cmd
SD_CMD : command type / issue.
@ k_sdhi_off_sd_info1
SD_INFO1 : interrupt flag 1.
@ k_sdhi_off_sd_info2
SD_INFO2 : interrupt flag 2.
@ k_sdhi_off_sd_seccnt
SD_SECCNT : multi-block count.
static RA8_INTERNAL void internal_sdhi_reset(void)
Reset the SDHI model: zero the shadow + command/data-phase engine.
sdhi_xfer_t
Block-transfer phase the model is mid-way through.
@ k_sdhi_xfer_none
No data phase in flight.
@ k_sdhi_xfer_write
CMD24/25 write data phase.
@ k_sdhi_xfer_read
CMD17/18 read data phase.
static const board_periph_block_t s_k_sdhi_block
SDHI0 block descriptor (self-registered with the core).
static RA8_INTERNAL void internal_sdhi_write(uc_engine *uc, uint64_t addr, unsigned size, uint64_t value)
MMIO write inside the SDHI window (SD_CMD issues; SD_BUF0 captures).
static RA8_INTERNAL uint32_t internal_sdhi_buf_read(void)
Serve one 32-bit FIFO word from the staging buffer (read phase).
sdhi_geom_t
Geometry / sizing constants (no magic numbers).
@ k_sdhi_csize_unit
CSD v2: blocks per (C_SIZE+1) unit.
@ k_sdhi_byte_bits
Bits per byte.
@ k_sdhi_block_bytes
SD block size in bytes.
@ k_sdhi_csize_shift
C_SIZE high-half shift.
@ k_sdhi_byte_mask
One byte.
@ k_sdhi_words
0x200-byte window as 32-bit words.
@ k_sdhi_words_per_blk
512 / 4 = 128 FIFO words per block.
sdhi_cmd_idx_t
SD command indices the model decodes (index = SD_CMD & 0x3F).
@ k_sdhi_cmd25_writem
CMD25 WRITE_MULTIPLE_BLOCK.
@ k_sdhi_cmd12_stop
CMD12 STOP_TRANSMISSION.
@ k_sdhi_cmd9_send_csd
CMD9 SEND_CSD (R2).
@ k_sdhi_cmd7_select
CMD7 SELECT_CARD.
@ k_sdhi_cmd18_readm
CMD18 READ_MULTIPLE_BLOCK.
@ k_sdhi_cmd55_app_cmd
CMD55 APP_CMD prefix.
@ k_sdhi_cmd16_blocklen
CMD16 SET_BLOCKLEN.
@ k_sdhi_cmd2_send_cid
CMD2 ALL_SEND_CID (R2).
@ k_sdhi_cmd24_write1
CMD24 WRITE_SINGLE_BLOCK.
@ k_sdhi_cmd8_if_cond
CMD8 SEND_IF_COND (R7).
@ k_sdhi_cmd17_read1
CMD17 READ_SINGLE_BLOCK.
@ k_sdhi_cmd0_go_idle
CMD0 GO_IDLE_STATE.
@ k_sdhi_cmd3_send_rca
CMD3 SEND_RELATIVE_ADDR.
@ k_sdhi_acmd41_op_cond
ACMD41 SD_SEND_OP_COND.
static RA8_INTERNAL void internal_sdhi_begin_write(bool multi)
Arm a write data phase for CMD24 (single) or CMD25 (multi).
static RA8_INTERNAL void internal_sdhi_begin_read(bool multi)
Arm a read data phase for CMD17 (single) or CMD18 (multi).
static RA8_INTERNAL void internal_sdhi_exec_ident(uint32_t idx, uint32_t arg)
Decode the identification + addressing commands into a response.
static RA8_INTERNAL void internal_sdhi_publish_response(void)
Copy the latched response words into the SD_RSP* shadow registers.
static RA8_INTERNAL void internal_sdhi_load_block(void)
Load the block at the current LBA into the staging buffer (read phase).
static RA8_INTERNAL void internal_sdhi_report(void)
End-of-run SDHI section: block I/O counts (only if the bus was used).
sdhi_console_t
Console-tap line buffer capacity for an SDHI block summary.
@ k_sdhi_console_line_cap
Max chars in an "SD rd lba=.." line.
static RA8_INTERNAL uint32_t internal_sdhi_word(uint64_t off)
Word index of register at byte offset off inside the shadow.
static RA8_INTERNAL void internal_sdhi_make_csd(void)
Encode the attached card's capacity into a CSD v2 response (CMD9).
static RA8_INTERNAL uint32_t internal_sdhi_bus_lanes(void)
Decode the SD_OPTION shadow into the host bus-width lane count.
Bounded raw-descriptor I/O seam for the RA8 emulator.
emu_io_result_t priv_emu_io_errf(const char *format,...)
Format bounded text and write it to the injected error descriptor.
-proof
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
void * memset(void *dst, int value, size_t n)
Fill memory with a constant byte value.
static ra8_err_t internal_sdhi_write(void *ctx, uint32_t lba, uint32_t count, const uint8_t *buf)
Native-SDHI backend: write count blocks from buf at lba.
static ra8_err_t internal_sdhi_read(void *ctx, uint32_t lba, uint32_t count, uint8_t *buf)
Native-SDHI backend: read count blocks at lba into buf.
A modelled peripheral block's self-description for the core registry.
SDHI model state: register shadow + command + data-phase engine.
uint8_t stage[k_sdhi_block_bytes]
Active 512-byte block buffer.
uint8_t xfer
sdhi_xfer_t data-phase state.
uint32_t word_idx
Next SD_BUF0 word in the block.
uint32_t lba
Current block address.
uint32_t regs[k_sdhi_words]
0x200-byte window shadow.
uint32_t rsp[4]
Latched response words.
uint8_t app_cmd
1 => previous command was CMD55.
uint32_t reads
Block reads served.
uint32_t blocks_left
Remaining blocks in the transfer.
uint32_t writes
Block writes committed.