diff --git a/services/cram-hal-service/src/api.rs b/services/cram-hal-service/src/api.rs index 5331969d3..6573037ed 100644 --- a/services/cram-hal-service/src/api.rs +++ b/services/cram-hal-service/src/api.rs @@ -41,6 +41,9 @@ pub enum Opcode { // blocking scalar ConfigureUdmaEvent = 8, + /// I2C operations + I2c = 9, + /// Exit server Quit = 255, @@ -105,3 +108,37 @@ pub struct IoxConfigMessage { pub slow_slew: Option, pub strength: Option, } + +#[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, PartialEq, Eq)] +pub enum I2cTransactionType { + Write, + Read, + ReadRepeatedStart, +} + +#[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] +pub enum I2cResult { + /// For the outbound message holder + Pending, + /// Returns # of bytes read or written if successful + Ack(usize), + /// An error occurred. + Nack, +} + +#[derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] +pub struct I2cTransaction { + pub i2c_type: I2cTransactionType, + pub device: u8, + pub address: u8, + pub data: Vec, + pub result: I2cResult, +} + +#[derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)] +pub struct I2cTransactions { + pub transactions: Vec, +} +impl From> for I2cTransactions { + fn from(value: Vec) -> Self { Self { transactions: value } } +} diff --git a/services/cram-hal-service/src/lib.rs b/services/cram-hal-service/src/lib.rs index f0b4e2e5a..4f78f8771 100644 --- a/services/cram-hal-service/src/lib.rs +++ b/services/cram-hal-service/src/lib.rs @@ -1,16 +1,19 @@ pub mod api; +pub mod i2c_lib; pub mod iox_lib; pub mod keyboard; pub mod trng; use api::Opcode; use cramium_hal::udma::{EventChannel, PeriphEventType, PeriphId, UdmaGlobalConfig}; +pub use i2c_lib::*; pub use iox_lib::*; use num_traits::*; /// Do not change this constant, it is hard-coded into libraries in order to break /// circular dependencies on the IFRAM block. pub const SERVER_NAME_CRAM_HAL: &str = "_Cramium-SoC HAL_"; +pub const PERCLK: u32 = 100_000_000; use core::sync::atomic::{AtomicU32, Ordering}; static REFCOUNT: AtomicU32 = AtomicU32::new(0); diff --git a/services/cram-hal-service/src/main.rs b/services/cram-hal-service/src/main.rs index 6975e7712..66d5dac47 100644 --- a/services/cram-hal-service/src/main.rs +++ b/services/cram-hal-service/src/main.rs @@ -4,7 +4,7 @@ mod hw; use api::*; use cramium_hal::{ iox, - udma::{EventChannel, GlobalConfig, PeriphId}, + udma::{EventChannel, GlobalConfig, I2cApi, PeriphId}, }; #[cfg(feature = "quantum-timer")] use utralib::utra; @@ -139,6 +139,27 @@ fn main() { .expect("couldn't map UDMA global control"); let udma_global = GlobalConfig::new(udma_global_csr.as_mut_ptr() as *mut u32); + let i2c_channel = cramium_hal::board::setup_i2c_pins(&iox); + udma_global.clock_on(PeriphId::from(i2c_channel)); + let i2c_pages = xous::syscall::map_memory( + xous::MemoryAddress::new(cramium_hal::board::I2C_IFRAM_ADDR), + None, + 4096, + xous::MemoryFlags::R | xous::MemoryFlags::W, + ) + .expect("couldn't claim I2C IFRAM page"); + + let i2c_ifram = unsafe { + cramium_hal::ifram::IframRange::from_raw_parts( + cramium_hal::board::I2C_IFRAM_ADDR, + i2c_pages.as_ptr() as usize, + i2c_pages.len(), + ) + }; + let mut i2c = unsafe { + cramium_hal::udma::I2c::new_with_ifram(i2c_channel, 400_000, cram_hal_service::PERCLK, i2c_ifram) + }; + // -------------------- begin timer workaround code // This code should go away with NTO as we have a proper, private ticktimer unit. let mut pio_ss = xous_pio::PioSharedState::new(); @@ -338,6 +359,34 @@ fn main() { udma_global.map_event_with_offset(periph, event_offset, to_channel); } } + Opcode::I2c => { + let mut buf = unsafe { + xous_ipc::Buffer::from_memory_message_mut(msg.body.memory_message_mut().unwrap()) + }; + let mut list = buf.to_original::().expect("I2c message format error"); + for transaction in list.transactions.iter_mut() { + match transaction.i2c_type { + I2cTransactionType::Write => { + match i2c.i2c_write(transaction.device, transaction.address, &transaction.data) { + Ok(b) => transaction.result = I2cResult::Ack(b), + _ => transaction.result = I2cResult::Nack, + } + } + I2cTransactionType::Read | I2cTransactionType::ReadRepeatedStart => { + match i2c.i2c_read( + transaction.device, + transaction.address, + &mut transaction.data, + transaction.i2c_type == I2cTransactionType::ReadRepeatedStart, + ) { + Ok(b) => transaction.result = I2cResult::Ack(b), + _ => transaction.result = I2cResult::Nack, + } + } + } + } + buf.replace(list).expect("I2c message format error"); + } Opcode::InvalidCall => { log::error!("Invalid opcode received: {:?}", msg); }