forked from M-Labs/zynq-rs
i2c: fix GPIO register mapping, I2C control & EEPROM write operations
This commit is contained in:
parent
f7d3135ec7
commit
16b2df91ca
|
@ -1,20 +1,24 @@
|
||||||
use super::I2C;
|
use super::I2C;
|
||||||
use crate::time::Microseconds;
|
use crate::time::Milliseconds;
|
||||||
use embedded_hal::timer::CountDown;
|
use embedded_hal::timer::CountDown;
|
||||||
|
|
||||||
pub struct EEPROM<'a> {
|
pub struct EEPROM<'a> {
|
||||||
i2c: &'a mut I2C,
|
i2c: &'a mut I2C,
|
||||||
port: u8,
|
port: u8,
|
||||||
address: u8,
|
address: u8,
|
||||||
|
page_size: u8,
|
||||||
|
count_down: crate::timer::global::CountDown<Milliseconds>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> EEPROM<'a> {
|
impl<'a> EEPROM<'a> {
|
||||||
#[cfg(feature = "target_zc706")]
|
#[cfg(feature = "target_zc706")]
|
||||||
pub fn new(i2c: &'a mut I2C) -> Self {
|
pub fn new(i2c: &'a mut I2C, page_size: u8) -> Self {
|
||||||
EEPROM {
|
EEPROM {
|
||||||
i2c: i2c,
|
i2c: i2c,
|
||||||
port: 2,
|
port: 2,
|
||||||
address: 0b1010100,
|
address: 0b1010100,
|
||||||
|
page_size: page_size,
|
||||||
|
count_down: unsafe { crate::timer::GlobalTimer::get() }.countdown()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,30 +49,49 @@ impl<'a> EEPROM<'a> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Page write
|
/// Smart multi-page writing
|
||||||
|
/// Using the "Page Write" function of an EEPROM, the memory region for each transaction
|
||||||
|
/// (i.e. from byte `addr` to byte `addr+buf.len()`) should fit under each page
|
||||||
|
/// (i.e. `addr+buf.len()` < `addr/self.page_size+1`); otherwise, a roll-oever occurs,
|
||||||
|
/// where bytes beyond the page end. This smart function takes care of the scenario to avoid
|
||||||
|
/// any roll-over when writing ambiguous memory regions.
|
||||||
pub fn write(&mut self, addr: u8, buf: &[u8]) -> Result<(), &'static str> {
|
pub fn write(&mut self, addr: u8, buf: &[u8]) -> Result<(), &'static str> {
|
||||||
self.select()?;
|
self.select()?;
|
||||||
|
|
||||||
self.i2c.start()?;
|
let buf_len = buf.len();
|
||||||
self.i2c.write(self.address << 1)?;
|
let mut pb: u8 = addr % self.page_size;
|
||||||
self.i2c.write(addr)?;
|
|
||||||
for (i, byte) in buf.iter().enumerate() {
|
for (i, byte) in buf.iter().enumerate() {
|
||||||
|
if (i == 0) || (pb == 0) {
|
||||||
|
self.i2c.start()?;
|
||||||
|
self.i2c.write(self.address << 1)?;
|
||||||
|
self.i2c.write(addr + (i as u8))?;
|
||||||
|
}
|
||||||
self.i2c.write(*byte)?;
|
self.i2c.write(*byte)?;
|
||||||
}
|
pb += 1;
|
||||||
|
|
||||||
self.i2c.stop()?;
|
if (i == buf_len-1) || (pb == self.page_size) {
|
||||||
self.poll(1_000_000_000)?;
|
self.i2c.stop()?;
|
||||||
|
self.poll(1_000)?;
|
||||||
|
pb = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Poll
|
/// Poll
|
||||||
pub fn poll(&mut self, timeout_us: u64) -> Result<(), &'static str> {
|
pub fn poll(&mut self, timeout_ms: u64) -> Result<(), &'static str> {
|
||||||
self.select()?;
|
self.select()?;
|
||||||
|
|
||||||
self.i2c.count_down.start(Microseconds(timeout_us));
|
self.count_down.start(Milliseconds(timeout_ms));
|
||||||
while !self.i2c.write((self.address << 1) | 1)? {
|
loop {
|
||||||
if !self.i2c.count_down.waiting() {
|
self.i2c.start()?;
|
||||||
|
let ack = self.i2c.write(self.address << 1)?;
|
||||||
|
self.i2c.stop()?;
|
||||||
|
if ack {
|
||||||
|
break
|
||||||
|
};
|
||||||
|
if !self.count_down.waiting() {
|
||||||
return Err("I2C polling timeout")
|
return Err("I2C polling timeout")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,11 @@ use libregister::{RegisterR, RegisterRW, RegisterW};
|
||||||
|
|
||||||
const INVALID_BUS: &'static str = "Invalid I2C bus";
|
const INVALID_BUS: &'static str = "Invalid I2C bus";
|
||||||
|
|
||||||
|
#[cfg(feature = "target_zc706")]
|
||||||
|
const GPIO_OUTPUT_MASK: u16 = 0xFFFF - 0x000C;
|
||||||
|
|
||||||
pub struct I2C {
|
pub struct I2C {
|
||||||
regs: regs::RegisterBlock,
|
regs: regs::RegisterWrapper,
|
||||||
count_down: super::timer::global::CountDown<Microseconds>
|
count_down: super::timer::global::CountDown<Microseconds>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,17 +26,21 @@ impl I2C {
|
||||||
// SCL
|
// SCL
|
||||||
slcr.mio_pin_50.write(
|
slcr.mio_pin_50.write(
|
||||||
slcr::MioPin50::zeroed()
|
slcr::MioPin50::zeroed()
|
||||||
.l3_sel(0b000) // GPIO 50
|
.l3_sel(0b000) // as GPIO 50
|
||||||
.io_type(slcr::IoBufferType::Lvcmos25)
|
.io_type(slcr::IoBufferType::Lvcmos18)
|
||||||
.pullup(true)
|
.pullup(true)
|
||||||
|
.disable_rcvr(true)
|
||||||
);
|
);
|
||||||
// SDA
|
// SDA
|
||||||
slcr.mio_pin_51.write(
|
slcr.mio_pin_51.write(
|
||||||
slcr::MioPin51::zeroed()
|
slcr::MioPin51::zeroed()
|
||||||
.l3_sel(0b00) // GPIO 51
|
.l3_sel(0b000) // as GPIO 51
|
||||||
.io_type(slcr::IoBufferType::Lvcmos25)
|
.io_type(slcr::IoBufferType::Lvcmos18)
|
||||||
.pullup(true)
|
.pullup(true)
|
||||||
|
.disable_rcvr(true)
|
||||||
);
|
);
|
||||||
|
// Reset
|
||||||
|
slcr.gpio_rst_ctrl.reset_gpio();
|
||||||
});
|
});
|
||||||
|
|
||||||
Self::ctor_common()
|
Self::ctor_common()
|
||||||
|
@ -42,27 +49,30 @@ impl I2C {
|
||||||
fn ctor_common() -> Self {
|
fn ctor_common() -> Self {
|
||||||
// Setup register block
|
// Setup register block
|
||||||
let clocks = Clocks::get();
|
let clocks = Clocks::get();
|
||||||
let mut self_ = Self {
|
let self_ = Self {
|
||||||
regs: unsafe { regs::RegisterBlock::new() },
|
regs: regs::RegisterWrapper::new(),
|
||||||
count_down: unsafe { super::timer::GlobalTimer::get() }.countdown()
|
count_down: unsafe { super::timer::GlobalTimer::get() }.countdown()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Setup GPIO output mask
|
// Setup GPIO output mask
|
||||||
self_.regs.gpio_output_mask.modify(|_, w| {
|
self_.regs.gpio_output_mask.modify(|_, w| {
|
||||||
w.scl_m(true).sda_m(true)
|
w.mask(GPIO_OUTPUT_MASK)
|
||||||
|
});
|
||||||
|
// Setup GPIO driver direction
|
||||||
|
self_.regs.gpio_direction.modify(|_, w| {
|
||||||
|
w.scl(true).sda(true)
|
||||||
});
|
});
|
||||||
|
|
||||||
self_.init();
|
|
||||||
self_
|
self_
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delay for I2C operations, simple wrapper for nb.
|
/// Delay for I2C operations, simple wrapper for nb.
|
||||||
fn delay(&mut self, us: u64) {
|
fn delay_us(&mut self, us: u64) {
|
||||||
self.count_down.start(Microseconds(us));
|
self.count_down.start(Microseconds(us));
|
||||||
nb::block!(self.count_down.wait()).unwrap();
|
nb::block!(self.count_down.wait()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn half_period(&mut self) { self.delay(100) }
|
fn half_period(&mut self) { self.delay_us(100) }
|
||||||
|
|
||||||
fn sda_i(&mut self) -> bool {
|
fn sda_i(&mut self) -> bool {
|
||||||
self.regs.gpio_input.read().sda()
|
self.regs.gpio_input.read().sda()
|
||||||
|
@ -171,7 +181,7 @@ impl I2C {
|
||||||
for bit in (0..8).rev() {
|
for bit in (0..8).rev() {
|
||||||
self.sda_oe(data & (1 << bit) == 0);
|
self.sda_oe(data & (1 << bit) == 0);
|
||||||
self.half_period();
|
self.half_period();
|
||||||
self.scl_o(false);
|
self.scl_oe(false);
|
||||||
self.half_period();
|
self.half_period();
|
||||||
self.scl_oe(true);
|
self.scl_oe(true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
use volatile_register::{RO, WO, RW};
|
use volatile_register::{RO, WO, RW};
|
||||||
|
|
||||||
use libregister::{register, register_bit, register_bits};
|
use libregister::{
|
||||||
|
register, register_at,
|
||||||
|
register_bit, register_bits
|
||||||
|
};
|
||||||
|
|
||||||
// With reference to:
|
// With reference to:
|
||||||
//
|
//
|
||||||
|
@ -20,76 +23,71 @@ use libregister::{register, register_bit, register_bits};
|
||||||
// Current compatibility:
|
// Current compatibility:
|
||||||
// zc706: GPIO 50, 51 == SCL, SDA
|
// zc706: GPIO 50, 51 == SCL, SDA
|
||||||
|
|
||||||
#[repr(C)]
|
pub struct RegisterWrapper {
|
||||||
pub struct RegisterBlock {
|
|
||||||
pub gpio_output_mask: &'static mut GPIOOutputMask,
|
pub gpio_output_mask: &'static mut GPIOOutputMask,
|
||||||
pub gpio_input: &'static mut GPIOInput,
|
pub gpio_input: &'static mut GPIOInput,
|
||||||
|
pub gpio_direction: &'static mut GPIODirection,
|
||||||
pub gpio_output_enable: &'static mut GPIOOutputEnable,
|
pub gpio_output_enable: &'static mut GPIOOutputEnable,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RegisterBlock {
|
impl RegisterWrapper {
|
||||||
pub unsafe fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
gpio_output_mask: GPIOOutputMask::new(),
|
gpio_output_mask: GPIOOutputMask::new(),
|
||||||
gpio_input: GPIOInput::new(),
|
gpio_input: GPIOInput::new(),
|
||||||
|
gpio_direction: GPIODirection::new(),
|
||||||
gpio_output_enable: GPIOOutputEnable::new()
|
gpio_output_enable: GPIOOutputEnable::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GPIOOutputMask {
|
|
||||||
#[cfg(feature = "target_zc706")]
|
|
||||||
pub unsafe fn new() -> &'static mut Self {
|
|
||||||
&mut *(0xE000A00C as *mut _)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GPIOInput {
|
|
||||||
#[cfg(feature = "target_zc706")]
|
|
||||||
pub unsafe fn new() -> &'static mut Self {
|
|
||||||
&mut *(0xE000A064 as *mut _)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GPIOOutputEnable {
|
|
||||||
#[cfg(feature = "target_zc706")]
|
|
||||||
pub unsafe fn new() -> &'static mut Self {
|
|
||||||
&mut *(0xE000A248 as *mut _)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MASK_DATA_1_MSW:
|
// MASK_DATA_1_MSW:
|
||||||
// Maskable output data for MIO[53:48]
|
// Maskable output data for MIO[53:48]
|
||||||
register!(gpio_output_mask, GPIOOutputMask, RW, u32);
|
register!(gpio_output_mask, GPIOOutputMask, RW, u32);
|
||||||
|
#[cfg(feature = "target_zc706")]
|
||||||
|
register_at!(GPIOOutputMask, 0xE000A00C, new);
|
||||||
// Output for SCL
|
// Output for SCL
|
||||||
#[cfg(feature = "target_zc706")]
|
#[cfg(feature = "target_zc706")]
|
||||||
register_bit!(gpio_output_mask, scl_o, 2);
|
register_bit!(gpio_output_mask, scl_o, 2);
|
||||||
// Output for SDA
|
// Output for SDA
|
||||||
#[cfg(feature = "target_zc706")]
|
#[cfg(feature = "target_zc706")]
|
||||||
register_bit!(gpio_output_mask, sda_o, 3);
|
register_bit!(gpio_output_mask, sda_o, 3);
|
||||||
// Mask for SCL; set to 1 to write to output
|
// Mask for keeping bits except SCL and SDA unchanged
|
||||||
#[cfg(feature = "target_zc706")]
|
#[cfg(feature = "target_zc706")]
|
||||||
register_bit!(gpio_output_mask, scl_m, 18);
|
register_bits!(gpio_output_mask, mask, u16, 16, 31);
|
||||||
// Mask for SDA; set to 1 to write to output
|
|
||||||
#[cfg(feature = "target_zc706")]
|
|
||||||
register_bit!(gpio_output_mask, sda_m, 19);
|
|
||||||
|
|
||||||
// DATA_1_RO:
|
// DATA_1_RO:
|
||||||
// Input data for MIO[53:32]
|
// Input data for MIO[53:32]
|
||||||
register!(gpio_input, GPIOInput, RO, u32);
|
register!(gpio_input, GPIOInput, RO, u32);
|
||||||
|
#[cfg(feature = "target_zc706")]
|
||||||
|
register_at!(GPIOInput, 0xE000A064, new);
|
||||||
// Input for SCL
|
// Input for SCL
|
||||||
#[cfg(feature = "target_zc706")]
|
#[cfg(feature = "target_zc706")]
|
||||||
register_bit!(gpio_input, scl, 8);
|
register_bit!(gpio_input, scl, 18);
|
||||||
// Input for SDA
|
// Input for SDA
|
||||||
#[cfg(feature = "target_zc706")]
|
#[cfg(feature = "target_zc706")]
|
||||||
register_bit!(gpio_input, sda, 9);
|
register_bit!(gpio_input, sda, 19);
|
||||||
|
|
||||||
|
// DIRM_1:
|
||||||
|
// Direction mode for MIO[53:32]; 0/1 = in/out
|
||||||
|
register!(gpio_direction, GPIODirection, RW, u32);
|
||||||
|
#[cfg(feature = "target_zc706")]
|
||||||
|
register_at!(GPIODirection, 0xE000A244, new);
|
||||||
|
// Direction for SCL
|
||||||
|
#[cfg(feature = "target_zc706")]
|
||||||
|
register_bit!(gpio_direction, scl, 18);
|
||||||
|
// Direction for SDA
|
||||||
|
#[cfg(feature = "target_zc706")]
|
||||||
|
register_bit!(gpio_direction, sda, 19);
|
||||||
|
|
||||||
// OEN_1:
|
// OEN_1:
|
||||||
// Output enable for MIO[53:32]
|
// Output enable for MIO[53:32]
|
||||||
register!(gpio_output_enable, GPIOOutputEnable, RW, u32);
|
register!(gpio_output_enable, GPIOOutputEnable, RW, u32);
|
||||||
|
#[cfg(feature = "target_zc706")]
|
||||||
|
register_at!(GPIOOutputEnable, 0xE000A248, new);
|
||||||
// Output enable for SCL
|
// Output enable for SCL
|
||||||
#[cfg(feature = "target_zc706")]
|
#[cfg(feature = "target_zc706")]
|
||||||
register_bit!(gpio_output_enable, scl, 8);
|
register_bit!(gpio_output_enable, scl, 18);
|
||||||
// Output enable for SDA
|
// Output enable for SDA
|
||||||
#[cfg(feature = "target_zc706")]
|
#[cfg(feature = "target_zc706")]
|
||||||
register_bit!(gpio_output_enable, sda, 9);
|
register_bit!(gpio_output_enable, sda, 19);
|
||||||
|
|
|
@ -132,7 +132,7 @@ pub struct RegisterBlock {
|
||||||
pub can_rst_ctrl: RW<u32>,
|
pub can_rst_ctrl: RW<u32>,
|
||||||
pub i2c_rst_ctrl: RW<u32>,
|
pub i2c_rst_ctrl: RW<u32>,
|
||||||
pub uart_rst_ctrl: UartRstCtrl,
|
pub uart_rst_ctrl: UartRstCtrl,
|
||||||
pub gpio_rst_ctrl: RW<u32>,
|
pub gpio_rst_ctrl: GpioRstCtrl,
|
||||||
pub lqspi_rst_ctrl: LqspiRstCtrl,
|
pub lqspi_rst_ctrl: LqspiRstCtrl,
|
||||||
pub smc_rst_ctrl: RW<u32>,
|
pub smc_rst_ctrl: RW<u32>,
|
||||||
pub ocm_rst_ctrl: RW<u32>,
|
pub ocm_rst_ctrl: RW<u32>,
|
||||||
|
@ -531,6 +531,20 @@ impl UartRstCtrl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
register!(gpio_rst_ctrl, GpioRstCtrl, RW, u32);
|
||||||
|
register_bit!(gpio_rst_ctrl, gpio_cpu1x_rst, 0);
|
||||||
|
register_at!(GpioRstCtrl, 0xF800022C, new);
|
||||||
|
impl GpioRstCtrl {
|
||||||
|
pub fn reset_gpio(&mut self) {
|
||||||
|
self.modify(|_, w|
|
||||||
|
w.gpio_cpu1x_rst(true)
|
||||||
|
);
|
||||||
|
self.modify(|_, w|
|
||||||
|
w.gpio_cpu1x_rst(false)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
register!(lqspi_clk_ctrl, LqspiClkCtrl, RW, u32);
|
register!(lqspi_clk_ctrl, LqspiClkCtrl, RW, u32);
|
||||||
register_bit!(lqspi_clk_ctrl, clkact, 0);
|
register_bit!(lqspi_clk_ctrl, clkact, 0);
|
||||||
register_bits_typed!(lqspi_clk_ctrl, src_sel, u8, PllSource, 4, 5);
|
register_bits_typed!(lqspi_clk_ctrl, src_sel, u8, PllSource, 4, 5);
|
||||||
|
|
Loading…
Reference in New Issue