Module Name:    src
Committed By:   martin
Date:           Tue Aug  1 14:06:37 UTC 2023

Modified Files:
        src/sys/arch/amd64/conf [netbsd-10]: GENERIC
        src/sys/arch/x86/pci [netbsd-10]: files.pci ichlpcib.c tco.c tco.h
        src/sys/dev/ic [netbsd-10]: i82801lpcreg.h
        src/sys/dev/pci [netbsd-10]: files.pci ichsmb.c

Log Message:
Pull up following revision(s) (requested by riastradh in ticket #282):

        sys/dev/pci/ichsmb.c: revision 1.82
        sys/arch/amd64/conf/GENERIC: revision 1.602
        sys/arch/x86/pci/tco.c: revision 1.10
        sys/arch/x86/pci/tco.h: revision 1.5
        sys/arch/x86/pci/ichlpcib.c: revision 1.59
        sys/dev/ic/i82801lpcreg.h: revision 1.17
        sys/arch/x86/pci/files.pci: revision 1.27
        sys/dev/pci/files.pci: revision 1.446

ichsmb(4), tco(4): Add support for TCO on newer Intel chipsets.

TCO (`Total Cost of Ownership', Intel's bizarre name for a watchdog
timer) used to hang off the Intel I/O platform controller hub's (ICH)
low-pin-count interface bridge (LPC IB), or ichlpcib(4).  On newer
devices, it hangs off the ICH SMBus instead.
Tested on INTEL 100SERIES_SMB (works) and INTEL 100SERIES_LP_SMB
(doesn't work, still not sure why).

XXX kernel revbump: This breaks the module ABI -- tco(4) modules
older than the change to make ta_has_rcba into ta_version will
incorrectly attach at buses they do not understand.  (However, the
tco(4) driver is statically built into GENERIC, so maybe it's safe
for pullup since the module wouldn't have worked anyway.)


To generate a diff of this commit:
cvs rdiff -u -r1.599 -r1.599.4.1 src/sys/arch/amd64/conf/GENERIC
cvs rdiff -u -r1.26 -r1.26.16.1 src/sys/arch/x86/pci/files.pci
cvs rdiff -u -r1.58 -r1.58.4.1 src/sys/arch/x86/pci/ichlpcib.c
cvs rdiff -u -r1.9 -r1.9.4.1 src/sys/arch/x86/pci/tco.c
cvs rdiff -u -r1.4 -r1.4.4.1 src/sys/arch/x86/pci/tco.h
cvs rdiff -u -r1.16 -r1.16.4.1 src/sys/dev/ic/i82801lpcreg.h
cvs rdiff -u -r1.445 -r1.445.2.1 src/sys/dev/pci/files.pci
cvs rdiff -u -r1.81.4.1 -r1.81.4.2 src/sys/dev/pci/ichsmb.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/amd64/conf/GENERIC
diff -u src/sys/arch/amd64/conf/GENERIC:1.599 src/sys/arch/amd64/conf/GENERIC:1.599.4.1
--- src/sys/arch/amd64/conf/GENERIC:1.599	Thu Sep 29 10:10:05 2022
+++ src/sys/arch/amd64/conf/GENERIC	Tue Aug  1 14:06:36 2023
@@ -1,4 +1,4 @@
-# $NetBSD: GENERIC,v 1.599 2022/09/29 10:10:05 riastradh Exp $
+# $NetBSD: GENERIC,v 1.599.4.1 2023/08/01 14:06:36 martin Exp $
 #
 # GENERIC machine description file
 #
@@ -22,7 +22,7 @@ include 	"arch/amd64/conf/std.amd64"
 
 options 	INCLUDE_CONFIG_FILE	# embed config file in kernel binary
 
-#ident		"GENERIC-$Revision: 1.599 $"
+#ident		"GENERIC-$Revision: 1.599.4.1 $"
 
 maxusers	64		# estimated number of users
 
@@ -401,7 +401,7 @@ ichlpcib* at pci? dev ? function ?	# Int
 					# watchdog, gpio, Speedstep and HPET
 fwhrng* at ichlpcib?		# Intel 82802 FWH Random Number Generator
 #hpet*	at ichlpcib?
-tco*	at ichlpcib?		# TCO watch dog timer
+tco*	at tcoichbus?		# TCO watch dog timer
 
 aapic*	at pci? dev ? function ?	# AMD 8131 IO apic
 

Index: src/sys/arch/x86/pci/files.pci
diff -u src/sys/arch/x86/pci/files.pci:1.26 src/sys/arch/x86/pci/files.pci:1.26.16.1
--- src/sys/arch/x86/pci/files.pci:1.26	Sat Apr 24 23:36:51 2021
+++ src/sys/arch/x86/pci/files.pci	Tue Aug  1 14:06:36 2023
@@ -1,4 +1,4 @@
-#	$NetBSD: files.pci,v 1.26 2021/04/24 23:36:51 thorpej Exp $
+#	$NetBSD: files.pci,v 1.26.16.1 2023/08/01 14:06:36 martin Exp $
 
 device 	aapic
 attach 	aapic at pci
@@ -59,7 +59,6 @@ file 	arch/x86/pci/rdcpcib.c 		rdcpcib
 
 define	fwhichbus {}
 define	hpetichbus {}
-define	tcoichbus {}
 device	ichlpcib: acpipmtimer, isabus, fwhichbus, hpetichbus, gpiobus, tcoichbus
 attach	ichlpcib at pci
 file 	arch/x86/pci/ichlpcib.c 	ichlpcib

Index: src/sys/arch/x86/pci/ichlpcib.c
diff -u src/sys/arch/x86/pci/ichlpcib.c:1.58 src/sys/arch/x86/pci/ichlpcib.c:1.58.4.1
--- src/sys/arch/x86/pci/ichlpcib.c:1.58	Thu Sep 22 14:42:29 2022
+++ src/sys/arch/x86/pci/ichlpcib.c	Tue Aug  1 14:06:36 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: ichlpcib.c,v 1.58 2022/09/22 14:42:29 riastradh Exp $	*/
+/*	$NetBSD: ichlpcib.c,v 1.58.4.1 2023/08/01 14:06:36 martin Exp $	*/
 
 /*-
  * Copyright (c) 2004 The NetBSD Foundation, Inc.
@@ -40,7 +40,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ichlpcib.c,v 1.58 2022/09/22 14:42:29 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ichlpcib.c,v 1.58.4.1 2023/08/01 14:06:36 martin Exp $");
 
 #include <sys/types.h>
 #include <sys/param.h>
@@ -89,6 +89,11 @@ struct lpcib_softc {
 	bus_space_handle_t	sc_pmh;
 	bus_size_t		sc_iosize;
 
+	/* TCO variables. */
+	bus_space_tag_t		sc_tcot;
+	bus_space_handle_t	sc_tcoh;
+	bus_size_t		sc_tcosz;
+
 	/* HPET variables. */
 	uint32_t		sc_hpet_reg;
 
@@ -348,6 +353,13 @@ lpcibattach(device_t parent, device_t se
 		return;
 	}
 
+	if (bus_space_subregion(sc->sc_pmt, sc->sc_pmh, PMC_TCO_BASE,
+		TCO_REGSIZE, &sc->sc_tcoh)) {
+		aprint_error_dev(self, "can't map TCO space\n");
+	} else {
+		sc->sc_tcot = sc->sc_pmt;
+	}
+
 	sc->sc_pmcon_orig = pci_conf_read(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag,
 	    LPCIB_PCI_GEN_PMCON_1);
 
@@ -644,6 +656,8 @@ tcotimer_configure(device_t self)
 	arg.ta_rcbat = sc->sc_rcbat;
 	arg.ta_rcbah = sc->sc_rcbah;
 	arg.ta_pcib = &sc->sc_pcib;
+	arg.ta_tcot = sc->sc_tcot;
+	arg.ta_tcoh = sc->sc_tcoh;
 
 	sc->sc_tco = config_found(self, &arg, NULL,
 	    CFARGS(.iattr = "tcoichbus"));

Index: src/sys/arch/x86/pci/tco.c
diff -u src/sys/arch/x86/pci/tco.c:1.9 src/sys/arch/x86/pci/tco.c:1.9.4.1
--- src/sys/arch/x86/pci/tco.c:1.9	Thu Sep 22 14:43:04 2022
+++ src/sys/arch/x86/pci/tco.c	Tue Aug  1 14:06:36 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: tco.c,v 1.9 2022/09/22 14:43:04 riastradh Exp $	*/
+/*	$NetBSD: tco.c,v 1.9.4.1 2023/08/01 14:06:36 martin Exp $	*/
 
 /*-
  * Copyright (c) 2015 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: tco.c,v 1.9 2022/09/22 14:43:04 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tco.c,v 1.9.4.1 2023/08/01 14:06:36 martin Exp $");
 
 #include <sys/types.h>
 #include <sys/param.h>
@@ -60,8 +60,10 @@ struct tco_softc {
 	bus_space_tag_t		sc_rcbat;
 	bus_space_handle_t	sc_rcbah;
 	struct pcib_softc *	sc_pcib;
+	pci_chipset_tag_t	sc_pc;
 	bus_space_tag_t		sc_tcot;
 	bus_space_handle_t	sc_tcoh;
+	int			(*sc_set_noreboot)(device_t, bool);
 	int			sc_armed;
 	unsigned int		sc_min_t;
 	unsigned int		sc_max_t;
@@ -93,12 +95,13 @@ tco_match(device_t parent, cfdata_t matc
 {
 	struct tco_attach_args *ta = aux;
 
-	if (ta->ta_pmt == 0)
-		return 0;
-
 	switch (ta->ta_version) {
+	case TCO_VERSION_SMBUS:
+		break;
 	case TCO_VERSION_RCBA:
 	case TCO_VERSION_PCIB:
+		if (ta->ta_pmt == 0)
+			return 0;
 		break;
 	default:
 		return 0;
@@ -125,11 +128,21 @@ tco_attach(device_t parent, device_t sel
 	aprint_normal(": TCO (watchdog) timer configured.\n");
 	aprint_naive("\n");
 
-	sc->sc_tcot = sc->sc_pmt;
-	if (bus_space_subregion(sc->sc_pmt, sc->sc_pmh, PMC_TCO_BASE,
-		TCO_REGSIZE, &sc->sc_tcoh)) {
-		aprint_error_dev(self, "failed to map TCO registers\n");
-		return;
+	switch (sc->sc_version) {
+	case TCO_VERSION_SMBUS:
+		sc->sc_tcot = ta->ta_tcot;
+		sc->sc_tcoh = ta->ta_tcoh;
+		sc->sc_set_noreboot = ta->ta_set_noreboot;
+		break;
+	case TCO_VERSION_RCBA:
+	case TCO_VERSION_PCIB:
+		sc->sc_tcot = sc->sc_pmt;
+		if (bus_space_subregion(sc->sc_pmt, sc->sc_pmh, PMC_TCO_BASE,
+			TCO_REGSIZE, &sc->sc_tcoh)) {
+			aprint_error_dev(self, "failed to map TCO\n");
+			return;
+		}
+		break;
 	}
 
 	/* Explicitly stop the TCO timer. */
@@ -140,6 +153,7 @@ tco_attach(device_t parent, device_t sel
 	 * work. We don't know what the SMBIOS does.
 	 */
 	ioreg = bus_space_read_4(sc->sc_pmt, sc->sc_pmh, PMC_SMI_EN);
+	aprint_debug_dev(self, "SMI_EN=0x%08x\n", ioreg);
 	ioreg &= ~PMC_SMI_EN_TCO_EN;
 
 	/*
@@ -150,7 +164,10 @@ tco_attach(device_t parent, device_t sel
 		ioreg |= PMC_SMI_EN_TCO_EN;
 	}
 	if ((ioreg & PMC_SMI_EN_GBL_SMI_EN) != 0) {
+		aprint_debug_dev(self, "SMI_EN:=0x%08x\n", ioreg);
 		bus_space_write_4(sc->sc_pmt, sc->sc_pmh, PMC_SMI_EN, ioreg);
+		aprint_debug_dev(self, "SMI_EN=0x%08x\n",
+		    bus_space_read_4(sc->sc_pmt, sc->sc_pmh, PMC_SMI_EN));
 	}
 
 	/* Reset the watchdog status registers. */
@@ -172,6 +189,7 @@ tco_attach(device_t parent, device_t sel
 	 *                              2secs          23secs
 	 */
 	switch (sc->sc_version) {
+	case TCO_VERSION_SMBUS:
 	case TCO_VERSION_RCBA:
 		sc->sc_max_t = TCOTIMER2_MAX_TICK;
 		sc->sc_min_t = TCOTIMER2_MIN_TICK;
@@ -256,6 +274,7 @@ tcotimer_setmode(struct sysmon_wdog *smw
 
 		/* set the timeout, */
 		switch (sc->sc_version) {
+		case TCO_VERSION_SMBUS:
 		case TCO_VERSION_RCBA:
 			/* ICH6 or newer */
 			ich6period = bus_space_read_2(sc->sc_tcot, sc->sc_tcoh,
@@ -289,6 +308,7 @@ tcotimer_tickle(struct sysmon_wdog *smw)
 
 	/* any value is allowed */
 	switch (sc->sc_version) {
+	case TCO_VERSION_SMBUS:
 	case TCO_VERSION_RCBA:
 		bus_space_write_2(sc->sc_tcot, sc->sc_tcoh, TCO_RLD, 1);
 		break;
@@ -339,8 +359,14 @@ static int
 tcotimer_disable_noreboot(device_t self)
 {
 	struct tco_softc *sc = device_private(self);
+	int error = EINVAL;
 
 	switch (sc->sc_version) {
+	case TCO_VERSION_SMBUS:
+		error = (*sc->sc_set_noreboot)(self, false);
+		if (error)
+			goto error;
+		break;
 	case TCO_VERSION_RCBA: {
 		uint32_t status;
 
@@ -376,7 +402,7 @@ tcotimer_disable_noreboot(device_t self)
 error:
 	aprint_error_dev(self, "TCO timer reboot disabled by hardware; "
 	    "hope SMBIOS properly handles it.\n");
-	return EINVAL;
+	return error;
 }
 
 MODULE(MODULE_CLASS_DRIVER, tco, "sysmon_wdog");

Index: src/sys/arch/x86/pci/tco.h
diff -u src/sys/arch/x86/pci/tco.h:1.4 src/sys/arch/x86/pci/tco.h:1.4.4.1
--- src/sys/arch/x86/pci/tco.h:1.4	Thu Sep 22 14:42:29 2022
+++ src/sys/arch/x86/pci/tco.h	Tue Aug  1 14:06:36 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: tco.h,v 1.4 2022/09/22 14:42:29 riastradh Exp $	*/
+/*	$NetBSD: tco.h,v 1.4.4.1 2023/08/01 14:06:36 martin Exp $	*/
 
 /*-
  * Copyright (c) 2015 The NetBSD Foundation, Inc.
@@ -40,12 +40,16 @@ struct tco_attach_args {
 	enum {
 		TCO_VERSION_PCIB = 0,
 		TCO_VERSION_RCBA = 1,
+		TCO_VERSION_SMBUS = 2,
 	}			ta_version;
 	bus_space_tag_t		ta_pmt;
 	bus_space_handle_t	ta_pmh;
 	bus_space_tag_t		ta_rcbat;
 	bus_space_handle_t	ta_rcbah;
 	struct pcib_softc *	ta_pcib;
+	bus_space_tag_t		ta_tcot;
+	bus_space_handle_t	ta_tcoh;
+	int			(*ta_set_noreboot)(device_t, bool);
 };
 
 #endif

Index: src/sys/dev/ic/i82801lpcreg.h
diff -u src/sys/dev/ic/i82801lpcreg.h:1.16 src/sys/dev/ic/i82801lpcreg.h:1.16.4.1
--- src/sys/dev/ic/i82801lpcreg.h:1.16	Thu Sep 22 14:45:33 2022
+++ src/sys/dev/ic/i82801lpcreg.h	Tue Aug  1 14:06:37 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: i82801lpcreg.h,v 1.16 2022/09/22 14:45:33 riastradh Exp $	*/
+/*	$NetBSD: i82801lpcreg.h,v 1.16.4.1 2023/08/01 14:06:37 martin Exp $	*/
 
 /*-
  * Copyright (c) 2004 The NetBSD Foundation, Inc.
@@ -171,6 +171,11 @@
 #define SMB_HOSTC_HSTEN		(1 << 0)	/* enable host controller */
 #define SMB_HOSTC_SMIEN		(1 << 1)	/* generate SMI */
 #define SMB_HOSTC_I2CEN		(1 << 2)	/* enable I2C commands */
+#define SMB_TCOBASE	0x50		/* TCO Base Address */
+#define  SMB_TCOBASE_TCOBA	__BITS(15,5)	/* TCO Base Address */
+#define  SMB_TCOBASE_IOS	__BIT(0)	/* I/O Space */
+#define SMB_TCOCTL	0x54		/* TCO Control */
+#define  SMB_TCOCTL_TCO_BASE_EN	__BIT(8)	/* TCO Base Enable */
 
 /* SMBus I/O registers */
 #define SMB_HS		0x00		/* host status */
@@ -301,4 +306,42 @@ tcotimer_second_to_tick(int ltick)
 #define TCOTIMER_MAX_TICK 	0x3f 	/* 39 seconds max */
 #define TCOTIMER2_MAX_TICK 	0x265	/* 613 seconds max */
 
+/*
+ * P2SB: Primary to Sideband Bridge, PCI configuration registers
+ */
+#define	P2SB_SBREG_BAR		0x10	/* Sideband Register Access BAR */
+#define	P2SB_SBREG_BARH		0x14	/* Sideband BAR High DWORD */
+#define	P2SB_P2SBC		0xe0	/* P2SB Control */
+#define	 P2SB_P2SBC_HIDE	__BIT(8)	/* Hide Device */
+
+/*
+ * PCH Private Configuration Space -- Sideband
+ */
+#define	SB_PORTID		__BITS(23,16)
+#define	 SB_PORTID_SMBUS		0xc6
+
+#define	SB_PORT(id)		__SHIFTIN(id, SB_PORTID)
+
+#define	SB_SMBUS_BASE		(SB_PORT(SB_PORTID_SMBUS) + 0x00)
+#define	SB_SMBUS_SIZE		0x14
+
+#define	SB_SMBUS_TCOCFG		0x00	/* TCO Configuration */
+#define	 SB_SMBUS_TCOCFG_IE	__BIT(7)	/* TCO IRQ Enable */
+#define	 SB_SMBUS_TCOCFG_IS	__BITS(2,0)	/* TCO IRQ Select */
+#define	 SB_SMBUS_TCOCFG_IS_IRQ9	0	/* maps to 8259 and APIC */
+#define	 SB_SMBUS_TCOCFG_IS_IRQ10	1	/* maps to 8259 and APIC */
+#define	 SB_SMBUS_TCOCFG_IS_IRQ11	2	/* maps to 8259 and APIC */
+#define	 SB_SMBUS_TCOCFG_IS_IRQ20	4	/* maps to APIC */
+#define	 SB_SMBUS_TCOCFG_IS_IRQ21	3	/* maps to APIC */
+#define	 SB_SMBUS_TCOCFG_IS_IRQ22	4	/* maps to APIC */
+#define	 SB_SMBUS_TCOCFG_IS_IRQ23	5	/* maps to APIC */
+#define	SB_SMBUS_GC		0x0c	/* General Control */
+#define	 SB_SMBUS_GC_NR		__BIT(1)	/* No Reboot */
+#define	 SB_SMBUS_GC_FD		__BIT(0)	/* Function Disable */
+#define	SB_SMBUS_PCE		0x10	/* Power Control Enable */
+#define	 SB_SMBUS_PCE_SE	__BIT(3)	/* Sleep Enable */
+#define	 SB_SMBUS_PCE_D3HE	__BIT(2)	/* D3-Hot Enable */
+#define	 SB_SMBUS_PCE_I3E	__BIT(1)	/* I3 Enable */
+#define	 SB_SMBUS_PCE_PMCRE	__BIT(0)	/* PMC Request Enable */
+
 #endif /*  _DEV_IC_I82801LPCREG_H_ */

Index: src/sys/dev/pci/files.pci
diff -u src/sys/dev/pci/files.pci:1.445 src/sys/dev/pci/files.pci:1.445.2.1
--- src/sys/dev/pci/files.pci:1.445	Fri Oct 14 22:10:15 2022
+++ src/sys/dev/pci/files.pci	Tue Aug  1 14:06:36 2023
@@ -1,4 +1,4 @@
-#	$NetBSD: files.pci,v 1.445 2022/10/14 22:10:15 jmcneill Exp $
+#	$NetBSD: files.pci,v 1.445.2.1 2023/08/01 14:06:36 martin Exp $
 #
 # Config file and device description for machine-independent PCI code.
 # Included by ports that need it.  Requires that the SCSI files be
@@ -980,8 +980,12 @@ device	nfsmb: i2cbus
 attach	nfsmb at nfsmbc
 file	dev/pci/nfsmb.c			nfsmbc | nfsmb
 
+# Intel ICH -- I/O or Platform Controller Hub
+# (most drivers under sys/arch/x86/pci)
+define	tcoichbus {}
+
 # Intel ICH SMBus controller
-device	ichsmb: i2cbus
+device	ichsmb: i2cbus, tcoichbus
 attach	ichsmb at pci
 file	dev/pci/ichsmb.c		ichsmb
 

Index: src/sys/dev/pci/ichsmb.c
diff -u src/sys/dev/pci/ichsmb.c:1.81.4.1 src/sys/dev/pci/ichsmb.c:1.81.4.2
--- src/sys/dev/pci/ichsmb.c:1.81.4.1	Sat Jul 29 10:50:05 2023
+++ src/sys/dev/pci/ichsmb.c	Tue Aug  1 14:06:36 2023
@@ -1,4 +1,4 @@
-/*	$NetBSD: ichsmb.c,v 1.81.4.1 2023/07/29 10:50:05 martin Exp $	*/
+/*	$NetBSD: ichsmb.c,v 1.81.4.2 2023/08/01 14:06:36 martin Exp $	*/
 /*	$OpenBSD: ichiic.c,v 1.44 2020/10/07 11:23:05 jsg Exp $	*/
 
 /*
@@ -22,7 +22,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ichsmb.c,v 1.81.4.1 2023/07/29 10:50:05 martin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ichsmb.c,v 1.81.4.2 2023/08/01 14:06:36 martin Exp $");
 
 #include <sys/param.h>
 #include <sys/device.h>
@@ -42,6 +42,8 @@ __KERNEL_RCSID(0, "$NetBSD: ichsmb.c,v 1
 
 #include <dev/i2c/i2cvar.h>
 
+#include <x86/pci/tco.h>
+
 #ifdef ICHIIC_DEBUG
 #define DPRINTF(x) printf x
 #else
@@ -75,6 +77,18 @@ struct ichsmb_softc {
 		bool         done;
 	}			sc_i2c_xfer;
 	device_t		sc_i2c_device;
+
+	bus_space_tag_t		sc_tcot;
+	bus_space_handle_t	sc_tcoh;
+	bus_size_t		sc_tcosz;
+	bus_space_tag_t		sc_sbregt;
+	bus_space_handle_t	sc_sbregh;
+	bus_size_t		sc_sbregsz;
+	bus_space_tag_t		sc_pmt;
+	bus_space_handle_t	sc_pmh;
+	bus_size_t		sc_pmsz;
+	bool			sc_tco_probed;
+	device_t		sc_tco_device;
 };
 
 static int	ichsmb_match(device_t, cfdata_t, void *);
@@ -83,6 +97,9 @@ static int	ichsmb_detach(device_t, int);
 static int	ichsmb_rescan(device_t, const char *, const int *);
 static void	ichsmb_chdet(device_t, device_t);
 
+static void	ichsmb_probe_tco(struct ichsmb_softc *,
+		    const struct pci_attach_args *);
+
 static int	ichsmb_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *,
 		    size_t, void *, size_t, int);
 
@@ -229,6 +246,12 @@ ichsmb_attach(device_t parent, device_t 
 	sc->sc_i2c_tag.ic_cookie = sc;
 	sc->sc_i2c_tag.ic_exec = ichsmb_i2c_exec;
 
+	/*
+	 * Probe to see if there's a TCO hanging here instead of the
+	 * LPCIB and map it if we can.
+	 */
+	ichsmb_probe_tco(sc, pa);
+
 	sc->sc_i2c_device = NULL;
 	ichsmb_rescan(self, NULL, NULL);
 
@@ -236,6 +259,188 @@ out:	if (!pmf_device_register(self, NULL
 		aprint_error_dev(self, "couldn't establish power handler\n");
 }
 
+static void
+ichsmb_probe_tco(struct ichsmb_softc *sc, const struct pci_attach_args *pa)
+{
+	const device_t self = sc->sc_dev;
+	const pci_chipset_tag_t pc = sc->sc_pc;
+	const pcitag_t p2sb_tag = pci_make_tag(pc,
+	    /*bus*/0, /*dev*/0x1f, /*fn*/1);
+	const pcitag_t pmc_tag = pci_make_tag(pc,
+	    /*bus*/0, /*dev*/0x1f, /*fn*/2);
+	pcireg_t tcoctl, tcobase, p2sbc, sbreglo, sbreghi;
+	bus_addr_t sbreg, pmbase;
+	int error = EIO;
+
+	/*
+	 * Only attempt this on devices where we expect to find a TCO.
+	 */
+	switch (PCI_PRODUCT(pa->pa_id)) {
+	case PCI_PRODUCT_INTEL_100SERIES_LP_SMB:
+	case PCI_PRODUCT_INTEL_100SERIES_SMB:
+		break;
+	default:
+		goto fail;
+	}
+
+	/*
+	 * Verify the TCO base address register is enabled.
+	 */
+	tcoctl = pci_conf_read(pa->pa_pc, pa->pa_tag, SMB_TCOCTL);
+	aprint_debug_dev(self, "TCOCTL=0x%"PRIx32"\n", tcoctl);
+	if ((tcoctl & SMB_TCOCTL_TCO_BASE_EN) == 0) {
+		aprint_debug_dev(self, "TCO disabled\n");
+		goto fail;
+	}
+
+	/*
+	 * Verify the TCO base address register has the I/O space bit
+	 * set -- otherwise we don't know how to interpret the
+	 * register.
+	 */
+	tcobase = pci_conf_read(pa->pa_pc, pa->pa_tag, SMB_TCOBASE);
+	aprint_debug_dev(self, "TCOBASE=0x%"PRIx32"\n", tcobase);
+	if ((tcobase & SMB_TCOBASE_IOS) == 0) {
+		aprint_error_dev(self, "unrecognized TCO space\n");
+		goto fail;
+	}
+
+	/*
+	 * Map the TCO I/O space.
+	 */
+	sc->sc_tcot = sc->sc_iot;
+	error = bus_space_map(sc->sc_tcot, tcobase & SMB_TCOBASE_TCOBA,
+	    TCO_REGSIZE, 0, &sc->sc_tcoh);
+	if (error) {
+		aprint_error_dev(self, "failed to map TCO: %d\n", error);
+		goto fail;
+	}
+	sc->sc_tcosz = TCO_REGSIZE;
+
+	/*
+	 * Clear the Hide Device bit so we can map the SBREG_BAR from
+	 * the P2SB registers; then restore the Hide Device bit so
+	 * nobody else gets confused.
+	 *
+	 * XXX Hope nobody else is trying to touch the P2SB!
+	 *
+	 * XXX Should we have a way to lock PCI bus enumeration,
+	 * e.g. from concurrent drvctl rescan?
+	 *
+	 * XXX pci_mapreg_info doesn't work to get the size, somehow
+	 * comes out as 4.  Datasheet for 100-series chipset says the
+	 * size is 16 MB, unconditionally, and the type is memory.
+	 *
+	 * XXX The above XXX comment was probably a result of PEBCAK
+	 * when I tried to use 0xe4 instead of 0xe0 for P2SBC -- should
+	 * try again with pci_mapreg_info or pci_mapreg_map.
+	 */
+	p2sbc = pci_conf_read(pc, p2sb_tag, P2SB_P2SBC);
+	aprint_debug_dev(self, "P2SBC=0x%x\n", p2sbc);
+	pci_conf_write(pc, p2sb_tag, P2SB_P2SBC, p2sbc & ~P2SB_P2SBC_HIDE);
+	aprint_debug_dev(self, "P2SBC=0x%x -> 0x%x\n", p2sbc,
+	    pci_conf_read(pc, p2sb_tag, P2SB_P2SBC));
+	sbreglo = pci_conf_read(pc, p2sb_tag, P2SB_SBREG_BAR);
+	sbreghi = pci_conf_read(pc, p2sb_tag, P2SB_SBREG_BARH);
+	aprint_debug_dev(self, "SBREG_BAR=0x%08x 0x%08x\n", sbreglo, sbreghi);
+	pci_conf_write(sc->sc_pc, p2sb_tag, P2SB_P2SBC, p2sbc);
+
+	/*
+	 * Map the sideband registers so we can touch the NO_REBOOT
+	 * bit.
+	 */
+	sbreg = ((uint64_t)sbreghi << 32) | (sbreglo & ~__BITS(0,3));
+	if (((uint64_t)sbreg >> 32) != sbreghi) {
+		/* paranoia for 32-bit non-PAE */
+		aprint_error_dev(self, "can't map 64-bit SBREG\n");
+		goto fail;
+	}
+	sc->sc_sbregt = pa->pa_memt;
+	error = bus_space_map(sc->sc_sbregt, sbreg + SB_SMBUS_BASE,
+	    SB_SMBUS_SIZE, 0, &sc->sc_sbregh);
+	if (error) {
+		aprint_error_dev(self, "failed to map SMBUS sideband: %d\n",
+		    error);
+		goto fail;
+	}
+	sc->sc_sbregsz = SB_SMBUS_SIZE;
+
+	/*
+	 * Map the power management configuration controller's I/O
+	 * space.  Older manual call this PMBASE for power management;
+	 * newer manuals call it ABASE for ACPI.  The chapters
+	 * describing the registers say `power management' and I can't
+	 * find any connection to ACPI (I suppose ACPI firmware logic
+	 * probably peeks and pokes registers here?) so we say PMBASE
+	 * here.
+	 *
+	 * XXX Hope nobody else is trying to touch it!
+	 */
+	pmbase = pci_conf_read(pc, pmc_tag, LPCIB_PCI_PMBASE);
+	aprint_debug_dev(self, "PMBASE=0x%"PRIxBUSADDR"\n", pmbase);
+	if ((pmbase & 1) != 1) {	/* I/O space bit? */
+		aprint_error_dev(self, "unrecognized PMC space\n");
+		goto fail;
+	}
+	sc->sc_pmt = sc->sc_iot;
+	error = bus_space_map(sc->sc_pmt, PCI_MAPREG_IO_ADDR(pmbase),
+	    LPCIB_PCI_PM_SIZE, 0, &sc->sc_pmh);
+	if (error) {
+		aprint_error_dev(self, "failed to map PMC space: %d\n", error);
+		goto fail;
+	}
+	sc->sc_pmsz = LPCIB_PCI_PM_SIZE;
+
+	/* Success! */
+	sc->sc_tco_probed = true;
+	return;
+
+fail:	if (sc->sc_pmsz) {
+		bus_space_unmap(sc->sc_pmt, sc->sc_pmh, sc->sc_pmsz);
+		sc->sc_pmsz = 0;
+	}
+	if (sc->sc_sbregsz) {
+		bus_space_unmap(sc->sc_sbregt, sc->sc_sbregh, sc->sc_sbregsz);
+		sc->sc_sbregsz = 0;
+	}
+	if (sc->sc_tcosz) {
+		bus_space_unmap(sc->sc_tcot, sc->sc_tcoh, sc->sc_tcosz);
+		sc->sc_tcosz = 0;
+	}
+}
+
+static int
+ichsmb_tco_set_noreboot(device_t tco, bool noreboot)
+{
+	device_t self = device_parent(tco);
+	struct ichsmb_softc *sc = device_private(self);
+	uint32_t gc, gc1;
+
+	KASSERTMSG(tco == sc->sc_tco_device || sc->sc_tco_device == NULL,
+	    "tco=%p child=%p", tco, sc->sc_tco_device);
+	KASSERTMSG(device_is_a(self, "ichsmb"), "%s@%s",
+	    device_xname(tco), device_xname(self));
+
+	/*
+	 * Try to clear the No Reboot bit.
+	 */
+	gc = bus_space_read_4(sc->sc_sbregt, sc->sc_sbregh, SB_SMBUS_GC);
+	if (noreboot)
+		gc |= SB_SMBUS_GC_NR;
+	else
+		gc &= ~SB_SMBUS_GC_NR;
+	bus_space_write_4(sc->sc_sbregt, sc->sc_sbregh, SB_SMBUS_GC, gc);
+
+	/*
+	 * Check whether we could make it what we want.
+	 */
+	gc1 = bus_space_read_4(sc->sc_sbregt, sc->sc_sbregh, SB_SMBUS_GC);
+	aprint_debug_dev(self, "gc=0x%x -> 0x%x\n", gc, gc1);
+	if ((gc1 & SB_SMBUS_GC_NR) != (gc & SB_SMBUS_GC_NR))
+		return ENODEV;
+	return 0;
+}
+
 static int
 ichsmb_rescan(device_t self, const char *ifattr, const int *locators)
 {
@@ -247,7 +452,22 @@ ichsmb_rescan(device_t self, const char 
 		memset(&iba, 0, sizeof(iba));
 		iba.iba_tag = &sc->sc_i2c_tag;
 		sc->sc_i2c_device = config_found(self, &iba, iicbus_print,
-		    CFARGS_NONE);
+		    CFARGS(.iattr = "i2cbus"));
+	}
+	if (sc->sc_tco_probed &&
+	    ifattr_match(ifattr, "tcoichbus") &&
+	    sc->sc_tco_device == NULL) {
+		struct tco_attach_args ta;
+
+		memset(&ta, 0, sizeof(ta));
+		ta.ta_version = TCO_VERSION_SMBUS;
+		ta.ta_pmt = sc->sc_pmt;
+		ta.ta_pmh = sc->sc_pmh;
+		ta.ta_tcot = sc->sc_tcot;
+		ta.ta_tcoh = sc->sc_tcoh;
+		ta.ta_set_noreboot = &ichsmb_tco_set_noreboot;
+		sc->sc_tco_device = config_found(self, &ta, NULL,
+		    CFARGS(.iattr = "tcoichbus"));
 	}
 
 	return 0;
@@ -275,6 +495,12 @@ ichsmb_detach(device_t self, int flags)
 		sc->sc_pihp = NULL;
 	}
 
+	if (sc->sc_pmsz != 0)
+		bus_space_unmap(sc->sc_pmt, sc->sc_pmh, sc->sc_pmsz);
+	if (sc->sc_sbregsz != 0)
+		bus_space_unmap(sc->sc_sbregt, sc->sc_sbregh, sc->sc_sbregsz);
+	if (sc->sc_tcosz != 0)
+		bus_space_unmap(sc->sc_tcot, sc->sc_tcoh, sc->sc_tcosz);
 	if (sc->sc_size != 0)
 		bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_size);
 
@@ -291,6 +517,8 @@ ichsmb_chdet(device_t self, device_t chi
 
 	if (sc->sc_i2c_device == child)
 		sc->sc_i2c_device = NULL;
+	if (sc->sc_tco_device == child)
+		sc->sc_tco_device = NULL;
 }
 
 static int

Reply via email to