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
This commit is contained in:
parent
fb1c6a1716
commit
ddbe3036e5
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
/*
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
||||
@ -674,6 +675,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.
|
||||
|
@ -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");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user