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_NAME:=mac80211 | ||||||
|  |  | ||||||
| PKG_VERSION:=2015-03-09 | PKG_VERSION:=2015-06-22 | ||||||
| PKG_RELEASE:=3 | PKG_RELEASE:=1 | ||||||
| PKG_SOURCE_URL:=http://mirror2.openwrt.org/sources | PKG_SOURCE_URL:=http://mirror2.openwrt.org/sources | ||||||
| PKG_BACKPORT_VERSION:= | PKG_BACKPORT_VERSION:= | ||||||
| PKG_MD5SUM:=6d4b04e4ce8a1f54dabfb04f4709453c | PKG_MD5SUM:=352b2b46d36a72aadc96161a3cefdb1c | ||||||
|  |  | ||||||
| PKG_SOURCE:=compat-wireless-$(PKG_VERSION)$(PKG_BACKPORT_VERSION).tar.bz2 | PKG_SOURCE:=compat-wireless-$(PKG_VERSION)$(PKG_BACKPORT_VERSION).tar.bz2 | ||||||
| PKG_BUILD_DIR:=$(KERNEL_BUILD_DIR)/compat-wireless-$(PKG_VERSION) | PKG_BUILD_DIR:=$(KERNEL_BUILD_DIR)/compat-wireless-$(PKG_VERSION) | ||||||
|   | |||||||
| @@ -27,7 +27,7 @@ | |||||||
|  	@set -e ; test -f .local-symbols || (						\ |  	@set -e ; test -f .local-symbols || (						\ | ||||||
|  	echo "/--------------"								;\ |  	echo "/--------------"								;\ | ||||||
|  	echo "| You shouldn't run make in the backports tree, but only in"		;\ |  	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 "| (that isn't currently running.)"					;\ | ||||||
|  	echo "\\--"									;\ |  	echo "\\--"									;\ | ||||||
|  	false) |  	false) | ||||||
| @@ -56,11 +56,12 @@ | |||||||
| -			done								\ | -			done								\ | ||||||
| -		) > Kconfig.kernel							;\ | -		) > Kconfig.kernel							;\ | ||||||
| -		kver=$$($(MAKE) --no-print-directory -C $(KLIB_BUILD) kernelversion |	\ | -		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" != "" || echo "Kernel version parse failed!"		;\ | ||||||
| -		test "$$kver" != ""							;\ | -		test "$$kver" != ""							;\ | ||||||
| -		kvers="$$(seq 14 39 | sed 's/^/2.6./')"					;\ | -		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									;\ | -		print=0									;\ | ||||||
| -		for v in $$kvers ; do							\ | -		for v in $$kvers ; do							\ | ||||||
| -			if [ "$$print" = "1" ] ; then					\ | -			if [ "$$print" = "1" ] ; then					\ | ||||||
| @@ -111,11 +112,12 @@ | |||||||
| + | + | ||||||
| +Kconfig.versions: Kconfig.kernel | +Kconfig.versions: Kconfig.kernel | ||||||
| +	@kver=$$($(MAKE) --no-print-directory -C $(KLIB_BUILD) kernelversion |	\ | +	@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" != "" || echo "Kernel version parse failed!"		;\ | ||||||
| +	test "$$kver" != ""							;\ | +	test "$$kver" != ""							;\ | ||||||
| +	kvers="$$(seq 14 39 | sed 's/^/2.6./')"					;\ | +	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									;\ | +	print=0									;\ | ||||||
| +	for v in $$kvers ; do							\ | +	for v in $$kvers ; do							\ | ||||||
| +		if [ "$$print" = "1" ] ; then					\ | +		if [ "$$print" = "1" ] ; then					\ | ||||||
|   | |||||||
| @@ -1,8 +1,8 @@ | |||||||
| --- a/backport-include/linux/debugfs.h | --- a/backport-include/linux/debugfs.h | ||||||
| +++ b/backport-include/linux/debugfs.h | +++ b/backport-include/linux/debugfs.h | ||||||
| @@ -3,6 +3,7 @@ | @@ -4,6 +4,7 @@ | ||||||
|  #include_next <linux/debugfs.h> |  | ||||||
|  #include <linux/version.h> |  #include <linux/version.h> | ||||||
|  |  #include <linux/device.h> | ||||||
|  #include <generated/utsrelease.h> |  #include <generated/utsrelease.h> | ||||||
| +#include <linux/device.h> | +#include <linux/device.h> | ||||||
|   |   | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| --- a/net/mac80211/iface.c | --- a/net/mac80211/iface.c | ||||||
| +++ b/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); |  	mutex_unlock(&local->iflist_mtx); | ||||||
|  	unregister_netdevice_many(&unreg_list); |  	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 | --- a/.local-symbols | ||||||
| +++ b/.local-symbols | +++ b/.local-symbols | ||||||
| @@ -344,40 +344,3 @@ USB_CDC_PHONET= | @@ -448,43 +448,6 @@ USB_CDC_PHONET= | ||||||
|  USB_IPHETH= |  USB_IPHETH= | ||||||
|  USB_SIERRA_NET= |  USB_SIERRA_NET= | ||||||
|  USB_VL600= |  USB_VL600= | ||||||
| @@ -32,26 +32,18 @@ | |||||||
| -BCMA_BLOCKIO= | -BCMA_BLOCKIO= | ||||||
| -BCMA_HOST_PCI_POSSIBLE= | -BCMA_HOST_PCI_POSSIBLE= | ||||||
| -BCMA_HOST_PCI= | -BCMA_HOST_PCI= | ||||||
| -BCMA_DRIVER_PCI_HOSTMODE= |  | ||||||
| -BCMA_HOST_SOC= | -BCMA_HOST_SOC= | ||||||
| -BCMA_DRIVER_PCI= | -BCMA_DRIVER_PCI= | ||||||
|  | -BCMA_DRIVER_PCI_HOSTMODE= | ||||||
| -BCMA_DRIVER_MIPS= | -BCMA_DRIVER_MIPS= | ||||||
| -BCMA_SFLASH= | -BCMA_SFLASH= | ||||||
| -BCMA_NFLASH= | -BCMA_NFLASH= | ||||||
| -BCMA_DRIVER_GMAC_CMN= | -BCMA_DRIVER_GMAC_CMN= | ||||||
| -BCMA_DRIVER_GPIO= | -BCMA_DRIVER_GPIO= | ||||||
| -BCMA_DEBUG= | -BCMA_DEBUG= | ||||||
| --- a/Makefile.kernel |  NFC= | ||||||
| +++ b/Makefile.kernel |  NFC_DIGITAL= | ||||||
| @@ -38,8 +38,6 @@ obj-$(CPTCFG_MAC80211) += net/mac80211/ |  NFC_NCI= | ||||||
|  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/ |  | ||||||
| --- a/drivers/net/wireless/b43/main.c | --- a/drivers/net/wireless/b43/main.c | ||||||
| +++ b/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 | @@ -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); |  	return (bus->chipco.dev ? bus->chipco.dev : bus->pcicore.dev); | ||||||
|  #else |  #else | ||||||
|  	return bus->chipco.dev; |  	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) |  	if (sprom->boardflags_lo & B43_BFL_XTAL_NOSLOW) | ||||||
|  		hf |= B43_HF_DSCRQ; /* Disable slowclock requests from ucode. */ |  		hf |= B43_HF_DSCRQ; /* Disable slowclock requests from ucode. */ | ||||||
| @@ -116,12 +108,23 @@ | |||||||
| --- a/Kconfig.sources | --- a/Kconfig.sources | ||||||
| +++ b/Kconfig.sources | +++ b/Kconfig.sources | ||||||
| @@ -9,9 +9,6 @@ source "$BACKPORT_DIR/drivers/net/wirele | @@ -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/net/usb/Kconfig" | ||||||
|   |   | ||||||
| -source "$BACKPORT_DIR/drivers/ssb/Kconfig" | -source "$BACKPORT_DIR/drivers/ssb/Kconfig" | ||||||
| -source "$BACKPORT_DIR/drivers/bcma/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 | --- a/net/mac80211/cfg.c | ||||||
| +++ b/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; |  	sdata->u.ap.driver_smps_mode = IEEE80211_SMPS_OFF; | ||||||
|   |   | ||||||
|  	__sta_info_flush(sdata, true); |  	__sta_info_flush(sdata, true); | ||||||
|   | |||||||
| @@ -18,7 +18,7 @@ | |||||||
|  static int ieee80211_ifa6_changed(struct notifier_block *nb, |  static int ieee80211_ifa6_changed(struct notifier_block *nb, | ||||||
|  				  unsigned long data, void *arg) |  				  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) |  	if (result) | ||||||
|  		goto fail_pm_qos; |  		goto fail_pm_qos; | ||||||
|   |   | ||||||
| @@ -35,7 +35,7 @@ | |||||||
|  	local->ifa6_notifier.notifier_call = ieee80211_ifa6_changed; |  	local->ifa6_notifier.notifier_call = ieee80211_ifa6_changed; | ||||||
|  	result = register_inet6addr_notifier(&local->ifa6_notifier); |  	result = register_inet6addr_notifier(&local->ifa6_notifier); | ||||||
|  	if (result) |  	if (result) | ||||||
| @@ -1073,13 +1073,13 @@ int ieee80211_register_hw(struct ieee802 | @@ -1102,13 +1102,13 @@ int ieee80211_register_hw(struct ieee802 | ||||||
|   |   | ||||||
|  	return 0; |  	return 0; | ||||||
|   |   | ||||||
| @@ -52,7 +52,7 @@ | |||||||
|   fail_ifa: |   fail_ifa: | ||||||
|  	pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY, |  	pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY, | ||||||
|  			       &local->network_latency_notifier); |  			       &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, |  	pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY, | ||||||
|  			       &local->network_latency_notifier); |  			       &local->network_latency_notifier); | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| --- a/net/mac80211/cfg.c | --- a/net/mac80211/cfg.c | ||||||
| +++ b/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 |  		 * the  frames sent while scanning on other channel will be | ||||||
|  		 * lost) |  		 * 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
 | --- a/drivers/net/wireless/ath/ath9k/hw.c
 | ||||||
| +++ b/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; |  		return; | ||||||
|  	case AR9300_DEVID_QCA956X: |  	case AR9300_DEVID_QCA956X: | ||||||
|  		ah->hw_version.macVersion = AR_SREV_VERSION_9561; |  		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
 | --- a/drivers/net/wireless/ath/ath9k/recv.c
 | ||||||
| +++ b/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) && |  	if (!(ah->ah_flags & AH_UNPLUGGED) && | ||||||
|  	    unlikely(!stopped)) { |  	    unlikely(!stopped)) { | ||||||
| @@ -58,7 +58,7 @@ Signed-off-by: Felix Fietkau <nbd@openwrt.org> | |||||||
|  } |  } | ||||||
| --- a/drivers/net/wireless/ath/ath9k/xmit.c
 | --- a/drivers/net/wireless/ath/ath9k/xmit.c
 | ||||||
| +++ b/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); |  			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