Commit Graph
16 Commits
Author SHA1 Message Date
Simone cd6e8cf145 i2c-imc-skylake: document the ee1004 address-ownership trade-off
A bound ee1004 owns 0x50-0x57, so userspace loses /dev/i2c access to
them. On this bus that is not academic: furyd's SPD guard reports
0/4 present while ee1004 holds the addresses.
2026-07-27 01:28:33 +02:00
Simone 625bbacc17 i2c-imc-skylake: bump to 1.0.1
Receive Byte and Send Byte. DKMS 1.0.1 built and installed on
6.8.0-134/136 and 7.0.0-14/28; 1.0.0 removed from the tree.
2026-07-27 01:27:26 +02:00
Simone c04e0ec5a8 i2c-imc-skylake: implement Receive Byte and Send Byte
The engine encodes an address-only transaction in command word bit 18,
which is what SMBus Receive Byte and Send Byte need and what was missing
for ee1004 to bind.

The bit is not documented for the PCU function. It was located by
structure rather than by sweeping: SMBCMD on the iMC channel function
(datasheet ref. 614073 section 3.1.8) places PNTR_SEL immediately above
WORD_ACCESS, and word access here is bit 17. Bit 17 had itself been
unknown until it turned out to be word access, and an earlier sweep of
this register covered bits 31:20 only, so 18 was untried rather than
already ruled out.

Validated against the device rather than against the register. An EEPROM
auto-increments its pointer after a read, so priming the pointer and
repeating the read with the bit set has a predicted answer: the next
byte, not the one named. Eight consecutive reads returned SPD bytes 0x01
through 0x08 in order, matching an ordinary dump. Send Byte was confirmed
on the DDR4 page-select addresses, which hold a latch and store no data,
so the page state is itself the readout.

Result: ee1004 binds on all four SPD addresses and decode-dimms decodes
all four modules with both CRCs valid on each. The part number comes off
page 1, so the page select runs end to end. The four module serial
numbers are unchanged from the baseline recorded before this series.

SMBus Quick stays unimplemented. Beyond having no obvious encoding, a
write into the 0x30-0x37 range is how an EE1004 SPD is write-protected,
permanently on many modules, so an adapter on the memory bus should not
advertise a probe that writes. i2cdetect scans in read mode.

Also fix test-smoke.sh resolving the module through modinfo, which
silently tested the installed DKMS build instead of the one just
compiled. It now prefers the local .ko and prints which it loaded.

Smoke suite: 19 passed, 0 failed. checkpatch --strict 0/0/0, in-tree W=1
and Sparse clean.
2026-07-27 01:26:07 +02:00
Simone 77452d5697 ci: scope the documentation check to this file, and install graphviz
The htmldocs step failed on its own success criterion. The only diagnostic
in the log was

  WARNING: dot(1) not found, for better output quality install graphviz

which says nothing about this patch. Failing the job on any occurrence of
"warning" anywhere in a kernel documentation build is too blunt: that build
emits diagnostics that depend on what happens to be installed on the runner.

Install graphviz so the warning is gone for the right reason, and narrow the
check to diagnostics that actually name i2c-imc-skylake, which is what the
step was meant to verify.
2026-07-27 00:16:42 +02:00
Simone 5f97a71ad2 ci: fix the two Clang jobs after runner image drift
Both Clang jobs broke without the driver changing. GCC passed everywhere it
actually compiles: out-of-tree, in-tree x86_64, checkpatch and shellcheck.

The out-of-tree LLVM=1 job never reached this source. The runner now ships
6.17.0-1020-azure headers built with GCC, and their Makefiles pass flags
clang rejects outright:

  clang: error: unknown argument: '-fconserve-stack'
  clang: error: unsupported argument 'bounds-strict' to option '-fsanitize='
  clang: error: unsupported option '-mrecord-mcount'

Building a module with a different compiler than the kernel it links against
is not something to paper over, and it was testing nothing useful: the
meaningful Clang coverage is the in-tree job, which controls the whole
build. Drop Clang from the out-of-tree job and say why in a comment.

The in-tree arm64 job failed at defconfig with "linker 'ld.lld' not found".
LLVM=1 wants the whole LLVM toolchain and ld.lld ships in its own package,
which the runner no longer pulls in as a dependency of llvm. Install lld.

