ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_cgc_eswclk.c
Go to the documentation of this file.
1
27
28#include <stdint.h>
29
30#include "ra8_attributes.h"
31#include "ra8_cgc.h"
32#include "ra8_cgc_internal.h"
33#include "ra8_cgc_regs.h"
34#include "ra8_check.h"
35#include "ra8_err.h"
36#include "ra8_hw_err.h"
37#include "ra8_log.h"
38#include "ra8_mstp.h"
40#include "ra8_system_regs.h"
41#include "ra8_time_constants.h"
42
43static const char* s_tag = "CGC";
44
54
70
84static uint32_t s_eswclk_hz = 0U;
85
113RA8_INTERNAL static ra8_err_t internal_wait_cksrdy(volatile const uint8_t* ckcr_reg,
114 uint8_t expected)
115{
116 const uint8_t mask = (uint8_t)(1U << k_ra8_eswckcr_bit_srdy);
117 /* Bounded wait through ra8_hw_err.h: on host tests the ra8_fake_mmio
118 * seam decides the poll (first-poll success unless a test arms a
119 * fault), so the real timeout leg is reachable everywhere. */
120 if (expected != 0U) {
121 return ra8_hw_wait_flag_set8(ckcr_reg, mask, (uint32_t)k_ra8_cgc_eswclk_poll_limit);
122 }
123 return ra8_hw_wait_flag_clear8(ckcr_reg, mask, (uint32_t)k_ra8_cgc_eswclk_poll_limit);
124}
125
157RA8_INTERNAL static ra8_err_t internal_switch_eswcr_to_pll1p(volatile uint8_t* ckcr_reg,
158 volatile uint8_t* divcr_reg,
159 uint8_t div_code)
160{
161 const uint8_t sreq_mask = (uint8_t)(1U << k_ra8_eswckcr_bit_sreq);
162 const uint8_t srdy_mask = (uint8_t)(1U << k_ra8_eswckcr_bit_srdy);
163 /* FSP `bsp_clocks.c` ::bsp_peripheral_clock_set sequence:
164 * 1. Set SREQ=1 (preserve current SEL via read-modify-write).
165 * 2. Spin-wait SRDY=1.
166 * 3. Programme CKDIVCR (peripheral clock now stopped).
167 * 4. Write `source | SREQ | SRDY`. Writing SRDY in the same word
168 * is mandatory per FSP (bsp_clocks.c line 2896); without it
169 * the subsequent SREQ-clear hangs.
170 * 5. Clear SREQ -> peripheral clock starts.
171 * 6. Spin-wait SRDY=0 (start acknowledged). */
172 *ckcr_reg = (uint8_t)(*ckcr_reg | sreq_mask);
173 ra8_err_t err = internal_wait_cksrdy(ckcr_reg, 1U);
174 if (err != k_ra8_ok) {
175 return err;
176 }
177 *divcr_reg = div_code;
178 const uint8_t src = (uint8_t)((uint8_t)k_ra8_eswcksel_pll1p & k_ra8_eswckcr_mask_sel);
179 *ckcr_reg = (uint8_t)(src | sreq_mask | srdy_mask);
180 *ckcr_reg = (uint8_t)(*ckcr_reg & (uint8_t)~sreq_mask);
181 return internal_wait_cksrdy(ckcr_reg, 0U);
182}
183
206{
207 volatile const uint8_t* const pd = ra8_sys_pdctreswm();
208 const uint8_t mask = (uint8_t)(1U << bit);
209 /* Bounded wait through ra8_hw_err.h: on host tests the ra8_fake_mmio
210 * seam decides the poll, so the stuck-flag timeout leg is testable. */
211 return ra8_hw_wait_flag_clear8(pd, mask, (uint32_t)k_ra8_cgc_eswclk_poll_limit);
212}
213
234{
236 {
237 volatile uint8_t* const pd = ra8_sys_pdctreswm();
238 /* Only kick the power-on path if the domain is currently gated --
239 * PDCSF=0 and PDPGSF=1 is the FSP-canonical "power off" signature. */
240 const uint8_t state = *pd;
241 if (((state & (1U << k_ra8_pdctr_bit_pdcsf)) == 0U) &&
242 ((state & (1U << k_ra8_pdctr_bit_pdpgsf)) != 0U)) {
243 *pd = (uint8_t)(state & (uint8_t)~(1U << k_ra8_pdctr_bit_pdde));
244 }
245 }
246 /* Wait for power-on outside the PRCR window (the status flags are
247 * read-only and not protected). */
249 if (err != k_ra8_ok) {
250 ra8_log_error(s_tag, "eswclk: PDCSF stuck");
251 return err;
252 }
254 if (err != k_ra8_ok) {
255 ra8_log_error(s_tag, "eswclk: PDPGSF stuck");
256 return err;
257 }
258 return k_ra8_ok;
259}
260
281{
282 ra8_err_t err = k_ra8_ok;
284 {
287 (uint8_t)k_ra8_eswckdivcr_div4);
288 if (err != k_ra8_ok) {
289 ra8_log_error(s_tag, "eswclk: ESWCKCR handshake timeout");
290 break;
291 }
294 (uint8_t)k_ra8_eswckdivcr_div2);
295 if (err != k_ra8_ok) {
296 ra8_log_error(s_tag, "eswclk: ESWPCKCR handshake timeout");
297 break;
298 }
299 }
300 return err;
301}
302
304{
305 ra8_log_info(s_tag, "eswclk init (PLL1P/4 = 250 MHz, eswphyclk PLL1P/2 = 500 MHz)");
306
307 /* Step 1: ESWCKCR / ESWPCKCR / USBCKCR / USB60CKCR all share the same
308 * CKSEL reset-default (HOCO) and the same SREQ -> SRDY handshake
309 * plumbing -- HOCO MUST be running before the SREQ is raised. */
311 if (hoco_err != k_ra8_ok) {
312 ra8_log_error(s_tag, "eswclk: HOCO stabilization timeout");
313 return hoco_err;
314 }
315 /* Step 2: turn on the ESWM peripheral power domain. */
317 if (pd_err != k_ra8_ok) {
318 return pd_err;
319 }
320 /* Step 3: release the MSTPC28 ETHPHYCLK module-stop gate. */
321 const ra8_err_t ethphy_mst_err = ra8_mstp_enable(k_ra8_mstp_ethphyclk);
322 if (ethphy_mst_err != k_ra8_ok) {
323 ra8_log_error(s_tag, "eswclk: ethphyclk MSTP release failed");
324 return ethphy_mst_err;
325 }
326 /* Step 4: switch ESWCKCR + ESWPCKCR to PLL1P. */
327 const ra8_err_t cks_err = internal_eswclk_program_cks();
328 if (cks_err != k_ra8_ok) {
329 return cks_err;
330 }
332 ra8_log_info(s_tag, "eswclk ready");
333 return k_ra8_ok;
334}
335
337{
338 if (out_hz == nullptr) {
339 return k_ra8_err_null_ptr;
340 }
341 *out_hz = s_eswclk_hz;
342 return k_ra8_ok;
343}
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).
High-level Clock Generation Circuit driver.
ra8_err_t ra8_cgc_eswclk_hz(uint32_t *out_hz)
Query the current ESWCLK (Ethernet Switch clock) frequency.
static ra8_err_t internal_wait_pdctreswm_clear(uint8_t bit)
Wait for a PDCTRESWM status bit to clear within a bounded budget.
static ra8_err_t internal_switch_eswcr_to_pll1p(volatile uint8_t *ckcr_reg, volatile uint8_t *divcr_reg, uint8_t div_code)
Switch one ESW-family peripheral clock to PLL1P with the given divider code.
ra8_err_t ra8_cgc_eswclk_init(void)
Bring up the Ethernet Switch Module (ESWM / RMAC) clock.
static ra8_err_t internal_wait_cksrdy(volatile const uint8_t *ckcr_reg, uint8_t expected)
Spin-wait for a peripheral clock control register's CKSRDY bit to match expected.
ra8_cgc_eswckdivcr_t
@ k_ra8_eswckdivcr_div32
/32
@ k_ra8_eswckdivcr_div4
/4 – PLL1P/4 = 250 MHz (ESWCLK target).
@ k_ra8_eswckdivcr_div2
/2 – PLL1P/2 = 500 MHz (ESWPHYCLK target).
@ k_ra8_eswckdivcr_div64
/64
@ k_ra8_eswckdivcr_div1
/1
@ k_ra8_eswckdivcr_div16
/16
@ k_ra8_eswckdivcr_div8
/8
static ra8_err_t internal_eswclk_power_on_domain(void)
Power on the ESWM peripheral domain via PDCTRESWM.
static ra8_err_t internal_eswclk_program_cks(void)
Switch ESWCLK + ESWPHYCLK to PLL1P (250 MHz + 500 MHz).
static uint32_t s_eswclk_hz
Live ESWCLK frequency, published by ra8_cgc_eswclk_init.
ra8_cgc_eswclk_const_t
Constants used by the ESWCLK bring-up path.
@ k_ra8_cgc_eswclk_poll_limit
CKSRDY spin budget.
@ k_ra8_cgc_eswphyclk_target_hz
ESWPHYCLK = PLL1P / 2.
@ k_ra8_cgc_eswclk_target_hz
ESWCLK = PLL1P / 4.
Cross-TU sharing for the split Clock Generation Circuit driver.
ra8_err_t priv_ra8_cgc_ensure_hoco_running_for_usb_ck(void)
Ensure HOCO is running so a USBCKCR/USB60CKCR/ESWCKCR SREQ->SRDY handshake can drain.
Clock Generation Circuit (CGC) field layouts for the Renesas RA8D2.
Validation and Error-Checking Macros for ra8-firmware.
Error Code Definitions for ra8-firmware.
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_null_ptr
Pointer was NULL where a valid pointer was required.
Definition ra8_err.h:478
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.
static ra8_err_t ra8_hw_wait_flag_clear8(volatile const uint8_t *reg, uint8_t mask, uint32_t budget)
Spin until (*reg & mask) == 0 or budget runs out.
Definition ra8_hw_err.h:262
static ra8_err_t ra8_hw_wait_flag_set8(volatile const uint8_t *reg, uint8_t mask, uint32_t budget)
Spin until (*reg & mask) != 0 or budget runs out.
Definition ra8_hw_err.h:219
Lightweight Logging Interface for ra8-firmware.
#define ra8_log_info(tag, message)
RA8 log info.
Definition ra8_log.h:364
#define ra8_log_error(tag, message)
RA8 log error.
Definition ra8_log.h:335
Ref-counted Module Stop Control wrapper for the RA8D2.
ra8_err_t ra8_mstp_enable(ra8_mstp_t id)
Reference-counted "ungate this peripheral" request.
Definition ra8_mstp.c:343
@ k_ra8_mstp_ethphyclk
MSTPC28 Ether-PHY clock.
Scoped PRCR unlock helper.
#define RA8_PROTECTED_WRITE(unlock_val)
Scoped PRCR unlock block.
System Control (SYSC) register layout for the Renesas RA8D2.
@ k_ra8_prcr_unlock_cgc
RA8 prcr unlock cgc.
@ k_ra8_prcr_unlock_lpm
RA8 prcr unlock lpm.
static volatile uint8_t * ra8_sys_eswpckcr(void)
Get pointer to the 8-bit ESWPCKCR (Ethernet-PHY clock source).
static volatile uint8_t * ra8_sys_eswckcr(void)
Get pointer to the 8-bit ESWCKCR (Ethernet Switch / RMAC clock source).
static volatile uint8_t * ra8_sys_eswpckdivcr(void)
Get pointer to the 8-bit ESWPCKDIVCR (ESWPHYCLK divider; 4-bit CKDIV field).
static volatile uint8_t * ra8_sys_pdctreswm(void)
Get pointer to the 8-bit PDCTRESWM (ESWM power-domain control).
@ k_ra8_eswcksel_pll1p
PLL1P (preferred for 125 MHz ESWCLK).
@ k_ra8_eswckcr_mask_sel
CKSEL field is 4 bits wide.
@ k_ra8_eswckcr_bit_sreq
CKSREQ – request switch.
@ k_ra8_eswckcr_bit_srdy
CKSRDY – handshake ready.
static volatile uint8_t * ra8_sys_eswckdivcr(void)
Get pointer to the 8-bit ESWCKDIVCR (ESWCLK divider; 4-bit CKDIV field).
@ k_ra8_pdctr_bit_pdpgsf
Read-only: power-gating status flag.
@ k_ra8_pdctr_bit_pdcsf
Read-only: power-control status flag.
@ k_ra8_pdctr_bit_pdde
1 = power disabled, 0 = power enabled.
Named Time Unit Constants for Delay / Timeout / Tick Math.