i2c: improve i2c architecture, add sifive i2c driver

This commit is contained in:
2026-03-25 19:07:59 +02:00
parent f416414b93
commit 7f256cf3a6
23 changed files with 1129 additions and 312 deletions
Generated
+1
View File
@@ -2727,6 +2727,7 @@ dependencies = [
name = "ygg_driver_bsp_sifive"
version = "0.1.0"
dependencies = [
"async-trait",
"bytemuck",
"device-api",
"device-tree",
+25
View File
@@ -235,6 +235,31 @@
status = "disabled";
};
i2c0: i2c@10030000 {
compatible = "sifive,fu740-c000-i2c", "sifive,i2c0";
reg = <0x00 0x10030000 0x00 0x1000>;
interrupt-parent = <&plic>;
interrupts = <52>;
clocks = <&prci CLK_PCLK>;
reg-shift = <2>;
reg-io-width = <1>;
#address-cells = <1>;
#size-cells = <0>;
status = "okay";
};
i2c1: i2c@10031000 {
compatible = "sifive,fu740-c000-i2c", "sifive,i2c0";
reg = <0x00 0x10031000 0x00 0x1000>;
interrupt-parent = <&plic>;
interrupts = <53>;
clocks = <&prci CLK_PCLK>;
reg-shift = <2>;
reg-io-width = <1>;
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
};
};
memory@80000000 {
+21
View File
@@ -1,3 +1,4 @@
use device_api::{ResetDevice, device::Device};
use yggdrasil_abi::{error::Error, primitive_enum};
const EXT_HSM: u64 = 0x48534D;
@@ -6,6 +7,7 @@ const EXT_DBCN: u64 = 0x4442434E;
const EXT_SPI: u64 = 0x735049;
const EXT_SYSTEM_SHUTDOWN: u64 = 0x53525354;
const EXT_SYSTEM_SHUTDOWN_LEGACY: u64 = 0x08;
const EXT_SYSTEM_RESET: u64 = 0x53525354;
primitive_enum! {
pub enum Status: i64 {
@@ -52,6 +54,20 @@ impl From<i64> for SbiError {
}
}
pub struct SbiResetMethod;
impl Device for SbiResetMethod {
fn display_name(&self) -> &str {
"SBI reset"
}
}
impl ResetDevice for SbiResetMethod {
unsafe fn reset(&self) -> ! {
sbi_system_reset()
}
}
#[allow(clippy::too_many_arguments)]
#[inline(always)]
unsafe fn sbi_do_call(
@@ -107,6 +123,11 @@ pub fn sbi_set_timer(next_event: u64) {
unsafe { sbi_do_call(EXT_TIME, 0x00, next_event, 0, 0, 0, 0, 0) }.ok();
}
pub fn sbi_system_reset() -> ! {
unsafe { sbi_do_call(EXT_SYSTEM_RESET, 0x00, 0x01, 0x00, 0, 0, 0, 0) }.ok();
unreachable!()
}
pub fn sbi_system_shutdown() -> ! {
unsafe { sbi_do_call(EXT_SYSTEM_SHUTDOWN, 0x00, 0, 0, 0, 0, 0, 0) }.ok();
unsafe { sbi_do_call(EXT_SYSTEM_SHUTDOWN_LEGACY, 0x00, 0, 0, 0, 0, 0, 0) }.ok();
+131 -123
View File
@@ -1,15 +1,19 @@
use alloc::sync::Arc;
use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait;
use device_api::{
clock::{ClockHandle, Hertz},
device::{Device, DeviceInitContext},
i2c::{I2CAddress, I2CController},
i2c::{I2CAddress, I2CController, I2CMessage, I2CTransfer},
interrupt::{InterruptHandler, IrqHandle, IrqVector},
};
use device_tree::driver::{Node, ProbeContext, device_tree_driver};
use futures_util::task::AtomicWaker;
use libk::{device::manager::DEVICE_REGISTRY, error::Error};
use libk_mm::device::DeviceMemoryIo;
use libk_util::{OneTimeInit, sync::spin_rwlock::IrqSafeRwLock};
use libk_util::{OneTimeInit, event::BitmapEvent, sync::spin_rwlock::IrqSafeRwLock};
use tock_registers::{
LocalRegisterCopy,
fields::FieldValue,
interfaces::{ReadWriteable, Readable, Writeable},
register_bitfields, register_structs,
registers::ReadWrite,
@@ -56,6 +60,10 @@ register_structs! {
}
}
struct I2CState {
status: BitmapEvent<AtomicWaker>,
}
struct I2C {
name: &'static str,
clock_frequency: Option<Hertz>,
@@ -63,87 +71,22 @@ struct I2C {
clock: Option<ClockHandle>,
regs: IrqSafeRwLock<DeviceMemoryIo<'static, Regs>>,
index: OneTimeInit<u32>,
state: I2CState,
}
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();
impl I2CState {
fn new() -> Self {
Self {
status: BitmapEvent::new(AtomicWaker::new()),
}
}
}
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();
}
// All of these are thread-unsafe, but I2C subsystem locks the peripheral by process
impl I2C {
async fn wait_for_event(&self) -> LocalRegisterCopy<u32, S::Register> {
let event = self.state.status.wait().await;
LocalRegisterCopy::new(event as u32)
}
}
@@ -158,16 +101,13 @@ impl Device for I2C {
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);
self.irq.register(self.clone())?;
self.irq.enable()?;
Ok(())
}
@@ -178,10 +118,26 @@ impl Device for I2C {
impl InterruptHandler for I2C {
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
todo!()
let regs = self.regs.write();
let event = regs.S.extract();
// log::info!("I2C irq {:#x}", event.get());
let mut clear = FieldValue::none();
if event.matches_all(S::RXR::SET) {
clear += C::INTR::CLEAR;
}
if event.matches_all(S::TXW::SET) {
clear += C::INTT::CLEAR;
}
if event.matches_all(S::DONE::SET) {
clear += C::INTD::CLEAR;
}
regs.C.modify(clear);
self.state.status.signal(event.get() as u64);
true
}
}
#[async_trait]
impl I2CController for I2C {
fn bus_number(&self) -> u32 {
*self.index.get()
@@ -217,47 +173,98 @@ impl I2CController for I2C {
}
}
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;
async fn i2c_transfer(
&self,
address: I2CAddress,
transfer: &mut I2CTransfer,
) -> Result<usize, Error> {
// TODO validate transfer
let address = address.as_8_bit().ok_or(Error::NotImplemented)?;
let repeat_start = transfer.repeat_start;
let mut nbytes = 0;
let mut error = None;
for (i, message) in transfer.messages.iter_mut().enumerate() {
if i == 0 || repeat_start {
let (read, len) = match message {
I2CMessage::Read(buffer) => (true, buffer.len()),
I2CMessage::Write(buffer) => (false, buffer.len()),
};
// log::info!("{}: START {:#x} read={}", self.name, address, read);
let read = if read { C::READ::SET } else { C::READ::CLEAR };
let regs = self.regs.write();
regs.S.write(S::ERR::SET + S::DONE::SET);
regs.DLEN.set(len as u32);
regs.C.modify(C::ST::CLEAR);
regs.A.set(address as u32);
regs.C.modify(
read + C::ST::SET + C::I2CEN::SET + C::INTD::SET + C::INTR::SET + C::INTT::SET,
);
}
}
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 {
let mut pos = 0;
let status = loop {
let s = self.wait_for_event().await;
let regs = self.regs.write();
if s.matches_all(S::ERR::SET) || s.matches_all(S::DONE::SET) {
break s;
}
match message {
I2CMessage::Write(buffer) => {
if s.matches_all(S::TXW::SET) {
while regs.S.matches_all(S::TXD::SET) && pos < buffer.len() {
let byte = buffer[pos];
// log::info!("{}: tx {:#x}", self.name, byte);
regs.FIFO.set(byte as u32);
pos += 1;
nbytes += 1;
}
}
regs.C.modify(C::INTT::SET);
}
I2CMessage::Read(buffer) => {
if s.matches_all(S::RXR::SET) {
while regs.S.matches_all(S::RXD::SET) && pos < buffer.len() {
let byte = regs.FIFO.get() as u8;
// log::info!("{}: rx {:#x}", self.name, byte);
buffer[pos] = byte;
pos += 1;
nbytes += 1;
}
}
regs.C.modify(C::INTR::SET);
}
}
};
// Finalize message
let regs = self.regs.write();
if status.matches_all(S::ERR::SET) {
regs.S.write(S::DONE::SET + S::ERR::SET);
error = Some(Error::HostUnreachable);
break;
} else if status.matches_all(S::DONE::SET) {
regs.S.write(S::DONE::SET);
} else {
log::error!("{}: transfer finished without DONE or ERR set", self.name);
log::error!("{}: S = {:#x}", self.name, status.get());
todo!();
}
}
regs.finish_transfer(self.name)?;
Ok(bytes_read)
// Finish transfer
// log::info!(
// "{}: finish xfer, error={:?}, nbytes={}",
// self.name,
// error,
// nbytes
// );
let regs = self.regs.write();
regs.C.set(0);
match error {
Some(error) => Err(error),
None => Ok(nbytes),
}
}
}
@@ -279,7 +286,8 @@ device_tree_driver! {
irq,
clock,
index: OneTimeInit::new(),
regs: IrqSafeRwLock::new(regs)
regs: IrqSafeRwLock::new(regs),
state: I2CState::new()
});
node.make_i2c_controller(i2c.clone());
+1
View File
@@ -17,3 +17,4 @@ tock-registers.workspace = true
log.workspace = true
bytemuck.workspace = true
futures-util.workspace = true
async-trait.workspace = true
+523
View File
@@ -0,0 +1,523 @@
use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait;
use device_api::{
clock::{ClockHandle, Hertz, IntoHertz},
device::{Device, DeviceInitContext},
i2c::{I2CAddress, I2CController, I2CMessage, I2CTransfer},
interrupt::{InterruptHandler, IrqHandle, IrqVector},
};
use device_tree::driver::{Node, ProbeContext, device_tree_driver};
use futures_util::task::AtomicWaker;
use libk::{block, device::manager::DEVICE_REGISTRY, error::Error};
use libk_mm::device::DeviceMemoryIo;
use libk_util::{OneTimeInit, event::BitmapEvent, sync::IrqSafeSpinlock};
use tock_registers::{
LocalRegisterCopy,
interfaces::{ReadWriteable, Readable, Writeable},
register_bitfields, register_structs,
registers::{Aliased, ReadWrite},
};
use yggdrasil_abi::io::device::i2c::I2CMasterInfo;
const CMD_START: u32 = 0x91;
const CMD_STOP: u32 = 0x41;
const CMD_WRITE: u32 = 0x11;
const CMD_READ_ACK: u32 = 0x21;
const CMD_READ_NACK: u32 = 0x29;
const CMD_IACK: u32 = 0x01;
register_bitfields! {
u32,
CTR [
EN OFFSET(7) NUMBITS(1) [],
IEN OFFSET(6) NUMBITS(1) [],
],
SR [
RXACK OFFSET(7) NUMBITS(1) [],
BUSY OFFSET(6) NUMBITS(1) [],
AL OFFSET(5) NUMBITS(1) [],
TIP OFFSET(1) NUMBITS(1) [],
IF OFFSET(0) NUMBITS(1) [],
],
}
register_structs! {
#[allow(non_snake_case)]
Regs {
(0x000 => PRER_LO: ReadWrite<u32>),
(0x004 => PRER_HI: ReadWrite<u32>),
(0x008 => CTR: ReadWrite<u32, CTR::Register>),
(0x00C => TXR_RXR: ReadWrite<u32>),
(0x010 => CR_SR: Aliased<u32, SR::Register, ()>),
(0x014 => _0),
(0x020 => @END),
}
}
struct AbortGuard<'a> {
i2c: &'a I2C,
abort: bool,
}
impl<'a> AbortGuard<'a> {
pub fn new(i2c: &'a I2C) -> Self {
Self { i2c, abort: true }
}
pub fn consume(mut self) {
self.abort = false;
}
}
impl Drop for AbortGuard<'_> {
fn drop(&mut self) {
if self.abort {
block!(self.i2c.finish_transfer(Err(Error::Interrupted)).await).ok();
}
}
}
pub struct I2C {
name: &'static str,
clock: ClockHandle,
irq: IrqHandle,
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
index: OneTimeInit<u32>,
sr: BitmapEvent<AtomicWaker>,
}
impl Regs {
fn set_clock_rate(&self, input_rate: Hertz, clock_rate: Hertz) -> Result<Hertz, Error> {
let divider = Hertz::divider(input_rate / 5, clock_rate).ok_or(Error::InvalidArgument)?;
if divider == 0 || divider > 0xFFFF {
return Err(Error::InvalidArgument);
}
log::info!("input_rate = {input_rate}, desired = {clock_rate}, divider = {divider}");
let divider = divider - 1;
self.PRER_LO.set(divider & 0xFF);
self.PRER_HI.set(divider >> 8);
let real_rate = input_rate / (5 * (divider + 1));
Ok(real_rate)
}
}
impl I2C {
async fn wait_for_event(&self) -> LocalRegisterCopy<u32, SR::Register> {
let value = self.sr.wait().await;
LocalRegisterCopy::new(value as u32)
}
async fn finish_transfer(&self, status: Result<usize, Error>) -> Result<usize, Error> {
if status == Err(Error::Interrupted) {
log::warn!("{}: transfer aborted", self.name);
let regs = self.regs.lock();
let stopped = {
if regs.CR_SR.matches_all(SR::TIP::CLEAR) {
true
} else {
false
}
};
if !stopped {
regs.CTR.modify(CTR::IEN::SET);
drop(regs);
loop {
let sr = self.wait_for_event().await;
if sr.matches_all(SR::TIP::CLEAR) {
break;
}
let regs = self.regs.lock();
regs.CTR.modify(CTR::IEN::SET); // Reenable IRQs
}
}
}
// Generate stop condition
{
let regs = self.regs.lock();
regs.CR_SR.set(CMD_STOP);
regs.CTR.modify(CTR::IEN::SET); // Reenable IRQs
}
loop {
let sr = self.wait_for_event().await;
if sr.matches_all(SR::BUSY::CLEAR) {
break;
}
let regs = self.regs.lock();
regs.CTR.modify(CTR::IEN::SET); // Reenable IRQs
}
let regs = self.regs.lock();
regs.CTR.modify(CTR::IEN::CLEAR); // Mask IRQs
regs.CR_SR.set(CMD_IACK);
match status {
Ok(count) => Ok(count),
Err(err) => Err(err),
}
}
}
impl Device for I2C {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
let input_clk = self.clock.rate()?;
let regs = self.regs.lock();
regs.CTR.set(0);
regs.set_clock_rate(input_clk, 100u64.khz())?;
regs.CTR.write(CTR::EN::SET);
regs.CR_SR.set(CMD_IACK);
let index = DEVICE_REGISTRY.i2c.register_bus(self.clone())?;
self.index.init(index);
Ok(())
}
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
self.irq.register(self.clone())?;
self.irq.enable()?;
Ok(())
}
fn display_name(&self) -> &str {
self.name
}
}
impl InterruptHandler for I2C {
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
let regs = self.regs.lock();
let status = regs.CR_SR.extract();
self.sr.signal(status.get() as u64);
regs.CTR.modify(CTR::IEN::CLEAR);
true
}
}
#[async_trait]
impl I2CController for I2C {
fn bus_number(&self) -> u32 {
*self.index.get()
}
fn child_number(&self) -> Option<u32> {
None
}
fn capabilities(&self) -> I2CMasterInfo {
let scl_max = self.clock.rate().expect("Couldn't get input clock rate") / 5;
I2CMasterInfo {
max_speed_hz: scl_max.0 as u32,
supports_10bit_addresses: true,
}
}
async fn i2c_transfer(
&self,
address: I2CAddress,
transfer: &mut I2CTransfer,
) -> Result<usize, Error> {
// TODO validate transfer
let address = address.as_8_bit().ok_or(Error::NotImplemented)?;
let repeat_start = transfer.repeat_start;
let mut nbytes = 0;
let mut error = None;
let mut abort = None;
for (i, message) in transfer.messages.iter_mut().enumerate() {
let (read, buffer_len) = match message {
I2CMessage::Read(buffer) => (true, buffer.len()),
I2CMessage::Write(buffer) => (false, buffer.len()),
};
if i == 0 || repeat_start {
//log::info!("{}: START {:#x} read={}", self.name, address, read);
let read_bit = if read { 1 << 0 } else { 0 << 0 };
let regs = self.regs.lock();
regs.TXR_RXR.set(((address as u32) << 1) | read_bit);
regs.CR_SR.set(CMD_START);
regs.CTR.modify(CTR::IEN::SET);
if abort.is_none() {
abort = Some(AbortGuard::new(self));
}
}
let mut pos = 0;
let mut start = true;
loop {
if pos == buffer_len {
break;
}
let sr = self.wait_for_event().await;
let regs = self.regs.lock();
if sr.matches_all(SR::AL::SET) {
error = Some(Error::HostUnreachable);
break;
}
match message {
I2CMessage::Read(buffer) => {
if pos == 0 && sr.matches_all(SR::RXACK::SET) {
error = Some(Error::HostUnreachable);
break;
}
if start {
regs.CR_SR.set(CMD_READ_ACK);
start = false;
} else if sr.matches_all(SR::TIP::CLEAR) {
let byte = regs.TXR_RXR.get() as u8;
//log::info!("{}: rx {:#x}", self.name, byte);
buffer[pos] = byte;
pos += 1;
nbytes += 1;
if pos >= buffer.len() {
regs.CR_SR.set(CMD_READ_NACK);
} else {
regs.CR_SR.set(CMD_READ_ACK);
}
}
}
I2CMessage::Write(buffer) => {
if sr.matches_all(SR::RXACK::SET) {
error = Some(Error::HostUnreachable);
break;
}
regs.TXR_RXR.set(buffer[pos] as u32);
//log::info!("{}: tx {:#x}", self.name, buffer[pos]);
pos += 1;
nbytes += 1;
regs.CR_SR.set(CMD_WRITE);
}
}
regs.CTR.modify(CTR::IEN::SET);
}
if error.is_some() {
break;
}
{
let regs = self.regs.lock();
if regs.CR_SR.matches_all(SR::TIP::CLEAR) {
regs.CTR.modify(CTR::IEN::SET); // Reenable IRQs
continue;
}
regs.CTR.modify(CTR::IEN::SET); // Reenable IRQs
}
loop {
let sr = self.wait_for_event().await;
if sr.matches_all(SR::TIP::CLEAR) {
break;
}
let regs = self.regs.lock();
regs.CTR.modify(CTR::IEN::SET); // Reenable IRQs
}
}
// Generate stop condition
{
//log::info!("{}: STOP", self.name);
let regs = self.regs.lock();
regs.CR_SR.set(CMD_STOP);
regs.CTR.modify(CTR::IEN::SET); // Reenable IRQs
}
loop {
let sr = self.wait_for_event().await;
if sr.matches_all(SR::BUSY::CLEAR) {
break;
}
let regs = self.regs.lock();
regs.CTR.modify(CTR::IEN::SET); // Reenable IRQs
}
if let Some(abort) = abort.take() {
abort.consume();
}
let regs = self.regs.lock();
regs.CTR.modify(CTR::IEN::CLEAR); // Mask IRQs
regs.CR_SR.set(CMD_IACK);
// log::info!(
// "{}: finish xfer, error={:?}, nbytes={}",
// self.name,
// error,
// nbytes
// );
match error {
Some(err) => Err(err),
None => Ok(nbytes),
}
}
// async fn i2c_read(&self, address: I2CAddress, buffer: &mut [u8]) -> Result<usize, Error> {
// self.start_transfer(address, true)?;
// let mut pos = 0;
// let status = loop {
// if pos >= buffer.len() {
// break Ok(pos);
// }
// let sr = self.wait_for_event().await;
// let regs = self.regs.lock();
// if sr.matches_all(SR::RXACK::SET) || sr.matches_all(SR::AL::SET) {
// break Err(sr);
// }
// };
// self.finish_transfer(status).await
// }
// async fn i2c_write(&self, address: I2CAddress, buffer: &[u8]) -> Result<usize, Error> {
// self.start_transfer(address, false)?;
// let mut pos = 0;
// let status = loop {
// if pos >= buffer.len() {
// break Ok(pos);
// }
// let sr = self.wait_for_event().await;
// let regs = self.regs.lock();
// if sr.matches_all(SR::RXACK::SET) || sr.matches_all(SR::AL::SET) {
// break Err(sr);
// }
// };
// self.finish_transfer(status).await
// }
// fn i2c_read(&self, address: I2CAddress, buffer: &mut [u8]) -> Result<usize, Error> {
// let address = address.as_8_bit().ok_or(Error::NotImplemented)?;
// let regs = self.regs.lock();
// log::info!(":::: start {:#x} read", address);
// regs.poll_wait(SR::BUSY::SET);
// regs.TXR_RXR.set(((address as u32) << 1) | 1);
// regs.CR_SR.set(CMD_START);
// let mut read = 0;
// let mut error = None;
// let mut start = true;
// loop {
// let sr = regs.poll_wait(SR::TIP::SET);
// if read == buffer.len() {
// break;
// }
// if sr.matches_all(SR::AL::SET) {
// error = Some(Error::WouldBlock);
// break;
// }
// if sr.matches_all(SR::RXACK::SET) {
// error = Some(Error::DoesNotExist);
// break;
// }
// if !start {
// let val = regs.TXR_RXR.get() as u8;
// buffer[read] = val;
// read += 1;
// if read >= buffer.len() {
// log::info!(":::: read_ack {:02x}", val);
// regs.CR_SR.set(CMD_READ_NACK);
// } else {
// log::info!(":::: read {:02x}", val);
// regs.CR_SR.set(CMD_READ_ACK);
// }
// } else {
// log::info!(":::: begin read");
// regs.CR_SR.set(CMD_READ);
// start = false;
// }
// }
// log::info!(":::: stop {:#x}", address);
// // Generate stop condition
// regs.CR_SR.set(CMD_STOP);
// regs.poll_wait(SR::BUSY::SET);
// regs.CR_SR.set(CMD_IACK);
// log::info!(":::: xfer finish {error:?}, read {read}");
// if let Some(error) = error {
// Err(error)
// } else {
// Ok(read)
// }
// }
// fn i2c_write(&self, address: I2CAddress, buffer: &[u8]) -> Result<usize, Error> {
// let address = address.as_8_bit().ok_or(Error::NotImplemented)?;
// let regs = self.regs.lock();
// log::info!(":::: start {:#x} write", address);
// regs.poll_wait(SR::BUSY::SET);
// regs.TXR_RXR.set((address as u32) << 1);
// regs.CR_SR.set(CMD_START);
// let mut sent = 0;
// let mut error = None;
// loop {
// let sr = regs.poll_wait(SR::TIP::SET);
// if sent == buffer.len() {
// // Generate stop condition
// regs.CR_SR.set(CMD_STOP);
// break;
// }
// if sr.matches_all(SR::AL::SET) {
// regs.CR_SR.set(CMD_STOP);
// error = Some(Error::WouldBlock);
// break;
// }
// if sr.matches_all(SR::RXACK::SET) {
// // Generate stop condition
// regs.CR_SR.set(CMD_STOP);
// error = Some(Error::DoesNotExist);
// break;
// }
// log::info!(":::: send {:#x}", buffer[sent]);
// regs.TXR_RXR.set(buffer[sent] as u32);
// regs.CR_SR.set(CMD_WRITE);
// sent += 1;
// }
// log::info!(":::: stop {:#x}", address);
// regs.poll_wait(SR::BUSY::SET);
// regs.CR_SR.set(CMD_IACK);
// log::info!(":::: xfer finish {error:?}, sent {sent}");
// if let Some(error) = error {
// Err(error)
// } else {
// Ok(sent)
// }
// }
fn set_speed(&self, speed: Hertz) -> Result<Hertz, Error> {
let input_rate = self.clock.rate()?;
let regs = self.regs.lock();
regs.set_clock_rate(input_rate, speed)
}
}
device_tree_driver! {
compatible: ["sifive,i2c0"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let name = node.name().unwrap_or("i2c");
let base = node.map_base(context, 0)?;
let clock = node.clock(0)?;
let irq = node.interrupt(0)?;
let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
let i2c = Arc::new(I2C {
name,
regs: IrqSafeSpinlock::new(regs),
clock,
irq,
index: OneTimeInit::new(),
sr: BitmapEvent::new(AtomicWaker::new()),
});
node.make_i2c_controller(i2c.clone());
Some(i2c)
}
}
}
+1
View File
@@ -5,6 +5,7 @@ extern crate alloc;
mod clock_fu540;
mod clock_fu740;
mod ethernet;
mod i2c;
mod pll;
mod pwm;
mod uart;
+20 -3
View File
@@ -4,7 +4,8 @@ use core::{
sync::atomic::{AtomicU16, AtomicU32, Ordering},
};
use alloc::sync::Arc;
use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait;
use yggdrasil_abi::{error::Error, io::device::i2c::I2CMasterInfo, process::ProcessId};
use crate::{
@@ -20,6 +21,17 @@ pub struct I2CDevice {
user: AtomicU32,
}
pub enum I2CMessage<'a> {
Read(&'a mut [u8]),
Write(&'a [u8]),
}
pub struct I2CTransfer<'a> {
pub repeat_start: bool,
pub messages: &'a mut [I2CMessage<'a>],
}
#[async_trait]
pub trait I2CController: Device {
fn bus_number(&self) -> u32;
fn child_number(&self) -> Option<u32>;
@@ -27,8 +39,13 @@ pub trait I2CController: Device {
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>;
async fn i2c_transfer(
&self,
address: I2CAddress,
transfer: &mut I2CTransfer,
) -> Result<usize, Error>;
// async fn i2c_write(&self, address: I2CAddress, buffer: &[u8]) -> Result<usize, Error>;
// async fn i2c_read(&self, address: I2CAddress, buffer: &mut [u8]) -> Result<usize, Error>;
}
impl I2CAddress {
+47 -10
View File
@@ -4,7 +4,7 @@ use alloc::boxed::Box;
use async_trait::async_trait;
use device_api::{
clock::Hertz,
i2c::{I2CAddress, I2CDevice},
i2c::{I2CAddress, I2CDevice, I2CMessage, I2CTransfer},
};
use yggdrasil_abi::{
error::Error,
@@ -13,26 +13,42 @@ use yggdrasil_abi::{
process::ProcessId,
};
use crate::{device::char::CharDevice, task::thread::Thread, vfs::FileReadiness};
use crate::{
device::char::CharDevice,
task::{mem::ForeignPointer, thread::Thread},
vfs::FileReadiness,
};
// 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)
let pid = Thread::current().process_id();
self.check_lock(pid)?;
let mut message = [I2CMessage::Read(buffer)];
let mut xfer = I2CTransfer {
messages: &mut message,
repeat_start: false,
};
self.i2c_transfer(self.slave_address(), &mut xfer).await
}
async fn write(&self, buffer: &[u8]) -> Result<usize, Error> {
self.write_nonblocking(buffer)
let pid = Thread::current().process_id();
self.check_lock(pid)?;
let mut message = [I2CMessage::Write(buffer)];
let mut xfer = I2CTransfer {
messages: &mut message,
repeat_start: false,
};
self.i2c_transfer(self.slave_address(), &mut xfer).await
}
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)
let _ = buffer;
Err(Error::InvalidOperation)
}
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)
let _ = buffer;
Err(Error::InvalidOperation)
}
fn device_request(&self, option: u32, buffer: &mut [u8], len: usize) -> Result<usize, Error> {
if let Ok(req) = I2CRequestVariant::try_from(option) {
@@ -55,6 +71,27 @@ impl CharDevice for I2CDevice {
let len = i2c::GetMasterInfo::store_response(&info, buffer)?;
Ok(len)
}
I2CRequestVariant::SmbusReadReg => {
let thread = Thread::current();
let pid = thread.process_id();
self.check_lock(pid)?;
let read_reg = i2c::SmbusReadReg::load_request(&buffer[..len])?;
let space = thread.address_space();
let rx_buffer = unsafe {
read_reg
.value_buffer
.validate_user_slice_mut(read_reg.value_buffer_len, space)
}?;
let tx_buffer = [read_reg.reg];
let mut messages = [I2CMessage::Write(&tx_buffer), I2CMessage::Read(rx_buffer)];
let mut xfer = I2CTransfer {
messages: &mut messages,
repeat_start: true,
};
let len = block!(self.i2c_transfer(self.slave_address(), &mut xfer).await)??;
let len = i2c::SmbusReadReg::store_response(&len.saturating_sub(1), buffer)?;
Ok(len)
}
}
} else {
Err(Error::InvalidArgument)
+3 -2
View File
@@ -7,7 +7,6 @@ use device_tree::{
DeviceTree, DeviceTreeNodeExt,
driver::{InitSequence, unflatten_device_tree},
};
use kernel_arch::{Architecture, ArchitectureImpl};
use kernel_arch_riscv64::{
PerCpuData, mem,
registers::{SIE, SSTATUS},
@@ -48,10 +47,12 @@ pub struct Riscv64 {
impl Platform for Riscv64 {
unsafe fn reset(&self) -> ! {
ArchitectureImpl::halt();
log::warn!("Performing system reset via SBI");
sbi::sbi_system_reset()
}
unsafe fn power_off(&self) -> Result<!, Error> {
log::warn!("Performing system shutdown via SBI");
sbi::sbi_system_shutdown()
}
+10 -10
View File
@@ -4,11 +4,12 @@ use abi::{
error::Error,
io::{FileMode, device::i2c::I2CMasterInfo},
};
use alloc::{string::String, sync::Arc};
use alloc::{boxed::Box, string::String, sync::Arc};
use async_trait::async_trait;
use device_api::{
clock::Hertz,
device::{Device, DeviceInitContext},
i2c::{I2CAddress, I2CController, I2CDevice},
i2c::{I2CAddress, I2CController, I2CDevice, I2CTransfer},
};
use device_tree::driver::{Node, ProbeContext, device_tree_driver, lookup_phandle};
use libk::fs::devfs;
@@ -71,6 +72,7 @@ impl Device for I2CMuxChild {
}
}
#[async_trait]
impl I2CController for I2CMuxChild {
fn bus_number(&self) -> u32 {
self.parent.bus_number()
@@ -88,14 +90,12 @@ impl I2CController for I2CMuxChild {
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)
async fn i2c_transfer(
&self,
address: I2CAddress,
transfer: &mut I2CTransfer,
) -> Result<usize, Error> {
self.parent.i2c_transfer(address, transfer).await
}
}
+6
View File
@@ -56,6 +56,7 @@ fn dump_panic_info(cpu: &LocalCpu, pi: &core::panic::PanicInfo) {
debug::panic_log!(sink, "--- END PANIC ---\n");
}
#[cfg_attr(any(rust_analyzer, target_arch = "riscv64"), allow(unreachable_code))]
pub(crate) fn panic_handler(pi: &core::panic::PanicInfo) -> ! {
let cpu = Cpu::local();
@@ -80,6 +81,11 @@ pub(crate) fn panic_handler(pi: &core::panic::PanicInfo) -> ! {
dump_panic_info(&cpu, pi);
#[cfg(any(rust_analyzer, target_arch = "riscv64"))]
unsafe {
PLATFORM.reset();
}
PANIC_FINISHED_FENCE.signal();
while PANIC_SEQUENCE.load(Ordering::Acquire) != cpu.id() {
core::hint::spin_loop();
+1
View File
@@ -96,6 +96,7 @@ pub(crate) fn system_control(option: u32, value: &mut [u8], size: usize) -> Resu
SystemControlVariant::PowerOff => {
unsafe { PLATFORM.power_off() }?;
}
SystemControlVariant::Reboot => unsafe { PLATFORM.reset() },
}
}
+18
View File
@@ -67,6 +67,18 @@ pub mod i2c {
pub supports_10bit_addresses: bool,
}
/// I²C SMBus read operation
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct SmbusRead {
/// Register to be read from
pub reg: u8,
/// Buffer pointer
pub value_buffer: *mut u8,
/// Buffer length
pub value_buffer_len: usize,
}
request_group!(
#[doc = "I²C device requests"]
pub enum I2CRequestVariant<'de> {
@@ -74,11 +86,17 @@ pub mod i2c {
0x2000: SetAddress(u16, ()),
#[doc = "Configures device speed (in Hertz)"]
0x2001: SetSpeed(u32, u32),
#[doc = "Performs a SMBus register read"]
0x2002: SmbusReadReg(SmbusRead, usize),
#[doc = "Queries I²C master capabilities"]
0x2010: GetMasterInfo((), I2CMasterInfo),
}
);
abi_serde::impl_struct_serde!(SmbusRead: [
reg, value_buffer, value_buffer_len
]);
abi_serde::impl_struct_serde!(I2CMasterInfo: [
max_speed_hz, supports_10bit_addresses
]);
+2
View File
@@ -26,6 +26,8 @@ request_group!(
pub enum SystemControlVariant<'de> {
#[doc = "Power down the system"]
0x1000: PowerOff((), ()),
#[doc = "Reboot the system"]
0x1001: Reboot((), ()),
}
);
+1
View File
@@ -1,3 +1,4 @@
#!/bin/sh
/bin/echo info >/sys/debug/0/level
/bin/echo info >/sys/debug/1/level
+14
View File
@@ -38,6 +38,20 @@ impl I2CMaster {
self.0.set_speed_hz(speed_hz)
}
pub fn smbus_read_byte(&mut self, reg: u8) -> io::Result<u8> {
let mut buffer = [0; 1];
let len = self.0.smbus_read(reg, &mut buffer)?;
if len != 1 {
println!("smbus_read_byte {len:?}");
Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"Incomplete message received",
))
} else {
Ok(buffer[0])
}
}
pub fn smbus_read(&mut self, reg: u8, data: &mut [u8]) -> io::Result<usize> {
self.0.smbus_read(reg, data)
}
+13 -3
View File
@@ -5,7 +5,10 @@ use std::{
path::Path,
};
use runtime::{abi::io::device::i2c, rt::io::device::device_request};
use runtime::{
abi::io::device::i2c,
rt::io::device::{device_request, i2c::SmbusRead},
};
use crate::sys::{I2CMaster, I2CMasterConfig};
@@ -55,11 +58,18 @@ impl I2CMaster for I2CMasterImpl {
}
fn smbus_read(&mut self, reg: u8, data: &mut [u8]) -> io::Result<usize> {
self.write_all(&[reg])?;
self.read(data)
let mut buffer = [0; 32];
let xfer = SmbusRead {
reg,
value_buffer: data.as_mut_ptr(),
value_buffer_len: data.len(),
};
let len = device_request::<i2c::SmbusReadReg>(self.file.as_raw_fd(), &mut buffer, &xfer)?;
Ok(len)
}
fn smbus_write(&mut self, reg: u8, data: &[u8]) -> io::Result<usize> {
// TODO incorrect
self.write_all(&[reg])?;
self.write(data)
}
+4
View File
@@ -98,6 +98,10 @@ path = "src/mv.rs"
name = "ln"
path = "src/ln.rs"
[[bin]]
name = "i2c"
path = "src/i2c.rs"
[[bin]]
name = "mkdir"
path = "src/mkdir.rs"
+270
View File
@@ -0,0 +1,270 @@
#![feature(yggdrasil_os)]
use std::{
io,
marker::PhantomData,
os::fd::AsRawFd,
path::{Path, PathBuf},
process::ExitCode,
str::FromStr,
time::{Duration, Instant},
};
use clap::Parser;
use cross::{
i2c::{I2CMaster, I2CMasterConfig},
io::{Poll, PollEvent},
};
use libterm::{Term, TermKey};
use tui::{
Terminal,
layout::Constraint,
style::{Color, Style},
widgets::{Cell, Row, Table},
};
#[derive(Debug, Parser)]
struct Args {
#[clap(subcommand)]
command: Subcommand,
}
#[derive(Debug, Clone, Copy)]
struct Address(u8);
#[derive(Debug, clap::Subcommand)]
enum Subcommand {
TMP421 {
#[clap(help = "I²C controller path")]
device: PathBuf,
#[clap(default_value = "0x40", help = "I²C device address")]
address: Address,
},
TMP451 {
#[clap(help = "I²C controller path")]
device: PathBuf,
#[clap(default_value = "0x4C", help = "I²C device address")]
address: Address,
},
Scan {
#[clap(help = "I²C controller path")]
device: PathBuf,
},
Dump {
#[clap(help = "I²C controller path")]
device: PathBuf,
#[clap(help = "I²C device address")]
address: Address,
},
}
trait TmpSensorDefinition {
const REG_LOCAL_TEMP_MSB: u8;
const REG_LOCAL_TEMP_LSB: u8;
const REG_REMOTE_TEMP_MSB: u8;
const REG_REMOTE_TEMP_LSB: u8;
fn process_temperature(lsb: u8, msb: u8) -> f64;
}
struct Tmp451;
struct Tmp421;
struct TmpSensor<T: TmpSensorDefinition> {
master: I2CMaster,
_definition: PhantomData<T>,
}
impl FromStr for Address {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
eprintln!("Parse {s:?}");
let value = if let Some(v) = s.strip_prefix("0x") {
u8::from_str_radix(v, 16)
} else {
s.parse::<u8>()
}
.map_err(|_| format!("Invalid address: {s:?}"))?;
if value >= 0x80 {
Err(format!("Invalid address: {s:?}"))
} else {
Ok(Self(value))
}
}
}
impl<T: TmpSensorDefinition> TmpSensor<T> {
pub fn open<P: AsRef<Path>>(path: P, address: Address) -> io::Result<Self> {
let i2c_config = I2CMasterConfig {
slave_address: Some(address.0 as _),
speed_hz: None,
};
let master = I2CMaster::open(path, &i2c_config)?;
Ok(Self {
master,
_definition: PhantomData,
})
}
fn read_temperature(&mut self, msb_reg: u8, lsb_reg: u8) -> io::Result<f64> {
let msb = self.master.smbus_read_byte(msb_reg)?;
let lsb = self.master.smbus_read_byte(lsb_reg)?;
Ok(T::process_temperature(lsb, msb))
}
pub fn read_local_temperature(&mut self) -> io::Result<f64> {
self.read_temperature(T::REG_LOCAL_TEMP_MSB, T::REG_LOCAL_TEMP_LSB)
}
pub fn read_remote_temperature(&mut self) -> io::Result<f64> {
self.read_temperature(T::REG_REMOTE_TEMP_MSB, T::REG_REMOTE_TEMP_LSB)
}
}
impl TmpSensorDefinition for Tmp421 {
const REG_LOCAL_TEMP_MSB: u8 = 0x00;
const REG_LOCAL_TEMP_LSB: u8 = 0x10;
const REG_REMOTE_TEMP_MSB: u8 = 0x01;
const REG_REMOTE_TEMP_LSB: u8 = 0x11;
fn process_temperature(lsb: u8, msb: u8) -> f64 {
(msb as i8) as f64 + (lsb >> 4) as f64 * 0.0625
}
}
impl TmpSensorDefinition for Tmp451 {
const REG_LOCAL_TEMP_MSB: u8 = 0x00;
const REG_LOCAL_TEMP_LSB: u8 = 0x15;
const REG_REMOTE_TEMP_MSB: u8 = 0x01;
const REG_REMOTE_TEMP_LSB: u8 = 0x10;
fn process_temperature(lsb: u8, msb: u8) -> f64 {
msb as f64 + (lsb >> 4) as f64 * 0.0625
}
}
fn run_dump<P: AsRef<Path>>(device: P, address: Address) -> io::Result<()> {
let i2c_config = I2CMasterConfig {
slave_address: Some(address.0 as _),
speed_hz: None,
};
let mut master = I2CMaster::open(device, &i2c_config)?;
for i in 0..16 {
print!("{:02x}: ", i * 16);
for j in 0..16 {
match master.smbus_read_byte(i * 16 + j) {
Ok(byte) => print!("{byte:02X}"),
Err(_) => print!("??"),
}
if j % 2 == 1 {
print!(" ");
}
}
println!();
}
Ok(())
}
fn run_scan<P: AsRef<Path>>(device: P) -> io::Result<()> {
for i in 0..16 {
print!("{:02x}: ", i * 16);
for j in 0..16 {
let address = i * 16 + j;
let i2c_config = I2CMasterConfig {
slave_address: Some(address),
speed_hz: None,
};
let Ok(mut master) = I2CMaster::open(&device, &i2c_config) else {
print!(" ");
continue;
};
match master.smbus_read_byte(0xFF) {
Ok(_) => print!("+"),
Err(e) if e.kind() == io::ErrorKind::HostUnreachable => print!("-"),
_ => print!("X"),
}
}
println!();
}
Ok(())
}
fn run_tmp<T: TmpSensorDefinition, P: AsRef<Path>>(device: P, address: Address) -> io::Result<()> {
let mut poll = Poll::new()?;
let term = Term::open()?;
let term_fd = term.as_raw_fd();
let mut term = Terminal::new(term)?;
poll.add(&term_fd)?;
let mut tmp = TmpSensor::<T>::open(device, address)?;
let mut local_temp = 0.0;
let mut remote_temp = 0.0;
let mut last_poll = Instant::now();
loop {
if last_poll.elapsed() >= Duration::from_millis(500) {
local_temp = tmp.read_local_temperature().unwrap_or_default();
remote_temp = tmp.read_remote_temperature().unwrap_or_default();
last_poll = Instant::now();
}
term.draw(|frame| {
let rect = frame.size();
let hint_style = Style::default().bg(Color::Blue).fg(Color::White);
let header = Row::new(vec![Cell::from("Name"), Cell::from("Value")])
.height(1)
.style(hint_style);
let rows = vec![
Row::new(vec![
Cell::from("Local"),
Cell::from(format!("{local_temp:.04}")),
]),
Row::new(vec![
Cell::from("Remote"),
Cell::from(format!("{remote_temp:.04}")),
]),
];
let table = Table::new(rows)
.widths(&[Constraint::Min(6), Constraint::Percentage(100)])
.header(header);
frame.render_widget(table, rect);
})?;
let p = poll.wait(Some(Duration::from_millis(100)))?;
match p {
PollEvent::Ready(_) => {
let key = term.backend_mut().read_key()?;
if key == TermKey::Char('q') {
break;
}
}
PollEvent::Error(_, error) => {
return Err(error);
}
PollEvent::None => (),
}
}
Ok(())
}
fn run(action: Subcommand) -> io::Result<()> {
match action {
Subcommand::Scan { device } => run_scan(device),
Subcommand::Dump { address, device } => run_dump(device, address),
Subcommand::TMP421 { address, device } => run_tmp::<Tmp421, _>(device, address),
Subcommand::TMP451 { address, device } => run_tmp::<Tmp451, _>(device, address),
}
}
fn main() -> ExitCode {
let args = Args::parse();
match run(args.command) {
Ok(()) => ExitCode::SUCCESS,
Err(error) => {
eprintln!("{error}");
ExitCode::FAILURE
}
}
}
+15 -1
View File
@@ -1 +1,15 @@
fn main() {}
#![feature(rustc_private)]
use std::{io, process::ExitCode};
use runtime::{abi::system, rt::system::system_control};
fn main() -> ExitCode {
if let Err(error) = system_control::<system::Reboot>(&mut [], &()) {
let error = io::Error::from(error);
eprintln!("{error}");
ExitCode::FAILURE
} else {
ExitCode::SUCCESS
}
}
+1 -160
View File
@@ -1,160 +1 @@
use std::{
io::{self, Write, stdout},
path::{Path, PathBuf},
process::ExitCode,
thread,
time::Duration,
};
use clap::Parser;
use cross::{
i2c::{I2CMaster, I2CMasterConfig},
spi::{SpiConfig, SpiDevice},
};
#[derive(Debug, Parser)]
struct Args {
#[clap(subcommand)]
command: Subcommand,
}
#[derive(Debug, clap::Subcommand)]
enum Subcommand {
M41T80 {
#[clap(short, long, default_value_t = 0x40, help = "I²C device address")]
address: u8,
#[clap(help = "I²C controller path")]
device: PathBuf,
},
JedecFlash {
#[clap(short, long, default_value_t = 4 * 1024 * 1024, help = "SPI flash capacity")]
capacity: usize,
#[clap(help = "SPI device path")]
device: PathBuf,
},
}
struct M41T80 {
master: I2CMaster,
}
#[derive(Debug, PartialEq)]
struct Time {
year: u16,
mon: u8,
day: u8,
hour: u8,
min: u8,
sec: u8,
millis: u16,
}
impl M41T80 {
fn open<P: AsRef<Path>>(path: P, config: &I2CMasterConfig) -> io::Result<Self> {
let master = I2CMaster::open(path, config)?;
Ok(Self { master })
}
fn read_time(&mut self) -> io::Result<Time> {
let mut buffer = [0; 9];
self.master.smbus_read(0x00, &mut buffer)?;
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 run_m41t80(device: PathBuf, address: u8) -> io::Result<()> {
// let device = "/dev/i2c0";
let i2c_config = I2CMasterConfig {
slave_address: Some(address as _),
speed_hz: None,
};
let mut m41t80 = M41T80::open(device, &i2c_config)?;
loop {
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 run_jedec(device: PathBuf, capacity: usize) -> io::Result<()> {
let config = SpiConfig::default();
let mut spi = SpiDevice::open(device, &config)?;
let tx = [0x13, 0x00, 0x00, 0x00, 0x00];
let mut rx = [0; 5];
spi.transfer(&tx, &mut rx).unwrap();
let mut memory = vec![0xFF; capacity];
const READ_BUFFER: usize = 1024;
let tx_word = [0xFF; READ_BUFFER];
let mut rx_word = [0; READ_BUFFER];
let mut bytes_read = 0;
for i in 0..capacity / READ_BUFFER {
if spi.transfer(&tx_word, &mut rx_word)? != READ_BUFFER {
break;
}
memory[i * READ_BUFFER..i * READ_BUFFER + READ_BUFFER].copy_from_slice(&rx_word);
bytes_read += READ_BUFFER;
}
for (i, &byte) in memory.iter().enumerate() {
if i >= bytes_read {
print!("??");
} else {
print!("{:02x}", byte);
}
if (i + 1) % 2 == 0 {
print!(" ");
}
if (i + 1) % 32 == 0 {
println!();
}
}
Ok(())
}
fn run(args: Args) -> io::Result<()> {
match args.command {
Subcommand::M41T80 { address, device } => run_m41t80(device, address),
Subcommand::JedecFlash { capacity, device } => run_jedec(device, capacity),
}
}
fn main() -> ExitCode {
logsink::setup_logging(true);
let args = Args::parse();
match run(args) {
Ok(()) => ExitCode::SUCCESS,
Err(error) => {
eprintln!("{error}");
ExitCode::FAILURE
}
}
}
fn main() {}
+1
View File
@@ -38,6 +38,7 @@ const PROGRAMS: &[(&str, &str)] = &[
("env", "bin/env"),
("grep", "bin/grep"),
("hexd", "bin/hexd"),
("i2c", "bin/i2c"),
("ln", "bin/ln"),
("login", "sbin/login"),
("ls", "bin/ls"),