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
35 changes: 33 additions & 2 deletions machine/cortex-m/src/native/gpio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,19 @@ pub fn configure_input(pin: Pin, pull: Pull) -> Result<()> {
ok_or_err(rc, ())
}

pub fn configure_output(pin: Pin, initial: Level) -> Result<()> {
pub fn configure_output(pin: Pin, initial: Level, pull: Pull) -> Result<()> {
let mask = pin_mask(pin)?;
let rc = unsafe { bindings::gpio_configure_output_pp(port_ptr(pin), mask, initial as u8) };
let rc = unsafe {
bindings::gpio_configure_output_pp(port_ptr(pin), mask, initial as u8, pull as u8)
};
ok_or_err(rc, ())
}

pub fn configure_output_od(pin: Pin, initial: Level, pull: Pull) -> Result<()> {
let mask = pin_mask(pin)?;
let rc = unsafe {
bindings::gpio_configure_output_od(port_ptr(pin), mask, initial as u8, pull as u8)
};
ok_or_err(rc, ())
}

Expand All @@ -88,6 +98,13 @@ pub fn write(pin: Pin, level: Level) -> Result<()> {
ok_or_err(rc, ())
}

/// Ungate the port's clock without touching mode/pull — needed
/// before `read_odr` on a still-analog pin.
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 All @@ -99,6 +116,20 @@ pub fn read(pin: Pin) -> Result<Level> {
}
}

/// Read ODR (latched output bit). Use instead of `read` on a pin
/// that may still be in analog mode — IDR is forced to 0 there
/// (RM0432 §8.3.12). Clock must be ungated.
pub fn read_odr(pin: Pin) -> Result<Level> {
let mask = pin_mask(pin)?;
let rc = unsafe { bindings::gpio_read_odr(port_ptr(pin), mask) };
match rc {
0 => Ok(Level::Low),
1 => Ok(Level::High),
other if other < 0 => Err(PosixError::from_errno(-other)),
_ => Err(PosixError::EIO),
}
}

pub fn toggle(pin: Pin) -> Result<()> {
let mask = pin_mask(pin)?;
let rc = unsafe { bindings::gpio_toggle(port_ptr(pin), mask) };
Expand Down
26 changes: 26 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,29 @@ 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,
}

#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LedOutputMode {
PushPull,
OpenDrain,
}
Comment thread
xarantolus marked this conversation as resolved.

#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LedPull {
None,
Up,
Down,
}

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

pub const LED_REGISTRY: &[LedRegistryEntry] = &[];
Expand Down
14 changes: 13 additions & 1 deletion machine/cortex-m/src/stub/gpio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,30 @@ pub fn configure_input(_pin: Pin, _pull: Pull) -> Result<()> {
Err(PosixError::EOPNOTSUPP)
}

pub fn configure_output(_pin: Pin, _initial: Level) -> Result<()> {
pub fn configure_output(_pin: Pin, _initial: Level, _pull: Pull) -> Result<()> {
Err(PosixError::EOPNOTSUPP)
}

pub fn configure_output_od(_pin: Pin, _initial: Level, _pull: Pull) -> Result<()> {
Err(PosixError::EOPNOTSUPP)
}

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)
}

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

pub fn toggle(_pin: Pin) -> Result<()> {
Err(PosixError::EOPNOTSUPP)
}
Expand Down
7 changes: 6 additions & 1 deletion machine/cortex-m/st/stm32l4/interface/export.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,10 +218,15 @@ void can_isr(uint8_t index);
#define GPIO_PULL_UP 1
#define GPIO_PULL_DOWN 2
int gpio_configure_input(void *port, uint16_t pin_mask, uint8_t pull);
int gpio_configure_output_pp(void *port, uint16_t pin_mask, uint8_t initial);
int gpio_configure_output_pp(void *port, uint16_t pin_mask, uint8_t initial,
uint8_t pull);
int gpio_configure_output_od(void *port, uint16_t pin_mask, uint8_t initial,
uint8_t pull);
int gpio_write(void *port, uint16_t pin_mask, uint8_t level);
int gpio_read(void *port, uint16_t pin_mask);
int gpio_read_odr(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
50 changes: 48 additions & 2 deletions machine/cortex-m/st/stm32l4/interface/gpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -139,19 +139,43 @@ int gpio_configure_input(void *port, uint16_t pin_mask, uint8_t pull)
return 0;
}

int gpio_configure_output_pp(void *port, uint16_t pin_mask, uint8_t initial)
int gpio_configure_output_pp(void *port, uint16_t pin_mask, uint8_t initial,
uint8_t pull)
{
GPIO_TypeDef *p = (GPIO_TypeDef *)port;
if (!port_is_known(p) || pin_mask == 0)
return -PosixError_EINVAL;
if (pull > GPIO_PULL_DOWN)
return -PosixError_EINVAL;

gpio_enable_clock(p);
/* Drive the initial level before switching the pin to output mode so the
* line never glitches the inverse polarity. */
HAL_GPIO_WritePin(p, pin_mask, initial ? GPIO_PIN_SET : GPIO_PIN_RESET);
GPIO_InitTypeDef gpio = {0};
gpio.Mode = GPIO_MODE_OUTPUT_PP;
gpio.Pull = GPIO_NOPULL;
gpio.Pull = pull_to_hal(pull);
gpio.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
gpio.Pin = pin_mask;
HAL_GPIO_Init(p, &gpio);
return 0;
}

