init commit
This commit is contained in:
+705
@@ -0,0 +1,705 @@
|
||||
pub mod regs;
|
||||
|
||||
use crate::{
|
||||
PeripheralSelect, enable_peripheral_clock, sealed::Sealed,
|
||||
sysconfig::reset_peripheral_for_cycles, time::Hertz,
|
||||
};
|
||||
use arbitrary_int::{u4, u10, u11, u20};
|
||||
use core::marker::PhantomData;
|
||||
use embedded_hal::i2c::{self, Operation, SevenBitAddress, TenBitAddress};
|
||||
use regs::ClkTimeoutLimit;
|
||||
pub use regs::{Bank, I2cSpeed, RxFifoFullMode, TxFifoEmptyMode};
|
||||
|
||||
#[cfg(feature = "vor1x")]
|
||||
use va108xx as pac;
|
||||
#[cfg(feature = "vor4x")]
|
||||
use va416xx as pac;
|
||||
|
||||
//==================================================================================================
|
||||
// Defintions
|
||||
//==================================================================================================
|
||||
|
||||
const CLK_100K: Hertz = Hertz::from_raw(100_000);
|
||||
const CLK_400K: Hertz = Hertz::from_raw(400_000);
|
||||
const MIN_CLK_400K: Hertz = Hertz::from_raw(8_000_000);
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[error("clock too slow for fast I2C mode")]
|
||||
pub struct ClockTooSlowForFastI2cError;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||
#[error("invalid timing parameters")]
|
||||
pub struct InvalidTimingParamsError;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Error {
|
||||
#[error("arbitration lost")]
|
||||
ArbitrationLost,
|
||||
#[error("nack address")]
|
||||
NackAddr,
|
||||
/// Data not acknowledged in write operation
|
||||
#[error("data not acknowledged in write operation")]
|
||||
NackData,
|
||||
/// Not enough data received in read operation
|
||||
#[error("insufficient data received")]
|
||||
InsufficientDataReceived,
|
||||
/// Number of bytes in transfer too large (larger than 0x7fe)
|
||||
#[error("data too large (larger than 0x7fe)")]
|
||||
DataTooLarge,
|
||||
#[error("clock timeout, SCL was low for {0} clock cycles")]
|
||||
ClockTimeout(u20),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum InitError {
|
||||
/// Wrong address used in constructor
|
||||
#[error("wrong address mode")]
|
||||
WrongAddrMode,
|
||||
/// APB1 clock is too slow for fast I2C mode.
|
||||
#[error("clock too slow for fast I2C mode: {0}")]
|
||||
ClkTooSlow(#[from] ClockTooSlowForFastI2cError),
|
||||
}
|
||||
|
||||
impl embedded_hal::i2c::Error for Error {
|
||||
fn kind(&self) -> embedded_hal::i2c::ErrorKind {
|
||||
match self {
|
||||
Error::ArbitrationLost => embedded_hal::i2c::ErrorKind::ArbitrationLoss,
|
||||
Error::NackAddr => {
|
||||
embedded_hal::i2c::ErrorKind::NoAcknowledge(i2c::NoAcknowledgeSource::Address)
|
||||
}
|
||||
Error::NackData => {
|
||||
embedded_hal::i2c::ErrorKind::NoAcknowledge(i2c::NoAcknowledgeSource::Data)
|
||||
}
|
||||
Error::DataTooLarge | Error::InsufficientDataReceived | Error::ClockTimeout(_) => {
|
||||
embedded_hal::i2c::ErrorKind::Other
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum I2cCmd {
|
||||
Start = 0b01,
|
||||
Stop = 0b10,
|
||||
StartWithStop = 0b11,
|
||||
Cancel = 0b100,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum I2cAddress {
|
||||
Regular(u8),
|
||||
TenBit(u16),
|
||||
}
|
||||
|
||||
impl I2cAddress {
|
||||
pub fn ten_bit_addr(&self) -> bool {
|
||||
match self {
|
||||
I2cAddress::Regular(_) => false,
|
||||
I2cAddress::TenBit(_) => true,
|
||||
}
|
||||
}
|
||||
pub fn raw(&self) -> u16 {
|
||||
match self {
|
||||
I2cAddress::Regular(addr) => *addr as u16,
|
||||
I2cAddress::TenBit(addr) => *addr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Common trait implemented by all PAC peripheral access structures. The register block
|
||||
/// format is the same for all SPI blocks.
|
||||
pub trait I2cMarker: Sealed {
|
||||
const ID: Bank;
|
||||
const PERIPH_SEL: PeripheralSelect;
|
||||
}
|
||||
|
||||
#[cfg(feature = "vor1x")]
|
||||
pub type I2c0 = pac::I2ca;
|
||||
#[cfg(feature = "vor4x")]
|
||||
pub type I2c0 = pac::I2c0;
|
||||
|
||||
impl I2cMarker for I2c0 {
|
||||
const ID: Bank = Bank::I2c0;
|
||||
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c0;
|
||||
}
|
||||
impl Sealed for I2c0 {}
|
||||
|
||||
#[cfg(feature = "vor1x")]
|
||||
pub type I2c1 = pac::I2cb;
|
||||
#[cfg(feature = "vor4x")]
|
||||
pub type I2c1 = pac::I2c1;
|
||||
|
||||
impl I2cMarker for I2c1 {
|
||||
const ID: Bank = Bank::I2c1;
|
||||
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::I2c1;
|
||||
}
|
||||
impl Sealed for I2c1 {}
|
||||
|
||||
//==================================================================================================
|
||||
// Config
|
||||
//==================================================================================================
|
||||
|
||||
fn calc_clk_div_generic(
|
||||
ref_clk: Hertz,
|
||||
speed_mode: I2cSpeed,
|
||||
) -> Result<u8, ClockTooSlowForFastI2cError> {
|
||||
if speed_mode == I2cSpeed::Regular100khz {
|
||||
Ok(((ref_clk.raw() / CLK_100K.raw() / 20) - 1) as u8)
|
||||
} else {
|
||||
if ref_clk.raw() < MIN_CLK_400K.raw() {
|
||||
return Err(ClockTooSlowForFastI2cError);
|
||||
}
|
||||
Ok(((ref_clk.raw() / CLK_400K.raw() / 25) - 1) as u8)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "vor4x")]
|
||||
fn calc_clk_div(
|
||||
clks: &crate::clock::Clocks,
|
||||
speed_mode: I2cSpeed,
|
||||
) -> Result<u8, ClockTooSlowForFastI2cError> {
|
||||
calc_clk_div_generic(clks.apb1(), speed_mode)
|
||||
}
|
||||
|
||||
#[cfg(feature = "vor1x")]
|
||||
fn calc_clk_div(sys_clk: Hertz, speed_mode: I2cSpeed) -> Result<u8, ClockTooSlowForFastI2cError> {
|
||||
calc_clk_div_generic(sys_clk, speed_mode)
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct TimingConfig {
|
||||
pub t_rise: u4,
|
||||
pub t_fall: u4,
|
||||
pub t_high: u4,
|
||||
pub t_low: u4,
|
||||
pub tsu_stop: u4,
|
||||
pub tsu_start: u4,
|
||||
pub thd_start: u4,
|
||||
pub t_buf: u4,
|
||||
}
|
||||
|
||||
/// Default configuration are the register reset value which are used by default.
|
||||
impl Default for TimingConfig {
|
||||
fn default() -> Self {
|
||||
TimingConfig {
|
||||
t_rise: u4::new(0b0010),
|
||||
t_fall: u4::new(0b0001),
|
||||
t_high: u4::new(0b1000),
|
||||
t_low: u4::new(0b1001),
|
||||
tsu_stop: u4::new(0b1000),
|
||||
tsu_start: u4::new(0b1010),
|
||||
thd_start: u4::new(0b1000),
|
||||
t_buf: u4::new(0b1010),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct MasterConfig {
|
||||
pub tx_empty_mode: TxFifoEmptyMode,
|
||||
pub rx_full_mode: RxFifoFullMode,
|
||||
/// Enable the analog delay glitch filter
|
||||
pub alg_filt: bool,
|
||||
/// Enable the digital glitch filter
|
||||
pub dlg_filt: bool,
|
||||
pub timing_config: Option<TimingConfig>,
|
||||
/// See [I2cMaster::set_clock_low_timeout] documentation.
|
||||
pub timeout: Option<u20>,
|
||||
// Loopback mode
|
||||
// lbm: bool,
|
||||
}
|
||||
|
||||
impl Default for MasterConfig {
|
||||
fn default() -> Self {
|
||||
MasterConfig {
|
||||
tx_empty_mode: TxFifoEmptyMode::Stall,
|
||||
rx_full_mode: RxFifoFullMode::Stall,
|
||||
alg_filt: false,
|
||||
dlg_filt: false,
|
||||
timeout: None,
|
||||
timing_config: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sealed for MasterConfig {}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum WriteCompletionCondition {
|
||||
Idle,
|
||||
Waiting,
|
||||
}
|
||||
|
||||
struct TimeoutGuard {
|
||||
clk_timeout_enabled: bool,
|
||||
regs: regs::MmioI2c<'static>,
|
||||
}
|
||||
|
||||
impl TimeoutGuard {
|
||||
fn new(regs: ®s::MmioI2c<'static>) -> Self {
|
||||
let clk_timeout_enabled = regs.read_clk_timeout_limit().value().value() > 0;
|
||||
let mut guard = TimeoutGuard {
|
||||
clk_timeout_enabled,
|
||||
regs: unsafe { regs.clone() },
|
||||
};
|
||||
if clk_timeout_enabled {
|
||||
// Clear any interrupts which might be pending.
|
||||
guard.regs.write_irq_clear(
|
||||
regs::InterruptClear::builder()
|
||||
.with_clock_timeout(true)
|
||||
.with_tx_overflow(false)
|
||||
.with_rx_overflow(false)
|
||||
.build(),
|
||||
);
|
||||
guard.regs.modify_irq_enb(|mut value| {
|
||||
value.set_clock_timeout(true);
|
||||
value
|
||||
});
|
||||
}
|
||||
guard
|
||||
}
|
||||
|
||||
fn timeout_enabled(&self) -> bool {
|
||||
self.clk_timeout_enabled
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TimeoutGuard {
|
||||
fn drop(&mut self) {
|
||||
if self.clk_timeout_enabled {
|
||||
self.regs.modify_irq_enb(|mut value| {
|
||||
value.set_clock_timeout(false);
|
||||
value
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
//==================================================================================================
|
||||
// I2C Master
|
||||
//==================================================================================================
|
||||
|
||||
pub struct I2cMaster<Addr = SevenBitAddress> {
|
||||
id: Bank,
|
||||
regs: regs::MmioI2c<'static>,
|
||||
addr: PhantomData<Addr>,
|
||||
}
|
||||
|
||||
impl<Addr> I2cMaster<Addr> {
|
||||
pub fn new<I2c: I2cMarker>(
|
||||
_i2c: I2c,
|
||||
#[cfg(feature = "vor1x")] sysclk: Hertz,
|
||||
#[cfg(feature = "vor4x")] clks: &crate::clock::Clocks,
|
||||
cfg: MasterConfig,
|
||||
speed_mode: I2cSpeed,
|
||||
) -> Result<Self, ClockTooSlowForFastI2cError> {
|
||||
reset_peripheral_for_cycles(I2c::PERIPH_SEL, 2);
|
||||
enable_peripheral_clock(I2c::PERIPH_SEL);
|
||||
let mut regs = regs::I2c::new_mmio(I2c::ID);
|
||||
#[cfg(feature = "vor1x")]
|
||||
let clk_div = calc_clk_div(sysclk, speed_mode)?;
|
||||
#[cfg(feature = "vor4x")]
|
||||
let clk_div = calc_clk_div(clks, speed_mode)?;
|
||||
regs.write_clkscale(
|
||||
regs::ClkScale::builder()
|
||||
.with_div(clk_div)
|
||||
.with_fastmode(speed_mode)
|
||||
.build(),
|
||||
);
|
||||
regs.modify_control(|mut value| {
|
||||
value.set_tx_fifo_empty_mode(cfg.tx_empty_mode);
|
||||
value.set_rx_fifo_full_mode(cfg.rx_full_mode);
|
||||
value.set_analog_filter(cfg.alg_filt);
|
||||
value.set_digital_filter(cfg.dlg_filt);
|
||||
value
|
||||
});
|
||||
|
||||
if let Some(ref timing_cfg) = cfg.timing_config {
|
||||
regs.modify_control(|mut value| {
|
||||
value.set_enable_timing_config(true);
|
||||
value
|
||||
});
|
||||
regs.write_timing_config(
|
||||
regs::TimingConfig::builder()
|
||||
.with_t_rise(timing_cfg.t_rise)
|
||||
.with_t_fall(timing_cfg.t_fall)
|
||||
.with_t_high(timing_cfg.t_high)
|
||||
.with_t_low(timing_cfg.t_low)
|
||||
.with_tsu_stop(timing_cfg.tsu_stop)
|
||||
.with_tsu_start(timing_cfg.tsu_start)
|
||||
.with_thd_start(timing_cfg.thd_start)
|
||||
.with_t_buf(timing_cfg.t_buf)
|
||||
.build(),
|
||||
);
|
||||
}
|
||||
regs.write_fifo_clear(
|
||||
regs::FifoClear::builder()
|
||||
.with_tx_fifo(true)
|
||||
.with_rx_fifo(true)
|
||||
.build(),
|
||||
);
|
||||
if let Some(timeout) = cfg.timeout {
|
||||
regs.write_clk_timeout_limit(ClkTimeoutLimit::new(timeout));
|
||||
}
|
||||
let mut i2c_master = I2cMaster {
|
||||
addr: PhantomData,
|
||||
id: I2c::ID,
|
||||
regs,
|
||||
};
|
||||
i2c_master.enable();
|
||||
Ok(i2c_master)
|
||||
}
|
||||
|
||||
pub const fn id(&self) -> Bank {
|
||||
self.id
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn perid(&self) -> u32 {
|
||||
self.regs.read_perid()
|
||||
}
|
||||
|
||||
/// Configures the clock scale for a given speed mode setting
|
||||
pub fn set_clk_scale(
|
||||
&mut self,
|
||||
#[cfg(feature = "vor1x")] sys_clk: Hertz,
|
||||
#[cfg(feature = "vor4x")] clks: &crate::clock::Clocks,
|
||||
speed_mode: I2cSpeed,
|
||||
) -> Result<(), ClockTooSlowForFastI2cError> {
|
||||
self.disable();
|
||||
#[cfg(feature = "vor1x")]
|
||||
let clk_div = calc_clk_div(sys_clk, speed_mode)?;
|
||||
#[cfg(feature = "vor4x")]
|
||||
let clk_div = calc_clk_div(clks, speed_mode)?;
|
||||
self.regs.write_clkscale(
|
||||
regs::ClkScale::builder()
|
||||
.with_div(clk_div)
|
||||
.with_fastmode(speed_mode)
|
||||
.build(),
|
||||
);
|
||||
self.enable();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn cancel_transfer(&mut self) {
|
||||
self.regs.write_cmd(
|
||||
regs::Command::builder()
|
||||
.with_start(false)
|
||||
.with_stop(false)
|
||||
.with_cancel(true)
|
||||
.build(),
|
||||
);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_tx_fifo(&mut self) {
|
||||
self.regs.write_fifo_clear(
|
||||
regs::FifoClear::builder()
|
||||
.with_tx_fifo(true)
|
||||
.with_rx_fifo(false)
|
||||
.build(),
|
||||
);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_rx_fifo(&mut self) {
|
||||
self.regs.write_fifo_clear(
|
||||
regs::FifoClear::builder()
|
||||
.with_tx_fifo(false)
|
||||
.with_rx_fifo(true)
|
||||
.build(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Configure a timeout limit on the amount of time the I2C clock is seen to be low.
|
||||
/// The timeout is specified as I2C clock cycles.
|
||||
///
|
||||
/// If the timeout is enabled, the blocking transaction handlers provided by the [I2cMaster]
|
||||
/// will poll the interrupt status register to check for timeouts. This can be used to avoid
|
||||
/// hang-ups of the I2C bus.
|
||||
#[inline]
|
||||
pub fn set_clock_low_timeout(&mut self, clock_cycles: u20) {
|
||||
self.regs
|
||||
.write_clk_timeout_limit(ClkTimeoutLimit::new(clock_cycles));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn disable_clock_low_timeout(&mut self) {
|
||||
self.regs
|
||||
.write_clk_timeout_limit(ClkTimeoutLimit::new(u20::new(0)));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn enable(&mut self) {
|
||||
self.regs.modify_control(|mut value| {
|
||||
value.set_enable(true);
|
||||
value
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn disable(&mut self) {
|
||||
self.regs.modify_control(|mut value| {
|
||||
value.set_enable(false);
|
||||
value
|
||||
});
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_fifo_unchecked(&mut self, word: u8) {
|
||||
self.regs.write_data(regs::Data::new(word));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn read_fifo_unchecked(&self) -> u8 {
|
||||
self.regs.read_data().data()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_status(&mut self) -> regs::Status {
|
||||
self.regs.read_status()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn write_command(&mut self, cmd: I2cCmd) {
|
||||
self.regs
|
||||
.write_cmd(regs::Command::new_with_raw_value(cmd as u32));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn write_address(&mut self, addr: I2cAddress, dir: regs::Direction) {
|
||||
self.regs.write_address(
|
||||
regs::Address::builder()
|
||||
.with_direction(dir)
|
||||
.with_address(u10::new(addr.raw()))
|
||||
.with_a10_mode(addr.ten_bit_addr())
|
||||
.build(),
|
||||
);
|
||||
}
|
||||
|
||||
fn error_handler_write(&mut self, init_cmd: I2cCmd) {
|
||||
if init_cmd == I2cCmd::Start {
|
||||
self.write_command(I2cCmd::Stop);
|
||||
}
|
||||
// The other case is start with stop where, so a CANCEL command should not be necessary
|
||||
// because the hardware takes care of it.
|
||||
self.clear_tx_fifo();
|
||||
}
|
||||
|
||||
/// Blocking write transaction on the I2C bus.
|
||||
pub fn write_blocking(&mut self, addr: I2cAddress, output: &[u8]) -> Result<(), Error> {
|
||||
self.write_blocking_generic(
|
||||
I2cCmd::StartWithStop,
|
||||
addr,
|
||||
output,
|
||||
WriteCompletionCondition::Idle,
|
||||
)
|
||||
}
|
||||
|
||||
/// Blocking read transaction on the I2C bus.
|
||||
pub fn read_blocking(&mut self, addr: I2cAddress, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
let len = buffer.len();
|
||||
if len > 0x7fe {
|
||||
return Err(Error::DataTooLarge);
|
||||
}
|
||||
// Clear the receive FIFO
|
||||
self.clear_rx_fifo();
|
||||
|
||||
let timeout_guard = TimeoutGuard::new(&self.regs);
|
||||
|
||||
// Load number of words
|
||||
self.regs
|
||||
.write_words(regs::Words::new(u11::new(len as u16)));
|
||||
// Load address
|
||||
self.write_address(addr, regs::Direction::Receive);
|
||||
|
||||
let mut buf_iter = buffer.iter_mut();
|
||||
let mut read_bytes = 0;
|
||||
// Start receive transfer
|
||||
self.write_command(I2cCmd::StartWithStop);
|
||||
loop {
|
||||
let status = self.read_status();
|
||||
if status.arb_lost() {
|
||||
self.clear_rx_fifo();
|
||||
return Err(Error::ArbitrationLost);
|
||||
}
|
||||
if status.nack_addr() {
|
||||
self.clear_rx_fifo();
|
||||
return Err(Error::NackAddr);
|
||||
}
|
||||
if status.idle() {
|
||||
if read_bytes != len {
|
||||
return Err(Error::InsufficientDataReceived);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
if timeout_guard.timeout_enabled() && self.regs.read_irq_status().clock_timeout() {
|
||||
return Err(Error::ClockTimeout(
|
||||
self.regs.read_clk_timeout_limit().value(),
|
||||
));
|
||||
}
|
||||
if status.rx_not_empty() {
|
||||
if let Some(next_byte) = buf_iter.next() {
|
||||
*next_byte = self.read_fifo_unchecked();
|
||||
}
|
||||
read_bytes += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write_blocking_generic(
|
||||
&mut self,
|
||||
init_cmd: I2cCmd,
|
||||
addr: I2cAddress,
|
||||
output: &[u8],
|
||||
end_condition: WriteCompletionCondition,
|
||||
) -> Result<(), Error> {
|
||||
let len = output.len();
|
||||
if len > 0x7fe {
|
||||
return Err(Error::DataTooLarge);
|
||||
}
|
||||
// Clear the send FIFO
|
||||
self.clear_tx_fifo();
|
||||
|
||||
let timeout_guard = TimeoutGuard::new(&self.regs);
|
||||
|
||||
// Load number of words
|
||||
self.regs
|
||||
.write_words(regs::Words::new(u11::new(len as u16)));
|
||||
let mut bytes = output.iter();
|
||||
// FIFO has a depth of 16. We load slightly above the trigger level
|
||||
// but not all of it because the transaction might fail immediately
|
||||
const FILL_DEPTH: usize = 12;
|
||||
|
||||
let mut current_index = core::cmp::min(FILL_DEPTH, len);
|
||||
// load the FIFO
|
||||
for _ in 0..current_index {
|
||||
self.write_fifo_unchecked(*bytes.next().unwrap());
|
||||
}
|
||||
self.write_address(addr, regs::Direction::Send);
|
||||
self.write_command(init_cmd);
|
||||
loop {
|
||||
let status = self.regs.read_status();
|
||||
if status.arb_lost() {
|
||||
self.error_handler_write(init_cmd);
|
||||
return Err(Error::ArbitrationLost);
|
||||
}
|
||||
if status.nack_addr() {
|
||||
self.error_handler_write(init_cmd);
|
||||
return Err(Error::NackAddr);
|
||||
}
|
||||
if status.nack_data() {
|
||||
self.error_handler_write(init_cmd);
|
||||
return Err(Error::NackData);
|
||||
}
|
||||
match end_condition {
|
||||
WriteCompletionCondition::Idle => {
|
||||
if status.idle() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
WriteCompletionCondition::Waiting => {
|
||||
if status.waiting() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
if timeout_guard.timeout_enabled() && self.regs.read_irq_status().clock_timeout() {
|
||||
return Err(Error::ClockTimeout(
|
||||
self.regs.read_clk_timeout_limit().value(),
|
||||
));
|
||||
}
|
||||
if status.tx_not_full() && current_index < len {
|
||||
self.write_fifo_unchecked(output[current_index]);
|
||||
current_index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Blocking write-read transaction on the I2C bus.
|
||||
pub fn write_read_blocking(
|
||||
&mut self,
|
||||
address: I2cAddress,
|
||||
write: &[u8],
|
||||
read: &mut [u8],
|
||||
) -> Result<(), Error> {
|
||||
self.write_blocking_generic(
|
||||
I2cCmd::Start,
|
||||
address,
|
||||
write,
|
||||
WriteCompletionCondition::Waiting,
|
||||
)?;
|
||||
self.read_blocking(address, read)
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================================
|
||||
// Embedded HAL I2C implementations
|
||||
//======================================================================================
|
||||
|
||||
impl embedded_hal::i2c::ErrorType for I2cMaster<SevenBitAddress> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl embedded_hal::i2c::I2c for I2cMaster<SevenBitAddress> {
|
||||
fn transaction(
|
||||
&mut self,
|
||||
address: SevenBitAddress,
|
||||
operations: &mut [Operation<'_>],
|
||||
) -> Result<(), Self::Error> {
|
||||
for operation in operations {
|
||||
match operation {
|
||||
Operation::Read(buf) => self.read_blocking(I2cAddress::Regular(address), buf)?,
|
||||
Operation::Write(buf) => self.write_blocking(I2cAddress::Regular(address), buf)?,
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_read(
|
||||
&mut self,
|
||||
address: u8,
|
||||
write: &[u8],
|
||||
read: &mut [u8],
|
||||
) -> Result<(), Self::Error> {
|
||||
let addr = I2cAddress::Regular(address);
|
||||
self.write_read_blocking(addr, write, read)
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_hal::i2c::ErrorType for I2cMaster<TenBitAddress> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl embedded_hal::i2c::I2c<TenBitAddress> for I2cMaster<TenBitAddress> {
|
||||
fn transaction(
|
||||
&mut self,
|
||||
address: TenBitAddress,
|
||||
operations: &mut [Operation<'_>],
|
||||
) -> Result<(), Self::Error> {
|
||||
for operation in operations {
|
||||
match operation {
|
||||
Operation::Read(buf) => self.read_blocking(I2cAddress::TenBit(address), buf)?,
|
||||
Operation::Write(buf) => self.write_blocking(I2cAddress::TenBit(address), buf)?,
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_read(
|
||||
&mut self,
|
||||
address: TenBitAddress,
|
||||
write: &[u8],
|
||||
read: &mut [u8],
|
||||
) -> Result<(), Self::Error> {
|
||||
let addr = I2cAddress::TenBit(address);
|
||||
self.write_read_blocking(addr, write, read)
|
||||
}
|
||||
}
|
||||
+671
@@ -0,0 +1,671 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use arbitrary_int::{u4, u5, u9, u10, u11, u20};
|
||||
|
||||
pub use crate::shared::{FifoClear, TriggerLevel};
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "vor1x")] {
|
||||
/// I2C A base address
|
||||
pub const BASE_ADDR_0: usize = 0x4006_0000;
|
||||
/// I2C B base address
|
||||
pub const BASE_ADDR_1: usize = 0x4006_1000;
|
||||
} else if #[cfg(feature = "vor4x")] {
|
||||
/// I2C 0 base address
|
||||
pub const BASE_ADDR_0: usize = 0x4001_6000;
|
||||
/// I2C 1 base address
|
||||
pub const BASE_ADDR_1: usize = 0x4001_6400;
|
||||
/// I2C 2 base address
|
||||
pub const BASE_ADDR_2: usize = 0x4001_6800;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Bank {
|
||||
I2c0 = 0,
|
||||
I2c1 = 1,
|
||||
#[cfg(feature = "vor4x")]
|
||||
I2c2 = 2,
|
||||
}
|
||||
|
||||
impl Bank {
|
||||
/// Unsafely steal the I2C peripheral block for the given port.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Circumvents ownership and safety guarantees by the HAL.
|
||||
pub unsafe fn steal_regs(&self) -> MmioI2c<'static> {
|
||||
I2c::new_mmio(*self)
|
||||
}
|
||||
}
|
||||
|
||||
#[bitbybit::bitenum(u1, exhaustive = true)]
|
||||
#[derive(Default, Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TxFifoEmptyMode {
|
||||
/// I2C clock is stretched until data is available.
|
||||
#[default]
|
||||
Stall = 0,
|
||||
EndTransaction = 1,
|
||||
}
|
||||
|
||||
#[bitbybit::bitenum(u1, exhaustive = true)]
|
||||
#[derive(Default, Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum RxFifoFullMode {
|
||||
/// I2C clock is stretched until data is available.
|
||||
#[default]
|
||||
Stall = 0,
|
||||
Nack = 1,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32)]
|
||||
#[derive(Debug)]
|
||||
pub struct Control {
|
||||
#[bit(0, r)]
|
||||
clk_enabled: bool,
|
||||
#[bit(1, r)]
|
||||
enabled: bool,
|
||||
#[bit(2, rw)]
|
||||
enable: bool,
|
||||
#[bit(3, rw)]
|
||||
tx_fifo_empty_mode: TxFifoEmptyMode,
|
||||
#[bit(4, rw)]
|
||||
rx_fifo_full_mode: RxFifoFullMode,
|
||||
/// Enables the analog delay glitch filter.
|
||||
#[bit(5, rw)]
|
||||
analog_filter: bool,
|
||||
/// Enables the digital glitch filter.
|
||||
#[bit(6, rw)]
|
||||
digital_filter: bool,
|
||||
#[bit(8, rw)]
|
||||
loopback: bool,
|
||||
#[bit(9, rw)]
|
||||
enable_timing_config: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[bitbybit::bitenum(u1, exhaustive = true)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum I2cSpeed {
|
||||
Regular100khz = 0,
|
||||
Fast400khz = 1,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32, default = 0x0)]
|
||||
#[derive(Debug)]
|
||||
pub struct ClkScale {
|
||||
/// Clock divide value. Reset value: 0x18.
|
||||
#[bits(0..=7, rw)]
|
||||
div: u8,
|
||||
#[bit(31, rw)]
|
||||
fastmode: I2cSpeed,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Words(arbitrary_int::UInt<u32, 11>);
|
||||
|
||||
impl Words {
|
||||
pub const fn new(value: u11) -> Self {
|
||||
Words(arbitrary_int::UInt::<u32, 11>::new(value.value() as u32))
|
||||
}
|
||||
pub const fn value(&self) -> u11 {
|
||||
u11::new(self.0.value() as u16)
|
||||
}
|
||||
}
|
||||
|
||||
#[bitbybit::bitenum(u1, exhaustive = true)]
|
||||
#[derive(Default, Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Direction {
|
||||
#[default]
|
||||
Send = 0,
|
||||
Receive = 1,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32, default = 0x0)]
|
||||
#[derive(Debug)]
|
||||
pub struct Address {
|
||||
#[bit(0, rw)]
|
||||
direction: Direction,
|
||||
#[bits(1..=10, rw)]
|
||||
address: u10,
|
||||
/// Enables 10-bit addressing mode.
|
||||
#[bit(15, rw)]
|
||||
a10_mode: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub struct Data(arbitrary_int::UInt<u32, 8>);
|
||||
|
||||
impl Data {
|
||||
pub const fn new(value: u8) -> Self {
|
||||
Data(arbitrary_int::UInt::<u32, 8>::new(value as u32))
|
||||
}
|
||||
|
||||
pub const fn data(&self) -> u8 {
|
||||
self.0.value() as u8
|
||||
}
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32, default = 0x0)]
|
||||
#[derive(Debug)]
|
||||
pub struct Command {
|
||||
#[bit(0, w)]
|
||||
start: bool,
|
||||
#[bit(1, w)]
|
||||
stop: bool,
|
||||
#[bit(2, w)]
|
||||
cancel: bool,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32)]
|
||||
#[derive(Debug)]
|
||||
pub struct Status {
|
||||
#[bit(0, r)]
|
||||
i2c_idle: bool,
|
||||
#[bit(1, r)]
|
||||
idle: bool,
|
||||
#[bit(2, r)]
|
||||
waiting: bool,
|
||||
#[bit(3, r)]
|
||||
stalled: bool,
|
||||
#[bit(4, r)]
|
||||
arb_lost: bool,
|
||||
#[bit(5, r)]
|
||||
nack_addr: bool,
|
||||
#[bit(6, r)]
|
||||
nack_data: bool,
|
||||
#[bit(8, r)]
|
||||
rx_not_empty: bool,
|
||||
#[bit(9, r)]
|
||||
rx_full: bool,
|
||||
#[bit(11, r)]
|
||||
rx_trigger: bool,
|
||||
#[bit(12, r)]
|
||||
tx_empty: bool,
|
||||
#[bit(13, r)]
|
||||
tx_not_full: bool,
|
||||
#[bit(15, r)]
|
||||
tx_trigger: bool,
|
||||
#[bit(30, r)]
|
||||
raw_sda: bool,
|
||||
#[bit(31, r)]
|
||||
raw_scl: bool,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32)]
|
||||
#[derive(Debug)]
|
||||
pub struct State {
|
||||
#[bits(0..=3, rw)]
|
||||
state: u4,
|
||||
#[bits(4..=7, rw)]
|
||||
step: u4,
|
||||
#[bits(8..=12, rw)]
|
||||
rx_fifo: u5,
|
||||
#[bits(14..=18, rw)]
|
||||
tx_fifo: u5,
|
||||
#[bits(20..=28, rw)]
|
||||
bitstate: u9,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct DataCount(arbitrary_int::UInt<u32, 11>);
|
||||
|
||||
#[bitbybit::bitfield(u32)]
|
||||
#[derive(Debug)]
|
||||
pub struct InterruptControl {
|
||||
#[bit(0, rw)]
|
||||
i2c_idle: bool,
|
||||
#[bit(1, rw)]
|
||||
idle: bool,
|
||||
#[bit(2, rw)]
|
||||
waiting: bool,
|
||||
#[bit(3, rw)]
|
||||
stalled: bool,
|
||||
#[bit(4, rw)]
|
||||
arb_lost: bool,
|
||||
#[bit(5, rw)]
|
||||
nack_addr: bool,
|
||||
#[bit(6, rw)]
|
||||
nack_data: bool,
|
||||
#[bit(7, rw)]
|
||||
clock_timeout: bool,
|
||||
#[bit(10, rw)]
|
||||
tx_overflow: bool,
|
||||
#[bit(11, rw)]
|
||||
rx_overflow: bool,
|
||||
#[bit(12, rw)]
|
||||
tx_ready: bool,
|
||||
#[bit(13, rw)]
|
||||
rx_ready: bool,
|
||||
#[bit(14, rw)]
|
||||
tx_empty: bool,
|
||||
#[bit(15, rw)]
|
||||
rx_full: bool,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32)]
|
||||
#[derive(Debug)]
|
||||
pub struct InterruptStatus {
|
||||
#[bit(0, r)]
|
||||
i2c_idle: bool,
|
||||
#[bit(1, r)]
|
||||
idle: bool,
|
||||
#[bit(2, r)]
|
||||
waiting: bool,
|
||||
#[bit(3, r)]
|
||||
stalled: bool,
|
||||
#[bit(4, r)]
|
||||
arb_lost: bool,
|
||||
#[bit(5, r)]
|
||||
nack_addr: bool,
|
||||
#[bit(6, r)]
|
||||
nack_data: bool,
|
||||
#[bit(7, r)]
|
||||
clock_timeout: bool,
|
||||
#[bit(10, r)]
|
||||
tx_overflow: bool,
|
||||
#[bit(11, r)]
|
||||
rx_overflow: bool,
|
||||
#[bit(12, r)]
|
||||
tx_ready: bool,
|
||||
#[bit(13, r)]
|
||||
rx_ready: bool,
|
||||
#[bit(14, r)]
|
||||
tx_empty: bool,
|
||||
#[bit(15, r)]
|
||||
rx_full: bool,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32, default = 0x0)]
|
||||
#[derive(Debug)]
|
||||
pub struct InterruptClear {
|
||||
#[bit(7, w)]
|
||||
clock_timeout: bool,
|
||||
#[bit(10, w)]
|
||||
tx_overflow: bool,
|
||||
#[bit(11, w)]
|
||||
rx_overflow: bool,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32)]
|
||||
#[derive(Debug)]
|
||||
pub struct TimingConfig {
|
||||
/// Rise time.
|
||||
#[bits(0..=3, rw)]
|
||||
t_rise: u4,
|
||||
/// Fall time.
|
||||
#[bits(4..=7, rw)]
|
||||
t_fall: u4,
|
||||
/// Duty cycle high time of SCL.
|
||||
#[bits(8..=11, rw)]
|
||||
t_high: u4,
|
||||
/// Duty cycle low time of SCL.
|
||||
#[bits(12..=15, rw)]
|
||||
t_low: u4,
|
||||
/// Setup time for STOP.
|
||||
#[bits(16..=19, rw)]
|
||||
tsu_stop: u4,
|
||||
/// Setup time for START.
|
||||
#[bits(20..=23, rw)]
|
||||
tsu_start: u4,
|
||||
/// Data hold time.
|
||||
#[bits(24..=27, rw)]
|
||||
thd_start: u4,
|
||||
/// TBus free time between STOP and START.
|
||||
#[bits(28..=31, rw)]
|
||||
t_buf: u4,
|
||||
}
|
||||
|
||||
pub struct ClkTimeoutLimit(pub arbitrary_int::UInt<u32, 20>);
|
||||
|
||||
impl ClkTimeoutLimit {
|
||||
pub fn new(value: u20) -> Self {
|
||||
ClkTimeoutLimit(arbitrary_int::UInt::<u32, 20>::new(value.value()))
|
||||
}
|
||||
pub fn value(&self) -> u20 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub mod slave {
|
||||
use super::{Data, DataCount, FifoClear, RxFifoFullMode, TriggerLevel, TxFifoEmptyMode};
|
||||
use arbitrary_int::{u3, u4, u5, u10, u11};
|
||||
|
||||
#[bitbybit::bitfield(u32)]
|
||||
#[derive(Debug)]
|
||||
pub struct Control {
|
||||
#[bit(0, r)]
|
||||
clk_enabled: bool,
|
||||
#[bit(1, r)]
|
||||
enabled: bool,
|
||||
#[bit(2, rw)]
|
||||
enable: bool,
|
||||
#[bit(3, rw)]
|
||||
tx_fifo_empty_mode: TxFifoEmptyMode,
|
||||
#[bit(4, rw)]
|
||||
rx_fifo_full_mode: RxFifoFullMode,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32)]
|
||||
#[derive(Debug)]
|
||||
pub struct Maxwords {
|
||||
#[bits(0..=10, rw)]
|
||||
maxwords: u11,
|
||||
#[bit(31, rw)]
|
||||
enable: bool,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32)]
|
||||
#[derive(Debug)]
|
||||
pub struct Address {
|
||||
#[bit(0, rw)]
|
||||
rw: bool,
|
||||
#[bits(1..=10, rw)]
|
||||
address: u10,
|
||||
#[bit(15, rw)]
|
||||
a10_mode: bool,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32)]
|
||||
#[derive(Debug)]
|
||||
pub struct AddressMask {
|
||||
/// Will normally be 0 to match both read and write addresses.
|
||||
#[bit(0, rw)]
|
||||
rw_mask: bool,
|
||||
/// Reset value 0x3FF.
|
||||
#[bits(1..=10, rw)]
|
||||
mask: u10,
|
||||
}
|
||||
|
||||
#[bitbybit::bitenum(u1, exhaustive = true)]
|
||||
#[derive(Default, Debug, PartialEq, Eq)]
|
||||
pub enum Direction {
|
||||
#[default]
|
||||
MasterSend = 0,
|
||||
MasterReceive = 1,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32)]
|
||||
#[derive(Debug)]
|
||||
pub struct LastAddress {
|
||||
#[bit(0, rw)]
|
||||
direction: Direction,
|
||||
#[bits(1..=10, rw)]
|
||||
address: u10,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32)]
|
||||
#[derive(Debug)]
|
||||
pub struct Status {
|
||||
#[bit(0, r)]
|
||||
completed: bool,
|
||||
#[bit(1, r)]
|
||||
idle: bool,
|
||||
#[bit(2, r)]
|
||||
waiting: bool,
|
||||
#[bit(3, r)]
|
||||
tx_stalled: bool,
|
||||
#[bit(4, r)]
|
||||
rx_stalled: bool,
|
||||
#[bit(5, r)]
|
||||
address_match: bool,
|
||||
#[bit(6, r)]
|
||||
nack_data: bool,
|
||||
#[bit(7, r)]
|
||||
rx_data_first: bool,
|
||||
#[bit(8, r)]
|
||||
rx_not_empty: bool,
|
||||
#[bit(9, r)]
|
||||
rx_full: bool,
|
||||
#[bit(11, r)]
|
||||
rx_trigger: bool,
|
||||
#[bit(12, r)]
|
||||
tx_empty: bool,
|
||||
#[bit(13, r)]
|
||||
tx_not_full: bool,
|
||||
#[bit(15, r)]
|
||||
tx_trigger: bool,
|
||||
#[bit(28, r)]
|
||||
raw_busy: bool,
|
||||
#[bit(30, r)]
|
||||
raw_sda: bool,
|
||||
#[bit(31, r)]
|
||||
raw_scl: bool,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32)]
|
||||
#[derive(Debug)]
|
||||
pub struct State {
|
||||
#[bits(0..=2, rw)]
|
||||
state: u3,
|
||||
#[bits(4..=7, rw)]
|
||||
step: u4,
|
||||
#[bits(8..=12, rw)]
|
||||
rx_fifo: u5,
|
||||
#[bits(14..=18, rw)]
|
||||
tx_fifo: u5,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32)]
|
||||
#[derive(Debug)]
|
||||
pub struct InterruptControl {
|
||||
#[bit(0, rw)]
|
||||
completed: bool,
|
||||
#[bit(1, rw)]
|
||||
idle: bool,
|
||||
#[bit(2, rw)]
|
||||
waiting: bool,
|
||||
#[bit(3, rw)]
|
||||
tx_stalled: bool,
|
||||
#[bit(4, rw)]
|
||||
rx_stalled: bool,
|
||||
#[bit(5, rw)]
|
||||
address_match: bool,
|
||||
#[bit(6, rw)]
|
||||
nack_data: bool,
|
||||
#[bit(7, rw)]
|
||||
rx_data_first: bool,
|
||||
|
||||
#[bit(8, rw)]
|
||||
i2c_start: bool,
|
||||
#[bit(9, rw)]
|
||||
i2c_stop: bool,
|
||||
#[bit(10, rw)]
|
||||
tx_underflow: bool,
|
||||
#[bit(11, rw)]
|
||||
rx_underflow: bool,
|
||||
#[bit(12, rw)]
|
||||
tx_ready: bool,
|
||||
#[bit(13, rw)]
|
||||
rx_ready: bool,
|
||||
#[bit(14, rw)]
|
||||
tx_empty: bool,
|
||||
#[bit(15, rw)]
|
||||
rx_full: bool,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32)]
|
||||
#[derive(Debug)]
|
||||
pub struct InterruptStatus {
|
||||
#[bit(0, r)]
|
||||
completed: bool,
|
||||
#[bit(1, r)]
|
||||
idle: bool,
|
||||
#[bit(2, r)]
|
||||
waiting: bool,
|
||||
#[bit(3, r)]
|
||||
tx_stalled: bool,
|
||||
#[bit(4, r)]
|
||||
rx_stalled: bool,
|
||||
#[bit(5, r)]
|
||||
address_match: bool,
|
||||
#[bit(6, r)]
|
||||
nack_data: bool,
|
||||
#[bit(7, r)]
|
||||
rx_data_first: bool,
|
||||
|
||||
#[bit(8, r)]
|
||||
i2c_start: bool,
|
||||
#[bit(9, r)]
|
||||
i2c_stop: bool,
|
||||
#[bit(10, r)]
|
||||
tx_underflow: bool,
|
||||
#[bit(11, r)]
|
||||
rx_underflow: bool,
|
||||
#[bit(12, r)]
|
||||
tx_ready: bool,
|
||||
#[bit(13, r)]
|
||||
rx_ready: bool,
|
||||
#[bit(14, r)]
|
||||
tx_empty: bool,
|
||||
#[bit(15, r)]
|
||||
rx_full: bool,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32, default = 0x0)]
|
||||
#[derive(Debug)]
|
||||
pub struct InterruptClear {
|
||||
#[bit(0, w)]
|
||||
completed: bool,
|
||||
#[bit(1, w)]
|
||||
idle: bool,
|
||||
#[bit(2, w)]
|
||||
waiting: bool,
|
||||
#[bit(3, w)]
|
||||
tx_stalled: bool,
|
||||
#[bit(4, w)]
|
||||
rx_stalled: bool,
|
||||
#[bit(5, w)]
|
||||
address_match: bool,
|
||||
#[bit(6, w)]
|
||||
nack_data: bool,
|
||||
#[bit(7, w)]
|
||||
rx_data_first: bool,
|
||||
|
||||
#[bit(8, w)]
|
||||
i2c_start: bool,
|
||||
#[bit(9, w)]
|
||||
i2c_stop: bool,
|
||||
#[bit(10, w)]
|
||||
tx_underflow: bool,
|
||||
#[bit(11, w)]
|
||||
rx_underflow: bool,
|
||||
#[bit(12, w)]
|
||||
tx_ready: bool,
|
||||
#[bit(13, w)]
|
||||
rx_ready: bool,
|
||||
#[bit(14, w)]
|
||||
tx_empty: bool,
|
||||
#[bit(15, w)]
|
||||
rx_full: bool,
|
||||
}
|
||||
|
||||
#[derive(derive_mmio::Mmio)]
|
||||
#[repr(C)]
|
||||
pub struct I2cSlave {
|
||||
s0_ctrl: Control,
|
||||
s0_maxwords: Maxwords,
|
||||
s0_address: Address,
|
||||
s0_addressmask: AddressMask,
|
||||
s0_data: Data,
|
||||
s0_lastaddress: LastAddress,
|
||||
#[mmio(PureRead)]
|
||||
s0_status: Status,
|
||||
#[mmio(PureRead)]
|
||||
s0_state: State,
|
||||
#[mmio(PureRead)]
|
||||
s0_tx_count: DataCount,
|
||||
#[mmio(PureRead)]
|
||||
s0_rx_count: DataCount,
|
||||
s0_irq_enb: InterruptControl,
|
||||
#[mmio(PureRead)]
|
||||
s0_irq_raw: InterruptStatus,
|
||||
#[mmio(PureRead)]
|
||||
s0_irq_status: InterruptStatus,
|
||||
#[mmio(Write)]
|
||||
s0_irq_clear: InterruptClear,
|
||||
s0_rx_fifo_trigger: TriggerLevel,
|
||||
s0_tx_fifo_trigger: TriggerLevel,
|
||||
#[mmio(Write)]
|
||||
s0_fifo_clear: FifoClear,
|
||||
s0_address_b: Address,
|
||||
s0_addressmask_b: AddressMask,
|
||||
}
|
||||
}
|
||||
#[derive(derive_mmio::Mmio)]
|
||||
#[mmio(no_ctors)]
|
||||
#[repr(C)]
|
||||
pub struct I2c {
|
||||
control: Control,
|
||||
clkscale: ClkScale,
|
||||
words: Words,
|
||||
address: Address,
|
||||
data: Data,
|
||||
#[mmio(Write)]
|
||||
cmd: Command,
|
||||
#[mmio(PureRead)]
|
||||
status: Status,
|
||||
#[mmio(PureRead)]
|
||||
state: State,
|
||||
#[mmio(PureRead)]
|
||||
tx_count: DataCount,
|
||||
#[mmio(PureRead)]
|
||||
rx_count: DataCount,
|
||||
irq_enb: InterruptControl,
|
||||
#[mmio(PureRead)]
|
||||
irq_raw: InterruptStatus,
|
||||
#[mmio(PureRead)]
|
||||
irq_status: InterruptStatus,
|
||||
#[mmio(Write)]
|
||||
irq_clear: InterruptClear,
|
||||
rx_fifo_trigger: TriggerLevel,
|
||||
tx_fifo_trigger: TriggerLevel,
|
||||
#[mmio(Write)]
|
||||
fifo_clear: FifoClear,
|
||||
timing_config: TimingConfig,
|
||||
clk_timeout_limit: ClkTimeoutLimit,
|
||||
|
||||
_reserved_0: [u32; 0x2D],
|
||||
|
||||
#[mmio(inner)]
|
||||
slave: slave::I2cSlave,
|
||||
|
||||
#[cfg(feature = "vor1x")]
|
||||
_reserved_1: [u32; 0x3AC],
|
||||
#[cfg(feature = "vor4x")]
|
||||
_reserved_1: [u32; 0xAC],
|
||||
|
||||
/// Vorago 4x: 0x0214_07E9. Vorago 1x: 0x0014_07E1.
|
||||
#[mmio(PureRead)]
|
||||
perid: u32,
|
||||
}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "vor1x")] {
|
||||
static_assertions::const_assert_eq!(core::mem::size_of::<I2c>(), 0x1000);
|
||||
} else if #[cfg(feature = "vor4x")] {
|
||||
static_assertions::const_assert_eq!(core::mem::size_of::<I2c>(), 0x400);
|
||||
}
|
||||
}
|
||||
|
||||
impl I2c {
|
||||
fn new_mmio_at(base: usize) -> MmioI2c<'static> {
|
||||
MmioI2c {
|
||||
ptr: base as *mut _,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_mmio(bank: Bank) -> MmioI2c<'static> {
|
||||
match bank {
|
||||
Bank::I2c0 => Self::new_mmio_at(BASE_ADDR_0),
|
||||
Bank::I2c1 => Self::new_mmio_at(BASE_ADDR_1),
|
||||
#[cfg(feature = "vor4x")]
|
||||
Bank::I2c2 => Self::new_mmio_at(BASE_ADDR_2),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user