summaryrefslogtreecommitdiffstats
path: root/sys/dev/pccbb
diff options
context:
space:
mode:
authorimp <imp@FreeBSD.org>2006-03-30 04:25:45 +0000
committerimp <imp@FreeBSD.org>2006-03-30 04:25:45 +0000
commit54a478ceca292cc147449c66835d59e0d936ceb6 (patch)
treed8dafdbd037d524e9f8733af8f937938cce099e6 /sys/dev/pccbb
parente846fbb446d8ecc179dacd246d2071938de96b57 (diff)
downloadFreeBSD-src-54a478ceca292cc147449c66835d59e0d936ceb6.zip
FreeBSD-src-54a478ceca292cc147449c66835d59e0d936ceb6.tar.gz
On some laptops, under very high loads, the socket event register read
in the ISR doesn't read the actual socket event register, but instead reads garbage (usually 0xffffffff, but other times other things). This totally violates the PCI spec, but happens rarely enough that a workaround is in order. This adds one test when we have a real interrupt to service (which is very rare), and doesn't affect the usualy 'nothing to see here' case at all. Problem reported by many, but sam@ gave me this workaround after diagnosing the problem.
Diffstat (limited to 'sys/dev/pccbb')
-rw-r--r--sys/dev/pccbb/pccbb.c15
1 files changed, 13 insertions, 2 deletions
diff --git a/sys/dev/pccbb/pccbb.c b/sys/dev/pccbb/pccbb.c
index 9d51a79..f3ced97 100644
--- a/sys/dev/pccbb/pccbb.c
+++ b/sys/dev/pccbb/pccbb.c
@@ -650,8 +650,19 @@ cbb_intr(void *arg)
struct cbb_softc *sc = arg;
uint32_t sockevent;
+ /*
+ * Read the socket event. Sometimes, the theory goes, the PCI
+ * bus is so loaded that it cannot satisfy the read request, so
+ * we get garbage back from the following read. We have to filter
+ * out the garbage so that we don't spontaneously reset the card
+ * under high load. PCI isn't supposed to act like this. No doubt
+ * this is a bug in the PCI bridge chipset (or cbb brige) that's being
+ * used in certain amd64 laptops today. Work around the issue by
+ * assuming that any bits we don't know about being set means that
+ * we got garbage.
+ */
sockevent = cbb_get(sc, CBB_SOCKET_EVENT);
- if (sockevent != 0) {
+ if (sockevent != 0 && (sockevent & CBB_SOCKET_EVENT_VALID_MASK) == 0) {
/* ack the interrupt */
cbb_set(sc, CBB_SOCKET_EVENT, sockevent);
@@ -697,7 +708,7 @@ cbb_intr(void *arg)
* indication above.
*
* We have to call this unconditionally because some bridges deliver
- * the even independent of the CBB_SOCKET_EVENT_CD above.
+ * the event independent of the CBB_SOCKET_EVENT_CD above.
*/
exca_getb(&sc->exca[0], EXCA_CSC);
}
OpenPOWER on IntegriCloud