Skip to content

Commit

Permalink
Gh-10 rework pin controller
Browse files Browse the repository at this point in the history
Rework to accomodate changes in the linux subsystem by #49.
  • Loading branch information
svenrademakers committed Sep 20, 2023
1 parent b5f5579 commit 7d558dd
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 106 deletions.
2 changes: 2 additions & 0 deletions tpi_rs/src/app/bmc_application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,15 @@ impl BmcApplication {
pub async fn power_on(&self) -> anyhow::Result<()> {
let activated = self.app_db.get::<u8>(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
}

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
}

Expand Down
50 changes: 21 additions & 29 deletions tpi_rs/src/middleware/gpio_definitions.rs
Original file line number Diff line number Diff line change
@@ -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;
21 changes: 10 additions & 11 deletions tpi_rs/src/middleware/pin_controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ 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
/// time. This structure the GPIOD device library internally.
pub struct PinController {
usb_vbus: Lines<Output>,
usb_mux: Lines<Output>,
usb_pwen: Lines<Output>,
usb_switch: Lines<Output>,
rpi_boot: Lines<Output>,
rtl_reset: Lines<Output>,
Expand All @@ -27,32 +27,31 @@ impl PinController {
/// create a new Pin controller
pub fn new() -> anyhow::Result<Self> {
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,
Expand Down Expand Up @@ -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
}
}
}
Expand Down
89 changes: 23 additions & 66 deletions tpi_rs/src/middleware/power_controller.rs
Original file line number Diff line number Diff line change
@@ -1,79 +1,35 @@
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<Output>; 4],
reset: [Lines<Output>; 4],
enable: [Lines<Output>; 4],
atx: Lines<Output>,
cache: AtomicU8,
}

impl PowerController {
pub fn new() -> anyhow::Result<Self> {
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,
PORT3_RST,
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<u8> {
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
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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<bool> {
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
}
}

Expand All @@ -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
}

0 comments on commit 7d558dd

Please sign in to comment.