svn commit: r227410 - in head/sys/dev/ath: . ath_hal ath_hal/ar5416

Adrian Chadd adrian at FreeBSD.org
Wed Nov 9 22:39:44 UTC 2011


Author: adrian
Date: Wed Nov  9 22:39:44 2011
New Revision: 227410
URL: http://svn.freebsd.org/changeset/base/227410

Log:
  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

Modified:
  head/sys/dev/ath/ah_osdep.c
  head/sys/dev/ath/ath_hal/ah.c
  head/sys/dev/ath/ath_hal/ah.h
  head/sys/dev/ath/ath_hal/ah_internal.h
  head/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c
  head/sys/dev/ath/if_ath.c
  head/sys/dev/ath/if_ath_sysctl.c

Modified: head/sys/dev/ath/ah_osdep.c
==============================================================================
--- head/sys/dev/ath/ah_osdep.c	Wed Nov  9 21:53:49 2011	(r227409)
+++ head/sys/dev/ath/ah_osdep.c	Wed Nov  9 22:39:44 2011	(r227410)
@@ -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_
 			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_i
 	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_
 	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_i
 	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 */

Modified: head/sys/dev/ath/ath_hal/ah.c
==============================================================================
--- head/sys/dev/ath/ath_hal/ah.c	Wed Nov  9 21:53:49 2011	(r227409)
+++ head/sys/dev/ath/ath_hal/ah.c	Wed Nov  9 22:39:44 2011	(r227410)
@@ -665,6 +665,8 @@ ath_hal_getcapability(struct ath_hal *ah
 		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;
 	}

Modified: head/sys/dev/ath/ath_hal/ah.h
==============================================================================
--- head/sys/dev/ath/ath_hal/ah.h	Wed Nov  9 21:53:49 2011	(r227409)
+++ head/sys/dev/ath/ath_hal/ah.h	Wed Nov  9 22:39:44 2011	(r227410)
@@ -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;
 
 /*

Modified: head/sys/dev/ath/ath_hal/ah_internal.h
==============================================================================
--- head/sys/dev/ath/ath_hal/ah_internal.h	Wed Nov  9 21:53:49 2011	(r227409)
+++ head/sys/dev/ath/ath_hal/ah_internal.h	Wed Nov  9 22:39:44 2011	(r227410)
@@ -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;

Modified: head/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c
==============================================================================
--- head/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c	Wed Nov  9 21:53:49 2011	(r227409)
+++ head/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c	Wed Nov  9 22:39:44 2011	(r227410)
@@ -908,8 +908,24 @@ ar5416FillCapabilityInfo(struct ath_hal 
 		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;
 }
 

Modified: head/sys/dev/ath/if_ath.c
==============================================================================
--- head/sys/dev/ath/if_ath.c	Wed Nov  9 21:53:49 2011	(r227409)
+++ head/sys/dev/ath/if_ath.c	Wed Nov  9 22:39:44 2011	(r227410)
@@ -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_s
 #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.
 	 */

Modified: head/sys/dev/ath/if_ath_sysctl.c
==============================================================================
--- head/sys/dev/ath/if_ath_sysctl.c	Wed Nov  9 21:53:49 2011	(r227409)
+++ head/sys/dev/ath/if_ath_sysctl.c	Wed Nov  9 22:39:44 2011	(r227410)
@@ -898,4 +898,11 @@ ath_sysctl_hal_attach(struct ath_softc *
 	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");
 }


More information about the svn-src-all mailing list