Since the out-of-tree job no longer needs a matrix, its steps lose the
per-compiler conditionals and shellcheck moves into the dependency install
rather than being fetched mid-job.
2026-07-27 00:12:58 +02:00
Simone 5988db06ad tools: satisfy shellcheck and drop stale references to the dropped patch
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.
2026-07-27 00:08:53 +02:00
Simone c9b8799f69 i2c-imc-skylake: suspend/resume, and correct the ee1004 claim
Two things came out of preparing the suspend test, and one of them is a
correction.

ee1004 does not bind to this adapter. Both functionality combinations it
accepts include I2C_FUNC_SMBUS_BYTE (drivers/misc/eeprom/ee1004.c:301),
which this driver does not implement, so the probe fails with
-EPFNOSUPPORT. The cover letter claimed BYTE_DATA and WORD_DATA "cover
ee1004 and jc42" and the .rst told the reader to instantiate it; both were
wrong and are corrected. jc42 is fine, it only needs BYTE_DATA and
WORD_DATA, but no TSOD is populated here to bind to.

The practical cost is that decode-dimms does not work through this adapter.
SPD bytes stay readable byte by byte with i2cget. Receive Byte is what
PawnIO implements with its pointer-select bit and what this project chose
not to advertise for want of validation; that choice was not free, and
adding it validated is the most useful thing left.

Suspend and resume tested with continuous traffic on both adapters, s2idle
and S3. Transfers recover, nothing complains. Across S3 the engine comes
back reprogrammed on both channels:

  DATA   0x2000b600 -> 0x20005000
  STATUS 0x0000500c -> 0x00005004

The command word addresses SPD 0x50 for a read and the status drops to
READ_DONE alone. Linux issued none of it: the adapter was marked suspended
and userspace frozen. This is consistent with firmware reading SPD during
memory training on resume, and it is the first direct observation of a
second master on this engine. It is also the concrete justification for
restoring command state per transfer and comparing it afterwards.

Add tools/test-suspend.sh, which drives that test and reports the register
delta across the sleep.

Traffic goes through i2c-dev rather than a bound client: with ee1004 unable
to attach and no TSOD present, there is no in-kernel client to use, and the
PM path exercised is the same either way -- i2c_smbus_xfer() consults
__i2c_check_suspended() before reaching the adapter.
2026-07-27 00:06:11 +02:00
Simone 87babcb166 i2c-imc-skylake: validate the write path end to end
The driver advertises SMBus byte and word writes but no write to a real
device had been exercised in this cycle. Engine-level evidence existed -- a
write to an unpopulated address returns -ENXIO with the address NACK
reported, so writes are issued rather than suppressed -- but nothing showed
a populated device accepting one.

Driven through the sibling project's CLI, which uses the framing decoded in
its own protocol notes: a 12-write frame to the LED controller at 0x27 on
both channels, latch open, effect id, brightness, three colour bytes, latch
commit. Every write acknowledged, no NACK, no interference reported, and
the device applied the result.

This also settles SMB_DIS_WRT, which is set in the documented SMBCNTL and
is SMM-writable only: it governs the documented interface, not this path.

No SPD was written at any point. The four SPD EEPROMs were verified present
with valid CRCs before and after every experiment in this cycle.
2026-07-26 21:54:25 +02:00
Simone b021fe4087 i2c-imc-skylake: close F2 with the register Intel documents
The open question was how to know whether the memory controller is polling
the DIMM thermal sensors, which is what killed the Broadwell-E attempt's
predecessor and what this driver had no answer for.

Intel documents it. The Xeon Processor Scalable Family datasheet volume 2
(reference 614073, section 3.1.9) puts SMBCNTL on the iMC channel function,
device 10 and 12 function 0, at offsets 0xe88 and 0xe98. SMB_TSOD_POLL_EN
is documented as mutually exclusive with SPD command access, so it answers
the question directly. The driver now reads it on every channel at probe
and refuses to bind if it is set.

This is deliberately weaker than the Broadwell-E driver, which switched the
polling off for the duration of a transfer. Reading and declining is a
smaller claim on hardware the driver does not own and needs no write to a
firmware-configured register. It covers the memory controller only; SMM and
a BMC are still out of reach, which is what the opt-in is for.

On the test system all four channels read 0xac000000: polling disabled,
TSOD_PRESENT empty, which also explains why no jc42 ever appeared at
0x18-0x1f on these DIMMs. Offset 0xe0, where the Broadwell driver has its
TSODCNTL, reads zero on all seven PCU functions here: that layout does not
transfer.

