From 5cc99b67e668f5523c24864a30cf0552bc77065c Mon Sep 17 00:00:00 2001 From: Aaron Hartwig Date: Fri, 8 Dec 2023 12:57:01 -0600 Subject: [PATCH 01/14] add support for the Medusa board --- Cargo.lock | 13 + app/medusa/Cargo.toml | 27 + app/medusa/README.md | 5 + app/medusa/base.toml | 663 +++++++++++++++++++++++ app/medusa/build.rs | 7 + app/medusa/model-a.toml | 3 + app/medusa/src/main.rs | 136 +++++ build/xtask/src/flash.rs | 12 +- drv/fpga-server/src/main.rs | 1 + drv/sidecar-front-io/build.rs | 2 +- drv/sidecar-front-io/src/leds.rs | 8 +- task/monorail-server/src/bsp/medusa_a.rs | 559 +++++++++++++++++++ task/monorail-server/src/main.rs | 1 + task/net/Cargo.toml | 1 + task/net/src/bsp/medusa_a.rs | 139 +++++ task/net/src/main.rs | 1 + 16 files changed, 1566 insertions(+), 12 deletions(-) create mode 100644 app/medusa/Cargo.toml create mode 100644 app/medusa/README.md create mode 100644 app/medusa/base.toml create mode 100644 app/medusa/build.rs create mode 100644 app/medusa/model-a.toml create mode 100644 app/medusa/src/main.rs create mode 100644 task/monorail-server/src/bsp/medusa_a.rs create mode 100644 task/net/src/bsp/medusa_a.rs diff --git a/Cargo.lock b/Cargo.lock index 9035975063..11fb6f813c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3066,6 +3066,19 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" +[[package]] +name = "medusa" +version = "0.1.0" +dependencies = [ + "build-util", + "cfg-if", + "cortex-m", + "cortex-m-rt", + "drv-stm32h7-startup", + "kern", + "stm32h7", +] + [[package]] name = "memchr" version = "2.4.1" diff --git a/app/medusa/Cargo.toml b/app/medusa/Cargo.toml new file mode 100644 index 0000000000..2d680780b8 --- /dev/null +++ b/app/medusa/Cargo.toml @@ -0,0 +1,27 @@ +[package] +edition = "2021" +readme = "README.md" +name = "medusa" +version = "0.1.0" + +[features] +dump = ["kern/dump"] + +[dependencies] +cortex-m = { workspace = true } +cortex-m-rt = { workspace = true } +cfg-if = { workspace = true } +stm32h7 = { workspace = true, features = ["rt", "stm32h753"] } + +drv-stm32h7-startup = { path = "../../drv/stm32h7-startup", features = ["h753"] } +kern = { path = "../../sys/kern" } + +[build-dependencies] +build-util = {path = "../../build/util"} + +# this lets you use `cargo fix`! +[[bin]] +name = "medusa" +test = false +doctest = false +bench = false diff --git a/app/medusa/README.md b/app/medusa/README.md new file mode 100644 index 0000000000..26f29c256f --- /dev/null +++ b/app/medusa/README.md @@ -0,0 +1,5 @@ +# Medusa Service Processor (SP) firmware + +The Medusa is a test fixture for the QSFP Front IO board. + +This folder contains the firmware that runs on its service processor (SP). diff --git a/app/medusa/base.toml b/app/medusa/base.toml new file mode 100644 index 0000000000..992036d368 --- /dev/null +++ b/app/medusa/base.toml @@ -0,0 +1,663 @@ +target = "thumbv7em-none-eabihf" +chip = "../../chips/stm32h7" +memory = "memory-large.toml" +stacksize = 896 +fwid = true + +[kernel] +name = "medusa" +requires = {flash = 24600, ram = 6256} + +[tasks.jefe] +name = "task-jefe" +priority = 0 +max-sizes = {flash = 16384, ram = 2048} +start = true +stacksize = 1536 +notifications = ["fault", "timer"] +extern-regions = ["sram2", "sram3", "sram4"] + +[tasks.jefe.config.allowed-callers] +set_reset_reason = ["sys"] +request_reset = ["hiffy"] + +[tasks.sys] +name = "drv-stm32xx-sys" +features = ["h753"] +priority = 1 +max-sizes = {flash = 2048, ram = 1024} +uses = ["rcc", "gpios1", "gpios2", "gpios3", "system_flash"] +start = true +task-slots = ["jefe"] + +[tasks.update_server] +name = "stm32h7-update-server" +priority = 3 +max-sizes = {flash = 16384, ram = 4096} +stacksize = 2048 +start = true +uses = ["flash_controller"] +extern-regions = ["bank2"] +notifications = ["flash-irq"] +interrupts = {"flash_controller.irq" = "flash-irq"} + +[caboose] +region = "flash" +size = 256 +default = true + +[tasks.caboose_reader] +name = "task-caboose-reader" +priority = 2 +max-sizes = {flash = 16384, ram = 2048} +start = true + +[tasks.hiffy] +name = "task-hiffy" +priority = 7 +max-sizes = {flash = 32768, ram = 32768} +stacksize = 2048 +start = true +task-slots = ["sys", "i2c_driver"] + +[tasks.i2c_driver] +name = "drv-stm32xx-i2c-server" +features = ["h753"] +priority = 2 +max-sizes = {flash = 16384, ram = 4096} +uses = ["i2c1", "i2c2", "i2c3", "i2c4"] +notifications = ["i2c1-irq", "i2c2-irq", "i2c3-irq", "i2c4-irq"] +start = true +task-slots = ["sys"] + +[tasks.i2c_driver.interrupts] +"i2c1.event" = "i2c1-irq" +"i2c1.error" = "i2c1-irq" +"i2c2.event" = "i2c2-irq" +"i2c2.error" = "i2c2-irq" +"i2c3.event" = "i2c3-irq" +"i2c3.error" = "i2c3-irq" +"i2c4.event" = "i2c4-irq" +"i2c4.error" = "i2c4-irq" + +[tasks.ecp5_front_io] +name = "drv-fpga-server" +features = ["front_io", "use-spi-core", "h753", "spi1"] +priority = 3 +max-sizes = {flash = 32768, ram = 8192} +stacksize = 2048 +start = true +uses = ["spi1"] +task-slots = ["sys", "i2c_driver"] +notifications = ["spi-irq"] +interrupts = {"spi1.irq" = "spi-irq"} + +# [tasks.monorail] +# name = "task-monorail-server" +# priority = 6 +# max-sizes = {flash = 262144, ram = 8192} +# features = ["mgmt", "sidecar", "vlan", "use-spi-core", "h753", "spi2"] +# stacksize = 4096 +# start = true +# # task-slots = ["ecp5_front_io", "sys", { seq = "sequencer" }] +# task-slots = ["ecp5_front_io", "sys"] +# uses = ["spi2"] +# notifications = ["spi-irq", "wake-timer"] +# interrupts = {"spi2.irq" = "spi-irq"} + +[tasks.net] +name = "task-net" +stacksize = 6040 +priority = 5 +features = ["mgmt", "h753", "medusa", "vlan", "vpd-mac", "use-spi-core", "spi3"] +max-sizes = {flash = 131072, ram = 65536, sram1 = 16384} +sections = {eth_bulk = "sram1"} +uses = ["eth", "eth_dma", "tim16", "spi3"] +start = true +notifications = ["eth-irq", "mdio-timer-irq", "spi-irq", "wake-timer"] +# task-slots = ["sys", "packrat", { seq = "sequencer" }, "jefe"] +task-slots = ["sys", "packrat", "jefe"] + +[tasks.net.interrupts] +"eth.irq" = "eth-irq" +"tim16.irq" = "mdio-timer-irq" +"spi3.irq" = "spi-irq" + +[tasks.transceivers] +name = "drv-transceivers-server" +features = ["vlan"] +priority = 6 +max-sizes = {flash = 65536, ram = 16384} +stacksize = 4096 +start = true +task-slots = [ + "i2c_driver", + "net", + "sensor", + "sys", + # "thermal", + {front_io = "ecp5_front_io"}] +# task-slots = [ +# "i2c_driver", +# "net", +# "sensor", +# "sys", +# "thermal", +# {front_io = "ecp5_front_io"}, +# {seq = "sequencer"}] +notifications = ["socket", "timer"] + +[tasks.packrat] +name = "task-packrat" +priority = 3 +max-sizes = {flash = 8192, ram = 2048} +start = true +# task-slots is explicitly empty: packrat should not send IPCs! +task-slots = [] + +[tasks.validate] +name = "task-validate" +priority = 5 +max-sizes = {flash = 16384, ram = 4096 } +stacksize = 1000 +start = true +task-slots = ["i2c_driver"] + +[tasks.sensor] +name = "task-sensor" +features = [] +priority = 4 +max-sizes = {flash = 8192, ram = 4096 } +stacksize = 1024 +start = true +notifications = ["timer"] + +[tasks.vpd] +name = "task-vpd" +priority = 3 +max-sizes = {flash = 8192, ram = 1024} +start = true +task-slots = ["sys", "i2c_driver"] +stacksize = 800 + +[tasks.idle] +name = "task-idle" +priority = 9 +max-sizes = {flash = 128, ram = 256} +stacksize = 256 +start = true + +[tasks.auxflash] +name = "drv-auxflash-server" +priority = 3 +max-sizes = {flash = 32768, ram = 4096} +features = ["h753"] +uses = ["quadspi"] +start = true +notifications = ["qspi-irq"] +interrupts = {"quadspi.irq" = "qspi-irq"} +stacksize = 3504 +task-slots = ["sys"] + +[tasks.udpecho] +name = "task-udpecho" +priority = 6 +max-sizes = {flash = 16384, ram = 8192} +stacksize = 4096 +start = true +task-slots = ["net"] +features = ["vlan"] +notifications = ["socket"] + +[tasks.udpbroadcast] +name = "task-udpbroadcast" +priority = 6 +max-sizes = {flash = 16384, ram = 8192} +stacksize = 4096 +start = true +task-slots = ["net", "packrat"] +features = ["vlan"] +notifications = ["socket"] + +# +# Configuration +# +[config] + +# +# Net +# +[config.net] +vlan = { start = 0x301, count = 2 } + +[config.net.sockets.echo] +kind = "udp" +owner = {name = "udpecho", notification = "socket"} +port = 7 +tx = { packets = 3, bytes = 1024 } +rx = { packets = 3, bytes = 1024 } + +[config.net.sockets.broadcast] +kind = "udp" +owner = {name = "udpbroadcast", notification = "socket"} +port = 997 +tx = { packets = 3, bytes = 1024 } +rx = { packets = 3, bytes = 1024 } + +[config.net.sockets.transceivers] +kind = "udp" +owner = {name = "transceivers", notification = "socket"} +port = 11112 +tx = { packets = 3, bytes = 2048 } +rx = { packets = 3, bytes = 2048 } + +# +# Auxflash +# +[config.auxflash] +memory-size = 33_554_432 # 256 Mib / 32 MiB +slot-count = 16 # 2 MiB slots + +[[auxflash.blobs]] +file = "drv/sidecar-front-io/sidecar_qsfp_x32_controller_rev_b_c.bit" +compress = true +tag = "QSFP" + +# +# I2C1: Primary Bus +# +[[config.i2c.controllers]] +controller = 1 + +[config.i2c.controllers.ports.B1] +name = "primary" +description = "Primary I2C bus to all on-board devices" +scl = { gpio_port = "B", pin = 6 } # I2C_SP_TO_ALL_SCL +sda = { gpio_port = "B", pin = 7 } # I2C_SP_TO_ALL_SDA +af = 4 + +[[config.i2c.devices]] +bus = "primary" +address = 0b1010_100 +device = "ltc4282" +description = "Front I/O hotswap controller" +power = { rails = [ "V12_SYS" ], pmbus = false } +sensors = { voltage = 1, current = 1 } +refdes = "U1" + +[[config.i2c.devices]] +bus = "primary" +address = 0b1010_000 +device = "at24csw080" +description = "Medusa FRUID" +name = "local_vpd" +refdes = "U9" + +[[config.i2c.devices]] +bus = "primary" +address = 0b0011_001 +device = "tps546b24a" +description = "V5P0_SYS rail" +power = { rails = [ "V5P0_SYS" ] } +sensors = { temperature = 1, voltage = 1, current = 1 } +refdes = "U10" + +[[config.i2c.devices]] +bus = "primary" +address = 0b0011_010 +device = "tps546b24a" +description = "V3P3_SYS rail" +power = { rails = [ "V3P3_SYS" ] } +sensors = { temperature = 1, voltage = 1, current = 1 } +refdes = "U11" + +[[config.i2c.devices]] +bus = "primary" +address = 0b0011_100 +device = "tps546b24a" +description = "V1P8_SYS rail" +power = { rails = [ "V1P8_SYS" ] } +sensors = { temperature = 1, voltage = 1, current = 1 } +refdes = "U12" + +[[config.i2c.devices]] +bus = "primary" +address = 0b0011_011 +device = "tps546b24a" +description = "V1P0_MGMT rail" +power = { rails = [ "V1P0_MGMT" ] } +sensors = { temperature = 1, voltage = 1, current = 1 } +refdes = "U13" + +[[config.i2c.devices]] +bus = "primary" +address = 0b1000_000 +device = "ltc4282" +description = "Front I/O hotswap controller" +power = { rails = [ "V12_QSFP_OUT" ], pmbus = false } +sensors = { voltage = 1, current = 1 } +refdes = "U16" + +# +# I2C2: Front I/O +# +[[config.i2c.controllers]] +controller = 2 +[config.i2c.controllers.ports.F] +name = "front_io" +description = "Front I/O Board" +scl.pin = 1 # I2C_FRONT_IO0_SCL +sda.pin = 0 # I2C_FRONT_IO0_SDA +af = 4 + +[[config.i2c.devices]] +bus = "front_io" +address = 0b1010_000 +device = "at24csw080" +description = "Front IO board FRUID" +removable = true + +[[config.i2c.devices]] +bus = "front_io" +address = 0b1110_011 +device = "pca9538" +description = "Front IO GPIO expander" +removable = true + +[[config.i2c.devices]] +bus = "front_io" +address = 0b0001_010 +device = "pca9956b" +name = "front_leds_left" +description = "Front IO LED driver (left)" +removable = true +refdes = "U5" + +[[config.i2c.devices]] +bus = "front_io" +address = 0b0001_011 +device = "pca9956b" +name = "front_leds_right" +description = "Front IO LED driver (right)" +removable = true +refdes = "U6" + +[[config.i2c.devices]] +bus = "front_io" +address = 0b0011_011 +device = "tps546b24a" +description = "Front IO V3P3_SYS_A2 rail" +removable = true +power = {rails = ["V3P3_SYS_A2"] } +sensors = { temperature = 1, voltage = 1, current = 1 } +refdes = "J61_U7" # on front IO board + +[[config.i2c.devices]] +bus = "front_io" +address = 0b0011_001 +device = "tps546b24a" +description = "Front IO V3P3_QSFP0_A0 rail" +removable = true +power = {rails = ["V3P3_QSFP0_A0"] } +sensors = { temperature = 1, voltage = 1, current = 1 } +refdes = "J61_U15" # on front IO board + +[[config.i2c.devices]] +bus = "front_io" +address = 0b0011_010 +device = "tps546b24a" +description = "Front IO V3P3_QSFP1_A0 rail" +removable = true +power = {rails = ["V3P3_QSFP1_A0"] } +sensors = { temperature = 1, voltage = 1, current = 1 } +refdes = "J61_U18" # on front IO board + +# +# SPI1: Front IO FPGAs +# +[config.spi.spi1] +controller = 1 + +[config.spi.spi1.mux_options.port_adg] +outputs = [ + {port = "A", pins = [5], af = 5}, # SPI_SP_TO_FRONT_IO_SCK + {port = "D", pins = [7], af = 5}, # SPI_SP_TO_FRONT_IO_MOSI +] +input = {port = "G", pin = 9, af = 5} # SPI_SP_TO_FRONT_IO_MISO + +[config.spi.spi1.devices.ecp5_front_io_fpga] +mux = "port_adg" +cs = [{port = "G", pin = 10}] # SPI_SP_TO_FRONT_IO_CS_CFG_L + +[config.spi.spi1.devices.ecp5_front_io_user_design] +mux = "port_adg" +cs = [{port = "A", pin = 15}] # SPI_SP_TO_FRONT_IO_CS_USER_L + +# +# SPI2: VSC7448 +# +[config.spi.spi2] +controller = 2 + +[config.spi.spi2.mux_options.port_i] +outputs = [ + # SPI_SP_TO_MGMT_SCK, SPI_SP_TO_MGMT_MOSI + {port = "I", pins = [1, 3], af = 5}, +] +input = {port = "I", pin = 2, af = 5} # SPI_SP_TO_MGMT_MISO + +[config.spi.spi2.devices.vsc7448] +mux = "port_i" +cs = [{port = "I", pin = 0}] # SPI_SP_TO_MGMT_CS_L + +# +# SPI3: KSZ8463 +# +[config.spi.spi3] +controller = 3 + +[config.spi.spi3.mux_options.port_c] +outputs = [ + # SPI_SP_TO_EPE_SCK, SPI_SP_TO_EPE_MOSI + {port = "C", pins = [10, 12], af = 6}, +] +input = {port = "C", pin = 11, af = 6} # SPI_SP_TO_EPE_MISO + +[config.spi.spi3.devices.ksz8463] +mux = "port_c" +cs = [{port = "A", pin = 4}] # SPI_SP_TO_EPE_CS_L + +# +# QSFP Temperature Sensors +# +[[config.sensor.devices]] +name = "xcvr0" +device = "qsfp" +description = "QSFP transceiver 0" +sensors.temperature = 1 + +[[config.sensor.devices]] +name = "xcvr1" +device = "qsfp" +description = "QSFP transceiver 1" +sensors.temperature = 1 + +[[config.sensor.devices]] +name = "xcvr2" +device = "qsfp" +description = "QSFP transceiver 2" +sensors.temperature = 1 + +[[config.sensor.devices]] +name = "xcvr3" +device = "qsfp" +description = "QSFP transceiver 3" +sensors.temperature = 1 + +[[config.sensor.devices]] +name = "xcvr4" +device = "qsfp" +description = "QSFP transceiver 4" +sensors.temperature = 1 + +[[config.sensor.devices]] +name = "xcvr5" +device = "qsfp" +description = "QSFP transceiver 5" +sensors.temperature = 1 + +[[config.sensor.devices]] +name = "xcvr6" +device = "qsfp" +description = "QSFP transceiver 6" +sensors.temperature = 1 + +[[config.sensor.devices]] +name = "xcvr7" +device = "qsfp" +description = "QSFP transceiver 7" +sensors.temperature = 1 + +[[config.sensor.devices]] +name = "xcvr8" +device = "qsfp" +description = "QSFP transceiver 8" +sensors.temperature = 1 + +[[config.sensor.devices]] +name = "xcvr9" +device = "qsfp" +description = "QSFP transceiver 9" +sensors.temperature = 1 + +[[config.sensor.devices]] +name = "xcvr10" +device = "qsfp" +description = "QSFP transceiver 10" +sensors.temperature = 1 + +[[config.sensor.devices]] +name = "xcvr11" +device = "qsfp" +description = "QSFP transceiver 11" +sensors.temperature = 1 + +[[config.sensor.devices]] +name = "xcvr12" +device = "qsfp" +description = "QSFP transceiver 12" +sensors.temperature = 1 + +[[config.sensor.devices]] +name = "xcvr13" +device = "qsfp" +description = "QSFP transceiver 13" +sensors.temperature = 1 + +[[config.sensor.devices]] +name = "xcvr14" +device = "qsfp" +description = "QSFP transceiver 14" +sensors.temperature = 1 + +[[config.sensor.devices]] +name = "xcvr15" +device = "qsfp" +description = "QSFP transceiver 15" +sensors.temperature = 1 + +[[config.sensor.devices]] +name = "xcvr16" +device = "qsfp" +description = "QSFP transceiver 16" +sensors.temperature = 1 + +[[config.sensor.devices]] +name = "xcvr17" +device = "qsfp" +description = "QSFP transceiver 17" +sensors.temperature = 1 + +[[config.sensor.devices]] +name = "xcvr18" +device = "qsfp" +description = "QSFP transceiver 18" +sensors.temperature = 1 + +[[config.sensor.devices]] +name = "xcvr19" +device = "qsfp" +description = "QSFP transceiver 19" +sensors.temperature = 1 + +[[config.sensor.devices]] +name = "xcvr20" +device = "qsfp" +description = "QSFP transceiver 20" +sensors.temperature = 1 + +[[config.sensor.devices]] +name = "xcvr21" +device = "qsfp" +description = "QSFP transceiver 21" +sensors.temperature = 1 + +[[config.sensor.devices]] +name = "xcvr22" +device = "qsfp" +description = "QSFP transceiver 22" +sensors.temperature = 1 + +[[config.sensor.devices]] +name = "xcvr23" +device = "qsfp" +description = "QSFP transceiver 23" +sensors.temperature = 1 + +[[config.sensor.devices]] +name = "xcvr24" +device = "qsfp" +description = "QSFP transceiver 24" +sensors.temperature = 1 + +[[config.sensor.devices]] +name = "xcvr25" +device = "qsfp" +description = "QSFP transceiver 25" +sensors.temperature = 1 + +[[config.sensor.devices]] +name = "xcvr26" +device = "qsfp" +description = "QSFP transceiver 26" +sensors.temperature = 1 + +[[config.sensor.devices]] +name = "xcvr27" +device = "qsfp" +description = "QSFP transceiver 27" +sensors.temperature = 1 + +[[config.sensor.devices]] +name = "xcvr28" +device = "qsfp" +description = "QSFP transceiver 28" +sensors.temperature = 1 + +[[config.sensor.devices]] +name = "xcvr29" +device = "qsfp" +description = "QSFP transceiver 29" +sensors.temperature = 1 + +[[config.sensor.devices]] +name = "xcvr30" +device = "qsfp" +description = "QSFP transceiver 30" +sensors.temperature = 1 + +[[config.sensor.devices]] +name = "xcvr31" +device = "qsfp" +description = "QSFP transceiver 31" +sensors.temperature = 1 diff --git a/app/medusa/build.rs b/app/medusa/build.rs new file mode 100644 index 0000000000..2800895934 --- /dev/null +++ b/app/medusa/build.rs @@ -0,0 +1,7 @@ +// 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/. + +fn main() { + build_util::expose_target_board(); +} diff --git a/app/medusa/model-a.toml b/app/medusa/model-a.toml new file mode 100644 index 0000000000..0531527cbd --- /dev/null +++ b/app/medusa/model-a.toml @@ -0,0 +1,3 @@ +name = "medusa-a" +board = "medusa-a" +inherit = "base.toml" diff --git a/app/medusa/src/main.rs b/app/medusa/src/main.rs new file mode 100644 index 0000000000..3beb4e06d0 --- /dev/null +++ b/app/medusa/src/main.rs @@ -0,0 +1,136 @@ +// 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/. + +#![no_std] +#![no_main] + +// We have to do this if we don't otherwise use it to ensure its vector table +// gets linked in. +extern crate stm32h7; + +use stm32h7::stm32h753 as device; + +use drv_stm32h7_startup::ClockConfig; + +use cortex_m_rt::entry; + +#[entry] +fn main() -> ! { + system_init(); + + const CYCLES_PER_MS: u32 = 400_000; + + #[cfg(feature = "traptrace")] + kern::profiling::configure_events_table(tracing::table()); + + unsafe { kern::startup::start_kernel(CYCLES_PER_MS) } +} + +fn system_init() { + let cp = cortex_m::Peripherals::take().unwrap(); + let p = device::Peripherals::take().unwrap(); + + // Check the package we've been flashed on. Medusa boards use BGA240. + // Gimletlet boards are very similar but use QFPs. This is designed to fail + // a Medusa firmware that was accidentally flashed onto a Gimletlet. + // + // We need to turn the SYSCFG block on to do this. + p.RCC.apb4enr.modify(|_, w| w.syscfgen().enabled()); + cortex_m::asm::dsb(); + // Now, we can read the appropriately-named package register to find out + // what package we're on. + match p.SYSCFG.pkgr.read().pkg().bits() { + 0b1000 => { + // TFBGA240, yay + } + _ => { + // uh + panic!(); + } + } + + // We read the board model very early in boot to try and detect the + // firmware being flashed on the wrong board. In particular, we read the + // model _before_ setting up the clock tree below, just in case we change + // the crystal configuration in a subsequent rev. + // + // Note that the firmware _does not_ adapt to different board models. We + // still require different firmware per model; this check serves to detect + // if you've flashed the wrong one, only. + // + // The model is on the following pins: + // - ID0: PE6 + // - ID1: PE7 + // - ID2: PE8 + // Un-gate the clock to GPIO bank E. + p.RCC.ahb4enr.modify(|_, w| w.gpiogen().set_bit()); + cortex_m::asm::dsb(); + // PE8:6 are already inputs after reset + #[rustfmt::skip] + p.GPIOG.moder.modify(|_, w| w + .moder6().input() + .moder7().input() + .moder8().input()); + + // Unlike other designs, we aren't using any internal pullup resistors, so + // we won't wait for the inputs or ID traces to charge. + + let id_mask = (1 << 6) | (1 << 7) | (1 << 8); + let model = p.GPIOE.idr.read().bits() & id_mask; + + cfg_if::cfg_if! { + if #[cfg(target_board = "medusa-a")] { + let expected_model = 0b000; + } else { + compile_error!("not a recognized medua board"); + } + } + + assert_eq!(model, expected_model); + + drv_stm32h7_startup::system_init_custom( + cp, + p, + ClockConfig { + source: drv_stm32h7_startup::ClockSource::ExternalCrystal, + // 8MHz HSE freq is within VCO input range of 2-16, so, DIVM=1 to bypass + // the prescaler. + divm: 1, + // VCO must tolerate an 8MHz input range: + vcosel: device::rcc::pllcfgr::PLL1VCOSEL_A::WIDEVCO, + pllrange: device::rcc::pllcfgr::PLL1RGE_A::RANGE8, + // DIVN governs the multiplication of the VCO input frequency to produce + // the intermediate frequency. We want an IF of 800MHz, or a + // multiplication of 100x. + // + // We subtract 1 to get the DIVN value because the PLL effectively adds + // one to what we write. + divn: 100 - 1, + // P is the divisor from the VCO IF to the system frequency. We want + // 400MHz, so: + divp: device::rcc::pll1divr::DIVP1_A::DIV2, + // Q produces kernel clocks; we set it to 200MHz: + divq: 4 - 1, + // R is mostly used by the trace unit and we leave it fast: + divr: 2 - 1, + + // We run the CPU at the full core rate of 400MHz: + cpu_div: device::rcc::d1cfgr::D1CPRE_A::DIV1, + // We down-shift the AHB by a factor of 2, to 200MHz, to meet its + // constraints: + ahb_div: device::rcc::d1cfgr::HPRE_A::DIV2, + // We configure all APB for 100MHz. These are relative to the AHB + // frequency. + apb1_div: device::rcc::d2cfgr::D2PPRE1_A::DIV2, + apb2_div: device::rcc::d2cfgr::D2PPRE2_A::DIV2, + apb3_div: device::rcc::d1cfgr::D1PPRE_A::DIV2, + apb4_div: device::rcc::d3cfgr::D3PPRE_A::DIV2, + + // Flash runs at 200MHz: 2WS, 2 programming cycles. See reference manual + // Table 13. + flash_latency: 2, + flash_write_delay: 2, + }, + ); +} diff --git a/build/xtask/src/flash.rs b/build/xtask/src/flash.rs index 49b47f57e6..45760615f7 100644 --- a/build/xtask/src/flash.rs +++ b/build/xtask/src/flash.rs @@ -174,9 +174,9 @@ pub fn config( "stm32f3-discovery" | "stm32f4-discovery" | "nucleo-h743zi2" | "nucleo-h753zi" | "stm32h7b3i-dk" | "gemini-bu-1" | "gimletlet-1" | "gimletlet-2" | "gimlet-b" | "gimlet-c" | "gimlet-d" | "gimlet-e" - | "gimlet-f" | "psc-a" | "psc-b" | "psc-c" | "sidecar-b" - | "sidecar-c" | "sidecar-d" | "stm32g031-nucleo" | "donglet-g030" - | "donglet-g031" | "oxcon2023g0" | "stm32g070" | "stm32g0b1" => { + | "psc-a" | "psc-b" | "psc-c" | "sidecar-b" | "sidecar-c" | "sidecar-d" + | "stm32g031-nucleo" | "donglet-g030" | "donglet-g031" + | "oxcon2023g0" | "stm32g070" | "stm32g0b1" | "medusa-a"=> { let cfg = FlashProgramConfig::new(chip_dir.join("openocd.cfg")); let mut flash = FlashConfig::new(FlashProgram::OpenOcd(cfg)); @@ -212,10 +212,8 @@ pub fn chip_name(board: &str) -> anyhow::Result<&'static str> { "nucleo-h753zi" => "STM32H753ZITx", "stm32h7b3i-dk" => "STM32H7B3IITx", "gemini-bu-1" | "gimletlet-1" | "gimletlet-2" | "gimlet-b" - | "gimlet-c" | "gimlet-d" | "gimlet-e" | "gimlet-f" | "psc-a" - | "psc-b" | "psc-c" | "sidecar-b" | "sidecar-c" | "sidecar-d" => { - "STM32H753ZITx" - } + | "gimlet-c" | "gimlet-d" | "gimlet-e" | "psc-a" | "psc-b" + | "psc-c" | "sidecar-b" | "sidecar-c" | "sidecar-d" | "medusa-a" => "STM32H753ZITx", "donglet-g030" => "STM32G030F6Px", "donglet-g031" => "STM32G031F8Px", "stm32g031-nucleo" => "STM32G031Y8Yx", diff --git a/drv/fpga-server/src/main.rs b/drv/fpga-server/src/main.rs index 563d38d8af..f42f1c95f3 100644 --- a/drv/fpga-server/src/main.rs +++ b/drv/fpga-server/src/main.rs @@ -91,6 +91,7 @@ fn main() -> ! { } else if #[cfg(all(any(target_board = "sidecar-b", target_board = "sidecar-c", target_board = "sidecar-d"), + target_board = "medusa-a"), feature = "front_io"))] { let configuration_port = spi.device(drv_spi_api::devices::ECP5_FRONT_IO_FPGA); diff --git a/drv/sidecar-front-io/build.rs b/drv/sidecar-front-io/build.rs index 98ba6465c2..6495e99645 100644 --- a/drv/sidecar-front-io/build.rs +++ b/drv/sidecar-front-io/build.rs @@ -9,7 +9,7 @@ fn main() -> Result<(), Box> { build_util::expose_target_board(); let board = build_util::env_var("HUBRIS_BOARD")?; - if board != "sidecar-b" && board != "sidecar-c" && board != "sidecar-d" { + if board != "sidecar-b" && board != "sidecar-c" && board != "sidecar-d" && board != "medusa-a" { panic!("unknown target board"); } diff --git a/drv/sidecar-front-io/src/leds.rs b/drv/sidecar-front-io/src/leds.rs index ffd73d1afe..c5e1aceee2 100644 --- a/drv/sidecar-front-io/src/leds.rs +++ b/drv/sidecar-front-io/src/leds.rs @@ -188,7 +188,7 @@ const LED_MAP: LedMap = LedMap([ controller: LedController::Left, output: 1, }, - #[cfg(any(target_board = "sidecar-c", target_board = "sidecar-d"))] + #[cfg(any(target_board = "sidecar-c", target_board = "sidecar-d", target_board = "medusa-a"))] // Port 16 LedLocation { controller: LedController::Left, @@ -200,7 +200,7 @@ const LED_MAP: LedMap = LedMap([ controller: LedController::Left, output: 3, }, - #[cfg(any(target_board = "sidecar-c", target_board = "sidecar-d"))] + #[cfg(any(target_board = "sidecar-c", target_board = "sidecar-d", target_board = "medusa-a"))] // Port 17 LedLocation { controller: LedController::Left, @@ -212,7 +212,7 @@ const LED_MAP: LedMap = LedMap([ controller: LedController::Left, output: 5, }, - #[cfg(any(target_board = "sidecar-c", target_board = "sidecar-d"))] + #[cfg(any(target_board = "sidecar-c", target_board = "sidecar-d", target_board = "medusa-a"))] // Port 18 LedLocation { controller: LedController::Left, @@ -224,7 +224,7 @@ const LED_MAP: LedMap = LedMap([ controller: LedController::Left, output: 7, }, - #[cfg(any(target_board = "sidecar-c", target_board = "sidecar-d"))] + #[cfg(any(target_board = "sidecar-c", target_board = "sidecar-d", target_board = "medusa-a"))] // Port 19 LedLocation { controller: LedController::Left, diff --git a/task/monorail-server/src/bsp/medusa_a.rs b/task/monorail-server/src/bsp/medusa_a.rs new file mode 100644 index 0000000000..62505cc2c7 --- /dev/null +++ b/task/monorail-server/src/bsp/medusa_a.rs @@ -0,0 +1,559 @@ +// 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/. + +use drv_sidecar_front_io::phy_smi::PhySmi; +use ringbuf::*; +use userlib::{hl::sleep_for, task_slot}; +use vsc7448::{ + config::Speed, miim_phy::Vsc7448MiimPhy, Vsc7448, Vsc7448Rw, VscError, +}; +use vsc7448_pac::{DEVCPU_GCB, HSIO, VAUI0, VAUI1}; +use vsc85xx::{vsc8504::Vsc8504, vsc8562::Vsc8562Phy, PhyRw}; + +task_slot!(FRONT_IO, ecp5_front_io); + +/// Interval at which `Bsp::wake()` is called by the main loop +const WAKE_INTERVAL_MS: u64 = 500; +pub const WAKE_INTERVAL: Option = Some(WAKE_INTERVAL_MS); + +#[derive(Copy, Clone, PartialEq)] +enum Trace { + None, + FrontIoSpeedChange { + port: u8, + before: Speed, + after: Speed, + }, + FrontIoPhyOscillatorBad, + AnegCheckFailed(VscError), + Restarted10GAneg, + Reinit, +} +ringbuf!(Trace, 16, Trace::None); + +//////////////////////////////////////////////////////////////////////////////// + +pub struct Bsp<'a, R> { + vsc7448: &'a Vsc7448<'a, R>, + + /// PHY for the on-board PHY ("PHY4") + vsc8504: Vsc8504, + + /// RPC handle for the front IO board's PHY, which is a VSC8562. This is + /// used for PHY control via a Rube Goldberg machine of + /// Hubris RPC -> SPI -> FPGA -> MDIO -> PHY + /// + /// This is `None` if the front IO board isn't connected. + vsc8562: Option, + + /// Configured speed of ports on the front IO board, from the perspective of + /// the VSC7448. + /// + /// They are initially configured to 1G, but the VSC8562 PHY may + /// autonegotiate to a different speed, in which case we have to reconfigure + /// the port on the VSC7448 to match. + front_io_speed: [Speed; 2], + + /// Time at which the 10G link went down + link_down_at: Option, +} + +pub const REFCLK_SEL: vsc7448::RefClockFreq = + vsc7448::RefClockFreq::Clk156p25MHz; +pub const REFCLK2_SEL: Option = None; + +mod map { + // Local module to avoid leaking imports + use vsc7448::config::{ + PortMap, + PortMode::{self, *}, + Speed::*, + }; + const SGMII: Option = Some(Sgmii(Speed100M)); + const QSGMII_100M: Option = Some(Qsgmii(Speed100M)); + const QSGMII_1G: Option = Some(Qsgmii(Speed1G)); + const BASE_KR: Option = Some(BaseKr); + + // See RFD144 for a detailed look at the design + pub const PORT_MAP: PortMap = PortMap::new([ + SGMII, // 0 | DEV1G_0 | SERDES1G_1 | Cubby 0 + SGMII, // 1 | DEV1G_1 | SERDES1G_2 | Cubby 1 + SGMII, // 2 | DEV1G_2 | SERDES1G_3 | Cubby 2 + SGMII, // 3 | DEV1G_3 | SERDES1G_4 | Cubby 3 + SGMII, // 4 | DEV1G_4 | SERDES1G_5 | Cubby 4 + SGMII, // 5 | DEV1G_5 | SERDES1G_6 | Cubby 5 + SGMII, // 6 | DEV1G_6 | SERDES1G_7 | Cubby 6 + SGMII, // 7 | DEV1G_7 | SERDES1G_8 | Cubby 7 + SGMII, // 8 | DEV2G5_0 | SERDES6G_0 | Cubby 8 + SGMII, // 9 | DEV2G5_1 | SERDES6G_1 | Cubby 9 + SGMII, // 10 | DEV2G5_2 | SERDES6G_2 | Cubby 10 + SGMII, // 11 | DEV2G5_3 | SERDES6G_3 | Cubby 11 + SGMII, // 12 | DEV2G5_4 | SERDES6G_4 | Cubby 12 + SGMII, // 13 | DEV2G5_5 | SERDES6G_5 | Cubby 13 + SGMII, // 14 | DEV2G5_6 | SERDES6G_6 | Cubby 14 + SGMII, // 15 | DEV2G5_7 | SERDES6G_7 | Cubby 15 + SGMII, // 16 | DEV2G5_8 | SERDES6G_8 | Cubby 16 + SGMII, // 17 | DEV2G5_9 | SERDES6G_9 | Cubby 17 + SGMII, // 18 | DEV2G5_10 | SERDES6G_10 | Cubby 18 + SGMII, // 19 | DEV2G5_11 | SERDES6G_11 | Cubby 19 + SGMII, // 20 | DEV2G5_12 | SERDES6G_12 | Cubby 20 + SGMII, // 21 | DEV2G5_13 | SERDES6G_13 | Cubby 21 + None, // 22 + None, // 23 + SGMII, // 24 | DEV2G5_16 | SERDES6G_16 | Cubby 22 + SGMII, // 25 | DEV2G5_17 | SERDES6G_17 | Cubby 23 + SGMII, // 26 | DEV2G5_18 | SERDES6G_18 | Cubby 24 + SGMII, // 27 | DEV2G5_19 | SERDES6G_19 | Cubby 25 + SGMII, // 28 | DEV2G5_20 | SERDES6G_20 | Cubby 26 + SGMII, // 29 | DEV2G5_21 | SERDES6G_21 | Cubby 27 + SGMII, // 30 | DEV2G5_22 | SERDES6G_22 | Cubby 28 + SGMII, // 31 | DEV2G5_23 | SERDES6G_23 | Cubby 29 + None, // 32 + None, // 33 + None, // 34 + None, // 35 + None, // 36 + None, // 37 + None, // 38 + None, // 39 + QSGMII_100M, // 40 | DEV1G_16 | SERDES6G_14 | Peer SP + QSGMII_100M, // 41 | DEV1G_17 | SERDES6G_14 | PSC0 + QSGMII_100M, // 42 | DEV1G_18 | SERDES6G_14 | PSC1 + QSGMII_100M, // 43 | Unused + QSGMII_1G, // 44 | DEV1G_20 | SERDES6G_15 | Technician 1 + QSGMII_1G, // 45 | DEV1G_21 | SERDES6G_15 | Technician 2 + None, // 46 | Unused (configured in QSGMII mode by port 44) + None, // 47 | Unused (configured in QSGMII mode by port 44) + SGMII, // 48 | DEV2G5_24 | SERDES1G_0 | Local SP + BASE_KR, // 49 | DEV10G_0 | SERDES10G_0 | Tofino 2 + None, // 50 | Unused + SGMII, // 51 | DEV2G5_27 | SERDES10G_2 | Cubby 30 (shadows DEV10G_2) + SGMII, // 52 | DEV2G5_28 | SERDES10G_3 | Cubby 31 (shadows DEV10G_3) + ]); +} + +impl<'a, R: Vsc7448Rw> Bsp<'a, R> { + /// Constructs and initializes a new BSP handle + pub fn new(vsc7448: &'a Vsc7448<'a, R>) -> Result { + let has_front_io = seq.front_io_board_present(); + let mut out = Bsp { + vsc7448, + vsc8504: Vsc8504::empty(), + vsc8562: if has_front_io { + Some(PhySmi::new(FRONT_IO.get_task_id())) + } else { + None + }, + front_io_speed: [Speed::Speed1G; 2], + link_down_at: None, + }; + + out.reinit()?; + Ok(out) + } + + fn preinit() { + // TODO + } + + pub fn reinit(&mut self) -> Result<(), VscError> { + ringbuf_entry!(Trace::Reinit); + self.vsc7448.init()?; + + // By default, the SERDES6G are grouped into 4x chunks for XAUI, + // where a single DEV10G runs 4x SERDES6G at 2.5G. This leads to very + // confusing behavior when only running a few SERDES6G: in particularly, + // we noticed that SERDES6G_14 seemed to depend on SERDES6G_12. + // + // We're never using this "lane sync" feature, so disable it everywhere. + for i in 0..=1 { + self.vsc7448.modify( + VAUI0().VAUI_CHANNEL_CFG().VAUI_CHANNEL_CFG(i), + |r| { + r.set_lane_sync_ena(0); + }, + )?; + self.vsc7448.modify( + VAUI1().VAUI_CHANNEL_CFG().VAUI_CHANNEL_CFG(i), + |r| { + r.set_lane_sync_ena(0); + }, + )?; + } + + // We must disable frame copying before configuring ports; otherwise, a + // rare failure mode can result in queues getting stuck (forever!). We + // disable frame copying by enabling VLANs, then removing all ports from + // them! + // + // (ports will be added back to VLANs after configuration is done, in + // the call to `configure_vlan_semistrict` below) + // + // The root cause is unknown, but we suspect a hardware race condition + // in the switch IC; see this issue for detailed discussion: + // https://github.com/oxidecomputer/hubris/issues/1399 + self.vsc7448.configure_vlan_none()?; + + // Reset internals + self.vsc8504 = Vsc8504::empty(); + self.front_io_speed = [Speed::Speed1G; 2]; + + self.phy_vsc8504_init()?; + + self.vsc7448.configure_ports_from_map(&PORT_MAP)?; + self.vsc7448.configure_vlan_semistrict()?; + self.vsc7448_postconfig()?; + + // Some front IO boards have a faulty oscillator driving the PHY, + // causing its clock to misbehave some fraction of (re-)boots. Init + // the PHY in a loop, requesting the sequencer to reset as much as + // necessary to try and correct the problem. + let mut osc_good = false; + + while self.vsc8562.is_some() && !osc_good { + self.phy_vsc8562_init()?; + + osc_good = self.is_front_io_link_good()?; + + // Notify the sequencer about the state of the oscillator. If the + // oscillator is good any future resets of the PHY do not require a + // full power cycle of the front IO board. + self.seq + .set_front_io_phy_osc_state(osc_good) + .map_err(|e| VscError::ProxyError(e.into()))?; + + if !osc_good { + ringbuf_entry!(Trace::FrontIoPhyOscillatorBad) + } + } + + if let Some(phy_rw) = &mut self.vsc8562 { + // Read the MAC_SERDES_PCS_STATUS register to clear a spurious + // MAC_CGBAD error that shows up on startup. + for p in 0..2 { + use vsc7448_pac::phy; + vsc85xx::Phy::new(p, phy_rw) + .read(phy::EXTENDED_3::MAC_SERDES_PCS_STATUS())?; + } + } + + Ok(()) + } + + fn vsc7448_postconfig(&mut self) -> Result<(), VscError> { + // The SERDES6G going to the front IO board needs to be tuned from + // its default settings, otherwise the signal quality is bad. + const FRONT_IO_SERDES6G: u8 = 15; + vsc7448::serdes6g::serdes6g_read(self.vsc7448, FRONT_IO_SERDES6G)?; + + // h monorail write HSIO:SERDES6G_ANA_CFG:SERDES6G_OB_CFG 0x28441001 + // h monorail write HSIO:SERDES6G_ANA_CFG:SERDES6G_OB_CFG1 0x3F + self.vsc7448.modify( + HSIO().SERDES6G_ANA_CFG().SERDES6G_OB_CFG(), + |r| { + r.set_ob_post0(0x10); + r.set_ob_prec(0x11); // -1, since MSB is sign + r.set_ob_post1(0x2); + r.set_ob_sr_h(0); // Full-rate mode + r.set_ob_sr(0); // Very fast edges (30 ps) + }, + )?; + self.vsc7448.modify( + HSIO().SERDES6G_ANA_CFG().SERDES6G_OB_CFG1(), + |r| { + r.set_ob_lev(0x3F); + }, + )?; + vsc7448::serdes6g::serdes6g_write(self.vsc7448, FRONT_IO_SERDES6G)?; + + // Same for the on-board QSGMII link to the VSC8504, with different + // settings. + // h monorail write SERDES6G_OB_CFG 0x26000131 + // h monorail write SERDES6G_OB_CFG1 0x20 + const VSC8504_SERDES6G: u8 = 14; + vsc7448::serdes6g::serdes6g_read(self.vsc7448, VSC8504_SERDES6G)?; + self.vsc7448.modify( + HSIO().SERDES6G_ANA_CFG().SERDES6G_OB_CFG(), + |r| { + // Leave all other values as default + r.set_ob_post0(0xc); + r.set_ob_sr_h(1); // half-rate mode + r.set_ob_sr(3); // medium speed edges (about 105 ps) + }, + )?; + self.vsc7448.modify( + HSIO().SERDES6G_ANA_CFG().SERDES6G_OB_CFG1(), + |r| { + r.set_ob_lev(0x20); + }, + )?; + vsc7448::serdes6g::serdes6g_write(self.vsc7448, VSC8504_SERDES6G)?; + + // Write to the base port on the VSC8504, patching the SERDES6G + // config to improve signal integrity. This is based on benchtop + // scoping of the QSGMII signals going from the VSC8504 to the VSC7448. + use vsc85xx::tesla::{TeslaPhy, TeslaSerdes6gObConfig}; + let rw = &mut Vsc7448MiimPhy::new(self.vsc7448, 0); + let mut vsc8504 = self.vsc8504.phy(0, rw); + let mut tesla = TeslaPhy { + phy: &mut vsc8504.phy, + }; + tesla.tune_serdes6g_ob(TeslaSerdes6gObConfig { + ob_post0: 0x6, + ob_post1: 0, + ob_prec: 0, + ob_sr_h: 1, // half rate + ob_sr: 0, + })?; + + // Tune QSGMII link from the front IO board's PHY + // These values are captured empirically with an oscilloscope + if let Some(phy) = self.vsc8562.as_mut() { + use vsc85xx::vsc8562::{Sd6gObCfg, Sd6gObCfg1}; + let mut p = vsc85xx::Phy::new(0, phy); // port 0 + let mut v = Vsc8562Phy { phy: &mut p }; + v.tune_sd6g_ob_cfg(Sd6gObCfg { + ob_ena1v_mode: 1, + ob_pol: 1, + ob_post0: 20, + ob_post1: 0, + ob_sr_h: 0, + ob_resistor_ctr: 1, + ob_sr: 15, + })?; + v.tune_sd6g_ob_cfg1(Sd6gObCfg1 { + ob_ena_cas: 0, + ob_lev: 48, + })?; + } + + Ok(()) + } + + /// Configures the local PHY ("PHY4"), which is an on-board VSC8504 + fn phy_vsc8504_init(&mut self) -> Result<(), VscError> { + // Let's configure the on-board PHY first + // + // It's always powered on, and COMA_MODE is controlled via the VSC7448 + // on GPIO_47. + const COMA_MODE_GPIO: u32 = 47; + + // The PHY talks on MIIM addresses 0x4-0x7 (configured by resistors + // on the board), using the VSC7448 as a MIIM bridge. + + // When the VSC7448 comes out of reset, GPIO_47 is an input and low. + // It's pulled up by a resistor on the board, keeping the PHY in + // COMA_MODE. That's fine! + + // Initialize the PHY + let rw = &mut Vsc7448MiimPhy::new(self.vsc7448, 0); + self.vsc8504 = Vsc8504::init(4, rw)?; + for p in 5..8 { + Vsc8504::init(p, rw)?; + } + + // The VSC8504 on the sidecar has its SIGDET GPIOs pulled down, + // for some reason. + self.vsc8504.set_sigdet_polarity(rw, true).unwrap(); + + // Switch the GPIO to an output. Since the output register is low + // by default, this pulls COMA_MODE low, bringing the VSC8504 into + // mission mode. + self.vsc7448.modify(DEVCPU_GCB().GPIO().GPIO_OE1(), |r| { + let mut g_oe1 = r.g_oe1(); + g_oe1 |= 1 << (COMA_MODE_GPIO - 32); + r.set_g_oe1(g_oe1); + })?; + + Ok(()) + } + + pub fn phy_vsc8562_init(&mut self) -> Result<(), VscError> { + if let Some(phy_rw) = &mut self.vsc8562 { + // Request a reset of the PHY. If we had previously marked the PHY + // oscillator as bad, then this power-cycles the entire front IO + // board; otherwise, it only power-cycles the PHY. + self.seq + .reset_front_io_phy() + .map_err(|e| VscError::ProxyError(e.into()))?; + + for p in 0..2 { + let mut phy = vsc85xx::Phy::new(p, phy_rw); + let mut v = Vsc8562Phy { phy: &mut phy }; + v.init_qsgmii()?; + } + phy_rw + .set_coma_mode(false) + .map_err(|e| VscError::ProxyError(e.into()))?; + } + + Ok(()) + } + + fn check_aneg_speed( + &mut self, + switch_port: u8, + phy_port: u8, + ) -> Result<(), VscError> { + if let Some(phy_rw) = &mut self.vsc8562 { + use vsc7448_pac::phy::*; + + let phy = vsc85xx::Phy::new(phy_port, phy_rw); + let status = phy.read(STANDARD::MODE_STATUS())?; + + // If autonegotiation is complete, then decide on a speed + let target_speed = if status.0 & (1 << 5) != 0 { + let status = phy.read(STANDARD::REG_1000BASE_T_STATUS())?; + // Check "LP 1000BASE-T FDX capable" bit + if status.0 & (1 << 11) != 0 { + Some(Speed::Speed1G) + } else { + Some(Speed::Speed100M) + } + // TODO: 10M? + } else { + None + }; + if let Some(target_speed) = target_speed { + let current_speed = self.front_io_speed[phy_port as usize]; + if target_speed != current_speed { + ringbuf_entry!(Trace::FrontIoSpeedChange { + port: switch_port, + before: current_speed, + after: target_speed, + }); + let cfg = PORT_MAP.port_config(switch_port).unwrap(); + self.vsc7448.reinit_sgmii(cfg.dev, target_speed)?; + self.front_io_speed[phy_port as usize] = target_speed; + + // Clear a spurious MAC_CGBAD flag that pops up when we + // change the link speed here. + for p in 0..2 { + use vsc7448_pac::phy; + vsc85xx::Phy::new(p, phy_rw) + .read(phy::EXTENDED_3::MAC_SERDES_PCS_STATUS())?; + } + } + } + } + Ok(()) + } + + fn is_front_io_link_good(&self) -> Result { + // Determine if the link is up which implies the PHY oscillator is good. + Ok(self + .vsc7448 + .read(HSIO().HW_CFGSTAT().HW_QSGMII_STAT(11))? + .sync() + == 1) + } + + pub fn wake(&mut self) -> Result<(), VscError> { + // Check for autonegotiation on the front IO board, then reconfigure + // on the switch side to change speeds. + for port in 44..=45 { + match self.check_aneg_speed(port, port - 44) { + Ok(()) => (), + Err(e) => ringbuf_entry!(Trace::AnegCheckFailed(e)), + } + } + + // Workaround for the link-stuck issue discussed in + // https://github.com/oxidecomputer/bf_sde/issues/5. This should only + // run if the Tofino PCIe link is up otherwise the Tofino port is almost + // certainly down and the condition we are trying to avoid here does not + // matter. + if self.seq.tofino_pcie_link_up() { + if self.vsc7448.check_10gbase_kr_aneg(0)? { + ringbuf_entry!(Trace::Restarted10GAneg); + } + + // Follow-up workaround for the other link-stuck issue discussed in + // https://github.com/oxidecomputer/bf_sde/issues/27 + // If we remain down for 20 seconds, then reinitialize everything. + // + // (see comment in `monorail-server/src/server.rs` about this check) + use vsc7448_pac::PCS10G_BR; + let link_down = self + .vsc7448 + .read(PCS10G_BR(0).PCS_10GBR_STATUS().PCS_STATUS())? + .rx_block_lock() + == 0; + if link_down { + let now = userlib::sys_get_timer().now; + if let Some(link_down_at) = self.link_down_at { + if now - link_down_at >= 20_000 { + self.link_down_at = None; + // This logs Trace::Reinit in the ringbuf + self.reinit()?; + } + } else { + self.link_down_at = Some(now); + } + } else { + self.link_down_at = None; + } + } else { + self.link_down_at = None; + } + + Ok(()) + } + + /// Calls a function on a `Phy` associated with the given port. + /// + /// Returns `None` if the given port isn't associated with a PHY + /// (for example, because it's an SGMII link) + pub fn phy_fn>) -> T>( + &mut self, + port: u8, + callback: F, + ) -> Option { + let (mut phy_rw, phy_port) = match port { + // Ports 40-43 connect to a VSC8504 PHY over QSGMII and represent + // ports 4-7 on the PHY. + 40..=43 => { + let phy_rw = GenericPhyRw::Vsc7448(Vsc7448MiimPhy::new( + self.vsc7448.rw, + 0, + )); + let phy_port = port - 40 + 4; + (phy_rw, phy_port) + } + 44..=45 => { + if let Some(phy_rw) = &self.vsc8562 { + (GenericPhyRw::FrontIo(phy_rw), port - 44) + } else { + return None; + } + } + _ => return None, + }; + let phy = vsc85xx::Phy::new(phy_port, &mut phy_rw); + Some(callback(phy)) + } +} + +/// Simple enum that contains all possible `PhyRw` handle types +pub enum GenericPhyRw<'a, R> { + Vsc7448(Vsc7448MiimPhy<'a, R>), + FrontIo(&'a PhySmi), +} + +impl<'a, R: Vsc7448Rw> PhyRw for GenericPhyRw<'a, R> { + #[inline(always)] + fn read_raw(&self, port: u8, reg: u8) -> Result { + match self { + GenericPhyRw::Vsc7448(n) => n.read_raw(port, reg), + GenericPhyRw::FrontIo(n) => n.read_raw(port, reg), + } + } + #[inline(always)] + fn write_raw(&self, port: u8, reg: u8, value: u16) -> Result<(), VscError> { + match self { + GenericPhyRw::Vsc7448(n) => n.write_raw(port, reg, value), + GenericPhyRw::FrontIo(n) => n.write_raw(port, reg, value), + } + } +} diff --git a/task/monorail-server/src/main.rs b/task/monorail-server/src/main.rs index 17f3e8525a..0add1f7e2c 100644 --- a/task/monorail-server/src/main.rs +++ b/task/monorail-server/src/main.rs @@ -13,6 +13,7 @@ ), path = "bsp/sidecar_bcd.rs" )] +#[cfg_attr(target_board = "medusa-a", path = "bsp/medusa_a.rs")] mod bsp; mod server; diff --git a/task/net/Cargo.toml b/task/net/Cargo.toml index 0f24272bdf..ec6b8590c8 100644 --- a/task/net/Cargo.toml +++ b/task/net/Cargo.toml @@ -46,6 +46,7 @@ mgmt = ["drv-spi-api", "ksz8463", "drv-user-leds-api", "task-net-api/mgmt"] vpd-mac = ["task-packrat-api"] gimlet = ["drv-gimlet-seq-api"] sidecar = ["drv-sidecar-seq-api"] +medusa = ["drv-medusa-seq-api"] psc = ["drv-psc-seq-api"] h743 = ["drv-stm32h7-eth/h743", "stm32h7/stm32h743", "drv-stm32xx-sys-api/h743", "drv-stm32h7-spi-server-core?/h743"] h753 = ["drv-stm32h7-eth/h753", "stm32h7/stm32h753", "drv-stm32xx-sys-api/h753", "drv-stm32h7-spi-server-core?/h753"] diff --git a/task/net/src/bsp/medusa_a.rs b/task/net/src/bsp/medusa_a.rs new file mode 100644 index 0000000000..815ad5932e --- /dev/null +++ b/task/net/src/bsp/medusa_a.rs @@ -0,0 +1,139 @@ +// 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/. + +//! BSP for the Sidecar rev B +//! +//! Right now, this is identical to the rev A BSP, but that may change in the +//! future, so they're kept separate. + +#[cfg(not(all(feature = "ksz8463", feature = "mgmt")))] +compile_error!("this BSP requires the ksz8463 and mgmt features"); + +use crate::{ + bsp_support::{self, Ksz8463}, + mgmt, + miim_bridge::MiimBridge, + pins, +}; +use drv_spi_api::SpiServer; +use drv_stm32h7_eth as eth; +use drv_stm32xx_sys_api::{Alternate, Port, Sys}; +use task_net_api::{ + ManagementCounters, ManagementLinkStatus, MgmtError, PhyError, +}; +use userlib::{hl::sleep_for, task_slot}; +use vsc7448_pac::types::PhyRegisterAddress; + +//////////////////////////////////////////////////////////////////////////////// + +pub struct BspImpl(mgmt::Bsp); + +impl bsp_support::Bsp for BspImpl { + // This system wants to be woken periodically to do logging + const WAKE_INTERVAL: Option = Some(500); + + /// Stateless function to configure ethernet pins before the Bsp struct + /// is actually constructed + fn configure_ethernet_pins(sys: &Sys) { + pins::RmiiPins { + refclk: Port::A.pin(1), // CLK_50M_SP_RMII_REFCLK + crs_dv: Port::A.pin(7), // RMII_SP_TO_EPE_RX_DV + tx_en: Port::G.pin(11), // RMII_SP_TO_EPE_TX_EN + txd0: Port::G.pin(13), // RMII_SP_TO_EPE_TXD0 + txd1: Port::G.pin(12), // RMII_SP_TO_EPE_TXD1 + rxd0: Port::C.pin(4), // RMII_SP_TO_EPE_RDX0 (typo in schematic) + rxd1: Port::C.pin(5), // RMII_SP_TO_EPE_RXD1 + af: Alternate::AF11, + } + .configure(sys); + + pins::MdioPins { + mdio: Port::A.pin(2), // MIIM_SP_TO_PHY_MDIO_3V3 + mdc: Port::C.pin(1), // MIIM_SP_TO_PHY_MDC_3V3 + af: Alternate::AF11, + } + .configure(sys); + } + + fn preinit() { + // TODO + } + + fn new(eth: ð::Ethernet, sys: &Sys) -> Self { + let spi = bsp_support::claim_spi(sys); + let ksz8463_dev = spi.device(drv_spi_api::devices::KSZ8463); + let bsp = mgmt::Config { + // SP_TO_LDO_PHY2_EN (turns on both P2V5 and P1V0) + power_en: Some(Port::I.pin(11)), + slow_power_en: false, + power_good: &[], // TODO + pll_lock: None, // TODO? + + ksz8463: Ksz8463::new(ksz8463_dev), + // SP_TO_EPE_RESET_L + ksz8463_nrst: Port::A.pin(0), + ksz8463_rst_type: mgmt::Ksz8463ResetSpeed::Normal, + + #[cfg(feature = "vlan")] + ksz8463_vlan_mode: ksz8463::VLanMode::Mandatory, + #[cfg(not(feature = "vlan"))] + ksz8463_vlan_mode: ksz8463::VLanMode::Optional, + + // SP_TO_PHY2_COMA_MODE_3V3 + vsc85x2_coma_mode: Some(Port::I.pin(15)), + // SP_TO_PHY2_RESET_3V3_L + vsc85x2_nrst: Port::I.pin(14), + vsc85x2_base_port: 0, + } + .build(sys, eth); + + // The VSC8552 on the sidecar has its SIGDET GPIOs pulled down, + // for some reason. + let rw = &mut MiimBridge::new(eth); + bsp.vsc85x2.set_sigdet_polarity(rw, true).unwrap(); + + Self(bsp) + } + + fn wake(&self, eth: ð::Ethernet) { + self.0.wake(eth); + } + + fn phy_read( + &mut self, + port: u8, + reg: PhyRegisterAddress, + eth: ð::Ethernet, + ) -> Result { + self.0.phy_read(port, reg, eth) + } + + fn phy_write( + &mut self, + port: u8, + reg: PhyRegisterAddress, + value: u16, + eth: ð::Ethernet, + ) -> Result<(), PhyError> { + self.0.phy_write(port, reg, value, eth) + } + + fn ksz8463(&self) -> &Ksz8463 { + &self.0.ksz8463 + } + + fn management_link_status( + &self, + eth: ð::Ethernet, + ) -> Result { + self.0.management_link_status(eth) + } + + fn management_counters( + &self, + eth: &crate::eth::Ethernet, + ) -> Result { + self.0.management_counters(eth) + } +} diff --git a/task/net/src/main.rs b/task/net/src/main.rs index 130ba220cd..a133787a7d 100644 --- a/task/net/src/main.rs +++ b/task/net/src/main.rs @@ -45,6 +45,7 @@ mod server; all(target_board = "gimletlet-2", feature = "gimletlet-nic"), path = "bsp/gimletlet_nic.rs" )] +#[cfg_attr(target_board = "medusa-a", path = "bsp/medusa_a.rs")] mod bsp; #[cfg_attr(feature = "vlan", path = "server_vlan.rs")] From a03d72b4077239728f011070682d7ec9383ab0e0 Mon Sep 17 00:00:00 2001 From: Aaron Hartwig Date: Fri, 5 Jan 2024 15:50:04 -0600 Subject: [PATCH 02/14] WIP --- Cargo.lock | 25 ++++ app/medusa/base.toml | 74 +++++++--- drv/medusa-seq-api/Cargo.toml | 0 drv/medusa-seq-api/build.rs | 0 drv/medusa-seq-api/src/lib.rs | 0 drv/medusa-seq-server/Cargo.toml | 40 ++++++ drv/medusa-seq-server/build.rs | 23 +++ drv/medusa-seq-server/src/main.rs | 51 +++++++ task/net/Cargo.toml | 2 +- task/thermal/Cargo.toml | 1 + task/thermal/src/bsp/medusa_a.rs | 226 ++++++++++++++++++++++++++++++ task/thermal/src/main.rs | 6 + 12 files changed, 424 insertions(+), 24 deletions(-) create mode 100644 drv/medusa-seq-api/Cargo.toml create mode 100644 drv/medusa-seq-api/build.rs create mode 100644 drv/medusa-seq-api/src/lib.rs create mode 100644 drv/medusa-seq-server/Cargo.toml create mode 100644 drv/medusa-seq-server/build.rs create mode 100644 drv/medusa-seq-server/src/main.rs create mode 100644 task/thermal/src/bsp/medusa_a.rs diff --git a/Cargo.lock b/Cargo.lock index 11fb6f813c..d15bb6b9c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1396,6 +1396,31 @@ dependencies = [ "zerocopy 0.6.6", ] +[[package]] +name = "drv-medusa-seq-server" +version = "0.1.0" +dependencies = [ + "build-i2c", + "build-util", + "byteorder", + "cfg-if", + "cortex-m", + "drv-fpga-api", + "drv-fpga-user-api", + "drv-i2c-api", + "drv-i2c-devices", + "drv-packrat-vpd-loader", + "drv-sidecar-front-io", + "hubpack", + "idol", + "idol-runtime", + "num-traits", + "ringbuf", + "serde", + "userlib", + "zerocopy", +] + [[package]] name = "drv-mock-gimlet-hf-server" version = "0.1.0" diff --git a/app/medusa/base.toml b/app/medusa/base.toml index 992036d368..16251e0503 100644 --- a/app/medusa/base.toml +++ b/app/medusa/base.toml @@ -109,20 +109,43 @@ interrupts = {"spi1.irq" = "spi-irq"} name = "task-net" stacksize = 6040 priority = 5 -features = ["mgmt", "h753", "medusa", "vlan", "vpd-mac", "use-spi-core", "spi3"] +# features = ["mgmt", "h753", "medusa", "vlan", "vpd-mac", "use-spi-core", "spi3"] +features = ["mgmt", "h753", "vlan", "vpd-mac", "use-spi-core", "spi3"] max-sizes = {flash = 131072, ram = 65536, sram1 = 16384} sections = {eth_bulk = "sram1"} uses = ["eth", "eth_dma", "tim16", "spi3"] start = true notifications = ["eth-irq", "mdio-timer-irq", "spi-irq", "wake-timer"] -# task-slots = ["sys", "packrat", { seq = "sequencer" }, "jefe"] -task-slots = ["sys", "packrat", "jefe"] +task-slots = ["sys", "packrat", { seq = "sequencer" }, "jefe"] [tasks.net.interrupts] "eth.irq" = "eth-irq" "tim16.irq" = "mdio-timer-irq" "spi3.irq" = "spi-irq" +[tasks.sequencer] +name = "drv-medusa-seq-server" +priority = 4 +stacksize = 4096 +start = true +task-slots = [ + "sys", + "i2c_driver", + "auxflash", + "packrat", + {front_io = "ecp5_front_io"}] +notifications = ["timer"] + +[tasks.thermal] +name = "task-thermal" +features = ["medusa"] +priority = 5 +max-sizes = {flash = 32768, ram = 16384 } +stacksize = 8096 +start = true +task-slots = ["i2c_driver", "sensor", "sequencer"] +notifications = ["timer"] + [tasks.transceivers] name = "drv-transceivers-server" features = ["vlan"] @@ -135,16 +158,9 @@ task-slots = [ "net", "sensor", "sys", - # "thermal", - {front_io = "ecp5_front_io"}] -# task-slots = [ -# "i2c_driver", -# "net", -# "sensor", -# "sys", -# "thermal", -# {front_io = "ecp5_front_io"}, -# {seq = "sequencer"}] + "thermal", + {front_io = "ecp5_front_io"}, + {seq = "sequencer"}] notifications = ["socket", "timer"] [tasks.packrat] @@ -339,33 +355,33 @@ sensors = { voltage = 1, current = 1 } refdes = "U16" # -# I2C2: Front I/O +# I2C2: Front I/O 0 # [[config.i2c.controllers]] controller = 2 [config.i2c.controllers.ports.F] -name = "front_io" +name = "front_io0" description = "Front I/O Board" scl.pin = 1 # I2C_FRONT_IO0_SCL sda.pin = 0 # I2C_FRONT_IO0_SDA af = 4 [[config.i2c.devices]] -bus = "front_io" +bus = "front_io0" address = 0b1010_000 device = "at24csw080" description = "Front IO board FRUID" removable = true [[config.i2c.devices]] -bus = "front_io" +bus = "front_io0" address = 0b1110_011 device = "pca9538" description = "Front IO GPIO expander" removable = true [[config.i2c.devices]] -bus = "front_io" +bus = "front_io0" address = 0b0001_010 device = "pca9956b" name = "front_leds_left" @@ -374,7 +390,7 @@ removable = true refdes = "U5" [[config.i2c.devices]] -bus = "front_io" +bus = "front_io0" address = 0b0001_011 device = "pca9956b" name = "front_leds_right" @@ -383,7 +399,7 @@ removable = true refdes = "U6" [[config.i2c.devices]] -bus = "front_io" +bus = "front_io0" address = 0b0011_011 device = "tps546b24a" description = "Front IO V3P3_SYS_A2 rail" @@ -393,7 +409,7 @@ sensors = { temperature = 1, voltage = 1, current = 1 } refdes = "J61_U7" # on front IO board [[config.i2c.devices]] -bus = "front_io" +bus = "front_io0" address = 0b0011_001 device = "tps546b24a" description = "Front IO V3P3_QSFP0_A0 rail" @@ -403,7 +419,7 @@ sensors = { temperature = 1, voltage = 1, current = 1 } refdes = "J61_U15" # on front IO board [[config.i2c.devices]] -bus = "front_io" +bus = "front_io0" address = 0b0011_010 device = "tps546b24a" description = "Front IO V3P3_QSFP1_A0 rail" @@ -412,6 +428,18 @@ power = {rails = ["V3P3_QSFP1_A0"] } sensors = { temperature = 1, voltage = 1, current = 1 } refdes = "J61_U18" # on front IO board +# +# I2C3: Front I/O 1 +# +[[config.i2c.controllers]] +controller = 3 +[config.i2c.controllers.ports.H] +name = "front_io1" +description = "Front I/O Board" +scl.pin = 7 # I2C_FRONT_IO1_SCL +sda.pin = 8 # I2C_FRONT_IO1_SDA +af = 4 + # # SPI1: Front IO FPGAs # @@ -468,7 +496,7 @@ mux = "port_c" cs = [{port = "A", pin = 4}] # SPI_SP_TO_EPE_CS_L # -# QSFP Temperature Sensors +# QSFP Module Temperature Sensors # [[config.sensor.devices]] name = "xcvr0" diff --git a/drv/medusa-seq-api/Cargo.toml b/drv/medusa-seq-api/Cargo.toml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/drv/medusa-seq-api/build.rs b/drv/medusa-seq-api/build.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/drv/medusa-seq-api/src/lib.rs b/drv/medusa-seq-api/src/lib.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/drv/medusa-seq-server/Cargo.toml b/drv/medusa-seq-server/Cargo.toml new file mode 100644 index 0000000000..a88518b768 --- /dev/null +++ b/drv/medusa-seq-server/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "drv-medusa-seq-server" +version = "0.1.0" +edition = "2021" + +[dependencies] +byteorder.workspace = true +cfg-if.workspace = true +cortex-m.workspace = true +hubpack.workspace = true +idol-runtime.workspace = true +num-traits.workspace = true +serde.workspace = true +zerocopy.workspace = true + +drv-fpga-api = { path = "../fpga-api", features = ["auxflash"] } +drv-fpga-user-api = { path = "../fpga-user-api" } +drv-i2c-api = { path = "../i2c-api" } +drv-i2c-devices = { path = "../i2c-devices" } +drv-packrat-vpd-loader = { path = "../packrat-vpd-loader" } +drv-sidecar-front-io = { path = "../sidecar-front-io", features = ["controller", "phy_smi"] } +ringbuf = { path = "../../lib/ringbuf" } +userlib = { path = "../../sys/userlib", features = ["panic-messages"] } + +[features] +h753 = ["build-i2c/h753"] +stay-in-a2 = [] + +[build-dependencies] +build-util = { path = "../../build/util" } +build-i2c = { path = "../../build/i2c" } +idol = { workspace = true } + +# This section is here to discourage RLS/rust-analyzer from doing test builds, +# since test builds don't work for cross compilation. +[[bin]] +name = "drv-medusa-seq-server" +test = false +doctest = false +bench = false diff --git a/drv/medusa-seq-server/build.rs b/drv/medusa-seq-server/build.rs new file mode 100644 index 0000000000..e461e5c3d2 --- /dev/null +++ b/drv/medusa-seq-server/build.rs @@ -0,0 +1,23 @@ +// 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/. + +fn main() -> Result<(), Box> { + build_util::expose_target_board(); + build_util::build_notifications()?; + + let disposition = build_i2c::Disposition::Devices; + + if let Err(e) = build_i2c::codegen(disposition) { + println!("code generation failed: {}", e); + std::process::exit(1); + } + + // idol::server::build_server_support( + // "../../idl/medusa-seq.idol", + // "server_stub.rs", + // idol::server::ServerStyle::InOrder, + // )?; + + Ok(()) +} diff --git a/drv/medusa-seq-server/src/main.rs b/drv/medusa-seq-server/src/main.rs new file mode 100644 index 0000000000..2ec94d51d5 --- /dev/null +++ b/drv/medusa-seq-server/src/main.rs @@ -0,0 +1,51 @@ +// 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/. + +//! Server for managing the Medusa sequencing process. + +#![no_std] +#![no_main] + +use userlib::*; + +task_slot!(I2C, i2c_driver); +task_slot!(FRONT_IO, front_io); +task_slot!(AUXFLASH, auxflash); +task_slot!(PACKRAT, packrat); + +include!(concat!(env!("OUT_DIR"), "/i2c_config.rs")); + +// const TIMER_INTERVAL: u64 = 1000; + +// struct ServerImpl { +// } + +// impl ServerImpl { +// } + +// impl NotificationHandler for ServerImpl { +// fn current_notification_mask(&self) -> u32 { +// notifications::TIMER_MASK +// } + +// fn handle_notification(&mut self, _bits: u32) { +// let next_deadline = sys_get_timer().now + TIMER_INTERVAL; + +// sys_set_timer(Some(next_deadline), notifications::TIMER_MASK); +// } +// } + +#[export_name = "main"] +fn main() -> ! { + + // + // This will put our timer in the past, and should immediately kick us. + // + // let deadline = sys_get_timer().now; + // sys_set_timer(Some(deadline), notifications::TIMER_MASK); + + loop { + + } +} diff --git a/task/net/Cargo.toml b/task/net/Cargo.toml index ec6b8590c8..6d32e04de8 100644 --- a/task/net/Cargo.toml +++ b/task/net/Cargo.toml @@ -46,7 +46,7 @@ mgmt = ["drv-spi-api", "ksz8463", "drv-user-leds-api", "task-net-api/mgmt"] vpd-mac = ["task-packrat-api"] gimlet = ["drv-gimlet-seq-api"] sidecar = ["drv-sidecar-seq-api"] -medusa = ["drv-medusa-seq-api"] +# medusa = ["drv-medusa-seq-api"] psc = ["drv-psc-seq-api"] h743 = ["drv-stm32h7-eth/h743", "stm32h7/stm32h743", "drv-stm32xx-sys-api/h743", "drv-stm32h7-spi-server-core?/h743"] h753 = ["drv-stm32h7-eth/h753", "stm32h7/stm32h753", "drv-stm32xx-sys-api/h753", "drv-stm32h7-spi-server-core?/h753"] diff --git a/task/thermal/Cargo.toml b/task/thermal/Cargo.toml index 931ad10589..fdaefa4ef4 100644 --- a/task/thermal/Cargo.toml +++ b/task/thermal/Cargo.toml @@ -37,6 +37,7 @@ build-util = { path = "../../build/util" } [features] gimlet = ["drv-gimlet-seq-api", "h753"] sidecar = ["drv-sidecar-seq-api", "drv-transceivers-api", "h753"] +medusa = ["h753"] h743 = ["build-i2c/h743"] h753 = ["build-i2c/h753"] h7b3 = ["build-i2c/h7b3"] diff --git a/task/thermal/src/bsp/medusa_a.rs b/task/thermal/src/bsp/medusa_a.rs new file mode 100644 index 0000000000..a8a632c631 --- /dev/null +++ b/task/thermal/src/bsp/medusa_a.rs @@ -0,0 +1,226 @@ +// 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/. + +//! BSP for Medusa + +use crate::control::{ + ChannelType, Device, FanControl, Fans, InputChannel, PidConfig, + TemperatureSensor, +}; +use core::convert::TryInto; +use drv_i2c_devices::max31790::Max31790; +use drv_i2c_devices::tmp451::*; +use task_sensor_api::SensorId; +use task_thermal_api::ThermalProperties; +use userlib::{task_slot, units::Celsius, TaskId}; + +include!(concat!(env!("OUT_DIR"), "/i2c_config.rs")); +use i2c_config::devices; +use i2c_config::sensors; + +task_slot!(SEQUENCER, sequencer); + +//////////////////////////////////////////////////////////////////////////////// +// Constants! + + +// Air temperature sensors, which aren't used in the control loop +const NUM_TEMPERATURE_SENSORS: usize = 0; + +// Temperature inputs (I2C devices), which are used in the control loop. +pub const NUM_TEMPERATURE_INPUTS: usize = 0; + +// External temperature inputs, which are provided to the task over IPC +// In practice, these are our transceivers. +pub const NUM_DYNAMIC_TEMPERATURE_INPUTS: usize = + drv_transceivers_api::NUM_PORTS as usize; + +// Number of individual fans - Medusa has none! +pub const NUM_FANS: usize = 0; + +// Run the PID loop on startup +pub const USE_CONTROLLER: bool = true; + +//////////////////////////////////////////////////////////////////////////////// + +bitflags::bitflags! { + pub struct PowerBitmask: u32 { + // As far as I know, we don't have any devices which are active only + // in A2; you probably want to use `POWER_STATE_A0_OR_A2` instead + const A2 = 0b00000001; + const A0 = 0b00000010; + const A0_OR_A2 = Self::A0.bits | Self::A2.bits; + } +} + +#[allow(dead_code)] +pub(crate) struct Bsp { + pub dynamic_inputs: &'static [SensorId], + + /// Monitored sensors + pub misc_sensors: &'static [TemperatureSensor], + + seq: Sequencer, + + pub pid_config: PidConfig, +} + +impl Bsp { + pub fn fan_control( + &self, + fan: crate::Fan, + ) -> crate::control::FanControl<'_> { + FanControl::Max31790(controller, fan_physical.try_into().unwrap()) + } + + pub fn for_each_fctrl(&self, mut fctrl: impl FnMut(FanControl<'_>)) { + // Run the function on each fan control chip + fctrl(self.fan_control(0.into())); + fctrl(self.fan_control(4.into())); + } + + pub fn power_mode(&self) -> PowerBitmask { + match self.seq.tofino_seq_state() { + Ok(r) => match r { + TofinoSeqState::A0 => PowerBitmask::A0, + TofinoSeqState::Init + | TofinoSeqState::A2 + | TofinoSeqState::InPowerUp + | TofinoSeqState::InPowerDown => PowerBitmask::A2, + }, + Err(_) => PowerBitmask::A0_OR_A2, + } + } + + pub fn power_down(&self) -> Result<(), SeqError> { + self.seq + .set_tofino_seq_policy(TofinoSequencerPolicy::Disabled) + } + + pub fn get_fan_presence(&self) -> Result, SeqError> { + let presence = self.seq.fan_module_presence()?; + let mut next = Fans::new(); + for (i, present) in presence.0.iter().enumerate() { + // two fans per module + let idx = i * 2; + if *present { + next[idx] = Some(sensors::MAX31790_SPEED_SENSORS[idx]); + next[idx + 1] = Some(sensors::MAX31790_SPEED_SENSORS[idx + 1]); + } + } + Ok(next) + } + + pub fn new(i2c_task: TaskId) -> Self { + // Handle for the sequencer task, which we check for power state and + // fan presence + let seq = Sequencer::from(SEQUENCER.get_task_id()); + + let fctrl_east = Max31790::new(&devices::max31790_east(i2c_task)); + let fctrl_west = Max31790::new(&devices::max31790_west(i2c_task)); + fctrl_east.initialize().unwrap(); + fctrl_west.initialize().unwrap(); + + Self { + seq, + fctrl_east, + fctrl_west, + + // TODO: this is all made up, copied from tuned Gimlet values + pid_config: PidConfig { + zero: 35.0, + gain_p: 1.75, + gain_i: 0.0135, + gain_d: 0.4, + }, + + inputs: &INPUTS, + dynamic_inputs: + &drv_transceivers_api::TRANSCEIVER_TEMPERATURE_SENSORS, + + // We monitor and log all of the air temperatures + misc_sensors: &MISC_SENSORS, + } + } +} + +// +// Guessing, big time +// +const TF2_THERMALS: ThermalProperties = ThermalProperties { + target_temperature: Celsius(60f32), + critical_temperature: Celsius(70f32), + power_down_temperature: Celsius(80f32), + temperature_slew_deg_per_sec: 0.5, +}; + +// The VSC7448 has a maximum die temperature of 110°C, which is very +// hot. Let's keep it a little cooler than that. +const VSC7448_THERMALS: ThermalProperties = ThermalProperties { + target_temperature: Celsius(85f32), + critical_temperature: Celsius(95f32), + power_down_temperature: Celsius(105f32), + temperature_slew_deg_per_sec: 0.5, +}; + +const INPUTS: [InputChannel; NUM_TEMPERATURE_INPUTS] = [ + InputChannel::new( + TemperatureSensor::new( + Device::Tmp451(Target::Remote), + devices::tmp451_tf2, + sensors::TMP451_TF2_TEMPERATURE_SENSOR, + ), + TF2_THERMALS, + PowerBitmask::A0, + ChannelType::MustBePresent, + ), + InputChannel::new( + TemperatureSensor::new( + Device::Tmp451(Target::Remote), + devices::tmp451_vsc7448, + sensors::TMP451_VSC7448_TEMPERATURE_SENSOR, + ), + VSC7448_THERMALS, + PowerBitmask::A0_OR_A2, + ChannelType::MustBePresent, + ), +]; + +const MISC_SENSORS: [TemperatureSensor; NUM_TEMPERATURE_SENSORS] = [ + TemperatureSensor::new( + Device::Tmp117, + devices::tmp117_northeast, + sensors::TMP117_NORTHEAST_TEMPERATURE_SENSOR, + ), + TemperatureSensor::new( + Device::Tmp117, + devices::tmp117_nne, + sensors::TMP117_NNE_TEMPERATURE_SENSOR, + ), + TemperatureSensor::new( + Device::Tmp117, + devices::tmp117_nnw, + sensors::TMP117_NNW_TEMPERATURE_SENSOR, + ), + TemperatureSensor::new( + Device::Tmp117, + devices::tmp117_northwest, + sensors::TMP117_NORTHWEST_TEMPERATURE_SENSOR, + ), + TemperatureSensor::new( + Device::Tmp117, + devices::tmp117_southeast, + sensors::TMP117_SOUTHEAST_TEMPERATURE_SENSOR, + ), + TemperatureSensor::new( + Device::Tmp117, + devices::tmp117_south, + sensors::TMP117_SOUTH_TEMPERATURE_SENSOR, + ), + TemperatureSensor::new( + Device::Tmp117, + devices::tmp117_southwest, + sensors::TMP117_SOUTHWEST_TEMPERATURE_SENSOR, + ), +]; diff --git a/task/thermal/src/main.rs b/task/thermal/src/main.rs index 2ddf20ee4e..150a321aff 100644 --- a/task/thermal/src/main.rs +++ b/task/thermal/src/main.rs @@ -30,6 +30,12 @@ ), path = "bsp/sidecar_bcd.rs" )] +#[cfg_attr( + any( + target_board = "medusa-a" + ), + path = "bsp/medusa_a.rs" +)] mod bsp; mod control; From 12cf55aa163ad293ef258b690c8e74c018c294f4 Mon Sep 17 00:00:00 2001 From: Aaron Hartwig Date: Thu, 22 Feb 2024 16:51:22 -0600 Subject: [PATCH 03/14] fix medusa-seq-api Cargo.toml so it compiles --- Cargo.lock | 6 +++++- build/xtask/src/flash.rs | 11 +++++++---- drv/fpga-server/src/main.rs | 2 +- drv/medusa-seq-api/Cargo.toml | 3 +++ drv/medusa-seq-api/build.rs | 1 + drv/medusa-seq-api/src/lib.rs | 1 + drv/medusa-seq-server/src/main.rs | 5 +---- drv/sidecar-front-io/build.rs | 6 +++++- drv/sidecar-front-io/src/leds.rs | 24 ++++++++++++++++++++---- task/thermal/src/bsp/medusa_a.rs | 1 - task/thermal/src/main.rs | 7 +------ 11 files changed, 45 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d15bb6b9c4..0b42844e9a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1396,6 +1396,10 @@ dependencies = [ "zerocopy 0.6.6", ] +[[package]] +name = "drv-medusa-seq-api" +version = "0.1.0" + [[package]] name = "drv-medusa-seq-server" version = "0.1.0" @@ -1418,7 +1422,7 @@ dependencies = [ "ringbuf", "serde", "userlib", - "zerocopy", + "zerocopy 0.6.4", ] [[package]] diff --git a/build/xtask/src/flash.rs b/build/xtask/src/flash.rs index 45760615f7..e067fe9f8c 100644 --- a/build/xtask/src/flash.rs +++ b/build/xtask/src/flash.rs @@ -174,9 +174,10 @@ pub fn config( "stm32f3-discovery" | "stm32f4-discovery" | "nucleo-h743zi2" | "nucleo-h753zi" | "stm32h7b3i-dk" | "gemini-bu-1" | "gimletlet-1" | "gimletlet-2" | "gimlet-b" | "gimlet-c" | "gimlet-d" | "gimlet-e" - | "psc-a" | "psc-b" | "psc-c" | "sidecar-b" | "sidecar-c" | "sidecar-d" - | "stm32g031-nucleo" | "donglet-g030" | "donglet-g031" - | "oxcon2023g0" | "stm32g070" | "stm32g0b1" | "medusa-a"=> { + | "psc-a" | "psc-b" | "psc-c" | "sidecar-b" | "sidecar-c" + | "sidecar-d" | "stm32g031-nucleo" | "donglet-g030" + | "donglet-g031" | "oxcon2023g0" | "stm32g070" | "stm32g0b1" + | "medusa-a" => { let cfg = FlashProgramConfig::new(chip_dir.join("openocd.cfg")); let mut flash = FlashConfig::new(FlashProgram::OpenOcd(cfg)); @@ -213,7 +214,9 @@ pub fn chip_name(board: &str) -> anyhow::Result<&'static str> { "stm32h7b3i-dk" => "STM32H7B3IITx", "gemini-bu-1" | "gimletlet-1" | "gimletlet-2" | "gimlet-b" | "gimlet-c" | "gimlet-d" | "gimlet-e" | "psc-a" | "psc-b" - | "psc-c" | "sidecar-b" | "sidecar-c" | "sidecar-d" | "medusa-a" => "STM32H753ZITx", + | "psc-c" | "sidecar-b" | "sidecar-c" | "sidecar-d" | "medusa-a" => { + "STM32H753ZITx" + } "donglet-g030" => "STM32G030F6Px", "donglet-g031" => "STM32G031F8Px", "stm32g031-nucleo" => "STM32G031Y8Yx", diff --git a/drv/fpga-server/src/main.rs b/drv/fpga-server/src/main.rs index f42f1c95f3..1c28450d42 100644 --- a/drv/fpga-server/src/main.rs +++ b/drv/fpga-server/src/main.rs @@ -90,7 +90,7 @@ fn main() -> ! { let devices = [ecp5::Ecp5::new(driver)]; } else if #[cfg(all(any(target_board = "sidecar-b", target_board = "sidecar-c", - target_board = "sidecar-d"), + target_board = "sidecar-d", target_board = "medusa-a"), feature = "front_io"))] { let configuration_port = diff --git a/drv/medusa-seq-api/Cargo.toml b/drv/medusa-seq-api/Cargo.toml index e69de29bb2..707c8371dc 100644 --- a/drv/medusa-seq-api/Cargo.toml +++ b/drv/medusa-seq-api/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "drv-medusa-seq-api" +version = "0.1.0" diff --git a/drv/medusa-seq-api/build.rs b/drv/medusa-seq-api/build.rs index e69de29bb2..8b13789179 100644 --- a/drv/medusa-seq-api/build.rs +++ b/drv/medusa-seq-api/build.rs @@ -0,0 +1 @@ + diff --git a/drv/medusa-seq-api/src/lib.rs b/drv/medusa-seq-api/src/lib.rs index e69de29bb2..8b13789179 100644 --- a/drv/medusa-seq-api/src/lib.rs +++ b/drv/medusa-seq-api/src/lib.rs @@ -0,0 +1 @@ + diff --git a/drv/medusa-seq-server/src/main.rs b/drv/medusa-seq-server/src/main.rs index 2ec94d51d5..586d345660 100644 --- a/drv/medusa-seq-server/src/main.rs +++ b/drv/medusa-seq-server/src/main.rs @@ -38,14 +38,11 @@ include!(concat!(env!("OUT_DIR"), "/i2c_config.rs")); #[export_name = "main"] fn main() -> ! { - // // This will put our timer in the past, and should immediately kick us. // // let deadline = sys_get_timer().now; // sys_set_timer(Some(deadline), notifications::TIMER_MASK); - loop { - - } + loop {} } diff --git a/drv/sidecar-front-io/build.rs b/drv/sidecar-front-io/build.rs index 6495e99645..ce49969834 100644 --- a/drv/sidecar-front-io/build.rs +++ b/drv/sidecar-front-io/build.rs @@ -9,7 +9,11 @@ fn main() -> Result<(), Box> { build_util::expose_target_board(); let board = build_util::env_var("HUBRIS_BOARD")?; - if board != "sidecar-b" && board != "sidecar-c" && board != "sidecar-d" && board != "medusa-a" { + if board != "sidecar-b" + && board != "sidecar-c" + && board != "sidecar-d" + && board != "medusa-a" + { panic!("unknown target board"); } diff --git a/drv/sidecar-front-io/src/leds.rs b/drv/sidecar-front-io/src/leds.rs index c5e1aceee2..c00f84a301 100644 --- a/drv/sidecar-front-io/src/leds.rs +++ b/drv/sidecar-front-io/src/leds.rs @@ -188,7 +188,11 @@ const LED_MAP: LedMap = LedMap([ controller: LedController::Left, output: 1, }, - #[cfg(any(target_board = "sidecar-c", target_board = "sidecar-d", target_board = "medusa-a"))] + #[cfg(any( + target_board = "sidecar-c", + target_board = "sidecar-d", + target_board = "medusa-a" + ))] // Port 16 LedLocation { controller: LedController::Left, @@ -200,7 +204,11 @@ const LED_MAP: LedMap = LedMap([ controller: LedController::Left, output: 3, }, - #[cfg(any(target_board = "sidecar-c", target_board = "sidecar-d", target_board = "medusa-a"))] + #[cfg(any( + target_board = "sidecar-c", + target_board = "sidecar-d", + target_board = "medusa-a" + ))] // Port 17 LedLocation { controller: LedController::Left, @@ -212,7 +220,11 @@ const LED_MAP: LedMap = LedMap([ controller: LedController::Left, output: 5, }, - #[cfg(any(target_board = "sidecar-c", target_board = "sidecar-d", target_board = "medusa-a"))] + #[cfg(any( + target_board = "sidecar-c", + target_board = "sidecar-d", + target_board = "medusa-a" + ))] // Port 18 LedLocation { controller: LedController::Left, @@ -224,7 +236,11 @@ const LED_MAP: LedMap = LedMap([ controller: LedController::Left, output: 7, }, - #[cfg(any(target_board = "sidecar-c", target_board = "sidecar-d", target_board = "medusa-a"))] + #[cfg(any( + target_board = "sidecar-c", + target_board = "sidecar-d", + target_board = "medusa-a" + ))] // Port 19 LedLocation { controller: LedController::Left, diff --git a/task/thermal/src/bsp/medusa_a.rs b/task/thermal/src/bsp/medusa_a.rs index a8a632c631..08f569ed42 100644 --- a/task/thermal/src/bsp/medusa_a.rs +++ b/task/thermal/src/bsp/medusa_a.rs @@ -24,7 +24,6 @@ task_slot!(SEQUENCER, sequencer); //////////////////////////////////////////////////////////////////////////////// // Constants! - // Air temperature sensors, which aren't used in the control loop const NUM_TEMPERATURE_SENSORS: usize = 0; diff --git a/task/thermal/src/main.rs b/task/thermal/src/main.rs index 150a321aff..aa9e95810e 100644 --- a/task/thermal/src/main.rs +++ b/task/thermal/src/main.rs @@ -30,12 +30,7 @@ ), path = "bsp/sidecar_bcd.rs" )] -#[cfg_attr( - any( - target_board = "medusa-a" - ), - path = "bsp/medusa_a.rs" -)] +#[cfg_attr(any(target_board = "medusa-a"), path = "bsp/medusa_a.rs")] mod bsp; mod control; From 3fbe53c381cab3b7bca5c0ffe9fabfc2e2923d29 Mon Sep 17 00:00:00 2001 From: "Cliff L. Biffle" Date: Wed, 27 Mar 2024 15:20:26 -0700 Subject: [PATCH 04/14] XXX dep update --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 0b42844e9a..2389567282 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1422,7 +1422,7 @@ dependencies = [ "ringbuf", "serde", "userlib", - "zerocopy 0.6.4", + "zerocopy 0.6.6", ] [[package]] From da048bc22ecc4e95d046c8c88e5db61397742e2b Mon Sep 17 00:00:00 2001 From: "Cliff L. Biffle" Date: Wed, 27 Mar 2024 15:20:52 -0700 Subject: [PATCH 05/14] net: remove an unwrap in medusa_a bsp --- task/net/src/bsp/medusa_a.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/task/net/src/bsp/medusa_a.rs b/task/net/src/bsp/medusa_a.rs index 815ad5932e..95a981a511 100644 --- a/task/net/src/bsp/medusa_a.rs +++ b/task/net/src/bsp/medusa_a.rs @@ -22,7 +22,7 @@ use drv_stm32xx_sys_api::{Alternate, Port, Sys}; use task_net_api::{ ManagementCounters, ManagementLinkStatus, MgmtError, PhyError, }; -use userlib::{hl::sleep_for, task_slot}; +use userlib::UnwrapLite; use vsc7448_pac::types::PhyRegisterAddress; //////////////////////////////////////////////////////////////////////////////// @@ -91,7 +91,7 @@ impl bsp_support::Bsp for BspImpl { // The VSC8552 on the sidecar has its SIGDET GPIOs pulled down, // for some reason. let rw = &mut MiimBridge::new(eth); - bsp.vsc85x2.set_sigdet_polarity(rw, true).unwrap(); + bsp.vsc85x2.set_sigdet_polarity(rw, true).unwrap_lite(); Self(bsp) } From 492547a863f7af50ea3e774a7099952bc985c7ef Mon Sep 17 00:00:00 2001 From: "Cliff L. Biffle" Date: Wed, 27 Mar 2024 15:21:13 -0700 Subject: [PATCH 06/14] thermal: get medusa_a BSP compiling --- task/thermal/Cargo.toml | 6 +- task/thermal/src/bsp/medusa_a.rs | 162 +++++-------------------------- task/thermal/src/control.rs | 5 + 3 files changed, 30 insertions(+), 143 deletions(-) diff --git a/task/thermal/Cargo.toml b/task/thermal/Cargo.toml index fdaefa4ef4..618c53630a 100644 --- a/task/thermal/Cargo.toml +++ b/task/thermal/Cargo.toml @@ -35,9 +35,9 @@ build-i2c = { path = "../../build/i2c" } build-util = { path = "../../build/util" } [features] -gimlet = ["drv-gimlet-seq-api", "h753"] -sidecar = ["drv-sidecar-seq-api", "drv-transceivers-api", "h753"] -medusa = ["h753"] +gimlet = ["dep:drv-gimlet-seq-api", "h753"] +sidecar = ["dep:drv-sidecar-seq-api", "dep:drv-transceivers-api", "h753"] +medusa = ["h753", "dep:drv-transceivers-api"] h743 = ["build-i2c/h743"] h753 = ["build-i2c/h753"] h7b3 = ["build-i2c/h7b3"] diff --git a/task/thermal/src/bsp/medusa_a.rs b/task/thermal/src/bsp/medusa_a.rs index 08f569ed42..804480caaa 100644 --- a/task/thermal/src/bsp/medusa_a.rs +++ b/task/thermal/src/bsp/medusa_a.rs @@ -5,21 +5,13 @@ //! BSP for Medusa use crate::control::{ - ChannelType, Device, FanControl, Fans, InputChannel, PidConfig, + FanControl, Fans, InputChannel, PidConfig, TemperatureSensor, }; -use core::convert::TryInto; -use drv_i2c_devices::max31790::Max31790; -use drv_i2c_devices::tmp451::*; use task_sensor_api::SensorId; -use task_thermal_api::ThermalProperties; -use userlib::{task_slot, units::Celsius, TaskId}; +use userlib::TaskId; include!(concat!(env!("OUT_DIR"), "/i2c_config.rs")); -use i2c_config::devices; -use i2c_config::sensors; - -task_slot!(SEQUENCER, sequencer); //////////////////////////////////////////////////////////////////////////////// // Constants! @@ -39,99 +31,63 @@ pub const NUM_DYNAMIC_TEMPERATURE_INPUTS: usize = pub const NUM_FANS: usize = 0; // Run the PID loop on startup -pub const USE_CONTROLLER: bool = true; +pub const USE_CONTROLLER: bool = false; //////////////////////////////////////////////////////////////////////////////// bitflags::bitflags! { + #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct PowerBitmask: u32 { - // As far as I know, we don't have any devices which are active only - // in A2; you probably want to use `POWER_STATE_A0_OR_A2` instead - const A2 = 0b00000001; - const A0 = 0b00000010; - const A0_OR_A2 = Self::A0.bits | Self::A2.bits; } } +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum SeqError {} + #[allow(dead_code)] pub(crate) struct Bsp { + pub inputs: &'static [InputChannel], pub dynamic_inputs: &'static [SensorId], /// Monitored sensors pub misc_sensors: &'static [TemperatureSensor], - seq: Sequencer, - pub pid_config: PidConfig, } impl Bsp { pub fn fan_control( &self, - fan: crate::Fan, + _fan: crate::Fan, ) -> crate::control::FanControl<'_> { - FanControl::Max31790(controller, fan_physical.try_into().unwrap()) + // Because we have zero fans, nothing should ever call fan_control. + unreachable!() } - pub fn for_each_fctrl(&self, mut fctrl: impl FnMut(FanControl<'_>)) { - // Run the function on each fan control chip - fctrl(self.fan_control(0.into())); - fctrl(self.fan_control(4.into())); + pub fn for_each_fctrl(&self, mut _fctrl: impl FnMut(FanControl<'_>)) { + // This one's reeeeal easy. } pub fn power_mode(&self) -> PowerBitmask { - match self.seq.tofino_seq_state() { - Ok(r) => match r { - TofinoSeqState::A0 => PowerBitmask::A0, - TofinoSeqState::Init - | TofinoSeqState::A2 - | TofinoSeqState::InPowerUp - | TofinoSeqState::InPowerDown => PowerBitmask::A2, - }, - Err(_) => PowerBitmask::A0_OR_A2, - } + PowerBitmask::empty() } pub fn power_down(&self) -> Result<(), SeqError> { - self.seq - .set_tofino_seq_policy(TofinoSequencerPolicy::Disabled) + Ok(()) } pub fn get_fan_presence(&self) -> Result, SeqError> { - let presence = self.seq.fan_module_presence()?; - let mut next = Fans::new(); - for (i, present) in presence.0.iter().enumerate() { - // two fans per module - let idx = i * 2; - if *present { - next[idx] = Some(sensors::MAX31790_SPEED_SENSORS[idx]); - next[idx + 1] = Some(sensors::MAX31790_SPEED_SENSORS[idx + 1]); - } - } - Ok(next) + Ok(Fans::new()) } - pub fn new(i2c_task: TaskId) -> Self { - // Handle for the sequencer task, which we check for power state and - // fan presence - let seq = Sequencer::from(SEQUENCER.get_task_id()); - - let fctrl_east = Max31790::new(&devices::max31790_east(i2c_task)); - let fctrl_west = Max31790::new(&devices::max31790_west(i2c_task)); - fctrl_east.initialize().unwrap(); - fctrl_west.initialize().unwrap(); - + pub fn new(_i2c_task: TaskId) -> Self { Self { - seq, - fctrl_east, - fctrl_west, - - // TODO: this is all made up, copied from tuned Gimlet values + // PID config doesn't matter since we have no fans. pid_config: PidConfig { - zero: 35.0, - gain_p: 1.75, - gain_i: 0.0135, - gain_d: 0.4, + zero: 0., + gain_p: 0., + gain_i: 0., + gain_d: 0., }, inputs: &INPUTS, @@ -144,82 +100,8 @@ impl Bsp { } } -// -// Guessing, big time -// -const TF2_THERMALS: ThermalProperties = ThermalProperties { - target_temperature: Celsius(60f32), - critical_temperature: Celsius(70f32), - power_down_temperature: Celsius(80f32), - temperature_slew_deg_per_sec: 0.5, -}; - -// The VSC7448 has a maximum die temperature of 110°C, which is very -// hot. Let's keep it a little cooler than that. -const VSC7448_THERMALS: ThermalProperties = ThermalProperties { - target_temperature: Celsius(85f32), - critical_temperature: Celsius(95f32), - power_down_temperature: Celsius(105f32), - temperature_slew_deg_per_sec: 0.5, -}; - const INPUTS: [InputChannel; NUM_TEMPERATURE_INPUTS] = [ - InputChannel::new( - TemperatureSensor::new( - Device::Tmp451(Target::Remote), - devices::tmp451_tf2, - sensors::TMP451_TF2_TEMPERATURE_SENSOR, - ), - TF2_THERMALS, - PowerBitmask::A0, - ChannelType::MustBePresent, - ), - InputChannel::new( - TemperatureSensor::new( - Device::Tmp451(Target::Remote), - devices::tmp451_vsc7448, - sensors::TMP451_VSC7448_TEMPERATURE_SENSOR, - ), - VSC7448_THERMALS, - PowerBitmask::A0_OR_A2, - ChannelType::MustBePresent, - ), ]; const MISC_SENSORS: [TemperatureSensor; NUM_TEMPERATURE_SENSORS] = [ - TemperatureSensor::new( - Device::Tmp117, - devices::tmp117_northeast, - sensors::TMP117_NORTHEAST_TEMPERATURE_SENSOR, - ), - TemperatureSensor::new( - Device::Tmp117, - devices::tmp117_nne, - sensors::TMP117_NNE_TEMPERATURE_SENSOR, - ), - TemperatureSensor::new( - Device::Tmp117, - devices::tmp117_nnw, - sensors::TMP117_NNW_TEMPERATURE_SENSOR, - ), - TemperatureSensor::new( - Device::Tmp117, - devices::tmp117_northwest, - sensors::TMP117_NORTHWEST_TEMPERATURE_SENSOR, - ), - TemperatureSensor::new( - Device::Tmp117, - devices::tmp117_southeast, - sensors::TMP117_SOUTHEAST_TEMPERATURE_SENSOR, - ), - TemperatureSensor::new( - Device::Tmp117, - devices::tmp117_south, - sensors::TMP117_SOUTH_TEMPERATURE_SENSOR, - ), - TemperatureSensor::new( - Device::Tmp117, - devices::tmp117_southwest, - sensors::TMP117_SOUTHWEST_TEMPERATURE_SENSOR, - ), ]; diff --git a/task/thermal/src/control.rs b/task/thermal/src/control.rs index 930a5eee1a..7f374d5ebb 100644 --- a/task/thermal/src/control.rs +++ b/task/thermal/src/control.rs @@ -46,6 +46,7 @@ pub enum Device { /// The sensor includes a device type, used to decide how to read it; /// a free function that returns the raw `I2cDevice`, so that this can be /// `const`); and the sensor ID, to post data to the `sensors` task. +#[allow(dead_code)] // not all BSPS pub struct TemperatureSensor { device: Device, builder: fn(TaskId) -> drv_i2c_api::I2cDevice, @@ -53,6 +54,7 @@ pub struct TemperatureSensor { } impl TemperatureSensor { +#[allow(dead_code)] // not all BSPS pub const fn new( device: Device, builder: fn(TaskId) -> drv_i2c_api::I2cDevice, @@ -125,6 +127,7 @@ impl Fans<{ bsp::NUM_FANS }> { /// Enum representing any of our fan controller types, bound to one of their /// fans. This lets us handle heterogeneous fan controller ICs generically /// (although there's only one at the moment) +#[allow(dead_code)] // a typical BSP uses only _one_ of these pub enum FanControl<'a> { Max31790(&'a Max31790, drv_i2c_devices::max31790::Fan), } @@ -168,6 +171,7 @@ pub(crate) struct InputChannel { } #[derive(Copy, Clone, Eq, PartialEq)] +#[allow(dead_code)] // a typical BSP uses only a subset of these. pub(crate) enum ChannelType { /// `MustBePresent` is exactly what it says on the tin /// @@ -207,6 +211,7 @@ pub(crate) enum ChannelType { } impl InputChannel { + #[allow(dead_code)] // not all BSPS pub const fn new( sensor: TemperatureSensor, model: ThermalProperties, From ccb5ec5fa428b99fae94bade35fd24e556598f78 Mon Sep 17 00:00:00 2001 From: "Cliff L. Biffle" Date: Wed, 27 Mar 2024 15:28:39 -0700 Subject: [PATCH 07/14] XXX modernize base.toml --- app/medusa/base.toml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/medusa/base.toml b/app/medusa/base.toml index 16251e0503..217d57c9f6 100644 --- a/app/medusa/base.toml +++ b/app/medusa/base.toml @@ -25,8 +25,7 @@ request_reset = ["hiffy"] name = "drv-stm32xx-sys" features = ["h753"] priority = 1 -max-sizes = {flash = 2048, ram = 1024} -uses = ["rcc", "gpios1", "gpios2", "gpios3", "system_flash"] +uses = ["rcc", "gpios", "system_flash"] start = true task-slots = ["jefe"] @@ -113,7 +112,7 @@ priority = 5 features = ["mgmt", "h753", "vlan", "vpd-mac", "use-spi-core", "spi3"] max-sizes = {flash = 131072, ram = 65536, sram1 = 16384} sections = {eth_bulk = "sram1"} -uses = ["eth", "eth_dma", "tim16", "spi3"] +uses = ["eth", "tim16", "spi3"] start = true notifications = ["eth-irq", "mdio-timer-irq", "spi-irq", "wake-timer"] task-slots = ["sys", "packrat", { seq = "sequencer" }, "jefe"] @@ -183,7 +182,6 @@ task-slots = ["i2c_driver"] name = "task-sensor" features = [] priority = 4 -max-sizes = {flash = 8192, ram = 4096 } stacksize = 1024 start = true notifications = ["timer"] From ce189059183d5de21d1c6618a61a006fe72287c0 Mon Sep 17 00:00:00 2001 From: "Cliff L. Biffle" Date: Wed, 27 Mar 2024 15:45:21 -0700 Subject: [PATCH 08/14] transceivers: make thermal optional --- app/medusa/base.toml | 11 --------- app/sidecar/base.toml | 2 +- drv/transceivers-server/src/main.rs | 38 ++++++++++++++++++----------- 3 files changed, 25 insertions(+), 26 deletions(-) diff --git a/app/medusa/base.toml b/app/medusa/base.toml index 217d57c9f6..f776248b85 100644 --- a/app/medusa/base.toml +++ b/app/medusa/base.toml @@ -135,16 +135,6 @@ task-slots = [ {front_io = "ecp5_front_io"}] notifications = ["timer"] -[tasks.thermal] -name = "task-thermal" -features = ["medusa"] -priority = 5 -max-sizes = {flash = 32768, ram = 16384 } -stacksize = 8096 -start = true -task-slots = ["i2c_driver", "sensor", "sequencer"] -notifications = ["timer"] - [tasks.transceivers] name = "drv-transceivers-server" features = ["vlan"] @@ -157,7 +147,6 @@ task-slots = [ "net", "sensor", "sys", - "thermal", {front_io = "ecp5_front_io"}, {seq = "sequencer"}] notifications = ["socket", "timer"] diff --git a/app/sidecar/base.toml b/app/sidecar/base.toml index f5e25fac5a..16ebb765d6 100644 --- a/app/sidecar/base.toml +++ b/app/sidecar/base.toml @@ -226,7 +226,7 @@ interrupts = {"spi1.irq" = "spi-irq"} [tasks.transceivers] name = "drv-transceivers-server" -features = ["vlan"] +features = ["vlan", "thermal-control"] priority = 6 max-sizes = {flash = 65536, ram = 16384} stacksize = 4096 diff --git a/drv/transceivers-server/src/main.rs b/drv/transceivers-server/src/main.rs index 2618526a35..02779bcbf1 100644 --- a/drv/transceivers-server/src/main.rs +++ b/drv/transceivers-server/src/main.rs @@ -40,9 +40,11 @@ task_slot!(I2C, i2c_driver); task_slot!(FRONT_IO, front_io); task_slot!(SEQ, seq); task_slot!(NET, net); -task_slot!(THERMAL, thermal); task_slot!(SENSOR, sensor); +#[cfg(feature = "thermal-control")] +task_slot!(THERMAL, thermal); + include!(concat!(env!("OUT_DIR"), "/i2c_config.rs")); #[allow(dead_code)] @@ -127,7 +129,7 @@ struct ServerImpl { consecutive_nacks: [u8; NUM_PORTS as usize], /// Handle to write thermal models and presence to the `thermal` task - thermal_api: Thermal, + thermal_api: Option, /// Handle to write temperatures to the `sensors` task sensor_api: Sensor, @@ -378,9 +380,11 @@ impl ServerImpl { } } } else if !operational && self.thermal_models[i].is_some() { - // This transceiver went away; remove it from the thermal loop - if let Err(e) = self.thermal_api.remove_dynamic_input(i) { - ringbuf_entry!(Trace::ThermalError(i, e)); + if let Some(thermal_api) = &self.thermal_api { + // This transceiver went away; remove it from the thermal loop + if let Err(e) = thermal_api.remove_dynamic_input(i) { + ringbuf_entry!(Trace::ThermalError(i, e)); + } } // Tell the `sensor` task that this device is no longer present @@ -408,14 +412,16 @@ impl ServerImpl { None => continue, }; - // *Always* post the thermal model over to the thermal task, so that - // the thermal task still has it in case of restart. This will - // return a `NotInAutoMode` error if the thermal loop is in manual - // mode; this is harmless and will be ignored (instead of cluttering - // up the logs). - match self.thermal_api.update_dynamic_input(i, m.model) { - Ok(()) | Err(ThermalError::NotInAutoMode) => (), - Err(e) => ringbuf_entry!(Trace::ThermalError(i, e)), + if let Some(thermal_api) = &self.thermal_api { + // *Always* post the thermal model over to the thermal task, so + // that the thermal task still has it in case of restart. This + // will return a `NotInAutoMode` error if the thermal loop is in + // manual mode; this is harmless and will be ignored (instead of + // cluttering up the logs). + match thermal_api.update_dynamic_input(i, m.model) { + Ok(()) | Err(ThermalError::NotInAutoMode) => (), + Err(e) => ringbuf_entry!(Trace::ThermalError(i, e)), + } } let temperature = match m.interface { @@ -609,7 +615,6 @@ fn main() -> ! { ); let net = task_net_api::Net::from(NET.get_task_id()); - let thermal_api = Thermal::from(THERMAL.get_task_id()); let sensor_api = Sensor::from(SENSOR.get_task_id()); let (tx_data_buf, rx_data_buf) = { @@ -620,6 +625,11 @@ fn main() -> ! { BUFS.claim() }; + #[cfg(feature = "thermal-control")] + let thermal_api = Some(Thermal::from(THERMAL.get_task_id())); + #[cfg(not(feature = "thermal-control"))] + let thermal_api = None; + let mut server = ServerImpl { transceivers, leds, From b69ec1a623a54fa16c588d36f2d15f7baf394070 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 31 May 2024 09:28:38 -0500 Subject: [PATCH 09/14] delete mgmt::Config param removed in 24bdff6 --- task/net/src/bsp/medusa_a.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/task/net/src/bsp/medusa_a.rs b/task/net/src/bsp/medusa_a.rs index 95a981a511..d7e7e38cd7 100644 --- a/task/net/src/bsp/medusa_a.rs +++ b/task/net/src/bsp/medusa_a.rs @@ -68,7 +68,6 @@ impl bsp_support::Bsp for BspImpl { power_en: Some(Port::I.pin(11)), slow_power_en: false, power_good: &[], // TODO - pll_lock: None, // TODO? ksz8463: Ksz8463::new(ksz8463_dev), // SP_TO_EPE_RESET_L From 725a499bb70015decdbaab2b5c348c8276926d4e Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 31 May 2024 17:46:13 -0500 Subject: [PATCH 10/14] crimes for basic sequencing --- Cargo.lock | 18 +- app/medusa/base.toml | 26 +- drv/medusa-seq-api/Cargo.toml | 19 ++ drv/medusa-seq-api/build.rs | 10 + drv/medusa-seq-api/src/lib.rs | 37 +++ drv/medusa-seq-server/Cargo.toml | 7 +- drv/medusa-seq-server/build.rs | 14 +- drv/medusa-seq-server/src/front_io.rs | 111 ++++++++ drv/medusa-seq-server/src/main.rs | 282 +++++++++++++++++++-- drv/medusa-seq-server/src/power_control.rs | 73 ++++++ drv/sidecar-seq-api/Cargo.toml | 2 +- idl/medusa-seq.idol | 30 +++ idl/sidecar-seq.idol | 2 +- task/monorail-server/Cargo.toml | 2 + task/monorail-server/src/bsp/medusa_a.rs | 65 ++--- task/net/Cargo.toml | 3 +- task/thermal/src/bsp/medusa_a.rs | 9 +- task/thermal/src/control.rs | 2 +- 18 files changed, 610 insertions(+), 102 deletions(-) create mode 100644 drv/medusa-seq-server/src/front_io.rs create mode 100644 drv/medusa-seq-server/src/power_control.rs create mode 100644 idl/medusa-seq.idol diff --git a/Cargo.lock b/Cargo.lock index 2389567282..a0a935ecf9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1399,6 +1399,16 @@ dependencies = [ [[package]] name = "drv-medusa-seq-api" version = "0.1.0" +dependencies = [ + "counters", + "derive-idol-err", + "drv-fpga-api", + "idol", + "idol-runtime", + "num-traits", + "userlib", + "zerocopy 0.6.6", +] [[package]] name = "drv-medusa-seq-server" @@ -1406,21 +1416,21 @@ version = "0.1.0" dependencies = [ "build-i2c", "build-util", - "byteorder", "cfg-if", "cortex-m", "drv-fpga-api", "drv-fpga-user-api", "drv-i2c-api", "drv-i2c-devices", + "drv-medusa-seq-api", "drv-packrat-vpd-loader", "drv-sidecar-front-io", - "hubpack", + "drv-sidecar-mainboard-controller", + "drv-stm32xx-sys-api", "idol", "idol-runtime", "num-traits", "ringbuf", - "serde", "userlib", "zerocopy 0.6.6", ] @@ -4587,6 +4597,7 @@ dependencies = [ "build-util", "cfg-if", "counters", + "drv-medusa-seq-api", "drv-monorail-api", "drv-sidecar-front-io", "drv-sidecar-mainboard-controller", @@ -4620,6 +4631,7 @@ dependencies = [ "cortex-m", "counters", "drv-gimlet-seq-api", + "drv-medusa-seq-api", "drv-psc-seq-api", "drv-sidecar-seq-api", "drv-spi-api", diff --git a/app/medusa/base.toml b/app/medusa/base.toml index f776248b85..a5a83cc127 100644 --- a/app/medusa/base.toml +++ b/app/medusa/base.toml @@ -91,25 +91,23 @@ task-slots = ["sys", "i2c_driver"] notifications = ["spi-irq"] interrupts = {"spi1.irq" = "spi-irq"} -# [tasks.monorail] -# name = "task-monorail-server" -# priority = 6 -# max-sizes = {flash = 262144, ram = 8192} -# features = ["mgmt", "sidecar", "vlan", "use-spi-core", "h753", "spi2"] -# stacksize = 4096 -# start = true -# # task-slots = ["ecp5_front_io", "sys", { seq = "sequencer" }] -# task-slots = ["ecp5_front_io", "sys"] -# uses = ["spi2"] -# notifications = ["spi-irq", "wake-timer"] -# interrupts = {"spi2.irq" = "spi-irq"} +[tasks.monorail] +name = "task-monorail-server" +priority = 6 +max-sizes = {flash = 262144, ram = 8192} +features = ["mgmt", "medusa", "vlan", "use-spi-core", "h753", "spi2"] +stacksize = 4096 +start = true +task-slots = ["ecp5_front_io", "sys", { seq = "sequencer" }] +uses = ["spi2"] +notifications = ["spi-irq", "wake-timer"] +interrupts = {"spi2.irq" = "spi-irq"} [tasks.net] name = "task-net" stacksize = 6040 priority = 5 -# features = ["mgmt", "h753", "medusa", "vlan", "vpd-mac", "use-spi-core", "spi3"] -features = ["mgmt", "h753", "vlan", "vpd-mac", "use-spi-core", "spi3"] +features = ["mgmt", "h753", "medusa", "vlan", "vpd-mac", "use-spi-core", "spi3"] max-sizes = {flash = 131072, ram = 65536, sram1 = 16384} sections = {eth_bulk = "sram1"} uses = ["eth", "tim16", "spi3"] diff --git a/drv/medusa-seq-api/Cargo.toml b/drv/medusa-seq-api/Cargo.toml index 707c8371dc..d4f7dd29e8 100644 --- a/drv/medusa-seq-api/Cargo.toml +++ b/drv/medusa-seq-api/Cargo.toml @@ -1,3 +1,22 @@ [package] name = "drv-medusa-seq-api" version = "0.1.0" +edition = "2021" + +[dependencies] +idol-runtime.workspace = true +num-traits.workspace = true +zerocopy.workspace = true + +counters = { path = "../../lib/counters" } +derive-idol-err = { path = "../../lib/derive-idol-err" } +drv-fpga-api = { path = "../fpga-api" } +userlib = { path = "../../sys/userlib" } + +[build-dependencies] +idol.workspace = true + +[lib] +test = false +doctest = false +bench = false diff --git a/drv/medusa-seq-api/build.rs b/drv/medusa-seq-api/build.rs index 8b13789179..0195421217 100644 --- a/drv/medusa-seq-api/build.rs +++ b/drv/medusa-seq-api/build.rs @@ -1 +1,11 @@ +// 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/. +fn main() -> Result<(), Box> { + idol::client::build_client_stub( + "../../idl/medusa-seq.idol", + "client_stub.rs", + )?; + Ok(()) +} diff --git a/drv/medusa-seq-api/src/lib.rs b/drv/medusa-seq-api/src/lib.rs index 8b13789179..3b40501ddd 100644 --- a/drv/medusa-seq-api/src/lib.rs +++ b/drv/medusa-seq-api/src/lib.rs @@ -1 +1,38 @@ +// 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/. +//! API crate for the Medusa Sequencer server. + +#![no_std] + +use derive_idol_err::IdolError; +use drv_fpga_api::FpgaError; +use userlib::{sys_send, FromPrimitive}; + +#[derive(Copy, Clone, Debug, Eq, PartialEq, FromPrimitive)] +#[repr(u8)] +pub enum PowerState { + Init = 0, + A2 = 1, +} + +#[derive( + Copy, Clone, Debug, FromPrimitive, Eq, PartialEq, IdolError, counters::Count, +)] +pub enum MedusaError { + FpgaError = 1, + NoFrontIOBoard, + FrontIOBoardPowerFault, + + #[idol(server_death)] + ServerRestarted, +} + +impl From for MedusaError { + fn from(_: FpgaError) -> Self { + Self::FpgaError + } +} + +include!(concat!(env!("OUT_DIR"), "/client_stub.rs")); diff --git a/drv/medusa-seq-server/Cargo.toml b/drv/medusa-seq-server/Cargo.toml index a88518b768..00112d277f 100644 --- a/drv/medusa-seq-server/Cargo.toml +++ b/drv/medusa-seq-server/Cargo.toml @@ -4,27 +4,28 @@ version = "0.1.0" edition = "2021" [dependencies] -byteorder.workspace = true cfg-if.workspace = true cortex-m.workspace = true -hubpack.workspace = true idol-runtime.workspace = true num-traits.workspace = true -serde.workspace = true zerocopy.workspace = true drv-fpga-api = { path = "../fpga-api", features = ["auxflash"] } drv-fpga-user-api = { path = "../fpga-user-api" } drv-i2c-api = { path = "../i2c-api" } drv-i2c-devices = { path = "../i2c-devices" } +drv-medusa-seq-api = { path = "../medusa-seq-api" } drv-packrat-vpd-loader = { path = "../packrat-vpd-loader" } drv-sidecar-front-io = { path = "../sidecar-front-io", features = ["controller", "phy_smi"] } +drv-sidecar-mainboard-controller = { path = "../sidecar-mainboard-controller" } +drv-stm32xx-sys-api = { path = "../../drv/stm32xx-sys-api", features = ["family-stm32h7"] } ringbuf = { path = "../../lib/ringbuf" } userlib = { path = "../../sys/userlib", features = ["panic-messages"] } [features] h753 = ["build-i2c/h753"] stay-in-a2 = [] +no-ipc-counters = ["idol/no-counters"] [build-dependencies] build-util = { path = "../../build/util" } diff --git a/drv/medusa-seq-server/build.rs b/drv/medusa-seq-server/build.rs index e461e5c3d2..2b1451d72c 100644 --- a/drv/medusa-seq-server/build.rs +++ b/drv/medusa-seq-server/build.rs @@ -13,11 +13,15 @@ fn main() -> Result<(), Box> { std::process::exit(1); } - // idol::server::build_server_support( - // "../../idl/medusa-seq.idol", - // "server_stub.rs", - // idol::server::ServerStyle::InOrder, - // )?; + idol::Generator::new() + .with_counters( + idol::CounterSettings::default().with_server_counters(false), + ) + .build_server_support( + "../../idl/medusa-seq.idol", + "server_stub.rs", + idol::server::ServerStyle::InOrder, + )?; Ok(()) } diff --git a/drv/medusa-seq-server/src/front_io.rs b/drv/medusa-seq-server/src/front_io.rs new file mode 100644 index 0000000000..7dd35d7f7c --- /dev/null +++ b/drv/medusa-seq-server/src/front_io.rs @@ -0,0 +1,111 @@ +// 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/. + +use crate::*; +use drv_fpga_api::{DeviceState, FpgaError}; +use drv_i2c_devices::{at24csw080::At24Csw080, Validate}; +use drv_sidecar_front_io::controller::FrontIOController; +use drv_sidecar_front_io::phy_smi::PhySmi; + +#[allow(dead_code)] +pub(crate) struct FrontIOBoard { + pub controllers: [FrontIOController; 2], + fpga_task: userlib::TaskId, + auxflash_task: userlib::TaskId, +} + +impl FrontIOBoard { + pub fn new( + fpga_task: userlib::TaskId, + auxflash_task: userlib::TaskId, + ) -> Self { + Self { + controllers: [ + FrontIOController::new(fpga_task, 0), + FrontIOController::new(fpga_task, 1), + ], + fpga_task, + auxflash_task, + } + } + + pub fn phy(&self) -> PhySmi { + PhySmi::new(self.fpga_task) + } + + pub fn present(i2c_task: userlib::TaskId) -> bool { + let fruid = i2c_config::devices::at24csw080_front_io0(i2c_task)[0]; + At24Csw080::validate(&fruid).unwrap_or(false) + } + + pub fn initialized(&self) -> bool { + self.controllers.iter().all(|c| c.ready().unwrap_or(false)) + } + + pub fn init(&mut self) -> Result { + let mut controllers_ready = true; + + for (i, controller) in self.controllers.iter_mut().enumerate() { + let state = controller.await_fpga_ready(25)?; + let mut ident; + let mut ident_valid = false; + let mut checksum; + let mut checksum_valid = false; + + if state == DeviceState::RunningUserDesign { + (ident, ident_valid) = controller.ident_valid()?; + ringbuf_entry!(Trace::FrontIOControllerIdent { + fpga_id: i, + ident + }); + + (checksum, checksum_valid) = controller.checksum_valid()?; + ringbuf_entry!(Trace::FrontIOControllerChecksum { + fpga_id: i, + checksum, + expected: FrontIOController::short_checksum(), + }); + + if !ident_valid || !checksum_valid { + // Attempt to correct the invalid IDENT by reloading the + // bitstream. + controller.fpga_reset()?; + } + } + + if ident_valid && checksum_valid { + ringbuf_entry!(Trace::SkipLoadingFrontIOControllerBitstream { + fpga_id: i + }); + } else { + ringbuf_entry!(Trace::LoadingFrontIOControllerBitstream { + fpga_id: i + }); + + if let Err(e) = controller.load_bitstream(self.auxflash_task) { + ringbuf_entry!(Trace::FpgaBitstreamError(u32::from(e))); + return Err(e); + } + + (ident, ident_valid) = controller.ident_valid()?; + ringbuf_entry!(Trace::FrontIOControllerIdent { + fpga_id: i, + ident + }); + + controller.write_checksum()?; + (checksum, checksum_valid) = controller.checksum_valid()?; + ringbuf_entry!(Trace::FrontIOControllerChecksum { + fpga_id: i, + checksum, + expected: FrontIOController::short_checksum(), + }); + } + + controllers_ready &= ident_valid & checksum_valid; + } + + Ok(controllers_ready) + } +} diff --git a/drv/medusa-seq-server/src/main.rs b/drv/medusa-seq-server/src/main.rs index 586d345660..ec99b98965 100644 --- a/drv/medusa-seq-server/src/main.rs +++ b/drv/medusa-seq-server/src/main.rs @@ -7,6 +7,13 @@ #![no_std] #![no_main] +use crate::front_io::FrontIOBoard; +use crate::power_control::PowerControl; +use core::convert::Infallible; +use drv_medusa_seq_api::MedusaError; +use drv_sidecar_front_io::phy_smi::PhyOscState; +use idol_runtime::{NotificationHandler, RequestError}; +use ringbuf::{ringbuf, ringbuf_entry}; use userlib::*; task_slot!(I2C, i2c_driver); @@ -16,33 +23,270 @@ task_slot!(PACKRAT, packrat); include!(concat!(env!("OUT_DIR"), "/i2c_config.rs")); -// const TIMER_INTERVAL: u64 = 1000; +mod front_io; +mod power_control; -// struct ServerImpl { -// } +#[allow(dead_code)] +#[derive(Copy, Clone, PartialEq)] +enum Trace { + None, + FpgaBitstreamError(u32), + FrontIOBoardNotPresent, + FrontIOBoardPresent, + FrontIOBoardPowerEnable(bool), + FrontIOBoardPowerFault, + FrontIOBoardPhyPowerEnable(bool), + FrontIOBoardPhyOscGood, + FrontIOBoardPhyOscBad, + LoadingFrontIOControllerBitstream { + fpga_id: usize, + }, + SkipLoadingFrontIOControllerBitstream { + fpga_id: usize, + }, + FrontIOControllerIdent { + fpga_id: usize, + ident: u32, + }, + FrontIOControllerChecksum { + fpga_id: usize, + checksum: [u8; 4], + expected: [u8; 4], + }, +} + +ringbuf!(Trace, 32, Trace::None); + +const TIMER_INTERVAL: u64 = 1000; + +struct ServerImpl { + power_control: PowerControl, + front_io_board: Option, +} + +impl ServerImpl { + fn front_io_board_preinit(&self) -> Result { + // Enale the V12_QSFP_OUT rail + self.power_control.v12_qsfp_out.set_enable(true); + + // Wait a bit for it to ramp and then check that we can that it is happy + userlib::hl::sleep_for(50); // TODO, tune this + + // Power is not good. Disable the rail and log that this happened. + if !self.power_control.v12_qsfp_out.power_good() { + self.power_control.v12_qsfp_out.set_enable(false); + return Err(MedusaError::FrontIOBoardPowerFault); + } + + userlib::hl::sleep_for(25); + + // Determine if a front IO board is present. + Ok(FrontIOBoard::present(I2C.get_task_id())) + } + + fn actually_reset_front_io_phy(&mut self) -> Result<(), MedusaError> { + if let Some(front_io_board) = self.front_io_board.as_mut() { + if front_io_board.initialized() { + // The board was initialized prior and this function is called + // by the monorail task because it is initializing the front IO + // PHY. Unfortunately some front IO boards have PHY oscillators + // which do not start reliably when their enable pin is used and + // the only way to resolve this is by power cycling the front IO + // board. But power cycling the board also bounces any QSFP + // transceivers which may be running, so this function attempts + // to determine what the monorail task wants to do. + // + // Whether or not the PHY oscillator was found to be operating + // nominally is recorded in the front IO board controller. Look + // up what this value is to determine if a power reset of the + // front IO board is needed. + match front_io_board.phy().osc_state()? { + PhyOscState::Bad => { + // The PHY was attempted to be initialized but its + // oscillator was deemed not functional. Unfortunately + // the only course of action is to power cycle the + // entire front IO board, so do so now. + self.power_control.v12_qsfp_out.set_enable(false); + ringbuf_entry!(Trace::FrontIOBoardPowerEnable(false)); + + // Wait some cool down period to allow caps to bleed off + // etc. + userlib::hl::sleep_for(1000); + } + PhyOscState::Good => { + // The PHY was initialized properly before and its + // oscillator declared operating nominally. Assume this + // has not changed and only a reset the PHY itself is + // desired. + front_io_board + .phy() + .set_phy_power_enabled(false) + .map_err(MedusaError::from)?; + ringbuf_entry!(Trace::FrontIOBoardPhyPowerEnable( + false + )); + + userlib::hl::sleep_for(10); + } + PhyOscState::Unknown => { + // Do nothing (yet) since the oscillator state is + // unknown. + } + } + } + } + + // Run preinit to check HSC status. + self.front_io_board_preinit()?; + + if let Some(front_io_board) = self.front_io_board.as_mut() { + // At this point the front IO board has either not yet been + // initalized or may have been power cycled and should be + // initialized. + if !front_io_board.initialized() { + front_io_board.init()?; + } + + // The PHY is still powered down. Request the sequencer to power up + // and wait for it to be ready. + front_io_board.phy().set_phy_power_enabled(true)?; + ringbuf_entry!(Trace::FrontIOBoardPhyPowerEnable(true)); + + while !front_io_board.phy().powered_up_and_ready()? { + userlib::hl::sleep_for(20); + } + + Ok(()) + } else { + Err(MedusaError::NoFrontIOBoard) + } + } +} + +impl idl::InOrderSequencerImpl for ServerImpl { + fn front_io_board_present( + &mut self, + _: &RecvMessage, + ) -> Result> { + Ok(self.front_io_board.is_some()) + } + + fn set_front_io_phy_osc_state( + &mut self, + _: &RecvMessage, + good: bool, + ) -> Result<(), RequestError> { + let front_io_board = self + .front_io_board + .as_ref() + .ok_or(MedusaError::NoFrontIOBoard)?; -// impl ServerImpl { -// } + match front_io_board + .phy() + .osc_state() + .map_err(MedusaError::from) + .map_err(RequestError::from)? + { + // The state of the oscillator has not yet been examined or was + // marked bad in the previous run. Update as appropriate. + PhyOscState::Unknown | PhyOscState::Bad => { + ringbuf_entry!(if good { + Trace::FrontIOBoardPhyOscGood + } else { + Trace::FrontIOBoardPhyOscBad + }); -// impl NotificationHandler for ServerImpl { -// fn current_notification_mask(&self) -> u32 { -// notifications::TIMER_MASK -// } + front_io_board + .phy() + .set_osc_good(good) + .map_err(MedusaError::from) + .map_err(RequestError::from) + } + // The oscillator is already marked good and this state only changes + // if it (and by extension the whole front IO board) is power + // cycled. In that case the value of this register in the FPGA is + // automatically reset when the bitstream is loaded and the other + // arm of this match would be taken. + // + // So ignore this call if the oscillator has been found good since the last power + // cycle of the front IO board. + PhyOscState::Good => Ok(()), + } + } + + fn reset_front_io_phy( + &mut self, + _: &RecvMessage, + ) -> Result<(), RequestError> { + self.actually_reset_front_io_phy() + .map_err(RequestError::from) + } +} -// fn handle_notification(&mut self, _bits: u32) { -// let next_deadline = sys_get_timer().now + TIMER_INTERVAL; +impl NotificationHandler for ServerImpl { + fn current_notification_mask(&self) -> u32 { + notifications::TIMER_MASK + } -// sys_set_timer(Some(next_deadline), notifications::TIMER_MASK); -// } -// } + fn handle_notification(&mut self, _bits: u32) { + let next_deadline = sys_get_timer().now + TIMER_INTERVAL; + + sys_set_timer(Some(next_deadline), notifications::TIMER_MASK); + } +} #[export_name = "main"] fn main() -> ! { - // + let mut buffer = [0; idl::INCOMING_SIZE]; + + let mut server = ServerImpl { + power_control: PowerControl::new(), + front_io_board: None, + }; + + // Enable the front IO hot swap controller and probe for a front IO board. + match server.front_io_board_preinit() { + Ok(true) => { + ringbuf_entry!(Trace::FrontIOBoardPresent); + + let mut front_io_board = FrontIOBoard::new( + FRONT_IO.get_task_id(), + AUXFLASH.get_task_id(), + ); + + front_io_board.init().unwrap_lite(); + + // TODO (arjen): check/load VPD data into packrat. + + // So far the front IO board looks functional. Assign it to the + // server, implicitly marking it present for the lifetime of this + // task. + server.front_io_board = Some(front_io_board); + } + Ok(false) => { + ringbuf_entry!(Trace::FrontIOBoardNotPresent); + server.power_control.v12_qsfp_out.set_enable(false); + } + Err(MedusaError::FrontIOBoardPowerFault) => { + ringbuf_entry!(Trace::FrontIOBoardPowerFault) + } + // Something went wrong getting the HSC status, eject. + Err(_) => panic!("unknown front IO board preinit failure"), + } + // This will put our timer in the past, and should immediately kick us. - // - // let deadline = sys_get_timer().now; - // sys_set_timer(Some(deadline), notifications::TIMER_MASK); + let deadline = sys_get_timer().now; + sys_set_timer(Some(deadline), notifications::TIMER_MASK); - loop {} + loop { + idol_runtime::dispatch(&mut buffer, &mut server); + } } + +mod idl { + use super::MedusaError; + + include!(concat!(env!("OUT_DIR"), "/server_stub.rs")); +} + +include!(concat!(env!("OUT_DIR"), "/notifications.rs")); diff --git a/drv/medusa-seq-server/src/power_control.rs b/drv/medusa-seq-server/src/power_control.rs new file mode 100644 index 0000000000..ca34b233f3 --- /dev/null +++ b/drv/medusa-seq-server/src/power_control.rs @@ -0,0 +1,73 @@ +// 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/. + +//! A crate for managing the power supplies on the Medusa board + +use crate::*; +use drv_stm32xx_sys_api as sys_api; +use sys_api::{OutputType, Port, Pull, Speed, Sys}; + +task_slot!(SYS, sys); + +pub struct PowerRail { + enable: sys_api::PinSet, + power_good: sys_api::PinSet, +} + +impl PowerRail { + pub fn new(enable: sys_api::PinSet, power_good: sys_api::PinSet) -> Self { + let sys = Sys::from(SYS.get_task_id()); + + sys.gpio_configure_output( + enable, + OutputType::PushPull, + Speed::Low, + Pull::None, + ); + + sys.gpio_configure_input(power_good, Pull::None); + + Self { enable, power_good } + } + pub fn set_enable(&self, enabled: bool) { + let sys = Sys::from(SYS.get_task_id()); + ringbuf_entry!(Trace::FrontIOBoardPowerEnable(enabled)); + sys.gpio_set_to(self.enable, enabled) + } + + pub fn power_good(&self) -> bool { + let sys = Sys::from(SYS.get_task_id()); + sys.gpio_read(self.power_good) != 0 + } +} + +pub struct PowerControl { + pub v12_qsfp_out: PowerRail, + pub v1p0_mgmt: PowerRail, + pub v1p2_mgmt: PowerRail, + pub v2p5_mgmt: PowerRail, + pub v1p0_phy: PowerRail, + pub v2p5_phy: PowerRail, +} + +impl PowerControl { + pub fn new() -> Self { + let v12_qsfp_out = PowerRail::new(Port::J.pin(2), Port::J.pin(1)); + let v1p0_mgmt = PowerRail::new(Port::J.pin(4), Port::J.pin(3)); + let v1p2_mgmt = PowerRail::new(Port::J.pin(6), Port::J.pin(5)); + let v2p5_mgmt = PowerRail::new(Port::J.pin(8), Port::J.pin(7)); + // The PHY rails are generated from the same LDO which shares an enable pin + let v1p0_phy = PowerRail::new(Port::J.pin(10), Port::J.pin(11)); + let v2p5_phy = PowerRail::new(Port::J.pin(10), Port::J.pin(12)); + + Self { + v12_qsfp_out, + v1p0_mgmt, + v1p2_mgmt, + v2p5_mgmt, + v1p0_phy, + v2p5_phy, + } + } +} diff --git a/drv/sidecar-seq-api/Cargo.toml b/drv/sidecar-seq-api/Cargo.toml index c5329ad74e..9fe990d81e 100644 --- a/drv/sidecar-seq-api/Cargo.toml +++ b/drv/sidecar-seq-api/Cargo.toml @@ -10,7 +10,7 @@ num-traits.workspace = true serde.workspace = true zerocopy.workspace = true counters = { path = "../../lib/counters" } -derive-idol-err = { path = "../../lib/derive-idol-err" } +derive-idol-err = { path = "../../lib/derive-idol-err" } drv-fpga-api = { path = "../fpga-api" } drv-fpga-user-api = { path = "../fpga-user-api" } drv-sidecar-mainboard-controller = { path = "../sidecar-mainboard-controller" } diff --git a/idl/medusa-seq.idol b/idl/medusa-seq.idol new file mode 100644 index 0000000000..48862a8e9f --- /dev/null +++ b/idl/medusa-seq.idol @@ -0,0 +1,30 @@ +// Medusa Sequencer API + +Interface( + name: "Sequencer", + ops: { + "front_io_board_present": ( + args: {}, + reply: Simple("bool"), + idempotent: true, + ), + + "set_front_io_phy_osc_state": ( + args: { + "good": "bool", + }, + reply: Result( + ok: "()", + err: CLike("MedusaError"), + ), + ), + + "reset_front_io_phy": ( + args: {}, + reply: Result( + ok: "()", + err: CLike("MedusaError"), + ), + ), + }, +) diff --git a/idl/sidecar-seq.idol b/idl/sidecar-seq.idol index 3e037f520a..2eeaed53d2 100644 --- a/idl/sidecar-seq.idol +++ b/idl/sidecar-seq.idol @@ -1,4 +1,4 @@ -// Gimlet Sequencer API +// Sidecar Sequencer API Interface( name: "Sequencer", diff --git a/task/monorail-server/Cargo.toml b/task/monorail-server/Cargo.toml index 827218c1ba..7f0de74d7f 100644 --- a/task/monorail-server/Cargo.toml +++ b/task/monorail-server/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] +drv-medusa-seq-api = { path = "../../drv/medusa-seq-api", optional = true } drv-monorail-api = { path = "../../drv/monorail-api" } drv-sidecar-mainboard-controller = { path = "../../drv/sidecar-mainboard-controller" } drv-sidecar-front-io = { path = "../../drv/sidecar-front-io", features = ["phy_smi"], optional = true } @@ -29,6 +30,7 @@ zerocopy.workspace = true [features] leds = ["drv-user-leds-api"] +medusa = ["drv-medusa-seq-api", "drv-sidecar-front-io"] mgmt = ["task-net-api"] sidecar = ["drv-sidecar-seq-api", "drv-sidecar-front-io"] vlan = ["task-net-api?/vlan"] diff --git a/task/monorail-server/src/bsp/medusa_a.rs b/task/monorail-server/src/bsp/medusa_a.rs index 62505cc2c7..73034b92c6 100644 --- a/task/monorail-server/src/bsp/medusa_a.rs +++ b/task/monorail-server/src/bsp/medusa_a.rs @@ -2,20 +2,21 @@ // 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/. +use drv_medusa_seq_api::Sequencer; use drv_sidecar_front_io::phy_smi::PhySmi; use ringbuf::*; -use userlib::{hl::sleep_for, task_slot}; +use userlib::{task_slot, UnwrapLite}; use vsc7448::{ config::Speed, miim_phy::Vsc7448MiimPhy, Vsc7448, Vsc7448Rw, VscError, }; use vsc7448_pac::{DEVCPU_GCB, HSIO, VAUI0, VAUI1}; use vsc85xx::{vsc8504::Vsc8504, vsc8562::Vsc8562Phy, PhyRw}; +task_slot!(SEQ, seq); task_slot!(FRONT_IO, ecp5_front_io); -/// Interval at which `Bsp::wake()` is called by the main loop -const WAKE_INTERVAL_MS: u64 = 500; -pub const WAKE_INTERVAL: Option = Some(WAKE_INTERVAL_MS); +/// Interval in milliseconds at which `Bsp::wake()` is called by the main loop +pub const WAKE_INTERVAL: Option = Some(500); #[derive(Copy, Clone, PartialEq)] enum Trace { @@ -27,7 +28,6 @@ enum Trace { }, FrontIoPhyOscillatorBad, AnegCheckFailed(VscError), - Restarted10GAneg, Reinit, } ringbuf!(Trace, 16, Trace::None); @@ -37,6 +37,9 @@ ringbuf!(Trace, 16, Trace::None); pub struct Bsp<'a, R> { vsc7448: &'a Vsc7448<'a, R>, + /// Handle for the sequencer task + seq: Sequencer, + /// PHY for the on-board PHY ("PHY4") vsc8504: Vsc8504, @@ -132,10 +135,16 @@ mod map { SGMII, // 52 | DEV2G5_28 | SERDES10G_3 | Cubby 31 (shadows DEV10G_3) ]); } +pub use map::PORT_MAP; + +pub fn preinit() { + // Nothing to do here, just stubbing out for the BSP interface +} impl<'a, R: Vsc7448Rw> Bsp<'a, R> { /// Constructs and initializes a new BSP handle pub fn new(vsc7448: &'a Vsc7448<'a, R>) -> Result { + let seq = Sequencer::from(SEQ.get_task_id()); let has_front_io = seq.front_io_board_present(); let mut out = Bsp { vsc7448, @@ -147,16 +156,13 @@ impl<'a, R: Vsc7448Rw> Bsp<'a, R> { }, front_io_speed: [Speed::Speed1G; 2], link_down_at: None, + seq, }; out.reinit()?; Ok(out) } - fn preinit() { - // TODO - } - pub fn reinit(&mut self) -> Result<(), VscError> { ringbuf_entry!(Trace::Reinit); self.vsc7448.init()?; @@ -355,7 +361,7 @@ impl<'a, R: Vsc7448Rw> Bsp<'a, R> { // The VSC8504 on the sidecar has its SIGDET GPIOs pulled down, // for some reason. - self.vsc8504.set_sigdet_polarity(rw, true).unwrap(); + self.vsc8504.set_sigdet_polarity(rw, true).unwrap_lite(); // Switch the GPIO to an output. Since the output register is low // by default, this pulls COMA_MODE low, bringing the VSC8504 into @@ -459,44 +465,7 @@ impl<'a, R: Vsc7448Rw> Bsp<'a, R> { } } - // Workaround for the link-stuck issue discussed in - // https://github.com/oxidecomputer/bf_sde/issues/5. This should only - // run if the Tofino PCIe link is up otherwise the Tofino port is almost - // certainly down and the condition we are trying to avoid here does not - // matter. - if self.seq.tofino_pcie_link_up() { - if self.vsc7448.check_10gbase_kr_aneg(0)? { - ringbuf_entry!(Trace::Restarted10GAneg); - } - - // Follow-up workaround for the other link-stuck issue discussed in - // https://github.com/oxidecomputer/bf_sde/issues/27 - // If we remain down for 20 seconds, then reinitialize everything. - // - // (see comment in `monorail-server/src/server.rs` about this check) - use vsc7448_pac::PCS10G_BR; - let link_down = self - .vsc7448 - .read(PCS10G_BR(0).PCS_10GBR_STATUS().PCS_STATUS())? - .rx_block_lock() - == 0; - if link_down { - let now = userlib::sys_get_timer().now; - if let Some(link_down_at) = self.link_down_at { - if now - link_down_at >= 20_000 { - self.link_down_at = None; - // This logs Trace::Reinit in the ringbuf - self.reinit()?; - } - } else { - self.link_down_at = Some(now); - } - } else { - self.link_down_at = None; - } - } else { - self.link_down_at = None; - } + self.link_down_at = None; Ok(()) } diff --git a/task/net/Cargo.toml b/task/net/Cargo.toml index 6d32e04de8..9ee6fe93a0 100644 --- a/task/net/Cargo.toml +++ b/task/net/Cargo.toml @@ -21,6 +21,7 @@ zerocopy = { workspace = true } counters = { path = "../../lib/counters" } drv-gimlet-seq-api = { path = "../../drv/gimlet-seq-api", optional = true } +drv-medusa-seq-api = { path = "../../drv/medusa-seq-api", optional = true } drv-psc-seq-api = { path = "../../drv/psc-seq-api", optional = true } drv-sidecar-seq-api = { path = "../../drv/sidecar-seq-api", optional = true } drv-spi-api = { path = "../../drv/spi-api", optional = true } @@ -46,7 +47,7 @@ mgmt = ["drv-spi-api", "ksz8463", "drv-user-leds-api", "task-net-api/mgmt"] vpd-mac = ["task-packrat-api"] gimlet = ["drv-gimlet-seq-api"] sidecar = ["drv-sidecar-seq-api"] -# medusa = ["drv-medusa-seq-api"] +medusa = ["drv-medusa-seq-api"] psc = ["drv-psc-seq-api"] h743 = ["drv-stm32h7-eth/h743", "stm32h7/stm32h743", "drv-stm32xx-sys-api/h743", "drv-stm32h7-spi-server-core?/h743"] h753 = ["drv-stm32h7-eth/h753", "stm32h7/stm32h753", "drv-stm32xx-sys-api/h753", "drv-stm32h7-spi-server-core?/h753"] diff --git a/task/thermal/src/bsp/medusa_a.rs b/task/thermal/src/bsp/medusa_a.rs index 804480caaa..7532b9fdd7 100644 --- a/task/thermal/src/bsp/medusa_a.rs +++ b/task/thermal/src/bsp/medusa_a.rs @@ -5,8 +5,7 @@ //! BSP for Medusa use crate::control::{ - FanControl, Fans, InputChannel, PidConfig, - TemperatureSensor, + FanControl, Fans, InputChannel, PidConfig, TemperatureSensor, }; use task_sensor_api::SensorId; use userlib::TaskId; @@ -100,8 +99,6 @@ impl Bsp { } } -const INPUTS: [InputChannel; NUM_TEMPERATURE_INPUTS] = [ -]; +const INPUTS: [InputChannel; NUM_TEMPERATURE_INPUTS] = []; -const MISC_SENSORS: [TemperatureSensor; NUM_TEMPERATURE_SENSORS] = [ -]; +const MISC_SENSORS: [TemperatureSensor; NUM_TEMPERATURE_SENSORS] = []; diff --git a/task/thermal/src/control.rs b/task/thermal/src/control.rs index 7f374d5ebb..f33864f074 100644 --- a/task/thermal/src/control.rs +++ b/task/thermal/src/control.rs @@ -54,7 +54,7 @@ pub struct TemperatureSensor { } impl TemperatureSensor { -#[allow(dead_code)] // not all BSPS + #[allow(dead_code)] // not all BSPS pub const fn new( device: Device, builder: fn(TaskId) -> drv_i2c_api::I2cDevice, From 2f8a49ad41ebd91f9d9e9dce15b6a25668b2fdda Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 3 Jun 2024 12:05:44 -0500 Subject: [PATCH 11/14] some basic power sequencing and idl interface to for rail control --- Cargo.lock | 4 + drv/medusa-seq-api/Cargo.toml | 2 + drv/medusa-seq-api/src/lib.rs | 18 +++- drv/medusa-seq-server/Cargo.toml | 2 + drv/medusa-seq-server/src/main.rs | 78 +++++++++++++-- drv/medusa-seq-server/src/power_control.rs | 110 ++++++++++++++++++--- drv/transceivers-server/Cargo.toml | 1 + idl/medusa-seq.idol | 30 ++++++ 8 files changed, 222 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a0a935ecf9..b277720726 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1403,9 +1403,11 @@ dependencies = [ "counters", "derive-idol-err", "drv-fpga-api", + "hubpack", "idol", "idol-runtime", "num-traits", + "serde", "userlib", "zerocopy 0.6.6", ] @@ -1427,10 +1429,12 @@ dependencies = [ "drv-sidecar-front-io", "drv-sidecar-mainboard-controller", "drv-stm32xx-sys-api", + "hubpack", "idol", "idol-runtime", "num-traits", "ringbuf", + "serde", "userlib", "zerocopy 0.6.6", ] diff --git a/drv/medusa-seq-api/Cargo.toml b/drv/medusa-seq-api/Cargo.toml index d4f7dd29e8..ccc0a311f6 100644 --- a/drv/medusa-seq-api/Cargo.toml +++ b/drv/medusa-seq-api/Cargo.toml @@ -4,8 +4,10 @@ version = "0.1.0" edition = "2021" [dependencies] +hubpack.workspace = true idol-runtime.workspace = true num-traits.workspace = true +serde.workspace = true zerocopy.workspace = true counters = { path = "../../lib/counters" } diff --git a/drv/medusa-seq-api/src/lib.rs b/drv/medusa-seq-api/src/lib.rs index 3b40501ddd..70cbd9c6f3 100644 --- a/drv/medusa-seq-api/src/lib.rs +++ b/drv/medusa-seq-api/src/lib.rs @@ -8,13 +8,20 @@ use derive_idol_err::IdolError; use drv_fpga_api::FpgaError; +use hubpack::SerializedSize; +use serde::{Deserialize, Serialize}; use userlib::{sys_send, FromPrimitive}; -#[derive(Copy, Clone, Debug, Eq, PartialEq, FromPrimitive)] -#[repr(u8)] -pub enum PowerState { - Init = 0, - A2 = 1, +#[derive( + Copy, Clone, Debug, PartialEq, Deserialize, Serialize, SerializedSize, +)] +pub enum RailName { + V1P0Mgmt, + V1P2Mgmt, + V2P5Mgmt, + V1P0Phy, + V2P5Phy, + V12QsfpOut, } #[derive( @@ -24,6 +31,7 @@ pub enum MedusaError { FpgaError = 1, NoFrontIOBoard, FrontIOBoardPowerFault, + PowerFault, #[idol(server_death)] ServerRestarted, diff --git a/drv/medusa-seq-server/Cargo.toml b/drv/medusa-seq-server/Cargo.toml index 00112d277f..eb4e4bc11b 100644 --- a/drv/medusa-seq-server/Cargo.toml +++ b/drv/medusa-seq-server/Cargo.toml @@ -6,8 +6,10 @@ edition = "2021" [dependencies] cfg-if.workspace = true cortex-m.workspace = true +hubpack.workspace = true idol-runtime.workspace = true num-traits.workspace = true +serde.workspace = true zerocopy.workspace = true drv-fpga-api = { path = "../fpga-api", features = ["auxflash"] } diff --git a/drv/medusa-seq-server/src/main.rs b/drv/medusa-seq-server/src/main.rs index ec99b98965..3dfac69747 100644 --- a/drv/medusa-seq-server/src/main.rs +++ b/drv/medusa-seq-server/src/main.rs @@ -10,7 +10,7 @@ use crate::front_io::FrontIOBoard; use crate::power_control::PowerControl; use core::convert::Infallible; -use drv_medusa_seq_api::MedusaError; +use drv_medusa_seq_api::{MedusaError, RailName}; use drv_sidecar_front_io::phy_smi::PhyOscState; use idol_runtime::{NotificationHandler, RequestError}; use ringbuf::{ringbuf, ringbuf_entry}; @@ -34,6 +34,7 @@ enum Trace { FrontIOBoardNotPresent, FrontIOBoardPresent, FrontIOBoardPowerEnable(bool), + FrontIOBoardPowerGood, FrontIOBoardPowerFault, FrontIOBoardPhyPowerEnable(bool), FrontIOBoardPhyOscGood, @@ -53,6 +54,10 @@ enum Trace { checksum: [u8; 4], expected: [u8; 4], }, + PowerEnable(RailName, bool), + PowerFault(RailName), + MgmtPowerGood, + PhyPowerGood, } ringbuf!(Trace, 32, Trace::None); @@ -70,11 +75,12 @@ impl ServerImpl { self.power_control.v12_qsfp_out.set_enable(true); // Wait a bit for it to ramp and then check that we can that it is happy - userlib::hl::sleep_for(50); // TODO, tune this + // The EN->PG time for this part was experimentally determined to be + // 35ms, so we roughly double that. + userlib::hl::sleep_for(75); // Power is not good. Disable the rail and log that this happened. - if !self.power_control.v12_qsfp_out.power_good() { - self.power_control.v12_qsfp_out.set_enable(false); + if !self.power_control.v12_qsfp_out.check_power_good() { return Err(MedusaError::FrontIOBoardPowerFault); } @@ -164,6 +170,56 @@ impl ServerImpl { } impl idl::InOrderSequencerImpl for ServerImpl { + fn control_mgmt_rails( + &mut self, + _: &RecvMessage, + enabled: bool, + ) -> Result<(), RequestError> { + self.power_control.v1p0_mgmt.set_enable(enabled); + self.power_control.v1p2_mgmt.set_enable(enabled); + self.power_control.v2p5_mgmt.set_enable(enabled); + + if enabled { + userlib::hl::sleep_for(10); + if !self.power_control.mgmt_power_check() { + return Err(RequestError::from(MedusaError::PowerFault)); + } + ringbuf_entry!(Trace::MgmtPowerGood); + } + + Ok(()) + } + + fn control_phy_rails( + &mut self, + _: &RecvMessage, + enabled: bool, + ) -> Result<(), RequestError> { + self.power_control.v1p0_phy.set_enable(enabled); + self.power_control.v2p5_phy.set_enable(enabled); + + if enabled { + userlib::hl::sleep_for(10); + if !self.power_control.phy_power_check() { + return Err(RequestError::from(MedusaError::PowerFault)); + } + ringbuf_entry!(Trace::PhyPowerGood); + } + + Ok(()) + } + + fn control_rail( + &mut self, + _: &RecvMessage, + name: RailName, + enabled: bool, + ) -> Result<(), RequestError> { + let rail = self.power_control.get_rail(name); + rail.set_enable(enabled); + Ok(()) + } + fn front_io_board_present( &mut self, _: &RecvMessage, @@ -248,6 +304,7 @@ fn main() -> ! { match server.front_io_board_preinit() { Ok(true) => { ringbuf_entry!(Trace::FrontIOBoardPresent); + ringbuf_entry!(Trace::FrontIOBoardPowerGood); let mut front_io_board = FrontIOBoard::new( FRONT_IO.get_task_id(), @@ -256,7 +313,7 @@ fn main() -> ! { front_io_board.init().unwrap_lite(); - // TODO (arjen): check/load VPD data into packrat. + // TODO: check/load VPD data into packrat. // So far the front IO board looks functional. Assign it to the // server, implicitly marking it present for the lifetime of this @@ -274,6 +331,15 @@ fn main() -> ! { Err(_) => panic!("unknown front IO board preinit failure"), } + // The MGMT and PHY rails are enabled automatically by pullups, so we will + // check their power good signals and take action as appropriate. + if server.power_control.mgmt_power_check() { + ringbuf_entry!(Trace::MgmtPowerGood); + } + if server.power_control.phy_power_check() { + ringbuf_entry!(Trace::PhyPowerGood); + } + // This will put our timer in the past, and should immediately kick us. let deadline = sys_get_timer().now; sys_set_timer(Some(deadline), notifications::TIMER_MASK); @@ -284,7 +350,7 @@ fn main() -> ! { } mod idl { - use super::MedusaError; + use super::{MedusaError, RailName}; include!(concat!(env!("OUT_DIR"), "/server_stub.rs")); } diff --git a/drv/medusa-seq-server/src/power_control.rs b/drv/medusa-seq-server/src/power_control.rs index ca34b233f3..2b9e4346e8 100644 --- a/drv/medusa-seq-server/src/power_control.rs +++ b/drv/medusa-seq-server/src/power_control.rs @@ -11,12 +11,20 @@ use sys_api::{OutputType, Port, Pull, Speed, Sys}; task_slot!(SYS, sys); pub struct PowerRail { + /// The output GPIO for the power rail's enable pin enable: sys_api::PinSet, + /// The input GPIO for the power rail's power good pin power_good: sys_api::PinSet, + /// A RailName variant for ringbuf activity + name: RailName, } impl PowerRail { - pub fn new(enable: sys_api::PinSet, power_good: sys_api::PinSet) -> Self { + pub fn new( + enable: sys_api::PinSet, + power_good: sys_api::PinSet, + name: RailName, + ) -> Self { let sys = Sys::from(SYS.get_task_id()); sys.gpio_configure_output( @@ -28,15 +36,35 @@ impl PowerRail { sys.gpio_configure_input(power_good, Pull::None); - Self { enable, power_good } + Self { + enable, + power_good, + name, + } } + + /// Sets the enable pin for the power rail to HIGH if `enabled` is true or + /// LOW if it is false. If `enabled` is true, this function will also + /// monitor the power good signal for the supply. pub fn set_enable(&self, enabled: bool) { let sys = Sys::from(SYS.get_task_id()); - ringbuf_entry!(Trace::FrontIOBoardPowerEnable(enabled)); - sys.gpio_set_to(self.enable, enabled) + sys.gpio_set_to(self.enable, enabled); + ringbuf_entry!(Trace::PowerEnable(self.name, enabled)); + } + + /// Returns the status of the power good pin. If power good is not HIGH this + /// function disables the power rail automatically. + pub fn check_power_good(&self) -> bool { + if !self.power_good() { + ringbuf_entry!(Trace::PowerFault(self.name)); + self.set_enable(false); + return false; + } + true } - pub fn power_good(&self) -> bool { + /// Returns the status of the power good signal for the rail + fn power_good(&self) -> bool { let sys = Sys::from(SYS.get_task_id()); sys.gpio_read(self.power_good) != 0 } @@ -53,13 +81,29 @@ pub struct PowerControl { impl PowerControl { pub fn new() -> Self { - let v12_qsfp_out = PowerRail::new(Port::J.pin(2), Port::J.pin(1)); - let v1p0_mgmt = PowerRail::new(Port::J.pin(4), Port::J.pin(3)); - let v1p2_mgmt = PowerRail::new(Port::J.pin(6), Port::J.pin(5)); - let v2p5_mgmt = PowerRail::new(Port::J.pin(8), Port::J.pin(7)); - // The PHY rails are generated from the same LDO which shares an enable pin - let v1p0_phy = PowerRail::new(Port::J.pin(10), Port::J.pin(11)); - let v2p5_phy = PowerRail::new(Port::J.pin(10), Port::J.pin(12)); + // Wait a bit for it to ramp and then check that we can that it is happy + // The EN->PG time for this part was experimentally determined to be + // 35ms, so we roughly double that. + let v12_qsfp_out = PowerRail::new( + Port::J.pin(2), + Port::J.pin(1), + RailName::V12QsfpOut, + ); + + // VSC7448 rails + let v1p0_mgmt = + PowerRail::new(Port::J.pin(4), Port::J.pin(3), RailName::V1P0Mgmt); + let v1p2_mgmt = + PowerRail::new(Port::J.pin(6), Port::J.pin(5), RailName::V1P2Mgmt); + let v2p5_mgmt = + PowerRail::new(Port::J.pin(8), Port::J.pin(7), RailName::V2P5Mgmt); + + // The VSC8562 rails are generated from the same LDO which shares an + // enable pin + let v1p0_phy = + PowerRail::new(Port::J.pin(10), Port::J.pin(11), RailName::V1P0Phy); + let v2p5_phy = + PowerRail::new(Port::J.pin(10), Port::J.pin(12), RailName::V2P5Phy); Self { v12_qsfp_out, @@ -70,4 +114,46 @@ impl PowerControl { v2p5_phy, } } + + /// Returns true if all MGMT power rails are good. If that is not the case, + /// disable all management rails and returns false. + pub fn mgmt_power_check(&self) -> bool { + let all_good = self.v1p0_mgmt.check_power_good() + && self.v1p2_mgmt.check_power_good() + && self.v2p5_mgmt.check_power_good(); + + if !all_good { + self.v1p0_mgmt.set_enable(false); + self.v1p2_mgmt.set_enable(false); + self.v2p5_mgmt.set_enable(false); + } + + all_good + } + + /// Returns true if all PHY power rails are good. If that is not the case, + /// disable all management rails and returns false. + pub fn phy_power_check(&self) -> bool { + let all_good = self.v1p0_phy.check_power_good() + && self.v2p5_phy.check_power_good(); + + if !all_good { + self.v1p0_phy.set_enable(false); + self.v2p5_phy.set_enable(false); + } + + all_good + } + + pub fn get_rail(&self, name: RailName) -> &PowerRail { + use RailName::*; + match name { + V1P0Mgmt => &self.v1p0_mgmt, + V1P2Mgmt => &self.v1p2_mgmt, + V2P5Mgmt => &self.v2p5_mgmt, + V1P0Phy => &self.v1p0_phy, + V2P5Phy => &self.v2p5_phy, + V12QsfpOut => &self.v12_qsfp_out, + } + } } diff --git a/drv/transceivers-server/Cargo.toml b/drv/transceivers-server/Cargo.toml index e10c5c8006..24a9c07aa4 100644 --- a/drv/transceivers-server/Cargo.toml +++ b/drv/transceivers-server/Cargo.toml @@ -33,6 +33,7 @@ zerocopy = { workspace = true } [features] vlan = ["task-net-api/vlan"] +thermal-control = [] no-ipc-counters = ["idol/no-counters"] [build-dependencies] diff --git a/idl/medusa-seq.idol b/idl/medusa-seq.idol index 48862a8e9f..c75b858e11 100644 --- a/idl/medusa-seq.idol +++ b/idl/medusa-seq.idol @@ -26,5 +26,35 @@ Interface( err: CLike("MedusaError"), ), ), + + "control_mgmt_rails": ( + args: { + "enabled": "bool", + }, + reply: Result( + ok: "()", + err: CLike("MedusaError"), + ), + ), + + "control_phy_rails": ( + args: { + "enabled": "bool", + }, + reply: Result( + ok: "()", + err: CLike("MedusaError"), + ), + ), + + "control_rail": ( + args: { + "name": "RailName", + "enabled": "bool", + }, + reply: Simple("()"), + idempotent: true, + encoding: Hubpack, + ), }, ) From 7d21b892145c083be590e59e8fe658b0c9c416bc Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 3 Jun 2024 13:29:23 -0500 Subject: [PATCH 12/14] fix ID pin reads --- app/medusa/src/main.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/app/medusa/src/main.rs b/app/medusa/src/main.rs index 3beb4e06d0..ca6260f187 100644 --- a/app/medusa/src/main.rs +++ b/app/medusa/src/main.rs @@ -60,30 +60,30 @@ fn system_init() { // if you've flashed the wrong one, only. // // The model is on the following pins: - // - ID0: PE6 - // - ID1: PE7 - // - ID2: PE8 - // Un-gate the clock to GPIO bank E. + // - ID0: PC6 + // - ID1: PC7 + // - ID2: PC13 + // Un-gate the clock to GPIO bank C. p.RCC.ahb4enr.modify(|_, w| w.gpiogen().set_bit()); cortex_m::asm::dsb(); - // PE8:6 are already inputs after reset + // PE{13,7,6} are already inputs after reset #[rustfmt::skip] - p.GPIOG.moder.modify(|_, w| w + p.GPIOC.moder.modify(|_, w| w .moder6().input() .moder7().input() - .moder8().input()); + .moder13().input()); // Unlike other designs, we aren't using any internal pullup resistors, so // we won't wait for the inputs or ID traces to charge. - let id_mask = (1 << 6) | (1 << 7) | (1 << 8); - let model = p.GPIOE.idr.read().bits() & id_mask; + let id_mask = (1 << 6) | (1 << 7) | (1 << 13); + let model = p.GPIOC.idr.read().bits() & id_mask; cfg_if::cfg_if! { if #[cfg(target_board = "medusa-a")] { let expected_model = 0b000; } else { - compile_error!("not a recognized medua board"); + compile_error!("not a recognized medusa board"); } } From 283b98ecdbce6ffd4c42e6918680473d0d55dda0 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 11 Jun 2024 12:54:39 -0500 Subject: [PATCH 13/14] feedback from aapoalas --- drv/medusa-seq-server/src/main.rs | 9 ++++----- drv/medusa-seq-server/src/power_control.rs | 9 +++------ 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/drv/medusa-seq-server/src/main.rs b/drv/medusa-seq-server/src/main.rs index 3dfac69747..81015153cc 100644 --- a/drv/medusa-seq-server/src/main.rs +++ b/drv/medusa-seq-server/src/main.rs @@ -74,7 +74,7 @@ impl ServerImpl { // Enale the V12_QSFP_OUT rail self.power_control.v12_qsfp_out.set_enable(true); - // Wait a bit for it to ramp and then check that we can that it is happy + // Wait a bit for it to ramp and then check that it is happy. // The EN->PG time for this part was experimentally determined to be // 35ms, so we roughly double that. userlib::hl::sleep_for(75); @@ -84,8 +84,6 @@ impl ServerImpl { return Err(MedusaError::FrontIOBoardPowerFault); } - userlib::hl::sleep_for(25); - // Determine if a front IO board is present. Ok(FrontIOBoard::present(I2C.get_task_id())) } @@ -327,8 +325,9 @@ fn main() -> ! { Err(MedusaError::FrontIOBoardPowerFault) => { ringbuf_entry!(Trace::FrontIOBoardPowerFault) } - // Something went wrong getting the HSC status, eject. - Err(_) => panic!("unknown front IO board preinit failure"), + // `front_io_board_preinit` currently only returns a + // MedusaError::FrontIOBoardPowerFault + Err(_) => unreachable!(), } // The MGMT and PHY rails are enabled automatically by pullups, so we will diff --git a/drv/medusa-seq-server/src/power_control.rs b/drv/medusa-seq-server/src/power_control.rs index 2b9e4346e8..f778316a7f 100644 --- a/drv/medusa-seq-server/src/power_control.rs +++ b/drv/medusa-seq-server/src/power_control.rs @@ -44,8 +44,7 @@ impl PowerRail { } /// Sets the enable pin for the power rail to HIGH if `enabled` is true or - /// LOW if it is false. If `enabled` is true, this function will also - /// monitor the power good signal for the supply. + /// LOW if it is false. pub fn set_enable(&self, enabled: bool) { let sys = Sys::from(SYS.get_task_id()); sys.gpio_set_to(self.enable, enabled); @@ -81,9 +80,7 @@ pub struct PowerControl { impl PowerControl { pub fn new() -> Self { - // Wait a bit for it to ramp and then check that we can that it is happy - // The EN->PG time for this part was experimentally determined to be - // 35ms, so we roughly double that. + // 12V HSC for the Front IO board let v12_qsfp_out = PowerRail::new( Port::J.pin(2), Port::J.pin(1), @@ -132,7 +129,7 @@ impl PowerControl { } /// Returns true if all PHY power rails are good. If that is not the case, - /// disable all management rails and returns false. + /// disable all PHY rails and returns false. pub fn phy_power_check(&self) -> bool { let all_good = self.v1p0_phy.check_power_good() && self.v2p5_phy.check_power_good(); From 92a46ef899819c6c259006346430b2335001d053 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 13 Jun 2024 15:40:01 -0500 Subject: [PATCH 14/14] feedback from Eliza --- drv/medusa-seq-api/src/lib.rs | 2 ++ drv/medusa-seq-server/src/main.rs | 38 ++++++++++++++--------------- drv/transceivers-server/src/main.rs | 21 +++++++++------- task/net/src/bsp/medusa_a.rs | 5 +--- task/thermal/Cargo.toml | 6 ++--- task/thermal/src/bsp/medusa_a.rs | 3 +-- 6 files changed, 38 insertions(+), 37 deletions(-) diff --git a/drv/medusa-seq-api/src/lib.rs b/drv/medusa-seq-api/src/lib.rs index 70cbd9c6f3..25b7eda25d 100644 --- a/drv/medusa-seq-api/src/lib.rs +++ b/drv/medusa-seq-api/src/lib.rs @@ -30,7 +30,9 @@ pub enum RailName { pub enum MedusaError { FpgaError = 1, NoFrontIOBoard, + // The Front IO board power faulted FrontIOBoardPowerFault, + // An power supply on Medusa faulted PowerFault, #[idol(server_death)] diff --git a/drv/medusa-seq-server/src/main.rs b/drv/medusa-seq-server/src/main.rs index 81015153cc..dac64e3423 100644 --- a/drv/medusa-seq-server/src/main.rs +++ b/drv/medusa-seq-server/src/main.rs @@ -71,7 +71,7 @@ struct ServerImpl { impl ServerImpl { fn front_io_board_preinit(&self) -> Result { - // Enale the V12_QSFP_OUT rail + // Enable the V12_QSFP_OUT rail self.power_control.v12_qsfp_out.set_enable(true); // Wait a bit for it to ramp and then check that it is happy. @@ -143,27 +143,27 @@ impl ServerImpl { // Run preinit to check HSC status. self.front_io_board_preinit()?; - if let Some(front_io_board) = self.front_io_board.as_mut() { - // At this point the front IO board has either not yet been - // initalized or may have been power cycled and should be - // initialized. - if !front_io_board.initialized() { - front_io_board.init()?; - } - - // The PHY is still powered down. Request the sequencer to power up - // and wait for it to be ready. - front_io_board.phy().set_phy_power_enabled(true)?; - ringbuf_entry!(Trace::FrontIOBoardPhyPowerEnable(true)); + let front_io_board = self + .front_io_board + .as_mut() + .ok_or(MedusaError::NoFrontIOBoard)?; - while !front_io_board.phy().powered_up_and_ready()? { - userlib::hl::sleep_for(20); - } + // At this point the front IO board has either not yet been + // initialized or may have been power cycled and should be + // initialized. + if !front_io_board.initialized() { + front_io_board.init()?; + } - Ok(()) - } else { - Err(MedusaError::NoFrontIOBoard) + // The PHY is still powered down. Request the sequencer to power up + // and wait for it to be ready. + front_io_board.phy().set_phy_power_enabled(true)?; + ringbuf_entry!(Trace::FrontIOBoardPhyPowerEnable(true)); + while !front_io_board.phy().powered_up_and_ready()? { + userlib::hl::sleep_for(20); } + + Ok(()) } } diff --git a/drv/transceivers-server/src/main.rs b/drv/transceivers-server/src/main.rs index 02779bcbf1..50fc5cf4c7 100644 --- a/drv/transceivers-server/src/main.rs +++ b/drv/transceivers-server/src/main.rs @@ -27,6 +27,7 @@ use drv_transceivers_api::{ }; use enum_map::Enum; use task_sensor_api::{NoData, Sensor}; +#[allow(unused_imports)] use task_thermal_api::{Thermal, ThermalError, ThermalProperties}; use transceiver_messages::{ message::LedState, mgmt::ManagementInterface, MAX_PACKET_SIZE, @@ -129,7 +130,8 @@ struct ServerImpl { consecutive_nacks: [u8; NUM_PORTS as usize], /// Handle to write thermal models and presence to the `thermal` task - thermal_api: Option, + #[cfg(feature = "thermal-control")] + thermal_api: Thermal, /// Handle to write temperatures to the `sensors` task sensor_api: Sensor, @@ -137,13 +139,13 @@ struct ServerImpl { /// Thermal models are populated by the host thermal_models: [Option; NUM_PORTS as usize], } - #[derive(Copy, Clone)] struct ThermalModel { /// What kind of transceiver is this? interface: ManagementInterface, /// What are its thermal properties, e.g. critical temperature? + #[allow(dead_code)] model: ThermalProperties, } @@ -380,9 +382,10 @@ impl ServerImpl { } } } else if !operational && self.thermal_models[i].is_some() { - if let Some(thermal_api) = &self.thermal_api { + #[cfg(feature = "thermal-control")] + { // This transceiver went away; remove it from the thermal loop - if let Err(e) = thermal_api.remove_dynamic_input(i) { + if let Err(e) = self.thermal_api.remove_dynamic_input(i) { ringbuf_entry!(Trace::ThermalError(i, e)); } } @@ -412,13 +415,14 @@ impl ServerImpl { None => continue, }; - if let Some(thermal_api) = &self.thermal_api { + #[cfg(feature = "thermal-control")] + { // *Always* post the thermal model over to the thermal task, so // that the thermal task still has it in case of restart. This // will return a `NotInAutoMode` error if the thermal loop is in // manual mode; this is harmless and will be ignored (instead of // cluttering up the logs). - match thermal_api.update_dynamic_input(i, m.model) { + match self.thermal_api.update_dynamic_input(i, m.model) { Ok(()) | Err(ThermalError::NotInAutoMode) => (), Err(e) => ringbuf_entry!(Trace::ThermalError(i, e)), } @@ -626,9 +630,7 @@ fn main() -> ! { }; #[cfg(feature = "thermal-control")] - let thermal_api = Some(Thermal::from(THERMAL.get_task_id())); - #[cfg(not(feature = "thermal-control"))] - let thermal_api = None; + let thermal_api = Thermal::from(THERMAL.get_task_id()); let mut server = ServerImpl { transceivers, @@ -643,6 +645,7 @@ fn main() -> ! { system_led_state: LedState::Off, disabled: LogicalPortMask(0), consecutive_nacks: [0; NUM_PORTS as usize], + #[cfg(feature = "thermal-control")] thermal_api, sensor_api, thermal_models: [None; NUM_PORTS as usize], diff --git a/task/net/src/bsp/medusa_a.rs b/task/net/src/bsp/medusa_a.rs index d7e7e38cd7..457da2a9dd 100644 --- a/task/net/src/bsp/medusa_a.rs +++ b/task/net/src/bsp/medusa_a.rs @@ -2,10 +2,7 @@ // 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/. -//! BSP for the Sidecar rev B -//! -//! Right now, this is identical to the rev A BSP, but that may change in the -//! future, so they're kept separate. +//! BSP for the Medusa model A #[cfg(not(all(feature = "ksz8463", feature = "mgmt")))] compile_error!("this BSP requires the ksz8463 and mgmt features"); diff --git a/task/thermal/Cargo.toml b/task/thermal/Cargo.toml index 618c53630a..cfdd05bdf9 100644 --- a/task/thermal/Cargo.toml +++ b/task/thermal/Cargo.toml @@ -35,9 +35,9 @@ build-i2c = { path = "../../build/i2c" } build-util = { path = "../../build/util" } [features] -gimlet = ["dep:drv-gimlet-seq-api", "h753"] -sidecar = ["dep:drv-sidecar-seq-api", "dep:drv-transceivers-api", "h753"] -medusa = ["h753", "dep:drv-transceivers-api"] +gimlet = ["drv-gimlet-seq-api", "h753"] +sidecar = ["drv-sidecar-seq-api", "drv-transceivers-api", "h753"] +medusa = ["h753", "drv-transceivers-api"] h743 = ["build-i2c/h743"] h753 = ["build-i2c/h753"] h7b3 = ["build-i2c/h7b3"] diff --git a/task/thermal/src/bsp/medusa_a.rs b/task/thermal/src/bsp/medusa_a.rs index 7532b9fdd7..711017c81d 100644 --- a/task/thermal/src/bsp/medusa_a.rs +++ b/task/thermal/src/bsp/medusa_a.rs @@ -36,8 +36,7 @@ pub const USE_CONTROLLER: bool = false; bitflags::bitflags! { #[derive(Copy, Clone, Debug, Eq, PartialEq)] - pub struct PowerBitmask: u32 { - } + pub struct PowerBitmask: u32 {} } #[derive(Copy, Clone, Debug, Eq, PartialEq)]