aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/pccbb
diff options
context:
space:
mode:
authorWarner Losh <imp@FreeBSD.org>2006-03-30 04:25:45 +0000
committerWarner Losh <imp@FreeBSD.org>2006-03-30 04:25:45 +0000
commita49ed2a67303d410e00f1be2d08230b1c61c6d41 (patch)
treed8dafdbd037d524e9f8733af8f937938cce099e6 /sys/dev/pccbb
parentee516cac43ecd89e2cda1e41a401d2ef2f172fa5 (diff)
downloadsrc-a49ed2a67303d410e00f1be2d08230b1c61c6d41.tar.gz
src-a49ed2a67303d410e00f1be2d08230b1c61c6d41.zip
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.
Notes
Notes: svn path=/head/; revision=157281
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 9d51a790d3ce..f3ced97d31d4 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);
}