Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions machine/cortex-m/src/native/gpio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ pub fn write(pin: Pin, level: Level) -> Result<()> {
ok_or_err(rc, ())
}

pub fn enable_port_clock(pin: Pin) -> Result<()> {
let rc = unsafe { bindings::gpio_clock_enable(port_ptr(pin)) };
ok_or_err(rc, ())
}

pub fn read(pin: Pin) -> Result<Level> {
let mask = pin_mask(pin)?;
let rc = unsafe { bindings::gpio_read(port_ptr(pin), mask) };
Expand Down
9 changes: 9 additions & 0 deletions machine/cortex-m/src/stub/device_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ pub fn can_by_compatible(_compatible: &str, _ord: usize) -> Option<&'static CanR
None
}

#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LedDefaultState {
Off,
On,
Keep,
}

#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct LedRegistryEntry {
Expand All @@ -94,6 +102,7 @@ pub struct LedRegistryEntry {
pub line: u8,
pub active_low: u8,
pub label: &'static str,
pub default_state: LedDefaultState,
}

pub const LED_REGISTRY: &[LedRegistryEntry] = &[];
Expand Down
4 changes: 4 additions & 0 deletions machine/cortex-m/src/stub/gpio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ pub fn write(_pin: Pin, _level: Level) -> Result<()> {
Err(PosixError::EOPNOTSUPP)
}

pub fn enable_port_clock(_pin: Pin) -> Result<()> {
Err(PosixError::EOPNOTSUPP)
}

pub fn read(_pin: Pin) -> Result<Level> {
Err(PosixError::EOPNOTSUPP)
}
Expand Down
1 change: 1 addition & 0 deletions machine/cortex-m/st/stm32l4/interface/export.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ int gpio_configure_output_pp(void *port, uint16_t pin_mask, uint8_t initial);
int gpio_write(void *port, uint16_t pin_mask, uint8_t level);
int gpio_read(void *port, uint16_t pin_mask);
int gpio_toggle(void *port, uint16_t pin_mask);
int gpio_clock_enable(void *port);

// exti.c
// edge_mask bitfield: 0x1 = rising, 0x2 = falling (see exti.h).
Expand Down
9 changes: 9 additions & 0 deletions machine/cortex-m/st/stm32l4/interface/gpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -183,3 +183,12 @@ int gpio_toggle(void *port, uint16_t pin_mask)
HAL_GPIO_TogglePin(p, pin_mask);
return 0;
}

int gpio_clock_enable(void *port)
{
GPIO_TypeDef *p = (GPIO_TypeDef *)port;
if (!port_is_known(p))
return -PosixError_EINVAL;
gpio_enable_clock(p);
return 0;
}
1 change: 1 addition & 0 deletions machine/cortex-m/st/stm32l4/interface/gpio.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ int gpio_write(void *port, uint16_t pin_mask, uint8_t level);
/* gpio_read returns 0 or 1 (level) on success, or negative PosixError. */
int gpio_read(void *port, uint16_t pin_mask);
int gpio_toggle(void *port, uint16_t pin_mask);
int gpio_clock_enable(void *port);
27 changes: 18 additions & 9 deletions src/drivers/led.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::error::Result;
use crate::hal;
use crate::sync::once::OnceCell;

use hal::device_tree::LedRegistryEntry;
use hal::device_tree::{LedDefaultState, LedRegistryEntry};
use hal::gpio::{Level, Pin};

struct LedState {
Expand Down Expand Up @@ -71,14 +71,14 @@ impl Led {
}
}

