-
Notifications
You must be signed in to change notification settings - Fork 109
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #542 from alistair23/alistair/spi
spi_controller: Initial support
- Loading branch information
Showing
7 changed files
with
389 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
[package] | ||
name = "libtock_spi_controller" | ||
version = "0.1.0" | ||
authors = [ | ||
"Tock Project Developers <[email protected]>", | ||
"Alistair Francis <[email protected]>", | ||
] | ||
license = "Apache-2.0 OR MIT" | ||
edition = "2021" | ||
repository = "https://www.github.com/tock/libtock-rs" | ||
rust-version.workspace = true | ||
description = "libtock SPI controller driver" | ||
|
||
[dependencies] | ||
libtock_platform = { path = "../../platform" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,224 @@ | ||
#![no_std] | ||
|
||
use core::cell::Cell; | ||
use libtock_platform as platform; | ||
use libtock_platform::allow_rw::AllowRw; | ||
use libtock_platform::share; | ||
use libtock_platform::subscribe::Subscribe; | ||
use libtock_platform::AllowRo; | ||
use libtock_platform::{DefaultConfig, ErrorCode, Syscalls}; | ||
|
||
pub struct SpiController<S: Syscalls, C: Config = DefaultConfig>(S, C); | ||
|
||
impl<S: Syscalls, C: Config> SpiController<S, C> { | ||
pub fn exists() -> Result<(), ErrorCode> { | ||
S::command(DRIVER_NUM, spi_controller_cmd::EXISTS, 0, 0).to_result() | ||
} | ||
|
||
/// # Summary | ||
/// | ||
/// Perform an I2C write followed by a read. | ||
/// | ||
/// TODO: Add async support | ||
/// | ||
/// # Parameter | ||
/// | ||
/// * `addr`: Slave device address | ||
/// * `buf`: Buffer | ||
/// * `w_len`: Number of bytes to write from @w_buf | ||
/// * `r_len`: Number of bytes to read into @r_buf | ||
/// | ||
/// # Returns | ||
/// On success: Returns Ok(()) | ||
/// On failure: Err(ErrorCode) | ||
pub fn spi_controller_write_read_sync( | ||
w_buf: &[u8], | ||
r_buf: &mut [u8], | ||
len: u32, | ||
) -> Result<(), ErrorCode> { | ||
if len as usize > w_buf.len() || len as usize > r_buf.len() { | ||
return Err(ErrorCode::NoMem); | ||
} | ||
|
||
let called: Cell<Option<(u32, u32, u32)>> = Cell::new(None); | ||
share::scope::< | ||
( | ||
AllowRw<_, DRIVER_NUM, { rw_allow::READ }>, | ||
AllowRo<_, DRIVER_NUM, { ro_allow::WRITE }>, | ||
Subscribe<_, DRIVER_NUM, { subscribe::COMPLETE }>, | ||
), | ||
_, | ||
_, | ||
>(|handle| { | ||
let (allow_rw, allow_ro, subscribe) = handle.split(); | ||
S::allow_rw::<C, DRIVER_NUM, { rw_allow::READ }>(allow_rw, r_buf)?; | ||
S::allow_ro::<C, DRIVER_NUM, { ro_allow::WRITE }>(allow_ro, w_buf)?; | ||
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::COMPLETE }>(subscribe, &called)?; | ||
|
||
S::command(DRIVER_NUM, spi_controller_cmd::READ_WRITE_BYTES, len, 0).to_result()?; | ||
|
||
loop { | ||
S::yield_wait(); | ||
if let Some((r0, status, _)) = called.get() { | ||
assert_eq!(r0, len); | ||
return match status { | ||
0 => Ok(()), | ||
e_status => Err(e_status.try_into().unwrap_or(ErrorCode::Fail)), | ||
}; | ||
} | ||
} | ||
}) | ||
} | ||
|
||
pub fn spi_controller_write_sync(w_buf: &[u8], len: u32) -> Result<(), ErrorCode> { | ||
if len as usize > w_buf.len() { | ||
return Err(ErrorCode::NoMem); | ||
} | ||
|
||
let called: Cell<Option<(u32, u32, u32)>> = Cell::new(None); | ||
share::scope::< | ||
( | ||
AllowRo<_, DRIVER_NUM, { ro_allow::WRITE }>, | ||
Subscribe<_, DRIVER_NUM, { subscribe::COMPLETE }>, | ||
), | ||
_, | ||
_, | ||
>(|handle| { | ||
let (allow_ro, subscribe) = handle.split(); | ||
S::allow_ro::<C, DRIVER_NUM, { ro_allow::WRITE }>(allow_ro, w_buf)?; | ||
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::COMPLETE }>(subscribe, &called)?; | ||
|
||
S::command(DRIVER_NUM, spi_controller_cmd::READ_WRITE_BYTES, len, 0).to_result()?; | ||
|
||
loop { | ||
S::yield_wait(); | ||
if let Some((r0, status, _)) = called.get() { | ||
assert_eq!(r0, len); | ||
return match status { | ||
0 => Ok(()), | ||
e_status => Err(e_status.try_into().unwrap_or(ErrorCode::Fail)), | ||
}; | ||
} | ||
} | ||
}) | ||
} | ||
|
||
pub fn spi_controller_read_sync(r_buf: &mut [u8], len: u32) -> Result<(), ErrorCode> { | ||
if len as usize > r_buf.len() { | ||
return Err(ErrorCode::NoMem); | ||
} | ||
|
||
let called: Cell<Option<(u32, u32, u32)>> = Cell::new(None); | ||
share::scope::< | ||
( | ||
AllowRw<_, DRIVER_NUM, { rw_allow::READ }>, | ||
Subscribe<_, DRIVER_NUM, { subscribe::COMPLETE }>, | ||
), | ||
_, | ||
_, | ||
>(|handle| { | ||
let (allow_rw, subscribe) = handle.split(); | ||
S::allow_rw::<C, DRIVER_NUM, { rw_allow::READ }>(allow_rw, r_buf)?; | ||
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::COMPLETE }>(subscribe, &called)?; | ||
|
||
S::command(DRIVER_NUM, spi_controller_cmd::READ_BYTES, len, 0).to_result()?; | ||
|
||
loop { | ||
S::yield_wait(); | ||
if let Some((r0, status, _)) = called.get() { | ||
assert_eq!(r0, len); | ||
return match status { | ||
0 => Ok(()), | ||
e_status => Err(e_status.try_into().unwrap_or(ErrorCode::Fail)), | ||
}; | ||
} | ||
} | ||
}) | ||
} | ||
|
||
pub fn spi_controller_inplace_write_read_sync( | ||
r_buf: &mut [u8], | ||
len: u32, | ||
) -> Result<(), ErrorCode> { | ||
if len as usize > r_buf.len() { | ||
return Err(ErrorCode::NoMem); | ||
} | ||
|
||
let called: Cell<Option<(u32, u32, u32)>> = Cell::new(None); | ||
share::scope::< | ||
( | ||
AllowRw<_, DRIVER_NUM, { rw_allow::READ }>, | ||
Subscribe<_, DRIVER_NUM, { subscribe::COMPLETE }>, | ||
), | ||
_, | ||
_, | ||
>(|handle| { | ||
let (allow_rw, subscribe) = handle.split(); | ||
S::allow_rw::<C, DRIVER_NUM, { rw_allow::READ }>(allow_rw, r_buf)?; | ||
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::COMPLETE }>(subscribe, &called)?; | ||
|
||
S::command( | ||
DRIVER_NUM, | ||
spi_controller_cmd::INPLACE_READ_WRITE_BYTES, | ||
len, | ||
0, | ||
) | ||
.to_result()?; | ||
|
||
loop { | ||
S::yield_wait(); | ||
if let Some((r0, status, _)) = called.get() { | ||
assert_eq!(r0, len); | ||
return match status { | ||
0 => Ok(()), | ||
e_status => Err(e_status.try_into().unwrap_or(ErrorCode::Fail)), | ||
}; | ||
} | ||
} | ||
}) | ||
} | ||
} | ||
|
||
/// System call configuration trait for `SpiController`. | ||
pub trait Config: | ||
platform::allow_ro::Config + platform::allow_rw::Config + platform::subscribe::Config | ||
{ | ||
} | ||
impl<T: platform::allow_ro::Config + platform::allow_rw::Config + platform::subscribe::Config> | ||
Config for T | ||
{ | ||
} | ||
|
||
// ----------------------------------------------------------------------------- | ||
// Driver number and command IDs | ||
// ----------------------------------------------------------------------------- | ||
const DRIVER_NUM: u32 = 0x20001; | ||
|
||
#[allow(unused)] | ||
mod subscribe { | ||
pub const COMPLETE: u32 = 0; | ||
} | ||
|
||
#[allow(unused)] | ||
mod ro_allow { | ||
pub const WRITE: u32 = 0; | ||
} | ||
|
||
#[allow(unused)] | ||
mod rw_allow { | ||
pub const READ: u32 = 0; | ||
} | ||
|
||
#[allow(unused)] | ||
mod spi_controller_cmd { | ||
pub const EXISTS: u32 = 0; | ||
pub const READ_WRITE_BYTES: u32 = 2; | ||
pub const SET_BAUD: u32 = 5; | ||
pub const GET_BAUD: u32 = 6; | ||
pub const SET_PHASE: u32 = 7; | ||
pub const GET_PHASE: u32 = 8; | ||
pub const SET_POLARITY: u32 = 9; | ||
pub const GET_POLARITY: u32 = 10; | ||
pub const READ_BYTES: u32 = 11; | ||
pub const INPLACE_READ_WRITE_BYTES: u32 = 12; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
//! This sample demonstrates setting up the SPI controller (assuming board has support) | ||
#![no_main] | ||
#![no_std] | ||
use core::fmt::Write; | ||
use libtock::console::Console; | ||
use libtock::runtime::{set_main, stack_size}; | ||
use libtock::spi_controller::SpiController; | ||
|
||
set_main! {main} | ||
stack_size! {0x400} | ||
|
||
const OPERATION_LEN: usize = 0x08; | ||
|
||
fn main() { | ||
let tx_buf: [u8; OPERATION_LEN] = [0x12; OPERATION_LEN]; | ||
let mut rx_buf: [u8; OPERATION_LEN] = [0; OPERATION_LEN]; | ||
|
||
writeln!(Console::writer(), "spi-controller: write-read\r").unwrap(); | ||
if let Err(why) = | ||
SpiController::spi_controller_write_read_sync(&tx_buf, &mut rx_buf, OPERATION_LEN as u32) | ||
{ | ||
writeln!( | ||
Console::writer(), | ||
"spi-controller: write-read operation failed {:?}\r", | ||
why | ||
) | ||
.unwrap(); | ||
} else { | ||
writeln!( | ||
Console::writer(), | ||
"spi-controller: write-read: wrote {:x?}: read {:x?}\r", | ||
tx_buf, | ||
rx_buf | ||
) | ||
.unwrap(); | ||
} | ||
|
||
writeln!(Console::writer(), "spi-controller: write\r").unwrap(); | ||
if let Err(why) = SpiController::spi_controller_write_sync(&tx_buf, OPERATION_LEN as u32) { | ||
writeln!( | ||
Console::writer(), | ||
"spi-controller: write operation failed {:?}\r", | ||
why | ||
) | ||
.unwrap(); | ||
} else { | ||
writeln!(Console::writer(), "spi-controller: wrote {:x?}\r", tx_buf).unwrap(); | ||
} | ||
|
||
writeln!(Console::writer(), "spi-controller: read\r").unwrap(); | ||
if let Err(why) = SpiController::spi_controller_read_sync(&mut rx_buf, OPERATION_LEN as u32) { | ||
writeln!( | ||
Console::writer(), | ||
"spi-controller: read operation failed {:?}\r", | ||
why | ||
) | ||
.unwrap(); | ||
} else { | ||
writeln!(Console::writer(), "spi-controller: read {:x?}\r", rx_buf).unwrap(); | ||
} | ||
|
||
writeln!(Console::writer(), "spi-controller: inplace write-read\r").unwrap(); | ||
if let Err(why) = | ||
SpiController::spi_controller_inplace_write_read_sync(&mut rx_buf, OPERATION_LEN as u32) | ||
{ | ||
writeln!( | ||
Console::writer(), | ||
"spi-controller: inplace write-read operation failed {:?}\r", | ||
why | ||
) | ||
.unwrap(); | ||
} else { | ||
writeln!( | ||
Console::writer(), | ||
"spi-controller: inplace write-read: wrote {:x?}: read {:x?}\r", | ||
tx_buf, | ||
rx_buf | ||
) | ||
.unwrap(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.