From 16b2df91ca21b040cdd94ed0dabd88d43aa16d37 Mon Sep 17 00:00:00 2001 From: Harry Ho Date: Fri, 7 Aug 2020 11:10:18 +0800 Subject: [PATCH] i2c: fix GPIO register mapping, I2C control & EEPROM write operations --- libboard_zynq/src/i2c/eeprom.rs | 49 +++++++++++++++++------- libboard_zynq/src/i2c/mod.rs | 34 +++++++++++------ libboard_zynq/src/i2c/regs.rs | 68 ++++++++++++++++----------------- libboard_zynq/src/slcr.rs | 16 +++++++- 4 files changed, 106 insertions(+), 61 deletions(-) diff --git a/libboard_zynq/src/i2c/eeprom.rs b/libboard_zynq/src/i2c/eeprom.rs index f7987c1..de8319d 100644 --- a/libboard_zynq/src/i2c/eeprom.rs +++ b/libboard_zynq/src/i2c/eeprom.rs @@ -1,20 +1,24 @@ use super::I2C; -use crate::time::Microseconds; +use crate::time::Milliseconds; use embedded_hal::timer::CountDown; pub struct EEPROM<'a> { i2c: &'a mut I2C, port: u8, address: u8, + page_size: u8, + count_down: crate::timer::global::CountDown } impl<'a> EEPROM<'a> { #[cfg(feature = "target_zc706")] - pub fn new(i2c: &'a mut I2C) -> Self { + pub fn new(i2c: &'a mut I2C, page_size: u8) -> Self { EEPROM { i2c: i2c, port: 2, address: 0b1010100, + page_size: page_size, + count_down: unsafe { crate::timer::GlobalTimer::get() }.countdown() } } @@ -45,30 +49,49 @@ impl<'a> EEPROM<'a> { 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> { self.select()?; - self.i2c.start()?; - self.i2c.write(self.address << 1)?; - self.i2c.write(addr)?; + let buf_len = buf.len(); + let mut pb: u8 = addr % self.page_size; 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)?; - } + pb += 1; - self.i2c.stop()?; - self.poll(1_000_000_000)?; + if (i == buf_len-1) || (pb == self.page_size) { + self.i2c.stop()?; + self.poll(1_000)?; + pb = 0; + } + } Ok(()) } /// 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.i2c.count_down.start(Microseconds(timeout_us)); - while !self.i2c.write((self.address << 1) | 1)? { - if !self.i2c.count_down.waiting() { + self.count_down.start(Milliseconds(timeout_ms)); + loop { + 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") } } diff --git a/libboard_zynq/src/i2c/mod.rs b/libboard_zynq/src/i2c/mod.rs index 24ef31f..80cbfd2 100644 --- a/libboard_zynq/src/i2c/mod.rs +++ b/libboard_zynq/src/i2c/mod.rs @@ -10,8 +10,11 @@ use libregister::{RegisterR, RegisterRW, RegisterW}; const INVALID_BUS: &'static str = "Invalid I2C bus"; +#[cfg(feature = "target_zc706")] +const GPIO_OUTPUT_MASK: u16 = 0xFFFF - 0x000C; + pub struct I2C { - regs: regs::RegisterBlock, + regs: regs::RegisterWrapper, count_down: super::timer::global::CountDown } @@ -23,17 +26,21 @@ impl I2C { // SCL slcr.mio_pin_50.write( slcr::MioPin50::zeroed() - .l3_sel(0b000) // GPIO 50 - .io_type(slcr::IoBufferType::Lvcmos25) + .l3_sel(0b000) // as GPIO 50 + .io_type(slcr::IoBufferType::Lvcmos18) .pullup(true) + .disable_rcvr(true) ); // SDA slcr.mio_pin_51.write( slcr::MioPin51::zeroed() - .l3_sel(0b00) // GPIO 51 - .io_type(slcr::IoBufferType::Lvcmos25) + .l3_sel(0b000) // as GPIO 51 + .io_type(slcr::IoBufferType::Lvcmos18) .pullup(true) + .disable_rcvr(true) ); + // Reset + slcr.gpio_rst_ctrl.reset_gpio(); }); Self::ctor_common() @@ -42,27 +49,30 @@ impl I2C { fn ctor_common() -> Self { // Setup register block let clocks = Clocks::get(); - let mut self_ = Self { - regs: unsafe { regs::RegisterBlock::new() }, + let self_ = Self { + regs: regs::RegisterWrapper::new(), count_down: unsafe { super::timer::GlobalTimer::get() }.countdown() }; // Setup GPIO output mask 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_ } /// 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)); 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 { self.regs.gpio_input.read().sda() @@ -171,7 +181,7 @@ impl I2C { for bit in (0..8).rev() { self.sda_oe(data & (1 << bit) == 0); self.half_period(); - self.scl_o(false); + self.scl_oe(false); self.half_period(); self.scl_oe(true); } diff --git a/libboard_zynq/src/i2c/regs.rs b/libboard_zynq/src/i2c/regs.rs index b7b211a..601b874 100644 --- a/libboard_zynq/src/i2c/regs.rs +++ b/libboard_zynq/src/i2c/regs.rs @@ -1,6 +1,9 @@ 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: // @@ -20,76 +23,71 @@ use libregister::{register, register_bit, register_bits}; // Current compatibility: // zc706: GPIO 50, 51 == SCL, SDA -#[repr(C)] -pub struct RegisterBlock { +pub struct RegisterWrapper { pub gpio_output_mask: &'static mut GPIOOutputMask, pub gpio_input: &'static mut GPIOInput, + pub gpio_direction: &'static mut GPIODirection, pub gpio_output_enable: &'static mut GPIOOutputEnable, } -impl RegisterBlock { - pub unsafe fn new() -> Self { +impl RegisterWrapper { + pub fn new() -> Self { Self { gpio_output_mask: GPIOOutputMask::new(), gpio_input: GPIOInput::new(), + gpio_direction: GPIODirection::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: // Maskable output data for MIO[53:48] register!(gpio_output_mask, GPIOOutputMask, RW, u32); +#[cfg(feature = "target_zc706")] +register_at!(GPIOOutputMask, 0xE000A00C, new); // Output for SCL #[cfg(feature = "target_zc706")] register_bit!(gpio_output_mask, scl_o, 2); // Output for SDA #[cfg(feature = "target_zc706")] 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")] -register_bit!(gpio_output_mask, scl_m, 18); -// Mask for SDA; set to 1 to write to output -#[cfg(feature = "target_zc706")] -register_bit!(gpio_output_mask, sda_m, 19); +register_bits!(gpio_output_mask, mask, u16, 16, 31); // DATA_1_RO: // Input data for MIO[53:32] register!(gpio_input, GPIOInput, RO, u32); +#[cfg(feature = "target_zc706")] +register_at!(GPIOInput, 0xE000A064, new); // Input for SCL #[cfg(feature = "target_zc706")] -register_bit!(gpio_input, scl, 8); +register_bit!(gpio_input, scl, 18); // Input for SDA #[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: // Output enable for MIO[53:32] register!(gpio_output_enable, GPIOOutputEnable, RW, u32); +#[cfg(feature = "target_zc706")] +register_at!(GPIOOutputEnable, 0xE000A248, new); // Output enable for SCL #[cfg(feature = "target_zc706")] -register_bit!(gpio_output_enable, scl, 8); +register_bit!(gpio_output_enable, scl, 18); // Output enable for SDA #[cfg(feature = "target_zc706")] -register_bit!(gpio_output_enable, sda, 9); +register_bit!(gpio_output_enable, sda, 19); diff --git a/libboard_zynq/src/slcr.rs b/libboard_zynq/src/slcr.rs index e8e36bb..771e212 100644 --- a/libboard_zynq/src/slcr.rs +++ b/libboard_zynq/src/slcr.rs @@ -132,7 +132,7 @@ pub struct RegisterBlock { pub can_rst_ctrl: RW, pub i2c_rst_ctrl: RW, pub uart_rst_ctrl: UartRstCtrl, - pub gpio_rst_ctrl: RW, + pub gpio_rst_ctrl: GpioRstCtrl, pub lqspi_rst_ctrl: LqspiRstCtrl, pub smc_rst_ctrl: RW, pub ocm_rst_ctrl: RW, @@ -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_bit!(lqspi_clk_ctrl, clkact, 0); register_bits_typed!(lqspi_clk_ctrl, src_sel, u8, PllSource, 4, 5);