int gpio_configure_output_od(void *port, uint16_t pin_mask, uint8_t initial,
uint8_t pull)
{
GPIO_TypeDef *p = (GPIO_TypeDef *)port;
if (!port_is_known(p) || pin_mask == 0)
return -PosixError_EINVAL;
if (pull > GPIO_PULL_DOWN)
return -PosixError_EINVAL;

gpio_enable_clock(p);
/* Pre-drive ODR before switching mode so the line never glitches. */
HAL_GPIO_WritePin(p, pin_mask, initial ? GPIO_PIN_SET : GPIO_PIN_RESET);
GPIO_InitTypeDef gpio = {0};
gpio.Mode = GPIO_MODE_OUTPUT_OD;
gpio.Pull = pull_to_hal(pull);
gpio.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
gpio.Pin = pin_mask;
HAL_GPIO_Init(p, &gpio);
Expand All @@ -175,6 +199,19 @@ int gpio_read(void *port, uint16_t pin_mask)
return HAL_GPIO_ReadPin(p, pin_mask) == GPIO_PIN_SET ? 1 : 0;
}

/* Read ODR directly; gpio_read goes via IDR which is forced to 0 in
* analog mode (RM0432 §8.3.12). Single-bit mask only — multi-bit
* would OR several ODR bits into one 0/1. Clock must be ungated. */
int gpio_read_odr(void *port, uint16_t pin_mask)
{
GPIO_TypeDef *p = (GPIO_TypeDef *)port;
if (!port_is_known(p) || pin_mask == 0)
return -PosixError_EINVAL;
if (pin_mask & (uint16_t)(pin_mask - 1))
return -PosixError_EINVAL;
return (p->ODR & pin_mask) ? 1 : 0;
}

int gpio_toggle(void *port, uint16_t pin_mask)
{
GPIO_TypeDef *p = (GPIO_TypeDef *)port;
Expand All @@ -183,3 +220,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;
}
7 changes: 6 additions & 1 deletion machine/cortex-m/st/stm32l4/interface/gpio.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,13 @@ void gpio_init_output_od(GPIO_TypeDef *port, uint16_t pin_mask);
* All return 0 on success or a negative PosixError code on failure. */

int gpio_configure_input(void *port, uint16_t pin_mask, uint8_t pull);
int gpio_configure_output_pp(void *port, uint16_t pin_mask, uint8_t initial);
int gpio_configure_output_pp(void *port, uint16_t pin_mask, uint8_t initial,
uint8_t pull);
int gpio_configure_output_od(void *port, uint16_t pin_mask, uint8_t initial,
uint8_t pull);
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_read_odr(void *port, uint16_t pin_mask);
int gpio_toggle(void *port, uint16_t pin_mask);
int gpio_clock_enable(void *port);
62 changes: 54 additions & 8 deletions src/drivers/led.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use crate::error::Result;
use crate::hal;
use crate::sync::once::OnceCell;

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

struct LedState {
entry: &'static LedRegistryEntry,
Expand Down Expand Up @@ -87,6 +87,36 @@ fn level_for(entry: &LedRegistryEntry, on: bool) -> Level {
}
}

/// `default-state = "keep"`. Reads ODR (set by whatever ran before us,
/// e.g. a bootloader) instead of IDR — IDR is forced to 0 on an
/// analog-mode pin (RM0432 §8.3.12). Only ODR=High is preserved; Low
/// falls back to logical-Off because ODR's reset value is also 0
/// (RM0432 §8.4.6), so Low is ambiguous and would illuminate
/// active-low LEDs on cold boot.
fn keep_initial_level(entry: &LedRegistryEntry) -> Level {
let pin = pin_of(entry);
if let Err(e) = hal::gpio::enable_port_clock(pin) {
kprintln!(
" LED {}: keep: enable_port_clock failed ({:?}); falling back to Off",
entry.label,
e,
);
return level_for(entry, false);
}
match hal::gpio::read_odr(pin) {
Ok(Level::High) => Level::High,
Ok(Level::Low) => level_for(entry, false),
Err(e) => {
kprintln!(
" LED {}: keep: read_odr failed ({:?}); falling back to Off",
entry.label,
e,
);
level_for(entry, false)
}
}
}

pub fn init() {
let entries = hal::device_tree::LED_REGISTRY;
kprintln!("Found {} gpio-led entries", entries.len());
Expand All @@ -98,17 +128,33 @@ 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 => keep_initial_level(entry),
};
let pull = match entry.pull {
LedPull::None => Pull::None,
LedPull::Up => Pull::Up,
LedPull::Down => Pull::Down,
};
let res = match entry.output_mode {
LedOutputMode::OpenDrain => {
hal::gpio::configure_output_od(pin_of(entry), initial, pull)
}
LedOutputMode::PushPull => hal::gpio::configure_output(pin_of(entry), initial, pull),
};
match res {
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,
entry.output_mode,
entry.pull,
);
}
Err(e) => kprintln!(" LED {}: configure_output failed: {:?}", entry.label, e),
Expand Down
Loading
Loading