Refactor i2c and io_expander to make them shared

Signed-off-by: Egor Savkin <es@m-labs.hk>
This commit is contained in:
Egor Savkin 2023-02-17 12:47:28 +08:00
parent dcc5cc7555
commit e51a5728e0
6 changed files with 141 additions and 125 deletions

View File

@ -0,0 +1,83 @@
use libboard_zynq;
pub static mut I2C_BUS: Option<libboard_zynq::i2c::I2c> = None;
pub fn start() -> Result<(), &'static str> {
unsafe {
if (&mut I2C_BUS).as_mut().unwrap().start().is_err() {
Err("I2C start failed")
} else {
Ok(())
}
}
}
pub fn restart() -> Result<(), &'static str> {
unsafe {
if (&mut I2C_BUS).as_mut().unwrap().restart().is_err() {
Err("I2C restart failed")
} else {
Ok(())
}
}
}
pub fn stop() -> Result<(), &'static str> {
unsafe {
if (&mut I2C_BUS).as_mut().unwrap().stop().is_err() {
Err("I2C stop failed")
} else {
Ok(())
}
}
}
pub fn write(data: i32) -> Result<bool, &'static str> {
unsafe {
match (&mut I2C_BUS).as_mut().unwrap().write(data as u8) {
Ok(r) =>Ok(r),
Err(_) => Err("I2C write failed"),
}
}
}
pub fn read(ack: bool) -> Result<i32, &'static str> {
unsafe {
match (&mut I2C_BUS).as_mut().unwrap().read(ack) {
Ok(r) => Ok(r as i32),
Err(_) => Err("I2C read failed"),
}
}
}
pub fn pca954x_select(address: i32, channel: Option<u8>) -> Result<(), &'static str> {
unsafe {
if (&mut I2C_BUS).as_mut().unwrap().pca954x_select(address as u8, channel).is_err() {
Err("switch select failed")
} else {
Ok(())
}
}
}
pub fn switch_select(address: i32, mask: i32) -> Result<(), &'static str> {
let ch = match mask { //decode from mainline, PCA9548-centric API
0x00 => None,
0x01 => Some(0),
0x02 => Some(1),
0x04 => Some(2),
0x08 => Some(3),
0x10 => Some(4),
0x20 => Some(5),
0x40 => Some(6),
0x80 => Some(7),
_ => return Err("switch select supports only one channel")
};
pca954x_select(address, ch)
}
pub fn init() {
let mut i2c = libboard_zynq::i2c::I2c::i2c0();
i2c.init().expect("I2C bus initialization failed");
unsafe { I2C_BUS = Some(i2c) };
}

View File

