char: add pwm subsystem, add sifive pwm driver

This commit is contained in:
2026-03-23 14:02:02 +02:00
parent 0386e3916d
commit f416414b93
20 changed files with 837 additions and 67 deletions
+84 -15
View File
@@ -1,7 +1,7 @@
use alloc::sync::Arc;
use device_api::{
clock::{ClockController, ClockHandle, Hertz},
device::Device,
device::{Device, DeviceInitContext},
};
use device_tree::{
DeviceTreePropertyRead, TProp,
@@ -9,8 +9,14 @@ use device_tree::{
};
use libk::error::Error;
use libk_mm::device::DeviceMemoryIo;
use libk_util::sync::IrqSafeSpinlock;
use tock_registers::{interfaces::Readable, register_structs, registers::ReadWrite};
use libk_util::{OneTimeInit, sync::IrqSafeSpinlock};
use tock_registers::{
interfaces::{Readable, Writeable},
register_structs,
registers::ReadWrite,
};
use crate::pll::{PllCfg, WrpllData};
const CLK_COREPLL: u32 = 0;
const CLK_DDRPLL: u32 = 1;
@@ -20,7 +26,7 @@ const CLK_TLCLK: u32 = 3;
register_structs! {
Regs {
(0x000 => hfxosccfg: ReadWrite<u32>),
(0x004 => corepllcfg0: ReadWrite<u32>),
(0x004 => corepllcfg0: ReadWrite<u32, PllCfg::Register>),
(0x008 => _0),
(0x00C => ddrpllcfg0: ReadWrite<u32>),
(0x010 => ddrpllcfg1: ReadWrite<u32>),
@@ -38,9 +44,62 @@ struct Prci {
clk_osc: ClockHandle,
clk_rtc: ClockHandle,
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
init: OneTimeInit<()>,
}
impl Regs {
fn read_core_pll(&self) -> WrpllData {
let pll = self.corepllcfg0.extract();
let divr = pll.read(PllCfg::PLLR);
let divf = pll.read(PllCfg::PLLF);
let divq = pll.read(PllCfg::PLLQ);
WrpllData {
divq,
divr,
divf,
int_feedback: true,
}
}
}
impl Prci {
fn ensure_init(&self) {
// U-Boot should configure coreclk to 1GHz, but when booting
// in QEMU, there is not U-Boot stage, and some peripherals
// like PWM expect the clock to be configured this way. So
// this needs to be done manually here
self.init.or_init_with(|| {
let regs = self.regs.lock();
if regs.coreclksel.get() != 0 {
// coreclk configured to hfclk
// configure corepll to output 1GHz and switch to it
// divf = 150
// divr = 4
// divq = 1
regs.corepllcfg0.write(
PllCfg::PLLF.val(150)
+ PllCfg::PLLR.val(4)
+ PllCfg::PLLQ.val(1)
+ PllCfg::PLLFSEBYPASS::SET,
);
while !regs.corepllcfg0.matches_all(PllCfg::PLLLOCK::SET) {
core::hint::spin_loop();
}
regs.coreclksel.set(0);
}
});
}
}
impl Device for Prci {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
self.ensure_init();
if let Ok(coreclk) = self.clock_rate(Some(CLK_COREPLL)) {
log::info!("coreclk = {coreclk}");
}
Ok(())
}
fn display_name(&self) -> &str {
"sifive,fu540-c000-prci"
}
@@ -48,19 +107,27 @@ impl Device for Prci {
impl ClockController for Prci {
fn clock_rate(&self, clock: Option<u32>) -> Result<Hertz, Error> {
self.ensure_init();
let _ = &self.clk_rtc;
let regs = self.regs.lock();
match clock {
Some(CLK_TLCLK) => {
// 0 = CORE_PLL
// 1 = HFCLK
let coreclksel = regs.coreclksel.get() & 1 != 0;
let glcm = if coreclksel {
self.clk_osc.rate()?
} else {
todo!()
Some(CLK_COREPLL) => {
let regs = self.regs.lock();
let hfclk = self.clk_osc.rate()?;
// == 0 -> use corepll
// == 1 -> use hfclk
let coreclksel = regs.coreclksel.get() != 0;
let coreclk = match coreclksel {
true => hfclk,
false => {
let pll = regs.read_core_pll();
pll.output_rate(hfclk)
}
};
Ok(glcm / 2)
Ok(coreclk)
}
Some(CLK_TLCLK) => {
let coreclk = self.clock_rate(Some(CLK_COREPLL))?;
Ok(coreclk / 2)
}
Some(_) => todo!(),
None => Err(Error::InvalidArgument),
@@ -72,6 +139,7 @@ impl ClockController for Prci {
}
fn enable_clock(&self, clock: Option<u32>) -> Result<(), Error> {
self.ensure_init();
match clock {
Some(CLK_GEMGXLPLL) | Some(CLK_DDRPLL) | Some(CLK_COREPLL) => todo!(),
Some(CLK_TLCLK) => Ok(()),
@@ -110,7 +178,8 @@ device_tree_driver! {
let prci = Arc::new(Prci {
regs: IrqSafeSpinlock::new(regs),
clk_osc,
clk_rtc
clk_rtc,
init: OneTimeInit::new()
});
node.make_clock_controller(prci.clone());
+3 -34
View File
@@ -12,10 +12,12 @@ use libk_mm::device::DeviceMemoryIo;
use libk_util::sync::IrqSafeSpinlock;
use tock_registers::{
interfaces::Readable,
register_bitfields, register_structs,
register_structs,
registers::{ReadOnly, ReadWrite},
};
use crate::pll::{PllCfg, WrpllData};
// const CLK_COREPLL: u32 = 0;
// const CLK_DDRPLL: u32 = 1;
// const CLK_GEMGXLPLL: u32 = 2;
@@ -26,39 +28,6 @@ use tock_registers::{
const CLK_PCLK: u32 = 7;
// const CLK_PCIE_AUX: u32 = 8;
#[derive(Debug)]
struct WrpllData {
divr: u32,
divf: u32,
divq: u32,
int_feedback: bool,
}
register_bitfields! {
u32,
PllCfg [
PLLR OFFSET(0) NUMBITS(6) [],
PLLF OFFSET(6) NUMBITS(9) [],
PLLQ OFFSET(15) NUMBITS(3) [],
PLLBYPASS OFFSET(24) NUMBITS(1) [],
PLLFSEBYPASS OFFSET(25) NUMBITS(1) [],
PLLLOCK OFFSET(31) NUMBITS(1) [],
]
}
impl WrpllData {
fn fbdiv(&self) -> u32 {
if self.int_feedback { 2 } else { 1 }
}
fn output_rate(&self, input_rate: Hertz) -> Hertz {
let fbdiv = self.fbdiv();
let n = input_rate * fbdiv * (self.divf + 1);
let n = n / (self.divr + 1);
n >> self.divq
}
}
register_structs! {
Regs {
(0x00 => hfxosccfg: ReadWrite<u32>),
+2
View File
@@ -5,4 +5,6 @@ extern crate alloc;
mod clock_fu540;
mod clock_fu740;
mod ethernet;
mod pll;
mod pwm;
mod uart;
+35
View File
@@ -0,0 +1,35 @@
use device_api::clock::Hertz;
use tock_registers::register_bitfields;
#[derive(Debug)]
pub(crate) struct WrpllData {
pub(crate) divr: u32,
pub(crate) divf: u32,
pub(crate) divq: u32,
pub(crate) int_feedback: bool,
}
register_bitfields! {
u32,
pub PllCfg [
PLLR OFFSET(0) NUMBITS(6) [],
PLLF OFFSET(6) NUMBITS(9) [],
PLLQ OFFSET(15) NUMBITS(3) [],
PLLBYPASS OFFSET(24) NUMBITS(1) [],
PLLFSEBYPASS OFFSET(25) NUMBITS(1) [],
PLLLOCK OFFSET(31) NUMBITS(1) [],
]
}
impl WrpllData {
fn fbdiv(&self) -> u32 {
if self.int_feedback { 2 } else { 1 }
}
pub(crate) fn output_rate(&self, input_rate: Hertz) -> Hertz {
let fbdiv = self.fbdiv();
let n = input_rate * fbdiv * (self.divf + 1);
let n = n / (self.divr + 1);
n >> self.divq
}
}
+206
View File
@@ -0,0 +1,206 @@
use alloc::sync::Arc;
use device_api::{
clock::{ClockHandle, Hertz},
device::{Device, DeviceInitContext},
pwm::{PwmController, PwmControllerInfo},
};
use device_tree::driver::{Node, ProbeContext, device_tree_driver};
use libk::{device::manager::DEVICE_REGISTRY, error::Error};
use libk_mm::device::DeviceMemoryIo;
use libk_util::sync::IrqSafeSpinlock;
use tock_registers::{
interfaces::{ReadWriteable, Readable, Writeable},
register_bitfields, register_structs,
registers::ReadWrite,
};
use yggdrasil_abi::time::NANOSECONDS_IN_SECOND;
const CHANNEL_COUNT: usize = 4;
const CMPWIDTH: usize = 16;
const SCALE_MAX: u32 = (1 << 4) - 1;
const MAX_PERIOD_CYCLES: u64 = (1u64 << (CMPWIDTH + 16)) - 1;
const MAX_COMPARE_VALUE: u32 = (1u32 << 16) - 1;
register_bitfields! {
u32,
PwmCfg [
SCALE OFFSET(0) NUMBITS(4) [],
STICKY OFFSET(8) NUMBITS(1) [],
ZEROCMP OFFSET(9) NUMBITS(1) [],
DEGLITCH OFFSET(10) NUMBITS(1) [],
ENALWAYS OFFSET(12) NUMBITS(1) [],
ENONESHOT OFFSET(13) NUMBITS(1) [],
CMP_CENTER OFFSET(16) NUMBITS(4) [],
CMP_GANG OFFSET(24) NUMBITS(4) [],
CMP_IP OFFSET(28) NUMBITS(4) [],
]
}
register_structs! {
Regs {
(0x00 => pwmcfg: ReadWrite<u32, PwmCfg::Register>),
(0x04 => _0),
(0x08 => pwmcount: ReadWrite<u32>),
(0x0C => _1),
(0x10 => pwms: ReadWrite<u32>),
(0x14 => _2),
(0x20 => pwmcmp: [ReadWrite<u32>; CHANNEL_COUNT]),
(0x30 => @END),
}
}
struct Pwm {
name: &'static str,
clock: ClockHandle,
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
}
impl Regs {
fn ns_to_compare_value(&self, input_clk: Hertz, ns: u64) -> Option<u32> {
let scale = self.pwmcfg.read(PwmCfg::SCALE);
let tick_duration_ns = NANOSECONDS_IN_SECOND / (input_clk.0 >> scale);
let ticks = ns / tick_duration_ns;
if ticks < MAX_COMPARE_VALUE as u64 {
Some(ticks as u32)
} else {
None
}
}
fn compare_value_to_ns(&self, input_clk: Hertz, cval: u32) -> u64 {
let scale = self.pwmcfg.read(PwmCfg::SCALE);
let tick_duration_ns = NANOSECONDS_IN_SECOND / (input_clk.0 >> scale);
tick_duration_ns * cval as u64
}
}
impl Device for Pwm {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
// pwm counter registers are (cmpwidth + 15) bits wide
self.clock.enable()?;
// Set all channels to max value and channel 0 sets the period
{
let regs = self.regs.lock();
regs.pwmcfg
.write(PwmCfg::ZEROCMP::SET + PwmCfg::ENALWAYS::SET);
for channel in 0..CHANNEL_COUNT {
regs.pwmcmp[channel].set(MAX_COMPARE_VALUE);
}
}
DEVICE_REGISTRY.pwm.register_controller(self)?;
Ok(())
}
fn display_name(&self) -> &str {
self.name
}
}
impl PwmController for Pwm {
fn controller_info(&self) -> Result<PwmControllerInfo, Error> {
Ok(PwmControllerInfo {
channel_count: CHANNEL_COUNT,
per_channel_period: false,
})
}
fn channel_period(&self, _channel: u32) -> Result<u64, Error> {
let input_clk = self.clock.rate()?;
let regs = self.regs.lock();
let compare_value = regs.pwmcmp[0].get();
if compare_value == MAX_COMPARE_VALUE {
return Ok(0);
}
let period_ns = regs.compare_value_to_ns(input_clk, compare_value);
Ok(period_ns)
}
fn set_channel_period(&self, _channel: u32, period: u64) -> Result<u64, Error> {
// Channel 0 sets the period
let input_clk = self.clock.rate()?;
let period_cycles = period * u64::from(input_clk) / NANOSECONDS_IN_SECOND;
let mut prescaler = 0;
if period_cycles > MAX_PERIOD_CYCLES {
return Err(Error::InvalidArgument);
}
while (period_cycles >> prescaler) >= MAX_COMPARE_VALUE as u64 {
prescaler += 1;
}
if prescaler > SCALE_MAX {
log::warn!("Cannot fit {prescaler} into 4-bit prescaler");
return Err(Error::InvalidArgument);
}
let period_ticks = period_cycles >> prescaler;
let regs = self.regs.lock();
regs.pwmcfg
.modify(PwmCfg::SCALE.val(prescaler) + PwmCfg::ZEROCMP::SET);
regs.pwmcmp[0].set(period_ticks as u32);
let tick_duration_ns = NANOSECONDS_IN_SECOND / (input_clk.0 >> prescaler);
let real_period_ns = tick_duration_ns * period_ticks;
Ok(real_period_ns)
}
fn set_duty_cycle(&self, channel: u32, t: u64) -> Result<(), Error> {
// Channel 0 sets the period
if channel == 0 {
return Ok(());
}
let input_clk = self.clock.rate()?;
let regs = self.regs.lock();
let cval = regs
.ns_to_compare_value(input_clk, t)
.ok_or(Error::InvalidArgument)?;
// If the cval exceeds the "period" comparator, the PWM will never fire
let cval = cval.min(regs.pwmcmp[0].get().saturating_sub(1));
regs.pwmcmp[channel as usize].set(cval);
Ok(())
}
fn duty_cycle(&self, channel: u32) -> Result<u64, Error> {
if channel == 0 {
return Ok(0);
}
let input_clk = self.clock.rate()?;
let regs = self.regs.lock();
Ok(regs.compare_value_to_ns(input_clk, regs.pwmcmp[channel as usize].get()))
}
fn stop_channel(&self, channel: u32) -> Result<(), Error> {
if channel == 0 {
return Ok(());
}
self.regs.lock().pwmcmp[channel as usize].set(MAX_COMPARE_VALUE);
Ok(())
}
}
device_tree_driver! {
compatible: ["sifive,pwm0"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let name = node.name().unwrap_or("pwm");
let base = node.map_base(context, 0)?;
let clock = node.clock(0)?;
let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
let pwm = Arc::new(Pwm {
name,
regs: IrqSafeSpinlock::new(regs),
clock,
});
Some(pwm)
}
}
}
+1 -1
View File
@@ -39,7 +39,7 @@ impl PciInterruptMap {
pub fn map_interrupt(
&self,
interrupt: &PciInterrupt,
priority: IrqPriority,
#[allow(unused)] priority: IrqPriority,
) -> Result<PciInterruptRoute, Error> {
match self {
Self::Fixed(map) => map.get(interrupt).cloned().ok_or(Error::DoesNotExist),
+6
View File
@@ -48,6 +48,12 @@ impl From<u64> for Hertz {
}
}
impl From<Hertz> for u64 {
fn from(value: Hertz) -> Self {
value.0
}
}
impl Shr<u32> for Hertz {
type Output = Hertz;
+1
View File
@@ -10,6 +10,7 @@ pub mod device;
pub mod gpio;
pub mod i2c;
pub mod interrupt;
pub mod pwm;
pub mod serial;
pub mod spi;
pub mod timer;
+55
View File
@@ -0,0 +1,55 @@
use alloc::sync::Arc;
use yggdrasil_abi::error::Error;
use crate::device::Device;
pub enum PwmChannelMode {
OneShot,
Repeat,
}
#[derive(Debug)]
pub struct PwmControllerInfo {
// If true, channel period can be configured individually,
// if false, channel 0 is used to set the period, and cannot be used to output PWM
pub per_channel_period: bool,
pub channel_count: usize,
}
pub struct PwmControllerDevice {
pub device: Arc<dyn PwmController>,
}
pub struct PwmChannelDevice {
pub device: Arc<dyn PwmController>,
pub channel: u32,
}
pub trait PwmController: Device {
fn controller_info(&self) -> Result<PwmControllerInfo, Error>;
fn channel_period(&self, channel: u32) -> Result<u64, Error>;
fn set_channel_period(&self, channel: u32, period: u64) -> Result<u64, Error>;
fn duty_cycle(&self, channel: u32) -> Result<u64, Error>;
fn set_duty_cycle(&self, channel: u32, t: u64) -> Result<(), Error>;
fn stop_channel(&self, channel: u32) -> Result<(), Error>;
}
impl From<Arc<dyn PwmController>> for PwmControllerDevice {
fn from(value: Arc<dyn PwmController>) -> Self {
Self { device: value }
}
}
impl Device for PwmControllerDevice {
fn display_name(&self) -> &str {
self.device.display_name()
}
}
impl Device for PwmChannelDevice {
fn display_name(&self) -> &str {
self.device.display_name()
}
}
@@ -11,6 +11,13 @@ use crate::{DeviceTreePropertyRead, TProp, driver::DeviceTreeGpioPins};
use super::{Node, lookup_phandle};
pub(crate) struct InterruptIter<'dt> {
pub(crate) interrupts: TProp<'dt>,
pub(crate) interrupt_parent: Arc<Node>,
pub(crate) interrupt_cells: usize,
pub(crate) offset: usize,
}
pub(crate) struct ClockIter<'dt> {
pub(crate) clocks: TProp<'dt>,
pub(crate) offset: usize,
@@ -27,6 +34,19 @@ pub(crate) struct GpioIter<'o, 'dt> {
pub(crate) offset: usize,
}
impl Iterator for InterruptIter<'_> {
type Item = IrqHandle;
fn next(&mut self) -> Option<Self::Item> {
if self.offset >= self.interrupts.len() {
return None;
}
let irq = map_interrupt_at(&self.interrupt_parent, &self.interrupts, self.offset)?;
self.offset += self.interrupt_cells;
Some(irq)
}
}
impl Iterator for ClockIter<'_> {
type Item = ClockHandle;
+16 -1
View File
@@ -22,7 +22,9 @@ use yggdrasil_abi::error::Error;
use crate::{
DeviceTree, DeviceTreeNodeExt, DeviceTreePropertyRead, TNode, TProp,
driver::{
DeviceTreeGpioPins, DeviceTreeSyscon, controller::GpioIter, traits::DeviceTreePinController,
DeviceTreeGpioPins, DeviceTreeSyscon,
controller::{GpioIter, InterruptIter},
traits::DeviceTreePinController,
},
tree,
};
@@ -402,6 +404,19 @@ impl Node {
.and_then(|i| self.reset(i))
}
/// Returns an iterator over the node's interrupts
pub fn interrupts(&self) -> Option<impl Iterator<Item = IrqHandle>> {
let interrupts = self.property("interrupts")?;
let interrupt_parent = self.interrupt_controller()?;
let interrupt_cells = interrupt_parent.self_interrupt_cells()?;
Some(InterruptIter {
interrupts,
interrupt_parent,
interrupt_cells,
offset: 0,
})
}
/// Returns an iterator over the node's input clocks
pub fn clocks(&self) -> Option<impl Iterator<Item = ClockHandle>> {
let clocks = self.property("clocks")?;
+1
View File
@@ -8,6 +8,7 @@ use yggdrasil_abi::{error::Error, process::ProcessId};
use crate::vfs::{CommonImpl, FileReadiness, NodeRef};
pub mod i2c;
pub mod pwm;
pub mod spi;
#[async_trait]
+95
View File
@@ -0,0 +1,95 @@
use core::task::{Context, Poll};
use alloc::boxed::Box;
use async_trait::async_trait;
use device_api::pwm::{PwmChannelDevice, PwmControllerDevice};
use yggdrasil_abi::error::Error;
use crate::{device::char::CharDevice, vfs::FileReadiness};
// TODO PWM controller operations
#[async_trait]
impl CharDevice for PwmControllerDevice {
async fn read(&self, buffer: &mut [u8]) -> Result<usize, Error> {
let _ = buffer;
Err(Error::InvalidOperation)
}
async fn write(&self, buffer: &[u8]) -> Result<usize, Error> {
let _ = buffer;
Err(Error::InvalidOperation)
}
fn read_nonblocking(&self, buffer: &mut [u8]) -> Result<usize, Error> {
let _ = buffer;
Err(Error::InvalidOperation)
}
fn write_nonblocking(&self, buffer: &[u8]) -> Result<usize, Error> {
let _ = buffer;
Err(Error::InvalidOperation)
}
fn device_request(&self, option: u32, buffer: &mut [u8], len: usize) -> Result<usize, Error> {
let _ = (option, buffer, len);
todo!()
}
}
impl FileReadiness for PwmControllerDevice {
fn poll_read(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
let _ = cx;
todo!()
}
}
#[async_trait]
impl CharDevice for PwmChannelDevice {
async fn read(&self, buffer: &mut [u8]) -> Result<usize, Error> {
self.read_nonblocking(buffer)
}
async fn write(&self, buffer: &[u8]) -> Result<usize, Error> {
self.write_nonblocking(buffer)
}
fn read_nonblocking(&self, buffer: &mut [u8]) -> Result<usize, Error> {
if buffer.len() < 8 {
return Err(Error::BufferTooSmall);
}
let mut len = 8;
let duty_cycle = self.device.duty_cycle(self.channel)?;
buffer[0..8].copy_from_slice(&u64::to_ne_bytes(duty_cycle));
if buffer.len() >= 16 {
let period = self.device.channel_period(self.channel)?;
buffer[8..16].copy_from_slice(&u64::to_ne_bytes(period));
len += 8;
}
Ok(len)
}
fn write_nonblocking(&self, buffer: &[u8]) -> Result<usize, Error> {
if buffer.len() < 8 {
return Err(Error::InvalidArgument);
}
let mut len = 8;
let duty_cycle = u64::from_ne_bytes(buffer[0..8].try_into().unwrap());
let period = if buffer.len() >= 16 {
len += 8;
Some(u64::from_ne_bytes(buffer[8..16].try_into().unwrap()))
} else {
None
};
if let Some(period) = period {
self.device.set_channel_period(self.channel, period)?;
}
if duty_cycle != u64::MAX {
self.device.set_duty_cycle(self.channel, duty_cycle)?;
}
Ok(len)
}
fn device_request(&self, option: u32, buffer: &mut [u8], len: usize) -> Result<usize, Error> {
let _ = (option, buffer, len);
todo!()
}
}
impl FileReadiness for PwmChannelDevice {
fn poll_read(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
let _ = cx;
todo!()
}
}
+77
View File
@@ -6,6 +6,7 @@ use core::{
use alloc::{collections::BTreeMap, format, sync::Arc, vec::Vec};
use device_api::{
i2c::{I2CController, I2CDevice},
pwm::{PwmChannelDevice, PwmController, PwmControllerDevice},
spi::SpiController,
};
use libk_util::{OneTimeInit, sync::spin_rwlock::IrqSafeRwLock};
@@ -61,12 +62,25 @@ pub struct SpiDeviceRegistry {
registry: GenericRegistry<Arc<SpiControllerDevice>>,
}
// Manages devices: pwm<N>, pwm<N>-<M>
#[allow(unused)]
struct PwmDeviceRegistryEntry {
controller: Arc<PwmControllerDevice>,
controller_node: Option<NodeRef>,
channels: BTreeMap<u32, (Arc<PwmChannelDevice>, Option<NodeRef>)>,
}
pub struct PwmDeviceRegistry {
registry: GenericRegistry<PwmDeviceRegistryEntry>,
}
pub struct DeviceRegistry {
pub display: DisplayDeviceRegistry,
pub terminal: TerminalRegistry,
pub serial_terminal: SerialTerminalRegistry,
pub i2c: I2CDeviceRegistry,
pub spi: SpiDeviceRegistry,
pub pwm: PwmDeviceRegistry,
}
pub static DEVICE_REGISTRY: DeviceRegistry = DeviceRegistry {
@@ -75,6 +89,7 @@ pub static DEVICE_REGISTRY: DeviceRegistry = DeviceRegistry {
serial_terminal: SerialTerminalRegistry::new(),
i2c: I2CDeviceRegistry::new(),
spi: SpiDeviceRegistry::new(),
pwm: PwmDeviceRegistry::new(),
};
impl TerminalRegistry {
@@ -168,6 +183,62 @@ impl SpiDeviceRegistry {
}
}
impl PwmDeviceRegistry {
pub const fn new() -> Self {
Self {
registry: GenericRegistry::new(),
}
}
pub fn register_controller(&self, controller: Arc<dyn PwmController>) -> Result<u32, Error> {
let controller_info = controller.controller_info()?;
let id = self.registry.register_with(|id| {
let controller_device = Arc::new(PwmControllerDevice::from(controller.clone()));
let controller_name = format!("pwm{id}");
log::info!("Register PWM controller {controller_name:?}");
let controller_node = devfs::add_named_char_device(
controller_device.clone(),
controller_name,
FileMode::new(0o600),
)
.ok();
let start_channel = if controller_info.per_channel_period {
0
} else {
1
};
let mut channels = BTreeMap::new();
for channel in start_channel..controller_info.channel_count {
let channel_name = format!("pwm{id}-{channel}");
log::info!("Register PWM channel {channel_name:?}");
let channel_device = Arc::new(PwmChannelDevice {
device: controller.clone(),
channel: channel as u32,
});
let channel_node = devfs::add_named_char_device(
channel_device.clone(),
channel_name,
FileMode::new(0o600),
)
.ok();
channels.insert(channel as u32, (channel_device, channel_node));
}
let entry = PwmDeviceRegistryEntry {
controller: controller_device,
controller_node,
channels,
};
entry
})?;
Ok(id)
}
}
impl<T> GenericRegistry<T> {
pub const fn new() -> Self {
Self {
@@ -180,7 +251,13 @@ impl<T> GenericRegistry<T> {
let mut members = self.members.write();
let id = self.last_index.fetch_add(1, Ordering::Release);
members.insert(id, member);
Ok(id)
}
pub fn register_with<F: FnOnce(u32) -> T>(&self, mapper: F) -> Result<u32, Error> {
let mut members = self.members.write();
let id = self.last_index.fetch_add(1, Ordering::Release);
members.insert(id, (mapper)(id));
Ok(id)
}
}
+3 -2
View File
@@ -53,7 +53,7 @@ pub fn add_named_char_device<S: AsRef<str>>(
dev: Arc<dyn CharDevice>,
name: S,
mode: FileMode,
) -> Result<(), Error> {
) -> Result<NodeRef, Error> {
let name = name.as_ref();
log::info!("Add char device: {}", name);
@@ -65,7 +65,8 @@ pub fn add_named_char_device<S: AsRef<str>>(
);
let filename = OwnedFilename::new(name)?;
DEVFS_ROOT.get().add_child(filename, node)
DEVFS_ROOT.get().add_child(filename, node.clone())?;
Ok(node)
}
/// Adds a block device with a custom name