From 7d558dd2fc44c5d83343248dfde5998cf2a8d97d Mon Sep 17 00:00:00 2001 From: Sven Rademakers Date: Tue, 1 Aug 2023 11:05:01 +0100 Subject: [PATCH] Gh-10 rework pin controller Rework to accomodate changes in the linux subsystem by #49. --- tpi_rs/src/app/bmc_application.rs | 2 + tpi_rs/src/middleware/gpio_definitions.rs | 50 ++++++------- tpi_rs/src/middleware/pin_controller.rs | 21 +++--- tpi_rs/src/middleware/power_controller.rs | 89 ++++++----------------- 4 files changed, 56 insertions(+), 106 deletions(-) diff --git a/tpi_rs/src/app/bmc_application.rs b/tpi_rs/src/app/bmc_application.rs index 9a1a92c..bf3dc83 100644 --- a/tpi_rs/src/app/bmc_application.rs +++ b/tpi_rs/src/app/bmc_application.rs @@ -153,6 +153,7 @@ impl BmcApplication { pub async fn power_on(&self) -> anyhow::Result<()> { let activated = self.app_db.get::(ACTIVATED_NODES_KEY).await; self.nodes_on.store(true, Ordering::Relaxed); + self.power_controller.power_led(true).await?; self.power_controller .set_power_node(activated, activated) .await @@ -160,6 +161,7 @@ impl BmcApplication { pub async fn power_off(&self) -> anyhow::Result<()> { self.nodes_on.store(false, Ordering::Relaxed); + self.power_controller.power_led(false).await?; self.power_controller.set_power_node(0b0000, 0b1111).await } diff --git a/tpi_rs/src/middleware/gpio_definitions.rs b/tpi_rs/src/middleware/gpio_definitions.rs index 61b230d..fcc33de 100644 --- a/tpi_rs/src/middleware/gpio_definitions.rs +++ b/tpi_rs/src/middleware/gpio_definitions.rs @@ -1,47 +1,39 @@ //! This module contains pin numbers of the connected gpios. A next step would //! be to configure it in the dst as a new gpiochip - #![allow(dead_code)] #![allow(clippy::identity_op)] pub const GPIO_PIN_PG: u32 = 192; -pub const GPIO_PIN_PD: u32 = 96; pub const RTL_RESET: u32 = GPIO_PIN_PG + 13; pub const SYS_RESET: u32 = GPIO_PIN_PG + 11; pub const POWER_DETECT: u32 = GPIO_PIN_PG + 10; pub const POWER_BOARD: u32 = GPIO_PIN_PG + 15; -pub const PORT1_EN: u32 = GPIO_PIN_PD + 11; -pub const PORT2_EN: u32 = GPIO_PIN_PD + 10; -pub const PORT3_EN: u32 = GPIO_PIN_PD + 9; -pub const PORT4_EN: u32 = GPIO_PIN_PD + 8; - -pub const MODE1_EN: u32 = GPIO_PIN_PD + 7; -pub const MODE2_EN: u32 = GPIO_PIN_PD + 6; -pub const MODE3_EN: u32 = GPIO_PIN_PD + 5; -pub const MODE4_EN: u32 = GPIO_PIN_PD + 4; -pub const POWER_EN: u32 = GPIO_PIN_PD + 3; - -pub const PORT1_RST: u32 = GPIO_PIN_PD + 0; -pub const PORT2_RST: u32 = GPIO_PIN_PD + 20; -pub const PORT3_RST: u32 = GPIO_PIN_PD + 21; -pub const PORT4_RST: u32 = GPIO_PIN_PD + 22; - -pub const PORT1_USB_VBUS: u32 = GPIO_PIN_PD + 19; -pub const PORT2_USB_VBUS: u32 = GPIO_PIN_PD + 18; -pub const PORT3_USB_VBUS: u32 = GPIO_PIN_PD + 17; -pub const PORT4_USB_VBUS: u32 = GPIO_PIN_PD + 16; - -pub const PORT1_RPIBOOT: u32 = GPIO_PIN_PD + 15; -pub const PORT2_RPIBOOT: u32 = GPIO_PIN_PD + 14; -pub const PORT3_RPIBOOT: u32 = GPIO_PIN_PD + 12; -pub const PORT4_RPIBOOT: u32 = GPIO_PIN_PD + 13; - pub const USB_SEL1: u32 = GPIO_PIN_PG + 1; pub const USB_SEL2: u32 = GPIO_PIN_PG + 0; pub const USB_OE1: u32 = GPIO_PIN_PG + 2; pub const USB_OE2: u32 = GPIO_PIN_PG + 3; pub const USB_SWITCH: u32 = GPIO_PIN_PG + 5; -pub const USB_PWEN: u32 = GPIO_PIN_PG + 4; + +// gpiochip1 aggregator +pub const NODE1_USBOTG_DEV: u32 = 2; +pub const NODE2_USBOTG_DEV: u32 = 6; +pub const NODE3_USBOTG_DEV: u32 = 10; +pub const NODE4_USBOTG_DEV: u32 = 14; + +pub const PORT1_EN: u32 = 0; +pub const PORT2_EN: u32 = 4; +pub const PORT3_EN: u32 = 8; +pub const PORT4_EN: u32 = 12; + +pub const PORT1_RST: u32 = 1; +pub const PORT2_RST: u32 = 5; +pub const PORT3_RST: u32 = 9; +pub const PORT4_RST: u32 = 13; + +pub const PORT1_RPIBOOT: u32 = 3; +pub const PORT2_RPIBOOT: u32 = 7; +pub const PORT3_RPIBOOT: u32 = 11; +pub const PORT4_RPIBOOT: u32 = 15; diff --git a/tpi_rs/src/middleware/pin_controller.rs b/tpi_rs/src/middleware/pin_controller.rs index 122bcd9..863cda1 100644 --- a/tpi_rs/src/middleware/pin_controller.rs +++ b/tpi_rs/src/middleware/pin_controller.rs @@ -10,6 +10,7 @@ use gpiod::{Chip, Lines, Output}; use log::trace; use std::time::Duration; use tokio::time::sleep; +const USB_PORT_POWER: &str = "/sys/bus/platform/devices/usb-port-power/state"; /// This middleware is responsible for controlling the gpio pins on the board, which includes USB /// multiplexers. Due to hardware limitations, only one node can be connected over the USB bus at a @@ -17,7 +18,6 @@ use tokio::time::sleep; pub struct PinController { usb_vbus: Lines, usb_mux: Lines, - usb_pwen: Lines, usb_switch: Lines, rpi_boot: Lines, rtl_reset: Lines, @@ -27,32 +27,31 @@ impl PinController { /// create a new Pin controller pub fn new() -> anyhow::Result { let chip0 = Chip::new("/dev/gpiochip0").context("gpiod chip0")?; + let chip1 = Chip::new("/dev/gpiochip1").context("gpiod chip1")?; let usb_vbus = gpio_output_lines!( - chip0, + chip1, Active::High, [ - PORT1_USB_VBUS, - PORT2_USB_VBUS, - PORT3_USB_VBUS, - PORT4_USB_VBUS + NODE1_USBOTG_DEV, + NODE2_USBOTG_DEV, + NODE3_USBOTG_DEV, + NODE4_USBOTG_DEV ] ); let rpi_boot = gpio_output_lines!( - chip0, + chip1, Active::Low, [PORT1_RPIBOOT, PORT2_RPIBOOT, PORT3_RPIBOOT, PORT4_RPIBOOT] ); let usb_mux = gpio_output_lines!(chip0, Active::High, [USB_SEL1, USB_OE1, USB_SEL2, USB_OE2]); let usb_switch = gpio_output_lines!(chip0, Active::High, [USB_SWITCH]); - let usb_pwen = gpio_output_lines!(chip0, Active::Low, [USB_PWEN]); let rtl_reset = gpio_output_lines!(chip0, Active::Low, [RTL_RESET]); Ok(Self { usb_vbus, usb_mux, - usb_pwen, usb_switch, rpi_boot, rtl_reset, @@ -84,11 +83,11 @@ impl PinController { match route { UsbRoute::UsbA => { self.usb_switch.set_values(0_u8)?; - self.usb_pwen.set_values(1_u8) + tokio::fs::write(USB_PORT_POWER, b"disabled").await } UsbRoute::Bmc => { self.usb_switch.set_values(1_u8)?; - self.usb_pwen.set_values(0_u8) + tokio::fs::write(USB_PORT_POWER, b"enabled").await } } } diff --git a/tpi_rs/src/middleware/power_controller.rs b/tpi_rs/src/middleware/power_controller.rs index 91140bd..ed2c4a9 100644 --- a/tpi_rs/src/middleware/power_controller.rs +++ b/tpi_rs/src/middleware/power_controller.rs @@ -1,42 +1,27 @@ use super::{gpio_definitions::*, NodeId}; -use crate::{gpio_output_array, gpio_output_lines}; +use crate::gpio_output_array; use anyhow::Context; use gpiod::{Active, Chip, Lines, Output}; -use log::{debug, error, trace}; -use std::{ - sync::atomic::{AtomicU8, Ordering}, - time::Duration, -}; +use log::trace; +use std::time::Duration; use tokio::time::sleep; const NODE_COUNT: u8 = 4; -const SYS_LED: &str = "/sys/class/leds/fp:sys/brightness"; // This structure is a thin layer that abstracts away the interaction details // towards the power subsystem and gpio devices. pub struct PowerController { - mode: [Lines; 4], reset: [Lines; 4], enable: [Lines; 4], - atx: Lines, - cache: AtomicU8, } impl PowerController { pub fn new() -> anyhow::Result { - let chip0 = Chip::new("/dev/gpiochip0").context("gpiod chip0")?; - - let current_state = { - let inputs = chip0.request_lines(gpiod::Options::input([ - PORT1_EN, PORT2_EN, PORT3_EN, PORT4_EN, - ]))?; - inputs.get_values(0b0000u8)? - }; - - let mode = gpio_output_array!(chip0, Active::High, MODE1_EN, MODE2_EN, MODE3_EN, MODE4_EN); - let enable = gpio_output_array!(chip0, Active::Low, PORT1_EN, PORT2_EN, PORT3_EN, PORT4_EN); + let chip1 = Chip::new("/dev/gpiochip1").context("gpiod chip1")?; + let enable = + gpio_output_array!(chip1, Active::High, PORT1_EN, PORT2_EN, PORT3_EN, PORT4_EN); let reset = gpio_output_array!( - chip0, + chip1, Active::High, PORT1_RST, PORT2_RST, @@ -44,36 +29,7 @@ impl PowerController { PORT4_RST ); - let atx = gpio_output_lines!(chip0, Active::High, [POWER_EN]); - atx.set_values(0b1_u8)?; - - let cache = AtomicU8::new(current_state); - debug!("cache value:{:#06b}", cache.load(Ordering::Relaxed)); - - Ok(PowerController { - mode, - reset, - enable, - atx, - cache, - }) - } - - async fn update_state_and_atx(&self, node_states: u8, node_mask: u8) -> anyhow::Result { - let current = self - .cache - .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |current| { - Some((current & !node_mask) | (node_states & node_mask)) - }) - .expect("cas always returns Some"); - let new = self.cache.load(Ordering::Relaxed); - - if let Some(on) = need_atx_change(current, new) { - tokio::fs::write(SYS_LED, if on { "1" } else { "0" }).await?; - self.atx.set_values([on])?; - } - - Ok(new) + Ok(PowerController { reset, enable }) } /// Function to power on/off given nodes. powering of the nodes is controlled by @@ -91,10 +47,6 @@ impl PowerController { /// * `Err(io error)` in the case there was a failure to write to the linux /// subsystem that handles the node powering. pub async fn set_power_node(&self, node_states: u8, node_mask: u8) -> anyhow::Result<()> { - if let Err(e) = self.update_state_and_atx(node_states, node_mask).await { - error!("error updating atx regulator {}", e); - } - let updates = (0..NODE_COUNT).filter_map(|n| { let mask = node_mask & (1 << n); let state = (node_states & mask) >> n; @@ -103,7 +55,7 @@ impl PowerController { for (idx, state) in updates { trace!("setting power of node {}. state:{}", idx + 1, state); - self.mode[idx].set_values(state)?; + set_mode(idx + 1, state).await?; sleep(Duration::from_millis(100)).await; self.enable[idx].set_values(state)?; sleep(Duration::from_millis(100)).await; @@ -123,16 +75,10 @@ impl PowerController { self.reset[idx].set_values(1u8)?; Ok(()) } -} -/// Helper function that returns the new state of ATX power -fn need_atx_change(current_node_state: u8, next_node_state: u8) -> Option { - if current_node_state == 0 && next_node_state > 0 { - Some(true) - } else if current_node_state > 0 && next_node_state == 0 { - Some(false) - } else { - None + pub async fn power_led(&self, on: bool) -> std::io::Result<()> { + const SYS_LED: &str = "/sys/class/leds/fp:sys/brightness"; + tokio::fs::write(SYS_LED, if on { "1" } else { "off" }).await } } @@ -141,3 +87,14 @@ impl std::fmt::Debug for PowerController { write!(f, "PowerController") } } + +async fn set_mode(node_id: usize, node_state: u8) -> std::io::Result<()> { + let node_value = if node_state > 0 { + "enabled" + } else { + "disabled" + }; + + let sys_path = format!("/sys/bus/platform/devices/node{}-power/state", node_id); + tokio::fs::write(sys_path, node_value).await +}