git: b2a49e88d1c6 - main - intpm: use MMIO for AMD EFCH CPUs

From: Warner Losh <imp_at_FreeBSD.org>
Date: Fri, 24 Jan 2025 21:43:03 UTC
The branch main has been updated by imp:

URL: https://cgit.FreeBSD.org/src/commit/?id=b2a49e88d1c65ee41279b593fe0d50ce4ec31f75

commit b2a49e88d1c65ee41279b593fe0d50ce4ec31f75
Author:     Brian Poole <brian90013@gmail.com>
AuthorDate: 2024-10-07 13:09:35 +0000
Commit:     Warner Losh <imp@FreeBSD.org>
CommitDate: 2025-01-24 21:42:40 +0000

    intpm: use MMIO for AMD EFCH CPUs
    
    Recent AMD CPUs (SMBus PCI revision ID >= 0x51) can disable port-mapped
    IO and only support memory-mapped IO. In practice this was observed on a
    Zen 3 CPU where PMIO reads all returned 0xff. Update the driver to use
    MMIO for these processors while continuing to use PMIO by default.
    
    Reviewed by: imp
    Pull Request: https://github.com/freebsd/freebsd-src/pull/1437
---
 sys/dev/amdsbwd/amd_chipset.h |  2 ++
 sys/dev/intpm/intpm.c         | 44 ++++++++++++++++++++++++++++++++-----------
 2 files changed, 35 insertions(+), 11 deletions(-)

diff --git a/sys/dev/amdsbwd/amd_chipset.h b/sys/dev/amdsbwd/amd_chipset.h
index 1bc637a30845..44db7b3168d0 100644
--- a/sys/dev/amdsbwd/amd_chipset.h
+++ b/sys/dev/amdsbwd/amd_chipset.h
@@ -123,6 +123,7 @@
  */
 #define	AMDFCH41_WDT_FIXED_ADDR		0xfeb00000u
 #define	AMDFCH41_MMIO_ADDR		0xfed80000u
+#define	AMDFCH41_MMIO_PM_OFF		0x0300
 #define AMDFCH41_MMIO_SMBUS_OFF		0x0a00
 #define AMDFCH41_MMIO_WDT_OFF		0x0b00
 
@@ -143,5 +144,6 @@
 #define	AMDFCH41_SMBUS_REVID		0x41
 #define	AMDCZ_SMBUS_DEVID		0x790b1022
 #define	AMDCZ49_SMBUS_REVID		0x49
+#define	AMDCZ51_SMBUS_REVID		0x51
 
 #define	HYGONCZ_SMBUS_DEVID		0x790b1d94
diff --git a/sys/dev/intpm/intpm.c b/sys/dev/intpm/intpm.c
index 3aa76ff48263..2f12e0dcf49b 100644
--- a/sys/dev/intpm/intpm.c
+++ b/sys/dev/intpm/intpm.c
@@ -55,6 +55,7 @@ struct intsmb_softc {
 	int			cfg_irq9;
 	int			sb8xx;
 	int			poll;
+	int			type;
 	struct mtx		lock;
 };
 
@@ -135,28 +136,44 @@ sb8xx_attach(device_t dev)
 	struct resource		*res;
 	uint32_t		devid;
 	uint8_t			revid;
-	uint16_t		addr;
+	uint32_t		addr;
 	int			rid;
 	int			rc;
 	bool			enabled;
 
 	sc = device_get_softc(dev);
+	devid = pci_get_devid(dev);
+	revid = pci_get_revid(dev);
+
+	/*
+	 * Comment from Linux i2c-piix4.c:
+	 *
+	 * cd6h/cd7h port I/O accesses can be disabled on AMD processors
+	 * w/ SMBus PCI revision ID 0x51 or greater. MMIO is supported on
+	 * the same processors and is the recommended access method.
+	 */
+	if (devid == AMDCZ_SMBUS_DEVID && revid >= AMDCZ51_SMBUS_REVID) {
+		sc->type = SYS_RES_MEMORY;
+		addr = AMDFCH41_MMIO_ADDR + AMDFCH41_MMIO_PM_OFF;
+	} else {
+		sc->type = SYS_RES_IOPORT;
+		addr = AMDSB_PMIO_INDEX;
+	}
+
 	rid = 0;
