mac80211: update to version 2015-06-22
Signed-off-by: Felix Fietkau <nbd@openwrt.org> SVN-Revision: 46198
This commit is contained in:
		| @@ -10,11 +10,11 @@ include $(INCLUDE_DIR)/kernel.mk | ||||
|  | ||||
| PKG_NAME:=mac80211 | ||||
|  | ||||
| PKG_VERSION:=2015-03-09 | ||||
| PKG_RELEASE:=3 | ||||
| PKG_VERSION:=2015-06-22 | ||||
| PKG_RELEASE:=1 | ||||
| PKG_SOURCE_URL:=http://mirror2.openwrt.org/sources | ||||
| PKG_BACKPORT_VERSION:= | ||||
| PKG_MD5SUM:=6d4b04e4ce8a1f54dabfb04f4709453c | ||||
| PKG_MD5SUM:=352b2b46d36a72aadc96161a3cefdb1c | ||||
|  | ||||
| PKG_SOURCE:=compat-wireless-$(PKG_VERSION)$(PKG_BACKPORT_VERSION).tar.bz2 | ||||
| PKG_BUILD_DIR:=$(KERNEL_BUILD_DIR)/compat-wireless-$(PKG_VERSION) | ||||
|   | ||||
| @@ -27,7 +27,7 @@ | ||||
|  	@set -e ; test -f .local-symbols || (						\ | ||||
|  	echo "/--------------"								;\ | ||||
|  	echo "| You shouldn't run make in the backports tree, but only in"		;\ | ||||
| @@ -60,56 +62,60 @@ mrproper: | ||||
| @@ -60,57 +62,61 @@ mrproper: | ||||
|  	echo "| (that isn't currently running.)"					;\ | ||||
|  	echo "\\--"									;\ | ||||
|  	false) | ||||
| @@ -56,11 +56,12 @@ | ||||
| -			done								\ | ||||
| -		) > Kconfig.kernel							;\ | ||||
| -		kver=$$($(MAKE) --no-print-directory -C $(KLIB_BUILD) kernelversion |	\ | ||||
| -			sed 's/^\(\(3\|2\.6\)\.[0-9]\+\).*/\1/;t;d')			;\ | ||||
| -			sed 's/^\(\([3-4]\|2\.6\)\.[0-9]\+\).*/\1/;t;d')		;\ | ||||
| -		test "$$kver" != "" || echo "Kernel version parse failed!"		;\ | ||||
| -		test "$$kver" != ""							;\ | ||||
| -		kvers="$$(seq 14 39 | sed 's/^/2.6./')"					;\ | ||||
| -		kvers="$$kvers $$(seq 0 99 | sed 's/^/3./')"				;\ | ||||
| -		kvers="$$kvers $$(seq 0 19 | sed 's/^/3./')"				;\ | ||||
| -		kvers="$$kvers $$(seq 0 99 | sed 's/^/4./')"				;\ | ||||
| -		print=0									;\ | ||||
| -		for v in $$kvers ; do							\ | ||||
| -			if [ "$$print" = "1" ] ; then					\ | ||||
| @@ -111,11 +112,12 @@ | ||||
| + | ||||
| +Kconfig.versions: Kconfig.kernel | ||||
| +	@kver=$$($(MAKE) --no-print-directory -C $(KLIB_BUILD) kernelversion |	\ | ||||
| +		sed 's/^\(\(3\|2\.6\)\.[0-9]\+\).*/\1/;t;d')			;\ | ||||
| +		sed 's/^\(\([3-4]\|2\.6\)\.[0-9]\+\).*/\1/;t;d')		;\ | ||||
| +	test "$$kver" != "" || echo "Kernel version parse failed!"		;\ | ||||
| +	test "$$kver" != ""							;\ | ||||
| +	kvers="$$(seq 14 39 | sed 's/^/2.6./')"					;\ | ||||
| +	kvers="$$kvers $$(seq 0 99 | sed 's/^/3./')"				;\ | ||||
| +	kvers="$$kvers $$(seq 0 19 | sed 's/^/3./')"				;\ | ||||
| +	kvers="$$kvers $$(seq 0 99 | sed 's/^/4./')"				;\ | ||||
| +	print=0									;\ | ||||
| +	for v in $$kvers ; do							\ | ||||
| +		if [ "$$print" = "1" ] ; then					\ | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| --- a/backport-include/linux/debugfs.h | ||||
| +++ b/backport-include/linux/debugfs.h | ||||
| @@ -3,6 +3,7 @@ | ||||
|  #include_next <linux/debugfs.h> | ||||
| @@ -4,6 +4,7 @@ | ||||
|  #include <linux/version.h> | ||||
|  #include <linux/device.h> | ||||
|  #include <generated/utsrelease.h> | ||||
| +#include <linux/device.h> | ||||
|   | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| --- a/net/mac80211/iface.c | ||||
| +++ b/net/mac80211/iface.c | ||||
| @@ -1858,6 +1858,13 @@ void ieee80211_remove_interfaces(struct | ||||
| @@ -1916,6 +1916,13 @@ void ieee80211_remove_interfaces(struct | ||||
|  	} | ||||
|  	mutex_unlock(&local->iflist_mtx); | ||||
|  	unregister_netdevice_many(&unreg_list); | ||||
|   | ||||
| @@ -1,15 +0,0 @@ | ||||
| --- a/backport-include/asm/barrier.h | ||||
| +++ b/backport-include/asm/barrier.h | ||||
| @@ -1,9 +1,9 @@ | ||||
| -#ifndef __BACKPORT_ASM_GENERIC_BARRIER_H | ||||
| -#define __BACKPORT_ASM_GENERIC_BARRIER_H | ||||
| +#ifndef __BACKPORT_ASM_BARRIER_H | ||||
| +#define __BACKPORT_ASM_BARRIER_H | ||||
|  #include_next <asm/barrier.h> | ||||
|   | ||||
|  #ifndef dma_rmb | ||||
|  #define dma_rmb()	rmb() | ||||
|  #endif | ||||
|   | ||||
| -#endif /* __BACKPORT_ASM_GENERIC_BARRIER_H */ | ||||
| +#endif /* __BACKPORT_ASM_BARRIER_H */ | ||||
| @@ -1,37 +0,0 @@ | ||||
| --- /dev/null | ||||
| +++ b/include/uapi/linux/mpls.h | ||||
| @@ -0,0 +1,34 @@ | ||||
| +#ifndef _UAPI_MPLS_H | ||||
| +#define _UAPI_MPLS_H | ||||
| + | ||||
| +#include <linux/types.h> | ||||
| +#include <asm/byteorder.h> | ||||
| + | ||||
| +/* Reference: RFC 5462, RFC 3032 | ||||
| + * | ||||
| + *  0                   1                   2                   3 | ||||
| + *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 | ||||
| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||||
| + * |                Label                  | TC  |S|       TTL     | | ||||
| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||||
| + * | ||||
| + *	Label:  Label Value, 20 bits | ||||
| + *	TC:     Traffic Class field, 3 bits | ||||
| + *	S:      Bottom of Stack, 1 bit | ||||
| + *	TTL:    Time to Live, 8 bits | ||||
| + */ | ||||
| + | ||||
| +struct mpls_label { | ||||
| +	__be32 entry; | ||||
| +}; | ||||
| + | ||||
| +#define MPLS_LS_LABEL_MASK      0xFFFFF000 | ||||
| +#define MPLS_LS_LABEL_SHIFT     12 | ||||
| +#define MPLS_LS_TC_MASK         0x00000E00 | ||||
| +#define MPLS_LS_TC_SHIFT        9 | ||||
| +#define MPLS_LS_S_MASK          0x00000100 | ||||
| +#define MPLS_LS_S_SHIFT         8 | ||||
| +#define MPLS_LS_TTL_MASK        0x000000FF | ||||
| +#define MPLS_LS_TTL_SHIFT       0 | ||||
| + | ||||
| +#endif /* _UAPI_MPLS_H */ | ||||
| @@ -1,104 +0,0 @@ | ||||
| --- a/drivers/bcma/driver_pci.c | ||||
| +++ b/drivers/bcma/driver_pci.c | ||||
| @@ -282,39 +282,6 @@ void bcma_core_pci_power_save(struct bcm | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(bcma_core_pci_power_save); | ||||
|   | ||||
| -int bcma_core_pci_irq_ctl(struct bcma_bus *bus, struct bcma_device *core, | ||||
| -			  bool enable) | ||||
| -{ | ||||
| -	struct pci_dev *pdev; | ||||
| -	u32 coremask, tmp; | ||||
| -	int err = 0; | ||||
| - | ||||
| -	if (bus->hosttype != BCMA_HOSTTYPE_PCI) { | ||||
| -		/* This bcma device is not on a PCI host-bus. So the IRQs are | ||||
| -		 * not routed through the PCI core. | ||||
| -		 * So we must not enable routing through the PCI core. */ | ||||
| -		goto out; | ||||
| -	} | ||||
| - | ||||
| -	pdev = bus->host_pci; | ||||
| - | ||||
| -	err = pci_read_config_dword(pdev, BCMA_PCI_IRQMASK, &tmp); | ||||
| -	if (err) | ||||
| -		goto out; | ||||
| - | ||||
| -	coremask = BIT(core->core_index) << 8; | ||||
| -	if (enable) | ||||
| -		tmp |= coremask; | ||||
| -	else | ||||
| -		tmp &= ~coremask; | ||||
| - | ||||
| -	err = pci_write_config_dword(pdev, BCMA_PCI_IRQMASK, tmp); | ||||
| - | ||||
| -out: | ||||
| -	return err; | ||||
| -} | ||||
| -EXPORT_SYMBOL_GPL(bcma_core_pci_irq_ctl); | ||||
| - | ||||
|  static void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend) | ||||
|  { | ||||
|  	u32 w; | ||||
| --- a/drivers/bcma/host_pci.c | ||||
| +++ b/drivers/bcma/host_pci.c | ||||
| @@ -351,3 +351,37 @@ void bcma_host_pci_down(struct bcma_bus | ||||
|  		bcma_core_pci_down(&bus->drv_pci[0]); | ||||
|  } | ||||
|  EXPORT_SYMBOL_GPL(bcma_host_pci_down); | ||||
| + | ||||
| +/* See also si_pci_setup */ | ||||
| +int bcma_host_pci_irq_ctl(struct bcma_bus *bus, struct bcma_device *core, | ||||
| +			  bool enable) | ||||
| +{ | ||||
| +	struct pci_dev *pdev; | ||||
| +	u32 coremask, tmp; | ||||
| +	int err = 0; | ||||
| + | ||||
| +	if (bus->hosttype != BCMA_HOSTTYPE_PCI) { | ||||
| +		/* This bcma device is not on a PCI host-bus. So the IRQs are | ||||
| +		 * not routed through the PCI core. | ||||
| +		 * So we must not enable routing through the PCI core. */ | ||||
| +		goto out; | ||||
| +	} | ||||
| + | ||||
| +	pdev = bus->host_pci; | ||||
| + | ||||
| +	err = pci_read_config_dword(pdev, BCMA_PCI_IRQMASK, &tmp); | ||||
| +	if (err) | ||||
| +		goto out; | ||||
| + | ||||
| +	coremask = BIT(core->core_index) << 8; | ||||
| +	if (enable) | ||||
| +		tmp |= coremask; | ||||
| +	else | ||||
| +		tmp &= ~coremask; | ||||
| + | ||||
| +	err = pci_write_config_dword(pdev, BCMA_PCI_IRQMASK, tmp); | ||||
| + | ||||
| +out: | ||||
| +	return err; | ||||
| +} | ||||
| +EXPORT_SYMBOL_GPL(bcma_host_pci_irq_ctl); | ||||
| --- a/drivers/net/wireless/b43/main.c | ||||
| +++ b/drivers/net/wireless/b43/main.c | ||||
| @@ -4866,7 +4866,7 @@ static int b43_wireless_core_init(struct | ||||
|  	switch (dev->dev->bus_type) { | ||||
|  #ifdef CPTCFG_B43_BCMA | ||||
|  	case B43_BUS_BCMA: | ||||
| -		bcma_core_pci_irq_ctl(dev->dev->bdev->bus, | ||||
| +		bcma_host_pci_irq_ctl(dev->dev->bdev->bus, | ||||
|  				      dev->dev->bdev, true); | ||||
|  		bcma_host_pci_up(dev->dev->bdev->bus); | ||||
|  		break; | ||||
| --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c | ||||
| @@ -4959,7 +4959,7 @@ static int brcms_b_up_prep(struct brcms_ | ||||
|  	 * Configure pci/pcmcia here instead of in brcms_c_attach() | ||||
|  	 * to allow mfg hotswap:  down, hotswap (chip power cycle), up. | ||||
|  	 */ | ||||
| -	bcma_core_pci_irq_ctl(wlc_hw->d11core->bus, wlc_hw->d11core, | ||||
| +	bcma_host_pci_irq_ctl(wlc_hw->d11core->bus, wlc_hw->d11core, | ||||
|  			      true); | ||||
|   | ||||
|  	/* | ||||
| @@ -1,6 +1,6 @@ | ||||
| --- a/.local-symbols | ||||
| +++ b/.local-symbols | ||||
| @@ -344,40 +344,3 @@ USB_CDC_PHONET= | ||||
| @@ -448,43 +448,6 @@ USB_CDC_PHONET= | ||||
|  USB_IPHETH= | ||||
|  USB_SIERRA_NET= | ||||
|  USB_VL600= | ||||
| @@ -32,26 +32,18 @@ | ||||
| -BCMA_BLOCKIO= | ||||
| -BCMA_HOST_PCI_POSSIBLE= | ||||
| -BCMA_HOST_PCI= | ||||
| -BCMA_DRIVER_PCI_HOSTMODE= | ||||
| -BCMA_HOST_SOC= | ||||
| -BCMA_DRIVER_PCI= | ||||
| -BCMA_DRIVER_PCI_HOSTMODE= | ||||
| -BCMA_DRIVER_MIPS= | ||||
| -BCMA_SFLASH= | ||||
| -BCMA_NFLASH= | ||||
| -BCMA_DRIVER_GMAC_CMN= | ||||
| -BCMA_DRIVER_GPIO= | ||||
| -BCMA_DEBUG= | ||||
| --- a/Makefile.kernel | ||||
| +++ b/Makefile.kernel | ||||
| @@ -38,8 +38,6 @@ obj-$(CPTCFG_MAC80211) += net/mac80211/ | ||||
|  obj-$(CPTCFG_WLAN) += drivers/net/wireless/ | ||||
|  #obj-$(CPTCFG_BT) += net/bluetooth/ | ||||
|  #obj-$(CPTCFG_BT) += drivers/bluetooth/ | ||||
| -obj-$(CPTCFG_SSB) += drivers/ssb/ | ||||
| -obj-$(CPTCFG_BCMA) += drivers/bcma/ | ||||
|  #obj-$(CPTCFG_ETHERNET) += drivers/net/ethernet/ | ||||
|  obj-$(CPTCFG_USB_NET_RNDIS_WLAN) += drivers/net/usb/ | ||||
|  #obj-$(CPTCFG_NFC) += net/nfc/ | ||||
|  NFC= | ||||
|  NFC_DIGITAL= | ||||
|  NFC_NCI= | ||||
| --- a/drivers/net/wireless/b43/main.c | ||||
| +++ b/drivers/net/wireless/b43/main.c | ||||
| @@ -2866,7 +2866,7 @@ static struct ssb_device *b43_ssb_gpio_d | ||||
| @@ -63,7 +55,7 @@ | ||||
|  	return (bus->chipco.dev ? bus->chipco.dev : bus->pcicore.dev); | ||||
|  #else | ||||
|  	return bus->chipco.dev; | ||||
| @@ -4907,7 +4907,7 @@ static int b43_wireless_core_init(struct | ||||
| @@ -4903,7 +4903,7 @@ static int b43_wireless_core_init(struct | ||||
|  	} | ||||
|  	if (sprom->boardflags_lo & B43_BFL_XTAL_NOSLOW) | ||||
|  		hf |= B43_HF_DSCRQ; /* Disable slowclock requests from ucode. */ | ||||
| @@ -116,12 +108,23 @@ | ||||
| --- a/Kconfig.sources | ||||
| +++ b/Kconfig.sources | ||||
| @@ -9,9 +9,6 @@ source "$BACKPORT_DIR/drivers/net/wirele | ||||
|  #source "$BACKPORT_DIR/drivers/net/ethernet/Kconfig" | ||||
|  source "$BACKPORT_DIR/drivers/net/ethernet/Kconfig" | ||||
|  source "$BACKPORT_DIR/drivers/net/usb/Kconfig" | ||||
|   | ||||
| -source "$BACKPORT_DIR/drivers/ssb/Kconfig" | ||||
| -source "$BACKPORT_DIR/drivers/bcma/Kconfig" | ||||
| - | ||||
|  #source "$BACKPORT_DIR/net/nfc/Kconfig" | ||||
|  source "$BACKPORT_DIR/net/nfc/Kconfig" | ||||
|   | ||||
|  #source "$BACKPORT_DIR/drivers/media/Kconfig" | ||||
|  source "$BACKPORT_DIR/drivers/media/Kconfig" | ||||
| --- a/Makefile.kernel | ||||
| +++ b/Makefile.kernel | ||||
| @@ -38,8 +38,6 @@ obj-$(CPTCFG_MAC80211) += net/mac80211/ | ||||
|  obj-$(CPTCFG_WLAN) += drivers/net/wireless/ | ||||
|  obj-$(CPTCFG_BT) += net/bluetooth/ | ||||
|  obj-$(CPTCFG_BT) += drivers/bluetooth/ | ||||
| -obj-$(CPTCFG_SSB) += drivers/ssb/ | ||||
| -obj-$(CPTCFG_BCMA) += drivers/bcma/ | ||||
|  obj-$(CPTCFG_ETHERNET) += drivers/net/ethernet/ | ||||
|  obj-$(CPTCFG_USB_NET_RNDIS_WLAN) += drivers/net/usb/ | ||||
|  obj-$(CPTCFG_NFC) += net/nfc/ | ||||
|   | ||||
| @@ -1,29 +0,0 @@ | ||||
| --- a/backport-include/linux/wait.h | ||||
| +++ b/backport-include/linux/wait.h | ||||
| @@ -23,7 +23,7 @@ backport_wait_on_bit_io(void *word, int | ||||
|   | ||||
|  #endif | ||||
|   | ||||
| -#if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0) | ||||
| +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,12) | ||||
|  #define WQ_FLAG_WOKEN		0x02 | ||||
|   | ||||
|  #define wait_woken LINUX_BACKPORT(wait_woken) | ||||
| --- a/compat/backport-3.19.c | ||||
| +++ b/compat/backport-3.19.c | ||||
| @@ -15,6 +15,7 @@ | ||||
|  #include <linux/netdevice.h> | ||||
|  #include <linux/debugfs.h> | ||||
|   | ||||
| +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,12) | ||||
|  static inline bool is_kthread_should_stop(void) | ||||
|  { | ||||
|  	return (current->flags & PF_KTHREAD) && kthread_should_stop(); | ||||
| @@ -79,6 +80,7 @@ int woken_wake_function(wait_queue_t *wa | ||||
|  	return default_wake_function(wait, mode, sync, key); | ||||
|  } | ||||
|  EXPORT_SYMBOL(woken_wake_function); | ||||
| +#endif | ||||
|   | ||||
|  #ifdef __BACKPORT_NETDEV_RSS_KEY_FILL | ||||
|  u8 netdev_rss_key[NETDEV_RSS_KEY_LEN]; | ||||
| @@ -0,0 +1,374 @@ | ||||
| --- a/net/mac80211/Makefile | ||||
| +++ b/net/mac80211/Makefile | ||||
| @@ -15,9 +15,7 @@ mac80211-y := \ | ||||
|  	michael.o \ | ||||
|  	tkip.o \ | ||||
|  	aes_ccm.o \ | ||||
| -	aes_gcm.o \ | ||||
|  	aes_cmac.o \ | ||||
| -	aes_gmac.o \ | ||||
|  	cfg.o \ | ||||
|  	ethtool.o \ | ||||
|  	rx.o \ | ||||
| --- a/net/mac80211/aes_gcm.h | ||||
| +++ b/net/mac80211/aes_gcm.h | ||||
| @@ -11,12 +11,28 @@ | ||||
|   | ||||
|  #include <linux/crypto.h> | ||||
|   | ||||
| -void ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad, | ||||
| -			       u8 *data, size_t data_len, u8 *mic); | ||||
| -int ieee80211_aes_gcm_decrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad, | ||||
| -			      u8 *data, size_t data_len, u8 *mic); | ||||
| -struct crypto_aead *ieee80211_aes_gcm_key_setup_encrypt(const u8 key[], | ||||
| -							size_t key_len); | ||||
| -void ieee80211_aes_gcm_key_free(struct crypto_aead *tfm); | ||||
| +static inline void | ||||
| +ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad, | ||||
| +			  u8 *data, size_t data_len, u8 *mic) | ||||
| +{ | ||||
| +} | ||||
| + | ||||
| +static inline int | ||||
| +ieee80211_aes_gcm_decrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad, | ||||
| +			  u8 *data, size_t data_len, u8 *mic) | ||||
| +{ | ||||
| +    return -EOPNOTSUPP; | ||||
| +} | ||||
| + | ||||
| +static inline struct crypto_aead * | ||||
| +ieee80211_aes_gcm_key_setup_encrypt(const u8 key[], size_t key_len) | ||||
| +{ | ||||
| +    return NULL; | ||||
| +} | ||||
| + | ||||
| +static inline void | ||||
| +ieee80211_aes_gcm_key_free(struct crypto_aead *tfm) | ||||
| +{ | ||||
| +} | ||||
|   | ||||
|  #endif /* AES_GCM_H */ | ||||
| --- a/net/mac80211/aes_gmac.h | ||||
| +++ b/net/mac80211/aes_gmac.h | ||||
| @@ -11,10 +11,22 @@ | ||||
|   | ||||
|  #include <linux/crypto.h> | ||||
|   | ||||
| -struct crypto_aead *ieee80211_aes_gmac_key_setup(const u8 key[], | ||||
| -						 size_t key_len); | ||||
| -int ieee80211_aes_gmac(struct crypto_aead *tfm, const u8 *aad, u8 *nonce, | ||||
| -		       const u8 *data, size_t data_len, u8 *mic); | ||||
| -void ieee80211_aes_gmac_key_free(struct crypto_aead *tfm); | ||||
| +static inline struct crypto_aead * | ||||
| +ieee80211_aes_gmac_key_setup(const u8 key[], size_t key_len) | ||||
| +{ | ||||
| +	return NULL; | ||||
| +} | ||||
| + | ||||
| +static inline int | ||||
| +ieee80211_aes_gmac(struct crypto_aead *tfm, const u8 *aad, u8 *nonce, | ||||
| +		   const u8 *data, size_t data_len, u8 *mic) | ||||
| +{ | ||||
| +	return -EOPNOTSUPP; | ||||
| +} | ||||
| + | ||||
| +static inline void | ||||
| +ieee80211_aes_gmac_key_free(struct crypto_aead *tfm) | ||||
| +{ | ||||
| +} | ||||
|   | ||||
|  #endif /* AES_GMAC_H */ | ||||
| --- a/net/mac80211/aes_ccm.c | ||||
| +++ b/net/mac80211/aes_ccm.c | ||||
| @@ -19,86 +19,126 @@ | ||||
|  #include "key.h" | ||||
|  #include "aes_ccm.h" | ||||
|   | ||||
| -void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, | ||||
| +static void aes_ccm_prepare(struct crypto_cipher *tfm, u8 *b_0, u8 *aad, u8 *s_0, | ||||
| +			    u8 *a, u8 *b) | ||||
| +{ | ||||
| +	int i; | ||||
| + | ||||
| +	crypto_cipher_encrypt_one(tfm, b, b_0); | ||||
| + | ||||
| +	/* Extra Authenticate-only data (always two AES blocks) */ | ||||
| +	for (i = 0; i < AES_BLOCK_SIZE; i++) | ||||
| +		aad[i] ^= b[i]; | ||||
| +	crypto_cipher_encrypt_one(tfm, b, aad); | ||||
| + | ||||
| +	aad += AES_BLOCK_SIZE; | ||||
| + | ||||
| +	for (i = 0; i < AES_BLOCK_SIZE; i++) | ||||
| +		aad[i] ^= b[i]; | ||||
| +	crypto_cipher_encrypt_one(tfm, a, aad); | ||||
| + | ||||
| +	/* Mask out bits from auth-only-b_0 */ | ||||
| +	b_0[0] &= 0x07; | ||||
| + | ||||
| +	/* S_0 is used to encrypt T (= MIC) */ | ||||
| +	b_0[14] = 0; | ||||
| +	b_0[15] = 0; | ||||
| +	crypto_cipher_encrypt_one(tfm, s_0, b_0); | ||||
| +} | ||||
| + | ||||
| + | ||||
| +void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *b_0, u8 *aad, | ||||
|  			       u8 *data, size_t data_len, u8 *mic, | ||||
|  			       size_t mic_len) | ||||
|  { | ||||
| -	struct scatterlist assoc, pt, ct[2]; | ||||
| +	int i, j, last_len, num_blocks; | ||||
| +	u8 b[AES_BLOCK_SIZE]; | ||||
| +	u8 s_0[AES_BLOCK_SIZE]; | ||||
| +	u8 e[AES_BLOCK_SIZE]; | ||||
| +	u8 *pos, *cpos; | ||||
| + | ||||
| +	num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE); | ||||
| +	last_len = data_len % AES_BLOCK_SIZE; | ||||
| +	aes_ccm_prepare(tfm, b_0, aad, s_0, b, b); | ||||
| + | ||||
| +	/* Process payload blocks */ | ||||
| +	pos = data; | ||||
| +	cpos = data; | ||||
| +	for (j = 1; j <= num_blocks; j++) { | ||||
| +		int blen = (j == num_blocks && last_len) ? | ||||
| +			last_len : AES_BLOCK_SIZE; | ||||
| + | ||||
| +		/* Authentication followed by encryption */ | ||||
| +		for (i = 0; i < blen; i++) | ||||
| +			b[i] ^= pos[i]; | ||||
| +		crypto_cipher_encrypt_one(tfm, b, b); | ||||
| + | ||||
| +		b_0[14] = (j >> 8) & 0xff; | ||||
| +		b_0[15] = j & 0xff; | ||||
| +		crypto_cipher_encrypt_one(tfm, e, b_0); | ||||
| +		for (i = 0; i < blen; i++) | ||||
| +			*cpos++ = *pos++ ^ e[i]; | ||||
| +	} | ||||
|   | ||||
| -	char aead_req_data[sizeof(struct aead_request) + | ||||
| -			   crypto_aead_reqsize(tfm)] | ||||
| -		__aligned(__alignof__(struct aead_request)); | ||||
| -	struct aead_request *aead_req = (void *) aead_req_data; | ||||
| - | ||||
| -	memset(aead_req, 0, sizeof(aead_req_data)); | ||||
| - | ||||
| -	sg_init_one(&pt, data, data_len); | ||||
| -	sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad)); | ||||
| -	sg_init_table(ct, 2); | ||||
| -	sg_set_buf(&ct[0], data, data_len); | ||||
| -	sg_set_buf(&ct[1], mic, mic_len); | ||||
| - | ||||
| -	aead_request_set_tfm(aead_req, tfm); | ||||
| -	aead_request_set_assoc(aead_req, &assoc, assoc.length); | ||||
| -	aead_request_set_crypt(aead_req, &pt, ct, data_len, b_0); | ||||
| - | ||||
| -	crypto_aead_encrypt(aead_req); | ||||
| +	for (i = 0; i < mic_len; i++) | ||||
| +		mic[i] = b[i] ^ s_0[i]; | ||||
|  } | ||||
|   | ||||
| -int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, | ||||
| +int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *b_0, u8 *aad, | ||||
|  			      u8 *data, size_t data_len, u8 *mic, | ||||
|  			      size_t mic_len) | ||||
|  { | ||||
| -	struct scatterlist assoc, pt, ct[2]; | ||||
| -	char aead_req_data[sizeof(struct aead_request) + | ||||
| -			   crypto_aead_reqsize(tfm)] | ||||
| -		__aligned(__alignof__(struct aead_request)); | ||||
| -	struct aead_request *aead_req = (void *) aead_req_data; | ||||
| - | ||||
| -	if (data_len == 0) | ||||
| -		return -EINVAL; | ||||
| - | ||||
| -	memset(aead_req, 0, sizeof(aead_req_data)); | ||||
| - | ||||
| -	sg_init_one(&pt, data, data_len); | ||||
| -	sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad)); | ||||
| -	sg_init_table(ct, 2); | ||||
| -	sg_set_buf(&ct[0], data, data_len); | ||||
| -	sg_set_buf(&ct[1], mic, mic_len); | ||||
| - | ||||
| -	aead_request_set_tfm(aead_req, tfm); | ||||
| -	aead_request_set_assoc(aead_req, &assoc, assoc.length); | ||||
| -	aead_request_set_crypt(aead_req, ct, &pt, data_len + mic_len, b_0); | ||||
| +	int i, j, last_len, num_blocks; | ||||
| +	u8 *pos, *cpos; | ||||
| +	u8 a[AES_BLOCK_SIZE]; | ||||
| +	u8 b[AES_BLOCK_SIZE]; | ||||
| +	u8 s_0[AES_BLOCK_SIZE]; | ||||
| + | ||||
| +	num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE); | ||||
| +	last_len = data_len % AES_BLOCK_SIZE; | ||||
| +	aes_ccm_prepare(tfm, b_0, aad, s_0, a, b); | ||||
| + | ||||
| +	/* Process payload blocks */ | ||||
| +	cpos = data; | ||||
| +	pos = data; | ||||
| +	for (j = 1; j <= num_blocks; j++) { | ||||
| +		int blen = (j == num_blocks && last_len) ? | ||||
| +			last_len : AES_BLOCK_SIZE; | ||||
| + | ||||
| +		/* Decryption followed by authentication */ | ||||
| +		b_0[14] = (j >> 8) & 0xff; | ||||
| +		b_0[15] = j & 0xff; | ||||
| +		crypto_cipher_encrypt_one(tfm, b, b_0); | ||||
| +		for (i = 0; i < blen; i++) { | ||||
| +			*pos = *cpos++ ^ b[i]; | ||||
| +			a[i] ^= *pos++; | ||||
| +		} | ||||
| +		crypto_cipher_encrypt_one(tfm, a, a); | ||||
| +	} | ||||
| + | ||||
| +	for (i = 0; i < mic_len; i++) { | ||||
| +		if ((mic[i] ^ s_0[i]) != a[i]) | ||||
| +			return -1; | ||||
| +	} | ||||
|   | ||||
| -	return crypto_aead_decrypt(aead_req); | ||||
| +	return 0; | ||||
|  } | ||||
|   | ||||
| -struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[], | ||||
| -						    size_t key_len, | ||||
| -						    size_t mic_len) | ||||
| +struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[], | ||||
| +						      size_t key_len, | ||||
| +						      size_t mic_len) | ||||
|  { | ||||
| -	struct crypto_aead *tfm; | ||||
| -	int err; | ||||
| +	struct crypto_cipher *tfm; | ||||
|   | ||||
| -	tfm = crypto_alloc_aead("ccm(aes)", 0, CRYPTO_ALG_ASYNC); | ||||
| -	if (IS_ERR(tfm)) | ||||
| -		return tfm; | ||||
| - | ||||
| -	err = crypto_aead_setkey(tfm, key, key_len); | ||||
| -	if (err) | ||||
| -		goto free_aead; | ||||
| -	err = crypto_aead_setauthsize(tfm, mic_len); | ||||
| -	if (err) | ||||
| -		goto free_aead; | ||||
| +	tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); | ||||
| +	if (!IS_ERR(tfm)) | ||||
| +		crypto_cipher_setkey(tfm, key, key_len); | ||||
|   | ||||
|  	return tfm; | ||||
| - | ||||
| -free_aead: | ||||
| -	crypto_free_aead(tfm); | ||||
| -	return ERR_PTR(err); | ||||
|  } | ||||
|   | ||||
| -void ieee80211_aes_key_free(struct crypto_aead *tfm) | ||||
| + | ||||
| +void ieee80211_aes_key_free(struct crypto_cipher *tfm) | ||||
|  { | ||||
| -	crypto_free_aead(tfm); | ||||
| +	crypto_free_cipher(tfm); | ||||
|  } | ||||
| --- a/net/mac80211/key.h | ||||
| +++ b/net/mac80211/key.h | ||||
| @@ -84,7 +84,7 @@ struct ieee80211_key { | ||||
|  			 * Management frames. | ||||
|  			 */ | ||||
|  			u8 rx_pn[IEEE80211_NUM_TIDS + 1][IEEE80211_CCMP_PN_LEN]; | ||||
| -			struct crypto_aead *tfm; | ||||
| +			struct crypto_cipher *tfm; | ||||
|  			u32 replays; /* dot11RSNAStatsCCMPReplays */ | ||||
|  		} ccmp; | ||||
|  		struct { | ||||
| --- a/net/mac80211/wpa.c | ||||
| +++ b/net/mac80211/wpa.c | ||||
| @@ -304,7 +304,8 @@ ieee80211_crypto_tkip_decrypt(struct iee | ||||
|  } | ||||
|   | ||||
|   | ||||
| -static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad) | ||||
| +static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad, | ||||
| +				u16 data_len) | ||||
|  { | ||||
|  	__le16 mask_fc; | ||||
|  	int a4_included, mgmt; | ||||
| @@ -334,14 +335,8 @@ static void ccmp_special_blocks(struct s | ||||
|  	else | ||||
|  		qos_tid = 0; | ||||
|   | ||||
| -	/* In CCM, the initial vectors (IV) used for CTR mode encryption and CBC | ||||
| -	 * mode authentication are not allowed to collide, yet both are derived | ||||
| -	 * from this vector b_0. We only set L := 1 here to indicate that the | ||||
| -	 * data size can be represented in (L+1) bytes. The CCM layer will take | ||||
| -	 * care of storing the data length in the top (L+1) bytes and setting | ||||
| -	 * and clearing the other bits as is required to derive the two IVs. | ||||
| -	 */ | ||||
| -	b_0[0] = 0x1; | ||||
| +	/* First block, b_0 */ | ||||
| +	b_0[0] = 0x59; /* flags: Adata: 1, M: 011, L: 001 */ | ||||
|   | ||||
|  	/* Nonce: Nonce Flags | A2 | PN | ||||
|  	 * Nonce Flags: Priority (b0..b3) | Management (b4) | Reserved (b5..b7) | ||||
| @@ -349,6 +344,8 @@ static void ccmp_special_blocks(struct s | ||||
|  	b_0[1] = qos_tid | (mgmt << 4); | ||||
|  	memcpy(&b_0[2], hdr->addr2, ETH_ALEN); | ||||
|  	memcpy(&b_0[8], pn, IEEE80211_CCMP_PN_LEN); | ||||
| +	/* l(m) */ | ||||
| +	put_unaligned_be16(data_len, &b_0[14]); | ||||
|   | ||||
|  	/* AAD (extra authenticate-only data) / masked 802.11 header | ||||
|  	 * FC | A1 | A2 | A3 | SC | [A4] | [QC] */ | ||||
| @@ -460,7 +457,7 @@ static int ccmp_encrypt_skb(struct ieee8 | ||||
|  		return 0; | ||||
|   | ||||
|  	pos += IEEE80211_CCMP_HDR_LEN; | ||||
| -	ccmp_special_blocks(skb, pn, b_0, aad); | ||||
| +	ccmp_special_blocks(skb, pn, b_0, aad, len); | ||||
|  	ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len, | ||||
|  				  skb_put(skb, mic_len), mic_len); | ||||
|   | ||||
| @@ -531,7 +528,7 @@ ieee80211_crypto_ccmp_decrypt(struct iee | ||||
|  			u8 aad[2 * AES_BLOCK_SIZE]; | ||||
|  			u8 b_0[AES_BLOCK_SIZE]; | ||||
|  			/* hardware didn't decrypt/verify MIC */ | ||||
| -			ccmp_special_blocks(skb, pn, b_0, aad); | ||||
| +			ccmp_special_blocks(skb, pn, b_0, aad, data_len); | ||||
|   | ||||
|  			if (ieee80211_aes_ccm_decrypt( | ||||
|  				    key->u.ccmp.tfm, b_0, aad, | ||||
| --- a/net/mac80211/aes_ccm.h | ||||
| +++ b/net/mac80211/aes_ccm.h | ||||
| @@ -12,15 +12,15 @@ | ||||
|   | ||||
|  #include <linux/crypto.h> | ||||
|   | ||||
| -struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[], | ||||
| -						    size_t key_len, | ||||
| -						    size_t mic_len); | ||||
| -void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, | ||||
| +struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[], | ||||
| +						      size_t key_len, | ||||
| +						      size_t mic_len); | ||||
| +void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *b_0, u8 *aad, | ||||
|  			       u8 *data, size_t data_len, u8 *mic, | ||||
|  			       size_t mic_len); | ||||
| -int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, | ||||
| +int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *b_0, u8 *aad, | ||||
|  			      u8 *data, size_t data_len, u8 *mic, | ||||
|  			      size_t mic_len); | ||||
| -void ieee80211_aes_key_free(struct crypto_aead *tfm); | ||||
| +void ieee80211_aes_key_free(struct crypto_cipher *tfm); | ||||
|   | ||||
|  #endif /* AES_CCM_H */ | ||||
| --- a/net/mac80211/Kconfig | ||||
| +++ b/net/mac80211/Kconfig | ||||
| @@ -5,8 +5,6 @@ config MAC80211 | ||||
|  	depends on CRYPTO | ||||
|  	depends on CRYPTO_ARC4 | ||||
|  	depends on CRYPTO_AES | ||||
| -	select BPAUTO_CRYPTO_CCM | ||||
| -	depends on CRYPTO_GCM | ||||
|  	depends on CRC32 | ||||
|  	select BPAUTO_AVERAGE | ||||
|  	---help--- | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -2,7 +2,7 @@ Used for AP+STA support in OpenWrt - preserve AP mode keys across STA reconnects | ||||
|  | ||||
| --- a/net/mac80211/cfg.c | ||||
| +++ b/net/mac80211/cfg.c | ||||
| @@ -856,7 +856,6 @@ static int ieee80211_stop_ap(struct wiph | ||||
| @@ -886,7 +886,6 @@ static int ieee80211_stop_ap(struct wiph | ||||
|  	sdata->u.ap.driver_smps_mode = IEEE80211_SMPS_OFF; | ||||
|   | ||||
|  	__sta_info_flush(sdata, true); | ||||
|   | ||||
| @@ -18,7 +18,7 @@ | ||||
|  static int ieee80211_ifa6_changed(struct notifier_block *nb, | ||||
|  				  unsigned long data, void *arg) | ||||
|  { | ||||
| @@ -1057,14 +1057,14 @@ int ieee80211_register_hw(struct ieee802 | ||||
| @@ -1086,14 +1086,14 @@ int ieee80211_register_hw(struct ieee802 | ||||
|  	if (result) | ||||
|  		goto fail_pm_qos; | ||||
|   | ||||
| @@ -35,7 +35,7 @@ | ||||
|  	local->ifa6_notifier.notifier_call = ieee80211_ifa6_changed; | ||||
|  	result = register_inet6addr_notifier(&local->ifa6_notifier); | ||||
|  	if (result) | ||||
| @@ -1073,13 +1073,13 @@ int ieee80211_register_hw(struct ieee802 | ||||
| @@ -1102,13 +1102,13 @@ int ieee80211_register_hw(struct ieee802 | ||||
|   | ||||
|  	return 0; | ||||
|   | ||||
| @@ -52,7 +52,7 @@ | ||||
|   fail_ifa: | ||||
|  	pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY, | ||||
|  			       &local->network_latency_notifier); | ||||
| @@ -1124,10 +1124,10 @@ void ieee80211_unregister_hw(struct ieee | ||||
| @@ -1141,10 +1141,10 @@ void ieee80211_unregister_hw(struct ieee | ||||
|   | ||||
|  	pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY, | ||||
|  			       &local->network_latency_notifier); | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| --- a/net/mac80211/cfg.c | ||||
| +++ b/net/mac80211/cfg.c | ||||
| @@ -1963,7 +1963,7 @@ static int ieee80211_scan(struct wiphy * | ||||
| @@ -2008,7 +2008,7 @@ static int ieee80211_scan(struct wiphy * | ||||
|  		 * the  frames sent while scanning on other channel will be | ||||
|  		 * lost) | ||||
|  		 */ | ||||
|   | ||||
| @@ -1,882 +0,0 @@ | ||||
| From: Felix Fietkau <nbd@openwrt.org> | ||||
| Date: Tue, 18 Nov 2014 23:58:51 +0100 | ||||
| Subject: [PATCH] mac80211: add an intermediate software queue implementation | ||||
|  | ||||
| This allows drivers to request per-vif and per-sta-tid queues from which | ||||
| they can pull frames. This makes it easier to keep the hardware queues | ||||
| short, and to improve fairness between clients and vifs. | ||||
|  | ||||
| The task of scheduling packet transmission is left up to the driver - | ||||
| queueing is controlled by mac80211. Drivers can only dequeue packets by | ||||
| calling ieee80211_tx_dequeue. This makes it possible to add active queue | ||||
| management later without changing drivers using this code. | ||||
|  | ||||
| This can also be used as a starting point to implement A-MSDU | ||||
| aggregation in a way that does not add artificially induced latency. | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@openwrt.org> | ||||
| --- | ||||
|  | ||||
| --- a/include/net/mac80211.h | ||||
| +++ b/include/net/mac80211.h | ||||
| @@ -84,6 +84,39 @@ | ||||
|   * | ||||
|   */ | ||||
|   | ||||
| +/** | ||||
| + * DOC: mac80211 software tx queueing | ||||
| + * | ||||
| + * mac80211 provides an optional intermediate queueing implementation designed | ||||
| + * to allow the driver to keep hardware queues short and provide some fairness | ||||
| + * between different stations/interfaces. | ||||
| + * In this model, the driver pulls data frames from the mac80211 queue instead | ||||
| + * of letting mac80211 push them via drv_tx(). | ||||
| + * Other frames (e.g. control or management) are still pushed using drv_tx(). | ||||
| + * | ||||
| + * Drivers indicate that they use this model by implementing the .wake_tx_queue | ||||
| + * driver operation. | ||||
| + * | ||||
| + * Intermediate queues (struct ieee80211_txq) are kept per-sta per-tid, with a | ||||
| + * single per-vif queue for multicast data frames. | ||||
| + * | ||||
| + * The driver is expected to initialize its private per-queue data for stations | ||||
| + * and interfaces in the .add_interface and .sta_add ops. | ||||
| + * | ||||
| + * The driver can't access the queue directly. To dequeue a frame, it calls | ||||
| + * ieee80211_tx_dequeue(). Whenever mac80211 adds a new frame to a queue, it | ||||
| + * calls the .wake_tx_queue driver op. | ||||
| + * | ||||
| + * For AP powersave TIM handling, the driver only needs to indicate if it has | ||||
| + * buffered packets in the driver specific data structures by calling | ||||
| + * ieee80211_sta_set_buffered(). For frames buffered in the ieee80211_txq | ||||
| + * struct, mac80211 sets the appropriate TIM PVB bits and calls | ||||
| + * .release_buffered_frames(). | ||||
| + * In that callback the driver is therefore expected to release its own | ||||
| + * buffered frames and afterwards also frames from the ieee80211_txq (obtained | ||||
| + * via the usual ieee80211_tx_dequeue). | ||||
| + */ | ||||
| + | ||||
|  struct device; | ||||
|   | ||||
|  /** | ||||
| @@ -1246,6 +1279,7 @@ enum ieee80211_vif_flags { | ||||
|   *	monitor interface (if that is requested.) | ||||
|   * @drv_priv: data area for driver use, will always be aligned to | ||||
|   *	sizeof(void *). | ||||
| + * @txq: the multicast data TX queue (if driver uses the TXQ abstraction) | ||||
|   */ | ||||
|  struct ieee80211_vif { | ||||
|  	enum nl80211_iftype type; | ||||
| @@ -1257,6 +1291,8 @@ struct ieee80211_vif { | ||||
|  	u8 cab_queue; | ||||
|  	u8 hw_queue[IEEE80211_NUM_ACS]; | ||||
|   | ||||
| +	struct ieee80211_txq *txq; | ||||
| + | ||||
|  	struct ieee80211_chanctx_conf __rcu *chanctx_conf; | ||||
|   | ||||
|  	u32 driver_flags; | ||||
| @@ -1501,6 +1537,7 @@ struct ieee80211_sta_rates { | ||||
|   * @tdls_initiator: indicates the STA is an initiator of the TDLS link. Only | ||||
|   *	valid if the STA is a TDLS peer in the first place. | ||||
|   * @mfp: indicates whether the STA uses management frame protection or not. | ||||
| + * @txq: per-TID data TX queues (if driver uses the TXQ abstraction) | ||||
|   */ | ||||
|  struct ieee80211_sta { | ||||
|  	u32 supp_rates[IEEE80211_NUM_BANDS]; | ||||
| @@ -1519,6 +1556,8 @@ struct ieee80211_sta { | ||||
|  	bool tdls_initiator; | ||||
|  	bool mfp; | ||||
|   | ||||
| +	struct ieee80211_txq *txq[IEEE80211_NUM_TIDS]; | ||||
| + | ||||
|  	/* must be last */ | ||||
|  	u8 drv_priv[0] __aligned(sizeof(void *)); | ||||
|  }; | ||||
| @@ -1547,6 +1586,27 @@ struct ieee80211_tx_control { | ||||
|  }; | ||||
|   | ||||
|  /** | ||||
| + * struct ieee80211_txq - Software intermediate tx queue | ||||
| + * | ||||
| + * @vif: &struct ieee80211_vif pointer from the add_interface callback. | ||||
| + * @sta: station table entry, %NULL for per-vif queue | ||||
| + * @tid: the TID for this queue (unused for per-vif queue) | ||||
| + * @ac: the AC for this queue | ||||
| + * | ||||
| + * The driver can obtain packets from this queue by calling | ||||
| + * ieee80211_tx_dequeue(). | ||||
| + */ | ||||
| +struct ieee80211_txq { | ||||
| +	struct ieee80211_vif *vif; | ||||
| +	struct ieee80211_sta *sta; | ||||
| +	u8 tid; | ||||
| +	u8 ac; | ||||
| + | ||||
| +	/* must be last */ | ||||
| +	u8 drv_priv[0] __aligned(sizeof(void *)); | ||||
| +}; | ||||
| + | ||||
| +/** | ||||
|   * enum ieee80211_hw_flags - hardware flags | ||||
|   * | ||||
|   * These flags are used to indicate hardware capabilities to | ||||
| @@ -1770,6 +1830,8 @@ enum ieee80211_hw_flags { | ||||
|   *	within &struct ieee80211_sta. | ||||
|   * @chanctx_data_size: size (in bytes) of the drv_priv data area | ||||
|   *	within &struct ieee80211_chanctx_conf. | ||||
| + * @txq_data_size: size (in bytes) of the drv_priv data area | ||||
| + *	within @struct ieee80211_txq. | ||||
|   * | ||||
|   * @max_rates: maximum number of alternate rate retry stages the hw | ||||
|   *	can handle. | ||||
| @@ -1818,6 +1880,9 @@ enum ieee80211_hw_flags { | ||||
|   * @n_cipher_schemes: a size of an array of cipher schemes definitions. | ||||
|   * @cipher_schemes: a pointer to an array of cipher scheme definitions | ||||
|   *	supported by HW. | ||||
| + * | ||||
| + * @txq_ac_max_pending: maximum number of frames per AC pending in all txq | ||||
| + *	entries for a vif. | ||||
|   */ | ||||
|  struct ieee80211_hw { | ||||
|  	struct ieee80211_conf conf; | ||||
| @@ -1830,6 +1895,7 @@ struct ieee80211_hw { | ||||
|  	int vif_data_size; | ||||
|  	int sta_data_size; | ||||
|  	int chanctx_data_size; | ||||
| +	int txq_data_size; | ||||
|  	u16 queues; | ||||
|  	u16 max_listen_interval; | ||||
|  	s8 max_signal; | ||||
| @@ -1846,6 +1912,7 @@ struct ieee80211_hw { | ||||
|  	u8 uapsd_max_sp_len; | ||||
|  	u8 n_cipher_schemes; | ||||
|  	const struct ieee80211_cipher_scheme *cipher_schemes; | ||||
| +	int txq_ac_max_pending; | ||||
|  }; | ||||
|   | ||||
|  /** | ||||
| @@ -3007,6 +3074,8 @@ enum ieee80211_reconfig_type { | ||||
|   *	response template is provided, together with the location of the | ||||
|   *	switch-timing IE within the template. The skb can only be used within | ||||
|   *	the function call. | ||||
| + * | ||||
| + * @wake_tx_queue: Called when new packets have been added to the queue. | ||||
|   */ | ||||
|  struct ieee80211_ops { | ||||
|  	void (*tx)(struct ieee80211_hw *hw, | ||||
| @@ -3238,6 +3307,9 @@ struct ieee80211_ops { | ||||
|  	void (*tdls_recv_channel_switch)(struct ieee80211_hw *hw, | ||||
|  					 struct ieee80211_vif *vif, | ||||
|  					 struct ieee80211_tdls_ch_sw_params *params); | ||||
| + | ||||
| +	void (*wake_tx_queue)(struct ieee80211_hw *hw, | ||||
| +			      struct ieee80211_txq *txq); | ||||
|  }; | ||||
|   | ||||
|  /** | ||||
| @@ -5249,4 +5321,15 @@ void ieee80211_unreserve_tid(struct ieee | ||||
|   */ | ||||
|  size_t ieee80211_ie_split(const u8 *ies, size_t ielen, | ||||
|  			  const u8 *ids, int n_ids, size_t offset); | ||||
| + | ||||
| +/** | ||||
| + * ieee80211_tx_dequeue - dequeue a packet from a software tx queue | ||||
| + * | ||||
| + * @hw: pointer as obtained from ieee80211_alloc_hw() | ||||
| + * @txq: pointer obtained from station or virtual interface | ||||
| + * | ||||
| + * Returns the skb if successful, %NULL if no frame was available. | ||||
| + */ | ||||
| +struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw, | ||||
| +				     struct ieee80211_txq *txq); | ||||
|  #endif /* MAC80211_H */ | ||||
| --- a/net/mac80211/driver-ops.h | ||||
| +++ b/net/mac80211/driver-ops.h | ||||
| @@ -1367,4 +1367,16 @@ drv_tdls_recv_channel_switch(struct ieee | ||||
|  	trace_drv_return_void(local); | ||||
|  } | ||||
|   | ||||
| +static inline void drv_wake_tx_queue(struct ieee80211_local *local, | ||||
| +				     struct txq_info *txq) | ||||
| +{ | ||||
| +	struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->txq.vif); | ||||
| + | ||||
| +	if (!check_sdata_in_driver(sdata)) | ||||
| +		return; | ||||
| + | ||||
| +	trace_drv_wake_tx_queue(local, sdata, txq); | ||||
| +	local->ops->wake_tx_queue(&local->hw, &txq->txq); | ||||
| +} | ||||
| + | ||||
|  #endif /* __MAC80211_DRIVER_OPS */ | ||||
| --- a/net/mac80211/ieee80211_i.h | ||||
| +++ b/net/mac80211/ieee80211_i.h | ||||
| @@ -809,6 +809,19 @@ struct mac80211_qos_map { | ||||
|  	struct rcu_head rcu_head; | ||||
|  }; | ||||
|   | ||||
| +enum txq_info_flags { | ||||
| +	IEEE80211_TXQ_STOP, | ||||
| +	IEEE80211_TXQ_AMPDU, | ||||
| +}; | ||||
| + | ||||
| +struct txq_info { | ||||
| +	struct sk_buff_head queue; | ||||
| +	unsigned long flags; | ||||
| + | ||||
| +	/* keep last! */ | ||||
| +	struct ieee80211_txq txq; | ||||
| +}; | ||||
| + | ||||
|  struct ieee80211_sub_if_data { | ||||
|  	struct list_head list; | ||||
|   | ||||
| @@ -853,6 +866,7 @@ struct ieee80211_sub_if_data { | ||||
|  	bool control_port_no_encrypt; | ||||
|  	int encrypt_headroom; | ||||
|   | ||||
| +	atomic_t txqs_len[IEEE80211_NUM_ACS]; | ||||
|  	struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS]; | ||||
|  	struct mac80211_qos_map __rcu *qos_map; | ||||
|   | ||||
| @@ -1453,6 +1467,10 @@ static inline struct ieee80211_local *hw | ||||
|  	return container_of(hw, struct ieee80211_local, hw); | ||||
|  } | ||||
|   | ||||
| +static inline struct txq_info *to_txq_info(struct ieee80211_txq *txq) | ||||
| +{ | ||||
| +	return container_of(txq, struct txq_info, txq); | ||||
| +} | ||||
|   | ||||
|  static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr) | ||||
|  { | ||||
| @@ -1905,6 +1923,9 @@ static inline bool ieee80211_can_run_wor | ||||
|  	return true; | ||||
|  } | ||||
|   | ||||
| +void ieee80211_init_tx_queue(struct ieee80211_sub_if_data *sdata, | ||||
| +			     struct sta_info *sta, | ||||
| +			     struct txq_info *txq, int tid); | ||||
|  void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, | ||||
|  			 u16 transaction, u16 auth_alg, u16 status, | ||||
|  			 const u8 *extra, size_t extra_len, const u8 *bssid, | ||||
| --- a/net/mac80211/iface.c | ||||
| +++ b/net/mac80211/iface.c | ||||
| @@ -969,6 +969,13 @@ static void ieee80211_do_stop(struct iee | ||||
|  	} | ||||
|  	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); | ||||
|   | ||||
| +	if (sdata->vif.txq) { | ||||
| +		struct txq_info *txqi = to_txq_info(sdata->vif.txq); | ||||
| + | ||||
| +		ieee80211_purge_tx_queue(&local->hw, &txqi->queue); | ||||
| +		atomic_set(&sdata->txqs_len[txqi->txq.ac], 0); | ||||
| +	} | ||||
| + | ||||
|  	if (local->open_count == 0) | ||||
|  		ieee80211_clear_tx_pending(local); | ||||
|   | ||||
| @@ -1674,6 +1681,7 @@ int ieee80211_if_add(struct ieee80211_lo | ||||
|  { | ||||
|  	struct net_device *ndev = NULL; | ||||
|  	struct ieee80211_sub_if_data *sdata = NULL; | ||||
| +	struct txq_info *txqi; | ||||
|  	int ret, i; | ||||
|  	int txqs = 1; | ||||
|   | ||||
| @@ -1693,10 +1701,18 @@ int ieee80211_if_add(struct ieee80211_lo | ||||
|  		ieee80211_assign_perm_addr(local, wdev->address, type); | ||||
|  		memcpy(sdata->vif.addr, wdev->address, ETH_ALEN); | ||||
|  	} else { | ||||
| +		int size = ALIGN(sizeof(*sdata) + local->hw.vif_data_size, | ||||
| +				 sizeof(void *)); | ||||
| +		int txq_size = 0; | ||||
| + | ||||
| +		if (local->ops->wake_tx_queue) | ||||
| +			txq_size += sizeof(struct txq_info) + | ||||
| +				    local->hw.txq_data_size; | ||||
| + | ||||
|  		if (local->hw.queues >= IEEE80211_NUM_ACS) | ||||
|  			txqs = IEEE80211_NUM_ACS; | ||||
|   | ||||
| -		ndev = alloc_netdev_mqs(sizeof(*sdata) + local->hw.vif_data_size, | ||||
| +		ndev = alloc_netdev_mqs(size + txq_size, | ||||
|  					name, NET_NAME_UNKNOWN, | ||||
|  					ieee80211_if_setup, txqs, 1); | ||||
|  		if (!ndev) | ||||
| @@ -1731,6 +1747,11 @@ int ieee80211_if_add(struct ieee80211_lo | ||||
|  		memcpy(sdata->vif.addr, ndev->dev_addr, ETH_ALEN); | ||||
|  		memcpy(sdata->name, ndev->name, IFNAMSIZ); | ||||
|   | ||||
| +		if (txq_size) { | ||||
| +			txqi = netdev_priv(ndev) + size; | ||||
| +			ieee80211_init_tx_queue(sdata, NULL, txqi, 0); | ||||
| +		} | ||||
| + | ||||
|  		sdata->dev = ndev; | ||||
|  	} | ||||
|   | ||||
| --- a/net/mac80211/main.c | ||||
| +++ b/net/mac80211/main.c | ||||
| @@ -1019,6 +1019,9 @@ int ieee80211_register_hw(struct ieee802 | ||||
|   | ||||
|  	local->dynamic_ps_forced_timeout = -1; | ||||
|   | ||||
| +	if (!local->hw.txq_ac_max_pending) | ||||
| +		local->hw.txq_ac_max_pending = 64; | ||||
| + | ||||
|  	result = ieee80211_wep_init(local); | ||||
|  	if (result < 0) | ||||
|  		wiphy_debug(local->hw.wiphy, "Failed to initialize wep: %d\n", | ||||
| --- a/net/mac80211/sta_info.c | ||||
| +++ b/net/mac80211/sta_info.c | ||||
| @@ -118,6 +118,16 @@ static void __cleanup_single_sta(struct | ||||
|  		atomic_dec(&ps->num_sta_ps); | ||||
|  	} | ||||
|   | ||||
| +	if (sta->sta.txq[0]) { | ||||
| +		for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { | ||||
| +			struct txq_info *txqi = to_txq_info(sta->sta.txq[i]); | ||||
| +			int n = skb_queue_len(&txqi->queue); | ||||
| + | ||||
| +			ieee80211_purge_tx_queue(&local->hw, &txqi->queue); | ||||
| +			atomic_sub(n, &sdata->txqs_len[txqi->txq.ac]); | ||||
| +		} | ||||
| +	} | ||||
| + | ||||
|  	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { | ||||
|  		local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]); | ||||
|  		ieee80211_purge_tx_queue(&local->hw, &sta->ps_tx_buf[ac]); | ||||
| @@ -234,6 +244,8 @@ void sta_info_free(struct ieee80211_loca | ||||
|   | ||||
|  	sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr); | ||||
|   | ||||
| +	if (sta->sta.txq[0]) | ||||
| +		kfree(to_txq_info(sta->sta.txq[0])); | ||||
|  	kfree(rcu_dereference_raw(sta->sta.rates)); | ||||
|  	kfree(sta); | ||||
|  } | ||||
| @@ -285,11 +297,12 @@ struct sta_info *sta_info_alloc(struct i | ||||
|  				const u8 *addr, gfp_t gfp) | ||||
|  { | ||||
|  	struct ieee80211_local *local = sdata->local; | ||||
| +	struct ieee80211_hw *hw = &local->hw; | ||||
|  	struct sta_info *sta; | ||||
|  	struct timespec uptime; | ||||
|  	int i; | ||||
|   | ||||
| -	sta = kzalloc(sizeof(*sta) + local->hw.sta_data_size, gfp); | ||||
| +	sta = kzalloc(sizeof(*sta) + hw->sta_data_size, gfp); | ||||
|  	if (!sta) | ||||
|  		return NULL; | ||||
|   | ||||
| @@ -321,11 +334,25 @@ struct sta_info *sta_info_alloc(struct i | ||||
|  	for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++) | ||||
|  		ewma_init(&sta->chain_signal_avg[i], 1024, 8); | ||||
|   | ||||
| -	if (sta_prepare_rate_control(local, sta, gfp)) { | ||||
| -		kfree(sta); | ||||
| -		return NULL; | ||||
| +	if (local->ops->wake_tx_queue) { | ||||
| +		void *txq_data; | ||||
| +		int size = sizeof(struct txq_info) + | ||||
| +			   ALIGN(hw->txq_data_size, sizeof(void *)); | ||||
| + | ||||
| +		txq_data = kcalloc(ARRAY_SIZE(sta->sta.txq), size, gfp); | ||||
| +		if (!txq_data) | ||||
| +			goto free; | ||||
| + | ||||
| +		for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { | ||||
| +			struct txq_info *txq = txq_data + i * size; | ||||
| + | ||||
| +			ieee80211_init_tx_queue(sdata, sta, txq, i); | ||||
| +		} | ||||
|  	} | ||||
|   | ||||
| +	if (sta_prepare_rate_control(local, sta, gfp)) | ||||
| +		goto free_txq; | ||||
| + | ||||
|  	for (i = 0; i < IEEE80211_NUM_TIDS; i++) { | ||||
|  		/* | ||||
|  		 * timer_to_tid must be initialized with identity mapping | ||||
| @@ -346,7 +373,7 @@ struct sta_info *sta_info_alloc(struct i | ||||
|  	if (sdata->vif.type == NL80211_IFTYPE_AP || | ||||
|  	    sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { | ||||
|  		struct ieee80211_supported_band *sband = | ||||
| -			local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)]; | ||||
| +			hw->wiphy->bands[ieee80211_get_sdata_band(sdata)]; | ||||
|  		u8 smps = (sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> | ||||
|  				IEEE80211_HT_CAP_SM_PS_SHIFT; | ||||
|  		/* | ||||
| @@ -371,6 +398,13 @@ struct sta_info *sta_info_alloc(struct i | ||||
|  	sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr); | ||||
|   | ||||
|  	return sta; | ||||
| + | ||||
| +free_txq: | ||||
| +	if (sta->sta.txq[0]) | ||||
| +		kfree(to_txq_info(sta->sta.txq[0])); | ||||
| +free: | ||||
| +	kfree(sta); | ||||
| +	return NULL; | ||||
|  } | ||||
|   | ||||
|  static int sta_info_insert_check(struct sta_info *sta) | ||||
| @@ -640,6 +674,8 @@ static void __sta_info_recalc_tim(struct | ||||
|   | ||||
|  		indicate_tim |= | ||||
|  			sta->driver_buffered_tids & tids; | ||||
| +		indicate_tim |= | ||||
| +			sta->txq_buffered_tids & tids; | ||||
|  	} | ||||
|   | ||||
|   done: | ||||
| @@ -1071,7 +1107,7 @@ void ieee80211_sta_ps_deliver_wakeup(str | ||||
|  	struct ieee80211_sub_if_data *sdata = sta->sdata; | ||||
|  	struct ieee80211_local *local = sdata->local; | ||||
|  	struct sk_buff_head pending; | ||||
| -	int filtered = 0, buffered = 0, ac; | ||||
| +	int filtered = 0, buffered = 0, ac, i; | ||||
|  	unsigned long flags; | ||||
|  	struct ps_data *ps; | ||||
|   | ||||
| @@ -1090,10 +1126,22 @@ void ieee80211_sta_ps_deliver_wakeup(str | ||||
|   | ||||
|  	BUILD_BUG_ON(BITS_TO_LONGS(IEEE80211_NUM_TIDS) > 1); | ||||
|  	sta->driver_buffered_tids = 0; | ||||
| +	sta->txq_buffered_tids = 0; | ||||
|   | ||||
|  	if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS)) | ||||
|  		drv_sta_notify(local, sdata, STA_NOTIFY_AWAKE, &sta->sta); | ||||
|   | ||||
| +	if (sta->sta.txq[0]) { | ||||
| +		for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { | ||||
| +			struct txq_info *txqi = to_txq_info(sta->sta.txq[i]); | ||||
| + | ||||
| +			if (!skb_queue_len(&txqi->queue)) | ||||
| +				continue; | ||||
| + | ||||
| +			drv_wake_tx_queue(local, txqi); | ||||
| +		} | ||||
| +	} | ||||
| + | ||||
|  	skb_queue_head_init(&pending); | ||||
|   | ||||
|  	/* sync with ieee80211_tx_h_unicast_ps_buf */ | ||||
| @@ -1275,8 +1323,10 @@ ieee80211_sta_ps_deliver_response(struct | ||||
|  		/* if we already have frames from software, then we can't also | ||||
|  		 * release from hardware queues | ||||
|  		 */ | ||||
| -		if (skb_queue_empty(&frames)) | ||||
| +		if (skb_queue_empty(&frames)) { | ||||
|  			driver_release_tids |= sta->driver_buffered_tids & tids; | ||||
| +			driver_release_tids |= sta->txq_buffered_tids & tids; | ||||
| +		} | ||||
|   | ||||
|  		if (driver_release_tids) { | ||||
|  			/* If the driver has data on more than one TID then | ||||
| @@ -1447,6 +1497,9 @@ ieee80211_sta_ps_deliver_response(struct | ||||
|   | ||||
|  		sta_info_recalc_tim(sta); | ||||
|  	} else { | ||||
| +		unsigned long tids = sta->txq_buffered_tids & driver_release_tids; | ||||
| +		int tid; | ||||
| + | ||||
|  		/* | ||||
|  		 * We need to release a frame that is buffered somewhere in the | ||||
|  		 * driver ... it'll have to handle that. | ||||
| @@ -1466,8 +1519,22 @@ ieee80211_sta_ps_deliver_response(struct | ||||
|  		 * that the TID(s) became empty before returning here from the | ||||
|  		 * release function. | ||||
|  		 * Either way, however, when the driver tells us that the TID(s) | ||||
| -		 * became empty we'll do the TIM recalculation. | ||||
| +		 * became empty or we find that a txq became empty, we'll do the | ||||
| +		 * TIM recalculation. | ||||
|  		 */ | ||||
| + | ||||
| +		if (!sta->sta.txq[0]) | ||||
| +			return; | ||||
| + | ||||
| +		for (tid = 0; tid < ARRAY_SIZE(sta->sta.txq); tid++) { | ||||
| +			struct txq_info *txqi = to_txq_info(sta->sta.txq[tid]); | ||||
| + | ||||
| +			if (!(tids & BIT(tid)) || skb_queue_len(&txqi->queue)) | ||||
| +				continue; | ||||
| + | ||||
| +			sta_info_recalc_tim(sta); | ||||
| +			break; | ||||
| +		} | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| --- a/net/mac80211/sta_info.h | ||||
| +++ b/net/mac80211/sta_info.h | ||||
| @@ -274,6 +274,7 @@ struct sta_ampdu_mlme { | ||||
|   *	entered power saving state, these are also delivered to | ||||
|   *	the station when it leaves powersave or polls for frames | ||||
|   * @driver_buffered_tids: bitmap of TIDs the driver has data buffered on | ||||
| + * @txq_buffered_tids: bitmap of TIDs that mac80211 has txq data buffered on | ||||
|   * @rx_packets: Number of MSDUs received from this STA | ||||
|   * @rx_bytes: Number of bytes received from this STA | ||||
|   * @last_rx: time (in jiffies) when last frame was received from this STA | ||||
| @@ -368,6 +369,7 @@ struct sta_info { | ||||
|  	struct sk_buff_head ps_tx_buf[IEEE80211_NUM_ACS]; | ||||
|  	struct sk_buff_head tx_filtered[IEEE80211_NUM_ACS]; | ||||
|  	unsigned long driver_buffered_tids; | ||||
| +	unsigned long txq_buffered_tids; | ||||
|   | ||||
|  	/* Updated from RX path only, no locking requirements */ | ||||
|  	unsigned long rx_packets; | ||||
| --- a/net/mac80211/trace.h | ||||
| +++ b/net/mac80211/trace.h | ||||
| @@ -2312,6 +2312,37 @@ TRACE_EVENT(drv_tdls_recv_channel_switch | ||||
|  	) | ||||
|  ); | ||||
|   | ||||
| +TRACE_EVENT(drv_wake_tx_queue, | ||||
| +	TP_PROTO(struct ieee80211_local *local, | ||||
| +		 struct ieee80211_sub_if_data *sdata, | ||||
| +		 struct txq_info *txq), | ||||
| + | ||||
| +	TP_ARGS(local, sdata, txq), | ||||
| + | ||||
| +	TP_STRUCT__entry( | ||||
| +		LOCAL_ENTRY | ||||
| +		VIF_ENTRY | ||||
| +		STA_ENTRY | ||||
| +		__field(u8, ac) | ||||
| +		__field(u8, tid) | ||||
| +	), | ||||
| + | ||||
| +	TP_fast_assign( | ||||
| +		struct ieee80211_sta *sta = txq->txq.sta; | ||||
| + | ||||
| +		LOCAL_ASSIGN; | ||||
| +		VIF_ASSIGN; | ||||
| +		STA_ASSIGN; | ||||
| +		__entry->ac = txq->txq.ac; | ||||
| +		__entry->tid = txq->txq.tid; | ||||
| +	), | ||||
| + | ||||
| +	TP_printk( | ||||
| +		LOCAL_PR_FMT  VIF_PR_FMT  STA_PR_FMT " ac:%d tid:%d", | ||||
| +		LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->ac, __entry->tid | ||||
| +	) | ||||
| +); | ||||
| + | ||||
|  #ifdef CPTCFG_MAC80211_MESSAGE_TRACING | ||||
|  #undef TRACE_SYSTEM | ||||
|  #define TRACE_SYSTEM mac80211_msg | ||||
| --- a/net/mac80211/tx.c | ||||
| +++ b/net/mac80211/tx.c | ||||
| @@ -776,12 +776,22 @@ ieee80211_tx_h_rate_ctrl(struct ieee8021 | ||||
|  	return TX_CONTINUE; | ||||
|  } | ||||
|   | ||||
| +static __le16 ieee80211_tx_next_seq(struct sta_info *sta, int tid) | ||||
| +{ | ||||
| +	u16 *seq = &sta->tid_seq[tid]; | ||||
| +	__le16 ret = cpu_to_le16(*seq); | ||||
| + | ||||
| +	/* Increase the sequence number. */ | ||||
| +	*seq = (*seq + 0x10) & IEEE80211_SCTL_SEQ; | ||||
| + | ||||
| +	return ret; | ||||
| +} | ||||
| + | ||||
|  static ieee80211_tx_result debug_noinline | ||||
|  ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx) | ||||
|  { | ||||
|  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); | ||||
|  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; | ||||
| -	u16 *seq; | ||||
|  	u8 *qc; | ||||
|  	int tid; | ||||
|   | ||||
| @@ -832,13 +842,10 @@ ieee80211_tx_h_sequence(struct ieee80211 | ||||
|   | ||||
|  	qc = ieee80211_get_qos_ctl(hdr); | ||||
|  	tid = *qc & IEEE80211_QOS_CTL_TID_MASK; | ||||
| -	seq = &tx->sta->tid_seq[tid]; | ||||
|  	tx->sta->tx_msdu[tid]++; | ||||
|   | ||||
| -	hdr->seq_ctrl = cpu_to_le16(*seq); | ||||
| - | ||||
| -	/* Increase the sequence number. */ | ||||
| -	*seq = (*seq + 0x10) & IEEE80211_SCTL_SEQ; | ||||
| +	if (!tx->sta->sta.txq[0]) | ||||
| +		hdr->seq_ctrl = ieee80211_tx_next_seq(tx->sta, tid); | ||||
|   | ||||
|  	return TX_CONTINUE; | ||||
|  } | ||||
| @@ -1067,7 +1074,7 @@ static bool ieee80211_tx_prep_agg(struct | ||||
|  		 * nothing -- this aggregation session is being started | ||||
|  		 * but that might still fail with the driver | ||||
|  		 */ | ||||
| -	} else { | ||||
| +	} else if (!tx->sta->sta.txq[tid]) { | ||||
|  		spin_lock(&tx->sta->lock); | ||||
|  		/* | ||||
|  		 * Need to re-check now, because we may get here | ||||
| @@ -1201,13 +1208,102 @@ ieee80211_tx_prepare(struct ieee80211_su | ||||
|  	return TX_CONTINUE; | ||||
|  } | ||||
|   | ||||
| +static void ieee80211_drv_tx(struct ieee80211_local *local, | ||||
| +			     struct ieee80211_vif *vif, | ||||
| +			     struct ieee80211_sta *pubsta, | ||||
| +			     struct sk_buff *skb) | ||||
| +{ | ||||
| +	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | ||||
| +	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); | ||||
| +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||||
| +	struct ieee80211_tx_control control = { | ||||
| +		.sta = pubsta, | ||||
| +	}; | ||||
| +	struct ieee80211_txq *txq = NULL; | ||||
| +	struct txq_info *txqi; | ||||
| +	u8 ac; | ||||
| + | ||||
| +	if (info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE) | ||||
| +		goto tx_normal; | ||||
| + | ||||
| +	if (!ieee80211_is_data(hdr->frame_control)) | ||||
| +		goto tx_normal; | ||||
| + | ||||
| +	if (pubsta) { | ||||
| +		u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK; | ||||
| + | ||||
| +		txq = pubsta->txq[tid]; | ||||
| +	} else if (vif) { | ||||
| +		txq = vif->txq; | ||||
| +	} | ||||
| + | ||||
| +	if (!txq) | ||||
| +		goto tx_normal; | ||||
| + | ||||
| +	ac = txq->ac; | ||||
| +	txqi = to_txq_info(txq); | ||||
| +	atomic_inc(&sdata->txqs_len[ac]); | ||||
| +	if (atomic_read(&sdata->txqs_len[ac]) >= local->hw.txq_ac_max_pending) | ||||
| +		netif_stop_subqueue(sdata->dev, ac); | ||||
| + | ||||
| +	skb_queue_tail(&txqi->queue, skb); | ||||
| +	drv_wake_tx_queue(local, txqi); | ||||
| + | ||||
| +	return; | ||||
| + | ||||
| +tx_normal: | ||||
| +	drv_tx(local, &control, skb); | ||||
| +} | ||||
| + | ||||
| +struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw, | ||||
| +				     struct ieee80211_txq *txq) | ||||
| +{ | ||||
| +	struct ieee80211_local *local = hw_to_local(hw); | ||||
| +	struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->vif); | ||||
| +	struct txq_info *txqi = container_of(txq, struct txq_info, txq); | ||||
| +	struct ieee80211_hdr *hdr; | ||||
| +	struct sk_buff *skb = NULL; | ||||
| +	u8 ac = txq->ac; | ||||
| + | ||||
| +	spin_lock_bh(&txqi->queue.lock); | ||||
| + | ||||
| +	if (test_bit(IEEE80211_TXQ_STOP, &txqi->flags)) | ||||
| +		goto out; | ||||
| + | ||||
| +	skb = __skb_dequeue(&txqi->queue); | ||||
| +	if (!skb) | ||||
| +		goto out; | ||||
| + | ||||
| +	atomic_dec(&sdata->txqs_len[ac]); | ||||
| +	if (__netif_subqueue_stopped(sdata->dev, ac)) | ||||
| +		ieee80211_propagate_queue_wake(local, sdata->vif.hw_queue[ac]); | ||||
| + | ||||
| +	hdr = (struct ieee80211_hdr *)skb->data; | ||||
| +	if (txq->sta && ieee80211_is_data_qos(hdr->frame_control)) { | ||||
| +		struct sta_info *sta = container_of(txq->sta, struct sta_info, | ||||
| +						    sta); | ||||
| +		struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||||
| + | ||||
| +		hdr->seq_ctrl = ieee80211_tx_next_seq(sta, txq->tid); | ||||
| +		if (test_bit(IEEE80211_TXQ_AMPDU, &txqi->flags)) | ||||
| +			info->flags |= IEEE80211_TX_CTL_AMPDU; | ||||
| +		else | ||||
| +			info->flags &= ~IEEE80211_TX_CTL_AMPDU; | ||||
| +	} | ||||
| + | ||||
| +out: | ||||
| +	spin_unlock_bh(&txqi->queue.lock); | ||||
| + | ||||
| +	return skb; | ||||
| +} | ||||
| +EXPORT_SYMBOL(ieee80211_tx_dequeue); | ||||
| + | ||||
|  static bool ieee80211_tx_frags(struct ieee80211_local *local, | ||||
|  			       struct ieee80211_vif *vif, | ||||
|  			       struct ieee80211_sta *sta, | ||||
|  			       struct sk_buff_head *skbs, | ||||
|  			       bool txpending) | ||||
|  { | ||||
| -	struct ieee80211_tx_control control; | ||||
|  	struct sk_buff *skb, *tmp; | ||||
|  	unsigned long flags; | ||||
|   | ||||
| @@ -1265,10 +1361,9 @@ static bool ieee80211_tx_frags(struct ie | ||||
|  		spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); | ||||
|   | ||||
|  		info->control.vif = vif; | ||||
| -		control.sta = sta; | ||||
|   | ||||
|  		__skb_unlink(skb, skbs); | ||||
| -		drv_tx(local, &control, skb); | ||||
| +		ieee80211_drv_tx(local, vif, sta, skb); | ||||
|  	} | ||||
|   | ||||
|  	return true; | ||||
| --- a/net/mac80211/util.c | ||||
| +++ b/net/mac80211/util.c | ||||
| @@ -308,6 +308,11 @@ void ieee80211_propagate_queue_wake(stru | ||||
|  		for (ac = 0; ac < n_acs; ac++) { | ||||
|  			int ac_queue = sdata->vif.hw_queue[ac]; | ||||
|   | ||||
| +			if (local->ops->wake_tx_queue && | ||||
| +			    (atomic_read(&sdata->txqs_len[ac]) > | ||||
| +			     local->hw.txq_ac_max_pending)) | ||||
| +				continue; | ||||
| + | ||||
|  			if (ac_queue == queue || | ||||
|  			    (sdata->vif.cab_queue == queue && | ||||
|  			     local->queue_stop_reasons[ac_queue] == 0 && | ||||
| @@ -3307,3 +3312,20 @@ u8 *ieee80211_add_wmm_info_ie(u8 *buf, u | ||||
|   | ||||
|  	return buf; | ||||
|  } | ||||
| + | ||||
| +void ieee80211_init_tx_queue(struct ieee80211_sub_if_data *sdata, | ||||
| +			     struct sta_info *sta, | ||||
| +			     struct txq_info *txqi, int tid) | ||||
| +{ | ||||
| +	skb_queue_head_init(&txqi->queue); | ||||
| +	txqi->txq.vif = &sdata->vif; | ||||
| + | ||||
| +	if (sta) { | ||||
| +		txqi->txq.sta = &sta->sta; | ||||
| +		sta->sta.txq[tid] = &txqi->txq; | ||||
| +		txqi->txq.ac = ieee802_1d_to_ac[tid & 7]; | ||||
| +	} else { | ||||
| +		sdata->vif.txq = &txqi->txq; | ||||
| +		txqi->txq.ac = IEEE80211_AC_BE; | ||||
| +	} | ||||
| +} | ||||
| --- a/net/mac80211/rx.c | ||||
| +++ b/net/mac80211/rx.c | ||||
| @@ -1176,6 +1176,7 @@ static void sta_ps_start(struct sta_info | ||||
|  	struct ieee80211_sub_if_data *sdata = sta->sdata; | ||||
|  	struct ieee80211_local *local = sdata->local; | ||||
|  	struct ps_data *ps; | ||||
| +	int tid; | ||||
|   | ||||
|  	if (sta->sdata->vif.type == NL80211_IFTYPE_AP || | ||||
|  	    sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) | ||||
| @@ -1189,6 +1190,18 @@ static void sta_ps_start(struct sta_info | ||||
|  		drv_sta_notify(local, sdata, STA_NOTIFY_SLEEP, &sta->sta); | ||||
|  	ps_dbg(sdata, "STA %pM aid %d enters power save mode\n", | ||||
|  	       sta->sta.addr, sta->sta.aid); | ||||
| + | ||||
| +	if (!sta->sta.txq[0]) | ||||
| +		return; | ||||
| + | ||||
| +	for (tid = 0; tid < ARRAY_SIZE(sta->sta.txq); tid++) { | ||||
| +		struct txq_info *txqi = to_txq_info(sta->sta.txq[tid]); | ||||
| + | ||||
| +		if (!skb_queue_len(&txqi->queue)) | ||||
| +			set_bit(tid, &sta->txq_buffered_tids); | ||||
| +		else | ||||
| +			clear_bit(tid, &sta->txq_buffered_tids); | ||||
| +	} | ||||
|  } | ||||
|   | ||||
|  static void sta_ps_end(struct sta_info *sta) | ||||
| --- a/net/mac80211/agg-tx.c | ||||
| +++ b/net/mac80211/agg-tx.c | ||||
| @@ -188,6 +188,43 @@ ieee80211_wake_queue_agg(struct ieee8021 | ||||
|  	__release(agg_queue); | ||||
|  } | ||||
|   | ||||
| +static void | ||||
| +ieee80211_agg_stop_txq(struct sta_info *sta, int tid) | ||||
| +{ | ||||
| +	struct ieee80211_txq *txq = sta->sta.txq[tid]; | ||||
| +	struct txq_info *txqi; | ||||
| + | ||||
| +	if (!txq) | ||||
| +		return; | ||||
| + | ||||
| +	txqi = to_txq_info(txq); | ||||
| + | ||||
| +	/* Lock here to protect against further seqno updates on dequeue */ | ||||
| +	spin_lock_bh(&txqi->queue.lock); | ||||
| +	set_bit(IEEE80211_TXQ_STOP, &txqi->flags); | ||||
| +	spin_unlock_bh(&txqi->queue.lock); | ||||
| +} | ||||
| + | ||||
| +static void | ||||
| +ieee80211_agg_start_txq(struct sta_info *sta, int tid, bool enable) | ||||
| +{ | ||||
| +	struct ieee80211_txq *txq = sta->sta.txq[tid]; | ||||
| +	struct txq_info *txqi; | ||||
| + | ||||
| +	if (!txq) | ||||
| +		return; | ||||
| + | ||||
| +	txqi = to_txq_info(txq); | ||||
| + | ||||
| +	if (enable) | ||||
| +		set_bit(IEEE80211_TXQ_AMPDU, &txqi->flags); | ||||
| +	else | ||||
| +		clear_bit(IEEE80211_TXQ_AMPDU, &txqi->flags); | ||||
| + | ||||
| +	clear_bit(IEEE80211_TXQ_STOP, &txqi->flags); | ||||
| +	drv_wake_tx_queue(sta->sdata->local, txqi); | ||||
| +} | ||||
| + | ||||
|  /* | ||||
|   * splice packets from the STA's pending to the local pending, | ||||
|   * requires a call to ieee80211_agg_splice_finish later | ||||
| @@ -247,6 +284,7 @@ static void ieee80211_remove_tid_tx(stru | ||||
|  	ieee80211_assign_tid_tx(sta, tid, NULL); | ||||
|   | ||||
|  	ieee80211_agg_splice_finish(sta->sdata, tid); | ||||
| +	ieee80211_agg_start_txq(sta, tid, false); | ||||
|   | ||||
|  	kfree_rcu(tid_tx, rcu_head); | ||||
|  } | ||||
| @@ -418,6 +456,8 @@ void ieee80211_tx_ba_session_handle_star | ||||
|  	 */ | ||||
|  	clear_bit(HT_AGG_STATE_WANT_START, &tid_tx->state); | ||||
|   | ||||
| +	ieee80211_agg_stop_txq(sta, tid); | ||||
| + | ||||
|  	/* | ||||
|  	 * Make sure no packets are being processed. This ensures that | ||||
|  	 * we have a valid starting sequence number and that in-flight | ||||
| @@ -440,6 +480,8 @@ void ieee80211_tx_ba_session_handle_star | ||||
|  		ieee80211_agg_splice_finish(sdata, tid); | ||||
|  		spin_unlock_bh(&sta->lock); | ||||
|   | ||||
| +		ieee80211_agg_start_txq(sta, tid, false); | ||||
| + | ||||
|  		kfree_rcu(tid_tx, rcu_head); | ||||
|  		return; | ||||
|  	} | ||||
| @@ -666,6 +708,8 @@ static void ieee80211_agg_tx_operational | ||||
|  	ieee80211_agg_splice_finish(sta->sdata, tid); | ||||
|   | ||||
|  	spin_unlock_bh(&sta->lock); | ||||
| + | ||||
| +	ieee80211_agg_start_txq(sta, tid, true); | ||||
|  } | ||||
|   | ||||
|  void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid) | ||||
| @@ -10,7 +10,7 @@ Signed-off-by: Felix Fietkau <nbd@openwrt.org> | ||||
| 
 | ||||
| --- a/drivers/net/wireless/ath/ath9k/hw.c
 | ||||
| +++ b/drivers/net/wireless/ath/ath9k/hw.c
 | ||||
| @@ -278,6 +278,7 @@ static void ath9k_hw_read_revisions(stru
 | ||||
| @@ -279,6 +279,7 @@ static void ath9k_hw_read_revisions(stru
 | ||||
|  		return; | ||||
|  	case AR9300_DEVID_QCA956X: | ||||
|  		ah->hw_version.macVersion = AR_SREV_VERSION_9561; | ||||
| @@ -1,125 +0,0 @@ | ||||
| From: Johannes Berg <johannes.berg@intel.com> | ||||
| Date: Wed, 11 Mar 2015 09:14:15 +0100 | ||||
| Subject: [PATCH] mac80211: lock rate control | ||||
|  | ||||
| Both minstrel (reported by Sven Eckelmann) and the iwlwifi rate | ||||
| control aren't properly taking concurrency into account. It's | ||||
| likely that the same is true for other rate control algorithms. | ||||
|  | ||||
| In the case of minstrel this manifests itself in crashes when an | ||||
| update and other data access are run concurrently, for example | ||||
| when the stations change bandwidth or similar. In iwlwifi, this | ||||
| can cause firmware crashes. | ||||
|  | ||||
| Since fixing all rate control algorithms will be very difficult, | ||||
| just provide locking for invocations. This protects the internal | ||||
| data structures the algorithms maintain. | ||||
|  | ||||
| I've manipulated hostapd to test this, by having it change its | ||||
| advertised bandwidth roughly ever 150ms. At the same time, I'm | ||||
| running a flood ping between the client and the AP, which causes | ||||
| this race of update vs. get_rate/status to easily happen on the | ||||
| client. With this change, the system survives this test. | ||||
|  | ||||
| Reported-by: Sven Eckelmann <sven@open-mesh.com> | ||||
| Signed-off-by: Johannes Berg <johannes.berg@intel.com> | ||||
| --- | ||||
|  | ||||
| --- a/net/mac80211/rate.c | ||||
| +++ b/net/mac80211/rate.c | ||||
| @@ -683,7 +683,13 @@ void rate_control_get_rate(struct ieee80 | ||||
|  	if (sdata->local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) | ||||
|  		return; | ||||
|   | ||||
| -	ref->ops->get_rate(ref->priv, ista, priv_sta, txrc); | ||||
| +	if (ista) { | ||||
| +		spin_lock_bh(&sta->rate_ctrl_lock); | ||||
| +		ref->ops->get_rate(ref->priv, ista, priv_sta, txrc); | ||||
| +		spin_unlock_bh(&sta->rate_ctrl_lock); | ||||
| +	} else { | ||||
| +		ref->ops->get_rate(ref->priv, NULL, NULL, txrc); | ||||
| +	} | ||||
|   | ||||
|  	if (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_RC_TABLE) | ||||
|  		return; | ||||
| --- a/net/mac80211/rate.h | ||||
| +++ b/net/mac80211/rate.h | ||||
| @@ -42,10 +42,12 @@ static inline void rate_control_tx_statu | ||||
|  	if (!ref || !test_sta_flag(sta, WLAN_STA_RATE_CONTROL)) | ||||
|  		return; | ||||
|   | ||||
| +	spin_lock_bh(&sta->rate_ctrl_lock); | ||||
|  	if (ref->ops->tx_status) | ||||
|  		ref->ops->tx_status(ref->priv, sband, ista, priv_sta, skb); | ||||
|  	else | ||||
|  		ref->ops->tx_status_noskb(ref->priv, sband, ista, priv_sta, info); | ||||
| +	spin_unlock_bh(&sta->rate_ctrl_lock); | ||||
|  } | ||||
|   | ||||
|  static inline void | ||||
| @@ -64,7 +66,9 @@ rate_control_tx_status_noskb(struct ieee | ||||
|  	if (WARN_ON_ONCE(!ref->ops->tx_status_noskb)) | ||||
|  		return; | ||||
|   | ||||
| +	spin_lock_bh(&sta->rate_ctrl_lock); | ||||
|  	ref->ops->tx_status_noskb(ref->priv, sband, ista, priv_sta, info); | ||||
| +	spin_unlock_bh(&sta->rate_ctrl_lock); | ||||
|  } | ||||
|   | ||||
|  static inline void rate_control_rate_init(struct sta_info *sta) | ||||
| @@ -91,8 +95,10 @@ static inline void rate_control_rate_ini | ||||
|   | ||||
|  	sband = local->hw.wiphy->bands[chanctx_conf->def.chan->band]; | ||||
|   | ||||
| +	spin_lock_bh(&sta->rate_ctrl_lock); | ||||
|  	ref->ops->rate_init(ref->priv, sband, &chanctx_conf->def, ista, | ||||
|  			    priv_sta); | ||||
| +	spin_unlock_bh(&sta->rate_ctrl_lock); | ||||
|  	rcu_read_unlock(); | ||||
|  	set_sta_flag(sta, WLAN_STA_RATE_CONTROL); | ||||
|  } | ||||
| @@ -115,18 +121,20 @@ static inline void rate_control_rate_upd | ||||
|  			return; | ||||
|  		} | ||||
|   | ||||
| +		spin_lock_bh(&sta->rate_ctrl_lock); | ||||
|  		ref->ops->rate_update(ref->priv, sband, &chanctx_conf->def, | ||||
|  				      ista, priv_sta, changed); | ||||
| +		spin_unlock_bh(&sta->rate_ctrl_lock); | ||||
|  		rcu_read_unlock(); | ||||
|  	} | ||||
|  	drv_sta_rc_update(local, sta->sdata, &sta->sta, changed); | ||||
|  } | ||||
|   | ||||
|  static inline void *rate_control_alloc_sta(struct rate_control_ref *ref, | ||||
| -					   struct ieee80211_sta *sta, | ||||
| -					   gfp_t gfp) | ||||
| +					   struct sta_info *sta, gfp_t gfp) | ||||
|  { | ||||
| -	return ref->ops->alloc_sta(ref->priv, sta, gfp); | ||||
| +	spin_lock_init(&sta->rate_ctrl_lock); | ||||
| +	return ref->ops->alloc_sta(ref->priv, &sta->sta, gfp); | ||||
|  } | ||||
|   | ||||
|  static inline void rate_control_free_sta(struct sta_info *sta) | ||||
| --- a/net/mac80211/sta_info.c | ||||
| +++ b/net/mac80211/sta_info.c | ||||
| @@ -286,7 +286,7 @@ static int sta_prepare_rate_control(stru | ||||
|   | ||||
|  	sta->rate_ctrl = local->rate_ctrl; | ||||
|  	sta->rate_ctrl_priv = rate_control_alloc_sta(sta->rate_ctrl, | ||||
| -						     &sta->sta, gfp); | ||||
| +						     sta, gfp); | ||||
|  	if (!sta->rate_ctrl_priv) | ||||
|  		return -ENOMEM; | ||||
|   | ||||
| --- a/net/mac80211/sta_info.h | ||||
| +++ b/net/mac80211/sta_info.h | ||||
| @@ -349,6 +349,7 @@ struct sta_info { | ||||
|  	u8 ptk_idx; | ||||
|  	struct rate_control_ref *rate_ctrl; | ||||
|  	void *rate_ctrl_priv; | ||||
| +	spinlock_t rate_ctrl_lock; | ||||
|  	spinlock_t lock; | ||||
|   | ||||
|  	struct work_struct drv_deliver_wk; | ||||
| @@ -0,0 +1,35 @@ | ||||
| From: Zefir Kurtisi <zefir.kurtisi@neratec.com> | ||||
| Date: Tue, 16 Jun 2015 10:34:03 +0200 | ||||
| Subject: [PATCH] ath: DFS - limit number of potential PRI sequences | ||||
|  | ||||
| In the PRI detector, after the current radar pulse | ||||
| has been checked agains existing PRI sequences, it | ||||
| is considered as part of a new potential sequence. | ||||
|  | ||||
| Previously, the condition to accept a new sequence | ||||
| was to have at least the same number of pulses as | ||||
| the longest matching sequence. This was wrong, | ||||
| since it led to duplicates of PRI sequences. | ||||
|  | ||||
| This patch changes the acceptance criteria for new | ||||
| potential sequences from 'at least' to 'more than' | ||||
| the longest existing. | ||||
|  | ||||
| Detection performance remains unaffected, while | ||||
| the number of PRI sequences accounted at runtime | ||||
| (and with it CPU load) is reduced by up to 50%. | ||||
|  | ||||
| Signed-off-by: Zefir Kurtisi <zefir.kurtisi@neratec.com> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/dfs_pri_detector.c | ||||
| +++ b/drivers/net/wireless/ath/dfs_pri_detector.c | ||||
| @@ -273,7 +273,7 @@ static bool pseq_handler_create_sequence | ||||
|  				tmp_false_count++; | ||||
|  			} | ||||
|  		} | ||||
| -		if (ps.count < min_count) | ||||
| +		if (ps.count <= min_count) | ||||
|  			/* did not reach minimum count, drop sequence */ | ||||
|  			continue; | ||||
|   | ||||
| @@ -1,21 +0,0 @@ | ||||
| From: Zefir Kurtisi <zefir.kurtisi@neratec.com> | ||||
| Date: Tue, 10 Mar 2015 17:49:29 +0100 | ||||
| Subject: [PATCH] ath9k: restart only triggering DFS detector line | ||||
|  | ||||
| To support HT40 DFS mode, a triggering detector must | ||||
| reset only itself but not other detector lines. | ||||
|  | ||||
| Signed-off-by: Zefir Kurtisi <zefir.kurtisi@neratec.com> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/dfs_pattern_detector.c | ||||
| +++ b/drivers/net/wireless/ath/dfs_pattern_detector.c | ||||
| @@ -289,7 +289,7 @@ dpd_add_pulse(struct dfs_pattern_detecto | ||||
|  				"count=%d, count_false=%d\n", | ||||
|  				event->freq, pd->rs->type_id, | ||||
|  				ps->pri, ps->count, ps->count_falses); | ||||
| -			channel_detector_reset(dpd, cd); | ||||
| +			pd->reset(pd, dpd->last_pulse_ts); | ||||
|  			return true; | ||||
|  		} | ||||
|  	} | ||||
| @@ -0,0 +1,25 @@ | ||||
| From: Zefir Kurtisi <zefir.kurtisi@neratec.com> | ||||
| Date: Tue, 16 Jun 2015 11:46:42 +0200 | ||||
| Subject: [PATCH] ath9k: DFS - consider ext_channel pulses only in HT40 | ||||
|  mode | ||||
|  | ||||
| The chip reports radar pulses on extension channel | ||||
| even if operating in HT20 mode. This patch adds a | ||||
| sanity check for HT40 mode before it feeds pulses | ||||
| on extension channel to the pattern detector. | ||||
|  | ||||
| Signed-off-by: Zefir Kurtisi <zefir.kurtisi@neratec.com> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath9k/dfs.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/dfs.c | ||||
| @@ -198,7 +198,8 @@ void ath9k_dfs_process_phyerr(struct ath | ||||
|  	sc->dfs_prev_pulse_ts = pe.ts; | ||||
|  	if (ard.pulse_bw_info & PRI_CH_RADAR_FOUND) | ||||
|  		ath9k_dfs_process_radar_pulse(sc, &pe); | ||||
| -	if (ard.pulse_bw_info & EXT_CH_RADAR_FOUND) { | ||||
| +	if (IS_CHAN_HT40(ah->curchan) && | ||||
| +	    ard.pulse_bw_info & EXT_CH_RADAR_FOUND) { | ||||
|  		pe.freq += IS_CHAN_HT40PLUS(ah->curchan) ? 20 : -20; | ||||
|  		ath9k_dfs_process_radar_pulse(sc, &pe); | ||||
|  	} | ||||
| @@ -1,76 +0,0 @@ | ||||
| From: Zefir Kurtisi <zefir.kurtisi@neratec.com> | ||||
| Date: Tue, 10 Mar 2015 17:49:30 +0100 | ||||
| Subject: [PATCH] ath9k: add DFS support for extension channel | ||||
|  | ||||
| In HT40 modes, pulse events on primary and extension | ||||
| channel are processed individually. If valid, a pulse | ||||
| event will be fed into the detector | ||||
| * for primary frequency, or | ||||
| * for extension frequency (+/-20MHz based on HT40-mode) | ||||
| * or both | ||||
|  | ||||
| With that, a 40MHz radar will result in two individual | ||||
| radar events. | ||||
|  | ||||
| Signed-off-by: Zefir Kurtisi <zefir.kurtisi@neratec.com> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath9k/dfs.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/dfs.c | ||||
| @@ -126,8 +126,19 @@ ath9k_postprocess_radar_event(struct ath | ||||
|  	DFS_STAT_INC(sc, pulses_detected); | ||||
|  	return true; | ||||
|  } | ||||
| -#undef PRI_CH_RADAR_FOUND | ||||
| -#undef EXT_CH_RADAR_FOUND | ||||
| + | ||||
| +static void | ||||
| +ath9k_dfs_process_radar_pulse(struct ath_softc *sc, struct pulse_event *pe) | ||||
| +{ | ||||
| +	struct dfs_pattern_detector *pd = sc->dfs_detector; | ||||
| +	DFS_STAT_INC(sc, pulses_processed); | ||||
| +	if (pd == NULL) | ||||
| +		return; | ||||
| +	if (!pd->add_pulse(pd, pe)) | ||||
| +		return; | ||||
| +	DFS_STAT_INC(sc, radar_detected); | ||||
| +	ieee80211_radar_detected(sc->hw); | ||||
| +} | ||||
|   | ||||
|  /* | ||||
|   * DFS: check PHY-error for radar pulse and feed the detector | ||||
| @@ -176,18 +187,21 @@ void ath9k_dfs_process_phyerr(struct ath | ||||
|  	ard.pulse_length_pri = vdata_end[-3]; | ||||
|  	pe.freq = ah->curchan->channel; | ||||
|  	pe.ts = mactime; | ||||
| -	if (ath9k_postprocess_radar_event(sc, &ard, &pe)) { | ||||
| -		struct dfs_pattern_detector *pd = sc->dfs_detector; | ||||
| -		ath_dbg(common, DFS, | ||||
| -			"ath9k_dfs_process_phyerr: channel=%d, ts=%llu, " | ||||
| -			"width=%d, rssi=%d, delta_ts=%llu\n", | ||||
| -			pe.freq, pe.ts, pe.width, pe.rssi, | ||||
| -			pe.ts - sc->dfs_prev_pulse_ts); | ||||
| -		sc->dfs_prev_pulse_ts = pe.ts; | ||||
| -		DFS_STAT_INC(sc, pulses_processed); | ||||
| -		if (pd != NULL && pd->add_pulse(pd, &pe)) { | ||||
| -			DFS_STAT_INC(sc, radar_detected); | ||||
| -			ieee80211_radar_detected(sc->hw); | ||||
| -		} | ||||
| +	if (!ath9k_postprocess_radar_event(sc, &ard, &pe)) | ||||
| +		return; | ||||
| + | ||||
| +	ath_dbg(common, DFS, | ||||
| +		"ath9k_dfs_process_phyerr: type=%d, freq=%d, ts=%llu, " | ||||
| +		"width=%d, rssi=%d, delta_ts=%llu\n", | ||||
| +		ard.pulse_bw_info, pe.freq, pe.ts, pe.width, pe.rssi, | ||||
| +		pe.ts - sc->dfs_prev_pulse_ts); | ||||
| +	sc->dfs_prev_pulse_ts = pe.ts; | ||||
| +	if (ard.pulse_bw_info & PRI_CH_RADAR_FOUND) | ||||
| +		ath9k_dfs_process_radar_pulse(sc, &pe); | ||||
| +	if (ard.pulse_bw_info & EXT_CH_RADAR_FOUND) { | ||||
| +		pe.freq += IS_CHAN_HT40PLUS(ah->curchan) ? 20 : -20; | ||||
| +		ath9k_dfs_process_radar_pulse(sc, &pe); | ||||
|  	} | ||||
|  } | ||||
| +#undef PRI_CH_RADAR_FOUND | ||||
| +#undef EXT_CH_RADAR_FOUND | ||||
| @@ -0,0 +1,211 @@ | ||||
| From: Zefir Kurtisi <zefir.kurtisi@neratec.com> | ||||
| Date: Tue, 16 Jun 2015 12:52:16 +0200 | ||||
| Subject: [PATCH] ath9k: DFS - add pulse chirp detection for FCC | ||||
|  | ||||
| FCC long pulse radar (type 5) requires pulses to be | ||||
| checked for chirping. This patch implements chirp | ||||
| detection based on the FFT data provided for long | ||||
| pulses. | ||||
|  | ||||
| A chirp is detected when a set of criteria defined | ||||
| by FCC pulse characteristics is met, including | ||||
| * have at least 4 FFT samples | ||||
| * max_bin index moves equidistantly between samples | ||||
| * the gradient is within defined range | ||||
|  | ||||
| The chirp detection has been tested with reference | ||||
| radar generating devices and proved to work reliably. | ||||
|  | ||||
| Signed-off-by: Zefir Kurtisi <zefir.kurtisi@neratec.com> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath9k/dfs.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/dfs.c | ||||
| @@ -30,6 +30,157 @@ struct ath_radar_data { | ||||
|  	u8 pulse_length_pri; | ||||
|  }; | ||||
|   | ||||
| +/**** begin: CHIRP ************************************************************/ | ||||
| + | ||||
| +/* min and max gradients for defined FCC chirping pulses, given by | ||||
| + * - 20MHz chirp width over a pulse width of  50us | ||||
| + * -  5MHz chirp width over a pulse width of 100us | ||||
| + */ | ||||
| +static const int BIN_DELTA_MIN		= 1; | ||||
| +static const int BIN_DELTA_MAX		= 10; | ||||
| + | ||||
| +/* we need at least 3 deltas / 4 samples for a reliable chirp detection */ | ||||
| +#define NUM_DIFFS 3 | ||||
| +static const int FFT_NUM_SAMPLES	= (NUM_DIFFS + 1); | ||||
| + | ||||
| +/* Threshold for difference of delta peaks */ | ||||
| +static const int MAX_DIFF		= 2; | ||||
| + | ||||
| +/* width range to be checked for chirping */ | ||||
| +static const int MIN_CHIRP_PULSE_WIDTH	= 20; | ||||
| +static const int MAX_CHIRP_PULSE_WIDTH	= 110; | ||||
| + | ||||
| +struct ath9k_dfs_fft_20 { | ||||
| +	u8 bin[28]; | ||||
| +	u8 lower_bins[3]; | ||||
| +} __packed; | ||||
| +struct ath9k_dfs_fft_40 { | ||||
| +	u8 bin[64]; | ||||
| +	u8 lower_bins[3]; | ||||
| +	u8 upper_bins[3]; | ||||
| +} __packed; | ||||
| + | ||||
| +static inline int fft_max_index(u8 *bins) | ||||
| +{ | ||||
| +	return (bins[2] & 0xfc) >> 2; | ||||
| +} | ||||
| +static inline int fft_max_magnitude(u8 *bins) | ||||
| +{ | ||||
| +	return (bins[0] & 0xc0) >> 6 | bins[1] << 2 | (bins[2] & 0x03) << 10; | ||||
| +} | ||||
| +static inline u8 fft_bitmap_weight(u8 *bins) | ||||
| +{ | ||||
| +	return bins[0] & 0x3f; | ||||
| +} | ||||
| + | ||||
| +static int ath9k_get_max_index_ht40(struct ath9k_dfs_fft_40 *fft, | ||||
| +				    bool is_ctl, bool is_ext) | ||||
| +{ | ||||
| +	const int DFS_UPPER_BIN_OFFSET = 64; | ||||
| +	/* if detected radar on both channels, select the significant one */ | ||||
| +	if (is_ctl && is_ext) { | ||||
| +		/* first check wether channels have 'strong' bins */ | ||||
| +		is_ctl = fft_bitmap_weight(fft->lower_bins) != 0; | ||||
| +		is_ext = fft_bitmap_weight(fft->upper_bins) != 0; | ||||
| + | ||||
| +		/* if still unclear, take higher magnitude */ | ||||
| +		if (is_ctl && is_ext) { | ||||
| +			int mag_lower = fft_max_magnitude(fft->lower_bins); | ||||
| +			int mag_upper = fft_max_magnitude(fft->upper_bins); | ||||
| +			if (mag_upper > mag_lower) | ||||
| +				is_ctl = false; | ||||
| +			else | ||||
| +				is_ext = false; | ||||
| +		} | ||||
| +	} | ||||
| +	if (is_ctl) | ||||
| +		return fft_max_index(fft->lower_bins); | ||||
| +	return fft_max_index(fft->upper_bins) + DFS_UPPER_BIN_OFFSET; | ||||
| +} | ||||
| +static bool ath9k_check_chirping(struct ath_softc *sc, u8 *data, | ||||
| +				 int datalen, bool is_ctl, bool is_ext) | ||||
| +{ | ||||
| +	int i; | ||||
| +	int max_bin[FFT_NUM_SAMPLES]; | ||||
| +	struct ath_hw *ah = sc->sc_ah; | ||||
| +	struct ath_common *common = ath9k_hw_common(ah); | ||||
| +	int prev_delta; | ||||
| + | ||||
| +	if (IS_CHAN_HT40(ah->curchan)) { | ||||
| +		struct ath9k_dfs_fft_40 *fft = (struct ath9k_dfs_fft_40 *) data; | ||||
| +		int num_fft_packets = datalen / sizeof(*fft); | ||||
| +		if (num_fft_packets == 0) | ||||
| +			return false; | ||||
| + | ||||
| +		ath_dbg(common, DFS, "HT40: datalen=%d, num_fft_packets=%d\n", | ||||
| +			datalen, num_fft_packets); | ||||
| +		if (num_fft_packets < (FFT_NUM_SAMPLES)) { | ||||
| +			ath_dbg(common, DFS, "not enough packets for chirp\n"); | ||||
| +			return false; | ||||
| +		} | ||||
| +		/* HW sometimes adds 2 garbage bytes in front of FFT samples */ | ||||
| +		if ((datalen % sizeof(*fft)) == 2) { | ||||
| +			fft = (struct ath9k_dfs_fft_40 *) (data + 2); | ||||
| +			ath_dbg(common, DFS, "fixing datalen by 2\n"); | ||||
| +		} | ||||
| +		if (IS_CHAN_HT40MINUS(ah->curchan)) { | ||||
| +			int temp = is_ctl; | ||||
| +			is_ctl = is_ext; | ||||
| +			is_ext = temp; | ||||
| +		} | ||||
| +		for (i = 0; i < FFT_NUM_SAMPLES; i++) | ||||
| +			max_bin[i] = ath9k_get_max_index_ht40(fft + i, is_ctl, | ||||
| +							      is_ext); | ||||
| +	} else { | ||||
| +		struct ath9k_dfs_fft_20 *fft = (struct ath9k_dfs_fft_20 *) data; | ||||
| +		int num_fft_packets = datalen / sizeof(*fft); | ||||
| +		if (num_fft_packets == 0) | ||||
| +			return false; | ||||
| +		ath_dbg(common, DFS, "HT20: datalen=%d, num_fft_packets=%d\n", | ||||
| +			datalen, num_fft_packets); | ||||
| +		if (num_fft_packets < (FFT_NUM_SAMPLES)) { | ||||
| +			ath_dbg(common, DFS, "not enough packets for chirp\n"); | ||||
| +			return false; | ||||
| +		} | ||||
| +		/* in ht20, this is a 6-bit signed number => shift it to 0 */ | ||||
| +		for (i = 0; i < FFT_NUM_SAMPLES; i++) | ||||
| +			max_bin[i] = fft_max_index(fft[i].lower_bins) ^ 0x20; | ||||
| +	} | ||||
| +	ath_dbg(common, DFS, "bin_max = [%d, %d, %d, %d]\n", | ||||
| +		max_bin[0], max_bin[1], max_bin[2], max_bin[3]); | ||||
| + | ||||
| +	/* Check for chirp attributes within specs | ||||
| +	 * a) delta of adjacent max_bins is within range | ||||
| +	 * b) delta of adjacent deltas are within tolerance | ||||
| +	 */ | ||||
| +	prev_delta = 0; | ||||
| +	for (i = 0; i < NUM_DIFFS; i++) { | ||||
| +		int ddelta = -1; | ||||
| +		int delta = max_bin[i + 1] - max_bin[i]; | ||||
| + | ||||
| +		/* ensure gradient is within valid range */ | ||||
| +		if (abs(delta) < BIN_DELTA_MIN || abs(delta) > BIN_DELTA_MAX) { | ||||
| +			ath_dbg(common, DFS, "CHIRP: invalid delta %d " | ||||
| +				"in sample %d\n", delta, i); | ||||
| +			return false; | ||||
| +		} | ||||
| +		if (i == 0) | ||||
| +			goto done; | ||||
| +		ddelta = delta - prev_delta; | ||||
| +		if (abs(ddelta) > MAX_DIFF) { | ||||
| +			ath_dbg(common, DFS, "CHIRP: ddelta %d too high\n", | ||||
| +				ddelta); | ||||
| +			return false; | ||||
| +		} | ||||
| +done: | ||||
| +		ath_dbg(common, DFS, "CHIRP - %d: delta=%d, ddelta=%d\n", | ||||
| +			i, delta, ddelta); | ||||
| +		prev_delta = delta; | ||||
| +	} | ||||
| +	return true; | ||||
| +} | ||||
| +/**** end: CHIRP **************************************************************/ | ||||
| + | ||||
|  /* convert pulse duration to usecs, considering clock mode */ | ||||
|  static u32 dur_to_usecs(struct ath_hw *ah, u32 dur) | ||||
|  { | ||||
| @@ -113,12 +264,6 @@ ath9k_postprocess_radar_event(struct ath | ||||
|  		return false; | ||||
|  	} | ||||
|   | ||||
| -	/* | ||||
| -	 * TODO: check chirping pulses | ||||
| -	 *	 checks for chirping are dependent on the DFS regulatory domain | ||||
| -	 *	 used, which is yet TBD | ||||
| -	 */ | ||||
| - | ||||
|  	/* convert duration to usecs */ | ||||
|  	pe->width = dur_to_usecs(sc->sc_ah, dur); | ||||
|  	pe->rssi = rssi; | ||||
| @@ -190,6 +335,16 @@ void ath9k_dfs_process_phyerr(struct ath | ||||
|  	if (!ath9k_postprocess_radar_event(sc, &ard, &pe)) | ||||
|  		return; | ||||
|   | ||||
| +	if (pe.width > MIN_CHIRP_PULSE_WIDTH && | ||||
| +	    pe.width < MAX_CHIRP_PULSE_WIDTH) { | ||||
| +		bool is_ctl = !!(ard.pulse_bw_info & PRI_CH_RADAR_FOUND); | ||||
| +		bool is_ext = !!(ard.pulse_bw_info & EXT_CH_RADAR_FOUND); | ||||
| +		int clen = datalen - 3; | ||||
| +		pe.chirp = ath9k_check_chirping(sc, data, clen, is_ctl, is_ext); | ||||
| +	} else { | ||||
| +		pe.chirp = false; | ||||
| +	} | ||||
| + | ||||
|  	ath_dbg(common, DFS, | ||||
|  		"ath9k_dfs_process_phyerr: type=%d, freq=%d, ts=%llu, " | ||||
|  		"width=%d, rssi=%d, delta_ts=%llu\n", | ||||
| @@ -1,19 +0,0 @@ | ||||
| From: Zefir Kurtisi <zefir.kurtisi@neratec.com> | ||||
| Date: Tue, 10 Mar 2015 17:49:31 +0100 | ||||
| Subject: [PATCH] ath9k: allow 40MHz radar detection width | ||||
|  | ||||
| Signed-off-by: Zefir Kurtisi <zefir.kurtisi@neratec.com> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath9k/init.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/init.c | ||||
| @@ -763,7 +763,8 @@ static const struct ieee80211_iface_comb | ||||
|  		.num_different_channels = 1, | ||||
|  		.beacon_int_infra_match = true, | ||||
|  		.radar_detect_widths =	BIT(NL80211_CHAN_WIDTH_20_NOHT) | | ||||
| -					BIT(NL80211_CHAN_WIDTH_20), | ||||
| +					BIT(NL80211_CHAN_WIDTH_20) | | ||||
| +					BIT(NL80211_CHAN_WIDTH_40), | ||||
|  	} | ||||
|  #endif | ||||
|  }; | ||||
| @@ -1,137 +0,0 @@ | ||||
| From: Sergey Ryazanov <ryazanov.s.a@gmail.com> | ||||
| Date: Wed, 4 Mar 2015 05:12:10 +0300 | ||||
| Subject: [PATCH] ath5k: channel change fix | ||||
|  | ||||
| ath5k updates the channel pointer and after that it stops the Rx logic | ||||
| and apply channel to HW. In case of channel switch, such sequence | ||||
| creates a small window when a frame, which is received on the old | ||||
| channel is considered as a frame received on the new one. | ||||
|  | ||||
| The most notable consequence of this situation occurs during the switch | ||||
| from 2 GHz band (CCK+OFDM) to the 5GHz band (OFDM-only). Frame received | ||||
| with CCK rate, e.g. beacon received at the 1mbps, causes the following | ||||
| warning: | ||||
|  | ||||
|   WARNING: at ath5k/base.c:589 ath5k_tasklet_rx+0x318/0x6ec [ath5k]() | ||||
|   invalid hw_rix: 1a | ||||
|   [..] | ||||
|   Call Trace: | ||||
|   [<802656a8>] show_stack+0x48/0x70 | ||||
|   [<802dd92c>] warn_slowpath_common+0x88/0xbc | ||||
|   [<802dd98c>] warn_slowpath_fmt+0x2c/0x38 | ||||
|   [<81b51be8>] ath5k_tasklet_rx+0x318/0x6ec [ath5k] | ||||
|   [<8028ac64>] tasklet_action+0x8c/0xf0 | ||||
|   [<80075804>] __do_softirq+0x180/0x32c | ||||
|   [<80196ce8>] irq_exit+0x54/0x70 | ||||
|   [<80041848>] ret_from_irq+0x0/0x4 | ||||
|   [<80182fdc>] ioread32+0x4/0xc | ||||
|   [<81b4c42c>] ath5k_hw_set_sleep_clock+0x2ec/0x474 [ath5k] | ||||
|   [<81b4cf28>] ath5k_hw_reset+0x50/0xeb8 [ath5k] | ||||
|   [<81b50900>] ath5k_reset+0xd4/0x310 [ath5k] | ||||
|   [<81b557e8>] ath5k_config+0x4c/0x104 [ath5k] | ||||
|   [<80d01770>] ieee80211_hw_config+0x2f4/0x35c [mac80211] | ||||
|   [<80d09aa8>] ieee80211_scan_work+0x2e4/0x414 [mac80211] | ||||
|   [<8022c3f4>] process_one_work+0x28c/0x400 | ||||
|   [<802df8f8>] worker_thread+0x258/0x3c0 | ||||
|   [<801b5710>] kthread+0xe0/0xec | ||||
|   [<800418a8>] ret_from_kernel_thread+0x14/0x1c | ||||
|  | ||||
| The easiest way to reproduce this warning is to run scan with dualband | ||||
| NIC in noisy environments, when the channel 11 runs multiple APs. In my | ||||
| tests if the APs num >= 12, the warning appears in the first few | ||||
| seconds of scanning. | ||||
|  | ||||
| In order to fix this, the Rx disable code moved to a higher level and | ||||
| placed before the channel pointer update. This is also makes the code a | ||||
| bit more symmetrical, since we disable and enable the Rx in the same | ||||
| function. | ||||
|  | ||||
| In fact, at the pointer update time new frames should not appear, | ||||
| because interrupt generation at this point should already be disabled. | ||||
| The next patch should address this issue. | ||||
|  | ||||
| CC: Jiri Slaby <jirislaby@gmail.com> | ||||
| CC: Nick Kossifidis <mickflemm@gmail.com> | ||||
| CC: Luis R. Rodriguez <mcgrof@do-not-panic.com> | ||||
| Reported-by: Christophe Prevotaux <cprevotaux@nltinc.com> | ||||
| Tested-by: Christophe Prevotaux <cprevotaux@nltinc.com> | ||||
| Tested-by: Eric Bree <ebree@nltinc.com> | ||||
| Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath5k/base.c | ||||
| +++ b/drivers/net/wireless/ath/ath5k/base.c | ||||
| @@ -2858,7 +2858,7 @@ ath5k_reset(struct ath5k_hw *ah, struct | ||||
|  { | ||||
|  	struct ath_common *common = ath5k_hw_common(ah); | ||||
|  	int ret, ani_mode; | ||||
| -	bool fast; | ||||
| +	bool fast = chan && modparam_fastchanswitch ? 1 : 0; | ||||
|   | ||||
|  	ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "resetting\n"); | ||||
|   | ||||
| @@ -2876,11 +2876,29 @@ ath5k_reset(struct ath5k_hw *ah, struct | ||||
|  	 * so we should also free any remaining | ||||
|  	 * tx buffers */ | ||||
|  	ath5k_drain_tx_buffs(ah); | ||||
| + | ||||
| +	/* Stop PCU */ | ||||
| +	ath5k_hw_stop_rx_pcu(ah); | ||||
| + | ||||
| +	/* Stop DMA | ||||
| +	 * | ||||
| +	 * Note: If DMA didn't stop continue | ||||
| +	 * since only a reset will fix it. | ||||
| +	 */ | ||||
| +	ret = ath5k_hw_dma_stop(ah); | ||||
| + | ||||
| +	/* RF Bus grant won't work if we have pending | ||||
| +	 * frames | ||||
| +	 */ | ||||
| +	if (ret && fast) { | ||||
| +		ATH5K_DBG(ah, ATH5K_DEBUG_RESET, | ||||
| +			  "DMA didn't stop, falling back to normal reset\n"); | ||||
| +		fast = false; | ||||
| +	} | ||||
| + | ||||
|  	if (chan) | ||||
|  		ah->curchan = chan; | ||||
|   | ||||
| -	fast = ((chan != NULL) && modparam_fastchanswitch) ? 1 : 0; | ||||
| - | ||||
|  	ret = ath5k_hw_reset(ah, ah->opmode, ah->curchan, fast, skip_pcu); | ||||
|  	if (ret) { | ||||
|  		ATH5K_ERR(ah, "can't reset hardware (%d)\n", ret); | ||||
| --- a/drivers/net/wireless/ath/ath5k/reset.c | ||||
| +++ b/drivers/net/wireless/ath/ath5k/reset.c | ||||
| @@ -1169,30 +1169,6 @@ ath5k_hw_reset(struct ath5k_hw *ah, enum | ||||
|  	if (ah->ah_version == AR5K_AR5212) | ||||
|  		ath5k_hw_set_sleep_clock(ah, false); | ||||
|   | ||||
| -	/* | ||||
| -	 * Stop PCU | ||||
| -	 */ | ||||
| -	ath5k_hw_stop_rx_pcu(ah); | ||||
| - | ||||
| -	/* | ||||
| -	 * Stop DMA | ||||
| -	 * | ||||
| -	 * Note: If DMA didn't stop continue | ||||
| -	 * since only a reset will fix it. | ||||
| -	 */ | ||||
| -	ret = ath5k_hw_dma_stop(ah); | ||||
| - | ||||
| -	/* RF Bus grant won't work if we have pending | ||||
| -	 * frames */ | ||||
| -	if (ret && fast) { | ||||
| -		ATH5K_DBG(ah, ATH5K_DEBUG_RESET, | ||||
| -			"DMA didn't stop, falling back to normal reset\n"); | ||||
| -		fast = false; | ||||
| -		/* Non fatal, just continue with | ||||
| -		 * normal reset */ | ||||
| -		ret = 0; | ||||
| -	} | ||||
| - | ||||
|  	mode = channel->hw_value; | ||||
|  	switch (mode) { | ||||
|  	case AR5K_MODE_11A: | ||||
| @@ -42,7 +42,7 @@ Signed-off-by: Felix Fietkau <nbd@openwrt.org> | ||||
|   | ||||
| --- a/drivers/net/wireless/ath/ath9k/recv.c
 | ||||
| +++ b/drivers/net/wireless/ath/ath9k/recv.c
 | ||||
| @@ -496,10 +496,9 @@ bool ath_stoprecv(struct ath_softc *sc)
 | ||||
| @@ -491,10 +491,9 @@ bool ath_stoprecv(struct ath_softc *sc)
 | ||||
|   | ||||
|  	if (!(ah->ah_flags & AH_UNPLUGGED) && | ||||
|  	    unlikely(!stopped)) { | ||||
| @@ -58,7 +58,7 @@ Signed-off-by: Felix Fietkau <nbd@openwrt.org> | ||||
|  } | ||||
| --- a/drivers/net/wireless/ath/ath9k/xmit.c
 | ||||
| +++ b/drivers/net/wireless/ath/ath9k/xmit.c
 | ||||
| @@ -1896,8 +1896,11 @@ bool ath_drain_all_txq(struct ath_softc
 | ||||
| @@ -1883,8 +1883,11 @@ bool ath_drain_all_txq(struct ath_softc
 | ||||
|  			npend |= BIT(i); | ||||
|  	} | ||||
|   | ||||
| @@ -1,96 +0,0 @@ | ||||
| From: Sergey Ryazanov <ryazanov.s.a@gmail.com> | ||||
| Date: Wed, 4 Mar 2015 05:12:11 +0300 | ||||
| Subject: [PATCH] ath5k: fix reset race | ||||
|  | ||||
| To prepare for reset ath5k should finish all asynchronous tasks. At | ||||
| first, it disables the interrupt generation, then it waits for the | ||||
| interrupt handler and tasklets completion, and then proceeds to the HW | ||||
| configuration update. But it does not consider that the interrupt | ||||
| handler or tasklet re-enables the interrupt generation. And we fall in a | ||||
| situation when ath5k assumes that interrupts are disabled, but it is | ||||
| not. | ||||
|  | ||||
| This can lead to different consequences, such as reception of the frame, | ||||
| when we do not expect it. Under certain circumstances, this can lead to | ||||
| the following warning: | ||||
|  | ||||
|   WARNING: at ath5k/base.c:589 ath5k_tasklet_rx+0x318/0x6ec [ath5k]() | ||||
|   invalid hw_rix: 1a | ||||
|   [..] | ||||
|   Call Trace: | ||||
|   [<802656a8>] show_stack+0x48/0x70 | ||||
|   [<802dd92c>] warn_slowpath_common+0x88/0xbc | ||||
|   [<802dd98c>] warn_slowpath_fmt+0x2c/0x38 | ||||
|   [<81b51be8>] ath5k_tasklet_rx+0x318/0x6ec [ath5k] | ||||
|   [<8028ac64>] tasklet_action+0x8c/0xf0 | ||||
|   [<80075804>] __do_softirq+0x180/0x32c | ||||
|   [<80196ce8>] irq_exit+0x54/0x70 | ||||
|   [<80041848>] ret_from_irq+0x0/0x4 | ||||
|   [<80182fdc>] ioread32+0x4/0xc | ||||
|   [<81b4c42c>] ath5k_hw_set_sleep_clock+0x2ec/0x474 [ath5k] | ||||
|   [<81b4cf28>] ath5k_hw_reset+0x50/0xeb8 [ath5k] | ||||
|   [<81b50900>] ath5k_reset+0xd4/0x310 [ath5k] | ||||
|   [<81b557e8>] ath5k_config+0x4c/0x104 [ath5k] | ||||
|   [<80d01770>] ieee80211_hw_config+0x2f4/0x35c [mac80211] | ||||
|   [<80d09aa8>] ieee80211_scan_work+0x2e4/0x414 [mac80211] | ||||
|   [<8022c3f4>] process_one_work+0x28c/0x400 | ||||
|   [<802df8f8>] worker_thread+0x258/0x3c0 | ||||
|   [<801b5710>] kthread+0xe0/0xec | ||||
|   [<800418a8>] ret_from_kernel_thread+0x14/0x1c | ||||
|  | ||||
| Fix this issue by adding a new status flag, which forbids to re-enable | ||||
| the interrupt generation until the HW configuration is completed. | ||||
|  | ||||
| Note: previous patch, which reorders the Rx disable code helps to avoid | ||||
| the above warning, but not fixes the root cause of unexpected frame | ||||
| receiving. | ||||
|  | ||||
| CC: Jiri Slaby <jirislaby@gmail.com> | ||||
| CC: Nick Kossifidis <mickflemm@gmail.com> | ||||
| CC: Luis R. Rodriguez <mcgrof@do-not-panic.com> | ||||
| Reported-by: Christophe Prevotaux <cprevotaux@nltinc.com> | ||||
| Tested-by: Christophe Prevotaux <cprevotaux@nltinc.com> | ||||
| Tested-by: Eric Bree <ebree@nltinc.com> | ||||
| Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath5k/ath5k.h | ||||
| +++ b/drivers/net/wireless/ath/ath5k/ath5k.h | ||||
| @@ -1283,6 +1283,7 @@ struct ath5k_hw { | ||||
|  #define ATH_STAT_PROMISC	1 | ||||
|  #define ATH_STAT_LEDSOFT	2		/* enable LED gpio status */ | ||||
|  #define ATH_STAT_STARTED	3		/* opened & irqs enabled */ | ||||
| +#define ATH_STAT_RESET		4		/* hw reset */ | ||||
|   | ||||
|  	unsigned int		filter_flags;	/* HW flags, AR5K_RX_FILTER_* */ | ||||
|  	unsigned int		fif_filter_flags; /* Current FIF_* filter flags */ | ||||
| --- a/drivers/net/wireless/ath/ath5k/base.c | ||||
| +++ b/drivers/net/wireless/ath/ath5k/base.c | ||||
| @@ -1523,6 +1523,9 @@ ath5k_set_current_imask(struct ath5k_hw | ||||
|  	enum ath5k_int imask; | ||||
|  	unsigned long flags; | ||||
|   | ||||
| +	if (test_bit(ATH_STAT_RESET, ah->status)) | ||||
| +		return; | ||||
| + | ||||
|  	spin_lock_irqsave(&ah->irqlock, flags); | ||||
|  	imask = ah->imask; | ||||
|  	if (ah->rx_pending) | ||||
| @@ -2862,6 +2865,8 @@ ath5k_reset(struct ath5k_hw *ah, struct | ||||
|   | ||||
|  	ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "resetting\n"); | ||||
|   | ||||
| +	__set_bit(ATH_STAT_RESET, ah->status); | ||||
| + | ||||
|  	ath5k_hw_set_imr(ah, 0); | ||||
|  	synchronize_irq(ah->irq); | ||||
|  	ath5k_stop_tasklets(ah); | ||||
| @@ -2952,6 +2957,8 @@ ath5k_reset(struct ath5k_hw *ah, struct | ||||
|  	 */ | ||||
|  /*	ath5k_chan_change(ah, c); */ | ||||
|   | ||||
| +	__clear_bit(ATH_STAT_RESET, ah->status); | ||||
| + | ||||
|  	ath5k_beacon_config(ah); | ||||
|  	/* intrs are enabled by ath5k_beacon_config */ | ||||
|   | ||||
| @@ -0,0 +1,121 @@ | ||||
| From: Felix Fietkau <nbd@openwrt.org> | ||||
| Date: Thu, 2 Jul 2015 15:20:56 +0200 | ||||
| Subject: [PATCH] ath9k: limit retries for powersave response frames | ||||
|  | ||||
| In some cases, the channel might be busy enough that an ath9k AP's | ||||
| response to PS-Poll frames might be too slow and the station has already | ||||
| gone to sleep. To avoid wasting too much airtime on this, limit the | ||||
| number of retries on such frames and ensure that no sample rate gets | ||||
| used. | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@openwrt.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath9k/xmit.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/xmit.c | ||||
| @@ -147,10 +147,25 @@ static void ath_send_bar(struct ath_atx_ | ||||
|  } | ||||
|   | ||||
|  static void ath_set_rates(struct ieee80211_vif *vif, struct ieee80211_sta *sta, | ||||
| -			  struct ath_buf *bf) | ||||
| +			  struct ath_buf *bf, bool ps) | ||||
|  { | ||||
| +	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(bf->bf_mpdu); | ||||
| + | ||||
| +	if (ps) { | ||||
| +		/* Clear the first rate to avoid using a sample rate for PS frames */ | ||||
| +		info->control.rates[0].idx = -1; | ||||
| +		info->control.rates[0].count = 0; | ||||
| +	} | ||||
| + | ||||
|  	ieee80211_get_tx_rates(vif, sta, bf->bf_mpdu, bf->rates, | ||||
|  			       ARRAY_SIZE(bf->rates)); | ||||
| +	if (!ps) | ||||
| +		return; | ||||
| + | ||||
| +	if (bf->rates[0].count > 2) | ||||
| +		bf->rates[0].count = 2; | ||||
| + | ||||
| +	bf->rates[1].idx = -1; | ||||
|  } | ||||
|   | ||||
|  static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq, | ||||
| @@ -1430,7 +1445,7 @@ ath_tx_form_burst(struct ath_softc *sc, | ||||
|  		if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) | ||||
|  			break; | ||||
|   | ||||
| -		ath_set_rates(tid->an->vif, tid->an->sta, bf); | ||||
| +		ath_set_rates(tid->an->vif, tid->an->sta, bf, false); | ||||
|  	} while (1); | ||||
|  } | ||||
|   | ||||
| @@ -1461,7 +1476,7 @@ static bool ath_tx_sched_aggr(struct ath | ||||
|  		return false; | ||||
|  	} | ||||
|   | ||||
| -	ath_set_rates(tid->an->vif, tid->an->sta, bf); | ||||
| +	ath_set_rates(tid->an->vif, tid->an->sta, bf, false); | ||||
|  	if (aggr) | ||||
|  		last = ath_tx_form_aggr(sc, txq, tid, &bf_q, bf, | ||||
|  					tid_q, &aggr_len); | ||||
| @@ -1653,7 +1668,7 @@ void ath9k_release_buffered_frames(struc | ||||
|   | ||||
|  			__skb_unlink(bf->bf_mpdu, tid_q); | ||||
|  			list_add_tail(&bf->list, &bf_q); | ||||
| -			ath_set_rates(tid->an->vif, tid->an->sta, bf); | ||||
| +			ath_set_rates(tid->an->vif, tid->an->sta, bf, true); | ||||
|  			if (bf_isampdu(bf)) { | ||||
|  				ath_tx_addto_baw(sc, tid, bf); | ||||
|  				bf->bf_state.bf_type &= ~BUF_AGGR; | ||||
| @@ -2318,7 +2333,7 @@ int ath_tx_start(struct ieee80211_hw *hw | ||||
|  	struct ath_txq *txq = txctl->txq; | ||||
|  	struct ath_atx_tid *tid = NULL; | ||||
|  	struct ath_buf *bf; | ||||
| -	bool queue, skip_uapsd = false, ps_resp; | ||||
| +	bool queue, ps_resp; | ||||
|  	int q, ret; | ||||
|   | ||||
|  	if (vif) | ||||
| @@ -2365,13 +2380,13 @@ int ath_tx_start(struct ieee80211_hw *hw | ||||
|  		if (!txctl->an) | ||||
|  			txctl->an = &avp->mcast_node; | ||||
|  		queue = true; | ||||
| -		skip_uapsd = true; | ||||
| +		ps_resp = false; | ||||
|  	} | ||||
|   | ||||
|  	if (txctl->an && queue) | ||||
|  		tid = ath_get_skb_tid(sc, txctl->an, skb); | ||||
|   | ||||
| -	if (!skip_uapsd && ps_resp) { | ||||
| +	if (ps_resp) { | ||||
|  		ath_txq_unlock(sc, txq); | ||||
|  		txq = sc->tx.uapsdq; | ||||
|  		ath_txq_lock(sc, txq); | ||||
| @@ -2409,7 +2424,7 @@ int ath_tx_start(struct ieee80211_hw *hw | ||||
|  	if (txctl->paprd) | ||||
|  		bf->bf_state.bfs_paprd_timestamp = jiffies; | ||||
|   | ||||
| -	ath_set_rates(vif, sta, bf); | ||||
| +	ath_set_rates(vif, sta, bf, ps_resp); | ||||
|  	ath_tx_send_normal(sc, txq, tid, skb); | ||||
|   | ||||
|  out: | ||||
| @@ -2448,7 +2463,7 @@ void ath_tx_cabq(struct ieee80211_hw *hw | ||||
|  			break; | ||||
|   | ||||
|  		bf->bf_lastbf = bf; | ||||
| -		ath_set_rates(vif, NULL, bf); | ||||
| +		ath_set_rates(vif, NULL, bf, false); | ||||
|  		ath_buf_set_rate(sc, bf, &info, fi->framelen, false); | ||||
|  		duration += info.rates[0].PktDuration; | ||||
|  		if (bf_tail) | ||||
| @@ -2968,7 +2983,7 @@ int ath9k_tx99_send(struct ath_softc *sc | ||||
|  		return -EINVAL; | ||||
|  	} | ||||
|   | ||||
| -	ath_set_rates(sc->tx99_vif, NULL, bf); | ||||
| +	ath_set_rates(sc->tx99_vif, NULL, bf, false); | ||||
|   | ||||
|  	ath9k_hw_set_desc_link(sc->sc_ah, bf->bf_desc, bf->bf_daddr); | ||||
|  	ath9k_hw_tx99_start(sc->sc_ah, txctl->txq->axq_qnum); | ||||
| @@ -0,0 +1,56 @@ | ||||
| From: Vasanthakumar Thiagarajan <vthiagar@qti.qualcomm.com> | ||||
| Date: Fri, 3 Jul 2015 11:45:42 +0530 | ||||
| Subject: [PATCH] ath10k: Delay device access after cold reset | ||||
|  | ||||
| It is observed that during cold reset pcie access right | ||||
| after a write operation to SOC_GLOBAL_RESET_ADDRESS causes | ||||
| Data Bus Error and system hard lockup. The reason | ||||
| for bus error is that pcie needs some time to get | ||||
| back to stable state for any transaction during cold reset. Add | ||||
| delay of 20 msecs after write of SOC_GLOBAL_RESET_ADDRESS | ||||
| to fix this issue. | ||||
|  | ||||
| Signed-off-by: Vasanthakumar Thiagarajan <vthiagar@qti.qualcomm.com> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath10k/pci.c | ||||
| +++ b/drivers/net/wireless/ath/ath10k/pci.c | ||||
| @@ -2602,7 +2602,6 @@ static int ath10k_pci_wait_for_target_in | ||||
|   | ||||
|  static int ath10k_pci_cold_reset(struct ath10k *ar) | ||||
|  { | ||||
| -	int i; | ||||
|  	u32 val; | ||||
|   | ||||
|  	ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot cold reset\n"); | ||||
| @@ -2618,23 +2617,18 @@ static int ath10k_pci_cold_reset(struct | ||||
|  	val |= 1; | ||||
|  	ath10k_pci_reg_write32(ar, SOC_GLOBAL_RESET_ADDRESS, val); | ||||
|   | ||||
| -	for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) { | ||||
| -		if (ath10k_pci_reg_read32(ar, RTC_STATE_ADDRESS) & | ||||
| -					  RTC_STATE_COLD_RESET_MASK) | ||||
| -			break; | ||||
| -		msleep(1); | ||||
| -	} | ||||
| +	/* After writing into SOC_GLOBAL_RESET to put device into | ||||
| +	 * reset and pulling out of reset pcie may not be stable | ||||
| +	 * for any immediate pcie register access and cause bus error, | ||||
| +	 * add delay before any pcie access request to fix this issue. | ||||
| +	 */ | ||||
| +	msleep(20); | ||||
|   | ||||
|  	/* Pull Target, including PCIe, out of RESET. */ | ||||
|  	val &= ~1; | ||||
|  	ath10k_pci_reg_write32(ar, SOC_GLOBAL_RESET_ADDRESS, val); | ||||
|   | ||||
| -	for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) { | ||||
| -		if (!(ath10k_pci_reg_read32(ar, RTC_STATE_ADDRESS) & | ||||
| -					    RTC_STATE_COLD_RESET_MASK)) | ||||
| -			break; | ||||
| -		msleep(1); | ||||
| -	} | ||||
| +	msleep(20); | ||||
|   | ||||
|  	ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot cold reset complete\n"); | ||||
|   | ||||
| @@ -1,76 +0,0 @@ | ||||
| From: Felix Fietkau <nbd@openwrt.org> | ||||
| Date: Thu, 12 Mar 2015 17:10:50 +0100 | ||||
| Subject: [PATCH] ath9k: fix tracking of enabled AP beacons | ||||
|  | ||||
| sc->nbcnvifs tracks assigned beacon slots, not enabled beacons. | ||||
| Therefore, it cannot be used to decide if cur_conf->enable_beacon (bool) | ||||
| should be updated, or if beacons have been enabled already. | ||||
| With the current code (depending on the order of calls), beacons often | ||||
| do not get enabled in an AP+STA setup. | ||||
| To fix tracking of enabled beacons, convert cur_conf->enable_beacon to a | ||||
| bitmask of enabled beacon slots. | ||||
|  | ||||
| Cc: stable@vger.kernel.org | ||||
| Signed-off-by: Felix Fietkau <nbd@openwrt.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath9k/beacon.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/beacon.c | ||||
| @@ -219,12 +219,15 @@ void ath9k_beacon_remove_slot(struct ath | ||||
|  	struct ath_common *common = ath9k_hw_common(sc->sc_ah); | ||||
|  	struct ath_vif *avp = (void *)vif->drv_priv; | ||||
|  	struct ath_buf *bf = avp->av_bcbuf; | ||||
| +	struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon; | ||||
|   | ||||
|  	ath_dbg(common, CONFIG, "Removing interface at beacon slot: %d\n", | ||||
|  		avp->av_bslot); | ||||
|   | ||||
|  	tasklet_disable(&sc->bcon_tasklet); | ||||
|   | ||||
| +	cur_conf->enable_beacon &= ~BIT(avp->av_bslot); | ||||
| + | ||||
|  	if (bf && bf->bf_mpdu) { | ||||
|  		struct sk_buff *skb = bf->bf_mpdu; | ||||
|  		dma_unmap_single(sc->dev, bf->bf_buf_addr, | ||||
| @@ -521,8 +524,7 @@ static bool ath9k_allow_beacon_config(st | ||||
|  	} | ||||
|   | ||||
|  	if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) { | ||||
| -		if ((vif->type != NL80211_IFTYPE_AP) || | ||||
| -		    (sc->nbcnvifs > 1)) { | ||||
| +		if (vif->type != NL80211_IFTYPE_AP) { | ||||
|  			ath_dbg(common, CONFIG, | ||||
|  				"An AP interface is already present !\n"); | ||||
|  			return false; | ||||
| @@ -616,12 +618,14 @@ void ath9k_beacon_config(struct ath_soft | ||||
|  	 * enabling/disabling SWBA. | ||||
|  	 */ | ||||
|  	if (changed & BSS_CHANGED_BEACON_ENABLED) { | ||||
| -		if (!bss_conf->enable_beacon && | ||||
| -		    (sc->nbcnvifs <= 1)) { | ||||
| -			cur_conf->enable_beacon = false; | ||||
| -		} else if (bss_conf->enable_beacon) { | ||||
| -			cur_conf->enable_beacon = true; | ||||
| -			ath9k_cache_beacon_config(sc, ctx, bss_conf); | ||||
| +		bool enabled = cur_conf->enable_beacon; | ||||
| + | ||||
| +		if (!bss_conf->enable_beacon) { | ||||
| +			cur_conf->enable_beacon &= ~BIT(avp->av_bslot); | ||||
| +		} else { | ||||
| +			cur_conf->enable_beacon |= BIT(avp->av_bslot); | ||||
| +			if (!enabled) | ||||
| +				ath9k_cache_beacon_config(sc, ctx, bss_conf); | ||||
|  		} | ||||
|  	} | ||||
|   | ||||
| --- a/drivers/net/wireless/ath/ath9k/common.h | ||||
| +++ b/drivers/net/wireless/ath/ath9k/common.h | ||||
| @@ -54,7 +54,7 @@ struct ath_beacon_config { | ||||
|  	u16 dtim_period; | ||||
|  	u16 bmiss_timeout; | ||||
|  	u8 dtim_count; | ||||
| -	bool enable_beacon; | ||||
| +	u8 enable_beacon; | ||||
|  	bool ibss_creator; | ||||
|  	u32 nexttbtt; | ||||
|  	u32 intval; | ||||
| @@ -1,43 +0,0 @@ | ||||
| From: Felix Fietkau <nbd@openwrt.org> | ||||
| Date: Fri, 13 Mar 2015 10:49:40 +0100 | ||||
| Subject: [PATCH] mac80211: minstrel_ht: fix rounding issue in MCS duration | ||||
|  calculation | ||||
|  | ||||
| On very high MCS bitrates, the calculated duration of rates that are | ||||
| next to each other can be very imprecise, due to the small packet size | ||||
| used as reference (1200 bytes). | ||||
| This is most visible in VHT80 nss=2 MCS8/9, for which minstrel shows the | ||||
| same throughput when the probability is also the same. This leads to a | ||||
| bad rate selection for such rates. | ||||
|  | ||||
| Fix this issue by introducing an average A-MPDU size factor into the | ||||
| calculation. | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@openwrt.org> | ||||
| --- | ||||
|  | ||||
| --- a/net/mac80211/rc80211_minstrel_ht.c | ||||
| +++ b/net/mac80211/rc80211_minstrel_ht.c | ||||
| @@ -17,10 +17,11 @@ | ||||
|  #include "rc80211_minstrel.h" | ||||
|  #include "rc80211_minstrel_ht.h" | ||||
|   | ||||
| +#define AVG_AMPDU_SIZE	16 | ||||
|  #define AVG_PKT_SIZE	1200 | ||||
|   | ||||
|  /* Number of bits for an average sized packet */ | ||||
| -#define MCS_NBITS (AVG_PKT_SIZE << 3) | ||||
| +#define MCS_NBITS ((AVG_PKT_SIZE * AVG_AMPDU_SIZE) << 3) | ||||
|   | ||||
|  /* Number of symbols for a packet with (bps) bits per symbol */ | ||||
|  #define MCS_NSYMS(bps) DIV_ROUND_UP(MCS_NBITS, (bps)) | ||||
| @@ -33,7 +34,8 @@ | ||||
|  	) | ||||
|   | ||||
|  /* Transmit duration for the raw data part of an average sized packet */ | ||||
| -#define MCS_DURATION(streams, sgi, bps) MCS_SYMBOL_TIME(sgi, MCS_NSYMS((streams) * (bps))) | ||||
| +#define MCS_DURATION(streams, sgi, bps) \ | ||||
| +	(MCS_SYMBOL_TIME(sgi, MCS_NSYMS((streams) * (bps))) / AVG_AMPDU_SIZE) | ||||
|   | ||||
|  #define BW_20			0 | ||||
|  #define BW_40			1 | ||||
| @@ -1,22 +0,0 @@ | ||||
| From: Felix Fietkau <nbd@openwrt.org> | ||||
| Date: Sun, 15 Mar 2015 08:02:37 +0100 | ||||
| Subject: [PATCH] ath9k: disable TPC support again (for now) | ||||
|  | ||||
| TPC support has been observed to cause some tx power fluctuations on | ||||
| some devices with at least AR934x and AR938x chips. | ||||
| Disable it for now until the bugs have been found and fixed | ||||
|  | ||||
| Signed-off-by: Felix Fietkau <nbd@openwrt.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath9k/hw.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/hw.c | ||||
| @@ -424,7 +424,7 @@ static void ath9k_hw_init_defaults(struc | ||||
|  	ah->power_mode = ATH9K_PM_UNDEFINED; | ||||
|  	ah->htc_reset_init = true; | ||||
|   | ||||
| -	ah->tpc_enabled = true; | ||||
| +	ah->tpc_enabled = false; | ||||
|   | ||||
|  	ah->ani_function = ATH9K_ANI_ALL; | ||||
|  	if (!AR_SREV_9300_20_OR_LATER(ah)) | ||||
| @@ -1,21 +0,0 @@ | ||||
| From: Johannes Berg <johannes.berg@intel.com> | ||||
| Date: Tue, 24 Feb 2015 00:28:18 +0100 | ||||
| Subject: [PATCH] mac80211: don't look up stations for multicast addresses | ||||
|  | ||||
| Since multicast addresses don't exist as stations, don't attempt | ||||
| to look them up in the hashtable on TX. | ||||
|  | ||||
| Signed-off-by: Johannes Berg <johannes.berg@intel.com> | ||||
| --- | ||||
|  | ||||
| --- a/net/mac80211/tx.c | ||||
| +++ b/net/mac80211/tx.c | ||||
| @@ -1161,7 +1161,7 @@ ieee80211_tx_prepare(struct ieee80211_su | ||||
|  		   tx->sdata->control_port_protocol == tx->skb->protocol) { | ||||
|  		tx->sta = sta_info_get_bss(sdata, hdr->addr1); | ||||
|  	} | ||||
| -	if (!tx->sta) | ||||
| +	if (!tx->sta && !is_multicast_ether_addr(hdr->addr1)) | ||||
|  		tx->sta = sta_info_get(sdata, hdr->addr1); | ||||
|   | ||||
|  	if (tx->sta && ieee80211_is_data_qos(hdr->frame_control) && | ||||
| @@ -1,130 +0,0 @@ | ||||
| From: Johannes Berg <johannes.berg@intel.com> | ||||
| Date: Fri, 20 Mar 2015 11:41:58 +0100 | ||||
| Subject: [PATCH] mac80211: remove drop_unencrypted code | ||||
|  | ||||
| This mechanism was historic, and only ever used by IBSS, which | ||||
| also doesn't need to have it as it properly manages station's | ||||
| 802.1X PAE state (or, with WEP, always has a key.) | ||||
|  | ||||
| Remove the mechanism to clean up the code. | ||||
|  | ||||
| Signed-off-by: Johannes Berg <johannes.berg@intel.com> | ||||
| --- | ||||
|  | ||||
| --- a/net/mac80211/debugfs.c | ||||
| +++ b/net/mac80211/debugfs.c | ||||
| @@ -274,8 +274,6 @@ void debugfs_hw_add(struct ieee80211_loc | ||||
|  #ifdef CPTCFG_MAC80211_DEBUG_COUNTERS | ||||
|  	DEBUGFS_STATS_ADD(tx_handlers_drop, local->tx_handlers_drop); | ||||
|  	DEBUGFS_STATS_ADD(tx_handlers_queued, local->tx_handlers_queued); | ||||
| -	DEBUGFS_STATS_ADD(tx_handlers_drop_unencrypted, | ||||
| -		local->tx_handlers_drop_unencrypted); | ||||
|  	DEBUGFS_STATS_ADD(tx_handlers_drop_fragment, | ||||
|  		local->tx_handlers_drop_fragment); | ||||
|  	DEBUGFS_STATS_ADD(tx_handlers_drop_wep, | ||||
| --- a/net/mac80211/debugfs_netdev.c | ||||
| +++ b/net/mac80211/debugfs_netdev.c | ||||
| @@ -177,7 +177,6 @@ static ssize_t ieee80211_if_write_##name | ||||
|  	IEEE80211_IF_FILE_R(name) | ||||
|   | ||||
|  /* common attributes */ | ||||
| -IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC); | ||||
|  IEEE80211_IF_FILE(rc_rateidx_mask_2ghz, rc_rateidx_mask[IEEE80211_BAND_2GHZ], | ||||
|  		  HEX); | ||||
|  IEEE80211_IF_FILE(rc_rateidx_mask_5ghz, rc_rateidx_mask[IEEE80211_BAND_5GHZ], | ||||
| @@ -562,7 +561,6 @@ IEEE80211_IF_FILE(dot11MeshAwakeWindowDu | ||||
|   | ||||
|  static void add_common_files(struct ieee80211_sub_if_data *sdata) | ||||
|  { | ||||
| -	DEBUGFS_ADD(drop_unencrypted); | ||||
|  	DEBUGFS_ADD(rc_rateidx_mask_2ghz); | ||||
|  	DEBUGFS_ADD(rc_rateidx_mask_5ghz); | ||||
|  	DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz); | ||||
| --- a/net/mac80211/ibss.c | ||||
| +++ b/net/mac80211/ibss.c | ||||
| @@ -249,8 +249,6 @@ static void __ieee80211_sta_join_ibss(st | ||||
|  	if (presp) | ||||
|  		kfree_rcu(presp, rcu_head); | ||||
|   | ||||
| -	sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0; | ||||
| - | ||||
|  	/* make a copy of the chandef, it could be modified below. */ | ||||
|  	chandef = *req_chandef; | ||||
|  	chan = chandef.chan; | ||||
| @@ -1289,8 +1287,6 @@ static void ieee80211_sta_create_ibss(st | ||||
|   | ||||
|  	if (ifibss->privacy) | ||||
|  		capability |= WLAN_CAPABILITY_PRIVACY; | ||||
| -	else | ||||
| -		sdata->drop_unencrypted = 0; | ||||
|   | ||||
|  	__ieee80211_sta_join_ibss(sdata, bssid, sdata->vif.bss_conf.beacon_int, | ||||
|  				  &ifibss->chandef, ifibss->basic_rates, | ||||
| --- a/net/mac80211/ieee80211_i.h | ||||
| +++ b/net/mac80211/ieee80211_i.h | ||||
| @@ -842,8 +842,6 @@ struct ieee80211_sub_if_data { | ||||
|   | ||||
|  	unsigned long state; | ||||
|   | ||||
| -	int drop_unencrypted; | ||||
| - | ||||
|  	char name[IFNAMSIZ]; | ||||
|   | ||||
|  	/* Fragment table for host-based reassembly */ | ||||
| @@ -1289,7 +1287,6 @@ struct ieee80211_local { | ||||
|  	/* TX/RX handler statistics */ | ||||
|  	unsigned int tx_handlers_drop; | ||||
|  	unsigned int tx_handlers_queued; | ||||
| -	unsigned int tx_handlers_drop_unencrypted; | ||||
|  	unsigned int tx_handlers_drop_fragment; | ||||
|  	unsigned int tx_handlers_drop_wep; | ||||
|  	unsigned int tx_handlers_drop_not_assoc; | ||||
| --- a/net/mac80211/iface.c | ||||
| +++ b/net/mac80211/iface.c | ||||
| @@ -1535,7 +1535,6 @@ int ieee80211_if_change_type(struct ieee | ||||
|  	} | ||||
|   | ||||
|  	/* reset some values that shouldn't be kept across type changes */ | ||||
| -	sdata->drop_unencrypted = 0; | ||||
|  	if (type == NL80211_IFTYPE_STATION) | ||||
|  		sdata->u.mgd.use_4addr = false; | ||||
|   | ||||
| --- a/net/mac80211/rx.c | ||||
| +++ b/net/mac80211/rx.c | ||||
| @@ -1897,8 +1897,7 @@ static int ieee80211_drop_unencrypted(st | ||||
|  	/* Drop unencrypted frames if key is set. */ | ||||
|  	if (unlikely(!ieee80211_has_protected(fc) && | ||||
|  		     !ieee80211_is_nullfunc(fc) && | ||||
| -		     ieee80211_is_data(fc) && | ||||
| -		     (rx->key || rx->sdata->drop_unencrypted))) | ||||
| +		     ieee80211_is_data(fc) && rx->key)) | ||||
|  		return -EACCES; | ||||
|   | ||||
|  	return 0; | ||||
| --- a/net/mac80211/tx.c | ||||
| +++ b/net/mac80211/tx.c | ||||
| @@ -594,23 +594,8 @@ ieee80211_tx_h_select_key(struct ieee802 | ||||
|  	else if (!is_multicast_ether_addr(hdr->addr1) && | ||||
|  		 (key = rcu_dereference(tx->sdata->default_unicast_key))) | ||||
|  		tx->key = key; | ||||
| -	else if (info->flags & IEEE80211_TX_CTL_INJECTED) | ||||
| +	else | ||||
|  		tx->key = NULL; | ||||
| -	else if (!tx->sdata->drop_unencrypted) | ||||
| -		tx->key = NULL; | ||||
| -	else if (tx->skb->protocol == tx->sdata->control_port_protocol) | ||||
| -		tx->key = NULL; | ||||
| -	else if (ieee80211_is_robust_mgmt_frame(tx->skb) && | ||||
| -		 !(ieee80211_is_action(hdr->frame_control) && | ||||
| -		   tx->sta && test_sta_flag(tx->sta, WLAN_STA_MFP))) | ||||
| -		tx->key = NULL; | ||||
| -	else if (ieee80211_is_mgmt(hdr->frame_control) && | ||||
| -		 !ieee80211_is_robust_mgmt_frame(tx->skb)) | ||||
| -		tx->key = NULL; | ||||
| -	else { | ||||
| -		I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted); | ||||
| -		return TX_DROP; | ||||
| -	} | ||||
|   | ||||
|  	if (tx->key) { | ||||
|  		bool skip_hw = false; | ||||
| @@ -1,71 +0,0 @@ | ||||
| From: Johannes Berg <johannes.berg@intel.com> | ||||
| Date: Fri, 20 Mar 2015 16:24:21 +0100 | ||||
| Subject: [PATCH] mac80211: don't look up destination station twice | ||||
|  | ||||
| There's no need to look up the destination station twice while | ||||
| building the 802.11 header for a given frame if the frame will | ||||
| actually be transmitted to the station we initially looked up. | ||||
|  | ||||
| This happens for 4-addr VLAN interfaces and TDLS connections, which | ||||
| both directly send the frame to the station they looked up, though | ||||
| in the case of TDLS some station conditions need to be checked. | ||||
|  | ||||
| To avoid that, add a variable indicating that we've looked up the | ||||
| station that the frame is going to be transmitted to, and avoid the | ||||
| lookup/flag checking if it already has been done. | ||||
|  | ||||
| In the TDLS case, also move the authorized/wme_sta flag assignment | ||||
| to the correct place, i.e. only when that station is really used. | ||||
| Before this change, the new lookup should always have succeeded so | ||||
| that the potentially erroneous data would be overwritten. | ||||
|  | ||||
| Signed-off-by: Johannes Berg <johannes.berg@intel.com> | ||||
| --- | ||||
|  | ||||
| --- a/net/mac80211/tx.c | ||||
| +++ b/net/mac80211/tx.c | ||||
| @@ -1894,6 +1894,7 @@ static struct sk_buff *ieee80211_build_h | ||||
|  	bool wme_sta = false, authorized = false, tdls_auth = false; | ||||
|  	bool tdls_peer = false, tdls_setup_frame = false; | ||||
|  	bool multicast; | ||||
| +	bool have_station = false; | ||||
|  	u16 info_id = 0; | ||||
|  	struct ieee80211_chanctx_conf *chanctx_conf; | ||||
|  	struct ieee80211_sub_if_data *ap_sdata; | ||||
| @@ -1918,6 +1919,7 @@ static struct sk_buff *ieee80211_build_h | ||||
|  			hdrlen = 30; | ||||
|  			authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); | ||||
|  			wme_sta = sta->sta.wme; | ||||
| +			have_station = true; | ||||
|  		} | ||||
|  		ap_sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, | ||||
|  					u.ap); | ||||
| @@ -2034,9 +2036,6 @@ static struct sk_buff *ieee80211_build_h | ||||
|  		if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) { | ||||
|  			sta = sta_info_get(sdata, skb->data); | ||||
|  			if (sta) { | ||||
| -				authorized = test_sta_flag(sta, | ||||
| -							WLAN_STA_AUTHORIZED); | ||||
| -				wme_sta = sta->sta.wme; | ||||
|  				tdls_peer = test_sta_flag(sta, | ||||
|  							  WLAN_STA_TDLS_PEER); | ||||
|  				tdls_auth = test_sta_flag(sta, | ||||
| @@ -2068,6 +2067,9 @@ static struct sk_buff *ieee80211_build_h | ||||
|  			memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); | ||||
|  			memcpy(hdr.addr3, sdata->u.mgd.bssid, ETH_ALEN); | ||||
|  			hdrlen = 24; | ||||
| +			have_station = true; | ||||
| +			authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); | ||||
| +			wme_sta = sta->sta.wme; | ||||
|  		}  else if (sdata->u.mgd.use_4addr && | ||||
|  			    cpu_to_be16(ethertype) != sdata->control_port_protocol) { | ||||
|  			fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | | ||||
| @@ -2130,7 +2132,7 @@ static struct sk_buff *ieee80211_build_h | ||||
|  	 * in AP mode) | ||||
|  	 */ | ||||
|  	multicast = is_multicast_ether_addr(hdr.addr1); | ||||
| -	if (!multicast) { | ||||
| +	if (!multicast && !have_station) { | ||||
|  		sta = sta_info_get(sdata, hdr.addr1); | ||||
|  		if (sta) { | ||||
|  			authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); | ||||
| @@ -1,27 +0,0 @@ | ||||
| From: Johannes Berg <johannes.berg@intel.com> | ||||
| Date: Fri, 20 Mar 2015 16:24:22 +0100 | ||||
| Subject: [PATCH] mac80211: drop 4-addr VLAN frames earlier if not | ||||
|  connected | ||||
|  | ||||
| If there's no station on the 4-addr VLAN interface, then frames | ||||
| cannot be transmitted. Drop such frames earlier, before setting | ||||
| up all the information for them. | ||||
|  | ||||
| We should keep the old check though since that code might be used | ||||
| for other internally-generated frames. | ||||
|  | ||||
| Signed-off-by: Johannes Berg <johannes.berg@intel.com> | ||||
| --- | ||||
|  | ||||
| --- a/net/mac80211/tx.c | ||||
| +++ b/net/mac80211/tx.c | ||||
| @@ -1920,6 +1920,9 @@ static struct sk_buff *ieee80211_build_h | ||||
|  			authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); | ||||
|  			wme_sta = sta->sta.wme; | ||||
|  			have_station = true; | ||||
| +		} else if (sdata->wdev.use_4addr) { | ||||
| +			ret = -ENOLINK; | ||||
| +			goto free; | ||||
|  		} | ||||
|  		ap_sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, | ||||
|  					u.ap); | ||||
| @@ -1,33 +0,0 @@ | ||||
| From: Johannes Berg <johannes.berg@intel.com> | ||||
| Date: Fri, 20 Mar 2015 16:24:23 +0100 | ||||
| Subject: [PATCH] mac80211: mesh: avoid pointless station lookup | ||||
|  | ||||
| In ieee80211_build_hdr(), the station is looked up to build the | ||||
| header correctly (QoS field) and to check for authorization. For | ||||
| mesh, authorization isn't checked here, and QoS capability is | ||||
| mandatory, so the station lookup can be avoided. | ||||
|  | ||||
| Signed-off-by: Johannes Berg <johannes.berg@intel.com> | ||||
| --- | ||||
|  | ||||
| --- a/net/mac80211/tx.c | ||||
| +++ b/net/mac80211/tx.c | ||||
| @@ -2130,12 +2130,14 @@ static struct sk_buff *ieee80211_build_h | ||||
|  	} | ||||
|   | ||||
|  	/* | ||||
| -	 * There's no need to try to look up the destination | ||||
| -	 * if it is a multicast address (which can only happen | ||||
| -	 * in AP mode) | ||||
| +	 * There's no need to try to look up the destination station | ||||
| +	 * if it is a multicast address. In mesh, there's no need to | ||||
| +	 * look up the station at all as it always must be QoS capable | ||||
| +	 * and mesh mode checks authorization later. | ||||
|  	 */ | ||||
|  	multicast = is_multicast_ether_addr(hdr.addr1); | ||||
| -	if (!multicast && !have_station) { | ||||
| +	if (!multicast && !have_station && | ||||
| +	    !ieee80211_vif_is_mesh(&sdata->vif)) { | ||||
|  		sta = sta_info_get(sdata, hdr.addr1); | ||||
|  		if (sta) { | ||||
|  			authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); | ||||
| @@ -1,267 +0,0 @@ | ||||
| From: Johannes Berg <johannes.berg@intel.com> | ||||
| Date: Fri, 20 Mar 2015 14:18:27 +0100 | ||||
| Subject: [PATCH] mac80211: avoid duplicate TX path station lookup | ||||
|  | ||||
| Instead of looking up the destination station twice in the TX path | ||||
| (first to build the header, and then for control processing), save | ||||
| it when building the header and use it later in the TX path. | ||||
|  | ||||
| To avoid having to look up the station in the many callers, allow | ||||
| those to pass %NULL which keeps the existing lookup. | ||||
|  | ||||
| Signed-off-by: Johannes Berg <johannes.berg@intel.com> | ||||
| --- | ||||
|  | ||||
| --- a/net/mac80211/cfg.c | ||||
| +++ b/net/mac80211/cfg.c | ||||
| @@ -3565,7 +3565,7 @@ static int ieee80211_probe_client(struct | ||||
|  		nullfunc->qos_ctrl = cpu_to_le16(7); | ||||
|   | ||||
|  	local_bh_disable(); | ||||
| -	ieee80211_xmit(sdata, skb); | ||||
| +	ieee80211_xmit(sdata, sta, skb); | ||||
|  	local_bh_enable(); | ||||
|  	rcu_read_unlock(); | ||||
|   | ||||
| --- a/net/mac80211/ieee80211_i.h | ||||
| +++ b/net/mac80211/ieee80211_i.h | ||||
| @@ -1775,7 +1775,8 @@ void mac80211_ev_michael_mic_failure(str | ||||
|  				     gfp_t gfp); | ||||
|  void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, | ||||
|  			       bool bss_notify); | ||||
| -void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); | ||||
| +void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, | ||||
| +		    struct sta_info *sta, struct sk_buff *skb); | ||||
|   | ||||
|  void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata, | ||||
|  				 struct sk_buff *skb, int tid, | ||||
| --- a/net/mac80211/sta_info.c | ||||
| +++ b/net/mac80211/sta_info.c | ||||
| @@ -1279,7 +1279,7 @@ static void ieee80211_send_null_response | ||||
|  	} | ||||
|   | ||||
|  	info->band = chanctx_conf->def.chan->band; | ||||
| -	ieee80211_xmit(sdata, skb); | ||||
| +	ieee80211_xmit(sdata, sta, skb); | ||||
|  	rcu_read_unlock(); | ||||
|  } | ||||
|   | ||||
| --- a/net/mac80211/tx.c | ||||
| +++ b/net/mac80211/tx.c | ||||
| @@ -1110,11 +1110,13 @@ static bool ieee80211_tx_prep_agg(struct | ||||
|   | ||||
|  /* | ||||
|   * initialises @tx | ||||
| + * pass %NULL for the station if unknown, a valid pointer if known | ||||
| + * or an ERR_PTR() if the station is known not to exist | ||||
|   */ | ||||
|  static ieee80211_tx_result | ||||
|  ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, | ||||
|  		     struct ieee80211_tx_data *tx, | ||||
| -		     struct sk_buff *skb) | ||||
| +		     struct sta_info *sta, struct sk_buff *skb) | ||||
|  { | ||||
|  	struct ieee80211_local *local = sdata->local; | ||||
|  	struct ieee80211_hdr *hdr; | ||||
| @@ -1137,17 +1139,22 @@ ieee80211_tx_prepare(struct ieee80211_su | ||||
|   | ||||
|  	hdr = (struct ieee80211_hdr *) skb->data; | ||||
|   | ||||
| -	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { | ||||
| -		tx->sta = rcu_dereference(sdata->u.vlan.sta); | ||||
| -		if (!tx->sta && sdata->dev->ieee80211_ptr->use_4addr) | ||||
| -			return TX_DROP; | ||||
| -	} else if (info->flags & (IEEE80211_TX_CTL_INJECTED | | ||||
| -				  IEEE80211_TX_INTFL_NL80211_FRAME_TX) || | ||||
| -		   tx->sdata->control_port_protocol == tx->skb->protocol) { | ||||
| -		tx->sta = sta_info_get_bss(sdata, hdr->addr1); | ||||
| +	if (likely(sta)) { | ||||
| +		if (!IS_ERR(sta)) | ||||
| +			tx->sta = sta; | ||||
| +	} else { | ||||
| +		if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { | ||||
| +			tx->sta = rcu_dereference(sdata->u.vlan.sta); | ||||
| +			if (!tx->sta && sdata->wdev.use_4addr) | ||||
| +				return TX_DROP; | ||||
| +		} else if (info->flags & (IEEE80211_TX_INTFL_NL80211_FRAME_TX | | ||||
| +					  IEEE80211_TX_CTL_INJECTED) || | ||||
| +			   tx->sdata->control_port_protocol == tx->skb->protocol) { | ||||
| +			tx->sta = sta_info_get_bss(sdata, hdr->addr1); | ||||
| +		} | ||||
| +		if (!tx->sta && !is_multicast_ether_addr(hdr->addr1)) | ||||
| +			tx->sta = sta_info_get(sdata, hdr->addr1); | ||||
|  	} | ||||
| -	if (!tx->sta && !is_multicast_ether_addr(hdr->addr1)) | ||||
| -		tx->sta = sta_info_get(sdata, hdr->addr1); | ||||
|   | ||||
|  	if (tx->sta && ieee80211_is_data_qos(hdr->frame_control) && | ||||
|  	    !ieee80211_is_qos_nullfunc(hdr->frame_control) && | ||||
| @@ -1485,7 +1492,7 @@ bool ieee80211_tx_prepare_skb(struct iee | ||||
|  	struct ieee80211_tx_data tx; | ||||
|  	struct sk_buff *skb2; | ||||
|   | ||||
| -	if (ieee80211_tx_prepare(sdata, &tx, skb) == TX_DROP) | ||||
| +	if (ieee80211_tx_prepare(sdata, &tx, NULL, skb) == TX_DROP) | ||||
|  		return false; | ||||
|   | ||||
|  	info->band = band; | ||||
| @@ -1518,7 +1525,8 @@ EXPORT_SYMBOL(ieee80211_tx_prepare_skb); | ||||
|   * Returns false if the frame couldn't be transmitted but was queued instead. | ||||
|   */ | ||||
|  static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata, | ||||
| -			 struct sk_buff *skb, bool txpending) | ||||
| +			 struct sta_info *sta, struct sk_buff *skb, | ||||
| +			 bool txpending) | ||||
|  { | ||||
|  	struct ieee80211_local *local = sdata->local; | ||||
|  	struct ieee80211_tx_data tx; | ||||
| @@ -1534,7 +1542,7 @@ static bool ieee80211_tx(struct ieee8021 | ||||
|   | ||||
|  	/* initialises tx */ | ||||
|  	led_len = skb->len; | ||||
| -	res_prepare = ieee80211_tx_prepare(sdata, &tx, skb); | ||||
| +	res_prepare = ieee80211_tx_prepare(sdata, &tx, sta, skb); | ||||
|   | ||||
|  	if (unlikely(res_prepare == TX_DROP)) { | ||||
|  		ieee80211_free_txskb(&local->hw, skb); | ||||
| @@ -1590,7 +1598,8 @@ static int ieee80211_skb_resize(struct i | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| -void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) | ||||
| +void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, | ||||
| +		    struct sta_info *sta, struct sk_buff *skb) | ||||
|  { | ||||
|  	struct ieee80211_local *local = sdata->local; | ||||
|  	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||||
| @@ -1625,7 +1634,7 @@ void ieee80211_xmit(struct ieee80211_sub | ||||
|  	} | ||||
|   | ||||
|  	ieee80211_set_qos_hdr(sdata, skb); | ||||
| -	ieee80211_tx(sdata, skb, false); | ||||
| +	ieee80211_tx(sdata, sta, skb, false); | ||||
|  } | ||||
|   | ||||
|  static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb) | ||||
| @@ -1846,7 +1855,7 @@ netdev_tx_t ieee80211_monitor_start_xmit | ||||
|  		goto fail_rcu; | ||||
|   | ||||
|  	info->band = chandef->chan->band; | ||||
| -	ieee80211_xmit(sdata, skb); | ||||
| +	ieee80211_xmit(sdata, NULL, skb); | ||||
|  	rcu_read_unlock(); | ||||
|   | ||||
|  	return NETDEV_TX_OK; | ||||
| @@ -1877,7 +1886,8 @@ fail: | ||||
|   * Returns: the (possibly reallocated) skb or an ERR_PTR() code | ||||
|   */ | ||||
|  static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, | ||||
| -					   struct sk_buff *skb, u32 info_flags) | ||||
| +					   struct sk_buff *skb, u32 info_flags, | ||||
| +					   struct sta_info **sta_out) | ||||
|  { | ||||
|  	struct ieee80211_local *local = sdata->local; | ||||
|  	struct ieee80211_tx_info *info; | ||||
| @@ -1920,6 +1930,7 @@ static struct sk_buff *ieee80211_build_h | ||||
|  			authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); | ||||
|  			wme_sta = sta->sta.wme; | ||||
|  			have_station = true; | ||||
| +			*sta_out = sta; | ||||
|  		} else if (sdata->wdev.use_4addr) { | ||||
|  			ret = -ENOLINK; | ||||
|  			goto free; | ||||
| @@ -2073,6 +2084,7 @@ static struct sk_buff *ieee80211_build_h | ||||
|  			have_station = true; | ||||
|  			authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); | ||||
|  			wme_sta = sta->sta.wme; | ||||
| +			*sta_out = sta; | ||||
|  		}  else if (sdata->u.mgd.use_4addr && | ||||
|  			    cpu_to_be16(ethertype) != sdata->control_port_protocol) { | ||||
|  			fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | | ||||
| @@ -2136,13 +2148,18 @@ static struct sk_buff *ieee80211_build_h | ||||
|  	 * and mesh mode checks authorization later. | ||||
|  	 */ | ||||
|  	multicast = is_multicast_ether_addr(hdr.addr1); | ||||
| -	if (!multicast && !have_station && | ||||
| -	    !ieee80211_vif_is_mesh(&sdata->vif)) { | ||||
| -		sta = sta_info_get(sdata, hdr.addr1); | ||||
| +	if (multicast) { | ||||
| +		*sta_out = ERR_PTR(-ENOENT); | ||||
| +	} else if (!have_station && !ieee80211_vif_is_mesh(&sdata->vif)) { | ||||
| +		if (sdata->control_port_protocol == skb->protocol) | ||||
| +			sta = sta_info_get_bss(sdata, hdr.addr1); | ||||
| +		else | ||||
| +			sta = sta_info_get(sdata, hdr.addr1); | ||||
|  		if (sta) { | ||||
|  			authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); | ||||
|  			wme_sta = sta->sta.wme; | ||||
|  		} | ||||
| +		*sta_out = sta ?: ERR_PTR(-ENOENT); | ||||
|  	} | ||||
|   | ||||
|  	/* For mesh, the use of the QoS header is mandatory */ | ||||
| @@ -2320,6 +2337,7 @@ void __ieee80211_subif_start_xmit(struct | ||||
|  				  u32 info_flags) | ||||
|  { | ||||
|  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||||
| +	struct sta_info *sta = NULL; | ||||
|   | ||||
|  	if (unlikely(skb->len < ETH_HLEN)) { | ||||
|  		kfree_skb(skb); | ||||
| @@ -2328,7 +2346,7 @@ void __ieee80211_subif_start_xmit(struct | ||||
|   | ||||
|  	rcu_read_lock(); | ||||
|   | ||||
| -	skb = ieee80211_build_hdr(sdata, skb, info_flags); | ||||
| +	skb = ieee80211_build_hdr(sdata, skb, info_flags, &sta); | ||||
|  	if (IS_ERR(skb)) | ||||
|  		goto out; | ||||
|   | ||||
| @@ -2336,7 +2354,7 @@ void __ieee80211_subif_start_xmit(struct | ||||
|  	dev->stats.tx_bytes += skb->len; | ||||
|  	dev->trans_start = jiffies; | ||||
|   | ||||
| -	ieee80211_xmit(sdata, skb); | ||||
| +	ieee80211_xmit(sdata, sta, skb); | ||||
|   out: | ||||
|  	rcu_read_unlock(); | ||||
|  } | ||||
| @@ -2364,10 +2382,11 @@ ieee80211_build_data_template(struct iee | ||||
|  		.local = sdata->local, | ||||
|  		.sdata = sdata, | ||||
|  	}; | ||||
| +	struct sta_info *sta_ignore; | ||||
|   | ||||
|  	rcu_read_lock(); | ||||
|   | ||||
| -	skb = ieee80211_build_hdr(sdata, skb, info_flags); | ||||
| +	skb = ieee80211_build_hdr(sdata, skb, info_flags, &sta_ignore); | ||||
|  	if (IS_ERR(skb)) | ||||
|  		goto out; | ||||
|   | ||||
| @@ -2425,7 +2444,7 @@ static bool ieee80211_tx_pending_skb(str | ||||
|  			return true; | ||||
|  		} | ||||
|  		info->band = chanctx_conf->def.chan->band; | ||||
| -		result = ieee80211_tx(sdata, skb, true); | ||||
| +		result = ieee80211_tx(sdata, NULL, skb, true); | ||||
|  	} else { | ||||
|  		struct sk_buff_head skbs; | ||||
|   | ||||
| @@ -3163,7 +3182,7 @@ ieee80211_get_buffered_bc(struct ieee802 | ||||
|   | ||||
|  		if (sdata->vif.type == NL80211_IFTYPE_AP) | ||||
|  			sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev); | ||||
| -		if (!ieee80211_tx_prepare(sdata, &tx, skb)) | ||||
| +		if (!ieee80211_tx_prepare(sdata, &tx, NULL, skb)) | ||||
|  			break; | ||||
|  		dev_kfree_skb_any(skb); | ||||
|  	} | ||||
| @@ -3295,6 +3314,6 @@ void __ieee80211_tx_skb_tid_band(struct | ||||
|  	 */ | ||||
|  	local_bh_disable(); | ||||
|  	IEEE80211_SKB_CB(skb)->band = band; | ||||
| -	ieee80211_xmit(sdata, skb); | ||||
| +	ieee80211_xmit(sdata, NULL, skb); | ||||
|  	local_bh_enable(); | ||||
|  } | ||||
| @@ -1,38 +0,0 @@ | ||||
| From: John Linville <linville@tuxdriver.com> | ||||
| Date: Tue, 31 Mar 2015 10:49:14 -0400 | ||||
| Subject: [PATCH] mac80211: reduce log spam from ieee80211_handle_pwr_constr | ||||
|  | ||||
| This changes a couple of messages from sdata_info to sdata_dbg. | ||||
| This should reduce some log spam, as reported here: | ||||
|  | ||||
| 	https://bugzilla.redhat.com/show_bug.cgi?id=1206468 | ||||
|  | ||||
| Signed-off-by: John W. Linville <linville@tuxdriver.com> | ||||
| Signed-off-by: Johannes Berg <johannes.berg@intel.com> | ||||
| --- | ||||
|  | ||||
| --- a/net/mac80211/mlme.c | ||||
| +++ b/net/mac80211/mlme.c | ||||
| @@ -1347,15 +1347,15 @@ static u32 ieee80211_handle_pwr_constr(s | ||||
|  	 */ | ||||
|  	if (has_80211h_pwr && | ||||
|  	    (!has_cisco_pwr || pwr_level_80211h <= pwr_level_cisco)) { | ||||
| -		sdata_info(sdata, | ||||
| -			   "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n", | ||||
| -			   pwr_level_80211h, chan_pwr, pwr_reduction_80211h, | ||||
| -			   sdata->u.mgd.bssid); | ||||
| +		sdata_dbg(sdata, | ||||
| +			  "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n", | ||||
| +			  pwr_level_80211h, chan_pwr, pwr_reduction_80211h, | ||||
| +			  sdata->u.mgd.bssid); | ||||
|  		new_ap_level = pwr_level_80211h; | ||||
|  	} else {  /* has_cisco_pwr is always true here. */ | ||||
| -		sdata_info(sdata, | ||||
| -			   "Limiting TX power to %d dBm as advertised by %pM\n", | ||||
| -			   pwr_level_cisco, sdata->u.mgd.bssid); | ||||
| +		sdata_dbg(sdata, | ||||
| +			  "Limiting TX power to %d dBm as advertised by %pM\n", | ||||
| +			  pwr_level_cisco, sdata->u.mgd.bssid); | ||||
|  		new_ap_level = pwr_level_cisco; | ||||
|  	} | ||||
|   | ||||
| @@ -1,35 +0,0 @@ | ||||
| From: Hante Meuleman <meuleman@broadcom.com> | ||||
| Date: Fri, 6 Mar 2015 18:40:41 +0100 | ||||
| Subject: [PATCH] brcmfmac: Fix race condition in msgbuf ioctl processing. | ||||
|  | ||||
| Msgbuf is using a wait_event_timeout to wait for the response on | ||||
| an ioctl. The wakeup routine uses waitqueue_active to see if | ||||
| wait_event_timeout has been called. There is a chance that the | ||||
| response arrives before wait_event_timeout is called, this | ||||
| will result in situation that wait_event_timeout never gets | ||||
| woken again and assumed result will be a timeout. This patch | ||||
| removes that errornous situation by always setting the | ||||
| ctl_completed var before checking for queue active. | ||||
|  | ||||
| Reviewed-by: Arend Van Spriel <arend@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Signed-off-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | ||||
| @@ -481,10 +481,9 @@ static int brcmf_msgbuf_ioctl_resp_wait( | ||||
|   | ||||
|  static void brcmf_msgbuf_ioctl_resp_wake(struct brcmf_msgbuf *msgbuf) | ||||
|  { | ||||
| -	if (waitqueue_active(&msgbuf->ioctl_resp_wait)) { | ||||
| -		msgbuf->ctl_completed = true; | ||||
| +	msgbuf->ctl_completed = true; | ||||
| +	if (waitqueue_active(&msgbuf->ioctl_resp_wait)) | ||||
|  		wake_up(&msgbuf->ioctl_resp_wait); | ||||
| -	} | ||||
|  } | ||||
|   | ||||
|   | ||||
| @@ -1,30 +0,0 @@ | ||||
| From: Hante Meuleman <meuleman@broadcom.com> | ||||
| Date: Wed, 18 Mar 2015 13:25:23 +0100 | ||||
| Subject: [PATCH] brcmfmac: Update msgbuf commonring size for improved | ||||
|  throughput. | ||||
|  | ||||
| Reviewed-by: Arend Van Spriel <arend@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Signed-off-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h | ||||
| @@ -17,11 +17,11 @@ | ||||
|   | ||||
|  #ifdef CPTCFG_BRCMFMAC_PROTO_MSGBUF | ||||
|   | ||||
| -#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_MAX_ITEM	20 | ||||
| -#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT_MAX_ITEM	256 | ||||
| -#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE_MAX_ITEM	20 | ||||
| +#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_MAX_ITEM	64 | ||||
| +#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT_MAX_ITEM	512 | ||||
| +#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE_MAX_ITEM	64 | ||||
|  #define BRCMF_D2H_MSGRING_TX_COMPLETE_MAX_ITEM		1024 | ||||
| -#define BRCMF_D2H_MSGRING_RX_COMPLETE_MAX_ITEM		256 | ||||
| +#define BRCMF_D2H_MSGRING_RX_COMPLETE_MAX_ITEM		512 | ||||
|  #define BRCMF_H2D_TXFLOWRING_MAX_ITEM			512 | ||||
|   | ||||
|  #define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_ITEMSIZE	40 | ||||
| @@ -1,307 +0,0 @@ | ||||
| From: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Date: Sun, 22 Mar 2015 19:29:46 +0100 | ||||
| Subject: [PATCH] ath9k_htc: add new WMI_REG_RMW_CMDID command | ||||
|  | ||||
| Since usb bus add extra delay on each request, a command | ||||
| with read + write requests is too expensive. We can dramtically | ||||
| reduce usb load by moving this command to firmware. | ||||
|  | ||||
| In my tests, this patch will reduce channel scan time | ||||
| for about 5-10 seconds. | ||||
|  | ||||
| Signed-off-by: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath.h | ||||
| +++ b/drivers/net/wireless/ath/ath.h | ||||
| @@ -131,6 +131,9 @@ struct ath_ops { | ||||
|  	void (*enable_write_buffer)(void *); | ||||
|  	void (*write_flush) (void *); | ||||
|  	u32 (*rmw)(void *, u32 reg_offset, u32 set, u32 clr); | ||||
| +	void (*enable_rmw_buffer)(void *); | ||||
| +	void (*rmw_flush) (void *); | ||||
| + | ||||
|  }; | ||||
|   | ||||
|  struct ath_common; | ||||
| --- a/drivers/net/wireless/ath/ath9k/htc.h | ||||
| +++ b/drivers/net/wireless/ath/ath9k/htc.h | ||||
| @@ -444,6 +444,10 @@ static inline void ath9k_htc_stop_btcoex | ||||
|  #define OP_BT_SCAN                 BIT(4) | ||||
|  #define OP_TSF_RESET               BIT(6) | ||||
|   | ||||
| +enum htc_op_flags { | ||||
| +	HTC_FWFLAG_NO_RMW, | ||||
| +}; | ||||
| + | ||||
|  struct ath9k_htc_priv { | ||||
|  	struct device *dev; | ||||
|  	struct ieee80211_hw *hw; | ||||
| @@ -482,6 +486,7 @@ struct ath9k_htc_priv { | ||||
|  	bool reconfig_beacon; | ||||
|  	unsigned int rxfilter; | ||||
|  	unsigned long op_flags; | ||||
| +	unsigned long fw_flags; | ||||
|   | ||||
|  	struct ath9k_hw_cal_data caldata; | ||||
|  	struct ath_spec_scan_priv spec_priv; | ||||
| --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c | ||||
| @@ -376,17 +376,139 @@ static void ath9k_regwrite_flush(void *h | ||||
|  	mutex_unlock(&priv->wmi->multi_write_mutex); | ||||
|  } | ||||
|   | ||||
| -static u32 ath9k_reg_rmw(void *hw_priv, u32 reg_offset, u32 set, u32 clr) | ||||
| +static void ath9k_reg_rmw_buffer(void *hw_priv, | ||||
| +				 u32 reg_offset, u32 set, u32 clr) | ||||
| +{ | ||||
| +	struct ath_hw *ah = (struct ath_hw *) hw_priv; | ||||
| +	struct ath_common *common = ath9k_hw_common(ah); | ||||
| +	struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; | ||||
| +	u32 rsp_status; | ||||
| +	int r; | ||||
| + | ||||
| +	mutex_lock(&priv->wmi->multi_rmw_mutex); | ||||
| + | ||||
| +	/* Store the register/value */ | ||||
| +	priv->wmi->multi_rmw[priv->wmi->multi_rmw_idx].reg = | ||||
| +		cpu_to_be32(reg_offset); | ||||
| +	priv->wmi->multi_rmw[priv->wmi->multi_rmw_idx].set = | ||||
| +		cpu_to_be32(set); | ||||
| +	priv->wmi->multi_rmw[priv->wmi->multi_rmw_idx].clr = | ||||
| +		cpu_to_be32(clr); | ||||
| + | ||||
| +	priv->wmi->multi_rmw_idx++; | ||||
| + | ||||
| +	/* If the buffer is full, send it out. */ | ||||
| +	if (priv->wmi->multi_rmw_idx == MAX_RMW_CMD_NUMBER) { | ||||
| +		r = ath9k_wmi_cmd(priv->wmi, WMI_REG_RMW_CMDID, | ||||
| +			  (u8 *) &priv->wmi->multi_rmw, | ||||
| +			  sizeof(struct register_write) * priv->wmi->multi_rmw_idx, | ||||
| +			  (u8 *) &rsp_status, sizeof(rsp_status), | ||||
| +			  100); | ||||
| +		if (unlikely(r)) { | ||||
| +			ath_dbg(common, WMI, | ||||
| +				"REGISTER RMW FAILED, multi len: %d\n", | ||||
| +				priv->wmi->multi_rmw_idx); | ||||
| +		} | ||||
| +		priv->wmi->multi_rmw_idx = 0; | ||||
| +	} | ||||
| + | ||||
| +	mutex_unlock(&priv->wmi->multi_rmw_mutex); | ||||
| +} | ||||
| + | ||||
| +static void ath9k_reg_rmw_flush(void *hw_priv) | ||||
|  { | ||||
| -	u32 val; | ||||
| +	struct ath_hw *ah = (struct ath_hw *) hw_priv; | ||||
| +	struct ath_common *common = ath9k_hw_common(ah); | ||||
| +	struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; | ||||
| +	u32 rsp_status; | ||||
| +	int r; | ||||
| + | ||||
| +	if (test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags)) | ||||
| +		return; | ||||
| + | ||||
| +	atomic_dec(&priv->wmi->m_rmw_cnt); | ||||
| + | ||||
| +	mutex_lock(&priv->wmi->multi_rmw_mutex); | ||||
| + | ||||
| +	if (priv->wmi->multi_rmw_idx) { | ||||
| +		r = ath9k_wmi_cmd(priv->wmi, WMI_REG_RMW_CMDID, | ||||
| +			  (u8 *) &priv->wmi->multi_rmw, | ||||
| +			  sizeof(struct register_rmw) * priv->wmi->multi_rmw_idx, | ||||
| +			  (u8 *) &rsp_status, sizeof(rsp_status), | ||||
| +			  100); | ||||
| +		if (unlikely(r)) { | ||||
| +			ath_dbg(common, WMI, | ||||
| +				"REGISTER RMW FAILED, multi len: %d\n", | ||||
| +				priv->wmi->multi_rmw_idx); | ||||
| +		} | ||||
| +		priv->wmi->multi_rmw_idx = 0; | ||||
| +	} | ||||
|   | ||||
| -	val = ath9k_regread(hw_priv, reg_offset); | ||||
| -	val &= ~clr; | ||||
| -	val |= set; | ||||
| -	ath9k_regwrite(hw_priv, val, reg_offset); | ||||
| +	mutex_unlock(&priv->wmi->multi_rmw_mutex); | ||||
| +} | ||||
| + | ||||
| +static void ath9k_enable_rmw_buffer(void *hw_priv) | ||||
| +{ | ||||
| +	struct ath_hw *ah = (struct ath_hw *) hw_priv; | ||||
| +	struct ath_common *common = ath9k_hw_common(ah); | ||||
| +	struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; | ||||
| + | ||||
| +	if (test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags)) | ||||
| +		return; | ||||
| + | ||||
| +	atomic_inc(&priv->wmi->m_rmw_cnt); | ||||
| +} | ||||
| + | ||||
| +static u32 ath9k_reg_rmw_single(void *hw_priv, | ||||
| +				 u32 reg_offset, u32 set, u32 clr) | ||||
| +{ | ||||
| +	struct ath_hw *ah = (struct ath_hw *) hw_priv; | ||||
| +	struct ath_common *common = ath9k_hw_common(ah); | ||||
| +	struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; | ||||
| +	struct register_rmw buf, buf_ret; | ||||
| +	int ret; | ||||
| +	u32 val = 0; | ||||
| + | ||||
| +	buf.reg = cpu_to_be32(reg_offset); | ||||
| +	buf.set = cpu_to_be32(set); | ||||
| +	buf.clr = cpu_to_be32(clr); | ||||
| + | ||||
| +	ret = ath9k_wmi_cmd(priv->wmi, WMI_REG_RMW_CMDID, | ||||
| +			  (u8 *) &buf, sizeof(buf), | ||||
| +			  (u8 *) &buf_ret, sizeof(buf_ret), | ||||
| +			  100); | ||||
| +	if (unlikely(ret)) { | ||||
| +		ath_dbg(common, WMI, "REGISTER RMW FAILED:(0x%04x, %d)\n", | ||||
| +			reg_offset, ret); | ||||
| +	} | ||||
|  	return val; | ||||
|  } | ||||
|   | ||||
| +static u32 ath9k_reg_rmw(void *hw_priv, u32 reg_offset, u32 set, u32 clr) | ||||
| +{ | ||||
| +	struct ath_hw *ah = (struct ath_hw *) hw_priv; | ||||
| +	struct ath_common *common = ath9k_hw_common(ah); | ||||
| +	struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; | ||||
| + | ||||
| +	if (test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags)) { | ||||
| +		u32 val; | ||||
| + | ||||
| +		val = REG_READ(ah, reg_offset); | ||||
| +		val &= ~clr; | ||||
| +		val |= set; | ||||
| +		REG_WRITE(ah, reg_offset, val); | ||||
| + | ||||
| +		return 0; | ||||
| +	} | ||||
| + | ||||
| +	if (atomic_read(&priv->wmi->m_rmw_cnt)) | ||||
| +		ath9k_reg_rmw_buffer(hw_priv, reg_offset, set, clr); | ||||
| +	else | ||||
| +		ath9k_reg_rmw_single(hw_priv, reg_offset, set, clr); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
|  static void ath_usb_read_cachesize(struct ath_common *common, int *csz) | ||||
|  { | ||||
|  	*csz = L1_CACHE_BYTES >> 2; | ||||
| @@ -501,6 +623,8 @@ static int ath9k_init_priv(struct ath9k_ | ||||
|  	ah->reg_ops.write = ath9k_regwrite; | ||||
|  	ah->reg_ops.enable_write_buffer = ath9k_enable_regwrite_buffer; | ||||
|  	ah->reg_ops.write_flush = ath9k_regwrite_flush; | ||||
| +	ah->reg_ops.enable_rmw_buffer = ath9k_enable_rmw_buffer; | ||||
| +	ah->reg_ops.rmw_flush = ath9k_reg_rmw_flush; | ||||
|  	ah->reg_ops.rmw = ath9k_reg_rmw; | ||||
|  	priv->ah = ah; | ||||
|   | ||||
| @@ -686,6 +810,12 @@ static int ath9k_init_firmware_version(s | ||||
|  		return -EINVAL; | ||||
|  	} | ||||
|   | ||||
| +	if (priv->fw_version_major == 1 && priv->fw_version_minor < 4) | ||||
| +		set_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags); | ||||
| + | ||||
| +	dev_info(priv->dev, "FW RMW support: %s\n", | ||||
| +		test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags) ? "Off" : "On"); | ||||
| + | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| --- a/drivers/net/wireless/ath/ath9k/hw.h | ||||
| +++ b/drivers/net/wireless/ath/ath9k/hw.h | ||||
| @@ -100,6 +100,18 @@ | ||||
|  			(_ah)->reg_ops.write_flush((_ah));	\ | ||||
|  	} while (0) | ||||
|   | ||||
| +#define ENABLE_REG_RMW_BUFFER(_ah)					\ | ||||
| +	do {								\ | ||||
| +		if ((_ah)->reg_ops.enable_rmw_buffer)	\ | ||||
| +			(_ah)->reg_ops.enable_rmw_buffer((_ah)); \ | ||||
| +	} while (0) | ||||
| + | ||||
| +#define REG_RMW_BUFFER_FLUSH(_ah)					\ | ||||
| +	do {								\ | ||||
| +		if ((_ah)->reg_ops.rmw_flush)		\ | ||||
| +			(_ah)->reg_ops.rmw_flush((_ah));	\ | ||||
| +	} while (0) | ||||
| + | ||||
|  #define PR_EEP(_s, _val)						\ | ||||
|  	do {								\ | ||||
|  		len += scnprintf(buf + len, size - len, "%20s : %10d\n",\ | ||||
| --- a/drivers/net/wireless/ath/ath9k/wmi.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/wmi.c | ||||
| @@ -61,6 +61,8 @@ static const char *wmi_cmd_to_name(enum | ||||
|  		return "WMI_REG_READ_CMDID"; | ||||
|  	case WMI_REG_WRITE_CMDID: | ||||
|  		return "WMI_REG_WRITE_CMDID"; | ||||
| +	case WMI_REG_RMW_CMDID: | ||||
| +		return "WMI_REG_RMW_CMDID"; | ||||
|  	case WMI_RC_STATE_CHANGE_CMDID: | ||||
|  		return "WMI_RC_STATE_CHANGE_CMDID"; | ||||
|  	case WMI_RC_RATE_UPDATE_CMDID: | ||||
| @@ -101,6 +103,7 @@ struct wmi *ath9k_init_wmi(struct ath9k_ | ||||
|  	spin_lock_init(&wmi->event_lock); | ||||
|  	mutex_init(&wmi->op_mutex); | ||||
|  	mutex_init(&wmi->multi_write_mutex); | ||||
| +	mutex_init(&wmi->multi_rmw_mutex); | ||||
|  	init_completion(&wmi->cmd_wait); | ||||
|  	INIT_LIST_HEAD(&wmi->pending_tx_events); | ||||
|  	tasklet_init(&wmi->wmi_event_tasklet, ath9k_wmi_event_tasklet, | ||||
| --- a/drivers/net/wireless/ath/ath9k/wmi.h | ||||
| +++ b/drivers/net/wireless/ath/ath9k/wmi.h | ||||
| @@ -112,6 +112,7 @@ enum wmi_cmd_id { | ||||
|  	WMI_TX_STATS_CMDID, | ||||
|  	WMI_RX_STATS_CMDID, | ||||
|  	WMI_BITRATE_MASK_CMDID, | ||||
| +	WMI_REG_RMW_CMDID, | ||||
|  }; | ||||
|   | ||||
|  enum wmi_event_id { | ||||
| @@ -125,12 +126,19 @@ enum wmi_event_id { | ||||
|  }; | ||||
|   | ||||
|  #define MAX_CMD_NUMBER 62 | ||||
| +#define MAX_RMW_CMD_NUMBER 15 | ||||
|   | ||||
|  struct register_write { | ||||
|  	__be32 reg; | ||||
|  	__be32 val; | ||||
|  }; | ||||
|   | ||||
| +struct register_rmw { | ||||
| +	__be32 reg; | ||||
| +	__be32 set; | ||||
| +	__be32 clr; | ||||
| +} __packed; | ||||
| + | ||||
|  struct ath9k_htc_tx_event { | ||||
|  	int count; | ||||
|  	struct __wmi_event_txstatus txs; | ||||
| @@ -156,10 +164,18 @@ struct wmi { | ||||
|   | ||||
|  	spinlock_t wmi_lock; | ||||
|   | ||||
| +	/* multi write section */ | ||||
|  	atomic_t mwrite_cnt; | ||||
|  	struct register_write multi_write[MAX_CMD_NUMBER]; | ||||
|  	u32 multi_write_idx; | ||||
|  	struct mutex multi_write_mutex; | ||||
| + | ||||
| +	/* multi rmw section */ | ||||
| +	atomic_t m_rmw_cnt; | ||||
| +	struct register_rmw multi_rmw[MAX_RMW_CMD_NUMBER]; | ||||
| +	u32 multi_rmw_idx; | ||||
| +	struct mutex multi_rmw_mutex; | ||||
| + | ||||
|  }; | ||||
|   | ||||
|  struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv); | ||||
| @@ -1,89 +0,0 @@ | ||||
| From: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Date: Sun, 22 Mar 2015 19:29:47 +0100 | ||||
| Subject: [PATCH] ath9k: ar9271_hw_pa_cal - use defs instead of magin | ||||
|  numbers | ||||
|  | ||||
| This function uses mixed styles for register names/numbers which | ||||
| is make harder reading and optimisation. | ||||
|  | ||||
| Signed-off-by: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath9k/ar9002_calib.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/ar9002_calib.c | ||||
| @@ -430,22 +430,22 @@ static void ar9271_hw_pa_cal(struct ath_ | ||||
|  	u32 regVal; | ||||
|  	unsigned int i; | ||||
|  	u32 regList[][2] = { | ||||
| -		{ 0x786c, 0 }, | ||||
| -		{ 0x7854, 0 }, | ||||
| -		{ 0x7820, 0 }, | ||||
| -		{ 0x7824, 0 }, | ||||
| -		{ 0x7868, 0 }, | ||||
| -		{ 0x783c, 0 }, | ||||
| -		{ 0x7838, 0 } , | ||||
| -		{ 0x7828, 0 } , | ||||
| +		{ AR9285_AN_TOP3, 0 }, | ||||
| +		{ AR9285_AN_RXTXBB1, 0 }, | ||||
| +		{ AR9285_AN_RF2G1, 0 }, | ||||
| +		{ AR9285_AN_RF2G2, 0 }, | ||||
| +		{ AR9285_AN_TOP2, 0 }, | ||||
| +		{ AR9285_AN_RF2G8, 0 }, | ||||
| +		{ AR9285_AN_RF2G7, 0 } , | ||||
| +		{ AR9285_AN_RF2G3, 0 } , | ||||
|  	}; | ||||
|   | ||||
|  	for (i = 0; i < ARRAY_SIZE(regList); i++) | ||||
|  		regList[i][1] = REG_READ(ah, regList[i][0]); | ||||
|   | ||||
| -	regVal = REG_READ(ah, 0x7834); | ||||
| +	regVal = REG_READ(ah, AR9285_AN_RF2G6); | ||||
|  	regVal &= (~(0x1)); | ||||
| -	REG_WRITE(ah, 0x7834, regVal); | ||||
| +	REG_WRITE(ah, AR9285_AN_RF2G6, regVal); | ||||
|  	regVal = REG_READ(ah, 0x9808); | ||||
|  	regVal |= (0x1 << 27); | ||||
|  	REG_WRITE(ah, 0x9808, regVal); | ||||
| @@ -477,7 +477,7 @@ static void ar9271_hw_pa_cal(struct ath_ | ||||
|  	 * does not matter since we turn it off | ||||
|  	 */ | ||||
|  	REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PADRVGN2TAB0, 0); | ||||
| - | ||||
| +	/* 7828, b0-11, ccom=fff */ | ||||
|  	REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9271_AN_RF2G3_CCOMP, 0xfff); | ||||
|   | ||||
|  	/* Set: | ||||
| @@ -490,15 +490,16 @@ static void ar9271_hw_pa_cal(struct ath_ | ||||
|   | ||||
|  	/* find off_6_1; */ | ||||
|  	for (i = 6; i > 0; i--) { | ||||
| -		regVal = REG_READ(ah, 0x7834); | ||||
| +		regVal = REG_READ(ah, AR9285_AN_RF2G6); | ||||
|  		regVal |= (1 << (20 + i)); | ||||
| -		REG_WRITE(ah, 0x7834, regVal); | ||||
| +		REG_WRITE(ah, AR9285_AN_RF2G6, regVal); | ||||
|  		udelay(1); | ||||
|  		/* regVal = REG_READ(ah, 0x7834); */ | ||||
|  		regVal &= (~(0x1 << (20 + i))); | ||||
| -		regVal |= (MS(REG_READ(ah, 0x7840), AR9285_AN_RXTXBB1_SPARE9) | ||||
| +		regVal |= (MS(REG_READ(ah, AR9285_AN_RF2G9), | ||||
| +			      AR9285_AN_RXTXBB1_SPARE9) | ||||
|  			    << (20 + i)); | ||||
| -		REG_WRITE(ah, 0x7834, regVal); | ||||
| +		REG_WRITE(ah, AR9285_AN_RF2G6, regVal); | ||||
|  	} | ||||
|   | ||||
|  	regVal = (regVal >> 20) & 0x7f; | ||||
| @@ -517,9 +518,9 @@ static void ar9271_hw_pa_cal(struct ath_ | ||||
|   | ||||
|  	ENABLE_REGWRITE_BUFFER(ah); | ||||
|   | ||||
| -	regVal = REG_READ(ah, 0x7834); | ||||
| +	regVal = REG_READ(ah, AR_AN_RF2G1_CH1); | ||||
|  	regVal |= 0x1; | ||||
| -	REG_WRITE(ah, 0x7834, regVal); | ||||
| +	REG_WRITE(ah, AR_AN_RF2G1_CH1, regVal); | ||||
|  	regVal = REG_READ(ah, 0x9808); | ||||
|  	regVal &= (~(0x1 << 27)); | ||||
|  	REG_WRITE(ah, 0x9808, regVal); | ||||
| @@ -1,79 +0,0 @@ | ||||
| From: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Date: Sun, 22 Mar 2015 19:29:48 +0100 | ||||
| Subject: [PATCH] ath9k: ar9271_hw_pa_cal: use proper makroses. | ||||
|  | ||||
| Signed-off-by: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath9k/ar9002_calib.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/ar9002_calib.c | ||||
| @@ -443,33 +443,30 @@ static void ar9271_hw_pa_cal(struct ath_ | ||||
|  	for (i = 0; i < ARRAY_SIZE(regList); i++) | ||||
|  		regList[i][1] = REG_READ(ah, regList[i][0]); | ||||
|   | ||||
| -	regVal = REG_READ(ah, AR9285_AN_RF2G6); | ||||
| -	regVal &= (~(0x1)); | ||||
| -	REG_WRITE(ah, AR9285_AN_RF2G6, regVal); | ||||
| -	regVal = REG_READ(ah, 0x9808); | ||||
| -	regVal |= (0x1 << 27); | ||||
| -	REG_WRITE(ah, 0x9808, regVal); | ||||
| - | ||||
| +	/* 7834, b1=0 */ | ||||
| +	REG_CLR_BIT(ah, AR9285_AN_RF2G6, 1 << 0); | ||||
| +	/* 9808, b27=1 */ | ||||
| +	REG_SET_BIT(ah, 0x9808, 1 << 27); | ||||
|  	/* 786c,b23,1, pwddac=1 */ | ||||
| -	REG_RMW_FIELD(ah, AR9285_AN_TOP3, AR9285_AN_TOP3_PWDDAC, 1); | ||||
| +	REG_SET_BIT(ah, AR9285_AN_TOP3, AR9285_AN_TOP3_PWDDAC); | ||||
|  	/* 7854, b5,1, pdrxtxbb=1 */ | ||||
| -	REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDRXTXBB1, 1); | ||||
| +	REG_SET_BIT(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDRXTXBB1); | ||||
|  	/* 7854, b7,1, pdv2i=1 */ | ||||
| -	REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDV2I, 1); | ||||
| +	REG_SET_BIT(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDV2I); | ||||
|  	/* 7854, b8,1, pddacinterface=1 */ | ||||
| -	REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDDACIF, 1); | ||||
| +	REG_SET_BIT(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDDACIF); | ||||
|  	/* 7824,b12,0, offcal=0 */ | ||||
| -	REG_RMW_FIELD(ah, AR9285_AN_RF2G2, AR9285_AN_RF2G2_OFFCAL, 0); | ||||
| +	REG_CLR_BIT(ah, AR9285_AN_RF2G2, AR9285_AN_RF2G2_OFFCAL); | ||||
|  	/* 7838, b1,0, pwddb=0 */ | ||||
| -	REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PWDDB, 0); | ||||
| +	REG_CLR_BIT(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PWDDB); | ||||
|  	/* 7820,b11,0, enpacal=0 */ | ||||
| -	REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_ENPACAL, 0); | ||||
| +	REG_CLR_BIT(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_ENPACAL); | ||||
|  	/* 7820,b25,1, pdpadrv1=0 */ | ||||
| -	REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV1, 0); | ||||
| +	REG_CLR_BIT(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV1); | ||||
|  	/* 7820,b24,0, pdpadrv2=0 */ | ||||
| -	REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV2, 0); | ||||
| +	REG_CLR_BIT(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV2); | ||||
|  	/* 7820,b23,0, pdpaout=0 */ | ||||
| -	REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPAOUT, 0); | ||||
| +	REG_CLR_BIT(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPAOUT); | ||||
|  	/* 783c,b14-16,7, padrvgn2tab_0=7 */ | ||||
|  	REG_RMW_FIELD(ah, AR9285_AN_RF2G8, AR9285_AN_RF2G8_PADRVGN2TAB0, 7); | ||||
|  	/* | ||||
| @@ -516,15 +513,13 @@ static void ar9271_hw_pa_cal(struct ath_ | ||||
|  		ah->pacal_info.prev_offset = regVal; | ||||
|  	} | ||||
|   | ||||
| -	ENABLE_REGWRITE_BUFFER(ah); | ||||
|   | ||||
| -	regVal = REG_READ(ah, AR_AN_RF2G1_CH1); | ||||
| -	regVal |= 0x1; | ||||
| -	REG_WRITE(ah, AR_AN_RF2G1_CH1, regVal); | ||||
| -	regVal = REG_READ(ah, 0x9808); | ||||
| -	regVal &= (~(0x1 << 27)); | ||||
| -	REG_WRITE(ah, 0x9808, regVal); | ||||
| +	/* 7834, b1=1 */ | ||||
| +	REG_SET_BIT(ah, AR9285_AN_RF2G6, 1 << 0); | ||||
| +	/* 9808, b27=0 */ | ||||
| +	REG_CLR_BIT(ah, 0x9808, 1 << 27); | ||||
|   | ||||
| +	ENABLE_REGWRITE_BUFFER(ah); | ||||
|  	for (i = 0; i < ARRAY_SIZE(regList); i++) | ||||
|  		REG_WRITE(ah, regList[i][0], regList[i][1]); | ||||
|   | ||||
| @@ -1,48 +0,0 @@ | ||||
| From: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Date: Sun, 22 Mar 2015 19:29:49 +0100 | ||||
| Subject: [PATCH] ath9k: ar9271_hw_pa_cal: use RMW buffer | ||||
|  | ||||
| Signed-off-by: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath9k/ar9002_calib.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/ar9002_calib.c | ||||
| @@ -436,13 +436,14 @@ static void ar9271_hw_pa_cal(struct ath_ | ||||
|  		{ AR9285_AN_RF2G2, 0 }, | ||||
|  		{ AR9285_AN_TOP2, 0 }, | ||||
|  		{ AR9285_AN_RF2G8, 0 }, | ||||
| -		{ AR9285_AN_RF2G7, 0 } , | ||||
| -		{ AR9285_AN_RF2G3, 0 } , | ||||
| +		{ AR9285_AN_RF2G7, 0 }, | ||||
| +		{ AR9285_AN_RF2G3, 0 }, | ||||
|  	}; | ||||
|   | ||||
|  	for (i = 0; i < ARRAY_SIZE(regList); i++) | ||||
|  		regList[i][1] = REG_READ(ah, regList[i][0]); | ||||
|   | ||||
| +	ENABLE_REG_RMW_BUFFER(ah); | ||||
|  	/* 7834, b1=0 */ | ||||
|  	REG_CLR_BIT(ah, AR9285_AN_RF2G6, 1 << 0); | ||||
|  	/* 9808, b27=1 */ | ||||
| @@ -476,6 +477,7 @@ static void ar9271_hw_pa_cal(struct ath_ | ||||
|  	REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PADRVGN2TAB0, 0); | ||||
|  	/* 7828, b0-11, ccom=fff */ | ||||
|  	REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9271_AN_RF2G3_CCOMP, 0xfff); | ||||
| +	REG_RMW_BUFFER_FLUSH(ah); | ||||
|   | ||||
|  	/* Set: | ||||
|  	 * localmode=1,bmode=1,bmoderxtx=1,synthon=1, | ||||
| @@ -514,10 +516,12 @@ static void ar9271_hw_pa_cal(struct ath_ | ||||
|  	} | ||||
|   | ||||
|   | ||||
| +	ENABLE_REG_RMW_BUFFER(ah); | ||||
|  	/* 7834, b1=1 */ | ||||
|  	REG_SET_BIT(ah, AR9285_AN_RF2G6, 1 << 0); | ||||
|  	/* 9808, b27=0 */ | ||||
|  	REG_CLR_BIT(ah, 0x9808, 1 << 27); | ||||
| +	REG_RMW_BUFFER_FLUSH(ah); | ||||
|   | ||||
|  	ENABLE_REGWRITE_BUFFER(ah); | ||||
|  	for (i = 0; i < ARRAY_SIZE(regList); i++) | ||||
| @@ -1,35 +0,0 @@ | ||||
| From: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Date: Sun, 22 Mar 2015 19:29:50 +0100 | ||||
| Subject: [PATCH] ath9k: add multi_read to be compatible with ath9k_htc | ||||
|  | ||||
| Signed-off-by: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath9k/init.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/init.c | ||||
| @@ -141,6 +141,16 @@ static unsigned int ath9k_ioread32(void | ||||
|  	return val; | ||||
|  } | ||||
|   | ||||
| +static void ath9k_multi_ioread32(void *hw_priv, u32 *addr, | ||||
| +                                u32 *val, u16 count) | ||||
| +{ | ||||
| +	int i; | ||||
| + | ||||
| +	for (i = 0; i < count; i++) | ||||
| +		val[i] = ath9k_ioread32(hw_priv, addr[i]); | ||||
| +} | ||||
| + | ||||
| + | ||||
|  static unsigned int __ath9k_reg_rmw(struct ath_softc *sc, u32 reg_offset, | ||||
|  				    u32 set, u32 clr) | ||||
|  { | ||||
| @@ -530,6 +540,7 @@ static int ath9k_init_softc(u16 devid, s | ||||
|  	ah->hw = sc->hw; | ||||
|  	ah->hw_version.devid = devid; | ||||
|  	ah->reg_ops.read = ath9k_ioread32; | ||||
| +	ah->reg_ops.multi_read = ath9k_multi_ioread32; | ||||
|  	ah->reg_ops.write = ath9k_iowrite32; | ||||
|  	ah->reg_ops.rmw = ath9k_reg_rmw; | ||||
|  	pCap = &ah->caps; | ||||
| @@ -1,69 +0,0 @@ | ||||
| From: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Date: Sun, 22 Mar 2015 19:29:51 +0100 | ||||
| Subject: [PATCH] ath9k: add new function ath9k_hw_read_array | ||||
|  | ||||
| REG_READ generate most overhead on usb bus. It send and read micro packages | ||||
| and reduce usb bandwidth. To reduce this overhead we should read in batches. | ||||
|  | ||||
| Signed-off-by: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath9k/hw.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/hw.c | ||||
| @@ -121,6 +121,36 @@ void ath9k_hw_write_array(struct ath_hw | ||||
|  	REGWRITE_BUFFER_FLUSH(ah); | ||||
|  } | ||||
|   | ||||
| +void ath9k_hw_read_array(struct ath_hw *ah, u32 array[][2], int size) | ||||
| +{ | ||||
| +	u32 *tmp_reg_list, *tmp_data; | ||||
| +	int i; | ||||
| + | ||||
| +	tmp_reg_list = kmalloc(size * sizeof(u32), GFP_KERNEL); | ||||
| +	if (!tmp_reg_list) { | ||||
| +		dev_err(ah->dev, "%s: tmp_reg_list: alloc filed\n", __func__); | ||||
| +		return; | ||||
| +	} | ||||
| + | ||||
| +	tmp_data = kmalloc(size * sizeof(u32), GFP_KERNEL); | ||||
| +	if (!tmp_data) { | ||||
| +		dev_err(ah->dev, "%s tmp_data: alloc filed\n", __func__); | ||||
| +		goto error_tmp_data; | ||||
| +	} | ||||
| + | ||||
| +	for (i = 0; i < size; i++) | ||||
| +		tmp_reg_list[i] = array[i][0]; | ||||
| + | ||||
| +	REG_READ_MULTI(ah, tmp_reg_list, tmp_data, size); | ||||
| + | ||||
| +	for (i = 0; i < size; i++) | ||||
| +		array[i][1] = tmp_data[i]; | ||||
| + | ||||
| +	kfree(tmp_data); | ||||
| +error_tmp_data: | ||||
| +	kfree(tmp_reg_list); | ||||
| +} | ||||
| + | ||||
|  u32 ath9k_hw_reverse_bits(u32 val, u32 n) | ||||
|  { | ||||
|  	u32 retval; | ||||
| --- a/drivers/net/wireless/ath/ath9k/hw.h | ||||
| +++ b/drivers/net/wireless/ath/ath9k/hw.h | ||||
| @@ -138,6 +138,8 @@ | ||||
|   | ||||
|  #define REG_WRITE_ARRAY(iniarray, column, regWr) \ | ||||
|  	ath9k_hw_write_array(ah, iniarray, column, &(regWr)) | ||||
| +#define REG_READ_ARRAY(ah, array, size) \ | ||||
| +	ath9k_hw_read_array(ah, array, size) | ||||
|   | ||||
|  #define AR_GPIO_OUTPUT_MUX_AS_OUTPUT             0 | ||||
|  #define AR_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED 1 | ||||
| @@ -1020,6 +1022,7 @@ void ath9k_hw_synth_delay(struct ath_hw | ||||
|  bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout); | ||||
|  void ath9k_hw_write_array(struct ath_hw *ah, const struct ar5416IniArray *array, | ||||
|  			  int column, unsigned int *writecnt); | ||||
| +void ath9k_hw_read_array(struct ath_hw *ah, u32 array[][2], int size); | ||||
|  u32 ath9k_hw_reverse_bits(u32 val, u32 n); | ||||
|  u16 ath9k_hw_computetxtime(struct ath_hw *ah, | ||||
|  			   u8 phy, int kbps, | ||||
| @@ -1,24 +0,0 @@ | ||||
| From: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Date: Sun, 22 Mar 2015 19:29:52 +0100 | ||||
| Subject: [PATCH] ath9k: ar9271_hw_pa_cal: use REG_READ_ARRAY | ||||
|  | ||||
| insted of reading each register separatly | ||||
| and waste 4ms on each operation, we can | ||||
| use one shot read. | ||||
|  | ||||
| Signed-off-by: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath9k/ar9002_calib.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/ar9002_calib.c | ||||
| @@ -440,8 +440,7 @@ static void ar9271_hw_pa_cal(struct ath_ | ||||
|  		{ AR9285_AN_RF2G3, 0 }, | ||||
|  	}; | ||||
|   | ||||
| -	for (i = 0; i < ARRAY_SIZE(regList); i++) | ||||
| -		regList[i][1] = REG_READ(ah, regList[i][0]); | ||||
| +	REG_READ_ARRAY(ah, regList, ARRAY_SIZE(regList)); | ||||
|   | ||||
|  	ENABLE_REG_RMW_BUFFER(ah); | ||||
|  	/* 7834, b1=0 */ | ||||
| @@ -1,39 +0,0 @@ | ||||
| From: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Date: Sun, 22 Mar 2015 19:29:53 +0100 | ||||
| Subject: [PATCH] ath9k: use one shot read in ath9k_hw_update_mibstats | ||||
|  | ||||
| this will reduce some overhead on usb bus. | ||||
|  | ||||
| Signed-off-by: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath9k/ani.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/ani.c | ||||
| @@ -107,11 +107,21 @@ static const struct ani_cck_level_entry | ||||
|  static void ath9k_hw_update_mibstats(struct ath_hw *ah, | ||||
|  				     struct ath9k_mib_stats *stats) | ||||
|  { | ||||
| -	stats->ackrcv_bad += REG_READ(ah, AR_ACK_FAIL); | ||||
| -	stats->rts_bad += REG_READ(ah, AR_RTS_FAIL); | ||||
| -	stats->fcs_bad += REG_READ(ah, AR_FCS_FAIL); | ||||
| -	stats->rts_good += REG_READ(ah, AR_RTS_OK); | ||||
| -	stats->beacons += REG_READ(ah, AR_BEACON_CNT); | ||||
| +	u32 addr[5] = {AR_RTS_OK, AR_RTS_FAIL, AR_ACK_FAIL, | ||||
| +		       AR_FCS_FAIL, AR_BEACON_CNT}; | ||||
| +	u32 data[5]; | ||||
| + | ||||
| +	REG_READ_MULTI(ah, &addr[0], &data[0], 5); | ||||
| +	/* AR_RTS_OK */ | ||||
| +	stats->rts_good += data[0]; | ||||
| +	/* AR_RTS_FAIL */ | ||||
| +	stats->rts_bad += data[1]; | ||||
| +	/* AR_ACK_FAIL */ | ||||
| +	stats->ackrcv_bad += data[2]; | ||||
| +	/* AR_FCS_FAIL */ | ||||
| +	stats->fcs_bad += data[3]; | ||||
| +	/* AR_BEACON_CNT */ | ||||
| +	stats->beacons += data[4]; | ||||
|  } | ||||
|   | ||||
|  static void ath9k_ani_restart(struct ath_hw *ah) | ||||
| @@ -1,71 +0,0 @@ | ||||
| From: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Date: Sun, 22 Mar 2015 19:29:54 +0100 | ||||
| Subject: [PATCH] ath9k: ath9k_hw_loadnf: use REG_RMW | ||||
|  | ||||
| Signed-off-by: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath9k/calib.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/calib.c | ||||
| @@ -238,7 +238,6 @@ int ath9k_hw_loadnf(struct ath_hw *ah, s | ||||
|  { | ||||
|  	struct ath9k_nfcal_hist *h = NULL; | ||||
|  	unsigned i, j; | ||||
| -	int32_t val; | ||||
|  	u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask; | ||||
|  	struct ath_common *common = ath9k_hw_common(ah); | ||||
|  	s16 default_nf = ath9k_hw_get_default_nf(ah, chan); | ||||
| @@ -246,6 +245,7 @@ int ath9k_hw_loadnf(struct ath_hw *ah, s | ||||
|  	if (ah->caldata) | ||||
|  		h = ah->caldata->nfCalHist; | ||||
|   | ||||
| +	ENABLE_REG_RMW_BUFFER(ah); | ||||
|  	for (i = 0; i < NUM_NF_READINGS; i++) { | ||||
|  		if (chainmask & (1 << i)) { | ||||
|  			s16 nfval; | ||||
| @@ -258,10 +258,8 @@ int ath9k_hw_loadnf(struct ath_hw *ah, s | ||||
|  			else | ||||
|  				nfval = default_nf; | ||||
|   | ||||
| -			val = REG_READ(ah, ah->nf_regs[i]); | ||||
| -			val &= 0xFFFFFE00; | ||||
| -			val |= (((u32) nfval << 1) & 0x1ff); | ||||
| -			REG_WRITE(ah, ah->nf_regs[i], val); | ||||
| +			REG_RMW(ah, ah->nf_regs[i], | ||||
| +				(((u32) nfval << 1) & 0x1ff), 0x1ff); | ||||
|  		} | ||||
|  	} | ||||
|   | ||||
| @@ -274,6 +272,7 @@ int ath9k_hw_loadnf(struct ath_hw *ah, s | ||||
|  	REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, | ||||
|  		    AR_PHY_AGC_CONTROL_NO_UPDATE_NF); | ||||
|  	REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); | ||||
| +	REG_RMW_BUFFER_FLUSH(ah); | ||||
|   | ||||
|  	/* | ||||
|  	 * Wait for load to complete, should be fast, a few 10s of us. | ||||
| @@ -309,19 +308,17 @@ int ath9k_hw_loadnf(struct ath_hw *ah, s | ||||
|  	 * by the median we just loaded.  This will be initial (and max) value | ||||
|  	 * of next noise floor calibration the baseband does. | ||||
|  	 */ | ||||
| -	ENABLE_REGWRITE_BUFFER(ah); | ||||
| +	ENABLE_REG_RMW_BUFFER(ah); | ||||
|  	for (i = 0; i < NUM_NF_READINGS; i++) { | ||||
|  		if (chainmask & (1 << i)) { | ||||
|  			if ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(chan)) | ||||
|  				continue; | ||||
|   | ||||
| -			val = REG_READ(ah, ah->nf_regs[i]); | ||||
| -			val &= 0xFFFFFE00; | ||||
| -			val |= (((u32) (-50) << 1) & 0x1ff); | ||||
| -			REG_WRITE(ah, ah->nf_regs[i], val); | ||||
| +			REG_RMW(ah, ah->nf_regs[i], | ||||
| +					(((u32) (-50) << 1) & 0x1ff), 0x1ff); | ||||
|  		} | ||||
|  	} | ||||
| -	REGWRITE_BUFFER_FLUSH(ah); | ||||
| +	REG_RMW_BUFFER_FLUSH(ah); | ||||
|   | ||||
|  	return 0; | ||||
|  } | ||||
| @@ -1,27 +0,0 @@ | ||||
| From: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Date: Sun, 22 Mar 2015 19:29:55 +0100 | ||||
| Subject: [PATCH] ath9k: write buffer related optimisation in | ||||
|  ar5008_hw_set_channel_regs | ||||
|  | ||||
| Signed-off-by: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c | ||||
| @@ -681,12 +681,13 @@ static void ar5008_hw_set_channel_regs(s | ||||
|  			phymode |= AR_PHY_FC_DYN2040_PRI_CH; | ||||
|   | ||||
|  	} | ||||
| +	ENABLE_REGWRITE_BUFFER(ah); | ||||
|  	REG_WRITE(ah, AR_PHY_TURBO, phymode); | ||||
|   | ||||
| +	/* This function do only REG_WRITE, so | ||||
| +	 * we can include it to REGWRITE_BUFFER. */ | ||||
|  	ath9k_hw_set11nmac2040(ah, chan); | ||||
|   | ||||
| -	ENABLE_REGWRITE_BUFFER(ah); | ||||
| - | ||||
|  	REG_WRITE(ah, AR_GTXTO, 25 << AR_GTXTO_TIMEOUT_LIMIT_S); | ||||
|  	REG_WRITE(ah, AR_CST, 0xF << AR_CST_TIMEOUT_LIMIT_S); | ||||
|   | ||||
| @@ -1,26 +0,0 @@ | ||||
| From: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Date: Sun, 22 Mar 2015 19:29:56 +0100 | ||||
| Subject: [PATCH] ath9k: ath9k_hw_set_4k_power_cal_tabl: use rmw buffer | ||||
|  | ||||
| Signed-off-by: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c | ||||
| @@ -389,6 +389,7 @@ static void ath9k_hw_set_4k_power_cal_ta | ||||
|  		} | ||||
|  	} | ||||
|   | ||||
| +	ENABLE_REG_RMW_BUFFER(ah); | ||||
|  	REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_NUM_PD_GAIN, | ||||
|  		      (numXpdGain - 1) & 0x3); | ||||
|  	REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_1, | ||||
| @@ -396,6 +397,7 @@ static void ath9k_hw_set_4k_power_cal_ta | ||||
|  	REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_2, | ||||
|  		      xpdGainValues[1]); | ||||
|  	REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_3, 0); | ||||
| +	REG_RMW_BUFFER_FLUSH(ah); | ||||
|   | ||||
|  	for (i = 0; i < AR5416_EEP4K_MAX_CHAINS; i++) { | ||||
|  		regChainOffset = i * 0x1000; | ||||
| @@ -1,43 +0,0 @@ | ||||
| From: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Date: Sun, 22 Mar 2015 19:29:57 +0100 | ||||
| Subject: [PATCH] ath9k: use rmw buffer in ath9k_hw_set_operating_mode | ||||
|  and ath9k_hw_reset | ||||
|  | ||||
| Signed-off-by: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath9k/hw.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/hw.c | ||||
| @@ -1227,6 +1227,7 @@ static void ath9k_hw_set_operating_mode( | ||||
|  	u32 mask = AR_STA_ID1_STA_AP | AR_STA_ID1_ADHOC; | ||||
|  	u32 set = AR_STA_ID1_KSRCH_MODE; | ||||
|   | ||||
| +	ENABLE_REG_RMW_BUFFER(ah); | ||||
|  	switch (opmode) { | ||||
|  	case NL80211_IFTYPE_ADHOC: | ||||
|  		if (!AR_SREV_9340_13(ah)) { | ||||
| @@ -1248,6 +1249,7 @@ static void ath9k_hw_set_operating_mode( | ||||
|  		break; | ||||
|  	} | ||||
|  	REG_RMW(ah, AR_STA_ID1, set, mask); | ||||
| +	REG_RMW_BUFFER_FLUSH(ah); | ||||
|  } | ||||
|   | ||||
|  void ath9k_hw_get_delta_slope_vals(struct ath_hw *ah, u32 coef_scaled, | ||||
| @@ -1960,6 +1962,7 @@ int ath9k_hw_reset(struct ath_hw *ah, st | ||||
|  	if (!ath9k_hw_mci_is_enabled(ah)) | ||||
|  		REG_WRITE(ah, AR_OBS, 8); | ||||
|   | ||||
| +	ENABLE_REG_RMW_BUFFER(ah); | ||||
|  	if (ah->config.rx_intr_mitigation) { | ||||
|  		REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_LAST, ah->config.rimt_last); | ||||
|  		REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_FIRST, ah->config.rimt_first); | ||||
| @@ -1969,6 +1972,7 @@ int ath9k_hw_reset(struct ath_hw *ah, st | ||||
|  		REG_RMW_FIELD(ah, AR_TIMT, AR_TIMT_LAST, 300); | ||||
|  		REG_RMW_FIELD(ah, AR_TIMT, AR_TIMT_FIRST, 750); | ||||
|  	} | ||||
| +	REG_RMW_BUFFER_FLUSH(ah); | ||||
|   | ||||
|  	ath9k_hw_init_bb(ah, chan); | ||||
|   | ||||
| @@ -1,26 +0,0 @@ | ||||
| From: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Date: Sun, 22 Mar 2015 19:29:58 +0100 | ||||
| Subject: [PATCH] ath9k: ath9k_hw_4k_set_board_values: use rmw buffer | ||||
|  | ||||
| Signed-off-by: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c | ||||
| @@ -1082,6 +1082,7 @@ static void ath9k_hw_4k_set_board_values | ||||
|  		mask = BIT(0)|BIT(5)|BIT(10)|BIT(15)|BIT(20)|BIT(25); | ||||
|  		pwrctrl = mask * bb_desired_scale; | ||||
|  		clr = mask * 0x1f; | ||||
| +		ENABLE_REG_RMW_BUFFER(ah); | ||||
|  		REG_RMW(ah, AR_PHY_TX_PWRCTRL8, pwrctrl, clr); | ||||
|  		REG_RMW(ah, AR_PHY_TX_PWRCTRL10, pwrctrl, clr); | ||||
|  		REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL12, pwrctrl, clr); | ||||
| @@ -1096,6 +1097,7 @@ static void ath9k_hw_4k_set_board_values | ||||
|  		clr = mask * 0x1f; | ||||
|  		REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL11, pwrctrl, clr); | ||||
|  		REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL13, pwrctrl, clr); | ||||
| +		REG_RMW_BUFFER_FLUSH(ah); | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| @@ -1,27 +0,0 @@ | ||||
| From: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Date: Sun, 22 Mar 2015 19:29:59 +0100 | ||||
| Subject: [PATCH] ath9k: ath9k_hw_analog_shift_rmw: use REG_RMW | ||||
|  | ||||
| use REG_RMW in ath9k_hw_analog_shift_rmw. | ||||
| It will double execution speed on usb bus. | ||||
|  | ||||
| Signed-off-by: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath9k/eeprom.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/eeprom.c | ||||
| @@ -27,12 +27,7 @@ void ath9k_hw_analog_shift_regwrite(stru | ||||
|  void ath9k_hw_analog_shift_rmw(struct ath_hw *ah, u32 reg, u32 mask, | ||||
|  			       u32 shift, u32 val) | ||||
|  { | ||||
| -	u32 regVal; | ||||
| - | ||||
| -	regVal = REG_READ(ah, reg) & ~mask; | ||||
| -	regVal |= (val << shift) & mask; | ||||
| - | ||||
| -	REG_WRITE(ah, reg, regVal); | ||||
| +	REG_RMW(ah, reg, ((val << shift) & mask), mask); | ||||
|   | ||||
|  	if (ah->config.analog_shiftreg) | ||||
|  		udelay(100); | ||||
| @@ -1,47 +0,0 @@ | ||||
| From: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Date: Sun, 22 Mar 2015 19:30:01 +0100 | ||||
| Subject: [PATCH] ath9k: use REG_RMW and rmw buffer in | ||||
|  ath9k_hw_4k_set_gain | ||||
|  | ||||
| it is possible to reduce time needed for this function | ||||
| by rplacing REG_WRITE with REG_RMW (plus dummy 0) and putt all commands | ||||
| in same buffer. | ||||
|  | ||||
| Signed-off-by: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c | ||||
| @@ -772,15 +772,14 @@ static void ath9k_hw_4k_set_gain(struct | ||||
|  				 struct ar5416_eeprom_4k *eep, | ||||
|  				 u8 txRxAttenLocal) | ||||
|  { | ||||
| -	REG_WRITE(ah, AR_PHY_SWITCH_CHAIN_0, | ||||
| -		  pModal->antCtrlChain[0]); | ||||
| - | ||||
| -	REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0), | ||||
| -		  (REG_READ(ah, AR_PHY_TIMING_CTRL4(0)) & | ||||
| -		   ~(AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF | | ||||
| -		     AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF)) | | ||||
| -		  SM(pModal->iqCalICh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) | | ||||
| -		  SM(pModal->iqCalQCh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF)); | ||||
| +	ENABLE_REG_RMW_BUFFER(ah); | ||||
| +	REG_RMW(ah, AR_PHY_SWITCH_CHAIN_0, | ||||
| +		pModal->antCtrlChain[0], 0); | ||||
| + | ||||
| +	REG_RMW(ah, AR_PHY_TIMING_CTRL4(0), | ||||
| +		SM(pModal->iqCalICh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) | | ||||
| +		SM(pModal->iqCalQCh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF), | ||||
| +		AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF | AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF); | ||||
|   | ||||
|  	if ((eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >= | ||||
|  	    AR5416_EEP_MINOR_VER_3) { | ||||
| @@ -819,6 +818,7 @@ static void ath9k_hw_4k_set_gain(struct | ||||
|  		      AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAttenLocal); | ||||
|  	REG_RMW_FIELD(ah, AR_PHY_RXGAIN + 0x1000, | ||||
|  		      AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[0]); | ||||
| +	REG_RMW_BUFFER_FLUSH(ah); | ||||
|  } | ||||
|   | ||||
|  /* | ||||
| @@ -1,67 +0,0 @@ | ||||
| From: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Date: Sun, 22 Mar 2015 19:30:03 +0100 | ||||
| Subject: [PATCH] ath9k: use REG_RMW and rmw buffer in | ||||
|  ath9k_hw_def_set_gain | ||||
|  | ||||
| Signed-off-by: Oleksij Rempel <linux@rempel-privat.de> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/ath/ath9k/eeprom_def.c | ||||
| +++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c | ||||
| @@ -466,6 +466,7 @@ static void ath9k_hw_def_set_gain(struct | ||||
|  				  struct ar5416_eeprom_def *eep, | ||||
|  				  u8 txRxAttenLocal, int regChainOffset, int i) | ||||
|  { | ||||
| +	ENABLE_REG_RMW_BUFFER(ah); | ||||
|  	if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_3) { | ||||
|  		txRxAttenLocal = pModal->txRxAttenCh[i]; | ||||
|   | ||||
| @@ -483,16 +484,12 @@ static void ath9k_hw_def_set_gain(struct | ||||
|  			      AR_PHY_GAIN_2GHZ_XATTEN2_DB, | ||||
|  			      pModal->xatten2Db[i]); | ||||
|  		} else { | ||||
| -			REG_WRITE(ah, AR_PHY_GAIN_2GHZ + regChainOffset, | ||||
| -			  (REG_READ(ah, AR_PHY_GAIN_2GHZ + regChainOffset) & | ||||
| -			   ~AR_PHY_GAIN_2GHZ_BSW_MARGIN) | ||||
| -			  | SM(pModal-> bswMargin[i], | ||||
| -			       AR_PHY_GAIN_2GHZ_BSW_MARGIN)); | ||||
| -			REG_WRITE(ah, AR_PHY_GAIN_2GHZ + regChainOffset, | ||||
| -			  (REG_READ(ah, AR_PHY_GAIN_2GHZ + regChainOffset) & | ||||
| -			   ~AR_PHY_GAIN_2GHZ_BSW_ATTEN) | ||||
| -			  | SM(pModal->bswAtten[i], | ||||
| -			       AR_PHY_GAIN_2GHZ_BSW_ATTEN)); | ||||
| +			REG_RMW(ah, AR_PHY_GAIN_2GHZ + regChainOffset, | ||||
| +				SM(pModal-> bswMargin[i], AR_PHY_GAIN_2GHZ_BSW_MARGIN), | ||||
| +				AR_PHY_GAIN_2GHZ_BSW_MARGIN); | ||||
| +			REG_RMW(ah, AR_PHY_GAIN_2GHZ + regChainOffset, | ||||
| +				SM(pModal->bswAtten[i], AR_PHY_GAIN_2GHZ_BSW_ATTEN), | ||||
| +				AR_PHY_GAIN_2GHZ_BSW_ATTEN); | ||||
|  		} | ||||
|  	} | ||||
|   | ||||
| @@ -504,17 +501,14 @@ static void ath9k_hw_def_set_gain(struct | ||||
|  		      AR_PHY_RXGAIN + regChainOffset, | ||||
|  		      AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[i]); | ||||
|  	} else { | ||||
| -		REG_WRITE(ah, | ||||
| -			  AR_PHY_RXGAIN + regChainOffset, | ||||
| -			  (REG_READ(ah, AR_PHY_RXGAIN + regChainOffset) & | ||||
| -			   ~AR_PHY_RXGAIN_TXRX_ATTEN) | ||||
| -			  | SM(txRxAttenLocal, AR_PHY_RXGAIN_TXRX_ATTEN)); | ||||
| -		REG_WRITE(ah, | ||||
| -			  AR_PHY_GAIN_2GHZ + regChainOffset, | ||||
| -			  (REG_READ(ah, AR_PHY_GAIN_2GHZ + regChainOffset) & | ||||
| -			   ~AR_PHY_GAIN_2GHZ_RXTX_MARGIN) | | ||||
| -			  SM(pModal->rxTxMarginCh[i], AR_PHY_GAIN_2GHZ_RXTX_MARGIN)); | ||||
| +		REG_RMW(ah, AR_PHY_RXGAIN + regChainOffset, | ||||
| +			SM(txRxAttenLocal, AR_PHY_RXGAIN_TXRX_ATTEN), | ||||
| +			AR_PHY_RXGAIN_TXRX_ATTEN); | ||||
| +		REG_RMW(ah, AR_PHY_GAIN_2GHZ + regChainOffset, | ||||
| +			SM(pModal->rxTxMarginCh[i], AR_PHY_GAIN_2GHZ_RXTX_MARGIN), | ||||
| +			AR_PHY_GAIN_2GHZ_RXTX_MARGIN); | ||||
|  	} | ||||
| +	REG_RMW_BUFFER_FLUSH(ah); | ||||
|  } | ||||
|   | ||||
|  static void ath9k_hw_def_set_board_values(struct ath_hw *ah, | ||||
| @@ -1,44 +0,0 @@ | ||||
| From: Hante Meuleman <meuleman@broadcom.com> | ||||
| Date: Fri, 6 Mar 2015 18:40:38 +0100 | ||||
| Subject: [PATCH] brcmfmac: Fix oops when SDIO device is removed. | ||||
|  | ||||
| On removal of SDIO card both functions of card will be getting | ||||
| a remove call. When the first is hanging in ctrl frame xmit then | ||||
| the second will cause oops. This patch fixes the xmit ctrl | ||||
| handling in case of serious errors and also limits the handling | ||||
| for remove to function 1 only. | ||||
|  | ||||
| Reviewed-by: Arend Van Spriel <arend@broadcom.com> | ||||
| Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com> | ||||
| Signed-off-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | ||||
| @@ -1194,7 +1194,7 @@ static void brcmf_ops_sdio_remove(struct | ||||
|  	brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device); | ||||
|  	brcmf_dbg(SDIO, "Function: %d\n", func->num); | ||||
|   | ||||
| -	if (func->num != 1 && func->num != 2) | ||||
| +	if (func->num != 1) | ||||
|  		return; | ||||
|   | ||||
|  	bus_if = dev_get_drvdata(&func->dev); | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c | ||||
| @@ -2740,6 +2740,11 @@ static void brcmf_sdio_dpc(struct brcmf_ | ||||
|  	if ((bus->sdiodev->state != BRCMF_SDIOD_DATA) || (err != 0)) { | ||||
|  		brcmf_err("failed backplane access over SDIO, halting operation\n"); | ||||
|  		atomic_set(&bus->intstatus, 0); | ||||
| +		if (bus->ctrl_frame_stat) { | ||||
| +			bus->ctrl_frame_err = -ENODEV; | ||||
| +			bus->ctrl_frame_stat = false; | ||||
| +			brcmf_sdio_wait_event_wakeup(bus); | ||||
| +		} | ||||
|  	} else if (atomic_read(&bus->intstatus) || | ||||
|  		   atomic_read(&bus->ipend) > 0 || | ||||
|  		   (!atomic_read(&bus->fcstate) && | ||||
| @@ -1,157 +0,0 @@ | ||||
| From: Hante Meuleman <meuleman@broadcom.com> | ||||
| Date: Fri, 6 Mar 2015 18:40:39 +0100 | ||||
| Subject: [PATCH] brcmfmac: Simplify watchdog sleep. | ||||
|  | ||||
| The watchdog thread is used to put the SDIO bus to sleep when the | ||||
| system is idling. This patch simplifies the way it is determined | ||||
| when sleep can be entered. | ||||
|  | ||||
| Reviewed-by: Arend Van Spriel <arend@broadcom.com> | ||||
| Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com> | ||||
| Signed-off-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c | ||||
| @@ -485,10 +485,9 @@ struct brcmf_sdio { | ||||
|  #endif				/* DEBUG */ | ||||
|   | ||||
|  	uint clkstate;		/* State of sd and backplane clock(s) */ | ||||
| -	bool activity;		/* Activity flag for clock down */ | ||||
|  	s32 idletime;		/* Control for activity timeout */ | ||||
| -	s32 idlecount;	/* Activity timeout counter */ | ||||
| -	s32 idleclock;	/* How to set bus driver when idle */ | ||||
| +	s32 idlecount;		/* Activity timeout counter */ | ||||
| +	s32 idleclock;		/* How to set bus driver when idle */ | ||||
|  	bool rxflow_mode;	/* Rx flow control mode */ | ||||
|  	bool rxflow;		/* Is rx flow control on */ | ||||
|  	bool alp_only;		/* Don't use HT clock (ALP only) */ | ||||
| @@ -511,6 +510,7 @@ struct brcmf_sdio { | ||||
|  	struct workqueue_struct *brcmf_wq; | ||||
|  	struct work_struct datawork; | ||||
|  	atomic_t dpc_tskcnt; | ||||
| +	atomic_t dpc_running; | ||||
|   | ||||
|  	bool txoff;		/* Transmit flow-controlled */ | ||||
|  	struct brcmf_sdio_count sdcnt; | ||||
| @@ -959,13 +959,8 @@ static int brcmf_sdio_clkctl(struct brcm | ||||
|  	brcmf_dbg(SDIO, "Enter\n"); | ||||
|   | ||||
|  	/* Early exit if we're already there */ | ||||
| -	if (bus->clkstate == target) { | ||||
| -		if (target == CLK_AVAIL) { | ||||
| -			brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); | ||||
| -			bus->activity = true; | ||||
| -		} | ||||
| +	if (bus->clkstate == target) | ||||
|  		return 0; | ||||
| -	} | ||||
|   | ||||
|  	switch (target) { | ||||
|  	case CLK_AVAIL: | ||||
| @@ -975,7 +970,6 @@ static int brcmf_sdio_clkctl(struct brcm | ||||
|  		/* Now request HT Avail on the backplane */ | ||||
|  		brcmf_sdio_htclk(bus, true, pendok); | ||||
|  		brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); | ||||
| -		bus->activity = true; | ||||
|  		break; | ||||
|   | ||||
|  	case CLK_SDONLY: | ||||
| @@ -1024,17 +1018,6 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio * | ||||
|   | ||||
|  		/* Going to sleep */ | ||||
|  		if (sleep) { | ||||
| -			/* Don't sleep if something is pending */ | ||||
| -			if (atomic_read(&bus->intstatus) || | ||||
| -			    atomic_read(&bus->ipend) > 0 || | ||||
| -			    bus->ctrl_frame_stat || | ||||
| -			    (!atomic_read(&bus->fcstate) && | ||||
| -			    brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && | ||||
| -			    data_ok(bus))) { | ||||
| -				 err = -EBUSY; | ||||
| -				 goto done; | ||||
| -			} | ||||
| - | ||||
|  			clkcsr = brcmf_sdiod_regrb(bus->sdiodev, | ||||
|  						   SBSDIO_FUNC1_CHIPCLKCSR, | ||||
|  						   &err); | ||||
| @@ -1045,11 +1028,7 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio * | ||||
|  						  SBSDIO_ALP_AVAIL_REQ, &err); | ||||
|  			} | ||||
|  			err = brcmf_sdio_kso_control(bus, false); | ||||
| -			/* disable watchdog */ | ||||
| -			if (!err) | ||||
| -				brcmf_sdio_wd_timer(bus, 0); | ||||
|  		} else { | ||||
| -			bus->idlecount = 0; | ||||
|  			err = brcmf_sdio_kso_control(bus, true); | ||||
|  		} | ||||
|  		if (err) { | ||||
| @@ -3566,7 +3545,7 @@ void brcmf_sdio_isr(struct brcmf_sdio *b | ||||
|  	queue_work(bus->brcmf_wq, &bus->datawork); | ||||
|  } | ||||
|   | ||||
| -static bool brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) | ||||
| +static void brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) | ||||
|  { | ||||
|  	brcmf_dbg(TIMER, "Enter\n"); | ||||
|   | ||||
| @@ -3627,22 +3606,21 @@ static bool brcmf_sdio_bus_watchdog(stru | ||||
|  #endif				/* DEBUG */ | ||||
|   | ||||
|  	/* On idle timeout clear activity flag and/or turn off clock */ | ||||
| -	if ((bus->idletime > 0) && (bus->clkstate == CLK_AVAIL)) { | ||||
| -		if (++bus->idlecount >= bus->idletime) { | ||||
| +	if ((atomic_read(&bus->dpc_tskcnt) == 0) && | ||||
| +	    (atomic_read(&bus->dpc_running) == 0) && | ||||
| +	    (bus->idletime > 0) && (bus->clkstate == CLK_AVAIL)) { | ||||
| +		bus->idlecount++; | ||||
| +		if (bus->idlecount > bus->idletime) { | ||||
| +			brcmf_dbg(SDIO, "idle\n"); | ||||
| +			sdio_claim_host(bus->sdiodev->func[1]); | ||||
| +			brcmf_sdio_wd_timer(bus, 0); | ||||
|  			bus->idlecount = 0; | ||||
| -			if (bus->activity) { | ||||
| -				bus->activity = false; | ||||
| -				brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); | ||||
| -			} else { | ||||
| -				brcmf_dbg(SDIO, "idle\n"); | ||||
| -				sdio_claim_host(bus->sdiodev->func[1]); | ||||
| -				brcmf_sdio_bus_sleep(bus, true, false); | ||||
| -				sdio_release_host(bus->sdiodev->func[1]); | ||||
| -			} | ||||
| +			brcmf_sdio_bus_sleep(bus, true, false); | ||||
| +			sdio_release_host(bus->sdiodev->func[1]); | ||||
|  		} | ||||
| +	} else { | ||||
| +		bus->idlecount = 0; | ||||
|  	} | ||||
| - | ||||
| -	return (atomic_read(&bus->ipend) > 0); | ||||
|  } | ||||
|   | ||||
|  static void brcmf_sdio_dataworker(struct work_struct *work) | ||||
| @@ -3651,8 +3629,11 @@ static void brcmf_sdio_dataworker(struct | ||||
|  					      datawork); | ||||
|   | ||||
|  	while (atomic_read(&bus->dpc_tskcnt)) { | ||||
| +		atomic_set(&bus->dpc_running, 1); | ||||
|  		atomic_set(&bus->dpc_tskcnt, 0); | ||||
|  		brcmf_sdio_dpc(bus); | ||||
| +		bus->idlecount = 0; | ||||
| +		atomic_set(&bus->dpc_running, 0); | ||||
|  	} | ||||
|  	if (brcmf_sdiod_freezing(bus->sdiodev)) { | ||||
|  		brcmf_sdiod_change_state(bus->sdiodev, BRCMF_SDIOD_DOWN); | ||||
| @@ -4154,6 +4135,7 @@ struct brcmf_sdio *brcmf_sdio_probe(stru | ||||
|  	} | ||||
|  	/* Initialize DPC thread */ | ||||
|  	atomic_set(&bus->dpc_tskcnt, 0); | ||||
| +	atomic_set(&bus->dpc_running, 0); | ||||
|   | ||||
|  	/* Assign bus interface call back */ | ||||
|  	bus->sdiodev->bus_if->dev = bus->sdiodev->dev; | ||||
| @@ -1,83 +0,0 @@ | ||||
| From: Hante Meuleman <meuleman@broadcom.com> | ||||
| Date: Fri, 6 Mar 2015 18:40:40 +0100 | ||||
| Subject: [PATCH] brcmfmac: Fix possible race-condition. | ||||
|  | ||||
| SDIO is using a "shared" variable to handoff ctl frames to DPC | ||||
| and to see when they are done. In a timeout situation this can | ||||
| lead to erroneous situation where DPC started to handle the ctl | ||||
| frame while the timeout expired. This patch will fix this by | ||||
| adding locking around the shared variable. | ||||
|  | ||||
| Reviewed-by: Arend Van Spriel <arend@broadcom.com> | ||||
| Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com> | ||||
| Signed-off-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c | ||||
| @@ -2700,11 +2700,13 @@ static void brcmf_sdio_dpc(struct brcmf_ | ||||
|  	if (bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL) && | ||||
|  	    data_ok(bus)) { | ||||
|  		sdio_claim_host(bus->sdiodev->func[1]); | ||||
| -		err = brcmf_sdio_tx_ctrlframe(bus,  bus->ctrl_frame_buf, | ||||
| -					      bus->ctrl_frame_len); | ||||
| +		if (bus->ctrl_frame_stat) { | ||||
| +			err = brcmf_sdio_tx_ctrlframe(bus,  bus->ctrl_frame_buf, | ||||
| +						      bus->ctrl_frame_len); | ||||
| +			bus->ctrl_frame_err = err; | ||||
| +			bus->ctrl_frame_stat = false; | ||||
| +		} | ||||
|  		sdio_release_host(bus->sdiodev->func[1]); | ||||
| -		bus->ctrl_frame_err = err; | ||||
| -		bus->ctrl_frame_stat = false; | ||||
|  		brcmf_sdio_wait_event_wakeup(bus); | ||||
|  	} | ||||
|  	/* Send queued frames (limit 1 if rx may still be pending) */ | ||||
| @@ -2720,9 +2722,13 @@ static void brcmf_sdio_dpc(struct brcmf_ | ||||
|  		brcmf_err("failed backplane access over SDIO, halting operation\n"); | ||||
|  		atomic_set(&bus->intstatus, 0); | ||||
|  		if (bus->ctrl_frame_stat) { | ||||
| -			bus->ctrl_frame_err = -ENODEV; | ||||
| -			bus->ctrl_frame_stat = false; | ||||
| -			brcmf_sdio_wait_event_wakeup(bus); | ||||
| +			sdio_claim_host(bus->sdiodev->func[1]); | ||||
| +			if (bus->ctrl_frame_stat) { | ||||
| +				bus->ctrl_frame_err = -ENODEV; | ||||
| +				bus->ctrl_frame_stat = false; | ||||
| +				brcmf_sdio_wait_event_wakeup(bus); | ||||
| +			} | ||||
| +			sdio_release_host(bus->sdiodev->func[1]); | ||||
|  		} | ||||
|  	} else if (atomic_read(&bus->intstatus) || | ||||
|  		   atomic_read(&bus->ipend) > 0 || | ||||
| @@ -2930,15 +2936,20 @@ brcmf_sdio_bus_txctl(struct device *dev, | ||||
|  	brcmf_sdio_trigger_dpc(bus); | ||||
|  	wait_event_interruptible_timeout(bus->ctrl_wait, !bus->ctrl_frame_stat, | ||||
|  					 msecs_to_jiffies(CTL_DONE_TIMEOUT)); | ||||
| - | ||||
| -	if (!bus->ctrl_frame_stat) { | ||||
| +	ret = 0; | ||||
| +	if (bus->ctrl_frame_stat) { | ||||
| +		sdio_claim_host(bus->sdiodev->func[1]); | ||||
| +		if (bus->ctrl_frame_stat) { | ||||
| +			brcmf_dbg(SDIO, "ctrl_frame timeout\n"); | ||||
| +			bus->ctrl_frame_stat = false; | ||||
| +			ret = -ETIMEDOUT; | ||||
| +		} | ||||
| +		sdio_release_host(bus->sdiodev->func[1]); | ||||
| +	} | ||||
| +	if (!ret) { | ||||
|  		brcmf_dbg(SDIO, "ctrl_frame complete, err=%d\n", | ||||
|  			  bus->ctrl_frame_err); | ||||
|  		ret = bus->ctrl_frame_err; | ||||
| -	} else { | ||||
| -		brcmf_dbg(SDIO, "ctrl_frame timeout\n"); | ||||
| -		bus->ctrl_frame_stat = false; | ||||
| -		ret = -ETIMEDOUT; | ||||
|  	} | ||||
|   | ||||
|  	if (ret) | ||||
| @@ -1,86 +0,0 @@ | ||||
| From: Syed Asifful Dayyan <syedd@broadcom.com> | ||||
| Date: Fri, 6 Mar 2015 18:40:42 +0100 | ||||
| Subject: [PATCH] brcmfmac: Add support for BCM4345 SDIO chipset. | ||||
|  | ||||
| These changes add support for BCM4345 SDIO chipset. | ||||
|  | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Reviewed-by: Arend Van Spriel <arend@broadcom.com> | ||||
| Reviewed-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com> | ||||
| Signed-off-by: Syed Asifful Dayyan <syedd@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | ||||
| @@ -1096,6 +1096,7 @@ static const struct sdio_device_id brcmf | ||||
|  	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43341), | ||||
|  	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43362), | ||||
|  	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4335_4339), | ||||
| +	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4345), | ||||
|  	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354), | ||||
|  	{ /* end: all zeroes */ } | ||||
|  }; | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c | ||||
| @@ -491,6 +491,10 @@ static void brcmf_chip_get_raminfo(struc | ||||
|  	case BRCM_CC_43362_CHIP_ID: | ||||
|  		ci->pub.ramsize = 0x3c000; | ||||
|  		break; | ||||
| +	case BRCM_CC_4345_CHIP_ID: | ||||
| +		ci->pub.ramsize = 0xc8000; | ||||
| +		ci->pub.rambase = 0x198000; | ||||
| +		break; | ||||
|  	case BRCM_CC_4339_CHIP_ID: | ||||
|  	case BRCM_CC_4354_CHIP_ID: | ||||
|  	case BRCM_CC_4356_CHIP_ID: | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c | ||||
| @@ -617,6 +617,8 @@ static const struct sdiod_drive_str sdio | ||||
|  #define BCM43362_NVRAM_NAME		"brcm/brcmfmac43362-sdio.txt" | ||||
|  #define BCM4339_FIRMWARE_NAME		"brcm/brcmfmac4339-sdio.bin" | ||||
|  #define BCM4339_NVRAM_NAME		"brcm/brcmfmac4339-sdio.txt" | ||||
| +#define BCM4345_FIRMWARE_NAME		"brcm/brcmfmac4345-sdio.bin" | ||||
| +#define BCM4345_NVRAM_NAME		"brcm/brcmfmac4345-sdio.txt" | ||||
|  #define BCM4354_FIRMWARE_NAME		"brcm/brcmfmac4354-sdio.bin" | ||||
|  #define BCM4354_NVRAM_NAME		"brcm/brcmfmac4354-sdio.txt" | ||||
|   | ||||
| @@ -640,6 +642,8 @@ MODULE_FIRMWARE(BCM43362_FIRMWARE_NAME); | ||||
|  MODULE_FIRMWARE(BCM43362_NVRAM_NAME); | ||||
|  MODULE_FIRMWARE(BCM4339_FIRMWARE_NAME); | ||||
|  MODULE_FIRMWARE(BCM4339_NVRAM_NAME); | ||||
| +MODULE_FIRMWARE(BCM4345_FIRMWARE_NAME); | ||||
| +MODULE_FIRMWARE(BCM4345_NVRAM_NAME); | ||||
|  MODULE_FIRMWARE(BCM4354_FIRMWARE_NAME); | ||||
|  MODULE_FIRMWARE(BCM4354_NVRAM_NAME); | ||||
|   | ||||
| @@ -669,6 +673,7 @@ static const struct brcmf_firmware_names | ||||
|  	{ BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) }, | ||||
|  	{ BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) }, | ||||
|  	{ BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) }, | ||||
| +	{ BRCM_CC_4345_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4345) }, | ||||
|  	{ BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) } | ||||
|  }; | ||||
|   | ||||
| --- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h | ||||
| +++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h | ||||
| @@ -37,6 +37,7 @@ | ||||
|  #define BRCM_CC_43362_CHIP_ID		43362 | ||||
|  #define BRCM_CC_4335_CHIP_ID		0x4335 | ||||
|  #define BRCM_CC_4339_CHIP_ID		0x4339 | ||||
| +#define BRCM_CC_4345_CHIP_ID		0x4345 | ||||
|  #define BRCM_CC_4354_CHIP_ID		0x4354 | ||||
|  #define BRCM_CC_4356_CHIP_ID		0x4356 | ||||
|  #define BRCM_CC_43566_CHIP_ID		43566 | ||||
| --- a/include/linux/mmc/sdio_ids.h | ||||
| +++ b/include/linux/mmc/sdio_ids.h | ||||
| @@ -33,6 +33,7 @@ | ||||
|  #define SDIO_DEVICE_ID_BROADCOM_43341		0xa94d | ||||
|  #define SDIO_DEVICE_ID_BROADCOM_4335_4339	0x4335 | ||||
|  #define SDIO_DEVICE_ID_BROADCOM_43362		0xa962 | ||||
| +#define SDIO_DEVICE_ID_BROADCOM_4345		0x4345 | ||||
|  #define SDIO_DEVICE_ID_BROADCOM_4354		0x4354 | ||||
|   | ||||
|  #define SDIO_VENDOR_ID_INTEL			0x0089 | ||||
| @@ -1,48 +0,0 @@ | ||||
| From: Arend van Spriel <arend@broadcom.com> | ||||
| Date: Wed, 11 Mar 2015 16:11:27 +0100 | ||||
| Subject: [PATCH] brcmfmac: remove duplication of ramsize info | ||||
|  | ||||
| Removing the ramsize from the brcmf_sdio structure to avoid | ||||
| duplication. The information is available in brcmf_chip | ||||
| structure. | ||||
|  | ||||
| Reviewed-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c | ||||
| @@ -432,8 +432,6 @@ struct brcmf_sdio { | ||||
|  	struct brcmf_sdio_dev *sdiodev;	/* sdio device handler */ | ||||
|  	struct brcmf_chip *ci;	/* Chip info struct */ | ||||
|   | ||||
| -	u32 ramsize;		/* Size of RAM in SOCRAM (bytes) */ | ||||
| - | ||||
|  	u32 hostintmask;	/* Copy of Host Interrupt Mask */ | ||||
|  	atomic_t intstatus;	/* Intstatus bits (events) pending */ | ||||
|  	atomic_t fcstate;	/* State of dongle flow-control */ | ||||
| @@ -1075,7 +1073,7 @@ static int brcmf_sdio_readshared(struct | ||||
|  	struct sdpcm_shared_le sh_le; | ||||
|  	__le32 addr_le; | ||||
|   | ||||
| -	shaddr = bus->ci->rambase + bus->ramsize - 4; | ||||
| +	shaddr = bus->ci->rambase + bus->ci->ramsize - 4; | ||||
|   | ||||
|  	/* | ||||
|  	 * Read last word in socram to determine | ||||
| @@ -3871,13 +3869,6 @@ brcmf_sdio_probe_attach(struct brcmf_sdi | ||||
|  		drivestrength = DEFAULT_SDIO_DRIVE_STRENGTH; | ||||
|  	brcmf_sdio_drivestrengthinit(bus->sdiodev, bus->ci, drivestrength); | ||||
|   | ||||
| -	/* Get info on the SOCRAM cores... */ | ||||
| -	bus->ramsize = bus->ci->ramsize; | ||||
| -	if (!(bus->ramsize)) { | ||||
| -		brcmf_err("failed to find SOCRAM memory!\n"); | ||||
| -		goto fail; | ||||
| -	} | ||||
| - | ||||
|  	/* Set card control so an SDIO card reset does a WLAN backplane reset */ | ||||
|  	reg_val = brcmf_sdiod_regrb(bus->sdiodev, | ||||
|  				    SDIO_CCCR_BRCM_CARDCTRL, &err); | ||||
| @@ -1,74 +0,0 @@ | ||||
| From: Arend van Spriel <arend@broadcom.com> | ||||
| Date: Wed, 11 Mar 2015 16:11:28 +0100 | ||||
| Subject: [PATCH] brcmfmac: always perform cores checks | ||||
|  | ||||
| Instead of checking the cores in the chip only if CONFIG_BRCMDBG | ||||
| is selected perform the check always and extend it with more sanity | ||||
| checking. | ||||
|  | ||||
| Reviewed-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c | ||||
| @@ -419,13 +419,13 @@ static struct brcmf_core *brcmf_chip_add | ||||
|  	return &core->pub; | ||||
|  } | ||||
|   | ||||
| -#ifdef DEBUG | ||||
|  /* safety check for chipinfo */ | ||||
|  static int brcmf_chip_cores_check(struct brcmf_chip_priv *ci) | ||||
|  { | ||||
|  	struct brcmf_core_priv *core; | ||||
|  	bool need_socram = false; | ||||
|  	bool has_socram = false; | ||||
| +	bool cpu_found = false; | ||||
|  	int idx = 1; | ||||
|   | ||||
|  	list_for_each_entry(core, &ci->cores, list) { | ||||
| @@ -435,12 +435,14 @@ static int brcmf_chip_cores_check(struct | ||||
|   | ||||
|  		switch (core->pub.id) { | ||||
|  		case BCMA_CORE_ARM_CM3: | ||||
| +			cpu_found = true; | ||||
|  			need_socram = true; | ||||
|  			break; | ||||
|  		case BCMA_CORE_INTERNAL_MEM: | ||||
|  			has_socram = true; | ||||
|  			break; | ||||
|  		case BCMA_CORE_ARM_CR4: | ||||
| +			cpu_found = true; | ||||
|  			if (ci->pub.rambase == 0) { | ||||
|  				brcmf_err("RAM base not provided with ARM CR4 core\n"); | ||||
|  				return -ENOMEM; | ||||
| @@ -451,19 +453,21 @@ static int brcmf_chip_cores_check(struct | ||||
|  		} | ||||
|  	} | ||||
|   | ||||
| +	if (!cpu_found) { | ||||
| +		brcmf_err("CPU core not detected\n"); | ||||
| +		return -ENXIO; | ||||
| +	} | ||||
|  	/* check RAM core presence for ARM CM3 core */ | ||||
|  	if (need_socram && !has_socram) { | ||||
|  		brcmf_err("RAM core not provided with ARM CM3 core\n"); | ||||
|  		return -ENODEV; | ||||
|  	} | ||||
| +	if (!ci->pub.ramsize) { | ||||
| +		brcmf_err("RAM size is undetermined\n"); | ||||
| +		return -ENOMEM; | ||||
| +	} | ||||
|  	return 0; | ||||
|  } | ||||
| -#else	/* DEBUG */ | ||||
| -static inline int brcmf_chip_cores_check(struct brcmf_chip_priv *ci) | ||||
| -{ | ||||
| -	return 0; | ||||
| -} | ||||
| -#endif | ||||
|   | ||||
|  static void brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) | ||||
|  { | ||||
| @@ -1,240 +0,0 @@ | ||||
| From: Arend van Spriel <arend@broadcom.com> | ||||
| Date: Wed, 11 Mar 2015 16:11:29 +0100 | ||||
| Subject: [PATCH] brcmfmac: rename chip download functions | ||||
|  | ||||
| The functions brcmf_chip_[enter/exit]_download() are not exclusively | ||||
| used for firmware download so rename these more appropriate. | ||||
|  | ||||
| Reviewed-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c | ||||
| @@ -807,7 +807,7 @@ struct brcmf_chip *brcmf_chip_attach(voi | ||||
|  		err = -EINVAL; | ||||
|  	if (WARN_ON(!ops->prepare)) | ||||
|  		err = -EINVAL; | ||||
| -	if (WARN_ON(!ops->exit_dl)) | ||||
| +	if (WARN_ON(!ops->activate)) | ||||
|  		err = -EINVAL; | ||||
|  	if (err < 0) | ||||
|  		return ERR_PTR(-EINVAL); | ||||
| @@ -905,7 +905,7 @@ void brcmf_chip_resetcore(struct brcmf_c | ||||
|  } | ||||
|   | ||||
|  static void | ||||
| -brcmf_chip_cm3_enterdl(struct brcmf_chip_priv *chip) | ||||
| +brcmf_chip_cm3_set_passive(struct brcmf_chip_priv *chip) | ||||
|  { | ||||
|  	struct brcmf_core *core; | ||||
|   | ||||
| @@ -919,7 +919,7 @@ brcmf_chip_cm3_enterdl(struct brcmf_chip | ||||
|  	brcmf_chip_resetcore(core, 0, 0, 0); | ||||
|  } | ||||
|   | ||||
| -static bool brcmf_chip_cm3_exitdl(struct brcmf_chip_priv *chip) | ||||
| +static bool brcmf_chip_cm3_set_active(struct brcmf_chip_priv *chip) | ||||
|  { | ||||
|  	struct brcmf_core *core; | ||||
|   | ||||
| @@ -929,7 +929,7 @@ static bool brcmf_chip_cm3_exitdl(struct | ||||
|  		return false; | ||||
|  	} | ||||
|   | ||||
| -	chip->ops->exit_dl(chip->ctx, &chip->pub, 0); | ||||
| +	chip->ops->activate(chip->ctx, &chip->pub, 0); | ||||
|   | ||||
|  	core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CM3); | ||||
|  	brcmf_chip_resetcore(core, 0, 0, 0); | ||||
| @@ -938,7 +938,7 @@ static bool brcmf_chip_cm3_exitdl(struct | ||||
|  } | ||||
|   | ||||
|  static inline void | ||||
| -brcmf_chip_cr4_enterdl(struct brcmf_chip_priv *chip) | ||||
| +brcmf_chip_cr4_set_passive(struct brcmf_chip_priv *chip) | ||||
|  { | ||||
|  	struct brcmf_core *core; | ||||
|   | ||||
| @@ -951,11 +951,11 @@ brcmf_chip_cr4_enterdl(struct brcmf_chip | ||||
|  			     D11_BCMA_IOCTL_PHYCLOCKEN); | ||||
|  } | ||||
|   | ||||
| -static bool brcmf_chip_cr4_exitdl(struct brcmf_chip_priv *chip, u32 rstvec) | ||||
| +static bool brcmf_chip_cr4_set_active(struct brcmf_chip_priv *chip, u32 rstvec) | ||||
|  { | ||||
|  	struct brcmf_core *core; | ||||
|   | ||||
| -	chip->ops->exit_dl(chip->ctx, &chip->pub, rstvec); | ||||
| +	chip->ops->activate(chip->ctx, &chip->pub, rstvec); | ||||
|   | ||||
|  	/* restore ARM */ | ||||
|  	core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CR4); | ||||
| @@ -964,7 +964,7 @@ static bool brcmf_chip_cr4_exitdl(struct | ||||
|  	return true; | ||||
|  } | ||||
|   | ||||
| -void brcmf_chip_enter_download(struct brcmf_chip *pub) | ||||
| +void brcmf_chip_set_passive(struct brcmf_chip *pub) | ||||
|  { | ||||
|  	struct brcmf_chip_priv *chip; | ||||
|  	struct brcmf_core *arm; | ||||
| @@ -974,14 +974,14 @@ void brcmf_chip_enter_download(struct br | ||||
|  	chip = container_of(pub, struct brcmf_chip_priv, pub); | ||||
|  	arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4); | ||||
|  	if (arm) { | ||||
| -		brcmf_chip_cr4_enterdl(chip); | ||||
| +		brcmf_chip_cr4_set_passive(chip); | ||||
|  		return; | ||||
|  	} | ||||
|   | ||||
| -	brcmf_chip_cm3_enterdl(chip); | ||||
| +	brcmf_chip_cm3_set_passive(chip); | ||||
|  } | ||||
|   | ||||
| -bool brcmf_chip_exit_download(struct brcmf_chip *pub, u32 rstvec) | ||||
| +bool brcmf_chip_set_active(struct brcmf_chip *pub, u32 rstvec) | ||||
|  { | ||||
|  	struct brcmf_chip_priv *chip; | ||||
|  	struct brcmf_core *arm; | ||||
| @@ -991,9 +991,9 @@ bool brcmf_chip_exit_download(struct brc | ||||
|  	chip = container_of(pub, struct brcmf_chip_priv, pub); | ||||
|  	arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4); | ||||
|  	if (arm) | ||||
| -		return brcmf_chip_cr4_exitdl(chip, rstvec); | ||||
| +		return brcmf_chip_cr4_set_active(chip, rstvec); | ||||
|   | ||||
| -	return brcmf_chip_cm3_exitdl(chip); | ||||
| +	return brcmf_chip_cm3_set_active(chip); | ||||
|  } | ||||
|   | ||||
|  bool brcmf_chip_sr_capable(struct brcmf_chip *pub) | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.h | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.h | ||||
| @@ -64,7 +64,7 @@ struct brcmf_core { | ||||
|   * @write32: write 32-bit value over bus. | ||||
|   * @prepare: prepare bus for core configuration. | ||||
|   * @setup: bus-specific core setup. | ||||
| - * @exit_dl: exit download state. | ||||
| + * @active: chip becomes active. | ||||
|   *	The callback should use the provided @rstvec when non-zero. | ||||
|   */ | ||||
|  struct brcmf_buscore_ops { | ||||
| @@ -72,7 +72,7 @@ struct brcmf_buscore_ops { | ||||
|  	void (*write32)(void *ctx, u32 addr, u32 value); | ||||
|  	int (*prepare)(void *ctx); | ||||
|  	int (*setup)(void *ctx, struct brcmf_chip *chip); | ||||
| -	void (*exit_dl)(void *ctx, struct brcmf_chip *chip, u32 rstvec); | ||||
| +	void (*activate)(void *ctx, struct brcmf_chip *chip, u32 rstvec); | ||||
|  }; | ||||
|   | ||||
|  struct brcmf_chip *brcmf_chip_attach(void *ctx, | ||||
| @@ -84,8 +84,8 @@ bool brcmf_chip_iscoreup(struct brcmf_co | ||||
|  void brcmf_chip_coredisable(struct brcmf_core *core, u32 prereset, u32 reset); | ||||
|  void brcmf_chip_resetcore(struct brcmf_core *core, u32 prereset, u32 reset, | ||||
|  			  u32 postreset); | ||||
| -void brcmf_chip_enter_download(struct brcmf_chip *ci); | ||||
| -bool brcmf_chip_exit_download(struct brcmf_chip *ci, u32 rstvec); | ||||
| +void brcmf_chip_set_passive(struct brcmf_chip *ci); | ||||
| +bool brcmf_chip_set_active(struct brcmf_chip *ci, u32 rstvec); | ||||
|  bool brcmf_chip_sr_capable(struct brcmf_chip *pub); | ||||
|   | ||||
|  #endif /* BRCMF_AXIDMP_H */ | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c | ||||
| @@ -509,7 +509,7 @@ static void brcmf_pcie_attach(struct brc | ||||
|   | ||||
|  static int brcmf_pcie_enter_download_state(struct brcmf_pciedev_info *devinfo) | ||||
|  { | ||||
| -	brcmf_chip_enter_download(devinfo->ci); | ||||
| +	brcmf_chip_set_passive(devinfo->ci); | ||||
|   | ||||
|  	if (devinfo->ci->chip == BRCM_CC_43602_CHIP_ID) { | ||||
|  		brcmf_pcie_select_core(devinfo, BCMA_CORE_ARM_CR4); | ||||
| @@ -536,7 +536,7 @@ static int brcmf_pcie_exit_download_stat | ||||
|  		brcmf_chip_resetcore(core, 0, 0, 0); | ||||
|  	} | ||||
|   | ||||
| -	return !brcmf_chip_exit_download(devinfo->ci, resetintr); | ||||
| +	return !brcmf_chip_set_active(devinfo->ci, resetintr); | ||||
|  } | ||||
|   | ||||
|   | ||||
| @@ -1566,8 +1566,8 @@ static int brcmf_pcie_buscoreprep(void * | ||||
|  } | ||||
|   | ||||
|   | ||||
| -static void brcmf_pcie_buscore_exitdl(void *ctx, struct brcmf_chip *chip, | ||||
| -				      u32 rstvec) | ||||
| +static void brcmf_pcie_buscore_activate(void *ctx, struct brcmf_chip *chip, | ||||
| +					u32 rstvec) | ||||
|  { | ||||
|  	struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx; | ||||
|   | ||||
| @@ -1577,7 +1577,7 @@ static void brcmf_pcie_buscore_exitdl(vo | ||||
|   | ||||
|  static const struct brcmf_buscore_ops brcmf_pcie_buscore_ops = { | ||||
|  	.prepare = brcmf_pcie_buscoreprep, | ||||
| -	.exit_dl = brcmf_pcie_buscore_exitdl, | ||||
| +	.activate = brcmf_pcie_buscore_activate, | ||||
|  	.read32 = brcmf_pcie_buscore_read32, | ||||
|  	.write32 = brcmf_pcie_buscore_write32, | ||||
|  }; | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c | ||||
| @@ -3357,7 +3357,7 @@ static int brcmf_sdio_download_firmware( | ||||
|  	brcmf_sdio_clkctl(bus, CLK_AVAIL, false); | ||||
|   | ||||
|  	/* Keep arm in reset */ | ||||
| -	brcmf_chip_enter_download(bus->ci); | ||||
| +	brcmf_chip_set_passive(bus->ci); | ||||
|   | ||||
|  	rstvec = get_unaligned_le32(fw->data); | ||||
|  	brcmf_dbg(SDIO, "firmware rstvec: %x\n", rstvec); | ||||
| @@ -3378,7 +3378,7 @@ static int brcmf_sdio_download_firmware( | ||||
|  	} | ||||
|   | ||||
|  	/* Take arm out of reset */ | ||||
| -	if (!brcmf_chip_exit_download(bus->ci, rstvec)) { | ||||
| +	if (!brcmf_chip_set_active(bus->ci, rstvec)) { | ||||
|  		brcmf_err("error getting out of ARM core reset\n"); | ||||
|  		goto err; | ||||
|  	} | ||||
| @@ -3771,8 +3771,8 @@ static int brcmf_sdio_buscoreprep(void * | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| -static void brcmf_sdio_buscore_exitdl(void *ctx, struct brcmf_chip *chip, | ||||
| -				      u32 rstvec) | ||||
| +static void brcmf_sdio_buscore_activate(void *ctx, struct brcmf_chip *chip, | ||||
| +					u32 rstvec) | ||||
|  { | ||||
|  	struct brcmf_sdio_dev *sdiodev = ctx; | ||||
|  	struct brcmf_core *core; | ||||
| @@ -3815,7 +3815,7 @@ static void brcmf_sdio_buscore_write32(v | ||||
|   | ||||
|  static const struct brcmf_buscore_ops brcmf_sdio_buscore_ops = { | ||||
|  	.prepare = brcmf_sdio_buscoreprep, | ||||
| -	.exit_dl = brcmf_sdio_buscore_exitdl, | ||||
| +	.activate = brcmf_sdio_buscore_activate, | ||||
|  	.read32 = brcmf_sdio_buscore_read32, | ||||
|  	.write32 = brcmf_sdio_buscore_write32, | ||||
|  }; | ||||
| @@ -4239,12 +4239,11 @@ void brcmf_sdio_remove(struct brcmf_sdio | ||||
|  				sdio_claim_host(bus->sdiodev->func[1]); | ||||
|  				brcmf_sdio_clkctl(bus, CLK_AVAIL, false); | ||||
|  				/* Leave the device in state where it is | ||||
| -				 * 'quiet'. This is done by putting it in | ||||
| -				 * download_state which essentially resets | ||||
| -				 * all necessary cores. | ||||
| +				 * 'passive'. This is done by resetting all | ||||
| +				 * necessary cores. | ||||
|  				 */ | ||||
|  				msleep(20); | ||||
| -				brcmf_chip_enter_download(bus->ci); | ||||
| +				brcmf_chip_set_passive(bus->ci); | ||||
|  				brcmf_sdio_clkctl(bus, CLK_NONE, false); | ||||
|  				sdio_release_host(bus->sdiodev->func[1]); | ||||
|  			} | ||||
| @@ -1,61 +0,0 @@ | ||||
| From: Arend van Spriel <arend@broadcom.com> | ||||
| Date: Wed, 11 Mar 2015 16:11:30 +0100 | ||||
| Subject: [PATCH] brcmfmac: assure device is ready for download after | ||||
|  brcmf_chip_attach() | ||||
|  | ||||
| Make the brcmf_chip_attach() function responsible for putting the | ||||
| device in a state where it is accessible for firmware download. | ||||
|  | ||||
| Reviewed-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c | ||||
| @@ -786,12 +786,6 @@ static int brcmf_chip_setup(struct brcmf | ||||
|  	if (chip->ops->setup) | ||||
|  		ret = chip->ops->setup(chip->ctx, pub); | ||||
|   | ||||
| -	/* | ||||
| -	 * Make sure any on-chip ARM is off (in case strapping is wrong), | ||||
| -	 * or downloaded code was already running. | ||||
| -	 */ | ||||
| -	brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CM3); | ||||
| -	brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CR4); | ||||
|  	return ret; | ||||
|  } | ||||
|   | ||||
| @@ -833,6 +827,8 @@ struct brcmf_chip *brcmf_chip_attach(voi | ||||
|  	if (err < 0) | ||||
|  		goto fail; | ||||
|   | ||||
| +	/* assure chip is passive for download */ | ||||
| +	brcmf_chip_set_passive(&chip->pub); | ||||
|  	return &chip->pub; | ||||
|   | ||||
|  fail: | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c | ||||
| @@ -509,8 +509,6 @@ static void brcmf_pcie_attach(struct brc | ||||
|   | ||||
|  static int brcmf_pcie_enter_download_state(struct brcmf_pciedev_info *devinfo) | ||||
|  { | ||||
| -	brcmf_chip_set_passive(devinfo->ci); | ||||
| - | ||||
|  	if (devinfo->ci->chip == BRCM_CC_43602_CHIP_ID) { | ||||
|  		brcmf_pcie_select_core(devinfo, BCMA_CORE_ARM_CR4); | ||||
|  		brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKIDX, | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c | ||||
| @@ -3356,9 +3356,6 @@ static int brcmf_sdio_download_firmware( | ||||
|  	sdio_claim_host(bus->sdiodev->func[1]); | ||||
|  	brcmf_sdio_clkctl(bus, CLK_AVAIL, false); | ||||
|   | ||||
| -	/* Keep arm in reset */ | ||||
| -	brcmf_chip_set_passive(bus->ci); | ||||
| - | ||||
|  	rstvec = get_unaligned_le32(fw->data); | ||||
|  	brcmf_dbg(SDIO, "firmware rstvec: %x\n", rstvec); | ||||
|   | ||||
| @@ -1,367 +0,0 @@ | ||||
| From: Arend van Spriel <arend@broadcom.com> | ||||
| Date: Wed, 11 Mar 2015 16:11:31 +0100 | ||||
| Subject: [PATCH] brcmfmac: extract ram size info from internal memory | ||||
|  registers | ||||
|  | ||||
| Instead of hard-coded memory sizes it is possible to obtain that | ||||
| information from the internal memory registers. | ||||
|  | ||||
| Reviewed-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c | ||||
| @@ -100,9 +100,6 @@ | ||||
|  #define BCM4329_CORE_SOCRAM_BASE	0x18003000 | ||||
|  /* ARM Cortex M3 core, ID 0x82a */ | ||||
|  #define BCM4329_CORE_ARM_BASE		0x18002000 | ||||
| -#define BCM4329_RAMSIZE			0x48000 | ||||
| -/* bcm43143 */ | ||||
| -#define BCM43143_RAMSIZE		0x70000 | ||||
|   | ||||
|  #define CORE_SB(base, field) \ | ||||
|  		(base + SBCONFIGOFF + offsetof(struct sbconfig, field)) | ||||
| @@ -150,6 +147,78 @@ struct sbconfig { | ||||
|  	u32 sbidhigh;	/* identification */ | ||||
|  }; | ||||
|   | ||||
| +/* bankidx and bankinfo reg defines corerev >= 8 */ | ||||
| +#define SOCRAM_BANKINFO_RETNTRAM_MASK	0x00010000 | ||||
| +#define SOCRAM_BANKINFO_SZMASK		0x0000007f | ||||
| +#define SOCRAM_BANKIDX_ROM_MASK		0x00000100 | ||||
| + | ||||
| +#define SOCRAM_BANKIDX_MEMTYPE_SHIFT	8 | ||||
| +/* socram bankinfo memtype */ | ||||
| +#define SOCRAM_MEMTYPE_RAM		0 | ||||
| +#define SOCRAM_MEMTYPE_R0M		1 | ||||
| +#define SOCRAM_MEMTYPE_DEVRAM		2 | ||||
| + | ||||
| +#define SOCRAM_BANKINFO_SZBASE		8192 | ||||
| +#define SRCI_LSS_MASK		0x00f00000 | ||||
| +#define SRCI_LSS_SHIFT		20 | ||||
| +#define	SRCI_SRNB_MASK		0xf0 | ||||
| +#define	SRCI_SRNB_SHIFT		4 | ||||
| +#define	SRCI_SRBSZ_MASK		0xf | ||||
| +#define	SRCI_SRBSZ_SHIFT	0 | ||||
| +#define SR_BSZ_BASE		14 | ||||
| + | ||||
| +struct sbsocramregs { | ||||
| +	u32 coreinfo; | ||||
| +	u32 bwalloc; | ||||
| +	u32 extracoreinfo; | ||||
| +	u32 biststat; | ||||
| +	u32 bankidx; | ||||
| +	u32 standbyctrl; | ||||
| + | ||||
| +	u32 errlogstatus;	/* rev 6 */ | ||||
| +	u32 errlogaddr;	/* rev 6 */ | ||||
| +	/* used for patching rev 3 & 5 */ | ||||
| +	u32 cambankidx; | ||||
| +	u32 cambankstandbyctrl; | ||||
| +	u32 cambankpatchctrl; | ||||
| +	u32 cambankpatchtblbaseaddr; | ||||
| +	u32 cambankcmdreg; | ||||
| +	u32 cambankdatareg; | ||||
| +	u32 cambankmaskreg; | ||||
| +	u32 PAD[1]; | ||||
| +	u32 bankinfo;	/* corev 8 */ | ||||
| +	u32 bankpda; | ||||
| +	u32 PAD[14]; | ||||
| +	u32 extmemconfig; | ||||
| +	u32 extmemparitycsr; | ||||
| +	u32 extmemparityerrdata; | ||||
| +	u32 extmemparityerrcnt; | ||||
| +	u32 extmemwrctrlandsize; | ||||
| +	u32 PAD[84]; | ||||
| +	u32 workaround; | ||||
| +	u32 pwrctl;		/* corerev >= 2 */ | ||||
| +	u32 PAD[133]; | ||||
| +	u32 sr_control;     /* corerev >= 15 */ | ||||
| +	u32 sr_status;      /* corerev >= 15 */ | ||||
| +	u32 sr_address;     /* corerev >= 15 */ | ||||
| +	u32 sr_data;        /* corerev >= 15 */ | ||||
| +}; | ||||
| + | ||||
| +#define SOCRAMREGOFFS(_f)	offsetof(struct sbsocramregs, _f) | ||||
| + | ||||
| +#define ARMCR4_CAP		(0x04) | ||||
| +#define ARMCR4_BANKIDX		(0x40) | ||||
| +#define ARMCR4_BANKINFO		(0x44) | ||||
| +#define ARMCR4_BANKPDA		(0x4C) | ||||
| + | ||||
| +#define	ARMCR4_TCBBNB_MASK	0xf0 | ||||
| +#define	ARMCR4_TCBBNB_SHIFT	4 | ||||
| +#define	ARMCR4_TCBANB_MASK	0xf | ||||
| +#define	ARMCR4_TCBANB_SHIFT	0 | ||||
| + | ||||
| +#define	ARMCR4_BSZ_MASK		0x3f | ||||
| +#define	ARMCR4_BSZ_MULT		8192 | ||||
| + | ||||
|  struct brcmf_core_priv { | ||||
|  	struct brcmf_core pub; | ||||
|  	u32 wrapbase; | ||||
| @@ -443,10 +512,6 @@ static int brcmf_chip_cores_check(struct | ||||
|  			break; | ||||
|  		case BCMA_CORE_ARM_CR4: | ||||
|  			cpu_found = true; | ||||
| -			if (ci->pub.rambase == 0) { | ||||
| -				brcmf_err("RAM base not provided with ARM CR4 core\n"); | ||||
| -				return -ENOMEM; | ||||
| -			} | ||||
|  			break; | ||||
|  		default: | ||||
|  			break; | ||||
| @@ -462,60 +527,160 @@ static int brcmf_chip_cores_check(struct | ||||
|  		brcmf_err("RAM core not provided with ARM CM3 core\n"); | ||||
|  		return -ENODEV; | ||||
|  	} | ||||
| -	if (!ci->pub.ramsize) { | ||||
| -		brcmf_err("RAM size is undetermined\n"); | ||||
| -		return -ENOMEM; | ||||
| -	} | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| -static void brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) | ||||
| +static u32 brcmf_chip_core_read32(struct brcmf_core_priv *core, u16 reg) | ||||
|  { | ||||
| -	switch (ci->pub.chip) { | ||||
| -	case BRCM_CC_4329_CHIP_ID: | ||||
| -		ci->pub.ramsize = BCM4329_RAMSIZE; | ||||
| -		break; | ||||
| -	case BRCM_CC_43143_CHIP_ID: | ||||
| -		ci->pub.ramsize = BCM43143_RAMSIZE; | ||||
| -		break; | ||||
| -	case BRCM_CC_43241_CHIP_ID: | ||||
| -		ci->pub.ramsize = 0x90000; | ||||
| -		break; | ||||
| -	case BRCM_CC_4330_CHIP_ID: | ||||
| -		ci->pub.ramsize = 0x48000; | ||||
| -		break; | ||||
| +	return core->chip->ops->read32(core->chip->ctx, core->pub.base + reg); | ||||
| +} | ||||
| + | ||||
| +static void brcmf_chip_core_write32(struct brcmf_core_priv *core, | ||||
| +				    u16 reg, u32 val) | ||||
| +{ | ||||
| +	core->chip->ops->write32(core->chip->ctx, core->pub.base + reg, val); | ||||
| +} | ||||
| + | ||||
| +static bool brcmf_chip_socram_banksize(struct brcmf_core_priv *core, u8 idx, | ||||
| +				       u32 *banksize) | ||||
| +{ | ||||
| +	u32 bankinfo; | ||||
| +	u32 bankidx = (SOCRAM_MEMTYPE_RAM << SOCRAM_BANKIDX_MEMTYPE_SHIFT); | ||||
| + | ||||
| +	bankidx |= idx; | ||||
| +	brcmf_chip_core_write32(core, SOCRAMREGOFFS(bankidx), bankidx); | ||||
| +	bankinfo = brcmf_chip_core_read32(core, SOCRAMREGOFFS(bankinfo)); | ||||
| +	*banksize = (bankinfo & SOCRAM_BANKINFO_SZMASK) + 1; | ||||
| +	*banksize *= SOCRAM_BANKINFO_SZBASE; | ||||
| +	return !!(bankinfo & SOCRAM_BANKINFO_RETNTRAM_MASK); | ||||
| +} | ||||
| + | ||||
| +static void brcmf_chip_socram_ramsize(struct brcmf_core_priv *sr, u32 *ramsize, | ||||
| +				      u32 *srsize) | ||||
| +{ | ||||
| +	u32 coreinfo; | ||||
| +	uint nb, banksize, lss; | ||||
| +	bool retent; | ||||
| +	int i; | ||||
| + | ||||
| +	*ramsize = 0; | ||||
| +	*srsize = 0; | ||||
| + | ||||
| +	if (WARN_ON(sr->pub.rev < 4)) | ||||
| +		return; | ||||
| + | ||||
| +	if (!brcmf_chip_iscoreup(&sr->pub)) | ||||
| +		brcmf_chip_resetcore(&sr->pub, 0, 0, 0); | ||||
| + | ||||
| +	/* Get info for determining size */ | ||||
| +	coreinfo = brcmf_chip_core_read32(sr, SOCRAMREGOFFS(coreinfo)); | ||||
| +	nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT; | ||||
| + | ||||
| +	if ((sr->pub.rev <= 7) || (sr->pub.rev == 12)) { | ||||
| +		banksize = (coreinfo & SRCI_SRBSZ_MASK); | ||||
| +		lss = (coreinfo & SRCI_LSS_MASK) >> SRCI_LSS_SHIFT; | ||||
| +		if (lss != 0) | ||||
| +			nb--; | ||||
| +		*ramsize = nb * (1 << (banksize + SR_BSZ_BASE)); | ||||
| +		if (lss != 0) | ||||
| +			*ramsize += (1 << ((lss - 1) + SR_BSZ_BASE)); | ||||
| +	} else { | ||||
| +		nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT; | ||||
| +		for (i = 0; i < nb; i++) { | ||||
| +			retent = brcmf_chip_socram_banksize(sr, i, &banksize); | ||||
| +			*ramsize += banksize; | ||||
| +			if (retent) | ||||
| +				*srsize += banksize; | ||||
| +		} | ||||
| +	} | ||||
| + | ||||
| +	/* hardcoded save&restore memory sizes */ | ||||
| +	switch (sr->chip->pub.chip) { | ||||
|  	case BRCM_CC_4334_CHIP_ID: | ||||
| -	case BRCM_CC_43340_CHIP_ID: | ||||
| -		ci->pub.ramsize = 0x80000; | ||||
| +		if (sr->chip->pub.chiprev < 2) | ||||
| +			*srsize = (32 * 1024); | ||||
|  		break; | ||||
| -	case BRCM_CC_4335_CHIP_ID: | ||||
| -		ci->pub.ramsize = 0xc0000; | ||||
| -		ci->pub.rambase = 0x180000; | ||||
| -		break; | ||||
| -	case BRCM_CC_43362_CHIP_ID: | ||||
| -		ci->pub.ramsize = 0x3c000; | ||||
| +	default: | ||||
|  		break; | ||||
| +	} | ||||
| +} | ||||
| + | ||||
| +/** Return the TCM-RAM size of the ARMCR4 core. */ | ||||
| +static u32 brcmf_chip_tcm_ramsize(struct brcmf_core_priv *cr4) | ||||
| +{ | ||||
| +	u32 corecap; | ||||
| +	u32 memsize = 0; | ||||
| +	u32 nab; | ||||
| +	u32 nbb; | ||||
| +	u32 totb; | ||||
| +	u32 bxinfo; | ||||
| +	u32 idx; | ||||
| + | ||||
| +	corecap = brcmf_chip_core_read32(cr4, ARMCR4_CAP); | ||||
| + | ||||
| +	nab = (corecap & ARMCR4_TCBANB_MASK) >> ARMCR4_TCBANB_SHIFT; | ||||
| +	nbb = (corecap & ARMCR4_TCBBNB_MASK) >> ARMCR4_TCBBNB_SHIFT; | ||||
| +	totb = nab + nbb; | ||||
| + | ||||
| +	for (idx = 0; idx < totb; idx++) { | ||||
| +		brcmf_chip_core_write32(cr4, ARMCR4_BANKIDX, idx); | ||||
| +		bxinfo = brcmf_chip_core_read32(cr4, ARMCR4_BANKINFO); | ||||
| +		memsize += ((bxinfo & ARMCR4_BSZ_MASK) + 1) * ARMCR4_BSZ_MULT; | ||||
| +	} | ||||
| + | ||||
| +	return memsize; | ||||
| +} | ||||
| + | ||||
| +static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci) | ||||
| +{ | ||||
| +	switch (ci->pub.chip) { | ||||
|  	case BRCM_CC_4345_CHIP_ID: | ||||
| -		ci->pub.ramsize = 0xc8000; | ||||
| -		ci->pub.rambase = 0x198000; | ||||
| -		break; | ||||
| +		return 0x198000; | ||||
| +	case BRCM_CC_4335_CHIP_ID: | ||||
|  	case BRCM_CC_4339_CHIP_ID: | ||||
|  	case BRCM_CC_4354_CHIP_ID: | ||||
|  	case BRCM_CC_4356_CHIP_ID: | ||||
|  	case BRCM_CC_43567_CHIP_ID: | ||||
|  	case BRCM_CC_43569_CHIP_ID: | ||||
|  	case BRCM_CC_43570_CHIP_ID: | ||||
| -		ci->pub.ramsize = 0xc0000; | ||||
| -		ci->pub.rambase = 0x180000; | ||||
| -		break; | ||||
|  	case BRCM_CC_43602_CHIP_ID: | ||||
| -		ci->pub.ramsize = 0xf0000; | ||||
| -		ci->pub.rambase = 0x180000; | ||||
| -		break; | ||||
| +		return 0x180000; | ||||
|  	default: | ||||
|  		brcmf_err("unknown chip: %s\n", ci->pub.name); | ||||
|  		break; | ||||
|  	} | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) | ||||
| +{ | ||||
| +	struct brcmf_core_priv *mem_core; | ||||
| +	struct brcmf_core *mem; | ||||
| + | ||||
| +	mem = brcmf_chip_get_core(&ci->pub, BCMA_CORE_ARM_CR4); | ||||
| +	if (mem) { | ||||
| +		mem_core = container_of(mem, struct brcmf_core_priv, pub); | ||||
| +		ci->pub.ramsize = brcmf_chip_tcm_ramsize(mem_core); | ||||
| +		ci->pub.rambase = brcmf_chip_tcm_rambase(ci); | ||||
| +		if (!ci->pub.rambase) { | ||||
| +			brcmf_err("RAM base not provided with ARM CR4 core\n"); | ||||
| +			return -EINVAL; | ||||
| +		} | ||||
| +	} else { | ||||
| +		mem = brcmf_chip_get_core(&ci->pub, BCMA_CORE_INTERNAL_MEM); | ||||
| +		mem_core = container_of(mem, struct brcmf_core_priv, pub); | ||||
| +		brcmf_chip_socram_ramsize(mem_core, &ci->pub.ramsize, | ||||
| +					  &ci->pub.srsize); | ||||
| +	} | ||||
| +	brcmf_dbg(INFO, "RAM: base=0x%x size=%d (0x%x) sr=%d (0x%x)\n", | ||||
| +		  ci->pub.rambase, ci->pub.ramsize, ci->pub.ramsize, | ||||
| +		  ci->pub.srsize, ci->pub.srsize); | ||||
| + | ||||
| +	if (!ci->pub.ramsize) { | ||||
| +		brcmf_err("RAM size is undetermined\n"); | ||||
| +		return -ENOMEM; | ||||
| +	} | ||||
| +	return 0; | ||||
|  } | ||||
|   | ||||
|  static u32 brcmf_chip_dmp_get_desc(struct brcmf_chip_priv *ci, u32 *eromaddr, | ||||
| @@ -668,6 +833,7 @@ static int brcmf_chip_recognition(struct | ||||
|  	struct brcmf_core *core; | ||||
|  	u32 regdata; | ||||
|  	u32 socitype; | ||||
| +	int ret; | ||||
|   | ||||
|  	/* Get CC core rev | ||||
|  	 * Chipid is assume to be at offset 0 from SI_ENUM_BASE | ||||
| @@ -720,9 +886,13 @@ static int brcmf_chip_recognition(struct | ||||
|  		return -ENODEV; | ||||
|  	} | ||||
|   | ||||
| -	brcmf_chip_get_raminfo(ci); | ||||
| - | ||||
| -	return brcmf_chip_cores_check(ci); | ||||
| +	ret = brcmf_chip_cores_check(ci); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	/* assure chip is passive for core access */ | ||||
| +	brcmf_chip_set_passive(&ci->pub); | ||||
| +	return brcmf_chip_get_raminfo(ci); | ||||
|  } | ||||
|   | ||||
|  static void brcmf_chip_disable_arm(struct brcmf_chip_priv *chip, u16 id) | ||||
| @@ -827,8 +997,6 @@ struct brcmf_chip *brcmf_chip_attach(voi | ||||
|  	if (err < 0) | ||||
|  		goto fail; | ||||
|   | ||||
| -	/* assure chip is passive for download */ | ||||
| -	brcmf_chip_set_passive(&chip->pub); | ||||
|  	return &chip->pub; | ||||
|   | ||||
|  fail: | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.h | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.h | ||||
| @@ -30,7 +30,8 @@ | ||||
|   * @pmucaps: PMU capabilities. | ||||
|   * @pmurev: PMU revision. | ||||
|   * @rambase: RAM base address (only applicable for ARM CR4 chips). | ||||
| - * @ramsize: amount of RAM on chip. | ||||
| + * @ramsize: amount of RAM on chip including retention. | ||||
| + * @srsize: amount of retention RAM on chip. | ||||
|   * @name: string representation of the chip identifier. | ||||
|   */ | ||||
|  struct brcmf_chip { | ||||
| @@ -41,6 +42,7 @@ struct brcmf_chip { | ||||
|  	u32 pmurev; | ||||
|  	u32 rambase; | ||||
|  	u32 ramsize; | ||||
| +	u32 srsize; | ||||
|  	char name[8]; | ||||
|  }; | ||||
|   | ||||
| @@ -1,96 +0,0 @@ | ||||
| From: Arend van Spriel <arend@broadcom.com> | ||||
| Date: Wed, 11 Mar 2015 16:11:32 +0100 | ||||
| Subject: [PATCH] brcmfmac: take save&restore memory into account for SDIO | ||||
|  shared info | ||||
|  | ||||
| The firmware provides pointer to SDIO shared information at end of | ||||
| RAM during firmware initialization. End of RAM is obviously determined | ||||
| by the actual ram size, but part of that may be used for save&restore | ||||
| memory. In that case another location in RAM will hold the pointer. | ||||
|  | ||||
| Reviewed-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c | ||||
| @@ -1067,44 +1067,47 @@ static inline bool brcmf_sdio_valid_shar | ||||
|  static int brcmf_sdio_readshared(struct brcmf_sdio *bus, | ||||
|  				 struct sdpcm_shared *sh) | ||||
|  { | ||||
| -	u32 addr; | ||||
| +	u32 addr = 0; | ||||
|  	int rv; | ||||
|  	u32 shaddr = 0; | ||||
|  	struct sdpcm_shared_le sh_le; | ||||
|  	__le32 addr_le; | ||||
|   | ||||
| -	shaddr = bus->ci->rambase + bus->ci->ramsize - 4; | ||||
| +	sdio_claim_host(bus->sdiodev->func[1]); | ||||
| +	brcmf_sdio_bus_sleep(bus, false, false); | ||||
|   | ||||
|  	/* | ||||
|  	 * Read last word in socram to determine | ||||
|  	 * address of sdpcm_shared structure | ||||
|  	 */ | ||||
| -	sdio_claim_host(bus->sdiodev->func[1]); | ||||
| -	brcmf_sdio_bus_sleep(bus, false, false); | ||||
| -	rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr, (u8 *)&addr_le, 4); | ||||
| -	sdio_release_host(bus->sdiodev->func[1]); | ||||
| +	shaddr = bus->ci->rambase + bus->ci->ramsize - 4; | ||||
| +	if (!bus->ci->rambase && brcmf_chip_sr_capable(bus->ci)) | ||||
| +		shaddr -= bus->ci->srsize; | ||||
| +	rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr, | ||||
| +			       (u8 *)&addr_le, 4); | ||||
|  	if (rv < 0) | ||||
| -		return rv; | ||||
| - | ||||
| -	addr = le32_to_cpu(addr_le); | ||||
| - | ||||
| -	brcmf_dbg(SDIO, "sdpcm_shared address 0x%08X\n", addr); | ||||
| +		goto fail; | ||||
|   | ||||
|  	/* | ||||
|  	 * Check if addr is valid. | ||||
|  	 * NVRAM length at the end of memory should have been overwritten. | ||||
|  	 */ | ||||
| +	addr = le32_to_cpu(addr_le); | ||||
|  	if (!brcmf_sdio_valid_shared_address(addr)) { | ||||
| -			brcmf_err("invalid sdpcm_shared address 0x%08X\n", | ||||
| -				  addr); | ||||
| -			return -EINVAL; | ||||
| +		brcmf_err("invalid sdpcm_shared address 0x%08X\n", addr); | ||||
| +		rv = -EINVAL; | ||||
| +		goto fail; | ||||
|  	} | ||||
|   | ||||
| +	brcmf_dbg(INFO, "sdpcm_shared address 0x%08X\n", addr); | ||||
| + | ||||
|  	/* Read hndrte_shared structure */ | ||||
|  	rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le, | ||||
|  			       sizeof(struct sdpcm_shared_le)); | ||||
|  	if (rv < 0) | ||||
| -		return rv; | ||||
| +		goto fail; | ||||
| + | ||||
| +	sdio_release_host(bus->sdiodev->func[1]); | ||||
|   | ||||
|  	/* Endianness */ | ||||
|  	sh->flags = le32_to_cpu(sh_le.flags); | ||||
| @@ -1121,8 +1124,13 @@ static int brcmf_sdio_readshared(struct | ||||
|  			  sh->flags & SDPCM_SHARED_VERSION_MASK); | ||||
|  		return -EPROTO; | ||||
|  	} | ||||
| - | ||||
|  	return 0; | ||||
| + | ||||
| +fail: | ||||
| +	brcmf_err("unable to obtain sdpcm_shared info: rv=%d (addr=0x%x)\n", | ||||
| +		  rv, addr); | ||||
| +	sdio_release_host(bus->sdiodev->func[1]); | ||||
| +	return rv; | ||||
|  } | ||||
|   | ||||
|  static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus) | ||||
| @@ -1,59 +0,0 @@ | ||||
| From: Arend van Spriel <arend@broadcom.com> | ||||
| Date: Wed, 11 Mar 2015 16:11:33 +0100 | ||||
| Subject: [PATCH] brcmfmac: fix watchdog timer regression | ||||
|  | ||||
| The watchdog timer is used to put the device in a low-power mode when | ||||
| it is idle for some time. This timer is stopped during that mode and | ||||
| should be restarted upon activity. This has been broken by commit | ||||
| d4150fced0365 ("brcmfmac: Simplify watchdog sleep."). This patch | ||||
| restores the behaviour as it was before that commit. | ||||
|  | ||||
| Reported-by: Pontus Fuchs <pontusf@broadcom.com> | ||||
| Reviewed-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c | ||||
| @@ -972,7 +972,6 @@ static int brcmf_sdio_clkctl(struct brcm | ||||
|  			brcmf_sdio_sdclk(bus, true); | ||||
|  		/* Now request HT Avail on the backplane */ | ||||
|  		brcmf_sdio_htclk(bus, true, pendok); | ||||
| -		brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); | ||||
|  		break; | ||||
|   | ||||
|  	case CLK_SDONLY: | ||||
| @@ -984,7 +983,6 @@ static int brcmf_sdio_clkctl(struct brcm | ||||
|  		else | ||||
|  			brcmf_err("request for %d -> %d\n", | ||||
|  				  bus->clkstate, target); | ||||
| -		brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); | ||||
|  		break; | ||||
|   | ||||
|  	case CLK_NONE: | ||||
| @@ -993,7 +991,6 @@ static int brcmf_sdio_clkctl(struct brcm | ||||
|  			brcmf_sdio_htclk(bus, false, false); | ||||
|  		/* Now remove the SD clock */ | ||||
|  		brcmf_sdio_sdclk(bus, false); | ||||
| -		brcmf_sdio_wd_timer(bus, 0); | ||||
|  		break; | ||||
|  	} | ||||
|  #ifdef DEBUG | ||||
| @@ -1048,6 +1045,7 @@ end: | ||||
|  			brcmf_sdio_clkctl(bus, CLK_NONE, pendok); | ||||
|  	} else { | ||||
|  		brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok); | ||||
| +		brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); | ||||
|  	} | ||||
|  	bus->sleeping = sleep; | ||||
|  	brcmf_dbg(SDIO, "new state %s\n", | ||||
| @@ -4242,6 +4240,7 @@ void brcmf_sdio_remove(struct brcmf_sdio | ||||
|  		if (bus->ci) { | ||||
|  			if (bus->sdiodev->state != BRCMF_SDIOD_NOMEDIUM) { | ||||
|  				sdio_claim_host(bus->sdiodev->func[1]); | ||||
| +				brcmf_sdio_wd_timer(bus, 0); | ||||
|  				brcmf_sdio_clkctl(bus, CLK_AVAIL, false); | ||||
|  				/* Leave the device in state where it is | ||||
|  				 * 'passive'. This is done by resetting all | ||||
| @@ -1,44 +0,0 @@ | ||||
| From: Arend van Spriel <arend@broadcom.com> | ||||
| Date: Wed, 18 Mar 2015 13:25:21 +0100 | ||||
| Subject: [PATCH] brcmfmac: avoid runtime-pm for sdio host controller | ||||
|  | ||||
| Several host controllers supporting runtime-pm are causing issues | ||||
| with our sdio wireless cards because they disable the sdio interrupt | ||||
| upon going into runtime suspend. This patch avoids that by doing | ||||
| a pm_runtime_forbid() call during the probe. Tested with Sony Vaio | ||||
| Duo 13 which uses sdhci-acpi host controller. | ||||
|  | ||||
| Reviewed-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | ||||
| @@ -29,6 +29,7 @@ | ||||
|  #include <linux/mmc/host.h> | ||||
|  #include <linux/platform_device.h> | ||||
|  #include <linux/platform_data/brcmfmac-sdio.h> | ||||
| +#include <linux/pm_runtime.h> | ||||
|  #include <linux/suspend.h> | ||||
|  #include <linux/errno.h> | ||||
|  #include <linux/module.h> | ||||
| @@ -1006,6 +1007,7 @@ static int brcmf_sdiod_remove(struct brc | ||||
|  	sg_free_table(&sdiodev->sgtable); | ||||
|  	sdiodev->sbwad = 0; | ||||
|   | ||||
| +	pm_runtime_allow(sdiodev->func[1]->card->host->parent); | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| @@ -1074,7 +1076,7 @@ static int brcmf_sdiod_probe(struct brcm | ||||
|  		ret = -ENODEV; | ||||
|  		goto out; | ||||
|  	} | ||||
| - | ||||
| +	pm_runtime_forbid(host->parent); | ||||
|  out: | ||||
|  	if (ret) | ||||
|  		brcmf_sdiod_remove(sdiodev); | ||||
| @@ -1,171 +0,0 @@ | ||||
| From: Hante Meuleman <meuleman@broadcom.com> | ||||
| Date: Wed, 18 Mar 2015 13:25:22 +0100 | ||||
| Subject: [PATCH] brcmfmac: Add necessary memory barriers for SDIO. | ||||
|  | ||||
| SDIO uses a thread to handle all communication with the device, | ||||
| for this data is exchanged between threads. This data needs proper | ||||
| memory barriers to make sure that data "exchange" is going correct. | ||||
|  | ||||
| Reviewed-by: Arend Van Spriel <arend@broadcom.com> | ||||
| Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com> | ||||
| Signed-off-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c | ||||
| @@ -507,8 +507,8 @@ struct brcmf_sdio { | ||||
|   | ||||
|  	struct workqueue_struct *brcmf_wq; | ||||
|  	struct work_struct datawork; | ||||
| -	atomic_t dpc_tskcnt; | ||||
| -	atomic_t dpc_running; | ||||
| +	bool dpc_triggered; | ||||
| +	bool dpc_running; | ||||
|   | ||||
|  	bool txoff;		/* Transmit flow-controlled */ | ||||
|  	struct brcmf_sdio_count sdcnt; | ||||
| @@ -2713,6 +2713,7 @@ static void brcmf_sdio_dpc(struct brcmf_ | ||||
|  			err = brcmf_sdio_tx_ctrlframe(bus,  bus->ctrl_frame_buf, | ||||
|  						      bus->ctrl_frame_len); | ||||
|  			bus->ctrl_frame_err = err; | ||||
| +			wmb(); | ||||
|  			bus->ctrl_frame_stat = false; | ||||
|  		} | ||||
|  		sdio_release_host(bus->sdiodev->func[1]); | ||||
| @@ -2734,6 +2735,7 @@ static void brcmf_sdio_dpc(struct brcmf_ | ||||
|  			sdio_claim_host(bus->sdiodev->func[1]); | ||||
|  			if (bus->ctrl_frame_stat) { | ||||
|  				bus->ctrl_frame_err = -ENODEV; | ||||
| +				wmb(); | ||||
|  				bus->ctrl_frame_stat = false; | ||||
|  				brcmf_sdio_wait_event_wakeup(bus); | ||||
|  			} | ||||
| @@ -2744,7 +2746,7 @@ static void brcmf_sdio_dpc(struct brcmf_ | ||||
|  		   (!atomic_read(&bus->fcstate) && | ||||
|  		    brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && | ||||
|  		    data_ok(bus))) { | ||||
| -		atomic_inc(&bus->dpc_tskcnt); | ||||
| +		bus->dpc_triggered = true; | ||||
|  	} | ||||
|  } | ||||
|   | ||||
| @@ -2940,6 +2942,7 @@ brcmf_sdio_bus_txctl(struct device *dev, | ||||
|  	/* Send from dpc */ | ||||
|  	bus->ctrl_frame_buf = msg; | ||||
|  	bus->ctrl_frame_len = msglen; | ||||
| +	wmb(); | ||||
|  	bus->ctrl_frame_stat = true; | ||||
|   | ||||
|  	brcmf_sdio_trigger_dpc(bus); | ||||
| @@ -2958,6 +2961,7 @@ brcmf_sdio_bus_txctl(struct device *dev, | ||||
|  	if (!ret) { | ||||
|  		brcmf_dbg(SDIO, "ctrl_frame complete, err=%d\n", | ||||
|  			  bus->ctrl_frame_err); | ||||
| +		rmb(); | ||||
|  		ret = bus->ctrl_frame_err; | ||||
|  	} | ||||
|   | ||||
| @@ -3526,8 +3530,8 @@ done: | ||||
|   | ||||
|  void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus) | ||||
|  { | ||||
| -	if (atomic_read(&bus->dpc_tskcnt) == 0) { | ||||
| -		atomic_inc(&bus->dpc_tskcnt); | ||||
| +	if (!bus->dpc_triggered) { | ||||
| +		bus->dpc_triggered = true; | ||||
|  		queue_work(bus->brcmf_wq, &bus->datawork); | ||||
|  	} | ||||
|  } | ||||
| @@ -3558,7 +3562,7 @@ void brcmf_sdio_isr(struct brcmf_sdio *b | ||||
|  	if (!bus->intr) | ||||
|  		brcmf_err("isr w/o interrupt configured!\n"); | ||||
|   | ||||
| -	atomic_inc(&bus->dpc_tskcnt); | ||||
| +	bus->dpc_triggered = true; | ||||
|  	queue_work(bus->brcmf_wq, &bus->datawork); | ||||
|  } | ||||
|   | ||||
| @@ -3578,7 +3582,7 @@ static void brcmf_sdio_bus_watchdog(stru | ||||
|  		if (!bus->intr || | ||||
|  		    (bus->sdcnt.intrcount == bus->sdcnt.lastintrs)) { | ||||
|   | ||||
| -			if (atomic_read(&bus->dpc_tskcnt) == 0) { | ||||
| +			if (!bus->dpc_triggered) { | ||||
|  				u8 devpend; | ||||
|   | ||||
|  				sdio_claim_host(bus->sdiodev->func[1]); | ||||
| @@ -3596,7 +3600,7 @@ static void brcmf_sdio_bus_watchdog(stru | ||||
|  				bus->sdcnt.pollcnt++; | ||||
|  				atomic_set(&bus->ipend, 1); | ||||
|   | ||||
| -				atomic_inc(&bus->dpc_tskcnt); | ||||
| +				bus->dpc_triggered = true; | ||||
|  				queue_work(bus->brcmf_wq, &bus->datawork); | ||||
|  			} | ||||
|  		} | ||||
| @@ -3623,17 +3627,21 @@ static void brcmf_sdio_bus_watchdog(stru | ||||
|  #endif				/* DEBUG */ | ||||
|   | ||||
|  	/* On idle timeout clear activity flag and/or turn off clock */ | ||||
| -	if ((atomic_read(&bus->dpc_tskcnt) == 0) && | ||||
| -	    (atomic_read(&bus->dpc_running) == 0) && | ||||
| -	    (bus->idletime > 0) && (bus->clkstate == CLK_AVAIL)) { | ||||
| -		bus->idlecount++; | ||||
| -		if (bus->idlecount > bus->idletime) { | ||||
| -			brcmf_dbg(SDIO, "idle\n"); | ||||
| -			sdio_claim_host(bus->sdiodev->func[1]); | ||||
| -			brcmf_sdio_wd_timer(bus, 0); | ||||
| +	if (!bus->dpc_triggered) { | ||||
| +		rmb(); | ||||
| +		if ((!bus->dpc_running) && (bus->idletime > 0) && | ||||
| +		    (bus->clkstate == CLK_AVAIL)) { | ||||
| +			bus->idlecount++; | ||||
| +			if (bus->idlecount > bus->idletime) { | ||||
| +				brcmf_dbg(SDIO, "idle\n"); | ||||
| +				sdio_claim_host(bus->sdiodev->func[1]); | ||||
| +				brcmf_sdio_wd_timer(bus, 0); | ||||
| +				bus->idlecount = 0; | ||||
| +				brcmf_sdio_bus_sleep(bus, true, false); | ||||
| +				sdio_release_host(bus->sdiodev->func[1]); | ||||
| +			} | ||||
| +		} else { | ||||
|  			bus->idlecount = 0; | ||||
| -			brcmf_sdio_bus_sleep(bus, true, false); | ||||
| -			sdio_release_host(bus->sdiodev->func[1]); | ||||
|  		} | ||||
|  	} else { | ||||
|  		bus->idlecount = 0; | ||||
| @@ -3645,13 +3653,14 @@ static void brcmf_sdio_dataworker(struct | ||||
|  	struct brcmf_sdio *bus = container_of(work, struct brcmf_sdio, | ||||
|  					      datawork); | ||||
|   | ||||
| -	while (atomic_read(&bus->dpc_tskcnt)) { | ||||
| -		atomic_set(&bus->dpc_running, 1); | ||||
| -		atomic_set(&bus->dpc_tskcnt, 0); | ||||
| +	bus->dpc_running = true; | ||||
| +	wmb(); | ||||
| +	while (ACCESS_ONCE(bus->dpc_triggered)) { | ||||
| +		bus->dpc_triggered = false; | ||||
|  		brcmf_sdio_dpc(bus); | ||||
|  		bus->idlecount = 0; | ||||
| -		atomic_set(&bus->dpc_running, 0); | ||||
|  	} | ||||
| +	bus->dpc_running = false; | ||||
|  	if (brcmf_sdiod_freezing(bus->sdiodev)) { | ||||
|  		brcmf_sdiod_change_state(bus->sdiodev, BRCMF_SDIOD_DOWN); | ||||
|  		brcmf_sdiod_try_freeze(bus->sdiodev); | ||||
| @@ -4144,8 +4153,8 @@ struct brcmf_sdio *brcmf_sdio_probe(stru | ||||
|  		bus->watchdog_tsk = NULL; | ||||
|  	} | ||||
|  	/* Initialize DPC thread */ | ||||
| -	atomic_set(&bus->dpc_tskcnt, 0); | ||||
| -	atomic_set(&bus->dpc_running, 0); | ||||
| +	bus->dpc_triggered = false; | ||||
| +	bus->dpc_running = false; | ||||
|   | ||||
|  	/* Assign bus interface call back */ | ||||
|  	bus->sdiodev->bus_if->dev = bus->sdiodev->dev; | ||||
| @@ -1,26 +0,0 @@ | ||||
| From: Hante Meuleman <meuleman@broadcom.com> | ||||
| Date: Wed, 18 Mar 2015 13:25:24 +0100 | ||||
| Subject: [PATCH] brcmfmac: Remove unnecessary new-line in pcie console | ||||
|  logging. | ||||
|  | ||||
| Reviewed-by: Arend Van Spriel <arend@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Signed-off-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c | ||||
| @@ -651,10 +651,9 @@ static void brcmf_pcie_bus_console_read( | ||||
|  			console->log_str[console->log_idx] = ch; | ||||
|  			console->log_idx++; | ||||
|  		} | ||||
| - | ||||
|  		if (ch == '\n') { | ||||
|  			console->log_str[console->log_idx] = 0; | ||||
| -			brcmf_dbg(PCIE, "CONSOLE: %s\n", console->log_str); | ||||
| +			brcmf_dbg(PCIE, "CONSOLE: %s", console->log_str); | ||||
|  			console->log_idx = 0; | ||||
|  		} | ||||
|  	} | ||||
| @@ -1,26 +0,0 @@ | ||||
| From: Arend van Spriel <arend@broadcom.com> | ||||
| Date: Wed, 18 Mar 2015 13:25:25 +0100 | ||||
| Subject: [PATCH] brcmfmac: add MODULE_FIRMWARE() macros for bcm4356 PCIe | ||||
|  device | ||||
|  | ||||
| The BCM4356 PCIe wireless device was added recently but overlooked | ||||
| the fact that the MODULE_FIRMWARE() macros were missing for the | ||||
| firmwares needed by this device. | ||||
|  | ||||
| Reviewed-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c | ||||
| @@ -189,6 +189,8 @@ MODULE_FIRMWARE(BRCMF_PCIE_43602_FW_NAME | ||||
|  MODULE_FIRMWARE(BRCMF_PCIE_43602_NVRAM_NAME); | ||||
|  MODULE_FIRMWARE(BRCMF_PCIE_4354_FW_NAME); | ||||
|  MODULE_FIRMWARE(BRCMF_PCIE_4354_NVRAM_NAME); | ||||
| +MODULE_FIRMWARE(BRCMF_PCIE_4356_FW_NAME); | ||||
| +MODULE_FIRMWARE(BRCMF_PCIE_4356_NVRAM_NAME); | ||||
|  MODULE_FIRMWARE(BRCMF_PCIE_43570_FW_NAME); | ||||
|  MODULE_FIRMWARE(BRCMF_PCIE_43570_NVRAM_NAME); | ||||
|   | ||||
| @@ -1,138 +0,0 @@ | ||||
| From: Arend van Spriel <arend@broadcom.com> | ||||
| Date: Wed, 18 Mar 2015 13:25:26 +0100 | ||||
| Subject: [PATCH] brcmfmac: add support for BCM43430 SDIO chipset | ||||
|  | ||||
| This patch added support for the BCM43430 802.11n SDIO chipset. | ||||
|  | ||||
| Reviewed-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com> | ||||
| Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | ||||
| @@ -1098,6 +1098,7 @@ static const struct sdio_device_id brcmf | ||||
|  	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43341), | ||||
|  	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43362), | ||||
|  	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4335_4339), | ||||
| +	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43430), | ||||
|  	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4345), | ||||
|  	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354), | ||||
|  	{ /* end: all zeroes */ } | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c | ||||
| @@ -600,6 +600,12 @@ static void brcmf_chip_socram_ramsize(st | ||||
|  		if (sr->chip->pub.chiprev < 2) | ||||
|  			*srsize = (32 * 1024); | ||||
|  		break; | ||||
| +	case BRCM_CC_43430_CHIP_ID: | ||||
| +		/* assume sr for now as we can not check | ||||
| +		 * firmware sr capability at this point. | ||||
| +		 */ | ||||
| +		*srsize = (64 * 1024); | ||||
| +		break; | ||||
|  	default: | ||||
|  		break; | ||||
|  	} | ||||
| @@ -1072,6 +1078,7 @@ static void | ||||
|  brcmf_chip_cm3_set_passive(struct brcmf_chip_priv *chip) | ||||
|  { | ||||
|  	struct brcmf_core *core; | ||||
| +	struct brcmf_core_priv *sr; | ||||
|   | ||||
|  	brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CM3); | ||||
|  	core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211); | ||||
| @@ -1081,6 +1088,13 @@ brcmf_chip_cm3_set_passive(struct brcmf_ | ||||
|  			     D11_BCMA_IOCTL_PHYCLOCKEN); | ||||
|  	core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_INTERNAL_MEM); | ||||
|  	brcmf_chip_resetcore(core, 0, 0, 0); | ||||
| + | ||||
| +	/* disable bank #3 remap for this device */ | ||||
| +	if (chip->pub.chip == BRCM_CC_43430_CHIP_ID) { | ||||
| +		sr = container_of(core, struct brcmf_core_priv, pub); | ||||
| +		brcmf_chip_core_write32(sr, SOCRAMREGOFFS(bankidx), 3); | ||||
| +		brcmf_chip_core_write32(sr, SOCRAMREGOFFS(bankpda), 0); | ||||
| +	} | ||||
|  } | ||||
|   | ||||
|  static bool brcmf_chip_cm3_set_active(struct brcmf_chip_priv *chip) | ||||
| @@ -1188,6 +1202,10 @@ bool brcmf_chip_sr_capable(struct brcmf_ | ||||
|  		addr = CORE_CC_REG(base, chipcontrol_data); | ||||
|  		reg = chip->ops->read32(chip->ctx, addr); | ||||
|  		return (reg & pmu_cc3_mask) != 0; | ||||
| +	case BRCM_CC_43430_CHIP_ID: | ||||
| +		addr = CORE_CC_REG(base, sr_control1); | ||||
| +		reg = chip->ops->read32(chip->ctx, addr); | ||||
| +		return reg != 0; | ||||
|  	default: | ||||
|  		addr = CORE_CC_REG(base, pmucapabilities_ext); | ||||
|  		reg = chip->ops->read32(chip->ctx, addr); | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c | ||||
| @@ -615,6 +615,8 @@ static const struct sdiod_drive_str sdio | ||||
|  #define BCM43362_NVRAM_NAME		"brcm/brcmfmac43362-sdio.txt" | ||||
|  #define BCM4339_FIRMWARE_NAME		"brcm/brcmfmac4339-sdio.bin" | ||||
|  #define BCM4339_NVRAM_NAME		"brcm/brcmfmac4339-sdio.txt" | ||||
| +#define BCM43430_FIRMWARE_NAME		"brcm/brcmfmac43430-sdio.bin" | ||||
| +#define BCM43430_NVRAM_NAME		"brcm/brcmfmac43430-sdio.txt" | ||||
|  #define BCM4345_FIRMWARE_NAME		"brcm/brcmfmac4345-sdio.bin" | ||||
|  #define BCM4345_NVRAM_NAME		"brcm/brcmfmac4345-sdio.txt" | ||||
|  #define BCM4354_FIRMWARE_NAME		"brcm/brcmfmac4354-sdio.bin" | ||||
| @@ -640,6 +642,8 @@ MODULE_FIRMWARE(BCM43362_FIRMWARE_NAME); | ||||
|  MODULE_FIRMWARE(BCM43362_NVRAM_NAME); | ||||
|  MODULE_FIRMWARE(BCM4339_FIRMWARE_NAME); | ||||
|  MODULE_FIRMWARE(BCM4339_NVRAM_NAME); | ||||
| +MODULE_FIRMWARE(BCM43430_FIRMWARE_NAME); | ||||
| +MODULE_FIRMWARE(BCM43430_NVRAM_NAME); | ||||
|  MODULE_FIRMWARE(BCM4345_FIRMWARE_NAME); | ||||
|  MODULE_FIRMWARE(BCM4345_NVRAM_NAME); | ||||
|  MODULE_FIRMWARE(BCM4354_FIRMWARE_NAME); | ||||
| @@ -671,6 +675,7 @@ static const struct brcmf_firmware_names | ||||
|  	{ BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) }, | ||||
|  	{ BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) }, | ||||
|  	{ BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) }, | ||||
| +	{ BRCM_CC_43430_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43430) }, | ||||
|  	{ BRCM_CC_4345_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4345) }, | ||||
|  	{ BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) } | ||||
|  }; | ||||
| --- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h | ||||
| +++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h | ||||
| @@ -37,6 +37,7 @@ | ||||
|  #define BRCM_CC_43362_CHIP_ID		43362 | ||||
|  #define BRCM_CC_4335_CHIP_ID		0x4335 | ||||
|  #define BRCM_CC_4339_CHIP_ID		0x4339 | ||||
| +#define BRCM_CC_43430_CHIP_ID		43430 | ||||
|  #define BRCM_CC_4345_CHIP_ID		0x4345 | ||||
|  #define BRCM_CC_4354_CHIP_ID		0x4354 | ||||
|  #define BRCM_CC_4356_CHIP_ID		0x4356 | ||||
| --- a/drivers/net/wireless/brcm80211/include/chipcommon.h | ||||
| +++ b/drivers/net/wireless/brcm80211/include/chipcommon.h | ||||
| @@ -183,7 +183,14 @@ struct chipcregs { | ||||
|  	u8 uart1lsr; | ||||
|  	u8 uart1msr; | ||||
|  	u8 uart1scratch; | ||||
| -	u32 PAD[126]; | ||||
| +	u32 PAD[62]; | ||||
| + | ||||
| +	/* save/restore, corerev >= 48 */ | ||||
| +	u32 sr_capability;          /* 0x500 */ | ||||
| +	u32 sr_control0;            /* 0x504 */ | ||||
| +	u32 sr_control1;            /* 0x508 */ | ||||
| +	u32 gpio_control;           /* 0x50C */ | ||||
| +	u32 PAD[60]; | ||||
|   | ||||
|  	/* PMU registers (corerev >= 20) */ | ||||
|  	u32 pmucontrol;	/* 0x600 */ | ||||
| --- a/include/linux/mmc/sdio_ids.h | ||||
| +++ b/include/linux/mmc/sdio_ids.h | ||||
| @@ -33,6 +33,7 @@ | ||||
|  #define SDIO_DEVICE_ID_BROADCOM_43341		0xa94d | ||||
|  #define SDIO_DEVICE_ID_BROADCOM_4335_4339	0x4335 | ||||
|  #define SDIO_DEVICE_ID_BROADCOM_43362		0xa962 | ||||
| +#define SDIO_DEVICE_ID_BROADCOM_43430		0xa9a6 | ||||
|  #define SDIO_DEVICE_ID_BROADCOM_4345		0x4345 | ||||
|  #define SDIO_DEVICE_ID_BROADCOM_4354		0x4354 | ||||
|   | ||||
| @@ -1,50 +0,0 @@ | ||||
| From: Arend van Spriel <arend@broadcom.com> | ||||
| Date: Wed, 18 Mar 2015 13:25:27 +0100 | ||||
| Subject: [PATCH] brcmfmac: only support the BCM43455/7 device | ||||
|  | ||||
| Recently support was added for the BCM4345 SDIO chipset by | ||||
| commit 9c51026509d7 ("brcmfmac: Add support for BCM4345 SDIO chipset") | ||||
| however this was verified using a BCM43455 device, which is | ||||
| a more recent revision of the chip. This patch assure that | ||||
| older revisions are not probed as they would fail. | ||||
|  | ||||
| Reviewed-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Reviewed-by: Syed Asifful Dayyan <syedd@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c | ||||
| @@ -617,8 +617,8 @@ static const struct sdiod_drive_str sdio | ||||
|  #define BCM4339_NVRAM_NAME		"brcm/brcmfmac4339-sdio.txt" | ||||
|  #define BCM43430_FIRMWARE_NAME		"brcm/brcmfmac43430-sdio.bin" | ||||
|  #define BCM43430_NVRAM_NAME		"brcm/brcmfmac43430-sdio.txt" | ||||
| -#define BCM4345_FIRMWARE_NAME		"brcm/brcmfmac4345-sdio.bin" | ||||
| -#define BCM4345_NVRAM_NAME		"brcm/brcmfmac4345-sdio.txt" | ||||
| +#define BCM43455_FIRMWARE_NAME		"brcm/brcmfmac43455-sdio.bin" | ||||
| +#define BCM43455_NVRAM_NAME		"brcm/brcmfmac43455-sdio.txt" | ||||
|  #define BCM4354_FIRMWARE_NAME		"brcm/brcmfmac4354-sdio.bin" | ||||
|  #define BCM4354_NVRAM_NAME		"brcm/brcmfmac4354-sdio.txt" | ||||
|   | ||||
| @@ -644,8 +644,8 @@ MODULE_FIRMWARE(BCM4339_FIRMWARE_NAME); | ||||
|  MODULE_FIRMWARE(BCM4339_NVRAM_NAME); | ||||
|  MODULE_FIRMWARE(BCM43430_FIRMWARE_NAME); | ||||
|  MODULE_FIRMWARE(BCM43430_NVRAM_NAME); | ||||
| -MODULE_FIRMWARE(BCM4345_FIRMWARE_NAME); | ||||
| -MODULE_FIRMWARE(BCM4345_NVRAM_NAME); | ||||
| +MODULE_FIRMWARE(BCM43455_FIRMWARE_NAME); | ||||
| +MODULE_FIRMWARE(BCM43455_NVRAM_NAME); | ||||
|  MODULE_FIRMWARE(BCM4354_FIRMWARE_NAME); | ||||
|  MODULE_FIRMWARE(BCM4354_NVRAM_NAME); | ||||
|   | ||||
| @@ -676,7 +676,7 @@ static const struct brcmf_firmware_names | ||||
|  	{ BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) }, | ||||
|  	{ BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) }, | ||||
|  	{ BRCM_CC_43430_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43430) }, | ||||
| -	{ BRCM_CC_4345_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4345) }, | ||||
| +	{ BRCM_CC_4345_CHIP_ID, 0xFFFFFFC0, BRCMF_FIRMWARE_NVRAM(BCM43455) }, | ||||
|  	{ BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) } | ||||
|  }; | ||||
|   | ||||
| @@ -1,52 +0,0 @@ | ||||
| From: Arend van Spriel <arend@broadcom.com> | ||||
| Date: Wed, 18 Mar 2015 13:25:28 +0100 | ||||
| Subject: [PATCH] brcmfmac: remove support for unreleased BCM4354 PCIe | ||||
|  | ||||
| There are no known BCM4354 PCIe devices released so removing | ||||
| support from the driver until proven otherwise. | ||||
|  | ||||
| Reviewed-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c | ||||
| @@ -47,8 +47,6 @@ enum brcmf_pcie_state { | ||||
|   | ||||
|  #define BRCMF_PCIE_43602_FW_NAME		"brcm/brcmfmac43602-pcie.bin" | ||||
|  #define BRCMF_PCIE_43602_NVRAM_NAME		"brcm/brcmfmac43602-pcie.txt" | ||||
| -#define BRCMF_PCIE_4354_FW_NAME			"brcm/brcmfmac4354-pcie.bin" | ||||
| -#define BRCMF_PCIE_4354_NVRAM_NAME		"brcm/brcmfmac4354-pcie.txt" | ||||
|  #define BRCMF_PCIE_4356_FW_NAME			"brcm/brcmfmac4356-pcie.bin" | ||||
|  #define BRCMF_PCIE_4356_NVRAM_NAME		"brcm/brcmfmac4356-pcie.txt" | ||||
|  #define BRCMF_PCIE_43570_FW_NAME		"brcm/brcmfmac43570-pcie.bin" | ||||
| @@ -187,8 +185,6 @@ enum brcmf_pcie_state { | ||||
|   | ||||
|  MODULE_FIRMWARE(BRCMF_PCIE_43602_FW_NAME); | ||||
|  MODULE_FIRMWARE(BRCMF_PCIE_43602_NVRAM_NAME); | ||||
| -MODULE_FIRMWARE(BRCMF_PCIE_4354_FW_NAME); | ||||
| -MODULE_FIRMWARE(BRCMF_PCIE_4354_NVRAM_NAME); | ||||
|  MODULE_FIRMWARE(BRCMF_PCIE_4356_FW_NAME); | ||||
|  MODULE_FIRMWARE(BRCMF_PCIE_4356_NVRAM_NAME); | ||||
|  MODULE_FIRMWARE(BRCMF_PCIE_43570_FW_NAME); | ||||
| @@ -1327,10 +1323,6 @@ static int brcmf_pcie_get_fwnames(struct | ||||
|  		fw_name = BRCMF_PCIE_43602_FW_NAME; | ||||
|  		nvram_name = BRCMF_PCIE_43602_NVRAM_NAME; | ||||
|  		break; | ||||
| -	case BRCM_CC_4354_CHIP_ID: | ||||
| -		fw_name = BRCMF_PCIE_4354_FW_NAME; | ||||
| -		nvram_name = BRCMF_PCIE_4354_NVRAM_NAME; | ||||
| -		break; | ||||
|  	case BRCM_CC_4356_CHIP_ID: | ||||
|  		fw_name = BRCMF_PCIE_4356_FW_NAME; | ||||
|  		nvram_name = BRCMF_PCIE_4356_NVRAM_NAME; | ||||
| @@ -1855,7 +1847,6 @@ cleanup: | ||||
|  	PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 } | ||||
|   | ||||
|  static struct pci_device_id brcmf_pcie_devid_table[] = { | ||||
| -	BRCMF_PCIE_DEVICE(BRCM_PCIE_4354_DEVICE_ID), | ||||
|  	BRCMF_PCIE_DEVICE(BRCM_PCIE_4356_DEVICE_ID), | ||||
|  	BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID), | ||||
|  	BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID), | ||||
| @@ -1,28 +0,0 @@ | ||||
| From: Arend van Spriel <arend@broadcom.com> | ||||
| Date: Fri, 20 Mar 2015 22:18:17 +0100 | ||||
| Subject: [PATCH] brcmfmac: disable MBSS feature for BCM43362 | ||||
|  | ||||
| The BCM43362 firmware falsely reports it is capable of providing | ||||
| MBSS. As a result AP mode no longer works for this device. Therefor | ||||
| disable MBSS in the driver for this chipset. | ||||
|  | ||||
| Cc: stable@vger.kernel.org # 3.19.y | ||||
| Reported-by: Jorg Krause <jkrause@posteo.de> | ||||
| Reviewed-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c | ||||
| @@ -126,7 +126,8 @@ void brcmf_feat_attach(struct brcmf_pub | ||||
|  	brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan"); | ||||
|  	if (drvr->bus_if->wowl_supported) | ||||
|  		brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl"); | ||||
| -	brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0); | ||||
| +	if (drvr->bus_if->chip != BRCM_CC_43362_CHIP_ID) | ||||
| +		brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0); | ||||
|   | ||||
|  	/* set chip related quirks */ | ||||
|  	switch (drvr->bus_if->chip) { | ||||
| @@ -1,300 +0,0 @@ | ||||
| From: Arend van Spriel <arend@broadcom.com> | ||||
| Date: Tue, 14 Apr 2015 20:10:24 +0200 | ||||
| Subject: [PATCH] brcmfmac: use static superset of channels for wiphy | ||||
|  bands | ||||
|  | ||||
| The driver was constructing a list of channels per wiphy band | ||||
| by querying the device. This list is not what the hardware is | ||||
| able to do as it is already filtered by the country setting in | ||||
| the device. As user-space may change the country this would | ||||
| require updating the channel list which is not recommended [1]. | ||||
| This patch introduces a superset of channels. The individual | ||||
| channels are disabled appropriately by querying the device. | ||||
|  | ||||
| [1] http://mid.gmane.org/1426706320.3001.21.camel@sipsolutions.net | ||||
|  | ||||
| Reviewed-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c | ||||
| @@ -129,13 +129,47 @@ static struct ieee80211_rate __wl_rates[ | ||||
|  	RATETAB_ENT(BRCM_RATE_54M, 0), | ||||
|  }; | ||||
|   | ||||
| -#define wl_a_rates		(__wl_rates + 4) | ||||
| -#define wl_a_rates_size	8 | ||||
|  #define wl_g_rates		(__wl_rates + 0) | ||||
| -#define wl_g_rates_size	12 | ||||
| +#define wl_g_rates_size		ARRAY_SIZE(__wl_rates) | ||||
| +#define wl_a_rates		(__wl_rates + 4) | ||||
| +#define wl_a_rates_size		(wl_g_rates_size - 4) | ||||
| + | ||||
| +#define CHAN2G(_channel, _freq) {				\ | ||||
| +	.band			= IEEE80211_BAND_2GHZ,		\ | ||||
| +	.center_freq		= (_freq),			\ | ||||
| +	.hw_value		= (_channel),			\ | ||||
| +	.flags			= IEEE80211_CHAN_DISABLED,	\ | ||||
| +	.max_antenna_gain	= 0,				\ | ||||
| +	.max_power		= 30,				\ | ||||
| +} | ||||
| + | ||||
| +#define CHAN5G(_channel) {					\ | ||||
| +	.band			= IEEE80211_BAND_5GHZ,		\ | ||||
| +	.center_freq		= 5000 + (5 * (_channel)),	\ | ||||
| +	.hw_value		= (_channel),			\ | ||||
| +	.flags			= IEEE80211_CHAN_DISABLED,	\ | ||||
| +	.max_antenna_gain	= 0,				\ | ||||
| +	.max_power		= 30,				\ | ||||
| +} | ||||
| + | ||||
| +static struct ieee80211_channel __wl_2ghz_channels[] = { | ||||
| +	CHAN2G(1, 2412), CHAN2G(2, 2417), CHAN2G(3, 2422), CHAN2G(4, 2427), | ||||
| +	CHAN2G(5, 2432), CHAN2G(6, 2437), CHAN2G(7, 2442), CHAN2G(8, 2447), | ||||
| +	CHAN2G(9, 2452), CHAN2G(10, 2457), CHAN2G(11, 2462), CHAN2G(12, 2467), | ||||
| +	CHAN2G(13, 2472), CHAN2G(14, 2484) | ||||
| +}; | ||||
| + | ||||
| +static struct ieee80211_channel __wl_5ghz_channels[] = { | ||||
| +	CHAN5G(34), CHAN5G(36), CHAN5G(38), CHAN5G(40), CHAN5G(42), | ||||
| +	CHAN5G(44), CHAN5G(46), CHAN5G(48), CHAN5G(52), CHAN5G(56), | ||||
| +	CHAN5G(60), CHAN5G(64), CHAN5G(100), CHAN5G(104), CHAN5G(108), | ||||
| +	CHAN5G(112), CHAN5G(116), CHAN5G(120), CHAN5G(124), CHAN5G(128), | ||||
| +	CHAN5G(132), CHAN5G(136), CHAN5G(140), CHAN5G(144), CHAN5G(149), | ||||
| +	CHAN5G(153), CHAN5G(157), CHAN5G(161), CHAN5G(165) | ||||
| +}; | ||||
|   | ||||
|  /* Band templates duplicated per wiphy. The channel info | ||||
| - * is filled in after querying the device. | ||||
| + * above is added to the band during setup. | ||||
|   */ | ||||
|  static const struct ieee80211_supported_band __wl_band_2ghz = { | ||||
|  	.band = IEEE80211_BAND_2GHZ, | ||||
| @@ -143,7 +177,7 @@ static const struct ieee80211_supported_ | ||||
|  	.n_bitrates = wl_g_rates_size, | ||||
|  }; | ||||
|   | ||||
| -static const struct ieee80211_supported_band __wl_band_5ghz_a = { | ||||
| +static const struct ieee80211_supported_band __wl_band_5ghz = { | ||||
|  	.band = IEEE80211_BAND_5GHZ, | ||||
|  	.bitrates = wl_a_rates, | ||||
|  	.n_bitrates = wl_a_rates_size, | ||||
| @@ -5252,40 +5286,6 @@ dongle_scantime_out: | ||||
|  	return err; | ||||
|  } | ||||
|   | ||||
| -/* Filter the list of channels received from firmware counting only | ||||
| - * the 20MHz channels. The wiphy band data only needs those which get | ||||
| - * flagged to indicate if they can take part in higher bandwidth. | ||||
| - */ | ||||
| -static void brcmf_count_20mhz_channels(struct brcmf_cfg80211_info *cfg, | ||||
| -				       struct brcmf_chanspec_list *chlist, | ||||
| -				       u32 chcnt[]) | ||||
| -{ | ||||
| -	u32 total = le32_to_cpu(chlist->count); | ||||
| -	struct brcmu_chan ch; | ||||
| -	int i; | ||||
| - | ||||
| -	for (i = 0; i < total; i++) { | ||||
| -		ch.chspec = (u16)le32_to_cpu(chlist->element[i]); | ||||
| -		cfg->d11inf.decchspec(&ch); | ||||
| - | ||||
| -		/* Firmware gives a ordered list. We skip non-20MHz | ||||
| -		 * channels is 2G. For 5G we can abort upon reaching | ||||
| -		 * a non-20MHz channel in the list. | ||||
| -		 */ | ||||
| -		if (ch.bw != BRCMU_CHAN_BW_20) { | ||||
| -			if (ch.band == BRCMU_CHAN_BAND_5G) | ||||
| -				break; | ||||
| -			else | ||||
| -				continue; | ||||
| -		} | ||||
| - | ||||
| -		if (ch.band == BRCMU_CHAN_BAND_2G) | ||||
| -			chcnt[0] += 1; | ||||
| -		else if (ch.band == BRCMU_CHAN_BAND_5G) | ||||
| -			chcnt[1] += 1; | ||||
| -	} | ||||
| -} | ||||
| - | ||||
|  static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel, | ||||
|  					   struct brcmu_chan *ch) | ||||
|  { | ||||
| @@ -5321,7 +5321,6 @@ static int brcmf_construct_chaninfo(stru | ||||
|  	u32 i, j; | ||||
|  	u32 total; | ||||
|  	u32 chaninfo; | ||||
| -	u32 chcnt[2] = { 0, 0 }; | ||||
|  	u32 index; | ||||
|   | ||||
|  	pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL); | ||||
| @@ -5338,42 +5337,15 @@ static int brcmf_construct_chaninfo(stru | ||||
|  		goto fail_pbuf; | ||||
|  	} | ||||
|   | ||||
| -	brcmf_count_20mhz_channels(cfg, list, chcnt); | ||||
|  	wiphy = cfg_to_wiphy(cfg); | ||||
| -	if (chcnt[0]) { | ||||
| -		band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz), | ||||
| -			       GFP_KERNEL); | ||||
| -		if (band == NULL) { | ||||
| -			err = -ENOMEM; | ||||
| -			goto fail_pbuf; | ||||
| -		} | ||||
| -		band->channels = kcalloc(chcnt[0], sizeof(*channel), | ||||
| -					 GFP_KERNEL); | ||||
| -		if (band->channels == NULL) { | ||||
| -			kfree(band); | ||||
| -			err = -ENOMEM; | ||||
| -			goto fail_pbuf; | ||||
| -		} | ||||
| -		band->n_channels = 0; | ||||
| -		wiphy->bands[IEEE80211_BAND_2GHZ] = band; | ||||
| -	} | ||||
| -	if (chcnt[1]) { | ||||
| -		band = kmemdup(&__wl_band_5ghz_a, sizeof(__wl_band_5ghz_a), | ||||
| -			       GFP_KERNEL); | ||||
| -		if (band == NULL) { | ||||
| -			err = -ENOMEM; | ||||
| -			goto fail_band2g; | ||||
| -		} | ||||
| -		band->channels = kcalloc(chcnt[1], sizeof(*channel), | ||||
| -					 GFP_KERNEL); | ||||
| -		if (band->channels == NULL) { | ||||
| -			kfree(band); | ||||
| -			err = -ENOMEM; | ||||
| -			goto fail_band2g; | ||||
| -		} | ||||
| -		band->n_channels = 0; | ||||
| -		wiphy->bands[IEEE80211_BAND_5GHZ] = band; | ||||
| -	} | ||||
| +	band = wiphy->bands[IEEE80211_BAND_2GHZ]; | ||||
| +	if (band) | ||||
| +		for (i = 0; i < band->n_channels; i++) | ||||
| +			band->channels[i].flags = IEEE80211_CHAN_DISABLED; | ||||
| +	band = wiphy->bands[IEEE80211_BAND_5GHZ]; | ||||
| +	if (band) | ||||
| +		for (i = 0; i < band->n_channels; i++) | ||||
| +			band->channels[i].flags = IEEE80211_CHAN_DISABLED; | ||||
|   | ||||
|  	total = le32_to_cpu(list->count); | ||||
|  	for (i = 0; i < total; i++) { | ||||
| @@ -5388,6 +5360,8 @@ static int brcmf_construct_chaninfo(stru | ||||
|  			brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec); | ||||
|  			continue; | ||||
|  		} | ||||
| +		if (!band) | ||||
| +			continue; | ||||
|  		if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) && | ||||
|  		    ch.bw == BRCMU_CHAN_BW_40) | ||||
|  			continue; | ||||
| @@ -5415,9 +5389,9 @@ static int brcmf_construct_chaninfo(stru | ||||
|  		} else if (ch.bw == BRCMU_CHAN_BW_40) { | ||||
|  			brcmf_update_bw40_channel_flag(&channel[index], &ch); | ||||
|  		} else { | ||||
| -			/* disable other bandwidths for now as mentioned | ||||
| -			 * order assure they are enabled for subsequent | ||||
| -			 * chanspecs. | ||||
| +			/* enable the channel and disable other bandwidths | ||||
| +			 * for now as mentioned order assure they are enabled | ||||
| +			 * for subsequent chanspecs. | ||||
|  			 */ | ||||
|  			channel[index].flags = IEEE80211_CHAN_NO_HT40 | | ||||
|  					       IEEE80211_CHAN_NO_80MHZ; | ||||
| @@ -5436,16 +5410,8 @@ static int brcmf_construct_chaninfo(stru | ||||
|  						IEEE80211_CHAN_NO_IR; | ||||
|  			} | ||||
|  		} | ||||
| -		if (index == band->n_channels) | ||||
| -			band->n_channels++; | ||||
|  	} | ||||
| -	kfree(pbuf); | ||||
| -	return 0; | ||||
|   | ||||
| -fail_band2g: | ||||
| -	kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels); | ||||
| -	kfree(wiphy->bands[IEEE80211_BAND_2GHZ]); | ||||
| -	wiphy->bands[IEEE80211_BAND_2GHZ] = NULL; | ||||
|  fail_pbuf: | ||||
|  	kfree(pbuf); | ||||
|  	return err; | ||||
| @@ -5778,7 +5744,12 @@ static void brcmf_wiphy_wowl_params(stru | ||||
|   | ||||
|  static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) | ||||
|  { | ||||
| +	struct ieee80211_supported_band *band; | ||||
|  	struct ieee80211_iface_combination ifc_combo; | ||||
| +	__le32 bandlist[3]; | ||||
| +	u32 n_bands; | ||||
| +	int err, i; | ||||
| + | ||||
|  	wiphy->max_scan_ssids = WL_NUM_SCAN_MAX; | ||||
|  	wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX; | ||||
|  	wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX; | ||||
| @@ -5820,7 +5791,52 @@ static int brcmf_setup_wiphy(struct wiph | ||||
|  	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL)) | ||||
|  		brcmf_wiphy_wowl_params(wiphy); | ||||
|   | ||||
| -	return brcmf_setup_wiphybands(wiphy); | ||||
| +	err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist, | ||||
| +				     sizeof(bandlist)); | ||||
| +	if (err) { | ||||
| +		brcmf_err("could not obtain band info: err=%d\n", err); | ||||
| +		return err; | ||||
| +	} | ||||
| +	/* first entry in bandlist is number of bands */ | ||||
| +	n_bands = le32_to_cpu(bandlist[0]); | ||||
| +	for (i = 1; i <= n_bands && i < ARRAY_SIZE(bandlist); i++) { | ||||
| +		if (bandlist[i] == cpu_to_le32(WLC_BAND_2G)) { | ||||
| +			band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz), | ||||
| +				       GFP_KERNEL); | ||||
| +			if (!band) | ||||
| +				return -ENOMEM; | ||||
| + | ||||
| +			band->channels = kmemdup(&__wl_2ghz_channels, | ||||
| +						 sizeof(__wl_2ghz_channels), | ||||
| +						 GFP_KERNEL); | ||||
| +			if (!band->channels) { | ||||
| +				kfree(band); | ||||
| +				return -ENOMEM; | ||||
| +			} | ||||
| + | ||||
| +			band->n_channels = ARRAY_SIZE(__wl_2ghz_channels); | ||||
| +			wiphy->bands[IEEE80211_BAND_2GHZ] = band; | ||||
| +		} | ||||
| +		if (bandlist[i] == cpu_to_le32(WLC_BAND_5G)) { | ||||
| +			band = kmemdup(&__wl_band_5ghz, sizeof(__wl_band_5ghz), | ||||
| +				       GFP_KERNEL); | ||||
| +			if (!band) | ||||
| +				return -ENOMEM; | ||||
| + | ||||
| +			band->channels = kmemdup(&__wl_5ghz_channels, | ||||
| +						 sizeof(__wl_5ghz_channels), | ||||
| +						 GFP_KERNEL); | ||||
| +			if (!band->channels) { | ||||
| +				kfree(band); | ||||
| +				return -ENOMEM; | ||||
| +			} | ||||
| + | ||||
| +			band->n_channels = ARRAY_SIZE(__wl_5ghz_channels); | ||||
| +			wiphy->bands[IEEE80211_BAND_5GHZ] = band; | ||||
| +		} | ||||
| +	} | ||||
| +	err = brcmf_setup_wiphybands(wiphy); | ||||
| +	return err; | ||||
|  } | ||||
|   | ||||
|  static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) | ||||
| @@ -6011,6 +6027,9 @@ static void brcmf_cfg80211_reg_notifier( | ||||
|   | ||||
|  static void brcmf_free_wiphy(struct wiphy *wiphy) | ||||
|  { | ||||
| +	if (!wiphy) | ||||
| +		return; | ||||
| + | ||||
|  	kfree(wiphy->iface_combinations); | ||||
|  	if (wiphy->bands[IEEE80211_BAND_2GHZ]) { | ||||
|  		kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels); | ||||
| @@ -1,29 +0,0 @@ | ||||
| From: Arend van Spriel <arend@broadcom.com> | ||||
| Date: Tue, 14 Apr 2015 20:10:25 +0200 | ||||
| Subject: [PATCH] brcmfmac: update wiphy band information upon updating | ||||
|  regulatory domain | ||||
|  | ||||
| When change the country code the available channels may change. So | ||||
| the wiphy bands should be updated accordingly. | ||||
|  | ||||
| Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com> | ||||
| Reviewed-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c | ||||
| @@ -6022,7 +6022,11 @@ static void brcmf_cfg80211_reg_notifier( | ||||
|  	memset(&ccreq, 0, sizeof(ccreq)); | ||||
|  	ccreq.rev = cpu_to_le32(-1); | ||||
|  	memcpy(ccreq.ccode, req->alpha2, sizeof(req->alpha2)); | ||||
| -	brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq)); | ||||
| +	if (brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq))) { | ||||
| +		brcmf_err("firmware rejected country setting\n"); | ||||
| +		return; | ||||
| +	} | ||||
| +	brcmf_setup_wiphybands(wiphy); | ||||
|  } | ||||
|   | ||||
|  static void brcmf_free_wiphy(struct wiphy *wiphy) | ||||
| @@ -1,24 +0,0 @@ | ||||
| From: Arend van Spriel <arend@broadcom.com> | ||||
| Date: Tue, 14 Apr 2015 20:10:26 +0200 | ||||
| Subject: [PATCH] brcmfmac: add description for feature flags | ||||
|  | ||||
| Some feature flags were not described in the header file. Adding | ||||
| the description. | ||||
|  | ||||
| Reviewed-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.h | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.h | ||||
| @@ -19,7 +19,9 @@ | ||||
|  /* | ||||
|   * Features: | ||||
|   * | ||||
| + * MBSS: multiple BSSID support (eg. guest network in AP mode). | ||||
|   * MCHAN: multi-channel for concurrent P2P. | ||||
| + * WOWL: Wake-On-WLAN. | ||||
|   */ | ||||
|  #define BRCMF_FEAT_LIST \ | ||||
|  	BRCMF_FEAT_DEF(MBSS) \ | ||||
| @@ -1,51 +0,0 @@ | ||||
| From: Arend van Spriel <arend@broadcom.com> | ||||
| Date: Tue, 14 Apr 2015 20:10:27 +0200 | ||||
| Subject: [PATCH] brcmfmac: make scheduled scan support conditional | ||||
|  | ||||
| The scheduled scan support depends on firmware supporting the PNO | ||||
| feature. This feature is optional so add a feature flag for this | ||||
| in the driver and announce scheduled scan support accordingly. | ||||
|  | ||||
| Reviewed-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c | ||||
| @@ -5782,7 +5782,8 @@ static int brcmf_setup_wiphy(struct wiph | ||||
|  		wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; | ||||
|  	wiphy->mgmt_stypes = brcmf_txrx_stypes; | ||||
|  	wiphy->max_remain_on_channel_duration = 5000; | ||||
| -	brcmf_wiphy_pno_params(wiphy); | ||||
| +	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) | ||||
| +		brcmf_wiphy_pno_params(wiphy); | ||||
|   | ||||
|  	/* vendor commands/events support */ | ||||
|  	wiphy->vendor_commands = brcmf_vendor_cmds; | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c | ||||
| @@ -124,6 +124,7 @@ void brcmf_feat_attach(struct brcmf_pub | ||||
|  	struct brcmf_if *ifp = drvr->iflist[0]; | ||||
|   | ||||
|  	brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan"); | ||||
| +	brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PNO, "pfn"); | ||||
|  	if (drvr->bus_if->wowl_supported) | ||||
|  		brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl"); | ||||
|  	if (drvr->bus_if->chip != BRCM_CC_43362_CHIP_ID) | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.h | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.h | ||||
| @@ -21,11 +21,13 @@ | ||||
|   * | ||||
|   * MBSS: multiple BSSID support (eg. guest network in AP mode). | ||||
|   * MCHAN: multi-channel for concurrent P2P. | ||||
| + * PNO: preferred network offload. | ||||
|   * WOWL: Wake-On-WLAN. | ||||
|   */ | ||||
|  #define BRCMF_FEAT_LIST \ | ||||
|  	BRCMF_FEAT_DEF(MBSS) \ | ||||
|  	BRCMF_FEAT_DEF(MCHAN) \ | ||||
| +	BRCMF_FEAT_DEF(PNO) \ | ||||
|  	BRCMF_FEAT_DEF(WOWL) | ||||
|  /* | ||||
|   * Quirks: | ||||
| @@ -1,43 +0,0 @@ | ||||
| From: Arend van Spriel <arend@broadcom.com> | ||||
| Date: Tue, 14 Apr 2015 20:10:28 +0200 | ||||
| Subject: [PATCH] brcmfmac: add support for BCM4324 rev B5 chipset | ||||
|  | ||||
| This patch adds support for the BCM4324 B5 revision. This device | ||||
| is similar to BCM43241 from driver and firmware perspective. It | ||||
| is known to be used in Lenovo Thinkpad Tablet devices. | ||||
|  | ||||
| Reviewed-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c | ||||
| @@ -601,6 +601,8 @@ static const struct sdiod_drive_str sdio | ||||
|  #define BCM43241B0_NVRAM_NAME		"brcm/brcmfmac43241b0-sdio.txt" | ||||
|  #define BCM43241B4_FIRMWARE_NAME	"brcm/brcmfmac43241b4-sdio.bin" | ||||
|  #define BCM43241B4_NVRAM_NAME		"brcm/brcmfmac43241b4-sdio.txt" | ||||
| +#define BCM43241B5_FIRMWARE_NAME	"brcm/brcmfmac43241b5-sdio.bin" | ||||
| +#define BCM43241B5_NVRAM_NAME		"brcm/brcmfmac43241b5-sdio.txt" | ||||
|  #define BCM4329_FIRMWARE_NAME		"brcm/brcmfmac4329-sdio.bin" | ||||
|  #define BCM4329_NVRAM_NAME		"brcm/brcmfmac4329-sdio.txt" | ||||
|  #define BCM4330_FIRMWARE_NAME		"brcm/brcmfmac4330-sdio.bin" | ||||
| @@ -628,6 +630,8 @@ MODULE_FIRMWARE(BCM43241B0_FIRMWARE_NAME | ||||
|  MODULE_FIRMWARE(BCM43241B0_NVRAM_NAME); | ||||
|  MODULE_FIRMWARE(BCM43241B4_FIRMWARE_NAME); | ||||
|  MODULE_FIRMWARE(BCM43241B4_NVRAM_NAME); | ||||
| +MODULE_FIRMWARE(BCM43241B5_FIRMWARE_NAME); | ||||
| +MODULE_FIRMWARE(BCM43241B5_NVRAM_NAME); | ||||
|  MODULE_FIRMWARE(BCM4329_FIRMWARE_NAME); | ||||
|  MODULE_FIRMWARE(BCM4329_NVRAM_NAME); | ||||
|  MODULE_FIRMWARE(BCM4330_FIRMWARE_NAME); | ||||
| @@ -667,7 +671,8 @@ enum brcmf_firmware_type { | ||||
|  static const struct brcmf_firmware_names brcmf_fwname_data[] = { | ||||
|  	{ BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43143) }, | ||||
|  	{ BRCM_CC_43241_CHIP_ID, 0x0000001F, BRCMF_FIRMWARE_NVRAM(BCM43241B0) }, | ||||
| -	{ BRCM_CC_43241_CHIP_ID, 0xFFFFFFE0, BRCMF_FIRMWARE_NVRAM(BCM43241B4) }, | ||||
| +	{ BRCM_CC_43241_CHIP_ID, 0x00000020, BRCMF_FIRMWARE_NVRAM(BCM43241B4) }, | ||||
| +	{ BRCM_CC_43241_CHIP_ID, 0xFFFFFFC0, BRCMF_FIRMWARE_NVRAM(BCM43241B5) }, | ||||
|  	{ BRCM_CC_4329_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4329) }, | ||||
|  	{ BRCM_CC_4330_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4330) }, | ||||
|  	{ BRCM_CC_4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) }, | ||||
| @@ -1,27 +0,0 @@ | ||||
| From: Arend van Spriel <arend@broadcom.com> | ||||
| Date: Tue, 14 Apr 2015 20:10:29 +0200 | ||||
| Subject: [PATCH] brcmfmac: process interrupt regardless sdiod state | ||||
|  | ||||
| When the sdio bus state is not ready to process we abort the | ||||
| interrupt service routine. This is not wanted as it keeps the | ||||
| interrupt source active. Better clear the interrupt source. | ||||
|  | ||||
| Reviewed-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c | ||||
| @@ -3555,10 +3555,6 @@ void brcmf_sdio_isr(struct brcmf_sdio *b | ||||
|  		return; | ||||
|  	} | ||||
|   | ||||
| -	if (bus->sdiodev->state != BRCMF_SDIOD_DATA) { | ||||
| -		brcmf_err("bus is down. we have nothing to do\n"); | ||||
| -		return; | ||||
| -	} | ||||
|  	/* Count the interrupt call */ | ||||
|  	bus->sdcnt.intrcount++; | ||||
|  	if (in_interrupt()) | ||||
| @@ -1,68 +0,0 @@ | ||||
| From: Arend van Spriel <arend@broadcom.com> | ||||
| Date: Tue, 14 Apr 2015 20:10:30 +0200 | ||||
| Subject: [PATCH] brcmfmac: fix sdio suspend and resume | ||||
|  | ||||
| commit 330b4e4be937 ("brcmfmac: Add wowl support for SDIO devices.") | ||||
| changed the behaviour by removing the MMC_PM_KEEP_POWER flag for | ||||
| non-wowl scenario, which needs to be restored. Another necessary | ||||
| change is to mark the card as being non-removable. With this in place | ||||
| the suspend resume test passes successfully doing: | ||||
|  | ||||
|  # echo devices > /sys/power/pm_test | ||||
|  # echo mem > /sys/power/state | ||||
|  | ||||
| Note that power may still be switched off when system is going | ||||
| in S3 state. | ||||
|  | ||||
| Reported-by: Fu, Zhonghui <<zhonghui.fu@linux.intel.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | ||||
| @@ -1011,6 +1011,14 @@ static int brcmf_sdiod_remove(struct brc | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +static void brcmf_sdiod_host_fixup(struct mmc_host *host) | ||||
| +{ | ||||
| +	/* runtime-pm powers off the device */ | ||||
| +	pm_runtime_forbid(host->parent); | ||||
| +	/* avoid removal detection upon resume */ | ||||
| +	host->caps |= MMC_CAP_NONREMOVABLE; | ||||
| +} | ||||
| + | ||||
|  static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev) | ||||
|  { | ||||
|  	struct sdio_func *func; | ||||
| @@ -1076,7 +1084,7 @@ static int brcmf_sdiod_probe(struct brcm | ||||
|  		ret = -ENODEV; | ||||
|  		goto out; | ||||
|  	} | ||||
| -	pm_runtime_forbid(host->parent); | ||||
| +	brcmf_sdiod_host_fixup(host); | ||||
|  out: | ||||
|  	if (ret) | ||||
|  		brcmf_sdiod_remove(sdiodev); | ||||
| @@ -1246,15 +1254,15 @@ static int brcmf_ops_sdio_suspend(struct | ||||
|  	brcmf_sdiod_freezer_on(sdiodev); | ||||
|  	brcmf_sdio_wd_timer(sdiodev->bus, 0); | ||||
|   | ||||
| +	sdio_flags = MMC_PM_KEEP_POWER; | ||||
|  	if (sdiodev->wowl_enabled) { | ||||
| -		sdio_flags = MMC_PM_KEEP_POWER; | ||||
|  		if (sdiodev->pdata->oob_irq_supported) | ||||
|  			enable_irq_wake(sdiodev->pdata->oob_irq_nr); | ||||
|  		else | ||||
| -			sdio_flags = MMC_PM_WAKE_SDIO_IRQ; | ||||
| -		if (sdio_set_host_pm_flags(sdiodev->func[1], sdio_flags)) | ||||
| -			brcmf_err("Failed to set pm_flags %x\n", sdio_flags); | ||||
| +			sdio_flags |= MMC_PM_WAKE_SDIO_IRQ; | ||||
|  	} | ||||
| +	if (sdio_set_host_pm_flags(sdiodev->func[1], sdio_flags)) | ||||
| +		brcmf_err("Failed to set pm_flags %x\n", sdio_flags); | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| @@ -1,77 +0,0 @@ | ||||
| From: Arend van Spriel <arend@broadcom.com> | ||||
| Date: Tue, 14 Apr 2015 20:10:31 +0200 | ||||
| Subject: [PATCH] brcmfmac: add support for BCM4358 PCIe device | ||||
|  | ||||
| This patch adds support for the BCM4358 2x2 11ac device. | ||||
|  | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c | ||||
| @@ -649,6 +649,7 @@ static u32 brcmf_chip_tcm_rambase(struct | ||||
|  	case BRCM_CC_43567_CHIP_ID: | ||||
|  	case BRCM_CC_43569_CHIP_ID: | ||||
|  	case BRCM_CC_43570_CHIP_ID: | ||||
| +	case BRCM_CC_4358_CHIP_ID: | ||||
|  	case BRCM_CC_43602_CHIP_ID: | ||||
|  		return 0x180000; | ||||
|  	default: | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c | ||||
| @@ -51,6 +51,8 @@ enum brcmf_pcie_state { | ||||
|  #define BRCMF_PCIE_4356_NVRAM_NAME		"brcm/brcmfmac4356-pcie.txt" | ||||
|  #define BRCMF_PCIE_43570_FW_NAME		"brcm/brcmfmac43570-pcie.bin" | ||||
|  #define BRCMF_PCIE_43570_NVRAM_NAME		"brcm/brcmfmac43570-pcie.txt" | ||||
| +#define BRCMF_PCIE_4358_FW_NAME			"brcm/brcmfmac4358-pcie.bin" | ||||
| +#define BRCMF_PCIE_4358_NVRAM_NAME		"brcm/brcmfmac4358-pcie.txt" | ||||
|   | ||||
|  #define BRCMF_PCIE_FW_UP_TIMEOUT		2000 /* msec */ | ||||
|   | ||||
| @@ -189,6 +191,8 @@ MODULE_FIRMWARE(BRCMF_PCIE_4356_FW_NAME) | ||||
|  MODULE_FIRMWARE(BRCMF_PCIE_4356_NVRAM_NAME); | ||||
|  MODULE_FIRMWARE(BRCMF_PCIE_43570_FW_NAME); | ||||
|  MODULE_FIRMWARE(BRCMF_PCIE_43570_NVRAM_NAME); | ||||
| +MODULE_FIRMWARE(BRCMF_PCIE_4358_FW_NAME); | ||||
| +MODULE_FIRMWARE(BRCMF_PCIE_4358_NVRAM_NAME); | ||||
|   | ||||
|   | ||||
|  struct brcmf_pcie_console { | ||||
| @@ -1333,6 +1337,10 @@ static int brcmf_pcie_get_fwnames(struct | ||||
|  		fw_name = BRCMF_PCIE_43570_FW_NAME; | ||||
|  		nvram_name = BRCMF_PCIE_43570_NVRAM_NAME; | ||||
|  		break; | ||||
| +	case BRCM_CC_4358_CHIP_ID: | ||||
| +		fw_name = BRCMF_PCIE_4358_FW_NAME; | ||||
| +		nvram_name = BRCMF_PCIE_4358_NVRAM_NAME; | ||||
| +		break; | ||||
|  	default: | ||||
|  		brcmf_err("Unsupported chip 0x%04x\n", devinfo->ci->chip); | ||||
|  		return -ENODEV; | ||||
| @@ -1850,6 +1858,7 @@ static struct pci_device_id brcmf_pcie_d | ||||
|  	BRCMF_PCIE_DEVICE(BRCM_PCIE_4356_DEVICE_ID), | ||||
|  	BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID), | ||||
|  	BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID), | ||||
| +	BRCMF_PCIE_DEVICE(BRCM_PCIE_4358_DEVICE_ID), | ||||
|  	BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_DEVICE_ID), | ||||
|  	BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_2G_DEVICE_ID), | ||||
|  	BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_5G_DEVICE_ID), | ||||
| --- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h | ||||
| +++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h | ||||
| @@ -45,6 +45,7 @@ | ||||
|  #define BRCM_CC_43567_CHIP_ID		43567 | ||||
|  #define BRCM_CC_43569_CHIP_ID		43569 | ||||
|  #define BRCM_CC_43570_CHIP_ID		43570 | ||||
| +#define BRCM_CC_4358_CHIP_ID		0x4358 | ||||
|  #define BRCM_CC_43602_CHIP_ID		43602 | ||||
|   | ||||
|  /* USB Device IDs */ | ||||
| @@ -59,6 +60,7 @@ | ||||
|  #define BRCM_PCIE_4356_DEVICE_ID	0x43ec | ||||
|  #define BRCM_PCIE_43567_DEVICE_ID	0x43d3 | ||||
|  #define BRCM_PCIE_43570_DEVICE_ID	0x43d9 | ||||
| +#define BRCM_PCIE_4358_DEVICE_ID	0x43e9 | ||||
|  #define BRCM_PCIE_43602_DEVICE_ID	0x43ba | ||||
|  #define BRCM_PCIE_43602_2G_DEVICE_ID	0x43bb | ||||
|  #define BRCM_PCIE_43602_5G_DEVICE_ID	0x43bc | ||||
| @@ -1,30 +0,0 @@ | ||||
| From: Hante Meuleman <meuleman@broadcom.com> | ||||
| Date: Tue, 14 Apr 2015 20:10:32 +0200 | ||||
| Subject: [PATCH] brcmfmac: add additional 43602 pcie device id. | ||||
|  | ||||
| Reviewed-by: Arend Van Spriel <arend@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Signed-off-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c | ||||
| @@ -1862,6 +1862,7 @@ static struct pci_device_id brcmf_pcie_d | ||||
|  	BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_DEVICE_ID), | ||||
|  	BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_2G_DEVICE_ID), | ||||
|  	BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_5G_DEVICE_ID), | ||||
| +	BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_RAW_DEVICE_ID), | ||||
|  	{ /* end: all zeroes */ } | ||||
|  }; | ||||
|   | ||||
| --- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h | ||||
| +++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h | ||||
| @@ -64,6 +64,7 @@ | ||||
|  #define BRCM_PCIE_43602_DEVICE_ID	0x43ba | ||||
|  #define BRCM_PCIE_43602_2G_DEVICE_ID	0x43bb | ||||
|  #define BRCM_PCIE_43602_5G_DEVICE_ID	0x43bc | ||||
| +#define BRCM_PCIE_43602_RAW_DEVICE_ID	43602 | ||||
|   | ||||
|  /* brcmsmac IDs */ | ||||
|  #define BCM4313_D11N2G_ID	0x4727	/* 4313 802.11n 2.4G device */ | ||||
| @@ -1,351 +0,0 @@ | ||||
| From: Hante Meuleman <meuleman@broadcom.com> | ||||
| Date: Tue, 14 Apr 2015 20:10:33 +0200 | ||||
| Subject: [PATCH] brcmfmac: Add support for multiple PCIE devices in | ||||
|  nvram. | ||||
|  | ||||
| With PCIE it is possible to support multiple devices with the | ||||
| same device type. They all load the same nvram file. In order to | ||||
| support this the nvram can specify which part of the nvram is | ||||
| for which pcie device. This patch adds support for these new | ||||
| types of nvram files. | ||||
|  | ||||
| Reviewed-by: Arend Van Spriel <arend@broadcom.com> | ||||
| Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com> | ||||
| Signed-off-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c | ||||
| @@ -23,6 +23,10 @@ | ||||
|  #include "debug.h" | ||||
|  #include "firmware.h" | ||||
|   | ||||
| +#define BRCMF_FW_MAX_NVRAM_SIZE			64000 | ||||
| +#define BRCMF_FW_NVRAM_DEVPATH_LEN		19	/* devpath0=pcie/1/4/ */ | ||||
| +#define BRCMF_FW_NVRAM_PCIEDEV_LEN		9	/* pcie/1/4/ */ | ||||
| + | ||||
|  char brcmf_firmware_path[BRCMF_FW_PATH_LEN]; | ||||
|  module_param_string(firmware_path, brcmf_firmware_path, | ||||
|  		    BRCMF_FW_PATH_LEN, 0440); | ||||
| @@ -46,6 +50,8 @@ enum nvram_parser_state { | ||||
|   * @column: current column in line. | ||||
|   * @pos: byte offset in input buffer. | ||||
|   * @entry: start position of key,value entry. | ||||
| + * @multi_dev_v1: detect pcie multi device v1 (compressed). | ||||
| + * @multi_dev_v2: detect pcie multi device v2. | ||||
|   */ | ||||
|  struct nvram_parser { | ||||
|  	enum nvram_parser_state state; | ||||
| @@ -56,6 +62,8 @@ struct nvram_parser { | ||||
|  	u32 column; | ||||
|  	u32 pos; | ||||
|  	u32 entry; | ||||
| +	bool multi_dev_v1; | ||||
| +	bool multi_dev_v2; | ||||
|  }; | ||||
|   | ||||
|  static bool is_nvram_char(char c) | ||||
| @@ -108,6 +116,10 @@ static enum nvram_parser_state brcmf_nvr | ||||
|  			st = COMMENT; | ||||
|  		else | ||||
|  			st = VALUE; | ||||
| +		if (strncmp(&nvp->fwnv->data[nvp->entry], "devpath", 7) == 0) | ||||
| +			nvp->multi_dev_v1 = true; | ||||
| +		if (strncmp(&nvp->fwnv->data[nvp->entry], "pcie/", 5) == 0) | ||||
| +			nvp->multi_dev_v2 = true; | ||||
|  	} else if (!is_nvram_char(c)) { | ||||
|  		brcmf_dbg(INFO, "warning: ln=%d:col=%d: '=' expected, skip invalid key entry\n", | ||||
|  			  nvp->line, nvp->column); | ||||
| @@ -133,6 +145,8 @@ brcmf_nvram_handle_value(struct nvram_pa | ||||
|  		ekv = (u8 *)&nvp->fwnv->data[nvp->pos]; | ||||
|  		skv = (u8 *)&nvp->fwnv->data[nvp->entry]; | ||||
|  		cplen = ekv - skv; | ||||
| +		if (nvp->nvram_len + cplen + 1 >= BRCMF_FW_MAX_NVRAM_SIZE) | ||||
| +			return END; | ||||
|  		/* copy to output buffer */ | ||||
|  		memcpy(&nvp->nvram[nvp->nvram_len], skv, cplen); | ||||
|  		nvp->nvram_len += cplen; | ||||
| @@ -180,10 +194,18 @@ static enum nvram_parser_state | ||||
|  static int brcmf_init_nvram_parser(struct nvram_parser *nvp, | ||||
|  				   const struct firmware *nv) | ||||
|  { | ||||
| +	size_t size; | ||||
| + | ||||
|  	memset(nvp, 0, sizeof(*nvp)); | ||||
|  	nvp->fwnv = nv; | ||||
| +	/* Limit size to MAX_NVRAM_SIZE, some files contain lot of comment */ | ||||
| +	if (nv->size > BRCMF_FW_MAX_NVRAM_SIZE) | ||||
| +		size = BRCMF_FW_MAX_NVRAM_SIZE; | ||||
| +	else | ||||
| +		size = nv->size; | ||||
|  	/* Alloc for extra 0 byte + roundup by 4 + length field */ | ||||
| -	nvp->nvram = kzalloc(nv->size + 1 + 3 + sizeof(u32), GFP_KERNEL); | ||||
| +	size += 1 + 3 + sizeof(u32); | ||||
| +	nvp->nvram = kzalloc(size, GFP_KERNEL); | ||||
|  	if (!nvp->nvram) | ||||
|  		return -ENOMEM; | ||||
|   | ||||
| @@ -192,12 +214,136 @@ static int brcmf_init_nvram_parser(struc | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| +/* brcmf_fw_strip_multi_v1 :Some nvram files contain settings for multiple | ||||
| + * devices. Strip it down for one device, use domain_nr/bus_nr to determine | ||||
| + * which data is to be returned. v1 is the version where nvram is stored | ||||
| + * compressed and "devpath" maps to index for valid entries. | ||||
| + */ | ||||
| +static void brcmf_fw_strip_multi_v1(struct nvram_parser *nvp, u16 domain_nr, | ||||
| +				    u16 bus_nr) | ||||
| +{ | ||||
| +	u32 i, j; | ||||
| +	bool found; | ||||
| +	u8 *nvram; | ||||
| +	u8 id; | ||||
| + | ||||
| +	nvram = kzalloc(nvp->nvram_len + 1 + 3 + sizeof(u32), GFP_KERNEL); | ||||
| +	if (!nvram) | ||||
| +		goto fail; | ||||
| + | ||||
| +	/* min length: devpath0=pcie/1/4/ + 0:x=y */ | ||||
| +	if (nvp->nvram_len < BRCMF_FW_NVRAM_DEVPATH_LEN + 6) | ||||
| +		goto fail; | ||||
| + | ||||
| +	/* First search for the devpathX and see if it is the configuration | ||||
| +	 * for domain_nr/bus_nr. Search complete nvp | ||||
| +	 */ | ||||
| +	found = false; | ||||
| +	i = 0; | ||||
| +	while (i < nvp->nvram_len - BRCMF_FW_NVRAM_DEVPATH_LEN) { | ||||
| +		/* Format: devpathX=pcie/Y/Z/ | ||||
| +		 * Y = domain_nr, Z = bus_nr, X = virtual ID | ||||
| +		 */ | ||||
| +		if ((strncmp(&nvp->nvram[i], "devpath", 7) == 0) && | ||||
| +		    (strncmp(&nvp->nvram[i + 8], "=pcie/", 6) == 0)) { | ||||
| +			if (((nvp->nvram[i + 14] - '0') == domain_nr) && | ||||
| +			    ((nvp->nvram[i + 16] - '0') == bus_nr)) { | ||||
| +				id = nvp->nvram[i + 7] - '0'; | ||||
| +				found = true; | ||||
| +				break; | ||||
| +			} | ||||
| +		} | ||||
| +		while (nvp->nvram[i] != 0) | ||||
| +			i++; | ||||
| +		i++; | ||||
| +	} | ||||
| +	if (!found) | ||||
| +		goto fail; | ||||
| + | ||||
| +	/* Now copy all valid entries, release old nvram and assign new one */ | ||||
| +	i = 0; | ||||
| +	j = 0; | ||||
| +	while (i < nvp->nvram_len) { | ||||
| +		if ((nvp->nvram[i] - '0' == id) && (nvp->nvram[i + 1] == ':')) { | ||||
| +			i += 2; | ||||
| +			while (nvp->nvram[i] != 0) { | ||||
| +				nvram[j] = nvp->nvram[i]; | ||||
| +				i++; | ||||
| +				j++; | ||||
| +			} | ||||
| +			nvram[j] = 0; | ||||
| +			j++; | ||||
| +		} | ||||
| +		while (nvp->nvram[i] != 0) | ||||
| +			i++; | ||||
| +		i++; | ||||
| +	} | ||||
| +	kfree(nvp->nvram); | ||||
| +	nvp->nvram = nvram; | ||||
| +	nvp->nvram_len = j; | ||||
| +	return; | ||||
| + | ||||
| +fail: | ||||
| +	kfree(nvram); | ||||
| +	nvp->nvram_len = 0; | ||||
| +} | ||||
| + | ||||
| +/* brcmf_fw_strip_multi_v2 :Some nvram files contain settings for multiple | ||||
| + * devices. Strip it down for one device, use domain_nr/bus_nr to determine | ||||
| + * which data is to be returned. v2 is the version where nvram is stored | ||||
| + * uncompressed, all relevant valid entries are identified by | ||||
| + * pcie/domain_nr/bus_nr: | ||||
| + */ | ||||
| +static void brcmf_fw_strip_multi_v2(struct nvram_parser *nvp, u16 domain_nr, | ||||
| +				    u16 bus_nr) | ||||
| +{ | ||||
| +	u32 i, j; | ||||
| +	u8 *nvram; | ||||
| + | ||||
| +	nvram = kzalloc(nvp->nvram_len + 1 + 3 + sizeof(u32), GFP_KERNEL); | ||||
| +	if (!nvram) | ||||
| +		goto fail; | ||||
| + | ||||
| +	/* Copy all valid entries, release old nvram and assign new one. | ||||
| +	 * Valid entries are of type pcie/X/Y/ where X = domain_nr and | ||||
| +	 * Y = bus_nr. | ||||
| +	 */ | ||||
| +	i = 0; | ||||
| +	j = 0; | ||||
| +	while (i < nvp->nvram_len - BRCMF_FW_NVRAM_PCIEDEV_LEN) { | ||||
| +		if ((strncmp(&nvp->nvram[i], "pcie/", 5) == 0) && | ||||
| +		    (nvp->nvram[i + 6] == '/') && (nvp->nvram[i + 8] == '/') && | ||||
| +		    ((nvp->nvram[i + 5] - '0') == domain_nr) && | ||||
| +		    ((nvp->nvram[i + 7] - '0') == bus_nr)) { | ||||
| +			i += BRCMF_FW_NVRAM_PCIEDEV_LEN; | ||||
| +			while (nvp->nvram[i] != 0) { | ||||
| +				nvram[j] = nvp->nvram[i]; | ||||
| +				i++; | ||||
| +				j++; | ||||
| +			} | ||||
| +			nvram[j] = 0; | ||||
| +			j++; | ||||
| +		} | ||||
| +		while (nvp->nvram[i] != 0) | ||||
| +			i++; | ||||
| +		i++; | ||||
| +	} | ||||
| +	kfree(nvp->nvram); | ||||
| +	nvp->nvram = nvram; | ||||
| +	nvp->nvram_len = j; | ||||
| +	return; | ||||
| +fail: | ||||
| +	kfree(nvram); | ||||
| +	nvp->nvram_len = 0; | ||||
| +} | ||||
| + | ||||
|  /* brcmf_nvram_strip :Takes a buffer of "<var>=<value>\n" lines read from a fil | ||||
|   * and ending in a NUL. Removes carriage returns, empty lines, comment lines, | ||||
|   * and converts newlines to NULs. Shortens buffer as needed and pads with NULs. | ||||
|   * End of buffer is completed with token identifying length of buffer. | ||||
|   */ | ||||
| -static void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length) | ||||
| +static void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length, | ||||
| +				  u16 domain_nr, u16 bus_nr) | ||||
|  { | ||||
|  	struct nvram_parser nvp; | ||||
|  	u32 pad; | ||||
| @@ -212,6 +358,16 @@ static void *brcmf_fw_nvram_strip(const | ||||
|  		if (nvp.state == END) | ||||
|  			break; | ||||
|  	} | ||||
| +	if (nvp.multi_dev_v1) | ||||
| +		brcmf_fw_strip_multi_v1(&nvp, domain_nr, bus_nr); | ||||
| +	else if (nvp.multi_dev_v2) | ||||
| +		brcmf_fw_strip_multi_v2(&nvp, domain_nr, bus_nr); | ||||
| + | ||||
| +	if (nvp.nvram_len == 0) { | ||||
| +		kfree(nvp.nvram); | ||||
| +		return NULL; | ||||
| +	} | ||||
| + | ||||
|  	pad = nvp.nvram_len; | ||||
|  	*new_length = roundup(nvp.nvram_len + 1, 4); | ||||
|  	while (pad != *new_length) { | ||||
| @@ -239,6 +395,8 @@ struct brcmf_fw { | ||||
|  	u16 flags; | ||||
|  	const struct firmware *code; | ||||
|  	const char *nvram_name; | ||||
| +	u16 domain_nr; | ||||
| +	u16 bus_nr; | ||||
|  	void (*done)(struct device *dev, const struct firmware *fw, | ||||
|  		     void *nvram_image, u32 nvram_len); | ||||
|  }; | ||||
| @@ -254,7 +412,8 @@ static void brcmf_fw_request_nvram_done( | ||||
|  		goto fail; | ||||
|   | ||||
|  	if (fw) { | ||||
| -		nvram = brcmf_fw_nvram_strip(fw, &nvram_length); | ||||
| +		nvram = brcmf_fw_nvram_strip(fw, &nvram_length, | ||||
| +					     fwctx->domain_nr, fwctx->bus_nr); | ||||
|  		release_firmware(fw); | ||||
|  		if (!nvram && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL)) | ||||
|  			goto fail; | ||||
| @@ -309,11 +468,12 @@ fail: | ||||
|  	kfree(fwctx); | ||||
|  } | ||||
|   | ||||
| -int brcmf_fw_get_firmwares(struct device *dev, u16 flags, | ||||
| -			   const char *code, const char *nvram, | ||||
| -			   void (*fw_cb)(struct device *dev, | ||||
| -					 const struct firmware *fw, | ||||
| -					 void *nvram_image, u32 nvram_len)) | ||||
| +int brcmf_fw_get_firmwares_pcie(struct device *dev, u16 flags, | ||||
| +				const char *code, const char *nvram, | ||||
| +				void (*fw_cb)(struct device *dev, | ||||
| +					      const struct firmware *fw, | ||||
| +					      void *nvram_image, u32 nvram_len), | ||||
| +				u16 domain_nr, u16 bus_nr) | ||||
|  { | ||||
|  	struct brcmf_fw *fwctx; | ||||
|   | ||||
| @@ -333,8 +493,21 @@ int brcmf_fw_get_firmwares(struct device | ||||
|  	fwctx->done = fw_cb; | ||||
|  	if (flags & BRCMF_FW_REQUEST_NVRAM) | ||||
|  		fwctx->nvram_name = nvram; | ||||
| +	fwctx->domain_nr = domain_nr; | ||||
| +	fwctx->bus_nr = bus_nr; | ||||
|   | ||||
|  	return request_firmware_nowait(THIS_MODULE, true, code, dev, | ||||
|  				       GFP_KERNEL, fwctx, | ||||
|  				       brcmf_fw_request_code_done); | ||||
|  } | ||||
| + | ||||
| +int brcmf_fw_get_firmwares(struct device *dev, u16 flags, | ||||
| +			   const char *code, const char *nvram, | ||||
| +			   void (*fw_cb)(struct device *dev, | ||||
| +					 const struct firmware *fw, | ||||
| +					 void *nvram_image, u32 nvram_len)) | ||||
| +{ | ||||
| +	return brcmf_fw_get_firmwares_pcie(dev, flags, code, nvram, fw_cb, 0, | ||||
| +					   0); | ||||
| +} | ||||
| + | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.h | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.h | ||||
| @@ -32,6 +32,12 @@ void brcmf_fw_nvram_free(void *nvram); | ||||
|   * fails it will not use the callback, but call device_release_driver() | ||||
|   * instead which will call the driver .remove() callback. | ||||
|   */ | ||||
| +int brcmf_fw_get_firmwares_pcie(struct device *dev, u16 flags, | ||||
| +				const char *code, const char *nvram, | ||||
| +				void (*fw_cb)(struct device *dev, | ||||
| +					      const struct firmware *fw, | ||||
| +					      void *nvram_image, u32 nvram_len), | ||||
| +				u16 domain_nr, u16 bus_nr); | ||||
|  int brcmf_fw_get_firmwares(struct device *dev, u16 flags, | ||||
|  			   const char *code, const char *nvram, | ||||
|  			   void (*fw_cb)(struct device *dev, | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c | ||||
| @@ -1649,8 +1649,13 @@ brcmf_pcie_probe(struct pci_dev *pdev, c | ||||
|  	struct brcmf_pciedev_info *devinfo; | ||||
|  	struct brcmf_pciedev *pcie_bus_dev; | ||||
|  	struct brcmf_bus *bus; | ||||
| +	u16 domain_nr; | ||||
| +	u16 bus_nr; | ||||
|   | ||||
| -	brcmf_dbg(PCIE, "Enter %x:%x\n", pdev->vendor, pdev->device); | ||||
| +	domain_nr = pci_domain_nr(pdev->bus) + 1; | ||||
| +	bus_nr = pdev->bus->number; | ||||
| +	brcmf_dbg(PCIE, "Enter %x:%x (%d/%d)\n", pdev->vendor, pdev->device, | ||||
| +		  domain_nr, bus_nr); | ||||
|   | ||||
|  	ret = -ENOMEM; | ||||
|  	devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL); | ||||
| @@ -1699,10 +1704,10 @@ brcmf_pcie_probe(struct pci_dev *pdev, c | ||||
|  	if (ret) | ||||
|  		goto fail_bus; | ||||
|   | ||||
| -	ret = brcmf_fw_get_firmwares(bus->dev, BRCMF_FW_REQUEST_NVRAM | | ||||
| -					       BRCMF_FW_REQ_NV_OPTIONAL, | ||||
| -				     devinfo->fw_name, devinfo->nvram_name, | ||||
| -				     brcmf_pcie_setup); | ||||
| +	ret = brcmf_fw_get_firmwares_pcie(bus->dev, BRCMF_FW_REQUEST_NVRAM | | ||||
| +						    BRCMF_FW_REQ_NV_OPTIONAL, | ||||
| +					  devinfo->fw_name, devinfo->nvram_name, | ||||
| +					  brcmf_pcie_setup, domain_nr, bus_nr); | ||||
|  	if (ret == 0) | ||||
|  		return 0; | ||||
|  fail_bus: | ||||
| @@ -1,23 +0,0 @@ | ||||
| From: Dan Carpenter <dan.carpenter@oracle.com> | ||||
| Date: Thu, 7 May 2015 12:59:19 +0300 | ||||
| Subject: [PATCH] brcmfmac: cleanup a sizeof() | ||||
|  | ||||
| "flowrings" and "*flowrings" are both pointers so this always returns | ||||
| sizeof(void *) and the current code works fine.  But "*flowrings" is | ||||
| intended here and static checkers complain, so lets change it. | ||||
|  | ||||
| Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c | ||||
| @@ -1617,7 +1617,7 @@ static void brcmf_pcie_setup(struct devi | ||||
|  		bus->msgbuf->commonrings[i] = | ||||
|  				&devinfo->shared.commonrings[i]->commonring; | ||||
|   | ||||
| -	flowrings = kcalloc(devinfo->shared.nrof_flowrings, sizeof(flowrings), | ||||
| +	flowrings = kcalloc(devinfo->shared.nrof_flowrings, sizeof(*flowrings), | ||||
|  			    GFP_KERNEL); | ||||
|  	if (!flowrings) | ||||
|  		goto fail; | ||||
| @@ -1,33 +0,0 @@ | ||||
| From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com> | ||||
| Date: Thu, 7 May 2015 14:13:03 +0200 | ||||
| Subject: [PATCH] brcmfmac: check result of USB firmware request | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
|  | ||||
| This prevents silence failures with driver waiting (infinitely) for a | ||||
| callback. | ||||
|  | ||||
| Signed-off-by: Rafał Miłecki <zajec5@gmail.com> | ||||
| Acked-by: Arend van Spriel <arend@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c | ||||
| @@ -1270,8 +1270,13 @@ static int brcmf_usb_probe_cb(struct brc | ||||
|  	bus->chiprev = bus_pub->chiprev; | ||||
|   | ||||
|  	/* request firmware here */ | ||||
| -	brcmf_fw_get_firmwares(dev, 0, brcmf_usb_get_fwname(devinfo), NULL, | ||||
| -			       brcmf_usb_probe_phase2); | ||||
| +	ret = brcmf_fw_get_firmwares(dev, 0, brcmf_usb_get_fwname(devinfo), | ||||
| +				     NULL, brcmf_usb_probe_phase2); | ||||
| +	if (ret) { | ||||
| +		brcmf_err("firmware request failed: %d\n", ret); | ||||
| +		goto fail; | ||||
| +	} | ||||
| + | ||||
|  	return 0; | ||||
|   | ||||
|  fail: | ||||
| @@ -1,47 +0,0 @@ | ||||
| From: "Fu, Zhonghui" <zhonghui.fu@linux.intel.com> | ||||
| Date: Mon, 11 May 2015 10:41:32 +0800 | ||||
| Subject: [PATCH] brcmfmac: prohibit ACPI power management for brcmfmac driver | ||||
|  | ||||
| ACPI will manage WiFi chip's power state during suspend/resume | ||||
| process on some tablet platforms(such as ASUS T100TA). This is | ||||
| not supported by brcmfmac driver now, and the context of WiFi | ||||
| chip will be damaged after resume. This patch informs ACPI not | ||||
| to manage WiFi chip's power state. | ||||
|  | ||||
| Signed-off-by: Zhonghui Fu <zhonghui.fu@linux.intel.com> | ||||
| Acked-by: Arend van Spriel <arend@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | ||||
| @@ -33,6 +33,7 @@ | ||||
|  #include <linux/suspend.h> | ||||
|  #include <linux/errno.h> | ||||
|  #include <linux/module.h> | ||||
| +#include <linux/acpi.h> | ||||
|  #include <net/cfg80211.h> | ||||
|   | ||||
|  #include <defs.h> | ||||
| @@ -1122,6 +1123,8 @@ static int brcmf_ops_sdio_probe(struct s | ||||
|  	int err; | ||||
|  	struct brcmf_sdio_dev *sdiodev; | ||||
|  	struct brcmf_bus *bus_if; | ||||
| +	struct device *dev; | ||||
| +	struct acpi_device *adev; | ||||
|   | ||||
|  	brcmf_dbg(SDIO, "Enter\n"); | ||||
|  	brcmf_dbg(SDIO, "Class=%x\n", func->class); | ||||
| @@ -1129,6 +1132,12 @@ static int brcmf_ops_sdio_probe(struct s | ||||
|  	brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device); | ||||
|  	brcmf_dbg(SDIO, "Function#: %d\n", func->num); | ||||
|   | ||||
| +	/* prohibit ACPI power management for this device */ | ||||
| +	dev = &func->dev; | ||||
| +	adev = ACPI_COMPANION(dev); | ||||
| +	if (adev) | ||||
| +		adev->flags.power_manageable = 0; | ||||
| + | ||||
|  	/* Consume func num 1 but dont do anything with it. */ | ||||
|  	if (func->num == 1) | ||||
|  		return 0; | ||||
| @@ -1,30 +0,0 @@ | ||||
| From: Arnd Bergmann <arnd@arndb.de> | ||||
| Date: Tue, 12 May 2015 23:54:25 +0200 | ||||
| Subject: [PATCH] brcmfmac: avoid gcc-5.1 warning | ||||
|  | ||||
| gcc-5.0 gained a new warning in the fwsignal portion of the brcmfmac | ||||
| driver: | ||||
|  | ||||
| drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c: In function 'brcmf_fws_txs_process': | ||||
| drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c:1478:8: warning: 'skb' may be used uninitialized in this function [-Wmaybe-uninitialized] | ||||
|  | ||||
| This is a false positive, and marking the brcmf_fws_hanger_poppkt function | ||||
| as 'static inline' makes the warning go away. I have checked the object | ||||
| file output and while a little code gets moved around, the size of | ||||
| the binary remains identical. | ||||
|  | ||||
| Signed-off-by: Arnd Bergmann <arnd@arndb.de> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | ||||
| @@ -635,7 +635,7 @@ static int brcmf_fws_hanger_pushpkt(stru | ||||
|  	return 0; | ||||
|  } | ||||
|   | ||||
| -static int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h, | ||||
| +static inline int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h, | ||||
|  					  u32 slot_id, struct sk_buff **pktout, | ||||
|  					  bool remove_item) | ||||
|  { | ||||
| @@ -1,45 +0,0 @@ | ||||
| From: Arend van Spriel <arend@broadcom.com> | ||||
| Date: Wed, 20 May 2015 14:09:47 +0200 | ||||
| Subject: [PATCH] brcmfmac: allow device tree node without 'interrupts' | ||||
|  property | ||||
|  | ||||
| As described in the device tree bindings for 'brcm,bcm4329-fmac' | ||||
| nodes, the interrupts property is optional. So adding a check | ||||
| for the presence of this property before attempting to parse | ||||
| and map the interrupt. If not present or parsing fails return | ||||
| and fallback to in-band sdio interrupt. | ||||
|  | ||||
| Reviewed-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/of.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/of.c | ||||
| @@ -39,10 +39,16 @@ void brcmf_of_probe(struct brcmf_sdio_de | ||||
|  	if (!sdiodev->pdata) | ||||
|  		return; | ||||
|   | ||||
| +	if (of_property_read_u32(np, "brcm,drive-strength", &val) == 0) | ||||
| +		sdiodev->pdata->drive_strength = val; | ||||
| + | ||||
| +	/* make sure there are interrupts defined in the node */ | ||||
| +	if (!of_find_property(np, "interrupts", NULL)) | ||||
| +		return; | ||||
| + | ||||
|  	irq = irq_of_parse_and_map(np, 0); | ||||
|  	if (!irq) { | ||||
|  		brcmf_err("interrupt could not be mapped\n"); | ||||
| -		devm_kfree(dev, sdiodev->pdata); | ||||
|  		return; | ||||
|  	} | ||||
|  	irqf = irqd_get_trigger_type(irq_get_irq_data(irq)); | ||||
| @@ -50,7 +56,4 @@ void brcmf_of_probe(struct brcmf_sdio_de | ||||
|  	sdiodev->pdata->oob_irq_supported = true; | ||||
|  	sdiodev->pdata->oob_irq_nr = irq; | ||||
|  	sdiodev->pdata->oob_irq_flags = irqf; | ||||
| - | ||||
| -	if (of_property_read_u32(np, "brcm,drive-strength", &val) == 0) | ||||
| -		sdiodev->pdata->drive_strength = val; | ||||
|  } | ||||
| @@ -1,87 +0,0 @@ | ||||
| From: Hante Meuleman <meuleman@broadcom.com> | ||||
| Date: Wed, 20 May 2015 14:09:48 +0200 | ||||
| Subject: [PATCH] brcmfmac: Improve throughput by scheduling msbug flow worker. | ||||
|  | ||||
| The tx flow worker in msgbuf gets scheduled at tx till a certain | ||||
| threshold has been reached. Then the tx completes will take over | ||||
| the scheduling. When amsdu and ampdu is used the frames are | ||||
| transferred wireless in a very bulky fashion, in combination | ||||
| with this scheduling algorithm and buffer limiters in the stack | ||||
| this can result in limited throughput. This change causes the | ||||
| flow worker to be scheduled more frequently from tx. | ||||
|  | ||||
| Reviewed-by: Arend Van Spriel <arend@broadcom.com> | ||||
| Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com> | ||||
| Signed-off-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c | ||||
| @@ -249,8 +249,8 @@ void brcmf_flowring_delete(struct brcmf_ | ||||
|  } | ||||
|   | ||||
|   | ||||
| -void brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, | ||||
| -			    struct sk_buff *skb) | ||||
| +u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, | ||||
| +			   struct sk_buff *skb) | ||||
|  { | ||||
|  	struct brcmf_flowring_ring *ring; | ||||
|   | ||||
| @@ -271,6 +271,7 @@ void brcmf_flowring_enqueue(struct brcmf | ||||
|  		if (skb_queue_len(&ring->skblist) < BRCMF_FLOWRING_LOW) | ||||
|  			brcmf_flowring_block(flow, flowid, false); | ||||
|  	} | ||||
| +	return skb_queue_len(&ring->skblist); | ||||
|  } | ||||
|   | ||||
|   | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.h | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.h | ||||
| @@ -64,8 +64,8 @@ u32 brcmf_flowring_create(struct brcmf_f | ||||
|  void brcmf_flowring_delete(struct brcmf_flowring *flow, u8 flowid); | ||||
|  void brcmf_flowring_open(struct brcmf_flowring *flow, u8 flowid); | ||||
|  u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u8 flowid); | ||||
| -void brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, | ||||
| -			    struct sk_buff *skb); | ||||
| +u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, | ||||
| +			   struct sk_buff *skb); | ||||
|  struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u8 flowid); | ||||
|  void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u8 flowid, | ||||
|  			     struct sk_buff *skb); | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | ||||
| @@ -73,7 +73,7 @@ | ||||
|  #define BRCMF_MSGBUF_TX_FLUSH_CNT1		32 | ||||
|  #define BRCMF_MSGBUF_TX_FLUSH_CNT2		96 | ||||
|   | ||||
| -#define BRCMF_MSGBUF_DELAY_TXWORKER_THRS	64 | ||||
| +#define BRCMF_MSGBUF_DELAY_TXWORKER_THRS	96 | ||||
|  #define BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS	32 | ||||
|   | ||||
|  struct msgbuf_common_hdr { | ||||
| @@ -797,6 +797,8 @@ static int brcmf_msgbuf_txdata(struct br | ||||
|  	struct brcmf_flowring *flow = msgbuf->flow; | ||||
|  	struct ethhdr *eh = (struct ethhdr *)(skb->data); | ||||
|  	u32 flowid; | ||||
| +	u32 queue_count; | ||||
| +	bool force; | ||||
|   | ||||
|  	flowid = brcmf_flowring_lookup(flow, eh->h_dest, skb->priority, ifidx); | ||||
|  	if (flowid == BRCMF_FLOWRING_INVALID_ID) { | ||||
| @@ -804,8 +806,9 @@ static int brcmf_msgbuf_txdata(struct br | ||||
|  		if (flowid == BRCMF_FLOWRING_INVALID_ID) | ||||
|  			return -ENOMEM; | ||||
|  	} | ||||
| -	brcmf_flowring_enqueue(flow, flowid, skb); | ||||
| -	brcmf_msgbuf_schedule_txdata(msgbuf, flowid, false); | ||||
| +	queue_count = brcmf_flowring_enqueue(flow, flowid, skb); | ||||
| +	force = ((queue_count % BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS) == 0); | ||||
| +	brcmf_msgbuf_schedule_txdata(msgbuf, flowid, force); | ||||
|   | ||||
|  	return 0; | ||||
|  } | ||||
| @@ -1,41 +0,0 @@ | ||||
| From: Franky Lin <frankyl@broadcom.com> | ||||
| Date: Wed, 20 May 2015 14:09:49 +0200 | ||||
| Subject: [PATCH] brcmfmac: remove pci shared structure rev4 support | ||||
|  | ||||
| All pcie full dongle chips supported by fmac are using rev 5+ shared | ||||
| structure. This patch removes the rev4 related code. | ||||
|  | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Reviewed-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Reviewed-by: Arend Van Spriel <arend@broadcom.com> | ||||
| Signed-off-by: Franky Lin <frankyl@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c | ||||
| @@ -112,10 +112,9 @@ enum brcmf_pcie_state { | ||||
|  						 BRCMF_PCIE_MB_INT_D2H3_DB0 | \ | ||||
|  						 BRCMF_PCIE_MB_INT_D2H3_DB1) | ||||
|   | ||||
| -#define BRCMF_PCIE_MIN_SHARED_VERSION		4 | ||||
| +#define BRCMF_PCIE_MIN_SHARED_VERSION		5 | ||||
|  #define BRCMF_PCIE_MAX_SHARED_VERSION		5 | ||||
|  #define BRCMF_PCIE_SHARED_VERSION_MASK		0x00FF | ||||
| -#define BRCMF_PCIE_SHARED_TXPUSH_SUPPORT	0x4000 | ||||
|   | ||||
|  #define BRCMF_PCIE_FLAGS_HTOD_SPLIT		0x4000 | ||||
|  #define BRCMF_PCIE_FLAGS_DTOH_SPLIT		0x8000 | ||||
| @@ -1280,11 +1279,6 @@ brcmf_pcie_init_share_ram_info(struct br | ||||
|  		brcmf_err("Unsupported PCIE version %d\n", version); | ||||
|  		return -EINVAL; | ||||
|  	} | ||||
| -	if (shared->flags & BRCMF_PCIE_SHARED_TXPUSH_SUPPORT) { | ||||
| -		brcmf_err("Unsupported legacy TX mode 0x%x\n", | ||||
| -			  shared->flags & BRCMF_PCIE_SHARED_TXPUSH_SUPPORT); | ||||
| -		return -EINVAL; | ||||
| -	} | ||||
|   | ||||
|  	addr = sharedram_addr + BRCMF_SHARED_MAX_RXBUFPOST_OFFSET; | ||||
|  	shared->max_rxbufpost = brcmf_pcie_read_tcm16(devinfo, addr); | ||||
| @@ -1,120 +0,0 @@ | ||||
| From: Franky Lin <frankyl@broadcom.com> | ||||
| Date: Wed, 20 May 2015 14:09:50 +0200 | ||||
| Subject: [PATCH] brcmfmac: remove dummy cache flush/invalidate function | ||||
|  | ||||
| brcmf_dma_flush and brcmf_dma_invalidate_cache are not necessary and | ||||
| have never been implemented. | ||||
|  | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Reviewed-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Reviewed-by: Arend Van Spriel <arend@broadcom.com> | ||||
| Signed-off-by: Franky Lin <frankyl@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/commonring.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/commonring.c | ||||
| @@ -22,17 +22,6 @@ | ||||
|  #include "core.h" | ||||
|  #include "commonring.h" | ||||
|   | ||||
| - | ||||
| -/* dma flushing needs implementation for mips and arm platforms. Should | ||||
| - * be put in util. Note, this is not real flushing. It is virtual non | ||||
| - * cached memory. Only write buffers should have to be drained. Though | ||||
| - * this may be different depending on platform...... | ||||
| - * SEE ALSO msgbuf.c | ||||
| - */ | ||||
| -#define brcmf_dma_flush(addr, len) | ||||
| -#define brcmf_dma_invalidate_cache(addr, len) | ||||
| - | ||||
| - | ||||
|  void brcmf_commonring_register_cb(struct brcmf_commonring *commonring, | ||||
|  				  int (*cr_ring_bell)(void *ctx), | ||||
|  				  int (*cr_update_rptr)(void *ctx), | ||||
| @@ -206,14 +195,9 @@ int brcmf_commonring_write_complete(stru | ||||
|  	address = commonring->buf_addr; | ||||
|  	address += (commonring->f_ptr * commonring->item_len); | ||||
|  	if (commonring->f_ptr > commonring->w_ptr) { | ||||
| -		brcmf_dma_flush(address, | ||||
| -				(commonring->depth - commonring->f_ptr) * | ||||
| -				commonring->item_len); | ||||
|  		address = commonring->buf_addr; | ||||
|  		commonring->f_ptr = 0; | ||||
|  	} | ||||
| -	brcmf_dma_flush(address, (commonring->w_ptr - commonring->f_ptr) * | ||||
| -			commonring->item_len); | ||||
|   | ||||
|  	commonring->f_ptr = commonring->w_ptr; | ||||
|   | ||||
| @@ -258,8 +242,6 @@ void *brcmf_commonring_get_read_ptr(stru | ||||
|  	if (commonring->r_ptr == commonring->depth) | ||||
|  		commonring->r_ptr = 0; | ||||
|   | ||||
| -	brcmf_dma_invalidate_cache(ret_addr, *n_ items * commonring->item_len); | ||||
| - | ||||
|  	return ret_addr; | ||||
|  } | ||||
|   | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | ||||
| @@ -278,16 +278,6 @@ struct brcmf_msgbuf_pktids { | ||||
|  	struct brcmf_msgbuf_pktid *array; | ||||
|  }; | ||||
|   | ||||
| - | ||||
| -/* dma flushing needs implementation for mips and arm platforms. Should | ||||
| - * be put in util. Note, this is not real flushing. It is virtual non | ||||
| - * cached memory. Only write buffers should have to be drained. Though | ||||
| - * this may be different depending on platform...... | ||||
| - */ | ||||
| -#define brcmf_dma_flush(addr, len) | ||||
| -#define brcmf_dma_invalidate_cache(addr, len) | ||||
| - | ||||
| - | ||||
|  static void brcmf_msgbuf_rxbuf_ioctlresp_post(struct brcmf_msgbuf *msgbuf); | ||||
|   | ||||
|   | ||||
| @@ -462,7 +452,6 @@ static int brcmf_msgbuf_tx_ioctl(struct | ||||
|  		memcpy(msgbuf->ioctbuf, buf, buf_len); | ||||
|  	else | ||||
|  		memset(msgbuf->ioctbuf, 0, buf_len); | ||||
| -	brcmf_dma_flush(ioctl_buf, buf_len); | ||||
|   | ||||
|  	err = brcmf_commonring_write_complete(commonring); | ||||
|  	brcmf_commonring_unlock(commonring); | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c | ||||
| @@ -276,15 +276,6 @@ static const u32 brcmf_ring_itemsize[BRC | ||||
|  }; | ||||
|   | ||||
|   | ||||
| -/* dma flushing needs implementation for mips and arm platforms. Should | ||||
| - * be put in util. Note, this is not real flushing. It is virtual non | ||||
| - * cached memory. Only write buffers should have to be drained. Though | ||||
| - * this may be different depending on platform...... | ||||
| - */ | ||||
| -#define brcmf_dma_flush(addr, len) | ||||
| -#define brcmf_dma_invalidate_cache(addr, len) | ||||
| - | ||||
| - | ||||
|  static u32 | ||||
|  brcmf_pcie_read_reg32(struct brcmf_pciedev_info *devinfo, u32 reg_offset) | ||||
|  { | ||||
| @@ -1174,7 +1165,6 @@ static int brcmf_pcie_init_scratchbuffer | ||||
|  		goto fail; | ||||
|   | ||||
|  	memset(devinfo->shared.scratch, 0, BRCMF_DMA_D2H_SCRATCH_BUF_LEN); | ||||
| -	brcmf_dma_flush(devinfo->shared.scratch, BRCMF_DMA_D2H_SCRATCH_BUF_LEN); | ||||
|   | ||||
|  	addr = devinfo->shared.tcm_base_address + | ||||
|  	       BRCMF_SHARED_DMA_SCRATCH_ADDR_OFFSET; | ||||
| @@ -1192,7 +1182,6 @@ static int brcmf_pcie_init_scratchbuffer | ||||
|  		goto fail; | ||||
|   | ||||
|  	memset(devinfo->shared.ringupd, 0, BRCMF_DMA_D2H_RINGUPD_BUF_LEN); | ||||
| -	brcmf_dma_flush(devinfo->shared.ringupd, BRCMF_DMA_D2H_RINGUPD_BUF_LEN); | ||||
|   | ||||
|  	addr = devinfo->shared.tcm_base_address + | ||||
|  	       BRCMF_SHARED_DMA_RINGUPD_ADDR_OFFSET; | ||||
| @@ -1,270 +0,0 @@ | ||||
| From: Franky Lin <frankyl@broadcom.com> | ||||
| Date: Wed, 20 May 2015 14:09:51 +0200 | ||||
| Subject: [PATCH] brcmfmac: add support for dma indices feature | ||||
|  | ||||
| PCIe full dongle firmware can support a dma indices feature with which | ||||
| firmware can update/fetch the read/write indices of message buffer | ||||
| rings on both host to dongle and dongle to host directions. The support is | ||||
| announced by firmware through shared flags. | ||||
|  | ||||
| Reviewed-by: Arend Van Spriel <arend@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Signed-off-by: Franky Lin <frankyl@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c | ||||
| @@ -115,6 +115,8 @@ enum brcmf_pcie_state { | ||||
|  #define BRCMF_PCIE_MIN_SHARED_VERSION		5 | ||||
|  #define BRCMF_PCIE_MAX_SHARED_VERSION		5 | ||||
|  #define BRCMF_PCIE_SHARED_VERSION_MASK		0x00FF | ||||
| +#define BRCMF_PCIE_SHARED_DMA_INDEX		0x10000 | ||||
| +#define BRCMF_PCIE_SHARED_DMA_2B_IDX		0x100000 | ||||
|   | ||||
|  #define BRCMF_PCIE_FLAGS_HTOD_SPLIT		0x4000 | ||||
|  #define BRCMF_PCIE_FLAGS_DTOH_SPLIT		0x8000 | ||||
| @@ -146,6 +148,10 @@ enum brcmf_pcie_state { | ||||
|  #define BRCMF_SHARED_RING_H2D_R_IDX_PTR_OFFSET	8 | ||||
|  #define BRCMF_SHARED_RING_D2H_W_IDX_PTR_OFFSET	12 | ||||
|  #define BRCMF_SHARED_RING_D2H_R_IDX_PTR_OFFSET	16 | ||||
| +#define BRCMF_SHARED_RING_H2D_WP_HADDR_OFFSET	20 | ||||
| +#define BRCMF_SHARED_RING_H2D_RP_HADDR_OFFSET	28 | ||||
| +#define BRCMF_SHARED_RING_D2H_WP_HADDR_OFFSET	36 | ||||
| +#define BRCMF_SHARED_RING_D2H_RP_HADDR_OFFSET	44 | ||||
|  #define BRCMF_SHARED_RING_TCM_MEMLOC_OFFSET	0 | ||||
|  #define BRCMF_SHARED_RING_MAX_SUB_QUEUES	52 | ||||
|   | ||||
| @@ -247,6 +253,13 @@ struct brcmf_pciedev_info { | ||||
|  	bool mbdata_completed; | ||||
|  	bool irq_allocated; | ||||
|  	bool wowl_enabled; | ||||
| +	u8 dma_idx_sz; | ||||
| +	void *idxbuf; | ||||
| +	u32 idxbuf_sz; | ||||
| +	dma_addr_t idxbuf_dmahandle; | ||||
| +	u16 (*read_ptr)(struct brcmf_pciedev_info *devinfo, u32 mem_offset); | ||||
| +	void (*write_ptr)(struct brcmf_pciedev_info *devinfo, u32 mem_offset, | ||||
| +			  u16 value); | ||||
|  }; | ||||
|   | ||||
|  struct brcmf_pcie_ringbuf { | ||||
| @@ -323,6 +336,25 @@ brcmf_pcie_write_tcm16(struct brcmf_pcie | ||||
|  } | ||||
|   | ||||
|   | ||||
| +static u16 | ||||
| +brcmf_pcie_read_idx(struct brcmf_pciedev_info *devinfo, u32 mem_offset) | ||||
| +{ | ||||
| +	u16 *address = devinfo->idxbuf + mem_offset; | ||||
| + | ||||
| +	return (*(address)); | ||||
| +} | ||||
| + | ||||
| + | ||||
| +static void | ||||
| +brcmf_pcie_write_idx(struct brcmf_pciedev_info *devinfo, u32 mem_offset, | ||||
| +		     u16 value) | ||||
| +{ | ||||
| +	u16 *address = devinfo->idxbuf + mem_offset; | ||||
| + | ||||
| +	*(address) = value; | ||||
| +} | ||||
| + | ||||
| + | ||||
|  static u32 | ||||
|  brcmf_pcie_read_tcm32(struct brcmf_pciedev_info *devinfo, u32 mem_offset) | ||||
|  { | ||||
| @@ -868,7 +900,7 @@ static int brcmf_pcie_ring_mb_write_rptr | ||||
|  	brcmf_dbg(PCIE, "W r_ptr %d (%d), ring %d\n", commonring->r_ptr, | ||||
|  		  commonring->w_ptr, ring->id); | ||||
|   | ||||
| -	brcmf_pcie_write_tcm16(devinfo, ring->r_idx_addr, commonring->r_ptr); | ||||
| +	devinfo->write_ptr(devinfo, ring->r_idx_addr, commonring->r_ptr); | ||||
|   | ||||
|  	return 0; | ||||
|  } | ||||
| @@ -886,7 +918,7 @@ static int brcmf_pcie_ring_mb_write_wptr | ||||
|  	brcmf_dbg(PCIE, "W w_ptr %d (%d), ring %d\n", commonring->w_ptr, | ||||
|  		  commonring->r_ptr, ring->id); | ||||
|   | ||||
| -	brcmf_pcie_write_tcm16(devinfo, ring->w_idx_addr, commonring->w_ptr); | ||||
| +	devinfo->write_ptr(devinfo, ring->w_idx_addr, commonring->w_ptr); | ||||
|   | ||||
|  	return 0; | ||||
|  } | ||||
| @@ -915,7 +947,7 @@ static int brcmf_pcie_ring_mb_update_rpt | ||||
|  	if (devinfo->state != BRCMFMAC_PCIE_STATE_UP) | ||||
|  		return -EIO; | ||||
|   | ||||
| -	commonring->r_ptr = brcmf_pcie_read_tcm16(devinfo, ring->r_idx_addr); | ||||
| +	commonring->r_ptr = devinfo->read_ptr(devinfo, ring->r_idx_addr); | ||||
|   | ||||
|  	brcmf_dbg(PCIE, "R r_ptr %d (%d), ring %d\n", commonring->r_ptr, | ||||
|  		  commonring->w_ptr, ring->id); | ||||
| @@ -933,7 +965,7 @@ static int brcmf_pcie_ring_mb_update_wpt | ||||
|  	if (devinfo->state != BRCMFMAC_PCIE_STATE_UP) | ||||
|  		return -EIO; | ||||
|   | ||||
| -	commonring->w_ptr = brcmf_pcie_read_tcm16(devinfo, ring->w_idx_addr); | ||||
| +	commonring->w_ptr = devinfo->read_ptr(devinfo, ring->w_idx_addr); | ||||
|   | ||||
|  	brcmf_dbg(PCIE, "R w_ptr %d (%d), ring %d\n", commonring->w_ptr, | ||||
|  		  commonring->r_ptr, ring->id); | ||||
| @@ -1038,6 +1070,13 @@ static void brcmf_pcie_release_ringbuffe | ||||
|  	} | ||||
|  	kfree(devinfo->shared.flowrings); | ||||
|  	devinfo->shared.flowrings = NULL; | ||||
| +	if (devinfo->idxbuf) { | ||||
| +		dma_free_coherent(&devinfo->pdev->dev, | ||||
| +				  devinfo->idxbuf_sz, | ||||
| +				  devinfo->idxbuf, | ||||
| +				  devinfo->idxbuf_dmahandle); | ||||
| +		devinfo->idxbuf = NULL; | ||||
| +	} | ||||
|  } | ||||
|   | ||||
|   | ||||
| @@ -1053,19 +1092,72 @@ static int brcmf_pcie_init_ringbuffers(s | ||||
|  	u32 addr; | ||||
|  	u32 ring_mem_ptr; | ||||
|  	u32 i; | ||||
| +	u64 address; | ||||
| +	u32 bufsz; | ||||
|  	u16 max_sub_queues; | ||||
| +	u8 idx_offset; | ||||
|   | ||||
|  	ring_addr = devinfo->shared.ring_info_addr; | ||||
|  	brcmf_dbg(PCIE, "Base ring addr = 0x%08x\n", ring_addr); | ||||
| +	addr = ring_addr + BRCMF_SHARED_RING_MAX_SUB_QUEUES; | ||||
| +	max_sub_queues = brcmf_pcie_read_tcm16(devinfo, addr); | ||||
|   | ||||
| -	addr = ring_addr + BRCMF_SHARED_RING_D2H_W_IDX_PTR_OFFSET; | ||||
| -	d2h_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); | ||||
| -	addr = ring_addr + BRCMF_SHARED_RING_D2H_R_IDX_PTR_OFFSET; | ||||
| -	d2h_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); | ||||
| -	addr = ring_addr + BRCMF_SHARED_RING_H2D_W_IDX_PTR_OFFSET; | ||||
| -	h2d_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); | ||||
| -	addr = ring_addr + BRCMF_SHARED_RING_H2D_R_IDX_PTR_OFFSET; | ||||
| -	h2d_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); | ||||
| +	if (devinfo->dma_idx_sz != 0) { | ||||
| +		bufsz = (BRCMF_NROF_D2H_COMMON_MSGRINGS + max_sub_queues) * | ||||
| +			devinfo->dma_idx_sz * 2; | ||||
| +		devinfo->idxbuf = dma_alloc_coherent(&devinfo->pdev->dev, bufsz, | ||||
| +						     &devinfo->idxbuf_dmahandle, | ||||
| +						     GFP_KERNEL); | ||||
| +		if (!devinfo->idxbuf) | ||||
| +			devinfo->dma_idx_sz = 0; | ||||
| +	} | ||||
| + | ||||
| +	if (devinfo->dma_idx_sz == 0) { | ||||
| +		addr = ring_addr + BRCMF_SHARED_RING_D2H_W_IDX_PTR_OFFSET; | ||||
| +		d2h_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); | ||||
| +		addr = ring_addr + BRCMF_SHARED_RING_D2H_R_IDX_PTR_OFFSET; | ||||
| +		d2h_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); | ||||
| +		addr = ring_addr + BRCMF_SHARED_RING_H2D_W_IDX_PTR_OFFSET; | ||||
| +		h2d_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); | ||||
| +		addr = ring_addr + BRCMF_SHARED_RING_H2D_R_IDX_PTR_OFFSET; | ||||
| +		h2d_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); | ||||
| +		idx_offset = sizeof(u32); | ||||
| +		devinfo->write_ptr = brcmf_pcie_write_tcm16; | ||||
| +		devinfo->read_ptr = brcmf_pcie_read_tcm16; | ||||
| +		brcmf_dbg(PCIE, "Using TCM indices\n"); | ||||
| +	} else { | ||||
| +		memset(devinfo->idxbuf, 0, bufsz); | ||||
| +		devinfo->idxbuf_sz = bufsz; | ||||
| +		idx_offset = devinfo->dma_idx_sz; | ||||
| +		devinfo->write_ptr = brcmf_pcie_write_idx; | ||||
| +		devinfo->read_ptr = brcmf_pcie_read_idx; | ||||
| + | ||||
| +		h2d_w_idx_ptr = 0; | ||||
| +		addr = ring_addr + BRCMF_SHARED_RING_H2D_WP_HADDR_OFFSET; | ||||
| +		address = (u64)devinfo->idxbuf_dmahandle; | ||||
| +		brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); | ||||
| +		brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); | ||||
| + | ||||
| +		h2d_r_idx_ptr = h2d_w_idx_ptr + max_sub_queues * idx_offset; | ||||
| +		addr = ring_addr + BRCMF_SHARED_RING_H2D_RP_HADDR_OFFSET; | ||||
| +		address += max_sub_queues * idx_offset; | ||||
| +		brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); | ||||
| +		brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); | ||||
| + | ||||
| +		d2h_w_idx_ptr = h2d_r_idx_ptr + max_sub_queues * idx_offset; | ||||
| +		addr = ring_addr + BRCMF_SHARED_RING_D2H_WP_HADDR_OFFSET; | ||||
| +		address += max_sub_queues * idx_offset; | ||||
| +		brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); | ||||
| +		brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); | ||||
| + | ||||
| +		d2h_r_idx_ptr = d2h_w_idx_ptr + | ||||
| +				BRCMF_NROF_D2H_COMMON_MSGRINGS * idx_offset; | ||||
| +		addr = ring_addr + BRCMF_SHARED_RING_D2H_RP_HADDR_OFFSET; | ||||
| +		address += BRCMF_NROF_D2H_COMMON_MSGRINGS * idx_offset; | ||||
| +		brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); | ||||
| +		brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); | ||||
| +		brcmf_dbg(PCIE, "Using host memory indices\n"); | ||||
| +	} | ||||
|   | ||||
|  	addr = ring_addr + BRCMF_SHARED_RING_TCM_MEMLOC_OFFSET; | ||||
|  	ring_mem_ptr = brcmf_pcie_read_tcm32(devinfo, addr); | ||||
| @@ -1079,8 +1171,8 @@ static int brcmf_pcie_init_ringbuffers(s | ||||
|  		ring->id = i; | ||||
|  		devinfo->shared.commonrings[i] = ring; | ||||
|   | ||||
| -		h2d_w_idx_ptr += sizeof(u32); | ||||
| -		h2d_r_idx_ptr += sizeof(u32); | ||||
| +		h2d_w_idx_ptr += idx_offset; | ||||
| +		h2d_r_idx_ptr += idx_offset; | ||||
|  		ring_mem_ptr += BRCMF_RING_MEM_SZ; | ||||
|  	} | ||||
|   | ||||
| @@ -1094,13 +1186,11 @@ static int brcmf_pcie_init_ringbuffers(s | ||||
|  		ring->id = i; | ||||
|  		devinfo->shared.commonrings[i] = ring; | ||||
|   | ||||
| -		d2h_w_idx_ptr += sizeof(u32); | ||||
| -		d2h_r_idx_ptr += sizeof(u32); | ||||
| +		d2h_w_idx_ptr += idx_offset; | ||||
| +		d2h_r_idx_ptr += idx_offset; | ||||
|  		ring_mem_ptr += BRCMF_RING_MEM_SZ; | ||||
|  	} | ||||
|   | ||||
| -	addr = ring_addr + BRCMF_SHARED_RING_MAX_SUB_QUEUES; | ||||
| -	max_sub_queues = brcmf_pcie_read_tcm16(devinfo, addr); | ||||
|  	devinfo->shared.nrof_flowrings = | ||||
|  			max_sub_queues - BRCMF_NROF_H2D_COMMON_MSGRINGS; | ||||
|  	rings = kcalloc(devinfo->shared.nrof_flowrings, sizeof(*ring), | ||||
| @@ -1124,15 +1214,15 @@ static int brcmf_pcie_init_ringbuffers(s | ||||
|  					     ring); | ||||
|  		ring->w_idx_addr = h2d_w_idx_ptr; | ||||
|  		ring->r_idx_addr = h2d_r_idx_ptr; | ||||
| -		h2d_w_idx_ptr += sizeof(u32); | ||||
| -		h2d_r_idx_ptr += sizeof(u32); | ||||
| +		h2d_w_idx_ptr += idx_offset; | ||||
| +		h2d_r_idx_ptr += idx_offset; | ||||
|  	} | ||||
|  	devinfo->shared.flowrings = rings; | ||||
|   | ||||
|  	return 0; | ||||
|   | ||||
|  fail: | ||||
| -	brcmf_err("Allocating commonring buffers failed\n"); | ||||
| +	brcmf_err("Allocating ring buffers failed\n"); | ||||
|  	brcmf_pcie_release_ringbuffers(devinfo); | ||||
|  	return -ENOMEM; | ||||
|  } | ||||
| @@ -1269,6 +1359,14 @@ brcmf_pcie_init_share_ram_info(struct br | ||||
|  		return -EINVAL; | ||||
|  	} | ||||
|   | ||||
| +	/* check firmware support dma indicies */ | ||||
| +	if (shared->flags & BRCMF_PCIE_SHARED_DMA_INDEX) { | ||||
| +		if (shared->flags & BRCMF_PCIE_SHARED_DMA_2B_IDX) | ||||
| +			devinfo->dma_idx_sz = sizeof(u16); | ||||
| +		else | ||||
| +			devinfo->dma_idx_sz = sizeof(u32); | ||||
| +	} | ||||
| + | ||||
|  	addr = sharedram_addr + BRCMF_SHARED_MAX_RXBUFPOST_OFFSET; | ||||
|  	shared->max_rxbufpost = brcmf_pcie_read_tcm16(devinfo, addr); | ||||
|  	if (shared->max_rxbufpost == 0) | ||||
| @@ -1,102 +0,0 @@ | ||||
| From: Arend van Spriel <arend@broadcom.com> | ||||
| Date: Tue, 26 May 2015 13:19:46 +0200 | ||||
| Subject: [PATCH] brcmfmac: avoid null pointer access when | ||||
|  brcmf_msgbuf_get_pktid() fails | ||||
|  | ||||
| The function brcmf_msgbuf_get_pktid() may return a NULL pointer so | ||||
| the callers should check the return pointer before accessing it to | ||||
| avoid the crash below (see [1]): | ||||
|  | ||||
| brcmfmac: brcmf_msgbuf_get_pktid: Invalid packet id 273 (not in use) | ||||
| BUG: unable to handle kernel NULL pointer dereference at 0000000000000080 | ||||
| IP: [<ffffffff8145b225>] skb_pull+0x5/0x50 | ||||
| PGD 0 | ||||
| Oops: 0000 [#1] PREEMPT SMP | ||||
| Modules linked in: pci_stub vboxpci(O) vboxnetflt(O) vboxnetadp(O) vboxdrv(O) | ||||
|  snd_hda_codec_hdmi bnep mousedev hid_generic ushwmon msr ext4 crc16 mbcache | ||||
|  jbd2 sd_mod uas usb_storage ahci libahci libata scsi_mod xhci_pci xhci_hcd | ||||
|  usbcore usb_common | ||||
| CPU: 0 PID: 1661 Comm: irq/61-brcmf_pc Tainted: G O    4.0.1-MacbookPro-ARCH #1 | ||||
| Hardware name: Apple Inc. MacBookPro12,1/Mac-E43C1C25D4880AD6, | ||||
|  BIOS MBP121.88Z.0167.B02.1503241251 03/24/2015 | ||||
| task: ffff880264203cc0 ti: ffff88025ffe4000 task.ti: ffff88025ffe4000 | ||||
| RIP: 0010:[<ffffffff8145b225>]  [<ffffffff8145b225>] skb_pull+0x5/0x50 | ||||
| RSP: 0018:ffff88025ffe7d40  EFLAGS: 00010202 | ||||
| RAX: 0000000000000000 RBX: ffff88008a33c000 RCX: 0000000000000044 | ||||
| RDX: 0000000000000000 RSI: 000000000000004a RDI: 0000000000000000 | ||||
| RBP: ffff88025ffe7da8 R08: 0000000000000096 R09: 000000000000004a | ||||
| R10: 0000000000000000 R11: 000000000000048e R12: ffff88025ff14f00 | ||||
| R13: 0000000000000000 R14: ffff880263b48200 R15: ffff88008a33c000 | ||||
| FS:  0000000000000000(0000) GS:ffff88026ec00000(0000) knlGS:0000000000000000 | ||||
| CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033 | ||||
| CR2: 0000000000000080 CR3: 000000000180b000 CR4: 00000000003407f0 | ||||
| Stack: | ||||
|  ffffffffa06aed74 ffff88025ffe7dc8 ffff880263b48270 ffff880263b48278 | ||||
|  05ea88020000004a 0002ffff81014635 000000001720b2f6 ffff88026ec116c0 | ||||
|  ffff880263b48200 0000000000010000 ffff880263b4ae00 ffff880264203cc0 | ||||
| Call Trace: | ||||
|  [<ffffffffa06aed74>] ? brcmf_msgbuf_process_rx+0x404/0x480 [brcmfmac] | ||||
|  [<ffffffff810cea60>] ? irq_finalize_oneshot.part.30+0xf0/0xf0 | ||||
|  [<ffffffffa06afb55>] brcmf_proto_msgbuf_rx_trigger+0x35/0xf0 [brcmfmac] | ||||
|  [<ffffffffa06baf2a>] brcmf_pcie_isr_thread_v2+0x8a/0x130 [brcmfmac] | ||||
|  [<ffffffff810cea80>] irq_thread_fn+0x20/0x50 | ||||
|  [<ffffffff810ceddf>] irq_thread+0x13f/0x170 | ||||
|  [<ffffffff810cebf0>] ? wake_threads_waitq+0x30/0x30 | ||||
|  [<ffffffff810ceca0>] ? irq_thread_dtor+0xb0/0xb0 | ||||
|  [<ffffffff81092a08>] kthread+0xd8/0xf0 | ||||
|  [<ffffffff81092930>] ? kthread_create_on_node+0x1c0/0x1c0 | ||||
|  [<ffffffff8156d898>] ret_from_fork+0x58/0x90 | ||||
|  [<ffffffff81092930>] ? kthread_create_on_node+0x1c0/0x1c0 | ||||
| Code: 01 83 e2 f7 88 50 01 48 83 c4 08 5b 5d f3 c3 0f 1f 80 00 00 00 00 83 e2 | ||||
|  f7 88 50 01 c3 66 0f 1f 84 00 00 00 00 00 0f 1f | ||||
| RIP  [<ffffffff8145b225>] skb_pull+0x5/0x50 | ||||
|  RSP <ffff88025ffe7d40> | ||||
| CR2: 0000000000000080 | ||||
| ---[ end trace b074c0f90e7c997d ]--- | ||||
|  | ||||
| [1] http://mid.gmane.org/20150430193259.GA5630@googlemail.com | ||||
|  | ||||
| Cc: <stable@vger.kernel.org> # v3.18, v3.19, v4.0, v4.1 | ||||
| Reported-by: Michael Hornung <mhornung.linux@gmail.com> | ||||
| Reviewed-by: Hante Meuleman <meuleman@broadcom.com> | ||||
| Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | ||||
| @@ -500,11 +500,9 @@ static int brcmf_msgbuf_query_dcmd(struc | ||||
|  				     msgbuf->rx_pktids, | ||||
|  				     msgbuf->ioctl_resp_pktid); | ||||
|  	if (msgbuf->ioctl_resp_ret_len != 0) { | ||||
| -		if (!skb) { | ||||
| -			brcmf_err("Invalid packet id idx recv'd %d\n", | ||||
| -				  msgbuf->ioctl_resp_pktid); | ||||
| +		if (!skb) | ||||
|  			return -EBADF; | ||||
| -		} | ||||
| + | ||||
|  		memcpy(buf, skb->data, (len < msgbuf->ioctl_resp_ret_len) ? | ||||
|  				       len : msgbuf->ioctl_resp_ret_len); | ||||
|  	} | ||||
| @@ -866,10 +864,8 @@ brcmf_msgbuf_process_txstatus(struct brc | ||||
|  	flowid -= BRCMF_NROF_H2D_COMMON_MSGRINGS; | ||||
|  	skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev, | ||||
|  				     msgbuf->tx_pktids, idx); | ||||
| -	if (!skb) { | ||||
| -		brcmf_err("Invalid packet id idx recv'd %d\n", idx); | ||||
| +	if (!skb) | ||||
|  		return; | ||||
| -	} | ||||
|   | ||||
|  	set_bit(flowid, msgbuf->txstatus_done_map); | ||||
|  	commonring = msgbuf->flowrings[flowid]; | ||||
| @@ -1148,6 +1144,8 @@ brcmf_msgbuf_process_rx_complete(struct | ||||
|   | ||||
|  	skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev, | ||||
|  				     msgbuf->rx_pktids, idx); | ||||
| +	if (!skb) | ||||
| +		return; | ||||
|   | ||||
|  	if (data_offset) | ||||
|  		skb_pull(skb, data_offset); | ||||
| @@ -1,63 +0,0 @@ | ||||
| From: Arend van Spriel <arend@broadcom.com> | ||||
| Date: Wed, 27 May 2015 19:31:41 +0200 | ||||
| Subject: [PATCH] brcmfmac: fix invalid access to struct acpi_device fields | ||||
|  | ||||
| The fields of struct acpi_device are only known when CONFIG_ACPI is | ||||
| defined. Fix this by using a helper function. This will resolve the | ||||
| issue found in linux-next: | ||||
|  | ||||
|  ../brcmfmac/bcmsdh.c: In function 'brcmf_ops_sdio_probe': | ||||
|  ../brcmfmac/bcmsdh.c:1139:7: error: dereferencing pointer to incomplete type | ||||
|    adev->flags.power_manageable = 0; | ||||
|        ^ | ||||
|  | ||||
| Fixes: f0992ace680c ("brcmfmac: prohibit ACPI power management ...") | ||||
| Cc: Fu, Zhonghui <zhonghui.fu@linux.intel.com> | ||||
| Reported-by: Stephen Rothwell <sfr@canb.auug.org.au> | ||||
| Signed-off-by: Arend van Spriel <arend@broadcom.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | ||||
| @@ -1117,6 +1117,18 @@ MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_id | ||||
|  static struct brcmfmac_sdio_platform_data *brcmfmac_sdio_pdata; | ||||
|   | ||||
|   | ||||
| +static void brcmf_sdiod_acpi_set_power_manageable(struct device *dev, | ||||
| +						  int val) | ||||
| +{ | ||||
| +#if IS_ENABLED(CONFIG_ACPI) | ||||
| +	struct acpi_device *adev; | ||||
| + | ||||
| +	adev = ACPI_COMPANION(dev); | ||||
| +	if (adev) | ||||
| +		adev->flags.power_manageable = 0; | ||||
| +#endif | ||||
| +} | ||||
| + | ||||
|  static int brcmf_ops_sdio_probe(struct sdio_func *func, | ||||
|  				const struct sdio_device_id *id) | ||||
|  { | ||||
| @@ -1124,7 +1136,6 @@ static int brcmf_ops_sdio_probe(struct s | ||||
|  	struct brcmf_sdio_dev *sdiodev; | ||||
|  	struct brcmf_bus *bus_if; | ||||
|  	struct device *dev; | ||||
| -	struct acpi_device *adev; | ||||
|   | ||||
|  	brcmf_dbg(SDIO, "Enter\n"); | ||||
|  	brcmf_dbg(SDIO, "Class=%x\n", func->class); | ||||
| @@ -1132,11 +1143,9 @@ static int brcmf_ops_sdio_probe(struct s | ||||
|  	brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device); | ||||
|  	brcmf_dbg(SDIO, "Function#: %d\n", func->num); | ||||
|   | ||||
| -	/* prohibit ACPI power management for this device */ | ||||
|  	dev = &func->dev; | ||||
| -	adev = ACPI_COMPANION(dev); | ||||
| -	if (adev) | ||||
| -		adev->flags.power_manageable = 0; | ||||
| +	/* prohibit ACPI power management for this device */ | ||||
| +	brcmf_sdiod_acpi_set_power_manageable(dev, 0); | ||||
|   | ||||
|  	/* Consume func num 1 but dont do anything with it. */ | ||||
|  	if (func->num == 1) | ||||
| @@ -1,56 +0,0 @@ | ||||
| From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com> | ||||
| Date: Wed, 20 May 2015 09:34:21 +0200 | ||||
| Subject: [PATCH] brcmfmac: simplify check stripping v2 NVRAM | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
|  | ||||
| Comparing NVRAM entry with a full filtering string is simpler than | ||||
| comparing it with a short prefix and then checking random chars at magic | ||||
| offsets. The cost of snprintf relatively low, we execute it just once. | ||||
| Tested on BCM43602 with NVRAM hacked to use V2 format. | ||||
|  | ||||
| Signed-off-by: Rafał Miłecki <zajec5@gmail.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c | ||||
| @@ -25,7 +25,7 @@ | ||||
|   | ||||
|  #define BRCMF_FW_MAX_NVRAM_SIZE			64000 | ||||
|  #define BRCMF_FW_NVRAM_DEVPATH_LEN		19	/* devpath0=pcie/1/4/ */ | ||||
| -#define BRCMF_FW_NVRAM_PCIEDEV_LEN		9	/* pcie/1/4/ */ | ||||
| +#define BRCMF_FW_NVRAM_PCIEDEV_LEN		10	/* pcie/1/4/ + \0 */ | ||||
|   | ||||
|  char brcmf_firmware_path[BRCMF_FW_PATH_LEN]; | ||||
|  module_param_string(firmware_path, brcmf_firmware_path, | ||||
| @@ -297,6 +297,8 @@ fail: | ||||
|  static void brcmf_fw_strip_multi_v2(struct nvram_parser *nvp, u16 domain_nr, | ||||
|  				    u16 bus_nr) | ||||
|  { | ||||
| +	char prefix[BRCMF_FW_NVRAM_PCIEDEV_LEN]; | ||||
| +	size_t len; | ||||
|  	u32 i, j; | ||||
|  	u8 *nvram; | ||||
|   | ||||
| @@ -308,14 +310,13 @@ static void brcmf_fw_strip_multi_v2(stru | ||||
|  	 * Valid entries are of type pcie/X/Y/ where X = domain_nr and | ||||
|  	 * Y = bus_nr. | ||||
|  	 */ | ||||
| +	snprintf(prefix, sizeof(prefix), "pcie/%d/%d/", domain_nr, bus_nr); | ||||
| +	len = strlen(prefix); | ||||
|  	i = 0; | ||||
|  	j = 0; | ||||
| -	while (i < nvp->nvram_len - BRCMF_FW_NVRAM_PCIEDEV_LEN) { | ||||
| -		if ((strncmp(&nvp->nvram[i], "pcie/", 5) == 0) && | ||||
| -		    (nvp->nvram[i + 6] == '/') && (nvp->nvram[i + 8] == '/') && | ||||
| -		    ((nvp->nvram[i + 5] - '0') == domain_nr) && | ||||
| -		    ((nvp->nvram[i + 7] - '0') == bus_nr)) { | ||||
| -			i += BRCMF_FW_NVRAM_PCIEDEV_LEN; | ||||
| +	while (i < nvp->nvram_len - len) { | ||||
| +		if (strncmp(&nvp->nvram[i], prefix, len) == 0) { | ||||
| +			i += len; | ||||
|  			while (nvp->nvram[i] != 0) { | ||||
|  				nvram[j] = nvp->nvram[i]; | ||||
|  				i++; | ||||
| @@ -1,57 +0,0 @@ | ||||
| From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com> | ||||
| Date: Wed, 20 May 2015 11:01:08 +0200 | ||||
| Subject: [PATCH] brcmfmac: simplify check finding NVRAM v1 device path | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
|  | ||||
| With a simple use of snprintf and small buffer we can compare NVRAM | ||||
| entry value with a full string. This way we avoid checking random chars | ||||
| at magic offsets. | ||||
| Tested on BCM43602 with NVRAM hacked to use v1 format. | ||||
|  | ||||
| Signed-off-by: Rafał Miłecki <zajec5@gmail.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c | ||||
| @@ -222,6 +222,10 @@ static int brcmf_init_nvram_parser(struc | ||||
|  static void brcmf_fw_strip_multi_v1(struct nvram_parser *nvp, u16 domain_nr, | ||||
|  				    u16 bus_nr) | ||||
|  { | ||||
| +	/* Device path with a leading '=' key-value separator */ | ||||
| +	char pcie_path[] = "=pcie/?/?"; | ||||
| +	size_t pcie_len; | ||||
| + | ||||
|  	u32 i, j; | ||||
|  	bool found; | ||||
|  	u8 *nvram; | ||||
| @@ -238,6 +242,9 @@ static void brcmf_fw_strip_multi_v1(stru | ||||
|  	/* First search for the devpathX and see if it is the configuration | ||||
|  	 * for domain_nr/bus_nr. Search complete nvp | ||||
|  	 */ | ||||
| +	snprintf(pcie_path, sizeof(pcie_path), "=pcie/%d/%d", domain_nr, | ||||
| +		 bus_nr); | ||||
| +	pcie_len = strlen(pcie_path); | ||||
|  	found = false; | ||||
|  	i = 0; | ||||
|  	while (i < nvp->nvram_len - BRCMF_FW_NVRAM_DEVPATH_LEN) { | ||||
| @@ -245,13 +252,10 @@ static void brcmf_fw_strip_multi_v1(stru | ||||
|  		 * Y = domain_nr, Z = bus_nr, X = virtual ID | ||||
|  		 */ | ||||
|  		if ((strncmp(&nvp->nvram[i], "devpath", 7) == 0) && | ||||
| -		    (strncmp(&nvp->nvram[i + 8], "=pcie/", 6) == 0)) { | ||||
| -			if (((nvp->nvram[i + 14] - '0') == domain_nr) && | ||||
| -			    ((nvp->nvram[i + 16] - '0') == bus_nr)) { | ||||
| -				id = nvp->nvram[i + 7] - '0'; | ||||
| -				found = true; | ||||
| -				break; | ||||
| -			} | ||||
| +		    (strncmp(&nvp->nvram[i + 8], pcie_path, pcie_len) == 0)) { | ||||
| +			id = nvp->nvram[i + 7] - '0'; | ||||
| +			found = true; | ||||
| +			break; | ||||
|  		} | ||||
|  		while (nvp->nvram[i] != 0) | ||||
|  			i++; | ||||
| @@ -1,45 +0,0 @@ | ||||
| From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com> | ||||
| Date: Wed, 20 May 2015 13:59:54 +0200 | ||||
| Subject: [PATCH] brcmfmac: treat \0 as end of comment when parsing NVRAM | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
|  | ||||
| This fixes brcmfmac dealing with NVRAM coming from platform e.g. from a | ||||
| flash MTD partition. In such cases entries are separated by \0 instead | ||||
| of \n which caused ignoring whole content after the first "comment". | ||||
| While platform NVRAM doesn't usually contain comments, we switch to | ||||
| COMMENT state after e.g. finding an unexpected char in key name. | ||||
|  | ||||
| Signed-off-by: Rafał Miłecki <zajec5@gmail.com> | ||||
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | ||||
| --- | ||||
|  | ||||
| --- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c | ||||
| +++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c | ||||
| @@ -162,17 +162,20 @@ brcmf_nvram_handle_value(struct nvram_pa | ||||
|  static enum nvram_parser_state | ||||
|  brcmf_nvram_handle_comment(struct nvram_parser *nvp) | ||||
|  { | ||||
| -	char *eol, *sol; | ||||
| +	char *eoc, *sol; | ||||
|   | ||||
|  	sol = (char *)&nvp->fwnv->data[nvp->pos]; | ||||
| -	eol = strchr(sol, '\n'); | ||||
| -	if (eol == NULL) | ||||
| -		return END; | ||||
| +	eoc = strchr(sol, '\n'); | ||||
| +	if (!eoc) { | ||||
| +		eoc = strchr(sol, '\0'); | ||||
| +		if (!eoc) | ||||
| +			return END; | ||||
| +	} | ||||
|   | ||||
|  	/* eat all moving to next line */ | ||||
|  	nvp->line++; | ||||
|  	nvp->column = 1; | ||||
| -	nvp->pos += (eol - sol) + 1; | ||||
| +	nvp->pos += (eoc - sol) + 1; | ||||
|  	return IDLE; | ||||
|  } | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user
	 Felix Fietkau
					Felix Fietkau