Skip to content

Commit

Permalink
Gh-10 rework pin controller
Browse files Browse the repository at this point in the history
Rework to accommodate changes in the Linux subsystem by #49.
  • Loading branch information
svenrademakers committed Sep 29, 2023
1 parent 71c0126 commit d8541f5
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 151 deletions.
1 change: 1 addition & 0 deletions bmcd/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ fn init_logger() {
.with_level(level)
.with_module_level("bmcd", LevelFilter::Info)
.with_module_level("actix_http", LevelFilter::Info)
.with_module_level("h2", LevelFilter::Info)
.with_colors(true)
.env()
.init()
Expand Down
19 changes: 11 additions & 8 deletions tpi_rs/src/app/bmc_application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ impl BmcApplication {
let current = self.nodes_on.load(Ordering::Relaxed);

info!(
"toggling nodes {:#6b} to {}. reset hselfened: {}",
"toggling nodes {:#6b} to {}. reset: {}",
node_values,
if current { "off" } else { "on" },
reset_activation,
Expand Down 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 All @@ -178,13 +180,13 @@ impl BmcApplication {
}

pub async fn usb_boot(&self, node: NodeId, on: bool) -> anyhow::Result<()> {
let result = if on {
self.pin_controller.set_usb_boot(node)
let node_bits = node.to_bitfield();
let (state, mask) = if on {
(node_bits, node_bits)
} else {
self.pin_controller.clear_usb_boot()
(0u8, node_bits)
};

Ok(result?)
Ok(self.pin_controller.set_usb_boot(state, mask)?)
}

pub async fn rtl_reset(&self) -> anyhow::Result<()> {
Expand Down Expand Up @@ -238,7 +240,8 @@ impl BmcApplication {

self.activate_slot(!node.to_bitfield(), node.to_bitfield())
.await?;
self.pin_controller.clear_usb_boot()?;
self.pin_controller
.set_usb_boot(!node.to_bitfield(), node.to_bitfield())?;

sleep(REBOOT_DELAY).await;

Expand Down Expand Up @@ -278,7 +281,7 @@ impl BmcApplication {

pub fn clear_usb_boot(&self) -> anyhow::Result<()> {
self.pin_controller
.clear_usb_boot()
.set_usb_boot(0u8, 0b1111)
.context("error clearing usbboot")
}

Expand Down
40 changes: 16 additions & 24 deletions tpi_rs/src/middleware/gpio_definitions.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//! This module contains static pin numbers related to GPIO
pub const GPIO_PIN_PG: u32 = 192;
pub const GPIO_PIN_PD: u32 = 96;

pub const RTL_RESET: u32 = GPIO_PIN_PG + 13;
#[allow(unused)]
Expand All @@ -9,32 +8,25 @@ pub const SYS_RESET: u32 = GPIO_PIN_PG + 11;
pub const POWER_DETECT: u32 = GPIO_PIN_PG + 10;
#[allow(unused)]
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_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; // 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 aggregater
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_RPIBOOT: u32 = 3;
pub const PORT2_RPIBOOT: u32 = 7;
pub const PORT3_RPIBOOT: u32 = 11;
pub const PORT4_RPIBOOT: u32 = 15;
32 changes: 28 additions & 4 deletions tpi_rs/src/middleware/helpers.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,45 @@
const NODE_COUNT: u8 = 4;

/// small helper macro which handles the code duplication of declaring gpio lines.
#[macro_export]
macro_rules! gpio_output_lines {
($chip:ident, $direction:expr, $output:expr) => {
($chip:ident, $output:expr) => {
$chip
.request_lines(gpiod::Options::output($output).active($direction))
.request_lines(gpiod::Options::output($output))
.context(concat!("error initializing pin ", stringify!($output)))?
};
}

/// uses [`gpio_output_lines`] to declare an array of `gpiod::Lines` objects
#[macro_export]
macro_rules! gpio_output_array {
($chip:ident, $direction:expr, $($pin:ident),+) => {
($chip:ident, $($pin:ident),+) => {
[
$(
$crate::gpio_output_lines!($chip, $direction, [$pin])
$crate::gpio_output_lines!($chip, [$pin])
),*
]
};
}

/// Helper function that converts a bitfield + mask into an iterator. This
/// iterator iterates over each bit, and skips the bits that are not set in the
/// nodes_mask.
///
/// # Arguments
///
/// * `node_states` bit-field where each bit represents a node on the
/// turing-pi board, if bit(n) = 1 equals 'select' and bit(n) = 0 equals
/// 'unselect'.
/// * `node_mask` mask which bits to select.
///
/// # Returns
///
/// iterator returns a tuple containing the index of a bit + the new value.
pub fn bit_iterator(nodes_state: u8, nodes_mask: u8) -> impl Iterator<Item = (usize, u8)> {
(0..NODE_COUNT).filter_map(move |n| {
let mask = nodes_mask & (1 << n);
let state = (nodes_state & mask) >> n;
(mask != 0).then_some((n as usize, state))
})
}
69 changes: 37 additions & 32 deletions tpi_rs/src/middleware/pin_controller.rs
Original file line number Diff line number Diff line change
@@ -1,58 +1,60 @@
use crate::gpio_output_array;
use crate::gpio_output_lines;
use crate::middleware::helpers::bit_iterator;

use super::gpio_definitions::*;
use super::NodeId;
use super::UsbMode;
use super::UsbRoute;
use anyhow::Context;
use gpiod::Active;
use gpiod::{Chip, Lines, Output};
use log::trace;
use log::debug;
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>,
rpi_boot: [Lines<Output>; 4],
rtl_reset: Lines<Output>,
}

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,
Active::High,
chip1,
[
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,
Active::Low,
[PORT1_RPIBOOT, PORT2_RPIBOOT, PORT3_RPIBOOT, PORT4_RPIBOOT]
let rpi_boot = gpio_output_array!(
chip1,
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]);
let usb_mux = gpio_output_lines!(chip0, [USB_SEL1, USB_OE1, USB_SEL2, USB_OE2]);
let usb_switch = gpio_output_lines!(chip0, [USB_SWITCH]);
let rtl_reset = chip0
.request_lines(gpiod::Options::output([RTL_RESET]).active(gpiod::Active::Low))
.context(concat!("error initializing pin rtl reset"))?;

Ok(Self {
usb_vbus,
usb_mux,
usb_pwen,
usb_switch,
rpi_boot,
rtl_reset,
Expand All @@ -61,7 +63,7 @@ impl PinController {

/// Select which node is active in the multiplexer (see PORTx in `set_usb_route()`)
pub fn select_usb(&self, node: NodeId, mode: UsbMode) -> std::io::Result<()> {
trace!("select USB for node {:?}, mode:{:?}", node, mode);
debug!("select USB for node {:?}, mode:{:?}", node, mode);
let values: u8 = match node {
NodeId::Node1 => 0b1100,
NodeId::Node2 => 0b1101,
Expand All @@ -80,30 +82,33 @@ impl PinController {
/// Set which way the USB is routed: USB-A ↔ PORTx (`UsbRoute::UsbA`) or BMC ↔ PORTx
/// (`UsbRoute::Bmc`)
pub async fn set_usb_route(&self, route: UsbRoute) -> std::io::Result<()> {
trace!("select USB route {:?}", route);
debug!("select USB route {:?}", route);
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"enabled").await
}
UsbRoute::Bmc => {
self.usb_switch.set_values(1_u8)?;
self.usb_pwen.set_values(0_u8)
tokio::fs::write(USB_PORT_POWER, b"disabled").await
}
}
}

/// Set given nodes into usb boot mode. When powering the node on with this mode enabled, the
/// given node will boot into USB mode. Typically means that booting of eMMC is disabled.
pub fn set_usb_boot(&self, node: NodeId) -> std::io::Result<()> {
trace!("setting usbboot {:#06b}", node.to_bitfield());
self.rpi_boot.set_values(node.to_bitfield())
}
pub fn set_usb_boot(&self, nodes_state: u8, nodes_mask: u8) -> std::io::Result<()> {
let updates = bit_iterator(nodes_state, nodes_mask);

/// Clear USB boot mode of all nodes
pub fn clear_usb_boot(&self) -> std::io::Result<()> {
trace!("clearing usbboot pins");
self.rpi_boot.set_values(0_u8)
for (idx, state) in updates {
debug!(
"updating usb_boot state of node {} to {}",
idx + 1,
if state != 0 { "enable" } else { "disable" }
);
self.rpi_boot[idx].set_values(state)?;
}
Ok(())
}

pub async fn rtl_reset(&self) -> std::io::Result<()> {
Expand Down
Loading

0 comments on commit d8541f5

Please sign in to comment.