spi: initial spi device support
This commit is contained in:
@@ -16,3 +16,4 @@ tock-registers.workspace = true
|
||||
log.workspace = true
|
||||
bytemuck.workspace = true
|
||||
futures-util.workspace = true
|
||||
async-trait.workspace = true
|
||||
|
||||
@@ -9,8 +9,13 @@ use device_tree::{
|
||||
};
|
||||
use libk::error::Error;
|
||||
|
||||
use crate::mbox::{Bcm2835Mbox, Bcm2835VcClock};
|
||||
|
||||
const CLK_VPU: u32 = 0x14;
|
||||
|
||||
struct Cprman {
|
||||
clk_osc: ClockHandle,
|
||||
mbox: Arc<Bcm2835Mbox>,
|
||||
}
|
||||
|
||||
impl Device for Cprman {
|
||||
@@ -26,8 +31,15 @@ impl Device for Cprman {
|
||||
|
||||
impl ClockController for Cprman {
|
||||
fn clock_rate(&self, clock: Option<u32>) -> Result<Hertz, Error> {
|
||||
let _ = clock;
|
||||
todo!()
|
||||
let vc_clock = match clock {
|
||||
Some(CLK_VPU) => Bcm2835VcClock::Core,
|
||||
Some(n) => todo!("clock_rate({n:#02x})"),
|
||||
None => return Err(Error::InvalidArgument),
|
||||
};
|
||||
|
||||
let rate = self.mbox.vc_clock_rate(vc_clock)?;
|
||||
|
||||
Ok(rate)
|
||||
}
|
||||
|
||||
fn set_clock_rate(&self, clock: Option<u32>, rate: Hertz) -> Result<Hertz, Error> {
|
||||
@@ -65,10 +77,13 @@ device_tree_driver! {
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let _base = node.map_base(context, 0)?;
|
||||
let mbox = Node::by_compatible("brcm,bcm2835-mbox", true).unwrap().device().unwrap();
|
||||
let mbox = Arc::downcast::<Bcm2835Mbox>(mbox.as_any()).unwrap();
|
||||
let clk_osc = node.clock(0)?;
|
||||
|
||||
let cprman = Arc::new(Cprman {
|
||||
clk_osc
|
||||
clk_osc,
|
||||
mbox,
|
||||
});
|
||||
|
||||
node.make_clock_controller(cprman.clone());
|
||||
|
||||
@@ -8,3 +8,4 @@ mod cprman;
|
||||
mod gpio;
|
||||
mod i2c;
|
||||
mod mbox;
|
||||
mod spi;
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
use core::{cell::UnsafeCell, mem::MaybeUninit};
|
||||
use core::{any::Any, cell::UnsafeCell, mem::MaybeUninit};
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use device_api::device::{Device, DeviceInitContext};
|
||||
use device_api::{
|
||||
clock::Hertz,
|
||||
device::{Device, DeviceInitContext},
|
||||
};
|
||||
use device_tree::driver::{Node, ProbeContext, device_tree_driver};
|
||||
use kernel_arch_aarch64::mem::table::L3;
|
||||
use libk::device::{
|
||||
@@ -61,7 +64,13 @@ register_structs! {
|
||||
}
|
||||
}
|
||||
|
||||
struct Bcm2835Mbox {
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
#[repr(u32)]
|
||||
pub(crate) enum Bcm2835VcClock {
|
||||
Core = 4,
|
||||
}
|
||||
|
||||
pub(crate) struct Bcm2835Mbox {
|
||||
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
|
||||
}
|
||||
|
||||
@@ -85,13 +94,20 @@ impl Device for Bcm2835Mbox {
|
||||
fn display_name(&self) -> &str {
|
||||
"bcm2835-mbox"
|
||||
}
|
||||
|
||||
fn as_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Bcm2835Mbox {
|
||||
const RAM_TO_GPU_OFFSET: u32 = 0x40000000;
|
||||
|
||||
const CHANNEL_FRAMEBUFFER: u32 = 1;
|
||||
const CHANNEL_ARM2VC: u32 = 8;
|
||||
|
||||
pub unsafe fn call_raw(&self, buffer: PhysicalAddress, channel: u32) -> Result<(), Error> {
|
||||
log::info!("bcm2835-mbox: mbox call buffer={buffer:#x}, channel={channel}");
|
||||
log::debug!("bcm2835-mbox: mbox call buffer={buffer:#x}, channel={channel}");
|
||||
|
||||
let address = buffer
|
||||
.try_into_u32()
|
||||
@@ -125,10 +141,31 @@ impl Bcm2835Mbox {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn configure_framebuffer(
|
||||
&self,
|
||||
mode_info: &DisplayMode,
|
||||
) -> Result<FramebufferResponse, Error> {
|
||||
pub fn vc_clock_rate(&self, clock: Bcm2835VcClock) -> Result<Hertz, Error> {
|
||||
let mut buffer = PageBox::new_slice(0u32, 4096 / 4)?;
|
||||
buffer[0] = 9 * 4; // buffer size
|
||||
buffer[1] = 0; // process request
|
||||
|
||||
// Tag 1 (Response)
|
||||
buffer[2] = 0x00030002; // tag
|
||||
buffer[3] = 0; // value buffer size
|
||||
buffer[4] = 0; // request
|
||||
buffer[5] = clock as u32; // value buffer[0]
|
||||
buffer[6] = 0; // value buffer[1]
|
||||
buffer[7] = 0;
|
||||
|
||||
// End tag
|
||||
buffer[8] = 0;
|
||||
|
||||
unsafe { self.call_raw(buffer.as_physical_address(), Self::CHANNEL_ARM2VC) }?;
|
||||
if buffer[4] == 0 || buffer[1] == 0 {
|
||||
todo!();
|
||||
}
|
||||
let rate = Hertz::from(buffer[6]);
|
||||
Ok(rate)
|
||||
}
|
||||
|
||||
fn configure_framebuffer(&self, mode_info: &DisplayMode) -> Result<FramebufferResponse, Error> {
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(C)]
|
||||
struct FramebufferRequest {
|
||||
@@ -158,7 +195,7 @@ impl Bcm2835Mbox {
|
||||
}))?;
|
||||
|
||||
// TODO flush cache for buffer
|
||||
unsafe { self.call_raw(buffer.as_physical_address(), 0x01) }?;
|
||||
unsafe { self.call_raw(buffer.as_physical_address(), Self::CHANNEL_FRAMEBUFFER) }?;
|
||||
|
||||
let buffer = buffer.get_mut();
|
||||
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
use core::iter;
|
||||
|
||||
use alloc::{boxed::Box, sync::Arc};
|
||||
use async_trait::async_trait;
|
||||
use device_api::{
|
||||
clock::{ClockHandle, Hertz},
|
||||
device::{Device, DeviceInitContext},
|
||||
interrupt::IrqHandle,
|
||||
spi::SpiController,
|
||||
};
|
||||
use device_tree::driver::{Node, ProbeContext, device_tree_driver};
|
||||
use libk::{device::manager::DEVICE_REGISTRY, error::Error};
|
||||
use libk_mm::device::DeviceMemoryIo;
|
||||
use libk_util::sync::IrqSafeSpinlock;
|
||||
use tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
register_bitfields, register_structs,
|
||||
registers::ReadWrite,
|
||||
};
|
||||
use yggdrasil_abi::io::device::spi::{SpiClockPhase, SpiClockPolarity, SpiConfig};
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
CS [
|
||||
LEN_LONG OFFSET(25) NUMBITS(1) [],
|
||||
DMA_LEN OFFSET(24) NUMBITS(1) [],
|
||||
CSPOL2 OFFSET(23) NUMBITS(1) [],
|
||||
CSPOL1 OFFSET(22) NUMBITS(1) [],
|
||||
CSPOL0 OFFSET(21) NUMBITS(1) [],
|
||||
RXF OFFSET(20) NUMBITS(1) [],
|
||||
RXR OFFSET(19) NUMBITS(1) [],
|
||||
TXD OFFSET(18) NUMBITS(1) [],
|
||||
RXD OFFSET(17) NUMBITS(1) [],
|
||||
DONE OFFSET(16) NUMBITS(1) [],
|
||||
LEN OFFSET(13) NUMBITS(1) [],
|
||||
REN OFFSET(12) NUMBITS(1) [],
|
||||
ADCS OFFSET(11) NUMBITS(1) [],
|
||||
INTR OFFSET(10) NUMBITS(1) [],
|
||||
INTD OFFSET(9) NUMBITS(1) [],
|
||||
DMAEN OFFSET(8) NUMBITS(1) [],
|
||||
TA OFFSET(7) NUMBITS(1) [],
|
||||
CSPOL OFFSET(6) NUMBITS(1) [],
|
||||
CLEAR OFFSET(4) NUMBITS(2) [],
|
||||
CPOL OFFSET(3) NUMBITS(1) [
|
||||
IdleLow = 0,
|
||||
IdleHigh = 1
|
||||
],
|
||||
CPHA OFFSET(2) NUMBITS(1) [
|
||||
CaptureOnFirstTransition = 0,
|
||||
CaptureOnSecondTransition = 1
|
||||
],
|
||||
CS OFFSET(0) NUMBITS(2) [],
|
||||
]
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
Regs {
|
||||
(0x00 => CS: ReadWrite<u32, CS::Register>),
|
||||
(0x04 => FIFO: ReadWrite<u32>),
|
||||
(0x08 => CLK: ReadWrite<u32>),
|
||||
(0x0C => DLEN: ReadWrite<u32>),
|
||||
(0x10 => LTOH: ReadWrite<u32>),
|
||||
(0x14 => DC: ReadWrite<u32>),
|
||||
(0x18 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Spi {
|
||||
name: &'static str,
|
||||
clock: ClockHandle,
|
||||
irq: IrqHandle,
|
||||
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
|
||||
}
|
||||
|
||||
impl Device for Spi {
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
let _ = &self.irq;
|
||||
let regs = self.regs.lock();
|
||||
regs.CS.set(0);
|
||||
|
||||
DEVICE_REGISTRY.spi.register_bus(self.clone()).ok();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl Regs {
|
||||
fn start_transfer(&self, core_clock: Hertz, config: &SpiConfig) -> Result<(), Error> {
|
||||
let want_clock = Hertz::from(config.frequency.unwrap_or(100000));
|
||||
let divider = Hertz::divider(core_clock, want_clock).ok_or(Error::InvalidArgument)?;
|
||||
let divider: u16 = divider.try_into().map_err(|_| Error::InvalidArgument)?;
|
||||
let divider = divider.wrapping_add(1) & !1;
|
||||
|
||||
let mut cs = CS::REN::SET + CS::TA::SET;
|
||||
if let Some(cs_index) = config.chip_select_index
|
||||
&& cs_index < 3
|
||||
{
|
||||
cs += CS::CS.val(cs_index);
|
||||
}
|
||||
if let Some(cpol) = config.clock_polarity {
|
||||
match cpol {
|
||||
SpiClockPolarity::IdleLow => cs += CS::CPOL::IdleLow,
|
||||
SpiClockPolarity::IdleHigh => cs += CS::CPOL::IdleHigh,
|
||||
}
|
||||
}
|
||||
if let Some(cpha) = config.clock_phase {
|
||||
match cpha {
|
||||
SpiClockPhase::CaptureOnFirstTransition => cs += CS::CPHA::CaptureOnFirstTransition,
|
||||
SpiClockPhase::CaptureOnSecondTransition => {
|
||||
cs += CS::CPHA::CaptureOnSecondTransition
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.CLK.set(divider as u32);
|
||||
self.CS.write(cs);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn tx_rx_byte(&self, tx: u8) -> Result<Option<u8>, Error> {
|
||||
// Wait for tx
|
||||
loop {
|
||||
let status = self.CS.extract();
|
||||
if status.matches_all(CS::TXD::SET) {
|
||||
break;
|
||||
}
|
||||
if status.matches_all(CS::DONE::SET) {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
self.FIFO.set(tx as u32);
|
||||
// Wait for rx
|
||||
loop {
|
||||
let status = self.CS.extract();
|
||||
if status.matches_all(CS::RXD::SET) {
|
||||
break;
|
||||
}
|
||||
if status.matches_all(CS::DONE::SET) {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
Ok(Some(self.FIFO.get() as u8))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl SpiController for Spi {
|
||||
async fn spi_transfer(
|
||||
&self,
|
||||
tx_buffer: &[u8],
|
||||
rx_buffer: &mut [u8],
|
||||
config: &SpiConfig,
|
||||
) -> Result<usize, Error> {
|
||||
// TODO interrupt/DMA
|
||||
log::debug!("{}: spi_transfer {} bytes", self.name, rx_buffer.len());
|
||||
let core_clock = self.clock.rate()?;
|
||||
let regs = self.regs.lock();
|
||||
regs.start_transfer(core_clock, config)?;
|
||||
|
||||
let mut len = 0;
|
||||
for (&tx, rx) in iter::zip(tx_buffer, rx_buffer) {
|
||||
match regs.tx_rx_byte(tx)? {
|
||||
Some(b) => {
|
||||
*rx = b;
|
||||
len += 1;
|
||||
}
|
||||
None => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["brcm,bcm2835-spi"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let name = node.name().unwrap_or("bcm2835-spi");
|
||||
let base = node.map_base(context, 0)?;
|
||||
let irq = node.interrupt(0)?;
|
||||
let clock = node.clock(0)?;
|
||||
|
||||
let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
|
||||
|
||||
let spi = Arc::new(Spi {
|
||||
regs: IrqSafeSpinlock::new(regs),
|
||||
name,
|
||||
clock,
|
||||
irq
|
||||
});
|
||||
|
||||
Some(spi)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ authors = ["Mark Poliakov <mark@alnyan.me>"]
|
||||
[dependencies]
|
||||
yggdrasil-abi.workspace = true
|
||||
device-api-macros = { workspace = true, optional = true }
|
||||
async-trait.workspace = true
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
@@ -26,6 +26,16 @@ pub trait IntoHertz {
|
||||
}
|
||||
}
|
||||
|
||||
impl Hertz {
|
||||
pub fn divider(base: Self, desired: Self) -> Option<u32> {
|
||||
if desired > base || desired.0 == 0 {
|
||||
return None;
|
||||
}
|
||||
let value = base.0 / desired.0;
|
||||
value.try_into().ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for Hertz {
|
||||
fn from(value: u32) -> Self {
|
||||
Self(value as u64)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use core::any::Any;
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
@@ -51,4 +53,8 @@ pub trait Device: Sync + Send {
|
||||
fn as_clock_controller(self: Arc<Self>) -> Option<Arc<dyn ClockController>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn as_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ pub mod gpio;
|
||||
pub mod i2c;
|
||||
pub mod interrupt;
|
||||
pub mod serial;
|
||||
pub mod spi;
|
||||
pub mod timer;
|
||||
|
||||
pub mod dma;
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
use alloc::boxed::Box;
|
||||
use async_trait::async_trait;
|
||||
use yggdrasil_abi::{error::Error, io::device::spi::SpiConfig};
|
||||
|
||||
use crate::device::Device;
|
||||
|
||||
#[async_trait]
|
||||
pub trait SpiController: Device {
|
||||
async fn spi_transfer(
|
||||
&self,
|
||||
tx_buffer: &[u8],
|
||||
rx_buffer: &mut [u8],
|
||||
config: &SpiConfig,
|
||||
) -> Result<usize, Error>;
|
||||
}
|
||||
@@ -326,6 +326,11 @@ impl Node {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the device associated with this node
|
||||
pub fn device(self: Arc<Self>) -> Option<Arc<dyn Device>> {
|
||||
Some(self.probe()?.device.clone())
|
||||
}
|
||||
|
||||
/// Returns an iterator over the node's children
|
||||
pub fn children(&self) -> impl Iterator<Item = &Arc<Node>> {
|
||||
self.children.get().iter()
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
use core::{
|
||||
any::Any,
|
||||
ops::Deref,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
use alloc::{boxed::Box, sync::Arc};
|
||||
use alloc::boxed::Box;
|
||||
use async_trait::async_trait;
|
||||
use device_api::{
|
||||
clock::Hertz,
|
||||
device::Device,
|
||||
i2c::{I2CAddress, I2CDevice},
|
||||
};
|
||||
use yggdrasil_abi::{
|
||||
@@ -18,78 +13,7 @@ use yggdrasil_abi::{
|
||||
process::ProcessId,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
task::thread::Thread,
|
||||
vfs::{CommonImpl, FileReadiness, NodeRef},
|
||||
};
|
||||
|
||||
#[async_trait]
|
||||
pub trait CharDevice: Device + FileReadiness {
|
||||
async fn read(&self, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
self.read_nonblocking(buffer)
|
||||
}
|
||||
async fn write(&self, buffer: &[u8]) -> Result<usize, Error> {
|
||||
self.write_nonblocking(buffer)
|
||||
}
|
||||
|
||||
fn read_nonblocking(&self, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
let _ = buffer;
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
fn write_nonblocking(&self, buffer: &[u8]) -> Result<usize, Error> {
|
||||
let _ = buffer;
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn open(&self, process: ProcessId) -> Result<(), Error> {
|
||||
let _ = process;
|
||||
Ok(())
|
||||
}
|
||||
fn close(&self, process: ProcessId) -> Result<(), Error> {
|
||||
let _ = process;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_readable(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn is_writeable(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn is_terminal(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn device_request(&self, option: u32, buffer: &mut [u8], len: usize) -> Result<usize, Error> {
|
||||
let _ = option;
|
||||
let _ = buffer;
|
||||
let _ = len;
|
||||
log::warn!("device_request unimplemented: {option:#x}");
|
||||
Err(Error::InvalidOperation)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CharDeviceFile(pub(crate) Arc<dyn CharDevice>);
|
||||
|
||||
impl CommonImpl for CharDeviceFile {
|
||||
fn size(&self, node: &NodeRef) -> Result<u64, Error> {
|
||||
let _ = node;
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self as _
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for CharDeviceFile {
|
||||
type Target = dyn CharDevice;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
use crate::{device::char::CharDevice, task::thread::Thread, vfs::FileReadiness};
|
||||
|
||||
// TODO timeouts and better access
|
||||
#[async_trait]
|
||||
@@ -0,0 +1,79 @@
|
||||
use core::{any::Any, ops::Deref};
|
||||
|
||||
use alloc::{boxed::Box, sync::Arc};
|
||||
use async_trait::async_trait;
|
||||
use device_api::device::Device;
|
||||
use yggdrasil_abi::{error::Error, process::ProcessId};
|
||||
|
||||
use crate::vfs::{CommonImpl, FileReadiness, NodeRef};
|
||||
|
||||
pub mod i2c;
|
||||
pub mod spi;
|
||||
|
||||
#[async_trait]
|
||||
pub trait CharDevice: Device + FileReadiness {
|
||||
async fn read(&self, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
self.read_nonblocking(buffer)
|
||||
}
|
||||
async fn write(&self, buffer: &[u8]) -> Result<usize, Error> {
|
||||
self.write_nonblocking(buffer)
|
||||
}
|
||||
|
||||
fn read_nonblocking(&self, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
let _ = buffer;
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
fn write_nonblocking(&self, buffer: &[u8]) -> Result<usize, Error> {
|
||||
let _ = buffer;
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn open(&self, process: ProcessId) -> Result<(), Error> {
|
||||
let _ = process;
|
||||
Ok(())
|
||||
}
|
||||
fn close(&self, process: ProcessId) -> Result<(), Error> {
|
||||
let _ = process;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_readable(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn is_writeable(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn is_terminal(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn device_request(&self, option: u32, buffer: &mut [u8], len: usize) -> Result<usize, Error> {
|
||||
let _ = option;
|
||||
let _ = buffer;
|
||||
let _ = len;
|
||||
log::warn!("device_request unimplemented: {option:#x}");
|
||||
Err(Error::InvalidOperation)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CharDeviceFile(pub(crate) Arc<dyn CharDevice>);
|
||||
|
||||
impl CommonImpl for CharDeviceFile {
|
||||
fn size(&self, node: &NodeRef) -> Result<u64, Error> {
|
||||
let _ = node;
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self as _
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for CharDeviceFile {
|
||||
type Target = dyn CharDevice;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
use alloc::{boxed::Box, sync::Arc};
|
||||
use async_trait::async_trait;
|
||||
use device_api::{device::Device, spi::SpiController};
|
||||
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
|
||||
use yggdrasil_abi::{
|
||||
error::Error,
|
||||
io::device::spi::{self, SpiConfig, SpiRequestVariant},
|
||||
option::RequestValue,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
device::char::CharDevice,
|
||||
task::{mem::ForeignPointer, thread::Thread},
|
||||
vfs::FileReadiness,
|
||||
};
|
||||
|
||||
pub struct SpiControllerDevice {
|
||||
device: Arc<dyn SpiController>,
|
||||
config: IrqSafeRwLock<SpiConfig>,
|
||||
}
|
||||
|
||||
impl From<Arc<dyn SpiController>> for SpiControllerDevice {
|
||||
fn from(value: Arc<dyn SpiController>) -> Self {
|
||||
Self {
|
||||
device: value,
|
||||
config: IrqSafeRwLock::new(Default::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for SpiControllerDevice {
|
||||
fn display_name(&self) -> &str {
|
||||
self.device.display_name()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO timeouts and better access
|
||||
#[async_trait]
|
||||
impl CharDevice for SpiControllerDevice {
|
||||
async fn read(&self, _buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
Err(Error::InvalidOperation)
|
||||
}
|
||||
async fn write(&self, _buffer: &[u8]) -> Result<usize, Error> {
|
||||
Err(Error::InvalidOperation)
|
||||
}
|
||||
fn read_nonblocking(&self, _buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
Err(Error::InvalidOperation)
|
||||
}
|
||||
fn write_nonblocking(&self, _buffer: &[u8]) -> Result<usize, Error> {
|
||||
Err(Error::InvalidOperation)
|
||||
}
|
||||
fn device_request(&self, option: u32, buffer: &mut [u8], len: usize) -> Result<usize, Error> {
|
||||
if let Ok(request) = SpiRequestVariant::try_from(option) {
|
||||
match request {
|
||||
SpiRequestVariant::Transfer => {
|
||||
let xfer = spi::Transfer::load_request(&buffer[..len])?;
|
||||
let thread = Thread::current();
|
||||
let space = thread.address_space();
|
||||
let len = xfer.len;
|
||||
let tx_buffer = unsafe { xfer.tx_buffer.validate_user_slice(len, space) }?;
|
||||
let rx_buffer = unsafe { xfer.rx_buffer.validate_user_slice_mut(len, space) }?;
|
||||
let config = self.config.read();
|
||||
|
||||
let len = block!(
|
||||
self.device
|
||||
.spi_transfer(tx_buffer, rx_buffer, &config)
|
||||
.await
|
||||
)??;
|
||||
|
||||
let len = spi::Transfer::store_response(&len, buffer)?;
|
||||
|
||||
Ok(len)
|
||||
}
|
||||
SpiRequestVariant::SetConfig => {
|
||||
// TODO validate config against min/max controller capabilities
|
||||
let config = spi::SetConfig::load_request(&buffer[..len])?;
|
||||
*self.config.write() = config;
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FileReadiness for SpiControllerDevice {
|
||||
fn poll_read(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
|
||||
let _ = cx;
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
@@ -4,13 +4,17 @@ use core::{
|
||||
};
|
||||
|
||||
use alloc::{collections::BTreeMap, format, sync::Arc, vec::Vec};
|
||||
use device_api::i2c::{I2CController, I2CDevice};
|
||||
use device_api::{
|
||||
i2c::{I2CController, I2CDevice},
|
||||
spi::SpiController,
|
||||
};
|
||||
use libk_util::{OneTimeInit, sync::spin_rwlock::IrqSafeRwLock};
|
||||
use yggdrasil_abi::{error::Error, io::FileMode};
|
||||
|
||||
use crate::{
|
||||
config,
|
||||
debug::{self, DebugSink},
|
||||
device::char::spi::SpiControllerDevice,
|
||||
fs::devfs,
|
||||
vfs::NodeRef,
|
||||
};
|
||||
@@ -52,11 +56,17 @@ pub struct I2CDeviceRegistry {
|
||||
registry: GenericRegistry<Arc<I2CDevice>>,
|
||||
}
|
||||
|
||||
// Manages devices: spi<X>.<Y>
|
||||
pub struct SpiDeviceRegistry {
|
||||
registry: GenericRegistry<Arc<SpiControllerDevice>>,
|
||||
}
|
||||
|
||||
pub struct DeviceRegistry {
|
||||
pub display: DisplayDeviceRegistry,
|
||||
pub terminal: TerminalRegistry,
|
||||
pub serial_terminal: SerialTerminalRegistry,
|
||||
pub i2c: I2CDeviceRegistry,
|
||||
pub spi: SpiDeviceRegistry,
|
||||
}
|
||||
|
||||
pub static DEVICE_REGISTRY: DeviceRegistry = DeviceRegistry {
|
||||
@@ -64,6 +74,7 @@ pub static DEVICE_REGISTRY: DeviceRegistry = DeviceRegistry {
|
||||
terminal: TerminalRegistry::new(),
|
||||
serial_terminal: SerialTerminalRegistry::new(),
|
||||
i2c: I2CDeviceRegistry::new(),
|
||||
spi: SpiDeviceRegistry::new(),
|
||||
};
|
||||
|
||||
impl TerminalRegistry {
|
||||
@@ -141,6 +152,22 @@ impl I2CDeviceRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
impl SpiDeviceRegistry {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
registry: GenericRegistry::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_bus(&self, device: Arc<dyn SpiController>) -> Result<u32, Error> {
|
||||
let wrapper = Arc::new(SpiControllerDevice::from(device));
|
||||
let id = self.registry.register(wrapper.clone())?;
|
||||
let name = format!("spi{id}");
|
||||
devfs::add_named_char_device(wrapper.clone(), &name, FileMode::new(0o600))?;
|
||||
Ok(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> GenericRegistry<T> {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
|
||||
Reference in New Issue
Block a user