From 04e753f314826f0630e32da4e2c589a83674c80a Mon Sep 17 00:00:00 2001 From: happy-thw Date: Mon, 14 Oct 2024 15:42:00 +0800 Subject: [PATCH 01/14] add interface and provide the corresponding abstraction --- r4l/Cargo.toml | 2 +- r4l/src/device.rs | 17 +++++++++++++++++ r4l/src/io/mod.rs | 6 ++++++ r4l/src/io/os_api.rs | 12 ++++++++++++ r4l/src/lib.rs | 2 ++ r4l/src/of/mod.rs | 4 ++-- r4l/src/of/{irq.rs => resource.rs} | 19 +++++++++++++++---- r4l/src/platform/platform_dev.rs | 13 ++++++++++++- r4l/src/regmap.rs | 28 ++++++++++++++++++++++++++++ 9 files changed, 95 insertions(+), 8 deletions(-) create mode 100644 r4l/src/io/mod.rs create mode 100644 r4l/src/io/os_api.rs rename r4l/src/of/{irq.rs => resource.rs} (59%) create mode 100644 r4l/src/regmap.rs diff --git a/r4l/Cargo.toml b/r4l/Cargo.toml index 6768a95..6dd2428 100644 --- a/r4l/Cargo.toml +++ b/r4l/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/guoweikang/r4l.git" [features] rust_os=[] -starry=["rust_os", "axerrno", "axtask", "axlog", "axhal", "axsync", "linked_list"] +starry=["rust_os", "axerrno", "axtask/irq", "axlog", "axhal", "axsync/irq", "linked_list"] [dependencies] bitflags = "2.5.0" diff --git a/r4l/src/device.rs b/r4l/src/device.rs index fd6b732..36f7f21 100644 --- a/r4l/src/device.rs +++ b/r4l/src/device.rs @@ -7,6 +7,7 @@ use crate::pr_info; use crate::prelude::*; +use crate::platform::PlatformDevice; use core::any::Any; use of::OfNode; @@ -26,6 +27,10 @@ impl Device { } } + pub fn get_resource(&self, index: usize) -> Result { + crate::of::of_membase_resource_get(self.of_node, index) + } + pub fn irq_resource(&self, index: usize) -> Result { crate::of::of_irq_get(self.of_node, index) } @@ -44,6 +49,18 @@ impl Device { None => false, } } + + pub fn device_property_read_u32(&self, propname: &'static CStr) -> Result { + let res = of::of_property_read_u32(self.of_node, propname, 0); + match res { + Some(val) => { Ok(val)} + None => { Err(EINVAL) } + } + } + + pub fn from_dev(pdev: &PlatformDevice) -> &Self { + pdev.get_device() + } } pub trait DeviceOps { diff --git a/r4l/src/io/mod.rs b/r4l/src/io/mod.rs new file mode 100644 index 0000000..29346ab --- /dev/null +++ b/r4l/src/io/mod.rs @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Memory-mapped IO. + +mod os_api; +pub use os_api::*; diff --git a/r4l/src/io/os_api.rs b/r4l/src/io/os_api.rs new file mode 100644 index 0000000..e6c84a5 --- /dev/null +++ b/r4l/src/io/os_api.rs @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0 + +#[cfg(feature = "starry")] +mod os_io_interface { + use axhal::mem::{phys_to_virt, PhysAddr}; + + pub fn ioremap(addr: usize) -> usize { + phys_to_virt(PhysAddr::from(addr)).as_usize() + } +} + +pub use os_io_interface::*; \ No newline at end of file diff --git a/r4l/src/lib.rs b/r4l/src/lib.rs index 66729aa..2d36f0a 100644 --- a/r4l/src/lib.rs +++ b/r4l/src/lib.rs @@ -30,6 +30,8 @@ pub mod str; pub mod sync; pub mod uapi; pub mod irq; +pub mod io; +pub mod regmap; pub use build_error::build_error; diff --git a/r4l/src/of/mod.rs b/r4l/src/of/mod.rs index c10e53b..cd526f8 100644 --- a/r4l/src/of/mod.rs +++ b/r4l/src/of/mod.rs @@ -2,8 +2,8 @@ mod device_id; mod platform; -mod irq; +mod resource; pub use device_id::*; pub use platform::*; -pub use irq::*; +pub use resource::*; diff --git a/r4l/src/of/irq.rs b/r4l/src/of/resource.rs similarity index 59% rename from r4l/src/of/irq.rs rename to r4l/src/of/resource.rs index fce7c92..042e9d8 100644 --- a/r4l/src/of/irq.rs +++ b/r4l/src/of/resource.rs @@ -5,20 +5,19 @@ use of::OfNode; const MAX_PHANDLE_ARGS: usize = 32; struct OfPhandleArgs { - np: OfNode<'static>, + np: Option>, args_count: usize, args: [u32; MAX_PHANDLE_ARGS], } impl OfPhandleArgs { - fn of_irq_parse_one(node: OfNode<'static>, index: usize) - -> Result { + fn of_irq_parse_one(node: OfNode<'static>, index: usize) -> Result { let parent = node.interrupt_parent().ok_or(EINVAL)?; let intsize = parent.interrupt_cells().ok_or(EINVAL)?; pr_debug!("irq {} parent intsize={}\n",parent.compatible().unwrap().first(), intsize); - let mut res = Self{np: parent, args_count: intsize, args: [0;MAX_PHANDLE_ARGS]}; + let mut res = Self{np: Some(parent), args_count: intsize, args: [0;MAX_PHANDLE_ARGS]}; for i in 0..intsize { res.args[i] = of::of_property_read_u32(node, "interrupts", (index * intsize) + i).ok_or(EINVAL)?; @@ -27,8 +26,15 @@ impl OfPhandleArgs { // TODO: Check if there are any interrupt-map translations to process Ok(res) } + + fn of_reg_parse_one(node: OfNode<'static>, _index: usize) -> Result { + let reg = node.reg().ok_or(EINVAL)?.next().unwrap(); + pr_debug!("starting_address is {:#x} , size={} \n", reg.starting_address as usize, reg.size.unwrap()); + Ok(reg.starting_address as usize) + } } + pub fn of_irq_get(node: OfNode<'static>, index: usize) -> Result { let oirq = OfPhandleArgs::of_irq_parse_one(node, index)?; if oirq.args_count != 3 { @@ -36,3 +42,8 @@ pub fn of_irq_get(node: OfNode<'static>, index: usize) -> Result { } Ok(oirq.args[1]) } + +pub fn of_membase_resource_get(node: OfNode<'static>, index: usize) -> Result { + let base = OfPhandleArgs::of_reg_parse_one(node, index)?; + Ok(base) +} \ No newline at end of file diff --git a/r4l/src/platform/platform_dev.rs b/r4l/src/platform/platform_dev.rs index 5f71e31..c8fba14 100644 --- a/r4l/src/platform/platform_dev.rs +++ b/r4l/src/platform/platform_dev.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 //! A platform device. -use crate::device; +use crate::{device, io}; use core::any::Any; use of::OfNode; @@ -24,6 +24,17 @@ impl PlatformDevice { pub fn irq_resource(&self, index: usize) -> Result { self.device.irq_resource(index) } + + /// Return ioremap ptr + pub fn ioremap_resource(&self, index: usize) -> Result{ + let addr = self.device.get_resource(index)?; + Ok(io::ioremap(addr)) + } + + /// get device + pub fn get_device(&self) -> &device::Device { + &self.device + } } impl device::DeviceOps for PlatformDevice { diff --git a/r4l/src/regmap.rs b/r4l/src/regmap.rs new file mode 100644 index 0000000..5359663 --- /dev/null +++ b/r4l/src/regmap.rs @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Register map access API. + +use crate::pr_info; + +/// reg_read +pub fn reg_read( + reg_base: usize, + offset: usize, +) -> u32 { + let base = reg_base + offset; + pr_info!("reg_read: reg_base is {:#x}, offset is {:#x}, reg_addr is {:#x}", reg_base, offset, base); + let val = unsafe {::core::ptr::read_volatile(base as _)}; + val +} + +/// reg_write +pub fn reg_write( + reg_base: usize, + offset: usize, + val: u32, +) -> u32 { + let base = reg_base + offset ; + pr_info!("reg_write: reg_base is {:#x}, offset is {:#x}, reg_addr is {:#x}, val is {val:#}", reg_base, offset, base); + unsafe { ::core::ptr::write_volatile(base as _, val) }; + return 0; +} \ No newline at end of file From dd1e58056d7e2f7a9a350df79e248f441eb03473 Mon Sep 17 00:00:00 2001 From: happy-thw Date: Mon, 14 Oct 2024 15:46:23 +0800 Subject: [PATCH 02/14] add types, math, delay, timekeeping abstract --- r4l/src/delay.rs | 15 +++ r4l/src/error.rs | 1 + r4l/src/init.rs | 2 +- r4l/src/lib.rs | 4 + r4l/src/math.rs | 52 ++++++++ r4l/src/timekeeping.rs | 69 ++++++++++ r4l/src/types.rs | 284 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 426 insertions(+), 1 deletion(-) create mode 100644 r4l/src/delay.rs create mode 100644 r4l/src/math.rs create mode 100644 r4l/src/timekeeping.rs create mode 100644 r4l/src/types.rs diff --git a/r4l/src/delay.rs b/r4l/src/delay.rs new file mode 100644 index 0000000..89c1f98 --- /dev/null +++ b/r4l/src/delay.rs @@ -0,0 +1,15 @@ +//! I2c delay interface +//! + +#[cfg(feature = "starry")] +mod delay { + pub use axtask::sleep; +} + +use delay::*; + +/// usleep +pub fn usleep(us: u64) { + sleep(core::time::Duration::from_nanos(us)); +} + diff --git a/r4l/src/error.rs b/r4l/src/error.rs index 262e035..d88df37 100644 --- a/r4l/src/error.rs +++ b/r4l/src/error.rs @@ -18,6 +18,7 @@ mod error { }; } + declare_err!(ETIMEDOUT, "Operation timeout", Timeout); declare_err!(EPERM, "Operation not permitted.", PermissionDenied); declare_err!(ENOENT, "No such file or directory.", NotFound); declare_err!(ESRCH, "No such process.", NotFound); diff --git a/r4l/src/init.rs b/r4l/src/init.rs index 80a36e2..000dc53 100644 --- a/r4l/src/init.rs +++ b/r4l/src/init.rs @@ -33,7 +33,7 @@ fn initcall(pair: InitcallAddrPair) { let func: extern "C" fn() -> c_int = unsafe { core::mem::transmute(func_ptr_value as *const extern "C" fn() -> c_int) }; let result = func(); - crate::pr_info!("Function at address {:p} returned: {}",current_addr,result); + crate::pr_info!("Function at address {:p} returned: {}", current_addr, result); if result < 0 { panic!("driver module init call failed"); } diff --git a/r4l/src/lib.rs b/r4l/src/lib.rs index 2d36f0a..bfb7036 100644 --- a/r4l/src/lib.rs +++ b/r4l/src/lib.rs @@ -29,9 +29,13 @@ pub mod print; pub mod str; pub mod sync; pub mod uapi; +pub mod types; pub mod irq; pub mod io; pub mod regmap; +pub mod math; +pub mod delay; +pub mod timekeeping; pub use build_error::build_error; diff --git a/r4l/src/math.rs b/r4l/src/math.rs new file mode 100644 index 0000000..28ecc53 --- /dev/null +++ b/r4l/src/math.rs @@ -0,0 +1,52 @@ +//! Some math funcs +//! + +/// PETA +pub const PETA: u64 = 1000000000000000; +/// TERA +pub const TERA: u64 = 1000000000000; +/// GIGA +pub const GIGA: u32 = 1000000000; +/// MEGA +pub const MEGA: u32 = 1000000; +/// KILO +pub const KILO: u32 = 1000; +/// HECTO +pub const HECTO: u32 = 100; +/// DECA +pub const DECA: u32 = 10; +/// DECI +pub const DECI: u32 = 10; +/// CENTI +pub const CENTI: u32 = 100; +/// MILLI +pub const MILLI: u32 = 1000; +/// MICRO +pub const MICRO: u32 = 1000000; +/// NANO +pub const NANO: u32 = 1000000000; +/// PICO +pub const PICO: u64 = 1000000000000; +/// FEMTO +pub const FEMTO: u64 = 1000000000000000; + +/// Function to perform division with rounding to the closest integer +pub fn div_round_closest_ull(x: u64, divisor: u32) -> u64 { + let tmp = x + (divisor as u64 / 2); + do_div(tmp, divisor) +} + +// Function to perform division and return the quotient +fn do_div(n: u64, base: u32) -> u64 { + if base == 0 { + panic!("Division by zero"); + } + + if base.is_power_of_two() { + return n >> base.trailing_zeros(); + } + + // Performing division + let quotient = n / base as u64; + quotient +} \ No newline at end of file diff --git a/r4l/src/timekeeping.rs b/r4l/src/timekeeping.rs new file mode 100644 index 0000000..3a51d57 --- /dev/null +++ b/r4l/src/timekeeping.rs @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! I2c kernel interface +//! + +#[cfg(feature = "starry")] +mod timekeeping { + pub use axhal::time::current_time_nanos; + + /// Get Ktime + pub fn ktime_get() -> u64 { + current_time_nanos() + } +} + +pub use timekeeping::*; +use crate::{ + prelude::*, + delay, + error::Result, +}; + +/// One usec to nsec +pub const NSEC_PER_USEC: u64 = 1000; + +/// ktime add us +pub fn time_add_us(us: u64) -> u64 { + ktime_get() + us * NSEC_PER_USEC +} + +/// current time +pub fn current_time() -> u64 { + ktime_get() +} + +/// Poll until a condition is met or a timeout occurs +pub fn read_poll_timeout T, C: Fn(T) -> bool>( + read_op: F, + cond: C, + sleep_us: u64, + timeout_us: u64, + sleep_before: bool, +) -> Result<()> { + let timeout: u64 = time_add_us(timeout_us); + + if sleep_us != 0 && sleep_before { + delay::usleep(sleep_us); + } + + let ret = loop { + if cond(read_op()) { + return Ok(()); + } + + if timeout_us != 0 && current_time() > timeout { + break read_op(); + } + + if sleep_us > 0 { + delay::usleep(sleep_us); + } + }; + + if cond(ret) { + return Ok(()); + } else { + return Err(ETIMEDOUT); + } +} \ No newline at end of file diff --git a/r4l/src/types.rs b/r4l/src/types.rs new file mode 100644 index 0000000..f329fae --- /dev/null +++ b/r4l/src/types.rs @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: GPL-2.0 +//! Kernel types. + +use core::ops; + + +/// A bitmask. +/// +/// It has a restriction that all bits must be the same, except one. For example, `0b1110111` and +/// `0b1000` are acceptable masks. +#[derive(Clone, Copy)] +pub struct Bit { + index: T, + inverted: bool, +} + +/// Creates a bit mask with a single bit set. +/// +/// # Examples +/// +/// ``` +/// # use kernel::bit; +/// let mut x = 0xfeu32; +/// +/// assert_eq!(x & bit(0), 0); +/// assert_eq!(x & bit(1), 2); +/// assert_eq!(x & bit(2), 4); +/// assert_eq!(x & bit(3), 8); +/// +/// x |= bit(0); +/// assert_eq!(x, 0xff); +/// +/// x &= !bit(1); +/// assert_eq!(x, 0xfd); +/// +/// x &= !bit(7); +/// assert_eq!(x, 0x7d); +/// +/// let y: u64 = bit(34).into(); +/// assert_eq!(y, 0x400000000); +/// +/// assert_eq!(y | bit(35), 0xc00000000); +/// ``` +pub const fn bit(index: T) -> Bit { + Bit { + index, + inverted: false, + } +} + +impl ops::Not for Bit { + type Output = Self; + fn not(self) -> Self { + Self { + index: self.index, + inverted: !self.inverted, + } + } +} + +/// Implemented by integer types that allow counting the number of trailing zeroes. +pub trait TrailingZeros { + /// Returns the number of trailing zeroes in the binary representation of `self`. + fn trailing_zeros(&self) -> u32; +} + +macro_rules! define_unsigned_number_traits { + ($type_name:ty) => { + impl TrailingZeros for $type_name { + fn trailing_zeros(&self) -> u32 { + <$type_name>::trailing_zeros(*self) + } + } + + impl core::convert::From> for $type_name + where + Self: ops::Shl + core::convert::From + ops::Not, + { + fn from(v: Bit) -> Self { + let c = Self::from(1u8) << v.index; + if v.inverted { + !c + } else { + c + } + } + } + + impl ops::BitAnd> for $type_name + where + Self: ops::Shl + core::convert::From, + { + type Output = Self; + fn bitand(self, rhs: Bit) -> Self::Output { + self & Self::from(rhs) + } + } + + impl ops::BitOr> for $type_name + where + Self: ops::Shl + core::convert::From, + { + type Output = Self; + fn bitor(self, rhs: Bit) -> Self::Output { + self | Self::from(rhs) + } + } + + impl ops::BitAndAssign> for $type_name + where + Self: ops::Shl + core::convert::From, + { + fn bitand_assign(&mut self, rhs: Bit) { + *self &= Self::from(rhs) + } + } + + impl ops::BitOrAssign> for $type_name + where + Self: ops::Shl + core::convert::From, + { + fn bitor_assign(&mut self, rhs: Bit) { + *self |= Self::from(rhs) + } + } + }; +} + +define_unsigned_number_traits!(u8); +define_unsigned_number_traits!(u16); +define_unsigned_number_traits!(u32); +define_unsigned_number_traits!(u64); +define_unsigned_number_traits!(usize); + +/// Returns an iterator over the set bits of `value`. +/// +/// # Examples +/// +/// ``` +/// use kernel::bits_iter; +/// +/// let mut iter = bits_iter(5usize); +/// assert_eq!(iter.next().unwrap(), 0); +/// assert_eq!(iter.next().unwrap(), 2); +/// assert!(iter.next().is_none()); +/// ``` +/// +/// ``` +/// use kernel::bits_iter; +/// +/// fn print_bits(x: usize) { +/// for bit in bits_iter(x) { +/// pr_info!("{}\n", bit); +/// } +/// } +/// +/// # print_bits(42); +/// ``` +#[inline] +pub fn bits_iter(value: T) -> impl Iterator +where + T: core::cmp::PartialEq + + From + + ops::Shl + + ops::Not + + ops::BitAndAssign + + TrailingZeros, +{ + struct BitIterator { + value: U, + } + + impl Iterator for BitIterator + where + U: core::cmp::PartialEq + + From + + ops::Shl + + ops::Not + + ops::BitAndAssign + + TrailingZeros, + { + type Item = u32; + + #[inline] + fn next(&mut self) -> Option { + if self.value == U::from(0u8) { + return None; + } + let ret = self.value.trailing_zeros(); + self.value &= !(U::from(1u8) << ret); + Some(ret) + } + } + + BitIterator { value } +} + +/// A trait for boolean types. +/// +/// This is meant to be used in type states to allow boolean constraints in implementation blocks. +/// In the example below, the implementation containing `MyType::set_value` could _not_ be +/// constrained to type states containing `Writable = true` if `Writable` were a constant instead +/// of a type. +/// +/// # Safety +/// +/// No additional implementations of [`Bool`] should be provided, as [`True`] and [`False`] are +/// already provided. +/// +/// # Examples +/// +/// ``` +/// # use kernel::{Bool, False, True}; +/// use core::marker::PhantomData; +/// +/// // Type state specifies whether the type is writable. +/// trait MyTypeState { +/// type Writable: Bool; +/// } +/// +/// // In state S1, the type is writable. +/// struct S1; +/// impl MyTypeState for S1 { +/// type Writable = True; +/// } +/// +/// // In state S2, the type is not writable. +/// struct S2; +/// impl MyTypeState for S2 { +/// type Writable = False; +/// } +/// +/// struct MyType { +/// value: u32, +/// _p: PhantomData, +/// } +/// +/// impl MyType { +/// fn new(value: u32) -> Self { +/// Self { +/// value, +/// _p: PhantomData, +/// } +/// } +/// } +/// +/// // This implementation block only applies if the type state is writable. +/// impl MyType +/// where +/// T: MyTypeState, +/// { +/// fn set_value(&mut self, v: u32) { +/// self.value = v; +/// } +/// } +/// +/// let mut x = MyType::::new(10); +/// let mut y = MyType::::new(20); +/// +/// x.set_value(30); +/// +/// // The code below fails to compile because `S2` is not writable. +/// // y.set_value(40); +/// ``` +pub unsafe trait Bool {} + +/// Represents the `true` value for types with [`Bool`] bound. +pub struct True; + +// SAFETY: This is one of the only two implementations of `Bool`. +unsafe impl Bool for True {} + +/// Represents the `false` value for types wth [`Bool`] bound. +pub struct False; + +// SAFETY: This is one of the only two implementations of `Bool`. +unsafe impl Bool for False {} + +/// Generate a mask where all bits >= `h` and <= `l` are set +/// +/// This is a re-implementation in rust of `GENMASK` +pub const fn genmask(h: u32, l: u32) -> u32 { + ((!0u32) - (1 << l) + 1) & ((!0u32) >> (32 - 1 - h)) +} \ No newline at end of file From a848b4b4a27188928f0434141228b335f633fa40 Mon Sep 17 00:00:00 2001 From: happy-thw Date: Mon, 14 Oct 2024 15:49:46 +0800 Subject: [PATCH 03/14] add i2c abstract --- r4l/src/i2c/functionality.rs | 64 +++++++++ r4l/src/i2c/mod.rs | 7 + r4l/src/i2c/msg.rs | 235 ++++++++++++++++++++++++++++++++ r4l/src/i2c/timing.rs | 252 +++++++++++++++++++++++++++++++++++ r4l/src/lib.rs | 3 + 5 files changed, 561 insertions(+) create mode 100644 r4l/src/i2c/functionality.rs create mode 100644 r4l/src/i2c/msg.rs create mode 100644 r4l/src/i2c/timing.rs diff --git a/r4l/src/i2c/functionality.rs b/r4l/src/i2c/functionality.rs new file mode 100644 index 0000000..3b71804 --- /dev/null +++ b/r4l/src/i2c/functionality.rs @@ -0,0 +1,64 @@ +//! To determine i2c what functionality is present +//! +//! From linux/include/uapi/linux/i2c.h + +/// I2cFuncFlags Struct +#[repr(C)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct I2cFuncFlags; +/// To determine what I2C functionality is present +impl I2cFuncFlags { + /// Support I2C + pub const I2C:u32 = 0x00000001; + /// required for I2C_M_TEN + pub const BIT_10_ADDR:u32 = 0x00000002; + /// required for I2C_M_IGNORE_NAK etc. + pub const PROTOCOL_MANGLING:u32 = 0x00000004; + /// Support smbus_pec + pub const SMBUS_PEC:u32 = 0x00000008; + /// Support Nostart + pub const NOSTART:u32 = 0x00000010; + /// Support Slave + pub const SLAVE:u32 = 0x00000020; + /// Fill Doc + pub const SMBUS_BLOCK_PROC_CALL:u32 = 0x00008000; /* SMBus 2.0 or later */ + /// Fill Doc + pub const SMBUS_QUICK:u32 = 0x00010000; + /// Fill Doc + pub const SMBUS_READ_BYTE:u32 = 0x00020000; + /// Fill Doc + pub const SMBUS_WRITE_BYTE:u32 = 0x00040000; + /// Fill Doc + pub const SMBUS_READ_BYTE_DATA:u32 = 0x00080000; + /// Fill Doc + pub const SMBUS_WRITE_BYTE_DATA:u32 = 0x00100000; + /// Fill Doc + pub const SMBUS_READ_WORD_DATA:u32 = 0x00200000; + /// Fill Doc + pub const SMBUS_WRITE_WORD_DATA:u32 = 0x00400000; + /// Fill Doc + pub const SMBUS_PROC_CALL:u32 = 0x00800000; + /// required for I2C_M_RECV_LEN + pub const SMBUS_READ_BLOCK_DATA:u32 = 0x01000000; + /// Fill Doc + pub const SMBUS_WRITE_BLOCK_DATA:u32 = 0x02000000; + /// I2C-like block xfer + pub const SMBUS_READ_I2C_BLOCK:u32 = 0x04000000; + /// w/ 1-byte reg. addr. + pub const SMBUS_WRITE_I2C_BLOCK:u32 = 0x08000000; + /// SMBus 2.0 or later + pub const SMBUS_HOST_NOTIFY:u32 = 0x10000000; + + // Multi-bit flags + /// Fill Doc + pub const SMBUS_BYTE:u32 = Self::SMBUS_READ_BYTE | Self::SMBUS_WRITE_BYTE; + /// Fill Doc + pub const SMBUS_BYTE_DATA:u32 = Self::SMBUS_READ_BYTE_DATA | Self::SMBUS_WRITE_BYTE_DATA; + /// Fill Doc + pub const SMBUS_WORD_DATA:u32 = Self::SMBUS_READ_WORD_DATA | Self::SMBUS_WRITE_WORD_DATA; + /// Fill Doc + pub const SMBUS_BLOCK_DATA:u32 = Self::SMBUS_READ_BLOCK_DATA | Self::SMBUS_WRITE_BLOCK_DATA; + /// Fill Doc + pub const SMBUS_I2C_BLOCK:u32 = Self::SMBUS_READ_I2C_BLOCK | Self::SMBUS_WRITE_I2C_BLOCK; + +} \ No newline at end of file diff --git a/r4l/src/i2c/mod.rs b/r4l/src/i2c/mod.rs index e69de29..ebda076 100644 --- a/r4l/src/i2c/mod.rs +++ b/r4l/src/i2c/mod.rs @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! I2c kernel interface + +pub mod timing; +pub mod functionality; +pub mod msg; diff --git a/r4l/src/i2c/msg.rs b/r4l/src/i2c/msg.rs new file mode 100644 index 0000000..6f41fd5 --- /dev/null +++ b/r4l/src/i2c/msg.rs @@ -0,0 +1,235 @@ +//! I2C msg abstraction +//! +//! Every OS should provide I2cMsg type and implement GeneralI2cMsg +//! +use crate::types::{bit, Bit, genmask}; + +/// I2cMsgFlags Struct +#[repr(C)] +pub struct I2cMsgFlags; +/// To determine what I2C functionality is present +impl I2cMsgFlags { + /// read data (from slave to master). Guaranteed to be 0x0001 + pub const I2C_MASTER_READ:u16 = 0x0001; + /// Use Packet Error Checking + pub const I2C_CLIENT_PEC:u16 = 0x0004; + /// this is a 10 bit chip address + /// Only if I2C_FUNC_10BIT_ADDR is set + pub const I2C_ADDR_TEN:u16 = 0x0010; + /// we are the slave + pub const I2C_CLIENT_SLAVE:u16 = 0x0020; + /// We want to use I2C host notify + pub const I2C_CLIENT_HOST_NOTIFY:u16 = 0x0040; + /// for board_info; true if can wake + pub const I2C_CLIENT_WAKE:u16 = 0x0080; + /// Linux kernel + pub const I2C_MASTER_DMA_SAFE:u16 = 0x0200; + /// message length will be first received byte + /// Only if I2C_FUNC_SMBUS_READ_BLOCK_DATA is set + pub const I2C_MASTER_RECV_LEN:u16 = 0x0400; + /// in a read message, master ACK/NACK bit is skipped + pub const I2C_MASTER_NOREADACK:u16 = 0x0800; + /// treat NACK from client as ACK + pub const I2C_MASTER_IGN_NAK:u16 = 0x1000; + /// toggles the Rd/Wr bit + pub const I2C_MASTER_REVDIR:u16 = 0x2000; + /// skip repeated start sequence + pub const I2C_MASTER_NOSTART:u16 = 0x4000; + /// force a STOP condition after the message + pub const I2C_MASTER_STOP:u16 = 0x8000; + + // Multi-bit flags + /// Use Omnivision SCCB protocol Must match I2C_M_STOP|IGNORE_NAK + pub const I2C_CLIENT_SCCB:u16 = Self::I2C_MASTER_STOP | Self::I2C_MASTER_IGN_NAK; +} + +/// GeneralI2cMsg data that Type I2cMsg must implement +pub trait GeneralI2cMsgInfo:Default+Sync+Send { + /// Create a new I2cMsg with addr and data that need to transfer + fn new_send(addr: u16, flags: u16, data: [u8;N]) -> Self; + /// Create a new I2cMsg with addr and an empty buf that want to recive + fn new_recieve(addr: u16, flags: u16, len: usize) -> Self; + /// Get msg addr + fn addr(&self) -> u16; + /// Get msg copy flags + fn flags(&self) -> u16; + /// int recieve cmd cnt + fn inc_recieve_cmd_cnt(&mut self); + /// Check whether the send msg is left last + fn send_left_last(&self) -> bool; + /// Check whether the send msg is end + fn send_end(&self) -> bool; + /// Check whether the recieve is end + fn recieve_end(&self) -> bool ; + /// Write 1byte to recieve msg + fn push_byte(&mut self, byte: u8); + /// Read 1byte from send msg front + fn pop_front_byte(&mut self) -> u8; + + /// modify msg buf len, only used when flags contains I2cMasterRecvLen + fn modify_recieve_threshold(&mut self, buf_len: usize); + /// modify recieve_cmd_cnt only flags contains I2cMasterRecvLen + fn modify_recieve_cmd_cnt(&mut self, read_cmd_cnt: isize); + /// remove one flag + fn remove_flag(&mut self, flag: u16); +} + +/// I2C Driver Sofware status flags. +/// active +pub const STATUS_ACTIVE : Bit = bit(0); +/// Signal-driven I/O is enabled. +pub const STATUS_WRITE_IN_PROGRESS : Bit = bit(1); +/// Signal-driven I/O is enabled. +pub const STATUS_READ_IN_PROGRESS : Bit = bit(2); +/// GENMASK(2, 0) +pub const STATUS_MASK: u32 = genmask(2, 0); + +///abrt cmd +pub const DW_IC_ERR_TX_ABRT:u32 = 0x1; + +/// an I2C transaction segment beginning with START +#[derive(Debug) ] +#[allow(dead_code)] +pub struct I2cMsgInfo{ + /// Slave address, either 7 or 10 bits. When this is a 10 bit address, + /// I2C_M_TEN must be set in @flags and the adapter must support I2C_FUNC_10BIT_ADDR + addr: u16, + /// msg flags: + flags: u16, + /// The buffer into which data is read, or from which it's written + buf: *mut u8, + /// The buffer length + buf_len: usize, + /// record current recieve idx + recieve_idx: isize, + /// record current send idx + send_idx: isize, + /// Only used when msg is used to recieve, + /// before recieve data, first need to send recieve cmd + /// so this is used to record recieve data cmd cnt + recieve_cmd_cnt: isize, +} + +impl Default for I2cMsgInfo { + fn default() -> Self { + Self { + addr: 0, + flags: 0, + buf: core::ptr::null_mut(), + buf_len: 0, + send_idx: 0, + recieve_idx: 0, + recieve_cmd_cnt: 0, + } + } +} + +unsafe impl Send for I2cMsgInfo {} +unsafe impl Sync for I2cMsgInfo {} + +impl I2cMsgInfo { + /// Create a new I2cMsg from linux + pub fn new_raw(addr: u16, flags: u16, buf: *mut u8, buf_len: usize) -> Self { + I2cMsgInfo { addr, flags, buf, buf_len, recieve_idx: 0, send_idx: 0, recieve_cmd_cnt: 0} + } +} + +impl GeneralI2cMsgInfo for I2cMsgInfo { + /// Create a new I2cMsg with addr and data that need to transfer + fn new_send(_addr: u16, _flags: u16, _data: [u8;N]) -> Self { + unimplemented!("Linux now use binding:i2c_msg"); + } + + /// Create a new I2cMsg with addr and an empty buf that want to recive + fn new_recieve(_addr: u16, _flags: u16, _len: usize) -> Self { + unimplemented!("Linux now use binding:i2c_msg"); + } + + /// Get msg addr + fn addr(&self) -> u16 { + self.addr + } + + /// Get msg copy flags + fn flags(&self) -> u16 { + self.flags + } + + /// inc recieve_cmd_cnt + fn inc_recieve_cmd_cnt(&mut self) { + assert!(self.flags & I2cMsgFlags::I2C_MASTER_READ != 0 ); + self.recieve_cmd_cnt +=1; + } + + /// Check whether the send msg is left last + fn send_left_last(&self) -> bool { + // MasterRead means msg can be write + if self.flags & I2cMsgFlags::I2C_MASTER_READ != 0 { + self.recieve_cmd_cnt as usize == self.buf_len -1 + } else { + self.send_idx as usize == self.buf_len - 1 + } + } + + /// Check whether the send msg is end + fn send_end(&self) -> bool { + // MasterRead means msg can be write + if self.flags & I2cMsgFlags::I2C_MASTER_READ != 0 { + self.recieve_cmd_cnt as usize == self.buf_len + } else { + self.send_idx as usize == self.buf_len + } + } + + /// Check whether the recieve is end + fn recieve_end(&self) -> bool { + // MasterRead means msg can be write + assert!(self.flags & I2cMsgFlags::I2C_MASTER_READ != 0); + self.recieve_idx as usize == self.buf_len + } + + /// Write 1byte to recieve msg + fn push_byte(&mut self, byte: u8) { + // MasterRead means msg can be write + assert!(self.flags & I2cMsgFlags::I2C_MASTER_READ != 0); + + if self.recieve_end() { + panic!("access buf overfllow"); + } + unsafe{*self.buf.offset(self.recieve_idx) = byte}; + self.recieve_idx +=1; + } + + /// Read 1byte from send msg front + fn pop_front_byte(&mut self) -> u8 { + // MasterRead means msg can be write, don't alow read + assert!(self.flags & I2cMsgFlags::I2C_MASTER_READ == 0); + + if self.send_end() { + panic!("access buf overfllow"); + } + + let byte = unsafe{*self.buf.offset(self.send_idx)}; + self.send_idx +=1; + byte + } + + /// modify msg buf len, only used when flags contains I2cMasterRecvLen + fn modify_recieve_threshold(&mut self, buf_len: usize) { + assert!(self.flags & I2cMsgFlags::I2C_MASTER_RECV_LEN !=0 ); + assert!(self.flags & I2cMsgFlags::I2C_MASTER_READ != 0); + self.buf_len = buf_len; + } + + /// modify recieve_cmd_cnt only flags contains I2cMasterRecvLen + fn modify_recieve_cmd_cnt(&mut self, read_cmd_cnt: isize) { + assert!(self.flags & I2cMsgFlags::I2C_MASTER_RECV_LEN !=0 ); + assert!(self.flags & I2cMsgFlags::I2C_MASTER_READ !=0 ); + self.recieve_cmd_cnt = read_cmd_cnt; + } + + /// remove one flag + fn remove_flag(&mut self, flag: u16) { + self.flags &= !flag; + } +} \ No newline at end of file diff --git a/r4l/src/i2c/timing.rs b/r4l/src/i2c/timing.rs new file mode 100644 index 0000000..462800c --- /dev/null +++ b/r4l/src/i2c/timing.rs @@ -0,0 +1,252 @@ +//! I2C Time configuration +use core::fmt; +use crate::device::Device; +use crate::c_str; +use crate::str::CStr; + +/// i2c Speed mode +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum I2cSpeedMode { + /// Standard Speed Mode. + StandMode = 0, + /// Fast Speed Mode. + FastMode, + /// Fast Plus Mode. + FastPlusMode, + /// TURBO Mode. + TurboMode, + /// High Speed. + HighSpeedMode, + /// ULTRA_FAST. + UltraFastMode, + /// Unknown. + UnknownMode, +} + +/// I2C standard mode max bus frequency in hz +pub const I2C_MAX_STANDARD_MODE_FREQ: u32 = 100000; +/// I2C fast mode max bus frequency in hz +pub const I2C_MAX_FAST_MODE_FREQ: u32 = 400000; +/// I2C fast plus mode max bus frequency in hz +pub const I2C_MAX_FAST_MODE_PLUS_FREQ: u32 = 1000000; +/// I2C turbo mode max bus frequency in hz +pub const I2C_MAX_TURBO_MODE_FREQ: u32 = 1400000; +/// I2C high speed mode max bus frequency in hz +pub const I2C_MAX_HIGH_SPEED_MODE_FREQ: u32 = 3400000; +/// I2C ultra fast mode max bus frequency in hz +pub const I2C_MAX_ULTRA_FAST_MODE_FREQ: u32 = 5000000; + +impl I2cSpeedMode { + /// From a u32 bus_freq_hz to SpeedMode + pub fn from_bus_freq(bus_freq: u32) -> Self { + match bus_freq { + I2C_MAX_STANDARD_MODE_FREQ => I2cSpeedMode::StandMode, + I2C_MAX_FAST_MODE_FREQ => I2cSpeedMode::FastMode, + I2C_MAX_FAST_MODE_PLUS_FREQ => I2cSpeedMode::FastPlusMode, + I2C_MAX_TURBO_MODE_FREQ => I2cSpeedMode::TurboMode, + I2C_MAX_HIGH_SPEED_MODE_FREQ => I2cSpeedMode::HighSpeedMode, + I2C_MAX_ULTRA_FAST_MODE_FREQ => I2cSpeedMode::UltraFastMode, + _ => I2cSpeedMode::UnknownMode, + } + } +} + +impl fmt::Display for I2cSpeedMode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +/// I2C timing config for all i2c driver +/// +/// An instance of `I2cTiming` include can be used for any i2c driver to describe +/// the bus frequency in Hz +/// time SCL signal takes to rise in ns; t(r) in the I2C specification +/// time SCL signal takes to fall in ns; t(f) in the I2C specification +/// time IP core additionally needs to setup SCL in ns +/// time SDA signal takes to fall in ns; t(f) in the I2C specification +/// time IP core additionally needs to hold SDA in ns +/// width in ns of spikes on i2c lines that the IP core digital filter can filter out +/// threshold frequency for the low pass IP core analog filter +#[derive(Clone,Copy,PartialEq, Eq, Debug)] +pub struct I2cTiming { + bus_freq_hz: u32, + scl_rise_ns: u32, + scl_fall_ns: u32, + scl_int_delay_ns: u32, + sda_fall_ns: u32, + sda_hold_ns: u32, + digital_filter_width_ns: u32, + analog_filter_cutoff_freq_hz: u32, +} + +impl Default for I2cTiming { + fn default() -> Self { + let mut s = ::core::mem::MaybeUninit::::uninit(); + unsafe { + ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} + +impl I2cTiming { + /// New An Default of timing configuration for a special SpeedMode + pub fn new(mode: I2cSpeedMode, use_default: bool) -> Self { + // SAFETY: The variables will be fully initialized later. + let t = Self::default(); + + if use_default { + match mode { + I2cSpeedMode::StandMode => t + .with_base_config_enable(I2C_MAX_STANDARD_MODE_FREQ,1000,300), + I2cSpeedMode::FastMode => t + .with_base_config_enable(I2C_MAX_FAST_MODE_FREQ, 300, 300), + I2cSpeedMode::FastPlusMode => t + .with_base_config_enable(I2C_MAX_FAST_MODE_PLUS_FREQ, 120, 120), + I2cSpeedMode::TurboMode => t + .with_base_config_enable(I2C_MAX_TURBO_MODE_FREQ, 120, 120), + I2cSpeedMode::HighSpeedMode => t + .with_base_config_enable(I2C_MAX_HIGH_SPEED_MODE_FREQ, 120, 120), + I2cSpeedMode::UltraFastMode => t + .with_base_config_enable(I2C_MAX_ULTRA_FAST_MODE_FREQ, 120, 120), + _ => panic!("unknown mode"), + }; + } + t + } + + /// Setup the base config of i2c speed mode + pub fn with_base_config_enable( + mut self, + bus_freq_hz: u32, + scl_rise_ns: u32, + scl_fall_ns: u32, + ) -> Self { + self.bus_freq_hz = bus_freq_hz; + self.scl_rise_ns = scl_rise_ns; + self.scl_fall_ns = scl_fall_ns; + self + } + + /// Create i2c timing config from Device + pub fn i2c_parse_fw_timings(dev: &Device, mode: I2cSpeedMode , use_default: bool) -> I2cTiming { + let mut builder = Self::new(mode, use_default); + i2c_parse_timing(dev, c_str!("clock-frequency"), |x| { + builder.with_bus_freq_hz(x) + }); + i2c_parse_timing(dev, c_str!("i2c-scl-rising-time-ns"), |x| { + builder.with_scl_rise_ns(x) + }); + i2c_parse_timing(dev, c_str!("i2c-scl-falling-time-ns"), |x| { + builder.with_scl_fall_ns(x) + }); + i2c_parse_timing(dev, c_str!("i2c-scl-internal-delay-ns"), |x| { + builder.with_scl_int_delay_ns(x) + }); + i2c_parse_timing(dev, c_str!("i2c-sda-falling-time-ns"), |x| { + builder.with_sda_fall_ns(x) + }); + i2c_parse_timing(dev, c_str!("i2c-sda-hold-time-ns"), |x| { + builder.with_sda_hold_ns(x) + }); + i2c_parse_timing(dev, c_str!("i2c-digital-filter-width-ns"), |x| { + builder.with_digital_filter_width_ns(x) + }); + i2c_parse_timing(dev, c_str!("i2c-analog-filter-cutoff-frequency"), |x| { + builder.with_analog_filter_cutoff_freq_hz(x) + }); + builder + } + + /// Setup bus freq HZ + #[inline] + pub fn with_bus_freq_hz(&mut self, bus_freq_hz:u32) -> &mut Self { + self.bus_freq_hz = bus_freq_hz; + self + } + + /// Setup scl rise ns + #[inline] + pub fn with_scl_rise_ns(&mut self, scl_fall_ns:u32) -> &mut Self { + self.scl_fall_ns = scl_fall_ns; + self + } + + /// Setup scl fall ns + #[inline] + pub fn with_scl_fall_ns(&mut self, scl_rise_ns:u32) -> &mut Self { + self.scl_rise_ns = scl_rise_ns; + self + } + + /// Setup scl int delay ns + #[inline] + pub fn with_scl_int_delay_ns(&mut self, scl_int_delay_ns:u32) -> &mut Self { + self.scl_int_delay_ns = scl_int_delay_ns; + self + } + + /// Setup sda fall ns + #[inline] + pub fn with_sda_fall_ns(&mut self, sda_fall_ns:u32) -> &mut Self { + self.sda_fall_ns = sda_fall_ns; + self + } + + /// Setup sda hold ns + #[inline] + pub fn with_sda_hold_ns(&mut self, sda_hold_ns:u32) -> &mut Self { + self.sda_hold_ns = sda_hold_ns; + self + } + + /// Setup digital filter width ns + #[inline] + pub fn with_digital_filter_width_ns(&mut self, digital_filter_width_ns:u32) -> &mut Self { + self.digital_filter_width_ns = digital_filter_width_ns; + self + } + + /// Setup analog filter cutoff freq_hz + #[inline] + pub fn with_analog_filter_cutoff_freq_hz(&mut self, analog_filter_cutoff_freq_hz:u32) -> &mut Self { + self.analog_filter_cutoff_freq_hz = analog_filter_cutoff_freq_hz; + self + } + + /// get bus freq HZ + #[inline] + pub fn get_bus_freq_hz(&self) -> u32 { + self.bus_freq_hz + } + + /// get sda fall ns + #[inline] + pub fn get_sda_fall_ns(&self) -> u32 { + self.sda_fall_ns + } + + /// get scl fall ns + #[inline] + pub fn get_scl_fall_ns(&self) -> u32 { + self.scl_fall_ns + } + + /// get sda hold time + #[inline] + pub fn get_sda_hold_ns(&self) -> u32 { + self.sda_hold_ns + } +} + +fn i2c_parse_timing<'a, F: FnOnce(u32) -> &'a mut I2cTiming>( + dev: &Device, + propname: &'static CStr, + f: F, +) { + // SAFETY: val always valid + if let Ok(val) = dev.device_property_read_u32(propname) { + f(val); + } +} diff --git a/r4l/src/lib.rs b/r4l/src/lib.rs index bfb7036..1d66ae1 100644 --- a/r4l/src/lib.rs +++ b/r4l/src/lib.rs @@ -37,6 +37,9 @@ pub mod math; pub mod delay; pub mod timekeeping; +/// driver +pub mod i2c; + pub use build_error::build_error; /// The top level entrypoint to implementing a kernel module. From c4d568f3103a6c7cfa11baaee88a553a62ceabd0 Mon Sep 17 00:00:00 2001 From: happy-thw Date: Mon, 14 Oct 2024 15:55:23 +0800 Subject: [PATCH 04/14] add dw-apb-i2c driver code that can initialize normally --- Cargo.toml | 1 + drivers/i2c/busses/i2c-designware/src/lib.rs | 1266 +++++++++++++++++- 2 files changed, 1264 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index caa1b6a..5f8582c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = [ "drivers/net/phy/ax88796b", "drivers/sample/minimal/", "drivers/sample/platform/", + "drivers/i2c/busses/i2c-designware", "r4l/", "macros/", ] diff --git a/drivers/i2c/busses/i2c-designware/src/lib.rs b/drivers/i2c/busses/i2c-designware/src/lib.rs index 4b8993f..831e864 100644 --- a/drivers/i2c/busses/i2c-designware/src/lib.rs +++ b/drivers/i2c/busses/i2c-designware/src/lib.rs @@ -10,7 +10,20 @@ use kernel::{ device::Device, module_platform_driver, of, platform, sync::Arc, + i2c::{ + timing::{self, I2cTiming, I2cSpeedMode }, + msg::{self, I2cMsgFlags, I2cMsgInfo, GeneralI2cMsgInfo }, + functionality::I2cFuncFlags, + }, + timekeeping::read_poll_timeout, + sync::SpinLock, + types::genmask, prelude::*, + regmap, + math, + delay, + new_spinlock, + // completion::Completion, }; module_platform_driver! { @@ -51,10 +64,1257 @@ impl platform::Driver for DwI2cDriver { // Linux Raw id table kernel::driver_of_id_table!(DW_I2C_OF_MATCH_TABLE); - fn probe(pdev: &mut platform::Device,_id_info: Option<&Self::IdInfo>, - ) -> Result { + fn probe(pdev: &mut platform::Device, _id_info: Option<&Self::IdInfo> ) -> Result { let irq = pdev.irq_resource(0)?; - pr_err!("enter i2c platform probe func, get irq {}",irq); + let reg_base = pdev.ioremap_resource(0)?; + let dev = Device::from_dev(pdev); + let timing = I2cTiming::i2c_parse_fw_timings(&dev, I2cSpeedMode::StandMode, false); + + pr_info!("enter i2c platform probe func, get irq {}",irq); + pr_info!("regbase is {:#x}",reg_base); + + // give a fixed value : clk.get_rate() = 100000000, clk_rate_khz = 100000 + let clk_rate_khz = (100000000 / 1000) as u32; + + // create master driver instance + let driver_config = I2cDwDriverConfig::new(timing, clk_rate_khz); + let mut i2c_master_driver = I2cDwMasterDriver::new(driver_config, reg_base); + i2c_master_driver.setup()?; + + // Todo: create data + Ok(Arc::new(DwI2cData())) } } + +/// I2cDwDriverConfig +#[allow(dead_code)] +#[derive(Debug, Clone)] +pub struct I2cDwDriverConfig { + timing: I2cTiming, + clk_rate_khz: u32, +} + +impl I2cDwDriverConfig { + /// Create a dw-apb-i2c timing Config + pub fn new(timing: I2cTiming, clk_rate_khz: u32) -> Self { + Self { + timing, + clk_rate_khz, + } + } +} + +/// The I2cDesignware Core Driver +#[allow(dead_code)] +pub(crate) struct I2cDwCoreDriver { + /// I2c controller base address + pub(crate) base: usize, + /// Config From external + pub(crate) ext_config: I2cDwDriverConfig, + /// Corrected bus_freq_hz + pub(crate) bus_freq_hz: u32, + /// Corrected sda_hold_time + pub(crate) sda_hold_time: u32, + /// I2c functionality + pub(crate) functionality: u32, + /// I2c SpeedMode + speed_mode: I2cSpeedMode, +} + +unsafe impl Sync for I2cDwCoreDriver {} +unsafe impl Send for I2cDwCoreDriver {} + + +#[allow(dead_code)] +impl I2cDwCoreDriver { + pub(crate) fn new(config: I2cDwDriverConfig, base_addr: usize) -> Self { + Self { + ext_config: config, + bus_freq_hz: 0, + sda_hold_time: 0, + functionality: DW_I2C_DEFAULT_FUNCTIONALITY, + speed_mode: I2cSpeedMode::StandMode, + base: base_addr, + } + } + + pub(crate) fn speed_check(&mut self) -> Result<()> { + let bus_freq_hz = self.ext_config.timing.get_bus_freq_hz(); + + if !I2C_DESIGNWARE_SUPPORT_SPEED.contains(&bus_freq_hz) { + pr_err!("{bus_freq_hz} Hz is unsupported, only 100kHz, 400kHz, 1MHz and 3.4MHz are supported"); + return Err(EINVAL); + } + self.bus_freq_hz = bus_freq_hz; + + // Check is high speed possible and fall back to fast mode if not + let comp_param1 = self.read_ic_comp_param1(); + if comp_param1 & DW_IC_COMP_PARAM_1_SPEED_MODE_MASK != DW_IC_COMP_PARAM_1_SPEED_MODE_HIGH + && self.bus_freq_hz == timing::I2C_MAX_HIGH_SPEED_MODE_FREQ + { + pr_err!("High Speed not supported! Fall back to fast mode"); + self.bus_freq_hz = timing::I2C_MAX_FAST_MODE_FREQ; + } + + self.speed_mode = I2cSpeedMode::from_bus_freq(self.bus_freq_hz); + Ok(()) + } + + pub(crate) fn com_type_check(&mut self) -> Result<()> { + let com_type = regmap::reg_read(self.base, DW_IC_COMP_TYPE); + if com_type == DW_IC_COMP_TYPE_VALUE { + return Ok(()) + } else if com_type == DW_IC_COMP_TYPE_VALUE & 0x0000ffff { + pr_err!("com_type check Failed, not support 16 bit system "); + return Err(EINVAL); + } else if com_type == DW_IC_COMP_TYPE_VALUE.to_be() { + pr_err!("com_type check Failed, not support BE system "); + return Err(EINVAL); + } else { + pr_err!( + "com_type check failed, Unknown Synopsys component type: {:x}", + com_type + ); + return Err(EINVAL); + } + } + + #[inline] + pub(crate) fn functionality_init(&mut self, functionality: u32) { + self.functionality |= functionality; + } + + #[inline] + pub(crate) fn read_ic_comp_param1(&self) -> u32 { + regmap::reg_read(self.base, DW_IC_COMP_PARAM_1) + } + + pub(crate) fn cfg_init_speed(&self, cfg: &mut u32) { + match self.speed_mode { + I2cSpeedMode::StandMode => *cfg |= DW_IC_CON_SPEED_STD, + I2cSpeedMode::HighSpeedMode => *cfg |= DW_IC_CON_SPEED_HIGH, + _ => *cfg |= DW_IC_CON_SPEED_FAST, + } + } + + #[inline] + pub(crate) fn read_ic_con(&self) -> u32 { + regmap::reg_read(self.base, DW_IC_CON) + } + + #[inline] + pub(crate) fn write_ic_con(&self, cfg: u32) { + regmap::reg_write(self.base, DW_IC_CON, cfg); + } + + #[inline] + pub(crate) fn enable_10bitaddr(&self, enable: bool) { + let ic_con = regmap::reg_read(self.base, DW_IC_CON); + if enable { + regmap::reg_write(self.base, DW_IC_CON, ic_con | DW_IC_CON_10BITADDR_MASTER); + } else { + regmap::reg_write(self.base, DW_IC_CON, ic_con & !DW_IC_CON_10BITADDR_MASTER); + } + } + + pub(crate) fn write_sda_hold_time(&self) { + if self.sda_hold_time !=0 { + regmap::reg_write(self.base, DW_IC_SDA_HOLD, self.sda_hold_time); + } + } + + pub(crate) fn sda_hold_time_init(&mut self) -> Result<()> { + let comp_ver = regmap::reg_read(self.base, DW_IC_COMP_VERSION); + let ext_sda_hold_ns = self.ext_config.timing.get_sda_hold_ns(); + + if comp_ver < DW_IC_SDA_HOLD_MIN_VERS { + pr_warn!("Hardware too old to adjust SDA hold time."); + self.sda_hold_time = 0; + return Ok(()); + } + + if ext_sda_hold_ns == 0 { + self.sda_hold_time = regmap::reg_read(self.base, DW_IC_SDA_HOLD); + } else { + let sda_hold_time = math::div_round_closest_ull( + (self.ext_config.clk_rate_khz * ext_sda_hold_ns).into(), + math::MICRO) as u32; + // Workaround for avoiding TX arbitration lost in case I2C + // slave pulls SDA down "too quickly" after falling edge of + // SCL by enabling non-zero SDA RX hold. Specification says it + // extends incoming SDA low to high transition while SCL is + // high but it appears to help also above issue. + if (sda_hold_time & DW_IC_SDA_HOLD_RX_MASK) == 0 { + self.sda_hold_time= sda_hold_time | (1 << 16); + } + } + pr_debug!( + "sda hold time is {}, and Tx:Rx = {}:{}", + self.sda_hold_time, + self.sda_hold_time & 0xFFFF, + self.sda_hold_time >> 16, + ); + pr_debug!("I2C Bus Speed: {}", self.speed_mode); + + Ok(()) + } + + pub(crate) fn write_lhcnt(&self, lhcnt: &DwI2cSclLHCnt) { + // Write standard speed timing parameters + regmap::reg_write(self.base, DW_IC_SS_SCL_LCNT, lhcnt.ss_lcnt.into()); + regmap::reg_write(self.base, DW_IC_SS_SCL_HCNT, lhcnt.ss_hcnt.into()); + pr_debug!("write SCL_LCNT:HCNT {}:{}", lhcnt.ss_lcnt, lhcnt.ss_hcnt); + + // Write fast mode/fast mode plus timing parameters + regmap::reg_write(self.base, DW_IC_FS_SCL_LCNT, lhcnt.fs_lcnt.into()); + regmap::reg_write(self.base, DW_IC_FS_SCL_HCNT, lhcnt.fs_hcnt.into()); + pr_debug!("write FS_SCL_LCNT:HCNT {}:{}", lhcnt.fs_lcnt, lhcnt.fs_hcnt); + + // Write high speed timing parameters if supported + if self.speed_mode == I2cSpeedMode::HighSpeedMode { + regmap::reg_write(self.base, DW_IC_HS_SCL_LCNT, lhcnt.hs_lcnt.into()); + regmap::reg_write(self.base, DW_IC_HS_SCL_HCNT, lhcnt.hs_hcnt.into()); + pr_debug!("write HS_SCL_LCNT:HCNT {}:{}", lhcnt.hs_lcnt, lhcnt.hs_hcnt); + } + } + + #[inline] + pub(crate) fn write_fifo(&self, ic_tx: u32, ic_rx: u32) { + regmap::reg_write(self.base, DW_IC_TX_TL, ic_tx); + regmap::reg_write(self.base, DW_IC_RX_TL, ic_rx); + pr_debug!("write fifo tx:rx {}:{}", ic_tx, ic_rx); + } + + pub(crate) fn wait_bus_not_busy(&self) -> Result<()> { + if let Err(e) = read_poll_timeout( + || return regmap::reg_read(self.base, DW_IC_STATUS), + move |x| x & DW_IC_STATUS_ACTIVITY == 0, + 1100, + 20000, + false, + ) { + pr_err!("{:?} while waiting for bus ready", e); + return Err(EBUSY); + } + Ok(()) + + //TODO: bus recovery + } + + pub(crate) fn ic_enable(&self) -> u32 { + regmap::reg_read(self.base, DW_IC_ENABLE) + } + + #[inline] + pub(crate) fn ic_enable_status(&self) { + let _ = regmap::reg_read(self.base, DW_IC_ENABLE_STATUS); + } + + pub(crate) fn read_and_clean_intrbits( + &self, + rx_outstanding: isize, + ) -> (u32 , u32) { + // The IC_INTR_STAT register just indicates "enabled" interrupts. + // The unmasked raw version of interrupt status bits is available + // in the IC_RAW_INTR_STAT register. + // + // That is, + // stat = readl(IC_INTR_STAT); + // equals to, + // stat = readl(IC_RAW_INTR_STAT) & readl(IC_INTR_MASK); + // The raw version might be useful for debugging purposes. + let stat = regmap::reg_read(self.base, DW_IC_INTR_STAT); + let mut abort_source = 0; + + // Do not use the IC_CLR_INTR register to clear interrupts, or + // you'll miss some interrupts, triggered during the period from + // readl(IC_INTR_STAT) to readl(IC_CLR_INTR). + // Instead, use the separately-prepared IC_CLR_* registers. + if stat & DW_IC_INTR_RX_UNDER != 0 { + let _ = regmap::reg_read(self.base, DW_IC_CLR_RX_UNDER); + } + if stat & DW_IC_INTR_RX_OVER != 0 { + let _ = regmap::reg_read(self.base, DW_IC_CLR_RX_OVER); + } + if stat & DW_IC_INTR_TX_OVER != 0 { + let _ = regmap::reg_read(self.base, DW_IC_CLR_TX_OVER); + } + if stat & DW_IC_INTR_RD_REQ != 0 { + let _ = regmap::reg_read(self.base, DW_IC_CLR_RD_REQ); + } + if stat & DW_IC_INTR_TX_ABRT != 0 { + // The IC_TX_ABRT_SOURCE register is cleared whenever + // the IC_CLR_TX_ABRT is read. Preserve it beforehand. + abort_source = regmap::reg_read(self.base, DW_IC_TX_ABRT_SOURCE); + let _ = regmap::reg_read(self.base, DW_IC_CLR_TX_ABRT); + } + if stat & DW_IC_INTR_RX_DONE != 0 { + let _ = regmap::reg_read(self.base, DW_IC_CLR_RX_DONE); + } + if stat & DW_IC_INTR_ACTIVITY != 0 { + let _ = regmap::reg_read(self.base, DW_IC_CLR_ACTIVITY); + } + if stat & DW_IC_INTR_STOP_DET != 0 { + if (rx_outstanding == 0) || (stat & DW_IC_INTR_RX_FULL !=0) { + let _ = regmap::reg_read(self.base, DW_IC_CLR_STOP_DET); + } + } + if stat & DW_IC_INTR_START_DET != 0 { + let _ = regmap::reg_read(self.base, DW_IC_CLR_START_DET); + } + if stat & DW_IC_INTR_GEN_CALL != 0 { + let _ = regmap::reg_read(self.base, DW_IC_CLR_GEN_CALL); + } + (stat, abort_source) + } + + #[inline] + pub(crate) fn write_ic_tar(&self, tar: u32) { + regmap::reg_write(self.base, DW_IC_TAR, tar); + } + + #[inline] + pub(crate) fn read_ic_data_cmd(&self) -> u32 { + regmap::reg_read(self.base, DW_IC_DATA_CMD) + } + + #[inline] + pub(crate) fn write_ic_data_cmd(&self, cmd: u32) { + regmap::reg_write(self.base, DW_IC_DATA_CMD, cmd); + } + + #[inline] + pub(crate) fn read_ic_txflr(&self) -> u32 { + regmap::reg_read(self.base, DW_IC_TXFLR) + } + + #[inline] + pub(crate) fn read_ic_rxflr(&self) -> u32 { + regmap::reg_read(self.base, DW_IC_RXFLR) + } + + #[inline] + pub(crate) fn read_raw_intr_stat(&self) -> u32 { + regmap::reg_read(self.base, DW_IC_RAW_INTR_STAT) + } + + #[inline] + pub(crate) fn write_interrupt_mask(&self, mask: u32) { + regmap::reg_write(self.base, DW_IC_INTR_MASK, mask); + } + + #[inline] + pub(crate) fn read_interrupt_mask(&self) -> u32 { + regmap::reg_read(self.base, DW_IC_INTR_MASK) + } + + #[inline] + pub(crate) fn disable_all_interrupt(&self) { + regmap::reg_write(self.base, DW_IC_INTR_MASK, 0); + } + + #[inline] + pub(crate) fn clear_all_interrupt(&self) { + let _ = regmap::reg_read(self.base, DW_IC_CLR_INTR); + } + + #[allow(dead_code)] + pub(crate) fn disable(&self) { + self.disable_controler(); + // Disable all interrupts + self.disable_all_interrupt(); + self.clear_all_interrupt(); + } + + pub(crate) fn enable_controler(&self) { + regmap::reg_write(self.base, DW_IC_ENABLE, 1); + } + + pub(crate) fn disable_controler(&self) { + let raw_int_stats = self.read_raw_intr_stat(); + let ic_enable = self.ic_enable(); + let need_aborted = raw_int_stats & DW_IC_INTR_MST_ON_HOLD; + + if need_aborted !=0 { + let _ = regmap::reg_write(self.base, DW_IC_ENABLE, ic_enable | DW_IC_ENABLE_ABORT); + if let Err(e) = read_poll_timeout( + || regmap::reg_read(self.base, DW_IC_ENABLE), + move |x| x & DW_IC_ENABLE_ABORT == 0 , + 10, + 100, + false, + ) { + pr_err!("{:?} while trying to abort current transfer", e); + } + } + + let mut try_cnt = 100; + loop { + self.disable_nowait(); + pr_debug!("==== usleep before ======"); + // delay::usleep(100); + // check enable_status + pr_debug!("==== usleep after ======"); + let status = regmap::reg_read(self.base, DW_IC_ENABLE_STATUS); + if status & 1 == 0 { + break; + } + try_cnt -= 1; + if try_cnt == 0 { + pr_err!("timeout in disabling i2c adapter"); + break; + } + } + } + + fn disable_nowait(&self) { + let _ = regmap::reg_write(self.base, DW_IC_ENABLE, 0); + } +} + +enum TransferResult { + // Unexpected irq + UnExpectedInterrupt, + // Recive IRQ abort + Abort, + // All msgs are process success + Fininsh, + // Still need next irq + Continue, +} + +/// Master driver transfer abstract +#[allow(dead_code)] +struct MasterXfer { + /// XferData + msgs: Vec, + /// run time hadware error code + cmd_err: u32, + /// the element index of the current rx message in the msgs array + msg_read_idx: usize, + /// the element index of the current tx message in the msgs array + msg_write_idx: usize, + /// error status of the current transfer + msg_err: Result<()>, + /// copy of the TX_ABRT_SOURCE register + abort_source: u32, + /// current master-rx elements in tx fifo + rx_outstanding: isize, + /// Driver Status + status: u32, +} + +impl Default for MasterXfer { + /// Create an empty XferData + fn default() -> Self { + Self { + msgs: Vec::new(), + cmd_err: 0, + msg_read_idx: 0, + msg_write_idx: 0, + msg_err: Ok(()), + abort_source: 0, + rx_outstanding: 0, + status: 0, + } + } +} + +impl MasterXfer { + #[allow(dead_code)] + fn init(&mut self, msgs: Vec) { + self.msgs = msgs; + self.cmd_err = 0; + self.msg_read_idx = 0; + self.msg_write_idx = 0; + self.msg_err = Ok(()); + self.abort_source = 0; + self.rx_outstanding = 0; + self.status = 0; + } + + #[inline] + pub(crate) fn is_empty_status(&self) -> bool { + self.status == 0 + } + + #[inline] + pub(crate) fn clear_active(&mut self) { + self.status &= !msg::STATUS_ACTIVE; + } + + #[inline] + pub(crate) fn set_active(&mut self) { + self.status |= msg::STATUS_ACTIVE; + } + + #[inline] + pub(crate) fn is_active(&self) -> bool { + (self.status & msg::STATUS_ACTIVE) != 0 + } + + #[inline] + pub(crate) fn is_write_in_progress(&self) -> bool { + (self.status & msg::STATUS_WRITE_IN_PROGRESS) != 0 + } + + #[inline] + pub(crate) fn set_write_in_progress(&mut self, set: bool) { + if set { + self.status |= msg::STATUS_WRITE_IN_PROGRESS; + } else { + self.status &= !msg::STATUS_WRITE_IN_PROGRESS; + } + } + + fn prepare(&mut self, msgs: Vec, master_driver: &I2cDwMasterDriver) { + self.init(msgs); + let core_driver = &master_driver.driver; + // disable the adapter + master_driver.disable(false); + + let first_msg = &self.msgs[self.msg_write_idx as usize]; + let mut ic_tar = 0 ; + // If the slave address is ten bit address, enable 10BITADDR + if first_msg.flags() & I2cMsgFlags::I2C_ADDR_TEN != 0 { + core_driver.enable_10bitaddr(true); + } else { + ic_tar = DW_IC_TAR_10BITADDR_MASTER; + core_driver.enable_10bitaddr(false); + } + + ic_tar |= first_msg.addr() as u32; + core_driver.write_ic_tar(ic_tar); + + // Enforce disabled interrupts (due to HW issues) + core_driver.disable_all_interrupt(); + + // Enable the adapter + core_driver.enable_controler(); + self.set_active(); + // Dummy read to avoid the register getting stuck on Bay Trail + let _ = core_driver.ic_enable_status(); + } + + fn irq_process(&mut self, master_driver: &I2cDwMasterDriver) -> TransferResult { + let core_driver = &master_driver.driver; + + let (stat, abort_source) = + core_driver.read_and_clean_intrbits(self.rx_outstanding); + self.abort_source = abort_source; + + // Unexpected interrupt in driver point of view. State + // variables are either unset or stale so acknowledge and + // disable interrupts for suppressing further interrupts if + // interrupt really came from this HW (E.g. firmware has left + // the HW active). + if !self.is_active() { + return TransferResult::UnExpectedInterrupt; + } + + if stat & DW_IC_INTR_TX_ABRT != 0 { + self.cmd_err |= msg::DW_IC_ERR_TX_ABRT; + self.status &= !msg::STATUS_MASK; + return TransferResult::Abort; + } + + if stat & DW_IC_INTR_RX_FULL != 0 { + self.read_msgs(&master_driver); + } + + if stat & DW_IC_INTR_TX_EMPTY != 0 { + self.write_msgs(&master_driver); + } + + if ((stat & DW_IC_INTR_STOP_DET != 0) || self.msg_err.is_err()) + && self.rx_outstanding == 0 { + return TransferResult::Fininsh; + } + + return TransferResult::Continue; + } + + fn exit(&mut self, master_driver: &I2cDwMasterDriver) -> Result<()> { + // We must disable the adapter before returning and signaling the end + // of the current transfer. Otherwise the hardware might continue + // generating interrupts which in turn causes a race condition with + // the following transfer. Needs some more investigation if the + // additional interrupts are a hardware bug or this driver doesn't + // handle them correctly yet. + master_driver.disable(true); + self.clear_active(); + + match self.msg_err { + Err(e) => { + pr_err!("i2c dw transfer process msg error: {:?}",e); + return Err(e); + } + Ok(_) => {}, + } + + if self.cmd_err == msg::DW_IC_ERR_TX_ABRT { + pr_err!("i2c dw transfer recv tx_abort"); + self.handle_tx_abort()?; + } + + if !self.is_empty_status() { + pr_err!("transfer terminated early - interrupt latency too high?"); + return Err(EIO); + } + Ok(()) + } + + fn handle_tx_abort(&mut self) -> Result<()> { + let abort_source = self.abort_source; + if abort_source & DW_IC_TX_ABRT_NOACK != 0 { + return Err(EIO); + } + if abort_source & DW_IC_TX_ARB_LOST != 0{ + return Err(EAGAIN); + } else if abort_source & DW_IC_TX_ABRT_GCALL_READ != 0{ + return Err(EINVAL); + } else { + return Err(EIO); + } + } + + /// Initiate (and continue) low level master read/write transaction. + /// This function is only called from i2c_dw_isr, and pumping i2c_msg + /// messages into the tx buffer. Even if the size of i2c_msg data is + /// longer than the size of the tx buffer, it handles everything. + /// Todo: need to fix intr_mask + fn write_msgs(&mut self, master_driver: &I2cDwMasterDriver) { + let msg_len = self.msgs.len(); + let core_driver = &master_driver.driver; + let mut intr_mask = DW_IC_INTR_MASTER_MASK; + let addr = self.msgs[self.msg_write_idx].addr(); + let mut need_restart = false; + loop { + let write_idx = self.msg_write_idx; + if write_idx >= msg_len { + break; + } + + if !self.is_write_in_progress() { + //If both IC_EMPTYFIFO_HOLD_MASTER_EN and + //IC_RESTART_EN are set, we must manually + //set restart bit between messages. + if (master_driver.cfg & DW_IC_CON_RESTART_EN !=0) && + write_idx > 0 + { + need_restart = true; + } + } + + let msg = &mut self.msgs[write_idx]; + + if msg.addr() != addr { + self.msg_err = Err(EINVAL); + break; + } + + let flr = core_driver.read_ic_txflr(); + let mut tx_limit = master_driver.tx_fifo_depth - flr; + + let flr = core_driver.read_ic_rxflr(); + let mut rx_limit = master_driver.rx_fifo_depth - flr; + + loop { + if msg.send_end() || rx_limit <=0 || tx_limit <=0 { + break; + } + let mut cmd: u32 = 0 ; + // If IC_EMPTYFIFO_HOLD_MASTER_EN is set we must + // manually set the stop bit. However, it cannot be + // detected from the registers so we set it always + // when writing/reading the last byte. + // + // i2c-core always sets the buffer length of + // I2C_FUNC_SMBUS_BLOCK_DATA to 1. The length will + // be adjusted when receiving the first byte. + // Thus we can't stop the transaction here. + if write_idx == msg_len-1 && + ( msg.flags() & I2cMsgFlags::I2C_MASTER_RECV_LEN == 0 ) && + msg.send_left_last() + { + cmd |= DW_IC_DATA_CMD_STOP; + } + + if need_restart { + cmd |= DW_IC_DATA_CMD_RESTART; + need_restart = false; + } + + if msg.flags() & I2cMsgFlags::I2C_MASTER_READ != 0 { + /* Avoid rx buffer overrun */ + if self.rx_outstanding >= + master_driver.rx_fifo_depth.try_into().unwrap() { + break; + } + cmd |= DW_IC_DATA_CMD_CMD; + rx_limit -= 1; + self.rx_outstanding += 1; + msg.inc_recieve_cmd_cnt(); + } else { + let buf = msg.pop_front_byte() as u32; + cmd |= buf; + } + core_driver.write_ic_data_cmd(cmd); + tx_limit -=1; + } + + // Because we don't know the buffer length in the + // I2C_FUNC_SMBUS_BLOCK_DATA case, we can't stop the + // transaction here. Also disable the TX_EMPTY IRQ + // while waiting for the data length byte to avoid the + // bogus interrupts flood. + if msg.flags() & I2cMsgFlags::I2C_MASTER_RECV_LEN !=0 { + self.set_write_in_progress(true); + intr_mask &= !DW_IC_INTR_TX_EMPTY; + break; + } else if !msg.send_end() { + // wait next time TX_EMPTY interrupt + self.set_write_in_progress(true); + break; + } else { + self.set_write_in_progress(false); + } + self.msg_write_idx +=1; + } + + // If i2c_msg index search is completed, we don't need TX_EMPTY + // interrupt any more. + if self.msg_write_idx == msg_len { + intr_mask &= !DW_IC_INTR_TX_EMPTY; + } + + if self.msg_err.is_err() { + intr_mask = 0; + } + + core_driver.write_interrupt_mask(intr_mask); + } + + fn read_msgs(&mut self, master_driver: &I2cDwMasterDriver) { + let msg_len = self.msgs.len(); + let core_driver = &master_driver.driver; + + loop { + let read_idx = self.msg_read_idx; + if read_idx >= msg_len { + break; + } + + let msg = &mut(self.msgs[read_idx]); + + if msg.flags() & I2cMsgFlags::I2C_MASTER_READ == 0 { + self.msg_read_idx += 1; + continue + } + + let rx_valid = core_driver.read_ic_rxflr(); + + for _ in 0..rx_valid { + // check if buf can be write + if msg.recieve_end() { + break; + } + + let mut ic_data: u8 = core_driver.read_ic_data_cmd() as u8; + + ic_data &= DW_IC_DATA_CMD_DAT as u8; + // Ensure length byte is a valid value + if msg.flags() & I2cMsgFlags::I2C_MASTER_RECV_LEN !=0 { + // if IC_EMPTYFIFO_HOLD_MASTER_EN is set, which cannot be + // detected from the registers, the controller can be + // disabled if the STOP bit is set. But it is only set + // after receiving block data response length in + // I2C_FUNC_SMBUS_BLOCK_DATA case. That needs to read + // another byte with STOP bit set when the block data + // response length is invalid to complete the transaction. + if ic_data == 0 || ic_data > I2C_SMBUS_BLOCK_MAX { + ic_data = 1; + } + let mut buf_len = ic_data as usize; + // Adjust the buffer length and mask the flag + // after receiving the first byte. + if msg.flags() & I2cMsgFlags::I2C_CLIENT_PEC != 0 { + buf_len+=2; + } else { + buf_len+=1; + }; + msg.modify_recieve_threshold(buf_len); + // cacluate read_cmd_cnt + msg.modify_recieve_cmd_cnt(self.rx_outstanding.min(buf_len as isize)); + msg.remove_flag(I2cMsgFlags::I2C_MASTER_RECV_LEN); + + // Received buffer length, re-enable TX_EMPTY interrupt + // to resume the SMBUS transaction. + // core_driver.enable_tx_empty_intr(true); + // let mut intr_mask = regmap::reg_read(base, DW_IC_INTR_MASK); + let mut intr_mask = core_driver.read_interrupt_mask(); + intr_mask |= DW_IC_INTR_TX_EMPTY; + core_driver.write_interrupt_mask(intr_mask); + } + msg.push_byte(ic_data.try_into().unwrap()); + self.rx_outstanding -= 1; + } + + if !msg.recieve_end() { + // wait next time RX_FULL interrupt + return + } else { + self.msg_read_idx +=1; + } + } + } + +} + +/// The I2cDesignware Driver +pub struct I2cDwMasterDriver { + /// I2c Config register set value + cfg: u32, + /// core Driver + driver: I2cDwCoreDriver, + /// I2c scl_LHCNT + lhcnt: DwI2cSclLHCnt, + /// Fifo + tx_fifo_depth: u32, + rx_fifo_depth: u32, + + /// Arc completion + // cmd_complete: Arc, + + /// Since xfer will be used in interrupt handler, + /// the data needs a concurrent mechanism to ensure safety. + /// The driver will ensure that it will not be triggered + /// by interrupts when using locks, + /// so there is no need to use spin_noirq + xfer: SpinLock, +} + +impl I2cDwMasterDriver { + /// Create a new I2cDesignwarDriver + pub fn new(config: I2cDwDriverConfig, base_addr: usize) -> Self { + Self { + cfg: 0, + driver: I2cDwCoreDriver::new(config, base_addr), + lhcnt: DwI2cSclLHCnt::default(), + tx_fifo_depth: 0, + rx_fifo_depth: 0, + // cmd_complete: Completion::new().unwrap(), + xfer: new_spinlock!(MasterXfer::default()), + } + } + + /// Initialize the designware I2C driver config + pub fn setup(&mut self) -> Result<()> { + // com and speed check must be the first step + self.driver.com_type_check()?; + self.driver.speed_check()?; + // init config + self.config_init()?; + self.scl_lhcnt_init()?; + self.driver.sda_hold_time_init()?; + self.fifo_size_init(); + + // Initialize the designware I2C master hardware + self.master_setup(); + self.driver.disable_all_interrupt(); + Ok(()) + } + + /// functionality and cfg init + fn config_init(&mut self) -> Result<()> { + // init functionality + let functionality = I2cFuncFlags::BIT_10_ADDR; + self.driver.functionality_init(functionality); + + // init master cfg + self.cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE | DW_IC_CON_RESTART_EN; + + // On AMD pltforms BIOS advertises the bus clear feature + // and enables the SCL/SDA stuck low. SMU FW does the + // bus recovery process. Driver should not ignore this BIOS + // advertisement of bus clear feature. + let ic_con = self.driver.read_ic_con(); + if ic_con & DW_IC_CON_BUS_CLEAR_CTRL !=0 { + self.cfg |= DW_IC_CON_BUS_CLEAR_CTRL; + } + + self.driver.cfg_init_speed(&mut self.cfg); + + Ok(()) + } + + /// return i2c functionality + pub fn get_functionality(&self) -> u32 { + self.driver.functionality + } + + /// Prepare controller for a transaction and call xfer_msg + pub fn master_transfer(&self, msgs: Vec) -> Result { + let msg_num = msgs.len(); + // reinit complete + // self.cmd_complete.reinit(); + // wait bus free + self.driver.wait_bus_not_busy()?; + // transfer exit make sure interrupt is disabled + // so here lock is safety + let mut transfer = self.xfer.lock(); + transfer.prepare(msgs, &self); + drop(transfer); + // Now, could enable interrupt + self.driver.clear_all_interrupt(); + self.driver.write_interrupt_mask(DW_IC_INTR_MASTER_MASK); + + // // wait transfer complete + // match self.cmd_complete.wait_for_completion_timeout_sec(1) { + // Err(e) => { + // pr_err!("wait complete timeout"); + // //master_setup implicitly disables the adapter + // self.master_setup(); + // self.driver.clear_all_interrupt(); + // self.driver.disable_all_interrupt(); + // return Err(e); + // } + // Ok(_) => (), + // } + + // complete make sure interrupt is disable + // so here lock is safety + let mut transfer = self.xfer.lock(); + transfer.exit(&self)?; + + Ok(msg_num.try_into().unwrap()) + } + + /// Interrupt service routine. This gets called whenever an I2C master interrupt + /// occurs + pub fn irq_handler(&self) -> irq::Return { + let enable = self.driver.ic_enable(); + let stat = self.driver.read_raw_intr_stat(); + + // check raw intr stat + if enable == 0 || (stat & !0b100000000) == 0 { + return irq::Return::None; + } + + // master_transfer make sure when irq hanppend(irq enable) + // no longer lock transfer, so here lock is safety + pr_debug!("enter irq stat: {:#x}, enable: {:#x}", stat, enable); + let mut transfer = self.xfer.lock(); + let result = transfer.irq_process(&self); + drop(transfer); + + match result { + TransferResult::UnExpectedInterrupt => { + self.driver.disable_all_interrupt(); + }, + TransferResult::Abort => { + // Anytime TX_ABRT is set, the contents of the tx/rx + // buffers are flushed. Make sure to skip them. + self.driver.disable_all_interrupt(); + // self.cmd_complete.complete(); + }, + TransferResult::Fininsh => { + () + // self.cmd_complete.complete(); + }, + TransferResult::Continue => (), + } + + return irq::Return::Handled; + } + + fn master_setup(&self) { + // Disable the adapter + self.disable(false); + // Write standard speed timing parameters + self.driver.write_lhcnt(&self.lhcnt); + // Write SDA hold time if supported + self.driver.write_sda_hold_time(); + // Write fifo + self.driver.write_fifo(self.tx_fifo_depth / 2, 0); + // set IC_CON + self.driver.write_ic_con(self.cfg); + } + + fn disable(&self, fast: bool) { + if fast { + self.driver.disable_nowait(); + } else { + self.driver.disable_controler(); + } + } + + fn fifo_size_init(&mut self) { + let param = self.driver.read_ic_comp_param1(); + self.tx_fifo_depth = ((param >> 16) & 0xff) + 1; + self.rx_fifo_depth = ((param >> 8) & 0xff) + 1; + pr_debug!( + "I2C fifo_depth RX:TX = {}: {}", + self.rx_fifo_depth, + self.tx_fifo_depth + ); + } + + fn scl_lhcnt_init(&mut self) -> Result<()> { + let driver = &mut self.driver; + let ic_clk = driver.ext_config.clk_rate_khz; + let mut scl_fall_ns = driver.ext_config.timing.get_scl_fall_ns(); + let mut sda_fall_ns = driver.ext_config.timing.get_sda_fall_ns(); + + // Set standard and fast speed dividers for high/low periods + if scl_fall_ns == 0 { + scl_fall_ns = 300; + } + + if sda_fall_ns == 0 { + sda_fall_ns = 300; + } + + // tLOW = 4.7 us and no offset + self.lhcnt.ss_lcnt = DwI2cSclLHCnt::scl_lcnt(ic_clk, 4700, scl_fall_ns, 0) as u16; + // tHigh = 4 us and no offset DW default + self.lhcnt.ss_hcnt = DwI2cSclLHCnt::scl_hcnt(ic_clk, 4000, sda_fall_ns, false, 0) as u16; + pr_debug!( + "I2C dw Standard Mode HCNT:LCNT = {} : {}", + self.lhcnt.ss_hcnt, + self.lhcnt.ss_lcnt + ); + + let speed_mode = driver.speed_mode; + if speed_mode == I2cSpeedMode::FastPlusMode { + self.lhcnt.fs_lcnt = DwI2cSclLHCnt::scl_lcnt(ic_clk, 500, scl_fall_ns, 0) as u16; + self.lhcnt.fs_hcnt = DwI2cSclLHCnt::scl_hcnt(ic_clk, 260, sda_fall_ns, false, 0) as u16; + pr_debug!( + "I2C Fast Plus Mode HCNT:LCNT = {} : {}", + self.lhcnt.fs_hcnt, + self.lhcnt.fs_lcnt + ); + } else { + self.lhcnt.fs_lcnt = DwI2cSclLHCnt::scl_lcnt(ic_clk, 1300, scl_fall_ns, 0) as u16; + self.lhcnt.fs_hcnt = DwI2cSclLHCnt::scl_hcnt(ic_clk, 600, sda_fall_ns, false, 0) as u16; + pr_debug!( + "I2C Fast Mode HCNT:LCNT = {} : {}", + self.lhcnt.fs_hcnt, + self.lhcnt.fs_lcnt + ); + } + + if speed_mode == I2cSpeedMode::HighSpeedMode { + self.lhcnt.hs_lcnt = DwI2cSclLHCnt::scl_lcnt(ic_clk, 320, scl_fall_ns, 0) as u16; + self.lhcnt.hs_hcnt = DwI2cSclLHCnt::scl_hcnt(ic_clk, 160, sda_fall_ns, false, 0) as u16; + pr_debug!( + "I2C High Speed Mode HCNT:LCNT = {} : {}", + self.lhcnt.hs_hcnt, + self.lhcnt.hs_lcnt + ); + } + Ok(()) + } + +} + +#[allow(dead_code)] +#[derive(Default, Debug, Copy, Clone)] +pub(crate) struct DwI2cSclLHCnt { + /// standard speed HCNT value + pub(crate) ss_hcnt: u16, + /// standard speed LCNT value + pub(crate) ss_lcnt: u16, + /// Fast Speed HCNT value + pub(crate) fs_hcnt: u16, + /// Fast Speed LCNT value + pub(crate) fs_lcnt: u16, + /// Fast Speed Plus HCNT value + pub(crate) fp_hcnt: u16, + /// Fast Speed Plus LCNT value + pub(crate) fp_lcnt: u16, + /// High Speed HCNT value + pub(crate) hs_hcnt: u16, + /// High Speed LCNT value + pub(crate) hs_lcnt: u16, +} + +#[allow(dead_code)] +impl DwI2cSclLHCnt { + /// Conditional expression: + /// + /// IC_[FS]S_SCL_LCNT + 1 >= IC_CLK * (tLOW + tf) + /// + /// DW I2C core starts counting the SCL CNTs for the LOW period + /// of the SCL clock (tLOW) as soon as it pulls the SCL line. + /// In order to meet the tLOW timing spec, we need to take into + /// account the fall time of SCL signal (tf). Default tf value + /// should be 0.3 us, for safety. + pub(crate) fn scl_lcnt(ic_clk: u32, tlow: u32, tf: u32, offset: u32) -> u32 { + pr_debug!( + "scl_lcnt: ic_clk: {} , tlow:{} tf:{} , offset:{}", + ic_clk, + tlow, + tf, + offset + ); + let right: u64 = ic_clk as u64 * (tlow as u64 + tf as u64); + (math::div_round_closest_ull(right, math::MICRO) - 1 + offset as u64).try_into().unwrap() + } + + /// DesignWare I2C core doesn't seem to have solid strategy to meet + /// the tHD;STA timing spec. Configuring _HCNT based on tHIGH spec + /// will result in violation of the tHD;STA spec. + /// Conditional expression1: + /// IC_[FS]S_SCL_HCNT + (1+4+3) >= IC_CLK * tHIGH + /// This is based on the DW manuals, and represents an ideal + /// configuration. The resulting I2C bus speed will be + /// If your hardware is free from tHD;STA issue, try this one. + /// + /// Conditional expression2: + /// IC_[FS]S_SCL_HCNT + 3 >= IC_CLK * (tHD;STA + tf) + /// This is just experimental rule; the tHD;STA period turned + /// out to be proportinal to (_HCNT + 3). With this setting + /// we could meet both tHIGH and tHD;STA timing specs. + /// If unsure, you'd better to take this alternative. + /// + /// The reason why we need to take into account "tf" here, + /// is the same as described in i2c_dw_scl_lcnt(). + pub(crate) fn scl_hcnt(ic_clk: u32, tsymbol: u32, tf: u32, cond: bool, offset: u32) -> u32 { + if cond { + let right: u64 = ic_clk as u64 * tsymbol as u64; + (math::div_round_closest_ull(right, math::MICRO) - 8 + offset as u64).try_into().unwrap() + } else { + let right: u64 = ic_clk as u64 * (tsymbol as u64 + tf as u64); + (math::div_round_closest_ull(right, math::MICRO) - 3 + offset as u64).try_into().unwrap() + } + } +} + + +/// Distributor Control Register Offset. +pub(crate) const DW_IC_CON: usize = 0x00; +pub(crate) const DW_IC_TAR: usize = 0x04; +#[allow(dead_code)] +pub(crate) const DW_IC_SAR: usize = 0x08; +pub(crate) const DW_IC_DATA_CMD: usize = 0x10; +pub(crate) const DW_IC_SS_SCL_HCNT: usize = 0x14; +pub(crate) const DW_IC_SS_SCL_LCNT: usize = 0x18; +pub(crate) const DW_IC_FS_SCL_HCNT: usize = 0x1c; +pub(crate) const DW_IC_FS_SCL_LCNT: usize = 0x20; +pub(crate) const DW_IC_HS_SCL_HCNT: usize = 0x24; +pub(crate) const DW_IC_HS_SCL_LCNT: usize = 0x28; +pub(crate) const DW_IC_INTR_STAT: usize = 0x2c; +pub(crate) const DW_IC_INTR_MASK: usize = 0x30; +pub(crate) const DW_IC_RAW_INTR_STAT: usize = 0x34; +pub(crate) const DW_IC_RX_TL: usize = 0x38; +pub(crate) const DW_IC_TX_TL: usize = 0x3c; +pub(crate) const DW_IC_CLR_INTR: usize = 0x40; +pub(crate) const DW_IC_CLR_RX_UNDER: usize = 0x44; +pub(crate) const DW_IC_CLR_RX_OVER: usize = 0x48; +pub(crate) const DW_IC_CLR_TX_OVER: usize = 0x4c; +pub(crate) const DW_IC_CLR_RD_REQ: usize = 0x50; +pub(crate) const DW_IC_CLR_TX_ABRT: usize = 0x54; +pub(crate) const DW_IC_CLR_RX_DONE: usize = 0x58; +pub(crate) const DW_IC_CLR_ACTIVITY: usize = 0x5c; +pub(crate) const DW_IC_CLR_STOP_DET: usize = 0x60; +pub(crate) const DW_IC_CLR_START_DET:usize = 0x64; +pub(crate) const DW_IC_CLR_GEN_CALL: usize = 0x68; +pub(crate) const DW_IC_ENABLE: usize = 0x6c; +pub(crate) const DW_IC_STATUS: usize = 0x70; +pub(crate) const DW_IC_TXFLR: usize = 0x74; +pub(crate) const DW_IC_RXFLR: usize = 0x78; +pub(crate) const DW_IC_SDA_HOLD: usize = 0x7c; +pub(crate) const DW_IC_TX_ABRT_SOURCE: usize = 0x80; +pub(crate) const DW_IC_ENABLE_STATUS: usize = 0x9c; +#[allow(dead_code)] +pub(crate) const DW_IC_CLR_RESTART_DET: usize = 0xa8; +pub(crate) const DW_IC_COMP_PARAM_1: usize = 0xf4; +pub(crate) const DW_IC_COMP_VERSION: usize = 0xf8; +pub(crate) const DW_IC_COMP_TYPE: usize = 0xfc; +/// Designware Component Type number = 0x44_57_01_40. This +/// assigned unique hex value is constant and is derived from the two +/// ASCII letters “DW” followed by a 16-bit unsigned number. +/// "DW" + 0x0140 +pub(crate) const DW_IC_COMP_TYPE_VALUE: u32 = 0x44570140; +/// "111" = v1.11 +pub(crate) const DW_IC_SDA_HOLD_MIN_VERS: u32 = 0x3131312A; + +/// DW_IC_CON functionality +pub(crate) const DW_IC_CON_MASTER: u32 = 1 << 0; +pub(crate) const DW_IC_CON_SPEED_STD: u32 = 1 << 1; +pub(crate) const DW_IC_CON_SPEED_FAST: u32 = 2 << 1; +pub(crate) const DW_IC_CON_SPEED_HIGH: u32 = 3 << 1; +pub(crate) const DW_IC_CON_10BITADDR_MASTER: u32 = 1 << 4; +pub(crate) const DW_IC_CON_RESTART_EN: u32 = 1 << 5; +pub(crate) const DW_IC_CON_SLAVE_DISABLE: u32 = 1 << 6; +pub(crate) const DW_IC_CON_BUS_CLEAR_CTRL: u32 = 1 << 11; + +/// DW_IC_INTR_RX functionality +pub(crate) const DW_IC_INTR_RX_UNDER:u32 = 1 << 0 ; +pub(crate) const DW_IC_INTR_RX_OVER: u32 = 1 << 1 ; +pub(crate) const DW_IC_INTR_RX_FULL: u32 = 1 << 2 ; +pub(crate) const DW_IC_INTR_TX_OVER: u32 = 1 << 3 ; +pub(crate) const DW_IC_INTR_TX_EMPTY:u32 = 1 << 4 ; +pub(crate) const DW_IC_INTR_RD_REQ: u32 = 1 << 5 ; +pub(crate) const DW_IC_INTR_TX_ABRT: u32 = 1 << 6 ; +pub(crate) const DW_IC_INTR_RX_DONE: u32 = 1 << 7 ; +pub(crate) const DW_IC_INTR_ACTIVITY:u32 = 1 << 8 ; +pub(crate) const DW_IC_INTR_STOP_DET:u32 = 1 << 9 ; +pub(crate) const DW_IC_INTR_START_DET: u32 = 1 << 10 ; +pub(crate) const DW_IC_INTR_GEN_CALL: u32 = 1 << 11 ; +#[allow(dead_code)] +pub(crate) const DW_IC_INTR_RESTART_DET:u32 = 1 << 12 ; +pub(crate) const DW_IC_INTR_MST_ON_HOLD:u32 = 1 << 13 ; + +pub(crate) const DW_IC_INTR_MASTER_MASK:u32 = + DW_IC_INTR_RX_FULL | + DW_IC_INTR_TX_EMPTY | + DW_IC_INTR_TX_ABRT | + DW_IC_INTR_STOP_DET ; + +/// DW_IC_ENABLE_ABORT functionality +pub(crate) const DW_IC_ENABLE_ABORT: u32 = 1 << 1; + +/// DW_IC_STATUS functionality +pub(crate) const DW_IC_STATUS_ACTIVITY:u32 = 1 << 0; + +/// DW_IC_COMP_PARAM_1 functionality +pub(crate) const DW_IC_COMP_PARAM_1_SPEED_MODE_HIGH:u32 = (1 << 2) | (1 << 3); +pub(crate) const DW_IC_COMP_PARAM_1_SPEED_MODE_MASK:u32 = genmask(3, 2); + +/// DW_IC_SDA_HOLD_RX_MASK +pub const DW_IC_SDA_HOLD_RX_MASK: u32 = genmask(23, 16); +/// DW_IC_TAR_10BITADDR_MASTER +pub(crate) const DW_IC_TAR_10BITADDR_MASTER: u32 = 1 << 12; + +/// DW_IC_DATA functionality +pub(crate) const DW_IC_DATA_CMD_DAT: u32 = genmask(7, 0); +pub(crate) const DW_IC_DATA_CMD_CMD: u32 = 1 << 8; +pub(crate) const DW_IC_DATA_CMD_STOP: u32 = 1 << 9; +pub(crate) const DW_IC_DATA_CMD_RESTART: u32 = 1 << 10; + +/// DW_IC_TX_ABRT functionality +pub(crate) const DW_IC_TX_ABRT_GCALL_READ: u32 = 1 << 5; +pub(crate) const DW_IC_TX_ARB_LOST: u32 = 1 << 12; +pub(crate) const DW_IC_TX_ABRT_NOACK:u32 = (1<<0) | (1<<1) | (1<<2) | (1<<3) | (1<<4) ; + +/// dw-i2c-defualt functionality +pub const DW_I2C_DEFAULT_FUNCTIONALITY: u32 = I2cFuncFlags::I2C | + I2cFuncFlags::SMBUS_BYTE | + I2cFuncFlags::SMBUS_BYTE_DATA | + I2cFuncFlags::SMBUS_WORD_DATA | + I2cFuncFlags::SMBUS_BLOCK_DATA | + I2cFuncFlags::SMBUS_I2C_BLOCK ; + +/// support mode speed +const I2C_DESIGNWARE_SUPPORT_SPEED: [u32; 4] = [ + timing::I2C_MAX_STANDARD_MODE_FREQ, + timing::I2C_MAX_FAST_MODE_FREQ, + timing::I2C_MAX_FAST_MODE_PLUS_FREQ, + timing::I2C_MAX_HIGH_SPEED_MODE_FREQ, +]; + +/// Data for SMBus Messages +pub const I2C_SMBUS_BLOCK_MAX:u8 = 32; \ No newline at end of file From 5561949fc1580b9963fde69eb2d5ee5d1c20055b Mon Sep 17 00:00:00 2001 From: happy-thw Date: Mon, 14 Oct 2024 15:56:41 +0800 Subject: [PATCH 05/14] add macro of new_spinlock --- r4l/src/sync.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/r4l/src/sync.rs b/r4l/src/sync.rs index b285cc5..647c3d5 100644 --- a/r4l/src/sync.rs +++ b/r4l/src/sync.rs @@ -8,8 +8,17 @@ #[cfg(feature = "starry")] mod sync { pub use alloc::sync::Arc; - pub use axsync::spin::SpinNoIrq; + pub use axsync::spin::{self, SpinNoIrq, SpinNoPreempt}; pub use axsync::Mutex; + + pub type SpinLock = SpinNoPreempt; } pub use sync::*; + +#[macro_export] +macro_rules! new_spinlock { + ($inner:expr $(, $name:literal)? $(,)?) => { + $crate::sync::SpinLock::new($inner) + }; +} \ No newline at end of file From eabd925503ff936796cf620048e68971bd761780 Mon Sep 17 00:00:00 2001 From: happy-thw Date: Mon, 14 Oct 2024 15:57:52 +0800 Subject: [PATCH 06/14] add completion that can't use --- r4l/src/completion.rs | 60 +++++++++++++++++++++++++++++++++++++++++++ r4l/src/lib.rs | 1 + r4l/src/sync.rs | 1 + 3 files changed, 62 insertions(+) create mode 100644 r4l/src/completion.rs diff --git a/r4l/src/completion.rs b/r4l/src/completion.rs new file mode 100644 index 0000000..633aaf0 --- /dev/null +++ b/r4l/src/completion.rs @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! I2c kernel interface +//! + +use crate::sync::Completion as AxCompletion; +use crate::prelude::*; +use alloc::sync::Arc; +use crate::error::Result; + +/// Linux completion wrapper +/// +/// Wraps the kernel's C `struct completion`. +/// +#[repr(transparent)] +pub struct Completion(AxCompletion); + +// SAFETY: `Device` only holds a pointer to a C device, which is safe to be used from any thread. +unsafe impl Send for Completion {} + +// SAFETY: `Device` only holds a pointer to a C device, references to which are safe to be used +// from any thread. +unsafe impl Sync for Completion {} + +/// Creates a [`completion`] initialiser with the given name and a newly-created lock class. +/// +/// It uses the name if one is given, otherwise it generates one based on the file name and line +/// number. +#[macro_export] +macro_rules! new_completion { + ($($name:literal)?) => { + $crate::completion::Completion::new($crate::optional_name!($($name)?), $crate::static_lock_class!()) + }; +} + +impl Completion { + /// Creates a new instance of [`Completion`]. + pub fn new() -> Result> { + Ok(Arc::new(Self(AxCompletion::new()))) + } + + pub fn reinit(&self) { + self.0.reinit(); + } + + pub fn complete(&self) { + self.0.complete(); + } + + pub fn wait_for_completion(&self) { + self.0.wait_for_completion(); + } + + pub fn wait_for_completion_timeout_sec(&self, timeout: usize) -> Result<()> { + if self.0.wait_for_completion_timeout(timeout as u64) { + return Err(ETIMEDOUT); + }; + Ok(()) + } +} \ No newline at end of file diff --git a/r4l/src/lib.rs b/r4l/src/lib.rs index 1d66ae1..a1f59a3 100644 --- a/r4l/src/lib.rs +++ b/r4l/src/lib.rs @@ -35,6 +35,7 @@ pub mod io; pub mod regmap; pub mod math; pub mod delay; +// pub mod completion; pub mod timekeeping; /// driver diff --git a/r4l/src/sync.rs b/r4l/src/sync.rs index 647c3d5..d99109c 100644 --- a/r4l/src/sync.rs +++ b/r4l/src/sync.rs @@ -10,6 +10,7 @@ mod sync { pub use alloc::sync::Arc; pub use axsync::spin::{self, SpinNoIrq, SpinNoPreempt}; pub use axsync::Mutex; + // pub use axsync::Completion; pub type SpinLock = SpinNoPreempt; } From f033c61750a4b72738f694f4afba80b572366c0a Mon Sep 17 00:00:00 2001 From: happy-thw Date: Tue, 29 Oct 2024 10:22:17 +0800 Subject: [PATCH 07/14] Modify the cfg to make it usable in Arceos --- drivers/i2c/busses/i2c-designware/Cargo.toml | 4 ++-- drivers/net/phy/ax88796b/Cargo.toml | 4 ++-- drivers/net/phy/mdio/Cargo.toml | 4 ++-- drivers/sample/minimal/Cargo.toml | 4 ++-- drivers/sample/platform/Cargo.toml | 4 ++-- r4l/Cargo.toml | 12 ++++++------ r4l/src/delay.rs | 2 +- r4l/src/error.rs | 2 +- r4l/src/io/os_api.rs | 2 +- r4l/src/irq/flags.rs | 2 +- r4l/src/irq/mod.rs | 10 +++++----- r4l/src/irq/os_api.rs | 2 +- r4l/src/linked_list.rs | 2 +- r4l/src/print.rs | 2 +- r4l/src/regmap.rs | 4 ---- r4l/src/sync.rs | 2 +- r4l/src/timekeeping.rs | 6 +++--- 17 files changed, 32 insertions(+), 36 deletions(-) diff --git a/drivers/i2c/busses/i2c-designware/Cargo.toml b/drivers/i2c/busses/i2c-designware/Cargo.toml index 41a4a06..7fc0db8 100644 --- a/drivers/i2c/busses/i2c-designware/Cargo.toml +++ b/drivers/i2c/busses/i2c-designware/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/guoweikang/r4l.git" [features] no_global_oom_handling=[] -starry = ["kernel/starry"] -default = ["starry"] +arceos = ["kernel/arceos"] +default = ["arceos"] [dependencies] kernel = { package="r4l", path = "../../../../r4l"} diff --git a/drivers/net/phy/ax88796b/Cargo.toml b/drivers/net/phy/ax88796b/Cargo.toml index 8c27f5d..877a4d3 100644 --- a/drivers/net/phy/ax88796b/Cargo.toml +++ b/drivers/net/phy/ax88796b/Cargo.toml @@ -9,7 +9,7 @@ homepage = "" repository = "https://github.com/guoweikang/r4l.git" [features] -starry=["kernel/starry"] -default =["starry"] +arceos=["kernel/arceos"] +default =["arceos"] [dependencies] kernel = { package="r4l", path = "../../../../r4l/"} diff --git a/drivers/net/phy/mdio/Cargo.toml b/drivers/net/phy/mdio/Cargo.toml index e026b46..25587fd 100644 --- a/drivers/net/phy/mdio/Cargo.toml +++ b/drivers/net/phy/mdio/Cargo.toml @@ -9,7 +9,7 @@ homepage = "" repository = "https://github.com/guoweikang/r4l.git" [features] -starry=["kernel/starry"] -default =["starry"] +arceos=["kernel/arceos"] +default =["arceos"] [dependencies] kernel = { package="r4l", path = "../../../../r4l/"} diff --git a/drivers/sample/minimal/Cargo.toml b/drivers/sample/minimal/Cargo.toml index 15543a9..a034e9e 100644 --- a/drivers/sample/minimal/Cargo.toml +++ b/drivers/sample/minimal/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/guoweikang/r4l.git" [features] no_global_oom_handling=[] -starry = ["kernel/starry"] -default = ["starry"] +arceos = ["kernel/arceos"] +default = ["arceos"] [dependencies] kernel = { package="r4l", path = "../../../r4l"} diff --git a/drivers/sample/platform/Cargo.toml b/drivers/sample/platform/Cargo.toml index 78dc5ec..ff41ba5 100644 --- a/drivers/sample/platform/Cargo.toml +++ b/drivers/sample/platform/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/guoweikang/r4l.git" [features] no_global_oom_handling=[] -starry = ["kernel/starry"] -default = ["starry"] +arceos = ["kernel/arceos"] +default = ["arceos"] [dependencies] kernel = { package="r4l", path = "../../../r4l"} diff --git a/r4l/Cargo.toml b/r4l/Cargo.toml index 6dd2428..d659a4c 100644 --- a/r4l/Cargo.toml +++ b/r4l/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/guoweikang/r4l.git" [features] rust_os=[] -starry=["rust_os", "axerrno", "axtask/irq", "axlog", "axhal", "axsync/irq", "linked_list"] +arceos=["rust_os", "axerrno", "axtask/irq", "axlog", "axhal", "axsync", "linked_list"] [dependencies] bitflags = "2.5.0" @@ -18,12 +18,12 @@ derive_builder = {version ="0.20", default-features = false} cfg-if = "1.0" macros = {package ="r4l-macros", path = "../macros"} -of = { git = "https://github.com/Starry-OS/of.git"} +of_fdt = {package ="of_fdt", path = "../of"} # Arceos Starry dependencies +axtask = { workspace = true, optional = true } +axlog = { workspace = true, optional = true } +axhal = { workspace = true, optional = true } +axsync = { workspace = true, optional = true } axerrno = {git = "https://github.com/Starry-OS/axerrno.git", optional=true} -axtask = {git = "https://github.com/Starry-OS/axtask.git", optional=true} -axlog = {git = "https://github.com/Starry-OS/axlog.git", optional=true} -axhal = {git = "https://github.com/Starry-OS/axhal.git", optional=true} -axsync = {git = "https://github.com/Starry-OS/axsync.git", optional=true} linked_list = {git = "https://github.com/Starry-OS/linked_list.git", optional=true} diff --git a/r4l/src/delay.rs b/r4l/src/delay.rs index 89c1f98..009c393 100644 --- a/r4l/src/delay.rs +++ b/r4l/src/delay.rs @@ -1,7 +1,7 @@ //! I2c delay interface //! -#[cfg(feature = "starry")] +#[cfg(feature = "arceos")] mod delay { pub use axtask::sleep; } diff --git a/r4l/src/error.rs b/r4l/src/error.rs index d88df37..d97d769 100644 --- a/r4l/src/error.rs +++ b/r4l/src/error.rs @@ -5,7 +5,7 @@ //! - Errno: const variable in mod code //! -#[cfg(feature = "starry")] +#[cfg(feature = "arceos")] mod error { use axerrno::AxError; pub type Error = AxError; diff --git a/r4l/src/io/os_api.rs b/r4l/src/io/os_api.rs index e6c84a5..44e5e47 100644 --- a/r4l/src/io/os_api.rs +++ b/r4l/src/io/os_api.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 -#[cfg(feature = "starry")] +#[cfg(feature = "arceos")] mod os_io_interface { use axhal::mem::{phys_to_virt, PhysAddr}; diff --git a/r4l/src/irq/flags.rs b/r4l/src/irq/flags.rs index b032eed..4f93099 100644 --- a/r4l/src/irq/flags.rs +++ b/r4l/src/irq/flags.rs @@ -6,7 +6,7 @@ bitflags! { /// Container for interrupt flags. #[repr(transparent)] #[derive(Debug, Copy, Clone, PartialEq, Eq)] - pub struct Flags: u32 { + pub struct Flags: usize { /// Use the interrupt line as already configured. const TRIGGER_NONE = 1; /// The interrupt is triggered when the signal goes from low to high. diff --git a/r4l/src/irq/mod.rs b/r4l/src/irq/mod.rs index 12c5e98..158157c 100644 --- a/r4l/src/irq/mod.rs +++ b/r4l/src/irq/mod.rs @@ -45,7 +45,7 @@ impl InternalRegistration { irq: u32, handler: IrqHandler, _thread_fn: Option, - flags: usize, + flags: Flags, name: fmt::Arguments<'_>, ) -> Result { let name = CString::try_from_fmt(name)?; @@ -88,13 +88,13 @@ pub trait Handler { /// impl irq::Handler for Example { /// type Data = Box; /// -/// fn handle_irq(_data: &u32) -> irq::Return { +/// fn handle_irq(_data: &Self::Data) -> irq::Return { /// irq::Return::None /// } /// } /// /// fn request_irq(irq: u32, data: Box) -> Result> { -/// irq::Registration::try_new(irq, data, irq::flags::SHARED, fmt!("example_{irq}")) +/// irq::Registration::try_new(irq, data, irq::Flags::SHARED, fmt!("example_{irq}")) /// } /// ``` pub struct Registration(InternalRegistration); @@ -119,14 +119,14 @@ impl Registration { pub fn try_new ( irq: u32, data: H::Data, - flags: usize, + flags: Flags, name: fmt::Arguments<'_>, ) -> Result where ::Data: 'static { IRQ_DATA_ARRAY.lock().push(IrqData{data: Box::new(data), irq}); Ok(Self(InternalRegistration::try_new(irq, Self::handler::, None, flags, name)?)) } - #[cfg(feature = "starry")] + #[cfg(feature = "arceos")] fn handler (irq:u32) where ::Data: 'static { let lock = IRQ_DATA_ARRAY.lock(); let irq_data = lock.iter().find(|x|x.irq == irq).unwrap(); diff --git a/r4l/src/irq/os_api.rs b/r4l/src/irq/os_api.rs index c3a9e17..02bed4c 100644 --- a/r4l/src/irq/os_api.rs +++ b/r4l/src/irq/os_api.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 -#[cfg(feature = "starry")] +#[cfg(feature = "arceos")] mod os_irq_interface { use axhal::irq::register_handler; diff --git a/r4l/src/linked_list.rs b/r4l/src/linked_list.rs index 83c05f7..59bff6f 100644 --- a/r4l/src/linked_list.rs +++ b/r4l/src/linked_list.rs @@ -3,7 +3,7 @@ //! Every OS should provides: //! linked_list -#[cfg(feature = "starry")] +#[cfg(feature = "arceos")] mod list { pub use linked_list::*; } diff --git a/r4l/src/print.rs b/r4l/src/print.rs index fad3a89..9f913d8 100644 --- a/r4l/src/print.rs +++ b/r4l/src/print.rs @@ -26,7 +26,7 @@ pub enum LogLevel { Cont, } -#[cfg(feature = "starry")] +#[cfg(feature = "arceos")] pub mod log { pub use axlog; #[doc(hidden)] diff --git a/r4l/src/regmap.rs b/r4l/src/regmap.rs index 5359663..b844c29 100644 --- a/r4l/src/regmap.rs +++ b/r4l/src/regmap.rs @@ -2,15 +2,12 @@ //! Register map access API. -use crate::pr_info; - /// reg_read pub fn reg_read( reg_base: usize, offset: usize, ) -> u32 { let base = reg_base + offset; - pr_info!("reg_read: reg_base is {:#x}, offset is {:#x}, reg_addr is {:#x}", reg_base, offset, base); let val = unsafe {::core::ptr::read_volatile(base as _)}; val } @@ -22,7 +19,6 @@ pub fn reg_write( val: u32, ) -> u32 { let base = reg_base + offset ; - pr_info!("reg_write: reg_base is {:#x}, offset is {:#x}, reg_addr is {:#x}, val is {val:#}", reg_base, offset, base); unsafe { ::core::ptr::write_volatile(base as _, val) }; return 0; } \ No newline at end of file diff --git a/r4l/src/sync.rs b/r4l/src/sync.rs index d99109c..54fe5fb 100644 --- a/r4l/src/sync.rs +++ b/r4l/src/sync.rs @@ -5,7 +5,7 @@ //! - Mutex //! - SpinLock -#[cfg(feature = "starry")] +#[cfg(feature = "arceos")] mod sync { pub use alloc::sync::Arc; pub use axsync::spin::{self, SpinNoIrq, SpinNoPreempt}; diff --git a/r4l/src/timekeeping.rs b/r4l/src/timekeeping.rs index 3a51d57..f7943e5 100644 --- a/r4l/src/timekeeping.rs +++ b/r4l/src/timekeeping.rs @@ -3,13 +3,13 @@ //! I2c kernel interface //! -#[cfg(feature = "starry")] +#[cfg(feature = "arceos")] mod timekeeping { - pub use axhal::time::current_time_nanos; + pub use axhal::time::monotonic_time_nanos; /// Get Ktime pub fn ktime_get() -> u64 { - current_time_nanos() + monotonic_time_nanos() } } From d5e9bd98963327c72cfa38b30edaa8a0176155c6 Mon Sep 17 00:00:00 2001 From: happy-thw Date: Tue, 29 Oct 2024 10:30:06 +0800 Subject: [PATCH 08/14] Add 'of' crate as part of the cross-kernel driver framework. --- Cargo.toml | 1 + of/.gitignore | 14 + of/Cargo.toml | 11 + of/src/kernel_nodes.rs | 70 + of/src/lib.rs | 148 ++ of/src/parsing.rs | 15 + of/src/phandle_arg.rs | 99 + of/tests/bsta1000b-fada-bus.dtb | Bin 0 -> 62598 bytes of/tests/bsta1000b-fada-bus.dts | 3575 ++++++++++++++++++++++++++++++ of/tests/of.rs | 102 + r4l/src/device.rs | 20 +- r4l/src/init.rs | 11 +- r4l/src/of/platform.rs | 6 +- r4l/src/of/resource.rs | 4 +- r4l/src/platform/platform_dev.rs | 14 +- r4l/src/platform/platform_drv.rs | 4 +- 16 files changed, 4072 insertions(+), 22 deletions(-) create mode 100644 of/.gitignore create mode 100644 of/Cargo.toml create mode 100644 of/src/kernel_nodes.rs create mode 100644 of/src/lib.rs create mode 100644 of/src/parsing.rs create mode 100644 of/src/phandle_arg.rs create mode 100644 of/tests/bsta1000b-fada-bus.dtb create mode 100644 of/tests/bsta1000b-fada-bus.dts create mode 100644 of/tests/of.rs diff --git a/Cargo.toml b/Cargo.toml index 5f8582c..15d7269 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "drivers/i2c/busses/i2c-designware", "r4l/", "macros/", + "of/", ] resolver = "2" diff --git a/of/.gitignore b/of/.gitignore new file mode 100644 index 0000000..6985cf1 --- /dev/null +++ b/of/.gitignore @@ -0,0 +1,14 @@ +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb diff --git a/of/Cargo.toml b/of/Cargo.toml new file mode 100644 index 0000000..ef6a449 --- /dev/null +++ b/of/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "of_fdt" +version = "0.1.0" +edition = "2021" +authors = ["Guo Weikang "] +description = "" +license = "GPL-3.0-or-later OR Apache-2.0" + +[dependencies] +fdt = "0.1.5" +lazy_static = { version = "1.4", features = ["spin_no_std"] } diff --git a/of/src/kernel_nodes.rs b/of/src/kernel_nodes.rs new file mode 100644 index 0000000..8e4fca1 --- /dev/null +++ b/of/src/kernel_nodes.rs @@ -0,0 +1,70 @@ +use crate::parsing::BigEndianU32; +use fdt::standard_nodes::MemoryRegion; + +#[derive(Clone, Copy)] +pub struct Memory { + pub(crate) node: fdt::node::FdtNode<'static, 'static>, +} + +impl Memory { + /// Returns an iterator over all of the available memory regions + pub fn regions(self) -> impl Iterator + 'static { + self.node.reg().unwrap() + } +} + +#[derive(Clone, Copy)] +pub struct Pcsi { + pub(crate) node: fdt::node::FdtNode<'static, 'static>, +} + +impl Pcsi { + /// `compatible` property + pub fn compatible(self) -> &'static str { + self.node.compatible().unwrap().first() + } + + /// `method` property + pub fn method(self) -> &'static str { + self.node + .properties() + .find(|p| p.name == "method") + .and_then(|p| { + core::str::from_utf8(p.value) + .map(|s| s.trim_end_matches('\0')) + .ok() + }) + .unwrap() + } + /// Optional`cpu_suspend` property + pub fn cpu_suspend(self) -> Option { + self.node + .properties() + .find(|p| p.name == "cpu_suspend") + .map(|p| BigEndianU32::from_bytes(p.value).unwrap().get()) + } + + /// Optional`cpu_on` property + pub fn cpu_on(self) -> Option { + self.node + .properties() + .find(|p| p.name == "cpu_on") + .map(|p| BigEndianU32::from_bytes(p.value).unwrap().get()) + } + + /// Optional`cpu_off` property + pub fn cpu_off(self) -> Option { + self.node + .properties() + .find(|p| p.name == "cpu_off") + .map(|p| BigEndianU32::from_bytes(p.value).unwrap().get()) + } + + /// Optional`migrate` property + pub fn migrate(self) -> Option { + self.node + .properties() + .find(|p| p.name == "migrate") + .map(|p| BigEndianU32::from_bytes(p.value).unwrap().get()) + } +} diff --git a/of/src/lib.rs b/of/src/lib.rs new file mode 100644 index 0000000..0f9149d --- /dev/null +++ b/of/src/lib.rs @@ -0,0 +1,148 @@ +//! A pure-Rust #![no_std] crate for parsing Flattened Devicetrees, +//! with the goal of having a very ergonomic and idiomatic API. + +#![no_std] +#![allow(missing_docs)] +pub struct MachineFdt<'a>(fdt::Fdt<'a>); +pub mod kernel_nodes; +pub use fdt::standard_nodes::Cpu; +pub use kernel_nodes::*; + +pub type OfNode<'a> = fdt::node::FdtNode<'a, 'a>; + +mod parsing; +mod phandle_arg; + +pub use phandle_arg::OfPhandleArgs; +use phandle_arg::OfPhandleIterator; + +use crate::parsing::BigEndianU32; + +static mut MY_FDT_PTR: Option<*const u8> = None; + +lazy_static::lazy_static! { + static ref MY_MACHINE_FDT: MachineFdt<'static> = + unsafe {init_from_ptr(MY_FDT_PTR.unwrap())}; +} + +pub fn get_fdt_ptr() -> Option<*const u8> { + unsafe { MY_FDT_PTR } +} + +/// # Safety +/// This function is unsafe because it dereferences a raw pointer. +pub unsafe fn init_fdt_ptr(virt_addr: *const u8) { + MY_FDT_PTR = Some(virt_addr); +} + +/// Init the DTB root, call after dtb finish mapping +unsafe fn init_from_ptr(virt_addr: *const u8) -> MachineFdt<'static> { + MachineFdt(fdt::Fdt::from_ptr(virt_addr).unwrap()) +} + +/// Root Node found model or first compatible +pub fn machin_name() -> &'static str { + let root_node = MY_MACHINE_FDT.0.root(); + let model = root_node + .properties() + .find(|p| p.name == "model") + .and_then(|p| { + core::str::from_utf8(p.value) + .map(|s| s.trim_end_matches('\0')) + .ok() + }); + + if let Some(name) = model { + name + } else { + root_node.compatible().first() + } +} + +/// Searches for a node which contains a `compatible` property and contains +/// one of the strings inside of `with` +pub fn find_compatible_node( + with: &'static [&'static str], +) -> impl Iterator> { + MY_MACHINE_FDT.0.all_nodes().filter(|n| { + n.compatible() + .and_then(|compats| compats.all().find(|c| with.contains(c))) + .is_some() + }) +} + +pub fn of_device_is_available(node: OfNode<'static>) -> bool { + let status = node.properties().find(|p| p.name == "status"); + let ret = match status { + None => true, + Some(st) => { + let res: &'static str = core::str::from_utf8(st.value) + .map(|s| s.trim_end_matches('\0')) + .ok() + .unwrap(); + if res.eq("okay") || res.eq("ok") { + true + } else { + false + } + } + }; + ret +} + +pub fn of_property_read_u32( + node: OfNode<'static>, + name: &'static str, + index: usize, +) -> Option { + let property = node.property(name)?; + let start_idx = index * 4; + if start_idx + 4 > property.value.len() { + return None; + } + Some( + BigEndianU32::from_bytes(&property.value[start_idx..]) + .unwrap() + .get(), + ) +} + +pub fn bootargs() -> Option<&'static str> { + MY_MACHINE_FDT.0.chosen().bootargs() +} + +pub fn fdt_size() -> usize { + MY_MACHINE_FDT.0.total_size() +} + +pub fn memory_nodes() -> impl Iterator { + MY_MACHINE_FDT + .0 + .find_all_nodes("/memory") + .map(|m| kernel_nodes::Memory { node: m }) +} + +pub fn pcsi() -> Option { + MY_MACHINE_FDT + .0 + .find_node("/psci") + .map(|n| kernel_nodes::Pcsi { node: n }) +} + +pub fn cpus() -> impl Iterator> { + MY_MACHINE_FDT.0.cpus() +} + +pub fn find_phandle(phandle: u32) -> Option> { + MY_MACHINE_FDT.0.find_phandle(phandle) +} + +pub fn of_parse_phandle_with_args( + node: OfNode<'static>, + list_name: &'static str, + cell_name: Option<&'static str>, + index: usize, +) -> Option { + let mut iter = OfPhandleIterator::new(node, list_name, cell_name, 0)?; + iter.nth(index) +} diff --git a/of/src/parsing.rs b/of/src/parsing.rs new file mode 100644 index 0000000..d22b3f5 --- /dev/null +++ b/of/src/parsing.rs @@ -0,0 +1,15 @@ +#[derive(Debug, Clone, Copy)] +#[repr(transparent)] +pub struct BigEndianU32(u32); + +impl BigEndianU32 { + pub fn get(self) -> u32 { + self.0 + } + + pub(crate) fn from_bytes(bytes: &[u8]) -> Option { + Some(BigEndianU32(u32::from_be_bytes( + bytes.get(..4)?.try_into().unwrap(), + ))) + } +} diff --git a/of/src/phandle_arg.rs b/of/src/phandle_arg.rs new file mode 100644 index 0000000..386ab7a --- /dev/null +++ b/of/src/phandle_arg.rs @@ -0,0 +1,99 @@ +use fdt::node::NodeProperty; + +use crate::find_phandle; +use crate::BigEndianU32; +use crate::OfNode; + +const MAX_PHANDLE_ARGS: usize = 32; + +pub struct OfPhandleArgs { + pub np: OfNode<'static>, + pub args_count: usize, + pub args: [u32; MAX_PHANDLE_ARGS], +} + +impl OfPhandleArgs { + pub fn new(node: OfNode<'static>, args_count: usize, args: [u32; MAX_PHANDLE_ARGS]) -> Self { + Self { + np: node, + args_count, + args, + } + } +} + +pub(crate) struct OfPhandleIterator { + np: OfNode<'static>, + cells_name: Option<&'static str>, + cell_count: usize, + lists: NodeProperty<'static>, + lists_len: usize, + curr_index: usize, + curr_phandle: Option>, +} + +impl OfPhandleIterator { + pub(crate) fn new( + node: OfNode<'static>, + list_name: &'static str, + cells_name: Option<&'static str>, + cell_count: usize, + ) -> Option { + let lists = node.property(list_name)?; + let lists_len = lists.value.len(); //already 4 aligned + Some(Self { + np: node, + cells_name, + cell_count, + lists, + lists_len, + curr_index: 0, + curr_phandle: None, + }) + } +} + +impl Iterator for OfPhandleIterator { + type Item = OfPhandleArgs; + + fn next(&mut self) -> Option { + if self.curr_index >= self.lists_len { + return None; + } + + let phandle_val = BigEndianU32::from_bytes(&self.lists.value[self.curr_index..]) + .unwrap() + .get(); + self.curr_phandle = Some(find_phandle(phandle_val).expect("Find Phandle")); + // move cusor to phandle + self.curr_index += 4; + + // parse cell count + if let Some(name) = self.cells_name { + let cell_count = self + .curr_phandle + .unwrap() + .property(name) + .expect("Cell name") + .as_usize() + .expect("Cell name"); + self.cell_count = cell_count; + } + + // parse cell args + let mut args = [0; MAX_PHANDLE_ARGS]; + for i in 0..self.cell_count { + if self.curr_index >= self.lists_len { + panic!("Cell count over lists len") + } + args[i] = BigEndianU32::from_bytes(&self.lists.value[self.curr_index..]) + .unwrap() + .get(); + + // move cusor + self.curr_index += 4; + } + + Some(OfPhandleArgs::new(self.np, self.cell_count, args)) + } +} diff --git a/of/tests/bsta1000b-fada-bus.dtb b/of/tests/bsta1000b-fada-bus.dtb new file mode 100644 index 0000000000000000000000000000000000000000..219535742c7d4276ee6735fb388a181cfcf72e3f GIT binary patch literal 62598 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 + #size-cells = <0x02>; + model = "BST A1000B FAD-A"; + + cpus { + #address-cells = <0x01>; + #size-cells = <0x00>; + + cpu@0 { + compatible = "arm,cortex-a55\0arm,armv8"; + device_type = "cpu"; + enable-method = "psci"; + next-level-cache = <0x03>; + reg = <0x00>; + cpu-idle-states = <0x04>; + phandle = <0x3e>; + }; + + cpu@1 { + compatible = "arm,cortex-a55\0arm,armv8"; + device_type = "cpu"; + enable-method = "psci"; + next-level-cache = <0x03>; + reg = <0x100>; + cpu-idle-states = <0x04>; + phandle = <0x3f>; + }; + + cpu@2 { + compatible = "arm,cortex-a55\0arm,armv8"; + device_type = "cpu"; + enable-method = "psci"; + next-level-cache = <0x03>; + reg = <0x200>; + cpu-idle-states = <0x04>; + phandle = <0x40>; + }; + + cpu@3 { + compatible = "arm,cortex-a55\0arm,armv8"; + device_type = "cpu"; + enable-method = "psci"; + next-level-cache = <0x03>; + reg = <0x300>; + cpu-idle-states = <0x04>; + phandle = <0x41>; + }; + + cpu@4 { + compatible = "arm,cortex-a55\0arm,armv8"; + device_type = "cpu"; + enable-method = "psci"; + next-level-cache = <0x03>; + reg = <0x400>; + cpu-idle-states = <0x04>; + phandle = <0x42>; + }; + + cpu@5 { + compatible = "arm,cortex-a55\0arm,armv8"; + device_type = "cpu"; + enable-method = "psci"; + next-level-cache = <0x03>; + reg = <0x500>; + cpu-idle-states = <0x04>; + phandle = <0x43>; + }; + + cpu@6 { + compatible = "arm,cortex-a55\0arm,armv8"; + device_type = "cpu"; + enable-method = "psci"; + next-level-cache = <0x03>; + reg = <0x600>; + cpu-idle-states = <0x04>; + phandle = <0x44>; + }; + + cpu@7 { + compatible = "arm,cortex-a55\0arm,armv8"; + device_type = "cpu"; + enable-method = "psci"; + next-level-cache = <0x03>; + reg = <0x700>; + cpu-idle-states = <0x04>; + phandle = <0x45>; + }; + + l2-cache0 { + compatible = "cache"; + phandle = <0x03>; + }; + + idle-states { + entry-method = "psci"; + + psci-cpu-sleep { + compatible = "arm,idle-state"; + local-timer-stop; + arm,psci-suspend-param = <0x00>; + entry-latency-us = <0x12c>; + exit-latency-us = <0x78>; + min-residency-us = <0xc8>; + phandle = <0x04>; + }; + }; + }; + + psci { + compatible = "arm,psci"; + method = "smc"; + cpu_on = <0xc4000003>; + cpu_off = <0x84000002>; + cpu_suspend = <0xc4000001>; + }; + + misc_clk { + compatible = "fixed-clock"; + #clock-cells = <0x00>; + clock-frequency = <0x3d0900>; + phandle = <0x02>; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupt-parent = <0x01>; + interrupts = <0x01 0x0d 0xff08 0x01 0x0e 0xff08 0x01 0x0b 0xff08 0x01 0x0a 0xff08>; + }; + + amba_apu@0 { + compatible = "simple-bus"; + #address-cells = <0x02>; + #size-cells = <0x01>; + ranges = <0x00 0x00 0x00 0x00 0xffffffff>; + + a1000bclkc@33002000 { + compatible = "bst,a1000b-clkc"; + osc-clk-frequency = <0x17d7840>; + rgmii0-rxclk-frequency = <0x7735940>; + rgmii1-rxclk-frequency = <0x7735940>; + ptp_clk-frequency = <0x7735940>; + #clock-cells = <0x01>; + reg = <0x00 0x33002000 0x1000 0x00 0x70035000 0x1000 0x00 0x20020000 0x1000 0x00 0x20021000 0x1000>; + phandle = <0x05>; + }; + + a1000rstc@0x3300217c { + compatible = "bst,a1000b-rstc"; + #reset-cells = <0x01>; + reg = <0x00 0x3300217c 0x04 0x00 0x33002180 0x04 0x00 0x70035008 0x04 0x00 0x20020000 0x04 0x00 0x20021000 0x04>; + phandle = <0x06>; + }; + + interrupt-controller@32000000 { + compatible = "arm,gic-400"; + #interrupt-cells = <0x03>; + interrupt-controller; + reg = <0x00 0x32001000 0x1000 0x00 0x32002000 0x2000 0x00 0x32004000 0x2000 0x00 0x32006000 0x2000>; + interrupt-parent = <0x01>; + interrupts = <0x01 0x09 0xff04>; + phandle = <0x01>; + }; + + a1000noc { + compatible = "bst,a1000-noc"; + echo-args = <0x01>; + noc-arg-num = <0x2a>; + noc-args = <0x3342000c 0x00 0x33420008 0x505 0x3342008c 0x00 0x33420088 0x505 0x3342010c 0x00 0x33420108 0x505 0x3342018c 0x00 0x33420188 0x505 0x3342020c 0x00 0x33420208 0x505 0x3342028c 0x00 0x33420288 0x505 0x3342030c 0x00 0x33420308 0x505 0x3342038c 0x00 0x33420388 0x303 0x3342040c 0x00 0x33420408 0x303 0x3342048c 0x00 0x33420488 0x303 0x3342050c 0x00 0x33420508 0x303 0x3342058c 0x00 0x33420588 0x303 0x3342060c 0x00 0x33420608 0x303 0x3342068c 0x00 0x33420688 0x303 0x3342070c 0x00 0x33420708 0x00 0x3342078c 0x00 0x33420788 0x707 0x3342080c 0x00 0x33420808 0x707 0x3342088c 0x00 0x33420888 0x707 0x3342090c 0x00 0x33420908 0x707 0x3340010c 0x00 0x33400108 0x505 0x3340018c 0x00 0x33400188 0x505>; + }; + + serial@20008000 { + status = "okay"; + compatible = "snps,dw-apb-uart"; + reg = <0x00 0x20008000 0x1000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0xd5 0x04>; + clocks = <0x05 0x54 0x05 0x66>; + clock-names = "baudclk\0apb_pclk"; + resets = <0x06 0x1d>; + resets-names = "uart_reset"; + reg-shift = <0x02>; + reg-io-width = <0x04>; + pinctrl-names = "default"; + pinctrl-0 = <0x07>; + }; + + serial@2000a000 { + status = "okay"; + compatible = "snps,dw-apb-uart"; + reg = <0x00 0x2000a000 0x1000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0xd6 0x04>; + clocks = <0x05 0x55 0x05 0x67>; + clock-names = "baudclk\0apb_pclk"; + resets = <0x06 0x17>; + resets-names = "uart_reset"; + reg-shift = <0x02>; + reg-io-width = <0x04>; + pinctrl-names = "default"; + pinctrl-0 = <0x08>; + }; + + serial@2000b000 { + status = "disable"; + compatible = "snps,dw-apb-uart"; + reg = <0x00 0x2000b000 0x1000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0xd7 0x04>; + clocks = <0x05 0x70 0x05 0x7d>; + clock-names = "baudclk\0apb_pclk"; + resets = <0x06 0x29>; + resets-names = "uart_reset"; + reg-shift = <0x02>; + reg-io-width = <0x04>; + pinctrl-names = "default"; + pinctrl-0 = <0x09>; + }; + + serial@20009000 { + status = "okay"; + compatible = "snps,dw-apb-uart"; + reg = <0x00 0x20009000 0x1000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0xd8 0x04>; + clocks = <0x05 0x6f 0x05 0x7e>; + clock-names = "baudclk\0apb_pclk"; + resets = <0x06 0x25>; + resets-names = "uart_reset"; + reg-shift = <0x02>; + reg-io-width = <0x04>; + pinctrl-names = "default"; + pinctrl-0 = <0x0a>; + }; + + dma-controller@33200000 { + status = "okay"; + compatible = "bst,dw-axi-gdma"; + reg = <0x00 0x33200000 0x1000>; + clocks = <0x05 0x2f 0x05 0x30>; + clock-names = "core-clk\0cfgr-clk"; + resets = <0x06 0x0c>; + reset-names = "gdma_reset"; + dma-channels = <0x08>; + snps,dma-masters = <0x01>; + snps,data-width = <0x04>; + snps,block-size = <0x1000 0x1000 0x1000 0x1000 0x1000 0x1000 0x1000 0x1000>; + snps,priority = <0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07>; + snps,axi-max-burst-len = <0x10>; + interrupt-parent = <0x01>; + interrupts = <0x00 0x90 0xff04 0x00 0x91 0xff04 0x00 0x92 0xff04 0x00 0x93 0xff04 0x00 0x94 0xff04 0x00 0x95 0xff04 0x00 0x96 0xff04 0x00 0x97 0xff04 0x00 0x98 0xff04>; + support-slave; + }; + + pcie-phy@30E02000 { + reg = <0x00 0x30e02000 0x1000>; + reg-names = "phy-base"; + dmc-lane = <0x01>; + dmc-mode = <0x02>; + pcie-ctl0 = <0x01>; + pcie-ctl1 = <0x00>; + phandle = <0x0b>; + }; + + pcie@30600000 { + status = "disable"; + compatible = "bst,dw-pcie-rc"; + device_type = "pci"; + controller-id = <0x00>; + bus-range = <0x00 0xff>; + linux,pci-domain = <0x00>; + reg = <0x00 0x30600000 0x10000 0x00 0x30700000 0x10000 0x00 0x30900000 0x40000 0x00 0x30980000 0x40000 0x00 0x40000000 0x40000>; + reg-names = "dbi\0dbi2\0atu\0dma\0config"; + num-iatu = <0x04>; + num-viewport = <0x04>; + #address-cells = <0x03>; + #size-cells = <0x02>; + ranges = <0x81000000 0x00 0x41000000 0x00 0x41000000 0x00 0x1000000 0x83000000 0x00 0x42000000 0x00 0x42000000 0x00 0x4000000>; + dma-ranges = <0x3000000 0x00 0x80000000 0x00 0x80000000 0x02 0x00>; + interrupt-parent = <0x01>; + interrupts = <0x00 0xc5 0x04 0x00 0xbf 0x04 0x00 0xc0 0x04 0x00 0xc1 0x04 0x00 0xc6 0x04>; + interrupt-names = "sys\0dma\0correctable\0uncorrectable\0other"; + #interrupt-cells = <0x01>; + interrupt-map-mask = <0x00 0x00 0x00 0x00>; + interrupt-map = <0x00 0x00 0x00 0x00 0x01 0x00 0xc5 0x04>; + pcie-phy = <0x0b>; + max-link-speed = <0x03>; + num-lanes = <0x02>; + picp-ctl = "mem"; + ob-memaddr-def = <0x0c>; + }; + + pcie0_ep@30600000 { + status = "disable"; + compatible = "bst,dw-pcie-ep"; + device_type = "pci"; + controller-id = <0x00>; + bus-range = <0x00 0x04>; + reg = <0x00 0x30600000 0x40000 0x00 0x30700000 0x40000 0x00 0x30900000 0x40000 0x00 0x30980000 0x40000 0x00 0x40000000 0x100000>; + reg-names = "dbi\0dbi2\0atu\0dma\0addr_space"; + num-ib-windows = <0x06>; + num-ob-windows = <0x06>; + max-functions = [04]; + pcie-phy = <0x0b>; + interrupt-parent = <0x01>; + interrupts = <0x00 0xc5 0x04 0x00 0xbf 0x04 0x00 0xc0 0x04 0x00 0xc1 0x04 0x00 0xc6 0x04>; + interrupt-names = "sys\0dma\0correctable\0uncorrectable\0other"; + #interrupt-cells = <0x01>; + interrupt-map-mask = <0x00 0x00 0x00 0x00>; + interrupt-map = <0x00 0x00 0x00 0x00 0x01 0x00 0xc5 0x04>; + }; + + pcie@30a00000 { + status = "disable"; + compatible = "bst,dw-pcie-ep"; + device_type = "pci"; + controller-id = <0x01>; + bus-range = <0x00 0x04>; + reg = <0x00 0x30a00000 0x40000 0x00 0x30b00000 0x40000 0x00 0x30d00000 0x40000 0x00 0x30d80000 0x40000 0x00 0x48000000 0x1000000>; + reg-names = "dbi\0dbi2\0atu\0dma\0addr_space"; + num-ib-windows = <0x06>; + num-ob-windows = <0x06>; + max-functions = [04]; + pcie-phy = <0x0b>; + interrupt-parent = <0x01>; + interrupts = <0x00 0xc2 0x04 0x00 0xc3 0x04 0x00 0xc4 0x04 0x00 0xc7 0x04>; + interrupt-names = "dma\0correctable\0uncorrectable\0other"; + #interrupt-cells = <0x01>; + interrupt-map-mask = <0x00 0x00 0x00 0x00>; + interrupt-map = <0x00 0x00 0x00 0x00 0x01 0x00 0xc5 0x04>; + }; + + pcie_vnet@0 { + status = "disable"; + compatible = "bst,pcie-vnet"; + vnet-id = <0x00>; + dma-chan-num = <0x01>; + rx_queues = <0x01>; + tx_queues = <0x01>; + tx-fifo-depth = <0x100>; + rx-fifo-depth = <0x100>; + extend-op = <0x10>; + memory-region = <0x0c>; + }; + + pcie_vnet@1 { + status = "disable"; + compatible = "bst,pcie-vnet"; + vnet-id = <0x01>; + dma-chan-num = <0x01>; + rx_queues = <0x01>; + tx_queues = <0x01>; + tx-fifo-depth = <0x100>; + rx-fifo-depth = <0x100>; + }; + + canfd@20016000 { + status = "okay"; + compatible = "BST,bst-canfd"; + reg = <0x00 0x20016000 0x800>; + bst-index = <0x00>; + bst-soc = <0x01>; + interrupt-parent = <0x01>; + interrupts = <0x00 0xcb 0x04>; + clocks = <0x05 0x69 0x05 0x5c>; + clock-names = "pclk\0wclk"; + resets = <0x06 0x21>; + resets-names = "can_reset"; + reset-gpios = <0x0d 0x12 0x00>; + reset-active-high; + pinctrl-names = "default"; + pinctrl-0 = <0x0f 0x10>; + memory-region = <0x11>; + }; + + canfd@20016800 { + status = "okay"; + compatible = "BST,bst-canfd"; + reg = <0x00 0x20016800 0x800>; + bst-index = <0x01>; + bst-soc = <0x01>; + interrupt-parent = <0x01>; + interrupts = <0x00 0xcc 0x04>; + clocks = <0x05 0x6a 0x05 0x5d>; + clock-names = "pclk\0wclk"; + resets = <0x06 0x22>; + resets-names = "can_reset"; + pinctrl-names = "default"; + pinctrl-0 = <0x12>; + }; + + canfd@20017000 { + status = "okay"; + compatible = "BST,bst-canfd"; + reg = <0x00 0x20017000 0x1000>; + bst-index = <0x02>; + bst-soc = <0x01>; + interrupt-parent = <0x01>; + interrupts = <0x00 0xcd 0x04>; + clocks = <0x05 0x7f 0x05 0x76>; + clock-names = "pclk\0wclk"; + resets = <0x06 0x2a>; + resets-names = "can_reset"; + reset-gpios = <0x0d 0x13 0x00>; + reset-active-high; + pinctrl-names = "default"; + pinctrl-0 = <0x13 0x14>; + }; + + gpio@20010000 { + status = "okay"; + #address-cells = <0x01>; + #size-cells = <0x00>; + compatible = "snps,dw-apb-gpio"; + reg = <0x00 0x20010000 0x1000>; + clocks = <0x05 0x62>; + clock-names = "bus"; + resets = <0x06 0x1c>; + resets-names = "gpio0_reset"; + pinctrl-names = "default"; + pinctrl-0 = <0x15 0x16 0x17 0x18 0x19 0x1a 0x1b>; + + gpio-controller@0 { + compatible = "snps,dw-apb-gpio-port"; + gpio-controller; + #gpio-cells = <0x02>; + snps,nr-gpios = <0x20>; + reg = <0x00>; + chipnum-base = <0x00>; + interrupt-controller; + #interrupt-cells = <0x03>; + interrupt-parent = <0x01>; + interrupts = <0x00 0xdf 0x04 0x00 0xe3 0x04 0x00 0xe4 0x04 0x00 0xe5 0x04 0x00 0xe6 0x04 0x00 0xe7 0x04 0x00 0xe8 0x04 0x00 0xe9 0x04 0x00 0xea 0x04>; + phandle = <0x1f>; + }; + + gpio-controller@1 { + compatible = "snps,dw-apb-gpio-port"; + gpio-controller; + #gpio-cells = <0x02>; + snps,nr-gpios = <0x20>; + reg = <0x01>; + chipnum-base = <0x20>; + phandle = <0x4e>; + }; + + gpio-controller@2 { + compatible = "snps,dw-apb-gpio-port"; + gpio-controller; + #gpio-cells = <0x02>; + snps,nr-gpios = <0x20>; + reg = <0x02>; + chipnum-base = <0x40>; + phandle = <0x4b>; + }; + + gpio-controller@3 { + compatible = "snps,dw-apb-gpio-port"; + gpio-controller; + #gpio-cells = <0x02>; + snps,nr-gpios = <0x20>; + reg = <0x03>; + chipnum-base = <0x60>; + phandle = <0x58>; + }; + }; + + gpio@20011000 { + status = "okay"; + #address-cells = <0x01>; + #size-cells = <0x00>; + compatible = "snps,dw-apb-gpio"; + reg = <0x00 0x20011000 0x1000>; + clocks = <0x05 0x82>; + clock-names = "bus"; + resets = <0x06 0x27>; + resets-names = "gpio1_reset"; + + gpio-controller@0 { + compatible = "snps,dw-apb-gpio-port"; + gpio-controller; + #gpio-cells = <0x02>; + snps,nr-gpios = <0x20>; + reg = <0x00>; + chipnum-base = <0x80>; + interrupt-controller; + #interrupt-cells = <0x03>; + interrupt-parent = <0x01>; + interrupts = <0x00 0xe0 0x04>; + }; + + gpio-controller@1 { + compatible = "snps,dw-apb-gpio-port"; + gpio-controller; + #gpio-cells = <0x02>; + snps,nr-gpios = <0x20>; + reg = <0x01>; + chipnum-base = <0xa0>; + }; + + gpio-controller@2 { + compatible = "snps,dw-apb-gpio-port"; + gpio-controller; + #gpio-cells = <0x02>; + snps,nr-gpios = <0x20>; + reg = <0x02>; + chipnum-base = <0xc0>; + }; + + gpio-controller@3 { + compatible = "snps,dw-apb-gpio-port"; + gpio-controller; + #gpio-cells = <0x02>; + snps,nr-gpios = <0x20>; + reg = <0x03>; + chipnum-base = <0xe0>; + phandle = <0x0d>; + }; + }; + + watchdog@2001a000 { + status = "disable"; + compatible = "snps,dw-wdt"; + reg = <0x00 0x2001a000 0x1000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0x5d 0x04>; + clocks = <0x05 0x5a 0x05 0x64>; + clock-names = "wclk\0pclk"; + resets = <0x06 0x1f>; + resets-names = "wdt_reset"; + response-mode = <0x00>; + bst_wdt_name = "lsp_wdt0"; + }; + + watchdog@2001b000 { + status = "okay"; + compatible = "snps,dw-wdt"; + reg = <0x00 0x2001b000 0x1000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0x5e 0x04>; + clocks = <0x05 0x5b 0x05 0x65>; + clock-names = "wclk\0pclk"; + resets = <0x06 0x20>; + resets-names = "wdt_reset"; + response-mode = <0x00>; + bst_wdt_name = "lsp_wdt1"; + }; + + watchdog@2001c000 { + status = "okay"; + compatible = "snps,dw-wdt"; + reg = <0x00 0x2001c000 0x1000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0x5f 0x04>; + clocks = <0x05 0x74 0x05 0x7b>; + clock-names = "wclk\0pclk"; + resets = <0x06 0x2e>; + resets-names = "wdt_reset"; + response-mode = <0x00>; + bst_wdt_name = "lsp_wdt2"; + }; + + watchdog@2001d000 { + status = "okay"; + compatible = "snps,dw-wdt"; + reg = <0x00 0x2001d000 0x1000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0x60 0x04>; + clocks = <0x05 0x75 0x05 0x7c>; + clock-names = "wclk\0pclk"; + resets = <0x06 0x2f>; + resets-names = "wdt_reset"; + response-mode = <0x00>; + bst_wdt_name = "lsp_wdt3"; + }; + + watchdog@32009000 { + status = "okay"; + compatible = "snps,dw-wdt"; + reg = <0x00 0x32009000 0x1000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0x43 0x04>; + clocks = <0x05 0x01>; + clock-names = "wclk"; + response-mode = <0x01>; + bst_wdt_name = "a55_wdt0"; + }; + + watchdog@3200a000 { + status = "okay"; + compatible = "snps,dw-wdt"; + reg = <0x00 0x3200a000 0x1000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0x44 0x04>; + clocks = <0x05 0x01>; + clock-names = "wclk"; + response-mode = <0x01>; + bst_wdt_name = "a55_wdt1"; + }; + + watchdog@3200b000 { + status = "okay"; + compatible = "snps,dw-wdt"; + reg = <0x00 0x3200b000 0x1000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0x45 0x04>; + clocks = <0x05 0x01>; + clock-names = "wclk"; + response-mode = <0x01>; + bst_wdt_name = "a55_wdt2"; + }; + + watchdog@3200c000 { + status = "okay"; + compatible = "snps,dw-wdt"; + reg = <0x00 0x3200c000 0x1000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0x46 0x04>; + clocks = <0x05 0x01>; + clock-names = "wclk"; + response-mode = <0x01>; + bst_wdt_name = "a55_wdt3"; + }; + + watchdog@3200d000 { + status = "okay"; + compatible = "snps,dw-wdt"; + reg = <0x00 0x3200d000 0x1000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0x47 0x04>; + clocks = <0x05 0x01>; + clock-names = "wclk"; + response-mode = <0x01>; + bst_wdt_name = "a55_wdt4"; + }; + + watchdog@3200e000 { + status = "okay"; + compatible = "snps,dw-wdt"; + reg = <0x00 0x3200e000 0x1000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0x48 0x04>; + clocks = <0x05 0x01>; + clock-names = "wclk"; + response-mode = <0x01>; + bst_wdt_name = "a55_wdt5"; + }; + + watchdog@3200f000 { + status = "okay"; + compatible = "snps,dw-wdt"; + reg = <0x00 0x3200f000 0x1000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0x49 0x04>; + clocks = <0x05 0x01>; + clock-names = "wclk"; + response-mode = <0x01>; + bst_wdt_name = "a55_wdt6"; + }; + + watchdog@32010000 { + status = "okay"; + compatible = "snps,dw-wdt"; + reg = <0x00 0x32010000 0x1000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0x4a 0x04>; + clocks = <0x05 0x01>; + clock-names = "wclk"; + response-mode = <0x01>; + bst_wdt_name = "a55_wdt7"; + }; + + i2c@20000000 { + status = "okay"; + #address-cells = <0x01>; + #size-cells = <0x01>; + compatible = "snps,designware-i2c"; + reg = <0x00 0x20000000 0x1000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0xcf 0x04>; + clock-frequency = <0x186a0>; + i2c-sda-hold-time-ns = <0x12c>; + i2c-sda-falling-time-ns = <0x12c>; + i2c-scl-falling-time-ns = <0x12c>; + clocks = <0x05 0x4b 0x05 0x49>; + clock-names = "LSP0_PCLK\0LSP0_WCLK"; + resets = <0x06 0x18>; + reset-names = "i2c0_reset"; + pinctrl-names = "default"; + pinctrl-0 = <0x1c>; + }; + + i2c@20001000 { + status = "okay"; + #address-cells = <0x01>; + #size-cells = <0x00>; + compatible = "snps,designware-i2c"; + reg = <0x00 0x20001000 0x1000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0xd0 0x04>; + clock-frequency = <0xf4240>; + i2c-sda-hold-time-ns = <0x12c>; + i2c-sda-falling-time-ns = <0x12c>; + i2c-scl-falling-time-ns = <0x12c>; + clocks = <0x05 0x4b 0x05 0x49>; + clock-names = "LSP0_PCLK\0LSP0_WCLK"; + resets = <0x06 0x19>; + reset-names = "i2c1_reset"; + pinctrl-names = "default"; + pinctrl-0 = <0x1d>; + }; + + i2c@20002000 { + status = "okay"; + #address-cells = <0x01>; + #size-cells = <0x00>; + compatible = "snps,designware-i2c"; + reg = <0x00 0x20002000 0x1000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0xd1 0x04>; + clock-frequency = <0xf4240>; + i2c-sda-hold-time-ns = <0x12c>; + i2c-sda-falling-time-ns = <0x12c>; + i2c-scl-falling-time-ns = <0x12c>; + clocks = <0x05 0x4b 0x05 0x49>; + clock-names = "LSP0_PCLK\0LSP0_WCLK"; + resets = <0x06 0x1a>; + reset-names = "i2c2_reset"; + pinctrl-names = "default"; + pinctrl-0 = <0x1e>; + + max96712@29 { + compatible = "bst,maxim-deser-hub"; + type = "max96712"; + ctl-mode = "fad-ctl"; + reg = <0x29>; + i2c-port = <0x00>; + csi2-port = <0x00>; + lane-speed = <0x640>; + regs = <0x40>; + data-type = <0x2d>; + trigger-mode = <0x01>; + trigger-fps = <0x14>; + trigger-rx-gpio = <0x01>; + maxim,hsync-invert = <0x00>; + maxim,vsync-invert = <0x00>; + maxim,linkrx-rate = <0x03 0x03 0x03 0x03>; + maxim,link-mode = "GMSL2"; + pdb-gpio = <0x1f 0x14 0x00>; + #address-cells = <0x01>; + #size-cells = <0x00>; + + csi-link { + + ports { + + port@0 { + clock-lanes = <0x00>; + data-lanes = <0x01 0x02 0x03 0x04>; + + endpoint { + remote-endpoint = <0x20>; + phandle = <0x77>; + }; + }; + }; + }; + + ports { + #address-cells = <0x01>; + #size-cells = <0x00>; + + port@0 { + reg = <0x00>; + + endpoint@0 { + remote-endpoint = <0x21>; + phandle = <0x2f>; + }; + }; + + port@1 { + reg = <0x01>; + + endpoint@1 { + remote-endpoint = <0x22>; + phandle = <0x30>; + }; + }; + + port@2 { + reg = <0x02>; + + endpoint@2 { + remote-endpoint = <0x23>; + phandle = <0x31>; + }; + }; + + port@3 { + reg = <0x03>; + + endpoint@3 { + remote-endpoint = <0x24>; + phandle = <0x32>; + }; + }; + }; + }; + + max96712@2a { + compatible = "bst,maxim-deser-hub"; + type = "max96712"; + ctl-mode = "fad-ctl"; + reg = <0x2a>; + i2c-port = <0x00>; + csi2-port = <0x00>; + lane-speed = <0x640>; + regs = <0x40>; + data-type = <0x2d>; + trigger-mode = <0x01>; + trigger-fps = <0x14>; + trigger-rx-gpio = <0x01>; + maxim,hsync-invert = <0x00>; + maxim,vsync-invert = <0x00>; + maxim,linkrx-rate = <0x03 0x03 0x03 0x03>; + maxim,link-mode = "GMSL2"; + pdb-gpio = <0x1f 0x15 0x00>; + #address-cells = <0x01>; + #size-cells = <0x00>; + + csi-link { + + ports { + + port@0 { + clock-lanes = <0x00>; + data-lanes = <0x01 0x02 0x03 0x04>; + + endpoint { + remote-endpoint = <0x25>; + phandle = <0x7c>; + }; + }; + }; + }; + + ports { + #address-cells = <0x01>; + #size-cells = <0x00>; + + port@0 { + reg = <0x00>; + + endpoint@0 { + remote-endpoint = <0x26>; + phandle = <0x33>; + }; + }; + + port@1 { + reg = <0x01>; + + endpoint@1 { + remote-endpoint = <0x27>; + phandle = <0x34>; + }; + }; + + port@2 { + reg = <0x02>; + + endpoint@2 { + remote-endpoint = <0x28>; + phandle = <0x35>; + }; + }; + + port@3 { + reg = <0x03>; + + endpoint@3 { + remote-endpoint = <0x29>; + phandle = <0x36>; + }; + }; + }; + }; + + ti960@34 { + status = "disabled"; + compatible = "bst,ti-deser-hub"; + type = "ti960"; + ctl-mode = "fad-ctl"; + reg = <0x34>; + i2c-port = <0x00>; + csi2-port = <0x00>; + lane-speed = <0x640>; + pdb-gpio = <0x1f 0x16 0x00>; + #address-cells = <0x01>; + #size-cells = <0x00>; + + csi-link { + + ports { + + port@0 { + clock-lanes = <0x00>; + data-lanes = <0x01 0x02>; + + endpoint { + remote-endpoint = <0x2a>; + phandle = <0x81>; + }; + }; + }; + }; + + ports { + #address-cells = <0x01>; + #size-cells = <0x00>; + + port@0 { + reg = <0x00>; + + endpoint@0 { + remote-endpoint = <0x2b>; + phandle = <0x37>; + }; + }; + + port@1 { + reg = <0x01>; + + endpoint@1 { + remote-endpoint = <0x2c>; + phandle = <0x38>; + }; + }; + + port@2 { + reg = <0x02>; + + endpoint@2 { + remote-endpoint = <0x2d>; + phandle = <0x39>; + }; + }; + + port@3 { + reg = <0x03>; + + endpoint@3 { + remote-endpoint = <0x2e>; + phandle = <0x3a>; + }; + }; + }; + }; + + ti960@36 { + status = "disabled"; + compatible = "bst,ti-deser-hub"; + type = "ti960"; + ctl-mode = "fad-ctl"; + reg = <0x36>; + i2c-port = <0x00>; + csi2-port = <0x00>; + lane-speed = <0x640>; + pdb-gpio = <0x1f 0x17 0x00>; + }; + + camera@50 { + status = "okay"; + reg = <0x50>; + ser-alias-id = <0x41>; + sensor-alias-id = <0x50>; + compatible = "bst,sy_ox3c_60"; + sensor-id = <0x36>; + data-type = <0x2d>; + fv-polarity-low = <0x00>; + fpd3-mode = "csi-2"; + serializer = "max96717f"; + algo-bin = "ox3c/OX03C10_raw14_Sunny_h60_AlgoParam_v1.2.0_20230815.bin"; + iq-bin = "ox3c/OX03C10_raw14_Sunny_h60_IqParam_v1.2.0_20230815.bin"; + hdr-stagger-en = <0x81>; + exp-num = <0x01>; + pwl-format = <0x0f>; + dvp-data-type = <0x2d>; + vin-data-type = <0x2d>; + size = <0x780 0x506>; + dvp-dummy = <0xabcd>; + view0-fmt = <0x01>; + view0-size = <0x500 0x2d0>; + view1-fmt = <0x01>; + view1-size = <0x780 0x438>; + pdns-mode = <0x00>; + pdns-input-view = <0x00>; + hblank = <0x00>; + input-crop = <0x00 0x780 0x64 0x49c>; + sensor-fps = <0x14>; + trigger-gpio = <0x08>; + trigger-tx-gpio = <0x08>; + maxim,rx_rate = <0x03>; + serializer-id = <0x40>; + maxim,link-mode = "GMSL2"; + + port { + + endpoint@0 { + remote-endpoint = <0x2f>; + phandle = <0x21>; + }; + }; + }; + + camera@51 { + status = "okay"; + reg = <0x51>; + ser-alias-id = <0x42>; + sensor-alias-id = <0x51>; + compatible = "bst,sy_ox3c_60"; + sensor-id = <0x36>; + data-type = <0x2d>; + fv-polarity-low = <0x00>; + fpd3-mode = "csi-2"; + serializer = "max96717f"; + algo-bin = "ox3c/OX03C10_raw14_Sunny_h60_AlgoParam_v1.2.0_20230815.bin"; + iq-bin = "ox3c/OX03C10_raw14_Sunny_h60_IqParam_v1.2.0_20230815.bin"; + hdr-stagger-en = <0x81>; + exp-num = <0x01>; + pwl-format = <0x0f>; + dvp-data-type = <0x2d>; + vin-data-type = <0x2d>; + size = <0x780 0x506>; + dvp-dummy = <0xabcd>; + view0-fmt = <0x01>; + view0-size = <0x500 0x2d0>; + view1-fmt = <0x01>; + view1-size = <0x780 0x438>; + pdns-mode = <0x00>; + pdns-input-view = <0x00>; + hblank = <0x00>; + input-crop = <0x00 0x780 0x64 0x49c>; + sensor-fps = <0x14>; + trigger-gpio = <0x08>; + trigger-tx-gpio = <0x08>; + maxim,rx_rate = <0x03>; + serializer-id = <0x40>; + maxim,link-mode = "GMSL2"; + + port { + + endpoint@0 { + remote-endpoint = <0x30>; + phandle = <0x22>; + }; + }; + }; + + camera@52 { + status = "okay"; + reg = <0x52>; + ser-alias-id = <0x43>; + sensor-alias-id = <0x52>; + compatible = "bst,sy_ox3c_100"; + sensor-id = <0x36>; + data-type = <0x2d>; + fv-polarity-low = <0x00>; + fpd3-mode = "csi-2"; + serializer = "max96717f"; + algo-bin = "ox3c/OX03C10_raw14_GQZL-Sunny_h100_AlgoParam_V1.2.0_20230824.bin"; + iq-bin = "ox3c/OX03C10_raw14_GQZL-Sunny_h100_IqParam_V1.2.0_20230824.bin"; + hdr-stagger-en = <0x81>; + exp-num = <0x01>; + pwl-format = <0x0f>; + dvp-data-type = <0x2d>; + vin-data-type = <0x2d>; + size = <0x780 0x506>; + dvp-dummy = <0xabcd>; + view0-fmt = <0x01>; + view0-size = <0x500 0x2d0>; + view1-fmt = <0x01>; + view1-size = <0x780 0x438>; + pdns-mode = <0x00>; + pdns-input-view = <0x00>; + hblank = <0x00>; + input-crop = <0x00 0x780 0x64 0x49c>; + sensor-fps = <0x14>; + trigger-gpio = <0x08>; + trigger-tx-gpio = <0x08>; + maxim,rx_rate = <0x03>; + serializer-id = <0x40>; + maxim,link-mode = "GMSL2"; + + port { + + endpoint@0 { + remote-endpoint = <0x31>; + phandle = <0x23>; + }; + }; + }; + + camera@53 { + status = "disabled"; + reg = <0x53>; + ser-alias-id = <0x44>; + sensor-alias-id = <0x53>; + compatible = "bst,sy_ox3c_100"; + sensor-id = <0x36>; + data-type = <0x2d>; + fv-polarity-low = <0x00>; + fpd3-mode = "csi-2"; + serializer = "max96717f"; + algo-bin = "ox3c/OX03C10_raw14_GQZL-Sunny_h100_AlgoParam_V1.2.0_20230824.bin"; + iq-bin = "ox3c/OX03C10_raw14_GQZL-Sunny_h100_IqParam_V1.2.0_20230824.bin"; + hdr-stagger-en = <0x81>; + exp-num = <0x01>; + pwl-format = <0x0f>; + dvp-data-type = <0x2d>; + vin-data-type = <0x2d>; + size = <0x780 0x506>; + dvp-dummy = <0xabcd>; + view0-fmt = <0x01>; + view0-size = <0x500 0x2d0>; + view1-fmt = <0x01>; + view1-size = <0x780 0x438>; + pdns-mode = <0x00>; + pdns-input-view = <0x00>; + hblank = <0x00>; + input-crop = <0x00 0x780 0x64 0x49c>; + sensor-fps = <0x14>; + trigger-gpio = <0x08>; + trigger-tx-gpio = <0x08>; + maxim,rx_rate = <0x03>; + serializer-id = <0x40>; + maxim,link-mode = "GMSL2"; + + port { + + endpoint@0 { + remote-endpoint = <0x32>; + phandle = <0x24>; + }; + }; + }; + + camera@54 { + status = "okay"; + reg = <0x54>; + ser-alias-id = <0x45>; + sensor-alias-id = <0x54>; + compatible = "bst,sy_ox3c_100"; + sensor-id = <0x36>; + data-type = <0x2d>; + fv-polarity-low = <0x00>; + fpd3-mode = "csi-2"; + serializer = "max96717f"; + algo-bin = "ox3c/OX03C10_raw14_GQZL-Sunny_h100_AlgoParam_V1.2.0_20230824.bin"; + iq-bin = "ox3c/OX03C10_raw14_GQZL-Sunny_h100_IqParam_V1.2.0_20230824.bin"; + hdr-stagger-en = <0x81>; + exp-num = <0x01>; + pwl-format = <0x0f>; + dvp-data-type = <0x2d>; + vin-data-type = <0x2d>; + size = <0x780 0x506>; + dvp-dummy = <0xabcd>; + view0-fmt = <0x01>; + view0-size = <0x500 0x2d0>; + view1-fmt = <0x01>; + view1-size = <0x780 0x438>; + pdns-mode = <0x00>; + pdns-input-view = <0x00>; + hblank = <0x00>; + input-crop = <0x00 0x780 0x64 0x49c>; + sensor-fps = <0x14>; + trigger-gpio = <0x08>; + trigger-tx-gpio = <0x08>; + maxim,rx_rate = <0x03>; + serializer-id = <0x40>; + maxim,link-mode = "GMSL2"; + + port { + + endpoint@0 { + remote-endpoint = <0x33>; + phandle = <0x26>; + }; + }; + }; + + camera@55 { + status = "okay"; + reg = <0x55>; + ser-alias-id = <0x46>; + sensor-alias-id = <0x55>; + compatible = "bst,sy_ox3c_100"; + sensor-id = <0x36>; + data-type = <0x2d>; + fv-polarity-low = <0x00>; + fpd3-mode = "csi-2"; + serializer = "max96717f"; + algo-bin = "ox3c/OX03C10_raw14_GQZL-Sunny_h100_AlgoParam_V1.2.0_20230824.bin"; + iq-bin = "ox3c/OX03C10_raw14_GQZL-Sunny_h100_IqParam_V1.2.0_20230824.bin"; + hdr-stagger-en = <0x81>; + exp-num = <0x01>; + pwl-format = <0x0f>; + dvp-data-type = <0x2d>; + vin-data-type = <0x2d>; + size = <0x780 0x506>; + dvp-dummy = <0xabcd>; + view0-fmt = <0x01>; + view0-size = <0x500 0x2d0>; + view1-fmt = <0x01>; + view1-size = <0x780 0x438>; + pdns-mode = <0x00>; + pdns-input-view = <0x00>; + hblank = <0x00>; + input-crop = <0x00 0x780 0x64 0x49c>; + sensor-fps = <0x14>; + trigger-gpio = <0x08>; + trigger-tx-gpio = <0x08>; + maxim,rx_rate = <0x03>; + serializer-id = <0x40>; + maxim,link-mode = "GMSL2"; + + port { + + endpoint@0 { + remote-endpoint = <0x34>; + phandle = <0x27>; + }; + }; + }; + + camera@56 { + status = "okay"; + reg = <0x56>; + ser-alias-id = <0x47>; + sensor-alias-id = <0x56>; + compatible = "bst,sy_ox3c_100"; + sensor-id = <0x36>; + data-type = <0x2d>; + fv-polarity-low = <0x00>; + fpd3-mode = "csi-2"; + serializer = "max96717f"; + algo-bin = "ox3c/OX03C10_raw14_GQZL-Sunny_h100_AlgoParam_V1.2.0_20230824.bin"; + iq-bin = "ox3c/OX03C10_raw14_GQZL-Sunny_h100_IqParam_V1.2.0_20230824.bin"; + hdr-stagger-en = <0x81>; + exp-num = <0x01>; + pwl-format = <0x0f>; + dvp-data-type = <0x2d>; + vin-data-type = <0x2d>; + size = <0x780 0x506>; + dvp-dummy = <0xabcd>; + view0-fmt = <0x01>; + view0-size = <0x500 0x2d0>; + view1-fmt = <0x01>; + view1-size = <0x780 0x438>; + pdns-mode = <0x00>; + pdns-input-view = <0x00>; + hblank = <0x00>; + input-crop = <0x00 0x780 0x64 0x49c>; + sensor-fps = <0x14>; + trigger-gpio = <0x08>; + trigger-tx-gpio = <0x08>; + maxim,rx_rate = <0x03>; + serializer-id = <0x40>; + maxim,link-mode = "GMSL2"; + + port { + + endpoint@0 { + remote-endpoint = <0x35>; + phandle = <0x28>; + }; + }; + }; + + camera@57 { + status = "okay"; + reg = <0x57>; + ser-alias-id = <0x48>; + sensor-alias-id = <0x57>; + compatible = "bst,sy_ox3c_100"; + sensor-id = <0x36>; + data-type = <0x2d>; + fv-polarity-low = <0x00>; + fpd3-mode = "csi-2"; + serializer = "max96717f"; + algo-bin = "ox3c/OX03C10_raw14_GQZL-Sunny_h100_AlgoParam_V1.2.0_20230824.bin"; + iq-bin = "ox3c/OX03C10_raw14_GQZL-Sunny_h100_IqParam_V1.2.0_20230824.bin"; + hdr-stagger-en = <0x81>; + exp-num = <0x01>; + pwl-format = <0x0f>; + dvp-data-type = <0x2d>; + vin-data-type = <0x2d>; + size = <0x780 0x506>; + dvp-dummy = <0xabcd>; + view0-fmt = <0x01>; + view0-size = <0x500 0x2d0>; + view1-fmt = <0x01>; + view1-size = <0x780 0x438>; + pdns-mode = <0x00>; + pdns-input-view = <0x00>; + hblank = <0x00>; + input-crop = <0x00 0x780 0x64 0x49c>; + sensor-fps = <0x14>; + trigger-gpio = <0x08>; + trigger-tx-gpio = <0x08>; + maxim,rx_rate = <0x03>; + serializer-id = <0x40>; + maxim,link-mode = "GMSL2"; + + port { + + endpoint@0 { + remote-endpoint = <0x36>; + phandle = <0x29>; + }; + }; + }; + + camera@58 { + status = "disabled"; + reg = <0x58>; + ser-alias-id = <0x48>; + sensor-alias-id = <0x58>; + compatible = "bst,ov10640-isp"; + data-type = <0x1e>; + fv-polarity-low = <0x01>; + fpd3-mode = "raw10"; + serializer = "ti933"; + trigger-gpio = <0x00>; + size = <0x500 0x2d0>; + dvp-dummy = <0xaaa0>; + view0-fmt = <0x01>; + view0-size = <0x280 0x140>; + view1-fmt = <0x01>; + view1-size = <0x500 0x2d0>; + pdns-mode = <0x00>; + pdns-input-view = <0x00>; + hblank = <0x12c>; + + port { + + endpoint@0 { + remote-endpoint = <0x37>; + phandle = <0x2b>; + }; + }; + }; + + camera@59 { + status = "disabled"; + reg = <0x59>; + ser-alias-id = <0x49>; + sensor-alias-id = <0x59>; + compatible = "bst,ov10640-isp"; + data-type = <0x1e>; + fv-polarity-low = <0x01>; + fpd3-mode = "raw10"; + serializer = "ti933"; + trigger-gpio = <0x00>; + size = <0x500 0x2d0>; + dvp-dummy = <0xaaa0>; + view0-fmt = <0x01>; + view0-size = <0x280 0x140>; + view1-fmt = <0x01>; + view1-size = <0x500 0x2d0>; + pdns-mode = <0x00>; + pdns-input-view = <0x00>; + hblank = <0x12c>; + + port { + + endpoint@0 { + remote-endpoint = <0x38>; + phandle = <0x2c>; + }; + }; + }; + + camera@5a { + status = "disabled"; + reg = <0x5a>; + ser-alias-id = <0x4a>; + sensor-alias-id = <0x5a>; + compatible = "bst,ov10640-isp"; + data-type = <0x1e>; + fv-polarity-low = <0x01>; + fpd3-mode = "raw10"; + serializer = "ti933"; + trigger-gpio = <0x00>; + size = <0x500 0x2d0>; + dvp-dummy = <0xaaa0>; + view0-fmt = <0x01>; + view0-size = <0x280 0x140>; + view1-fmt = <0x01>; + view1-size = <0x500 0x2d0>; + pdns-mode = <0x00>; + pdns-input-view = <0x00>; + hblank = <0x12c>; + + port { + + endpoint@0 { + remote-endpoint = <0x39>; + phandle = <0x2d>; + }; + }; + }; + + camera@5b { + status = "disabled"; + reg = <0x5b>; + ser-alias-id = <0x4b>; + sensor-alias-id = <0x5b>; + compatible = "bst,ov10640-isp"; + data-type = <0x1e>; + fv-polarity-low = <0x01>; + fpd3-mode = "raw10"; + serializer = "ti933"; + trigger-gpio = <0x00>; + size = <0x500 0x2d0>; + dvp-dummy = <0xaaa0>; + view0-fmt = <0x01>; + view0-size = <0x280 0x140>; + view1-fmt = <0x01>; + view1-size = <0x500 0x2d0>; + pdns-mode = <0x00>; + pdns-input-view = <0x00>; + hblank = <0x12c>; + + port { + + endpoint@0 { + remote-endpoint = <0x3a>; + phandle = <0x2e>; + }; + }; + }; + }; + + i2c@20003000 { + status = "okay"; + #address-cells = <0x01>; + #size-cells = <0x01>; + compatible = "snps,designware-i2c"; + reg = <0x00 0x20003000 0x1000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0xd2 0x04>; + clock-frequency = <0x186a0>; + i2c-sda-hold-time-ns = <0x12c>; + i2c-sda-falling-time-ns = <0x12c>; + i2c-scl-falling-time-ns = <0x12c>; + clocks = <0x05 0x4c 0x05 0x4a>; + clock-names = "LSP1_PCLK\0LSP1_WCLK"; + resets = <0x06 0x2c>; + reset-names = "i2c3_reset"; + pinctrl-names = "default"; + pinctrl-0 = <0x3b>; + }; + + i2c@20004000 { + status = "okay"; + #address-cells = <0x01>; + #size-cells = <0x00>; + compatible = "snps,designware-i2c"; + reg = <0x00 0x20004000 0x1000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0xd3 0x04>; + clock-frequency = <0x186a0>; + i2c-sda-hold-time-ns = <0x12c>; + i2c-sda-falling-time-ns = <0x12c>; + i2c-scl-falling-time-ns = <0x12c>; + clocks = <0x05 0x4c 0x05 0x4a>; + clock-names = "LSP1_PCLK\0LSP1_WCLK"; + resets = <0x06 0x2d>; + reset-names = "i2c4_reset"; + pinctrl-names = "default"; + pinctrl-0 = <0x3c>; + + lt9211@2d { + compatible = "bst,lt9211"; + reg = <0x2d>; + }; + }; + + i2c@20005000 { + status = "okay"; + #address-cells = <0x01>; + #size-cells = <0x01>; + compatible = "snps,designware-i2c"; + reg = <0x00 0x20005000 0x1000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0xd4 0x04>; + clock-frequency = <0x186a0>; + i2c-sda-hold-time-ns = <0x12c>; + i2c-sda-falling-time-ns = <0x12c>; + i2c-scl-falling-time-ns = <0x12c>; + clocks = <0x05 0x4c 0x05 0x4a>; + clock-names = "LSP1_PCLK\0LSP1_WCLK"; + resets = <0x06 0x26>; + reset-names = "i2c5_reset"; + pinctrl-names = "default"; + pinctrl-0 = <0x3d>; + }; + + ddr_ecc { + status = "okay"; + compatible = "bst,a1000_ddr_ecc"; + reg = <0x00 0x38000000 0x1400 0x00 0x3c000000 0x1400 0x00 0x33000000 0x140>; + reg-names = "ddr0\0ddr1\0a55_ctrl"; + interrupt-parent = <0x01>; + interrupts = <0x00 0x8b 0x04 0x00 0x8d 0x04>; + interrupt-names = "ddr0_ecc_irq\0ddr1_ecc_irq"; + mbox-names = "bstn-mbox"; + }; + + arm_pmu { + status = "okay"; + compatible = "arm,armv8-pmuv3"; + interrupt-parent = <0x01>; + interrupts = <0x00 0x38 0xff04 0x00 0x39 0xff04 0x00 0x3a 0xff04 0x00 0x3b 0xff04 0x00 0x3c 0xff04 0x00 0x3d 0xff04 0x00 0x3e 0xff04 0x00 0x3f 0xff04>; + interrupt-affinity = <0x3e 0x3f 0x40 0x41 0x42 0x43 0x44 0x45>; + }; + + noc_pmu@0x32702000 { + status = "okay"; + compatible = "bst,bst_noc_pmu"; + reg = <0x00 0x32702000 0x1000 0x00 0x32703000 0x1000 0x00 0x32704000 0x2000 0x00 0x32708000 0x1000 0x00 0x33402000 0x2400 0x00 0x33422000 0x4400 0x00 0x33401100 0x10 0x00 0x33421480 0x10>; + reg-names = "coresight_cpunoc_etf\0coresight_etr\0coresight_funnel\0coresight_corenoc_etf\0cpu_port_set\0core_port_set\0cpunoc_atb\0corenoc_atb"; + memory-region = <0x46>; + }; + + lsp0_pwm0@20012000 { + status = "disable"; + compatible = "snps,bst-pwm"; + clocks = <0x05 0x56 0x05 0x6b>; + clock-names = "wclk\0pclk"; + clock-frequency = <0x17d7840>; + reg = <0x00 0x20012000 0x14 0x00 0x200120b0 0x04>; + reg-names = "base\0top"; + pwm-ch = <0x01>; + pinctrl-names = "default"; + pinctrl-0 = <0x47>; + }; + + lsp0_pwm1@20012014 { + status = "disable"; + compatible = "snps,bst-pwm"; + clocks = <0x05 0x57 0x05 0x6b>; + clock-names = "wclk\0pclk"; + clock-frequency = <0x17d7840>; + reg = <0x00 0x20012014 0x14 0x00 0x200120b4 0x04>; + reg-names = "base\0top"; + pwm-ch = <0x01>; + pinctrl-names = "default"; + pinctrl-0 = <0x48>; + }; + + lsp1_pwm0@20013000 { + status = "disable"; + compatible = "snps,bst-pwm"; + clocks = <0x05 0x71 0x05 0x7a>; + clock-names = "wclk\0pclk"; + clock-frequency = <0x17d7840>; + reg = <0x00 0x20013000 0x14 0x00 0x200130b0 0x04>; + reg-names = "base\0top"; + pwm-ch = <0x01>; + pinctrl-names = "default"; + pinctrl-0 = <0x49>; + }; + + lsp1_pwm1@20013014 { + status = "disable"; + compatible = "snps,bst-pwm"; + clocks = <0x05 0x72 0x05 0x7a>; + clock-names = "wclk\0pclk"; + clock-frequency = <0x17d7840>; + reg = <0x00 0x20013014 0x14 0x00 0x200130b4 0x04>; + reg-names = "base\0top"; + pwm-ch = <0x01>; + pinctrl-names = "default"; + pinctrl-0 = <0x4a>; + }; + + a55_timer0@32008000 { + status = "disable"; + compatible = "snps,dw-apb-timer"; + clock-frequency = <0x14dc9380>; + interrupt-parent = <0x01>; + interrupts = <0x00 0x4b 0x04>; + reg = <0x00 0x32008000 0x14>; + }; + + a55_timer1@32008014 { + status = "disable"; + compatible = "snps,dw-apb-timer"; + clock-frequency = <0x14dc9380>; + interrupt-parent = <0x01>; + interrupts = <0x00 0x4c 0x04>; + reg = <0x00 0x32008014 0x14>; + }; + + a55_timer2@32008028 { + status = "disable"; + compatible = "snps,dw-apb-timer"; + clock-frequency = <0x14dc9380>; + interrupt-parent = <0x01>; + interrupts = <0x00 0x4d 0x04>; + reg = <0x00 0x32008028 0x14>; + }; + + a55_timer3@3200803c { + status = "disable"; + compatible = "snps,dw-apb-timer"; + clock-frequency = <0x14dc9380>; + interrupt-parent = <0x01>; + interrupts = <0x00 0x4e 0x04>; + reg = <0x00 0x3200803c 0x14>; + }; + + a55_timer4@32008050 { + status = "disable"; + compatible = "snps,dw-apb-timer"; + clock-frequency = <0x14dc9380>; + interrupt-parent = <0x01>; + interrupts = <0x00 0x4f 0x04>; + reg = <0x00 0x32008050 0x14>; + }; + + a55_timer5@32008064 { + status = "disable"; + compatible = "snps,dw-apb-timer"; + clock-frequency = <0x14dc9380>; + interrupt-parent = <0x01>; + interrupts = <0x00 0x50 0x04>; + reg = <0x00 0x32008064 0x14>; + }; + + a55_timer6@32008078 { + status = "disable"; + compatible = "snps,dw-apb-timer"; + clock-frequency = <0x14dc9380>; + interrupt-parent = <0x01>; + interrupts = <0x00 0x51 0x04>; + reg = <0x00 0x32008078 0x14>; + }; + + a55_timer7@3200808c { + status = "disable"; + compatible = "snps,dw-apb-timer"; + clock-frequency = <0x14dc9380>; + interrupt-parent = <0x01>; + interrupts = <0x00 0x52 0x04>; + reg = <0x00 0x3200808c 0x14>; + }; + + spi@2000c000 { + status = "disable"; + compatible = "snps,dw-apb-ssi"; + #address-cells = <0x01>; + #size-cells = <0x00>; + reg = <0x00 0x2000c000 0x800>; + interrupt-parent = <0x01>; + interrupts = <0x00 0xdb 0x04>; + clocks = <0x05 0x58 0x05 0x68>; + clock-names = "spi_wclk\0spi_pclk"; + num-cs = <0x01>; + bus-num = <0x00>; + cs-gpios = <0x4b 0x02 0x00>; + pinctrl-names = "default"; + pinctrl-0 = <0x4c>; + }; + + spi@2000d000 { + status = "disable"; + compatible = "snps,dw-apb-ssi"; + #address-cells = <0x01>; + #size-cells = <0x00>; + reg = <0x00 0x2000d000 0x800>; + interrupt-parent = <0x01>; + interrupts = <0x00 0xdc 0x04>; + clocks = <0x05 0x73 0x05 0x80>; + clock-names = "spi_wclk\0spi_pclk"; + num-cs = <0x01>; + bus-num = <0x01>; + cs-gpios = <0x4b 0x06 0x00>; + pinctrl-names = "default"; + pinctrl-0 = <0x4d>; + }; + + spi@2000c800 { + status = "disable"; + compatible = "snps,dw-apb-ssi"; + #address-cells = <0x01>; + #size-cells = <0x00>; + reg = <0x00 0x2000c800 0x800>; + interrupt-parent = <0x01>; + interrupts = <0x00 0xf4 0x04>; + clocks = <0x05 0x58 0x05 0x68>; + clock-names = "spi_wclk\0spi_pclk"; + num-cs = <0x01>; + bus-num = <0x02>; + cs-gpios = <0x4e 0x08 0x00>; + pinctrl-names = "default"; + pinctrl-0 = <0x4f 0x50>; + }; + + spi@2000d800 { + status = "disable"; + compatible = "snps,dw-apb-ssi"; + #address-cells = <0x01>; + #size-cells = <0x00>; + reg = <0x00 0x2000d800 0x800>; + interrupt-parent = <0x01>; + interrupts = <0x00 0xf5 0x04>; + clocks = <0x05 0x73 0x05 0x80>; + clock-names = "spi_wclk\0spi_pclk"; + num-cs = <0x01>; + bus-num = <0x03>; + cs-gpios = <0x4e 0x10 0x00>; + pinctrl-names = "default"; + pinctrl-0 = <0x51 0x52>; + }; + + spi@20022000 { + status = "okay"; + compatible = "snps,dw-apb-ssi"; + #address-cells = <0x01>; + #size-cells = <0x00>; + reg = <0x00 0x20022000 0x800>; + interrupt-parent = <0x01>; + interrupts = <0x00 0xe2 0x04>; + clocks = <0x05 0x58 0x05 0x68>; + clock-names = "spi_wclk\0spi_pclk"; + num-cs = <0x01>; + bus-num = <0x04>; + cs-gpios = <0x4b 0x02 0x00>; + pinctrl-names = "default"; + pinctrl-0 = <0x53>; + spi-slave; + + slave@0 { + compatible = "rohm,dh2228fv"; + reg = <0x00>; + spi-max-frequency = <0x989680>; + }; + }; + + spi@20023000 { + status = "disable"; + compatible = "snps,dw-apb-ssi"; + #address-cells = <0x01>; + #size-cells = <0x00>; + reg = <0x00 0x20023000 0x800>; + interrupt-parent = <0x01>; + interrupts = <0x00 0xe1 0x04>; + clocks = <0x05 0x73 0x05 0x80>; + clock-names = "spi_wclk\0spi_pclk"; + num-cs = <0x01>; + bus-num = <0x05>; + cs-gpios = <0x4b 0x06 0x00>; + pinctrl-names = "default"; + pinctrl-0 = <0x54>; + }; + + i2s-play@2000e000 { + status = "disable"; + compatible = "snps,designware-i2s"; + reg = <0x00 0x2000e000 0x1000>; + interrupt-names = "play_irq"; + interrupt-parent = <0x01>; + interrupts = <0x00 0xdd 0x04>; + clocks = <0x05 0x61 0x05 0x59>; + clock-names = "i2spclk\0i2sclk"; + play; + channel = <0x02>; + #sound-dai-cells = <0x00>; + pinctrl-names = "default"; + pinctrl-0 = <0x55>; + }; + + i2s-rec@2000f000 { + status = "disable"; + compatible = "snps,designware-i2s"; + reg = <0x00 0x2000f000 0x1000>; + interrupt-names = "record_irq"; + interrupt-parent = <0x01>; + interrupts = <0x00 0xde 0x04>; + clocks = <0x02>; + record; + channel = <0x02>; + #sound-dai-cells = <0x00>; + pinctrl-names = "default"; + pinctrl-0 = <0x56>; + }; + + qspi@00000000 { + status = "okay"; + compatible = "snps,dwc-ssi-1.01a"; + #address-cells = <0x01>; + #size-cells = <0x00>; + reg = <0x00 0x00 0x4000000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0xd9 0x04>; + clocks = <0x05 0x4d 0x05 0x4e>; + clock-names = "hclk\0wclk"; + work-mode = <0x01>; + reg-io-width = <0x04>; + bst,use-gpio-cs; + spi-rx-bus-width = <0x04>; + spi-tx-bus-width = <0x04>; + cs-gpios = <0x4b 0x15 0x00>; + num-cs = <0x01>; + bus-num = <0x06>; + pinctrl-names = "default"; + pinctrl-0 = <0x57>; + + qspi0-nor0@0 { + #address-cells = <0x01>; + #size-cells = <0x01>; + spi-rx-bus-width = <0x01>; + spi-tx-bus-width = <0x01>; + compatible = "jedec,spi-nor"; + status = "okay"; + spi-max-frequency = <0xf4240>; + reg = <0x00>; + mode = <0x00>; + powerctl-fad-gpios = <0x1f 0x1c 0x00>; + + partition@0 { + reg = <0x00 0x1e00000>; + label = "nor0_part0"; + }; + + partition@1e00000 { + reg = <0x1e00000 0x200000>; + label = "nor0_part1"; + }; + }; + }; + + qspi@14000000 { + status = "disable"; + compatible = "snps,dwc-ssi-1.01a"; + #address-cells = <0x01>; + #size-cells = <0x00>; + reg = <0x00 0x14000000 0x4000000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0xda 0x04>; + clocks = <0x05 0x4f 0x05 0x50>; + clock-names = "hclk\0wclk"; + work-mode = <0x01>; + reg-io-width = <0x04>; + bst,use-gpio-cs; + spi-rx-bus-width = <0x04>; + spi-tx-bus-width = <0x04>; + cs-gpios = <0x58 0x01 0x00>; + num-cs = <0x01>; + bus-num = <0x07>; + pinctrl-names = "default"; + pinctrl-0 = <0x59>; + }; + + stmmac-axi-config { + snps,wr_osr_lmt = <0x0f>; + snps,rd_osr_lmt = <0x0f>; + snps,axi_fb; + snps,blen = <0x04 0x08 0x10 0x00 0x00 0x00 0x00>; + phandle = <0x5a>; + }; + + rx-queues-config { + snps,rx-queues-to-use = <0x04>; + snps,rx-sched-sp; + phandle = <0x5b>; + + queue0 { + snps,dcb-algorithm; + snps,map-to-dma-channel = <0x00>; + snps,priority = <0x00>; + }; + + queue1 { + snps,dcb-algorithm; + snps,map-to-dma-channel = <0x01>; + snps,priority = <0x01>; + }; + + queue2 { + snps,dcb-algorithm; + snps,map-to-dma-channel = <0x02>; + snps,priority = <0x02>; + }; + + queue3 { + snps,dcb-algorithm; + snps,map-to-dma-channel = <0x03>; + snps,priority = <0x03>; + }; + }; + + tx-queues-config { + snps,tx-queues-to-use = <0x04>; + snps,tx-sched-wrr; + phandle = <0x5c>; + + queue0 { + snps,weight = <0x10>; + snps,dcb-algorithm; + snps,priority = <0x00>; + }; + + queue1 { + snps,weight = <0x10>; + snps,dcb-algorithm; + snps,priority = <0x01>; + }; + + queue2 { + snps,weight = <0x10>; + snps,dcb-algorithm; + snps,priority = <0x02>; + }; + + queue3 { + snps,weight = <0x10>; + snps,dcb-algorithm; + snps,priority = <0x03>; + }; + }; + + thermal@70039000 { + status = "okay"; + compatible = "bst,bst-thermal"; + reg = <0x00 0x70039000 0x1000>; + #thermal-sensor-cells = <0x00>; + phandle = <0x65>; + }; + + ethernet@30000000 { + status = "okay"; + compatible = "bst,dw-eqos-eth"; + reg = <0x00 0x30000000 0x100000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0x9f 0xff04 0x00 0xa0 0xff04 0x00 0xa1 0xff04 0x00 0xa2 0xff04 0x00 0xa3 0xff04 0x00 0xa4 0xff04 0x00 0xa5 0xff04 0x00 0xa6 0xff04 0x00 0xa7 0xff04 0x00 0xa8 0xff04 0x00 0xa9 0xff04 0x00 0xaa 0xff04>; + interrupt-names = "sbd_irq\0sfty_ce_irq\0sfty_ue_irq\0tx_chan0_irq\0tx_chan1_irq\0tx_chan2_irq\0tx_chan3_irq\0rx_chan0_irq\0rx_chan1_irq\0rx_chan2_irq\0rx_chan3_irq\0eth_lpi"; + ethernet-id = <0x00>; + mac-address = [00 00 00 00 00 00]; + max-frame-size = <0xed8>; + phy-mode = "rgmii"; + snps,multicast-filter-bins = <0x100>; + snps,perfect-filter-entries = <0x80>; + rx-fifo-depth = <0x4000>; + tx-fifo-depth = <0x4000>; + clocks = <0x05 0x0f 0x05 0x13 0x05 0x15 0x05 0x11>; + clock-names = "wclk\0axim_aclk\0pclk\0ptp_ref"; + bst,fix-safety = <0x00>; + bst,dma_int_mode = <0x01>; + snps,fixed-burst; + snps,force_sf_dma_mode; + snps,ps-speed = <0x3e8>; + snps,axi-config = <0x5a>; + snps,mtl-rx-config = <0x5b>; + snps,mtl-tx-config = <0x5c>; + label = "gmac0"; + resets = <0x06 0x11>; + reset-names = "bstgmaceth"; + eth-name = "gmac0"; + eth-number = <0x00>; + mac-mode = "rgmii"; + extend-op = <0x02>; + pinctrl-names = "default"; + pinctrl-0 = <0x5d>; + + fixed-link { + speed = <0x3e8>; + full-duplex; + }; + }; + + ethernet@30100000 { + status = "okay"; + compatible = "bst,dw-eqos-eth"; + reg = <0x00 0x30100000 0x100000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0xab 0xff04 0x00 0xac 0xff04 0x00 0xad 0xff04 0x00 0xae 0xff04 0x00 0xaf 0xff04 0x00 0xb0 0xff04 0x00 0xb1 0xff04 0x00 0xb2 0xff04 0x00 0xb3 0xff04 0x00 0xb4 0xff04 0x00 0xb5 0xff04 0x00 0xb6 0xff04>; + interrupt-names = "sbd_irq\0sfty_ce_irq\0sfty_ue_irq\0tx_chan0_irq\0tx_chan1_irq\0tx_chan2_irq\0tx_chan3_irq\0rx_chan0_irq\0rx_chan1_irq\0rx_chan2_irq\0rx_chan3_irq\0eth_lpi"; + ethernet-id = <0x01>; + mac-address = [00 00 00 00 00 00]; + max-frame-size = <0xed8>; + phy-mode = "rgmii"; + snps,multicast-filter-bins = <0x100>; + snps,perfect-filter-entries = <0x80>; + rx-fifo-depth = <0x4000>; + tx-fifo-depth = <0x4000>; + clocks = <0x05 0x10 0x05 0x14 0x05 0x16 0x05 0x12>; + clock-names = "wclk\0axim_aclk\0pclk\0ptp_ref"; + bst,fix-safety = <0x00>; + bst,dma_int_mode = <0x01>; + snps,fixed-burst; + snps,force_sf_dma_mode; + snps,ps-speed = <0x3e8>; + snps,axi-config = <0x5a>; + snps,mtl-rx-config = <0x5b>; + snps,mtl-tx-config = <0x5c>; + label = "gmac1"; + resets = <0x06 0x12>; + reset-names = "bstgmaceth"; + pinctrl-names = "default"; + pinctrl-0 = <0x5e>; + extend-op = <0x02>; + eth-name = "gmac1"; + eth-number = <0x01>; + mac-mode = "rgmii"; + phy-handle = <0x5f>; + + mdio1 { + compatible = "snps,dwmac-mdio"; + #address-cells = <0x01>; + #size-cells = <0x00>; + + eth_phy1@1 { + compatible = "marvell,88Q2112\0ethernet-phy-id002B.0983\0ethernet-phy-ieee802.3-c45"; + device_type = "ethernet-phy"; + max-speed = <0x3e8>; + reg = <0x07>; + reset-gpios = <0x1f 0x09 0x01>; + reset-active-low; + reset-assert-us = <0x4e20>; + reset-deassert-us = <0x4e20>; + phandle = <0x5f>; + }; + }; + }; + + phy@30E01000 { + compatible = "bst,dwc-usb-phy"; + #phy-cells = <0x00>; + reg = <0x00 0x30e01000 0x1000>; + usb_mode = "usb20"; + phandle = <0x62>; + }; + + phy@30E00000 { + compatible = "bst,dwc-usb-phy"; + reg = <0x00 0x30e00000 0x1000>; + #phy-cells = <0x00>; + usb_mode = "usb30"; + pll_type = "internal"; + phandle = <0x60>; + }; + + usb3 { + compatible = "bst,dwc3usb"; + status = "okay"; + ranges; + #address-cells = <0x02>; + #size-cells = <0x01>; + clock-names = "suspend\0ref\0axi\0apb"; + clocks = <0x05 0x17 0x05 0x18 0x05 0x19 0x05 0x1a>; + resets = <0x06 0x04>; + reset-names = "usb3_reset"; + phys = <0x60>; + phy-names = "usb-phy"; + pll_type = "internal"; + powerctl-gpios = <0x1f 0x0e 0x00>; + pinctrl-names = "default"; + pinctrl-0 = <0x61>; + + dwc3@30200000 { + compatible = "snps,dwc3"; + reg-shift = <0x02>; + reg-io-width = <0x04>; + reg = <0x00 0x30200000 0x100000>; + interrupts = <0x00 0xc8 0x04>; + interrupt-parent = <0x01>; + dr_mode = "host"; + snps,dis_u3_susphy_quirk; + }; + }; + + usb2 { + compatible = "bst,dwc3usb"; + status = "okay"; + ranges; + #address-cells = <0x02>; + #size-cells = <0x01>; + clock-names = "ahb\0ref\0apb"; + clocks = <0x05 0x1b 0x05 0x1d 0x05 0x1c>; + reset-names = "usb2_reset"; + resets = <0x06 0x05>; + phys = <0x62>; + phy-names = "usb-phy"; + pll_type = "internal"; + + dwc3@30300000 { + status = "okay"; + compatible = "snps,dwc3"; + reg = <0x00 0x30300000 0x100000>; + interrupts = <0x00 0xc9 0x04>; + interrupt-parent = <0x01>; + dr_mode = "peripheral"; + snps,incr-burst-type-adjustment = <0x01 0x04 0x08 0x10>; + snps,reqinfo-for-data-read = <0x08>; + snps,reqinfo-for-descriptor-read = <0x08>; + }; + }; + + dwmmc0@30400000 { + status = "okay"; + compatible = "bst,dwcmshc-sdhci"; + clocks = <0x05 0x1f 0x05 0x1e>; + clock-names = "core\0bus"; + reg = <0x00 0x30400000 0x1000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0xb9 0x04>; + interrupt-names = "IRQDWMMC0"; + #address-cells = <0x01>; + #size-cells = <0x00>; + data-addr = <0x200>; + fifo-watermark-aligned; + clock-frequency = <0x2faf080>; + max-frequency = <0xbebc200>; + min-frequency = <0x61a80>; + broken-64bit-dma; + clear-tarns-mode; + fifo-depth = <0x400>; + card-detect-delay = <0xc8>; + bus-width = <0x08>; + cap-mmc-highspeed; + non-removable; + no-sdio; + no-sd; + keep-power-in-suspend; + no-3-3-v; + sdhci,auto-cmd12; + pinctrl-names = "default"; + pinctrl-0 = <0x63>; + }; + + dwmmc1@30500000 { + status = "okay"; + compatible = "bst,dwcmshc-sdhci"; + clocks = <0x05 0x21 0x05 0x20>; + clock-names = "core\0bus"; + reg = <0x00 0x30500000 0x1000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0xbd 0x04>; + interrupt-names = "IRQDWMMC1"; + #address-cells = <0x01>; + #size-cells = <0x00>; + data-addr = <0x200>; + fifo-watermark-aligned; + clock-frequency = <0x5f5e100>; + max-frequency = <0xbebc200>; + min-frequency = <0x61a80>; + broken-64bit-dma; + clear-tarns-mode; + fifo-depth = <0x400>; + card-detect-delay = <0xc8>; + bus-width = <0x04>; + cap-sd-highspeed; + sd-uhs-sdr12; + sd-uhs-sdr25; + sd-uhs-sdr50; + no-sdio; + no-mmc; + sdhci,auto-cmd12; + keep-power-in-suspend; + pinctrl-names = "default"; + pinctrl-0 = <0x64>; + }; + + gpu@33300000 { + status = "okay"; + compatible = "arm,mali-450\0arm,mali-utgard"; + reg = <0x00 0x33300000 0x30000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0x81 0x04 0x00 0x82 0x04 0x00 0x83 0x04 0x00 0x84 0x04 0x00 0x85 0x04 0x00 0x86 0x04 0x00 0x87 0x04 0x00 0x88 0x04>; + interrupt-names = "IRQPP0\0IRQPPMMU0\0IRQPP1\0IRQPPMMU1\0IRQGP\0IRQGPMMU\0IRQPMU\0IRQPP"; + clocks = <0x05 0x31 0x05 0x32>; + clock-names = "clk_mali\0clk_mali_apb"; + resets = <0x06 0x0b>; + reset-names = "gpu_reset"; + ppcores = <0x02>; + dedicated_mem_start = <0x00>; + dedicated_mem_size = <0x00>; + }; + + mali-v500@0x55000000 { + status = "okay"; + compatible = "arm,mali-v500"; + reg = <0x00 0x55000000 0xffff>; + interrupt-parent = <0x01>; + interrupts = <0x00 0x9a 0x04>; + interrupt-names = "IRQV500"; + resets = <0x06 0x00>; + reset-names = "codec_reset"; + clocks = <0x05 0x3d>; + clock-names = "clk_v500"; + }; + }; + + cooling_dev { + + pwm { + cpumask = <0x0f>; + capacitance = <0x5dc>; + #cooling-cells = <0x02>; + phandle = <0x67>; + }; + }; + + thermal-zones { + + cpu-thermal { + polling-delay-passive = <0x1f4>; + polling-delay = <0x3e8>; + thermal-sensors = <0x65>; + + trips { + + switch_trip { + temperature = <0x15f90>; + hysteresis = <0x7d0>; + type = "passive"; + phandle = <0x66>; + }; + + critical_trip { + temperature = <0x1e848>; + hysteresis = <0x00>; + type = "critical"; + }; + }; + + cooling-maps { + + map0 { + trip = <0x66>; + cooling-device = <0x67 0x00 0x01>; + }; + }; + }; + }; + + pinctrl@70038000 { + status = "okay"; + compatible = "bst,pinctrl-a1000b"; + #address-cells = <0x02>; + #size-cells = <0x02>; + reg = <0x00 0x70038000 0x00 0x1000 0x00 0x33001000 0x00 0x1000>; + reg-names = "aon\0top"; + #gpio-cells = <0x02>; + + spi0_pinctrl { + phandle = <0x4c>; + + mux { + pins = "spi0_miso\0spi0_mosi\0spi0_sclk"; + function = "spi0"; + }; + }; + + spi1_pinctrl { + phandle = <0x4d>; + + mux { + pins = "spi1_miso\0spi1_mosi\0spi1_sclk"; + function = "spi1"; + }; + }; + + spi2_pinctrl { + phandle = <0x4f>; + + mux { + pins = "uart0_cts\0uart1_cts\0uart1_rts"; + function = "spi2"; + }; + }; + + spi2_cs_pinctrl { + phandle = <0x50>; + + mux { + pins = "uart0_rts"; + function = "gpio"; + }; + }; + + spi3_pinctrl { + phandle = <0x51>; + + mux { + pins = "uart2_cts\0uart3_cts\0uart3_rts"; + function = "spi3"; + }; + }; + + spi3_cs_pinctrl { + phandle = <0x52>; + + mux { + pins = "uart2_rts"; + function = "gpio"; + }; + }; + + spi4_pinctrl { + phandle = <0x53>; + + mux { + pins = "spi0_miso\0spi0_mosi\0spi0_sclk\0spi0_cs"; + function = "spi0_s"; + }; + }; + + spi5_pinctrl { + phandle = <0x54>; + + mux { + pins = "spi1_miso\0spi1_mosi\0spi1_sclk"; + function = "spi1_s"; + }; + }; + + i2c0_pinctrl { + phandle = <0x1c>; + + mux { + pins = "i2c0_scl\0i2c0_sda"; + function = "i2c0"; + }; + }; + + i2c1_pinctrl { + phandle = <0x1d>; + + mux { + pins = "i2c1_scl\0i2c1_sda"; + function = "i2c1"; + }; + }; + + i2c2_pinctrl { + phandle = <0x1e>; + + mux { + pins = "i2c2_scl\0i2c2_sda"; + function = "i2c2"; + }; + }; + + i2c3_pinctrl { + phandle = <0x3b>; + + mux { + pins = "i2c3_scl\0i2c3_sda"; + function = "i2c3"; + }; + }; + + i2c4_pinctrl { + phandle = <0x3c>; + + mux { + pins = "i2c4_scl\0i2c4_sda"; + function = "i2c4"; + }; + }; + + i2c5_pinctrl { + phandle = <0x3d>; + + mux { + pins = "i2c5_scl\0i2c5_sda"; + function = "i2c5"; + }; + }; + + uart0_pinctrl { + phandle = <0x07>; + + mux { + pins = "uart0_txd\0uart0_rxd"; + function = "uart0"; + }; + }; + + uart1_pinctrl { + phandle = <0x08>; + + mux { + pins = "uart1_txd\0uart1_rxd"; + function = "uart1"; + }; + }; + + uart2_pinctrl { + phandle = <0x09>; + + mux { + pins = "uart2_txd\0uart2_rxd"; + function = "uart2"; + }; + }; + + uart3_pinctrl { + phandle = <0x0a>; + + mux { + pins = "uart3_txd\0uart3_rxd"; + function = "uart3"; + }; + }; + + gpio0_pinctrl { + + mux { + pins = "gpio_29"; + function = "gpio"; + }; + }; + + gpio_special_func_pinctrl { + + mux { + pins = "gpio_24\0gpio_29\0debug4\0debug5\0uart0_cts\0uart0_rts"; + function = "gpio"; + }; + }; + + qspi0_pinctrl { + phandle = <0x57>; + + mux { + pins = "qspi0_cs0"; + function = "gpio"; + }; + }; + + usb3_pinctrl { + phandle = <0x61>; + + mux { + pins = "gpio_14"; + function = "gpio"; + }; + }; + + qspi1_pinctrl { + phandle = <0x59>; + + mux { + pins = "qspi1_cs1"; + function = "gpio"; + }; + }; + + des_960_1_pinctrl { + + mux { + pins = "qspi1_io1"; + function = "gpio"; + }; + }; + + des_960_2_pinctrl { + + mux { + pins = "qspi1_io3"; + function = "gpio"; + }; + }; + + des_960_3_pinctrl { + + mux { + pins = "qspi1_io5"; + function = "gpio"; + }; + }; + + bist_pinctrl { + + mux { + pins = "spi1_mosi"; + function = "bist"; + }; + }; + + can0_pinctrl { + phandle = <0x0f>; + + mux { + pins = "can_rx0\0can_tx0"; + function = "can0"; + }; + }; + + can1_pinctrl { + phandle = <0x12>; + + mux { + pins = "can_rx1\0can_tx1"; + function = "can1"; + }; + }; + + can2_pinctrl { + phandle = <0x13>; + + mux { + pins = "jtag_tck\0jtag_trst"; + function = "can2"; + }; + }; + + i2s0_pinctrl { + phandle = <0x55>; + + mux { + pins = "i2s0_ck\0i2s0_mck\0i2s0_sd_out\0i2s0_ws\0qspi0_io4"; + function = "i2s0"; + }; + }; + + i2s1_pinctrl { + phandle = <0x56>; + + mux { + pins = "i2s1_ck\0i2s1_sd_in\0i2s1_ws"; + function = "i2s1"; + }; + }; + + isp_pinctrl { + phandle = <0x6a>; + + mux { + pins = "isp_fsync0\0isp_fsync1\0isp_fsync2\0isp_fsync3"; + function = "isp"; + }; + }; + + ptp_pinctrl { + + mux { + pins = "ptp_pps0\0ptp_pps1\0ptp_clk"; + function = "ptp"; + }; + }; + + ptp_pinconfig { + + config { + pins = "ptp_clk"; + drive-strength = <0x02>; + input-enable; + }; + }; + + err_rpt_l0_pinctrl { + + mux { + pins = "err_rpt_l0_n\0err_rpt_l0_p"; + function = "err_rpt_l0"; + }; + }; + + err_rpt_gpio_l0_pinctrl { + + mux { + pins = "err_rpt_l0_n\0err_rpt_l0_p"; + function = "gpio"; + }; + }; + + err_rpt_l1_pinctrl { + + mux { + pins = "err_rpt_l1_n\0err_rpt_l1_p"; + function = "err_rpt_l1"; + }; + }; + + pwm_lsp0_pwm0_pinctrl { + phandle = <0x47>; + + mux { + pins = "pwm0"; + function = "pwm"; + }; + }; + + pwm_lsp0_pwm1_pinctrl { + phandle = <0x48>; + + mux { + pins = "pwm1"; + function = "pwm"; + }; + }; + + pwm_lsp1_pwm0_pinctrl { + phandle = <0x49>; + + mux { + pins = "pwm2"; + function = "pwm"; + }; + }; + + pwm_lsp1_pwm1_pinctrl { + phandle = <0x4a>; + + mux { + pins = "pwm3"; + function = "pwm"; + }; + }; + + ts_pinctrl { + + mux { + pins = "ts_trig_in00\0ts_trig_in01\0ts_trig_in10\0ts_trig_in11"; + function = "ts"; + }; + }; + + sdemmc0_pinctrl { + phandle = <0x15>; + + mux { + pins = "sdemmc0_clk\0sdemmc0_cmd\0sdemmc0_dat0\0sdemmc0_dat1\0sdemmc0_dat2\0sdemmc0_dat3\0sdemmc0_dat4\0sdemmc0_dat5\0sdemmc0_dat6\0sdemmc0_dat7\0sdemmc0_rstb\0sdemmc0_cdn\0sdemmc0_wp"; + function = "sdemmc0"; + }; + }; + + sdemmc0_pinconfig { + phandle = <0x63>; + + config { + pins = "sdemmc0_clk\0sdemmc0_cmd\0sdemmc0_dat0\0sdemmc0_dat1\0sdemmc0_dat2\0sdemmc0_dat3\0sdemmc0_dat4\0sdemmc0_dat5\0sdemmc0_dat6\0sdemmc0_dat7\0sdemmc0_rstb\0sdemmc0_cdn\0sdemmc0_wp"; + drive-strength = <0x02>; + input-enable; + }; + }; + + sdemmc1_pinctrl { + phandle = <0x16>; + + mux { + pins = "sdemmc1_clk\0sdemmc1_cmd\0sdemmc1_dat0\0sdemmc1_dat1\0sdemmc1_dat2\0sdemmc1_dat3\0sdemmc1_dat4\0sdemmc1_dat5\0sdemmc1_dat6\0sdemmc1_dat7\0sdemmc1_rstb\0sdemmc1_cdn\0sdemmc1_wp"; + function = "sdemmc1"; + }; + }; + + sdemmc1_pinconfig { + phandle = <0x64>; + + config { + pins = "sdemmc1_clk\0sdemmc1_cmd\0sdemmc1_dat0\0sdemmc1_dat1\0sdemmc1_dat2\0sdemmc1_dat3\0sdemmc1_dat4\0sdemmc1_dat5\0sdemmc1_dat6\0sdemmc1_dat7\0sdemmc1_rstb\0sdemmc1_cdn\0sdemmc1_wp"; + drive-strength = <0x0f>; + input-enable; + }; + }; + + debug_pinctrl { + phandle = <0x17>; + + mux { + pins = "debug0\0debug1\0debug2\0debug3\0debug4\0debug5\0debug6\0debug7"; + function = "debug"; + }; + }; + + strap_pinctrl { + + mux { + pins = "gpio_24\0gpio_25\0gpio_26\0gpio_27\0gpio_28\0gpio_29\0spi1_sclk\0i2s0_mck\0i2s0_ck\0gpio_107\0gpio_108"; + function = "strap"; + }; + }; + + vout_pinctrl { + phandle = <0x18>; + + mux { + pins = "vout_r0\0vout_r1\0vout_r2\0vout_r3\0vout_r4\0vout_r5\0vout_r6\0vout_r7\0vout_g0\0vout_g1\0vout_g2\0vout_g3\0vout_g4\0vout_g5\0vout_g6\0vout_g7\0vout_b0\0vout_b1\0vout_b2\0vout_b3\0vout_b4\0vout_b5\0vout_b6\0vout_b7\0vout_hs\0vout_vs\0vout_de\0vout_pclk\0vout_pdb"; + function = "vout"; + }; + }; + + vout_pinconfig { + + config { + pins = "vout_r0\0vout_r1\0vout_r2\0vout_r3\0vout_r4\0vout_r5\0vout_r6\0vout_r7\0vout_g0\0vout_g1\0vout_g2\0vout_g3\0vout_g4\0vout_g5\0vout_g6\0vout_g7\0vout_b0\0vout_b1\0vout_b2\0vout_b3\0vout_b4\0vout_b5\0vout_b6\0vout_b7\0vout_hs\0vout_vs\0vout_de\0vout_pclk\0vout_pdb"; + drive-strength = <0x02>; + input-enable; + }; + }; + + vin_pinctrl { + phandle = <0x19>; + + mux { + pins = "vin_b0\0vin_b1\0vin_b2\0vin_b3\0vin_b4\0vin_de\0vin_g0\0vin_g1\0vin_g2\0vin_g3\0vin_g4\0vin_g5\0vin_hs\0vin_llc\0vin_r0\0vin_r1\0vin_r2\0vin_r3\0vin_r4\0vin_vs"; + function = "vin"; + }; + }; + + vin_pinconfig { + + config { + pins = "vin_b0\0vin_b1\0vin_b2\0vin_b3\0vin_b4\0vin_de\0vin_g0\0vin_g1\0vin_g2\0vin_g3\0vin_g4\0vin_g5\0vin_hs\0vin_llc\0vin_r0\0vin_r1\0vin_r2\0vin_r3\0vin_r4\0vin_vs"; + drive-strength = <0x02>; + input-enable; + }; + }; + + rgmii0_pinctrl { + phandle = <0x5d>; + + mux { + pins = "rgmii0_txd0\0rgmii0_txd1\0rgmii0_txd2\0rgmii0_txd3\0gmii0_txd4\0gmii0_txd5\0gmii0_txd6\0gmii0_txd7\0gmii0_txer\0rgmii0_txctrl\0mii0_txclk\0rgmii0_gtxclk\0rgmii0_rxd0\0rgmii0_rxd1\0rgmii0_rxd2\0rgmii0_rxd3\0gmii0_rxd4\0gmii0_rxd5\0gmii0_rxd6\0gmii0_rxd7\0gmii0_rxer\0rgmii0_rxctrl\0rgmii0_rxclk\0rgmii0_mdio\0rgmii0_mdc\0rgmii0_intr"; + function = "rgmii0"; + }; + }; + + rgmii0_pinconfig { + + config { + pins = "rgmii0_gtxclk\0rgmii0_mdc\0rgmii0_mdio\0rgmii0_rxclk\0rgmii0_rxctrl\0rgmii0_rxd0\0rgmii0_rxd1\0rgmii0_rxd2\0rgmii0_rxd3\0rgmii0_txctrl\0rgmii0_txd0\0rgmii0_txd1\0rgmii0_txd2\0rgmii0_txd3"; + drive-strength = <0x02>; + input-enable; + }; + }; + + rgmii1_pinctrl { + phandle = <0x5e>; + + mux { + pins = "rgmii1_txd0\0rgmii1_txd1\0rgmii1_txd2\0rgmii1_txd3\0mii1_rxer\0mii1_txclk\0rgmii1_txctrl\0rgmii1_gtxclk\0rgmii1_rxd0\0rgmii1_rxd1\0rgmii1_rxd2\0rgmii1_rxd3\0rgmii1_rxctrl\0rgmii1_rxclk\0rgmii1_mdc\0rgmii1_mdio\0rgmii1_intr"; + function = "rgmii1"; + }; + }; + + rgmii1_pinconfig { + + config { + pins = "rgmii1_gtxclk\0rgmii1_mdc\0rgmii1_mdio\0rgmii1_rxclk\0rgmii1_rxctrl\0rgmii1_rxd0\0rgmii1_rxd1\0rgmii1_rxd2\0rgmii1_rxd3\0rgmii1_txctrl\0rgmii1_txd0\0rgmii1_txd1\0rgmii1_txd2\0rgmii1_txd3"; + drive-strength = <0x02>; + input-enable; + }; + }; + + gmii0_pinconfig { + + config { + pins = "gmii0_rxd4\0gmii0_rxd5\0gmii0_rxd6\0gmii0_rxd7\0gmii0_rxer\0gmii0_txd4\0gmii0_txd5\0gmii0_txd6\0gmii0_txd7\0gmii0_txer"; + drive-strength = <0x02>; + input-enable; + }; + }; + + ssd_pinctrl { + + mux { + pins = "sdemmc0_led_ctl\0sdemmc1_led_ctl\0gpio_26\0gpio_27"; + function = "gpio"; + }; + }; + + gnss_pinctrl { + + mux { + pins = "gpio_31\0gpio_12\0pwm1"; + function = "gpio"; + }; + }; + + fan_pinctrl { + phandle = <0x1a>; + + mux { + pins = "gpio_27\0gpio_28"; + function = "gpio"; + }; + }; + + can0_transceiver_pinctrl { + phandle = <0x10>; + + mux { + pins = "pcie0_rstb"; + function = "gpio"; + }; + }; + + can2_transceiver_pinctrl { + phandle = <0x14>; + + mux { + pins = "pcie1_rstb"; + function = "gpio"; + }; + }; + + board_special_func_pinctrl { + phandle = <0x1b>; + + mux { + pins = "vout_pdb\0gpio_31"; + function = "gpio"; + }; + }; + }; + + isp { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "bst,a1000b-isp"; + memory-region = <0x68 0x69>; + assigned-mem-size = <0x1000>; + mbox-names = "bstn-mbox"; + mboxes = <0xea 0x06>; + isp-fw-fbuf-addr = <0xa2000000>; + isp-fw-fbuf-size = <0x10000000>; + dma-coherent; + pinctrl-names = "default"; + pinctrl-0 = <0x6a>; + + core@0 { + id = <0x00>; + + ports { + #address-cells = <0x01>; + #size-cells = <0x00>; + + port@0 { + reg = <0x00>; + + endpoint@0 { + remote-endpoint = <0x6b>; + phandle = <0x78>; + }; + }; + + port@1 { + reg = <0x01>; + + endpoint@1 { + remote-endpoint = <0x6c>; + phandle = <0x79>; + }; + }; + + port@2 { + reg = <0x02>; + + endpoint@2 { + remote-endpoint = <0x6d>; + phandle = <0x7a>; + }; + }; + + port@3 { + reg = <0x03>; + + endpoint@3 { + remote-endpoint = <0x6e>; + phandle = <0x7b>; + }; + }; + }; + }; + + core@1 { + id = <0x01>; + + ports { + #address-cells = <0x01>; + #size-cells = <0x00>; + + port@0 { + reg = <0x00>; + + endpoint@4 { + remote-endpoint = <0x6f>; + phandle = <0x7d>; + }; + }; + + port@1 { + reg = <0x01>; + + endpoint@5 { + remote-endpoint = <0x70>; + phandle = <0x7e>; + }; + }; + + port@2 { + reg = <0x02>; + + endpoint@6 { + remote-endpoint = <0x71>; + phandle = <0x7f>; + }; + }; + + port@3 { + reg = <0x03>; + + endpoint@7 { + remote-endpoint = <0x72>; + phandle = <0x80>; + }; + }; + }; + }; + + core@2 { + id = <0x02>; + + ports { + #address-cells = <0x01>; + #size-cells = <0x00>; + + port@0 { + reg = <0x00>; + + endpoint@8 { + remote-endpoint = <0x73>; + phandle = <0x82>; + }; + }; + + port@1 { + reg = <0x01>; + + endpoint@9 { + remote-endpoint = <0x74>; + phandle = <0x83>; + }; + }; + + port@2 { + reg = <0x02>; + + endpoint@10 { + remote-endpoint = <0x75>; + phandle = <0x84>; + }; + }; + + port@3 { + reg = <0x03>; + + endpoint@11 { + remote-endpoint = <0x76>; + phandle = <0x85>; + }; + }; + }; + }; + }; + + csi@0 { + compatible = "bst,a1000b_csi2"; + #address-cells = <0x01>; + #size-cells = <0x00>; + clock-lanes = <0x00>; + data-lanes = <0x01 0x02 0x03 0x04>; + lane-speed = <0x640>; + id = <0x00>; + resets = <0x06 0x0d>; + reset-names = "csi0_reset"; + + csi-link { + + ports { + + port@0 { + + endpoint@0 { + remote-endpoint = <0x77>; + phandle = <0x20>; + }; + }; + }; + }; + + ports { + #address-cells = <0x01>; + #size-cells = <0x00>; + + port@0 { + reg = <0x00>; + + endpoint@0 { + remote-endpoint = <0x78>; + phandle = <0x6b>; + }; + }; + + port@1 { + reg = <0x01>; + + endpoint@1 { + remote-endpoint = <0x79>; + phandle = <0x6c>; + }; + }; + + port@2 { + reg = <0x02>; + + endpoint@2 { + remote-endpoint = <0x7a>; + phandle = <0x6d>; + }; + }; + + port@3 { + reg = <0x03>; + + endpoint@3 { + remote-endpoint = <0x7b>; + phandle = <0x6e>; + }; + }; + }; + }; + + csi@1 { + compatible = "bst,a1000b_csi2"; + #address-cells = <0x01>; + #size-cells = <0x00>; + clock-lanes = <0x00>; + data-lanes = <0x01 0x02 0x03 0x04>; + lane-speed = <0x640>; + id = <0x01>; + resets = <0x06 0x0e>; + reset-names = "csi1_reset"; + + csi-link { + + ports { + + port@0 { + + endpoint@0 { + remote-endpoint = <0x7c>; + phandle = <0x25>; + }; + }; + }; + }; + + ports { + #address-cells = <0x01>; + #size-cells = <0x00>; + + port@0 { + reg = <0x00>; + + endpoint@0 { + remote-endpoint = <0x7d>; + phandle = <0x6f>; + }; + }; + + port@1 { + reg = <0x01>; + + endpoint@1 { + remote-endpoint = <0x7e>; + phandle = <0x70>; + }; + }; + + port@2 { + reg = <0x02>; + + endpoint@2 { + remote-endpoint = <0x7f>; + phandle = <0x71>; + }; + }; + + port@3 { + reg = <0x03>; + + endpoint@3 { + remote-endpoint = <0x80>; + phandle = <0x72>; + }; + }; + }; + }; + + csi@2 { + compatible = "bst,a1000b-csi2-2x2"; + #address-cells = <0x01>; + #size-cells = <0x00>; + clock-lanes = <0x00>; + data-lanes = <0x01 0x02>; + lane-speed = <0x640>; + resets = <0x06 0x0f>; + reset-names = "csi2_reset"; + id = <0x02>; + + csi-link { + + ports { + + port@0 { + + endpoint@0 { + remote-endpoint = <0x81>; + phandle = <0x2a>; + }; + }; + }; + }; + + ports { + #address-cells = <0x01>; + #size-cells = <0x00>; + + port@0 { + reg = <0x00>; + + endpoint@0 { + remote-endpoint = <0x82>; + phandle = <0x73>; + }; + }; + + port@1 { + reg = <0x01>; + + endpoint@1 { + remote-endpoint = <0x83>; + phandle = <0x74>; + }; + }; + + port@2 { + reg = <0x02>; + + endpoint@2 { + remote-endpoint = <0x84>; + phandle = <0x75>; + }; + }; + + port@3 { + reg = <0x03>; + + endpoint@3 { + remote-endpoint = <0x85>; + phandle = <0x76>; + }; + }; + }; + }; + + chosen { + bootargs = "earlycon=uart8250,mmio32,0x20008000 console=ttyS0,115200n8 memreserve=64M@0xf8000000 rdinit=/sbin/init root=/dev/ram rw init=/linuxrc rodata=n"; + stdout-path = "uart0"; + }; + + aliases { + }; + + memory@80000000 { + device_type = "memory"; + reg = <0x00 0x80000000 0x00 0x70000000>; + }; + + memory@198000000 { + device_type = "memory"; + reg = <0x01 0x98000000 0x00 0x58000000>; + }; + + memory1@18000000 { + device_type = "memory"; + reg = <0x00 0x18000000 0x00 0x100000>; + }; + + reserved-memory { + #address-cells = <0x02>; + #size-cells = <0x02>; + ranges; + + pcie_ctrl@8fd00000 { + compatible = "bst,pcie-ctrl"; + reg = <0x00 0x8fd00000 0x00 0x100000>; + no-map; + phandle = <0x0c>; + }; + + bst_atf@8b000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0x8b000000 0x00 0x2000000>; + no-map; + }; + + bst_tee@8fec0000 { + compatible = "shared-dma-pool"; + reg = <0x00 0x8fec0000 0x00 0x40000>; + no-map; + phandle = <0x89>; + }; + + bstn_cma@8ff00000 { + compatible = "shared-dma-pool"; + reg = <0x00 0x8ff00000 0x00 0x100000>; + no-map; + phandle = <0x86>; + }; + + bstn@90000000 { + compatible = "bst,bstn"; + reg = <0x00 0x90000000 0x00 0x2000000>; + no-map; + }; + + bst_lwnn@92000000 { + compatible = "bst,bst_lwnn"; + reg = <0x00 0x92000000 0x00 0x2000000>; + no-map; + }; + + bst_lwnn@94000000 { + compatible = "bst,bst_lwnn"; + reg = <0x00 0x94000000 0x00 0x2000000>; + no-map; + }; + + bst_cv@96000000 { + compatible = "bst,bst_cv"; + reg = <0x00 0x96000000 0x00 0x4000000>; + no-map; + }; + + bst_cv_cma@9a000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0x9a000000 0x00 0x2000000>; + align-shift = <0x08>; + no-map; + phandle = <0x87>; + }; + + vsp@0x9c000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0x9c000000 0x00 0x1000000>; + no-map; + phandle = <0x88>; + }; + + vsp_fw@0x9d000000 { + reg = <0x00 0x9d000000 0x00 0x4000000>; + no-map; + }; + + bst_isp@0xa1000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xa1000000 0x00 0x1000000>; + no-map; + phandle = <0x68>; + }; + + bst_isp_fw@0xa2000000 { + reg = <0x00 0xa2000000 0x00 0x10000000>; + no-map; + }; + + coreip_pub_cma@0xb2000000 { + compatible = "shared-dma-pool"; + align-shift = <0x08>; + reg = <0x00 0xb2000000 0x00 0x36000000>; + reusable; + phandle = <0x69>; + }; + + noc_pmu@0xe8000000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xe8000000 0x00 0x800000>; + reusable; + phandle = <0x46>; + }; + + canfd@0xe8800000 { + compatible = "shared-dma-pool"; + reg = <0x00 0xe8800000 0x00 0x800000>; + reusable; + phandle = <0x11>; + }; + + ddr0@0xf0000000 { + reg = <0x00 0xf0000000 0x00 0x10000000>; + no-map; + }; + + ddr1@0x1f0000000 { + reg = <0x01 0xf0000000 0x00 0x10000000>; + no-map; + }; + }; + + mbox-poll-clients { + compatible = "bst,ipc-mbox-client"; + reg = <0xfec00020 0x08 0x52030090 0x08 0x53090008 0x08 0xfec00028 0x08>; + #mbox-cells = <0x01>; + phandle = <0x0e>; + }; + + bstn-mbox { + compatible = "bstn,bstn-mbox"; + reg = <0x00 0x33102000 0x00 0x2000 0x00 0x33100000 0x00 0x2000 0x00 0x80000000 0x00 0x04 0x00 0x80002000 0x00 0x04>; + fpga-reset = <0x01>; + memory-region = <0x86>; + assigned-mem-size = <0x1000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0x71 0xff04 0x00 0x72 0xff04 0x00 0x73 0xff04 0x00 0x74 0xff04 0x00 0x75 0xff04 0x00 0x76 0xff04 0x00 0x77 0xff04 0x00 0x78 0xff04>; + #mbox-cells = <0x01>; + phandle = <0xea>; + }; + + bstn@0 { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "bst,bstn-a1000b,cma"; + reg = <0x00 0x50020000 0x00 0x100 0x00 0x90000000 0x00 0x2000000>; + memory-region = <0x69>; + rmem-base = <0x00 0xb2000000>; + rmem-size = <0x00 0x36000000>; + id = <0x00>; + assigned-mem-size = <0x1000>; + bus-offset = <0x00 0x00>; + mbox-names = "bstn-mbox"; + mboxes = <0xea 0x08>; + firmware = "bstn_dsp_rtos.rbf"; + }; + + bst_cv@0x51030000 { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "bst,bst_cv,cma"; + reg = <0x00 0x51030000 0x00 0x100 0x00 0x96000000 0x00 0x2000000 0x00 0x98000000 0x00 0x2000000 0x00 0x92000000 0x00 0x2000000 0x00 0x94000000 0x00 0x2000000>; + memory-region = <0x87>; + assigned-mem-size = <0x4000>; + bus-offset = <0x00 0x00>; + mbox-names = "bstn-mbox"; + mboxes = <0xea 0x09>; + dsp-num = <0x04>; + ipc-register-addr = <0x8ff00000>; + + dsp@0x96000000 { + index = <0x00>; + firmware = "bst_cv_dsp_rt.rbf"; + assigned-mem-size = <0x1000>; + rt-init-addr = <0x967ff000>; + ipc-src-core = <0x03>; + }; + + dsp@0x98000000 { + index = <0x01>; + firmware = "bst_cv_dsp1_rt.rbf"; + assigned-mem-size = <0x1000>; + rt-init-addr = <0x987ff000>; + ipc-src-core = <0x03>; + }; + + dsp@0x92000000 { + index = <0x02>; + firmware = "bst_cv_dsp2_rt.rbf"; + assigned-mem-size = <0x1000>; + rt-init-addr = <0x927ff000>; + ipc-src-core = <0x03>; + }; + + dsp@0x94000000 { + index = <0x03>; + firmware = "bst_cv_dsp3_rt.rbf"; + assigned-mem-size = <0x1000>; + rt-init-addr = <0x947ff000>; + ipc-src-core = <0x03>; + }; + }; + + bst_gwarp_scaler@0x51060000 { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "bst,bst_gwarp_scaler,cma"; + reg = <0x00 0x51030000 0x00 0x100 0x00 0x51050000 0x00 0x100 0x00 0x51060000 0x00 0x100>; + memory-region = <0x69>; + id = <0x00>; + bus-offset = <0x00 0x00>; + assigned-mem-size = <0x1000>; + interrupt-parent = <0x01>; + interrupts = <0x00 0x9b 0x04>; + interrupt-names = "bst_cv_irq"; + }; + + bare_cv@51030000 { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "bst,bare_cv,cma"; + reg = <0x00 0x51030000 0x00 0x100 0x00 0x96000000 0x00 0x1000000 0x00 0x98000000 0x00 0x1000000 0x00 0x97000000 0x00 0x1000000 0x00 0x99000000 0x00 0x1000000>; + memory-region = <0x87>; + id = <0x01>; + assigned-mem-size = <0x2000000 0x2000000 0x2000000 0x2000000>; + bus-offset = <0x00 0x00>; + mbox-names = "bstn-mbox"; + mboxes = <0xea 0x09>; + firmware = "bstcv0.rbf\0bstcv1.rbf\0bstcv2.rbf\0bstcv3.rbf"; + }; + + bst_lwnn@0x51030000 { + compatible = "bst,bst_lwnn-a1000b,cma"; + reg = <0x00 0x51030000 0x00 0x100 0x00 0x92000000 0x00 0x2000000 0x00 0x94000000 0x00 0x2000000>; + memory-region = <0x69>; + bus-offset = <0x00 0x00>; + mbox-names = "bst-lwnn-mbox"; + dsp-num = <0x02>; + ipc-register-addr = <0x8ff00000>; + + dsp@0x92000000 { + index = <0x02>; + firmware = "bst_lwnn_dsp2_rt.rbf"; + assigned-mem-size = <0x2000>; + rt-init-addr = <0x927ff000>; + ipc-src-core = <0x06>; + }; + + dsp@0x94000000 { + index = <0x03>; + firmware = "bst_lwnn_dsp3_rt.rbf"; + assigned-mem-size = <0x2000>; + rt-init-addr = <0x947ff000>; + ipc-src-core = <0x00>; + }; + }; + + ipc_vsp@0 { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "bst,bst-vsp-ipc"; + reg = <0x00 0x9c000000 0x00 0x100000 0x00 0x9c100000 0x00 0x80000 0x00 0x9c180000 0x00 0x80000 0x00 0x53090004 0x00 0x04 0x00 0x53090010 0x00 0x0c 0x00 0x33102fbc 0x00 0x04 0x00 0x9d000000 0x00 0x4000000>; + memory-region = <0x88>; + mbox-names = "bstn-mbox"; + mboxes = <0xea 0x07>; + }; + + vsp@1 { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "bst,bst-vsp"; + memory-region = <0x69>; + assigned-mem-size = <0x1000>; + clocks = <0x05 0x43>; + clock-names = "vout_display_clk"; + output-format = "HDMI_RGB"; + output-hsize = <0x780>; + output-vsize = <0x438>; + output-fresh = <0x3c>; + }; + + gmwarp@0 { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "bst,bst-gmwarp"; + memory-region = <0x69>; + }; + + encoder@0 { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "bst,bst-encoder"; + memory-region = <0x69>; + }; + + codec_dma_buf@0 { + compatible = "bst,dma_buf_te"; + memory-region = <0x69>; + }; + + firmware { + + optee { + compatible = "linaro,optee-tz"; + method = "smc"; + chip-number = <0x02>; + }; + }; + + tee { + #address-cells = <0x02>; + #size-cells = <0x02>; + reg = <0x00 0x18060000 0x00 0x20000>; + memory-region = <0x89>; + mbox-names = "bstn-mbox"; + chip-number = <0x01>; + }; + + ipc_arm0@0 { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "bst,arm0"; + reg = <0x04 0xfec00000 0x00 0x20 0x04 0x80000000 0x00 0x20000>; + memory-region = <0x86>; + assigned-mem-size = <0x1000>; + mbox-names = "bstn-mbox"; + mboxes = <0xea 0x00>; + }; + + ipc_arm3@3 { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "bst,arm3"; + reg = <0x04 0xfec10000 0x00 0x20 0x04 0x80000000 0x00 0x20000>; + memory-region = <0x86>; + assigned-mem-size = <0x1000>; + mbox-names = "bstn-mbox"; + mboxes = <0xea 0x03>; + }; + + ipc_arm2@2 { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "bst,arm2"; + reg = <0x04 0xfec10000 0x00 0x20 0x04 0x80000000 0x00 0x20000>; + memory-region = <0x86>; + assigned-mem-size = <0x1000>; + mbox-names = "bstn-mbox"; + mboxes = <0xea 0x02>; + }; + + ipc_arm1@1 { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "bst,arm1"; + reg = <0x04 0xfec10000 0x00 0x20 0x04 0x80000000 0x00 0x20000>; + memory-region = <0x86>; + assigned-mem-size = <0x1000>; + mbox-names = "bstn-mbox"; + mboxes = <0xea 0x01>; + }; + + ipc@0 { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "bst,ipc"; + reg = <0x04 0xfec00000 0x00 0x20 0x04 0x80000000 0x00 0x20000>; + memory-region = <0x86>; + assigned-mem-size = <0x1000>; + mbox-names = "bstn-mbox"; + mboxes = <0xea 0x05>; + }; +}; diff --git a/of/tests/of.rs b/of/tests/of.rs new file mode 100644 index 0000000..f6c7bcf --- /dev/null +++ b/of/tests/of.rs @@ -0,0 +1,102 @@ +static BST_DTB_DATA: &[u8] = include_bytes!("./bsta1000b-fada-bus.dtb"); + +fn setup() { + unsafe { + of_fdt::init_fdt_ptr(BST_DTB_DATA.as_ptr()); + } +} + +#[test] +fn test_module() { + setup(); + let model = of_fdt::machin_name(); + assert_eq!(model, "BST A1000B FAD-A"); +} + +#[test] +fn test_find_compatible() { + const CONSOLE_COMPATIABLE: &'static [&'static str] = &["snps,dw-apb-uart"]; + const CONSOLE_COUNT: usize = 4; + setup(); + let console_node = of_fdt::find_compatible_node(CONSOLE_COMPATIABLE); + assert_eq!(console_node.count(), CONSOLE_COUNT); +} + + +#[test] +fn test_find_i2c_compatible() { + const CONSOLE_COMPATIABLE: &'static [&'static str] = &["snps,designware-i2c"]; + setup(); + let mut i2c_bus_node = of_fdt::find_compatible_node(CONSOLE_COMPATIABLE); + + let i2c0 = i2c_bus_node.next().unwrap(); + let i2c1 = i2c_bus_node.next().unwrap(); + let i2c2 = i2c_bus_node.next().unwrap(); + assert_eq!(i2c0.name, "i2c@20000000"); + assert_eq!(i2c1.name, "i2c@20001000"); + assert_eq!(i2c2.name, "i2c@20002000"); + assert!(i2c0.children().next().is_none()); + let i2c2_child = i2c2.children().next().unwrap(); + assert_eq!(i2c2_child.name, "max96712@29"); +} + +#[test] +fn test_pcsi() { + setup(); + let of_pcsi = of_fdt::pcsi(); + assert!(of_pcsi.is_some()); + let of_pcsi = of_pcsi.unwrap(); + assert_eq!(of_pcsi.method(), "smc"); + assert_eq!(of_pcsi.cpu_on().unwrap(), 0xC4000003); + assert_eq!(of_pcsi.cpu_off().unwrap(), 0x84000002); + assert_eq!(of_pcsi.cpu_suspend().unwrap(), 0xC4000001); +} + +#[test] +fn test_platform() { + const OF_DEFAULT_BUS_MATCH_TABLE: [&'static [&'static str]; 4] = [ + &["simple-bus"], + &["simple-mfd"], + &["simple-isa"], + &["arm,amba-bus"], + ]; + setup(); + for b in OF_DEFAULT_BUS_MATCH_TABLE { + let bus_nodes = of_fdt::find_compatible_node(b); + if b[0].eq("simple-bus") { + assert_eq!(bus_nodes.count(), 1); + } else { + assert_eq!(bus_nodes.count(), 0); + } + } +} + +#[test] +fn test_irqcontroler() { + const I2C_COMPATIABLE: &'static [&'static str] = &["snps,designware-i2c"]; + setup(); + let i2c_node = of_fdt::find_compatible_node(I2C_COMPATIABLE).next().unwrap(); + let irq_controler = i2c_node.interrupt_parent().unwrap(); + assert_eq!("arm,gic-400", irq_controler.compatible().unwrap().first()); + assert_eq!(3, irq_controler.interrupt_cells().unwrap()); + let mut res: [u32; 3] = [0; 3]; + for i in 0..irq_controler.interrupt_cells().unwrap() { + res[i] = of_fdt::of_property_read_u32(i2c_node, "interrupts", i).unwrap(); + } + assert_eq!([0, 0xcf, 0x04], res); +} + +#[test] +fn test_phandle_arg() { + const I2C_COMPATIABLE: &'static [&'static str] = &["snps,designware-i2c"]; + setup(); + + let i2c_node = of_fdt::find_compatible_node(I2C_COMPATIABLE).next().unwrap(); + let phandle_arg = of_fdt::of_parse_phandle_with_args(i2c_node, "clocks", Some("#clock-cells"), 0) + .expect("i2c no clocks"); + assert_eq!(phandle_arg.args_count, 1); + assert_eq!(phandle_arg.args[0], 75); // LSP0_PCLK + let phandle_arg = of_fdt::of_parse_phandle_with_args(i2c_node, "clocks", Some("#clock-cells"), 1) + .expect("i2c no clocks"); + assert_eq!(phandle_arg.args[0], 73); // LSP0_WCLK +} diff --git a/r4l/src/device.rs b/r4l/src/device.rs index 36f7f21..ff9f41e 100644 --- a/r4l/src/device.rs +++ b/r4l/src/device.rs @@ -9,13 +9,15 @@ use crate::pr_info; use crate::prelude::*; use crate::platform::PlatformDevice; use core::any::Any; -use of::OfNode; +use crate::sync::Arc; +use of_fdt::OfNode; +#[derive(Clone)] pub struct Device { of_node: OfNode<'static>, // Driver matched the first device compatiable drv_matched: Option<&'static str>, - drv_data: Option>, + drv_data: Option>, } impl Device { @@ -27,6 +29,10 @@ impl Device { } } + pub fn get_node(&self) -> OfNode<'static> { + self.of_node + } + pub fn get_resource(&self, index: usize) -> Result { crate::of::of_membase_resource_get(self.of_node, index) } @@ -35,8 +41,8 @@ impl Device { crate::of::of_irq_get(self.of_node, index) } - pub fn set_drv_data(&mut self, drv_data: T) { - self.drv_data = Some(Box::new(drv_data)); + pub fn set_drv_data(&mut self, drv_data: T) { + self.drv_data = Some(Arc::new(drv_data)); } pub fn get_drv_data(&self) -> Option<&T> { @@ -51,20 +57,20 @@ impl Device { } pub fn device_property_read_u32(&self, propname: &'static CStr) -> Result { - let res = of::of_property_read_u32(self.of_node, propname, 0); + let res = of_fdt::of_property_read_u32(self.of_node, propname, 0); match res { Some(val) => { Ok(val)} None => { Err(EINVAL) } } } - pub fn from_dev(pdev: &PlatformDevice) -> &Self { + pub fn from_dev(pdev: &PlatformDevice) -> Self { pdev.get_device() } } pub trait DeviceOps { - fn set_drv_data(&mut self, drv_data: T); + fn set_drv_data(&mut self, drv_data: T); fn get_drv_data(&self) -> Option<&T>; fn compatible_match(&self, compatible: &'static str) -> bool; } diff --git a/r4l/src/init.rs b/r4l/src/init.rs index 000dc53..07eb218 100644 --- a/r4l/src/init.rs +++ b/r4l/src/init.rs @@ -5,14 +5,17 @@ use core::ffi::{c_int, c_void}; struct InitcallAddrPair(*const u8, *const u8); -pub fn driver_framework_init() { - subsys_fn_init(); +pub fn driver_framework_init(dtb_virt_addr: *const u8) { + subsys_fn_init(dtb_virt_addr); module_fn_init(); } -fn subsys_fn_init() { +fn subsys_fn_init(dtb_virt_addr: *const u8) { + // # Safety + // unsafe because it dereferences a raw pointer. + unsafe { of_fdt::init_fdt_ptr(dtb_virt_addr) }; if let Err(e) = crate::of::of_platform_default_populate_init() { - panic!("subsys fn init failed"); + panic!("subsys fn init failed, {}", e); } } diff --git a/r4l/src/of/platform.rs b/r4l/src/of/platform.rs index 8868d0e..a15b796 100644 --- a/r4l/src/of/platform.rs +++ b/r4l/src/of/platform.rs @@ -6,13 +6,13 @@ use crate::platform::{platform_device_register, PlatformDevice}; use crate::pr_debug; use crate::sync::Arc; use crate::sync::Mutex; -use of::OfNode; +use of_fdt::OfNode; const OF_DEFAULT_BUS_MATCH_TABLE: [&'static str; 4] = ["simple-bus", "simple-mfd", "isa", "arm,amba-bus"]; fn of_platform_bus_device_create(node: OfNode<'static>) -> Result { - if !of::of_device_is_available(node) { + if !of_fdt::of_device_is_available(node) { return Ok(()); } let pdev = Arc::new(Mutex::new(PlatformDevice::new(node))); @@ -21,7 +21,7 @@ fn of_platform_bus_device_create(node: OfNode<'static>) -> Result { } pub fn of_platform_default_populate_init() -> Result { - let bus_node = of::find_compatible_node(&OF_DEFAULT_BUS_MATCH_TABLE); + let bus_node = of_fdt::find_compatible_node(&OF_DEFAULT_BUS_MATCH_TABLE); for i in bus_node { crate::pr_info!("bus node: {:?} ", i.compatible().unwrap().first()); for c in i.children() { diff --git a/r4l/src/of/resource.rs b/r4l/src/of/resource.rs index 042e9d8..0715a67 100644 --- a/r4l/src/of/resource.rs +++ b/r4l/src/of/resource.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 use crate::prelude::*; -use of::OfNode; +use of_fdt::OfNode; const MAX_PHANDLE_ARGS: usize = 32; struct OfPhandleArgs { @@ -20,7 +20,7 @@ impl OfPhandleArgs { let mut res = Self{np: Some(parent), args_count: intsize, args: [0;MAX_PHANDLE_ARGS]}; for i in 0..intsize { - res.args[i] = of::of_property_read_u32(node, "interrupts", (index * intsize) + i).ok_or(EINVAL)?; + res.args[i] = of_fdt::of_property_read_u32(node, "interrupts", (index * intsize) + i).ok_or(EINVAL)?; } pr_debug!(" intspec={:?}\n", res.args); // TODO: Check if there are any interrupt-map translations to process diff --git a/r4l/src/platform/platform_dev.rs b/r4l/src/platform/platform_dev.rs index c8fba14..8fbcb02 100644 --- a/r4l/src/platform/platform_dev.rs +++ b/r4l/src/platform/platform_dev.rs @@ -3,7 +3,7 @@ //! A platform device. use crate::{device, io}; use core::any::Any; -use of::OfNode; +use of_fdt::OfNode; use crate::error::Result; @@ -32,13 +32,19 @@ impl PlatformDevice { } /// get device - pub fn get_device(&self) -> &device::Device { - &self.device + pub fn get_device(&self) -> device::Device { + self.device.clone() } + + /// get node + pub fn of_node(&self) -> OfNode<'static> { + self.device.get_node() + } + } impl device::DeviceOps for PlatformDevice { - fn set_drv_data(&mut self, drv_data: T) { + fn set_drv_data(&mut self, drv_data: T) { self.device.set_drv_data(drv_data); } diff --git a/r4l/src/platform/platform_drv.rs b/r4l/src/platform/platform_drv.rs index b33d53f..d5177da 100644 --- a/r4l/src/platform/platform_drv.rs +++ b/r4l/src/platform/platform_drv.rs @@ -37,7 +37,7 @@ impl PlatformDriver { self.id_table = id_table; } - fn register(this: Arc, name: &'static CStr, module: &'static ThisModule) -> Result { + fn register(_this: Arc, _name: &'static CStr, _module: &'static ThisModule) -> Result { Ok(()) } @@ -108,7 +108,7 @@ impl driver::DriverOps for Adapter { Ok(()) } - fn unregister(pdrv: &mut Self::RegType) {} + fn unregister(_pdrv: &mut Self::RegType) {} } impl Adapter { From 4aca1b850dc69153fe39186498cacce429a2d821 Mon Sep 17 00:00:00 2001 From: happy-thw Date: Tue, 29 Oct 2024 10:31:42 +0800 Subject: [PATCH 09/14] pass module name to Module::init --- drivers/sample/minimal/src/lib.rs | 2 +- macros/src/module.rs | 5 ++++- r4l/src/driver/driver.rs | 4 ++-- r4l/src/lib.rs | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/sample/minimal/src/lib.rs b/drivers/sample/minimal/src/lib.rs index cb5f9ee..9f0c235 100644 --- a/drivers/sample/minimal/src/lib.rs +++ b/drivers/sample/minimal/src/lib.rs @@ -18,7 +18,7 @@ struct RustMinimal { } impl kernel::Module for RustMinimal { - fn init(_module: &'static ThisModule) -> Result { + fn init(_name: &'static CStr, _module: &'static ThisModule) -> Result { pr_info!("Rust minimal sample (init)\n"); pr_info!("Am I built-in? {}\n", !cfg!(MODULE)); diff --git a/macros/src/module.rs b/macros/src/module.rs index fa66c46..6cf718c 100644 --- a/macros/src/module.rs +++ b/macros/src/module.rs @@ -139,7 +139,10 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream { /// /// This function must only be called once. unsafe fn __init() -> core::ffi::c_int {{ - match <{type_} as kernel::Module>::init(&super::super::THIS_MODULE) {{ + match <{type_} as kernel::Module>::init( + kernel::c_str!(\"{name}\"), + &super::super::THIS_MODULE + ) {{ Ok(m) => {{ // SAFETY: No data race, since `__MOD` can only be accessed by this // module and there only `__init` and `__exit` access it. These diff --git a/r4l/src/driver/driver.rs b/r4l/src/driver/driver.rs index cc2efb8..f1cbc25 100644 --- a/r4l/src/driver/driver.rs +++ b/r4l/src/driver/driver.rs @@ -151,9 +151,9 @@ pub struct Module { } impl crate::Module for Module { - fn init(module: &'static ThisModule) -> Result { + fn init(name: &'static CStr, module: &'static ThisModule) -> Result { Ok(Self { - _driver: Registration::new_pinned(c_str!("no-name"), module)?, + _driver: Registration::new_pinned(name, module)?, }) } } diff --git a/r4l/src/lib.rs b/r4l/src/lib.rs index a1f59a3..459d305 100644 --- a/r4l/src/lib.rs +++ b/r4l/src/lib.rs @@ -53,7 +53,7 @@ pub trait Module: Sized + Sync + Send { /// should do. /// /// Equivalent to the `module_init` macro in the C API. - fn init(module: &'static ThisModule) -> error::Result; + fn init(name: &'static str::CStr, module: &'static ThisModule) -> error::Result; } /// Replace Linux `THIS_MODULE` in the C API. From f9c1e5d4d1dff03447b9463c4eb8d3f5a5078e97 Mon Sep 17 00:00:00 2001 From: happy-thw Date: Tue, 29 Oct 2024 10:33:46 +0800 Subject: [PATCH 10/14] add fmt! macro --- r4l/src/prelude.rs | 1 + r4l/src/str.rs | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/r4l/src/prelude.rs b/r4l/src/prelude.rs index b1bfe95..e63dfce 100644 --- a/r4l/src/prelude.rs +++ b/r4l/src/prelude.rs @@ -15,6 +15,7 @@ pub use alloc::{boxed::Box, vec::Vec}; pub use super::error::{code::*, Error, Result}; +pub use super::fmt; pub use super::{pr_alert, pr_crit, pr_debug, pr_emerg, pr_err, pr_info, pr_notice, pr_warn}; pub use super::{str::CStr, ThisModule}; pub use crate::build_error::build_error; diff --git a/r4l/src/str.rs b/r4l/src/str.rs index a7e256f..dfb255a 100644 --- a/r4l/src/str.rs +++ b/r4l/src/str.rs @@ -38,3 +38,9 @@ mod str { } pub use str::*; + +/// A convenience alias for [`core::format_args`]. +#[macro_export] +macro_rules! fmt { + ($($f:tt)*) => ( core::format_args!($($f)*) ) +} From c9f2c56734ec343714f001fc7db509ea6036064b Mon Sep 17 00:00:00 2001 From: happy-thw Date: Tue, 29 Oct 2024 10:36:30 +0800 Subject: [PATCH 11/14] add the I2C driver core framework A Linux-based I2C driver framework providing R4L-like abstractions. Signed-off-by: happy-thw --- r4l/src/i2c/i2c_bus.rs | 114 +++++++++++++++++++++++++ r4l/src/i2c/i2c_client.rs | 73 ++++++++++++++++ r4l/src/i2c/i2c_drv.rs | 175 ++++++++++++++++++++++++++++++++++++++ r4l/src/i2c/mod.rs | 145 +++++++++++++++++++++++++++++++ r4l/src/i2c/msg.rs | 2 +- 5 files changed, 508 insertions(+), 1 deletion(-) create mode 100644 r4l/src/i2c/i2c_bus.rs create mode 100644 r4l/src/i2c/i2c_client.rs create mode 100644 r4l/src/i2c/i2c_drv.rs diff --git a/r4l/src/i2c/i2c_bus.rs b/r4l/src/i2c/i2c_bus.rs new file mode 100644 index 0000000..5976634 --- /dev/null +++ b/r4l/src/i2c/i2c_bus.rs @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0 + +use super::{I2cClient, I2cDriver,I2cAdapter,I2cAlgo}; +use alloc::collections::VecDeque; +use axlog::info; + +use crate::bus::BusType; +use crate::device::DeviceOps; +use crate::of::DeviceId::Compatible; +use crate::prelude::Vec; +use crate::sync::{Arc, Mutex}; +use crate::{error::*, pr_debug, pr_info}; + +pub struct I2cBus { + i2c_clients: VecDeque>>, + i2c_drivers: VecDeque>>, +} + +impl I2cBus { + const fn new() -> Self { + I2cBus { + i2c_clients: VecDeque::new(), + i2c_drivers: VecDeque::new(), + } + } +} + +unsafe impl Send for I2cBus {} +unsafe impl Sync for I2cBus {} + +impl BusType for I2cBus { + const NAME: &'static str = "i2c"; + type Device = Arc>; + type Driver = Arc>; + + fn bus_driver_match(&self, i2cdrv: Self::Driver) -> Vec { + let mut matched_i2cdrv: Vec<_> = Vec::new(); + let table = i2cdrv + .lock() + .id_table() + .expect("i2c driver not define Compatible Table"); + for clinet in self.i2c_clients.iter() { + for id in table { + match id { + Compatible(id) => { + if clinet.lock().compatible_match(id) { + pr_info!("i2c driver : {} i2c client matched", id); + matched_i2cdrv.push(clinet.clone()); + } + } + _ => panic!("invalid id table"), + } + } + } + matched_i2cdrv + } + + fn add_device(&mut self, i2cdev: Self::Device) -> Result { + self.i2c_clients.push_back(i2cdev); + Ok(()) + } + + fn add_driver(&mut self, i2cdrv: Self::Driver) -> Result { + self.i2c_drivers.push_back(i2cdrv); + Ok(()) + } + +} + +static I2C_BUS: Mutex = Mutex::new(I2cBus::new()); + +pub fn i2c_register_device(client: Arc>) -> Result { + I2C_BUS.lock().add_device(client)?; + Ok(()) +} + +pub fn i2c_register_driver(i2cdrv: Arc>) -> Result { + let mut i2c_bus = I2C_BUS.lock(); + i2c_bus.add_driver(i2cdrv.clone())?; + let matched_i2cdrv = i2c_bus.bus_driver_match(i2cdrv.clone()); + // before probe, unlock bus + drop(i2c_bus); + for clinet in matched_i2cdrv { + match i2cdrv.lock().probe { + Some(fn_probe) => fn_probe(clinet)?, + None => panic!("pdev not have probe call back"), + } + } + Ok(()) +} + +pub fn i2c_register_adapter(adpter: I2cAdapter, data: T::Data) -> Result + where ::Data: 'static +{ + let adpt_of_node = adpter.of_node; + let mut child = adpt_of_node.children().peekable(); + if child.peek().is_some() { + for c in child { + if !of_fdt::of_device_is_available(c) { + continue; + } + let client = Arc::new( + Mutex::new(I2cClient::new(c, adpter.clone())) + ); + client.lock().set_adpt_data(data.clone()); + I2C_BUS.lock().add_device(client.clone())?; + pr_info!("i2c clinet {} register ok ", c.name); + } + } else { + pr_debug!("there is no childen in {}", adpt_of_node.name); + } + Ok(()) +} + diff --git a/r4l/src/i2c/i2c_client.rs b/r4l/src/i2c/i2c_client.rs new file mode 100644 index 0000000..6815381 --- /dev/null +++ b/r4l/src/i2c/i2c_client.rs @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! A platform device. +use crate::{device, io}; +use core::any::{self, Any}; +use of_fdt::OfNode; + +use crate::error::Result; +use crate::sync::{Arc, Mutex}; +use crate::i2c::Box; + +use super::{I2cAdapter,I2cAlgo }; + +pub struct I2cClient { + device: device::Device, + adapter: I2cAdapter, + data: Option>, +} + +impl I2cClient { + pub const fn new(of_node: OfNode<'static>, adpt: I2cAdapter) -> Self { + I2cClient { + device: device::Device::new(of_node), + adapter: adpt, + data: None, + } + } + + /// Returns irq of the i2c device. + pub fn irq_resource(&self, index: usize) -> Result { + self.device.irq_resource(index) + } + + /// Return ioremap ptr + pub fn ioremap_resource(&self, index: usize) -> Result{ + let addr = self.device.get_resource(index)?; + Ok(io::ioremap(addr)) + } + + /// get device + pub fn get_device(&self) -> &device::Device { + &self.device + } + + /// get adapter + pub fn get_adapter(&self) -> &I2cAdapter { + &self.adapter + } + + /// set adapter data + pub fn set_adpt_data(&mut self, adpt_data: T) { + self.data = Some(Box::new(adpt_data)); + } + + /// get adapter data + pub fn get_adpt_data(&self) -> Option<&T> { + self.data.as_ref()?.downcast_ref::() + } +} + +impl device::DeviceOps for I2cClient { + fn set_drv_data(&mut self, drv_data: T) { + self.device.set_drv_data(drv_data); + } + + fn get_drv_data(&self) -> Option<&T> { + self.device.get_drv_data::() + } + + fn compatible_match(&self, compatible: &'static str) -> bool { + self.device.compatible_match(compatible) + } +} diff --git a/r4l/src/i2c/i2c_drv.rs b/r4l/src/i2c/i2c_drv.rs new file mode 100644 index 0000000..022ad76 --- /dev/null +++ b/r4l/src/i2c/i2c_drv.rs @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0 + +use super::{i2c_register_driver, I2cClient}; +use crate::{ + device::DeviceOps, + driver, + driver::IdArray, + driver::IdTable, + error::*, + of, + prelude::*, + sync::Arc, sync::Mutex, +}; + +type PlatformIdTable = &'static [of::DeviceId]; + +pub struct I2cDriver { + driver: driver::DeviceDriver, + pub probe: Option>) -> Result>, + remove: Option Result>, + id_table: Option, +} + +impl Default for I2cDriver { + fn default() -> Self { + let mut s = ::core::mem::MaybeUninit::::uninit(); + unsafe { + ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} + +impl I2cDriver { + fn init( + &mut self, + probe: fn(dev: Arc>) -> Result, + remove: fn(dev: &mut I2cClient) -> Result, + id_table: Option, + ) { + self.probe = Some(probe); + self.remove = Some(remove); + self.id_table = id_table; + } + + fn register(this: Arc, name: &'static CStr, module: &'static ThisModule) -> Result { + Ok(()) + } + + fn unregister(&mut self) {} + + pub fn id_table(&self) -> Option { + self.id_table + } +} + +/// A i2c driver. +pub trait Driver +where + Self: 'static, +{ + /// Data stored on RawDeviceIdce by driver. + /// + /// Corresponds to the data set or retrieved via the kernel's + /// `platform_{set,get}_drvdata()` functions. + /// + /// Require that `Data` implements `ForeignOwnable`. We guarantee to + /// never move the underlying wrapped data structure. This allows + type Data: Send + Sync + driver::DeviceRemoval + Clone = (); + + /// The type holding information about each device id supported by the driver. + type IdInfo: 'static = (); + + const OF_DEVICE_ID_TABLE_SIZE: usize = 0; + /// The table of device ids supported by the driver. + const OF_DEVICE_ID_TABLE: Option<&'static [of::DeviceId]> = None; + + /// I2c driver probe. + /// + /// Called when a new i2c device is added or discovered. + /// Implementers should attempt to initialize the device here. + fn probe(dev: &mut I2cClient) -> Result; + + /// I2c driver remove. + /// + /// Called when a i2c device is removed. + /// Implementers should prepare the device for complete removal here. + fn remove(_data: &Self::Data) -> Result { + Ok(()) + } +} + +/// A registration of a i2c driver. +pub type Registration = driver::Registration>; + +/// An adapter for the registration of i2c drivers. +pub struct Adapter(T); + +impl driver::DriverOps for Adapter { + type RegType = Arc>; + + fn register( + i2cdrv: &mut Self::RegType, + name: &'static CStr, + module: &'static ThisModule, + ) -> Result { + i2cdrv.lock().init( + Self::probe_callback, + Self::remove_callback, + T::OF_DEVICE_ID_TABLE, + ); + i2cdrv.lock().driver.init(name, module)?; + i2c_register_driver(i2cdrv.clone())?; + Ok(()) + } + + fn unregister(_i2cdrv: &mut Self::RegType) {} +} + +impl Adapter { + + fn probe_callback(client: Arc>) -> Result { + let mut client = client.lock(); + let data = T::probe(&mut client)?; + client.set_drv_data(data.clone()); + Ok(()) + } + + fn remove_callback(pdev: &mut I2cClient) -> Result { + let data = pdev.get_drv_data::().unwrap(); + T::remove(data)?; + ::device_remove(data); + Ok(()) + } +} + +macro_rules! module_i2c_device { + ($($f:tt)*) => { + $crate::module_driver!(, $crate::i2c::Adapter, { $($f)* }); + }; +} + +/// Declares a kernel module that exposes a single i2c driver. +/// +/// # Examples +/// +/// ```ignore +/// # use kernel::{i2c, define_i2c_id_table, module_i2c_driver}; +/// kernel::module_i2c_id_table!(MOD_TABLE, I2C_CLIENT_I2C_ID_TABLE); +/// kernel::define_i2c_id_table! {I2C_CLIENT_I2C_ID_TABLE, (), [ +/// (i2c::DeviceId(b"fpga"), None), +/// ]} +/// struct MyDriver; +/// impl i2c::Driver for MyDriver { +/// kernel::driver_i2c_id_table!(I2C_CLIENT_I2C_ID_TABLE); +/// // [...] +/// # fn probe(_client: &mut i2c::Client) -> Result { +/// # Ok(()) +/// # } +/// } +/// +/// module_i2c_driver! { +/// type: MyDriver, +/// name: "module_name", +/// author: "Author name", +/// license: "GPL", +/// } +/// ``` + +#[macro_export] +macro_rules! module_i2c_driver { + ($($f:tt)*) => { + $crate::module_driver!(, $crate::i2c::Adapter, { $($f)* }); + }; +} diff --git a/r4l/src/i2c/mod.rs b/r4l/src/i2c/mod.rs index ebda076..82fcb1f 100644 --- a/r4l/src/i2c/mod.rs +++ b/r4l/src/i2c/mod.rs @@ -5,3 +5,148 @@ pub mod timing; pub mod functionality; pub mod msg; + +mod i2c_bus; +mod i2c_client; +mod i2c_drv; + +pub use i2c_bus::*; +pub use i2c_client::*; +pub use i2c_drv::*; + +use crate::{prelude::*, device}; +use core::marker::PhantomData; +use of_fdt::OfNode; +use msg::I2cMsgInfo; +use crate::sync::{Arc,Mutex}; +use crate::driver; + +use macros::vtable; + +/// A i2cAdapter. +#[vtable] +pub trait I2cAlgo { + /// Context data associated with the gpio chip. + /// + /// It determines the type of the context data passed to each of the methods of the trait. + type Data: Send + Sync + Clone ; + + /// master xfer + fn master_xfer(_data: &Self::Data, _msg: &I2cMsg, _msg_len: usize) -> Result; + + /// functonality + fn functionality(_data: &Self::Data) -> u32; +} + +/// Wraps the kernel's struct of i2c_msg. +pub struct I2cMsg(Vec); + +// SAFETY: `msg` only holds a pointer to a C i2c_msg, which is safe to be used from any thread. +unsafe impl Send for I2cMsg {} + +// SAFETY: `Device` only holds a pointer to a C i2c_msg, references to which are safe to be used +// from any thread. +unsafe impl Sync for I2cMsg {} + + +impl I2cMsg { + /// From arceos msgs to user msg array + pub fn into_array(&self) -> Result> + { + Ok(self.0.clone()) + } +} + +#[derive(Clone)] +pub struct I2cAlgorithm { + pub master_xfer: Option Result>, + pub functionality: Option Result>, +} + +impl Default for I2cAlgorithm { + fn default() -> Self { + let mut s = ::core::mem::MaybeUninit::::uninit(); + unsafe { + ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} + +/// +/// `Struct i2c_adapter`. +/// +#[derive(Clone)] +pub struct I2cAdapter{ + name: &'static str, + owner: &'static ThisModule, + of_node: OfNode<'static>, + parent: device::Device, + pub alogrithm: I2cAlgorithm, +} + +unsafe impl Sync for I2cAdapter {} +unsafe impl Send for I2cAdapter {} + + +impl I2cAdapter { + // Creates a new [`I2cAdapter`] but does not register it yet. + pub fn new ( + name: &'static CStr, + owner: &'static ThisModule, + of_node: OfNode<'static>, + device: device::Device, + ) -> Self + where ::Data: 'static { + let mut instance = Self { + name: name, + owner: owner, + of_node: of_node, + parent: device, + alogrithm: I2cAlgorithm::default(), + }; + + let algo = &mut instance.alogrithm; + + if T::HAS_MASTER_XFER { + algo.master_xfer = Some(master_xfer_callback::); + } + if T::HAS_FUNCTIONALITY { + algo.functionality = Some(functionality_callback::); + } + instance + } + + pub fn get_node(&self) -> OfNode<'static> { + self.of_node + } + + // Add the number of adapter,and register adapter into clinet + pub fn add_numbered_adapter(&self, data: T::Data) -> Result + where ::Data: 'static + { + i2c_register_adapter::( self.clone(), data)?; + Ok(()) + } + +} + +impl Drop for I2cAdapter { + fn drop(&mut self) { + pr_warn!("i2c adapter dropped"); + } +} + +pub fn master_xfer_callback(client: &mut I2cClient, msg: &I2cMsg, msg_len: usize) -> Result + where ::Data: 'static +{ + let data = client.get_adpt_data::().unwrap(); + T::master_xfer(data, msg, msg_len) +} + +pub fn functionality_callback(client: &mut I2cClient) -> Result + where ::Data: 'static +{ + let data = client.get_adpt_data::().unwrap(); + Ok(T::functionality(data)) +} \ No newline at end of file diff --git a/r4l/src/i2c/msg.rs b/r4l/src/i2c/msg.rs index 6f41fd5..b698373 100644 --- a/r4l/src/i2c/msg.rs +++ b/r4l/src/i2c/msg.rs @@ -88,7 +88,7 @@ pub const STATUS_MASK: u32 = genmask(2, 0); pub const DW_IC_ERR_TX_ABRT:u32 = 0x1; /// an I2C transaction segment beginning with START -#[derive(Debug) ] +#[derive(Debug, Clone)] #[allow(dead_code)] pub struct I2cMsgInfo{ /// Slave address, either 7 or 10 bits. When this is a 10 bit address, From 28268fd9e116a0f5651f87fb17df549d6f3671a4 Mon Sep 17 00:00:00 2001 From: happy-thw Date: Tue, 29 Oct 2024 10:39:29 +0800 Subject: [PATCH 12/14] update i2c-designware driver, add i2c_adapter and so on --- drivers/i2c/busses/i2c-designware/src/lib.rs | 109 +++++++++++++++---- 1 file changed, 88 insertions(+), 21 deletions(-) diff --git a/drivers/i2c/busses/i2c-designware/src/lib.rs b/drivers/i2c/busses/i2c-designware/src/lib.rs index 831e864..38d4890 100644 --- a/drivers/i2c/busses/i2c-designware/src/lib.rs +++ b/drivers/i2c/busses/i2c-designware/src/lib.rs @@ -6,24 +6,35 @@ use kernel::{ driver, - irq, - device::Device, - module_platform_driver, of, platform, - sync::Arc, + c_str, i2c::{ - timing::{self, I2cTiming, I2cSpeedMode }, - msg::{self, I2cMsgFlags, I2cMsgInfo, GeneralI2cMsgInfo }, + self, + I2cAlgo, + I2cMsg, + I2cAdapter, functionality::I2cFuncFlags, + msg::{ + self, I2cMsgFlags, I2cMsgInfo, GeneralI2cMsgInfo, + }, + timing::{ + self, I2cTiming, I2cSpeedMode, + } }, - timekeeping::read_poll_timeout, - sync::SpinLock, types::genmask, + timekeeping::read_poll_timeout, + device::Device, + + module_platform_driver, of, platform, + sync::{Arc,SpinLock, Mutex }, prelude::*, regmap, math, delay, - new_spinlock, + irq, // completion::Completion, + new_spinlock, + + }; module_platform_driver! { @@ -40,7 +51,11 @@ kernel::define_of_id_table! {DW_I2C_OF_MATCH_TABLE, (), [ (of::DeviceId::Compatible("snps,designware-i2c"),None), ]} -struct DwI2cData(); +struct DwI2cData { + i2c_adapter: I2cAdapter, + driver: I2cDwMasterDriver, + irq: Mutex>, +} impl driver::DeviceRemoval for DwI2cData { fn device_remove(&self) { @@ -48,13 +63,60 @@ impl driver::DeviceRemoval for DwI2cData { } } +impl DwI2cData { + fn new( + driver: I2cDwMasterDriver, + parent: Device, + pdev: &mut platform::Device, + irq: u32, + ) -> Arc { + let arc_instance = Arc::new(Self { + i2c_adapter: I2cAdapter::new::( + c_str!("Synopsys DesignWare I2C adapter"), + &THIS_MODULE, + pdev.of_node(), + parent, + ), + driver: driver, + // TODO: ugly method to deal with irq's data. + irq: Mutex::new(Vec::new()), + }); + let irq_register= irq::Registration::try_new::( + irq, + arc_instance.clone(), + irq::Flags::SHARED |irq::Flags::COND_SUSPEND , + fmt!("dw_i2c_irq_{irq}")).unwrap(); + arc_instance.irq.lock().push(irq_register); + arc_instance + } +} + +struct DwI2cAlgo; +#[vtable] +impl I2cAlgo for DwI2cAlgo { + type Data = Arc; + fn master_xfer(data: &Self::Data, msg: &I2cMsg, _msg_len: usize) -> Result { + pr_info!("master_xfer...."); + let trans_msgs = msg.into_array()?; + let master_driver = &data.driver; + master_driver.master_transfer(trans_msgs) + } + + fn functionality(data: &Self::Data) -> u32 { + pr_info!("functionality...."); + let master_driver = &data.driver; + master_driver.get_functionality() + } +} + struct DwI2cIrqHandler; impl irq::Handler for DwI2cIrqHandler { - type Data = i32; + type Data = Arc; - fn handle_irq(data: &i32) -> irq::Return { - pr_info!("handled i2c irq get data {} ", data); - irq::Return::Handled + fn handle_irq(data: &Self::Data) -> irq::Return { + pr_info!("handled i2c irq ..."); + let master_driver = &data.driver; + master_driver.irq_handler() } } @@ -81,9 +143,17 @@ impl platform::Driver for DwI2cDriver { let mut i2c_master_driver = I2cDwMasterDriver::new(driver_config, reg_base); i2c_master_driver.setup()?; - // Todo: create data + // create data + let data: Arc = DwI2cData::new( + i2c_master_driver, + dev, + pdev, + irq as u32, + ); - Ok(Arc::new(DwI2cData())) + // register adapter + (&(data.i2c_adapter)).add_numbered_adapter::(data.clone())?; + Ok(data) } } @@ -125,8 +195,6 @@ pub(crate) struct I2cDwCoreDriver { unsafe impl Sync for I2cDwCoreDriver {} unsafe impl Send for I2cDwCoreDriver {} - -#[allow(dead_code)] impl I2cDwCoreDriver { pub(crate) fn new(config: I2cDwDriverConfig, base_addr: usize) -> Self { Self { @@ -452,10 +520,8 @@ impl I2cDwCoreDriver { let mut try_cnt = 100; loop { self.disable_nowait(); - pr_debug!("==== usleep before ======"); - // delay::usleep(100); + delay::usleep(100); // check enable_status - pr_debug!("==== usleep after ======"); let status = regmap::reg_read(self.base, DW_IC_ENABLE_STATUS); if status & 1 == 0 { break; @@ -597,6 +663,7 @@ impl MasterXfer { let _ = core_driver.ic_enable_status(); } + /// Interrupt service routine. fn irq_process(&mut self, master_driver: &I2cDwMasterDriver) -> TransferResult { let core_driver = &master_driver.driver; From 6ea5ea9ae698960f324182e4fee64ad6aa26ee9c Mon Sep 17 00:00:00 2001 From: happy-thw Date: Tue, 29 Oct 2024 10:39:55 +0800 Subject: [PATCH 13/14] add a sample of ox3c-raw i2c device driver, verify the transform callback Signed-off-by: happy-thw --- Cargo.toml | 1 + drivers/i2c/ox3c-raw/Cargo.toml | 13 ++++++++++ drivers/i2c/ox3c-raw/src/lib.rs | 45 +++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 drivers/i2c/ox3c-raw/Cargo.toml create mode 100644 drivers/i2c/ox3c-raw/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 15d7269..7f57664 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ members = [ "drivers/sample/minimal/", "drivers/sample/platform/", "drivers/i2c/busses/i2c-designware", + "drivers/i2c/ox3c-raw", "r4l/", "macros/", "of/", diff --git a/drivers/i2c/ox3c-raw/Cargo.toml b/drivers/i2c/ox3c-raw/Cargo.toml new file mode 100644 index 0000000..f3620b2 --- /dev/null +++ b/drivers/i2c/ox3c-raw/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "ox3c_raw" +version = "0.1.0" +edition = "2021" +description = "An r4l i2c_clinet driver sample" +license = "GPL-3.0-or-later OR Apache-2.0" +homepage = "" + +[features] +arceos=["kernel/arceos"] +default =["arceos"] +[dependencies] +kernel = { package="r4l", path = "../../../r4l/"} \ No newline at end of file diff --git a/drivers/i2c/ox3c-raw/src/lib.rs b/drivers/i2c/ox3c-raw/src/lib.rs new file mode 100644 index 0000000..eef00ac --- /dev/null +++ b/drivers/i2c/ox3c-raw/src/lib.rs @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Rust ofilm_ox3c i2c_camera driver + +#![no_std] + +use kernel::{of, i2c, prelude::*}; + + +kernel::module_i2c_driver! { + type: CameraOx3c, + name: "ofilm_ox3c", + author: "Heaven Tom", + description: "Rust ofilm_ox3c driver sample", + license: "GPL", +} + +kernel::module_of_id_table!(OF_MOD_TABLE, OX3C_OF_ID_TABLE); +kernel::define_of_id_table! {OX3C_OF_ID_TABLE, (), [ + (of::DeviceId::Compatible("bst,ofilm_ox3c"), None), +]} + +struct CameraOx3c; +impl i2c::Driver for CameraOx3c { + type Data = (); + + kernel::driver_of_id_table!(OX3C_OF_ID_TABLE); + + fn probe(client: &mut i2c::I2cClient) -> Result<()> { + pr_info!("media ofilm_ox3c i2c driver probe start"); + let adpt = client.get_adapter(); + pr_info!("its adapter node name is {}", adpt.get_node().name); + + let functionality = match &adpt.alogrithm.functionality { + Some(fn_functionality) => fn_functionality(client)?, + None => panic!("client not have functionality call back"), + }; + + pr_info!("functionality is {} ", functionality); + pr_info!("media ofilm_ox3c i2c driver probe success"); + + Ok(()) + } +} + From e0869e503810f9d5995b0649c28db4ab306695b9 Mon Sep 17 00:00:00 2001 From: happy-thw Date: Tue, 29 Oct 2024 13:41:54 +0800 Subject: [PATCH 14/14] update README.md and add patch for use it in Arceos Signed-off-by: happy-thw --- README.md | 71 +++++++++++ patches/0001-for-arceos.patch | 220 ++++++++++++++++++++++++++++++++++ 2 files changed, 291 insertions(+) create mode 100644 patches/0001-for-arceos.patch diff --git a/README.md b/README.md index 9099ffc..63f41bf 100644 --- a/README.md +++ b/README.md @@ -16,3 +16,74 @@ In reality, hardware manufacturers are more inclined to adapt to mainstream OSs hardware ecosystem of these mainstream OSs is already relatively stable. Therefore, we proposed a second solution. Based on the idea of the first solution, we let OSL follow the framework of a particular OS (Linux). This is the basis of this project. + +## How to use it in ArceOS +In your Arceos directory, clone this project +```shell +cd $(path to arceos)/ +git clone https://github.com/kern-crates/r4l.git +``` + +### Modify Arceos configurations +Modify the Arceos code to ensure that cross-kernel drivers can be used.” + +1. modify modules code to add the cross-kernel drivers framework in Arceos + + If you use the i2c-designware driver, you need to add + + in modules/axdriver/Cargo.toml: + ``` shell + # r4l driver + r4l = { path = "../../r4l/r4l/" , features=["arceos"] } + i2c_designware = { path = "../../r4l/drivers/i2c/busses/i2c-designware" , features=["arceos"] } + ox3c_raw = { path = "../../r4l/drivers/i2c/ox3c-raw" , features=["arceos"] } + ``` + in modules/axdriver/src/lib.rs + ``` shell + extern crate ox3c_raw; + extern crate i2c_designware; + + # in init_drivers function + r4l::init::driver_framework_init(dtb_vaddr); + ``` + For other modifications, please refer to the patch: ./r4l/patches/0001-for-arceos.patch. + or refer to github commit: + + https://github.com/happy-thw/arceos/commit/06b390d264df04ba49c904a00c96ed1ad21a4caf + + https://github.com/happy-thw/arceos/commit/2f7ee3e8d9237256fe2730e483ab9d5d2a24d237 + + +2. modify `handler_table` crate + ``` shell + diff --git a/src/lib.rs b/src/lib.rs + index becd784..4d5aec6 100644 + --- a/src/lib.rs + +++ b/src/lib.rs + @@ -6,7 +6,7 @@ use core::sync::atomic::{AtomicUsize, Ordering}; + /// The type of an event handler. + /// + /// Currently no arguments and return values are supported. + -pub type Handler = fn(); + +pub type Handler = fn(u32); + + /// A lock-free table of event handlers. + /// + @@ -40,7 +40,7 @@ impl HandlerTable { + let handler = self.handlers[idx].load(Ordering::Acquire); + if handler != 0 { + let handler: Handler = unsafe { core::mem::transmute(handler) }; + - handler(); + + handler(idx as u32); + true + } else { + false + ``` +3. if use it in A1000b + - compile + ``` shell + make A=examples/shell PLATFORM=aarch64-bsta1000b LOG=debug SMP=8 FEATURES="driver-ramdisk,multitask,irq" fada + ``` + - replace Img file: + + replace the generated arceos-fada.itb with the A1000b kernel image format. \ No newline at end of file diff --git a/patches/0001-for-arceos.patch b/patches/0001-for-arceos.patch new file mode 100644 index 0000000..ee5271d --- /dev/null +++ b/patches/0001-for-arceos.patch @@ -0,0 +1,220 @@ +From cd0c40c5c3b93730737288fdda82fb30197b3da3 Mon Sep 17 00:00:00 2001 +From: happy-thw +Date: Tue, 29 Oct 2024 11:37:59 +0800 +Subject: [PATCH] in order to use the cross-kernel drivers framework + +--- + modules/axdriver/Cargo.toml | 7 ++++- + modules/axdriver/src/lib.rs | 9 +++++- + modules/axhal/Cargo.toml | 2 +- + modules/axhal/linker.lds.S | 28 +++++++++++++++++++ + .../platform/aarch64_bsta1000b/dw_apb_uart.rs | 2 +- + modules/axmm/src/lib.rs | 12 +++++--- + modules/axruntime/src/lib.rs | 6 ++-- + platforms/aarch64-bsta1000b.toml | 6 ++++ + 8 files changed, 61 insertions(+), 11 deletions(-) + +diff --git a/modules/axdriver/Cargo.toml b/modules/axdriver/Cargo.toml +index 1bc362a..7f9ee9b 100644 +--- a/modules/axdriver/Cargo.toml ++++ b/modules/axdriver/Cargo.toml +@@ -43,4 +43,9 @@ axdriver_virtio = { git = "https://github.com/arceos-org/axdriver_crates.git", t + axalloc = { workspace = true, optional = true } + axhal = { workspace = true, optional = true } + axconfig = { workspace = true, optional = true } +-axdma = { workspace = true, optional = true } +\ No newline at end of file ++axdma = { workspace = true, optional = true } ++ ++# r4l driver ++r4l = { path = "../../r4l/r4l/" , features=["arceos"] } ++i2c_designware = { path = "../../r4l/drivers/i2c/busses/i2c-designware" , features=["arceos"] } ++ox3c_raw = { path = "../../r4l/drivers/i2c/ox3c-raw" , features=["arceos"] } +diff --git a/modules/axdriver/src/lib.rs b/modules/axdriver/src/lib.rs +index 659d95c..1254eec 100644 +--- a/modules/axdriver/src/lib.rs ++++ b/modules/axdriver/src/lib.rs +@@ -67,6 +67,9 @@ extern crate alloc; + #[macro_use] + mod macros; + ++extern crate ox3c_raw; ++extern crate i2c_designware; ++ + mod bus; + mod drivers; + mod dummy; +@@ -83,6 +86,7 @@ pub mod prelude; + #[allow(unused_imports)] + use self::prelude::*; + pub use self::structs::{AxDeviceContainer, AxDeviceEnum}; ++use axhal::mem::phys_to_virt; + + #[cfg(feature = "block")] + pub use self::structs::AxBlockDevice; +@@ -148,10 +152,13 @@ impl AllDevices { + } + + /// Probes and initializes all device drivers, returns the [`AllDevices`] struct. +-pub fn init_drivers() -> AllDevices { ++pub fn init_drivers(dtb: usize) -> AllDevices { + info!("Initialize device drivers..."); + info!(" device model: {}", AllDevices::device_model()); + ++ let dtb_vaddr = phys_to_virt(dtb.into()).as_usize() as *const u8; ++ r4l::init::driver_framework_init(dtb_vaddr); ++ + let mut all_devs = AllDevices::default(); + all_devs.probe(); + +diff --git a/modules/axhal/Cargo.toml b/modules/axhal/Cargo.toml +index 86da2ef..525917c 100644 +--- a/modules/axhal/Cargo.toml ++++ b/modules/axhal/Cargo.toml +@@ -31,7 +31,7 @@ int_ratio = "0.1" + lazyinit = "0.2" + percpu = "0.1" + memory_addr = "0.3" +-handler_table = "0.1" ++handler_table = { path = "../../crates/handler_table/" } + page_table_entry = "0.4" + page_table_multiarch = { version = "0.4", optional = true } + axlog = { workspace = true } +diff --git a/modules/axhal/linker.lds.S b/modules/axhal/linker.lds.S +index 2ec5f00..33b3132 100644 +--- a/modules/axhal/linker.lds.S ++++ b/modules/axhal/linker.lds.S +@@ -32,6 +32,34 @@ SECTIONS + *(.data .data.*) + *(.sdata .sdata.*) + *(.got .got.*) ++ ++ _initcall1 = .; ++ KEEP(*(.initcall1.*)) ++ _initcall1_end =.; ++ ++ _initcall2 = .; ++ KEEP(*(.initcall2.*)) ++ _initcall2_end =.; ++ ++ _initcall3 = .; ++ KEEP(*(.initcall3.*)) ++ _initcall3_end =.; ++ ++ _initcall4 = .; ++ KEEP(*(.initcall4.*)) ++ _initcall4_end =.; ++ ++ _initcall5 = .; ++ KEEP(*(.initcall5.*)) ++ _initcall5_end =.; ++ ++ _initcall6 = .; ++ KEEP(*(.initcall6.*)) ++ _initcall6_end =.; ++ ++ _initcall7 = .; ++ KEEP(*(.initcall7.*)) ++ _initcall7_end =.; + } + + .tdata : ALIGN(0x10) { +diff --git a/modules/axhal/src/platform/aarch64_bsta1000b/dw_apb_uart.rs b/modules/axhal/src/platform/aarch64_bsta1000b/dw_apb_uart.rs +index ef6b327..19bc7a4 100644 +--- a/modules/axhal/src/platform/aarch64_bsta1000b/dw_apb_uart.rs ++++ b/modules/axhal/src/platform/aarch64_bsta1000b/dw_apb_uart.rs +@@ -39,6 +39,6 @@ pub fn init_irq() { + } + + /// UART IRQ Handler +-pub fn handle() { ++pub fn handle(_idx:u32) { + trace!("Uart IRQ Handler"); + } +diff --git a/modules/axmm/src/lib.rs b/modules/axmm/src/lib.rs +index ef579b1..ffb7461 100644 +--- a/modules/axmm/src/lib.rs ++++ b/modules/axmm/src/lib.rs +@@ -11,11 +11,11 @@ mod aspace; + pub use self::aspace::AddrSpace; + + use axerrno::{AxError, AxResult}; +-use axhal::mem::phys_to_virt; ++use axhal::mem::{ phys_to_virt, MemRegionFlags }; + use axhal::paging::PagingError; + use kspin::SpinNoIrq; + use lazyinit::LazyInit; +-use memory_addr::{va, PhysAddr}; ++use memory_addr::{va, PhysAddr, pa}; + + static KERNEL_ASPACE: LazyInit> = LazyInit::new(); + +@@ -56,11 +56,15 @@ pub fn kernel_page_table_root() -> PhysAddr { + /// + /// It mainly sets up the kernel virtual memory address space and recreate a + /// fine-grained kernel page table. +-pub fn init_memory_management() { ++pub fn init_memory_management(dtb: usize) { + info!("Initialize virtual memory management..."); + +- let kernel_aspace = new_kernel_aspace().expect("failed to initialize kernel address space"); ++ let mut kernel_aspace = new_kernel_aspace().expect("failed to initialize kernel address space"); + debug!("kernel address space init OK: {:#x?}", kernel_aspace); ++ ++ let dtb_pa = pa!(dtb); ++ kernel_aspace.map_linear(phys_to_virt(dtb_pa), dtb_pa, 0x10_0000, (MemRegionFlags::RESERVED | MemRegionFlags::READ).into()) ++ .expect("failed to mapping dtb address space"); + KERNEL_ASPACE.init_once(SpinNoIrq::new(kernel_aspace)); + unsafe { axhal::arch::write_page_table_root(kernel_page_table_root()) }; + } +diff --git a/modules/axruntime/src/lib.rs b/modules/axruntime/src/lib.rs +index 8207ea1..b953efc 100644 +--- a/modules/axruntime/src/lib.rs ++++ b/modules/axruntime/src/lib.rs +@@ -145,7 +145,7 @@ pub extern "C" fn rust_main(cpu_id: usize, dtb: usize) -> ! { + init_allocator(); + + #[cfg(feature = "paging")] +- axmm::init_memory_management(); ++ axmm::init_memory_management(dtb); + + info!("Initialize platform devices..."); + axhal::platform_init(); +@@ -156,7 +156,7 @@ pub extern "C" fn rust_main(cpu_id: usize, dtb: usize) -> ! { + #[cfg(any(feature = "fs", feature = "net", feature = "display"))] + { + #[allow(unused_variables)] +- let all_devices = axdriver::init_drivers(); ++ let all_devices = axdriver::init_drivers(dtb); + + #[cfg(feature = "fs")] + axfs::init_filesystems(all_devices.block); +@@ -252,7 +252,7 @@ fn init_interrupt() { + axhal::time::set_oneshot_timer(deadline); + } + +- axhal::irq::register_handler(TIMER_IRQ_NUM, || { ++ axhal::irq::register_handler(TIMER_IRQ_NUM, |_| { + update_timer(); + #[cfg(feature = "multitask")] + axtask::on_timer_tick(); +diff --git a/platforms/aarch64-bsta1000b.toml b/platforms/aarch64-bsta1000b.toml +index 48f66ef..621e3eb 100644 +--- a/platforms/aarch64-bsta1000b.toml ++++ b/platforms/aarch64-bsta1000b.toml +@@ -25,6 +25,12 @@ kernel-aspace-base = "0xffff_0000_0000_0000" + kernel-aspace-size = "0x0000_ffff_ffff_f000" + # MMIO regions with format (`base_paddr`, `size`). + mmio-regions = [ ++ ["0x20000000", "0x1000"], # I2C0 ++ ["0x20001000", "0x1000"], # I2C1 ++ ["0x20002000", "0x1000"], # I2C2 ++ ["0x20003000", "0x1000"], # I2C3 ++ ["0x20004000", "0x1000"], # I2C4 ++ ["0x20005000", "0x1000"], # I2C5 + ["0x20008000", "0x1000"], # uart8250 UART0 + ["0x32000000", "0x8000"], # arm,gic-400 + ["0x32011000", "0x1000"], # CPU CSR +-- +2.25.1 +