A conf1 access is two I/O instructions (index on 0xCF8, data on 0xCFC)
serialised against the kernel but not against SMM: an SMI landing between
them, whose handler runs its own CF8/CFC dialogue without restoring the
index, redirects our data phase to whatever register the firmware
addressed last. The CMD write is covered by the landing check, but the
DATA-register write has no readback, so its payload would land unnoticed
in a foreign config register. Suspected (not proven) trigger of the ws1
poweroff hang: the wedge sits in the firmware's S5 path and the poison
channel must be invisible to the driver, which this one is.
Map the function's ECAM page from the ACPI MCFG table at probe and route
every register touch through a single MMIO access, which no SMI can
split. use_ecam=0 (load-time) restores the config-cycle baseline for
A/B; MCFG lookup failure falls back automatically.
Downstream-only branch, not for submission. Two additions on top of the
v4 driver:
Approximate SMBus Quick with a byte-data read of register 0 so that
i2cdetect and OpenRGB's DDR4 probe see the devices. The engine cannot
do a zero-length transaction, so this is a real read against every
probed address - acceptable on a known bus, not as a generic facility,
which is why mainline does not get it.
Add protect_spd (bool, 0644, default on): reject SMBus writes to the
SPD EEPROMs (0x50-0x57) and to the DDR4 protection command addresses
SWPn/CWP (0x30-0x35) with -EPERM, and log the attempt ratelimited.
RSWP cannot be set in-system on this platform (SWPn/CWP require VHV on
SA0), so a driver-level refusal is the strongest available protection
against stray SPD writes from any i2c-dev client. The page-select
addresses 0x36/0x37 stay writable because ee1004 needs them (Send Byte
page select); ee1004 itself never writes the EEPROM contents. Toggle
at runtime via /sys/module/i2c_imc_skylake/parameters/protect_spd for
deliberate SPD maintenance, e.g. the documented repair recipe.
The old compat branch predates 1.0.1 (no native Receive Byte, no ee1004
instantiation, no I2C_FUNC_SMBUS_BYTE, so ee1004 cannot even bind) and
is renamed to attic/compat-stale-pre-1.0.1 so nobody builds on it.
The engine is shared with the firmware, and until now the driver never
returned it: at shutdown the BIOS finds whatever command state the last
transfer left behind. ws1-mint shows an intermittent poweroff hang
isolated to machine_power_off() -> ACPI _S5 with the engine in session
use; a BIOS SMI handler wedging on a mid-session engine at the S5 trap
is the working hypothesis. Handing the engine back the way the
firmware left it is correct hygiene regardless of that outcome.
- probe snapshots both pristine CMD words before the driver's first
transfer, and fails if they cannot be read: an unreadable snapshot
would turn the handback into writing garbage
- new .shutdown callback (shutdown and kexec): mark both adapters
suspended, drain the possible in-flight transfer under the lock,
wait for the engine to go idle, restore the pristine words with GO
masked. Best effort: it never blocks the power-off
- resume refreshes the snapshot after the firmware-polling re-check
passes, since the firmware demonstrably drives the engine across
suspend; an unreadable refresh leaves the adapters suspended
- the unbind comment now distinguishes unbind (state left as-is, next
user is another instance of this driver) from shutdown (next user is
the firmware)
Version 1.0.4.
Call i2c_register_spd() per adapter, like i801 and piix4, instead of
telling the user to echo into new_device. The helper counts populated
slots from DMI and probes upwards from 0x50 via i2c_new_scanned_device(),
so it claims no address that does not answer; each channel carries half
the DIMMs and the scan is per adapter, so each finds its own. Verified:
four clients on load, two per channel, none on the empty slots, and
decode-dimms works with no manual step.
This needed Receive Byte to be worth doing — without I2C_FUNC_SMBUS_BYTE
ee1004 would not have bound to what was instantiated.
The write_disable variant is deliberate. This part is DDR4 only, where
the two are equivalent (the split gates spd5118 on DDR5), and of two
equivalent calls the one that does not read as "this driver enables SPD
writes" is the better name to leave in a driver whose bus is the memory
bus. In-tree convention is write_enable; the cover letter offers to
switch.
Kconfig gains select I2C_SMBUS.
Out-of-tree, compat.h maps the call onto the older unsplit
i2c_register_spd() where that is what the target kernel has. It is force
included from the Makefile so i2c-imc-skylake.c stays byte-identical to
the submitted file, and the Makefile detects the API by grepping the
target kernel's header rather than testing a version number, which keeps
it correct on backported trees. Builds clean against both 6.8 and 7.0
headers; DKMS 1.0.2 installs on all four kernels here.
test-smoke.sh needed two fixes to keep testing what it claims:
- A bound driver owns its address, so the raw transaction checks lost
the SPDs the moment the driver started instantiating them. The script
now unbinds ee1004 for the duration rather than deleting the clients:
sysfs delete_device only removes clients that new_device created,
and unbinding is sufficient because i2c-dev refuses an address only
while a driver is bound (i2cdev_check: `return dev->driver ? -EBUSY
: 0`). Rebound from an EXIT trap.
- Those checks were guarded on an SPD address having been found, and
when the lookup failed they vanished from the run silently: the suite
reported "12 passed, 0 failed" while seven checks had not executed.
Skipped checks are now counted and printed, which is how the above
was noticed at all.
Smoke suite: 20 passed, 0 failed, 0 skipped. checkpatch --strict 0/0/0,
in-tree W=1 and Sparse clean.
The driver reached the PCU function's config space by walking the ACPI MCFG
table and mapping the ECAM page itself. That is one leaf driver deciding
config-space policy, and no other driver in mainline does it, so it was the
single most likely reason for a NAK.
Move that decision where it belongs: the upstream series is now two patches,
a PCI/x86 accessor that forces the memory-mapped path with the core's
locking, and this driver on top of it. Reviewers on each side now get
something they can act on, and get_maintainer.pl selects the PCI and x86
people by itself.
Correctness work in the same pass:
- the completion bits are latched, so a command the engine never accepted
was indistinguishable from a completed one and returned the previous
transaction's data as valid. Require a positive sign that the engine
acted on the command;
- compare the command registers after every transfer against what the
driver left in them and fail with -EAGAIN on a mismatch. The original
iMC driver had this check and it is what makes an unarbitrated system
detectable instead of silently wrong;
- use the i2c core's suspended-adapter mechanism rather than a private
flag and -EHOSTDOWN;
- use devm_mutex_init(), drop the unnecessary pci_enable_device(), and
return -ENODEV rather than -EPERM from the default-off gate, which was
printing a driver-core error on every boot;
- mask GO when restoring a saved command word.
Stop attributing the dropped CF8/CFC write to System Management Mode. The
only evidence was a boot log line naming the access method, which says
nothing about who filters the write. tools/probe-cf8-write.sh is there to
settle it with an MSR_SMI_COUNT measurement.
Add Documentation/i2c/busses/i2c-imc-skylake.rst, which every other PC SMBus
driver has and which was explicitly asked for on the 2020 iMC thread, the
tools/ scripts for the hardware measurements the submission still owes, and
compat/pci-mmcfg-compat.h so the driver still builds out-of-tree against a
kernel without the new accessor while the source stays byte-identical to the
submitted patch.