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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0131orciRKrejchsuDk6UpXk
This commit is contained in:
@@ -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 <<USAGE
|
||||||
|
Usage: $0 <asic 0|1> <retimer-addr> <lane_map> <off|on|status> [--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 "$@" </dev/null 2>&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
|
||||||
@@ -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 <<USAGE
|
||||||
|
Usage: $0 <off|on|status> [--asic 0|1|all] [--method squelch|power] [--th6] [--only <target>] [--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" </dev/null 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 ]
|
||||||
Reference in New Issue
Block a user