diff options
author | Brent Cook <bcook@cvs.openbsd.org> | 2019-05-10 16:44:37 +0000 |
---|---|---|
committer | Brent Cook <bcook@cvs.openbsd.org> | 2019-05-10 16:44:37 +0000 |
commit | 032ef8d187f210dc6b0f4946908d35e1e170a795 (patch) | |
tree | 8044ad5e52f395c6808c086b145448f79da13d4a | |
parent | 1115c4526220ad67e1ec9efc08c1a1a628a0b778 (diff) |
Explicitly disable BCM4331 chips present in 2011-2012 Apple Mac systems.
The Mac EFI firmware enables the wireless controller, but does not disable it,
so it continues to receive packets and signal interrupts. This
was originally seen as an interrupt storm that consumes about 50% of
CPU0 on affected machines. The issue was originally discovered in 2012 by
Matthew Garret with a partial fix in Grub, then Lukas Wunner added a fix for
the Linux kernel in 2016.
This piggy-backs on the most-related driver (bwi) for the purpose of detection
and mapping the control registers, but does not actually register the driver if
the affected chip is detected.
See this archived discussion for further analysis of the bug:
https://www.mail-archive.com/linux-kernel@vger.kernel.org/msg1165355.html
ok kettenis stsp
-rw-r--r-- | sys/dev/ic/bwireg.h | 9 | ||||
-rw-r--r-- | sys/dev/pci/if_bwi_pci.c | 59 |
2 files changed, 53 insertions, 15 deletions
diff --git a/sys/dev/ic/bwireg.h b/sys/dev/ic/bwireg.h index 5047a3c06e3..dcf9c7f2dcb 100644 --- a/sys/dev/ic/bwireg.h +++ b/sys/dev/ic/bwireg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: bwireg.h,v 1.9 2014/06/07 11:55:02 stsp Exp $ */ +/* $OpenBSD: bwireg.h,v 1.10 2019/05/10 16:44:36 bcook Exp $ */ /* * Copyright (c) 2007 The DragonFly Project. All rights reserved. @@ -332,6 +332,13 @@ #define BWI_GPIO_CTRL 0x0000006c /* + * Core reset + */ +#define BWI_RESET_CTRL 0x1800 +#define BWI_RESET_STATUS 0x1804 +#define BWI_RESET_CTRL_RESET (1 << 0) + +/* * Extended PCI registers */ #define BWI_PCIR_BAR PCIR_BAR(0) diff --git a/sys/dev/pci/if_bwi_pci.c b/sys/dev/pci/if_bwi_pci.c index c4fe875b25a..c9699036b97 100644 --- a/sys/dev/pci/if_bwi_pci.c +++ b/sys/dev/pci/if_bwi_pci.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_bwi_pci.c,v 1.16 2015/11/24 17:11:39 mpi Exp $ */ +/* $OpenBSD: if_bwi_pci.c,v 1.17 2019/05/10 16:44:36 bcook Exp $ */ /* * Copyright (c) 2007 Marcus Glocker <mglocker@openbsd.org> @@ -45,6 +45,7 @@ #include <net80211/ieee80211_radiotap.h> #include <dev/ic/bwivar.h> +#include <dev/ic/bwireg.h> #include <dev/pci/pcireg.h> #include <dev/pci/pcivar.h> @@ -86,7 +87,8 @@ const struct pci_matchid bwi_pci_devices[] = { { PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_BCM4312 }, { PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_BCM4318 }, { PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_BCM4319 }, - { PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_BCM43XG } + { PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_BCM43XG }, + { PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_BCM4331 }, }; int @@ -109,6 +111,28 @@ bwi_pci_match(struct device *parent, void *match, void *aux) } void +bwi_reset_bcm4331(struct bwi_softc *sc) +{ + int i; + + /* + * The BCM4331 is not actually supported by this driver, but buggy EFI + * revisions in 2011-2012 Macs leave this chip enabled by default, + * causing it to emit spurious interrupts when the shared interrupt + * line is enabled. + */ + for (i = 0; CSR_READ_4(sc, BWI_RESET_STATUS) && i < 30; i++) + delay(10); + + CSR_WRITE_4(sc, BWI_RESET_CTRL, BWI_RESET_CTRL_RESET); + CSR_READ_4(sc, BWI_RESET_CTRL); + delay(1); + CSR_WRITE_4(sc, BWI_RESET_CTRL, 0); + CSR_READ_4(sc, BWI_RESET_CTRL); + delay(10); +} + +void bwi_pci_attach(struct device *parent, struct device *self, void *aux) { struct bwi_pci_softc *psc = (struct bwi_pci_softc *)self; @@ -123,13 +147,31 @@ bwi_pci_attach(struct device *parent, struct device *self, void *aux) psc->psc_pcitag = pa->pa_tag; /* map control / status registers */ - memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, BWI_PCI_BAR0); + memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, BWI_PCI_BAR0); if (pci_mapreg_map(pa, BWI_PCI_BAR0, memtype, 0, &sc->sc_mem_bt, &sc->sc_mem_bh, NULL, &psc->psc_mapsize, 0)) { printf(": can't map mem space\n"); return; } + /* we need to access PCI config space from the driver */ + sc->sc_conf_write = bwi_pci_conf_write; + sc->sc_conf_read = bwi_pci_conf_read; + + reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG); + + sc->sc_pci_revid = PCI_REVISION(pa->pa_class); + sc->sc_pci_did = PCI_PRODUCT(pa->pa_id); + sc->sc_pci_subvid = PCI_VENDOR(reg); + sc->sc_pci_subdid = PCI_PRODUCT(reg); + + if (sc->sc_pci_did == PCI_PRODUCT_BROADCOM_BCM4331) { + printf(": disabling\n"); + bwi_reset_bcm4331(sc); + bus_space_unmap(sc->sc_mem_bt, sc->sc_mem_bh, psc->psc_mapsize); + return; + } + /* map interrupt */ if (pci_intr_map(pa, &ih) != 0) { printf(": can't map interrupt\n"); @@ -149,17 +191,6 @@ bwi_pci_attach(struct device *parent, struct device *self, void *aux) } printf(": %s", intrstr); - /* we need to access PCI config space from the driver */ - sc->sc_conf_write = bwi_pci_conf_write; - sc->sc_conf_read = bwi_pci_conf_read; - - reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG); - - sc->sc_pci_revid = PCI_REVISION(pa->pa_class); - sc->sc_pci_did = PCI_PRODUCT(pa->pa_id); - sc->sc_pci_subvid = PCI_VENDOR(reg); - sc->sc_pci_subdid = PCI_PRODUCT(reg); - bwi_attach(sc); } |