Files
i2c-imc-skylake/i2c-imc-skylake.c
Simone 6bf5879a9f i2c-imc-skylake: add SMBus QUICK/BYTE emulation for compatibility
Add support for I2C_SMBUS_QUICK and I2C_SMBUS_BYTE transfers to maintain
compatibility with older userspace tools (e.g., legacy i2cdetect versions).

- I2C_SMBUS_QUICK: Emulated as read-byte from offset 0, allowing device
  probing without sending data.
- I2C_SMBUS_BYTE: For reads, the command byte becomes the register offset;
  for writes, the command byte is the value written to offset 0.

This preserves the LKML v3 corrections (channel index, FRAME decomposition,
timeout documentation, ECAM overflow checks) while adding backward
compatibility for tools that expect QUICK/BYTE support.

Note: This functionality is only in the compat branch and will not be
submitted upstream, as the hardware requires a register offset for every
transaction.
2026-07-02 12:49:37 +02:00

581 lines
18 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Intel Skylake-X iMC SMBus I2C adapter.
*
* The integrated memory controller (iMC) on Intel Skylake-X / Cascade Lake-X
* processors exposes an SMBus engine used to reach the SPD EEPROMs and thermal
* sensors on DDR4 DIMMs. The engine is driven through the PCI configuration
* space of the Sky Lake-E PCU function (0000:16:1e.5, 8086:2085). This driver
* presents that engine as two standard Linux I2C adapters - one per hardware
* SMBus channel - so that i2c-tools and lm-sensors can use it without bespoke
* sysfs hacks.
*
* Why ECAM MMIO instead of the usual CF8/CFC config accessors:
* On this platform System Management Mode (SMM) traps and mangles port-based
* (CF8/CFC) writes to the first 256 config bytes of this function, so the SMBus
* command never completes. The boot log reports "PCI: Using configuration
* type 1 (probe)" confirming the default path is port-based; the MMCONFIG
* (ECAM) window is not trapped. Windows' NTIOLib reaches the registers via
* ECAM, which is how we confirmed the register layout on hardware. We map the
* ECAM page of the target function and drive the registers by MMIO, exactly as
* the firmware does.
*
* ECAM phys(off) = mmcfg_base + (bus<<20) + (dev<<15) + (fn<<12) + off
* mmcfg_base is read from the ACPI MCFG table at probe time (not hardcoded).
*
* Per-channel register triple within the config page:
* ch0 ch1
* CTRL (data) 0xB4 0xB8 write: data byte in bits[23:16]; read: low byte
* DATA (cmd) 0x9C 0xA0 FRAME | (cmd << 8) | reg ; bit19 = GO
* STATUS 0xA8 0xAC busy while bit0 set; done when clear,
* bit1 (0x02) set on completion = device NACKed
* SMBus command byte (DATA bits[15:8]) = (rw << 7) | addr7: the 7-bit slave
* address with bit7 = direction (1 write / 0 read). The register/offset goes
* in DATA[7:0]. So 0x50 reads SPD EEPROM 0x50. This was decoded and
* confirmed on hardware.
*
* The two channels are independent SMBus buses (DIMMs 1,2 on ch0; DIMMs 3,4 on
* ch1), hence two i2c_adapter instances exposed as separate /dev/i2c-* nodes.
* Each channel carries the DIMM SPD EEPROMs (0x50-0x57) and thermal sensors -
* all reachable by 7-bit address.
*
* Bus arbitration / concurrency with firmware:
* The engine could in principle also be driven by SMM or by the iMC's own
* closed-loop thermal throttling (CLTT) TSOD polling. On the X299 HEDT
* platform there is no BMC and CLTT firmware polling is not active, so the
* quiesce handshake required on server parts (Sandy Bridge-EP, Broadwell-E)
* is not needed here. On the tested hardware the engine is idle at probe
* and no firmware-initiated transaction has ever been observed between
* driver transactions. The global mutex still serialises the two channels
* against each other, as they share a single engine.
*/
#include <linux/acpi.h>
#include <linux/bits.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
/* expected PCI id of the Sky Lake-E PCU SMBus function */
#define PCU_DEVICE 0x2085
#define PCU_ID ((PCU_DEVICE << 16) | PCI_VENDOR_ID_INTEL)
#define CFG_SIZE 0x1000UL
/* function-global config registers (offsets within the config page) */
#define CFG_VENDOR_DEV 0x00 /* cfg[0]: vendor/device id, for ECAM sanity check */
#define CFG_IMC_BUS 0xCC /* cfg[0xCC] bits[15:8]: iMC SMBus bus number */
/* per-channel register offsets within the config page */
#define CH0_CTRL 0xB4
#define CH0_DATA 0x9C
#define CH0_STAT 0xA8
#define CH1_CTRL 0xB8
#define CH1_DATA 0xA0
#define CH1_STAT 0xAC
/*
* Command word written to the DATA register:
* bits[31:16] = FRAME (engine config + GO bit19), constant
* bits[15:8] = SMBus command byte = (rw << 7) | addr7
* rw bit (0x80): 1 = write, 0 = read
* addr7 : 7-bit SMBus slave address
* bits[7:0] = register/offset within the addressed device
* For a write the data byte is latched into CTRL[23:16] beforehand. This
* encoding was confirmed on hardware: command 0x50 reads SPD EEPROM 0x50
* (DDR4 signature).
*/
#define ENGINE_ENABLE BIT(29) /* engine enable bit */
#define GO_BIT BIT(19) /* start transaction */
#define FRAME (ENGINE_ENABLE | GO_BIT)
#define RW_WRITE 0x80 /* OR into the command byte for a write */
#define WORD_BIT BIT(17) /* 16-bit word transfer (vs 8-bit byte) */
#define STAT_BUSY BIT(0) /* low bit set while transaction in flight */
#define STAT_NACK BIT(1) /* set on completion if the device NACKed */
struct imc_chan {
u32 ctrl, data, stat;
int idx; /* channel index 0 or 1 */
};
static const struct imc_chan imc_chans[2] = {
{ CH0_CTRL, CH0_DATA, CH0_STAT, 0 },
{ CH1_CTRL, CH1_DATA, CH1_STAT, 1 },
};
/* one driver state object, shared by both per-channel adapters */
struct imc_smbus {
struct device *dev; /* &pdev->dev, for dev_*() logging */
void __iomem *cfg; /* ioremapped ECAM page of the function */
struct mutex lock; /* serialises all SMBus transactions */
/* needed: both channels share same ECAM mapping */
struct i2c_adapter adap[2]; /* one per hardware channel */
};
/*
* ECAM base discovery from ACPI MCFG. acpi_table_parse() is not exported to
* modules, so map the MCFG table with acpi_get_table() (exported) and walk the
* allocation entries by hand. Uses pdev's PCI segment and bus number so no
* module parameter override is needed. CONFIG_ACPI is guaranteed by Kconfig.
*/
static u64 imc_detect_mmcfg_base(struct pci_dev *pdev)
{
unsigned int seg = pci_domain_nr(pdev->bus);
unsigned int bus = pdev->bus->number;
struct acpi_table_header *hdr;
struct acpi_table_mcfg *mcfg;
struct acpi_mcfg_allocation *e;
unsigned long n, i;
u64 base = 0;
if (ACPI_FAILURE(acpi_get_table(ACPI_SIG_MCFG, 0, &hdr)))
return 0;
mcfg = (struct acpi_table_mcfg *)hdr;
if (hdr->length < sizeof(*mcfg)) {
acpi_put_table(hdr);
return 0;
}
e = (struct acpi_mcfg_allocation *)(mcfg + 1);
n = (hdr->length - sizeof(*mcfg)) / sizeof(*e);
for (i = 0; i < n; i++) {
if (e[i].pci_segment == seg &&
bus >= e[i].start_bus_number &&
bus <= e[i].end_bus_number) {
base = e[i].address;
break;
}
}
acpi_put_table(hdr);
return base;
}
/*
* Wait until GO clears (transaction issued), then until the busy bit drops.
* Returns 0 on completion, -ETIMEDOUT if the engine never went idle. On
* success *status (if non-NULL) gets the final status word. Process context
* only - it sleeps between polls.
*
* On the test system (Skylake-X, 4 DDR4 DIMMs) transactions complete in a few
* milliseconds; the 200ms (GO clear) and 50ms (BUSY clear) timeouts leave a
* generous margin for SMM interference and SMBus clock stretching.
*/
static int imc_wait(struct imc_smbus *s, const struct imc_chan *c, u32 *status)
{
u32 val;
int ret;
ret = readl_poll_timeout(s->cfg + c->data, val, !(val & GO_BIT), 10, 200000);
if (ret)
return ret;
ret = readl_poll_timeout(s->cfg + c->stat, val, !(val & STAT_BUSY), 10, 50000);
if (ret)
return ret;
if (status)
*status = val;
return 0;
}
/*
* Poll the busy bit clear only (no GO check). The firmware polls STATUS after
* the CTRL (data-latch) write too, before issuing the DATA/GO word.
* Timeout: 50ms, same rationale as in imc_wait().
*
* Note: devm_ioremap_uc() returns an uncached (UC) mapping, which enforces
* strong ordering. writel() includes a full mb() barrier, ensuring the write
* is visible to hardware before polling begins.
*/
static int imc_wait_status(struct imc_smbus *s, const struct imc_chan *c)
{
u32 val;
int ret;
ret = readl_poll_timeout(s->cfg + c->stat, val, !(val & STAT_BUSY), 10, 50000);
if (ret) {
dev_dbg(s->dev, "pre-command poll timed out, engine still busy\n");
return ret;
}
return 0;
}
/* translate a completed transaction's status into an errno (0 = ACK) */
static int imc_check_status(struct imc_smbus *s, u32 status, u8 addr, u8 reg)
{
if (status & STAT_NACK) {
/* no device at this address, or it refused the access */
dev_dbg(s->dev, "addr 0x%02x reg 0x%02x NACK (stat 0x%08x)\n",
addr, reg, status);
return -ENXIO;
}
return 0;
}
/* SMBus write-byte to addr: latch the data byte, then issue the command. */
static int imc_write_byte(struct imc_smbus *s, const struct imc_chan *c,
u8 addr, u8 reg, u8 val)
{
u32 status = 0;
int ret;
writel((u32)val << 16, s->cfg + c->ctrl);
ret = imc_wait_status(s, c);
if (ret)
return ret;
writel(FRAME | ((u32)(addr | RW_WRITE) << 8) | reg, s->cfg + c->data);
ret = imc_wait(s, c, &status);
if (ret) {
dev_warn_ratelimited(s->dev,
"write addr 0x%02x reg 0x%02x timed out\n",
addr, reg);
return ret;
}
ret = imc_check_status(s, status, addr, reg);
if (ret)
return ret;
return 0;
}
/* SMBus read-byte from addr: issue the command, return the low data byte. */
static int imc_read_byte(struct imc_smbus *s, const struct imc_chan *c,
u8 addr, u8 reg, u8 *val)
{
u32 status = 0;
int ret;
ret = imc_wait_status(s, c);
if (ret)
return ret;
writel(FRAME | ((u32)addr << 8) | reg, s->cfg + c->data);
ret = imc_wait(s, c, &status);
if (ret) {
dev_warn_ratelimited(s->dev,
"read addr 0x%02x reg 0x%02x timed out\n",
addr, reg);
return ret;
}
ret = imc_check_status(s, status, addr, reg);
if (ret)
return ret;
*val = readl(s->cfg + c->ctrl) & 0xFF;
return 0;
}
/*
* SMBus write-word to addr: latch the 16-bit value into CTRL[31:16], then
* issue the command with WORD_BIT set. The value is byte-swapped between host
* order and the engine's CTRL byte layout (swab16) so that data->word follows
* the standard SMBus convention (low data byte first). The read path applies
* the inverse swap.
*
* Validated on hardware: a WORD read returns the same byte order as two
* consecutive BYTE_DATA reads (word low byte == register R, high byte == R+1),
* confirmed against DDR4 SPD bytes. This is exactly the convention jc42 relies
* on via i2c_smbus_read_word_swapped(), so no double swap occurs.
*/
static int imc_write_word(struct imc_smbus *s, const struct imc_chan *c,
u8 addr, u8 reg, u16 val)
{
u32 status = 0;
int ret;
/* host order -> engine CTRL byte order (see function comment) */
writel((u32)swab16(val) << 16, s->cfg + c->ctrl);
ret = imc_wait_status(s, c);
if (ret)
return ret;
writel(FRAME | WORD_BIT | ((u32)(addr | RW_WRITE) << 8) | reg,
s->cfg + c->data);
ret = imc_wait(s, c, &status);
if (ret) {
dev_warn_ratelimited(s->dev,
"write word addr 0x%02x reg 0x%02x timed out\n",
addr, reg);
return ret;
}
return imc_check_status(s, status, addr, reg);
}
/*
* SMBus read-word from addr: issue the command with WORD_BIT set, return the
* 16-bit value from CTRL[15:0] byte-swapped back to host order (inverse of the
* write path). See imc_write_word() for the byte-order convention.
*/
static int imc_read_word(struct imc_smbus *s, const struct imc_chan *c,
u8 addr, u8 reg, u16 *val)
{
u32 status = 0, raw;
int ret;
ret = imc_wait_status(s, c);
if (ret)
return ret;
writel(FRAME | WORD_BIT | ((u32)addr << 8) | reg, s->cfg + c->data);
ret = imc_wait(s, c, &status);
if (ret) {
dev_warn_ratelimited(s->dev,
"read word addr 0x%02x reg 0x%02x timed out\n",
addr, reg);
return ret;
}
ret = imc_check_status(s, status, addr, reg);
if (ret)
return ret;
raw = readl(s->cfg + c->ctrl) & 0xFFFF;
*val = swab16(raw);
return 0;
}
/*
* Standard SMBus transfer callback. The per-channel imc_chan is stashed in the
* adapter's algo_data. The command word carries the 7-bit address, so
* SPD EEPROMs (0x50-0x57) are reachable on each channel. BYTE_DATA and
* WORD_DATA are supported; larger transfers would need the engine's block
* primitives, which are not used by the devices on this bus.
*/
static s32 imc_smbus_xfer(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write, u8 command,
int size, union i2c_smbus_data *data)
{
struct imc_smbus *s = i2c_get_adapdata(adap);
const struct imc_chan *c = adap->algo_data;
u8 reg, val = 0;
int ret;
if (addr > 0x7f)
return -EINVAL;
/*
* Emulate I2C_SMBUS_QUICK as a read-byte from offset 0.
* This allows i2cdetect to probe devices without sending data.
*/
if (size == I2C_SMBUS_QUICK) {
mutex_lock(&s->lock);
ret = imc_read_byte(s, c, addr, 0, &val);
mutex_unlock(&s->lock);
return ret;
}
if (size != I2C_SMBUS_BYTE && size != I2C_SMBUS_BYTE_DATA &&
size != I2C_SMBUS_WORD_DATA)
return -EOPNOTSUPP;
/*
* Every transaction on this engine carries a register/offset byte. For
* BYTE_DATA the SMBus "command" is that register; for plain BYTE there
* is no register phase, so offset 0 is used (read = receive-byte;
* write = the command byte is the value written to offset 0).
*/
switch (size) {
case I2C_SMBUS_BYTE:
reg = (read_write == I2C_SMBUS_WRITE) ? 0 : command;
break;
case I2C_SMBUS_BYTE_DATA:
case I2C_SMBUS_WORD_DATA:
reg = command;
break;
default:
return -EOPNOTSUPP;
}
mutex_lock(&s->lock);
if (read_write == I2C_SMBUS_WRITE) {
if (size == I2C_SMBUS_WORD_DATA) {
dev_dbg(s->dev, "ch%d W addr=%02x reg=%02x val=%04x\n",
c->idx, addr, reg, data->word);
ret = imc_write_word(s, c, addr, reg, data->word);
} else {
val = (size == I2C_SMBUS_BYTE) ? command : data->byte;
dev_dbg(s->dev, "ch%d W addr=%02x reg=%02x val=%02x\n",
c->idx, addr, reg, val);
ret = imc_write_byte(s, c, addr, reg, val);
}
} else {
if (size == I2C_SMBUS_WORD_DATA) {
u16 wval = 0;
ret = imc_read_word(s, c, addr, reg, &wval);
if (!ret)
data->word = wval;
dev_dbg(s->dev, "ch%d R addr=%02x reg=%02x -> %04x (ret %d)\n",
c->idx, addr, reg, wval, ret);
} else {
ret = imc_read_byte(s, c, addr, reg, &val);
if (!ret)
data->byte = val;
dev_dbg(s->dev, "ch%d R addr=%02x reg=%02x -> %02x (ret %d)\n",
c->idx, addr, reg, val, ret);
}
}
mutex_unlock(&s->lock);
return ret;
}
static u32 imc_func(struct i2c_adapter *adap)
{
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA;
}
static const struct i2c_algorithm imc_algo = {
.smbus_xfer = imc_smbus_xfer,
.functionality = imc_func,
};
static void imc_mutex_destroy(void *data)
{
struct mutex *lock = data;
mutex_destroy(lock);
}
static int imc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
resource_size_t phys;
struct imc_smbus *s;
u8 imc_bus_hw;
u64 base;
u32 cfg0, cc;
int ret, i;
s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL);
if (!s)
return -ENOMEM;
ret = pcim_enable_device(pdev);
if (ret) {
dev_err(&pdev->dev, "cannot enable PCI device: %d\n", ret);
return ret;
}
s->dev = &pdev->dev;
mutex_init(&s->lock);
ret = devm_add_action_or_reset(&pdev->dev, imc_mutex_destroy, &s->lock);
if (ret)
return ret;
base = imc_detect_mmcfg_base(pdev);
if (!base) {
dev_err(&pdev->dev, "cannot resolve MMCONFIG base\n");
return -ENODEV;
}
phys = base +
((resource_size_t)pdev->bus->number << 20) +
((resource_size_t)PCI_SLOT(pdev->devfn) << 15) +
((resource_size_t)PCI_FUNC(pdev->devfn) << 12);
if (phys < base || phys + CFG_SIZE < phys) {
dev_err(&pdev->dev, "ECAM address overflow\n");
return -EINVAL;
}
/*
* Deliberately no request_mem_region(): the MMCONFIG window is already
* claimed as a firmware/PCI resource, so a reservation would fail with
* -EBUSY. The pci_driver binding keeps the function alive; the registers
* we drive are side-band controls the kernel does not otherwise touch.
*/
s->cfg = devm_ioremap_uc(&pdev->dev, phys, CFG_SIZE);
if (!s->cfg) {
dev_err(&pdev->dev, "ioremap(%pa) failed\n", &phys);
return -ENOMEM;
}
cfg0 = readl(s->cfg + CFG_VENDOR_DEV);
if (cfg0 != PCU_ID) {
dev_err(&pdev->dev, "wrong device at ECAM %pa (cfg[0]=0x%08x)\n",
&phys, cfg0);
return -ENODEV;
}
/*
* Cross-check the iMC bus number against the configuration register
* value: cfg[0xCC] bits[15:8] = the iMC SMBus bus number as seen by
* the PCU. On all known Skylake-X / Cascade Lake-X boards this matches
* the probed bus number.
* A mismatch means the ECAM walk landed on the wrong slot — warn but
* continue; the binding is already locked to 8086:2085.
*/
cc = readl(s->cfg + CFG_IMC_BUS);
imc_bus_hw = (cc >> 8) & 0xFF;
if (imc_bus_hw && imc_bus_hw != (u8)pdev->bus->number)
dev_warn(&pdev->dev,
"cfg[0xCC] reports iMC bus 0x%02x but probed bus=0x%02x\n",
imc_bus_hw, (u8)pdev->bus->number);
else
dev_dbg(&pdev->dev,
"cfg[0xCC]=0x%08x iMC bus 0x%02x confirmed\n",
cc, imc_bus_hw);
dev_dbg(&pdev->dev, "ECAM mapped at %pa\n", &phys);
/*
* Lifetime safety: the I2C core guarantees that smbus_xfer callbacks
* are not invoked after i2c_del_adapter() returns. Since we use
* devm_i2c_add_adapter(), the adapter is automatically removed on
* driver detach, and no concurrent xfer can be in flight at that point.
*/
for (i = 0; i < ARRAY_SIZE(s->adap); i++) {
struct i2c_adapter *a = &s->adap[i];
int n;
a->owner = THIS_MODULE;
a->algo = &imc_algo;
a->algo_data = (void *)&imc_chans[i];
a->dev.parent = &pdev->dev;
i2c_set_adapdata(a, s);
n = snprintf(a->name, sizeof(a->name),
"iMC SMBus Skylake-X channel %d", i);
if (n >= sizeof(a->name))
dev_warn(&pdev->dev, "adapter name truncated\n");
ret = devm_i2c_add_adapter(&pdev->dev, a);
if (ret) {
dev_err(&pdev->dev,
"i2c_add_adapter ch%d failed: %d\n", i, ret);
return ret;
}
}
dev_dbg(&pdev->dev, "registered 2 SMBus channels\n");
return 0;
}
static const struct pci_device_id imc_pci_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCU_DEVICE) },
{ }
};
MODULE_DEVICE_TABLE(pci, imc_pci_ids);
/*
* All resources (ioremap, i2c adapters, mutex) are devm-managed and are
* released automatically after probe returns or during device unbinding, so
* no .remove callback is needed. The register state is deliberately left
* as-is on unbind: the engine carries no driver-private latched state that
* needs teardown, and the SMBus controls we drive are side-band registers the
* rest of the kernel does not touch.
*/
static struct pci_driver imc_driver = {
.name = "i2c-imc-skylake",
.id_table = imc_pci_ids,
.probe = imc_pci_probe,
};
module_pci_driver(imc_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Simone Chifari");
MODULE_DESCRIPTION("Intel Skylake-X iMC SMBus I2C adapter (ECAM MMIO)");