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