pinctrl: basic support for gpio
This commit is contained in:
+1
-1
@@ -45,7 +45,7 @@ $ booti ${loadaddr} ${initrd_addr_r}:<SIZE-PRINTED-WHEN-LOADING-INITRD> ${fdt_ad
|
||||
env set ipaddr 13.0.0.2; env set fdt_addr_r 0x11000000; env set initrd_addr_r 0x04000000; tftpboot ${initrd_addr_r} 13.0.0.1:initrd.tar; tftpboot ${loadaddr} 13.0.0.1:kernel.bin; load mmc 0:1 ${fdt_addr_r} bcm2711-rpi-4-b.dtb; fdt addr ${fdt_addr_r}; fdt resize; fdt memory 0x0 0x3C000000; booti ${loadaddr} ${initrd_addr_r}:67108864 ${fdt_addr_r}
|
||||
|
||||
dhcp;
|
||||
env set fdt_addr_r 0x04000000; env set initrd_addr_r 0x20000000; tftpboot ${initrd_addr_r} 192.168.88.10:initrd.img; tftpboot ${loadaddr} 192.168.88.10:yggdrasil-kernel.bin; load mmc 1:1 ${fdt_addr_r} bcm2711-rpi-4-b.dtb; fdt addr ${fdt_addr_r}; fdt resize; fdt memory 0x0 0x3C000000; booti ${loadaddr} ${initrd_addr_r}:0x4000000 ${fdt_addr_r}
|
||||
env set initrd_addr_r 0x20000000; tftpboot ${initrd_addr_r} 192.168.88.10:initrd.img; tftpboot ${loadaddr} 192.168.88.10:yggdrasil-kernel.bin; load mmc 1:1 ${fdt_addr_r} bcm2711-rpi-4-b.dtb; fdt addr ${fdt_addr_r}; fdt resize; fdt memory 0x0 0x3C000000; booti ${loadaddr} ${initrd_addr_r}:0x4000000 ${fdt_addr_r}
|
||||
|
||||
|
||||
Missing drivers:
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
use alloc::sync::Arc;
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::device::Device;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum InputPinBias {
|
||||
PullUp,
|
||||
PullDown,
|
||||
Floating,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum OutputPinBias {
|
||||
PushPull,
|
||||
OpenDrain,
|
||||
HighZ,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum GpioPinLevel {
|
||||
High,
|
||||
Low,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum SinglePinDirection {
|
||||
Output,
|
||||
Input,
|
||||
}
|
||||
|
||||
pub struct GpioPinConfig {
|
||||
pub input: bool,
|
||||
pub output: bool,
|
||||
pub input_bias: Option<InputPinBias>,
|
||||
pub output_bias: Option<OutputPinBias>,
|
||||
pub active_level: GpioPinLevel,
|
||||
pub initial_level: GpioPinLevel,
|
||||
}
|
||||
|
||||
pub struct PinHandle {
|
||||
pub index: u32,
|
||||
pub config: GpioPinConfig,
|
||||
pub parent: Arc<dyn GpioController>,
|
||||
}
|
||||
|
||||
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>;
|
||||
}
|
||||
|
||||
impl PinHandle {
|
||||
pub fn configure(&self) -> Result<(), Error> {
|
||||
self.parent.setup_gpio(self, None)
|
||||
}
|
||||
}
|
||||
|
||||
impl GpioPinConfig {
|
||||
pub fn force_single_direction(&self) -> Option<SinglePinDirection> {
|
||||
match (self.input, self.output) {
|
||||
(true, false) => Some(SinglePinDirection::Input),
|
||||
(false, true) => Some(SinglePinDirection::Output),
|
||||
(false, false) => None,
|
||||
(true, true) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ extern crate alloc;
|
||||
pub mod bus;
|
||||
pub mod clock;
|
||||
pub mod device;
|
||||
pub mod gpio;
|
||||
pub mod interrupt;
|
||||
pub mod serial;
|
||||
pub mod timer;
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
clock::{ClockHandle, ResetHandle},
|
||||
gpio::PinHandle,
|
||||
interrupt::FullIrq,
|
||||
};
|
||||
use fdt_rs::spec::Phandle;
|
||||
|
||||
use crate::{DeviceTreePropertyRead, TProp};
|
||||
use crate::{driver::DeviceTreeGpioPins, DeviceTreePropertyRead, TProp};
|
||||
|
||||
use super::{lookup_phandle, Node};
|
||||
|
||||
@@ -20,6 +21,12 @@ pub(crate) struct ResetIter<'dt> {
|
||||
pub(crate) offset: usize,
|
||||
}
|
||||
|
||||
pub(crate) struct GpioIter<'o, 'dt> {
|
||||
pub(crate) options: &'o DeviceTreeGpioPins,
|
||||
pub(crate) gpios: TProp<'dt>,
|
||||
pub(crate) offset: usize,
|
||||
}
|
||||
|
||||
impl Iterator for ClockIter<'_> {
|
||||
type Item = ClockHandle;
|
||||
|
||||
@@ -52,6 +59,22 @@ impl Iterator for ResetIter<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for GpioIter<'_, '_> {
|
||||
type Item = PinHandle;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.offset >= self.gpios.len() {
|
||||
return None;
|
||||
}
|
||||
let phandle = self.gpios.read_cell(self.offset, 1)? as Phandle;
|
||||
let gpioc = lookup_phandle(phandle, true)?;
|
||||
let gpioc = gpioc.as_pin_controller()?;
|
||||
let (pin, len) = gpioc.map_gpio(&self.gpios, self.offset + 1, self.options)?;
|
||||
self.offset += len + 1;
|
||||
Some(pin)
|
||||
}
|
||||
}
|
||||
|
||||
// Interrupt controller handling
|
||||
|
||||
/// Reads interrupt information, as interpreted by `interrupt_controller`, from `property` at a
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Device tree-based driver definitions
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use device_api::gpio::GpioPinLevel;
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
mod controller;
|
||||
@@ -22,6 +23,16 @@ pub use traits::{
|
||||
};
|
||||
pub use tree::{find_node, unflatten_device_tree, walk_device_tree, Node};
|
||||
|
||||
/// Contextual information about a GPIO pin being configured
|
||||
pub struct DeviceTreeGpioPins {
|
||||
/// Whether to enable input on the pin
|
||||
pub input: bool,
|
||||
/// Whether to enable output on the pin
|
||||
pub output: bool,
|
||||
/// Initial level to configure the GPIO with
|
||||
pub initial_level: GpioPinLevel,
|
||||
}
|
||||
|
||||
/// Specifies initialization sequence requirement for a driver
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum InitSequence {
|
||||
|
||||
@@ -6,11 +6,12 @@ use device_api::{
|
||||
bus::Bus,
|
||||
clock::{ClockHandle, ResetHandle},
|
||||
device::Device,
|
||||
gpio::PinHandle,
|
||||
interrupt::{ExternalInterruptController, FullIrq},
|
||||
};
|
||||
use libk::error::Error;
|
||||
|
||||
use crate::TProp;
|
||||
use crate::{driver::DeviceTreeGpioPins, TProp};
|
||||
|
||||
use super::{InitSequence, Node};
|
||||
|
||||
@@ -56,6 +57,14 @@ pub struct ProbeContext {
|
||||
pub trait DeviceTreePinController {
|
||||
/// Configure a pin group
|
||||
fn configure_pin_group(self: Arc<Self>, pins: &Arc<Node>) -> Result<(), Error>;
|
||||
/// Reads GPIO pin information from `property` at given `offset` and maps it to a
|
||||
/// [PinHandle], returning the handle + the size of the GPIO entry.
|
||||
fn map_gpio(
|
||||
self: Arc<Self>,
|
||||
property: &TProp,
|
||||
offset: usize,
|
||||
info: &DeviceTreeGpioPins,
|
||||
) -> Option<(PinHandle, usize)>;
|
||||
}
|
||||
|
||||
impl ProbeContext {
|
||||
|
||||
@@ -9,6 +9,7 @@ use device_api::{
|
||||
bus::Bus,
|
||||
clock::{ClockController, ClockHandle, ResetHandle},
|
||||
device::{Device, DeviceInitContext},
|
||||
gpio::PinHandle,
|
||||
interrupt::{ExternalInterruptController, FullIrq, MessageInterruptController},
|
||||
};
|
||||
use fdt_rs::spec::Phandle;
|
||||
@@ -18,7 +19,9 @@ use libk_util::OneTimeInit;
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::{
|
||||
driver::{traits::DeviceTreePinController, DeviceTreeSyscon},
|
||||
driver::{
|
||||
controller::GpioIter, traits::DeviceTreePinController, DeviceTreeGpioPins, DeviceTreeSyscon,
|
||||
},
|
||||
tree, DeviceTree, DeviceTreeNodeExt, DeviceTreePropertyRead, TNode, TProp,
|
||||
};
|
||||
|
||||
@@ -349,6 +352,24 @@ impl Node {
|
||||
.map(|range| PhysicalAddress::from_u64(range.start))
|
||||
}
|
||||
|
||||
/// Returns an iterator over the node's defined `gpios`
|
||||
pub fn gpios<'a>(
|
||||
&self,
|
||||
options: &'a DeviceTreeGpioPins,
|
||||
) -> Option<impl Iterator<Item = PinHandle> + 'a> {
|
||||
let gpios = self.property("gpios")?;
|
||||
Some(GpioIter {
|
||||
gpios,
|
||||
options,
|
||||
offset: 0,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a `gpios` entry with given index
|
||||
pub fn gpio(&self, options: &DeviceTreeGpioPins, index: usize) -> Option<PinHandle> {
|
||||
self.gpios(options)?.nth(index)
|
||||
}
|
||||
|
||||
/// Returns an input `clock` handle for a given reset name
|
||||
pub fn named_clock(&self, name: &str) -> Option<ClockHandle> {
|
||||
self.property("clock-names")?
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
//! General helpers and utilities for device tree drivers
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use device_api::interrupt::{FullIrq, MessageInterruptController};
|
||||
use device_api::{
|
||||
gpio::{GpioPinConfig, GpioPinLevel, InputPinBias, OutputPinBias},
|
||||
interrupt::{FullIrq, MessageInterruptController},
|
||||
};
|
||||
use fdt_rs::prelude::PropReader;
|
||||
use yggdrasil_abi::net::MacAddress;
|
||||
|
||||
use crate::{
|
||||
driver::{lookup_phandle, map_interrupt_at},
|
||||
driver::{lookup_phandle, map_interrupt_at, DeviceTreeGpioPins},
|
||||
DeviceTreePropertyRead, TProp,
|
||||
};
|
||||
|
||||
@@ -272,3 +275,40 @@ pub fn read_mac_address(node: &Arc<Node>) -> Option<MacAddress> {
|
||||
let mac_bytes: [u8; 6] = property.raw().try_into().ok()?;
|
||||
Some(MacAddress::from(mac_bytes))
|
||||
}
|
||||
|
||||
/// Parse a generic gpio configuration from the device tree 1-cell format
|
||||
pub fn generic_gpio_config(cell: u32, gpio: &DeviceTreeGpioPins) -> GpioPinConfig {
|
||||
const GPIO_ACTIVE_LOW: u32 = 1 << 0;
|
||||
const GPIO_SINGLE_ENDED: u32 = 1 << 1;
|
||||
const GPIO_LINE_OPEN_DRAIN: u32 = 1 << 2;
|
||||
const GPIO_PULL_UP: u32 = 1 << 4;
|
||||
const GPIO_PULL_DOWN: u32 = 1 << 5;
|
||||
const GPIO_PULL_DISABLE: u32 = 1 << 6;
|
||||
const GPIO_OPEN_DRAIN: u32 = GPIO_LINE_OPEN_DRAIN | GPIO_SINGLE_ENDED;
|
||||
|
||||
let active_level = if cell & GPIO_ACTIVE_LOW != 0 {
|
||||
GpioPinLevel::Low
|
||||
} else {
|
||||
GpioPinLevel::High
|
||||
};
|
||||
let output_bias = match cell & (GPIO_SINGLE_ENDED | GPIO_LINE_OPEN_DRAIN) {
|
||||
0 => Some(OutputPinBias::PushPull),
|
||||
GPIO_OPEN_DRAIN | GPIO_SINGLE_ENDED => Some(OutputPinBias::OpenDrain),
|
||||
_ => None,
|
||||
};
|
||||
let input_bias = match cell & (GPIO_PULL_UP | GPIO_PULL_DOWN | GPIO_PULL_DISABLE) {
|
||||
GPIO_PULL_DISABLE => Some(InputPinBias::Floating),
|
||||
GPIO_PULL_UP => Some(InputPinBias::PullUp),
|
||||
GPIO_PULL_DOWN => Some(InputPinBias::PullDown),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
GpioPinConfig {
|
||||
input: gpio.input,
|
||||
output: gpio.output,
|
||||
initial_level: gpio.initial_level,
|
||||
input_bias,
|
||||
output_bias,
|
||||
active_level,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
use core::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
use alloc::{boxed::Box, collections::BTreeMap};
|
||||
|
||||
use device_api::gpio::PinHandle;
|
||||
use libk_util::{ring::LossyRingQueue, sync::spin_rwlock::IrqSafeRwLock};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
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);
|
||||
|
||||
pub trait GpioEvent {
|
||||
fn configure_with_interrupt(&self, handler: Box<dyn Fn() + Send>) -> 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))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn gpio_event_handler_task() {
|
||||
loop {
|
||||
let id = GPIO_EVENT_QUEUE.read().await;
|
||||
|
||||
let lock = GPIO_EVENT_MAP.read();
|
||||
if let Some(handler) = lock.get(&id) {
|
||||
handler();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
id
|
||||
}
|
||||
|
||||
pub fn signal_gpio_event(ev: u64) {
|
||||
GPIO_EVENT_QUEUE.write(ev);
|
||||
}
|
||||
@@ -36,6 +36,7 @@ pub mod arch;
|
||||
pub mod config;
|
||||
pub mod debug;
|
||||
pub mod device;
|
||||
pub mod event;
|
||||
pub mod fs;
|
||||
pub mod module;
|
||||
pub mod random;
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
//! GPIO drivers
|
||||
|
||||
use abi::error::Error;
|
||||
use alloc::{boxed::Box, string::String, sync::Arc, vec::Vec};
|
||||
use device_api::{
|
||||
device::{Device, DeviceInitContext},
|
||||
gpio::{GpioPinLevel, PinHandle},
|
||||
};
|
||||
use device_tree::driver::{device_tree_driver, DeviceTreeGpioPins, Node, ProbeContext};
|
||||
use libk::event::GpioEvent;
|
||||
|
||||
struct LedPin {
|
||||
name: String,
|
||||
handle: PinHandle,
|
||||
}
|
||||
|
||||
struct KeyPin {
|
||||
name: String,
|
||||
handle: PinHandle,
|
||||
}
|
||||
|
||||
trait Pin: Sized + Send + Sync + 'static {
|
||||
fn from_device_tree(node: &Arc<Node>) -> Option<Self>;
|
||||
fn configure(&self) -> Result<(), Error>;
|
||||
fn name(&self) -> &str;
|
||||
}
|
||||
|
||||
struct PinGroup<P: Pin> {
|
||||
name: String,
|
||||
pins: Vec<P>,
|
||||
}
|
||||
|
||||
impl<P: Pin> PinGroup<P> {
|
||||
fn from_device_tree(node: &Arc<Node>) -> Option<Arc<dyn Device>> {
|
||||
let pins = node
|
||||
.children()
|
||||
.filter_map(P::from_device_tree)
|
||||
.collect::<Vec<_>>();
|
||||
if pins.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let name = node.name().unwrap_or("pin-group");
|
||||
Some(Arc::new(Self {
|
||||
pins,
|
||||
name: name.into(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pin> Device for PinGroup<P> {
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
for pin in self.pins.iter() {
|
||||
if let Err(error) = pin.configure() {
|
||||
log::error!("gpio {} setup error: {:?}", pin.name(), error);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl Pin for LedPin {
|
||||
fn from_device_tree(node: &Arc<Node>) -> Option<Self> {
|
||||
let handle = node.gpio(
|
||||
&DeviceTreeGpioPins {
|
||||
input: false,
|
||||
output: true,
|
||||
initial_level: GpioPinLevel::Low,
|
||||
},
|
||||
0,
|
||||
)?;
|
||||
let name = node.name().unwrap_or("gpio-led").into();
|
||||
Some(Self { name, handle })
|
||||
}
|
||||
|
||||
fn configure(&self) -> Result<(), Error> {
|
||||
self.handle.configure()
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl Pin for KeyPin {
|
||||
fn from_device_tree(node: &Arc<Node>) -> Option<Self> {
|
||||
let handle = node.gpio(
|
||||
&DeviceTreeGpioPins {
|
||||
input: true,
|
||||
output: false,
|
||||
initial_level: GpioPinLevel::Low,
|
||||
},
|
||||
0,
|
||||
)?;
|
||||
let name = node.name().unwrap_or("gpio-key").into();
|
||||
Some(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 name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["gpio-leds"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, _context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
PinGroup::<LedPin>::from_device_tree(node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["gpio-keys"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, _context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
PinGroup::<KeyPin>::from_device_tree(node)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ use libk_util::OneTimeInit;
|
||||
pub mod bus;
|
||||
pub mod clock;
|
||||
pub mod display;
|
||||
pub mod gpio;
|
||||
pub mod interrupt;
|
||||
pub mod mbox;
|
||||
pub mod pinctrl;
|
||||
|
||||
@@ -2,12 +2,17 @@ use abi::error::Error;
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
device::{Device, DeviceInitContext},
|
||||
interrupt::FullIrq,
|
||||
gpio::{GpioController, PinHandle},
|
||||
interrupt::{FullIrq, InterruptHandler, IrqVector},
|
||||
};
|
||||
use device_tree::{
|
||||
driver::{device_tree_driver, DeviceTreePinController, Node, ProbeContext},
|
||||
DeviceTreePropertyRead,
|
||||
driver::{
|
||||
device_tree_driver, util::generic_gpio_config, DeviceTreeGpioPins, DeviceTreePinController,
|
||||
Node, ProbeContext,
|
||||
},
|
||||
DeviceTreePropertyRead, TProp,
|
||||
};
|
||||
use libk::device::external_interrupt_controller;
|
||||
use libk_mm::device::DeviceMemoryIo;
|
||||
use libk_util::sync::IrqSafeSpinlock;
|
||||
use tock_registers::{
|
||||
@@ -137,6 +142,12 @@ impl Device for Bcm2711Gpio {
|
||||
regs.configure_pin_interrupts(pin, false, false, false, false);
|
||||
}
|
||||
|
||||
let intc = external_interrupt_controller()?;
|
||||
for irq in self.irqs.iter() {
|
||||
intc.register_irq(irq.irq, irq.options, self.clone())?;
|
||||
intc.enable_irq(irq.irq)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -145,6 +156,33 @@ impl Device for Bcm2711Gpio {
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptHandler for Bcm2711Gpio {
|
||||
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
|
||||
log::warn!("TODO: handle bcm2711-gpio interrupts");
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl GpioController for Bcm2711Gpio {
|
||||
fn setup_gpio(&self, pin: &PinHandle, _event: Option<u64>) -> Result<(), Error> {
|
||||
log::warn!(
|
||||
"TOOD: bcm2711 gpio pin #{} setup: input={}, output={}",
|
||||
pin.index,
|
||||
pin.config.input,
|
||||
pin.config.output
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_gpio(&self, _pin: &PinHandle) -> Result<bool, Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn write_gpio(&self, _pin: &PinHandle, _value: bool) -> Result<(), Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceTreePinController for Bcm2711Gpio {
|
||||
fn configure_pin_group(self: Arc<Self>, group: &Arc<Node>) -> Result<(), Error> {
|
||||
let pins = group.property("brcm,pins").ok_or(Error::InvalidArgument)?;
|
||||
@@ -171,6 +209,24 @@ impl DeviceTreePinController for Bcm2711Gpio {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn map_gpio(
|
||||
self: Arc<Self>,
|
||||
property: &TProp,
|
||||
offset: usize,
|
||||
info: &DeviceTreeGpioPins,
|
||||
) -> Option<(PinHandle, usize)> {
|
||||
let (pin, options) = property.read_cells_at(offset, (1, 1))?;
|
||||
let config = generic_gpio_config(options as u32, info);
|
||||
Some((
|
||||
PinHandle {
|
||||
index: pin as u32,
|
||||
config,
|
||||
parent: self,
|
||||
},
|
||||
2,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
|
||||
@@ -3,14 +3,15 @@ use alloc::{sync::Arc, vec::Vec};
|
||||
use device_api::{
|
||||
clock::{ClockHandle, ResetHandle},
|
||||
device::{Device, DeviceInitContext},
|
||||
gpio::{GpioController, PinHandle},
|
||||
};
|
||||
use device_tree::{
|
||||
driver::{
|
||||
device_tree_driver,
|
||||
util::{GenericPinctrlBiasConfig, GenericPinctrlConfig},
|
||||
DeviceTreePinController, Node, ProbeContext,
|
||||
util::{generic_gpio_config, GenericPinctrlBiasConfig, GenericPinctrlConfig},
|
||||
DeviceTreeGpioPins, DeviceTreePinController, Node, ProbeContext,
|
||||
},
|
||||
DeviceTreePropertyRead,
|
||||
DeviceTreePropertyRead, TProp,
|
||||
};
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIoMut};
|
||||
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
|
||||
@@ -267,6 +268,26 @@ 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> {
|
||||
log::warn!(
|
||||
"TODO: setup gpio #{} input={}, output={}",
|
||||
pin.index,
|
||||
pin.config.input,
|
||||
pin.config.output
|
||||
);
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: GpioRegs> DeviceTreePinController for Gpio<R> {
|
||||
fn configure_pin_group(self: Arc<Self>, pins: &Arc<Node>) -> Result<(), Error> {
|
||||
self.ensure_init()?;
|
||||
@@ -283,6 +304,24 @@ impl<R: GpioRegs> DeviceTreePinController for Gpio<R> {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn map_gpio(
|
||||
self: Arc<Self>,
|
||||
property: &TProp,
|
||||
offset: usize,
|
||||
info: &DeviceTreeGpioPins,
|
||||
) -> Option<(PinHandle, usize)> {
|
||||
let (pin, options) = property.read_cells_at(offset, (1, 1))?;
|
||||
let config = generic_gpio_config(options as u32, info);
|
||||
Some((
|
||||
PinHandle {
|
||||
index: pin as u32,
|
||||
config,
|
||||
parent: self,
|
||||
},
|
||||
2,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl GpioRegs for SysRegs {
|
||||
|
||||
@@ -1,13 +1,25 @@
|
||||
use core::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
use abi::error::Error;
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use device_api::{
|
||||
clock::{ClockHandle, ResetHandle},
|
||||
device::{Device, DeviceInitContext},
|
||||
gpio::{GpioController, GpioPinLevel, PinHandle, SinglePinDirection},
|
||||
interrupt::{FullIrq, InterruptHandler, IrqVector},
|
||||
};
|
||||
use device_tree::driver::{device_tree_driver, DeviceTreePinController, Node, ProbeContext};
|
||||
use device_tree::{
|
||||
driver::{
|
||||
device_tree_driver, util::generic_gpio_config, DeviceTreeGpioPins, DeviceTreePinController,
|
||||
Node, ProbeContext,
|
||||
},
|
||||
DeviceTreePropertyRead, TProp,
|
||||
};
|
||||
use libk::{device::external_interrupt_controller, event::signal_gpio_event};
|
||||
use libk_mm::device::DeviceMemoryIo;
|
||||
use libk_util::sync::IrqSafeSpinlock;
|
||||
use tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
register_structs,
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
};
|
||||
@@ -35,8 +47,10 @@ register_structs! {
|
||||
struct Pl061 {
|
||||
#[allow(unused)]
|
||||
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
|
||||
irq: FullIrq,
|
||||
clocks: Vec<ClockHandle>,
|
||||
resets: Vec<ResetHandle>,
|
||||
gpio_events: [AtomicU64; 8],
|
||||
}
|
||||
|
||||
impl Device for Pl061 {
|
||||
@@ -47,6 +61,14 @@ impl Device for Pl061 {
|
||||
for reset in self.resets.iter() {
|
||||
reset.deassert()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
|
||||
let intc = external_interrupt_controller()?;
|
||||
intc.register_irq(self.irq.irq, self.irq.options, self.clone())?;
|
||||
intc.enable_irq(self.irq.irq)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -55,11 +77,113 @@ impl Device for Pl061 {
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptHandler for Pl061 {
|
||||
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
|
||||
let status = {
|
||||
let lock = self.regs.lock();
|
||||
let val = lock.GPIOMIS.get();
|
||||
lock.GPIOIC.set(0xFF);
|
||||
val
|
||||
};
|
||||
|
||||
for bit in 0..8 {
|
||||
let ev = self.gpio_events[bit].load(Ordering::Acquire);
|
||||
if ev != 0 {
|
||||
signal_gpio_event(ev);
|
||||
}
|
||||
}
|
||||
|
||||
status != 0
|
||||
}
|
||||
}
|
||||
|
||||
impl GpioController for Pl061 {
|
||||
fn write_gpio(&self, _pin: &PinHandle, _value: bool) -> Result<(), Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn read_gpio(&self, _pin: &PinHandle) -> Result<bool, Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn setup_gpio(&self, pin: &PinHandle, event: Option<u64>) -> Result<(), Error> {
|
||||
let regs = self.regs.lock();
|
||||
|
||||
let direction = pin
|
||||
.config
|
||||
.force_single_direction()
|
||||
.ok_or(Error::InvalidArgument)
|
||||
.inspect_err(|_| {
|
||||
log::warn!(
|
||||
"pl061: gpio #{} has invalid direction input={}, output={}",
|
||||
pin.index,
|
||||
pin.config.input,
|
||||
pin.config.output
|
||||
)
|
||||
})?;
|
||||
|
||||
// Enable software control
|
||||
regs.GPIOAFSEL.set(regs.GPIOAFSEL.get() & !(1 << pin.index));
|
||||
|
||||
match direction {
|
||||
SinglePinDirection::Output => {
|
||||
log::info!("pl061: gpio #{} set as output", pin.index);
|
||||
regs.GPIODIR.set(regs.GPIODIR.get() | (1 << pin.index));
|
||||
// Disable interrupt
|
||||
regs.GPIOIE.set(regs.GPIOIE.get() & !(1 << pin.index));
|
||||
|
||||
let level = match pin.config.initial_level {
|
||||
GpioPinLevel::Low => 0,
|
||||
GpioPinLevel::High => 1,
|
||||
};
|
||||
let mut val = regs.GPIODATA[0].get();
|
||||
val &= !(1 << pin.index);
|
||||
val |= level << pin.index;
|
||||
regs.GPIODATA[0].set(val);
|
||||
}
|
||||
SinglePinDirection::Input => {
|
||||
log::info!("pl061: gpio #{} set as input", pin.index);
|
||||
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);
|
||||
|
||||
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));
|
||||
regs.GPIOIE.set(regs.GPIOIE.get() | (1 << pin.index));
|
||||
regs.GPIOIC.set(1 << pin.index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceTreePinController for Pl061 {
|
||||
fn configure_pin_group(self: Arc<Self>, _pins: &Arc<Node>) -> Result<(), Error> {
|
||||
// TODO implement this when I get some board with this pinctrl
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn map_gpio(
|
||||
self: Arc<Self>,
|
||||
property: &TProp,
|
||||
offset: usize,
|
||||
info: &DeviceTreeGpioPins,
|
||||
) -> Option<(PinHandle, usize)> {
|
||||
let (pin, options) = property.read_cells_at(offset, (1, 1))?;
|
||||
let config = generic_gpio_config(options as u32, info);
|
||||
Some((
|
||||
PinHandle {
|
||||
index: pin as u32,
|
||||
config,
|
||||
parent: self,
|
||||
},
|
||||
2,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
@@ -76,14 +200,17 @@ device_tree_driver! {
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
let irq = node.interrupt(0)?;
|
||||
|
||||
let base = node.map_base(context, 0)?;
|
||||
let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
|
||||
|
||||
let pinctrl = Arc::new(Pl061 {
|
||||
regs: IrqSafeSpinlock::new(regs),
|
||||
irq,
|
||||
clocks,
|
||||
resets,
|
||||
gpio_events: [const { AtomicU64::new(0) }; 8]
|
||||
});
|
||||
|
||||
node.make_pin_controller(pinctrl.clone());
|
||||
|
||||
@@ -37,6 +37,7 @@ pub fn kinit() -> Result<(), Error> {
|
||||
#[cfg(any(rust_analyzer, target_arch = "riscv64", target_arch = "aarch64"))]
|
||||
{
|
||||
use device_tree::driver::InitSequence;
|
||||
use libk::event;
|
||||
|
||||
device_tree::driver::lazy_init(
|
||||
|_| (),
|
||||
@@ -55,6 +56,8 @@ pub fn kinit() -> Result<(), Error> {
|
||||
},
|
||||
InitSequence::Late,
|
||||
);
|
||||
|
||||
runtime::spawn(event::gpio_event_handler_task()).ok();
|
||||
}
|
||||
// Initialize PCI devices
|
||||
if let Err(error) = PciBusManager::setup_bus_devices(false) {
|
||||
|
||||
Reference in New Issue
Block a user