The CI shellcheck step would have failed on three unused variables. Two were leftovers in probe-cf8-transaction.sh from before the status decoding was simplified; the third was a loop counter in probe-imc-documented.sh that nothing reads. While there: probe-cf8-transaction.sh still described itself in terms of "patch 1/2 of the series", which no longer exists precisely because of what this script measured. Reworded to say what it establishes and why it is kept.
136 lines
4.1 KiB
Bash
Executable File
136 lines
4.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# WRITES TO HARDWARE. Issues an SMBus read through the interface Intel
|
|
# documents, rather than the one this driver currently uses.
|
|
#
|
|
# Intel Xeon Processor Scalable Family datasheet vol. 2 (ref. 614073) chapter 3
|
|
# puts the iMC SMBus interface on device 10 and 12, function 0:
|
|
#
|
|
# SMB_STAT_[0:1] 0xe80 0xe90 b31 RDO, b30 WOD, b29 SBE, b28 BUSY,
|
|
# b27:24 tsod_sa, b15:0 RDATA
|
|
# SMBCMD_[0:1] 0xe84 0xe94 b31 CMD_TRIGGER, b30 PNTR_SEL,
|
|
# b29 WORD_ACCESS, b28:27 rw, b26:24 SA,
|
|
# b23:16 BA, b15:0 WDATA
|
|
# SMBCNTL_[0:1] 0xe88 0xe98 b31:28 DTI (0xa EEPROM, 0x3 TSOD),
|
|
# b27 CKOVRD, b26 DIS_WRT, b10 SOFT_RST,
|
|
# b9 start_tsod_poll, b8 TSOD_POLL_EN,
|
|
# b7:0 TSOD_PRESENT
|
|
#
|
|
# The driver instead uses PCU function 5 at 0x9c-0xb8, which that document does
|
|
# not describe (it covers device 30 functions 0 and 2 only). This script reads
|
|
# a known SPD byte through the documented path so the two can be compared.
|
|
#
|
|
# Read transaction only, against an EEPROM that is certainly present. SMBCMD is
|
|
# restored on every exit path. SMBCNTL is not written at all.
|
|
#
|
|
set -euo pipefail
|
|
|
|
# shellcheck source=tools/common.sh
|
|
source "$(dirname "$(readlink -f "$0")")/common.sh"
|
|
|
|
SMB_STAT=(0xe80 0xe90)
|
|
SMB_CMD=(0xe84 0xe94)
|
|
SMB_CNTL=(0xe88 0xe98)
|
|
|
|
CMD_TRIGGER=$((1 << 31))
|
|
STAT_RDO=$((1 << 31))
|
|
STAT_WOD=$((1 << 30))
|
|
STAT_SBE=$((1 << 29))
|
|
STAT_BUSY=$((1 << 28))
|
|
CNTL_TSOD_POLL_EN=$((1 << 8))
|
|
|
|
SA=0 # slave address low bits; DTI 0xa | SA 0 selects 0x50
|
|
BA=0x02 # SPD byte 2, DDR4 type code, expected 0x0c
|
|
CONFIRMED=false
|
|
|
|
while (($#)); do
|
|
case $1 in
|
|
--i-understand) CONFIRMED=true; shift ;;
|
|
--sa) SA=$2; shift 2 ;;
|
|
--ba) BA=$2; shift 2 ;;
|
|
-h|--help)
|
|
sed -n '3,26p' "$0"
|
|
echo "usage: $0 --i-understand [--sa N] [--ba 0xNN]"
|
|
exit 0
|
|
;;
|
|
*) die "unknown argument: $1" ;;
|
|
esac
|
|
done
|
|
|
|
$CONFIRMED || {
|
|
echo "Issues an SMBus read from userspace. Re-run with --i-understand." >&2
|
|
exit 2
|
|
}
|
|
|
|
require_root
|
|
require_cmd lspci setpci awk
|
|
driver_is_loaded && die "unload i2c-imc-skylake first"
|
|
|
|
mapfile -t IMCS < <(lspci -Dn -d 8086:2040 | awk '{print $1}')
|
|
((${#IMCS[@]})) || die "no iMC channel device (8086:2040) found"
|
|
|
|
probe_one()
|
|
{
|
|
local bdf=$1 ch=$2
|
|
local stat=${SMB_STAT[ch]} cmd=${SMB_CMD[ch]} cntl=${SMB_CNTL[ch]}
|
|
local cntl_val stat_val cmd_saved command data
|
|
|
|
cntl_val=$(read_reg "$bdf" "$cntl")
|
|
stat_val=$(read_reg "$bdf" "$stat")
|
|
cmd_saved=$(read_reg "$bdf" "$cmd")
|
|
|
|
printf ' SMBCNTL=%s SMB_STAT=%s SMBCMD=%s\n' \
|
|
"$cntl_val" "$stat_val" "$cmd_saved"
|
|
|
|
if ((cntl_val & CNTL_TSOD_POLL_EN)); then
|
|
echo " SKIP: TSOD_POLL_EN is set, firmware owns this channel"
|
|
return 0
|
|
fi
|
|
if ((stat_val & STAT_BUSY)); then
|
|
echo " SKIP: SMB_BUSY is set"
|
|
return 0
|
|
fi
|
|
|
|
# byte read: TRIGGER, PNTR_SEL=0, WORD=0, rw=00, SA, BA
|
|
command=$((CMD_TRIGGER | (SA << 24) | (BA << 16)))
|
|
printf ' issuing SMBCMD=0x%08x (read SA=%d BA=%s)\n' "$command" "$SA" "$BA"
|
|
setpci -s "$bdf" "$cmd.L=$(printf '%08x' "$command")"
|
|
|
|
for _ in $(seq 1 50); do
|
|
stat_val=$(read_reg "$bdf" "$stat")
|
|
((stat_val & STAT_BUSY)) || break
|
|
sleep 0.005
|
|
done
|
|
|
|
setpci -s "$bdf" "$cmd.L=$(printf '%08x' "$cmd_saved")"
|
|
|
|
data=$((stat_val & 0xff))
|
|
printf ' SMB_STAT=%s RDO=%d WOD=%d SBE=%d BUSY=%d RDATA=0x%02x\n' \
|
|
"$stat_val" \
|
|
"$(((stat_val & STAT_RDO) != 0))" \
|
|
"$(((stat_val & STAT_WOD) != 0))" \
|
|
"$(((stat_val & STAT_SBE) != 0))" \
|
|
"$(((stat_val & STAT_BUSY) != 0))" \
|
|
"$data"
|
|
|
|
if ((stat_val & STAT_SBE)); then
|
|
echo " -> SBE: the addressed device did not answer on this channel"
|
|
elif ((stat_val & STAT_RDO)); then
|
|
printf ' -> READ OK, data 0x%02x\n' "$data"
|
|
if [[ "$BA" == "0x02" ]] && ((data == 0x0c)); then
|
|
echo " matches the DDR4 type code, so the documented"
|
|
echo " interface reaches the same devices."
|
|
fi
|
|
else
|
|
echo " -> no completion: the interface did not act on the command"
|
|
fi
|
|
}
|
|
|
|
for bdf in "${IMCS[@]}"; do
|
|
for ch in 0 1; do
|
|
echo "=== $bdf channel $ch ==="
|
|
probe_one "$bdf" "$ch"
|
|
echo
|
|
done
|
|
done
|