[PATCH v4 5/8] wil6210: add support for enhanced DMA RX data flows

2018-06-21 Thread Maya Erez
From: Gidon Studinski 

Enhanced DMA RX data path is handled using a single
RX descriptor ring for all VIFs.
Multiple RX status rings are supported, to allow RSS
and multi MSI support.
The driver gets the RX completions via the RX status rings.
The RX status message includes the completed RX buffer ID,
which points to the allocated SKB.

The enhanced DMA RX data flow supports RX chaining, where
multiple SKBs are merged into a single packet.

Enhanced DMA HW supports RX HW reorder offload, enabled by
default for Talyn-MB.

amsdu_en debugfs entry was added to allow control MSDU aggregation.
Use the following command to disable AMSDU (enabled by default):
echo 0 > amsdu_en

Signed-off-by: Gidon Studinski 
Signed-off-by: Maya Erez 
---
 drivers/net/wireless/ath/wil6210/debugfs.c|   1 +
 drivers/net/wireless/ath/wil6210/interrupt.c  |  86 -
 drivers/net/wireless/ath/wil6210/main.c   |   3 +
 drivers/net/wireless/ath/wil6210/netdev.c |  35 +-
 drivers/net/wireless/ath/wil6210/pcie_bus.c   |   1 +
 drivers/net/wireless/ath/wil6210/pm.c |   6 +-
 drivers/net/wireless/ath/wil6210/rx_reorder.c |  24 +-
 drivers/net/wireless/ath/wil6210/trace.h  |  34 ++
 drivers/net/wireless/ath/wil6210/txrx.c   |  53 ++-
 drivers/net/wireless/ath/wil6210/txrx.h   |   7 +
 drivers/net/wireless/ath/wil6210/txrx_edma.c  | 456 ++
 drivers/net/wireless/ath/wil6210/txrx_edma.h  | 189 +++
 drivers/net/wireless/ath/wil6210/wil6210.h|  24 +-
 drivers/net/wireless/ath/wil6210/wmi.c|  59 +++-
 drivers/net/wireless/ath/wil6210/wmi.h|   1 +
 15 files changed, 939 insertions(+), 40 deletions(-)

diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c 
b/drivers/net/wireless/ath/wil6210/debugfs.c
index 212baf4..8232fdd 100644
--- a/drivers/net/wireless/ath/wil6210/debugfs.c
+++ b/drivers/net/wireless/ath/wil6210/debugfs.c
@@ -1919,6 +1919,7 @@ static void wil6210_debugfs_init_isr(struct wil6210_priv 
*wil,
WIL_FIELD(rx_status_ring_order, 0644,   doff_u32),
WIL_FIELD(tx_status_ring_order, 0644,   doff_u32),
WIL_FIELD(rx_buff_id_count, 0644,   doff_u32),
+   WIL_FIELD(amsdu_en, 0644,   doff_u8),
{},
 };
 
diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c 
b/drivers/net/wireless/ath/wil6210/interrupt.c
index 1603b9f..d7e112d 100644
--- a/drivers/net/wireless/ath/wil6210/interrupt.c
+++ b/drivers/net/wireless/ath/wil6210/interrupt.c
@@ -45,6 +45,7 @@
 #define WIL6210_IMC_TX (BIT_DMA_EP_TX_ICR_TX_DONE | \
BIT_DMA_EP_TX_ICR_TX_DONE_N(0))
 #define WIL6210_IMC_TX_EDMABIT_TX_STATUS_IRQ
+#define WIL6210_IMC_RX_EDMABIT_RX_STATUS_IRQ
 #define WIL6210_IMC_MISC_NO_HALP   (ISR_MISC_FW_READY | \
 ISR_MISC_MBOX_EVT | \
 ISR_MISC_FW_ERROR)
@@ -100,6 +101,12 @@ static void wil6210_mask_irq_rx(struct wil6210_priv *wil)
  WIL6210_IRQ_DISABLE);
 }
 
+static void wil6210_mask_irq_rx_edma(struct wil6210_priv *wil)
+{
+   wil_w(wil, RGF_INT_GEN_RX_ICR + offsetof(struct RGF_ICR, IMS),
+ WIL6210_IRQ_DISABLE);
+}
+
 static void wil6210_mask_irq_misc(struct wil6210_priv *wil, bool mask_halp)
 {
wil_dbg_irq(wil, "mask_irq_misc: mask_halp(%s)\n",
@@ -146,6 +153,12 @@ void wil6210_unmask_irq_rx(struct wil6210_priv *wil)
  unmask_rx_htrsh ? WIL6210_IMC_RX : WIL6210_IMC_RX_NO_RX_HTRSH);
 }
 
+void wil6210_unmask_irq_rx_edma(struct wil6210_priv *wil)
+{
+   wil_w(wil, RGF_INT_GEN_RX_ICR + offsetof(struct RGF_ICR, IMC),
+ WIL6210_IMC_RX_EDMA);
+}
+
 static void wil6210_unmask_irq_misc(struct wil6210_priv *wil, bool unmask_halp)
 {
wil_dbg_irq(wil, "unmask_irq_misc: unmask_halp(%s)\n",
@@ -179,6 +192,7 @@ void wil_mask_irq(struct wil6210_priv *wil)
wil6210_mask_irq_tx(wil);
wil6210_mask_irq_tx_edma(wil);
wil6210_mask_irq_rx(wil);
+   wil6210_mask_irq_rx_edma(wil);
wil6210_mask_irq_misc(wil, true);
wil6210_mask_irq_pseudo(wil);
 }
@@ -195,10 +209,13 @@ void wil_unmask_irq(struct wil6210_priv *wil)
  WIL_ICR_ICC_MISC_VALUE);
wil_w(wil, RGF_INT_GEN_TX_ICR + offsetof(struct RGF_ICR, ICC),
  WIL_ICR_ICC_VALUE);
+   wil_w(wil, RGF_INT_GEN_RX_ICR + offsetof(struct RGF_ICR, ICC),
+ WIL_ICR_ICC_VALUE);
 
wil6210_unmask_irq_pseudo(wil);
if (wil->use_enhanced_dma_hw) {
wil6210_unmask_irq_tx_edma(wil);
+   wil6210_unmask_irq_rx_edma(wil);
} else {
wil6210_unmask_irq_tx(wil);
wil6210_unmask_irq_rx(wil);
@@ -335,6 +352,54 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
return IRQ_HANDLED;
 }
 