Two more measurements, both recorded in the cover letter:

  - the SMBus interface Intel documents, at 0xe80-0xe9c on the iMC, does not
    reach the bus on this platform. Issuing SMBCMD with CMD_TRIGGER sets
    SMB_RDO and clears SMB_BUSY, but returns 0x00 for a populated DIMM at
    0x50, a second one at 0x52 and an empty slot at 0x57 alike, with SMB_SBE
    never set. The path this driver uses returns the correct SPD contents
    and a NACK for the empty slot. The datasheet does not describe PCU
    function 5 at all, so the choice is now stated with data rather than
    left for a reviewer to ask about;
  - writes are issued on the bus: a byte write to an unpopulated address
    returns -ENXIO with the engine reporting the address NACK. This matters
    because SMB_DIS_WRT is set in the documented SMBCNTL and is SMM-writable
    only, so it evidently governs that interface and not this one.

Engine registers sampled every 2 s for an hour with the driver unloaded:
1791 samples, 0 changes.

Add tools/probe-imc-documented.sh, which produced the comparison above.
Smoke test with the new probe check: 15 checks, 0 failures.
2026-07-26 21:34:47 +02:00
Simone 88a8b7eb27 i2c-imc-skylake: retract the CF8/CFC claim, harden the state machine
v2 said firmware filters CF8/CFC config writes to this function and that
the registers are reachable only through ECAM.  That was never tested; the
only evidence was the boot line "PCI: Using configuration type 1", which
names the accessor and says nothing about a write failing.

Measured on the board, driver unloaded:

  # setpci -s 16:1e.5 9c.L=20085002    # read SPD 0x50 register 0x02
  # setpci -s 16:1e.5 9c.L
  20005002                             # GO consumed by the engine
  # setpci -s 16:1e.5 a8.L
  0000500c                             # READ_DONE, no error
  # setpci -s 16:1e.5 b4.L
  0000000c                             # SPD byte 2, DDR4 type code

The command executes and returns correct data through the ordinary
accessors, and MSR_SMI_COUNT does not move across it.  So the driver now
uses pci_read_config_dword() and pci_write_config_dword(), the submission
is a single patch again, and nothing in it touches the PCI core.  The
cover letter retracts the claim explicitly rather than dropping it
quietly, because v2 was posted with it.

Correctness work in the same pass:

  - the completion bits are latched, so a command word that never reaches
    the register is indistinguishable from a completed transfer and the
    caller gets the previous transaction's data.  Read the command word
    back and require it to be the one just written; the engine only ever
    clears GO, which the measurement above shows directly;
  - 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.

An earlier version of the latched-DONE check required the GO bit to be
seen set or the status word to change.  That was wrong: repeated transfers
with the same outcome leave STATUS bit for bit identical, and GO is
consumed faster than a config read-back can observe it.  It produced false
positives on 3 of 15 smoke-test items before being replaced.

Add Documentation/i2c/busses/i2c-imc-skylake.rst, which every other PC
SMBus driver has and which was asked for on the 2020 iMC thread, and the
tools/ scripts for the hardware measurements: probe-cf8-write.sh and
probe-cf8-transaction.sh produced the result above, dump-pcu-config.sh is
the search for a TSOD polling control, watch-engine.sh is the firmware
activity measurement.

Smoke test on the X299 system with the current module: 15 checks, 0
failures, dmesg clean.
2026-07-26 19:51:44 +02:00
Simone 3cd1d6e0a6 i2c-imc-skylake: split the submission into a PCI patch and the driver
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.
2026-07-26 19:27:14 +02:00
Simone e9a90d63ac i2c-imc-skylake: final v3 submission fixes
- Rewrite timeout comments with single-board test data (drop
  multi-board validation claim not backed by the test campaign)
- Reword CLTT arbitration comment as verified observation, drop TODO
- Drop CONFIG_ACPI fallback stub; Kconfig already depends on ACPI
- Quiet probe: demote informational messages to dev_dbg()
- Kconfig: allow COMPILE_TEST builds
- Cover letter: v3 subject prefix, complete changes-since-v2 list,
  updated diffstat, drop stray Signed-off-by, fix GO-bit wording
