mac80211: rt2x00: add experimental patches from Stanislaw Gruszka
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
This commit is contained in:
parent
c837c41a76
commit
de1c58a64b
5 changed files with 1067 additions and 0 deletions
|
@ -0,0 +1,365 @@
|
||||||
|
From 0381bfbc400ce4c3000b0b31c577ad9e8071e4f6 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Stanislaw Gruszka <sgruszka@redhat.com>
|
||||||
|
Date: Fri, 18 May 2018 12:25:08 +0200
|
||||||
|
Subject: [PATCH 1/5] rt2800: move usb specific txdone/txstatus routines to
|
||||||
|
rt2800lib
|
||||||
|
|
||||||
|
In order to reuse usb txdone/txstatus routines for mmio, move them
|
||||||
|
to common rt2800lib.c file.
|
||||||
|
|
||||||
|
Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com>
|
||||||
|
---
|
||||||
|
.../net/wireless/ralink/rt2x00/rt2800lib.c | 138 +++++++++++++++++
|
||||||
|
.../net/wireless/ralink/rt2x00/rt2800lib.h | 3 +
|
||||||
|
.../net/wireless/ralink/rt2x00/rt2800usb.c | 143 +-----------------
|
||||||
|
3 files changed, 145 insertions(+), 139 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
|
||||||
|
index a567bc273ffc..9f2835729016 100644
|
||||||
|
--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
|
||||||
|
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
|
||||||
|
@@ -957,6 +957,47 @@ static void rt2800_rate_from_status(struct skb_frame_desc *skbdesc,
|
||||||
|
skbdesc->tx_rate_flags = flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
+static bool rt2800_txdone_entry_check(struct queue_entry *entry, u32 reg)
|
||||||
|
+{
|
||||||
|
+ __le32 *txwi;
|
||||||
|
+ u32 word;
|
||||||
|
+ int wcid, ack, pid;
|
||||||
|
+ int tx_wcid, tx_ack, tx_pid, is_agg;
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * This frames has returned with an IO error,
|
||||||
|
+ * so the status report is not intended for this
|
||||||
|
+ * frame.
|
||||||
|
+ */
|
||||||
|
+ if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags))
|
||||||
|
+ return false;
|
||||||
|
+
|
||||||
|
+ wcid = rt2x00_get_field32(reg, TX_STA_FIFO_WCID);
|
||||||
|
+ ack = rt2x00_get_field32(reg, TX_STA_FIFO_TX_ACK_REQUIRED);
|
||||||
|
+ pid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_TYPE);
|
||||||
|
+ is_agg = rt2x00_get_field32(reg, TX_STA_FIFO_TX_AGGRE);
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * Validate if this TX status report is intended for
|
||||||
|
+ * this entry by comparing the WCID/ACK/PID fields.
|
||||||
|
+ */
|
||||||
|
+ txwi = rt2800_drv_get_txwi(entry);
|
||||||
|
+
|
||||||
|
+ word = rt2x00_desc_read(txwi, 1);
|
||||||
|
+ tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID);
|
||||||
|
+ tx_ack = rt2x00_get_field32(word, TXWI_W1_ACK);
|
||||||
|
+ tx_pid = rt2x00_get_field32(word, TXWI_W1_PACKETID);
|
||||||
|
+
|
||||||
|
+ if (wcid != tx_wcid || ack != tx_ack || (!is_agg && pid != tx_pid)) {
|
||||||
|
+ rt2x00_dbg(entry->queue->rt2x00dev,
|
||||||
|
+ "TX status report missed for queue %d entry %d\n",
|
||||||
|
+ entry->queue->qid, entry->entry_idx);
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return true;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32 *txwi,
|
||||||
|
bool match)
|
||||||
|
{
|
||||||
|
@@ -1059,6 +1100,103 @@ void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32 *txwi,
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(rt2800_txdone_entry);
|
||||||
|
|
||||||
|
+void rt2800_txdone(struct rt2x00_dev *rt2x00dev)
|
||||||
|
+{
|
||||||
|
+ struct data_queue *queue;
|
||||||
|
+ struct queue_entry *entry;
|
||||||
|
+ u32 reg;
|
||||||
|
+ u8 qid;
|
||||||
|
+ bool match;
|
||||||
|
+
|
||||||
|
+ while (kfifo_get(&rt2x00dev->txstatus_fifo, ®)) {
|
||||||
|
+ /*
|
||||||
|
+ * TX_STA_FIFO_PID_QUEUE is a 2-bit field, thus qid is
|
||||||
|
+ * guaranteed to be one of the TX QIDs .
|
||||||
|
+ */
|
||||||
|
+ qid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_QUEUE);
|
||||||
|
+ queue = rt2x00queue_get_tx_queue(rt2x00dev, qid);
|
||||||
|
+
|
||||||
|
+ if (unlikely(rt2x00queue_empty(queue))) {
|
||||||
|
+ rt2x00_dbg(rt2x00dev, "Got TX status for an empty queue %u, dropping\n",
|
||||||
|
+ qid);
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
|
||||||
|
+
|
||||||
|
+ if (unlikely(test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) ||
|
||||||
|
+ !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags))) {
|
||||||
|
+ rt2x00_warn(rt2x00dev, "Data pending for entry %u in queue %u\n",
|
||||||
|
+ entry->entry_idx, qid);
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ match = rt2800_txdone_entry_check(entry, reg);
|
||||||
|
+ rt2800_txdone_entry(entry, reg, rt2800_drv_get_txwi(entry), match);
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+EXPORT_SYMBOL_GPL(rt2800_txdone);
|
||||||
|
+
|
||||||
|
+static inline bool rt2800_entry_txstatus_timeout(struct queue_entry *entry)
|
||||||
|
+{
|
||||||
|
+ bool tout;
|
||||||
|
+
|
||||||
|
+ if (!test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags))
|
||||||
|
+ return false;
|
||||||
|
+
|
||||||
|
+ tout = time_after(jiffies, entry->last_action + msecs_to_jiffies(500));
|
||||||
|
+ if (unlikely(tout))
|
||||||
|
+ rt2x00_dbg(entry->queue->rt2x00dev,
|
||||||
|
+ "TX status timeout for entry %d in queue %d\n",
|
||||||
|
+ entry->entry_idx, entry->queue->qid);
|
||||||
|
+ return tout;
|
||||||
|
+
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+bool rt2800_txstatus_timeout(struct rt2x00_dev *rt2x00dev)
|
||||||
|
+{
|
||||||
|
+ struct data_queue *queue;
|
||||||
|
+ struct queue_entry *entry;
|
||||||
|
+
|
||||||
|
+ tx_queue_for_each(rt2x00dev, queue) {
|
||||||
|
+ entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
|
||||||
|
+ if (rt2800_entry_txstatus_timeout(entry))
|
||||||
|
+ return true;
|
||||||
|
+ }
|
||||||
|
+ return false;
|
||||||
|
+}
|
||||||
|
+EXPORT_SYMBOL_GPL(rt2800_txstatus_timeout);
|
||||||
|
+
|
||||||
|
+void rt2800_txdone_nostatus(struct rt2x00_dev *rt2x00dev)
|
||||||
|
+{
|
||||||
|
+ struct data_queue *queue;
|
||||||
|
+ struct queue_entry *entry;
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * Process any trailing TX status reports for IO failures,
|
||||||
|
+ * we loop until we find the first non-IO error entry. This
|
||||||
|
+ * can either be a frame which is free, is being uploaded,
|
||||||
|
+ * or has completed the upload but didn't have an entry
|
||||||
|
+ * in the TX_STAT_FIFO register yet.
|
||||||
|
+ */
|
||||||
|
+ tx_queue_for_each(rt2x00dev, queue) {
|
||||||
|
+ while (!rt2x00queue_empty(queue)) {
|
||||||
|
+ entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
|
||||||
|
+
|
||||||
|
+ if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) ||
|
||||||
|
+ !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags))
|
||||||
|
+ break;
|
||||||
|
+
|
||||||
|
+ if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags) ||
|
||||||
|
+ rt2800_entry_txstatus_timeout(entry))
|
||||||
|
+ rt2x00lib_txdone_noinfo(entry, TXDONE_FAILURE);
|
||||||
|
+ else
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+EXPORT_SYMBOL_GPL(rt2800_txdone_nostatus);
|
||||||
|
+
|
||||||
|
static unsigned int rt2800_hw_beacon_base(struct rt2x00_dev *rt2x00dev,
|
||||||
|
unsigned int index)
|
||||||
|
{
|
||||||
|
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
|
||||||
|
index 51d9c2a932cc..0dff2c7b3010 100644
|
||||||
|
--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
|
||||||
|
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
|
||||||
|
@@ -195,6 +195,9 @@ void rt2800_process_rxwi(struct queue_entry *entry, struct rxdone_entry_desc *tx
|
||||||
|
|
||||||
|
void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32 *txwi,
|
||||||
|
bool match);
|
||||||
|
+void rt2800_txdone(struct rt2x00_dev *rt2x00dev);
|
||||||
|
+void rt2800_txdone_nostatus(struct rt2x00_dev *rt2x00dev);
|
||||||
|
+bool rt2800_txstatus_timeout(struct rt2x00_dev *rt2x00dev);
|
||||||
|
|
||||||
|
void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc);
|
||||||
|
void rt2800_clear_beacon(struct queue_entry *entry);
|
||||||
|
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
|
||||||
|
index 98a7313fea4a..19eabf16147b 100644
|
||||||
|
--- a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
|
||||||
|
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
|
||||||
|
@@ -116,35 +116,6 @@ static bool rt2800usb_txstatus_pending(struct rt2x00_dev *rt2x00dev)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
-static inline bool rt2800usb_entry_txstatus_timeout(struct queue_entry *entry)
|
||||||
|
-{
|
||||||
|
- bool tout;
|
||||||
|
-
|
||||||
|
- if (!test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags))
|
||||||
|
- return false;
|
||||||
|
-
|
||||||
|
- tout = time_after(jiffies, entry->last_action + msecs_to_jiffies(500));
|
||||||
|
- if (unlikely(tout))
|
||||||
|
- rt2x00_dbg(entry->queue->rt2x00dev,
|
||||||
|
- "TX status timeout for entry %d in queue %d\n",
|
||||||
|
- entry->entry_idx, entry->queue->qid);
|
||||||
|
- return tout;
|
||||||
|
-
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static bool rt2800usb_txstatus_timeout(struct rt2x00_dev *rt2x00dev)
|
||||||
|
-{
|
||||||
|
- struct data_queue *queue;
|
||||||
|
- struct queue_entry *entry;
|
||||||
|
-
|
||||||
|
- tx_queue_for_each(rt2x00dev, queue) {
|
||||||
|
- entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
|
||||||
|
- if (rt2800usb_entry_txstatus_timeout(entry))
|
||||||
|
- return true;
|
||||||
|
- }
|
||||||
|
- return false;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
#define TXSTATUS_READ_INTERVAL 1000000
|
||||||
|
|
||||||
|
static bool rt2800usb_tx_sta_fifo_read_completed(struct rt2x00_dev *rt2x00dev,
|
||||||
|
@@ -171,7 +142,7 @@ static bool rt2800usb_tx_sta_fifo_read_completed(struct rt2x00_dev *rt2x00dev,
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if there is any entry that timedout waiting on TX status */
|
||||||
|
- if (rt2800usb_txstatus_timeout(rt2x00dev))
|
||||||
|
+ if (rt2800_txstatus_timeout(rt2x00dev))
|
||||||
|
queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work);
|
||||||
|
|
||||||
|
if (rt2800usb_txstatus_pending(rt2x00dev)) {
|
||||||
|
@@ -501,123 +472,17 @@ static int rt2800usb_get_tx_data_len(struct queue_entry *entry)
|
||||||
|
/*
|
||||||
|
* TX control handlers
|
||||||
|
*/
|
||||||
|
-static bool rt2800usb_txdone_entry_check(struct queue_entry *entry, u32 reg)
|
||||||
|
-{
|
||||||
|
- __le32 *txwi;
|
||||||
|
- u32 word;
|
||||||
|
- int wcid, ack, pid;
|
||||||
|
- int tx_wcid, tx_ack, tx_pid, is_agg;
|
||||||
|
-
|
||||||
|
- /*
|
||||||
|
- * This frames has returned with an IO error,
|
||||||
|
- * so the status report is not intended for this
|
||||||
|
- * frame.
|
||||||
|
- */
|
||||||
|
- if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags))
|
||||||
|
- return false;
|
||||||
|
-
|
||||||
|
- wcid = rt2x00_get_field32(reg, TX_STA_FIFO_WCID);
|
||||||
|
- ack = rt2x00_get_field32(reg, TX_STA_FIFO_TX_ACK_REQUIRED);
|
||||||
|
- pid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_TYPE);
|
||||||
|
- is_agg = rt2x00_get_field32(reg, TX_STA_FIFO_TX_AGGRE);
|
||||||
|
-
|
||||||
|
- /*
|
||||||
|
- * Validate if this TX status report is intended for
|
||||||
|
- * this entry by comparing the WCID/ACK/PID fields.
|
||||||
|
- */
|
||||||
|
- txwi = rt2800usb_get_txwi(entry);
|
||||||
|
-
|
||||||
|
- word = rt2x00_desc_read(txwi, 1);
|
||||||
|
- tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID);
|
||||||
|
- tx_ack = rt2x00_get_field32(word, TXWI_W1_ACK);
|
||||||
|
- tx_pid = rt2x00_get_field32(word, TXWI_W1_PACKETID);
|
||||||
|
-
|
||||||
|
- if (wcid != tx_wcid || ack != tx_ack || (!is_agg && pid != tx_pid)) {
|
||||||
|
- rt2x00_dbg(entry->queue->rt2x00dev,
|
||||||
|
- "TX status report missed for queue %d entry %d\n",
|
||||||
|
- entry->queue->qid, entry->entry_idx);
|
||||||
|
- return false;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- return true;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static void rt2800usb_txdone(struct rt2x00_dev *rt2x00dev)
|
||||||
|
-{
|
||||||
|
- struct data_queue *queue;
|
||||||
|
- struct queue_entry *entry;
|
||||||
|
- u32 reg;
|
||||||
|
- u8 qid;
|
||||||
|
- bool match;
|
||||||
|
-
|
||||||
|
- while (kfifo_get(&rt2x00dev->txstatus_fifo, ®)) {
|
||||||
|
- /*
|
||||||
|
- * TX_STA_FIFO_PID_QUEUE is a 2-bit field, thus qid is
|
||||||
|
- * guaranteed to be one of the TX QIDs .
|
||||||
|
- */
|
||||||
|
- qid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_QUEUE);
|
||||||
|
- queue = rt2x00queue_get_tx_queue(rt2x00dev, qid);
|
||||||
|
-
|
||||||
|
- if (unlikely(rt2x00queue_empty(queue))) {
|
||||||
|
- rt2x00_dbg(rt2x00dev, "Got TX status for an empty queue %u, dropping\n",
|
||||||
|
- qid);
|
||||||
|
- break;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
|
||||||
|
-
|
||||||
|
- if (unlikely(test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) ||
|
||||||
|
- !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags))) {
|
||||||
|
- rt2x00_warn(rt2x00dev, "Data pending for entry %u in queue %u\n",
|
||||||
|
- entry->entry_idx, qid);
|
||||||
|
- break;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- match = rt2800usb_txdone_entry_check(entry, reg);
|
||||||
|
- rt2800_txdone_entry(entry, reg, rt2800usb_get_txwi(entry), match);
|
||||||
|
- }
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static void rt2800usb_txdone_nostatus(struct rt2x00_dev *rt2x00dev)
|
||||||
|
-{
|
||||||
|
- struct data_queue *queue;
|
||||||
|
- struct queue_entry *entry;
|
||||||
|
-
|
||||||
|
- /*
|
||||||
|
- * Process any trailing TX status reports for IO failures,
|
||||||
|
- * we loop until we find the first non-IO error entry. This
|
||||||
|
- * can either be a frame which is free, is being uploaded,
|
||||||
|
- * or has completed the upload but didn't have an entry
|
||||||
|
- * in the TX_STAT_FIFO register yet.
|
||||||
|
- */
|
||||||
|
- tx_queue_for_each(rt2x00dev, queue) {
|
||||||
|
- while (!rt2x00queue_empty(queue)) {
|
||||||
|
- entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
|
||||||
|
-
|
||||||
|
- if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) ||
|
||||||
|
- !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags))
|
||||||
|
- break;
|
||||||
|
-
|
||||||
|
- if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags) ||
|
||||||
|
- rt2800usb_entry_txstatus_timeout(entry))
|
||||||
|
- rt2x00lib_txdone_noinfo(entry, TXDONE_FAILURE);
|
||||||
|
- else
|
||||||
|
- break;
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
static void rt2800usb_work_txdone(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct rt2x00_dev *rt2x00dev =
|
||||||
|
container_of(work, struct rt2x00_dev, txdone_work);
|
||||||
|
|
||||||
|
while (!kfifo_is_empty(&rt2x00dev->txstatus_fifo) ||
|
||||||
|
- rt2800usb_txstatus_timeout(rt2x00dev)) {
|
||||||
|
+ rt2800_txstatus_timeout(rt2x00dev)) {
|
||||||
|
|
||||||
|
- rt2800usb_txdone(rt2x00dev);
|
||||||
|
+ rt2800_txdone(rt2x00dev);
|
||||||
|
|
||||||
|
- rt2800usb_txdone_nostatus(rt2x00dev);
|
||||||
|
+ rt2800_txdone_nostatus(rt2x00dev);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The hw may delay sending the packet after DMA complete
|
||||||
|
--
|
||||||
|
2.18.0
|
||||||
|
|
|
@ -0,0 +1,250 @@
|
||||||
|
From 7993486bbab17f8916993710a8660eb47fd991e9 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Stanislaw Gruszka <sgruszka@redhat.com>
|
||||||
|
Date: Mon, 9 Jul 2018 16:07:42 +0200
|
||||||
|
Subject: [PATCH 2/5] rt2800mmio: use txdone/txstatus routines from lib
|
||||||
|
|
||||||
|
Use usb txdone/txstatus routines (now in rt2800libc) for mmio devices.
|
||||||
|
|
||||||
|
Note this also change how we handle INT_SOURCE_CSR_TX_FIFO_STATUS
|
||||||
|
interrupt. Now it is disabled since IRQ routine till end of the txstatus
|
||||||
|
tasklet (the same behaviour like others interrupts). Reason to do not
|
||||||
|
disable this interrupt was not to miss any tx status from 16 entries
|
||||||
|
FIFO register. Now, since we check for tx status timeout, we can
|
||||||
|
allow to miss some tx statuses. However this will be improved in further
|
||||||
|
patch where I also implement read status FIFO register in the tasklet.
|
||||||
|
|
||||||
|
Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com>
|
||||||
|
---
|
||||||
|
.../net/wireless/ralink/rt2x00/rt2800mmio.c | 180 +-----------------
|
||||||
|
.../net/wireless/ralink/rt2x00/rt2x00queue.c | 1 +
|
||||||
|
2 files changed, 9 insertions(+), 172 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c
|
||||||
|
index e1a7ed7e4892..aa8449a5e8fe 100644
|
||||||
|
--- a/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c
|
||||||
|
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c
|
||||||
|
@@ -175,161 +175,6 @@ static void rt2800mmio_wakeup(struct rt2x00_dev *rt2x00dev)
|
||||||
|
rt2800_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS);
|
||||||
|
}
|
||||||
|
|
||||||
|
-static bool rt2800mmio_txdone_entry_check(struct queue_entry *entry, u32 status)
|
||||||
|
-{
|
||||||
|
- __le32 *txwi;
|
||||||
|
- u32 word;
|
||||||
|
- int wcid, tx_wcid;
|
||||||
|
-
|
||||||
|
- wcid = rt2x00_get_field32(status, TX_STA_FIFO_WCID);
|
||||||
|
-
|
||||||
|
- txwi = rt2800_drv_get_txwi(entry);
|
||||||
|
- word = rt2x00_desc_read(txwi, 1);
|
||||||
|
- tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID);
|
||||||
|
-
|
||||||
|
- return (tx_wcid == wcid);
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static bool rt2800mmio_txdone_find_entry(struct queue_entry *entry, void *data)
|
||||||
|
-{
|
||||||
|
- u32 status = *(u32 *)data;
|
||||||
|
-
|
||||||
|
- /*
|
||||||
|
- * rt2800pci hardware might reorder frames when exchanging traffic
|
||||||
|
- * with multiple BA enabled STAs.
|
||||||
|
- *
|
||||||
|
- * For example, a tx queue
|
||||||
|
- * [ STA1 | STA2 | STA1 | STA2 ]
|
||||||
|
- * can result in tx status reports
|
||||||
|
- * [ STA1 | STA1 | STA2 | STA2 ]
|
||||||
|
- * when the hw decides to aggregate the frames for STA1 into one AMPDU.
|
||||||
|
- *
|
||||||
|
- * To mitigate this effect, associate the tx status to the first frame
|
||||||
|
- * in the tx queue with a matching wcid.
|
||||||
|
- */
|
||||||
|
- if (rt2800mmio_txdone_entry_check(entry, status) &&
|
||||||
|
- !test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) {
|
||||||
|
- /*
|
||||||
|
- * Got a matching frame, associate the tx status with
|
||||||
|
- * the frame
|
||||||
|
- */
|
||||||
|
- entry->status = status;
|
||||||
|
- set_bit(ENTRY_DATA_STATUS_SET, &entry->flags);
|
||||||
|
- return true;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* Check the next frame */
|
||||||
|
- return false;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static bool rt2800mmio_txdone_match_first(struct queue_entry *entry, void *data)
|
||||||
|
-{
|
||||||
|
- u32 status = *(u32 *)data;
|
||||||
|
-
|
||||||
|
- /*
|
||||||
|
- * Find the first frame without tx status and assign this status to it
|
||||||
|
- * regardless if it matches or not.
|
||||||
|
- */
|
||||||
|
- if (!test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) {
|
||||||
|
- /*
|
||||||
|
- * Got a matching frame, associate the tx status with
|
||||||
|
- * the frame
|
||||||
|
- */
|
||||||
|
- entry->status = status;
|
||||||
|
- set_bit(ENTRY_DATA_STATUS_SET, &entry->flags);
|
||||||
|
- return true;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* Check the next frame */
|
||||||
|
- return false;
|
||||||
|
-}
|
||||||
|
-static bool rt2800mmio_txdone_release_entries(struct queue_entry *entry,
|
||||||
|
- void *data)
|
||||||
|
-{
|
||||||
|
- if (test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) {
|
||||||
|
- rt2800_txdone_entry(entry, entry->status,
|
||||||
|
- rt2800mmio_get_txwi(entry), true);
|
||||||
|
- return false;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* No more frames to release */
|
||||||
|
- return true;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static bool rt2800mmio_txdone(struct rt2x00_dev *rt2x00dev)
|
||||||
|
-{
|
||||||
|
- struct data_queue *queue;
|
||||||
|
- u32 status;
|
||||||
|
- u8 qid;
|
||||||
|
- int max_tx_done = 16;
|
||||||
|
-
|
||||||
|
- while (kfifo_get(&rt2x00dev->txstatus_fifo, &status)) {
|
||||||
|
- qid = rt2x00_get_field32(status, TX_STA_FIFO_PID_QUEUE);
|
||||||
|
- if (unlikely(qid >= QID_RX)) {
|
||||||
|
- /*
|
||||||
|
- * Unknown queue, this shouldn't happen. Just drop
|
||||||
|
- * this tx status.
|
||||||
|
- */
|
||||||
|
- rt2x00_warn(rt2x00dev, "Got TX status report with unexpected pid %u, dropping\n",
|
||||||
|
- qid);
|
||||||
|
- break;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- queue = rt2x00queue_get_tx_queue(rt2x00dev, qid);
|
||||||
|
- if (unlikely(queue == NULL)) {
|
||||||
|
- /*
|
||||||
|
- * The queue is NULL, this shouldn't happen. Stop
|
||||||
|
- * processing here and drop the tx status
|
||||||
|
- */
|
||||||
|
- rt2x00_warn(rt2x00dev, "Got TX status for an unavailable queue %u, dropping\n",
|
||||||
|
- qid);
|
||||||
|
- break;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if (unlikely(rt2x00queue_empty(queue))) {
|
||||||
|
- /*
|
||||||
|
- * The queue is empty. Stop processing here
|
||||||
|
- * and drop the tx status.
|
||||||
|
- */
|
||||||
|
- rt2x00_warn(rt2x00dev, "Got TX status for an empty queue %u, dropping\n",
|
||||||
|
- qid);
|
||||||
|
- break;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /*
|
||||||
|
- * Let's associate this tx status with the first
|
||||||
|
- * matching frame.
|
||||||
|
- */
|
||||||
|
- if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE,
|
||||||
|
- Q_INDEX, &status,
|
||||||
|
- rt2800mmio_txdone_find_entry)) {
|
||||||
|
- /*
|
||||||
|
- * We cannot match the tx status to any frame, so just
|
||||||
|
- * use the first one.
|
||||||
|
- */
|
||||||
|
- if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE,
|
||||||
|
- Q_INDEX, &status,
|
||||||
|
- rt2800mmio_txdone_match_first)) {
|
||||||
|
- rt2x00_warn(rt2x00dev, "No frame found for TX status on queue %u, dropping\n",
|
||||||
|
- qid);
|
||||||
|
- break;
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /*
|
||||||
|
- * Release all frames with a valid tx status.
|
||||||
|
- */
|
||||||
|
- rt2x00queue_for_each_entry(queue, Q_INDEX_DONE,
|
||||||
|
- Q_INDEX, NULL,
|
||||||
|
- rt2800mmio_txdone_release_entries);
|
||||||
|
-
|
||||||
|
- if (--max_tx_done == 0)
|
||||||
|
- break;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- return !max_tx_done;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
static inline void rt2800mmio_enable_interrupt(struct rt2x00_dev *rt2x00dev,
|
||||||
|
struct rt2x00_field32 irq_field)
|
||||||
|
{
|
||||||
|
@@ -349,14 +194,14 @@ static inline void rt2800mmio_enable_interrupt(struct rt2x00_dev *rt2x00dev,
|
||||||
|
void rt2800mmio_txstatus_tasklet(unsigned long data)
|
||||||
|
{
|
||||||
|
struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
|
||||||
|
- if (rt2800mmio_txdone(rt2x00dev))
|
||||||
|
- tasklet_schedule(&rt2x00dev->txstatus_tasklet);
|
||||||
|
|
||||||
|
- /*
|
||||||
|
- * No need to enable the tx status interrupt here as we always
|
||||||
|
- * leave it enabled to minimize the possibility of a tx status
|
||||||
|
- * register overflow. See comment in interrupt handler.
|
||||||
|
- */
|
||||||
|
+ rt2800_txdone(rt2x00dev);
|
||||||
|
+
|
||||||
|
+ rt2800_txdone_nostatus(rt2x00dev);
|
||||||
|
+
|
||||||
|
+ if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
|
||||||
|
+ rt2800mmio_enable_interrupt(rt2x00dev,
|
||||||
|
+ INT_SOURCE_CSR_TX_FIFO_STATUS);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(rt2800mmio_txstatus_tasklet);
|
||||||
|
|
||||||
|
@@ -440,10 +285,6 @@ static void rt2800mmio_txstatus_interrupt(struct rt2x00_dev *rt2x00dev)
|
||||||
|
* because we can schedule the tasklet multiple times (when the
|
||||||
|
* interrupt fires again during tx status processing).
|
||||||
|
*
|
||||||
|
- * Furthermore we don't disable the TX_FIFO_STATUS
|
||||||
|
- * interrupt here but leave it enabled so that the TX_STA_FIFO
|
||||||
|
- * can also be read while the tx status tasklet gets executed.
|
||||||
|
- *
|
||||||
|
* Since we have only one producer and one consumer we don't
|
||||||
|
* need to lock the kfifo.
|
||||||
|
*/
|
||||||
|
@@ -485,13 +326,8 @@ irqreturn_t rt2800mmio_interrupt(int irq, void *dev_instance)
|
||||||
|
*/
|
||||||
|
mask = ~reg;
|
||||||
|
|
||||||
|
- if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) {
|
||||||
|
+ if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS))
|
||||||
|
rt2800mmio_txstatus_interrupt(rt2x00dev);
|
||||||
|
- /*
|
||||||
|
- * Never disable the TX_FIFO_STATUS interrupt.
|
||||||
|
- */
|
||||||
|
- rt2x00_set_field32(&mask, INT_MASK_CSR_TX_FIFO_STATUS, 1);
|
||||||
|
- }
|
||||||
|
|
||||||
|
if (rt2x00_get_field32(reg, INT_SOURCE_CSR_PRE_TBTT))
|
||||||
|
tasklet_hi_schedule(&rt2x00dev->pretbtt_tasklet);
|
||||||
|
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00queue.c b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.c
|
||||||
|
index 710e9641552e..6e8beb7ea350 100644
|
||||||
|
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00queue.c
|
||||||
|
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.c
|
||||||
|
@@ -113,6 +113,7 @@ int rt2x00queue_map_txskb(struct queue_entry *entry)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
skbdesc->flags |= SKBDESC_DMA_MAPPED_TX;
|
||||||
|
+ rt2x00lib_dmadone(entry);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(rt2x00queue_map_txskb);
|
||||||
|
--
|
||||||
|
2.18.0
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
From b5d6e37ea15949a126907050d8cfa4408153a0cd Mon Sep 17 00:00:00 2001
|
||||||
|
From: Stanislaw Gruszka <sgruszka@redhat.com>
|
||||||
|
Date: Fri, 18 May 2018 12:25:10 +0200
|
||||||
|
Subject: [PATCH 3/5] rt2x00: do not check for txstatus timeout every time on
|
||||||
|
tasklet
|
||||||
|
|
||||||
|
Do not check for tx status timeout everytime we perform txstatus tasklet.
|
||||||
|
Perform check once per half a second.
|
||||||
|
|
||||||
|
Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com>
|
||||||
|
---
|
||||||
|
drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 7 +++++++
|
||||||
|
drivers/net/wireless/ralink/rt2x00/rt2800mmio.c | 3 ++-
|
||||||
|
drivers/net/wireless/ralink/rt2x00/rt2x00.h | 2 ++
|
||||||
|
drivers/net/wireless/ralink/rt2x00/rt2x00queue.c | 1 +
|
||||||
|
4 files changed, 12 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
|
||||||
|
index 9f2835729016..0c56c7dca55f 100644
|
||||||
|
--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
|
||||||
|
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
|
||||||
|
@@ -1158,11 +1158,18 @@ bool rt2800_txstatus_timeout(struct rt2x00_dev *rt2x00dev)
|
||||||
|
struct data_queue *queue;
|
||||||
|
struct queue_entry *entry;
|
||||||
|
|
||||||
|
+ if (time_before(jiffies,
|
||||||
|
+ rt2x00dev->last_nostatus_check + msecs_to_jiffies(500)))
|
||||||
|
+ return false;
|
||||||
|
+
|
||||||
|
+ rt2x00dev->last_nostatus_check = jiffies;
|
||||||
|
+
|
||||||
|
tx_queue_for_each(rt2x00dev, queue) {
|
||||||
|
entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
|
||||||
|
if (rt2800_entry_txstatus_timeout(entry))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
+
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(rt2800_txstatus_timeout);
|
||||||
|
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c
|
||||||
|
index aa8449a5e8fe..d0426314c2df 100644
|
||||||
|
--- a/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c
|
||||||
|
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c
|
||||||
|
@@ -197,7 +197,8 @@ void rt2800mmio_txstatus_tasklet(unsigned long data)
|
||||||
|
|
||||||
|
rt2800_txdone(rt2x00dev);
|
||||||
|
|
||||||
|
- rt2800_txdone_nostatus(rt2x00dev);
|
||||||
|
+ if (rt2800_txstatus_timeout(rt2x00dev))
|
||||||
|
+ rt2800_txdone_nostatus(rt2x00dev);
|
||||||
|
|
||||||
|
if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
|
||||||
|
rt2800mmio_enable_interrupt(rt2x00dev,
|
||||||
|
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00.h b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
|
||||||
|
index a279a4363bc1..af062cda4a23 100644
|
||||||
|
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h
|
||||||
|
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
|
||||||
|
@@ -980,6 +980,8 @@ struct rt2x00_dev {
|
||||||
|
*/
|
||||||
|
DECLARE_KFIFO_PTR(txstatus_fifo, u32);
|
||||||
|
|
||||||
|
+ unsigned long last_nostatus_check;
|
||||||
|
+
|
||||||
|
/*
|
||||||
|
* Timer to ensure tx status reports are read (rt2800usb).
|
||||||
|
*/
|
||||||
|
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00queue.c b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.c
|
||||||
|
index 6e8beb7ea350..92ddc19e7bf7 100644
|
||||||
|
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00queue.c
|
||||||
|
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.c
|
||||||
|
@@ -1039,6 +1039,7 @@ void rt2x00queue_start_queues(struct rt2x00_dev *rt2x00dev)
|
||||||
|
*/
|
||||||
|
tx_queue_for_each(rt2x00dev, queue)
|
||||||
|
rt2x00queue_start_queue(queue);
|
||||||
|
+ rt2x00dev->last_nostatus_check = jiffies;
|
||||||
|
|
||||||
|
rt2x00queue_start_queue(rt2x00dev->rx);
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.18.0
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
From feb87977b6d251fb01a329905719e45908f6c939 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Stanislaw Gruszka <sgruszka@redhat.com>
|
||||||
|
Date: Fri, 10 Aug 2018 12:31:55 +0200
|
||||||
|
Subject: [PATCH 4/5] rt2x00: use different txstatus timeouts when flushing
|
||||||
|
|
||||||
|
Use different tx status timeouts for normal operation and when flushing.
|
||||||
|
This increase timeout to 2s for normal operation as when there are bad
|
||||||
|
radio conditions and frames are reposted many times device can not provide
|
||||||
|
the status for quite long. With new timeout we can still get valid status
|
||||||
|
on such bad conditions.
|
||||||
|
|
||||||
|
Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com>
|
||||||
|
---
|
||||||
|
.../net/wireless/ralink/rt2x00/rt2800lib.c | 31 +++++++++++++------
|
||||||
|
drivers/net/wireless/ralink/rt2x00/rt2x00.h | 1 +
|
||||||
|
.../net/wireless/ralink/rt2x00/rt2x00mac.c | 4 +++
|
||||||
|
3 files changed, 26 insertions(+), 10 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
|
||||||
|
index 0c56c7dca55f..595cb9c90b81 100644
|
||||||
|
--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
|
||||||
|
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
|
||||||
|
@@ -1137,36 +1137,47 @@ void rt2800_txdone(struct rt2x00_dev *rt2x00dev)
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(rt2800_txdone);
|
||||||
|
|
||||||
|
-static inline bool rt2800_entry_txstatus_timeout(struct queue_entry *entry)
|
||||||
|
+static inline bool rt2800_entry_txstatus_timeout(struct rt2x00_dev *rt2x00dev,
|
||||||
|
+ struct queue_entry *entry)
|
||||||
|
{
|
||||||
|
- bool tout;
|
||||||
|
+ bool ret;
|
||||||
|
+ unsigned long tout;
|
||||||
|
|
||||||
|
if (!test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
- tout = time_after(jiffies, entry->last_action + msecs_to_jiffies(500));
|
||||||
|
- if (unlikely(tout))
|
||||||
|
+ if (test_bit(DEVICE_STATE_FLUSHING, &rt2x00dev->flags))
|
||||||
|
+ tout = msecs_to_jiffies(100);
|
||||||
|
+ else
|
||||||
|
+ tout = msecs_to_jiffies(2000);
|
||||||
|
+
|
||||||
|
+ ret = time_after(jiffies, entry->last_action + tout);
|
||||||
|
+ if (unlikely(ret))
|
||||||
|
rt2x00_dbg(entry->queue->rt2x00dev,
|
||||||
|
"TX status timeout for entry %d in queue %d\n",
|
||||||
|
entry->entry_idx, entry->queue->qid);
|
||||||
|
- return tout;
|
||||||
|
-
|
||||||
|
+ return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rt2800_txstatus_timeout(struct rt2x00_dev *rt2x00dev)
|
||||||
|
{
|
||||||
|
struct data_queue *queue;
|
||||||
|
struct queue_entry *entry;
|
||||||
|
+ unsigned long tout;
|
||||||
|
+
|
||||||
|
+ if (test_bit(DEVICE_STATE_FLUSHING, &rt2x00dev->flags))
|
||||||
|
+ tout = msecs_to_jiffies(50);
|
||||||
|
+ else
|
||||||
|
+ tout = msecs_to_jiffies(1000);
|
||||||
|
|
||||||
|
- if (time_before(jiffies,
|
||||||
|
- rt2x00dev->last_nostatus_check + msecs_to_jiffies(500)))
|
||||||
|
+ if (time_before(jiffies, rt2x00dev->last_nostatus_check + tout))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
rt2x00dev->last_nostatus_check = jiffies;
|
||||||
|
|
||||||
|
tx_queue_for_each(rt2x00dev, queue) {
|
||||||
|
entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
|
||||||
|
- if (rt2800_entry_txstatus_timeout(entry))
|
||||||
|
+ if (rt2800_entry_txstatus_timeout(rt2x00dev, entry))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -1195,7 +1206,7 @@ void rt2800_txdone_nostatus(struct rt2x00_dev *rt2x00dev)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags) ||
|
||||||
|
- rt2800_entry_txstatus_timeout(entry))
|
||||||
|
+ rt2800_entry_txstatus_timeout(rt2x00dev, entry))
|
||||||
|
rt2x00lib_txdone_noinfo(entry, TXDONE_FAILURE);
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00.h b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
|
||||||
|
index af062cda4a23..4b1744e9fb78 100644
|
||||||
|
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h
|
||||||
|
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
|
||||||
|
@@ -665,6 +665,7 @@ enum rt2x00_state_flags {
|
||||||
|
DEVICE_STATE_STARTED,
|
||||||
|
DEVICE_STATE_ENABLED_RADIO,
|
||||||
|
DEVICE_STATE_SCANNING,
|
||||||
|
+ DEVICE_STATE_FLUSHING,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Driver configuration
|
||||||
|
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c b/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c
|
||||||
|
index fa2fd64084ac..2825560e2424 100644
|
||||||
|
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c
|
||||||
|
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c
|
||||||
|
@@ -720,8 +720,12 @@ void rt2x00mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||||
|
if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
|
||||||
|
return;
|
||||||
|
|
||||||
|
+ set_bit(DEVICE_STATE_FLUSHING, &rt2x00dev->flags);
|
||||||
|
+
|
||||||
|
tx_queue_for_each(rt2x00dev, queue)
|
||||||
|
rt2x00queue_flush_queue(queue, drop);
|
||||||
|
+
|
||||||
|
+ clear_bit(DEVICE_STATE_FLUSHING, &rt2x00dev->flags);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(rt2x00mac_flush);
|
||||||
|
|
||||||
|
--
|
||||||
|
2.18.0
|
||||||
|
|
|
@ -0,0 +1,250 @@
|
||||||
|
From 0d9fbb738a5eadc7abc8060f43ebcc71f6324c07 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Stanislaw Gruszka <sgruszka@redhat.com>
|
||||||
|
Date: Tue, 14 Aug 2018 08:58:48 +0200
|
||||||
|
Subject: [PATCH 5/5] rt2800: flush and txstatus rework for rt2800mmio
|
||||||
|
|
||||||
|
Implement custom rt2800mmio flush routine and change txstatus
|
||||||
|
routine to read TX_STA_FIFO also in the tasklet.
|
||||||
|
|
||||||
|
Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com>
|
||||||
|
---
|
||||||
|
.../net/wireless/ralink/rt2x00/rt2800lib.c | 14 +-
|
||||||
|
.../net/wireless/ralink/rt2x00/rt2800mmio.c | 120 +++++++++++++-----
|
||||||
|
.../net/wireless/ralink/rt2x00/rt2800mmio.h | 1 +
|
||||||
|
.../net/wireless/ralink/rt2x00/rt2800pci.c | 2 +-
|
||||||
|
4 files changed, 99 insertions(+), 38 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
|
||||||
|
index 595cb9c90b81..9e7b8933d30c 100644
|
||||||
|
--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
|
||||||
|
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
|
||||||
|
@@ -1147,7 +1147,7 @@ static inline bool rt2800_entry_txstatus_timeout(struct rt2x00_dev *rt2x00dev,
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (test_bit(DEVICE_STATE_FLUSHING, &rt2x00dev->flags))
|
||||||
|
- tout = msecs_to_jiffies(100);
|
||||||
|
+ tout = msecs_to_jiffies(50);
|
||||||
|
else
|
||||||
|
tout = msecs_to_jiffies(2000);
|
||||||
|
|
||||||
|
@@ -1163,15 +1163,13 @@ bool rt2800_txstatus_timeout(struct rt2x00_dev *rt2x00dev)
|
||||||
|
{
|
||||||
|
struct data_queue *queue;
|
||||||
|
struct queue_entry *entry;
|
||||||
|
- unsigned long tout;
|
||||||
|
|
||||||
|
- if (test_bit(DEVICE_STATE_FLUSHING, &rt2x00dev->flags))
|
||||||
|
- tout = msecs_to_jiffies(50);
|
||||||
|
- else
|
||||||
|
- tout = msecs_to_jiffies(1000);
|
||||||
|
+ if (!test_bit(DEVICE_STATE_FLUSHING, &rt2x00dev->flags)) {
|
||||||
|
+ unsigned long tout = msecs_to_jiffies(1000);
|
||||||
|
|
||||||
|
- if (time_before(jiffies, rt2x00dev->last_nostatus_check + tout))
|
||||||
|
- return false;
|
||||||
|
+ if (time_before(jiffies, rt2x00dev->last_nostatus_check + tout))
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
rt2x00dev->last_nostatus_check = jiffies;
|
||||||
|
|
||||||
|
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c
|
||||||
|
index d0426314c2df..7d83dabb913d 100644
|
||||||
|
--- a/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c
|
||||||
|
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c
|
||||||
|
@@ -191,21 +191,6 @@ static inline void rt2800mmio_enable_interrupt(struct rt2x00_dev *rt2x00dev,
|
||||||
|
spin_unlock_irq(&rt2x00dev->irqmask_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
-void rt2800mmio_txstatus_tasklet(unsigned long data)
|
||||||
|
-{
|
||||||
|
- struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
|
||||||
|
-
|
||||||
|
- rt2800_txdone(rt2x00dev);
|
||||||
|
-
|
||||||
|
- if (rt2800_txstatus_timeout(rt2x00dev))
|
||||||
|
- rt2800_txdone_nostatus(rt2x00dev);
|
||||||
|
-
|
||||||
|
- if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
|
||||||
|
- rt2800mmio_enable_interrupt(rt2x00dev,
|
||||||
|
- INT_SOURCE_CSR_TX_FIFO_STATUS);
|
||||||
|
-}
|
||||||
|
-EXPORT_SYMBOL_GPL(rt2800mmio_txstatus_tasklet);
|
||||||
|
-
|
||||||
|
void rt2800mmio_pretbtt_tasklet(unsigned long data)
|
||||||
|
{
|
||||||
|
struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
|
||||||
|
@@ -270,12 +255,26 @@ void rt2800mmio_autowake_tasklet(unsigned long data)
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(rt2800mmio_autowake_tasklet);
|
||||||
|
|
||||||
|
-static void rt2800mmio_txstatus_interrupt(struct rt2x00_dev *rt2x00dev)
|
||||||
|
+static void rt2800mmio_txdone(struct rt2x00_dev *rt2x00dev)
|
||||||
|
+{
|
||||||
|
+ bool timeout = false;
|
||||||
|
+
|
||||||
|
+ while (!kfifo_is_empty(&rt2x00dev->txstatus_fifo) ||
|
||||||
|
+ (timeout = rt2800_txstatus_timeout(rt2x00dev))) {
|
||||||
|
+
|
||||||
|
+ rt2800_txdone(rt2x00dev);
|
||||||
|
+
|
||||||
|
+ if (timeout)
|
||||||
|
+ rt2800_txdone_nostatus(rt2x00dev);
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static bool rt2800mmio_fetch_txstatus(struct rt2x00_dev *rt2x00dev)
|
||||||
|
{
|
||||||
|
u32 status;
|
||||||
|
- int i;
|
||||||
|
+ bool more = false;
|
||||||
|
|
||||||
|
- /*
|
||||||
|
+ /* FIXEME: rewrite this comment
|
||||||
|
* The TX_FIFO_STATUS interrupt needs special care. We should
|
||||||
|
* read TX_STA_FIFO but we should do it immediately as otherwise
|
||||||
|
* the register can overflow and we would lose status reports.
|
||||||
|
@@ -286,25 +285,37 @@ static void rt2800mmio_txstatus_interrupt(struct rt2x00_dev *rt2x00dev)
|
||||||
|
* because we can schedule the tasklet multiple times (when the
|
||||||
|
* interrupt fires again during tx status processing).
|
||||||
|
*
|
||||||
|
- * Since we have only one producer and one consumer we don't
|
||||||
|
+ * txstatus tasklet is called with INT_SOURCE_CSR_TX_FIFO_STATUS
|
||||||
|
+ * disabled so have only one producer and one consumer - we don't
|
||||||
|
* need to lock the kfifo.
|
||||||
|
*/
|
||||||
|
- for (i = 0; i < rt2x00dev->tx->limit; i++) {
|
||||||
|
+ while (!kfifo_is_full(&rt2x00dev->txstatus_fifo)) {
|
||||||
|
status = rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO);
|
||||||
|
-
|
||||||
|
if (!rt2x00_get_field32(status, TX_STA_FIFO_VALID))
|
||||||
|
break;
|
||||||
|
|
||||||
|
- if (!kfifo_put(&rt2x00dev->txstatus_fifo, status)) {
|
||||||
|
- rt2x00_warn(rt2x00dev, "TX status FIFO overrun, drop tx status report\n");
|
||||||
|
- break;
|
||||||
|
- }
|
||||||
|
+ kfifo_put(&rt2x00dev->txstatus_fifo, status);
|
||||||
|
+ more = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
- /* Schedule the tasklet for processing the tx status. */
|
||||||
|
- tasklet_schedule(&rt2x00dev->txstatus_tasklet);
|
||||||
|
+ return more;
|
||||||
|
}
|
||||||
|
|
||||||
|
+void rt2800mmio_txstatus_tasklet(unsigned long data)
|
||||||
|
+{
|
||||||
|
+ struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
|
||||||
|
+
|
||||||
|
+ do {
|
||||||
|
+ rt2800mmio_txdone(rt2x00dev);
|
||||||
|
+
|
||||||
|
+ } while (rt2800mmio_fetch_txstatus(rt2x00dev));
|
||||||
|
+
|
||||||
|
+ if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
|
||||||
|
+ rt2800mmio_enable_interrupt(rt2x00dev,
|
||||||
|
+ INT_SOURCE_CSR_TX_FIFO_STATUS);
|
||||||
|
+}
|
||||||
|
+EXPORT_SYMBOL_GPL(rt2800mmio_txstatus_tasklet);
|
||||||
|
+
|
||||||
|
irqreturn_t rt2800mmio_interrupt(int irq, void *dev_instance)
|
||||||
|
{
|
||||||
|
struct rt2x00_dev *rt2x00dev = dev_instance;
|
||||||
|
@@ -327,8 +338,10 @@ irqreturn_t rt2800mmio_interrupt(int irq, void *dev_instance)
|
||||||
|
*/
|
||||||
|
mask = ~reg;
|
||||||
|
|
||||||
|
- if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS))
|
||||||
|
- rt2800mmio_txstatus_interrupt(rt2x00dev);
|
||||||
|
+ if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) {
|
||||||
|
+ rt2800mmio_fetch_txstatus(rt2x00dev);
|
||||||
|
+ tasklet_schedule(&rt2x00dev->txstatus_tasklet);
|
||||||
|
+ }
|
||||||
|
|
||||||
|
if (rt2x00_get_field32(reg, INT_SOURCE_CSR_PRE_TBTT))
|
||||||
|
tasklet_hi_schedule(&rt2x00dev->pretbtt_tasklet);
|
||||||
|
@@ -453,6 +466,55 @@ void rt2800mmio_kick_queue(struct data_queue *queue)
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(rt2800mmio_kick_queue);
|
||||||
|
|
||||||
|
+void rt2800mmio_flush_queue(struct data_queue *queue, bool drop)
|
||||||
|
+{
|
||||||
|
+ struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
|
||||||
|
+ bool tx_queue = false;
|
||||||
|
+ unsigned int i;
|
||||||
|
+
|
||||||
|
+ //printk("FLUSH queue %d len %d drop %d\n", queue->qid, queue->length, drop);
|
||||||
|
+
|
||||||
|
+ switch (queue->qid) {
|
||||||
|
+ case QID_AC_VO:
|
||||||
|
+ case QID_AC_VI:
|
||||||
|
+ case QID_AC_BE:
|
||||||
|
+ case QID_AC_BK:
|
||||||
|
+ tx_queue = true;
|
||||||
|
+ break;
|
||||||
|
+ case QID_RX:
|
||||||
|
+ break;
|
||||||
|
+ default:
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ for (i = 0; i < 5; i++) {
|
||||||
|
+ /*
|
||||||
|
+ * Check if the driver is already done, otherwise we
|
||||||
|
+ * have to sleep a little while to give the driver/hw
|
||||||
|
+ * the oppurtunity to complete interrupt process itself.
|
||||||
|
+ */
|
||||||
|
+ if (rt2x00queue_empty(queue))
|
||||||
|
+ break;
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * For TX queues schedule completion tasklet to catch
|
||||||
|
+ * tx status timeouts, othewise just wait.
|
||||||
|
+ */
|
||||||
|
+ if (tx_queue) {
|
||||||
|
+ tasklet_disable(&rt2x00dev->txstatus_tasklet);
|
||||||
|
+ rt2800mmio_txdone(rt2x00dev);
|
||||||
|
+ tasklet_enable(&rt2x00dev->txstatus_tasklet);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * Wait for a little while to give the driver
|
||||||
|
+ * the oppurtunity to recover itself.
|
||||||
|
+ */
|
||||||
|
+ msleep(50);
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+EXPORT_SYMBOL_GPL(rt2800mmio_flush_queue);
|
||||||
|
+
|
||||||
|
void rt2800mmio_stop_queue(struct data_queue *queue)
|
||||||
|
{
|
||||||
|
struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
|
||||||
|
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800mmio.h b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.h
|
||||||
|
index b63312ce3f27..3a513273f414 100644
|
||||||
|
--- a/drivers/net/wireless/ralink/rt2x00/rt2800mmio.h
|
||||||
|
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.h
|
||||||
|
@@ -148,6 +148,7 @@ void rt2800mmio_toggle_irq(struct rt2x00_dev *rt2x00dev,
|
||||||
|
/* Queue handlers */
|
||||||
|
void rt2800mmio_start_queue(struct data_queue *queue);
|
||||||
|
void rt2800mmio_kick_queue(struct data_queue *queue);
|
||||||
|
+void rt2800mmio_flush_queue(struct data_queue *queue, bool drop);
|
||||||
|
void rt2800mmio_stop_queue(struct data_queue *queue);
|
||||||
|
void rt2800mmio_queue_init(struct data_queue *queue);
|
||||||
|
|
||||||
|
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800pci.c b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
|
||||||
|
index 71b1affc3885..0291441ac548 100644
|
||||||
|
--- a/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
|
||||||
|
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
|
||||||
|
@@ -364,7 +364,7 @@ static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = {
|
||||||
|
.start_queue = rt2800mmio_start_queue,
|
||||||
|
.kick_queue = rt2800mmio_kick_queue,
|
||||||
|
.stop_queue = rt2800mmio_stop_queue,
|
||||||
|
- .flush_queue = rt2x00mmio_flush_queue,
|
||||||
|
+ .flush_queue = rt2800mmio_flush_queue,
|
||||||
|
.write_tx_desc = rt2800mmio_write_tx_desc,
|
||||||
|
.write_tx_data = rt2800_write_tx_data,
|
||||||
|
.write_beacon = rt2800_write_beacon,
|
||||||
|
--
|
||||||
|
2.18.0
|
||||||
|
|
Loading…
Reference in a new issue