Merge branch imgsystems/4.1-imgsystems into aosp/master
Conflicts:
arch/mips/boot/dts/pistachio/Makefile
arch/mips/boot/dts/pistachio/pistachio_marduk.dts
Change-Id: Ibddb2db255830eb640d1df21b282b6a51369ea7c
diff --git a/Documentation/devicetree/bindings/misc/uboot-bootcount.txt b/Documentation/devicetree/bindings/misc/uboot-bootcount.txt
new file mode 100644
index 0000000..7ab1134
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/uboot-bootcount.txt
@@ -0,0 +1,34 @@
+U-Boot bootcount driver
+
+This driver implements the Linux kernel half of the boot count feature -
+the boot counter can only be reset after it is clear that the
+application has been started and is running correctly, which usually
+can only be determined by the application code itself. Thus the reset
+of the boot counter must be done by application code, which thus needs
+an appropriate driver.
+
+Required feature by the Carrier Grade Linux Requirements Definition;
+see for example document "Carrier Grade Linux Requirements Definition
+Overview V3.0" at
+
+http://www.linuxfoundation.org/collaborate/workgroups/cgl/requirements#SMM.6.0_Boot_Cycle_Detection
+
+ Description: OSDL CGL specifies that carrier grade Linux
+ shall provide support for detecting a repeating reboot cycle
+ due to recurring failures. This detection should happen in
+ user space before system services are started.
+
+This driver provides read/write access to the U-Boot bootcounter
+through sysfs file.
+
+Required properties:
+
+ - compatible : should be "uboot,bootcount"
+ - reg: the address of the bootcounter
+
+Example:
+
+bootcount@1c23000 {
+ compatible = "uboot,bootcount";
+ reg = <0x23060 0x1>;
+};
diff --git a/Documentation/devicetree/bindings/net/ieee802154/cc2520.txt b/Documentation/devicetree/bindings/net/ieee802154/cc2520.txt
index 3e1356b..338a015 100644
--- a/Documentation/devicetree/bindings/net/ieee802154/cc2520.txt
+++ b/Documentation/devicetree/bindings/net/ieee802154/cc2520.txt
@@ -15,11 +15,12 @@
- reset-gpio: GPIO spec for the RESET pin
Optional properties:
- amplified: include if the CC2520 is connected to a CC2591 amplifier
+ - #clock-cells from common clock binding; shall be set to 0
+ required if extclock-freq is specified
- extclock-freq: frequency setting of external clock generator, should be
between 1000000-16000000 (check datasheet for supported values)
extclock is disabled if extclock-freq = <0>, if not specified
defaults to 1MHz (reset value)
-
Example:
cc2520@0 {
compatible = "ti,cc2520";
@@ -34,5 +35,6 @@
cca-gpio = <&gpio1 16 0>;
vreg-gpio = <&gpio0 31 0>;
reset-gpio = <&gpio1 12 0>;
+ #clock-cells = <0>;
extclock-freq = <16000000>;
};
diff --git a/arch/mips/boot/dts/pistachio/pistachio.dtsi b/arch/mips/boot/dts/pistachio/pistachio.dtsi
index 05222e8..cb10b12 100644
--- a/arch/mips/boot/dts/pistachio/pistachio.dtsi
+++ b/arch/mips/boot/dts/pistachio/pistachio.dtsi
@@ -797,14 +797,14 @@
};
dac_clk_pin: dac-clk-pin {
- dac-clk {
+ pin_dac_clk: dac-clk {
pins = "mfio45";
function = "i2s_dac_clk";
};
};
i2s_mclk_pin: i2s-mclk-pin {
- i2s-mclk {
+ pin_i2s_mclk: i2s-mclk {
pins = "mfio36";
function = "i2s_out";
};
@@ -826,7 +826,7 @@
};
i2s_out_pins: i2s-out-pins {
- i2s-out {
+ pins_i2s_out: i2s-out {
pins = "mfio37", "mfio38", "mfio39",
"mfio40", "mfio41", "mfio42",
"mfio43", "mfio44";
@@ -878,7 +878,7 @@
wdt: watchdog@18102100 {
compatible = "img,pdc-wdt";
- reg = <0x18102100 0x100>;
+ reg = <0x18102100 0x20>;
interrupts = <GIC_SHARED 52 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk_periph PERIPH_CLK_WD>, <&cr_periph SYS_CLK_WD>;
clock-names = "wdt", "sys";
diff --git a/arch/mips/boot/dts/pistachio/pistachio_beetle.dtsi b/arch/mips/boot/dts/pistachio/pistachio_beetle.dtsi
index 6b7d4b9..11ae16e 100644
--- a/arch/mips/boot/dts/pistachio/pistachio_beetle.dtsi
+++ b/arch/mips/boot/dts/pistachio/pistachio_beetle.dtsi
@@ -50,19 +50,32 @@
};
};
+&uccpsystem {
+ status = "okay";
+};
+
+&uccphostport {
+ status = "okay";
+};
+
+&uccpbthp {
+ status = "okay";
+};
+
+&uccpdummyhp {
+ status = "okay";
+};
+
&uart0 {
- pinctrl-0 = <&uart0_pins>, <&uart0_rts_cts_pins>;
- pinctrl-names = "default";
-
status = "okay";
+ assigned-clock-rates = <114278400>, <1843200>;
};
-&uart1 {
+&sdhost {
status = "okay";
-};
-&usb {
- status = "okay";
+ bus-width = <4>;
+ disable-wp;
};
&i2c3 {
@@ -85,7 +98,7 @@
status = "okay";
mac-address0 = [0123456789AC];
mac-address1 = [0123456789AD];
- rf-params = [1E00000000002426292A2C2E3237393F454A52576066000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F00000000002426292A2C2E3237393F454A52576066000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F0808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808];
+ rf-params = [1A1f24292e33383c4045484d5157595d6165686d7177263d3135393c41454a4d505356595c5f6164676c7023282c3135393d4145494d53565a5d616466686b6f20252b3134373c4144484d52555a5e62666a6f747923282c31363a3e4245494e51575b5f63666b71767a1a1f24292e33383c4045484d5055585b5e62666a7223272e32363a3e43474c4f53585b5f63666a6f7479252a2e32373b3f43474b4f55585c5f626467696c7223272a3134393d4044494e52565a5d6165696d727425292f33383c4043474c5054595d6165696d73787c0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B];
num_streams = [02];
io-channels = <&adc 4>;
};
diff --git a/arch/mips/boot/dts/pistachio/pistachio_beetle_boardtest.dts b/arch/mips/boot/dts/pistachio/pistachio_beetle_boardtest.dts
index bd9bbd3..fea3fb8 100644
--- a/arch/mips/boot/dts/pistachio/pistachio_beetle_boardtest.dts
+++ b/arch/mips/boot/dts/pistachio/pistachio_beetle_boardtest.dts
@@ -7,102 +7,41 @@
* published by the Free Software Foundation.
*/
-/dts-v1/;
+#include "pistachio_beetle_mbub.dts"
-#include "pistachio_beetle.dtsi"
-#include <dt-bindings/sound/pistachio-audio.h>
-
-/ {
- model = "IMG Pistachio Concerto mBuB with a Beetle module";
- compatible = "img,pistachio-bub", "img,pistachio";
-
- aliases {
- serial0 = &uart0;
- serial1 = &uart1;
- };
-
- chosen {
- bootargs = "console=ttyS1,115200n8 earlycon=uart8250,mmio32,0x18101500,115200 root=/dev/sda1 rootwait ro";
- };
-
- reg_1v8: fixed-regulator {
- compatible = "regulator-fixed";
- regulator-name = "aux_adc_vref";
- regulator-min-microvolt = <1800000>;
- regulator-max-microvolt = <1800000>;
- regulator-boot-on;
- };
-
- supply5v0: supply5v0@0 {
- compatible = "regulator-fixed";
- regulator-name = "5V-supply";
- regulator-min-microvolt = <5000000>;
- regulator-max-microvolt = <5000000>;
- };
-
- pistachio_audio_card {
- compatible = "img,pistachio-audio";
-
- clocks = <&clk_core CLK_AUDIO_PLL>,
- <&clk_core CLK_I2S>,
- <&clk_core CLK_AUDIO>;
- clock-names = "audio_pll", "i2s_mclk", "dac_clk";
-
- img,cr-periph = <&cr_periph>;
- img,event-timer = <&event_timer>;
-
- parallel-out {
- cpu-dai = <¶llel_out>;
- sound-dai = <&internal_dac>;
- };
-
- };
-
+&ir {
+ status = "disabled";
};
-&uccpsystem {
- status = "okay";
+&pwm {
+ status = "disabled";
};
-&uccphostport {
- status = "okay";
+&i2c0 {
+ status = "disabled";
};
-&uccpbthp {
- status = "okay";
+&i2c1 {
+ status = "disabled";
};
-&uccpdummyhp {
- status = "okay";
+&i2c2 {
+ status = "disabled";
};
-&uart0 {
- status = "okay";
- assigned-clock-rates = <114278400>, <1843200>;
+&i2s_out {
+ status = "disabled";
};
-&uart1 {
- status = "okay";
+&i2s_in {
+ status = "disabled";
};
-&usb {
- status = "okay";
+&spdif_out {
+ status = "disabled";
};
-&enet {
- status = "okay";
-
- mac-address = [0123456789AB];
-};
-
-&sdhost {
- status = "okay";
-
- bus-width = <4>;
- disable-wp;
-};
-
-¶llel_out {
- status = "okay";
+&spdif_in {
+ status = "disabled";
};
diff --git a/arch/mips/boot/dts/pistachio/pistachio_beetle_mbub.dts b/arch/mips/boot/dts/pistachio/pistachio_beetle_mbub.dts
index 49be49c..ad152b5 100644
--- a/arch/mips/boot/dts/pistachio/pistachio_beetle_mbub.dts
+++ b/arch/mips/boot/dts/pistachio/pistachio_beetle_mbub.dts
@@ -11,9 +11,10 @@
#include "pistachio_beetle.dtsi"
#include <dt-bindings/sound/pistachio-audio.h>
+#include "pistachio_board_uboot.dtsi"
/ {
- model = "IMG Pistachio Concerto mBuB with a Beetle module";
+ model = "Beetle Bring-Up Board";
compatible = "img,pistachio-bub", "img,pistachio";
aliases {
@@ -95,25 +96,16 @@
};
};
-&uccpsystem {
- status = "okay";
+&pin_i2s_mclk {
+ drive-strength = <2>;
};
-&uccphostport {
- status = "okay";
+&pin_dac_clk {
+ drive-strength = <2>;
};
-&uccpbthp {
- status = "okay";
-};
-
-&uccpdummyhp {
- status = "okay";
-};
-
-&uart0 {
- status = "okay";
- assigned-clock-rates = <114278400>, <1843200>;
+&pins_i2s_out {
+ drive-strength = <2>;
};
&uart1 {
@@ -136,13 +128,6 @@
mac-address = [0123456789AB];
};
-&sdhost {
- status = "okay";
-
- bus-width = <4>;
- disable-wp;
-};
-
&ir {
status = "okay";
};
diff --git a/arch/mips/boot/dts/pistachio/pistachio_board_uboot.dtsi b/arch/mips/boot/dts/pistachio/pistachio_board_uboot.dtsi
new file mode 100644
index 0000000..2fde543
--- /dev/null
+++ b/arch/mips/boot/dts/pistachio/pistachio_board_uboot.dtsi
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) 2015 Imagination Technologies Ltd.
+ *
+ * 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.
+ */
+
+
+/ {
+ uboot-count {
+ compatible = "uboot,bootcount";
+ reg = <0x18102120 0x4>;
+ };
+};
diff --git a/arch/mips/boot/dts/pistachio/pistachio_bub.dts b/arch/mips/boot/dts/pistachio/pistachio_bub.dts
index 0750ca8..9e9aee4 100644
--- a/arch/mips/boot/dts/pistachio/pistachio_bub.dts
+++ b/arch/mips/boot/dts/pistachio/pistachio_bub.dts
@@ -10,6 +10,7 @@
/dts-v1/;
#include "pistachio_bub_audio_no_daughterboard_example.dtsi"
+#include "pistachio_board_uboot.dtsi"
/ {
model = "IMG Pistachio BuB";
diff --git a/arch/mips/boot/dts/pistachio/pistachio_concerto.dtsi b/arch/mips/boot/dts/pistachio/pistachio_concerto.dtsi
index 5e35f4d..137afb0 100644
--- a/arch/mips/boot/dts/pistachio/pistachio_concerto.dtsi
+++ b/arch/mips/boot/dts/pistachio/pistachio_concerto.dtsi
@@ -31,9 +31,10 @@
cs-gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>, <&gpio0 1 GPIO_ACTIVE_HIGH>;
flash@0 {
- compatible = "spidev";
+ compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <50000000>;
+ linux,mtd-name = "spi-nor";
};
flash@1 {
@@ -44,22 +45,29 @@
spi-rx-bus-width = <4>;
#address-cells = <1>;
#size-cells = <1>;
+ linux,mtd-name = "spi-nand";
};
};
+&uccpsystem {
+ status = "okay";
+};
+
+&uccphostport {
+ status = "okay";
+};
+
+&uccpbthp {
+ status = "okay";
+};
+
+&uccpdummyhp {
+ status = "okay";
+};
+
&uart0 {
- pinctrl-0 = <&uart0_pins>, <&uart0_rts_cts_pins>;
- pinctrl-names = "default";
-
status = "okay";
-};
-
-&uart1 {
- status = "okay";
-};
-
-&usb {
- status = "okay";
+ assigned-clock-rates = <114278400>, <1843200>;
};
&i2c3 {
@@ -67,16 +75,20 @@
clock-frequency = <400000>;
};
-//&i2s_out {
-// status = "okay";
-//};
-
-//&i2s_in {
-// status = "okay";
-//};
+&sdhost {
+ status = "okay";
+};
&adc {
status = "okay";
vref-supply = <&adc_1v8>;
};
+
+&wifi {
+ status = "okay";
+ mac-address0 = [0123456789AC];
+ mac-address1 = [0123456789AD];
+ rf-params = [1E00000000002426292A2C2E3237393F454A52576066000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F00000000002426292A2C2E3237393F454A52576066000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F0808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808];
+ num_streams = [02];
+};
diff --git a/arch/mips/boot/dts/pistachio/pistachio_concerto_mbub.dts b/arch/mips/boot/dts/pistachio/pistachio_concerto_mbub.dts
index b0d8689..afddda8 100644
--- a/arch/mips/boot/dts/pistachio/pistachio_concerto_mbub.dts
+++ b/arch/mips/boot/dts/pistachio/pistachio_concerto_mbub.dts
@@ -9,11 +9,12 @@
/dts-v1/;
-#include "pistachio.dtsi"
+#include "pistachio_concerto.dtsi"
#include <dt-bindings/sound/pistachio-audio.h>
+#include "pistachio_board_uboot.dtsi"
/ {
- model = "IMG Pistachio Concerto mBuB";
+ model = "Concerto Bring-Up-Board";
compatible = "img,pistachio-bub", "img,pistachio";
aliases {
@@ -101,53 +102,6 @@
};
};
-&spfi1 {
- status = "okay";
-
- pinctrl-0 = <&spim1_pins>, <&spim1_quad_pins>;
- pinctrl-names = "default";
- cs-gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>, <&gpio0 1 GPIO_ACTIVE_HIGH>;
-
- flash@0 {
- compatible = "jedec,spi-nor";
- reg = <0>;
- spi-max-frequency = <50000000>;
- linux,mtd-name = "spi-nor";
- };
-
- flash@1 {
- compatible = "gigadevice,gd5f";
- reg = <1>;
- spi-max-frequency = <50000000>;
- nand-on-flash-bbt;
- spi-rx-bus-width = <4>;
- #address-cells = <1>;
- #size-cells = <1>;
- linux,mtd-name = "spi-nand";
- };
-};
-
-&uccpsystem {
- status = "okay";
-};
-
-&uccphostport {
- status = "okay";
-};
-
-&uccpbthp {
- status = "okay";
-};
-
-&uccpdummyhp {
- status = "okay";
-};
-
-&uart0 {
- status = "okay";
- assigned-clock-rates = <114278400>, <1843200>;
-};
-
&uart1 {
status = "okay";
};
@@ -162,10 +116,6 @@
mac-address = [0123456789AB];
};
-&sdhost {
- status = "okay";
-};
-
&ir {
status = "okay";
};
@@ -198,19 +148,6 @@
clock-frequency = <400000>;
};
-&i2c3 {
- status = "okay";
- clock-frequency = <400000>;
-};
-
-&wifi {
- status = "okay";
- mac-address0 = [0123456789AC];
- mac-address1 = [0123456789AD];
- rf-params = [1E00000000002426292A2C2E3237393F454A52576066000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F00000000002426292A2C2E3237393F454A52576066000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F0808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808];
- num_streams = [02];
-};
-
&i2s_out {
status = "okay";
};
diff --git a/arch/mips/boot/dts/pistachio/pistachio_marduk.dts b/arch/mips/boot/dts/pistachio/pistachio_marduk.dts
index d626837..003271a 100644
--- a/arch/mips/boot/dts/pistachio/pistachio_marduk.dts
+++ b/arch/mips/boot/dts/pistachio/pistachio_marduk.dts
@@ -10,6 +10,7 @@
#include "pistachio.dtsi"
#include <dt-bindings/sound/pistachio-audio.h>
+#include "pistachio_board_uboot.dtsi"
/ {
model = "IMG Marduk";
@@ -38,14 +39,6 @@
regulator-boot-on;
};
- /* EXT clock from cc2520 is fed to sc16is752 */
- cc2520_ext_clk: cc2520-ext-clk {
- compatible = "fixed-clock";
- #clock-cells = <0>;
- clock-frequency = <16000000>;
- clock-output-names = "cc2520_ext_clock";
- };
-
pistachio_audio_card {
compatible = "img,pistachio-audio";
@@ -81,6 +74,13 @@
i2s-in {
cpu-dai = <&i2s_in>;
format = "i2s";
+
+ ak5720vt {
+ mclk = <PISTACHIO_MCLK_I2S>;
+ mclk-fs = <256>;
+ mclk-min-freq = <8192000>;
+ mclk-max-freq = <24576000>;
+ };
};
};
@@ -148,7 +148,7 @@
cs-gpios = <&gpio1 14 GPIO_ACTIVE_HIGH>, <&gpio0 2 GPIO_ACTIVE_HIGH>,
<&gpio1 12 GPIO_ACTIVE_HIGH>, <&gpio1 13 GPIO_ACTIVE_HIGH>;
- cc2520@0 {
+ cc2520: cc2520@0 {
compatible = "ti,cc2520";
reg = <0>;
spi-max-frequency = <4000000>;
@@ -158,13 +158,14 @@
cca-gpio = <&gpio3 6 GPIO_ACTIVE_HIGH>;
vreg-gpio = <&gpio2 12 GPIO_ACTIVE_HIGH>;
reset-gpio = <&gpio0 12 GPIO_ACTIVE_HIGH>;
+ #clock-cells = <0>;
extclock-freq = <16000000>;
};
sc16is752: sc16is752@1 {
compatible = "nxp,sc16is752";
reg = <1>;
- clocks = <&cc2520_ext_clk>;
+ clocks = <&cc2520 0>;
spi-max-frequency = <4000000>;
interrupt-parent = <&gpio0>;
interrupts = <11 IRQ_TYPE_EDGE_FALLING>;
@@ -265,6 +266,7 @@
&adc {
status = "okay";
vref-supply = <®_1v8>;
+ adc-reserved-channels = <0x10>;
};
&i2c2 {
@@ -281,7 +283,7 @@
status = "okay";
mac-address0 = [0019f5ffff01];
mac-address1 = [0019f5ffff02];
- rf-params = [1E00000000002426292A2C2E3237393F454A52576066000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F00000000002426292A2C2E3237393F454A52576066000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F0808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808];
+ rf-params = [1A1c1f24292e33383c4045484d5157595d6165686d712023263d3135393c41454a4d505356595c5f6164672023282c3135393d4145494d53565a5e626567696c1c20252b3134373c4144484d52555a5e62666a6f742023282c31363a3e4245494e51575b5f63666b71761c1f24292e33383c4045494d5155585b5e62666a6f1e22262d3135393d42464b4e52575a5c6064676b702023282c3135393d4145494d5356595b5e606366692023272a31363b3f44484c5054575a5e6165696d711f2225292f33383c4043474c5054595d6165696d732828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828282828];
num_streams = [02];
};
diff --git a/arch/mips/configs/pistachio_defconfig b/arch/mips/configs/pistachio_defconfig
index 2c602de..260f6c2 100644
--- a/arch/mips/configs/pistachio_defconfig
+++ b/arch/mips/configs/pistachio_defconfig
@@ -164,11 +164,13 @@
CONFIG_MTD_M25P80=y
CONFIG_MTD_SPI_NAND=y
CONFIG_MTD_SPI_NOR=y
+CONFIG_MTD_SPI_NOR_WINBOND_OTP=y
CONFIG_MTD_UBI=y
CONFIG_MTD_UBI_BLOCK=y
CONFIG_ZRAM=m
CONFIG_BLK_DEV_LOOP=y
CONFIG_SRAM=y
+CONFIG_UBOOT_BOOTCOUNT=y
CONFIG_ATU=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
@@ -224,7 +226,7 @@
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_DW=y
CONFIG_SERIAL_OF_PLATFORM=y
-CONFIG_SERIAL_SC16IS7XX=m
+CONFIG_SERIAL_SC16IS7XX=y
# CONFIG_SERIAL_SC16IS7XX_I2C is not set
CONFIG_SERIAL_SC16IS7XX_SPI=y
CONFIG_HW_RANDOM=y
@@ -335,7 +337,7 @@
CONFIG_JOLIET=y
CONFIG_ZISOFS=y
CONFIG_UDF_FS=m
-CONFIG_VFAT_FS=m
+CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y
CONFIG_ECRYPT_FS=y
@@ -350,9 +352,9 @@
CONFIG_NFS_FS=y
CONFIG_ROOT_NFS=y
CONFIG_NLS_DEFAULT="utf8"
-CONFIG_NLS_CODEPAGE_437=m
+CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_ASCII=m
-CONFIG_NLS_ISO8859_1=m
+CONFIG_NLS_ISO8859_1=y
CONFIG_PRINTK_TIME=y
CONFIG_DEBUG_INFO=y
CONFIG_MAGIC_SYSRQ=y
@@ -385,3 +387,4 @@
CONFIG_CRC7=m
CONFIG_LIBCRC32C=m
# CONFIG_XZ_DEC_X86 is not set
+CONFIG_MAC80211_RC_MINSTREL_VHT=y
diff --git a/arch/mips/configs/pistachio_prodtest_defconfig b/arch/mips/configs/pistachio_prodtest_defconfig
new file mode 100644
index 0000000..74bea90
--- /dev/null
+++ b/arch/mips/configs/pistachio_prodtest_defconfig
@@ -0,0 +1,390 @@
+CONFIG_MACH_PISTACHIO=y
+CONFIG_PISTACHIO_GPTIMER_CLKSRC=y
+CONFIG_MIPS_MT_SMP=y
+CONFIG_MIPS_CPS=y
+# CONFIG_COMPACTION is not set
+CONFIG_DEFAULT_MMAP_MIN_ADDR=32768
+CONFIG_CMA=y
+CONFIG_ZSMALLOC=y
+CONFIG_NR_CPUS=4
+CONFIG_PREEMPT_VOLUNTARY=y
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_KERNEL_LZO=y
+CONFIG_DEFAULT_HOSTNAME="localhost"
+CONFIG_SYSVIPC=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_IKCONFIG=m
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=18
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_CFS_BANDWIDTH=y
+CONFIG_NAMESPACES=y
+CONFIG_USER_NS=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_RD_BZIP2 is not set
+# CONFIG_RD_LZMA is not set
+# CONFIG_RD_LZO is not set
+# CONFIG_RD_LZ4 is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_EMBEDDED=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_PROFILING=y
+CONFIG_CC_STACKPROTECTOR_STRONG=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_PM_AUTOSLEEP=y
+CONFIG_PM_WAKELOCKS=y
+CONFIG_PM_DEBUG=y
+CONFIG_PM_ADVANCED_DEBUG=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_STAT_DETAILS=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPUFREQ_DT=y
+CONFIG_CPU_IDLE=y
+# CONFIG_MIPS_CPS_CPUIDLE is not set
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=m
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_MROUTE=y
+CONFIG_IP_PIMSM_V1=y
+CONFIG_IP_PIMSM_V2=y
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+CONFIG_INET_XFRM_MODE_TRANSPORT=m
+CONFIG_INET_XFRM_MODE_TUNNEL=m
+CONFIG_INET_XFRM_MODE_BEET=m
+# CONFIG_INET_DIAG is not set
+CONFIG_TCP_CONG_ADVANCED=y
+# CONFIG_TCP_CONG_BIC is not set
+# CONFIG_TCP_CONG_WESTWOOD is not set
+# CONFIG_TCP_CONG_HTCP is not set
+CONFIG_TCP_CONG_LP=m
+CONFIG_TCP_MD5SIG=y
+CONFIG_IPV6=y
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_XFRM_MODE_TRANSPORT=m
+CONFIG_INET6_XFRM_MODE_TUNNEL=m
+CONFIG_INET6_XFRM_MODE_BEET=m
+CONFIG_IPV6_SIT=m
+CONFIG_NETWORK_SECMARK=y
+CONFIG_NETFILTER=y
+# CONFIG_BRIDGE_NETFILTER is not set
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CT_NETLINK=y
+CONFIG_NETFILTER_XT_MARK=m
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
+CONFIG_NETFILTER_XT_TARGET_DSCP=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_TARGET_SECMARK=y
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=m
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_DSCP=y
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_NF_NAT_IPV4=m
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_MANGLE=y
+CONFIG_NF_CONNTRACK_IPV6=m
+CONFIG_NF_NAT_IPV6=m
+CONFIG_IP6_NF_IPTABLES=m
+CONFIG_IP6_NF_MATCH_IPV6HEADER=m
+CONFIG_IP6_NF_FILTER=m
+CONFIG_IP6_NF_TARGET_REJECT=m
+CONFIG_IP6_NF_MANGLE=m
+CONFIG_BRIDGE=m
+CONFIG_VLAN_8021Q=m
+CONFIG_6LOWPAN=y
+CONFIG_IEEE802154=y
+CONFIG_IEEE802154_6LOWPAN=y
+CONFIG_MAC802154=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=m
+CONFIG_NET_SCH_CODEL=m
+CONFIG_NET_SCH_FQ_CODEL=m
+CONFIG_NET_CLS_U32=m
+CONFIG_CLS_U32_MARK=y
+CONFIG_NET_PKTGEN=y
+CONFIG_BT=y
+CONFIG_BT_RFCOMM=y
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=y
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=y
+CONFIG_BT_HCIBTUSB=y
+CONFIG_BT_HCIBTSDIO=y
+CONFIG_BT_HCIUART=y
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_BCSP=y
+CONFIG_BT_HCIBFUSB=y
+CONFIG_BT_HCIVHCI=y
+CONFIG_BT_IMG=m
+CONFIG_CFG80211=m
+CONFIG_NL80211_TESTMODE=y
+# CONFIG_CFG80211_DEFAULT_PS is not set
+CONFIG_CFG80211_DEBUGFS=y
+CONFIG_CFG80211_WEXT=y
+CONFIG_MAC80211=m
+CONFIG_MAC80211_LEDS=y
+CONFIG_MAC80211_DEBUGFS=y
+CONFIG_MAC80211_DEBUG_MENU=y
+CONFIG_MAC80211_VERBOSE_DEBUG=y
+CONFIG_RFKILL=y
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_DEBUG_DEVRES=y
+CONFIG_CONNECTOR=y
+CONFIG_MTD=y
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_M25P80=y
+CONFIG_MTD_SPI_NAND=y
+CONFIG_MTD_SPI_NOR=y
+CONFIG_MTD_SPI_NOR_WINBOND_OTP=y
+CONFIG_MTD_UBI=y
+CONFIG_MTD_UBI_BLOCK=y
+CONFIG_ZRAM=m
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_SRAM=y
+CONFIG_UBOOT_BOOTCOUNT=y
+CONFIG_ATU=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_BLK_DEV_SR=m
+CONFIG_SCSI_SPI_ATTRS=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_VERITY=y
+CONFIG_NETDEVICES=y
+CONFIG_TUN=m
+CONFIG_VETH=m
+# 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_NET_VENDOR_SMSC is not set
+CONFIG_STMMAC_ETH=y
+# CONFIG_NET_VENDOR_VIA is not set
+CONFIG_PPP=m
+CONFIG_PPP_ASYNC=m
+CONFIG_USB_PEGASUS=m
+CONFIG_USB_RTL8150=m
+CONFIG_USB_RTL8152=m
+CONFIG_USB_NET_DM9601=m
+CONFIG_USB_NET_SMSC75XX=m
+CONFIG_USB_NET_SMSC95XX=m
+CONFIG_USB_NET_MCS7830=m
+# CONFIG_USB_NET_CDC_SUBSET is not set
+# CONFIG_USB_NET_ZAURUS is not set
+CONFIG_LIBERTAS_THINFIRM=m
+CONFIG_USB_NET_RNDIS_WLAN=m
+CONFIG_MAC80211_HWSIM=m
+CONFIG_HOSTAP=m
+CONFIG_HOSTAP_FIRMWARE=y
+CONFIG_HOSTAP_FIRMWARE_NVRAM=y
+CONFIG_RT2X00=m
+CONFIG_RT2800USB=m
+CONFIG_UCCP420WLAN=m
+CONFIG_IEEE802154_CC2520=y
+# CONFIG_INPUT_MOUSEDEV is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_KEYBOARD_ATKBD is not set
+CONFIG_KEYBOARD_GPIO=y
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_SERIO is not set
+# CONFIG_VT is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_8250=y
+# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
+# CONFIG_SERIAL_8250_CONSOLE is not set
+CONFIG_SERIAL_8250_DW=y
+CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_SERIAL_SC16IS7XX=y
+# CONFIG_SERIAL_SC16IS7XX_I2C is not set
+CONFIG_SERIAL_SC16IS7XX_SPI=y
+CONFIG_HW_RANDOM=y
+CONFIG_TCG_TPM=y
+CONFIG_TCG_TIS_I2C_INFINEON=y
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=m
+CONFIG_I2C_IMG=y
+CONFIG_I2C_STUB=m
+CONFIG_SPI=y
+CONFIG_SPI_BITBANG=m
+CONFIG_SPI_IMG_SPFI=y
+CONFIG_SPI_SPIDEV=y
+CONFIG_DEBUG_GPIO=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_THERMAL=y
+CONFIG_WATCHDOG=y
+CONFIG_WATCHDOG_CORE=y
+CONFIG_IMGPDC_WDT=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_GPIO=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_RC_SUPPORT=y
+# CONFIG_RC_DECODERS is not set
+CONFIG_RC_DEVICES=y
+CONFIG_IR_IMG=y
+CONFIG_IR_IMG_NEC=y
+CONFIG_IR_IMG_JVC=y
+CONFIG_IR_IMG_SONY=y
+CONFIG_IR_IMG_SHARP=y
+CONFIG_IR_IMG_SANYO=y
+CONFIG_IR_IMG_RC5=y
+CONFIG_IR_IMG_RC6=y
+# CONFIG_DVB_TUNER_DIB0070 is not set
+# CONFIG_DVB_TUNER_DIB0090 is not set
+CONFIG_FB=y
+CONFIG_FB_MODE_HELPERS=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SEQUENCER=m
+CONFIG_SND_SEQ_DUMMY=m
+CONFIG_SND_HRTIMER=m
+CONFIG_SND_DYNAMIC_MINORS=y
+# CONFIG_SND_SPI is not set
+CONFIG_SND_USB_AUDIO=m
+CONFIG_SND_SOC=y
+CONFIG_SND_SOC_IMG=y
+CONFIG_SND_SOC_IMG_PISTACHIO_BUB=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_OTG=y
+CONFIG_USB_MON=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_ROOT_HUB_TT=y
+CONFIG_USB_ACM=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_DWC2=y
+CONFIG_USB_DWC2_PLATFORM=y
+CONFIG_USB_SERIAL=y
+CONFIG_USB_SERIAL_GENERIC=y
+CONFIG_USB_SERIAL_CP210X=m
+CONFIG_USB_SERIAL_FTDI_SIO=m
+CONFIG_USB_SERIAL_KEYSPAN=m
+CONFIG_USB_SERIAL_PL2303=m
+CONFIG_USB_SERIAL_OTI6858=m
+CONFIG_USB_SERIAL_QUALCOMM=m
+CONFIG_USB_SERIAL_SIERRAWIRELESS=m
+CONFIG_USB_SERIAL_OPTION=m
+CONFIG_USB_GADGET=y
+CONFIG_USB_MASS_STORAGE=m
+CONFIG_USB_G_SERIAL=m
+CONFIG_MMC=y
+CONFIG_MMC_BLOCK_MINORS=16
+CONFIG_MMC_TEST=m
+CONFIG_MMC_DW=y
+CONFIG_MMC_DW_IDMAC=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=m
+CONFIG_LEDS_TRIGGER_HEARTBEAT=m
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_PISTACHIO=y
+CONFIG_DMADEVICES=y
+CONFIG_IMG_MDC_DMA=y
+CONFIG_STAGING=y
+CONFIG_ASHMEM=y
+# CONFIG_ANDROID_TIMED_OUTPUT is not set
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_SOC_IMG=y
+CONFIG_MEMORY=y
+CONFIG_IIO=y
+CONFIG_CC10001_ADC=y
+CONFIG_PWM=y
+CONFIG_PWM_IMG=y
+CONFIG_PHY_PISTACHIO_USB=y
+CONFIG_ANDROID=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
+# CONFIG_DNOTIFY is not set
+CONFIG_FUSE_FS=m
+CONFIG_OVERLAY_FS=y
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_UDF_FS=m
+CONFIG_VFAT_FS=m
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_ECRYPT_FS=y
+CONFIG_HFSPLUS_FS=m
+CONFIG_UBIFS_FS=y
+CONFIG_SQUASHFS=y
+CONFIG_SQUASHFS_FILE_DIRECT=y
+CONFIG_SQUASHFS_LZO=y
+CONFIG_PSTORE=y
+CONFIG_PSTORE_CONSOLE=y
+CONFIG_PSTORE_RAM=y
+CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
+CONFIG_NLS_DEFAULT="utf8"
+CONFIG_NLS_CODEPAGE_437=m
+CONFIG_NLS_ASCII=m
+CONFIG_NLS_ISO8859_1=m
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_INFO=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0
+CONFIG_LOCKUP_DETECTOR=y
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y
+CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y
+# CONFIG_SCHED_DEBUG is not set
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+CONFIG_DEBUG_SPINLOCK=y
+CONFIG_DEBUG_CREDENTIALS=y
+CONFIG_FUNCTION_TRACER=y
+CONFIG_BLK_DEV_IO_TRACE=y
+CONFIG_LKDTM=y
+CONFIG_TEST_UDELAY=m
+CONFIG_KEYS=y
+CONFIG_SECURITY=y
+CONFIG_SECURITY_NETWORK=y
+CONFIG_SECURITY_YAMA=y
+CONFIG_SECURITY_YAMA_STACKED=y
+CONFIG_DEFAULT_SECURITY_DAC=y
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_SHA512=m
+CONFIG_CRYPTO_ARC4=y
+CONFIG_CRYPTO_DES=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRYPTO_DEV_IMGTEC_HASH=y
+CONFIG_CRC_T10DIF=m
+CONFIG_CRC7=m
+CONFIG_LIBCRC32C=m
+# CONFIG_XZ_DEC_X86 is not set
+CONFIG_MAC80211_RC_MINSTREL_VHT=y
diff --git a/arch/mips/include/asm/syscall.h b/arch/mips/include/asm/syscall.h
index 6499d93..47bc45a 100644
--- a/arch/mips/include/asm/syscall.h
+++ b/arch/mips/include/asm/syscall.h
@@ -101,10 +101,8 @@
/* O32 ABI syscall() - Either 64-bit with O32 or 32-bit */
if ((config_enabled(CONFIG_32BIT) ||
test_tsk_thread_flag(task, TIF_32BIT_REGS)) &&
- (regs->regs[2] == __NR_syscall)) {
+ (regs->regs[2] == __NR_syscall))
i++;
- n++;
- }
while (n--)
ret |= mips_get_syscall_arg(args++, task, regs, i++);
diff --git a/arch/mips/kernel/scall32-o32.S b/arch/mips/kernel/scall32-o32.S
index 4cc1350..aa2df3e8 100644
--- a/arch/mips/kernel/scall32-o32.S
+++ b/arch/mips/kernel/scall32-o32.S
@@ -36,16 +36,8 @@
lw t1, PT_EPC(sp) # skip syscall on return
subu v0, v0, __NR_O32_Linux # check syscall number
- sltiu t0, v0, __NR_O32_Linux_syscalls + 1
addiu t1, 4 # skip to next instruction
sw t1, PT_EPC(sp)
- beqz t0, illegal_syscall
-
- sll t0, v0, 2
- la t1, sys_call_table
- addu t1, t0
- lw t2, (t1) # syscall routine
- beqz t2, illegal_syscall
sw a3, PT_R26(sp) # save a3 for syscall restarting
@@ -96,6 +88,16 @@
li t1, _TIF_WORK_SYSCALL_ENTRY
and t0, t1
bnez t0, syscall_trace_entry # -> yes
+syscall_common:
+ sltiu t0, v0, __NR_O32_Linux_syscalls + 1
+ beqz t0, illegal_syscall
+
+ sll t0, v0, 2
+ la t1, sys_call_table
+ addu t1, t0
+ lw t2, (t1) # syscall routine
+
+ beqz t2, illegal_syscall
jalr t2 # Do The Real Thing (TM)
@@ -116,7 +118,7 @@
syscall_trace_entry:
SAVE_STATIC
- move s0, t2
+ move s0, v0
move a0, sp
/*
@@ -129,27 +131,18 @@
1: jal syscall_trace_enter
- bltz v0, 2f # seccomp failed? Skip syscall
+ bltz v0, 1f # seccomp failed? Skip syscall
- move t0, s0
+ move v0, s0 # restore syscall
+
RESTORE_STATIC
lw a0, PT_R4(sp) # Restore argument registers
lw a1, PT_R5(sp)
lw a2, PT_R6(sp)
lw a3, PT_R7(sp)
- jalr t0
+ j syscall_common
- li t0, -EMAXERRNO - 1 # error?
- sltu t0, t0, v0
- sw t0, PT_R7(sp) # set error flag
- beqz t0, 1f
-
- lw t1, PT_R2(sp) # syscall number
- negu v0 # error
- sw t1, PT_R0(sp) # save it for syscall restarting
-1: sw v0, PT_R2(sp) # result
-
-2: j syscall_exit
+1: j syscall_exit
/* ------------------------------------------------------------------------ */
diff --git a/arch/mips/kernel/scall64-64.S b/arch/mips/kernel/scall64-64.S
index ad4d4463..676fda7 100644
--- a/arch/mips/kernel/scall64-64.S
+++ b/arch/mips/kernel/scall64-64.S
@@ -39,18 +39,11 @@
.set at
#endif
- dsubu t0, v0, __NR_64_Linux # check syscall number
- sltiu t0, t0, __NR_64_Linux_syscalls + 1
#if !defined(CONFIG_MIPS32_O32) && !defined(CONFIG_MIPS32_N32)
ld t1, PT_EPC(sp) # skip syscall on return
daddiu t1, 4 # skip to next instruction
sd t1, PT_EPC(sp)
#endif
- beqz t0, illegal_syscall
-
- dsll t0, v0, 3 # offset into table
- ld t2, (sys_call_table - (__NR_64_Linux * 8))(t0)
- # syscall routine
sd a3, PT_R26(sp) # save a3 for syscall restarting
@@ -59,6 +52,17 @@
and t0, t1, t0
bnez t0, syscall_trace_entry
+syscall_common:
+ dsubu t2, v0, __NR_64_Linux
+ sltiu t0, t2, __NR_64_Linux_syscalls + 1
+ beqz t0, illegal_syscall
+
+ dsll t0, t2, 3 # offset into table
+ dla t2, sys_call_table
+ daddu t0, t2, t0
+ ld t2, (t0) # syscall routine
+ beqz t2, illegal_syscall
+
jalr t2 # Do The Real Thing (TM)
li t0, -EMAXERRNO - 1 # error?
@@ -78,14 +82,14 @@
syscall_trace_entry:
SAVE_STATIC
- move s0, t2
+ move s0, v0
move a0, sp
daddiu a1, v0, __NR_64_Linux
jal syscall_trace_enter
- bltz v0, 2f # seccomp failed? Skip syscall
+ bltz v0, 1f # seccomp failed? Skip syscall
- move t0, s0
+ move v0, s0
RESTORE_STATIC
ld a0, PT_R4(sp) # Restore argument registers
ld a1, PT_R5(sp)
@@ -93,19 +97,9 @@
ld a3, PT_R7(sp)
ld a4, PT_R8(sp)
ld a5, PT_R9(sp)
- jalr t0
+ j syscall_common
- li t0, -EMAXERRNO - 1 # error?
- sltu t0, t0, v0
- sd t0, PT_R7(sp) # set error flag
- beqz t0, 1f
-
- ld t1, PT_R2(sp) # syscall number
- dnegu v0 # error
- sd t1, PT_R0(sp) # save it for syscall restarting
-1: sd v0, PT_R2(sp) # result
-
-2: j syscall_exit
+1: j syscall_exit
illegal_syscall:
/* This also isn't a 64-bit syscall, throw an error. */
diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S
index 446cc65..611ba3b 100644
--- a/arch/mips/kernel/scall64-n32.S
+++ b/arch/mips/kernel/scall64-n32.S
@@ -52,6 +52,7 @@
and t0, t1, t0
bnez t0, n32_syscall_trace_entry
+syscall_common:
jalr t2 # Do The Real Thing (TM)
li t0, -EMAXERRNO - 1 # error?
@@ -75,9 +76,9 @@
daddiu a1, v0, __NR_N32_Linux
jal syscall_trace_enter
- bltz v0, 2f # seccomp failed? Skip syscall
+ bltz v0, 1f # seccomp failed? Skip syscall
- move t0, s0
+ move t2, s0
RESTORE_STATIC
ld a0, PT_R4(sp) # Restore argument registers
ld a1, PT_R5(sp)
@@ -85,19 +86,9 @@
ld a3, PT_R7(sp)
ld a4, PT_R8(sp)
ld a5, PT_R9(sp)
- jalr t0
+ j syscall_common
- li t0, -EMAXERRNO - 1 # error?
- sltu t0, t0, v0
- sd t0, PT_R7(sp) # set error flag
- beqz t0, 1f
-
- ld t1, PT_R2(sp) # syscall number
- dnegu v0 # error
- sd t1, PT_R0(sp) # save it for syscall restarting
-1: sd v0, PT_R2(sp) # result
-
-2: j syscall_exit
+1: j syscall_exit
not_n32_scall:
/* This is not an n32 compatibility syscall, pass it on to
diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S
index 66d618b..64397d2 100644
--- a/arch/mips/kernel/scall64-o32.S
+++ b/arch/mips/kernel/scall64-o32.S
@@ -87,6 +87,7 @@
and t0, t1, t0
bnez t0, trace_a_syscall
+syscall_common:
jalr t2 # Do The Real Thing (TM)
li t0, -EMAXERRNO - 1 # error?
@@ -130,9 +131,9 @@
1: jal syscall_trace_enter
- bltz v0, 2f # seccomp failed? Skip syscall
+ bltz v0, 1f # seccomp failed? Skip syscall
- move t0, s0
+ move t2, s0
RESTORE_STATIC
ld a0, PT_R4(sp) # Restore argument registers
ld a1, PT_R5(sp)
@@ -142,19 +143,9 @@
ld a5, PT_R9(sp)
ld a6, PT_R10(sp)
ld a7, PT_R11(sp) # For indirect syscalls
- jalr t0
+ j syscall_common
- li t0, -EMAXERRNO - 1 # error?
- sltu t0, t0, v0
- sd t0, PT_R7(sp) # set error flag
- beqz t0, 1f
-
- ld t1, PT_R2(sp) # syscall number
- dnegu v0 # error
- sd t1, PT_R0(sp) # save it for syscall restarting
-1: sd v0, PT_R2(sp) # result
-
-2: j syscall_exit
+1: j syscall_exit
/* ------------------------------------------------------------------------ */
diff --git a/arch/mips/pistachio/init.c b/arch/mips/pistachio/init.c
index 890ac8a..1f90fd9 100644
--- a/arch/mips/pistachio/init.c
+++ b/arch/mips/pistachio/init.c
@@ -2,6 +2,7 @@
* Pistachio platform setup
*
* Copyright (C) 2014 Google, Inc.
+ * Copyright (C) 2016 Imagination Technologies
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -9,6 +10,7 @@
*/
#include <linux/init.h>
+#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/of_address.h>
#include <linux/of_fdt.h>
@@ -26,9 +28,28 @@
#include <asm/traps.h>
#include <asm/reboot.h>
+/*
+ * Core revision register decoding
+ * Bits 23 to 20: Major rev
+ * Bits 15 to 8: Minor rev
+ * Bits 7 to 0: Maintenance rev
+ */
+#define PISTACHIO_CORE_REV_REG 0xB81483D0
+#define PISTACHIO_CORE_REV_A1 0x00100006
+#define PISTACHIO_CORE_REV_B0 0x00100106
+
const char *get_system_type(void)
{
- return "IMG Pistachio SoC";
+ u32 core_rev;
+
+ core_rev = __raw_readl((const void *)PISTACHIO_CORE_REV_REG);
+
+ if (core_rev == PISTACHIO_CORE_REV_B0)
+ return "IMG Pistachio SoC (B0)";
+ else if (core_rev == PISTACHIO_CORE_REV_A1)
+ return "IMG_Pistachio SoC (A1)";
+ else
+ return "IMG_Pistachio SoC";
}
static void __init plat_setup_iocoherency(void)
@@ -130,6 +151,8 @@
mips_cm_probe();
mips_cpc_probe();
register_cps_smp_ops();
+
+ pr_info("SoC Type: %s\n", get_system_type());
}
void __init prom_free_prom_memory(void)
diff --git a/drivers/clk/pistachio/clk-pistachio.c b/drivers/clk/pistachio/clk-pistachio.c
index fd64bd9..7271c4e 100644
--- a/drivers/clk/pistachio/clk-pistachio.c
+++ b/drivers/clk/pistachio/clk-pistachio.c
@@ -44,7 +44,7 @@
GATE(CLK_AUX_ADC_INTERNAL, "aux_adc_internal", "sys_internal_div",
0x104, 22),
GATE(CLK_AUX_ADC, "aux_adc", "aux_adc_div", 0x104, 23),
- GATE(CLK_SD_HOST, "sd_host", "sd_host_div", 0x104, 24),
+ GATE(CLK_SD_HOST, "sd_host", "sd_host_div4", 0x104, 24),
GATE(CLK_BT, "bt", "bt_div", 0x104, 25),
GATE(CLK_BT_DIV4, "bt_div4", "bt_div4_div", 0x104, 26),
GATE(CLK_BT_DIV8, "bt_div8", "bt_div8_div", 0x104, 27),
@@ -54,6 +54,7 @@
static struct pistachio_fixed_factor pistachio_ffs[] __initdata = {
FIXED_FACTOR(CLK_WIFI_DIV4, "wifi_div4", "wifi_pll", 4),
FIXED_FACTOR(CLK_WIFI_DIV8, "wifi_div8", "wifi_pll", 8),
+ FIXED_FACTOR(CLK_SDHOST_DIV4, "sd_host_div4", "sd_host_div", 4),
};
static struct pistachio_div pistachio_divs[] __initdata = {
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 2c2719a..a2f25b8 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -538,6 +538,13 @@
To compile this driver as a module, choose M here: the module will
be called img-pdm.
+config UBOOT_BOOTCOUNT
+ tristate "U-Boot Bootcount driver"
+ depends on OF
+ help
+ The U-Boot Bootcount driver allows to access the
+ bootcounter through sysfs file.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index f6e02a3..4b26b2e 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -47,6 +47,7 @@
obj-y += lis3lv02d/
obj-y += carma/
obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
+obj-$(CONFIG_UBOOT_BOOTCOUNT) += uboot_bootcount.o
obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/
obj-$(CONFIG_INTEL_MEI) += mei/
obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/
diff --git a/drivers/misc/atu/atu_clk_maintainer.c b/drivers/misc/atu/atu_clk_maintainer.c
index 2589fff..297dff8 100644
--- a/drivers/misc/atu/atu_clk_maintainer.c
+++ b/drivers/misc/atu/atu_clk_maintainer.c
@@ -639,7 +639,6 @@
int dir;
freq = txc->freq;
- freq = (freq * NSEC_PER_USEC) >> 16;
if (freq < 0) {
dir = -1;
diff --git a/drivers/misc/uboot_bootcount.c b/drivers/misc/uboot_bootcount.c
new file mode 100644
index 0000000..684993a
--- /dev/null
+++ b/drivers/misc/uboot_bootcount.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2015 Imagination Technologies Ltd.
+ *
+ * This driver gives access(read/write) to the bootcounter used by u-boot.
+ * Access is supported via sysfs.
+ *
+ * Based on work from: Steffen Rumler <Steffen.Rumler@siemens.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+*/
+
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+
+#define UBOOT_BOOTCOUNT_MAGIC 0xB001C041 /* magic number value */
+#define UBOOT_BOOTCOUNT_MAGIC_MASK 0xFFFF0000 /* magic, when combined */
+#define UBOOT_BOOTCOUNT_COUNT_MASK 0x0000FFFF /* value, when combined */
+
+
+struct bootcount_device {
+ void __iomem *reg;
+};
+
+static int bootcount_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ unsigned long bootcount;
+ struct bootcount_device *priv = dev_get_drvdata(dev);
+
+ bootcount = readl(priv->reg);
+ if ((bootcount & UBOOT_BOOTCOUNT_MAGIC_MASK) !=
+ (UBOOT_BOOTCOUNT_MAGIC & UBOOT_BOOTCOUNT_MAGIC_MASK)) {
+ return -EINVAL;
+ }
+ bootcount &= UBOOT_BOOTCOUNT_COUNT_MASK;
+ return sprintf(buf, "%lu\n", bootcount);
+}
+
+static int bootcount_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ int ret;
+ u32 value;
+ struct bootcount_device *priv = dev_get_drvdata(dev);
+
+ ret = kstrtou32(buf, 0, &value);
+ if (ret < 0)
+ return ret;
+
+ value = (UBOOT_BOOTCOUNT_MAGIC & UBOOT_BOOTCOUNT_MAGIC_MASK) |
+ (value & UBOOT_BOOTCOUNT_COUNT_MASK);
+ writel(value, priv->reg);
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(bootcount);
+
+static int bootcount_probe(struct platform_device *ofdev)
+{
+ unsigned long magic;
+ struct bootcount_device *priv;
+ struct resource *res;
+ int status;
+
+ priv = devm_kzalloc(&ofdev->dev, sizeof(struct bootcount_device), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&ofdev->dev, "Unable to allocate device private data\n");
+ return -ENOMEM;
+ }
+
+ res = platform_get_resource(ofdev, IORESOURCE_MEM, 0);
+ priv->reg = devm_ioremap_resource(&ofdev->dev, res);
+ if (IS_ERR(priv->reg)) {
+ dev_err(&ofdev->dev, "unable to map register\n");
+ return PTR_ERR(priv->reg);
+ }
+
+ magic = readl(priv->reg);
+ if ((magic & UBOOT_BOOTCOUNT_MAGIC_MASK) !=
+ (UBOOT_BOOTCOUNT_MAGIC & UBOOT_BOOTCOUNT_MAGIC_MASK)) {
+ dev_err(&ofdev->dev, "bad magic\n");
+ return -EINVAL;
+ }
+
+ status = device_create_file(&ofdev->dev, &dev_attr_bootcount);
+ if (status) {
+ dev_err(&ofdev->dev, "unable to register sysfs entry\n");
+ return status;
+ }
+ dev_set_drvdata(&ofdev->dev, priv);
+ return 0;
+}
+
+static int bootcount_remove(struct platform_device *ofdev)
+{
+ device_remove_file(&ofdev->dev, &dev_attr_bootcount);
+ return 0;
+}
+
+static const struct of_device_id bootcount_match[] = {
+ { .compatible = "uboot,bootcount", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, bootcount_match);
+
+static struct platform_driver bootcount_driver = {
+ .driver = {
+ .name = "bootcount",
+ .of_match_table = bootcount_match,
+ },
+ .probe = bootcount_probe,
+ .remove = bootcount_remove,
+};
+
+module_platform_driver(bootcount_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Avinash Tahakik <avinash.tahakik@imgtec.com>");
+MODULE_DESCRIPTION("Provide (read/write) access to the U-Boot bootcounter via sysfs");
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 3af137f..9000241 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -48,13 +48,15 @@
return ret;
}
-static void m25p_addr2cmd(struct spi_nor *nor, unsigned int addr, u8 *cmd)
+static void m25p_addr2cmd(unsigned int addr, unsigned int addr_width, u8 *cmd)
{
- /* opcode is in cmd[0] */
- cmd[1] = addr >> (nor->addr_width * 8 - 8);
- cmd[2] = addr >> (nor->addr_width * 8 - 16);
- cmd[3] = addr >> (nor->addr_width * 8 - 24);
- cmd[4] = addr >> (nor->addr_width * 8 - 32);
+ if (addr_width) {
+ /* opcode is in cmd[0] */
+ cmd[1] = addr >> (addr_width * 8 - 8);
+ cmd[2] = addr >> (addr_width * 8 - 16);
+ cmd[3] = addr >> (addr_width * 8 - 24);
+ cmd[4] = addr >> (addr_width * 8 - 32);
+ }
}
static int m25p_cmdsz(struct spi_nor *nor)
@@ -90,7 +92,7 @@
cmd_sz = 1;
flash->command[0] = nor->program_opcode;
- m25p_addr2cmd(nor, to, flash->command);
+ m25p_addr2cmd(to, nor->addr_width, flash->command);
t[0].tx_buf = flash->command;
t[0].len = cmd_sz;
@@ -105,9 +107,9 @@
*retlen += m.actual_length - cmd_sz;
}
-static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor)
+static inline unsigned int m25p80_rx_nbits(enum read_mode mode)
{
- switch (nor->flash_read) {
+ switch (mode) {
case SPI_NOR_DUAL:
return 2;
case SPI_NOR_QUAD:
@@ -137,14 +139,14 @@
memset(t, 0, (sizeof t));
flash->command[0] = nor->read_opcode;
- m25p_addr2cmd(nor, from, flash->command);
+ m25p_addr2cmd(from, nor->addr_width, flash->command);
t[0].tx_buf = flash->command;
t[0].len = m25p_cmdsz(nor) + dummy;
spi_message_add_tail(&t[0], &m);
t[1].rx_buf = buf;
- t[1].rx_nbits = m25p80_rx_nbits(nor);
+ t[1].rx_nbits = m25p80_rx_nbits(nor->flash_read);
t[1].len = len;
spi_message_add_tail(&t[1], &m);
@@ -163,13 +165,97 @@
/* Set up command buffer. */
flash->command[0] = nor->erase_opcode;
- m25p_addr2cmd(nor, offset, flash->command);
+ m25p_addr2cmd(offset, nor->addr_width, flash->command);
spi_write(flash->spi, flash->command, m25p_cmdsz(nor));
return 0;
}
+/* From spi_nor_xfer_cfg, this call ignores cmd_pins, addr_pins, so single I/O
+ * line is used for cmd and addr
+ * mode_pins, mode_cycles are ignored, decides nbits based on mode
+ */
+static int m25p80_read_xfer(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg,
+ u8 *buf, size_t len, size_t *retlen)
+{
+ struct m25p *flash = nor->priv;
+ struct spi_device *spi = flash->spi;
+ struct spi_transfer t[2];
+ struct spi_message m;
+ u32 dummy = cfg->dummy_cycles/8; /* convert dummy cycles into bytes */
+ u32 cmd_sz = (1 + cfg->addr_width + dummy);
+ int ret;
+
+ if (cfg->addr_width > 4 || cmd_sz > MAX_CMD_SIZE)
+ return -EINVAL;
+
+ spi_message_init(&m);
+ memset(t, 0, sizeof (t));
+
+ memset(flash->command, 0, MAX_CMD_SIZE);
+ flash->command[0] = cfg->cmd;
+ m25p_addr2cmd(cfg->addr, cfg->addr_width, flash->command);
+
+ t[0].tx_buf = flash->command;
+ t[0].len = cmd_sz;
+ spi_message_add_tail(&t[0], &m);
+
+ if (len) {
+ t[1].rx_buf = buf;
+ t[1].rx_nbits = m25p80_rx_nbits(cfg->mode);
+ t[1].len = len;
+ spi_message_add_tail(&t[1], &m);
+ }
+
+ ret = spi_sync(spi, &m);
+
+ if (!ret)
+ *retlen += (m.actual_length - cmd_sz);
+ return ret;
+}
+
+/* From spi_nor_xfer_cfg, this call ignores cmd_pins, addr_pins, mode, mode_pins,
+ * mode_cycles, so single I/O line is used for cmd, addr and data
+ */
+static int m25p80_write_xfer(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg,
+ u8 *buf, size_t len, size_t *retlen)
+{
+ struct m25p *flash = nor->priv;
+ struct spi_device *spi = flash->spi;
+ struct spi_transfer t[2];
+ struct spi_message m;
+ u32 dummy = cfg->dummy_cycles/8; /* convert dummy cycles into bytes */
+ u32 cmd_sz = (1 + cfg->addr_width + dummy);
+ int ret;
+
+ if (cfg->addr_width > 4 || cmd_sz > MAX_CMD_SIZE)
+ return -EINVAL;
+
+ spi_message_init(&m);
+ memset(t, 0, sizeof(t));
+
+ memset(flash->command, 0, MAX_CMD_SIZE);
+ flash->command[0] = cfg->cmd;
+ m25p_addr2cmd(cfg->addr, cfg->addr_width, flash->command);
+
+ t[0].tx_buf = flash->command;
+ t[0].len = cmd_sz;
+ spi_message_add_tail(&t[0], &m);
+
+ if (len) {
+ t[1].tx_buf = buf;
+ t[1].len = len;
+ spi_message_add_tail(&t[1], &m);
+ }
+
+ ret = spi_sync(spi, &m);
+
+ if (!ret)
+ *retlen += (m.actual_length - cmd_sz);
+ return ret;
+}
+
/*
* board specific setup should have ensured the SPI clock used here
* matches what the READ command supports, at least until this driver
@@ -199,6 +285,8 @@
nor->erase = m25p80_erase;
nor->write_reg = m25p80_write_reg;
nor->read_reg = m25p80_read_reg;
+ nor->read_xfer = m25p80_read_xfer;
+ nor->write_xfer = m25p80_write_xfer;
nor->dev = &spi->dev;
nor->mtd = &flash->mtd;
diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig
index 64a4f0e..e157c33 100644
--- a/drivers/mtd/spi-nor/Kconfig
+++ b/drivers/mtd/spi-nor/Kconfig
@@ -28,4 +28,12 @@
This enables support for the Quad SPI controller in master mode.
We only connect the NOR to this controller now.
+config MTD_SPI_NOR_WINBOND_OTP
+ bool "Support for winbond security register and unique ID"
+ depends on MTD_SPI_NOR
+ default n
+ help
+ This enables support for read/write of winbond security registers
+ as user OTP and also reading of NOR unique ID as factory OTP.
+
endif # MTD_SPI_NOR
diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile
index 6a7ce14..6aba941 100644
--- a/drivers/mtd/spi-nor/Makefile
+++ b/drivers/mtd/spi-nor/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o
+obj-$(CONFIG_MTD_SPI_NOR_WINBOND_OTP) += winbond-otp.o
obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o
diff --git a/drivers/mtd/spi-nor/spi-nor-common.h b/drivers/mtd/spi-nor/spi-nor-common.h
new file mode 100644
index 0000000..ff15d42
--- /dev/null
+++ b/drivers/mtd/spi-nor/spi-nor-common.h
@@ -0,0 +1,8 @@
+#ifndef SPI_NOR_COMMON_H
+#define SPI_NOR_COMMON_H
+
+int spi_nor_wait_till_ready(struct spi_nor *nor);
+int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops);
+void spi_nor_unlock_and_unprep(struct spi_nor *nor, enum spi_nor_ops ops);
+
+#endif
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 1194f33..46d2f06 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -22,6 +22,8 @@
#include <linux/of_platform.h>
#include <linux/spi/flash.h>
#include <linux/mtd/spi-nor.h>
+#include "winbond-otp.h"
+#include "spi-nor-common.h"
/* Define max times to check status register before we give up. */
#define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */
@@ -55,6 +57,7 @@
#define SPI_NOR_DUAL_READ 0x20 /* Flash supports Dual Read */
#define SPI_NOR_QUAD_READ 0x40 /* Flash supports Quad Read */
#define USE_FSR 0x80 /* use flag status register */
+#define WINBOND_OTP 0x100 /* use winbond security reg as OTP */
};
#define JEDEC_MFR(info) ((info)->id[0])
@@ -231,7 +234,7 @@
* Service routine to read status register until ready, or timeout occurs.
* Returns non-zero if error.
*/
-static int spi_nor_wait_till_ready(struct spi_nor *nor)
+int spi_nor_wait_till_ready(struct spi_nor *nor)
{
unsigned long deadline;
int timeout = 0, ret;
@@ -268,7 +271,7 @@
return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0, 0);
}
-static int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops)
+int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops)
{
int ret = 0;
@@ -285,7 +288,7 @@
return ret;
}
-static void spi_nor_unlock_and_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
+void spi_nor_unlock_and_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
{
if (nor->unprepare)
nor->unprepare(nor, ops);
@@ -611,7 +614,7 @@
{ "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) },
{ "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) },
{ "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) },
- { "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) },
+ { "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K | WINBOND_OTP) },
{ "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
{ "s25fl132k", INFO(0x014016, 0, 64 * 1024, 64, 0) },
@@ -1078,6 +1081,9 @@
mtd->_unlock = spi_nor_unlock;
}
+ if (info->flags & WINBOND_OTP)
+ winbond_otp_register(mtd);
+
/* sst nor chips use AAI word program */
if (info->flags & SST_WRITE)
mtd->_write = sst_write;
diff --git a/drivers/mtd/spi-nor/winbond-otp.c b/drivers/mtd/spi-nor/winbond-otp.c
new file mode 100644
index 0000000..a9db770
--- /dev/null
+++ b/drivers/mtd/spi-nor/winbond-otp.c
@@ -0,0 +1,482 @@
+/*
+ * Imagination Technologies
+ *
+ * Copyright (c) 2015 Imagination Technologies Ltd.
+ *
+ * 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 driver provides read/write access to the 3 x 256 bytes security
+ * registers as user OTP and unique ID of the NOR can be read as factory OTP
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/spi-nor.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include "spi-nor-common.h"
+
+#define SECURITY_REG_START_ADDR 0x1000 /*first security register addr*/
+#define SECURITY_REG_ADDR_OFFSET 0x1000 /*diff between consecutive reg*/
+#define SECURITY_REG_NUM 3 /* number of security registers */
+#define SECURITY_REG_SIZE 256 /* bytes per security register */
+#define SECURITY_REG_TOTAL_SIZE (SECURITY_REG_NUM * SECURITY_REG_SIZE)
+#define SPI_NOR_UNIQUE_ID_LEN 8 /*number of bytes of unique ID */
+
+/* SPI FLASH opcodes */
+#define SPINOR_OP_RD_SR2 0x35 /* Read status register 2 */
+#define SPINOR_OP_PR_SECURITY_REG 0x42 /* Program security register */
+#define SPINOR_OP_ER_SECURITY_REG 0x44 /* Erase security register */
+#define SPINOR_OP_RD_SECURITY_REG 0x48 /* Read security register */
+#define SPINOR_OP_RD_UNIQUE_ID 0x4B /* Read unique id */
+
+/* Status register 2 */
+#define SR2_LB1_BIT 3 /* security register lock bit 1 */
+
+/* Get start addr of the security reg*/
+#define SEC_REG_START_ADDR(addr) (addr & 0x3000)
+
+static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
+{
+ return mtd->priv;
+}
+
+static inline int write_enable(struct spi_nor *nor)
+{
+ return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0);
+}
+
+static inline int write_disable(struct spi_nor *nor)
+{
+ return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0, 0);
+}
+
+static int read_sr(struct spi_nor *nor, u8 opcode, u8 *val)
+{
+ int ret;
+
+ ret = nor->read_reg(nor, opcode, val, 1);
+ if (ret < 0)
+ pr_err("error %d reading SR\n", ret);
+ return ret;
+}
+
+/*
+ * Converts address range
+ * 0 - 0xFF -> 0x1000 - 0x10FF
+ * 0x100 - 0x1FF -> 0x2000 - 0x20FF
+ * 0x200 - 0x2FF -> 0x3000 - 0x30FF
+ *
+ * This func assumes that sanity checks on addr are done and is in valid range
+ */
+static loff_t translate_addr(loff_t addr)
+{
+ int i;
+ loff_t new_addr = SECURITY_REG_START_ADDR;
+
+ for (i = 0; i < SECURITY_REG_NUM; i++) {
+ if (addr < ((i+1)*SECURITY_REG_SIZE)) {
+ new_addr |= addr & (SECURITY_REG_SIZE-1);
+ break;
+ }
+ new_addr += SECURITY_REG_ADDR_OFFSET;
+ }
+
+ return new_addr;
+}
+
+/*
+ * Return 3 blocks of 256 bytes security register as user OTP,
+ * address of these blocks will be 0, 0x100, 0x200
+ * driver will convert these address to actual address while doing
+ * read/write
+ */
+static int winbond_get_user_otp_info(struct mtd_info *mtd, size_t len,
+ size_t *retlen,
+ struct otp_info *otpinfo)
+{
+ u8 val;
+ int i, ret;
+ struct spi_nor *nor = mtd_to_spi_nor(mtd);
+
+ mutex_lock(&nor->lock);
+ ret = read_sr(nor, SPINOR_OP_RD_SR2, &val);
+ mutex_unlock(&nor->lock);
+
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < SECURITY_REG_NUM; i++) {
+ otpinfo[i].start = i * SECURITY_REG_SIZE;
+ otpinfo[i].length = SECURITY_REG_SIZE;
+ otpinfo[i].locked = !!(val & BIT(SR2_LB1_BIT + i));
+ }
+
+ *retlen = SECURITY_REG_NUM * sizeof(*otpinfo);
+
+ return 0;
+}
+
+static int spi_otp_read(struct spi_nor *nor, loff_t from,
+ size_t len, size_t *retlen, u_char *buf)
+{
+ struct spi_nor_xfer_cfg cfg = {
+ .cmd = SPINOR_OP_RD_SECURITY_REG,
+ .addr = from,
+ .addr_width = nor->addr_width,
+ .mode = SPI_NOR_NORMAL,
+ .dummy_cycles = 8,
+ };
+
+ return nor->read_xfer(nor, &cfg, buf, len, retlen);
+}
+
+static int spi_otp_write(struct spi_nor *nor, loff_t to,
+ size_t len, size_t *retlen, u_char *buf)
+{
+ struct spi_nor_xfer_cfg cfg = {
+ .cmd = SPINOR_OP_PR_SECURITY_REG,
+ .addr = to,
+ .addr_width = nor->addr_width,
+ .mode = SPI_NOR_NORMAL,
+ };
+
+ return nor->write_xfer(nor, &cfg, buf, len, retlen);
+}
+
+static int spi_otp_erase(struct spi_nor *nor, loff_t offs)
+{
+ size_t temp_retlen;
+ struct spi_nor_xfer_cfg cfg = {
+ .cmd = SPINOR_OP_ER_SECURITY_REG,
+ .addr = offs,
+ .addr_width = nor->addr_width,
+ .mode = SPI_NOR_NORMAL,
+ };
+
+ return nor->write_xfer(nor, &cfg, NULL, 0, &temp_retlen);
+}
+
+static int spi_read_uniqueid(struct spi_nor *nor, u8 *buf)
+{
+ size_t temp_retlen;
+ struct spi_nor_xfer_cfg cfg = {
+ .cmd = SPINOR_OP_RD_UNIQUE_ID,
+ .addr_width = 0,
+ .mode = SPI_NOR_NORMAL,
+ .dummy_cycles = 32,
+ };
+
+ return nor->read_xfer(nor, &cfg, buf, SPI_NOR_UNIQUE_ID_LEN,
+ &temp_retlen);
+}
+
+
+static int winbond_read_user_otp(struct mtd_info *mtd, loff_t from,
+ size_t len, size_t *retlen, u_char *buf)
+{
+ int ret;
+ u32 i, read_len, end_addr, sreg_offset;
+ loff_t temp_addr;
+ struct spi_nor *nor = mtd_to_spi_nor(mtd);
+
+ *retlen = 0;
+
+ if (from < 0 || from >= SECURITY_REG_TOTAL_SIZE
+ || (from + len) > SECURITY_REG_TOTAL_SIZE)
+ return -EINVAL;
+
+ if (!len)
+ return 0;
+
+ end_addr = from + len;
+
+ ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_READ);
+ if (ret)
+ return ret;
+
+ for (i = from; i < end_addr; i += read_len) {
+ sreg_offset = i & (SECURITY_REG_SIZE-1);
+ /* if offset not on boundary, read first few bytes */
+ if (sreg_offset) {
+ /* check if everything has to be read from 1 reg */
+ if ((sreg_offset + len) <= SECURITY_REG_SIZE)
+ read_len = len;
+ else
+ read_len = SECURITY_REG_SIZE - sreg_offset;
+ }
+ /* if it is last chunk, read the remaining bytes */
+ else if ((end_addr - i) < SECURITY_REG_SIZE)
+ read_len = end_addr - i;
+ else
+ read_len = SECURITY_REG_SIZE;
+
+ temp_addr = translate_addr(i);
+ ret = spi_otp_read(nor, temp_addr, read_len, retlen,
+ buf + (i-from));
+ if (ret < 0)
+ goto error;
+ }
+error:
+ spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_READ);
+ return ret;
+}
+
+/*
+ * This func assumes that offset is within valid range of security registers,
+ * valid offset are 0x1000, 0x2000 or 0x3000
+ */
+static int winbond_erase_security_reg(struct spi_nor *nor, loff_t offset)
+{
+ int ret;
+
+ ret = write_enable(nor);
+ if (ret < 0)
+ return ret;
+
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ return ret;
+
+ ret = spi_otp_erase(nor, offset);
+ if (ret < 0)
+ return ret;
+
+ ret = spi_nor_wait_till_ready(nor);
+
+ return ret;
+}
+
+/*
+ * This function does read, modify locally, erase and write to the register to
+ * be written
+ * It doesn't do any range checks on reg_addr, sreg_offset, len
+ */
+static int winbond_write_security_reg(struct spi_nor *nor, loff_t reg_addr,
+ u32 sreg_offset, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ int ret;
+ size_t temp_retlen = 0;
+ u8 *reg_buffer;
+
+ if (unlikely(sreg_offset + len > SECURITY_REG_SIZE))
+ return -EINVAL;
+
+ reg_buffer = kmalloc(SECURITY_REG_SIZE, GFP_KERNEL);
+ if (!reg_buffer)
+ return -ENOMEM;
+
+ /* read the security register */
+ ret = spi_otp_read(nor, reg_addr, SECURITY_REG_SIZE, &temp_retlen,
+ reg_buffer);
+ if (ret < 0 || temp_retlen != SECURITY_REG_SIZE)
+ goto error;
+
+ /* modify the part to be written */
+ memcpy(reg_buffer + sreg_offset, buf, len);
+
+ /* erase the security register */
+ ret = winbond_erase_security_reg(nor, reg_addr);
+ if (ret < 0)
+ goto error;
+
+ /* write the security reg*/
+ ret = write_enable(nor);
+ if (ret < 0)
+ goto error;
+
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ goto error;
+
+ temp_retlen = 0;
+
+ ret = spi_otp_write(nor, reg_addr, SECURITY_REG_SIZE, &temp_retlen,
+ reg_buffer);
+ if (ret < 0 || temp_retlen != SECURITY_REG_SIZE)
+ goto error;
+
+ ret = spi_nor_wait_till_ready(nor);
+
+ *retlen += len;
+
+error:
+ kfree(reg_buffer);
+ return ret;
+}
+
+static int winbond_write_user_otp(struct mtd_info *mtd, loff_t to,
+ size_t len, size_t *retlen, u_char *buf)
+{
+ int ret;
+ u32 i, write_len, end_addr, sreg_offset;
+ loff_t temp_addr;
+ struct spi_nor *nor = mtd_to_spi_nor(mtd);
+
+ *retlen = 0;
+
+ if (to < 0 || to >= SECURITY_REG_TOTAL_SIZE
+ || (to + len) > SECURITY_REG_TOTAL_SIZE)
+ return -EINVAL;
+
+ if (!len)
+ return 0;
+
+ end_addr = to + len;
+
+ ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_WRITE);
+ if (ret)
+ return ret;
+
+ for (i = to; i < end_addr; i += write_len) {
+ sreg_offset = i & (SECURITY_REG_SIZE-1);
+ /* if offset not on boundary, write first few bytes */
+ if (sreg_offset) {
+ /* check if everything has to be written in 1 reg */
+ if ((sreg_offset + len) <= SECURITY_REG_SIZE)
+ write_len = len;
+ else
+ write_len = SECURITY_REG_SIZE - sreg_offset;
+ }
+ /* if it is last chunk, write the remaining bytes */
+ else if ((end_addr - i) < SECURITY_REG_SIZE)
+ write_len = end_addr - i;
+ else
+ write_len = SECURITY_REG_SIZE;
+
+ temp_addr = translate_addr(i);
+ ret = winbond_write_security_reg(nor,
+ SEC_REG_START_ADDR(temp_addr),
+ sreg_offset, write_len,
+ retlen, buf + (i-to));
+ if (ret < 0)
+ goto error;
+ }
+
+error:
+ spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);
+ return ret;
+}
+
+static int winbond_lock_user_otp(struct mtd_info *mtd, loff_t from, size_t len)
+{
+ int ret;
+ u8 sr1, sr2, security_reg_num;
+ struct spi_nor *nor = mtd_to_spi_nor(mtd);
+
+ /* allow locking 1 register at a time,
+ * so ensure that len is 256
+ * also check if address is on security register boundary
+ */
+ if (len != SECURITY_REG_SIZE || from < 0
+ || from >= SECURITY_REG_TOTAL_SIZE
+ || from & (SECURITY_REG_SIZE - 1))
+ return -EINVAL;
+
+ /* find out the security reg to set */
+ security_reg_num = from / SECURITY_REG_SIZE;
+
+ if (unlikely(security_reg_num > (SECURITY_REG_NUM-1)))
+ return -EINVAL;
+
+ ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_LOCK);
+ if (ret)
+ return ret;
+
+ /* read status registers */
+ ret = read_sr(nor, SPINOR_OP_RDSR, &sr1);
+ if (ret < 0)
+ goto error;
+
+ ret = read_sr(nor, SPINOR_OP_RD_SR2, &sr2);
+ if (ret < 0)
+ goto error;
+
+ ret = write_enable(nor);
+ if (ret < 0)
+ goto error;
+
+ /* set the corresponding LB bit in security register 2 */
+ sr2 |= BIT(SR2_LB1_BIT + security_reg_num);
+
+ /* write status registers */
+ nor->cmd_buf[0] = sr1;
+ nor->cmd_buf[1] = sr2;
+ ret = nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 2, 0);
+
+ write_disable(nor);
+
+error:
+ spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK);
+ return ret;
+}
+
+/*
+ * Unique ID of NOR device will be reported as factory OTP
+ */
+static int winbond_get_fact_otp_info(struct mtd_info *mtd, size_t len,
+ size_t *retlen,
+ struct otp_info *otpinfo)
+{
+ otpinfo->start = 0;
+ otpinfo->length = SPI_NOR_UNIQUE_ID_LEN;
+ otpinfo->locked = 1;
+
+ *retlen = sizeof(*otpinfo);
+
+ return 0;
+}
+
+static int winbond_read_fact_otp(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ int ret;
+
+ char unique_id[SPI_NOR_UNIQUE_ID_LEN] = {0};
+ struct spi_nor *nor = mtd_to_spi_nor(mtd);
+
+ *retlen = 0;
+
+ if (from < 0 || from >= SPI_NOR_UNIQUE_ID_LEN
+ || (from + len) > SPI_NOR_UNIQUE_ID_LEN)
+ return -EINVAL;
+
+ if (!len)
+ return 0;
+
+ ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_READ);
+ if (ret)
+ return ret;
+
+ ret = spi_read_uniqueid(nor, unique_id);
+ if (ret < 0)
+ goto error;
+
+ /* Read complete unique ID,but just copy whatever is requested */
+ memcpy(buf, unique_id + from, len);
+ *retlen = len;
+error:
+ spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_READ);
+ return ret;
+}
+
+void winbond_otp_register(struct mtd_info *mtd)
+{
+ struct spi_nor *nor = mtd_to_spi_nor(mtd);
+
+ if (nor->read_xfer && nor->write_xfer) {
+ mtd->_get_user_prot_info = winbond_get_user_otp_info;
+ mtd->_read_user_prot_reg = winbond_read_user_otp;
+ mtd->_write_user_prot_reg = winbond_write_user_otp;
+ mtd->_lock_user_prot_reg = winbond_lock_user_otp;
+ mtd->_get_fact_prot_info = winbond_get_fact_otp_info;
+ mtd->_read_fact_prot_reg = winbond_read_fact_otp;
+ } else
+ dev_err(nor->dev, "Required nor interfaces "
+ "(read_xfer, write_xfer) not defined\n");
+}
diff --git a/drivers/mtd/spi-nor/winbond-otp.h b/drivers/mtd/spi-nor/winbond-otp.h
new file mode 100644
index 0000000..29cbbcc
--- /dev/null
+++ b/drivers/mtd/spi-nor/winbond-otp.h
@@ -0,0 +1,20 @@
+/*
+ * Imagination Technologies
+ *
+ * Copyright (c) 2015 Imagination Technologies Ltd.
+ *
+ * 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.
+ */
+
+#ifndef WINBOND_OTP_H
+#define WINBOND_OTP_H
+
+#ifdef CONFIG_MTD_SPI_NOR_WINBOND_OTP
+void winbond_otp_register(struct mtd_info *mtd);
+#else
+static inline void winbond_otp_register(struct mtd_info *mtd) { return; }
+#endif
+
+#endif
diff --git a/drivers/net/ieee802154/cc2520.c b/drivers/net/ieee802154/cc2520.c
index d25eb39..2759fb3 100644
--- a/drivers/net/ieee802154/cc2520.c
+++ b/drivers/net/ieee802154/cc2520.c
@@ -21,6 +21,7 @@
#include <linux/skbuff.h>
#include <linux/of_gpio.h>
#include <linux/ieee802154.h>
+#include <linux/clk-provider.h>
#include <net/mac802154.h>
#include <net/cfg802154.h>
@@ -212,6 +213,7 @@
bool disable_tx; /* don't send any packets */
bool disable_rx; /* disable rx */
bool started; /* Flag to know if device is up */
+ struct clk *clk; /* external clock */
};
/* Generic Functions */
@@ -789,11 +791,45 @@
* default to 1MHz(reset value)
*/
pdata->extclockfreq = CC2520_EXTCLOCK_DEFAULT_FREQ;
- }
+ } else
+ pdata->registerclk = true;
return 0;
}
+static int cc2520_register_clk(struct spi_device *spi,
+ struct cc2520_platform_data *pdata)
+{
+ struct device_node *np = spi->dev.of_node;
+ struct cc2520_private *priv = spi_get_drvdata(spi);
+ int ret = 0;
+
+ if (pdata->registerclk) {
+ if (np) {
+ priv->clk = clk_register_fixed_rate(&spi->dev, np->name,
+ NULL, CLK_IS_ROOT, pdata->extclockfreq);
+
+ if (!IS_ERR(priv->clk)) {
+ ret = of_clk_add_provider(np,
+ of_clk_src_simple_get,
+ priv->clk);
+ if (ret) {
+ clk_unregister(priv->clk);
+ dev_err(&spi->dev,
+ "Failed to add clk provider\n");
+ }
+ } else {
+ dev_err(&spi->dev, "Failed to register clk\n");
+ ret = PTR_ERR(priv->clk);
+ }
+ } else
+ dev_err(&spi->dev, "No device node found, ext-clk won't"
+ " be registered\n");
+ }
+
+ return ret;
+}
+
static int cc2520_hw_init(struct cc2520_private *priv)
{
u8 status = 0, state = 0xff;
@@ -1153,10 +1189,21 @@
ret = sysfs_create_group(&spi->dev.kobj, &dev_attr_group);
if (ret)
- goto err_hw_init;
+ goto err_free_device;
+
+ ret = cc2520_register_clk(spi, &pdata);
+ if (ret)
+ goto err_free_sysfs;
return 0;
+err_free_sysfs:
+ sysfs_remove_group(&spi->dev.kobj, &dev_attr_group);
+
+err_free_device:
+ ieee802154_unregister_hw(priv->hw);
+ ieee802154_free_hw(priv->hw);
+
err_hw_init:
mutex_destroy(&priv->buffer_mutex);
flush_work(&priv->fifop_irqwork);
@@ -1167,6 +1214,11 @@
{
struct cc2520_private *priv = spi_get_drvdata(spi);
+ if (priv->clk) {
+ of_clk_del_provider(spi->dev.of_node);
+ clk_unregister(priv->clk);
+ }
+
sysfs_remove_group(&spi->dev.kobj, &dev_attr_group);
mutex_destroy(&priv->buffer_mutex);
flush_work(&priv->fifop_irqwork);
diff --git a/drivers/net/wireless/uccp420wlan/inc/core.h b/drivers/net/wireless/uccp420wlan/inc/core.h
index abb9fa3..1adf5cf 100644
--- a/drivers/net/wireless/uccp420wlan/inc/core.h
+++ b/drivers/net/wireless/uccp420wlan/inc/core.h
@@ -88,7 +88,8 @@
#define TX_COMPLETE_TIMEOUT_TICKS msecs_to_jiffies(TX_COMPLETE_TIMEOUT)
#define SCAN_ABORT_TIMEOUT 1000
#define SCAN_ABORT_TIMEOUT_TICKS msecs_to_jiffies(SCAN_ABORT_TIMEOUT)
-
+#define CANCEL_HW_ROC_TIMEOUT 1000
+#define CANCEL_HW_ROC_TIMEOUT_TICKS msecs_to_jiffies(CANCEL_HW_ROC_TIMEOUT)
#define DEFAULT_TX_ANT_SELECT 3 /* bitmap of antennas for tx, 3=> both first and
* second antenna to be used
@@ -202,6 +203,8 @@
int payload_length;
int start_prod_mode;
int init_prod;
+ unsigned char bypass_vpd;
+ unsigned int cont_tx;
};
struct cmd_send_recv_cnt {
@@ -334,6 +337,10 @@
unsigned int cts_received_mcp_cnt;
/*MAC Stats*/
+ unsigned int roc_start;
+ unsigned int roc_stop;
+ unsigned int roc_complete;
+ unsigned int roc_stop_complete;
/* TX related */
unsigned int tx_cmd_cnt; /* Num of TX commands received from host */
unsigned int tx_done_cnt; /* Num of Tx done events sent to host */
@@ -388,10 +395,12 @@
struct sk_buff_head pkt;
unsigned int hdr_len;
unsigned int queue;
+ unsigned int vif_index;
unsigned int rate[4];
unsigned int retries[4];
unsigned int curr_retries;
unsigned int max_retries;
+ int roc_peer_id;
bool adjusted_rates;
};
@@ -410,11 +419,13 @@
unsigned int next_spare_token_ac;
/* Used to store the address of pending skbs per ac */
- struct sk_buff_head pending_pkt[MAX_PEND_Q_PER_AC][NUM_ACS];
+ struct sk_buff_head pending_pkt[MAX_UMAC_VIF_CHANCTX_TYPES]
+ [MAX_PEND_Q_PER_AC]
+ [NUM_ACS];
#ifdef MULTI_CHAN_SUPPORT
/* Peer which has the opportunity to xmit next on a queue */
- unsigned int curr_peer_opp[MAX_CHANCTX][NUM_ACS];
+ unsigned int curr_peer_opp[MAX_CHANCTX + MAX_OFF_CHANCTX][NUM_ACS];
#else
unsigned int curr_peer_opp[NUM_ACS];
#endif
@@ -423,8 +434,9 @@
* it will be used in tx complete.
*/
#ifdef MULTI_CHAN_SUPPORT
- unsigned char desc_chan_map[NUM_TX_DESCS];
- struct tx_pkt_info pkt_info[MAX_CHANCTX][NUM_TX_DESCS];
+ int desc_chan_map[NUM_TX_DESCS];
+ struct tx_pkt_info pkt_info[MAX_CHANCTX + MAX_OFF_CHANCTX]
+ [NUM_TX_DESCS];
#else
struct tx_pkt_info pkt_info[NUM_TX_DESCS];
#endif
@@ -462,17 +474,17 @@
#endif
struct current_channel {
+ unsigned int pri_chnl_num;
unsigned int center_freq1;
unsigned int center_freq2;
unsigned int freq_band;
unsigned int ch_width;
- unsigned int pri_chnl_num;
};
struct roc_params {
unsigned char roc_in_progress;
- unsigned char roc_ps_changed;
- unsigned char roc_chan_changed;
+ unsigned int roc_type;
+ bool need_offchan;
atomic_t roc_mgmt_tx_count;
};
@@ -480,11 +492,12 @@
struct proc_dir_entry *umac_proc_dir_entry;
struct device *dev;
struct mac_address if_mac_addresses[MAX_VIFS];
+ unsigned int current_vif_count;
unsigned int active_vifs;
struct mutex mutex;
int state;
int txpower;
- unsigned char mc_filters[MCST_ADDR_LIMIT][6];
+ unsigned char mc_filters[MCST_ADDR_LIMIT][6];
int mc_filter_count;
struct tasklet_struct proc_tx_tasklet;
@@ -504,6 +517,8 @@
struct wifi_stats *stats;
char name[20];
char scan_abort_done;
+ char cancel_hw_roc_done;
+ char cancel_roc;
char chan_prog_done;
char reset_complete;
int power_save; /* Will be set only when a single VIF in
@@ -517,12 +532,15 @@
* when transmitting bcast frames in AP in IBSS
* modes
*/
+ spinlock_t roc_lock;
unsigned char tx_antenna;
unsigned char tx_last_beacon;
unsigned int rts_threshold;
#ifdef MULTI_CHAN_SUPPORT
spinlock_t chanctx_lock;
struct ieee80211_chanctx_conf *chanctx[MAX_CHANCTX];
+ struct umac_chanctx *off_chanctx[MAX_OFF_CHANCTX];
+ int roc_off_chanctx_idx;
int curr_chanctx_idx;
int num_active_chanctx;
#endif
@@ -564,6 +582,7 @@
#ifdef MULTI_CHAN_SUPPORT
struct list_head list;
struct umac_chanctx *chanctx;
+ struct umac_chanctx *off_chanctx;
#endif
};
@@ -585,15 +604,23 @@
#endif
+struct curr_peer_info {
+ int id;
+ int op_chan_idx;
+};
-extern int wait_for_scan_abort(struct mac80211_dev *dev);
-extern int wait_for_channel_prog_complete(struct mac80211_dev *dev);
-extern int uccp420wlan_prog_nw_selection(unsigned int nw_select_enabled,
- unsigned char *mac_addr);
+
#ifdef MULTI_CHAN_SUPPORT
void uccp420wlan_proc_ch_sw_event(struct umac_event_ch_switch *ch_sw_info,
void *context);
#endif
+extern int wait_for_cancel_hw_roc(struct mac80211_dev *dev);
+extern int wait_for_scan_abort(struct mac80211_dev *dev);
+extern int wait_for_channel_prog_complete(struct mac80211_dev *dev);
+extern int wait_for_tx_queue_flush_complete(struct mac80211_dev *dev,
+ unsigned int token);
+extern int uccp420wlan_prog_nw_selection(unsigned int nw_select_enabled,
+ unsigned char *mac_addr);
extern int uccp420wlan_core_init(struct mac80211_dev *dev, unsigned int ftm);
extern void uccp420wlan_core_deinit(struct mac80211_dev *dev, unsigned int ftm);
extern void uccp420wlan_vif_add(struct umac_vif *uvif);
@@ -622,30 +649,43 @@
bool retry);
extern void uccp420wlan_tx_init(struct mac80211_dev *dev);
extern void uccp420wlan_tx_deinit(struct mac80211_dev *dev);
-
+void uccp420wlan_tx_proc_send_pend_frms_all(struct mac80211_dev *dev,
+ int chan_id);
extern void proc_bss_info_changed(unsigned char *mac_addr, int value);
extern void packet_generation(unsigned long data);
extern int wait_for_reset_complete(struct mac80211_dev *dev);
-extern void uccp420wlan_tx_proc_pend_frms(struct mac80211_dev *dev,
+extern int uccp420wlan_tx_proc_pend_frms(struct mac80211_dev *dev,
int queue,
#ifdef MULTI_CHAN_SUPPORT
int curr_chanctx_idx,
#endif
- int peer_id,
int token_id);
-int get_curr_peer_opp(struct mac80211_dev *dev,
+void free_token(struct mac80211_dev *dev,
+ int token_id,
+ int queue);
+
+struct curr_peer_info get_curr_peer_opp(struct mac80211_dev *dev,
#ifdef MULTI_CHAN_SUPPORT
int curr_chanctx_idx,
#endif
int queue);
+int uccp420_flush_vif_queues(struct mac80211_dev *dev,
+ struct umac_vif *uvif,
+ int chanctx_idx,
+ unsigned int hw_queue_map,
+ enum UMAC_VIF_CHANCTX_TYPE vif_chanctx_type);
+
/* Beacon TimeStamp */
__s32 __attribute__((weak)) frc_to_atu(__u32 frccnt, __u64 *patu, s32 dir);
int __attribute__((weak)) get_evt_timer_freq(unsigned int *mask,
unsigned int *num,
unsigned int *denom);
+int tx_queue_map(int queue);
+int tx_queue_unmap(int queue);
+
extern unsigned char *rf_params_vpd;
extern int num_streams_vpd;
diff --git a/drivers/net/wireless/uccp420wlan/inc/host_umac_if.h b/drivers/net/wireless/uccp420wlan/inc/host_umac_if.h
index 5d41ad9..bb3034a 100644
--- a/drivers/net/wireless/uccp420wlan/inc/host_umac_if.h
+++ b/drivers/net/wireless/uccp420wlan/inc/host_umac_if.h
@@ -40,7 +40,9 @@
#define MAX_PEND_Q_PER_AC (MAX_PEERS + MAX_VIFS)
#ifdef MULTI_CHAN_SUPPORT
-#define MAX_CHANCTX 2
+#define MAX_CHANCTX MAX_VIFS
+#define MAX_OFF_CHANCTX MAX_VIFS
+#define OFF_CHANCTX_IDX_BASE MAX_CHANCTX
#endif
#define WEP40_KEYLEN 5
@@ -246,6 +248,10 @@
struct umac_event_mac_stats {
struct host_mac_msg_hdr hdr;
+ unsigned int roc_start;
+ unsigned int roc_stop;
+ unsigned int roc_complete;
+ unsigned int roc_stop_complete;
/* TX related */
unsigned int tx_cmd_cnt; /* Num of TX commands received from host */
unsigned int tx_done_cnt; /* Num of Tx done events sent to host */
@@ -342,12 +348,6 @@
TRIG_DISCONNECT
};
-struct umac_event_roc_status {
- struct host_mac_msg_hdr hdr;
- unsigned int roc_status;
-} __packed;
-
-
struct umac_event_ps_econ_wake {
struct host_mac_msg_hdr hdr;
enum UMAC_PS_ECON_WAKE_TRIG trigger;
@@ -402,6 +402,7 @@
#ifdef MULTI_CHAN_SUPPORT
UMAC_CMD_CHANCTX_TIME_INFO,
#endif
+ UMAC_CMD_CONT_TX,
};
enum UMAC_EVENT_TAG {
@@ -709,7 +710,9 @@
unsigned int roc_ctrl;
unsigned int roc_channel;
unsigned int roc_duration;
-
+#define ROC_TYPE_NORMAL 0
+#define ROC_TYPE_OFFCHANNEL_TX 1
+ unsigned int roc_type;
} __packed;
enum POWER_SAVE_TAG {
@@ -932,6 +935,12 @@
unsigned int chain_id;
} __packed;
+struct cmd_cont_tx {
+ struct host_mac_msg_hdr hdr;
+ unsigned int op;
+} __packed;
+
+
/* DFS SUPPORT */
/* Command to start/stop Radar detection operation */
@@ -1127,4 +1136,9 @@
unsigned int bt_state;
} __packed;
+struct umac_event_roc_status {
+ struct host_mac_msg_hdr hdr;
+ unsigned int roc_status;
+} __packed;
+
#endif /*_UCCP420HOST_UMAC_IF_H_*/
diff --git a/drivers/net/wireless/uccp420wlan/inc/umac_if.h b/drivers/net/wireless/uccp420wlan/inc/umac_if.h
index cd6ba74..63c1584 100644
--- a/drivers/net/wireless/uccp420wlan/inc/umac_if.h
+++ b/drivers/net/wireless/uccp420wlan/inc/umac_if.h
@@ -33,6 +33,8 @@
#include "hal.h"
#include "host_umac_if.h"
+#define UMAC_ROC_AC WLAN_AC_VO
+
struct umac_key {
unsigned char *peer_mac;
unsigned char *tx_mic;
@@ -184,6 +186,7 @@
extern int uccp420wlan_prog_rcv_bcn_mode(unsigned int bcn_rcv_mode);
extern int uccp420wlan_prog_aux_adc_chain(unsigned int chain_id);
+extern int uccp420wlan_cont_tx(int val);
extern int uccp420wlan_prog_txq_params(int index,
unsigned char *vif_addr,
unsigned int queue,
@@ -235,7 +238,8 @@
extern int uccp420wlan_prog_roc(unsigned int roc_status,
unsigned int roc_channel,
- unsigned int roc_duration);
+ unsigned int roc_duration,
+ unsigned int roc_type);
#ifdef CONFIG_PM
extern int uccp420wlan_prog_econ_ps_state(int if_index,
diff --git a/drivers/net/wireless/uccp420wlan/inc/version.h b/drivers/net/wireless/uccp420wlan/inc/version.h
index c2a476c..99f0685 100644
--- a/drivers/net/wireless/uccp420wlan/inc/version.h
+++ b/drivers/net/wireless/uccp420wlan/inc/version.h
@@ -23,7 +23,7 @@
*/
#ifndef _UCCP420WLAN_VERSION_H
#define _UCCP420WLAN_VERSION_H
-#define UCCP_DRIVER_VERSION "4_5_8"
+#define UCCP_DRIVER_VERSION "6_0_2"
#define UCCP_DRIVER_NAME "UCCP420WIFI"
#endif /* _UCCP420WLAN_VERSION_H */
diff --git a/drivers/net/wireless/uccp420wlan/src/80211_if.c b/drivers/net/wireless/uccp420wlan/src/80211_if.c
index b6f13ff..774d9c0 100644
--- a/drivers/net/wireless/uccp420wlan/src/80211_if.c
+++ b/drivers/net/wireless/uccp420wlan/src/80211_if.c
@@ -178,7 +178,7 @@
};
-/* Interface combinations for Virtual interfaces*/
+/* Interface combinations for Virtual interfaces */
static const struct ieee80211_iface_limit if_limit1[] = {
{ .max = 2, .types = BIT(NL80211_IFTYPE_STATION)}
};
@@ -203,7 +203,8 @@
#ifdef MULTI_CHAN_SUPPORT
static const struct ieee80211_iface_limit if_limit5[] = {
{ .max = 1, .types = BIT(NL80211_IFTYPE_STATION)},
- { .max = 1, .types = BIT(NL80211_IFTYPE_AP)}
+ { .max = 1, .types = BIT(NL80211_IFTYPE_AP) |
+ BIT(NL80211_IFTYPE_P2P_GO)}
};
#endif
@@ -274,6 +275,111 @@
}
+static void uccp420_roc_complete_work(struct work_struct *work)
+{
+ struct delayed_work *dwork = NULL;
+ struct mac80211_dev *dev = NULL;
+ unsigned long flags;
+ struct umac_chanctx *off_chanctx = NULL;
+ struct umac_vif *uvif = NULL, *tmp = NULL;
+ struct tx_config *tx = NULL;
+ u32 roc_queue = 0;
+ bool need_offchan;
+ int roc_off_chanctx_idx = -1;
+ int chan_id = 0;
+
+ dwork = container_of(work, struct delayed_work, work);
+ dev = container_of(dwork, struct mac80211_dev, roc_complete_work);
+ tx = &dev->tx;
+
+ mutex_lock(&dev->mutex);
+ need_offchan = dev->roc_params.need_offchan;
+
+ roc_queue = tx_queue_unmap(UMAC_ROC_AC);
+ roc_off_chanctx_idx = dev->roc_off_chanctx_idx;
+
+ /* Stop the ROC queue */
+ ieee80211_stop_queue(dev->hw, roc_queue);
+ /* Unlock RCU immediately as we are freeing off_chanctx in this funciton
+ * only and because flush_vif_queues sleep
+ */
+ rcu_read_lock();
+ off_chanctx = rcu_dereference(dev->off_chanctx[roc_off_chanctx_idx]);
+ rcu_read_unlock();
+
+ list_for_each_entry_safe(uvif, tmp, &off_chanctx->vifs, list) {
+ if (uvif == NULL || uvif->off_chanctx == NULL)
+ continue;
+ /* Flush the TX queues */
+ uccp420_flush_vif_queues(dev,
+ uvif,
+ uvif->off_chanctx->index,
+ BIT(UMAC_ROC_AC),
+ UMAC_VIF_CHANCTX_TYPE_OFF);
+
+
+ spin_lock_irqsave(&tx->lock, flags);
+ spin_lock(&dev->chanctx_lock);
+
+ /* ROC DONE: Move the channel context */
+ if (uvif->chanctx)
+ dev->curr_chanctx_idx = uvif->chanctx->index;
+ else
+ dev->curr_chanctx_idx = -1;
+
+ spin_unlock(&dev->chanctx_lock);
+ spin_unlock_irqrestore(&tx->lock, flags);
+
+ if (need_offchan) {
+ /* DEL from OFF chan list */
+ list_del_init(&uvif->list);
+ if (uvif->chanctx) {
+ /* Add it back to OP chan list */
+ list_add_tail(&uvif->list,
+ &uvif->chanctx->vifs);
+
+ /* !need_offchan: In this case, the frames are
+ * transmitted, so trigger is not needed.
+ *
+ * need_offchan: In this case, frames are
+ * buffered so we need trigger in case no frames
+ * come from mac80211.
+ */
+ /* Process OPER pending frames only.
+ * TXQ is flushed before start of ROC
+ */
+ chan_id = uvif->chanctx->index;
+ uccp420wlan_tx_proc_send_pend_frms_all(dev,
+ chan_id);
+ }
+ off_chanctx->nvifs--;
+ }
+ uvif->off_chanctx = NULL;
+ }
+
+ if (need_offchan)
+ kfree(off_chanctx);
+
+
+ rcu_assign_pointer(dev->off_chanctx[roc_off_chanctx_idx], NULL);
+ dev->roc_off_chanctx_idx = -1;
+ dev->roc_params.roc_in_progress = 0;
+
+ if (dev->cancel_roc == 0) {
+ ieee80211_remain_on_channel_expired(dev->hw);
+ DEBUG_LOG("%s-80211IF: ROC STOPPED..\n", dev->name);
+ } else {
+ dev->cancel_hw_roc_done = 1;
+ dev->cancel_roc = 0;
+ DEBUG_LOG("%s-80211IF: ROC CANCELLED..\n", dev->name);
+ }
+
+ /* Start the ROC queue */
+ ieee80211_wake_queue(dev->hw, roc_queue);
+ mutex_unlock(&dev->mutex);
+}
+
+
static void tx(struct ieee80211_hw *hw,
struct ieee80211_tx_control *txctl,
struct sk_buff *skb)
@@ -377,10 +483,13 @@
mutex_unlock(&dev->mutex);
return -ENODEV;
}
+
INIT_DELAYED_WORK(&dev->roc_complete_work, uccp420_roc_complete_work);
+
dev->state = STARTED;
memset(dev->params->pdout_voltage, 0,
sizeof(char) * MAX_AUX_ADC_SAMPLES);
+ dev->roc_off_chanctx_idx = -1;
mutex_unlock(&dev->mutex);
return 0;
@@ -408,19 +517,28 @@
struct umac_vif *uvif;
int vif_index, iftype;
+ mutex_lock(&dev->mutex);
iftype = vif->type;
v = vif;
vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER;
vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD;
- if (!(iftype == NL80211_IFTYPE_STATION ||
- iftype == NL80211_IFTYPE_ADHOC ||
- iftype == NL80211_IFTYPE_AP)) {
- pr_err("Invalid Interface type\n");
+ if (dev->current_vif_count == wifi->params.num_vifs) {
+ pr_err("%s: Exceeded Maximum supported VIF's cur:%d max: %d.\n",
+ __func__,
+ dev->current_vif_count,
+ wifi->params.num_vifs);
+
+ mutex_unlock(&dev->mutex);
return -ENOTSUPP;
}
- mutex_lock(&dev->mutex);
+ if (!(iftype == NL80211_IFTYPE_STATION ||
+ iftype == NL80211_IFTYPE_ADHOC ||
+ iftype == NL80211_IFTYPE_AP)) {
+ pr_err("Invalid Interface type\n");
+ return -ENOTSUPP;
+ }
if (wifi->params.production_test) {
if (dev->active_vifs || iftype != NL80211_IFTYPE_ADHOC) {
@@ -448,6 +566,7 @@
uvif->seq_no = 0;
uccp420wlan_vif_add(uvif);
dev->active_vifs |= (1 << vif_index);
+ dev->current_vif_count++;
if (iftype == NL80211_IFTYPE_ADHOC)
dev->tx_last_beacon = 0;
@@ -467,16 +586,16 @@
struct ieee80211_vif *v;
int vif_index;
+ mutex_lock(&dev->mutex);
v = vif;
vif_index = ((struct umac_vif *)&v->drv_priv)->vif_index;
- mutex_lock(&dev->mutex);
-
uccp420wlan_vif_remove((struct umac_vif *)&v->drv_priv);
dev->active_vifs &= ~(1 << vif_index);
rcu_assign_pointer(dev->vifs[vif_index], NULL);
synchronize_rcu();
+ dev->current_vif_count--;
mutex_unlock(&dev->mutex);
}
@@ -587,9 +706,9 @@
if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) {
DEBUG_LOG("%s-80211IF:Retry Limits changed to %d and %d\n",
- dev->name,
- conf->short_frame_max_tx_count,
- conf->long_frame_max_tx_count);
+ dev->name,
+ conf->short_frame_max_tx_count,
+ conf->long_frame_max_tx_count);
}
for (i = 0; i < MAX_VIFS; i++) {
@@ -1136,6 +1255,7 @@
hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
hw->max_listen_interval = 10;
hw->wiphy->max_remain_on_channel_duration = 5000; /*ROC*/
+ hw->offchannel_tx_hw_queue = WLAN_AC_VO;
hw->max_rates = 4;
hw->max_rate_tries = 5;
hw->queues = 4;
@@ -1271,193 +1391,160 @@
}
-static void uccp420_roc_complete_work(struct work_struct *work)
-{
- struct delayed_work *dwork;
- int i;
- struct mac80211_dev *dev;
-
- dwork = container_of(work, struct delayed_work, work);
- dev = container_of(dwork, struct mac80211_dev, roc_complete_work);
-
- if (atomic_read(&dev->roc_params.roc_mgmt_tx_count) != 0) {
- DEBUG_LOG("%s:%d but %d off channel tx frames pending\n",
- __func__,
- __LINE__,
- atomic_read(&dev->roc_params.roc_mgmt_tx_count));
- return;
- }
-
- /* ROC Completed */
- mutex_lock(&dev->mutex);
-
- /* Put the chip back to its original state */
- for (i = 0; i < MAX_VIFS; i++) {
-
- if (!dev->roc_params.roc_ps_changed)
- break;
-
- if (!(dev->active_vifs & (1 << i)))
- continue;
-
- uccp420wlan_prog_ps_state(i,
- dev->if_mac_addresses[i].addr,
- dev->power_save);
- }
-
- dev->roc_params.roc_ps_changed = 0;
-
- if (dev->roc_params.roc_chan_changed) {
- dev->chan_prog_done = 0;
-
- uccp420wlan_prog_channel(dev->cur_chan.pri_chnl_num,
- dev->cur_chan.center_freq1,
- dev->cur_chan.center_freq2,
- dev->cur_chan.ch_width,
-#ifdef MULTI_CHAN_SUPPORT
- 0,
-#endif
- dev->cur_chan.freq_band);
-
- if (wait_for_channel_prog_complete(dev)) {
- pr_err("%s:%d ROC Complete: Programming the Channel %d Timed-out (500ms)\n",
- __func__, __LINE__, dev->cur_chan.pri_chnl_num);
- dev->roc_params.roc_in_progress = 0;
- dev->roc_params.roc_chan_changed = 0;
- ieee80211_remain_on_channel_expired(dev->hw);
- mutex_unlock(&dev->mutex);
-
- /* Unable to go back to Home channel, what next?? */
- return;
- }
-
- dev->roc_params.roc_chan_changed = 0;
- }
-
- /* Inform FW that ROC is started */
- uccp420wlan_prog_roc(ROC_START, dev->cur_chan.pri_chnl_num, 0);
-
- ieee80211_remain_on_channel_expired(dev->hw);
- dev->roc_params.roc_in_progress = 0;
-
- DEBUG_LOG("%s:%d Coming back to Orig: %d\n",
- __func__,
- __LINE__,
- dev->power_save);
-
- mutex_unlock(&dev->mutex);
-}
-
-
static int remain_on_channel(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_channel *channel,
int duration,
enum ieee80211_roc_type type)
-
{
- int i;
struct mac80211_dev *dev = (struct mac80211_dev *)hw->priv;
- unsigned int pri_chnl_num = 0;
- unsigned int chnl_num1 = 0;
- unsigned int freq_band = channel->band;
- unsigned int ch_width = 0; /* 20MHz */
-#ifdef MULTI_CHAN_SUPPORT
+ unsigned int pri_chnl_num =
+ ieee80211_frequency_to_channel(channel->center_freq);
struct umac_vif *uvif = (struct umac_vif *)vif->drv_priv;
-#endif
-
- pri_chnl_num = ieee80211_frequency_to_channel(channel->center_freq);
- chnl_num1 = ieee80211_frequency_to_channel(channel->center_freq);
+ struct umac_chanctx *off_chanctx = NULL;
+ int off_chanctx_id = 0, i = 0;
+ unsigned long flags;
+ struct tx_config *tx = &dev->tx;
+ u32 hw_queue_map = 0;
+ struct ieee80211_chanctx_conf *vif_chanctx;
+ bool need_offchan = true;
mutex_lock(&dev->mutex);
- DEBUG_LOG("%s:%d orig_ps: %d The Params are: channel:%d\n",
- __func__, __LINE__,
- dev->power_save,
- pri_chnl_num);
- DEBUG_LOG(" duration:%d type: %d c1:%d band:%d\n",
+ DEBUG_LOG("%s-80211IF: Params are Chan:%d Dur:%d Type: %d\n",
+ dev->name,
+ ieee80211_frequency_to_channel(channel->center_freq),
duration,
- type,
- chnl_num1,
- freq_band);
+ type);
- /* Put the chip in powersave */
- for (i = 0; i < MAX_VIFS; i++) {
- if (dev->power_save == PWRSAVE_STATE_AWAKE)
- break;
-
- dev->roc_params.roc_ps_changed = 1;
-
- if (!(dev->active_vifs & (1 << i)))
- continue;
-
- uccp420wlan_prog_ps_state(i,
- dev->if_mac_addresses[i].addr,
- PWRSAVE_STATE_AWAKE);
+ if (dev->roc_params.roc_in_progress) {
+ DEBUG_LOG("%s-80211IF: Dropping roc...Busy\n", dev->name);
+ mutex_unlock(&dev->mutex);
+ return -EBUSY;
}
- do {
- if (dev->cur_chan.pri_chnl_num == pri_chnl_num)
- break;
+ if (dev->num_active_chanctx == 2) {
+ DEBUG_LOG("%s-80211IF: ROC is not supported in TSMC Mode\n",
+ dev->name);
- DEBUG_LOG("%s:%d Programming the Channel\n",
- __func__, __LINE__);
-
- dev->chan_prog_done = 0;
-
- uccp420wlan_prog_channel(dev->cur_chan.pri_chnl_num,
- channel->center_freq,
- 0,
- ch_width,
-#ifdef MULTI_CHAN_SUPPORT
- uvif->vif_index,
-#endif
- freq_band);
-
- if (!wait_for_channel_prog_complete(dev)) {
- dev->roc_params.roc_chan_changed = 1;
- break;
- }
-
- pr_err("%s:%d ROC Start: Programming the Channel %d Timed-out (500ms)\n",
- __func__, __LINE__, pri_chnl_num);
-
- /* Put the chip back to its orig state*/
- for (i = 0; i < MAX_VIFS; i++) {
- if (!dev->roc_params.roc_ps_changed)
- break;
-
- if (!(dev->active_vifs & (1 << i)))
- continue;
-
- uccp420wlan_prog_ps_state(i,
- dev->if_mac_addresses[i].addr,
- dev->power_save);
- }
-
- dev->roc_params.roc_ps_changed = 0;
-
- ieee80211_remain_on_channel_expired(dev->hw);
mutex_unlock(&dev->mutex);
+ return -ENOTSUPP;
+ }
- return 0;
+ /* Inform FW that ROC is started:
+ * For pure TX we send OFFCHANNEL_TX so that driver can terminate ROC
+ * For Tx + Rx we use NORMAL, FW will terminate ROC based on duration.
+ */
+ if (duration != 10 && type == ROC_TYPE_OFFCHANNEL_TX)
+ type = ROC_TYPE_NORMAL;
- } while (0);
+ /* uvif is in connected state
+ */
+ if (uvif->chanctx) {
+ rcu_read_lock();
- DEBUG_LOG("%s:%d Programming the Channel Success:%d\n",
- __func__, __LINE__,
- dev->chan_prog_done);
+ vif_chanctx =
+ rcu_dereference(dev->chanctx[uvif->chanctx->index]);
- /* Inform FW that ROC is started */
- uccp420wlan_prog_roc(ROC_START, pri_chnl_num, duration);
+ /* AS ROC frames are MGMT frames, checking only for Primary
+ * Channel.
+ */
+ if (vif_chanctx->def.chan->center_freq == channel->center_freq)
+ need_offchan = false;
- ieee80211_queue_delayed_work(hw,
- &dev->roc_complete_work,
- msecs_to_jiffies(duration));
+ rcu_read_unlock();
+ }
- dev->roc_params.roc_in_progress = 1;
+ DEBUG_LOG("%s-80211IF: need_offchan: %d\n", dev->name, need_offchan);
+ dev->roc_params.need_offchan = need_offchan;
- ieee80211_ready_on_channel(dev->hw);
+ if (need_offchan) {
+ /* Different chan context than the uvif */
+ off_chanctx = kmalloc(sizeof(struct umac_chanctx),
+ GFP_KERNEL);
+
+ if (!off_chanctx) {
+ pr_err("%s: Unable to alloc mem for channel context\n",
+ __func__);
+ mutex_unlock(&dev->mutex);
+ return -ENOMEM;
+ }
+
+ /** Currently OFFCHAN is limited to handling ROC case
+ * but it is meant for a generic case.
+ * ideally we should look for existing offchan context
+ * and re-use/create.
+ */
+ for (i = 0; i < MAX_OFF_CHANCTX; i++) {
+ if (!dev->off_chanctx[i]) {
+ off_chanctx_id = i;
+ break;
+ }
+ }
+
+ if (uvif->chanctx) {
+ ieee80211_stop_queues(hw);
+
+ hw_queue_map = BIT(WLAN_AC_BK) |
+ BIT(WLAN_AC_BE) |
+ BIT(WLAN_AC_VI) |
+ BIT(WLAN_AC_VO) |
+ BIT(WLAN_AC_BCN);
+
+ uccp420_flush_vif_queues(dev,
+ uvif,
+ uvif->chanctx->index,
+ hw_queue_map,
+ UMAC_VIF_CHANCTX_TYPE_OPER);
+ }
+
+
+ off_chanctx->index = OFF_CHANCTX_IDX_BASE + off_chanctx_id;
+ dev->roc_off_chanctx_idx = off_chanctx_id;
+ INIT_LIST_HEAD(&off_chanctx->vifs);
+ off_chanctx->nvifs = 0;
+
+ if (uvif->chanctx) {
+ /* Delete the uvif from OP channel list */
+ list_del_init(&uvif->list);
+ }
+ /* Add the vif to the off_chanctx */
+ list_add_tail(&uvif->list, &off_chanctx->vifs);
+ off_chanctx->nvifs++;
+ rcu_assign_pointer(dev->off_chanctx[off_chanctx_id],
+ off_chanctx);
+ synchronize_rcu();
+
+
+ /* Move the channel context */
+ spin_lock_bh(&dev->chanctx_lock);
+ dev->curr_chanctx_idx = off_chanctx->index;
+ spin_unlock_bh(&dev->chanctx_lock);
+ } else {
+ /* Same channel context, just update off_chanctx
+ * to chanctx
+ */
+ off_chanctx = uvif->chanctx;
+
+ for (i = 0; i < MAX_OFF_CHANCTX; i++) {
+ if (!dev->off_chanctx[i]) {
+ off_chanctx_id = i;
+ break;
+ }
+ }
+ dev->roc_off_chanctx_idx = off_chanctx->index;
+ rcu_assign_pointer(dev->off_chanctx[off_chanctx_id],
+ off_chanctx);
+ synchronize_rcu();
+ }
+ spin_lock_irqsave(&tx->lock, flags);
+ uvif->off_chanctx = off_chanctx;
+ spin_unlock_irqrestore(&tx->lock, flags);
+
+ uccp420wlan_prog_roc(ROC_START, pri_chnl_num, duration, type);
+
+ if (uvif->chanctx)
+ ieee80211_wake_queues(hw);
mutex_unlock(&dev->mutex);
@@ -1467,34 +1554,34 @@
static int cancel_remain_on_channel(struct ieee80211_hw *hw)
{
- int i = 0;
struct mac80211_dev *dev = (struct mac80211_dev *)hw->priv;
+ int ret = 0;
mutex_lock(&dev->mutex);
if (dev->roc_params.roc_in_progress) {
- cancel_delayed_work_sync(&dev->roc_complete_work);
+ dev->cancel_hw_roc_done = 0;
+ dev->cancel_roc = 1;
+ DEBUG_LOG("%s-80211IF: Cancelling HW ROC....\n", dev->name);
- /* Put the chip back to its original state */
- for (i = 0; i < MAX_VIFS; i++) {
- if (!(dev->active_vifs & (1 << i)))
- continue;
+ uccp420wlan_prog_roc(ROC_STOP, 0, 0, 0);
- uccp420wlan_prog_ps_state(i,
- dev->if_mac_addresses[i].addr,
- dev->power_save);
+ mutex_unlock(&dev->mutex);
+
+ if (!wait_for_cancel_hw_roc(dev)) {
+ DEBUG_LOG("%s-80211IF: Cancel HW ROC....done\n",
+ dev->name);
+ ret = 0;
+ } else {
+ DEBUG_LOG("%s-80211IF: Cancel HW ROC..timedout\n",
+ dev->name);
+ ret = -1;
}
-
- DEBUG_LOG("%s:%d Coming back to Orig:%d\n",
- __func__, __LINE__,
- dev->power_save);
-
- dev->roc_params.roc_in_progress = 0;
+ } else {
+ mutex_unlock(&dev->mutex);
}
- mutex_unlock(&dev->mutex);
-
- return 0;
+ return ret;
}
@@ -1563,7 +1650,7 @@
if (uccp420wlan_prog_econ_ps_state(active_vif_index,
PWRSAVE_STATE_AWAKE)) {
- pr_err(" %s : Error Occured\n",
+ pr_err("%s : Error Occured\n",
__func__);
mutex_unlock(&dev->mutex);
return -1;
@@ -1617,7 +1704,7 @@
}
if (count != 1) {
- pr_err("%s: Economy mode supported only for single VIF in STA mode\n",
+ pr_err("%s: Economy mode supp only for single VIF(STA mode)\n",
__func__);
mutex_unlock(&dev->mutex);
return -1;
@@ -1731,6 +1818,9 @@
unsigned int len)
{
struct mac80211_dev *dev = (struct mac80211_dev *)context;
+ int i = 0;
+ struct ieee80211_vif *vif = NULL;
+ const char ra[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
/* DO NOT update the scan results through cfg80211 API's we just pass
* the beacons and probe responses up and mac80211 will inform cfg80211
@@ -1750,6 +1840,25 @@
dev->stats->umac_scan_complete++;
ieee80211_scan_completed(dev->hw, false);
+ /* WAR for TT_PRB0164. To be removed after patch
+ * submitted to kernel
+ */
+ for (i = 0; i < MAX_VIFS; i++) {
+
+ if (!(dev->active_vifs & (1 << i)))
+ continue;
+
+ rcu_read_lock();
+ vif = rcu_dereference(dev->vifs[i]);
+ rcu_read_unlock();
+
+ if (vif->type != NL80211_IFTYPE_AP)
+ continue;
+
+ ieee80211_stop_tx_ba_cb_irqsafe(vif,
+ ra, IEEE80211_NUM_TIDS);
+ }
+
/* Keep track of HW Scan requests and compeltes */
wifi->params.hw_scan_status = HW_SCAN_STATUS_NONE;
}
@@ -1987,8 +2096,8 @@
}
DEBUG_LOG("%s: %d MHz\n",
- __func__,
- conf->def.chan->center_freq);
+ __func__,
+ conf->def.chan->center_freq);
mutex_lock(&dev->mutex);
@@ -2117,20 +2226,37 @@
struct mac80211_dev *dev = NULL;
struct umac_vif *uvif = NULL;
struct umac_chanctx *ctx = NULL;
+ u32 hw_queue_map = 0;
+ int i = 0;
dev = hw->priv;
uvif = (struct umac_vif *)vif->drv_priv;
ctx = (struct umac_chanctx *)conf->drv_priv;
DEBUG_LOG("%s: addr: %pM, type: %d, p2p: %d chan: %d MHz\n",
- __func__,
- vif->addr,
- vif->type,
- vif->p2p,
- conf->def.chan->center_freq);
+ __func__,
+ vif->addr,
+ vif->type,
+ vif->p2p,
+ conf->def.chan->center_freq);
mutex_lock(&dev->mutex);
+ /* We need to specifically handle flushing tx queues for the AP VIF
+ * here (for STA VIF, mac80211 handles this via flush_queues)
+ */
+ if (vif->type == NL80211_IFTYPE_AP) {
+ /* Flush all queues for this VIF */
+ for (i = 0; i < NUM_ACS; i++)
+ hw_queue_map |= BIT(i);
+
+ uccp420_flush_vif_queues(dev,
+ uvif,
+ uvif->chanctx->index,
+ hw_queue_map,
+ UMAC_VIF_CHANCTX_TYPE_OPER);
+ }
+
uvif->chanctx = NULL;
list_del(&uvif->list);
@@ -2154,25 +2280,13 @@
{
struct mac80211_dev *dev = NULL;
struct umac_vif *uvif = NULL;
- struct umac_chanctx *ctx = NULL;
- unsigned int chan_ctx_id = 0;
- unsigned int queue = 0;
- unsigned int pending = 0;
- int count = 0;
- int peer_id = -1;
+ u32 hw_queue_map = 0;
int i = 0;
- unsigned long flags = 0;
- struct sk_buff_head *pend_pkt_q = NULL;
- struct tx_config *tx = NULL;
- struct ieee80211_sta *sta = NULL;
- struct umac_sta *usta = NULL;
dev = hw->priv;
mutex_lock(&dev->mutex);
- tx = &dev->tx;
-
if (!vif)
goto out;
@@ -2181,75 +2295,21 @@
if (!uvif->chanctx)
goto out;
- if (dev->num_active_chanctx != 2) {
- DEBUG_LOG("%s-80211IF: Flush is only supported for TSMC case\n",
- __func__);
- goto out;
+ /* Convert the mac80211 queue map to our hw queue map */
+ for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+ if (queues & BIT(i))
+ hw_queue_map |= BIT(tx_queue_map(i));
}
-
- ctx = uvif->chanctx;
- chan_ctx_id = ctx->index;
-
- for (queue = 0; queue < WLAN_AC_MAX_CNT; queue++) {
- if (!((1 << queue) & queues))
- continue;
-
-check_tokens_flush_complete:
- pending = 0;
-
- spin_lock_irqsave(&tx->lock, flags);
- rcu_read_lock();
-
- for (i = 0; i < MAX_PEND_Q_PER_AC; i++) {
- if (i < MAX_PEERS) {
- sta = rcu_dereference(dev->peers[i]);
-
- if (!sta)
- continue;
-
- usta = (struct umac_sta *)(sta->drv_priv);
-
- if (usta->vif_index == uvif->vif_index)
- peer_id = i;
- else
- continue;
- } else if (i == uvif->vif_index) {
- peer_id = uvif->vif_index;
- } else
- continue;
-
- pend_pkt_q = &tx->pending_pkt[peer_id][queue];
-
- /* Assuming all packets for the peer have same channel
- * context
- */
- pending = skb_queue_len(pend_pkt_q);
- }
-
- rcu_read_unlock();
- spin_unlock_irqrestore(&tx->lock, flags);
-
- if (pending && (count < QUEUE_FLUSH_TIMEOUT_TICKS)) {
- current->state = TASK_INTERRUPTIBLE;
-
- if (0 == schedule_timeout(1))
- count++;
-
- goto check_tokens_flush_complete;
- }
-
- if (pending)
- DEBUG_LOG("%s: failed for VIF: %d and Queue: %d, pending: %d\n",
- __func__,
- uvif->vif_index,
- queue,
- pending);
- else
- DEBUG_LOG("%s: Flush for VIF: %d and Queue: %d success\n",
- __func__,
- uvif->vif_index,
- queue);
- }
+ /* This op should not get called during ROC operation, so we can assume
+ * that the vif_chanctx_type will be UMAC_VIF_CHANCTX_TYPE_OPER. As for
+ * TSMC operation the VIF can only be associated to one channel context,
+ * so we pass uvif->chanctx->index as the parameter for chanctx_idx
+ */
+ uccp420_flush_vif_queues(dev,
+ uvif,
+ uvif->chanctx->index,
+ hw_queue_map,
+ UMAC_VIF_CHANCTX_TYPE_OPER);
out:
mutex_unlock(&dev->mutex);
@@ -2331,6 +2391,7 @@
}
dev = (struct mac80211_dev *)hw->priv;
+ memset(dev, 0, sizeof(struct mac80211_dev));
hwsim_class = class_create(THIS_MODULE, "uccp420");
@@ -2374,6 +2435,7 @@
spin_lock_init(&dev->chanctx_lock);
#endif
+ spin_lock_init(&dev->roc_lock);
dev->state = STOPPED;
dev->active_vifs = 0;
dev->txpower = DEFAULT_TX_POWER;
@@ -2391,6 +2453,7 @@
dev->params = &wifi->params;
dev->stats = &wifi->stats;
dev->umac_proc_dir_entry = wifi->umac_proc_dir_entry;
+ dev->current_vif_count = 0;
dev->stats->system_rev = system_rev;
#ifdef MULTI_CHAN_SUPPORT
dev->num_active_chanctx = 0;
@@ -2462,6 +2525,7 @@
seq_puts(m, "\n");
seq_printf(m, "production_test = %d\n", wifi->params.production_test);
+ seq_printf(m, "bypass_vpd = %d\n", wifi->params.bypass_vpd);
seq_printf(m, "tx_fixed_mcs_indx = %d (%s)\n",
wifi->params.tx_fixed_mcs_indx,
(wifi->params.prod_mode_rate_flag &
@@ -2616,6 +2680,8 @@
wifi->params.payload_length);
seq_printf(m, "start_prod_mode = channel: %d\n",
wifi->params.start_prod_mode);
+ seq_printf(m, "continuous_tx = %d\n",
+ wifi->params.cont_tx);
if (ftm || wifi->params.production_test)
seq_printf(m, "set_tx_power = %d dB\n",
@@ -2915,6 +2981,14 @@
total_rssi_samples);
seq_puts(m, "************* LMAC STATS ***********\n");
+ seq_printf(m, "roc_start =%d\n",
+ wifi->stats.roc_start);
+ seq_printf(m, "roc_stop =%d\n",
+ wifi->stats.roc_stop);
+ seq_printf(m, "roc_complete =%d\n",
+ wifi->stats.roc_complete);
+ seq_printf(m, "roc_stop_complete =%d\n",
+ wifi->stats.roc_stop_complete);
/* TX related */
seq_printf(m, "tx_cmd_cnt =%d\n",
wifi->stats.tx_cmd_cnt);
@@ -3117,6 +3191,12 @@
}
} else
pr_err("Invalid parameter value\n");
+ } else if (param_get_val(buf, "bypass_vpd=", &val)) {
+ if ((val == 0) || (val == 1)) {
+ if (wifi->params.bypass_vpd != val)
+ wifi->params.bypass_vpd = val;
+ } else
+ pr_err("Invalid parameter value\n");
} else if (param_get_val(buf, "num_vifs=", &val)) {
if (val > 0 && val <= MAX_VIFS) {
if (wifi->params.num_vifs != val) {
@@ -3709,6 +3789,13 @@
AUX_ADC_CHAIN1,
AUX_ADC_CHAIN2);
} else if ((wifi->params.production_test) &&
+ param_get_val(buf, "continuous_tx=", &val)) {
+ if (val == 0 || val == 1) {
+ wifi->params.cont_tx = val;
+ uccp420wlan_cont_tx(val);
+ } else
+ pr_err("Invalid tx_continuous parameter\n");
+ } else if ((wifi->params.production_test) &&
param_get_val(buf, "start_prod_mode=", &val)) {
unsigned int pri_chnl_num = 0;
unsigned int freq_band = IEEE80211_BAND_5GHZ;
diff --git a/drivers/net/wireless/uccp420wlan/src/core.c b/drivers/net/wireless/uccp420wlan/src/core.c
index 9df0fda..45e754a 100644
--- a/drivers/net/wireless/uccp420wlan/src/core.c
+++ b/drivers/net/wireless/uccp420wlan/src/core.c
@@ -117,6 +117,31 @@
}
+int wait_for_cancel_hw_roc(struct mac80211_dev *dev)
+{
+ int count = 0;
+
+check_cancel_hw_roc_complete:
+ if (!dev->cancel_hw_roc_done && (count < CANCEL_HW_ROC_TIMEOUT_TICKS)) {
+ current->state = TASK_INTERRUPTIBLE;
+ if (0 == schedule_timeout(1))
+ count++;
+ goto check_cancel_hw_roc_complete;
+ }
+
+ if (!dev->cancel_hw_roc_done) {
+ pr_err("%s-UMAC: Warning: Didn't get CANCEL_HW_ROC_DONE after %ld timer ticks\n",
+ dev->name,
+ CANCEL_HW_ROC_TIMEOUT_TICKS);
+ return -1;
+ }
+
+ DEBUG_LOG("%s-UMAC: Cancel HW RoC complete after %d timer ticks\n",
+ dev->name, count);
+
+ return 0;
+
+}
int wait_for_channel_prog_complete(struct mac80211_dev *dev)
{
@@ -147,6 +172,38 @@
}
+int wait_for_tx_queue_flush_complete(struct mac80211_dev *dev,
+ unsigned int queue)
+{
+ int count = 0;
+
+check_tx_queue_flush_complete:
+ if (dev->tx.outstanding_tokens[queue] &&
+ (count < QUEUE_FLUSH_TIMEOUT_TICKS)) {
+ current->state = TASK_INTERRUPTIBLE;
+ if (0 == schedule_timeout(1))
+ count++;
+ goto check_tx_queue_flush_complete;
+ }
+
+ if (dev->tx.outstanding_tokens[queue]) {
+ pr_err("%s-UMAC: Warning: Tx Queue %d flush failed pending: %d after %ld timer ticks\n",
+ dev->name,
+ queue,
+ dev->tx.outstanding_tokens[queue],
+ QUEUE_FLUSH_TIMEOUT_TICKS);
+ return -1;
+ }
+
+ DEBUG_LOG("%s-UMAC: Flushed Tx queue %d in %d timer ticks\n",
+ dev->name,
+ queue,
+ count);
+
+ return 0;
+
+}
+
int wait_for_reset_complete(struct mac80211_dev *dev)
{
@@ -880,6 +937,10 @@
struct mac80211_dev *dev = (struct mac80211_dev *)context;
/* TX related */
+ dev->stats->roc_start = mac_stats->roc_start;
+ dev->stats->roc_stop = mac_stats->roc_stop;
+ dev->stats->roc_complete = mac_stats->roc_complete;
+ dev->stats->roc_stop_complete = mac_stats->roc_stop_complete;
dev->stats->tx_cmd_cnt = mac_stats->tx_cmd_cnt;
dev->stats->tx_done_cnt = mac_stats->tx_done_cnt;
dev->stats->tx_edca_trigger_cnt = mac_stats->tx_edca_trigger_cnt;
@@ -1045,168 +1106,6 @@
#endif
-#ifdef MULTI_CHAN_SUPPORT
-void uccp420wlan_proc_ch_sw_event(struct umac_event_ch_switch *ch_sw_info,
- void *context)
-{
- struct mac80211_dev *dev = NULL;
- int chan = 0;
- int curr_freq = 0;
- int chan_id = 0;
- struct sk_buff_head *txq = NULL;
- int txq_len = 0;
- int i = 0;
- int queue = 0;
- unsigned long flags = 0;
- int curr_bit = 0;
- int pool_id = 0;
- int ret = 0;
- int peer_id = -1;
- int ac = 0;
- struct ieee80211_chanctx_conf *curr_chanctx = NULL;
- struct tx_config *tx = NULL;
-
- if (!ch_sw_info || !context) {
- pr_err("%s: Invalid Parameters\n", __func__);
- return;
- }
-
- dev = (struct mac80211_dev *)context;
- chan = ch_sw_info->chan;
- tx = &dev->tx;
-
- rcu_read_lock();
-
- for (i = 0; i < MAX_CHANCTX; i++) {
- curr_chanctx = rcu_dereference(dev->chanctx[i]);
-
- if (curr_chanctx) {
- curr_freq = curr_chanctx->def.chan->center_freq;
-
- if (ieee80211_frequency_to_channel(curr_freq) == chan) {
- chan_id = i;
- break;
- }
- }
- }
-
- rcu_read_unlock();
-
- if (i == MAX_CHANCTX) {
- pr_err("%s: Invalid Channel Context: chan: %d\n",
- __func__,
- chan);
- return;
- }
-
-
- /* Switch to the new channel context */
- spin_lock(&dev->chanctx_lock);
- dev->curr_chanctx_idx = chan_id;
- spin_unlock(&dev->chanctx_lock);
-
- /* We now try to xmit any frames whose xmission got cancelled due to a
- * previous channel switch
- */
- for (i = 0; i < NUM_TX_DESCS; i++) {
- spin_lock_irqsave(&tx->lock, flags);
-
- curr_bit = (i % TX_DESC_BUCKET_BOUND);
- pool_id = (i / TX_DESC_BUCKET_BOUND);
-
- if (test_and_set_bit(curr_bit, &tx->buf_pool_bmp[pool_id])) {
- spin_unlock_irqrestore(&tx->lock, flags);
- continue;
- }
-
- txq = &tx->pkt_info[chan_id][i].pkt;
- txq_len = skb_queue_len(txq);
- queue = tx->pkt_info[chan_id][i].queue;
-
- if (!txq_len) {
- /* Reserved token */
- if (i < (NUM_TX_DESCS_PER_AC * NUM_ACS)) {
- queue = (i % NUM_ACS);
- peer_id = get_curr_peer_opp(dev,
- chan_id,
- queue);
-
- if (peer_id == -1) {
- /* Mark the token as available */
- __clear_bit(curr_bit,
- &tx->buf_pool_bmp[pool_id]);
-
- spin_unlock_irqrestore(&tx->lock,
- flags);
- continue;
- }
-
- /* Spare token */
- } else {
- for (ac = WLAN_AC_VO; ac >= 0; ac--) {
- peer_id = get_curr_peer_opp(dev,
- chan_id,
- ac);
-
- if (peer_id != -1) {
- queue = ac;
- break;
- }
- }
-
- if (ac < 0) {
- /* Mark the token as available */
- __clear_bit(curr_bit,
- &tx->buf_pool_bmp[pool_id]);
-
- spin_unlock_irqrestore(&tx->lock,
- flags);
- continue;
- }
- }
-
- uccp420wlan_tx_proc_pend_frms(dev,
- queue,
- chan_id,
- peer_id,
- i);
-
- tx->outstanding_tokens[queue]++;
-
- }
-
- spin_unlock_irqrestore(&tx->lock, flags);
-
- ret = __uccp420wlan_tx_frame(dev,
- queue,
- i,
- chan_id,
- 0,
- 0); /* TODO: Currently sending 0
- since this param is not used
- as expected in the orig
- code for multiple frames etc
- Need to set this
- properly when the orig code
- logic is corrected
- */
- if (ret < 0) {
- /* SDK: Check if we need to clear the TX bitmap and
- * desc_chan_map here
- */
- pr_err("%s: Queueing of TX frame to FW failed\n",
- __func__);
- } else {
- spin_lock_irqsave(&tx->lock, flags);
- tx->desc_chan_map[i] = chan_id;
- spin_unlock_irqrestore(&tx->lock, flags);
- }
-
- }
-}
-#endif
-
-
void uccp420wlan_rx_frame(struct sk_buff *skb, void *context)
{
struct mac80211_dev *dev = (struct mac80211_dev *)context;
diff --git a/drivers/net/wireless/uccp420wlan/src/tx.c b/drivers/net/wireless/uccp420wlan/src/tx.c
index 1d43578..465d87d 100644
--- a/drivers/net/wireless/uccp420wlan/src/tx.c
+++ b/drivers/net/wireless/uccp420wlan/src/tx.c
@@ -61,8 +61,7 @@
}
}
-
-static inline int tx_queue_map(int queue)
+int tx_queue_map(int queue)
{
unsigned int ac[4] = {WLAN_AC_VO, WLAN_AC_VI, WLAN_AC_BE, WLAN_AC_BK};
@@ -72,8 +71,7 @@
return WLAN_AC_VO;
}
-
-static inline int tx_queue_unmap(int queue)
+int tx_queue_unmap(int queue)
{
unsigned int ac[4] = {3, 2, 1, 0};
@@ -105,6 +103,9 @@
int tx_fixed_mcs_idx = 0;
int tx_fixed_rate = 0;
struct ieee80211_supported_band *band = NULL;
+ struct umac_vif *uvif = NULL;
+
+ uvif = (struct umac_vif *)(tx_info->control.vif->drv_priv);
/* Rate info will be retained, except the count*/
ieee80211_tx_info_clear_status(tx_info);
@@ -207,13 +208,18 @@
index++;
}
- if ((tx_info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) &&
+ if (((tx_info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) ||
+ (uvif->chanctx &&
+ (uvif->chanctx->index == dev->roc_off_chanctx_idx))) &&
(atomic_dec_return(&dev->roc_params.roc_mgmt_tx_count) == 0)) {
- if (dev->roc_params.roc_in_progress) {
- /* Reuse the delayed workqueue with 1ms delay */
- ieee80211_queue_delayed_work(dev->hw,
- &dev->roc_complete_work,
- msecs_to_jiffies(1));
+ DEBUG_LOG("%s-UMACTX: TXDONE Frame: %d\n",
+ dev->name,
+ atomic_read(&dev->roc_params.roc_mgmt_tx_count));
+ if (dev->roc_params.roc_in_progress &&
+ dev->roc_params.roc_type == ROC_TYPE_OFFCHANNEL_TX) {
+ uccp420wlan_prog_roc(ROC_STOP, 0, 0, 0);
+ DEBUG_LOG("%s-UMACTX: all offchan pend frames clear\n",
+ dev->name);
}
}
@@ -242,6 +248,7 @@
if (!test_and_set_bit(curr_bit, &tx->buf_pool_bmp[pool_id])) {
token_id = queue + (NUM_ACS * cnt);
+ tx->outstanding_tokens[queue]++;
break;
}
}
@@ -258,29 +265,40 @@
/* Do not set, we will queue to the same token */
if (!test_and_set_bit(curr_bit,
&tx->buf_pool_bmp[pool_id])) {
+ tx->outstanding_tokens[queue]++;
break;
}
}
}
- if (token_id != NUM_TX_DESCS) {
- tx->outstanding_tokens[queue]++;
-#ifdef MULTI_CHAN_SUPPORT
- tx->desc_chan_map[token_id] = curr_chanctx_idx;
-#endif
- }
-
return token_id;
}
-
-int get_curr_peer_opp(struct mac80211_dev *dev,
-#ifdef MULTI_CHAN_SUPPORT
- int curr_chanctx_idx,
-#endif
- int queue)
+void free_token(struct mac80211_dev *dev,
+ int token_id,
+ int queue)
{
+ struct tx_config *tx = &dev->tx;
+ int bit = -1;
+ int pool_id = -1;
+
+ bit = (token_id % TX_DESC_BUCKET_BOUND);
+ pool_id = (token_id / TX_DESC_BUCKET_BOUND);
+
+ __clear_bit(bit, &tx->buf_pool_bmp[pool_id]);
+
+ tx->outstanding_tokens[queue]--;
+}
+
+
+struct curr_peer_info get_curr_peer_opp(struct mac80211_dev *dev,
+#ifdef MULTI_CHAN_SUPPORT
+ int curr_chanctx_idx,
+#endif
+ int ac)
+ {
unsigned int curr_peer_opp = 0;
+ unsigned int curr_vif_op_chan = UMAC_VIF_CHANCTX_TYPE_OPER;
unsigned int i = 0;
struct tx_config *tx = NULL;
#ifdef MULTI_CHAN_SUPPORT
@@ -291,21 +309,28 @@
int vif_index = -1;
#endif
unsigned int init_peer_opp = 0;
+ struct curr_peer_info peer_info;
+ unsigned int pend_q_len;
+ struct sk_buff_head *pend_q = NULL;
tx = &dev->tx;
#ifdef MULTI_CHAN_SUPPORT
- init_peer_opp = tx->curr_peer_opp[curr_chanctx_idx][queue];
+ init_peer_opp = tx->curr_peer_opp[curr_chanctx_idx][ac];
#else
- init_peer_opp = tx->curr_peer_opp[queue];
+ init_peer_opp = tx->curr_peer_opp[ac];
#endif
-
+ /*TODO: Optimize this loop for BCN_Q
+ */
for (i = 0; i < MAX_PEND_Q_PER_AC; i++) {
curr_peer_opp = (init_peer_opp + i) % MAX_PEND_Q_PER_AC;
#ifdef MULTI_CHAN_SUPPORT
rcu_read_lock();
+ /* RoC Frame do not have a "sta" entry.
+ * so we need not handle RoC stuff here
+ */
if (curr_peer_opp < MAX_PEERS) {
sta = rcu_dereference(dev->peers[curr_peer_opp]);
@@ -316,16 +341,43 @@
usta = (struct umac_sta *)(sta->drv_priv);
- if (!usta->chanctx) {
+ vif = rcu_dereference(dev->vifs[usta->vif_index]);
+
+ if (!vif) {
rcu_read_unlock();
continue;
}
- if (usta->chanctx->index != curr_chanctx_idx) {
+
+ uvif = (struct umac_vif *)(vif->drv_priv);
+
+ if (!uvif->chanctx && !uvif->off_chanctx) {
rcu_read_unlock();
continue;
}
+ if ((uvif->chanctx &&
+ (uvif->chanctx->index != curr_chanctx_idx)) ||
+ !uvif->chanctx) {
+ if ((uvif->off_chanctx &&
+ (uvif->off_chanctx->index !=
+ curr_chanctx_idx)) ||
+ !uvif->off_chanctx) {
+ rcu_read_unlock();
+ continue;
+ } else {
+ curr_vif_op_chan =
+ UMAC_VIF_CHANCTX_TYPE_OFF;
+ }
+ } else {
+ if (dev->roc_params.roc_in_progress &&
+ !dev->roc_params.need_offchan)
+ curr_vif_op_chan =
+ UMAC_VIF_CHANCTX_TYPE_OFF;
+ else
+ curr_vif_op_chan =
+ UMAC_VIF_CHANCTX_TYPE_OPER;
+ }
} else {
vif_index = (curr_peer_opp - MAX_PEERS);
@@ -338,46 +390,177 @@
uvif = (struct umac_vif *)(vif->drv_priv);
- if (!uvif->chanctx) {
+ if (!uvif->chanctx && !uvif->off_chanctx) {
rcu_read_unlock();
continue;
}
- if (uvif->chanctx->index != curr_chanctx_idx) {
- rcu_read_unlock();
- continue;
+ /* For a beacon queue we will process the frames
+ * irrespective of the current channel context.
+ * The FW will take care of transmitting them in the
+ * appropriate channel.
+ */
+
+ if (ac != WLAN_AC_BCN &&
+ ((uvif->chanctx &&
+ (uvif->chanctx->index != curr_chanctx_idx)) ||
+ !uvif->chanctx)) {
+ if ((uvif->off_chanctx &&
+ (uvif->off_chanctx->index !=
+ curr_chanctx_idx)) ||
+ !uvif->off_chanctx) {
+ rcu_read_unlock();
+ continue;
+ } else {
+ curr_vif_op_chan =
+ UMAC_VIF_CHANCTX_TYPE_OFF;
+ }
+ } else {
+ if (dev->roc_params.roc_in_progress &&
+ !dev->roc_params.need_offchan)
+ curr_vif_op_chan =
+ UMAC_VIF_CHANCTX_TYPE_OFF;
+ else
+ curr_vif_op_chan =
+ UMAC_VIF_CHANCTX_TYPE_OPER;
}
}
rcu_read_unlock();
#endif
+ pend_q = &tx->pending_pkt[curr_vif_op_chan][curr_peer_opp][ac];
+ pend_q_len = skb_queue_len(pend_q);
- if (skb_queue_len(&tx->pending_pkt[curr_peer_opp][queue])) {
+ if (pend_q_len) {
#ifdef MULTI_CHAN_SUPPORT
- tx->curr_peer_opp[curr_chanctx_idx][queue] =
+ tx->curr_peer_opp[curr_chanctx_idx][ac] =
(curr_peer_opp + 1) % MAX_PEND_Q_PER_AC;
#else
- tx->curr_peer_opp[queue] =
+ tx->curr_peer_opp[ac] =
(curr_peer_opp + 1) % MAX_PEND_Q_PER_AC;
#endif
break;
}
}
- if (i == MAX_PEND_Q_PER_AC)
- return -1;
+ if (i == MAX_PEND_Q_PER_AC) {
+ peer_info.id = -1;
+ peer_info.op_chan_idx = -1;
+ } else {
+ peer_info.id = curr_peer_opp;
+ peer_info.op_chan_idx = curr_vif_op_chan;
+ DEBUG_LOG("%s-UMACTX: Queue: %d Peer: %d op_chan: %d ",
+ dev->name,
+ ac,
+ curr_peer_opp,
+ curr_vif_op_chan);
+ DEBUG_LOG("chanctx: %d got opportunity, pending: %d\n",
+ curr_chanctx_idx,
+ pend_q_len);
+ }
- return curr_peer_opp;
+ return peer_info;
}
-void uccp420wlan_tx_proc_pend_frms(struct mac80211_dev *dev,
- int queue,
#ifdef MULTI_CHAN_SUPPORT
- int curr_chanctx_idx,
+void uccp420wlan_tx_proc_send_pend_frms_all(struct mac80211_dev *dev,
+ int ch_id)
+{
+ int txq_len = 0;
+ int i = 0, cnt = 0;
+ int queue = 0;
+ unsigned long flags = 0;
+ int curr_bit = 0;
+ int pool_id = 0;
+ int ret = 0;
+ int start_ac, end_ac;
+ unsigned int pkts_pend = 0;
+ struct tx_config *tx = NULL;
+ struct sk_buff_head *txq = NULL;
+
+ tx = &dev->tx;
+
+ for (i = 0; i < NUM_TX_DESCS; i++) {
+ spin_lock_irqsave(&tx->lock, flags);
+
+ curr_bit = (i % TX_DESC_BUCKET_BOUND);
+ pool_id = (i / TX_DESC_BUCKET_BOUND);
+
+ if (test_and_set_bit(curr_bit, &tx->buf_pool_bmp[pool_id])) {
+ spin_unlock_irqrestore(&tx->lock, flags);
+ continue;
+ }
+
+ txq = &tx->pkt_info[ch_id][i].pkt;
+ txq_len = skb_queue_len(txq);
+
+ /* Not valid when txq len is 0 */
+ queue = tx->pkt_info[ch_id][i].queue;
+
+ if (!txq_len) {
+ /* Reserved token */
+ if (i < (NUM_TX_DESCS_PER_AC * NUM_ACS)) {
+ queue = (i % NUM_ACS);
+ start_ac = end_ac = queue;
+ } else {
+ /* Spare token:
+ * Loop through all AC's
+ */
+ start_ac = WLAN_AC_VO;
+ end_ac = WLAN_AC_BK;
+ }
+
+ for (cnt = start_ac; cnt >= end_ac; cnt--) {
+ pkts_pend = uccp420wlan_tx_proc_pend_frms(dev,
+ cnt,
+ ch_id,
+ i);
+ if (pkts_pend) {
+ queue = cnt;
+ break;
+ }
+ }
+
+ if (pkts_pend == 0) {
+ __clear_bit(curr_bit,
+ &tx->buf_pool_bmp[pool_id]);
+ spin_unlock_irqrestore(&tx->lock, flags);
+ continue;
+ }
+ }
+
+ tx->outstanding_tokens[queue]++;
+ spin_unlock_irqrestore(&tx->lock, flags);
+
+ ret = __uccp420wlan_tx_frame(dev,
+ queue,
+ i,
+ ch_id,
+ 0,
+ 0); /* TODO: Currently sending 0
+ since this param is not used
+ as expected in the orig
+ code for multiple frames etc
+ Need to set this
+ properly when the orig code
+ logic is corrected
+ */
+ if (ret < 0) {
+ pr_err("%s: Queueing of TX frame to FW failed\n",
+ __func__);
+ }
+ }
+}
#endif
- int peer_id,
- int token_id)
+
+
+int uccp420wlan_tx_proc_pend_frms(struct mac80211_dev *dev,
+ int ac,
+#ifdef MULTI_CHAN_SUPPORT
+ int curr_chanctx_idx,
+#endif
+ int token_id)
{
struct tx_config *tx = &dev->tx;
unsigned long ampdu_len = 0;
@@ -394,8 +577,22 @@
unsigned int max_tx_cmds = dev->params->max_tx_cmds;
struct sk_buff_head *txq = NULL;
struct sk_buff_head *pend_pkt_q = NULL;
+ unsigned int total_pending_processed = 0;
+ int pend_pkt_q_len = 0;
+ struct curr_peer_info peer_info;
- pend_pkt_q = &tx->pending_pkt[peer_id][queue];
+ peer_info = get_curr_peer_opp(dev,
+#ifdef MULTI_CHAN_SUPPORT
+ curr_chanctx_idx,
+#endif
+ ac);
+
+ /* No pending frames for any peer in that AC.
+ */
+ if (peer_info.id == -1)
+ return 0;
+
+ pend_pkt_q = &tx->pending_pkt[peer_info.op_chan_idx][peer_info.id][ac];
#ifdef MULTI_CHAN_SUPPORT
txq = &dev->tx.pkt_info[curr_chanctx_idx][token_id].pkt;
@@ -405,6 +602,12 @@
skb_first = skb_peek(pend_pkt_q);
+ if (skb_first == NULL)
+ pr_err("%s:%d Null SKB: peer: %d\n",
+ __func__,
+ __LINE__,
+ peer_info.id);
+
mac_hdr_first = (struct ieee80211_hdr *)skb_first->data;
tx_info_first = IEEE80211_SKB_CB(skb_first);
@@ -443,8 +646,18 @@
IEEE80211_TX_MAX_RATES) != 0) ||
(tx_info_first->flags != tx_info->flags) ||
#endif
+ /* RPU has a limitation, it expects A1-A2-A3 to be same
+ * for all MPDU's within an AMPDU. This is a temporary
+ * solution, remove it when RPU has fix for this.
+ */
(memcmp(mac_hdr->addr1,
mac_hdr_first->addr1,
+ ETH_ALEN) != 0) ||
+ (memcmp(mac_hdr->addr2,
+ mac_hdr_first->addr2,
+ ETH_ALEN) != 0) ||
+ (memcmp(mac_hdr->addr3,
+ mac_hdr_first->addr3,
ETH_ALEN) != 0))
break;
@@ -457,115 +670,120 @@
if (!skb_queue_len(txq))
skb_queue_tail(txq, skb_dequeue(pend_pkt_q));
- DEBUG_LOG("%s-UMACTX:Max_pkt_thresh: send spare: %d with %d\n",
+ total_pending_processed = skb_queue_len(txq);
+
+ pend_pkt_q_len = skb_queue_len(pend_pkt_q);
+ if ((ac != WLAN_AC_BCN) &&
+ (tx->queue_stopped_bmp & (1 << ac)) &&
+ pend_pkt_q_len < (MAX_TX_QUEUE_LEN / 2)) {
+ ieee80211_wake_queue(dev->hw, tx_queue_unmap(ac));
+ tx->queue_stopped_bmp &= ~(1 << (ac));
+ }
+
+ DEBUG_LOG("%s-UMACTX: token_id: %d total_pending_packets_process: %d\n",
dev->name,
token_id,
skb_queue_len(txq));
+
+ return total_pending_processed;
}
-int uccp420wlan_tx_alloc_buff_req(struct mac80211_dev *dev,
- int queue,
+int uccp420wlan_tx_alloc_token(struct mac80211_dev *dev,
+ int ac,
#ifdef MULTI_CHAN_SUPPORT
- struct umac_vif *uvif,
- int curr_chanctx_idx,
+ int off_chanctx_idx,
+ int curr_chanctx_idx,
#endif
- int peer_id,
- struct sk_buff *skb)
+ int peer_id,
+ struct sk_buff *skb)
{
int token_id = NUM_TX_DESCS;
struct tx_config *tx = &dev->tx;
- struct sk_buff_head *txq = NULL;
- unsigned long flags = 0;
+ unsigned long flags;
struct sk_buff_head *pend_pkt_q = NULL;
- int tx_peer_id = 0;
- struct ieee80211_hdr *mac_hdr = NULL;
+ unsigned int pkts_pend = 0;
spin_lock_irqsave(&tx->lock, flags);
- pend_pkt_q = &tx->pending_pkt[peer_id][queue];
+ pend_pkt_q = &tx->pending_pkt[off_chanctx_idx][peer_id][ac];
- DEBUG_LOG("%s-UMACTX:Alloc buf Req q = %d,\n", dev->name, queue);
+ DEBUG_LOG("%s-UMACTX:Alloc buf Req q = %d off_chan: %d peerid: %d,\n",
+ dev->name,
+ ac,
+ off_chanctx_idx,
+ peer_id);
-#ifdef MULTI_CHAN_SUPPORT
- if (uvif->chanctx->index == curr_chanctx_idx)
- token_id = get_token(dev,
- curr_chanctx_idx,
- queue);
-#else
- token_id = get_token(dev,
- queue);
-#endif
+ /* Queue the frame to the pending frames queue */
+ skb_queue_tail(pend_pkt_q, skb);
+ /* If the number of outstanding Tx tokens is greater than
+ * NUM_TX_DESCS_PER_AC we try and encourage aggregation to the max size
+ * supported (dev->params->max_tx_cmds)
+ */
+ if (tx->outstanding_tokens[ac] >= NUM_TX_DESCS_PER_AC) {
+ if ((skb_queue_len(pend_pkt_q) < dev->params->max_tx_cmds) ||
+ ac == WLAN_AC_BCN)
+ goto out;
+ }
- /* If we got a reserved token, then queue frame to the Xmit queue */
- if (token_id < NUM_TX_DESCS_PER_AC * NUM_ACS) {
- DEBUG_LOG("%s-UMACTX:Reserved Token, Sending single\n",
- dev->name);
-#ifdef MULTI_CHAN_SUPPORT
- txq = &dev->tx.pkt_info[curr_chanctx_idx][token_id].pkt;
-#else
- txq = &dev->tx.pkt_info[token_id].pkt;
-#endif
- skb_queue_tail(txq, skb);
- } else {
- /* A frame for a beacon queue should never get a reserved
- * token
- */
- mac_hdr = (struct ieee80211_hdr *)(skb->data);
-
- if ((queue == WLAN_AC_BCN) &&
- (ieee80211_is_beacon(mac_hdr->frame_control))) {
- if (net_ratelimit())
- pr_warn("Did not get rsvd token for beacon\n");
- }
-
- /* Queue the frame to the pending frames queue */
- skb_queue_tail(pend_pkt_q, skb);
-
- if (queue != WLAN_AC_BCN) {
- /* Take steps to stop the TX traffic if we have reached
- * the queueing limit
- */
- if (skb_queue_len(pend_pkt_q) >= MAX_TX_QUEUE_LEN) {
- ieee80211_stop_queue(dev->hw,
- skb->queue_mapping);
- tx->queue_stopped_bmp |= (1 << queue);
- }
-
- /* If we got a spare token, try sending out pending
- * frames
- */
- if (token_id < NUM_TX_DESCS) {
- tx_peer_id = get_curr_peer_opp(dev,
-#ifdef MULTI_CHAN_SUPPORT
- curr_chanctx_idx,
-#endif
- queue);
-
- uccp420wlan_tx_proc_pend_frms(dev,
- queue,
-#ifdef MULTI_CHAN_SUPPORT
- curr_chanctx_idx,
-#endif
- tx_peer_id,
- token_id);
- }
+ /* Take steps to stop the TX traffic if we have reached
+ * the queueing limit.
+ * We dont this for the ROC queue to avoid the case where we are in the
+ * OFF channel but there is lot of traffic for the operating channel on
+ * the shared ROC queue (which is VO right now), since this would block
+ * ROC traffic too.
+ */
+ if (skb_queue_len(pend_pkt_q) >= MAX_TX_QUEUE_LEN) {
+ if ((!dev->roc_params.roc_in_progress) ||
+ (dev->roc_params.roc_in_progress &&
+ (ac != UMAC_ROC_AC))) {
+ ieee80211_stop_queue(dev->hw,
+ skb->queue_mapping);
+ tx->queue_stopped_bmp |= (1 << ac);
}
}
- DEBUG_LOG("%s-UMACTX:Alloc buf Result *id = %d q = %d peer_id = %d\n",
+ token_id = get_token(dev,
+#ifdef MULTI_CHAN_SUPPORT
+ curr_chanctx_idx,
+#endif
+ ac);
+
+ DEBUG_LOG("%s-UMACTX:Alloc buf Result *id= %d q = %d peerid: %d,\n",
dev->name,
- token_id
- queue,
+ token_id,
+ ac,
peer_id);
+ if (token_id == NUM_TX_DESCS)
+ goto out;
+
+ pkts_pend = uccp420wlan_tx_proc_pend_frms(dev,
+ ac,
+#ifdef MULTI_CHAN_SUPPORT
+ curr_chanctx_idx,
+#endif
+ token_id);
+
+ /* We have just added a frame to pending_q but channel context is
+ * mismatch.
+ */
+
+ if (!pkts_pend) {
+ free_token(dev, token_id, ac);
+ token_id = NUM_TX_DESCS;
+ }
+
+out:
spin_unlock_irqrestore(&tx->lock, flags);
+ DEBUG_LOG("%s-UMACTX:Alloc buf Result *id= %d\n", dev->name, token_id);
/* If token is available, just return tokenid, list will be sent*/
return token_id;
}
+
int get_band_chanctx(struct mac80211_dev *dev, struct umac_vif *uvif)
{
struct ieee80211_chanctx_conf *chanctx = NULL;
@@ -586,9 +804,10 @@
return band;
}
+
int uccp420wlan_tx_free_buff_req(struct mac80211_dev *dev,
struct umac_event_tx_done *tx_done,
- unsigned char *queue,
+ unsigned char *ac,
#ifdef MULTI_CHAN_SUPPORT
int curr_chanctx_idx,
#endif
@@ -606,18 +825,15 @@
int vif_index = -1;
unsigned int pkt = 0;
int cnt = 0;
- int bit = 0;
- int pool_id = 0;
unsigned int desc_id = tx_done->descriptor_id;
- unsigned int max_tx_cmds = dev->params->max_tx_cmds;
struct umac_vif *uvif = NULL;
struct ieee80211_vif *ivif = NULL;
unsigned long bcn_int = 0;
- int pend_pkt_q_len = 0;
- int peer_id = 0;
#ifdef MULTI_CHAN_SUPPORT
int chanctx_idx = 0;
+ struct tx_pkt_info *pkt_info = NULL;
#endif
+ int start_ac, end_ac;
skb_queue_head_init(&tx_done_list);
@@ -628,67 +844,17 @@
spin_lock_irqsave(&tx->lock, flags);
- tx->outstanding_tokens[tx_done->queue]--;
-
#ifdef MULTI_CHAN_SUPPORT
chanctx_idx = tx->desc_chan_map[desc_id];
-#endif
-
- bit = (desc_id % TX_DESC_BUCKET_BOUND);
- pool_id = (desc_id / TX_DESC_BUCKET_BOUND);
-
- /* Reserved token */
- if (desc_id < (NUM_TX_DESCS_PER_AC * NUM_ACS)) {
- *queue = tx_done->queue;
-
- peer_id = get_curr_peer_opp(dev,
-#ifdef MULTI_CHAN_SUPPORT
- curr_chanctx_idx,
-#endif
- *queue);
-
- if (peer_id == -1) {
- __clear_bit(bit, &tx->buf_pool_bmp[pool_id]);
-#ifdef MULTI_CHAN_SUPPORT
- tx->desc_chan_map[desc_id] = -1;
-#endif
- }
- /* Spare token */
- } else {
- for (cnt = WLAN_AC_VO; cnt >= 0; cnt--) {
- peer_id = get_curr_peer_opp(dev,
-#ifdef MULTI_CHAN_SUPPORT
- curr_chanctx_idx,
-#endif
- cnt);
-
- if (peer_id != -1) {
- *queue = cnt;
- break;
- }
- }
-
- /* If beacon queue has pending and no other AC
- * has pending
- */
- if (peer_id == -1) {
- __clear_bit(bit, &tx->buf_pool_bmp[pool_id]);
-#ifdef MULTI_CHAN_SUPPORT
- tx->desc_chan_map[desc_id] = -1;
-#endif
- }
+ if (chanctx_idx == -1) {
+ spin_unlock_irqrestore(&tx->lock, flags);
+ pr_err("%s: Unexpected channel context\n", __func__);
+ goto out;
}
+ pkt_info = &dev->tx.pkt_info[chanctx_idx][desc_id];
+#endif
- if (peer_id != -1)
- pkts_pend = skb_queue_len(&tx->pending_pkt[peer_id][*queue]);
- DEBUG_LOG("%s-UMACTX:%s pend_q = %d, sta_id = %d desc_id: %d pend:%d\n",
- dev->name,
- __func__,
- *queue,
- peer_id,
- desc_id,
- pkts_pend);
/* Defer Tx Done Processsing */
#ifdef MULTI_CHAN_SUPPORT
@@ -705,48 +871,28 @@
dev->name, skb_list);
}
- if (pkts_pend > 0) {
- /* For a beacon queue we will process the frames irrespective
- * of the current channel context. The FW will take care of
- * transmitting them in the appropriate channel. Hence pass the
- * interfaces channel context instead of the actual current
- * channel context.
+ /* Reserved token */
+ if (desc_id < (NUM_TX_DESCS_PER_AC * NUM_ACS)) {
+ start_ac = end_ac = tx_done->queue;
+ } else {
+ /* Spare token:
+ * Loop through all AC's
*/
- if (*queue == WLAN_AC_BCN) {
- rcu_read_lock();
-
- vif_index = (peer_id - MAX_PEERS);
- ivif = rcu_dereference(dev->vifs[vif_index]);
- uvif = (struct umac_vif *)(ivif->drv_priv);
- curr_chanctx_idx = uvif->chanctx->index;
- rcu_read_unlock();
- }
-
- uccp420wlan_tx_proc_pend_frms(dev,
- *queue,
+ start_ac = WLAN_AC_VO;
+ end_ac = WLAN_AC_BK;
+ }
+ for (cnt = start_ac; cnt >= end_ac; cnt--) {
+ pkts_pend = uccp420wlan_tx_proc_pend_frms(dev,
+ cnt,
#ifdef MULTI_CHAN_SUPPORT
curr_chanctx_idx,
#endif
- peer_id,
desc_id);
- tx->outstanding_tokens[*queue]++;
-
- DEBUG_LOG("%s-UMACTX:Pending packets: %d, Total: %d\n",
- dev->name,
- pkts_pend,
- skb_queue_len(skb_list));
- } else {
- DEBUG_LOG("%s-UMACTX:No Pending Packets\n", dev->name);
- }
-
- pend_pkt_q_len = skb_queue_len(&tx->pending_pkt[peer_id][*queue]);
-
- if ((*queue != WLAN_AC_BCN) &&
- (tx->queue_stopped_bmp & (1 << *queue)) &&
- pend_pkt_q_len < (MAX_TX_QUEUE_LEN / 2)) {
- ieee80211_wake_queue(dev->hw, tx_queue_unmap(*queue));
- tx->queue_stopped_bmp &= ~(1 << (*queue));
+ if (pkts_pend) {
+ *ac = cnt;
+ break;
+ }
}
/* Unmap here before release lock to avoid race */
@@ -879,35 +1025,81 @@
pkt++;
}
out:
- return min(pkts_pend, max_tx_cmds);
+ return pkts_pend;
}
#ifdef MULTI_CHAN_SUPPORT
-void uccp420wlan_proc_tx_discard_chsw(struct mac80211_dev *dev,
- int curr_chanctx_idx,
- struct umac_event_tx_done *tx_done)
+void uccp420wlan_proc_ch_sw_event(struct umac_event_ch_switch *ch_sw_info,
+ void *context)
+{
+ struct mac80211_dev *dev = NULL;
+ int chan = 0;
+ int curr_freq = 0;
+ int chan_id = 0;
+ struct ieee80211_chanctx_conf *curr_chanctx = NULL;
+ int i = 0;
+
+ if (!ch_sw_info || !context) {
+ pr_err("%s: Invalid Parameters:\n", __func__);
+ return;
+ }
+
+ dev = (struct mac80211_dev *)context;
+ chan = ch_sw_info->chan;
+
+ rcu_read_lock();
+
+ for (i = 0; i < MAX_CHANCTX; i++) {
+ curr_chanctx = rcu_dereference(dev->chanctx[i]);
+
+ if (curr_chanctx) {
+ curr_freq = curr_chanctx->def.chan->center_freq;
+
+ if (ieee80211_frequency_to_channel(curr_freq) == chan) {
+ chan_id = i;
+ break;
+ }
+ }
+ }
+
+ rcu_read_unlock();
+
+ if (i == MAX_CHANCTX) {
+ pr_err("%s: Invalid Channel Context: chan: %d\n",
+ __func__,
+ chan);
+ return;
+ }
+
+ /* Switch to the new channel context */
+ spin_lock(&dev->chanctx_lock);
+ dev->curr_chanctx_idx = chan_id;
+ spin_unlock(&dev->chanctx_lock);
+
+ /* We now try to xmit any frames whose xmission got cancelled due to a
+ * previous channel switch
+ */
+ uccp420wlan_tx_proc_send_pend_frms_all(dev, chan_id);
+}
+
+
+unsigned int uccp420wlan_proc_tx_dscrd_chsw(struct mac80211_dev *dev,
+ int curr_chanctx_idx,
+ struct umac_event_tx_done *tx_done)
{
struct tx_config *tx = &dev->tx;
- struct sk_buff_head *txq = NULL;
- struct sk_buff_head tx_done_list;
+ struct sk_buff_head *txq = NULL, tx_done_list;
int chanctx_idx = -1;
int pkt = 0;
-#ifdef notyet
- int i = 0;
-#endif
unsigned long flags;
int txq_len = 0;
struct sk_buff *skb = NULL;
struct sk_buff *skb_first = NULL;
struct sk_buff *tmp = NULL;
- int curr_bit = 0;
- int pool_id = 0;
int queue = 0;
- int ret = 0;
+ int ret = 0, cnt = 0;
unsigned int desc_id = 0;
- int peer_id = -1;
- int ac = -1;
unsigned int *curr_retries = NULL;
unsigned int max_retries = 0;
struct ieee80211_tx_info tx_info_1st_mpdu;
@@ -915,6 +1107,8 @@
bool retries_exceeded = false;
unsigned int *rate = NULL;
unsigned int *retries = NULL;
+ int start_ac, end_ac;
+ unsigned int pkts_pend = 0;
skb_queue_head_init(&tx_done_list);
@@ -928,8 +1122,11 @@
*/
chanctx_idx = tx->desc_chan_map[desc_id];
- if (chanctx_idx == -1) {
- pr_err("%s: Unexpected channel context\n", __func__);
+ if ((chanctx_idx == -1) ||
+ (chanctx_idx > (MAX_CHANCTX + MAX_OFF_CHANCTX))) {
+ pr_err("%s: Unexpected channel context: %d\n",
+ __func__,
+ chanctx_idx);
goto out;
}
@@ -937,7 +1134,10 @@
txq_len = skb_queue_len(txq);
if (!txq_len) {
- pr_err("%s: TX_DONE received for empty queue\n", __func__);
+ pr_err("%s: TX_DONE received for empty queue: chan: %d desc_id: %d\n",
+ __func__,
+ chanctx_idx,
+ desc_id);
goto out;
}
@@ -951,34 +1151,25 @@
pkt = 0;
skb_first = skb_peek(txq);
+
+ if (!skb_first) {
+ pr_err("%s: Empty txq: chan: %d desc_id: %d\n",
+ __func__,
+ chanctx_idx,
+ desc_id);
+ goto out;
+ }
+
curr_retries = &tx->pkt_info[chanctx_idx][desc_id].curr_retries;
max_retries = tx->pkt_info[chanctx_idx][desc_id].max_retries;
retries = tx->pkt_info[chanctx_idx][desc_id].retries;
rate = tx->pkt_info[chanctx_idx][desc_id].rate;
tx->pkt_info[chanctx_idx][desc_id].adjusted_rates = true;
- if ((tx_done->retries_num[0] + *curr_retries) > max_retries) {
+ if ((tx_done->retries_num[0] + *curr_retries) > max_retries)
retries_exceeded = true;
- } else {
+ else
*curr_retries += tx_done->retries_num[0];
-#ifdef notyet
- /* Adjust the counters here */
- for (i = 0; i < 4; i++) {
- if (tx_done->rate[0] != rate[i])
- retries[i] = 0;
- else
- retries[i] -= tx_done->retries_num[0];
-
- DEBUG_LOG("%s-UMACTX: %s: %d %s %d == %d retries\n",
- dev->name,
- __func__,
- __LINE__,
- "adjusted indices are",
- i,
- retries[i]);
- }
-#endif
- }
memcpy(&tx_info_1st_mpdu,
(struct ieee80211_tx_info *)IEEE80211_SKB_CB(skb_first),
@@ -1026,79 +1217,69 @@
pkt++;
}
- if (chanctx_idx != curr_chanctx_idx) {
- /* First check if there is a packet in the txq of the current
- * chanctx that needs to be transmitted
+ /* First check if there is a packet in the txq of the current
+ * chanctx that needs to be transmitted
+ */
+ txq = &tx->pkt_info[curr_chanctx_idx][desc_id].pkt;
+ txq_len = skb_queue_len(txq);
+ queue = tx->pkt_info[curr_chanctx_idx][desc_id].queue;
+ pkts_pend = txq_len;
+
+ if (txq_len) {
+ spin_unlock_irqrestore(&tx->lock, flags);
+
+ /* TODO: Currently sending 0 since this param is not
+ * used as expected in the orig code for multiple
+ * frames etc Need to set this properly when the orig
+ * code logic is corrected
*/
- txq = &tx->pkt_info[curr_chanctx_idx][desc_id].pkt;
- txq_len = skb_queue_len(txq);
- queue = tx->pkt_info[curr_chanctx_idx][desc_id].queue;
-
- if (txq_len) {
- spin_unlock_irqrestore(&tx->lock, flags);
-
- /* TODO: Currently sending 0 since this param is not
- * used as expected in the orig code for multiple
- * frames etc Need to set this properly when the orig
- * code logic is corrected
+ ret = __uccp420wlan_tx_frame(dev,
+ queue,
+ desc_id,
+ curr_chanctx_idx,
+ 0,
+ 1);
+ if (ret < 0) {
+ /* TODO: Check if we need to clear the TX bitmap
+ * and desc_chan_map here
*/
- ret = __uccp420wlan_tx_frame(dev,
- queue,
- desc_id,
- curr_chanctx_idx,
- 0,
- 1);
- if (ret < 0) {
- /* TODO: Check if we need to clear the TX bitmap
- * and desc_chan_map here
- */
- pr_err("%s: Queueing of TX frame to FW failed\n",
- __func__);
- } else {
- spin_lock_irqsave(&tx->lock, flags);
- tx->desc_chan_map[desc_id] = curr_chanctx_idx;
- spin_unlock_irqrestore(&tx->lock, flags);
- }
+ pr_err("%s: Queueing of TX frame to FW failed\n",
+ __func__);
+ }
- goto tx_done;
+ /* This is needed to avoid freeing up the token
+ */
+ pkts_pend = 1;
+
+ goto tx_done;
+ } else {
+ /* Check pending queue */
+ /* Reserved token */
+ if (desc_id < (NUM_TX_DESCS_PER_AC * NUM_ACS)) {
+ queue = (desc_id % NUM_ACS);
+ start_ac = end_ac = queue;
} else {
- /* Check pending queue */
- /* Reserved token */
- if (desc_id < (NUM_TX_DESCS_PER_AC * NUM_ACS)) {
- queue = (desc_id % NUM_ACS);
+ /* Spare token:
+ * Loop through all AC's
+ */
+ start_ac = WLAN_AC_VO;
+ end_ac = WLAN_AC_BK;
+ }
- peer_id = get_curr_peer_opp(dev,
- curr_chanctx_idx,
- queue);
-
- if (peer_id == -1)
- goto done;
-
- /* Spare token */
- } else {
- for (ac = WLAN_AC_VO; ac >= 0; ac--) {
- peer_id = get_curr_peer_opp(dev,
- curr_chanctx_idx,
- ac);
-
- if (peer_id != -1) {
- queue = ac;
- break;
- }
- }
-
- if (ac < 0)
- goto done;
- }
-
- uccp420wlan_tx_proc_pend_frms(dev,
- queue,
+ for (cnt = start_ac; cnt >= end_ac; cnt--) {
+ pkts_pend = uccp420wlan_tx_proc_pend_frms(dev,
+ cnt,
curr_chanctx_idx,
- peer_id,
desc_id);
+ if (pkts_pend) {
+ queue = cnt;
+ break;
+ }
+ }
- spin_unlock_irqrestore(&tx->lock, flags);
+ spin_unlock_irqrestore(&tx->lock, flags);
+ if (pkts_pend > 0) {
/* TODO: Currently sending 0 since this param is not
* used as expected in the orig code for multiple
* frames etc. Need to set this properly when the orig
@@ -1112,32 +1293,13 @@
0);
if (ret < 0) {
- /* SDK: Check if we need to clear the TX bitmap
- * and desc_chan_map here
- */
pr_err("%s: Queueing of TX frame to FW failed\n",
__func__);
- } else {
- spin_lock_irqsave(&tx->lock, flags);
- tx->desc_chan_map[desc_id] = curr_chanctx_idx;
- spin_unlock_irqrestore(&tx->lock, flags);
}
-
- goto tx_done;
}
+ goto tx_done;
}
-done:
- curr_bit = (desc_id % TX_DESC_BUCKET_BOUND);
- pool_id = (desc_id / TX_DESC_BUCKET_BOUND);
-
- /* Mark the token as available */
- __clear_bit(curr_bit, &tx->buf_pool_bmp[pool_id]);
-
- tx->desc_chan_map[desc_id] = -1;
-
- tx->outstanding_tokens[tx_done->queue]--;
-
if (txq_len == 1)
dev->stats->tx_cmd_send_count_single--;
else
@@ -1146,6 +1308,8 @@
out:
spin_unlock_irqrestore(&tx->lock, flags);
+ return pkts_pend;
+
tx_done:
skb_queue_walk_safe(&tx_done_list, skb, tmp) {
tx_status(skb,
@@ -1154,6 +1318,8 @@
dev,
tx_info_1st_mpdu);
}
+
+ return pkts_pend;
}
#endif
@@ -1187,6 +1353,7 @@
{
int i = 0;
int j = 0;
+ int k = 0;
struct tx_config *tx = &dev->tx;
memset(&tx->buf_pool_bmp,
@@ -1197,8 +1364,10 @@
tx->next_spare_token_ac = WLAN_AC_BE;
for (i = 0; i < NUM_ACS; i++) {
- for (j = 0; j < MAX_PEND_Q_PER_AC; j++)
- skb_queue_head_init(&tx->pending_pkt[j][i]);
+ for (j = 0; j < MAX_PEND_Q_PER_AC; j++) {
+ for (k = 0; k < MAX_UMAC_VIF_CHANCTX_TYPES; k++)
+ skb_queue_head_init(&tx->pending_pkt[k][j][i]);
+ }
tx->outstanding_tokens[i] = 0;
}
@@ -1207,7 +1376,7 @@
#ifdef MULTI_CHAN_SUPPORT
tx->desc_chan_map[i] = -1;
- for (j = 0; j < MAX_CHANCTX; j++)
+ for (j = 0; j < MAX_CHANCTX + MAX_OFF_CHANCTX ; j++)
skb_queue_head_init(&tx->pkt_info[j][i].pkt);
#else
skb_queue_head_init(&tx->pkt_info[i].pkt);
@@ -1228,10 +1397,12 @@
tx->persec_timer.function = print_persec_stats;
mod_timer(&tx->persec_timer, jiffies + msecs_to_jiffies(1000));
#endif
+ dev->curr_chanctx_idx = -1;
spin_lock_init(&tx->lock);
ieee80211_wake_queues(dev->hw);
- DEBUG_LOG("%s-UMACTX:Initialization successful\n", dev->name);
+ DEBUG_LOG("%s-UMACTX: initialization successful\n",
+ UMACTX_TO_MACDEV(tx)->name);
}
@@ -1239,9 +1410,12 @@
{
int i = 0;
int j = 0;
- unsigned long flags = 0;
+ int k = 0;
+ unsigned long flags;
struct tx_config *tx = &dev->tx;
- struct sk_buff *skb;
+ struct sk_buff *skb = NULL;
+ unsigned int qlen = 0;
+ struct sk_buff_head *pend_q = NULL;
ieee80211_stop_queues(dev->hw);
@@ -1251,10 +1425,17 @@
for (i = 0; i < NUM_TX_DESCS; i++) {
#ifdef MULTI_CHAN_SUPPORT
- for (j = 0; j < MAX_CHANCTX; j++)
- while ((skb = skb_dequeue(&tx->pkt_info[j][i].pkt)) !=
- NULL)
- dev_kfree_skb_any(skb);
+ for (j = 0; j < MAX_CHANCTX + MAX_OFF_CHANCTX; j++) {
+ qlen = skb_queue_len(&tx->pkt_info[j][i].pkt);
+
+ if (qlen) {
+ while ((skb =
+ skb_dequeue(&tx->pkt_info[j][i].pkt)) !=
+ NULL) {
+ dev_kfree_skb_any(skb);
+ }
+ }
+ }
#else
while ((skb = skb_dequeue(&tx->pkt_info[i].pkt)) != NULL)
dev_kfree_skb_any(skb);
@@ -1263,16 +1444,19 @@
for (i = 0; i < NUM_ACS; i++) {
for (j = 0; j < MAX_PEND_Q_PER_AC; j++) {
- while ((skb =
- skb_dequeue(&tx->pending_pkt[j][i])) !=
- NULL)
- dev_kfree_skb_any(skb);
+ for (k = 0; k < MAX_UMAC_VIF_CHANCTX_TYPES; k++) {
+ pend_q = &tx->pending_pkt[k][j][i];
+
+ while ((skb = skb_dequeue(pend_q)) != NULL)
+ dev_kfree_skb_any(skb);
+ }
}
}
spin_unlock_irqrestore(&tx->lock, flags);
- DEBUG_LOG("%s-UMACTX:Deinitialization successful\n", dev->name);
+ DEBUG_LOG("%s-UMACTX: deinitialization successful\n",
+ UMACTX_TO_MACDEV(tx)->name);
}
@@ -1304,6 +1488,7 @@
tx_done.descriptor_id = token_id;
tx_done.queue = queue;
+ dev->tx.desc_chan_map[token_id] = curr_chanctx_idx;
#ifdef MULTI_CHAN_SUPPORT
txq = &dev->tx.pkt_info[curr_chanctx_idx][token_id].pkt;
@@ -1344,6 +1529,9 @@
struct umac_vif *uvif = NULL;
struct umac_sta *usta = NULL;
int peer_id = -1;
+#ifdef MULTI_CHAN_SUPPORT
+ int off_chanctx_idx;
+#endif
uvif = (struct umac_vif *)(tx_info->control.vif->drv_priv);
@@ -1357,21 +1545,27 @@
if (bcast == false) {
queue = tx_queue_map(skb->queue_mapping);
more_frames = 0;
+ dev->stats->tx_cmds_from_stack++;
} else {
queue = WLAN_AC_BCN;
/* Hack: skb->priority is used to indicate more frames */
more_frames = skb->priority;
}
- dev->stats->tx_cmds_from_stack++;
if (dev->params->production_test == 1)
tx_info->flags |= IEEE80211_TX_CTL_AMPDU;
- if (tx_info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
- /* These are high priority frames, send them in VO */
- queue = WLAN_AC_VO;
+ if ((tx_info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) ||
+ (uvif->chanctx &&
+ uvif->chanctx->index == dev->roc_off_chanctx_idx)) {
atomic_inc(&dev->roc_params.roc_mgmt_tx_count);
+ off_chanctx_idx = UMAC_VIF_CHANCTX_TYPE_OFF;
+ DEBUG_LOG("%s-UMACTX: Sending OFFCHAN Frame: %d\n",
+ dev->name,
+ atomic_read(&dev->roc_params.roc_mgmt_tx_count));
+ } else {
+ off_chanctx_idx = UMAC_VIF_CHANCTX_TYPE_OPER;
}
mac_hdr = (struct ieee80211_hdr *)(skb->data);
@@ -1385,10 +1579,10 @@
skb->queue_mapping,
ieee80211_is_beacon(mac_hdr->frame_control));
- token_id = uccp420wlan_tx_alloc_buff_req(dev,
+ token_id = uccp420wlan_tx_alloc_token(dev,
queue,
#ifdef MULTI_CHAN_SUPPORT
- uvif,
+ off_chanctx_idx,
curr_chanctx_idx,
#endif
peer_id,
@@ -1465,19 +1659,23 @@
qlen = skb_queue_len(&dev->tx.pkt_info[token_id].pkt);
#endif
- DEBUG_LOG("%s-UMACTX:TX Done Rx for desc_id: %d qlen: %d\n",
+ DEBUG_LOG("%s-UMACTX:TX Done Rx for desc_id: %d Q: %d qlen: %d ",
dev->name,
tx_done->descriptor_id,
- qlen);
+ tx_done->queue, qlen);
+ DEBUG_LOG("status: %d chactx: %d out_tok: %d\n",
+ tx_done->frm_status[0],
+ curr_chanctx_idx,
+ dev->tx.outstanding_tokens[tx_done->queue]);
update_aux_adc_voltage(dev, tx_done->pdout_voltage);
#ifdef MULTI_CHAN_SUPPORT
if (tx_done->frm_status[0] == TX_DONE_STAT_DISCARD_CHSW) {
- uccp420wlan_proc_tx_discard_chsw(dev,
- curr_chanctx_idx,
- tx_done);
- return;
+ pkts_pending = uccp420wlan_proc_tx_dscrd_chsw(dev,
+ curr_chanctx_idx,
+ tx_done);
+ goto out;
}
#endif
pkts_pending = uccp420wlan_tx_free_buff_req(dev,
@@ -1490,12 +1688,6 @@
if (pkts_pending) {
/*TODO..Do we need to check each skb for more_frames??*/
-#if 0
- if ((queue == WLAN_AC_BCN) && (skb->priority == 1))
- more_frames = 1;
- else
- more_frames = 0;
-#endif
more_frames = 0;
DEBUG_LOG("%s-UMACTX:%s:%d Transfer Pending Frames:\n",
@@ -1515,6 +1707,13 @@
DEBUG_LOG("%s-UMACTX:No Pending Packets\n", dev->name);
}
+out:
+ if (!pkts_pending) {
+ /* Mark the token as available */
+ free_token(dev, token_id, tx_done->queue);
+ dev->tx.desc_chan_map[token_id] = -1;
+ }
+
for (vif_index = 0; vif_index < MAX_VIFS; vif_index++) {
if (vif_index_bitmap & (1 << vif_index)) {
memset(&noa_event, 0, sizeof(noa_event));
@@ -1526,3 +1725,191 @@
}
}
}
+
+
+static int uccp420_flush_vif_all_pend_q(struct mac80211_dev *dev,
+ struct umac_vif *uvif,
+ unsigned int hw_queue_map,
+ enum UMAC_VIF_CHANCTX_TYPE chanctx_type)
+{
+ unsigned int pending = 0;
+ int count = 0;
+ int peer_id = -1;
+ unsigned int queue = 0;
+ int pend_q = 0;
+ unsigned long flags;
+ struct sk_buff_head *pend_pkt_q = NULL;
+ struct tx_config *tx = NULL;
+ struct ieee80211_sta *sta = NULL;
+ struct umac_sta *usta = NULL;
+
+ tx = &dev->tx;
+
+ if (!uvif->chanctx) {
+ DEBUG_LOG("%s-UMACTX: Chanctx NULL, returning\n", dev->name);
+ return -1;
+ }
+
+ for (queue = 0; queue < NUM_ACS; queue++) {
+ if (!(BIT(queue) & hw_queue_map))
+ continue;
+
+ for (pend_q = 0; pend_q < MAX_PEND_Q_PER_AC; pend_q++) {
+ if (pend_q < MAX_PEERS) {
+ rcu_read_lock();
+ sta = rcu_dereference(dev->peers[pend_q]);
+
+ if (!sta) {
+ rcu_read_unlock();
+ continue;
+ }
+
+ usta = (struct umac_sta *)(sta->drv_priv);
+
+ if (usta->vif_index == uvif->vif_index)
+ peer_id = pend_q;
+ else {
+ rcu_read_unlock();
+ continue;
+ }
+
+ rcu_read_unlock();
+ } else if (pend_q == uvif->vif_index)
+ peer_id = uvif->vif_index;
+ else
+ continue;
+
+ while (1) {
+ spin_lock_irqsave(&tx->lock, flags);
+
+ pend_pkt_q =
+ &tx->pending_pkt[chanctx_type]
+ [peer_id]
+ [queue];
+
+ /* Assuming all packets for the peer have same
+ * channel context
+ */
+ pending = skb_queue_len(pend_pkt_q);
+
+ spin_unlock_irqrestore(&tx->lock, flags);
+
+ if (!pending)
+ break;
+
+ if (count >= QUEUE_FLUSH_TIMEOUT_TICKS)
+ break;
+
+ current->state = TASK_INTERRUPTIBLE;
+
+ if (0 == schedule_timeout(1))
+ count++;
+
+ }
+
+ if (pending) {
+ pr_err("%s-UMACTX: Failed for VIF: %d and Queue: %d, pending: %d\n",
+ dev->name,
+ uvif->vif_index,
+ queue,
+ pending);
+
+ return -1;
+ }
+ }
+ }
+
+ DEBUG_LOG("%s-UMACTX: Success for VIF: %d and Queue: %d\n",
+ dev->name,
+ uvif->vif_index,
+ queue);
+ return 0;
+}
+
+
+static int uccp420_flush_vif_tx_queues(struct mac80211_dev *dev,
+ struct umac_vif *uvif,
+ int chanctx_idx,
+ unsigned int hw_queue_map)
+{
+ unsigned int tokens = 0;
+ unsigned int i = 0;
+ unsigned long buf_pool_bmp = 0;
+ unsigned long flags;
+ struct tx_pkt_info *pkt_info = NULL;
+ struct tx_config *tx = NULL;
+ int count = 0;
+
+ tx = &dev->tx;
+
+ spin_lock_irqsave(&tx->lock, flags);
+
+ for (i = 0; i < NUM_TX_DESCS; i++) {
+ pkt_info = &tx->pkt_info[chanctx_idx][i];
+
+ if ((pkt_info->vif_index == uvif->vif_index) &&
+ (BIT(pkt_info->queue) & hw_queue_map))
+ tokens |= BIT(i);
+ }
+
+ spin_unlock_irqrestore(&tx->lock, flags);
+
+ if (!tokens)
+ return 0;
+
+ while (1) {
+ spin_lock_irqsave(&tx->lock, flags);
+ buf_pool_bmp = tx->buf_pool_bmp[0];
+ spin_unlock_irqrestore(&tx->lock, flags);
+
+ if (!(buf_pool_bmp & tokens))
+ break;
+
+ if (count >= QUEUE_FLUSH_TIMEOUT_TICKS)
+ break;
+
+ current->state = TASK_INTERRUPTIBLE;
+
+ if (0 == schedule_timeout(1))
+ count++;
+ }
+
+ if (buf_pool_bmp & tokens) {
+ pr_err("%s-UMACTX: Failed for VIF: %d, buf_pool_bmp : 0x%lx\n",
+ dev->name,
+ uvif->vif_index,
+ buf_pool_bmp);
+
+ return -1;
+ }
+
+ DEBUG_LOG("%s-UMACTX: Success for VIF: %d, buf_pool_bmp : 0x%lx\n",
+ dev->name,
+ uvif->vif_index,
+ buf_pool_bmp);
+ return 0;
+}
+
+
+int uccp420_flush_vif_queues(struct mac80211_dev *dev,
+ struct umac_vif *uvif,
+ int chanctx_idx,
+ unsigned int hw_queue_map,
+ enum UMAC_VIF_CHANCTX_TYPE vif_chanctx_type)
+{
+ int result = -1;
+
+ result = uccp420_flush_vif_all_pend_q(dev,
+ uvif,
+ hw_queue_map,
+ vif_chanctx_type);
+
+ if (result == 0) {
+ result = uccp420_flush_vif_tx_queues(dev,
+ uvif,
+ chanctx_idx,
+ hw_queue_map);
+ }
+
+ return result;
+}
diff --git a/drivers/net/wireless/uccp420wlan/src/umac_if.c b/drivers/net/wireless/uccp420wlan/src/umac_if.c
index f37aa30..74bbffd 100644
--- a/drivers/net/wireless/uccp420wlan/src/umac_if.c
+++ b/drivers/net/wireless/uccp420wlan/src/umac_if.c
@@ -581,7 +581,8 @@
reset.lmac_mode = lmac_mode;
reset.antenna_sel = dev->params->antenna_sel;
- if (dev->params->production_test == 0) {
+ if (dev->params->production_test == 0 &&
+ dev->params->bypass_vpd == 0) {
memcpy(reset.rf_params, dev->params->rf_params_vpd,
RF_PARAMS_SIZE);
} else {
@@ -839,15 +840,17 @@
int uccp420wlan_prog_roc(unsigned int roc_ctrl,
unsigned int roc_channel,
- unsigned int roc_duration)
+ unsigned int roc_duration,
+ unsigned int roc_type)
{
struct cmd_roc cmd_roc;
memset(&cmd_roc, 0, sizeof(struct cmd_roc));
- cmd_roc.roc_ctrl = roc_ctrl;
+ cmd_roc.roc_ctrl = roc_ctrl;
cmd_roc.roc_channel = roc_channel;
- cmd_roc.roc_duration = roc_duration;
+ cmd_roc.roc_duration = roc_duration;
+ cmd_roc.roc_type = roc_type;
return uccp420wlan_send_cmd((unsigned char *) &cmd_roc,
sizeof(struct cmd_roc), UMAC_CMD_ROC_CTRL);
@@ -1302,6 +1305,8 @@
dev->cur_chan.freq_band = freq_band;
dev->chan_prog_done = 0;
+ rcu_read_unlock();
+
return uccp420wlan_send_cmd((unsigned char *) &channel,
sizeof(struct cmd_channel),
UMAC_CMD_CHANNEL);
@@ -1404,6 +1409,7 @@
unsigned long irq_flags, tx_irq_flags;
#ifdef MULTI_CHAN_SUPPORT
struct tx_pkt_info *pkt_info = NULL;
+ struct tx_config *tx;
#endif
memset(&tx_cmd, 0, sizeof(struct cmd_tx_ctrl));
@@ -1419,6 +1425,7 @@
dev = p->context;
spin_lock_irqsave(&dev->tx.lock, tx_irq_flags);
+ tx = &dev->tx;
#ifdef MULTI_CHAN_SUPPORT
txq = &dev->tx.pkt_info[curr_chanctx_idx][descriptor_id].pkt;
pkt_info = &dev->tx.pkt_info[curr_chanctx_idx][descriptor_id];
@@ -1461,6 +1468,9 @@
hdrlen += tx_info_first->control.hw_key->iv_len;
}
+ if (tx_info_first->flags & IEEE80211_TX_CTL_TX_OFFCHAN)
+ tx_cmd.tx_flags |= (1 << UMAC_TX_FLAG_OFFCHAN_FRM);
+
/* For injected frames (wlantest) hw_key is not set,as PMF uses
* CCMP always so hardcode this to CCMP IV LEN 8.
* For Auth3: It is completely handled in SW (mac80211).
@@ -1493,6 +1503,14 @@
tx_cmd.pkt_gram_payload_len = hdrlen;
tx_cmd.aggregate_mpdu = AMPDU_AGGR_DISABLED;
+#ifdef MULTI_CHAN_SUPPORT
+ dev->tx.pkt_info[curr_chanctx_idx][descriptor_id].vif_index = vif_index;
+ dev->tx.pkt_info[curr_chanctx_idx][descriptor_id].queue = queue;
+#else
+ dev->tx.pkt_info[descriptor_id].vif_index = vif_index;
+ dev->tx.pkt_info[descriptor_id].queue = queue;
+#endif
+
uvif = (struct umac_vif *) (tx_info_first->control.vif->drv_priv);
nbuf = alloc_skb(sizeof(struct cmd_tx_ctrl) +
@@ -1541,6 +1559,10 @@
tx_cmd.rate_retries[2],
tx_cmd.rate_retries[3]);
+#ifdef MULTI_CHAN_SUPPORT
+ tx->desc_chan_map[descriptor_id] = curr_chanctx_idx;
+#endif
+
skb_queue_walk_safe(txq, skb, tmp) {
if (!skb || (pkt > tx_cmd.num_frames_per_desc))
break;
@@ -1566,11 +1588,8 @@
#ifdef MULTI_CHAN_SUPPORT
dev->tx.pkt_info[curr_chanctx_idx][descriptor_id].hdr_len =
hdrlen;
- dev->tx.pkt_info[curr_chanctx_idx][descriptor_id].queue =
- queue;
#else
dev->tx.pkt_info[descriptor_id].hdr_len = hdrlen;
- dev->tx.pkt_info[descriptor_id].queue = queue;
#endif
/* Complete packet length */
@@ -2018,6 +2037,19 @@
UMAC_CMD_AUX_ADC_CHAIN_SEL);
}
+int uccp420wlan_cont_tx(int val)
+{
+ struct cmd_cont_tx status;
+
+ memset(&status, 0, sizeof(struct cmd_cont_tx));
+ status.op = val;
+
+ return uccp420wlan_send_cmd((unsigned char *)&status,
+ sizeof(struct cmd_cont_tx),
+ UMAC_CMD_CONT_TX);
+}
+
+
int uccp420wlan_prog_mib_stats(void)
{
@@ -2280,13 +2312,37 @@
uccp420wlan_ch_prog_complete(event,
(struct umac_event_ch_prog_complete *)buff, p->context);
} else if (event == UMAC_EVENT_RF_CALIB_DATA) {
- struct umac_event_rf_calib_data *rf_data = (void *) buff;
+ struct umac_event_rf_calib_data *rf_data = (void *)buff;
uccp420wlan_rf_calib_data(rf_data, p->context);
+ } else if (event == UMAC_EVENT_ROC_STATUS) {
+ struct umac_event_roc_status *roc_status = (void *)buff;
+ struct delayed_work *work = NULL;
+
+ DEBUG_LOG("%s-UMACIF: ROC status is %d\n",
+ dev->name,
+ roc_status->roc_status);
+
+ switch (roc_status->roc_status) {
+ case UMAC_ROC_STAT_STARTED:
+ if (dev->roc_params.roc_in_progress == 0) {
+ dev->roc_params.roc_in_progress = 1;
+ ieee80211_ready_on_channel(dev->hw);
+ DEBUG_LOG("%s-UMACIF: ROC READY..\n",
+ dev->name);
+ }
+ break;
+ case UMAC_ROC_STAT_DONE:
+ case UMAC_ROC_STAT_STOPPED:
+ if (dev->roc_params.roc_in_progress == 1) {
+ work = &dev->roc_complete_work;
+ ieee80211_queue_delayed_work(dev->hw,
+ work,
+ 0);
+ }
+ break;
+ }
#ifdef MULTI_CHAN_SUPPORT
- /* SDK: Need to see if this will work in tasklet context (due to
- * scheduling latencies)
- */
} else if (event == UMAC_EVENT_CHAN_SWITCH) {
uccp420wlan_proc_ch_sw_event((void *)buff,
p->context);
diff --git a/drivers/soc/img/connectivity/img-hostport.c b/drivers/soc/img/connectivity/img-hostport.c
index ee2b8ba..0c8c496 100644
--- a/drivers/soc/img/connectivity/img-hostport.c
+++ b/drivers/soc/img/connectivity/img-hostport.c
@@ -179,7 +179,7 @@
static void notify_common(u16 user_data, int user_id, gen_handler poke_ready,
void *poke_ready_arg)
{
- dbgn("snd -- %d:%d:%02X", user_id, user_id, user_data);
+ trace_printk("img-hostport: snd -- %d:%d:%02X\n", user_id, user_id, user_data);
if (poke_ready)
poke_ready(poke_ready_arg);
iowrite32(0x87 << 24 | user_data << 8 | id_to_field(user_id),
@@ -201,27 +201,28 @@
/* TODO: need to change that to support platforms other that 32 bit */
first_bit = (reg_value & (1 << 31)) >> 31;
if (0 == first_bit) {
- err("unexpected spurious interrupt detected!\n");
+ trace_printk("img-hostport: unexpected spurious interrupt detected (0x%08X)!\n",
+ reg_value);
goto exit;
}
callee_id = CALLEE(reg_value);
caller_id = CALLER(reg_value);
user_message = USERMSG(reg_value);
- dbgn("rcv -- %d:%d:%02X", callee_id, caller_id, user_message);
+ trace_printk("img-hostport: rcv -%c %d:%d:%02X\n", first_bit ? '-' : '*', callee_id, caller_id, user_message);
/*
* callee_id is tainted, therefore must be checked.
*/
if (callee_id > MAX_ENDPOINT_ID) {
- errn("endpoint with id = %u doesn't exist", callee_id);
+ trace_printk("img-hostport: endpoint with id = %u doesn't exist\n", callee_id);
goto deassert;
}
handler = module->endpoints.f[callee_id];
handler_in_use = module->endpoints.in_use + callee_id;
if (NULL == handler) {
- errn("endpoint with id = %u not registered", callee_id);
+ trace_printk("img-hostport: endpoint with id = %u not registered\n", callee_id);
goto deassert;
}
spin_lock_irqsave(handler_in_use, flags);
diff --git a/drivers/spi/spi-img-spfi.c b/drivers/spi/spi-img-spfi.c
index c311958..b16ba2c 100644
--- a/drivers/spi/spi-img-spfi.c
+++ b/drivers/spi/spi-img-spfi.c
@@ -437,6 +437,9 @@
u32 val;
val = spfi_readl(spfi, SPFI_PORT_STATE);
+ val &= ~(SPFI_PORT_STATE_DEV_SEL_MASK <<
+ SPFI_PORT_STATE_DEV_SEL_SHIFT);
+ val |= msg->spi->chip_select << SPFI_PORT_STATE_DEV_SEL_SHIFT;
if (msg->spi->mode & SPI_CPHA)
val |= SPFI_PORT_STATE_CK_PHASE(msg->spi->chip_select);
else
diff --git a/include/dt-bindings/clock/pistachio-clk.h b/include/dt-bindings/clock/pistachio-clk.h
index dfda0c3..bfb915d 100644
--- a/include/dt-bindings/clock/pistachio-clk.h
+++ b/include/dt-bindings/clock/pistachio-clk.h
@@ -21,6 +21,7 @@
/* Fixed-factor clocks */
#define CLK_WIFI_DIV4 16
#define CLK_WIFI_DIV8 17
+#define CLK_SDHOST_DIV4 18
/* Gate clocks */
#define CLK_MIPS 32
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index e540952..6f01653 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -178,9 +178,9 @@
int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops);
int (*read_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg,
- u8 *buf, size_t len);
+ u8 *buf, size_t len, size_t *retlen);
int (*write_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg,
- u8 *buf, size_t len);
+ u8 *buf, size_t len, size_t *retlen);
int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);
int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len,
int write_enable);
diff --git a/include/linux/spi/cc2520.h b/include/linux/spi/cc2520.h
index 14137e3..a591179 100644
--- a/include/linux/spi/cc2520.h
+++ b/include/linux/spi/cc2520.h
@@ -22,6 +22,7 @@
int reset;
int vreg;
unsigned int extclockfreq;
+ bool registerclk;
bool amplified;
};
diff --git a/sound/soc/img/pistachio.c b/sound/soc/img/pistachio.c
index 1a8a6d7..e4b6546 100644
--- a/sound/soc/img/pistachio.c
+++ b/sound/soc/img/pistachio.c
@@ -30,10 +30,10 @@
#include "pistachio-event-timer.h"
-#define PLL_RATE_8000_16000_32000_48000_96000_192000 147456000
-#define PLL_RATE_11025_22050_44100_64000_88200_176400 135475200
-#define PISTACHIO_MAX_DIV 256
-#define PISTACHIO_MIN_MCLK_FREQ (135475200 / 256)
+#define PISTACHIO_PLL_RATE_A 147456000
+#define PISTACHIO_PLL_RATE_B 135475200
+#define PISTACHIO_MAX_DIV 256
+#define PISTACHIO_MIN_MCLK_FREQ (135475200 / 256)
#define PISTACHIO_CLOCK_MASTER_EXT -1
#define PISTACHIO_CLOCK_MASTER_LOOPBACK -2
@@ -398,16 +398,16 @@
case 16000:
case 32000:
case 48000:
+ case 64000:
case 96000:
case 192000:
- return PLL_RATE_8000_16000_32000_48000_96000_192000;
+ return PISTACHIO_PLL_RATE_A;
case 11025:
case 22050:
case 44100:
- case 64000:
case 88200:
case 176400:
- return PLL_RATE_11025_22050_44100_64000_88200_176400;
+ return PISTACHIO_PLL_RATE_B;
default:
return -EINVAL;
}
@@ -455,6 +455,7 @@
int ret;
mutex_lock(&pbc->rate_mutex);
+ *active_rate = 0;
ret = _pistachio_card_change_rate(pbc, rate, i2s);
if (!ret)
*active_rate = rate;
@@ -1543,7 +1544,7 @@
unsigned int rate;
int ret;
- rate = PLL_RATE_11025_22050_44100_64000_88200_176400;
+ rate = PISTACHIO_PLL_RATE_B;
ret = clk_set_rate(pbc->audio_pll, rate);
if (ret)
return ret;