From aed2be66b2cf5ce08c26a5ddb207aeab269e6933 Mon Sep 17 00:00:00 2001 From: ETWen Date: Wed, 21 Oct 2026 09:01:55 +0800 Subject: [PATCH] feat(retimer): Add tools to turn off TX on unused lanes retimer_lanes.sh turns TX off / on, or shows the state, for a lane map of one Agera3 retimer on both the sys and the line side, by TX squelch (default) or lane power-down. It refuses 'off' on lanes with RX PMD lock unless --force is given, and reads the state back to print PASS or FAIL. unused_lanes.sh applies that to a built-in table of 14 retimer rows and one direct-TH6 row (TSC31) on each switch board; TH6 rows go through 'port ... enable=' in the syncd container. --asic, --th6, --only and --force select what it touches; it ends with ok / refused / failed counts. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_0131orciRKrejchsuDk6UpXk --- .../Blanton_Script/retimer_lanes.sh | 98 +++++++++++++++++ .../Blanton_Script/unused_lanes.sh | 103 ++++++++++++++++++ 2 files changed, 201 insertions(+) create mode 100644 src/Script_ABC_Blanton/Blanton_Script/retimer_lanes.sh create mode 100644 src/Script_ABC_Blanton/Blanton_Script/unused_lanes.sh diff --git a/src/Script_ABC_Blanton/Blanton_Script/retimer_lanes.sh b/src/Script_ABC_Blanton/Blanton_Script/retimer_lanes.sh new file mode 100644 index 0000000..8c7e0ff --- /dev/null +++ b/src/Script_ABC_Blanton/Blanton_Script/retimer_lanes.sh @@ -0,0 +1,98 @@ +#!/bin/bash +# Turn TX off/on, or show state, for lanes of one Agera3 retimer on the sys and line side. +usage() { + cat < [--method squelch|power] [--force] + squelch TX squelch on sys and line side; lanes stay powered (works with the installed agera3) + power TX+RX lane power-down, the call SONiC makes on admin down; needs an agera3 with 'power' + --force allow 'off' on lanes that receive a signal +USAGE + exit 1 +} +[ $# -ge 4 ] || usage +ASIC=$1; ADDR=$2; MAP=$3; ACT=$4; shift 4 +METHOD=squelch; FORCE=0 +while [ $# -gt 0 ]; do + case $1 in + --method) METHOD=$2; shift 2 ;; + --force) FORCE=1; shift ;; + *) usage ;; + esac +done +case $ASIC in 0|1) ;; *) usage ;; esac +case $ACT in off|on|status) ;; *) usage ;; esac +case $METHOD in squelch|power) ;; *) usage ;; esac +[[ $ADDR =~ ^0x[0-9a-fA-F]+$ && $MAP =~ ^0x[0-9a-fA-F]{1,2}$ ]] || usage +MAPV=$((MAP)) +[ "$MAPV" -ge 1 ] || usage + +ag() { sudo timeout 30 agera3 "$ASIC" "$ADDR" --no-cleanup "$@" &1; } + +if [ "$METHOD" = power ] && ! sudo agera3 -h 2>&1 | grep -q -E '^\s+power '; then + echo "agera3 on this system has no 'power' command; use --method squelch" + exit 4 +fi + +lock_mask() { + ag rx-pmd-lock get side "$1" lane_map=0xff | awk '$2=="|" && $3=="LOCKED" {m += 2^$1} END {printf "%d", m}' +} + +lane_tx() { + local lm + lm=$(printf '0x%02x' $((1 << $2))) + if [ "$METHOD" = squelch ]; then + ag squelch get tx "$1" lane_map=$lm | sed -n 's/.*squelch is \([A-Z]*\).*/\1/p' | awk '{print ($1 == "CLEAR") ? "on" : "off"}' + else + ag power get "$1" lane_map=$lm | awk -v l="$2" '$1 == l && $2 == "|" {print $3}' + fi +} + +txoff_mask() { + local m=0 l s + for l in 0 1 2 3 4 5 6 7; do + [ $((MAPV >> l & 1)) = 1 ] || continue + s=$(lane_tx "$1" $l) + case $s in + off) m=$((m | 1 << l)) ;; + on) ;; + *) echo "unknown"; return ;; + esac + done + printf '0x%02x' $m +} + +report() { + local side + for side in sys line; do + printf 'asic%s %-6s %-4s lane_map=0x%02x rx_lock=0x%02x tx_off=%s (%s)\n' \ + "$ASIC" "$ADDR" "$side" "$MAPV" "$(lock_mask $side)" "$(txoff_mask $side)" "$METHOD" + done +} + +[ "$ACT" = status ] && { report; exit 0; } + +if [ "$ACT" = off ] && [ $FORCE = 0 ]; then + live=$(( ($(lock_mask sys) | $(lock_mask line)) & MAPV )) + if [ $live != 0 ]; then + printf 'REFUSED: asic%s %s lanes 0x%02x receive a signal; use --force to override\n' "$ASIC" "$ADDR" $live + exit 2 + fi +fi + +for side in sys line; do + if [ "$METHOD" = squelch ]; then + ag squelch set tx $side lane_map=$MAP $([ $ACT = off ] && echo enable || echo disable) + else + ag power set $side lane_map=$MAP $ACT + fi | grep -E 'FAIL|Error' +done + +want=0x00; [ "$ACT" = off ] && want=$(printf '0x%02x' $MAPV) +rc=0 +for side in sys line; do + got=$(txoff_mask $side) + printf 'asic%s %-6s %-4s lane_map=0x%02x tx_off=%s expected=%s (%s)\n' "$ASIC" "$ADDR" "$side" "$MAPV" "$got" "$want" "$METHOD" + [ "$got" = "$want" ] || rc=3 +done +[ $rc = 0 ] && echo "PASS" || echo "FAIL" +exit $rc diff --git a/src/Script_ABC_Blanton/Blanton_Script/unused_lanes.sh b/src/Script_ABC_Blanton/Blanton_Script/unused_lanes.sh new file mode 100644 index 0000000..1d799ba --- /dev/null +++ b/src/Script_ABC_Blanton/Blanton_Script/unused_lanes.sh @@ -0,0 +1,103 @@ +#!/bin/bash +# Apply off/on/status to the unused retimer lanes and TH6 ports listed below, on each ASIC. + +# Same rows apply to both switch boards. lanes: retimer lane_map, sys and line side. +# tsc: TH6 TSC as in HW docs and the "#TSCn" labels in th6-helios.config.yml. +# th6_ports: BCM shell port name or range; used by th6 rows, and by retimer rows with --th6. +UNUSED=$(cat <<'TABLE' +# kind target lanes refdes tsc th6_ports note +retimer 0x102 0xff U22 23 cd80-cd83 no used ports +retimer 0x142 0xff U32 38 cd148-cd151 no used ports +retimer 0x21 0xff U39 7 cd16-cd19 no used ports +retimer 0x240 0xff U67 54 cd212-cd215 no used ports +retimer 0x264 0xff U73 62 cd244-cd247 no used ports +retimer 0x48 0xff U82 15 cd48-cd51 no used ports +retimer 0x202 0xff U92 46 cd180-cd183 no used ports +retimer 0x122 0x30 U18 27 cd111 lanes 4,5 unused +retimer 0x248 0x30 U63 58 cd235 lanes 4,5 unused +retimer 0x2 0x30 U70 3 cd15 lanes 4,5 unused +retimer 0x40 0x30 U75 11 cd47 lanes 4,5 unused +retimer 0x162 0x30 U80 42 cd171 lanes 4,5 unused +retimer 0x222 0x30 U87 50 cd203 lanes 4,5 unused +retimer 0x62 0x30 U96 19 cd79 lanes 4,5 unused +th6 TSC31 - - 31 cd112-cd115 direct TH6 TSC, no retimer +# th6 TSC34 - - 34 cd139 direct TH6 TSC34 lanes 6,7; down and unused in full rack, not in HW list +TABLE +) + +DIR=$(cd "$(dirname "$0")" && pwd) +usage() { + cat < [--asic 0|1|all] [--method squelch|power] [--th6] [--only ] [--force] + retimer rows: retimer lanes on sys and line side, via retimer_lanes.sh + th6 rows : TH6 ports in the th6_ports column, via the BCM shell + --th6 : also act on the th6_ports of retimer rows (TH6 ports feeding those retimer lanes) + --only : act on one row, matched by its target column (e.g. 0x102 or TSC31) +USAGE + exit 1 +} +[ $# -ge 1 ] || usage +ACT=$1; shift +ASICS="0 1"; METHOD=squelch; TH6=0; ONLY=""; FORCE="" +while [ $# -gt 0 ]; do + case $1 in + --asic) case $2 in all) ASICS="0 1" ;; 0|1) ASICS=$2 ;; *) usage ;; esac; shift 2 ;; + --method) METHOD=$2; shift 2 ;; + --th6) TH6=1; shift ;; + --only) ONLY=$2; shift 2 ;; + --force) FORCE=--force; shift ;; + *) usage ;; + esac +done +case $ACT in off|on|status) ;; *) usage ;; esac +[ -x "$DIR/retimer_lanes.sh" ] || { echo "retimer_lanes.sh not found next to $0"; exit 1; } + +bcm() { docker exec "syncd$1" bcmcmd -t 60 "$2" &1 | tr -d '\r'; } +ps_state() { bcm "$1" "ps $2" | sed -E 's/\( +/(/' | awk '$1 ~ /^cd[0-9]+\(/ {print $1, $2}'; } + +th6_ports() { + local a=$1 act=$2 ports=$3 st + st=$(ps_state "$a" "$ports") + [ -n "$st" ] || { echo "asic$a th6 $ports: no such ports"; return 3; } + if [ "$act" = status ]; then + echo "$st" | sed "s/^/asic$a th6 /" + return 0 + fi + if [ "$act" = off ] && [ -z "$FORCE" ] && echo "$st" | awk '$2 == "up"' | grep -q .; then + echo "REFUSED: asic$a th6 $ports has link up: $(echo "$st" | awk '$2 == "up" {printf "%s ", $1}')" + return 2 + fi + bcm "$a" "port $ports enable=$([ "$act" = off ] && echo false || echo true)" >/dev/null + sleep 1 + st=$(ps_state "$a" "$ports") + echo "$st" | sed "s/^/asic$a th6 /" + if [ "$act" = off ]; then + echo "$st" | awk '$2 != "!ena"' | grep -q . && return 3 + else + echo "$st" | awk '$2 == "!ena"' | grep -q . && return 3 + fi + return 0 +} + +mapfile -t ROWS < <(grep -v -E '^[[:space:]]*(#|$)' <<<"$UNUSED") +ok=0; refused=0; failed=0 +for a in $ASICS; do + for row in "${ROWS[@]}"; do + read -r kind target lanes refdes tsc ports note <<<"$row" + [ -n "$ONLY" ] && [ "$target" != "$ONLY" ] && continue + echo "== asic$a $kind $target refdes=$refdes TSC$tsc th6=$ports: $note" + rc=0 + case $kind in + retimer) + "$DIR/retimer_lanes.sh" "$a" "$target" "$lanes" "$ACT" --method "$METHOD" $FORCE; rc=$? + if [ $TH6 = 1 ] && [ "$ports" != "-" ] && [ $rc != 2 ]; then + th6_ports "$a" "$ACT" "$ports" || rc=$? + fi ;; + th6) th6_ports "$a" "$ACT" "$ports"; rc=$? ;; + *) echo "unknown kind '$kind'"; rc=1 ;; + esac + case $rc in 0) ok=$((ok + 1)) ;; 2) refused=$((refused + 1)) ;; *) failed=$((failed + 1)) ;; esac + done +done +echo "== $ACT done: ok=$ok refused=$refused failed=$failed" +[ $failed = 0 ]