summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrent Cook <bcook@cvs.openbsd.org>2019-05-10 16:44:37 +0000
committerBrent Cook <bcook@cvs.openbsd.org>2019-05-10 16:44:37 +0000
commit032ef8d187f210dc6b0f4946908d35e1e170a795 (patch)
tree8044ad5e52f395c6808c086b145448f79da13d4a
parent1115c4526220ad67e1ec9efc08c1a1a628a0b778 (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.h9
-rw-r--r--sys/dev/pci/if_bwi_pci.c59
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);
}