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-08 15:40:17 +08:00
|
|
|
enum PCA954X {
|
|
|
|
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>,
|
|
|
|
pca_type: PCA954X
|
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-07 11:10:18 +08:00
|
|
|
// Reset
|
|
|
|
slcr.gpio_rst_ctrl.reset_gpio();
|
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(),
|
|
|
|
pca_type: PCA954X::PCA9548
|
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-08 15:40:17 +08:00
|
|
|
//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)
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
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")]
|
|
|
|
fn pca_autodetect(&mut self) -> Result<PCA954X, &'static str> {
|
|
|
|
// 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);
|
|
|
|
|
2022-02-08 16:39:12 +08:00
|
|
|
let pca954x_addr = 0x71;
|
2022-02-08 15:40:17 +08:00
|
|
|
|
|
|
|
self.start()?;
|
|
|
|
// read the config register
|
2022-02-08 16:39:12 +08:00
|
|
|
if !self.write(pca954x_addr)? {
|
2022-02-08 15:40:17 +08:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
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-08 15:40:17 +08:00
|
|
|
#[cfg(feature = "target_kasli_soc")]
|
|
|
|
{
|
|
|
|
self.pca_type = self.pca_autodetect()?;
|
|
|
|
}
|
|
|
|
#[cfg(not(feature = "target_kasli_soc"))]
|
|
|
|
{
|
|
|
|
self.pca_type = PCA954X::PCA9548;
|
|
|
|
}
|
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);
|
|
|
|
}
|
|
|
|
// Send ack/nack
|
|
|
|
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-08 15:40:17 +08:00
|
|
|
pub fn pca954x_select(&mut self, address: u8, channel: 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
|
|
|
|
// 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,
|
|
|
|
};
|
|
|
|
|
2020-08-05 17:35:33 +08:00
|
|
|
if !self.write(address << 1)? {
|
|
|
|
return Err("PCA9548 failed to ack write address")
|
|
|
|
}
|
2022-02-08 15:40:17 +08:00
|
|
|
if !self.write(setting)? {
|
2020-08-05 17:35:33 +08:00
|
|
|
return Err("PCA9548 failed to ack control word")
|
|
|
|
}
|
|
|
|
self.stop()?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|