Merge branch 'ecam-accessors' into guard
ECAM MMIO accessors (use_ecam, default on): remove the CF8/CFC SMI window, suspected poison channel of the ws1 poweroff hang.
This commit is contained in:
+96
-3
@@ -44,6 +44,22 @@
|
|||||||
* /sys/module/i2c_imc_skylake/parameters/protect_spd for deliberate
|
* /sys/module/i2c_imc_skylake/parameters/protect_spd for deliberate
|
||||||
* SPD maintenance.
|
* SPD maintenance.
|
||||||
*
|
*
|
||||||
|
* - use_ecam (default on): reach the engine registers through a private
|
||||||
|
* ECAM (MMCONFIG) mapping instead of the kernel's legacy config
|
||||||
|
* cycles. For standard config space the kernel uses conf1: two I/O
|
||||||
|
* instructions, index on 0xCF8 and data on 0xCFC. pci_config_lock
|
||||||
|
* serialises that pair against the rest of the kernel but not against
|
||||||
|
* SMM: an SMI landing between the two, whose handler runs its own
|
||||||
|
* CF8/CFC dialogue without restoring the index, silently redirects
|
||||||
|
* the data phase to whatever register the firmware addressed last.
|
||||||
|
* A misdirected read only perturbs one poll iteration and the CMD
|
||||||
|
* write is covered by the landing check below, but the DATA-register
|
||||||
|
* write has no readback, so its payload would land unnoticed in a
|
||||||
|
* foreign config register. A single MMIO access cannot be split by
|
||||||
|
* an SMI, so the window does not exist on the ECAM path. Suspected
|
||||||
|
* (not proven) trigger of the ws1 poweroff hang. Falls back to
|
||||||
|
* config cycles when MCFG has no entry for the device.
|
||||||
|
*
|
||||||
* Bus arbitration / concurrency with firmware:
|
* Bus arbitration / concurrency with firmware:
|
||||||
* The driver cannot coordinate with SMM, a BMC, or the iMC's own closed-loop
|
* The driver cannot coordinate with SMM, a BMC, or the iMC's own closed-loop
|
||||||
* thermal throttling (CLTT), and the PCI ID alone does not prove that none of
|
* thermal throttling (CLTT), and the PCI ID alone does not prove that none of
|
||||||
@@ -63,6 +79,7 @@
|
|||||||
* to be arbitrated against.
|
* to be arbitrated against.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/acpi.h>
|
||||||
#include <linux/bits.h>
|
#include <linux/bits.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
@@ -183,6 +200,16 @@ module_param(protect_spd, bool, 0644);
|
|||||||
MODULE_PARM_DESC(protect_spd,
|
MODULE_PARM_DESC(protect_spd,
|
||||||
"reject SMBus writes to SPD addresses 0x30-0x35 and 0x50-0x57 (default on)");
|
"reject SMBus writes to SPD addresses 0x30-0x35 and 0x50-0x57 (default on)");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Load-time only: the accessors pick their path from s->ecam, captured at
|
||||||
|
* probe. Flipping this at runtime would race in-flight transfers for no
|
||||||
|
* benefit; the A/B lever is a reboot away, like the pre-ECAM baseline.
|
||||||
|
*/
|
||||||
|
static bool use_ecam = true;
|
||||||
|
module_param(use_ecam, bool, 0444);
|
||||||
|
MODULE_PARM_DESC(use_ecam,
|
||||||
|
"reach the engine via an ECAM mapping instead of CF8/CFC config cycles (default on)");
|
||||||
|
|
||||||
struct imc_chan {
|
struct imc_chan {
|
||||||
unsigned int cmd, data, stat;
|
unsigned int cmd, data, stat;
|
||||||
int idx; /* channel index 0 or 1 */
|
int idx; /* channel index 0 or 1 */
|
||||||
@@ -196,6 +223,8 @@ static const struct imc_chan imc_chans[2] = {
|
|||||||
/* one driver state object, shared by both per-channel adapters */
|
/* one driver state object, shared by both per-channel adapters */
|
||||||
struct imc_smbus {
|
struct imc_smbus {
|
||||||
struct pci_dev *pdev; /* config space carrying the engine */
|
struct pci_dev *pdev; /* config space carrying the engine */
|
||||||
|
void __iomem *ecam; /* this function's config page, or NULL
|
||||||
|
to fall back to config cycles */
|
||||||
struct device *dev; /* &pdev->dev, for dev_*() logging */
|
struct device *dev; /* &pdev->dev, for dev_*() logging */
|
||||||
struct mutex lock; /* serialises all SMBus transactions */
|
struct mutex lock; /* serialises all SMBus transactions */
|
||||||
/* needed: both channels share one engine */
|
/* needed: both channels share one engine */
|
||||||
@@ -211,9 +240,52 @@ struct imc_xfer_state {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Register accessors. A config-space failure is recorded in s->io_err and
|
* Locate this function's configuration page in the ECAM window described by
|
||||||
* reported once, at the end of the transfer, so the transaction logic does not
|
* the ACPI MCFG table and map it. The arch code keeps its own MCFG mapping
|
||||||
* have to check a return value on every register touch. A failed read yields
|
* for extended config space but exposes no way to route standard config
|
||||||
|
* space through it, hence the private lookup. Mapping the page a second
|
||||||
|
* time is harmless: it is device MMIO, not RAM.
|
||||||
|
*/
|
||||||
|
static void __iomem *imc_map_ecam(struct pci_dev *pdev)
|
||||||
|
{
|
||||||
|
struct acpi_table_mcfg *mcfg;
|
||||||
|
struct acpi_mcfg_allocation *alloc;
|
||||||
|
void __iomem *map = NULL;
|
||||||
|
unsigned int bus = pdev->bus->number;
|
||||||
|
int i, n;
|
||||||
|
|
||||||
|
if (ACPI_FAILURE(acpi_get_table(ACPI_SIG_MCFG, 0,
|
||||||
|
(struct acpi_table_header **)&mcfg)))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
n = (mcfg->header.length - sizeof(*mcfg)) / sizeof(*alloc);
|
||||||
|
alloc = (struct acpi_mcfg_allocation *)(mcfg + 1);
|
||||||
|
for (i = 0; i < n; i++, alloc++) {
|
||||||
|
resource_size_t addr;
|
||||||
|
|
||||||
|
if (alloc->pci_segment != pci_domain_nr(pdev->bus) ||
|
||||||
|
bus < alloc->start_bus_number ||
|
||||||
|
bus > alloc->end_bus_number)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
addr = alloc->address +
|
||||||
|
((resource_size_t)(bus - alloc->start_bus_number) << 20) +
|
||||||
|
((resource_size_t)pdev->devfn << 12);
|
||||||
|
map = devm_ioremap(&pdev->dev, addr, SZ_4K);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
acpi_put_table(&mcfg->header);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Register accessors. On the ECAM path a register touch is a single MMIO
|
||||||
|
* access; it has no failure channel, and the function sits on the CPU die,
|
||||||
|
* so it cannot vanish and produce the all-ones of a master abort. On the
|
||||||
|
* config-cycle fallback a failure is recorded in s->io_err and reported
|
||||||
|
* once, at the end of the transfer, so the transaction logic does not have
|
||||||
|
* to check a return value on every register touch. A failed read yields
|
||||||
* all-ones, which no polling loop mistakes for a completed transaction.
|
* all-ones, which no polling loop mistakes for a completed transaction.
|
||||||
*/
|
*/
|
||||||
static u32 imc_reg(struct imc_smbus *s, unsigned int off)
|
static u32 imc_reg(struct imc_smbus *s, unsigned int off)
|
||||||
@@ -221,6 +293,9 @@ static u32 imc_reg(struct imc_smbus *s, unsigned int off)
|
|||||||
u32 val;
|
u32 val;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
if (s->ecam)
|
||||||
|
return readl(s->ecam + off);
|
||||||
|
|
||||||
ret = pci_read_config_dword(s->pdev, off, &val);
|
ret = pci_read_config_dword(s->pdev, off, &val);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
s->io_err = pcibios_err_to_errno(ret);
|
s->io_err = pcibios_err_to_errno(ret);
|
||||||
@@ -234,6 +309,11 @@ static void imc_set(struct imc_smbus *s, unsigned int off, u32 val)
|
|||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
if (s->ecam) {
|
||||||
|
writel(val, s->ecam + off);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ret = pci_write_config_dword(s->pdev, off, val);
|
ret = pci_write_config_dword(s->pdev, off, val);
|
||||||
if (ret)
|
if (ret)
|
||||||
s->io_err = pcibios_err_to_errno(ret);
|
s->io_err = pcibios_err_to_errno(ret);
|
||||||
@@ -837,6 +917,19 @@ static int imc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Before the first register touch, so that even the pristine-word
|
||||||
|
* snapshot below goes through the SMI-safe path.
|
||||||
|
*/
|
||||||
|
if (use_ecam) {
|
||||||
|
s->ecam = imc_map_ecam(pdev);
|
||||||
|
if (!s->ecam)
|
||||||
|
dev_warn(&pdev->dev,
|
||||||
|
"no usable MCFG entry; falling back to CF8/CFC config cycles\n");
|
||||||
|
}
|
||||||
|
dev_dbg(&pdev->dev, "engine access via %s\n",
|
||||||
|
s->ecam ? "ECAM MMIO" : "CF8/CFC config cycles");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Pristine command words: the state the firmware left in the engine at
|
* Pristine command words: the state the firmware left in the engine at
|
||||||
* boot, captured before this driver's first transfer. They are handed
|
* boot, captured before this driver's first transfer. They are handed
|
||||||
|
|||||||
Reference in New Issue
Block a user