@ -11,7 +11,6 @@ struct Registers {
}
pub struct IoExpander {
busno: i32,
port: u8,
address: i32,
iodir: [u8; 2],
@ -27,7 +26,6 @@ impl IoExpander {
// Both expanders on SHARED I2C bus
let mut io_expander = match index {
0 => IoExpander {
busno: 0,
port: 11,
address: 0x40,
iodir: [0xff; 2],
@ -41,7 +39,6 @@ impl IoExpander {
},
},
1 => IoExpander {
busno: 0,
port: 11,
address: 0x42,
iodir: [0xff; 2],
@ -77,26 +74,26 @@ impl IoExpander {
fn select(&self) -> Result<(), &'static str> {
let mask: u16 = 1 << self.port;
i2c::switch_select(self.busno, 0x70, mask as u8 as i32);
i2c::switch_select(self.busno, 0x71, (mask >> 8) as u8 as i32);
i2c::switch_select(0x70, mask as u8 as i32)?;
i2c::switch_select(0x71, (mask >> 8) as u8 as i32)?;
Ok(())
}
fn write(&self, addr: u8, value: u8) -> Result<(), &'static str> {
i2c::start(self.busno);
i2c::write(self.busno, self.address as i32);
i2c::write(self.busno, addr as i32);
i2c::write(self.busno, value as i32);
i2c::stop(self.busno);
i2c::start()?;
i2c::write(self.address as i32)?;
i2c::write(addr as i32)?;
i2c::write(value as i32)?;
i2c::stop()?;
Ok(())
}
fn check_ack(&self) -> Result<bool, &'static str> {
// Check for ack from io expander
self.select()?;
i2c::start(self.busno);
let ack = i2c::write(self.busno, self.address);
i2c::stop(self.busno);
i2c::start()?;
let ack = i2c::write(self.address)?;
i2c::stop()?;
Ok(ack)
}

View File

@ -27,6 +27,9 @@ pub mod drtioaux_async;
#[cfg(has_drtio)]
#[path = "../../../build/mem.rs"]
pub mod mem;
pub mod i2c;
#[cfg(feature = "target_kasli_soc")]
pub mod io_expander;
use core::{cmp, str};

View File

@ -1,90 +0,0 @@
use libboard_zynq;
use crate::artiq_raise;
pub static mut I2C_BUS: Option<libboard_zynq::i2c::I2c> = None;
pub extern fn start(busno: i32) {
if busno > 0 {
artiq_raise!("I2CError", "I2C bus could not be accessed");
}
unsafe {
if (&mut I2C_BUS).as_mut().unwrap().start().is_err() {
artiq_raise!("I2CError", "I2C start failed");
}
}
}
pub extern fn restart(busno: i32) {
if busno > 0 {
artiq_raise!("I2CError", "I2C bus could not be accessed");
}
unsafe {
if (&mut I2C_BUS).as_mut().unwrap().restart().is_err() {
artiq_raise!("I2CError", "I2C restart failed");
}
}
}
pub extern fn stop(busno: i32) {
if busno > 0 {
artiq_raise!("I2CError", "I2C bus could not be accessed");
}
unsafe {
if (&mut I2C_BUS).as_mut().unwrap().stop().is_err() {
artiq_raise!("I2CError", "I2C stop failed");
}
}
}
pub extern fn write(busno: i32, data: i32) -> bool {
if busno > 0 {
artiq_raise!("I2CError", "I2C bus could not be accessed");
}
unsafe {
match (&mut I2C_BUS).as_mut().unwrap().write(data as u8) {
Ok(r) => r,
Err(_) => artiq_raise!("I2CError", "I2C write failed"),
}
}
}
pub extern fn read(busno: i32, ack: bool) -> i32 {
if busno > 0 {
artiq_raise!("I2CError", "I2C bus could not be accessed");
}
unsafe {
match (&mut I2C_BUS).as_mut().unwrap().read(ack) {
Ok(r) => r as i32,
Err(_) => artiq_raise!("I2CError", "I2C read failed"),
}
}
}
pub extern fn switch_select(busno: i32, address: i32, mask: i32) {
if busno > 0 {
artiq_raise!("I2CError", "I2C bus could not be accessed");
}
let ch = match mask { //decode from mainline, PCA9548-centric API
0x00 => None,
0x01 => Some(0),
0x02 => Some(1),
0x04 => Some(2),
0x08 => Some(3),
0x10 => Some(4),
0x20 => Some(5),
0x40 => Some(6),
0x80 => Some(7),
_ => artiq_raise!("I2CError", "switch select supports only one channel")
};
unsafe {
if (&mut I2C_BUS).as_mut().unwrap().pca954x_select(address as u8, ch).is_err() {
artiq_raise!("I2CError", "switch select failed");
}
}
}
pub fn init() {
let mut i2c = libboard_zynq::i2c::I2c::i2c0();
i2c.init().expect("I2C bus initialization failed");
unsafe { I2C_BUS = Some(i2c) };
}

View File

@ -21,7 +21,7 @@ use nb;
use void::Void;
use libconfig::Config;
use libcortex_a9::l2c::enable_l2_cache;
use libboard_artiq::{logger, identifier_read, pl};
use libboard_artiq::{logger, identifier_read, pl, i2c, io_expander};
const ASYNC_ERROR_COLLISION: u8 = 1 << 0;
const ASYNC_ERROR_BUSY: u8 = 1 << 1;
@ -45,9 +45,6 @@ mod panic;
mod mgmt;
mod analyzer;
mod irq;
mod i2c;
#[cfg(feature = "target_kasli_soc")]
mod io_expander;
static mut SEEN_ASYNC_ERRORS: u8 = 0;

View File

@ -18,11 +18,12 @@ extern crate unwind;
extern crate alloc;
use libboard_zynq::{i2c::I2c, timer::GlobalTimer, time::Milliseconds, print, println, mpcore, gic, stdio};
use libboard_zynq::{timer::GlobalTimer, time::Milliseconds, print, println, mpcore, gic, stdio};
use libsupport_zynq::ram;
#[cfg(has_si5324)]
use libboard_artiq::si5324;
use libboard_artiq::{pl::csr, drtio_routing, drtioaux, logger, identifier_read};
use libboard_artiq::{pl::csr, drtio_routing, drtioaux, logger,
identifier_read, i2c, io_expander};
use libcortex_a9::{spin_lock_yield, interrupt_handler, regs::{MPIDR, SP}, notify_spin_lock, asm, l2c::enable_l2_cache};
use libregister::{RegisterW, RegisterR};
#[cfg(feature = "target_kasli_soc")]
@ -84,7 +85,7 @@ macro_rules! forward {
fn process_aux_packet(_repeaters: &mut [repeater::Repeater],
_routing_table: &mut drtio_routing::RoutingTable, _rank: &mut u8,
packet: drtioaux::Packet, timer: &mut GlobalTimer, i2c: &mut I2c) -> Result<(), drtioaux::Error> {
packet: drtioaux::Packet, timer: &mut GlobalTimer) -> Result<(), drtioaux::Error> {
// In the code below, *_chan_sel_write takes an u8 if there are fewer than 256 channels,
// and u16 otherwise; hence the `as _` conversion.
match packet {
@ -253,22 +254,22 @@ fn process_aux_packet(_repeaters: &mut [repeater::Repeater],
drtioaux::Packet::I2cStartRequest { destination: _destination, busno: _busno } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
let succeeded = i2c.start().is_ok();
let succeeded = i2c::start().is_ok();
drtioaux::send(0, &drtioaux::Packet::I2cBasicReply { succeeded: succeeded })
}
drtioaux::Packet::I2cRestartRequest { destination: _destination, busno: _busno } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
let succeeded = i2c.restart().is_ok();
let succeeded = i2c::restart().is_ok();
drtioaux::send(0, &drtioaux::Packet::I2cBasicReply { succeeded: succeeded })
}
drtioaux::Packet::I2cStopRequest { destination: _destination, busno: _busno } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
let succeeded = i2c.stop().is_ok();
let succeeded = i2c::stop().is_ok();
drtioaux::send(0, &drtioaux::Packet::I2cBasicReply { succeeded: succeeded })
}
drtioaux::Packet::I2cWriteRequest { destination: _destination, busno: _busno, data } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
match i2c.write(data) {
match i2c::write(data as i32) {
Ok(ack) => drtioaux::send(0,
&drtioaux::Packet::I2cWriteReply { succeeded: true, ack: ack }),
Err(_) => drtioaux::send(0,
@ -277,9 +278,9 @@ fn process_aux_packet(_repeaters: &mut [repeater::Repeater],
}
drtioaux::Packet::I2cReadRequest { destination: _destination, busno: _busno, ack } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet, timer);
match i2c.read(ack) {
match i2c::read(ack) {
Ok(data) => drtioaux::send(0,
&drtioaux::Packet::I2cReadReply { succeeded: true, data: data }),
&drtioaux::Packet::I2cReadReply { succeeded: true, data: data as u8 }),
Err(_) => drtioaux::send(0,
&drtioaux::Packet::I2cReadReply { succeeded: false, data: 0xff })
}
@ -298,7 +299,7 @@ fn process_aux_packet(_repeaters: &mut [repeater::Repeater],
0x80 => Some(7),
_ => { return drtioaux::send(0, &drtioaux::Packet::I2cBasicReply { succeeded: false }); }
};
let succeeded = i2c.pca954x_select(address, ch).is_ok();
let succeeded = i2c::pca954x_select(address as i32, ch).is_ok();
drtioaux::send(0, &drtioaux::Packet::I2cBasicReply { succeeded: succeeded })
}
@ -339,11 +340,11 @@ fn process_aux_packet(_repeaters: &mut [repeater::Repeater],
fn process_aux_packets(repeaters: &mut [repeater::Repeater],
routing_table: &mut drtio_routing::RoutingTable, rank: &mut u8,
timer: &mut GlobalTimer, i2c: &mut I2c) {
timer: &mut GlobalTimer) {
let result =
drtioaux::recv(0).and_then(|packet| {
if let Some(packet) = packet {
process_aux_packet(repeaters, routing_table, rank, packet, timer, i2c)
process_aux_packet(repeaters, routing_table, rank, packet, timer)
} else {
Ok(())
}
@ -447,11 +448,32 @@ pub extern fn main_core0() -> i32 {
ram::init_alloc_core0();
let mut i2c = I2c::i2c0();
i2c.init().expect("I2C initialization failed");
i2c::init();
#[cfg(feature = "target_kasli_soc")]
{
let (mut io_expander0, mut io_expander1) = (io_expander::IoExpander::new(0).unwrap(), io_expander::IoExpander::new(1).unwrap());
io_expander0.init().expect("I2C I/O expander #0 initialization failed");
io_expander1.init().expect("I2C I/O expander #1 initialization failed");
// Actively drive TX_DISABLE to false on SFP0..3
io_expander0.set_oe(0, 1 << 1).unwrap();
io_expander0.set_oe(1, 1 << 1).unwrap();
io_expander1.set_oe(0, 1 << 1).unwrap();
io_expander1.set_oe(1, 1 << 1).unwrap();
io_expander0.set(0, 1, false);
io_expander0.set(1, 1, false);
io_expander1.set(0, 1, false);
io_expander1.set(1, 1, false);
io_expander0.service().unwrap();
io_expander1.service().unwrap();
}
#[cfg(has_si5324)]
{
let mut i2c = unsafe { (&mut i2c::I2C_BUS).as_mut().unwrap() };
si5324::setup(&mut i2c, &SI5324_SETTINGS, si5324::Input::Ckin1, &mut timer).expect("cannot initialize Si5324");
}
timer.delay_us(100_000);
info!("Switching SYS clocks...");
@ -495,6 +517,7 @@ pub extern fn main_core0() -> i32 {
info!("uplink is up, switching to recovered clock");
#[cfg(has_siphaser)]
{
let mut i2c = unsafe { (&mut i2c::I2C_BUS).as_mut().unwrap() };
si5324::siphaser::select_recovered_clock(&mut i2c, true, &mut timer).expect("failed to switch clocks");
si5324::siphaser::calibrate_skew(&mut timer).expect("failed to calibrate skew");
}
@ -505,7 +528,7 @@ pub extern fn main_core0() -> i32 {
while drtiosat_link_rx_up() {
drtiosat_process_errors();
process_aux_packets(&mut repeaters, &mut routing_table, &mut rank, &mut timer, &mut i2c);
process_aux_packets(&mut repeaters, &mut routing_table, &mut rank, &mut timer);
#[allow(unused_mut)]
for mut rep in repeaters.iter_mut() {
rep.service(&routing_table, rank, &mut timer);
@ -529,8 +552,11 @@ pub extern fn main_core0() -> i32 {
drtiosat_tsc_loaded();
info!("uplink is down, switching to local oscillator clock");
#[cfg(has_siphaser)]
{
let mut i2c = unsafe { (&mut i2c::I2C_BUS).as_mut().unwrap() };
si5324::siphaser::select_recovered_clock(&mut i2c, false, &mut timer).expect("failed to switch clocks");
}
}
}
extern "C" {