mirror of
https://github.com/saymrwulf/xous-core.git
synced 2026-11-15 08:30:02 +00:00
add APIs and glue logic for implementation of shared I2C resource
The API now provides, in addition to stand-alone read/write transactions, the ability to send a group of transactions that will be executed in-order without any other transactions in between. This allows better compatibility with devices that are designed assuming they are the sole target on the bus. Note that the current implementation does not thread out the I2C handler into its own thread so the service provider blocks until all the I2C transactions are done. Adding a thread to handle the I2C stuff would allow other requests to make progress but at the moment this isn't required.
This commit is contained in:
parent
a11f2f7f54
commit
7cceb9bea4
3 changed files with 90 additions and 1 deletions
|
|
@ -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<iox::IoxEnable>,
|
||||
pub strength: Option<iox::IoxDriveStrength>,
|
||||
}
|
||||
|
||||
#[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<u8>,
|
||||
pub result: I2cResult,
|
||||
}
|
||||
|
||||
#[derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
|
||||
pub struct I2cTransactions {
|
||||
pub transactions: Vec<I2cTransaction>,
|
||||
}
|
||||
impl From<Vec<I2cTransaction>> for I2cTransactions {
|
||||
fn from(value: Vec<I2cTransaction>) -> Self { Self { transactions: value } }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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::<I2cTransactions, _>().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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue