From 3efc682bd6e9e39b9bd151fda92191d9237b4214 Mon Sep 17 00:00:00 2001 From: mwojcik Date: Tue, 8 Feb 2022 15:40:17 +0800 Subject: [PATCH 1/6] add pca954x autodetection, pca9547 support --- libboard_zynq/src/i2c/eeprom.rs | 6 +-- libboard_zynq/src/i2c/mod.rs | 88 ++++++++++++++++++++++++++++++--- libboard_zynq/src/i2c/regs.rs | 33 ++++++++++++- 3 files changed, 115 insertions(+), 12 deletions(-) diff --git a/libboard_zynq/src/i2c/eeprom.rs b/libboard_zynq/src/i2c/eeprom.rs index e00dd15..fb90f9c 100644 --- a/libboard_zynq/src/i2c/eeprom.rs +++ b/libboard_zynq/src/i2c/eeprom.rs @@ -35,16 +35,14 @@ impl<'a> EEPROM<'a> { #[cfg(feature = "target_zc706")] fn select(&mut self) -> Result<(), &'static str> { - let mask: u16 = 1 << self.port; - self.i2c.pca9548_select(0b1110100, mask as u8)?; + self.i2c.pca954x_select(0b1110100, self.port)?; Ok(()) } #[cfg(feature = "target_kasli_soc")] fn select(&mut self) -> Result<(), &'static str> { - let mask: u16 = 1 << self.port; // tca9548 is compatible with pca9548 - self.i2c.pca9548_select(0b1110001, mask as u8)?; + self.i2c.pca954x_select(0b1110001, self.port)?; Ok(()) } diff --git a/libboard_zynq/src/i2c/mod.rs b/libboard_zynq/src/i2c/mod.rs index 6129799..80e76d2 100644 --- a/libboard_zynq/src/i2c/mod.rs +++ b/libboard_zynq/src/i2c/mod.rs @@ -7,9 +7,16 @@ use super::time::Microseconds; use embedded_hal::timer::CountDown; use libregister::{RegisterR, RegisterRW, RegisterW}; +enum PCA954X { + PCA9548 = 0, + #[cfg(feature = "target_kasli_soc")] + PCA9547 = 1, +} + pub struct I2c { regs: regs::RegisterBlock, - count_down: super::timer::global::CountDown + count_down: super::timer::global::CountDown, + pca_type: PCA954X } impl I2c { @@ -48,14 +55,15 @@ impl I2c { slcr.gpio_rst_ctrl.reset_gpio(); }); - Self::i2c_common(0xFFFF - 0x000C) + Self::i2c_common(0xFFFF - 0x000C, 0xFFFF - 0x0002) } - fn i2c_common(gpio_output_mask: u16) -> Self { + fn i2c_common(gpio_output_mask: u16, _gpio_output_mask_lower: u16) -> Self { // Setup register block let self_ = Self { regs: regs::RegisterBlock::i2c(), - count_down: unsafe { super::timer::GlobalTimer::get() }.countdown() + count_down: unsafe { super::timer::GlobalTimer::get() }.countdown(), + pca_type: PCA954X::PCA9548 }; // Setup GPIO output mask @@ -67,6 +75,17 @@ impl I2c { w.scl(true).sda(true) }); + //Kasli-SoC exclusive: I2C_SW_RESET configuration + #[cfg(feature = "target_kasli_soc")] + { + self_.regs.gpio_output_mask_lower.modify(|_, w| { + w.mask(_gpio_output_mask_lower) + }); + self_.regs.gpio_direction.modify(|_, w| { + w.i2cswr(true) + }); + } + self_ } @@ -110,6 +129,47 @@ impl I2c { }) } + #[cfg(feature = "target_kasli_soc")] + fn i2cswr_oe(&mut self, oe: bool) { + self.regs.gpio_output_enable.modify(|_, w| { + w.i2cswr(oe) + }) + } + + #[cfg(feature = "target_kasli_soc")] + fn i2cswr_o(&mut self, o: bool) { + self.regs.gpio_output_mask_lower.modify(|_, w| { + w.i2cswr_o(o) + }) + } + + #[cfg(feature = "target_kasli_soc")] + fn pca_autodetect(&mut self) -> Result { + // start with resetting the PCA954X + self.i2cswr_oe(false); + self.i2cswr_o(false); + self.delay_us(10); // reset time is just 500ns + self.i2cswr_oe(true); + self.i2cswr_o(true); + self.delay_us(10); + + let pca954x_addr = 0x70; + + self.start()?; + // read the config register + if !self.write(pca954x_addr << 1 | 0x01)? { + return Err("PCA954X failed to ack read address"); + } + let config = self.read(true)?; + let pca = match config { + 0x00 => PCA954X::PCA9548, + 0x08 => PCA954X::PCA9547, + _ => { return Err("Unknown PCA954X type."); } + }; + self.stop()?; + Ok(pca) + } + pub fn init(&mut self) -> Result<(), &'static str> { self.scl_oe(false); self.sda_oe(false); @@ -136,6 +196,14 @@ impl I2c { return Err("SCL is stuck low"); } // postcondition: SCL and SDA high + #[cfg(feature = "target_kasli_soc")] + { + self.pca_type = self.pca_autodetect()?; + } + #[cfg(not(feature = "target_kasli_soc"))] + { + self.pca_type = PCA954X::PCA9548; + } Ok(()) } @@ -231,12 +299,20 @@ impl I2c { Ok(data) } - pub fn pca9548_select(&mut self, address: u8, channels: u8) -> Result<(), &'static str> { + pub fn pca954x_select(&mut self, address: u8, channel: u8) -> Result<(), &'static str> { self.start()?; + // PCA9547 supports only one channel at a time + // for compatibility, PCA9548 is treated as such + let setting = match self.pca_type { + PCA954X::PCA9548 => 1 << channel, + #[cfg(feature = "target_kasli_soc")] + PCA954X::PCA9547 => channel | 0x08, + }; + if !self.write(address << 1)? { return Err("PCA9548 failed to ack write address") } - if !self.write(channels)? { + if !self.write(setting)? { return Err("PCA9548 failed to ack control word") } self.stop()?; diff --git a/libboard_zynq/src/i2c/regs.rs b/libboard_zynq/src/i2c/regs.rs index f0fae8a..01d26ae 100644 --- a/libboard_zynq/src/i2c/regs.rs +++ b/libboard_zynq/src/i2c/regs.rs @@ -20,13 +20,15 @@ use libregister::{ // // Current compatibility: // zc706: GPIO 50, 51 == SCL, SDA -// kasli_soc: GPIO 50, 51 == SCL, SDA +// kasli_soc: GPIO 50, 51 == SCL, SDA; GPIO 33 == I2C_SW_RESET pub struct RegisterBlock { 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, + #[cfg(feature = "target_kasli_soc")] + pub gpio_output_mask_lower: &'static mut GPIOOutputMaskLower, } impl RegisterBlock { @@ -35,7 +37,9 @@ impl RegisterBlock { gpio_output_mask: GPIOOutputMask::new(), gpio_input: GPIOInput::new(), gpio_direction: GPIODirection::new(), - gpio_output_enable: GPIOOutputEnable::new() + gpio_output_enable: GPIOOutputEnable::new(), + #[cfg(feature = "target_kasli_soc")] + gpio_output_mask_lower: GPIOOutputMaskLower::new(), } } } @@ -59,6 +63,21 @@ register_bits!(gpio_output_mask, /// Mask for keeping bits except SCL and SDA unchanged mask, u16, 16, 31); + +register!(gpio_output_mask_lower, + /// MASK_DATA_1_LSW: + /// Maskable output data for MIO[47:32] + GPIOOutputMaskLower, RW, u32); +#[cfg(feature = "target_kasli_soc")] +register_at!(GPIOOutputMaskLower, 0xE000A008, new); +#[cfg(feature = "target_kasli_soc")] +register_bit!(gpio_output_mask_lower, + /// Output for I2C_SW_RESET (MIO[33]) + i2cswr_o, 1); +#[cfg(feature = "target_kasli_soc")] +register_bits!(gpio_output_mask_lower, + mask, u16, 16, 31); + register!(gpio_input, /// DATA_1_RO: /// Input data for MIO[53:32] @@ -74,6 +93,7 @@ register_bit!(gpio_input, /// Input for SDA sda, 19); + register!(gpio_direction, /// DIRM_1: /// Direction mode for MIO[53:32]; 0/1 = in/out @@ -88,6 +108,10 @@ register_bit!(gpio_direction, register_bit!(gpio_direction, /// Direction for SDA sda, 19); +#[cfg(feature = "target_kasli_soc")] +register_bit!(gpio_direction, + /// Direction for I2C_SW_RESET + i2cswr, 1); register!(gpio_output_enable, /// OEN_1: @@ -103,3 +127,8 @@ register_bit!(gpio_output_enable, register_bit!(gpio_output_enable, /// Output enable for SDA sda, 19); +#[cfg(feature = "target_kasli_soc")] +register_bit!(gpio_output_enable, + /// Output enable for I2C_SW_RESET + i2cswr, 1); + \ No newline at end of file -- 2.42.0 From bc41b9119226180d6152df73c182d56870efbdc2 Mon Sep 17 00:00:00 2001 From: mwojcik Date: Tue, 8 Feb 2022 16:39:12 +0800 Subject: [PATCH 2/6] adjust pca address --- libboard_zynq/src/i2c/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libboard_zynq/src/i2c/mod.rs b/libboard_zynq/src/i2c/mod.rs index 80e76d2..9ac8a33 100644 --- a/libboard_zynq/src/i2c/mod.rs +++ b/libboard_zynq/src/i2c/mod.rs @@ -153,11 +153,11 @@ impl I2c { self.i2cswr_o(true); self.delay_us(10); - let pca954x_addr = 0x70; + let pca954x_addr = 0x71; self.start()?; // read the config register - if !self.write(pca954x_addr << 1 | 0x01)? { + if !self.write(pca954x_addr)? { return Err("PCA954X failed to ack read address"); } let config = self.read(true)?; -- 2.42.0 From 14b0247716fe19a9bb2b028e86553a83dc76e8e5 Mon Sep 17 00:00:00 2001 From: mwojcik Date: Wed, 9 Feb 2022 17:23:33 +0800 Subject: [PATCH 3/6] pca954x: fix to work on cold boot --- libboard_zynq/src/i2c/mod.rs | 30 ++++++++++++++---------------- libboard_zynq/src/i2c/regs.rs | 2 +- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/libboard_zynq/src/i2c/mod.rs b/libboard_zynq/src/i2c/mod.rs index 9ac8a33..f68221d 100644 --- a/libboard_zynq/src/i2c/mod.rs +++ b/libboard_zynq/src/i2c/mod.rs @@ -144,27 +144,26 @@ impl I2c { } #[cfg(feature = "target_kasli_soc")] - fn pca_autodetect(&mut self) -> Result { + pub fn pca_autodetect(&mut self) -> Result { // start with resetting the PCA954X - self.i2cswr_oe(false); - self.i2cswr_o(false); - self.delay_us(10); // reset time is just 500ns - self.i2cswr_oe(true); - self.i2cswr_o(true); - self.delay_us(10); - let pca954x_addr = 0x71; + /*self.i2cswr_oe(false); + self.unit_delay(); + self.i2cswr_oe(true);*/ + + let pca954x_read_addr = (0x71 << 1) | 0x01; self.start()?; // read the config register - if !self.write(pca954x_addr)? { + if !self.write(pca954x_read_addr)? { return Err("PCA954X failed to ack read address"); } - let config = self.read(true)?; + let config = self.read(false)?; + let pca = match config { 0x00 => PCA954X::PCA9548, 0x08 => PCA954X::PCA9547, - _ => { return Err("Unknown PCA954X type."); } + _ => { return Err("Unknown response for PCA954X autodetect")}, }; self.stop()?; Ok(pca) @@ -196,14 +195,13 @@ impl I2c { return Err("SCL is stuck low"); } // postcondition: SCL and SDA high + #[cfg(feature = "target_kasli_soc")] { + self.unit_delay(); self.pca_type = self.pca_autodetect()?; } - #[cfg(not(feature = "target_kasli_soc"))] - { - self.pca_type = PCA954X::PCA9548; - } + Ok(()) } @@ -287,7 +285,7 @@ impl I2c { if self.sda_i() { data |= 1 << bit } self.scl_oe(true); } - // Send ack/nack + // Send ack/nack (true = nack, false = ack) self.sda_oe(ack); self.unit_delay(); self.scl_oe(false); diff --git a/libboard_zynq/src/i2c/regs.rs b/libboard_zynq/src/i2c/regs.rs index 01d26ae..7d566fd 100644 --- a/libboard_zynq/src/i2c/regs.rs +++ b/libboard_zynq/src/i2c/regs.rs @@ -76,7 +76,7 @@ register_bit!(gpio_output_mask_lower, i2cswr_o, 1); #[cfg(feature = "target_kasli_soc")] register_bits!(gpio_output_mask_lower, - mask, u16, 16, 31); + mask, u16, 16, 31); register!(gpio_input, /// DATA_1_RO: -- 2.42.0 From fbc783d7add2978baac3d6ea6da3aad967f6e8a1 Mon Sep 17 00:00:00 2001 From: mwojcik Date: Fri, 11 Feb 2022 11:19:15 +0800 Subject: [PATCH 4/6] flake: add pyftdi to env --- flake.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/flake.nix b/flake.nix index a2935bb..cf5d035 100644 --- a/flake.nix +++ b/flake.nix @@ -278,6 +278,7 @@ openocd gdb openssh rsync llvmPackages_9.clang-unwrapped + (python3.withPackages(ps: [ ps.pyftdi ])) mkbootimage ]; }; }; -- 2.42.0 From dacc816eb44c9c9904a4b1a251578f5a9dd1b49d Mon Sep 17 00:00:00 2001 From: mwojcik Date: Fri, 11 Feb 2022 11:22:53 +0800 Subject: [PATCH 5/6] pca954x: improve code, fix I2C_SW_RESET behavior --- libboard_zynq/src/i2c/mod.rs | 38 +++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/libboard_zynq/src/i2c/mod.rs b/libboard_zynq/src/i2c/mod.rs index f68221d..25e65fa 100644 --- a/libboard_zynq/src/i2c/mod.rs +++ b/libboard_zynq/src/i2c/mod.rs @@ -7,7 +7,7 @@ use super::time::Microseconds; use embedded_hal::timer::CountDown; use libregister::{RegisterR, RegisterRW, RegisterW}; -enum PCA954X { +pub enum I2cMultiplexer { PCA9548 = 0, #[cfg(feature = "target_kasli_soc")] PCA9547 = 1, @@ -16,7 +16,7 @@ enum PCA954X { pub struct I2c { regs: regs::RegisterBlock, count_down: super::timer::global::CountDown, - pca_type: PCA954X + pca_type: I2cMultiplexer } impl I2c { @@ -63,7 +63,7 @@ impl I2c { let self_ = Self { regs: regs::RegisterBlock::i2c(), count_down: unsafe { super::timer::GlobalTimer::get() }.countdown(), - pca_type: PCA954X::PCA9548 + pca_type: I2cMultiplexer::PCA9548 //default for zc706 }; // Setup GPIO output mask @@ -75,7 +75,7 @@ impl I2c { w.scl(true).sda(true) }); - //Kasli-SoC exclusive: I2C_SW_RESET configuration + //Kasli-SoC only: I2C_SW_RESET configuration #[cfg(feature = "target_kasli_soc")] { self_.regs.gpio_output_mask_lower.modify(|_, w| { @@ -144,13 +144,15 @@ impl I2c { } #[cfg(feature = "target_kasli_soc")] - pub fn pca_autodetect(&mut self) -> Result { + fn pca_autodetect(&mut self) -> Result { // start with resetting the PCA954X - - /*self.i2cswr_oe(false); - self.unit_delay(); - self.i2cswr_oe(true);*/ - + // SDA must be clear (before start) + // reset time is 500ns, unit_delay (100us) to account for propagation + self.i2cswr_o(true); + self.unit_delay(); + self.i2cswr_o(false); + self.unit_delay(); + let pca954x_read_addr = (0x71 << 1) | 0x01; self.start()?; @@ -161,8 +163,8 @@ impl I2c { let config = self.read(false)?; let pca = match config { - 0x00 => PCA954X::PCA9548, - 0x08 => PCA954X::PCA9547, + 0x00 => I2cMultiplexer::PCA9548, + 0x08 => I2cMultiplexer::PCA9547, _ => { return Err("Unknown response for PCA954X autodetect")}, }; self.stop()?; @@ -198,7 +200,7 @@ impl I2c { #[cfg(feature = "target_kasli_soc")] { - self.unit_delay(); + self.i2cswr_oe(true); self.pca_type = self.pca_autodetect()?; } @@ -300,18 +302,18 @@ impl I2c { pub fn pca954x_select(&mut self, address: u8, channel: u8) -> Result<(), &'static str> { self.start()?; // PCA9547 supports only one channel at a time - // for compatibility, PCA9548 is treated as such + // for compatibility, PCA9548 is treated as such too let setting = match self.pca_type { - PCA954X::PCA9548 => 1 << channel, + I2cMultiplexer::PCA9548 => 1 << channel, #[cfg(feature = "target_kasli_soc")] - PCA954X::PCA9547 => channel | 0x08, + I2cMultiplexer::PCA9547 => channel | 0x08, }; if !self.write(address << 1)? { - return Err("PCA9548 failed to ack write address") + return Err("PCA954X failed to ack write address") } if !self.write(setting)? { - return Err("PCA9548 failed to ack control word") + return Err("PCA954X failed to ack control word") } self.stop()?; Ok(()) -- 2.42.0 From 26ab2927b9cec5abb9fcccd85f086971bd73dbe1 Mon Sep 17 00:00:00 2001 From: mwojcik Date: Fri, 11 Feb 2022 12:00:39 +0800 Subject: [PATCH 6/6] pca954x: log detected type --- libboard_zynq/src/i2c/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libboard_zynq/src/i2c/mod.rs b/libboard_zynq/src/i2c/mod.rs index 25e65fa..0e48798 100644 --- a/libboard_zynq/src/i2c/mod.rs +++ b/libboard_zynq/src/i2c/mod.rs @@ -6,6 +6,8 @@ use super::slcr; use super::time::Microseconds; use embedded_hal::timer::CountDown; use libregister::{RegisterR, RegisterRW, RegisterW}; +#[cfg(feature = "target_kasli_soc")] +use log::info; pub enum I2cMultiplexer { PCA9548 = 0, @@ -163,8 +165,8 @@ impl I2c { let config = self.read(false)?; let pca = match config { - 0x00 => I2cMultiplexer::PCA9548, - 0x08 => I2cMultiplexer::PCA9547, + 0x00 => { info!("PCA9548 detected"); I2cMultiplexer::PCA9548 }, + 0x08 => { info!("PCA9547 detected"); I2cMultiplexer::PCA9547 }, _ => { return Err("Unknown response for PCA954X autodetect")}, }; self.stop()?; -- 2.42.0