-	rc = bus_set_resource(dev, SYS_RES_IOPORT, rid, AMDSB_PMIO_INDEX,
+	rc = bus_set_resource(dev, sc->type, rid, addr,
 	    AMDSB_PMIO_WIDTH);
 	if (rc != 0) {
 		device_printf(dev, "bus_set_resource for PM IO failed\n");
 		return (ENXIO);
 	}
-	res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid,
+	res = bus_alloc_resource_any(dev, sc->type, &rid,
 	    RF_ACTIVE);
 	if (res == NULL) {
 		device_printf(dev, "bus_alloc_resource for PM IO failed\n");
 		return (ENXIO);
 	}
 
-	devid = pci_get_devid(dev);
-	revid = pci_get_revid(dev);
 	if (devid == AMDSB_SMBUS_DEVID ||
 	    (devid == AMDFCH_SMBUS_DEVID && revid < AMDFCH41_SMBUS_REVID) ||
 	    (devid == AMDCZ_SMBUS_DEVID  && revid < AMDCZ49_SMBUS_REVID)) {
@@ -165,6 +182,10 @@ sb8xx_attach(device_t dev)
 		addr |= amd_pmio_read(res, AMDSB8_PM_SMBUS_EN);
 		enabled = (addr & AMDSB8_SMBUS_EN) != 0;
 		addr &= AMDSB8_SMBUS_ADDR_MASK;
+	} else if (devid == AMDCZ_SMBUS_DEVID && revid >= AMDCZ51_SMBUS_REVID) {
+		addr = bus_read_1(res, AMDFCH41_PM_DECODE_EN0);
+		enabled = (addr & AMDFCH41_SMBUS_EN) != 0;
+		addr = AMDFCH41_MMIO_ADDR + AMDFCH41_MMIO_SMBUS_OFF;
 	} else {
 		addr = amd_pmio_read(res, AMDFCH41_PM_DECODE_EN0);
 		enabled = (addr & AMDFCH41_SMBUS_EN) != 0;
@@ -172,8 +193,8 @@ sb8xx_attach(device_t dev)
 		addr <<= 8;
 	}
 
-	bus_release_resource(dev, SYS_RES_IOPORT, rid, res);
-	bus_delete_resource(dev, SYS_RES_IOPORT, rid);
+	bus_release_resource(dev, sc->type, rid, res);
+	bus_delete_resource(dev, sc->type, rid);
 
 	if (!enabled) {
 		device_printf(dev, "SB8xx/SB9xx/FCH SMBus not enabled\n");
@@ -181,13 +202,13 @@ sb8xx_attach(device_t dev)
 	}
 
 	sc->io_rid = 0;
-	rc = bus_set_resource(dev, SYS_RES_IOPORT, sc->io_rid, addr,
+	rc = bus_set_resource(dev, sc->type, sc->io_rid, addr,
 	    AMDSB_SMBIO_WIDTH);
 	if (rc != 0) {
 		device_printf(dev, "bus_set_resource for SMBus IO failed\n");
 		return (ENXIO);
 	}
-	sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &sc->io_rid,
+	sc->io_res = bus_alloc_resource_any(dev, sc->type, &sc->io_rid,
 	    RF_ACTIVE);
 	if (sc->io_res == NULL) {
 		device_printf(dev, "Could not allocate I/O space\n");
@@ -208,7 +229,7 @@ intsmb_release_resources(device_t dev)
 	if (sc->irq_res)
 		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
 	if (sc->io_res)
-		bus_release_resource(dev, SYS_RES_IOPORT, sc->io_rid,
+		bus_release_resource(dev, sc->type, sc->io_rid,
 		    sc->io_res);
 	mtx_destroy(&sc->lock);
 }
@@ -226,6 +247,7 @@ intsmb_attach(device_t dev)
 	mtx_init(&sc->lock, device_get_nameunit(dev), "intsmb", MTX_DEF);
 
 	sc->cfg_irq9 = 0;
+	sc->type = SYS_RES_IOPORT;
 	switch (pci_get_devid(dev)) {
 #ifndef NO_CHANGE_PCICONF
 	case 0x71138086:	/* Intel 82371AB */
@@ -254,7 +276,7 @@ intsmb_attach(device_t dev)
 	}
 
 	sc->io_rid = PCI_BASE_ADDR_SMB;
-	sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &sc->io_rid,
+	sc->io_res = bus_alloc_resource_any(dev, sc->type, &sc->io_rid,
 	    RF_ACTIVE);
 	if (sc->io_res == NULL) {
 		device_printf(dev, "Could not allocate I/O space\n");