[meta-freescale] [meta-fsl-arm-extra][PATCH 1/3] linux-apf6: add linux kernel for APF6 boards

Vincent Dehors vincent.dehors at gmail.com
Fri May 22 09:39:51 PDT 2015


There are two new kernel recipes compatible with Armadeus APF6 boards : the
standard version and RT one. These kernels are based on linux-wandboard code.

Most of the patchs applied over the linux-wandboard code come from the Armadeus
BSP (based on BR).

The default config allow to use most of the features available in APF6 dev boards.
---
 .../0001-Fix-Build-after-applying-rt-patch.patch   |    37 +
 .../0001-add-imx6-dtb-for-dual-and-quad-apf6.patch |   739 ++
 .../linux/linux-apf6-rt/0001-fix-build.patch       |    57 +
 ...se-pcie_set_read_rq-size-from-4096-to-256.patch |    25 +
 .../0002-fix-build-with-rt-enabled.patch           |    19 +
 .../linux-apf6-rt/0003-no-split-ptlocks.patch      |    84 +
 ...04-armadeus-add_sx8674_touchscreen_driver.patch |   510 +
 .../0505-armadeus-add_mcp7940x_rtc_driver.patch    |   524 +
 .../059X-armadeus-add-wireless-driver.patch        | 11643 +++++++++++++++++++
 recipes-kernel/linux/linux-apf6-rt/defconfig       |   409 +
 recipes-kernel/linux/linux-apf6-rt_3.10.53.bb      |    19 +
 recipes-kernel/linux/linux-apf6.inc                |    21 +
 .../0001-add-imx6-dtb-for-dual-and-quad-apf6.patch |   739 ++
 ...se-pcie_set_read_rq-size-from-4096-to-256.patch |    25 +
 ...04-armadeus-add_sx8674_touchscreen_driver.patch |   510 +
 .../0505-armadeus-add_mcp7940x_rtc_driver.patch    |   524 +
 .../059X-armadeus-add-wireless-driver.patch        | 11643 +++++++++++++++++++
 recipes-kernel/linux/linux-apf6/defconfig          |   409 +
 recipes-kernel/linux/linux-apf6_3.10.53.bb         |    10 +
 19 files changed, 27947 insertions(+)
 create mode 100644 recipes-kernel/linux/linux-apf6-rt/0001-Fix-Build-after-applying-rt-patch.patch
 create mode 100644 recipes-kernel/linux/linux-apf6-rt/0001-add-imx6-dtb-for-dual-and-quad-apf6.patch
 create mode 100644 recipes-kernel/linux/linux-apf6-rt/0001-fix-build.patch
 create mode 100644 recipes-kernel/linux/linux-apf6-rt/0002-decrease-pcie_set_read_rq-size-from-4096-to-256.patch
 create mode 100644 recipes-kernel/linux/linux-apf6-rt/0002-fix-build-with-rt-enabled.patch
 create mode 100644 recipes-kernel/linux/linux-apf6-rt/0003-no-split-ptlocks.patch
 create mode 100644 recipes-kernel/linux/linux-apf6-rt/0504-armadeus-add_sx8674_touchscreen_driver.patch
 create mode 100644 recipes-kernel/linux/linux-apf6-rt/0505-armadeus-add_mcp7940x_rtc_driver.patch
 create mode 100644 recipes-kernel/linux/linux-apf6-rt/059X-armadeus-add-wireless-driver.patch
 create mode 100644 recipes-kernel/linux/linux-apf6-rt/defconfig
 create mode 100644 recipes-kernel/linux/linux-apf6-rt_3.10.53.bb
 create mode 100644 recipes-kernel/linux/linux-apf6.inc
 create mode 100644 recipes-kernel/linux/linux-apf6/0001-add-imx6-dtb-for-dual-and-quad-apf6.patch
 create mode 100644 recipes-kernel/linux/linux-apf6/0002-decrease-pcie_set_read_rq-size-from-4096-to-256.patch
 create mode 100644 recipes-kernel/linux/linux-apf6/0504-armadeus-add_sx8674_touchscreen_driver.patch
 create mode 100644 recipes-kernel/linux/linux-apf6/0505-armadeus-add_mcp7940x_rtc_driver.patch
 create mode 100644 recipes-kernel/linux/linux-apf6/059X-armadeus-add-wireless-driver.patch
 create mode 100644 recipes-kernel/linux/linux-apf6/defconfig
 create mode 100644 recipes-kernel/linux/linux-apf6_3.10.53.bb

diff --git a/recipes-kernel/linux/linux-apf6-rt/0001-Fix-Build-after-applying-rt-patch.patch b/recipes-kernel/linux/linux-apf6-rt/0001-Fix-Build-after-applying-rt-patch.patch
new file mode 100644
index 0000000..640437b
--- /dev/null
+++ b/recipes-kernel/linux/linux-apf6-rt/0001-Fix-Build-after-applying-rt-patch.patch
@@ -0,0 +1,37 @@
+From 7d7002836d19bf12853857dbac96cff0aed877ca Mon Sep 17 00:00:00 2001
+From: Nicolas Aguirre <nicolas.aguirre at openwide.fr>
+Date: Fri, 20 Mar 2015 14:47:26 +0100
+Subject: [PATCH] Fix Build after applying rt patch
+
+---
+ arch/arm/mach-imx/clk-pfd.c   | 1 +
+ arch/arm/mach-imx/clk-pllv3.c | 1 +
+ 2 files changed, 2 insertions(+)
+
+diff --git a/arch/arm/mach-imx/clk-pfd.c b/arch/arm/mach-imx/clk-pfd.c
+index 1593dae..7e9416f 100644
+--- a/arch/arm/mach-imx/clk-pfd.c
++++ b/arch/arm/mach-imx/clk-pfd.c
+@@ -12,6 +12,7 @@
+ 
+ #include <linux/clk.h>
+ #include <linux/clk-provider.h>
++#include <linux/wait.h>
+ #include <linux/imx_sema4.h>
+ #include <linux/io.h>
+ #include <linux/slab.h>
+diff --git a/arch/arm/mach-imx/clk-pllv3.c b/arch/arm/mach-imx/clk-pllv3.c
+index 1e3d1e5..05c867a 100644
+--- a/arch/arm/mach-imx/clk-pllv3.c
++++ b/arch/arm/mach-imx/clk-pllv3.c
+@@ -12,6 +12,7 @@
+ 
+ #include <linux/clk.h>
+ #include <linux/clk-provider.h>
++#include <linux/wait.h>
+ #include <linux/imx_sema4.h>
+ #include <linux/io.h>
+ #include <linux/slab.h>
+-- 
+1.9.1
+
diff --git a/recipes-kernel/linux/linux-apf6-rt/0001-add-imx6-dtb-for-dual-and-quad-apf6.patch b/recipes-kernel/linux/linux-apf6-rt/0001-add-imx6-dtb-for-dual-and-quad-apf6.patch
new file mode 100644
index 0000000..4302f70
--- /dev/null
+++ b/recipes-kernel/linux/linux-apf6-rt/0001-add-imx6-dtb-for-dual-and-quad-apf6.patch
@@ -0,0 +1,739 @@
+From 354ce3d246a1b190004f3b72629d1f5356efac1f Mon Sep 17 00:00:00 2001
+From: Mallory Daries <mallory.daries at openwide.fr>
+Date: Tue, 10 Mar 2015 14:00:35 +0100
+Subject: [PATCH 1/2] add imx6 dtb for dual and quad apf6
+
+---
+ arch/arm/boot/dts/imx6dl-apf6dev.dts   |  24 ++
+ arch/arm/boot/dts/imx6q-apf6dev.dts    |  30 ++
+ arch/arm/boot/dts/imx6qdl-apf6.dtsi    | 132 +++++++++
+ arch/arm/boot/dts/imx6qdl-apf6dev.dtsi | 507 +++++++++++++++++++++++++++++++++
+ 4 files changed, 693 insertions(+)
+ create mode 100644 arch/arm/boot/dts/imx6dl-apf6dev.dts
+ create mode 100644 arch/arm/boot/dts/imx6q-apf6dev.dts
+ create mode 100644 arch/arm/boot/dts/imx6qdl-apf6.dtsi
+ create mode 100644 arch/arm/boot/dts/imx6qdl-apf6dev.dtsi
+
+diff --git a/arch/arm/boot/dts/imx6dl-apf6dev.dts b/arch/arm/boot/dts/imx6dl-apf6dev.dts
+new file mode 100644
+index 0000000..a875115
+--- /dev/null
++++ b/arch/arm/boot/dts/imx6dl-apf6dev.dts
+@@ -0,0 +1,24 @@
++/*
++ * Copyright 2015 Armadeus Systems
++ *
++ * Author: Armadeus Systems <support at armadeus.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ */
++
++/dts-v1/;
++#include "imx6dl.dtsi"
++#include "imx6qdl-apf6.dtsi"
++#include "imx6qdl-apf6dev.dtsi"
++
++/ {
++	model = "Armadeus APF6 i.MX6 Dual Lite Module on APF6Dev Board";
++	compatible = "armadeus,imx6dl-apf6dev", "armadeus,imx6dl-apf6", "fsl,imx6dl";
++
++	memory {
++		reg = <0x10000000 0x40000000>;
++	};
++};
+diff --git a/arch/arm/boot/dts/imx6q-apf6dev.dts b/arch/arm/boot/dts/imx6q-apf6dev.dts
+new file mode 100644
+index 0000000..7d63a14
+--- /dev/null
++++ b/arch/arm/boot/dts/imx6q-apf6dev.dts
+@@ -0,0 +1,30 @@
++/*
++ * Copyright 2015 Armadeus Systems
++ *
++ * Author: Armadeus Systems <support at armadeus.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ */
++
++/dts-v1/;
++#include "imx6q.dtsi"
++#include "imx6qdl-apf6.dtsi"
++#include "imx6qdl-apf6dev.dtsi"
++
++/ {
++	model = "Armadeus APF6 i.MX6 Quad / Dual Module on APF6Dev Board";
++	compatible = "armadeus,imx6q-apf6dev", "armadeus,imx6q-apf6", "fsl,imx6q";
++
++	memory {
++		reg = <0x10000000 0x80000000>;
++	};
++};
++
++
++&sata {
++	status = "okay";
++};
++
+diff --git a/arch/arm/boot/dts/imx6qdl-apf6.dtsi b/arch/arm/boot/dts/imx6qdl-apf6.dtsi
+new file mode 100644
+index 0000000..9932d73
+--- /dev/null
++++ b/arch/arm/boot/dts/imx6qdl-apf6.dtsi
+@@ -0,0 +1,132 @@
++/*
++ * Copyright 2015 Armadeus Systems
++ *
++ * Author: Armadeus Systems <support at armadeus.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ */
++
++#include <dt-bindings/interrupt-controller/irq.h>
++
++/ {
++	wlan {
++		compatible = "ti,wilink6";
++		interrupt-parent = <&gpio2>;
++		interrupts = <10 IRQ_TYPE_LEVEL_HIGH>; /* gpio line 42, high level triggered */
++		clocks = <&refclock>, <&tcxoclock>;
++		clock-names = "refclock", "tcxoclock";
++
++		refclock: refclock {
++			compatible = "ti,wilink-clock";
++			#clock-cells = <0>;
++			clock-frequency = <38400000>;
++		};
++
++		tcxoclock: tcxoclock {
++			compatible = "ti,wilink-clock";
++			#clock-cells = <0>;
++			clock-frequency = <38400000>;
++	  	};
++	};
++};
++
++&fec {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_enet>;
++	phy-mode = "rgmii";
++	//phy-reset-duration = <10>;
++	//phy-reset-gpios = <&gpio1 24 GPIO_ACTIVE_HIGH>;
++	status = "okay";
++};
++
++/* Bluetooth */
++&uart2 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_uart2>;
++	status = "okay";
++};
++
++/* Wi-Fi */
++&usdhc1 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_usdhc1>;
++	non-removable;
++	status = "okay";
++};
++
++/* eMMC */
++&usdhc3 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_usdhc3>;
++	bus-width = <8>;
++	no-1-8-v;
++	non-removable;
++	status = "okay";
++};
++
++&iomuxc {
++	apf6 {
++		pinctrl_enet: enetgrp {
++			fsl,pins = <
++				MX6QDL_PAD_ENET_MDIO__ENET_MDIO		0x1b0b0
++				MX6QDL_PAD_ENET_MDC__ENET_MDC		0x1b0b0
++				MX6QDL_PAD_RGMII_TXC__RGMII_TXC		0x1b030
++				MX6QDL_PAD_RGMII_TD0__RGMII_TD0		0x1b030
++				MX6QDL_PAD_RGMII_TD1__RGMII_TD1		0x1b030
++				MX6QDL_PAD_RGMII_TD2__RGMII_TD2		0x1b030
++				MX6QDL_PAD_RGMII_TD3__RGMII_TD3		0x1b030
++				MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL	0x1b030
++				MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK	0x1b0b0
++				MX6QDL_PAD_RGMII_RXC__RGMII_RXC		0x1b030
++				MX6QDL_PAD_RGMII_RD0__RGMII_RD0		0x1b030
++				MX6QDL_PAD_RGMII_RD1__RGMII_RD1		0x1b030
++				MX6QDL_PAD_RGMII_RD2__RGMII_RD2		0x1f030
++				MX6QDL_PAD_RGMII_RD3__RGMII_RD3		0x1f030
++				MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL	0x1b030
++				MX6QDL_PAD_ENET_RX_ER__GPIO1_IO24	0x80000000
++				MX6QDL_PAD_ENET_TX_EN__GPIO1_IO28	0x80000000
++			>;
++		};
++
++		pinctrl_uart2: uart2grp {
++			fsl,pins = <
++				MX6QDL_PAD_SD4_DAT4__UART2_RX_DATA	0x1b0b1
++				MX6QDL_PAD_SD4_DAT5__UART2_RTS_B	0x1b0b1
++				MX6QDL_PAD_SD4_DAT6__UART2_CTS_B	0x1b0b1
++				MX6QDL_PAD_SD4_DAT7__UART2_TX_DATA	0x1b0b1
++				MX6QDL_PAD_SD4_DAT3__GPIO2_IO11		0x80000000 /* BT_EN */
++			>;
++		};
++
++		pinctrl_usdhc1: usdhc1grp {
++			fsl,pins = <
++				MX6QDL_PAD_SD1_CMD__SD1_CMD	0x17059
++				MX6QDL_PAD_SD1_CLK__SD1_CLK	0x10059
++				MX6QDL_PAD_SD1_DAT0__SD1_DATA0	0x17059
++				MX6QDL_PAD_SD1_DAT1__SD1_DATA1	0x17059
++				MX6QDL_PAD_SD1_DAT2__SD1_DATA2	0x17059
++				MX6QDL_PAD_SD1_DAT3__SD1_DATA3	0x17059
++				MX6QDL_PAD_SD4_DAT0__GPIO2_IO08	0x80000000 /* WL_EN */
++				MX6QDL_PAD_SD4_DAT2__GPIO2_IO10	0x80000000 /* WL_IRQ */
++			>;
++		};
++
++		pinctrl_usdhc3: usdhc3grp {
++			fsl,pins = <
++				MX6QDL_PAD_SD3_CMD__SD3_CMD	0x17059
++				MX6QDL_PAD_SD3_CLK__SD3_CLK	0x10059
++				MX6QDL_PAD_SD3_DAT0__SD3_DATA0	0x17059
++				MX6QDL_PAD_SD3_DAT1__SD3_DATA1	0x17059
++				MX6QDL_PAD_SD3_DAT2__SD3_DATA2	0x17059
++				MX6QDL_PAD_SD3_DAT3__SD3_DATA3	0x17059
++				MX6QDL_PAD_SD3_DAT4__SD3_DATA4	0x17059
++				MX6QDL_PAD_SD3_DAT5__SD3_DATA5	0x17059
++				MX6QDL_PAD_SD3_DAT6__SD3_DATA6	0x17059
++				MX6QDL_PAD_SD3_DAT7__SD3_DATA7	0x17059
++			>;
++		};
++	};
++};
+diff --git a/arch/arm/boot/dts/imx6qdl-apf6dev.dtsi b/arch/arm/boot/dts/imx6qdl-apf6dev.dtsi
+new file mode 100644
+index 0000000..a810e9b
+--- /dev/null
++++ b/arch/arm/boot/dts/imx6qdl-apf6dev.dtsi
+@@ -0,0 +1,510 @@
++/*
++ * Copyright 2015 Armadeus Systems
++ *
++ * Author: Armadeus Systems <support at armadeus.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ */
++
++/ {
++	aliases {
++		mxcfb0 = &mxcfb3;
++	};
++
++	gpio-keys {
++		compatible = "gpio-keys";
++		pinctrl-names = "default";
++		pinctrl-0 = <&pinctrl_gpio_keys>;
++
++		user-button {
++			label = "User button";
++			gpios = <&gpio1 9 GPIO_ACTIVE_LOW>;
++			linux,code = <0x100>;
++			gpio-key,wakeup;
++		};
++	};
++
++	gpio-leds {
++		compatible = "gpio-leds";
++		pinctrl-names = "default";
++		pinctrl-0 = <&pinctrl_gpio_leds>;
++
++		user-led {
++			label = "User LED";
++			gpios = <&gpio7 12 GPIO_ACTIVE_HIGH>;
++			linux,default-trigger = "heartbeat";
++			default-state = "on";
++		};
++	};
++
++	mxcfb1: fb at 0 {
++		compatible = "fsl,mxc_sdc_fb";
++		disp_dev = "hdmi";
++		interface_pix_fmt = "RGB24";
++		mode_str ="1920x1080M at 60";
++		default_bpp = <24>;
++		int_clk = <0>;
++		late_init = <0>;
++		status = "okay";
++	};
++
++	mxcfb3: fb at 2 {
++		compatible = "fsl,mxc_sdc_fb";
++		disp_dev = "lcd";
++		interface_pix_fmt = "RGB666";
++		mode_str ="SEIKO-WVGA";
++		default_bpp = <18>;
++		int_clk = <0>;
++		late_init = <0>;
++		status = "okay";
++	};
++
++	lcd at 0 {
++		compatible = "fsl,lcd";
++		ipu_id = <0>;
++		disp_id = <0>;
++		default_ifmt = "RGB666";
++		pinctrl-names = "default";
++		pinctrl-0 = <&pinctrl_ipu1>;
++		status = "okay";
++	};
++
++	backlight_lcd {
++		compatible = "pwm-backlight";
++		pwms = <&pwm3 0 5000000>;
++		brightness-levels = <0 4 8 16 32 64 128 255>;
++		default-brightness-level = <6>;
++	};
++
++	sound {
++		compatible = "fsl,imx6-armadeus-sgtl5000",
++			   "fsl,imx-audio-sgtl5000";
++		model = "imx6-armadeus-sgtl5000";
++		cpu-dai = <&ssi1>;
++		audio-codec = <&codec>;
++		audio-routing =
++			"MIC_IN", "Mic Jack",
++			"Mic Jack", "Mic Bias",
++			"Headphone Jack", "HP_OUT";
++		mux-int-port = <1>;
++		mux-ext-port = <3>;
++	};
++
++	sound-spdif {
++		compatible = "fsl,imx-audio-spdif";
++		model = "imx-spdif";
++		spdif-controller = <&spdif>;
++		spdif-out;
++	};
++
++	sound-hdmi {
++		compatible = "fsl,imx6q-audio-hdmi",
++			   "fsl,imx-audio-hdmi";
++		model = "imx-audio-hdmi";
++		hdmi-controller = <&hdmi_audio>;
++	};
++
++	regulators {
++		compatible = "simple-bus";
++
++		reg_1p8v: 1p8v {
++			compatible = "regulator-fixed";
++			regulator-name = "1P8V";
++			regulator-min-microvolt = <1800000>;
++			regulator-max-microvolt = <1800000>;
++			regulator-always-on;
++		};
++
++		reg_2p5v: 2p5v {
++			compatible = "regulator-fixed";
++			regulator-name = "2P5V";
++			regulator-min-microvolt = <2500000>;
++			regulator-max-microvolt = <2500000>;
++			regulator-always-on;
++		};
++
++		reg_3p3v: 3p3v {
++			compatible = "regulator-fixed";
++			regulator-name = "3P3V";
++			regulator-min-microvolt = <3300000>;
++			regulator-max-microvolt = <3300000>;
++			regulator-always-on;
++		};
++
++		reg_usb_otg_vbus: usb_otg_vbus {
++			compatible = "regulator-fixed";
++			regulator-name = "usb_otg_vbus";
++			regulator-min-microvolt = <5000000>;
++			regulator-max-microvolt = <5000000>;
++			regulator-always-on;
++		};
++	};
++
++	v4l2_cap_0 {
++		compatible = "fsl,imx6q-v4l2-capture";
++		ipu_id = <0>;
++		csi_id = <0>;
++		mclk_source = <0>;
++		status = "okay";
++	};
++
++	v4l2_out {
++		compatible = "fsl,mxc_v4l2_output";
++		status = "okay";
++	};
++};
++
++&audmux {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_audmux>;
++	status = "okay";
++};
++
++&flexcan2 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_flexcan2>;
++	status = "okay";
++};
++
++&ecspi1 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_ecspi1>;
++	fsl,spi-num-chipselects = <3>;
++	cs-gpios = <&gpio4 9 GPIO_ACTIVE_HIGH>,
++		   <&gpio4 10 GPIO_ACTIVE_HIGH>,
++		   <&gpio4 11 GPIO_ACTIVE_HIGH>;
++	status = "okay";
++};
++
++&hdmi_audio {
++	status = "okay";
++};
++
++&hdmi_core {
++	ipu_id = <0>;
++	disp_id = <0>;
++	status = "okay";
++};
++
++&hdmi_video {
++	fsl,phy_reg_vlev = <0x0294>;
++	fsl,phy_reg_cksymtx = <0x800d>;
++	status = "okay";
++};
++
++
++&i2c1 {
++	clock-frequency = <400000>;
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_i2c1>;
++	status = "okay";
++
++	touchscreen at 48 {
++		compatible = "semtech,sx8654";
++		reg = <0x48>;
++		pinctrl-names = "default";
++		pinctrl-0 = <&pinctrl_touchscreen>;
++		interrupt-parent = <&gpio6>;
++		interrupts = <3 IRQ_TYPE_EDGE_FALLING>;
++		x-plate-ohms = <1100>;
++		y-plate-ohms = <500>;
++		min-x = <0>;
++		max-x = <800>;
++		min-y = <0>;
++		max-y = <480>;
++	};
++};
++
++&i2c2 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_i2c2>;
++	clock-frequency = <400000>;
++	status = "okay";
++
++	rtc at 6f {
++		compatible = "microchip,mcp7940x-rtc";
++		reg = <0x6F>;
++	};
++
++	codec: sgtl5000 at 0a {
++		compatible = "fsl,sgtl5000";
++		reg = <0x0a>;
++		clocks = <&clks 201>;
++		VDDA-supply = <&reg_3p3v>;
++		VDDIO-supply = <&reg_3p3v>;
++	};
++};
++
++&i2c3 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_i2c3>;
++	clock-frequency = <400000>;
++	status = "okay";
++
++	hdmi: edid at 50 {
++		compatible = "fsl,imx6-hdmi-i2c";
++		reg = <0x50>;
++	};
++
++};
++
++&mipi_csi {
++	ipu_id = <0>;
++	csi_id = <0>;
++	v_channel = <0>;
++	lanes = <2>;
++	status = "okay";
++};
++
++&pcie {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_pcie>;
++	reset-gpio = <&gpio6 2 GPIO_ACTIVE_HIGH>;
++	status = "okay";
++};
++
++&pwm3 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_pwm3_1>;
++	status = "okay";
++};
++
++&spdif {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_spdif>;
++	status = "okay";
++};
++
++&ssi1 {
++	fsl,mode = "i2s-slave";
++	status = "okay";
++};
++
++/* GPS */
++&uart1 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_uart1>;
++	status = "okay";
++};
++
++/* GSM */
++&uart3 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_uart3 &pinctrl_gsm>;
++	fsl,uart-has-rtscts;
++	status = "okay";
++};
++
++/* console */
++&uart4 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_uart4>;
++	status = "okay";
++};
++
++&usbh1 {
++	dr_mode = "host";
++	phy_type = "utmi";
++	status = "okay";
++};
++
++&usbotg {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_usbotg>;
++	vbus-supply = <&reg_usb_otg_vbus>;
++	dr_mode = "otg";
++	status = "okay";
++};
++
++/* microSD */
++&usdhc2 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_usdhc2>;
++	bus-width = <4>;
++	cd-gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>;
++	status = "okay";
++};
++
++&iomuxc {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_gpios>;
++
++	apf6dev {
++		pinctrl_audmux: audmuxgrp {
++			fsl,pins = <
++				MX6QDL_PAD_CSI0_DAT7__AUD3_RXD  0x80000000
++				MX6QDL_PAD_CSI0_DAT4__AUD3_TXC  0x80000000
++				MX6QDL_PAD_CSI0_DAT5__AUD3_TXD  0x80000000
++				MX6QDL_PAD_CSI0_DAT6__AUD3_TXFS 0x80000000
++				MX6QDL_PAD_GPIO_0__CCM_CLKO1    0x130b0
++			>;
++		};
++
++		pinctrl_camera_ov5640: cameragrp {
++			fsl,pins = <
++				MX6QDL_PAD_GPIO_3__XTALOSC_REF_CLK_24M 0x1b0b0 /* CSI_MCLK */
++				MX6QDL_PAD_CSI0_MCLK__GPIO5_IO19       0x80000000
++				MX6QDL_PAD_CSI0_DATA_EN__GPIO5_IO20    0x80000000
++			>;
++		};
++
++		pinctrl_ipu1: ipu1grp-1 {
++			fsl,pins = <
++				MX6QDL_PAD_DI0_DISP_CLK__IPU1_DI0_DISP_CLK 0x10
++				MX6QDL_PAD_DI0_PIN15__IPU1_DI0_PIN15       0x10
++				MX6QDL_PAD_DI0_PIN2__IPU1_DI0_PIN02        0x10
++				MX6QDL_PAD_DI0_PIN3__IPU1_DI0_PIN03        0x10
++				MX6QDL_PAD_DI0_PIN4__IPU1_DI0_PIN04        0x80000000
++				MX6QDL_PAD_DISP0_DAT0__IPU1_DISP0_DATA00   0x10
++				MX6QDL_PAD_DISP0_DAT1__IPU1_DISP0_DATA01   0x10
++				MX6QDL_PAD_DISP0_DAT2__IPU1_DISP0_DATA02   0x10
++				MX6QDL_PAD_DISP0_DAT3__IPU1_DISP0_DATA03   0x10
++				MX6QDL_PAD_DISP0_DAT4__IPU1_DISP0_DATA04   0x10
++				MX6QDL_PAD_DISP0_DAT5__IPU1_DISP0_DATA05   0x10
++				MX6QDL_PAD_DISP0_DAT6__IPU1_DISP0_DATA06   0x10
++				MX6QDL_PAD_DISP0_DAT7__IPU1_DISP0_DATA07   0x10
++				MX6QDL_PAD_DISP0_DAT8__IPU1_DISP0_DATA08   0x10
++				MX6QDL_PAD_DISP0_DAT9__IPU1_DISP0_DATA09   0x10
++				MX6QDL_PAD_DISP0_DAT10__IPU1_DISP0_DATA10  0x10
++				MX6QDL_PAD_DISP0_DAT11__IPU1_DISP0_DATA11  0x10
++				MX6QDL_PAD_DISP0_DAT12__IPU1_DISP0_DATA12  0x10
++				MX6QDL_PAD_DISP0_DAT13__IPU1_DISP0_DATA13  0x10
++				MX6QDL_PAD_DISP0_DAT14__IPU1_DISP0_DATA14  0x10
++				MX6QDL_PAD_DISP0_DAT15__IPU1_DISP0_DATA15  0x10
++				MX6QDL_PAD_DISP0_DAT16__IPU1_DISP0_DATA16  0x10
++				MX6QDL_PAD_DISP0_DAT17__IPU1_DISP0_DATA17  0x10
++			>;
++		};
++
++		pinctrl_ecspi1: ecspi1grp {
++			fsl,pins = <
++				MX6QDL_PAD_KEY_COL1__ECSPI1_MISO 0x100b1
++				MX6QDL_PAD_KEY_ROW0__ECSPI1_MOSI 0x100b1
++				MX6QDL_PAD_KEY_COL0__ECSPI1_SCLK 0x100b1
++				MX6QDL_PAD_KEY_ROW1__GPIO4_IO09  0x80000000
++				MX6QDL_PAD_KEY_ROW2__GPIO4_IO11  0x80000000
++				MX6QDL_PAD_KEY_COL2__GPIO4_IO10  0x80000000
++			>;
++		};
++
++		pinctrl_flexcan2: flexcan2grp {
++			fsl,pins = <
++				MX6QDL_PAD_KEY_COL4__FLEXCAN2_TX 0x80000000
++				MX6QDL_PAD_KEY_ROW4__FLEXCAN2_RX 0x80000000
++			>;
++		};
++
++		pinctrl_gpio_keys: gpiokeysgrp {
++			fsl,pins = <
++				MX6QDL_PAD_GPIO_9__GPIO1_IO09 0x1b0b0
++			>;
++		};
++		pinctrl_gpio_leds: gpioledsgrp {
++			fsl,pins = <
++				MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x80000000
++			>;
++		};
++
++		pinctrl_gpios: gpiosgrp {
++			fsl,pins = <
++				MX6QDL_PAD_DISP0_DAT18__GPIO5_IO12      0x80000000
++				MX6QDL_PAD_DISP0_DAT19__GPIO5_IO13      0x80000000
++				MX6QDL_PAD_DISP0_DAT20__GPIO5_IO14      0x80000000
++				MX6QDL_PAD_DISP0_DAT21__GPIO5_IO15      0x80000000
++				MX6QDL_PAD_DISP0_DAT22__GPIO5_IO16      0x80000000
++				MX6QDL_PAD_DISP0_DAT23__GPIO5_IO17      0x80000000
++				MX6QDL_PAD_CSI0_PIXCLK__GPIO5_IO18      0x80000000
++				MX6QDL_PAD_CSI0_VSYNC__GPIO5_IO21       0x80000000
++			>;
++		};
++
++		pinctrl_gsm: gsmgrp {
++			fsl,pins = <
++				MX6QDL_PAD_GPIO_4__GPIO1_IO04  0x80000000 /* GSM_POKIN */
++				MX6QDL_PAD_GPIO_18__GPIO7_IO13 0x80000000 /* GSM_PWR_EN */
++			>;
++		};
++
++		pinctrl_i2c1: i2c1grp {
++			fsl,pins = <
++				MX6QDL_PAD_CSI0_DAT8__I2C1_SDA 0x4001b8b1
++				MX6QDL_PAD_CSI0_DAT9__I2C1_SCL 0x4001b8b1
++			>;
++		};
++
++		pinctrl_i2c2: i2c2grp {
++			fsl,pins = <
++				MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1
++				MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1
++			>;
++		};
++
++		pinctrl_i2c3: i2c3grp {
++			fsl,pins = <
++				MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1
++				MX6QDL_PAD_GPIO_5__I2C3_SCL 0x4001b8b1
++			>;
++		};
++
++		pinctrl_pcie: pciegrp {
++			fsl,pins = <
++				MX6QDL_PAD_CSI0_DAT16__GPIO6_IO02 0x80000000
++			>;
++		};
++
++		pinctrl_uart1: uart1grp {
++			fsl,pins = <
++				MX6QDL_PAD_CSI0_DAT10__UART1_TX_DATA 0x1b0b1
++				MX6QDL_PAD_CSI0_DAT11__UART1_RX_DATA 0x1b0b1
++			>;
++		};
++
++		pinctrl_uart3: uart3grp {
++			fsl,pins = <
++				MX6QDL_PAD_EIM_D23__UART3_CTS_B   0x1b0b1
++				MX6QDL_PAD_EIM_D24__UART3_TX_DATA 0x1b0b1
++				MX6QDL_PAD_EIM_D25__UART3_RX_DATA 0x1b0b1
++				MX6QDL_PAD_EIM_D31__UART3_RTS_B   0x1b0b1
++			>;
++		};
++
++		pinctrl_uart4: uart4grp {
++			fsl,pins = <
++				MX6QDL_PAD_CSI0_DAT12__UART4_TX_DATA 0x1b0b1
++				MX6QDL_PAD_CSI0_DAT13__UART4_RX_DATA 0x1b0b1
++			>;
++		};
++
++		pinctrl_usbotg: usbotggrp {
++			fsl,pins = <
++				MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059
++			>;
++		};
++
++		pinctrl_usdhc2: usdhc2grp {
++			fsl,pins = <
++				MX6QDL_PAD_SD2_CMD__SD2_CMD    0x17059
++				MX6QDL_PAD_SD2_CLK__SD2_CLK    0x10059
++				MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x17059
++				MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x17059
++				MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x17059
++				MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x17059
++			>;
++		};
++
++		pinctrl_spdif: spdifgrp {
++			fsl,pins = <
++				MX6QDL_PAD_GPIO_19__SPDIF_OUT 0x1b0b0
++			>;
++		};
++
++		pinctrl_touchscreen: touchscreengrp {
++			fsl,pins = <
++				MX6QDL_PAD_CSI0_DAT17__GPIO6_IO03 0x80000000
++			>;
++		};
++	};
++};
+-- 
+1.9.1
+
diff --git a/recipes-kernel/linux/linux-apf6-rt/0001-fix-build.patch b/recipes-kernel/linux/linux-apf6-rt/0001-fix-build.patch
new file mode 100644
index 0000000..ffc6b74
--- /dev/null
+++ b/recipes-kernel/linux/linux-apf6-rt/0001-fix-build.patch
@@ -0,0 +1,57 @@
+Fix build errors when RT patch is applied
+
+Upstream-Status: Inappropriate [other]
+Freescale does not support the RT patch
+
+Signed-off-by: Jacob Kroon <jacob.kroon at mikrodidakt.se>
+
+Index: git/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_os.c
+===================================================================
+--- git.orig/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_os.c
++++ git/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_os.c
+@@ -7746,7 +7746,7 @@ gckOS_WaitSignal(
+ 
+     might_sleep();
+ 
+-    spin_lock_irq(&signal->obj.wait.lock);
++    raw_spin_lock_irq(&signal->obj.wait.lock);
+ 
+     if (signal->obj.done)
+     {
+@@ -7768,9 +7768,8 @@ gckOS_WaitSignal(
+             ? MAX_SCHEDULE_TIMEOUT
+             : Wait * HZ / 1000;
+ 
+-        DECLARE_WAITQUEUE(wait, current);
+-        wait.flags |= WQ_FLAG_EXCLUSIVE;
+-        __add_wait_queue_tail(&signal->obj.wait, &wait);
++        DEFINE_SWAITER(wait);
++        swait_prepare_locked(&signal->obj.wait, &wait);
+ 
+         while (gcvTRUE)
+         {
+@@ -7782,9 +7781,9 @@ gckOS_WaitSignal(
+             }
+ 
+             __set_current_state(TASK_INTERRUPTIBLE);
+-            spin_unlock_irq(&signal->obj.wait.lock);
++            raw_spin_unlock_irq(&signal->obj.wait.lock);
+             timeout = schedule_timeout(timeout);
+-            spin_lock_irq(&signal->obj.wait.lock);
++            raw_spin_lock_irq(&signal->obj.wait.lock);
+ 
+             if (signal->obj.done)
+             {
+@@ -7805,10 +7804,10 @@ gckOS_WaitSignal(
+             }
+         }
+ 
+-        __remove_wait_queue(&signal->obj.wait, &wait);
++        swait_finish_locked(&signal->obj.wait, &wait);
+     }
+ 
+-    spin_unlock_irq(&signal->obj.wait.lock);
++    raw_spin_unlock_irq(&signal->obj.wait.lock);
+ 
+ OnError:
+     /* Return status. */
diff --git a/recipes-kernel/linux/linux-apf6-rt/0002-decrease-pcie_set_read_rq-size-from-4096-to-256.patch b/recipes-kernel/linux/linux-apf6-rt/0002-decrease-pcie_set_read_rq-size-from-4096-to-256.patch
new file mode 100644
index 0000000..b980d9d
--- /dev/null
+++ b/recipes-kernel/linux/linux-apf6-rt/0002-decrease-pcie_set_read_rq-size-from-4096-to-256.patch
@@ -0,0 +1,25 @@
+From 90cf8e084b81a0a66a5765f8f20f237797a19336 Mon Sep 17 00:00:00 2001
+From: Mallory Daries <mallory.daries at openwide.fr>
+Date: Tue, 10 Mar 2015 14:10:36 +0100
+Subject: [PATCH 2/2] decrease pcie_set_read_rq size from 4096 to 256
+
+---
+ drivers/ata/sata_sil24.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c
+index 59f0d63..e728b8e 100644
+--- a/drivers/ata/sata_sil24.c
++++ b/drivers/ata/sata_sil24.c
+@@ -1336,7 +1336,7 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+ 	/* Set max read request size to 4096.  This slightly increases
+ 	 * write throughput for pci-e variants.
+ 	 */
+-	pcie_set_readrq(pdev, 4096);
++	pcie_set_readrq(pdev, 1024);
+ 
+ 	sil24_init_controller(host);
+ 
+-- 
+1.9.1
+
diff --git a/recipes-kernel/linux/linux-apf6-rt/0002-fix-build-with-rt-enabled.patch b/recipes-kernel/linux/linux-apf6-rt/0002-fix-build-with-rt-enabled.patch
new file mode 100644
index 0000000..83652c9
--- /dev/null
+++ b/recipes-kernel/linux/linux-apf6-rt/0002-fix-build-with-rt-enabled.patch
@@ -0,0 +1,19 @@
+Fix build error when PREEMPT_RT_FULL is enabled
+
+Upstream-Status: Pending
+
+Signed-off-by: Jacob Kroon <jacob.kroon at mikrodidakt.se>
+
+Index: git/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_os.c
+===================================================================
+--- git.orig/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_os.c
++++ git/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_os.c
+@@ -3266,7 +3266,7 @@ gckOS_CreateMutex(
+     gcmkONERROR(gckOS_Allocate(Os, gcmSIZEOF(struct mutex), Mutex));
+ 
+     /* Initialize the mutex. */
+-    mutex_init(*Mutex);
++    mutex_init((struct mutex*)*Mutex);
+ 
+     /* Return status. */
+     gcmkFOOTER_ARG("*Mutex=0x%X", *Mutex);
diff --git a/recipes-kernel/linux/linux-apf6-rt/0003-no-split-ptlocks.patch b/recipes-kernel/linux/linux-apf6-rt/0003-no-split-ptlocks.patch
new file mode 100644
index 0000000..b7995b6
--- /dev/null
+++ b/recipes-kernel/linux/linux-apf6-rt/0003-no-split-ptlocks.patch
@@ -0,0 +1,84 @@
+Work around the oops below by disabling split ptlocks
+
+Upstream-Status: Inappropriate [disable feature]
+
+Signed-off-by: Jacob Kroon <jacob.kroon at mikrodidakt.se>
+
+Unable to handle kernel NULL pointer dereference at virtual address 00000000
+pgd = ac5a8000
+[00000000] *pgd=3c8db831, *pte=00000000, *ppte=00000000
+Internal error: Oops: 17 [#1] PREEMPT SMP ARM
+Modules linked in:
+CPU: 1 PID: 277 Comm: test-qt5 Not tainted 3.10.17-rt12-monkey+gec1af9f #2
+task: ac65df80 ti: ac968000 task.ti: ac968000
+PC is at _raw_spin_lock+0x10/0x4c
+LR is at get_parent_ip+0x10/0x2c
+pc : [<80623fd4>]    lr : [<8004ef18>]    psr: 60010013
+sp : ac969c38  ip : 80c44404  fp : 00000000
+r10: ac65df80  r9 : ac969cd8  r8 : 00000000
+r7 : 00000054  r6 : afffe000  r5 : 00000000  r4 : 00000000
+r3 : ac65df80  r2 : 00000001  r1 : 00000000  r0 : 00000000
+Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user
+Control: 10c53c7d  Table: 3c5a804a  DAC: 00000015
+Process test-qt5 (pid: 277, stack limit = 0xac968238)
+Stack: (0xac969c38 to 0xac96a000)
+9c20:                                                       b00549a8 806235e0
+9c40: 00000000 ac968000 812f9a00 80017cec ac969c74 fffffffe 00000000 00005000
+9c60: 00000000 80017e6c ac968000 ac65df80 00000000 00000000 00000001 ac968001
+9c80: ac65df80 b00549a8 00000000 afffe000 00000054 00000000 ac969cd8 afffe150
+9ca0: b0054bf8 8044e540 ac969cd8 b00549a8 ac0b8180 00000230 ac8fd000 00000230
+9cc0: 00000000 8044f6f4 ac155700 ac969cf4 ac155700 804605b8 ac155700 ac969d8c
+9ce0: 00000002 ac969d24 b0054bf8 804607f4 7ef1fcd0 80450078 ac0b8180 ac155700
+9d00: ac969d8c 00000002 ac8fd000 8045f1a0 ac969d24 00000000 ac8fd01c b00549a8
+9d20: b0050000 00000008 ac969d98 ac0b8300 ac969d8c 00000115 ac969d98 ac969e00
+9d40: ac155600 ac3985f0 7ef1fcd0 8045661c 00000115 00000000 00000001 00000000
+9d60: 00007530 80454144 80c32e18 00000001 81518e18 806237dc 00000001 ac968038
+9d80: 00000000 00000000 00000000 00000000 00000115 806237dc ac6f5688 ac968018
+9da0: 812e1640 00000000 00000001 ac968000 ac969dc4 8004efe4 ac968008 ac39a000
+9dc0: ac744400 ac968020 00007530 ac968000 ac968000 ac3985f0 7ef1fcd0 8044d374
+9de0: 7ef1fd20 00000000 000000f8 00000000 7ef1fd20 00000000 000000f8 00000000
+9e00: 00000028 00000002 00000000 6437d1dd 7ef1fddc 76f9c030 7ef1fd88 76fb9abc
+9e20: 00000320 00005007 e02c7eca c1399eff 020fb2db 00000000 00000000 00000000
+9e40: 00000001 00000040 00000400 00000004 00000010 00000400 00000002 00000100
+9e60: 00000100 00000008 00000008 00000000 ffffffff 00000000 7588e180 76fb1000
+9e80: 08000000 00000000 00000000 76fb11b8 000000af 760b6cd8 ffffffff 7431d4c0
+9ea0: 000cb1ac 75889000 00000000 00000001 000000b0 00000000 00000000 76fa0da0
+9ec0: 00000000 00000001 00000001 00000000 00000000 7588cb40 00000001 75957ecc
+9ee0: 00000001 7ef1fe7c 75953e94 7ef1fedc 00000002 76fa789c 00007530 ac6813c0
+9f00: 00000000 7ef1fcd0 ac6813c0 ac968000 ac3985f0 800d68fc 00000001 ac968000
+9f20: ac969f34 8004efe4 ac4f71d4 6c31d000 00000000 80623fd4 00000000 80623578
+9f40: ac4f71d4 800a3d40 00000001 00000000 ac969f5c 00000003 ac6813c8 00000000
+9f60: 00000001 00007530 00000003 00000000 7ef1fcd0 ac6813c0 ac968000 00000000
+9f80: 00000000 800d6afc ac4f7180 00000000 00002710 00007530 75957e40 00000036
+9fa0: 8000e2c4 8000e140 00002710 00007530 00000003 00007530 7ef1fcd0 01bc3008
+9fc0: 00002710 00007530 75957e40 00000036 01bd2e70 00000001 7ef1fd20 00000000
+9fe0: 759540c4 7ef1fcbc 7593f698 760a0ccc 20010010 00000003 00000000 00000000
+[<80623fd4>] (_raw_spin_lock+0x10/0x4c) from [<806235e0>] (rt_spin_lock_slowlock+0x34/0x29c)
+[<806235e0>] (rt_spin_lock_slowlock+0x34/0x29c) from [<8044e540>] (_QueryProcessPageTable+0x8c/0xdc)
+[<8044e540>] (_QueryProcessPageTable+0x8c/0xdc) from [<8044f6f4>] (gckOS_GetPhysicalAddress+0x20/0x58)
+[<8044f6f4>] (gckOS_GetPhysicalAddress+0x20/0x58) from [<804605b8>] (gckHARDWARE_ConvertLogical+0x2c/0x90)
+[<804605b8>] (gckHARDWARE_ConvertLogical+0x2c/0x90) from [<804607f4>] (gckHARDWARE_Link+0x60/0x104)
+[<804607f4>] (gckHARDWARE_Link+0x60/0x104) from [<8045f1a0>] (gckCONTEXT_Construct+0x264/0x2bc)
+[<8045f1a0>] (gckCONTEXT_Construct+0x264/0x2bc) from [<8045661c>] (gckCOMMAND_Attach+0x44/0x88)
+[<8045661c>] (gckCOMMAND_Attach+0x44/0x88) from [<80454144>] (gckKERNEL_Dispatch+0x358/0x12c8)
+[<80454144>] (gckKERNEL_Dispatch+0x358/0x12c8) from [<8044d374>] (drv_ioctl+0x120/0x284)
+[<8044d374>] (drv_ioctl+0x120/0x284) from [<800d68fc>] (do_vfs_ioctl+0x408/0x5d0)
+[<800d68fc>] (do_vfs_ioctl+0x408/0x5d0) from [<800d6afc>] (SyS_ioctl+0x38/0x64)
+[<800d6afc>] (SyS_ioctl+0x38/0x64) from [<8000e140>] (ret_fast_syscall+0x0/0x30)
+Code: e92d4010 e1a04000 e3a00001 ebe8abd7 (e1943f9f) 
+---[ end trace 0000000000000002 ]---
+note: test-qt5[277] exited with preempt_count 1
+
+Index: git/include/linux/mm_types.h
+===================================================================
+--- git.orig/include/linux/mm_types.h
++++ git/include/linux/mm_types.h
+@@ -24,7 +24,7 @@
+ 
+ struct address_space;
+ 
+-#define USE_SPLIT_PTLOCKS	(NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS)
++#define USE_SPLIT_PTLOCKS	(0)
+ 
+ /*
+  * Each physical page in the system has a struct page associated with
diff --git a/recipes-kernel/linux/linux-apf6-rt/0504-armadeus-add_sx8674_touchscreen_driver.patch b/recipes-kernel/linux/linux-apf6-rt/0504-armadeus-add_sx8674_touchscreen_driver.patch
new file mode 100644
index 0000000..f31f7c2
--- /dev/null
+++ b/recipes-kernel/linux/linux-apf6-rt/0504-armadeus-add_sx8674_touchscreen_driver.patch
@@ -0,0 +1,510 @@
+--- git/drivers/input/touchscreen/Kconfig.old	2015-03-04 11:55:10.242346171 +0100
++++ git/drivers/input/touchscreen/Kconfig	2015-03-04 11:54:55.574346311 +0100
+@@ -900,6 +900,17 @@
+ 	  To compile this driver as a module, choose M here: the
+ 	  module will be called tps6507x_ts.
+ 
++config TOUCHSCREEN_SX8674
++	tristate "Semtech SX8674 touchscreen controllers"
++	depends on I2C
++	help
++	  Say Y here if you have a Semtech SX8674 touchscreen controller.
++
++	  If unsure, say N
++
++	  To compile this driver as a module, choose M here: the
++	  module will be called sx8674.
++
+ config TOUCHSCREEN_FUSION
+ 	tristate "fusion touchscreen"
+ 	help
+--- git/drivers/input/touchscreen/Makefile.old	2015-03-04 11:56:09.890345602 +0100
++++ git/drivers/input/touchscreen/Makefile	2015-03-04 11:55:58.250345713 +0100
+@@ -52,6 +52,7 @@
+ obj-$(CONFIG_TOUCHSCREEN_S3C2410)	+= s3c2410_ts.o
+ obj-$(CONFIG_TOUCHSCREEN_ST1232)	+= st1232.o
+ obj-$(CONFIG_TOUCHSCREEN_STMPE)		+= stmpe-ts.o
++obj-$(CONFIG_TOUCHSCREEN_SX8674)	+= sx8674.o
+ obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC)	+= ti_am335x_tsc.o
+ obj-$(CONFIG_TOUCHSCREEN_TNETV107X)	+= tnetv107x-ts.o
+ obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213)	+= touchit213.o
+--- /dev/null
++++ git//drivers/input/touchscreen/sx8674.c
+@@ -0,0 +1,477 @@
++/*
++ * drivers/input/touchscreen/sx8674.c
++ *
++ * Copyright (c) 2014 ARMadeus Systems
++ *	Sébastien Szymanski <sebastien.szymanski at armadeus.com>
++ *
++ *
++ * Using code from:
++ *  - sx865x.c
++ *	Copyright (c) 2013 U-MoBo Srl
++ *	Pierluigi Passaro <p.passaro at u-mobo.com>
++ *  - sx8650.c
++ *      Copyright (c) 2009 Wayne Roberts
++ *  - tsc2007.c
++ *      Copyright (c) 2008 Kwangwoo Lee
++ *  - ads7846.c
++ *      Copyright (c) 2005 David Brownell
++ *      Copyright (c) 2006 Nokia Corporation
++ *  - corgi_ts.c
++ *      Copyright (C) 2004-2005 Richard Purdie
++ *  - omap_ts.[hc], ads7846.h, ts_osk.c
++ *      Copyright (C) 2002 MontaVista Software
++ *      Copyright (C) 2004 Texas Instruments
++ *      Copyright (C) 2005 Dirk Behme
++ *
++ *  This program is free software; you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License version 2 as
++ *  published by the Free Software Foundation.
++ */
++
++#include <linux/i2c.h>
++#include <linux/init.h>
++#include <linux/input.h>
++#include <linux/interrupt.h>
++#include <linux/irq.h>
++#include <linux/module.h>
++#include <linux/of_irq.h>
++#include <linux/slab.h>
++
++/* analog channels */
++#define CH_X			0
++#define CH_Y			1
++#define CH_Z1			2
++#define CH_Z2			3
++#define CH_RX			5
++#define CH_RY			6
++
++/* commands */
++#define CMD_READ_REGISTER	0x40
++#define CMD_PENTRG		0xe0
++
++/* register addresses */
++#define I2C_REG_TOUCH0		0x00
++#define I2C_REG_TOUCH1		0x01
++#define I2C_REG_TOUCH2		0x02
++#define I2C_REG_TOUCH3		0x03
++#define I2C_REG_CHANMASK	0x04
++#define I2C_REG_IRQMASK		0x22
++#define I2C_REG_IRQSRC		0x23
++#define I2C_REG_STAT		0x24
++#define I2C_REG_SOFTRESET	0x3f
++
++/* value for I2C_REG_SOFTRESET */
++#define SOFTRESET_VALUE		0xde
++
++/* bits for I2C_REG_IRQSRC */
++#define IRQ_HAPTSHORT			(1 << 7)
++#define IRQ_PROXCLOSE			(1 << 6)
++#define IRQ_PROXFAR_CONVDONE		(1 << 5)
++#define IRQ_PROXCOMPDONE		(1 << 4)
++#define IRQ_PENTOUCH_TOUCHCONVDONE	(1 << 3)
++#define IRQ_PENRELEASE			(1 << 2)
++#define IRQ_TEMPWARNING			(1 << 1)
++#define IRQ_TEMPALARM			(1 << 0)
++
++/* bits for RegTouch1 */
++#define CONDIRQ			0x20
++#define FILT_NONE		0x00	/* no averaging */
++#define FILT_3SA		0x01	/* 3 sample averaging */
++#define FILT_5SA		0x02	/* 5 sample averaging */
++#define FILT_7SA		0x03	/* 7 samples, sort, then average of 3 middle samples */
++
++/* bits for I2C_REG_CHANMASK */
++#define CONV_X			0x80
++#define CONV_Y			0x40
++#define CONV_Z1			0x20
++#define CONV_Z2			0x10
++#define CONV_AUX		0x08
++#define CONV_RX			0x04
++#define CONV_RY			0x02
++
++/* cordinantes rate: higher nibble of CTRL0 register */
++#define RATE_MANUAL		0x00
++#define RATE_10CPS		0x10
++#define RATE_20CPS		0x20
++#define RATE_40CPS		0x30
++#define RATE_60CPS		0x40
++#define RATE_80CPS		0x50
++#define RATE_100CPS		0x60
++#define RATE_200CPS		0x70
++#define RATE_300CPS		0x80
++#define RATE_400CPS		0x90
++#define RATE_500CPS		0xa0
++#define RATE_1000CPS		0xb0
++#define RATE_2000CPS		0xc0
++#define RATE_3000CPS		0xd0
++#define RATE_4000CPS		0xe0
++#define RATE_5000CPS		0xf0
++
++/* power delay: lower nibble of CTRL0 register */
++#define POWDLY_IMMEDIATE	0x00
++#define POWDLY_1_1US		0x01
++#define POWDLY_2_2US		0x02
++#define POWDLY_4_4US		0x03
++#define POWDLY_8_9US		0x04
++#define POWDLY_17_8US		0x05
++#define POWDLY_35_5US		0x06
++#define POWDLY_71US		0x07
++#define POWDLY_140US		0x08
++#define POWDLY_280US		0x09
++#define POWDLY_570US		0x0a
++#define POWDLY_1_1MS		0x0b
++#define POWDLY_2_3MS		0x0c
++#define POWDLY_4_6MS		0x0d
++#define POWDLY_9MS		0x0e
++#define POWDLY_18MS		0x0f
++
++#define MAX_12BIT		((1 << 12) - 1)
++
++/* when changing the channel mask, also change the read length appropriately */
++#define CHAN_SINGLETOUCH_MASK	(CONV_X | CONV_Y | CONV_Z1 | CONV_Z2)
++#define CHAN_MULTITOUCH_MASK	(CHAN_SINGLETOUCH_MASK | CONV_RX | CONV_RY)
++#define NUM_SINGLETOUCH_CHANS	4
++#define NUM_MULTITOUCH_CHANS	6
++#define NUM_MAX_CHANS		NUM_MULTITOUCH_CHANS
++#define CHAN_READ_LENGTH(chans)	(chans << 1)
++
++#define SX_MULTITOUCH		0x01
++#define SX_PROXIMITY_SENSING	0x02
++#define SX_HAPTICS_GENERIC	0x04
++#define SX_HAPTICS_IMMERSION	0x08
++
++struct ts_event {
++	u16 x, y;
++	u16 z1, z2;
++	u16 rx, ry;
++};
++
++struct sx8674 {
++	struct input_dev *input;
++	struct ts_event tc;
++
++	struct i2c_client *client;
++
++	struct workqueue_struct *ts_workq;
++	struct delayed_work pen_event_work;
++
++	u16 features;
++	u16 x_plate_ohms;
++	u16 y_plate_ohms;
++
++	int irq;
++};
++
++static const struct i2c_device_id sx8674_idtable[] = {
++	{ "sx8654", SX_PROXIMITY_SENSING | SX_HAPTICS_GENERIC },
++	{ "sx8674", SX_MULTITOUCH | SX_PROXIMITY_SENSING | SX_HAPTICS_GENERIC },
++	{ /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(i2c, sx8674_idtable);
++
++static int sx8674_read_values(struct sx8674 *ts)
++{
++	s32 data;
++	u16 vals[NUM_MAX_CHANS+1];	// +1 for last dummy read
++	int length, chan_read_length;
++	int i;
++
++	memset(&(ts->tc), 0, sizeof(ts->tc));
++	/* The protocol and raw data format from i2c interface:
++	 * S Addr R A [DataLow] A [DataHigh] A (repeat) NA P
++	 * Where DataLow has (channel | [D11-D8]), DataHigh has [D7-D0].
++	 */
++	if (ts->features & SX_MULTITOUCH)
++		chan_read_length = CHAN_READ_LENGTH(NUM_MULTITOUCH_CHANS);
++	else
++		chan_read_length = CHAN_READ_LENGTH(NUM_SINGLETOUCH_CHANS);
++
++	length = i2c_master_recv(ts->client, (char *)vals, chan_read_length);
++
++	if (likely(length == chan_read_length)) {
++		length >>= 1;
++		for (i = 0; i < length; i++) {
++			u16 ch;
++			data = swab16(vals[i]);
++			if (unlikely(data & 0x8000)) {
++				dev_err(&ts->client->dev, "hibit @ %d [0x%04x]\n", i, data);
++				return -EAGAIN;
++			}
++			ch = data >> 12;
++			if (ch == CH_X)
++				ts->tc.x = data & 0xfff;
++			else if (ch == CH_Y)
++				ts->tc.y = data & 0xfff;
++			else if (ch == CH_Z1)
++				ts->tc.z1 = data & 0xfff;
++			else if (ch == CH_Z2)
++				ts->tc.z2 = data & 0xfff;
++			else if (ch == CH_RX)
++				ts->tc.rx = data & 0xfff;
++			else if (ch == CH_RY)
++				ts->tc.ry = data & 0xfff;
++			else
++				dev_err(&ts->client->dev, "? CH%d %x\n", ch, data & 0xfff);
++		}
++	} else
++		dev_err(&ts->client->dev, "%d = recv()\n", length);
++
++	dev_dbg(&ts->client->dev, "X:%d Y:%d Z1:%d Z2:%d RX:%d RY:%d\n",
++		ts->tc.x, ts->tc.y, ts->tc.z1, ts->tc.z2, ts->tc.rx, ts->tc.ry);
++	dev_dbg(&ts->client->dev, "X+:%d Y+:%d X-:%d Y-:%d\n",
++		ts->tc.x+ts->tc.rx, ts->tc.y+ts->tc.ry, ts->tc.x-ts->tc.rx, ts->tc.y-ts->tc.ry);
++
++	return !ts->tc.z1;	/* return 0 only if pressure not 0 */
++}
++
++static void sx8674_pen_irq_worker(struct work_struct *work)
++{
++	struct sx8674 *ts = container_of(work, struct sx8674, pen_event_work.work);
++	u8 irqsrc;
++
++	dev_dbg(&ts->client->dev, "%s\n", __func__);
++
++	irqsrc = i2c_smbus_read_byte_data(ts->client, CMD_READ_REGISTER | I2C_REG_IRQSRC);
++	dev_dbg(&ts->client->dev, "irqsrc: 0x%x", irqsrc);
++
++	if (irqsrc & IRQ_PENRELEASE) {
++		dev_dbg(&ts->client->dev, "pen release interrupt");
++		input_report_key(ts->input, BTN_TOUCH, 0);
++		input_sync(ts->input);
++	}
++
++	if (irqsrc & IRQ_PENTOUCH_TOUCHCONVDONE) {
++		dev_dbg(&ts->client->dev, "pen touch interrupt");
++
++		sx8674_read_values(ts);
++
++		input_report_abs(ts->input, ABS_X, ts->tc.x);
++		input_report_abs(ts->input, ABS_Y, ts->tc.y);
++		input_report_key(ts->input, BTN_TOUCH, 1);
++		input_sync(ts->input);
++
++		dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n", ts->tc.x, ts->tc.y, 0);
++	}
++}
++
++static irqreturn_t sx8674_irq(int irq, void *handle)
++{
++	struct sx8674 *ts = handle;
++
++	/* the reading of the samples can be time-consuming if using
++	 * a slow i2c, so the work is done in a queue */
++	queue_delayed_work(ts->ts_workq, &ts->pen_event_work, 0);
++
++	return IRQ_HANDLED;
++}
++
++static int get_rmsel(u16 plate_ohms)
++{
++	int rmsel;
++
++	if (plate_ohms < 187)
++		rmsel = 0;
++	else if (plate_ohms < 312)
++		rmsel = 1;
++	else if (plate_ohms < 938)
++		rmsel = 2;
++	else if (plate_ohms < 1875)
++		rmsel = 3;
++	else if (plate_ohms < 4375)
++		rmsel = 4;
++	else if (plate_ohms < 9375)
++		rmsel = 5;
++	else if (plate_ohms < 18780)
++		rmsel = 6;
++	else
++		rmsel = 7;
++
++	return rmsel;
++}
++
++static int sx8674_parse_dt(struct device *dev, struct sx8674 *ts) {
++	struct device_node *node;
++
++	if (!dev || !ts)
++		return -ENOENT;
++
++	node = dev->of_node;
++	if (!node)
++		return -ENOENT;
++
++	ts->irq = irq_of_parse_and_map(node, 0);
++	if (!ts->irq)
++		return -EINVAL;
++
++	if(of_property_read_u16(node, "x-plate-ohms", &ts->x_plate_ohms))
++		return -EINVAL;
++
++	if(of_property_read_u16(node, "y-plate-ohms", &ts->y_plate_ohms))
++		return -EINVAL;
++
++	return 0;
++}
++
++static int sx8674_probe(struct i2c_client *client,
++			const struct i2c_device_id *id)
++{
++	struct sx8674 *ts;
++	struct input_dev *input_dev;
++	int err;
++	u16 rmselx, rmsely;
++
++	if (!i2c_check_functionality(client->adapter,
++				     I2C_FUNC_SMBUS_READ_WORD_DATA))
++		return -EIO;
++
++	ts = devm_kzalloc(&client->dev, sizeof(struct sx8674), GFP_KERNEL);
++	input_dev = devm_input_allocate_device(&client->dev);
++	if (!ts || !input_dev)
++		return -ENOMEM;
++
++	err = sx8674_parse_dt(&client->dev, ts);
++	if (err)
++		return err;
++
++	ts->client = client;
++	i2c_set_clientdata(client, ts);
++
++	ts->input = input_dev;
++
++	ts->features = id->driver_data;
++
++	input_dev->name = "SX8674 Touchscreen";
++	input_dev->id.bustype = BUS_I2C;
++	input_dev->dev.parent = &client->dev;
++	input_set_drvdata(input_dev, ts);
++
++	__set_bit(EV_ABS, input_dev->evbit);
++	__set_bit(EV_KEY, input_dev->evbit);
++	__set_bit(BTN_TOUCH, input_dev->keybit);
++	__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
++
++	input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);
++	input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
++	/* TODO */
++	/*input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0);*/
++
++	err = i2c_smbus_write_byte_data(client, I2C_REG_SOFTRESET,
++					SOFTRESET_VALUE);
++	if (err < 0) {
++		dev_err(&client->dev, "writing softreset value failed");
++		return -EIO;
++	}
++
++	if (ts->features & SX_MULTITOUCH) {
++		input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, MAX_12BIT, 0, 0);
++		input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, MAX_12BIT, 0, 0);
++		input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, MAX_12BIT, 0, 0);
++		input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, 1, 0, 0);
++
++		/* set mask to convert X, Y, Z1, Z2, RX, RY for CH_SEQ */
++		err = i2c_smbus_write_byte_data(client, I2C_REG_CHANMASK,
++						CHAN_MULTITOUCH_MASK);
++		if (err < 0) {
++			dev_err(&client->dev, "writing to I2C_REG_CHANMASK failed");
++			return -EIO;
++		}
++
++		if (ts->x_plate_ohms < 100)
++			ts->x_plate_ohms = 100;
++		if (ts->y_plate_ohms < 100)
++			ts->y_plate_ohms = 100;
++		rmselx = get_rmsel(ts->x_plate_ohms);
++		rmsely = get_rmsel(ts->y_plate_ohms);
++
++		err = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH3,
++						(rmsely << 3) | rmselx);
++		if (err < 0) {
++			dev_err(&client->dev, "writing to I2C_REG_TOUCH3 failed");
++			return -EIO;
++		}
++	} else {
++		/* set mask to convert X, Y, Z1, Z2 for CH_SEQ */
++		err = i2c_smbus_write_byte_data(client, I2C_REG_CHANMASK, CHAN_SINGLETOUCH_MASK);
++		if (err < 0) {
++			dev_err(&client->dev, "writing to I2C_REG_CHANMASK failed");
++			return -EIO;
++		}
++	}
++
++	err = i2c_smbus_write_byte_data(client, I2C_REG_IRQMASK, (IRQ_PENTOUCH_TOUCHCONVDONE | IRQ_PENRELEASE));
++	if (err < 0) {
++		dev_err(&client->dev, "writing to I2C_REG_IRQMASK failed");
++		return -EIO;
++	}
++
++	err = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH1, CONDIRQ | FILT_7SA);
++	if (err < 0) {
++		dev_err(&client->dev, "writing to I2C_REG_TOUCH1 failed");
++		return -EIO;
++	}
++
++	err = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, RATE_5000CPS | POWDLY_1_1MS);
++	if (err < 0) {
++		dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed");
++		return -EIO;
++	}
++
++	ts->ts_workq = create_singlethread_workqueue("sx8674");
++	if (ts->ts_workq == NULL) {
++		dev_err(&client->dev, "failed to create workqueue\n");
++		return -ENOENT;
++	}
++
++	INIT_DELAYED_WORK(&ts->pen_event_work, sx8674_pen_irq_worker);
++
++	err = devm_request_threaded_irq(&client->dev, ts->irq,
++					NULL, sx8674_irq,
++					IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
++					client->dev.driver->name, ts);
++	if (err < 0) {
++		dev_err(&client->dev, "irq %d busy?\n", ts->irq);
++		return -EBUSY;
++	}
++
++	err = input_register_device(input_dev);
++	if (err)
++		return -ENONET;
++
++	/* enter pen-trigger mode */
++	err = i2c_smbus_write_byte(client, CMD_PENTRG);
++	if (err < 0) {
++		dev_err(&client->dev, "writing command CMD_PENTRG failed");
++		input_unregister_device(input_dev);
++		return -EIO;
++	}
++
++	return 0;
++}
++
++static int sx8674_remove(struct i2c_client *client)
++{
++	struct sx8674 *ts = i2c_get_clientdata(client);
++
++	input_unregister_device(ts->input);
++
++	cancel_delayed_work_sync(&ts->pen_event_work);
++	destroy_workqueue(ts->ts_workq);
++
++	return 0;
++}
++
++static struct i2c_driver sx8674_driver = {
++	.driver = {
++		.owner = THIS_MODULE,
++		.name = "sx8674"
++	},
++	.id_table = sx8674_idtable,
++	.probe = sx8674_probe,
++	.remove	= sx8674_remove,
++};
++
++module_i2c_driver(sx8674_driver)
++
++MODULE_AUTHOR("Sébastien Szymanski <sebastien.szymanski at armadeus.com>");
++MODULE_DESCRIPTION("SX8674 Touchscreen Driver");
++MODULE_LICENSE("GPL");
diff --git a/recipes-kernel/linux/linux-apf6-rt/0505-armadeus-add_mcp7940x_rtc_driver.patch b/recipes-kernel/linux/linux-apf6-rt/0505-armadeus-add_mcp7940x_rtc_driver.patch
new file mode 100644
index 0000000..f2c7d37
--- /dev/null
+++ b/recipes-kernel/linux/linux-apf6-rt/0505-armadeus-add_mcp7940x_rtc_driver.patch
@@ -0,0 +1,524 @@
+--- /dev/null
++++ git/drivers/rtc/rtc-mcp7940x.c
+@@ -0,0 +1,492 @@
++/*
++ * RTC client/driver for the Microchip MCP7940x Real-Time Clock over I2C
++ *
++ * Based on code by Scott Wood <scottwood at freescale.com>
++ * which was based on code by Randy Vinson <rvinson at mvista.com>,
++ * which was based on the m41t00.c by Mark Greer <mgreer at mvista.com>.
++ *
++ * Copyright (C) 2014 Armadeus Systems
++ *
++ * This file is licensed under the terms of the GNU General Public License
++ * version 2. This program is licensed "as is" without any warranty of any
++ * kind, whether express or implied.
++ */
++/*
++ * It would be more efficient to use i2c msgs/i2c_transfer directly but, as
++ * recommened in .../Documentation/i2c/writing-clients section
++ * "Sending and receiving", using SMBus level communication is preferred.
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/interrupt.h>
++#include <linux/i2c.h>
++#include <linux/rtc.h>
++#include <linux/bcd.h>
++#include <linux/slab.h>
++
++/* Time of Day */
++#define MCP7940X_REG_TD_SECONDS		0x00
++#define MCP7940X_REG_TD_MINUTES		0x01
++#define MCP7940X_REG_TD_HOURS		0x02
++#define MCP7940X_REG_TD_DAY		0x03
++#define MCP7940X_REG_TD_DATE		0x04
++#define MCP7940X_REG_TD_MONTH		0x05
++#define MCP7940X_REG_TD_YEAR		0x06
++
++/* Controls */
++#define MCP7940X_REG_CONTROL		0x07
++#define MCP7940X_REG_CALIBRATION	0x08
++#define MCP7940X_REG_UNLOCK_ID		0x08
++
++/* Alarm 0 */
++#define MCP7940X_REG_ALRM0_SECONDS	0x0A
++#define MCP7940X_REG_ALRM0_MINUTES	0x0B
++#define MCP7940X_REG_ALRM0_HOURS	0x0C
++#define MCP7940X_REG_ALRM0_DAY		0x0D
++#define MCP7940X_REG_ALRM0_DATE		0x0E
++#define MCP7940X_REG_ALRM0_MONTH	0x0F
++
++/* Alarm 1 */
++#define MCP7940X_REG_ALRM1_SECONDS	0x11
++#define MCP7940X_REG_ALRM1_MINUTES	0x12
++#define MCP7940X_REG_ALRM1_HOURS	0x13
++#define MCP7940X_REG_ALRM1_DAY		0x14
++#define MCP7940X_REG_ALRM1_DATE		0x15
++#define MCP7940X_REG_ALRM1_MONTH	0x16
++
++/* Timestamp 0 */
++#define MCP7940X_REG_TS0_MINUTES	0x18
++#define MCP7940X_REG_TS0_HOURS		0x19
++#define MCP7940X_REG_TS0_DATE		0x1A
++#define MCP7940X_REG_TS0_MONTH		0x1B
++
++/* Timestamp 1 */
++#define MCP7940X_REG_TS1_MINUTES	0x1C
++#define MCP7940X_REG_TS1_HOURS		0x1D
++#define MCP7940X_REG_TS1_DATE		0x1E
++#define MCP7940X_REG_TS1_MONTH		0x1F
++
++/* bits definitions */
++#define MCP7940X_START_BITS	0x80
++#define MCP7940X_24H_BITS	0x40
++#define MCP7940X_VBATEN		0x08
++
++static const struct i2c_device_id mcp7940x_id[] = {
++	{ "mcp79400", 0 }, /* no unique ID */
++	{ "mcp79401", 1 }, /* EUI-48 */
++	{ "mcp79402", 2 }, /* EUI-64 */
++	{ }
++};
++MODULE_DEVICE_TABLE(i2c, mcp7940x_id);
++
++struct mcp7940x {
++	struct rtc_device *rtc;
++	struct i2c_client *client;
++	int exiting;
++	int irq;
++	unsigned int irqen;
++	int alrm_sec;
++	int alrm_min;
++	int alrm_hour;
++	int alrm_mday;
++	spinlock_t lock;
++};
++
++static struct i2c_driver mcp7940x_driver;
++
++#if 0
++static void mcp7940x_reg_dump(struct device *dev)
++{
++	struct i2c_client *client = to_i2c_client(dev);
++
++	printk(" MCP7940X_REG_TD_SECONDS	(0x%02X) -> 0x%02X\n", MCP7940X_REG_TD_SECONDS	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_TD_SECONDS));
++	printk(" MCP7940X_REG_TD_MINUTES	(0x%02X) -> 0x%02X\n", MCP7940X_REG_TD_MINUTES	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_TD_MINUTES));
++	printk(" MCP7940X_REG_TD_HOURS		(0x%02X) -> 0x%02X\n", MCP7940X_REG_TD_HOURS	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_TD_HOURS));
++	printk(" MCP7940X_REG_TD_DAY		(0x%02X) -> 0x%02X\n", MCP7940X_REG_TD_DAY	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_TD_DAY));
++	printk(" MCP7940X_REG_TD_DATE		(0x%02X) -> 0x%02X\n", MCP7940X_REG_TD_DATE	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_TD_DATE));
++	printk(" MCP7940X_REG_TD_MONTH		(0x%02X) -> 0x%02X\n", MCP7940X_REG_TD_MONTH	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_TD_MONTH));
++	printk(" MCP7940X_REG_TD_YEAR		(0x%02X) -> 0x%02X\n", MCP7940X_REG_TD_YEAR	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_TD_YEAR));
++	printk(" MCP7940X_REG_CONTROL		(0x%02X) -> 0x%02X\n", MCP7940X_REG_CONTROL	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_CONTROL));
++	printk(" MCP7940X_REG_CALIBRATION	(0x%02X) -> 0x%02X\n", MCP7940X_REG_CALIBRATION	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_CALIBRATION));
++	printk(" MCP7940X_REG_UNLOCK_ID		(0x%02X) -> 0x%02X\n", MCP7940X_REG_UNLOCK_ID	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_UNLOCK_ID));
++	printk(" MCP7940X_REG_ALRM0_SECONDS	(0x%02X) -> 0x%02X\n", MCP7940X_REG_ALRM0_SECONDS,i2c_smbus_read_byte_data(client,MCP7940X_REG_ALRM0_SECONDS));
++	printk(" MCP7940X_REG_ALRM0_MINUTES	(0x%02X) -> 0x%02X\n", MCP7940X_REG_ALRM0_MINUTES,i2c_smbus_read_byte_data(client,MCP7940X_REG_ALRM0_MINUTES));
++	printk(" MCP7940X_REG_ALRM0_HOURS	(0x%02X) -> 0x%02X\n", MCP7940X_REG_ALRM0_HOURS	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_ALRM0_HOURS));
++	printk(" MCP7940X_REG_ALRM0_DAY		(0x%02X) -> 0x%02X\n", MCP7940X_REG_ALRM0_DAY	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_ALRM0_DAY));
++	printk(" MCP7940X_REG_ALRM0_DATE	(0x%02X) -> 0x%02X\n", MCP7940X_REG_ALRM0_DATE	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_ALRM0_DATE));
++	printk(" MCP7940X_REG_ALRM0_MONTH	(0x%02X) -> 0x%02X\n", MCP7940X_REG_ALRM0_MONTH	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_ALRM0_MONTH));
++	printk(" MCP7940X_REG_ALRM1_SECONDS	(0x%02X) -> 0x%02X\n", MCP7940X_REG_ALRM1_SECONDS,i2c_smbus_read_byte_data(client,MCP7940X_REG_ALRM1_SECONDS));
++	printk(" MCP7940X_REG_ALRM1_MINUTES	(0x%02X) -> 0x%02X\n", MCP7940X_REG_ALRM1_MINUTES,i2c_smbus_read_byte_data(client,MCP7940X_REG_ALRM1_MINUTES));
++	printk(" MCP7940X_REG_ALRM1_HOURS	(0x%02X) -> 0x%02X\n", MCP7940X_REG_ALRM1_HOURS	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_ALRM1_HOURS));
++	printk(" MCP7940X_REG_ALRM1_DAY		(0x%02X) -> 0x%02X\n", MCP7940X_REG_ALRM1_DAY	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_ALRM1_DAY));
++	printk(" MCP7940X_REG_ALRM1_DATE	(0x%02X) -> 0x%02X\n", MCP7940X_REG_ALRM1_DATE	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_ALRM1_DATE));
++	printk(" MCP7940X_REG_ALRM1_MONTH	(0x%02X) -> 0x%02X\n", MCP7940X_REG_ALRM1_MONTH	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_ALRM1_MONTH));
++	printk(" MCP7940X_REG_TS0_MINUTES	(0x%02X) -> 0x%02X\n", MCP7940X_REG_TS0_MINUTES	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_TS0_MINUTES));
++	printk(" MCP7940X_REG_TS0_HOURS		(0x%02X) -> 0x%02X\n", MCP7940X_REG_TS0_HOURS	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_TS0_HOURS));
++	printk(" MCP7940X_REG_TS0_DATE		(0x%02X) -> 0x%02X\n", MCP7940X_REG_TS0_DATE	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_TS0_DATE));
++	printk(" MCP7940X_REG_TS0_MONTH		(0x%02X) -> 0x%02X\n", MCP7940X_REG_TS0_MONTH	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_TS0_MONTH));
++	printk(" MCP7940X_REG_TS1_MINUTES	(0x%02X) -> 0x%02X\n", MCP7940X_REG_TS1_MINUTES	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_TS1_MINUTES));
++	printk(" MCP7940X_REG_TS1_HOURS		(0x%02X) -> 0x%02X\n", MCP7940X_REG_TS1_HOURS	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_TS1_HOURS));
++	printk(" MCP7940X_REG_TS1_DATE		(0x%02X) -> 0x%02X\n", MCP7940X_REG_TS1_DATE	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_TS1_DATE));
++	printk(" MCP7940X_REG_TS1_MONTH		(0x%02X) -> 0x%02X\n", MCP7940X_REG_TS1_MONTH	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_TS1_MONTH));
++}
++#endif
++
++static int mcp7940x_read_time(struct device *dev, struct rtc_time *time)
++{
++	struct i2c_client *client = to_i2c_client(dev);
++	int ret;
++
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_TD_SECONDS);
++	if (ret < 0)
++		return ret;
++	time->tm_sec = ((ret&0x70)>>4)*10 + (ret&0x0F);
++
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_TD_MINUTES);
++	if (ret < 0)
++		return ret;
++	time->tm_min = ((ret&0x70)>>4)*10 + (ret&0x0F);
++
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_TD_HOURS);
++	if (ret < 0)
++		return ret;
++	time->tm_hour = ((ret&0x30)>>4)*10 + (ret&0x0F);
++
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_TD_DATE);
++	if (ret < 0)
++		return ret;
++	time->tm_mday = ((ret&0x30)>>4)*10 + (ret&0x0F);
++
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_TD_MONTH);
++	if (ret < 0)
++		return ret;
++	time->tm_mon = (((ret&0x10)>>4)*10 + (ret&0x0F)) - 1;
++
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_TD_YEAR);
++	if (ret < 0)
++		return ret;
++	time->tm_year = ((((ret&0xF0)>>4)*10 + (ret&0x0F))) + 100;
++
++	return 0;
++}
++
++static int mcp7940x_set_time(struct device *dev, struct rtc_time *time)
++{
++	struct i2c_client *client = to_i2c_client(dev);
++	int ret;
++	int value;
++	int day;
++
++	value = ((((time->tm_sec/10)&0x07)<<4)|(time->tm_sec % 10))|
++		MCP7940X_START_BITS;
++	ret = i2c_smbus_write_byte_data(client,
++					MCP7940X_REG_TD_SECONDS,
++					value);
++	if (ret < 0)
++		return ret;
++
++	value = (((time->tm_min/10)&0x07)<<4)|(time->tm_min % 10);
++	ret = i2c_smbus_write_byte_data(client,
++					MCP7940X_REG_TD_MINUTES,
++					value);
++	if (ret < 0)
++		return ret;
++
++	value = ((((time->tm_hour/10)&0x03)<<4)|(time->tm_hour % 10));
++	ret = i2c_smbus_write_byte_data(client,
++					MCP7940X_REG_TD_HOURS,
++					value);
++	if (ret < 0)
++		return ret;
++
++	value = (((time->tm_mday/10)&0x03)<<4)|(time->tm_mday % 10);
++	ret = i2c_smbus_write_byte_data(client,
++					MCP7940X_REG_TD_DATE,
++					value);
++	if (ret < 0)
++		return ret;
++
++	/*****/
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_TD_DAY);
++	if (ret < 0)
++		return ret;
++	day = (time->tm_wday == 0)?7:time->tm_wday;
++	ret = i2c_smbus_write_byte_data(client,
++					MCP7940X_REG_TD_DAY,
++					(ret&0xF8)|day);
++	if (ret < 0)
++		return ret;
++	/*****/
++
++	value = ((((time->tm_mon+1)/10)&0x01)<<4)|((time->tm_mon+1) % 10);
++	ret = i2c_smbus_write_byte_data(client,
++					MCP7940X_REG_TD_MONTH,
++					value);
++	if (ret < 0)
++		return ret;
++
++	value = ((((time->tm_year-100)/10)&0x0F)<<4)|((time->tm_year-100) % 10);
++	ret = i2c_smbus_write_byte_data(client,
++					MCP7940X_REG_TD_YEAR,
++					value);
++	if (ret < 0)
++		return ret;
++
++	return 0;
++}
++
++//struct rtc_wkalrm {
++//        unsigned char enabled;  /* 0 = alarm disabled, 1 = alarm enabled */
++//        unsigned char pending;  /* 0 = alarm not pending, 1 = alarm pending */
++//        struct rtc_time time;   /* time the alarm is set to */
++//};
++
++static int mcp79400_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) {
++	struct i2c_client *client = to_i2c_client(dev);
++	struct rtc_time *time = &wkalrm->time;
++	int ret;
++
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_ALRM0_SECONDS);
++	if (ret < 0)
++		return ret;
++	time->tm_sec = ((ret&0x70)>>4)*10 + (ret&0x0F);
++
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_ALRM0_MINUTES);
++	if (ret < 0)
++		return ret;
++	time->tm_min = ((ret&0x70)>>4)*10 + (ret&0x0F);
++
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_ALRM0_HOURS);
++	if (ret < 0)
++		return ret;
++	time->tm_hour = ((ret&0x30)>>4)*10 + (ret&0x0F);
++
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_ALRM0_DATE);
++	if (ret < 0)
++		return ret;
++	time->tm_mday = ((ret&0x30)>>4)*10 + (ret&0x0F);
++
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_ALRM0_MONTH);
++	if (ret < 0)
++		return ret;
++	time->tm_mon = (((ret&0x10)>>4)*10 + (ret&0x0F)) - 1;
++
++	time->tm_year = -1;
++	time->tm_wday = -1;
++	time->tm_yday = -1;
++	time->tm_isdst= -1;
++
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_CONTROL);
++	if (ret < 0)
++		return ret;
++	wkalrm->enabled	= ((ret&0x10)>>4);
++
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_ALRM0_DAY);
++	if (ret < 0)
++		return ret;
++	wkalrm->pending	= ((ret&0x08)>>3);
++
++	return 0;
++}
++
++static int mcp79400_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) {
++	struct i2c_client *client = to_i2c_client(dev);
++	struct rtc_time *time = &wkalrm->time;
++	int ret;
++	int value;
++	int day;
++
++	/* clear pending alarm */
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_ALRM0_DAY);
++	if (ret < 0)
++		return ret;
++	day = (time->tm_wday == 0)?7:time->tm_wday;
++	ret = i2c_smbus_write_byte_data(client,
++					MCP7940X_REG_ALRM0_DAY,
++					((ret|0x70)&0x70)|day);
++	if (ret < 0)
++		return ret;
++
++	value = ((((time->tm_sec/10)&0x07)<<4)|(time->tm_sec % 10))|
++							MCP7940X_START_BITS;
++	ret = i2c_smbus_write_byte_data(client,
++					MCP7940X_REG_ALRM0_SECONDS,
++					value);
++	if (ret < 0)
++		return ret;
++
++	value = (((time->tm_min/10)&0x07)<<4)|(time->tm_min % 10);
++	ret = i2c_smbus_write_byte_data(client,
++					MCP7940X_REG_ALRM0_MINUTES,
++					value);
++	if (ret < 0)
++		return ret;
++
++	value = ((((time->tm_hour/10)&0x03)<<4)|(time->tm_hour % 10));
++	ret = i2c_smbus_write_byte_data(client,
++					MCP7940X_REG_ALRM0_HOURS,
++					value);
++	if (ret < 0)
++		return ret;
++
++	value = (((time->tm_mday/10)&0x03)<<4)|(time->tm_mday % 10);
++	ret = i2c_smbus_write_byte_data(client,
++					MCP7940X_REG_ALRM0_DATE,
++					value);
++	if (ret < 0)
++		return ret;
++
++	value = ((((time->tm_mon+1)/10)&0x01)<<4)|((time->tm_mon+1) % 10);
++	ret = i2c_smbus_write_byte_data(client,
++					MCP7940X_REG_ALRM0_MONTH,
++					value);
++	if (ret < 0)
++		return ret;
++
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_CONTROL);
++	if (ret < 0)
++		return ret;
++	if (wkalrm->enabled) {
++		ret = i2c_smbus_write_byte_data(client,
++						MCP7940X_REG_CONTROL,
++						ret | 0x10);
++	} else {
++		ret = i2c_smbus_write_byte_data(client,
++						MCP7940X_REG_CONTROL,
++						ret & ~(0x10));
++	}
++	if (ret < 0)
++		return ret;
++
++	return 0;
++}
++
++/* TODO: see rtc-ds1511.c */
++static int mcp79400_alarm_irq_enable(struct device *dev, unsigned int enabled)
++{
++	return 0;
++}
++
++static const struct rtc_class_ops mcp7940x_rtc_ops = {
++	.read_time = mcp7940x_read_time,
++	.set_time = mcp7940x_set_time,
++	.read_alarm = mcp79400_read_alarm, /* Use ALARM0 only */
++	.set_alarm = mcp79400_set_alarm,
++	.alarm_irq_enable = mcp79400_alarm_irq_enable,
++};
++
++static const struct of_device_id mcp7940x_rtc_dt_match[] = {
++	{ .compatible = "microchip,mcp7940x-rtc", },
++	{ /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, mcp7940x_rtc_dt_match);
++
++static int mcp7940x_probe(struct i2c_client *client,
++			const struct i2c_device_id *id)
++{
++	struct mcp7940x *mcp7940x;
++	int ret;
++
++	mcp7940x = devm_kzalloc(&client->dev, sizeof(struct mcp7940x),
++				GFP_KERNEL);
++	if (!mcp7940x)
++		return -ENOMEM;
++
++	mcp7940x->client = client;
++	i2c_set_clientdata(client, mcp7940x);
++
++	ret = i2c_smbus_read_byte_data(mcp7940x->client, MCP7940X_REG_TD_HOURS);
++	if (ret < 0)
++		return -EIO;
++
++	/* set rtc to 24h format */
++	if ((ret & MCP7940X_24H_BITS)) {
++		ret = i2c_smbus_write_byte_data(mcp7940x->client,
++						MCP7940X_REG_TD_HOURS,
++						(u8)(ret & ~MCP7940X_24H_BITS));
++		if(ret < 0)
++			return -EIO;
++	}
++
++	ret = i2c_smbus_read_byte_data(mcp7940x->client, MCP7940X_REG_TD_DAY);
++	if (ret < 0)
++		return -EIO;
++
++	/* Use external baterie if Vcc fails */
++	if (!(ret & MCP7940X_VBATEN)) {
++		ret = i2c_smbus_write_byte_data(mcp7940x->client,
++						MCP7940X_REG_TD_DAY,
++						(u8)(ret | MCP7940X_VBATEN));
++		if(ret < 0)
++			return -EIO;
++	}
++
++	ret = i2c_smbus_read_byte_data(mcp7940x->client, MCP7940X_REG_TD_SECONDS);
++	if (ret < 0)
++		return -EIO;
++
++	/* If time counter not launched, launch it */
++	if (!(ret & MCP7940X_START_BITS)) {
++		ret = i2c_smbus_write_byte_data(mcp7940x->client,
++						MCP7940X_REG_TD_SECONDS,
++						(u8)(ret | MCP7940X_START_BITS));
++		if(ret < 0)
++			return -EIO;
++	}
++
++	device_set_wakeup_capable(&client->dev, 1);
++
++	mcp7940x->rtc = devm_rtc_device_register(&client->dev, client->name,
++						&mcp7940x_rtc_ops, THIS_MODULE);
++	if (IS_ERR(mcp7940x->rtc)) {
++		dev_err(&client->dev, "unable to register the class device\n");
++		return PTR_ERR(mcp7940x->rtc);
++	}
++
++	mcp7940x->rtc->uie_unsupported = 1;
++
++	return 0;
++}
++
++static int mcp7940x_remove(struct i2c_client *client)
++{
++	return 0;
++}
++
++#ifdef CONFIG_PM
++static int mcp7940x_suspend(struct device *dev)
++{
++	return 0;
++}
++
++static int mcp7940x_resume(struct device *dev)
++{
++	return 0;
++}
++
++static const struct dev_pm_ops mcp7940x_pm_ops = {
++	.suspend        = mcp7940x_suspend,
++	.resume         = mcp7940x_resume,
++};
++#endif
++
++static struct i2c_driver mcp7940x_driver = {
++	.driver = {
++		.name	= "rtc-mcp7940x",
++		.owner	= THIS_MODULE,
++#ifdef CONFIG_PM
++		.pm	= &mcp7940x_pm_ops,
++#endif
++		.of_match_table = mcp7940x_rtc_dt_match,
++	},
++	.probe = mcp7940x_probe,
++	.remove = mcp7940x_remove,
++	.id_table = mcp7940x_id,
++};
++
++module_i2c_driver(mcp7940x_driver)
++
++MODULE_AUTHOR("Fabien Marteau <fabien.marteau at armadeus.com>");
++MODULE_DESCRIPTION("Microchip mcp7940x RTC Driver");
++MODULE_LICENSE("GPL");
+--- git/drivers/rtc/Kconfig.old	2015-03-04 12:01:27.402342574 +0100
++++ git/drivers/rtc/Kconfig	2015-03-04 12:01:18.434342660 +0100
+@@ -488,6 +488,16 @@
+ 	  This driver can also be built as a module. If so, the module
+ 	  will be called rtc-rv3029c2.
+ 
++config RTC_DRV_MCP7940X
++	tristate "Microchip MCP7940X"
++	help
++	  If you say yes here you get support for Microchip
++	  MCP7940x real-time clock chips.
++	  The alarm functionality is not supported.
++
++	  This driver can also be built as a module. If so, the module
++	  will be called rtc-mcp7940x.
++
+ endif # I2C
+ 
+ comment "SPI RTC drivers"
+--- git/drivers/rtc/Makefile.old	2015-03-04 12:01:56.666342295 +0100
++++ git/drivers/rtc/Makefile	2015-03-04 12:01:49.234342366 +0100
+@@ -76,6 +76,7 @@
+ obj-$(CONFIG_RTC_DRV_MAX6902)	+= rtc-max6902.o
+ obj-$(CONFIG_RTC_DRV_MAX77686)	+= rtc-max77686.o
+ obj-$(CONFIG_RTC_DRV_MC13XXX)	+= rtc-mc13xxx.o
++obj-$(CONFIG_RTC_DRV_MCP7940X)  += rtc-mcp7940x.o
+ obj-$(CONFIG_RTC_DRV_MSM6242)	+= rtc-msm6242.o
+ obj-$(CONFIG_RTC_DRV_MPC5121)	+= rtc-mpc5121.o
+ obj-$(CONFIG_RTC_DRV_MV)	+= rtc-mv.o
diff --git a/recipes-kernel/linux/linux-apf6-rt/059X-armadeus-add-wireless-driver.patch b/recipes-kernel/linux/linux-apf6-rt/059X-armadeus-add-wireless-driver.patch
new file mode 100644
index 0000000..321f64f
--- /dev/null
+++ b/recipes-kernel/linux/linux-apf6-rt/059X-armadeus-add-wireless-driver.patch
@@ -0,0 +1,11643 @@
+diff -Nuar git.old/Documentation/devicetree/bindings/net/wireless/ti-wilink.txt git/Documentation/devicetree/bindings/net/wireless/ti-wilink.txt
+--- git.old/Documentation/devicetree/bindings/net/wireless/ti-wilink.txt	1970-01-01 01:00:00.000000000 +0100
++++ git/Documentation/devicetree/bindings/net/wireless/ti-wilink.txt	2015-03-04 17:09:05.110166548 +0100
+@@ -0,0 +1,68 @@
++TI WiLink Wireless Modules Device Tree Bindings
++===============================================
++
++The WiLink modules provide wireless connectivity, such as WLAN,
++Bluetooth, FM and NFC.
++
++There are several different modules available, which can be grouped by
++their generation: WiLink6, WiLink7 and WiLink8.  WiLink4 is not
++currently supported with device tree.
++
++Currently, only the WLAN portion of the modules is supported with
++device tree.
++
++Required properties:
++--------------------
++
++- compatible: should be "ti,wilink6", "ti,wilink7" or "ti,wilink8"
++- interrupt-parent: the interrupt controller
++- interrupts: out-of-band WLAN interrupt
++	See the interrupt controller's bindings documentation for
++	detailed definition.
++
++Optional properties:
++--------------------
++
++- clocks: list of clocks needed by the chip as follows:
++
++  refclock: the internal WLAN reference clock frequency (required for
++	WiLink6 and WiLink7; not used for WiLink8).
++
++  tcxoclock: the internal WLAN TCXO clock frequency (required for
++	WiLink7 not used for WiLink6 and WiLink8).
++
++  The clocks must be defined and named accordingly.  For example:
++
++  clocks = <&refclock>
++  clock-names = "refclock";
++
++  refclock: refclock {
++		     compatible = "ti,wilink-clock";
++		     #clock-cells = <0>;
++		     clock-frequency = <38400000>;
++	};
++
++  Some modules that contain the WiLink chip provide clocks in the
++  module itself.  In this case, we define a "ti,wilink-clock" as shown
++  above.  But any other clock could in theory be used, so the proper
++  clock definition should be used.
++
++
++Example:
++--------
++
++Example definition that can be used in OMAP4 Panda:
++
++wlan {
++	compatible = "ti,wilink6";
++	interrupt-parent = <&gpio2>;
++	interrupts = <21 IRQ_TYPE_LEVEL_HIGH>;	/* gpio line 53 */
++	clocks = <&refclock>;
++	clock-names = "refclock";
++
++	refclock: refclock {
++		compatible = "ti,wilink-clock";
++		#clock-cells = <0>;
++		clock-frequency = <38400000>;
++	};
++};
+diff -Nuar git.old/drivers/net/wireless/ti/wl12xx/main.c git/drivers/net/wireless/ti/wl12xx/main.c
+--- git.old/drivers/net/wireless/ti/wl12xx/main.c	2015-03-04 16:35:05.646185998 +0100
++++ git/drivers/net/wireless/ti/wl12xx/main.c	2015-03-04 17:07:29.614167458 +0100
+@@ -927,6 +927,11 @@
+ 	u16 sys_clk_cfg;
+ 	int ret;
+ 
++	if ((priv->ref_clock < 0) || (priv->tcxo_clock < 0)) {
++		wl1271_error("Missing fref and/or tcxo clock settings\n");
++		return -EINVAL;
++	}
++
+ 	/* For XTAL-only modes, FREF will be used after switching from TCXO */
+ 	if (priv->ref_clock == WL12XX_REFCLOCK_26_XTAL ||
+ 	    priv->ref_clock == WL12XX_REFCLOCK_38_XTAL) {
+@@ -976,6 +981,11 @@
+ 	u32 clk;
+ 	int ret;
+ 
++	if (priv->ref_clock < 0) {
++		wl1271_error("Missing fref clock settings\n");
++		return -EINVAL;
++	}
++
+ 	if (WL127X_PG_GET_MAJOR(wl->hw_pg_ver) < 3)
+ 		wl->quirks |= WLCORE_QUIRK_END_OF_TRANSACTION;
+ 
+@@ -1701,6 +1711,43 @@
+ 		},
+ };
+ 
++static const struct wl12xx_clock wl12xx_refclock_table[] = {
++	{ 19200000,	false,	WL12XX_REFCLOCK_19	},
++	{ 26000000,	false,	WL12XX_REFCLOCK_26	},
++	{ 26000000,	true,	WL12XX_REFCLOCK_26_XTAL	},
++	{ 38400000,	false,	WL12XX_REFCLOCK_38	},
++	{ 38400000,	true,	WL12XX_REFCLOCK_38_XTAL	},
++	{ 52000000,	false,	WL12XX_REFCLOCK_52	},
++	{ 0,		false,	0 }
++};
++
++static const struct wl12xx_clock wl12xx_tcxoclock_table[] = {
++	{ 16368000,	true,	WL12XX_TCXOCLOCK_16_368	},
++	{ 16800000,	true,	WL12XX_TCXOCLOCK_16_8	},
++	{ 19200000,	true,	WL12XX_TCXOCLOCK_19_2	},
++	{ 26000000,	true,	WL12XX_TCXOCLOCK_26	},
++	{ 32736000,	true,	WL12XX_TCXOCLOCK_32_736	},
++	{ 33600000,	true,	WL12XX_TCXOCLOCK_33_6	},
++	{ 38400000,	true,	WL12XX_TCXOCLOCK_38_4	},
++	{ 52000000,	true,	WL12XX_TCXOCLOCK_52	},
++	{ 0,		false,	0 }
++};
++
++static int wl12xx_get_clock_idx(const struct wl12xx_clock *table,
++				u32 freq, bool xtal)
++{
++	int i = 0;
++
++	while(table[i].freq != 0) {
++		if ((table[i].freq == freq) &&
++		    (table[i].xtal == xtal))
++			return table[i].hw_idx;
++		i++;
++	};
++
++	return -EINVAL;
++}
++
+ static int wl12xx_setup(struct wl1271 *wl)
+ {
+ 	struct wl12xx_priv *priv = wl->priv;
+@@ -1721,8 +1768,19 @@
+ 	wlcore_set_ht_cap(wl, IEEE80211_BAND_5GHZ, &wl12xx_ht_cap);
+ 	wl12xx_conf_init(wl);
+ 
+-	if (!fref_param) {
+-		priv->ref_clock = pdata->board_ref_clock;
++	if (!fref_param && (pdata->ref_clock_freq > 0)) {
++		priv->ref_clock = wl12xx_get_clock_idx(wl12xx_refclock_table,
++						       pdata->ref_clock_freq,
++						       pdata->ref_clock_xtal);
++		if (priv->ref_clock < 0) {
++			wl1271_error("Invalid ref_clock frequency (%d Hz, %s)",
++				pdata->ref_clock_freq,
++				pdata->ref_clock_xtal ? "XTAL" : "not XTAL");
++
++			return priv->ref_clock;
++		}
++	} else if (!fref_param) {
++		priv->ref_clock = -EINVAL;
+ 	} else {
+ 		if (!strcmp(fref_param, "19.2"))
+ 			priv->ref_clock = WL12XX_REFCLOCK_19;
+@@ -1740,9 +1798,19 @@
+ 			wl1271_error("Invalid fref parameter %s", fref_param);
+ 	}
+ 
+-	if (!tcxo_param) {
+-		priv->tcxo_clock = pdata->board_tcxo_clock;
+-	} else {
++	if (!fref_param && (pdata->tcxo_clock_freq > 0)) {
++		priv->tcxo_clock = wl12xx_get_clock_idx(wl12xx_tcxoclock_table,
++							pdata->tcxo_clock_freq,
++							true);
++		if (priv->tcxo_clock < 0) {
++			wl1271_error("Invalid tcxo_clock frequency (%d Hz)",
++				pdata->tcxo_clock_freq);
++
++			return priv->tcxo_clock;
++		}
++	} else if (!fref_param) {
++		priv->tcxo_clock = -EINVAL;
++	}else {
+ 		if (!strcmp(tcxo_param, "19.2"))
+ 			priv->tcxo_clock = WL12XX_TCXOCLOCK_19_2;
+ 		else if (!strcmp(tcxo_param, "26"))
+diff -Nuar git.old/drivers/net/wireless/ti/wl12xx/main.c.orig git/drivers/net/wireless/ti/wl12xx/main.c.orig
+--- git.old/drivers/net/wireless/ti/wl12xx/main.c.orig	1970-01-01 01:00:00.000000000 +0100
++++ git/drivers/net/wireless/ti/wl12xx/main.c.orig	2015-03-04 16:35:05.646185998 +0100
+@@ -0,0 +1,1850 @@
++/*
++ * This file is part of wl1271
++ *
++ * Copyright (C) 2008-2010 Nokia Corporation
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++ * 02110-1301 USA
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/platform_device.h>
++
++#include <linux/err.h>
++
++#include <linux/wl12xx.h>
++
++#include "../wlcore/wlcore.h"
++#include "../wlcore/debug.h"
++#include "../wlcore/io.h"
++#include "../wlcore/acx.h"
++#include "../wlcore/tx.h"
++#include "../wlcore/rx.h"
++#include "../wlcore/boot.h"
++
++#include "wl12xx.h"
++#include "reg.h"
++#include "cmd.h"
++#include "acx.h"
++#include "scan.h"
++#include "event.h"
++#include "debugfs.h"
++
++static char *fref_param;
++static char *tcxo_param;
++
++static struct wlcore_conf wl12xx_conf = {
++	.sg = {
++		.params = {
++			[CONF_SG_ACL_BT_MASTER_MIN_BR] = 10,
++			[CONF_SG_ACL_BT_MASTER_MAX_BR] = 180,
++			[CONF_SG_ACL_BT_SLAVE_MIN_BR] = 10,
++			[CONF_SG_ACL_BT_SLAVE_MAX_BR] = 180,
++			[CONF_SG_ACL_BT_MASTER_MIN_EDR] = 10,
++			[CONF_SG_ACL_BT_MASTER_MAX_EDR] = 80,
++			[CONF_SG_ACL_BT_SLAVE_MIN_EDR] = 10,
++			[CONF_SG_ACL_BT_SLAVE_MAX_EDR] = 80,
++			[CONF_SG_ACL_WLAN_PS_MASTER_BR] = 8,
++			[CONF_SG_ACL_WLAN_PS_SLAVE_BR] = 8,
++			[CONF_SG_ACL_WLAN_PS_MASTER_EDR] = 20,
++			[CONF_SG_ACL_WLAN_PS_SLAVE_EDR] = 20,
++			[CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_BR] = 20,
++			[CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_BR] = 35,
++			[CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_BR] = 16,
++			[CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_BR] = 35,
++			[CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_EDR] = 32,
++			[CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_EDR] = 50,
++			[CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_EDR] = 28,
++			[CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_EDR] = 50,
++			[CONF_SG_ACL_ACTIVE_SCAN_WLAN_BR] = 10,
++			[CONF_SG_ACL_ACTIVE_SCAN_WLAN_EDR] = 20,
++			[CONF_SG_ACL_PASSIVE_SCAN_BT_BR] = 75,
++			[CONF_SG_ACL_PASSIVE_SCAN_WLAN_BR] = 15,
++			[CONF_SG_ACL_PASSIVE_SCAN_BT_EDR] = 27,
++			[CONF_SG_ACL_PASSIVE_SCAN_WLAN_EDR] = 17,
++			/* active scan params */
++			[CONF_SG_AUTO_SCAN_PROBE_REQ] = 170,
++			[CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50,
++			[CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100,
++			/* passive scan params */
++			[CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_BR] = 800,
++			[CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_EDR] = 200,
++			[CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3] = 200,
++			/* passive scan in dual antenna params */
++			[CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN] = 0,
++			[CONF_SG_BCN_HV3_COLLISION_THRESH_IN_PASSIVE_SCAN] = 0,
++			[CONF_SG_TX_RX_PROTECTION_BWIDTH_IN_PASSIVE_SCAN] = 0,
++			/* general params */
++			[CONF_SG_STA_FORCE_PS_IN_BT_SCO] = 1,
++			[CONF_SG_ANTENNA_CONFIGURATION] = 0,
++			[CONF_SG_BEACON_MISS_PERCENT] = 60,
++			[CONF_SG_DHCP_TIME] = 5000,
++			[CONF_SG_RXT] = 1200,
++			[CONF_SG_TXT] = 1000,
++			[CONF_SG_ADAPTIVE_RXT_TXT] = 1,
++			[CONF_SG_GENERAL_USAGE_BIT_MAP] = 3,
++			[CONF_SG_HV3_MAX_SERVED] = 6,
++			[CONF_SG_PS_POLL_TIMEOUT] = 10,
++			[CONF_SG_UPSD_TIMEOUT] = 10,
++			[CONF_SG_CONSECUTIVE_CTS_THRESHOLD] = 2,
++			[CONF_SG_STA_RX_WINDOW_AFTER_DTIM] = 5,
++			[CONF_SG_STA_CONNECTION_PROTECTION_TIME] = 30,
++			/* AP params */
++			[CONF_AP_BEACON_MISS_TX] = 3,
++			[CONF_AP_RX_WINDOW_AFTER_BEACON] = 10,
++			[CONF_AP_BEACON_WINDOW_INTERVAL] = 2,
++			[CONF_AP_CONNECTION_PROTECTION_TIME] = 0,
++			[CONF_AP_BT_ACL_VAL_BT_SERVE_TIME] = 25,
++			[CONF_AP_BT_ACL_VAL_WL_SERVE_TIME] = 25,
++			/* CTS Diluting params */
++			[CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH] = 0,
++			[CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER] = 0,
++		},
++		.state = CONF_SG_PROTECTIVE,
++	},
++	.rx = {
++		.rx_msdu_life_time           = 512000,
++		.packet_detection_threshold  = 0,
++		.ps_poll_timeout             = 15,
++		.upsd_timeout                = 15,
++		.rts_threshold               = IEEE80211_MAX_RTS_THRESHOLD,
++		.rx_cca_threshold            = 0,
++		.irq_blk_threshold           = 0xFFFF,
++		.irq_pkt_threshold           = 0,
++		.irq_timeout                 = 600,
++		.queue_type                  = CONF_RX_QUEUE_TYPE_LOW_PRIORITY,
++	},
++	.tx = {
++		.tx_energy_detection         = 0,
++		.sta_rc_conf                 = {
++			.enabled_rates       = 0,
++			.short_retry_limit   = 10,
++			.long_retry_limit    = 10,
++			.aflags              = 0,
++		},
++		.ac_conf_count               = 4,
++		.ac_conf                     = {
++			[CONF_TX_AC_BE] = {
++				.ac          = CONF_TX_AC_BE,
++				.cw_min      = 15,
++				.cw_max      = 63,
++				.aifsn       = 3,
++				.tx_op_limit = 0,
++			},
++			[CONF_TX_AC_BK] = {
++				.ac          = CONF_TX_AC_BK,
++				.cw_min      = 15,
++				.cw_max      = 63,
++				.aifsn       = 7,
++				.tx_op_limit = 0,
++			},
++			[CONF_TX_AC_VI] = {
++				.ac          = CONF_TX_AC_VI,
++				.cw_min      = 15,
++				.cw_max      = 63,
++				.aifsn       = CONF_TX_AIFS_PIFS,
++				.tx_op_limit = 3008,
++			},
++			[CONF_TX_AC_VO] = {
++				.ac          = CONF_TX_AC_VO,
++				.cw_min      = 15,
++				.cw_max      = 63,
++				.aifsn       = CONF_TX_AIFS_PIFS,
++				.tx_op_limit = 1504,
++			},
++		},
++		.max_tx_retries = 100,
++		.ap_aging_period = 300,
++		.tid_conf_count = 4,
++		.tid_conf = {
++			[CONF_TX_AC_BE] = {
++				.queue_id    = CONF_TX_AC_BE,
++				.channel_type = CONF_CHANNEL_TYPE_EDCF,
++				.tsid        = CONF_TX_AC_BE,
++				.ps_scheme   = CONF_PS_SCHEME_LEGACY,
++				.ack_policy  = CONF_ACK_POLICY_LEGACY,
++				.apsd_conf   = {0, 0},
++			},
++			[CONF_TX_AC_BK] = {
++				.queue_id    = CONF_TX_AC_BK,
++				.channel_type = CONF_CHANNEL_TYPE_EDCF,
++				.tsid        = CONF_TX_AC_BK,
++				.ps_scheme   = CONF_PS_SCHEME_LEGACY,
++				.ack_policy  = CONF_ACK_POLICY_LEGACY,
++				.apsd_conf   = {0, 0},
++			},
++			[CONF_TX_AC_VI] = {
++				.queue_id    = CONF_TX_AC_VI,
++				.channel_type = CONF_CHANNEL_TYPE_EDCF,
++				.tsid        = CONF_TX_AC_VI,
++				.ps_scheme   = CONF_PS_SCHEME_LEGACY,
++				.ack_policy  = CONF_ACK_POLICY_LEGACY,
++				.apsd_conf   = {0, 0},
++			},
++			[CONF_TX_AC_VO] = {
++				.queue_id    = CONF_TX_AC_VO,
++				.channel_type = CONF_CHANNEL_TYPE_EDCF,
++				.tsid        = CONF_TX_AC_VO,
++				.ps_scheme   = CONF_PS_SCHEME_LEGACY,
++				.ack_policy  = CONF_ACK_POLICY_LEGACY,
++				.apsd_conf   = {0, 0},
++			},
++		},
++		.frag_threshold              = IEEE80211_MAX_FRAG_THRESHOLD,
++		.tx_compl_timeout            = 700,
++		.tx_compl_threshold          = 4,
++		.basic_rate                  = CONF_HW_BIT_RATE_1MBPS,
++		.basic_rate_5                = CONF_HW_BIT_RATE_6MBPS,
++		.tmpl_short_retry_limit      = 10,
++		.tmpl_long_retry_limit       = 10,
++		.tx_watchdog_timeout         = 5000,
++		.slow_link_thold             = 3,
++		.fast_link_thold             = 10,
++	},
++	.conn = {
++		.wake_up_event               = CONF_WAKE_UP_EVENT_DTIM,
++		.listen_interval             = 1,
++		.suspend_wake_up_event       = CONF_WAKE_UP_EVENT_N_DTIM,
++		.suspend_listen_interval     = 3,
++		.bcn_filt_mode               = CONF_BCN_FILT_MODE_ENABLED,
++		.bcn_filt_ie_count           = 3,
++		.bcn_filt_ie = {
++			[0] = {
++				.ie          = WLAN_EID_CHANNEL_SWITCH,
++				.rule        = CONF_BCN_RULE_PASS_ON_APPEARANCE,
++			},
++			[1] = {
++				.ie          = WLAN_EID_HT_OPERATION,
++				.rule        = CONF_BCN_RULE_PASS_ON_CHANGE,
++			},
++			[2] = {
++				.ie	     = WLAN_EID_ERP_INFO,
++				.rule	     = CONF_BCN_RULE_PASS_ON_CHANGE,
++			},
++		},
++		.synch_fail_thold            = 12,
++		.bss_lose_timeout            = 400,
++		.beacon_rx_timeout           = 10000,
++		.broadcast_timeout           = 20000,
++		.rx_broadcast_in_ps          = 1,
++		.ps_poll_threshold           = 10,
++		.bet_enable                  = CONF_BET_MODE_ENABLE,
++		.bet_max_consecutive         = 50,
++		.psm_entry_retries           = 8,
++		.psm_exit_retries            = 16,
++		.psm_entry_nullfunc_retries  = 3,
++		.dynamic_ps_timeout          = 1500,
++		.forced_ps                   = false,
++		.keep_alive_interval         = 55000,
++		.max_listen_interval         = 20,
++		.sta_sleep_auth              = WL1271_PSM_ILLEGAL,
++	},
++	.itrim = {
++		.enable = false,
++		.timeout = 50000,
++	},
++	.pm_config = {
++		.host_clk_settling_time = 5000,
++		.host_fast_wakeup_support = CONF_FAST_WAKEUP_DISABLE,
++	},
++	.roam_trigger = {
++		.trigger_pacing               = 1,
++		.avg_weight_rssi_beacon       = 20,
++		.avg_weight_rssi_data         = 10,
++		.avg_weight_snr_beacon        = 20,
++		.avg_weight_snr_data          = 10,
++	},
++	.scan = {
++		.min_dwell_time_active        = 7500,
++		.max_dwell_time_active        = 30000,
++		.min_dwell_time_active_long   = 25000,
++		.max_dwell_time_active_long   = 50000,
++		.dwell_time_passive           = 100000,
++		.dwell_time_dfs               = 150000,
++		.num_probe_reqs               = 2,
++		.split_scan_timeout           = 50000,
++	},
++	.sched_scan = {
++		/*
++		 * Values are in TU/1000 but since sched scan FW command
++		 * params are in TUs rounding up may occur.
++		 */
++		.base_dwell_time		= 7500,
++		.max_dwell_time_delta		= 22500,
++		/* based on 250bits per probe @1Mbps */
++		.dwell_time_delta_per_probe	= 2000,
++		/* based on 250bits per probe @6Mbps (plus a bit more) */
++		.dwell_time_delta_per_probe_5	= 350,
++		.dwell_time_passive		= 100000,
++		.dwell_time_dfs			= 150000,
++		.num_probe_reqs			= 2,
++		.rssi_threshold			= -90,
++		.snr_threshold			= 0,
++	},
++	.ht = {
++		.rx_ba_win_size = 8,
++		.tx_ba_win_size = 64,
++		.inactivity_timeout = 10000,
++		.tx_ba_tid_bitmap = CONF_TX_BA_ENABLED_TID_BITMAP,
++	},
++	/*
++	 * Memory config for wl127x chips is given in the
++	 * wl12xx_default_priv_conf struct. The below configuration is
++	 * for wl128x chips.
++	 */
++	.mem = {
++		.num_stations                 = 1,
++		.ssid_profiles                = 1,
++		.rx_block_num                 = 40,
++		.tx_min_block_num             = 40,
++		.dynamic_memory               = 1,
++		.min_req_tx_blocks            = 45,
++		.min_req_rx_blocks            = 22,
++		.tx_min                       = 27,
++	},
++	.fm_coex = {
++		.enable                       = true,
++		.swallow_period               = 5,
++		.n_divider_fref_set_1         = 0xff,       /* default */
++		.n_divider_fref_set_2         = 12,
++		.m_divider_fref_set_1         = 0xffff,
++		.m_divider_fref_set_2         = 148,	    /* default */
++		.coex_pll_stabilization_time  = 0xffffffff, /* default */
++		.ldo_stabilization_time       = 0xffff,     /* default */
++		.fm_disturbed_band_margin     = 0xff,       /* default */
++		.swallow_clk_diff             = 0xff,       /* default */
++	},
++	.rx_streaming = {
++		.duration                      = 150,
++		.queues                        = 0x1,
++		.interval                      = 20,
++		.always                        = 0,
++	},
++	.fwlog = {
++		.mode                         = WL12XX_FWLOG_ON_DEMAND,
++		.mem_blocks                   = 2,
++		.severity                     = 0,
++		.timestamp                    = WL12XX_FWLOG_TIMESTAMP_DISABLED,
++		.output                       = WL12XX_FWLOG_OUTPUT_HOST,
++		.threshold                    = 0,
++	},
++	.rate = {
++		.rate_retry_score = 32000,
++		.per_add = 8192,
++		.per_th1 = 2048,
++		.per_th2 = 4096,
++		.max_per = 8100,
++		.inverse_curiosity_factor = 5,
++		.tx_fail_low_th = 4,
++		.tx_fail_high_th = 10,
++		.per_alpha_shift = 4,
++		.per_add_shift = 13,
++		.per_beta1_shift = 10,
++		.per_beta2_shift = 8,
++		.rate_check_up = 2,
++		.rate_check_down = 12,
++		.rate_retry_policy = {
++			0x00, 0x00, 0x00, 0x00, 0x00,
++			0x00, 0x00, 0x00, 0x00, 0x00,
++			0x00, 0x00, 0x00,
++		},
++	},
++	.hangover = {
++		.recover_time               = 0,
++		.hangover_period            = 20,
++		.dynamic_mode               = 1,
++		.early_termination_mode     = 1,
++		.max_period                 = 20,
++		.min_period                 = 1,
++		.increase_delta             = 1,
++		.decrease_delta             = 2,
++		.quiet_time                 = 4,
++		.increase_time              = 1,
++		.window_size                = 16,
++	},
++	.recovery = {
++		.bug_on_recovery	    = 0,
++		.no_recovery		    = 0,
++	},
++};
++
++static struct wl12xx_priv_conf wl12xx_default_priv_conf = {
++	.rf = {
++		.tx_per_channel_power_compensation_2 = {
++			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++		},
++		.tx_per_channel_power_compensation_5 = {
++			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++		},
++	},
++	.mem_wl127x = {
++		.num_stations                 = 1,
++		.ssid_profiles                = 1,
++		.rx_block_num                 = 70,
++		.tx_min_block_num             = 40,
++		.dynamic_memory               = 1,
++		.min_req_tx_blocks            = 100,
++		.min_req_rx_blocks            = 22,
++		.tx_min                       = 27,
++	},
++
++};
++
++#define WL12XX_TX_HW_BLOCK_SPARE_DEFAULT        1
++#define WL12XX_TX_HW_BLOCK_GEM_SPARE            2
++#define WL12XX_TX_HW_BLOCK_SIZE                 252
++
++static const u8 wl12xx_rate_to_idx_2ghz[] = {
++	/* MCS rates are used only with 11n */
++	7,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS7_SGI */
++	7,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS7 */
++	6,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS6 */
++	5,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS5 */
++	4,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS4 */
++	3,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS3 */
++	2,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS2 */
++	1,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS1 */
++	0,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS0 */
++
++	11,                            /* WL12XX_CONF_HW_RXTX_RATE_54   */
++	10,                            /* WL12XX_CONF_HW_RXTX_RATE_48   */
++	9,                             /* WL12XX_CONF_HW_RXTX_RATE_36   */
++	8,                             /* WL12XX_CONF_HW_RXTX_RATE_24   */
++
++	/* TI-specific rate */
++	CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL12XX_CONF_HW_RXTX_RATE_22   */
++
++	7,                             /* WL12XX_CONF_HW_RXTX_RATE_18   */
++	6,                             /* WL12XX_CONF_HW_RXTX_RATE_12   */
++	3,                             /* WL12XX_CONF_HW_RXTX_RATE_11   */
++	5,                             /* WL12XX_CONF_HW_RXTX_RATE_9    */
++	4,                             /* WL12XX_CONF_HW_RXTX_RATE_6    */
++	2,                             /* WL12XX_CONF_HW_RXTX_RATE_5_5  */
++	1,                             /* WL12XX_CONF_HW_RXTX_RATE_2    */
++	0                              /* WL12XX_CONF_HW_RXTX_RATE_1    */
++};
++
++static const u8 wl12xx_rate_to_idx_5ghz[] = {
++	/* MCS rates are used only with 11n */
++	7,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS7_SGI */
++	7,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS7 */
++	6,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS6 */
++	5,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS5 */
++	4,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS4 */
++	3,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS3 */
++	2,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS2 */
++	1,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS1 */
++	0,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS0 */
++
++	7,                             /* WL12XX_CONF_HW_RXTX_RATE_54   */
++	6,                             /* WL12XX_CONF_HW_RXTX_RATE_48   */
++	5,                             /* WL12XX_CONF_HW_RXTX_RATE_36   */
++	4,                             /* WL12XX_CONF_HW_RXTX_RATE_24   */
++
++	/* TI-specific rate */
++	CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL12XX_CONF_HW_RXTX_RATE_22   */
++
++	3,                             /* WL12XX_CONF_HW_RXTX_RATE_18   */
++	2,                             /* WL12XX_CONF_HW_RXTX_RATE_12   */
++	CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL12XX_CONF_HW_RXTX_RATE_11   */
++	1,                             /* WL12XX_CONF_HW_RXTX_RATE_9    */
++	0,                             /* WL12XX_CONF_HW_RXTX_RATE_6    */
++	CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL12XX_CONF_HW_RXTX_RATE_5_5  */
++	CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL12XX_CONF_HW_RXTX_RATE_2    */
++	CONF_HW_RXTX_RATE_UNSUPPORTED  /* WL12XX_CONF_HW_RXTX_RATE_1    */
++};
++
++static const u8 *wl12xx_band_rate_to_idx[] = {
++	[IEEE80211_BAND_2GHZ] = wl12xx_rate_to_idx_2ghz,
++	[IEEE80211_BAND_5GHZ] = wl12xx_rate_to_idx_5ghz
++};
++
++enum wl12xx_hw_rates {
++	WL12XX_CONF_HW_RXTX_RATE_MCS7_SGI = 0,
++	WL12XX_CONF_HW_RXTX_RATE_MCS7,
++	WL12XX_CONF_HW_RXTX_RATE_MCS6,
++	WL12XX_CONF_HW_RXTX_RATE_MCS5,
++	WL12XX_CONF_HW_RXTX_RATE_MCS4,
++	WL12XX_CONF_HW_RXTX_RATE_MCS3,
++	WL12XX_CONF_HW_RXTX_RATE_MCS2,
++	WL12XX_CONF_HW_RXTX_RATE_MCS1,
++	WL12XX_CONF_HW_RXTX_RATE_MCS0,
++	WL12XX_CONF_HW_RXTX_RATE_54,
++	WL12XX_CONF_HW_RXTX_RATE_48,
++	WL12XX_CONF_HW_RXTX_RATE_36,
++	WL12XX_CONF_HW_RXTX_RATE_24,
++	WL12XX_CONF_HW_RXTX_RATE_22,
++	WL12XX_CONF_HW_RXTX_RATE_18,
++	WL12XX_CONF_HW_RXTX_RATE_12,
++	WL12XX_CONF_HW_RXTX_RATE_11,
++	WL12XX_CONF_HW_RXTX_RATE_9,
++	WL12XX_CONF_HW_RXTX_RATE_6,
++	WL12XX_CONF_HW_RXTX_RATE_5_5,
++	WL12XX_CONF_HW_RXTX_RATE_2,
++	WL12XX_CONF_HW_RXTX_RATE_1,
++	WL12XX_CONF_HW_RXTX_RATE_MAX,
++};
++
++static struct wlcore_partition_set wl12xx_ptable[PART_TABLE_LEN] = {
++	[PART_DOWN] = {
++		.mem = {
++			.start = 0x00000000,
++			.size  = 0x000177c0
++		},
++		.reg = {
++			.start = REGISTERS_BASE,
++			.size  = 0x00008800
++		},
++		.mem2 = {
++			.start = 0x00000000,
++			.size  = 0x00000000
++		},
++		.mem3 = {
++			.start = 0x00000000,
++			.size  = 0x00000000
++		},
++	},
++
++	[PART_BOOT] = { /* in wl12xx we can use a mix of work and down
++			 * partition here */
++		.mem = {
++			.start = 0x00040000,
++			.size  = 0x00014fc0
++		},
++		.reg = {
++			.start = REGISTERS_BASE,
++			.size  = 0x00008800
++		},
++		.mem2 = {
++			.start = 0x00000000,
++			.size  = 0x00000000
++		},
++		.mem3 = {
++			.start = 0x00000000,
++			.size  = 0x00000000
++		},
++	},
++
++	[PART_WORK] = {
++		.mem = {
++			.start = 0x00040000,
++			.size  = 0x00014fc0
++		},
++		.reg = {
++			.start = REGISTERS_BASE,
++			.size  = 0x0000a000
++		},
++		.mem2 = {
++			.start = 0x003004f8,
++			.size  = 0x00000004
++		},
++		.mem3 = {
++			.start = 0x00040404,
++			.size  = 0x00000000
++		},
++	},
++
++	[PART_DRPW] = {
++		.mem = {
++			.start = 0x00040000,
++			.size  = 0x00014fc0
++		},
++		.reg = {
++			.start = DRPW_BASE,
++			.size  = 0x00006000
++		},
++		.mem2 = {
++			.start = 0x00000000,
++			.size  = 0x00000000
++		},
++		.mem3 = {
++			.start = 0x00000000,
++			.size  = 0x00000000
++		}
++	}
++};
++
++static const int wl12xx_rtable[REG_TABLE_LEN] = {
++	[REG_ECPU_CONTROL]		= WL12XX_REG_ECPU_CONTROL,
++	[REG_INTERRUPT_NO_CLEAR]	= WL12XX_REG_INTERRUPT_NO_CLEAR,
++	[REG_INTERRUPT_ACK]		= WL12XX_REG_INTERRUPT_ACK,
++	[REG_COMMAND_MAILBOX_PTR]	= WL12XX_REG_COMMAND_MAILBOX_PTR,
++	[REG_EVENT_MAILBOX_PTR]		= WL12XX_REG_EVENT_MAILBOX_PTR,
++	[REG_INTERRUPT_TRIG]		= WL12XX_REG_INTERRUPT_TRIG,
++	[REG_INTERRUPT_MASK]		= WL12XX_REG_INTERRUPT_MASK,
++	[REG_PC_ON_RECOVERY]		= WL12XX_SCR_PAD4,
++	[REG_CHIP_ID_B]			= WL12XX_CHIP_ID_B,
++	[REG_CMD_MBOX_ADDRESS]		= WL12XX_CMD_MBOX_ADDRESS,
++
++	/* data access memory addresses, used with partition translation */
++	[REG_SLV_MEM_DATA]		= WL1271_SLV_MEM_DATA,
++	[REG_SLV_REG_DATA]		= WL1271_SLV_REG_DATA,
++
++	/* raw data access memory addresses */
++	[REG_RAW_FW_STATUS_ADDR]	= FW_STATUS_ADDR,
++};
++
++/* TODO: maybe move to a new header file? */
++#define WL127X_FW_NAME_MULTI	"ti-connectivity/wl127x-fw-5-mr.bin"
++#define WL127X_FW_NAME_SINGLE	"ti-connectivity/wl127x-fw-5-sr.bin"
++#define WL127X_PLT_FW_NAME	"ti-connectivity/wl127x-fw-5-plt.bin"
++
++#define WL128X_FW_NAME_MULTI	"ti-connectivity/wl128x-fw-5-mr.bin"
++#define WL128X_FW_NAME_SINGLE	"ti-connectivity/wl128x-fw-5-sr.bin"
++#define WL128X_PLT_FW_NAME	"ti-connectivity/wl128x-fw-5-plt.bin"
++
++static int wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len)
++{
++	int ret;
++
++	if (wl->chip.id != CHIP_ID_128X_PG20) {
++		struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map;
++		struct wl12xx_priv *priv = wl->priv;
++
++		/*
++		 * Choose the block we want to read
++		 * For aggregated packets, only the first memory block
++		 * should be retrieved. The FW takes care of the rest.
++		 */
++		u32 mem_block = rx_desc & RX_MEM_BLOCK_MASK;
++
++		priv->rx_mem_addr->addr = (mem_block << 8) +
++			le32_to_cpu(wl_mem_map->packet_memory_pool_start);
++
++		priv->rx_mem_addr->addr_extra = priv->rx_mem_addr->addr + 4;
++
++		ret = wlcore_write(wl, WL1271_SLV_REG_DATA, priv->rx_mem_addr,
++				   sizeof(*priv->rx_mem_addr), false);
++		if (ret < 0)
++			return ret;
++	}
++
++	return 0;
++}
++
++static int wl12xx_identify_chip(struct wl1271 *wl)
++{
++	int ret = 0;
++
++	switch (wl->chip.id) {
++	case CHIP_ID_127X_PG10:
++		wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete",
++			       wl->chip.id);
++
++		wl->quirks |= WLCORE_QUIRK_LEGACY_NVS |
++			      WLCORE_QUIRK_DUAL_PROBE_TMPL |
++			      WLCORE_QUIRK_TKIP_HEADER_SPACE |
++			      WLCORE_QUIRK_START_STA_FAILS |
++			      WLCORE_QUIRK_AP_ZERO_SESSION_ID;
++		wl->sr_fw_name = WL127X_FW_NAME_SINGLE;
++		wl->mr_fw_name = WL127X_FW_NAME_MULTI;
++		memcpy(&wl->conf.mem, &wl12xx_default_priv_conf.mem_wl127x,
++		       sizeof(wl->conf.mem));
++
++		/* read data preparation is only needed by wl127x */
++		wl->ops->prepare_read = wl127x_prepare_read;
++
++		wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER,
++			      WL127X_IFTYPE_SR_VER,  WL127X_MAJOR_SR_VER,
++			      WL127X_SUBTYPE_SR_VER, WL127X_MINOR_SR_VER,
++			      WL127X_IFTYPE_MR_VER,  WL127X_MAJOR_MR_VER,
++			      WL127X_SUBTYPE_MR_VER, WL127X_MINOR_MR_VER);
++		break;
++
++	case CHIP_ID_127X_PG20:
++		wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
++			     wl->chip.id);
++
++		wl->quirks |= WLCORE_QUIRK_LEGACY_NVS |
++			      WLCORE_QUIRK_DUAL_PROBE_TMPL |
++			      WLCORE_QUIRK_TKIP_HEADER_SPACE |
++			      WLCORE_QUIRK_START_STA_FAILS |
++			      WLCORE_QUIRK_AP_ZERO_SESSION_ID;
++		wl->plt_fw_name = WL127X_PLT_FW_NAME;
++		wl->sr_fw_name = WL127X_FW_NAME_SINGLE;
++		wl->mr_fw_name = WL127X_FW_NAME_MULTI;
++		memcpy(&wl->conf.mem, &wl12xx_default_priv_conf.mem_wl127x,
++		       sizeof(wl->conf.mem));
++
++		/* read data preparation is only needed by wl127x */
++		wl->ops->prepare_read = wl127x_prepare_read;
++
++		wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER,
++			      WL127X_IFTYPE_SR_VER,  WL127X_MAJOR_SR_VER,
++			      WL127X_SUBTYPE_SR_VER, WL127X_MINOR_SR_VER,
++			      WL127X_IFTYPE_MR_VER,  WL127X_MAJOR_MR_VER,
++			      WL127X_SUBTYPE_MR_VER, WL127X_MINOR_MR_VER);
++		break;
++
++	case CHIP_ID_128X_PG20:
++		wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1283 PG20)",
++			     wl->chip.id);
++		wl->plt_fw_name = WL128X_PLT_FW_NAME;
++		wl->sr_fw_name = WL128X_FW_NAME_SINGLE;
++		wl->mr_fw_name = WL128X_FW_NAME_MULTI;
++
++		/* wl128x requires TX blocksize alignment */
++		wl->quirks |= WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN |
++			      WLCORE_QUIRK_DUAL_PROBE_TMPL |
++			      WLCORE_QUIRK_TKIP_HEADER_SPACE |
++			      WLCORE_QUIRK_START_STA_FAILS |
++			      WLCORE_QUIRK_AP_ZERO_SESSION_ID;
++
++		wlcore_set_min_fw_ver(wl, WL128X_CHIP_VER,
++			      WL128X_IFTYPE_SR_VER,  WL128X_MAJOR_SR_VER,
++			      WL128X_SUBTYPE_SR_VER, WL128X_MINOR_SR_VER,
++			      WL128X_IFTYPE_MR_VER,  WL128X_MAJOR_MR_VER,
++			      WL128X_SUBTYPE_MR_VER, WL128X_MINOR_MR_VER);
++		break;
++	case CHIP_ID_128X_PG10:
++	default:
++		wl1271_warning("unsupported chip id: 0x%x", wl->chip.id);
++		ret = -ENODEV;
++		goto out;
++	}
++
++	/* common settings */
++	wl->scan_templ_id_2_4 = CMD_TEMPL_APP_PROBE_REQ_2_4_LEGACY;
++	wl->scan_templ_id_5 = CMD_TEMPL_APP_PROBE_REQ_5_LEGACY;
++	wl->sched_scan_templ_id_2_4 = CMD_TEMPL_CFG_PROBE_REQ_2_4;
++	wl->sched_scan_templ_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5;
++	wl->max_channels_5 = WL12XX_MAX_CHANNELS_5GHZ;
++	wl->ba_rx_session_count_max = WL12XX_RX_BA_MAX_SESSIONS;
++out:
++	return ret;
++}
++
++static int __must_check wl12xx_top_reg_write(struct wl1271 *wl, int addr,
++					     u16 val)
++{
++	int ret;
++
++	/* write address >> 1 + 0x30000 to OCP_POR_CTR */
++	addr = (addr >> 1) + 0x30000;
++	ret = wlcore_write32(wl, WL12XX_OCP_POR_CTR, addr);
++	if (ret < 0)
++		goto out;
++
++	/* write value to OCP_POR_WDATA */
++	ret = wlcore_write32(wl, WL12XX_OCP_DATA_WRITE, val);
++	if (ret < 0)
++		goto out;
++
++	/* write 1 to OCP_CMD */
++	ret = wlcore_write32(wl, WL12XX_OCP_CMD, OCP_CMD_WRITE);
++	if (ret < 0)
++		goto out;
++
++out:
++	return ret;
++}
++
++static int __must_check wl12xx_top_reg_read(struct wl1271 *wl, int addr,
++					    u16 *out)
++{
++	u32 val;
++	int timeout = OCP_CMD_LOOP;
++	int ret;
++
++	/* write address >> 1 + 0x30000 to OCP_POR_CTR */
++	addr = (addr >> 1) + 0x30000;
++	ret = wlcore_write32(wl, WL12XX_OCP_POR_CTR, addr);
++	if (ret < 0)
++		return ret;
++
++	/* write 2 to OCP_CMD */
++	ret = wlcore_write32(wl, WL12XX_OCP_CMD, OCP_CMD_READ);
++	if (ret < 0)
++		return ret;
++
++	/* poll for data ready */
++	do {
++		ret = wlcore_read32(wl, WL12XX_OCP_DATA_READ, &val);
++		if (ret < 0)
++			return ret;
++	} while (!(val & OCP_READY_MASK) && --timeout);
++
++	if (!timeout) {
++		wl1271_warning("Top register access timed out.");
++		return -ETIMEDOUT;
++	}
++
++	/* check data status and return if OK */
++	if ((val & OCP_STATUS_MASK) != OCP_STATUS_OK) {
++		wl1271_warning("Top register access returned error.");
++		return -EIO;
++	}
++
++	if (out)
++		*out = val & 0xffff;
++
++	return 0;
++}
++
++static int wl128x_switch_tcxo_to_fref(struct wl1271 *wl)
++{
++	u16 spare_reg;
++	int ret;
++
++	/* Mask bits [2] & [8:4] in the sys_clk_cfg register */
++	ret = wl12xx_top_reg_read(wl, WL_SPARE_REG, &spare_reg);
++	if (ret < 0)
++		return ret;
++
++	if (spare_reg == 0xFFFF)
++		return -EFAULT;
++	spare_reg |= (BIT(3) | BIT(5) | BIT(6));
++	ret = wl12xx_top_reg_write(wl, WL_SPARE_REG, spare_reg);
++	if (ret < 0)
++		return ret;
++
++	/* Enable FREF_CLK_REQ & mux MCS and coex PLLs to FREF */
++	ret = wl12xx_top_reg_write(wl, SYS_CLK_CFG_REG,
++				   WL_CLK_REQ_TYPE_PG2 | MCS_PLL_CLK_SEL_FREF);
++	if (ret < 0)
++		return ret;
++
++	/* Delay execution for 15msec, to let the HW settle */
++	mdelay(15);
++
++	return 0;
++}
++
++static bool wl128x_is_tcxo_valid(struct wl1271 *wl)
++{
++	u16 tcxo_detection;
++	int ret;
++
++	ret = wl12xx_top_reg_read(wl, TCXO_CLK_DETECT_REG, &tcxo_detection);
++	if (ret < 0)
++		return false;
++
++	if (tcxo_detection & TCXO_DET_FAILED)
++		return false;
++
++	return true;
++}
++
++static bool wl128x_is_fref_valid(struct wl1271 *wl)
++{
++	u16 fref_detection;
++	int ret;
++
++	ret = wl12xx_top_reg_read(wl, FREF_CLK_DETECT_REG, &fref_detection);
++	if (ret < 0)
++		return false;
++
++	if (fref_detection & FREF_CLK_DETECT_FAIL)
++		return false;
++
++	return true;
++}
++
++static int wl128x_manually_configure_mcs_pll(struct wl1271 *wl)
++{
++	int ret;
++
++	ret = wl12xx_top_reg_write(wl, MCS_PLL_M_REG, MCS_PLL_M_REG_VAL);
++	if (ret < 0)
++		goto out;
++
++	ret = wl12xx_top_reg_write(wl, MCS_PLL_N_REG, MCS_PLL_N_REG_VAL);
++	if (ret < 0)
++		goto out;
++
++	ret = wl12xx_top_reg_write(wl, MCS_PLL_CONFIG_REG,
++				   MCS_PLL_CONFIG_REG_VAL);
++
++out:
++	return ret;
++}
++
++static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk)
++{
++	u16 spare_reg;
++	u16 pll_config;
++	u8 input_freq;
++	struct wl12xx_priv *priv = wl->priv;
++	int ret;
++
++	/* Mask bits [3:1] in the sys_clk_cfg register */
++	ret = wl12xx_top_reg_read(wl, WL_SPARE_REG, &spare_reg);
++	if (ret < 0)
++		return ret;
++
++	if (spare_reg == 0xFFFF)
++		return -EFAULT;
++	spare_reg |= BIT(2);
++	ret = wl12xx_top_reg_write(wl, WL_SPARE_REG, spare_reg);
++	if (ret < 0)
++		return ret;
++
++	/* Handle special cases of the TCXO clock */
++	if (priv->tcxo_clock == WL12XX_TCXOCLOCK_16_8 ||
++	    priv->tcxo_clock == WL12XX_TCXOCLOCK_33_6)
++		return wl128x_manually_configure_mcs_pll(wl);
++
++	/* Set the input frequency according to the selected clock source */
++	input_freq = (clk & 1) + 1;
++
++	ret = wl12xx_top_reg_read(wl, MCS_PLL_CONFIG_REG, &pll_config);
++	if (ret < 0)
++		return ret;
++
++	if (pll_config == 0xFFFF)
++		return -EFAULT;
++	pll_config |= (input_freq << MCS_SEL_IN_FREQ_SHIFT);
++	pll_config |= MCS_PLL_ENABLE_HP;
++	ret = wl12xx_top_reg_write(wl, MCS_PLL_CONFIG_REG, pll_config);
++
++	return ret;
++}
++
++/*
++ * WL128x has two clocks input - TCXO and FREF.
++ * TCXO is the main clock of the device, while FREF is used to sync
++ * between the GPS and the cellular modem.
++ * In cases where TCXO is 32.736MHz or 16.368MHz, the FREF will be used
++ * as the WLAN/BT main clock.
++ */
++static int wl128x_boot_clk(struct wl1271 *wl, int *selected_clock)
++{
++	struct wl12xx_priv *priv = wl->priv;
++	u16 sys_clk_cfg;
++	int ret;
++
++	/* For XTAL-only modes, FREF will be used after switching from TCXO */
++	if (priv->ref_clock == WL12XX_REFCLOCK_26_XTAL ||
++	    priv->ref_clock == WL12XX_REFCLOCK_38_XTAL) {
++		if (!wl128x_switch_tcxo_to_fref(wl))
++			return -EINVAL;
++		goto fref_clk;
++	}
++
++	/* Query the HW, to determine which clock source we should use */
++	ret = wl12xx_top_reg_read(wl, SYS_CLK_CFG_REG, &sys_clk_cfg);
++	if (ret < 0)
++		return ret;
++
++	if (sys_clk_cfg == 0xFFFF)
++		return -EINVAL;
++	if (sys_clk_cfg & PRCM_CM_EN_MUX_WLAN_FREF)
++		goto fref_clk;
++
++	/* If TCXO is either 32.736MHz or 16.368MHz, switch to FREF */
++	if (priv->tcxo_clock == WL12XX_TCXOCLOCK_16_368 ||
++	    priv->tcxo_clock == WL12XX_TCXOCLOCK_32_736) {
++		if (!wl128x_switch_tcxo_to_fref(wl))
++			return -EINVAL;
++		goto fref_clk;
++	}
++
++	/* TCXO clock is selected */
++	if (!wl128x_is_tcxo_valid(wl))
++		return -EINVAL;
++	*selected_clock = priv->tcxo_clock;
++	goto config_mcs_pll;
++
++fref_clk:
++	/* FREF clock is selected */
++	if (!wl128x_is_fref_valid(wl))
++		return -EINVAL;
++	*selected_clock = priv->ref_clock;
++
++config_mcs_pll:
++	return wl128x_configure_mcs_pll(wl, *selected_clock);
++}
++
++static int wl127x_boot_clk(struct wl1271 *wl)
++{
++	struct wl12xx_priv *priv = wl->priv;
++	u32 pause;
++	u32 clk;
++	int ret;
++
++	if (WL127X_PG_GET_MAJOR(wl->hw_pg_ver) < 3)
++		wl->quirks |= WLCORE_QUIRK_END_OF_TRANSACTION;
++
++	if (priv->ref_clock == CONF_REF_CLK_19_2_E ||
++	    priv->ref_clock == CONF_REF_CLK_38_4_E ||
++	    priv->ref_clock == CONF_REF_CLK_38_4_M_XTAL)
++		/* ref clk: 19.2/38.4/38.4-XTAL */
++		clk = 0x3;
++	else if (priv->ref_clock == CONF_REF_CLK_26_E ||
++		 priv->ref_clock == CONF_REF_CLK_26_M_XTAL ||
++		 priv->ref_clock == CONF_REF_CLK_52_E)
++		/* ref clk: 26/52 */
++		clk = 0x5;
++	else
++		return -EINVAL;
++
++	if (priv->ref_clock != CONF_REF_CLK_19_2_E) {
++		u16 val;
++		/* Set clock type (open drain) */
++		ret = wl12xx_top_reg_read(wl, OCP_REG_CLK_TYPE, &val);
++		if (ret < 0)
++			goto out;
++
++		val &= FREF_CLK_TYPE_BITS;
++		ret = wl12xx_top_reg_write(wl, OCP_REG_CLK_TYPE, val);
++		if (ret < 0)
++			goto out;
++
++		/* Set clock pull mode (no pull) */
++		ret = wl12xx_top_reg_read(wl, OCP_REG_CLK_PULL, &val);
++		if (ret < 0)
++			goto out;
++
++		val |= NO_PULL;
++		ret = wl12xx_top_reg_write(wl, OCP_REG_CLK_PULL, val);
++		if (ret < 0)
++			goto out;
++	} else {
++		u16 val;
++		/* Set clock polarity */
++		ret = wl12xx_top_reg_read(wl, OCP_REG_CLK_POLARITY, &val);
++		if (ret < 0)
++			goto out;
++
++		val &= FREF_CLK_POLARITY_BITS;
++		val |= CLK_REQ_OUTN_SEL;
++		ret = wl12xx_top_reg_write(wl, OCP_REG_CLK_POLARITY, val);
++		if (ret < 0)
++			goto out;
++	}
++
++	ret = wlcore_write32(wl, WL12XX_PLL_PARAMETERS, clk);
++	if (ret < 0)
++		goto out;
++
++	ret = wlcore_read32(wl, WL12XX_PLL_PARAMETERS, &pause);
++	if (ret < 0)
++		goto out;
++
++	wl1271_debug(DEBUG_BOOT, "pause1 0x%x", pause);
++
++	pause &= ~(WU_COUNTER_PAUSE_VAL);
++	pause |= WU_COUNTER_PAUSE_VAL;
++	ret = wlcore_write32(wl, WL12XX_WU_COUNTER_PAUSE, pause);
++
++out:
++	return ret;
++}
++
++static int wl1271_boot_soft_reset(struct wl1271 *wl)
++{
++	unsigned long timeout;
++	u32 boot_data;
++	int ret = 0;
++
++	/* perform soft reset */
++	ret = wlcore_write32(wl, WL12XX_SLV_SOFT_RESET, ACX_SLV_SOFT_RESET_BIT);
++	if (ret < 0)
++		goto out;
++
++	/* SOFT_RESET is self clearing */
++	timeout = jiffies + usecs_to_jiffies(SOFT_RESET_MAX_TIME);
++	while (1) {
++		ret = wlcore_read32(wl, WL12XX_SLV_SOFT_RESET, &boot_data);
++		if (ret < 0)
++			goto out;
++
++		wl1271_debug(DEBUG_BOOT, "soft reset bootdata 0x%x", boot_data);
++		if ((boot_data & ACX_SLV_SOFT_RESET_BIT) == 0)
++			break;
++
++		if (time_after(jiffies, timeout)) {
++			/* 1.2 check pWhalBus->uSelfClearTime if the
++			 * timeout was reached */
++			wl1271_error("soft reset timeout");
++			return -1;
++		}
++
++		udelay(SOFT_RESET_STALL_TIME);
++	}
++
++	/* disable Rx/Tx */
++	ret = wlcore_write32(wl, WL12XX_ENABLE, 0x0);
++	if (ret < 0)
++		goto out;
++
++	/* disable auto calibration on start*/
++	ret = wlcore_write32(wl, WL12XX_SPARE_A2, 0xffff);
++
++out:
++	return ret;
++}
++
++static int wl12xx_pre_boot(struct wl1271 *wl)
++{
++	struct wl12xx_priv *priv = wl->priv;
++	int ret = 0;
++	u32 clk;
++	int selected_clock = -1;
++
++	if (wl->chip.id == CHIP_ID_128X_PG20) {
++		ret = wl128x_boot_clk(wl, &selected_clock);
++		if (ret < 0)
++			goto out;
++	} else {
++		ret = wl127x_boot_clk(wl);
++		if (ret < 0)
++			goto out;
++	}
++
++	/* Continue the ELP wake up sequence */
++	ret = wlcore_write32(wl, WL12XX_WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL);
++	if (ret < 0)
++		goto out;
++
++	udelay(500);
++
++	ret = wlcore_set_partition(wl, &wl->ptable[PART_DRPW]);
++	if (ret < 0)
++		goto out;
++
++	/* Read-modify-write DRPW_SCRATCH_START register (see next state)
++	   to be used by DRPw FW. The RTRIM value will be added by the FW
++	   before taking DRPw out of reset */
++
++	ret = wlcore_read32(wl, WL12XX_DRPW_SCRATCH_START, &clk);
++	if (ret < 0)
++		goto out;
++
++	wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk);
++
++	if (wl->chip.id == CHIP_ID_128X_PG20)
++		clk |= ((selected_clock & 0x3) << 1) << 4;
++	else
++		clk |= (priv->ref_clock << 1) << 4;
++
++	ret = wlcore_write32(wl, WL12XX_DRPW_SCRATCH_START, clk);
++	if (ret < 0)
++		goto out;
++
++	ret = wlcore_set_partition(wl, &wl->ptable[PART_WORK]);
++	if (ret < 0)
++		goto out;
++
++	/* Disable interrupts */
++	ret = wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL);
++	if (ret < 0)
++		goto out;
++
++	ret = wl1271_boot_soft_reset(wl);
++	if (ret < 0)
++		goto out;
++
++out:
++	return ret;
++}
++
++static int wl12xx_pre_upload(struct wl1271 *wl)
++{
++	u32 tmp;
++	u16 polarity;
++	int ret;
++
++	/* write firmware's last address (ie. it's length) to
++	 * ACX_EEPROMLESS_IND_REG */
++	wl1271_debug(DEBUG_BOOT, "ACX_EEPROMLESS_IND_REG");
++
++	ret = wlcore_write32(wl, WL12XX_EEPROMLESS_IND, WL12XX_EEPROMLESS_IND);
++	if (ret < 0)
++		goto out;
++
++	ret = wlcore_read_reg(wl, REG_CHIP_ID_B, &tmp);
++	if (ret < 0)
++		goto out;
++
++	wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp);
++
++	/* 6. read the EEPROM parameters */
++	ret = wlcore_read32(wl, WL12XX_SCR_PAD2, &tmp);
++	if (ret < 0)
++		goto out;
++
++	/* WL1271: The reference driver skips steps 7 to 10 (jumps directly
++	 * to upload_fw) */
++
++	if (wl->chip.id == CHIP_ID_128X_PG20) {
++		ret = wl12xx_top_reg_write(wl, SDIO_IO_DS, HCI_IO_DS_6MA);
++		if (ret < 0)
++			goto out;
++	}
++
++	/* polarity must be set before the firmware is loaded */
++	ret = wl12xx_top_reg_read(wl, OCP_REG_POLARITY, &polarity);
++	if (ret < 0)
++		goto out;
++
++	/* We use HIGH polarity, so unset the LOW bit */
++	polarity &= ~POLARITY_LOW;
++	ret = wl12xx_top_reg_write(wl, OCP_REG_POLARITY, polarity);
++
++out:
++	return ret;
++}
++
++static int wl12xx_enable_interrupts(struct wl1271 *wl)
++{
++	int ret;
++
++	ret = wlcore_write_reg(wl, REG_INTERRUPT_MASK,
++			       WL12XX_ACX_ALL_EVENTS_VECTOR);
++	if (ret < 0)
++		goto out;
++
++	wlcore_enable_interrupts(wl);
++	ret = wlcore_write_reg(wl, REG_INTERRUPT_MASK,
++			       WL1271_ACX_INTR_ALL & ~(WL12XX_INTR_MASK));
++	if (ret < 0)
++		goto disable_interrupts;
++
++	ret = wlcore_write32(wl, WL12XX_HI_CFG, HI_CFG_DEF_VAL);
++	if (ret < 0)
++		goto disable_interrupts;
++
++	return ret;
++
++disable_interrupts:
++	wlcore_disable_interrupts(wl);
++
++out:
++	return ret;
++}
++
++static int wl12xx_boot(struct wl1271 *wl)
++{
++	int ret;
++
++	ret = wl12xx_pre_boot(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wlcore_boot_upload_nvs(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wl12xx_pre_upload(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wlcore_boot_upload_firmware(wl);
++	if (ret < 0)
++		goto out;
++
++	wl->event_mask = BSS_LOSE_EVENT_ID |
++		REGAINED_BSS_EVENT_ID |
++		SCAN_COMPLETE_EVENT_ID |
++		ROLE_STOP_COMPLETE_EVENT_ID |
++		RSSI_SNR_TRIGGER_0_EVENT_ID |
++		PSPOLL_DELIVERY_FAILURE_EVENT_ID |
++		SOFT_GEMINI_SENSE_EVENT_ID |
++		PERIODIC_SCAN_REPORT_EVENT_ID |
++		PERIODIC_SCAN_COMPLETE_EVENT_ID |
++		DUMMY_PACKET_EVENT_ID |
++		PEER_REMOVE_COMPLETE_EVENT_ID |
++		BA_SESSION_RX_CONSTRAINT_EVENT_ID |
++		REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID |
++		INACTIVE_STA_EVENT_ID |
++		MAX_TX_RETRY_EVENT_ID |
++		CHANNEL_SWITCH_COMPLETE_EVENT_ID;
++
++	ret = wlcore_boot_run_firmware(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wl12xx_enable_interrupts(wl);
++
++out:
++	return ret;
++}
++
++static int wl12xx_trigger_cmd(struct wl1271 *wl, int cmd_box_addr,
++			       void *buf, size_t len)
++{
++	int ret;
++
++	ret = wlcore_write(wl, cmd_box_addr, buf, len, false);
++	if (ret < 0)
++		return ret;
++
++	ret = wlcore_write_reg(wl, REG_INTERRUPT_TRIG, WL12XX_INTR_TRIG_CMD);
++
++	return ret;
++}
++
++static int wl12xx_ack_event(struct wl1271 *wl)
++{
++	return wlcore_write_reg(wl, REG_INTERRUPT_TRIG,
++				WL12XX_INTR_TRIG_EVENT_ACK);
++}
++
++static u32 wl12xx_calc_tx_blocks(struct wl1271 *wl, u32 len, u32 spare_blks)
++{
++	u32 blk_size = WL12XX_TX_HW_BLOCK_SIZE;
++	u32 align_len = wlcore_calc_packet_alignment(wl, len);
++
++	return (align_len + blk_size - 1) / blk_size + spare_blks;
++}
++
++static void
++wl12xx_set_tx_desc_blocks(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc,
++			  u32 blks, u32 spare_blks)
++{
++	if (wl->chip.id == CHIP_ID_128X_PG20) {
++		desc->wl128x_mem.total_mem_blocks = blks;
++	} else {
++		desc->wl127x_mem.extra_blocks = spare_blks;
++		desc->wl127x_mem.total_mem_blocks = blks;
++	}
++}
++
++static void
++wl12xx_set_tx_desc_data_len(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc,
++			    struct sk_buff *skb)
++{
++	u32 aligned_len = wlcore_calc_packet_alignment(wl, skb->len);
++
++	if (wl->chip.id == CHIP_ID_128X_PG20) {
++		desc->wl128x_mem.extra_bytes = aligned_len - skb->len;
++		desc->length = cpu_to_le16(aligned_len >> 2);
++
++		wl1271_debug(DEBUG_TX,
++			     "tx_fill_hdr: hlid: %d len: %d life: %d mem: %d extra: %d",
++			     desc->hlid,
++			     le16_to_cpu(desc->length),
++			     le16_to_cpu(desc->life_time),
++			     desc->wl128x_mem.total_mem_blocks,
++			     desc->wl128x_mem.extra_bytes);
++	} else {
++		/* calculate number of padding bytes */
++		int pad = aligned_len - skb->len;
++		desc->tx_attr |=
++			cpu_to_le16(pad << TX_HW_ATTR_OFST_LAST_WORD_PAD);
++
++		/* Store the aligned length in terms of words */
++		desc->length = cpu_to_le16(aligned_len >> 2);
++
++		wl1271_debug(DEBUG_TX,
++			     "tx_fill_hdr: pad: %d hlid: %d len: %d life: %d mem: %d",
++			     pad, desc->hlid,
++			     le16_to_cpu(desc->length),
++			     le16_to_cpu(desc->life_time),
++			     desc->wl127x_mem.total_mem_blocks);
++	}
++}
++
++static enum wl_rx_buf_align
++wl12xx_get_rx_buf_align(struct wl1271 *wl, u32 rx_desc)
++{
++	if (rx_desc & RX_BUF_UNALIGNED_PAYLOAD)
++		return WLCORE_RX_BUF_UNALIGNED;
++
++	return WLCORE_RX_BUF_ALIGNED;
++}
++
++static u32 wl12xx_get_rx_packet_len(struct wl1271 *wl, void *rx_data,
++				    u32 data_len)
++{
++	struct wl1271_rx_descriptor *desc = rx_data;
++
++	/* invalid packet */
++	if (data_len < sizeof(*desc) ||
++	    data_len < sizeof(*desc) + desc->pad_len)
++		return 0;
++
++	return data_len - sizeof(*desc) - desc->pad_len;
++}
++
++static int wl12xx_tx_delayed_compl(struct wl1271 *wl)
++{
++	if (wl->fw_status_1->tx_results_counter ==
++	    (wl->tx_results_count & 0xff))
++		return 0;
++
++	return wlcore_tx_complete(wl);
++}
++
++static int wl12xx_hw_init(struct wl1271 *wl)
++{
++	int ret;
++
++	if (wl->chip.id == CHIP_ID_128X_PG20) {
++		u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE;
++
++		ret = wl128x_cmd_general_parms(wl);
++		if (ret < 0)
++			goto out;
++
++		/*
++		 * If we are in calibrator based auto detect then we got the FEM nr
++		 * in wl->fem_manuf. No need to continue further
++		 */
++		if (wl->plt_mode == PLT_FEM_DETECT)
++			goto out;
++
++		ret = wl128x_cmd_radio_parms(wl);
++		if (ret < 0)
++			goto out;
++
++		if (wl->quirks & WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN)
++			/* Enable SDIO padding */
++			host_cfg_bitmap |= HOST_IF_CFG_TX_PAD_TO_SDIO_BLK;
++
++		/* Must be before wl1271_acx_init_mem_config() */
++		ret = wl1271_acx_host_if_cfg_bitmap(wl, host_cfg_bitmap);
++		if (ret < 0)
++			goto out;
++	} else {
++		ret = wl1271_cmd_general_parms(wl);
++		if (ret < 0)
++			goto out;
++
++		/*
++		 * If we are in calibrator based auto detect then we got the FEM nr
++		 * in wl->fem_manuf. No need to continue further
++		 */
++		if (wl->plt_mode == PLT_FEM_DETECT)
++			goto out;
++
++		ret = wl1271_cmd_radio_parms(wl);
++		if (ret < 0)
++			goto out;
++		ret = wl1271_cmd_ext_radio_parms(wl);
++		if (ret < 0)
++			goto out;
++	}
++out:
++	return ret;
++}
++
++static u32 wl12xx_sta_get_ap_rate_mask(struct wl1271 *wl,
++				       struct wl12xx_vif *wlvif)
++{
++	return wlvif->rate_set;
++}
++
++static void wl12xx_conf_init(struct wl1271 *wl)
++{
++	struct wl12xx_priv *priv = wl->priv;
++
++	/* apply driver default configuration */
++	memcpy(&wl->conf, &wl12xx_conf, sizeof(wl12xx_conf));
++
++	/* apply default private configuration */
++	memcpy(&priv->conf, &wl12xx_default_priv_conf, sizeof(priv->conf));
++}
++
++static bool wl12xx_mac_in_fuse(struct wl1271 *wl)
++{
++	bool supported = false;
++	u8 major, minor;
++
++	if (wl->chip.id == CHIP_ID_128X_PG20) {
++		major = WL128X_PG_GET_MAJOR(wl->hw_pg_ver);
++		minor = WL128X_PG_GET_MINOR(wl->hw_pg_ver);
++
++		/* in wl128x we have the MAC address if the PG is >= (2, 1) */
++		if (major > 2 || (major == 2 && minor >= 1))
++			supported = true;
++	} else {
++		major = WL127X_PG_GET_MAJOR(wl->hw_pg_ver);
++		minor = WL127X_PG_GET_MINOR(wl->hw_pg_ver);
++
++		/* in wl127x we have the MAC address if the PG is >= (3, 1) */
++		if (major == 3 && minor >= 1)
++			supported = true;
++	}
++
++	wl1271_debug(DEBUG_PROBE,
++		     "PG Ver major = %d minor = %d, MAC %s present",
++		     major, minor, supported ? "is" : "is not");
++
++	return supported;
++}
++
++static int wl12xx_get_fuse_mac(struct wl1271 *wl)
++{
++	u32 mac1, mac2;
++	int ret;
++
++	ret = wlcore_set_partition(wl, &wl->ptable[PART_DRPW]);
++	if (ret < 0)
++		goto out;
++
++	ret = wlcore_read32(wl, WL12XX_REG_FUSE_BD_ADDR_1, &mac1);
++	if (ret < 0)
++		goto out;
++
++	ret = wlcore_read32(wl, WL12XX_REG_FUSE_BD_ADDR_2, &mac2);
++	if (ret < 0)
++		goto out;
++
++	/* these are the two parts of the BD_ADDR */
++	wl->fuse_oui_addr = ((mac2 & 0xffff) << 8) +
++		((mac1 & 0xff000000) >> 24);
++	wl->fuse_nic_addr = mac1 & 0xffffff;
++
++	ret = wlcore_set_partition(wl, &wl->ptable[PART_DOWN]);
++
++out:
++	return ret;
++}
++
++static int wl12xx_get_pg_ver(struct wl1271 *wl, s8 *ver)
++{
++	u16 die_info;
++	int ret;
++
++	if (wl->chip.id == CHIP_ID_128X_PG20)
++		ret = wl12xx_top_reg_read(wl, WL128X_REG_FUSE_DATA_2_1,
++					  &die_info);
++	else
++		ret = wl12xx_top_reg_read(wl, WL127X_REG_FUSE_DATA_2_1,
++					  &die_info);
++
++	if (ret >= 0 && ver)
++		*ver = (s8)((die_info & PG_VER_MASK) >> PG_VER_OFFSET);
++
++	return ret;
++}
++
++static int wl12xx_get_mac(struct wl1271 *wl)
++{
++	if (wl12xx_mac_in_fuse(wl))
++		return wl12xx_get_fuse_mac(wl);
++
++	return 0;
++}
++
++static void wl12xx_set_tx_desc_csum(struct wl1271 *wl,
++				    struct wl1271_tx_hw_descr *desc,
++				    struct sk_buff *skb)
++{
++	desc->wl12xx_reserved = 0;
++}
++
++static int wl12xx_plt_init(struct wl1271 *wl)
++{
++	int ret;
++
++	ret = wl->ops->boot(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wl->ops->hw_init(wl);
++	if (ret < 0)
++		goto out_irq_disable;
++
++	/*
++	 * If we are in calibrator based auto detect then we got the FEM nr
++	 * in wl->fem_manuf. No need to continue further
++	 */
++	if (wl->plt_mode == PLT_FEM_DETECT)
++		goto out;
++
++	ret = wl1271_acx_init_mem_config(wl);
++	if (ret < 0)
++		goto out_irq_disable;
++
++	ret = wl12xx_acx_mem_cfg(wl);
++	if (ret < 0)
++		goto out_free_memmap;
++
++	/* Enable data path */
++	ret = wl1271_cmd_data_path(wl, 1);
++	if (ret < 0)
++		goto out_free_memmap;
++
++	/* Configure for CAM power saving (ie. always active) */
++	ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
++	if (ret < 0)
++		goto out_free_memmap;
++
++	/* configure PM */
++	ret = wl1271_acx_pm_config(wl);
++	if (ret < 0)
++		goto out_free_memmap;
++
++	goto out;
++
++out_free_memmap:
++	kfree(wl->target_mem_map);
++	wl->target_mem_map = NULL;
++
++out_irq_disable:
++	mutex_unlock(&wl->mutex);
++	/* Unlocking the mutex in the middle of handling is
++	   inherently unsafe. In this case we deem it safe to do,
++	   because we need to let any possibly pending IRQ out of
++	   the system (and while we are WL1271_STATE_OFF the IRQ
++	   work function will not do anything.) Also, any other
++	   possible concurrent operations will fail due to the
++	   current state, hence the wl1271 struct should be safe. */
++	wlcore_disable_interrupts(wl);
++	mutex_lock(&wl->mutex);
++out:
++	return ret;
++}
++
++static int wl12xx_get_spare_blocks(struct wl1271 *wl, bool is_gem)
++{
++	if (is_gem)
++		return WL12XX_TX_HW_BLOCK_GEM_SPARE;
++
++	return WL12XX_TX_HW_BLOCK_SPARE_DEFAULT;
++}
++
++static int wl12xx_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
++			  struct ieee80211_vif *vif,
++			  struct ieee80211_sta *sta,
++			  struct ieee80211_key_conf *key_conf)
++{
++	return wlcore_set_key(wl, cmd, vif, sta, key_conf);
++}
++
++static int wl12xx_set_peer_cap(struct wl1271 *wl,
++			       struct ieee80211_sta_ht_cap *ht_cap,
++			       bool allow_ht_operation,
++			       u32 rate_set, u8 hlid)
++{
++	return wl1271_acx_set_ht_capabilities(wl, ht_cap, allow_ht_operation,
++					      hlid);
++}
++
++static bool wl12xx_lnk_high_prio(struct wl1271 *wl, u8 hlid,
++				 struct wl1271_link *lnk)
++{
++	u8 thold;
++
++	if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map))
++		thold = wl->conf.tx.fast_link_thold;
++	else
++		thold = wl->conf.tx.slow_link_thold;
++
++	return lnk->allocated_pkts < thold;
++}
++
++static bool wl12xx_lnk_low_prio(struct wl1271 *wl, u8 hlid,
++				struct wl1271_link *lnk)
++{
++	/* any link is good for low priority */
++	return true;
++}
++
++static int wl12xx_setup(struct wl1271 *wl);
++
++static struct wlcore_ops wl12xx_ops = {
++	.setup			= wl12xx_setup,
++	.identify_chip		= wl12xx_identify_chip,
++	.boot			= wl12xx_boot,
++	.plt_init		= wl12xx_plt_init,
++	.trigger_cmd		= wl12xx_trigger_cmd,
++	.ack_event		= wl12xx_ack_event,
++	.wait_for_event		= wl12xx_wait_for_event,
++	.process_mailbox_events	= wl12xx_process_mailbox_events,
++	.calc_tx_blocks		= wl12xx_calc_tx_blocks,
++	.set_tx_desc_blocks	= wl12xx_set_tx_desc_blocks,
++	.set_tx_desc_data_len	= wl12xx_set_tx_desc_data_len,
++	.get_rx_buf_align	= wl12xx_get_rx_buf_align,
++	.get_rx_packet_len	= wl12xx_get_rx_packet_len,
++	.tx_immediate_compl	= NULL,
++	.tx_delayed_compl	= wl12xx_tx_delayed_compl,
++	.hw_init		= wl12xx_hw_init,
++	.init_vif		= NULL,
++	.sta_get_ap_rate_mask	= wl12xx_sta_get_ap_rate_mask,
++	.get_pg_ver		= wl12xx_get_pg_ver,
++	.get_mac		= wl12xx_get_mac,
++	.set_tx_desc_csum	= wl12xx_set_tx_desc_csum,
++	.set_rx_csum		= NULL,
++	.ap_get_mimo_wide_rate_mask = NULL,
++	.debugfs_init		= wl12xx_debugfs_add_files,
++	.scan_start		= wl12xx_scan_start,
++	.scan_stop		= wl12xx_scan_stop,
++	.sched_scan_start	= wl12xx_sched_scan_start,
++	.sched_scan_stop	= wl12xx_scan_sched_scan_stop,
++	.get_spare_blocks	= wl12xx_get_spare_blocks,
++	.set_key		= wl12xx_set_key,
++	.channel_switch		= wl12xx_cmd_channel_switch,
++	.pre_pkt_send		= NULL,
++	.set_peer_cap		= wl12xx_set_peer_cap,
++	.lnk_high_prio		= wl12xx_lnk_high_prio,
++	.lnk_low_prio		= wl12xx_lnk_low_prio,
++};
++
++static struct ieee80211_sta_ht_cap wl12xx_ht_cap = {
++	.cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 |
++	       (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT),
++	.ht_supported = true,
++	.ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K,
++	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_8,
++	.mcs = {
++		.rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
++		.rx_highest = cpu_to_le16(72),
++		.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
++		},
++};
++
++static int wl12xx_setup(struct wl1271 *wl)
++{
++	struct wl12xx_priv *priv = wl->priv;
++	struct wlcore_platdev_data *pdev_data = wl->pdev->dev.platform_data;
++	struct wl12xx_platform_data *pdata = pdev_data->pdata;
++
++	wl->rtable = wl12xx_rtable;
++	wl->num_tx_desc = WL12XX_NUM_TX_DESCRIPTORS;
++	wl->num_rx_desc = WL12XX_NUM_RX_DESCRIPTORS;
++	wl->num_channels = 1;
++	wl->num_mac_addr = WL12XX_NUM_MAC_ADDRESSES;
++	wl->band_rate_to_idx = wl12xx_band_rate_to_idx;
++	wl->hw_tx_rate_tbl_size = WL12XX_CONF_HW_RXTX_RATE_MAX;
++	wl->hw_min_ht_rate = WL12XX_CONF_HW_RXTX_RATE_MCS0;
++	wl->fw_status_priv_len = 0;
++	wl->stats.fw_stats_len = sizeof(struct wl12xx_acx_statistics);
++	wlcore_set_ht_cap(wl, IEEE80211_BAND_2GHZ, &wl12xx_ht_cap);
++	wlcore_set_ht_cap(wl, IEEE80211_BAND_5GHZ, &wl12xx_ht_cap);
++	wl12xx_conf_init(wl);
++
++	if (!fref_param) {
++		priv->ref_clock = pdata->board_ref_clock;
++	} else {
++		if (!strcmp(fref_param, "19.2"))
++			priv->ref_clock = WL12XX_REFCLOCK_19;
++		else if (!strcmp(fref_param, "26"))
++			priv->ref_clock = WL12XX_REFCLOCK_26;
++		else if (!strcmp(fref_param, "26x"))
++			priv->ref_clock = WL12XX_REFCLOCK_26_XTAL;
++		else if (!strcmp(fref_param, "38.4"))
++			priv->ref_clock = WL12XX_REFCLOCK_38;
++		else if (!strcmp(fref_param, "38.4x"))
++			priv->ref_clock = WL12XX_REFCLOCK_38_XTAL;
++		else if (!strcmp(fref_param, "52"))
++			priv->ref_clock = WL12XX_REFCLOCK_52;
++		else
++			wl1271_error("Invalid fref parameter %s", fref_param);
++	}
++
++	if (!tcxo_param) {
++		priv->tcxo_clock = pdata->board_tcxo_clock;
++	} else {
++		if (!strcmp(tcxo_param, "19.2"))
++			priv->tcxo_clock = WL12XX_TCXOCLOCK_19_2;
++		else if (!strcmp(tcxo_param, "26"))
++			priv->tcxo_clock = WL12XX_TCXOCLOCK_26;
++		else if (!strcmp(tcxo_param, "38.4"))
++			priv->tcxo_clock = WL12XX_TCXOCLOCK_38_4;
++		else if (!strcmp(tcxo_param, "52"))
++			priv->tcxo_clock = WL12XX_TCXOCLOCK_52;
++		else if (!strcmp(tcxo_param, "16.368"))
++			priv->tcxo_clock = WL12XX_TCXOCLOCK_16_368;
++		else if (!strcmp(tcxo_param, "32.736"))
++			priv->tcxo_clock = WL12XX_TCXOCLOCK_32_736;
++		else if (!strcmp(tcxo_param, "16.8"))
++			priv->tcxo_clock = WL12XX_TCXOCLOCK_16_8;
++		else if (!strcmp(tcxo_param, "33.6"))
++			priv->tcxo_clock = WL12XX_TCXOCLOCK_33_6;
++		else
++			wl1271_error("Invalid tcxo parameter %s", tcxo_param);
++	}
++
++	priv->rx_mem_addr = kmalloc(sizeof(*priv->rx_mem_addr), GFP_KERNEL);
++	if (!priv->rx_mem_addr)
++		return -ENOMEM;
++
++	return 0;
++}
++
++static int wl12xx_probe(struct platform_device *pdev)
++{
++	struct wl1271 *wl;
++	struct ieee80211_hw *hw;
++	int ret;
++
++	hw = wlcore_alloc_hw(sizeof(struct wl12xx_priv),
++			     WL12XX_AGGR_BUFFER_SIZE,
++			     sizeof(struct wl12xx_event_mailbox));
++	if (IS_ERR(hw)) {
++		wl1271_error("can't allocate hw");
++		ret = PTR_ERR(hw);
++		goto out;
++	}
++
++	wl = hw->priv;
++	wl->ops = &wl12xx_ops;
++	wl->ptable = wl12xx_ptable;
++	ret = wlcore_probe(wl, pdev);
++	if (ret)
++		goto out_free;
++
++	return ret;
++
++out_free:
++	wlcore_free_hw(wl);
++out:
++	return ret;
++}
++
++static int wl12xx_remove(struct platform_device *pdev)
++{
++	struct wl1271 *wl = platform_get_drvdata(pdev);
++	struct wl12xx_priv *priv;
++
++	if (!wl)
++		goto out;
++	priv = wl->priv;
++
++	kfree(priv->rx_mem_addr);
++
++out:
++	return wlcore_remove(pdev);
++}
++
++static const struct platform_device_id wl12xx_id_table[] = {
++	{ "wl12xx", 0 },
++	{  } /* Terminating Entry */
++};
++MODULE_DEVICE_TABLE(platform, wl12xx_id_table);
++
++static struct platform_driver wl12xx_driver = {
++	.probe		= wl12xx_probe,
++	.remove		= wl12xx_remove,
++	.id_table	= wl12xx_id_table,
++	.driver = {
++		.name	= "wl12xx_driver",
++		.owner	= THIS_MODULE,
++	}
++};
++
++module_platform_driver(wl12xx_driver);
++
++module_param_named(fref, fref_param, charp, 0);
++MODULE_PARM_DESC(fref, "FREF clock: 19.2, 26, 26x, 38.4, 38.4x, 52");
++
++module_param_named(tcxo, tcxo_param, charp, 0);
++MODULE_PARM_DESC(tcxo,
++		 "TCXO clock: 19.2, 26, 38.4, 52, 16.368, 32.736, 16.8, 33.6");
++
++MODULE_LICENSE("GPL v2");
++MODULE_AUTHOR("Luciano Coelho <coelho at ti.com>");
++MODULE_FIRMWARE(WL127X_FW_NAME_SINGLE);
++MODULE_FIRMWARE(WL127X_FW_NAME_MULTI);
++MODULE_FIRMWARE(WL127X_PLT_FW_NAME);
++MODULE_FIRMWARE(WL128X_FW_NAME_SINGLE);
++MODULE_FIRMWARE(WL128X_FW_NAME_MULTI);
++MODULE_FIRMWARE(WL128X_PLT_FW_NAME);
+diff -Nuar git.old/drivers/net/wireless/ti/wl12xx/wl12xx.h git/drivers/net/wireless/ti/wl12xx/wl12xx.h
+--- git.old/drivers/net/wireless/ti/wl12xx/wl12xx.h	2015-03-04 16:35:05.646185998 +0100
++++ git/drivers/net/wireless/ti/wl12xx/wl12xx.h	2015-03-04 16:52:01.554176309 +0100
+@@ -79,4 +79,32 @@
+ 	struct wl127x_rx_mem_pool_addr *rx_mem_addr;
+ };
+ 
++/* Reference clock values */
++enum {
++	WL12XX_REFCLOCK_19	= 0, /* 19.2 MHz */
++	WL12XX_REFCLOCK_26	= 1, /* 26 MHz */
++	WL12XX_REFCLOCK_38	= 2, /* 38.4 MHz */
++	WL12XX_REFCLOCK_52	= 3, /* 52 MHz */
++	WL12XX_REFCLOCK_38_XTAL = 4, /* 38.4 MHz, XTAL */
++	WL12XX_REFCLOCK_26_XTAL = 5, /* 26 MHz, XTAL */
++};
++
++/* TCXO clock values */
++enum {
++	WL12XX_TCXOCLOCK_19_2	= 0, /* 19.2MHz */
++	WL12XX_TCXOCLOCK_26	= 1, /* 26 MHz */
++	WL12XX_TCXOCLOCK_38_4	= 2, /* 38.4MHz */
++	WL12XX_TCXOCLOCK_52	= 3, /* 52 MHz */
++	WL12XX_TCXOCLOCK_16_368	= 4, /* 16.368 MHz */
++	WL12XX_TCXOCLOCK_32_736	= 5, /* 32.736 MHz */
++	WL12XX_TCXOCLOCK_16_8	= 6, /* 16.8 MHz */
++	WL12XX_TCXOCLOCK_33_6	= 7, /* 33.6 MHz */
++};
++
++struct wl12xx_clock {
++	u32	freq;
++	bool	xtal;
++	u8	hw_idx;
++};
++
+ #endif /* __WL12XX_PRIV_H__ */
+diff -Nuar git.old/drivers/net/wireless/ti/wlcore/debugfs.c git/drivers/net/wireless/ti/wlcore/debugfs.c
+--- git.old/drivers/net/wireless/ti/wlcore/debugfs.c	2015-03-04 16:35:05.702185997 +0100
++++ git/drivers/net/wireless/ti/wlcore/debugfs.c	2015-03-04 16:49:20.826177842 +0100
+@@ -486,7 +486,7 @@
+ 	DRIVER_STATE_PRINT_HEX(irq);
+ 	/* TODO: ref_clock and tcxo_clock were moved to wl12xx priv */
+ 	DRIVER_STATE_PRINT_HEX(hw_pg_ver);
+-	DRIVER_STATE_PRINT_HEX(platform_quirks);
++	DRIVER_STATE_PRINT_HEX(irq_flags);
+ 	DRIVER_STATE_PRINT_HEX(chip.id);
+ 	DRIVER_STATE_PRINT_STR(chip.fw_ver_str);
+ 	DRIVER_STATE_PRINT_STR(chip.phy_fw_ver_str);
+diff -Nuar git.old/drivers/net/wireless/ti/wlcore/debugfs.c.orig git/drivers/net/wireless/ti/wlcore/debugfs.c.orig
+--- git.old/drivers/net/wireless/ti/wlcore/debugfs.c.orig	1970-01-01 01:00:00.000000000 +0100
++++ git/drivers/net/wireless/ti/wlcore/debugfs.c.orig	2015-03-04 16:35:05.702185997 +0100
+@@ -0,0 +1,1330 @@
++/*
++ * This file is part of wl1271
++ *
++ * Copyright (C) 2009 Nokia Corporation
++ *
++ * Contact: Luciano Coelho <luciano.coelho at nokia.com>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++ * 02110-1301 USA
++ *
++ */
++
++#include "debugfs.h"
++
++#include <linux/skbuff.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++
++#include "wlcore.h"
++#include "debug.h"
++#include "acx.h"
++#include "ps.h"
++#include "io.h"
++#include "tx.h"
++#include "hw_ops.h"
++
++/* ms */
++#define WL1271_DEBUGFS_STATS_LIFETIME 1000
++
++#define WLCORE_MAX_BLOCK_SIZE ((size_t)(4*PAGE_SIZE))
++
++/* debugfs macros idea from mac80211 */
++int wl1271_format_buffer(char __user *userbuf, size_t count,
++			 loff_t *ppos, char *fmt, ...)
++{
++	va_list args;
++	char buf[DEBUGFS_FORMAT_BUFFER_SIZE];
++	int res;
++
++	va_start(args, fmt);
++	res = vscnprintf(buf, sizeof(buf), fmt, args);
++	va_end(args);
++
++	return simple_read_from_buffer(userbuf, count, ppos, buf, res);
++}
++EXPORT_SYMBOL_GPL(wl1271_format_buffer);
++
++void wl1271_debugfs_update_stats(struct wl1271 *wl)
++{
++	int ret;
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	if (!wl->plt &&
++	    time_after(jiffies, wl->stats.fw_stats_update +
++		       msecs_to_jiffies(WL1271_DEBUGFS_STATS_LIFETIME))) {
++		wl1271_acx_statistics(wl, wl->stats.fw_stats);
++		wl->stats.fw_stats_update = jiffies;
++	}
++
++	wl1271_ps_elp_sleep(wl);
++
++out:
++	mutex_unlock(&wl->mutex);
++}
++EXPORT_SYMBOL_GPL(wl1271_debugfs_update_stats);
++
++DEBUGFS_READONLY_FILE(retry_count, "%u", wl->stats.retry_count);
++DEBUGFS_READONLY_FILE(excessive_retries, "%u",
++		      wl->stats.excessive_retries);
++
++static ssize_t tx_queue_len_read(struct file *file, char __user *userbuf,
++				 size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	u32 queue_len;
++	char buf[20];
++	int res;
++
++	queue_len = wl1271_tx_total_queue_count(wl);
++
++	res = scnprintf(buf, sizeof(buf), "%u\n", queue_len);
++	return simple_read_from_buffer(userbuf, count, ppos, buf, res);
++}
++
++static const struct file_operations tx_queue_len_ops = {
++	.read = tx_queue_len_read,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static void chip_op_handler(struct wl1271 *wl, unsigned long value,
++			    void *arg)
++{
++	int ret;
++	int (*chip_op) (struct wl1271 *wl);
++
++	if (!arg) {
++		wl1271_warning("debugfs chip_op_handler with no callback");
++		return;
++	}
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		return;
++
++	chip_op = arg;
++	chip_op(wl);
++
++	wl1271_ps_elp_sleep(wl);
++}
++
++
++static inline void no_write_handler(struct wl1271 *wl,
++				    unsigned long value,
++				    unsigned long param)
++{
++}
++
++#define WL12XX_CONF_DEBUGFS(param, conf_sub_struct,			\
++			    min_val, max_val, write_handler_locked,	\
++			    write_handler_arg)				\
++	static ssize_t param##_read(struct file *file,			\
++				      char __user *user_buf,		\
++				      size_t count, loff_t *ppos)	\
++	{								\
++	struct wl1271 *wl = file->private_data;				\
++	return wl1271_format_buffer(user_buf, count,			\
++				    ppos, "%d\n",			\
++				    wl->conf.conf_sub_struct.param);	\
++	}								\
++									\
++	static ssize_t param##_write(struct file *file,			\
++				     const char __user *user_buf,	\
++				     size_t count, loff_t *ppos)	\
++	{								\
++	struct wl1271 *wl = file->private_data;				\
++	unsigned long value;						\
++	int ret;							\
++									\
++	ret = kstrtoul_from_user(user_buf, count, 10, &value);		\
++	if (ret < 0) {							\
++		wl1271_warning("illegal value for " #param);		\
++		return -EINVAL;						\
++	}								\
++									\
++	if (value < min_val || value > max_val) {			\
++		wl1271_warning(#param " is not in valid range");	\
++		return -ERANGE;						\
++	}								\
++									\
++	mutex_lock(&wl->mutex);						\
++	wl->conf.conf_sub_struct.param = value;				\
++									\
++	write_handler_locked(wl, value, write_handler_arg);		\
++									\
++	mutex_unlock(&wl->mutex);					\
++	return count;							\
++	}								\
++									\
++	static const struct file_operations param##_ops = {		\
++		.read = param##_read,					\
++		.write = param##_write,					\
++		.open = simple_open,					\
++		.llseek = default_llseek,				\
++	};
++
++WL12XX_CONF_DEBUGFS(irq_pkt_threshold, rx, 0, 65535,
++		    chip_op_handler, wl1271_acx_init_rx_interrupt)
++WL12XX_CONF_DEBUGFS(irq_blk_threshold, rx, 0, 65535,
++		    chip_op_handler, wl1271_acx_init_rx_interrupt)
++WL12XX_CONF_DEBUGFS(irq_timeout, rx, 0, 100,
++		    chip_op_handler, wl1271_acx_init_rx_interrupt)
++
++static ssize_t gpio_power_read(struct file *file, char __user *user_buf,
++			  size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	bool state = test_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
++
++	int res;
++	char buf[10];
++
++	res = scnprintf(buf, sizeof(buf), "%d\n", state);
++
++	return simple_read_from_buffer(user_buf, count, ppos, buf, res);
++}
++
++static ssize_t gpio_power_write(struct file *file,
++			   const char __user *user_buf,
++			   size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	unsigned long value;
++	int ret;
++
++	ret = kstrtoul_from_user(user_buf, count, 10, &value);
++	if (ret < 0) {
++		wl1271_warning("illegal value in gpio_power");
++		return -EINVAL;
++	}
++
++	mutex_lock(&wl->mutex);
++
++	if (value)
++		wl1271_power_on(wl);
++	else
++		wl1271_power_off(wl);
++
++	mutex_unlock(&wl->mutex);
++	return count;
++}
++
++static const struct file_operations gpio_power_ops = {
++	.read = gpio_power_read,
++	.write = gpio_power_write,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static ssize_t start_recovery_write(struct file *file,
++				    const char __user *user_buf,
++				    size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++
++	mutex_lock(&wl->mutex);
++	wl12xx_queue_recovery_work(wl);
++	mutex_unlock(&wl->mutex);
++
++	return count;
++}
++
++static const struct file_operations start_recovery_ops = {
++	.write = start_recovery_write,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static ssize_t dynamic_ps_timeout_read(struct file *file, char __user *user_buf,
++			  size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++
++	return wl1271_format_buffer(user_buf, count,
++				    ppos, "%d\n",
++				    wl->conf.conn.dynamic_ps_timeout);
++}
++
++static ssize_t dynamic_ps_timeout_write(struct file *file,
++				    const char __user *user_buf,
++				    size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	struct wl12xx_vif *wlvif;
++	unsigned long value;
++	int ret;
++
++	ret = kstrtoul_from_user(user_buf, count, 10, &value);
++	if (ret < 0) {
++		wl1271_warning("illegal value in dynamic_ps");
++		return -EINVAL;
++	}
++
++	if (value < 1 || value > 65535) {
++		wl1271_warning("dyanmic_ps_timeout is not in valid range");
++		return -ERANGE;
++	}
++
++	mutex_lock(&wl->mutex);
++
++	wl->conf.conn.dynamic_ps_timeout = value;
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	/* In case we're already in PSM, trigger it again to set new timeout
++	 * immediately without waiting for re-association
++	 */
++
++	wl12xx_for_each_wlvif_sta(wl, wlvif) {
++		if (test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags))
++			wl1271_ps_set_mode(wl, wlvif, STATION_AUTO_PS_MODE);
++	}
++
++	wl1271_ps_elp_sleep(wl);
++
++out:
++	mutex_unlock(&wl->mutex);
++	return count;
++}
++
++static const struct file_operations dynamic_ps_timeout_ops = {
++	.read = dynamic_ps_timeout_read,
++	.write = dynamic_ps_timeout_write,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static ssize_t forced_ps_read(struct file *file, char __user *user_buf,
++			  size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++
++	return wl1271_format_buffer(user_buf, count,
++				    ppos, "%d\n",
++				    wl->conf.conn.forced_ps);
++}
++
++static ssize_t forced_ps_write(struct file *file,
++				    const char __user *user_buf,
++				    size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	struct wl12xx_vif *wlvif;
++	unsigned long value;
++	int ret, ps_mode;
++
++	ret = kstrtoul_from_user(user_buf, count, 10, &value);
++	if (ret < 0) {
++		wl1271_warning("illegal value in forced_ps");
++		return -EINVAL;
++	}
++
++	if (value != 1 && value != 0) {
++		wl1271_warning("forced_ps should be either 0 or 1");
++		return -ERANGE;
++	}
++
++	mutex_lock(&wl->mutex);
++
++	if (wl->conf.conn.forced_ps == value)
++		goto out;
++
++	wl->conf.conn.forced_ps = value;
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	/* In case we're already in PSM, trigger it again to switch mode
++	 * immediately without waiting for re-association
++	 */
++
++	ps_mode = value ? STATION_POWER_SAVE_MODE : STATION_AUTO_PS_MODE;
++
++	wl12xx_for_each_wlvif_sta(wl, wlvif) {
++		if (test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags))
++			wl1271_ps_set_mode(wl, wlvif, ps_mode);
++	}
++
++	wl1271_ps_elp_sleep(wl);
++
++out:
++	mutex_unlock(&wl->mutex);
++	return count;
++}
++
++static const struct file_operations forced_ps_ops = {
++	.read = forced_ps_read,
++	.write = forced_ps_write,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static ssize_t split_scan_timeout_read(struct file *file, char __user *user_buf,
++			  size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++
++	return wl1271_format_buffer(user_buf, count,
++				    ppos, "%d\n",
++				    wl->conf.scan.split_scan_timeout / 1000);
++}
++
++static ssize_t split_scan_timeout_write(struct file *file,
++				    const char __user *user_buf,
++				    size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	unsigned long value;
++	int ret;
++
++	ret = kstrtoul_from_user(user_buf, count, 10, &value);
++	if (ret < 0) {
++		wl1271_warning("illegal value in split_scan_timeout");
++		return -EINVAL;
++	}
++
++	if (value == 0)
++		wl1271_info("split scan will be disabled");
++
++	mutex_lock(&wl->mutex);
++
++	wl->conf.scan.split_scan_timeout = value * 1000;
++
++	mutex_unlock(&wl->mutex);
++	return count;
++}
++
++static const struct file_operations split_scan_timeout_ops = {
++	.read = split_scan_timeout_read,
++	.write = split_scan_timeout_write,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static ssize_t driver_state_read(struct file *file, char __user *user_buf,
++				 size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	int res = 0;
++	ssize_t ret;
++	char *buf;
++
++#define DRIVER_STATE_BUF_LEN 1024
++
++	buf = kmalloc(DRIVER_STATE_BUF_LEN, GFP_KERNEL);
++	if (!buf)
++		return -ENOMEM;
++
++	mutex_lock(&wl->mutex);
++
++#define DRIVER_STATE_PRINT(x, fmt)   \
++	(res += scnprintf(buf + res, DRIVER_STATE_BUF_LEN - res,\
++			  #x " = " fmt "\n", wl->x))
++
++#define DRIVER_STATE_PRINT_LONG(x) DRIVER_STATE_PRINT(x, "%ld")
++#define DRIVER_STATE_PRINT_INT(x)  DRIVER_STATE_PRINT(x, "%d")
++#define DRIVER_STATE_PRINT_STR(x)  DRIVER_STATE_PRINT(x, "%s")
++#define DRIVER_STATE_PRINT_LHEX(x) DRIVER_STATE_PRINT(x, "0x%lx")
++#define DRIVER_STATE_PRINT_HEX(x)  DRIVER_STATE_PRINT(x, "0x%x")
++
++	DRIVER_STATE_PRINT_INT(tx_blocks_available);
++	DRIVER_STATE_PRINT_INT(tx_allocated_blocks);
++	DRIVER_STATE_PRINT_INT(tx_allocated_pkts[0]);
++	DRIVER_STATE_PRINT_INT(tx_allocated_pkts[1]);
++	DRIVER_STATE_PRINT_INT(tx_allocated_pkts[2]);
++	DRIVER_STATE_PRINT_INT(tx_allocated_pkts[3]);
++	DRIVER_STATE_PRINT_INT(tx_frames_cnt);
++	DRIVER_STATE_PRINT_LHEX(tx_frames_map[0]);
++	DRIVER_STATE_PRINT_INT(tx_queue_count[0]);
++	DRIVER_STATE_PRINT_INT(tx_queue_count[1]);
++	DRIVER_STATE_PRINT_INT(tx_queue_count[2]);
++	DRIVER_STATE_PRINT_INT(tx_queue_count[3]);
++	DRIVER_STATE_PRINT_INT(tx_packets_count);
++	DRIVER_STATE_PRINT_INT(tx_results_count);
++	DRIVER_STATE_PRINT_LHEX(flags);
++	DRIVER_STATE_PRINT_INT(tx_blocks_freed);
++	DRIVER_STATE_PRINT_INT(rx_counter);
++	DRIVER_STATE_PRINT_INT(state);
++	DRIVER_STATE_PRINT_INT(channel);
++	DRIVER_STATE_PRINT_INT(band);
++	DRIVER_STATE_PRINT_INT(power_level);
++	DRIVER_STATE_PRINT_INT(sg_enabled);
++	DRIVER_STATE_PRINT_INT(enable_11a);
++	DRIVER_STATE_PRINT_INT(noise);
++	DRIVER_STATE_PRINT_HEX(ap_fw_ps_map);
++	DRIVER_STATE_PRINT_LHEX(ap_ps_map);
++	DRIVER_STATE_PRINT_HEX(quirks);
++	DRIVER_STATE_PRINT_HEX(irq);
++	/* TODO: ref_clock and tcxo_clock were moved to wl12xx priv */
++	DRIVER_STATE_PRINT_HEX(hw_pg_ver);
++	DRIVER_STATE_PRINT_HEX(platform_quirks);
++	DRIVER_STATE_PRINT_HEX(chip.id);
++	DRIVER_STATE_PRINT_STR(chip.fw_ver_str);
++	DRIVER_STATE_PRINT_STR(chip.phy_fw_ver_str);
++	DRIVER_STATE_PRINT_INT(recovery_count);
++
++#undef DRIVER_STATE_PRINT_INT
++#undef DRIVER_STATE_PRINT_LONG
++#undef DRIVER_STATE_PRINT_HEX
++#undef DRIVER_STATE_PRINT_LHEX
++#undef DRIVER_STATE_PRINT_STR
++#undef DRIVER_STATE_PRINT
++#undef DRIVER_STATE_BUF_LEN
++
++	mutex_unlock(&wl->mutex);
++
++	ret = simple_read_from_buffer(user_buf, count, ppos, buf, res);
++	kfree(buf);
++	return ret;
++}
++
++static const struct file_operations driver_state_ops = {
++	.read = driver_state_read,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static ssize_t vifs_state_read(struct file *file, char __user *user_buf,
++				 size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	struct wl12xx_vif *wlvif;
++	int ret, res = 0;
++	const int buf_size = 4096;
++	char *buf;
++	char tmp_buf[64];
++
++	buf = kzalloc(buf_size, GFP_KERNEL);
++	if (!buf)
++		return -ENOMEM;
++
++	mutex_lock(&wl->mutex);
++
++#define VIF_STATE_PRINT(x, fmt)				\
++	(res += scnprintf(buf + res, buf_size - res,	\
++			  #x " = " fmt "\n", wlvif->x))
++
++#define VIF_STATE_PRINT_LONG(x)  VIF_STATE_PRINT(x, "%ld")
++#define VIF_STATE_PRINT_INT(x)   VIF_STATE_PRINT(x, "%d")
++#define VIF_STATE_PRINT_STR(x)   VIF_STATE_PRINT(x, "%s")
++#define VIF_STATE_PRINT_LHEX(x)  VIF_STATE_PRINT(x, "0x%lx")
++#define VIF_STATE_PRINT_LLHEX(x) VIF_STATE_PRINT(x, "0x%llx")
++#define VIF_STATE_PRINT_HEX(x)   VIF_STATE_PRINT(x, "0x%x")
++
++#define VIF_STATE_PRINT_NSTR(x, len)				\
++	do {							\
++		memset(tmp_buf, 0, sizeof(tmp_buf));		\
++		memcpy(tmp_buf, wlvif->x,			\
++		       min_t(u8, len, sizeof(tmp_buf) - 1));	\
++		res += scnprintf(buf + res, buf_size - res,	\
++				 #x " = %s\n", tmp_buf);	\
++	} while (0)
++
++	wl12xx_for_each_wlvif(wl, wlvif) {
++		VIF_STATE_PRINT_INT(role_id);
++		VIF_STATE_PRINT_INT(bss_type);
++		VIF_STATE_PRINT_LHEX(flags);
++		VIF_STATE_PRINT_INT(p2p);
++		VIF_STATE_PRINT_INT(dev_role_id);
++		VIF_STATE_PRINT_INT(dev_hlid);
++
++		if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
++		    wlvif->bss_type == BSS_TYPE_IBSS) {
++			VIF_STATE_PRINT_INT(sta.hlid);
++			VIF_STATE_PRINT_INT(sta.basic_rate_idx);
++			VIF_STATE_PRINT_INT(sta.ap_rate_idx);
++			VIF_STATE_PRINT_INT(sta.p2p_rate_idx);
++			VIF_STATE_PRINT_INT(sta.qos);
++		} else {
++			VIF_STATE_PRINT_INT(ap.global_hlid);
++			VIF_STATE_PRINT_INT(ap.bcast_hlid);
++			VIF_STATE_PRINT_LHEX(ap.sta_hlid_map[0]);
++			VIF_STATE_PRINT_INT(ap.mgmt_rate_idx);
++			VIF_STATE_PRINT_INT(ap.bcast_rate_idx);
++			VIF_STATE_PRINT_INT(ap.ucast_rate_idx[0]);
++			VIF_STATE_PRINT_INT(ap.ucast_rate_idx[1]);
++			VIF_STATE_PRINT_INT(ap.ucast_rate_idx[2]);
++			VIF_STATE_PRINT_INT(ap.ucast_rate_idx[3]);
++		}
++		VIF_STATE_PRINT_INT(last_tx_hlid);
++		VIF_STATE_PRINT_INT(tx_queue_count[0]);
++		VIF_STATE_PRINT_INT(tx_queue_count[1]);
++		VIF_STATE_PRINT_INT(tx_queue_count[2]);
++		VIF_STATE_PRINT_INT(tx_queue_count[3]);
++		VIF_STATE_PRINT_LHEX(links_map[0]);
++		VIF_STATE_PRINT_NSTR(ssid, wlvif->ssid_len);
++		VIF_STATE_PRINT_INT(band);
++		VIF_STATE_PRINT_INT(channel);
++		VIF_STATE_PRINT_HEX(bitrate_masks[0]);
++		VIF_STATE_PRINT_HEX(bitrate_masks[1]);
++		VIF_STATE_PRINT_HEX(basic_rate_set);
++		VIF_STATE_PRINT_HEX(basic_rate);
++		VIF_STATE_PRINT_HEX(rate_set);
++		VIF_STATE_PRINT_INT(beacon_int);
++		VIF_STATE_PRINT_INT(default_key);
++		VIF_STATE_PRINT_INT(aid);
++		VIF_STATE_PRINT_INT(psm_entry_retry);
++		VIF_STATE_PRINT_INT(power_level);
++		VIF_STATE_PRINT_INT(rssi_thold);
++		VIF_STATE_PRINT_INT(last_rssi_event);
++		VIF_STATE_PRINT_INT(ba_support);
++		VIF_STATE_PRINT_INT(ba_allowed);
++		VIF_STATE_PRINT_LLHEX(total_freed_pkts);
++	}
++
++#undef VIF_STATE_PRINT_INT
++#undef VIF_STATE_PRINT_LONG
++#undef VIF_STATE_PRINT_HEX
++#undef VIF_STATE_PRINT_LHEX
++#undef VIF_STATE_PRINT_LLHEX
++#undef VIF_STATE_PRINT_STR
++#undef VIF_STATE_PRINT_NSTR
++#undef VIF_STATE_PRINT
++
++	mutex_unlock(&wl->mutex);
++
++	ret = simple_read_from_buffer(user_buf, count, ppos, buf, res);
++	kfree(buf);
++	return ret;
++}
++
++static const struct file_operations vifs_state_ops = {
++	.read = vifs_state_read,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static ssize_t dtim_interval_read(struct file *file, char __user *user_buf,
++				  size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	u8 value;
++
++	if (wl->conf.conn.wake_up_event == CONF_WAKE_UP_EVENT_DTIM ||
++	    wl->conf.conn.wake_up_event == CONF_WAKE_UP_EVENT_N_DTIM)
++		value = wl->conf.conn.listen_interval;
++	else
++		value = 0;
++
++	return wl1271_format_buffer(user_buf, count, ppos, "%d\n", value);
++}
++
++static ssize_t dtim_interval_write(struct file *file,
++				   const char __user *user_buf,
++				   size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	unsigned long value;
++	int ret;
++
++	ret = kstrtoul_from_user(user_buf, count, 10, &value);
++	if (ret < 0) {
++		wl1271_warning("illegal value for dtim_interval");
++		return -EINVAL;
++	}
++
++	if (value < 1 || value > 10) {
++		wl1271_warning("dtim value is not in valid range");
++		return -ERANGE;
++	}
++
++	mutex_lock(&wl->mutex);
++
++	wl->conf.conn.listen_interval = value;
++	/* for some reason there are different event types for 1 and >1 */
++	if (value == 1)
++		wl->conf.conn.wake_up_event = CONF_WAKE_UP_EVENT_DTIM;
++	else
++		wl->conf.conn.wake_up_event = CONF_WAKE_UP_EVENT_N_DTIM;
++
++	/*
++	 * we don't reconfigure ACX_WAKE_UP_CONDITIONS now, so it will only
++	 * take effect on the next time we enter psm.
++	 */
++	mutex_unlock(&wl->mutex);
++	return count;
++}
++
++static const struct file_operations dtim_interval_ops = {
++	.read = dtim_interval_read,
++	.write = dtim_interval_write,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++
++
++static ssize_t suspend_dtim_interval_read(struct file *file,
++					  char __user *user_buf,
++					  size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	u8 value;
++
++	if (wl->conf.conn.suspend_wake_up_event == CONF_WAKE_UP_EVENT_DTIM ||
++	    wl->conf.conn.suspend_wake_up_event == CONF_WAKE_UP_EVENT_N_DTIM)
++		value = wl->conf.conn.suspend_listen_interval;
++	else
++		value = 0;
++
++	return wl1271_format_buffer(user_buf, count, ppos, "%d\n", value);
++}
++
++static ssize_t suspend_dtim_interval_write(struct file *file,
++					   const char __user *user_buf,
++					   size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	unsigned long value;
++	int ret;
++
++	ret = kstrtoul_from_user(user_buf, count, 10, &value);
++	if (ret < 0) {
++		wl1271_warning("illegal value for suspend_dtim_interval");
++		return -EINVAL;
++	}
++
++	if (value < 1 || value > 10) {
++		wl1271_warning("suspend_dtim value is not in valid range");
++		return -ERANGE;
++	}
++
++	mutex_lock(&wl->mutex);
++
++	wl->conf.conn.suspend_listen_interval = value;
++	/* for some reason there are different event types for 1 and >1 */
++	if (value == 1)
++		wl->conf.conn.suspend_wake_up_event = CONF_WAKE_UP_EVENT_DTIM;
++	else
++		wl->conf.conn.suspend_wake_up_event = CONF_WAKE_UP_EVENT_N_DTIM;
++
++	mutex_unlock(&wl->mutex);
++	return count;
++}
++
++
++static const struct file_operations suspend_dtim_interval_ops = {
++	.read = suspend_dtim_interval_read,
++	.write = suspend_dtim_interval_write,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static ssize_t beacon_interval_read(struct file *file, char __user *user_buf,
++				    size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	u8 value;
++
++	if (wl->conf.conn.wake_up_event == CONF_WAKE_UP_EVENT_BEACON ||
++	    wl->conf.conn.wake_up_event == CONF_WAKE_UP_EVENT_N_BEACONS)
++		value = wl->conf.conn.listen_interval;
++	else
++		value = 0;
++
++	return wl1271_format_buffer(user_buf, count, ppos, "%d\n", value);
++}
++
++static ssize_t beacon_interval_write(struct file *file,
++				     const char __user *user_buf,
++				     size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	unsigned long value;
++	int ret;
++
++	ret = kstrtoul_from_user(user_buf, count, 10, &value);
++	if (ret < 0) {
++		wl1271_warning("illegal value for beacon_interval");
++		return -EINVAL;
++	}
++
++	if (value < 1 || value > 255) {
++		wl1271_warning("beacon interval value is not in valid range");
++		return -ERANGE;
++	}
++
++	mutex_lock(&wl->mutex);
++
++	wl->conf.conn.listen_interval = value;
++	/* for some reason there are different event types for 1 and >1 */
++	if (value == 1)
++		wl->conf.conn.wake_up_event = CONF_WAKE_UP_EVENT_BEACON;
++	else
++		wl->conf.conn.wake_up_event = CONF_WAKE_UP_EVENT_N_BEACONS;
++
++	/*
++	 * we don't reconfigure ACX_WAKE_UP_CONDITIONS now, so it will only
++	 * take effect on the next time we enter psm.
++	 */
++	mutex_unlock(&wl->mutex);
++	return count;
++}
++
++static const struct file_operations beacon_interval_ops = {
++	.read = beacon_interval_read,
++	.write = beacon_interval_write,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static ssize_t rx_streaming_interval_write(struct file *file,
++			   const char __user *user_buf,
++			   size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	struct wl12xx_vif *wlvif;
++	unsigned long value;
++	int ret;
++
++	ret = kstrtoul_from_user(user_buf, count, 10, &value);
++	if (ret < 0) {
++		wl1271_warning("illegal value in rx_streaming_interval!");
++		return -EINVAL;
++	}
++
++	/* valid values: 0, 10-100 */
++	if (value && (value < 10 || value > 100)) {
++		wl1271_warning("value is not in range!");
++		return -ERANGE;
++	}
++
++	mutex_lock(&wl->mutex);
++
++	wl->conf.rx_streaming.interval = value;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	wl12xx_for_each_wlvif_sta(wl, wlvif) {
++		wl1271_recalc_rx_streaming(wl, wlvif);
++	}
++
++	wl1271_ps_elp_sleep(wl);
++out:
++	mutex_unlock(&wl->mutex);
++	return count;
++}
++
++static ssize_t rx_streaming_interval_read(struct file *file,
++			    char __user *userbuf,
++			    size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	return wl1271_format_buffer(userbuf, count, ppos,
++				    "%d\n", wl->conf.rx_streaming.interval);
++}
++
++static const struct file_operations rx_streaming_interval_ops = {
++	.read = rx_streaming_interval_read,
++	.write = rx_streaming_interval_write,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static ssize_t rx_streaming_always_write(struct file *file,
++			   const char __user *user_buf,
++			   size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	struct wl12xx_vif *wlvif;
++	unsigned long value;
++	int ret;
++
++	ret = kstrtoul_from_user(user_buf, count, 10, &value);
++	if (ret < 0) {
++		wl1271_warning("illegal value in rx_streaming_write!");
++		return -EINVAL;
++	}
++
++	/* valid values: 0, 10-100 */
++	if (!(value == 0 || value == 1)) {
++		wl1271_warning("value is not in valid!");
++		return -EINVAL;
++	}
++
++	mutex_lock(&wl->mutex);
++
++	wl->conf.rx_streaming.always = value;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	wl12xx_for_each_wlvif_sta(wl, wlvif) {
++		wl1271_recalc_rx_streaming(wl, wlvif);
++	}
++
++	wl1271_ps_elp_sleep(wl);
++out:
++	mutex_unlock(&wl->mutex);
++	return count;
++}
++
++static ssize_t rx_streaming_always_read(struct file *file,
++			    char __user *userbuf,
++			    size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	return wl1271_format_buffer(userbuf, count, ppos,
++				    "%d\n", wl->conf.rx_streaming.always);
++}
++
++static const struct file_operations rx_streaming_always_ops = {
++	.read = rx_streaming_always_read,
++	.write = rx_streaming_always_write,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static ssize_t beacon_filtering_write(struct file *file,
++				      const char __user *user_buf,
++				      size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	struct wl12xx_vif *wlvif;
++	char buf[10];
++	size_t len;
++	unsigned long value;
++	int ret;
++
++	len = min(count, sizeof(buf) - 1);
++	if (copy_from_user(buf, user_buf, len))
++		return -EFAULT;
++	buf[len] = '\0';
++
++	ret = kstrtoul(buf, 0, &value);
++	if (ret < 0) {
++		wl1271_warning("illegal value for beacon_filtering!");
++		return -EINVAL;
++	}
++
++	mutex_lock(&wl->mutex);
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	wl12xx_for_each_wlvif(wl, wlvif) {
++		ret = wl1271_acx_beacon_filter_opt(wl, wlvif, !!value);
++	}
++
++	wl1271_ps_elp_sleep(wl);
++out:
++	mutex_unlock(&wl->mutex);
++	return count;
++}
++
++static const struct file_operations beacon_filtering_ops = {
++	.write = beacon_filtering_write,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static ssize_t fw_stats_raw_read(struct file *file,
++				 char __user *userbuf,
++				 size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++
++	wl1271_debugfs_update_stats(wl);
++
++	return simple_read_from_buffer(userbuf, count, ppos,
++				       wl->stats.fw_stats,
++				       wl->stats.fw_stats_len);
++}
++
++static const struct file_operations fw_stats_raw_ops = {
++	.read = fw_stats_raw_read,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static ssize_t sleep_auth_read(struct file *file, char __user *user_buf,
++			       size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++
++	return wl1271_format_buffer(user_buf, count,
++				    ppos, "%d\n",
++				    wl->sleep_auth);
++}
++
++static ssize_t sleep_auth_write(struct file *file,
++				const char __user *user_buf,
++				size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	unsigned long value;
++	int ret;
++
++	ret = kstrtoul_from_user(user_buf, count, 0, &value);
++	if (ret < 0) {
++		wl1271_warning("illegal value in sleep_auth");
++		return -EINVAL;
++	}
++
++	if (value > WL1271_PSM_MAX) {
++		wl1271_warning("sleep_auth must be between 0 and %d",
++			       WL1271_PSM_MAX);
++		return -ERANGE;
++	}
++
++	mutex_lock(&wl->mutex);
++
++	wl->conf.conn.sta_sleep_auth = value;
++
++	if (unlikely(wl->state != WLCORE_STATE_ON)) {
++		/* this will show up on "read" in case we are off */
++		wl->sleep_auth = value;
++		goto out;
++	}
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wl1271_acx_sleep_auth(wl, value);
++	if (ret < 0)
++		goto out_sleep;
++
++out_sleep:
++	wl1271_ps_elp_sleep(wl);
++out:
++	mutex_unlock(&wl->mutex);
++	return count;
++}
++
++static const struct file_operations sleep_auth_ops = {
++	.read = sleep_auth_read,
++	.write = sleep_auth_write,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static ssize_t dev_mem_read(struct file *file,
++	     char __user *user_buf, size_t count,
++	     loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	struct wlcore_partition_set part, old_part;
++	size_t bytes = count;
++	int ret;
++	char *buf;
++
++	/* only requests of dword-aligned size and offset are supported */
++	if (bytes % 4)
++		return -EINVAL;
++
++	if (*ppos % 4)
++		return -EINVAL;
++
++	/* function should return in reasonable time */
++	bytes = min(bytes, WLCORE_MAX_BLOCK_SIZE);
++
++	if (bytes == 0)
++		return -EINVAL;
++
++	memset(&part, 0, sizeof(part));
++	part.mem.start = file->f_pos;
++	part.mem.size = bytes;
++
++	buf = kmalloc(bytes, GFP_KERNEL);
++	if (!buf)
++		return -ENOMEM;
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state == WLCORE_STATE_OFF)) {
++		ret = -EFAULT;
++		goto skip_read;
++	}
++
++	/*
++	 * Don't fail if elp_wakeup returns an error, so the device's memory
++	 * could be read even if the FW crashed
++	 */
++	wl1271_ps_elp_wakeup(wl);
++
++	/* store current partition and switch partition */
++	memcpy(&old_part, &wl->curr_part, sizeof(old_part));
++	ret = wlcore_set_partition(wl, &part);
++	if (ret < 0)
++		goto part_err;
++
++	ret = wlcore_raw_read(wl, 0, buf, bytes, false);
++	if (ret < 0)
++		goto read_err;
++
++read_err:
++	/* recover partition */
++	ret = wlcore_set_partition(wl, &old_part);
++	if (ret < 0)
++		goto part_err;
++
++part_err:
++	wl1271_ps_elp_sleep(wl);
++
++skip_read:
++	mutex_unlock(&wl->mutex);
++
++	if (ret == 0) {
++		ret = copy_to_user(user_buf, buf, bytes);
++		if (ret < bytes) {
++			bytes -= ret;
++			*ppos += bytes;
++			ret = 0;
++		} else {
++			ret = -EFAULT;
++		}
++	}
++
++	kfree(buf);
++
++	return ((ret == 0) ? bytes : ret);
++}
++
++static ssize_t dev_mem_write(struct file *file, const char __user *user_buf,
++		size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	struct wlcore_partition_set part, old_part;
++	size_t bytes = count;
++	int ret;
++	char *buf;
++
++	/* only requests of dword-aligned size and offset are supported */
++	if (bytes % 4)
++		return -EINVAL;
++
++	if (*ppos % 4)
++		return -EINVAL;
++
++	/* function should return in reasonable time */
++	bytes = min(bytes, WLCORE_MAX_BLOCK_SIZE);
++
++	if (bytes == 0)
++		return -EINVAL;
++
++	memset(&part, 0, sizeof(part));
++	part.mem.start = file->f_pos;
++	part.mem.size = bytes;
++
++	buf = kmalloc(bytes, GFP_KERNEL);
++	if (!buf)
++		return -ENOMEM;
++
++	ret = copy_from_user(buf, user_buf, bytes);
++	if (ret) {
++		ret = -EFAULT;
++		goto err_out;
++	}
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state == WLCORE_STATE_OFF)) {
++		ret = -EFAULT;
++		goto skip_write;
++	}
++
++	/*
++	 * Don't fail if elp_wakeup returns an error, so the device's memory
++	 * could be read even if the FW crashed
++	 */
++	wl1271_ps_elp_wakeup(wl);
++
++	/* store current partition and switch partition */
++	memcpy(&old_part, &wl->curr_part, sizeof(old_part));
++	ret = wlcore_set_partition(wl, &part);
++	if (ret < 0)
++		goto part_err;
++
++	ret = wlcore_raw_write(wl, 0, buf, bytes, false);
++	if (ret < 0)
++		goto write_err;
++
++write_err:
++	/* recover partition */
++	ret = wlcore_set_partition(wl, &old_part);
++	if (ret < 0)
++		goto part_err;
++
++part_err:
++	wl1271_ps_elp_sleep(wl);
++
++skip_write:
++	mutex_unlock(&wl->mutex);
++
++	if (ret == 0)
++		*ppos += bytes;
++
++err_out:
++	kfree(buf);
++
++	return ((ret == 0) ? bytes : ret);
++}
++
++static loff_t dev_mem_seek(struct file *file, loff_t offset, int orig)
++{
++	loff_t ret;
++
++	/* only requests of dword-aligned size and offset are supported */
++	if (offset % 4)
++		return -EINVAL;
++
++	switch (orig) {
++	case SEEK_SET:
++		file->f_pos = offset;
++		ret = file->f_pos;
++		break;
++	case SEEK_CUR:
++		file->f_pos += offset;
++		ret = file->f_pos;
++		break;
++	default:
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static const struct file_operations dev_mem_ops = {
++	.open = simple_open,
++	.read = dev_mem_read,
++	.write = dev_mem_write,
++	.llseek = dev_mem_seek,
++};
++
++static int wl1271_debugfs_add_files(struct wl1271 *wl,
++				    struct dentry *rootdir)
++{
++	int ret = 0;
++	struct dentry *entry, *streaming;
++
++	DEBUGFS_ADD(tx_queue_len, rootdir);
++	DEBUGFS_ADD(retry_count, rootdir);
++	DEBUGFS_ADD(excessive_retries, rootdir);
++
++	DEBUGFS_ADD(gpio_power, rootdir);
++	DEBUGFS_ADD(start_recovery, rootdir);
++	DEBUGFS_ADD(driver_state, rootdir);
++	DEBUGFS_ADD(vifs_state, rootdir);
++	DEBUGFS_ADD(dtim_interval, rootdir);
++	DEBUGFS_ADD(suspend_dtim_interval, rootdir);
++	DEBUGFS_ADD(beacon_interval, rootdir);
++	DEBUGFS_ADD(beacon_filtering, rootdir);
++	DEBUGFS_ADD(dynamic_ps_timeout, rootdir);
++	DEBUGFS_ADD(forced_ps, rootdir);
++	DEBUGFS_ADD(split_scan_timeout, rootdir);
++	DEBUGFS_ADD(irq_pkt_threshold, rootdir);
++	DEBUGFS_ADD(irq_blk_threshold, rootdir);
++	DEBUGFS_ADD(irq_timeout, rootdir);
++	DEBUGFS_ADD(fw_stats_raw, rootdir);
++	DEBUGFS_ADD(sleep_auth, rootdir);
++
++	streaming = debugfs_create_dir("rx_streaming", rootdir);
++	if (!streaming || IS_ERR(streaming))
++		goto err;
++
++	DEBUGFS_ADD_PREFIX(rx_streaming, interval, streaming);
++	DEBUGFS_ADD_PREFIX(rx_streaming, always, streaming);
++
++	DEBUGFS_ADD_PREFIX(dev, mem, rootdir);
++
++	return 0;
++
++err:
++	if (IS_ERR(entry))
++		ret = PTR_ERR(entry);
++	else
++		ret = -ENOMEM;
++
++	return ret;
++}
++
++void wl1271_debugfs_reset(struct wl1271 *wl)
++{
++	if (!wl->stats.fw_stats)
++		return;
++
++	memset(wl->stats.fw_stats, 0, wl->stats.fw_stats_len);
++	wl->stats.retry_count = 0;
++	wl->stats.excessive_retries = 0;
++}
++
++int wl1271_debugfs_init(struct wl1271 *wl)
++{
++	int ret;
++	struct dentry *rootdir;
++
++	rootdir = debugfs_create_dir(KBUILD_MODNAME,
++				     wl->hw->wiphy->debugfsdir);
++
++	if (IS_ERR(rootdir)) {
++		ret = PTR_ERR(rootdir);
++		goto out;
++	}
++
++	wl->stats.fw_stats = kzalloc(wl->stats.fw_stats_len, GFP_KERNEL);
++	if (!wl->stats.fw_stats) {
++		ret = -ENOMEM;
++		goto out_remove;
++	}
++
++	wl->stats.fw_stats_update = jiffies;
++
++	ret = wl1271_debugfs_add_files(wl, rootdir);
++	if (ret < 0)
++		goto out_exit;
++
++	ret = wlcore_debugfs_init(wl, rootdir);
++	if (ret < 0)
++		goto out_exit;
++
++	goto out;
++
++out_exit:
++	wl1271_debugfs_exit(wl);
++
++out_remove:
++	debugfs_remove_recursive(rootdir);
++
++out:
++	return ret;
++}
++
++void wl1271_debugfs_exit(struct wl1271 *wl)
++{
++	kfree(wl->stats.fw_stats);
++	wl->stats.fw_stats = NULL;
++}
+diff -Nuar git.old/drivers/net/wireless/ti/wlcore/main.c git/drivers/net/wireless/ti/wlcore/main.c
+--- git.old/drivers/net/wireless/ti/wlcore/main.c	2015-03-04 16:35:05.706185997 +0100
++++ git/drivers/net/wireless/ti/wlcore/main.c	2015-03-04 16:51:35.578176557 +0100
+@@ -34,6 +34,7 @@
+ #include <linux/wl12xx.h>
+ #include <linux/sched.h>
+ #include <linux/interrupt.h>
++#include <linux/irq.h>
+ 
+ #include "wlcore.h"
+ #include "debug.h"
+@@ -529,7 +530,7 @@
+ 	 * In case edge triggered interrupt must be used, we cannot iterate
+ 	 * more than once without introducing race conditions with the hardirq.
+ 	 */
+-	if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ)
++	if (wl->irq_flags & IRQF_TRIGGER_RISING)
+ 		loopcount = 1;
+ 
+ 	wl1271_debug(DEBUG_IRQ, "IRQ work");
+@@ -5894,7 +5895,6 @@
+ 	wl->ap_ps_map = 0;
+ 	wl->ap_fw_ps_map = 0;
+ 	wl->quirks = 0;
+-	wl->platform_quirks = 0;
+ 	wl->system_hlid = WL12XX_SYSTEM_HLID;
+ 	wl->active_sta_count = 0;
+ 	wl->active_link_count = 0;
+@@ -6024,7 +6024,7 @@
+ 	struct platform_device *pdev = wl->pdev;
+ 	struct wlcore_platdev_data *pdev_data = pdev->dev.platform_data;
+ 	struct wl12xx_platform_data *pdata = pdev_data->pdata;
+-	unsigned long irqflags;
++
+ 	int ret;
+ 
+ 	if (fw) {
+@@ -6051,16 +6051,15 @@
+ 	wlcore_adjust_conf(wl);
+ 
+ 	wl->irq = platform_get_irq(pdev, 0);
+-	wl->platform_quirks = pdata->platform_quirks;
+ 	wl->if_ops = pdev_data->if_ops;
+ 
+-	if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ)
+-		irqflags = IRQF_TRIGGER_RISING;
+-	else
+-		irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT;
++
++
++
++	wl->irq_flags |= IRQF_ONESHOT;
+ 
+ 	ret = request_threaded_irq(wl->irq, NULL, wlcore_irq,
+-				   irqflags, pdev->name, wl);
++				   wl->irq_flags, pdev->name, wl);
+ 	if (ret < 0) {
+ 		wl1271_error("request_irq() failed: %d", ret);
+ 		goto out_free_nvs;
+@@ -6071,7 +6070,7 @@
+ 	if (!ret) {
+ 		wl->irq_wake_enabled = true;
+ 		device_init_wakeup(wl->dev, 1);
+-		if (pdata->pwr_in_suspend) {
++		if (pdev_data->pwr_in_suspend) {
+ 			wl->hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY;
+ 			wl->hw->wiphy->wowlan.n_patterns =
+ 				WL1271_MAX_RX_FILTERS;
+diff -Nuar git.old/drivers/net/wireless/ti/wlcore/main.c.orig git/drivers/net/wireless/ti/wlcore/main.c.orig
+--- git.old/drivers/net/wireless/ti/wlcore/main.c.orig	1970-01-01 01:00:00.000000000 +0100
++++ git/drivers/net/wireless/ti/wlcore/main.c.orig	2015-03-04 16:46:03.618179723 +0100
+@@ -0,0 +1,6209 @@
++
++/*
++ * This file is part of wl1271
++ *
++ * Copyright (C) 2008-2010 Nokia Corporation
++ *
++ * Contact: Luciano Coelho <luciano.coelho at nokia.com>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++ * 02110-1301 USA
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/firmware.h>
++#include <linux/delay.h>
++#include <linux/spi/spi.h>
++#include <linux/crc32.h>
++#include <linux/etherdevice.h>
++#include <linux/vmalloc.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++#include <linux/wl12xx.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++
++#include "wlcore.h"
++#include "debug.h"
++#include "wl12xx_80211.h"
++#include "io.h"
++#include "event.h"
++#include "tx.h"
++#include "rx.h"
++#include "ps.h"
++#include "init.h"
++#include "debugfs.h"
++#include "cmd.h"
++#include "boot.h"
++#include "testmode.h"
++#include "scan.h"
++#include "hw_ops.h"
++
++#define WL1271_BOOT_RETRIES 3
++
++#define WL1271_BOOT_RETRIES 3
++
++static char *fwlog_param;
++static int bug_on_recovery = -1;
++static int no_recovery     = -1;
++
++static void __wl1271_op_remove_interface(struct wl1271 *wl,
++					 struct ieee80211_vif *vif,
++					 bool reset_tx_queues);
++static void wlcore_op_stop_locked(struct wl1271 *wl);
++static void wl1271_free_ap_keys(struct wl1271 *wl, struct wl12xx_vif *wlvif);
++
++static int wl12xx_set_authorized(struct wl1271 *wl,
++				 struct wl12xx_vif *wlvif)
++{
++	int ret;
++
++	if (WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS))
++		return -EINVAL;
++
++	if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
++		return 0;
++
++	if (test_and_set_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags))
++		return 0;
++
++	ret = wl12xx_cmd_set_peer_state(wl, wlvif, wlvif->sta.hlid);
++	if (ret < 0)
++		return ret;
++
++	wl1271_info("Association completed.");
++	return 0;
++}
++
++static void wl1271_reg_notify(struct wiphy *wiphy,
++			      struct regulatory_request *request)
++{
++	struct ieee80211_supported_band *band;
++	struct ieee80211_channel *ch;
++	int i;
++	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++	struct wl1271 *wl = hw->priv;
++
++	band = wiphy->bands[IEEE80211_BAND_5GHZ];
++	for (i = 0; i < band->n_channels; i++) {
++		ch = &band->channels[i];
++		if (ch->flags & IEEE80211_CHAN_DISABLED)
++			continue;
++
++		if (ch->flags & IEEE80211_CHAN_RADAR)
++			ch->flags |= IEEE80211_CHAN_NO_IBSS |
++				     IEEE80211_CHAN_PASSIVE_SCAN;
++
++	}
++
++	wlcore_regdomain_config(wl);
++}
++
++static int wl1271_set_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif,
++				   bool enable)
++{
++	int ret = 0;
++
++	/* we should hold wl->mutex */
++	ret = wl1271_acx_ps_rx_streaming(wl, wlvif, enable);
++	if (ret < 0)
++		goto out;
++
++	if (enable)
++		set_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags);
++	else
++		clear_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags);
++out:
++	return ret;
++}
++
++/*
++ * this function is being called when the rx_streaming interval
++ * has beed changed or rx_streaming should be disabled
++ */
++int wl1271_recalc_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif)
++{
++	int ret = 0;
++	int period = wl->conf.rx_streaming.interval;
++
++	/* don't reconfigure if rx_streaming is disabled */
++	if (!test_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags))
++		goto out;
++
++	/* reconfigure/disable according to new streaming_period */
++	if (period &&
++	    test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) &&
++	    (wl->conf.rx_streaming.always ||
++	     test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags)))
++		ret = wl1271_set_rx_streaming(wl, wlvif, true);
++	else {
++		ret = wl1271_set_rx_streaming(wl, wlvif, false);
++		/* don't cancel_work_sync since we might deadlock */
++		del_timer_sync(&wlvif->rx_streaming_timer);
++	}
++out:
++	return ret;
++}
++
++static void wl1271_rx_streaming_enable_work(struct work_struct *work)
++{
++	int ret;
++	struct wl12xx_vif *wlvif = container_of(work, struct wl12xx_vif,
++						rx_streaming_enable_work);
++	struct wl1271 *wl = wlvif->wl;
++
++	mutex_lock(&wl->mutex);
++
++	if (test_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags) ||
++	    !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) ||
++	    (!wl->conf.rx_streaming.always &&
++	     !test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags)))
++		goto out;
++
++	if (!wl->conf.rx_streaming.interval)
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wl1271_set_rx_streaming(wl, wlvif, true);
++	if (ret < 0)
++		goto out_sleep;
++
++	/* stop it after some time of inactivity */
++	mod_timer(&wlvif->rx_streaming_timer,
++		  jiffies + msecs_to_jiffies(wl->conf.rx_streaming.duration));
++
++out_sleep:
++	wl1271_ps_elp_sleep(wl);
++out:
++	mutex_unlock(&wl->mutex);
++}
++
++static void wl1271_rx_streaming_disable_work(struct work_struct *work)
++{
++	int ret;
++	struct wl12xx_vif *wlvif = container_of(work, struct wl12xx_vif,
++						rx_streaming_disable_work);
++	struct wl1271 *wl = wlvif->wl;
++
++	mutex_lock(&wl->mutex);
++
++	if (!test_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wl1271_set_rx_streaming(wl, wlvif, false);
++	if (ret)
++		goto out_sleep;
++
++out_sleep:
++	wl1271_ps_elp_sleep(wl);
++out:
++	mutex_unlock(&wl->mutex);
++}
++
++static void wl1271_rx_streaming_timer(unsigned long data)
++{
++	struct wl12xx_vif *wlvif = (struct wl12xx_vif *)data;
++	struct wl1271 *wl = wlvif->wl;
++	ieee80211_queue_work(wl->hw, &wlvif->rx_streaming_disable_work);
++}
++
++/* wl->mutex must be taken */
++void wl12xx_rearm_tx_watchdog_locked(struct wl1271 *wl)
++{
++	/* if the watchdog is not armed, don't do anything */
++	if (wl->tx_allocated_blocks == 0)
++		return;
++
++	cancel_delayed_work(&wl->tx_watchdog_work);
++	ieee80211_queue_delayed_work(wl->hw, &wl->tx_watchdog_work,
++		msecs_to_jiffies(wl->conf.tx.tx_watchdog_timeout));
++}
++
++static void wl12xx_tx_watchdog_work(struct work_struct *work)
++{
++	struct delayed_work *dwork;
++	struct wl1271 *wl;
++
++	dwork = container_of(work, struct delayed_work, work);
++	wl = container_of(dwork, struct wl1271, tx_watchdog_work);
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	/* Tx went out in the meantime - everything is ok */
++	if (unlikely(wl->tx_allocated_blocks == 0))
++		goto out;
++
++	/*
++	 * if a ROC is in progress, we might not have any Tx for a long
++	 * time (e.g. pending Tx on the non-ROC channels)
++	 */
++	if (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES) {
++		wl1271_debug(DEBUG_TX, "No Tx (in FW) for %d ms due to ROC",
++			     wl->conf.tx.tx_watchdog_timeout);
++		wl12xx_rearm_tx_watchdog_locked(wl);
++		goto out;
++	}
++
++	/*
++	 * if a scan is in progress, we might not have any Tx for a long
++	 * time
++	 */
++	if (wl->scan.state != WL1271_SCAN_STATE_IDLE) {
++		wl1271_debug(DEBUG_TX, "No Tx (in FW) for %d ms due to scan",
++			     wl->conf.tx.tx_watchdog_timeout);
++		wl12xx_rearm_tx_watchdog_locked(wl);
++		goto out;
++	}
++
++	/*
++	* AP might cache a frame for a long time for a sleeping station,
++	* so rearm the timer if there's an AP interface with stations. If
++	* Tx is genuinely stuck we will most hopefully discover it when all
++	* stations are removed due to inactivity.
++	*/
++	if (wl->active_sta_count) {
++		wl1271_debug(DEBUG_TX, "No Tx (in FW) for %d ms. AP has "
++			     " %d stations",
++			      wl->conf.tx.tx_watchdog_timeout,
++			      wl->active_sta_count);
++		wl12xx_rearm_tx_watchdog_locked(wl);
++		goto out;
++	}
++
++	wl1271_error("Tx stuck (in FW) for %d ms. Starting recovery",
++		     wl->conf.tx.tx_watchdog_timeout);
++	wl12xx_queue_recovery_work(wl);
++
++out:
++	mutex_unlock(&wl->mutex);
++}
++
++static void wlcore_adjust_conf(struct wl1271 *wl)
++{
++	/* Adjust settings according to optional module parameters */
++
++	if (fwlog_param) {
++		if (!strcmp(fwlog_param, "continuous")) {
++			wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS;
++		} else if (!strcmp(fwlog_param, "ondemand")) {
++			wl->conf.fwlog.mode = WL12XX_FWLOG_ON_DEMAND;
++		} else if (!strcmp(fwlog_param, "dbgpins")) {
++			wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS;
++			wl->conf.fwlog.output = WL12XX_FWLOG_OUTPUT_DBG_PINS;
++		} else if (!strcmp(fwlog_param, "disable")) {
++			wl->conf.fwlog.mem_blocks = 0;
++			wl->conf.fwlog.output = WL12XX_FWLOG_OUTPUT_NONE;
++		} else {
++			wl1271_error("Unknown fwlog parameter %s", fwlog_param);
++		}
++	}
++
++	if (bug_on_recovery != -1)
++		wl->conf.recovery.bug_on_recovery = (u8) bug_on_recovery;
++
++	if (no_recovery != -1)
++		wl->conf.recovery.no_recovery = (u8) no_recovery;
++}
++
++static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl,
++					struct wl12xx_vif *wlvif,
++					u8 hlid, u8 tx_pkts)
++{
++	bool fw_ps;
++
++	fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
++
++	/*
++	 * Wake up from high level PS if the STA is asleep with too little
++	 * packets in FW or if the STA is awake.
++	 */
++	if (!fw_ps || tx_pkts < WL1271_PS_STA_MAX_PACKETS)
++		wl12xx_ps_link_end(wl, wlvif, hlid);
++
++	/*
++	 * Start high-level PS if the STA is asleep with enough blocks in FW.
++	 * Make an exception if this is the only connected link. In this
++	 * case FW-memory congestion is less of a problem.
++	 * Note that a single connected STA means 3 active links, since we must
++	 * account for the global and broadcast AP links. The "fw_ps" check
++	 * assures us the third link is a STA connected to the AP. Otherwise
++	 * the FW would not set the PSM bit.
++	 */
++	else if (wl->active_link_count > 3 && fw_ps &&
++		 tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
++		wl12xx_ps_link_start(wl, wlvif, hlid, true);
++}
++
++static void wl12xx_irq_update_links_status(struct wl1271 *wl,
++					   struct wl12xx_vif *wlvif,
++					   struct wl_fw_status_2 *status)
++{
++	u32 cur_fw_ps_map;
++	u8 hlid;
++
++	cur_fw_ps_map = le32_to_cpu(status->link_ps_bitmap);
++	if (wl->ap_fw_ps_map != cur_fw_ps_map) {
++		wl1271_debug(DEBUG_PSM,
++			     "link ps prev 0x%x cur 0x%x changed 0x%x",
++			     wl->ap_fw_ps_map, cur_fw_ps_map,
++			     wl->ap_fw_ps_map ^ cur_fw_ps_map);
++
++		wl->ap_fw_ps_map = cur_fw_ps_map;
++	}
++
++	for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, WL12XX_MAX_LINKS)
++		wl12xx_irq_ps_regulate_link(wl, wlvif, hlid,
++					    wl->links[hlid].allocated_pkts);
++}
++
++static int wlcore_fw_status(struct wl1271 *wl,
++			    struct wl_fw_status_1 *status_1,
++			    struct wl_fw_status_2 *status_2)
++{
++	struct wl12xx_vif *wlvif;
++	struct timespec ts;
++	u32 old_tx_blk_count = wl->tx_blocks_available;
++	int avail, freed_blocks;
++	int i;
++	size_t status_len;
++	int ret;
++	struct wl1271_link *lnk;
++
++	status_len = WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
++		sizeof(*status_2) + wl->fw_status_priv_len;
++
++	ret = wlcore_raw_read_data(wl, REG_RAW_FW_STATUS_ADDR, status_1,
++				   status_len, false);
++	if (ret < 0)
++		return ret;
++
++	wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, "
++		     "drv_rx_counter = %d, tx_results_counter = %d)",
++		     status_1->intr,
++		     status_1->fw_rx_counter,
++		     status_1->drv_rx_counter,
++		     status_1->tx_results_counter);
++
++	for (i = 0; i < NUM_TX_QUEUES; i++) {
++		/* prevent wrap-around in freed-packets counter */
++		wl->tx_allocated_pkts[i] -=
++				(status_2->counters.tx_released_pkts[i] -
++				wl->tx_pkts_freed[i]) & 0xff;
++
++		wl->tx_pkts_freed[i] = status_2->counters.tx_released_pkts[i];
++	}
++
++
++	for_each_set_bit(i, wl->links_map, WL12XX_MAX_LINKS) {
++		u8 diff;
++		lnk = &wl->links[i];
++
++		/* prevent wrap-around in freed-packets counter */
++		diff = (status_2->counters.tx_lnk_free_pkts[i] -
++		       lnk->prev_freed_pkts) & 0xff;
++
++		if (diff == 0)
++			continue;
++
++		lnk->allocated_pkts -= diff;
++		lnk->prev_freed_pkts = status_2->counters.tx_lnk_free_pkts[i];
++
++		/* accumulate the prev_freed_pkts counter */
++		lnk->total_freed_pkts += diff;
++	}
++
++	/* prevent wrap-around in total blocks counter */
++	if (likely(wl->tx_blocks_freed <=
++		   le32_to_cpu(status_2->total_released_blks)))
++		freed_blocks = le32_to_cpu(status_2->total_released_blks) -
++			       wl->tx_blocks_freed;
++	else
++		freed_blocks = 0x100000000LL - wl->tx_blocks_freed +
++			       le32_to_cpu(status_2->total_released_blks);
++
++	wl->tx_blocks_freed = le32_to_cpu(status_2->total_released_blks);
++
++	wl->tx_allocated_blocks -= freed_blocks;
++
++	/*
++	 * If the FW freed some blocks:
++	 * If we still have allocated blocks - re-arm the timer, Tx is
++	 * not stuck. Otherwise, cancel the timer (no Tx currently).
++	 */
++	if (freed_blocks) {
++		if (wl->tx_allocated_blocks)
++			wl12xx_rearm_tx_watchdog_locked(wl);
++		else
++			cancel_delayed_work(&wl->tx_watchdog_work);
++	}
++
++	avail = le32_to_cpu(status_2->tx_total) - wl->tx_allocated_blocks;
++
++	/*
++	 * The FW might change the total number of TX memblocks before
++	 * we get a notification about blocks being released. Thus, the
++	 * available blocks calculation might yield a temporary result
++	 * which is lower than the actual available blocks. Keeping in
++	 * mind that only blocks that were allocated can be moved from
++	 * TX to RX, tx_blocks_available should never decrease here.
++	 */
++	wl->tx_blocks_available = max((int)wl->tx_blocks_available,
++				      avail);
++
++	/* if more blocks are available now, tx work can be scheduled */
++	if (wl->tx_blocks_available > old_tx_blk_count)
++		clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags);
++
++	/* for AP update num of allocated TX blocks per link and ps status */
++	wl12xx_for_each_wlvif_ap(wl, wlvif) {
++		wl12xx_irq_update_links_status(wl, wlvif, status_2);
++	}
++
++	/* update the host-chipset time offset */
++	getnstimeofday(&ts);
++	wl->time_offset = (timespec_to_ns(&ts) >> 10) -
++		(s64)le32_to_cpu(status_2->fw_localtime);
++
++	wl->fw_fast_lnk_map = le32_to_cpu(status_2->link_fast_bitmap);
++
++	return 0;
++}
++
++static void wl1271_flush_deferred_work(struct wl1271 *wl)
++{
++	struct sk_buff *skb;
++
++	/* Pass all received frames to the network stack */
++	while ((skb = skb_dequeue(&wl->deferred_rx_queue)))
++		ieee80211_rx_ni(wl->hw, skb);
++
++	/* Return sent skbs to the network stack */
++	while ((skb = skb_dequeue(&wl->deferred_tx_queue)))
++		ieee80211_tx_status_ni(wl->hw, skb);
++}
++
++static void wl1271_netstack_work(struct work_struct *work)
++{
++	struct wl1271 *wl =
++		container_of(work, struct wl1271, netstack_work);
++
++	do {
++		wl1271_flush_deferred_work(wl);
++	} while (skb_queue_len(&wl->deferred_rx_queue));
++}
++
++#define WL1271_IRQ_MAX_LOOPS 256
++
++static int wlcore_irq_locked(struct wl1271 *wl)
++{
++	int ret = 0;
++	u32 intr;
++	int loopcount = WL1271_IRQ_MAX_LOOPS;
++	bool done = false;
++	unsigned int defer_count;
++	unsigned long flags;
++
++	/*
++	 * In case edge triggered interrupt must be used, we cannot iterate
++	 * more than once without introducing race conditions with the hardirq.
++	 */
++	if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ)
++		loopcount = 1;
++
++	wl1271_debug(DEBUG_IRQ, "IRQ work");
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	while (!done && loopcount--) {
++		/*
++		 * In order to avoid a race with the hardirq, clear the flag
++		 * before acknowledging the chip. Since the mutex is held,
++		 * wl1271_ps_elp_wakeup cannot be called concurrently.
++		 */
++		clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
++		smp_mb__after_clear_bit();
++
++		ret = wlcore_fw_status(wl, wl->fw_status_1, wl->fw_status_2);
++		if (ret < 0)
++			goto out;
++
++		wlcore_hw_tx_immediate_compl(wl);
++
++		intr = le32_to_cpu(wl->fw_status_1->intr);
++		intr &= WLCORE_ALL_INTR_MASK;
++		if (!intr) {
++			done = true;
++			continue;
++		}
++
++		if (unlikely(intr & WL1271_ACX_INTR_WATCHDOG)) {
++			wl1271_error("HW watchdog interrupt received! starting recovery.");
++			wl->watchdog_recovery = true;
++			ret = -EIO;
++
++			/* restarting the chip. ignore any other interrupt. */
++			goto out;
++		}
++
++		if (unlikely(intr & WL1271_ACX_SW_INTR_WATCHDOG)) {
++			wl1271_error("SW watchdog interrupt received! "
++				     "starting recovery.");
++			wl->watchdog_recovery = true;
++			ret = -EIO;
++
++			/* restarting the chip. ignore any other interrupt. */
++			goto out;
++		}
++
++		if (likely(intr & WL1271_ACX_INTR_DATA)) {
++			wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA");
++
++			ret = wlcore_rx(wl, wl->fw_status_1);
++			if (ret < 0)
++				goto out;
++
++			/* Check if any tx blocks were freed */
++			spin_lock_irqsave(&wl->wl_lock, flags);
++			if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags) &&
++			    wl1271_tx_total_queue_count(wl) > 0) {
++				spin_unlock_irqrestore(&wl->wl_lock, flags);
++				/*
++				 * In order to avoid starvation of the TX path,
++				 * call the work function directly.
++				 */
++				ret = wlcore_tx_work_locked(wl);
++				if (ret < 0)
++					goto out;
++			} else {
++				spin_unlock_irqrestore(&wl->wl_lock, flags);
++			}
++
++			/* check for tx results */
++			ret = wlcore_hw_tx_delayed_compl(wl);
++			if (ret < 0)
++				goto out;
++
++			/* Make sure the deferred queues don't get too long */
++			defer_count = skb_queue_len(&wl->deferred_tx_queue) +
++				      skb_queue_len(&wl->deferred_rx_queue);
++			if (defer_count > WL1271_DEFERRED_QUEUE_LIMIT)
++				wl1271_flush_deferred_work(wl);
++		}
++
++		if (intr & WL1271_ACX_INTR_EVENT_A) {
++			wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_A");
++			ret = wl1271_event_handle(wl, 0);
++			if (ret < 0)
++				goto out;
++		}
++
++		if (intr & WL1271_ACX_INTR_EVENT_B) {
++			wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_B");
++			ret = wl1271_event_handle(wl, 1);
++			if (ret < 0)
++				goto out;
++		}
++
++		if (intr & WL1271_ACX_INTR_INIT_COMPLETE)
++			wl1271_debug(DEBUG_IRQ,
++				     "WL1271_ACX_INTR_INIT_COMPLETE");
++
++		if (intr & WL1271_ACX_INTR_HW_AVAILABLE)
++			wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE");
++	}
++
++	wl1271_ps_elp_sleep(wl);
++
++out:
++	return ret;
++}
++
++static irqreturn_t wlcore_irq(int irq, void *cookie)
++{
++	int ret;
++	unsigned long flags;
++	struct wl1271 *wl = cookie;
++
++	/* complete the ELP completion */
++	spin_lock_irqsave(&wl->wl_lock, flags);
++	set_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
++	if (wl->elp_compl) {
++		complete(wl->elp_compl);
++		wl->elp_compl = NULL;
++	}
++
++	if (test_bit(WL1271_FLAG_SUSPENDED, &wl->flags)) {
++		/* don't enqueue a work right now. mark it as pending */
++		set_bit(WL1271_FLAG_PENDING_WORK, &wl->flags);
++		wl1271_debug(DEBUG_IRQ, "should not enqueue work");
++		disable_irq_nosync(wl->irq);
++		pm_wakeup_event(wl->dev, 0);
++		spin_unlock_irqrestore(&wl->wl_lock, flags);
++		return IRQ_HANDLED;
++	}
++	spin_unlock_irqrestore(&wl->wl_lock, flags);
++
++	/* TX might be handled here, avoid redundant work */
++	set_bit(WL1271_FLAG_TX_PENDING, &wl->flags);
++	cancel_work_sync(&wl->tx_work);
++
++	mutex_lock(&wl->mutex);
++
++	ret = wlcore_irq_locked(wl);
++	if (ret)
++		wl12xx_queue_recovery_work(wl);
++
++	spin_lock_irqsave(&wl->wl_lock, flags);
++	/* In case TX was not handled here, queue TX work */
++	clear_bit(WL1271_FLAG_TX_PENDING, &wl->flags);
++	if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags) &&
++	    wl1271_tx_total_queue_count(wl) > 0)
++		ieee80211_queue_work(wl->hw, &wl->tx_work);
++	spin_unlock_irqrestore(&wl->wl_lock, flags);
++
++	mutex_unlock(&wl->mutex);
++
++	return IRQ_HANDLED;
++}
++
++struct vif_counter_data {
++	u8 counter;
++
++	struct ieee80211_vif *cur_vif;
++	bool cur_vif_running;
++};
++
++static void wl12xx_vif_count_iter(void *data, u8 *mac,
++				  struct ieee80211_vif *vif)
++{
++	struct vif_counter_data *counter = data;
++
++	counter->counter++;
++	if (counter->cur_vif == vif)
++		counter->cur_vif_running = true;
++}
++
++/* caller must not hold wl->mutex, as it might deadlock */
++static void wl12xx_get_vif_count(struct ieee80211_hw *hw,
++			       struct ieee80211_vif *cur_vif,
++			       struct vif_counter_data *data)
++{
++	memset(data, 0, sizeof(*data));
++	data->cur_vif = cur_vif;
++
++	ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
++					    wl12xx_vif_count_iter, data);
++}
++
++static int wl12xx_fetch_firmware(struct wl1271 *wl, bool plt)
++{
++	const struct firmware *fw;
++	const char *fw_name;
++	enum wl12xx_fw_type fw_type;
++	int ret;
++
++	if (plt) {
++		fw_type = WL12XX_FW_TYPE_PLT;
++		fw_name = wl->plt_fw_name;
++	} else {
++		/*
++		 * we can't call wl12xx_get_vif_count() here because
++		 * wl->mutex is taken, so use the cached last_vif_count value
++		 */
++		if (wl->last_vif_count > 1 && wl->mr_fw_name) {
++			fw_type = WL12XX_FW_TYPE_MULTI;
++			fw_name = wl->mr_fw_name;
++		} else {
++			fw_type = WL12XX_FW_TYPE_NORMAL;
++			fw_name = wl->sr_fw_name;
++		}
++	}
++
++	if (wl->fw_type == fw_type)
++		return 0;
++
++	wl1271_debug(DEBUG_BOOT, "booting firmware %s", fw_name);
++
++	ret = request_firmware(&fw, fw_name, wl->dev);
++
++	if (ret < 0) {
++		wl1271_error("could not get firmware %s: %d", fw_name, ret);
++		return ret;
++	}
++
++	if (fw->size % 4) {
++		wl1271_error("firmware size is not multiple of 32 bits: %zu",
++			     fw->size);
++		ret = -EILSEQ;
++		goto out;
++	}
++
++	vfree(wl->fw);
++	wl->fw_type = WL12XX_FW_TYPE_NONE;
++	wl->fw_len = fw->size;
++	wl->fw = vmalloc(wl->fw_len);
++
++	if (!wl->fw) {
++		wl1271_error("could not allocate memory for the firmware");
++		ret = -ENOMEM;
++		goto out;
++	}
++
++	memcpy(wl->fw, fw->data, wl->fw_len);
++	ret = 0;
++	wl->fw_type = fw_type;
++out:
++	release_firmware(fw);
++
++	return ret;
++}
++
++void wl12xx_queue_recovery_work(struct wl1271 *wl)
++{
++	WARN_ON(!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags));
++
++	/* Avoid a recursive recovery */
++	if (wl->state == WLCORE_STATE_ON) {
++		wl->state = WLCORE_STATE_RESTARTING;
++		set_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags);
++		wlcore_disable_interrupts_nosync(wl);
++		ieee80211_queue_work(wl->hw, &wl->recovery_work);
++	}
++}
++
++size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen)
++{
++	size_t len = 0;
++
++	/* The FW log is a length-value list, find where the log end */
++	while (len < maxlen) {
++		if (memblock[len] == 0)
++			break;
++		if (len + memblock[len] + 1 > maxlen)
++			break;
++		len += memblock[len] + 1;
++	}
++
++	/* Make sure we have enough room */
++	len = min(len, (size_t)(PAGE_SIZE - wl->fwlog_size));
++
++	/* Fill the FW log file, consumed by the sysfs fwlog entry */
++	memcpy(wl->fwlog + wl->fwlog_size, memblock, len);
++	wl->fwlog_size += len;
++
++	return len;
++}
++
++#define WLCORE_FW_LOG_END 0x2000000
++
++static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
++{
++	u32 addr;
++	u32 offset;
++	u32 end_of_log;
++	u8 *block;
++	int ret;
++
++	if ((wl->quirks & WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED) ||
++	    (wl->conf.fwlog.mem_blocks == 0))
++		return;
++
++	wl1271_info("Reading FW panic log");
++
++	block = kmalloc(WL12XX_HW_BLOCK_SIZE, GFP_KERNEL);
++	if (!block)
++		return;
++
++	/*
++	 * Make sure the chip is awake and the logger isn't active.
++	 * Do not send a stop fwlog command if the fw is hanged or if
++	 * dbgpins are used (due to some fw bug).
++	 */
++	if (wl1271_ps_elp_wakeup(wl))
++		goto out;
++	if (!wl->watchdog_recovery &&
++	    wl->conf.fwlog.output != WL12XX_FWLOG_OUTPUT_DBG_PINS)
++		wl12xx_cmd_stop_fwlog(wl);
++
++	/* Read the first memory block address */
++	ret = wlcore_fw_status(wl, wl->fw_status_1, wl->fw_status_2);
++	if (ret < 0)
++		goto out;
++
++	addr = le32_to_cpu(wl->fw_status_2->log_start_addr);
++	if (!addr)
++		goto out;
++
++	if (wl->conf.fwlog.mode == WL12XX_FWLOG_CONTINUOUS) {
++		offset = sizeof(addr) + sizeof(struct wl1271_rx_descriptor);
++		end_of_log = WLCORE_FW_LOG_END;
++	} else {
++		offset = sizeof(addr);
++		end_of_log = addr;
++	}
++
++	/* Traverse the memory blocks linked list */
++	do {
++		memset(block, 0, WL12XX_HW_BLOCK_SIZE);
++		ret = wlcore_read_hwaddr(wl, addr, block, WL12XX_HW_BLOCK_SIZE,
++					 false);
++		if (ret < 0)
++			goto out;
++
++		/*
++		 * Memory blocks are linked to one another. The first 4 bytes
++		 * of each memory block hold the hardware address of the next
++		 * one. The last memory block points to the first one in
++		 * on demand mode and is equal to 0x2000000 in continuous mode.
++		 */
++		addr = le32_to_cpup((__le32 *)block);
++		if (!wl12xx_copy_fwlog(wl, block + offset,
++				       WL12XX_HW_BLOCK_SIZE - offset))
++			break;
++	} while (addr && (addr != end_of_log));
++
++	wake_up_interruptible(&wl->fwlog_waitq);
++
++out:
++	kfree(block);
++}
++
++static void wlcore_print_recovery(struct wl1271 *wl)
++{
++	u32 pc = 0;
++	u32 hint_sts = 0;
++	int ret;
++
++	wl1271_info("Hardware recovery in progress. FW ver: %s",
++		    wl->chip.fw_ver_str);
++
++	/* change partitions momentarily so we can read the FW pc */
++	ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]);
++	if (ret < 0)
++		return;
++
++	ret = wlcore_read_reg(wl, REG_PC_ON_RECOVERY, &pc);
++	if (ret < 0)
++		return;
++
++	ret = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR, &hint_sts);
++	if (ret < 0)
++		return;
++
++	wl1271_info("pc: 0x%x, hint_sts: 0x%08x count: %d",
++				pc, hint_sts, ++wl->recovery_count);
++
++	wlcore_set_partition(wl, &wl->ptable[PART_WORK]);
++}
++
++
++static void wl1271_recovery_work(struct work_struct *work)
++{
++	struct wl1271 *wl =
++		container_of(work, struct wl1271, recovery_work);
++	struct wl12xx_vif *wlvif;
++	struct ieee80211_vif *vif;
++
++	mutex_lock(&wl->mutex);
++
++	if (wl->state == WLCORE_STATE_OFF || wl->plt)
++		goto out_unlock;
++
++	if (!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags)) {
++		wl12xx_read_fwlog_panic(wl);
++		wlcore_print_recovery(wl);
++	}
++
++	BUG_ON(wl->conf.recovery.bug_on_recovery &&
++	       !test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags));
++
++	if (wl->conf.recovery.no_recovery) {
++		wl1271_info("No recovery (chosen on module load). Fw will remain stuck.");
++		goto out_unlock;
++	}
++
++	/* Prevent spurious TX during FW restart */
++	wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_FW_RESTART);
++
++	/* reboot the chipset */
++	while (!list_empty(&wl->wlvif_list)) {
++		wlvif = list_first_entry(&wl->wlvif_list,
++				       struct wl12xx_vif, list);
++		vif = wl12xx_wlvif_to_vif(wlvif);
++		__wl1271_op_remove_interface(wl, vif, false);
++	}
++
++	wlcore_op_stop_locked(wl);
++
++	ieee80211_restart_hw(wl->hw);
++
++	/*
++	 * Its safe to enable TX now - the queues are stopped after a request
++	 * to restart the HW.
++	 */
++	wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_FW_RESTART);
++
++out_unlock:
++	wl->watchdog_recovery = false;
++	clear_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags);
++	mutex_unlock(&wl->mutex);
++}
++
++static int wlcore_fw_wakeup(struct wl1271 *wl)
++{
++	return wlcore_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG, ELPCTRL_WAKE_UP);
++}
++
++static int wl1271_setup(struct wl1271 *wl)
++{
++	wl->fw_status_1 = kmalloc(WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
++				  sizeof(*wl->fw_status_2) +
++				  wl->fw_status_priv_len, GFP_KERNEL);
++	if (!wl->fw_status_1)
++		return -ENOMEM;
++
++	wl->fw_status_2 = (struct wl_fw_status_2 *)
++				(((u8 *) wl->fw_status_1) +
++				WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc));
++
++	wl->tx_res_if = kmalloc(sizeof(*wl->tx_res_if), GFP_KERNEL);
++	if (!wl->tx_res_if) {
++		kfree(wl->fw_status_1);
++		return -ENOMEM;
++	}
++
++	return 0;
++}
++
++static int wl12xx_set_power_on(struct wl1271 *wl)
++{
++	int ret;
++
++	msleep(WL1271_PRE_POWER_ON_SLEEP);
++	ret = wl1271_power_on(wl);
++	if (ret < 0)
++		goto out;
++	msleep(WL1271_POWER_ON_SLEEP);
++	wl1271_io_reset(wl);
++	wl1271_io_init(wl);
++
++	ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]);
++	if (ret < 0)
++		goto fail;
++
++	/* ELP module wake up */
++	ret = wlcore_fw_wakeup(wl);
++	if (ret < 0)
++		goto fail;
++
++out:
++	return ret;
++
++fail:
++	wl1271_power_off(wl);
++	return ret;
++}
++
++static int wl12xx_chip_wakeup(struct wl1271 *wl, bool plt)
++{
++	int ret = 0;
++
++	ret = wl12xx_set_power_on(wl);
++	if (ret < 0)
++		goto out;
++
++	/*
++	 * For wl127x based devices we could use the default block
++	 * size (512 bytes), but due to a bug in the sdio driver, we
++	 * need to set it explicitly after the chip is powered on.  To
++	 * simplify the code and since the performance impact is
++	 * negligible, we use the same block size for all different
++	 * chip types.
++	 *
++	 * Check if the bus supports blocksize alignment and, if it
++	 * doesn't, make sure we don't have the quirk.
++	 */
++	if (!wl1271_set_block_size(wl))
++		wl->quirks &= ~WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN;
++
++	/* TODO: make sure the lower driver has set things up correctly */
++
++	ret = wl1271_setup(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wl12xx_fetch_firmware(wl, plt);
++	if (ret < 0)
++		goto out;
++
++out:
++	return ret;
++}
++
++int wl1271_plt_start(struct wl1271 *wl, const enum plt_mode plt_mode)
++{
++	int retries = WL1271_BOOT_RETRIES;
++	struct wiphy *wiphy = wl->hw->wiphy;
++
++	static const char* const PLT_MODE[] = {
++		"PLT_OFF",
++		"PLT_ON",
++		"PLT_FEM_DETECT"
++	};
++
++	int ret;
++
++	mutex_lock(&wl->mutex);
++
++	wl1271_notice("power up");
++
++	if (wl->state != WLCORE_STATE_OFF) {
++		wl1271_error("cannot go into PLT state because not "
++			     "in off state: %d", wl->state);
++		ret = -EBUSY;
++		goto out;
++	}
++
++	/* Indicate to lower levels that we are now in PLT mode */
++	wl->plt = true;
++	wl->plt_mode = plt_mode;
++
++	while (retries) {
++		retries--;
++		ret = wl12xx_chip_wakeup(wl, true);
++		if (ret < 0)
++			goto power_off;
++
++		ret = wl->ops->plt_init(wl);
++		if (ret < 0)
++			goto power_off;
++
++		wl->state = WLCORE_STATE_ON;
++		wl1271_notice("firmware booted in PLT mode %s (%s)",
++			      PLT_MODE[plt_mode],
++			      wl->chip.fw_ver_str);
++
++		/* update hw/fw version info in wiphy struct */
++		wiphy->hw_version = wl->chip.id;
++		strncpy(wiphy->fw_version, wl->chip.fw_ver_str,
++			sizeof(wiphy->fw_version));
++
++		goto out;
++
++power_off:
++		wl1271_power_off(wl);
++	}
++
++	wl->plt = false;
++	wl->plt_mode = PLT_OFF;
++
++	wl1271_error("firmware boot in PLT mode failed despite %d retries",
++		     WL1271_BOOT_RETRIES);
++out:
++	mutex_unlock(&wl->mutex);
++
++	return ret;
++}
++
++int wl1271_plt_stop(struct wl1271 *wl)
++{
++	int ret = 0;
++
++	wl1271_notice("power down");
++
++	/*
++	 * Interrupts must be disabled before setting the state to OFF.
++	 * Otherwise, the interrupt handler might be called and exit without
++	 * reading the interrupt status.
++	 */
++	wlcore_disable_interrupts(wl);
++	mutex_lock(&wl->mutex);
++	if (!wl->plt) {
++		mutex_unlock(&wl->mutex);
++
++		/*
++		 * This will not necessarily enable interrupts as interrupts
++		 * may have been disabled when op_stop was called. It will,
++		 * however, balance the above call to disable_interrupts().
++		 */
++		wlcore_enable_interrupts(wl);
++
++		wl1271_error("cannot power down because not in PLT "
++			     "state: %d", wl->state);
++		ret = -EBUSY;
++		goto out;
++	}
++
++	mutex_unlock(&wl->mutex);
++
++	wl1271_flush_deferred_work(wl);
++	cancel_work_sync(&wl->netstack_work);
++	cancel_work_sync(&wl->recovery_work);
++	cancel_delayed_work_sync(&wl->elp_work);
++	cancel_delayed_work_sync(&wl->tx_watchdog_work);
++
++	mutex_lock(&wl->mutex);
++	wl1271_power_off(wl);
++	wl->flags = 0;
++	wl->sleep_auth = WL1271_PSM_ILLEGAL;
++	wl->state = WLCORE_STATE_OFF;
++	wl->plt = false;
++	wl->plt_mode = PLT_OFF;
++	wl->rx_counter = 0;
++	mutex_unlock(&wl->mutex);
++
++out:
++	return ret;
++}
++
++static void wl1271_op_tx(struct ieee80211_hw *hw,
++			 struct ieee80211_tx_control *control,
++			 struct sk_buff *skb)
++{
++	struct wl1271 *wl = hw->priv;
++	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
++	struct ieee80211_vif *vif = info->control.vif;
++	struct wl12xx_vif *wlvif = NULL;
++	unsigned long flags;
++	int q, mapping;
++	u8 hlid;
++
++	if (!vif) {
++		wl1271_debug(DEBUG_TX, "DROP skb with no vif");
++		ieee80211_free_txskb(hw, skb);
++		return;
++	}
++
++	wlvif = wl12xx_vif_to_data(vif);
++	mapping = skb_get_queue_mapping(skb);
++	q = wl1271_tx_get_queue(mapping);
++
++	hlid = wl12xx_tx_get_hlid(wl, wlvif, skb, control->sta);
++
++	spin_lock_irqsave(&wl->wl_lock, flags);
++
++	/*
++	 * drop the packet if the link is invalid or the queue is stopped
++	 * for any reason but watermark. Watermark is a "soft"-stop so we
++	 * allow these packets through.
++	 */
++	if (hlid == WL12XX_INVALID_LINK_ID ||
++	    (!test_bit(hlid, wlvif->links_map)) ||
++	     (wlcore_is_queue_stopped_locked(wl, wlvif, q) &&
++	      !wlcore_is_queue_stopped_by_reason_locked(wl, wlvif, q,
++			WLCORE_QUEUE_STOP_REASON_WATERMARK))) {
++		wl1271_debug(DEBUG_TX, "DROP skb hlid %d q %d", hlid, q);
++		ieee80211_free_txskb(hw, skb);
++		goto out;
++	}
++
++	wl1271_debug(DEBUG_TX, "queue skb hlid %d q %d len %d",
++		     hlid, q, skb->len);
++	skb_queue_tail(&wl->links[hlid].tx_queue[q], skb);
++
++	wl->tx_queue_count[q]++;
++	wlvif->tx_queue_count[q]++;
++
++	/*
++	 * The workqueue is slow to process the tx_queue and we need stop
++	 * the queue here, otherwise the queue will get too long.
++	 */
++	if (wlvif->tx_queue_count[q] >= WL1271_TX_QUEUE_HIGH_WATERMARK &&
++	    !wlcore_is_queue_stopped_by_reason_locked(wl, wlvif, q,
++					WLCORE_QUEUE_STOP_REASON_WATERMARK)) {
++		wl1271_debug(DEBUG_TX, "op_tx: stopping queues for q %d", q);
++		wlcore_stop_queue_locked(wl, wlvif, q,
++					 WLCORE_QUEUE_STOP_REASON_WATERMARK);
++	}
++
++	/*
++	 * The chip specific setup must run before the first TX packet -
++	 * before that, the tx_work will not be initialized!
++	 */
++
++	if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags) &&
++	    !test_bit(WL1271_FLAG_TX_PENDING, &wl->flags))
++		ieee80211_queue_work(wl->hw, &wl->tx_work);
++
++out:
++	spin_unlock_irqrestore(&wl->wl_lock, flags);
++}
++
++int wl1271_tx_dummy_packet(struct wl1271 *wl)
++{
++	unsigned long flags;
++	int q;
++
++	/* no need to queue a new dummy packet if one is already pending */
++	if (test_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags))
++		return 0;
++
++	q = wl1271_tx_get_queue(skb_get_queue_mapping(wl->dummy_packet));
++
++	spin_lock_irqsave(&wl->wl_lock, flags);
++	set_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags);
++	wl->tx_queue_count[q]++;
++	spin_unlock_irqrestore(&wl->wl_lock, flags);
++
++	/* The FW is low on RX memory blocks, so send the dummy packet asap */
++	if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags))
++		return wlcore_tx_work_locked(wl);
++
++	/*
++	 * If the FW TX is busy, TX work will be scheduled by the threaded
++	 * interrupt handler function
++	 */
++	return 0;
++}
++
++/*
++ * The size of the dummy packet should be at least 1400 bytes. However, in
++ * order to minimize the number of bus transactions, aligning it to 512 bytes
++ * boundaries could be beneficial, performance wise
++ */
++#define TOTAL_TX_DUMMY_PACKET_SIZE (ALIGN(1400, 512))
++
++static struct sk_buff *wl12xx_alloc_dummy_packet(struct wl1271 *wl)
++{
++	struct sk_buff *skb;
++	struct ieee80211_hdr_3addr *hdr;
++	unsigned int dummy_packet_size;
++
++	dummy_packet_size = TOTAL_TX_DUMMY_PACKET_SIZE -
++			    sizeof(struct wl1271_tx_hw_descr) - sizeof(*hdr);
++
++	skb = dev_alloc_skb(TOTAL_TX_DUMMY_PACKET_SIZE);
++	if (!skb) {
++		wl1271_warning("Failed to allocate a dummy packet skb");
++		return NULL;
++	}
++
++	skb_reserve(skb, sizeof(struct wl1271_tx_hw_descr));
++
++	hdr = (struct ieee80211_hdr_3addr *) skb_put(skb, sizeof(*hdr));
++	memset(hdr, 0, sizeof(*hdr));
++	hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
++					 IEEE80211_STYPE_NULLFUNC |
++					 IEEE80211_FCTL_TODS);
++
++	memset(skb_put(skb, dummy_packet_size), 0, dummy_packet_size);
++
++	/* Dummy packets require the TID to be management */
++	skb->priority = WL1271_TID_MGMT;
++
++	/* Initialize all fields that might be used */
++	skb_set_queue_mapping(skb, 0);
++	memset(IEEE80211_SKB_CB(skb), 0, sizeof(struct ieee80211_tx_info));
++
++	return skb;
++}
++
++
++#ifdef CONFIG_PM
++static int
++wl1271_validate_wowlan_pattern(struct cfg80211_wowlan_trig_pkt_pattern *p)
++{
++	int num_fields = 0, in_field = 0, fields_size = 0;
++	int i, pattern_len = 0;
++
++	if (!p->mask) {
++		wl1271_warning("No mask in WoWLAN pattern");
++		return -EINVAL;
++	}
++
++	/*
++	 * The pattern is broken up into segments of bytes at different offsets
++	 * that need to be checked by the FW filter. Each segment is called
++	 * a field in the FW API. We verify that the total number of fields
++	 * required for this pattern won't exceed FW limits (8)
++	 * as well as the total fields buffer won't exceed the FW limit.
++	 * Note that if there's a pattern which crosses Ethernet/IP header
++	 * boundary a new field is required.
++	 */
++	for (i = 0; i < p->pattern_len; i++) {
++		if (test_bit(i, (unsigned long *)p->mask)) {
++			if (!in_field) {
++				in_field = 1;
++				pattern_len = 1;
++			} else {
++				if (i == WL1271_RX_FILTER_ETH_HEADER_SIZE) {
++					num_fields++;
++					fields_size += pattern_len +
++						RX_FILTER_FIELD_OVERHEAD;
++					pattern_len = 1;
++				} else
++					pattern_len++;
++			}
++		} else {
++			if (in_field) {
++				in_field = 0;
++				fields_size += pattern_len +
++					RX_FILTER_FIELD_OVERHEAD;
++				num_fields++;
++			}
++		}
++	}
++
++	if (in_field) {
++		fields_size += pattern_len + RX_FILTER_FIELD_OVERHEAD;
++		num_fields++;
++	}
++
++	if (num_fields > WL1271_RX_FILTER_MAX_FIELDS) {
++		wl1271_warning("RX Filter too complex. Too many segments");
++		return -EINVAL;
++	}
++
++	if (fields_size > WL1271_RX_FILTER_MAX_FIELDS_SIZE) {
++		wl1271_warning("RX filter pattern is too big");
++		return -E2BIG;
++	}
++
++	return 0;
++}
++
++struct wl12xx_rx_filter *wl1271_rx_filter_alloc(void)
++{
++	return kzalloc(sizeof(struct wl12xx_rx_filter), GFP_KERNEL);
++}
++
++void wl1271_rx_filter_free(struct wl12xx_rx_filter *filter)
++{
++	int i;
++
++	if (filter == NULL)
++		return;
++
++	for (i = 0; i < filter->num_fields; i++)
++		kfree(filter->fields[i].pattern);
++
++	kfree(filter);
++}
++
++int wl1271_rx_filter_alloc_field(struct wl12xx_rx_filter *filter,
++				 u16 offset, u8 flags,
++				 u8 *pattern, u8 len)
++{
++	struct wl12xx_rx_filter_field *field;
++
++	if (filter->num_fields == WL1271_RX_FILTER_MAX_FIELDS) {
++		wl1271_warning("Max fields per RX filter. can't alloc another");
++		return -EINVAL;
++	}
++
++	field = &filter->fields[filter->num_fields];
++
++	field->pattern = kzalloc(len, GFP_KERNEL);
++	if (!field->pattern) {
++		wl1271_warning("Failed to allocate RX filter pattern");
++		return -ENOMEM;
++	}
++
++	filter->num_fields++;
++
++	field->offset = cpu_to_le16(offset);
++	field->flags = flags;
++	field->len = len;
++	memcpy(field->pattern, pattern, len);
++
++	return 0;
++}
++
++int wl1271_rx_filter_get_fields_size(struct wl12xx_rx_filter *filter)
++{
++	int i, fields_size = 0;
++
++	for (i = 0; i < filter->num_fields; i++)
++		fields_size += filter->fields[i].len +
++			sizeof(struct wl12xx_rx_filter_field) -
++			sizeof(u8 *);
++
++	return fields_size;
++}
++
++void wl1271_rx_filter_flatten_fields(struct wl12xx_rx_filter *filter,
++				    u8 *buf)
++{
++	int i;
++	struct wl12xx_rx_filter_field *field;
++
++	for (i = 0; i < filter->num_fields; i++) {
++		field = (struct wl12xx_rx_filter_field *)buf;
++
++		field->offset = filter->fields[i].offset;
++		field->flags = filter->fields[i].flags;
++		field->len = filter->fields[i].len;
++
++		memcpy(&field->pattern, filter->fields[i].pattern, field->len);
++		buf += sizeof(struct wl12xx_rx_filter_field) -
++			sizeof(u8 *) + field->len;
++	}
++}
++
++/*
++ * Allocates an RX filter returned through f
++ * which needs to be freed using rx_filter_free()
++ */
++static int wl1271_convert_wowlan_pattern_to_rx_filter(
++	struct cfg80211_wowlan_trig_pkt_pattern *p,
++	struct wl12xx_rx_filter **f)
++{
++	int i, j, ret = 0;
++	struct wl12xx_rx_filter *filter;
++	u16 offset;
++	u8 flags, len;
++
++	filter = wl1271_rx_filter_alloc();
++	if (!filter) {
++		wl1271_warning("Failed to alloc rx filter");
++		ret = -ENOMEM;
++		goto err;
++	}
++
++	i = 0;
++	while (i < p->pattern_len) {
++		if (!test_bit(i, (unsigned long *)p->mask)) {
++			i++;
++			continue;
++		}
++
++		for (j = i; j < p->pattern_len; j++) {
++			if (!test_bit(j, (unsigned long *)p->mask))
++				break;
++
++			if (i < WL1271_RX_FILTER_ETH_HEADER_SIZE &&
++			    j >= WL1271_RX_FILTER_ETH_HEADER_SIZE)
++				break;
++		}
++
++		if (i < WL1271_RX_FILTER_ETH_HEADER_SIZE) {
++			offset = i;
++			flags = WL1271_RX_FILTER_FLAG_ETHERNET_HEADER;
++		} else {
++			offset = i - WL1271_RX_FILTER_ETH_HEADER_SIZE;
++			flags = WL1271_RX_FILTER_FLAG_IP_HEADER;
++		}
++
++		len = j - i;
++
++		ret = wl1271_rx_filter_alloc_field(filter,
++						   offset,
++						   flags,
++						   &p->pattern[i], len);
++		if (ret)
++			goto err;
++
++		i = j;
++	}
++
++	filter->action = FILTER_SIGNAL;
++
++	*f = filter;
++	return 0;
++
++err:
++	wl1271_rx_filter_free(filter);
++	*f = NULL;
++
++	return ret;
++}
++
++static int wl1271_configure_wowlan(struct wl1271 *wl,
++				   struct cfg80211_wowlan *wow)
++{
++	int i, ret;
++
++	if (!wow || wow->any || !wow->n_patterns) {
++		ret = wl1271_acx_default_rx_filter_enable(wl, 0,
++							  FILTER_SIGNAL);
++		if (ret)
++			goto out;
++
++		ret = wl1271_rx_filter_clear_all(wl);
++		if (ret)
++			goto out;
++
++		return 0;
++	}
++
++	if (WARN_ON(wow->n_patterns > WL1271_MAX_RX_FILTERS))
++		return -EINVAL;
++
++	/* Validate all incoming patterns before clearing current FW state */
++	for (i = 0; i < wow->n_patterns; i++) {
++		ret = wl1271_validate_wowlan_pattern(&wow->patterns[i]);
++		if (ret) {
++			wl1271_warning("Bad wowlan pattern %d", i);
++			return ret;
++		}
++	}
++
++	ret = wl1271_acx_default_rx_filter_enable(wl, 0, FILTER_SIGNAL);
++	if (ret)
++		goto out;
++
++	ret = wl1271_rx_filter_clear_all(wl);
++	if (ret)
++		goto out;
++
++	/* Translate WoWLAN patterns into filters */
++	for (i = 0; i < wow->n_patterns; i++) {
++		struct cfg80211_wowlan_trig_pkt_pattern *p;
++		struct wl12xx_rx_filter *filter = NULL;
++
++		p = &wow->patterns[i];
++
++		ret = wl1271_convert_wowlan_pattern_to_rx_filter(p, &filter);
++		if (ret) {
++			wl1271_warning("Failed to create an RX filter from "
++				       "wowlan pattern %d", i);
++			goto out;
++		}
++
++		ret = wl1271_rx_filter_enable(wl, i, 1, filter);
++
++		wl1271_rx_filter_free(filter);
++		if (ret)
++			goto out;
++	}
++
++	ret = wl1271_acx_default_rx_filter_enable(wl, 1, FILTER_DROP);
++
++out:
++	return ret;
++}
++
++static int wl1271_configure_suspend_sta(struct wl1271 *wl,
++					struct wl12xx_vif *wlvif,
++					struct cfg80211_wowlan *wow)
++{
++	int ret = 0;
++
++	if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wl1271_configure_wowlan(wl, wow);
++	if (ret < 0)
++		goto out_sleep;
++
++	if ((wl->conf.conn.suspend_wake_up_event ==
++	     wl->conf.conn.wake_up_event) &&
++	    (wl->conf.conn.suspend_listen_interval ==
++	     wl->conf.conn.listen_interval))
++		goto out_sleep;
++
++	ret = wl1271_acx_wake_up_conditions(wl, wlvif,
++				    wl->conf.conn.suspend_wake_up_event,
++				    wl->conf.conn.suspend_listen_interval);
++
++	if (ret < 0)
++		wl1271_error("suspend: set wake up conditions failed: %d", ret);
++
++out_sleep:
++	wl1271_ps_elp_sleep(wl);
++out:
++	return ret;
++
++}
++
++static int wl1271_configure_suspend_ap(struct wl1271 *wl,
++				       struct wl12xx_vif *wlvif)
++{
++	int ret = 0;
++
++	if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true);
++
++	wl1271_ps_elp_sleep(wl);
++out:
++	return ret;
++
++}
++
++static int wl1271_configure_suspend(struct wl1271 *wl,
++				    struct wl12xx_vif *wlvif,
++				    struct cfg80211_wowlan *wow)
++{
++	if (wlvif->bss_type == BSS_TYPE_STA_BSS)
++		return wl1271_configure_suspend_sta(wl, wlvif, wow);
++	if (wlvif->bss_type == BSS_TYPE_AP_BSS)
++		return wl1271_configure_suspend_ap(wl, wlvif);
++	return 0;
++}
++
++static void wl1271_configure_resume(struct wl1271 *wl,
++				    struct wl12xx_vif *wlvif)
++{
++	int ret = 0;
++	bool is_ap = wlvif->bss_type == BSS_TYPE_AP_BSS;
++	bool is_sta = wlvif->bss_type == BSS_TYPE_STA_BSS;
++
++	if ((!is_ap) && (!is_sta))
++		return;
++
++	if (is_sta && !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
++		return;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		return;
++
++	if (is_sta) {
++		wl1271_configure_wowlan(wl, NULL);
++
++		if ((wl->conf.conn.suspend_wake_up_event ==
++		     wl->conf.conn.wake_up_event) &&
++		    (wl->conf.conn.suspend_listen_interval ==
++		     wl->conf.conn.listen_interval))
++			goto out_sleep;
++
++		ret = wl1271_acx_wake_up_conditions(wl, wlvif,
++				    wl->conf.conn.wake_up_event,
++				    wl->conf.conn.listen_interval);
++
++		if (ret < 0)
++			wl1271_error("resume: wake up conditions failed: %d",
++				     ret);
++
++	} else if (is_ap) {
++		ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false);
++	}
++
++out_sleep:
++	wl1271_ps_elp_sleep(wl);
++}
++
++static int wl1271_op_suspend(struct ieee80211_hw *hw,
++			    struct cfg80211_wowlan *wow)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif;
++	int ret;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 suspend wow=%d", !!wow);
++	WARN_ON(!wow);
++
++	/* we want to perform the recovery before suspending */
++	if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) {
++		wl1271_warning("postponing suspend to perform recovery");
++		return -EBUSY;
++	}
++
++	wl1271_tx_flush(wl);
++
++	mutex_lock(&wl->mutex);
++	wl->wow_enabled = true;
++	wl12xx_for_each_wlvif(wl, wlvif) {
++		ret = wl1271_configure_suspend(wl, wlvif, wow);
++		if (ret < 0) {
++			mutex_unlock(&wl->mutex);
++			wl1271_warning("couldn't prepare device to suspend");
++			return ret;
++		}
++	}
++	mutex_unlock(&wl->mutex);
++	/* flush any remaining work */
++	wl1271_debug(DEBUG_MAC80211, "flushing remaining works");
++
++	/*
++	 * disable and re-enable interrupts in order to flush
++	 * the threaded_irq
++	 */
++	wlcore_disable_interrupts(wl);
++
++	/*
++	 * set suspended flag to avoid triggering a new threaded_irq
++	 * work. no need for spinlock as interrupts are disabled.
++	 */
++	set_bit(WL1271_FLAG_SUSPENDED, &wl->flags);
++
++	wlcore_enable_interrupts(wl);
++	flush_work(&wl->tx_work);
++	flush_delayed_work(&wl->elp_work);
++
++	return 0;
++}
++
++static int wl1271_op_resume(struct ieee80211_hw *hw)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif;
++	unsigned long flags;
++	bool run_irq_work = false, pending_recovery;
++	int ret;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 resume wow=%d",
++		     wl->wow_enabled);
++	WARN_ON(!wl->wow_enabled);
++
++	/*
++	 * re-enable irq_work enqueuing, and call irq_work directly if
++	 * there is a pending work.
++	 */
++	spin_lock_irqsave(&wl->wl_lock, flags);
++	clear_bit(WL1271_FLAG_SUSPENDED, &wl->flags);
++	if (test_and_clear_bit(WL1271_FLAG_PENDING_WORK, &wl->flags))
++		run_irq_work = true;
++	spin_unlock_irqrestore(&wl->wl_lock, flags);
++
++	mutex_lock(&wl->mutex);
++
++	/* test the recovery flag before calling any SDIO functions */
++	pending_recovery = test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS,
++				    &wl->flags);
++
++	if (run_irq_work) {
++		wl1271_debug(DEBUG_MAC80211,
++			     "run postponed irq_work directly");
++
++		/* don't talk to the HW if recovery is pending */
++		if (!pending_recovery) {
++			ret = wlcore_irq_locked(wl);
++			if (ret)
++				wl12xx_queue_recovery_work(wl);
++		}
++
++		wlcore_enable_interrupts(wl);
++	}
++
++	if (pending_recovery) {
++		wl1271_warning("queuing forgotten recovery on resume");
++		ieee80211_queue_work(wl->hw, &wl->recovery_work);
++		goto out;
++	}
++
++	wl12xx_for_each_wlvif(wl, wlvif) {
++		wl1271_configure_resume(wl, wlvif);
++	}
++
++out:
++	wl->wow_enabled = false;
++	mutex_unlock(&wl->mutex);
++
++	return 0;
++}
++#endif
++
++static int wl1271_op_start(struct ieee80211_hw *hw)
++{
++	wl1271_debug(DEBUG_MAC80211, "mac80211 start");
++
++	/*
++	 * We have to delay the booting of the hardware because
++	 * we need to know the local MAC address before downloading and
++	 * initializing the firmware. The MAC address cannot be changed
++	 * after boot, and without the proper MAC address, the firmware
++	 * will not function properly.
++	 *
++	 * The MAC address is first known when the corresponding interface
++	 * is added. That is where we will initialize the hardware.
++	 */
++
++	return 0;
++}
++
++static void wlcore_op_stop_locked(struct wl1271 *wl)
++{
++	int i;
++
++	if (wl->state == WLCORE_STATE_OFF) {
++		if (test_and_clear_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS,
++					&wl->flags))
++			wlcore_enable_interrupts(wl);
++
++		return;
++	}
++
++	/*
++	 * this must be before the cancel_work calls below, so that the work
++	 * functions don't perform further work.
++	 */
++	wl->state = WLCORE_STATE_OFF;
++
++	/*
++	 * Use the nosync variant to disable interrupts, so the mutex could be
++	 * held while doing so without deadlocking.
++	 */
++	wlcore_disable_interrupts_nosync(wl);
++
++	mutex_unlock(&wl->mutex);
++
++	wlcore_synchronize_interrupts(wl);
++	if (!test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags))
++		cancel_work_sync(&wl->recovery_work);
++	wl1271_flush_deferred_work(wl);
++	cancel_delayed_work_sync(&wl->scan_complete_work);
++	cancel_work_sync(&wl->netstack_work);
++	cancel_work_sync(&wl->tx_work);
++	cancel_delayed_work_sync(&wl->elp_work);
++	cancel_delayed_work_sync(&wl->tx_watchdog_work);
++
++	/* let's notify MAC80211 about the remaining pending TX frames */
++	mutex_lock(&wl->mutex);
++	wl12xx_tx_reset(wl);
++
++	wl1271_power_off(wl);
++	/*
++	 * In case a recovery was scheduled, interrupts were disabled to avoid
++	 * an interrupt storm. Now that the power is down, it is safe to
++	 * re-enable interrupts to balance the disable depth
++	 */
++	if (test_and_clear_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags))
++		wlcore_enable_interrupts(wl);
++
++	wl->band = IEEE80211_BAND_2GHZ;
++
++	wl->rx_counter = 0;
++	wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
++	wl->channel_type = NL80211_CHAN_NO_HT;
++	wl->tx_blocks_available = 0;
++	wl->tx_allocated_blocks = 0;
++	wl->tx_results_count = 0;
++	wl->tx_packets_count = 0;
++	wl->time_offset = 0;
++	wl->ap_fw_ps_map = 0;
++	wl->ap_ps_map = 0;
++	wl->sleep_auth = WL1271_PSM_ILLEGAL;
++	memset(wl->roles_map, 0, sizeof(wl->roles_map));
++	memset(wl->links_map, 0, sizeof(wl->links_map));
++	memset(wl->roc_map, 0, sizeof(wl->roc_map));
++	memset(wl->session_ids, 0, sizeof(wl->session_ids));
++	wl->active_sta_count = 0;
++	wl->active_link_count = 0;
++
++	/* The system link is always allocated */
++	wl->links[WL12XX_SYSTEM_HLID].allocated_pkts = 0;
++	wl->links[WL12XX_SYSTEM_HLID].prev_freed_pkts = 0;
++	__set_bit(WL12XX_SYSTEM_HLID, wl->links_map);
++
++	/*
++	 * this is performed after the cancel_work calls and the associated
++	 * mutex_lock, so that wl1271_op_add_interface does not accidentally
++	 * get executed before all these vars have been reset.
++	 */
++	wl->flags = 0;
++
++	wl->tx_blocks_freed = 0;
++
++	for (i = 0; i < NUM_TX_QUEUES; i++) {
++		wl->tx_pkts_freed[i] = 0;
++		wl->tx_allocated_pkts[i] = 0;
++	}
++
++	wl1271_debugfs_reset(wl);
++
++	kfree(wl->fw_status_1);
++	wl->fw_status_1 = NULL;
++	wl->fw_status_2 = NULL;
++	kfree(wl->tx_res_if);
++	wl->tx_res_if = NULL;
++	kfree(wl->target_mem_map);
++	wl->target_mem_map = NULL;
++
++	/*
++	 * FW channels must be re-calibrated after recovery,
++	 * clear the last Reg-Domain channel configuration.
++	 */
++	memset(wl->reg_ch_conf_last, 0, sizeof(wl->reg_ch_conf_last));
++}
++
++static void wlcore_op_stop(struct ieee80211_hw *hw)
++{
++	struct wl1271 *wl = hw->priv;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 stop");
++
++	mutex_lock(&wl->mutex);
++
++	wlcore_op_stop_locked(wl);
++
++	mutex_unlock(&wl->mutex);
++}
++
++static void wlcore_channel_switch_work(struct work_struct *work)
++{
++	struct delayed_work *dwork;
++	struct wl1271 *wl;
++	struct ieee80211_vif *vif;
++	struct wl12xx_vif *wlvif;
++	int ret;
++
++	dwork = container_of(work, struct delayed_work, work);
++	wlvif = container_of(dwork, struct wl12xx_vif, channel_switch_work);
++	wl = wlvif->wl;
++
++	wl1271_info("channel switch failed (role_id: %d).", wlvif->role_id);
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	/* check the channel switch is still ongoing */
++	if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags))
++		goto out;
++
++	vif = wl12xx_wlvif_to_vif(wlvif);
++	ieee80211_chswitch_done(vif, false);
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	wl12xx_cmd_stop_channel_switch(wl, wlvif);
++
++	wl1271_ps_elp_sleep(wl);
++out:
++	mutex_unlock(&wl->mutex);
++}
++
++static void wlcore_connection_loss_work(struct work_struct *work)
++{
++	struct delayed_work *dwork;
++	struct wl1271 *wl;
++	struct ieee80211_vif *vif;
++	struct wl12xx_vif *wlvif;
++
++	dwork = container_of(work, struct delayed_work, work);
++	wlvif = container_of(dwork, struct wl12xx_vif, connection_loss_work);
++	wl = wlvif->wl;
++
++	wl1271_info("Connection loss work (role_id: %d).", wlvif->role_id);
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	/* Call mac80211 connection loss */
++	if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
++		goto out;
++
++	vif = wl12xx_wlvif_to_vif(wlvif);
++	ieee80211_connection_loss(vif);
++out:
++	mutex_unlock(&wl->mutex);
++}
++
++static int wl12xx_allocate_rate_policy(struct wl1271 *wl, u8 *idx)
++{
++	u8 policy = find_first_zero_bit(wl->rate_policies_map,
++					WL12XX_MAX_RATE_POLICIES);
++	if (policy >= WL12XX_MAX_RATE_POLICIES)
++		return -EBUSY;
++
++	__set_bit(policy, wl->rate_policies_map);
++	*idx = policy;
++	return 0;
++}
++
++static void wl12xx_free_rate_policy(struct wl1271 *wl, u8 *idx)
++{
++	if (WARN_ON(*idx >= WL12XX_MAX_RATE_POLICIES))
++		return;
++
++	__clear_bit(*idx, wl->rate_policies_map);
++	*idx = WL12XX_MAX_RATE_POLICIES;
++}
++
++static int wlcore_allocate_klv_template(struct wl1271 *wl, u8 *idx)
++{
++	u8 policy = find_first_zero_bit(wl->klv_templates_map,
++					WLCORE_MAX_KLV_TEMPLATES);
++	if (policy >= WLCORE_MAX_KLV_TEMPLATES)
++		return -EBUSY;
++
++	__set_bit(policy, wl->klv_templates_map);
++	*idx = policy;
++	return 0;
++}
++
++static void wlcore_free_klv_template(struct wl1271 *wl, u8 *idx)
++{
++	if (WARN_ON(*idx >= WLCORE_MAX_KLV_TEMPLATES))
++		return;
++
++	__clear_bit(*idx, wl->klv_templates_map);
++	*idx = WLCORE_MAX_KLV_TEMPLATES;
++}
++
++static u8 wl12xx_get_role_type(struct wl1271 *wl, struct wl12xx_vif *wlvif)
++{
++	switch (wlvif->bss_type) {
++	case BSS_TYPE_AP_BSS:
++		if (wlvif->p2p)
++			return WL1271_ROLE_P2P_GO;
++		else
++			return WL1271_ROLE_AP;
++
++	case BSS_TYPE_STA_BSS:
++		if (wlvif->p2p)
++			return WL1271_ROLE_P2P_CL;
++		else
++			return WL1271_ROLE_STA;
++
++	case BSS_TYPE_IBSS:
++		return WL1271_ROLE_IBSS;
++
++	default:
++		wl1271_error("invalid bss_type: %d", wlvif->bss_type);
++	}
++	return WL12XX_INVALID_ROLE_TYPE;
++}
++
++static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
++{
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	int i;
++
++	/* clear everything but the persistent data */
++	memset(wlvif, 0, offsetof(struct wl12xx_vif, persistent));
++
++	switch (ieee80211_vif_type_p2p(vif)) {
++	case NL80211_IFTYPE_P2P_CLIENT:
++		wlvif->p2p = 1;
++		/* fall-through */
++	case NL80211_IFTYPE_STATION:
++		wlvif->bss_type = BSS_TYPE_STA_BSS;
++		break;
++	case NL80211_IFTYPE_ADHOC:
++		wlvif->bss_type = BSS_TYPE_IBSS;
++		break;
++	case NL80211_IFTYPE_P2P_GO:
++		wlvif->p2p = 1;
++		/* fall-through */
++	case NL80211_IFTYPE_AP:
++		wlvif->bss_type = BSS_TYPE_AP_BSS;
++		break;
++	default:
++		wlvif->bss_type = MAX_BSS_TYPE;
++		return -EOPNOTSUPP;
++	}
++
++	wlvif->role_id = WL12XX_INVALID_ROLE_ID;
++	wlvif->dev_role_id = WL12XX_INVALID_ROLE_ID;
++	wlvif->dev_hlid = WL12XX_INVALID_LINK_ID;
++
++	if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
++	    wlvif->bss_type == BSS_TYPE_IBSS) {
++		/* init sta/ibss data */
++		wlvif->sta.hlid = WL12XX_INVALID_LINK_ID;
++		wl12xx_allocate_rate_policy(wl, &wlvif->sta.basic_rate_idx);
++		wl12xx_allocate_rate_policy(wl, &wlvif->sta.ap_rate_idx);
++		wl12xx_allocate_rate_policy(wl, &wlvif->sta.p2p_rate_idx);
++		wlcore_allocate_klv_template(wl, &wlvif->sta.klv_template_id);
++		wlvif->basic_rate_set = CONF_TX_RATE_MASK_BASIC;
++		wlvif->basic_rate = CONF_TX_RATE_MASK_BASIC;
++		wlvif->rate_set = CONF_TX_RATE_MASK_BASIC;
++	} else {
++		/* init ap data */
++		wlvif->ap.bcast_hlid = WL12XX_INVALID_LINK_ID;
++		wlvif->ap.global_hlid = WL12XX_INVALID_LINK_ID;
++		wl12xx_allocate_rate_policy(wl, &wlvif->ap.mgmt_rate_idx);
++		wl12xx_allocate_rate_policy(wl, &wlvif->ap.bcast_rate_idx);
++		for (i = 0; i < CONF_TX_MAX_AC_COUNT; i++)
++			wl12xx_allocate_rate_policy(wl,
++						&wlvif->ap.ucast_rate_idx[i]);
++		wlvif->basic_rate_set = CONF_TX_ENABLED_RATES;
++		/*
++		 * TODO: check if basic_rate shouldn't be
++		 * wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
++		 * instead (the same thing for STA above).
++		*/
++		wlvif->basic_rate = CONF_TX_ENABLED_RATES;
++		/* TODO: this seems to be used only for STA, check it */
++		wlvif->rate_set = CONF_TX_ENABLED_RATES;
++	}
++
++	wlvif->bitrate_masks[IEEE80211_BAND_2GHZ] = wl->conf.tx.basic_rate;
++	wlvif->bitrate_masks[IEEE80211_BAND_5GHZ] = wl->conf.tx.basic_rate_5;
++	wlvif->beacon_int = WL1271_DEFAULT_BEACON_INT;
++
++	/*
++	 * mac80211 configures some values globally, while we treat them
++	 * per-interface. thus, on init, we have to copy them from wl
++	 */
++	wlvif->band = wl->band;
++	wlvif->channel = wl->channel;
++	wlvif->power_level = wl->power_level;
++	wlvif->channel_type = wl->channel_type;
++
++	INIT_WORK(&wlvif->rx_streaming_enable_work,
++		  wl1271_rx_streaming_enable_work);
++	INIT_WORK(&wlvif->rx_streaming_disable_work,
++		  wl1271_rx_streaming_disable_work);
++	INIT_DELAYED_WORK(&wlvif->channel_switch_work,
++			  wlcore_channel_switch_work);
++	INIT_DELAYED_WORK(&wlvif->connection_loss_work,
++			  wlcore_connection_loss_work);
++	INIT_LIST_HEAD(&wlvif->list);
++
++	setup_timer(&wlvif->rx_streaming_timer, wl1271_rx_streaming_timer,
++		    (unsigned long) wlvif);
++	return 0;
++}
++
++static int wl12xx_init_fw(struct wl1271 *wl)
++{
++	int retries = WL1271_BOOT_RETRIES;
++	bool booted = false;
++	struct wiphy *wiphy = wl->hw->wiphy;
++	int ret;
++
++	while (retries) {
++		retries--;
++		ret = wl12xx_chip_wakeup(wl, false);
++		if (ret < 0)
++			goto power_off;
++
++		ret = wl->ops->boot(wl);
++		if (ret < 0)
++			goto power_off;
++
++		ret = wl1271_hw_init(wl);
++		if (ret < 0)
++			goto irq_disable;
++
++		booted = true;
++		break;
++
++irq_disable:
++		mutex_unlock(&wl->mutex);
++		/* Unlocking the mutex in the middle of handling is
++		   inherently unsafe. In this case we deem it safe to do,
++		   because we need to let any possibly pending IRQ out of
++		   the system (and while we are WLCORE_STATE_OFF the IRQ
++		   work function will not do anything.) Also, any other
++		   possible concurrent operations will fail due to the
++		   current state, hence the wl1271 struct should be safe. */
++		wlcore_disable_interrupts(wl);
++		wl1271_flush_deferred_work(wl);
++		cancel_work_sync(&wl->netstack_work);
++		mutex_lock(&wl->mutex);
++power_off:
++		wl1271_power_off(wl);
++	}
++
++	if (!booted) {
++		wl1271_error("firmware boot failed despite %d retries",
++			     WL1271_BOOT_RETRIES);
++		goto out;
++	}
++
++	wl1271_info("firmware booted (%s)", wl->chip.fw_ver_str);
++
++	/* update hw/fw version info in wiphy struct */
++	wiphy->hw_version = wl->chip.id;
++	strncpy(wiphy->fw_version, wl->chip.fw_ver_str,
++		sizeof(wiphy->fw_version));
++
++	/*
++	 * Now we know if 11a is supported (info from the NVS), so disable
++	 * 11a channels if not supported
++	 */
++	if (!wl->enable_11a)
++		wiphy->bands[IEEE80211_BAND_5GHZ]->n_channels = 0;
++
++	wl1271_debug(DEBUG_MAC80211, "11a is %ssupported",
++		     wl->enable_11a ? "" : "not ");
++
++	wl->state = WLCORE_STATE_ON;
++out:
++	return ret;
++}
++
++static bool wl12xx_dev_role_started(struct wl12xx_vif *wlvif)
++{
++	return wlvif->dev_hlid != WL12XX_INVALID_LINK_ID;
++}
++
++/*
++ * Check whether a fw switch (i.e. moving from one loaded
++ * fw to another) is needed. This function is also responsible
++ * for updating wl->last_vif_count, so it must be called before
++ * loading a non-plt fw (so the correct fw (single-role/multi-role)
++ * will be used).
++ */
++static bool wl12xx_need_fw_change(struct wl1271 *wl,
++				  struct vif_counter_data vif_counter_data,
++				  bool add)
++{
++	enum wl12xx_fw_type current_fw = wl->fw_type;
++	u8 vif_count = vif_counter_data.counter;
++
++	if (test_bit(WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, &wl->flags))
++		return false;
++
++	/* increase the vif count if this is a new vif */
++	if (add && !vif_counter_data.cur_vif_running)
++		vif_count++;
++
++	wl->last_vif_count = vif_count;
++
++	/* no need for fw change if the device is OFF */
++	if (wl->state == WLCORE_STATE_OFF)
++		return false;
++
++	/* no need for fw change if a single fw is used */
++	if (!wl->mr_fw_name)
++		return false;
++
++	if (vif_count > 1 && current_fw == WL12XX_FW_TYPE_NORMAL)
++		return true;
++	if (vif_count <= 1 && current_fw == WL12XX_FW_TYPE_MULTI)
++		return true;
++
++	return false;
++}
++
++/*
++ * Enter "forced psm". Make sure the sta is in psm against the ap,
++ * to make the fw switch a bit more disconnection-persistent.
++ */
++static void wl12xx_force_active_psm(struct wl1271 *wl)
++{
++	struct wl12xx_vif *wlvif;
++
++	wl12xx_for_each_wlvif_sta(wl, wlvif) {
++		wl1271_ps_set_mode(wl, wlvif, STATION_POWER_SAVE_MODE);
++	}
++}
++
++struct wlcore_hw_queue_iter_data {
++	unsigned long hw_queue_map[BITS_TO_LONGS(WLCORE_NUM_MAC_ADDRESSES)];
++	/* current vif */
++	struct ieee80211_vif *vif;
++	/* is the current vif among those iterated */
++	bool cur_running;
++};
++
++static void wlcore_hw_queue_iter(void *data, u8 *mac,
++				 struct ieee80211_vif *vif)
++{
++	struct wlcore_hw_queue_iter_data *iter_data = data;
++
++	if (WARN_ON_ONCE(vif->hw_queue[0] == IEEE80211_INVAL_HW_QUEUE))
++		return;
++
++	if (iter_data->cur_running || vif == iter_data->vif) {
++		iter_data->cur_running = true;
++		return;
++	}
++
++	__set_bit(vif->hw_queue[0] / NUM_TX_QUEUES, iter_data->hw_queue_map);
++}
++
++static int wlcore_allocate_hw_queue_base(struct wl1271 *wl,
++					 struct wl12xx_vif *wlvif)
++{
++	struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
++	struct wlcore_hw_queue_iter_data iter_data = {};
++	int i, q_base;
++
++	iter_data.vif = vif;
++
++	/* mark all bits taken by active interfaces */
++	ieee80211_iterate_active_interfaces_atomic(wl->hw,
++					IEEE80211_IFACE_ITER_RESUME_ALL,
++					wlcore_hw_queue_iter, &iter_data);
++
++	/* the current vif is already running in mac80211 (resume/recovery) */
++	if (iter_data.cur_running) {
++		wlvif->hw_queue_base = vif->hw_queue[0];
++		wl1271_debug(DEBUG_MAC80211,
++			     "using pre-allocated hw queue base %d",
++			     wlvif->hw_queue_base);
++
++		/* interface type might have changed type */
++		goto adjust_cab_queue;
++	}
++
++	q_base = find_first_zero_bit(iter_data.hw_queue_map,
++				     WLCORE_NUM_MAC_ADDRESSES);
++	if (q_base >= WLCORE_NUM_MAC_ADDRESSES)
++		return -EBUSY;
++
++	wlvif->hw_queue_base = q_base * NUM_TX_QUEUES;
++	wl1271_debug(DEBUG_MAC80211, "allocating hw queue base: %d",
++		     wlvif->hw_queue_base);
++
++	for (i = 0; i < NUM_TX_QUEUES; i++) {
++		wl->queue_stop_reasons[wlvif->hw_queue_base + i] = 0;
++		/* register hw queues in mac80211 */
++		vif->hw_queue[i] = wlvif->hw_queue_base + i;
++	}
++
++adjust_cab_queue:
++	/* the last places are reserved for cab queues per interface */
++	if (wlvif->bss_type == BSS_TYPE_AP_BSS)
++		vif->cab_queue = NUM_TX_QUEUES * WLCORE_NUM_MAC_ADDRESSES +
++				 wlvif->hw_queue_base / NUM_TX_QUEUES;
++	else
++		vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
++
++	return 0;
++}
++
++static int wl1271_op_add_interface(struct ieee80211_hw *hw,
++				   struct ieee80211_vif *vif)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	struct vif_counter_data vif_count;
++	int ret = 0;
++	u8 role_type;
++
++	vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
++			     IEEE80211_VIF_SUPPORTS_CQM_RSSI;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
++		     ieee80211_vif_type_p2p(vif), vif->addr);
++
++	wl12xx_get_vif_count(hw, vif, &vif_count);
++
++	mutex_lock(&wl->mutex);
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out_unlock;
++
++	/*
++	 * in some very corner case HW recovery scenarios its possible to
++	 * get here before __wl1271_op_remove_interface is complete, so
++	 * opt out if that is the case.
++	 */
++	if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags) ||
++	    test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)) {
++		ret = -EBUSY;
++		goto out;
++	}
++
++
++	ret = wl12xx_init_vif_data(wl, vif);
++	if (ret < 0)
++		goto out;
++
++	wlvif->wl = wl;
++	role_type = wl12xx_get_role_type(wl, wlvif);
++	if (role_type == WL12XX_INVALID_ROLE_TYPE) {
++		ret = -EINVAL;
++		goto out;
++	}
++
++	ret = wlcore_allocate_hw_queue_base(wl, wlvif);
++	if (ret < 0)
++		goto out;
++
++	if (wl12xx_need_fw_change(wl, vif_count, true)) {
++		wl12xx_force_active_psm(wl);
++		set_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags);
++		mutex_unlock(&wl->mutex);
++		wl1271_recovery_work(&wl->recovery_work);
++		return 0;
++	}
++
++	/*
++	 * TODO: after the nvs issue will be solved, move this block
++	 * to start(), and make sure here the driver is ON.
++	 */
++	if (wl->state == WLCORE_STATE_OFF) {
++		/*
++		 * we still need this in order to configure the fw
++		 * while uploading the nvs
++		 */
++		memcpy(wl->addresses[0].addr, vif->addr, ETH_ALEN);
++
++		ret = wl12xx_init_fw(wl);
++		if (ret < 0)
++			goto out;
++	}
++
++	ret = wl12xx_cmd_role_enable(wl, vif->addr,
++				     role_type, &wlvif->role_id);
++	if (ret < 0)
++		goto out;
++
++	ret = wl1271_init_vif_specific(wl, vif);
++	if (ret < 0)
++		goto out;
++
++	list_add(&wlvif->list, &wl->wlvif_list);
++	set_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags);
++
++	if (wlvif->bss_type == BSS_TYPE_AP_BSS)
++		wl->ap_count++;
++	else
++		wl->sta_count++;
++out:
++	wl1271_ps_elp_sleep(wl);
++out_unlock:
++	mutex_unlock(&wl->mutex);
++
++	return ret;
++}
++
++static void __wl1271_op_remove_interface(struct wl1271 *wl,
++					 struct ieee80211_vif *vif,
++					 bool reset_tx_queues)
++{
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	int i, ret;
++	bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
++
++	if (!test_and_clear_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))
++		return;
++
++	/* because of hardware recovery, we may get here twice */
++	if (wl->state == WLCORE_STATE_OFF)
++		return;
++
++	wl1271_info("down");
++
++	if (wl->scan.state != WL1271_SCAN_STATE_IDLE &&
++	    wl->scan_wlvif == wlvif) {
++		/*
++		 * Rearm the tx watchdog just before idling scan. This
++		 * prevents just-finished scans from triggering the watchdog
++		 */
++		wl12xx_rearm_tx_watchdog_locked(wl);
++
++		wl->scan.state = WL1271_SCAN_STATE_IDLE;
++		memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
++		wl->scan_wlvif = NULL;
++		wl->scan.req = NULL;
++		ieee80211_scan_completed(wl->hw, true);
++	}
++
++	if (wl->sched_vif == wlvif) {
++		ieee80211_sched_scan_stopped(wl->hw);
++		wl->sched_vif = NULL;
++	}
++
++	if (wl->roc_vif == vif) {
++		wl->roc_vif = NULL;
++		ieee80211_remain_on_channel_expired(wl->hw);
++	}
++
++	if (!test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) {
++		/* disable active roles */
++		ret = wl1271_ps_elp_wakeup(wl);
++		if (ret < 0)
++			goto deinit;
++
++		if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
++		    wlvif->bss_type == BSS_TYPE_IBSS) {
++			if (wl12xx_dev_role_started(wlvif))
++				wl12xx_stop_dev(wl, wlvif);
++		}
++
++		ret = wl12xx_cmd_role_disable(wl, &wlvif->role_id);
++		if (ret < 0)
++			goto deinit;
++
++		wl1271_ps_elp_sleep(wl);
++	}
++deinit:
++	wl12xx_tx_reset_wlvif(wl, wlvif);
++
++	/* clear all hlids (except system_hlid) */
++	wlvif->dev_hlid = WL12XX_INVALID_LINK_ID;
++
++	if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
++	    wlvif->bss_type == BSS_TYPE_IBSS) {
++		wlvif->sta.hlid = WL12XX_INVALID_LINK_ID;
++		wl12xx_free_rate_policy(wl, &wlvif->sta.basic_rate_idx);
++		wl12xx_free_rate_policy(wl, &wlvif->sta.ap_rate_idx);
++		wl12xx_free_rate_policy(wl, &wlvif->sta.p2p_rate_idx);
++		wlcore_free_klv_template(wl, &wlvif->sta.klv_template_id);
++	} else {
++		wlvif->ap.bcast_hlid = WL12XX_INVALID_LINK_ID;
++		wlvif->ap.global_hlid = WL12XX_INVALID_LINK_ID;
++		wl12xx_free_rate_policy(wl, &wlvif->ap.mgmt_rate_idx);
++		wl12xx_free_rate_policy(wl, &wlvif->ap.bcast_rate_idx);
++		for (i = 0; i < CONF_TX_MAX_AC_COUNT; i++)
++			wl12xx_free_rate_policy(wl,
++						&wlvif->ap.ucast_rate_idx[i]);
++		wl1271_free_ap_keys(wl, wlvif);
++	}
++
++	dev_kfree_skb(wlvif->probereq);
++	wlvif->probereq = NULL;
++	if (wl->last_wlvif == wlvif)
++		wl->last_wlvif = NULL;
++	list_del(&wlvif->list);
++	memset(wlvif->ap.sta_hlid_map, 0, sizeof(wlvif->ap.sta_hlid_map));
++	wlvif->role_id = WL12XX_INVALID_ROLE_ID;
++	wlvif->dev_role_id = WL12XX_INVALID_ROLE_ID;
++
++	if (is_ap)
++		wl->ap_count--;
++	else
++		wl->sta_count--;
++
++	/*
++	 * Last AP, have more stations. Configure sleep auth according to STA.
++	 * Don't do thin on unintended recovery.
++	 */
++	if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags) &&
++	    !test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags))
++		goto unlock;
++
++	if (wl->ap_count == 0 && is_ap && wl->sta_count) {
++		u8 sta_auth = wl->conf.conn.sta_sleep_auth;
++		/* Configure for power according to debugfs */
++		if (sta_auth != WL1271_PSM_ILLEGAL)
++			wl1271_acx_sleep_auth(wl, sta_auth);
++		/* Configure for ELP power saving */
++		else
++			wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
++	}
++
++unlock:
++	mutex_unlock(&wl->mutex);
++
++	del_timer_sync(&wlvif->rx_streaming_timer);
++	cancel_work_sync(&wlvif->rx_streaming_enable_work);
++	cancel_work_sync(&wlvif->rx_streaming_disable_work);
++	cancel_delayed_work_sync(&wlvif->connection_loss_work);
++
++	mutex_lock(&wl->mutex);
++}
++
++static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
++				       struct ieee80211_vif *vif)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	struct wl12xx_vif *iter;
++	struct vif_counter_data vif_count;
++
++	wl12xx_get_vif_count(hw, vif, &vif_count);
++	mutex_lock(&wl->mutex);
++
++	if (wl->state == WLCORE_STATE_OFF ||
++	    !test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))
++		goto out;
++
++	/*
++	 * wl->vif can be null here if someone shuts down the interface
++	 * just when hardware recovery has been started.
++	 */
++	wl12xx_for_each_wlvif(wl, iter) {
++		if (iter != wlvif)
++			continue;
++
++		__wl1271_op_remove_interface(wl, vif, true);
++		break;
++	}
++	WARN_ON(iter != wlvif);
++	if (wl12xx_need_fw_change(wl, vif_count, false)) {
++		wl12xx_force_active_psm(wl);
++		set_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags);
++		wl12xx_queue_recovery_work(wl);
++	}
++out:
++	mutex_unlock(&wl->mutex);
++}
++
++static int wl12xx_op_change_interface(struct ieee80211_hw *hw,
++				      struct ieee80211_vif *vif,
++				      enum nl80211_iftype new_type, bool p2p)
++{
++	struct wl1271 *wl = hw->priv;
++	int ret;
++
++	set_bit(WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, &wl->flags);
++	wl1271_op_remove_interface(hw, vif);
++
++	vif->type = new_type;
++	vif->p2p = p2p;
++	ret = wl1271_op_add_interface(hw, vif);
++
++	clear_bit(WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, &wl->flags);
++	return ret;
++}
++
++static int wlcore_join(struct wl1271 *wl, struct wl12xx_vif *wlvif)
++{
++	int ret;
++	bool is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS);
++
++	/*
++	 * One of the side effects of the JOIN command is that is clears
++	 * WPA/WPA2 keys from the chipset. Performing a JOIN while associated
++	 * to a WPA/WPA2 access point will therefore kill the data-path.
++	 * Currently the only valid scenario for JOIN during association
++	 * is on roaming, in which case we will also be given new keys.
++	 * Keep the below message for now, unless it starts bothering
++	 * users who really like to roam a lot :)
++	 */
++	if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
++		wl1271_info("JOIN while associated.");
++
++	/* clear encryption type */
++	wlvif->encryption_type = KEY_NONE;
++
++	if (is_ibss)
++		ret = wl12xx_cmd_role_start_ibss(wl, wlvif);
++	else {
++		if (wl->quirks & WLCORE_QUIRK_START_STA_FAILS) {
++			/*
++			 * TODO: this is an ugly workaround for wl12xx fw
++			 * bug - we are not able to tx/rx after the first
++			 * start_sta, so make dummy start+stop calls,
++			 * and then call start_sta again.
++			 * this should be fixed in the fw.
++			 */
++			wl12xx_cmd_role_start_sta(wl, wlvif);
++			wl12xx_cmd_role_stop_sta(wl, wlvif);
++		}
++
++		ret = wl12xx_cmd_role_start_sta(wl, wlvif);
++	}
++
++	return ret;
++}
++
++static int wl1271_ssid_set(struct wl12xx_vif *wlvif, struct sk_buff *skb,
++			    int offset)
++{
++	u8 ssid_len;
++	const u8 *ptr = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset,
++					 skb->len - offset);
++
++	if (!ptr) {
++		wl1271_error("No SSID in IEs!");
++		return -ENOENT;
++	}
++
++	ssid_len = ptr[1];
++	if (ssid_len > IEEE80211_MAX_SSID_LEN) {
++		wl1271_error("SSID is too long!");
++		return -EINVAL;
++	}
++
++	wlvif->ssid_len = ssid_len;
++	memcpy(wlvif->ssid, ptr+2, ssid_len);
++	return 0;
++}
++
++static int wlcore_set_ssid(struct wl1271 *wl, struct wl12xx_vif *wlvif)
++{
++	struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
++	struct sk_buff *skb;
++	int ieoffset;
++
++	/* we currently only support setting the ssid from the ap probe req */
++	if (wlvif->bss_type != BSS_TYPE_STA_BSS)
++		return -EINVAL;
++
++	skb = ieee80211_ap_probereq_get(wl->hw, vif);
++	if (!skb)
++		return -EINVAL;
++
++	ieoffset = offsetof(struct ieee80211_mgmt,
++			    u.probe_req.variable);
++	wl1271_ssid_set(wlvif, skb, ieoffset);
++	dev_kfree_skb(skb);
++
++	return 0;
++}
++
++static int wlcore_set_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif,
++			    struct ieee80211_bss_conf *bss_conf,
++			    u32 sta_rate_set)
++{
++	int ieoffset;
++	int ret;
++
++	wlvif->aid = bss_conf->aid;
++	wlvif->channel_type = cfg80211_get_chandef_type(&bss_conf->chandef);
++	wlvif->beacon_int = bss_conf->beacon_int;
++	wlvif->wmm_enabled = bss_conf->qos;
++
++	set_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags);
++
++	/*
++	 * with wl1271, we don't need to update the
++	 * beacon_int and dtim_period, because the firmware
++	 * updates it by itself when the first beacon is
++	 * received after a join.
++	 */
++	ret = wl1271_cmd_build_ps_poll(wl, wlvif, wlvif->aid);
++	if (ret < 0)
++		return ret;
++
++	/*
++	 * Get a template for hardware connection maintenance
++	 */
++	dev_kfree_skb(wlvif->probereq);
++	wlvif->probereq = wl1271_cmd_build_ap_probe_req(wl,
++							wlvif,
++							NULL);
++	ieoffset = offsetof(struct ieee80211_mgmt,
++			    u.probe_req.variable);
++	wl1271_ssid_set(wlvif, wlvif->probereq, ieoffset);
++
++	/* enable the connection monitoring feature */
++	ret = wl1271_acx_conn_monit_params(wl, wlvif, true);
++	if (ret < 0)
++		return ret;
++
++	/*
++	 * The join command disable the keep-alive mode, shut down its process,
++	 * and also clear the template config, so we need to reset it all after
++	 * the join. The acx_aid starts the keep-alive process, and the order
++	 * of the commands below is relevant.
++	 */
++	ret = wl1271_acx_keep_alive_mode(wl, wlvif, true);
++	if (ret < 0)
++		return ret;
++
++	ret = wl1271_acx_aid(wl, wlvif, wlvif->aid);
++	if (ret < 0)
++		return ret;
++
++	ret = wl12xx_cmd_build_klv_null_data(wl, wlvif);
++	if (ret < 0)
++		return ret;
++
++	ret = wl1271_acx_keep_alive_config(wl, wlvif,
++					   wlvif->sta.klv_template_id,
++					   ACX_KEEP_ALIVE_TPL_VALID);
++	if (ret < 0)
++		return ret;
++
++	/*
++	 * The default fw psm configuration is AUTO, while mac80211 default
++	 * setting is off (ACTIVE), so sync the fw with the correct value.
++	 */
++	ret = wl1271_ps_set_mode(wl, wlvif, STATION_ACTIVE_MODE);
++	if (ret < 0)
++		return ret;
++
++	if (sta_rate_set) {
++		wlvif->rate_set =
++			wl1271_tx_enabled_rates_get(wl,
++						    sta_rate_set,
++						    wlvif->band);
++		ret = wl1271_acx_sta_rate_policies(wl, wlvif);
++		if (ret < 0)
++			return ret;
++	}
++
++	return ret;
++}
++
++static int wlcore_unset_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif)
++{
++	int ret;
++	bool sta = wlvif->bss_type == BSS_TYPE_STA_BSS;
++
++	/* make sure we are connected (sta) joined */
++	if (sta &&
++	    !test_and_clear_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
++		return false;
++
++	/* make sure we are joined (ibss) */
++	if (!sta &&
++	    test_and_clear_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags))
++		return false;
++
++	if (sta) {
++		/* use defaults when not associated */
++		wlvif->aid = 0;
++
++		/* free probe-request template */
++		dev_kfree_skb(wlvif->probereq);
++		wlvif->probereq = NULL;
++
++		/* disable connection monitor features */
++		ret = wl1271_acx_conn_monit_params(wl, wlvif, false);
++		if (ret < 0)
++			return ret;
++
++		/* Disable the keep-alive feature */
++		ret = wl1271_acx_keep_alive_mode(wl, wlvif, false);
++		if (ret < 0)
++			return ret;
++	}
++
++	if (test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags)) {
++		struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
++
++		wl12xx_cmd_stop_channel_switch(wl, wlvif);
++		ieee80211_chswitch_done(vif, false);
++		cancel_delayed_work(&wlvif->channel_switch_work);
++	}
++
++	/* invalidate keep-alive template */
++	wl1271_acx_keep_alive_config(wl, wlvif,
++				     wlvif->sta.klv_template_id,
++				     ACX_KEEP_ALIVE_TPL_INVALID);
++
++	return 0;
++}
++
++static void wl1271_set_band_rate(struct wl1271 *wl, struct wl12xx_vif *wlvif)
++{
++	wlvif->basic_rate_set = wlvif->bitrate_masks[wlvif->band];
++	wlvif->rate_set = wlvif->basic_rate_set;
++}
++
++static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif,
++			     struct ieee80211_conf *conf, u32 changed)
++{
++	int ret;
++
++	if (conf->power_level != wlvif->power_level) {
++		ret = wl1271_acx_tx_power(wl, wlvif, conf->power_level);
++		if (ret < 0)
++			return ret;
++
++		wlvif->power_level = conf->power_level;
++	}
++
++	return 0;
++}
++
++static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif;
++	struct ieee80211_conf *conf = &hw->conf;
++	int ret = 0;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 config psm %s power %d %s"
++		     " changed 0x%x",
++		     conf->flags & IEEE80211_CONF_PS ? "on" : "off",
++		     conf->power_level,
++		     conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use",
++			 changed);
++
++	mutex_lock(&wl->mutex);
++
++	if (changed & IEEE80211_CONF_CHANGE_POWER)
++		wl->power_level = conf->power_level;
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	/* configure each interface */
++	wl12xx_for_each_wlvif(wl, wlvif) {
++		ret = wl12xx_config_vif(wl, wlvif, conf, changed);
++		if (ret < 0)
++			goto out_sleep;
++	}
++
++out_sleep:
++	wl1271_ps_elp_sleep(wl);
++
++out:
++	mutex_unlock(&wl->mutex);
++
++	return ret;
++}
++
++struct wl1271_filter_params {
++	bool enabled;
++	int mc_list_length;
++	u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN];
++};
++
++static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw,
++				       struct netdev_hw_addr_list *mc_list)
++{
++	struct wl1271_filter_params *fp;
++	struct netdev_hw_addr *ha;
++
++	fp = kzalloc(sizeof(*fp), GFP_ATOMIC);
++	if (!fp) {
++		wl1271_error("Out of memory setting filters.");
++		return 0;
++	}
++
++	/* update multicast filtering parameters */
++	fp->mc_list_length = 0;
++	if (netdev_hw_addr_list_count(mc_list) > ACX_MC_ADDRESS_GROUP_MAX) {
++		fp->enabled = false;
++	} else {
++		fp->enabled = true;
++		netdev_hw_addr_list_for_each(ha, mc_list) {
++			memcpy(fp->mc_list[fp->mc_list_length],
++					ha->addr, ETH_ALEN);
++			fp->mc_list_length++;
++		}
++	}
++
++	return (u64)(unsigned long)fp;
++}
++
++#define WL1271_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \
++				  FIF_ALLMULTI | \
++				  FIF_FCSFAIL | \
++				  FIF_BCN_PRBRESP_PROMISC | \
++				  FIF_CONTROL | \
++				  FIF_OTHER_BSS)
++
++static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
++				       unsigned int changed,
++				       unsigned int *total, u64 multicast)
++{
++	struct wl1271_filter_params *fp = (void *)(unsigned long)multicast;
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif;
++
++	int ret;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 configure filter changed %x"
++		     " total %x", changed, *total);
++
++	mutex_lock(&wl->mutex);
++
++	*total &= WL1271_SUPPORTED_FILTERS;
++	changed &= WL1271_SUPPORTED_FILTERS;
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	wl12xx_for_each_wlvif(wl, wlvif) {
++		if (wlvif->bss_type != BSS_TYPE_AP_BSS) {
++			if (*total & FIF_ALLMULTI)
++				ret = wl1271_acx_group_address_tbl(wl, wlvif,
++								   false,
++								   NULL, 0);
++			else if (fp)
++				ret = wl1271_acx_group_address_tbl(wl, wlvif,
++							fp->enabled,
++							fp->mc_list,
++							fp->mc_list_length);
++			if (ret < 0)
++				goto out_sleep;
++		}
++	}
++
++	/*
++	 * the fw doesn't provide an api to configure the filters. instead,
++	 * the filters configuration is based on the active roles / ROC
++	 * state.
++	 */
++
++out_sleep:
++	wl1271_ps_elp_sleep(wl);
++
++out:
++	mutex_unlock(&wl->mutex);
++	kfree(fp);
++}
++
++static int wl1271_record_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
++				u8 id, u8 key_type, u8 key_size,
++				const u8 *key, u8 hlid, u32 tx_seq_32,
++				u16 tx_seq_16)
++{
++	struct wl1271_ap_key *ap_key;
++	int i;
++
++	wl1271_debug(DEBUG_CRYPT, "record ap key id %d", (int)id);
++
++	if (key_size > MAX_KEY_SIZE)
++		return -EINVAL;
++
++	/*
++	 * Find next free entry in ap_keys. Also check we are not replacing
++	 * an existing key.
++	 */
++	for (i = 0; i < MAX_NUM_KEYS; i++) {
++		if (wlvif->ap.recorded_keys[i] == NULL)
++			break;
++
++		if (wlvif->ap.recorded_keys[i]->id == id) {
++			wl1271_warning("trying to record key replacement");
++			return -EINVAL;
++		}
++	}
++
++	if (i == MAX_NUM_KEYS)
++		return -EBUSY;
++
++	ap_key = kzalloc(sizeof(*ap_key), GFP_KERNEL);
++	if (!ap_key)
++		return -ENOMEM;
++
++	ap_key->id = id;
++	ap_key->key_type = key_type;
++	ap_key->key_size = key_size;
++	memcpy(ap_key->key, key, key_size);
++	ap_key->hlid = hlid;
++	ap_key->tx_seq_32 = tx_seq_32;
++	ap_key->tx_seq_16 = tx_seq_16;
++
++	wlvif->ap.recorded_keys[i] = ap_key;
++	return 0;
++}
++
++static void wl1271_free_ap_keys(struct wl1271 *wl, struct wl12xx_vif *wlvif)
++{
++	int i;
++
++	for (i = 0; i < MAX_NUM_KEYS; i++) {
++		kfree(wlvif->ap.recorded_keys[i]);
++		wlvif->ap.recorded_keys[i] = NULL;
++	}
++}
++
++static int wl1271_ap_init_hwenc(struct wl1271 *wl, struct wl12xx_vif *wlvif)
++{
++	int i, ret = 0;
++	struct wl1271_ap_key *key;
++	bool wep_key_added = false;
++
++	for (i = 0; i < MAX_NUM_KEYS; i++) {
++		u8 hlid;
++		if (wlvif->ap.recorded_keys[i] == NULL)
++			break;
++
++		key = wlvif->ap.recorded_keys[i];
++		hlid = key->hlid;
++		if (hlid == WL12XX_INVALID_LINK_ID)
++			hlid = wlvif->ap.bcast_hlid;
++
++		ret = wl1271_cmd_set_ap_key(wl, wlvif, KEY_ADD_OR_REPLACE,
++					    key->id, key->key_type,
++					    key->key_size, key->key,
++					    hlid, key->tx_seq_32,
++					    key->tx_seq_16);
++		if (ret < 0)
++			goto out;
++
++		if (key->key_type == KEY_WEP)
++			wep_key_added = true;
++	}
++
++	if (wep_key_added) {
++		ret = wl12xx_cmd_set_default_wep_key(wl, wlvif->default_key,
++						     wlvif->ap.bcast_hlid);
++		if (ret < 0)
++			goto out;
++	}
++
++out:
++	wl1271_free_ap_keys(wl, wlvif);
++	return ret;
++}
++
++static int wl1271_set_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
++		       u16 action, u8 id, u8 key_type,
++		       u8 key_size, const u8 *key, u32 tx_seq_32,
++		       u16 tx_seq_16, struct ieee80211_sta *sta)
++{
++	int ret;
++	bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
++
++	if (is_ap) {
++		struct wl1271_station *wl_sta;
++		u8 hlid;
++
++		if (sta) {
++			wl_sta = (struct wl1271_station *)sta->drv_priv;
++			hlid = wl_sta->hlid;
++		} else {
++			hlid = wlvif->ap.bcast_hlid;
++		}
++
++		if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) {
++			/*
++			 * We do not support removing keys after AP shutdown.
++			 * Pretend we do to make mac80211 happy.
++			 */
++			if (action != KEY_ADD_OR_REPLACE)
++				return 0;
++
++			ret = wl1271_record_ap_key(wl, wlvif, id,
++					     key_type, key_size,
++					     key, hlid, tx_seq_32,
++					     tx_seq_16);
++		} else {
++			ret = wl1271_cmd_set_ap_key(wl, wlvif, action,
++					     id, key_type, key_size,
++					     key, hlid, tx_seq_32,
++					     tx_seq_16);
++		}
++
++		if (ret < 0)
++			return ret;
++	} else {
++		const u8 *addr;
++		static const u8 bcast_addr[ETH_ALEN] = {
++			0xff, 0xff, 0xff, 0xff, 0xff, 0xff
++		};
++
++		addr = sta ? sta->addr : bcast_addr;
++
++		if (is_zero_ether_addr(addr)) {
++			/* We dont support TX only encryption */
++			return -EOPNOTSUPP;
++		}
++
++		/* The wl1271 does not allow to remove unicast keys - they
++		   will be cleared automatically on next CMD_JOIN. Ignore the
++		   request silently, as we dont want the mac80211 to emit
++		   an error message. */
++		if (action == KEY_REMOVE && !is_broadcast_ether_addr(addr))
++			return 0;
++
++		/* don't remove key if hlid was already deleted */
++		if (action == KEY_REMOVE &&
++		    wlvif->sta.hlid == WL12XX_INVALID_LINK_ID)
++			return 0;
++
++		ret = wl1271_cmd_set_sta_key(wl, wlvif, action,
++					     id, key_type, key_size,
++					     key, addr, tx_seq_32,
++					     tx_seq_16);
++		if (ret < 0)
++			return ret;
++
++		/* the default WEP key needs to be configured at least once */
++		if (key_type == KEY_WEP) {
++			ret = wl12xx_cmd_set_default_wep_key(wl,
++							wlvif->default_key,
++							wlvif->sta.hlid);
++			if (ret < 0)
++				return ret;
++		}
++	}
++
++	return 0;
++}
++
++static int wlcore_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
++			     struct ieee80211_vif *vif,
++			     struct ieee80211_sta *sta,
++			     struct ieee80211_key_conf *key_conf)
++{
++	struct wl1271 *wl = hw->priv;
++	int ret;
++	bool might_change_spare =
++		key_conf->cipher == WL1271_CIPHER_SUITE_GEM ||
++		key_conf->cipher == WLAN_CIPHER_SUITE_TKIP;
++
++	if (might_change_spare) {
++		/*
++		 * stop the queues and flush to ensure the next packets are
++		 * in sync with FW spare block accounting
++		 */
++		wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_SPARE_BLK);
++		wl1271_tx_flush(wl);
++	}
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON)) {
++		ret = -EAGAIN;
++		goto out_wake_queues;
++	}
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out_wake_queues;
++
++	ret = wlcore_hw_set_key(wl, cmd, vif, sta, key_conf);
++
++	wl1271_ps_elp_sleep(wl);
++
++out_wake_queues:
++	if (might_change_spare)
++		wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_SPARE_BLK);
++
++	mutex_unlock(&wl->mutex);
++
++	return ret;
++}
++
++int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
++		   struct ieee80211_vif *vif,
++		   struct ieee80211_sta *sta,
++		   struct ieee80211_key_conf *key_conf)
++{
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	int ret;
++	u32 tx_seq_32 = 0;
++	u16 tx_seq_16 = 0;
++	u8 key_type;
++	u8 hlid;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 set key");
++
++	wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x sta: %p", cmd, sta);
++	wl1271_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x",
++		     key_conf->cipher, key_conf->keyidx,
++		     key_conf->keylen, key_conf->flags);
++	wl1271_dump(DEBUG_CRYPT, "KEY: ", key_conf->key, key_conf->keylen);
++
++	if (wlvif->bss_type == BSS_TYPE_AP_BSS)
++		if (sta) {
++			struct wl1271_station *wl_sta = (void *)sta->drv_priv;
++			hlid = wl_sta->hlid;
++		} else {
++			hlid = wlvif->ap.bcast_hlid;
++		}
++	else
++		hlid = wlvif->sta.hlid;
++
++	if (hlid != WL12XX_INVALID_LINK_ID) {
++		u64 tx_seq = wl->links[hlid].total_freed_pkts;
++		tx_seq_32 = WL1271_TX_SECURITY_HI32(tx_seq);
++		tx_seq_16 = WL1271_TX_SECURITY_LO16(tx_seq);
++	}
++
++	switch (key_conf->cipher) {
++	case WLAN_CIPHER_SUITE_WEP40:
++	case WLAN_CIPHER_SUITE_WEP104:
++		key_type = KEY_WEP;
++
++		key_conf->hw_key_idx = key_conf->keyidx;
++		break;
++	case WLAN_CIPHER_SUITE_TKIP:
++		key_type = KEY_TKIP;
++		key_conf->hw_key_idx = key_conf->keyidx;
++		break;
++	case WLAN_CIPHER_SUITE_CCMP:
++		key_type = KEY_AES;
++		key_conf->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE;
++		break;
++	case WL1271_CIPHER_SUITE_GEM:
++		key_type = KEY_GEM;
++		break;
++	default:
++		wl1271_error("Unknown key algo 0x%x", key_conf->cipher);
++
++		return -EOPNOTSUPP;
++	}
++
++	switch (cmd) {
++	case SET_KEY:
++		ret = wl1271_set_key(wl, wlvif, KEY_ADD_OR_REPLACE,
++				 key_conf->keyidx, key_type,
++				 key_conf->keylen, key_conf->key,
++				 tx_seq_32, tx_seq_16, sta);
++		if (ret < 0) {
++			wl1271_error("Could not add or replace key");
++			return ret;
++		}
++
++		/*
++		 * reconfiguring arp response if the unicast (or common)
++		 * encryption key type was changed
++		 */
++		if (wlvif->bss_type == BSS_TYPE_STA_BSS &&
++		    (sta || key_type == KEY_WEP) &&
++		    wlvif->encryption_type != key_type) {
++			wlvif->encryption_type = key_type;
++			ret = wl1271_cmd_build_arp_rsp(wl, wlvif);
++			if (ret < 0) {
++				wl1271_warning("build arp rsp failed: %d", ret);
++				return ret;
++			}
++		}
++		break;
++
++	case DISABLE_KEY:
++		ret = wl1271_set_key(wl, wlvif, KEY_REMOVE,
++				     key_conf->keyidx, key_type,
++				     key_conf->keylen, key_conf->key,
++				     0, 0, sta);
++		if (ret < 0) {
++			wl1271_error("Could not remove key");
++			return ret;
++		}
++		break;
++
++	default:
++		wl1271_error("Unsupported key cmd 0x%x", cmd);
++		return -EOPNOTSUPP;
++	}
++
++	return ret;
++}
++EXPORT_SYMBOL_GPL(wlcore_set_key);
++
++void wlcore_regdomain_config(struct wl1271 *wl)
++{
++	int ret;
++
++	if (!(wl->quirks & WLCORE_QUIRK_REGDOMAIN_CONF))
++		return;
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wlcore_cmd_regdomain_config_locked(wl);
++	if (ret < 0) {
++		wl12xx_queue_recovery_work(wl);
++		goto out;
++	}
++
++	wl1271_ps_elp_sleep(wl);
++out:
++	mutex_unlock(&wl->mutex);
++}
++
++static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
++			     struct ieee80211_vif *vif,
++			     struct cfg80211_scan_request *req)
++{
++	struct wl1271 *wl = hw->priv;
++	int ret;
++	u8 *ssid = NULL;
++	size_t len = 0;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 hw scan");
++
++	if (req->n_ssids) {
++		ssid = req->ssids[0].ssid;
++		len = req->ssids[0].ssid_len;
++	}
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON)) {
++		/*
++		 * We cannot return -EBUSY here because cfg80211 will expect
++		 * a call to ieee80211_scan_completed if we do - in this case
++		 * there won't be any call.
++		 */
++		ret = -EAGAIN;
++		goto out;
++	}
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	/* fail if there is any role in ROC */
++	if (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES) {
++		/* don't allow scanning right now */
++		ret = -EBUSY;
++		goto out_sleep;
++	}
++
++	ret = wlcore_scan(hw->priv, vif, ssid, len, req);
++out_sleep:
++	wl1271_ps_elp_sleep(wl);
++out:
++	mutex_unlock(&wl->mutex);
++
++	return ret;
++}
++
++static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw,
++				     struct ieee80211_vif *vif)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	int ret;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 cancel hw scan");
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	if (wl->scan.state == WL1271_SCAN_STATE_IDLE)
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	if (wl->scan.state != WL1271_SCAN_STATE_DONE) {
++		ret = wl->ops->scan_stop(wl, wlvif);
++		if (ret < 0)
++			goto out_sleep;
++	}
++
++	/*
++	 * Rearm the tx watchdog just before idling scan. This
++	 * prevents just-finished scans from triggering the watchdog
++	 */
++	wl12xx_rearm_tx_watchdog_locked(wl);
++
++	wl->scan.state = WL1271_SCAN_STATE_IDLE;
++	memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
++	wl->scan_wlvif = NULL;
++	wl->scan.req = NULL;
++	ieee80211_scan_completed(wl->hw, true);
++
++out_sleep:
++	wl1271_ps_elp_sleep(wl);
++out:
++	mutex_unlock(&wl->mutex);
++
++	cancel_delayed_work_sync(&wl->scan_complete_work);
++}
++
++static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw,
++				      struct ieee80211_vif *vif,
++				      struct cfg80211_sched_scan_request *req,
++				      struct ieee80211_sched_scan_ies *ies)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	int ret;
++
++	wl1271_debug(DEBUG_MAC80211, "wl1271_op_sched_scan_start");
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON)) {
++		ret = -EAGAIN;
++		goto out;
++	}
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wl->ops->sched_scan_start(wl, wlvif, req, ies);
++	if (ret < 0)
++		goto out_sleep;
++
++	wl->sched_vif = wlvif;
++
++out_sleep:
++	wl1271_ps_elp_sleep(wl);
++out:
++	mutex_unlock(&wl->mutex);
++	return ret;
++}
++
++static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw,
++				      struct ieee80211_vif *vif)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	int ret;
++
++	wl1271_debug(DEBUG_MAC80211, "wl1271_op_sched_scan_stop");
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	wl->ops->sched_scan_stop(wl, wlvif);
++
++	wl1271_ps_elp_sleep(wl);
++out:
++	mutex_unlock(&wl->mutex);
++}
++
++static int wl1271_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
++{
++	struct wl1271 *wl = hw->priv;
++	int ret = 0;
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON)) {
++		ret = -EAGAIN;
++		goto out;
++	}
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wl1271_acx_frag_threshold(wl, value);
++	if (ret < 0)
++		wl1271_warning("wl1271_op_set_frag_threshold failed: %d", ret);
++
++	wl1271_ps_elp_sleep(wl);
++
++out:
++	mutex_unlock(&wl->mutex);
++
++	return ret;
++}
++
++static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif;
++	int ret = 0;
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON)) {
++		ret = -EAGAIN;
++		goto out;
++	}
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	wl12xx_for_each_wlvif(wl, wlvif) {
++		ret = wl1271_acx_rts_threshold(wl, wlvif, value);
++		if (ret < 0)
++			wl1271_warning("set rts threshold failed: %d", ret);
++	}
++	wl1271_ps_elp_sleep(wl);
++
++out:
++	mutex_unlock(&wl->mutex);
++
++	return ret;
++}
++
++static void wl12xx_remove_ie(struct sk_buff *skb, u8 eid, int ieoffset)
++{
++	int len;
++	const u8 *next, *end = skb->data + skb->len;
++	u8 *ie = (u8 *)cfg80211_find_ie(eid, skb->data + ieoffset,
++					skb->len - ieoffset);
++	if (!ie)
++		return;
++	len = ie[1] + 2;
++	next = ie + len;
++	memmove(ie, next, end - next);
++	skb_trim(skb, skb->len - len);
++}
++
++static void wl12xx_remove_vendor_ie(struct sk_buff *skb,
++					    unsigned int oui, u8 oui_type,
++					    int ieoffset)
++{
++	int len;
++	const u8 *next, *end = skb->data + skb->len;
++	u8 *ie = (u8 *)cfg80211_find_vendor_ie(oui, oui_type,
++					       skb->data + ieoffset,
++					       skb->len - ieoffset);
++	if (!ie)
++		return;
++	len = ie[1] + 2;
++	next = ie + len;
++	memmove(ie, next, end - next);
++	skb_trim(skb, skb->len - len);
++}
++
++static int wl1271_ap_set_probe_resp_tmpl(struct wl1271 *wl, u32 rates,
++					 struct ieee80211_vif *vif)
++{
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	struct sk_buff *skb;
++	int ret;
++
++	skb = ieee80211_proberesp_get(wl->hw, vif);
++	if (!skb)
++		return -EOPNOTSUPP;
++
++	ret = wl1271_cmd_template_set(wl, wlvif->role_id,
++				      CMD_TEMPL_AP_PROBE_RESPONSE,
++				      skb->data,
++				      skb->len, 0,
++				      rates);
++	dev_kfree_skb(skb);
++
++	if (ret < 0)
++		goto out;
++
++	wl1271_debug(DEBUG_AP, "probe response updated");
++	set_bit(WLVIF_FLAG_AP_PROBE_RESP_SET, &wlvif->flags);
++
++out:
++	return ret;
++}
++
++static int wl1271_ap_set_probe_resp_tmpl_legacy(struct wl1271 *wl,
++					     struct ieee80211_vif *vif,
++					     u8 *probe_rsp_data,
++					     size_t probe_rsp_len,
++					     u32 rates)
++{
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
++	u8 probe_rsp_templ[WL1271_CMD_TEMPL_MAX_SIZE];
++	int ssid_ie_offset, ie_offset, templ_len;
++	const u8 *ptr;
++
++	/* no need to change probe response if the SSID is set correctly */
++	if (wlvif->ssid_len > 0)
++		return wl1271_cmd_template_set(wl, wlvif->role_id,
++					       CMD_TEMPL_AP_PROBE_RESPONSE,
++					       probe_rsp_data,
++					       probe_rsp_len, 0,
++					       rates);
++
++	if (probe_rsp_len + bss_conf->ssid_len > WL1271_CMD_TEMPL_MAX_SIZE) {
++		wl1271_error("probe_rsp template too big");
++		return -EINVAL;
++	}
++
++	/* start searching from IE offset */
++	ie_offset = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
++
++	ptr = cfg80211_find_ie(WLAN_EID_SSID, probe_rsp_data + ie_offset,
++			       probe_rsp_len - ie_offset);
++	if (!ptr) {
++		wl1271_error("No SSID in beacon!");
++		return -EINVAL;
++	}
++
++	ssid_ie_offset = ptr - probe_rsp_data;
++	ptr += (ptr[1] + 2);
++
++	memcpy(probe_rsp_templ, probe_rsp_data, ssid_ie_offset);
++
++	/* insert SSID from bss_conf */
++	probe_rsp_templ[ssid_ie_offset] = WLAN_EID_SSID;
++	probe_rsp_templ[ssid_ie_offset + 1] = bss_conf->ssid_len;
++	memcpy(probe_rsp_templ + ssid_ie_offset + 2,
++	       bss_conf->ssid, bss_conf->ssid_len);
++	templ_len = ssid_ie_offset + 2 + bss_conf->ssid_len;
++
++	memcpy(probe_rsp_templ + ssid_ie_offset + 2 + bss_conf->ssid_len,
++	       ptr, probe_rsp_len - (ptr - probe_rsp_data));
++	templ_len += probe_rsp_len - (ptr - probe_rsp_data);
++
++	return wl1271_cmd_template_set(wl, wlvif->role_id,
++				       CMD_TEMPL_AP_PROBE_RESPONSE,
++				       probe_rsp_templ,
++				       templ_len, 0,
++				       rates);
++}
++
++static int wl1271_bss_erp_info_changed(struct wl1271 *wl,
++				       struct ieee80211_vif *vif,
++				       struct ieee80211_bss_conf *bss_conf,
++				       u32 changed)
++{
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	int ret = 0;
++
++	if (changed & BSS_CHANGED_ERP_SLOT) {
++		if (bss_conf->use_short_slot)
++			ret = wl1271_acx_slot(wl, wlvif, SLOT_TIME_SHORT);
++		else
++			ret = wl1271_acx_slot(wl, wlvif, SLOT_TIME_LONG);
++		if (ret < 0) {
++			wl1271_warning("Set slot time failed %d", ret);
++			goto out;
++		}
++	}
++
++	if (changed & BSS_CHANGED_ERP_PREAMBLE) {
++		if (bss_conf->use_short_preamble)
++			wl1271_acx_set_preamble(wl, wlvif, ACX_PREAMBLE_SHORT);
++		else
++			wl1271_acx_set_preamble(wl, wlvif, ACX_PREAMBLE_LONG);
++	}
++
++	if (changed & BSS_CHANGED_ERP_CTS_PROT) {
++		if (bss_conf->use_cts_prot)
++			ret = wl1271_acx_cts_protect(wl, wlvif,
++						     CTSPROTECT_ENABLE);
++		else
++			ret = wl1271_acx_cts_protect(wl, wlvif,
++						     CTSPROTECT_DISABLE);
++		if (ret < 0) {
++			wl1271_warning("Set ctsprotect failed %d", ret);
++			goto out;
++		}
++	}
++
++out:
++	return ret;
++}
++
++static int wlcore_set_beacon_template(struct wl1271 *wl,
++				      struct ieee80211_vif *vif,
++				      bool is_ap)
++{
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	struct ieee80211_hdr *hdr;
++	u32 min_rate;
++	int ret;
++	int ieoffset = offsetof(struct ieee80211_mgmt,
++				u.beacon.variable);
++	struct sk_buff *beacon = ieee80211_beacon_get(wl->hw, vif);
++	u16 tmpl_id;
++
++	if (!beacon) {
++		ret = -EINVAL;
++		goto out;
++	}
++
++	wl1271_debug(DEBUG_MASTER, "beacon updated");
++
++	ret = wl1271_ssid_set(wlvif, beacon, ieoffset);
++	if (ret < 0) {
++		dev_kfree_skb(beacon);
++		goto out;
++	}
++	min_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
++	tmpl_id = is_ap ? CMD_TEMPL_AP_BEACON :
++		CMD_TEMPL_BEACON;
++	ret = wl1271_cmd_template_set(wl, wlvif->role_id, tmpl_id,
++				      beacon->data,
++				      beacon->len, 0,
++				      min_rate);
++	if (ret < 0) {
++		dev_kfree_skb(beacon);
++		goto out;
++	}
++
++	wlvif->wmm_enabled =
++		cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
++					WLAN_OUI_TYPE_MICROSOFT_WMM,
++					beacon->data + ieoffset,
++					beacon->len - ieoffset);
++
++	/*
++	 * In case we already have a probe-resp beacon set explicitly
++	 * by usermode, don't use the beacon data.
++	 */
++	if (test_bit(WLVIF_FLAG_AP_PROBE_RESP_SET, &wlvif->flags))
++		goto end_bcn;
++
++	/* remove TIM ie from probe response */
++	wl12xx_remove_ie(beacon, WLAN_EID_TIM, ieoffset);
++
++	/*
++	 * remove p2p ie from probe response.
++	 * the fw reponds to probe requests that don't include
++	 * the p2p ie. probe requests with p2p ie will be passed,
++	 * and will be responded by the supplicant (the spec
++	 * forbids including the p2p ie when responding to probe
++	 * requests that didn't include it).
++	 */
++	wl12xx_remove_vendor_ie(beacon, WLAN_OUI_WFA,
++				WLAN_OUI_TYPE_WFA_P2P, ieoffset);
++
++	hdr = (struct ieee80211_hdr *) beacon->data;
++	hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
++					 IEEE80211_STYPE_PROBE_RESP);
++	if (is_ap)
++		ret = wl1271_ap_set_probe_resp_tmpl_legacy(wl, vif,
++							   beacon->data,
++							   beacon->len,
++							   min_rate);
++	else
++		ret = wl1271_cmd_template_set(wl, wlvif->role_id,
++					      CMD_TEMPL_PROBE_RESPONSE,
++					      beacon->data,
++					      beacon->len, 0,
++					      min_rate);
++end_bcn:
++	dev_kfree_skb(beacon);
++	if (ret < 0)
++		goto out;
++
++out:
++	return ret;
++}
++
++static int wl1271_bss_beacon_info_changed(struct wl1271 *wl,
++					  struct ieee80211_vif *vif,
++					  struct ieee80211_bss_conf *bss_conf,
++					  u32 changed)
++{
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
++	int ret = 0;
++
++	if (changed & BSS_CHANGED_BEACON_INT) {
++		wl1271_debug(DEBUG_MASTER, "beacon interval updated: %d",
++			bss_conf->beacon_int);
++
++		wlvif->beacon_int = bss_conf->beacon_int;
++	}
++
++	if ((changed & BSS_CHANGED_AP_PROBE_RESP) && is_ap) {
++		u32 rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
++
++		wl1271_ap_set_probe_resp_tmpl(wl, rate, vif);
++	}
++
++	if (changed & BSS_CHANGED_BEACON) {
++		ret = wlcore_set_beacon_template(wl, vif, is_ap);
++		if (ret < 0)
++			goto out;
++	}
++
++out:
++	if (ret != 0)
++		wl1271_error("beacon info change failed: %d", ret);
++	return ret;
++}
++
++/* AP mode changes */
++static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
++				       struct ieee80211_vif *vif,
++				       struct ieee80211_bss_conf *bss_conf,
++				       u32 changed)
++{
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	int ret = 0;
++
++	if (changed & BSS_CHANGED_BASIC_RATES) {
++		u32 rates = bss_conf->basic_rates;
++
++		wlvif->basic_rate_set = wl1271_tx_enabled_rates_get(wl, rates,
++								 wlvif->band);
++		wlvif->basic_rate = wl1271_tx_min_rate_get(wl,
++							wlvif->basic_rate_set);
++
++		ret = wl1271_init_ap_rates(wl, wlvif);
++		if (ret < 0) {
++			wl1271_error("AP rate policy change failed %d", ret);
++			goto out;
++		}
++
++		ret = wl1271_ap_init_templates(wl, vif);
++		if (ret < 0)
++			goto out;
++
++		ret = wl1271_ap_set_probe_resp_tmpl(wl, wlvif->basic_rate, vif);
++		if (ret < 0)
++			goto out;
++
++		ret = wlcore_set_beacon_template(wl, vif, true);
++		if (ret < 0)
++			goto out;
++	}
++
++	ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf, changed);
++	if (ret < 0)
++		goto out;
++
++	if (changed & BSS_CHANGED_BEACON_ENABLED) {
++		if (bss_conf->enable_beacon) {
++			if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) {
++				ret = wl12xx_cmd_role_start_ap(wl, wlvif);
++				if (ret < 0)
++					goto out;
++
++				ret = wl1271_ap_init_hwenc(wl, wlvif);
++				if (ret < 0)
++					goto out;
++
++				set_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags);
++				wl1271_debug(DEBUG_AP, "started AP");
++			}
++		} else {
++			if (test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) {
++				ret = wl12xx_cmd_role_stop_ap(wl, wlvif);
++				if (ret < 0)
++					goto out;
++
++				clear_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags);
++				clear_bit(WLVIF_FLAG_AP_PROBE_RESP_SET,
++					  &wlvif->flags);
++				wl1271_debug(DEBUG_AP, "stopped AP");
++			}
++		}
++	}
++
++	ret = wl1271_bss_erp_info_changed(wl, vif, bss_conf, changed);
++	if (ret < 0)
++		goto out;
++
++	/* Handle HT information change */
++	if ((changed & BSS_CHANGED_HT) &&
++	    (bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT)) {
++		ret = wl1271_acx_set_ht_information(wl, wlvif,
++					bss_conf->ht_operation_mode);
++		if (ret < 0) {
++			wl1271_warning("Set ht information failed %d", ret);
++			goto out;
++		}
++	}
++
++out:
++	return;
++}
++
++static int wlcore_set_bssid(struct wl1271 *wl, struct wl12xx_vif *wlvif,
++			    struct ieee80211_bss_conf *bss_conf,
++			    u32 sta_rate_set)
++{
++	u32 rates;
++	int ret;
++
++	wl1271_debug(DEBUG_MAC80211,
++	     "changed_bssid: %pM, aid: %d, bcn_int: %d, brates: 0x%x sta_rate_set: 0x%x",
++	     bss_conf->bssid, bss_conf->aid,
++	     bss_conf->beacon_int,
++	     bss_conf->basic_rates, sta_rate_set);
++
++	wlvif->beacon_int = bss_conf->beacon_int;
++	rates = bss_conf->basic_rates;
++	wlvif->basic_rate_set =
++		wl1271_tx_enabled_rates_get(wl, rates,
++					    wlvif->band);
++	wlvif->basic_rate =
++		wl1271_tx_min_rate_get(wl,
++				       wlvif->basic_rate_set);
++
++	if (sta_rate_set)
++		wlvif->rate_set =
++			wl1271_tx_enabled_rates_get(wl,
++						sta_rate_set,
++						wlvif->band);
++
++	/* we only support sched_scan while not connected */
++	if (wl->sched_vif == wlvif)
++		wl->ops->sched_scan_stop(wl, wlvif);
++
++	ret = wl1271_acx_sta_rate_policies(wl, wlvif);
++	if (ret < 0)
++		return ret;
++
++	ret = wl12xx_cmd_build_null_data(wl, wlvif);
++	if (ret < 0)
++		return ret;
++
++	ret = wl1271_build_qos_null_data(wl, wl12xx_wlvif_to_vif(wlvif));
++	if (ret < 0)
++		return ret;
++
++	wlcore_set_ssid(wl, wlvif);
++
++	set_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
++
++	return 0;
++}
++
++static int wlcore_clear_bssid(struct wl1271 *wl, struct wl12xx_vif *wlvif)
++{
++	int ret;
++
++	/* revert back to minimum rates for the current band */
++	wl1271_set_band_rate(wl, wlvif);
++	wlvif->basic_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
++
++	ret = wl1271_acx_sta_rate_policies(wl, wlvif);
++	if (ret < 0)
++		return ret;
++
++	if (wlvif->bss_type == BSS_TYPE_STA_BSS &&
++	    test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags)) {
++		ret = wl12xx_cmd_role_stop_sta(wl, wlvif);
++		if (ret < 0)
++			return ret;
++	}
++
++	clear_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
++	return 0;
++}
++/* STA/IBSS mode changes */
++static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
++					struct ieee80211_vif *vif,
++					struct ieee80211_bss_conf *bss_conf,
++					u32 changed)
++{
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	bool do_join = false;
++	bool is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS);
++	bool ibss_joined = false;
++	u32 sta_rate_set = 0;
++	int ret;
++	struct ieee80211_sta *sta;
++	bool sta_exists = false;
++	struct ieee80211_sta_ht_cap sta_ht_cap;
++
++	if (is_ibss) {
++		ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf,
++						     changed);
++		if (ret < 0)
++			goto out;
++	}
++
++	if (changed & BSS_CHANGED_IBSS) {
++		if (bss_conf->ibss_joined) {
++			set_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags);
++			ibss_joined = true;
++		} else {
++			wlcore_unset_assoc(wl, wlvif);
++			wl12xx_cmd_role_stop_sta(wl, wlvif);
++		}
++	}
++
++	if ((changed & BSS_CHANGED_BEACON_INT) && ibss_joined)
++		do_join = true;
++
++	/* Need to update the SSID (for filtering etc) */
++	if ((changed & BSS_CHANGED_BEACON) && ibss_joined)
++		do_join = true;
++
++	if ((changed & BSS_CHANGED_BEACON_ENABLED) && ibss_joined) {
++		wl1271_debug(DEBUG_ADHOC, "ad-hoc beaconing: %s",
++			     bss_conf->enable_beacon ? "enabled" : "disabled");
++
++		do_join = true;
++	}
++
++	if (changed & BSS_CHANGED_CQM) {
++		bool enable = false;
++		if (bss_conf->cqm_rssi_thold)
++			enable = true;
++		ret = wl1271_acx_rssi_snr_trigger(wl, wlvif, enable,
++						  bss_conf->cqm_rssi_thold,
++						  bss_conf->cqm_rssi_hyst);
++		if (ret < 0)
++			goto out;
++		wlvif->rssi_thold = bss_conf->cqm_rssi_thold;
++	}
++
++	if (changed & (BSS_CHANGED_BSSID | BSS_CHANGED_HT |
++		       BSS_CHANGED_ASSOC)) {
++		rcu_read_lock();
++		sta = ieee80211_find_sta(vif, bss_conf->bssid);
++		if (sta) {
++			u8 *rx_mask = sta->ht_cap.mcs.rx_mask;
++
++			/* save the supp_rates of the ap */
++			sta_rate_set = sta->supp_rates[wlvif->band];
++			if (sta->ht_cap.ht_supported)
++				sta_rate_set |=
++					(rx_mask[0] << HW_HT_RATES_OFFSET) |
++					(rx_mask[1] << HW_MIMO_RATES_OFFSET);
++			sta_ht_cap = sta->ht_cap;
++			sta_exists = true;
++		}
++
++		rcu_read_unlock();
++	}
++
++	if (changed & BSS_CHANGED_BSSID) {
++		if (!is_zero_ether_addr(bss_conf->bssid)) {
++			ret = wlcore_set_bssid(wl, wlvif, bss_conf,
++					       sta_rate_set);
++			if (ret < 0)
++				goto out;
++
++			/* Need to update the BSSID (for filtering etc) */
++			do_join = true;
++		} else {
++			ret = wlcore_clear_bssid(wl, wlvif);
++			if (ret < 0)
++				goto out;
++		}
++	}
++
++	if (changed & BSS_CHANGED_IBSS) {
++		wl1271_debug(DEBUG_ADHOC, "ibss_joined: %d",
++			     bss_conf->ibss_joined);
++
++		if (bss_conf->ibss_joined) {
++			u32 rates = bss_conf->basic_rates;
++			wlvif->basic_rate_set =
++				wl1271_tx_enabled_rates_get(wl, rates,
++							    wlvif->band);
++			wlvif->basic_rate =
++				wl1271_tx_min_rate_get(wl,
++						       wlvif->basic_rate_set);
++
++			/* by default, use 11b + OFDM rates */
++			wlvif->rate_set = CONF_TX_IBSS_DEFAULT_RATES;
++			ret = wl1271_acx_sta_rate_policies(wl, wlvif);
++			if (ret < 0)
++				goto out;
++		}
++	}
++
++	ret = wl1271_bss_erp_info_changed(wl, vif, bss_conf, changed);
++	if (ret < 0)
++		goto out;
++
++	if (do_join) {
++		ret = wlcore_join(wl, wlvif);
++		if (ret < 0) {
++			wl1271_warning("cmd join failed %d", ret);
++			goto out;
++		}
++	}
++
++	if (changed & BSS_CHANGED_ASSOC) {
++		if (bss_conf->assoc) {
++			ret = wlcore_set_assoc(wl, wlvif, bss_conf,
++					       sta_rate_set);
++			if (ret < 0)
++				goto out;
++
++			if (test_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags))
++				wl12xx_set_authorized(wl, wlvif);
++		} else {
++			wlcore_unset_assoc(wl, wlvif);
++		}
++	}
++
++	if (changed & BSS_CHANGED_PS) {
++		if ((bss_conf->ps) &&
++		    test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) &&
++		    !test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) {
++			int ps_mode;
++			char *ps_mode_str;
++
++			if (wl->conf.conn.forced_ps) {
++				ps_mode = STATION_POWER_SAVE_MODE;
++				ps_mode_str = "forced";
++			} else {
++				ps_mode = STATION_AUTO_PS_MODE;
++				ps_mode_str = "auto";
++			}
++
++			wl1271_debug(DEBUG_PSM, "%s ps enabled", ps_mode_str);
++
++			ret = wl1271_ps_set_mode(wl, wlvif, ps_mode);
++			if (ret < 0)
++				wl1271_warning("enter %s ps failed %d",
++					       ps_mode_str, ret);
++		} else if (!bss_conf->ps &&
++			   test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) {
++			wl1271_debug(DEBUG_PSM, "auto ps disabled");
++
++			ret = wl1271_ps_set_mode(wl, wlvif,
++						 STATION_ACTIVE_MODE);
++			if (ret < 0)
++				wl1271_warning("exit auto ps failed %d", ret);
++		}
++	}
++
++	/* Handle new association with HT. Do this after join. */
++	if (sta_exists &&
++	    (changed & BSS_CHANGED_HT)) {
++		bool enabled =
++			bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT;
++
++		ret = wlcore_hw_set_peer_cap(wl,
++					     &sta_ht_cap,
++					     enabled,
++					     wlvif->rate_set,
++					     wlvif->sta.hlid);
++		if (ret < 0) {
++			wl1271_warning("Set ht cap failed %d", ret);
++			goto out;
++
++		}
++
++		if (enabled) {
++			ret = wl1271_acx_set_ht_information(wl, wlvif,
++						bss_conf->ht_operation_mode);
++			if (ret < 0) {
++				wl1271_warning("Set ht information failed %d",
++					       ret);
++				goto out;
++			}
++		}
++	}
++
++	/* Handle arp filtering. Done after join. */
++	if ((changed & BSS_CHANGED_ARP_FILTER) ||
++	    (!is_ibss && (changed & BSS_CHANGED_QOS))) {
++		__be32 addr = bss_conf->arp_addr_list[0];
++		wlvif->sta.qos = bss_conf->qos;
++		WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS);
++
++		if (bss_conf->arp_addr_cnt == 1 && bss_conf->assoc) {
++			wlvif->ip_addr = addr;
++			/*
++			 * The template should have been configured only upon
++			 * association. however, it seems that the correct ip
++			 * isn't being set (when sending), so we have to
++			 * reconfigure the template upon every ip change.
++			 */
++			ret = wl1271_cmd_build_arp_rsp(wl, wlvif);
++			if (ret < 0) {
++				wl1271_warning("build arp rsp failed: %d", ret);
++				goto out;
++			}
++
++			ret = wl1271_acx_arp_ip_filter(wl, wlvif,
++				(ACX_ARP_FILTER_ARP_FILTERING |
++				 ACX_ARP_FILTER_AUTO_ARP),
++				addr);
++		} else {
++			wlvif->ip_addr = 0;
++			ret = wl1271_acx_arp_ip_filter(wl, wlvif, 0, addr);
++		}
++
++		if (ret < 0)
++			goto out;
++	}
++
++out:
++	return;
++}
++
++static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
++				       struct ieee80211_vif *vif,
++				       struct ieee80211_bss_conf *bss_conf,
++				       u32 changed)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
++	int ret;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 bss info role %d changed 0x%x",
++		     wlvif->role_id, (int)changed);
++
++	/*
++	 * make sure to cancel pending disconnections if our association
++	 * state changed
++	 */
++	if (!is_ap && (changed & BSS_CHANGED_ASSOC))
++		cancel_delayed_work_sync(&wlvif->connection_loss_work);
++
++	if (is_ap && (changed & BSS_CHANGED_BEACON_ENABLED) &&
++	    !bss_conf->enable_beacon)
++		wl1271_tx_flush(wl);
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	if (unlikely(!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	if (is_ap)
++		wl1271_bss_info_changed_ap(wl, vif, bss_conf, changed);
++	else
++		wl1271_bss_info_changed_sta(wl, vif, bss_conf, changed);
++
++	wl1271_ps_elp_sleep(wl);
++
++out:
++	mutex_unlock(&wl->mutex);
++}
++
++static int wlcore_op_add_chanctx(struct ieee80211_hw *hw,
++				 struct ieee80211_chanctx_conf *ctx)
++{
++	wl1271_debug(DEBUG_MAC80211, "mac80211 add chanctx %d (type %d)",
++		     ieee80211_frequency_to_channel(ctx->def.chan->center_freq),
++		     cfg80211_get_chandef_type(&ctx->def));
++	return 0;
++}
++
++static void wlcore_op_remove_chanctx(struct ieee80211_hw *hw,
++				     struct ieee80211_chanctx_conf *ctx)
++{
++	wl1271_debug(DEBUG_MAC80211, "mac80211 remove chanctx %d (type %d)",
++		     ieee80211_frequency_to_channel(ctx->def.chan->center_freq),
++		     cfg80211_get_chandef_type(&ctx->def));
++}
++
++static void wlcore_op_change_chanctx(struct ieee80211_hw *hw,
++				     struct ieee80211_chanctx_conf *ctx,
++				     u32 changed)
++{
++	wl1271_debug(DEBUG_MAC80211,
++		     "mac80211 change chanctx %d (type %d) changed 0x%x",
++		     ieee80211_frequency_to_channel(ctx->def.chan->center_freq),
++		     cfg80211_get_chandef_type(&ctx->def), changed);
++}
++
++static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw,
++					struct ieee80211_vif *vif,
++					struct ieee80211_chanctx_conf *ctx)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	int channel = ieee80211_frequency_to_channel(
++		ctx->def.chan->center_freq);
++
++	wl1271_debug(DEBUG_MAC80211,
++		     "mac80211 assign chanctx (role %d) %d (type %d)",
++		     wlvif->role_id, channel, cfg80211_get_chandef_type(&ctx->def));
++
++	mutex_lock(&wl->mutex);
++
++	wlvif->band = ctx->def.chan->band;
++	wlvif->channel = channel;
++	wlvif->channel_type = cfg80211_get_chandef_type(&ctx->def);
++
++	/* update default rates according to the band */
++	wl1271_set_band_rate(wl, wlvif);
++
++	mutex_unlock(&wl->mutex);
++
++	return 0;
++}
++
++static void wlcore_op_unassign_vif_chanctx(struct ieee80211_hw *hw,
++					   struct ieee80211_vif *vif,
++					   struct ieee80211_chanctx_conf *ctx)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++
++	wl1271_debug(DEBUG_MAC80211,
++		     "mac80211 unassign chanctx (role %d) %d (type %d)",
++		     wlvif->role_id,
++		     ieee80211_frequency_to_channel(ctx->def.chan->center_freq),
++		     cfg80211_get_chandef_type(&ctx->def));
++
++	wl1271_tx_flush(wl);
++}
++
++static int wl1271_op_conf_tx(struct ieee80211_hw *hw,
++			     struct ieee80211_vif *vif, u16 queue,
++			     const struct ieee80211_tx_queue_params *params)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	u8 ps_scheme;
++	int ret = 0;
++
++	mutex_lock(&wl->mutex);
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 conf tx %d", queue);
++
++	if (params->uapsd)
++		ps_scheme = CONF_PS_SCHEME_UPSD_TRIGGER;
++	else
++		ps_scheme = CONF_PS_SCHEME_LEGACY;
++
++	if (!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	/*
++	 * the txop is confed in units of 32us by the mac80211,
++	 * we need us
++	 */
++	ret = wl1271_acx_ac_cfg(wl, wlvif, wl1271_tx_get_queue(queue),
++				params->cw_min, params->cw_max,
++				params->aifs, params->txop << 5);
++	if (ret < 0)
++		goto out_sleep;
++
++	ret = wl1271_acx_tid_cfg(wl, wlvif, wl1271_tx_get_queue(queue),
++				 CONF_CHANNEL_TYPE_EDCF,
++				 wl1271_tx_get_queue(queue),
++				 ps_scheme, CONF_ACK_POLICY_LEGACY,
++				 0, 0);
++
++out_sleep:
++	wl1271_ps_elp_sleep(wl);
++
++out:
++	mutex_unlock(&wl->mutex);
++
++	return ret;
++}
++
++static u64 wl1271_op_get_tsf(struct ieee80211_hw *hw,
++			     struct ieee80211_vif *vif)
++{
++
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	u64 mactime = ULLONG_MAX;
++	int ret;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 get tsf");
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wl12xx_acx_tsf_info(wl, wlvif, &mactime);
++	if (ret < 0)
++		goto out_sleep;
++
++out_sleep:
++	wl1271_ps_elp_sleep(wl);
++
++out:
++	mutex_unlock(&wl->mutex);
++	return mactime;
++}
++
++static int wl1271_op_get_survey(struct ieee80211_hw *hw, int idx,
++				struct survey_info *survey)
++{
++	struct ieee80211_conf *conf = &hw->conf;
++
++	if (idx != 0)
++		return -ENOENT;
++
++	survey->channel = conf->chandef.chan;
++	survey->filled = 0;
++	return 0;
++}
++
++static int wl1271_allocate_sta(struct wl1271 *wl,
++			     struct wl12xx_vif *wlvif,
++			     struct ieee80211_sta *sta)
++{
++	struct wl1271_station *wl_sta;
++	int ret;
++
++
++	if (wl->active_sta_count >= AP_MAX_STATIONS) {
++		wl1271_warning("could not allocate HLID - too much stations");
++		return -EBUSY;
++	}
++
++	wl_sta = (struct wl1271_station *)sta->drv_priv;
++	ret = wl12xx_allocate_link(wl, wlvif, &wl_sta->hlid);
++	if (ret < 0) {
++		wl1271_warning("could not allocate HLID - too many links");
++		return -EBUSY;
++	}
++
++	/* use the previous security seq, if this is a recovery/resume */
++	wl->links[wl_sta->hlid].total_freed_pkts = wl_sta->total_freed_pkts;
++
++	set_bit(wl_sta->hlid, wlvif->ap.sta_hlid_map);
++	memcpy(wl->links[wl_sta->hlid].addr, sta->addr, ETH_ALEN);
++	wl->active_sta_count++;
++	return 0;
++}
++
++void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid)
++{
++	struct wl1271_station *wl_sta;
++	struct ieee80211_sta *sta;
++	struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
++
++	if (!test_bit(hlid, wlvif->ap.sta_hlid_map))
++		return;
++
++	clear_bit(hlid, wlvif->ap.sta_hlid_map);
++	__clear_bit(hlid, &wl->ap_ps_map);
++	__clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
++
++	/*
++	 * save the last used PN in the private part of iee80211_sta,
++	 * in case of recovery/suspend
++	 */
++	rcu_read_lock();
++	sta = ieee80211_find_sta(vif, wl->links[hlid].addr);
++	if (sta) {
++		wl_sta = (void *)sta->drv_priv;
++		wl_sta->total_freed_pkts = wl->links[hlid].total_freed_pkts;
++
++		/*
++		 * increment the initial seq number on recovery to account for
++		 * transmitted packets that we haven't yet got in the FW status
++		 */
++		if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags))
++			wl_sta->total_freed_pkts +=
++					WL1271_TX_SQN_POST_RECOVERY_PADDING;
++	}
++	rcu_read_unlock();
++
++	wl12xx_free_link(wl, wlvif, &hlid);
++	wl->active_sta_count--;
++
++	/*
++	 * rearm the tx watchdog when the last STA is freed - give the FW a
++	 * chance to return STA-buffered packets before complaining.
++	 */
++	if (wl->active_sta_count == 0)
++		wl12xx_rearm_tx_watchdog_locked(wl);
++}
++
++static int wl12xx_sta_add(struct wl1271 *wl,
++			  struct wl12xx_vif *wlvif,
++			  struct ieee80211_sta *sta)
++{
++	struct wl1271_station *wl_sta;
++	int ret = 0;
++	u8 hlid;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 add sta %d", (int)sta->aid);
++
++	ret = wl1271_allocate_sta(wl, wlvif, sta);
++	if (ret < 0)
++		return ret;
++
++	wl_sta = (struct wl1271_station *)sta->drv_priv;
++	hlid = wl_sta->hlid;
++
++	ret = wl12xx_cmd_add_peer(wl, wlvif, sta, hlid);
++	if (ret < 0)
++		wl1271_free_sta(wl, wlvif, hlid);
++
++	return ret;
++}
++
++static int wl12xx_sta_remove(struct wl1271 *wl,
++			     struct wl12xx_vif *wlvif,
++			     struct ieee80211_sta *sta)
++{
++	struct wl1271_station *wl_sta;
++	int ret = 0, id;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 remove sta %d", (int)sta->aid);
++
++	wl_sta = (struct wl1271_station *)sta->drv_priv;
++	id = wl_sta->hlid;
++	if (WARN_ON(!test_bit(id, wlvif->ap.sta_hlid_map)))
++		return -EINVAL;
++
++	ret = wl12xx_cmd_remove_peer(wl, wl_sta->hlid);
++	if (ret < 0)
++		return ret;
++
++	wl1271_free_sta(wl, wlvif, wl_sta->hlid);
++	return ret;
++}
++
++static void wlcore_roc_if_possible(struct wl1271 *wl,
++				   struct wl12xx_vif *wlvif)
++{
++	if (find_first_bit(wl->roc_map,
++			   WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES)
++		return;
++
++	if (WARN_ON(wlvif->role_id == WL12XX_INVALID_ROLE_ID))
++		return;
++
++	wl12xx_roc(wl, wlvif, wlvif->role_id, wlvif->band, wlvif->channel);
++}
++
++static void wlcore_update_inconn_sta(struct wl1271 *wl,
++				     struct wl12xx_vif *wlvif,
++				     struct wl1271_station *wl_sta,
++				     bool in_connection)
++{
++	if (in_connection) {
++		if (WARN_ON(wl_sta->in_connection))
++			return;
++		wl_sta->in_connection = true;
++		if (!wlvif->inconn_count++)
++			wlcore_roc_if_possible(wl, wlvif);
++	} else {
++		if (!wl_sta->in_connection)
++			return;
++
++		wl_sta->in_connection = false;
++		wlvif->inconn_count--;
++		if (WARN_ON(wlvif->inconn_count < 0))
++			return;
++
++		if (!wlvif->inconn_count)
++			if (test_bit(wlvif->role_id, wl->roc_map))
++				wl12xx_croc(wl, wlvif->role_id);
++	}
++}
++
++static int wl12xx_update_sta_state(struct wl1271 *wl,
++				   struct wl12xx_vif *wlvif,
++				   struct ieee80211_sta *sta,
++				   enum ieee80211_sta_state old_state,
++				   enum ieee80211_sta_state new_state)
++{
++	struct wl1271_station *wl_sta;
++	bool is_ap = wlvif->bss_type == BSS_TYPE_AP_BSS;
++	bool is_sta = wlvif->bss_type == BSS_TYPE_STA_BSS;
++	int ret;
++
++	wl_sta = (struct wl1271_station *)sta->drv_priv;
++
++	/* Add station (AP mode) */
++	if (is_ap &&
++	    old_state == IEEE80211_STA_NOTEXIST &&
++	    new_state == IEEE80211_STA_NONE) {
++		ret = wl12xx_sta_add(wl, wlvif, sta);
++		if (ret)
++			return ret;
++
++		wlcore_update_inconn_sta(wl, wlvif, wl_sta, true);
++	}
++
++	/* Remove station (AP mode) */
++	if (is_ap &&
++	    old_state == IEEE80211_STA_NONE &&
++	    new_state == IEEE80211_STA_NOTEXIST) {
++		/* must not fail */
++		wl12xx_sta_remove(wl, wlvif, sta);
++
++		wlcore_update_inconn_sta(wl, wlvif, wl_sta, false);
++	}
++
++	/* Authorize station (AP mode) */
++	if (is_ap &&
++	    new_state == IEEE80211_STA_AUTHORIZED) {
++		ret = wl12xx_cmd_set_peer_state(wl, wlvif, wl_sta->hlid);
++		if (ret < 0)
++			return ret;
++
++		ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, true,
++						     wl_sta->hlid);
++		if (ret)
++			return ret;
++
++		wlcore_update_inconn_sta(wl, wlvif, wl_sta, false);
++	}
++
++	/* Authorize station */
++	if (is_sta &&
++	    new_state == IEEE80211_STA_AUTHORIZED) {
++		set_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags);
++		ret = wl12xx_set_authorized(wl, wlvif);
++		if (ret)
++			return ret;
++	}
++
++	if (is_sta &&
++	    old_state == IEEE80211_STA_AUTHORIZED &&
++	    new_state == IEEE80211_STA_ASSOC) {
++		clear_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags);
++		clear_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags);
++	}
++
++	/* clear ROCs on failure or authorization */
++	if (is_sta &&
++	    (new_state == IEEE80211_STA_AUTHORIZED ||
++	     new_state == IEEE80211_STA_NOTEXIST)) {
++		if (test_bit(wlvif->role_id, wl->roc_map))
++			wl12xx_croc(wl, wlvif->role_id);
++	}
++
++	if (is_sta &&
++	    old_state == IEEE80211_STA_NOTEXIST &&
++	    new_state == IEEE80211_STA_NONE) {
++		if (find_first_bit(wl->roc_map,
++				   WL12XX_MAX_ROLES) >= WL12XX_MAX_ROLES) {
++			WARN_ON(wlvif->role_id == WL12XX_INVALID_ROLE_ID);
++			wl12xx_roc(wl, wlvif, wlvif->role_id,
++				   wlvif->band, wlvif->channel);
++		}
++	}
++	return 0;
++}
++
++static int wl12xx_op_sta_state(struct ieee80211_hw *hw,
++			       struct ieee80211_vif *vif,
++			       struct ieee80211_sta *sta,
++			       enum ieee80211_sta_state old_state,
++			       enum ieee80211_sta_state new_state)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	int ret;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 sta %d state=%d->%d",
++		     sta->aid, old_state, new_state);
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON)) {
++		ret = -EBUSY;
++		goto out;
++	}
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wl12xx_update_sta_state(wl, wlvif, sta, old_state, new_state);
++
++	wl1271_ps_elp_sleep(wl);
++out:
++	mutex_unlock(&wl->mutex);
++	if (new_state < old_state)
++		return 0;
++	return ret;
++}
++
++static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
++				  struct ieee80211_vif *vif,
++				  enum ieee80211_ampdu_mlme_action action,
++				  struct ieee80211_sta *sta, u16 tid, u16 *ssn,
++				  u8 buf_size)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	int ret;
++	u8 hlid, *ba_bitmap;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 ampdu action %d tid %d", action,
++		     tid);
++
++	/* sanity check - the fields in FW are only 8bits wide */
++	if (WARN_ON(tid > 0xFF))
++		return -ENOTSUPP;
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON)) {
++		ret = -EAGAIN;
++		goto out;
++	}
++
++	if (wlvif->bss_type == BSS_TYPE_STA_BSS) {
++		hlid = wlvif->sta.hlid;
++	} else if (wlvif->bss_type == BSS_TYPE_AP_BSS) {
++		struct wl1271_station *wl_sta;
++
++		wl_sta = (struct wl1271_station *)sta->drv_priv;
++		hlid = wl_sta->hlid;
++	} else {
++		ret = -EINVAL;
++		goto out;
++	}
++
++	ba_bitmap = &wl->links[hlid].ba_bitmap;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 ampdu: Rx tid %d action %d",
++		     tid, action);
++
++	switch (action) {
++	case IEEE80211_AMPDU_RX_START:
++		if (!wlvif->ba_support || !wlvif->ba_allowed) {
++			ret = -ENOTSUPP;
++			break;
++		}
++
++		if (wl->ba_rx_session_count >= wl->ba_rx_session_count_max) {
++			ret = -EBUSY;
++			wl1271_error("exceeded max RX BA sessions");
++			break;
++		}
++
++		if (*ba_bitmap & BIT(tid)) {
++			ret = -EINVAL;
++			wl1271_error("cannot enable RX BA session on active "
++				     "tid: %d", tid);
++			break;
++		}
++
++		ret = wl12xx_acx_set_ba_receiver_session(wl, tid, *ssn, true,
++							 hlid);
++		if (!ret) {
++			*ba_bitmap |= BIT(tid);
++			wl->ba_rx_session_count++;
++		}
++		break;
++
++	case IEEE80211_AMPDU_RX_STOP:
++		if (!(*ba_bitmap & BIT(tid))) {
++			/*
++			 * this happens on reconfig - so only output a debug
++			 * message for now, and don't fail the function.
++			 */
++			wl1271_debug(DEBUG_MAC80211,
++				     "no active RX BA session on tid: %d",
++				     tid);
++			ret = 0;
++			break;
++		}
++
++		ret = wl12xx_acx_set_ba_receiver_session(wl, tid, 0, false,
++							 hlid);
++		if (!ret) {
++			*ba_bitmap &= ~BIT(tid);
++			wl->ba_rx_session_count--;
++		}
++		break;
++
++	/*
++	 * The BA initiator session management in FW independently.
++	 * Falling break here on purpose for all TX APDU commands.
++	 */
++	case IEEE80211_AMPDU_TX_START:
++	case IEEE80211_AMPDU_TX_STOP_CONT:
++	case IEEE80211_AMPDU_TX_STOP_FLUSH:
++	case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
++	case IEEE80211_AMPDU_TX_OPERATIONAL:
++		ret = -EINVAL;
++		break;
++
++	default:
++		wl1271_error("Incorrect ampdu action id=%x\n", action);
++		ret = -EINVAL;
++	}
++
++	wl1271_ps_elp_sleep(wl);
++
++out:
++	mutex_unlock(&wl->mutex);
++
++	return ret;
++}
++
++static int wl12xx_set_bitrate_mask(struct ieee80211_hw *hw,
++				   struct ieee80211_vif *vif,
++				   const struct cfg80211_bitrate_mask *mask)
++{
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	struct wl1271 *wl = hw->priv;
++	int i, ret = 0;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 set_bitrate_mask 0x%x 0x%x",
++		mask->control[NL80211_BAND_2GHZ].legacy,
++		mask->control[NL80211_BAND_5GHZ].legacy);
++
++	mutex_lock(&wl->mutex);
++
++	for (i = 0; i < WLCORE_NUM_BANDS; i++)
++		wlvif->bitrate_masks[i] =
++			wl1271_tx_enabled_rates_get(wl,
++						    mask->control[i].legacy,
++						    i);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	if (wlvif->bss_type == BSS_TYPE_STA_BSS &&
++	    !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) {
++
++		ret = wl1271_ps_elp_wakeup(wl);
++		if (ret < 0)
++			goto out;
++
++		wl1271_set_band_rate(wl, wlvif);
++		wlvif->basic_rate =
++			wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
++		ret = wl1271_acx_sta_rate_policies(wl, wlvif);
++
++		wl1271_ps_elp_sleep(wl);
++	}
++out:
++	mutex_unlock(&wl->mutex);
++
++	return ret;
++}
++
++static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
++				     struct ieee80211_channel_switch *ch_switch)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif;
++	int ret;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 channel switch");
++
++	wl1271_tx_flush(wl);
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state == WLCORE_STATE_OFF)) {
++		wl12xx_for_each_wlvif_sta(wl, wlvif) {
++			struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
++			ieee80211_chswitch_done(vif, false);
++		}
++		goto out;
++	} else if (unlikely(wl->state != WLCORE_STATE_ON)) {
++		goto out;
++	}
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	/* TODO: change mac80211 to pass vif as param */
++	wl12xx_for_each_wlvif_sta(wl, wlvif) {
++		unsigned long delay_usec;
++
++		ret = wl->ops->channel_switch(wl, wlvif, ch_switch);
++		if (ret)
++			goto out_sleep;
++
++		set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags);
++
++		/* indicate failure 5 seconds after channel switch time */
++		delay_usec = ieee80211_tu_to_usec(wlvif->beacon_int) *
++			     ch_switch->count;
++		ieee80211_queue_delayed_work(hw, &wlvif->channel_switch_work,
++				usecs_to_jiffies(delay_usec) +
++				msecs_to_jiffies(5000));
++	}
++
++out_sleep:
++	wl1271_ps_elp_sleep(wl);
++
++out:
++	mutex_unlock(&wl->mutex);
++}
++
++static void wlcore_op_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
++{
++	struct wl1271 *wl = hw->priv;
++
++	wl1271_tx_flush(wl);
++}
++
++static int wlcore_op_remain_on_channel(struct ieee80211_hw *hw,
++				       struct ieee80211_vif *vif,
++				       struct ieee80211_channel *chan,
++				       int duration,
++				       enum ieee80211_roc_type type)
++{
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	struct wl1271 *wl = hw->priv;
++	int channel, ret = 0;
++
++	channel = ieee80211_frequency_to_channel(chan->center_freq);
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 roc %d (%d)",
++		     channel, wlvif->role_id);
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	/* return EBUSY if we can't ROC right now */
++	if (WARN_ON(wl->roc_vif ||
++		    find_first_bit(wl->roc_map,
++				   WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES)) {
++		ret = -EBUSY;
++		goto out;
++	}
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wl12xx_start_dev(wl, wlvif, chan->band, channel);
++	if (ret < 0)
++		goto out_sleep;
++
++	wl->roc_vif = vif;
++	ieee80211_queue_delayed_work(hw, &wl->roc_complete_work,
++				     msecs_to_jiffies(duration));
++out_sleep:
++	wl1271_ps_elp_sleep(wl);
++out:
++	mutex_unlock(&wl->mutex);
++	return ret;
++}
++
++static int __wlcore_roc_completed(struct wl1271 *wl)
++{
++	struct wl12xx_vif *wlvif;
++	int ret;
++
++	/* already completed */
++	if (unlikely(!wl->roc_vif))
++		return 0;
++
++	wlvif = wl12xx_vif_to_data(wl->roc_vif);
++
++	if (!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))
++		return -EBUSY;
++
++	ret = wl12xx_stop_dev(wl, wlvif);
++	if (ret < 0)
++		return ret;
++
++	wl->roc_vif = NULL;
++
++	return 0;
++}
++
++static int wlcore_roc_completed(struct wl1271 *wl)
++{
++	int ret;
++
++	wl1271_debug(DEBUG_MAC80211, "roc complete");
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON)) {
++		ret = -EBUSY;
++		goto out;
++	}
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = __wlcore_roc_completed(wl);
++
++	wl1271_ps_elp_sleep(wl);
++out:
++	mutex_unlock(&wl->mutex);
++
++	return ret;
++}
++
++static void wlcore_roc_complete_work(struct work_struct *work)
++{
++	struct delayed_work *dwork;
++	struct wl1271 *wl;
++	int ret;
++
++	dwork = container_of(work, struct delayed_work, work);
++	wl = container_of(dwork, struct wl1271, roc_complete_work);
++
++	ret = wlcore_roc_completed(wl);
++	if (!ret)
++		ieee80211_remain_on_channel_expired(wl->hw);
++}
++
++static int wlcore_op_cancel_remain_on_channel(struct ieee80211_hw *hw)
++{
++	struct wl1271 *wl = hw->priv;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 croc");
++
++	/* TODO: per-vif */
++	wl1271_tx_flush(wl);
++
++	/*
++	 * we can't just flush_work here, because it might deadlock
++	 * (as we might get called from the same workqueue)
++	 */
++	cancel_delayed_work_sync(&wl->roc_complete_work);
++	wlcore_roc_completed(wl);
++
++	return 0;
++}
++
++static void wlcore_op_sta_rc_update(struct ieee80211_hw *hw,
++				    struct ieee80211_vif *vif,
++				    struct ieee80211_sta *sta,
++				    u32 changed)
++{
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	struct wl1271 *wl = hw->priv;
++
++	wlcore_hw_sta_rc_update(wl, wlvif, sta, changed);
++}
++
++static int wlcore_op_get_rssi(struct ieee80211_hw *hw,
++			       struct ieee80211_vif *vif,
++			       struct ieee80211_sta *sta,
++			       s8 *rssi_dbm)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	int ret = 0;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 get_rssi");
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out_sleep;
++
++	ret = wlcore_acx_average_rssi(wl, wlvif, rssi_dbm);
++	if (ret < 0)
++		goto out_sleep;
++
++out_sleep:
++	wl1271_ps_elp_sleep(wl);
++
++out:
++	mutex_unlock(&wl->mutex);
++
++	return ret;
++}
++
++static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw)
++{
++	struct wl1271 *wl = hw->priv;
++	bool ret = false;
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	/* packets are considered pending if in the TX queue or the FW */
++	ret = (wl1271_tx_total_queue_count(wl) > 0) || (wl->tx_frames_cnt > 0);
++out:
++	mutex_unlock(&wl->mutex);
++
++	return ret;
++}
++
++/* can't be const, mac80211 writes to this */
++static struct ieee80211_rate wl1271_rates[] = {
++	{ .bitrate = 10,
++	  .hw_value = CONF_HW_BIT_RATE_1MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_1MBPS, },
++	{ .bitrate = 20,
++	  .hw_value = CONF_HW_BIT_RATE_2MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_2MBPS,
++	  .flags = IEEE80211_RATE_SHORT_PREAMBLE },
++	{ .bitrate = 55,
++	  .hw_value = CONF_HW_BIT_RATE_5_5MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_5_5MBPS,
++	  .flags = IEEE80211_RATE_SHORT_PREAMBLE },
++	{ .bitrate = 110,
++	  .hw_value = CONF_HW_BIT_RATE_11MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_11MBPS,
++	  .flags = IEEE80211_RATE_SHORT_PREAMBLE },
++	{ .bitrate = 60,
++	  .hw_value = CONF_HW_BIT_RATE_6MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
++	{ .bitrate = 90,
++	  .hw_value = CONF_HW_BIT_RATE_9MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
++	{ .bitrate = 120,
++	  .hw_value = CONF_HW_BIT_RATE_12MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
++	{ .bitrate = 180,
++	  .hw_value = CONF_HW_BIT_RATE_18MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
++	{ .bitrate = 240,
++	  .hw_value = CONF_HW_BIT_RATE_24MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
++	{ .bitrate = 360,
++	 .hw_value = CONF_HW_BIT_RATE_36MBPS,
++	 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
++	{ .bitrate = 480,
++	  .hw_value = CONF_HW_BIT_RATE_48MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
++	{ .bitrate = 540,
++	  .hw_value = CONF_HW_BIT_RATE_54MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
++};
++
++/* can't be const, mac80211 writes to this */
++static struct ieee80211_channel wl1271_channels[] = {
++	{ .hw_value = 1, .center_freq = 2412, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 2, .center_freq = 2417, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 3, .center_freq = 2422, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 4, .center_freq = 2427, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 5, .center_freq = 2432, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 6, .center_freq = 2437, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 7, .center_freq = 2442, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 8, .center_freq = 2447, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 9, .center_freq = 2452, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 10, .center_freq = 2457, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 11, .center_freq = 2462, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 12, .center_freq = 2467, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 13, .center_freq = 2472, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 14, .center_freq = 2484, .max_power = WLCORE_MAX_TXPWR },
++};
++
++/* can't be const, mac80211 writes to this */
++static struct ieee80211_supported_band wl1271_band_2ghz = {
++	.channels = wl1271_channels,
++	.n_channels = ARRAY_SIZE(wl1271_channels),
++	.bitrates = wl1271_rates,
++	.n_bitrates = ARRAY_SIZE(wl1271_rates),
++};
++
++/* 5 GHz data rates for WL1273 */
++static struct ieee80211_rate wl1271_rates_5ghz[] = {
++	{ .bitrate = 60,
++	  .hw_value = CONF_HW_BIT_RATE_6MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
++	{ .bitrate = 90,
++	  .hw_value = CONF_HW_BIT_RATE_9MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
++	{ .bitrate = 120,
++	  .hw_value = CONF_HW_BIT_RATE_12MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
++	{ .bitrate = 180,
++	  .hw_value = CONF_HW_BIT_RATE_18MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
++	{ .bitrate = 240,
++	  .hw_value = CONF_HW_BIT_RATE_24MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
++	{ .bitrate = 360,
++	 .hw_value = CONF_HW_BIT_RATE_36MBPS,
++	 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
++	{ .bitrate = 480,
++	  .hw_value = CONF_HW_BIT_RATE_48MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
++	{ .bitrate = 540,
++	  .hw_value = CONF_HW_BIT_RATE_54MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
++};
++
++/* 5 GHz band channels for WL1273 */
++static struct ieee80211_channel wl1271_channels_5ghz[] = {
++	{ .hw_value = 7, .center_freq = 5035, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 8, .center_freq = 5040, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 9, .center_freq = 5045, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 11, .center_freq = 5055, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 12, .center_freq = 5060, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 16, .center_freq = 5080, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 34, .center_freq = 5170, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 36, .center_freq = 5180, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 38, .center_freq = 5190, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 40, .center_freq = 5200, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 42, .center_freq = 5210, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 44, .center_freq = 5220, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 46, .center_freq = 5230, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 48, .center_freq = 5240, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 52, .center_freq = 5260, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 56, .center_freq = 5280, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 60, .center_freq = 5300, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 64, .center_freq = 5320, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 100, .center_freq = 5500, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 104, .center_freq = 5520, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 108, .center_freq = 5540, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 112, .center_freq = 5560, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 116, .center_freq = 5580, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 120, .center_freq = 5600, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 124, .center_freq = 5620, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 128, .center_freq = 5640, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 132, .center_freq = 5660, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 136, .center_freq = 5680, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 140, .center_freq = 5700, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 149, .center_freq = 5745, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 153, .center_freq = 5765, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 157, .center_freq = 5785, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 161, .center_freq = 5805, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 165, .center_freq = 5825, .max_power = WLCORE_MAX_TXPWR },
++};
++
++static struct ieee80211_supported_band wl1271_band_5ghz = {
++	.channels = wl1271_channels_5ghz,
++	.n_channels = ARRAY_SIZE(wl1271_channels_5ghz),
++	.bitrates = wl1271_rates_5ghz,
++	.n_bitrates = ARRAY_SIZE(wl1271_rates_5ghz),
++};
++
++static const struct ieee80211_ops wl1271_ops = {
++	.start = wl1271_op_start,
++	.stop = wlcore_op_stop,
++	.add_interface = wl1271_op_add_interface,
++	.remove_interface = wl1271_op_remove_interface,
++	.change_interface = wl12xx_op_change_interface,
++#ifdef CONFIG_PM
++	.suspend = wl1271_op_suspend,
++	.resume = wl1271_op_resume,
++#endif
++	.config = wl1271_op_config,
++	.prepare_multicast = wl1271_op_prepare_multicast,
++	.configure_filter = wl1271_op_configure_filter,
++	.tx = wl1271_op_tx,
++	.set_key = wlcore_op_set_key,
++	.hw_scan = wl1271_op_hw_scan,
++	.cancel_hw_scan = wl1271_op_cancel_hw_scan,
++	.sched_scan_start = wl1271_op_sched_scan_start,
++	.sched_scan_stop = wl1271_op_sched_scan_stop,
++	.bss_info_changed = wl1271_op_bss_info_changed,
++	.set_frag_threshold = wl1271_op_set_frag_threshold,
++	.set_rts_threshold = wl1271_op_set_rts_threshold,
++	.conf_tx = wl1271_op_conf_tx,
++	.get_tsf = wl1271_op_get_tsf,
++	.get_survey = wl1271_op_get_survey,
++	.sta_state = wl12xx_op_sta_state,
++	.ampdu_action = wl1271_op_ampdu_action,
++	.tx_frames_pending = wl1271_tx_frames_pending,
++	.set_bitrate_mask = wl12xx_set_bitrate_mask,
++	.channel_switch = wl12xx_op_channel_switch,
++	.flush = wlcore_op_flush,
++	.remain_on_channel = wlcore_op_remain_on_channel,
++	.cancel_remain_on_channel = wlcore_op_cancel_remain_on_channel,
++	.add_chanctx = wlcore_op_add_chanctx,
++	.remove_chanctx = wlcore_op_remove_chanctx,
++	.change_chanctx = wlcore_op_change_chanctx,
++	.assign_vif_chanctx = wlcore_op_assign_vif_chanctx,
++	.unassign_vif_chanctx = wlcore_op_unassign_vif_chanctx,
++	.sta_rc_update = wlcore_op_sta_rc_update,
++	.get_rssi = wlcore_op_get_rssi,
++	CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
++};
++
++
++u8 wlcore_rate_to_idx(struct wl1271 *wl, u8 rate, enum ieee80211_band band)
++{
++	u8 idx;
++
++	BUG_ON(band >= 2);
++
++	if (unlikely(rate >= wl->hw_tx_rate_tbl_size)) {
++		wl1271_error("Illegal RX rate from HW: %d", rate);
++		return 0;
++	}
++
++	idx = wl->band_rate_to_idx[band][rate];
++	if (unlikely(idx == CONF_HW_RXTX_RATE_UNSUPPORTED)) {
++		wl1271_error("Unsupported RX rate from HW: %d", rate);
++		return 0;
++	}
++
++	return idx;
++}
++
++static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev,
++					       struct device_attribute *attr,
++					       char *buf)
++{
++	struct wl1271 *wl = dev_get_drvdata(dev);
++	ssize_t len;
++
++	len = PAGE_SIZE;
++
++	mutex_lock(&wl->mutex);
++	len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n",
++		       wl->sg_enabled);
++	mutex_unlock(&wl->mutex);
++
++	return len;
++
++}
++
++static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev,
++						struct device_attribute *attr,
++						const char *buf, size_t count)
++{
++	struct wl1271 *wl = dev_get_drvdata(dev);
++	unsigned long res;
++	int ret;
++
++	ret = kstrtoul(buf, 10, &res);
++	if (ret < 0) {
++		wl1271_warning("incorrect value written to bt_coex_mode");
++		return count;
++	}
++
++	mutex_lock(&wl->mutex);
++
++	res = !!res;
++
++	if (res == wl->sg_enabled)
++		goto out;
++
++	wl->sg_enabled = res;
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	wl1271_acx_sg_enable(wl, wl->sg_enabled);
++	wl1271_ps_elp_sleep(wl);
++
++ out:
++	mutex_unlock(&wl->mutex);
++	return count;
++}
++
++static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR,
++		   wl1271_sysfs_show_bt_coex_state,
++		   wl1271_sysfs_store_bt_coex_state);
++
++static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev,
++					   struct device_attribute *attr,
++					   char *buf)
++{
++	struct wl1271 *wl = dev_get_drvdata(dev);
++	ssize_t len;
++
++	len = PAGE_SIZE;
++
++	mutex_lock(&wl->mutex);
++	if (wl->hw_pg_ver >= 0)
++		len = snprintf(buf, len, "%d\n", wl->hw_pg_ver);
++	else
++		len = snprintf(buf, len, "n/a\n");
++	mutex_unlock(&wl->mutex);
++
++	return len;
++}
++
++static DEVICE_ATTR(hw_pg_ver, S_IRUGO,
++		   wl1271_sysfs_show_hw_pg_ver, NULL);
++
++static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj,
++				       struct bin_attribute *bin_attr,
++				       char *buffer, loff_t pos, size_t count)
++{
++	struct device *dev = container_of(kobj, struct device, kobj);
++	struct wl1271 *wl = dev_get_drvdata(dev);
++	ssize_t len;
++	int ret;
++
++	ret = mutex_lock_interruptible(&wl->mutex);
++	if (ret < 0)
++		return -ERESTARTSYS;
++
++	/* Let only one thread read the log at a time, blocking others */
++	while (wl->fwlog_size == 0) {
++		DEFINE_WAIT(wait);
++
++		prepare_to_wait_exclusive(&wl->fwlog_waitq,
++					  &wait,
++					  TASK_INTERRUPTIBLE);
++
++		if (wl->fwlog_size != 0) {
++			finish_wait(&wl->fwlog_waitq, &wait);
++			break;
++		}
++
++		mutex_unlock(&wl->mutex);
++
++		schedule();
++		finish_wait(&wl->fwlog_waitq, &wait);
++
++		if (signal_pending(current))
++			return -ERESTARTSYS;
++
++		ret = mutex_lock_interruptible(&wl->mutex);
++		if (ret < 0)
++			return -ERESTARTSYS;
++	}
++
++	/* Check if the fwlog is still valid */
++	if (wl->fwlog_size < 0) {
++		mutex_unlock(&wl->mutex);
++		return 0;
++	}
++
++	/* Seeking is not supported - old logs are not kept. Disregard pos. */
++	len = min(count, (size_t)wl->fwlog_size);
++	wl->fwlog_size -= len;
++	memcpy(buffer, wl->fwlog, len);
++
++	/* Make room for new messages */
++	memmove(wl->fwlog, wl->fwlog + len, wl->fwlog_size);
++
++	mutex_unlock(&wl->mutex);
++
++	return len;
++}
++
++static struct bin_attribute fwlog_attr = {
++	.attr = {.name = "fwlog", .mode = S_IRUSR},
++	.read = wl1271_sysfs_read_fwlog,
++};
++
++static void wl12xx_derive_mac_addresses(struct wl1271 *wl, u32 oui, u32 nic)
++{
++	int i;
++
++	wl1271_debug(DEBUG_PROBE, "base address: oui %06x nic %06x",
++		     oui, nic);
++
++	if (nic + WLCORE_NUM_MAC_ADDRESSES - wl->num_mac_addr > 0xffffff)
++		wl1271_warning("NIC part of the MAC address wraps around!");
++
++	for (i = 0; i < wl->num_mac_addr; i++) {
++		wl->addresses[i].addr[0] = (u8)(oui >> 16);
++		wl->addresses[i].addr[1] = (u8)(oui >> 8);
++		wl->addresses[i].addr[2] = (u8) oui;
++		wl->addresses[i].addr[3] = (u8)(nic >> 16);
++		wl->addresses[i].addr[4] = (u8)(nic >> 8);
++		wl->addresses[i].addr[5] = (u8) nic;
++		nic++;
++	}
++
++	/* we may be one address short at the most */
++	WARN_ON(wl->num_mac_addr + 1 < WLCORE_NUM_MAC_ADDRESSES);
++
++	/*
++	 * turn on the LAA bit in the first address and use it as
++	 * the last address.
++	 */
++	if (wl->num_mac_addr < WLCORE_NUM_MAC_ADDRESSES) {
++		int idx = WLCORE_NUM_MAC_ADDRESSES - 1;
++		memcpy(&wl->addresses[idx], &wl->addresses[0],
++		       sizeof(wl->addresses[0]));
++		/* LAA bit */
++		wl->addresses[idx].addr[2] |= BIT(1);
++	}
++
++	wl->hw->wiphy->n_addresses = WLCORE_NUM_MAC_ADDRESSES;
++	wl->hw->wiphy->addresses = wl->addresses;
++}
++
++static int wl12xx_get_hw_info(struct wl1271 *wl)
++{
++	int ret;
++
++	ret = wl12xx_set_power_on(wl);
++	if (ret < 0)
++		return ret;
++
++	ret = wlcore_read_reg(wl, REG_CHIP_ID_B, &wl->chip.id);
++	if (ret < 0)
++		goto out;
++
++	wl->fuse_oui_addr = 0;
++	wl->fuse_nic_addr = 0;
++
++	ret = wl->ops->get_pg_ver(wl, &wl->hw_pg_ver);
++	if (ret < 0)
++		goto out;
++
++	if (wl->ops->get_mac)
++		ret = wl->ops->get_mac(wl);
++
++out:
++	wl1271_power_off(wl);
++	return ret;
++}
++
++static int wl1271_register_hw(struct wl1271 *wl)
++{
++	int ret;
++	u32 oui_addr = 0, nic_addr = 0;
++
++	if (wl->mac80211_registered)
++		return 0;
++
++	if (wl->nvs_len >= 12) {
++		/* NOTE: The wl->nvs->nvs element must be first, in
++		 * order to simplify the casting, we assume it is at
++		 * the beginning of the wl->nvs structure.
++		 */
++		u8 *nvs_ptr = (u8 *)wl->nvs;
++
++		oui_addr =
++			(nvs_ptr[11] << 16) + (nvs_ptr[10] << 8) + nvs_ptr[6];
++		nic_addr =
++			(nvs_ptr[5] << 16) + (nvs_ptr[4] << 8) + nvs_ptr[3];
++	}
++
++	/* if the MAC address is zeroed in the NVS derive from fuse */
++	if (oui_addr == 0 && nic_addr == 0) {
++		oui_addr = wl->fuse_oui_addr;
++		/* fuse has the BD_ADDR, the WLAN addresses are the next two */
++		nic_addr = wl->fuse_nic_addr + 1;
++	}
++
++	wl12xx_derive_mac_addresses(wl, oui_addr, nic_addr);
++
++	ret = ieee80211_register_hw(wl->hw);
++	if (ret < 0) {
++		wl1271_error("unable to register mac80211 hw: %d", ret);
++		goto out;
++	}
++
++	wl->mac80211_registered = true;
++
++	wl1271_debugfs_init(wl);
++
++	wl1271_notice("loaded");
++
++out:
++	return ret;
++}
++
++static void wl1271_unregister_hw(struct wl1271 *wl)
++{
++	if (wl->plt)
++		wl1271_plt_stop(wl);
++
++	ieee80211_unregister_hw(wl->hw);
++	wl->mac80211_registered = false;
++
++}
++
++static const struct ieee80211_iface_limit wlcore_iface_limits[] = {
++	{
++		.max = 3,
++		.types = BIT(NL80211_IFTYPE_STATION),
++	},
++	{
++		.max = 1,
++		.types = BIT(NL80211_IFTYPE_AP) |
++			 BIT(NL80211_IFTYPE_P2P_GO) |
++			 BIT(NL80211_IFTYPE_P2P_CLIENT),
++	},
++};
++
++static struct ieee80211_iface_combination
++wlcore_iface_combinations[] = {
++	{
++	  .max_interfaces = 3,
++	  .limits = wlcore_iface_limits,
++	  .n_limits = ARRAY_SIZE(wlcore_iface_limits),
++	},
++};
++
++static int wl1271_init_ieee80211(struct wl1271 *wl)
++{
++	int i;
++	static const u32 cipher_suites[] = {
++		WLAN_CIPHER_SUITE_WEP40,
++		WLAN_CIPHER_SUITE_WEP104,
++		WLAN_CIPHER_SUITE_TKIP,
++		WLAN_CIPHER_SUITE_CCMP,
++		WL1271_CIPHER_SUITE_GEM,
++	};
++
++	/* The tx descriptor buffer */
++	wl->hw->extra_tx_headroom = sizeof(struct wl1271_tx_hw_descr);
++
++	if (wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE)
++		wl->hw->extra_tx_headroom += WL1271_EXTRA_SPACE_TKIP;
++
++	/* unit us */
++	/* FIXME: find a proper value */
++	wl->hw->channel_change_time = 10000;
++	wl->hw->max_listen_interval = wl->conf.conn.max_listen_interval;
++
++	wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
++		IEEE80211_HW_SUPPORTS_PS |
++		IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
++		IEEE80211_HW_SUPPORTS_UAPSD |
++		IEEE80211_HW_HAS_RATE_CONTROL |
++		IEEE80211_HW_CONNECTION_MONITOR |
++		IEEE80211_HW_REPORTS_TX_ACK_STATUS |
++		IEEE80211_HW_SPECTRUM_MGMT |
++		IEEE80211_HW_AP_LINK_PS |
++		IEEE80211_HW_AMPDU_AGGREGATION |
++		IEEE80211_HW_TX_AMPDU_SETUP_IN_HW |
++		IEEE80211_HW_QUEUE_CONTROL;
++
++	wl->hw->wiphy->cipher_suites = cipher_suites;
++	wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
++
++	wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
++		BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP) |
++		BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO);
++	wl->hw->wiphy->max_scan_ssids = 1;
++	wl->hw->wiphy->max_sched_scan_ssids = 16;
++	wl->hw->wiphy->max_match_sets = 16;
++	/*
++	 * Maximum length of elements in scanning probe request templates
++	 * should be the maximum length possible for a template, without
++	 * the IEEE80211 header of the template
++	 */
++	wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE -
++			sizeof(struct ieee80211_header);
++
++	wl->hw->wiphy->max_sched_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE -
++		sizeof(struct ieee80211_header);
++
++	wl->hw->wiphy->max_remain_on_channel_duration = 5000;
++
++	wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD |
++				WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
++
++	/* make sure all our channels fit in the scanned_ch bitmask */
++	BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) +
++		     ARRAY_SIZE(wl1271_channels_5ghz) >
++		     WL1271_MAX_CHANNELS);
++	/*
++	* clear channel flags from the previous usage
++	* and restore max_power & max_antenna_gain values.
++	*/
++	for (i = 0; i < ARRAY_SIZE(wl1271_channels); i++) {
++		wl1271_band_2ghz.channels[i].flags = 0;
++		wl1271_band_2ghz.channels[i].max_power = WLCORE_MAX_TXPWR;
++		wl1271_band_2ghz.channels[i].max_antenna_gain = 0;
++	}
++
++	for (i = 0; i < ARRAY_SIZE(wl1271_channels_5ghz); i++) {
++		wl1271_band_5ghz.channels[i].flags = 0;
++		wl1271_band_5ghz.channels[i].max_power = WLCORE_MAX_TXPWR;
++		wl1271_band_5ghz.channels[i].max_antenna_gain = 0;
++	}
++
++	/*
++	 * We keep local copies of the band structs because we need to
++	 * modify them on a per-device basis.
++	 */
++	memcpy(&wl->bands[IEEE80211_BAND_2GHZ], &wl1271_band_2ghz,
++	       sizeof(wl1271_band_2ghz));
++	memcpy(&wl->bands[IEEE80211_BAND_2GHZ].ht_cap,
++	       &wl->ht_cap[IEEE80211_BAND_2GHZ],
++	       sizeof(*wl->ht_cap));
++	memcpy(&wl->bands[IEEE80211_BAND_5GHZ], &wl1271_band_5ghz,
++	       sizeof(wl1271_band_5ghz));
++	memcpy(&wl->bands[IEEE80211_BAND_5GHZ].ht_cap,
++	       &wl->ht_cap[IEEE80211_BAND_5GHZ],
++	       sizeof(*wl->ht_cap));
++
++	wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
++		&wl->bands[IEEE80211_BAND_2GHZ];
++	wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
++		&wl->bands[IEEE80211_BAND_5GHZ];
++
++	/*
++	 * allow 4 queues per mac address we support +
++	 * 1 cab queue per mac + one global offchannel Tx queue
++	 */
++	wl->hw->queues = (NUM_TX_QUEUES + 1) * WLCORE_NUM_MAC_ADDRESSES + 1;
++
++	/* the last queue is the offchannel queue */
++	wl->hw->offchannel_tx_hw_queue = wl->hw->queues - 1;
++	wl->hw->max_rates = 1;
++
++	wl->hw->wiphy->reg_notifier = wl1271_reg_notify;
++
++	/* the FW answers probe-requests in AP-mode */
++	wl->hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
++	wl->hw->wiphy->probe_resp_offload =
++		NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
++		NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
++		NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
++
++	/* allowed interface combinations */
++	wlcore_iface_combinations[0].num_different_channels = wl->num_channels;
++	wl->hw->wiphy->iface_combinations = wlcore_iface_combinations;
++	wl->hw->wiphy->n_iface_combinations =
++		ARRAY_SIZE(wlcore_iface_combinations);
++
++	SET_IEEE80211_DEV(wl->hw, wl->dev);
++
++	wl->hw->sta_data_size = sizeof(struct wl1271_station);
++	wl->hw->vif_data_size = sizeof(struct wl12xx_vif);
++
++	wl->hw->max_rx_aggregation_subframes = wl->conf.ht.rx_ba_win_size;
++
++	return 0;
++}
++
++#define WL1271_DEFAULT_CHANNEL 0
++
++struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
++				     u32 mbox_size)
++{
++	struct ieee80211_hw *hw;
++	struct wl1271 *wl;
++	int i, j, ret;
++	unsigned int order;
++
++	BUILD_BUG_ON(AP_MAX_STATIONS > WL12XX_MAX_LINKS);
++
++	hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops);
++	if (!hw) {
++		wl1271_error("could not alloc ieee80211_hw");
++		ret = -ENOMEM;
++		goto err_hw_alloc;
++	}
++
++	wl = hw->priv;
++	memset(wl, 0, sizeof(*wl));
++
++	wl->priv = kzalloc(priv_size, GFP_KERNEL);
++	if (!wl->priv) {
++		wl1271_error("could not alloc wl priv");
++		ret = -ENOMEM;
++		goto err_priv_alloc;
++	}
++
++	INIT_LIST_HEAD(&wl->wlvif_list);
++
++	wl->hw = hw;
++
++	for (i = 0; i < NUM_TX_QUEUES; i++)
++		for (j = 0; j < WL12XX_MAX_LINKS; j++)
++			skb_queue_head_init(&wl->links[j].tx_queue[i]);
++
++	skb_queue_head_init(&wl->deferred_rx_queue);
++	skb_queue_head_init(&wl->deferred_tx_queue);
++
++	INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work);
++	INIT_WORK(&wl->netstack_work, wl1271_netstack_work);
++	INIT_WORK(&wl->tx_work, wl1271_tx_work);
++	INIT_WORK(&wl->recovery_work, wl1271_recovery_work);
++	INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work);
++	INIT_DELAYED_WORK(&wl->roc_complete_work, wlcore_roc_complete_work);
++	INIT_DELAYED_WORK(&wl->tx_watchdog_work, wl12xx_tx_watchdog_work);
++
++	wl->freezable_wq = create_freezable_workqueue("wl12xx_wq");
++	if (!wl->freezable_wq) {
++		ret = -ENOMEM;
++		goto err_hw;
++	}
++
++	wl->channel = WL1271_DEFAULT_CHANNEL;
++	wl->rx_counter = 0;
++	wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
++	wl->band = IEEE80211_BAND_2GHZ;
++	wl->channel_type = NL80211_CHAN_NO_HT;
++	wl->flags = 0;
++	wl->sg_enabled = true;
++	wl->sleep_auth = WL1271_PSM_ILLEGAL;
++	wl->recovery_count = 0;
++	wl->hw_pg_ver = -1;
++	wl->ap_ps_map = 0;
++	wl->ap_fw_ps_map = 0;
++	wl->quirks = 0;
++	wl->platform_quirks = 0;
++	wl->system_hlid = WL12XX_SYSTEM_HLID;
++	wl->active_sta_count = 0;
++	wl->active_link_count = 0;
++	wl->fwlog_size = 0;
++	init_waitqueue_head(&wl->fwlog_waitq);
++
++	/* The system link is always allocated */
++	__set_bit(WL12XX_SYSTEM_HLID, wl->links_map);
++
++	memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map));
++	for (i = 0; i < wl->num_tx_desc; i++)
++		wl->tx_frames[i] = NULL;
++
++	spin_lock_init(&wl->wl_lock);
++
++	wl->state = WLCORE_STATE_OFF;
++	wl->fw_type = WL12XX_FW_TYPE_NONE;
++	mutex_init(&wl->mutex);
++	mutex_init(&wl->flush_mutex);
++	init_completion(&wl->nvs_loading_complete);
++
++	order = get_order(aggr_buf_size);
++	wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order);
++	if (!wl->aggr_buf) {
++		ret = -ENOMEM;
++		goto err_wq;
++	}
++	wl->aggr_buf_size = aggr_buf_size;
++
++	wl->dummy_packet = wl12xx_alloc_dummy_packet(wl);
++	if (!wl->dummy_packet) {
++		ret = -ENOMEM;
++		goto err_aggr;
++	}
++
++	/* Allocate one page for the FW log */
++	wl->fwlog = (u8 *)get_zeroed_page(GFP_KERNEL);
++	if (!wl->fwlog) {
++		ret = -ENOMEM;
++		goto err_dummy_packet;
++	}
++
++	wl->mbox_size = mbox_size;
++	wl->mbox = kmalloc(wl->mbox_size, GFP_KERNEL | GFP_DMA);
++	if (!wl->mbox) {
++		ret = -ENOMEM;
++		goto err_fwlog;
++	}
++
++	wl->buffer_32 = kmalloc(sizeof(*wl->buffer_32), GFP_KERNEL);
++	if (!wl->buffer_32) {
++		ret = -ENOMEM;
++		goto err_mbox;
++	}
++
++	return hw;
++
++err_mbox:
++	kfree(wl->mbox);
++
++err_fwlog:
++	free_page((unsigned long)wl->fwlog);
++
++err_dummy_packet:
++	dev_kfree_skb(wl->dummy_packet);
++
++err_aggr:
++	free_pages((unsigned long)wl->aggr_buf, order);
++
++err_wq:
++	destroy_workqueue(wl->freezable_wq);
++
++err_hw:
++	wl1271_debugfs_exit(wl);
++	kfree(wl->priv);
++
++err_priv_alloc:
++	ieee80211_free_hw(hw);
++
++err_hw_alloc:
++
++	return ERR_PTR(ret);
++}
++EXPORT_SYMBOL_GPL(wlcore_alloc_hw);
++
++int wlcore_free_hw(struct wl1271 *wl)
++{
++	/* Unblock any fwlog readers */
++	mutex_lock(&wl->mutex);
++	wl->fwlog_size = -1;
++	wake_up_interruptible_all(&wl->fwlog_waitq);
++	mutex_unlock(&wl->mutex);
++
++	device_remove_bin_file(wl->dev, &fwlog_attr);
++
++	device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
++
++	device_remove_file(wl->dev, &dev_attr_bt_coex_state);
++	kfree(wl->buffer_32);
++	kfree(wl->mbox);
++	free_page((unsigned long)wl->fwlog);
++	dev_kfree_skb(wl->dummy_packet);
++	free_pages((unsigned long)wl->aggr_buf, get_order(wl->aggr_buf_size));
++
++	wl1271_debugfs_exit(wl);
++
++	vfree(wl->fw);
++	wl->fw = NULL;
++	wl->fw_type = WL12XX_FW_TYPE_NONE;
++	kfree(wl->nvs);
++	wl->nvs = NULL;
++
++	kfree(wl->fw_status_1);
++	kfree(wl->tx_res_if);
++	destroy_workqueue(wl->freezable_wq);
++
++	kfree(wl->priv);
++	ieee80211_free_hw(wl->hw);
++
++	return 0;
++}
++EXPORT_SYMBOL_GPL(wlcore_free_hw);
++
++static void wlcore_nvs_cb(const struct firmware *fw, void *context)
++{
++	struct wl1271 *wl = context;
++	struct platform_device *pdev = wl->pdev;
++	struct wlcore_platdev_data *pdev_data = pdev->dev.platform_data;
++	struct wl12xx_platform_data *pdata = pdev_data->pdata;
++	unsigned long irqflags;
++	int ret;
++
++	if (fw) {
++		wl->nvs = kmemdup(fw->data, fw->size, GFP_KERNEL);
++		if (!wl->nvs) {
++			wl1271_error("Could not allocate nvs data");
++			goto out;
++		}
++		wl->nvs_len = fw->size;
++	} else {
++		wl1271_debug(DEBUG_BOOT, "Could not get nvs file %s",
++			     WL12XX_NVS_NAME);
++		wl->nvs = NULL;
++		wl->nvs_len = 0;
++	}
++
++	ret = wl->ops->setup(wl);
++	if (ret < 0)
++		goto out_free_nvs;
++
++	BUG_ON(wl->num_tx_desc > WLCORE_MAX_TX_DESCRIPTORS);
++
++	/* adjust some runtime configuration parameters */
++	wlcore_adjust_conf(wl);
++
++	wl->irq = platform_get_irq(pdev, 0);
++	wl->platform_quirks = pdata->platform_quirks;
++	wl->if_ops = pdev_data->if_ops;
++
++	if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ)
++		irqflags = IRQF_TRIGGER_RISING;
++	else
++		irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT;
++
++	ret = request_threaded_irq(wl->irq, NULL, wlcore_irq,
++				   irqflags, pdev->name, wl);
++	if (ret < 0) {
++		wl1271_error("request_irq() failed: %d", ret);
++		goto out_free_nvs;
++	}
++
++#ifdef CONFIG_PM
++	ret = enable_irq_wake(wl->irq);
++	if (!ret) {
++		wl->irq_wake_enabled = true;
++		device_init_wakeup(wl->dev, 1);
++		if (pdev_data->pwr_in_suspend) {
++			wl->hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY;
++			wl->hw->wiphy->wowlan.n_patterns =
++				WL1271_MAX_RX_FILTERS;
++			wl->hw->wiphy->wowlan.pattern_min_len = 1;
++			wl->hw->wiphy->wowlan.pattern_max_len =
++				WL1271_RX_FILTER_MAX_PATTERN_SIZE;
++		}
++	}
++#endif
++	disable_irq(wl->irq);
++
++	ret = wl12xx_get_hw_info(wl);
++	if (ret < 0) {
++		wl1271_error("couldn't get hw info");
++		goto out_irq;
++	}
++
++	ret = wl->ops->identify_chip(wl);
++	if (ret < 0)
++		goto out_irq;
++
++	ret = wl1271_init_ieee80211(wl);
++	if (ret)
++		goto out_irq;
++
++	ret = wl1271_register_hw(wl);
++	if (ret)
++		goto out_irq;
++
++	/* Create sysfs file to control bt coex state */
++	ret = device_create_file(wl->dev, &dev_attr_bt_coex_state);
++	if (ret < 0) {
++		wl1271_error("failed to create sysfs file bt_coex_state");
++		goto out_unreg;
++	}
++
++	/* Create sysfs file to get HW PG version */
++	ret = device_create_file(wl->dev, &dev_attr_hw_pg_ver);
++	if (ret < 0) {
++		wl1271_error("failed to create sysfs file hw_pg_ver");
++		goto out_bt_coex_state;
++	}
++
++	/* Create sysfs file for the FW log */
++	ret = device_create_bin_file(wl->dev, &fwlog_attr);
++	if (ret < 0) {
++		wl1271_error("failed to create sysfs file fwlog");
++		goto out_hw_pg_ver;
++	}
++
++	wl->initialized = true;
++	goto out;
++
++out_hw_pg_ver:
++	device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
++
++out_bt_coex_state:
++	device_remove_file(wl->dev, &dev_attr_bt_coex_state);
++
++out_unreg:
++	wl1271_unregister_hw(wl);
++
++out_irq:
++	free_irq(wl->irq, wl);
++
++out_free_nvs:
++	kfree(wl->nvs);
++
++out:
++	release_firmware(fw);
++	complete_all(&wl->nvs_loading_complete);
++}
++
++int wlcore_probe(struct wl1271 *wl, struct platform_device *pdev)
++{
++	int ret;
++
++	if (!wl->ops || !wl->ptable)
++		return -EINVAL;
++
++	wl->dev = &pdev->dev;
++	wl->pdev = pdev;
++	platform_set_drvdata(pdev, wl);
++
++	ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
++				      WL12XX_NVS_NAME, &pdev->dev, GFP_KERNEL,
++				      wl, wlcore_nvs_cb);
++	if (ret < 0) {
++		wl1271_error("request_firmware_nowait failed: %d", ret);
++		complete_all(&wl->nvs_loading_complete);
++	}
++
++	return ret;
++}
++EXPORT_SYMBOL_GPL(wlcore_probe);
++
++int wlcore_remove(struct platform_device *pdev)
++{
++	struct wl1271 *wl = platform_get_drvdata(pdev);
++
++	wait_for_completion(&wl->nvs_loading_complete);
++	if (!wl->initialized)
++		return 0;
++
++	if (wl->irq_wake_enabled) {
++		device_init_wakeup(wl->dev, 0);
++		disable_irq_wake(wl->irq);
++	}
++	wl1271_unregister_hw(wl);
++	free_irq(wl->irq, wl);
++	wlcore_free_hw(wl);
++
++	return 0;
++}
++EXPORT_SYMBOL_GPL(wlcore_remove);
++
++u32 wl12xx_debug_level = DEBUG_NONE;
++EXPORT_SYMBOL_GPL(wl12xx_debug_level);
++module_param_named(debug_level, wl12xx_debug_level, uint, S_IRUSR | S_IWUSR);
++MODULE_PARM_DESC(debug_level, "wl12xx debugging level");
++
++module_param_named(fwlog, fwlog_param, charp, 0);
++MODULE_PARM_DESC(fwlog,
++		 "FW logger options: continuous, ondemand, dbgpins or disable");
++
++module_param(bug_on_recovery, int, S_IRUSR | S_IWUSR);
++MODULE_PARM_DESC(bug_on_recovery, "BUG() on fw recovery");
++
++module_param(no_recovery, int, S_IRUSR | S_IWUSR);
++MODULE_PARM_DESC(no_recovery, "Prevent HW recovery. FW will remain stuck.");
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Luciano Coelho <coelho at ti.com>");
++MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen at nokia.com>");
++MODULE_FIRMWARE(WL12XX_NVS_NAME);
+diff -Nuar git.old/drivers/net/wireless/ti/wlcore/sdio.c git/drivers/net/wireless/ti/wlcore/sdio.c
+--- git.old/drivers/net/wireless/ti/wlcore/sdio.c	2015-03-04 16:35:05.722185997 +0100
++++ git/drivers/net/wireless/ti/wlcore/sdio.c	2015-03-04 17:07:29.614167458 +0100
+@@ -30,10 +30,11 @@
+ #include <linux/mmc/sdio_ids.h>
+ #include <linux/mmc/card.h>
+ #include <linux/mmc/host.h>
+-#include <linux/gpio.h>
++#include <linux/of_irq.h>
+ #include <linux/wl12xx.h>
+ #include <linux/pm_runtime.h>
+ #include <linux/printk.h>
++#include <linux/clk-provider.h>
+ 
+ #include "wlcore.h"
+ #include "wl12xx_80211.h"
+@@ -52,6 +53,7 @@
+ struct wl12xx_sdio_glue {
+ 	struct device *dev;
+ 	struct platform_device *core;
++	struct clk *refclock, *tcxoclock;
+ };
+ 
+ static const struct sdio_device_id wl1271_devices[] = {
+@@ -214,6 +216,68 @@
+ 	.set_block_size = wl1271_sdio_set_block_size,
+ };
+ 
++static const struct of_device_id wlcore_sdio_of_clk_match_table[] = {
++	{ .compatible = "ti,wilink-clock" },
++};
++
++static struct wl12xx_platform_data *wlcore_get_pdata_from_of(struct device *dev)
++{
++	struct wl12xx_platform_data *pdata;
++	struct device_node *np = dev->of_node;
++	struct device_node *clock_node;
++	struct wl12xx_sdio_glue *glue = sdio_get_drvdata(dev_to_sdio_func(dev));
++
++	if (!np) {
++		np = of_find_matching_node(NULL, dev->driver->of_match_table);
++		if (!np) {
++			dev_notice(dev, "device tree node not available\n");
++			pdata = ERR_PTR(-ENODEV);
++			goto out;
++		}
++	}
++
++	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
++	if (!pdata) {
++		dev_err(dev, "can't allocate platform data\n");
++		pdata = ERR_PTR(-ENODEV);
++		goto out;
++	}
++
++	pdata->irq = irq_of_parse_and_map(np, 0);
++	if (pdata->irq < 0) {
++		dev_err(dev, "can't get interrupt gpio from the device tree\n");
++		goto out_free;
++	}
++
++	for_each_matching_node(clock_node, wlcore_sdio_of_clk_match_table)
++		of_fixed_clk_setup(clock_node);
++
++	glue->refclock = of_clk_get_by_name(np, "refclock");
++	if (IS_ERR(glue->refclock)) {
++		glue->refclock = NULL;
++	} else {
++		clk_prepare_enable(glue->refclock);
++		pdata->ref_clock_freq = clk_get_rate(glue->refclock);
++	}
++
++	glue->tcxoclock = of_clk_get_by_name(np, "tcxoclock");
++	if (IS_ERR(glue->tcxoclock)) {
++		glue->tcxoclock = NULL;
++	} else {
++		clk_prepare_enable(glue->tcxoclock);
++		pdata->ref_clock_freq = clk_get_rate(glue->tcxoclock);
++	}
++
++	goto out;
++
++out_free:
++	kfree(pdata);
++	pdata = ERR_PTR(-ENODEV);
++
++out:
++	return pdata;
++}
++
+ static int wl1271_probe(struct sdio_func *func,
+ 				  const struct sdio_device_id *id)
+ {
+@@ -248,11 +312,24 @@
+ 	/* Use block mode for transferring over one block size of data */
+ 	func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE;
+ 
++	sdio_set_drvdata(func, glue);
++
++	/* The pdata allocated here is freed when the device is freed,
++	 * so we don't need an additional out label to free it in case
++	 * of error further on.
++	 */
++
++	/* Try to get legacy platform data from the board file */
+ 	pdev_data->pdata = wl12xx_get_platform_data();
+ 	if (IS_ERR(pdev_data->pdata)) {
+-		ret = PTR_ERR(pdev_data->pdata);
+-		dev_err(glue->dev, "missing wlan platform data: %d\n", ret);
+-		goto out_free_glue;
++		dev_info(&func->dev,
++			 "legacy platform data not found, trying device tree\n");
++
++		pdev_data->pdata = wlcore_get_pdata_from_of(&func->dev);
++		if (IS_ERR(pdev_data->pdata)) {
++			dev_err(&func->dev, "can't get platform data\n");
++			goto out_free_glue;
++		}
+ 	}
+ 
+ 	/* if sdio can keep power while host is suspended, enable wow */
+@@ -260,9 +337,7 @@
+ 	dev_dbg(glue->dev, "sdio PM caps = 0x%x\n", mmcflags);
+ 
+ 	if (mmcflags & MMC_PM_KEEP_POWER)
+-		pdev_data->pdata->pwr_in_suspend = true;
+-
+-	sdio_set_drvdata(func, glue);
++		pdev_data->pwr_in_suspend = true;
+ 
+ 	/* Tell PM core that we don't need the card to be powered now */
+ 	pm_runtime_put_noidle(&func->dev);
+@@ -330,6 +405,16 @@
+ {
+ 	struct wl12xx_sdio_glue *glue = sdio_get_drvdata(func);
+ 
++	if (glue->refclock) {
++		clk_disable_unprepare(glue->refclock);
++		clk_put(glue->refclock);
++	}
++
++	if (glue->tcxoclock) {
++		clk_disable_unprepare(glue->tcxoclock);
++		clk_put(glue->tcxoclock);
++	}
++
+ 	/* Undo decrement done above in wl1271_probe */
+ 	pm_runtime_get_noresume(&func->dev);
+ 
+@@ -386,16 +471,25 @@
+ };
+ #endif
+ 
++static const struct of_device_id wlcore_sdio_of_match_table[] = {
++	{ .compatible = "ti,wilink6" },
++	{ .compatible = "ti,wilink7" },
++	{ .compatible = "ti,wilink8" },
++	{ }
++};
++MODULE_DEVICE_TABLE(of, wlcore_sdio_of_match_table);
++
+ static struct sdio_driver wl1271_sdio_driver = {
+ 	.name		= "wl1271_sdio",
+ 	.id_table	= wl1271_devices,
+ 	.probe		= wl1271_probe,
+ 	.remove		= wl1271_remove,
+-#ifdef CONFIG_PM
+ 	.drv = {
++#ifdef CONFIG_PM
+ 		.pm = &wl1271_sdio_pm_ops,
+-	},
+ #endif
++		.of_match_table = of_match_ptr(wlcore_sdio_of_match_table),
++	},
+ };
+ 
+ static int __init wl1271_init(void)
+diff -Nuar git.old/drivers/net/wireless/ti/wlcore/sdio.c.orig git/drivers/net/wireless/ti/wlcore/sdio.c.orig
+--- git.old/drivers/net/wireless/ti/wlcore/sdio.c.orig	1970-01-01 01:00:00.000000000 +0100
++++ git/drivers/net/wireless/ti/wlcore/sdio.c.orig	2015-03-04 16:37:26.010184659 +0100
+@@ -0,0 +1,419 @@
++/*
++ * This file is part of wl1271
++ *
++ * Copyright (C) 2009-2010 Nokia Corporation
++ *
++ * Contact: Luciano Coelho <luciano.coelho at nokia.com>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++ * 02110-1301 USA
++ *
++ */
++
++#include <linux/irq.h>
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/platform_device.h>
++#include <linux/mmc/sdio.h>
++#include <linux/mmc/sdio_func.h>
++#include <linux/mmc/sdio_ids.h>
++#include <linux/mmc/card.h>
++#include <linux/mmc/host.h>
++#include <linux/gpio.h>
++#include <linux/wl12xx.h>
++#include <linux/pm_runtime.h>
++#include <linux/printk.h>
++
++#include "wlcore.h"
++#include "wl12xx_80211.h"
++#include "io.h"
++
++#ifndef SDIO_VENDOR_ID_TI
++#define SDIO_VENDOR_ID_TI		0x0097
++#endif
++
++#ifndef SDIO_DEVICE_ID_TI_WL1271
++#define SDIO_DEVICE_ID_TI_WL1271	0x4076
++#endif
++
++static bool dump = false;
++
++struct wl12xx_sdio_glue {
++	struct device *dev;
++	struct platform_device *core;
++};
++
++static const struct sdio_device_id wl1271_devices[] = {
++	{ SDIO_DEVICE(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271) },
++	{}
++};
++MODULE_DEVICE_TABLE(sdio, wl1271_devices);
++
++static void wl1271_sdio_set_block_size(struct device *child,
++				       unsigned int blksz)
++{
++	struct wl12xx_sdio_glue *glue = dev_get_drvdata(child->parent);
++	struct sdio_func *func = dev_to_sdio_func(glue->dev);
++
++	sdio_claim_host(func);
++	sdio_set_block_size(func, blksz);
++	sdio_release_host(func);
++}
++
++static int __must_check wl12xx_sdio_raw_read(struct device *child, int addr,
++					     void *buf, size_t len, bool fixed)
++{
++	int ret;
++	struct wl12xx_sdio_glue *glue = dev_get_drvdata(child->parent);
++	struct sdio_func *func = dev_to_sdio_func(glue->dev);
++
++	sdio_claim_host(func);
++
++	if (unlikely(dump)) {
++		printk(KERN_DEBUG "wlcore_sdio: READ from 0x%04x\n", addr);
++		print_hex_dump(KERN_DEBUG, "wlcore_sdio: READ ",
++				DUMP_PREFIX_OFFSET, 16, 1,
++				buf, len, false);
++	}
++
++	if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG)) {
++		((u8 *)buf)[0] = sdio_f0_readb(func, addr, &ret);
++		dev_dbg(child->parent, "sdio read 52 addr 0x%x, byte 0x%02x\n",
++			addr, ((u8 *)buf)[0]);
++	} else {
++		if (fixed)
++			ret = sdio_readsb(func, buf, addr, len);
++		else
++			ret = sdio_memcpy_fromio(func, buf, addr, len);
++
++		dev_dbg(child->parent, "sdio read 53 addr 0x%x, %zu bytes\n",
++			addr, len);
++	}
++
++	sdio_release_host(func);
++
++	if (WARN_ON(ret))
++		dev_err(child->parent, "sdio read failed (%d)\n", ret);
++
++	return ret;
++}
++
++static int __must_check wl12xx_sdio_raw_write(struct device *child, int addr,
++					      void *buf, size_t len, bool fixed)
++{
++	int ret;
++	struct wl12xx_sdio_glue *glue = dev_get_drvdata(child->parent);
++	struct sdio_func *func = dev_to_sdio_func(glue->dev);
++
++	sdio_claim_host(func);
++
++	if (unlikely(dump)) {
++		printk(KERN_DEBUG "wlcore_sdio: WRITE to 0x%04x\n", addr);
++		print_hex_dump(KERN_DEBUG, "wlcore_sdio: WRITE ",
++				DUMP_PREFIX_OFFSET, 16, 1,
++				buf, len, false);
++	}
++
++	if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG)) {
++		sdio_f0_writeb(func, ((u8 *)buf)[0], addr, &ret);
++		dev_dbg(child->parent, "sdio write 52 addr 0x%x, byte 0x%02x\n",
++			addr, ((u8 *)buf)[0]);
++	} else {
++		dev_dbg(child->parent, "sdio write 53 addr 0x%x, %zu bytes\n",
++			addr, len);
++
++		if (fixed)
++			ret = sdio_writesb(func, addr, buf, len);
++		else
++			ret = sdio_memcpy_toio(func, addr, buf, len);
++	}
++
++	sdio_release_host(func);
++
++	if (WARN_ON(ret))
++		dev_err(child->parent, "sdio write failed (%d)\n", ret);
++
++	return ret;
++}
++
++static int wl12xx_sdio_power_on(struct wl12xx_sdio_glue *glue)
++{
++	int ret;
++	struct sdio_func *func = dev_to_sdio_func(glue->dev);
++	struct mmc_card *card = func->card;
++
++	ret = pm_runtime_get_sync(&card->dev);
++	if (ret) {
++		/*
++		 * Runtime PM might be temporarily disabled, or the device
++		 * might have a positive reference counter. Make sure it is
++		 * really powered on.
++		 */
++		ret = mmc_power_restore_host(card->host);
++		if (ret < 0) {
++			pm_runtime_put_sync(&card->dev);
++			goto out;
++		}
++	}
++
++	sdio_claim_host(func);
++	sdio_enable_func(func);
++	sdio_release_host(func);
++
++out:
++	return ret;
++}
++
++static int wl12xx_sdio_power_off(struct wl12xx_sdio_glue *glue)
++{
++	int ret;
++	struct sdio_func *func = dev_to_sdio_func(glue->dev);
++	struct mmc_card *card = func->card;
++
++	sdio_claim_host(func);
++	sdio_disable_func(func);
++	sdio_release_host(func);
++
++	/* Power off the card manually in case it wasn't powered off above */
++	ret = mmc_power_save_host(card->host);
++	if (ret < 0)
++		goto out;
++
++	/* Let runtime PM know the card is powered off */
++	pm_runtime_put_sync(&card->dev);
++
++out:
++	return ret;
++}
++
++static int wl12xx_sdio_set_power(struct device *child, bool enable)
++{
++	struct wl12xx_sdio_glue *glue = dev_get_drvdata(child->parent);
++
++	if (enable)
++		return wl12xx_sdio_power_on(glue);
++	else
++		return wl12xx_sdio_power_off(glue);
++}
++
++static struct wl1271_if_operations sdio_ops = {
++	.read		= wl12xx_sdio_raw_read,
++	.write		= wl12xx_sdio_raw_write,
++	.power		= wl12xx_sdio_set_power,
++	.set_block_size = wl1271_sdio_set_block_size,
++};
++
++static int wl1271_probe(struct sdio_func *func,
++				  const struct sdio_device_id *id)
++{
++	struct wlcore_platdev_data *pdev_data;
++	struct wl12xx_sdio_glue *glue;
++	struct resource res[1];
++	mmc_pm_flag_t mmcflags;
++	int ret = -ENOMEM;
++	const char *chip_family;
++
++	/* We are only able to handle the wlan function */
++	if (func->num != 0x02)
++		return -ENODEV;
++
++	pdev_data = kzalloc(sizeof(*pdev_data), GFP_KERNEL);
++	if (!pdev_data)
++		goto out;
++
++	pdev_data->if_ops = &sdio_ops;
++
++	glue = kzalloc(sizeof(*glue), GFP_KERNEL);
++	if (!glue) {
++		dev_err(&func->dev, "can't allocate glue\n");
++		goto out_free_pdev_data;
++	}
++
++	glue->dev = &func->dev;
++
++	/* Grab access to FN0 for ELP reg. */
++	func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
++
++	/* Use block mode for transferring over one block size of data */
++	func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE;
++
++	pdev_data->pdata = wl12xx_get_platform_data();
++	if (IS_ERR(pdev_data->pdata)) {
++		ret = PTR_ERR(pdev_data->pdata);
++		dev_err(glue->dev, "missing wlan platform data: %d\n", ret);
++		goto out_free_glue;
++	}
++
++	/* if sdio can keep power while host is suspended, enable wow */
++	mmcflags = sdio_get_host_pm_caps(func);
++	dev_dbg(glue->dev, "sdio PM caps = 0x%x\n", mmcflags);
++
++	if (mmcflags & MMC_PM_KEEP_POWER)
++		pdev_data->pwr_in_suspend = true;
++
++	sdio_set_drvdata(func, glue);
++
++	/* Tell PM core that we don't need the card to be powered now */
++	pm_runtime_put_noidle(&func->dev);
++
++	/*
++	 * Due to a hardware bug, we can't differentiate wl18xx from
++	 * wl12xx, because both report the same device ID.  The only
++	 * way to differentiate is by checking the SDIO revision,
++	 * which is 3.00 on the wl18xx chips.
++	 */
++	if (func->card->cccr.sdio_vsn == SDIO_SDIO_REV_3_00)
++		chip_family = "wl18xx";
++	else
++		chip_family = "wl12xx";
++
++	glue->core = platform_device_alloc(chip_family, PLATFORM_DEVID_AUTO);
++	if (!glue->core) {
++		dev_err(glue->dev, "can't allocate platform_device");
++		ret = -ENOMEM;
++		goto out_free_glue;
++	}
++
++	glue->core->dev.parent = &func->dev;
++
++	memset(res, 0x00, sizeof(res));
++
++	res[0].start = pdev_data->pdata->irq;
++	res[0].flags = IORESOURCE_IRQ;
++	res[0].name = "irq";
++
++	ret = platform_device_add_resources(glue->core, res, ARRAY_SIZE(res));
++	if (ret) {
++		dev_err(glue->dev, "can't add resources\n");
++		goto out_dev_put;
++	}
++
++	ret = platform_device_add_data(glue->core, pdev_data,
++				       sizeof(*pdev_data));
++	if (ret) {
++		dev_err(glue->dev, "can't add platform data\n");
++		goto out_dev_put;
++	}
++
++	ret = platform_device_add(glue->core);
++	if (ret) {
++		dev_err(glue->dev, "can't add platform device\n");
++		goto out_dev_put;
++	}
++	return 0;
++
++out_dev_put:
++	platform_device_put(glue->core);
++
++out_free_glue:
++	kfree(glue);
++
++out_free_pdev_data:
++	kfree(pdev_data);
++
++out:
++	return ret;
++}
++
++static void wl1271_remove(struct sdio_func *func)
++{
++	struct wl12xx_sdio_glue *glue = sdio_get_drvdata(func);
++
++	/* Undo decrement done above in wl1271_probe */
++	pm_runtime_get_noresume(&func->dev);
++
++	platform_device_unregister(glue->core);
++	kfree(glue);
++}
++
++#ifdef CONFIG_PM
++static int wl1271_suspend(struct device *dev)
++{
++	/* Tell MMC/SDIO core it's OK to power down the card
++	 * (if it isn't already), but not to remove it completely */
++	struct sdio_func *func = dev_to_sdio_func(dev);
++	struct wl12xx_sdio_glue *glue = sdio_get_drvdata(func);
++	struct wl1271 *wl = platform_get_drvdata(glue->core);
++	mmc_pm_flag_t sdio_flags;
++	int ret = 0;
++
++	dev_dbg(dev, "wl1271 suspend. wow_enabled: %d\n",
++		wl->wow_enabled);
++
++	/* check whether sdio should keep power */
++	if (wl->wow_enabled) {
++		sdio_flags = sdio_get_host_pm_caps(func);
++
++		if (!(sdio_flags & MMC_PM_KEEP_POWER)) {
++			dev_err(dev, "can't keep power while host "
++				     "is suspended\n");
++			ret = -EINVAL;
++			goto out;
++		}
++
++		/* keep power while host suspended */
++		ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
++		if (ret) {
++			dev_err(dev, "error while trying to keep power\n");
++			goto out;
++		}
++	}
++out:
++	return ret;
++}
++
++static int wl1271_resume(struct device *dev)
++{
++	dev_dbg(dev, "wl1271 resume\n");
++
++	return 0;
++}
++
++static const struct dev_pm_ops wl1271_sdio_pm_ops = {
++	.suspend	= wl1271_suspend,
++	.resume		= wl1271_resume,
++};
++#endif
++
++static struct sdio_driver wl1271_sdio_driver = {
++	.name		= "wl1271_sdio",
++	.id_table	= wl1271_devices,
++	.probe		= wl1271_probe,
++	.remove		= wl1271_remove,
++#ifdef CONFIG_PM
++	.drv = {
++		.pm = &wl1271_sdio_pm_ops,
++	},
++#endif
++};
++
++static int __init wl1271_init(void)
++{
++	return sdio_register_driver(&wl1271_sdio_driver);
++}
++
++static void __exit wl1271_exit(void)
++{
++	sdio_unregister_driver(&wl1271_sdio_driver);
++}
++
++module_init(wl1271_init);
++module_exit(wl1271_exit);
++
++module_param(dump, bool, S_IRUSR | S_IWUSR);
++MODULE_PARM_DESC(dump, "Enable sdio read/write dumps.");
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Luciano Coelho <coelho at ti.com>");
++MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen at nokia.com>");
+diff -Nuar git.old/drivers/net/wireless/ti/wlcore/wlcore.h git/drivers/net/wireless/ti/wlcore/wlcore.h
+--- git.old/drivers/net/wireless/ti/wlcore/wlcore.h	2015-03-04 16:35:05.722185997 +0100
++++ git/drivers/net/wireless/ti/wlcore/wlcore.h	2015-03-04 16:49:20.830177842 +0100
+@@ -185,6 +185,8 @@
+ 
+ 	int irq;
+ 
++	int irq_flags;
++
+ 	spinlock_t wl_lock;
+ 
+ 	enum wlcore_state state;
+@@ -384,9 +386,6 @@
+ 	/* Quirks of specific hardware revisions */
+ 	unsigned int quirks;
+ 
+-	/* Platform limitations */
+-	unsigned int platform_quirks;
+-
+ 	/* number of currently active RX BA sessions */
+ 	int ba_rx_session_count;
+ 
+diff -Nuar git.old/drivers/net/wireless/ti/wlcore/wlcore.h.orig git/drivers/net/wireless/ti/wlcore/wlcore.h.orig
+--- git.old/drivers/net/wireless/ti/wlcore/wlcore.h.orig	1970-01-01 01:00:00.000000000 +0100
++++ git/drivers/net/wireless/ti/wlcore/wlcore.h.orig	2015-03-04 16:35:05.722185997 +0100
+@@ -0,0 +1,616 @@
++/*
++ * This file is part of wlcore
++ *
++ * Copyright (C) 2011 Texas Instruments Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++ * 02110-1301 USA
++ *
++ */
++
++#ifndef __WLCORE_H__
++#define __WLCORE_H__
++
++#include <linux/platform_device.h>
++
++#include "wlcore_i.h"
++#include "event.h"
++#include "boot.h"
++
++/* The maximum number of Tx descriptors in all chip families */
++#define WLCORE_MAX_TX_DESCRIPTORS 32
++
++/*
++ * We always allocate this number of mac addresses. If we don't
++ * have enough allocated addresses, the LAA bit is used
++ */
++#define WLCORE_NUM_MAC_ADDRESSES 3
++
++/* wl12xx/wl18xx maximum transmission power (in dBm) */
++#define WLCORE_MAX_TXPWR        25
++
++/* forward declaration */
++struct wl1271_tx_hw_descr;
++enum wl_rx_buf_align;
++struct wl1271_rx_descriptor;
++
++struct wlcore_ops {
++	int (*setup)(struct wl1271 *wl);
++	int (*identify_chip)(struct wl1271 *wl);
++	int (*identify_fw)(struct wl1271 *wl);
++	int (*boot)(struct wl1271 *wl);
++	int (*plt_init)(struct wl1271 *wl);
++	int (*trigger_cmd)(struct wl1271 *wl, int cmd_box_addr,
++			   void *buf, size_t len);
++	int (*ack_event)(struct wl1271 *wl);
++	int (*wait_for_event)(struct wl1271 *wl, enum wlcore_wait_event event,
++			      bool *timeout);
++	int (*process_mailbox_events)(struct wl1271 *wl);
++	u32 (*calc_tx_blocks)(struct wl1271 *wl, u32 len, u32 spare_blks);
++	void (*set_tx_desc_blocks)(struct wl1271 *wl,
++				   struct wl1271_tx_hw_descr *desc,
++				   u32 blks, u32 spare_blks);
++	void (*set_tx_desc_data_len)(struct wl1271 *wl,
++				     struct wl1271_tx_hw_descr *desc,
++				     struct sk_buff *skb);
++	enum wl_rx_buf_align (*get_rx_buf_align)(struct wl1271 *wl,
++						 u32 rx_desc);
++	int (*prepare_read)(struct wl1271 *wl, u32 rx_desc, u32 len);
++	u32 (*get_rx_packet_len)(struct wl1271 *wl, void *rx_data,
++				 u32 data_len);
++	int (*tx_delayed_compl)(struct wl1271 *wl);
++	void (*tx_immediate_compl)(struct wl1271 *wl);
++	int (*hw_init)(struct wl1271 *wl);
++	int (*init_vif)(struct wl1271 *wl, struct wl12xx_vif *wlvif);
++	u32 (*sta_get_ap_rate_mask)(struct wl1271 *wl,
++				    struct wl12xx_vif *wlvif);
++	int (*get_pg_ver)(struct wl1271 *wl, s8 *ver);
++	int (*get_mac)(struct wl1271 *wl);
++	void (*set_tx_desc_csum)(struct wl1271 *wl,
++				 struct wl1271_tx_hw_descr *desc,
++				 struct sk_buff *skb);
++	void (*set_rx_csum)(struct wl1271 *wl,
++			    struct wl1271_rx_descriptor *desc,
++			    struct sk_buff *skb);
++	u32 (*ap_get_mimo_wide_rate_mask)(struct wl1271 *wl,
++					  struct wl12xx_vif *wlvif);
++	int (*debugfs_init)(struct wl1271 *wl, struct dentry *rootdir);
++	int (*handle_static_data)(struct wl1271 *wl,
++				  struct wl1271_static_data *static_data);
++	int (*scan_start)(struct wl1271 *wl, struct wl12xx_vif *wlvif,
++			  struct cfg80211_scan_request *req);
++	int (*scan_stop)(struct wl1271 *wl, struct wl12xx_vif *wlvif);
++	int (*sched_scan_start)(struct wl1271 *wl, struct wl12xx_vif *wlvif,
++				struct cfg80211_sched_scan_request *req,
++				struct ieee80211_sched_scan_ies *ies);
++	void (*sched_scan_stop)(struct wl1271 *wl, struct wl12xx_vif *wlvif);
++	int (*get_spare_blocks)(struct wl1271 *wl, bool is_gem);
++	int (*set_key)(struct wl1271 *wl, enum set_key_cmd cmd,
++		       struct ieee80211_vif *vif,
++		       struct ieee80211_sta *sta,
++		       struct ieee80211_key_conf *key_conf);
++	int (*channel_switch)(struct wl1271 *wl,
++			      struct wl12xx_vif *wlvif,
++			      struct ieee80211_channel_switch *ch_switch);
++	u32 (*pre_pkt_send)(struct wl1271 *wl, u32 buf_offset, u32 last_len);
++	void (*sta_rc_update)(struct wl1271 *wl, struct wl12xx_vif *wlvif,
++			      struct ieee80211_sta *sta, u32 changed);
++	int (*set_peer_cap)(struct wl1271 *wl,
++			    struct ieee80211_sta_ht_cap *ht_cap,
++			    bool allow_ht_operation,
++			    u32 rate_set, u8 hlid);
++	bool (*lnk_high_prio)(struct wl1271 *wl, u8 hlid,
++			      struct wl1271_link *lnk);
++	bool (*lnk_low_prio)(struct wl1271 *wl, u8 hlid,
++			     struct wl1271_link *lnk);
++};
++
++enum wlcore_partitions {
++	PART_DOWN,
++	PART_WORK,
++	PART_BOOT,
++	PART_DRPW,
++	PART_TOP_PRCM_ELP_SOC,
++	PART_PHY_INIT,
++
++	PART_TABLE_LEN,
++};
++
++struct wlcore_partition {
++	u32 size;
++	u32 start;
++};
++
++struct wlcore_partition_set {
++	struct wlcore_partition mem;
++	struct wlcore_partition reg;
++	struct wlcore_partition mem2;
++	struct wlcore_partition mem3;
++};
++
++enum wlcore_registers {
++	/* register addresses, used with partition translation */
++	REG_ECPU_CONTROL,
++	REG_INTERRUPT_NO_CLEAR,
++	REG_INTERRUPT_ACK,
++	REG_COMMAND_MAILBOX_PTR,
++	REG_EVENT_MAILBOX_PTR,
++	REG_INTERRUPT_TRIG,
++	REG_INTERRUPT_MASK,
++	REG_PC_ON_RECOVERY,
++	REG_CHIP_ID_B,
++	REG_CMD_MBOX_ADDRESS,
++
++	/* data access memory addresses, used with partition translation */
++	REG_SLV_MEM_DATA,
++	REG_SLV_REG_DATA,
++
++	/* raw data access memory addresses */
++	REG_RAW_FW_STATUS_ADDR,
++
++	REG_TABLE_LEN,
++};
++
++struct wl1271_stats {
++	void *fw_stats;
++	unsigned long fw_stats_update;
++	size_t fw_stats_len;
++
++	unsigned int retry_count;
++	unsigned int excessive_retries;
++};
++
++struct wl1271 {
++	bool initialized;
++	struct ieee80211_hw *hw;
++	bool mac80211_registered;
++
++	struct device *dev;
++	struct platform_device *pdev;
++
++	void *if_priv;
++
++	struct wl1271_if_operations *if_ops;
++
++	int irq;
++
++	spinlock_t wl_lock;
++
++	enum wlcore_state state;
++	enum wl12xx_fw_type fw_type;
++	bool plt;
++	enum plt_mode plt_mode;
++	u8 fem_manuf;
++	u8 last_vif_count;
++	struct mutex mutex;
++
++	unsigned long flags;
++
++	struct wlcore_partition_set curr_part;
++
++	struct wl1271_chip chip;
++
++	int cmd_box_addr;
++
++	u8 *fw;
++	size_t fw_len;
++	void *nvs;
++	size_t nvs_len;
++
++	s8 hw_pg_ver;
++
++	/* address read from the fuse ROM */
++	u32 fuse_oui_addr;
++	u32 fuse_nic_addr;
++
++	/* we have up to 2 MAC addresses */
++	struct mac_address addresses[WLCORE_NUM_MAC_ADDRESSES];
++	int channel;
++	u8 system_hlid;
++
++	unsigned long links_map[BITS_TO_LONGS(WL12XX_MAX_LINKS)];
++	unsigned long roles_map[BITS_TO_LONGS(WL12XX_MAX_ROLES)];
++	unsigned long roc_map[BITS_TO_LONGS(WL12XX_MAX_ROLES)];
++	unsigned long rate_policies_map[
++			BITS_TO_LONGS(WL12XX_MAX_RATE_POLICIES)];
++	unsigned long klv_templates_map[
++			BITS_TO_LONGS(WLCORE_MAX_KLV_TEMPLATES)];
++
++	u8 session_ids[WL12XX_MAX_LINKS];
++
++	struct list_head wlvif_list;
++
++	u8 sta_count;
++	u8 ap_count;
++
++	struct wl1271_acx_mem_map *target_mem_map;
++
++	/* Accounting for allocated / available TX blocks on HW */
++	u32 tx_blocks_freed;
++	u32 tx_blocks_available;
++	u32 tx_allocated_blocks;
++	u32 tx_results_count;
++
++	/* Accounting for allocated / available Tx packets in HW */
++	u32 tx_pkts_freed[NUM_TX_QUEUES];
++	u32 tx_allocated_pkts[NUM_TX_QUEUES];
++
++	/* Transmitted TX packets counter for chipset interface */
++	u32 tx_packets_count;
++
++	/* Time-offset between host and chipset clocks */
++	s64 time_offset;
++
++	/* Frames scheduled for transmission, not handled yet */
++	int tx_queue_count[NUM_TX_QUEUES];
++	unsigned long queue_stop_reasons[
++				NUM_TX_QUEUES * WLCORE_NUM_MAC_ADDRESSES];
++
++	/* Frames received, not handled yet by mac80211 */
++	struct sk_buff_head deferred_rx_queue;
++
++	/* Frames sent, not returned yet to mac80211 */
++	struct sk_buff_head deferred_tx_queue;
++
++	struct work_struct tx_work;
++	struct workqueue_struct *freezable_wq;
++
++	/* Pending TX frames */
++	unsigned long tx_frames_map[BITS_TO_LONGS(WLCORE_MAX_TX_DESCRIPTORS)];
++	struct sk_buff *tx_frames[WLCORE_MAX_TX_DESCRIPTORS];
++	int tx_frames_cnt;
++
++	/* FW Rx counter */
++	u32 rx_counter;
++
++	/* Intermediate buffer, used for packet aggregation */
++	u8 *aggr_buf;
++	u32 aggr_buf_size;
++
++	/* Reusable dummy packet template */
++	struct sk_buff *dummy_packet;
++
++	/* Network stack work  */
++	struct work_struct netstack_work;
++
++	/* FW log buffer */
++	u8 *fwlog;
++
++	/* Number of valid bytes in the FW log buffer */
++	ssize_t fwlog_size;
++
++	/* Sysfs FW log entry readers wait queue */
++	wait_queue_head_t fwlog_waitq;
++
++	/* Hardware recovery work */
++	struct work_struct recovery_work;
++	bool watchdog_recovery;
++
++	/* Reg domain last configuration */
++	u32 reg_ch_conf_last[2];
++	/* Reg domain pending configuration */
++	u32 reg_ch_conf_pending[2];
++
++	/* Pointer that holds DMA-friendly block for the mailbox */
++	void *mbox;
++
++	/* The mbox event mask */
++	u32 event_mask;
++
++	/* Mailbox pointers */
++	u32 mbox_size;
++	u32 mbox_ptr[2];
++
++	/* Are we currently scanning */
++	struct wl12xx_vif *scan_wlvif;
++	struct wl1271_scan scan;
++	struct delayed_work scan_complete_work;
++
++	struct ieee80211_vif *roc_vif;
++	struct delayed_work roc_complete_work;
++
++	struct wl12xx_vif *sched_vif;
++
++	/* The current band */
++	enum ieee80211_band band;
++
++	struct completion *elp_compl;
++	struct delayed_work elp_work;
++
++	/* in dBm */
++	int power_level;
++
++	struct wl1271_stats stats;
++
++	__le32 *buffer_32;
++	u32 buffer_cmd;
++	u32 buffer_busyword[WL1271_BUSY_WORD_CNT];
++
++	struct wl_fw_status_1 *fw_status_1;
++	struct wl_fw_status_2 *fw_status_2;
++	struct wl1271_tx_hw_res_if *tx_res_if;
++
++	/* Current chipset configuration */
++	struct wlcore_conf conf;
++
++	bool sg_enabled;
++
++	bool enable_11a;
++
++	int recovery_count;
++
++	/* Most recently reported noise in dBm */
++	s8 noise;
++
++	/* bands supported by this instance of wl12xx */
++	struct ieee80211_supported_band bands[WLCORE_NUM_BANDS];
++
++	/*
++	 * wowlan trigger was configured during suspend.
++	 * (currently, only "ANY" trigger is supported)
++	 */
++	bool wow_enabled;
++	bool irq_wake_enabled;
++
++	/*
++	 * AP-mode - links indexed by HLID. The global and broadcast links
++	 * are always active.
++	 */
++	struct wl1271_link links[WL12XX_MAX_LINKS];
++
++	/* number of currently active links */
++	int active_link_count;
++
++	/* Fast/slow links bitmap according to FW */
++	u32 fw_fast_lnk_map;
++
++	/* AP-mode - a bitmap of links currently in PS mode according to FW */
++	u32 ap_fw_ps_map;
++
++	/* AP-mode - a bitmap of links currently in PS mode in mac80211 */
++	unsigned long ap_ps_map;
++
++	/* Quirks of specific hardware revisions */
++	unsigned int quirks;
++
++	/* Platform limitations */
++	unsigned int platform_quirks;
++
++	/* number of currently active RX BA sessions */
++	int ba_rx_session_count;
++
++	/* Maximum number of supported RX BA sessions */
++	int ba_rx_session_count_max;
++
++	/* AP-mode - number of currently connected stations */
++	int active_sta_count;
++
++	/* last wlvif we transmitted from */
++	struct wl12xx_vif *last_wlvif;
++
++	/* work to fire when Tx is stuck */
++	struct delayed_work tx_watchdog_work;
++
++	struct wlcore_ops *ops;
++	/* pointer to the lower driver partition table */
++	const struct wlcore_partition_set *ptable;
++	/* pointer to the lower driver register table */
++	const int *rtable;
++	/* name of the firmwares to load - for PLT, single role, multi-role */
++	const char *plt_fw_name;
++	const char *sr_fw_name;
++	const char *mr_fw_name;
++
++	u8 scan_templ_id_2_4;
++	u8 scan_templ_id_5;
++	u8 sched_scan_templ_id_2_4;
++	u8 sched_scan_templ_id_5;
++	u8 max_channels_5;
++
++	/* per-chip-family private structure */
++	void *priv;
++
++	/* number of TX descriptors the HW supports. */
++	u32 num_tx_desc;
++	/* number of RX descriptors the HW supports. */
++	u32 num_rx_desc;
++
++	/* translate HW Tx rates to standard rate-indices */
++	const u8 **band_rate_to_idx;
++
++	/* size of table for HW rates that can be received from chip */
++	u8 hw_tx_rate_tbl_size;
++
++	/* this HW rate and below are considered HT rates for this chip */
++	u8 hw_min_ht_rate;
++
++	/* HW HT (11n) capabilities */
++	struct ieee80211_sta_ht_cap ht_cap[WLCORE_NUM_BANDS];
++
++	/* size of the private FW status data */
++	size_t fw_status_priv_len;
++
++	/* RX Data filter rule state - enabled/disabled */
++	bool rx_filter_enabled[WL1271_MAX_RX_FILTERS];
++
++	/* size of the private static data */
++	size_t static_data_priv_len;
++
++	/* the current channel type */
++	enum nl80211_channel_type channel_type;
++
++	/* mutex for protecting the tx_flush function */
++	struct mutex flush_mutex;
++
++	/* sleep auth value currently configured to FW */
++	int sleep_auth;
++
++	/* the number of allocated MAC addresses in this chip */
++	int num_mac_addr;
++
++	/* minimum FW version required for the driver to work in single-role */
++	unsigned int min_sr_fw_ver[NUM_FW_VER];
++
++	/* minimum FW version required for the driver to work in multi-role */
++	unsigned int min_mr_fw_ver[NUM_FW_VER];
++
++	struct completion nvs_loading_complete;
++
++	/* number of concurrent channels the HW supports */
++	u32 num_channels;
++};
++
++int wlcore_probe(struct wl1271 *wl, struct platform_device *pdev);
++int wlcore_remove(struct platform_device *pdev);
++struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
++				     u32 mbox_size);
++int wlcore_free_hw(struct wl1271 *wl);
++int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
++		   struct ieee80211_vif *vif,
++		   struct ieee80211_sta *sta,
++		   struct ieee80211_key_conf *key_conf);
++void wlcore_regdomain_config(struct wl1271 *wl);
++
++static inline void
++wlcore_set_ht_cap(struct wl1271 *wl, enum ieee80211_band band,
++		  struct ieee80211_sta_ht_cap *ht_cap)
++{
++	memcpy(&wl->ht_cap[band], ht_cap, sizeof(*ht_cap));
++}
++
++/* Tell wlcore not to care about this element when checking the version */
++#define WLCORE_FW_VER_IGNORE	-1
++
++static inline void
++wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip,
++		      unsigned int iftype_sr, unsigned int major_sr,
++		      unsigned int subtype_sr, unsigned int minor_sr,
++		      unsigned int iftype_mr, unsigned int major_mr,
++		      unsigned int subtype_mr, unsigned int minor_mr)
++{
++	wl->min_sr_fw_ver[FW_VER_CHIP] = chip;
++	wl->min_sr_fw_ver[FW_VER_IF_TYPE] = iftype_sr;
++	wl->min_sr_fw_ver[FW_VER_MAJOR] = major_sr;
++	wl->min_sr_fw_ver[FW_VER_SUBTYPE] = subtype_sr;
++	wl->min_sr_fw_ver[FW_VER_MINOR] = minor_sr;
++
++	wl->min_mr_fw_ver[FW_VER_CHIP] = chip;
++	wl->min_mr_fw_ver[FW_VER_IF_TYPE] = iftype_mr;
++	wl->min_mr_fw_ver[FW_VER_MAJOR] = major_mr;
++	wl->min_mr_fw_ver[FW_VER_SUBTYPE] = subtype_mr;
++	wl->min_mr_fw_ver[FW_VER_MINOR] = minor_mr;
++}
++
++/* Firmware image load chunk size */
++#define CHUNK_SIZE	16384
++
++/* Quirks */
++
++/* Each RX/TX transaction requires an end-of-transaction transfer */
++#define WLCORE_QUIRK_END_OF_TRANSACTION		BIT(0)
++
++/* the first start_role(sta) sometimes doesn't work on wl12xx */
++#define WLCORE_QUIRK_START_STA_FAILS		BIT(1)
++
++/* wl127x and SPI don't support SDIO block size alignment */
++#define WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN		BIT(2)
++
++/* means aggregated Rx packets are aligned to a SDIO block */
++#define WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN		BIT(3)
++
++/* Older firmwares did not implement the FW logger over bus feature */
++#define WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED	BIT(4)
++
++/* Older firmwares use an old NVS format */
++#define WLCORE_QUIRK_LEGACY_NVS			BIT(5)
++
++/* pad only the last frame in the aggregate buffer */
++#define WLCORE_QUIRK_TX_PAD_LAST_FRAME		BIT(7)
++
++/* extra header space is required for TKIP */
++#define WLCORE_QUIRK_TKIP_HEADER_SPACE		BIT(8)
++
++/* Some firmwares not support sched scans while connected */
++#define WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN	BIT(9)
++
++/* separate probe response templates for one-shot and sched scans */
++#define WLCORE_QUIRK_DUAL_PROBE_TMPL		BIT(10)
++
++/* Firmware requires reg domain configuration for active calibration */
++#define WLCORE_QUIRK_REGDOMAIN_CONF		BIT(11)
++
++/* The FW only support a zero session id for AP */
++#define WLCORE_QUIRK_AP_ZERO_SESSION_ID		BIT(12)
++
++/* TODO: move all these common registers and values elsewhere */
++#define HW_ACCESS_ELP_CTRL_REG		0x1FFFC
++
++/* ELP register commands */
++#define ELPCTRL_WAKE_UP             0x1
++#define ELPCTRL_WAKE_UP_WLAN_READY  0x5
++#define ELPCTRL_SLEEP               0x0
++/* ELP WLAN_READY bit */
++#define ELPCTRL_WLAN_READY          0x2
++
++/*************************************************************************
++
++    Interrupt Trigger Register (Host -> WiLink)
++
++**************************************************************************/
++
++/* Hardware to Embedded CPU Interrupts - first 32-bit register set */
++
++/*
++ * The host sets this bit to inform the Wlan
++ * FW that a TX packet is in the XFER
++ * Buffer #0.
++ */
++#define INTR_TRIG_TX_PROC0 BIT(2)
++
++/*
++ * The host sets this bit to inform the FW
++ * that it read a packet from RX XFER
++ * Buffer #0.
++ */
++#define INTR_TRIG_RX_PROC0 BIT(3)
++
++#define INTR_TRIG_DEBUG_ACK BIT(4)
++
++#define INTR_TRIG_STATE_CHANGED BIT(5)
++
++/* Hardware to Embedded CPU Interrupts - second 32-bit register set */
++
++/*
++ * The host sets this bit to inform the FW
++ * that it read a packet from RX XFER
++ * Buffer #1.
++ */
++#define INTR_TRIG_RX_PROC1 BIT(17)
++
++/*
++ * The host sets this bit to inform the Wlan
++ * hardware that a TX packet is in the XFER
++ * Buffer #1.
++ */
++#define INTR_TRIG_TX_PROC1 BIT(18)
++
++#define ACX_SLV_SOFT_RESET_BIT	BIT(1)
++#define SOFT_RESET_MAX_TIME	1000000
++#define SOFT_RESET_STALL_TIME	1000
++
++#define ECPU_CONTROL_HALT	0x00000101
++
++#define WELP_ARM_COMMAND_VAL	0x4
++
++#endif /* __WLCORE_H__ */
+diff -Nuar git.old/drivers/net/wireless/ti/wlcore/wlcore_i.h git/drivers/net/wireless/ti/wlcore/wlcore_i.h
+--- git.old/drivers/net/wireless/ti/wlcore/wlcore_i.h	2015-03-04 16:35:05.722185997 +0100
++++ git/drivers/net/wireless/ti/wlcore/wlcore_i.h	2015-03-04 16:36:46.174185039 +0100
+@@ -209,6 +209,7 @@
+ struct wlcore_platdev_data {
+ 	struct wl12xx_platform_data *pdata;
+ 	struct wl1271_if_operations *if_ops;
++	bool pwr_in_suspend;
+ };
+ 
+ #define MAX_NUM_KEYS 14
+diff -Nuar git.old/drivers/net/wireless/ti/wlcore/wlcore_i.h.orig git/drivers/net/wireless/ti/wlcore/wlcore_i.h.orig
+--- git.old/drivers/net/wireless/ti/wlcore/wlcore_i.h.orig	1970-01-01 01:00:00.000000000 +0100
++++ git/drivers/net/wireless/ti/wlcore/wlcore_i.h.orig	2015-03-04 16:37:26.014184659 +0100
+@@ -0,0 +1,545 @@
++/*
++ * This file is part of wl1271
++ *
++ * Copyright (C) 1998-2009 Texas Instruments. All rights reserved.
++ * Copyright (C) 2008-2009 Nokia Corporation
++ *
++ * Contact: Luciano Coelho <luciano.coelho at nokia.com>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++ * 02110-1301 USA
++ *
++ */
++
++#ifndef __WLCORE_I_H__
++#define __WLCORE_I_H__
++
++#include <linux/mutex.h>
++#include <linux/completion.h>
++#include <linux/spinlock.h>
++#include <linux/list.h>
++#include <linux/bitops.h>
++#include <net/mac80211.h>
++
++#include "conf.h"
++#include "ini.h"
++
++/*
++ * wl127x and wl128x are using the same NVS file name. However, the
++ * ini parameters between them are different.  The driver validates
++ * the correct NVS size in wl1271_boot_upload_nvs().
++ */
++#define WL12XX_NVS_NAME "ti-connectivity/wl1271-nvs.bin"
++
++#define WL1271_TX_SECURITY_LO16(s) ((u16)((s) & 0xffff))
++#define WL1271_TX_SECURITY_HI32(s) ((u32)(((s) >> 16) & 0xffffffff))
++#define WL1271_TX_SQN_POST_RECOVERY_PADDING 0xff
++
++#define WL1271_CIPHER_SUITE_GEM 0x00147201
++
++#define WL1271_BUSY_WORD_CNT 1
++#define WL1271_BUSY_WORD_LEN (WL1271_BUSY_WORD_CNT * sizeof(u32))
++
++#define WL1271_ELP_HW_STATE_ASLEEP 0
++#define WL1271_ELP_HW_STATE_IRQ    1
++
++#define WL1271_DEFAULT_BEACON_INT  100
++#define WL1271_DEFAULT_DTIM_PERIOD 1
++
++#define WL12XX_MAX_ROLES           4
++#define WL12XX_MAX_LINKS           12
++#define WL12XX_INVALID_ROLE_ID     0xff
++#define WL12XX_INVALID_LINK_ID     0xff
++
++/* the driver supports the 2.4Ghz and 5Ghz bands */
++#define WLCORE_NUM_BANDS           2
++
++#define WL12XX_MAX_RATE_POLICIES 16
++#define WLCORE_MAX_KLV_TEMPLATES 4
++
++/* Defined by FW as 0. Will not be freed or allocated. */
++#define WL12XX_SYSTEM_HLID         0
++
++/*
++ * When in AP-mode, we allow (at least) this number of packets
++ * to be transmitted to FW for a STA in PS-mode. Only when packets are
++ * present in the FW buffers it will wake the sleeping STA. We want to put
++ * enough packets for the driver to transmit all of its buffered data before
++ * the STA goes to sleep again. But we don't want to take too much memory
++ * as it might hurt the throughput of active STAs.
++ */
++#define WL1271_PS_STA_MAX_PACKETS  2
++
++#define WL1271_AP_BSS_INDEX        0
++#define WL1271_AP_DEF_BEACON_EXP   20
++
++enum wlcore_state {
++	WLCORE_STATE_OFF,
++	WLCORE_STATE_RESTARTING,
++	WLCORE_STATE_ON,
++};
++
++enum wl12xx_fw_type {
++	WL12XX_FW_TYPE_NONE,
++	WL12XX_FW_TYPE_NORMAL,
++	WL12XX_FW_TYPE_MULTI,
++	WL12XX_FW_TYPE_PLT,
++};
++
++struct wl1271;
++
++enum {
++	FW_VER_CHIP,
++	FW_VER_IF_TYPE,
++	FW_VER_MAJOR,
++	FW_VER_SUBTYPE,
++	FW_VER_MINOR,
++
++	NUM_FW_VER
++};
++
++struct wl1271_chip {
++	u32 id;
++	char fw_ver_str[ETHTOOL_FWVERS_LEN];
++	unsigned int fw_ver[NUM_FW_VER];
++	char phy_fw_ver_str[ETHTOOL_FWVERS_LEN];
++};
++
++#define NUM_TX_QUEUES              4
++
++#define AP_MAX_STATIONS            8
++
++struct wl_fw_packet_counters {
++	/* Cumulative counter of released packets per AC */
++	u8 tx_released_pkts[NUM_TX_QUEUES];
++
++	/* Cumulative counter of freed packets per HLID */
++	u8 tx_lnk_free_pkts[WL12XX_MAX_LINKS];
++
++	/* Cumulative counter of released Voice memory blocks */
++	u8 tx_voice_released_blks;
++
++	/* Tx rate of the last transmitted packet */
++	u8 tx_last_rate;
++
++	u8 padding[2];
++} __packed;
++
++/* FW status registers */
++struct wl_fw_status_1 {
++	__le32 intr;
++	u8  fw_rx_counter;
++	u8  drv_rx_counter;
++	u8  reserved;
++	u8  tx_results_counter;
++	__le32 rx_pkt_descs[0];
++} __packed;
++
++/*
++ * Each HW arch has a different number of Rx descriptors.
++ * The length of the status depends on it, since it holds an array
++ * of descriptors.
++ */
++#define WLCORE_FW_STATUS_1_LEN(num_rx_desc) \
++		(sizeof(struct wl_fw_status_1) + \
++		(sizeof(((struct wl_fw_status_1 *)0)->rx_pkt_descs[0])) * \
++		num_rx_desc)
++
++struct wl_fw_status_2 {
++	__le32 fw_localtime;
++
++	/*
++	 * A bitmap (where each bit represents a single HLID)
++	 * to indicate if the station is in PS mode.
++	 */
++	__le32 link_ps_bitmap;
++
++	/*
++	 * A bitmap (where each bit represents a single HLID) to indicate
++	 * if the station is in Fast mode
++	 */
++	__le32 link_fast_bitmap;
++
++	/* Cumulative counter of total released mem blocks since FW-reset */
++	__le32 total_released_blks;
++
++	/* Size (in Memory Blocks) of TX pool */
++	__le32 tx_total;
++
++	struct wl_fw_packet_counters counters;
++
++	__le32 log_start_addr;
++
++	/* Private status to be used by the lower drivers */
++	u8 priv[0];
++} __packed;
++
++#define WL1271_MAX_CHANNELS 64
++struct wl1271_scan {
++	struct cfg80211_scan_request *req;
++	unsigned long scanned_ch[BITS_TO_LONGS(WL1271_MAX_CHANNELS)];
++	bool failed;
++	u8 state;
++	u8 ssid[IEEE80211_MAX_SSID_LEN+1];
++	size_t ssid_len;
++};
++
++struct wl1271_if_operations {
++	int __must_check (*read)(struct device *child, int addr, void *buf,
++				 size_t len, bool fixed);
++	int __must_check (*write)(struct device *child, int addr, void *buf,
++				  size_t len, bool fixed);
++	void (*reset)(struct device *child);
++	void (*init)(struct device *child);
++	int (*power)(struct device *child, bool enable);
++	void (*set_block_size) (struct device *child, unsigned int blksz);
++};
++
++struct wlcore_platdev_data {
++	struct wl12xx_platform_data *pdata;
++	struct wl1271_if_operations *if_ops;
++	bool pwr_in_suspend;
++};
++
++#define MAX_NUM_KEYS 14
++#define MAX_KEY_SIZE 32
++
++struct wl1271_ap_key {
++	u8 id;
++	u8 key_type;
++	u8 key_size;
++	u8 key[MAX_KEY_SIZE];
++	u8 hlid;
++	u32 tx_seq_32;
++	u16 tx_seq_16;
++};
++
++enum wl12xx_flags {
++	WL1271_FLAG_GPIO_POWER,
++	WL1271_FLAG_TX_QUEUE_STOPPED,
++	WL1271_FLAG_TX_PENDING,
++	WL1271_FLAG_IN_ELP,
++	WL1271_FLAG_ELP_REQUESTED,
++	WL1271_FLAG_IRQ_RUNNING,
++	WL1271_FLAG_FW_TX_BUSY,
++	WL1271_FLAG_DUMMY_PACKET_PENDING,
++	WL1271_FLAG_SUSPENDED,
++	WL1271_FLAG_PENDING_WORK,
++	WL1271_FLAG_SOFT_GEMINI,
++	WL1271_FLAG_RECOVERY_IN_PROGRESS,
++	WL1271_FLAG_VIF_CHANGE_IN_PROGRESS,
++	WL1271_FLAG_INTENDED_FW_RECOVERY,
++	WL1271_FLAG_IO_FAILED,
++};
++
++enum wl12xx_vif_flags {
++	WLVIF_FLAG_INITIALIZED,
++	WLVIF_FLAG_STA_ASSOCIATED,
++	WLVIF_FLAG_STA_AUTHORIZED,
++	WLVIF_FLAG_IBSS_JOINED,
++	WLVIF_FLAG_AP_STARTED,
++	WLVIF_FLAG_IN_PS,
++	WLVIF_FLAG_STA_STATE_SENT,
++	WLVIF_FLAG_RX_STREAMING_STARTED,
++	WLVIF_FLAG_PSPOLL_FAILURE,
++	WLVIF_FLAG_CS_PROGRESS,
++	WLVIF_FLAG_AP_PROBE_RESP_SET,
++	WLVIF_FLAG_IN_USE,
++};
++
++struct wl12xx_vif;
++
++struct wl1271_link {
++	/* AP-mode - TX queue per AC in link */
++	struct sk_buff_head tx_queue[NUM_TX_QUEUES];
++
++	/* accounting for allocated / freed packets in FW */
++	u8 allocated_pkts;
++	u8 prev_freed_pkts;
++
++	u8 addr[ETH_ALEN];
++
++	/* bitmap of TIDs where RX BA sessions are active for this link */
++	u8 ba_bitmap;
++
++	/* The wlvif this link belongs to. Might be null for global links */
++	struct wl12xx_vif *wlvif;
++
++	/*
++	 * total freed FW packets on the link - used for tracking the
++	 * AES/TKIP PN across recoveries. Re-initialized each time
++	 * from the wl1271_station structure.
++	 */
++	u64 total_freed_pkts;
++};
++
++#define WL1271_MAX_RX_FILTERS 5
++#define WL1271_RX_FILTER_MAX_FIELDS 8
++
++#define WL1271_RX_FILTER_ETH_HEADER_SIZE 14
++#define WL1271_RX_FILTER_MAX_FIELDS_SIZE 95
++#define RX_FILTER_FIELD_OVERHEAD				\
++	(sizeof(struct wl12xx_rx_filter_field) - sizeof(u8 *))
++#define WL1271_RX_FILTER_MAX_PATTERN_SIZE			\
++	(WL1271_RX_FILTER_MAX_FIELDS_SIZE - RX_FILTER_FIELD_OVERHEAD)
++
++#define WL1271_RX_FILTER_FLAG_MASK                BIT(0)
++#define WL1271_RX_FILTER_FLAG_IP_HEADER           0
++#define WL1271_RX_FILTER_FLAG_ETHERNET_HEADER     BIT(1)
++
++enum rx_filter_action {
++	FILTER_DROP = 0,
++	FILTER_SIGNAL = 1,
++	FILTER_FW_HANDLE = 2
++};
++
++enum plt_mode {
++	PLT_OFF = 0,
++	PLT_ON = 1,
++	PLT_FEM_DETECT = 2,
++};
++
++struct wl12xx_rx_filter_field {
++	__le16 offset;
++	u8 len;
++	u8 flags;
++	u8 *pattern;
++} __packed;
++
++struct wl12xx_rx_filter {
++	u8 action;
++	int num_fields;
++	struct wl12xx_rx_filter_field fields[WL1271_RX_FILTER_MAX_FIELDS];
++};
++
++struct wl1271_station {
++	u8 hlid;
++	bool in_connection;
++
++	/*
++	 * total freed FW packets on the link to the STA - used for tracking the
++	 * AES/TKIP PN across recoveries. Re-initialized each time from the
++	 * wl1271_station structure.
++	 */
++	u64 total_freed_pkts;
++};
++
++struct wl12xx_vif {
++	struct wl1271 *wl;
++	struct list_head list;
++	unsigned long flags;
++	u8 bss_type;
++	u8 p2p; /* we are using p2p role */
++	u8 role_id;
++
++	/* sta/ibss specific */
++	u8 dev_role_id;
++	u8 dev_hlid;
++
++	union {
++		struct {
++			u8 hlid;
++
++			u8 basic_rate_idx;
++			u8 ap_rate_idx;
++			u8 p2p_rate_idx;
++
++			u8 klv_template_id;
++
++			bool qos;
++			/* channel type we started the STA role with */
++			enum nl80211_channel_type role_chan_type;
++		} sta;
++		struct {
++			u8 global_hlid;
++			u8 bcast_hlid;
++
++			/* HLIDs bitmap of associated stations */
++			unsigned long sta_hlid_map[BITS_TO_LONGS(
++							WL12XX_MAX_LINKS)];
++
++			/* recoreded keys - set here before AP startup */
++			struct wl1271_ap_key *recorded_keys[MAX_NUM_KEYS];
++
++			u8 mgmt_rate_idx;
++			u8 bcast_rate_idx;
++			u8 ucast_rate_idx[CONF_TX_MAX_AC_COUNT];
++		} ap;
++	};
++
++	/* the hlid of the last transmitted skb */
++	int last_tx_hlid;
++
++	/* counters of packets per AC, across all links in the vif */
++	int tx_queue_count[NUM_TX_QUEUES];
++
++	unsigned long links_map[BITS_TO_LONGS(WL12XX_MAX_LINKS)];
++
++	u8 ssid[IEEE80211_MAX_SSID_LEN + 1];
++	u8 ssid_len;
++
++	/* The current band */
++	enum ieee80211_band band;
++	int channel;
++	enum nl80211_channel_type channel_type;
++
++	u32 bitrate_masks[WLCORE_NUM_BANDS];
++	u32 basic_rate_set;
++
++	/*
++	 * currently configured rate set:
++	 *	bits  0-15 - 802.11abg rates
++	 *	bits 16-23 - 802.11n   MCS index mask
++	 * support only 1 stream, thus only 8 bits for the MCS rates (0-7).
++	 */
++	u32 basic_rate;
++	u32 rate_set;
++
++	/* probe-req template for the current AP */
++	struct sk_buff *probereq;
++
++	/* Beaconing interval (needed for ad-hoc) */
++	u32 beacon_int;
++
++	/* Default key (for WEP) */
++	u32 default_key;
++
++	/* Our association ID */
++	u16 aid;
++
++	/* retry counter for PSM entries */
++	u8 psm_entry_retry;
++
++	/* in dBm */
++	int power_level;
++
++	int rssi_thold;
++	int last_rssi_event;
++
++	/* save the current encryption type for auto-arp config */
++	u8 encryption_type;
++	__be32 ip_addr;
++
++	/* RX BA constraint value */
++	bool ba_support;
++	bool ba_allowed;
++
++	bool wmm_enabled;
++
++	/* Rx Streaming */
++	struct work_struct rx_streaming_enable_work;
++	struct work_struct rx_streaming_disable_work;
++	struct timer_list rx_streaming_timer;
++
++	struct delayed_work channel_switch_work;
++	struct delayed_work connection_loss_work;
++
++	/* number of in connection stations */
++	int inconn_count;
++
++	/*
++	 * This vif's queues are mapped to mac80211 HW queues as:
++	 * VO - hw_queue_base
++	 * VI - hw_queue_base + 1
++	 * BE - hw_queue_base + 2
++	 * BK - hw_queue_base + 3
++	 */
++	int hw_queue_base;
++
++	/*
++	 * This struct must be last!
++	 * data that has to be saved acrossed reconfigs (e.g. recovery)
++	 * should be declared in this struct.
++	 */
++	struct {
++		u8 persistent[0];
++
++		/*
++		 * total freed FW packets on the link - used for
++		 * storing the AES/TKIP PN during recovery, as this
++		 * structure is not zeroed out.
++		 * For STA this holds the PN of the link to the AP.
++		 * For AP this holds the PN of the broadcast link.
++		 */
++		u64 total_freed_pkts;
++	};
++};
++
++static inline struct wl12xx_vif *wl12xx_vif_to_data(struct ieee80211_vif *vif)
++{
++	WARN_ON(!vif);
++	return (struct wl12xx_vif *)vif->drv_priv;
++}
++
++static inline
++struct ieee80211_vif *wl12xx_wlvif_to_vif(struct wl12xx_vif *wlvif)
++{
++	return container_of((void *)wlvif, struct ieee80211_vif, drv_priv);
++}
++
++#define wl12xx_for_each_wlvif(wl, wlvif) \
++		list_for_each_entry(wlvif, &wl->wlvif_list, list)
++
++#define wl12xx_for_each_wlvif_continue(wl, wlvif) \
++		list_for_each_entry_continue(wlvif, &wl->wlvif_list, list)
++
++#define wl12xx_for_each_wlvif_bss_type(wl, wlvif, _bss_type)	\
++		wl12xx_for_each_wlvif(wl, wlvif)		\
++			if (wlvif->bss_type == _bss_type)
++
++#define wl12xx_for_each_wlvif_sta(wl, wlvif)	\
++		wl12xx_for_each_wlvif_bss_type(wl, wlvif, BSS_TYPE_STA_BSS)
++
++#define wl12xx_for_each_wlvif_ap(wl, wlvif)	\
++		wl12xx_for_each_wlvif_bss_type(wl, wlvif, BSS_TYPE_AP_BSS)
++
++int wl1271_plt_start(struct wl1271 *wl, const enum plt_mode plt_mode);
++int wl1271_plt_stop(struct wl1271 *wl);
++int wl1271_recalc_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif);
++void wl12xx_queue_recovery_work(struct wl1271 *wl);
++size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen);
++int wl1271_rx_filter_alloc_field(struct wl12xx_rx_filter *filter,
++					u16 offset, u8 flags,
++					u8 *pattern, u8 len);
++void wl1271_rx_filter_free(struct wl12xx_rx_filter *filter);
++struct wl12xx_rx_filter *wl1271_rx_filter_alloc(void);
++int wl1271_rx_filter_get_fields_size(struct wl12xx_rx_filter *filter);
++void wl1271_rx_filter_flatten_fields(struct wl12xx_rx_filter *filter,
++				     u8 *buf);
++
++#define JOIN_TIMEOUT 5000 /* 5000 milliseconds to join */
++
++#define SESSION_COUNTER_MAX 6 /* maximum value for the session counter */
++#define SESSION_COUNTER_INVALID 7 /* used with dummy_packet */
++
++#define WL1271_DEFAULT_POWER_LEVEL 0
++
++#define WL1271_TX_QUEUE_LOW_WATERMARK  32
++#define WL1271_TX_QUEUE_HIGH_WATERMARK 256
++
++#define WL1271_DEFERRED_QUEUE_LIMIT    64
++
++/* WL1271 needs a 200ms sleep after power on, and a 20ms sleep before power
++   on in case is has been shut down shortly before */
++#define WL1271_PRE_POWER_ON_SLEEP 20 /* in milliseconds */
++#define WL1271_POWER_ON_SLEEP 200 /* in milliseconds */
++
++/* Macros to handle wl1271.sta_rate_set */
++#define HW_BG_RATES_MASK	0xffff
++#define HW_HT_RATES_OFFSET	16
++#define HW_MIMO_RATES_OFFSET	24
++
++#define WL12XX_HW_BLOCK_SIZE	256
++
++#endif /* __WLCORE_I_H__ */
+diff -Nuar git.old/include/linux/wl12xx.h git/include/linux/wl12xx.h
+--- git.old/include/linux/wl12xx.h	2015-03-04 16:35:12.462185933 +0100
++++ git/include/linux/wl12xx.h	2015-03-04 16:57:06.170173404 +0100
+@@ -26,42 +26,16 @@
+ 
+ #include <linux/err.h>
+ 
+-/* Reference clock values */
+-enum {
+-	WL12XX_REFCLOCK_19	= 0, /* 19.2 MHz */
+-	WL12XX_REFCLOCK_26	= 1, /* 26 MHz */
+-	WL12XX_REFCLOCK_38	= 2, /* 38.4 MHz */
+-	WL12XX_REFCLOCK_52	= 3, /* 52 MHz */
+-	WL12XX_REFCLOCK_38_XTAL = 4, /* 38.4 MHz, XTAL */
+-	WL12XX_REFCLOCK_26_XTAL = 5, /* 26 MHz, XTAL */
+-};
+-
+-/* TCXO clock values */
+-enum {
+-	WL12XX_TCXOCLOCK_19_2	= 0, /* 19.2MHz */
+-	WL12XX_TCXOCLOCK_26	= 1, /* 26 MHz */
+-	WL12XX_TCXOCLOCK_38_4	= 2, /* 38.4MHz */
+-	WL12XX_TCXOCLOCK_52	= 3, /* 52 MHz */
+-	WL12XX_TCXOCLOCK_16_368	= 4, /* 16.368 MHz */
+-	WL12XX_TCXOCLOCK_32_736	= 5, /* 32.736 MHz */
+-	WL12XX_TCXOCLOCK_16_8	= 6, /* 16.8 MHz */
+-	WL12XX_TCXOCLOCK_33_6	= 7, /* 33.6 MHz */
+-};
+-
+ struct wl12xx_platform_data {
+ 	void (*set_power)(bool enable);
+ 	/* SDIO only: IRQ number if WLAN_IRQ line is used, 0 for SDIO IRQs */
+ 	int irq;
+ 	bool use_eeprom;
+-	int board_ref_clock;
+-	int board_tcxo_clock;
+-	unsigned long platform_quirks;
+-	bool pwr_in_suspend;
++	int ref_clock_freq;	/* in Hertz */
++	bool ref_clock_xtal;	/* specify whether the clock is XTAL or not */
++	int tcxo_clock_freq;	/* in Hertz, tcxo is always XTAL */
+ };
+ 
+-/* Platform does not support level trigger interrupts */
+-#define WL12XX_PLATFORM_QUIRK_EDGE_IRQ	BIT(0)
+-
+ #ifdef CONFIG_WILINK_PLATFORM_DATA
+ 
+ int wl12xx_set_platform_data(const struct wl12xx_platform_data *data);
+diff -Nuar git.old/include/linux/wl12xx.h.orig git/include/linux/wl12xx.h.orig
+--- git.old/include/linux/wl12xx.h.orig	1970-01-01 01:00:00.000000000 +0100
++++ git/include/linux/wl12xx.h.orig	2015-03-04 16:52:01.554176309 +0100
+@@ -0,0 +1,83 @@
++/*
++ * This file is part of wl12xx
++ *
++ * Copyright (C) 2009 Nokia Corporation
++ *
++ * Contact: Luciano Coelho <luciano.coelho at nokia.com>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++ * 02110-1301 USA
++ *
++ */
++
++#ifndef _LINUX_WL12XX_H
++#define _LINUX_WL12XX_H
++
++#include <linux/err.h>
++
++/* Reference clock values */
++enum {
++	WL12XX_REFCLOCK_19	= 0, /* 19.2 MHz */
++	WL12XX_REFCLOCK_26	= 1, /* 26 MHz */
++	WL12XX_REFCLOCK_38	= 2, /* 38.4 MHz */
++	WL12XX_REFCLOCK_52	= 3, /* 52 MHz */
++	WL12XX_REFCLOCK_38_XTAL = 4, /* 38.4 MHz, XTAL */
++	WL12XX_REFCLOCK_26_XTAL = 5, /* 26 MHz, XTAL */
++};
++
++/* TCXO clock values */
++enum {
++	WL12XX_TCXOCLOCK_19_2	= 0, /* 19.2MHz */
++	WL12XX_TCXOCLOCK_26	= 1, /* 26 MHz */
++	WL12XX_TCXOCLOCK_38_4	= 2, /* 38.4MHz */
++	WL12XX_TCXOCLOCK_52	= 3, /* 52 MHz */
++	WL12XX_TCXOCLOCK_16_368	= 4, /* 16.368 MHz */
++	WL12XX_TCXOCLOCK_32_736	= 5, /* 32.736 MHz */
++	WL12XX_TCXOCLOCK_16_8	= 6, /* 16.8 MHz */
++	WL12XX_TCXOCLOCK_33_6	= 7, /* 33.6 MHz */
++};
++
++struct wl12xx_platform_data {
++	void (*set_power)(bool enable);
++	/* SDIO only: IRQ number if WLAN_IRQ line is used, 0 for SDIO IRQs */
++	int irq;
++	bool use_eeprom;
++	int board_ref_clock;
++	int board_tcxo_clock;
++	bool pwr_in_suspend;
++};
++
++#ifdef CONFIG_WILINK_PLATFORM_DATA
++
++int wl12xx_set_platform_data(const struct wl12xx_platform_data *data);
++
++struct wl12xx_platform_data *wl12xx_get_platform_data(void);
++
++#else
++
++static inline
++int wl12xx_set_platform_data(const struct wl12xx_platform_data *data)
++{
++	return -ENOSYS;
++}
++
++static inline
++struct wl12xx_platform_data *wl12xx_get_platform_data(void)
++{
++	return ERR_PTR(-ENODATA);
++}
++
++#endif
++
++#endif
diff --git a/recipes-kernel/linux/linux-apf6-rt/defconfig b/recipes-kernel/linux/linux-apf6-rt/defconfig
new file mode 100644
index 0000000..67573d8
--- /dev/null
+++ b/recipes-kernel/linux/linux-apf6-rt/defconfig
@@ -0,0 +1,409 @@
+CONFIG_LOCALVERSION="_1.1.0_ga-apf6"
+CONFIG_KERNEL_LZO=y
+CONFIG_SYSVIPC=y
+CONFIG_FHANDLE=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=18
+CONFIG_CGROUPS=y
+CONFIG_RELAY=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_EMBEDDED=y
+CONFIG_PERF_EVENTS=y
+# CONFIG_SLUB_DEBUG is not set
+# CONFIG_COMPAT_BRK is not set
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
+# CONFIG_BLK_DEV_BSG is not set
+CONFIG_GPIO_PCA953X=y
+CONFIG_ARCH_MXC=y
+CONFIG_MXC_DEBUG_BOARD=y
+CONFIG_MACH_IMX51_DT=y
+CONFIG_MACH_EUKREA_CPUIMX51SD=y
+CONFIG_SOC_IMX53=y
+CONFIG_SOC_IMX6Q=y
+CONFIG_SOC_IMX6SL=y
+CONFIG_SOC_IMX6SX=y
+CONFIG_SOC_VF610=y
+CONFIG_WAND_RFKILL=y
+# CONFIG_SWP_EMULATE is not set
+CONFIG_PCI=y
+CONFIG_PCI_IMX6=y
+# CONFIG_PCIEAER is not set
+# CONFIG_PCIEASPM is not set
+CONFIG_SMP=y
+CONFIG_VMSPLIT_2G=y
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+CONFIG_HIGHMEM=y
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="noinitrd console=ttymxc3,115200"
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_ARM_IMX6_CPUFREQ=y
+CONFIG_CPU_IDLE=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_BINFMT_MISC=m
+CONFIG_PM_RUNTIME=y
+CONFIG_PM_DEBUG=y
+CONFIG_PM_TEST_SUSPEND=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+CONFIG_IPV6=y
+CONFIG_NETFILTER=y
+CONFIG_VLAN_8021Q=y
+CONFIG_LLC2=y
+CONFIG_CAN=y
+CONFIG_CAN_FLEXCAN=y
+CONFIG_CAN_M_CAN=y
+CONFIG_BT=y
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=m
+CONFIG_BT_HCIUART=m
+CONFIG_BT_HCIUART_H4=y
+CONFIG_CFG80211=y
+CONFIG_NL80211_TESTMODE=y
+CONFIG_CFG80211_WEXT=y
+CONFIG_MAC80211=y
+CONFIG_RFKILL_INPUT=y
+CONFIG_RFKILL_GPIO=y
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+# CONFIG_STANDALONE is not set
+CONFIG_CMA=y
+CONFIG_CMA_SIZE_MBYTES=320
+CONFIG_IMX_WEIM=y
+CONFIG_CONNECTOR=y
+CONFIG_MTD=y
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_CFI=y
+CONFIG_MTD_JEDECPROBE=y
+CONFIG_MTD_CFI_INTELEXT=y
+CONFIG_MTD_CFI_AMDSTD=y
+CONFIG_MTD_CFI_STAA=y
+CONFIG_MTD_PHYSMAP_OF=y
+CONFIG_MTD_DATAFLASH=y
+CONFIG_MTD_M25P80=y
+CONFIG_MTD_SST25L=y
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_GPMI_NAND=y
+CONFIG_MTD_NAND_MXC=y
+CONFIG_MTD_SPI_NOR_BASE=y
+CONFIG_SPI_FSL_QUADSPI=y
+CONFIG_MTD_UBI=y
+CONFIG_PROC_DEVICETREE=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=65536
+CONFIG_EEPROM_AT24=y
+CONFIG_EEPROM_AT25=y
+# CONFIG_SCSI_PROC_FS is not set
+CONFIG_BLK_DEV_SD=y
+CONFIG_BLK_DEV_SR=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_CHR_DEV_SCH=y
+CONFIG_SCSI_MULTI_LUN=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_LOGGING=y
+CONFIG_SCSI_SCAN_ASYNC=y
+# CONFIG_SCSI_LOWLEVEL is not set
+CONFIG_ATA=y
+CONFIG_SATA_AHCI_PLATFORM=y
+CONFIG_AHCI_IMX=y
+CONFIG_PATA_IMX=y
+CONFIG_NETDEVICES=y
+# CONFIG_NET_VENDOR_BROADCOM is not set
+CONFIG_CS89x0=y
+CONFIG_CS89x0_PLATFORM=y
+# CONFIG_NET_VENDOR_FARADAY is not set
+# CONFIG_NET_VENDOR_INTEL is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_MICROCHIP is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+CONFIG_SMC91X=y
+CONFIG_SMC911X=y
+CONFIG_SMSC911X=y
+# CONFIG_NET_VENDOR_STMICRO is not set
+CONFIG_AT803X_PHY=y
+CONFIG_USB_HSO=y
+CONFIG_ATH_CARDS=y
+CONFIG_ATH6KL=m
+CONFIG_ATH6KL_SDIO=m
+CONFIG_BRCMFMAC=m
+CONFIG_BRCMDBG=y
+CONFIG_WL_TI=y
+CONFIG_WL12XX=m
+CONFIG_WLCORE_SDIO=m
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_EVBUG=m
+CONFIG_KEYBOARD_GPIO=y
+CONFIG_KEYBOARD_SNVS_PWRKEY=y
+CONFIG_KEYBOARD_IMX=y
+CONFIG_MOUSE_PS2=m
+CONFIG_MOUSE_PS2_ELANTECH=y
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_EGALAX=y
+CONFIG_TOUCHSCREEN_ELAN=y
+CONFIG_TOUCHSCREEN_MAX11801=y
+CONFIG_TOUCHSCREEN_MC13783=y
+CONFIG_TOUCHSCREEN_SX8674=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_MMA8450=y
+CONFIG_INPUT_ISL29023=y
+CONFIG_SERIO_SERPORT=m
+CONFIG_VT_HW_CONSOLE_BINDING=y
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_IMX=y
+CONFIG_SERIAL_IMX_CONSOLE=y
+CONFIG_SERIAL_FSL_LPUART=y
+CONFIG_SERIAL_FSL_LPUART_CONSOLE=y
+CONFIG_FSL_OTP=y
+CONFIG_MXS_VIIM=y
+# CONFIG_I2C_COMPAT is not set
+CONFIG_I2C_CHARDEV=y
+# CONFIG_I2C_HELPER_AUTO is not set
+CONFIG_I2C_ALGOPCF=m
+CONFIG_I2C_ALGOPCA=m
+CONFIG_I2C_IMX=y
+CONFIG_SPI=y
+CONFIG_SPI_IMX=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_MAX732X=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_SABRESD_MAX8903=y
+CONFIG_IMX6_USB_CHARGER=y
+CONFIG_SENSORS_MAX17135=y
+CONFIG_SENSORS_MAG3110=y
+CONFIG_THERMAL=y
+CONFIG_CPU_THERMAL=y
+CONFIG_IMX_THERMAL=y
+CONFIG_DEVICE_THERMAL=y
+CONFIG_WATCHDOG=y
+CONFIG_IMX2_WDT=y
+CONFIG_MFD_DA9052_I2C=y
+CONFIG_MFD_MC13XXX_SPI=y
+CONFIG_MFD_MC13XXX_I2C=y
+CONFIG_MFD_MAX17135=y
+CONFIG_MFD_SI476X_CORE=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_DA9052=y
+CONFIG_REGULATOR_ANATOP=y
+CONFIG_REGULATOR_MC13783=y
+CONFIG_REGULATOR_MC13892=y
+CONFIG_REGULATOR_MAX17135=y
+CONFIG_REGULATOR_PFUZE100=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_MEDIA_RADIO_SUPPORT=y
+CONFIG_VIDEO_V4L2_INT_DEVICE=y
+CONFIG_MEDIA_USB_SUPPORT=y
+CONFIG_USB_VIDEO_CLASS=m
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_VIDEO_MXC_OUTPUT=y
+CONFIG_VIDEO_MXC_CAPTURE=m
+CONFIG_VIDEO_MXC_CSI_CAMERA=m
+CONFIG_MXC_CAMERA_OV5640=m
+CONFIG_MXC_CAMERA_OV5642=m
+CONFIG_MXC_CAMERA_OV5640_MIPI=m
+CONFIG_MXC_TVIN_ADV7180=m
+CONFIG_MXC_VADC=m
+CONFIG_MXC_IPU_DEVICE_QUEUE_SDC=m
+CONFIG_VIDEO_MXC_IPU_OUTPUT=y
+CONFIG_VIDEO_MXC_PXP_V4L2=y
+CONFIG_SOC_CAMERA=y
+CONFIG_VIDEO_MX3=y
+CONFIG_RADIO_SI476X=y
+CONFIG_SOC_CAMERA_OV2640=y
+CONFIG_DRM=y
+CONFIG_DRM_VIVANTE=y
+CONFIG_FB=y
+CONFIG_FB_MXS=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LCD_CLASS_DEVICE=y
+CONFIG_LCD_L4F00242T03=y
+CONFIG_LCD_PLATFORM=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_PWM=y
+CONFIG_FB_MXC_SYNC_PANEL=y
+CONFIG_FB_MXC_LDB=y
+CONFIG_FB_MXC_MIPI_DSI=y
+CONFIG_FB_MXC_TRULY_WVGA_SYNC_PANEL=y
+CONFIG_FB_MXC_HDMI=y
+CONFIG_FB_MXC_EINK_PANEL=y
+CONFIG_FB_MXS_SII902X=y
+CONFIG_FB_MXC_DCIC=m
+CONFIG_HANNSTAR_CABC=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y
+CONFIG_FONTS=y
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+CONFIG_LOGO=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_USB_AUDIO=m
+CONFIG_SND_SOC=y
+CONFIG_SND_IMX_SOC=y
+CONFIG_SND_SOC_EUKREA_TLV320=y
+CONFIG_SND_SOC_IMX_CS42888=y
+CONFIG_SND_SOC_IMX_WM8962=y
+CONFIG_SND_SOC_IMX_SGTL5000=y
+CONFIG_SND_SOC_IMX_SPDIF=y
+CONFIG_SND_SOC_IMX_MC13783=y
+CONFIG_SND_SOC_IMX_HDMI=y
+CONFIG_SND_SOC_IMX_SI476X=y
+CONFIG_SND_SOC_CS42XX8_I2C=y
+CONFIG_HID_SUNPLUS=m
+CONFIG_USB=y
+CONFIG_USB_OTG=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_ACM=m
+CONFIG_USB_STORAGE=y
+CONFIG_USB_CHIPIDEA=y
+CONFIG_USB_CHIPIDEA_UDC=y
+CONFIG_USB_CHIPIDEA_HOST=y
+CONFIG_USB_SERIAL=y
+CONFIG_USB_SERIAL_FTDI_SIO=m
+CONFIG_USB_SERIAL_KEYSPAN_PDA=y
+CONFIG_NOP_USB_XCEIV=y
+CONFIG_USB_MXS_PHY=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_ZERO=m
+CONFIG_USB_ETH=m
+CONFIG_USB_G_NCM=m
+CONFIG_USB_MASS_STORAGE=m
+CONFIG_FSL_UTP=y
+CONFIG_USB_G_SERIAL=m
+CONFIG_MMC=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_ESDHC_IMX=y
+CONFIG_MXC_IPU=y
+CONFIG_MXC_GPU_VIV=y
+CONFIG_MXC_ASRC=y
+CONFIG_MXC_MIPI_CSI2=y
+CONFIG_MXC_MLB150=m
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_GPIO=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_INTF_DEV_UIE_EMUL=y
+CONFIG_RTC_DRV_MCP7940X=y
+CONFIG_RTC_DRV_MC13XXX=y
+CONFIG_RTC_DRV_MXC=y
+CONFIG_RTC_DRV_SNVS=y
+CONFIG_DMADEVICES=y
+CONFIG_MXC_PXP_V2=y
+CONFIG_IMX_SDMA=y
+CONFIG_MXS_DMA=y
+CONFIG_STAGING=y
+CONFIG_COMMON_CLK_DEBUG=y
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_IIO=y
+CONFIG_VF610_ADC=y
+CONFIG_PWM=y
+CONFIG_PWM_IMX=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_QUOTA=y
+CONFIG_QUOTA_NETLINK_INTERFACE=y
+# CONFIG_PRINT_QUOTA_WARNING is not set
+CONFIG_AUTOFS4_FS=y
+CONFIG_FUSE_FS=y
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_UDF_FS=m
+CONFIG_MSDOS_FS=m
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_JFFS2_FS=y
+CONFIG_UBIFS_FS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_ROOT_NFS=y
+CONFIG_NLS_DEFAULT="cp437"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_UTF8=y
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_SCHED_DEBUG is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_FTRACE is not set
+CONFIG_SECURITYFS=y
+CONFIG_CRYPTO_USER=y
+CONFIG_CRYPTO_TEST=m
+CONFIG_CRYPTO_CCM=y
+CONFIG_CRYPTO_GCM=y
+CONFIG_CRYPTO_CBC=y
+CONFIG_CRYPTO_CTS=y
+CONFIG_CRYPTO_LRW=y
+CONFIG_CRYPTO_XTS=y
+CONFIG_CRYPTO_MD4=y
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_MICHAEL_MIC=y
+CONFIG_CRYPTO_RMD128=y
+CONFIG_CRYPTO_RMD160=y
+CONFIG_CRYPTO_RMD256=y
+CONFIG_CRYPTO_RMD320=y
+CONFIG_CRYPTO_SHA1_ARM=y
+CONFIG_CRYPTO_SHA512=y
+CONFIG_CRYPTO_TGR192=y
+CONFIG_CRYPTO_WP512=y
+CONFIG_CRYPTO_AES_ARM=y
+CONFIG_CRYPTO_BLOWFISH=y
+CONFIG_CRYPTO_CAMELLIA=y
+CONFIG_CRYPTO_DES=y
+CONFIG_CRYPTO_TWOFISH=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRYPTO_DEV_FSL_CAAM=y
+CONFIG_CRYPTO_DEV_FSL_CAAM_SM=y
+CONFIG_CRYPTO_DEV_FSL_CAAM_SM_TEST=y
+CONFIG_CRYPTO_DEV_FSL_CAAM_SECVIO=y
+CONFIG_CRC_CCITT=m
+CONFIG_CRC_T10DIF=y
+CONFIG_CRC7=m
+CONFIG_LIBCRC32C=m
diff --git a/recipes-kernel/linux/linux-apf6-rt_3.10.53.bb b/recipes-kernel/linux/linux-apf6-rt_3.10.53.bb
new file mode 100644
index 0000000..f92d742
--- /dev/null
+++ b/recipes-kernel/linux/linux-apf6-rt_3.10.53.bb
@@ -0,0 +1,19 @@
+SUMMARY = "Linux kernel for Armadeus board with Preemt RT patch"
+
+require linux-apf6.inc
+
+# Wandboard branch - based on 3.10.53_1.1.0_ga from Freescale git
+SRCBRANCH = "wandboard_imx_3.10.53_1.1.0_ga"
+SRCREV = "13bb18444208d5065f0e6345c7743e9662bc97d2"
+LOCALVERSION = "_1.1.0_ga-apf6-rt"
+
+SRC_URI += "https://www.kernel.org/pub/linux/kernel/projects/rt/3.10/older/patch-3.10.53-rt56.patch.gz;name=rt-patch \
+            file://0001-fix-build.patch \
+	    file://0002-fix-build-with-rt-enabled.patch \
+	    file://0003-no-split-ptlocks.patch \
+	    file://0001-Fix-Build-after-applying-rt-patch.patch \
+	    file://defconfig \
+"
+
+SRC_URI[rt-patch.md5sum] = "5172eba68b81eb952513a47dd6733728"
+SRC_URI[rt-patch.sha256sum] = "b900e52ecf4cda0c1543a7424c48191c60c20211bd950349fd8438016d5ed532"
diff --git a/recipes-kernel/linux/linux-apf6.inc b/recipes-kernel/linux/linux-apf6.inc
new file mode 100644
index 0000000..007d3f6
--- /dev/null
+++ b/recipes-kernel/linux/linux-apf6.inc
@@ -0,0 +1,21 @@
+# Adapted from linux-wandboard and linux-imx recipes.
+# The supported kernel for APF6 boards use linux-wandboard code.
+
+require recipes-kernel/linux/linux-dtb.inc
+require recipes-kernel/linux/linux-imx.inc
+
+LICENSE = "GPLv2"
+LIC_FILES_CHKSUM = "file://COPYING;md5=d7810fab7487fb0aad327b76f1be7cd7"
+
+DEPENDS += "lzop-native bc-native"
+
+COMPATIBLE_MACHINE = "(apf6dl|apf6q)"
+
+SRC_URI = "git://github.com/wandboard-org/linux.git;branch=${SRCBRANCH} \
+           file://0001-add-imx6-dtb-for-dual-and-quad-apf6.patch \
+           file://0002-decrease-pcie_set_read_rq-size-from-4096-to-256.patch \
+           file://0504-armadeus-add_sx8674_touchscreen_driver.patch \
+           file://0505-armadeus-add_mcp7940x_rtc_driver.patch \
+           file://059X-armadeus-add-wireless-driver.patch \
+"
+
diff --git a/recipes-kernel/linux/linux-apf6/0001-add-imx6-dtb-for-dual-and-quad-apf6.patch b/recipes-kernel/linux/linux-apf6/0001-add-imx6-dtb-for-dual-and-quad-apf6.patch
new file mode 100644
index 0000000..eac698d
--- /dev/null
+++ b/recipes-kernel/linux/linux-apf6/0001-add-imx6-dtb-for-dual-and-quad-apf6.patch
@@ -0,0 +1,739 @@
+From 354ce3d246a1b190004f3b72629d1f5356efac1f Mon Sep 17 00:00:00 2001
+From: Mallory Daries <mallory.daries at openwide.fr>
+Date: Tue, 10 Mar 2015 14:00:35 +0100
+Subject: [PATCH 1/2] add imx6 dtb for dual and quad apf6
+
+---
+ arch/arm/boot/dts/imx6dl-apf6dev.dts   |  24 ++
+ arch/arm/boot/dts/imx6q-apf6dev.dts    |  30 ++
+ arch/arm/boot/dts/imx6qdl-apf6.dtsi    | 132 +++++++++
+ arch/arm/boot/dts/imx6qdl-apf6dev.dtsi | 507 +++++++++++++++++++++++++++++++++
+ 4 files changed, 693 insertions(+)
+ create mode 100644 arch/arm/boot/dts/imx6dl-apf6dev.dts
+ create mode 100644 arch/arm/boot/dts/imx6q-apf6dev.dts
+ create mode 100644 arch/arm/boot/dts/imx6qdl-apf6.dtsi
+ create mode 100644 arch/arm/boot/dts/imx6qdl-apf6dev.dtsi
+
+diff --git a/arch/arm/boot/dts/imx6dl-apf6dev.dts b/arch/arm/boot/dts/imx6dl-apf6dev.dts
+new file mode 100644
+index 0000000..a875115
+--- /dev/null
++++ b/arch/arm/boot/dts/imx6dl-apf6dev.dts
+@@ -0,0 +1,24 @@
++/*
++ * Copyright 2015 Armadeus Systems
++ *
++ * Author: Armadeus Systems <support at armadeus.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ */
++
++/dts-v1/;
++#include "imx6dl.dtsi"
++#include "imx6qdl-apf6.dtsi"
++#include "imx6qdl-apf6dev.dtsi"
++
++/ {
++	model = "Armadeus APF6 i.MX6 Dual Lite Module on APF6Dev Board";
++	compatible = "armadeus,imx6dl-apf6dev", "armadeus,imx6dl-apf6", "fsl,imx6dl";
++
++	memory {
++		reg = <0x10000000 0x40000000>;
++	};
++};
+diff --git a/arch/arm/boot/dts/imx6q-apf6dev.dts b/arch/arm/boot/dts/imx6q-apf6dev.dts
+new file mode 100644
+index 0000000..7d63a14
+--- /dev/null
++++ b/arch/arm/boot/dts/imx6q-apf6dev.dts
+@@ -0,0 +1,30 @@
++/*
++ * Copyright 2015 Armadeus Systems
++ *
++ * Author: Armadeus Systems <support at armadeus.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ */
++
++/dts-v1/;
++#include "imx6q.dtsi"
++#include "imx6qdl-apf6.dtsi"
++#include "imx6qdl-apf6dev.dtsi"
++
++/ {
++	model = "Armadeus APF6 i.MX6 Quad / Dual Module on APF6Dev Board";
++	compatible = "armadeus,imx6q-apf6dev", "armadeus,imx6q-apf6", "fsl,imx6q";
++
++	memory {
++		reg = <0x10000000 0x80000000>;
++	};
++};
++
++
++&sata {
++	status = "okay";
++};
++
+diff --git a/arch/arm/boot/dts/imx6qdl-apf6.dtsi b/arch/arm/boot/dts/imx6qdl-apf6.dtsi
+new file mode 100644
+index 0000000..9932d73
+--- /dev/null
++++ b/arch/arm/boot/dts/imx6qdl-apf6.dtsi
+@@ -0,0 +1,132 @@
++/*
++ * Copyright 2015 Armadeus Systems
++ *
++ * Author: Armadeus Systems <support at armadeus.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ */
++
++#include <dt-bindings/interrupt-controller/irq.h>
++
++/ {
++	wlan {
++		compatible = "ti,wilink6";
++		interrupt-parent = <&gpio2>;
++		interrupts = <10 IRQ_TYPE_LEVEL_HIGH>; /* gpio line 42, high level triggered */
++		clocks = <&refclock>, <&tcxoclock>;
++		clock-names = "refclock", "tcxoclock";
++
++		refclock: refclock {
++			compatible = "ti,wilink-clock";
++			#clock-cells = <0>;
++			clock-frequency = <38400000>;
++		};
++
++		tcxoclock: tcxoclock {
++			compatible = "ti,wilink-clock";
++			#clock-cells = <0>;
++			clock-frequency = <38400000>;
++	  	};
++	};
++};
++
++&fec {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_enet>;
++	phy-mode = "rgmii";
++	//phy-reset-duration = <10>;
++	//phy-reset-gpios = <&gpio1 24 GPIO_ACTIVE_HIGH>;
++	status = "okay";
++};
++
++/* Bluetooth */
++&uart2 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_uart2>;
++	status = "okay";
++};
++
++/* Wi-Fi */
++&usdhc1 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_usdhc1>;
++	non-removable;
++	status = "okay";
++};
++
++/* eMMC */
++&usdhc3 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_usdhc3>;
++	bus-width = <8>;
++	no-1-8-v;
++	non-removable;
++	status = "okay";
++};
++
++&iomuxc {
++	apf6 {
++		pinctrl_enet: enetgrp {
++			fsl,pins = <
++				MX6QDL_PAD_ENET_MDIO__ENET_MDIO		0x1b0b0
++				MX6QDL_PAD_ENET_MDC__ENET_MDC		0x1b0b0
++				MX6QDL_PAD_RGMII_TXC__RGMII_TXC		0x1b030
++				MX6QDL_PAD_RGMII_TD0__RGMII_TD0		0x1b030
++				MX6QDL_PAD_RGMII_TD1__RGMII_TD1		0x1b030
++				MX6QDL_PAD_RGMII_TD2__RGMII_TD2		0x1b030
++				MX6QDL_PAD_RGMII_TD3__RGMII_TD3		0x1b030
++				MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL	0x1b030
++				MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK	0x1b0b0
++				MX6QDL_PAD_RGMII_RXC__RGMII_RXC		0x1b030
++				MX6QDL_PAD_RGMII_RD0__RGMII_RD0		0x1b030
++				MX6QDL_PAD_RGMII_RD1__RGMII_RD1		0x1b030
++				MX6QDL_PAD_RGMII_RD2__RGMII_RD2		0x1f030
++				MX6QDL_PAD_RGMII_RD3__RGMII_RD3		0x1f030
++				MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL	0x1b030
++				MX6QDL_PAD_ENET_RX_ER__GPIO1_IO24	0x80000000
++				MX6QDL_PAD_ENET_TX_EN__GPIO1_IO28	0x80000000
++			>;
++		};
++
++		pinctrl_uart2: uart2grp {
++			fsl,pins = <
++				MX6QDL_PAD_SD4_DAT4__UART2_RX_DATA	0x1b0b1
++				MX6QDL_PAD_SD4_DAT5__UART2_RTS_B	0x1b0b1
++				MX6QDL_PAD_SD4_DAT6__UART2_CTS_B	0x1b0b1
++				MX6QDL_PAD_SD4_DAT7__UART2_TX_DATA	0x1b0b1
++				MX6QDL_PAD_SD4_DAT3__GPIO2_IO11		0x80000000 /* BT_EN */
++			>;
++		};
++
++		pinctrl_usdhc1: usdhc1grp {
++			fsl,pins = <
++				MX6QDL_PAD_SD1_CMD__SD1_CMD	0x17059
++				MX6QDL_PAD_SD1_CLK__SD1_CLK	0x10059
++				MX6QDL_PAD_SD1_DAT0__SD1_DATA0	0x17059
++				MX6QDL_PAD_SD1_DAT1__SD1_DATA1	0x17059
++				MX6QDL_PAD_SD1_DAT2__SD1_DATA2	0x17059
++				MX6QDL_PAD_SD1_DAT3__SD1_DATA3	0x17059
++				MX6QDL_PAD_SD4_DAT0__GPIO2_IO08	0x80000000 /* WL_EN */
++				MX6QDL_PAD_SD4_DAT2__GPIO2_IO10	0x80000000 /* WL_IRQ */
++			>;
++		};
++
++		pinctrl_usdhc3: usdhc3grp {
++			fsl,pins = <
++				MX6QDL_PAD_SD3_CMD__SD3_CMD	0x17059
++				MX6QDL_PAD_SD3_CLK__SD3_CLK	0x10059
++				MX6QDL_PAD_SD3_DAT0__SD3_DATA0	0x17059
++				MX6QDL_PAD_SD3_DAT1__SD3_DATA1	0x17059
++				MX6QDL_PAD_SD3_DAT2__SD3_DATA2	0x17059
++				MX6QDL_PAD_SD3_DAT3__SD3_DATA3	0x17059
++				MX6QDL_PAD_SD3_DAT4__SD3_DATA4	0x17059
++				MX6QDL_PAD_SD3_DAT5__SD3_DATA5	0x17059
++				MX6QDL_PAD_SD3_DAT6__SD3_DATA6	0x17059
++				MX6QDL_PAD_SD3_DAT7__SD3_DATA7	0x17059
++			>;
++		};
++	};
++};
+diff --git a/arch/arm/boot/dts/imx6qdl-apf6dev.dtsi b/arch/arm/boot/dts/imx6qdl-apf6dev.dtsi
+new file mode 100644
+index 0000000..a810e9b
+--- /dev/null
++++ b/arch/arm/boot/dts/imx6qdl-apf6dev.dtsi
+@@ -0,0 +1,510 @@
++/*
++ * Copyright 2015 Armadeus Systems
++ *
++ * Author: Armadeus Systems <support at armadeus.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ */
++
++/ {
++	aliases {
++		mxcfb0 = &mxcfb1;
++	};
++
++	gpio-keys {
++		compatible = "gpio-keys";
++		pinctrl-names = "default";
++		pinctrl-0 = <&pinctrl_gpio_keys>;
++
++		user-button {
++			label = "User button";
++			gpios = <&gpio1 9 GPIO_ACTIVE_LOW>;
++			linux,code = <0x100>;
++			gpio-key,wakeup;
++		};
++	};
++
++	gpio-leds {
++		compatible = "gpio-leds";
++		pinctrl-names = "default";
++		pinctrl-0 = <&pinctrl_gpio_leds>;
++
++		user-led {
++			label = "User LED";
++			gpios = <&gpio7 12 GPIO_ACTIVE_HIGH>;
++			linux,default-trigger = "heartbeat";
++			default-state = "on";
++		};
++	};
++
++	mxcfb1: fb at 0 {
++		compatible = "fsl,mxc_sdc_fb";
++		disp_dev = "hdmi";
++		interface_pix_fmt = "RGB24";
++		mode_str ="1920x1080M at 60";
++		default_bpp = <24>;
++		int_clk = <0>;
++		late_init = <0>;
++		status = "okay";
++	};
++
++	mxcfb3: fb at 2 {
++		compatible = "fsl,mxc_sdc_fb";
++		disp_dev = "lcd";
++		interface_pix_fmt = "RGB666";
++		mode_str ="SEIKO-WVGA";
++		default_bpp = <18>;
++		int_clk = <0>;
++		late_init = <0>;
++		status = "okay";
++	};
++
++	lcd at 0 {
++		compatible = "fsl,lcd";
++		ipu_id = <0>;
++		disp_id = <0>;
++		default_ifmt = "RGB666";
++		pinctrl-names = "default";
++		pinctrl-0 = <&pinctrl_ipu1>;
++		status = "okay";
++	};
++
++	backlight_lcd {
++		compatible = "pwm-backlight";
++		pwms = <&pwm3 0 5000000>;
++		brightness-levels = <0 4 8 16 32 64 128 255>;
++		default-brightness-level = <6>;
++	};
++
++	sound {
++		compatible = "fsl,imx6-armadeus-sgtl5000",
++			   "fsl,imx-audio-sgtl5000";
++		model = "imx6-armadeus-sgtl5000";
++		cpu-dai = <&ssi1>;
++		audio-codec = <&codec>;
++		audio-routing =
++			"MIC_IN", "Mic Jack",
++			"Mic Jack", "Mic Bias",
++			"Headphone Jack", "HP_OUT";
++		mux-int-port = <1>;
++		mux-ext-port = <3>;
++	};
++
++	sound-spdif {
++		compatible = "fsl,imx-audio-spdif";
++		model = "imx-spdif";
++		spdif-controller = <&spdif>;
++		spdif-out;
++	};
++
++	sound-hdmi {
++		compatible = "fsl,imx6q-audio-hdmi",
++			   "fsl,imx-audio-hdmi";
++		model = "imx-audio-hdmi";
++		hdmi-controller = <&hdmi_audio>;
++	};
++
++	regulators {
++		compatible = "simple-bus";
++
++		reg_1p8v: 1p8v {
++			compatible = "regulator-fixed";
++			regulator-name = "1P8V";
++			regulator-min-microvolt = <1800000>;
++			regulator-max-microvolt = <1800000>;
++			regulator-always-on;
++		};
++
++		reg_2p5v: 2p5v {
++			compatible = "regulator-fixed";
++			regulator-name = "2P5V";
++			regulator-min-microvolt = <2500000>;
++			regulator-max-microvolt = <2500000>;
++			regulator-always-on;
++		};
++
++		reg_3p3v: 3p3v {
++			compatible = "regulator-fixed";
++			regulator-name = "3P3V";
++			regulator-min-microvolt = <3300000>;
++			regulator-max-microvolt = <3300000>;
++			regulator-always-on;
++		};
++
++		reg_usb_otg_vbus: usb_otg_vbus {
++			compatible = "regulator-fixed";
++			regulator-name = "usb_otg_vbus";
++			regulator-min-microvolt = <5000000>;
++			regulator-max-microvolt = <5000000>;
++			regulator-always-on;
++		};
++	};
++
++	v4l2_cap_0 {
++		compatible = "fsl,imx6q-v4l2-capture";
++		ipu_id = <0>;
++		csi_id = <0>;
++		mclk_source = <0>;
++		status = "okay";
++	};
++
++	v4l2_out {
++		compatible = "fsl,mxc_v4l2_output";
++		status = "okay";
++	};
++};
++
++&audmux {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_audmux>;
++	status = "okay";
++};
++
++&flexcan2 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_flexcan2>;
++	status = "okay";
++};
++
++&ecspi1 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_ecspi1>;
++	fsl,spi-num-chipselects = <3>;
++	cs-gpios = <&gpio4 9 GPIO_ACTIVE_HIGH>,
++		   <&gpio4 10 GPIO_ACTIVE_HIGH>,
++		   <&gpio4 11 GPIO_ACTIVE_HIGH>;
++	status = "okay";
++};
++
++&hdmi_audio {
++	status = "okay";
++};
++
++&hdmi_core {
++	ipu_id = <0>;
++	disp_id = <0>;
++	status = "okay";
++};
++
++&hdmi_video {
++	fsl,phy_reg_vlev = <0x0294>;
++	fsl,phy_reg_cksymtx = <0x800d>;
++	status = "okay";
++};
++
++
++&i2c1 {
++	clock-frequency = <400000>;
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_i2c1>;
++	status = "okay";
++
++	touchscreen at 48 {
++		compatible = "semtech,sx8654";
++		reg = <0x48>;
++		pinctrl-names = "default";
++		pinctrl-0 = <&pinctrl_touchscreen>;
++		interrupt-parent = <&gpio6>;
++		interrupts = <3 IRQ_TYPE_EDGE_FALLING>;
++		x-plate-ohms = <1100>;
++		y-plate-ohms = <500>;
++		min-x = <0>;
++		max-x = <800>;
++		min-y = <0>;
++		max-y = <480>;
++	};
++};
++
++&i2c2 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_i2c2>;
++	clock-frequency = <400000>;
++	status = "okay";
++
++	rtc at 6f {
++		compatible = "microchip,mcp7940x-rtc";
++		reg = <0x6F>;
++	};
++
++	codec: sgtl5000 at 0a {
++		compatible = "fsl,sgtl5000";
++		reg = <0x0a>;
++		clocks = <&clks 201>;
++		VDDA-supply = <&reg_3p3v>;
++		VDDIO-supply = <&reg_3p3v>;
++	};
++};
++
++&i2c3 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_i2c3>;
++	clock-frequency = <400000>;
++	status = "okay";
++
++	hdmi: edid at 50 {
++		compatible = "fsl,imx6-hdmi-i2c";
++		reg = <0x50>;
++	};
++
++};
++
++&mipi_csi {
++	ipu_id = <0>;
++	csi_id = <0>;
++	v_channel = <0>;
++	lanes = <2>;
++	status = "okay";
++};
++
++&pcie {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_pcie>;
++	reset-gpio = <&gpio6 2 GPIO_ACTIVE_HIGH>;
++	status = "okay";
++};
++
++&pwm3 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_pwm3_1>;
++	status = "okay";
++};
++
++&spdif {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_spdif>;
++	status = "okay";
++};
++
++&ssi1 {
++	fsl,mode = "i2s-slave";
++	status = "okay";
++};
++
++/* GPS */
++&uart1 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_uart1>;
++	status = "okay";
++};
++
++/* GSM */
++&uart3 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_uart3 &pinctrl_gsm>;
++	fsl,uart-has-rtscts;
++	status = "okay";
++};
++
++/* console */
++&uart4 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_uart4>;
++	status = "okay";
++};
++
++&usbh1 {
++	dr_mode = "host";
++	phy_type = "utmi";
++	status = "okay";
++};
++
++&usbotg {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_usbotg>;
++	vbus-supply = <&reg_usb_otg_vbus>;
++	dr_mode = "otg";
++	status = "okay";
++};
++
++/* microSD */
++&usdhc2 {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_usdhc2>;
++	bus-width = <4>;
++	cd-gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>;
++	status = "okay";
++};
++
++&iomuxc {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_gpios>;
++
++	apf6dev {
++		pinctrl_audmux: audmuxgrp {
++			fsl,pins = <
++				MX6QDL_PAD_CSI0_DAT7__AUD3_RXD  0x80000000
++				MX6QDL_PAD_CSI0_DAT4__AUD3_TXC  0x80000000
++				MX6QDL_PAD_CSI0_DAT5__AUD3_TXD  0x80000000
++				MX6QDL_PAD_CSI0_DAT6__AUD3_TXFS 0x80000000
++				MX6QDL_PAD_GPIO_0__CCM_CLKO1    0x130b0
++			>;
++		};
++
++		pinctrl_camera_ov5640: cameragrp {
++			fsl,pins = <
++				MX6QDL_PAD_GPIO_3__XTALOSC_REF_CLK_24M 0x1b0b0 /* CSI_MCLK */
++				MX6QDL_PAD_CSI0_MCLK__GPIO5_IO19       0x80000000
++				MX6QDL_PAD_CSI0_DATA_EN__GPIO5_IO20    0x80000000
++			>;
++		};
++
++		pinctrl_ipu1: ipu1grp-1 {
++			fsl,pins = <
++				MX6QDL_PAD_DI0_DISP_CLK__IPU1_DI0_DISP_CLK 0x10
++				MX6QDL_PAD_DI0_PIN15__IPU1_DI0_PIN15       0x10
++				MX6QDL_PAD_DI0_PIN2__IPU1_DI0_PIN02        0x10
++				MX6QDL_PAD_DI0_PIN3__IPU1_DI0_PIN03        0x10
++				MX6QDL_PAD_DI0_PIN4__IPU1_DI0_PIN04        0x80000000
++				MX6QDL_PAD_DISP0_DAT0__IPU1_DISP0_DATA00   0x10
++				MX6QDL_PAD_DISP0_DAT1__IPU1_DISP0_DATA01   0x10
++				MX6QDL_PAD_DISP0_DAT2__IPU1_DISP0_DATA02   0x10
++				MX6QDL_PAD_DISP0_DAT3__IPU1_DISP0_DATA03   0x10
++				MX6QDL_PAD_DISP0_DAT4__IPU1_DISP0_DATA04   0x10
++				MX6QDL_PAD_DISP0_DAT5__IPU1_DISP0_DATA05   0x10
++				MX6QDL_PAD_DISP0_DAT6__IPU1_DISP0_DATA06   0x10
++				MX6QDL_PAD_DISP0_DAT7__IPU1_DISP0_DATA07   0x10
++				MX6QDL_PAD_DISP0_DAT8__IPU1_DISP0_DATA08   0x10
++				MX6QDL_PAD_DISP0_DAT9__IPU1_DISP0_DATA09   0x10
++				MX6QDL_PAD_DISP0_DAT10__IPU1_DISP0_DATA10  0x10
++				MX6QDL_PAD_DISP0_DAT11__IPU1_DISP0_DATA11  0x10
++				MX6QDL_PAD_DISP0_DAT12__IPU1_DISP0_DATA12  0x10
++				MX6QDL_PAD_DISP0_DAT13__IPU1_DISP0_DATA13  0x10
++				MX6QDL_PAD_DISP0_DAT14__IPU1_DISP0_DATA14  0x10
++				MX6QDL_PAD_DISP0_DAT15__IPU1_DISP0_DATA15  0x10
++				MX6QDL_PAD_DISP0_DAT16__IPU1_DISP0_DATA16  0x10
++				MX6QDL_PAD_DISP0_DAT17__IPU1_DISP0_DATA17  0x10
++			>;
++		};
++
++		pinctrl_ecspi1: ecspi1grp {
++			fsl,pins = <
++				MX6QDL_PAD_KEY_COL1__ECSPI1_MISO 0x100b1
++				MX6QDL_PAD_KEY_ROW0__ECSPI1_MOSI 0x100b1
++				MX6QDL_PAD_KEY_COL0__ECSPI1_SCLK 0x100b1
++				MX6QDL_PAD_KEY_ROW1__GPIO4_IO09  0x80000000
++				MX6QDL_PAD_KEY_ROW2__GPIO4_IO11  0x80000000
++				MX6QDL_PAD_KEY_COL2__GPIO4_IO10  0x80000000
++			>;
++		};
++
++		pinctrl_flexcan2: flexcan2grp {
++			fsl,pins = <
++				MX6QDL_PAD_KEY_COL4__FLEXCAN2_TX 0x80000000
++				MX6QDL_PAD_KEY_ROW4__FLEXCAN2_RX 0x80000000
++			>;
++		};
++
++		pinctrl_gpio_keys: gpiokeysgrp {
++			fsl,pins = <
++				MX6QDL_PAD_GPIO_9__GPIO1_IO09 0x1b0b0
++			>;
++		};
++		pinctrl_gpio_leds: gpioledsgrp {
++			fsl,pins = <
++				MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x80000000
++			>;
++		};
++
++		pinctrl_gpios: gpiosgrp {
++			fsl,pins = <
++				MX6QDL_PAD_DISP0_DAT18__GPIO5_IO12      0x80000000
++				MX6QDL_PAD_DISP0_DAT19__GPIO5_IO13      0x80000000
++				MX6QDL_PAD_DISP0_DAT20__GPIO5_IO14      0x80000000
++				MX6QDL_PAD_DISP0_DAT21__GPIO5_IO15      0x80000000
++				MX6QDL_PAD_DISP0_DAT22__GPIO5_IO16      0x80000000
++				MX6QDL_PAD_DISP0_DAT23__GPIO5_IO17      0x80000000
++				MX6QDL_PAD_CSI0_PIXCLK__GPIO5_IO18      0x80000000
++				MX6QDL_PAD_CSI0_VSYNC__GPIO5_IO21       0x80000000
++			>;
++		};
++
++		pinctrl_gsm: gsmgrp {
++			fsl,pins = <
++				MX6QDL_PAD_GPIO_4__GPIO1_IO04  0x80000000 /* GSM_POKIN */
++				MX6QDL_PAD_GPIO_18__GPIO7_IO13 0x80000000 /* GSM_PWR_EN */
++			>;
++		};
++
++		pinctrl_i2c1: i2c1grp {
++			fsl,pins = <
++				MX6QDL_PAD_CSI0_DAT8__I2C1_SDA 0x4001b8b1
++				MX6QDL_PAD_CSI0_DAT9__I2C1_SCL 0x4001b8b1
++			>;
++		};
++
++		pinctrl_i2c2: i2c2grp {
++			fsl,pins = <
++				MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1
++				MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1
++			>;
++		};
++
++		pinctrl_i2c3: i2c3grp {
++			fsl,pins = <
++				MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1
++				MX6QDL_PAD_GPIO_5__I2C3_SCL 0x4001b8b1
++			>;
++		};
++
++		pinctrl_pcie: pciegrp {
++			fsl,pins = <
++				MX6QDL_PAD_CSI0_DAT16__GPIO6_IO02 0x80000000
++			>;
++		};
++
++		pinctrl_uart1: uart1grp {
++			fsl,pins = <
++				MX6QDL_PAD_CSI0_DAT10__UART1_TX_DATA 0x1b0b1
++				MX6QDL_PAD_CSI0_DAT11__UART1_RX_DATA 0x1b0b1
++			>;
++		};
++
++		pinctrl_uart3: uart3grp {
++			fsl,pins = <
++				MX6QDL_PAD_EIM_D23__UART3_CTS_B   0x1b0b1
++				MX6QDL_PAD_EIM_D24__UART3_TX_DATA 0x1b0b1
++				MX6QDL_PAD_EIM_D25__UART3_RX_DATA 0x1b0b1
++				MX6QDL_PAD_EIM_D31__UART3_RTS_B   0x1b0b1
++			>;
++		};
++
++		pinctrl_uart4: uart4grp {
++			fsl,pins = <
++				MX6QDL_PAD_CSI0_DAT12__UART4_TX_DATA 0x1b0b1
++				MX6QDL_PAD_CSI0_DAT13__UART4_RX_DATA 0x1b0b1
++			>;
++		};
++
++		pinctrl_usbotg: usbotggrp {
++			fsl,pins = <
++				MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059
++			>;
++		};
++
++		pinctrl_usdhc2: usdhc2grp {
++			fsl,pins = <
++				MX6QDL_PAD_SD2_CMD__SD2_CMD    0x17059
++				MX6QDL_PAD_SD2_CLK__SD2_CLK    0x10059
++				MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x17059
++				MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x17059
++				MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x17059
++				MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x17059
++			>;
++		};
++
++		pinctrl_spdif: spdifgrp {
++			fsl,pins = <
++				MX6QDL_PAD_GPIO_19__SPDIF_OUT 0x1b0b0
++			>;
++		};
++
++		pinctrl_touchscreen: touchscreengrp {
++			fsl,pins = <
++				MX6QDL_PAD_CSI0_DAT17__GPIO6_IO03 0x80000000
++			>;
++		};
++	};
++};
+-- 
+1.9.1
+
diff --git a/recipes-kernel/linux/linux-apf6/0002-decrease-pcie_set_read_rq-size-from-4096-to-256.patch b/recipes-kernel/linux/linux-apf6/0002-decrease-pcie_set_read_rq-size-from-4096-to-256.patch
new file mode 100644
index 0000000..f4163c4
--- /dev/null
+++ b/recipes-kernel/linux/linux-apf6/0002-decrease-pcie_set_read_rq-size-from-4096-to-256.patch
@@ -0,0 +1,25 @@
+From a83813fb401c29a9a2eff6b7140c1ee96d278afd Mon Sep 17 00:00:00 2001
+From: Nicolas Aguirre <nicolas.aguirre at openwide.fr>
+Date: Wed, 15 Apr 2015 12:56:51 +0200
+Subject: [PATCH] decrease pcie_set_read_rq size from 4096 to 256
+
+---
+ drivers/ata/sata_sil24.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c
+index 59f0d63..e728b8e 100644
+--- a/drivers/ata/sata_sil24.c
++++ b/drivers/ata/sata_sil24.c
+@@ -1336,7 +1336,7 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+ 	/* Set max read request size to 4096.  This slightly increases
+ 	 * write throughput for pci-e variants.
+ 	 */
+-	pcie_set_readrq(pdev, 4096);
++	pcie_set_readrq(pdev, 256);
+ 
+ 	sil24_init_controller(host);
+ 
+-- 
+1.9.1
+
diff --git a/recipes-kernel/linux/linux-apf6/0504-armadeus-add_sx8674_touchscreen_driver.patch b/recipes-kernel/linux/linux-apf6/0504-armadeus-add_sx8674_touchscreen_driver.patch
new file mode 100644
index 0000000..f31f7c2
--- /dev/null
+++ b/recipes-kernel/linux/linux-apf6/0504-armadeus-add_sx8674_touchscreen_driver.patch
@@ -0,0 +1,510 @@
+--- git/drivers/input/touchscreen/Kconfig.old	2015-03-04 11:55:10.242346171 +0100
++++ git/drivers/input/touchscreen/Kconfig	2015-03-04 11:54:55.574346311 +0100
+@@ -900,6 +900,17 @@
+ 	  To compile this driver as a module, choose M here: the
+ 	  module will be called tps6507x_ts.
+ 
++config TOUCHSCREEN_SX8674
++	tristate "Semtech SX8674 touchscreen controllers"
++	depends on I2C
++	help
++	  Say Y here if you have a Semtech SX8674 touchscreen controller.
++
++	  If unsure, say N
++
++	  To compile this driver as a module, choose M here: the
++	  module will be called sx8674.
++
+ config TOUCHSCREEN_FUSION
+ 	tristate "fusion touchscreen"
+ 	help
+--- git/drivers/input/touchscreen/Makefile.old	2015-03-04 11:56:09.890345602 +0100
++++ git/drivers/input/touchscreen/Makefile	2015-03-04 11:55:58.250345713 +0100
+@@ -52,6 +52,7 @@
+ obj-$(CONFIG_TOUCHSCREEN_S3C2410)	+= s3c2410_ts.o
+ obj-$(CONFIG_TOUCHSCREEN_ST1232)	+= st1232.o
+ obj-$(CONFIG_TOUCHSCREEN_STMPE)		+= stmpe-ts.o
++obj-$(CONFIG_TOUCHSCREEN_SX8674)	+= sx8674.o
+ obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC)	+= ti_am335x_tsc.o
+ obj-$(CONFIG_TOUCHSCREEN_TNETV107X)	+= tnetv107x-ts.o
+ obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213)	+= touchit213.o
+--- /dev/null
++++ git//drivers/input/touchscreen/sx8674.c
+@@ -0,0 +1,477 @@
++/*
++ * drivers/input/touchscreen/sx8674.c
++ *
++ * Copyright (c) 2014 ARMadeus Systems
++ *	Sébastien Szymanski <sebastien.szymanski at armadeus.com>
++ *
++ *
++ * Using code from:
++ *  - sx865x.c
++ *	Copyright (c) 2013 U-MoBo Srl
++ *	Pierluigi Passaro <p.passaro at u-mobo.com>
++ *  - sx8650.c
++ *      Copyright (c) 2009 Wayne Roberts
++ *  - tsc2007.c
++ *      Copyright (c) 2008 Kwangwoo Lee
++ *  - ads7846.c
++ *      Copyright (c) 2005 David Brownell
++ *      Copyright (c) 2006 Nokia Corporation
++ *  - corgi_ts.c
++ *      Copyright (C) 2004-2005 Richard Purdie
++ *  - omap_ts.[hc], ads7846.h, ts_osk.c
++ *      Copyright (C) 2002 MontaVista Software
++ *      Copyright (C) 2004 Texas Instruments
++ *      Copyright (C) 2005 Dirk Behme
++ *
++ *  This program is free software; you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License version 2 as
++ *  published by the Free Software Foundation.
++ */
++
++#include <linux/i2c.h>
++#include <linux/init.h>
++#include <linux/input.h>
++#include <linux/interrupt.h>
++#include <linux/irq.h>
++#include <linux/module.h>
++#include <linux/of_irq.h>
++#include <linux/slab.h>
++
++/* analog channels */
++#define CH_X			0
++#define CH_Y			1
++#define CH_Z1			2
++#define CH_Z2			3
++#define CH_RX			5
++#define CH_RY			6
++
++/* commands */
++#define CMD_READ_REGISTER	0x40
++#define CMD_PENTRG		0xe0
++
++/* register addresses */
++#define I2C_REG_TOUCH0		0x00
++#define I2C_REG_TOUCH1		0x01
++#define I2C_REG_TOUCH2		0x02
++#define I2C_REG_TOUCH3		0x03
++#define I2C_REG_CHANMASK	0x04
++#define I2C_REG_IRQMASK		0x22
++#define I2C_REG_IRQSRC		0x23
++#define I2C_REG_STAT		0x24
++#define I2C_REG_SOFTRESET	0x3f
++
++/* value for I2C_REG_SOFTRESET */
++#define SOFTRESET_VALUE		0xde
++
++/* bits for I2C_REG_IRQSRC */
++#define IRQ_HAPTSHORT			(1 << 7)
++#define IRQ_PROXCLOSE			(1 << 6)
++#define IRQ_PROXFAR_CONVDONE		(1 << 5)
++#define IRQ_PROXCOMPDONE		(1 << 4)
++#define IRQ_PENTOUCH_TOUCHCONVDONE	(1 << 3)
++#define IRQ_PENRELEASE			(1 << 2)
++#define IRQ_TEMPWARNING			(1 << 1)
++#define IRQ_TEMPALARM			(1 << 0)
++
++/* bits for RegTouch1 */
++#define CONDIRQ			0x20
++#define FILT_NONE		0x00	/* no averaging */
++#define FILT_3SA		0x01	/* 3 sample averaging */
++#define FILT_5SA		0x02	/* 5 sample averaging */
++#define FILT_7SA		0x03	/* 7 samples, sort, then average of 3 middle samples */
++
++/* bits for I2C_REG_CHANMASK */
++#define CONV_X			0x80
++#define CONV_Y			0x40
++#define CONV_Z1			0x20
++#define CONV_Z2			0x10
++#define CONV_AUX		0x08
++#define CONV_RX			0x04
++#define CONV_RY			0x02
++
++/* cordinantes rate: higher nibble of CTRL0 register */
++#define RATE_MANUAL		0x00
++#define RATE_10CPS		0x10
++#define RATE_20CPS		0x20
++#define RATE_40CPS		0x30
++#define RATE_60CPS		0x40
++#define RATE_80CPS		0x50
++#define RATE_100CPS		0x60
++#define RATE_200CPS		0x70
++#define RATE_300CPS		0x80
++#define RATE_400CPS		0x90
++#define RATE_500CPS		0xa0
++#define RATE_1000CPS		0xb0
++#define RATE_2000CPS		0xc0
++#define RATE_3000CPS		0xd0
++#define RATE_4000CPS		0xe0
++#define RATE_5000CPS		0xf0
++
++/* power delay: lower nibble of CTRL0 register */
++#define POWDLY_IMMEDIATE	0x00
++#define POWDLY_1_1US		0x01
++#define POWDLY_2_2US		0x02
++#define POWDLY_4_4US		0x03
++#define POWDLY_8_9US		0x04
++#define POWDLY_17_8US		0x05
++#define POWDLY_35_5US		0x06
++#define POWDLY_71US		0x07
++#define POWDLY_140US		0x08
++#define POWDLY_280US		0x09
++#define POWDLY_570US		0x0a
++#define POWDLY_1_1MS		0x0b
++#define POWDLY_2_3MS		0x0c
++#define POWDLY_4_6MS		0x0d
++#define POWDLY_9MS		0x0e
++#define POWDLY_18MS		0x0f
++
++#define MAX_12BIT		((1 << 12) - 1)
++
++/* when changing the channel mask, also change the read length appropriately */
++#define CHAN_SINGLETOUCH_MASK	(CONV_X | CONV_Y | CONV_Z1 | CONV_Z2)
++#define CHAN_MULTITOUCH_MASK	(CHAN_SINGLETOUCH_MASK | CONV_RX | CONV_RY)
++#define NUM_SINGLETOUCH_CHANS	4
++#define NUM_MULTITOUCH_CHANS	6
++#define NUM_MAX_CHANS		NUM_MULTITOUCH_CHANS
++#define CHAN_READ_LENGTH(chans)	(chans << 1)
++
++#define SX_MULTITOUCH		0x01
++#define SX_PROXIMITY_SENSING	0x02
++#define SX_HAPTICS_GENERIC	0x04
++#define SX_HAPTICS_IMMERSION	0x08
++
++struct ts_event {
++	u16 x, y;
++	u16 z1, z2;
++	u16 rx, ry;
++};
++
++struct sx8674 {
++	struct input_dev *input;
++	struct ts_event tc;
++
++	struct i2c_client *client;
++
++	struct workqueue_struct *ts_workq;
++	struct delayed_work pen_event_work;
++
++	u16 features;
++	u16 x_plate_ohms;
++	u16 y_plate_ohms;
++
++	int irq;
++};
++
++static const struct i2c_device_id sx8674_idtable[] = {
++	{ "sx8654", SX_PROXIMITY_SENSING | SX_HAPTICS_GENERIC },
++	{ "sx8674", SX_MULTITOUCH | SX_PROXIMITY_SENSING | SX_HAPTICS_GENERIC },
++	{ /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(i2c, sx8674_idtable);
++
++static int sx8674_read_values(struct sx8674 *ts)
++{
++	s32 data;
++	u16 vals[NUM_MAX_CHANS+1];	// +1 for last dummy read
++	int length, chan_read_length;
++	int i;
++
++	memset(&(ts->tc), 0, sizeof(ts->tc));
++	/* The protocol and raw data format from i2c interface:
++	 * S Addr R A [DataLow] A [DataHigh] A (repeat) NA P
++	 * Where DataLow has (channel | [D11-D8]), DataHigh has [D7-D0].
++	 */
++	if (ts->features & SX_MULTITOUCH)
++		chan_read_length = CHAN_READ_LENGTH(NUM_MULTITOUCH_CHANS);
++	else
++		chan_read_length = CHAN_READ_LENGTH(NUM_SINGLETOUCH_CHANS);
++
++	length = i2c_master_recv(ts->client, (char *)vals, chan_read_length);
++
++	if (likely(length == chan_read_length)) {
++		length >>= 1;
++		for (i = 0; i < length; i++) {
++			u16 ch;
++			data = swab16(vals[i]);
++			if (unlikely(data & 0x8000)) {
++				dev_err(&ts->client->dev, "hibit @ %d [0x%04x]\n", i, data);
++				return -EAGAIN;
++			}
++			ch = data >> 12;
++			if (ch == CH_X)
++				ts->tc.x = data & 0xfff;
++			else if (ch == CH_Y)
++				ts->tc.y = data & 0xfff;
++			else if (ch == CH_Z1)
++				ts->tc.z1 = data & 0xfff;
++			else if (ch == CH_Z2)
++				ts->tc.z2 = data & 0xfff;
++			else if (ch == CH_RX)
++				ts->tc.rx = data & 0xfff;
++			else if (ch == CH_RY)
++				ts->tc.ry = data & 0xfff;
++			else
++				dev_err(&ts->client->dev, "? CH%d %x\n", ch, data & 0xfff);
++		}
++	} else
++		dev_err(&ts->client->dev, "%d = recv()\n", length);
++
++	dev_dbg(&ts->client->dev, "X:%d Y:%d Z1:%d Z2:%d RX:%d RY:%d\n",
++		ts->tc.x, ts->tc.y, ts->tc.z1, ts->tc.z2, ts->tc.rx, ts->tc.ry);
++	dev_dbg(&ts->client->dev, "X+:%d Y+:%d X-:%d Y-:%d\n",
++		ts->tc.x+ts->tc.rx, ts->tc.y+ts->tc.ry, ts->tc.x-ts->tc.rx, ts->tc.y-ts->tc.ry);
++
++	return !ts->tc.z1;	/* return 0 only if pressure not 0 */
++}
++
++static void sx8674_pen_irq_worker(struct work_struct *work)
++{
++	struct sx8674 *ts = container_of(work, struct sx8674, pen_event_work.work);
++	u8 irqsrc;
++
++	dev_dbg(&ts->client->dev, "%s\n", __func__);
++
++	irqsrc = i2c_smbus_read_byte_data(ts->client, CMD_READ_REGISTER | I2C_REG_IRQSRC);
++	dev_dbg(&ts->client->dev, "irqsrc: 0x%x", irqsrc);
++
++	if (irqsrc & IRQ_PENRELEASE) {
++		dev_dbg(&ts->client->dev, "pen release interrupt");
++		input_report_key(ts->input, BTN_TOUCH, 0);
++		input_sync(ts->input);
++	}
++
++	if (irqsrc & IRQ_PENTOUCH_TOUCHCONVDONE) {
++		dev_dbg(&ts->client->dev, "pen touch interrupt");
++
++		sx8674_read_values(ts);
++
++		input_report_abs(ts->input, ABS_X, ts->tc.x);
++		input_report_abs(ts->input, ABS_Y, ts->tc.y);
++		input_report_key(ts->input, BTN_TOUCH, 1);
++		input_sync(ts->input);
++
++		dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n", ts->tc.x, ts->tc.y, 0);
++	}
++}
++
++static irqreturn_t sx8674_irq(int irq, void *handle)
++{
++	struct sx8674 *ts = handle;
++
++	/* the reading of the samples can be time-consuming if using
++	 * a slow i2c, so the work is done in a queue */
++	queue_delayed_work(ts->ts_workq, &ts->pen_event_work, 0);
++
++	return IRQ_HANDLED;
++}
++
++static int get_rmsel(u16 plate_ohms)
++{
++	int rmsel;
++
++	if (plate_ohms < 187)
++		rmsel = 0;
++	else if (plate_ohms < 312)
++		rmsel = 1;
++	else if (plate_ohms < 938)
++		rmsel = 2;
++	else if (plate_ohms < 1875)
++		rmsel = 3;
++	else if (plate_ohms < 4375)
++		rmsel = 4;
++	else if (plate_ohms < 9375)
++		rmsel = 5;
++	else if (plate_ohms < 18780)
++		rmsel = 6;
++	else
++		rmsel = 7;
++
++	return rmsel;
++}
++
++static int sx8674_parse_dt(struct device *dev, struct sx8674 *ts) {
++	struct device_node *node;
++
++	if (!dev || !ts)
++		return -ENOENT;
++
++	node = dev->of_node;
++	if (!node)
++		return -ENOENT;
++
++	ts->irq = irq_of_parse_and_map(node, 0);
++	if (!ts->irq)
++		return -EINVAL;
++
++	if(of_property_read_u16(node, "x-plate-ohms", &ts->x_plate_ohms))
++		return -EINVAL;
++
++	if(of_property_read_u16(node, "y-plate-ohms", &ts->y_plate_ohms))
++		return -EINVAL;
++
++	return 0;
++}
++
++static int sx8674_probe(struct i2c_client *client,
++			const struct i2c_device_id *id)
++{
++	struct sx8674 *ts;
++	struct input_dev *input_dev;
++	int err;
++	u16 rmselx, rmsely;
++
++	if (!i2c_check_functionality(client->adapter,
++				     I2C_FUNC_SMBUS_READ_WORD_DATA))
++		return -EIO;
++
++	ts = devm_kzalloc(&client->dev, sizeof(struct sx8674), GFP_KERNEL);
++	input_dev = devm_input_allocate_device(&client->dev);
++	if (!ts || !input_dev)
++		return -ENOMEM;
++
++	err = sx8674_parse_dt(&client->dev, ts);
++	if (err)
++		return err;
++
++	ts->client = client;
++	i2c_set_clientdata(client, ts);
++
++	ts->input = input_dev;
++
++	ts->features = id->driver_data;
++
++	input_dev->name = "SX8674 Touchscreen";
++	input_dev->id.bustype = BUS_I2C;
++	input_dev->dev.parent = &client->dev;
++	input_set_drvdata(input_dev, ts);
++
++	__set_bit(EV_ABS, input_dev->evbit);
++	__set_bit(EV_KEY, input_dev->evbit);
++	__set_bit(BTN_TOUCH, input_dev->keybit);
++	__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
++
++	input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);
++	input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
++	/* TODO */
++	/*input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0);*/
++
++	err = i2c_smbus_write_byte_data(client, I2C_REG_SOFTRESET,
++					SOFTRESET_VALUE);
++	if (err < 0) {
++		dev_err(&client->dev, "writing softreset value failed");
++		return -EIO;
++	}
++
++	if (ts->features & SX_MULTITOUCH) {
++		input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, MAX_12BIT, 0, 0);
++		input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, MAX_12BIT, 0, 0);
++		input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, MAX_12BIT, 0, 0);
++		input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, 1, 0, 0);
++
++		/* set mask to convert X, Y, Z1, Z2, RX, RY for CH_SEQ */
++		err = i2c_smbus_write_byte_data(client, I2C_REG_CHANMASK,
++						CHAN_MULTITOUCH_MASK);
++		if (err < 0) {
++			dev_err(&client->dev, "writing to I2C_REG_CHANMASK failed");
++			return -EIO;
++		}
++
++		if (ts->x_plate_ohms < 100)
++			ts->x_plate_ohms = 100;
++		if (ts->y_plate_ohms < 100)
++			ts->y_plate_ohms = 100;
++		rmselx = get_rmsel(ts->x_plate_ohms);
++		rmsely = get_rmsel(ts->y_plate_ohms);
++
++		err = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH3,
++						(rmsely << 3) | rmselx);
++		if (err < 0) {
++			dev_err(&client->dev, "writing to I2C_REG_TOUCH3 failed");
++			return -EIO;
++		}
++	} else {
++		/* set mask to convert X, Y, Z1, Z2 for CH_SEQ */
++		err = i2c_smbus_write_byte_data(client, I2C_REG_CHANMASK, CHAN_SINGLETOUCH_MASK);
++		if (err < 0) {
++			dev_err(&client->dev, "writing to I2C_REG_CHANMASK failed");
++			return -EIO;
++		}
++	}
++
++	err = i2c_smbus_write_byte_data(client, I2C_REG_IRQMASK, (IRQ_PENTOUCH_TOUCHCONVDONE | IRQ_PENRELEASE));
++	if (err < 0) {
++		dev_err(&client->dev, "writing to I2C_REG_IRQMASK failed");
++		return -EIO;
++	}
++
++	err = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH1, CONDIRQ | FILT_7SA);
++	if (err < 0) {
++		dev_err(&client->dev, "writing to I2C_REG_TOUCH1 failed");
++		return -EIO;
++	}
++
++	err = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, RATE_5000CPS | POWDLY_1_1MS);
++	if (err < 0) {
++		dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed");
++		return -EIO;
++	}
++
++	ts->ts_workq = create_singlethread_workqueue("sx8674");
++	if (ts->ts_workq == NULL) {
++		dev_err(&client->dev, "failed to create workqueue\n");
++		return -ENOENT;
++	}
++
++	INIT_DELAYED_WORK(&ts->pen_event_work, sx8674_pen_irq_worker);
++
++	err = devm_request_threaded_irq(&client->dev, ts->irq,
++					NULL, sx8674_irq,
++					IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
++					client->dev.driver->name, ts);
++	if (err < 0) {
++		dev_err(&client->dev, "irq %d busy?\n", ts->irq);
++		return -EBUSY;
++	}
++
++	err = input_register_device(input_dev);
++	if (err)
++		return -ENONET;
++
++	/* enter pen-trigger mode */
++	err = i2c_smbus_write_byte(client, CMD_PENTRG);
++	if (err < 0) {
++		dev_err(&client->dev, "writing command CMD_PENTRG failed");
++		input_unregister_device(input_dev);
++		return -EIO;
++	}
++
++	return 0;
++}
++
++static int sx8674_remove(struct i2c_client *client)
++{
++	struct sx8674 *ts = i2c_get_clientdata(client);
++
++	input_unregister_device(ts->input);
++
++	cancel_delayed_work_sync(&ts->pen_event_work);
++	destroy_workqueue(ts->ts_workq);
++
++	return 0;
++}
++
++static struct i2c_driver sx8674_driver = {
++	.driver = {
++		.owner = THIS_MODULE,
++		.name = "sx8674"
++	},
++	.id_table = sx8674_idtable,
++	.probe = sx8674_probe,
++	.remove	= sx8674_remove,
++};
++
++module_i2c_driver(sx8674_driver)
++
++MODULE_AUTHOR("Sébastien Szymanski <sebastien.szymanski at armadeus.com>");
++MODULE_DESCRIPTION("SX8674 Touchscreen Driver");
++MODULE_LICENSE("GPL");
diff --git a/recipes-kernel/linux/linux-apf6/0505-armadeus-add_mcp7940x_rtc_driver.patch b/recipes-kernel/linux/linux-apf6/0505-armadeus-add_mcp7940x_rtc_driver.patch
new file mode 100644
index 0000000..f2c7d37
--- /dev/null
+++ b/recipes-kernel/linux/linux-apf6/0505-armadeus-add_mcp7940x_rtc_driver.patch
@@ -0,0 +1,524 @@
+--- /dev/null
++++ git/drivers/rtc/rtc-mcp7940x.c
+@@ -0,0 +1,492 @@
++/*
++ * RTC client/driver for the Microchip MCP7940x Real-Time Clock over I2C
++ *
++ * Based on code by Scott Wood <scottwood at freescale.com>
++ * which was based on code by Randy Vinson <rvinson at mvista.com>,
++ * which was based on the m41t00.c by Mark Greer <mgreer at mvista.com>.
++ *
++ * Copyright (C) 2014 Armadeus Systems
++ *
++ * This file is licensed under the terms of the GNU General Public License
++ * version 2. This program is licensed "as is" without any warranty of any
++ * kind, whether express or implied.
++ */
++/*
++ * It would be more efficient to use i2c msgs/i2c_transfer directly but, as
++ * recommened in .../Documentation/i2c/writing-clients section
++ * "Sending and receiving", using SMBus level communication is preferred.
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/interrupt.h>
++#include <linux/i2c.h>
++#include <linux/rtc.h>
++#include <linux/bcd.h>
++#include <linux/slab.h>
++
++/* Time of Day */
++#define MCP7940X_REG_TD_SECONDS		0x00
++#define MCP7940X_REG_TD_MINUTES		0x01
++#define MCP7940X_REG_TD_HOURS		0x02
++#define MCP7940X_REG_TD_DAY		0x03
++#define MCP7940X_REG_TD_DATE		0x04
++#define MCP7940X_REG_TD_MONTH		0x05
++#define MCP7940X_REG_TD_YEAR		0x06
++
++/* Controls */
++#define MCP7940X_REG_CONTROL		0x07
++#define MCP7940X_REG_CALIBRATION	0x08
++#define MCP7940X_REG_UNLOCK_ID		0x08
++
++/* Alarm 0 */
++#define MCP7940X_REG_ALRM0_SECONDS	0x0A
++#define MCP7940X_REG_ALRM0_MINUTES	0x0B
++#define MCP7940X_REG_ALRM0_HOURS	0x0C
++#define MCP7940X_REG_ALRM0_DAY		0x0D
++#define MCP7940X_REG_ALRM0_DATE		0x0E
++#define MCP7940X_REG_ALRM0_MONTH	0x0F
++
++/* Alarm 1 */
++#define MCP7940X_REG_ALRM1_SECONDS	0x11
++#define MCP7940X_REG_ALRM1_MINUTES	0x12
++#define MCP7940X_REG_ALRM1_HOURS	0x13
++#define MCP7940X_REG_ALRM1_DAY		0x14
++#define MCP7940X_REG_ALRM1_DATE		0x15
++#define MCP7940X_REG_ALRM1_MONTH	0x16
++
++/* Timestamp 0 */
++#define MCP7940X_REG_TS0_MINUTES	0x18
++#define MCP7940X_REG_TS0_HOURS		0x19
++#define MCP7940X_REG_TS0_DATE		0x1A
++#define MCP7940X_REG_TS0_MONTH		0x1B
++
++/* Timestamp 1 */
++#define MCP7940X_REG_TS1_MINUTES	0x1C
++#define MCP7940X_REG_TS1_HOURS		0x1D
++#define MCP7940X_REG_TS1_DATE		0x1E
++#define MCP7940X_REG_TS1_MONTH		0x1F
++
++/* bits definitions */
++#define MCP7940X_START_BITS	0x80
++#define MCP7940X_24H_BITS	0x40
++#define MCP7940X_VBATEN		0x08
++
++static const struct i2c_device_id mcp7940x_id[] = {
++	{ "mcp79400", 0 }, /* no unique ID */
++	{ "mcp79401", 1 }, /* EUI-48 */
++	{ "mcp79402", 2 }, /* EUI-64 */
++	{ }
++};
++MODULE_DEVICE_TABLE(i2c, mcp7940x_id);
++
++struct mcp7940x {
++	struct rtc_device *rtc;
++	struct i2c_client *client;
++	int exiting;
++	int irq;
++	unsigned int irqen;
++	int alrm_sec;
++	int alrm_min;
++	int alrm_hour;
++	int alrm_mday;
++	spinlock_t lock;
++};
++
++static struct i2c_driver mcp7940x_driver;
++
++#if 0
++static void mcp7940x_reg_dump(struct device *dev)
++{
++	struct i2c_client *client = to_i2c_client(dev);
++
++	printk(" MCP7940X_REG_TD_SECONDS	(0x%02X) -> 0x%02X\n", MCP7940X_REG_TD_SECONDS	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_TD_SECONDS));
++	printk(" MCP7940X_REG_TD_MINUTES	(0x%02X) -> 0x%02X\n", MCP7940X_REG_TD_MINUTES	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_TD_MINUTES));
++	printk(" MCP7940X_REG_TD_HOURS		(0x%02X) -> 0x%02X\n", MCP7940X_REG_TD_HOURS	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_TD_HOURS));
++	printk(" MCP7940X_REG_TD_DAY		(0x%02X) -> 0x%02X\n", MCP7940X_REG_TD_DAY	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_TD_DAY));
++	printk(" MCP7940X_REG_TD_DATE		(0x%02X) -> 0x%02X\n", MCP7940X_REG_TD_DATE	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_TD_DATE));
++	printk(" MCP7940X_REG_TD_MONTH		(0x%02X) -> 0x%02X\n", MCP7940X_REG_TD_MONTH	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_TD_MONTH));
++	printk(" MCP7940X_REG_TD_YEAR		(0x%02X) -> 0x%02X\n", MCP7940X_REG_TD_YEAR	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_TD_YEAR));
++	printk(" MCP7940X_REG_CONTROL		(0x%02X) -> 0x%02X\n", MCP7940X_REG_CONTROL	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_CONTROL));
++	printk(" MCP7940X_REG_CALIBRATION	(0x%02X) -> 0x%02X\n", MCP7940X_REG_CALIBRATION	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_CALIBRATION));
++	printk(" MCP7940X_REG_UNLOCK_ID		(0x%02X) -> 0x%02X\n", MCP7940X_REG_UNLOCK_ID	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_UNLOCK_ID));
++	printk(" MCP7940X_REG_ALRM0_SECONDS	(0x%02X) -> 0x%02X\n", MCP7940X_REG_ALRM0_SECONDS,i2c_smbus_read_byte_data(client,MCP7940X_REG_ALRM0_SECONDS));
++	printk(" MCP7940X_REG_ALRM0_MINUTES	(0x%02X) -> 0x%02X\n", MCP7940X_REG_ALRM0_MINUTES,i2c_smbus_read_byte_data(client,MCP7940X_REG_ALRM0_MINUTES));
++	printk(" MCP7940X_REG_ALRM0_HOURS	(0x%02X) -> 0x%02X\n", MCP7940X_REG_ALRM0_HOURS	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_ALRM0_HOURS));
++	printk(" MCP7940X_REG_ALRM0_DAY		(0x%02X) -> 0x%02X\n", MCP7940X_REG_ALRM0_DAY	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_ALRM0_DAY));
++	printk(" MCP7940X_REG_ALRM0_DATE	(0x%02X) -> 0x%02X\n", MCP7940X_REG_ALRM0_DATE	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_ALRM0_DATE));
++	printk(" MCP7940X_REG_ALRM0_MONTH	(0x%02X) -> 0x%02X\n", MCP7940X_REG_ALRM0_MONTH	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_ALRM0_MONTH));
++	printk(" MCP7940X_REG_ALRM1_SECONDS	(0x%02X) -> 0x%02X\n", MCP7940X_REG_ALRM1_SECONDS,i2c_smbus_read_byte_data(client,MCP7940X_REG_ALRM1_SECONDS));
++	printk(" MCP7940X_REG_ALRM1_MINUTES	(0x%02X) -> 0x%02X\n", MCP7940X_REG_ALRM1_MINUTES,i2c_smbus_read_byte_data(client,MCP7940X_REG_ALRM1_MINUTES));
++	printk(" MCP7940X_REG_ALRM1_HOURS	(0x%02X) -> 0x%02X\n", MCP7940X_REG_ALRM1_HOURS	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_ALRM1_HOURS));
++	printk(" MCP7940X_REG_ALRM1_DAY		(0x%02X) -> 0x%02X\n", MCP7940X_REG_ALRM1_DAY	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_ALRM1_DAY));
++	printk(" MCP7940X_REG_ALRM1_DATE	(0x%02X) -> 0x%02X\n", MCP7940X_REG_ALRM1_DATE	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_ALRM1_DATE));
++	printk(" MCP7940X_REG_ALRM1_MONTH	(0x%02X) -> 0x%02X\n", MCP7940X_REG_ALRM1_MONTH	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_ALRM1_MONTH));
++	printk(" MCP7940X_REG_TS0_MINUTES	(0x%02X) -> 0x%02X\n", MCP7940X_REG_TS0_MINUTES	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_TS0_MINUTES));
++	printk(" MCP7940X_REG_TS0_HOURS		(0x%02X) -> 0x%02X\n", MCP7940X_REG_TS0_HOURS	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_TS0_HOURS));
++	printk(" MCP7940X_REG_TS0_DATE		(0x%02X) -> 0x%02X\n", MCP7940X_REG_TS0_DATE	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_TS0_DATE));
++	printk(" MCP7940X_REG_TS0_MONTH		(0x%02X) -> 0x%02X\n", MCP7940X_REG_TS0_MONTH	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_TS0_MONTH));
++	printk(" MCP7940X_REG_TS1_MINUTES	(0x%02X) -> 0x%02X\n", MCP7940X_REG_TS1_MINUTES	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_TS1_MINUTES));
++	printk(" MCP7940X_REG_TS1_HOURS		(0x%02X) -> 0x%02X\n", MCP7940X_REG_TS1_HOURS	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_TS1_HOURS));
++	printk(" MCP7940X_REG_TS1_DATE		(0x%02X) -> 0x%02X\n", MCP7940X_REG_TS1_DATE	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_TS1_DATE));
++	printk(" MCP7940X_REG_TS1_MONTH		(0x%02X) -> 0x%02X\n", MCP7940X_REG_TS1_MONTH	 ,i2c_smbus_read_byte_data(client,MCP7940X_REG_TS1_MONTH));
++}
++#endif
++
++static int mcp7940x_read_time(struct device *dev, struct rtc_time *time)
++{
++	struct i2c_client *client = to_i2c_client(dev);
++	int ret;
++
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_TD_SECONDS);
++	if (ret < 0)
++		return ret;
++	time->tm_sec = ((ret&0x70)>>4)*10 + (ret&0x0F);
++
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_TD_MINUTES);
++	if (ret < 0)
++		return ret;
++	time->tm_min = ((ret&0x70)>>4)*10 + (ret&0x0F);
++
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_TD_HOURS);
++	if (ret < 0)
++		return ret;
++	time->tm_hour = ((ret&0x30)>>4)*10 + (ret&0x0F);
++
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_TD_DATE);
++	if (ret < 0)
++		return ret;
++	time->tm_mday = ((ret&0x30)>>4)*10 + (ret&0x0F);
++
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_TD_MONTH);
++	if (ret < 0)
++		return ret;
++	time->tm_mon = (((ret&0x10)>>4)*10 + (ret&0x0F)) - 1;
++
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_TD_YEAR);
++	if (ret < 0)
++		return ret;
++	time->tm_year = ((((ret&0xF0)>>4)*10 + (ret&0x0F))) + 100;
++
++	return 0;
++}
++
++static int mcp7940x_set_time(struct device *dev, struct rtc_time *time)
++{
++	struct i2c_client *client = to_i2c_client(dev);
++	int ret;
++	int value;
++	int day;
++
++	value = ((((time->tm_sec/10)&0x07)<<4)|(time->tm_sec % 10))|
++		MCP7940X_START_BITS;
++	ret = i2c_smbus_write_byte_data(client,
++					MCP7940X_REG_TD_SECONDS,
++					value);
++	if (ret < 0)
++		return ret;
++
++	value = (((time->tm_min/10)&0x07)<<4)|(time->tm_min % 10);
++	ret = i2c_smbus_write_byte_data(client,
++					MCP7940X_REG_TD_MINUTES,
++					value);
++	if (ret < 0)
++		return ret;
++
++	value = ((((time->tm_hour/10)&0x03)<<4)|(time->tm_hour % 10));
++	ret = i2c_smbus_write_byte_data(client,
++					MCP7940X_REG_TD_HOURS,
++					value);
++	if (ret < 0)
++		return ret;
++
++	value = (((time->tm_mday/10)&0x03)<<4)|(time->tm_mday % 10);
++	ret = i2c_smbus_write_byte_data(client,
++					MCP7940X_REG_TD_DATE,
++					value);
++	if (ret < 0)
++		return ret;
++
++	/*****/
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_TD_DAY);
++	if (ret < 0)
++		return ret;
++	day = (time->tm_wday == 0)?7:time->tm_wday;
++	ret = i2c_smbus_write_byte_data(client,
++					MCP7940X_REG_TD_DAY,
++					(ret&0xF8)|day);
++	if (ret < 0)
++		return ret;
++	/*****/
++
++	value = ((((time->tm_mon+1)/10)&0x01)<<4)|((time->tm_mon+1) % 10);
++	ret = i2c_smbus_write_byte_data(client,
++					MCP7940X_REG_TD_MONTH,
++					value);
++	if (ret < 0)
++		return ret;
++
++	value = ((((time->tm_year-100)/10)&0x0F)<<4)|((time->tm_year-100) % 10);
++	ret = i2c_smbus_write_byte_data(client,
++					MCP7940X_REG_TD_YEAR,
++					value);
++	if (ret < 0)
++		return ret;
++
++	return 0;
++}
++
++//struct rtc_wkalrm {
++//        unsigned char enabled;  /* 0 = alarm disabled, 1 = alarm enabled */
++//        unsigned char pending;  /* 0 = alarm not pending, 1 = alarm pending */
++//        struct rtc_time time;   /* time the alarm is set to */
++//};
++
++static int mcp79400_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) {
++	struct i2c_client *client = to_i2c_client(dev);
++	struct rtc_time *time = &wkalrm->time;
++	int ret;
++
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_ALRM0_SECONDS);
++	if (ret < 0)
++		return ret;
++	time->tm_sec = ((ret&0x70)>>4)*10 + (ret&0x0F);
++
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_ALRM0_MINUTES);
++	if (ret < 0)
++		return ret;
++	time->tm_min = ((ret&0x70)>>4)*10 + (ret&0x0F);
++
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_ALRM0_HOURS);
++	if (ret < 0)
++		return ret;
++	time->tm_hour = ((ret&0x30)>>4)*10 + (ret&0x0F);
++
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_ALRM0_DATE);
++	if (ret < 0)
++		return ret;
++	time->tm_mday = ((ret&0x30)>>4)*10 + (ret&0x0F);
++
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_ALRM0_MONTH);
++	if (ret < 0)
++		return ret;
++	time->tm_mon = (((ret&0x10)>>4)*10 + (ret&0x0F)) - 1;
++
++	time->tm_year = -1;
++	time->tm_wday = -1;
++	time->tm_yday = -1;
++	time->tm_isdst= -1;
++
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_CONTROL);
++	if (ret < 0)
++		return ret;
++	wkalrm->enabled	= ((ret&0x10)>>4);
++
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_ALRM0_DAY);
++	if (ret < 0)
++		return ret;
++	wkalrm->pending	= ((ret&0x08)>>3);
++
++	return 0;
++}
++
++static int mcp79400_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) {
++	struct i2c_client *client = to_i2c_client(dev);
++	struct rtc_time *time = &wkalrm->time;
++	int ret;
++	int value;
++	int day;
++
++	/* clear pending alarm */
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_ALRM0_DAY);
++	if (ret < 0)
++		return ret;
++	day = (time->tm_wday == 0)?7:time->tm_wday;
++	ret = i2c_smbus_write_byte_data(client,
++					MCP7940X_REG_ALRM0_DAY,
++					((ret|0x70)&0x70)|day);
++	if (ret < 0)
++		return ret;
++
++	value = ((((time->tm_sec/10)&0x07)<<4)|(time->tm_sec % 10))|
++							MCP7940X_START_BITS;
++	ret = i2c_smbus_write_byte_data(client,
++					MCP7940X_REG_ALRM0_SECONDS,
++					value);
++	if (ret < 0)
++		return ret;
++
++	value = (((time->tm_min/10)&0x07)<<4)|(time->tm_min % 10);
++	ret = i2c_smbus_write_byte_data(client,
++					MCP7940X_REG_ALRM0_MINUTES,
++					value);
++	if (ret < 0)
++		return ret;
++
++	value = ((((time->tm_hour/10)&0x03)<<4)|(time->tm_hour % 10));
++	ret = i2c_smbus_write_byte_data(client,
++					MCP7940X_REG_ALRM0_HOURS,
++					value);
++	if (ret < 0)
++		return ret;
++
++	value = (((time->tm_mday/10)&0x03)<<4)|(time->tm_mday % 10);
++	ret = i2c_smbus_write_byte_data(client,
++					MCP7940X_REG_ALRM0_DATE,
++					value);
++	if (ret < 0)
++		return ret;
++
++	value = ((((time->tm_mon+1)/10)&0x01)<<4)|((time->tm_mon+1) % 10);
++	ret = i2c_smbus_write_byte_data(client,
++					MCP7940X_REG_ALRM0_MONTH,
++					value);
++	if (ret < 0)
++		return ret;
++
++	ret = i2c_smbus_read_byte_data(client, MCP7940X_REG_CONTROL);
++	if (ret < 0)
++		return ret;
++	if (wkalrm->enabled) {
++		ret = i2c_smbus_write_byte_data(client,
++						MCP7940X_REG_CONTROL,
++						ret | 0x10);
++	} else {
++		ret = i2c_smbus_write_byte_data(client,
++						MCP7940X_REG_CONTROL,
++						ret & ~(0x10));
++	}
++	if (ret < 0)
++		return ret;
++
++	return 0;
++}
++
++/* TODO: see rtc-ds1511.c */
++static int mcp79400_alarm_irq_enable(struct device *dev, unsigned int enabled)
++{
++	return 0;
++}
++
++static const struct rtc_class_ops mcp7940x_rtc_ops = {
++	.read_time = mcp7940x_read_time,
++	.set_time = mcp7940x_set_time,
++	.read_alarm = mcp79400_read_alarm, /* Use ALARM0 only */
++	.set_alarm = mcp79400_set_alarm,
++	.alarm_irq_enable = mcp79400_alarm_irq_enable,
++};
++
++static const struct of_device_id mcp7940x_rtc_dt_match[] = {
++	{ .compatible = "microchip,mcp7940x-rtc", },
++	{ /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, mcp7940x_rtc_dt_match);
++
++static int mcp7940x_probe(struct i2c_client *client,
++			const struct i2c_device_id *id)
++{
++	struct mcp7940x *mcp7940x;
++	int ret;
++
++	mcp7940x = devm_kzalloc(&client->dev, sizeof(struct mcp7940x),
++				GFP_KERNEL);
++	if (!mcp7940x)
++		return -ENOMEM;
++
++	mcp7940x->client = client;
++	i2c_set_clientdata(client, mcp7940x);
++
++	ret = i2c_smbus_read_byte_data(mcp7940x->client, MCP7940X_REG_TD_HOURS);
++	if (ret < 0)
++		return -EIO;
++
++	/* set rtc to 24h format */
++	if ((ret & MCP7940X_24H_BITS)) {
++		ret = i2c_smbus_write_byte_data(mcp7940x->client,
++						MCP7940X_REG_TD_HOURS,
++						(u8)(ret & ~MCP7940X_24H_BITS));
++		if(ret < 0)
++			return -EIO;
++	}
++
++	ret = i2c_smbus_read_byte_data(mcp7940x->client, MCP7940X_REG_TD_DAY);
++	if (ret < 0)
++		return -EIO;
++
++	/* Use external baterie if Vcc fails */
++	if (!(ret & MCP7940X_VBATEN)) {
++		ret = i2c_smbus_write_byte_data(mcp7940x->client,
++						MCP7940X_REG_TD_DAY,
++						(u8)(ret | MCP7940X_VBATEN));
++		if(ret < 0)
++			return -EIO;
++	}
++
++	ret = i2c_smbus_read_byte_data(mcp7940x->client, MCP7940X_REG_TD_SECONDS);
++	if (ret < 0)
++		return -EIO;
++
++	/* If time counter not launched, launch it */
++	if (!(ret & MCP7940X_START_BITS)) {
++		ret = i2c_smbus_write_byte_data(mcp7940x->client,
++						MCP7940X_REG_TD_SECONDS,
++						(u8)(ret | MCP7940X_START_BITS));
++		if(ret < 0)
++			return -EIO;
++	}
++
++	device_set_wakeup_capable(&client->dev, 1);
++
++	mcp7940x->rtc = devm_rtc_device_register(&client->dev, client->name,
++						&mcp7940x_rtc_ops, THIS_MODULE);
++	if (IS_ERR(mcp7940x->rtc)) {
++		dev_err(&client->dev, "unable to register the class device\n");
++		return PTR_ERR(mcp7940x->rtc);
++	}
++
++	mcp7940x->rtc->uie_unsupported = 1;
++
++	return 0;
++}
++
++static int mcp7940x_remove(struct i2c_client *client)
++{
++	return 0;
++}
++
++#ifdef CONFIG_PM
++static int mcp7940x_suspend(struct device *dev)
++{
++	return 0;
++}
++
++static int mcp7940x_resume(struct device *dev)
++{
++	return 0;
++}
++
++static const struct dev_pm_ops mcp7940x_pm_ops = {
++	.suspend        = mcp7940x_suspend,
++	.resume         = mcp7940x_resume,
++};
++#endif
++
++static struct i2c_driver mcp7940x_driver = {
++	.driver = {
++		.name	= "rtc-mcp7940x",
++		.owner	= THIS_MODULE,
++#ifdef CONFIG_PM
++		.pm	= &mcp7940x_pm_ops,
++#endif
++		.of_match_table = mcp7940x_rtc_dt_match,
++	},
++	.probe = mcp7940x_probe,
++	.remove = mcp7940x_remove,
++	.id_table = mcp7940x_id,
++};
++
++module_i2c_driver(mcp7940x_driver)
++
++MODULE_AUTHOR("Fabien Marteau <fabien.marteau at armadeus.com>");
++MODULE_DESCRIPTION("Microchip mcp7940x RTC Driver");
++MODULE_LICENSE("GPL");
+--- git/drivers/rtc/Kconfig.old	2015-03-04 12:01:27.402342574 +0100
++++ git/drivers/rtc/Kconfig	2015-03-04 12:01:18.434342660 +0100
+@@ -488,6 +488,16 @@
+ 	  This driver can also be built as a module. If so, the module
+ 	  will be called rtc-rv3029c2.
+ 
++config RTC_DRV_MCP7940X
++	tristate "Microchip MCP7940X"
++	help
++	  If you say yes here you get support for Microchip
++	  MCP7940x real-time clock chips.
++	  The alarm functionality is not supported.
++
++	  This driver can also be built as a module. If so, the module
++	  will be called rtc-mcp7940x.
++
+ endif # I2C
+ 
+ comment "SPI RTC drivers"
+--- git/drivers/rtc/Makefile.old	2015-03-04 12:01:56.666342295 +0100
++++ git/drivers/rtc/Makefile	2015-03-04 12:01:49.234342366 +0100
+@@ -76,6 +76,7 @@
+ obj-$(CONFIG_RTC_DRV_MAX6902)	+= rtc-max6902.o
+ obj-$(CONFIG_RTC_DRV_MAX77686)	+= rtc-max77686.o
+ obj-$(CONFIG_RTC_DRV_MC13XXX)	+= rtc-mc13xxx.o
++obj-$(CONFIG_RTC_DRV_MCP7940X)  += rtc-mcp7940x.o
+ obj-$(CONFIG_RTC_DRV_MSM6242)	+= rtc-msm6242.o
+ obj-$(CONFIG_RTC_DRV_MPC5121)	+= rtc-mpc5121.o
+ obj-$(CONFIG_RTC_DRV_MV)	+= rtc-mv.o
diff --git a/recipes-kernel/linux/linux-apf6/059X-armadeus-add-wireless-driver.patch b/recipes-kernel/linux/linux-apf6/059X-armadeus-add-wireless-driver.patch
new file mode 100644
index 0000000..321f64f
--- /dev/null
+++ b/recipes-kernel/linux/linux-apf6/059X-armadeus-add-wireless-driver.patch
@@ -0,0 +1,11643 @@
+diff -Nuar git.old/Documentation/devicetree/bindings/net/wireless/ti-wilink.txt git/Documentation/devicetree/bindings/net/wireless/ti-wilink.txt
+--- git.old/Documentation/devicetree/bindings/net/wireless/ti-wilink.txt	1970-01-01 01:00:00.000000000 +0100
++++ git/Documentation/devicetree/bindings/net/wireless/ti-wilink.txt	2015-03-04 17:09:05.110166548 +0100
+@@ -0,0 +1,68 @@
++TI WiLink Wireless Modules Device Tree Bindings
++===============================================
++
++The WiLink modules provide wireless connectivity, such as WLAN,
++Bluetooth, FM and NFC.
++
++There are several different modules available, which can be grouped by
++their generation: WiLink6, WiLink7 and WiLink8.  WiLink4 is not
++currently supported with device tree.
++
++Currently, only the WLAN portion of the modules is supported with
++device tree.
++
++Required properties:
++--------------------
++
++- compatible: should be "ti,wilink6", "ti,wilink7" or "ti,wilink8"
++- interrupt-parent: the interrupt controller
++- interrupts: out-of-band WLAN interrupt
++	See the interrupt controller's bindings documentation for
++	detailed definition.
++
++Optional properties:
++--------------------
++
++- clocks: list of clocks needed by the chip as follows:
++
++  refclock: the internal WLAN reference clock frequency (required for
++	WiLink6 and WiLink7; not used for WiLink8).
++
++  tcxoclock: the internal WLAN TCXO clock frequency (required for
++	WiLink7 not used for WiLink6 and WiLink8).
++
++  The clocks must be defined and named accordingly.  For example:
++
++  clocks = <&refclock>
++  clock-names = "refclock";
++
++  refclock: refclock {
++		     compatible = "ti,wilink-clock";
++		     #clock-cells = <0>;
++		     clock-frequency = <38400000>;
++	};
++
++  Some modules that contain the WiLink chip provide clocks in the
++  module itself.  In this case, we define a "ti,wilink-clock" as shown
++  above.  But any other clock could in theory be used, so the proper
++  clock definition should be used.
++
++
++Example:
++--------
++
++Example definition that can be used in OMAP4 Panda:
++
++wlan {
++	compatible = "ti,wilink6";
++	interrupt-parent = <&gpio2>;
++	interrupts = <21 IRQ_TYPE_LEVEL_HIGH>;	/* gpio line 53 */
++	clocks = <&refclock>;
++	clock-names = "refclock";
++
++	refclock: refclock {
++		compatible = "ti,wilink-clock";
++		#clock-cells = <0>;
++		clock-frequency = <38400000>;
++	};
++};
+diff -Nuar git.old/drivers/net/wireless/ti/wl12xx/main.c git/drivers/net/wireless/ti/wl12xx/main.c
+--- git.old/drivers/net/wireless/ti/wl12xx/main.c	2015-03-04 16:35:05.646185998 +0100
++++ git/drivers/net/wireless/ti/wl12xx/main.c	2015-03-04 17:07:29.614167458 +0100
+@@ -927,6 +927,11 @@
+ 	u16 sys_clk_cfg;
+ 	int ret;
+ 
++	if ((priv->ref_clock < 0) || (priv->tcxo_clock < 0)) {
++		wl1271_error("Missing fref and/or tcxo clock settings\n");
++		return -EINVAL;
++	}
++
+ 	/* For XTAL-only modes, FREF will be used after switching from TCXO */
+ 	if (priv->ref_clock == WL12XX_REFCLOCK_26_XTAL ||
+ 	    priv->ref_clock == WL12XX_REFCLOCK_38_XTAL) {
+@@ -976,6 +981,11 @@
+ 	u32 clk;
+ 	int ret;
+ 
++	if (priv->ref_clock < 0) {
++		wl1271_error("Missing fref clock settings\n");
++		return -EINVAL;
++	}
++
+ 	if (WL127X_PG_GET_MAJOR(wl->hw_pg_ver) < 3)
+ 		wl->quirks |= WLCORE_QUIRK_END_OF_TRANSACTION;
+ 
+@@ -1701,6 +1711,43 @@
+ 		},
+ };
+ 
++static const struct wl12xx_clock wl12xx_refclock_table[] = {
++	{ 19200000,	false,	WL12XX_REFCLOCK_19	},
++	{ 26000000,	false,	WL12XX_REFCLOCK_26	},
++	{ 26000000,	true,	WL12XX_REFCLOCK_26_XTAL	},
++	{ 38400000,	false,	WL12XX_REFCLOCK_38	},
++	{ 38400000,	true,	WL12XX_REFCLOCK_38_XTAL	},
++	{ 52000000,	false,	WL12XX_REFCLOCK_52	},
++	{ 0,		false,	0 }
++};
++
++static const struct wl12xx_clock wl12xx_tcxoclock_table[] = {
++	{ 16368000,	true,	WL12XX_TCXOCLOCK_16_368	},
++	{ 16800000,	true,	WL12XX_TCXOCLOCK_16_8	},
++	{ 19200000,	true,	WL12XX_TCXOCLOCK_19_2	},
++	{ 26000000,	true,	WL12XX_TCXOCLOCK_26	},
++	{ 32736000,	true,	WL12XX_TCXOCLOCK_32_736	},
++	{ 33600000,	true,	WL12XX_TCXOCLOCK_33_6	},
++	{ 38400000,	true,	WL12XX_TCXOCLOCK_38_4	},
++	{ 52000000,	true,	WL12XX_TCXOCLOCK_52	},
++	{ 0,		false,	0 }
++};
++
++static int wl12xx_get_clock_idx(const struct wl12xx_clock *table,
++				u32 freq, bool xtal)
++{
++	int i = 0;
++
++	while(table[i].freq != 0) {
++		if ((table[i].freq == freq) &&
++		    (table[i].xtal == xtal))
++			return table[i].hw_idx;
++		i++;
++	};
++
++	return -EINVAL;
++}
++
+ static int wl12xx_setup(struct wl1271 *wl)
+ {
+ 	struct wl12xx_priv *priv = wl->priv;
+@@ -1721,8 +1768,19 @@
+ 	wlcore_set_ht_cap(wl, IEEE80211_BAND_5GHZ, &wl12xx_ht_cap);
+ 	wl12xx_conf_init(wl);
+ 
+-	if (!fref_param) {
+-		priv->ref_clock = pdata->board_ref_clock;
++	if (!fref_param && (pdata->ref_clock_freq > 0)) {
++		priv->ref_clock = wl12xx_get_clock_idx(wl12xx_refclock_table,
++						       pdata->ref_clock_freq,
++						       pdata->ref_clock_xtal);
++		if (priv->ref_clock < 0) {
++			wl1271_error("Invalid ref_clock frequency (%d Hz, %s)",
++				pdata->ref_clock_freq,
++				pdata->ref_clock_xtal ? "XTAL" : "not XTAL");
++
++			return priv->ref_clock;
++		}
++	} else if (!fref_param) {
++		priv->ref_clock = -EINVAL;
+ 	} else {
+ 		if (!strcmp(fref_param, "19.2"))
+ 			priv->ref_clock = WL12XX_REFCLOCK_19;
+@@ -1740,9 +1798,19 @@
+ 			wl1271_error("Invalid fref parameter %s", fref_param);
+ 	}
+ 
+-	if (!tcxo_param) {
+-		priv->tcxo_clock = pdata->board_tcxo_clock;
+-	} else {
++	if (!fref_param && (pdata->tcxo_clock_freq > 0)) {
++		priv->tcxo_clock = wl12xx_get_clock_idx(wl12xx_tcxoclock_table,
++							pdata->tcxo_clock_freq,
++							true);
++		if (priv->tcxo_clock < 0) {
++			wl1271_error("Invalid tcxo_clock frequency (%d Hz)",
++				pdata->tcxo_clock_freq);
++
++			return priv->tcxo_clock;
++		}
++	} else if (!fref_param) {
++		priv->tcxo_clock = -EINVAL;
++	}else {
+ 		if (!strcmp(tcxo_param, "19.2"))
+ 			priv->tcxo_clock = WL12XX_TCXOCLOCK_19_2;
+ 		else if (!strcmp(tcxo_param, "26"))
+diff -Nuar git.old/drivers/net/wireless/ti/wl12xx/main.c.orig git/drivers/net/wireless/ti/wl12xx/main.c.orig
+--- git.old/drivers/net/wireless/ti/wl12xx/main.c.orig	1970-01-01 01:00:00.000000000 +0100
++++ git/drivers/net/wireless/ti/wl12xx/main.c.orig	2015-03-04 16:35:05.646185998 +0100
+@@ -0,0 +1,1850 @@
++/*
++ * This file is part of wl1271
++ *
++ * Copyright (C) 2008-2010 Nokia Corporation
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++ * 02110-1301 USA
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/platform_device.h>
++
++#include <linux/err.h>
++
++#include <linux/wl12xx.h>
++
++#include "../wlcore/wlcore.h"
++#include "../wlcore/debug.h"
++#include "../wlcore/io.h"
++#include "../wlcore/acx.h"
++#include "../wlcore/tx.h"
++#include "../wlcore/rx.h"
++#include "../wlcore/boot.h"
++
++#include "wl12xx.h"
++#include "reg.h"
++#include "cmd.h"
++#include "acx.h"
++#include "scan.h"
++#include "event.h"
++#include "debugfs.h"
++
++static char *fref_param;
++static char *tcxo_param;
++
++static struct wlcore_conf wl12xx_conf = {
++	.sg = {
++		.params = {
++			[CONF_SG_ACL_BT_MASTER_MIN_BR] = 10,
++			[CONF_SG_ACL_BT_MASTER_MAX_BR] = 180,
++			[CONF_SG_ACL_BT_SLAVE_MIN_BR] = 10,
++			[CONF_SG_ACL_BT_SLAVE_MAX_BR] = 180,
++			[CONF_SG_ACL_BT_MASTER_MIN_EDR] = 10,
++			[CONF_SG_ACL_BT_MASTER_MAX_EDR] = 80,
++			[CONF_SG_ACL_BT_SLAVE_MIN_EDR] = 10,
++			[CONF_SG_ACL_BT_SLAVE_MAX_EDR] = 80,
++			[CONF_SG_ACL_WLAN_PS_MASTER_BR] = 8,
++			[CONF_SG_ACL_WLAN_PS_SLAVE_BR] = 8,
++			[CONF_SG_ACL_WLAN_PS_MASTER_EDR] = 20,
++			[CONF_SG_ACL_WLAN_PS_SLAVE_EDR] = 20,
++			[CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_BR] = 20,
++			[CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_BR] = 35,
++			[CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_BR] = 16,
++			[CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_BR] = 35,
++			[CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_EDR] = 32,
++			[CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_EDR] = 50,
++			[CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_EDR] = 28,
++			[CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_EDR] = 50,
++			[CONF_SG_ACL_ACTIVE_SCAN_WLAN_BR] = 10,
++			[CONF_SG_ACL_ACTIVE_SCAN_WLAN_EDR] = 20,
++			[CONF_SG_ACL_PASSIVE_SCAN_BT_BR] = 75,
++			[CONF_SG_ACL_PASSIVE_SCAN_WLAN_BR] = 15,
++			[CONF_SG_ACL_PASSIVE_SCAN_BT_EDR] = 27,
++			[CONF_SG_ACL_PASSIVE_SCAN_WLAN_EDR] = 17,
++			/* active scan params */
++			[CONF_SG_AUTO_SCAN_PROBE_REQ] = 170,
++			[CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50,
++			[CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100,
++			/* passive scan params */
++			[CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_BR] = 800,
++			[CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_EDR] = 200,
++			[CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3] = 200,
++			/* passive scan in dual antenna params */
++			[CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN] = 0,
++			[CONF_SG_BCN_HV3_COLLISION_THRESH_IN_PASSIVE_SCAN] = 0,
++			[CONF_SG_TX_RX_PROTECTION_BWIDTH_IN_PASSIVE_SCAN] = 0,
++			/* general params */
++			[CONF_SG_STA_FORCE_PS_IN_BT_SCO] = 1,
++			[CONF_SG_ANTENNA_CONFIGURATION] = 0,
++			[CONF_SG_BEACON_MISS_PERCENT] = 60,
++			[CONF_SG_DHCP_TIME] = 5000,
++			[CONF_SG_RXT] = 1200,
++			[CONF_SG_TXT] = 1000,
++			[CONF_SG_ADAPTIVE_RXT_TXT] = 1,
++			[CONF_SG_GENERAL_USAGE_BIT_MAP] = 3,
++			[CONF_SG_HV3_MAX_SERVED] = 6,
++			[CONF_SG_PS_POLL_TIMEOUT] = 10,
++			[CONF_SG_UPSD_TIMEOUT] = 10,
++			[CONF_SG_CONSECUTIVE_CTS_THRESHOLD] = 2,
++			[CONF_SG_STA_RX_WINDOW_AFTER_DTIM] = 5,
++			[CONF_SG_STA_CONNECTION_PROTECTION_TIME] = 30,
++			/* AP params */
++			[CONF_AP_BEACON_MISS_TX] = 3,
++			[CONF_AP_RX_WINDOW_AFTER_BEACON] = 10,
++			[CONF_AP_BEACON_WINDOW_INTERVAL] = 2,
++			[CONF_AP_CONNECTION_PROTECTION_TIME] = 0,
++			[CONF_AP_BT_ACL_VAL_BT_SERVE_TIME] = 25,
++			[CONF_AP_BT_ACL_VAL_WL_SERVE_TIME] = 25,
++			/* CTS Diluting params */
++			[CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH] = 0,
++			[CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER] = 0,
++		},
++		.state = CONF_SG_PROTECTIVE,
++	},
++	.rx = {
++		.rx_msdu_life_time           = 512000,
++		.packet_detection_threshold  = 0,
++		.ps_poll_timeout             = 15,
++		.upsd_timeout                = 15,
++		.rts_threshold               = IEEE80211_MAX_RTS_THRESHOLD,
++		.rx_cca_threshold            = 0,
++		.irq_blk_threshold           = 0xFFFF,
++		.irq_pkt_threshold           = 0,
++		.irq_timeout                 = 600,
++		.queue_type                  = CONF_RX_QUEUE_TYPE_LOW_PRIORITY,
++	},
++	.tx = {
++		.tx_energy_detection         = 0,
++		.sta_rc_conf                 = {
++			.enabled_rates       = 0,
++			.short_retry_limit   = 10,
++			.long_retry_limit    = 10,
++			.aflags              = 0,
++		},
++		.ac_conf_count               = 4,
++		.ac_conf                     = {
++			[CONF_TX_AC_BE] = {
++				.ac          = CONF_TX_AC_BE,
++				.cw_min      = 15,
++				.cw_max      = 63,
++				.aifsn       = 3,
++				.tx_op_limit = 0,
++			},
++			[CONF_TX_AC_BK] = {
++				.ac          = CONF_TX_AC_BK,
++				.cw_min      = 15,
++				.cw_max      = 63,
++				.aifsn       = 7,
++				.tx_op_limit = 0,
++			},
++			[CONF_TX_AC_VI] = {
++				.ac          = CONF_TX_AC_VI,
++				.cw_min      = 15,
++				.cw_max      = 63,
++				.aifsn       = CONF_TX_AIFS_PIFS,
++				.tx_op_limit = 3008,
++			},
++			[CONF_TX_AC_VO] = {
++				.ac          = CONF_TX_AC_VO,
++				.cw_min      = 15,
++				.cw_max      = 63,
++				.aifsn       = CONF_TX_AIFS_PIFS,
++				.tx_op_limit = 1504,
++			},
++		},
++		.max_tx_retries = 100,
++		.ap_aging_period = 300,
++		.tid_conf_count = 4,
++		.tid_conf = {
++			[CONF_TX_AC_BE] = {
++				.queue_id    = CONF_TX_AC_BE,
++				.channel_type = CONF_CHANNEL_TYPE_EDCF,
++				.tsid        = CONF_TX_AC_BE,
++				.ps_scheme   = CONF_PS_SCHEME_LEGACY,
++				.ack_policy  = CONF_ACK_POLICY_LEGACY,
++				.apsd_conf   = {0, 0},
++			},
++			[CONF_TX_AC_BK] = {
++				.queue_id    = CONF_TX_AC_BK,
++				.channel_type = CONF_CHANNEL_TYPE_EDCF,
++				.tsid        = CONF_TX_AC_BK,
++				.ps_scheme   = CONF_PS_SCHEME_LEGACY,
++				.ack_policy  = CONF_ACK_POLICY_LEGACY,
++				.apsd_conf   = {0, 0},
++			},
++			[CONF_TX_AC_VI] = {
++				.queue_id    = CONF_TX_AC_VI,
++				.channel_type = CONF_CHANNEL_TYPE_EDCF,
++				.tsid        = CONF_TX_AC_VI,
++				.ps_scheme   = CONF_PS_SCHEME_LEGACY,
++				.ack_policy  = CONF_ACK_POLICY_LEGACY,
++				.apsd_conf   = {0, 0},
++			},
++			[CONF_TX_AC_VO] = {
++				.queue_id    = CONF_TX_AC_VO,
++				.channel_type = CONF_CHANNEL_TYPE_EDCF,
++				.tsid        = CONF_TX_AC_VO,
++				.ps_scheme   = CONF_PS_SCHEME_LEGACY,
++				.ack_policy  = CONF_ACK_POLICY_LEGACY,
++				.apsd_conf   = {0, 0},
++			},
++		},
++		.frag_threshold              = IEEE80211_MAX_FRAG_THRESHOLD,
++		.tx_compl_timeout            = 700,
++		.tx_compl_threshold          = 4,
++		.basic_rate                  = CONF_HW_BIT_RATE_1MBPS,
++		.basic_rate_5                = CONF_HW_BIT_RATE_6MBPS,
++		.tmpl_short_retry_limit      = 10,
++		.tmpl_long_retry_limit       = 10,
++		.tx_watchdog_timeout         = 5000,
++		.slow_link_thold             = 3,
++		.fast_link_thold             = 10,
++	},
++	.conn = {
++		.wake_up_event               = CONF_WAKE_UP_EVENT_DTIM,
++		.listen_interval             = 1,
++		.suspend_wake_up_event       = CONF_WAKE_UP_EVENT_N_DTIM,
++		.suspend_listen_interval     = 3,
++		.bcn_filt_mode               = CONF_BCN_FILT_MODE_ENABLED,
++		.bcn_filt_ie_count           = 3,
++		.bcn_filt_ie = {
++			[0] = {
++				.ie          = WLAN_EID_CHANNEL_SWITCH,
++				.rule        = CONF_BCN_RULE_PASS_ON_APPEARANCE,
++			},
++			[1] = {
++				.ie          = WLAN_EID_HT_OPERATION,
++				.rule        = CONF_BCN_RULE_PASS_ON_CHANGE,
++			},
++			[2] = {
++				.ie	     = WLAN_EID_ERP_INFO,
++				.rule	     = CONF_BCN_RULE_PASS_ON_CHANGE,
++			},
++		},
++		.synch_fail_thold            = 12,
++		.bss_lose_timeout            = 400,
++		.beacon_rx_timeout           = 10000,
++		.broadcast_timeout           = 20000,
++		.rx_broadcast_in_ps          = 1,
++		.ps_poll_threshold           = 10,
++		.bet_enable                  = CONF_BET_MODE_ENABLE,
++		.bet_max_consecutive         = 50,
++		.psm_entry_retries           = 8,
++		.psm_exit_retries            = 16,
++		.psm_entry_nullfunc_retries  = 3,
++		.dynamic_ps_timeout          = 1500,
++		.forced_ps                   = false,
++		.keep_alive_interval         = 55000,
++		.max_listen_interval         = 20,
++		.sta_sleep_auth              = WL1271_PSM_ILLEGAL,
++	},
++	.itrim = {
++		.enable = false,
++		.timeout = 50000,
++	},
++	.pm_config = {
++		.host_clk_settling_time = 5000,
++		.host_fast_wakeup_support = CONF_FAST_WAKEUP_DISABLE,
++	},
++	.roam_trigger = {
++		.trigger_pacing               = 1,
++		.avg_weight_rssi_beacon       = 20,
++		.avg_weight_rssi_data         = 10,
++		.avg_weight_snr_beacon        = 20,
++		.avg_weight_snr_data          = 10,
++	},
++	.scan = {
++		.min_dwell_time_active        = 7500,
++		.max_dwell_time_active        = 30000,
++		.min_dwell_time_active_long   = 25000,
++		.max_dwell_time_active_long   = 50000,
++		.dwell_time_passive           = 100000,
++		.dwell_time_dfs               = 150000,
++		.num_probe_reqs               = 2,
++		.split_scan_timeout           = 50000,
++	},
++	.sched_scan = {
++		/*
++		 * Values are in TU/1000 but since sched scan FW command
++		 * params are in TUs rounding up may occur.
++		 */
++		.base_dwell_time		= 7500,
++		.max_dwell_time_delta		= 22500,
++		/* based on 250bits per probe @1Mbps */
++		.dwell_time_delta_per_probe	= 2000,
++		/* based on 250bits per probe @6Mbps (plus a bit more) */
++		.dwell_time_delta_per_probe_5	= 350,
++		.dwell_time_passive		= 100000,
++		.dwell_time_dfs			= 150000,
++		.num_probe_reqs			= 2,
++		.rssi_threshold			= -90,
++		.snr_threshold			= 0,
++	},
++	.ht = {
++		.rx_ba_win_size = 8,
++		.tx_ba_win_size = 64,
++		.inactivity_timeout = 10000,
++		.tx_ba_tid_bitmap = CONF_TX_BA_ENABLED_TID_BITMAP,
++	},
++	/*
++	 * Memory config for wl127x chips is given in the
++	 * wl12xx_default_priv_conf struct. The below configuration is
++	 * for wl128x chips.
++	 */
++	.mem = {
++		.num_stations                 = 1,
++		.ssid_profiles                = 1,
++		.rx_block_num                 = 40,
++		.tx_min_block_num             = 40,
++		.dynamic_memory               = 1,
++		.min_req_tx_blocks            = 45,
++		.min_req_rx_blocks            = 22,
++		.tx_min                       = 27,
++	},
++	.fm_coex = {
++		.enable                       = true,
++		.swallow_period               = 5,
++		.n_divider_fref_set_1         = 0xff,       /* default */
++		.n_divider_fref_set_2         = 12,
++		.m_divider_fref_set_1         = 0xffff,
++		.m_divider_fref_set_2         = 148,	    /* default */
++		.coex_pll_stabilization_time  = 0xffffffff, /* default */
++		.ldo_stabilization_time       = 0xffff,     /* default */
++		.fm_disturbed_band_margin     = 0xff,       /* default */
++		.swallow_clk_diff             = 0xff,       /* default */
++	},
++	.rx_streaming = {
++		.duration                      = 150,
++		.queues                        = 0x1,
++		.interval                      = 20,
++		.always                        = 0,
++	},
++	.fwlog = {
++		.mode                         = WL12XX_FWLOG_ON_DEMAND,
++		.mem_blocks                   = 2,
++		.severity                     = 0,
++		.timestamp                    = WL12XX_FWLOG_TIMESTAMP_DISABLED,
++		.output                       = WL12XX_FWLOG_OUTPUT_HOST,
++		.threshold                    = 0,
++	},
++	.rate = {
++		.rate_retry_score = 32000,
++		.per_add = 8192,
++		.per_th1 = 2048,
++		.per_th2 = 4096,
++		.max_per = 8100,
++		.inverse_curiosity_factor = 5,
++		.tx_fail_low_th = 4,
++		.tx_fail_high_th = 10,
++		.per_alpha_shift = 4,
++		.per_add_shift = 13,
++		.per_beta1_shift = 10,
++		.per_beta2_shift = 8,
++		.rate_check_up = 2,
++		.rate_check_down = 12,
++		.rate_retry_policy = {
++			0x00, 0x00, 0x00, 0x00, 0x00,
++			0x00, 0x00, 0x00, 0x00, 0x00,
++			0x00, 0x00, 0x00,
++		},
++	},
++	.hangover = {
++		.recover_time               = 0,
++		.hangover_period            = 20,
++		.dynamic_mode               = 1,
++		.early_termination_mode     = 1,
++		.max_period                 = 20,
++		.min_period                 = 1,
++		.increase_delta             = 1,
++		.decrease_delta             = 2,
++		.quiet_time                 = 4,
++		.increase_time              = 1,
++		.window_size                = 16,
++	},
++	.recovery = {
++		.bug_on_recovery	    = 0,
++		.no_recovery		    = 0,
++	},
++};
++
++static struct wl12xx_priv_conf wl12xx_default_priv_conf = {
++	.rf = {
++		.tx_per_channel_power_compensation_2 = {
++			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++		},
++		.tx_per_channel_power_compensation_5 = {
++			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++		},
++	},
++	.mem_wl127x = {
++		.num_stations                 = 1,
++		.ssid_profiles                = 1,
++		.rx_block_num                 = 70,
++		.tx_min_block_num             = 40,
++		.dynamic_memory               = 1,
++		.min_req_tx_blocks            = 100,
++		.min_req_rx_blocks            = 22,
++		.tx_min                       = 27,
++	},
++
++};
++
++#define WL12XX_TX_HW_BLOCK_SPARE_DEFAULT        1
++#define WL12XX_TX_HW_BLOCK_GEM_SPARE            2
++#define WL12XX_TX_HW_BLOCK_SIZE                 252
++
++static const u8 wl12xx_rate_to_idx_2ghz[] = {
++	/* MCS rates are used only with 11n */
++	7,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS7_SGI */
++	7,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS7 */
++	6,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS6 */
++	5,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS5 */
++	4,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS4 */
++	3,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS3 */
++	2,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS2 */
++	1,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS1 */
++	0,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS0 */
++
++	11,                            /* WL12XX_CONF_HW_RXTX_RATE_54   */
++	10,                            /* WL12XX_CONF_HW_RXTX_RATE_48   */
++	9,                             /* WL12XX_CONF_HW_RXTX_RATE_36   */
++	8,                             /* WL12XX_CONF_HW_RXTX_RATE_24   */
++
++	/* TI-specific rate */
++	CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL12XX_CONF_HW_RXTX_RATE_22   */
++
++	7,                             /* WL12XX_CONF_HW_RXTX_RATE_18   */
++	6,                             /* WL12XX_CONF_HW_RXTX_RATE_12   */
++	3,                             /* WL12XX_CONF_HW_RXTX_RATE_11   */
++	5,                             /* WL12XX_CONF_HW_RXTX_RATE_9    */
++	4,                             /* WL12XX_CONF_HW_RXTX_RATE_6    */
++	2,                             /* WL12XX_CONF_HW_RXTX_RATE_5_5  */
++	1,                             /* WL12XX_CONF_HW_RXTX_RATE_2    */
++	0                              /* WL12XX_CONF_HW_RXTX_RATE_1    */
++};
++
++static const u8 wl12xx_rate_to_idx_5ghz[] = {
++	/* MCS rates are used only with 11n */
++	7,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS7_SGI */
++	7,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS7 */
++	6,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS6 */
++	5,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS5 */
++	4,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS4 */
++	3,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS3 */
++	2,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS2 */
++	1,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS1 */
++	0,                             /* WL12XX_CONF_HW_RXTX_RATE_MCS0 */
++
++	7,                             /* WL12XX_CONF_HW_RXTX_RATE_54   */
++	6,                             /* WL12XX_CONF_HW_RXTX_RATE_48   */
++	5,                             /* WL12XX_CONF_HW_RXTX_RATE_36   */
++	4,                             /* WL12XX_CONF_HW_RXTX_RATE_24   */
++
++	/* TI-specific rate */
++	CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL12XX_CONF_HW_RXTX_RATE_22   */
++
++	3,                             /* WL12XX_CONF_HW_RXTX_RATE_18   */
++	2,                             /* WL12XX_CONF_HW_RXTX_RATE_12   */
++	CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL12XX_CONF_HW_RXTX_RATE_11   */
++	1,                             /* WL12XX_CONF_HW_RXTX_RATE_9    */
++	0,                             /* WL12XX_CONF_HW_RXTX_RATE_6    */
++	CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL12XX_CONF_HW_RXTX_RATE_5_5  */
++	CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL12XX_CONF_HW_RXTX_RATE_2    */
++	CONF_HW_RXTX_RATE_UNSUPPORTED  /* WL12XX_CONF_HW_RXTX_RATE_1    */
++};
++
++static const u8 *wl12xx_band_rate_to_idx[] = {
++	[IEEE80211_BAND_2GHZ] = wl12xx_rate_to_idx_2ghz,
++	[IEEE80211_BAND_5GHZ] = wl12xx_rate_to_idx_5ghz
++};
++
++enum wl12xx_hw_rates {
++	WL12XX_CONF_HW_RXTX_RATE_MCS7_SGI = 0,
++	WL12XX_CONF_HW_RXTX_RATE_MCS7,
++	WL12XX_CONF_HW_RXTX_RATE_MCS6,
++	WL12XX_CONF_HW_RXTX_RATE_MCS5,
++	WL12XX_CONF_HW_RXTX_RATE_MCS4,
++	WL12XX_CONF_HW_RXTX_RATE_MCS3,
++	WL12XX_CONF_HW_RXTX_RATE_MCS2,
++	WL12XX_CONF_HW_RXTX_RATE_MCS1,
++	WL12XX_CONF_HW_RXTX_RATE_MCS0,
++	WL12XX_CONF_HW_RXTX_RATE_54,
++	WL12XX_CONF_HW_RXTX_RATE_48,
++	WL12XX_CONF_HW_RXTX_RATE_36,
++	WL12XX_CONF_HW_RXTX_RATE_24,
++	WL12XX_CONF_HW_RXTX_RATE_22,
++	WL12XX_CONF_HW_RXTX_RATE_18,
++	WL12XX_CONF_HW_RXTX_RATE_12,
++	WL12XX_CONF_HW_RXTX_RATE_11,
++	WL12XX_CONF_HW_RXTX_RATE_9,
++	WL12XX_CONF_HW_RXTX_RATE_6,
++	WL12XX_CONF_HW_RXTX_RATE_5_5,
++	WL12XX_CONF_HW_RXTX_RATE_2,
++	WL12XX_CONF_HW_RXTX_RATE_1,
++	WL12XX_CONF_HW_RXTX_RATE_MAX,
++};
++
++static struct wlcore_partition_set wl12xx_ptable[PART_TABLE_LEN] = {
++	[PART_DOWN] = {
++		.mem = {
++			.start = 0x00000000,
++			.size  = 0x000177c0
++		},
++		.reg = {
++			.start = REGISTERS_BASE,
++			.size  = 0x00008800
++		},
++		.mem2 = {
++			.start = 0x00000000,
++			.size  = 0x00000000
++		},
++		.mem3 = {
++			.start = 0x00000000,
++			.size  = 0x00000000
++		},
++	},
++
++	[PART_BOOT] = { /* in wl12xx we can use a mix of work and down
++			 * partition here */
++		.mem = {
++			.start = 0x00040000,
++			.size  = 0x00014fc0
++		},
++		.reg = {
++			.start = REGISTERS_BASE,
++			.size  = 0x00008800
++		},
++		.mem2 = {
++			.start = 0x00000000,
++			.size  = 0x00000000
++		},
++		.mem3 = {
++			.start = 0x00000000,
++			.size  = 0x00000000
++		},
++	},
++
++	[PART_WORK] = {
++		.mem = {
++			.start = 0x00040000,
++			.size  = 0x00014fc0
++		},
++		.reg = {
++			.start = REGISTERS_BASE,
++			.size  = 0x0000a000
++		},
++		.mem2 = {
++			.start = 0x003004f8,
++			.size  = 0x00000004
++		},
++		.mem3 = {
++			.start = 0x00040404,
++			.size  = 0x00000000
++		},
++	},
++
++	[PART_DRPW] = {
++		.mem = {
++			.start = 0x00040000,
++			.size  = 0x00014fc0
++		},
++		.reg = {
++			.start = DRPW_BASE,
++			.size  = 0x00006000
++		},
++		.mem2 = {
++			.start = 0x00000000,
++			.size  = 0x00000000
++		},
++		.mem3 = {
++			.start = 0x00000000,
++			.size  = 0x00000000
++		}
++	}
++};
++
++static const int wl12xx_rtable[REG_TABLE_LEN] = {
++	[REG_ECPU_CONTROL]		= WL12XX_REG_ECPU_CONTROL,
++	[REG_INTERRUPT_NO_CLEAR]	= WL12XX_REG_INTERRUPT_NO_CLEAR,
++	[REG_INTERRUPT_ACK]		= WL12XX_REG_INTERRUPT_ACK,
++	[REG_COMMAND_MAILBOX_PTR]	= WL12XX_REG_COMMAND_MAILBOX_PTR,
++	[REG_EVENT_MAILBOX_PTR]		= WL12XX_REG_EVENT_MAILBOX_PTR,
++	[REG_INTERRUPT_TRIG]		= WL12XX_REG_INTERRUPT_TRIG,
++	[REG_INTERRUPT_MASK]		= WL12XX_REG_INTERRUPT_MASK,
++	[REG_PC_ON_RECOVERY]		= WL12XX_SCR_PAD4,
++	[REG_CHIP_ID_B]			= WL12XX_CHIP_ID_B,
++	[REG_CMD_MBOX_ADDRESS]		= WL12XX_CMD_MBOX_ADDRESS,
++
++	/* data access memory addresses, used with partition translation */
++	[REG_SLV_MEM_DATA]		= WL1271_SLV_MEM_DATA,
++	[REG_SLV_REG_DATA]		= WL1271_SLV_REG_DATA,
++
++	/* raw data access memory addresses */
++	[REG_RAW_FW_STATUS_ADDR]	= FW_STATUS_ADDR,
++};
++
++/* TODO: maybe move to a new header file? */
++#define WL127X_FW_NAME_MULTI	"ti-connectivity/wl127x-fw-5-mr.bin"
++#define WL127X_FW_NAME_SINGLE	"ti-connectivity/wl127x-fw-5-sr.bin"
++#define WL127X_PLT_FW_NAME	"ti-connectivity/wl127x-fw-5-plt.bin"
++
++#define WL128X_FW_NAME_MULTI	"ti-connectivity/wl128x-fw-5-mr.bin"
++#define WL128X_FW_NAME_SINGLE	"ti-connectivity/wl128x-fw-5-sr.bin"
++#define WL128X_PLT_FW_NAME	"ti-connectivity/wl128x-fw-5-plt.bin"
++
++static int wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len)
++{
++	int ret;
++
++	if (wl->chip.id != CHIP_ID_128X_PG20) {
++		struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map;
++		struct wl12xx_priv *priv = wl->priv;
++
++		/*
++		 * Choose the block we want to read
++		 * For aggregated packets, only the first memory block
++		 * should be retrieved. The FW takes care of the rest.
++		 */
++		u32 mem_block = rx_desc & RX_MEM_BLOCK_MASK;
++
++		priv->rx_mem_addr->addr = (mem_block << 8) +
++			le32_to_cpu(wl_mem_map->packet_memory_pool_start);
++
++		priv->rx_mem_addr->addr_extra = priv->rx_mem_addr->addr + 4;
++
++		ret = wlcore_write(wl, WL1271_SLV_REG_DATA, priv->rx_mem_addr,
++				   sizeof(*priv->rx_mem_addr), false);
++		if (ret < 0)
++			return ret;
++	}
++
++	return 0;
++}
++
++static int wl12xx_identify_chip(struct wl1271 *wl)
++{
++	int ret = 0;
++
++	switch (wl->chip.id) {
++	case CHIP_ID_127X_PG10:
++		wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete",
++			       wl->chip.id);
++
++		wl->quirks |= WLCORE_QUIRK_LEGACY_NVS |
++			      WLCORE_QUIRK_DUAL_PROBE_TMPL |
++			      WLCORE_QUIRK_TKIP_HEADER_SPACE |
++			      WLCORE_QUIRK_START_STA_FAILS |
++			      WLCORE_QUIRK_AP_ZERO_SESSION_ID;
++		wl->sr_fw_name = WL127X_FW_NAME_SINGLE;
++		wl->mr_fw_name = WL127X_FW_NAME_MULTI;
++		memcpy(&wl->conf.mem, &wl12xx_default_priv_conf.mem_wl127x,
++		       sizeof(wl->conf.mem));
++
++		/* read data preparation is only needed by wl127x */
++		wl->ops->prepare_read = wl127x_prepare_read;
++
++		wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER,
++			      WL127X_IFTYPE_SR_VER,  WL127X_MAJOR_SR_VER,
++			      WL127X_SUBTYPE_SR_VER, WL127X_MINOR_SR_VER,
++			      WL127X_IFTYPE_MR_VER,  WL127X_MAJOR_MR_VER,
++			      WL127X_SUBTYPE_MR_VER, WL127X_MINOR_MR_VER);
++		break;
++
++	case CHIP_ID_127X_PG20:
++		wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
++			     wl->chip.id);
++
++		wl->quirks |= WLCORE_QUIRK_LEGACY_NVS |
++			      WLCORE_QUIRK_DUAL_PROBE_TMPL |
++			      WLCORE_QUIRK_TKIP_HEADER_SPACE |
++			      WLCORE_QUIRK_START_STA_FAILS |
++			      WLCORE_QUIRK_AP_ZERO_SESSION_ID;
++		wl->plt_fw_name = WL127X_PLT_FW_NAME;
++		wl->sr_fw_name = WL127X_FW_NAME_SINGLE;
++		wl->mr_fw_name = WL127X_FW_NAME_MULTI;
++		memcpy(&wl->conf.mem, &wl12xx_default_priv_conf.mem_wl127x,
++		       sizeof(wl->conf.mem));
++
++		/* read data preparation is only needed by wl127x */
++		wl->ops->prepare_read = wl127x_prepare_read;
++
++		wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER,
++			      WL127X_IFTYPE_SR_VER,  WL127X_MAJOR_SR_VER,
++			      WL127X_SUBTYPE_SR_VER, WL127X_MINOR_SR_VER,
++			      WL127X_IFTYPE_MR_VER,  WL127X_MAJOR_MR_VER,
++			      WL127X_SUBTYPE_MR_VER, WL127X_MINOR_MR_VER);
++		break;
++
++	case CHIP_ID_128X_PG20:
++		wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1283 PG20)",
++			     wl->chip.id);
++		wl->plt_fw_name = WL128X_PLT_FW_NAME;
++		wl->sr_fw_name = WL128X_FW_NAME_SINGLE;
++		wl->mr_fw_name = WL128X_FW_NAME_MULTI;
++
++		/* wl128x requires TX blocksize alignment */
++		wl->quirks |= WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN |
++			      WLCORE_QUIRK_DUAL_PROBE_TMPL |
++			      WLCORE_QUIRK_TKIP_HEADER_SPACE |
++			      WLCORE_QUIRK_START_STA_FAILS |
++			      WLCORE_QUIRK_AP_ZERO_SESSION_ID;
++
++		wlcore_set_min_fw_ver(wl, WL128X_CHIP_VER,
++			      WL128X_IFTYPE_SR_VER,  WL128X_MAJOR_SR_VER,
++			      WL128X_SUBTYPE_SR_VER, WL128X_MINOR_SR_VER,
++			      WL128X_IFTYPE_MR_VER,  WL128X_MAJOR_MR_VER,
++			      WL128X_SUBTYPE_MR_VER, WL128X_MINOR_MR_VER);
++		break;
++	case CHIP_ID_128X_PG10:
++	default:
++		wl1271_warning("unsupported chip id: 0x%x", wl->chip.id);
++		ret = -ENODEV;
++		goto out;
++	}
++
++	/* common settings */
++	wl->scan_templ_id_2_4 = CMD_TEMPL_APP_PROBE_REQ_2_4_LEGACY;
++	wl->scan_templ_id_5 = CMD_TEMPL_APP_PROBE_REQ_5_LEGACY;
++	wl->sched_scan_templ_id_2_4 = CMD_TEMPL_CFG_PROBE_REQ_2_4;
++	wl->sched_scan_templ_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5;
++	wl->max_channels_5 = WL12XX_MAX_CHANNELS_5GHZ;
++	wl->ba_rx_session_count_max = WL12XX_RX_BA_MAX_SESSIONS;
++out:
++	return ret;
++}
++
++static int __must_check wl12xx_top_reg_write(struct wl1271 *wl, int addr,
++					     u16 val)
++{
++	int ret;
++
++	/* write address >> 1 + 0x30000 to OCP_POR_CTR */
++	addr = (addr >> 1) + 0x30000;
++	ret = wlcore_write32(wl, WL12XX_OCP_POR_CTR, addr);
++	if (ret < 0)
++		goto out;
++
++	/* write value to OCP_POR_WDATA */
++	ret = wlcore_write32(wl, WL12XX_OCP_DATA_WRITE, val);
++	if (ret < 0)
++		goto out;
++
++	/* write 1 to OCP_CMD */
++	ret = wlcore_write32(wl, WL12XX_OCP_CMD, OCP_CMD_WRITE);
++	if (ret < 0)
++		goto out;
++
++out:
++	return ret;
++}
++
++static int __must_check wl12xx_top_reg_read(struct wl1271 *wl, int addr,
++					    u16 *out)
++{
++	u32 val;
++	int timeout = OCP_CMD_LOOP;
++	int ret;
++
++	/* write address >> 1 + 0x30000 to OCP_POR_CTR */
++	addr = (addr >> 1) + 0x30000;
++	ret = wlcore_write32(wl, WL12XX_OCP_POR_CTR, addr);
++	if (ret < 0)
++		return ret;
++
++	/* write 2 to OCP_CMD */
++	ret = wlcore_write32(wl, WL12XX_OCP_CMD, OCP_CMD_READ);
++	if (ret < 0)
++		return ret;
++
++	/* poll for data ready */
++	do {
++		ret = wlcore_read32(wl, WL12XX_OCP_DATA_READ, &val);
++		if (ret < 0)
++			return ret;
++	} while (!(val & OCP_READY_MASK) && --timeout);
++
++	if (!timeout) {
++		wl1271_warning("Top register access timed out.");
++		return -ETIMEDOUT;
++	}
++
++	/* check data status and return if OK */
++	if ((val & OCP_STATUS_MASK) != OCP_STATUS_OK) {
++		wl1271_warning("Top register access returned error.");
++		return -EIO;
++	}
++
++	if (out)
++		*out = val & 0xffff;
++
++	return 0;
++}
++
++static int wl128x_switch_tcxo_to_fref(struct wl1271 *wl)
++{
++	u16 spare_reg;
++	int ret;
++
++	/* Mask bits [2] & [8:4] in the sys_clk_cfg register */
++	ret = wl12xx_top_reg_read(wl, WL_SPARE_REG, &spare_reg);
++	if (ret < 0)
++		return ret;
++
++	if (spare_reg == 0xFFFF)
++		return -EFAULT;
++	spare_reg |= (BIT(3) | BIT(5) | BIT(6));
++	ret = wl12xx_top_reg_write(wl, WL_SPARE_REG, spare_reg);
++	if (ret < 0)
++		return ret;
++
++	/* Enable FREF_CLK_REQ & mux MCS and coex PLLs to FREF */
++	ret = wl12xx_top_reg_write(wl, SYS_CLK_CFG_REG,
++				   WL_CLK_REQ_TYPE_PG2 | MCS_PLL_CLK_SEL_FREF);
++	if (ret < 0)
++		return ret;
++
++	/* Delay execution for 15msec, to let the HW settle */
++	mdelay(15);
++
++	return 0;
++}
++
++static bool wl128x_is_tcxo_valid(struct wl1271 *wl)
++{
++	u16 tcxo_detection;
++	int ret;
++
++	ret = wl12xx_top_reg_read(wl, TCXO_CLK_DETECT_REG, &tcxo_detection);
++	if (ret < 0)
++		return false;
++
++	if (tcxo_detection & TCXO_DET_FAILED)
++		return false;
++
++	return true;
++}
++
++static bool wl128x_is_fref_valid(struct wl1271 *wl)
++{
++	u16 fref_detection;
++	int ret;
++
++	ret = wl12xx_top_reg_read(wl, FREF_CLK_DETECT_REG, &fref_detection);
++	if (ret < 0)
++		return false;
++
++	if (fref_detection & FREF_CLK_DETECT_FAIL)
++		return false;
++
++	return true;
++}
++
++static int wl128x_manually_configure_mcs_pll(struct wl1271 *wl)
++{
++	int ret;
++
++	ret = wl12xx_top_reg_write(wl, MCS_PLL_M_REG, MCS_PLL_M_REG_VAL);
++	if (ret < 0)
++		goto out;
++
++	ret = wl12xx_top_reg_write(wl, MCS_PLL_N_REG, MCS_PLL_N_REG_VAL);
++	if (ret < 0)
++		goto out;
++
++	ret = wl12xx_top_reg_write(wl, MCS_PLL_CONFIG_REG,
++				   MCS_PLL_CONFIG_REG_VAL);
++
++out:
++	return ret;
++}
++
++static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk)
++{
++	u16 spare_reg;
++	u16 pll_config;
++	u8 input_freq;
++	struct wl12xx_priv *priv = wl->priv;
++	int ret;
++
++	/* Mask bits [3:1] in the sys_clk_cfg register */
++	ret = wl12xx_top_reg_read(wl, WL_SPARE_REG, &spare_reg);
++	if (ret < 0)
++		return ret;
++
++	if (spare_reg == 0xFFFF)
++		return -EFAULT;
++	spare_reg |= BIT(2);
++	ret = wl12xx_top_reg_write(wl, WL_SPARE_REG, spare_reg);
++	if (ret < 0)
++		return ret;
++
++	/* Handle special cases of the TCXO clock */
++	if (priv->tcxo_clock == WL12XX_TCXOCLOCK_16_8 ||
++	    priv->tcxo_clock == WL12XX_TCXOCLOCK_33_6)
++		return wl128x_manually_configure_mcs_pll(wl);
++
++	/* Set the input frequency according to the selected clock source */
++	input_freq = (clk & 1) + 1;
++
++	ret = wl12xx_top_reg_read(wl, MCS_PLL_CONFIG_REG, &pll_config);
++	if (ret < 0)
++		return ret;
++
++	if (pll_config == 0xFFFF)
++		return -EFAULT;
++	pll_config |= (input_freq << MCS_SEL_IN_FREQ_SHIFT);
++	pll_config |= MCS_PLL_ENABLE_HP;
++	ret = wl12xx_top_reg_write(wl, MCS_PLL_CONFIG_REG, pll_config);
++
++	return ret;
++}
++
++/*
++ * WL128x has two clocks input - TCXO and FREF.
++ * TCXO is the main clock of the device, while FREF is used to sync
++ * between the GPS and the cellular modem.
++ * In cases where TCXO is 32.736MHz or 16.368MHz, the FREF will be used
++ * as the WLAN/BT main clock.
++ */
++static int wl128x_boot_clk(struct wl1271 *wl, int *selected_clock)
++{
++	struct wl12xx_priv *priv = wl->priv;
++	u16 sys_clk_cfg;
++	int ret;
++
++	/* For XTAL-only modes, FREF will be used after switching from TCXO */
++	if (priv->ref_clock == WL12XX_REFCLOCK_26_XTAL ||
++	    priv->ref_clock == WL12XX_REFCLOCK_38_XTAL) {
++		if (!wl128x_switch_tcxo_to_fref(wl))
++			return -EINVAL;
++		goto fref_clk;
++	}
++
++	/* Query the HW, to determine which clock source we should use */
++	ret = wl12xx_top_reg_read(wl, SYS_CLK_CFG_REG, &sys_clk_cfg);
++	if (ret < 0)
++		return ret;
++
++	if (sys_clk_cfg == 0xFFFF)
++		return -EINVAL;
++	if (sys_clk_cfg & PRCM_CM_EN_MUX_WLAN_FREF)
++		goto fref_clk;
++
++	/* If TCXO is either 32.736MHz or 16.368MHz, switch to FREF */
++	if (priv->tcxo_clock == WL12XX_TCXOCLOCK_16_368 ||
++	    priv->tcxo_clock == WL12XX_TCXOCLOCK_32_736) {
++		if (!wl128x_switch_tcxo_to_fref(wl))
++			return -EINVAL;
++		goto fref_clk;
++	}
++
++	/* TCXO clock is selected */
++	if (!wl128x_is_tcxo_valid(wl))
++		return -EINVAL;
++	*selected_clock = priv->tcxo_clock;
++	goto config_mcs_pll;
++
++fref_clk:
++	/* FREF clock is selected */
++	if (!wl128x_is_fref_valid(wl))
++		return -EINVAL;
++	*selected_clock = priv->ref_clock;
++
++config_mcs_pll:
++	return wl128x_configure_mcs_pll(wl, *selected_clock);
++}
++
++static int wl127x_boot_clk(struct wl1271 *wl)
++{
++	struct wl12xx_priv *priv = wl->priv;
++	u32 pause;
++	u32 clk;
++	int ret;
++
++	if (WL127X_PG_GET_MAJOR(wl->hw_pg_ver) < 3)
++		wl->quirks |= WLCORE_QUIRK_END_OF_TRANSACTION;
++
++	if (priv->ref_clock == CONF_REF_CLK_19_2_E ||
++	    priv->ref_clock == CONF_REF_CLK_38_4_E ||
++	    priv->ref_clock == CONF_REF_CLK_38_4_M_XTAL)
++		/* ref clk: 19.2/38.4/38.4-XTAL */
++		clk = 0x3;
++	else if (priv->ref_clock == CONF_REF_CLK_26_E ||
++		 priv->ref_clock == CONF_REF_CLK_26_M_XTAL ||
++		 priv->ref_clock == CONF_REF_CLK_52_E)
++		/* ref clk: 26/52 */
++		clk = 0x5;
++	else
++		return -EINVAL;
++
++	if (priv->ref_clock != CONF_REF_CLK_19_2_E) {
++		u16 val;
++		/* Set clock type (open drain) */
++		ret = wl12xx_top_reg_read(wl, OCP_REG_CLK_TYPE, &val);
++		if (ret < 0)
++			goto out;
++
++		val &= FREF_CLK_TYPE_BITS;
++		ret = wl12xx_top_reg_write(wl, OCP_REG_CLK_TYPE, val);
++		if (ret < 0)
++			goto out;
++
++		/* Set clock pull mode (no pull) */
++		ret = wl12xx_top_reg_read(wl, OCP_REG_CLK_PULL, &val);
++		if (ret < 0)
++			goto out;
++
++		val |= NO_PULL;
++		ret = wl12xx_top_reg_write(wl, OCP_REG_CLK_PULL, val);
++		if (ret < 0)
++			goto out;
++	} else {
++		u16 val;
++		/* Set clock polarity */
++		ret = wl12xx_top_reg_read(wl, OCP_REG_CLK_POLARITY, &val);
++		if (ret < 0)
++			goto out;
++
++		val &= FREF_CLK_POLARITY_BITS;
++		val |= CLK_REQ_OUTN_SEL;
++		ret = wl12xx_top_reg_write(wl, OCP_REG_CLK_POLARITY, val);
++		if (ret < 0)
++			goto out;
++	}
++
++	ret = wlcore_write32(wl, WL12XX_PLL_PARAMETERS, clk);
++	if (ret < 0)
++		goto out;
++
++	ret = wlcore_read32(wl, WL12XX_PLL_PARAMETERS, &pause);
++	if (ret < 0)
++		goto out;
++
++	wl1271_debug(DEBUG_BOOT, "pause1 0x%x", pause);
++
++	pause &= ~(WU_COUNTER_PAUSE_VAL);
++	pause |= WU_COUNTER_PAUSE_VAL;
++	ret = wlcore_write32(wl, WL12XX_WU_COUNTER_PAUSE, pause);
++
++out:
++	return ret;
++}
++
++static int wl1271_boot_soft_reset(struct wl1271 *wl)
++{
++	unsigned long timeout;
++	u32 boot_data;
++	int ret = 0;
++
++	/* perform soft reset */
++	ret = wlcore_write32(wl, WL12XX_SLV_SOFT_RESET, ACX_SLV_SOFT_RESET_BIT);
++	if (ret < 0)
++		goto out;
++
++	/* SOFT_RESET is self clearing */
++	timeout = jiffies + usecs_to_jiffies(SOFT_RESET_MAX_TIME);
++	while (1) {
++		ret = wlcore_read32(wl, WL12XX_SLV_SOFT_RESET, &boot_data);
++		if (ret < 0)
++			goto out;
++
++		wl1271_debug(DEBUG_BOOT, "soft reset bootdata 0x%x", boot_data);
++		if ((boot_data & ACX_SLV_SOFT_RESET_BIT) == 0)
++			break;
++
++		if (time_after(jiffies, timeout)) {
++			/* 1.2 check pWhalBus->uSelfClearTime if the
++			 * timeout was reached */
++			wl1271_error("soft reset timeout");
++			return -1;
++		}
++
++		udelay(SOFT_RESET_STALL_TIME);
++	}
++
++	/* disable Rx/Tx */
++	ret = wlcore_write32(wl, WL12XX_ENABLE, 0x0);
++	if (ret < 0)
++		goto out;
++
++	/* disable auto calibration on start*/
++	ret = wlcore_write32(wl, WL12XX_SPARE_A2, 0xffff);
++
++out:
++	return ret;
++}
++
++static int wl12xx_pre_boot(struct wl1271 *wl)
++{
++	struct wl12xx_priv *priv = wl->priv;
++	int ret = 0;
++	u32 clk;
++	int selected_clock = -1;
++
++	if (wl->chip.id == CHIP_ID_128X_PG20) {
++		ret = wl128x_boot_clk(wl, &selected_clock);
++		if (ret < 0)
++			goto out;
++	} else {
++		ret = wl127x_boot_clk(wl);
++		if (ret < 0)
++			goto out;
++	}
++
++	/* Continue the ELP wake up sequence */
++	ret = wlcore_write32(wl, WL12XX_WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL);
++	if (ret < 0)
++		goto out;
++
++	udelay(500);
++
++	ret = wlcore_set_partition(wl, &wl->ptable[PART_DRPW]);
++	if (ret < 0)
++		goto out;
++
++	/* Read-modify-write DRPW_SCRATCH_START register (see next state)
++	   to be used by DRPw FW. The RTRIM value will be added by the FW
++	   before taking DRPw out of reset */
++
++	ret = wlcore_read32(wl, WL12XX_DRPW_SCRATCH_START, &clk);
++	if (ret < 0)
++		goto out;
++
++	wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk);
++
++	if (wl->chip.id == CHIP_ID_128X_PG20)
++		clk |= ((selected_clock & 0x3) << 1) << 4;
++	else
++		clk |= (priv->ref_clock << 1) << 4;
++
++	ret = wlcore_write32(wl, WL12XX_DRPW_SCRATCH_START, clk);
++	if (ret < 0)
++		goto out;
++
++	ret = wlcore_set_partition(wl, &wl->ptable[PART_WORK]);
++	if (ret < 0)
++		goto out;
++
++	/* Disable interrupts */
++	ret = wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL);
++	if (ret < 0)
++		goto out;
++
++	ret = wl1271_boot_soft_reset(wl);
++	if (ret < 0)
++		goto out;
++
++out:
++	return ret;
++}
++
++static int wl12xx_pre_upload(struct wl1271 *wl)
++{
++	u32 tmp;
++	u16 polarity;
++	int ret;
++
++	/* write firmware's last address (ie. it's length) to
++	 * ACX_EEPROMLESS_IND_REG */
++	wl1271_debug(DEBUG_BOOT, "ACX_EEPROMLESS_IND_REG");
++
++	ret = wlcore_write32(wl, WL12XX_EEPROMLESS_IND, WL12XX_EEPROMLESS_IND);
++	if (ret < 0)
++		goto out;
++
++	ret = wlcore_read_reg(wl, REG_CHIP_ID_B, &tmp);
++	if (ret < 0)
++		goto out;
++
++	wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp);
++
++	/* 6. read the EEPROM parameters */
++	ret = wlcore_read32(wl, WL12XX_SCR_PAD2, &tmp);
++	if (ret < 0)
++		goto out;
++
++	/* WL1271: The reference driver skips steps 7 to 10 (jumps directly
++	 * to upload_fw) */
++
++	if (wl->chip.id == CHIP_ID_128X_PG20) {
++		ret = wl12xx_top_reg_write(wl, SDIO_IO_DS, HCI_IO_DS_6MA);
++		if (ret < 0)
++			goto out;
++	}
++
++	/* polarity must be set before the firmware is loaded */
++	ret = wl12xx_top_reg_read(wl, OCP_REG_POLARITY, &polarity);
++	if (ret < 0)
++		goto out;
++
++	/* We use HIGH polarity, so unset the LOW bit */
++	polarity &= ~POLARITY_LOW;
++	ret = wl12xx_top_reg_write(wl, OCP_REG_POLARITY, polarity);
++
++out:
++	return ret;
++}
++
++static int wl12xx_enable_interrupts(struct wl1271 *wl)
++{
++	int ret;
++
++	ret = wlcore_write_reg(wl, REG_INTERRUPT_MASK,
++			       WL12XX_ACX_ALL_EVENTS_VECTOR);
++	if (ret < 0)
++		goto out;
++
++	wlcore_enable_interrupts(wl);
++	ret = wlcore_write_reg(wl, REG_INTERRUPT_MASK,
++			       WL1271_ACX_INTR_ALL & ~(WL12XX_INTR_MASK));
++	if (ret < 0)
++		goto disable_interrupts;
++
++	ret = wlcore_write32(wl, WL12XX_HI_CFG, HI_CFG_DEF_VAL);
++	if (ret < 0)
++		goto disable_interrupts;
++
++	return ret;
++
++disable_interrupts:
++	wlcore_disable_interrupts(wl);
++
++out:
++	return ret;
++}
++
++static int wl12xx_boot(struct wl1271 *wl)
++{
++	int ret;
++
++	ret = wl12xx_pre_boot(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wlcore_boot_upload_nvs(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wl12xx_pre_upload(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wlcore_boot_upload_firmware(wl);
++	if (ret < 0)
++		goto out;
++
++	wl->event_mask = BSS_LOSE_EVENT_ID |
++		REGAINED_BSS_EVENT_ID |
++		SCAN_COMPLETE_EVENT_ID |
++		ROLE_STOP_COMPLETE_EVENT_ID |
++		RSSI_SNR_TRIGGER_0_EVENT_ID |
++		PSPOLL_DELIVERY_FAILURE_EVENT_ID |
++		SOFT_GEMINI_SENSE_EVENT_ID |
++		PERIODIC_SCAN_REPORT_EVENT_ID |
++		PERIODIC_SCAN_COMPLETE_EVENT_ID |
++		DUMMY_PACKET_EVENT_ID |
++		PEER_REMOVE_COMPLETE_EVENT_ID |
++		BA_SESSION_RX_CONSTRAINT_EVENT_ID |
++		REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID |
++		INACTIVE_STA_EVENT_ID |
++		MAX_TX_RETRY_EVENT_ID |
++		CHANNEL_SWITCH_COMPLETE_EVENT_ID;
++
++	ret = wlcore_boot_run_firmware(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wl12xx_enable_interrupts(wl);
++
++out:
++	return ret;
++}
++
++static int wl12xx_trigger_cmd(struct wl1271 *wl, int cmd_box_addr,
++			       void *buf, size_t len)
++{
++	int ret;
++
++	ret = wlcore_write(wl, cmd_box_addr, buf, len, false);
++	if (ret < 0)
++		return ret;
++
++	ret = wlcore_write_reg(wl, REG_INTERRUPT_TRIG, WL12XX_INTR_TRIG_CMD);
++
++	return ret;
++}
++
++static int wl12xx_ack_event(struct wl1271 *wl)
++{
++	return wlcore_write_reg(wl, REG_INTERRUPT_TRIG,
++				WL12XX_INTR_TRIG_EVENT_ACK);
++}
++
++static u32 wl12xx_calc_tx_blocks(struct wl1271 *wl, u32 len, u32 spare_blks)
++{
++	u32 blk_size = WL12XX_TX_HW_BLOCK_SIZE;
++	u32 align_len = wlcore_calc_packet_alignment(wl, len);
++
++	return (align_len + blk_size - 1) / blk_size + spare_blks;
++}
++
++static void
++wl12xx_set_tx_desc_blocks(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc,
++			  u32 blks, u32 spare_blks)
++{
++	if (wl->chip.id == CHIP_ID_128X_PG20) {
++		desc->wl128x_mem.total_mem_blocks = blks;
++	} else {
++		desc->wl127x_mem.extra_blocks = spare_blks;
++		desc->wl127x_mem.total_mem_blocks = blks;
++	}
++}
++
++static void
++wl12xx_set_tx_desc_data_len(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc,
++			    struct sk_buff *skb)
++{
++	u32 aligned_len = wlcore_calc_packet_alignment(wl, skb->len);
++
++	if (wl->chip.id == CHIP_ID_128X_PG20) {
++		desc->wl128x_mem.extra_bytes = aligned_len - skb->len;
++		desc->length = cpu_to_le16(aligned_len >> 2);
++
++		wl1271_debug(DEBUG_TX,
++			     "tx_fill_hdr: hlid: %d len: %d life: %d mem: %d extra: %d",
++			     desc->hlid,
++			     le16_to_cpu(desc->length),
++			     le16_to_cpu(desc->life_time),
++			     desc->wl128x_mem.total_mem_blocks,
++			     desc->wl128x_mem.extra_bytes);
++	} else {
++		/* calculate number of padding bytes */
++		int pad = aligned_len - skb->len;
++		desc->tx_attr |=
++			cpu_to_le16(pad << TX_HW_ATTR_OFST_LAST_WORD_PAD);
++
++		/* Store the aligned length in terms of words */
++		desc->length = cpu_to_le16(aligned_len >> 2);
++
++		wl1271_debug(DEBUG_TX,
++			     "tx_fill_hdr: pad: %d hlid: %d len: %d life: %d mem: %d",
++			     pad, desc->hlid,
++			     le16_to_cpu(desc->length),
++			     le16_to_cpu(desc->life_time),
++			     desc->wl127x_mem.total_mem_blocks);
++	}
++}
++
++static enum wl_rx_buf_align
++wl12xx_get_rx_buf_align(struct wl1271 *wl, u32 rx_desc)
++{
++	if (rx_desc & RX_BUF_UNALIGNED_PAYLOAD)
++		return WLCORE_RX_BUF_UNALIGNED;
++
++	return WLCORE_RX_BUF_ALIGNED;
++}
++
++static u32 wl12xx_get_rx_packet_len(struct wl1271 *wl, void *rx_data,
++				    u32 data_len)
++{
++	struct wl1271_rx_descriptor *desc = rx_data;
++
++	/* invalid packet */
++	if (data_len < sizeof(*desc) ||
++	    data_len < sizeof(*desc) + desc->pad_len)
++		return 0;
++
++	return data_len - sizeof(*desc) - desc->pad_len;
++}
++
++static int wl12xx_tx_delayed_compl(struct wl1271 *wl)
++{
++	if (wl->fw_status_1->tx_results_counter ==
++	    (wl->tx_results_count & 0xff))
++		return 0;
++
++	return wlcore_tx_complete(wl);
++}
++
++static int wl12xx_hw_init(struct wl1271 *wl)
++{
++	int ret;
++
++	if (wl->chip.id == CHIP_ID_128X_PG20) {
++		u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE;
++
++		ret = wl128x_cmd_general_parms(wl);
++		if (ret < 0)
++			goto out;
++
++		/*
++		 * If we are in calibrator based auto detect then we got the FEM nr
++		 * in wl->fem_manuf. No need to continue further
++		 */
++		if (wl->plt_mode == PLT_FEM_DETECT)
++			goto out;
++
++		ret = wl128x_cmd_radio_parms(wl);
++		if (ret < 0)
++			goto out;
++
++		if (wl->quirks & WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN)
++			/* Enable SDIO padding */
++			host_cfg_bitmap |= HOST_IF_CFG_TX_PAD_TO_SDIO_BLK;
++
++		/* Must be before wl1271_acx_init_mem_config() */
++		ret = wl1271_acx_host_if_cfg_bitmap(wl, host_cfg_bitmap);
++		if (ret < 0)
++			goto out;
++	} else {
++		ret = wl1271_cmd_general_parms(wl);
++		if (ret < 0)
++			goto out;
++
++		/*
++		 * If we are in calibrator based auto detect then we got the FEM nr
++		 * in wl->fem_manuf. No need to continue further
++		 */
++		if (wl->plt_mode == PLT_FEM_DETECT)
++			goto out;
++
++		ret = wl1271_cmd_radio_parms(wl);
++		if (ret < 0)
++			goto out;
++		ret = wl1271_cmd_ext_radio_parms(wl);
++		if (ret < 0)
++			goto out;
++	}
++out:
++	return ret;
++}
++
++static u32 wl12xx_sta_get_ap_rate_mask(struct wl1271 *wl,
++				       struct wl12xx_vif *wlvif)
++{
++	return wlvif->rate_set;
++}
++
++static void wl12xx_conf_init(struct wl1271 *wl)
++{
++	struct wl12xx_priv *priv = wl->priv;
++
++	/* apply driver default configuration */
++	memcpy(&wl->conf, &wl12xx_conf, sizeof(wl12xx_conf));
++
++	/* apply default private configuration */
++	memcpy(&priv->conf, &wl12xx_default_priv_conf, sizeof(priv->conf));
++}
++
++static bool wl12xx_mac_in_fuse(struct wl1271 *wl)
++{
++	bool supported = false;
++	u8 major, minor;
++
++	if (wl->chip.id == CHIP_ID_128X_PG20) {
++		major = WL128X_PG_GET_MAJOR(wl->hw_pg_ver);
++		minor = WL128X_PG_GET_MINOR(wl->hw_pg_ver);
++
++		/* in wl128x we have the MAC address if the PG is >= (2, 1) */
++		if (major > 2 || (major == 2 && minor >= 1))
++			supported = true;
++	} else {
++		major = WL127X_PG_GET_MAJOR(wl->hw_pg_ver);
++		minor = WL127X_PG_GET_MINOR(wl->hw_pg_ver);
++
++		/* in wl127x we have the MAC address if the PG is >= (3, 1) */
++		if (major == 3 && minor >= 1)
++			supported = true;
++	}
++
++	wl1271_debug(DEBUG_PROBE,
++		     "PG Ver major = %d minor = %d, MAC %s present",
++		     major, minor, supported ? "is" : "is not");
++
++	return supported;
++}
++
++static int wl12xx_get_fuse_mac(struct wl1271 *wl)
++{
++	u32 mac1, mac2;
++	int ret;
++
++	ret = wlcore_set_partition(wl, &wl->ptable[PART_DRPW]);
++	if (ret < 0)
++		goto out;
++
++	ret = wlcore_read32(wl, WL12XX_REG_FUSE_BD_ADDR_1, &mac1);
++	if (ret < 0)
++		goto out;
++
++	ret = wlcore_read32(wl, WL12XX_REG_FUSE_BD_ADDR_2, &mac2);
++	if (ret < 0)
++		goto out;
++
++	/* these are the two parts of the BD_ADDR */
++	wl->fuse_oui_addr = ((mac2 & 0xffff) << 8) +
++		((mac1 & 0xff000000) >> 24);
++	wl->fuse_nic_addr = mac1 & 0xffffff;
++
++	ret = wlcore_set_partition(wl, &wl->ptable[PART_DOWN]);
++
++out:
++	return ret;
++}
++
++static int wl12xx_get_pg_ver(struct wl1271 *wl, s8 *ver)
++{
++	u16 die_info;
++	int ret;
++
++	if (wl->chip.id == CHIP_ID_128X_PG20)
++		ret = wl12xx_top_reg_read(wl, WL128X_REG_FUSE_DATA_2_1,
++					  &die_info);
++	else
++		ret = wl12xx_top_reg_read(wl, WL127X_REG_FUSE_DATA_2_1,
++					  &die_info);
++
++	if (ret >= 0 && ver)
++		*ver = (s8)((die_info & PG_VER_MASK) >> PG_VER_OFFSET);
++
++	return ret;
++}
++
++static int wl12xx_get_mac(struct wl1271 *wl)
++{
++	if (wl12xx_mac_in_fuse(wl))
++		return wl12xx_get_fuse_mac(wl);
++
++	return 0;
++}
++
++static void wl12xx_set_tx_desc_csum(struct wl1271 *wl,
++				    struct wl1271_tx_hw_descr *desc,
++				    struct sk_buff *skb)
++{
++	desc->wl12xx_reserved = 0;
++}
++
++static int wl12xx_plt_init(struct wl1271 *wl)
++{
++	int ret;
++
++	ret = wl->ops->boot(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wl->ops->hw_init(wl);
++	if (ret < 0)
++		goto out_irq_disable;
++
++	/*
++	 * If we are in calibrator based auto detect then we got the FEM nr
++	 * in wl->fem_manuf. No need to continue further
++	 */
++	if (wl->plt_mode == PLT_FEM_DETECT)
++		goto out;
++
++	ret = wl1271_acx_init_mem_config(wl);
++	if (ret < 0)
++		goto out_irq_disable;
++
++	ret = wl12xx_acx_mem_cfg(wl);
++	if (ret < 0)
++		goto out_free_memmap;
++
++	/* Enable data path */
++	ret = wl1271_cmd_data_path(wl, 1);
++	if (ret < 0)
++		goto out_free_memmap;
++
++	/* Configure for CAM power saving (ie. always active) */
++	ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
++	if (ret < 0)
++		goto out_free_memmap;
++
++	/* configure PM */
++	ret = wl1271_acx_pm_config(wl);
++	if (ret < 0)
++		goto out_free_memmap;
++
++	goto out;
++
++out_free_memmap:
++	kfree(wl->target_mem_map);
++	wl->target_mem_map = NULL;
++
++out_irq_disable:
++	mutex_unlock(&wl->mutex);
++	/* Unlocking the mutex in the middle of handling is
++	   inherently unsafe. In this case we deem it safe to do,
++	   because we need to let any possibly pending IRQ out of
++	   the system (and while we are WL1271_STATE_OFF the IRQ
++	   work function will not do anything.) Also, any other
++	   possible concurrent operations will fail due to the
++	   current state, hence the wl1271 struct should be safe. */
++	wlcore_disable_interrupts(wl);
++	mutex_lock(&wl->mutex);
++out:
++	return ret;
++}
++
++static int wl12xx_get_spare_blocks(struct wl1271 *wl, bool is_gem)
++{
++	if (is_gem)
++		return WL12XX_TX_HW_BLOCK_GEM_SPARE;
++
++	return WL12XX_TX_HW_BLOCK_SPARE_DEFAULT;
++}
++
++static int wl12xx_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
++			  struct ieee80211_vif *vif,
++			  struct ieee80211_sta *sta,
++			  struct ieee80211_key_conf *key_conf)
++{
++	return wlcore_set_key(wl, cmd, vif, sta, key_conf);
++}
++
++static int wl12xx_set_peer_cap(struct wl1271 *wl,
++			       struct ieee80211_sta_ht_cap *ht_cap,
++			       bool allow_ht_operation,
++			       u32 rate_set, u8 hlid)
++{
++	return wl1271_acx_set_ht_capabilities(wl, ht_cap, allow_ht_operation,
++					      hlid);
++}
++
++static bool wl12xx_lnk_high_prio(struct wl1271 *wl, u8 hlid,
++				 struct wl1271_link *lnk)
++{
++	u8 thold;
++
++	if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map))
++		thold = wl->conf.tx.fast_link_thold;
++	else
++		thold = wl->conf.tx.slow_link_thold;
++
++	return lnk->allocated_pkts < thold;
++}
++
++static bool wl12xx_lnk_low_prio(struct wl1271 *wl, u8 hlid,
++				struct wl1271_link *lnk)
++{
++	/* any link is good for low priority */
++	return true;
++}
++
++static int wl12xx_setup(struct wl1271 *wl);
++
++static struct wlcore_ops wl12xx_ops = {
++	.setup			= wl12xx_setup,
++	.identify_chip		= wl12xx_identify_chip,
++	.boot			= wl12xx_boot,
++	.plt_init		= wl12xx_plt_init,
++	.trigger_cmd		= wl12xx_trigger_cmd,
++	.ack_event		= wl12xx_ack_event,
++	.wait_for_event		= wl12xx_wait_for_event,
++	.process_mailbox_events	= wl12xx_process_mailbox_events,
++	.calc_tx_blocks		= wl12xx_calc_tx_blocks,
++	.set_tx_desc_blocks	= wl12xx_set_tx_desc_blocks,
++	.set_tx_desc_data_len	= wl12xx_set_tx_desc_data_len,
++	.get_rx_buf_align	= wl12xx_get_rx_buf_align,
++	.get_rx_packet_len	= wl12xx_get_rx_packet_len,
++	.tx_immediate_compl	= NULL,
++	.tx_delayed_compl	= wl12xx_tx_delayed_compl,
++	.hw_init		= wl12xx_hw_init,
++	.init_vif		= NULL,
++	.sta_get_ap_rate_mask	= wl12xx_sta_get_ap_rate_mask,
++	.get_pg_ver		= wl12xx_get_pg_ver,
++	.get_mac		= wl12xx_get_mac,
++	.set_tx_desc_csum	= wl12xx_set_tx_desc_csum,
++	.set_rx_csum		= NULL,
++	.ap_get_mimo_wide_rate_mask = NULL,
++	.debugfs_init		= wl12xx_debugfs_add_files,
++	.scan_start		= wl12xx_scan_start,
++	.scan_stop		= wl12xx_scan_stop,
++	.sched_scan_start	= wl12xx_sched_scan_start,
++	.sched_scan_stop	= wl12xx_scan_sched_scan_stop,
++	.get_spare_blocks	= wl12xx_get_spare_blocks,
++	.set_key		= wl12xx_set_key,
++	.channel_switch		= wl12xx_cmd_channel_switch,
++	.pre_pkt_send		= NULL,
++	.set_peer_cap		= wl12xx_set_peer_cap,
++	.lnk_high_prio		= wl12xx_lnk_high_prio,
++	.lnk_low_prio		= wl12xx_lnk_low_prio,
++};
++
++static struct ieee80211_sta_ht_cap wl12xx_ht_cap = {
++	.cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 |
++	       (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT),
++	.ht_supported = true,
++	.ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K,
++	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_8,
++	.mcs = {
++		.rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
++		.rx_highest = cpu_to_le16(72),
++		.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
++		},
++};
++
++static int wl12xx_setup(struct wl1271 *wl)
++{
++	struct wl12xx_priv *priv = wl->priv;
++	struct wlcore_platdev_data *pdev_data = wl->pdev->dev.platform_data;
++	struct wl12xx_platform_data *pdata = pdev_data->pdata;
++
++	wl->rtable = wl12xx_rtable;
++	wl->num_tx_desc = WL12XX_NUM_TX_DESCRIPTORS;
++	wl->num_rx_desc = WL12XX_NUM_RX_DESCRIPTORS;
++	wl->num_channels = 1;
++	wl->num_mac_addr = WL12XX_NUM_MAC_ADDRESSES;
++	wl->band_rate_to_idx = wl12xx_band_rate_to_idx;
++	wl->hw_tx_rate_tbl_size = WL12XX_CONF_HW_RXTX_RATE_MAX;
++	wl->hw_min_ht_rate = WL12XX_CONF_HW_RXTX_RATE_MCS0;
++	wl->fw_status_priv_len = 0;
++	wl->stats.fw_stats_len = sizeof(struct wl12xx_acx_statistics);
++	wlcore_set_ht_cap(wl, IEEE80211_BAND_2GHZ, &wl12xx_ht_cap);
++	wlcore_set_ht_cap(wl, IEEE80211_BAND_5GHZ, &wl12xx_ht_cap);
++	wl12xx_conf_init(wl);
++
++	if (!fref_param) {
++		priv->ref_clock = pdata->board_ref_clock;
++	} else {
++		if (!strcmp(fref_param, "19.2"))
++			priv->ref_clock = WL12XX_REFCLOCK_19;
++		else if (!strcmp(fref_param, "26"))
++			priv->ref_clock = WL12XX_REFCLOCK_26;
++		else if (!strcmp(fref_param, "26x"))
++			priv->ref_clock = WL12XX_REFCLOCK_26_XTAL;
++		else if (!strcmp(fref_param, "38.4"))
++			priv->ref_clock = WL12XX_REFCLOCK_38;
++		else if (!strcmp(fref_param, "38.4x"))
++			priv->ref_clock = WL12XX_REFCLOCK_38_XTAL;
++		else if (!strcmp(fref_param, "52"))
++			priv->ref_clock = WL12XX_REFCLOCK_52;
++		else
++			wl1271_error("Invalid fref parameter %s", fref_param);
++	}
++
++	if (!tcxo_param) {
++		priv->tcxo_clock = pdata->board_tcxo_clock;
++	} else {
++		if (!strcmp(tcxo_param, "19.2"))
++			priv->tcxo_clock = WL12XX_TCXOCLOCK_19_2;
++		else if (!strcmp(tcxo_param, "26"))
++			priv->tcxo_clock = WL12XX_TCXOCLOCK_26;
++		else if (!strcmp(tcxo_param, "38.4"))
++			priv->tcxo_clock = WL12XX_TCXOCLOCK_38_4;
++		else if (!strcmp(tcxo_param, "52"))
++			priv->tcxo_clock = WL12XX_TCXOCLOCK_52;
++		else if (!strcmp(tcxo_param, "16.368"))
++			priv->tcxo_clock = WL12XX_TCXOCLOCK_16_368;
++		else if (!strcmp(tcxo_param, "32.736"))
++			priv->tcxo_clock = WL12XX_TCXOCLOCK_32_736;
++		else if (!strcmp(tcxo_param, "16.8"))
++			priv->tcxo_clock = WL12XX_TCXOCLOCK_16_8;
++		else if (!strcmp(tcxo_param, "33.6"))
++			priv->tcxo_clock = WL12XX_TCXOCLOCK_33_6;
++		else
++			wl1271_error("Invalid tcxo parameter %s", tcxo_param);
++	}
++
++	priv->rx_mem_addr = kmalloc(sizeof(*priv->rx_mem_addr), GFP_KERNEL);
++	if (!priv->rx_mem_addr)
++		return -ENOMEM;
++
++	return 0;
++}
++
++static int wl12xx_probe(struct platform_device *pdev)
++{
++	struct wl1271 *wl;
++	struct ieee80211_hw *hw;
++	int ret;
++
++	hw = wlcore_alloc_hw(sizeof(struct wl12xx_priv),
++			     WL12XX_AGGR_BUFFER_SIZE,
++			     sizeof(struct wl12xx_event_mailbox));
++	if (IS_ERR(hw)) {
++		wl1271_error("can't allocate hw");
++		ret = PTR_ERR(hw);
++		goto out;
++	}
++
++	wl = hw->priv;
++	wl->ops = &wl12xx_ops;
++	wl->ptable = wl12xx_ptable;
++	ret = wlcore_probe(wl, pdev);
++	if (ret)
++		goto out_free;
++
++	return ret;
++
++out_free:
++	wlcore_free_hw(wl);
++out:
++	return ret;
++}
++
++static int wl12xx_remove(struct platform_device *pdev)
++{
++	struct wl1271 *wl = platform_get_drvdata(pdev);
++	struct wl12xx_priv *priv;
++
++	if (!wl)
++		goto out;
++	priv = wl->priv;
++
++	kfree(priv->rx_mem_addr);
++
++out:
++	return wlcore_remove(pdev);
++}
++
++static const struct platform_device_id wl12xx_id_table[] = {
++	{ "wl12xx", 0 },
++	{  } /* Terminating Entry */
++};
++MODULE_DEVICE_TABLE(platform, wl12xx_id_table);
++
++static struct platform_driver wl12xx_driver = {
++	.probe		= wl12xx_probe,
++	.remove		= wl12xx_remove,
++	.id_table	= wl12xx_id_table,
++	.driver = {
++		.name	= "wl12xx_driver",
++		.owner	= THIS_MODULE,
++	}
++};
++
++module_platform_driver(wl12xx_driver);
++
++module_param_named(fref, fref_param, charp, 0);
++MODULE_PARM_DESC(fref, "FREF clock: 19.2, 26, 26x, 38.4, 38.4x, 52");
++
++module_param_named(tcxo, tcxo_param, charp, 0);
++MODULE_PARM_DESC(tcxo,
++		 "TCXO clock: 19.2, 26, 38.4, 52, 16.368, 32.736, 16.8, 33.6");
++
++MODULE_LICENSE("GPL v2");
++MODULE_AUTHOR("Luciano Coelho <coelho at ti.com>");
++MODULE_FIRMWARE(WL127X_FW_NAME_SINGLE);
++MODULE_FIRMWARE(WL127X_FW_NAME_MULTI);
++MODULE_FIRMWARE(WL127X_PLT_FW_NAME);
++MODULE_FIRMWARE(WL128X_FW_NAME_SINGLE);
++MODULE_FIRMWARE(WL128X_FW_NAME_MULTI);
++MODULE_FIRMWARE(WL128X_PLT_FW_NAME);
+diff -Nuar git.old/drivers/net/wireless/ti/wl12xx/wl12xx.h git/drivers/net/wireless/ti/wl12xx/wl12xx.h
+--- git.old/drivers/net/wireless/ti/wl12xx/wl12xx.h	2015-03-04 16:35:05.646185998 +0100
++++ git/drivers/net/wireless/ti/wl12xx/wl12xx.h	2015-03-04 16:52:01.554176309 +0100
+@@ -79,4 +79,32 @@
+ 	struct wl127x_rx_mem_pool_addr *rx_mem_addr;
+ };
+ 
++/* Reference clock values */
++enum {
++	WL12XX_REFCLOCK_19	= 0, /* 19.2 MHz */
++	WL12XX_REFCLOCK_26	= 1, /* 26 MHz */
++	WL12XX_REFCLOCK_38	= 2, /* 38.4 MHz */
++	WL12XX_REFCLOCK_52	= 3, /* 52 MHz */
++	WL12XX_REFCLOCK_38_XTAL = 4, /* 38.4 MHz, XTAL */
++	WL12XX_REFCLOCK_26_XTAL = 5, /* 26 MHz, XTAL */
++};
++
++/* TCXO clock values */
++enum {
++	WL12XX_TCXOCLOCK_19_2	= 0, /* 19.2MHz */
++	WL12XX_TCXOCLOCK_26	= 1, /* 26 MHz */
++	WL12XX_TCXOCLOCK_38_4	= 2, /* 38.4MHz */
++	WL12XX_TCXOCLOCK_52	= 3, /* 52 MHz */
++	WL12XX_TCXOCLOCK_16_368	= 4, /* 16.368 MHz */
++	WL12XX_TCXOCLOCK_32_736	= 5, /* 32.736 MHz */
++	WL12XX_TCXOCLOCK_16_8	= 6, /* 16.8 MHz */
++	WL12XX_TCXOCLOCK_33_6	= 7, /* 33.6 MHz */
++};
++
++struct wl12xx_clock {
++	u32	freq;
++	bool	xtal;
++	u8	hw_idx;
++};
++
+ #endif /* __WL12XX_PRIV_H__ */
+diff -Nuar git.old/drivers/net/wireless/ti/wlcore/debugfs.c git/drivers/net/wireless/ti/wlcore/debugfs.c
+--- git.old/drivers/net/wireless/ti/wlcore/debugfs.c	2015-03-04 16:35:05.702185997 +0100
++++ git/drivers/net/wireless/ti/wlcore/debugfs.c	2015-03-04 16:49:20.826177842 +0100
+@@ -486,7 +486,7 @@
+ 	DRIVER_STATE_PRINT_HEX(irq);
+ 	/* TODO: ref_clock and tcxo_clock were moved to wl12xx priv */
+ 	DRIVER_STATE_PRINT_HEX(hw_pg_ver);
+-	DRIVER_STATE_PRINT_HEX(platform_quirks);
++	DRIVER_STATE_PRINT_HEX(irq_flags);
+ 	DRIVER_STATE_PRINT_HEX(chip.id);
+ 	DRIVER_STATE_PRINT_STR(chip.fw_ver_str);
+ 	DRIVER_STATE_PRINT_STR(chip.phy_fw_ver_str);
+diff -Nuar git.old/drivers/net/wireless/ti/wlcore/debugfs.c.orig git/drivers/net/wireless/ti/wlcore/debugfs.c.orig
+--- git.old/drivers/net/wireless/ti/wlcore/debugfs.c.orig	1970-01-01 01:00:00.000000000 +0100
++++ git/drivers/net/wireless/ti/wlcore/debugfs.c.orig	2015-03-04 16:35:05.702185997 +0100
+@@ -0,0 +1,1330 @@
++/*
++ * This file is part of wl1271
++ *
++ * Copyright (C) 2009 Nokia Corporation
++ *
++ * Contact: Luciano Coelho <luciano.coelho at nokia.com>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++ * 02110-1301 USA
++ *
++ */
++
++#include "debugfs.h"
++
++#include <linux/skbuff.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++
++#include "wlcore.h"
++#include "debug.h"
++#include "acx.h"
++#include "ps.h"
++#include "io.h"
++#include "tx.h"
++#include "hw_ops.h"
++
++/* ms */
++#define WL1271_DEBUGFS_STATS_LIFETIME 1000
++
++#define WLCORE_MAX_BLOCK_SIZE ((size_t)(4*PAGE_SIZE))
++
++/* debugfs macros idea from mac80211 */
++int wl1271_format_buffer(char __user *userbuf, size_t count,
++			 loff_t *ppos, char *fmt, ...)
++{
++	va_list args;
++	char buf[DEBUGFS_FORMAT_BUFFER_SIZE];
++	int res;
++
++	va_start(args, fmt);
++	res = vscnprintf(buf, sizeof(buf), fmt, args);
++	va_end(args);
++
++	return simple_read_from_buffer(userbuf, count, ppos, buf, res);
++}
++EXPORT_SYMBOL_GPL(wl1271_format_buffer);
++
++void wl1271_debugfs_update_stats(struct wl1271 *wl)
++{
++	int ret;
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	if (!wl->plt &&
++	    time_after(jiffies, wl->stats.fw_stats_update +
++		       msecs_to_jiffies(WL1271_DEBUGFS_STATS_LIFETIME))) {
++		wl1271_acx_statistics(wl, wl->stats.fw_stats);
++		wl->stats.fw_stats_update = jiffies;
++	}
++
++	wl1271_ps_elp_sleep(wl);
++
++out:
++	mutex_unlock(&wl->mutex);
++}
++EXPORT_SYMBOL_GPL(wl1271_debugfs_update_stats);
++
++DEBUGFS_READONLY_FILE(retry_count, "%u", wl->stats.retry_count);
++DEBUGFS_READONLY_FILE(excessive_retries, "%u",
++		      wl->stats.excessive_retries);
++
++static ssize_t tx_queue_len_read(struct file *file, char __user *userbuf,
++				 size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	u32 queue_len;
++	char buf[20];
++	int res;
++
++	queue_len = wl1271_tx_total_queue_count(wl);
++
++	res = scnprintf(buf, sizeof(buf), "%u\n", queue_len);
++	return simple_read_from_buffer(userbuf, count, ppos, buf, res);
++}
++
++static const struct file_operations tx_queue_len_ops = {
++	.read = tx_queue_len_read,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static void chip_op_handler(struct wl1271 *wl, unsigned long value,
++			    void *arg)
++{
++	int ret;
++	int (*chip_op) (struct wl1271 *wl);
++
++	if (!arg) {
++		wl1271_warning("debugfs chip_op_handler with no callback");
++		return;
++	}
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		return;
++
++	chip_op = arg;
++	chip_op(wl);
++
++	wl1271_ps_elp_sleep(wl);
++}
++
++
++static inline void no_write_handler(struct wl1271 *wl,
++				    unsigned long value,
++				    unsigned long param)
++{
++}
++
++#define WL12XX_CONF_DEBUGFS(param, conf_sub_struct,			\
++			    min_val, max_val, write_handler_locked,	\
++			    write_handler_arg)				\
++	static ssize_t param##_read(struct file *file,			\
++				      char __user *user_buf,		\
++				      size_t count, loff_t *ppos)	\
++	{								\
++	struct wl1271 *wl = file->private_data;				\
++	return wl1271_format_buffer(user_buf, count,			\
++				    ppos, "%d\n",			\
++				    wl->conf.conf_sub_struct.param);	\
++	}								\
++									\
++	static ssize_t param##_write(struct file *file,			\
++				     const char __user *user_buf,	\
++				     size_t count, loff_t *ppos)	\
++	{								\
++	struct wl1271 *wl = file->private_data;				\
++	unsigned long value;						\
++	int ret;							\
++									\
++	ret = kstrtoul_from_user(user_buf, count, 10, &value);		\
++	if (ret < 0) {							\
++		wl1271_warning("illegal value for " #param);		\
++		return -EINVAL;						\
++	}								\
++									\
++	if (value < min_val || value > max_val) {			\
++		wl1271_warning(#param " is not in valid range");	\
++		return -ERANGE;						\
++	}								\
++									\
++	mutex_lock(&wl->mutex);						\
++	wl->conf.conf_sub_struct.param = value;				\
++									\
++	write_handler_locked(wl, value, write_handler_arg);		\
++									\
++	mutex_unlock(&wl->mutex);					\
++	return count;							\
++	}								\
++									\
++	static const struct file_operations param##_ops = {		\
++		.read = param##_read,					\
++		.write = param##_write,					\
++		.open = simple_open,					\
++		.llseek = default_llseek,				\
++	};
++
++WL12XX_CONF_DEBUGFS(irq_pkt_threshold, rx, 0, 65535,
++		    chip_op_handler, wl1271_acx_init_rx_interrupt)
++WL12XX_CONF_DEBUGFS(irq_blk_threshold, rx, 0, 65535,
++		    chip_op_handler, wl1271_acx_init_rx_interrupt)
++WL12XX_CONF_DEBUGFS(irq_timeout, rx, 0, 100,
++		    chip_op_handler, wl1271_acx_init_rx_interrupt)
++
++static ssize_t gpio_power_read(struct file *file, char __user *user_buf,
++			  size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	bool state = test_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
++
++	int res;
++	char buf[10];
++
++	res = scnprintf(buf, sizeof(buf), "%d\n", state);
++
++	return simple_read_from_buffer(user_buf, count, ppos, buf, res);
++}
++
++static ssize_t gpio_power_write(struct file *file,
++			   const char __user *user_buf,
++			   size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	unsigned long value;
++	int ret;
++
++	ret = kstrtoul_from_user(user_buf, count, 10, &value);
++	if (ret < 0) {
++		wl1271_warning("illegal value in gpio_power");
++		return -EINVAL;
++	}
++
++	mutex_lock(&wl->mutex);
++
++	if (value)
++		wl1271_power_on(wl);
++	else
++		wl1271_power_off(wl);
++
++	mutex_unlock(&wl->mutex);
++	return count;
++}
++
++static const struct file_operations gpio_power_ops = {
++	.read = gpio_power_read,
++	.write = gpio_power_write,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static ssize_t start_recovery_write(struct file *file,
++				    const char __user *user_buf,
++				    size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++
++	mutex_lock(&wl->mutex);
++	wl12xx_queue_recovery_work(wl);
++	mutex_unlock(&wl->mutex);
++
++	return count;
++}
++
++static const struct file_operations start_recovery_ops = {
++	.write = start_recovery_write,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static ssize_t dynamic_ps_timeout_read(struct file *file, char __user *user_buf,
++			  size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++
++	return wl1271_format_buffer(user_buf, count,
++				    ppos, "%d\n",
++				    wl->conf.conn.dynamic_ps_timeout);
++}
++
++static ssize_t dynamic_ps_timeout_write(struct file *file,
++				    const char __user *user_buf,
++				    size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	struct wl12xx_vif *wlvif;
++	unsigned long value;
++	int ret;
++
++	ret = kstrtoul_from_user(user_buf, count, 10, &value);
++	if (ret < 0) {
++		wl1271_warning("illegal value in dynamic_ps");
++		return -EINVAL;
++	}
++
++	if (value < 1 || value > 65535) {
++		wl1271_warning("dyanmic_ps_timeout is not in valid range");
++		return -ERANGE;
++	}
++
++	mutex_lock(&wl->mutex);
++
++	wl->conf.conn.dynamic_ps_timeout = value;
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	/* In case we're already in PSM, trigger it again to set new timeout
++	 * immediately without waiting for re-association
++	 */
++
++	wl12xx_for_each_wlvif_sta(wl, wlvif) {
++		if (test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags))
++			wl1271_ps_set_mode(wl, wlvif, STATION_AUTO_PS_MODE);
++	}
++
++	wl1271_ps_elp_sleep(wl);
++
++out:
++	mutex_unlock(&wl->mutex);
++	return count;
++}
++
++static const struct file_operations dynamic_ps_timeout_ops = {
++	.read = dynamic_ps_timeout_read,
++	.write = dynamic_ps_timeout_write,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static ssize_t forced_ps_read(struct file *file, char __user *user_buf,
++			  size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++
++	return wl1271_format_buffer(user_buf, count,
++				    ppos, "%d\n",
++				    wl->conf.conn.forced_ps);
++}
++
++static ssize_t forced_ps_write(struct file *file,
++				    const char __user *user_buf,
++				    size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	struct wl12xx_vif *wlvif;
++	unsigned long value;
++	int ret, ps_mode;
++
++	ret = kstrtoul_from_user(user_buf, count, 10, &value);
++	if (ret < 0) {
++		wl1271_warning("illegal value in forced_ps");
++		return -EINVAL;
++	}
++
++	if (value != 1 && value != 0) {
++		wl1271_warning("forced_ps should be either 0 or 1");
++		return -ERANGE;
++	}
++
++	mutex_lock(&wl->mutex);
++
++	if (wl->conf.conn.forced_ps == value)
++		goto out;
++
++	wl->conf.conn.forced_ps = value;
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	/* In case we're already in PSM, trigger it again to switch mode
++	 * immediately without waiting for re-association
++	 */
++
++	ps_mode = value ? STATION_POWER_SAVE_MODE : STATION_AUTO_PS_MODE;
++
++	wl12xx_for_each_wlvif_sta(wl, wlvif) {
++		if (test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags))
++			wl1271_ps_set_mode(wl, wlvif, ps_mode);
++	}
++
++	wl1271_ps_elp_sleep(wl);
++
++out:
++	mutex_unlock(&wl->mutex);
++	return count;
++}
++
++static const struct file_operations forced_ps_ops = {
++	.read = forced_ps_read,
++	.write = forced_ps_write,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static ssize_t split_scan_timeout_read(struct file *file, char __user *user_buf,
++			  size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++
++	return wl1271_format_buffer(user_buf, count,
++				    ppos, "%d\n",
++				    wl->conf.scan.split_scan_timeout / 1000);
++}
++
++static ssize_t split_scan_timeout_write(struct file *file,
++				    const char __user *user_buf,
++				    size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	unsigned long value;
++	int ret;
++
++	ret = kstrtoul_from_user(user_buf, count, 10, &value);
++	if (ret < 0) {
++		wl1271_warning("illegal value in split_scan_timeout");
++		return -EINVAL;
++	}
++
++	if (value == 0)
++		wl1271_info("split scan will be disabled");
++
++	mutex_lock(&wl->mutex);
++
++	wl->conf.scan.split_scan_timeout = value * 1000;
++
++	mutex_unlock(&wl->mutex);
++	return count;
++}
++
++static const struct file_operations split_scan_timeout_ops = {
++	.read = split_scan_timeout_read,
++	.write = split_scan_timeout_write,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static ssize_t driver_state_read(struct file *file, char __user *user_buf,
++				 size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	int res = 0;
++	ssize_t ret;
++	char *buf;
++
++#define DRIVER_STATE_BUF_LEN 1024
++
++	buf = kmalloc(DRIVER_STATE_BUF_LEN, GFP_KERNEL);
++	if (!buf)
++		return -ENOMEM;
++
++	mutex_lock(&wl->mutex);
++
++#define DRIVER_STATE_PRINT(x, fmt)   \
++	(res += scnprintf(buf + res, DRIVER_STATE_BUF_LEN - res,\
++			  #x " = " fmt "\n", wl->x))
++
++#define DRIVER_STATE_PRINT_LONG(x) DRIVER_STATE_PRINT(x, "%ld")
++#define DRIVER_STATE_PRINT_INT(x)  DRIVER_STATE_PRINT(x, "%d")
++#define DRIVER_STATE_PRINT_STR(x)  DRIVER_STATE_PRINT(x, "%s")
++#define DRIVER_STATE_PRINT_LHEX(x) DRIVER_STATE_PRINT(x, "0x%lx")
++#define DRIVER_STATE_PRINT_HEX(x)  DRIVER_STATE_PRINT(x, "0x%x")
++
++	DRIVER_STATE_PRINT_INT(tx_blocks_available);
++	DRIVER_STATE_PRINT_INT(tx_allocated_blocks);
++	DRIVER_STATE_PRINT_INT(tx_allocated_pkts[0]);
++	DRIVER_STATE_PRINT_INT(tx_allocated_pkts[1]);
++	DRIVER_STATE_PRINT_INT(tx_allocated_pkts[2]);
++	DRIVER_STATE_PRINT_INT(tx_allocated_pkts[3]);
++	DRIVER_STATE_PRINT_INT(tx_frames_cnt);
++	DRIVER_STATE_PRINT_LHEX(tx_frames_map[0]);
++	DRIVER_STATE_PRINT_INT(tx_queue_count[0]);
++	DRIVER_STATE_PRINT_INT(tx_queue_count[1]);
++	DRIVER_STATE_PRINT_INT(tx_queue_count[2]);
++	DRIVER_STATE_PRINT_INT(tx_queue_count[3]);
++	DRIVER_STATE_PRINT_INT(tx_packets_count);
++	DRIVER_STATE_PRINT_INT(tx_results_count);
++	DRIVER_STATE_PRINT_LHEX(flags);
++	DRIVER_STATE_PRINT_INT(tx_blocks_freed);
++	DRIVER_STATE_PRINT_INT(rx_counter);
++	DRIVER_STATE_PRINT_INT(state);
++	DRIVER_STATE_PRINT_INT(channel);
++	DRIVER_STATE_PRINT_INT(band);
++	DRIVER_STATE_PRINT_INT(power_level);
++	DRIVER_STATE_PRINT_INT(sg_enabled);
++	DRIVER_STATE_PRINT_INT(enable_11a);
++	DRIVER_STATE_PRINT_INT(noise);
++	DRIVER_STATE_PRINT_HEX(ap_fw_ps_map);
++	DRIVER_STATE_PRINT_LHEX(ap_ps_map);
++	DRIVER_STATE_PRINT_HEX(quirks);
++	DRIVER_STATE_PRINT_HEX(irq);
++	/* TODO: ref_clock and tcxo_clock were moved to wl12xx priv */
++	DRIVER_STATE_PRINT_HEX(hw_pg_ver);
++	DRIVER_STATE_PRINT_HEX(platform_quirks);
++	DRIVER_STATE_PRINT_HEX(chip.id);
++	DRIVER_STATE_PRINT_STR(chip.fw_ver_str);
++	DRIVER_STATE_PRINT_STR(chip.phy_fw_ver_str);
++	DRIVER_STATE_PRINT_INT(recovery_count);
++
++#undef DRIVER_STATE_PRINT_INT
++#undef DRIVER_STATE_PRINT_LONG
++#undef DRIVER_STATE_PRINT_HEX
++#undef DRIVER_STATE_PRINT_LHEX
++#undef DRIVER_STATE_PRINT_STR
++#undef DRIVER_STATE_PRINT
++#undef DRIVER_STATE_BUF_LEN
++
++	mutex_unlock(&wl->mutex);
++
++	ret = simple_read_from_buffer(user_buf, count, ppos, buf, res);
++	kfree(buf);
++	return ret;
++}
++
++static const struct file_operations driver_state_ops = {
++	.read = driver_state_read,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static ssize_t vifs_state_read(struct file *file, char __user *user_buf,
++				 size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	struct wl12xx_vif *wlvif;
++	int ret, res = 0;
++	const int buf_size = 4096;
++	char *buf;
++	char tmp_buf[64];
++
++	buf = kzalloc(buf_size, GFP_KERNEL);
++	if (!buf)
++		return -ENOMEM;
++
++	mutex_lock(&wl->mutex);
++
++#define VIF_STATE_PRINT(x, fmt)				\
++	(res += scnprintf(buf + res, buf_size - res,	\
++			  #x " = " fmt "\n", wlvif->x))
++
++#define VIF_STATE_PRINT_LONG(x)  VIF_STATE_PRINT(x, "%ld")
++#define VIF_STATE_PRINT_INT(x)   VIF_STATE_PRINT(x, "%d")
++#define VIF_STATE_PRINT_STR(x)   VIF_STATE_PRINT(x, "%s")
++#define VIF_STATE_PRINT_LHEX(x)  VIF_STATE_PRINT(x, "0x%lx")
++#define VIF_STATE_PRINT_LLHEX(x) VIF_STATE_PRINT(x, "0x%llx")
++#define VIF_STATE_PRINT_HEX(x)   VIF_STATE_PRINT(x, "0x%x")
++
++#define VIF_STATE_PRINT_NSTR(x, len)				\
++	do {							\
++		memset(tmp_buf, 0, sizeof(tmp_buf));		\
++		memcpy(tmp_buf, wlvif->x,			\
++		       min_t(u8, len, sizeof(tmp_buf) - 1));	\
++		res += scnprintf(buf + res, buf_size - res,	\
++				 #x " = %s\n", tmp_buf);	\
++	} while (0)
++
++	wl12xx_for_each_wlvif(wl, wlvif) {
++		VIF_STATE_PRINT_INT(role_id);
++		VIF_STATE_PRINT_INT(bss_type);
++		VIF_STATE_PRINT_LHEX(flags);
++		VIF_STATE_PRINT_INT(p2p);
++		VIF_STATE_PRINT_INT(dev_role_id);
++		VIF_STATE_PRINT_INT(dev_hlid);
++
++		if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
++		    wlvif->bss_type == BSS_TYPE_IBSS) {
++			VIF_STATE_PRINT_INT(sta.hlid);
++			VIF_STATE_PRINT_INT(sta.basic_rate_idx);
++			VIF_STATE_PRINT_INT(sta.ap_rate_idx);
++			VIF_STATE_PRINT_INT(sta.p2p_rate_idx);
++			VIF_STATE_PRINT_INT(sta.qos);
++		} else {
++			VIF_STATE_PRINT_INT(ap.global_hlid);
++			VIF_STATE_PRINT_INT(ap.bcast_hlid);
++			VIF_STATE_PRINT_LHEX(ap.sta_hlid_map[0]);
++			VIF_STATE_PRINT_INT(ap.mgmt_rate_idx);
++			VIF_STATE_PRINT_INT(ap.bcast_rate_idx);
++			VIF_STATE_PRINT_INT(ap.ucast_rate_idx[0]);
++			VIF_STATE_PRINT_INT(ap.ucast_rate_idx[1]);
++			VIF_STATE_PRINT_INT(ap.ucast_rate_idx[2]);
++			VIF_STATE_PRINT_INT(ap.ucast_rate_idx[3]);
++		}
++		VIF_STATE_PRINT_INT(last_tx_hlid);
++		VIF_STATE_PRINT_INT(tx_queue_count[0]);
++		VIF_STATE_PRINT_INT(tx_queue_count[1]);
++		VIF_STATE_PRINT_INT(tx_queue_count[2]);
++		VIF_STATE_PRINT_INT(tx_queue_count[3]);
++		VIF_STATE_PRINT_LHEX(links_map[0]);
++		VIF_STATE_PRINT_NSTR(ssid, wlvif->ssid_len);
++		VIF_STATE_PRINT_INT(band);
++		VIF_STATE_PRINT_INT(channel);
++		VIF_STATE_PRINT_HEX(bitrate_masks[0]);
++		VIF_STATE_PRINT_HEX(bitrate_masks[1]);
++		VIF_STATE_PRINT_HEX(basic_rate_set);
++		VIF_STATE_PRINT_HEX(basic_rate);
++		VIF_STATE_PRINT_HEX(rate_set);
++		VIF_STATE_PRINT_INT(beacon_int);
++		VIF_STATE_PRINT_INT(default_key);
++		VIF_STATE_PRINT_INT(aid);
++		VIF_STATE_PRINT_INT(psm_entry_retry);
++		VIF_STATE_PRINT_INT(power_level);
++		VIF_STATE_PRINT_INT(rssi_thold);
++		VIF_STATE_PRINT_INT(last_rssi_event);
++		VIF_STATE_PRINT_INT(ba_support);
++		VIF_STATE_PRINT_INT(ba_allowed);
++		VIF_STATE_PRINT_LLHEX(total_freed_pkts);
++	}
++
++#undef VIF_STATE_PRINT_INT
++#undef VIF_STATE_PRINT_LONG
++#undef VIF_STATE_PRINT_HEX
++#undef VIF_STATE_PRINT_LHEX
++#undef VIF_STATE_PRINT_LLHEX
++#undef VIF_STATE_PRINT_STR
++#undef VIF_STATE_PRINT_NSTR
++#undef VIF_STATE_PRINT
++
++	mutex_unlock(&wl->mutex);
++
++	ret = simple_read_from_buffer(user_buf, count, ppos, buf, res);
++	kfree(buf);
++	return ret;
++}
++
++static const struct file_operations vifs_state_ops = {
++	.read = vifs_state_read,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static ssize_t dtim_interval_read(struct file *file, char __user *user_buf,
++				  size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	u8 value;
++
++	if (wl->conf.conn.wake_up_event == CONF_WAKE_UP_EVENT_DTIM ||
++	    wl->conf.conn.wake_up_event == CONF_WAKE_UP_EVENT_N_DTIM)
++		value = wl->conf.conn.listen_interval;
++	else
++		value = 0;
++
++	return wl1271_format_buffer(user_buf, count, ppos, "%d\n", value);
++}
++
++static ssize_t dtim_interval_write(struct file *file,
++				   const char __user *user_buf,
++				   size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	unsigned long value;
++	int ret;
++
++	ret = kstrtoul_from_user(user_buf, count, 10, &value);
++	if (ret < 0) {
++		wl1271_warning("illegal value for dtim_interval");
++		return -EINVAL;
++	}
++
++	if (value < 1 || value > 10) {
++		wl1271_warning("dtim value is not in valid range");
++		return -ERANGE;
++	}
++
++	mutex_lock(&wl->mutex);
++
++	wl->conf.conn.listen_interval = value;
++	/* for some reason there are different event types for 1 and >1 */
++	if (value == 1)
++		wl->conf.conn.wake_up_event = CONF_WAKE_UP_EVENT_DTIM;
++	else
++		wl->conf.conn.wake_up_event = CONF_WAKE_UP_EVENT_N_DTIM;
++
++	/*
++	 * we don't reconfigure ACX_WAKE_UP_CONDITIONS now, so it will only
++	 * take effect on the next time we enter psm.
++	 */
++	mutex_unlock(&wl->mutex);
++	return count;
++}
++
++static const struct file_operations dtim_interval_ops = {
++	.read = dtim_interval_read,
++	.write = dtim_interval_write,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++
++
++static ssize_t suspend_dtim_interval_read(struct file *file,
++					  char __user *user_buf,
++					  size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	u8 value;
++
++	if (wl->conf.conn.suspend_wake_up_event == CONF_WAKE_UP_EVENT_DTIM ||
++	    wl->conf.conn.suspend_wake_up_event == CONF_WAKE_UP_EVENT_N_DTIM)
++		value = wl->conf.conn.suspend_listen_interval;
++	else
++		value = 0;
++
++	return wl1271_format_buffer(user_buf, count, ppos, "%d\n", value);
++}
++
++static ssize_t suspend_dtim_interval_write(struct file *file,
++					   const char __user *user_buf,
++					   size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	unsigned long value;
++	int ret;
++
++	ret = kstrtoul_from_user(user_buf, count, 10, &value);
++	if (ret < 0) {
++		wl1271_warning("illegal value for suspend_dtim_interval");
++		return -EINVAL;
++	}
++
++	if (value < 1 || value > 10) {
++		wl1271_warning("suspend_dtim value is not in valid range");
++		return -ERANGE;
++	}
++
++	mutex_lock(&wl->mutex);
++
++	wl->conf.conn.suspend_listen_interval = value;
++	/* for some reason there are different event types for 1 and >1 */
++	if (value == 1)
++		wl->conf.conn.suspend_wake_up_event = CONF_WAKE_UP_EVENT_DTIM;
++	else
++		wl->conf.conn.suspend_wake_up_event = CONF_WAKE_UP_EVENT_N_DTIM;
++
++	mutex_unlock(&wl->mutex);
++	return count;
++}
++
++
++static const struct file_operations suspend_dtim_interval_ops = {
++	.read = suspend_dtim_interval_read,
++	.write = suspend_dtim_interval_write,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static ssize_t beacon_interval_read(struct file *file, char __user *user_buf,
++				    size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	u8 value;
++
++	if (wl->conf.conn.wake_up_event == CONF_WAKE_UP_EVENT_BEACON ||
++	    wl->conf.conn.wake_up_event == CONF_WAKE_UP_EVENT_N_BEACONS)
++		value = wl->conf.conn.listen_interval;
++	else
++		value = 0;
++
++	return wl1271_format_buffer(user_buf, count, ppos, "%d\n", value);
++}
++
++static ssize_t beacon_interval_write(struct file *file,
++				     const char __user *user_buf,
++				     size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	unsigned long value;
++	int ret;
++
++	ret = kstrtoul_from_user(user_buf, count, 10, &value);
++	if (ret < 0) {
++		wl1271_warning("illegal value for beacon_interval");
++		return -EINVAL;
++	}
++
++	if (value < 1 || value > 255) {
++		wl1271_warning("beacon interval value is not in valid range");
++		return -ERANGE;
++	}
++
++	mutex_lock(&wl->mutex);
++
++	wl->conf.conn.listen_interval = value;
++	/* for some reason there are different event types for 1 and >1 */
++	if (value == 1)
++		wl->conf.conn.wake_up_event = CONF_WAKE_UP_EVENT_BEACON;
++	else
++		wl->conf.conn.wake_up_event = CONF_WAKE_UP_EVENT_N_BEACONS;
++
++	/*
++	 * we don't reconfigure ACX_WAKE_UP_CONDITIONS now, so it will only
++	 * take effect on the next time we enter psm.
++	 */
++	mutex_unlock(&wl->mutex);
++	return count;
++}
++
++static const struct file_operations beacon_interval_ops = {
++	.read = beacon_interval_read,
++	.write = beacon_interval_write,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static ssize_t rx_streaming_interval_write(struct file *file,
++			   const char __user *user_buf,
++			   size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	struct wl12xx_vif *wlvif;
++	unsigned long value;
++	int ret;
++
++	ret = kstrtoul_from_user(user_buf, count, 10, &value);
++	if (ret < 0) {
++		wl1271_warning("illegal value in rx_streaming_interval!");
++		return -EINVAL;
++	}
++
++	/* valid values: 0, 10-100 */
++	if (value && (value < 10 || value > 100)) {
++		wl1271_warning("value is not in range!");
++		return -ERANGE;
++	}
++
++	mutex_lock(&wl->mutex);
++
++	wl->conf.rx_streaming.interval = value;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	wl12xx_for_each_wlvif_sta(wl, wlvif) {
++		wl1271_recalc_rx_streaming(wl, wlvif);
++	}
++
++	wl1271_ps_elp_sleep(wl);
++out:
++	mutex_unlock(&wl->mutex);
++	return count;
++}
++
++static ssize_t rx_streaming_interval_read(struct file *file,
++			    char __user *userbuf,
++			    size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	return wl1271_format_buffer(userbuf, count, ppos,
++				    "%d\n", wl->conf.rx_streaming.interval);
++}
++
++static const struct file_operations rx_streaming_interval_ops = {
++	.read = rx_streaming_interval_read,
++	.write = rx_streaming_interval_write,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static ssize_t rx_streaming_always_write(struct file *file,
++			   const char __user *user_buf,
++			   size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	struct wl12xx_vif *wlvif;
++	unsigned long value;
++	int ret;
++
++	ret = kstrtoul_from_user(user_buf, count, 10, &value);
++	if (ret < 0) {
++		wl1271_warning("illegal value in rx_streaming_write!");
++		return -EINVAL;
++	}
++
++	/* valid values: 0, 10-100 */
++	if (!(value == 0 || value == 1)) {
++		wl1271_warning("value is not in valid!");
++		return -EINVAL;
++	}
++
++	mutex_lock(&wl->mutex);
++
++	wl->conf.rx_streaming.always = value;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	wl12xx_for_each_wlvif_sta(wl, wlvif) {
++		wl1271_recalc_rx_streaming(wl, wlvif);
++	}
++
++	wl1271_ps_elp_sleep(wl);
++out:
++	mutex_unlock(&wl->mutex);
++	return count;
++}
++
++static ssize_t rx_streaming_always_read(struct file *file,
++			    char __user *userbuf,
++			    size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	return wl1271_format_buffer(userbuf, count, ppos,
++				    "%d\n", wl->conf.rx_streaming.always);
++}
++
++static const struct file_operations rx_streaming_always_ops = {
++	.read = rx_streaming_always_read,
++	.write = rx_streaming_always_write,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static ssize_t beacon_filtering_write(struct file *file,
++				      const char __user *user_buf,
++				      size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	struct wl12xx_vif *wlvif;
++	char buf[10];
++	size_t len;
++	unsigned long value;
++	int ret;
++
++	len = min(count, sizeof(buf) - 1);
++	if (copy_from_user(buf, user_buf, len))
++		return -EFAULT;
++	buf[len] = '\0';
++
++	ret = kstrtoul(buf, 0, &value);
++	if (ret < 0) {
++		wl1271_warning("illegal value for beacon_filtering!");
++		return -EINVAL;
++	}
++
++	mutex_lock(&wl->mutex);
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	wl12xx_for_each_wlvif(wl, wlvif) {
++		ret = wl1271_acx_beacon_filter_opt(wl, wlvif, !!value);
++	}
++
++	wl1271_ps_elp_sleep(wl);
++out:
++	mutex_unlock(&wl->mutex);
++	return count;
++}
++
++static const struct file_operations beacon_filtering_ops = {
++	.write = beacon_filtering_write,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static ssize_t fw_stats_raw_read(struct file *file,
++				 char __user *userbuf,
++				 size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++
++	wl1271_debugfs_update_stats(wl);
++
++	return simple_read_from_buffer(userbuf, count, ppos,
++				       wl->stats.fw_stats,
++				       wl->stats.fw_stats_len);
++}
++
++static const struct file_operations fw_stats_raw_ops = {
++	.read = fw_stats_raw_read,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static ssize_t sleep_auth_read(struct file *file, char __user *user_buf,
++			       size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++
++	return wl1271_format_buffer(user_buf, count,
++				    ppos, "%d\n",
++				    wl->sleep_auth);
++}
++
++static ssize_t sleep_auth_write(struct file *file,
++				const char __user *user_buf,
++				size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	unsigned long value;
++	int ret;
++
++	ret = kstrtoul_from_user(user_buf, count, 0, &value);
++	if (ret < 0) {
++		wl1271_warning("illegal value in sleep_auth");
++		return -EINVAL;
++	}
++
++	if (value > WL1271_PSM_MAX) {
++		wl1271_warning("sleep_auth must be between 0 and %d",
++			       WL1271_PSM_MAX);
++		return -ERANGE;
++	}
++
++	mutex_lock(&wl->mutex);
++
++	wl->conf.conn.sta_sleep_auth = value;
++
++	if (unlikely(wl->state != WLCORE_STATE_ON)) {
++		/* this will show up on "read" in case we are off */
++		wl->sleep_auth = value;
++		goto out;
++	}
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wl1271_acx_sleep_auth(wl, value);
++	if (ret < 0)
++		goto out_sleep;
++
++out_sleep:
++	wl1271_ps_elp_sleep(wl);
++out:
++	mutex_unlock(&wl->mutex);
++	return count;
++}
++
++static const struct file_operations sleep_auth_ops = {
++	.read = sleep_auth_read,
++	.write = sleep_auth_write,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
++static ssize_t dev_mem_read(struct file *file,
++	     char __user *user_buf, size_t count,
++	     loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	struct wlcore_partition_set part, old_part;
++	size_t bytes = count;
++	int ret;
++	char *buf;
++
++	/* only requests of dword-aligned size and offset are supported */
++	if (bytes % 4)
++		return -EINVAL;
++
++	if (*ppos % 4)
++		return -EINVAL;
++
++	/* function should return in reasonable time */
++	bytes = min(bytes, WLCORE_MAX_BLOCK_SIZE);
++
++	if (bytes == 0)
++		return -EINVAL;
++
++	memset(&part, 0, sizeof(part));
++	part.mem.start = file->f_pos;
++	part.mem.size = bytes;
++
++	buf = kmalloc(bytes, GFP_KERNEL);
++	if (!buf)
++		return -ENOMEM;
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state == WLCORE_STATE_OFF)) {
++		ret = -EFAULT;
++		goto skip_read;
++	}
++
++	/*
++	 * Don't fail if elp_wakeup returns an error, so the device's memory
++	 * could be read even if the FW crashed
++	 */
++	wl1271_ps_elp_wakeup(wl);
++
++	/* store current partition and switch partition */
++	memcpy(&old_part, &wl->curr_part, sizeof(old_part));
++	ret = wlcore_set_partition(wl, &part);
++	if (ret < 0)
++		goto part_err;
++
++	ret = wlcore_raw_read(wl, 0, buf, bytes, false);
++	if (ret < 0)
++		goto read_err;
++
++read_err:
++	/* recover partition */
++	ret = wlcore_set_partition(wl, &old_part);
++	if (ret < 0)
++		goto part_err;
++
++part_err:
++	wl1271_ps_elp_sleep(wl);
++
++skip_read:
++	mutex_unlock(&wl->mutex);
++
++	if (ret == 0) {
++		ret = copy_to_user(user_buf, buf, bytes);
++		if (ret < bytes) {
++			bytes -= ret;
++			*ppos += bytes;
++			ret = 0;
++		} else {
++			ret = -EFAULT;
++		}
++	}
++
++	kfree(buf);
++
++	return ((ret == 0) ? bytes : ret);
++}
++
++static ssize_t dev_mem_write(struct file *file, const char __user *user_buf,
++		size_t count, loff_t *ppos)
++{
++	struct wl1271 *wl = file->private_data;
++	struct wlcore_partition_set part, old_part;
++	size_t bytes = count;
++	int ret;
++	char *buf;
++
++	/* only requests of dword-aligned size and offset are supported */
++	if (bytes % 4)
++		return -EINVAL;
++
++	if (*ppos % 4)
++		return -EINVAL;
++
++	/* function should return in reasonable time */
++	bytes = min(bytes, WLCORE_MAX_BLOCK_SIZE);
++
++	if (bytes == 0)
++		return -EINVAL;
++
++	memset(&part, 0, sizeof(part));
++	part.mem.start = file->f_pos;
++	part.mem.size = bytes;
++
++	buf = kmalloc(bytes, GFP_KERNEL);
++	if (!buf)
++		return -ENOMEM;
++
++	ret = copy_from_user(buf, user_buf, bytes);
++	if (ret) {
++		ret = -EFAULT;
++		goto err_out;
++	}
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state == WLCORE_STATE_OFF)) {
++		ret = -EFAULT;
++		goto skip_write;
++	}
++
++	/*
++	 * Don't fail if elp_wakeup returns an error, so the device's memory
++	 * could be read even if the FW crashed
++	 */
++	wl1271_ps_elp_wakeup(wl);
++
++	/* store current partition and switch partition */
++	memcpy(&old_part, &wl->curr_part, sizeof(old_part));
++	ret = wlcore_set_partition(wl, &part);
++	if (ret < 0)
++		goto part_err;
++
++	ret = wlcore_raw_write(wl, 0, buf, bytes, false);
++	if (ret < 0)
++		goto write_err;
++
++write_err:
++	/* recover partition */
++	ret = wlcore_set_partition(wl, &old_part);
++	if (ret < 0)
++		goto part_err;
++
++part_err:
++	wl1271_ps_elp_sleep(wl);
++
++skip_write:
++	mutex_unlock(&wl->mutex);
++
++	if (ret == 0)
++		*ppos += bytes;
++
++err_out:
++	kfree(buf);
++
++	return ((ret == 0) ? bytes : ret);
++}
++
++static loff_t dev_mem_seek(struct file *file, loff_t offset, int orig)
++{
++	loff_t ret;
++
++	/* only requests of dword-aligned size and offset are supported */
++	if (offset % 4)
++		return -EINVAL;
++
++	switch (orig) {
++	case SEEK_SET:
++		file->f_pos = offset;
++		ret = file->f_pos;
++		break;
++	case SEEK_CUR:
++		file->f_pos += offset;
++		ret = file->f_pos;
++		break;
++	default:
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static const struct file_operations dev_mem_ops = {
++	.open = simple_open,
++	.read = dev_mem_read,
++	.write = dev_mem_write,
++	.llseek = dev_mem_seek,
++};
++
++static int wl1271_debugfs_add_files(struct wl1271 *wl,
++				    struct dentry *rootdir)
++{
++	int ret = 0;
++	struct dentry *entry, *streaming;
++
++	DEBUGFS_ADD(tx_queue_len, rootdir);
++	DEBUGFS_ADD(retry_count, rootdir);
++	DEBUGFS_ADD(excessive_retries, rootdir);
++
++	DEBUGFS_ADD(gpio_power, rootdir);
++	DEBUGFS_ADD(start_recovery, rootdir);
++	DEBUGFS_ADD(driver_state, rootdir);
++	DEBUGFS_ADD(vifs_state, rootdir);
++	DEBUGFS_ADD(dtim_interval, rootdir);
++	DEBUGFS_ADD(suspend_dtim_interval, rootdir);
++	DEBUGFS_ADD(beacon_interval, rootdir);
++	DEBUGFS_ADD(beacon_filtering, rootdir);
++	DEBUGFS_ADD(dynamic_ps_timeout, rootdir);
++	DEBUGFS_ADD(forced_ps, rootdir);
++	DEBUGFS_ADD(split_scan_timeout, rootdir);
++	DEBUGFS_ADD(irq_pkt_threshold, rootdir);
++	DEBUGFS_ADD(irq_blk_threshold, rootdir);
++	DEBUGFS_ADD(irq_timeout, rootdir);
++	DEBUGFS_ADD(fw_stats_raw, rootdir);
++	DEBUGFS_ADD(sleep_auth, rootdir);
++
++	streaming = debugfs_create_dir("rx_streaming", rootdir);
++	if (!streaming || IS_ERR(streaming))
++		goto err;
++
++	DEBUGFS_ADD_PREFIX(rx_streaming, interval, streaming);
++	DEBUGFS_ADD_PREFIX(rx_streaming, always, streaming);
++
++	DEBUGFS_ADD_PREFIX(dev, mem, rootdir);
++
++	return 0;
++
++err:
++	if (IS_ERR(entry))
++		ret = PTR_ERR(entry);
++	else
++		ret = -ENOMEM;
++
++	return ret;
++}
++
++void wl1271_debugfs_reset(struct wl1271 *wl)
++{
++	if (!wl->stats.fw_stats)
++		return;
++
++	memset(wl->stats.fw_stats, 0, wl->stats.fw_stats_len);
++	wl->stats.retry_count = 0;
++	wl->stats.excessive_retries = 0;
++}
++
++int wl1271_debugfs_init(struct wl1271 *wl)
++{
++	int ret;
++	struct dentry *rootdir;
++
++	rootdir = debugfs_create_dir(KBUILD_MODNAME,
++				     wl->hw->wiphy->debugfsdir);
++
++	if (IS_ERR(rootdir)) {
++		ret = PTR_ERR(rootdir);
++		goto out;
++	}
++
++	wl->stats.fw_stats = kzalloc(wl->stats.fw_stats_len, GFP_KERNEL);
++	if (!wl->stats.fw_stats) {
++		ret = -ENOMEM;
++		goto out_remove;
++	}
++
++	wl->stats.fw_stats_update = jiffies;
++
++	ret = wl1271_debugfs_add_files(wl, rootdir);
++	if (ret < 0)
++		goto out_exit;
++
++	ret = wlcore_debugfs_init(wl, rootdir);
++	if (ret < 0)
++		goto out_exit;
++
++	goto out;
++
++out_exit:
++	wl1271_debugfs_exit(wl);
++
++out_remove:
++	debugfs_remove_recursive(rootdir);
++
++out:
++	return ret;
++}
++
++void wl1271_debugfs_exit(struct wl1271 *wl)
++{
++	kfree(wl->stats.fw_stats);
++	wl->stats.fw_stats = NULL;
++}
+diff -Nuar git.old/drivers/net/wireless/ti/wlcore/main.c git/drivers/net/wireless/ti/wlcore/main.c
+--- git.old/drivers/net/wireless/ti/wlcore/main.c	2015-03-04 16:35:05.706185997 +0100
++++ git/drivers/net/wireless/ti/wlcore/main.c	2015-03-04 16:51:35.578176557 +0100
+@@ -34,6 +34,7 @@
+ #include <linux/wl12xx.h>
+ #include <linux/sched.h>
+ #include <linux/interrupt.h>
++#include <linux/irq.h>
+ 
+ #include "wlcore.h"
+ #include "debug.h"
+@@ -529,7 +530,7 @@
+ 	 * In case edge triggered interrupt must be used, we cannot iterate
+ 	 * more than once without introducing race conditions with the hardirq.
+ 	 */
+-	if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ)
++	if (wl->irq_flags & IRQF_TRIGGER_RISING)
+ 		loopcount = 1;
+ 
+ 	wl1271_debug(DEBUG_IRQ, "IRQ work");
+@@ -5894,7 +5895,6 @@
+ 	wl->ap_ps_map = 0;
+ 	wl->ap_fw_ps_map = 0;
+ 	wl->quirks = 0;
+-	wl->platform_quirks = 0;
+ 	wl->system_hlid = WL12XX_SYSTEM_HLID;
+ 	wl->active_sta_count = 0;
+ 	wl->active_link_count = 0;
+@@ -6024,7 +6024,7 @@
+ 	struct platform_device *pdev = wl->pdev;
+ 	struct wlcore_platdev_data *pdev_data = pdev->dev.platform_data;
+ 	struct wl12xx_platform_data *pdata = pdev_data->pdata;
+-	unsigned long irqflags;
++
+ 	int ret;
+ 
+ 	if (fw) {
+@@ -6051,16 +6051,15 @@
+ 	wlcore_adjust_conf(wl);
+ 
+ 	wl->irq = platform_get_irq(pdev, 0);
+-	wl->platform_quirks = pdata->platform_quirks;
+ 	wl->if_ops = pdev_data->if_ops;
+ 
+-	if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ)
+-		irqflags = IRQF_TRIGGER_RISING;
+-	else
+-		irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT;
++
++
++
++	wl->irq_flags |= IRQF_ONESHOT;
+ 
+ 	ret = request_threaded_irq(wl->irq, NULL, wlcore_irq,
+-				   irqflags, pdev->name, wl);
++				   wl->irq_flags, pdev->name, wl);
+ 	if (ret < 0) {
+ 		wl1271_error("request_irq() failed: %d", ret);
+ 		goto out_free_nvs;
+@@ -6071,7 +6070,7 @@
+ 	if (!ret) {
+ 		wl->irq_wake_enabled = true;
+ 		device_init_wakeup(wl->dev, 1);
+-		if (pdata->pwr_in_suspend) {
++		if (pdev_data->pwr_in_suspend) {
+ 			wl->hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY;
+ 			wl->hw->wiphy->wowlan.n_patterns =
+ 				WL1271_MAX_RX_FILTERS;
+diff -Nuar git.old/drivers/net/wireless/ti/wlcore/main.c.orig git/drivers/net/wireless/ti/wlcore/main.c.orig
+--- git.old/drivers/net/wireless/ti/wlcore/main.c.orig	1970-01-01 01:00:00.000000000 +0100
++++ git/drivers/net/wireless/ti/wlcore/main.c.orig	2015-03-04 16:46:03.618179723 +0100
+@@ -0,0 +1,6209 @@
++
++/*
++ * This file is part of wl1271
++ *
++ * Copyright (C) 2008-2010 Nokia Corporation
++ *
++ * Contact: Luciano Coelho <luciano.coelho at nokia.com>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++ * 02110-1301 USA
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/firmware.h>
++#include <linux/delay.h>
++#include <linux/spi/spi.h>
++#include <linux/crc32.h>
++#include <linux/etherdevice.h>
++#include <linux/vmalloc.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++#include <linux/wl12xx.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++
++#include "wlcore.h"
++#include "debug.h"
++#include "wl12xx_80211.h"
++#include "io.h"
++#include "event.h"
++#include "tx.h"
++#include "rx.h"
++#include "ps.h"
++#include "init.h"
++#include "debugfs.h"
++#include "cmd.h"
++#include "boot.h"
++#include "testmode.h"
++#include "scan.h"
++#include "hw_ops.h"
++
++#define WL1271_BOOT_RETRIES 3
++
++#define WL1271_BOOT_RETRIES 3
++
++static char *fwlog_param;
++static int bug_on_recovery = -1;
++static int no_recovery     = -1;
++
++static void __wl1271_op_remove_interface(struct wl1271 *wl,
++					 struct ieee80211_vif *vif,
++					 bool reset_tx_queues);
++static void wlcore_op_stop_locked(struct wl1271 *wl);
++static void wl1271_free_ap_keys(struct wl1271 *wl, struct wl12xx_vif *wlvif);
++
++static int wl12xx_set_authorized(struct wl1271 *wl,
++				 struct wl12xx_vif *wlvif)
++{
++	int ret;
++
++	if (WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS))
++		return -EINVAL;
++
++	if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
++		return 0;
++
++	if (test_and_set_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags))
++		return 0;
++
++	ret = wl12xx_cmd_set_peer_state(wl, wlvif, wlvif->sta.hlid);
++	if (ret < 0)
++		return ret;
++
++	wl1271_info("Association completed.");
++	return 0;
++}
++
++static void wl1271_reg_notify(struct wiphy *wiphy,
++			      struct regulatory_request *request)
++{
++	struct ieee80211_supported_band *band;
++	struct ieee80211_channel *ch;
++	int i;
++	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
++	struct wl1271 *wl = hw->priv;
++
++	band = wiphy->bands[IEEE80211_BAND_5GHZ];
++	for (i = 0; i < band->n_channels; i++) {
++		ch = &band->channels[i];
++		if (ch->flags & IEEE80211_CHAN_DISABLED)
++			continue;
++
++		if (ch->flags & IEEE80211_CHAN_RADAR)
++			ch->flags |= IEEE80211_CHAN_NO_IBSS |
++				     IEEE80211_CHAN_PASSIVE_SCAN;
++
++	}
++
++	wlcore_regdomain_config(wl);
++}
++
++static int wl1271_set_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif,
++				   bool enable)
++{
++	int ret = 0;
++
++	/* we should hold wl->mutex */
++	ret = wl1271_acx_ps_rx_streaming(wl, wlvif, enable);
++	if (ret < 0)
++		goto out;
++
++	if (enable)
++		set_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags);
++	else
++		clear_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags);
++out:
++	return ret;
++}
++
++/*
++ * this function is being called when the rx_streaming interval
++ * has beed changed or rx_streaming should be disabled
++ */
++int wl1271_recalc_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif)
++{
++	int ret = 0;
++	int period = wl->conf.rx_streaming.interval;
++
++	/* don't reconfigure if rx_streaming is disabled */
++	if (!test_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags))
++		goto out;
++
++	/* reconfigure/disable according to new streaming_period */
++	if (period &&
++	    test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) &&
++	    (wl->conf.rx_streaming.always ||
++	     test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags)))
++		ret = wl1271_set_rx_streaming(wl, wlvif, true);
++	else {
++		ret = wl1271_set_rx_streaming(wl, wlvif, false);
++		/* don't cancel_work_sync since we might deadlock */
++		del_timer_sync(&wlvif->rx_streaming_timer);
++	}
++out:
++	return ret;
++}
++
++static void wl1271_rx_streaming_enable_work(struct work_struct *work)
++{
++	int ret;
++	struct wl12xx_vif *wlvif = container_of(work, struct wl12xx_vif,
++						rx_streaming_enable_work);
++	struct wl1271 *wl = wlvif->wl;
++
++	mutex_lock(&wl->mutex);
++
++	if (test_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags) ||
++	    !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) ||
++	    (!wl->conf.rx_streaming.always &&
++	     !test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags)))
++		goto out;
++
++	if (!wl->conf.rx_streaming.interval)
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wl1271_set_rx_streaming(wl, wlvif, true);
++	if (ret < 0)
++		goto out_sleep;
++
++	/* stop it after some time of inactivity */
++	mod_timer(&wlvif->rx_streaming_timer,
++		  jiffies + msecs_to_jiffies(wl->conf.rx_streaming.duration));
++
++out_sleep:
++	wl1271_ps_elp_sleep(wl);
++out:
++	mutex_unlock(&wl->mutex);
++}
++
++static void wl1271_rx_streaming_disable_work(struct work_struct *work)
++{
++	int ret;
++	struct wl12xx_vif *wlvif = container_of(work, struct wl12xx_vif,
++						rx_streaming_disable_work);
++	struct wl1271 *wl = wlvif->wl;
++
++	mutex_lock(&wl->mutex);
++
++	if (!test_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wl1271_set_rx_streaming(wl, wlvif, false);
++	if (ret)
++		goto out_sleep;
++
++out_sleep:
++	wl1271_ps_elp_sleep(wl);
++out:
++	mutex_unlock(&wl->mutex);
++}
++
++static void wl1271_rx_streaming_timer(unsigned long data)
++{
++	struct wl12xx_vif *wlvif = (struct wl12xx_vif *)data;
++	struct wl1271 *wl = wlvif->wl;
++	ieee80211_queue_work(wl->hw, &wlvif->rx_streaming_disable_work);
++}
++
++/* wl->mutex must be taken */
++void wl12xx_rearm_tx_watchdog_locked(struct wl1271 *wl)
++{
++	/* if the watchdog is not armed, don't do anything */
++	if (wl->tx_allocated_blocks == 0)
++		return;
++
++	cancel_delayed_work(&wl->tx_watchdog_work);
++	ieee80211_queue_delayed_work(wl->hw, &wl->tx_watchdog_work,
++		msecs_to_jiffies(wl->conf.tx.tx_watchdog_timeout));
++}
++
++static void wl12xx_tx_watchdog_work(struct work_struct *work)
++{
++	struct delayed_work *dwork;
++	struct wl1271 *wl;
++
++	dwork = container_of(work, struct delayed_work, work);
++	wl = container_of(dwork, struct wl1271, tx_watchdog_work);
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	/* Tx went out in the meantime - everything is ok */
++	if (unlikely(wl->tx_allocated_blocks == 0))
++		goto out;
++
++	/*
++	 * if a ROC is in progress, we might not have any Tx for a long
++	 * time (e.g. pending Tx on the non-ROC channels)
++	 */
++	if (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES) {
++		wl1271_debug(DEBUG_TX, "No Tx (in FW) for %d ms due to ROC",
++			     wl->conf.tx.tx_watchdog_timeout);
++		wl12xx_rearm_tx_watchdog_locked(wl);
++		goto out;
++	}
++
++	/*
++	 * if a scan is in progress, we might not have any Tx for a long
++	 * time
++	 */
++	if (wl->scan.state != WL1271_SCAN_STATE_IDLE) {
++		wl1271_debug(DEBUG_TX, "No Tx (in FW) for %d ms due to scan",
++			     wl->conf.tx.tx_watchdog_timeout);
++		wl12xx_rearm_tx_watchdog_locked(wl);
++		goto out;
++	}
++
++	/*
++	* AP might cache a frame for a long time for a sleeping station,
++	* so rearm the timer if there's an AP interface with stations. If
++	* Tx is genuinely stuck we will most hopefully discover it when all
++	* stations are removed due to inactivity.
++	*/
++	if (wl->active_sta_count) {
++		wl1271_debug(DEBUG_TX, "No Tx (in FW) for %d ms. AP has "
++			     " %d stations",
++			      wl->conf.tx.tx_watchdog_timeout,
++			      wl->active_sta_count);
++		wl12xx_rearm_tx_watchdog_locked(wl);
++		goto out;
++	}
++
++	wl1271_error("Tx stuck (in FW) for %d ms. Starting recovery",
++		     wl->conf.tx.tx_watchdog_timeout);
++	wl12xx_queue_recovery_work(wl);
++
++out:
++	mutex_unlock(&wl->mutex);
++}
++
++static void wlcore_adjust_conf(struct wl1271 *wl)
++{
++	/* Adjust settings according to optional module parameters */
++
++	if (fwlog_param) {
++		if (!strcmp(fwlog_param, "continuous")) {
++			wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS;
++		} else if (!strcmp(fwlog_param, "ondemand")) {
++			wl->conf.fwlog.mode = WL12XX_FWLOG_ON_DEMAND;
++		} else if (!strcmp(fwlog_param, "dbgpins")) {
++			wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS;
++			wl->conf.fwlog.output = WL12XX_FWLOG_OUTPUT_DBG_PINS;
++		} else if (!strcmp(fwlog_param, "disable")) {
++			wl->conf.fwlog.mem_blocks = 0;
++			wl->conf.fwlog.output = WL12XX_FWLOG_OUTPUT_NONE;
++		} else {
++			wl1271_error("Unknown fwlog parameter %s", fwlog_param);
++		}
++	}
++
++	if (bug_on_recovery != -1)
++		wl->conf.recovery.bug_on_recovery = (u8) bug_on_recovery;
++
++	if (no_recovery != -1)
++		wl->conf.recovery.no_recovery = (u8) no_recovery;
++}
++
++static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl,
++					struct wl12xx_vif *wlvif,
++					u8 hlid, u8 tx_pkts)
++{
++	bool fw_ps;
++
++	fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
++
++	/*
++	 * Wake up from high level PS if the STA is asleep with too little
++	 * packets in FW or if the STA is awake.
++	 */
++	if (!fw_ps || tx_pkts < WL1271_PS_STA_MAX_PACKETS)
++		wl12xx_ps_link_end(wl, wlvif, hlid);
++
++	/*
++	 * Start high-level PS if the STA is asleep with enough blocks in FW.
++	 * Make an exception if this is the only connected link. In this
++	 * case FW-memory congestion is less of a problem.
++	 * Note that a single connected STA means 3 active links, since we must
++	 * account for the global and broadcast AP links. The "fw_ps" check
++	 * assures us the third link is a STA connected to the AP. Otherwise
++	 * the FW would not set the PSM bit.
++	 */
++	else if (wl->active_link_count > 3 && fw_ps &&
++		 tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
++		wl12xx_ps_link_start(wl, wlvif, hlid, true);
++}
++
++static void wl12xx_irq_update_links_status(struct wl1271 *wl,
++					   struct wl12xx_vif *wlvif,
++					   struct wl_fw_status_2 *status)
++{
++	u32 cur_fw_ps_map;
++	u8 hlid;
++
++	cur_fw_ps_map = le32_to_cpu(status->link_ps_bitmap);
++	if (wl->ap_fw_ps_map != cur_fw_ps_map) {
++		wl1271_debug(DEBUG_PSM,
++			     "link ps prev 0x%x cur 0x%x changed 0x%x",
++			     wl->ap_fw_ps_map, cur_fw_ps_map,
++			     wl->ap_fw_ps_map ^ cur_fw_ps_map);
++
++		wl->ap_fw_ps_map = cur_fw_ps_map;
++	}
++
++	for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, WL12XX_MAX_LINKS)
++		wl12xx_irq_ps_regulate_link(wl, wlvif, hlid,
++					    wl->links[hlid].allocated_pkts);
++}
++
++static int wlcore_fw_status(struct wl1271 *wl,
++			    struct wl_fw_status_1 *status_1,
++			    struct wl_fw_status_2 *status_2)
++{
++	struct wl12xx_vif *wlvif;
++	struct timespec ts;
++	u32 old_tx_blk_count = wl->tx_blocks_available;
++	int avail, freed_blocks;
++	int i;
++	size_t status_len;
++	int ret;
++	struct wl1271_link *lnk;
++
++	status_len = WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
++		sizeof(*status_2) + wl->fw_status_priv_len;
++
++	ret = wlcore_raw_read_data(wl, REG_RAW_FW_STATUS_ADDR, status_1,
++				   status_len, false);
++	if (ret < 0)
++		return ret;
++
++	wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, "
++		     "drv_rx_counter = %d, tx_results_counter = %d)",
++		     status_1->intr,
++		     status_1->fw_rx_counter,
++		     status_1->drv_rx_counter,
++		     status_1->tx_results_counter);
++
++	for (i = 0; i < NUM_TX_QUEUES; i++) {
++		/* prevent wrap-around in freed-packets counter */
++		wl->tx_allocated_pkts[i] -=
++				(status_2->counters.tx_released_pkts[i] -
++				wl->tx_pkts_freed[i]) & 0xff;
++
++		wl->tx_pkts_freed[i] = status_2->counters.tx_released_pkts[i];
++	}
++
++
++	for_each_set_bit(i, wl->links_map, WL12XX_MAX_LINKS) {
++		u8 diff;
++		lnk = &wl->links[i];
++
++		/* prevent wrap-around in freed-packets counter */
++		diff = (status_2->counters.tx_lnk_free_pkts[i] -
++		       lnk->prev_freed_pkts) & 0xff;
++
++		if (diff == 0)
++			continue;
++
++		lnk->allocated_pkts -= diff;
++		lnk->prev_freed_pkts = status_2->counters.tx_lnk_free_pkts[i];
++
++		/* accumulate the prev_freed_pkts counter */
++		lnk->total_freed_pkts += diff;
++	}
++
++	/* prevent wrap-around in total blocks counter */
++	if (likely(wl->tx_blocks_freed <=
++		   le32_to_cpu(status_2->total_released_blks)))
++		freed_blocks = le32_to_cpu(status_2->total_released_blks) -
++			       wl->tx_blocks_freed;
++	else
++		freed_blocks = 0x100000000LL - wl->tx_blocks_freed +
++			       le32_to_cpu(status_2->total_released_blks);
++
++	wl->tx_blocks_freed = le32_to_cpu(status_2->total_released_blks);
++
++	wl->tx_allocated_blocks -= freed_blocks;
++
++	/*
++	 * If the FW freed some blocks:
++	 * If we still have allocated blocks - re-arm the timer, Tx is
++	 * not stuck. Otherwise, cancel the timer (no Tx currently).
++	 */
++	if (freed_blocks) {
++		if (wl->tx_allocated_blocks)
++			wl12xx_rearm_tx_watchdog_locked(wl);
++		else
++			cancel_delayed_work(&wl->tx_watchdog_work);
++	}
++
++	avail = le32_to_cpu(status_2->tx_total) - wl->tx_allocated_blocks;
++
++	/*
++	 * The FW might change the total number of TX memblocks before
++	 * we get a notification about blocks being released. Thus, the
++	 * available blocks calculation might yield a temporary result
++	 * which is lower than the actual available blocks. Keeping in
++	 * mind that only blocks that were allocated can be moved from
++	 * TX to RX, tx_blocks_available should never decrease here.
++	 */
++	wl->tx_blocks_available = max((int)wl->tx_blocks_available,
++				      avail);
++
++	/* if more blocks are available now, tx work can be scheduled */
++	if (wl->tx_blocks_available > old_tx_blk_count)
++		clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags);
++
++	/* for AP update num of allocated TX blocks per link and ps status */
++	wl12xx_for_each_wlvif_ap(wl, wlvif) {
++		wl12xx_irq_update_links_status(wl, wlvif, status_2);
++	}
++
++	/* update the host-chipset time offset */
++	getnstimeofday(&ts);
++	wl->time_offset = (timespec_to_ns(&ts) >> 10) -
++		(s64)le32_to_cpu(status_2->fw_localtime);
++
++	wl->fw_fast_lnk_map = le32_to_cpu(status_2->link_fast_bitmap);
++
++	return 0;
++}
++
++static void wl1271_flush_deferred_work(struct wl1271 *wl)
++{
++	struct sk_buff *skb;
++
++	/* Pass all received frames to the network stack */
++	while ((skb = skb_dequeue(&wl->deferred_rx_queue)))
++		ieee80211_rx_ni(wl->hw, skb);
++
++	/* Return sent skbs to the network stack */
++	while ((skb = skb_dequeue(&wl->deferred_tx_queue)))
++		ieee80211_tx_status_ni(wl->hw, skb);
++}
++
++static void wl1271_netstack_work(struct work_struct *work)
++{
++	struct wl1271 *wl =
++		container_of(work, struct wl1271, netstack_work);
++
++	do {
++		wl1271_flush_deferred_work(wl);
++	} while (skb_queue_len(&wl->deferred_rx_queue));
++}
++
++#define WL1271_IRQ_MAX_LOOPS 256
++
++static int wlcore_irq_locked(struct wl1271 *wl)
++{
++	int ret = 0;
++	u32 intr;
++	int loopcount = WL1271_IRQ_MAX_LOOPS;
++	bool done = false;
++	unsigned int defer_count;
++	unsigned long flags;
++
++	/*
++	 * In case edge triggered interrupt must be used, we cannot iterate
++	 * more than once without introducing race conditions with the hardirq.
++	 */
++	if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ)
++		loopcount = 1;
++
++	wl1271_debug(DEBUG_IRQ, "IRQ work");
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	while (!done && loopcount--) {
++		/*
++		 * In order to avoid a race with the hardirq, clear the flag
++		 * before acknowledging the chip. Since the mutex is held,
++		 * wl1271_ps_elp_wakeup cannot be called concurrently.
++		 */
++		clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
++		smp_mb__after_clear_bit();
++
++		ret = wlcore_fw_status(wl, wl->fw_status_1, wl->fw_status_2);
++		if (ret < 0)
++			goto out;
++
++		wlcore_hw_tx_immediate_compl(wl);
++
++		intr = le32_to_cpu(wl->fw_status_1->intr);
++		intr &= WLCORE_ALL_INTR_MASK;
++		if (!intr) {
++			done = true;
++			continue;
++		}
++
++		if (unlikely(intr & WL1271_ACX_INTR_WATCHDOG)) {
++			wl1271_error("HW watchdog interrupt received! starting recovery.");
++			wl->watchdog_recovery = true;
++			ret = -EIO;
++
++			/* restarting the chip. ignore any other interrupt. */
++			goto out;
++		}
++
++		if (unlikely(intr & WL1271_ACX_SW_INTR_WATCHDOG)) {
++			wl1271_error("SW watchdog interrupt received! "
++				     "starting recovery.");
++			wl->watchdog_recovery = true;
++			ret = -EIO;
++
++			/* restarting the chip. ignore any other interrupt. */
++			goto out;
++		}
++
++		if (likely(intr & WL1271_ACX_INTR_DATA)) {
++			wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA");
++
++			ret = wlcore_rx(wl, wl->fw_status_1);
++			if (ret < 0)
++				goto out;
++
++			/* Check if any tx blocks were freed */
++			spin_lock_irqsave(&wl->wl_lock, flags);
++			if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags) &&
++			    wl1271_tx_total_queue_count(wl) > 0) {
++				spin_unlock_irqrestore(&wl->wl_lock, flags);
++				/*
++				 * In order to avoid starvation of the TX path,
++				 * call the work function directly.
++				 */
++				ret = wlcore_tx_work_locked(wl);
++				if (ret < 0)
++					goto out;
++			} else {
++				spin_unlock_irqrestore(&wl->wl_lock, flags);
++			}
++
++			/* check for tx results */
++			ret = wlcore_hw_tx_delayed_compl(wl);
++			if (ret < 0)
++				goto out;
++
++			/* Make sure the deferred queues don't get too long */
++			defer_count = skb_queue_len(&wl->deferred_tx_queue) +
++				      skb_queue_len(&wl->deferred_rx_queue);
++			if (defer_count > WL1271_DEFERRED_QUEUE_LIMIT)
++				wl1271_flush_deferred_work(wl);
++		}
++
++		if (intr & WL1271_ACX_INTR_EVENT_A) {
++			wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_A");
++			ret = wl1271_event_handle(wl, 0);
++			if (ret < 0)
++				goto out;
++		}
++
++		if (intr & WL1271_ACX_INTR_EVENT_B) {
++			wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_B");
++			ret = wl1271_event_handle(wl, 1);
++			if (ret < 0)
++				goto out;
++		}
++
++		if (intr & WL1271_ACX_INTR_INIT_COMPLETE)
++			wl1271_debug(DEBUG_IRQ,
++				     "WL1271_ACX_INTR_INIT_COMPLETE");
++
++		if (intr & WL1271_ACX_INTR_HW_AVAILABLE)
++			wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE");
++	}
++
++	wl1271_ps_elp_sleep(wl);
++
++out:
++	return ret;
++}
++
++static irqreturn_t wlcore_irq(int irq, void *cookie)
++{
++	int ret;
++	unsigned long flags;
++	struct wl1271 *wl = cookie;
++
++	/* complete the ELP completion */
++	spin_lock_irqsave(&wl->wl_lock, flags);
++	set_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
++	if (wl->elp_compl) {
++		complete(wl->elp_compl);
++		wl->elp_compl = NULL;
++	}
++
++	if (test_bit(WL1271_FLAG_SUSPENDED, &wl->flags)) {
++		/* don't enqueue a work right now. mark it as pending */
++		set_bit(WL1271_FLAG_PENDING_WORK, &wl->flags);
++		wl1271_debug(DEBUG_IRQ, "should not enqueue work");
++		disable_irq_nosync(wl->irq);
++		pm_wakeup_event(wl->dev, 0);
++		spin_unlock_irqrestore(&wl->wl_lock, flags);
++		return IRQ_HANDLED;
++	}
++	spin_unlock_irqrestore(&wl->wl_lock, flags);
++
++	/* TX might be handled here, avoid redundant work */
++	set_bit(WL1271_FLAG_TX_PENDING, &wl->flags);
++	cancel_work_sync(&wl->tx_work);
++
++	mutex_lock(&wl->mutex);
++
++	ret = wlcore_irq_locked(wl);
++	if (ret)
++		wl12xx_queue_recovery_work(wl);
++
++	spin_lock_irqsave(&wl->wl_lock, flags);
++	/* In case TX was not handled here, queue TX work */
++	clear_bit(WL1271_FLAG_TX_PENDING, &wl->flags);
++	if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags) &&
++	    wl1271_tx_total_queue_count(wl) > 0)
++		ieee80211_queue_work(wl->hw, &wl->tx_work);
++	spin_unlock_irqrestore(&wl->wl_lock, flags);
++
++	mutex_unlock(&wl->mutex);
++
++	return IRQ_HANDLED;
++}
++
++struct vif_counter_data {
++	u8 counter;
++
++	struct ieee80211_vif *cur_vif;
++	bool cur_vif_running;
++};
++
++static void wl12xx_vif_count_iter(void *data, u8 *mac,
++				  struct ieee80211_vif *vif)
++{
++	struct vif_counter_data *counter = data;
++
++	counter->counter++;
++	if (counter->cur_vif == vif)
++		counter->cur_vif_running = true;
++}
++
++/* caller must not hold wl->mutex, as it might deadlock */
++static void wl12xx_get_vif_count(struct ieee80211_hw *hw,
++			       struct ieee80211_vif *cur_vif,
++			       struct vif_counter_data *data)
++{
++	memset(data, 0, sizeof(*data));
++	data->cur_vif = cur_vif;
++
++	ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
++					    wl12xx_vif_count_iter, data);
++}
++
++static int wl12xx_fetch_firmware(struct wl1271 *wl, bool plt)
++{
++	const struct firmware *fw;
++	const char *fw_name;
++	enum wl12xx_fw_type fw_type;
++	int ret;
++
++	if (plt) {
++		fw_type = WL12XX_FW_TYPE_PLT;
++		fw_name = wl->plt_fw_name;
++	} else {
++		/*
++		 * we can't call wl12xx_get_vif_count() here because
++		 * wl->mutex is taken, so use the cached last_vif_count value
++		 */
++		if (wl->last_vif_count > 1 && wl->mr_fw_name) {
++			fw_type = WL12XX_FW_TYPE_MULTI;
++			fw_name = wl->mr_fw_name;
++		} else {
++			fw_type = WL12XX_FW_TYPE_NORMAL;
++			fw_name = wl->sr_fw_name;
++		}
++	}
++
++	if (wl->fw_type == fw_type)
++		return 0;
++
++	wl1271_debug(DEBUG_BOOT, "booting firmware %s", fw_name);
++
++	ret = request_firmware(&fw, fw_name, wl->dev);
++
++	if (ret < 0) {
++		wl1271_error("could not get firmware %s: %d", fw_name, ret);
++		return ret;
++	}
++
++	if (fw->size % 4) {
++		wl1271_error("firmware size is not multiple of 32 bits: %zu",
++			     fw->size);
++		ret = -EILSEQ;
++		goto out;
++	}
++
++	vfree(wl->fw);
++	wl->fw_type = WL12XX_FW_TYPE_NONE;
++	wl->fw_len = fw->size;
++	wl->fw = vmalloc(wl->fw_len);
++
++	if (!wl->fw) {
++		wl1271_error("could not allocate memory for the firmware");
++		ret = -ENOMEM;
++		goto out;
++	}
++
++	memcpy(wl->fw, fw->data, wl->fw_len);
++	ret = 0;
++	wl->fw_type = fw_type;
++out:
++	release_firmware(fw);
++
++	return ret;
++}
++
++void wl12xx_queue_recovery_work(struct wl1271 *wl)
++{
++	WARN_ON(!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags));
++
++	/* Avoid a recursive recovery */
++	if (wl->state == WLCORE_STATE_ON) {
++		wl->state = WLCORE_STATE_RESTARTING;
++		set_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags);
++		wlcore_disable_interrupts_nosync(wl);
++		ieee80211_queue_work(wl->hw, &wl->recovery_work);
++	}
++}
++
++size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen)
++{
++	size_t len = 0;
++
++	/* The FW log is a length-value list, find where the log end */
++	while (len < maxlen) {
++		if (memblock[len] == 0)
++			break;
++		if (len + memblock[len] + 1 > maxlen)
++			break;
++		len += memblock[len] + 1;
++	}
++
++	/* Make sure we have enough room */
++	len = min(len, (size_t)(PAGE_SIZE - wl->fwlog_size));
++
++	/* Fill the FW log file, consumed by the sysfs fwlog entry */
++	memcpy(wl->fwlog + wl->fwlog_size, memblock, len);
++	wl->fwlog_size += len;
++
++	return len;
++}
++
++#define WLCORE_FW_LOG_END 0x2000000
++
++static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
++{
++	u32 addr;
++	u32 offset;
++	u32 end_of_log;
++	u8 *block;
++	int ret;
++
++	if ((wl->quirks & WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED) ||
++	    (wl->conf.fwlog.mem_blocks == 0))
++		return;
++
++	wl1271_info("Reading FW panic log");
++
++	block = kmalloc(WL12XX_HW_BLOCK_SIZE, GFP_KERNEL);
++	if (!block)
++		return;
++
++	/*
++	 * Make sure the chip is awake and the logger isn't active.
++	 * Do not send a stop fwlog command if the fw is hanged or if
++	 * dbgpins are used (due to some fw bug).
++	 */
++	if (wl1271_ps_elp_wakeup(wl))
++		goto out;
++	if (!wl->watchdog_recovery &&
++	    wl->conf.fwlog.output != WL12XX_FWLOG_OUTPUT_DBG_PINS)
++		wl12xx_cmd_stop_fwlog(wl);
++
++	/* Read the first memory block address */
++	ret = wlcore_fw_status(wl, wl->fw_status_1, wl->fw_status_2);
++	if (ret < 0)
++		goto out;
++
++	addr = le32_to_cpu(wl->fw_status_2->log_start_addr);
++	if (!addr)
++		goto out;
++
++	if (wl->conf.fwlog.mode == WL12XX_FWLOG_CONTINUOUS) {
++		offset = sizeof(addr) + sizeof(struct wl1271_rx_descriptor);
++		end_of_log = WLCORE_FW_LOG_END;
++	} else {
++		offset = sizeof(addr);
++		end_of_log = addr;
++	}
++
++	/* Traverse the memory blocks linked list */
++	do {
++		memset(block, 0, WL12XX_HW_BLOCK_SIZE);
++		ret = wlcore_read_hwaddr(wl, addr, block, WL12XX_HW_BLOCK_SIZE,
++					 false);
++		if (ret < 0)
++			goto out;
++
++		/*
++		 * Memory blocks are linked to one another. The first 4 bytes
++		 * of each memory block hold the hardware address of the next
++		 * one. The last memory block points to the first one in
++		 * on demand mode and is equal to 0x2000000 in continuous mode.
++		 */
++		addr = le32_to_cpup((__le32 *)block);
++		if (!wl12xx_copy_fwlog(wl, block + offset,
++				       WL12XX_HW_BLOCK_SIZE - offset))
++			break;
++	} while (addr && (addr != end_of_log));
++
++	wake_up_interruptible(&wl->fwlog_waitq);
++
++out:
++	kfree(block);
++}
++
++static void wlcore_print_recovery(struct wl1271 *wl)
++{
++	u32 pc = 0;
++	u32 hint_sts = 0;
++	int ret;
++
++	wl1271_info("Hardware recovery in progress. FW ver: %s",
++		    wl->chip.fw_ver_str);
++
++	/* change partitions momentarily so we can read the FW pc */
++	ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]);
++	if (ret < 0)
++		return;
++
++	ret = wlcore_read_reg(wl, REG_PC_ON_RECOVERY, &pc);
++	if (ret < 0)
++		return;
++
++	ret = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR, &hint_sts);
++	if (ret < 0)
++		return;
++
++	wl1271_info("pc: 0x%x, hint_sts: 0x%08x count: %d",
++				pc, hint_sts, ++wl->recovery_count);
++
++	wlcore_set_partition(wl, &wl->ptable[PART_WORK]);
++}
++
++
++static void wl1271_recovery_work(struct work_struct *work)
++{
++	struct wl1271 *wl =
++		container_of(work, struct wl1271, recovery_work);
++	struct wl12xx_vif *wlvif;
++	struct ieee80211_vif *vif;
++
++	mutex_lock(&wl->mutex);
++
++	if (wl->state == WLCORE_STATE_OFF || wl->plt)
++		goto out_unlock;
++
++	if (!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags)) {
++		wl12xx_read_fwlog_panic(wl);
++		wlcore_print_recovery(wl);
++	}
++
++	BUG_ON(wl->conf.recovery.bug_on_recovery &&
++	       !test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags));
++
++	if (wl->conf.recovery.no_recovery) {
++		wl1271_info("No recovery (chosen on module load). Fw will remain stuck.");
++		goto out_unlock;
++	}
++
++	/* Prevent spurious TX during FW restart */
++	wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_FW_RESTART);
++
++	/* reboot the chipset */
++	while (!list_empty(&wl->wlvif_list)) {
++		wlvif = list_first_entry(&wl->wlvif_list,
++				       struct wl12xx_vif, list);
++		vif = wl12xx_wlvif_to_vif(wlvif);
++		__wl1271_op_remove_interface(wl, vif, false);
++	}
++
++	wlcore_op_stop_locked(wl);
++
++	ieee80211_restart_hw(wl->hw);
++
++	/*
++	 * Its safe to enable TX now - the queues are stopped after a request
++	 * to restart the HW.
++	 */
++	wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_FW_RESTART);
++
++out_unlock:
++	wl->watchdog_recovery = false;
++	clear_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags);
++	mutex_unlock(&wl->mutex);
++}
++
++static int wlcore_fw_wakeup(struct wl1271 *wl)
++{
++	return wlcore_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG, ELPCTRL_WAKE_UP);
++}
++
++static int wl1271_setup(struct wl1271 *wl)
++{
++	wl->fw_status_1 = kmalloc(WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
++				  sizeof(*wl->fw_status_2) +
++				  wl->fw_status_priv_len, GFP_KERNEL);
++	if (!wl->fw_status_1)
++		return -ENOMEM;
++
++	wl->fw_status_2 = (struct wl_fw_status_2 *)
++				(((u8 *) wl->fw_status_1) +
++				WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc));
++
++	wl->tx_res_if = kmalloc(sizeof(*wl->tx_res_if), GFP_KERNEL);
++	if (!wl->tx_res_if) {
++		kfree(wl->fw_status_1);
++		return -ENOMEM;
++	}
++
++	return 0;
++}
++
++static int wl12xx_set_power_on(struct wl1271 *wl)
++{
++	int ret;
++
++	msleep(WL1271_PRE_POWER_ON_SLEEP);
++	ret = wl1271_power_on(wl);
++	if (ret < 0)
++		goto out;
++	msleep(WL1271_POWER_ON_SLEEP);
++	wl1271_io_reset(wl);
++	wl1271_io_init(wl);
++
++	ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]);
++	if (ret < 0)
++		goto fail;
++
++	/* ELP module wake up */
++	ret = wlcore_fw_wakeup(wl);
++	if (ret < 0)
++		goto fail;
++
++out:
++	return ret;
++
++fail:
++	wl1271_power_off(wl);
++	return ret;
++}
++
++static int wl12xx_chip_wakeup(struct wl1271 *wl, bool plt)
++{
++	int ret = 0;
++
++	ret = wl12xx_set_power_on(wl);
++	if (ret < 0)
++		goto out;
++
++	/*
++	 * For wl127x based devices we could use the default block
++	 * size (512 bytes), but due to a bug in the sdio driver, we
++	 * need to set it explicitly after the chip is powered on.  To
++	 * simplify the code and since the performance impact is
++	 * negligible, we use the same block size for all different
++	 * chip types.
++	 *
++	 * Check if the bus supports blocksize alignment and, if it
++	 * doesn't, make sure we don't have the quirk.
++	 */
++	if (!wl1271_set_block_size(wl))
++		wl->quirks &= ~WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN;
++
++	/* TODO: make sure the lower driver has set things up correctly */
++
++	ret = wl1271_setup(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wl12xx_fetch_firmware(wl, plt);
++	if (ret < 0)
++		goto out;
++
++out:
++	return ret;
++}
++
++int wl1271_plt_start(struct wl1271 *wl, const enum plt_mode plt_mode)
++{
++	int retries = WL1271_BOOT_RETRIES;
++	struct wiphy *wiphy = wl->hw->wiphy;
++
++	static const char* const PLT_MODE[] = {
++		"PLT_OFF",
++		"PLT_ON",
++		"PLT_FEM_DETECT"
++	};
++
++	int ret;
++
++	mutex_lock(&wl->mutex);
++
++	wl1271_notice("power up");
++
++	if (wl->state != WLCORE_STATE_OFF) {
++		wl1271_error("cannot go into PLT state because not "
++			     "in off state: %d", wl->state);
++		ret = -EBUSY;
++		goto out;
++	}
++
++	/* Indicate to lower levels that we are now in PLT mode */
++	wl->plt = true;
++	wl->plt_mode = plt_mode;
++
++	while (retries) {
++		retries--;
++		ret = wl12xx_chip_wakeup(wl, true);
++		if (ret < 0)
++			goto power_off;
++
++		ret = wl->ops->plt_init(wl);
++		if (ret < 0)
++			goto power_off;
++
++		wl->state = WLCORE_STATE_ON;
++		wl1271_notice("firmware booted in PLT mode %s (%s)",
++			      PLT_MODE[plt_mode],
++			      wl->chip.fw_ver_str);
++
++		/* update hw/fw version info in wiphy struct */
++		wiphy->hw_version = wl->chip.id;
++		strncpy(wiphy->fw_version, wl->chip.fw_ver_str,
++			sizeof(wiphy->fw_version));
++
++		goto out;
++
++power_off:
++		wl1271_power_off(wl);
++	}
++
++	wl->plt = false;
++	wl->plt_mode = PLT_OFF;
++
++	wl1271_error("firmware boot in PLT mode failed despite %d retries",
++		     WL1271_BOOT_RETRIES);
++out:
++	mutex_unlock(&wl->mutex);
++
++	return ret;
++}
++
++int wl1271_plt_stop(struct wl1271 *wl)
++{
++	int ret = 0;
++
++	wl1271_notice("power down");
++
++	/*
++	 * Interrupts must be disabled before setting the state to OFF.
++	 * Otherwise, the interrupt handler might be called and exit without
++	 * reading the interrupt status.
++	 */
++	wlcore_disable_interrupts(wl);
++	mutex_lock(&wl->mutex);
++	if (!wl->plt) {
++		mutex_unlock(&wl->mutex);
++
++		/*
++		 * This will not necessarily enable interrupts as interrupts
++		 * may have been disabled when op_stop was called. It will,
++		 * however, balance the above call to disable_interrupts().
++		 */
++		wlcore_enable_interrupts(wl);
++
++		wl1271_error("cannot power down because not in PLT "
++			     "state: %d", wl->state);
++		ret = -EBUSY;
++		goto out;
++	}
++
++	mutex_unlock(&wl->mutex);
++
++	wl1271_flush_deferred_work(wl);
++	cancel_work_sync(&wl->netstack_work);
++	cancel_work_sync(&wl->recovery_work);
++	cancel_delayed_work_sync(&wl->elp_work);
++	cancel_delayed_work_sync(&wl->tx_watchdog_work);
++
++	mutex_lock(&wl->mutex);
++	wl1271_power_off(wl);
++	wl->flags = 0;
++	wl->sleep_auth = WL1271_PSM_ILLEGAL;
++	wl->state = WLCORE_STATE_OFF;
++	wl->plt = false;
++	wl->plt_mode = PLT_OFF;
++	wl->rx_counter = 0;
++	mutex_unlock(&wl->mutex);
++
++out:
++	return ret;
++}
++
++static void wl1271_op_tx(struct ieee80211_hw *hw,
++			 struct ieee80211_tx_control *control,
++			 struct sk_buff *skb)
++{
++	struct wl1271 *wl = hw->priv;
++	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
++	struct ieee80211_vif *vif = info->control.vif;
++	struct wl12xx_vif *wlvif = NULL;
++	unsigned long flags;
++	int q, mapping;
++	u8 hlid;
++
++	if (!vif) {
++		wl1271_debug(DEBUG_TX, "DROP skb with no vif");
++		ieee80211_free_txskb(hw, skb);
++		return;
++	}
++
++	wlvif = wl12xx_vif_to_data(vif);
++	mapping = skb_get_queue_mapping(skb);
++	q = wl1271_tx_get_queue(mapping);
++
++	hlid = wl12xx_tx_get_hlid(wl, wlvif, skb, control->sta);
++
++	spin_lock_irqsave(&wl->wl_lock, flags);
++
++	/*
++	 * drop the packet if the link is invalid or the queue is stopped
++	 * for any reason but watermark. Watermark is a "soft"-stop so we
++	 * allow these packets through.
++	 */
++	if (hlid == WL12XX_INVALID_LINK_ID ||
++	    (!test_bit(hlid, wlvif->links_map)) ||
++	     (wlcore_is_queue_stopped_locked(wl, wlvif, q) &&
++	      !wlcore_is_queue_stopped_by_reason_locked(wl, wlvif, q,
++			WLCORE_QUEUE_STOP_REASON_WATERMARK))) {
++		wl1271_debug(DEBUG_TX, "DROP skb hlid %d q %d", hlid, q);
++		ieee80211_free_txskb(hw, skb);
++		goto out;
++	}
++
++	wl1271_debug(DEBUG_TX, "queue skb hlid %d q %d len %d",
++		     hlid, q, skb->len);
++	skb_queue_tail(&wl->links[hlid].tx_queue[q], skb);
++
++	wl->tx_queue_count[q]++;
++	wlvif->tx_queue_count[q]++;
++
++	/*
++	 * The workqueue is slow to process the tx_queue and we need stop
++	 * the queue here, otherwise the queue will get too long.
++	 */
++	if (wlvif->tx_queue_count[q] >= WL1271_TX_QUEUE_HIGH_WATERMARK &&
++	    !wlcore_is_queue_stopped_by_reason_locked(wl, wlvif, q,
++					WLCORE_QUEUE_STOP_REASON_WATERMARK)) {
++		wl1271_debug(DEBUG_TX, "op_tx: stopping queues for q %d", q);
++		wlcore_stop_queue_locked(wl, wlvif, q,
++					 WLCORE_QUEUE_STOP_REASON_WATERMARK);
++	}
++
++	/*
++	 * The chip specific setup must run before the first TX packet -
++	 * before that, the tx_work will not be initialized!
++	 */
++
++	if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags) &&
++	    !test_bit(WL1271_FLAG_TX_PENDING, &wl->flags))
++		ieee80211_queue_work(wl->hw, &wl->tx_work);
++
++out:
++	spin_unlock_irqrestore(&wl->wl_lock, flags);
++}
++
++int wl1271_tx_dummy_packet(struct wl1271 *wl)
++{
++	unsigned long flags;
++	int q;
++
++	/* no need to queue a new dummy packet if one is already pending */
++	if (test_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags))
++		return 0;
++
++	q = wl1271_tx_get_queue(skb_get_queue_mapping(wl->dummy_packet));
++
++	spin_lock_irqsave(&wl->wl_lock, flags);
++	set_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags);
++	wl->tx_queue_count[q]++;
++	spin_unlock_irqrestore(&wl->wl_lock, flags);
++
++	/* The FW is low on RX memory blocks, so send the dummy packet asap */
++	if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags))
++		return wlcore_tx_work_locked(wl);
++
++	/*
++	 * If the FW TX is busy, TX work will be scheduled by the threaded
++	 * interrupt handler function
++	 */
++	return 0;
++}
++
++/*
++ * The size of the dummy packet should be at least 1400 bytes. However, in
++ * order to minimize the number of bus transactions, aligning it to 512 bytes
++ * boundaries could be beneficial, performance wise
++ */
++#define TOTAL_TX_DUMMY_PACKET_SIZE (ALIGN(1400, 512))
++
++static struct sk_buff *wl12xx_alloc_dummy_packet(struct wl1271 *wl)
++{
++	struct sk_buff *skb;
++	struct ieee80211_hdr_3addr *hdr;
++	unsigned int dummy_packet_size;
++
++	dummy_packet_size = TOTAL_TX_DUMMY_PACKET_SIZE -
++			    sizeof(struct wl1271_tx_hw_descr) - sizeof(*hdr);
++
++	skb = dev_alloc_skb(TOTAL_TX_DUMMY_PACKET_SIZE);
++	if (!skb) {
++		wl1271_warning("Failed to allocate a dummy packet skb");
++		return NULL;
++	}
++
++	skb_reserve(skb, sizeof(struct wl1271_tx_hw_descr));
++
++	hdr = (struct ieee80211_hdr_3addr *) skb_put(skb, sizeof(*hdr));
++	memset(hdr, 0, sizeof(*hdr));
++	hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
++					 IEEE80211_STYPE_NULLFUNC |
++					 IEEE80211_FCTL_TODS);
++
++	memset(skb_put(skb, dummy_packet_size), 0, dummy_packet_size);
++
++	/* Dummy packets require the TID to be management */
++	skb->priority = WL1271_TID_MGMT;
++
++	/* Initialize all fields that might be used */
++	skb_set_queue_mapping(skb, 0);
++	memset(IEEE80211_SKB_CB(skb), 0, sizeof(struct ieee80211_tx_info));
++
++	return skb;
++}
++
++
++#ifdef CONFIG_PM
++static int
++wl1271_validate_wowlan_pattern(struct cfg80211_wowlan_trig_pkt_pattern *p)
++{
++	int num_fields = 0, in_field = 0, fields_size = 0;
++	int i, pattern_len = 0;
++
++	if (!p->mask) {
++		wl1271_warning("No mask in WoWLAN pattern");
++		return -EINVAL;
++	}
++
++	/*
++	 * The pattern is broken up into segments of bytes at different offsets
++	 * that need to be checked by the FW filter. Each segment is called
++	 * a field in the FW API. We verify that the total number of fields
++	 * required for this pattern won't exceed FW limits (8)
++	 * as well as the total fields buffer won't exceed the FW limit.
++	 * Note that if there's a pattern which crosses Ethernet/IP header
++	 * boundary a new field is required.
++	 */
++	for (i = 0; i < p->pattern_len; i++) {
++		if (test_bit(i, (unsigned long *)p->mask)) {
++			if (!in_field) {
++				in_field = 1;
++				pattern_len = 1;
++			} else {
++				if (i == WL1271_RX_FILTER_ETH_HEADER_SIZE) {
++					num_fields++;
++					fields_size += pattern_len +
++						RX_FILTER_FIELD_OVERHEAD;
++					pattern_len = 1;
++				} else
++					pattern_len++;
++			}
++		} else {
++			if (in_field) {
++				in_field = 0;
++				fields_size += pattern_len +
++					RX_FILTER_FIELD_OVERHEAD;
++				num_fields++;
++			}
++		}
++	}
++
++	if (in_field) {
++		fields_size += pattern_len + RX_FILTER_FIELD_OVERHEAD;
++		num_fields++;
++	}
++
++	if (num_fields > WL1271_RX_FILTER_MAX_FIELDS) {
++		wl1271_warning("RX Filter too complex. Too many segments");
++		return -EINVAL;
++	}
++
++	if (fields_size > WL1271_RX_FILTER_MAX_FIELDS_SIZE) {
++		wl1271_warning("RX filter pattern is too big");
++		return -E2BIG;
++	}
++
++	return 0;
++}
++
++struct wl12xx_rx_filter *wl1271_rx_filter_alloc(void)
++{
++	return kzalloc(sizeof(struct wl12xx_rx_filter), GFP_KERNEL);
++}
++
++void wl1271_rx_filter_free(struct wl12xx_rx_filter *filter)
++{
++	int i;
++
++	if (filter == NULL)
++		return;
++
++	for (i = 0; i < filter->num_fields; i++)
++		kfree(filter->fields[i].pattern);
++
++	kfree(filter);
++}
++
++int wl1271_rx_filter_alloc_field(struct wl12xx_rx_filter *filter,
++				 u16 offset, u8 flags,
++				 u8 *pattern, u8 len)
++{
++	struct wl12xx_rx_filter_field *field;
++
++	if (filter->num_fields == WL1271_RX_FILTER_MAX_FIELDS) {
++		wl1271_warning("Max fields per RX filter. can't alloc another");
++		return -EINVAL;
++	}
++
++	field = &filter->fields[filter->num_fields];
++
++	field->pattern = kzalloc(len, GFP_KERNEL);
++	if (!field->pattern) {
++		wl1271_warning("Failed to allocate RX filter pattern");
++		return -ENOMEM;
++	}
++
++	filter->num_fields++;
++
++	field->offset = cpu_to_le16(offset);
++	field->flags = flags;
++	field->len = len;
++	memcpy(field->pattern, pattern, len);
++
++	return 0;
++}
++
++int wl1271_rx_filter_get_fields_size(struct wl12xx_rx_filter *filter)
++{
++	int i, fields_size = 0;
++
++	for (i = 0; i < filter->num_fields; i++)
++		fields_size += filter->fields[i].len +
++			sizeof(struct wl12xx_rx_filter_field) -
++			sizeof(u8 *);
++
++	return fields_size;
++}
++
++void wl1271_rx_filter_flatten_fields(struct wl12xx_rx_filter *filter,
++				    u8 *buf)
++{
++	int i;
++	struct wl12xx_rx_filter_field *field;
++
++	for (i = 0; i < filter->num_fields; i++) {
++		field = (struct wl12xx_rx_filter_field *)buf;
++
++		field->offset = filter->fields[i].offset;
++		field->flags = filter->fields[i].flags;
++		field->len = filter->fields[i].len;
++
++		memcpy(&field->pattern, filter->fields[i].pattern, field->len);
++		buf += sizeof(struct wl12xx_rx_filter_field) -
++			sizeof(u8 *) + field->len;
++	}
++}
++
++/*
++ * Allocates an RX filter returned through f
++ * which needs to be freed using rx_filter_free()
++ */
++static int wl1271_convert_wowlan_pattern_to_rx_filter(
++	struct cfg80211_wowlan_trig_pkt_pattern *p,
++	struct wl12xx_rx_filter **f)
++{
++	int i, j, ret = 0;
++	struct wl12xx_rx_filter *filter;
++	u16 offset;
++	u8 flags, len;
++
++	filter = wl1271_rx_filter_alloc();
++	if (!filter) {
++		wl1271_warning("Failed to alloc rx filter");
++		ret = -ENOMEM;
++		goto err;
++	}
++
++	i = 0;
++	while (i < p->pattern_len) {
++		if (!test_bit(i, (unsigned long *)p->mask)) {
++			i++;
++			continue;
++		}
++
++		for (j = i; j < p->pattern_len; j++) {
++			if (!test_bit(j, (unsigned long *)p->mask))
++				break;
++
++			if (i < WL1271_RX_FILTER_ETH_HEADER_SIZE &&
++			    j >= WL1271_RX_FILTER_ETH_HEADER_SIZE)
++				break;
++		}
++
++		if (i < WL1271_RX_FILTER_ETH_HEADER_SIZE) {
++			offset = i;
++			flags = WL1271_RX_FILTER_FLAG_ETHERNET_HEADER;
++		} else {
++			offset = i - WL1271_RX_FILTER_ETH_HEADER_SIZE;
++			flags = WL1271_RX_FILTER_FLAG_IP_HEADER;
++		}
++
++		len = j - i;
++
++		ret = wl1271_rx_filter_alloc_field(filter,
++						   offset,
++						   flags,
++						   &p->pattern[i], len);
++		if (ret)
++			goto err;
++
++		i = j;
++	}
++
++	filter->action = FILTER_SIGNAL;
++
++	*f = filter;
++	return 0;
++
++err:
++	wl1271_rx_filter_free(filter);
++	*f = NULL;
++
++	return ret;
++}
++
++static int wl1271_configure_wowlan(struct wl1271 *wl,
++				   struct cfg80211_wowlan *wow)
++{
++	int i, ret;
++
++	if (!wow || wow->any || !wow->n_patterns) {
++		ret = wl1271_acx_default_rx_filter_enable(wl, 0,
++							  FILTER_SIGNAL);
++		if (ret)
++			goto out;
++
++		ret = wl1271_rx_filter_clear_all(wl);
++		if (ret)
++			goto out;
++
++		return 0;
++	}
++
++	if (WARN_ON(wow->n_patterns > WL1271_MAX_RX_FILTERS))
++		return -EINVAL;
++
++	/* Validate all incoming patterns before clearing current FW state */
++	for (i = 0; i < wow->n_patterns; i++) {
++		ret = wl1271_validate_wowlan_pattern(&wow->patterns[i]);
++		if (ret) {
++			wl1271_warning("Bad wowlan pattern %d", i);
++			return ret;
++		}
++	}
++
++	ret = wl1271_acx_default_rx_filter_enable(wl, 0, FILTER_SIGNAL);
++	if (ret)
++		goto out;
++
++	ret = wl1271_rx_filter_clear_all(wl);
++	if (ret)
++		goto out;
++
++	/* Translate WoWLAN patterns into filters */
++	for (i = 0; i < wow->n_patterns; i++) {
++		struct cfg80211_wowlan_trig_pkt_pattern *p;
++		struct wl12xx_rx_filter *filter = NULL;
++
++		p = &wow->patterns[i];
++
++		ret = wl1271_convert_wowlan_pattern_to_rx_filter(p, &filter);
++		if (ret) {
++			wl1271_warning("Failed to create an RX filter from "
++				       "wowlan pattern %d", i);
++			goto out;
++		}
++
++		ret = wl1271_rx_filter_enable(wl, i, 1, filter);
++
++		wl1271_rx_filter_free(filter);
++		if (ret)
++			goto out;
++	}
++
++	ret = wl1271_acx_default_rx_filter_enable(wl, 1, FILTER_DROP);
++
++out:
++	return ret;
++}
++
++static int wl1271_configure_suspend_sta(struct wl1271 *wl,
++					struct wl12xx_vif *wlvif,
++					struct cfg80211_wowlan *wow)
++{
++	int ret = 0;
++
++	if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wl1271_configure_wowlan(wl, wow);
++	if (ret < 0)
++		goto out_sleep;
++
++	if ((wl->conf.conn.suspend_wake_up_event ==
++	     wl->conf.conn.wake_up_event) &&
++	    (wl->conf.conn.suspend_listen_interval ==
++	     wl->conf.conn.listen_interval))
++		goto out_sleep;
++
++	ret = wl1271_acx_wake_up_conditions(wl, wlvif,
++				    wl->conf.conn.suspend_wake_up_event,
++				    wl->conf.conn.suspend_listen_interval);
++
++	if (ret < 0)
++		wl1271_error("suspend: set wake up conditions failed: %d", ret);
++
++out_sleep:
++	wl1271_ps_elp_sleep(wl);
++out:
++	return ret;
++
++}
++
++static int wl1271_configure_suspend_ap(struct wl1271 *wl,
++				       struct wl12xx_vif *wlvif)
++{
++	int ret = 0;
++
++	if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true);
++
++	wl1271_ps_elp_sleep(wl);
++out:
++	return ret;
++
++}
++
++static int wl1271_configure_suspend(struct wl1271 *wl,
++				    struct wl12xx_vif *wlvif,
++				    struct cfg80211_wowlan *wow)
++{
++	if (wlvif->bss_type == BSS_TYPE_STA_BSS)
++		return wl1271_configure_suspend_sta(wl, wlvif, wow);
++	if (wlvif->bss_type == BSS_TYPE_AP_BSS)
++		return wl1271_configure_suspend_ap(wl, wlvif);
++	return 0;
++}
++
++static void wl1271_configure_resume(struct wl1271 *wl,
++				    struct wl12xx_vif *wlvif)
++{
++	int ret = 0;
++	bool is_ap = wlvif->bss_type == BSS_TYPE_AP_BSS;
++	bool is_sta = wlvif->bss_type == BSS_TYPE_STA_BSS;
++
++	if ((!is_ap) && (!is_sta))
++		return;
++
++	if (is_sta && !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
++		return;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		return;
++
++	if (is_sta) {
++		wl1271_configure_wowlan(wl, NULL);
++
++		if ((wl->conf.conn.suspend_wake_up_event ==
++		     wl->conf.conn.wake_up_event) &&
++		    (wl->conf.conn.suspend_listen_interval ==
++		     wl->conf.conn.listen_interval))
++			goto out_sleep;
++
++		ret = wl1271_acx_wake_up_conditions(wl, wlvif,
++				    wl->conf.conn.wake_up_event,
++				    wl->conf.conn.listen_interval);
++
++		if (ret < 0)
++			wl1271_error("resume: wake up conditions failed: %d",
++				     ret);
++
++	} else if (is_ap) {
++		ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false);
++	}
++
++out_sleep:
++	wl1271_ps_elp_sleep(wl);
++}
++
++static int wl1271_op_suspend(struct ieee80211_hw *hw,
++			    struct cfg80211_wowlan *wow)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif;
++	int ret;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 suspend wow=%d", !!wow);
++	WARN_ON(!wow);
++
++	/* we want to perform the recovery before suspending */
++	if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) {
++		wl1271_warning("postponing suspend to perform recovery");
++		return -EBUSY;
++	}
++
++	wl1271_tx_flush(wl);
++
++	mutex_lock(&wl->mutex);
++	wl->wow_enabled = true;
++	wl12xx_for_each_wlvif(wl, wlvif) {
++		ret = wl1271_configure_suspend(wl, wlvif, wow);
++		if (ret < 0) {
++			mutex_unlock(&wl->mutex);
++			wl1271_warning("couldn't prepare device to suspend");
++			return ret;
++		}
++	}
++	mutex_unlock(&wl->mutex);
++	/* flush any remaining work */
++	wl1271_debug(DEBUG_MAC80211, "flushing remaining works");
++
++	/*
++	 * disable and re-enable interrupts in order to flush
++	 * the threaded_irq
++	 */
++	wlcore_disable_interrupts(wl);
++
++	/*
++	 * set suspended flag to avoid triggering a new threaded_irq
++	 * work. no need for spinlock as interrupts are disabled.
++	 */
++	set_bit(WL1271_FLAG_SUSPENDED, &wl->flags);
++
++	wlcore_enable_interrupts(wl);
++	flush_work(&wl->tx_work);
++	flush_delayed_work(&wl->elp_work);
++
++	return 0;
++}
++
++static int wl1271_op_resume(struct ieee80211_hw *hw)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif;
++	unsigned long flags;
++	bool run_irq_work = false, pending_recovery;
++	int ret;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 resume wow=%d",
++		     wl->wow_enabled);
++	WARN_ON(!wl->wow_enabled);
++
++	/*
++	 * re-enable irq_work enqueuing, and call irq_work directly if
++	 * there is a pending work.
++	 */
++	spin_lock_irqsave(&wl->wl_lock, flags);
++	clear_bit(WL1271_FLAG_SUSPENDED, &wl->flags);
++	if (test_and_clear_bit(WL1271_FLAG_PENDING_WORK, &wl->flags))
++		run_irq_work = true;
++	spin_unlock_irqrestore(&wl->wl_lock, flags);
++
++	mutex_lock(&wl->mutex);
++
++	/* test the recovery flag before calling any SDIO functions */
++	pending_recovery = test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS,
++				    &wl->flags);
++
++	if (run_irq_work) {
++		wl1271_debug(DEBUG_MAC80211,
++			     "run postponed irq_work directly");
++
++		/* don't talk to the HW if recovery is pending */
++		if (!pending_recovery) {
++			ret = wlcore_irq_locked(wl);
++			if (ret)
++				wl12xx_queue_recovery_work(wl);
++		}
++
++		wlcore_enable_interrupts(wl);
++	}
++
++	if (pending_recovery) {
++		wl1271_warning("queuing forgotten recovery on resume");
++		ieee80211_queue_work(wl->hw, &wl->recovery_work);
++		goto out;
++	}
++
++	wl12xx_for_each_wlvif(wl, wlvif) {
++		wl1271_configure_resume(wl, wlvif);
++	}
++
++out:
++	wl->wow_enabled = false;
++	mutex_unlock(&wl->mutex);
++
++	return 0;
++}
++#endif
++
++static int wl1271_op_start(struct ieee80211_hw *hw)
++{
++	wl1271_debug(DEBUG_MAC80211, "mac80211 start");
++
++	/*
++	 * We have to delay the booting of the hardware because
++	 * we need to know the local MAC address before downloading and
++	 * initializing the firmware. The MAC address cannot be changed
++	 * after boot, and without the proper MAC address, the firmware
++	 * will not function properly.
++	 *
++	 * The MAC address is first known when the corresponding interface
++	 * is added. That is where we will initialize the hardware.
++	 */
++
++	return 0;
++}
++
++static void wlcore_op_stop_locked(struct wl1271 *wl)
++{
++	int i;
++
++	if (wl->state == WLCORE_STATE_OFF) {
++		if (test_and_clear_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS,
++					&wl->flags))
++			wlcore_enable_interrupts(wl);
++
++		return;
++	}
++
++	/*
++	 * this must be before the cancel_work calls below, so that the work
++	 * functions don't perform further work.
++	 */
++	wl->state = WLCORE_STATE_OFF;
++
++	/*
++	 * Use the nosync variant to disable interrupts, so the mutex could be
++	 * held while doing so without deadlocking.
++	 */
++	wlcore_disable_interrupts_nosync(wl);
++
++	mutex_unlock(&wl->mutex);
++
++	wlcore_synchronize_interrupts(wl);
++	if (!test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags))
++		cancel_work_sync(&wl->recovery_work);
++	wl1271_flush_deferred_work(wl);
++	cancel_delayed_work_sync(&wl->scan_complete_work);
++	cancel_work_sync(&wl->netstack_work);
++	cancel_work_sync(&wl->tx_work);
++	cancel_delayed_work_sync(&wl->elp_work);
++	cancel_delayed_work_sync(&wl->tx_watchdog_work);
++
++	/* let's notify MAC80211 about the remaining pending TX frames */
++	mutex_lock(&wl->mutex);
++	wl12xx_tx_reset(wl);
++
++	wl1271_power_off(wl);
++	/*
++	 * In case a recovery was scheduled, interrupts were disabled to avoid
++	 * an interrupt storm. Now that the power is down, it is safe to
++	 * re-enable interrupts to balance the disable depth
++	 */
++	if (test_and_clear_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags))
++		wlcore_enable_interrupts(wl);
++
++	wl->band = IEEE80211_BAND_2GHZ;
++
++	wl->rx_counter = 0;
++	wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
++	wl->channel_type = NL80211_CHAN_NO_HT;
++	wl->tx_blocks_available = 0;
++	wl->tx_allocated_blocks = 0;
++	wl->tx_results_count = 0;
++	wl->tx_packets_count = 0;
++	wl->time_offset = 0;
++	wl->ap_fw_ps_map = 0;
++	wl->ap_ps_map = 0;
++	wl->sleep_auth = WL1271_PSM_ILLEGAL;
++	memset(wl->roles_map, 0, sizeof(wl->roles_map));
++	memset(wl->links_map, 0, sizeof(wl->links_map));
++	memset(wl->roc_map, 0, sizeof(wl->roc_map));
++	memset(wl->session_ids, 0, sizeof(wl->session_ids));
++	wl->active_sta_count = 0;
++	wl->active_link_count = 0;
++
++	/* The system link is always allocated */
++	wl->links[WL12XX_SYSTEM_HLID].allocated_pkts = 0;
++	wl->links[WL12XX_SYSTEM_HLID].prev_freed_pkts = 0;
++	__set_bit(WL12XX_SYSTEM_HLID, wl->links_map);
++
++	/*
++	 * this is performed after the cancel_work calls and the associated
++	 * mutex_lock, so that wl1271_op_add_interface does not accidentally
++	 * get executed before all these vars have been reset.
++	 */
++	wl->flags = 0;
++
++	wl->tx_blocks_freed = 0;
++
++	for (i = 0; i < NUM_TX_QUEUES; i++) {
++		wl->tx_pkts_freed[i] = 0;
++		wl->tx_allocated_pkts[i] = 0;
++	}
++
++	wl1271_debugfs_reset(wl);
++
++	kfree(wl->fw_status_1);
++	wl->fw_status_1 = NULL;
++	wl->fw_status_2 = NULL;
++	kfree(wl->tx_res_if);
++	wl->tx_res_if = NULL;
++	kfree(wl->target_mem_map);
++	wl->target_mem_map = NULL;
++
++	/*
++	 * FW channels must be re-calibrated after recovery,
++	 * clear the last Reg-Domain channel configuration.
++	 */
++	memset(wl->reg_ch_conf_last, 0, sizeof(wl->reg_ch_conf_last));
++}
++
++static void wlcore_op_stop(struct ieee80211_hw *hw)
++{
++	struct wl1271 *wl = hw->priv;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 stop");
++
++	mutex_lock(&wl->mutex);
++
++	wlcore_op_stop_locked(wl);
++
++	mutex_unlock(&wl->mutex);
++}
++
++static void wlcore_channel_switch_work(struct work_struct *work)
++{
++	struct delayed_work *dwork;
++	struct wl1271 *wl;
++	struct ieee80211_vif *vif;
++	struct wl12xx_vif *wlvif;
++	int ret;
++
++	dwork = container_of(work, struct delayed_work, work);
++	wlvif = container_of(dwork, struct wl12xx_vif, channel_switch_work);
++	wl = wlvif->wl;
++
++	wl1271_info("channel switch failed (role_id: %d).", wlvif->role_id);
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	/* check the channel switch is still ongoing */
++	if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags))
++		goto out;
++
++	vif = wl12xx_wlvif_to_vif(wlvif);
++	ieee80211_chswitch_done(vif, false);
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	wl12xx_cmd_stop_channel_switch(wl, wlvif);
++
++	wl1271_ps_elp_sleep(wl);
++out:
++	mutex_unlock(&wl->mutex);
++}
++
++static void wlcore_connection_loss_work(struct work_struct *work)
++{
++	struct delayed_work *dwork;
++	struct wl1271 *wl;
++	struct ieee80211_vif *vif;
++	struct wl12xx_vif *wlvif;
++
++	dwork = container_of(work, struct delayed_work, work);
++	wlvif = container_of(dwork, struct wl12xx_vif, connection_loss_work);
++	wl = wlvif->wl;
++
++	wl1271_info("Connection loss work (role_id: %d).", wlvif->role_id);
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	/* Call mac80211 connection loss */
++	if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
++		goto out;
++
++	vif = wl12xx_wlvif_to_vif(wlvif);
++	ieee80211_connection_loss(vif);
++out:
++	mutex_unlock(&wl->mutex);
++}
++
++static int wl12xx_allocate_rate_policy(struct wl1271 *wl, u8 *idx)
++{
++	u8 policy = find_first_zero_bit(wl->rate_policies_map,
++					WL12XX_MAX_RATE_POLICIES);
++	if (policy >= WL12XX_MAX_RATE_POLICIES)
++		return -EBUSY;
++
++	__set_bit(policy, wl->rate_policies_map);
++	*idx = policy;
++	return 0;
++}
++
++static void wl12xx_free_rate_policy(struct wl1271 *wl, u8 *idx)
++{
++	if (WARN_ON(*idx >= WL12XX_MAX_RATE_POLICIES))
++		return;
++
++	__clear_bit(*idx, wl->rate_policies_map);
++	*idx = WL12XX_MAX_RATE_POLICIES;
++}
++
++static int wlcore_allocate_klv_template(struct wl1271 *wl, u8 *idx)
++{
++	u8 policy = find_first_zero_bit(wl->klv_templates_map,
++					WLCORE_MAX_KLV_TEMPLATES);
++	if (policy >= WLCORE_MAX_KLV_TEMPLATES)
++		return -EBUSY;
++
++	__set_bit(policy, wl->klv_templates_map);
++	*idx = policy;
++	return 0;
++}
++
++static void wlcore_free_klv_template(struct wl1271 *wl, u8 *idx)
++{
++	if (WARN_ON(*idx >= WLCORE_MAX_KLV_TEMPLATES))
++		return;
++
++	__clear_bit(*idx, wl->klv_templates_map);
++	*idx = WLCORE_MAX_KLV_TEMPLATES;
++}
++
++static u8 wl12xx_get_role_type(struct wl1271 *wl, struct wl12xx_vif *wlvif)
++{
++	switch (wlvif->bss_type) {
++	case BSS_TYPE_AP_BSS:
++		if (wlvif->p2p)
++			return WL1271_ROLE_P2P_GO;
++		else
++			return WL1271_ROLE_AP;
++
++	case BSS_TYPE_STA_BSS:
++		if (wlvif->p2p)
++			return WL1271_ROLE_P2P_CL;
++		else
++			return WL1271_ROLE_STA;
++
++	case BSS_TYPE_IBSS:
++		return WL1271_ROLE_IBSS;
++
++	default:
++		wl1271_error("invalid bss_type: %d", wlvif->bss_type);
++	}
++	return WL12XX_INVALID_ROLE_TYPE;
++}
++
++static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
++{
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	int i;
++
++	/* clear everything but the persistent data */
++	memset(wlvif, 0, offsetof(struct wl12xx_vif, persistent));
++
++	switch (ieee80211_vif_type_p2p(vif)) {
++	case NL80211_IFTYPE_P2P_CLIENT:
++		wlvif->p2p = 1;
++		/* fall-through */
++	case NL80211_IFTYPE_STATION:
++		wlvif->bss_type = BSS_TYPE_STA_BSS;
++		break;
++	case NL80211_IFTYPE_ADHOC:
++		wlvif->bss_type = BSS_TYPE_IBSS;
++		break;
++	case NL80211_IFTYPE_P2P_GO:
++		wlvif->p2p = 1;
++		/* fall-through */
++	case NL80211_IFTYPE_AP:
++		wlvif->bss_type = BSS_TYPE_AP_BSS;
++		break;
++	default:
++		wlvif->bss_type = MAX_BSS_TYPE;
++		return -EOPNOTSUPP;
++	}
++
++	wlvif->role_id = WL12XX_INVALID_ROLE_ID;
++	wlvif->dev_role_id = WL12XX_INVALID_ROLE_ID;
++	wlvif->dev_hlid = WL12XX_INVALID_LINK_ID;
++
++	if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
++	    wlvif->bss_type == BSS_TYPE_IBSS) {
++		/* init sta/ibss data */
++		wlvif->sta.hlid = WL12XX_INVALID_LINK_ID;
++		wl12xx_allocate_rate_policy(wl, &wlvif->sta.basic_rate_idx);
++		wl12xx_allocate_rate_policy(wl, &wlvif->sta.ap_rate_idx);
++		wl12xx_allocate_rate_policy(wl, &wlvif->sta.p2p_rate_idx);
++		wlcore_allocate_klv_template(wl, &wlvif->sta.klv_template_id);
++		wlvif->basic_rate_set = CONF_TX_RATE_MASK_BASIC;
++		wlvif->basic_rate = CONF_TX_RATE_MASK_BASIC;
++		wlvif->rate_set = CONF_TX_RATE_MASK_BASIC;
++	} else {
++		/* init ap data */
++		wlvif->ap.bcast_hlid = WL12XX_INVALID_LINK_ID;
++		wlvif->ap.global_hlid = WL12XX_INVALID_LINK_ID;
++		wl12xx_allocate_rate_policy(wl, &wlvif->ap.mgmt_rate_idx);
++		wl12xx_allocate_rate_policy(wl, &wlvif->ap.bcast_rate_idx);
++		for (i = 0; i < CONF_TX_MAX_AC_COUNT; i++)
++			wl12xx_allocate_rate_policy(wl,
++						&wlvif->ap.ucast_rate_idx[i]);
++		wlvif->basic_rate_set = CONF_TX_ENABLED_RATES;
++		/*
++		 * TODO: check if basic_rate shouldn't be
++		 * wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
++		 * instead (the same thing for STA above).
++		*/
++		wlvif->basic_rate = CONF_TX_ENABLED_RATES;
++		/* TODO: this seems to be used only for STA, check it */
++		wlvif->rate_set = CONF_TX_ENABLED_RATES;
++	}
++
++	wlvif->bitrate_masks[IEEE80211_BAND_2GHZ] = wl->conf.tx.basic_rate;
++	wlvif->bitrate_masks[IEEE80211_BAND_5GHZ] = wl->conf.tx.basic_rate_5;
++	wlvif->beacon_int = WL1271_DEFAULT_BEACON_INT;
++
++	/*
++	 * mac80211 configures some values globally, while we treat them
++	 * per-interface. thus, on init, we have to copy them from wl
++	 */
++	wlvif->band = wl->band;
++	wlvif->channel = wl->channel;
++	wlvif->power_level = wl->power_level;
++	wlvif->channel_type = wl->channel_type;
++
++	INIT_WORK(&wlvif->rx_streaming_enable_work,
++		  wl1271_rx_streaming_enable_work);
++	INIT_WORK(&wlvif->rx_streaming_disable_work,
++		  wl1271_rx_streaming_disable_work);
++	INIT_DELAYED_WORK(&wlvif->channel_switch_work,
++			  wlcore_channel_switch_work);
++	INIT_DELAYED_WORK(&wlvif->connection_loss_work,
++			  wlcore_connection_loss_work);
++	INIT_LIST_HEAD(&wlvif->list);
++
++	setup_timer(&wlvif->rx_streaming_timer, wl1271_rx_streaming_timer,
++		    (unsigned long) wlvif);
++	return 0;
++}
++
++static int wl12xx_init_fw(struct wl1271 *wl)
++{
++	int retries = WL1271_BOOT_RETRIES;
++	bool booted = false;
++	struct wiphy *wiphy = wl->hw->wiphy;
++	int ret;
++
++	while (retries) {
++		retries--;
++		ret = wl12xx_chip_wakeup(wl, false);
++		if (ret < 0)
++			goto power_off;
++
++		ret = wl->ops->boot(wl);
++		if (ret < 0)
++			goto power_off;
++
++		ret = wl1271_hw_init(wl);
++		if (ret < 0)
++			goto irq_disable;
++
++		booted = true;
++		break;
++
++irq_disable:
++		mutex_unlock(&wl->mutex);
++		/* Unlocking the mutex in the middle of handling is
++		   inherently unsafe. In this case we deem it safe to do,
++		   because we need to let any possibly pending IRQ out of
++		   the system (and while we are WLCORE_STATE_OFF the IRQ
++		   work function will not do anything.) Also, any other
++		   possible concurrent operations will fail due to the
++		   current state, hence the wl1271 struct should be safe. */
++		wlcore_disable_interrupts(wl);
++		wl1271_flush_deferred_work(wl);
++		cancel_work_sync(&wl->netstack_work);
++		mutex_lock(&wl->mutex);
++power_off:
++		wl1271_power_off(wl);
++	}
++
++	if (!booted) {
++		wl1271_error("firmware boot failed despite %d retries",
++			     WL1271_BOOT_RETRIES);
++		goto out;
++	}
++
++	wl1271_info("firmware booted (%s)", wl->chip.fw_ver_str);
++
++	/* update hw/fw version info in wiphy struct */
++	wiphy->hw_version = wl->chip.id;
++	strncpy(wiphy->fw_version, wl->chip.fw_ver_str,
++		sizeof(wiphy->fw_version));
++
++	/*
++	 * Now we know if 11a is supported (info from the NVS), so disable
++	 * 11a channels if not supported
++	 */
++	if (!wl->enable_11a)
++		wiphy->bands[IEEE80211_BAND_5GHZ]->n_channels = 0;
++
++	wl1271_debug(DEBUG_MAC80211, "11a is %ssupported",
++		     wl->enable_11a ? "" : "not ");
++
++	wl->state = WLCORE_STATE_ON;
++out:
++	return ret;
++}
++
++static bool wl12xx_dev_role_started(struct wl12xx_vif *wlvif)
++{
++	return wlvif->dev_hlid != WL12XX_INVALID_LINK_ID;
++}
++
++/*
++ * Check whether a fw switch (i.e. moving from one loaded
++ * fw to another) is needed. This function is also responsible
++ * for updating wl->last_vif_count, so it must be called before
++ * loading a non-plt fw (so the correct fw (single-role/multi-role)
++ * will be used).
++ */
++static bool wl12xx_need_fw_change(struct wl1271 *wl,
++				  struct vif_counter_data vif_counter_data,
++				  bool add)
++{
++	enum wl12xx_fw_type current_fw = wl->fw_type;
++	u8 vif_count = vif_counter_data.counter;
++
++	if (test_bit(WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, &wl->flags))
++		return false;
++
++	/* increase the vif count if this is a new vif */
++	if (add && !vif_counter_data.cur_vif_running)
++		vif_count++;
++
++	wl->last_vif_count = vif_count;
++
++	/* no need for fw change if the device is OFF */
++	if (wl->state == WLCORE_STATE_OFF)
++		return false;
++
++	/* no need for fw change if a single fw is used */
++	if (!wl->mr_fw_name)
++		return false;
++
++	if (vif_count > 1 && current_fw == WL12XX_FW_TYPE_NORMAL)
++		return true;
++	if (vif_count <= 1 && current_fw == WL12XX_FW_TYPE_MULTI)
++		return true;
++
++	return false;
++}
++
++/*
++ * Enter "forced psm". Make sure the sta is in psm against the ap,
++ * to make the fw switch a bit more disconnection-persistent.
++ */
++static void wl12xx_force_active_psm(struct wl1271 *wl)
++{
++	struct wl12xx_vif *wlvif;
++
++	wl12xx_for_each_wlvif_sta(wl, wlvif) {
++		wl1271_ps_set_mode(wl, wlvif, STATION_POWER_SAVE_MODE);
++	}
++}
++
++struct wlcore_hw_queue_iter_data {
++	unsigned long hw_queue_map[BITS_TO_LONGS(WLCORE_NUM_MAC_ADDRESSES)];
++	/* current vif */
++	struct ieee80211_vif *vif;
++	/* is the current vif among those iterated */
++	bool cur_running;
++};
++
++static void wlcore_hw_queue_iter(void *data, u8 *mac,
++				 struct ieee80211_vif *vif)
++{
++	struct wlcore_hw_queue_iter_data *iter_data = data;
++
++	if (WARN_ON_ONCE(vif->hw_queue[0] == IEEE80211_INVAL_HW_QUEUE))
++		return;
++
++	if (iter_data->cur_running || vif == iter_data->vif) {
++		iter_data->cur_running = true;
++		return;
++	}
++
++	__set_bit(vif->hw_queue[0] / NUM_TX_QUEUES, iter_data->hw_queue_map);
++}
++
++static int wlcore_allocate_hw_queue_base(struct wl1271 *wl,
++					 struct wl12xx_vif *wlvif)
++{
++	struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
++	struct wlcore_hw_queue_iter_data iter_data = {};
++	int i, q_base;
++
++	iter_data.vif = vif;
++
++	/* mark all bits taken by active interfaces */
++	ieee80211_iterate_active_interfaces_atomic(wl->hw,
++					IEEE80211_IFACE_ITER_RESUME_ALL,
++					wlcore_hw_queue_iter, &iter_data);
++
++	/* the current vif is already running in mac80211 (resume/recovery) */
++	if (iter_data.cur_running) {
++		wlvif->hw_queue_base = vif->hw_queue[0];
++		wl1271_debug(DEBUG_MAC80211,
++			     "using pre-allocated hw queue base %d",
++			     wlvif->hw_queue_base);
++
++		/* interface type might have changed type */
++		goto adjust_cab_queue;
++	}
++
++	q_base = find_first_zero_bit(iter_data.hw_queue_map,
++				     WLCORE_NUM_MAC_ADDRESSES);
++	if (q_base >= WLCORE_NUM_MAC_ADDRESSES)
++		return -EBUSY;
++
++	wlvif->hw_queue_base = q_base * NUM_TX_QUEUES;
++	wl1271_debug(DEBUG_MAC80211, "allocating hw queue base: %d",
++		     wlvif->hw_queue_base);
++
++	for (i = 0; i < NUM_TX_QUEUES; i++) {
++		wl->queue_stop_reasons[wlvif->hw_queue_base + i] = 0;
++		/* register hw queues in mac80211 */
++		vif->hw_queue[i] = wlvif->hw_queue_base + i;
++	}
++
++adjust_cab_queue:
++	/* the last places are reserved for cab queues per interface */
++	if (wlvif->bss_type == BSS_TYPE_AP_BSS)
++		vif->cab_queue = NUM_TX_QUEUES * WLCORE_NUM_MAC_ADDRESSES +
++				 wlvif->hw_queue_base / NUM_TX_QUEUES;
++	else
++		vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
++
++	return 0;
++}
++
++static int wl1271_op_add_interface(struct ieee80211_hw *hw,
++				   struct ieee80211_vif *vif)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	struct vif_counter_data vif_count;
++	int ret = 0;
++	u8 role_type;
++
++	vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
++			     IEEE80211_VIF_SUPPORTS_CQM_RSSI;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
++		     ieee80211_vif_type_p2p(vif), vif->addr);
++
++	wl12xx_get_vif_count(hw, vif, &vif_count);
++
++	mutex_lock(&wl->mutex);
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out_unlock;
++
++	/*
++	 * in some very corner case HW recovery scenarios its possible to
++	 * get here before __wl1271_op_remove_interface is complete, so
++	 * opt out if that is the case.
++	 */
++	if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags) ||
++	    test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)) {
++		ret = -EBUSY;
++		goto out;
++	}
++
++
++	ret = wl12xx_init_vif_data(wl, vif);
++	if (ret < 0)
++		goto out;
++
++	wlvif->wl = wl;
++	role_type = wl12xx_get_role_type(wl, wlvif);
++	if (role_type == WL12XX_INVALID_ROLE_TYPE) {
++		ret = -EINVAL;
++		goto out;
++	}
++
++	ret = wlcore_allocate_hw_queue_base(wl, wlvif);
++	if (ret < 0)
++		goto out;
++
++	if (wl12xx_need_fw_change(wl, vif_count, true)) {
++		wl12xx_force_active_psm(wl);
++		set_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags);
++		mutex_unlock(&wl->mutex);
++		wl1271_recovery_work(&wl->recovery_work);
++		return 0;
++	}
++
++	/*
++	 * TODO: after the nvs issue will be solved, move this block
++	 * to start(), and make sure here the driver is ON.
++	 */
++	if (wl->state == WLCORE_STATE_OFF) {
++		/*
++		 * we still need this in order to configure the fw
++		 * while uploading the nvs
++		 */
++		memcpy(wl->addresses[0].addr, vif->addr, ETH_ALEN);
++
++		ret = wl12xx_init_fw(wl);
++		if (ret < 0)
++			goto out;
++	}
++
++	ret = wl12xx_cmd_role_enable(wl, vif->addr,
++				     role_type, &wlvif->role_id);
++	if (ret < 0)
++		goto out;
++
++	ret = wl1271_init_vif_specific(wl, vif);
++	if (ret < 0)
++		goto out;
++
++	list_add(&wlvif->list, &wl->wlvif_list);
++	set_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags);
++
++	if (wlvif->bss_type == BSS_TYPE_AP_BSS)
++		wl->ap_count++;
++	else
++		wl->sta_count++;
++out:
++	wl1271_ps_elp_sleep(wl);
++out_unlock:
++	mutex_unlock(&wl->mutex);
++
++	return ret;
++}
++
++static void __wl1271_op_remove_interface(struct wl1271 *wl,
++					 struct ieee80211_vif *vif,
++					 bool reset_tx_queues)
++{
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	int i, ret;
++	bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
++
++	if (!test_and_clear_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))
++		return;
++
++	/* because of hardware recovery, we may get here twice */
++	if (wl->state == WLCORE_STATE_OFF)
++		return;
++
++	wl1271_info("down");
++
++	if (wl->scan.state != WL1271_SCAN_STATE_IDLE &&
++	    wl->scan_wlvif == wlvif) {
++		/*
++		 * Rearm the tx watchdog just before idling scan. This
++		 * prevents just-finished scans from triggering the watchdog
++		 */
++		wl12xx_rearm_tx_watchdog_locked(wl);
++
++		wl->scan.state = WL1271_SCAN_STATE_IDLE;
++		memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
++		wl->scan_wlvif = NULL;
++		wl->scan.req = NULL;
++		ieee80211_scan_completed(wl->hw, true);
++	}
++
++	if (wl->sched_vif == wlvif) {
++		ieee80211_sched_scan_stopped(wl->hw);
++		wl->sched_vif = NULL;
++	}
++
++	if (wl->roc_vif == vif) {
++		wl->roc_vif = NULL;
++		ieee80211_remain_on_channel_expired(wl->hw);
++	}
++
++	if (!test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) {
++		/* disable active roles */
++		ret = wl1271_ps_elp_wakeup(wl);
++		if (ret < 0)
++			goto deinit;
++
++		if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
++		    wlvif->bss_type == BSS_TYPE_IBSS) {
++			if (wl12xx_dev_role_started(wlvif))
++				wl12xx_stop_dev(wl, wlvif);
++		}
++
++		ret = wl12xx_cmd_role_disable(wl, &wlvif->role_id);
++		if (ret < 0)
++			goto deinit;
++
++		wl1271_ps_elp_sleep(wl);
++	}
++deinit:
++	wl12xx_tx_reset_wlvif(wl, wlvif);
++
++	/* clear all hlids (except system_hlid) */
++	wlvif->dev_hlid = WL12XX_INVALID_LINK_ID;
++
++	if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
++	    wlvif->bss_type == BSS_TYPE_IBSS) {
++		wlvif->sta.hlid = WL12XX_INVALID_LINK_ID;
++		wl12xx_free_rate_policy(wl, &wlvif->sta.basic_rate_idx);
++		wl12xx_free_rate_policy(wl, &wlvif->sta.ap_rate_idx);
++		wl12xx_free_rate_policy(wl, &wlvif->sta.p2p_rate_idx);
++		wlcore_free_klv_template(wl, &wlvif->sta.klv_template_id);
++	} else {
++		wlvif->ap.bcast_hlid = WL12XX_INVALID_LINK_ID;
++		wlvif->ap.global_hlid = WL12XX_INVALID_LINK_ID;
++		wl12xx_free_rate_policy(wl, &wlvif->ap.mgmt_rate_idx);
++		wl12xx_free_rate_policy(wl, &wlvif->ap.bcast_rate_idx);
++		for (i = 0; i < CONF_TX_MAX_AC_COUNT; i++)
++			wl12xx_free_rate_policy(wl,
++						&wlvif->ap.ucast_rate_idx[i]);
++		wl1271_free_ap_keys(wl, wlvif);
++	}
++
++	dev_kfree_skb(wlvif->probereq);
++	wlvif->probereq = NULL;
++	if (wl->last_wlvif == wlvif)
++		wl->last_wlvif = NULL;
++	list_del(&wlvif->list);
++	memset(wlvif->ap.sta_hlid_map, 0, sizeof(wlvif->ap.sta_hlid_map));
++	wlvif->role_id = WL12XX_INVALID_ROLE_ID;
++	wlvif->dev_role_id = WL12XX_INVALID_ROLE_ID;
++
++	if (is_ap)
++		wl->ap_count--;
++	else
++		wl->sta_count--;
++
++	/*
++	 * Last AP, have more stations. Configure sleep auth according to STA.
++	 * Don't do thin on unintended recovery.
++	 */
++	if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags) &&
++	    !test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags))
++		goto unlock;
++
++	if (wl->ap_count == 0 && is_ap && wl->sta_count) {
++		u8 sta_auth = wl->conf.conn.sta_sleep_auth;
++		/* Configure for power according to debugfs */
++		if (sta_auth != WL1271_PSM_ILLEGAL)
++			wl1271_acx_sleep_auth(wl, sta_auth);
++		/* Configure for ELP power saving */
++		else
++			wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
++	}
++
++unlock:
++	mutex_unlock(&wl->mutex);
++
++	del_timer_sync(&wlvif->rx_streaming_timer);
++	cancel_work_sync(&wlvif->rx_streaming_enable_work);
++	cancel_work_sync(&wlvif->rx_streaming_disable_work);
++	cancel_delayed_work_sync(&wlvif->connection_loss_work);
++
++	mutex_lock(&wl->mutex);
++}
++
++static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
++				       struct ieee80211_vif *vif)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	struct wl12xx_vif *iter;
++	struct vif_counter_data vif_count;
++
++	wl12xx_get_vif_count(hw, vif, &vif_count);
++	mutex_lock(&wl->mutex);
++
++	if (wl->state == WLCORE_STATE_OFF ||
++	    !test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))
++		goto out;
++
++	/*
++	 * wl->vif can be null here if someone shuts down the interface
++	 * just when hardware recovery has been started.
++	 */
++	wl12xx_for_each_wlvif(wl, iter) {
++		if (iter != wlvif)
++			continue;
++
++		__wl1271_op_remove_interface(wl, vif, true);
++		break;
++	}
++	WARN_ON(iter != wlvif);
++	if (wl12xx_need_fw_change(wl, vif_count, false)) {
++		wl12xx_force_active_psm(wl);
++		set_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags);
++		wl12xx_queue_recovery_work(wl);
++	}
++out:
++	mutex_unlock(&wl->mutex);
++}
++
++static int wl12xx_op_change_interface(struct ieee80211_hw *hw,
++				      struct ieee80211_vif *vif,
++				      enum nl80211_iftype new_type, bool p2p)
++{
++	struct wl1271 *wl = hw->priv;
++	int ret;
++
++	set_bit(WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, &wl->flags);
++	wl1271_op_remove_interface(hw, vif);
++
++	vif->type = new_type;
++	vif->p2p = p2p;
++	ret = wl1271_op_add_interface(hw, vif);
++
++	clear_bit(WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, &wl->flags);
++	return ret;
++}
++
++static int wlcore_join(struct wl1271 *wl, struct wl12xx_vif *wlvif)
++{
++	int ret;
++	bool is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS);
++
++	/*
++	 * One of the side effects of the JOIN command is that is clears
++	 * WPA/WPA2 keys from the chipset. Performing a JOIN while associated
++	 * to a WPA/WPA2 access point will therefore kill the data-path.
++	 * Currently the only valid scenario for JOIN during association
++	 * is on roaming, in which case we will also be given new keys.
++	 * Keep the below message for now, unless it starts bothering
++	 * users who really like to roam a lot :)
++	 */
++	if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
++		wl1271_info("JOIN while associated.");
++
++	/* clear encryption type */
++	wlvif->encryption_type = KEY_NONE;
++
++	if (is_ibss)
++		ret = wl12xx_cmd_role_start_ibss(wl, wlvif);
++	else {
++		if (wl->quirks & WLCORE_QUIRK_START_STA_FAILS) {
++			/*
++			 * TODO: this is an ugly workaround for wl12xx fw
++			 * bug - we are not able to tx/rx after the first
++			 * start_sta, so make dummy start+stop calls,
++			 * and then call start_sta again.
++			 * this should be fixed in the fw.
++			 */
++			wl12xx_cmd_role_start_sta(wl, wlvif);
++			wl12xx_cmd_role_stop_sta(wl, wlvif);
++		}
++
++		ret = wl12xx_cmd_role_start_sta(wl, wlvif);
++	}
++
++	return ret;
++}
++
++static int wl1271_ssid_set(struct wl12xx_vif *wlvif, struct sk_buff *skb,
++			    int offset)
++{
++	u8 ssid_len;
++	const u8 *ptr = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset,
++					 skb->len - offset);
++
++	if (!ptr) {
++		wl1271_error("No SSID in IEs!");
++		return -ENOENT;
++	}
++
++	ssid_len = ptr[1];
++	if (ssid_len > IEEE80211_MAX_SSID_LEN) {
++		wl1271_error("SSID is too long!");
++		return -EINVAL;
++	}
++
++	wlvif->ssid_len = ssid_len;
++	memcpy(wlvif->ssid, ptr+2, ssid_len);
++	return 0;
++}
++
++static int wlcore_set_ssid(struct wl1271 *wl, struct wl12xx_vif *wlvif)
++{
++	struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
++	struct sk_buff *skb;
++	int ieoffset;
++
++	/* we currently only support setting the ssid from the ap probe req */
++	if (wlvif->bss_type != BSS_TYPE_STA_BSS)
++		return -EINVAL;
++
++	skb = ieee80211_ap_probereq_get(wl->hw, vif);
++	if (!skb)
++		return -EINVAL;
++
++	ieoffset = offsetof(struct ieee80211_mgmt,
++			    u.probe_req.variable);
++	wl1271_ssid_set(wlvif, skb, ieoffset);
++	dev_kfree_skb(skb);
++
++	return 0;
++}
++
++static int wlcore_set_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif,
++			    struct ieee80211_bss_conf *bss_conf,
++			    u32 sta_rate_set)
++{
++	int ieoffset;
++	int ret;
++
++	wlvif->aid = bss_conf->aid;
++	wlvif->channel_type = cfg80211_get_chandef_type(&bss_conf->chandef);
++	wlvif->beacon_int = bss_conf->beacon_int;
++	wlvif->wmm_enabled = bss_conf->qos;
++
++	set_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags);
++
++	/*
++	 * with wl1271, we don't need to update the
++	 * beacon_int and dtim_period, because the firmware
++	 * updates it by itself when the first beacon is
++	 * received after a join.
++	 */
++	ret = wl1271_cmd_build_ps_poll(wl, wlvif, wlvif->aid);
++	if (ret < 0)
++		return ret;
++
++	/*
++	 * Get a template for hardware connection maintenance
++	 */
++	dev_kfree_skb(wlvif->probereq);
++	wlvif->probereq = wl1271_cmd_build_ap_probe_req(wl,
++							wlvif,
++							NULL);
++	ieoffset = offsetof(struct ieee80211_mgmt,
++			    u.probe_req.variable);
++	wl1271_ssid_set(wlvif, wlvif->probereq, ieoffset);
++
++	/* enable the connection monitoring feature */
++	ret = wl1271_acx_conn_monit_params(wl, wlvif, true);
++	if (ret < 0)
++		return ret;
++
++	/*
++	 * The join command disable the keep-alive mode, shut down its process,
++	 * and also clear the template config, so we need to reset it all after
++	 * the join. The acx_aid starts the keep-alive process, and the order
++	 * of the commands below is relevant.
++	 */
++	ret = wl1271_acx_keep_alive_mode(wl, wlvif, true);
++	if (ret < 0)
++		return ret;
++
++	ret = wl1271_acx_aid(wl, wlvif, wlvif->aid);
++	if (ret < 0)
++		return ret;
++
++	ret = wl12xx_cmd_build_klv_null_data(wl, wlvif);
++	if (ret < 0)
++		return ret;
++
++	ret = wl1271_acx_keep_alive_config(wl, wlvif,
++					   wlvif->sta.klv_template_id,
++					   ACX_KEEP_ALIVE_TPL_VALID);
++	if (ret < 0)
++		return ret;
++
++	/*
++	 * The default fw psm configuration is AUTO, while mac80211 default
++	 * setting is off (ACTIVE), so sync the fw with the correct value.
++	 */
++	ret = wl1271_ps_set_mode(wl, wlvif, STATION_ACTIVE_MODE);
++	if (ret < 0)
++		return ret;
++
++	if (sta_rate_set) {
++		wlvif->rate_set =
++			wl1271_tx_enabled_rates_get(wl,
++						    sta_rate_set,
++						    wlvif->band);
++		ret = wl1271_acx_sta_rate_policies(wl, wlvif);
++		if (ret < 0)
++			return ret;
++	}
++
++	return ret;
++}
++
++static int wlcore_unset_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif)
++{
++	int ret;
++	bool sta = wlvif->bss_type == BSS_TYPE_STA_BSS;
++
++	/* make sure we are connected (sta) joined */
++	if (sta &&
++	    !test_and_clear_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
++		return false;
++
++	/* make sure we are joined (ibss) */
++	if (!sta &&
++	    test_and_clear_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags))
++		return false;
++
++	if (sta) {
++		/* use defaults when not associated */
++		wlvif->aid = 0;
++
++		/* free probe-request template */
++		dev_kfree_skb(wlvif->probereq);
++		wlvif->probereq = NULL;
++
++		/* disable connection monitor features */
++		ret = wl1271_acx_conn_monit_params(wl, wlvif, false);
++		if (ret < 0)
++			return ret;
++
++		/* Disable the keep-alive feature */
++		ret = wl1271_acx_keep_alive_mode(wl, wlvif, false);
++		if (ret < 0)
++			return ret;
++	}
++
++	if (test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags)) {
++		struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
++
++		wl12xx_cmd_stop_channel_switch(wl, wlvif);
++		ieee80211_chswitch_done(vif, false);
++		cancel_delayed_work(&wlvif->channel_switch_work);
++	}
++
++	/* invalidate keep-alive template */
++	wl1271_acx_keep_alive_config(wl, wlvif,
++				     wlvif->sta.klv_template_id,
++				     ACX_KEEP_ALIVE_TPL_INVALID);
++
++	return 0;
++}
++
++static void wl1271_set_band_rate(struct wl1271 *wl, struct wl12xx_vif *wlvif)
++{
++	wlvif->basic_rate_set = wlvif->bitrate_masks[wlvif->band];
++	wlvif->rate_set = wlvif->basic_rate_set;
++}
++
++static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif,
++			     struct ieee80211_conf *conf, u32 changed)
++{
++	int ret;
++
++	if (conf->power_level != wlvif->power_level) {
++		ret = wl1271_acx_tx_power(wl, wlvif, conf->power_level);
++		if (ret < 0)
++			return ret;
++
++		wlvif->power_level = conf->power_level;
++	}
++
++	return 0;
++}
++
++static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif;
++	struct ieee80211_conf *conf = &hw->conf;
++	int ret = 0;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 config psm %s power %d %s"
++		     " changed 0x%x",
++		     conf->flags & IEEE80211_CONF_PS ? "on" : "off",
++		     conf->power_level,
++		     conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use",
++			 changed);
++
++	mutex_lock(&wl->mutex);
++
++	if (changed & IEEE80211_CONF_CHANGE_POWER)
++		wl->power_level = conf->power_level;
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	/* configure each interface */
++	wl12xx_for_each_wlvif(wl, wlvif) {
++		ret = wl12xx_config_vif(wl, wlvif, conf, changed);
++		if (ret < 0)
++			goto out_sleep;
++	}
++
++out_sleep:
++	wl1271_ps_elp_sleep(wl);
++
++out:
++	mutex_unlock(&wl->mutex);
++
++	return ret;
++}
++
++struct wl1271_filter_params {
++	bool enabled;
++	int mc_list_length;
++	u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN];
++};
++
++static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw,
++				       struct netdev_hw_addr_list *mc_list)
++{
++	struct wl1271_filter_params *fp;
++	struct netdev_hw_addr *ha;
++
++	fp = kzalloc(sizeof(*fp), GFP_ATOMIC);
++	if (!fp) {
++		wl1271_error("Out of memory setting filters.");
++		return 0;
++	}
++
++	/* update multicast filtering parameters */
++	fp->mc_list_length = 0;
++	if (netdev_hw_addr_list_count(mc_list) > ACX_MC_ADDRESS_GROUP_MAX) {
++		fp->enabled = false;
++	} else {
++		fp->enabled = true;
++		netdev_hw_addr_list_for_each(ha, mc_list) {
++			memcpy(fp->mc_list[fp->mc_list_length],
++					ha->addr, ETH_ALEN);
++			fp->mc_list_length++;
++		}
++	}
++
++	return (u64)(unsigned long)fp;
++}
++
++#define WL1271_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \
++				  FIF_ALLMULTI | \
++				  FIF_FCSFAIL | \
++				  FIF_BCN_PRBRESP_PROMISC | \
++				  FIF_CONTROL | \
++				  FIF_OTHER_BSS)
++
++static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
++				       unsigned int changed,
++				       unsigned int *total, u64 multicast)
++{
++	struct wl1271_filter_params *fp = (void *)(unsigned long)multicast;
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif;
++
++	int ret;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 configure filter changed %x"
++		     " total %x", changed, *total);
++
++	mutex_lock(&wl->mutex);
++
++	*total &= WL1271_SUPPORTED_FILTERS;
++	changed &= WL1271_SUPPORTED_FILTERS;
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	wl12xx_for_each_wlvif(wl, wlvif) {
++		if (wlvif->bss_type != BSS_TYPE_AP_BSS) {
++			if (*total & FIF_ALLMULTI)
++				ret = wl1271_acx_group_address_tbl(wl, wlvif,
++								   false,
++								   NULL, 0);
++			else if (fp)
++				ret = wl1271_acx_group_address_tbl(wl, wlvif,
++							fp->enabled,
++							fp->mc_list,
++							fp->mc_list_length);
++			if (ret < 0)
++				goto out_sleep;
++		}
++	}
++
++	/*
++	 * the fw doesn't provide an api to configure the filters. instead,
++	 * the filters configuration is based on the active roles / ROC
++	 * state.
++	 */
++
++out_sleep:
++	wl1271_ps_elp_sleep(wl);
++
++out:
++	mutex_unlock(&wl->mutex);
++	kfree(fp);
++}
++
++static int wl1271_record_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
++				u8 id, u8 key_type, u8 key_size,
++				const u8 *key, u8 hlid, u32 tx_seq_32,
++				u16 tx_seq_16)
++{
++	struct wl1271_ap_key *ap_key;
++	int i;
++
++	wl1271_debug(DEBUG_CRYPT, "record ap key id %d", (int)id);
++
++	if (key_size > MAX_KEY_SIZE)
++		return -EINVAL;
++
++	/*
++	 * Find next free entry in ap_keys. Also check we are not replacing
++	 * an existing key.
++	 */
++	for (i = 0; i < MAX_NUM_KEYS; i++) {
++		if (wlvif->ap.recorded_keys[i] == NULL)
++			break;
++
++		if (wlvif->ap.recorded_keys[i]->id == id) {
++			wl1271_warning("trying to record key replacement");
++			return -EINVAL;
++		}
++	}
++
++	if (i == MAX_NUM_KEYS)
++		return -EBUSY;
++
++	ap_key = kzalloc(sizeof(*ap_key), GFP_KERNEL);
++	if (!ap_key)
++		return -ENOMEM;
++
++	ap_key->id = id;
++	ap_key->key_type = key_type;
++	ap_key->key_size = key_size;
++	memcpy(ap_key->key, key, key_size);
++	ap_key->hlid = hlid;
++	ap_key->tx_seq_32 = tx_seq_32;
++	ap_key->tx_seq_16 = tx_seq_16;
++
++	wlvif->ap.recorded_keys[i] = ap_key;
++	return 0;
++}
++
++static void wl1271_free_ap_keys(struct wl1271 *wl, struct wl12xx_vif *wlvif)
++{
++	int i;
++
++	for (i = 0; i < MAX_NUM_KEYS; i++) {
++		kfree(wlvif->ap.recorded_keys[i]);
++		wlvif->ap.recorded_keys[i] = NULL;
++	}
++}
++
++static int wl1271_ap_init_hwenc(struct wl1271 *wl, struct wl12xx_vif *wlvif)
++{
++	int i, ret = 0;
++	struct wl1271_ap_key *key;
++	bool wep_key_added = false;
++
++	for (i = 0; i < MAX_NUM_KEYS; i++) {
++		u8 hlid;
++		if (wlvif->ap.recorded_keys[i] == NULL)
++			break;
++
++		key = wlvif->ap.recorded_keys[i];
++		hlid = key->hlid;
++		if (hlid == WL12XX_INVALID_LINK_ID)
++			hlid = wlvif->ap.bcast_hlid;
++
++		ret = wl1271_cmd_set_ap_key(wl, wlvif, KEY_ADD_OR_REPLACE,
++					    key->id, key->key_type,
++					    key->key_size, key->key,
++					    hlid, key->tx_seq_32,
++					    key->tx_seq_16);
++		if (ret < 0)
++			goto out;
++
++		if (key->key_type == KEY_WEP)
++			wep_key_added = true;
++	}
++
++	if (wep_key_added) {
++		ret = wl12xx_cmd_set_default_wep_key(wl, wlvif->default_key,
++						     wlvif->ap.bcast_hlid);
++		if (ret < 0)
++			goto out;
++	}
++
++out:
++	wl1271_free_ap_keys(wl, wlvif);
++	return ret;
++}
++
++static int wl1271_set_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
++		       u16 action, u8 id, u8 key_type,
++		       u8 key_size, const u8 *key, u32 tx_seq_32,
++		       u16 tx_seq_16, struct ieee80211_sta *sta)
++{
++	int ret;
++	bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
++
++	if (is_ap) {
++		struct wl1271_station *wl_sta;
++		u8 hlid;
++
++		if (sta) {
++			wl_sta = (struct wl1271_station *)sta->drv_priv;
++			hlid = wl_sta->hlid;
++		} else {
++			hlid = wlvif->ap.bcast_hlid;
++		}
++
++		if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) {
++			/*
++			 * We do not support removing keys after AP shutdown.
++			 * Pretend we do to make mac80211 happy.
++			 */
++			if (action != KEY_ADD_OR_REPLACE)
++				return 0;
++
++			ret = wl1271_record_ap_key(wl, wlvif, id,
++					     key_type, key_size,
++					     key, hlid, tx_seq_32,
++					     tx_seq_16);
++		} else {
++			ret = wl1271_cmd_set_ap_key(wl, wlvif, action,
++					     id, key_type, key_size,
++					     key, hlid, tx_seq_32,
++					     tx_seq_16);
++		}
++
++		if (ret < 0)
++			return ret;
++	} else {
++		const u8 *addr;
++		static const u8 bcast_addr[ETH_ALEN] = {
++			0xff, 0xff, 0xff, 0xff, 0xff, 0xff
++		};
++
++		addr = sta ? sta->addr : bcast_addr;
++
++		if (is_zero_ether_addr(addr)) {
++			/* We dont support TX only encryption */
++			return -EOPNOTSUPP;
++		}
++
++		/* The wl1271 does not allow to remove unicast keys - they
++		   will be cleared automatically on next CMD_JOIN. Ignore the
++		   request silently, as we dont want the mac80211 to emit
++		   an error message. */
++		if (action == KEY_REMOVE && !is_broadcast_ether_addr(addr))
++			return 0;
++
++		/* don't remove key if hlid was already deleted */
++		if (action == KEY_REMOVE &&
++		    wlvif->sta.hlid == WL12XX_INVALID_LINK_ID)
++			return 0;
++
++		ret = wl1271_cmd_set_sta_key(wl, wlvif, action,
++					     id, key_type, key_size,
++					     key, addr, tx_seq_32,
++					     tx_seq_16);
++		if (ret < 0)
++			return ret;
++
++		/* the default WEP key needs to be configured at least once */
++		if (key_type == KEY_WEP) {
++			ret = wl12xx_cmd_set_default_wep_key(wl,
++							wlvif->default_key,
++							wlvif->sta.hlid);
++			if (ret < 0)
++				return ret;
++		}
++	}
++
++	return 0;
++}
++
++static int wlcore_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
++			     struct ieee80211_vif *vif,
++			     struct ieee80211_sta *sta,
++			     struct ieee80211_key_conf *key_conf)
++{
++	struct wl1271 *wl = hw->priv;
++	int ret;
++	bool might_change_spare =
++		key_conf->cipher == WL1271_CIPHER_SUITE_GEM ||
++		key_conf->cipher == WLAN_CIPHER_SUITE_TKIP;
++
++	if (might_change_spare) {
++		/*
++		 * stop the queues and flush to ensure the next packets are
++		 * in sync with FW spare block accounting
++		 */
++		wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_SPARE_BLK);
++		wl1271_tx_flush(wl);
++	}
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON)) {
++		ret = -EAGAIN;
++		goto out_wake_queues;
++	}
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out_wake_queues;
++
++	ret = wlcore_hw_set_key(wl, cmd, vif, sta, key_conf);
++
++	wl1271_ps_elp_sleep(wl);
++
++out_wake_queues:
++	if (might_change_spare)
++		wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_SPARE_BLK);
++
++	mutex_unlock(&wl->mutex);
++
++	return ret;
++}
++
++int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
++		   struct ieee80211_vif *vif,
++		   struct ieee80211_sta *sta,
++		   struct ieee80211_key_conf *key_conf)
++{
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	int ret;
++	u32 tx_seq_32 = 0;
++	u16 tx_seq_16 = 0;
++	u8 key_type;
++	u8 hlid;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 set key");
++
++	wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x sta: %p", cmd, sta);
++	wl1271_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x",
++		     key_conf->cipher, key_conf->keyidx,
++		     key_conf->keylen, key_conf->flags);
++	wl1271_dump(DEBUG_CRYPT, "KEY: ", key_conf->key, key_conf->keylen);
++
++	if (wlvif->bss_type == BSS_TYPE_AP_BSS)
++		if (sta) {
++			struct wl1271_station *wl_sta = (void *)sta->drv_priv;
++			hlid = wl_sta->hlid;
++		} else {
++			hlid = wlvif->ap.bcast_hlid;
++		}
++	else
++		hlid = wlvif->sta.hlid;
++
++	if (hlid != WL12XX_INVALID_LINK_ID) {
++		u64 tx_seq = wl->links[hlid].total_freed_pkts;
++		tx_seq_32 = WL1271_TX_SECURITY_HI32(tx_seq);
++		tx_seq_16 = WL1271_TX_SECURITY_LO16(tx_seq);
++	}
++
++	switch (key_conf->cipher) {
++	case WLAN_CIPHER_SUITE_WEP40:
++	case WLAN_CIPHER_SUITE_WEP104:
++		key_type = KEY_WEP;
++
++		key_conf->hw_key_idx = key_conf->keyidx;
++		break;
++	case WLAN_CIPHER_SUITE_TKIP:
++		key_type = KEY_TKIP;
++		key_conf->hw_key_idx = key_conf->keyidx;
++		break;
++	case WLAN_CIPHER_SUITE_CCMP:
++		key_type = KEY_AES;
++		key_conf->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE;
++		break;
++	case WL1271_CIPHER_SUITE_GEM:
++		key_type = KEY_GEM;
++		break;
++	default:
++		wl1271_error("Unknown key algo 0x%x", key_conf->cipher);
++
++		return -EOPNOTSUPP;
++	}
++
++	switch (cmd) {
++	case SET_KEY:
++		ret = wl1271_set_key(wl, wlvif, KEY_ADD_OR_REPLACE,
++				 key_conf->keyidx, key_type,
++				 key_conf->keylen, key_conf->key,
++				 tx_seq_32, tx_seq_16, sta);
++		if (ret < 0) {
++			wl1271_error("Could not add or replace key");
++			return ret;
++		}
++
++		/*
++		 * reconfiguring arp response if the unicast (or common)
++		 * encryption key type was changed
++		 */
++		if (wlvif->bss_type == BSS_TYPE_STA_BSS &&
++		    (sta || key_type == KEY_WEP) &&
++		    wlvif->encryption_type != key_type) {
++			wlvif->encryption_type = key_type;
++			ret = wl1271_cmd_build_arp_rsp(wl, wlvif);
++			if (ret < 0) {
++				wl1271_warning("build arp rsp failed: %d", ret);
++				return ret;
++			}
++		}
++		break;
++
++	case DISABLE_KEY:
++		ret = wl1271_set_key(wl, wlvif, KEY_REMOVE,
++				     key_conf->keyidx, key_type,
++				     key_conf->keylen, key_conf->key,
++				     0, 0, sta);
++		if (ret < 0) {
++			wl1271_error("Could not remove key");
++			return ret;
++		}
++		break;
++
++	default:
++		wl1271_error("Unsupported key cmd 0x%x", cmd);
++		return -EOPNOTSUPP;
++	}
++
++	return ret;
++}
++EXPORT_SYMBOL_GPL(wlcore_set_key);
++
++void wlcore_regdomain_config(struct wl1271 *wl)
++{
++	int ret;
++
++	if (!(wl->quirks & WLCORE_QUIRK_REGDOMAIN_CONF))
++		return;
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wlcore_cmd_regdomain_config_locked(wl);
++	if (ret < 0) {
++		wl12xx_queue_recovery_work(wl);
++		goto out;
++	}
++
++	wl1271_ps_elp_sleep(wl);
++out:
++	mutex_unlock(&wl->mutex);
++}
++
++static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
++			     struct ieee80211_vif *vif,
++			     struct cfg80211_scan_request *req)
++{
++	struct wl1271 *wl = hw->priv;
++	int ret;
++	u8 *ssid = NULL;
++	size_t len = 0;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 hw scan");
++
++	if (req->n_ssids) {
++		ssid = req->ssids[0].ssid;
++		len = req->ssids[0].ssid_len;
++	}
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON)) {
++		/*
++		 * We cannot return -EBUSY here because cfg80211 will expect
++		 * a call to ieee80211_scan_completed if we do - in this case
++		 * there won't be any call.
++		 */
++		ret = -EAGAIN;
++		goto out;
++	}
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	/* fail if there is any role in ROC */
++	if (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES) {
++		/* don't allow scanning right now */
++		ret = -EBUSY;
++		goto out_sleep;
++	}
++
++	ret = wlcore_scan(hw->priv, vif, ssid, len, req);
++out_sleep:
++	wl1271_ps_elp_sleep(wl);
++out:
++	mutex_unlock(&wl->mutex);
++
++	return ret;
++}
++
++static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw,
++				     struct ieee80211_vif *vif)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	int ret;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 cancel hw scan");
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	if (wl->scan.state == WL1271_SCAN_STATE_IDLE)
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	if (wl->scan.state != WL1271_SCAN_STATE_DONE) {
++		ret = wl->ops->scan_stop(wl, wlvif);
++		if (ret < 0)
++			goto out_sleep;
++	}
++
++	/*
++	 * Rearm the tx watchdog just before idling scan. This
++	 * prevents just-finished scans from triggering the watchdog
++	 */
++	wl12xx_rearm_tx_watchdog_locked(wl);
++
++	wl->scan.state = WL1271_SCAN_STATE_IDLE;
++	memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
++	wl->scan_wlvif = NULL;
++	wl->scan.req = NULL;
++	ieee80211_scan_completed(wl->hw, true);
++
++out_sleep:
++	wl1271_ps_elp_sleep(wl);
++out:
++	mutex_unlock(&wl->mutex);
++
++	cancel_delayed_work_sync(&wl->scan_complete_work);
++}
++
++static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw,
++				      struct ieee80211_vif *vif,
++				      struct cfg80211_sched_scan_request *req,
++				      struct ieee80211_sched_scan_ies *ies)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	int ret;
++
++	wl1271_debug(DEBUG_MAC80211, "wl1271_op_sched_scan_start");
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON)) {
++		ret = -EAGAIN;
++		goto out;
++	}
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wl->ops->sched_scan_start(wl, wlvif, req, ies);
++	if (ret < 0)
++		goto out_sleep;
++
++	wl->sched_vif = wlvif;
++
++out_sleep:
++	wl1271_ps_elp_sleep(wl);
++out:
++	mutex_unlock(&wl->mutex);
++	return ret;
++}
++
++static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw,
++				      struct ieee80211_vif *vif)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	int ret;
++
++	wl1271_debug(DEBUG_MAC80211, "wl1271_op_sched_scan_stop");
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	wl->ops->sched_scan_stop(wl, wlvif);
++
++	wl1271_ps_elp_sleep(wl);
++out:
++	mutex_unlock(&wl->mutex);
++}
++
++static int wl1271_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
++{
++	struct wl1271 *wl = hw->priv;
++	int ret = 0;
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON)) {
++		ret = -EAGAIN;
++		goto out;
++	}
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wl1271_acx_frag_threshold(wl, value);
++	if (ret < 0)
++		wl1271_warning("wl1271_op_set_frag_threshold failed: %d", ret);
++
++	wl1271_ps_elp_sleep(wl);
++
++out:
++	mutex_unlock(&wl->mutex);
++
++	return ret;
++}
++
++static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif;
++	int ret = 0;
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON)) {
++		ret = -EAGAIN;
++		goto out;
++	}
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	wl12xx_for_each_wlvif(wl, wlvif) {
++		ret = wl1271_acx_rts_threshold(wl, wlvif, value);
++		if (ret < 0)
++			wl1271_warning("set rts threshold failed: %d", ret);
++	}
++	wl1271_ps_elp_sleep(wl);
++
++out:
++	mutex_unlock(&wl->mutex);
++
++	return ret;
++}
++
++static void wl12xx_remove_ie(struct sk_buff *skb, u8 eid, int ieoffset)
++{
++	int len;
++	const u8 *next, *end = skb->data + skb->len;
++	u8 *ie = (u8 *)cfg80211_find_ie(eid, skb->data + ieoffset,
++					skb->len - ieoffset);
++	if (!ie)
++		return;
++	len = ie[1] + 2;
++	next = ie + len;
++	memmove(ie, next, end - next);
++	skb_trim(skb, skb->len - len);
++}
++
++static void wl12xx_remove_vendor_ie(struct sk_buff *skb,
++					    unsigned int oui, u8 oui_type,
++					    int ieoffset)
++{
++	int len;
++	const u8 *next, *end = skb->data + skb->len;
++	u8 *ie = (u8 *)cfg80211_find_vendor_ie(oui, oui_type,
++					       skb->data + ieoffset,
++					       skb->len - ieoffset);
++	if (!ie)
++		return;
++	len = ie[1] + 2;
++	next = ie + len;
++	memmove(ie, next, end - next);
++	skb_trim(skb, skb->len - len);
++}
++
++static int wl1271_ap_set_probe_resp_tmpl(struct wl1271 *wl, u32 rates,
++					 struct ieee80211_vif *vif)
++{
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	struct sk_buff *skb;
++	int ret;
++
++	skb = ieee80211_proberesp_get(wl->hw, vif);
++	if (!skb)
++		return -EOPNOTSUPP;
++
++	ret = wl1271_cmd_template_set(wl, wlvif->role_id,
++				      CMD_TEMPL_AP_PROBE_RESPONSE,
++				      skb->data,
++				      skb->len, 0,
++				      rates);
++	dev_kfree_skb(skb);
++
++	if (ret < 0)
++		goto out;
++
++	wl1271_debug(DEBUG_AP, "probe response updated");
++	set_bit(WLVIF_FLAG_AP_PROBE_RESP_SET, &wlvif->flags);
++
++out:
++	return ret;
++}
++
++static int wl1271_ap_set_probe_resp_tmpl_legacy(struct wl1271 *wl,
++					     struct ieee80211_vif *vif,
++					     u8 *probe_rsp_data,
++					     size_t probe_rsp_len,
++					     u32 rates)
++{
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
++	u8 probe_rsp_templ[WL1271_CMD_TEMPL_MAX_SIZE];
++	int ssid_ie_offset, ie_offset, templ_len;
++	const u8 *ptr;
++
++	/* no need to change probe response if the SSID is set correctly */
++	if (wlvif->ssid_len > 0)
++		return wl1271_cmd_template_set(wl, wlvif->role_id,
++					       CMD_TEMPL_AP_PROBE_RESPONSE,
++					       probe_rsp_data,
++					       probe_rsp_len, 0,
++					       rates);
++
++	if (probe_rsp_len + bss_conf->ssid_len > WL1271_CMD_TEMPL_MAX_SIZE) {
++		wl1271_error("probe_rsp template too big");
++		return -EINVAL;
++	}
++
++	/* start searching from IE offset */
++	ie_offset = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
++
++	ptr = cfg80211_find_ie(WLAN_EID_SSID, probe_rsp_data + ie_offset,
++			       probe_rsp_len - ie_offset);
++	if (!ptr) {
++		wl1271_error("No SSID in beacon!");
++		return -EINVAL;
++	}
++
++	ssid_ie_offset = ptr - probe_rsp_data;
++	ptr += (ptr[1] + 2);
++
++	memcpy(probe_rsp_templ, probe_rsp_data, ssid_ie_offset);
++
++	/* insert SSID from bss_conf */
++	probe_rsp_templ[ssid_ie_offset] = WLAN_EID_SSID;
++	probe_rsp_templ[ssid_ie_offset + 1] = bss_conf->ssid_len;
++	memcpy(probe_rsp_templ + ssid_ie_offset + 2,
++	       bss_conf->ssid, bss_conf->ssid_len);
++	templ_len = ssid_ie_offset + 2 + bss_conf->ssid_len;
++
++	memcpy(probe_rsp_templ + ssid_ie_offset + 2 + bss_conf->ssid_len,
++	       ptr, probe_rsp_len - (ptr - probe_rsp_data));
++	templ_len += probe_rsp_len - (ptr - probe_rsp_data);
++
++	return wl1271_cmd_template_set(wl, wlvif->role_id,
++				       CMD_TEMPL_AP_PROBE_RESPONSE,
++				       probe_rsp_templ,
++				       templ_len, 0,
++				       rates);
++}
++
++static int wl1271_bss_erp_info_changed(struct wl1271 *wl,
++				       struct ieee80211_vif *vif,
++				       struct ieee80211_bss_conf *bss_conf,
++				       u32 changed)
++{
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	int ret = 0;
++
++	if (changed & BSS_CHANGED_ERP_SLOT) {
++		if (bss_conf->use_short_slot)
++			ret = wl1271_acx_slot(wl, wlvif, SLOT_TIME_SHORT);
++		else
++			ret = wl1271_acx_slot(wl, wlvif, SLOT_TIME_LONG);
++		if (ret < 0) {
++			wl1271_warning("Set slot time failed %d", ret);
++			goto out;
++		}
++	}
++
++	if (changed & BSS_CHANGED_ERP_PREAMBLE) {
++		if (bss_conf->use_short_preamble)
++			wl1271_acx_set_preamble(wl, wlvif, ACX_PREAMBLE_SHORT);
++		else
++			wl1271_acx_set_preamble(wl, wlvif, ACX_PREAMBLE_LONG);
++	}
++
++	if (changed & BSS_CHANGED_ERP_CTS_PROT) {
++		if (bss_conf->use_cts_prot)
++			ret = wl1271_acx_cts_protect(wl, wlvif,
++						     CTSPROTECT_ENABLE);
++		else
++			ret = wl1271_acx_cts_protect(wl, wlvif,
++						     CTSPROTECT_DISABLE);
++		if (ret < 0) {
++			wl1271_warning("Set ctsprotect failed %d", ret);
++			goto out;
++		}
++	}
++
++out:
++	return ret;
++}
++
++static int wlcore_set_beacon_template(struct wl1271 *wl,
++				      struct ieee80211_vif *vif,
++				      bool is_ap)
++{
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	struct ieee80211_hdr *hdr;
++	u32 min_rate;
++	int ret;
++	int ieoffset = offsetof(struct ieee80211_mgmt,
++				u.beacon.variable);
++	struct sk_buff *beacon = ieee80211_beacon_get(wl->hw, vif);
++	u16 tmpl_id;
++
++	if (!beacon) {
++		ret = -EINVAL;
++		goto out;
++	}
++
++	wl1271_debug(DEBUG_MASTER, "beacon updated");
++
++	ret = wl1271_ssid_set(wlvif, beacon, ieoffset);
++	if (ret < 0) {
++		dev_kfree_skb(beacon);
++		goto out;
++	}
++	min_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
++	tmpl_id = is_ap ? CMD_TEMPL_AP_BEACON :
++		CMD_TEMPL_BEACON;
++	ret = wl1271_cmd_template_set(wl, wlvif->role_id, tmpl_id,
++				      beacon->data,
++				      beacon->len, 0,
++				      min_rate);
++	if (ret < 0) {
++		dev_kfree_skb(beacon);
++		goto out;
++	}
++
++	wlvif->wmm_enabled =
++		cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
++					WLAN_OUI_TYPE_MICROSOFT_WMM,
++					beacon->data + ieoffset,
++					beacon->len - ieoffset);
++
++	/*
++	 * In case we already have a probe-resp beacon set explicitly
++	 * by usermode, don't use the beacon data.
++	 */
++	if (test_bit(WLVIF_FLAG_AP_PROBE_RESP_SET, &wlvif->flags))
++		goto end_bcn;
++
++	/* remove TIM ie from probe response */
++	wl12xx_remove_ie(beacon, WLAN_EID_TIM, ieoffset);
++
++	/*
++	 * remove p2p ie from probe response.
++	 * the fw reponds to probe requests that don't include
++	 * the p2p ie. probe requests with p2p ie will be passed,
++	 * and will be responded by the supplicant (the spec
++	 * forbids including the p2p ie when responding to probe
++	 * requests that didn't include it).
++	 */
++	wl12xx_remove_vendor_ie(beacon, WLAN_OUI_WFA,
++				WLAN_OUI_TYPE_WFA_P2P, ieoffset);
++
++	hdr = (struct ieee80211_hdr *) beacon->data;
++	hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
++					 IEEE80211_STYPE_PROBE_RESP);
++	if (is_ap)
++		ret = wl1271_ap_set_probe_resp_tmpl_legacy(wl, vif,
++							   beacon->data,
++							   beacon->len,
++							   min_rate);
++	else
++		ret = wl1271_cmd_template_set(wl, wlvif->role_id,
++					      CMD_TEMPL_PROBE_RESPONSE,
++					      beacon->data,
++					      beacon->len, 0,
++					      min_rate);
++end_bcn:
++	dev_kfree_skb(beacon);
++	if (ret < 0)
++		goto out;
++
++out:
++	return ret;
++}
++
++static int wl1271_bss_beacon_info_changed(struct wl1271 *wl,
++					  struct ieee80211_vif *vif,
++					  struct ieee80211_bss_conf *bss_conf,
++					  u32 changed)
++{
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
++	int ret = 0;
++
++	if (changed & BSS_CHANGED_BEACON_INT) {
++		wl1271_debug(DEBUG_MASTER, "beacon interval updated: %d",
++			bss_conf->beacon_int);
++
++		wlvif->beacon_int = bss_conf->beacon_int;
++	}
++
++	if ((changed & BSS_CHANGED_AP_PROBE_RESP) && is_ap) {
++		u32 rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
++
++		wl1271_ap_set_probe_resp_tmpl(wl, rate, vif);
++	}
++
++	if (changed & BSS_CHANGED_BEACON) {
++		ret = wlcore_set_beacon_template(wl, vif, is_ap);
++		if (ret < 0)
++			goto out;
++	}
++
++out:
++	if (ret != 0)
++		wl1271_error("beacon info change failed: %d", ret);
++	return ret;
++}
++
++/* AP mode changes */
++static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
++				       struct ieee80211_vif *vif,
++				       struct ieee80211_bss_conf *bss_conf,
++				       u32 changed)
++{
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	int ret = 0;
++
++	if (changed & BSS_CHANGED_BASIC_RATES) {
++		u32 rates = bss_conf->basic_rates;
++
++		wlvif->basic_rate_set = wl1271_tx_enabled_rates_get(wl, rates,
++								 wlvif->band);
++		wlvif->basic_rate = wl1271_tx_min_rate_get(wl,
++							wlvif->basic_rate_set);
++
++		ret = wl1271_init_ap_rates(wl, wlvif);
++		if (ret < 0) {
++			wl1271_error("AP rate policy change failed %d", ret);
++			goto out;
++		}
++
++		ret = wl1271_ap_init_templates(wl, vif);
++		if (ret < 0)
++			goto out;
++
++		ret = wl1271_ap_set_probe_resp_tmpl(wl, wlvif->basic_rate, vif);
++		if (ret < 0)
++			goto out;
++
++		ret = wlcore_set_beacon_template(wl, vif, true);
++		if (ret < 0)
++			goto out;
++	}
++
++	ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf, changed);
++	if (ret < 0)
++		goto out;
++
++	if (changed & BSS_CHANGED_BEACON_ENABLED) {
++		if (bss_conf->enable_beacon) {
++			if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) {
++				ret = wl12xx_cmd_role_start_ap(wl, wlvif);
++				if (ret < 0)
++					goto out;
++
++				ret = wl1271_ap_init_hwenc(wl, wlvif);
++				if (ret < 0)
++					goto out;
++
++				set_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags);
++				wl1271_debug(DEBUG_AP, "started AP");
++			}
++		} else {
++			if (test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) {
++				ret = wl12xx_cmd_role_stop_ap(wl, wlvif);
++				if (ret < 0)
++					goto out;
++
++				clear_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags);
++				clear_bit(WLVIF_FLAG_AP_PROBE_RESP_SET,
++					  &wlvif->flags);
++				wl1271_debug(DEBUG_AP, "stopped AP");
++			}
++		}
++	}
++
++	ret = wl1271_bss_erp_info_changed(wl, vif, bss_conf, changed);
++	if (ret < 0)
++		goto out;
++
++	/* Handle HT information change */
++	if ((changed & BSS_CHANGED_HT) &&
++	    (bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT)) {
++		ret = wl1271_acx_set_ht_information(wl, wlvif,
++					bss_conf->ht_operation_mode);
++		if (ret < 0) {
++			wl1271_warning("Set ht information failed %d", ret);
++			goto out;
++		}
++	}
++
++out:
++	return;
++}
++
++static int wlcore_set_bssid(struct wl1271 *wl, struct wl12xx_vif *wlvif,
++			    struct ieee80211_bss_conf *bss_conf,
++			    u32 sta_rate_set)
++{
++	u32 rates;
++	int ret;
++
++	wl1271_debug(DEBUG_MAC80211,
++	     "changed_bssid: %pM, aid: %d, bcn_int: %d, brates: 0x%x sta_rate_set: 0x%x",
++	     bss_conf->bssid, bss_conf->aid,
++	     bss_conf->beacon_int,
++	     bss_conf->basic_rates, sta_rate_set);
++
++	wlvif->beacon_int = bss_conf->beacon_int;
++	rates = bss_conf->basic_rates;
++	wlvif->basic_rate_set =
++		wl1271_tx_enabled_rates_get(wl, rates,
++					    wlvif->band);
++	wlvif->basic_rate =
++		wl1271_tx_min_rate_get(wl,
++				       wlvif->basic_rate_set);
++
++	if (sta_rate_set)
++		wlvif->rate_set =
++			wl1271_tx_enabled_rates_get(wl,
++						sta_rate_set,
++						wlvif->band);
++
++	/* we only support sched_scan while not connected */
++	if (wl->sched_vif == wlvif)
++		wl->ops->sched_scan_stop(wl, wlvif);
++
++	ret = wl1271_acx_sta_rate_policies(wl, wlvif);
++	if (ret < 0)
++		return ret;
++
++	ret = wl12xx_cmd_build_null_data(wl, wlvif);
++	if (ret < 0)
++		return ret;
++
++	ret = wl1271_build_qos_null_data(wl, wl12xx_wlvif_to_vif(wlvif));
++	if (ret < 0)
++		return ret;
++
++	wlcore_set_ssid(wl, wlvif);
++
++	set_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
++
++	return 0;
++}
++
++static int wlcore_clear_bssid(struct wl1271 *wl, struct wl12xx_vif *wlvif)
++{
++	int ret;
++
++	/* revert back to minimum rates for the current band */
++	wl1271_set_band_rate(wl, wlvif);
++	wlvif->basic_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
++
++	ret = wl1271_acx_sta_rate_policies(wl, wlvif);
++	if (ret < 0)
++		return ret;
++
++	if (wlvif->bss_type == BSS_TYPE_STA_BSS &&
++	    test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags)) {
++		ret = wl12xx_cmd_role_stop_sta(wl, wlvif);
++		if (ret < 0)
++			return ret;
++	}
++
++	clear_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
++	return 0;
++}
++/* STA/IBSS mode changes */
++static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
++					struct ieee80211_vif *vif,
++					struct ieee80211_bss_conf *bss_conf,
++					u32 changed)
++{
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	bool do_join = false;
++	bool is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS);
++	bool ibss_joined = false;
++	u32 sta_rate_set = 0;
++	int ret;
++	struct ieee80211_sta *sta;
++	bool sta_exists = false;
++	struct ieee80211_sta_ht_cap sta_ht_cap;
++
++	if (is_ibss) {
++		ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf,
++						     changed);
++		if (ret < 0)
++			goto out;
++	}
++
++	if (changed & BSS_CHANGED_IBSS) {
++		if (bss_conf->ibss_joined) {
++			set_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags);
++			ibss_joined = true;
++		} else {
++			wlcore_unset_assoc(wl, wlvif);
++			wl12xx_cmd_role_stop_sta(wl, wlvif);
++		}
++	}
++
++	if ((changed & BSS_CHANGED_BEACON_INT) && ibss_joined)
++		do_join = true;
++
++	/* Need to update the SSID (for filtering etc) */
++	if ((changed & BSS_CHANGED_BEACON) && ibss_joined)
++		do_join = true;
++
++	if ((changed & BSS_CHANGED_BEACON_ENABLED) && ibss_joined) {
++		wl1271_debug(DEBUG_ADHOC, "ad-hoc beaconing: %s",
++			     bss_conf->enable_beacon ? "enabled" : "disabled");
++
++		do_join = true;
++	}
++
++	if (changed & BSS_CHANGED_CQM) {
++		bool enable = false;
++		if (bss_conf->cqm_rssi_thold)
++			enable = true;
++		ret = wl1271_acx_rssi_snr_trigger(wl, wlvif, enable,
++						  bss_conf->cqm_rssi_thold,
++						  bss_conf->cqm_rssi_hyst);
++		if (ret < 0)
++			goto out;
++		wlvif->rssi_thold = bss_conf->cqm_rssi_thold;
++	}
++
++	if (changed & (BSS_CHANGED_BSSID | BSS_CHANGED_HT |
++		       BSS_CHANGED_ASSOC)) {
++		rcu_read_lock();
++		sta = ieee80211_find_sta(vif, bss_conf->bssid);
++		if (sta) {
++			u8 *rx_mask = sta->ht_cap.mcs.rx_mask;
++
++			/* save the supp_rates of the ap */
++			sta_rate_set = sta->supp_rates[wlvif->band];
++			if (sta->ht_cap.ht_supported)
++				sta_rate_set |=
++					(rx_mask[0] << HW_HT_RATES_OFFSET) |
++					(rx_mask[1] << HW_MIMO_RATES_OFFSET);
++			sta_ht_cap = sta->ht_cap;
++			sta_exists = true;
++		}
++
++		rcu_read_unlock();
++	}
++
++	if (changed & BSS_CHANGED_BSSID) {
++		if (!is_zero_ether_addr(bss_conf->bssid)) {
++			ret = wlcore_set_bssid(wl, wlvif, bss_conf,
++					       sta_rate_set);
++			if (ret < 0)
++				goto out;
++
++			/* Need to update the BSSID (for filtering etc) */
++			do_join = true;
++		} else {
++			ret = wlcore_clear_bssid(wl, wlvif);
++			if (ret < 0)
++				goto out;
++		}
++	}
++
++	if (changed & BSS_CHANGED_IBSS) {
++		wl1271_debug(DEBUG_ADHOC, "ibss_joined: %d",
++			     bss_conf->ibss_joined);
++
++		if (bss_conf->ibss_joined) {
++			u32 rates = bss_conf->basic_rates;
++			wlvif->basic_rate_set =
++				wl1271_tx_enabled_rates_get(wl, rates,
++							    wlvif->band);
++			wlvif->basic_rate =
++				wl1271_tx_min_rate_get(wl,
++						       wlvif->basic_rate_set);
++
++			/* by default, use 11b + OFDM rates */
++			wlvif->rate_set = CONF_TX_IBSS_DEFAULT_RATES;
++			ret = wl1271_acx_sta_rate_policies(wl, wlvif);
++			if (ret < 0)
++				goto out;
++		}
++	}
++
++	ret = wl1271_bss_erp_info_changed(wl, vif, bss_conf, changed);
++	if (ret < 0)
++		goto out;
++
++	if (do_join) {
++		ret = wlcore_join(wl, wlvif);
++		if (ret < 0) {
++			wl1271_warning("cmd join failed %d", ret);
++			goto out;
++		}
++	}
++
++	if (changed & BSS_CHANGED_ASSOC) {
++		if (bss_conf->assoc) {
++			ret = wlcore_set_assoc(wl, wlvif, bss_conf,
++					       sta_rate_set);
++			if (ret < 0)
++				goto out;
++
++			if (test_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags))
++				wl12xx_set_authorized(wl, wlvif);
++		} else {
++			wlcore_unset_assoc(wl, wlvif);
++		}
++	}
++
++	if (changed & BSS_CHANGED_PS) {
++		if ((bss_conf->ps) &&
++		    test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) &&
++		    !test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) {
++			int ps_mode;
++			char *ps_mode_str;
++
++			if (wl->conf.conn.forced_ps) {
++				ps_mode = STATION_POWER_SAVE_MODE;
++				ps_mode_str = "forced";
++			} else {
++				ps_mode = STATION_AUTO_PS_MODE;
++				ps_mode_str = "auto";
++			}
++
++			wl1271_debug(DEBUG_PSM, "%s ps enabled", ps_mode_str);
++
++			ret = wl1271_ps_set_mode(wl, wlvif, ps_mode);
++			if (ret < 0)
++				wl1271_warning("enter %s ps failed %d",
++					       ps_mode_str, ret);
++		} else if (!bss_conf->ps &&
++			   test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) {
++			wl1271_debug(DEBUG_PSM, "auto ps disabled");
++
++			ret = wl1271_ps_set_mode(wl, wlvif,
++						 STATION_ACTIVE_MODE);
++			if (ret < 0)
++				wl1271_warning("exit auto ps failed %d", ret);
++		}
++	}
++
++	/* Handle new association with HT. Do this after join. */
++	if (sta_exists &&
++	    (changed & BSS_CHANGED_HT)) {
++		bool enabled =
++			bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT;
++
++		ret = wlcore_hw_set_peer_cap(wl,
++					     &sta_ht_cap,
++					     enabled,
++					     wlvif->rate_set,
++					     wlvif->sta.hlid);
++		if (ret < 0) {
++			wl1271_warning("Set ht cap failed %d", ret);
++			goto out;
++
++		}
++
++		if (enabled) {
++			ret = wl1271_acx_set_ht_information(wl, wlvif,
++						bss_conf->ht_operation_mode);
++			if (ret < 0) {
++				wl1271_warning("Set ht information failed %d",
++					       ret);
++				goto out;
++			}
++		}
++	}
++
++	/* Handle arp filtering. Done after join. */
++	if ((changed & BSS_CHANGED_ARP_FILTER) ||
++	    (!is_ibss && (changed & BSS_CHANGED_QOS))) {
++		__be32 addr = bss_conf->arp_addr_list[0];
++		wlvif->sta.qos = bss_conf->qos;
++		WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS);
++
++		if (bss_conf->arp_addr_cnt == 1 && bss_conf->assoc) {
++			wlvif->ip_addr = addr;
++			/*
++			 * The template should have been configured only upon
++			 * association. however, it seems that the correct ip
++			 * isn't being set (when sending), so we have to
++			 * reconfigure the template upon every ip change.
++			 */
++			ret = wl1271_cmd_build_arp_rsp(wl, wlvif);
++			if (ret < 0) {
++				wl1271_warning("build arp rsp failed: %d", ret);
++				goto out;
++			}
++
++			ret = wl1271_acx_arp_ip_filter(wl, wlvif,
++				(ACX_ARP_FILTER_ARP_FILTERING |
++				 ACX_ARP_FILTER_AUTO_ARP),
++				addr);
++		} else {
++			wlvif->ip_addr = 0;
++			ret = wl1271_acx_arp_ip_filter(wl, wlvif, 0, addr);
++		}
++
++		if (ret < 0)
++			goto out;
++	}
++
++out:
++	return;
++}
++
++static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
++				       struct ieee80211_vif *vif,
++				       struct ieee80211_bss_conf *bss_conf,
++				       u32 changed)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
++	int ret;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 bss info role %d changed 0x%x",
++		     wlvif->role_id, (int)changed);
++
++	/*
++	 * make sure to cancel pending disconnections if our association
++	 * state changed
++	 */
++	if (!is_ap && (changed & BSS_CHANGED_ASSOC))
++		cancel_delayed_work_sync(&wlvif->connection_loss_work);
++
++	if (is_ap && (changed & BSS_CHANGED_BEACON_ENABLED) &&
++	    !bss_conf->enable_beacon)
++		wl1271_tx_flush(wl);
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	if (unlikely(!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	if (is_ap)
++		wl1271_bss_info_changed_ap(wl, vif, bss_conf, changed);
++	else
++		wl1271_bss_info_changed_sta(wl, vif, bss_conf, changed);
++
++	wl1271_ps_elp_sleep(wl);
++
++out:
++	mutex_unlock(&wl->mutex);
++}
++
++static int wlcore_op_add_chanctx(struct ieee80211_hw *hw,
++				 struct ieee80211_chanctx_conf *ctx)
++{
++	wl1271_debug(DEBUG_MAC80211, "mac80211 add chanctx %d (type %d)",
++		     ieee80211_frequency_to_channel(ctx->def.chan->center_freq),
++		     cfg80211_get_chandef_type(&ctx->def));
++	return 0;
++}
++
++static void wlcore_op_remove_chanctx(struct ieee80211_hw *hw,
++				     struct ieee80211_chanctx_conf *ctx)
++{
++	wl1271_debug(DEBUG_MAC80211, "mac80211 remove chanctx %d (type %d)",
++		     ieee80211_frequency_to_channel(ctx->def.chan->center_freq),
++		     cfg80211_get_chandef_type(&ctx->def));
++}
++
++static void wlcore_op_change_chanctx(struct ieee80211_hw *hw,
++				     struct ieee80211_chanctx_conf *ctx,
++				     u32 changed)
++{
++	wl1271_debug(DEBUG_MAC80211,
++		     "mac80211 change chanctx %d (type %d) changed 0x%x",
++		     ieee80211_frequency_to_channel(ctx->def.chan->center_freq),
++		     cfg80211_get_chandef_type(&ctx->def), changed);
++}
++
++static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw,
++					struct ieee80211_vif *vif,
++					struct ieee80211_chanctx_conf *ctx)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	int channel = ieee80211_frequency_to_channel(
++		ctx->def.chan->center_freq);
++
++	wl1271_debug(DEBUG_MAC80211,
++		     "mac80211 assign chanctx (role %d) %d (type %d)",
++		     wlvif->role_id, channel, cfg80211_get_chandef_type(&ctx->def));
++
++	mutex_lock(&wl->mutex);
++
++	wlvif->band = ctx->def.chan->band;
++	wlvif->channel = channel;
++	wlvif->channel_type = cfg80211_get_chandef_type(&ctx->def);
++
++	/* update default rates according to the band */
++	wl1271_set_band_rate(wl, wlvif);
++
++	mutex_unlock(&wl->mutex);
++
++	return 0;
++}
++
++static void wlcore_op_unassign_vif_chanctx(struct ieee80211_hw *hw,
++					   struct ieee80211_vif *vif,
++					   struct ieee80211_chanctx_conf *ctx)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++
++	wl1271_debug(DEBUG_MAC80211,
++		     "mac80211 unassign chanctx (role %d) %d (type %d)",
++		     wlvif->role_id,
++		     ieee80211_frequency_to_channel(ctx->def.chan->center_freq),
++		     cfg80211_get_chandef_type(&ctx->def));
++
++	wl1271_tx_flush(wl);
++}
++
++static int wl1271_op_conf_tx(struct ieee80211_hw *hw,
++			     struct ieee80211_vif *vif, u16 queue,
++			     const struct ieee80211_tx_queue_params *params)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	u8 ps_scheme;
++	int ret = 0;
++
++	mutex_lock(&wl->mutex);
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 conf tx %d", queue);
++
++	if (params->uapsd)
++		ps_scheme = CONF_PS_SCHEME_UPSD_TRIGGER;
++	else
++		ps_scheme = CONF_PS_SCHEME_LEGACY;
++
++	if (!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	/*
++	 * the txop is confed in units of 32us by the mac80211,
++	 * we need us
++	 */
++	ret = wl1271_acx_ac_cfg(wl, wlvif, wl1271_tx_get_queue(queue),
++				params->cw_min, params->cw_max,
++				params->aifs, params->txop << 5);
++	if (ret < 0)
++		goto out_sleep;
++
++	ret = wl1271_acx_tid_cfg(wl, wlvif, wl1271_tx_get_queue(queue),
++				 CONF_CHANNEL_TYPE_EDCF,
++				 wl1271_tx_get_queue(queue),
++				 ps_scheme, CONF_ACK_POLICY_LEGACY,
++				 0, 0);
++
++out_sleep:
++	wl1271_ps_elp_sleep(wl);
++
++out:
++	mutex_unlock(&wl->mutex);
++
++	return ret;
++}
++
++static u64 wl1271_op_get_tsf(struct ieee80211_hw *hw,
++			     struct ieee80211_vif *vif)
++{
++
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	u64 mactime = ULLONG_MAX;
++	int ret;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 get tsf");
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wl12xx_acx_tsf_info(wl, wlvif, &mactime);
++	if (ret < 0)
++		goto out_sleep;
++
++out_sleep:
++	wl1271_ps_elp_sleep(wl);
++
++out:
++	mutex_unlock(&wl->mutex);
++	return mactime;
++}
++
++static int wl1271_op_get_survey(struct ieee80211_hw *hw, int idx,
++				struct survey_info *survey)
++{
++	struct ieee80211_conf *conf = &hw->conf;
++
++	if (idx != 0)
++		return -ENOENT;
++
++	survey->channel = conf->chandef.chan;
++	survey->filled = 0;
++	return 0;
++}
++
++static int wl1271_allocate_sta(struct wl1271 *wl,
++			     struct wl12xx_vif *wlvif,
++			     struct ieee80211_sta *sta)
++{
++	struct wl1271_station *wl_sta;
++	int ret;
++
++
++	if (wl->active_sta_count >= AP_MAX_STATIONS) {
++		wl1271_warning("could not allocate HLID - too much stations");
++		return -EBUSY;
++	}
++
++	wl_sta = (struct wl1271_station *)sta->drv_priv;
++	ret = wl12xx_allocate_link(wl, wlvif, &wl_sta->hlid);
++	if (ret < 0) {
++		wl1271_warning("could not allocate HLID - too many links");
++		return -EBUSY;
++	}
++
++	/* use the previous security seq, if this is a recovery/resume */
++	wl->links[wl_sta->hlid].total_freed_pkts = wl_sta->total_freed_pkts;
++
++	set_bit(wl_sta->hlid, wlvif->ap.sta_hlid_map);
++	memcpy(wl->links[wl_sta->hlid].addr, sta->addr, ETH_ALEN);
++	wl->active_sta_count++;
++	return 0;
++}
++
++void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid)
++{
++	struct wl1271_station *wl_sta;
++	struct ieee80211_sta *sta;
++	struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
++
++	if (!test_bit(hlid, wlvif->ap.sta_hlid_map))
++		return;
++
++	clear_bit(hlid, wlvif->ap.sta_hlid_map);
++	__clear_bit(hlid, &wl->ap_ps_map);
++	__clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
++
++	/*
++	 * save the last used PN in the private part of iee80211_sta,
++	 * in case of recovery/suspend
++	 */
++	rcu_read_lock();
++	sta = ieee80211_find_sta(vif, wl->links[hlid].addr);
++	if (sta) {
++		wl_sta = (void *)sta->drv_priv;
++		wl_sta->total_freed_pkts = wl->links[hlid].total_freed_pkts;
++
++		/*
++		 * increment the initial seq number on recovery to account for
++		 * transmitted packets that we haven't yet got in the FW status
++		 */
++		if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags))
++			wl_sta->total_freed_pkts +=
++					WL1271_TX_SQN_POST_RECOVERY_PADDING;
++	}
++	rcu_read_unlock();
++
++	wl12xx_free_link(wl, wlvif, &hlid);
++	wl->active_sta_count--;
++
++	/*
++	 * rearm the tx watchdog when the last STA is freed - give the FW a
++	 * chance to return STA-buffered packets before complaining.
++	 */
++	if (wl->active_sta_count == 0)
++		wl12xx_rearm_tx_watchdog_locked(wl);
++}
++
++static int wl12xx_sta_add(struct wl1271 *wl,
++			  struct wl12xx_vif *wlvif,
++			  struct ieee80211_sta *sta)
++{
++	struct wl1271_station *wl_sta;
++	int ret = 0;
++	u8 hlid;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 add sta %d", (int)sta->aid);
++
++	ret = wl1271_allocate_sta(wl, wlvif, sta);
++	if (ret < 0)
++		return ret;
++
++	wl_sta = (struct wl1271_station *)sta->drv_priv;
++	hlid = wl_sta->hlid;
++
++	ret = wl12xx_cmd_add_peer(wl, wlvif, sta, hlid);
++	if (ret < 0)
++		wl1271_free_sta(wl, wlvif, hlid);
++
++	return ret;
++}
++
++static int wl12xx_sta_remove(struct wl1271 *wl,
++			     struct wl12xx_vif *wlvif,
++			     struct ieee80211_sta *sta)
++{
++	struct wl1271_station *wl_sta;
++	int ret = 0, id;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 remove sta %d", (int)sta->aid);
++
++	wl_sta = (struct wl1271_station *)sta->drv_priv;
++	id = wl_sta->hlid;
++	if (WARN_ON(!test_bit(id, wlvif->ap.sta_hlid_map)))
++		return -EINVAL;
++
++	ret = wl12xx_cmd_remove_peer(wl, wl_sta->hlid);
++	if (ret < 0)
++		return ret;
++
++	wl1271_free_sta(wl, wlvif, wl_sta->hlid);
++	return ret;
++}
++
++static void wlcore_roc_if_possible(struct wl1271 *wl,
++				   struct wl12xx_vif *wlvif)
++{
++	if (find_first_bit(wl->roc_map,
++			   WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES)
++		return;
++
++	if (WARN_ON(wlvif->role_id == WL12XX_INVALID_ROLE_ID))
++		return;
++
++	wl12xx_roc(wl, wlvif, wlvif->role_id, wlvif->band, wlvif->channel);
++}
++
++static void wlcore_update_inconn_sta(struct wl1271 *wl,
++				     struct wl12xx_vif *wlvif,
++				     struct wl1271_station *wl_sta,
++				     bool in_connection)
++{
++	if (in_connection) {
++		if (WARN_ON(wl_sta->in_connection))
++			return;
++		wl_sta->in_connection = true;
++		if (!wlvif->inconn_count++)
++			wlcore_roc_if_possible(wl, wlvif);
++	} else {
++		if (!wl_sta->in_connection)
++			return;
++
++		wl_sta->in_connection = false;
++		wlvif->inconn_count--;
++		if (WARN_ON(wlvif->inconn_count < 0))
++			return;
++
++		if (!wlvif->inconn_count)
++			if (test_bit(wlvif->role_id, wl->roc_map))
++				wl12xx_croc(wl, wlvif->role_id);
++	}
++}
++
++static int wl12xx_update_sta_state(struct wl1271 *wl,
++				   struct wl12xx_vif *wlvif,
++				   struct ieee80211_sta *sta,
++				   enum ieee80211_sta_state old_state,
++				   enum ieee80211_sta_state new_state)
++{
++	struct wl1271_station *wl_sta;
++	bool is_ap = wlvif->bss_type == BSS_TYPE_AP_BSS;
++	bool is_sta = wlvif->bss_type == BSS_TYPE_STA_BSS;
++	int ret;
++
++	wl_sta = (struct wl1271_station *)sta->drv_priv;
++
++	/* Add station (AP mode) */
++	if (is_ap &&
++	    old_state == IEEE80211_STA_NOTEXIST &&
++	    new_state == IEEE80211_STA_NONE) {
++		ret = wl12xx_sta_add(wl, wlvif, sta);
++		if (ret)
++			return ret;
++
++		wlcore_update_inconn_sta(wl, wlvif, wl_sta, true);
++	}
++
++	/* Remove station (AP mode) */
++	if (is_ap &&
++	    old_state == IEEE80211_STA_NONE &&
++	    new_state == IEEE80211_STA_NOTEXIST) {
++		/* must not fail */
++		wl12xx_sta_remove(wl, wlvif, sta);
++
++		wlcore_update_inconn_sta(wl, wlvif, wl_sta, false);
++	}
++
++	/* Authorize station (AP mode) */
++	if (is_ap &&
++	    new_state == IEEE80211_STA_AUTHORIZED) {
++		ret = wl12xx_cmd_set_peer_state(wl, wlvif, wl_sta->hlid);
++		if (ret < 0)
++			return ret;
++
++		ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, true,
++						     wl_sta->hlid);
++		if (ret)
++			return ret;
++
++		wlcore_update_inconn_sta(wl, wlvif, wl_sta, false);
++	}
++
++	/* Authorize station */
++	if (is_sta &&
++	    new_state == IEEE80211_STA_AUTHORIZED) {
++		set_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags);
++		ret = wl12xx_set_authorized(wl, wlvif);
++		if (ret)
++			return ret;
++	}
++
++	if (is_sta &&
++	    old_state == IEEE80211_STA_AUTHORIZED &&
++	    new_state == IEEE80211_STA_ASSOC) {
++		clear_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags);
++		clear_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags);
++	}
++
++	/* clear ROCs on failure or authorization */
++	if (is_sta &&
++	    (new_state == IEEE80211_STA_AUTHORIZED ||
++	     new_state == IEEE80211_STA_NOTEXIST)) {
++		if (test_bit(wlvif->role_id, wl->roc_map))
++			wl12xx_croc(wl, wlvif->role_id);
++	}
++
++	if (is_sta &&
++	    old_state == IEEE80211_STA_NOTEXIST &&
++	    new_state == IEEE80211_STA_NONE) {
++		if (find_first_bit(wl->roc_map,
++				   WL12XX_MAX_ROLES) >= WL12XX_MAX_ROLES) {
++			WARN_ON(wlvif->role_id == WL12XX_INVALID_ROLE_ID);
++			wl12xx_roc(wl, wlvif, wlvif->role_id,
++				   wlvif->band, wlvif->channel);
++		}
++	}
++	return 0;
++}
++
++static int wl12xx_op_sta_state(struct ieee80211_hw *hw,
++			       struct ieee80211_vif *vif,
++			       struct ieee80211_sta *sta,
++			       enum ieee80211_sta_state old_state,
++			       enum ieee80211_sta_state new_state)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	int ret;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 sta %d state=%d->%d",
++		     sta->aid, old_state, new_state);
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON)) {
++		ret = -EBUSY;
++		goto out;
++	}
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wl12xx_update_sta_state(wl, wlvif, sta, old_state, new_state);
++
++	wl1271_ps_elp_sleep(wl);
++out:
++	mutex_unlock(&wl->mutex);
++	if (new_state < old_state)
++		return 0;
++	return ret;
++}
++
++static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
++				  struct ieee80211_vif *vif,
++				  enum ieee80211_ampdu_mlme_action action,
++				  struct ieee80211_sta *sta, u16 tid, u16 *ssn,
++				  u8 buf_size)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	int ret;
++	u8 hlid, *ba_bitmap;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 ampdu action %d tid %d", action,
++		     tid);
++
++	/* sanity check - the fields in FW are only 8bits wide */
++	if (WARN_ON(tid > 0xFF))
++		return -ENOTSUPP;
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON)) {
++		ret = -EAGAIN;
++		goto out;
++	}
++
++	if (wlvif->bss_type == BSS_TYPE_STA_BSS) {
++		hlid = wlvif->sta.hlid;
++	} else if (wlvif->bss_type == BSS_TYPE_AP_BSS) {
++		struct wl1271_station *wl_sta;
++
++		wl_sta = (struct wl1271_station *)sta->drv_priv;
++		hlid = wl_sta->hlid;
++	} else {
++		ret = -EINVAL;
++		goto out;
++	}
++
++	ba_bitmap = &wl->links[hlid].ba_bitmap;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 ampdu: Rx tid %d action %d",
++		     tid, action);
++
++	switch (action) {
++	case IEEE80211_AMPDU_RX_START:
++		if (!wlvif->ba_support || !wlvif->ba_allowed) {
++			ret = -ENOTSUPP;
++			break;
++		}
++
++		if (wl->ba_rx_session_count >= wl->ba_rx_session_count_max) {
++			ret = -EBUSY;
++			wl1271_error("exceeded max RX BA sessions");
++			break;
++		}
++
++		if (*ba_bitmap & BIT(tid)) {
++			ret = -EINVAL;
++			wl1271_error("cannot enable RX BA session on active "
++				     "tid: %d", tid);
++			break;
++		}
++
++		ret = wl12xx_acx_set_ba_receiver_session(wl, tid, *ssn, true,
++							 hlid);
++		if (!ret) {
++			*ba_bitmap |= BIT(tid);
++			wl->ba_rx_session_count++;
++		}
++		break;
++
++	case IEEE80211_AMPDU_RX_STOP:
++		if (!(*ba_bitmap & BIT(tid))) {
++			/*
++			 * this happens on reconfig - so only output a debug
++			 * message for now, and don't fail the function.
++			 */
++			wl1271_debug(DEBUG_MAC80211,
++				     "no active RX BA session on tid: %d",
++				     tid);
++			ret = 0;
++			break;
++		}
++
++		ret = wl12xx_acx_set_ba_receiver_session(wl, tid, 0, false,
++							 hlid);
++		if (!ret) {
++			*ba_bitmap &= ~BIT(tid);
++			wl->ba_rx_session_count--;
++		}
++		break;
++
++	/*
++	 * The BA initiator session management in FW independently.
++	 * Falling break here on purpose for all TX APDU commands.
++	 */
++	case IEEE80211_AMPDU_TX_START:
++	case IEEE80211_AMPDU_TX_STOP_CONT:
++	case IEEE80211_AMPDU_TX_STOP_FLUSH:
++	case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
++	case IEEE80211_AMPDU_TX_OPERATIONAL:
++		ret = -EINVAL;
++		break;
++
++	default:
++		wl1271_error("Incorrect ampdu action id=%x\n", action);
++		ret = -EINVAL;
++	}
++
++	wl1271_ps_elp_sleep(wl);
++
++out:
++	mutex_unlock(&wl->mutex);
++
++	return ret;
++}
++
++static int wl12xx_set_bitrate_mask(struct ieee80211_hw *hw,
++				   struct ieee80211_vif *vif,
++				   const struct cfg80211_bitrate_mask *mask)
++{
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	struct wl1271 *wl = hw->priv;
++	int i, ret = 0;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 set_bitrate_mask 0x%x 0x%x",
++		mask->control[NL80211_BAND_2GHZ].legacy,
++		mask->control[NL80211_BAND_5GHZ].legacy);
++
++	mutex_lock(&wl->mutex);
++
++	for (i = 0; i < WLCORE_NUM_BANDS; i++)
++		wlvif->bitrate_masks[i] =
++			wl1271_tx_enabled_rates_get(wl,
++						    mask->control[i].legacy,
++						    i);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	if (wlvif->bss_type == BSS_TYPE_STA_BSS &&
++	    !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) {
++
++		ret = wl1271_ps_elp_wakeup(wl);
++		if (ret < 0)
++			goto out;
++
++		wl1271_set_band_rate(wl, wlvif);
++		wlvif->basic_rate =
++			wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
++		ret = wl1271_acx_sta_rate_policies(wl, wlvif);
++
++		wl1271_ps_elp_sleep(wl);
++	}
++out:
++	mutex_unlock(&wl->mutex);
++
++	return ret;
++}
++
++static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
++				     struct ieee80211_channel_switch *ch_switch)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif;
++	int ret;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 channel switch");
++
++	wl1271_tx_flush(wl);
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state == WLCORE_STATE_OFF)) {
++		wl12xx_for_each_wlvif_sta(wl, wlvif) {
++			struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
++			ieee80211_chswitch_done(vif, false);
++		}
++		goto out;
++	} else if (unlikely(wl->state != WLCORE_STATE_ON)) {
++		goto out;
++	}
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	/* TODO: change mac80211 to pass vif as param */
++	wl12xx_for_each_wlvif_sta(wl, wlvif) {
++		unsigned long delay_usec;
++
++		ret = wl->ops->channel_switch(wl, wlvif, ch_switch);
++		if (ret)
++			goto out_sleep;
++
++		set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags);
++
++		/* indicate failure 5 seconds after channel switch time */
++		delay_usec = ieee80211_tu_to_usec(wlvif->beacon_int) *
++			     ch_switch->count;
++		ieee80211_queue_delayed_work(hw, &wlvif->channel_switch_work,
++				usecs_to_jiffies(delay_usec) +
++				msecs_to_jiffies(5000));
++	}
++
++out_sleep:
++	wl1271_ps_elp_sleep(wl);
++
++out:
++	mutex_unlock(&wl->mutex);
++}
++
++static void wlcore_op_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
++{
++	struct wl1271 *wl = hw->priv;
++
++	wl1271_tx_flush(wl);
++}
++
++static int wlcore_op_remain_on_channel(struct ieee80211_hw *hw,
++				       struct ieee80211_vif *vif,
++				       struct ieee80211_channel *chan,
++				       int duration,
++				       enum ieee80211_roc_type type)
++{
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	struct wl1271 *wl = hw->priv;
++	int channel, ret = 0;
++
++	channel = ieee80211_frequency_to_channel(chan->center_freq);
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 roc %d (%d)",
++		     channel, wlvif->role_id);
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	/* return EBUSY if we can't ROC right now */
++	if (WARN_ON(wl->roc_vif ||
++		    find_first_bit(wl->roc_map,
++				   WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES)) {
++		ret = -EBUSY;
++		goto out;
++	}
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = wl12xx_start_dev(wl, wlvif, chan->band, channel);
++	if (ret < 0)
++		goto out_sleep;
++
++	wl->roc_vif = vif;
++	ieee80211_queue_delayed_work(hw, &wl->roc_complete_work,
++				     msecs_to_jiffies(duration));
++out_sleep:
++	wl1271_ps_elp_sleep(wl);
++out:
++	mutex_unlock(&wl->mutex);
++	return ret;
++}
++
++static int __wlcore_roc_completed(struct wl1271 *wl)
++{
++	struct wl12xx_vif *wlvif;
++	int ret;
++
++	/* already completed */
++	if (unlikely(!wl->roc_vif))
++		return 0;
++
++	wlvif = wl12xx_vif_to_data(wl->roc_vif);
++
++	if (!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))
++		return -EBUSY;
++
++	ret = wl12xx_stop_dev(wl, wlvif);
++	if (ret < 0)
++		return ret;
++
++	wl->roc_vif = NULL;
++
++	return 0;
++}
++
++static int wlcore_roc_completed(struct wl1271 *wl)
++{
++	int ret;
++
++	wl1271_debug(DEBUG_MAC80211, "roc complete");
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON)) {
++		ret = -EBUSY;
++		goto out;
++	}
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	ret = __wlcore_roc_completed(wl);
++
++	wl1271_ps_elp_sleep(wl);
++out:
++	mutex_unlock(&wl->mutex);
++
++	return ret;
++}
++
++static void wlcore_roc_complete_work(struct work_struct *work)
++{
++	struct delayed_work *dwork;
++	struct wl1271 *wl;
++	int ret;
++
++	dwork = container_of(work, struct delayed_work, work);
++	wl = container_of(dwork, struct wl1271, roc_complete_work);
++
++	ret = wlcore_roc_completed(wl);
++	if (!ret)
++		ieee80211_remain_on_channel_expired(wl->hw);
++}
++
++static int wlcore_op_cancel_remain_on_channel(struct ieee80211_hw *hw)
++{
++	struct wl1271 *wl = hw->priv;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 croc");
++
++	/* TODO: per-vif */
++	wl1271_tx_flush(wl);
++
++	/*
++	 * we can't just flush_work here, because it might deadlock
++	 * (as we might get called from the same workqueue)
++	 */
++	cancel_delayed_work_sync(&wl->roc_complete_work);
++	wlcore_roc_completed(wl);
++
++	return 0;
++}
++
++static void wlcore_op_sta_rc_update(struct ieee80211_hw *hw,
++				    struct ieee80211_vif *vif,
++				    struct ieee80211_sta *sta,
++				    u32 changed)
++{
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	struct wl1271 *wl = hw->priv;
++
++	wlcore_hw_sta_rc_update(wl, wlvif, sta, changed);
++}
++
++static int wlcore_op_get_rssi(struct ieee80211_hw *hw,
++			       struct ieee80211_vif *vif,
++			       struct ieee80211_sta *sta,
++			       s8 *rssi_dbm)
++{
++	struct wl1271 *wl = hw->priv;
++	struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
++	int ret = 0;
++
++	wl1271_debug(DEBUG_MAC80211, "mac80211 get_rssi");
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out_sleep;
++
++	ret = wlcore_acx_average_rssi(wl, wlvif, rssi_dbm);
++	if (ret < 0)
++		goto out_sleep;
++
++out_sleep:
++	wl1271_ps_elp_sleep(wl);
++
++out:
++	mutex_unlock(&wl->mutex);
++
++	return ret;
++}
++
++static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw)
++{
++	struct wl1271 *wl = hw->priv;
++	bool ret = false;
++
++	mutex_lock(&wl->mutex);
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	/* packets are considered pending if in the TX queue or the FW */
++	ret = (wl1271_tx_total_queue_count(wl) > 0) || (wl->tx_frames_cnt > 0);
++out:
++	mutex_unlock(&wl->mutex);
++
++	return ret;
++}
++
++/* can't be const, mac80211 writes to this */
++static struct ieee80211_rate wl1271_rates[] = {
++	{ .bitrate = 10,
++	  .hw_value = CONF_HW_BIT_RATE_1MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_1MBPS, },
++	{ .bitrate = 20,
++	  .hw_value = CONF_HW_BIT_RATE_2MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_2MBPS,
++	  .flags = IEEE80211_RATE_SHORT_PREAMBLE },
++	{ .bitrate = 55,
++	  .hw_value = CONF_HW_BIT_RATE_5_5MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_5_5MBPS,
++	  .flags = IEEE80211_RATE_SHORT_PREAMBLE },
++	{ .bitrate = 110,
++	  .hw_value = CONF_HW_BIT_RATE_11MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_11MBPS,
++	  .flags = IEEE80211_RATE_SHORT_PREAMBLE },
++	{ .bitrate = 60,
++	  .hw_value = CONF_HW_BIT_RATE_6MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
++	{ .bitrate = 90,
++	  .hw_value = CONF_HW_BIT_RATE_9MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
++	{ .bitrate = 120,
++	  .hw_value = CONF_HW_BIT_RATE_12MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
++	{ .bitrate = 180,
++	  .hw_value = CONF_HW_BIT_RATE_18MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
++	{ .bitrate = 240,
++	  .hw_value = CONF_HW_BIT_RATE_24MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
++	{ .bitrate = 360,
++	 .hw_value = CONF_HW_BIT_RATE_36MBPS,
++	 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
++	{ .bitrate = 480,
++	  .hw_value = CONF_HW_BIT_RATE_48MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
++	{ .bitrate = 540,
++	  .hw_value = CONF_HW_BIT_RATE_54MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
++};
++
++/* can't be const, mac80211 writes to this */
++static struct ieee80211_channel wl1271_channels[] = {
++	{ .hw_value = 1, .center_freq = 2412, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 2, .center_freq = 2417, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 3, .center_freq = 2422, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 4, .center_freq = 2427, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 5, .center_freq = 2432, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 6, .center_freq = 2437, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 7, .center_freq = 2442, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 8, .center_freq = 2447, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 9, .center_freq = 2452, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 10, .center_freq = 2457, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 11, .center_freq = 2462, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 12, .center_freq = 2467, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 13, .center_freq = 2472, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 14, .center_freq = 2484, .max_power = WLCORE_MAX_TXPWR },
++};
++
++/* can't be const, mac80211 writes to this */
++static struct ieee80211_supported_band wl1271_band_2ghz = {
++	.channels = wl1271_channels,
++	.n_channels = ARRAY_SIZE(wl1271_channels),
++	.bitrates = wl1271_rates,
++	.n_bitrates = ARRAY_SIZE(wl1271_rates),
++};
++
++/* 5 GHz data rates for WL1273 */
++static struct ieee80211_rate wl1271_rates_5ghz[] = {
++	{ .bitrate = 60,
++	  .hw_value = CONF_HW_BIT_RATE_6MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
++	{ .bitrate = 90,
++	  .hw_value = CONF_HW_BIT_RATE_9MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
++	{ .bitrate = 120,
++	  .hw_value = CONF_HW_BIT_RATE_12MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
++	{ .bitrate = 180,
++	  .hw_value = CONF_HW_BIT_RATE_18MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
++	{ .bitrate = 240,
++	  .hw_value = CONF_HW_BIT_RATE_24MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
++	{ .bitrate = 360,
++	 .hw_value = CONF_HW_BIT_RATE_36MBPS,
++	 .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
++	{ .bitrate = 480,
++	  .hw_value = CONF_HW_BIT_RATE_48MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
++	{ .bitrate = 540,
++	  .hw_value = CONF_HW_BIT_RATE_54MBPS,
++	  .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
++};
++
++/* 5 GHz band channels for WL1273 */
++static struct ieee80211_channel wl1271_channels_5ghz[] = {
++	{ .hw_value = 7, .center_freq = 5035, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 8, .center_freq = 5040, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 9, .center_freq = 5045, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 11, .center_freq = 5055, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 12, .center_freq = 5060, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 16, .center_freq = 5080, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 34, .center_freq = 5170, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 36, .center_freq = 5180, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 38, .center_freq = 5190, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 40, .center_freq = 5200, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 42, .center_freq = 5210, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 44, .center_freq = 5220, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 46, .center_freq = 5230, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 48, .center_freq = 5240, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 52, .center_freq = 5260, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 56, .center_freq = 5280, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 60, .center_freq = 5300, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 64, .center_freq = 5320, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 100, .center_freq = 5500, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 104, .center_freq = 5520, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 108, .center_freq = 5540, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 112, .center_freq = 5560, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 116, .center_freq = 5580, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 120, .center_freq = 5600, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 124, .center_freq = 5620, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 128, .center_freq = 5640, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 132, .center_freq = 5660, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 136, .center_freq = 5680, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 140, .center_freq = 5700, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 149, .center_freq = 5745, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 153, .center_freq = 5765, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 157, .center_freq = 5785, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 161, .center_freq = 5805, .max_power = WLCORE_MAX_TXPWR },
++	{ .hw_value = 165, .center_freq = 5825, .max_power = WLCORE_MAX_TXPWR },
++};
++
++static struct ieee80211_supported_band wl1271_band_5ghz = {
++	.channels = wl1271_channels_5ghz,
++	.n_channels = ARRAY_SIZE(wl1271_channels_5ghz),
++	.bitrates = wl1271_rates_5ghz,
++	.n_bitrates = ARRAY_SIZE(wl1271_rates_5ghz),
++};
++
++static const struct ieee80211_ops wl1271_ops = {
++	.start = wl1271_op_start,
++	.stop = wlcore_op_stop,
++	.add_interface = wl1271_op_add_interface,
++	.remove_interface = wl1271_op_remove_interface,
++	.change_interface = wl12xx_op_change_interface,
++#ifdef CONFIG_PM
++	.suspend = wl1271_op_suspend,
++	.resume = wl1271_op_resume,
++#endif
++	.config = wl1271_op_config,
++	.prepare_multicast = wl1271_op_prepare_multicast,
++	.configure_filter = wl1271_op_configure_filter,
++	.tx = wl1271_op_tx,
++	.set_key = wlcore_op_set_key,
++	.hw_scan = wl1271_op_hw_scan,
++	.cancel_hw_scan = wl1271_op_cancel_hw_scan,
++	.sched_scan_start = wl1271_op_sched_scan_start,
++	.sched_scan_stop = wl1271_op_sched_scan_stop,
++	.bss_info_changed = wl1271_op_bss_info_changed,
++	.set_frag_threshold = wl1271_op_set_frag_threshold,
++	.set_rts_threshold = wl1271_op_set_rts_threshold,
++	.conf_tx = wl1271_op_conf_tx,
++	.get_tsf = wl1271_op_get_tsf,
++	.get_survey = wl1271_op_get_survey,
++	.sta_state = wl12xx_op_sta_state,
++	.ampdu_action = wl1271_op_ampdu_action,
++	.tx_frames_pending = wl1271_tx_frames_pending,
++	.set_bitrate_mask = wl12xx_set_bitrate_mask,
++	.channel_switch = wl12xx_op_channel_switch,
++	.flush = wlcore_op_flush,
++	.remain_on_channel = wlcore_op_remain_on_channel,
++	.cancel_remain_on_channel = wlcore_op_cancel_remain_on_channel,
++	.add_chanctx = wlcore_op_add_chanctx,
++	.remove_chanctx = wlcore_op_remove_chanctx,
++	.change_chanctx = wlcore_op_change_chanctx,
++	.assign_vif_chanctx = wlcore_op_assign_vif_chanctx,
++	.unassign_vif_chanctx = wlcore_op_unassign_vif_chanctx,
++	.sta_rc_update = wlcore_op_sta_rc_update,
++	.get_rssi = wlcore_op_get_rssi,
++	CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
++};
++
++
++u8 wlcore_rate_to_idx(struct wl1271 *wl, u8 rate, enum ieee80211_band band)
++{
++	u8 idx;
++
++	BUG_ON(band >= 2);
++
++	if (unlikely(rate >= wl->hw_tx_rate_tbl_size)) {
++		wl1271_error("Illegal RX rate from HW: %d", rate);
++		return 0;
++	}
++
++	idx = wl->band_rate_to_idx[band][rate];
++	if (unlikely(idx == CONF_HW_RXTX_RATE_UNSUPPORTED)) {
++		wl1271_error("Unsupported RX rate from HW: %d", rate);
++		return 0;
++	}
++
++	return idx;
++}
++
++static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev,
++					       struct device_attribute *attr,
++					       char *buf)
++{
++	struct wl1271 *wl = dev_get_drvdata(dev);
++	ssize_t len;
++
++	len = PAGE_SIZE;
++
++	mutex_lock(&wl->mutex);
++	len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n",
++		       wl->sg_enabled);
++	mutex_unlock(&wl->mutex);
++
++	return len;
++
++}
++
++static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev,
++						struct device_attribute *attr,
++						const char *buf, size_t count)
++{
++	struct wl1271 *wl = dev_get_drvdata(dev);
++	unsigned long res;
++	int ret;
++
++	ret = kstrtoul(buf, 10, &res);
++	if (ret < 0) {
++		wl1271_warning("incorrect value written to bt_coex_mode");
++		return count;
++	}
++
++	mutex_lock(&wl->mutex);
++
++	res = !!res;
++
++	if (res == wl->sg_enabled)
++		goto out;
++
++	wl->sg_enabled = res;
++
++	if (unlikely(wl->state != WLCORE_STATE_ON))
++		goto out;
++
++	ret = wl1271_ps_elp_wakeup(wl);
++	if (ret < 0)
++		goto out;
++
++	wl1271_acx_sg_enable(wl, wl->sg_enabled);
++	wl1271_ps_elp_sleep(wl);
++
++ out:
++	mutex_unlock(&wl->mutex);
++	return count;
++}
++
++static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR,
++		   wl1271_sysfs_show_bt_coex_state,
++		   wl1271_sysfs_store_bt_coex_state);
++
++static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev,
++					   struct device_attribute *attr,
++					   char *buf)
++{
++	struct wl1271 *wl = dev_get_drvdata(dev);
++	ssize_t len;
++
++	len = PAGE_SIZE;
++
++	mutex_lock(&wl->mutex);
++	if (wl->hw_pg_ver >= 0)
++		len = snprintf(buf, len, "%d\n", wl->hw_pg_ver);
++	else
++		len = snprintf(buf, len, "n/a\n");
++	mutex_unlock(&wl->mutex);
++
++	return len;
++}
++
++static DEVICE_ATTR(hw_pg_ver, S_IRUGO,
++		   wl1271_sysfs_show_hw_pg_ver, NULL);
++
++static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj,
++				       struct bin_attribute *bin_attr,
++				       char *buffer, loff_t pos, size_t count)
++{
++	struct device *dev = container_of(kobj, struct device, kobj);
++	struct wl1271 *wl = dev_get_drvdata(dev);
++	ssize_t len;
++	int ret;
++
++	ret = mutex_lock_interruptible(&wl->mutex);
++	if (ret < 0)
++		return -ERESTARTSYS;
++
++	/* Let only one thread read the log at a time, blocking others */
++	while (wl->fwlog_size == 0) {
++		DEFINE_WAIT(wait);
++
++		prepare_to_wait_exclusive(&wl->fwlog_waitq,
++					  &wait,
++					  TASK_INTERRUPTIBLE);
++
++		if (wl->fwlog_size != 0) {
++			finish_wait(&wl->fwlog_waitq, &wait);
++			break;
++		}
++
++		mutex_unlock(&wl->mutex);
++
++		schedule();
++		finish_wait(&wl->fwlog_waitq, &wait);
++
++		if (signal_pending(current))
++			return -ERESTARTSYS;
++
++		ret = mutex_lock_interruptible(&wl->mutex);
++		if (ret < 0)
++			return -ERESTARTSYS;
++	}
++
++	/* Check if the fwlog is still valid */
++	if (wl->fwlog_size < 0) {
++		mutex_unlock(&wl->mutex);
++		return 0;
++	}
++
++	/* Seeking is not supported - old logs are not kept. Disregard pos. */
++	len = min(count, (size_t)wl->fwlog_size);
++	wl->fwlog_size -= len;
++	memcpy(buffer, wl->fwlog, len);
++
++	/* Make room for new messages */
++	memmove(wl->fwlog, wl->fwlog + len, wl->fwlog_size);
++
++	mutex_unlock(&wl->mutex);
++
++	return len;
++}
++
++static struct bin_attribute fwlog_attr = {
++	.attr = {.name = "fwlog", .mode = S_IRUSR},
++	.read = wl1271_sysfs_read_fwlog,
++};
++
++static void wl12xx_derive_mac_addresses(struct wl1271 *wl, u32 oui, u32 nic)
++{
++	int i;
++
++	wl1271_debug(DEBUG_PROBE, "base address: oui %06x nic %06x",
++		     oui, nic);
++
++	if (nic + WLCORE_NUM_MAC_ADDRESSES - wl->num_mac_addr > 0xffffff)
++		wl1271_warning("NIC part of the MAC address wraps around!");
++
++	for (i = 0; i < wl->num_mac_addr; i++) {
++		wl->addresses[i].addr[0] = (u8)(oui >> 16);
++		wl->addresses[i].addr[1] = (u8)(oui >> 8);
++		wl->addresses[i].addr[2] = (u8) oui;
++		wl->addresses[i].addr[3] = (u8)(nic >> 16);
++		wl->addresses[i].addr[4] = (u8)(nic >> 8);
++		wl->addresses[i].addr[5] = (u8) nic;
++		nic++;
++	}
++
++	/* we may be one address short at the most */
++	WARN_ON(wl->num_mac_addr + 1 < WLCORE_NUM_MAC_ADDRESSES);
++
++	/*
++	 * turn on the LAA bit in the first address and use it as
++	 * the last address.
++	 */
++	if (wl->num_mac_addr < WLCORE_NUM_MAC_ADDRESSES) {
++		int idx = WLCORE_NUM_MAC_ADDRESSES - 1;
++		memcpy(&wl->addresses[idx], &wl->addresses[0],
++		       sizeof(wl->addresses[0]));
++		/* LAA bit */
++		wl->addresses[idx].addr[2] |= BIT(1);
++	}
++
++	wl->hw->wiphy->n_addresses = WLCORE_NUM_MAC_ADDRESSES;
++	wl->hw->wiphy->addresses = wl->addresses;
++}
++
++static int wl12xx_get_hw_info(struct wl1271 *wl)
++{
++	int ret;
++
++	ret = wl12xx_set_power_on(wl);
++	if (ret < 0)
++		return ret;
++
++	ret = wlcore_read_reg(wl, REG_CHIP_ID_B, &wl->chip.id);
++	if (ret < 0)
++		goto out;
++
++	wl->fuse_oui_addr = 0;
++	wl->fuse_nic_addr = 0;
++
++	ret = wl->ops->get_pg_ver(wl, &wl->hw_pg_ver);
++	if (ret < 0)
++		goto out;
++
++	if (wl->ops->get_mac)
++		ret = wl->ops->get_mac(wl);
++
++out:
++	wl1271_power_off(wl);
++	return ret;
++}
++
++static int wl1271_register_hw(struct wl1271 *wl)
++{
++	int ret;
++	u32 oui_addr = 0, nic_addr = 0;
++
++	if (wl->mac80211_registered)
++		return 0;
++
++	if (wl->nvs_len >= 12) {
++		/* NOTE: The wl->nvs->nvs element must be first, in
++		 * order to simplify the casting, we assume it is at
++		 * the beginning of the wl->nvs structure.
++		 */
++		u8 *nvs_ptr = (u8 *)wl->nvs;
++
++		oui_addr =
++			(nvs_ptr[11] << 16) + (nvs_ptr[10] << 8) + nvs_ptr[6];
++		nic_addr =
++			(nvs_ptr[5] << 16) + (nvs_ptr[4] << 8) + nvs_ptr[3];
++	}
++
++	/* if the MAC address is zeroed in the NVS derive from fuse */
++	if (oui_addr == 0 && nic_addr == 0) {
++		oui_addr = wl->fuse_oui_addr;
++		/* fuse has the BD_ADDR, the WLAN addresses are the next two */
++		nic_addr = wl->fuse_nic_addr + 1;
++	}
++
++	wl12xx_derive_mac_addresses(wl, oui_addr, nic_addr);
++
++	ret = ieee80211_register_hw(wl->hw);
++	if (ret < 0) {
++		wl1271_error("unable to register mac80211 hw: %d", ret);
++		goto out;
++	}
++
++	wl->mac80211_registered = true;
++
++	wl1271_debugfs_init(wl);
++
++	wl1271_notice("loaded");
++
++out:
++	return ret;
++}
++
++static void wl1271_unregister_hw(struct wl1271 *wl)
++{
++	if (wl->plt)
++		wl1271_plt_stop(wl);
++
++	ieee80211_unregister_hw(wl->hw);
++	wl->mac80211_registered = false;
++
++}
++
++static const struct ieee80211_iface_limit wlcore_iface_limits[] = {
++	{
++		.max = 3,
++		.types = BIT(NL80211_IFTYPE_STATION),
++	},
++	{
++		.max = 1,
++		.types = BIT(NL80211_IFTYPE_AP) |
++			 BIT(NL80211_IFTYPE_P2P_GO) |
++			 BIT(NL80211_IFTYPE_P2P_CLIENT),
++	},
++};
++
++static struct ieee80211_iface_combination
++wlcore_iface_combinations[] = {
++	{
++	  .max_interfaces = 3,
++	  .limits = wlcore_iface_limits,
++	  .n_limits = ARRAY_SIZE(wlcore_iface_limits),
++	},
++};
++
++static int wl1271_init_ieee80211(struct wl1271 *wl)
++{
++	int i;
++	static const u32 cipher_suites[] = {
++		WLAN_CIPHER_SUITE_WEP40,
++		WLAN_CIPHER_SUITE_WEP104,
++		WLAN_CIPHER_SUITE_TKIP,
++		WLAN_CIPHER_SUITE_CCMP,
++		WL1271_CIPHER_SUITE_GEM,
++	};
++
++	/* The tx descriptor buffer */
++	wl->hw->extra_tx_headroom = sizeof(struct wl1271_tx_hw_descr);
++
++	if (wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE)
++		wl->hw->extra_tx_headroom += WL1271_EXTRA_SPACE_TKIP;
++
++	/* unit us */
++	/* FIXME: find a proper value */
++	wl->hw->channel_change_time = 10000;
++	wl->hw->max_listen_interval = wl->conf.conn.max_listen_interval;
++
++	wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
++		IEEE80211_HW_SUPPORTS_PS |
++		IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
++		IEEE80211_HW_SUPPORTS_UAPSD |
++		IEEE80211_HW_HAS_RATE_CONTROL |
++		IEEE80211_HW_CONNECTION_MONITOR |
++		IEEE80211_HW_REPORTS_TX_ACK_STATUS |
++		IEEE80211_HW_SPECTRUM_MGMT |
++		IEEE80211_HW_AP_LINK_PS |
++		IEEE80211_HW_AMPDU_AGGREGATION |
++		IEEE80211_HW_TX_AMPDU_SETUP_IN_HW |
++		IEEE80211_HW_QUEUE_CONTROL;
++
++	wl->hw->wiphy->cipher_suites = cipher_suites;
++	wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
++
++	wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
++		BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP) |
++		BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO);
++	wl->hw->wiphy->max_scan_ssids = 1;
++	wl->hw->wiphy->max_sched_scan_ssids = 16;
++	wl->hw->wiphy->max_match_sets = 16;
++	/*
++	 * Maximum length of elements in scanning probe request templates
++	 * should be the maximum length possible for a template, without
++	 * the IEEE80211 header of the template
++	 */
++	wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE -
++			sizeof(struct ieee80211_header);
++
++	wl->hw->wiphy->max_sched_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE -
++		sizeof(struct ieee80211_header);
++
++	wl->hw->wiphy->max_remain_on_channel_duration = 5000;
++
++	wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD |
++				WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
++
++	/* make sure all our channels fit in the scanned_ch bitmask */
++	BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) +
++		     ARRAY_SIZE(wl1271_channels_5ghz) >
++		     WL1271_MAX_CHANNELS);
++	/*
++	* clear channel flags from the previous usage
++	* and restore max_power & max_antenna_gain values.
++	*/
++	for (i = 0; i < ARRAY_SIZE(wl1271_channels); i++) {
++		wl1271_band_2ghz.channels[i].flags = 0;
++		wl1271_band_2ghz.channels[i].max_power = WLCORE_MAX_TXPWR;
++		wl1271_band_2ghz.channels[i].max_antenna_gain = 0;
++	}
++
++	for (i = 0; i < ARRAY_SIZE(wl1271_channels_5ghz); i++) {
++		wl1271_band_5ghz.channels[i].flags = 0;
++		wl1271_band_5ghz.channels[i].max_power = WLCORE_MAX_TXPWR;
++		wl1271_band_5ghz.channels[i].max_antenna_gain = 0;
++	}
++
++	/*
++	 * We keep local copies of the band structs because we need to
++	 * modify them on a per-device basis.
++	 */
++	memcpy(&wl->bands[IEEE80211_BAND_2GHZ], &wl1271_band_2ghz,
++	       sizeof(wl1271_band_2ghz));
++	memcpy(&wl->bands[IEEE80211_BAND_2GHZ].ht_cap,
++	       &wl->ht_cap[IEEE80211_BAND_2GHZ],
++	       sizeof(*wl->ht_cap));
++	memcpy(&wl->bands[IEEE80211_BAND_5GHZ], &wl1271_band_5ghz,
++	       sizeof(wl1271_band_5ghz));
++	memcpy(&wl->bands[IEEE80211_BAND_5GHZ].ht_cap,
++	       &wl->ht_cap[IEEE80211_BAND_5GHZ],
++	       sizeof(*wl->ht_cap));
++
++	wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
++		&wl->bands[IEEE80211_BAND_2GHZ];
++	wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
++		&wl->bands[IEEE80211_BAND_5GHZ];
++
++	/*
++	 * allow 4 queues per mac address we support +
++	 * 1 cab queue per mac + one global offchannel Tx queue
++	 */
++	wl->hw->queues = (NUM_TX_QUEUES + 1) * WLCORE_NUM_MAC_ADDRESSES + 1;
++
++	/* the last queue is the offchannel queue */
++	wl->hw->offchannel_tx_hw_queue = wl->hw->queues - 1;
++	wl->hw->max_rates = 1;
++
++	wl->hw->wiphy->reg_notifier = wl1271_reg_notify;
++
++	/* the FW answers probe-requests in AP-mode */
++	wl->hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
++	wl->hw->wiphy->probe_resp_offload =
++		NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
++		NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
++		NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
++
++	/* allowed interface combinations */
++	wlcore_iface_combinations[0].num_different_channels = wl->num_channels;
++	wl->hw->wiphy->iface_combinations = wlcore_iface_combinations;
++	wl->hw->wiphy->n_iface_combinations =
++		ARRAY_SIZE(wlcore_iface_combinations);
++
++	SET_IEEE80211_DEV(wl->hw, wl->dev);
++
++	wl->hw->sta_data_size = sizeof(struct wl1271_station);
++	wl->hw->vif_data_size = sizeof(struct wl12xx_vif);
++
++	wl->hw->max_rx_aggregation_subframes = wl->conf.ht.rx_ba_win_size;
++
++	return 0;
++}
++
++#define WL1271_DEFAULT_CHANNEL 0
++
++struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
++				     u32 mbox_size)
++{
++	struct ieee80211_hw *hw;
++	struct wl1271 *wl;
++	int i, j, ret;
++	unsigned int order;
++
++	BUILD_BUG_ON(AP_MAX_STATIONS > WL12XX_MAX_LINKS);
++
++	hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops);
++	if (!hw) {
++		wl1271_error("could not alloc ieee80211_hw");
++		ret = -ENOMEM;
++		goto err_hw_alloc;
++	}
++
++	wl = hw->priv;
++	memset(wl, 0, sizeof(*wl));
++
++	wl->priv = kzalloc(priv_size, GFP_KERNEL);
++	if (!wl->priv) {
++		wl1271_error("could not alloc wl priv");
++		ret = -ENOMEM;
++		goto err_priv_alloc;
++	}
++
++	INIT_LIST_HEAD(&wl->wlvif_list);
++
++	wl->hw = hw;
++
++	for (i = 0; i < NUM_TX_QUEUES; i++)
++		for (j = 0; j < WL12XX_MAX_LINKS; j++)
++			skb_queue_head_init(&wl->links[j].tx_queue[i]);
++
++	skb_queue_head_init(&wl->deferred_rx_queue);
++	skb_queue_head_init(&wl->deferred_tx_queue);
++
++	INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work);
++	INIT_WORK(&wl->netstack_work, wl1271_netstack_work);
++	INIT_WORK(&wl->tx_work, wl1271_tx_work);
++	INIT_WORK(&wl->recovery_work, wl1271_recovery_work);
++	INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work);
++	INIT_DELAYED_WORK(&wl->roc_complete_work, wlcore_roc_complete_work);
++	INIT_DELAYED_WORK(&wl->tx_watchdog_work, wl12xx_tx_watchdog_work);
++
++	wl->freezable_wq = create_freezable_workqueue("wl12xx_wq");
++	if (!wl->freezable_wq) {
++		ret = -ENOMEM;
++		goto err_hw;
++	}
++
++	wl->channel = WL1271_DEFAULT_CHANNEL;
++	wl->rx_counter = 0;
++	wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
++	wl->band = IEEE80211_BAND_2GHZ;
++	wl->channel_type = NL80211_CHAN_NO_HT;
++	wl->flags = 0;
++	wl->sg_enabled = true;
++	wl->sleep_auth = WL1271_PSM_ILLEGAL;
++	wl->recovery_count = 0;
++	wl->hw_pg_ver = -1;
++	wl->ap_ps_map = 0;
++	wl->ap_fw_ps_map = 0;
++	wl->quirks = 0;
++	wl->platform_quirks = 0;
++	wl->system_hlid = WL12XX_SYSTEM_HLID;
++	wl->active_sta_count = 0;
++	wl->active_link_count = 0;
++	wl->fwlog_size = 0;
++	init_waitqueue_head(&wl->fwlog_waitq);
++
++	/* The system link is always allocated */
++	__set_bit(WL12XX_SYSTEM_HLID, wl->links_map);
++
++	memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map));
++	for (i = 0; i < wl->num_tx_desc; i++)
++		wl->tx_frames[i] = NULL;
++
++	spin_lock_init(&wl->wl_lock);
++
++	wl->state = WLCORE_STATE_OFF;
++	wl->fw_type = WL12XX_FW_TYPE_NONE;
++	mutex_init(&wl->mutex);
++	mutex_init(&wl->flush_mutex);
++	init_completion(&wl->nvs_loading_complete);
++
++	order = get_order(aggr_buf_size);
++	wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order);
++	if (!wl->aggr_buf) {
++		ret = -ENOMEM;
++		goto err_wq;
++	}
++	wl->aggr_buf_size = aggr_buf_size;
++
++	wl->dummy_packet = wl12xx_alloc_dummy_packet(wl);
++	if (!wl->dummy_packet) {
++		ret = -ENOMEM;
++		goto err_aggr;
++	}
++
++	/* Allocate one page for the FW log */
++	wl->fwlog = (u8 *)get_zeroed_page(GFP_KERNEL);
++	if (!wl->fwlog) {
++		ret = -ENOMEM;
++		goto err_dummy_packet;
++	}
++
++	wl->mbox_size = mbox_size;
++	wl->mbox = kmalloc(wl->mbox_size, GFP_KERNEL | GFP_DMA);
++	if (!wl->mbox) {
++		ret = -ENOMEM;
++		goto err_fwlog;
++	}
++
++	wl->buffer_32 = kmalloc(sizeof(*wl->buffer_32), GFP_KERNEL);
++	if (!wl->buffer_32) {
++		ret = -ENOMEM;
++		goto err_mbox;
++	}
++
++	return hw;
++
++err_mbox:
++	kfree(wl->mbox);
++
++err_fwlog:
++	free_page((unsigned long)wl->fwlog);
++
++err_dummy_packet:
++	dev_kfree_skb(wl->dummy_packet);
++
++err_aggr:
++	free_pages((unsigned long)wl->aggr_buf, order);
++
++err_wq:
++	destroy_workqueue(wl->freezable_wq);
++
++err_hw:
++	wl1271_debugfs_exit(wl);
++	kfree(wl->priv);
++
++err_priv_alloc:
++	ieee80211_free_hw(hw);
++
++err_hw_alloc:
++
++	return ERR_PTR(ret);
++}
++EXPORT_SYMBOL_GPL(wlcore_alloc_hw);
++
++int wlcore_free_hw(struct wl1271 *wl)
++{
++	/* Unblock any fwlog readers */
++	mutex_lock(&wl->mutex);
++	wl->fwlog_size = -1;
++	wake_up_interruptible_all(&wl->fwlog_waitq);
++	mutex_unlock(&wl->mutex);
++
++	device_remove_bin_file(wl->dev, &fwlog_attr);
++
++	device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
++
++	device_remove_file(wl->dev, &dev_attr_bt_coex_state);
++	kfree(wl->buffer_32);
++	kfree(wl->mbox);
++	free_page((unsigned long)wl->fwlog);
++	dev_kfree_skb(wl->dummy_packet);
++	free_pages((unsigned long)wl->aggr_buf, get_order(wl->aggr_buf_size));
++
++	wl1271_debugfs_exit(wl);
++
++	vfree(wl->fw);
++	wl->fw = NULL;
++	wl->fw_type = WL12XX_FW_TYPE_NONE;
++	kfree(wl->nvs);
++	wl->nvs = NULL;
++
++	kfree(wl->fw_status_1);
++	kfree(wl->tx_res_if);
++	destroy_workqueue(wl->freezable_wq);
++
++	kfree(wl->priv);
++	ieee80211_free_hw(wl->hw);
++
++	return 0;
++}
++EXPORT_SYMBOL_GPL(wlcore_free_hw);
++
++static void wlcore_nvs_cb(const struct firmware *fw, void *context)
++{
++	struct wl1271 *wl = context;
++	struct platform_device *pdev = wl->pdev;
++	struct wlcore_platdev_data *pdev_data = pdev->dev.platform_data;
++	struct wl12xx_platform_data *pdata = pdev_data->pdata;
++	unsigned long irqflags;
++	int ret;
++
++	if (fw) {
++		wl->nvs = kmemdup(fw->data, fw->size, GFP_KERNEL);
++		if (!wl->nvs) {
++			wl1271_error("Could not allocate nvs data");
++			goto out;
++		}
++		wl->nvs_len = fw->size;
++	} else {
++		wl1271_debug(DEBUG_BOOT, "Could not get nvs file %s",
++			     WL12XX_NVS_NAME);
++		wl->nvs = NULL;
++		wl->nvs_len = 0;
++	}
++
++	ret = wl->ops->setup(wl);
++	if (ret < 0)
++		goto out_free_nvs;
++
++	BUG_ON(wl->num_tx_desc > WLCORE_MAX_TX_DESCRIPTORS);
++
++	/* adjust some runtime configuration parameters */
++	wlcore_adjust_conf(wl);
++
++	wl->irq = platform_get_irq(pdev, 0);
++	wl->platform_quirks = pdata->platform_quirks;
++	wl->if_ops = pdev_data->if_ops;
++
++	if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ)
++		irqflags = IRQF_TRIGGER_RISING;
++	else
++		irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT;
++
++	ret = request_threaded_irq(wl->irq, NULL, wlcore_irq,
++				   irqflags, pdev->name, wl);
++	if (ret < 0) {
++		wl1271_error("request_irq() failed: %d", ret);
++		goto out_free_nvs;
++	}
++
++#ifdef CONFIG_PM
++	ret = enable_irq_wake(wl->irq);
++	if (!ret) {
++		wl->irq_wake_enabled = true;
++		device_init_wakeup(wl->dev, 1);
++		if (pdev_data->pwr_in_suspend) {
++			wl->hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY;
++			wl->hw->wiphy->wowlan.n_patterns =
++				WL1271_MAX_RX_FILTERS;
++			wl->hw->wiphy->wowlan.pattern_min_len = 1;
++			wl->hw->wiphy->wowlan.pattern_max_len =
++				WL1271_RX_FILTER_MAX_PATTERN_SIZE;
++		}
++	}
++#endif
++	disable_irq(wl->irq);
++
++	ret = wl12xx_get_hw_info(wl);
++	if (ret < 0) {
++		wl1271_error("couldn't get hw info");
++		goto out_irq;
++	}
++
++	ret = wl->ops->identify_chip(wl);
++	if (ret < 0)
++		goto out_irq;
++
++	ret = wl1271_init_ieee80211(wl);
++	if (ret)
++		goto out_irq;
++
++	ret = wl1271_register_hw(wl);
++	if (ret)
++		goto out_irq;
++
++	/* Create sysfs file to control bt coex state */
++	ret = device_create_file(wl->dev, &dev_attr_bt_coex_state);
++	if (ret < 0) {
++		wl1271_error("failed to create sysfs file bt_coex_state");
++		goto out_unreg;
++	}
++
++	/* Create sysfs file to get HW PG version */
++	ret = device_create_file(wl->dev, &dev_attr_hw_pg_ver);
++	if (ret < 0) {
++		wl1271_error("failed to create sysfs file hw_pg_ver");
++		goto out_bt_coex_state;
++	}
++
++	/* Create sysfs file for the FW log */
++	ret = device_create_bin_file(wl->dev, &fwlog_attr);
++	if (ret < 0) {
++		wl1271_error("failed to create sysfs file fwlog");
++		goto out_hw_pg_ver;
++	}
++
++	wl->initialized = true;
++	goto out;
++
++out_hw_pg_ver:
++	device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
++
++out_bt_coex_state:
++	device_remove_file(wl->dev, &dev_attr_bt_coex_state);
++
++out_unreg:
++	wl1271_unregister_hw(wl);
++
++out_irq:
++	free_irq(wl->irq, wl);
++
++out_free_nvs:
++	kfree(wl->nvs);
++
++out:
++	release_firmware(fw);
++	complete_all(&wl->nvs_loading_complete);
++}
++
++int wlcore_probe(struct wl1271 *wl, struct platform_device *pdev)
++{
++	int ret;
++
++	if (!wl->ops || !wl->ptable)
++		return -EINVAL;
++
++	wl->dev = &pdev->dev;
++	wl->pdev = pdev;
++	platform_set_drvdata(pdev, wl);
++
++	ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
++				      WL12XX_NVS_NAME, &pdev->dev, GFP_KERNEL,
++				      wl, wlcore_nvs_cb);
++	if (ret < 0) {
++		wl1271_error("request_firmware_nowait failed: %d", ret);
++		complete_all(&wl->nvs_loading_complete);
++	}
++
++	return ret;
++}
++EXPORT_SYMBOL_GPL(wlcore_probe);
++
++int wlcore_remove(struct platform_device *pdev)
++{
++	struct wl1271 *wl = platform_get_drvdata(pdev);
++
++	wait_for_completion(&wl->nvs_loading_complete);
++	if (!wl->initialized)
++		return 0;
++
++	if (wl->irq_wake_enabled) {
++		device_init_wakeup(wl->dev, 0);
++		disable_irq_wake(wl->irq);
++	}
++	wl1271_unregister_hw(wl);
++	free_irq(wl->irq, wl);
++	wlcore_free_hw(wl);
++
++	return 0;
++}
++EXPORT_SYMBOL_GPL(wlcore_remove);
++
++u32 wl12xx_debug_level = DEBUG_NONE;
++EXPORT_SYMBOL_GPL(wl12xx_debug_level);
++module_param_named(debug_level, wl12xx_debug_level, uint, S_IRUSR | S_IWUSR);
++MODULE_PARM_DESC(debug_level, "wl12xx debugging level");
++
++module_param_named(fwlog, fwlog_param, charp, 0);
++MODULE_PARM_DESC(fwlog,
++		 "FW logger options: continuous, ondemand, dbgpins or disable");
++
++module_param(bug_on_recovery, int, S_IRUSR | S_IWUSR);
++MODULE_PARM_DESC(bug_on_recovery, "BUG() on fw recovery");
++
++module_param(no_recovery, int, S_IRUSR | S_IWUSR);
++MODULE_PARM_DESC(no_recovery, "Prevent HW recovery. FW will remain stuck.");
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Luciano Coelho <coelho at ti.com>");
++MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen at nokia.com>");
++MODULE_FIRMWARE(WL12XX_NVS_NAME);
+diff -Nuar git.old/drivers/net/wireless/ti/wlcore/sdio.c git/drivers/net/wireless/ti/wlcore/sdio.c
+--- git.old/drivers/net/wireless/ti/wlcore/sdio.c	2015-03-04 16:35:05.722185997 +0100
++++ git/drivers/net/wireless/ti/wlcore/sdio.c	2015-03-04 17:07:29.614167458 +0100
+@@ -30,10 +30,11 @@
+ #include <linux/mmc/sdio_ids.h>
+ #include <linux/mmc/card.h>
+ #include <linux/mmc/host.h>
+-#include <linux/gpio.h>
++#include <linux/of_irq.h>
+ #include <linux/wl12xx.h>
+ #include <linux/pm_runtime.h>
+ #include <linux/printk.h>
++#include <linux/clk-provider.h>
+ 
+ #include "wlcore.h"
+ #include "wl12xx_80211.h"
+@@ -52,6 +53,7 @@
+ struct wl12xx_sdio_glue {
+ 	struct device *dev;
+ 	struct platform_device *core;
++	struct clk *refclock, *tcxoclock;
+ };
+ 
+ static const struct sdio_device_id wl1271_devices[] = {
+@@ -214,6 +216,68 @@
+ 	.set_block_size = wl1271_sdio_set_block_size,
+ };
+ 
++static const struct of_device_id wlcore_sdio_of_clk_match_table[] = {
++	{ .compatible = "ti,wilink-clock" },
++};
++
++static struct wl12xx_platform_data *wlcore_get_pdata_from_of(struct device *dev)
++{
++	struct wl12xx_platform_data *pdata;
++	struct device_node *np = dev->of_node;
++	struct device_node *clock_node;
++	struct wl12xx_sdio_glue *glue = sdio_get_drvdata(dev_to_sdio_func(dev));
++
++	if (!np) {
++		np = of_find_matching_node(NULL, dev->driver->of_match_table);
++		if (!np) {
++			dev_notice(dev, "device tree node not available\n");
++			pdata = ERR_PTR(-ENODEV);
++			goto out;
++		}
++	}
++
++	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
++	if (!pdata) {
++		dev_err(dev, "can't allocate platform data\n");
++		pdata = ERR_PTR(-ENODEV);
++		goto out;
++	}
++
++	pdata->irq = irq_of_parse_and_map(np, 0);
++	if (pdata->irq < 0) {
++		dev_err(dev, "can't get interrupt gpio from the device tree\n");
++		goto out_free;
++	}
++
++	for_each_matching_node(clock_node, wlcore_sdio_of_clk_match_table)
++		of_fixed_clk_setup(clock_node);
++
++	glue->refclock = of_clk_get_by_name(np, "refclock");
++	if (IS_ERR(glue->refclock)) {
++		glue->refclock = NULL;
++	} else {
++		clk_prepare_enable(glue->refclock);
++		pdata->ref_clock_freq = clk_get_rate(glue->refclock);
++	}
++
++	glue->tcxoclock = of_clk_get_by_name(np, "tcxoclock");
++	if (IS_ERR(glue->tcxoclock)) {
++		glue->tcxoclock = NULL;
++	} else {
++		clk_prepare_enable(glue->tcxoclock);
++		pdata->ref_clock_freq = clk_get_rate(glue->tcxoclock);
++	}
++
++	goto out;
++
++out_free:
++	kfree(pdata);
++	pdata = ERR_PTR(-ENODEV);
++
++out:
++	return pdata;
++}
++
+ static int wl1271_probe(struct sdio_func *func,
+ 				  const struct sdio_device_id *id)
+ {
+@@ -248,11 +312,24 @@
+ 	/* Use block mode for transferring over one block size of data */
+ 	func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE;
+ 
++	sdio_set_drvdata(func, glue);
++
++	/* The pdata allocated here is freed when the device is freed,
++	 * so we don't need an additional out label to free it in case
++	 * of error further on.
++	 */
++
++	/* Try to get legacy platform data from the board file */
+ 	pdev_data->pdata = wl12xx_get_platform_data();
+ 	if (IS_ERR(pdev_data->pdata)) {
+-		ret = PTR_ERR(pdev_data->pdata);
+-		dev_err(glue->dev, "missing wlan platform data: %d\n", ret);
+-		goto out_free_glue;
++		dev_info(&func->dev,
++			 "legacy platform data not found, trying device tree\n");
++
++		pdev_data->pdata = wlcore_get_pdata_from_of(&func->dev);
++		if (IS_ERR(pdev_data->pdata)) {
++			dev_err(&func->dev, "can't get platform data\n");
++			goto out_free_glue;
++		}
+ 	}
+ 
+ 	/* if sdio can keep power while host is suspended, enable wow */
+@@ -260,9 +337,7 @@
+ 	dev_dbg(glue->dev, "sdio PM caps = 0x%x\n", mmcflags);
+ 
+ 	if (mmcflags & MMC_PM_KEEP_POWER)
+-		pdev_data->pdata->pwr_in_suspend = true;
+-
+-	sdio_set_drvdata(func, glue);
++		pdev_data->pwr_in_suspend = true;
+ 
+ 	/* Tell PM core that we don't need the card to be powered now */
+ 	pm_runtime_put_noidle(&func->dev);
+@@ -330,6 +405,16 @@
+ {
+ 	struct wl12xx_sdio_glue *glue = sdio_get_drvdata(func);
+ 
++	if (glue->refclock) {
++		clk_disable_unprepare(glue->refclock);
++		clk_put(glue->refclock);
++	}
++
++	if (glue->tcxoclock) {
++		clk_disable_unprepare(glue->tcxoclock);
++		clk_put(glue->tcxoclock);
++	}
++
+ 	/* Undo decrement done above in wl1271_probe */
+ 	pm_runtime_get_noresume(&func->dev);
+ 
+@@ -386,16 +471,25 @@
+ };
+ #endif
+ 
++static const struct of_device_id wlcore_sdio_of_match_table[] = {
++	{ .compatible = "ti,wilink6" },
++	{ .compatible = "ti,wilink7" },
++	{ .compatible = "ti,wilink8" },
++	{ }
++};
++MODULE_DEVICE_TABLE(of, wlcore_sdio_of_match_table);
++
+ static struct sdio_driver wl1271_sdio_driver = {
+ 	.name		= "wl1271_sdio",
+ 	.id_table	= wl1271_devices,
+ 	.probe		= wl1271_probe,
+ 	.remove		= wl1271_remove,
+-#ifdef CONFIG_PM
+ 	.drv = {
++#ifdef CONFIG_PM
+ 		.pm = &wl1271_sdio_pm_ops,
+-	},
+ #endif
++		.of_match_table = of_match_ptr(wlcore_sdio_of_match_table),
++	},
+ };
+ 
+ static int __init wl1271_init(void)
+diff -Nuar git.old/drivers/net/wireless/ti/wlcore/sdio.c.orig git/drivers/net/wireless/ti/wlcore/sdio.c.orig
+--- git.old/drivers/net/wireless/ti/wlcore/sdio.c.orig	1970-01-01 01:00:00.000000000 +0100
++++ git/drivers/net/wireless/ti/wlcore/sdio.c.orig	2015-03-04 16:37:26.010184659 +0100
+@@ -0,0 +1,419 @@
++/*
++ * This file is part of wl1271
++ *
++ * Copyright (C) 2009-2010 Nokia Corporation
++ *
++ * Contact: Luciano Coelho <luciano.coelho at nokia.com>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++ * 02110-1301 USA
++ *
++ */
++
++#include <linux/irq.h>
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/platform_device.h>
++#include <linux/mmc/sdio.h>
++#include <linux/mmc/sdio_func.h>
++#include <linux/mmc/sdio_ids.h>
++#include <linux/mmc/card.h>
++#include <linux/mmc/host.h>
++#include <linux/gpio.h>
++#include <linux/wl12xx.h>
++#include <linux/pm_runtime.h>
++#include <linux/printk.h>
++
++#include "wlcore.h"
++#include "wl12xx_80211.h"
++#include "io.h"
++
++#ifndef SDIO_VENDOR_ID_TI
++#define SDIO_VENDOR_ID_TI		0x0097
++#endif
++
++#ifndef SDIO_DEVICE_ID_TI_WL1271
++#define SDIO_DEVICE_ID_TI_WL1271	0x4076
++#endif
++
++static bool dump = false;
++
++struct wl12xx_sdio_glue {
++	struct device *dev;
++	struct platform_device *core;
++};
++
++static const struct sdio_device_id wl1271_devices[] = {
++	{ SDIO_DEVICE(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271) },
++	{}
++};
++MODULE_DEVICE_TABLE(sdio, wl1271_devices);
++
++static void wl1271_sdio_set_block_size(struct device *child,
++				       unsigned int blksz)
++{
++	struct wl12xx_sdio_glue *glue = dev_get_drvdata(child->parent);
++	struct sdio_func *func = dev_to_sdio_func(glue->dev);
++
++	sdio_claim_host(func);
++	sdio_set_block_size(func, blksz);
++	sdio_release_host(func);
++}
++
++static int __must_check wl12xx_sdio_raw_read(struct device *child, int addr,
++					     void *buf, size_t len, bool fixed)
++{
++	int ret;
++	struct wl12xx_sdio_glue *glue = dev_get_drvdata(child->parent);
++	struct sdio_func *func = dev_to_sdio_func(glue->dev);
++
++	sdio_claim_host(func);
++
++	if (unlikely(dump)) {
++		printk(KERN_DEBUG "wlcore_sdio: READ from 0x%04x\n", addr);
++		print_hex_dump(KERN_DEBUG, "wlcore_sdio: READ ",
++				DUMP_PREFIX_OFFSET, 16, 1,
++				buf, len, false);
++	}
++
++	if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG)) {
++		((u8 *)buf)[0] = sdio_f0_readb(func, addr, &ret);
++		dev_dbg(child->parent, "sdio read 52 addr 0x%x, byte 0x%02x\n",
++			addr, ((u8 *)buf)[0]);
++	} else {
++		if (fixed)
++			ret = sdio_readsb(func, buf, addr, len);
++		else
++			ret = sdio_memcpy_fromio(func, buf, addr, len);
++
++		dev_dbg(child->parent, "sdio read 53 addr 0x%x, %zu bytes\n",
++			addr, len);
++	}
++
++	sdio_release_host(func);
++
++	if (WARN_ON(ret))
++		dev_err(child->parent, "sdio read failed (%d)\n", ret);
++
++	return ret;
++}
++
++static int __must_check wl12xx_sdio_raw_write(struct device *child, int addr,
++					      void *buf, size_t len, bool fixed)
++{
++	int ret;
++	struct wl12xx_sdio_glue *glue = dev_get_drvdata(child->parent);
++	struct sdio_func *func = dev_to_sdio_func(glue->dev);
++
++	sdio_claim_host(func);
++
++	if (unlikely(dump)) {
++		printk(KERN_DEBUG "wlcore_sdio: WRITE to 0x%04x\n", addr);
++		print_hex_dump(KERN_DEBUG, "wlcore_sdio: WRITE ",
++				DUMP_PREFIX_OFFSET, 16, 1,
++				buf, len, false);
++	}
++
++	if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG)) {
++		sdio_f0_writeb(func, ((u8 *)buf)[0], addr, &ret);
++		dev_dbg(child->parent, "sdio write 52 addr 0x%x, byte 0x%02x\n",
++			addr, ((u8 *)buf)[0]);
++	} else {
++		dev_dbg(child->parent, "sdio write 53 addr 0x%x, %zu bytes\n",
++			addr, len);
++
++		if (fixed)
++			ret = sdio_writesb(func, addr, buf, len);
++		else
++			ret = sdio_memcpy_toio(func, addr, buf, len);
++	}
++
++	sdio_release_host(func);
++
++	if (WARN_ON(ret))
++		dev_err(child->parent, "sdio write failed (%d)\n", ret);
++
++	return ret;
++}
++
++static int wl12xx_sdio_power_on(struct wl12xx_sdio_glue *glue)
++{
++	int ret;
++	struct sdio_func *func = dev_to_sdio_func(glue->dev);
++	struct mmc_card *card = func->card;
++
++	ret = pm_runtime_get_sync(&card->dev);
++	if (ret) {
++		/*
++		 * Runtime PM might be temporarily disabled, or the device
++		 * might have a positive reference counter. Make sure it is
++		 * really powered on.
++		 */
++		ret = mmc_power_restore_host(card->host);
++		if (ret < 0) {
++			pm_runtime_put_sync(&card->dev);
++			goto out;
++		}
++	}
++
++	sdio_claim_host(func);
++	sdio_enable_func(func);
++	sdio_release_host(func);
++
++out:
++	return ret;
++}
++
++static int wl12xx_sdio_power_off(struct wl12xx_sdio_glue *glue)
++{
++	int ret;
++	struct sdio_func *func = dev_to_sdio_func(glue->dev);
++	struct mmc_card *card = func->card;
++
++	sdio_claim_host(func);
++	sdio_disable_func(func);
++	sdio_release_host(func);
++
++	/* Power off the card manually in case it wasn't powered off above */
++	ret = mmc_power_save_host(card->host);
++	if (ret < 0)
++		goto out;
++
++	/* Let runtime PM know the card is powered off */
++	pm_runtime_put_sync(&card->dev);
++
++out:
++	return ret;
++}
++
++static int wl12xx_sdio_set_power(struct device *child, bool enable)
++{
++	struct wl12xx_sdio_glue *glue = dev_get_drvdata(child->parent);
++
++	if (enable)
++		return wl12xx_sdio_power_on(glue);
++	else
++		return wl12xx_sdio_power_off(glue);
++}
++
++static struct wl1271_if_operations sdio_ops = {
++	.read		= wl12xx_sdio_raw_read,
++	.write		= wl12xx_sdio_raw_write,
++	.power		= wl12xx_sdio_set_power,
++	.set_block_size = wl1271_sdio_set_block_size,
++};
++
++static int wl1271_probe(struct sdio_func *func,
++				  const struct sdio_device_id *id)
++{
++	struct wlcore_platdev_data *pdev_data;
++	struct wl12xx_sdio_glue *glue;
++	struct resource res[1];
++	mmc_pm_flag_t mmcflags;
++	int ret = -ENOMEM;
++	const char *chip_family;
++
++	/* We are only able to handle the wlan function */
++	if (func->num != 0x02)
++		return -ENODEV;
++
++	pdev_data = kzalloc(sizeof(*pdev_data), GFP_KERNEL);
++	if (!pdev_data)
++		goto out;
++
++	pdev_data->if_ops = &sdio_ops;
++
++	glue = kzalloc(sizeof(*glue), GFP_KERNEL);
++	if (!glue) {
++		dev_err(&func->dev, "can't allocate glue\n");
++		goto out_free_pdev_data;
++	}
++
++	glue->dev = &func->dev;
++
++	/* Grab access to FN0 for ELP reg. */
++	func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
++
++	/* Use block mode for transferring over one block size of data */
++	func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE;
++
++	pdev_data->pdata = wl12xx_get_platform_data();
++	if (IS_ERR(pdev_data->pdata)) {
++		ret = PTR_ERR(pdev_data->pdata);
++		dev_err(glue->dev, "missing wlan platform data: %d\n", ret);
++		goto out_free_glue;
++	}
++
++	/* if sdio can keep power while host is suspended, enable wow */
++	mmcflags = sdio_get_host_pm_caps(func);
++	dev_dbg(glue->dev, "sdio PM caps = 0x%x\n", mmcflags);
++
++	if (mmcflags & MMC_PM_KEEP_POWER)
++		pdev_data->pwr_in_suspend = true;
++
++	sdio_set_drvdata(func, glue);
++
++	/* Tell PM core that we don't need the card to be powered now */
++	pm_runtime_put_noidle(&func->dev);
++
++	/*
++	 * Due to a hardware bug, we can't differentiate wl18xx from
++	 * wl12xx, because both report the same device ID.  The only
++	 * way to differentiate is by checking the SDIO revision,
++	 * which is 3.00 on the wl18xx chips.
++	 */
++	if (func->card->cccr.sdio_vsn == SDIO_SDIO_REV_3_00)
++		chip_family = "wl18xx";
++	else
++		chip_family = "wl12xx";
++
++	glue->core = platform_device_alloc(chip_family, PLATFORM_DEVID_AUTO);
++	if (!glue->core) {
++		dev_err(glue->dev, "can't allocate platform_device");
++		ret = -ENOMEM;
++		goto out_free_glue;
++	}
++
++	glue->core->dev.parent = &func->dev;
++
++	memset(res, 0x00, sizeof(res));
++
++	res[0].start = pdev_data->pdata->irq;
++	res[0].flags = IORESOURCE_IRQ;
++	res[0].name = "irq";
++
++	ret = platform_device_add_resources(glue->core, res, ARRAY_SIZE(res));
++	if (ret) {
++		dev_err(glue->dev, "can't add resources\n");
++		goto out_dev_put;
++	}
++
++	ret = platform_device_add_data(glue->core, pdev_data,
++				       sizeof(*pdev_data));
++	if (ret) {
++		dev_err(glue->dev, "can't add platform data\n");
++		goto out_dev_put;
++	}
++
++	ret = platform_device_add(glue->core);
++	if (ret) {
++		dev_err(glue->dev, "can't add platform device\n");
++		goto out_dev_put;
++	}
++	return 0;
++
++out_dev_put:
++	platform_device_put(glue->core);
++
++out_free_glue:
++	kfree(glue);
++
++out_free_pdev_data:
++	kfree(pdev_data);
++
++out:
++	return ret;
++}
++
++static void wl1271_remove(struct sdio_func *func)
++{
++	struct wl12xx_sdio_glue *glue = sdio_get_drvdata(func);
++
++	/* Undo decrement done above in wl1271_probe */
++	pm_runtime_get_noresume(&func->dev);
++
++	platform_device_unregister(glue->core);
++	kfree(glue);
++}
++
++#ifdef CONFIG_PM
++static int wl1271_suspend(struct device *dev)
++{
++	/* Tell MMC/SDIO core it's OK to power down the card
++	 * (if it isn't already), but not to remove it completely */
++	struct sdio_func *func = dev_to_sdio_func(dev);
++	struct wl12xx_sdio_glue *glue = sdio_get_drvdata(func);
++	struct wl1271 *wl = platform_get_drvdata(glue->core);
++	mmc_pm_flag_t sdio_flags;
++	int ret = 0;
++
++	dev_dbg(dev, "wl1271 suspend. wow_enabled: %d\n",
++		wl->wow_enabled);
++
++	/* check whether sdio should keep power */
++	if (wl->wow_enabled) {
++		sdio_flags = sdio_get_host_pm_caps(func);
++
++		if (!(sdio_flags & MMC_PM_KEEP_POWER)) {
++			dev_err(dev, "can't keep power while host "
++				     "is suspended\n");
++			ret = -EINVAL;
++			goto out;
++		}
++
++		/* keep power while host suspended */
++		ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
++		if (ret) {
++			dev_err(dev, "error while trying to keep power\n");
++			goto out;
++		}
++	}
++out:
++	return ret;
++}
++
++static int wl1271_resume(struct device *dev)
++{
++	dev_dbg(dev, "wl1271 resume\n");
++
++	return 0;
++}
++
++static const struct dev_pm_ops wl1271_sdio_pm_ops = {
++	.suspend	= wl1271_suspend,
++	.resume		= wl1271_resume,
++};
++#endif
++
++static struct sdio_driver wl1271_sdio_driver = {
++	.name		= "wl1271_sdio",
++	.id_table	= wl1271_devices,
++	.probe		= wl1271_probe,
++	.remove		= wl1271_remove,
++#ifdef CONFIG_PM
++	.drv = {
++		.pm = &wl1271_sdio_pm_ops,
++	},
++#endif
++};
++
++static int __init wl1271_init(void)
++{
++	return sdio_register_driver(&wl1271_sdio_driver);
++}
++
++static void __exit wl1271_exit(void)
++{
++	sdio_unregister_driver(&wl1271_sdio_driver);
++}
++
++module_init(wl1271_init);
++module_exit(wl1271_exit);
++
++module_param(dump, bool, S_IRUSR | S_IWUSR);
++MODULE_PARM_DESC(dump, "Enable sdio read/write dumps.");
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Luciano Coelho <coelho at ti.com>");
++MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen at nokia.com>");
+diff -Nuar git.old/drivers/net/wireless/ti/wlcore/wlcore.h git/drivers/net/wireless/ti/wlcore/wlcore.h
+--- git.old/drivers/net/wireless/ti/wlcore/wlcore.h	2015-03-04 16:35:05.722185997 +0100
++++ git/drivers/net/wireless/ti/wlcore/wlcore.h	2015-03-04 16:49:20.830177842 +0100
+@@ -185,6 +185,8 @@
+ 
+ 	int irq;
+ 
++	int irq_flags;
++
+ 	spinlock_t wl_lock;
+ 
+ 	enum wlcore_state state;
+@@ -384,9 +386,6 @@
+ 	/* Quirks of specific hardware revisions */
+ 	unsigned int quirks;
+ 
+-	/* Platform limitations */
+-	unsigned int platform_quirks;
+-
+ 	/* number of currently active RX BA sessions */
+ 	int ba_rx_session_count;
+ 
+diff -Nuar git.old/drivers/net/wireless/ti/wlcore/wlcore.h.orig git/drivers/net/wireless/ti/wlcore/wlcore.h.orig
+--- git.old/drivers/net/wireless/ti/wlcore/wlcore.h.orig	1970-01-01 01:00:00.000000000 +0100
++++ git/drivers/net/wireless/ti/wlcore/wlcore.h.orig	2015-03-04 16:35:05.722185997 +0100
+@@ -0,0 +1,616 @@
++/*
++ * This file is part of wlcore
++ *
++ * Copyright (C) 2011 Texas Instruments Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++ * 02110-1301 USA
++ *
++ */
++
++#ifndef __WLCORE_H__
++#define __WLCORE_H__
++
++#include <linux/platform_device.h>
++
++#include "wlcore_i.h"
++#include "event.h"
++#include "boot.h"
++
++/* The maximum number of Tx descriptors in all chip families */
++#define WLCORE_MAX_TX_DESCRIPTORS 32
++
++/*
++ * We always allocate this number of mac addresses. If we don't
++ * have enough allocated addresses, the LAA bit is used
++ */
++#define WLCORE_NUM_MAC_ADDRESSES 3
++
++/* wl12xx/wl18xx maximum transmission power (in dBm) */
++#define WLCORE_MAX_TXPWR        25
++
++/* forward declaration */
++struct wl1271_tx_hw_descr;
++enum wl_rx_buf_align;
++struct wl1271_rx_descriptor;
++
++struct wlcore_ops {
++	int (*setup)(struct wl1271 *wl);
++	int (*identify_chip)(struct wl1271 *wl);
++	int (*identify_fw)(struct wl1271 *wl);
++	int (*boot)(struct wl1271 *wl);
++	int (*plt_init)(struct wl1271 *wl);
++	int (*trigger_cmd)(struct wl1271 *wl, int cmd_box_addr,
++			   void *buf, size_t len);
++	int (*ack_event)(struct wl1271 *wl);
++	int (*wait_for_event)(struct wl1271 *wl, enum wlcore_wait_event event,
++			      bool *timeout);
++	int (*process_mailbox_events)(struct wl1271 *wl);
++	u32 (*calc_tx_blocks)(struct wl1271 *wl, u32 len, u32 spare_blks);
++	void (*set_tx_desc_blocks)(struct wl1271 *wl,
++				   struct wl1271_tx_hw_descr *desc,
++				   u32 blks, u32 spare_blks);
++	void (*set_tx_desc_data_len)(struct wl1271 *wl,
++				     struct wl1271_tx_hw_descr *desc,
++				     struct sk_buff *skb);
++	enum wl_rx_buf_align (*get_rx_buf_align)(struct wl1271 *wl,
++						 u32 rx_desc);
++	int (*prepare_read)(struct wl1271 *wl, u32 rx_desc, u32 len);
++	u32 (*get_rx_packet_len)(struct wl1271 *wl, void *rx_data,
++				 u32 data_len);
++	int (*tx_delayed_compl)(struct wl1271 *wl);
++	void (*tx_immediate_compl)(struct wl1271 *wl);
++	int (*hw_init)(struct wl1271 *wl);
++	int (*init_vif)(struct wl1271 *wl, struct wl12xx_vif *wlvif);
++	u32 (*sta_get_ap_rate_mask)(struct wl1271 *wl,
++				    struct wl12xx_vif *wlvif);
++	int (*get_pg_ver)(struct wl1271 *wl, s8 *ver);
++	int (*get_mac)(struct wl1271 *wl);
++	void (*set_tx_desc_csum)(struct wl1271 *wl,
++				 struct wl1271_tx_hw_descr *desc,
++				 struct sk_buff *skb);
++	void (*set_rx_csum)(struct wl1271 *wl,
++			    struct wl1271_rx_descriptor *desc,
++			    struct sk_buff *skb);
++	u32 (*ap_get_mimo_wide_rate_mask)(struct wl1271 *wl,
++					  struct wl12xx_vif *wlvif);
++	int (*debugfs_init)(struct wl1271 *wl, struct dentry *rootdir);
++	int (*handle_static_data)(struct wl1271 *wl,
++				  struct wl1271_static_data *static_data);
++	int (*scan_start)(struct wl1271 *wl, struct wl12xx_vif *wlvif,
++			  struct cfg80211_scan_request *req);
++	int (*scan_stop)(struct wl1271 *wl, struct wl12xx_vif *wlvif);
++	int (*sched_scan_start)(struct wl1271 *wl, struct wl12xx_vif *wlvif,
++				struct cfg80211_sched_scan_request *req,
++				struct ieee80211_sched_scan_ies *ies);
++	void (*sched_scan_stop)(struct wl1271 *wl, struct wl12xx_vif *wlvif);
++	int (*get_spare_blocks)(struct wl1271 *wl, bool is_gem);
++	int (*set_key)(struct wl1271 *wl, enum set_key_cmd cmd,
++		       struct ieee80211_vif *vif,
++		       struct ieee80211_sta *sta,
++		       struct ieee80211_key_conf *key_conf);
++	int (*channel_switch)(struct wl1271 *wl,
++			      struct wl12xx_vif *wlvif,
++			      struct ieee80211_channel_switch *ch_switch);
++	u32 (*pre_pkt_send)(struct wl1271 *wl, u32 buf_offset, u32 last_len);
++	void (*sta_rc_update)(struct wl1271 *wl, struct wl12xx_vif *wlvif,
++			      struct ieee80211_sta *sta, u32 changed);
++	int (*set_peer_cap)(struct wl1271 *wl,
++			    struct ieee80211_sta_ht_cap *ht_cap,
++			    bool allow_ht_operation,
++			    u32 rate_set, u8 hlid);
++	bool (*lnk_high_prio)(struct wl1271 *wl, u8 hlid,
++			      struct wl1271_link *lnk);
++	bool (*lnk_low_prio)(struct wl1271 *wl, u8 hlid,
++			     struct wl1271_link *lnk);
++};
++
++enum wlcore_partitions {
++	PART_DOWN,
++	PART_WORK,
++	PART_BOOT,
++	PART_DRPW,
++	PART_TOP_PRCM_ELP_SOC,
++	PART_PHY_INIT,
++
++	PART_TABLE_LEN,
++};
++
++struct wlcore_partition {
++	u32 size;
++	u32 start;
++};
++
++struct wlcore_partition_set {
++	struct wlcore_partition mem;
++	struct wlcore_partition reg;
++	struct wlcore_partition mem2;
++	struct wlcore_partition mem3;
++};
++
++enum wlcore_registers {
++	/* register addresses, used with partition translation */
++	REG_ECPU_CONTROL,
++	REG_INTERRUPT_NO_CLEAR,
++	REG_INTERRUPT_ACK,
++	REG_COMMAND_MAILBOX_PTR,
++	REG_EVENT_MAILBOX_PTR,
++	REG_INTERRUPT_TRIG,
++	REG_INTERRUPT_MASK,
++	REG_PC_ON_RECOVERY,
++	REG_CHIP_ID_B,
++	REG_CMD_MBOX_ADDRESS,
++
++	/* data access memory addresses, used with partition translation */
++	REG_SLV_MEM_DATA,
++	REG_SLV_REG_DATA,
++
++	/* raw data access memory addresses */
++	REG_RAW_FW_STATUS_ADDR,
++
++	REG_TABLE_LEN,
++};
++
++struct wl1271_stats {
++	void *fw_stats;
++	unsigned long fw_stats_update;
++	size_t fw_stats_len;
++
++	unsigned int retry_count;
++	unsigned int excessive_retries;
++};
++
++struct wl1271 {
++	bool initialized;
++	struct ieee80211_hw *hw;
++	bool mac80211_registered;
++
++	struct device *dev;
++	struct platform_device *pdev;
++
++	void *if_priv;
++
++	struct wl1271_if_operations *if_ops;
++
++	int irq;
++
++	spinlock_t wl_lock;
++
++	enum wlcore_state state;
++	enum wl12xx_fw_type fw_type;
++	bool plt;
++	enum plt_mode plt_mode;
++	u8 fem_manuf;
++	u8 last_vif_count;
++	struct mutex mutex;
++
++	unsigned long flags;
++
++	struct wlcore_partition_set curr_part;
++
++	struct wl1271_chip chip;
++
++	int cmd_box_addr;
++
++	u8 *fw;
++	size_t fw_len;
++	void *nvs;
++	size_t nvs_len;
++
++	s8 hw_pg_ver;
++
++	/* address read from the fuse ROM */
++	u32 fuse_oui_addr;
++	u32 fuse_nic_addr;
++
++	/* we have up to 2 MAC addresses */
++	struct mac_address addresses[WLCORE_NUM_MAC_ADDRESSES];
++	int channel;
++	u8 system_hlid;
++
++	unsigned long links_map[BITS_TO_LONGS(WL12XX_MAX_LINKS)];
++	unsigned long roles_map[BITS_TO_LONGS(WL12XX_MAX_ROLES)];
++	unsigned long roc_map[BITS_TO_LONGS(WL12XX_MAX_ROLES)];
++	unsigned long rate_policies_map[
++			BITS_TO_LONGS(WL12XX_MAX_RATE_POLICIES)];
++	unsigned long klv_templates_map[
++			BITS_TO_LONGS(WLCORE_MAX_KLV_TEMPLATES)];
++
++	u8 session_ids[WL12XX_MAX_LINKS];
++
++	struct list_head wlvif_list;
++
++	u8 sta_count;
++	u8 ap_count;
++
++	struct wl1271_acx_mem_map *target_mem_map;
++
++	/* Accounting for allocated / available TX blocks on HW */
++	u32 tx_blocks_freed;
++	u32 tx_blocks_available;
++	u32 tx_allocated_blocks;
++	u32 tx_results_count;
++
++	/* Accounting for allocated / available Tx packets in HW */
++	u32 tx_pkts_freed[NUM_TX_QUEUES];
++	u32 tx_allocated_pkts[NUM_TX_QUEUES];
++
++	/* Transmitted TX packets counter for chipset interface */
++	u32 tx_packets_count;
++
++	/* Time-offset between host and chipset clocks */
++	s64 time_offset;
++
++	/* Frames scheduled for transmission, not handled yet */
++	int tx_queue_count[NUM_TX_QUEUES];
++	unsigned long queue_stop_reasons[
++				NUM_TX_QUEUES * WLCORE_NUM_MAC_ADDRESSES];
++
++	/* Frames received, not handled yet by mac80211 */
++	struct sk_buff_head deferred_rx_queue;
++
++	/* Frames sent, not returned yet to mac80211 */
++	struct sk_buff_head deferred_tx_queue;
++
++	struct work_struct tx_work;
++	struct workqueue_struct *freezable_wq;
++
++	/* Pending TX frames */
++	unsigned long tx_frames_map[BITS_TO_LONGS(WLCORE_MAX_TX_DESCRIPTORS)];
++	struct sk_buff *tx_frames[WLCORE_MAX_TX_DESCRIPTORS];
++	int tx_frames_cnt;
++
++	/* FW Rx counter */
++	u32 rx_counter;
++
++	/* Intermediate buffer, used for packet aggregation */
++	u8 *aggr_buf;
++	u32 aggr_buf_size;
++
++	/* Reusable dummy packet template */
++	struct sk_buff *dummy_packet;
++
++	/* Network stack work  */
++	struct work_struct netstack_work;
++
++	/* FW log buffer */
++	u8 *fwlog;
++
++	/* Number of valid bytes in the FW log buffer */
++	ssize_t fwlog_size;
++
++	/* Sysfs FW log entry readers wait queue */
++	wait_queue_head_t fwlog_waitq;
++
++	/* Hardware recovery work */
++	struct work_struct recovery_work;
++	bool watchdog_recovery;
++
++	/* Reg domain last configuration */
++	u32 reg_ch_conf_last[2];
++	/* Reg domain pending configuration */
++	u32 reg_ch_conf_pending[2];
++
++	/* Pointer that holds DMA-friendly block for the mailbox */
++	void *mbox;
++
++	/* The mbox event mask */
++	u32 event_mask;
++
++	/* Mailbox pointers */
++	u32 mbox_size;
++	u32 mbox_ptr[2];
++
++	/* Are we currently scanning */
++	struct wl12xx_vif *scan_wlvif;
++	struct wl1271_scan scan;
++	struct delayed_work scan_complete_work;
++
++	struct ieee80211_vif *roc_vif;
++	struct delayed_work roc_complete_work;
++
++	struct wl12xx_vif *sched_vif;
++
++	/* The current band */
++	enum ieee80211_band band;
++
++	struct completion *elp_compl;
++	struct delayed_work elp_work;
++
++	/* in dBm */
++	int power_level;
++
++	struct wl1271_stats stats;
++
++	__le32 *buffer_32;
++	u32 buffer_cmd;
++	u32 buffer_busyword[WL1271_BUSY_WORD_CNT];
++
++	struct wl_fw_status_1 *fw_status_1;
++	struct wl_fw_status_2 *fw_status_2;
++	struct wl1271_tx_hw_res_if *tx_res_if;
++
++	/* Current chipset configuration */
++	struct wlcore_conf conf;
++
++	bool sg_enabled;
++
++	bool enable_11a;
++
++	int recovery_count;
++
++	/* Most recently reported noise in dBm */
++	s8 noise;
++
++	/* bands supported by this instance of wl12xx */
++	struct ieee80211_supported_band bands[WLCORE_NUM_BANDS];
++
++	/*
++	 * wowlan trigger was configured during suspend.
++	 * (currently, only "ANY" trigger is supported)
++	 */
++	bool wow_enabled;
++	bool irq_wake_enabled;
++
++	/*
++	 * AP-mode - links indexed by HLID. The global and broadcast links
++	 * are always active.
++	 */
++	struct wl1271_link links[WL12XX_MAX_LINKS];
++
++	/* number of currently active links */
++	int active_link_count;
++
++	/* Fast/slow links bitmap according to FW */
++	u32 fw_fast_lnk_map;
++
++	/* AP-mode - a bitmap of links currently in PS mode according to FW */
++	u32 ap_fw_ps_map;
++
++	/* AP-mode - a bitmap of links currently in PS mode in mac80211 */
++	unsigned long ap_ps_map;
++
++	/* Quirks of specific hardware revisions */
++	unsigned int quirks;
++
++	/* Platform limitations */
++	unsigned int platform_quirks;
++
++	/* number of currently active RX BA sessions */
++	int ba_rx_session_count;
++
++	/* Maximum number of supported RX BA sessions */
++	int ba_rx_session_count_max;
++
++	/* AP-mode - number of currently connected stations */
++	int active_sta_count;
++
++	/* last wlvif we transmitted from */
++	struct wl12xx_vif *last_wlvif;
++
++	/* work to fire when Tx is stuck */
++	struct delayed_work tx_watchdog_work;
++
++	struct wlcore_ops *ops;
++	/* pointer to the lower driver partition table */
++	const struct wlcore_partition_set *ptable;
++	/* pointer to the lower driver register table */
++	const int *rtable;
++	/* name of the firmwares to load - for PLT, single role, multi-role */
++	const char *plt_fw_name;
++	const char *sr_fw_name;
++	const char *mr_fw_name;
++
++	u8 scan_templ_id_2_4;
++	u8 scan_templ_id_5;
++	u8 sched_scan_templ_id_2_4;
++	u8 sched_scan_templ_id_5;
++	u8 max_channels_5;
++
++	/* per-chip-family private structure */
++	void *priv;
++
++	/* number of TX descriptors the HW supports. */
++	u32 num_tx_desc;
++	/* number of RX descriptors the HW supports. */
++	u32 num_rx_desc;
++
++	/* translate HW Tx rates to standard rate-indices */
++	const u8 **band_rate_to_idx;
++
++	/* size of table for HW rates that can be received from chip */
++	u8 hw_tx_rate_tbl_size;
++
++	/* this HW rate and below are considered HT rates for this chip */
++	u8 hw_min_ht_rate;
++
++	/* HW HT (11n) capabilities */
++	struct ieee80211_sta_ht_cap ht_cap[WLCORE_NUM_BANDS];
++
++	/* size of the private FW status data */
++	size_t fw_status_priv_len;
++
++	/* RX Data filter rule state - enabled/disabled */
++	bool rx_filter_enabled[WL1271_MAX_RX_FILTERS];
++
++	/* size of the private static data */
++	size_t static_data_priv_len;
++
++	/* the current channel type */
++	enum nl80211_channel_type channel_type;
++
++	/* mutex for protecting the tx_flush function */
++	struct mutex flush_mutex;
++
++	/* sleep auth value currently configured to FW */
++	int sleep_auth;
++
++	/* the number of allocated MAC addresses in this chip */
++	int num_mac_addr;
++
++	/* minimum FW version required for the driver to work in single-role */
++	unsigned int min_sr_fw_ver[NUM_FW_VER];
++
++	/* minimum FW version required for the driver to work in multi-role */
++	unsigned int min_mr_fw_ver[NUM_FW_VER];
++
++	struct completion nvs_loading_complete;
++
++	/* number of concurrent channels the HW supports */
++	u32 num_channels;
++};
++
++int wlcore_probe(struct wl1271 *wl, struct platform_device *pdev);
++int wlcore_remove(struct platform_device *pdev);
++struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
++				     u32 mbox_size);
++int wlcore_free_hw(struct wl1271 *wl);
++int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
++		   struct ieee80211_vif *vif,
++		   struct ieee80211_sta *sta,
++		   struct ieee80211_key_conf *key_conf);
++void wlcore_regdomain_config(struct wl1271 *wl);
++
++static inline void
++wlcore_set_ht_cap(struct wl1271 *wl, enum ieee80211_band band,
++		  struct ieee80211_sta_ht_cap *ht_cap)
++{
++	memcpy(&wl->ht_cap[band], ht_cap, sizeof(*ht_cap));
++}
++
++/* Tell wlcore not to care about this element when checking the version */
++#define WLCORE_FW_VER_IGNORE	-1
++
++static inline void
++wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip,
++		      unsigned int iftype_sr, unsigned int major_sr,
++		      unsigned int subtype_sr, unsigned int minor_sr,
++		      unsigned int iftype_mr, unsigned int major_mr,
++		      unsigned int subtype_mr, unsigned int minor_mr)
++{
++	wl->min_sr_fw_ver[FW_VER_CHIP] = chip;
++	wl->min_sr_fw_ver[FW_VER_IF_TYPE] = iftype_sr;
++	wl->min_sr_fw_ver[FW_VER_MAJOR] = major_sr;
++	wl->min_sr_fw_ver[FW_VER_SUBTYPE] = subtype_sr;
++	wl->min_sr_fw_ver[FW_VER_MINOR] = minor_sr;
++
++	wl->min_mr_fw_ver[FW_VER_CHIP] = chip;
++	wl->min_mr_fw_ver[FW_VER_IF_TYPE] = iftype_mr;
++	wl->min_mr_fw_ver[FW_VER_MAJOR] = major_mr;
++	wl->min_mr_fw_ver[FW_VER_SUBTYPE] = subtype_mr;
++	wl->min_mr_fw_ver[FW_VER_MINOR] = minor_mr;
++}
++
++/* Firmware image load chunk size */
++#define CHUNK_SIZE	16384
++
++/* Quirks */
++
++/* Each RX/TX transaction requires an end-of-transaction transfer */
++#define WLCORE_QUIRK_END_OF_TRANSACTION		BIT(0)
++
++/* the first start_role(sta) sometimes doesn't work on wl12xx */
++#define WLCORE_QUIRK_START_STA_FAILS		BIT(1)
++
++/* wl127x and SPI don't support SDIO block size alignment */
++#define WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN		BIT(2)
++
++/* means aggregated Rx packets are aligned to a SDIO block */
++#define WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN		BIT(3)
++
++/* Older firmwares did not implement the FW logger over bus feature */
++#define WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED	BIT(4)
++
++/* Older firmwares use an old NVS format */
++#define WLCORE_QUIRK_LEGACY_NVS			BIT(5)
++
++/* pad only the last frame in the aggregate buffer */
++#define WLCORE_QUIRK_TX_PAD_LAST_FRAME		BIT(7)
++
++/* extra header space is required for TKIP */
++#define WLCORE_QUIRK_TKIP_HEADER_SPACE		BIT(8)
++
++/* Some firmwares not support sched scans while connected */
++#define WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN	BIT(9)
++
++/* separate probe response templates for one-shot and sched scans */
++#define WLCORE_QUIRK_DUAL_PROBE_TMPL		BIT(10)
++
++/* Firmware requires reg domain configuration for active calibration */
++#define WLCORE_QUIRK_REGDOMAIN_CONF		BIT(11)
++
++/* The FW only support a zero session id for AP */
++#define WLCORE_QUIRK_AP_ZERO_SESSION_ID		BIT(12)
++
++/* TODO: move all these common registers and values elsewhere */
++#define HW_ACCESS_ELP_CTRL_REG		0x1FFFC
++
++/* ELP register commands */
++#define ELPCTRL_WAKE_UP             0x1
++#define ELPCTRL_WAKE_UP_WLAN_READY  0x5
++#define ELPCTRL_SLEEP               0x0
++/* ELP WLAN_READY bit */
++#define ELPCTRL_WLAN_READY          0x2
++
++/*************************************************************************
++
++    Interrupt Trigger Register (Host -> WiLink)
++
++**************************************************************************/
++
++/* Hardware to Embedded CPU Interrupts - first 32-bit register set */
++
++/*
++ * The host sets this bit to inform the Wlan
++ * FW that a TX packet is in the XFER
++ * Buffer #0.
++ */
++#define INTR_TRIG_TX_PROC0 BIT(2)
++
++/*
++ * The host sets this bit to inform the FW
++ * that it read a packet from RX XFER
++ * Buffer #0.
++ */
++#define INTR_TRIG_RX_PROC0 BIT(3)
++
++#define INTR_TRIG_DEBUG_ACK BIT(4)
++
++#define INTR_TRIG_STATE_CHANGED BIT(5)
++
++/* Hardware to Embedded CPU Interrupts - second 32-bit register set */
++
++/*
++ * The host sets this bit to inform the FW
++ * that it read a packet from RX XFER
++ * Buffer #1.
++ */
++#define INTR_TRIG_RX_PROC1 BIT(17)
++
++/*
++ * The host sets this bit to inform the Wlan
++ * hardware that a TX packet is in the XFER
++ * Buffer #1.
++ */
++#define INTR_TRIG_TX_PROC1 BIT(18)
++
++#define ACX_SLV_SOFT_RESET_BIT	BIT(1)
++#define SOFT_RESET_MAX_TIME	1000000
++#define SOFT_RESET_STALL_TIME	1000
++
++#define ECPU_CONTROL_HALT	0x00000101
++
++#define WELP_ARM_COMMAND_VAL	0x4
++
++#endif /* __WLCORE_H__ */
+diff -Nuar git.old/drivers/net/wireless/ti/wlcore/wlcore_i.h git/drivers/net/wireless/ti/wlcore/wlcore_i.h
+--- git.old/drivers/net/wireless/ti/wlcore/wlcore_i.h	2015-03-04 16:35:05.722185997 +0100
++++ git/drivers/net/wireless/ti/wlcore/wlcore_i.h	2015-03-04 16:36:46.174185039 +0100
+@@ -209,6 +209,7 @@
+ struct wlcore_platdev_data {
+ 	struct wl12xx_platform_data *pdata;
+ 	struct wl1271_if_operations *if_ops;
++	bool pwr_in_suspend;
+ };
+ 
+ #define MAX_NUM_KEYS 14
+diff -Nuar git.old/drivers/net/wireless/ti/wlcore/wlcore_i.h.orig git/drivers/net/wireless/ti/wlcore/wlcore_i.h.orig
+--- git.old/drivers/net/wireless/ti/wlcore/wlcore_i.h.orig	1970-01-01 01:00:00.000000000 +0100
++++ git/drivers/net/wireless/ti/wlcore/wlcore_i.h.orig	2015-03-04 16:37:26.014184659 +0100
+@@ -0,0 +1,545 @@
++/*
++ * This file is part of wl1271
++ *
++ * Copyright (C) 1998-2009 Texas Instruments. All rights reserved.
++ * Copyright (C) 2008-2009 Nokia Corporation
++ *
++ * Contact: Luciano Coelho <luciano.coelho at nokia.com>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++ * 02110-1301 USA
++ *
++ */
++
++#ifndef __WLCORE_I_H__
++#define __WLCORE_I_H__
++
++#include <linux/mutex.h>
++#include <linux/completion.h>
++#include <linux/spinlock.h>
++#include <linux/list.h>
++#include <linux/bitops.h>
++#include <net/mac80211.h>
++
++#include "conf.h"
++#include "ini.h"
++
++/*
++ * wl127x and wl128x are using the same NVS file name. However, the
++ * ini parameters between them are different.  The driver validates
++ * the correct NVS size in wl1271_boot_upload_nvs().
++ */
++#define WL12XX_NVS_NAME "ti-connectivity/wl1271-nvs.bin"
++
++#define WL1271_TX_SECURITY_LO16(s) ((u16)((s) & 0xffff))
++#define WL1271_TX_SECURITY_HI32(s) ((u32)(((s) >> 16) & 0xffffffff))
++#define WL1271_TX_SQN_POST_RECOVERY_PADDING 0xff
++
++#define WL1271_CIPHER_SUITE_GEM 0x00147201
++
++#define WL1271_BUSY_WORD_CNT 1
++#define WL1271_BUSY_WORD_LEN (WL1271_BUSY_WORD_CNT * sizeof(u32))
++
++#define WL1271_ELP_HW_STATE_ASLEEP 0
++#define WL1271_ELP_HW_STATE_IRQ    1
++
++#define WL1271_DEFAULT_BEACON_INT  100
++#define WL1271_DEFAULT_DTIM_PERIOD 1
++
++#define WL12XX_MAX_ROLES           4
++#define WL12XX_MAX_LINKS           12
++#define WL12XX_INVALID_ROLE_ID     0xff
++#define WL12XX_INVALID_LINK_ID     0xff
++
++/* the driver supports the 2.4Ghz and 5Ghz bands */
++#define WLCORE_NUM_BANDS           2
++
++#define WL12XX_MAX_RATE_POLICIES 16
++#define WLCORE_MAX_KLV_TEMPLATES 4
++
++/* Defined by FW as 0. Will not be freed or allocated. */
++#define WL12XX_SYSTEM_HLID         0
++
++/*
++ * When in AP-mode, we allow (at least) this number of packets
++ * to be transmitted to FW for a STA in PS-mode. Only when packets are
++ * present in the FW buffers it will wake the sleeping STA. We want to put
++ * enough packets for the driver to transmit all of its buffered data before
++ * the STA goes to sleep again. But we don't want to take too much memory
++ * as it might hurt the throughput of active STAs.
++ */
++#define WL1271_PS_STA_MAX_PACKETS  2
++
++#define WL1271_AP_BSS_INDEX        0
++#define WL1271_AP_DEF_BEACON_EXP   20
++
++enum wlcore_state {
++	WLCORE_STATE_OFF,
++	WLCORE_STATE_RESTARTING,
++	WLCORE_STATE_ON,
++};
++
++enum wl12xx_fw_type {
++	WL12XX_FW_TYPE_NONE,
++	WL12XX_FW_TYPE_NORMAL,
++	WL12XX_FW_TYPE_MULTI,
++	WL12XX_FW_TYPE_PLT,
++};
++
++struct wl1271;
++
++enum {
++	FW_VER_CHIP,
++	FW_VER_IF_TYPE,
++	FW_VER_MAJOR,
++	FW_VER_SUBTYPE,
++	FW_VER_MINOR,
++
++	NUM_FW_VER
++};
++
++struct wl1271_chip {
++	u32 id;
++	char fw_ver_str[ETHTOOL_FWVERS_LEN];
++	unsigned int fw_ver[NUM_FW_VER];
++	char phy_fw_ver_str[ETHTOOL_FWVERS_LEN];
++};
++
++#define NUM_TX_QUEUES              4
++
++#define AP_MAX_STATIONS            8
++
++struct wl_fw_packet_counters {
++	/* Cumulative counter of released packets per AC */
++	u8 tx_released_pkts[NUM_TX_QUEUES];
++
++	/* Cumulative counter of freed packets per HLID */
++	u8 tx_lnk_free_pkts[WL12XX_MAX_LINKS];
++
++	/* Cumulative counter of released Voice memory blocks */
++	u8 tx_voice_released_blks;
++
++	/* Tx rate of the last transmitted packet */
++	u8 tx_last_rate;
++
++	u8 padding[2];
++} __packed;
++
++/* FW status registers */
++struct wl_fw_status_1 {
++	__le32 intr;
++	u8  fw_rx_counter;
++	u8  drv_rx_counter;
++	u8  reserved;
++	u8  tx_results_counter;
++	__le32 rx_pkt_descs[0];
++} __packed;
++
++/*
++ * Each HW arch has a different number of Rx descriptors.
++ * The length of the status depends on it, since it holds an array
++ * of descriptors.
++ */
++#define WLCORE_FW_STATUS_1_LEN(num_rx_desc) \
++		(sizeof(struct wl_fw_status_1) + \
++		(sizeof(((struct wl_fw_status_1 *)0)->rx_pkt_descs[0])) * \
++		num_rx_desc)
++
++struct wl_fw_status_2 {
++	__le32 fw_localtime;
++
++	/*
++	 * A bitmap (where each bit represents a single HLID)
++	 * to indicate if the station is in PS mode.
++	 */
++	__le32 link_ps_bitmap;
++
++	/*
++	 * A bitmap (where each bit represents a single HLID) to indicate
++	 * if the station is in Fast mode
++	 */
++	__le32 link_fast_bitmap;
++
++	/* Cumulative counter of total released mem blocks since FW-reset */
++	__le32 total_released_blks;
++
++	/* Size (in Memory Blocks) of TX pool */
++	__le32 tx_total;
++
++	struct wl_fw_packet_counters counters;
++
++	__le32 log_start_addr;
++
++	/* Private status to be used by the lower drivers */
++	u8 priv[0];
++} __packed;
++
++#define WL1271_MAX_CHANNELS 64
++struct wl1271_scan {
++	struct cfg80211_scan_request *req;
++	unsigned long scanned_ch[BITS_TO_LONGS(WL1271_MAX_CHANNELS)];
++	bool failed;
++	u8 state;
++	u8 ssid[IEEE80211_MAX_SSID_LEN+1];
++	size_t ssid_len;
++};
++
++struct wl1271_if_operations {
++	int __must_check (*read)(struct device *child, int addr, void *buf,
++				 size_t len, bool fixed);
++	int __must_check (*write)(struct device *child, int addr, void *buf,
++				  size_t len, bool fixed);
++	void (*reset)(struct device *child);
++	void (*init)(struct device *child);
++	int (*power)(struct device *child, bool enable);
++	void (*set_block_size) (struct device *child, unsigned int blksz);
++};
++
++struct wlcore_platdev_data {
++	struct wl12xx_platform_data *pdata;
++	struct wl1271_if_operations *if_ops;
++	bool pwr_in_suspend;
++};
++
++#define MAX_NUM_KEYS 14
++#define MAX_KEY_SIZE 32
++
++struct wl1271_ap_key {
++	u8 id;
++	u8 key_type;
++	u8 key_size;
++	u8 key[MAX_KEY_SIZE];
++	u8 hlid;
++	u32 tx_seq_32;
++	u16 tx_seq_16;
++};
++
++enum wl12xx_flags {
++	WL1271_FLAG_GPIO_POWER,
++	WL1271_FLAG_TX_QUEUE_STOPPED,
++	WL1271_FLAG_TX_PENDING,
++	WL1271_FLAG_IN_ELP,
++	WL1271_FLAG_ELP_REQUESTED,
++	WL1271_FLAG_IRQ_RUNNING,
++	WL1271_FLAG_FW_TX_BUSY,
++	WL1271_FLAG_DUMMY_PACKET_PENDING,
++	WL1271_FLAG_SUSPENDED,
++	WL1271_FLAG_PENDING_WORK,
++	WL1271_FLAG_SOFT_GEMINI,
++	WL1271_FLAG_RECOVERY_IN_PROGRESS,
++	WL1271_FLAG_VIF_CHANGE_IN_PROGRESS,
++	WL1271_FLAG_INTENDED_FW_RECOVERY,
++	WL1271_FLAG_IO_FAILED,
++};
++
++enum wl12xx_vif_flags {
++	WLVIF_FLAG_INITIALIZED,
++	WLVIF_FLAG_STA_ASSOCIATED,
++	WLVIF_FLAG_STA_AUTHORIZED,
++	WLVIF_FLAG_IBSS_JOINED,
++	WLVIF_FLAG_AP_STARTED,
++	WLVIF_FLAG_IN_PS,
++	WLVIF_FLAG_STA_STATE_SENT,
++	WLVIF_FLAG_RX_STREAMING_STARTED,
++	WLVIF_FLAG_PSPOLL_FAILURE,
++	WLVIF_FLAG_CS_PROGRESS,
++	WLVIF_FLAG_AP_PROBE_RESP_SET,
++	WLVIF_FLAG_IN_USE,
++};
++
++struct wl12xx_vif;
++
++struct wl1271_link {
++	/* AP-mode - TX queue per AC in link */
++	struct sk_buff_head tx_queue[NUM_TX_QUEUES];
++
++	/* accounting for allocated / freed packets in FW */
++	u8 allocated_pkts;
++	u8 prev_freed_pkts;
++
++	u8 addr[ETH_ALEN];
++
++	/* bitmap of TIDs where RX BA sessions are active for this link */
++	u8 ba_bitmap;
++
++	/* The wlvif this link belongs to. Might be null for global links */
++	struct wl12xx_vif *wlvif;
++
++	/*
++	 * total freed FW packets on the link - used for tracking the
++	 * AES/TKIP PN across recoveries. Re-initialized each time
++	 * from the wl1271_station structure.
++	 */
++	u64 total_freed_pkts;
++};
++
++#define WL1271_MAX_RX_FILTERS 5
++#define WL1271_RX_FILTER_MAX_FIELDS 8
++
++#define WL1271_RX_FILTER_ETH_HEADER_SIZE 14
++#define WL1271_RX_FILTER_MAX_FIELDS_SIZE 95
++#define RX_FILTER_FIELD_OVERHEAD				\
++	(sizeof(struct wl12xx_rx_filter_field) - sizeof(u8 *))
++#define WL1271_RX_FILTER_MAX_PATTERN_SIZE			\
++	(WL1271_RX_FILTER_MAX_FIELDS_SIZE - RX_FILTER_FIELD_OVERHEAD)
++
++#define WL1271_RX_FILTER_FLAG_MASK                BIT(0)
++#define WL1271_RX_FILTER_FLAG_IP_HEADER           0
++#define WL1271_RX_FILTER_FLAG_ETHERNET_HEADER     BIT(1)
++
++enum rx_filter_action {
++	FILTER_DROP = 0,
++	FILTER_SIGNAL = 1,
++	FILTER_FW_HANDLE = 2
++};
++
++enum plt_mode {
++	PLT_OFF = 0,
++	PLT_ON = 1,
++	PLT_FEM_DETECT = 2,
++};
++
++struct wl12xx_rx_filter_field {
++	__le16 offset;
++	u8 len;
++	u8 flags;
++	u8 *pattern;
++} __packed;
++
++struct wl12xx_rx_filter {
++	u8 action;
++	int num_fields;
++	struct wl12xx_rx_filter_field fields[WL1271_RX_FILTER_MAX_FIELDS];
++};
++
++struct wl1271_station {
++	u8 hlid;
++	bool in_connection;
++
++	/*
++	 * total freed FW packets on the link to the STA - used for tracking the
++	 * AES/TKIP PN across recoveries. Re-initialized each time from the
++	 * wl1271_station structure.
++	 */
++	u64 total_freed_pkts;
++};
++
++struct wl12xx_vif {
++	struct wl1271 *wl;
++	struct list_head list;
++	unsigned long flags;
++	u8 bss_type;
++	u8 p2p; /* we are using p2p role */
++	u8 role_id;
++
++	/* sta/ibss specific */
++	u8 dev_role_id;
++	u8 dev_hlid;
++
++	union {
++		struct {
++			u8 hlid;
++
++			u8 basic_rate_idx;
++			u8 ap_rate_idx;
++			u8 p2p_rate_idx;
++
++			u8 klv_template_id;
++
++			bool qos;
++			/* channel type we started the STA role with */
++			enum nl80211_channel_type role_chan_type;
++		} sta;
++		struct {
++			u8 global_hlid;
++			u8 bcast_hlid;
++
++			/* HLIDs bitmap of associated stations */
++			unsigned long sta_hlid_map[BITS_TO_LONGS(
++							WL12XX_MAX_LINKS)];
++
++			/* recoreded keys - set here before AP startup */
++			struct wl1271_ap_key *recorded_keys[MAX_NUM_KEYS];
++
++			u8 mgmt_rate_idx;
++			u8 bcast_rate_idx;
++			u8 ucast_rate_idx[CONF_TX_MAX_AC_COUNT];
++		} ap;
++	};
++
++	/* the hlid of the last transmitted skb */
++	int last_tx_hlid;
++
++	/* counters of packets per AC, across all links in the vif */
++	int tx_queue_count[NUM_TX_QUEUES];
++
++	unsigned long links_map[BITS_TO_LONGS(WL12XX_MAX_LINKS)];
++
++	u8 ssid[IEEE80211_MAX_SSID_LEN + 1];
++	u8 ssid_len;
++
++	/* The current band */
++	enum ieee80211_band band;
++	int channel;
++	enum nl80211_channel_type channel_type;
++
++	u32 bitrate_masks[WLCORE_NUM_BANDS];
++	u32 basic_rate_set;
++
++	/*
++	 * currently configured rate set:
++	 *	bits  0-15 - 802.11abg rates
++	 *	bits 16-23 - 802.11n   MCS index mask
++	 * support only 1 stream, thus only 8 bits for the MCS rates (0-7).
++	 */
++	u32 basic_rate;
++	u32 rate_set;
++
++	/* probe-req template for the current AP */
++	struct sk_buff *probereq;
++
++	/* Beaconing interval (needed for ad-hoc) */
++	u32 beacon_int;
++
++	/* Default key (for WEP) */
++	u32 default_key;
++
++	/* Our association ID */
++	u16 aid;
++
++	/* retry counter for PSM entries */
++	u8 psm_entry_retry;
++
++	/* in dBm */
++	int power_level;
++
++	int rssi_thold;
++	int last_rssi_event;
++
++	/* save the current encryption type for auto-arp config */
++	u8 encryption_type;
++	__be32 ip_addr;
++
++	/* RX BA constraint value */
++	bool ba_support;
++	bool ba_allowed;
++
++	bool wmm_enabled;
++
++	/* Rx Streaming */
++	struct work_struct rx_streaming_enable_work;
++	struct work_struct rx_streaming_disable_work;
++	struct timer_list rx_streaming_timer;
++
++	struct delayed_work channel_switch_work;
++	struct delayed_work connection_loss_work;
++
++	/* number of in connection stations */
++	int inconn_count;
++
++	/*
++	 * This vif's queues are mapped to mac80211 HW queues as:
++	 * VO - hw_queue_base
++	 * VI - hw_queue_base + 1
++	 * BE - hw_queue_base + 2
++	 * BK - hw_queue_base + 3
++	 */
++	int hw_queue_base;
++
++	/*
++	 * This struct must be last!
++	 * data that has to be saved acrossed reconfigs (e.g. recovery)
++	 * should be declared in this struct.
++	 */
++	struct {
++		u8 persistent[0];
++
++		/*
++		 * total freed FW packets on the link - used for
++		 * storing the AES/TKIP PN during recovery, as this
++		 * structure is not zeroed out.
++		 * For STA this holds the PN of the link to the AP.
++		 * For AP this holds the PN of the broadcast link.
++		 */
++		u64 total_freed_pkts;
++	};
++};
++
++static inline struct wl12xx_vif *wl12xx_vif_to_data(struct ieee80211_vif *vif)
++{
++	WARN_ON(!vif);
++	return (struct wl12xx_vif *)vif->drv_priv;
++}
++
++static inline
++struct ieee80211_vif *wl12xx_wlvif_to_vif(struct wl12xx_vif *wlvif)
++{
++	return container_of((void *)wlvif, struct ieee80211_vif, drv_priv);
++}
++
++#define wl12xx_for_each_wlvif(wl, wlvif) \
++		list_for_each_entry(wlvif, &wl->wlvif_list, list)
++
++#define wl12xx_for_each_wlvif_continue(wl, wlvif) \
++		list_for_each_entry_continue(wlvif, &wl->wlvif_list, list)
++
++#define wl12xx_for_each_wlvif_bss_type(wl, wlvif, _bss_type)	\
++		wl12xx_for_each_wlvif(wl, wlvif)		\
++			if (wlvif->bss_type == _bss_type)
++
++#define wl12xx_for_each_wlvif_sta(wl, wlvif)	\
++		wl12xx_for_each_wlvif_bss_type(wl, wlvif, BSS_TYPE_STA_BSS)
++
++#define wl12xx_for_each_wlvif_ap(wl, wlvif)	\
++		wl12xx_for_each_wlvif_bss_type(wl, wlvif, BSS_TYPE_AP_BSS)
++
++int wl1271_plt_start(struct wl1271 *wl, const enum plt_mode plt_mode);
++int wl1271_plt_stop(struct wl1271 *wl);
++int wl1271_recalc_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif);
++void wl12xx_queue_recovery_work(struct wl1271 *wl);
++size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen);
++int wl1271_rx_filter_alloc_field(struct wl12xx_rx_filter *filter,
++					u16 offset, u8 flags,
++					u8 *pattern, u8 len);
++void wl1271_rx_filter_free(struct wl12xx_rx_filter *filter);
++struct wl12xx_rx_filter *wl1271_rx_filter_alloc(void);
++int wl1271_rx_filter_get_fields_size(struct wl12xx_rx_filter *filter);
++void wl1271_rx_filter_flatten_fields(struct wl12xx_rx_filter *filter,
++				     u8 *buf);
++
++#define JOIN_TIMEOUT 5000 /* 5000 milliseconds to join */
++
++#define SESSION_COUNTER_MAX 6 /* maximum value for the session counter */
++#define SESSION_COUNTER_INVALID 7 /* used with dummy_packet */
++
++#define WL1271_DEFAULT_POWER_LEVEL 0
++
++#define WL1271_TX_QUEUE_LOW_WATERMARK  32
++#define WL1271_TX_QUEUE_HIGH_WATERMARK 256
++
++#define WL1271_DEFERRED_QUEUE_LIMIT    64
++
++/* WL1271 needs a 200ms sleep after power on, and a 20ms sleep before power
++   on in case is has been shut down shortly before */
++#define WL1271_PRE_POWER_ON_SLEEP 20 /* in milliseconds */
++#define WL1271_POWER_ON_SLEEP 200 /* in milliseconds */
++
++/* Macros to handle wl1271.sta_rate_set */
++#define HW_BG_RATES_MASK	0xffff
++#define HW_HT_RATES_OFFSET	16
++#define HW_MIMO_RATES_OFFSET	24
++
++#define WL12XX_HW_BLOCK_SIZE	256
++
++#endif /* __WLCORE_I_H__ */
+diff -Nuar git.old/include/linux/wl12xx.h git/include/linux/wl12xx.h
+--- git.old/include/linux/wl12xx.h	2015-03-04 16:35:12.462185933 +0100
++++ git/include/linux/wl12xx.h	2015-03-04 16:57:06.170173404 +0100
+@@ -26,42 +26,16 @@
+ 
+ #include <linux/err.h>
+ 
+-/* Reference clock values */
+-enum {
+-	WL12XX_REFCLOCK_19	= 0, /* 19.2 MHz */
+-	WL12XX_REFCLOCK_26	= 1, /* 26 MHz */
+-	WL12XX_REFCLOCK_38	= 2, /* 38.4 MHz */
+-	WL12XX_REFCLOCK_52	= 3, /* 52 MHz */
+-	WL12XX_REFCLOCK_38_XTAL = 4, /* 38.4 MHz, XTAL */
+-	WL12XX_REFCLOCK_26_XTAL = 5, /* 26 MHz, XTAL */
+-};
+-
+-/* TCXO clock values */
+-enum {
+-	WL12XX_TCXOCLOCK_19_2	= 0, /* 19.2MHz */
+-	WL12XX_TCXOCLOCK_26	= 1, /* 26 MHz */
+-	WL12XX_TCXOCLOCK_38_4	= 2, /* 38.4MHz */
+-	WL12XX_TCXOCLOCK_52	= 3, /* 52 MHz */
+-	WL12XX_TCXOCLOCK_16_368	= 4, /* 16.368 MHz */
+-	WL12XX_TCXOCLOCK_32_736	= 5, /* 32.736 MHz */
+-	WL12XX_TCXOCLOCK_16_8	= 6, /* 16.8 MHz */
+-	WL12XX_TCXOCLOCK_33_6	= 7, /* 33.6 MHz */
+-};
+-
+ struct wl12xx_platform_data {
+ 	void (*set_power)(bool enable);
+ 	/* SDIO only: IRQ number if WLAN_IRQ line is used, 0 for SDIO IRQs */
+ 	int irq;
+ 	bool use_eeprom;
+-	int board_ref_clock;
+-	int board_tcxo_clock;
+-	unsigned long platform_quirks;
+-	bool pwr_in_suspend;
++	int ref_clock_freq;	/* in Hertz */
++	bool ref_clock_xtal;	/* specify whether the clock is XTAL or not */
++	int tcxo_clock_freq;	/* in Hertz, tcxo is always XTAL */
+ };
+ 
+-/* Platform does not support level trigger interrupts */
+-#define WL12XX_PLATFORM_QUIRK_EDGE_IRQ	BIT(0)
+-
+ #ifdef CONFIG_WILINK_PLATFORM_DATA
+ 
+ int wl12xx_set_platform_data(const struct wl12xx_platform_data *data);
+diff -Nuar git.old/include/linux/wl12xx.h.orig git/include/linux/wl12xx.h.orig
+--- git.old/include/linux/wl12xx.h.orig	1970-01-01 01:00:00.000000000 +0100
++++ git/include/linux/wl12xx.h.orig	2015-03-04 16:52:01.554176309 +0100
+@@ -0,0 +1,83 @@
++/*
++ * This file is part of wl12xx
++ *
++ * Copyright (C) 2009 Nokia Corporation
++ *
++ * Contact: Luciano Coelho <luciano.coelho at nokia.com>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
++ * 02110-1301 USA
++ *
++ */
++
++#ifndef _LINUX_WL12XX_H
++#define _LINUX_WL12XX_H
++
++#include <linux/err.h>
++
++/* Reference clock values */
++enum {
++	WL12XX_REFCLOCK_19	= 0, /* 19.2 MHz */
++	WL12XX_REFCLOCK_26	= 1, /* 26 MHz */
++	WL12XX_REFCLOCK_38	= 2, /* 38.4 MHz */
++	WL12XX_REFCLOCK_52	= 3, /* 52 MHz */
++	WL12XX_REFCLOCK_38_XTAL = 4, /* 38.4 MHz, XTAL */
++	WL12XX_REFCLOCK_26_XTAL = 5, /* 26 MHz, XTAL */
++};
++
++/* TCXO clock values */
++enum {
++	WL12XX_TCXOCLOCK_19_2	= 0, /* 19.2MHz */
++	WL12XX_TCXOCLOCK_26	= 1, /* 26 MHz */
++	WL12XX_TCXOCLOCK_38_4	= 2, /* 38.4MHz */
++	WL12XX_TCXOCLOCK_52	= 3, /* 52 MHz */
++	WL12XX_TCXOCLOCK_16_368	= 4, /* 16.368 MHz */
++	WL12XX_TCXOCLOCK_32_736	= 5, /* 32.736 MHz */
++	WL12XX_TCXOCLOCK_16_8	= 6, /* 16.8 MHz */
++	WL12XX_TCXOCLOCK_33_6	= 7, /* 33.6 MHz */
++};
++
++struct wl12xx_platform_data {
++	void (*set_power)(bool enable);
++	/* SDIO only: IRQ number if WLAN_IRQ line is used, 0 for SDIO IRQs */
++	int irq;
++	bool use_eeprom;
++	int board_ref_clock;
++	int board_tcxo_clock;
++	bool pwr_in_suspend;
++};
++
++#ifdef CONFIG_WILINK_PLATFORM_DATA
++
++int wl12xx_set_platform_data(const struct wl12xx_platform_data *data);
++
++struct wl12xx_platform_data *wl12xx_get_platform_data(void);
++
++#else
++
++static inline
++int wl12xx_set_platform_data(const struct wl12xx_platform_data *data)
++{
++	return -ENOSYS;
++}
++
++static inline
++struct wl12xx_platform_data *wl12xx_get_platform_data(void)
++{
++	return ERR_PTR(-ENODATA);
++}
++
++#endif
++
++#endif
diff --git a/recipes-kernel/linux/linux-apf6/defconfig b/recipes-kernel/linux/linux-apf6/defconfig
new file mode 100644
index 0000000..67573d8
--- /dev/null
+++ b/recipes-kernel/linux/linux-apf6/defconfig
@@ -0,0 +1,409 @@
+CONFIG_LOCALVERSION="_1.1.0_ga-apf6"
+CONFIG_KERNEL_LZO=y
+CONFIG_SYSVIPC=y
+CONFIG_FHANDLE=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=18
+CONFIG_CGROUPS=y
+CONFIG_RELAY=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_EMBEDDED=y
+CONFIG_PERF_EVENTS=y
+# CONFIG_SLUB_DEBUG is not set
+# CONFIG_COMPAT_BRK is not set
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
+# CONFIG_BLK_DEV_BSG is not set
+CONFIG_GPIO_PCA953X=y
+CONFIG_ARCH_MXC=y
+CONFIG_MXC_DEBUG_BOARD=y
+CONFIG_MACH_IMX51_DT=y
+CONFIG_MACH_EUKREA_CPUIMX51SD=y
+CONFIG_SOC_IMX53=y
+CONFIG_SOC_IMX6Q=y
+CONFIG_SOC_IMX6SL=y
+CONFIG_SOC_IMX6SX=y
+CONFIG_SOC_VF610=y
+CONFIG_WAND_RFKILL=y
+# CONFIG_SWP_EMULATE is not set
+CONFIG_PCI=y
+CONFIG_PCI_IMX6=y
+# CONFIG_PCIEAER is not set
+# CONFIG_PCIEASPM is not set
+CONFIG_SMP=y
+CONFIG_VMSPLIT_2G=y
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+CONFIG_HIGHMEM=y
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="noinitrd console=ttymxc3,115200"
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_ARM_IMX6_CPUFREQ=y
+CONFIG_CPU_IDLE=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_BINFMT_MISC=m
+CONFIG_PM_RUNTIME=y
+CONFIG_PM_DEBUG=y
+CONFIG_PM_TEST_SUSPEND=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+CONFIG_IPV6=y
+CONFIG_NETFILTER=y
+CONFIG_VLAN_8021Q=y
+CONFIG_LLC2=y
+CONFIG_CAN=y
+CONFIG_CAN_FLEXCAN=y
+CONFIG_CAN_M_CAN=y
+CONFIG_BT=y
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=m
+CONFIG_BT_HCIUART=m
+CONFIG_BT_HCIUART_H4=y
+CONFIG_CFG80211=y
+CONFIG_NL80211_TESTMODE=y
+CONFIG_CFG80211_WEXT=y
+CONFIG_MAC80211=y
+CONFIG_RFKILL_INPUT=y
+CONFIG_RFKILL_GPIO=y
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+# CONFIG_STANDALONE is not set
+CONFIG_CMA=y
+CONFIG_CMA_SIZE_MBYTES=320
+CONFIG_IMX_WEIM=y
+CONFIG_CONNECTOR=y
+CONFIG_MTD=y
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_CFI=y
+CONFIG_MTD_JEDECPROBE=y
+CONFIG_MTD_CFI_INTELEXT=y
+CONFIG_MTD_CFI_AMDSTD=y
+CONFIG_MTD_CFI_STAA=y
+CONFIG_MTD_PHYSMAP_OF=y
+CONFIG_MTD_DATAFLASH=y
+CONFIG_MTD_M25P80=y
+CONFIG_MTD_SST25L=y
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_GPMI_NAND=y
+CONFIG_MTD_NAND_MXC=y
+CONFIG_MTD_SPI_NOR_BASE=y
+CONFIG_SPI_FSL_QUADSPI=y
+CONFIG_MTD_UBI=y
+CONFIG_PROC_DEVICETREE=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=65536
+CONFIG_EEPROM_AT24=y
+CONFIG_EEPROM_AT25=y
+# CONFIG_SCSI_PROC_FS is not set
+CONFIG_BLK_DEV_SD=y
+CONFIG_BLK_DEV_SR=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_CHR_DEV_SCH=y
+CONFIG_SCSI_MULTI_LUN=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_LOGGING=y
+CONFIG_SCSI_SCAN_ASYNC=y
+# CONFIG_SCSI_LOWLEVEL is not set
+CONFIG_ATA=y
+CONFIG_SATA_AHCI_PLATFORM=y
+CONFIG_AHCI_IMX=y
+CONFIG_PATA_IMX=y
+CONFIG_NETDEVICES=y
+# CONFIG_NET_VENDOR_BROADCOM is not set
+CONFIG_CS89x0=y
+CONFIG_CS89x0_PLATFORM=y
+# CONFIG_NET_VENDOR_FARADAY is not set
+# CONFIG_NET_VENDOR_INTEL is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_MICROCHIP is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+CONFIG_SMC91X=y
+CONFIG_SMC911X=y
+CONFIG_SMSC911X=y
+# CONFIG_NET_VENDOR_STMICRO is not set
+CONFIG_AT803X_PHY=y
+CONFIG_USB_HSO=y
+CONFIG_ATH_CARDS=y
+CONFIG_ATH6KL=m
+CONFIG_ATH6KL_SDIO=m
+CONFIG_BRCMFMAC=m
+CONFIG_BRCMDBG=y
+CONFIG_WL_TI=y
+CONFIG_WL12XX=m
+CONFIG_WLCORE_SDIO=m
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_EVBUG=m
+CONFIG_KEYBOARD_GPIO=y
+CONFIG_KEYBOARD_SNVS_PWRKEY=y
+CONFIG_KEYBOARD_IMX=y
+CONFIG_MOUSE_PS2=m
+CONFIG_MOUSE_PS2_ELANTECH=y
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_EGALAX=y
+CONFIG_TOUCHSCREEN_ELAN=y
+CONFIG_TOUCHSCREEN_MAX11801=y
+CONFIG_TOUCHSCREEN_MC13783=y
+CONFIG_TOUCHSCREEN_SX8674=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_MMA8450=y
+CONFIG_INPUT_ISL29023=y
+CONFIG_SERIO_SERPORT=m
+CONFIG_VT_HW_CONSOLE_BINDING=y
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_IMX=y
+CONFIG_SERIAL_IMX_CONSOLE=y
+CONFIG_SERIAL_FSL_LPUART=y
+CONFIG_SERIAL_FSL_LPUART_CONSOLE=y
+CONFIG_FSL_OTP=y
+CONFIG_MXS_VIIM=y
+# CONFIG_I2C_COMPAT is not set
+CONFIG_I2C_CHARDEV=y
+# CONFIG_I2C_HELPER_AUTO is not set
+CONFIG_I2C_ALGOPCF=m
+CONFIG_I2C_ALGOPCA=m
+CONFIG_I2C_IMX=y
+CONFIG_SPI=y
+CONFIG_SPI_IMX=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_MAX732X=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_SABRESD_MAX8903=y
+CONFIG_IMX6_USB_CHARGER=y
+CONFIG_SENSORS_MAX17135=y
+CONFIG_SENSORS_MAG3110=y
+CONFIG_THERMAL=y
+CONFIG_CPU_THERMAL=y
+CONFIG_IMX_THERMAL=y
+CONFIG_DEVICE_THERMAL=y
+CONFIG_WATCHDOG=y
+CONFIG_IMX2_WDT=y
+CONFIG_MFD_DA9052_I2C=y
+CONFIG_MFD_MC13XXX_SPI=y
+CONFIG_MFD_MC13XXX_I2C=y
+CONFIG_MFD_MAX17135=y
+CONFIG_MFD_SI476X_CORE=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_DA9052=y
+CONFIG_REGULATOR_ANATOP=y
+CONFIG_REGULATOR_MC13783=y
+CONFIG_REGULATOR_MC13892=y
+CONFIG_REGULATOR_MAX17135=y
+CONFIG_REGULATOR_PFUZE100=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_MEDIA_RADIO_SUPPORT=y
+CONFIG_VIDEO_V4L2_INT_DEVICE=y
+CONFIG_MEDIA_USB_SUPPORT=y
+CONFIG_USB_VIDEO_CLASS=m
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_VIDEO_MXC_OUTPUT=y
+CONFIG_VIDEO_MXC_CAPTURE=m
+CONFIG_VIDEO_MXC_CSI_CAMERA=m
+CONFIG_MXC_CAMERA_OV5640=m
+CONFIG_MXC_CAMERA_OV5642=m
+CONFIG_MXC_CAMERA_OV5640_MIPI=m
+CONFIG_MXC_TVIN_ADV7180=m
+CONFIG_MXC_VADC=m
+CONFIG_MXC_IPU_DEVICE_QUEUE_SDC=m
+CONFIG_VIDEO_MXC_IPU_OUTPUT=y
+CONFIG_VIDEO_MXC_PXP_V4L2=y
+CONFIG_SOC_CAMERA=y
+CONFIG_VIDEO_MX3=y
+CONFIG_RADIO_SI476X=y
+CONFIG_SOC_CAMERA_OV2640=y
+CONFIG_DRM=y
+CONFIG_DRM_VIVANTE=y
+CONFIG_FB=y
+CONFIG_FB_MXS=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LCD_CLASS_DEVICE=y
+CONFIG_LCD_L4F00242T03=y
+CONFIG_LCD_PLATFORM=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_PWM=y
+CONFIG_FB_MXC_SYNC_PANEL=y
+CONFIG_FB_MXC_LDB=y
+CONFIG_FB_MXC_MIPI_DSI=y
+CONFIG_FB_MXC_TRULY_WVGA_SYNC_PANEL=y
+CONFIG_FB_MXC_HDMI=y
+CONFIG_FB_MXC_EINK_PANEL=y
+CONFIG_FB_MXS_SII902X=y
+CONFIG_FB_MXC_DCIC=m
+CONFIG_HANNSTAR_CABC=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y
+CONFIG_FONTS=y
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+CONFIG_LOGO=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_USB_AUDIO=m
+CONFIG_SND_SOC=y
+CONFIG_SND_IMX_SOC=y
+CONFIG_SND_SOC_EUKREA_TLV320=y
+CONFIG_SND_SOC_IMX_CS42888=y
+CONFIG_SND_SOC_IMX_WM8962=y
+CONFIG_SND_SOC_IMX_SGTL5000=y
+CONFIG_SND_SOC_IMX_SPDIF=y
+CONFIG_SND_SOC_IMX_MC13783=y
+CONFIG_SND_SOC_IMX_HDMI=y
+CONFIG_SND_SOC_IMX_SI476X=y
+CONFIG_SND_SOC_CS42XX8_I2C=y
+CONFIG_HID_SUNPLUS=m
+CONFIG_USB=y
+CONFIG_USB_OTG=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_ACM=m
+CONFIG_USB_STORAGE=y
+CONFIG_USB_CHIPIDEA=y
+CONFIG_USB_CHIPIDEA_UDC=y
+CONFIG_USB_CHIPIDEA_HOST=y
+CONFIG_USB_SERIAL=y
+CONFIG_USB_SERIAL_FTDI_SIO=m
+CONFIG_USB_SERIAL_KEYSPAN_PDA=y
+CONFIG_NOP_USB_XCEIV=y
+CONFIG_USB_MXS_PHY=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_ZERO=m
+CONFIG_USB_ETH=m
+CONFIG_USB_G_NCM=m
+CONFIG_USB_MASS_STORAGE=m
+CONFIG_FSL_UTP=y
+CONFIG_USB_G_SERIAL=m
+CONFIG_MMC=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_ESDHC_IMX=y
+CONFIG_MXC_IPU=y
+CONFIG_MXC_GPU_VIV=y
+CONFIG_MXC_ASRC=y
+CONFIG_MXC_MIPI_CSI2=y
+CONFIG_MXC_MLB150=m
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_GPIO=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_INTF_DEV_UIE_EMUL=y
+CONFIG_RTC_DRV_MCP7940X=y
+CONFIG_RTC_DRV_MC13XXX=y
+CONFIG_RTC_DRV_MXC=y
+CONFIG_RTC_DRV_SNVS=y
+CONFIG_DMADEVICES=y
+CONFIG_MXC_PXP_V2=y
+CONFIG_IMX_SDMA=y
+CONFIG_MXS_DMA=y
+CONFIG_STAGING=y
+CONFIG_COMMON_CLK_DEBUG=y
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_IIO=y
+CONFIG_VF610_ADC=y
+CONFIG_PWM=y
+CONFIG_PWM_IMX=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_QUOTA=y
+CONFIG_QUOTA_NETLINK_INTERFACE=y
+# CONFIG_PRINT_QUOTA_WARNING is not set
+CONFIG_AUTOFS4_FS=y
+CONFIG_FUSE_FS=y
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_UDF_FS=m
+CONFIG_MSDOS_FS=m
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_JFFS2_FS=y
+CONFIG_UBIFS_FS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_ROOT_NFS=y
+CONFIG_NLS_DEFAULT="cp437"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_UTF8=y
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_SCHED_DEBUG is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_FTRACE is not set
+CONFIG_SECURITYFS=y
+CONFIG_CRYPTO_USER=y
+CONFIG_CRYPTO_TEST=m
+CONFIG_CRYPTO_CCM=y
+CONFIG_CRYPTO_GCM=y
+CONFIG_CRYPTO_CBC=y
+CONFIG_CRYPTO_CTS=y
+CONFIG_CRYPTO_LRW=y
+CONFIG_CRYPTO_XTS=y
+CONFIG_CRYPTO_MD4=y
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_MICHAEL_MIC=y
+CONFIG_CRYPTO_RMD128=y
+CONFIG_CRYPTO_RMD160=y
+CONFIG_CRYPTO_RMD256=y
+CONFIG_CRYPTO_RMD320=y
+CONFIG_CRYPTO_SHA1_ARM=y
+CONFIG_CRYPTO_SHA512=y
+CONFIG_CRYPTO_TGR192=y
+CONFIG_CRYPTO_WP512=y
+CONFIG_CRYPTO_AES_ARM=y
+CONFIG_CRYPTO_BLOWFISH=y
+CONFIG_CRYPTO_CAMELLIA=y
+CONFIG_CRYPTO_DES=y
+CONFIG_CRYPTO_TWOFISH=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRYPTO_DEV_FSL_CAAM=y
+CONFIG_CRYPTO_DEV_FSL_CAAM_SM=y
+CONFIG_CRYPTO_DEV_FSL_CAAM_SM_TEST=y
+CONFIG_CRYPTO_DEV_FSL_CAAM_SECVIO=y
+CONFIG_CRC_CCITT=m
+CONFIG_CRC_T10DIF=y
+CONFIG_CRC7=m
+CONFIG_LIBCRC32C=m
diff --git a/recipes-kernel/linux/linux-apf6_3.10.53.bb b/recipes-kernel/linux/linux-apf6_3.10.53.bb
new file mode 100644
index 0000000..443ee1b
--- /dev/null
+++ b/recipes-kernel/linux/linux-apf6_3.10.53.bb
@@ -0,0 +1,10 @@
+SUMMARY = "Linux kernel for Armadeus board with Preemt RT patch"
+
+require linux-apf6.inc
+
+SRCBRANCH = "wandboard_imx_3.10.53_1.1.0_ga"
+SRCREV = "13bb18444208d5065f0e6345c7743e9662bc97d2"
+LOCALVERSION = "_1.1.0_ga-apf6"
+
+SRC_URI += "file://defconfig"
+
-- 
2.1.4



More information about the meta-freescale mailing list