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
+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() },
}
}