+static irqreturn_t wil6210_irq_rx_edma(int irq, void *cookie)
+{
+   struct wil6210_priv *wil = cookie;
+   u32 isr = wil_ioread32_and_clear(wil->csr 

[PATCH v4 7/8] wil6210: add support for Talyn-MB boot flow

2018-06-21 Thread Maya Erez
Talyn-MB introduces various of FW download options:
FW download via PCIe, SPI or PBL for secured access.
The boot and FW download path is determined based on the
OTP HW register. Driver reads this register as part of the
SW reset flow and performs the appropriate initialization
sequence.

Signed-off-by: Maya Erez 
---
 drivers/net/wireless/ath/wil6210/main.c| 194 -
 drivers/net/wireless/ath/wil6210/wil6210.h |   9 +-
 2 files changed, 171 insertions(+), 32 deletions(-)

diff --git a/drivers/net/wireless/ath/wil6210/main.c 
b/drivers/net/wireless/ath/wil6210/main.c
index e0072b6..4de19bd 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -112,9 +112,29 @@ static int ring_order_set(const char *val, const struct 
kernel_param *kp)
 module_param_cb(bcast_ring_order, _order_ops, _ring_order, 0444);
 MODULE_PARM_DESC(bcast_ring_order, " Bcast ring order; size = 1 << order");
 
-#define RST_DELAY (20) /* msec, for loop in @wil_target_reset */
+enum {
+   WIL_BOOT_ERR,
+   WIL_BOOT_VANILLA,
+   WIL_BOOT_PRODUCTION,
+   WIL_BOOT_DEVELOPMENT,
+};
+
+enum {
+   WIL_SIG_STATUS_VANILLA = 0x0,
+   WIL_SIG_STATUS_DEVELOPMENT = 0x1,
+   WIL_SIG_STATUS_PRODUCTION = 0x2,
+   WIL_SIG_STATUS_CORRUPTED_PRODUCTION = 0x3,
+};
+
+#define RST_DELAY (20) /* msec, for loop in @wil_wait_device_ready */
 #define RST_COUNT (1 + 1000/RST_DELAY) /* round up to be above 1 sec total */
 
+#define PMU_READY_DELAY_MS (4) /* ms, for sleep in @wil_wait_device_ready */
+
+#define OTP_HW_DELAY (200) /* usec, loop in @wil_wait_device_ready_talyn_mb */
+/* round up to be above 2 ms total */
+#define OTP_HW_COUNT (1 + 2000 / OTP_HW_DELAY)
+
 /*
  * Due to a hardware issue,
  * one has to read/write to/from NIC in 32-bit chunks;
@@ -831,11 +851,146 @@ static void wil_set_oob_mode(struct wil6210_priv *wil, 
u8 mode)
}
 }
 
-static int wil_target_reset(struct wil6210_priv *wil, int no_flash)
+static int wil_wait_device_ready(struct wil6210_priv *wil, int no_flash)
 {
int delay = 0;
u32 x, x1 = 0;
 
+   /* wait until device ready. */
+   if (no_flash) {
+   msleep(PMU_READY_DELAY_MS);
+
+   wil_dbg_misc(wil, "Reset completed\n");
+   } else {
+   do {
+   msleep(RST_DELAY);
+   x = wil_r(wil, RGF_USER_BL +
+ offsetof(struct bl_dedicated_registers_v0,
+  boot_loader_ready));
+   if (x1 != x) {
+   wil_dbg_misc(wil, "BL.ready 0x%08x => 0x%08x\n",
+x1, x);
+   x1 = x;
+   }
+   if (delay++ > RST_COUNT) {
+   wil_err(wil, "Reset not completed, bl.ready 
0x%08x\n",
+   x);
+   return -ETIME;
+   }
+   } while (x != BL_READY);
+
+   wil_dbg_misc(wil, "Reset completed in %d ms\n",
+delay * RST_DELAY);
+   }
+
+   return 0;
+}
+
+static int wil_wait_device_ready_talyn_mb(struct wil6210_priv *wil)
+{
+   u32 otp_hw;
+   u8 signature_status;
+   bool otp_signature_err;
+   bool hw_section_done;
+   u32 otp_qc_secured;
+   int delay = 0;
+
+   /* Wait for OTP signature test to complete */
+   usleep_range(2000, 2200);
+
+   wil->boot_config = WIL_BOOT_ERR;
+
+   /* Poll until OTP signature status is valid.
+* In vanilla and development modes, when signature test is complete
+* HW sets BIT_OTP_SIGNATURE_ERR_TALYN_MB.
+* In production mode BIT_OTP_SIGNATURE_ERR_TALYN_MB remains 0, poll
+* for signature status change to 2 or 3.
+*/
+   do {
+   otp_hw = wil_r(wil, RGF_USER_OTP_HW_RD_MACHINE_1);
+   signature_status = WIL_GET_BITS(otp_hw, 8, 9);
+   otp_signature_err = otp_hw & BIT_OTP_SIGNATURE_ERR_TALYN_MB;
+
+   if (otp_signature_err &&
+   signature_status == WIL_SIG_STATUS_VANILLA) {
+   wil->boot_config = WIL_BOOT_VANILLA;
+   break;
+   }
+   if (otp_signature_err &&
+   signature_status == WIL_SIG_STATUS_DEVELOPMENT) {
+   wil->boot_config = WIL_BOOT_DEVELOPMENT;
+   break;
+   }
+   if (!otp_signature_err &&
+   signature_status == WIL_SIG_STATUS_PRODUCTION) {
+   wil->boot_config = WIL_BOOT_PRODUCTION;
+   break;
+   }
+   if  (!otp_signature_err &&
+signature_status ==
+WIL_SIG_STATUS_CORRUPTED_PRODUCTION) {
+   

[PATCH v4 8/8] wil6210: remove crash dump collection from OTP section

2018-06-21 Thread Maya Erez
In some cases where the device is stuck, reading from OTP
can timeout. As OTP section is known there is no need to read
it during device crash dump collection.
Adding a new field to struct fw_map to indicate if to include
this section in crash dump collection.

Signed-off-by: Maya Erez 
---
 drivers/net/wireless/ath/wil6210/wil6210.h|  1 +
 drivers/net/wireless/ath/wil6210/wil_crash_dump.c |  5 +-
 drivers/net/wireless/ath/wil6210/wmi.c| 86 +++
 3 files changed, 47 insertions(+), 45 deletions(-)

diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h 
b/drivers/net/wireless/ath/wil6210/wil6210.h
index e639ba7..d963c76 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -403,6 +403,7 @@ struct fw_map {
u32 host; /* PCI/Host address - BAR0 + 0x88 */
const char *name; /* for debugfs */
bool fw; /* true if FW mapping, false if UCODE mapping */
+   bool crash_dump; /* true if should be dumped during crash dump */
 };
 
 /* array size should be in sync with actual definition in the wmi.c */
diff --git a/drivers/net/wireless/ath/wil6210/wil_crash_dump.c 
b/drivers/net/wireless/ath/wil6210/wil_crash_dump.c
index 1ed3306..dc33a0b 100644
--- a/drivers/net/wireless/ath/wil6210/wil_crash_dump.c
+++ b/drivers/net/wireless/ath/wil6210/wil_crash_dump.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2015,2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -36,7 +37,7 @@ static int wil_fw_get_crash_dump_bounds(struct wil6210_priv 
*wil,
for (i = 1; i < ARRAY_SIZE(fw_mapping); i++) {
map = _mapping[i];
 
-   if (!map->fw)
+   if (!map->crash_dump)
continue;
 
if (map->host < host_min)
@@ -85,7 +86,7 @@ int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void 
*dest, u32 size)
for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) {
map = _mapping[i];
 
-   if (!map->fw)
+   if (!map->crash_dump)
continue;
 
data = (void * __force)wil->csr + HOSTADDR(map->host);
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c 
b/drivers/net/wireless/ath/wil6210/wmi.c
index 0370b7e..71056c8 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -89,28 +89,28 @@
  */
 const struct fw_map sparrow_fw_mapping[] = {
/* FW code RAM 256k */
-   {0x00, 0x04, 0x8c, "fw_code", true},
+   {0x00, 0x04, 0x8c, "fw_code", true, true},
/* FW data RAM 32k */
-   {0x80, 0x808000, 0x90, "fw_data", true},
+   {0x80, 0x808000, 0x90, "fw_data", true, true},
/* periph data 128k */
-   {0x84, 0x86, 0x908000, "fw_peri", true},
+   {0x84, 0x86, 0x908000, "fw_peri", true, true},
/* various RGF 40k */
-   {0x88, 0x88a000, 0x88, "rgf", true},
+   {0x88, 0x88a000, 0x88, "rgf", true, true},
/* AGC table   4k */
-   {0x88a000, 0x88b000, 0x88a000, "AGC_tbl", true},
+   {0x88a000, 0x88b000, 0x88a000, "AGC_tbl", true, true},
/* Pcie_ext_rgf 4k */
-   {0x88b000, 0x88c000, 0x88b000, "rgf_ext", true},
+   {0x88b000, 0x88c000, 0x88b000, "rgf_ext", true, true},
/* mac_ext_rgf 512b */
-   {0x88c000, 0x88c200, 0x88c000, "mac_rgf_ext", true},
+   {0x88c000, 0x88c200, 0x88c000, "mac_rgf_ext", true, true},
/* upper area 548k */
-   {0x8c, 0x949000, 0x8c, "upper", true},
+   {0x8c, 0x949000, 0x8c, "upper", true, true},
/* UCODE areas - accessible by debugfs blobs but not by
 * wmi_addr_remap. UCODE areas MUST be added AFTER FW areas!
 */
/* ucode code RAM 128k */
-   {0x00, 0x02, 0x92, "uc_code", false},
+   {0x00, 0x02, 0x92, "uc_code", false, false},
/* ucode data RAM 16k */
-   {0x80, 0x804000, 0x94, "uc_data", false},
+   {0x80, 0x804000, 0x94, "uc_data", false, false},
 };
 
 /**
@@ -118,7 +118,7 @@
  * it is a bit larger to support extra features
  */
 const struct fw_map sparrow_d0_mac_rgf_ext = {
-   0x88c000, 0x88c500, 0x88c000, "mac_rgf_ext", true
+   0x88c000, 0x88c500, 0x88c000, "mac_rgf_ext", true, true
 };
 
 /**
@@ -134,34 +134,34 @@
  */
 const struct fw_map talyn_fw_mapping[] = {
/* FW code RAM 1M */
-   {0x00, 0x10, 0x90, "fw_code", true},
+   {0x00, 0x10, 0x90, "fw_code", true, true},
/* FW data RAM 128k */
-   {0x80, 0x82, 0xa0, "fw_data", true},
+   {0x80, 0x82, 0xa0, "fw_data", true, true},
/* periph. data RAM 96k */
-   {0x84, 

[PATCH v4 4/8] wil6210: add support for enhanced DMA TX data flows

2018-06-21 Thread Maya Erez
The enhanced DMA TX data path is handled using a descriptor
ring per connection and a single status ring.

The driver gets TX completions via the TX status ring. Each
status message points to the completed descriptor ring and
includes the number of completed descriptors in this ring.

Non TSO enhanced DMA TX descriptors are similar to legacy DMA
TX descriptors, hence the same transmit function can be used.

However, enhanced DMA TSO frames division is performed by the
HW, hence a new function is added to handle enhanced DMA TSO.

Signed-off-by: Gidon Studinski 
Signed-off-by: Maya Erez 
---
 drivers/net/wireless/ath/wil6210/interrupt.c | 126 ++--
 drivers/net/wireless/ath/wil6210/netdev.c|  36 ++-
 drivers/net/wireless/ath/wil6210/trace.h |  25 ++
 drivers/net/wireless/ath/wil6210/txrx.c  | 194 ++--
 drivers/net/wireless/ath/wil6210/txrx.h  |  22 ++
 drivers/net/wireless/ath/wil6210/txrx_edma.c | 427 ++-
 drivers/net/wireless/ath/wil6210/txrx_edma.h |  33 ++-
 drivers/net/wireless/ath/wil6210/wil6210.h   |  15 +
 8 files changed, 750 insertions(+), 128 deletions(-)

diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c 
b/drivers/net/wireless/ath/wil6210/interrupt.c
index 311d482..1603b9f 100644
--- a/drivers/net/wireless/ath/wil6210/interrupt.c
+++ b/drivers/net/wireless/ath/wil6210/interrupt.c
@@ -44,6 +44,7 @@
(~(BIT_DMA_EP_RX_ICR_RX_HTRSH)))
 #define WIL6210_IMC_TX (BIT_DMA_EP_TX_ICR_TX_DONE | \
BIT_DMA_EP_TX_ICR_TX_DONE_N(0))
+#define WIL6210_IMC_TX_EDMABIT_TX_STATUS_IRQ
 #define WIL6210_IMC_MISC_NO_HALP   (ISR_MISC_FW_READY | \
 ISR_MISC_MBOX_EVT | \
 ISR_MISC_FW_ERROR)
@@ -87,6 +88,12 @@ static void wil6210_mask_irq_tx(struct wil6210_priv *wil)
  WIL6210_IRQ_DISABLE);
 }
 
+static void wil6210_mask_irq_tx_edma(struct wil6210_priv *wil)
+{
+   wil_w(wil, RGF_INT_GEN_TX_ICR + offsetof(struct RGF_ICR, IMS),
+ WIL6210_IRQ_DISABLE);
+}
+
 static void wil6210_mask_irq_rx(struct wil6210_priv *wil)
 {
wil_w(wil, RGF_DMA_EP_RX_ICR + offsetof(struct RGF_ICR, IMS),
@@ -125,6 +132,12 @@ void wil6210_unmask_irq_tx(struct wil6210_priv *wil)
  WIL6210_IMC_TX);
 }
 
+void wil6210_unmask_irq_tx_edma(struct wil6210_priv *wil)
+{
+   wil_w(wil, RGF_INT_GEN_TX_ICR + offsetof(struct RGF_ICR, IMC),
+ WIL6210_IMC_TX_EDMA);
+}
+
 void wil6210_unmask_irq_rx(struct wil6210_priv *wil)
 {
bool unmask_rx_htrsh = atomic_read(>connected_vifs) > 0;
@@ -164,6 +177,7 @@ void wil_mask_irq(struct wil6210_priv *wil)
wil_dbg_irq(wil, "mask_irq\n");
 
wil6210_mask_irq_tx(wil);
+   wil6210_mask_irq_tx_edma(wil);
wil6210_mask_irq_rx(wil);
wil6210_mask_irq_misc(wil, true);
wil6210_mask_irq_pseudo(wil);
@@ -179,10 +193,16 @@ void wil_unmask_irq(struct wil6210_priv *wil)
  WIL_ICR_ICC_VALUE);
wil_w(wil, RGF_DMA_EP_MISC_ICR + offsetof(struct RGF_ICR, ICC),
  WIL_ICR_ICC_MISC_VALUE);
+   wil_w(wil, RGF_INT_GEN_TX_ICR + offsetof(struct RGF_ICR, ICC),
+ WIL_ICR_ICC_VALUE);
 
wil6210_unmask_irq_pseudo(wil);
-   wil6210_unmask_irq_tx(wil);
-   wil6210_unmask_irq_rx(wil);
+   if (wil->use_enhanced_dma_hw) {
+   wil6210_unmask_irq_tx_edma(wil);
+   } else {
+   wil6210_unmask_irq_tx(wil);
+   wil6210_unmask_irq_rx(wil);
+   }
wil6210_unmask_irq_misc(wil, true);
 }
 
@@ -315,6 +335,49 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
return IRQ_HANDLED;
 }
 
