2020-08-05 17:35:33 +08:00
|
|
|
//! I2C Bit-banging Controller
|
|
|
|
|
|
|
|
mod regs;
|
2020-08-05 20:10:30 +08:00
|
|
|
pub mod eeprom;
|
2020-08-05 17:35:33 +08:00
|
|
|
use super::slcr;
|
|
|
|
use super::time::Microseconds;
|
|
|
|
use embedded_hal::timer::CountDown;
|
|
|
|
use libregister::{RegisterR, RegisterRW, RegisterW};
|
2022-02-11 12:00:39 +08:00
|
|
|
#[cfg(feature = "target_kasli_soc")]
|
|
|
|
use log::info;
|
2020-08-05 17:35:33 +08:00
|
|
|
|
2022-02-11 11:22:53 +08:00
|
|
|
pub enum I2cMultiplexer {
|
2022-02-08 15:40:17 +08:00
|
|
|
PCA9548 = 0,
|
|
|
|
#[cfg(feature = "target_kasli_soc")]
|
|
|
|
PCA9547 = 1,
|
|
|
|
}
|
|
|
|
|
2020-08-12 16:27:17 +08:00
|
|
|
pub struct I2c {
|
2020-08-13 13:39:04 +08:00
|
|
|
regs: regs::RegisterBlock,
|
2022-02-08 15:40:17 +08:00
|
|
|
count_down: super::timer::global::CountDown<Microseconds>,
|
2022-02-11 11:22:53 +08:00
|
|
|
pca_type: I2cMultiplexer
|
2020-08-05 17:35:33 +08:00
|
|
|
}
|
|
|
|
|
2020-08-12 16:27:17 +08:00
|
|
|
impl I2c {
|
2020-11-20 03:17:36 +08:00
|
|
|
#[cfg(any(feature = "target_zc706", feature = "target_kasli_soc"))]
|
2020-08-12 16:27:17 +08:00
|
|
|
pub fn i2c0() -> Self {
|
2020-08-05 17:35:33 +08:00
|
|
|
// Route I2C 0 SCL / SDA Signals to MIO Pins 50 / 51
|
|
|
|
slcr::RegisterBlock::unlocked(|slcr| {
|
|
|
|
// SCL
|
|
|
|
slcr.mio_pin_50.write(
|
|
|
|
slcr::MioPin50::zeroed()
|
2020-08-07 11:10:18 +08:00
|
|
|
.l3_sel(0b000) // as GPIO 50
|
|
|
|
.io_type(slcr::IoBufferType::Lvcmos18)
|
2020-08-05 17:35:33 +08:00
|
|
|
.pullup(true)
|
2020-08-07 11:10:18 +08:00
|
|
|
.disable_rcvr(true)
|
2020-08-05 17:35:33 +08:00
|
|
|
);
|
|
|
|
// SDA
|
|
|
|
slcr.mio_pin_51.write(
|
|
|
|
slcr::MioPin51::zeroed()
|
2020-08-07 11:10:18 +08:00
|
|
|
.l3_sel(0b000) // as GPIO 51
|
|
|
|
.io_type(slcr::IoBufferType::Lvcmos18)
|
2020-08-05 17:35:33 +08:00
|
|
|
.pullup(true)
|
2020-08-07 11:10:18 +08:00
|
|
|
.disable_rcvr(true)
|
2020-08-05 17:35:33 +08:00
|
|
|
);
|
2021-06-19 18:49:20 +08:00
|
|
|
// On Kasli-SoC prototype, leakage through the unconfigured I2C_SW_RESET
|
|
|
|
// MIO pin develops enough voltage on the T21 gate to assert the reset.
|
|
|
|
// Configure the pin to avoid this problem.
|
|
|
|
#[cfg(feature = "target_kasli_soc")]
|
|
|
|
slcr.mio_pin_33.write(
|
|
|
|
slcr::MioPin33::zeroed()
|
|
|
|
.l3_sel(0b000)
|
|
|
|
.io_type(slcr::IoBufferType::Lvcmos33)
|
|
|
|
.pullup(false)
|
|
|
|
.disable_rcvr(true)
|
|
|
|
);
|
2020-08-05 17:35:33 +08:00
|
|
|
});
|
|
|
|
|
2022-02-08 15:40:17 +08:00
|
|
|
Self::i2c_common(0xFFFF - 0x000C, 0xFFFF - 0x0002)
|
2020-08-05 17:35:33 +08:00
|
|
|
}
|
|
|
|
|
2022-02-08 15:40:17 +08:00
|
|
|
fn i2c_common(gpio_output_mask: u16, _gpio_output_mask_lower: u16) -> Self {
|
2020-08-05 17:35:33 +08:00
|
|
|
// Setup register block
|
2020-08-07 11:10:18 +08:00
|
|
|
let self_ = Self {
|
2020-08-13 13:39:04 +08:00
|
|
|
regs: regs::RegisterBlock::i2c(),
|
2022-02-08 15:40:17 +08:00
|
|
|
count_down: unsafe { super::timer::GlobalTimer::get() }.countdown(),
|
2022-02-11 11:22:53 +08:00
|
|
|
pca_type: I2cMultiplexer::PCA9548 //default for zc706
|
2020-08-05 17:35:33 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
// Setup GPIO output mask
|
|
|
|
self_.regs.gpio_output_mask.modify(|_, w| {
|
2020-08-07 12:18:54 +08:00
|
|
|
w.mask(gpio_output_mask)
|
2020-08-07 11:10:18 +08:00
|
|
|
});
|
|
|
|
// Setup GPIO driver direction
|
|
|
|
self_.regs.gpio_direction.modify(|_, w| {
|
|
|
|
w.scl(true).sda(true)
|
2020-08-05 17:35:33 +08:00
|
|
|
});
|
|
|
|
|
2022-02-11 11:22:53 +08:00
|
|
|
//Kasli-SoC only: I2C_SW_RESET configuration
|
2022-02-08 15:40:17 +08:00
|
|
|
#[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)
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-08-05 17:35:33 +08:00
|
|
|
self_
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Delay for I2C operations, simple wrapper for nb.
|
2020-08-07 11:10:18 +08:00
|
|
|
fn delay_us(&mut self, us: u64) {
|
2020-08-05 17:35:33 +08:00
|
|
|
self.count_down.start(Microseconds(us));
|
|
|
|
nb::block!(self.count_down.wait()).unwrap();
|
|
|
|
}
|
|
|
|
|
2021-06-25 16:26:53 +08:00
|
|
|
fn unit_delay(&mut self) { self.delay_us(100) }
|
2020-08-05 17:35:33 +08:00
|
|
|
|
|
|
|
fn sda_i(&mut self) -> bool {
|
|
|
|
self.regs.gpio_input.read().sda()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn scl_i(&mut self) -> bool {
|
|
|
|
self.regs.gpio_input.read().scl()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn sda_oe(&mut self, oe: bool) {
|
|
|
|
self.regs.gpio_output_enable.modify(|_, w| {
|
|
|
|
w.sda(oe)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn sda_o(&mut self, o: bool) {
|
|
|
|
self.regs.gpio_output_mask.modify(|_, w| {
|
|
|
|
w.sda_o(o)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn scl_oe(&mut self, oe: bool) {
|
|
|
|
self.regs.gpio_output_enable.modify(|_, w| {
|
|
|
|
w.scl(oe)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn scl_o(&mut self, o: bool) {
|
|
|
|
self.regs.gpio_output_mask.modify(|_, w| {
|
|
|
|
w.scl_o(o)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-02-08 15:40:17 +08:00
|
|
|
#[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")]
|
2022-02-11 11:22:53 +08:00
|
|
|
fn pca_autodetect(&mut self) -> Result<I2cMultiplexer, &'static str> {
|
2022-02-08 15:40:17 +08:00
|
|
|
// start with resetting the PCA954X
|
2022-02-11 11:22:53 +08:00
|
|
|
// 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();
|
|
|
|
|
2022-02-09 17:23:33 +08:00
|
|
|
let pca954x_read_addr = (0x71 << 1) | 0x01;
|
2022-02-08 15:40:17 +08:00
|
|
|
|
|
|
|
self.start()?;
|
|
|
|
// read the config register
|
2022-02-09 17:23:33 +08:00
|
|
|
if !self.write(pca954x_read_addr)? {
|
2022-02-08 15:40:17 +08:00
|
|
|
return Err("PCA954X failed to ack read address");
|
|
|
|
}
|
2022-02-09 17:23:33 +08:00
|
|
|
let config = self.read(false)?;
|
|
|
|
|
2022-02-08 15:40:17 +08:00
|
|
|
let pca = match config {
|
2022-02-11 12:00:39 +08:00
|
|
|
0x00 => { info!("PCA9548 detected"); I2cMultiplexer::PCA9548 },
|
|
|
|
0x08 => { info!("PCA9547 detected"); I2cMultiplexer::PCA9547 },
|
2022-02-09 17:23:33 +08:00
|
|
|
_ => { return Err("Unknown response for PCA954X autodetect")},
|
2022-02-08 15:40:17 +08:00
|
|
|
};
|
|
|
|
self.stop()?;
|
|
|
|
Ok(pca)
|
|
|
|
}
|
|
|
|
|
2020-08-05 17:35:33 +08:00
|
|
|
pub fn init(&mut self) -> Result<(), &'static str> {
|
|
|
|
self.scl_oe(false);
|
|
|
|
self.sda_oe(false);
|
|
|
|
self.scl_o(false);
|
|
|
|
self.sda_o(false);
|
|
|
|
|
|
|
|
// Check the I2C bus is ready
|
2021-06-25 16:26:53 +08:00
|
|
|
self.unit_delay();
|
|
|
|
self.unit_delay();
|
2020-08-05 17:35:33 +08:00
|
|
|
if !self.sda_i() {
|
|
|
|
// Try toggling SCL a few times
|
|
|
|
for _bit in 0..8 {
|
|
|
|
self.scl_oe(true);
|
2021-06-25 16:26:53 +08:00
|
|
|
self.unit_delay();
|
2020-08-05 17:35:33 +08:00
|
|
|
self.scl_oe(false);
|
2021-06-25 16:26:53 +08:00
|
|
|
self.unit_delay();
|
2020-08-05 17:35:33 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !self.sda_i() {
|
|
|
|
return Err("SDA is stuck low and doesn't get unstuck");
|
|
|
|
}
|
|
|
|
if !self.scl_i() {
|
2021-06-25 16:23:50 +08:00
|
|
|
return Err("SCL is stuck low");
|
2020-08-05 17:35:33 +08:00
|
|
|
}
|
|
|
|
// postcondition: SCL and SDA high
|
2022-02-09 17:23:33 +08:00
|
|
|
|
2022-02-08 15:40:17 +08:00
|
|
|
#[cfg(feature = "target_kasli_soc")]
|
|
|
|
{
|
2022-02-11 11:22:53 +08:00
|
|
|
self.i2cswr_oe(true);
|
2022-02-08 15:40:17 +08:00
|
|
|
self.pca_type = self.pca_autodetect()?;
|
|
|
|
}
|
2022-02-09 17:23:33 +08:00
|
|
|
|
2020-08-05 17:35:33 +08:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn start(&mut self) -> Result<(), &'static str> {
|
|
|
|
// precondition: SCL and SDA high
|
|
|
|
if !self.scl_i() {
|
2021-06-25 16:23:50 +08:00
|
|
|
return Err("SCL is stuck low");
|
2020-08-05 17:35:33 +08:00
|
|
|
}
|
|
|
|
if !self.sda_i() {
|
|
|
|
return Err("SDA arbitration lost");
|
|
|
|
}
|
|
|
|
self.sda_oe(true);
|
2021-06-25 16:26:53 +08:00
|
|
|
self.unit_delay();
|
2020-08-05 17:35:33 +08:00
|
|
|
self.scl_oe(true);
|
2021-06-25 16:26:53 +08:00
|
|
|
self.unit_delay();
|
2020-08-05 17:35:33 +08:00
|
|
|
// postcondition: SCL and SDA low
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn restart(&mut self) -> Result<(), &'static str> {
|
|
|
|
// precondition SCL and SDA low
|
|
|
|
self.sda_oe(false);
|
2021-06-25 16:26:53 +08:00
|
|
|
self.unit_delay();
|
2020-08-05 17:35:33 +08:00
|
|
|
self.scl_oe(false);
|
2021-06-25 16:26:53 +08:00
|
|
|
self.unit_delay();
|
2020-08-05 17:35:33 +08:00
|
|
|
self.start()?;
|
|
|
|
// postcondition: SCL and SDA low
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn stop(&mut self) -> Result<(), &'static str> {
|
|
|
|
// precondition: SCL and SDA low
|
2021-06-25 16:26:53 +08:00
|
|
|
self.unit_delay();
|
2020-08-05 17:35:33 +08:00
|
|
|
self.scl_oe(false);
|
2021-06-25 16:26:53 +08:00
|
|
|
self.unit_delay();
|
2020-08-05 17:35:33 +08:00
|
|
|
self.sda_oe(false);
|
2021-06-25 16:26:53 +08:00
|
|
|
self.unit_delay();
|
2020-08-05 17:35:33 +08:00
|
|
|
if !self.sda_i() {
|
|
|
|
return Err("SDA arbitration lost");
|
|
|
|
}
|
|
|
|
// postcondition: SCL and SDA high
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn write(&mut self, data: u8) -> Result<bool, &'static str> {
|
|
|
|
// precondition: SCL and SDA low
|
|
|
|
// MSB first
|
|
|
|
for bit in (0..8).rev() {
|
|
|
|
self.sda_oe(data & (1 << bit) == 0);
|
2021-06-25 16:26:53 +08:00
|
|
|
self.unit_delay();
|
2020-08-07 11:10:18 +08:00
|
|
|
self.scl_oe(false);
|
2021-06-25 16:26:53 +08:00
|
|
|
self.unit_delay();
|
2020-08-05 17:35:33 +08:00
|
|
|
self.scl_oe(true);
|
2021-06-25 16:26:53 +08:00
|
|
|
self.unit_delay();
|
2020-08-05 17:35:33 +08:00
|
|
|
}
|
|
|
|
self.sda_oe(false);
|
2021-06-25 16:26:53 +08:00
|
|
|
self.unit_delay();
|
2020-08-05 17:35:33 +08:00
|
|
|
self.scl_oe(false);
|
2021-06-25 16:26:53 +08:00
|
|
|
self.unit_delay();
|
2020-08-05 17:35:33 +08:00
|
|
|
// Read ack/nack
|
|
|
|
let ack = !self.sda_i();
|
|
|
|
self.scl_oe(true);
|
2021-06-25 16:26:53 +08:00
|
|
|
self.unit_delay();
|
2020-08-05 17:35:33 +08:00
|
|
|
self.sda_oe(true);
|
|
|
|
// postcondition: SCL and SDA low
|
|
|
|
|
|
|
|
Ok(ack)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn read(&mut self, ack: bool) -> Result<u8, &'static str> {
|
|
|
|
// precondition: SCL and SDA low
|
|
|
|
self.sda_oe(false);
|
|
|
|
|
|
|
|
let mut data: u8 = 0;
|
|
|
|
|
|
|
|
// MSB first
|
|
|
|
for bit in (0..8).rev() {
|
2021-06-25 16:26:53 +08:00
|
|
|
self.unit_delay();
|
2020-08-05 17:35:33 +08:00
|
|
|
self.scl_oe(false);
|
2021-06-25 16:26:53 +08:00
|
|
|
self.unit_delay();
|
2020-08-05 17:35:33 +08:00
|
|
|
if self.sda_i() { data |= 1 << bit }
|
|
|
|
self.scl_oe(true);
|
|
|
|
}
|
2022-02-09 17:23:33 +08:00
|
|
|
// Send ack/nack (true = nack, false = ack)
|
2020-08-05 17:35:33 +08:00
|
|
|
self.sda_oe(ack);
|
2021-06-25 16:26:53 +08:00
|
|
|
self.unit_delay();
|
2020-08-05 17:35:33 +08:00
|
|
|
self.scl_oe(false);
|
2021-06-25 16:26:53 +08:00
|
|
|
self.unit_delay();
|
2020-08-05 17:35:33 +08:00
|
|
|
self.scl_oe(true);
|
|
|
|
self.sda_oe(true);
|
|
|
|
// postcondition: SCL and SDA low
|
|
|
|
|
|
|
|
Ok(data)
|
|
|
|
}
|
|
|
|
|
2022-02-11 13:46:51 +08:00
|
|
|
pub fn pca954x_select(&mut self, address: u8, channel: Option<u8>) -> Result<(), &'static str> {
|
2020-08-05 17:35:33 +08:00
|
|
|
self.start()?;
|
2022-02-08 15:40:17 +08:00
|
|
|
// PCA9547 supports only one channel at a time
|
2022-02-11 11:22:53 +08:00
|
|
|
// for compatibility, PCA9548 is treated as such too
|
2022-02-11 13:46:51 +08:00
|
|
|
// channel - Some(x) - # of the channel [0,7], or None for all disabled
|
2022-02-08 15:40:17 +08:00
|
|
|
let setting = match self.pca_type {
|
2022-02-11 13:46:51 +08:00
|
|
|
I2cMultiplexer::PCA9548 => {
|
|
|
|
match channel {
|
|
|
|
Some(ch) => 1 << ch,
|
|
|
|
None => 0,
|
|
|
|
}
|
|
|
|
},
|
2022-02-08 15:40:17 +08:00
|
|
|
#[cfg(feature = "target_kasli_soc")]
|
2022-02-11 13:46:51 +08:00
|
|
|
I2cMultiplexer::PCA9547 => {
|
|
|
|
match channel {
|
|
|
|
Some(ch) => ch | 0x08,
|
|
|
|
None => 0,
|
|
|
|
}
|
|
|
|
}
|
2022-02-08 15:40:17 +08:00
|
|
|
};
|
|
|
|
|
2020-08-05 17:35:33 +08:00
|
|
|
if !self.write(address << 1)? {
|
2022-02-11 11:22:53 +08:00
|
|
|
return Err("PCA954X failed to ack write address")
|
2020-08-05 17:35:33 +08:00
|
|
|
}
|
2022-02-08 15:40:17 +08:00
|
|
|
if !self.write(setting)? {
|
2022-02-11 11:22:53 +08:00
|
|
|
return Err("PCA954X failed to ack control word")
|
2020-08-05 17:35:33 +08:00
|
|
|
}
|
|
|
|
self.stop()?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|