forked from M-Labs/zynq-rs
add pca954x autodetection, pca9547 support
This commit is contained in:
parent
e5e646f40e
commit
3efc682bd6
|
@ -35,16 +35,14 @@ impl<'a> EEPROM<'a> {
|
||||||
|
|
||||||
#[cfg(feature = "target_zc706")]
|
#[cfg(feature = "target_zc706")]
|
||||||
fn select(&mut self) -> Result<(), &'static str> {
|
fn select(&mut self) -> Result<(), &'static str> {
|
||||||
let mask: u16 = 1 << self.port;
|
self.i2c.pca954x_select(0b1110100, self.port)?;
|
||||||
self.i2c.pca9548_select(0b1110100, mask as u8)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "target_kasli_soc")]
|
#[cfg(feature = "target_kasli_soc")]
|
||||||
fn select(&mut self) -> Result<(), &'static str> {
|
fn select(&mut self) -> Result<(), &'static str> {
|
||||||
let mask: u16 = 1 << self.port;
|
|
||||||
// tca9548 is compatible with pca9548
|
// tca9548 is compatible with pca9548
|
||||||
self.i2c.pca9548_select(0b1110001, mask as u8)?;
|
self.i2c.pca954x_select(0b1110001, self.port)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,9 +7,16 @@ use super::time::Microseconds;
|
||||||
use embedded_hal::timer::CountDown;
|
use embedded_hal::timer::CountDown;
|
||||||
use libregister::{RegisterR, RegisterRW, RegisterW};
|
use libregister::{RegisterR, RegisterRW, RegisterW};
|
||||||
|
|
||||||
|
enum PCA954X {
|
||||||
|
PCA9548 = 0,
|
||||||
|
#[cfg(feature = "target_kasli_soc")]
|
||||||
|
PCA9547 = 1,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct I2c {
|
pub struct I2c {
|
||||||
regs: regs::RegisterBlock,
|
regs: regs::RegisterBlock,
|
||||||
count_down: super::timer::global::CountDown<Microseconds>
|
count_down: super::timer::global::CountDown<Microseconds>,
|
||||||
|
pca_type: PCA954X
|
||||||
}
|
}
|
||||||
|
|
||||||
impl I2c {
|
impl I2c {
|
||||||
|
@ -48,14 +55,15 @@ impl I2c {
|
||||||
slcr.gpio_rst_ctrl.reset_gpio();
|
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
|
// Setup register block
|
||||||
let self_ = Self {
|
let self_ = Self {
|
||||||
regs: regs::RegisterBlock::i2c(),
|
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
|
// Setup GPIO output mask
|
||||||
|
@ -67,6 +75,17 @@ impl I2c {
|
||||||
w.scl(true).sda(true)
|
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_
|
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<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);
|
||||||
|
|
||||||
|
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> {
|
pub fn init(&mut self) -> Result<(), &'static str> {
|
||||||
self.scl_oe(false);
|
self.scl_oe(false);
|
||||||
self.sda_oe(false);
|
self.sda_oe(false);
|
||||||
|
@ -136,6 +196,14 @@ impl I2c {
|
||||||
return Err("SCL is stuck low");
|
return Err("SCL is stuck low");
|
||||||
}
|
}
|
||||||
// postcondition: SCL and SDA high
|
// 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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,12 +299,20 @@ impl I2c {
|
||||||
Ok(data)
|
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()?;
|
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)? {
|
if !self.write(address << 1)? {
|
||||||
return Err("PCA9548 failed to ack write address")
|
return Err("PCA9548 failed to ack write address")
|
||||||
}
|
}
|
||||||
if !self.write(channels)? {
|
if !self.write(setting)? {
|
||||||
return Err("PCA9548 failed to ack control word")
|
return Err("PCA9548 failed to ack control word")
|
||||||
}
|
}
|
||||||
self.stop()?;
|
self.stop()?;
|
||||||
|
|
|
@ -20,13 +20,15 @@ use libregister::{
|
||||||
//
|
//
|
||||||
// Current compatibility:
|
// Current compatibility:
|
||||||
// zc706: GPIO 50, 51 == SCL, SDA
|
// 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 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_direction: &'static mut GPIODirection,
|
||||||
pub gpio_output_enable: &'static mut GPIOOutputEnable,
|
pub gpio_output_enable: &'static mut GPIOOutputEnable,
|
||||||
|
#[cfg(feature = "target_kasli_soc")]
|
||||||
|
pub gpio_output_mask_lower: &'static mut GPIOOutputMaskLower,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RegisterBlock {
|
impl RegisterBlock {
|
||||||
|
@ -35,7 +37,9 @@ impl RegisterBlock {
|
||||||
gpio_output_mask: GPIOOutputMask::new(),
|
gpio_output_mask: GPIOOutputMask::new(),
|
||||||
gpio_input: GPIOInput::new(),
|
gpio_input: GPIOInput::new(),
|
||||||
gpio_direction: GPIODirection::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 for keeping bits except SCL and SDA unchanged
|
||||||
mask, u16, 16, 31);
|
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,
|
register!(gpio_input,
|
||||||
/// DATA_1_RO:
|
/// DATA_1_RO:
|
||||||
/// Input data for MIO[53:32]
|
/// Input data for MIO[53:32]
|
||||||
|
@ -74,6 +93,7 @@ register_bit!(gpio_input,
|
||||||
/// Input for SDA
|
/// Input for SDA
|
||||||
sda, 19);
|
sda, 19);
|
||||||
|
|
||||||
|
|
||||||
register!(gpio_direction,
|
register!(gpio_direction,
|
||||||
/// DIRM_1:
|
/// DIRM_1:
|
||||||
/// Direction mode for MIO[53:32]; 0/1 = in/out
|
/// Direction mode for MIO[53:32]; 0/1 = in/out
|
||||||
|
@ -88,6 +108,10 @@ register_bit!(gpio_direction,
|
||||||
register_bit!(gpio_direction,
|
register_bit!(gpio_direction,
|
||||||
/// Direction for SDA
|
/// Direction for SDA
|
||||||
sda, 19);
|
sda, 19);
|
||||||
|
#[cfg(feature = "target_kasli_soc")]
|
||||||
|
register_bit!(gpio_direction,
|
||||||
|
/// Direction for I2C_SW_RESET
|
||||||
|
i2cswr, 1);
|
||||||
|
|
||||||
register!(gpio_output_enable,
|
register!(gpio_output_enable,
|
||||||
/// OEN_1:
|
/// OEN_1:
|
||||||
|
@ -103,3 +127,8 @@ register_bit!(gpio_output_enable,
|
||||||
register_bit!(gpio_output_enable,
|
register_bit!(gpio_output_enable,
|
||||||
/// Output enable for SDA
|
/// Output enable for SDA
|
||||||
sda, 19);
|
sda, 19);
|
||||||
|
#[cfg(feature = "target_kasli_soc")]
|
||||||
|
register_bit!(gpio_output_enable,
|
||||||
|
/// Output enable for I2C_SW_RESET
|
||||||
|
i2cswr, 1);
|
||||||
|
|
Loading…
Reference in New Issue