+static irqreturn_t wil6210_irq_tx_edma(int irq, void *cookie)
+{
+   struct wil6210_priv *wil = cookie;
+   u32 isr = wil_ioread32_and_clear(wil->csr +
+HOSTADDR(RGF_INT_GEN_TX_ICR) +
+offsetof(struct RGF_ICR, ICR));
+   bool need_unmask = true;
+
+   trace_wil6210_irq_tx(isr);
+   wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr);
+
+   if (unlikely(!isr)) {
+   wil_err(wil, "spurious IRQ: TX\n");
+   return IRQ_NONE;
+   }
+
+   wil6210_mask_irq_tx_edma(wil);
+
+   if (likely(isr & BIT_TX_STATUS_IRQ)) {
+   wil_dbg_irq(wil, "TX status ring\n");
+   isr &= ~BIT_TX_STATUS_IRQ;
+   if (likely(test_bit(wil_status_fwready, wil->status))) {
+   wil_dbg_txrx(wil, "NAPI(Tx) schedule\n");
+   need_unmask = false;
+   napi_schedule(>napi_tx);
+   } else {
+   wil_err(wil, "Got Tx status ring IRQ while in reset\n");
+   }
+   }
+
+   if (unlikely(isr))
+   wil_err(wil, "un-handled TX ISR bits 0x%08x\n", isr);
+
+   /* Tx IRQ will be enabled 

[PATCH v4 0/8] wil6210 patches

2018-06-21 Thread Maya Erez
Changes from v3:
Fix copyright of wil_crash_dump.c

Changes from v2:
- Fix amsdu setting in BA response
- Remove crash dump collection from OTP sectionas it can cause PCIe halt

Changes from v1:
- Removal of module parameters
- Addition of debugfs entries for configuring enhanced DMA
- Fixing kbuild bot error
- Enabling AMSDU by default (based on FW support)
- Fixing L2 translation mode

The following set of patches add support for new wil6210 device (Talyn ver 2.0):
- Definition of the new device ID and memory areas
- New boot flow procedure and reset sequence
- Support new DMA interface - enhanced DMA
- Support new HW offloads, such as TSO, RX reorder and AMSDU

Gidon Studinski (3):
  wil6210: add support for enhanced DMA structures
  wil6210: initialize TX and RX enhanced DMA rings
  wil6210: add support for enhanced DMA RX data flows

Maya Erez (5):
  wil6210: add support for Talyn-MB (Talyn ver 2.0) device
  wil6210: add support for enhanced DMA TX data flows
  wil6210: add support for enhanced DMA debugfs
  wil6210: add support for Talyn-MB boot flow
  wil6210: remove crash dump collection from OTP section

 drivers/net/wireless/ath/wil6210/Makefile |1 +
 drivers/net/wireless/ath/wil6210/cfg80211.c   |   12 +-
 drivers/net/wireless/ath/wil6210/debugfs.c|  490 +--
 drivers/net/wireless/ath/wil6210/ethtool.c|2 +-
 drivers/net/wireless/ath/wil6210/interrupt.c  |  225 ++-
 drivers/net/wireless/ath/wil6210/main.c   |  360 -
 drivers/net/wireless/ath/wil6210/netdev.c |   73 +-
 drivers/net/wireless/ath/wil6210/pcie_bus.c   |   59 +-
 drivers/net/wireless/ath/wil6210/pm.c |6 +-
 drivers/net/wireless/ath/wil6210/rx_reorder.c |   26 +-
 drivers/net/wireless/ath/wil6210/trace.h  |   59 +
 drivers/net/wireless/ath/wil6210/txrx.c   |  649 -
 drivers/net/wireless/ath/wil6210/txrx.h   |  105 +-
 drivers/net/wireless/ath/wil6210/txrx_edma.c  | 1598 +
 drivers/net/wireless/ath/wil6210/txrx_edma.h  |  562 
 drivers/net/wireless/ath/wil6210/wil6210.h|  228 ++-
 drivers/net/wireless/ath/wil6210/wil_crash_dump.c |5 +-
 drivers/net/wireless/ath/wil6210/wmi.c|  502 ++-
 drivers/net/wireless/ath/wil6210/wmi.h|  167 ++-
 19 files changed, 4468 insertions(+), 661 deletions(-)
 create mode 100644 drivers/net/wireless/ath/wil6210/txrx_edma.c
 create mode 100644 drivers/net/wireless/ath/wil6210/txrx_edma.h

-- 
1.9.1



[PATCH v4 1/8] wil6210: add support for Talyn-MB (Talyn ver 2.0) device

2018-06-21 Thread Maya Erez
Add changes to support initialization of Talyn-MB wil6210
device:
- Add definition for Talyn-MB new JTAG id
- Define talyn_mb_fw_mapping array
- Add Talyn-MB reset sequence

Signed-off-by: Maya Erez 
---
 drivers/net/wireless/ath/wil6210/main.c | 48 +++--
 drivers/net/wireless/ath/wil6210/pcie_bus.c | 11 +-
 drivers/net/wireless/ath/wil6210/wil6210.h  | 11 +-
 drivers/net/wireless/ath/wil6210/wmi.c  | 55 +
 4 files changed, 113 insertions(+), 12 deletions(-)

diff --git a/drivers/net/wireless/ath/wil6210/main.c 
b/drivers/net/wireless/ath/wil6210/main.c
index e7006c2..1282e1a 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -736,14 +736,24 @@ static void wil_bl_prepare_halt(struct wil6210_priv *wil)
 
 static inline void wil_halt_cpu(struct wil6210_priv *wil)
 {
-   wil_w(wil, RGF_USER_USER_CPU_0, BIT_USER_USER_CPU_MAN_RST);
-   wil_w(wil, RGF_USER_MAC_CPU_0,  BIT_USER_MAC_CPU_MAN_RST);
+   if (wil->hw_version >= HW_VER_TALYN_MB) {
+   wil_w(wil, RGF_USER_USER_CPU_0_TALYN_MB,
+ BIT_USER_USER_CPU_MAN_RST);
+   wil_w(wil, RGF_USER_MAC_CPU_0_TALYN_MB,
+ BIT_USER_MAC_CPU_MAN_RST);
+   } else {
+   wil_w(wil, RGF_USER_USER_CPU_0, BIT_USER_USER_CPU_MAN_RST);
+   wil_w(wil, RGF_USER_MAC_CPU_0,  BIT_USER_MAC_CPU_MAN_RST);
+   }
 }
 
 static inline void wil_release_cpu(struct wil6210_priv *wil)
 {
/* Start CPU */
-   wil_w(wil, RGF_USER_USER_CPU_0, 1);
+   if (wil->hw_version >= HW_VER_TALYN_MB)
+   wil_w(wil, RGF_USER_USER_CPU_0_TALYN_MB, 1);
+   else
+   wil_w(wil, RGF_USER_USER_CPU_0, 1);
 }
 
 static void wil_set_oob_mode(struct wil6210_priv *wil, u8 mode)
@@ -811,10 +821,17 @@ static int wil_target_reset(struct wil6210_priv *wil, int 
no_flash)
wil_w(wil, RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x3ff81f);
wil_w(wil, RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_1, 0xf);
 
-   wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE00);
-   wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x003F);
-   wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00f0);
-   wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FE00);
+   if (wil->hw_version >= HW_VER_TALYN_MB) {
+   wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x7e00);
+   wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x003f);
+   wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0xc0f0);
+   wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xffe7fe00);
+   } else {
+   wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xfe00);
+   wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x003f);
+   wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00f0);
+   wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xffe7fe00);
+   }
 
wil_w(wil, RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x0);
wil_w(wil, RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_1, 0x0);
@@ -1042,8 +1059,14 @@ static int wil_get_otp_info(struct wil6210_priv *wil)
struct net_device *ndev = wil->main_ndev;
struct wiphy *wiphy = wil_to_wiphy(wil);
u8 mac[8];
+   int mac_addr;
+
+   if (wil->hw_version >= HW_VER_TALYN_MB)
+   mac_addr = RGF_OTP_MAC_TALYN_MB;
+   else
+   mac_addr = RGF_OTP_MAC;
 
-   wil_memcpy_fromio_32(mac, wil->csr + HOSTADDR(RGF_OTP_MAC),
+   wil_memcpy_fromio_32(mac, wil->csr + HOSTADDR(mac_addr),
 sizeof(mac));
if (!is_valid_ether_addr(mac)) {
wil_err(wil, "Invalid MAC %pM\n", mac);
@@ -1147,8 +1170,13 @@ static void wil_pre_fw_config(struct wil6210_priv *wil)
/* it is W1C, clear by writing back same value */
wil_s(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, ICR), 0);
wil_w(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, IMV), ~0);
-   /* clear PAL_UNIT_ICR (potential D0->D3 leftover) */
-   wil_s(wil, RGF_PAL_UNIT_ICR + offsetof(struct RGF_ICR, ICR), 0);
+   /* clear PAL_UNIT_ICR (potential D0->D3 leftover)
+* In Talyn-MB host cannot access this register due to
+* access control, hence PAL_UNIT_ICR is cleared by the FW
+*/
+   if (wil->hw_version < HW_VER_TALYN_MB)
+   wil_s(wil, RGF_PAL_UNIT_ICR + offsetof(struct RGF_ICR, ICR),
+ 0);
 
