i2c: implement initial support for i2c devices
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
clock::{ClockController, ClockHandle, Hertz},
|
||||
device::{Device, DeviceInitContext},
|
||||
};
|
||||
use device_tree::{
|
||||
DeviceTreePropertyRead, TProp,
|
||||
driver::{DeviceTreeClockController, Node, ProbeContext, device_tree_driver},
|
||||
};
|
||||
use libk::error::Error;
|
||||
|
||||
struct Cprman {
|
||||
clk_osc: ClockHandle,
|
||||
}
|
||||
|
||||
impl Device for Cprman {
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
let _ = &self.clk_osc;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
"bcm2711-cprman"
|
||||
}
|
||||
}
|
||||
|
||||
impl ClockController for Cprman {
|
||||
fn clock_rate(&self, clock: Option<u32>) -> Result<Hertz, Error> {
|
||||
let _ = clock;
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_clock_rate(&self, clock: Option<u32>, rate: Hertz) -> Result<Hertz, Error> {
|
||||
let _ = clock;
|
||||
let _ = rate;
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn enable_clock(&self, clock: Option<u32>) -> Result<(), Error> {
|
||||
let _ = clock;
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn disable_clock(&self, clock: Option<u32>) -> Result<(), Error> {
|
||||
let _ = clock;
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceTreeClockController for Cprman {
|
||||
fn map_clock(self: Arc<Self>, property: &TProp, offset: usize) -> Option<(ClockHandle, usize)> {
|
||||
let clock = property.read_cell(offset, 1)? as u32;
|
||||
Some((
|
||||
ClockHandle {
|
||||
parent: self.clone(),
|
||||
clock: Some(clock),
|
||||
},
|
||||
1,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["brcm,bcm2711-cprman"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let _base = node.map_base(context, 0)?;
|
||||
let clk_osc = node.clock(0)?;
|
||||
|
||||
let cprman = Arc::new(Cprman {
|
||||
clk_osc
|
||||
});
|
||||
|
||||
node.make_clock_controller(cprman.clone());
|
||||
Some(cprman)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
clock::{ClockHandle, Hertz},
|
||||
device::{Device, DeviceInitContext},
|
||||
i2c::{I2CAddress, I2CController},
|
||||
interrupt::{InterruptHandler, IrqHandle, IrqVector},
|
||||
};
|
||||
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::{OneTimeInit, sync::spin_rwlock::IrqSafeRwLock};
|
||||
use tock_registers::{
|
||||
interfaces::{ReadWriteable, Readable, Writeable},
|
||||
register_bitfields, register_structs,
|
||||
registers::ReadWrite,
|
||||
};
|
||||
use yggdrasil_abi::io::device::i2c::I2CMasterInfo;
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
C [
|
||||
I2CEN OFFSET(15) NUMBITS(1) [],
|
||||
INTR OFFSET(10) NUMBITS(1) [],
|
||||
INTT OFFSET(9) NUMBITS(1) [],
|
||||
INTD OFFSET(8) NUMBITS(1) [],
|
||||
ST OFFSET(7) NUMBITS(1) [],
|
||||
CLEAR OFFSET(4) NUMBITS(2) [],
|
||||
READ OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
S [
|
||||
CLKT OFFSET(9) NUMBITS(1) [],
|
||||
ERR OFFSET(8) NUMBITS(1) [],
|
||||
RXF OFFSET(7) NUMBITS(1) [],
|
||||
TXE OFFSET(6) NUMBITS(1) [],
|
||||
RXD OFFSET(5) NUMBITS(1) [],
|
||||
TXD OFFSET(4) NUMBITS(1) [],
|
||||
RXR OFFSET(3) NUMBITS(1) [],
|
||||
TXW OFFSET(2) NUMBITS(1) [],
|
||||
DONE OFFSET(1) NUMBITS(1) [],
|
||||
TA OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
Regs {
|
||||
(0x00 => C: ReadWrite<u32, C::Register>),
|
||||
(0x04 => S: ReadWrite<u32, S::Register>),
|
||||
(0x08 => DLEN: ReadWrite<u32>),
|
||||
(0x0C => A: ReadWrite<u32>),
|
||||
(0x10 => FIFO: ReadWrite<u32>),
|
||||
(0x14 => DIV: ReadWrite<u32>),
|
||||
(0x18 => DEL: ReadWrite<u32>),
|
||||
(0x1C => CLKT: ReadWrite<u32>),
|
||||
(0x20 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
struct I2C {
|
||||
name: &'static str,
|
||||
clock_frequency: Option<Hertz>,
|
||||
irq: IrqHandle,
|
||||
clock: Option<ClockHandle>,
|
||||
regs: IrqSafeRwLock<DeviceMemoryIo<'static, Regs>>,
|
||||
index: OneTimeInit<u32>,
|
||||
}
|
||||
|
||||
impl Regs {
|
||||
fn start_transfer(&self, name: &str, buflen: u16, address: I2CAddress, read: bool) {
|
||||
log::debug!(
|
||||
"{}: start address={}, read={}, len={}",
|
||||
name,
|
||||
address,
|
||||
read,
|
||||
buflen
|
||||
);
|
||||
|
||||
let address = address.as_8_bit().unwrap();
|
||||
let read = if read { C::READ::SET } else { C::READ::CLEAR };
|
||||
|
||||
self.S.write(S::ERR::SET + S::DONE::SET);
|
||||
self.DLEN.set(buflen as u32);
|
||||
self.C.modify(C::ST::CLEAR + C::I2CEN::CLEAR);
|
||||
self.A.set(address as u32);
|
||||
self.C.modify(read + C::ST::SET + C::I2CEN::SET);
|
||||
}
|
||||
|
||||
fn finish_transfer(&self, name: &str) -> Result<(), Error> {
|
||||
log::debug!("{}: finish transfer", name);
|
||||
let status = self.S.extract();
|
||||
self.C.set(0);
|
||||
if status.matches_all(S::ERR::SET) {
|
||||
self.S.write(S::DONE::SET + S::ERR::SET);
|
||||
return Err(Error::HostUnreachable);
|
||||
}
|
||||
if status.matches_all(S::DONE::SET) {
|
||||
self.S.write(S::DONE::SET);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_byte(&self, byte: u8) -> Result<bool, Error> {
|
||||
loop {
|
||||
let status = self.S.extract();
|
||||
if status.matches_all(S::ERR::SET) {
|
||||
self.C.write(C::CLEAR.val(1));
|
||||
self.S.write(S::ERR::SET + S::DONE::SET);
|
||||
// TODO better code
|
||||
return Err(Error::HostUnreachable);
|
||||
}
|
||||
if status.matches_all(S::DONE::SET) {
|
||||
self.C.set(0);
|
||||
self.S.write(S::DONE::SET);
|
||||
return Ok(false);
|
||||
}
|
||||
if status.matches_all(S::TXD::SET) {
|
||||
self.FIFO.set(byte as u32);
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
|
||||
fn read_byte(&self) -> Result<Option<u8>, Error> {
|
||||
loop {
|
||||
let status = self.S.extract();
|
||||
if status.matches_all(S::ERR::SET) {
|
||||
self.C.write(C::CLEAR.val(1));
|
||||
self.S.write(S::ERR::SET + S::DONE::SET);
|
||||
// TODO better code
|
||||
return Err(Error::HostUnreachable);
|
||||
}
|
||||
if status.matches_all(S::DONE::SET) {
|
||||
self.C.set(0);
|
||||
self.S.write(S::DONE::SET);
|
||||
return Ok(None);
|
||||
}
|
||||
if status.matches_all(S::RXD::SET) {
|
||||
let val = self.FIFO.get() as u8;
|
||||
return Ok(Some(val));
|
||||
}
|
||||
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for I2C {
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
// TODO
|
||||
let _ = &self.clock;
|
||||
|
||||
log::info!("{}: initialize", self.name);
|
||||
let regs = self.regs.write();
|
||||
regs.C.set(0);
|
||||
|
||||
let index = DEVICE_REGISTRY.i2c.register_bus(self.clone())?;
|
||||
self.index.init(index);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
|
||||
// TODO
|
||||
let _ = &self.irq;
|
||||
// self.irq.register(self.clone())?;
|
||||
// self.irq.enable()?;
|
||||
// let regs = self.regs.write();
|
||||
// regs.C.modify(C::INTD::SET);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptHandler for I2C {
|
||||
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl I2CController for I2C {
|
||||
fn bus_number(&self) -> u32 {
|
||||
*self.index.get()
|
||||
}
|
||||
|
||||
fn child_number(&self) -> Option<u32> {
|
||||
None
|
||||
}
|
||||
|
||||
fn set_speed(&self, speed: Hertz) -> Result<Hertz, Error> {
|
||||
if let Some(frequency) = self.clock_frequency {
|
||||
// Fixed frequency
|
||||
if speed == frequency {
|
||||
Ok(speed)
|
||||
} else {
|
||||
Err(Error::InvalidArgument)
|
||||
}
|
||||
} else {
|
||||
todo!("Set i2c speed = {speed}")
|
||||
}
|
||||
}
|
||||
|
||||
fn capabilities(&self) -> I2CMasterInfo {
|
||||
let max_speed_hz = if let Some(frequency) = self.clock_frequency {
|
||||
frequency
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
.0 as u32;
|
||||
I2CMasterInfo {
|
||||
max_speed_hz,
|
||||
supports_10bit_addresses: true,
|
||||
}
|
||||
}
|
||||
|
||||
fn i2c_write(&self, address: I2CAddress, buffer: &[u8]) -> Result<usize, Error> {
|
||||
let buflen: u16 = buffer
|
||||
.len()
|
||||
.try_into()
|
||||
.map_err(|_| Error::InvalidArgument)?;
|
||||
let regs = self.regs.write();
|
||||
// TODO DMA/interrupts
|
||||
regs.start_transfer(self.name, buflen, address, false);
|
||||
let mut bytes_written = 0;
|
||||
// let mut done = false;
|
||||
for &byte in buffer {
|
||||
if regs.write_byte(byte)? {
|
||||
bytes_written += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
regs.finish_transfer(self.name)?;
|
||||
Ok(bytes_written)
|
||||
}
|
||||
|
||||
fn i2c_read(&self, address: I2CAddress, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
let buflen: u16 = buffer
|
||||
.len()
|
||||
.try_into()
|
||||
.map_err(|_| Error::InvalidArgument)?;
|
||||
let regs = self.regs.write();
|
||||
// TODO DMA/interrupts
|
||||
regs.start_transfer(self.name, buflen, address, true);
|
||||
let mut bytes_read = 0;
|
||||
// let mut done = false;
|
||||
for byte in buffer {
|
||||
if let Some(val) = regs.read_byte()? {
|
||||
*byte = val;
|
||||
bytes_read += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
regs.finish_transfer(self.name)?;
|
||||
Ok(bytes_read)
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["brcm,bcm2711-i2c", "brcm,bcm2835-i2c"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let clock_frequency = node.prop_usize("clock-frequency").map(|p| Hertz(p as u64));
|
||||
let base = node.map_base(context, 0)?;
|
||||
let name = node.name().unwrap_or("bcm2835-i2c");
|
||||
let irq = node.interrupt(0)?;
|
||||
let clock = node.clock(0);
|
||||
|
||||
let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
|
||||
|
||||
let i2c = Arc::new(I2C {
|
||||
name,
|
||||
clock_frequency,
|
||||
irq,
|
||||
clock,
|
||||
index: OneTimeInit::new(),
|
||||
regs: IrqSafeRwLock::new(regs)
|
||||
});
|
||||
|
||||
node.make_i2c_controller(i2c.clone());
|
||||
|
||||
Some(i2c)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,5 +4,7 @@ extern crate alloc;
|
||||
|
||||
mod aux;
|
||||
// mod aux_uart;
|
||||
mod cprman;
|
||||
mod gpio;
|
||||
mod i2c;
|
||||
mod mbox;
|
||||
|
||||
@@ -26,6 +26,18 @@ pub trait IntoHertz {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for Hertz {
|
||||
fn from(value: u32) -> Self {
|
||||
Self(value as u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for Hertz {
|
||||
fn from(value: u64) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<u32> for Hertz {
|
||||
type Output = Hertz;
|
||||
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
use core::{
|
||||
fmt,
|
||||
ops::Deref,
|
||||
sync::atomic::{AtomicU16, AtomicU32, Ordering},
|
||||
};
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use yggdrasil_abi::{error::Error, io::device::i2c::I2CMasterInfo, process::ProcessId};
|
||||
|
||||
use crate::{
|
||||
clock::Hertz,
|
||||
device::{Device, DeviceInitContext},
|
||||
};
|
||||
|
||||
pub struct I2CAddress(u16);
|
||||
|
||||
pub struct I2CDevice {
|
||||
device: Arc<dyn I2CController>,
|
||||
address: AtomicU16,
|
||||
user: AtomicU32,
|
||||
}
|
||||
|
||||
pub trait I2CController: Device {
|
||||
fn bus_number(&self) -> u32;
|
||||
fn child_number(&self) -> Option<u32>;
|
||||
|
||||
fn set_speed(&self, speed: Hertz) -> Result<Hertz, Error>;
|
||||
fn capabilities(&self) -> I2CMasterInfo;
|
||||
|
||||
fn i2c_write(&self, address: I2CAddress, buffer: &[u8]) -> Result<usize, Error>;
|
||||
fn i2c_read(&self, address: I2CAddress, buffer: &mut [u8]) -> Result<usize, Error>;
|
||||
}
|
||||
|
||||
impl I2CAddress {
|
||||
pub fn as_8_bit(&self) -> Option<u8> {
|
||||
self.0.try_into().ok()
|
||||
}
|
||||
|
||||
pub fn as_10_bit(&self) -> u16 {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn is_8_bit(&self) -> bool {
|
||||
self.0 <= 0xFF
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for I2CAddress {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for I2CAddress {
|
||||
fn from(value: u8) -> Self {
|
||||
Self(value as u16)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u16> for I2CAddress {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: u16) -> Result<Self, Self::Error> {
|
||||
if value >= (1 << 10) {
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
Ok(Self(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for I2CDevice {
|
||||
type Target = dyn I2CController;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&*self.device
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Arc<dyn I2CController>> for I2CDevice {
|
||||
fn from(value: Arc<dyn I2CController>) -> Self {
|
||||
Self {
|
||||
device: value,
|
||||
address: AtomicU16::new(0),
|
||||
user: AtomicU32::new(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl I2CDevice {
|
||||
pub fn set_slave_address(&self, address: I2CAddress) {
|
||||
self.address.store(address.0, Ordering::Release);
|
||||
}
|
||||
|
||||
pub fn set_speed(&self, speed: Hertz) -> Result<Hertz, Error> {
|
||||
self.device.set_speed(speed)
|
||||
}
|
||||
|
||||
pub fn slave_address(&self) -> I2CAddress {
|
||||
I2CAddress(self.address.load(Ordering::Acquire))
|
||||
}
|
||||
|
||||
pub fn check_lock(&self, id: ProcessId) -> Result<(), Error> {
|
||||
if self.user.load(Ordering::Acquire) == id.bits() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::PermissionDenied)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lock(&self, process: ProcessId) -> Result<(), Error> {
|
||||
if self
|
||||
.user
|
||||
.compare_exchange(0, process.bits(), Ordering::Acquire, Ordering::Relaxed)
|
||||
.is_ok()
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::WouldBlock)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn release(&self, process: ProcessId) -> Result<(), Error> {
|
||||
if self
|
||||
.user
|
||||
.compare_exchange(process.bits(), 0, Ordering::Release, Ordering::Relaxed)
|
||||
.is_ok()
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::InvalidOperation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for I2CDevice {
|
||||
unsafe fn init(self: Arc<Self>, cx: DeviceInitContext) -> Result<(), Error> {
|
||||
self.device.clone().init(cx)
|
||||
}
|
||||
|
||||
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
|
||||
self.device.clone().init_irq()
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
self.device.display_name()
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ pub mod bus;
|
||||
pub mod clock;
|
||||
pub mod device;
|
||||
pub mod gpio;
|
||||
pub mod i2c;
|
||||
pub mod interrupt;
|
||||
pub mod serial;
|
||||
pub mod timer;
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
/// `.init_array`-based mechanism.
|
||||
pub macro device_tree_driver(
|
||||
compatible: [$($compatible:literal),+ $(,)?],
|
||||
$(config: {
|
||||
$($config_field:ident : $config_value:expr),+
|
||||
$(,)?
|
||||
},)?
|
||||
driver: $driver:tt
|
||||
) {
|
||||
struct __DtDriver;
|
||||
@@ -21,8 +25,15 @@ pub macro device_tree_driver(
|
||||
static __REGISTER_FN: extern "C" fn() = __register_fn;
|
||||
|
||||
extern "C" fn __register_fn() {
|
||||
let config = $crate::driver::DriverConfig {
|
||||
$(
|
||||
$($config_field: $config_value,)+
|
||||
)?
|
||||
..Default::default()
|
||||
};
|
||||
$crate::driver::register_driver(
|
||||
&[$($compatible),+],
|
||||
config,
|
||||
&__DT_DRIVER
|
||||
)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ pub mod util;
|
||||
|
||||
pub use controller::{map_interrupt, map_interrupt_at};
|
||||
pub use macros::device_tree_driver;
|
||||
pub use registry::{lookup_phandle, register_driver};
|
||||
pub use registry::{DriverConfig, lookup_phandle, register_driver};
|
||||
pub use syscon::DeviceTreeSyscon;
|
||||
pub use traits::{
|
||||
DeviceTreeClockController, DeviceTreeInterruptController, DeviceTreePinController,
|
||||
|
||||
@@ -7,8 +7,22 @@ use crate::DeviceTree;
|
||||
|
||||
use super::{Driver, Node};
|
||||
|
||||
/// Device tree driver configuration
|
||||
#[derive(Debug)]
|
||||
pub struct DriverConfig {
|
||||
/// If `false`, pinctrl setup for the node needs to be performed manually
|
||||
pub auto_pinctrl: bool,
|
||||
}
|
||||
|
||||
impl Default for DriverConfig {
|
||||
fn default() -> Self {
|
||||
Self { auto_pinctrl: true }
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct DriverRegistration {
|
||||
pub(crate) compatible: &'static [&'static str],
|
||||
pub(crate) config: DriverConfig,
|
||||
pub(crate) imp: &'static dyn Driver,
|
||||
}
|
||||
|
||||
@@ -25,9 +39,14 @@ impl DriverRegistration {
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn register_driver(compatible: &'static [&'static str], driver: &'static dyn Driver) {
|
||||
pub fn register_driver(
|
||||
compatible: &'static [&'static str],
|
||||
config: DriverConfig,
|
||||
driver: &'static dyn Driver,
|
||||
) {
|
||||
DRIVERS.write().push(DriverRegistration {
|
||||
compatible,
|
||||
config,
|
||||
imp: driver,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use device_api::{
|
||||
clock::{ClockController, ClockHandle, ResetHandle},
|
||||
device::{Device, DeviceInitContext},
|
||||
gpio::PinHandle,
|
||||
i2c::I2CController,
|
||||
interrupt::{ExternalInterruptController, IrqHandle, MessageInterruptController},
|
||||
};
|
||||
use fdt_rs::spec::Phandle;
|
||||
@@ -61,6 +62,7 @@ pub struct Node {
|
||||
pub(crate) reset_controller: OneTimeInit<Arc<dyn DeviceTreeResetController>>,
|
||||
pub(crate) system_controller: OneTimeInit<Arc<dyn DeviceTreeSyscon>>,
|
||||
pub(crate) pin_controller: OneTimeInit<Arc<dyn DeviceTreePinController>>,
|
||||
pub(crate) i2c_controller: OneTimeInit<Arc<dyn I2CController>>,
|
||||
}
|
||||
|
||||
pub(crate) struct ProbedDevice {
|
||||
@@ -105,24 +107,26 @@ impl Node {
|
||||
.as_str_list()
|
||||
.find_map(|c| drivers.iter().find(|d| d.matches(c)));
|
||||
|
||||
// if libk::config::get().device_tree.log_missing && driver.is_none() {
|
||||
// // FIXME don't spam virtio missing stuff
|
||||
// if !name.is_some_and(|n| n.starts_with("virtio_mmio")) {
|
||||
// for (i, compatible) in compatible.as_str_list().enumerate() {
|
||||
// if i == 0 {
|
||||
// log::warn!("No driver for {name:?} ({compatible:?})");
|
||||
// } else {
|
||||
// log::warn!(" also {compatible:?}");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
if libk::config::get().device_tree.log_missing && driver.is_none() {
|
||||
// FIXME don't spam virtio missing stuff
|
||||
if !name.is_some_and(|n| n.starts_with("virtio_mmio")) {
|
||||
for (i, compatible) in compatible.as_str_list().enumerate() {
|
||||
if i == 0 {
|
||||
log::warn!("No driver for {name:?} ({compatible:?})");
|
||||
} else {
|
||||
log::warn!(" also {compatible:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let driver = driver?;
|
||||
|
||||
// Initialize default pinctrl before probing
|
||||
node.setup_pins()
|
||||
.inspect_err(|e| log::error!("{name:?}: pinctrl init error {e:?}"))
|
||||
.ok()?;
|
||||
if driver.config.auto_pinctrl {
|
||||
node.setup_pins(0)
|
||||
.inspect_err(|e| log::error!("{name:?}: pinctrl init error {e:?}"))
|
||||
.ok()?;
|
||||
}
|
||||
|
||||
let device = driver.imp.probe(node, cx);
|
||||
|
||||
@@ -198,6 +202,11 @@ impl Node {
|
||||
self.interrupt_controller.init(intc);
|
||||
}
|
||||
|
||||
/// Makes node an I²C controller node
|
||||
pub fn make_i2c_controller(&self, i2c: Arc<dyn I2CController>) {
|
||||
self.i2c_controller.init(i2c);
|
||||
}
|
||||
|
||||
/// When called from an msi-controller driver, informs the node of its capability as an MSI
|
||||
/// controller.
|
||||
pub fn make_msi_controller(&self, intc: Arc<dyn MessageInterruptController>) {
|
||||
@@ -451,6 +460,11 @@ impl Node {
|
||||
self.pin_controller.try_get().cloned()
|
||||
}
|
||||
|
||||
/// Attempts to get an I2C controller represented by this node, if any
|
||||
pub fn as_i2c_controller(&self) -> Option<Arc<dyn I2CController>> {
|
||||
self.i2c_controller.try_get().cloned()
|
||||
}
|
||||
|
||||
/// Returns the `#address-cells` value of the node's parent bus
|
||||
pub fn bus_address_cells(&self) -> usize {
|
||||
self.bus_address_cells
|
||||
@@ -527,9 +541,11 @@ impl Node {
|
||||
tree::dump(self.dt_node.clone(), 0);
|
||||
}
|
||||
|
||||
fn setup_pins(&self) -> Result<(), Error> {
|
||||
/// Configure the `index`th pinctrl mode for the peripheral node
|
||||
pub fn setup_pins(&self, index: usize) -> Result<(), Error> {
|
||||
// TODO lookup pin state by name
|
||||
let Some(pinctrl0) = self.prop_usize("pinctrl-0") else {
|
||||
let field_name = alloc::format!("pinctrl-{index}");
|
||||
let Some(pinctrl0) = self.prop_usize(&field_name) else {
|
||||
return Ok(());
|
||||
};
|
||||
let pinctrl0 = lookup_phandle(pinctrl0 as Phandle, false).ok_or(Error::DoesNotExist)?;
|
||||
@@ -596,6 +612,7 @@ fn unflatten_node(
|
||||
reset_controller: OneTimeInit::new(),
|
||||
system_controller: OneTimeInit::new(),
|
||||
pin_controller: OneTimeInit::new(),
|
||||
i2c_controller: OneTimeInit::new(),
|
||||
});
|
||||
|
||||
if let Some(phandle) = phandle {
|
||||
|
||||
@@ -1,11 +1,27 @@
|
||||
use core::{any::Any, ops::Deref};
|
||||
use core::{
|
||||
any::Any,
|
||||
ops::Deref,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use alloc::{boxed::Box, sync::Arc};
|
||||
use async_trait::async_trait;
|
||||
use device_api::device::Device;
|
||||
use yggdrasil_abi::error::Error;
|
||||
use device_api::{
|
||||
clock::Hertz,
|
||||
device::Device,
|
||||
i2c::{I2CAddress, I2CDevice},
|
||||
};
|
||||
use yggdrasil_abi::{
|
||||
error::Error,
|
||||
io::device::i2c::{self, I2CRequestVariant},
|
||||
option::RequestValue,
|
||||
process::ProcessId,
|
||||
};
|
||||
|
||||
use crate::vfs::{CommonImpl, FileReadiness, NodeRef};
|
||||
use crate::{
|
||||
task::thread::Thread,
|
||||
vfs::{CommonImpl, FileReadiness, NodeRef},
|
||||
};
|
||||
|
||||
#[async_trait]
|
||||
pub trait CharDevice: Device + FileReadiness {
|
||||
@@ -25,6 +41,15 @@ pub trait CharDevice: Device + FileReadiness {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn lock(&self, process: ProcessId) -> Result<(), Error> {
|
||||
let _ = process;
|
||||
Ok(())
|
||||
}
|
||||
fn release(&self, process: ProcessId) -> Result<(), Error> {
|
||||
let _ = process;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_readable(&self) -> bool {
|
||||
true
|
||||
}
|
||||
@@ -65,3 +90,63 @@ impl Deref for CharDeviceFile {
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO timeouts and better access
|
||||
#[async_trait]
|
||||
impl CharDevice for I2CDevice {
|
||||
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> {
|
||||
let pid = Thread::current().process_id();
|
||||
self.check_lock(pid)?;
|
||||
self.i2c_read(self.slave_address(), buffer)
|
||||
}
|
||||
fn write_nonblocking(&self, buffer: &[u8]) -> Result<usize, Error> {
|
||||
let pid = Thread::current().process_id();
|
||||
self.check_lock(pid)?;
|
||||
self.i2c_write(self.slave_address(), buffer)
|
||||
}
|
||||
fn device_request(&self, option: u32, buffer: &mut [u8], len: usize) -> Result<usize, Error> {
|
||||
if let Ok(req) = I2CRequestVariant::try_from(option) {
|
||||
match req {
|
||||
I2CRequestVariant::SetAddress => {
|
||||
let address = i2c::SetAddress::load_request(&buffer[..len])?;
|
||||
let address = I2CAddress::try_from(address)?;
|
||||
self.set_slave_address(address);
|
||||
Ok(0)
|
||||
}
|
||||
I2CRequestVariant::SetSpeed => {
|
||||
let speed = i2c::SetSpeed::load_request(&buffer[..len])?;
|
||||
let speed = Hertz::from(speed);
|
||||
let actual = self.set_speed(speed)?.0 as u32;
|
||||
let len = i2c::SetSpeed::store_response(&actual, buffer)?;
|
||||
Ok(len)
|
||||
}
|
||||
I2CRequestVariant::GetMasterInfo => {
|
||||
let info = self.capabilities();
|
||||
let len = i2c::GetMasterInfo::store_response(&info, buffer)?;
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(Error::InvalidArgument)
|
||||
}
|
||||
}
|
||||
|
||||
fn lock(&self, process: ProcessId) -> Result<(), Error> {
|
||||
I2CDevice::lock(self, process)
|
||||
}
|
||||
fn release(&self, process: ProcessId) -> Result<(), Error> {
|
||||
I2CDevice::release(self, process)
|
||||
}
|
||||
}
|
||||
impl FileReadiness for I2CDevice {
|
||||
fn poll_read(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
|
||||
let _ = cx;
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ use core::{
|
||||
};
|
||||
|
||||
use alloc::{collections::BTreeMap, format, sync::Arc, vec::Vec};
|
||||
use device_api::i2c::{I2CController, I2CDevice};
|
||||
use libk_util::{OneTimeInit, sync::spin_rwlock::IrqSafeRwLock};
|
||||
use yggdrasil_abi::{error::Error, io::FileMode};
|
||||
|
||||
@@ -46,16 +47,23 @@ pub struct SerialTerminalRegistry {
|
||||
registry: GenericRegistry<Arc<dyn CharDevice>>,
|
||||
}
|
||||
|
||||
// Manages devices: i2c<N>, i2c<N>-<M>
|
||||
pub struct I2CDeviceRegistry {
|
||||
registry: GenericRegistry<Arc<I2CDevice>>,
|
||||
}
|
||||
|
||||
pub struct DeviceRegistry {
|
||||
pub display: DisplayDeviceRegistry,
|
||||
pub terminal: TerminalRegistry,
|
||||
pub serial_terminal: SerialTerminalRegistry,
|
||||
pub i2c: I2CDeviceRegistry,
|
||||
}
|
||||
|
||||
pub static DEVICE_REGISTRY: DeviceRegistry = DeviceRegistry {
|
||||
display: DisplayDeviceRegistry::new(),
|
||||
terminal: TerminalRegistry::new(),
|
||||
serial_terminal: SerialTerminalRegistry::new(),
|
||||
i2c: I2CDeviceRegistry::new(),
|
||||
};
|
||||
|
||||
impl TerminalRegistry {
|
||||
@@ -117,6 +125,22 @@ impl DisplayDeviceRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
impl I2CDeviceRegistry {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
registry: GenericRegistry::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_bus(&self, device: Arc<dyn I2CController>) -> Result<u32, Error> {
|
||||
let wrapper = Arc::new(I2CDevice::from(device));
|
||||
let id = self.registry.register(wrapper.clone())?;
|
||||
let name = format!("i2c{id}");
|
||||
devfs::add_named_char_device(wrapper.clone(), &name, FileMode::new(0o600))?;
|
||||
Ok(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> GenericRegistry<T> {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use libk_util::sync::IrqSafeSpinlock;
|
||||
use yggdrasil_abi::{error::Error, io::SeekFrom};
|
||||
use yggdrasil_abi::{error::Error, io::SeekFrom, process::ProcessId};
|
||||
|
||||
use crate::{
|
||||
device::{block::BlockDeviceFile, char::CharDeviceFile},
|
||||
@@ -20,6 +20,7 @@ pub struct CharFile {
|
||||
pub(super) node: NodeRef,
|
||||
pub(super) read: bool,
|
||||
pub(super) write: bool,
|
||||
pub(super) pid: ProcessId,
|
||||
}
|
||||
|
||||
impl BlockFile {
|
||||
@@ -103,3 +104,10 @@ impl CharFile {
|
||||
self.device.0.is_terminal()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for CharFile {
|
||||
fn drop(&mut self) {
|
||||
// TODO doesn't work with fork
|
||||
self.device.release(self.pid).ok();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ use yggdrasil_abi::{
|
||||
|
||||
use crate::{
|
||||
device::{block::BlockDeviceFile, char::CharDeviceFile},
|
||||
task::process::Process,
|
||||
task::{process::Process, thread::Thread},
|
||||
vfs::{
|
||||
FdPoll, FileReadiness, Node, SharedMemory, TimerFile,
|
||||
node::NodeRef,
|
||||
@@ -254,6 +254,10 @@ impl File {
|
||||
node: NodeRef,
|
||||
opts: OpenOptions,
|
||||
) -> Result<Arc<Self>, Error> {
|
||||
let pid = Thread::current().process_id();
|
||||
|
||||
device.lock(pid)?;
|
||||
|
||||
let read = opts.contains(OpenOptions::READ);
|
||||
let write = opts.contains(OpenOptions::WRITE);
|
||||
|
||||
@@ -269,6 +273,7 @@ impl File {
|
||||
node,
|
||||
read,
|
||||
write,
|
||||
pid,
|
||||
})))
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use abi::{
|
||||
error::Error,
|
||||
io::{FileMode, device::i2c::I2CMasterInfo},
|
||||
};
|
||||
use alloc::{string::String, sync::Arc};
|
||||
use device_api::{
|
||||
clock::Hertz,
|
||||
device::{Device, DeviceInitContext},
|
||||
i2c::{I2CAddress, I2CController, I2CDevice},
|
||||
};
|
||||
use device_tree::driver::{Node, ProbeContext, device_tree_driver, lookup_phandle};
|
||||
use libk::fs::devfs;
|
||||
|
||||
pub struct I2CMuxChild {
|
||||
name: String,
|
||||
channel: u32,
|
||||
parent: Arc<dyn I2CController>,
|
||||
}
|
||||
|
||||
pub struct I2CMux {
|
||||
name: &'static str,
|
||||
}
|
||||
|
||||
impl I2CMux {
|
||||
pub fn from_fdt(node: &Arc<Node>, _context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let i2c_parent = node.prop_usize("i2c-parent")? as u32;
|
||||
let i2c_parent = lookup_phandle(i2c_parent, true)?;
|
||||
let i2c_parent = i2c_parent.as_i2c_controller()?;
|
||||
let name = node.name().unwrap_or("i2c-mux-pinctrl");
|
||||
|
||||
let bus_index = i2c_parent.bus_number();
|
||||
|
||||
for (index, child) in node.children().enumerate() {
|
||||
node.setup_pins(index).unwrap();
|
||||
let channel_index = child.prop_usize("reg")? as u32;
|
||||
let name = alloc::format!("{name}@{channel_index}");
|
||||
let channel: Arc<dyn I2CController> = Arc::new(I2CMuxChild {
|
||||
name,
|
||||
channel: channel_index,
|
||||
parent: i2c_parent.clone(),
|
||||
});
|
||||
|
||||
let device = Arc::new(I2CDevice::from(channel));
|
||||
let fs_name = alloc::format!("i2c{bus_index}-{channel_index}");
|
||||
devfs::add_named_char_device(device, fs_name, FileMode::new(0o600)).ok();
|
||||
}
|
||||
|
||||
Some(Arc::new(I2CMux { name }))
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for I2CMux {
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for I2CMuxChild {
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl I2CController for I2CMuxChild {
|
||||
fn bus_number(&self) -> u32 {
|
||||
self.parent.bus_number()
|
||||
}
|
||||
|
||||
fn child_number(&self) -> Option<u32> {
|
||||
Some(self.channel)
|
||||
}
|
||||
|
||||
fn set_speed(&self, speed: Hertz) -> Result<Hertz, Error> {
|
||||
self.parent.set_speed(speed)
|
||||
}
|
||||
|
||||
fn capabilities(&self) -> I2CMasterInfo {
|
||||
self.parent.capabilities()
|
||||
}
|
||||
|
||||
fn i2c_write(&self, address: I2CAddress, buffer: &[u8]) -> Result<usize, Error> {
|
||||
// TODO channel select?
|
||||
self.parent.i2c_write(address, buffer)
|
||||
}
|
||||
|
||||
fn i2c_read(&self, address: I2CAddress, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
// TODO channel select?
|
||||
self.parent.i2c_read(address, buffer)
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["i2c-mux-pinctrl"],
|
||||
config: {
|
||||
auto_pinctrl: false,
|
||||
},
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
I2CMux::from_fdt(node, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ use libk_util::OneTimeInit;
|
||||
pub mod bus;
|
||||
pub mod clock;
|
||||
pub mod display;
|
||||
pub mod i2c;
|
||||
pub mod power;
|
||||
// pub mod timer;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user