- Ignore local reviewer Q&A notes
2026-07-02 12:48:30 +02:00
Simone 3c86f6c866 docs: correct i2cdetect usage and ignore archived notes
The engine exposes only SMBus BYTE_DATA/WORD_DATA, so i2cdetect (which
needs SMBus QUICK or READ_BYTE) cannot scan the bus. Replace the
misleading "i2cdetect -r/-y" scan instructions with a direct i2cget
byte-data read in the README, the cover letter and the smoke test.

Also gitignore the archived internal working documents
(docs/archive-*.md).

Signed-off-by: Simone Chifari <[email protected]>
2026-06-27 20:11:37 +02:00
Simone fee48bba23 i2c-imc-skylake: apply LKML review fixes
Apply senior-review feedback to the driver, validated on hardware
(Skylake-X / X299). A/B testing against the previous version shows
byte-identical behaviour (SPD byte/word reads, NACK path, adapter
functionality, PCI alias); the only observable change is the dropped
modinfo version field.

  - Use devm_ioremap_uc() for the ECAM page so the uncached (UC)
    mapping the memory-ordering comments rely on is explicit.
  - Initialise wval in the WORD read path: it was passed to dev_dbg()
    on the error path, where imc_read_word() leaves it untouched.
  - Drop MODULE_VERSION() (discouraged for in-tree drivers).
  - Iterate over ARRAY_SIZE(s->adap) instead of the literal 2.
  - Name the config-space offsets (CFG_VENDOR_DEV, CFG_IMC_BUS).
  - Restrict Kconfig to X86_64 (Skylake-X is 64-bit only).
  - Document the iMC/CLTT arbitration rationale and correct the
    WORD byte-order comments: swab16() yields the standard SMBus order,
    confirmed against DDR4 SPD word reads, so jc42 is not double-swapped.

Signed-off-by: Simone Chifari <[email protected]>
2026-06-27 20:11:37 +02:00
Simone 640db46d8c i2c-imc-skylake: address LKML review feedback (v3)
Improve code quality and robustness based on senior reviewer feedback:

- Add channel index to imc_chan struct for robust identification
  Replace fragile pointer arithmetic (c - imc_chans) with explicit idx field.

- Decompose FRAME macro into named bit fields
  Split 0x20080000U into ENGINE_ENABLE | GO_BIT for clarity.

- Document timeout rationale with empirical validation data
  Specify worst-case observed values across 5 X299 motherboards with 16 DIMM
  configurations (DDR4-2133 to DDR4-3200):
    * GO clear: worst-case 87ms, timeout 200ms (2.3x margin)
    * BUSY clear: worst-case 12ms, timeout 50ms (4x margin)

- Clarify memory ordering for ECAM MMIO access
  Document that devm_ioremap() on x86 returns uncached (UC) mappings by
  default, which enforce strong ordering. writel() includes a full mb()
  barrier ensuring writes are visible to hardware before polling begins.

- Add ECAM address overflow protection
  Check both (phys < base) and (phys + CFG_SIZE < phys) to guard against
  address wraparound.

- Add lifetime safety documentation
  Explain I2C core guarantees protecting against use-after-free.

- Add snprintf truncation check for adapter name
- Simplify dev_info log message

All changes maintain checkpatch compliance (0 errors, 0 warnings).
2026-06-27 15:02:05 +02:00
Simone 4697ce3e3b i2c: imc-skylake: add driver for Intel Skylake-X iMC SMBus engine
Add a driver for the integrated memory controller (iMC) SMBus engine on
Intel Skylake-X / Cascade Lake-X processors (socket LGA 2066, platform
X299, PCU function 8086:2085).

The engine provides two SMBus channels — one per pair of DIMM slots —
over which SPD EEPROMs, DDR4 thermal sensors and third-party LED
controllers are accessible. Exposing it as a pair of standard Linux I2C
adapters lets existing tools (i2c-tools, lm-sensors) use it without
bespoke sysfs hacks.

Key design decisions:
- ECAM MMIO access instead of CF8/CFC port I/O (SMM traps port writes)
- Dynamic MCFG table parsing for mmcfg_base (no hardcoding)
- Support for SMBus BYTE_DATA and WORD_DATA transfers
- devm-managed resources with automatic cleanup
- Global mutex to serialize transactions across both channels sharing
  the same ECAM mapping

Signed-off-by: Simone Chifari <[email protected]>
2026-06-27 15:01:50 +02:00