Compare commits
2
Commits
8aa5c90166
...
fd35857d80
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd35857d80 | ||
|
|
cdf38a09ae |
+2433
File diff suppressed because it is too large
Load Diff
@@ -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 <off> (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 <fn> <off> [data] ----------
|
||||
# _cbi2c_reg <abs_offset> [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<CB_I2C_TIP_POLL_MAX; i++ )); do
|
||||
status=$(_cbi2c_num "$(_cbi2c_reg "$cmd_off")")
|
||||
if (( (status & _CBI2C_TIP) == 0 )); then
|
||||
printf '0x%X' "$status"; return 0
|
||||
fi
|
||||
done
|
||||
echo -e "[\033[31mERR\033[0m] CB I2C TIP timeout (status=$status)" >&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 <ch> [prescale_lo] [prescale_hi]
|
||||
cb_i2c_init() {
|
||||
[[ $# -lt 1 ]] && { echo "Usage: cb_i2c_init <ch> [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 <ch>
|
||||
cb_i2c_reset() {
|
||||
[[ $# -lt 1 ]] && { echo "Usage: cb_i2c_reset <ch>"; 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 <ch> [status|acquire [val]|release]
|
||||
cb_i2c_sem() {
|
||||
[[ $# -lt 1 ]] && { echo "Usage: cb_i2c_sem <ch> [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 <ch> [status|acquire [val]|release]"; return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# cb_i2c_muxsel <ch> <dev_sel> -- write I2C_MUX_SEL_REG (FPGA drives ext mux pins)
|
||||
cb_i2c_muxsel() {
|
||||
[[ $# -lt 2 ]] && { echo "Usage: cb_i2c_muxsel <ch> <dev_sel>"; 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 <ch> -- 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 <ch> -- 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 <ch> <slave7> <reg> <nbytes> 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<n; i++ )); do
|
||||
last=$(( i == n-1 ))
|
||||
if (( last )); then cmdv=$(( _CBI2C_RD | _CBI2C_NACK | _CBI2C_STO )); else cmdv=$(( _CBI2C_RD )); fi
|
||||
_cbi2c_cmd_wait "$cmd" "$(printf '0x%X' "$cmdv")" >/dev/null
|
||||
val=$(_cbi2c_num "$(_cbi2c_reg "$rx")")
|
||||
out+="$(printf '0x%02X ' $((val & 0xFF)))"
|
||||
done
|
||||
echo "${out% }"
|
||||
}
|
||||
|
||||
# _cbi2c_xfer_write <ch> <slave7> <reg> <byte> [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 <ch> <slave7> <cmd> -- 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 <ch> <slave> <reg> [nbytes]
|
||||
cb_i2c_read() {
|
||||
[[ $# -lt 3 ]] && { echo "Usage: cb_i2c_read <ch> <slave> <reg> [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 <ch> <slave> <reg> <byte> [byte...]
|
||||
cb_i2c_write() {
|
||||
[[ $# -lt 4 ]] && { echo "Usage: cb_i2c_write <ch> <slave> <reg> <byte> [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 <ch> <mux_slave> <ctrl_byte> -- raw 1-byte write, no reg (TCA954x)
|
||||
cb_i2c_muxwrite() {
|
||||
[[ $# -lt 3 ]] && { echo "Usage: cb_i2c_muxwrite <ch> <mux_slave> <ctrl_byte>"; 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 <ch> -- probe 0x08..0x77, report slaves that ACK
|
||||
cb_i2c_scan() {
|
||||
[[ $# -lt 1 ]] && { echo "Usage: cb_i2c_scan <ch>"; 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 <ch> <slave> <cmd> [nbytes] (default 2 bytes, LSB first)
|
||||
cb_pmbus_read() {
|
||||
[[ $# -lt 3 ]] && { echo "Usage: cb_pmbus_read <ch> <slave> <cmd> [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 <ch> <slave> <cmd> [data byte...] (no byte = send-byte)
|
||||
cb_pmbus_write() {
|
||||
[[ $# -lt 3 ]] && { echo "Usage: cb_pmbus_write <ch> <slave> <cmd> [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 <word>
|
||||
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 <ch> [pre_lo] [pre_hi] prescale + enable (def 0x88/0x00 = 100kHz)"
|
||||
echo -e " \033[33mcb_i2c_reset\033[0m <ch> LCL_RST=0xD"
|
||||
echo -e " \033[33mcb_i2c_sem\033[0m <ch> [status|acquire [v]|release]"
|
||||
echo -e " \033[33mcb_i2c_muxsel\033[0m <ch> <dev_sel> 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 <ch>"
|
||||
echo -e " \033[33mcb_i2c_read\033[0m <ch> <slave> <reg> [nbytes]"
|
||||
echo -e " \033[33mcb_i2c_write\033[0m <ch> <slave> <reg> <byte> [byte...]"
|
||||
echo -e " \033[33mcb_i2c_muxwrite\033[0m <ch> <mux_slave> <ctrl_byte> raw 1-byte (TCA954x)"
|
||||
echo ""
|
||||
echo -e "\033[1mPMBus / SMBus\033[0m"
|
||||
echo -e " \033[33mcb_pmbus_read\033[0m <ch> <slave> <cmd> [nbytes] (def 2, LE word + linear16)"
|
||||
echo -e " \033[33mcb_pmbus_write\033[0m <ch> <slave> <cmd> [byte...]"
|
||||
echo -e " \033[33mcb_pmbus_linear16\033[0m <word>"
|
||||
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
|
||||
@@ -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 "<fn> <addr>" (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 <fn:0-3> <addr> [data]
|
||||
# pmc_fpga <addr> [data]
|
||||
# icb_fpga <addr> [data]
|
||||
# swb0_fpga <addr> [data]
|
||||
# swb1_fpga <addr> [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 <sysfile> <offset> [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 <addr> [data], NO fn arg.
|
||||
# Common pitfall: using cb_fpga's "<fn> <addr>" 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 <name> <addr> <data> 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 <addr> [data] (NOT cb_fpga's <fn> <addr>)." >&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 <fn:0-3> <addr> [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 <addr> [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 <addr> [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 <addr> [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 <addr> [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 <fn:0-3> <addr> [data] CB FPGA direct (sysfs resource0)"
|
||||
echo -e " \033[33mpmc_fpga\033[0m <addr> [data] PMC FPGA via VSPI"
|
||||
echo -e " \033[33micb_fpga\033[0m <addr> [data] ICB FPGA via VSPI"
|
||||
echo -e " \033[33mswb0_fpga\033[0m <addr> [data] SWB0 FPGA via VSPI"
|
||||
echo -e " \033[33mswb1_fpga\033[0m <addr> [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
|
||||
@@ -5,6 +5,20 @@
|
||||
# =============================================================================
|
||||
# Version Control
|
||||
# -----------------------------------------------------------------------------
|
||||
# v2.6.0 SWB path actually wired to the shared CB I2C package. blanton_cb_i2c.sh
|
||||
# and blanton_fpga_pcimem.sh live in the PARENT dir (Blanton_Script/), not
|
||||
# next to margin.sh, so the backend is now searched in both places.
|
||||
# Adds, for TRANSPORT=swb only (CB/i2c path untouched):
|
||||
# * cb_i2c_init <ch> once in margin_init (prescale 0x88 = 100kHz) and
|
||||
# CB_I2C_AUTO_INIT=0 afterwards -> 4 fewer pcimem writes per transfer
|
||||
# * cb_pmbus_read presence probe of every LTC2977 in CHIPS
|
||||
# * return-code checking on every cb_pmbus_read/write (NACK is reported
|
||||
# instead of silently reading 0x0000)
|
||||
# * DEBUG_MODE collision fix: blanton_fpga_pcimem.sh uses the same var
|
||||
# name, so pcimem tracing is gated by MARGIN_SWB_PCIMEM_DEBUG
|
||||
# * margin_swb_* wrappers (info/probe/scan/reset/sem) for debugging
|
||||
# Physical map: CB F3 Ch6=SWB0 CONN13/14, Ch8=SWB0 CONN15/16,
|
||||
# Ch7=SWB1 CONN13/14, Ch9=SWB1 CONN15/16
|
||||
# v2.5.0 SWB margin support via a per-conf TRANSPORT layer. The SWB margin
|
||||
# boards are ALSO LTC2980 (2x LTC2977) -- same chip as CB -- just reached
|
||||
# through the CB FPGA F3 I2C master instead of a native bus. So the whole
|
||||
@@ -55,6 +69,37 @@ LOG_MODE=1
|
||||
TRANSPORT="i2c"
|
||||
CB_I2C_CH=""
|
||||
|
||||
# --- SWB transport backend (blanton_cb_i2c.sh + blanton_fpga_pcimem.sh) -------
|
||||
# The SWB margin boards' LTC2980s hang off the CB FPGA Function 3 I2C master.
|
||||
# Physical mapping (per bench wiring):
|
||||
# CB F3 Ch6 -> SWB0 CONN13 / CONN14
|
||||
# CB F3 Ch8 -> SWB0 CONN15 / CONN16
|
||||
# CB F3 Ch7 -> SWB1 CONN13 / CONN14
|
||||
# CB F3 Ch9 -> SWB1 CONN15 / CONN16
|
||||
# Two CONNs share one FPGA I2C channel, so the two boards on a channel MUST use
|
||||
# different LTC2977 slave addresses (the CHIPS array in each .conf).
|
||||
MARGIN_SWB_CHANNELS="${MARGIN_SWB_CHANNELS:-6 7 8 9}"
|
||||
# SCL prescale for cb_i2c_init: 0x88 = 100kHz, 0x24 = 400kHz (@75MHz core clk).
|
||||
MARGIN_SWB_PRESCALE_LO="${MARGIN_SWB_PRESCALE_LO:-0x88}"
|
||||
MARGIN_SWB_PRESCALE_HI="${MARGIN_SWB_PRESCALE_HI:-0x00}"
|
||||
# 0 = cb_i2c_init once in margin_init, then CB_I2C_AUTO_INIT=0 so each
|
||||
# cb_pmbus_* transfer skips the 4-register prescale/enable dance (much faster:
|
||||
# margin_status all = 32 transfers). Set 1 to keep the package default (re-init
|
||||
# on every transfer) if something else on the box reprograms the channel.
|
||||
MARGIN_SWB_AUTO_INIT="${MARGIN_SWB_AUTO_INIT:-0}"
|
||||
# 1 = grab the per-channel FPGA semaphore (cb_i2c_sem acquire) in margin_init and
|
||||
# hold it for the whole session; margin_swb_sem_release frees it. Default off so
|
||||
# margin never fails just because someone else holds it.
|
||||
MARGIN_SWB_USE_SEM="${MARGIN_SWB_USE_SEM:-0}"
|
||||
# 1 = probe every LTC2977 in CHIPS (PMBus PAGE read) at margin_init.
|
||||
MARGIN_SWB_PROBE="${MARGIN_SWB_PROBE:-1}"
|
||||
# 1 = also let blanton_fpga_pcimem.sh print every underlying pcimem command.
|
||||
# Needed because that script's debug flag is ALSO called DEBUG_MODE; margin_debug
|
||||
# would otherwise flood the console with pcimem lines.
|
||||
MARGIN_SWB_PCIMEM_DEBUG="${MARGIN_SWB_PCIMEM_DEBUG:-0}"
|
||||
_SWB_READY=0
|
||||
_SWB_SEM_HELD=0
|
||||
|
||||
# Channel enable for margining. Rails the LTC2980 does not sequence (externally
|
||||
# enabled, e.g. an always-on MPQ8625 buck) sit with STATUS_WORD OFF=1 and won't
|
||||
# margin, because the servo DAC never soft-connects. Writing ON_OFF_CONFIG (0x02)
|
||||
@@ -131,6 +176,233 @@ margin_debug() {
|
||||
echo -e "[\033[34mINFO\033[0m] DEBUG_MODE=$DEBUG_MODE"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# SWB transport backend (TRANSPORT=swb only -- the CB/i2c path never gets here)
|
||||
# -----------------------------------------------------------------------------
|
||||
# Wire ops come from the shared CB I2C package:
|
||||
# blanton_fpga_pcimem.sh -> cb_fpga <fn> <off> [data] (PCIe BAR via pcimem)
|
||||
# blanton_cb_i2c.sh -> cb_i2c_init / cb_i2c_reset / cb_i2c_sem /
|
||||
# cb_i2c_scan / cb_pmbus_read / cb_pmbus_write
|
||||
# Both live in the parent Blanton_Script/ directory (a copy next to margin.sh is
|
||||
# also honoured if you ever bundle one).
|
||||
# =============================================================================
|
||||
|
||||
# Locate a backend script: next to margin.sh first, then the parent dir.
|
||||
_swb_find_backend() {
|
||||
local name="$1" p
|
||||
for p in "$SCRIPT_DIR/$name" "$SCRIPT_DIR/../$name"; do
|
||||
[ -f "$p" ] && { echo "$p"; return 0; }
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# Source blanton_fpga_pcimem.sh (cb_fpga) + blanton_cb_i2c.sh (cb_pmbus_*).
|
||||
_swb_backend_load() {
|
||||
local f
|
||||
if ! declare -F cb_fpga >/dev/null 2>&1; then
|
||||
if f=$(_swb_find_backend "blanton_fpga_pcimem.sh"); then
|
||||
# shellcheck source=/dev/null
|
||||
source "$f"
|
||||
echo -e "[\033[34mINFO\033[0m] SWB backend: sourced $f"
|
||||
else
|
||||
echo -e "[\033[31mERRO\033[0m] blanton_fpga_pcimem.sh not found in $SCRIPT_DIR or its parent"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
if ! declare -F cb_pmbus_read >/dev/null 2>&1; then
|
||||
if f=$(_swb_find_backend "blanton_cb_i2c.sh"); then
|
||||
# shellcheck source=/dev/null
|
||||
source "$f" >/dev/null
|
||||
echo -e "[\033[34mINFO\033[0m] SWB backend: sourced $f"
|
||||
else
|
||||
echo -e "[\033[31mERRO\033[0m] blanton_cb_i2c.sh not found in $SCRIPT_DIR or its parent"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
declare -F cb_pmbus_read >/dev/null 2>&1 || {
|
||||
echo -e "[\033[31mERRO\033[0m] cb_pmbus_read still undefined after sourcing the backend"; return 1; }
|
||||
return 0
|
||||
}
|
||||
|
||||
# Run a cb_* command with pcimem tracing suppressed (shared DEBUG_MODE var).
|
||||
# Echoes the command's stdout, preserves its return code.
|
||||
_swb_call() {
|
||||
local _saved="$DEBUG_MODE" _rc
|
||||
[ "$MARGIN_SWB_PCIMEM_DEBUG" = "1" ] || DEBUG_MODE=0
|
||||
"$@"
|
||||
_rc=$?
|
||||
DEBUG_MODE="$_saved"
|
||||
return $_rc
|
||||
}
|
||||
|
||||
# Probe one LTC2977: 1-byte PMBus read of PAGE (0x00). Returns cb_pmbus_read's rc.
|
||||
_swb_probe_addr() {
|
||||
_swb_call cb_pmbus_read "$CB_I2C_CH" "$1" 0x00 1 >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# --- SWB wire ops used by the transport switch below ------------------------
|
||||
# Both use _ADDR (LTC2977 7-bit slave) set by _get_chip and CB_I2C_CH from the
|
||||
# .conf. Unlike i2cset/i2cget these report failures: a NACK on the FPGA I2C
|
||||
# master used to silently turn into Vout=0.0000.
|
||||
|
||||
# _swb_write <reg> <byte> [byte...] -> cb_pmbus_write (LSB first for LINEAR16)
|
||||
_swb_write() {
|
||||
local reg=$1; shift
|
||||
[ "$DEBUG_MODE" = "1" ] && echo -e "\033[90m[DEBG] cb_pmbus_write $CB_I2C_CH $_ADDR $reg $*\033[0m"
|
||||
if ! _swb_call cb_pmbus_write "$CB_I2C_CH" "$_ADDR" "$reg" "$@" >/dev/null 2>&1; then
|
||||
echo -e "[\033[31mERRO\033[0m] cb_pmbus_write ch=$CB_I2C_CH $_ADDR reg=$reg failed (NACK/timeout)" >&2
|
||||
_log_detail "ERRO cb_pmbus_write ch=$CB_I2C_CH $_ADDR $reg $*"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# _swb_read_word <reg> -> echoes "0xXXXX" parsed out of cb_pmbus_read's "=> 0xXXXX"
|
||||
_swb_read_word() {
|
||||
local reg=$1 out word
|
||||
[ "$DEBUG_MODE" = "1" ] && echo -e "\033[90m[DEBG] cb_pmbus_read $CB_I2C_CH $_ADDR $reg 2\033[0m" >&2
|
||||
if ! out=$(_swb_call cb_pmbus_read "$CB_I2C_CH" "$_ADDR" "$reg" 2 2>/dev/null); then
|
||||
echo -e "[\033[31mERRO\033[0m] cb_pmbus_read ch=$CB_I2C_CH $_ADDR reg=$reg failed (NACK/timeout)" >&2
|
||||
_log_detail "ERRO cb_pmbus_read ch=$CB_I2C_CH $_ADDR $reg"
|
||||
echo "0x0000"
|
||||
return 1
|
||||
fi
|
||||
# cb_pmbus_read prints " [PMB-R] ... => 0xXXXX raw=[...] linear16=..."
|
||||
word=$(printf '%s' "$out" | awk -F'=> ' 'NF>1{print $2}' | awk '{print $1}')
|
||||
[[ "$word" =~ ^0[xX][0-9a-fA-F]+$ ]] || word="0x0000"
|
||||
echo "$word"
|
||||
}
|
||||
|
||||
# Bring up the FPGA I2C channel for this .conf: validate CB_I2C_CH, prescale +
|
||||
# enable it once (cb_i2c_init), optionally take the semaphore, then probe CHIPS.
|
||||
_swb_setup() {
|
||||
_SWB_READY=0
|
||||
|
||||
if [ -z "$CB_I2C_CH" ]; then
|
||||
echo -e "[\033[31mERRO\033[0m] TRANSPORT=swb but CB_I2C_CH is empty in the .conf"
|
||||
return 1
|
||||
fi
|
||||
if ! [[ "$CB_I2C_CH" =~ ^[0-9]+$ ]] || [ "$CB_I2C_CH" -gt 17 ]; then
|
||||
echo -e "[\033[31mERRO\033[0m] CB_I2C_CH='$CB_I2C_CH' invalid (CB F3 I2C channels are 0..17)"
|
||||
return 1
|
||||
fi
|
||||
case " $MARGIN_SWB_CHANNELS " in
|
||||
*" $CB_I2C_CH "*) ;;
|
||||
*) echo -e "[\033[33mWARN\033[0m] CB_I2C_CH=$CB_I2C_CH is not a known SWB VRM channel ($MARGIN_SWB_CHANNELS)" ;;
|
||||
esac
|
||||
|
||||
# Prescale + MOD_EN on this channel.
|
||||
if ! _swb_call cb_i2c_init "$CB_I2C_CH" "$MARGIN_SWB_PRESCALE_LO" "$MARGIN_SWB_PRESCALE_HI"; then
|
||||
echo -e "[\033[31mERRO\033[0m] cb_i2c_init $CB_I2C_CH failed (FPGA/pcimem access?)"
|
||||
return 1
|
||||
fi
|
||||
CB_I2C_AUTO_INIT="$MARGIN_SWB_AUTO_INIT"
|
||||
local _khz="?"; [ "$MARGIN_SWB_PRESCALE_LO" = "0x88" ] && _khz="100"
|
||||
[ "$MARGIN_SWB_PRESCALE_LO" = "0x24" ] && _khz="400"
|
||||
echo -e "[\033[34mINFO\033[0m] cb_i2c_init ch=$CB_I2C_CH prescale=$MARGIN_SWB_PRESCALE_LO/$MARGIN_SWB_PRESCALE_HI (~${_khz}kHz), CB_I2C_AUTO_INIT=$CB_I2C_AUTO_INIT"
|
||||
_log "swb: cb_i2c_init ch=$CB_I2C_CH prescale=$MARGIN_SWB_PRESCALE_LO auto_init=$CB_I2C_AUTO_INIT"
|
||||
|
||||
if [ "$MARGIN_SWB_USE_SEM" = "1" ]; then
|
||||
if _swb_call cb_i2c_sem "$CB_I2C_CH" acquire; then
|
||||
_SWB_SEM_HELD=1
|
||||
else
|
||||
echo -e "[\033[33mWARN\033[0m] semaphore on ch=$CB_I2C_CH not acquired; continuing without it"
|
||||
fi
|
||||
fi
|
||||
|
||||
_SWB_READY=1
|
||||
|
||||
if [ "$MARGIN_SWB_PROBE" = "1" ]; then
|
||||
local idx=0 chip addr bad=0
|
||||
for chip in "${CHIPS[@]}"; do
|
||||
IFS=':' read -r _ addr <<< "$chip"
|
||||
if _swb_probe_addr "$addr"; then
|
||||
echo -e "[\033[34mINFO\033[0m] probe ch=$CB_I2C_CH LTC2977[$idx] $addr : \033[32mACK\033[0m"
|
||||
else
|
||||
echo -e "[\033[33mWARN\033[0m] probe ch=$CB_I2C_CH LTC2977[$idx] $addr : \033[31mNACK\033[0m (wrong CB_I2C_CH / addr, board absent, or channel stuck -> margin_swb_scan / margin_swb_reset)"
|
||||
bad=1
|
||||
fi
|
||||
idx=$((idx+1))
|
||||
done
|
||||
_log "swb: probe ch=$CB_I2C_CH chips=${CHIPS[*]} $( [ "$bad" = "0" ] && echo all-ACK || echo has-NACK )"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# --- SWB debug / maintenance wrappers (thin shells over blanton_cb_i2c.sh) ---
|
||||
_swb_guard() {
|
||||
if [ "$TRANSPORT" != "swb" ]; then
|
||||
echo -e "[\033[33mWARN\033[0m] current setting is TRANSPORT=$TRANSPORT (not swb); nothing to do"
|
||||
return 1
|
||||
fi
|
||||
declare -F cb_pmbus_read >/dev/null 2>&1 || { echo -e "[\033[31mERRO\033[0m] SWB backend not loaded; run margin_init first"; return 1; }
|
||||
return 0
|
||||
}
|
||||
|
||||
# margin_swb_info -- show the transport wiring of the loaded setting
|
||||
margin_swb_info() {
|
||||
echo "-------------"
|
||||
echo "[$PART_NUMBER] transport"
|
||||
echo "-------------"
|
||||
echo " TRANSPORT : $TRANSPORT"
|
||||
if [ "$TRANSPORT" = "swb" ]; then
|
||||
echo " CB_I2C_CH : $CB_I2C_CH (CB FPGA F3, base $(printf '0x%X' $(( 0x300 + CB_I2C_CH * 0x20 ))))"
|
||||
echo " LTC2977 : ${CHIPS[*]} (bus field unused on swb)"
|
||||
echo " prescale : $MARGIN_SWB_PRESCALE_LO/$MARGIN_SWB_PRESCALE_HI auto_init=$CB_I2C_AUTO_INIT sem_held=$_SWB_SEM_HELD"
|
||||
echo " wire ops : cb_pmbus_write $CB_I2C_CH <addr> 0x00 <page> / cb_pmbus_read $CB_I2C_CH <addr> 0x8b 2"
|
||||
else
|
||||
echo " bus:addr : ${CHIPS[*]} (i2cset/i2cget)"
|
||||
fi
|
||||
}
|
||||
|
||||
# margin_swb_probe -- re-run the PAGE-read presence check on every LTC2977
|
||||
margin_swb_probe() {
|
||||
_swb_guard || return 1
|
||||
local idx=0 chip addr
|
||||
for chip in "${CHIPS[@]}"; do
|
||||
IFS=':' read -r _ addr <<< "$chip"
|
||||
if _swb_probe_addr "$addr"; then
|
||||
echo -e " ch=$CB_I2C_CH $addr : \033[32mACK\033[0m"
|
||||
else
|
||||
echo -e " ch=$CB_I2C_CH $addr : \033[31mNACK\033[0m"
|
||||
fi
|
||||
idx=$((idx+1))
|
||||
done
|
||||
}
|
||||
|
||||
# margin_swb_scan [ch] -- cb_i2c_scan on this conf's channel (0x08..0x77)
|
||||
margin_swb_scan() {
|
||||
_swb_guard || return 1
|
||||
_swb_call cb_i2c_scan "${1:-$CB_I2C_CH}"
|
||||
}
|
||||
|
||||
# margin_swb_reset -- local I2C controller reset (0xD) + re-init, for a stuck bus
|
||||
margin_swb_reset() {
|
||||
_swb_guard || return 1
|
||||
_swb_call cb_i2c_reset "$CB_I2C_CH"
|
||||
_swb_call cb_i2c_init "$CB_I2C_CH" "$MARGIN_SWB_PRESCALE_LO" "$MARGIN_SWB_PRESCALE_HI"
|
||||
echo -e "[\033[34mINFO\033[0m] ch=$CB_I2C_CH reset + re-init done"
|
||||
_log "swb: cb_i2c_reset + cb_i2c_init ch=$CB_I2C_CH"
|
||||
}
|
||||
|
||||
# margin_swb_sem [status|acquire|release]
|
||||
margin_swb_sem() {
|
||||
_swb_guard || return 1
|
||||
local act="${1:-status}"
|
||||
_swb_call cb_i2c_sem "$CB_I2C_CH" "$act"
|
||||
case "$act" in
|
||||
acquire) _SWB_SEM_HELD=1 ;;
|
||||
release) _SWB_SEM_HELD=0 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# margin_swb_sem_release -- release the session semaphore if margin_init took it
|
||||
margin_swb_sem_release() {
|
||||
[ "$_SWB_SEM_HELD" = "1" ] || { echo -e "[\033[34mINFO\033[0m] no semaphore held by this session"; return 0; }
|
||||
_swb_call cb_i2c_sem "$CB_I2C_CH" release
|
||||
_SWB_SEM_HELD=0
|
||||
}
|
||||
|
||||
# --- Init: load setting by name ---
|
||||
margin_init() {
|
||||
if [ -z "$1" ]; then
|
||||
@@ -150,15 +422,13 @@ margin_init() {
|
||||
TRANSPORT="${TRANSPORT:-i2c}" # a blank TRANSPORT="" in a .conf means i2c
|
||||
echo -e "[\033[34mINFO\033[0m] Loaded setting: $setting_file (transport=$TRANSPORT)"
|
||||
|
||||
# SWB rails need the cb_pmbus_* backend; source it on demand.
|
||||
if [ "$TRANSPORT" = "swb" ] && ! declare -F cb_pmbus_read >/dev/null 2>&1; then
|
||||
if [ -f "$SCRIPT_DIR/blanton_cb_i2c.sh" ]; then
|
||||
# shellcheck source=/dev/null
|
||||
source "$SCRIPT_DIR/blanton_cb_i2c.sh"
|
||||
else
|
||||
echo -e "[\033[31mERRO\033[0m] TRANSPORT=swb but blanton_cb_i2c.sh not found in $SCRIPT_DIR"
|
||||
return 1
|
||||
fi
|
||||
# SWB rails need the cb_pmbus_* backend (blanton_cb_i2c.sh + blanton_fpga_pcimem.sh,
|
||||
# normally in the parent Blanton_Script/ dir); load it and bring the FPGA I2C
|
||||
# channel up once. CB/i2c settings skip this entirely.
|
||||
_SWB_READY=0; _SWB_SEM_HELD=0
|
||||
if [ "$TRANSPORT" = "swb" ]; then
|
||||
_swb_backend_load || return 1
|
||||
_swb_setup || return 1
|
||||
fi
|
||||
_log_start_session
|
||||
}
|
||||
@@ -221,8 +491,7 @@ _get_page() {
|
||||
# PAGE is written the same way on both (PMBus command 0x00).
|
||||
_open_page() {
|
||||
if [ "$TRANSPORT" = "swb" ]; then
|
||||
[ "$DEBUG_MODE" = "1" ] && echo -e "\033[90m[DEBG] cb_pmbus_write $CB_I2C_CH $_ADDR 0x00 $1\033[0m"
|
||||
cb_pmbus_write "$CB_I2C_CH" "$_ADDR" 0x00 "$1" >/dev/null 2>&1
|
||||
_swb_write 0x00 "$1"
|
||||
return
|
||||
fi
|
||||
[ "$DEBUG_MODE" = "1" ] && echo -e "\033[90m[DEBG] i2cset -y $_BUS $_ADDR 0x00 $1 b\033[0m"
|
||||
@@ -233,8 +502,7 @@ _open_page() {
|
||||
_i2c_write_word() {
|
||||
local reg=$1 lo=$2 hi=$3
|
||||
if [ "$TRANSPORT" = "swb" ]; then
|
||||
[ "$DEBUG_MODE" = "1" ] && echo -e "\033[90m[DEBG] cb_pmbus_write $CB_I2C_CH $_ADDR $reg $lo $hi\033[0m"
|
||||
cb_pmbus_write "$CB_I2C_CH" "$_ADDR" "$reg" "$lo" "$hi" >/dev/null 2>&1
|
||||
_swb_write "$reg" "$lo" "$hi"
|
||||
return
|
||||
fi
|
||||
local word=$(( (hi << 8) | lo ))
|
||||
@@ -245,8 +513,7 @@ _i2c_write_word() {
|
||||
|
||||
_i2c_write_byte() {
|
||||
if [ "$TRANSPORT" = "swb" ]; then
|
||||
[ "$DEBUG_MODE" = "1" ] && echo -e "\033[90m[DEBG] cb_pmbus_write $CB_I2C_CH $_ADDR $1 $2\033[0m"
|
||||
cb_pmbus_write "$CB_I2C_CH" "$_ADDR" "$1" "$2" >/dev/null 2>&1
|
||||
_swb_write "$1" "$2"
|
||||
return
|
||||
fi
|
||||
[ "$DEBUG_MODE" = "1" ] && echo -e "\033[90m[DEBG] i2cset -y $_BUS $_ADDR $1 $2 b\033[0m"
|
||||
@@ -256,13 +523,7 @@ _i2c_write_byte() {
|
||||
# Read a 16-bit word; echoes "0xXXXX". For swb, parse cb_pmbus_read's "=> 0xXXXX".
|
||||
_i2c_read_word() {
|
||||
if [ "$TRANSPORT" = "swb" ]; then
|
||||
[ "$DEBUG_MODE" = "1" ] && echo -e "\033[90m[DEBG] cb_pmbus_read $CB_I2C_CH $_ADDR $1 2\033[0m" >&2
|
||||
local out word
|
||||
out=$(cb_pmbus_read "$CB_I2C_CH" "$_ADDR" "$1" 2 2>/dev/null)
|
||||
# cb_pmbus_read prints "... => 0xXXXX raw=[...] ..."; take the word after "=>".
|
||||
word=$(printf '%s' "$out" | awk -F'=> ' 'NF>1{print $2}' | awk '{print $1}')
|
||||
[[ "$word" =~ ^0[xX][0-9a-fA-F]+$ ]] || word="0x0000"
|
||||
echo "$word"
|
||||
_swb_read_word "$1"
|
||||
return
|
||||
fi
|
||||
[ "$DEBUG_MODE" = "1" ] && echo -e "\033[90m[DEBG] i2cget -y $_BUS $_ADDR $1 w\033[0m" >&2
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
# --- SWB transport (added): this LTC2980 sits behind the CB FPGA F3 I2C; reach
|
||||
# it with cb_pmbus, not i2cset. CB_I2C_CH = FPGA I2C channel for this CONN. ---
|
||||
TRANSPORT="swb"
|
||||
CB_I2C_CH=6
|
||||
CB_I2C_CH=6 # CB F3 Ch6 = SWB0 CONN13/CONN14 (shared with CONN14, addr must differ)
|
||||
|
||||
# --- I2C Configuration ---
|
||||
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
# --- SWB transport (added): this LTC2980 sits behind the CB FPGA F3 I2C; reach
|
||||
# it with cb_pmbus, not i2cset. CB_I2C_CH = FPGA I2C channel for this CONN. ---
|
||||
TRANSPORT="swb"
|
||||
CB_I2C_CH=8
|
||||
CB_I2C_CH=6 # CB F3 Ch6 = SWB0 CONN13/CONN14 (shared with CONN13, addr must differ)
|
||||
|
||||
# --- I2C Configuration ---
|
||||
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
# --- SWB transport (added): this LTC2980 sits behind the CB FPGA F3 I2C; reach
|
||||
# it with cb_pmbus, not i2cset. CB_I2C_CH = FPGA I2C channel for this CONN. ---
|
||||
TRANSPORT="swb"
|
||||
CB_I2C_CH=6
|
||||
CB_I2C_CH=8 # CB F3 Ch8 = SWB0 CONN15/CONN16 (shared with CONN16, addr must differ)
|
||||
|
||||
# --- I2C Configuration ---
|
||||
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
# --- SWB transport (added): this LTC2980 sits behind the CB FPGA F3 I2C; reach
|
||||
# it with cb_pmbus, not i2cset. CB_I2C_CH = FPGA I2C channel for this CONN. ---
|
||||
TRANSPORT="swb"
|
||||
CB_I2C_CH=8
|
||||
CB_I2C_CH=8 # CB F3 Ch8 = SWB0 CONN15/CONN16 (shared with CONN15, addr must differ)
|
||||
|
||||
# --- I2C Configuration ---
|
||||
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
# --- SWB transport (added): this LTC2980 sits behind the CB FPGA F3 I2C; reach
|
||||
# it with cb_pmbus, not i2cset. CB_I2C_CH = FPGA I2C channel for this CONN. ---
|
||||
TRANSPORT="swb"
|
||||
CB_I2C_CH=7
|
||||
CB_I2C_CH=7 # CB F3 Ch7 = SWB1 CONN13/CONN14 (shared with CONN14, addr must differ)
|
||||
|
||||
# --- I2C Configuration ---
|
||||
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
# --- SWB transport (added): this LTC2980 sits behind the CB FPGA F3 I2C; reach
|
||||
# it with cb_pmbus, not i2cset. CB_I2C_CH = FPGA I2C channel for this CONN. ---
|
||||
TRANSPORT="swb"
|
||||
CB_I2C_CH=9
|
||||
CB_I2C_CH=7 # CB F3 Ch7 = SWB1 CONN13/CONN14 (shared with CONN13, addr must differ)
|
||||
|
||||
# --- I2C Configuration ---
|
||||
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
# --- SWB transport (added): this LTC2980 sits behind the CB FPGA F3 I2C; reach
|
||||
# it with cb_pmbus, not i2cset. CB_I2C_CH = FPGA I2C channel for this CONN. ---
|
||||
TRANSPORT="swb"
|
||||
CB_I2C_CH=7
|
||||
CB_I2C_CH=9 # CB F3 Ch9 = SWB1 CONN15/CONN16 (shared with CONN16, addr must differ)
|
||||
|
||||
# --- I2C Configuration ---
|
||||
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
# --- SWB transport (added): this LTC2980 sits behind the CB FPGA F3 I2C; reach
|
||||
# it with cb_pmbus, not i2cset. CB_I2C_CH = FPGA I2C channel for this CONN. ---
|
||||
TRANSPORT="swb"
|
||||
CB_I2C_CH=9
|
||||
CB_I2C_CH=9 # CB F3 Ch9 = SWB1 CONN15/CONN16 (shared with CONN15, addr must differ)
|
||||
|
||||
# --- I2C Configuration ---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user