From fd35857d80007f661f582caf09ab0a8c0563c00b Mon Sep 17 00:00:00 2001 From: ETWen Date: Tue, 18 Aug 2026 10:38:55 +0800 Subject: [PATCH] refactor(margin): Drop duplicated cb_i2c/pcimem copies margin.sh v2.6.0 resolves the SWB backend via _swb_find_backend, which searches the parent Blanton_Script/ dir, so the copies bundled inside LTC2980_Margin_Script/ are dead weight and would silently drift from the shared originals. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_014RetWKFZFG1ZHcitQAyhwM --- .../LTC2980_Margin_Script/blanton_cb_i2c.sh | 444 ------------------ .../blanton_fpga_pcimem.sh | 270 ----------- 2 files changed, 714 deletions(-) delete mode 100644 src/Script_ABC_Blanton/Blanton_Script/LTC2980_Margin_Script/blanton_cb_i2c.sh delete mode 100644 src/Script_ABC_Blanton/Blanton_Script/LTC2980_Margin_Script/blanton_fpga_pcimem.sh diff --git a/src/Script_ABC_Blanton/Blanton_Script/LTC2980_Margin_Script/blanton_cb_i2c.sh b/src/Script_ABC_Blanton/Blanton_Script/LTC2980_Margin_Script/blanton_cb_i2c.sh deleted file mode 100644 index 296a9b5..0000000 --- a/src/Script_ABC_Blanton/Blanton_Script/LTC2980_Margin_Script/blanton_cb_i2c.sh +++ /dev/null @@ -1,444 +0,0 @@ -#!/usr/bin/env bash -# ============================================================================= -# blanton_cb_i2c.sh -- CB FPGA (F3) I2C / PMBus over the OpenCores I2C master -# ============================================================================= -# Version History: -# V0.1.0 2026-06-16 CB sibling of blanton_icb_i2c.sh. Same OpenCores I2C -# handshake, but the I2C block lives in CB FPGA *Function 3* -# register space (base 0x300, stride 0x20, Ch0..Ch17), and -# is reached via cb_fpga 3 (direct PCIe BAR -> .3/resource0). -# ============================================================================= -# -# IMPORTANT (vs the hand-written cb_fpga.md pcimem commands): -# * The CB I2C channels are on PCI **Function 3**, so the sysfs file is -# .../0000:02:00.3/resource0 (NOT .0). cb_fpga 3 uses FUNCT3_RES for this. -# * This tool uses **Repeated START** for reads (write reg pointer with WR only, -# no STOP, then Sr + slave|R) so it works for the SMBus/PMBus parts (VRM, -# EFUSE) as well as plain register parts. STOP+START only works for the latter. -# * Slave addresses are 7-bit here. The spec lists 8-bit pairs (e.g. MAX31790 -# "0x40/0x41" -> 7-bit 0x20; TCA9546 "0xE8/0xE9" -> 7-bit 0x74). Divide by 2. -# -# CB FPGA F3 I2C channels (base 0x300, stride 0x20) -- 7-bit addrs in (...): -# Ch0 0x300 SWB_0: TCA9546 mux (0x74), M0.C0 temp sensor, ... -# Ch1 0x320 SWB_1: TCA9546 mux (0x74), ... -# Ch2 0x340 SWB_0: SWB_FPGA (0x40), IO Exp PCA9555 (0x24), ... -# Ch3 0x360 SWB_1: SWB_FPGA (0x40), PCA9555 (0x24), ... -# Ch4 0x380 SWB_0: M1 TCA9546 mux (0x74), Clk Gen SI5..., ... -# Ch5 0x3A0 SWB_1: M1 TCA9546 mux (0x74), ... -# Ch6 0x3C0 SWB_0: VRM M29816 (0x20), VRM M2985B (0x21), ... [PMBus] -# Ch7 0x3E0 SWB_1: VRM M29816 (0x20), VRM M2985B (0x21), ... [PMBus] -# Ch8 0x400 SWB_0: VRM M2985B (0x25), M2985B (0x26), ... [PMBus] -# Ch9 0x420 SWB_1: VRM M2985B (0x25), M2985B (0x26), ... [PMBus] -# Ch10 0x440 M0 TCA9543 mux (0x70), M0.C0 Clk Buf 9DBL0452, ... -# Ch11 0x460 Fan Ctrl MAX31790 (0x20) -# Ch12 0x480 ADC leakage ADC128D818 (0x1F) -# Ch13 0x4A0 NFC SM-MFAD4-C02 (0x28) -# Ch14 0x4C0 M1 TCA9543 mux (0x70), M1.C0 QSFP28.P1 xcvr, ... -# Ch15 0x4E0 Fan1/2 EFUSE STEF48H28 (0x10/0x11), ... [PMBus-ish] -# Ch16 0x500 1G PHY I210 Ethernet ctrlr (0x49) -# Ch17 0x520 Chassis EEPROM (0x28..0x29), Diag EEPROM (0x2B), ... -# (slave 7-bit addrs derived from the 8-bit pairs in the register map; verify -# against the device datasheet / your board before driving writes.) -# -# Per-channel register layout (offset from channel base): -# +0x00 I2C_PRSCL_LO +0x04 I2C_PRSCL_HI +0x08 I2C_CTRL (bit7 MOD_EN) -# +0x0C I2C_TX(W)/I2C_RX(R) +0x10 I2C_CMD(W)/I2C_STAT(R) -# +0x14 I2C_MUX_SEL +0x18 I2C_RST (0xD) +0x1C SEM -# CMD : STA=0x80 STO=0x40 RD=0x20 WR=0x10 ACK=0x08(1=NACK) IACK=0x01 -# STAT: RX_ACK=0x80(1=NACK) BUSY=0x40 ARB_LOST=0x20 TIP=0x02 INT_FLAG=0x01 -# Prescale = (in_clk/(5*scl))-1 @75MHz: 0x88=100kHz, 0x24=400kHz -# -# Usage: -# source blanton_fpga_pcimem.sh # provides cb_fpga / fpga_debug -# source blanton_cb_i2c.sh -# cb_i2c_init 11 # Ch11 (Fan Ctrl) @100kHz -# cb_i2c_scan 11 -# cb_i2c_read 11 0x20 0x01 1 # MAX31790 (7-bit 0x20) reg 0x01 -# cb_pmbus_read 6 0x20 0x8B 2 # Ch6 VRM READ_VOUT -# ============================================================================= - -# --- Pull in the register-access backend (cb_fpga) if not present ----------- -if ! declare -F cb_fpga >/dev/null 2>&1; then - _CBI2C_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" 2>/dev/null && pwd)" - if [ -n "$_CBI2C_DIR" ] && [ -f "$_CBI2C_DIR/blanton_fpga_pcimem.sh" ]; then - # shellcheck source=/dev/null - source "$_CBI2C_DIR/blanton_fpga_pcimem.sh" - else - echo -e "[\033[33mWARN\033[0m] blanton_cb_i2c.sh: cb_fpga() not found." >&2 - echo " source blanton_fpga_pcimem.sh first (provides cb_fpga)." >&2 - fi -fi - -# === CB I2C geometry ========================================================= -CBI2C_FN=3 # CB PCI Function hosting the I2C block -CBI2C_BASE=0x300 # channel 0 base offset (relative to F3 resource0) -CBI2C_CH_STRIDE=0x20 -CBI2C_CH_MAX=17 - -# Per-channel register offsets -_CBI2C_PRSCL_LO=0x00 -_CBI2C_PRSCL_HI=0x04 -_CBI2C_CTRL=0x08 -_CBI2C_TX=0x0C -_CBI2C_RX=0x0C -_CBI2C_CMD=0x10 -_CBI2C_STAT=0x10 -_CBI2C_MUX_SEL=0x14 -_CBI2C_RST=0x18 -_CBI2C_SEM=0x1C - -# I2C_CTRL_REG / CMD / STAT bits -_CBI2C_EN=0x80 -_CBI2C_STA=0x80 -_CBI2C_STO=0x40 -_CBI2C_RD=0x20 -_CBI2C_WR=0x10 -_CBI2C_NACK=0x08 -_CBI2C_RXACK=0x80 -_CBI2C_TIP=0x02 -_CBI2C_RST_VAL=0xD - -# Defaults -CB_I2C_PRESCALE_LO="${CB_I2C_PRESCALE_LO:-0x88}" # 100 kHz @ 75 MHz -CB_I2C_PRESCALE_HI="${CB_I2C_PRESCALE_HI:-0x00}" -CB_I2C_AUTO_INIT="${CB_I2C_AUTO_INIT:-1}" -CB_I2C_TIP_POLL_MAX="${CB_I2C_TIP_POLL_MAX:-100}" - -# --- External I2C-mux RESET control (verified on COM34, 2026-06-16) ---------- -# The TCA9543 muxes on CH10 (M0) and CH14 (M1) have an FPGA-driven, active-low -# RESET line. On power-up / FPGA reload the FPGA holds them in reset, so 0x70 -# does NOT ACK on the root bus and all downstream legs are unreachable. -# -# The control is a GLOBAL register in CB FPGA *Function 0*, offset 0x7D4 -# (I2C_MUX_RST_REG) -- NOT the per-channel I2C_MUX_SEL_REG (+0x14) which is a -# device-select and does nothing to the RESET pin. Per bit: 1 = release, -# 0 = hold-in-reset. Observed default 0xDD (bit1=CH10 mux, bit5=CH14 mux held). -# Writing 0xFF releases all external muxes (normal operating state). This value -# is volatile and is lost on the next power cycle / FPGA reload. -CBI2C_MUX_RST_FN="${CBI2C_MUX_RST_FN:-0}" -CBI2C_MUX_RST_OFF="${CBI2C_MUX_RST_OFF:-0x7D4}" - -# Channels whose root bus carries an external mux that hangs off the RESET reg. -# CH10/CH14 = TCA9543; CH0/1/4/5 = TCA9546 (SWB/M1) per the channel map above. -CBI2C_MUX_CHANNELS="${CBI2C_MUX_CHANNELS:-0 1 4 5 10 14}" - -# Auto-release external mux RESET before scanning / mux-writing a mux channel. -CB_I2C_AUTO_MUX_RST="${CB_I2C_AUTO_MUX_RST:-1}" - -# --- Single FPGA register read/write via cb_fpga [data] ---------- -# _cbi2c_reg [data] data present = write, absent = read (echo hex) -_cbi2c_reg() { - local off="$1" data="${2:-}" - if [ -n "$data" ]; then cb_fpga "$CBI2C_FN" "$off" "$data"; else cb_fpga "$CBI2C_FN" "$off"; fi -} - -# Channel base absolute offset -_cbi2c_chan_base() { - local ch="$1" - if (( ch < 0 || ch > CBI2C_CH_MAX )); then - echo "blanton_cb_i2c: ch must be 0..$CBI2C_CH_MAX" >&2; return 1 - fi - printf '0x%X' $(( CBI2C_BASE + ch * CBI2C_CH_STRIDE )) -} - -# Normalize a register read-back to a number string ("0x...." -> 0x....) -_cbi2c_num() { local v="$1"; [[ "$v" =~ (0[xX][0-9a-fA-F]+) ]] && echo "${BASH_REMATCH[1]}" || echo "0"; } - -# Write CMD then poll STAT (same offset) until TIP clears; echo final status hex -_cbi2c_cmd_wait() { - local cmd_off="$1" cmd_val="$2" i status - _cbi2c_reg "$cmd_off" "$cmd_val" >/dev/null - for (( i=0; i&2 - printf '0x%X' "${status:-0}"; return 1 -} - -# Check RX_ACK in status (bit7). 0 = ACK, 1 = NACK -> return 1 -_cbi2c_check_ack() { - local status; status=$(_cbi2c_num "$1") - (( (status & _CBI2C_RXACK) != 0 )) && return 1 || return 0 -} - -# === Public: init / reset / semaphore / mux-sel ============================= - -# cb_i2c_init [prescale_lo] [prescale_hi] -cb_i2c_init() { - [[ $# -lt 1 ]] && { echo "Usage: cb_i2c_init [prescale_lo] [prescale_hi]"; return 1; } - local ch="$1" lo="${2:-$CB_I2C_PRESCALE_LO}" hi="${3:-$CB_I2C_PRESCALE_HI}" base - base=$(_cbi2c_chan_base "$ch") || return 1 - _cbi2c_reg "$(printf '0x%X' $((base + _CBI2C_CTRL)))" 0x00 >/dev/null # disable - _cbi2c_reg "$(printf '0x%X' $((base + _CBI2C_PRSCL_LO)))" "$lo" >/dev/null - _cbi2c_reg "$(printf '0x%X' $((base + _CBI2C_PRSCL_HI)))" "$hi" >/dev/null - _cbi2c_reg "$(printf '0x%X' $((base + _CBI2C_CTRL)))" "$_CBI2C_EN" >/dev/null # enable - return 0 -} - -# cb_i2c_reset -cb_i2c_reset() { - [[ $# -lt 1 ]] && { echo "Usage: cb_i2c_reset "; return 1; } - local ch="$1" base; base=$(_cbi2c_chan_base "$ch") || return 1 - _cbi2c_reg "$(printf '0x%X' $((base + _CBI2C_RST)))" "$_CBI2C_RST_VAL" >/dev/null - echo " [RST] ch=$ch local I2C controller reset (0xD)" -} - -# cb_i2c_sem [status|acquire [val]|release] -cb_i2c_sem() { - [[ $# -lt 1 ]] && { echo "Usage: cb_i2c_sem [status|acquire [val]|release]"; return 1; } - local ch="$1" act="${2:-status}" base off cur - base=$(_cbi2c_chan_base "$ch") || return 1 - off=$(printf '0x%X' $((base + _CBI2C_SEM))) - case "$act" in - status) - cur=$(_cbi2c_num "$(_cbi2c_reg "$off")") - (( (cur & 0xFF) == 0 )) && echo " [SEM] ch=$ch free (0x00)" \ - || printf ' [SEM] ch=%s held (0x%02X)\n' "$ch" $((cur & 0xFF)) ;; - acquire) - local val="${3:-0x01}" - cur=$(_cbi2c_num "$(_cbi2c_reg "$off")") - if (( (cur & 0xFF) != 0 )); then - printf ' [SEM] ch=%s already held (0x%02X) -- not acquired\n' "$ch" $((cur & 0xFF)); return 1 - fi - _cbi2c_reg "$off" "$val" >/dev/null - cur=$(_cbi2c_num "$(_cbi2c_reg "$off")") - (( (cur & 0xFF) != 0 )) && printf ' [SEM] ch=%s acquired (0x%02X)\n' "$ch" $((cur & 0xFF)) \ - || { echo " [SEM] ch=$ch acquire failed"; return 1; } ;; - release) - _cbi2c_reg "$off" 0x00 >/dev/null; echo " [SEM] ch=$ch released (0x00)" ;; - *) echo "Usage: cb_i2c_sem [status|acquire [val]|release]"; return 1 ;; - esac -} - -# cb_i2c_muxsel -- write I2C_MUX_SEL_REG (FPGA drives ext mux pins) -cb_i2c_muxsel() { - [[ $# -lt 2 ]] && { echo "Usage: cb_i2c_muxsel "; return 1; } - local ch="$1" sel="$2" base; base=$(_cbi2c_chan_base "$ch") || return 1 - _cbi2c_reg "$(printf '0x%X' $((base + _CBI2C_MUX_SEL)))" "$sel" >/dev/null - printf ' [MUXSEL] ch=%s DEV_SEL <= 0x%02X\n' "$ch" $((sel & 0xFF)) -} - -# cb_i2c_mux_reset_release -- de-assert ALL external mux RESET lines (F0 0x7D4 <= 0xFF) -# Global, not per-channel. Volatile: lost on power cycle / FPGA reload. -cb_i2c_mux_reset_release() { - cb_fpga "$CBI2C_MUX_RST_FN" "$CBI2C_MUX_RST_OFF" 0xFF >/dev/null - local rb; rb=$(_cbi2c_num "$(cb_fpga "$CBI2C_MUX_RST_FN" "$CBI2C_MUX_RST_OFF")") - printf ' [MUX-RST] I2C_MUX_RST_REG (F%s %s) <= 0xFF (all external muxes released, readback %s)\n' \ - "$CBI2C_MUX_RST_FN" "$CBI2C_MUX_RST_OFF" "$rb" -} - -# _cbi2c_is_mux_channel -- 0 (true) if ch is in CBI2C_MUX_CHANNELS -_cbi2c_is_mux_channel() { - local ch="$1" c - for c in $CBI2C_MUX_CHANNELS; do [[ "$c" == "$ch" ]] && return 0; done - return 1 -} - -# _cbi2c_auto_mux_rst -- release external mux RESET if ch is a mux channel -_cbi2c_auto_mux_rst() { - [[ "$CB_I2C_AUTO_MUX_RST" == "1" ]] || return 0 - _cbi2c_is_mux_channel "$1" || return 0 - cb_i2c_mux_reset_release -} - -# === Core transfers (Repeated START, SMBus/PMBus compatible) ================= - -# _cbi2c_xfer_read echoes space-separated hex bytes -_cbi2c_xfer_read() { - local ch="$1" slave="$2" reg="$3" n="$4" base tx cmd rx status i last cmdv val out="" - base=$(_cbi2c_chan_base "$ch") || return 1 - tx=$(printf '0x%X' $((base + _CBI2C_TX))) - cmd=$(printf '0x%X' $((base + _CBI2C_CMD))) - rx=$(printf '0x%X' $((base + _CBI2C_RX))) - [[ "$CB_I2C_AUTO_INIT" == "1" ]] && cb_i2c_init "$ch" >/dev/null - - _cbi2c_reg "$tx" "$(printf '0x%X' $(( (slave << 1) & 0xFF )))" >/dev/null - status=$(_cbi2c_cmd_wait "$cmd" "$(printf '0x%X' $((_CBI2C_STA | _CBI2C_WR)))") - _cbi2c_check_ack "$status" || { echo "ERR: NACK on slave addr (W) 0x$(printf %02X "$slave")" >&2; return 1; } - - _cbi2c_reg "$tx" "$(printf '0x%X' $((reg & 0xFF)))" >/dev/null - status=$(_cbi2c_cmd_wait "$cmd" "$(printf '0x%X' $((_CBI2C_WR)))") # WR only, no STOP - _cbi2c_check_ack "$status" || { echo "ERR: NACK on reg 0x$(printf %02X "$reg")" >&2; return 1; } - - _cbi2c_reg "$tx" "$(printf '0x%X' $(( ((slave << 1) | 1) & 0xFF )))" >/dev/null - status=$(_cbi2c_cmd_wait "$cmd" "$(printf '0x%X' $((_CBI2C_STA | _CBI2C_WR)))") # Repeated START - _cbi2c_check_ack "$status" || { echo "ERR: NACK on slave addr (R) 0x$(printf %02X "$slave")" >&2; return 1; } - - for (( i=0; i/dev/null - val=$(_cbi2c_num "$(_cbi2c_reg "$rx")") - out+="$(printf '0x%02X ' $((val & 0xFF)))" - done - echo "${out% }" -} - -# _cbi2c_xfer_write [byte...] -_cbi2c_xfer_write() { - local ch="$1" slave="$2" reg="$3"; shift 3 - local base tx cmd status b last cmdv n=$# idx=0 - [[ $n -lt 1 ]] && { echo "ERR: no data bytes" >&2; return 1; } - base=$(_cbi2c_chan_base "$ch") || return 1 - tx=$(printf '0x%X' $((base + _CBI2C_TX))) - cmd=$(printf '0x%X' $((base + _CBI2C_CMD))) - [[ "$CB_I2C_AUTO_INIT" == "1" ]] && cb_i2c_init "$ch" >/dev/null - - _cbi2c_reg "$tx" "$(printf '0x%X' $(( (slave << 1) & 0xFF )))" >/dev/null - status=$(_cbi2c_cmd_wait "$cmd" "$(printf '0x%X' $((_CBI2C_STA | _CBI2C_WR)))") - _cbi2c_check_ack "$status" || { echo "ERR: NACK on slave addr (W) 0x$(printf %02X "$slave")" >&2; return 1; } - - _cbi2c_reg "$tx" "$(printf '0x%X' $((reg & 0xFF)))" >/dev/null - status=$(_cbi2c_cmd_wait "$cmd" "$(printf '0x%X' $((_CBI2C_WR)))") - _cbi2c_check_ack "$status" || { echo "ERR: NACK on reg 0x$(printf %02X "$reg")" >&2; return 1; } - - for b in "$@"; do - idx=$((idx+1)); last=$(( idx == n )) - _cbi2c_reg "$tx" "$(printf '0x%X' $(( $((b)) & 0xFF )))" >/dev/null - if (( last )); then cmdv=$(( _CBI2C_WR | _CBI2C_STO )); else cmdv=$(( _CBI2C_WR )); fi - status=$(_cbi2c_cmd_wait "$cmd" "$(printf '0x%X' "$cmdv")") - _cbi2c_check_ack "$status" || { echo "ERR: NACK on data byte #$idx" >&2; return 1; } - done - return 0 -} - -# _cbi2c_xfer_cmd_only -- START+W, cmd byte, STOP (no data) -_cbi2c_xfer_cmd_only() { - local ch="$1" slave="$2" cc="$3" base tx cmd status - base=$(_cbi2c_chan_base "$ch") || return 1 - tx=$(printf '0x%X' $((base + _CBI2C_TX))) - cmd=$(printf '0x%X' $((base + _CBI2C_CMD))) - [[ "$CB_I2C_AUTO_INIT" == "1" ]] && cb_i2c_init "$ch" >/dev/null - _cbi2c_reg "$tx" "$(printf '0x%X' $(( (slave << 1) & 0xFF )))" >/dev/null - status=$(_cbi2c_cmd_wait "$cmd" "$(printf '0x%X' $((_CBI2C_STA | _CBI2C_WR)))") - _cbi2c_check_ack "$status" || { echo "ERR: NACK on slave addr (W) 0x$(printf %02X "$slave")" >&2; return 1; } - _cbi2c_reg "$tx" "$(printf '0x%X' $((cc & 0xFF)))" >/dev/null - status=$(_cbi2c_cmd_wait "$cmd" "$(printf '0x%X' $((_CBI2C_WR | _CBI2C_STO)))") - _cbi2c_check_ack "$status" || { echo "ERR: NACK on cmd 0x$(printf %02X "$cc")" >&2; return 1; } - return 0 -} - -# === Public: generic I2C ===================================================== - -# cb_i2c_read [nbytes] -cb_i2c_read() { - [[ $# -lt 3 ]] && { echo "Usage: cb_i2c_read [nbytes]"; return 1; } - local ch="$1" slave=$(( $2 )) reg=$(( $3 )) n="${4:-1}" bytes - bytes=$(_cbi2c_xfer_read "$ch" "$slave" "$reg" "$n") || return 1 - printf ' [I2C-R] ch=%s slave=0x%02X reg=0x%02X => [%s]\n' "$ch" "$slave" "$reg" "$bytes" -} - -# cb_i2c_write [byte...] -cb_i2c_write() { - [[ $# -lt 4 ]] && { echo "Usage: cb_i2c_write [byte...]"; return 1; } - local ch="$1" slave=$(( $2 )) reg=$(( $3 )); shift 3 - _cbi2c_xfer_write "$ch" "$slave" "$reg" "$@" || return 1 - printf ' [I2C-W] ch=%s slave=0x%02X reg=0x%02X <= [%s]\n' "$ch" "$slave" "$reg" "$*" -} - -# cb_i2c_muxwrite -- raw 1-byte write, no reg (TCA954x) -cb_i2c_muxwrite() { - [[ $# -lt 3 ]] && { echo "Usage: cb_i2c_muxwrite "; return 1; } - local ch="$1" slave=$(( $2 )) val=$(( $3 )) base tx cmd status - base=$(_cbi2c_chan_base "$ch") || return 1 - tx=$(printf '0x%X' $((base + _CBI2C_TX))) - cmd=$(printf '0x%X' $((base + _CBI2C_CMD))) - [[ "$CB_I2C_AUTO_INIT" == "1" ]] && cb_i2c_init "$ch" >/dev/null - _cbi2c_auto_mux_rst "$ch" - _cbi2c_reg "$tx" "$(printf '0x%X' $(( (slave << 1) & 0xFF )))" >/dev/null - status=$(_cbi2c_cmd_wait "$cmd" "$(printf '0x%X' $((_CBI2C_STA | _CBI2C_WR)))") - _cbi2c_check_ack "$status" || { echo "ERR: NACK on mux addr 0x$(printf %02X "$slave")" >&2; return 1; } - _cbi2c_reg "$tx" "$(printf '0x%X' $((val & 0xFF)))" >/dev/null - status=$(_cbi2c_cmd_wait "$cmd" "$(printf '0x%X' $((_CBI2C_WR | _CBI2C_STO)))") - _cbi2c_check_ack "$status" || { echo "ERR: NACK on mux ctrl byte" >&2; return 1; } - printf ' [MUX-W] ch=%s mux=0x%02X <= 0x%02X\n' "$ch" "$slave" "$val" -} - -# cb_i2c_scan -- probe 0x08..0x77, report slaves that ACK -cb_i2c_scan() { - [[ $# -lt 1 ]] && { echo "Usage: cb_i2c_scan "; return 1; } - local ch="$1" base tx cmd a status found="" - base=$(_cbi2c_chan_base "$ch") || return 1 - tx=$(printf '0x%X' $((base + _CBI2C_TX))) - cmd=$(printf '0x%X' $((base + _CBI2C_CMD))) - cb_i2c_init "$ch" >/dev/null - _cbi2c_auto_mux_rst "$ch" - for (( a=0x08; a<=0x77; a++ )); do - _cbi2c_reg "$tx" "$(printf '0x%X' $(( (a << 1) & 0xFF )))" >/dev/null - status=$(_cbi2c_cmd_wait "$cmd" "$(printf '0x%X' $((_CBI2C_STA | _CBI2C_WR | _CBI2C_STO)))") - _cbi2c_check_ack "$status" && found+="$(printf '0x%02X ' "$a")" - done - [[ -n "$found" ]] && echo " [SCAN] ch=$ch found: ${found% }" || echo " [SCAN] ch=$ch no devices" -} - -# === Public: PMBus (== SMBus; command code + Repeated START read) ============ - -# cb_pmbus_read [nbytes] (default 2 bytes, LSB first) -cb_pmbus_read() { - [[ $# -lt 3 ]] && { echo "Usage: cb_pmbus_read [nbytes]"; return 1; } - local ch="$1" slave=$(( $2 )) cc=$(( $3 )) n="${4:-2}" bytes - bytes=$(_cbi2c_xfer_read "$ch" "$slave" "$cc" "$n") || return 1 - if [[ "$n" == "2" ]]; then - local b0 b1 word; b0=$(( $(echo "$bytes" | awk '{print $1}') )); b1=$(( $(echo "$bytes" | awk '{print $2}') )) - word=$(( b0 | (b1 << 8) )) - printf ' [PMB-R] ch=%s slave=0x%02X cmd=0x%02X => 0x%04X raw=[%s] linear16=%s\n' \ - "$ch" "$slave" "$cc" "$word" "$bytes" "$(cb_pmbus_linear16 "$word")" - else - printf ' [PMB-R] ch=%s slave=0x%02X cmd=0x%02X => [%s]\n' "$ch" "$slave" "$cc" "$bytes" - fi -} - -# cb_pmbus_write [data byte...] (no byte = send-byte) -cb_pmbus_write() { - [[ $# -lt 3 ]] && { echo "Usage: cb_pmbus_write [data byte...]"; return 1; } - local ch="$1" slave=$(( $2 )) cc=$(( $3 )); shift 3 - if [[ $# -eq 0 ]]; then - _cbi2c_xfer_cmd_only "$ch" "$slave" "$cc" || return 1 - printf ' [PMB-W] ch=%s slave=0x%02X cmd=0x%02X <= (send-byte)\n' "$ch" "$slave" "$cc"; return 0 - fi - _cbi2c_xfer_write "$ch" "$slave" "$cc" "$@" || return 1 - printf ' [PMB-W] ch=%s slave=0x%02X cmd=0x%02X <= [%s]\n' "$ch" "$slave" "$cc" "$*" -} - -# cb_pmbus_linear16 -cb_pmbus_linear16() { - local word=$(( $1 & 0xFFFF )) - awk -v w="$word" 'BEGIN{ - e=int(w/2048)%32; if(e>15)e-=32; - m=w%2048; if(m>1023)m-=2048; - printf "%.4f", m * (2.0 ^ e); - }' -} - -# === Help ==================================================================== -blanton_cb_i2c_help() { - echo -e "\033[1mblanton_cb_i2c.sh - CB FPGA (F3) I2C / PMBus over OpenCores I2C master\033[0m" - echo "" - echo -e " Backend: \033[36mcb_fpga $CBI2C_FN\033[0m (direct PCIe BAR -> .3/resource0), base 0x300, Ch0..$CBI2C_CH_MAX" - echo "" - echo -e "\033[1mSetup\033[0m" - echo -e " \033[33mcb_i2c_init\033[0m [pre_lo] [pre_hi] prescale + enable (def 0x88/0x00 = 100kHz)" - echo -e " \033[33mcb_i2c_reset\033[0m LCL_RST=0xD" - echo -e " \033[33mcb_i2c_sem\033[0m [status|acquire [v]|release]" - echo -e " \033[33mcb_i2c_muxsel\033[0m write I2C_MUX_SEL_REG" - echo -e " \033[33mcb_i2c_mux_reset_release\033[0m de-assert ALL ext mux RESET (F0 0x7D4<=0xFF)" - echo "" - echo -e "\033[1mGeneric I2C\033[0m" - echo -e " \033[33mcb_i2c_scan\033[0m " - echo -e " \033[33mcb_i2c_read\033[0m [nbytes]" - echo -e " \033[33mcb_i2c_write\033[0m [byte...]" - echo -e " \033[33mcb_i2c_muxwrite\033[0m raw 1-byte (TCA954x)" - echo "" - echo -e "\033[1mPMBus / SMBus\033[0m" - echo -e " \033[33mcb_pmbus_read\033[0m [nbytes] (def 2, LE word + linear16)" - echo -e " \033[33mcb_pmbus_write\033[0m [byte...]" - echo -e " \033[33mcb_pmbus_linear16\033[0m " - echo "" - echo -e " Slave = 7-bit (tool shifts). Spec lists 8-bit pairs -> divide by 2." - echo -e " \033[33mfpga_debug on\033[0m to see every underlying pcimem command." -} - -blanton_cb_i2c_help diff --git a/src/Script_ABC_Blanton/Blanton_Script/LTC2980_Margin_Script/blanton_fpga_pcimem.sh b/src/Script_ABC_Blanton/Blanton_Script/LTC2980_Margin_Script/blanton_fpga_pcimem.sh deleted file mode 100644 index ca21689..0000000 --- a/src/Script_ABC_Blanton/Blanton_Script/LTC2980_Margin_Script/blanton_fpga_pcimem.sh +++ /dev/null @@ -1,270 +0,0 @@ -#!/usr/bin/env bash -# ============================================================================= -# blanton_fpga_pcimem.sh -- pcimem backend, sysfs resource file + relative offset -# ============================================================================= -# Version History: -# V1.3.0 2026-06-12 pcimem variant (SONiC hardware). memtool variant: blanton_fpga_memtool.sh -# Version number kept in sync with blanton_fpga_memtool.sh -# V1.4.0 2026-06-15 Fix: VSPI window bases pointed at the VSPI-Flash block -# (0x340/0x380/0x3C0/0x400, config-flash channel) so register -# reads returned 0; corrected to the VSPI register block -# (PMC 0x640 / ICB 0x680 / SWB0 0x6C0 / SWB1 0x700) -# Add: _vspi_fnwarn guard - warns when PMC/ICB/SWB are called with -# cb_fpga-style " " (a read turning into a write) -# Change: suppress pcimem write stdout noise (mmap/Written lines) -# Docs: comments/help translated to English (ASCII-only for SONiC console) -# V1.4.1 2026-06-23 Fix: BDF in example auto-detect was wrong (02:00 vs 04:00); corrected to match the lspci example. -# ============================================================================= -# -# SONiC hardware has no memtool / devmem, but it has pcimem. pcimem mmaps the PCI -# sysfs resource file directly and takes "resource file + offset relative to BAR -# start", not an absolute physical address. -# -# pcimem { sysfile } { offset } [ type*count [ data ] ] -# sysfile : sysfs file for the pci resource (e.g. .../resource0) -# offset : offset into pci memory region -# type : [b]yte, [h]alfword, [w]ord, [d]ouble-word -# *count : number of items (w*100 dump 100 words) -# data : data to be written -# -# This tool always accesses as word (w, 32-bit). -# -# After sourcing this file the following functions are available: -# cb_fpga [data] -# pmc_fpga [data] -# icb_fpga [data] -# swb0_fpga [data] -# swb1_fpga [data] -# -# data present = write, absent = read. -# Usage: source blanton_fpga_pcimem.sh - -# === CB FPGA PCIe resource file (sysfs) === -# Use lspci -D to find the BDF (domain:bus:dev.fn); resource0 is the sysfs mapping of BAR0. -# e.g. lspci -Dnn | grep -i fpga -> 0000:02:00.0 ... -# memtool variant fills in the BAR value; pcimem variant fills in the resource file -# path instead (offset becomes relative to BAR). -FUNCT0_RES=/sys/bus/pci/devices/0000:04:00.0/resource0 -FUNCT1_RES=/sys/bus/pci/devices/0000:04:00.1/resource0 -FUNCT2_RES=/sys/bus/pci/devices/0000:04:00.2/resource0 -FUNCT3_RES=/sys/bus/pci/devices/0000:04:00.3/resource0 - -# Auto-detect example (uncomment to use; needs the FPGA's BDF prefix): -# _BDF=0000:02:00 -# FUNCT0_RES=/sys/bus/pci/devices/${_BDF}.0/resource0 -# FUNCT1_RES=/sys/bus/pci/devices/${_BDF}.1/resource0 -# FUNCT2_RES=/sys/bus/pci/devices/${_BDF}.2/resource0 -# FUNCT3_RES=/sys/bus/pci/devices/${_BDF}.3/resource0 - -# === VSPI window base offset (relative to CB Function 2 resource0 start) === -# NOTE: CB.F2 has TWO near-identical VSPI register blocks (see spec register map): -# - VSPI-Flash-* @ 0x340/0x380/0x3C0/0x400 -> accesses the remote FPGA's CONFIG FLASH -# - VSPI-* @ 0x640/0x680/0x6C0/0x700 -> accesses the remote FPGA's REGISTERS -# This tool reads/writes remote registers, so it uses the VSPI-* block (0x640+). -# The VSPI-Flash bases are kept below (commented) in case flash access is needed later. -VSPI_PMC_BASE=0x640 -VSPI_ICB_BASE=0x680 -VSPI_SWB0_BASE=0x6C0 -VSPI_SWB1_BASE=0x700 - -# VSPI-Flash window bases (config-flash access, not register access): -# VSPI_FLASH_PMC_BASE=0x340 -# VSPI_FLASH_ICB_BASE=0x380 -# VSPI_FLASH_SWB0_BASE=0x3C0 -# VSPI_FLASH_SWB1_BASE=0x400 - -# === VSPI protocol constants === -_VSPI_CMD_STAT=0x00 -_VSPI_ADDR=0x04 -_VSPI_WR_DATA=0x08 -_VSPI_RD_DATA=0x0C -_VSPI_NEW_CMD=0x01 -_VSPI_READ_W=0x02 -_VSPI_WRITE_W=0x04 -_VSPI_READY_MASK=0x100 -_VSPI_POLL_MAX=200 - -# === pcimem command === -PCIMEM_CMD="${PCIMEM_CMD:-pcimem}" - -# === Debug mode (1=print commands AND execute, 0=execute only) === -DEBUG_MODE=${DEBUG_MODE:-0} - -fpga_debug() { - if [ "${1:-}" = "on" ]; then DEBUG_MODE=1 - elif [ "${1:-}" = "off" ]; then DEBUG_MODE=0 - else [ "$DEBUG_MODE" = "0" ] && DEBUG_MODE=1 || DEBUG_MODE=0; fi - echo -e "[\033[34mINFO\033[0m] DEBUG_MODE=$DEBUG_MODE" -} - -# Wrapper: debug mode prints command (to stderr) AND executes, normal mode executes only. -# On read, returns the parsed hex value (stripping pcimem's mmap noise). -# Usage: _pcimem [data] (data present = write, absent = read) -_pcimem() { - local sysfile="$1" offset="$2" data="${3:-}" - if [[ -n "$data" ]]; then - [ "$DEBUG_MODE" = "1" ] && \ - echo -e "\033[90m[DEBG] $PCIMEM_CMD $sysfile $offset w $data\033[0m" >&2 - # Write output (opened / Target offset / mmap / Written...readback) is never - # parsed -> drop stdout noise; keep stderr so real errors still surface. - $PCIMEM_CMD "$sysfile" "$offset" w "$data" >/dev/null - else - [ "$DEBUG_MODE" = "1" ] && \ - echo -e "\033[90m[DEBG] $PCIMEM_CMD $sysfile $offset w\033[0m" >&2 - # pcimem read prints mmap info + "Value at offset ...: 0xXXXX"; - # take the last 0x... token as the actual read-back value. - $PCIMEM_CMD "$sysfile" "$offset" w | grep -oiE '0x[0-9a-f]+' | tail -1 - fi -} - -# --- Internal VSPI helpers (all hang off CB Function 2's resource0) --- - -_vspi_read() { - local win_off="$1" remote_offset="$2" - local cmd_off add_off rd_off addr_val status i - - cmd_off=$(printf '0x%X' $(( win_off + _VSPI_CMD_STAT ))) - add_off=$(printf '0x%X' $(( win_off + _VSPI_ADDR ))) - rd_off=$(printf '0x%X' $(( win_off + _VSPI_RD_DATA ))) - addr_val=$(printf '0x%08X' $(( (0x0 << 20) | (remote_offset & 0xFFFFF) ))) - - _pcimem "$FUNCT2_RES" "$add_off" "$addr_val" - _pcimem "$FUNCT2_RES" "$cmd_off" $(printf '0x%X' $(( _VSPI_NEW_CMD | _VSPI_READ_W ))) - - for (( i=0; i<_VSPI_POLL_MAX; i++ )); do - status=$(_pcimem "$FUNCT2_RES" "$cmd_off") - if (( ( ${status:-0} & _VSPI_READY_MASK) != 0 )); then - _pcimem "$FUNCT2_RES" "$rd_off" - return 0 - fi - done - echo "ERROR: VSPI read timeout @ offset $remote_offset" >&2 - return 1 -} - -_vspi_write() { - local win_off="$1" remote_offset="$2" data="$3" - local cmd_off add_off wr_off addr_val status i - - cmd_off=$(printf '0x%X' $(( win_off + _VSPI_CMD_STAT ))) - add_off=$(printf '0x%X' $(( win_off + _VSPI_ADDR ))) - wr_off=$(printf '0x%X' $(( win_off + _VSPI_WR_DATA ))) - addr_val=$(printf '0x%08X' $(( (0x0 << 20) | (remote_offset & 0xFFFFF) ))) - - _pcimem "$FUNCT2_RES" "$wr_off" "$data" - _pcimem "$FUNCT2_RES" "$add_off" "$addr_val" - _pcimem "$FUNCT2_RES" "$cmd_off" $(printf '0x%X' $(( _VSPI_NEW_CMD | _VSPI_WRITE_W ))) - - for (( i=0; i<_VSPI_POLL_MAX; i++ )); do - status=$(_pcimem "$FUNCT2_RES" "$cmd_off") - if (( ( ${status:-0} & _VSPI_READY_MASK) != 0 )); then - return 0 - fi - done - echo "ERROR: VSPI write timeout @ offset $remote_offset" >&2 - return 1 -} - -# Guard: PMC/ICB/SWB are single-function; signature is [data], NO fn arg. -# Common pitfall: using cb_fpga's " " form -> 2nd arg becomes data, a read -# turns into a write. If addr is 0~3 (looks like fn) AND data is given, warn and -# confirm; on a non-tty (script) just warn, don't block. -# Usage: _vspi_fnwarn non-zero return = cancel -_vspi_fnwarn() { - local name="$1" addr="$2" data="$3" - [[ -z "$data" ]] && return 0 # read, nothing to warn - case "$addr" in - 0|1|2|3|0x0|0x1|0x2|0x3|0x00|0x01|0x02|0x03) ;; # looks like fn, warn below - *) return 0 ;; # normal offset, allow - esac - echo -e "[\033[33mWARN\033[0m] $name has no fn arg; signature is [data] (NOT cb_fpga's )." >&2 - echo -e " This will WRITE $data to PMC/ICB/SWB offset $addr." >&2 - echo -e " To READ offset $addr, pass a single arg: $name $addr" >&2 - if [ -t 0 ]; then - local ans - read -r -p " Proceed with write anyway? [y/N] " ans - case "$ans" in - y|Y|yes|YES) return 0 ;; - *) echo " Cancelled." >&2; return 1 ;; - esac - fi - return 0 -} - -# === Public functions === - -cb_fpga() { - if [[ $# -lt 2 ]]; then - echo "Usage: cb_fpga [data]"; return 1 - fi - local fn="$1" addr="$2" data="${3:-}" res - case "$fn" in - 0) res=$FUNCT0_RES ;; 1) res=$FUNCT1_RES ;; - 2) res=$FUNCT2_RES ;; 3) res=$FUNCT3_RES ;; - *) echo "cb_fpga: fn must be 0-3" >&2; return 1 ;; - esac - # pcimem offset is relative to BAR start; do not add BAR. - if [[ -z "$data" ]]; then - _pcimem "$res" "$addr" - else - _pcimem "$res" "$addr" "$data" - fi -} - -pmc_fpga() { - if [[ $# -lt 1 ]]; then - echo "Usage: pmc_fpga [data]"; return 1 - fi - local addr="$1" data="${2:-}" - _vspi_fnwarn pmc_fpga "$addr" "$data" || return 1 - if [[ -z "$data" ]]; then _vspi_read "$VSPI_PMC_BASE" "$addr" - else _vspi_write "$VSPI_PMC_BASE" "$addr" "$data"; fi -} - -icb_fpga() { - if [[ $# -lt 1 ]]; then - echo "Usage: icb_fpga [data]"; return 1 - fi - local addr="$1" data="${2:-}" - _vspi_fnwarn icb_fpga "$addr" "$data" || return 1 - if [[ -z "$data" ]]; then _vspi_read "$VSPI_ICB_BASE" "$addr" - else _vspi_write "$VSPI_ICB_BASE" "$addr" "$data"; fi -} - -swb0_fpga() { - if [[ $# -lt 1 ]]; then - echo "Usage: swb0_fpga [data]"; return 1 - fi - local addr="$1" data="${2:-}" - _vspi_fnwarn swb0_fpga "$addr" "$data" || return 1 - if [[ -z "$data" ]]; then _vspi_read "$VSPI_SWB0_BASE" "$addr" - else _vspi_write "$VSPI_SWB0_BASE" "$addr" "$data"; fi -} - -swb1_fpga() { - if [[ $# -lt 1 ]]; then - echo "Usage: swb1_fpga [data]"; return 1 - fi - local addr="$1" data="${2:-}" - _vspi_fnwarn swb1_fpga "$addr" "$data" || return 1 - if [[ -z "$data" ]]; then _vspi_read "$VSPI_SWB1_BASE" "$addr" - else _vspi_write "$VSPI_SWB1_BASE" "$addr" "$data"; fi -} - -blanton_fpga_help() { - echo -e "\033[1mblanton_fpga (pcimem) - FPGA Register Access Tool\033[0m" - echo "" - echo -e "\033[1mUsage:\033[0m" - echo -e " \033[33mcb_fpga\033[0m [data] CB FPGA direct (sysfs resource0)" - echo -e " \033[33mpmc_fpga\033[0m [data] PMC FPGA via VSPI" - echo -e " \033[33micb_fpga\033[0m [data] ICB FPGA via VSPI" - echo -e " \033[33mswb0_fpga\033[0m [data] SWB0 FPGA via VSPI" - echo -e " \033[33mswb1_fpga\033[0m [data] SWB1 FPGA via VSPI" - echo -e " \033[33mfpga_debug\033[0m on|off Toggle debug mode (print commands and execute)" - echo "" - echo -e " Omit [data] to \033[32mread\033[0m, provide [data] to \033[31mwrite\033[0m." - echo -e " Backend: \033[36mpcimem\033[0m (sysfs resource file + relative offset)." - echo -e " Run \033[33mblanton_fpga_help\033[0m to show this message again." -} - -blanton_fpga_help