if (wil->fw_calib_result > 0) {
__le32 val = cpu_to_le32(wil->fw_calib_result |
diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c 
b/drivers/net/wireless/ath/wil6210/pcie_bus.c
index 19cbc6a..3a7e406 100644
--- a/drivers/net/wireless/ath/wil6210/pcie_bus.c
+++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
@@ -85,7 +85,7 @@ int wil_set_capabilities(struct wil6210_priv *wil)
wil->rgf_ucode_assert_code_addr = SPARROW_RGF_UCODE_ASSERT_CODE;
   

[PATCH v4 2/8] wil6210: add support for enhanced DMA structures

2018-06-21 Thread Maya Erez
From: Gidon Studinski 

In enhanced DMA the vrings are handled internally by the FW
and are not exposed to the driver.
Instead, the driver handles descriptor rings, which are mapped
by the FW to vrings.
The completions of the TX and RX descriptors are notified to
the driver using status rings. Each status ring descriptor
includes information of the completed descriptors and the ring id
of their descriptor ring.

This patch changes struct vring to generic wil_ring to allow
its reuse for enhanced DMA descriptor rings and adds the descriptor
and status rings specific descriptors.

The vring debugfs entries have changed as follows:
- dbg_vring_index has changed to dbg_ring_index
- vrings has changed to rings
- vring_idle_trsh has changed to ring_idle_trsh
- vring_index has changed to ring_index

Signed-off-by: Gidon Studinski 
Signed-off-by: Maya Erez 
---
 drivers/net/wireless/ath/wil6210/cfg80211.c   |  12 +-
 drivers/net/wireless/ath/wil6210/debugfs.c| 111 
 drivers/net/wireless/ath/wil6210/main.c   |  34 +--
 drivers/net/wireless/ath/wil6210/netdev.c |   8 +-
 drivers/net/wireless/ath/wil6210/rx_reorder.c |   2 +-
 drivers/net/wireless/ath/wil6210/txrx.c   | 356 --
 drivers/net/wireless/ath/wil6210/txrx.h   |  68 -
 drivers/net/wireless/ath/wil6210/txrx_edma.h  | 290 +
 drivers/net/wireless/ath/wil6210/wil6210.h| 110 ++--
 drivers/net/wireless/ath/wil6210/wmi.c|  24 +-
 10 files changed, 696 insertions(+), 319 deletions(-)
 create mode 100644 drivers/net/wireless/ath/wil6210/txrx_edma.h

diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c 
b/drivers/net/wireless/ath/wil6210/cfg80211.c
index 78946f2..dfe64b0 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -1726,7 +1726,7 @@ static int wil_cfg80211_change_station(struct wiphy 
*wiphy,
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
int authorize;
int cid, i;
-   struct vring_tx_data *txdata = NULL;
+   struct wil_ring_tx_data *txdata = NULL;
 
wil_dbg_misc(wil, "change station %pM mask 0x%x set 0x%x mid %d\n",
 mac, params->sta_flags_mask, params->sta_flags_set,
@@ -1746,20 +1746,20 @@ static int wil_cfg80211_change_station(struct wiphy 
*wiphy,
return -ENOLINK;
}
 
-   for (i = 0; i < ARRAY_SIZE(wil->vring2cid_tid); i++)
-   if (wil->vring2cid_tid[i][0] == cid) {
-   txdata = >vring_tx_data[i];
+   for (i = 0; i < ARRAY_SIZE(wil->ring2cid_tid); i++)
+   if (wil->ring2cid_tid[i][0] == cid) {
+   txdata = >ring_tx_data[i];
break;
}
 
if (!txdata) {
-   wil_err(wil, "vring data not found\n");
+   wil_err(wil, "ring data not found\n");
return -ENOLINK;
}
 
authorize = params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED);
txdata->dot1x_open = authorize ? 1 : 0;
-   wil_dbg_misc(wil, "cid %d vring %d authorize %d\n", cid, i,
+   wil_dbg_misc(wil, "cid %d ring %d authorize %d\n", cid, i,
 txdata->dot1x_open);
 
return 0;
diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c 
b/drivers/net/wireless/ath/wil6210/debugfs.c
index ebfdff4..55e03e9b 100644
--- a/drivers/net/wireless/ath/wil6210/debugfs.c
+++ b/drivers/net/wireless/ath/wil6210/debugfs.c
@@ -29,7 +29,7 @@
 /* Nasty hack. Better have per device instances */
 static u32 mem_addr;
 static u32 dbg_txdesc_index;
-static u32 dbg_vring_index; /* 24+ for Rx, 0..23 for Tx */
+static u32 dbg_ring_index; /* 24+ for Rx, 0..23 for Tx */
 
 enum dbg_off_type {
doff_u32 = 0,
@@ -47,20 +47,20 @@ struct dbg_off {
enum dbg_off_type type;
 };
 
-static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil,
-   const char *name, struct vring *vring,
-   char _s, char _h)
+static void wil_print_ring(struct seq_file *s, struct wil6210_priv *wil,
+  const char *name, struct wil_ring *ring,
+  char _s, char _h)
 {
-   void __iomem *x = wmi_addr(wil, vring->hwtail);
+   void __iomem *x = wmi_addr(wil, ring->hwtail);
u32 v;
 
-   seq_printf(s, "VRING %s = {\n", name);
-   seq_printf(s, "  pa = %pad\n", >pa);
-   seq_printf(s, "  va = 0x%p\n", vring->va);
-   seq_printf(s, "  size   = %d\n", vring->size);
-   seq_printf(s, "  swtail = %d\n", vring->swtail);
-   seq_printf(s, "  swhead = %d\n", vring->swhead);
-   seq_printf(s, "  hwtail = [0x%08x] -> ", vring->hwtail);
+   seq_printf(s, "RING %s = {\n", name);
+   seq_printf(s, "  pa = %pad\n", >pa);
+   seq_printf(s, "  va = 0x%p\n", ring->va);
+   seq_printf(s, "  size   = %d\n", ring->size);
+   seq_printf(s, "  swtail 

[PATCH v4 6/8] wil6210: add support for enhanced DMA debugfs

2018-06-21 Thread Maya Erez
Add debugfs support for enhanced DMA TX and RX descriptor rings,
TX and RX status rings and RX buffer management.

Run the following command to print the TX and RX status rings:
cat srings

Run the following command in order to select the status ring:
echo STATUS_RING_IDX > dbg_sring_index

Run the following command in order to select the status message:
echo STATUS_MSG_IDX > dbg_status_msg_index

Run the following command in order to print the selected status
message from the selected status ring:
cat status_msg

Run the following command in order to print the RX buffer management
debug information:
cat rx_buff_mgmt

Signed-off-by: Gidon Studinski 
Signed-off-by: Maya Erez 
---
 drivers/net/wireless/ath/wil6210/debugfs.c | 347 +
 1 file changed, 307 insertions(+), 40 deletions(-)

diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c 
b/drivers/net/wireless/ath/wil6210/debugfs.c
index 8232fdd..58ce044 100644
--- a/drivers/net/wireless/ath/wil6210/debugfs.c
+++ b/drivers/net/wireless/ath/wil6210/debugfs.c
@@ -30,6 +30,9 @@
 static u32 mem_addr;
 static u32 dbg_txdesc_index;
 static u32 dbg_ring_index; /* 24+ for Rx, 0..23 for Tx */
+static u32 dbg_status_msg_index;
+/* 0..wil->num_rx_status_rings-1 for Rx, wil->tx_sring_idx for Tx */
+static u32 dbg_sring_index;
 
 enum dbg_off_type {
doff_u32 = 0,
@@ -47,6 +50,36 @@ struct dbg_off {
enum dbg_off_type type;
 };
 
+static void wil_print_desc_edma(struct seq_file *s, struct wil6210_priv *wil,
+   struct wil_ring *ring,
+   char _s, char _h, int idx)
+{
+   u8 num_of_descs;
+   bool has_skb = false;
+
+   if (ring->is_rx) {
+   struct wil_rx_enhanced_desc *rx_d =
+   (struct wil_rx_enhanced_desc *)
+   >va[idx].rx.enhanced;
+   u16 buff_id = le16_to_cpu(rx_d->mac.buff_id);
+
+   has_skb = wil->rx_buff_mgmt.buff_arr[buff_id].skb;
+   seq_printf(s, "%c", (has_skb) ? _h : _s);
+   } else {
+   struct wil_tx_enhanced_desc *d =
+   (struct wil_tx_enhanced_desc *)
+   >va[idx].tx.enhanced;
+
+   num_of_descs = (u8)d->mac.d[2];
+   has_skb = ring->ctx[idx].skb;
+   if (num_of_descs >= 1)
+   seq_printf(s, "%c", ring->ctx[idx].skb ? _h : _s);
+   else
+   /* num_of_descs == 0, it's a frag in a list of descs */
+   seq_printf(s, "%c", has_skb ? 'h' : _s);
+   }
+}
+
 static void wil_print_ring(struct seq_file *s, struct wil6210_priv *wil,
   const char *name, struct wil_ring *ring,
   char _s, char _h)
@@ -58,7 +91,10 @@ static void wil_print_ring(struct seq_file *s, struct 
wil6210_priv *wil,
seq_printf(s, "  pa = %pad\n", >pa);
seq_printf(s, "  va = 0x%p\n", ring->va);
seq_printf(s, "  size   = %d\n", ring->size);
-   seq_printf(s, "  swtail = %d\n", ring->swtail);
+   if (wil->use_enhanced_dma_hw && ring->is_rx)
+   seq_printf(s, "  swtail = %u\n", *ring->edma_rx_swtail.va);
+   else
+   seq_printf(s, "  swtail = %d\n", ring->swtail);
seq_printf(s, "  swhead = %d\n", ring->swhead);
seq_printf(s, "  hwtail = [0x%08x] -> ", ring->hwtail);
if (x) {
@@ -72,13 +108,16 @@ static void wil_print_ring(struct seq_file *s, struct 
wil6210_priv *wil,
uint i;
 
for (i = 0; i < ring->size; i++) {
-   volatile struct vring_tx_desc *d =
-   >va[i].tx.legacy;
-
-   if ((i % 128) == 0 && (i != 0))
+   if ((i % 128) == 0 && i != 0)
seq_puts(s, "\n");
-   seq_printf(s, "%c", (d->dma.status & BIT(0)) ?
-   _s : (ring->ctx[i].skb ? _h : 'h'));
+   if (wil->use_enhanced_dma_hw) {
+   wil_print_desc_edma(s, wil, ring, _s, _h, i);
+   } else {
+   volatile struct vring_tx_desc *d =
+   >va[i].tx.legacy;
+   seq_printf(s, "%c", (d->dma.status & BIT(0)) ?
+  _s : (ring->ctx[i].skb ? _h : 'h'));
+   }
}
seq_puts(s, "\n");
}
@@ -157,6 +196,74 @@ static int wil_ring_seq_open(struct inode *inode, struct 
file *file)
.llseek = seq_lseek,
 };
 
+static void wil_print_sring(struct seq_file *s, struct wil6210_priv *wil,
+   struct wil_status_ring *sring)
+{
+   void __iomem *x = wmi_addr(wil, sring->hwtail);
+   int sring_idx = sring - wil->srings;
+   u32 v;
+
+   seq_printf(s, 

[PATCH v2] rtlwifi: Fix kernel Oops "Fw download fail!!"

2018-06-21 Thread pkshih
From: Ping-Ke Shih 

When connecting to AP, mac80211 asks driver to enter and leave PS quickly,
but driver deinit doesn't wait for delayed work complete when entering PS,
then driver reinit procedure and delay work are running simultaneously.
This will cause unpredictable kernel oops or crash like

rtl8723be: error H2C cmd because of Fw download fail!!!
WARNING: CPU: 3 PID: 159 at drivers/net/wireless/realtek/rtlwifi/
 rtl8723be/fw.c:227 rtl8723be_fill_h2c_cmd+0x182/0x510 [rtl8723be]
CPU: 3 PID: 159 Comm: kworker/3:2 Tainted: G   O 4.16.13-2-ARCH #1
Hardware name: ASUSTeK COMPUTER INC. X556UF/X556UF, BIOS X556UF.406
   10/21/2016
Workqueue: rtl8723be_pci rtl_c2hcmd_wq_callback [rtlwifi]
RIP: 0010:rtl8723be_fill_h2c_cmd+0x182/0x510 [rtl8723be]
RSP: 0018:a6ab01e1bd70 EFLAGS: 00010282
RAX:  RBX: a26069071520 RCX: 0001
RDX: 8001 RSI: 8be70e9c RDI: 
RBP:  R08: 0048 R09: 0348
R10:  R11: 0001 R12: 
R13: a26069071520 R14:  R15: a2607d205f70
FS:  () GS:a26081d8() knlGS:000
CS:  0010 DS:  ES:  CR0: 80050033
CR2: 0443b39d3000 CR3: 00037700a005 CR4: 003606e0
Call Trace:
 ? halbtc_send_bt_mp_operation.constprop.17+0xd5/0xe0 [btcoexist]
 ? ex_btc8723b1ant_bt_info_notify+0x3b8/0x820 [btcoexist]
 ? rtl_c2hcmd_launcher+0xab/0x110 [rtlwifi]
 ? process_one_work+0x1d1/0x3b0
 ? worker_thread+0x2b/0x3d0
 ? process_one_work+0x3b0/0x3b0
 ? kthread+0x112/0x130
 ? kthread_create_on_node+0x60/0x60
 ? ret_from_fork+0x35/0x40
Code: 00 76 b4 e9 e2 fe ff ff 4c 89 ee 4c 89 e7 e8 56 22 86 ca e9 5e ...

This patch ensures all delayed works done before entering PS to satisfy
our expectation, so use cancel_delayed_work_sync() instead. An exception
is delayed work ips_nic_off_wq because running task may be itself, so add
a parameter ips_wq to deinit function to handle this case.

This issue is reported and fixed in below threads:
https://github.com/lwfinger/rtlwifi_new/issues/367
https://github.com/lwfinger/rtlwifi_new/issues/366

Tested-by: Evgeny Kapun  # 8723DE
Tested-by: Shivam Kakkar  # 8723BE on 4.18-rc1
Signed-off-by: Ping-Ke Shih 
Fixes: cceb0a597320 ("rtlwifi: Add work queue for c2h cmd.")
Cc: Stable  # 4.11+
Reviewed-by: Larry Finger 
---
v2: Add Fixes, Cc and Reviewed-by tags suggested by Larry and Arend.
---
 drivers/net/wireless/realtek/rtlwifi/base.c | 17 ++---
 drivers/net/wireless/realtek/rtlwifi/base.h |  2 +-
 drivers/net/wireless/realtek/rtlwifi/core.c |  2 +-
 drivers/net/wireless/realtek/rtlwifi/pci.c  |  2 +-
 drivers/net/wireless/realtek/rtlwifi/ps.c   |  4 ++--
 drivers/net/wireless/realtek/rtlwifi/usb.c  |  2 +-
 6 files changed, 16 insertions(+), 13 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtlwifi/base.c 
b/drivers/net/wireless/realtek/rtlwifi/base.c
index 39c817eddd78..54c9f6ab0c8c 100644
--- a/drivers/net/wireless/realtek/rtlwifi/base.c
+++ b/drivers/net/wireless/realtek/rtlwifi/base.c
@@ -484,18 +484,21 @@ static void _rtl_init_deferred_work(struct ieee80211_hw 
*hw)
 
 }
 
-void rtl_deinit_deferred_work(struct ieee80211_hw *hw)
+void rtl_deinit_deferred_work(struct ieee80211_hw *hw, bool ips_wq)
 {
struct rtl_priv *rtlpriv = rtl_priv(hw);
 
del_timer_sync(>works.watchdog_timer);
 
-   cancel_delayed_work(>works.watchdog_wq);
-   cancel_delayed_work(>works.ips_nic_off_wq);
-   cancel_delayed_work(>works.ps_work);
-   cancel_delayed_work(>works.ps_rfon_wq);
-   cancel_delayed_work(>works.fwevt_wq);
-   cancel_delayed_work(>works.c2hcmd_wq);
+   cancel_delayed_work_sync(>works.watchdog_wq);
+   if (ips_wq)
+   cancel_delayed_work(>works.ips_nic_off_wq);
+   else
+   cancel_delayed_work_sync(>works.ips_nic_off_wq);
+   cancel_delayed_work_sync(>works.ps_work);
+   cancel_delayed_work_sync(>works.ps_rfon_wq);
+   cancel_delayed_work_sync(>works.fwevt_wq);
+   cancel_delayed_work_sync(>works.c2hcmd_wq);
 }
 EXPORT_SYMBOL_GPL(rtl_deinit_deferred_work);
 
diff --git a/drivers/net/wireless/realtek/rtlwifi/base.h 
b/drivers/net/wireless/realtek/rtlwifi/base.h
index 912f205779c3..a7ae40eaa3cd 100644
--- a/drivers/net/wireless/realtek/rtlwifi/base.h
+++ b/drivers/net/wireless/realtek/rtlwifi/base.h
@@ -121,7 +121,7 @@ void rtl_init_rfkill(struct ieee80211_hw *hw);
 void rtl_deinit_rfkill(struct ieee80211_hw *hw);
 
 void rtl_watch_dog_timer_callback(struct timer_list *t);
-void rtl_deinit_deferred_work(struct ieee80211_hw *hw);
+void rtl_deinit_deferred_work(struct ieee80211_hw *hw, bool ips_wq);
 
 bool rtl_action_proc(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx);
 int rtlwifi_rate_mapping(struct ieee80211_hw *hw, bool isht,
diff --git a/drivers/net/wireless/realtek/rtlwifi/core.c 

Re: [PATCH] rtlwifi: Fix kernel Oops "Fw download fail!!"

2018-06-21 Thread Pkshih
On Fri, 2018-06-22 at 07:00 +0200, Arend Van Spriel wrote:
> Op vr 22 jun. 2018 04:27 schreef Pkshih :
> >
> > On Thu, 2018-06-21 at 10:14 -0500, Larry Finger wrote:
> > > On 06/21/2018 02:06 AM, pks...@realtek.com wrote:
> > > > From: Ping-Ke Shih 
> [snip]
> > > > Hi Kalle,
> > > > 
> > > > I'd like to queue this fix to 4.18, because kernel oops in users' 
> > > > laptop.
> > > 
> > > Reviewed by Larry Finger 
> > > 
> > > Is is sufficient for this patch to go to 4.18, or should it be sent to 
> > > stable, 
> > > as well.
> > > 
> >
> > You're right, it should be sent to stable.
> > It had been a potential race condition issue for a long time, but it exposed
> > when delayed work take a long time to process or wait for event.
> > I think this issue would be possibly happened since v4.11 when I move C2H 
> > handler
> > to delayed work.
> Can you provide the commit id for the Fixes: tag?

Sure.

Fixes: cceb0a597320 ("rtlwifi: Add work queue for c2h cmd.")

Thanks for your reminder.
PK



Re: [PATCH] rtlwifi: Fix kernel Oops "Fw download fail!!"

2018-06-21 Thread Pkshih
On Thu, 2018-06-21 at 10:14 -0500, Larry Finger wrote:
> On 06/21/2018 02:06 AM, pks...@realtek.com wrote:
> > From: Ping-Ke Shih 
> > 
> > When connecting to AP, mac80211 asks driver to enter and leave PS quickly,
> > but driver deinit doesn't wait for delayed work complete when entering PS,
> > then driver reinit procedure and delay work are running simultaneously.
> > This will cause unpredictable kernel oops or crash like
> > 
> > rtl8723be: error H2C cmd because of Fw download fail!!!
> > WARNING: CPU: 3 PID: 159 at drivers/net/wireless/realtek/rtlwifi/
> >  rtl8723be/fw.c:227 rtl8723be_fill_h2c_cmd+0x182/0x510 [rtl8723be]
> > CPU: 3 PID: 159 Comm: kworker/3:2 Tainted: G   O 4.16.13-2-ARCH #1
> > Hardware name: ASUSTeK COMPUTER INC. X556UF/X556UF, BIOS X556UF.406
> >    10/21/2016
> > Workqueue: rtl8723be_pci rtl_c2hcmd_wq_callback [rtlwifi]
> > RIP: 0010:rtl8723be_fill_h2c_cmd+0x182/0x510 [rtl8723be]
> > RSP: 0018:a6ab01e1bd70 EFLAGS: 00010282
> > RAX:  RBX: a26069071520 RCX: 0001
> > RDX: 8001 RSI: 8be70e9c RDI: 
> > RBP:  R08: 0048 R09: 0348
> > R10:  R11: 0001 R12: 
> > R13: a26069071520 R14:  R15: a2607d205f70
> > FS:  () GS:a26081d8() knlGS:000
> > CS:  0010 DS:  ES:  CR0: 80050033
> > CR2: 0443b39d3000 CR3: 00037700a005 CR4: 003606e0
> > Call Trace:
> >   ? halbtc_send_bt_mp_operation.constprop.17+0xd5/0xe0 [btcoexist]
> >   ? ex_btc8723b1ant_bt_info_notify+0x3b8/0x820 [btcoexist]
> >   ? rtl_c2hcmd_launcher+0xab/0x110 [rtlwifi]
> >   ? process_one_work+0x1d1/0x3b0
> >   ? worker_thread+0x2b/0x3d0
> >   ? process_one_work+0x3b0/0x3b0
> >   ? kthread+0x112/0x130
> >   ? kthread_create_on_node+0x60/0x60
> >   ? ret_from_fork+0x35/0x40
> > Code: 00 76 b4 e9 e2 fe ff ff 4c 89 ee 4c 89 e7 e8 56 22 86 ca e9 5e ...
> > 
> > This patch ensures all delayed works done before entering PS to satisfy
> > our expectation, so use cancel_delayed_work_sync() instead. An exception
> > is delayed work ips_nic_off_wq because running task may be itself, so add
> > a parameter ips_wq to deinit function to handle this case.
> > 
> > This issue is reported and fixed in below threads:
> > https://github.com/lwfinger/rtlwifi_new/issues/367
> > https://github.com/lwfinger/rtlwifi_new/issues/366
> > 
> > Tested-by: Evgeny Kapun  # 8723DE
> > Tested-by: Shivam Kakkar  # 8723BE on 4.18-rc1
> > Signed-off-by: Ping-Ke Shih 
> > ---
> > Hi Kalle,
> > 
> > I'd like to queue this fix to 4.18, because kernel oops in users' laptop.
> 
> Reviewed by Larry Finger 
> 
> Is is sufficient for this patch to go to 4.18, or should it be sent to 
> stable, 
> as well.
> 

You're right, it should be sent to stable.
It had been a potential race condition issue for a long time, but it exposed
when delayed work take a long time to process or wait for event.
I think this issue would be possibly happened since v4.11 when I move C2H 
handler
to delayed work.

Cc: Stable  # 4.11+

PK



hi

2018-06-21 Thread Dr.Abdul Fha
Dear
I am.Dr.Abdul Fha,Manager Auditing and Accountancy Department,Ecobank,
Burkina Faso.I have a business proposal for you in the tune of 3.5
million usd.If you know you are capable of involving and partaking in 
this transaction this will be disbursed or shared between the both of 
us. email me so that i will give you all the informetion of it (dr.
abdulfh...@gmail.com) 
Regards,
Dr.Abdul fha,


Re: [PATCH] rtlwifi: Fix kernel Oops "Fw download fail!!"

2018-06-21 Thread Larry Finger

On 06/21/2018 02:06 AM, pks...@realtek.com wrote:

From: Ping-Ke Shih 

When connecting to AP, mac80211 asks driver to enter and leave PS quickly,
but driver deinit doesn't wait for delayed work complete when entering PS,
then driver reinit procedure and delay work are running simultaneously.
This will cause unpredictable kernel oops or crash like

rtl8723be: error H2C cmd because of Fw download fail!!!
WARNING: CPU: 3 PID: 159 at drivers/net/wireless/realtek/rtlwifi/
 rtl8723be/fw.c:227 rtl8723be_fill_h2c_cmd+0x182/0x510 [rtl8723be]
CPU: 3 PID: 159 Comm: kworker/3:2 Tainted: G   O 4.16.13-2-ARCH #1
Hardware name: ASUSTeK COMPUTER INC. X556UF/X556UF, BIOS X556UF.406
   10/21/2016
Workqueue: rtl8723be_pci rtl_c2hcmd_wq_callback [rtlwifi]
RIP: 0010:rtl8723be_fill_h2c_cmd+0x182/0x510 [rtl8723be]
RSP: 0018:a6ab01e1bd70 EFLAGS: 00010282
RAX:  RBX: a26069071520 RCX: 0001
RDX: 8001 RSI: 8be70e9c RDI: 
RBP:  R08: 0048 R09: 0348
R10:  R11: 0001 R12: 
R13: a26069071520 R14:  R15: a2607d205f70
FS:  () GS:a26081d8() knlGS:000
CS:  0010 DS:  ES:  CR0: 80050033
CR2: 0443b39d3000 CR3: 00037700a005 CR4: 003606e0
Call Trace:
  ? halbtc_send_bt_mp_operation.constprop.17+0xd5/0xe0 [btcoexist]
  ? ex_btc8723b1ant_bt_info_notify+0x3b8/0x820 [btcoexist]
  ? rtl_c2hcmd_launcher+0xab/0x110 [rtlwifi]
  ? process_one_work+0x1d1/0x3b0
  ? worker_thread+0x2b/0x3d0
  ? process_one_work+0x3b0/0x3b0
  ? kthread+0x112/0x130
  ? kthread_create_on_node+0x60/0x60
  ? ret_from_fork+0x35/0x40
Code: 00 76 b4 e9 e2 fe ff ff 4c 89 ee 4c 89 e7 e8 56 22 86 ca e9 5e ...

This patch ensures all delayed works done before entering PS to satisfy
our expectation, so use cancel_delayed_work_sync() instead. An exception
is delayed work ips_nic_off_wq because running task may be itself, so add
a parameter ips_wq to deinit function to handle this case.

This issue is reported and fixed in below threads:
https://github.com/lwfinger/rtlwifi_new/issues/367
https://github.com/lwfinger/rtlwifi_new/issues/366

Tested-by: Evgeny Kapun  # 8723DE
Tested-by: Shivam Kakkar  # 8723BE on 4.18-rc1
Signed-off-by: Ping-Ke Shih 
---
Hi Kalle,

I'd like to queue this fix to 4.18, because kernel oops in users' laptop.


Reviewed by Larry Finger 

Is is sufficient for this patch to go to 4.18, or should it be sent to stable, 
as well.


Larry


Re: Atheros AR9462 - 5Ghz not working

2018-06-21 Thread Tom Psyborg
with mPCIe version i got one could only read eeprom but not write, or
the information presented were wrong..

On 21/06/2018, mgre...@cinci.rr.com  wrote:
>
>  Arend van Spriel  wrote:
>> + Martin
>>
>> On 6/18/2018 3:53 PM, mgre...@cinci.rr.com wrote:
>> >
>> >  Tom Psyborg  wrote:
>> >> Hi
>> >>
>> >> Your log only show attemps on ch 2447, Can you try connecting to 5GHz
>> >> AP? Connect to Hidden Wireless Network option at the bottom of the
>> >> nm-applet? Running airodump in monitor mode to see if it captures
>> >> anything? Maybe your laptop's antennas were designed for 2.4G card
>> >> only but this just dumb guessing...
>> >>
>> >
>> > It won't connect to any 5GHz AP.
>> > I don't run network manager or Gnome, in fact X11 is not installed on
>> > this machine at all.
>> > I don't think there is such a thing as 2.4GHz vs 5GHz antennas.
>>
>> Actually there is. At least there are 2.4G specific antennas and
>> dual-band antennas.
>>
>> In 4.11 there have been a couple eeprom related changes dealing with
>> endianness of fields in eeprom. Could be those cause a regression for
>> you. I don't have the exact sha id of those commits, but I added the
>> author to this thread.
>>
>> Regards,
>> Arend
>>
>
> An interesting new development:
>
> I've confirmed it's not the antenna as I was able to acquire some more cards
> from a different source which work fine.
>
> Ostensibly they are the same card:  Atheros AR9462 : QCNFA222 reference
> design.
>
> The output from lspci for both working and non-working cards is: "03:00.0
> Network controller: Qualcomm Atheros AR9462 Wireless Network Adapter".
>
> However the Subsystem listed in lspci is different. I don't have the exact
> value at the moment for the working cards, but it lists Dell and the part
> number DW1802. The non-working cards are AzureWave.
>
> I've seen a lot of similar cards on ebay that I suspect are the AzureWave. A
> few offers on ebay are for the genuine Dell parts.
>
> What would be the best way to compare and contrast these cards to determine
> if there is a way to make them work?
>
> Inspect EEPROM? Someone previously mentioned calibration data, but I have no
> idea if it's possible to inspect or alter that.
>


Re: Atheros AR9462 - 5Ghz not working

2018-06-21 Thread mgreger


 Arend van Spriel  wrote: 
> + Martin
> 
> On 6/18/2018 3:53 PM, mgre...@cinci.rr.com wrote:
> >
> >  Tom Psyborg  wrote:
> >> Hi
> >>
> >> Your log only show attemps on ch 2447, Can you try connecting to 5GHz
> >> AP? Connect to Hidden Wireless Network option at the bottom of the
> >> nm-applet? Running airodump in monitor mode to see if it captures
> >> anything? Maybe your laptop's antennas were designed for 2.4G card
> >> only but this just dumb guessing...
> >>
> >
> > It won't connect to any 5GHz AP.
> > I don't run network manager or Gnome, in fact X11 is not installed on this 
> > machine at all.
> > I don't think there is such a thing as 2.4GHz vs 5GHz antennas.
> 
> Actually there is. At least there are 2.4G specific antennas and 
> dual-band antennas.
> 
> In 4.11 there have been a couple eeprom related changes dealing with 
> endianness of fields in eeprom. Could be those cause a regression for 
> you. I don't have the exact sha id of those commits, but I added the 
> author to this thread.
> 
> Regards,
> Arend
> 

An interesting new development:

I've confirmed it's not the antenna as I was able to acquire some more cards 
from a different source which work fine.

Ostensibly they are the same card:  Atheros AR9462 : QCNFA222 reference design.

The output from lspci for both working and non-working cards is: "03:00.0 
Network controller: Qualcomm Atheros AR9462 Wireless Network Adapter".

However the Subsystem listed in lspci is different. I don't have the exact 
value at the moment for the working cards, but it lists Dell and the part 
number DW1802. The non-working cards are AzureWave.

I've seen a lot of similar cards on ebay that I suspect are the AzureWave. A 
few offers on ebay are for the genuine Dell parts.

What would be the best way to compare and contrast these cards to determine if 
there is a way to make them work?

Inspect EEPROM? Someone previously mentioned calibration data, but I have no 
idea if it's possible to inspect or alter that.


[PATCH 8/9] mt76: track ewma rssi for gain adjustment per station

2018-06-21 Thread Felix Fietkau
This preserves more sensitivity when weak stations are active and avoids
counting signal measurements from other unrelated networks

Signed-off-by: Felix Fietkau 
---
 drivers/net/wireless/mediatek/mt76/mt76x2.h   | 10 +++-
 .../net/wireless/mediatek/mt76/mt76x2_mac.c   | 33 ---
 .../net/wireless/mediatek/mt76/mt76x2_main.c  |  2 +
 .../net/wireless/mediatek/mt76/mt76x2_phy.c   | 58 ---
 4 files changed, 83 insertions(+), 20 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2.h 
b/drivers/net/wireless/mediatek/mt76/mt76x2.h
index 21de1168076d..71fcfa44fb2e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2.h
@@ -27,6 +27,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #define MT7662_FIRMWARE"mt7662.bin"
 #define MT7662_ROM_PATCH   "mt7662_rom_patch.bin"
@@ -47,6 +48,8 @@
 #include "mt76x2_mac.h"
 #include "mt76x2_dfs.h"
 
+DECLARE_EWMA(signal, 10, 8)
+
 struct mt76x2_mcu {
struct mutex mutex;
 
@@ -69,10 +72,8 @@ struct mt76x2_calibration {
u8 agc_gain_init[MT_MAX_CHAINS];
u8 agc_gain_cur[MT_MAX_CHAINS];
 
-   int avg_rssi[MT_MAX_CHAINS];
-   int avg_rssi_all;
-
u16 false_cca;
+   s8 avg_rssi_all;
s8 agc_gain_adjust;
s8 low_gain;
 
@@ -153,6 +154,9 @@ struct mt76x2_sta {
struct mt76x2_vif *vif;
struct mt76x2_tx_status status;
int n_frames;
+
+   struct ewma_signal rssi;
+   int inactive_count;
 };
 
 static inline bool is_mt7612(struct mt76x2_dev *dev)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c 
b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
index b49aea4da2d6..f34b4d6413e3 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
@@ -269,21 +269,31 @@ static void mt76x2_remove_hdr_pad(struct sk_buff *skb, 
int len)
skb_pull(skb, len);
 }
 
-static struct mt76_wcid *
-mt76x2_rx_get_sta_wcid(struct mt76x2_dev *dev, u8 idx, bool unicast)
+static struct mt76x2_sta *
+mt76x2_rx_get_sta(struct mt76x2_dev *dev, u8 idx)
 {
-   struct mt76x2_sta *sta;
struct mt76_wcid *wcid;
 
if (idx >= ARRAY_SIZE(dev->wcid))
return NULL;
 
wcid = rcu_dereference(dev->wcid[idx]);
-   if (unicast || !wcid)
-   return wcid;
+   if (!wcid)
+   return NULL;
 
-   sta = container_of(wcid, struct mt76x2_sta, wcid);
-   return >vif->group_wcid;
+   return container_of(wcid, struct mt76x2_sta, wcid);
+}
+
+static struct mt76_wcid *
+mt76x2_rx_get_sta_wcid(struct mt76x2_dev *dev, struct mt76x2_sta *sta, bool 
unicast)
+{
+   if (!sta)
+   return NULL;
+
+   if (unicast)
+   return >wcid;
+   else
+   return >vif->group_wcid;
 }
 
 int mt76x2_mac_process_rx(struct mt76x2_dev *dev, struct sk_buff *skb,
@@ -291,6 +301,7 @@ int mt76x2_mac_process_rx(struct mt76x2_dev *dev, struct 
sk_buff *skb,
 {
struct mt76_rx_status *status = (struct mt76_rx_status *) skb->cb;
struct mt76x2_rxwi *rxwi = rxi;
+   struct mt76x2_sta *sta;
u32 rxinfo = le32_to_cpu(rxwi->rxinfo);
u32 ctl = le32_to_cpu(rxwi->ctl);
u16 rate = le16_to_cpu(rxwi->rate);
@@ -315,7 +326,8 @@ int mt76x2_mac_process_rx(struct mt76x2_dev *dev, struct 
sk_buff *skb,
}
 
wcid = FIELD_GET(MT_RXWI_CTL_WCID, ctl);
-   status->wcid = mt76x2_rx_get_sta_wcid(dev, wcid, unicast);
+   sta = mt76x2_rx_get_sta(dev, wcid);
+   status->wcid = mt76x2_rx_get_sta_wcid(dev, sta, unicast);
 
len = FIELD_GET(MT_RXWI_CTL_MPDU_LEN, ctl);
pn_len = FIELD_GET(MT_RXINFO_PN_LEN, rxinfo);
@@ -361,6 +373,11 @@ int mt76x2_mac_process_rx(struct mt76x2_dev *dev, struct 
sk_buff *skb,
status->tid = FIELD_GET(MT_RXWI_TID, tid_sn);
status->seqno = FIELD_GET(MT_RXWI_SN, tid_sn);
 
+   if (sta) {
+   ewma_signal_add(>rssi, status->signal);
+   sta->inactive_count = 0;
+   }
+
return mt76x2_mac_process_rate(status, rate);
 }
 
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_main.c 
b/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
index e4e41faf6739..3c0ebe6d231c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
@@ -294,6 +294,8 @@ mt76x2_sta_add(struct ieee80211_hw *hw, struct 
ieee80211_vif *vif,
if (vif->type == NL80211_IFTYPE_AP)
set_bit(MT_WCID_FLAG_CHECK_PS, >wcid.flags);
 
+   ewma_signal_init(>rssi);
+
rcu_assign_pointer(dev->wcid[idx], >wcid);
 
 out:
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c 
b/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
index 94943aeb249d..14aedc3ef731 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
@@ -499,22 +499,62 @@ 

[PATCH 2/9] mt76: fix threshold for gain adjustment

2018-06-21 Thread Felix Fietkau
The gain should be reduced only for very strong connections, not for mid
range.

Signed-off-by: Felix Fietkau 
---
 drivers/net/wireless/mediatek/mt76/mt76x2_phy.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c 
b/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
index c1c38ca3330a..4ed6641c3a32 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
@@ -530,7 +530,7 @@ mt76x2_phy_update_channel_gain(struct mt76x2_dev *dev)
else
mt76_wr(dev, MT_BBP(RXO, 14), 0x00560423);
 
-   if (low_gain) {
+   if (low_gain == 2) {
mt76_wr(dev, MT_BBP(RXO, 18), 0xf000a991);
mt76_wr(dev, MT_BBP(AGC, 35), 0x08080808);
mt76_wr(dev, MT_BBP(AGC, 37), 0x08080808);
-- 
2.17.0



[PATCH 5/9] mt76: clear false CCA counters after changing gain settings

2018-06-21 Thread Felix Fietkau
They will be read on the next calibration step without gain change and
must not count earlier events

Signed-off-by: Felix Fietkau 
---
 drivers/net/wireless/mediatek/mt76/mt76x2_phy.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c 
b/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
index a2c3f0e35f86..51fd7ddfa8f5 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
@@ -559,6 +559,9 @@ mt76x2_phy_update_channel_gain(struct mt76x2_dev *dev)
dev->cal.agc_gain_cur[1] = gain[1] - gain_delta;
dev->cal.agc_gain_adjust = 0;
mt76x2_phy_set_gain_val(dev);
+
+   /* clear false CCA counters */
+   mt76_rr(dev, MT_RX_STAT_1);
 }
 
 int mt76x2_phy_set_channel(struct mt76x2_dev *dev,
-- 
2.17.0



[PATCH 9/9] mt76: improve gain adjustment in noisy environments

2018-06-21 Thread Felix Fietkau
When switching between low gain (high RSSI) and high gain settings, it
can take a few seconds to adjust to the current environment.
This can lead to short periods of time with extreme packet loss.

When switching from low_gain=1 to low_gain=2, start with the same gain
adjustment value instead of the lowest to avoid spikes of huge numbers
of false CCA events

Also avoid resetting adjustment values on switching between low_gain
values 0 and 1, since it affects only the upper limit of vga adjustment

Signed-off-by: Felix Fietkau 
---
 .../net/wireless/mediatek/mt76/mt76x2_phy.c   | 26 ---
 1 file changed, 16 insertions(+), 10 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c 
b/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
index 14aedc3ef731..20ffa6a40d39 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
@@ -491,7 +491,8 @@ mt76x2_phy_adjust_vga_gain(struct mt76x2_dev *dev)
dev->cal.false_cca = false_cca;
if (false_cca > 800 && dev->cal.agc_gain_adjust < limit)
dev->cal.agc_gain_adjust += 2;
-   else if (false_cca < 10 && dev->cal.agc_gain_adjust > 0)
+   else if ((false_cca < 10 && dev->cal.agc_gain_adjust > 0) ||
+(dev->cal.agc_gain_adjust >= limit && false_cca < 500))
dev->cal.agc_gain_adjust -= 2;
else
return;
@@ -550,7 +551,8 @@ static void
 mt76x2_phy_update_channel_gain(struct mt76x2_dev *dev)
 {
u8 *gain = dev->cal.agc_gain_init;
-   u8 gain_delta;
+   u8 low_gain_delta, gain_delta;
+   bool gain_change;
int low_gain;
u32 val;
 
@@ -559,13 +561,14 @@ mt76x2_phy_update_channel_gain(struct mt76x2_dev *dev)
low_gain = (dev->cal.avg_rssi_all > mt76x2_get_rssi_gain_thresh(dev)) +
   (dev->cal.avg_rssi_all > 
mt76x2_get_low_rssi_gain_thresh(dev));
 
-   if (dev->cal.low_gain == low_gain) {
+   gain_change = (dev->cal.low_gain & 2) ^ (low_gain & 2);
+   dev->cal.low_gain = low_gain;
+
+   if (!gain_change) {
mt76x2_phy_adjust_vga_gain(dev);
return;
}
 
-   dev->cal.low_gain = low_gain;
-
if (dev->mt76.chandef.width == NL80211_CHAN_WIDTH_80) {
mt76_wr(dev, MT_BBP(RXO, 14), 0x00560211);
val = mt76_rr(dev, MT_BBP(AGC, 26)) & ~0xf;
@@ -578,14 +581,17 @@ mt76x2_phy_update_channel_gain(struct mt76x2_dev *dev)
mt76_wr(dev, MT_BBP(RXO, 14), 0x00560423);
}
 
+   if (mt76x2_has_ext_lna(dev))
+   low_gain_delta = 10;
+   else
+   low_gain_delta = 14;
+
if (low_gain == 2) {
mt76_wr(dev, MT_BBP(RXO, 18), 0xf000a990);
mt76_wr(dev, MT_BBP(AGC, 35), 0x08080808);
mt76_wr(dev, MT_BBP(AGC, 37), 0x08080808);
-   if (mt76x2_has_ext_lna(dev))
-   gain_delta = 10;
-   else
-   gain_delta = 14;
+   gain_delta = low_gain_delta;
+   dev->cal.agc_gain_adjust = 0;
} else {
mt76_wr(dev, MT_BBP(RXO, 18), 0xf000a991);
if (dev->mt76.chandef.width == NL80211_CHAN_WIDTH_80)
@@ -594,11 +600,11 @@ mt76x2_phy_update_channel_gain(struct mt76x2_dev *dev)
mt76_wr(dev, MT_BBP(AGC, 35), 0x1116);
mt76_wr(dev, MT_BBP(AGC, 37), 0x2121262C);
gain_delta = 0;
+   dev->cal.agc_gain_adjust = low_gain_delta;
}
 
dev->cal.agc_gain_cur[0] = gain[0] - gain_delta;
dev->cal.agc_gain_cur[1] = gain[1] - gain_delta;
-   dev->cal.agc_gain_adjust = 0;
mt76x2_phy_set_gain_val(dev);
 
/* clear false CCA counters */
-- 
2.17.0



[PATCH 6/9] mt76: fix variable gain adjustment range

2018-06-21 Thread Felix Fietkau
The range should only be limited to 4 for really weak signals, for all
other gain settings the range is 16.

Signed-off-by: Felix Fietkau 
---
 drivers/net/wireless/mediatek/mt76/mt76x2_phy.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c 
b/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
index 51fd7ddfa8f5..9c7b19ce73f2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
@@ -485,7 +485,7 @@ static void
 mt76x2_phy_adjust_vga_gain(struct mt76x2_dev *dev)
 {
u32 false_cca;
-   u8 limit = dev->cal.low_gain > 1 ? 4 : 16;
+   u8 limit = dev->cal.low_gain > 0 ? 16 : 4;
 
false_cca = FIELD_GET(MT_RX_STAT_1_CCA_ERRORS, mt76_rr(dev, 
MT_RX_STAT_1));
if (false_cca > 800 && dev->cal.agc_gain_adjust < limit)
-- 
2.17.0



[PATCH 3/9] mt76: fix swapped values for RXO-18 in gain control

2018-06-21 Thread Felix Fietkau
The lowest bit should be set to 0 only for strong links, not for weak
ones.

Signed-off-by: Felix Fietkau 
---
 drivers/net/wireless/mediatek/mt76/mt76x2_phy.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c 
b/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
index 4ed6641c3a32..a510f116d52a 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
@@ -531,7 +531,7 @@ mt76x2_phy_update_channel_gain(struct mt76x2_dev *dev)
mt76_wr(dev, MT_BBP(RXO, 14), 0x00560423);
 
if (low_gain == 2) {
-   mt76_wr(dev, MT_BBP(RXO, 18), 0xf000a991);
+   mt76_wr(dev, MT_BBP(RXO, 18), 0xf000a990);
mt76_wr(dev, MT_BBP(AGC, 35), 0x08080808);
mt76_wr(dev, MT_BBP(AGC, 37), 0x08080808);
if (mt76x2_has_ext_lna(dev))
@@ -539,7 +539,7 @@ mt76x2_phy_update_channel_gain(struct mt76x2_dev *dev)
else
gain_delta = 14;
} else {
-   mt76_wr(dev, MT_BBP(RXO, 18), 0xf000a990);
+   mt76_wr(dev, MT_BBP(RXO, 18), 0xf000a991);
if (dev->mt76.chandef.width == NL80211_CHAN_WIDTH_80)
mt76_wr(dev, MT_BBP(AGC, 35), 0x10101014);
else
-- 
2.17.0



[PATCH 7/9] mt76: add a debugfs file to dump agc calibration information

2018-06-21 Thread Felix Fietkau
Useful for debugging gain adjustment issues triggered by signal strength
changes.

Signed-off-by: Felix Fietkau 
---
 drivers/net/wireless/mediatek/mt76/mt76x2.h|  1 +
 .../net/wireless/mediatek/mt76/mt76x2_debugfs.c| 14 ++
 drivers/net/wireless/mediatek/mt76/mt76x2_phy.c|  1 +
 3 files changed, 16 insertions(+)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2.h 
b/drivers/net/wireless/mediatek/mt76/mt76x2.h
index 06ca5a77dfdf..21de1168076d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2.h
@@ -72,6 +72,7 @@ struct mt76x2_calibration {
int avg_rssi[MT_MAX_CHAINS];
int avg_rssi_all;
 
+   u16 false_cca;
s8 agc_gain_adjust;
s8 low_gain;
 
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_debugfs.c 
b/drivers/net/wireless/mediatek/mt76/mt76x2_debugfs.c
index 955ea3e692dd..3f86e01049f3 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_debugfs.c
@@ -115,6 +115,18 @@ static const struct file_operations fops_dfs_stat = {
.release = single_release,
 };
 
+static int read_agc(struct seq_file *file, void *data)
+{
+   struct mt76x2_dev *dev = dev_get_drvdata(file->private);
+
+   seq_printf(file, "avg_rssi: %d\n", dev->cal.avg_rssi_all);
+   seq_printf(file, "low_gain: %d\n", dev->cal.low_gain);
+   seq_printf(file, "false_cca: %d\n", dev->cal.false_cca);
+   seq_printf(file, "agc_gain_adjust: %d\n", dev->cal.agc_gain_adjust);
+
+   return 0;
+}
+
 void mt76x2_init_debugfs(struct mt76x2_dev *dev)
 {
struct dentry *dir;
@@ -130,4 +142,6 @@ void mt76x2_init_debugfs(struct mt76x2_dev *dev)
debugfs_create_file("dfs_stats", 0400, dir, dev, _dfs_stat);
debugfs_create_devm_seqfile(dev->mt76.dev, "txpower", dir,
read_txpower);
+
+   debugfs_create_devm_seqfile(dev->mt76.dev, "agc", dir, read_agc);
 }
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c 
b/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
index 9c7b19ce73f2..94943aeb249d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
@@ -488,6 +488,7 @@ mt76x2_phy_adjust_vga_gain(struct mt76x2_dev *dev)
u8 limit = dev->cal.low_gain > 0 ? 16 : 4;
 
false_cca = FIELD_GET(MT_RX_STAT_1_CCA_ERRORS, mt76_rr(dev, 
MT_RX_STAT_1));
+   dev->cal.false_cca = false_cca;
if (false_cca > 800 && dev->cal.agc_gain_adjust < limit)
dev->cal.agc_gain_adjust += 2;
else if (false_cca < 10 && dev->cal.agc_gain_adjust > 0)
-- 
2.17.0



[PATCH 1/9] mt76: fix beacon timer drift

2018-06-21 Thread Felix Fietkau
The beacon timer drifts by 1 microsecond every TBTT. After 20 minutes
with a beacon interval of 100, the drift will be almost 12 ms, enough to
cause weird issues for devices in powersave mode.

Since the beacon timer is configured in units of 1/16 TU (64 us), we
need to adjust it once every 64 beacons and only for one beacon.

Signed-off-by: Felix Fietkau 
---
 drivers/net/wireless/mediatek/mt76/mt76x2.h   |  5 ++-
 .../net/wireless/mediatek/mt76/mt76x2_main.c  |  5 ++-
 .../net/wireless/mediatek/mt76/mt76x2_tx.c| 33 +++
 3 files changed, 41 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2.h 
b/drivers/net/wireless/mediatek/mt76/mt76x2.h
index dc12bbdbb2ee..06ca5a77dfdf 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2.h
@@ -120,10 +120,13 @@ struct mt76x2_dev {
u8 beacon_mask;
u8 beacon_data_mask;
 
-   u32 rxfilter;
+   u8 tbtt_count;
+   u16 beacon_int;
 
u16 chainmask;
 
+   u32 rxfilter;
+
struct mt76x2_calibration cal;
 
s8 target_power;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_main.c 
b/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
index ce90ff999b49..e4e41faf6739 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
@@ -238,10 +238,13 @@ mt76x2_bss_info_changed(struct ieee80211_hw *hw, struct 
ieee80211_vif *vif,
if (changed & BSS_CHANGED_BSSID)
mt76x2_mac_set_bssid(dev, mvif->idx, info->bssid);
 
-   if (changed & BSS_CHANGED_BEACON_INT)
+   if (changed & BSS_CHANGED_BEACON_INT) {
mt76_rmw_field(dev, MT_BEACON_TIME_CFG,
   MT_BEACON_TIME_CFG_INTVAL,
   info->beacon_int << 4);
+   dev->beacon_int = info->beacon_int;
+   dev->tbtt_count = 0;
+   }
 
if (changed & BSS_CHANGED_BEACON_ENABLED) {
tasklet_disable(>pre_tbtt_tasklet);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_tx.c 
b/drivers/net/wireless/mediatek/mt76/mt76x2_tx.c
index e46eafc4c436..560376dd1133 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_tx.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_tx.c
@@ -218,6 +218,37 @@ mt76x2_add_buffered_bc(void *priv, u8 *mac, struct 
ieee80211_vif *vif)
data->tail[mvif->idx] = skb;
 }
 
+static void
+mt76x2_resync_beacon_timer(struct mt76x2_dev *dev)
+{
+   u32 timer_val = dev->beacon_int << 4;
+
+   dev->tbtt_count++;
+
+   /*
+* Beacon timer drifts by 1us every tick, the timer is configured
+* in 1/16 TU (64us) units.
+*/
+   if (dev->tbtt_count < 62)
+   return;
+
+   if (dev->tbtt_count >= 64) {
+   dev->tbtt_count = 0;
+   return;
+   }
+
+   /*
+* The updated beacon interval takes effect after two TBTT, because
+* at this point the original interval has already been loaded into
+* the next TBTT_TIMER value
+*/
+   if (dev->tbtt_count == 62)
+   timer_val -= 1;
+
+   mt76_rmw_field(dev, MT_BEACON_TIME_CFG,
+  MT_BEACON_TIME_CFG_INTVAL, timer_val);
+}
+
 void mt76x2_pre_tbtt_tasklet(unsigned long arg)
 {
struct mt76x2_dev *dev = (struct mt76x2_dev *) arg;
@@ -226,6 +257,8 @@ void mt76x2_pre_tbtt_tasklet(unsigned long arg)
struct sk_buff *skb;
int i, nframes;
 
+   mt76x2_resync_beacon_timer(dev);
+
data.dev = dev;
__skb_queue_head_init();
 
-- 
2.17.0



[PATCH 4/9] mt76: adjust AGC control register 26 based on gain for VHT80

2018-06-21 Thread Felix Fietkau
Use values based on the vendor driver

Signed-off-by: Felix Fietkau 
---
 drivers/net/wireless/mediatek/mt76/mt76x2_phy.c | 11 +--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c 
b/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
index a510f116d52a..a2c3f0e35f86 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
@@ -525,10 +525,17 @@ mt76x2_phy_update_channel_gain(struct mt76x2_dev *dev)
 
dev->cal.low_gain = low_gain;
 
-   if (dev->mt76.chandef.width == NL80211_CHAN_WIDTH_80)
+   if (dev->mt76.chandef.width == NL80211_CHAN_WIDTH_80) {
mt76_wr(dev, MT_BBP(RXO, 14), 0x00560211);
-   else
+   val = mt76_rr(dev, MT_BBP(AGC, 26)) & ~0xf;
+   if (low_gain == 2)
+   val |= 0x3;
+   else
+   val |= 0x5;
+   mt76_wr(dev, MT_BBP(AGC, 26), val);
+   } else {
mt76_wr(dev, MT_BBP(RXO, 14), 0x00560423);
+   }
 
if (low_gain == 2) {
mt76_wr(dev, MT_BBP(RXO, 18), 0xf000a990);
-- 
2.17.0



[PATCH] rtlwifi: Fix kernel Oops "Fw download fail!!"

2018-06-21 Thread pkshih
From: Ping-Ke Shih 

When connecting to AP, mac80211 asks driver to enter and leave PS quickly,
but driver deinit doesn't wait for delayed work complete when entering PS,
then driver reinit procedure and delay work are running simultaneously.
This will cause unpredictable kernel oops or crash like

rtl8723be: error H2C cmd because of Fw download fail!!!
WARNING: CPU: 3 PID: 159 at drivers/net/wireless/realtek/rtlwifi/
 rtl8723be/fw.c:227 rtl8723be_fill_h2c_cmd+0x182/0x510 [rtl8723be]
CPU: 3 PID: 159 Comm: kworker/3:2 Tainted: G   O 4.16.13-2-ARCH #1
Hardware name: ASUSTeK COMPUTER INC. X556UF/X556UF, BIOS X556UF.406
   10/21/2016
Workqueue: rtl8723be_pci rtl_c2hcmd_wq_callback [rtlwifi]
RIP: 0010:rtl8723be_fill_h2c_cmd+0x182/0x510 [rtl8723be]
RSP: 0018:a6ab01e1bd70 EFLAGS: 00010282
RAX:  RBX: a26069071520 RCX: 0001
RDX: 8001 RSI: 8be70e9c RDI: 
RBP:  R08: 0048 R09: 0348
R10:  R11: 0001 R12: 
R13: a26069071520 R14:  R15: a2607d205f70
FS:  () GS:a26081d8() knlGS:000
CS:  0010 DS:  ES:  CR0: 80050033
CR2: 0443b39d3000 CR3: 00037700a005 CR4: 003606e0
Call Trace:
 ? halbtc_send_bt_mp_operation.constprop.17+0xd5/0xe0 [btcoexist]
 ? ex_btc8723b1ant_bt_info_notify+0x3b8/0x820 [btcoexist]
 ? rtl_c2hcmd_launcher+0xab/0x110 [rtlwifi]
 ? process_one_work+0x1d1/0x3b0
 ? worker_thread+0x2b/0x3d0
 ? process_one_work+0x3b0/0x3b0
 ? kthread+0x112/0x130
 ? kthread_create_on_node+0x60/0x60
 ? ret_from_fork+0x35/0x40
Code: 00 76 b4 e9 e2 fe ff ff 4c 89 ee 4c 89 e7 e8 56 22 86 ca e9 5e ...

This patch ensures all delayed works done before entering PS to satisfy
our expectation, so use cancel_delayed_work_sync() instead. An exception
is delayed work ips_nic_off_wq because running task may be itself, so add
a parameter ips_wq to deinit function to handle this case.

This issue is reported and fixed in below threads:
https://github.com/lwfinger/rtlwifi_new/issues/367
https://github.com/lwfinger/rtlwifi_new/issues/366

Tested-by: Evgeny Kapun  # 8723DE
Tested-by: Shivam Kakkar  # 8723BE on 4.18-rc1
Signed-off-by: Ping-Ke Shih 
---
Hi Kalle,

I'd like to queue this fix to 4.18, because kernel oops in users' laptop.

Thanks
PK

---
 drivers/net/wireless/realtek/rtlwifi/base.c | 17 ++---
 drivers/net/wireless/realtek/rtlwifi/base.h |  2 +-
 drivers/net/wireless/realtek/rtlwifi/core.c |  2 +-
 drivers/net/wireless/realtek/rtlwifi/pci.c  |  2 +-
 drivers/net/wireless/realtek/rtlwifi/ps.c   |  4 ++--
 drivers/net/wireless/realtek/rtlwifi/usb.c  |  2 +-
 6 files changed, 16 insertions(+), 13 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtlwifi/base.c 
b/drivers/net/wireless/realtek/rtlwifi/base.c
index 39c817eddd78..54c9f6ab0c8c 100644
--- a/drivers/net/wireless/realtek/rtlwifi/base.c
+++ b/drivers/net/wireless/realtek/rtlwifi/base.c
@@ -484,18 +484,21 @@ static void _rtl_init_deferred_work(struct ieee80211_hw 
*hw)
 
 }
 
-void rtl_deinit_deferred_work(struct ieee80211_hw *hw)
+void rtl_deinit_deferred_work(struct ieee80211_hw *hw, bool ips_wq)
 {
struct rtl_priv *rtlpriv = rtl_priv(hw);
 
del_timer_sync(>works.watchdog_timer);
 
-   cancel_delayed_work(>works.watchdog_wq);
-   cancel_delayed_work(>works.ips_nic_off_wq);
-   cancel_delayed_work(>works.ps_work);
-   cancel_delayed_work(>works.ps_rfon_wq);
-   cancel_delayed_work(>works.fwevt_wq);
-   cancel_delayed_work(>works.c2hcmd_wq);
+   cancel_delayed_work_sync(>works.watchdog_wq);
+   if (ips_wq)
+   cancel_delayed_work(>works.ips_nic_off_wq);
+   else
+   cancel_delayed_work_sync(>works.ips_nic_off_wq);
+   cancel_delayed_work_sync(>works.ps_work);
+   cancel_delayed_work_sync(>works.ps_rfon_wq);
+   cancel_delayed_work_sync(>works.fwevt_wq);
+   cancel_delayed_work_sync(>works.c2hcmd_wq);
 }
 EXPORT_SYMBOL_GPL(rtl_deinit_deferred_work);
 
diff --git a/drivers/net/wireless/realtek/rtlwifi/base.h 
b/drivers/net/wireless/realtek/rtlwifi/base.h
index 912f205779c3..a7ae40eaa3cd 100644
--- a/drivers/net/wireless/realtek/rtlwifi/base.h
+++ b/drivers/net/wireless/realtek/rtlwifi/base.h
@@ -121,7 +121,7 @@ void rtl_init_rfkill(struct ieee80211_hw *hw);
 void rtl_deinit_rfkill(struct ieee80211_hw *hw);
 
 void rtl_watch_dog_timer_callback(struct timer_list *t);
-void rtl_deinit_deferred_work(struct ieee80211_hw *hw);
+void rtl_deinit_deferred_work(struct ieee80211_hw *hw, bool ips_wq);
 
 bool rtl_action_proc(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx);
 int rtlwifi_rate_mapping(struct ieee80211_hw *hw, bool isht,
diff --git a/drivers/net/wireless/realtek/rtlwifi/core.c 
b/drivers/net/wireless/realtek/rtlwifi/core.c
index cfea57efa7f4..a3f46203ee7a 100644
---