ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_usb_phy.c
Go to the documentation of this file.
1
23
24#include <stdint.h>
25
26#include "ra8_attributes.h"
27#include "ra8_check.h"
28#include "ra8_err.h"
29#include "ra8_hw_err.h"
30#include "ra8_log.h"
31#include "ra8_time.h"
32#include "ra8_usb.h"
33#include "ra8_usb_internal.h"
34#include "ra8_usb_regs.h"
35
36static const char* s_tag = "USB";
37
38/* =============================================================================
39 * USBHS PHY + shared module bring-up
40 * =============================================================================
41 */
42
43/* =============================================================================
44 * Lifecycle
45 * =============================================================================
46 */
47
61typedef enum : uint32_t {
63 /* PHY analog PLL can take multiple ms to lock at cold start; FSP
64 * uses an unbounded while loop. We pick 5 million NOP-spins
65 * (~5 ms ceiling at 1 GHz Cortex-M85) which is well above the
66 * worst-case observed lock time but still bounded for NASA Rule 2. */
68 /* Per-CLKSEL bisect attempt: ~50 ms ceiling at 1 GHz so the 4-attempt
69 * sweep stays under the 250 ms total wall-time budget mandated by the
70 * USB-HS bring-up debug spec. */
76
101
119
138static volatile uint8_t s_phy_step_probe = 0U;
139
155static volatile uint32_t s_usbhs_init_probe = 0U;
156
165static volatile uint16_t s_usbhs_pllsta_probe = 0U;
166
181static volatile uint8_t s_clksel_winner = (uint8_t)k_ra8_usbhs_clksel_no_winner;
182
196static volatile uint16_t s_clksel_attempt_pllsta[4] = {0U, 0U, 0U, 0U};
197
214static void internal_usb_delay_1us(void)
215{
216 for (volatile uint32_t i = 0U; i < (uint32_t)k_ra8_usbhs_delay_1us_iters; ++i) {
217 __asm__ volatile("nop");
218 }
219}
220
244{
245 /* HUM Ch 37.2.1 "SYSCFG : System Configuration Control Register", p 2061 */
246 reg->SYSCFG = (uint16_t)(reg->SYSCFG & (uint16_t)~(uint16_t)(1U << k_ra8_syscfg_bit_drpd));
247 reg->SYSCFG = (uint16_t)(reg->SYSCFG | (uint16_t)(1U << k_ra8_syscfg_bit_usbe));
248
249 const uint32_t lo = s_usbhs_init_probe & (uint32_t)k_ra8_usbhs_probe_lo_mask;
250 const uint32_t hi = (uint32_t)reg->SYSCFG << (uint32_t)k_ra8_usbhs_probe_hi_shift;
251 s_usbhs_init_probe = lo | hi;
252}
253
276{
277 volatile const uint16_t* const pllsta = ra8_usbhs_pllsta();
279 for (uint32_t i = 0U; i < (uint32_t)k_ra8_usbhs_pll_lock_attempt_limit; ++i) {
280#if defined(RA8_OFF_TARGET) && defined(UNIT_TEST)
281 if (ra8_fake_mmio_wait_eval(pllsta, i, ((*pllsta & (uint16_t)k_ra8_pllsta_plllock) != 0U))) {
282 lock = k_ra8_ok;
283 break;
284 }
285#else
286 if ((*pllsta & (uint16_t)k_ra8_pllsta_plllock) != 0U) {
287 lock = k_ra8_ok;
288 break;
289 }
290#endif
291 }
292 s_usbhs_pllsta_probe = *pllsta;
293 return lock;
294}
295
321static void internal_usbhs_arm_phy_reset(volatile uint16_t* physet_reg, uint16_t clksel_value)
322{
323 /* HUM Ch 37.2.17 PHYSET, p 2080: assert PHY power-down and PLL
324 * reset, mask out CLKSEL[1:0], then OR in the candidate field. */
325 uint16_t v = *physet_reg;
326 v = (uint16_t)(v | (uint16_t)k_ra8_physet_dirpd | (uint16_t)k_ra8_physet_pllreset);
327 v = (uint16_t)(v & (uint16_t)~(uint16_t)k_ra8_physet_clksel);
328 v = (uint16_t)(v | (uint16_t)(clksel_value & (uint16_t)k_ra8_physet_clksel));
329 *physet_reg = v;
330}
331
365static ra8_err_t internal_usbhs_try_clksel(volatile uint16_t* physet,
366 volatile uint16_t* lpsts,
367 uint16_t clksel_value)
368{
369 /* Drop SUSPENDM so the PHY clock is gated; subsequent PHYSET writes
370 * are then sequenced through a single SUSPENDM 0->1 transition. */
371 *lpsts = (uint16_t)(*lpsts & (uint16_t)~(uint16_t)k_ra8_lpsts_suspendm);
373
374 internal_usbhs_arm_phy_reset(physet, clksel_value);
376
377 /* Release DIRPD (PHY analog up), wait 1 ms, release PLLRESET. */
378 *physet = (uint16_t)(*physet & (uint16_t)~(uint16_t)k_ra8_physet_dirpd);
379 ra8_delay_ms(1U);
380 *physet = (uint16_t)(*physet & (uint16_t)~(uint16_t)k_ra8_physet_pllreset);
381
382 /* HUM Ch 37.2.43 LPSTS, p 2111: SUSPENDM=1 starts the PHY clock,
383 * which both commits any previously-latched PHYSET writes and
384 * begins the PLL lock countdown. */
385 *lpsts = (uint16_t)(*lpsts | (uint16_t)k_ra8_lpsts_suspendm);
386
388}
389
428{
429 volatile uint16_t* const physet = ra8_usbhs_physet();
430 volatile uint16_t* const lpsts = ra8_usbhs_lpsts();
431
432 /* Step 1: SYSCFG.HSE = 1 (mirrors FSP `hw_usb_set_hse`, called from
433 * r_usb_basic.c BEFORE hw_usb_pmodule_init). HUM Ch 37 "SYSCFG"
434 * register, p 2060. */
435 reg->SYSCFG = (uint16_t)(reg->SYSCFG | (uint16_t)(1U << k_ra8_syscfg_bit_hse));
437
438 /* Step 2: CLKSEL = 24 MHz (PHYSET[5:4] = 11b, value 0x30).
439 *
440 * Per HUM Ch 37.3.3 "Supplying the Clock", Table 37.17 (p 2120) the
441 * USB-PHY internal PLL takes its reference clock DIRECTLY FROM THE
442 * EXTAL PIN -- not from USB60CLK. Allowed EXTAL frequencies are 12,
443 * 20, 24 or 48 MHz; PHYSET.CLKSEL[1:0] selects which one.
444 *
445 * EK-RA8D2 fits a 24 MHz crystal (see ra8_cgc.c::k_ra8_pll2_local_t
446 * comments), so CLKSEL must be 11b (k_ra8_physet_clksel_24). USB60CLK
447 * (60 MHz, PLL2P/4) is still required -- it is the LINK domain clock
448 * for the USBHS module itself -- but it does NOT feed the PHY UTMI
449 * PLL. Earlier revisions of this code mis-modeled CLKSEL as
450 * "USB60CLK / 5 = 12 MHz" and bisected over {12,48,24,20}; the host
451 * never enumerated because the PHY PLL was locking at the wrong
452 * frequency, producing malformed HS chirp K signaling. */
454 const ra8_err_t lock_err =
455 internal_usbhs_try_clksel(physet, lpsts, (uint16_t)k_ra8_physet_clksel_24);
457 s_usbhs_init_probe = (uint32_t)*physet;
458
459 if (lock_err != k_ra8_ok) {
461 ra8_log_error(s_tag, "usbhs: PHY PLL lock timeout (CLKSEL=24)");
462 } else {
465
466 /* HUM Figure 37.2 p 2121: USBE/DRPD are programmed AFTER PLLLOCK
467 * is observed. Internally splits into a separate helper to keep
468 * each function under the NASA Rule 4 budget. */
471
472 /* HUM Ch 37.2.2 "BUSWAIT : CPU Bus Wait Register", p 2063 */
473 reg->BUSWAIT = (uint16_t)k_ra8_buswait_default;
474 *physet = (uint16_t)(*physet | (uint16_t)k_ra8_physet_repsel_16);
475 }
476 return lock_err;
477}
478
496{
497 /* HUM Ch 36.2.7 "CFIFOSEL : CFIFO Port Select Register", p 1976 */
498 /* HUM Ch 37.2.8 "CFIFOSEL : CFIFO Port Select Register", p 2071 */
502
503 /* HUM Ch 36.2.20 "DCPMAXP : DCP Max Packet Size Register", p 1999 */
504 /* HUM Ch 37.2.30 "DCPMAXP : DCP Max Packet Size Register", p 2092 */
505 reg->DCPCFG = 0U;
507 reg->DCPCTR = 0U;
508
509 /* HUM Ch 37.2.18 INTSTS0 Note 3 (p 2082): "To clear the CTRT, DVST,
510 * SOFR, RESM, or VBINT flags, write 0 only to the flags to be cleared.
511 * Write 1 to the other flags. Do not write 0 to the status flags
512 * indicating 0." A wholesale ``INTSTS0 = 0`` write therefore clears
513 * any stale CTRT/DVST/SOFR/RESM/VBINT edges that the PHY bring-up
514 * sequence latched (mirrors FSP IP1 ``hw_usb_pmodule_init``: see
515 * r_usb_preg_access.c::hw_usb_pmodule_init which writes
516 * ``INTSTS0 = 0`` immediately before INTENB0 on the HS branch).
517 * Without this flush a stale RESM/VBINT bit gets ORed into every
518 * polled-dispatch snapshot and clutters the JLink-readable
519 * ``s_intsts0_observed_or`` probe (observed value 0xD0D0 with bits
520 * 14/15 set even though the host issued no real resume). */
521 reg->INTSTS0 = 0U;
522
523 /* HUM Ch 36.2.10 "INTENB0 : Interrupt Enable Register 0", p 1980.
524 * Mirrors FSP r_usb_basic.c::usb_pmodule_init mask: BEMP, BRDY, NRDY,
525 * CTRT, DVST, SOFR, RSME, VBSE. Enabling SOFR + RSME is required for
526 * HS so the IP latches resume / SOF events in INTSTS0; without RSME
527 * a host wake from suspend leaves the device asleep. */
528 /* SOFR and RSME are intentionally NOT enabled here:
529 * - SOFR fires every 125us on HS (= 8 kHz);
530 * - RSME stays asserted on USBHS via the PHY's USBR signal
531 * while the host holds the bus in resume signalling;
532 * Both starve PendSV and the demo worker thread never gets
533 * scheduled. NRDY is still useful (drives the bridge's per-pipe
534 * NAK re-arm) and does not cause a storm on its own. RSME / SOFR
535 * status is still readable from INTSTS0 for poll-style drivers. */
536 reg->INTENB0 = (uint16_t)((1U << k_ra8_int0_bit_bemp) | (1U << k_ra8_int0_bit_brdy) |
537 (1U << k_ra8_int0_bit_nrdy) | (1U << k_ra8_int0_bit_ctrt) |
538 (1U << k_ra8_int0_bit_dvst) | (1U << k_ra8_int0_bit_vbse));
539 reg->INTENB1 = 0U;
540 reg->BRDYENB = 0U;
541 reg->NRDYENB = 0U;
542 reg->BEMPENB = 0U;
543}
544
567{
568 /* HUM Ch 36.2.1 "SYSCFG : System Configuration Control Register", p 1967 */
569 reg->SYSCFG = (uint16_t)(reg->SYSCFG | (uint16_t)(1U << k_ra8_syscfg_bit_scke));
571 bool scke_ready = false;
572 for (uint32_t i = 0U; i < (uint32_t)k_ra8_usbhs_scke_poll_limit; ++i) {
573 /* HUM Ch 36.2.1 "SYSCFG : System Configuration Control Register" p 1967 */
574 const bool scke_set = (reg->SYSCFG & (uint16_t)(1U << k_ra8_syscfg_bit_scke)) != 0U;
575#if defined(RA8_OFF_TARGET) && defined(UNIT_TEST)
576 /* HUM Ch 36.2.1 "SYSCFG : System Configuration Control Register" p 1967 */
577 const bool observed = ra8_fake_mmio_poll(&reg->SYSCFG, i, scke_set);
578#else
579 const bool observed = scke_set;
580#endif
581 if (observed) {
582 scke_ready = true;
583 break;
584 }
585 }
586 if (scke_ready) {
587 reg->SYSCFG = (uint16_t)(reg->SYSCFG & (uint16_t)~(uint16_t)(1U << k_ra8_syscfg_bit_drpd));
588 reg->SYSCFG = (uint16_t)(reg->SYSCFG | (uint16_t)(1U << k_ra8_syscfg_bit_usbe));
589 status = k_ra8_ok;
590 }
591 return status;
592}
static const char * s_tag
Logging / check tag.
Definition ra8_app.c:17
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Validation and Error-Checking Macros for ra8-firmware.
Error Code Definitions for ra8-firmware.
@ k_ra8_err_hw_timeout
Hardware timed out waiting for a flag or handshake.
Definition ra8_err.h:304
@ 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
Bounded wait-flag primitives for RA8D2 HAL drivers.
Lightweight Logging Interface for ra8-firmware.
#define ra8_log_error(tag, message)
RA8 log error.
Definition ra8_log.h:335
SysTick-based tick counter, delay and timestamp helpers.
void ra8_delay_ms(uint32_t ms)
Busy-wait for at least ms milliseconds.
Definition ra8_time.c:129
Native USB controller driver public API (device + host modes).
Cross-TU surface for the ra8_usb driver split.
@ k_ra8_usb_dcp_max_packet
EP0 default packet size.
static ra8_err_t internal_usbhs_wait_pll_lock_short(void)
Bounded PHY PLL-lock poll for a single CLKSEL attempt.
ra8_usbhs_phy_step_t
Sentinel values for the per-step USBHS PHY bring-up probe.
@ k_ra8_usbhs_phy_step_dirpd_clear
PHY analog powered.
@ k_ra8_usbhs_phy_step_pll_released
PHY PLLRESET cleared.
@ k_ra8_usbhs_phy_step_clksel_12
PHYSET CLKSEL forced to 12.
@ k_ra8_usbhs_phy_step_hse_set
SYSCFG.HSE asserted.
@ k_ra8_usbhs_phy_step_suspendm_set
LPSTS SUSPENDM=1.
@ k_ra8_usbhs_phy_step_pll_locked
PLLSTA.PLLLOCK observed.
@ k_ra8_usbhs_phy_step_usbe_set
SYSCFG DRPD=0 USBE=1.
static ra8_err_t internal_usbhs_try_clksel(volatile uint16_t *physet, volatile uint16_t *lpsts, uint16_t clksel_value)
One iteration of the CLKSEL bisect: try a single codepoint.
static volatile uint16_t s_clksel_attempt_pllsta[4]
Per-attempt PLLSTA capture array for the bisect harness.
static volatile uint8_t s_clksel_winner
Diagnostic capture of the PHYSET.CLKSEL[1:0] codepoint that produced PLL lock during the bisect harne...
static void internal_usb_delay_1us(void)
Microsecond busy-wait helper for USB PHY bring-up.
ra8_err_t priv_usbhs_phy_bringup(volatile r_usb_regs_t *reg)
USBHS embedded-PHY bring-up (HUM Figure 37.2 p 2121).
static void internal_usbhs_arm_phy_reset(volatile uint16_t *physet_reg, uint16_t clksel_value)
Drive PHYSET into "PHY in reset" with the given CLKSEL field.
static volatile uint16_t s_usbhs_pllsta_probe
Diagnostic capture of PLLSTA at end of PHY bring-up.
ra8_usbhs_init_local_t
Local sentinels used by the USBHS PHY bring-up sequence.
Definition ra8_usb_phy.c:61
@ k_ra8_usbhs_probe_hi_shift
RA8 usbhs probe hi shift.
Definition ra8_usb_phy.c:73
@ k_ra8_usbhs_scke_poll_limit
RA8 usbhs scke poll limit.
Definition ra8_usb_phy.c:72
@ k_ra8_usbhs_pll_lock_poll_limit
RA8 usbhs pll lock poll limit.
Definition ra8_usb_phy.c:67
@ k_ra8_usbhs_delay_1us_iters
RA8 usbhs delay 1us iters.
Definition ra8_usb_phy.c:62
@ k_ra8_usbhs_probe_lo_mask
RA8 usbhs probe lo mask.
Definition ra8_usb_phy.c:74
@ k_ra8_usbhs_pll_lock_attempt_limit
RA8 usbhs pll lock attempt limit.
Definition ra8_usb_phy.c:71
ra8_usbhs_clksel_attempt_t
Iteration order for the CLKSEL[1:0] bisect harness.
Definition ra8_usb_phy.c:97
@ k_ra8_usbhs_clksel_attempts_n
RA8 usbhs clksel attempts n.
Definition ra8_usb_phy.c:98
@ k_ra8_usbhs_clksel_no_winner
Sentinel: bisect exhausted.
Definition ra8_usb_phy.c:99
static volatile uint8_t s_phy_step_probe
Diagnostic step counter for the USBHS PHY bring-up sequence.
static volatile uint32_t s_usbhs_init_probe
Diagnostic capture of staged USBHS bring-up register values.
void priv_usb_init_common(volatile r_usb_regs_t *reg)
Programme the post-SYSCFG common registers (FIFO, DCP, INTENB).
static void internal_usbhs_enable_syscfg(volatile r_usb_regs_t *reg)
Drive USBHS SYSCFG DRPD=0, USBE=1 after PLLRESET is released.
ra8_err_t priv_usbfs_module_bringup(volatile r_usb_regs_t *reg)
USBFS module bring-up (FSP hw_usb_pmodule_init IP0 branch).
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.
@ k_ra8_lpsts_suspendm
b14: UTMI SuspendM (1=run).
@ k_ra8_pllsta_plllock
b0 : PHY PLL locked flag.
@ k_ra8_physet_clksel
b5-4: input clock select mask.
@ k_ra8_physet_pllreset
b1 : PHY PLL reset.
@ k_ra8_physet_repsel_16
b9-8: 16-cycle terminator.
@ k_ra8_physet_clksel_24
b5-4 = 11b: 24 MHz reference.
@ k_ra8_physet_dirpd
b0 : PHY power-down (1=PD).
@ k_ra8_syscfg_bit_usbe
USB module enable.
@ k_ra8_syscfg_bit_hse
HS enable (HS instance only).
@ k_ra8_syscfg_bit_drpd
D+/D- pull-down (host).
@ k_ra8_syscfg_bit_scke
USB clock enable.
static volatile uint16_t * ra8_usbhs_physet(void)
Get pointer to USBHS PHYSET register.
static volatile uint16_t * ra8_usbhs_pllsta(void)
Get pointer to USBHS PLLSTA register (read-only).
@ k_ra8_fifosel_mbw_16
16-bit FIFO access width.
@ k_ra8_int0_bit_bemp
Buffer-empty interrupt.
@ k_ra8_int0_bit_dvst
Device-state transition.
@ k_ra8_int0_bit_brdy
Buffer-ready interrupt.
@ k_ra8_int0_bit_ctrt
Control-transfer-stage transition.
@ k_ra8_int0_bit_vbse
VBUS change.
@ k_ra8_int0_bit_nrdy
Buffer-not-ready interrupt.
@ k_ra8_buswait_default
4-wait + b11-b8 reserved-write.
USB FS / HS register window (device-mode subset).
volatile uint16_t NRDYENB
+0x038 NRDY Interrupt Enable.
volatile uint16_t DCPCTR
+0x060 DCP Control.
volatile uint16_t CFIFOSEL
+0x020 Control FIFO select.
volatile uint16_t DCPMAXP
+0x05E DCP Max Packet.
volatile uint16_t INTENB0
+0x030 Interrupt Enable 0.
volatile uint16_t INTSTS0
+0x040 Interrupt Status 0.
volatile uint16_t SYSCFG
+0x000 System Configuration.
volatile uint16_t BRDYENB
+0x036 BRDY Interrupt Enable.
volatile uint16_t BUSWAIT
+0x002 CPU bus wait.
volatile uint16_t INTENB1
+0x032 Interrupt Enable 1.
volatile uint16_t DCPCFG
+0x05C DCP Configuration.
volatile uint16_t D0FIFOSEL
+0x028 DMA FIFO 0 select.
volatile uint16_t BEMPENB
+0x03A BEMP Interrupt Enable.
volatile uint16_t D1FIFOSEL
+0x02C DMA FIFO 1 select.