Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
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
23 changes: 14 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,22 @@ 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) {
// Pre-MODER level from DT `default-state`; `Keep` is only
// meaningful on warm restart (cold reset reads 0 from analog).
let initial = match entry.default_state {
LedDefaultState::Off => level_for(entry, false),
LedDefaultState::On => level_for(entry, true),
LedDefaultState::Keep => hal::gpio::read(pin_of(entry)).unwrap_or(Level::Low),
};
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