[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 = <®_3p3v>;
++ VDDIO-supply = <®_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 = <®_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 = <®_3p3v>;
++ VDDIO-supply = <®_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 = <®_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