pinctrl: basic gpio led support
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
use alloc::sync::Arc;
|
||||
use alloc::{boxed::Box, sync::Arc};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::device::Device;
|
||||
@@ -29,6 +29,25 @@ pub enum SinglePinDirection {
|
||||
Input,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum GpioInterruptMode {
|
||||
RisingEdge,
|
||||
FallingEdge,
|
||||
BothEdges,
|
||||
HighLevel,
|
||||
LowLevel,
|
||||
}
|
||||
|
||||
pub struct GpioInterruptEvent {
|
||||
pub mode: GpioInterruptMode,
|
||||
pub event: u64,
|
||||
}
|
||||
|
||||
pub struct GpioInterrupt {
|
||||
pub mode: GpioInterruptMode,
|
||||
pub handler: Box<dyn Fn() + Send>,
|
||||
}
|
||||
|
||||
pub struct GpioPinConfig {
|
||||
pub input: bool,
|
||||
pub output: bool,
|
||||
@@ -48,13 +67,21 @@ pub trait GpioController: Device {
|
||||
fn write_gpio(&self, pin: &PinHandle, value: bool) -> Result<(), Error>;
|
||||
fn read_gpio(&self, pin: &PinHandle) -> Result<bool, Error>;
|
||||
|
||||
fn setup_gpio(&self, pin: &PinHandle, event: Option<u64>) -> Result<(), Error>;
|
||||
fn setup_gpio(&self, pin: &PinHandle, event: Option<GpioInterruptEvent>) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
pub trait GpioOutput: Sync + Send {
|
||||
fn write(&self, value: bool) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
impl PinHandle {
|
||||
pub fn configure(&self) -> Result<(), Error> {
|
||||
self.parent.setup_gpio(self, None)
|
||||
}
|
||||
|
||||
pub fn write(&self, value: bool) -> Result<(), Error> {
|
||||
self.parent.write_gpio(self, value)
|
||||
}
|
||||
}
|
||||
|
||||
impl GpioPinConfig {
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
pub trait BitField: Sized {
|
||||
fn modify_bit(&self, bit: usize, value: bool) -> Self;
|
||||
fn set_bit(&mut self, bit: usize, value: bool);
|
||||
fn get_bit(&self, bit: usize) -> bool;
|
||||
}
|
||||
|
||||
macro_rules! impl_bit_field {
|
||||
($($ty:ty),+ $(,)?) => {
|
||||
$(
|
||||
impl BitField for $ty {
|
||||
#[inline]
|
||||
fn modify_bit(&self, bit: usize, value: bool) -> Self {
|
||||
if value {
|
||||
*self | (1 << bit)
|
||||
} else {
|
||||
*self & !(1 << bit)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_bit(&mut self, bit: usize, value: bool) {
|
||||
*self = self.modify_bit(bit, value);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_bit(&self, bit: usize) -> bool {
|
||||
*self & (1 << bit) != 0
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
impl_bit_field!(u8, u16, u32, u64, usize);
|
||||
impl_bit_field!(i8, i16, i32, i64, isize);
|
||||
@@ -18,6 +18,7 @@ use core::{
|
||||
panic,
|
||||
};
|
||||
|
||||
pub mod bit;
|
||||
pub mod event;
|
||||
pub mod ext;
|
||||
pub mod hash_table;
|
||||
|
||||
@@ -1,24 +1,37 @@
|
||||
use core::sync::atomic::{AtomicU64, Ordering};
|
||||
use core::{
|
||||
sync::atomic::{AtomicU64, Ordering},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use alloc::{boxed::Box, collections::BTreeMap};
|
||||
use alloc::{boxed::Box, collections::BTreeMap, sync::Arc, vec::Vec};
|
||||
|
||||
use device_api::gpio::PinHandle;
|
||||
use device_api::gpio::{GpioInterrupt, GpioInterruptEvent, GpioOutput, PinHandle};
|
||||
use libk_util::{ring::LossyRingQueue, sync::spin_rwlock::IrqSafeRwLock};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::task::runtime;
|
||||
|
||||
static GPIO_EVENT_QUEUE: LossyRingQueue<u64> = LossyRingQueue::with_capacity(64);
|
||||
static GPIO_EVENT_MAP: IrqSafeRwLock<BTreeMap<u64, Box<dyn Fn() + Send>>> =
|
||||
IrqSafeRwLock::new(BTreeMap::new());
|
||||
static GPIO_EVENT_ID: AtomicU64 = AtomicU64::new(1);
|
||||
|
||||
static GPIO_HEARTBEATS: IrqSafeRwLock<Vec<Arc<dyn GpioOutput>>> = IrqSafeRwLock::new(Vec::new());
|
||||
|
||||
pub trait GpioEvent {
|
||||
fn configure_with_interrupt(&self, handler: Box<dyn Fn() + Send>) -> Result<(), Error>;
|
||||
fn configure_with_interrupt(&self, int: GpioInterrupt) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
impl GpioEvent for PinHandle {
|
||||
fn configure_with_interrupt(&self, handler: Box<dyn Fn() + Send>) -> Result<(), Error> {
|
||||
let ev = bind_gpio_event(handler);
|
||||
self.parent.setup_gpio(self, Some(ev))
|
||||
fn configure_with_interrupt(&self, int: GpioInterrupt) -> Result<(), Error> {
|
||||
let event = bind_gpio_event(int.handler);
|
||||
self.parent.setup_gpio(
|
||||
self,
|
||||
Some(GpioInterruptEvent {
|
||||
mode: int.mode,
|
||||
event,
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +46,30 @@ pub async fn gpio_event_handler_task() {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn heartbeat_handler_task() {
|
||||
const LEVELS: &[(Duration, bool)] = &[
|
||||
(Duration::from_millis(50), true),
|
||||
(Duration::from_millis(200), false),
|
||||
(Duration::from_millis(50), true),
|
||||
(Duration::from_millis(700), false),
|
||||
];
|
||||
let mut index = 0;
|
||||
loop {
|
||||
let (timeout, level) = LEVELS[index];
|
||||
|
||||
for pin in GPIO_HEARTBEATS.read().iter() {
|
||||
pin.write(level).ok();
|
||||
}
|
||||
runtime::sleep(timeout).await;
|
||||
|
||||
index = (index + 1) % LEVELS.len();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_heartbeat_gpio(gpio: Arc<dyn GpioOutput>) {
|
||||
GPIO_HEARTBEATS.write().push(gpio);
|
||||
}
|
||||
|
||||
pub fn bind_gpio_event(handler: Box<dyn Fn() + Send>) -> u64 {
|
||||
let id = GPIO_EVENT_ID.fetch_add(1, Ordering::Acquire);
|
||||
GPIO_EVENT_MAP.write().insert(id, handler);
|
||||
|
||||
+51
-16
@@ -4,13 +4,19 @@ use abi::error::Error;
|
||||
use alloc::{boxed::Box, string::String, sync::Arc, vec::Vec};
|
||||
use device_api::{
|
||||
device::{Device, DeviceInitContext},
|
||||
gpio::{GpioPinLevel, PinHandle},
|
||||
gpio::{GpioInterrupt, GpioInterruptMode, GpioOutput, GpioPinLevel, PinHandle},
|
||||
};
|
||||
use device_tree::driver::{device_tree_driver, DeviceTreeGpioPins, Node, ProbeContext};
|
||||
use libk::event::GpioEvent;
|
||||
use libk::event::{self, GpioEvent};
|
||||
|
||||
enum LedFunction {
|
||||
Heartbeat,
|
||||
Other,
|
||||
}
|
||||
|
||||
struct LedPin {
|
||||
name: String,
|
||||
function: LedFunction,
|
||||
handle: PinHandle,
|
||||
}
|
||||
|
||||
@@ -20,14 +26,14 @@ struct KeyPin {
|
||||
}
|
||||
|
||||
trait Pin: Sized + Send + Sync + 'static {
|
||||
fn from_device_tree(node: &Arc<Node>) -> Option<Self>;
|
||||
fn configure(&self) -> Result<(), Error>;
|
||||
fn from_device_tree(node: &Arc<Node>) -> Option<Arc<Self>>;
|
||||
fn configure(self: &Arc<Self>) -> Result<(), Error>;
|
||||
fn name(&self) -> &str;
|
||||
}
|
||||
|
||||
struct PinGroup<P: Pin> {
|
||||
name: String,
|
||||
pins: Vec<P>,
|
||||
pins: Vec<Arc<P>>,
|
||||
}
|
||||
|
||||
impl<P: Pin> PinGroup<P> {
|
||||
@@ -63,7 +69,7 @@ impl<P: Pin> Device for PinGroup<P> {
|
||||
}
|
||||
|
||||
impl Pin for LedPin {
|
||||
fn from_device_tree(node: &Arc<Node>) -> Option<Self> {
|
||||
fn from_device_tree(node: &Arc<Node>) -> Option<Arc<Self>> {
|
||||
let handle = node.gpio(
|
||||
&DeviceTreeGpioPins {
|
||||
input: false,
|
||||
@@ -72,12 +78,32 @@ impl Pin for LedPin {
|
||||
},
|
||||
0,
|
||||
)?;
|
||||
|
||||
let function = match node.prop_str("function") {
|
||||
Some("heartbeat") => LedFunction::Heartbeat,
|
||||
_ => LedFunction::Other,
|
||||
};
|
||||
|
||||
let name = node.name().unwrap_or("gpio-led").into();
|
||||
Some(Self { name, handle })
|
||||
Some(Arc::new(Self {
|
||||
name,
|
||||
function,
|
||||
handle,
|
||||
}))
|
||||
}
|
||||
|
||||
fn configure(&self) -> Result<(), Error> {
|
||||
self.handle.configure()
|
||||
fn configure(self: &Arc<Self>) -> Result<(), Error> {
|
||||
self.handle.configure()?;
|
||||
|
||||
match self.function {
|
||||
LedFunction::Heartbeat => {
|
||||
log::info!("Register led {:?} as heartbeat indicator", self.name);
|
||||
event::register_heartbeat_gpio(self.clone())
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
@@ -85,8 +111,14 @@ impl Pin for LedPin {
|
||||
}
|
||||
}
|
||||
|
||||
impl GpioOutput for LedPin {
|
||||
fn write(&self, value: bool) -> Result<(), Error> {
|
||||
self.handle.write(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Pin for KeyPin {
|
||||
fn from_device_tree(node: &Arc<Node>) -> Option<Self> {
|
||||
fn from_device_tree(node: &Arc<Node>) -> Option<Arc<Self>> {
|
||||
let handle = node.gpio(
|
||||
&DeviceTreeGpioPins {
|
||||
input: true,
|
||||
@@ -96,14 +128,17 @@ impl Pin for KeyPin {
|
||||
0,
|
||||
)?;
|
||||
let name = node.name().unwrap_or("gpio-key").into();
|
||||
Some(Self { name, handle })
|
||||
Some(Arc::new(Self { name, handle }))
|
||||
}
|
||||
|
||||
fn configure(&self) -> Result<(), Error> {
|
||||
let name = self.name.clone();
|
||||
self.handle.configure_with_interrupt(Box::new(move || {
|
||||
log::info!("GPIO key {name:?} triggered");
|
||||
}))
|
||||
fn configure(self: &Arc<Self>) -> Result<(), Error> {
|
||||
let key = self.clone();
|
||||
self.handle.configure_with_interrupt(GpioInterrupt {
|
||||
mode: GpioInterruptMode::FallingEdge,
|
||||
handler: Box::new(move || {
|
||||
log::info!("GPIO key triggered: {:?}", key.name);
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
|
||||
@@ -2,7 +2,7 @@ use abi::error::Error;
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
device::{Device, DeviceInitContext},
|
||||
gpio::{GpioController, PinHandle},
|
||||
gpio::{GpioController, GpioInterruptEvent, PinHandle},
|
||||
interrupt::{FullIrq, InterruptHandler, IrqVector},
|
||||
};
|
||||
use device_tree::{
|
||||
@@ -164,7 +164,7 @@ impl InterruptHandler for Bcm2711Gpio {
|
||||
}
|
||||
|
||||
impl GpioController for Bcm2711Gpio {
|
||||
fn setup_gpio(&self, pin: &PinHandle, _event: Option<u64>) -> Result<(), Error> {
|
||||
fn setup_gpio(&self, pin: &PinHandle, _event: Option<GpioInterruptEvent>) -> Result<(), Error> {
|
||||
log::warn!(
|
||||
"TOOD: bcm2711 gpio pin #{} setup: input={}, output={}",
|
||||
pin.index,
|
||||
@@ -175,11 +175,11 @@ impl GpioController for Bcm2711Gpio {
|
||||
}
|
||||
|
||||
fn read_gpio(&self, _pin: &PinHandle) -> Result<bool, Error> {
|
||||
todo!()
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
fn write_gpio(&self, _pin: &PinHandle, _value: bool) -> Result<(), Error> {
|
||||
todo!()
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,10 @@ use alloc::{sync::Arc, vec::Vec};
|
||||
use device_api::{
|
||||
clock::{ClockHandle, ResetHandle},
|
||||
device::{Device, DeviceInitContext},
|
||||
gpio::{GpioController, PinHandle},
|
||||
gpio::{
|
||||
GpioController, GpioInterruptEvent, GpioPinLevel, OutputPinBias, PinHandle,
|
||||
SinglePinDirection,
|
||||
},
|
||||
};
|
||||
use device_tree::{
|
||||
driver::{
|
||||
@@ -14,7 +17,7 @@ use device_tree::{
|
||||
DeviceTreePropertyRead, TProp,
|
||||
};
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIoMut};
|
||||
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
|
||||
use libk_util::{bit::BitField, sync::IrqSafeSpinlock, OneTimeInit};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct PinMuxConfig {
|
||||
@@ -55,6 +58,17 @@ trait GpioRegs: Sized + Send + 'static {
|
||||
fn reg_mut(&mut self, index: usize) -> *mut u32;
|
||||
fn pin_functions(&self, pin: usize) -> Option<(usize, usize, u32)>;
|
||||
|
||||
fn write_output_pin(&mut self, pin: usize, value: bool) {
|
||||
let reg = pin / 4;
|
||||
let shift = (pin % 4) * 8;
|
||||
let reg_dout = self.reg_mut(Self::DOUT_OFFSET + reg);
|
||||
unsafe {
|
||||
let mut val = reg_dout.read_volatile();
|
||||
val.set_bit(shift, value);
|
||||
reg_dout.write_volatile(val);
|
||||
}
|
||||
}
|
||||
|
||||
fn configure_pin(&mut self, pin: usize, doen: u32, dout: u32, din: u32) {
|
||||
let reg = pin / 4;
|
||||
let shift = (pin % 4) * 8;
|
||||
@@ -240,6 +254,21 @@ impl<R: GpioRegs> Gpio<R> {
|
||||
}
|
||||
}
|
||||
|
||||
fn configure_pin_output(
|
||||
&self,
|
||||
pin: usize,
|
||||
_active_level: GpioPinLevel,
|
||||
initial_level: GpioPinLevel,
|
||||
_output_bias: Option<OutputPinBias>,
|
||||
) -> Result<(), Error> {
|
||||
// TODO configure pull up/pull down?
|
||||
let mut regs = self.regs.lock();
|
||||
let dout_val = initial_level == GpioPinLevel::High;
|
||||
regs.configure_pin_function(pin, 0);
|
||||
regs.configure_pin(pin, 0, dout_val as u32, 0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ensure_init(&self) -> Result<(), Error> {
|
||||
let res = self.init.or_init_with(|| {
|
||||
for clock in self.clocks.iter() {
|
||||
@@ -269,22 +298,35 @@ impl<R: GpioRegs> Device for Gpio<R> {
|
||||
}
|
||||
|
||||
impl<R: GpioRegs> GpioController for Gpio<R> {
|
||||
fn setup_gpio(&self, pin: &PinHandle, _event: Option<u64>) -> Result<(), Error> {
|
||||
fn setup_gpio(&self, pin: &PinHandle, _event: Option<GpioInterruptEvent>) -> Result<(), Error> {
|
||||
log::warn!(
|
||||
"TODO: setup gpio #{} input={}, output={}",
|
||||
pin.index,
|
||||
pin.config.input,
|
||||
pin.config.output
|
||||
);
|
||||
Err(Error::NotImplemented)
|
||||
// TODO bidi pins?
|
||||
match pin.config.force_single_direction() {
|
||||
Some(SinglePinDirection::Input) => todo!(),
|
||||
Some(SinglePinDirection::Output) => self.configure_pin_output(
|
||||
pin.index as usize,
|
||||
pin.config.active_level,
|
||||
pin.config.initial_level,
|
||||
pin.config.output_bias,
|
||||
),
|
||||
None => {
|
||||
todo!("{}: support for bidi gpio", R::NAME);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_gpio(&self, _pin: &PinHandle) -> Result<bool, Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn write_gpio(&self, _pin: &PinHandle, _value: bool) -> Result<(), Error> {
|
||||
Err(Error::NotImplemented)
|
||||
fn write_gpio(&self, pin: &PinHandle, value: bool) -> Result<(), Error> {
|
||||
self.regs.lock().write_output_pin(pin.index as _, value);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,10 @@ use alloc::{sync::Arc, vec::Vec};
|
||||
use device_api::{
|
||||
clock::{ClockHandle, ResetHandle},
|
||||
device::{Device, DeviceInitContext},
|
||||
gpio::{GpioController, GpioPinLevel, PinHandle, SinglePinDirection},
|
||||
gpio::{
|
||||
GpioController, GpioInterruptEvent, GpioInterruptMode, GpioPinLevel, PinHandle,
|
||||
SinglePinDirection,
|
||||
},
|
||||
interrupt::{FullIrq, InterruptHandler, IrqVector},
|
||||
};
|
||||
use device_tree::{
|
||||
@@ -17,7 +20,7 @@ use device_tree::{
|
||||
};
|
||||
use libk::{device::external_interrupt_controller, event::signal_gpio_event};
|
||||
use libk_mm::device::DeviceMemoryIo;
|
||||
use libk_util::sync::IrqSafeSpinlock;
|
||||
use libk_util::{bit::BitField, sync::IrqSafeSpinlock};
|
||||
use tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
register_structs,
|
||||
@@ -106,7 +109,7 @@ impl GpioController for Pl061 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn setup_gpio(&self, pin: &PinHandle, event: Option<u64>) -> Result<(), Error> {
|
||||
fn setup_gpio(&self, pin: &PinHandle, event: Option<GpioInterruptEvent>) -> Result<(), Error> {
|
||||
let regs = self.regs.lock();
|
||||
|
||||
let direction = pin
|
||||
@@ -146,11 +149,20 @@ impl GpioController for Pl061 {
|
||||
regs.GPIODIR.set(regs.GPIODIR.get() & !(1 << pin.index));
|
||||
|
||||
if let Some(event) = event {
|
||||
self.gpio_events[pin.index as usize].store(event, Ordering::Release);
|
||||
let ibe = event.mode == GpioInterruptMode::BothEdges;
|
||||
let is = event.mode == GpioInterruptMode::HighLevel
|
||||
|| event.mode == GpioInterruptMode::LowLevel;
|
||||
let iev = event.mode == GpioInterruptMode::HighLevel
|
||||
|| event.mode == GpioInterruptMode::RisingEdge;
|
||||
|
||||
regs.GPIOIS.set(regs.GPIOIS.get() & !(1 << pin.index));
|
||||
regs.GPIOIBE.set(regs.GPIOIBE.get() & !(1 << pin.index));
|
||||
regs.GPIOIEV.set(regs.GPIOIEV.get() | (1 << pin.index));
|
||||
self.gpio_events[pin.index as usize].store(event.event, Ordering::Release);
|
||||
|
||||
regs.GPIOIS
|
||||
.set(regs.GPIOIS.get().modify_bit(pin.index as usize, is));
|
||||
regs.GPIOIBE
|
||||
.set(regs.GPIOIBE.get().modify_bit(pin.index as usize, ibe));
|
||||
regs.GPIOIEV
|
||||
.set(regs.GPIOIEV.get().modify_bit(pin.index as usize, iev));
|
||||
regs.GPIOIE.set(regs.GPIOIE.get() | (1 << pin.index));
|
||||
regs.GPIOIC.set(1 << pin.index);
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ pub fn kinit() -> Result<(), Error> {
|
||||
);
|
||||
|
||||
runtime::spawn(event::gpio_event_handler_task()).ok();
|
||||
runtime::spawn(event::heartbeat_handler_task()).ok();
|
||||
}
|
||||
// Initialize PCI devices
|
||||
if let Err(error) = PciBusManager::setup_bus_devices(false) {
|
||||
|
||||
Reference in New Issue
Block a user