summaryrefslogtreecommitdiffstats
path: root/sys/dev
diff options
context:
space:
mode:
authoradrian <adrian@FreeBSD.org>2011-11-09 22:39:44 +0000
committeradrian <adrian@FreeBSD.org>2011-11-09 22:39:44 +0000
commite5b49f0c7ed315e158898613578059687b2a1746 (patch)
tree8b602c0352d0beaefbd8d0506f3665295a64a4b2 /sys/dev
parent8e06d9e08e0ba646624564499631f71633de7e1c (diff)
downloadFreeBSD-src-e5b49f0c7ed315e158898613578059687b2a1746.zip
FreeBSD-src-e5b49f0c7ed315e158898613578059687b2a1746.tar.gz
Introduce a work-around for issues with the AR5416 based MAC on SMP devices.
The AR5416 MAC (which shows up in the AR5008, AR9001, AR9002 devices) has issues with PCI transactions on SMP machines. This work-around enforces that register access is serialised through a (global for now) spinlock. This should stop the hangs people have seen with the AR5416 PCI devices on SMP hosts. Obtained by: Linux, Atheros
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/ath/ah_osdep.c29
-rw-r--r--sys/dev/ath/ath_hal/ah.c2
-rw-r--r--sys/dev/ath/ath_hal/ah.h2
-rw-r--r--sys/dev/ath/ath_hal/ah_internal.h3
-rw-r--r--sys/dev/ath/ath_hal/ar5416/ar5416_attach.c16
-rw-r--r--sys/dev/ath/if_ath.c12
-rw-r--r--sys/dev/ath/if_ath_sysctl.c7
7 files changed, 70 insertions, 1 deletions
diff --git a/sys/dev/ath/ah_osdep.c b/sys/dev/ath/ah_osdep.c
index 0272f6b..37b07c1 100644
--- a/sys/dev/ath/ah_osdep.c
+++ b/sys/dev/ath/ah_osdep.c
@@ -38,6 +38,8 @@
#include <sys/bus.h>
#include <sys/malloc.h>
#include <sys/proc.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
#include <machine/stdarg.h>
@@ -59,6 +61,17 @@
#define BUSTAG(ah) ((ah)->ah_st)
#endif
+/*
+ * This lock is used to seralise register access for chips which have
+ * problems w/ SMP CPUs issuing concurrent PCI transactions.
+ *
+ * XXX This is a global lock for now; it should be pushed to
+ * a per-device lock in some platform-independent fashion.
+ */
+struct mtx ah_regser_mtx;
+MTX_SYSINIT(ah_regser, &ah_regser_mtx, "Atheros register access mutex",
+ MTX_SPIN);
+
extern void ath_hal_printf(struct ath_hal *, const char*, ...)
__printflike(2,3);
extern void ath_hal_vprintf(struct ath_hal *, const char*, __va_list)
@@ -250,12 +263,16 @@ ath_hal_reg_write(struct ath_hal *ah, u_int32_t reg, u_int32_t val)
alq_post(ath_hal_alq, ale);
}
}
+ if (ah->ah_config.ah_serialise_reg_war)
+ mtx_lock_spin(&ah_regser_mtx);
#if _BYTE_ORDER == _BIG_ENDIAN
if (OS_REG_UNSWAPPED(reg))
bus_space_write_4(tag, h, reg, val);
else
#endif
bus_space_write_stream_4(tag, h, reg, val);
+ if (ah->ah_config.ah_serialise_reg_war)
+ mtx_unlock_spin(&ah_regser_mtx);
}
u_int32_t
@@ -265,12 +282,16 @@ ath_hal_reg_read(struct ath_hal *ah, u_int32_t reg)
bus_space_handle_t h = ah->ah_sh;
u_int32_t val;
+ if (ah->ah_config.ah_serialise_reg_war)
+ mtx_lock_spin(&ah_regser_mtx);
#if _BYTE_ORDER == _BIG_ENDIAN
if (OS_REG_UNSWAPPED(reg))
val = bus_space_read_4(tag, h, reg);
else
#endif
val = bus_space_read_stream_4(tag, h, reg);
+ if (ah->ah_config.ah_serialise_reg_war)
+ mtx_unlock_spin(&ah_regser_mtx);
if (ath_hal_alq) {
struct ale *ale = ath_hal_alq_get(ah);
if (ale) {
@@ -316,12 +337,16 @@ ath_hal_reg_write(struct ath_hal *ah, u_int32_t reg, u_int32_t val)
bus_space_tag_t tag = BUSTAG(ah);
bus_space_handle_t h = ah->ah_sh;
+ if (ah->ah_config.ah_serialise_reg_war)
+ mtx_lock_spin(&ah_regser_mtx);
#if _BYTE_ORDER == _BIG_ENDIAN
if (OS_REG_UNSWAPPED(reg))
bus_space_write_4(tag, h, reg, val);
else
#endif
bus_space_write_stream_4(tag, h, reg, val);
+ if (ah->ah_config.ah_serialise_reg_war)
+ mtx_unlock_spin(&ah_regser_mtx);
}
u_int32_t
@@ -331,12 +356,16 @@ ath_hal_reg_read(struct ath_hal *ah, u_int32_t reg)
bus_space_handle_t h = ah->ah_sh;
u_int32_t val;
+ if (ah->ah_config.ah_serialise_reg_war)
+ mtx_lock_spin(&ah_regser_mtx);
#if _BYTE_ORDER == _BIG_ENDIAN
if (OS_REG_UNSWAPPED(reg))
val = bus_space_read_4(tag, h, reg);
else
#endif
val = bus_space_read_stream_4(tag, h, reg);
+ if (ah->ah_config.ah_serialise_reg_war)
+ mtx_unlock_spin(&ah_regser_mtx);
return val;
}
#endif /* AH_DEBUG || AH_REGOPS_FUNC */
diff --git a/sys/dev/ath/ath_hal/ah.c b/sys/dev/ath/ath_hal/ah.c
index c42fb5a..3af16aa 100644
--- a/sys/dev/ath/ath_hal/ah.c
+++ b/sys/dev/ath/ath_hal/ah.c
@@ -665,6 +665,8 @@ ath_hal_getcapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type,
return pCap->halHasLongRxDescTsf ? HAL_OK : HAL_ENOTSUPP;
case HAL_CAP_BB_READ_WAR: /* Baseband read WAR */
return pCap->halHasBBReadWar? HAL_OK : HAL_ENOTSUPP;
+ case HAL_CAP_SERIALISE_WAR: /* PCI register serialisation */
+ return pCap->halSerialiseRegWar ? HAL_OK : HAL_ENOTSUPP;
default:
return HAL_EINVAL;
}
diff --git a/sys/dev/ath/ath_hal/ah.h b/sys/dev/ath/ath_hal/ah.h
index 2ea4e48..b195d02 100644
--- a/sys/dev/ath/ath_hal/ah.h
+++ b/sys/dev/ath/ath_hal/ah.h
@@ -150,6 +150,7 @@ typedef enum {
HAL_CAP_RXDESC_SELFLINK = 242, /* support a self-linked tail RX descriptor */
HAL_CAP_LONG_RXDESC_TSF = 243, /* hardware supports 32bit TSF in RX descriptor */
HAL_CAP_BB_READ_WAR = 244, /* baseband read WAR */
+ HAL_CAP_SERIALISE_WAR = 245, /* serialise register access on PCI */
} HAL_CAPABILITY_TYPE;
/*
@@ -781,6 +782,7 @@ typedef struct
int ah_sw_beacon_response_time; /* in TU's */
int ah_additional_swba_backoff; /* in TU's */
int ah_force_full_reset; /* force full chip reset rather then warm reset */
+ int ah_serialise_reg_war; /* force serialisation of register IO */
} HAL_OPS_CONFIG;
/*
diff --git a/sys/dev/ath/ath_hal/ah_internal.h b/sys/dev/ath/ath_hal/ah_internal.h
index 4378c83..a253b65 100644
--- a/sys/dev/ath/ath_hal/ah_internal.h
+++ b/sys/dev/ath/ath_hal/ah_internal.h
@@ -210,7 +210,8 @@ typedef struct {
halHasRxSelfLinkedTail : 1,
halSupportsFastClock5GHz : 1, /* Hardware supports 5ghz fast clock; check eeprom/channel before using */
halHasLongRxDescTsf : 1,
- halHasBBReadWar : 1;
+ halHasBBReadWar : 1,
+ halSerialiseRegWar : 1;
uint32_t halWirelessModes;
uint16_t halTotalQueues;
uint16_t halKeyCacheSize;
diff --git a/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c b/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c
index 43ec6fe..37412fa 100644
--- a/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c
+++ b/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c
@@ -908,8 +908,24 @@ ar5416FillCapabilityInfo(struct ath_hal *ah)
pCap->halRfSilentSupport = AH_TRUE;
}
+ /*
+ * The MAC will mark frames as RXed if there's a descriptor
+ * to write them to. So if it hits a self-linked final descriptor,
+ * it'll keep ACKing frames even though they're being silently
+ * dropped. Thus, this particular feature of the driver can't
+ * be used for 802.11n devices.
+ */
ahpriv->ah_rxornIsFatal = AH_FALSE;
+ /*
+ * If it's a PCI NIC, ask the HAL OS layer to serialise
+ * register access, or SMP machines may cause the hardware
+ * to hang. This is applicable to AR5416 and AR9220; I'm not
+ * sure about AR9160 or AR9227.
+ */
+ if (! AH_PRIVATE(ah)->ah_ispcie)
+ pCap->halSerialiseRegWar = 1;
+
return AH_TRUE;
}
diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c
index ac2dd2a..c3485e4 100644
--- a/sys/dev/ath/if_ath.c
+++ b/sys/dev/ath/if_ath.c
@@ -65,6 +65,7 @@ __FBSDID("$FreeBSD$");
#include <sys/priv.h>
#include <sys/module.h>
#include <sys/ktr.h>
+#include <sys/smp.h> /* for mp_ncpus */
#include <machine/bus.h>
@@ -675,6 +676,17 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
#endif
/*
+ * Check if the hardware requires PCI register serialisation.
+ * Some of the Owl based MACs require this.
+ */
+ if (mp_ncpus > 1 &&
+ ath_hal_getcapability(ah, HAL_CAP_SERIALISE_WAR,
+ 0, NULL) == HAL_OK) {
+ sc->sc_ah->ah_config.ah_serialise_reg_war = 1;
+ device_printf(sc->sc_dev, "Enabling register serialisation\n");
+ }
+
+ /*
* Indicate we need the 802.11 header padded to a
* 32-bit boundary for 4-address and QoS frames.
*/
diff --git a/sys/dev/ath/if_ath_sysctl.c b/sys/dev/ath/if_ath_sysctl.c
index e9f4ad4..5323504 100644
--- a/sys/dev/ath/if_ath_sysctl.c
+++ b/sys/dev/ath/if_ath_sysctl.c
@@ -898,4 +898,11 @@ ath_sysctl_hal_attach(struct ath_softc *sc)
SYSCTL_ADD_INT(ctx, child, OID_AUTO, "force_full_reset", CTLFLAG_RW,
&sc->sc_ah->ah_config.ah_force_full_reset, 0,
"Force full chip reset rather than a warm reset");
+
+ /*
+ * This is initialised by the driver.
+ */
+ SYSCTL_ADD_INT(ctx, child, OID_AUTO, "serialise_reg_war", CTLFLAG_RW,
+ &sc->sc_ah->ah_config.ah_serialise_reg_war, 0,
+ "Force register access serialisation");
}
OpenPOWER on IntegriCloud