ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
gpio.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_gpio_constants.h"
30#include "ra8_icu.h"
31#include "ra8_isr.h"
32#include "ra8_log.h"
33#include "ra8_pfs_regs.h"
34#include "ra8_pin_interface.h"
35#include "ra8_pin_validator.h"
36#include "ra8_port_constants.h"
37#include "ra8_port_regs.h"
38#include "ra8_port_utils.h"
39
48
49static const char* s_tag = "GPIO";
50
70{
71 const ra8_port_t p = RA8_PIN_PORT(pin);
72 const ra8_pin_t b = RA8_PIN_PIN(pin);
73 if ((uint8_t)p > k_ra8_port_max) {
75 }
76 if ((uint8_t)b > k_ra8_pin_max) {
78 }
80 if (err != k_ra8_ok) {
81 return err;
82 }
83 *out_port = p;
84 *out_pin = b;
85 return k_ra8_ok;
86}
87
89{
92
93 ra8_err_t err = internal_claim(pin, &port, &bit);
94 if (err != k_ra8_ok) {
95 ra8_log_error_val(s_tag, "claim failed", (uint32_t)err);
96 return err;
97 }
98
99 /* Programme PFS: PMR=0 (GPIO), PDR=1 (output), PODR=init level. */
100 volatile uint32_t* pfs = ra8_pfs_pmn(port, bit);
101 if (pfs == nullptr) {
102 /* The claim is already taken by internal_claim above, so it has to be
103 * handed back before bailing out -- otherwise the pin stays owned by a
104 * call that configured nothing, and every later claim of it fails. This
105 * mirrors ra8_pfs_route_peripheral, which releases on the same branch. */
108 }
109
110 const uint32_t new_val =
111 (uint32_t)((init_level == k_ra8_level_high) ? k_ra8_pfs_mask_podr : 0U) | k_ra8_pfs_mask_pdr;
112
114 *pfs = new_val;
116
117 ra8_log_info_val(s_tag, "output init pin", (uint32_t)pin);
118 return k_ra8_ok;
119}
120
122{
125
126 ra8_err_t err = internal_claim(pin, &port, &bit);
127 if (err != k_ra8_ok) {
128 ra8_log_error_val(s_tag, "claim failed", (uint32_t)err);
129 return err;
130 }
131
132 volatile uint32_t* pfs = ra8_pfs_pmn(port, bit);
133 if (pfs == nullptr) {
134 /* Same ownership hand-back as ra8_gpio_output_init: internal_claim has
135 * already taken the pin, so returning without releasing would strand it. */
138 }
139
140 /* PMR=0 (GPIO), PDR=0 (input), PCR according to pull. */
141 uint32_t new_val = 0U;
142 if (pull == k_ra8_pull_up) {
143 new_val |= k_ra8_pfs_mask_pcr;
144 }
145
147 *pfs = new_val;
149
150 ra8_log_info_val(s_tag, "input init pin", (uint32_t)pin);
151 return k_ra8_ok;
152}
153
155{
156 const ra8_port_t port = RA8_PIN_PORT(pin);
157 const ra8_pin_t bit = RA8_PIN_PIN(pin);
158 if ((uint8_t)port > k_ra8_port_max) {
160 }
161 if ((uint8_t)bit > k_ra8_pin_max) {
163 }
164
165 volatile r_port_regs_t* port_regs = ra8_port(port);
166 if (port_regs == nullptr) {
168 }
169
170 /* PCNTR3 high half = PORR (clear), low half = POSR (set). Writing
171 * a 1 to the relevant half drives the pin; writing 0 leaves other
172 * pins alone -- race-free. */
173 const uint32_t bit_mask = (uint32_t)(1UL << (uint32_t)bit);
174 if (level == k_ra8_level_high) {
175 port_regs->PCNTR3 = bit_mask; /* POSR in low half. */
176 } else {
177 port_regs->PCNTR3 = bit_mask << (uint32_t)k_ra8_pcntr_high_half_shift;
178 }
179 return k_ra8_ok;
180}
181
183{
184 const ra8_port_t port = RA8_PIN_PORT(pin);
185 const ra8_pin_t bit = RA8_PIN_PIN(pin);
186 if ((uint8_t)port > k_ra8_port_max) {
188 }
189 if ((uint8_t)bit > k_ra8_pin_max) {
191 }
192
193 volatile r_port_regs_t* port_regs = ra8_port(port);
194 if (port_regs == nullptr) {
196 }
197
198 /* Read PODR (high half of PCNTR1) and invert the bit. */
199 const uint32_t pcntr1_val = port_regs->PCNTR1;
200 const uint32_t podr = (pcntr1_val >> (uint32_t)k_ra8_pcntr_high_half_shift);
201 const uint32_t bit_mask = (uint32_t)(1UL << (uint32_t)bit);
202 if ((podr & bit_mask) != 0U) {
203 port_regs->PCNTR3 = bit_mask << (uint32_t)k_ra8_pcntr_high_half_shift; /* PORR clear */
204 } else {
205 port_regs->PCNTR3 = bit_mask; /* POSR set */
206 }
207 return k_ra8_ok;
208}
209
211{
212 RA8_CHECK_NULL_PTR(out_level, s_tag, "out_level must not be nullptr");
213 const ra8_port_t port = RA8_PIN_PORT(pin);
214 const ra8_pin_t bit = RA8_PIN_PIN(pin);
215 if ((uint8_t)port > k_ra8_port_max) {
217 }
218 if ((uint8_t)bit > k_ra8_pin_max) {
220 }
221
222 volatile const r_port_regs_t* port_regs = ra8_port(port);
223 if (port_regs == nullptr) {
225 }
226
227 const uint32_t pidr = port_regs->PCNTR2;
228 const uint32_t bit_mask = (uint32_t)(1UL << (uint32_t)bit);
229 *out_level = ((pidr & bit_mask) != 0U) ? k_ra8_level_high : k_ra8_level_low;
230 return k_ra8_ok;
231}
232
237
239{
240 RA8_CHECK_NULL_PTR(owner, s_tag, "owner must not be nullptr");
241
242 const ra8_port_t port = RA8_PIN_PORT(pin);
243 const ra8_pin_t bit = RA8_PIN_PIN(pin);
244 if ((uint8_t)port > k_ra8_port_max) {
246 }
247 if ((uint8_t)bit > k_ra8_pin_max) {
249 }
250
252 if (claim != k_ra8_ok) {
253 ra8_log_error_val(s_tag, "peripheral claim failed", (uint32_t)claim);
254 return claim;
255 }
256
257 volatile uint32_t* pfs = ra8_pfs_pmn(port, bit);
258 if (pfs == nullptr) {
261 }
262
263 /* HUM Ch 20.2.4 "Notes on the PmnPFS Register Setting" p 859 -- PMR
264 * MUST be cleared before specifying a new PSEL, otherwise the pin can
265 * glitch through whatever the previous peripheral function happened
266 * to be while the new PSEL is decoded. The correct sequence is:
267 * 1. PMR := 0 (force GPIO mode)
268 * 2. PFS := PSEL only, PMR still 0 (programme the new function)
269 * 3. PFS := PSEL | PMR=1 (hand the pin to the peripheral)
270 * @note PMSAR defaults to all-Secure on RA8D2 reset, so writing PWPR
271 * (NS) and PWPRS (S) unconditionally is harmless: the chip
272 * silently drops whichever side does not own the port. */
273 const uint32_t psel_only = (((uint32_t)psel) << (uint32_t)k_ra8_pfs_bit_psel0);
274 const uint32_t new_val = k_ra8_pfs_mask_pmr | psel_only;
275
277 /* HUM Ch 20.2.4 "Notes on the PmnPFS Register Setting" p 859 */
278 *pfs = 0U; /* step 1: PMR = 0. */
279 *pfs = psel_only; /* step 2: PSEL with PMR=0. */
280 *pfs = new_val; /* step 3: PSEL | PMR=1. */
282
283 ra8_log_info_val(s_tag, "peripheral route pin", (uint32_t)pin);
284 return k_ra8_ok;
285}
286
288{
289 const ra8_port_t port = RA8_PIN_PORT(pin);
290 const ra8_pin_t bit = RA8_PIN_PIN(pin);
291 if ((uint8_t)port > k_ra8_port_max) {
293 }
294 if ((uint8_t)bit > k_ra8_pin_max) {
296 }
297
298 volatile uint32_t* pfs = ra8_pfs_pmn(port, bit);
299 if (pfs == nullptr) {
301 }
302
303 /* Read-modify-write only the DSCR[1:0] field; PSEL / PMR / direction
304 * are preserved so an already-routed pin keeps its function.
305 * HUM Ch 20.2.4 "Notes on the PmnPFS Register Setting" p 859 -- the
306 * PMR-clear dance is required only when PSEL changes; a DSCR-only
307 * update of an in-function pin does not retouch PSEL. */
308 const uint32_t dscr_field =
309 ((uint32_t)dscr << (uint32_t)k_ra8_pfs_bit_dscr0) & (uint32_t)k_ra8_pfs_mask_dscr;
311 *pfs = (*pfs & ~(uint32_t)k_ra8_pfs_mask_dscr) | dscr_field;
313
314 ra8_log_info_val(s_tag, "pin drive strength", (uint32_t)pin);
315 return k_ra8_ok;
316}
317
318/* =============================================================================
319 * External IRQ attachment
320 * =============================================================================
321 */
322
337{
338 return (ra8_elc_event_t)((uint16_t)k_ra8_gpio_irq_event_base + (uint16_t)irq_num);
339}
340
342 uint8_t irq_num,
343 const ra8_gpio_irq_cfg_t* cfg,
344 ra8_isr_handler_t handler,
345 void* ctx)
346{
347 RA8_CHECK_NULL_PTR(cfg, s_tag, "cfg must not be nullptr");
348 RA8_CHECK_NULL_PTR(handler, s_tag, "handler must not be nullptr");
349 if (irq_num > k_ra8_gpio_irq_num_max) {
351 }
352
354 if (err != k_ra8_ok) {
355 return err;
356 }
357
358 const ra8_icu_irq_cfg_t icu_cfg = {
359 .sense = cfg->sense,
360 .filter_div = cfg->filter_div,
361 .filter_en = cfg->filter_en,
362 };
363 err = ra8_icu_configure_irq_pin(irq_num, &icu_cfg);
364 if (err != k_ra8_ok) { /* GCOVR_EXCL_BR_LINE -- irq_num bounded */
365 (void)ra8_pin_validator_release(pin); /* GCOVR_EXCL_LINE -- icu-reject cleanup */
366 return err; /* GCOVR_EXCL_LINE -- icu-reject cleanup */
367 }
368
369 const ra8_elc_event_t event = internal_event_for_irq(irq_num);
370 err = ra8_isr_register(event, handler, ctx, cfg->priority, nullptr);
371 if (err != k_ra8_ok) { /* GCOVR_EXCL_BR_LINE -- fresh reset_irq_state */
372 /* GCOVR_EXCL_START -- isr-reject unwind; ra8_isr_register cannot fail here */
373 const ra8_icu_irq_cfg_t zero_cfg = {
374 .sense = (ra8_icu_irqmd_t)0U,
375 .filter_div = (ra8_icu_fclksel_t)0U,
376 .filter_en = false,
377 };
378 /* GCOVR_EXCL_STOP */
379 (void)ra8_icu_configure_irq_pin(irq_num, &zero_cfg); /* GCOVR_EXCL_LINE -- isr-reject unwind */
380 (void)ra8_pin_validator_release(pin); /* GCOVR_EXCL_LINE -- isr-reject unwind */
381 return err; /* GCOVR_EXCL_LINE -- isr-reject unwind */
382 }
383
384 ra8_log_info_val(s_tag, "attach irq pin", (uint32_t)pin);
385 return k_ra8_ok;
386}
387
389{
390 if (irq_num > k_ra8_gpio_irq_num_max) {
392 }
393
394 const ra8_elc_event_t event = internal_event_for_irq(irq_num);
395 const ra8_err_t err = ra8_isr_unregister(event);
396 if (err != k_ra8_ok) {
397 return err;
398 }
399
400 const ra8_icu_irq_cfg_t zero_cfg = {
401 .sense = (ra8_icu_irqmd_t)0U,
402 .filter_div = (ra8_icu_fclksel_t)0U,
403 .filter_en = false,
404 };
405 (void)ra8_icu_configure_irq_pin(irq_num, &zero_cfg);
407
408 ra8_log_info_val(s_tag, "detach irq pin", (uint32_t)pin);
409 return k_ra8_ok;
410}
411
412/* =============================================================================
413 * Concrete DI vtable (forwarded thunks)
414 * =============================================================================
415 */
416
434{
435 (void)ctx;
436 return ra8_gpio_output_init(pin, level);
437}
438
456{
457 (void)ctx;
458 return ra8_gpio_write(pin, level);
459}
460
477{
478 (void)ctx;
479 return ra8_gpio_read(pin, out);
480}
481
497{
498 (void)ctx;
499 return ra8_gpio_toggle(pin);
500}
501
503 .output_init = internal_pin_if_output_init,
504 .write = internal_pin_if_write,
505 .read = internal_pin_if_read,
506 .toggle = internal_pin_if_toggle,
507 .ctx = nullptr,
508};
ra8_err_t ra8_gpio_read(ra8_port_pin_t pin, ra8_level_t *out_level)
Read a previously-configured input.
Definition gpio.c:210
ra8_err_t ra8_gpio_input_init(ra8_port_pin_t pin, ra8_pin_pull_t pull)
Configure a pin as a digital input.
Definition gpio.c:121
ra8_err_t ra8_pfs_set_drive_strength(ra8_port_pin_t pin, ra8_pfs_dscr_t dscr)
Set the output drive strength (PmnPFS.DSCR) of an already-routed pin.
Definition gpio.c:287
ra8_err_t ra8_gpio_toggle(ra8_port_pin_t pin)
Toggle a previously-configured output.
Definition gpio.c:182
ra8_err_t ra8_gpio_output_init(ra8_port_pin_t pin, ra8_level_t init_level)
Configure a pin as a digital output and drive it to an initial level.
Definition gpio.c:88
static ra8_err_t internal_pin_if_toggle(void *ctx, ra8_port_pin_t pin)
Internal helper.
Definition gpio.c:496
static ra8_err_t internal_claim(ra8_port_pin_t pin, ra8_port_t *out_port, ra8_pin_t *out_pin)
Claim the pin in the validator and return the port / pin indices if the claim succeeded.
Definition gpio.c:69
ra8_gpio_irq_limits_t
Local limits for IRQ attach API.
Definition gpio.c:44
@ k_ra8_gpio_irq_num_max
External IRQ pins 0..15.
Definition gpio.c:45
@ k_ra8_gpio_irq_event_base
ELC event for IRQ0.
Definition gpio.c:46
ra8_err_t ra8_gpio_write(ra8_port_pin_t pin, ra8_level_t level)
Drive a previously-configured output to the given level.
Definition gpio.c:154
ra8_err_t ra8_gpio_detach_irq(ra8_port_pin_t pin, uint8_t irq_num)
Tear down a previously attached external IRQ.
Definition gpio.c:388
ra8_err_t ra8_gpio_release(ra8_port_pin_t pin)
Release a GPIO pin claim.
Definition gpio.c:233
static ra8_err_t internal_pin_if_output_init(void *ctx, ra8_port_pin_t pin, ra8_level_t level)
Internal helper.
Definition gpio.c:433
static ra8_elc_event_t internal_event_for_irq(uint8_t irq_num)
Internal helper.
Definition gpio.c:336
static ra8_err_t internal_pin_if_read(void *ctx, ra8_port_pin_t pin, ra8_level_t *out)
Internal helper.
Definition gpio.c:476
static ra8_err_t internal_pin_if_write(void *ctx, ra8_port_pin_t pin, ra8_level_t level)
Internal helper.
Definition gpio.c:455
ra8_err_t ra8_gpio_attach_irq(ra8_port_pin_t pin, uint8_t irq_num, const ra8_gpio_irq_cfg_t *cfg, ra8_isr_handler_t handler, void *ctx)
Wire a pin as an external IRQ input and install a handler.
Definition gpio.c:341
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
const ra8_pin_interface_t g_ra8_gpio_pin_interface
Definition gpio.c:502
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).
const char * owner
Owner.
ra8_board_eth_pin_t pin
Pin.
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
ra8_elc_event_t
Partial list of ELC events (populate as drivers need them).
Error Code Definitions for ra8-firmware.
@ k_ra8_err_hw_unmapped
Peripheral register block is not mapped in this MCU variant.
Definition ra8_err.h:345
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_gpio_invalid_pin
GPIO pin index out of range within its port (valid: 0..15).
Definition ra8_err.h:331
@ k_ra8_err_gpio_invalid_port
GPIO port index out of range for this MCU.
Definition ra8_err.h:325
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
PSEL codes for peripheral pin routing on the RA8D2.
ra8_psel_t
Peripheral-function select codes (written to PSEL[4:0]).
Interrupt Control Unit driver (IRQ pins, NMI).
ra8_err_t ra8_icu_configure_irq_pin(uint8_t irq_num, const ra8_icu_irq_cfg_t *cfg)
Programme one IRQCRi register.
Definition ra8_icu.c:76
ra8_icu_fclksel_t
IRQCRi.FCLKSEL[1:0] digital filter sampling clock (HUM 14.2.12 p 535).
ra8_icu_irqmd_t
IRQCRi.IRQMD[1:0] detection sense values (HUM 14.2.12 p 535).
NVIC + ICU IELSR allocator.
void(* ra8_isr_handler_t)(void *ctx)
Driver-supplied callback invoked on interrupt entry.
Definition ra8_isr.h:128
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
ra8_err_t ra8_isr_unregister(ra8_elc_event_t event)
Release a previously-allocated IELSR slot.
Definition ra8_isr.c:335
Lightweight Logging Interface for ra8-firmware.
#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
Pin Function Select (PFS) register layout for the Renesas RA8D2.
@ k_ra8_pfs_mask_dscr
bits 11..10.
@ k_ra8_pfs_mask_pmr
RA8 PFS mask pmr.
@ k_ra8_pfs_mask_podr
RA8 PFS mask podr.
@ k_ra8_pfs_mask_pdr
RA8 PFS mask pdr.
@ k_ra8_pfs_mask_pcr
RA8 PFS mask pcr.
@ k_ra8_pfs_bit_psel0
Peripheral select bit 0.
@ k_ra8_pfs_bit_dscr0
Drive strength (low bit).
static void ra8_pfs_pwpr_lock(void)
Re-lock PFS writes.
ra8_pfs_dscr_t
PmnPFS.DSCR[1:0] port drive-capability encodings.
static void ra8_pfs_pwpr_unlock(void)
Unlock PFS writes.
static volatile uint32_t * ra8_pfs_pmn(ra8_port_t port, ra8_pin_t pin)
Get pointer to the PmnPFS register for a single pin.
Abstract pin-driver interface for dependency injection.
Runtime Pin-Ownership Validator.
ra8_err_t ra8_pin_validator_claim(ra8_port_pin_t pin, const char *owner)
Claim a pin for a driver.
ra8_err_t ra8_pin_validator_release(ra8_port_pin_t pin)
Release a pin previously claimed by a driver.
Typed Port / Pin Constants for the RA8D2 IOPORT Module.
ra8_port_pin_t
Packed (port << 8) | pin pin identifier.
ra8_port_t
Port indices for the RA8D2 IOPORT module.
@ k_ra8_port_max
Highest valid port index.
@ k_ra8_port_0
RA8 port 0.
ra8_level_t
Digital output / input level.
@ k_ra8_level_high
Drive or read 1.
@ k_ra8_level_low
Drive or read 0.
ra8_pin_t
Pin indices within an IOPORT port (0..15).
@ k_ra8_pin_max
Highest valid pin index.
@ k_ra8_pin_0
RA8 pin 0.
#define RA8_PIN_PORT(p)
Extract the port index from a packed ra8_port_pin_t.
ra8_pin_pull_t
Pull-up/pull-down selection (where supported).
@ k_ra8_pull_up
Internal pull-up.
#define RA8_PIN_PIN(p)
Extract the pin index from a packed ra8_port_pin_t.
IOPORT (GPIO) register layout for the Renesas RA8D2.
@ k_ra8_pcntr_high_half_shift
PODR/EIDR/PORR/EORR half.
static volatile r_port_regs_t * ra8_port(ra8_port_t port)
Get the PORT register window for a given port index.
High-level GPIO helpers on top of the PORT + PFS register layer.
Per-port IOPORT register window.
volatile uint32_t PCNTR2
PCNTR2 – { EIDR[31:16], PIDR[15:0] } (read-only).
volatile uint32_t PCNTR3
PCNTR3 – { PORR[31:16], POSR[15:0] } (write-only).
volatile uint32_t PCNTR1
PCNTR1 – { PODR[31:16], PDR[15:0] }.
Configuration descriptor for ra8_gpio_attach_irq.
ra8_icu_irqmd_t sense
Edge / level detection mode.
ra8_icu_fclksel_t filter_div
Digital filter clock divider.
uint8_t priority
NVIC priority 0..15.
ra8_pin_pull_t pull
Input pull setting.
bool filter_en
True -> enable digital filter.
One external-IRQ-pin configuration descriptor.
Definition ra8_icu.h:65
Vtable for a pin driver.