ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ux_dcd_ra8_usb.c
Go to the documentation of this file.
1
25
26#define UX_SOURCE_CODE
27
28#include "ux_dcd_ra8_usb.h"
29
30#include <stdint.h>
31#include <string.h>
32
33#include "ra8_check.h"
34#include "ra8_elc_regs.h"
35#include "ra8_isr.h"
36#include "ra8_log.h"
37#include "ra8_usb_regs.h"
38#include "tx_api.h"
39#include "ux_api.h"
41#include "ux_device_stack.h"
42#include "ux_system.h"
43#include "ux_utility.h"
44
56typedef enum : uint8_t {
57 /* Priority 8 (NVIC 0x80) leaves SysTick (NVIC 0x40) and PendSV able to
58 * preempt the USB ISR. With same priority, SysTick cannot interrupt the
59 * USB ISR and the 8 kHz SOFR + per-pipe NRDY events starve thread mode,
60 * preventing tx_thread_sleep from ever waking. HUM Ch 13 NVIC priorities. */
63
64/* Tag used by ra8_log_*. Must be a static lifetime string. */
65static const char* const s_tag = "ux_dcd_ra8_usb";
66
80volatile uint16_t g_syscfg_after_dcd_init = 0U;
81
92volatile uint16_t g_lpsts_after_dcd_init = 0U;
93
105 .speed = k_ra8_usb_speed_fs,
106 .owner = nullptr,
107 .pipes = {},
108};
109
110volatile uint32_t g_dcd_auto_echo_enable = 0U;
111
113
115
141void ux_dcd_ra8_usb_auto_echo_enable(uint8_t out_pipe, uint8_t in_pipe)
142{
143 g_dcd_auto_echo_out_pipe = out_pipe;
144 g_dcd_auto_echo_in_pipe = in_pipe;
146}
147
152typedef enum : uintptr_t {
153 k_nvic_iser_base = 0xE000E100U,
154 k_nvic_icer_base = 0xE000E180U,
155 k_nvic_ispr_base = 0xE000E200U,
157
162typedef enum : uint32_t {
166
175static uint16_t s_usb_irq_slot = 0U;
176
185volatile uint32_t g_isr_spurious_run = 0U;
186
206{
207 const uint32_t word = (uint32_t)s_usb_irq_slot / (uint32_t)k_nvic_irqs_per_reg;
208 const uint32_t bit = 1UL << ((uint32_t)s_usb_irq_slot % (uint32_t)k_nvic_irqs_per_reg);
209 const uintptr_t base = enabled ? (uintptr_t)k_nvic_iser_base : (uintptr_t)k_nvic_icer_base;
210 *(volatile uint32_t*)(base + ((uintptr_t)word * (uintptr_t)k_nvic_reg_stride)) = bit;
211}
212
235
261{
264 /* Watchdog kick for stalled transfers: a stashed pipe transfer with
265 * no pending controller event generates no IRQ, so the walk -- and
266 * with it the IN-pipe strand recovery -- never runs. Pend the line
267 * once per SysTick while any transfer is stashed; the ISR walk is
268 * idempotent for pipes with nothing to do. */
269 bool stashed = false;
270 for (uint8_t i = 1U; i < (uint8_t)k_ux_dcd_ra8_usb_max_pipes; i++) {
271 if (g_dcd.pipes[i].xfer != UX_NULL) {
272 stashed = true;
273 }
274 }
275 if (stashed) {
276 const uint32_t word = (uint32_t)s_usb_irq_slot / (uint32_t)k_nvic_irqs_per_reg;
277 const uint32_t bit = 1UL << ((uint32_t)s_usb_irq_slot % (uint32_t)k_nvic_irqs_per_reg);
278 const uintptr_t addr =
279 (uintptr_t)k_nvic_ispr_base + ((uintptr_t)word * (uintptr_t)k_nvic_reg_stride);
280 *(volatile uint32_t*)addr = bit;
281 }
282}
283
292
297typedef enum : uint8_t {
300
301/* -------------------------------------------------------------------------- */
302/* Lifecycle */
303/* -------------------------------------------------------------------------- */
304
305#ifndef RA8_USB_POLLED_ONLY
326{
327 uint16_t slot = 0U;
328 if (ra8_isr_lookup_slot(priv_pick_event(speed), &slot) == k_ra8_ok) {
329 s_usb_irq_slot = slot;
330 }
331}
332#endif
333
357{
358 if (_ux_system_slave == UX_NULL) {
360 }
361 UX_SLAVE_DCD* owner = &_ux_system_slave->ux_system_slave_dcd;
362 owner->ux_slave_dcd_status = UX_DCD_STATUS_OPERATIONAL;
363 owner->ux_slave_dcd_controller_type = (UINT)k_ra8_usb_dcd_controller_id; /* RA-USB private id. */
364 owner->ux_slave_dcd_function = _ux_dcd_ra8_usb_function;
365 owner->ux_slave_dcd_controller_hardware = (void*)&g_dcd;
366
367 g_dcd.speed = speed;
368 g_dcd.owner = owner;
370
371 for (uint8_t i = 0U; i < (uint8_t)k_ux_dcd_ra8_usb_max_pipes; i++) {
372 g_dcd.pipes[i].xfer = nullptr;
373 }
374
375#ifndef RA8_USB_POLLED_ONLY
376 /* Wire the controller's ELC event onto an NVIC line. ra8_isr_init is
377 * idempotent. HUM Ch 13 NVIC + Ch 14 ICU IELSR. */
378 RA8_RETURN_ON_ERROR(ra8_isr_init(), s_tag, "ra8_isr_init");
380 priv_pick_isr(speed),
381 nullptr,
382 (uint8_t)k_ra8_usb_dcd_isr_prio,
383 nullptr),
384 s_tag,
385 "ra8_isr_register");
387#else
388 /* RA8_USB_POLLED_ONLY (TrustZone NS image, #96): the worker drives the
389 * controller by calling ra8_usb_dispatch() in a tight loop instead of taking
390 * the USB NVIC line. The ICU IELSR + NVIC are Secure-attributed and would
391 * fault from Non-secure state, so skip ra8_isr_register entirely. Bus events,
392 * chapter-9 SETUP handling, and bulk auto-echo all run inside the polled
393 * dispatch -> priv_event_cb -> ux_dcd_ra8_usb_irq path. */
394 (void)speed;
395#endif
396 return k_ra8_ok;
397}
398
420RA8_INTERNAL static void internal_init_parse_framework(UX_SLAVE_DEVICE* device)
421{
422 if (_ux_system_slave->ux_system_slave_speed == UX_HIGH_SPEED_DEVICE) {
423 _ux_system_slave->ux_system_slave_device_framework =
424 _ux_system_slave->ux_system_slave_device_framework_high_speed;
425 _ux_system_slave->ux_system_slave_device_framework_length =
426 _ux_system_slave->ux_system_slave_device_framework_length_high_speed;
427 } else {
428 _ux_system_slave->ux_system_slave_device_framework =
429 _ux_system_slave->ux_system_slave_device_framework_full_speed;
430 _ux_system_slave->ux_system_slave_device_framework_length =
431 _ux_system_slave->ux_system_slave_device_framework_length_full_speed;
432 }
433
434 if (_ux_system_slave->ux_system_slave_device_framework != UX_NULL) {
435 _ux_utility_descriptor_parse(_ux_system_slave->ux_system_slave_device_framework,
436 _ux_system_device_descriptor_structure,
437 UX_DEVICE_DESCRIPTOR_ENTRIES,
438 (UCHAR*)&device->ux_slave_device_descriptor);
439 }
440}
441
464RA8_INTERNAL static void internal_init_setup_ep0(UX_SLAVE_DEVICE* device, UX_SLAVE_DCD* owner)
465{
466 UX_SLAVE_TRANSFER* tr =
467 &device->ux_slave_device_control_endpoint.ux_slave_endpoint_transfer_request;
468 tr->ux_slave_transfer_request_timeout = UX_MS_TO_TICK(UX_CONTROL_TRANSFER_TIMEOUT);
469 tr->ux_slave_transfer_request_current_data_pointer = tr->ux_slave_transfer_request_data_pointer;
470 tr->ux_slave_transfer_request_endpoint = &device->ux_slave_device_control_endpoint;
471 device->ux_slave_device_control_endpoint.ux_slave_endpoint_descriptor.wMaxPacketSize =
472 device->ux_slave_device_descriptor.bMaxPacketSize0;
473 tr->ux_slave_transfer_request_requested_length =
474 device->ux_slave_device_descriptor.bMaxPacketSize0;
475 tr->ux_slave_transfer_request_transfer_length =
476 device->ux_slave_device_descriptor.bMaxPacketSize0;
477
478 /* Hand EP0 to ourselves so any future TRANSFER_REQUEST has the
479 * pipe table populated. */
481 UX_DCD_CREATE_ENDPOINT,
482 (void*)&device->ux_slave_device_control_endpoint);
483
484 device->ux_slave_device_control_endpoint.ux_slave_endpoint_state = UX_ENDPOINT_RESET;
485 tr->ux_slave_transfer_request_phase = UX_TRANSFER_PHASE_DATA_IN;
486
487 /* Stamp ATTACHED so the chapter-9 dispatcher accepts the host's
488 * first SETUP (gate is state in {ATTACHED, ADDRESSED, CONFIGURED}). */
489 device->ux_slave_device_state = (unsigned long)UX_DEVICE_ATTACHED;
490}
491
522{
523 if ((uint8_t)speed > (uint8_t)k_ra8_usb_speed_hs) {
525 }
526 RA8_RETURN_ON_ERROR(ra8_usb_device_init(speed), s_tag, "ra8_usb_device_init");
527 ra8_usb_attach_handler(speed, priv_event_cb, nullptr);
528
530
531 /* Tell USBX system the speed. HS-controller reports HS (512-byte bulk
532 * MPS); FS-controller reports FS. */
533 _ux_system_slave->ux_system_slave_speed =
534 (speed == k_ra8_usb_speed_hs) ? UX_HIGH_SPEED_DEVICE : UX_FULL_SPEED_DEVICE;
535
536 UX_SLAVE_DEVICE* device = &_ux_system_slave->ux_system_slave_device;
538 internal_init_setup_ep0(device, g_dcd.owner);
539
540 /* Bisect probes: SYSCFG/LPSTS state at end of DCD init, BEFORE the
541 * application calls ra8_usb_device_attach(true). HUM Ch 37.2.1 SYSCFG
542 * p 2060, HUM Ch 37.2.43 LPSTS p 2111. */
543 if (speed == k_ra8_usb_speed_hs) {
546 }
547
548 ra8_log_info(s_tag, "DCD bridge installed");
549 return k_ra8_ok;
550}
551
570{
573 }
574 /* Matching pair to the disabled ra8_isr_register in the init path. */
575 ra8_usb_attach_handler(g_dcd.speed, nullptr, nullptr);
576 (void)ra8_usb_device_deinit(g_dcd.speed);
577 if (g_dcd.owner != nullptr) {
578 g_dcd.owner->ux_slave_dcd_status = UX_DCD_STATUS_HALTED;
579 g_dcd.owner->ux_slave_dcd_function = nullptr;
580 }
582 g_dcd.owner = nullptr;
583 return k_ra8_ok;
584}
585
604{
605 return g_dcd.state;
606}
static const char * s_tag
Logging / check tag.
Definition ra8_app.c:17
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
const char * owner
Owner.
Validation and Error-Checking Macros for ra8-firmware.
#define RA8_RETURN_ON_ERROR(err, tag, message)
Early return on error, propagating the code upward.
Definition ra8_check.h:184
Event Link Controller (ELC) register layout for the Renesas RA8D2.
@ 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_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
NVIC + ICU IELSR allocator.
ra8_err_t ra8_isr_init(void)
Initialise the ra8_isr table.
Definition ra8_isr.c:229
ra8_err_t ra8_isr_lookup_slot(ra8_elc_event_t event, uint16_t *out_slot)
Look up the slot allocated to a registered event.
Definition ra8_isr.c:408
ra8_err_t ra8_isr_register(ra8_elc_event_t event, ra8_isr_handler_t handler, void *ctx, uint8_t priority, uint16_t *out_slot)
Allocate an IELSR slot for an ELC event + handler.
Definition ra8_isr.c:296
Lightweight Logging Interface for ra8-firmware.
#define ra8_log_info(tag, message)
RA8 log info.
Definition ra8_log.h:364
ra8_err_t ra8_usb_device_init(ra8_usb_speed_t speed)
Bring up a USB controller in device mode.
ra8_err_t ra8_usb_device_deinit(ra8_usb_speed_t speed)
Tear down a USB controller and drop its MSTP reference.
void ra8_usb_attach_handler(ra8_usb_speed_t speed, ra8_usb_event_fn_t fn, void *ctx)
Install (or detach) the per-controller USB event handler.
Definition ra8_usb_irq.c:90
ra8_usb_speed_t
Selects which controller instance the call targets.
@ k_ra8_usb_speed_hs
High-Speed controller (USBHS @ 0x40351000).
@ k_ra8_usb_speed_fs
Full-Speed controller (USBFS @ 0x40250000).
USB Full-Speed + High-Speed controller layout for the Renesas RA8D2.
static volatile uint16_t * ra8_usbhs_lpsts(void)
Get pointer to USBHS LPSTS register.
static volatile r_usb_regs_t * ra8_usb_hs(void)
Get pointer to the USB HS controller register block.
unsigned int UINT
ThreadX-compatible unsigned int (host stub).
volatile uint16_t SYSCFG
+0x000 System Configuration.
Externally-readable diagnostic counters for OUT-pipe stall debug.
Bridge-singleton state.
static void internal_init_parse_framework(UX_SLAVE_DEVICE *device)
Select the active device-framework pair and parse the descriptor.
void ux_dcd_ra8_usb_irq_reenable(void)
Storm-guard recovery: zero the run counter and re-enable the USB IRQ.
ra8_usb_dcd_state_t ux_dcd_ra8_usb_state(void)
Ux dcd ra usb state.
ra8_usb_dcd_isr_prio_t
NVIC priority chosen for the USB controller IRQs.
@ k_ra8_usb_dcd_isr_prio
RA8 USB dcd ISR prio.
static uint16_t s_usb_irq_slot
NVIC slot the USB controller's ELC event is routed to.
static void internal_init_setup_ep0(UX_SLAVE_DEVICE *device, UX_SLAVE_DCD *owner)
Bind the EP0 transfer-request endpoint and run CREATE_ENDPOINT.
volatile uint16_t g_syscfg_after_dcd_init
SYSCFG snapshot at end of ux_dcd_ra8_usb_initialize.
static void internal_usbfs_storm_guard_init(ra8_usb_speed_t speed)
Resolve the USB controller's NVIC slot for the storm guard.
ra8_nvic_reg_t
Cortex-M NVIC set/clear-enable register array base addresses.
@ k_nvic_ispr_base
NVIC Interrupt Set-Pending Register array.
@ k_nvic_icer_base
NVIC Interrupt Clear-Enable Register array.
@ k_nvic_iser_base
NVIC Interrupt Set-Enable Register array.
volatile uint16_t g_lpsts_after_dcd_init
LPSTS snapshot at end of ux_dcd_ra8_usb_initialize.
void priv_usbfs_irq_mask(void)
Mask the USB IRQ at the NVIC to break an interrupt storm.
ra8_usb_dcd_ctrl_id_t
Private USBX controller-type id reported by this DCD bridge.
@ k_ra8_usb_dcd_controller_id
RA-USB private controller id.
void ux_dcd_ra8_usb_auto_echo_enable(uint8_t out_pipe, uint8_t in_pipe)
Enable bridge-side auto-echo on the configured pipe pair.
static ra8_err_t internal_init_bind_owner(ra8_usb_speed_t speed)
Bind _ux_system_slave->ux_system_slave_dcd to this bridge.
ra8_err_t ux_dcd_ra8_usb_initialize(ra8_usb_speed_t speed)
Bring up the USBX DCD bridge for the selected RA8 USB controller.
static void internal_usbfs_irq_set_enabled(bool enabled)
Set or clear the USB controller's NVIC enable bit.
ra8_nvic_layout_t
Bit layout of the NVIC ISER/ICER register array.
@ k_nvic_irqs_per_reg
IRQ lines covered by one ISER/ICER word.
@ k_nvic_reg_stride
Byte stride between consecutive ISER/ICER words.
ra8_err_t ux_dcd_ra8_usb_uninitialize(void)
Ux dcd ra usb uninitialize.
USBX device-controller-driver (DCD) bridge to ra8_usb.
@ k_ux_dcd_ra8_usb_max_pipes
DCP + 9 PIPE entries (HUM 36.1).
unsigned int _ux_dcd_ra8_usb_function(struct UX_SLAVE_DCD_STRUCT *dcd, unsigned int function, void *parameter)
ux dcd ra usb function.
ra8_usb_dcd_state_t
Run-state of the DCD bridge.
@ k_ux_dcd_ra8_usb_state_ready
Bridge installed; bus idle.
@ k_ux_dcd_ra8_usb_state_uninit
Bridge not yet initialized.
USBX device-controller-driver bridge to ra8_usb – per-module cross-translation-unit contract.
volatile uint32_t g_dcd_auto_echo_enable
Non-zero once ISR-side auto-echo is armed.
uint8_t g_dcd_auto_echo_out_pipe
Pipe drained by the in-ISR auto-echo path.
uint8_t g_dcd_auto_echo_in_pipe
Pipe the auto-echo path re-queues onto.
ra8_usb_dcd_t g_dcd
The single bridge instance, defined in ux_dcd_ra8_usb.c.
ra8_elc_event_t priv_pick_event(ra8_usb_speed_t speed)
Pick the ELC event number for a controller.
ra8_usb_dcd_diag_t g_diag
Bridge diagnostic counter block, defined in ux_dcd_ra8_usb.c.
ra8_isr_handler_t priv_pick_isr(ra8_usb_speed_t speed)
Pick the ISR trampoline for a controller.
void priv_event_cb(void *ctx, ra8_usb_speed_t speed, uint16_t status_mask)
ra8_usb_attach_handler trampoline into ux_dcd_ra8_usb_irq.
volatile uint32_t g_isr_spurious_run
Consecutive event-less ISR entries since the last SysTick re-enable.