fn pin_of(entry: &LedRegistryEntry) -> Pin {
const fn pin_of(entry: &LedRegistryEntry) -> Pin {
Pin {
port: entry.port,
line: entry.line,
}
}

fn level_for(entry: &LedRegistryEntry, on: bool) -> Level {
const fn level_for(entry: &LedRegistryEntry, on: bool) -> Level {
let active_low = entry.active_low != 0;
if on ^ active_low {
Level::High
Expand All @@ -98,17 +98,26 @@ pub fn init() {
};
let state_ref: &'static LedState = SLOTS[i].set_or_get(state);

// Drive the off level before switching to output so the line
// never glitches the active polarity on boot.
let off = level_for(entry, false);
match hal::gpio::configure_output(pin_of(entry), off) {
let initial = match entry.default_state {
LedDefaultState::Off => level_for(entry, false),
LedDefaultState::On => level_for(entry, true),
LedDefaultState::Keep => {
// enable clock, as we read before configure_output, which internally turns on clock
let _ = hal::gpio::enable_port_clock(pin_of(entry)).map_err(|e| {
kprintln!(" LED {}: enable_port_clock failed: {:?}", entry.label, e)
});
hal::gpio::read(pin_of(entry)).unwrap_or(level_for(entry, false))
}
};
match hal::gpio::configure_output(pin_of(entry), initial) {
Ok(()) => {
state_ref.initialized.store(true, Ordering::Release);
kprintln!(
" Initialized LED {} on port 0x{:x} line {}",
" Initialized LED {} on port 0x{:x} line {} ({:?})",
entry.label,
entry.port,
entry.line
entry.line,
entry.default_state,
);
}
Err(e) => kprintln!(" LED {}: configure_output failed: {:?}", entry.label, e),
Expand Down
57 changes: 54 additions & 3 deletions xtasks/crates/dtgen/src/codegen/led.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,54 @@
//! gpio-leds registry codegen. One entry per child of a `compatible =
//! "gpio-leds"` node; the (port, line, active_low, label) extraction is
//! shared with gpio-keys via [`super::collect_gpio_children`].
//! gpio-leds registry codegen. Shared (port, line, active_low, label)
//! extraction via [`super::collect_gpio_children`]; `default-state`
//! (Linux/Zephyr binding) is parsed here into a `LedDefaultState` enum.

use super::*;

#[derive(Clone, Copy)]
enum DefaultState {
Off,
On,
Keep,
}

impl DefaultState {
fn from_node(node: &crate::ir::Node) -> Self {
match node.extra.get("default-state") {
Some(PropValue::Str(s)) => match s.as_str() {
"on" => DefaultState::On,
"keep" => DefaultState::Keep,
"off" => DefaultState::Off,
other => panic!(
"gpio-leds child {}: unknown `default-state` value {:?} \
(expected \"on\", \"off\", or \"keep\")",
node.name, other
),
},
None => DefaultState::Off,
_ => panic!(
"gpio-leds child {}: `default-state` must be a string",
node.name
),
}
}

fn tokens(self) -> TokenStream {
match self {
DefaultState::Off => quote! { LedDefaultState::Off },
DefaultState::On => quote! { LedDefaultState::On },
DefaultState::Keep => quote! { LedDefaultState::Keep },
}
}
}

#[derive(Clone)]
struct Led {
node: usize,
port: usize,
line: u8,
active_low: u8,
label: String,
default_state: DefaultState,
}

fn collect_leds(dt: &DeviceTree) -> Vec<Led> {
Expand All @@ -22,6 +60,7 @@ fn collect_leds(dt: &DeviceTree) -> Vec<Led> {
line: c.line,
active_low: c.active_low,
label: c.label,
default_state: DefaultState::from_node(c.child),
})
.collect();

Expand Down Expand Up @@ -53,18 +92,29 @@ pub fn emit_registry(dt: &DeviceTree) -> TokenStream {
let line = l.line;
let active_low = l.active_low;
let label = l.label.as_str();
let default_state = l.default_state.tokens();
quote! {
LedRegistryEntry {
node: #node,
port: #port,
line: #line,
active_low: #active_low,
label: #label,
default_state: #default_state,
},
}
});

quote! {
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LedDefaultState {
Off,
On,
/// Preserve current ODR (warm restart); cold boot reads analog → 0.
Keep,
}

#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct LedRegistryEntry {
Expand All @@ -73,6 +123,7 @@ pub fn emit_registry(dt: &DeviceTree) -> TokenStream {
pub line: u8,
pub active_low: u8,
pub label: &'static str,
pub default_state: LedDefaultState,
}

pub const LED_REGISTRY: &[LedRegistryEntry] = &[
Expand Down
Loading