ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_esp_hosted_spi.c
Go to the documentation of this file.
1
31
32#include <stdint.h>
33
34/*
35 * ``port_esp_hosted_host_config.h`` is deliberately NOT named here even
36 * though this file depends on it. It defines H_ESP_PAYLOAD_HEADER_OFFSET
37 * under an #ifndef guard, while the vendored ``esp_hosted_header.h`` defines
38 * the same macro unconditionally from a sizeof; whichever is reached first
39 * wins and the other is skipped. ``transport_drv.h`` pulls them in that
40 * order. Naming the config header here would sort it ahead of
41 * ``transport_drv.h`` and turn the arrangement into a redefinition warning.
42 */
45#include "ra8_attributes.h"
47#include "ra8_check.h"
48#include "ra8_err.h"
49#include "ra8_esp_hosted_pins.h"
51#include "ra8_gpio_constants.h"
52#include "ra8_io_spi_bus.h"
54#include "ra8_log.h"
55#include "ra8_pin_interface.h"
56#include "ra8_port_constants.h"
57#include "ra8_port_utils.h"
58#include "ra8_sci_spi.h"
59#include "ra8_spi.h"
60#include "transport_drv.h"
61
71static const char* const s_tag = "eh_spi";
72
100
101static_assert((uint8_t)k_ra8_esp_hosted_spi_mode == (uint8_t)k_ra8_spi_mode_3,
102 "the co-processor image is built CONFIG_ESP_SPI_MODE=3");
103/*
104 * Not a tautology: the left operand is the bound reached through
105 * ``MAX_TRANSPORT_BUFFER_SIZE``, which ``transport_drv.h`` resolves through
106 * whichever port header its ``H_TRANSPORT_IN_USE`` chain selected, while the
107 * right is the constant the vendored ``spi_drv.c`` clocks. Selecting a
108 * different transport, or editing this port's SPI header to a hand-written
109 * number, makes them disagree and stops the build here rather than on the
110 * wire.
111 */
112static_assert(k_ra8_esp_hosted_spi_frame_bytes == MAX_SPI_BUFFER_SIZE,
113 "the port's frame bound must be the one the vendored SPI driver clocks");
114
128
140
152
162static uint8_t s_channel;
163
175static bool s_open;
176
177/*
178 * The production pin driver is defined in libs/ra8_hal/src/gpio.c and, like
179 * every other consumer of it in this tree, is reached by declaration rather
180 * than through a header -- ra8_pin_interface.h describes the type, not the
181 * instance.
182 */
184
206{
207 return (s_pin_if != nullptr) ? s_pin_if : &g_ra8_gpio_pin_interface;
208}
209
233{
234 if (s_injected_bus != nullptr) {
235 return s_injected_bus;
236 }
237 return (s_bus.iface != nullptr) ? &s_bus : nullptr;
238}
239
265{
268 "esp_hosted.sck");
269 if (err != k_ra8_ok) {
270 return err;
271 }
274 "esp_hosted.copi");
275 if (err != k_ra8_ok) {
276 return err;
277 }
280 "esp_hosted.cipo");
281}
282
310
312 uint32_t pclk_hz,
313 uint32_t sck_hz)
314{
315 if (s_open) {
317 }
318 if ((pclk_hz == 0U) || (sck_hz == 0U) ||
319 (sci_channel != (uint8_t)k_ra8_board_pmod1_sci_channel)) {
321 }
322
324 if (err == k_ra8_ok) {
325 const ra8_pin_interface_t* pin_if = internal_pin_if();
326 err = pin_if->output_init(pin_if->ctx,
329 }
330 if (err != k_ra8_ok) {
332 ra8_log_error_val(s_tag, "pin setup failed", (uint32_t)err);
333 return err;
334 }
335
336 const ra8_sci_spi_cfg_t cfg = {
337 .baud_hz = sck_hz,
338 .pclk_hz = pclk_hz,
340 .lsb_first = false,
341 };
342 err = ra8_sci_spi_init(sci_channel, &cfg);
343 if (err == k_ra8_ok) {
344 err = ra8_io_spi_bus_bind_sci_spi(&s_bus, sci_channel);
345 }
346 if (err != k_ra8_ok) {
347 (void)ra8_sci_spi_deinit(sci_channel);
350 ra8_log_error_val(s_tag, "sci simple-spi open failed", (uint32_t)err);
351 return k_ra8_err_spi_error;
352 }
353
354 s_channel = sci_channel;
355 s_open = true;
356 ra8_log_info_val(s_tag, "bus open on sci channel", (uint32_t)sci_channel);
357 return k_ra8_ok;
358}
359
361{
362 if (!s_open) {
364 }
368 s_open = false;
369 return (err == k_ra8_ok) ? k_ra8_ok : k_ra8_err_spi_error;
370}
371
373{
374 return s_open;
375}
376
381
386
411static void* internal_bus_init(void)
412{
413 if (internal_bus() == nullptr) {
414 ra8_log_warn(s_tag, "bus init before the port opened the channel");
415 return nullptr;
416 }
417 return &s_bus;
418}
419
446static int internal_bus_deinit(void* bus_handle)
447{
448 if (bus_handle == nullptr) {
449 return RET_INVALID;
450 }
451 if (bus_handle != (void*)&s_bus) {
452 return RET_INVALID;
453 }
455}
456
492static int internal_do_bus_transfer(void* transfer_context)
493{
494 struct hosted_transport_context_t* ctx = (struct hosted_transport_context_t*)transfer_context;
495 if (ctx == nullptr) {
496 return RET_INVALID;
497 }
498 if ((ctx->tx_buf == nullptr) || (ctx->rx_buf == nullptr)) {
499 return RET_INVALID;
500 }
501 if ((ctx->tx_buf_size == 0U) || (ctx->tx_buf_size > (uint32_t)MAX_TRANSPORT_BUFFER_SIZE)) {
502 return RET_INVALID;
503 }
504 const ra8_io_spi_bus_t* bus = internal_bus();
505 if (bus == nullptr) {
506 return RET_FAIL;
507 }
508
509 const ra8_pin_interface_t* pin_if = internal_pin_if();
511 if (pin_if->write(pin_if->ctx, cs, k_ra8_level_low) != k_ra8_ok) {
512 return RET_FAIL;
513 }
514 const ra8_err_t err =
515 ra8_io_spi_bus_write_read(bus, ctx->tx_buf, ctx->rx_buf, ctx->tx_buf_size, k_ra8_spi_width_8);
516 const ra8_err_t cs_err = pin_if->write(pin_if->ctx, cs, k_ra8_level_high);
517 if (err != k_ra8_ok) {
518 ra8_log_error_val(s_tag, "frame transfer failed", (uint32_t)err);
519 return RET_FAIL;
520 }
521 return (cs_err == k_ra8_ok) ? RET_OK : RET_FAIL;
522}
523
525{
526 RA8_CHECK_NULL_PTR(out, s_tag, "vtable must not be nullptr");
527 out->_h_bus_init = internal_bus_init;
528 out->_h_bus_deinit = internal_bus_deinit;
529 out->_h_do_bus_transfer = internal_do_bus_transfer;
530 return (out->_h_do_bus_transfer != nullptr) ? k_ra8_ok : k_ra8_err_invalid_state;
531}
const ra8_pin_interface_t g_ra8_gpio_pin_interface
Definition gpio.c:502
esp-hosted port header: RTOS handle types, return codes and budgets.
#define RET_FAIL
Operation failed for an unspecified reason.
#define RET_INVALID
A parameter was out of contract (null handle, bad size).
#define RET_OK
Operation completed.
#define MAX_TRANSPORT_BUFFER_SIZE
Bytes moved in one SDIO block transfer.
esp-hosted port header: full-duplex SPI transport buffer size.
static const char * s_tag
Logging / check tag.
Definition ra8_app.c:17
Annotation-attribute framework macros for ra8-firmware.
#define RA8_PRIV
Module-private helper: shared across TUs but only inside one library.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Board-identity, LEDs, switches, display, audio, Arduino, Pmod, and MikroBUS portion of the EK-RA8D2 v...
@ k_ra8_board_pmod1_sci_channel
Pmod1 (J26) Simple-SPI is SCI2.
@ k_ra8_board_pmod1_spi_copi
Pmod1.2 COPI (MOSI2/TXD2 per UM),P801.
@ k_ra8_board_pmod1_spi_sck
Pmod1.4 SCK (SCK2), P803.
@ k_ra8_board_pmod1_spi_cipo
Pmod1.3 CIPO (MISO2/RXD2 per UM),P802.
Validation and Error-Checking Macros for ra8-firmware.
#define RA8_CHECK_NULL_PTR(ptr, tag, message)
Reject nullptr pointer, returning k_ra8_err_null_ptr.
Definition ra8_check.h:243
Error Code Definitions for ra8-firmware.
@ k_ra8_err_spi_error
SPI transfer failed (bus fault, mode fault, overrun, ...).
Definition ra8_err.h:405
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_err_invalid_state
Module in wrong state for requested operation.
Definition ra8_err.h:161
@ k_ra8_err_not_initialized
Module not initialized – _init() not yet called successfully.
Definition ra8_err.h:235
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
static const ra8_pin_interface_t * s_pin_if
The pin driver every level read and write goes through.
THE single point of change for the RA8 <-> ESP32-C6 harness map.
@ k_ra8_esp_hosted_pin_chip_select
Chip select, RA8 output.
static ra8_err_t internal_route_data_pins(void)
Route the three Pmod1 SPI data pins to the SCI peripheral.
static const ra8_io_spi_bus_t * s_injected_bus
Test-injected replacement for s_bus, or null in production.
static void internal_release_pins(void)
Hand every pin the transport claimed back to the pin validator.
ra8_err_t priv_ra8_esp_hosted_spi_close(void)
Close the SCI Simple-SPI channel and release every pin it took.
void priv_ra8_esp_hosted_spi_set_bus(const ra8_io_spi_bus_t *bus)
Replace the SPI bus the transfer slot clocks frames through.
static bool s_open
True between a successful open and its matching close.
ra8_err_t priv_ra8_esp_hosted_spi_bind(hosted_osi_funcs_t *out)
Populate the three transport slots of the OS-abstraction vtable.
bool priv_ra8_esp_hosted_spi_is_open(void)
Report whether the SCI Simple-SPI channel is currently open.
ra8_esp_hosted_spi_frame_t
The frame bound this port supplies, as a named constant.
@ k_ra8_esp_hosted_spi_frame_bytes
Bytes clocked in one full-duplex transaction, from the port header.
void priv_ra8_esp_hosted_spi_set_pin_interface(const ra8_pin_interface_t *iface)
Replace the pin driver the transfer slot drives chip select with.
static ra8_io_spi_bus_t s_bus
Bus handle bound to the SCI Simple-SPI channel by the open.
ra8_err_t priv_ra8_esp_hosted_spi_open(uint8_t sci_channel, uint32_t pclk_hz, uint32_t sck_hz)
Route the Pmod1 SPI pins and open the SCI Simple-SPI channel.
static uint8_t s_channel
SCI channel the open brought up, meaningful only while s_open.
static int internal_do_bus_transfer(void *transfer_context)
_h_do_bus_transfer: clock one full-duplex esp-hosted frame.
static const ra8_io_spi_bus_t * internal_bus(void)
Report the bus the transfer slot should clock through.
static int internal_bus_deinit(void *bus_handle)
_h_bus_deinit: close the channel behind an opaque bus handle.
static const ra8_pin_interface_t * internal_pin_if(void)
Report the pin driver the chip select is driven through.
static void * internal_bus_init(void)
_h_bus_init: hand the vendored transport its opaque bus handle.
Module-private surface of the esp-hosted full-duplex SPI transport.
@ k_ra8_esp_hosted_spi_mode
Clock polarity/phase both high: the C6 image's CONFIG_ESP_SPI_MODE.
PSEL codes for peripheral pin routing on the RA8D2.
@ k_ra8_psel_sci_async
00100b: SCI async serial (UART).
ra8_io SPI-bus facade – one controller-transfer vtable over thechip's two SPI implementations.
ra8_err_t ra8_io_spi_bus_write_read(const ra8_io_spi_bus_t *bus, const void *tx, void *rx, uint32_t len, ra8_spi_bit_width_t width)
Full-duplex multi-frame transfer on the bound bus.
SCI Simple-SPI backend binder for the ra8_io SPI-bus facade.
ra8_err_t ra8_io_spi_bus_bind_sci_spi(ra8_io_spi_bus_t *bus, uint8_t channel)
Bind bus to SCI Simple-SPI channel channel.
Lightweight Logging Interface for ra8-firmware.
#define ra8_log_warn(tag, message)
RA8 log warn.
Definition ra8_log.h:347
#define ra8_log_info_val(tag, message, value)
RA8 log info val.
Definition ra8_log.h:366
#define ra8_log_error_val(tag, message, value)
RA8 log error val.
Definition ra8_log.h:337
Abstract pin-driver interface for dependency injection.
Typed Port / Pin Constants for the RA8D2 IOPORT Module.
ra8_port_pin_t
Packed (port << 8) | pin pin identifier.
@ k_ra8_level_high
Drive or read 1.
@ k_ra8_level_low
Drive or read 0.
High-level GPIO helpers on top of the PORT + PFS register layer.
ra8_err_t ra8_gpio_release(ra8_port_pin_t pin)
Release a GPIO pin claim.
Definition gpio.c:233
ra8_err_t ra8_pfs_route_peripheral(ra8_port_pin_t pin, ra8_psel_t psel, const char *owner)
Route a pin to a non-IRQ peripheral function via PFS.PSEL.
Definition gpio.c:238
SCI Simple-SPI controller-mode driver (polling, full-duplex).
ra8_err_t ra8_sci_spi_init(uint8_t channel, const ra8_sci_spi_cfg_t *cfg)
Bring an SCI channel up as a Simple-SPI controller.
ra8_err_t ra8_sci_spi_deinit(uint8_t channel)
Disable the channel and release its MSTP gate.
SPI_B controller driver (RA8D2 Type-B SPI peripheral).
@ k_ra8_spi_width_8
8-bit frame -> SPCMDn.SPB = 0b00111.
Definition ra8_spi.h:85
ra8_spi_mode_t
CPOL / CPHA combinations.
Definition ra8_spi.h:61
@ k_ra8_spi_mode_3
CPOL=1, CPHA=1.
Definition ra8_spi.h:65
Caller-allocated SPI-bus handle binding a backend to its context.
Vtable for a pin driver.
ra8_err_t(* output_init)(void *ctx, ra8_port_pin_t pin, ra8_level_t init_level)
Configure a pin as an output at an initial level.
void * ctx
Opaque context handed to every call.
ra8_err_t(* write)(void *ctx, ra8_port_pin_t pin, ra8_level_t level)
Drive a pin.
Configuration descriptor for ra8_sci_spi_init.
Definition ra8_sci_spi.h:49