forked from M-Labs/artiq
1
0
Fork 0

firmware: I2C I/O expander support

This commit is contained in:
Sebastien Bourdeauducq 2020-05-05 21:38:17 +08:00
parent ef4e5bc69b
commit 4982fde898
4 changed files with 141 additions and 3 deletions

View File

@ -0,0 +1,101 @@
use i2c;
pub struct IoExpander {
busno: u8,
port: u8,
address: u8,
virtual_led_mapping: &'static [(u8, u8, u8)],
out_current: [u8; 2],
out_target: [u8; 2],
}
impl IoExpander {
#[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))]
pub fn new(index: u8) -> Self {
const VIRTUAL_LED_MAPPING0: [(u8, u8, u8); 2] = [(0, 0, 6), (1, 1, 6)];
const VIRTUAL_LED_MAPPING1: [(u8, u8, u8); 2] = [(2, 0, 6), (3, 1, 6)];
// Both expanders on SHARED I2C bus
match index {
0 => IoExpander {
busno: 0,
port: 11,
address: 0x40,
virtual_led_mapping: &VIRTUAL_LED_MAPPING0,
out_current: [0; 2],
out_target: [0; 2],
},
1 => IoExpander {
busno: 0,
port: 11,
address: 0x42,
virtual_led_mapping: &VIRTUAL_LED_MAPPING1,
out_current: [0; 2],
out_target: [0; 2],
},
_ => panic!("incorrect I/O expander index"),
}
}
#[cfg(soc_platform = "kasli")]
fn select(&self) -> Result<(), &'static str> {
let mask: u16 = 1 << self.port;
i2c::pca9548_select(self.busno, 0x70, mask as u8)?;
i2c::pca9548_select(self.busno, 0x71, (mask >> 8) as u8)?;
Ok(())
}
fn write(&self, addr: u8, value: u8) -> Result<(), &'static str> {
i2c::start(self.busno)?;
i2c::write(self.busno, self.address)?;
i2c::write(self.busno, addr)?;
i2c::write(self.busno, value)?;
i2c::stop(self.busno)?;
Ok(())
}
pub fn init(&mut self) -> Result<(), &'static str> {
self.select()?;
let mut iodir = [0xffu8; 2];
for (_led, port, bit) in self.virtual_led_mapping.iter() {
iodir[*port as usize] &= !(1 << *bit);
}
self.write(0x00, iodir[0])?;
self.write(0x01, iodir[1])?;
self.out_current[0] = 0x00;
self.write(0x12, 0x00)?;
self.out_current[1] = 0x00;
self.write(0x13, 0x00)?;
Ok(())
}
pub fn set(&mut self, port: u8, bit: u8, high: bool) {
if high {
self.out_target[port as usize] |= 1 << bit;
} else {
self.out_target[port as usize] &= !(1 << bit);
}
}
pub fn service(&mut self) -> Result<(), &'static str> {
for (led, port, bit) in self.virtual_led_mapping.iter() {
// TODO: get level from gateware
self.set(*port, *bit, false);
}
if self.out_target != self.out_current {
self.select()?;
if self.out_target[0] != self.out_current[0] {
self.write(0x12, self.out_target[0])?;
self.out_current[0] = self.out_target[0];
}
if self.out_target[1] != self.out_current[1] {
self.write(0x13, self.out_target[1])?;
self.out_current[1] = self.out_target[1];
}
}
Ok(())
}
}

View File

@ -37,6 +37,8 @@ pub mod ethmac;
pub mod i2c;
#[cfg(soc_platform = "kasli")]
pub mod i2c_eeprom;
#[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))]
pub mod io_expander;
#[cfg(all(has_ethmac, feature = "smoltcp"))]
pub mod net_settings;
#[cfg(has_slave_fpga_cfg)]

View File

@ -99,6 +99,15 @@ fn startup() {
setup_log_levels();
#[cfg(has_i2c)]
board_misoc::i2c::init().expect("I2C initialization failed");
#[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))]
let (mut io_expander0, mut io_expander1);
#[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))]
{
io_expander0 = board_misoc::io_expander::IoExpander::new(0);
io_expander1 = board_misoc::io_expander::IoExpander::new(1);
io_expander0.init().expect("I2C I/O expander #0 initialization failed");
io_expander1.init().expect("I2C I/O expander #1 initialization failed");
}
rtio_clocking::init();
let mut net_device = unsafe { ethmac::EthernetDevice::new() };
@ -210,6 +219,12 @@ fn startup() {
if let Some(_net_stats_diff) = net_stats.update() {
debug!("ethernet mac:{}", ethmac::EthernetStatistics::new());
}
#[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))]
{
io_expander0.service().expect("I2C I/O expander #0 service failed");
io_expander1.service().expect("I2C I/O expander #1 service failed");
}
}
}

View File

@ -454,13 +454,23 @@ pub extern fn main() -> i32 {
info!("software ident {}", csr::CONFIG_IDENTIFIER_STR);
info!("gateware ident {}", ident::read(&mut [0; 64]));
#[cfg(has_si5324)]
{
#[cfg(has_i2c)]
i2c::init().expect("I2C initialization failed");
si5324::setup(&SI5324_SETTINGS, si5324::Input::Ckin1).expect("cannot initialize Si5324");
#[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))]
let (mut io_expander0, mut io_expander1);
#[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))]
{
io_expander0 = board_misoc::io_expander::IoExpander::new(0);
io_expander1 = board_misoc::io_expander::IoExpander::new(1);
io_expander0.init().expect("I2C I/O expander #0 initialization failed");
io_expander1.init().expect("I2C I/O expander #1 initialization failed");
}
#[cfg(has_si5324)]
si5324::setup(&SI5324_SETTINGS, si5324::Input::Ckin1).expect("cannot initialize Si5324");
#[cfg(has_wrpll)]
wrpll::init();
unsafe {
csr::drtio_transceiver::stable_clkin_write(1);
}
@ -507,6 +517,11 @@ pub extern fn main() -> i32 {
for mut rep in repeaters.iter_mut() {
rep.service(&routing_table, rank);
}
#[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))]
{
io_expander0.service().expect("I2C I/O expander #0 service failed");
io_expander1.service().expect("I2C I/O expander #1 service failed");
}
hardware_tick(&mut hardware_tick_ts);
}
@ -531,6 +546,11 @@ pub extern fn main() -> i32 {
for mut rep in repeaters.iter_mut() {
rep.service(&routing_table, rank);
}
#[cfg(all(soc_platform = "kasli", hw_rev = "v2.0"))]
{
io_expander0.service().expect("I2C I/O expander #0 service failed");
io_expander1.service().expect("I2C I/O expander #1 service failed");
}
hardware_tick(&mut hardware_tick_ts);
if drtiosat_tsc_loaded() {
info!("TSC loaded from uplink");