From 98c0e37dd0d99ea860bf660d5d5c8f54dcdea810 Mon Sep 17 00:00:00 2001 From: Matt Keeter Date: Tue, 14 Jan 2025 17:23:26 -0500 Subject: [PATCH] Add PCA9545 driver, use on Gimlet + Sidecar --- app/gimlet/rev-b.toml | 8 ++-- app/gimlet/rev-d.toml | 8 ++-- app/sidecar/base.toml | 4 +- drv/stm32xx-i2c/src/lib.rs | 1 + drv/stm32xx-i2c/src/pca9545.rs | 86 ++++++++++++++++++++++++++++++++++ 5 files changed, 97 insertions(+), 10 deletions(-) create mode 100644 drv/stm32xx-i2c/src/pca9545.rs diff --git a/app/gimlet/rev-b.toml b/app/gimlet/rev-b.toml index feb3e46b9..7c63e4864 100644 --- a/app/gimlet/rev-b.toml +++ b/app/gimlet/rev-b.toml @@ -20,7 +20,7 @@ scl.pin = 10 sda.pin = 11 af = 4 [[config.i2c.controllers.ports.B.muxes]] -driver = "pca9548" +driver = "pca9545" address = 0x73 # @@ -38,15 +38,15 @@ af = 4 # Shark fin muxes # [[config.i2c.controllers.ports.F.muxes]] -driver = "pca9548" +driver = "pca9545" address = 0x70 [[config.i2c.controllers.ports.F.muxes]] -driver = "pca9548" +driver = "pca9545" address = 0x71 [[config.i2c.controllers.ports.F.muxes]] -driver = "pca9548" +driver = "pca9545" address = 0x72 [[config.i2c.devices]] diff --git a/app/gimlet/rev-d.toml b/app/gimlet/rev-d.toml index 1f4535bc5..3a6af8f90 100644 --- a/app/gimlet/rev-d.toml +++ b/app/gimlet/rev-d.toml @@ -25,7 +25,7 @@ sda.pin = 11 af = 4 [[config.i2c.controllers.ports.B.muxes]] -driver = "pca9548" +driver = "pca9545" address = 0x73 nreset = { port = "E", pin = 15 } # SP_TO_I2C_SW_M2_A2_V3P3 @@ -44,17 +44,17 @@ af = 4 # Shark fin muxes # [[config.i2c.controllers.ports.F.muxes]] -driver = "pca9548" +driver = "pca9545" address = 0x70 nreset = { port = "E", pin = 12 } # SP_TO_I2C_SW_CEMABCD_A2_V3P3 [[config.i2c.controllers.ports.F.muxes]] -driver = "pca9548" +driver = "pca9545" address = 0x71 nreset = { port = "E", pin = 14 } # SP_TO_I2C_SW_CEMEFGH_A2_V3P3 [[config.i2c.controllers.ports.F.muxes]] -driver = "pca9548" +driver = "pca9545" address = 0x72 nreset = { port = "E" , pin = 13 } # SP_TO_I2C_SW_CEMIJ_FRU_A2_V3P3 diff --git a/app/sidecar/base.toml b/app/sidecar/base.toml index 23b1838b6..d4eb1ff91 100644 --- a/app/sidecar/base.toml +++ b/app/sidecar/base.toml @@ -360,7 +360,7 @@ description = "Northeast Corridor 0" scl = { gpio_port = "B", pin = 6 } sda = { gpio_port = "B", pin = 7 } af = 4 -muxes = [ { driver = "pca9548", address = 0x70 } ] +muxes = [ { driver = "pca9545", address = 0x70 } ] # # I2C_NORTH_EAST1_SCL @@ -428,7 +428,7 @@ description = "Northwest Corridor 1" scl.pin = 7 sda.pin = 8 af = 4 -muxes = [ { driver = "pca9548", address = 0x70 } ] +muxes = [ { driver = "pca9545", address = 0x70 } ] # # I2C4: South bend diff --git a/drv/stm32xx-i2c/src/lib.rs b/drv/stm32xx-i2c/src/lib.rs index 19e81957b..3ad18c6d9 100644 --- a/drv/stm32xx-i2c/src/lib.rs +++ b/drv/stm32xx-i2c/src/lib.rs @@ -37,6 +37,7 @@ pub type Isr = device::i2c1::isr::R; pub mod ltc4306; pub mod max7358; +pub mod pca9545; pub mod pca9548; use ringbuf::*; diff --git a/drv/stm32xx-i2c/src/pca9545.rs b/drv/stm32xx-i2c/src/pca9545.rs new file mode 100644 index 000000000..d5b345f23 --- /dev/null +++ b/drv/stm32xx-i2c/src/pca9545.rs @@ -0,0 +1,86 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Driver for the PCA9545 I2C mux + +use crate::*; +use bitfield::bitfield; +use drv_i2c_api::{ResponseCode, Segment}; + +pub struct Pca9545; + +bitfield! { + #[derive(Copy, Clone, Eq, PartialEq)] + pub struct ControlRegister(u8); + channel3_enabled, set_channel3_enabled: 3; + channel2_enabled, set_channel2_enabled: 2; + channel1_enabled, set_channel1_enabled: 1; + channel0_enabled, set_channel0_enabled: 0; +} + +impl I2cMuxDriver for Pca9545 { + fn configure( + &self, + mux: &I2cMux<'_>, + _controller: &I2cController<'_>, + gpio: &sys_api::Sys, + _ctrl: &I2cControl, + ) -> Result<(), drv_i2c_api::ResponseCode> { + mux.configure(gpio) + } + + fn enable_segment( + &self, + mux: &I2cMux<'_>, + controller: &I2cController<'_>, + segment: Option, + ctrl: &I2cControl, + ) -> Result<(), ResponseCode> { + let mut reg = ControlRegister(0); + + if let Some(segment) = segment { + match segment { + Segment::S1 => { + reg.set_channel0_enabled(true); + } + Segment::S2 => { + reg.set_channel1_enabled(true); + } + Segment::S3 => { + reg.set_channel2_enabled(true); + } + Segment::S4 => { + reg.set_channel3_enabled(true); + } + _ => { + return Err(ResponseCode::SegmentNotFound); + } + } + } + + // + // This part has but one register -- any write is to the control + // register. + // + match controller.write_read( + mux.address, + 1, + |_| Some(reg.0), + ReadLength::Fixed(0), + |_, _| Some(()), + ctrl, + ) { + Err(code) => Err(mux.error_code(code)), + _ => Ok(()), + } + } + + fn reset( + &self, + mux: &I2cMux<'_>, + gpio: &sys_api::Sys, + ) -> Result<(), drv_i2c_api::ResponseCode> { + mux.reset(gpio) + } +}