i2c: implement initial support for i2c devices
This commit is contained in:
@@ -134,23 +134,57 @@
|
||||
pinctrl-names = "default";
|
||||
bootph-all;
|
||||
|
||||
// UART
|
||||
uart0_ctsrts_gpio30: uart0_ctsrts_gpio30 {
|
||||
brcm,pins = <30>, <31>;
|
||||
brcm,pull = <2>, <0>;
|
||||
brcm,function = <7>;
|
||||
};
|
||||
|
||||
uart0_gpio32: uart0_gpio32 {
|
||||
brcm,pins = <32>, <33>;
|
||||
brcm,pull = <0>, <2>;
|
||||
brcm,function = <8>;
|
||||
};
|
||||
|
||||
uart1_gpio14: uart1_gpio14 {
|
||||
brcm,pins = <14>, <15>;
|
||||
brcm,function = <2>;
|
||||
bootph-all;
|
||||
};
|
||||
|
||||
// I²C
|
||||
i2c0if_gpio0: i2c0if-gpio0 {
|
||||
brcm,pins = <0>, <1>;
|
||||
brcm,function = <4>;
|
||||
};
|
||||
i2c0if_gpio44: i2c0if-gpio44 {
|
||||
brcm,pins = <44>, <45>;
|
||||
brcm,function = <5>;
|
||||
};
|
||||
i2c1_gpio: i2c1 {
|
||||
brcm,pins = <2>, <3>;
|
||||
brcm,function = <4>;
|
||||
brcm,pull = <2>;
|
||||
};
|
||||
i2c3_gpio: i2c3 {
|
||||
brcm,pins = <4>, <5>;
|
||||
brcm,function = <2>;
|
||||
brcm,pull = <2>;
|
||||
};
|
||||
i2c4_gpio: i2c4 {
|
||||
brcm,pins = <8>, <9>;
|
||||
brcm,function = <2>;
|
||||
brcm,pull = <2>;
|
||||
};
|
||||
i2c5_gpio: i2c5 {
|
||||
brcm,pins = <12>, <13>;
|
||||
brcm,function = <2>;
|
||||
brcm,pull = <2>;
|
||||
};
|
||||
i2c6_gpio: i2c6 {
|
||||
brcm,pins = <22>, <23>;
|
||||
brcm,function = <2>;
|
||||
brcm,pull = <2>;
|
||||
};
|
||||
};
|
||||
|
||||
uart0: serial@7e201000 {
|
||||
@@ -163,7 +197,7 @@
|
||||
"apb_pclk";
|
||||
arm,primecell-periphid = <0x241011>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&uart0_ctsrts_gpio30 &uart0_gpio32>;
|
||||
pinctrl-0 = <&uart0_ctsrts_gpio30>, <&uart0_gpio32>;
|
||||
uart-has-rtscts;
|
||||
status = "okay";
|
||||
skip-init;
|
||||
@@ -189,6 +223,100 @@
|
||||
bootph-all;
|
||||
};
|
||||
|
||||
i2c0if: i2c@7e205000 {
|
||||
compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
|
||||
reg = <0x7e205000 0x200>;
|
||||
interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&cprman 0x14>;
|
||||
#address-cells = <0x01>;
|
||||
#size-cells = <0x00>;
|
||||
status = "okay";
|
||||
clock-frequency = <0x186a0>;
|
||||
};
|
||||
|
||||
i2c0mux: i2c0mux {
|
||||
compatible = "i2c-mux-pinctrl";
|
||||
#address-cells = <0x01>;
|
||||
#size-cells = <0x00>;
|
||||
i2c-parent = <&i2c0if>;
|
||||
status = "okay";
|
||||
pinctrl-names = "i2c0", "i2c_csi_dsi";
|
||||
pinctrl-0 = <&i2c0if_gpio0>;
|
||||
pinctrl-1 = <&i2c0if_gpio44>;
|
||||
|
||||
i2c0: i2c@0 {
|
||||
reg = <0x00>;
|
||||
#address-cells = <0x01>;
|
||||
#size-cells = <0x00>;
|
||||
};
|
||||
|
||||
i2c_csi_dsi: i2c@1 {
|
||||
reg = <0x01>;
|
||||
#address-cells = <0x01>;
|
||||
#size-cells = <0x00>;
|
||||
};
|
||||
};
|
||||
i2c1: i2c@7e804000 {
|
||||
compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
|
||||
reg = <0x7e804000 0x1000>;
|
||||
interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&cprman 0x14>;
|
||||
#address-cells = <0x01>;
|
||||
#size-cells = <0x00>;
|
||||
status = "okay";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&i2c1_gpio>;
|
||||
clock-frequency = <0x186a0>;
|
||||
};
|
||||
// TODO: memory access crashes on qemu (not implemented?)
|
||||
i2c3: i2c@7e205600 {
|
||||
compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
|
||||
reg = <0x7e205600 0x200>;
|
||||
interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&cprman 0x14>;
|
||||
#address-cells = <0x01>;
|
||||
#size-cells = <0x00>;
|
||||
status = "disabled";
|
||||
pinctrl-0 = <&i2c3_gpio>;
|
||||
pinctrl-names = "default";
|
||||
};
|
||||
// TODO: memory access crashes on qemu (not implemented?)
|
||||
i2c4: i2c@7e205800 {
|
||||
compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
|
||||
reg = <0x7e205800 0x200>;
|
||||
interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&cprman 0x14>;
|
||||
#address-cells = <0x01>;
|
||||
#size-cells = <0x00>;
|
||||
status = "disabled";
|
||||
pinctrl-0 = <&i2c4_gpio>;
|
||||
pinctrl-names = "default";
|
||||
};
|
||||
// TODO: memory access crashes on qemu (not implemented?)
|
||||
i2c5: i2c@7e205a00 {
|
||||
compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
|
||||
reg = <0x7e205a00 0x200>;
|
||||
interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&cprman 0x14>;
|
||||
#address-cells = <0x01>;
|
||||
#size-cells = <0x00>;
|
||||
status = "disabled";
|
||||
pinctrl-0 = <&i2c5_gpio>;
|
||||
pinctrl-names = "default";
|
||||
};
|
||||
// TODO: memory access crashes on qemu (not implemented?)
|
||||
i2c6: i2c@7e205c00 {
|
||||
compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
|
||||
reg = <0x7e205c00 0x200>;
|
||||
interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&cprman 0x14>;
|
||||
#address-cells = <0x01>;
|
||||
#size-cells = <0x00>;
|
||||
status = "disabled";
|
||||
pinctrl-0 = <&i2c6_gpio>;
|
||||
pinctrl-names = "default";
|
||||
};
|
||||
|
||||
l1_intc: local_intc@40000000 {
|
||||
compatible = "brcm,bcm2836-l1-intc";
|
||||
reg = <0x40000000 0x100>;
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
|
||||
|
||||
@@ -39,6 +39,34 @@ impl_primitive_serde!(
|
||||
usize: [read_usize, write_usize]
|
||||
);
|
||||
|
||||
impl<T> Serialize for *const T {
|
||||
fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
|
||||
serializer.write_usize(self.addr())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Serialize for *mut T {
|
||||
fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
|
||||
serializer.write_usize(self.addr())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T: 'de> Deserialize<'de> for *const T {
|
||||
fn deserialize<D: Deserializer<'de>>(deserializer: &mut D) -> Result<Self, D::Error> {
|
||||
deserializer
|
||||
.read_usize()
|
||||
.map(core::ptr::with_exposed_provenance)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T: 'de> Deserialize<'de> for *mut T {
|
||||
fn deserialize<D: Deserializer<'de>>(deserializer: &mut D) -> Result<Self, D::Error> {
|
||||
deserializer
|
||||
.read_usize()
|
||||
.map(core::ptr::with_exposed_provenance_mut)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for () {
|
||||
fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
|
||||
let _ = serializer;
|
||||
|
||||
@@ -55,6 +55,90 @@ request_group!(
|
||||
}
|
||||
);
|
||||
|
||||
/// I²C device requests
|
||||
pub mod i2c {
|
||||
/// I²C master device information
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct I2CMasterInfo {
|
||||
/// Maximum clock rate supported by the controller
|
||||
pub max_speed_hz: u32,
|
||||
/// `true` if the controller supports 10-bit addressing mode
|
||||
pub supports_10bit_addresses: bool,
|
||||
}
|
||||
|
||||
request_group!(
|
||||
#[doc = "I²C device requests"]
|
||||
pub enum I2CRequestVariant<'de> {
|
||||
#[doc = "Selects the address on the I²C bus"]
|
||||
0x2000: SetAddress(u16, ()),
|
||||
#[doc = "Configures device speed (in Hertz)"]
|
||||
0x2001: SetSpeed(u32, u32),
|
||||
#[doc = "Queries I²C master capabilities"]
|
||||
0x2010: GetMasterInfo((), I2CMasterInfo),
|
||||
}
|
||||
);
|
||||
|
||||
abi_serde::impl_struct_serde!(I2CMasterInfo: [
|
||||
max_speed_hz, supports_10bit_addresses
|
||||
]);
|
||||
}
|
||||
|
||||
/// SPI device requests
|
||||
pub mod spi {
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use crate::bitflags;
|
||||
|
||||
/// Represents a single SPI transfer
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct SpiTransfer {
|
||||
/// Tx buffer base pointer
|
||||
pub tx_buffer: *const u8,
|
||||
/// Rx buffer base pointer
|
||||
pub rx_buffer: *mut u8,
|
||||
/// Transfer size
|
||||
pub len: usize,
|
||||
/// SPI clock rate
|
||||
pub speed_hz: Option<u32>,
|
||||
/// Bits per SPI word
|
||||
pub bits_per_word: u8,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[doc = "SPI supported bits-per-word settings"]
|
||||
pub struct SpiSupportedBitsPerWord: u32 {
|
||||
#[doc = "8-bit words support"]
|
||||
const BITS_8: bit 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// SPI controller capabilities
|
||||
pub struct SpiCapabilities {
|
||||
/// Maximum clock rate supported
|
||||
pub max_speed_hz: u32,
|
||||
/// Supports SPI master mode
|
||||
pub supports_master_mode: bool,
|
||||
/// Supports SPI slave mode
|
||||
pub supports_slave_mode: bool,
|
||||
/// Mask
|
||||
pub supported_bits_per_word: u32,
|
||||
}
|
||||
|
||||
request_group!(
|
||||
#[doc = "SPI device requests"]
|
||||
pub enum SpiRequestVariant<'de> {
|
||||
#[doc = "Perform a SPI transfer"]
|
||||
0x2000: Transfer(SpiTransfer, usize)
|
||||
}
|
||||
);
|
||||
|
||||
abi_serde::impl_struct_serde!(SpiTransfer: [
|
||||
tx_buffer, rx_buffer, len, speed_hz, bits_per_word
|
||||
]);
|
||||
}
|
||||
|
||||
abi_serde::impl_struct_serde!(Framebuffer: [
|
||||
width, height, stride, size
|
||||
]);
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
use std::{
|
||||
io::{self, Read, Write},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use crate::sys::{I2CMaster as SysI2CMaster, i2c::I2CMasterImpl};
|
||||
|
||||
pub use crate::sys::I2CMasterConfig;
|
||||
|
||||
pub struct I2CMaster(I2CMasterImpl);
|
||||
|
||||
impl Read for I2CMaster {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for I2CMaster {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.0.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.0.flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl I2CMaster {
|
||||
pub fn open<P: AsRef<Path>>(device: P, config: &I2CMasterConfig) -> io::Result<Self> {
|
||||
I2CMasterImpl::open(device, config).map(Self)
|
||||
}
|
||||
|
||||
pub fn set_slave_address(&mut self, address: u16) -> io::Result<()> {
|
||||
self.0.set_slave_address(address)
|
||||
}
|
||||
|
||||
pub fn set_speed_hz(&mut self, speed_hz: u32) -> io::Result<()> {
|
||||
self.0.set_speed_hz(speed_hz)
|
||||
}
|
||||
|
||||
pub fn smbus_read(&mut self, reg: u8, data: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.smbus_read(reg, data)
|
||||
}
|
||||
|
||||
pub fn smbus_write(&mut self, reg: u8, data: &[u8]) -> io::Result<usize> {
|
||||
self.0.smbus_write(reg, data)
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
pub(crate) mod sys;
|
||||
|
||||
pub mod fs;
|
||||
pub mod i2c;
|
||||
pub mod io;
|
||||
pub mod mem;
|
||||
pub mod net;
|
||||
|
||||
@@ -64,6 +64,20 @@ pub(crate) trait SerialPort: Read + Write + AsRawFd + IntoRawFd + Sized {
|
||||
fn open<P: AsRef<Path>>(device: P, options: &SerialPortSettings) -> io::Result<Self>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct I2CMasterConfig {
|
||||
pub slave_address: Option<u16>,
|
||||
pub speed_hz: Option<u32>,
|
||||
}
|
||||
|
||||
pub(crate) trait I2CMaster: Read + Write + AsRawFd + Sized {
|
||||
fn open<P: AsRef<Path>>(device: P, config: &I2CMasterConfig) -> io::Result<Self>;
|
||||
fn set_slave_address(&mut self, address: u16) -> io::Result<()>;
|
||||
fn set_speed_hz(&mut self, speed_hz: u32) -> io::Result<()>;
|
||||
fn smbus_read(&mut self, reg: u8, data: &mut [u8]) -> io::Result<usize>;
|
||||
fn smbus_write(&mut self, reg: u8, data: &[u8]) -> io::Result<usize>;
|
||||
}
|
||||
|
||||
pub trait TerminalOptions: Copy {
|
||||
fn normal() -> Self;
|
||||
fn raw() -> Self;
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
use std::{
|
||||
fs::{File, OpenOptions},
|
||||
io::{self, Read, Write},
|
||||
os::fd::{AsRawFd, RawFd},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use runtime::{abi::io::device::i2c, rt::io::device::device_request};
|
||||
|
||||
use crate::sys::{I2CMaster, I2CMasterConfig};
|
||||
|
||||
pub struct I2CMasterImpl {
|
||||
file: File,
|
||||
}
|
||||
|
||||
impl Read for I2CMasterImpl {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.file.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for I2CMasterImpl {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.file.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.file.flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl I2CMaster for I2CMasterImpl {
|
||||
fn open<P: AsRef<Path>>(device: P, config: &I2CMasterConfig) -> io::Result<Self> {
|
||||
let file = OpenOptions::new().read(true).write(true).open(device)?;
|
||||
let mut this = Self { file };
|
||||
if let Some(address) = config.slave_address {
|
||||
this.set_slave_address(address)?;
|
||||
}
|
||||
if let Some(speed_hz) = config.speed_hz {
|
||||
this.set_speed_hz(speed_hz)?;
|
||||
}
|
||||
Ok(this)
|
||||
}
|
||||
|
||||
fn set_slave_address(&mut self, address: u16) -> io::Result<()> {
|
||||
let mut buffer = [0; 4];
|
||||
device_request::<i2c::SetAddress>(self.file.as_raw_fd(), &mut buffer, &address)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_speed_hz(&mut self, speed_hz: u32) -> io::Result<()> {
|
||||
let mut buffer = [0; 4];
|
||||
device_request::<i2c::SetSpeed>(self.file.as_raw_fd(), &mut buffer, &speed_hz)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn smbus_read(&mut self, reg: u8, data: &mut [u8]) -> io::Result<usize> {
|
||||
self.write_all(&[reg])?;
|
||||
self.read(data)
|
||||
}
|
||||
|
||||
fn smbus_write(&mut self, reg: u8, data: &[u8]) -> io::Result<usize> {
|
||||
self.write_all(&[reg])?;
|
||||
self.write(data)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for I2CMasterImpl {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.file.as_raw_fd()
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod fs;
|
||||
pub mod i2c;
|
||||
pub mod mem;
|
||||
pub mod path;
|
||||
pub mod pid;
|
||||
|
||||
@@ -1,62 +1,97 @@
|
||||
#![feature(let_chains)]
|
||||
|
||||
use std::collections::{HashSet, VecDeque};
|
||||
|
||||
use stuff::{
|
||||
autocomplete,
|
||||
readline::{Buffer, NonEmptyVec, Readline, ReadlineProvider},
|
||||
use std::{
|
||||
io::{self, Write, stdout},
|
||||
path::{Path, PathBuf},
|
||||
process::ExitCode,
|
||||
thread,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
struct P {
|
||||
history: VecDeque<String>,
|
||||
use clap::Parser;
|
||||
use cross::i2c::{I2CMaster, I2CMasterConfig};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
struct Args {
|
||||
#[clap(short, long, default_value_t = 0x40, help = "I²C device address")]
|
||||
address: u8,
|
||||
#[clap(help = "I²C controller path")]
|
||||
device: PathBuf,
|
||||
}
|
||||
|
||||
impl ReadlineProvider for P {
|
||||
fn completions(&mut self, buffer: &Buffer) -> Option<NonEmptyVec<String>> {
|
||||
let word_before_cursor = buffer.word_before_cursor();
|
||||
let is_first = buffer.is_first_word();
|
||||
struct M41T80 {
|
||||
master: I2CMaster,
|
||||
}
|
||||
|
||||
let mut entries = HashSet::new();
|
||||
if let Some(word) = word_before_cursor
|
||||
&& !is_first
|
||||
{
|
||||
autocomplete::complete_from_cwd(&mut entries, word, &Default::default());
|
||||
} else {
|
||||
let prefix = word_before_cursor.unwrap_or("");
|
||||
autocomplete::complete_binary_from_env(&mut entries, prefix);
|
||||
}
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct Time {
|
||||
year: u16,
|
||||
mon: u8,
|
||||
day: u8,
|
||||
|
||||
let mut entries = NonEmptyVec::try_from_iter(entries.drain()).ok()?;
|
||||
entries.sort();
|
||||
hour: u8,
|
||||
min: u8,
|
||||
sec: u8,
|
||||
millis: u16,
|
||||
}
|
||||
|
||||
Some(entries)
|
||||
impl M41T80 {
|
||||
fn open<P: AsRef<Path>>(path: P, config: &I2CMasterConfig) -> io::Result<Self> {
|
||||
let master = I2CMaster::open(path, config)?;
|
||||
Ok(Self { master })
|
||||
}
|
||||
|
||||
fn push_history(&mut self, entry: &str) {
|
||||
self.history.push_back(entry.into());
|
||||
}
|
||||
fn read_time(&mut self) -> io::Result<Time> {
|
||||
let mut buffer = [0; 9];
|
||||
self.master.smbus_read(0x00, &mut buffer)?;
|
||||
|
||||
fn lookup_history(&mut self, depth: usize) -> Option<String> {
|
||||
if depth >= self.history.len() {
|
||||
return None;
|
||||
}
|
||||
let index = self.history.len() - depth - 1;
|
||||
Some(self.history[index].clone())
|
||||
let millis = ((buffer[0] >> 4) as u16 * 100) | ((buffer[0] & 0xF) as u16 * 10);
|
||||
let sec = ((buffer[1] >> 4) & 0x7) * 10 + (buffer[1] & 0xF);
|
||||
let min = ((buffer[2] >> 4) & 0x7) * 10 + (buffer[2] & 0xF);
|
||||
let hour = ((buffer[3] >> 4) & 0x3) * 10 + (buffer[3] & 0xF);
|
||||
|
||||
let day = ((buffer[5] >> 4) & 0x3) * 10 + (buffer[5] & 0xF);
|
||||
let mon = ((buffer[6] >> 4) & 0x1) * 10 + (buffer[6] & 0xF);
|
||||
let year = ((buffer[7] >> 4) as u16 * 10) + ((buffer[7] & 0xF) as u16) + 2000;
|
||||
|
||||
Ok(Time {
|
||||
millis,
|
||||
sec,
|
||||
min,
|
||||
hour,
|
||||
|
||||
year,
|
||||
day,
|
||||
mon,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
logsink::setup_logging(true);
|
||||
let mut readline = Readline::new(P::default()).unwrap();
|
||||
|
||||
fn run(args: Args) -> io::Result<()> {
|
||||
// let device = "/dev/i2c0";
|
||||
let i2c_config = I2CMasterConfig {
|
||||
slave_address: Some(args.address as _),
|
||||
speed_hz: None,
|
||||
};
|
||||
let mut m41t80 = M41T80::open(args.device, &i2c_config)?;
|
||||
loop {
|
||||
let Some(line) = readline.readline().unwrap() else {
|
||||
break;
|
||||
};
|
||||
println!("{line:?}");
|
||||
if line == "exit" {
|
||||
break;
|
||||
let time = m41t80.read_time()?;
|
||||
print!(
|
||||
"\r{:04}/{:02}/{:02} {:02}:{:02}:{:02}",
|
||||
time.year, time.mon, time.day, time.hour, time.min, time.sec,
|
||||
);
|
||||
stdout().flush().ok();
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
logsink::setup_logging(true);
|
||||
let args = Args::parse();
|
||||
|
||||
match run(args) {
|
||||
Ok(()) => ExitCode::SUCCESS,
|
||||
Err(error) => {
|
||||
eprintln!("{error}");
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user