* [RFC v2 11/11] ath10k: Added sdio support
From: Erik Stromdahl @ 2016-11-18 19:22 UTC (permalink / raw)
To: kvalo, linux-wireless, ath10k; +Cc: Erik Stromdahl
In-Reply-To: <1479496971-19174-1-git-send-email-erik.stromdahl@gmail.com>
Initial HIF sdio/mailbox implementation.
Signed-off-by: Erik Stromdahl <erik.stromdahl@gmail.com>
---
drivers/net/wireless/ath/ath10k/Kconfig | 6 +
drivers/net/wireless/ath/ath10k/Makefile | 3 +
drivers/net/wireless/ath/ath10k/sdio.c | 1860 ++++++++++++++++++++++++++++++
drivers/net/wireless/ath/ath10k/sdio.h | 276 +++++
4 files changed, 2145 insertions(+)
create mode 100644 drivers/net/wireless/ath/ath10k/sdio.c
create mode 100644 drivers/net/wireless/ath/ath10k/sdio.h
diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig
index db1ca62..9a03178 100644
--- a/drivers/net/wireless/ath/ath10k/Kconfig
+++ b/drivers/net/wireless/ath/ath10k/Kconfig
@@ -21,6 +21,12 @@ config ATH10K_AHB
---help---
This module adds support for AHB bus
+config ATH10K_SDIO
+ tristate "Atheros ath10k SDIO support (EXPERIMENTAL)"
+ depends on ATH10K && MMC
+ ---help---
+ This module adds support for SDIO/MMC bus
+
config ATH10K_DEBUG
bool "Atheros ath10k debugging"
depends on ATH10K
diff --git a/drivers/net/wireless/ath/ath10k/Makefile b/drivers/net/wireless/ath/ath10k/Makefile
index 930fadd..b0b19a7 100644
--- a/drivers/net/wireless/ath/ath10k/Makefile
+++ b/drivers/net/wireless/ath/ath10k/Makefile
@@ -27,5 +27,8 @@ ath10k_pci-y += pci.o \
ath10k_pci-$(CONFIG_ATH10K_AHB) += ahb.o
+obj-$(CONFIG_ATH10K_SDIO) += ath10k_sdio.o
+ath10k_sdio-y += sdio.o
+
# for tracing framework to find trace.h
CFLAGS_trace.o := -I$(src)
diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c
new file mode 100644
index 0000000..4c123d9
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/sdio.c
@@ -0,0 +1,1860 @@
+/*
+ * Copyright (c) 2004-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
+ * Copyright (c) 2016 Kapsch Trafficcom AB
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/module.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sd.h>
+#include "core.h"
+#include "bmi.h"
+#include "debug.h"
+#include "hif.h"
+#include "htc.h"
+#include "targaddrs.h"
+#include "trace.h"
+#include "sdio.h"
+
+#define CALC_TXRX_PADDED_LEN(ar_sdio, len) \
+ (__ALIGN_MASK((len), (ar_sdio)->mbox_info.block_mask))
+
+static int ath10k_sdio_read_write_sync(struct ath10k *ar, u32 addr, u8 *buf,
+ u32 len, u32 request);
+static int ath10k_sdio_hif_diag_read(struct ath10k *ar, u32 address, void *buf,
+ size_t buf_len);
+static int ath10k_sdio_hif_diag_read32(struct ath10k *ar, u32 address,
+ u32 *value);
+
+/* HIF mbox interrupt handling */
+
+static int ath10k_sdio_mbox_rx_process_packet(struct ath10k_sdio *ar_sdio,
+ struct ath10k_sdio_rx_data *pkt,
+ u32 *lookaheads,
+ int *n_lookaheads)
+{
+ int status = 0;
+ struct ath10k_htc *htc = &ar_sdio->ar->htc;
+ struct sk_buff *skb = pkt->skb;
+ struct ath10k_htc_hdr *htc_hdr = (struct ath10k_htc_hdr *)skb->data;
+ bool trailer_present = htc_hdr->flags & ATH10K_HTC_FLAG_TRAILER_PRESENT;
+ u16 payload_len;
+
+ payload_len = le16_to_cpu(htc_hdr->len);
+
+ if (trailer_present) {
+ u8 *trailer;
+ enum ath10k_htc_ep_id eid;
+
+ trailer = skb->data + sizeof(struct ath10k_htc_hdr) +
+ payload_len - htc_hdr->trailer_len;
+
+ eid = (enum ath10k_htc_ep_id)htc_hdr->eid;
+
+ status = ath10k_htc_process_trailer(htc,
+ trailer,
+ htc_hdr->trailer_len,
+ eid,
+ lookaheads,
+ n_lookaheads);
+ if (status)
+ goto err;
+
+ skb_pull(skb, sizeof(*htc_hdr));
+ skb_trim(skb, skb->len - htc_hdr->trailer_len);
+ }
+
+err:
+ return status;
+}
+
+static inline void ath10k_sdio_mbox_free_rx_pkt(struct ath10k_sdio_rx_data *pkt)
+{
+ dev_kfree_skb(pkt->skb);
+ pkt->skb = NULL;
+ pkt->alloc_len = 0;
+ pkt->act_len = 0;
+}
+
+static inline int ath10k_sdio_mbox_alloc_rx_pkt(struct ath10k_sdio_rx_data *pkt,
+ size_t act_len, size_t full_len,
+ bool part_of_bundle,
+ bool last_in_bundle)
+{
+ pkt->skb = dev_alloc_skb(full_len);
+ if (!pkt->skb)
+ return -ENOMEM;
+
+ pkt->act_len = act_len;
+ pkt->alloc_len = full_len;
+ pkt->part_of_bundle = part_of_bundle;
+ pkt->last_in_bundle = last_in_bundle;
+
+ return 0;
+}
+
+static int ath10k_sdio_mbox_rx_process_packets(struct ath10k_sdio *ar_sdio,
+ u32 lookaheads[],
+ int *n_lookahead)
+{
+ struct ath10k *ar = ar_sdio->ar;
+ struct ath10k_htc *htc = &ar->htc;
+ struct ath10k_sdio_rx_data *pkt;
+ int status = 0, i;
+
+ for (i = 0; i < ar_sdio->n_rx_pkts; i++) {
+ struct ath10k_htc_ep *ep;
+ enum ath10k_htc_ep_id id;
+ u32 *lookaheads_local = lookaheads;
+ int *n_lookahead_local = n_lookahead;
+
+ id = ((struct ath10k_htc_hdr *)&lookaheads[i])->eid;
+
+ if (id >= ATH10K_HTC_EP_COUNT) {
+ ath10k_err(ar, "Invalid endpoint in look-ahead: %d\n",
+ id);
+ status = -ENOMEM;
+ goto out;
+ }
+
+ ep = &htc->endpoint[id];
+
+ if (ep->service_id == 0) {
+ ath10k_err(ar, "ep %d is not connected !\n", id);
+ status = -ENOMEM;
+ goto out;
+ }
+
+ pkt = &ar_sdio->rx_pkts[i];
+
+ if (pkt->part_of_bundle && !pkt->last_in_bundle) {
+ /* Only read lookahead's from RX trailers
+ * for the last packet in a bundle.
+ */
+ lookaheads_local = NULL;
+ n_lookahead_local = NULL;
+ }
+
+ status = ath10k_sdio_mbox_rx_process_packet(ar_sdio,
+ pkt,
+ lookaheads_local,
+ n_lookahead_local);
+ if (status)
+ goto out;
+
+ ep->ep_ops.ep_rx_complete(ar_sdio->ar, pkt->skb);
+ /* The RX complete handler now owns the skb...*/
+ pkt->skb = NULL;
+ pkt->alloc_len = 0;
+ }
+
+out:
+ /* Free all packets that was not passed on to the RX completion
+ * handler...
+ */
+ for (; i < ar_sdio->n_rx_pkts; i++)
+ ath10k_sdio_mbox_free_rx_pkt(&ar_sdio->rx_pkts[i]);
+
+ return status;
+}
+
+static int alloc_pkt_bundle(struct ath10k *ar,
+ struct ath10k_sdio_rx_data *rx_pkts,
+ struct ath10k_htc_hdr *htc_hdr,
+ size_t full_len, size_t act_len, size_t *bndl_cnt)
+{
+ int i, status = 0;
+
+ *bndl_cnt = (htc_hdr->flags & ATH10K_HTC_FLAG_BUNDLE_MASK) >>
+ ATH10K_HTC_FLAG_BUNDLE_LSB;
+
+ if (*bndl_cnt > HTC_HOST_MAX_MSG_PER_BUNDLE) {
+ ath10k_err(ar,
+ "HTC bundle len %u exceeds maximum %u !\n",
+ le16_to_cpu(htc_hdr->len),
+ HTC_HOST_MAX_MSG_PER_BUNDLE);
+ status = -ENOMEM;
+ goto out;
+ }
+
+ /* Allocate bndl_cnt extra skb's for the bundle.
+ * The package containing the
+ * ATH10K_HTC_FLAG_BUNDLE_MASK flag is not included
+ * in bndl_cnt. The skb for that packet will be
+ * allocated separately.
+ */
+ for (i = 0; i < *bndl_cnt; i++) {
+ status = ath10k_sdio_mbox_alloc_rx_pkt(&rx_pkts[i],
+ act_len,
+ full_len,
+ true,
+ false);
+ if (status)
+ goto out;
+ }
+
+out:
+ return status;
+}
+
+static int ath10k_sdio_mbox_rx_alloc(struct ath10k_sdio *ar_sdio,
+ u32 lookaheads[], int n_lookaheads)
+{
+ int status = 0, i;
+ struct ath10k *ar = ar_sdio->ar;
+
+ if (n_lookaheads > ATH10K_SDIO_MAX_RX_MSGS) {
+ ath10k_err(ar,
+ "The total number of pkgs to be fetched (%u) exceeds maximum %u !\n",
+ n_lookaheads,
+ ATH10K_SDIO_MAX_RX_MSGS);
+ status = -ENOMEM;
+ goto err;
+ }
+
+ for (i = 0; i < n_lookaheads; i++) {
+ struct ath10k_htc_hdr *htc_hdr =
+ (struct ath10k_htc_hdr *)&lookaheads[i];
+ size_t full_len, act_len;
+ bool last_in_bundle = false;
+
+ if (le16_to_cpu(htc_hdr->len) >
+ ATH10K_HTC_MBOX_MAX_PAYLOAD_LENGTH) {
+ ath10k_err(ar,
+ "payload len %d exceeds max htc : %u!\n",
+ le16_to_cpu(htc_hdr->len),
+ ATH10K_HTC_MBOX_MAX_PAYLOAD_LENGTH);
+ status = -ENOMEM;
+ goto err;
+ }
+
+ act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr);
+ full_len = CALC_TXRX_PADDED_LEN(ar_sdio, act_len);
+
+ if (full_len > ATH10K_SDIO_MAX_BUFFER_SIZE) {
+ ath10k_warn(ar,
+ "Rx buffer requested with invalid length htc_hdr:eid %d, flags 0x%x, len %d\n",
+ htc_hdr->eid, htc_hdr->flags,
+ le16_to_cpu(htc_hdr->len));
+ status = -EINVAL;
+ goto err;
+ }
+
+ if (htc_hdr->flags & ATH10K_HTC_FLAG_BUNDLE_MASK) {
+ /* HTC header indicates that every packet to follow
+ * has the same padded length so that it can be
+ * optimally fetched as a full bundle.
+ */
+ size_t bndl_cnt;
+
+ status = alloc_pkt_bundle(ar, &ar_sdio->rx_pkts[i],
+ htc_hdr,
+ full_len, act_len, &bndl_cnt);
+
+ n_lookaheads += bndl_cnt;
+ i += bndl_cnt;
+ /*Next buffer will be the last in the bundle */
+ last_in_bundle = true;
+ }
+
+ /* Allocate skb for packet. If the packet had the
+ * ATH10K_HTC_FLAG_BUNDLE_MASK flag set, all bundled
+ * packet skb's have been allocated in the previous step.
+ */
+ status = ath10k_sdio_mbox_alloc_rx_pkt(&ar_sdio->rx_pkts[i],
+ act_len,
+ full_len,
+ last_in_bundle,
+ last_in_bundle);
+ }
+
+ ar_sdio->n_rx_pkts = i;
+
+ return 0;
+err:
+
+ for (i = 0; i < ATH10K_SDIO_MAX_RX_MSGS; i++) {
+ if (!ar_sdio->rx_pkts[i].alloc_len)
+ break;
+ ath10k_sdio_mbox_free_rx_pkt(&ar_sdio->rx_pkts[i]);
+ }
+
+ return status;
+}
+
+static int ath10k_sdio_mbox_rx_packet(struct ath10k_sdio *ar_sdio,
+ struct ath10k_sdio_rx_data *pkt)
+{
+ struct ath10k *ar = ar_sdio->ar;
+ struct sk_buff *skb = pkt->skb;
+ int status;
+
+ status = ath10k_sdio_read_write_sync(ar,
+ ar_sdio->mbox_info.htc_addr,
+ skb->data, pkt->alloc_len,
+ HIF_RD_SYNC_BLOCK_FIX);
+
+ pkt->status = status;
+ if (!status)
+ skb_put(skb, pkt->act_len);
+
+ return status;
+}
+
+static int ath10k_sdio_mbox_rx_fetch(struct ath10k_sdio *ar_sdio)
+{
+ int i, status = 0;
+
+ for (i = 0; i < ar_sdio->n_rx_pkts; i++) {
+ status = ath10k_sdio_mbox_rx_packet(ar_sdio,
+ &ar_sdio->rx_pkts[i]);
+ if (status)
+ goto err;
+ }
+
+ return 0;
+err:
+ /* Free all packets that was not successfully fetched. */
+ for (; i < ar_sdio->n_rx_pkts; i++)
+ ath10k_sdio_mbox_free_rx_pkt(&ar_sdio->rx_pkts[i]);
+
+ return status;
+}
+
+/* Disable packet reception (used in case the host runs out of buffers)
+ * using the interrupt enable registers through the host I/F
+ */
+static int ath10k_sdio_hif_rx_control(struct ath10k_sdio *ar_sdio,
+ bool enable_rx)
+{
+ int status = 0;
+ struct ath10k_sdio_irq_enable_reg regs;
+ struct ath10k_sdio_irq_data *irq_data = &ar_sdio->irq_data;
+
+ ath10k_dbg(ar_sdio->ar, ATH10K_DBG_SDIO, "hif rx %s\n",
+ enable_rx ? "enable" : "disable");
+
+ spin_lock_bh(&irq_data->lock);
+
+ if (enable_rx)
+ irq_data->irq_en_reg.int_status_en |=
+ SM(0x01, MBOX_INT_STATUS_ENABLE_MBOX_DATA);
+ else
+ irq_data->irq_en_reg.int_status_en &=
+ ~SM(0x01, MBOX_INT_STATUS_ENABLE_MBOX_DATA);
+
+ regs = irq_data->irq_en_reg;
+
+ spin_unlock_bh(&irq_data->lock);
+
+ status = ath10k_sdio_read_write_sync(ar_sdio->ar,
+ MBOX_INT_STATUS_ENABLE_ADDRESS,
+ ®s.int_status_en, sizeof(regs),
+ HIF_WR_SYNC_BYTE_INC);
+
+ return status;
+}
+
+int ath10k_sdio_mbox_rxmsg_pending_handler(struct ath10k_sdio *ar_sdio,
+ u32 msg_lookahead, bool *done)
+{
+ struct ath10k *ar = ar_sdio->ar;
+ int status = 0;
+ u32 lookaheads[ATH10K_SDIO_MAX_RX_MSGS];
+ int n_lookaheads = 1;
+
+ *done = true;
+
+ /* Copy the lookahead obtained from the HTC register table into our
+ * temp array as a start value.
+ */
+ lookaheads[0] = msg_lookahead;
+
+ for (;;) {
+ /* Try to allocate as many HTC RX packets indicated by
+ * n_lookaheads.
+ */
+ status = ath10k_sdio_mbox_rx_alloc(ar_sdio, lookaheads,
+ n_lookaheads);
+ if (status)
+ break;
+
+ if (ar_sdio->n_rx_pkts >= 2)
+ /* A recv bundle was detected, force IRQ status
+ * re-check again.
+ */
+ *done = false;
+
+ status = ath10k_sdio_mbox_rx_fetch(ar_sdio);
+
+ /* Process fetched packets. This will potentially update
+ * n_lookaheads depending on if the packets contain lookahead
+ * reports.
+ */
+ n_lookaheads = 0;
+ status = ath10k_sdio_mbox_rx_process_packets(ar_sdio,
+ lookaheads,
+ &n_lookaheads);
+
+ if (!n_lookaheads || status)
+ break;
+
+ /* For SYNCH processing, if we get here, we are running
+ * through the loop again due to updated lookaheads. Set
+ * flag that we should re-check IRQ status registers again
+ * before leaving IRQ processing, this can net better
+ * performance in high throughput situations.
+ */
+ *done = false;
+ }
+
+ if (status && (status != -ECANCELED))
+ ath10k_err(ar, "failed to get pending recv messages: %d\n",
+ status);
+
+ if (atomic_read(&ar_sdio->stopping)) {
+ ath10k_warn(ar, "host is going to stop. Turning of RX\n");
+ ath10k_sdio_hif_rx_control(ar_sdio, false);
+ }
+
+ return status;
+}
+
+static int ath10k_sdio_mbox_proc_dbg_intr(struct ath10k_sdio *ar_sdio)
+{
+ int ret;
+ u32 dummy;
+ struct ath10k *ar = ar_sdio->ar;
+
+ ath10k_warn(ar, "firmware crashed\n");
+
+ /* read counter to clear the interrupt, the debug error interrupt is
+ * counter 0.
+ */
+ ret = ath10k_sdio_read_write_sync(ar, MBOX_COUNT_DEC_ADDRESS,
+ (u8 *)&dummy, 4,
+ HIF_RD_SYNC_BYTE_INC);
+ if (ret)
+ ath10k_warn(ar, "Failed to clear debug interrupt: %d\n", ret);
+
+ return ret;
+}
+
+static int ath10k_sdio_mbox_proc_counter_intr(struct ath10k_sdio *ar_sdio)
+{
+ u8 counter_int_status;
+ struct ath10k_sdio_irq_data *irq_data = &ar_sdio->irq_data;
+
+ counter_int_status = irq_data->irq_proc_reg.counter_int_status &
+ irq_data->irq_en_reg.cntr_int_status_en;
+
+ /* NOTE: other modules like GMBOX may use the counter interrupt for
+ * credit flow control on other counters, we only need to check for
+ * the debug assertion counter interrupt.
+ */
+ if (counter_int_status & ATH10K_SDIO_TARGET_DEBUG_INTR_MASK)
+ return ath10k_sdio_mbox_proc_dbg_intr(ar_sdio);
+
+ return 0;
+}
+
+static int ath10k_sdio_mbox_proc_err_intr(struct ath10k_sdio *ar_sdio)
+{
+ int status;
+ u8 error_int_status;
+ u8 reg_buf[4];
+ struct ath10k_sdio_irq_data *irq_data = &ar_sdio->irq_data;
+ struct ath10k *ar = ar_sdio->ar;
+
+ ath10k_dbg(ar, ATH10K_DBG_SDIO, "error interrupt\n");
+
+ error_int_status = irq_data->irq_proc_reg.error_int_status & 0x0F;
+ if (!error_int_status) {
+ WARN_ON(1);
+ return -EIO;
+ }
+
+ ath10k_dbg(ar, ATH10K_DBG_SDIO,
+ "valid interrupt source(s) in ERROR_INT_STATUS: 0x%x\n",
+ error_int_status);
+
+ if (MS(error_int_status, MBOX_ERROR_INT_STATUS_WAKEUP))
+ ath10k_dbg(ar, ATH10K_DBG_SDIO, "error : wakeup\n");
+
+ if (MS(error_int_status, MBOX_ERROR_INT_STATUS_RX_UNDERFLOW))
+ ath10k_err(ar, "rx underflow\n");
+
+ if (MS(error_int_status, MBOX_ERROR_INT_STATUS_TX_OVERFLOW))
+ ath10k_err(ar, "tx overflow\n");
+
+ /* Clear the interrupt */
+ irq_data->irq_proc_reg.error_int_status &= ~error_int_status;
+
+ /* set W1C value to clear the interrupt, this hits the register first */
+ reg_buf[0] = error_int_status;
+ reg_buf[1] = 0;
+ reg_buf[2] = 0;
+ reg_buf[3] = 0;
+
+ status = ath10k_sdio_read_write_sync(ar,
+ MBOX_ERROR_INT_STATUS_ADDRESS,
+ reg_buf, 4, HIF_WR_SYNC_BYTE_FIX);
+
+ WARN_ON(status);
+
+ return status;
+}
+
+static int ath10k_sdio_mbox_proc_cpu_intr(struct ath10k_sdio *ar_sdio)
+{
+ int status;
+ struct ath10k_sdio_irq_data *irq_data = &ar_sdio->irq_data;
+ struct ath10k *ar = ar_sdio->ar;
+ u8 cpu_int_status, reg_buf[4];
+
+ cpu_int_status = irq_data->irq_proc_reg.cpu_int_status &
+ irq_data->irq_en_reg.cpu_int_status_en;
+ if (!cpu_int_status) {
+ WARN_ON(1);
+ return -EIO;
+ }
+
+ /* Clear the interrupt */
+ irq_data->irq_proc_reg.cpu_int_status &= ~cpu_int_status;
+
+ /* Set up the register transfer buffer to hit the register 4 times ,
+ * this is done to make the access 4-byte aligned to mitigate issues
+ * with host bus interconnects that restrict bus transfer lengths to
+ * be a multiple of 4-bytes.
+ */
+
+ /* set W1C value to clear the interrupt, this hits the register first */
+ reg_buf[0] = cpu_int_status;
+ /* the remaining are set to zero which have no-effect */
+ reg_buf[1] = 0;
+ reg_buf[2] = 0;
+ reg_buf[3] = 0;
+
+ status = ath10k_sdio_read_write_sync(ar,
+ MBOX_CPU_INT_STATUS_ADDRESS,
+ reg_buf, 4, HIF_WR_SYNC_BYTE_FIX);
+
+ WARN_ON(status);
+
+ return status;
+}
+
+/* process pending interrupts synchronously */
+static int ath10k_sdio_mbox_proc_pending_irqs(struct ath10k_sdio *ar_sdio,
+ bool *done)
+{
+ struct ath10k_sdio_irq_data *irq_data = &ar_sdio->irq_data;
+ struct ath10k *ar = ar_sdio->ar;
+ struct ath10k_sdio_irq_proc_registers *rg;
+ int status = 0;
+ u8 host_int_status = 0;
+ u32 lookahead = 0;
+ u8 htc_mbox = 1 << ATH10K_HTC_MAILBOX;
+
+ /* NOTE: HIF implementation guarantees that the context of this
+ * call allows us to perform SYNCHRONOUS I/O, that is we can block,
+ * sleep or call any API that can block or switch thread/task
+ * contexts. This is a fully schedulable context.
+ */
+
+ /* Process pending intr only when int_status_en is clear, it may
+ * result in unnecessary bus transaction otherwise. Target may be
+ * unresponsive at the time.
+ */
+ if (irq_data->irq_en_reg.int_status_en) {
+ /* Read the first sizeof(struct ath10k_irq_proc_registers)
+ * bytes of the HTC register table. This
+ * will yield us the value of different int status
+ * registers and the lookahead registers.
+ */
+ status = ath10k_sdio_read_write_sync(
+ ar,
+ MBOX_HOST_INT_STATUS_ADDRESS,
+ (u8 *)&irq_data->irq_proc_reg,
+ sizeof(irq_data->irq_proc_reg),
+ HIF_RD_SYNC_BYTE_INC);
+ if (status)
+ goto out;
+
+ /* Update only those registers that are enabled */
+ host_int_status = irq_data->irq_proc_reg.host_int_status &
+ irq_data->irq_en_reg.int_status_en;
+
+ /* Look at mbox status */
+ if (host_int_status & htc_mbox) {
+ /* Mask out pending mbox value, we use look ahead as
+ * the real flag for mbox processing.
+ */
+ host_int_status &= ~htc_mbox;
+ if (irq_data->irq_proc_reg.rx_lookahead_valid &
+ htc_mbox) {
+ rg = &irq_data->irq_proc_reg;
+ lookahead = le32_to_cpu(
+ rg->rx_lookahead[ATH10K_HTC_MAILBOX]);
+ if (!lookahead)
+ ath10k_err(ar, "lookahead is zero!\n");
+ }
+ }
+ }
+
+ if (!host_int_status && !lookahead) {
+ *done = true;
+ goto out;
+ }
+
+ if (lookahead) {
+ ath10k_dbg(ar, ATH10K_DBG_SDIO,
+ "pending mailbox msg, lookahead: 0x%08X\n",
+ lookahead);
+
+ status = ath10k_sdio_mbox_rxmsg_pending_handler(ar_sdio,
+ lookahead,
+ done);
+ if (status)
+ goto out;
+ }
+
+ /* now, handle the rest of the interrupts */
+ ath10k_dbg(ar, ATH10K_DBG_SDIO,
+ "valid interrupt source(s) for other interrupts: 0x%x\n",
+ host_int_status);
+
+ if (MS(host_int_status, MBOX_HOST_INT_STATUS_CPU)) {
+ /* CPU Interrupt */
+ status = ath10k_sdio_mbox_proc_cpu_intr(ar_sdio);
+ if (status)
+ goto out;
+ }
+
+ if (MS(host_int_status, MBOX_HOST_INT_STATUS_ERROR)) {
+ /* Error Interrupt */
+ status = ath10k_sdio_mbox_proc_err_intr(ar_sdio);
+ if (status)
+ goto out;
+ }
+
+ if (MS(host_int_status, MBOX_HOST_INT_STATUS_COUNTER))
+ /* Counter Interrupt */
+ status = ath10k_sdio_mbox_proc_counter_intr(ar_sdio);
+
+out:
+ /* An optimization to bypass reading the IRQ status registers
+ * unecessarily which can re-wake the target, if upper layers
+ * determine that we are in a low-throughput mode, we can rely on
+ * taking another interrupt rather than re-checking the status
+ * registers which can re-wake the target.
+ *
+ * NOTE : for host interfaces that makes use of detecting pending
+ * mbox messages at hif can not use this optimization due to
+ * possible side effects, SPI requires the host to drain all
+ * messages from the mailbox before exiting the ISR routine.
+ */
+
+ ath10k_dbg(ar, ATH10K_DBG_SDIO,
+ "%s: (done:%d, status=%d)\n", __func__, *done, status);
+
+ return status;
+}
+
+/* Macro to check if DMA buffer is WORD-aligned and DMA-able.
+ * Most host controllers assume the buffer is DMA'able and will
+ * bug-check otherwise (i.e. buffers on the stack). virt_addr_valid
+ * check fails on stack memory.
+ */
+static inline bool buf_needs_bounce(u8 *buf)
+{
+ return ((unsigned long)buf & 0x3) || !virt_addr_valid(buf);
+}
+
+static inline enum ath10k_htc_ep_id pipe_id_to_eid(u8 pipe_id)
+{
+ return (enum ath10k_htc_ep_id)pipe_id;
+}
+
+static void ath10k_sdio_set_mbox_info(struct ath10k_sdio *ar_sdio)
+{
+ struct ath10k_mbox_info *mbox_info = &ar_sdio->mbox_info;
+ u16 device = ar_sdio->func->device;
+
+ mbox_info->htc_addr = ATH10K_HIF_MBOX_BASE_ADDR;
+ mbox_info->block_size = ATH10K_HIF_MBOX_BLOCK_SIZE;
+ mbox_info->block_mask = ATH10K_HIF_MBOX_BLOCK_SIZE - 1;
+ mbox_info->gmbox_addr = ATH10K_HIF_GMBOX_BASE_ADDR;
+ mbox_info->gmbox_sz = ATH10K_HIF_GMBOX_WIDTH;
+
+ mbox_info->ext_info[0].htc_ext_addr = ATH10K_HIF_MBOX0_EXT_BASE_ADDR;
+
+ if ((device & ATH10K_MANUFACTURER_ID_REV_MASK) < 4)
+ mbox_info->ext_info[0].htc_ext_sz = ATH10K_HIF_MBOX0_EXT_WIDTH;
+ else
+ /* from rome 2.0(0x504), the width has been extended
+ * to 56K
+ */
+ mbox_info->ext_info[0].htc_ext_sz =
+ ATH10K_HIF_MBOX0_EXT_WIDTH_ROME_2_0;
+
+ mbox_info->ext_info[1].htc_ext_addr =
+ mbox_info->ext_info[0].htc_ext_addr +
+ mbox_info->ext_info[0].htc_ext_sz +
+ ATH10K_HIF_MBOX_DUMMY_SPACE_SIZE;
+ mbox_info->ext_info[1].htc_ext_sz = ATH10K_HIF_MBOX1_EXT_WIDTH;
+}
+
+static inline void ath10k_sdio_set_cmd52_arg(u32 *arg, u8 write, u8 raw,
+ unsigned int address,
+ unsigned char val)
+{
+ const u8 func = 0;
+
+ *arg = ((write & 1) << 31) |
+ ((func & 0x7) << 28) |
+ ((raw & 1) << 27) |
+ (1 << 26) |
+ ((address & 0x1FFFF) << 9) |
+ (1 << 8) |
+ (val & 0xFF);
+}
+
+static int ath10k_sdio_func0_cmd52_wr_byte(struct mmc_card *card,
+ unsigned int address,
+ unsigned char byte)
+{
+ struct mmc_command io_cmd;
+
+ memset(&io_cmd, 0, sizeof(io_cmd));
+ ath10k_sdio_set_cmd52_arg(&io_cmd.arg, 1, 0, address, byte);
+ io_cmd.opcode = SD_IO_RW_DIRECT;
+ io_cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
+
+ return mmc_wait_for_cmd(card->host, &io_cmd, 0);
+}
+
+static int ath10k_sdio_func0_cmd52_rd_byte(struct mmc_card *card,
+ unsigned int address,
+ unsigned char *byte)
+{
+ int ret;
+ struct mmc_command io_cmd;
+
+ memset(&io_cmd, 0, sizeof(io_cmd));
+ ath10k_sdio_set_cmd52_arg(&io_cmd.arg, 0, 0, address, 0);
+ io_cmd.opcode = SD_IO_RW_DIRECT;
+ io_cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
+
+ ret = mmc_wait_for_cmd(card->host, &io_cmd, 0);
+ if (!ret)
+ *byte = io_cmd.resp[0];
+
+ return ret;
+}
+
+static int ath10k_sdio_io(struct ath10k_sdio *ar_sdio, u32 request, u32 addr,
+ u8 *buf, u32 len)
+{
+ int ret = 0;
+ struct sdio_func *func = ar_sdio->func;
+ struct ath10k *ar = ar_sdio->ar;
+
+ sdio_claim_host(func);
+
+ if (request & HIF_WRITE) {
+ if (request & HIF_FIXED_ADDRESS)
+ ret = sdio_writesb(func, addr, buf, len);
+ else
+ ret = sdio_memcpy_toio(func, addr, buf, len);
+ } else {
+ if (request & HIF_FIXED_ADDRESS)
+ ret = sdio_readsb(func, buf, addr, len);
+ else
+ ret = sdio_memcpy_fromio(func, buf, addr, len);
+ }
+
+ sdio_release_host(func);
+
+ ath10k_dbg(ar, ATH10K_DBG_SDIO, "%s addr 0x%x%s buf 0x%p len %d\n",
+ request & HIF_WRITE ? "wr" : "rd", addr,
+ request & HIF_FIXED_ADDRESS ? " (fixed)" : "", buf, len);
+ ath10k_dbg_dump(ar, ATH10K_DBG_SDIO_DUMP, NULL,
+ request & HIF_WRITE ? "sdio wr " : "sdio rd ",
+ buf, len);
+
+ return ret;
+}
+
+static struct ath10k_sdio_bus_request
+*ath10k_sdio_alloc_busreq(struct ath10k_sdio *ar_sdio)
+{
+ struct ath10k_sdio_bus_request *bus_req;
+
+ spin_lock_bh(&ar_sdio->lock);
+
+ if (list_empty(&ar_sdio->bus_req_freeq)) {
+ spin_unlock_bh(&ar_sdio->lock);
+ return NULL;
+ }
+
+ bus_req = list_first_entry(&ar_sdio->bus_req_freeq,
+ struct ath10k_sdio_bus_request, list);
+ list_del(&bus_req->list);
+
+ spin_unlock_bh(&ar_sdio->lock);
+
+ return bus_req;
+}
+
+static void ath10k_sdio_free_bus_req(struct ath10k_sdio *ar_sdio,
+ struct ath10k_sdio_bus_request *bus_req)
+{
+ spin_lock_bh(&ar_sdio->lock);
+ list_add_tail(&bus_req->list, &ar_sdio->bus_req_freeq);
+ spin_unlock_bh(&ar_sdio->lock);
+}
+
+static int ath10k_sdio_read_write_sync(struct ath10k *ar, u32 addr, u8 *buf,
+ u32 len, u32 request)
+{
+ struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
+ u8 *tbuf = NULL;
+ int ret;
+ bool bounced = false;
+
+ if (request & HIF_BLOCK_BASIS)
+ len = round_down(len, ar_sdio->mbox_info.block_size);
+
+ if (buf_needs_bounce(buf)) {
+ if (!ar_sdio->dma_buffer)
+ return -ENOMEM;
+ /* FIXME: I am not sure if it is always correct to assume
+ * that the SDIO irq is a "fake" irq and sleep is possible.
+ * (this function will get called from
+ * ath10k_sdio_irq_handler
+ */
+ mutex_lock(&ar_sdio->dma_buffer_mutex);
+ tbuf = ar_sdio->dma_buffer;
+
+ if (request & HIF_WRITE)
+ memcpy(tbuf, buf, len);
+
+ bounced = true;
+ } else {
+ tbuf = buf;
+ }
+
+ ret = ath10k_sdio_io(ar_sdio, request, addr, tbuf, len);
+ if ((request & HIF_READ) && bounced)
+ memcpy(buf, tbuf, len);
+
+ if (bounced)
+ mutex_unlock(&ar_sdio->dma_buffer_mutex);
+
+ return ret;
+}
+
+static void __ath10k_sdio_write_async(struct ath10k_sdio *ar_sdio,
+ struct ath10k_sdio_bus_request *req)
+{
+ int status;
+ struct ath10k_htc_ep *ep;
+ struct sk_buff *skb;
+
+ skb = req->skb;
+ status = ath10k_sdio_read_write_sync(ar_sdio->ar, req->address,
+ skb->data, req->len,
+ req->request);
+ ep = &ar_sdio->ar->htc.endpoint[req->eid];
+ ath10k_htc_notify_tx_completion(ep, skb);
+ ath10k_sdio_free_bus_req(ar_sdio, req);
+}
+
+static void ath10k_sdio_write_async_work(struct work_struct *work)
+{
+ struct ath10k_sdio *ar_sdio;
+ struct ath10k_sdio_bus_request *req, *tmp_req;
+
+ ar_sdio = container_of(work, struct ath10k_sdio, wr_async_work);
+
+ spin_lock_bh(&ar_sdio->wr_async_lock);
+ list_for_each_entry_safe(req, tmp_req, &ar_sdio->wr_asyncq, list) {
+ list_del(&req->list);
+ spin_unlock_bh(&ar_sdio->wr_async_lock);
+ __ath10k_sdio_write_async(ar_sdio, req);
+ spin_lock_bh(&ar_sdio->wr_async_lock);
+ }
+ spin_unlock_bh(&ar_sdio->wr_async_lock);
+}
+
+static void ath10k_sdio_irq_handler(struct sdio_func *func)
+{
+ int status = 0;
+ unsigned long timeout;
+ struct ath10k_sdio *ar_sdio;
+ bool done = false;
+
+ ar_sdio = sdio_get_drvdata(func);
+ atomic_set(&ar_sdio->irq_handling, 1);
+
+ /* Release the host during interrupts so we can pick it back up when
+ * we process commands.
+ */
+ sdio_release_host(ar_sdio->func);
+
+ timeout = jiffies + ATH10K_SDIO_HIF_COMMUNICATION_TIMEOUT_HZ;
+ while (time_before(jiffies, timeout) && !done) {
+ status = ath10k_sdio_mbox_proc_pending_irqs(ar_sdio, &done);
+ if (status)
+ break;
+ }
+
+ sdio_claim_host(ar_sdio->func);
+
+ atomic_set(&ar_sdio->irq_handling, 0);
+ wake_up(&ar_sdio->irq_wq);
+
+ WARN_ON(status && status != -ECANCELED);
+}
+
+static int ath10k_sdio_hif_disable_intrs(struct ath10k_sdio *ar_sdio)
+{
+ int ret;
+ struct ath10k_sdio_irq_enable_reg regs;
+ struct ath10k_sdio_irq_data *irq_data = &ar_sdio->irq_data;
+
+ memset(®s, 0, sizeof(regs));
+
+ ret = ath10k_sdio_read_write_sync(ar_sdio->ar,
+ MBOX_INT_STATUS_ENABLE_ADDRESS,
+ ®s.int_status_en, sizeof(regs),
+ HIF_WR_SYNC_BYTE_INC);
+ if (ret) {
+ ath10k_err(ar_sdio->ar, "Unable to disable sdio interrupts\n");
+ return ret;
+ }
+
+ spin_lock_bh(&irq_data->lock);
+ irq_data->irq_en_reg = regs;
+ spin_unlock_bh(&irq_data->lock);
+
+ return 0;
+}
+
+static int ath10k_sdio_hif_power_up(struct ath10k *ar)
+{
+ struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
+ struct sdio_func *func = ar_sdio->func;
+ int ret = 0;
+
+ if (!ar_sdio->is_disabled)
+ return 0;
+
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "sdio power on\n");
+
+ sdio_claim_host(func);
+
+ ret = sdio_enable_func(func);
+ if (ret) {
+ ath10k_err(ar, "Unable to enable sdio func: %d)\n", ret);
+ sdio_release_host(func);
+ return ret;
+ }
+
+ sdio_release_host(func);
+
+ /* Wait for hardware to initialise. It should take a lot less than
+ * 20 ms but let's be conservative here.
+ */
+ msleep(20);
+
+ ar_sdio->is_disabled = false;
+
+ ret = ath10k_sdio_hif_disable_intrs(ar_sdio);
+
+ return ret;
+}
+
+static void ath10k_sdio_hif_power_down(struct ath10k *ar)
+{
+ struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
+ int ret;
+
+ if (ar_sdio->is_disabled)
+ return;
+
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "sdio power off\n");
+
+ /* Disable the card */
+ sdio_claim_host(ar_sdio->func);
+ ret = sdio_disable_func(ar_sdio->func);
+ sdio_release_host(ar_sdio->func);
+
+ if (ret)
+ ath10k_dbg(ar, ATH10K_DBG_BOOT,
+ "Unable to disable sdio: %d\n", ret);
+
+ ar_sdio->is_disabled = true;
+}
+
+int ath10k_sdio_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
+ struct ath10k_hif_sg_item *items, int n_items)
+{
+ int i;
+ u32 address;
+ struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
+ struct ath10k_sdio_bus_request *bus_req;
+
+ bus_req = ath10k_sdio_alloc_busreq(ar_sdio);
+
+ if (WARN_ON_ONCE(!bus_req))
+ return -ENOMEM;
+
+ for (i = 0; i < n_items; i++) {
+ bus_req->skb = items[i].transfer_context;
+ bus_req->request = HIF_WRITE;
+ bus_req->eid = pipe_id_to_eid(pipe_id);
+ /* Write TX data to the end of the mbox address space */
+ address = ar_sdio->mbox_addr[bus_req->eid] +
+ ar_sdio->mbox_size[bus_req->eid] - bus_req->skb->len;
+ bus_req->address = address;
+ bus_req->len = CALC_TXRX_PADDED_LEN(ar_sdio, bus_req->skb->len);
+
+ spin_lock_bh(&ar_sdio->wr_async_lock);
+ list_add_tail(&bus_req->list, &ar_sdio->wr_asyncq);
+ spin_unlock_bh(&ar_sdio->wr_async_lock);
+ }
+
+ queue_work(ar_sdio->workqueue, &ar_sdio->wr_async_work);
+
+ return 0;
+}
+
+static int ath10k_sdio_hif_enable_intrs(struct ath10k_sdio *ar_sdio)
+{
+ struct ath10k_sdio_irq_enable_reg regs;
+ int status;
+ struct ath10k_sdio_irq_data *irq_data = &ar_sdio->irq_data;
+
+ memset(®s, 0, sizeof(regs));
+
+ /* Enable all but CPU interrupts */
+ regs.int_status_en = SM(0x01, MBOX_INT_STATUS_ENABLE_ERROR) |
+ SM(0x01, MBOX_INT_STATUS_ENABLE_CPU) |
+ SM(0x01, MBOX_INT_STATUS_ENABLE_COUNTER);
+
+ /* NOTE: There are some cases where HIF can do detection of
+ * pending mbox messages which is disabled now.
+ */
+ regs.int_status_en |= SM(0x01, MBOX_INT_STATUS_ENABLE_MBOX_DATA);
+
+ /* Set up the CPU Interrupt status Register */
+ regs.cpu_int_status_en = 0;
+
+ /* Set up the Error Interrupt status Register */
+ regs.err_int_status_en =
+ SM(0x01, MBOX_ERROR_STATUS_ENABLE_RX_UNDERFLOW) |
+ SM(0x01, MBOX_ERROR_STATUS_ENABLE_TX_OVERFLOW);
+
+ /* Enable Counter interrupt status register to get fatal errors for
+ * debugging.
+ */
+ regs.cntr_int_status_en = SM(ATH10K_SDIO_TARGET_DEBUG_INTR_MASK,
+ MBOX_COUNTER_INT_STATUS_ENABLE_BIT);
+
+ status = ath10k_sdio_read_write_sync(ar_sdio->ar,
+ MBOX_INT_STATUS_ENABLE_ADDRESS,
+ ®s.int_status_en, sizeof(regs),
+ HIF_WR_SYNC_BYTE_INC);
+ if (status) {
+ ath10k_err(ar_sdio->ar,
+ "failed to update interrupt ctl reg err: %d\n",
+ status);
+ return status;
+ }
+
+ spin_lock_bh(&irq_data->lock);
+ irq_data->irq_en_reg = regs;
+ spin_unlock_bh(&irq_data->lock);
+
+ return 0;
+}
+
+static int ath10k_sdio_hif_start(struct ath10k *ar)
+{
+ int ret;
+ struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
+ u32 addr, val;
+
+ addr = host_interest_item_address(HI_ITEM(hi_acs_flags));
+
+ ret = ath10k_sdio_hif_diag_read32(ar, addr, &val);
+ if (ret) {
+ ath10k_err(ar, "Unable to read diag mem: %d\n", ret);
+ goto out;
+ }
+
+ if (val & HI_ACS_FLAGS_SDIO_SWAP_MAILBOX_FW_ACK) {
+ ath10k_dbg(ar, ATH10K_DBG_SDIO,
+ "Mailbox SWAP Service is enabled\n");
+ ar_sdio->swap_mbox = true;
+ }
+
+ /* eid 0 always uses the lower part of the extended mailbox address
+ * space (ext_info[0].htc_ext_addr).
+ */
+ ar_sdio->mbox_addr[0] = ar_sdio->mbox_info.ext_info[0].htc_ext_addr;
+ ar_sdio->mbox_size[0] = ar_sdio->mbox_info.ext_info[0].htc_ext_sz;
+
+ sdio_claim_host(ar_sdio->func);
+
+ /* Register the isr */
+ ret = sdio_claim_irq(ar_sdio->func, ath10k_sdio_irq_handler);
+ if (ret) {
+ ath10k_err(ar, "Failed to claim sdio irq: %d\n", ret);
+ sdio_release_host(ar_sdio->func);
+ goto out;
+ }
+
+ sdio_release_host(ar_sdio->func);
+
+ ret = ath10k_sdio_hif_enable_intrs(ar_sdio);
+ if (ret)
+ ath10k_err(ar, "Failed to enable sdio interrupts: %d\n", ret);
+
+out:
+ return ret;
+}
+
+static bool ath10k_sdio_is_on_irq(struct ath10k *ar)
+{
+ struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
+
+ return !atomic_read(&ar_sdio->irq_handling);
+}
+
+static void ath10k_sdio_irq_disable(struct ath10k *ar)
+{
+ struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
+ int ret;
+
+ sdio_claim_host(ar_sdio->func);
+
+ atomic_set(&ar_sdio->stopping, 1);
+
+ if (atomic_read(&ar_sdio->irq_handling)) {
+ sdio_release_host(ar_sdio->func);
+
+ ret = wait_event_interruptible(ar_sdio->irq_wq,
+ ath10k_sdio_is_on_irq(ar));
+ if (ret)
+ return;
+
+ sdio_claim_host(ar_sdio->func);
+ }
+
+ ret = sdio_release_irq(ar_sdio->func);
+ if (ret)
+ ath10k_err(ar, "Failed to release sdio irq: %d\n", ret);
+
+ sdio_release_host(ar_sdio->func);
+}
+
+static int ath10k_sdio_config(struct ath10k_sdio *ar_sdio)
+{
+ struct ath10k *ar = ar_sdio->ar;
+ struct sdio_func *func = ar_sdio->func;
+ unsigned char byte, asyncintdelay = 2;
+ int ret;
+
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "SDIO configuration\n");
+
+ sdio_claim_host(func);
+
+ byte = 0;
+ ret = ath10k_sdio_func0_cmd52_rd_byte(func->card,
+ SDIO_CCCR_DRIVE_STRENGTH,
+ &byte);
+
+ byte = (byte & (~(SDIO_DRIVE_DTSx_MASK << SDIO_DRIVE_DTSx_SHIFT))) |
+ SDIO_DTSx_SET_TYPE_D;
+
+ ret = ath10k_sdio_func0_cmd52_wr_byte(func->card,
+ SDIO_CCCR_DRIVE_STRENGTH,
+ byte);
+
+ byte = 0;
+ ret = ath10k_sdio_func0_cmd52_rd_byte(
+ func->card,
+ CCCR_SDIO_DRIVER_STRENGTH_ENABLE_ADDR,
+ &byte);
+
+ byte |= (CCCR_SDIO_DRIVER_STRENGTH_ENABLE_A |
+ CCCR_SDIO_DRIVER_STRENGTH_ENABLE_C |
+ CCCR_SDIO_DRIVER_STRENGTH_ENABLE_D);
+
+ ret = ath10k_sdio_func0_cmd52_wr_byte(
+ func->card,
+ CCCR_SDIO_DRIVER_STRENGTH_ENABLE_ADDR,
+ byte);
+ if (ret) {
+ ath10k_err(ar, "Failed to enable driver strengt\n");
+ goto out;
+ }
+
+ byte = 0;
+ ret = ath10k_sdio_func0_cmd52_rd_byte(func->card,
+ CCCR_SDIO_IRQ_MODE_REG_SDIO3,
+ &byte);
+
+ byte |= SDIO_IRQ_MODE_ASYNC_4BIT_IRQ_SDIO3;
+
+ ret = ath10k_sdio_func0_cmd52_wr_byte(func->card,
+ CCCR_SDIO_IRQ_MODE_REG_SDIO3,
+ byte);
+ if (ret) {
+ ath10k_err(ar, "Failed to enable 4-bit async irq mode %d\n",
+ ret);
+ goto out;
+ }
+
+ byte = 0;
+ ret = ath10k_sdio_func0_cmd52_rd_byte(func->card,
+ CCCR_SDIO_ASYNC_INT_DELAY_ADDRESS,
+ &byte);
+
+ byte = (byte & ~CCCR_SDIO_ASYNC_INT_DELAY_MASK) |
+ ((asyncintdelay << CCCR_SDIO_ASYNC_INT_DELAY_LSB) &
+ CCCR_SDIO_ASYNC_INT_DELAY_MASK);
+
+ ret = ath10k_sdio_func0_cmd52_wr_byte(func->card,
+ CCCR_SDIO_ASYNC_INT_DELAY_ADDRESS,
+ byte);
+
+ /* give us some time to enable, in ms */
+ func->enable_timeout = 100;
+
+ ret = sdio_set_block_size(func, ar_sdio->mbox_info.block_size);
+ if (ret) {
+ ath10k_err(ar, "Set sdio block size %d failed: %d)\n",
+ ar_sdio->mbox_info.block_size, ret);
+ goto out;
+ }
+
+out:
+ sdio_release_host(func);
+
+ return ret;
+}
+
+/* set the window address register (using 4-byte register access ). */
+static int ath10k_set_addrwin_reg(struct ath10k *ar, u32 reg_addr, u32 addr)
+{
+ int status;
+
+ status = ath10k_sdio_read_write_sync(ar, reg_addr, (u8 *)(&addr),
+ 4, HIF_WR_SYNC_BYTE_INC);
+
+ if (status) {
+ ath10k_err(ar, "%s: failed to write 0x%x to window reg: 0x%X\n",
+ __func__, addr, reg_addr);
+ return status;
+ }
+
+ return 0;
+}
+
+static int ath10k_sdio_hif_diag_read32(struct ath10k *ar, u32 address,
+ u32 *value)
+{
+ __le32 val = 0;
+ int ret;
+
+ ret = ath10k_sdio_hif_diag_read(ar, address, &val, sizeof(val));
+ *value = __le32_to_cpu(val);
+
+ return ret;
+}
+
+static int ath10k_sdio_hif_diag_read(struct ath10k *ar, u32 address, void *buf,
+ size_t buf_len)
+{
+ int status;
+
+ /* set window register to start read cycle */
+ status = ath10k_set_addrwin_reg(ar, MBOX_WINDOW_READ_ADDR_ADDRESS,
+ address);
+
+ if (status)
+ return status;
+
+ /* read the data */
+ status = ath10k_sdio_read_write_sync(ar, MBOX_WINDOW_DATA_ADDRESS,
+ (u8 *)buf, buf_len,
+ HIF_RD_SYNC_BYTE_INC);
+ if (status) {
+ ath10k_err(ar, "%s: failed to read from window data addr\n",
+ __func__);
+ return status;
+ }
+
+ return status;
+}
+
+static int ath10k_sdio_diag_write_mem(struct ath10k *ar, u32 address,
+ const void *data, int nbytes)
+{
+ int status;
+
+ /* set write data */
+ status = ath10k_sdio_read_write_sync(ar, MBOX_WINDOW_DATA_ADDRESS,
+ (u8 *)data, nbytes,
+ HIF_WR_SYNC_BYTE_INC);
+ if (status) {
+ ath10k_err(ar, "%s: failed to write 0x%p to window data addr\n",
+ __func__, data);
+ return status;
+ }
+
+ /* set window register, which starts the write cycle */
+ return ath10k_set_addrwin_reg(ar, MBOX_WINDOW_WRITE_ADDR_ADDRESS,
+ address);
+}
+
+static int ath10k_sdio_bmi_credits(struct ath10k *ar)
+{
+ u32 addr, cmd_credits;
+ unsigned long timeout;
+ int ret;
+
+ cmd_credits = 0;
+
+ /* Read the counter register to get the command credits */
+ addr = MBOX_COUNT_DEC_ADDRESS + ATH10K_HIF_MBOX_NUM_MAX * 4;
+
+ timeout = jiffies + BMI_COMMUNICATION_TIMEOUT_HZ;
+ while (time_before(jiffies, timeout) && !cmd_credits) {
+ /* Hit the credit counter with a 4-byte access, the first byte
+ * read will hit the counter and cause a decrement, while the
+ * remaining 3 bytes has no effect. The rationale behind this
+ * is to make all HIF accesses 4-byte aligned.
+ */
+ ret = ath10k_sdio_read_write_sync(ar, addr,
+ (u8 *)&cmd_credits,
+ sizeof(cmd_credits),
+ HIF_RD_SYNC_BYTE_INC);
+ if (ret) {
+ ath10k_err(ar,
+ "Unable to decrement the command credit count register: %d\n",
+ ret);
+ return ret;
+ }
+
+ /* The counter is only 8 bits.
+ * Ignore anything in the upper 3 bytes
+ */
+ cmd_credits &= 0xFF;
+ }
+
+ if (!cmd_credits) {
+ ath10k_err(ar, "bmi communication timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int ath10k_sdio_bmi_get_rx_lookahead(struct ath10k *ar)
+{
+ unsigned long timeout;
+ u32 rx_word = 0;
+ int ret = 0;
+
+ timeout = jiffies + BMI_COMMUNICATION_TIMEOUT_HZ;
+ while ((time_before(jiffies, timeout)) && !rx_word) {
+ ret = ath10k_sdio_read_write_sync(ar,
+ MBOX_HOST_INT_STATUS_ADDRESS,
+ (u8 *)&rx_word,
+ sizeof(rx_word),
+ HIF_RD_SYNC_BYTE_INC);
+ if (ret) {
+ ath10k_err(ar, "unable to read RX_LOOKAHEAD_VALID\n");
+ return ret;
+ }
+
+ /* all we really want is one bit */
+ rx_word &= 1;
+ }
+
+ if (!rx_word) {
+ ath10k_err(ar, "bmi_recv_buf FIFO empty\n");
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+int ath10k_sdio_hif_exchange_bmi_msg(struct ath10k *ar,
+ void *req, u32 req_len,
+ void *resp, u32 *resp_len)
+{
+ int ret = 0;
+ u32 addr;
+ struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
+
+ ret = ath10k_sdio_bmi_credits(ar);
+ if (ret)
+ return ret;
+
+ /* BMI data is written to the end of the mailbox address space */
+ addr = ar_sdio->mbox_info.htc_addr + ATH10K_HIF_MBOX_WIDTH - req_len;
+
+ ret = ath10k_sdio_read_write_sync(ar, addr, req, req_len,
+ HIF_WR_SYNC_BYTE_INC);
+ if (ret) {
+ ath10k_err(ar, "unable to send the bmi data to the device\n");
+ return ret;
+ }
+
+ if (!resp || !resp_len)
+ /* No response expected */
+ goto out;
+
+ /* During normal bootup, small reads may be required.
+ * Rather than issue an HIF Read and then wait as the Target
+ * adds successive bytes to the FIFO, we wait here until
+ * we know that response data is available.
+ *
+ * This allows us to cleanly timeout on an unexpected
+ * Target failure rather than risk problems at the HIF level.
+ * In particular, this avoids SDIO timeouts and possibly garbage
+ * data on some host controllers. And on an interconnect
+ * such as Compact Flash (as well as some SDIO masters) which
+ * does not provide any indication on data timeout, it avoids
+ * a potential hang or garbage response.
+ *
+ * Synchronization is more difficult for reads larger than the
+ * size of the MBOX FIFO (128B), because the Target is unable
+ * to push the 129th byte of data until AFTER the Host posts an
+ * HIF Read and removes some FIFO data. So for large reads the
+ * Host proceeds to post an HIF Read BEFORE all the data is
+ * actually available to read. Fortunately, large BMI reads do
+ * not occur in practice -- they're supported for debug/development.
+ *
+ * So Host/Target BMI synchronization is divided into these cases:
+ * CASE 1: length < 4
+ * Should not happen
+ *
+ * CASE 2: 4 <= length <= 128
+ * Wait for first 4 bytes to be in FIFO
+ * If CONSERVATIVE_BMI_READ is enabled, also wait for
+ * a BMI command credit, which indicates that the ENTIRE
+ * response is available in the the FIFO
+ *
+ * CASE 3: length > 128
+ * Wait for the first 4 bytes to be in FIFO
+ *
+ * For most uses, a small timeout should be sufficient and we will
+ * usually see a response quickly; but there may be some unusual
+ * (debug) cases of BMI_EXECUTE where we want an larger timeout.
+ * For now, we use an unbounded busy loop while waiting for
+ * BMI_EXECUTE.
+ *
+ * If BMI_EXECUTE ever needs to support longer-latency execution,
+ * especially in production, this code needs to be enhanced to sleep
+ * and yield. Also note that BMI_COMMUNICATION_TIMEOUT is currently
+ * a function of Host processor speed.
+ */
+ ret = ath10k_sdio_bmi_get_rx_lookahead(ar);
+ if (ret)
+ goto out;
+
+ /* We always read from the start of the mbox address */
+ addr = ar_sdio->mbox_info.htc_addr;
+ ret = ath10k_sdio_read_write_sync(ar, addr, resp, *resp_len,
+ HIF_RD_SYNC_BYTE_INC);
+ if (ret)
+ ath10k_err(ar, "Unable to read the bmi data from the device: %d\n",
+ ret);
+
+out:
+ return ret;
+}
+
+static void ath10k_sdio_hif_stop(struct ath10k *ar)
+{
+ struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
+ struct ath10k_sdio_bus_request *req, *tmp_req;
+
+ ath10k_sdio_irq_disable(ar);
+
+ cancel_work_sync(&ar_sdio->wr_async_work);
+
+ spin_lock_bh(&ar_sdio->wr_async_lock);
+
+ list_for_each_entry_safe(req, tmp_req, &ar_sdio->wr_asyncq, list) {
+ struct ath10k_htc_ep *ep;
+
+ list_del(&req->list);
+
+ ep = &ar->htc.endpoint[req->eid];
+ ath10k_htc_notify_tx_completion(ep, req->skb);
+ ath10k_sdio_free_bus_req(ar_sdio, req);
+ }
+
+ spin_unlock_bh(&ar_sdio->wr_async_lock);
+}
+
+#ifdef CONFIG_PM
+
+static int ath10k_sdio_hif_suspend(struct ath10k *ar)
+{
+ return -EOPNOTSUPP;
+}
+
+static int ath10k_sdio_hif_resume(struct ath10k *ar)
+{
+ struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
+
+ switch (ar->state) {
+ case ATH10K_STATE_OFF:
+ ath10k_dbg(ar, ATH10K_DBG_SDIO,
+ "sdio resume configuring sdio\n");
+
+ /* need to set sdio settings after power is cut from sdio */
+ ath10k_sdio_config(ar_sdio);
+ break;
+
+ case ATH10K_STATE_ON:
+ default:
+ break;
+ }
+
+ return 0;
+}
+#endif
+
+int ath10k_sdio_hif_map_service_to_pipe(struct ath10k *ar, u16 service_id,
+ u8 *ul_pipe, u8 *dl_pipe)
+{
+ int ret = 0, i;
+ bool ep_found = false;
+ enum ath10k_htc_ep_id eid;
+ struct ath10k_htc *htc = &ar->htc;
+ struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
+ u32 htt_addr, wmi_addr, htt_mbox_size, wmi_mbox_size;
+
+ /* For sdio, we are interested in the mapping between eid
+ * and pipeid rather than service_id to pipe_id.
+ * First we find out which eid has been allocated to the
+ * service...
+ */
+ for (i = 0; i < ATH10K_HTC_EP_COUNT; i++) {
+ if (htc->endpoint[i].service_id == service_id) {
+ eid = htc->endpoint[i].eid;
+ ep_found = true;
+ break;
+ }
+ }
+
+ if (!ep_found) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Then we create the simplest mapping possible between pipeid
+ * and eid
+ */
+ *ul_pipe = *dl_pipe = (u8)eid;
+
+ /* Normally, HTT will use the upper part of the extended
+ * mailbox address space (ext_info[1].htc_ext_addr) and WMI ctrl
+ * the lower part (ext_info[0].htc_ext_addr).
+ * If fw wants swapping of mailbox addresses, the opposite is true.
+ */
+ if (ar_sdio->swap_mbox) {
+ htt_addr = ar_sdio->mbox_info.ext_info[0].htc_ext_addr;
+ wmi_addr = ar_sdio->mbox_info.ext_info[1].htc_ext_addr;
+ htt_mbox_size = ar_sdio->mbox_info.ext_info[0].htc_ext_sz;
+ wmi_mbox_size = ar_sdio->mbox_info.ext_info[1].htc_ext_sz;
+ } else {
+ htt_addr = ar_sdio->mbox_info.ext_info[1].htc_ext_addr;
+ wmi_addr = ar_sdio->mbox_info.ext_info[0].htc_ext_addr;
+ htt_mbox_size = ar_sdio->mbox_info.ext_info[1].htc_ext_sz;
+ wmi_mbox_size = ar_sdio->mbox_info.ext_info[0].htc_ext_sz;
+ }
+
+ switch (service_id) {
+ case ATH10K_HTC_SVC_ID_RSVD_CTRL:
+ /* HTC ctrl ep mbox address has already been setup in
+ * ath10k_sdio_hif_start
+ */
+ break;
+ case ATH10K_HTC_SVC_ID_WMI_CONTROL:
+ ar_sdio->mbox_addr[eid] = wmi_addr;
+ ar_sdio->mbox_size[eid] = wmi_mbox_size;
+ ath10k_dbg(ar, ATH10K_DBG_SDIO,
+ "WMI ctrl mbox addr = %x, mbox_size = %x\n",
+ ar_sdio->mbox_addr[eid], ar_sdio->mbox_size[eid]);
+ break;
+ case ATH10K_HTC_SVC_ID_HTT_DATA_MSG:
+ ar_sdio->mbox_addr[eid] = htt_addr;
+ ar_sdio->mbox_size[eid] = htt_mbox_size;
+ ath10k_dbg(ar, ATH10K_DBG_SDIO,
+ "HTT data mbox addr = %x, mbox_size = %x\n",
+ ar_sdio->mbox_addr[eid], ar_sdio->mbox_size[eid]);
+ break;
+ default:
+ ath10k_err(ar, "Unsupported service ID: %x\n",
+ service_id);
+ ret = -EINVAL;
+ }
+
+out:
+ return ret;
+}
+
+void ath10k_sdio_hif_get_default_pipe(struct ath10k *ar,
+ u8 *ul_pipe, u8 *dl_pipe)
+{
+ ath10k_dbg(ar, ATH10K_DBG_SDIO, "sdio hif get default pipe\n");
+
+ /* HTC ctrl ep (SVC id 1) always has eid (and pipe_id in our
+ * case) == 0
+ */
+ *ul_pipe = 0;
+ *dl_pipe = 0;
+}
+
+static int ath10k_sdio_hif_fetch_cal_eeprom(struct ath10k *ar, void **data,
+ size_t *data_len)
+{
+ return -EOPNOTSUPP;
+}
+
+static void ath10k_sdio_write32(struct ath10k *ar, u32 offset, u32 value)
+{
+}
+
+static u32 ath10k_sdio_read32(struct ath10k *ar, u32 offset)
+{
+ return 0;
+}
+
+static void ath10k_sdio_hif_send_complete_check(struct ath10k *ar,
+ u8 pipe, int force)
+{
+}
+
+static u16 ath10k_sdio_hif_get_free_queue_number(struct ath10k *ar, u8 pipe)
+{
+ return 0;
+}
+
+static const struct ath10k_hif_ops ath10k_sdio_hif_ops = {
+ .tx_sg = ath10k_sdio_hif_tx_sg,
+ .diag_read = ath10k_sdio_hif_diag_read,
+ .diag_write = ath10k_sdio_diag_write_mem,
+ .exchange_bmi_msg = ath10k_sdio_hif_exchange_bmi_msg,
+ .start = ath10k_sdio_hif_start,
+ .stop = ath10k_sdio_hif_stop,
+ .map_service_to_pipe = ath10k_sdio_hif_map_service_to_pipe,
+ .get_default_pipe = ath10k_sdio_hif_get_default_pipe,
+ .send_complete_check = ath10k_sdio_hif_send_complete_check,
+ .get_free_queue_number = ath10k_sdio_hif_get_free_queue_number,
+ .power_up = ath10k_sdio_hif_power_up,
+ .power_down = ath10k_sdio_hif_power_down,
+ .read32 = ath10k_sdio_read32,
+ .write32 = ath10k_sdio_write32,
+#ifdef CONFIG_PM
+ .suspend = ath10k_sdio_hif_suspend,
+ .resume = ath10k_sdio_hif_resume,
+#endif
+ .fetch_cal_eeprom = ath10k_sdio_hif_fetch_cal_eeprom,
+};
+
+#ifdef CONFIG_PM_SLEEP
+
+/* Empty handlers so that mmc subsystem doesn't remove us entirely during
+ * suspend. We instead follow cfg80211 suspend/resume handlers.
+ */
+static int ath10k_sdio_pm_suspend(struct device *device)
+{
+ return 0;
+}
+
+static int ath10k_sdio_pm_resume(struct device *device)
+{
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(ath10k_sdio_pm_ops, ath10k_sdio_pm_suspend,
+ ath10k_sdio_pm_resume);
+
+#define ATH10K_SDIO_PM_OPS (&ath10k_sdio_pm_ops)
+
+#else
+
+#define ATH10K_SDIO_PM_OPS NULL
+
+#endif /* CONFIG_PM_SLEEP */
+
+static int ath10k_sdio_probe(struct sdio_func *func,
+ const struct sdio_device_id *id)
+{
+ int ret;
+ struct ath10k_sdio *ar_sdio;
+ struct ath10k *ar;
+ int i;
+ enum ath10k_hw_rev hw_rev;
+
+ hw_rev = ATH10K_HW_QCA6174;
+
+ ar = ath10k_core_create(sizeof(*ar_sdio), &func->dev, ATH10K_BUS_SDIO,
+ hw_rev, &ath10k_sdio_hif_ops);
+ if (!ar) {
+ dev_err(&func->dev, "failed to allocate core\n");
+ return -ENOMEM;
+ }
+
+ ath10k_dbg(ar, ATH10K_DBG_BOOT,
+ "sdio new func %d vendor 0x%x device 0x%x block 0x%x/0x%x\n",
+ func->num, func->vendor, func->device,
+ func->max_blksize, func->cur_blksize);
+
+ ar_sdio = ath10k_sdio_priv(ar);
+
+ ar_sdio->dma_buffer = kzalloc(ATH10K_HIF_DMA_BUFFER_SIZE, GFP_KERNEL);
+ if (!ar_sdio->dma_buffer) {
+ ret = -ENOMEM;
+ goto err_core_destroy;
+ }
+
+ ar_sdio->func = func;
+ sdio_set_drvdata(func, ar_sdio);
+
+ ar_sdio->is_disabled = true;
+ ar_sdio->ar = ar;
+
+ spin_lock_init(&ar_sdio->lock);
+ spin_lock_init(&ar_sdio->wr_async_lock);
+ spin_lock_init(&ar_sdio->irq_data.lock);
+ mutex_init(&ar_sdio->dma_buffer_mutex);
+
+ INIT_LIST_HEAD(&ar_sdio->bus_req_freeq);
+ INIT_LIST_HEAD(&ar_sdio->wr_asyncq);
+
+ INIT_WORK(&ar_sdio->wr_async_work, ath10k_sdio_write_async_work);
+ ar_sdio->workqueue = create_singlethread_workqueue("ath10k_sdio_wq");
+ if (!ar_sdio->workqueue)
+ goto err;
+
+ init_waitqueue_head(&ar_sdio->irq_wq);
+
+ for (i = 0; i < ATH10K_SDIO_BUS_REQUEST_MAX_NUM; i++)
+ ath10k_sdio_free_bus_req(ar_sdio, &ar_sdio->bus_req[i]);
+
+ ar->dev_id = id->device;
+ ar->id.vendor = id->vendor;
+ ar->id.device = id->device;
+
+ ath10k_sdio_set_mbox_info(ar_sdio);
+
+ ret = ath10k_sdio_config(ar_sdio);
+ if (ret) {
+ ath10k_err(ar, "Failed to config sdio: %d\n", ret);
+ goto err;
+ }
+
+ ret = ath10k_core_register(ar, 0/*chip_id is not applicaple for SDIO*/);
+ if (ret) {
+ ath10k_err(ar, "failed to register driver core: %d\n", ret);
+ goto err;
+ }
+
+ return ret;
+
+err:
+ kfree(ar_sdio->dma_buffer);
+err_core_destroy:
+ ath10k_core_destroy(ar);
+
+ return ret;
+}
+
+static void ath10k_sdio_remove(struct sdio_func *func)
+{
+ struct ath10k_sdio *ar_sdio;
+ struct ath10k *ar;
+
+ ar_sdio = sdio_get_drvdata(func);
+ ar = ar_sdio->ar;
+
+ ath10k_dbg(ar, ATH10K_DBG_BOOT,
+ "sdio removed func %d vendor 0x%x device 0x%x\n",
+ func->num, func->vendor, func->device);
+
+ (void)ath10k_sdio_hif_disable_intrs(ar_sdio);
+ cancel_work_sync(&ar_sdio->wr_async_work);
+ ath10k_core_unregister(ar);
+ ath10k_core_destroy(ar);
+
+ kfree(ar_sdio->dma_buffer);
+}
+
+static const struct sdio_device_id ath10k_sdio_devices[] = {
+ {SDIO_DEVICE(ATH10K_MANUFACTURER_CODE,
+ (ATH10K_MANUFACTURER_ID_AR6005_BASE | 0xA))},
+ {},
+};
+
+MODULE_DEVICE_TABLE(sdio, ath10k_sdio_devices);
+
+static struct sdio_driver ath10k_sdio_driver = {
+ .name = "ath10k_sdio",
+ .id_table = ath10k_sdio_devices,
+ .probe = ath10k_sdio_probe,
+ .remove = ath10k_sdio_remove,
+ .drv.pm = ATH10K_SDIO_PM_OPS,
+};
+
+static int __init ath10k_sdio_init(void)
+{
+ int ret;
+
+ ret = sdio_register_driver(&ath10k_sdio_driver);
+ if (ret)
+ pr_err("sdio driver registration failed: %d\n", ret);
+
+ return ret;
+}
+
+static void __exit ath10k_sdio_exit(void)
+{
+ sdio_unregister_driver(&ath10k_sdio_driver);
+}
+
+module_init(ath10k_sdio_init);
+module_exit(ath10k_sdio_exit);
+
+MODULE_AUTHOR("Qualcomm Atheros");
+MODULE_DESCRIPTION("Driver support for Qualcomm Atheros 802.11ac WLAN SDIO devices");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/ath/ath10k/sdio.h b/drivers/net/wireless/ath/ath10k/sdio.h
new file mode 100644
index 0000000..b5887c1
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/sdio.h
@@ -0,0 +1,276 @@
+/*
+ * Copyright (c) 2004-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
+ * Copyright (c) 2016 Kapsch Trafficcom AB
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _SDIO_H_
+#define _SDIO_H_
+
+#define ATH10K_HIF_MBOX_BLOCK_SIZE 256
+#define ATH10K_MANUFACTURER_ID_AR6005_BASE 0x500
+
+#define ATH10K_MANUFACTURER_ID_REV_MASK 0x00FF
+#define ATH10K_MANUFACTURER_CODE 0x271 /* Atheros */
+
+#define ATH10K_HIF_DMA_BUFFER_SIZE (32 * 1024)
+#define ATH10K_SDIO_MAX_BUFFER_SIZE 4096 /*Unsure of this constant*/
+
+/* Mailbox address in SDIO address space */
+#define ATH10K_HIF_MBOX_BASE_ADDR 0x1000
+#define ATH10K_HIF_MBOX_WIDTH 0x800
+
+#define ATH10K_HIF_MBOX_TOT_WIDTH \
+ (ATH10K_HIF_MBOX_NUM_MAX * ATH10K_HIF_MBOX_WIDTH)
+
+#define ATH10K_HIF_MBOX0_EXT_BASE_ADDR 0x5000
+#define ATH10K_HIF_MBOX0_EXT_WIDTH (36 * 1024)
+#define ATH10K_HIF_MBOX0_EXT_WIDTH_ROME_2_0 (56 * 1024)
+#define ATH10K_HIF_MBOX1_EXT_WIDTH (36 * 1024)
+#define ATH10K_HIF_MBOX_DUMMY_SPACE_SIZE (2 * 1024)
+
+#define ATH10K_HTC_MBOX_MAX_PAYLOAD_LENGTH \
+ (ATH10K_SDIO_MAX_BUFFER_SIZE - sizeof(struct ath10k_htc_hdr))
+
+#define ATH10K_HIF_MBOX_NUM_MAX 4
+#define ATH10K_SDIO_BUS_REQUEST_MAX_NUM 64
+
+#define ATH10K_SDIO_HIF_COMMUNICATION_TIMEOUT_HZ (100 * HZ)
+
+/* HTC runs over mailbox 0 */
+#define ATH10K_HTC_MAILBOX 0
+
+/* GMBOX addresses */
+#define ATH10K_HIF_GMBOX_BASE_ADDR 0x7000
+#define ATH10K_HIF_GMBOX_WIDTH 0x4000
+
+/* interrupt mode register */
+#define CCCR_SDIO_IRQ_MODE_REG 0xF0
+#define CCCR_SDIO_IRQ_MODE_REG_SDIO3 0x16
+
+#define CCCR_SDIO_DRIVER_STRENGTH_ENABLE_ADDR 0xF2
+
+#define CCCR_SDIO_DRIVER_STRENGTH_ENABLE_A 0x02
+#define CCCR_SDIO_DRIVER_STRENGTH_ENABLE_C 0x04
+#define CCCR_SDIO_DRIVER_STRENGTH_ENABLE_D 0x08
+
+#define CCCR_SDIO_ASYNC_INT_DELAY_ADDRESS 0xF0
+#define CCCR_SDIO_ASYNC_INT_DELAY_LSB 0x06
+#define CCCR_SDIO_ASYNC_INT_DELAY_MASK 0xC0
+
+/* mode to enable special 4-bit interrupt assertion without clock */
+#define SDIO_IRQ_MODE_ASYNC_4BIT_IRQ BIT(0)
+#define SDIO_IRQ_MODE_ASYNC_4BIT_IRQ_SDIO3 BIT(1)
+
+#define ATH10K_SDIO_TARGET_DEBUG_INTR_MASK 0x01
+
+/* The theoretical maximum number of RX messages that can be fetched
+ * from the mbox interrupt handler in one loop is derived in the following
+ * way:
+ *
+ * Let's assume that each packet in a bundle of the maximum bundle size
+ * (HTC_HOST_MAX_MSG_PER_BUNDLE) has the HTC header bundle count set
+ * to the maximum value (HTC_HOST_MAX_MSG_PER_BUNDLE).
+ *
+ * in this case the driver must allocate
+ * (HTC_HOST_MAX_MSG_PER_BUNDLE * HTC_HOST_MAX_MSG_PER_BUNDLE) skb's.
+ */
+#define ATH10K_SDIO_MAX_RX_MSGS \
+ (HTC_HOST_MAX_MSG_PER_BUNDLE * HTC_HOST_MAX_MSG_PER_BUNDLE)
+
+struct ath10k_sdio_bus_request {
+ struct list_head list;
+
+ /* sdio address */
+ u32 address;
+ u32 request;
+ size_t len;
+ struct sk_buff *skb;
+ enum ath10k_htc_ep_id eid;
+ int status;
+};
+
+struct ath10k_sdio_rx_data {
+ struct sk_buff *skb;
+ size_t alloc_len;
+ size_t act_len;
+ enum ath10k_htc_ep_id eid;
+ bool part_of_bundle;
+ bool last_in_bundle;
+ int status;
+};
+
+/* direction of transfer (read/write) */
+#define HIF_READ 0x00000001
+#define HIF_WRITE 0x00000002
+#define HIF_DIR_MASK (HIF_READ | HIF_WRITE)
+
+/* emode - This indicates the whether the command is to be executed in a
+ * blocking or non-blocking fashion (HIF_SYNCHRONOUS/
+ * HIF_ASYNCHRONOUS). The read/write data paths in HTC have been
+ * implemented using the asynchronous mode allowing the the bus
+ * driver to indicate the completion of operation through the
+ * registered callback routine. The requirement primarily comes
+ * from the contexts these operations get called from (a driver's
+ * transmit context or the ISR context in case of receive).
+ * Support for both of these modes is essential.
+ */
+#define HIF_SYNCHRONOUS 0x00000010
+#define HIF_ASYNCHRONOUS 0x00000020
+#define HIF_EMODE_MASK (HIF_SYNCHRONOUS | HIF_ASYNCHRONOUS)
+
+/* dmode - An interface may support different kinds of commands based on
+ * the tradeoff between the amount of data it can carry and the
+ * setup time. Byte and Block modes are supported (HIF_BYTE_BASIS/
+ * HIF_BLOCK_BASIS). In case of latter, the data is rounded off
+ * to the nearest block size by padding. The size of the block is
+ * configurable at compile time using the HIF_BLOCK_SIZE and is
+ * negotiated with the target during initialization after the
+ * ATH10K interrupts are enabled.
+ */
+#define HIF_BYTE_BASIS 0x00000040
+#define HIF_BLOCK_BASIS 0x00000080
+#define HIF_DMODE_MASK (HIF_BYTE_BASIS | HIF_BLOCK_BASIS)
+
+/* amode - This indicates if the address has to be incremented on ATH10K
+ * after every read/write operation (HIF?FIXED_ADDRESS/
+ * HIF_INCREMENTAL_ADDRESS).
+ */
+#define HIF_FIXED_ADDRESS 0x00000100
+#define HIF_INCREMENTAL_ADDRESS 0x00000200
+#define HIF_AMODE_MASK (HIF_FIXED_ADDRESS | HIF_INCREMENTAL_ADDRESS)
+
+#define HIF_WR_ASYNC_BYTE_INC \
+ (HIF_WRITE | HIF_ASYNCHRONOUS | \
+ HIF_BYTE_BASIS | HIF_INCREMENTAL_ADDRESS)
+
+#define HIF_WR_ASYNC_BLOCK_INC \
+ (HIF_WRITE | HIF_ASYNCHRONOUS | \
+ HIF_BLOCK_BASIS | HIF_INCREMENTAL_ADDRESS)
+
+#define HIF_WR_SYNC_BYTE_FIX \
+ (HIF_WRITE | HIF_SYNCHRONOUS | \
+ HIF_BYTE_BASIS | HIF_FIXED_ADDRESS)
+
+#define HIF_WR_SYNC_BYTE_INC \
+ (HIF_WRITE | HIF_SYNCHRONOUS | \
+ HIF_BYTE_BASIS | HIF_INCREMENTAL_ADDRESS)
+
+#define HIF_WR_SYNC_BLOCK_INC \
+ (HIF_WRITE | HIF_SYNCHRONOUS | \
+ HIF_BLOCK_BASIS | HIF_INCREMENTAL_ADDRESS)
+
+#define HIF_RD_SYNC_BYTE_INC \
+ (HIF_READ | HIF_SYNCHRONOUS | \
+ HIF_BYTE_BASIS | HIF_INCREMENTAL_ADDRESS)
+
+#define HIF_RD_SYNC_BYTE_FIX \
+ (HIF_READ | HIF_SYNCHRONOUS | \
+ HIF_BYTE_BASIS | HIF_FIXED_ADDRESS)
+
+#define HIF_RD_ASYNC_BLOCK_FIX \
+ (HIF_READ | HIF_ASYNCHRONOUS | \
+ HIF_BLOCK_BASIS | HIF_FIXED_ADDRESS)
+
+#define HIF_RD_SYNC_BLOCK_FIX \
+ (HIF_READ | HIF_SYNCHRONOUS | \
+ HIF_BLOCK_BASIS | HIF_FIXED_ADDRESS)
+
+struct ath10k_sdio_irq_proc_registers {
+ u8 host_int_status;
+ u8 cpu_int_status;
+ u8 error_int_status;
+ u8 counter_int_status;
+ u8 mbox_frame;
+ u8 rx_lookahead_valid;
+ u8 host_int_status2;
+ u8 gmbox_rx_avail;
+ __le32 rx_lookahead[2];
+ __le32 rx_gmbox_lookahead_alias[2];
+};
+
+struct ath10k_sdio_irq_enable_reg {
+ u8 int_status_en;
+ u8 cpu_int_status_en;
+ u8 err_int_status_en;
+ u8 cntr_int_status_en;
+};
+
+struct ath10k_sdio_irq_data {
+ /* protects irq_proc_reg and irq_en_reg below */
+ spinlock_t lock;
+ struct ath10k_sdio_irq_proc_registers irq_proc_reg;
+ struct ath10k_sdio_irq_enable_reg irq_en_reg;
+};
+
+struct ath10k_mbox_ext_info {
+ u32 htc_ext_addr;
+ u32 htc_ext_sz;
+};
+
+struct ath10k_mbox_info {
+ u32 htc_addr;
+ struct ath10k_mbox_ext_info ext_info[2];
+ u32 block_size;
+ u32 block_mask;
+ u32 gmbox_addr;
+ u32 gmbox_sz;
+};
+
+struct ath10k_sdio {
+ struct sdio_func *func;
+
+ struct ath10k_mbox_info mbox_info;
+ bool swap_mbox;
+ u32 mbox_addr[ATH10K_HTC_EP_COUNT];
+ u32 mbox_size[ATH10K_HTC_EP_COUNT];
+
+ /* available bus requests */
+ struct ath10k_sdio_bus_request bus_req[ATH10K_SDIO_BUS_REQUEST_MAX_NUM];
+ /* free list of bus requests */
+ struct list_head bus_req_freeq;
+ /* protects access to bus_req_freeq */
+ spinlock_t lock;
+
+ struct ath10k_sdio_rx_data rx_pkts[ATH10K_SDIO_MAX_RX_MSGS];
+ size_t n_rx_pkts;
+
+ struct ath10k *ar;
+ struct ath10k_sdio_irq_data irq_data;
+
+ u8 *dma_buffer;
+
+ /* protects access to dma_buffer */
+ struct mutex dma_buffer_mutex;
+
+ atomic_t irq_handling;
+ atomic_t stopping;
+ wait_queue_head_t irq_wq;
+
+ bool is_disabled;
+
+ struct workqueue_struct *workqueue;
+ struct work_struct wr_async_work;
+ struct list_head wr_asyncq;
+ /* protects access to wr_asyncq */
+ spinlock_t wr_async_lock;
+};
+
+static inline struct ath10k_sdio *ath10k_sdio_priv(struct ath10k *ar)
+{
+ return (struct ath10k_sdio *)ar->drv_priv;
+}
+
+#endif
--
1.7.9.5
^ permalink raw reply related
* Re: [PATCH] mmc: pwrseq: add support for Marvell SD8787 chip
From: Tony Lindgren @ 2016-11-18 20:19 UTC (permalink / raw)
To: Matt Ranostay
Cc: linux-wireless, linux-kernel, devicetree, linux-mmc, Ulf Hansson,
Mark Rutland, Srinivas Kandagatla
In-Reply-To: <1479434109-8745-1-git-send-email-matt@ranostay.consulting>
* Matt Ranostay <matt@ranostay.consulting> [161117 17:55]:
> Allow power sequencing for the Marvell SD8787 Wifi/BT chip.
> This can be abstracted to other chipsets if needed in the future.
Good to see this happening:
Acked-by: Tony Lindgren <tony@atomide.com>
^ permalink raw reply
* ath10k stuck in mesh mode
From: Matteo Grandi @ 2016-11-18 21:43 UTC (permalink / raw)
To: LinuxWireless Mailing List, me
Hello,
I experimented a strange behavior during some data rate tests between
two wireless interfaces in mesh mode. The data rate stuck on 120Mbps
(iperf UDP test) and MCS7 that is the higher MCS of 80211n without
using MIMO, even when the channel is completely free.
My configuration:
two boards Gateworks Ventana 5410 running Ubuntu 14.04 kernel 3.14,
each board has two miniPCIe WiFi adapter Compex WLE600V5-27, 2
antennas each, ath10k drivers 802.11ac capabilities.
Backports-4.4.2
Firmware firmware-5.bin_10.2.4.70.58
I set up a mesh configuring, on each board, one interface as mesh
point, and I run an iperf UDP test, but even using channels free of
any other communication (48 and 149 in particular) the max data rate
achieved was 120Mbps on MCS7, and it stucked on that value and MIMO
was not in use despite the two antennas.
By sniffing the packets I discovered that only 802.11n was in use, and
I didn't notice nothing clearly strange in the syslog.
I tried on other channels but no MIMO nor 80MHz bandwidth (as expected
from the capabilities) was never used (at most 40MHz with SGI). I
tried to have line of sight and play with the antennas position and
distance, but the data rate stuck on that value.
Note: I didn't the "@80" in any regulatoru domain by iw reg get (but
only @40), as it's using the reg. domain from ath9k (used on a
previous 802.11n miniPCIe adapter).
Note: I also tried to set up an AP and STA, and in that case MIMO was used.
So the question is: who decide if use MIMO and higher MCS hopefully on
80MHz channel or not? Is the firmware? Is there a way to force the
interface to use 80MHz and/or MIMO? (iw provide the possibility to
choose between [HT20/HT40-/HT40+]).
Any advice is the welcome, this project is part of my master thesis
and are weeks that I'm struggling with this problem.
Thank you
Matteo
^ permalink raw reply
* [PATCH 1/7] rtl8xxxu: Fix memory leak in handling rxdesc16 packets
From: Jes.Sorensen @ 2016-11-18 21:44 UTC (permalink / raw)
To: linux-wireless; +Cc: kvalo, Larry.Finger, Jes Sorensen
In-Reply-To: <1479505468-29383-1-git-send-email-Jes.Sorensen@redhat.com>
From: Jes Sorensen <Jes.Sorensen@redhat.com>
A device running without RX package aggregation could return more data
in the USB packet than the actual network packet. In this case the
could would clone the skb but then determine that that there was no
packet to handle and exit without freeing the cloned skb first.
This has so far only been observed with 8188eu devices, but could
affect others.
Signed-off-by: Jes Sorensen <Jes.Sorensen@redhat.com>
---
drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
index b2d7f6e..a96ff17 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
@@ -5197,7 +5197,12 @@ int rtl8xxxu_parse_rxdesc16(struct rtl8xxxu_priv *priv, struct sk_buff *skb)
pkt_offset = roundup(pkt_len + drvinfo_sz + desc_shift +
sizeof(struct rtl8xxxu_rxdesc16), 128);
- if (pkt_cnt > 1)
+ /*
+ * Only clone the skb if there's enough data at the end to
+ * at least cover the rx descriptor
+ */
+ if (pkt_cnt > 1 &&
+ urb_len > (pkt_offset + sizeof(struct rtl8xxxu_rxdesc16)))
next_skb = skb_clone(skb, GFP_ATOMIC);
rx_status = IEEE80211_SKB_RXCB(skb);
--
2.7.4
^ permalink raw reply related
* [PATCH 4/7] rtl8xxxu: Fix rtl8192eu driver reload issue
From: Jes.Sorensen @ 2016-11-18 21:44 UTC (permalink / raw)
To: linux-wireless; +Cc: kvalo, Larry.Finger, Jes Sorensen
In-Reply-To: <1479505468-29383-1-git-send-email-Jes.Sorensen@redhat.com>
From: Jes Sorensen <Jes.Sorensen@redhat.com>
The 8192eu suffered from two issues when reloading the driver.
The same problems as with the 8723bu where REG_RX_WAIT_CCA bits 22 and
23 didn't get set in rtl8192e_enable_rf().
In addition it also seems prone to issues when setting REG_RF_CTRL to
0 intead of just disabling the RF_ENABLE bit. Similar to what was
causing issues with the 8188eu.
With this patch I can successfully reload the driver and reassociate
to an APi with an 8192eu dongle.
Signed-off-by: Jes Sorensen <Jes.Sorensen@redhat.com>
---
drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c
index df54d27..a793fed 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c
@@ -1461,7 +1461,9 @@ static int rtl8192eu_active_to_emu(struct rtl8xxxu_priv *priv)
int count, ret = 0;
/* Turn off RF */
- rtl8xxxu_write8(priv, REG_RF_CTRL, 0);
+ val8 = rtl8xxxu_read8(priv, REG_RF_CTRL);
+ val8 &= ~RF_ENABLE;
+ rtl8xxxu_write8(priv, REG_RF_CTRL, val8);
/* Switch DPDT_SEL_P output from register 0x65[2] */
val8 = rtl8xxxu_read8(priv, REG_LEDCFG2);
@@ -1593,6 +1595,10 @@ static void rtl8192e_enable_rf(struct rtl8xxxu_priv *priv)
u32 val32;
u8 val8;
+ val32 = rtl8xxxu_read32(priv, REG_RX_WAIT_CCA);
+ val32 |= (BIT(22) | BIT(23));
+ rtl8xxxu_write32(priv, REG_RX_WAIT_CCA, val32);
+
val8 = rtl8xxxu_read8(priv, REG_GPIO_MUXCFG);
val8 |= BIT(5);
rtl8xxxu_write8(priv, REG_GPIO_MUXCFG, val8);
--
2.7.4
^ permalink raw reply related
* [PATCH 7/7] rtl8xxxu: Fix non static symbol warning
From: Jes.Sorensen @ 2016-11-18 21:44 UTC (permalink / raw)
To: linux-wireless; +Cc: kvalo, Larry.Finger, Wei Yongjun, Jes Sorensen
In-Reply-To: <1479505468-29383-1-git-send-email-Jes.Sorensen@redhat.com>
From: Wei Yongjun <weiyongjun1@huawei.com>
Fixes the following sparse warning:
drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c:1559:6: warning:
symbol 'rtl8192eu_power_off' was not declared. Should it be static?
Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
Signed-off-by: Jes Sorensen <Jes.Sorensen@redhat.com>
---
drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c
index a793fed..a1178c5 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c
@@ -1556,7 +1556,7 @@ static int rtl8192eu_power_on(struct rtl8xxxu_priv *priv)
return ret;
}
-void rtl8192eu_power_off(struct rtl8xxxu_priv *priv)
+static void rtl8192eu_power_off(struct rtl8xxxu_priv *priv)
{
u8 val8;
u16 val16;
--
2.7.4
^ permalink raw reply related
* [PATCH 2/7] rtl8xxxu: Fix big-endian problem reporting mactime
From: Jes.Sorensen @ 2016-11-18 21:44 UTC (permalink / raw)
To: linux-wireless; +Cc: kvalo, Larry.Finger, Jes Sorensen
In-Reply-To: <1479505468-29383-1-git-send-email-Jes.Sorensen@redhat.com>
From: Jes Sorensen <Jes.Sorensen@redhat.com>
The full RX descriptor is converted so converting tsfl again would
return it to it's original endian value.
Signed-off-by: Jes Sorensen <Jes.Sorensen@redhat.com>
---
drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h | 4 ++--
drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
index 10166289..08d587a 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
@@ -238,7 +238,7 @@ struct rtl8xxxu_rxdesc16 {
u32 pattern1match:1;
u32 pattern0match:1;
#endif
- __le32 tsfl;
+ u32 tsfl;
#if 0
u32 bassn:12;
u32 bavld:1;
@@ -368,7 +368,7 @@ struct rtl8xxxu_rxdesc24 {
u32 ldcp:1;
u32 splcp:1;
#endif
- __le32 tsfl;
+ u32 tsfl;
};
struct rtl8xxxu_txdesc32 {
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
index a96ff17..a5e6ec2 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
@@ -5220,7 +5220,7 @@ int rtl8xxxu_parse_rxdesc16(struct rtl8xxxu_priv *priv, struct sk_buff *skb)
rtl8xxxu_rx_parse_phystats(priv, rx_status, phy_stats,
rx_desc->rxmcs);
- rx_status->mactime = le32_to_cpu(rx_desc->tsfl);
+ rx_status->mactime = rx_desc->tsfl;
rx_status->flag |= RX_FLAG_MACTIME_START;
if (!rx_desc->swdec)
@@ -5290,7 +5290,7 @@ int rtl8xxxu_parse_rxdesc24(struct rtl8xxxu_priv *priv, struct sk_buff *skb)
rtl8xxxu_rx_parse_phystats(priv, rx_status, phy_stats,
rx_desc->rxmcs);
- rx_status->mactime = le32_to_cpu(rx_desc->tsfl);
+ rx_status->mactime = rx_desc->tsfl;
rx_status->flag |= RX_FLAG_MACTIME_START;
if (!rx_desc->swdec)
--
2.7.4
^ permalink raw reply related
* [PATCH 3/7] rtl8xxxu: Fix rtl8723bu driver reload issue
From: Jes.Sorensen @ 2016-11-18 21:44 UTC (permalink / raw)
To: linux-wireless; +Cc: kvalo, Larry.Finger, Jes Sorensen
In-Reply-To: <1479505468-29383-1-git-send-email-Jes.Sorensen@redhat.com>
From: Jes Sorensen <Jes.Sorensen@redhat.com>
The generic disable_rf() function clears bits 22 and 23 in
REG_RX_WAIT_CCA, however we did not re-enable them again in
rtl8723b_enable_rf()
This resolves the problem for me with 8723bu devices not working again
after reloading the driver.
Signed-off-by: Jes Sorensen <Jes.Sorensen@redhat.com>
---
drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c
index 6c086b5..02b8ddd 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c
@@ -1498,6 +1498,10 @@ static void rtl8723b_enable_rf(struct rtl8xxxu_priv *priv)
u32 val32;
u8 val8;
+ val32 = rtl8xxxu_read32(priv, REG_RX_WAIT_CCA);
+ val32 |= (BIT(22) | BIT(23));
+ rtl8xxxu_write32(priv, REG_RX_WAIT_CCA, val32);
+
/*
* No indication anywhere as to what 0x0790 does. The 2 antenna
* vendor code preserves bits 6-7 here.
--
2.7.4
^ permalink raw reply related
* [PATCH 0/7] rtl8xxxu: Pending patches
From: Jes.Sorensen @ 2016-11-18 21:44 UTC (permalink / raw)
To: linux-wireless; +Cc: kvalo, Larry.Finger, Jes Sorensen
From: Jes Sorensen <Jes.Sorensen@redhat.com>
Kalle,
Please find attached a number of patches for the rtl8xxxu
driver.
The issues reported with wpa_supplicant on 8723bu still needs further
investigation.
Note the memory leak issue has only been seen with 8188eu devices so
far, but it's serious and needs to be plugged.
These are what I currently have in my pending queue. Apologies if I
already posted some of these, trying to get back in sync after
Plumbers.
Cheers,
Jes
Jes Sorensen (6):
rtl8xxxu: Fix memory leak in handling rxdesc16 packets
rtl8xxxu: Fix big-endian problem reporting mactime
rtl8xxxu: Fix rtl8723bu driver reload issue
rtl8xxxu: Fix rtl8192eu driver reload issue
rtl8xxxu: Obtain RTS rates from mac80211
rtl8xxxu: Pass tx_info to fill_txdesc in order to have access to retry
count
Wei Yongjun (1):
rtl8xxxu: Fix non static symbol warning
drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h | 31 +++---
.../net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c | 10 +-
.../net/wireless/realtek/rtl8xxxu/rtl8xxxu_8723b.c | 4 +
.../net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 114 ++++++++++++++-------
4 files changed, 104 insertions(+), 55 deletions(-)
--
2.7.4
^ permalink raw reply
* [PATCH 6/7] rtl8xxxu: Pass tx_info to fill_txdesc in order to have access to retry count
From: Jes.Sorensen @ 2016-11-18 21:44 UTC (permalink / raw)
To: linux-wireless; +Cc: kvalo, Larry.Finger, Jes Sorensen
In-Reply-To: <1479505468-29383-1-git-send-email-Jes.Sorensen@redhat.com>
From: Jes Sorensen <Jes.Sorensen@redhat.com>
In order to obtain retry count for a given rate we need to pass the
full struct ieee80211_tx_info to the function setting the rate in he
TX descriptor.
This uncovered a huge bug where the old code would use struct
ieee80211_rate.flags to test for rate parameters, which is always
zero, instead of the flags value from struct ieee80211_tx_rate.
Time to find a brown paper bag :(
Signed-off-by: Jes Sorensen <Jes.Sorensen@redhat.com>
---
drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h | 27 ++++----
.../net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 71 ++++++++++++++--------
2 files changed, 60 insertions(+), 38 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
index bc3c990..df551b2 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
@@ -1337,10 +1337,11 @@ struct rtl8xxxu_fileops {
u32 ramask, int sgi);
void (*report_connect) (struct rtl8xxxu_priv *priv,
u8 macid, bool connect);
- void (*fill_txdesc) (struct ieee80211_hdr *hdr,
- struct rtl8xxxu_txdesc32 *tx_desc, u32 rate,
- u16 rate_flag, bool sgi, bool short_preamble,
- bool ampdu_enable, u32 rts_rate);
+ void (*fill_txdesc) (struct ieee80211_hw *hw, struct ieee80211_hdr *hdr,
+ struct ieee80211_tx_info *tx_info,
+ struct rtl8xxxu_txdesc32 *tx_desc, bool sgi,
+ bool short_preamble, bool ampdu_enable,
+ u32 rts_rate);
int writeN_block_size;
int rx_agg_buf_size;
char tx_desc_size;
@@ -1434,14 +1435,16 @@ int rtl8xxxu_parse_rxdesc24(struct rtl8xxxu_priv *priv, struct sk_buff *skb);
int rtl8xxxu_gen2_channel_to_group(int channel);
bool rtl8xxxu_gen2_simularity_compare(struct rtl8xxxu_priv *priv,
int result[][8], int c1, int c2);
-void rtl8xxxu_fill_txdesc_v1(struct ieee80211_hdr *hdr,
- struct rtl8xxxu_txdesc32 *tx_desc, u32 rate,
- u16 rate_flag, bool sgi, bool short_preamble,
- bool ampdu_enable, u32 rts_rate);
-void rtl8xxxu_fill_txdesc_v2(struct ieee80211_hdr *hdr,
- struct rtl8xxxu_txdesc32 *tx_desc32, u32 rate,
- u16 rate_flag, bool sgi, bool short_preamble,
- bool ampdu_enable, u32 rts_rate);
+void rtl8xxxu_fill_txdesc_v1(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr,
+ struct ieee80211_tx_info *tx_info,
+ struct rtl8xxxu_txdesc32 *tx_desc, bool sgi,
+ bool short_preamble, bool ampdu_enable,
+ u32 rts_rate);
+void rtl8xxxu_fill_txdesc_v2(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr,
+ struct ieee80211_tx_info *tx_info,
+ struct rtl8xxxu_txdesc32 *tx_desc32, bool sgi,
+ bool short_preamble, bool ampdu_enable,
+ u32 rts_rate);
extern struct rtl8xxxu_fileops rtl8192cu_fops;
extern struct rtl8xxxu_fileops rtl8192eu_fops;
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
index a4ac557..04141e5 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
@@ -4759,13 +4759,28 @@ static void rtl8xxxu_dump_action(struct device *dev,
* This format is used on 8188cu/8192cu/8723au
*/
void
-rtl8xxxu_fill_txdesc_v1(struct ieee80211_hdr *hdr,
- struct rtl8xxxu_txdesc32 *tx_desc, u32 rate,
- u16 rate_flag, bool sgi, bool short_preamble,
- bool ampdu_enable, u32 rts_rate)
+rtl8xxxu_fill_txdesc_v1(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr,
+ struct ieee80211_tx_info *tx_info,
+ struct rtl8xxxu_txdesc32 *tx_desc, bool sgi,
+ bool short_preamble, bool ampdu_enable, u32 rts_rate)
{
+ struct ieee80211_rate *tx_rate = ieee80211_get_tx_rate(hw, tx_info);
+ struct rtl8xxxu_priv *priv = hw->priv;
+ struct device *dev = &priv->udev->dev;
+ u32 rate;
+ u16 rate_flags = tx_info->control.rates[0].flags;
u16 seq_number;
+ if (rate_flags & IEEE80211_TX_RC_MCS &&
+ !ieee80211_is_mgmt(hdr->frame_control))
+ rate = tx_info->control.rates[0].idx + DESC_RATE_MCS0;
+ else
+ rate = tx_rate->hw_value;
+
+ if (rtl8xxxu_debug & RTL8XXXU_DEBUG_TX)
+ dev_info(dev, "%s: TX rate: %d, pkt size %d\n",
+ __func__, rate, cpu_to_le16(tx_desc->pkt_size));
+
seq_number = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl));
tx_desc->txdw5 = cpu_to_le32(rate);
@@ -4800,10 +4815,10 @@ rtl8xxxu_fill_txdesc_v1(struct ieee80211_hdr *hdr,
* rts_rate is zero if RTS/CTS or CTS to SELF are not enabled
*/
tx_desc->txdw4 |= cpu_to_le32(rts_rate << TXDESC32_RTS_RATE_SHIFT);
- if (rate_flag & IEEE80211_TX_RC_USE_RTS_CTS) {
+ if (rate_flags & IEEE80211_TX_RC_USE_RTS_CTS) {
tx_desc->txdw4 |= cpu_to_le32(TXDESC32_RTS_CTS_ENABLE);
tx_desc->txdw4 |= cpu_to_le32(TXDESC32_HW_RTS_ENABLE);
- } else if (rate_flag & IEEE80211_TX_RC_USE_CTS_PROTECT) {
+ } else if (rate_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
tx_desc->txdw4 |= cpu_to_le32(TXDESC32_CTS_SELF_ENABLE);
tx_desc->txdw4 |= cpu_to_le32(TXDESC32_HW_RTS_ENABLE);
}
@@ -4814,16 +4829,31 @@ rtl8xxxu_fill_txdesc_v1(struct ieee80211_hdr *hdr,
* This format is used on 8192eu/8723bu
*/
void
-rtl8xxxu_fill_txdesc_v2(struct ieee80211_hdr *hdr,
- struct rtl8xxxu_txdesc32 *tx_desc32, u32 rate,
- u16 rate_flag, bool sgi, bool short_preamble,
- bool ampdu_enable, u32 rts_rate)
+rtl8xxxu_fill_txdesc_v2(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr,
+ struct ieee80211_tx_info *tx_info,
+ struct rtl8xxxu_txdesc32 *tx_desc32, bool sgi,
+ bool short_preamble, bool ampdu_enable, u32 rts_rate)
{
+ struct ieee80211_rate *tx_rate = ieee80211_get_tx_rate(hw, tx_info);
+ struct rtl8xxxu_priv *priv = hw->priv;
+ struct device *dev = &priv->udev->dev;
struct rtl8xxxu_txdesc40 *tx_desc40;
+ u32 rate;
+ u16 rate_flags = tx_info->control.rates[0].flags;
u16 seq_number;
tx_desc40 = (struct rtl8xxxu_txdesc40 *)tx_desc32;
+ if (rtl8xxxu_debug & RTL8XXXU_DEBUG_TX)
+ dev_info(dev, "%s: TX rate: %d, pkt size %d\n",
+ __func__, rate, cpu_to_le16(tx_desc40->pkt_size));
+
+ if (rate_flags & IEEE80211_TX_RC_MCS &&
+ !ieee80211_is_mgmt(hdr->frame_control))
+ rate = tx_info->control.rates[0].idx + DESC_RATE_MCS0;
+ else
+ rate = tx_rate->hw_value;
+
seq_number = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl));
tx_desc40->txdw4 = cpu_to_le32(rate);
@@ -4854,10 +4884,10 @@ rtl8xxxu_fill_txdesc_v2(struct ieee80211_hdr *hdr,
/*
* rts_rate is zero if RTS/CTS or CTS to SELF are not enabled
*/
- if (rate_flag & IEEE80211_TX_RC_USE_RTS_CTS) {
+ if (rate_flags & IEEE80211_TX_RC_USE_RTS_CTS) {
tx_desc40->txdw3 |= cpu_to_le32(TXDESC40_RTS_CTS_ENABLE);
tx_desc40->txdw3 |= cpu_to_le32(TXDESC40_HW_RTS_ENABLE);
- } else if (rate_flag & IEEE80211_TX_RC_USE_CTS_PROTECT) {
+ } else if (rate_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
/*
* For some reason the vendor driver doesn't set
* TXDESC40_HW_RTS_ENABLE for CTS to SELF
@@ -4872,14 +4902,13 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw,
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
- struct ieee80211_rate *tx_rate = ieee80211_get_tx_rate(hw, tx_info);
struct rtl8xxxu_priv *priv = hw->priv;
struct rtl8xxxu_txdesc32 *tx_desc;
struct rtl8xxxu_tx_urb *tx_urb;
struct ieee80211_sta *sta = NULL;
struct ieee80211_vif *vif = tx_info->control.vif;
struct device *dev = &priv->udev->dev;
- u32 queue, rate, rts_rate;
+ u32 queue, rts_rate;
u16 pktlen = skb->len;
u16 seq_number;
u16 rate_flag = tx_info->control.rates[0].flags;
@@ -4906,10 +4935,6 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw,
goto error;
}
- if (rtl8xxxu_debug & RTL8XXXU_DEBUG_TX)
- dev_info(dev, "%s: TX rate: %d (%d), pkt size %d\n",
- __func__, tx_rate->bitrate, tx_rate->hw_value, pktlen);
-
if (ieee80211_is_action(hdr->frame_control))
rtl8xxxu_dump_action(dev, hdr);
@@ -4963,12 +4988,6 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw,
}
}
- if (rate_flag & IEEE80211_TX_RC_MCS &&
- !ieee80211_is_mgmt(hdr->frame_control))
- rate = tx_info->control.rates[0].idx + DESC_RATE_MCS0;
- else
- rate = tx_rate->hw_value;
-
if (rate_flag & IEEE80211_TX_RC_SHORT_GI ||
(ieee80211_is_data_qos(hdr->frame_control) &&
sta && sta->ht_cap.cap &
@@ -4988,8 +5007,8 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw,
seq_number = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl));
- priv->fops->fill_txdesc(hdr, tx_desc, rate, rate_flag, sgi,
- short_preamble, ampdu_enable, rts_rate);
+ priv->fops->fill_txdesc(hw, hdr, tx_info, tx_desc, sgi, short_preamble,
+ ampdu_enable, rts_rate);
rtl8xxxu_calc_tx_desc_csum(tx_desc);
--
2.7.4
^ permalink raw reply related
* [PATCH 5/7] rtl8xxxu: Obtain RTS rates from mac80211
From: Jes.Sorensen @ 2016-11-18 21:44 UTC (permalink / raw)
To: linux-wireless; +Cc: kvalo, Larry.Finger, Jes Sorensen
In-Reply-To: <1479505468-29383-1-git-send-email-Jes.Sorensen@redhat.com>
From: Jes Sorensen <Jes.Sorensen@redhat.com>
Use the mac80211 provided rate for RTS rather than the hard coded
24Mbps as suggested by the vendor drivers.
Reported-by: Andrea Merello <andrea.merello@gmail.com>
Signed-off-by: Jes Sorensen <Jes.Sorensen@redhat.com>
---
drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h | 6 +--
.../net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 46 ++++++++++++++--------
2 files changed, 32 insertions(+), 20 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
index 08d587a..bc3c990 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
@@ -1340,7 +1340,7 @@ struct rtl8xxxu_fileops {
void (*fill_txdesc) (struct ieee80211_hdr *hdr,
struct rtl8xxxu_txdesc32 *tx_desc, u32 rate,
u16 rate_flag, bool sgi, bool short_preamble,
- bool ampdu_enable);
+ bool ampdu_enable, u32 rts_rate);
int writeN_block_size;
int rx_agg_buf_size;
char tx_desc_size;
@@ -1437,11 +1437,11 @@ bool rtl8xxxu_gen2_simularity_compare(struct rtl8xxxu_priv *priv,
void rtl8xxxu_fill_txdesc_v1(struct ieee80211_hdr *hdr,
struct rtl8xxxu_txdesc32 *tx_desc, u32 rate,
u16 rate_flag, bool sgi, bool short_preamble,
- bool ampdu_enable);
+ bool ampdu_enable, u32 rts_rate);
void rtl8xxxu_fill_txdesc_v2(struct ieee80211_hdr *hdr,
struct rtl8xxxu_txdesc32 *tx_desc32, u32 rate,
u16 rate_flag, bool sgi, bool short_preamble,
- bool ampdu_enable);
+ bool ampdu_enable, u32 rts_rate);
extern struct rtl8xxxu_fileops rtl8192cu_fops;
extern struct rtl8xxxu_fileops rtl8192eu_fops;
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
index a5e6ec2..a4ac557 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
@@ -4762,7 +4762,7 @@ void
rtl8xxxu_fill_txdesc_v1(struct ieee80211_hdr *hdr,
struct rtl8xxxu_txdesc32 *tx_desc, u32 rate,
u16 rate_flag, bool sgi, bool short_preamble,
- bool ampdu_enable)
+ bool ampdu_enable, u32 rts_rate)
{
u16 seq_number;
@@ -4796,15 +4796,16 @@ rtl8xxxu_fill_txdesc_v1(struct ieee80211_hdr *hdr,
if (sgi)
tx_desc->txdw5 |= cpu_to_le32(TXDESC32_SHORT_GI);
+ /*
+ * rts_rate is zero if RTS/CTS or CTS to SELF are not enabled
+ */
+ tx_desc->txdw4 |= cpu_to_le32(rts_rate << TXDESC32_RTS_RATE_SHIFT);
if (rate_flag & IEEE80211_TX_RC_USE_RTS_CTS) {
- /*
- * Use RTS rate 24M - does the mac80211 tell
- * us which to use?
- */
- tx_desc->txdw4 |= cpu_to_le32(DESC_RATE_24M <<
- TXDESC32_RTS_RATE_SHIFT);
tx_desc->txdw4 |= cpu_to_le32(TXDESC32_RTS_CTS_ENABLE);
tx_desc->txdw4 |= cpu_to_le32(TXDESC32_HW_RTS_ENABLE);
+ } else if (rate_flag & IEEE80211_TX_RC_USE_CTS_PROTECT) {
+ tx_desc->txdw4 |= cpu_to_le32(TXDESC32_CTS_SELF_ENABLE);
+ tx_desc->txdw4 |= cpu_to_le32(TXDESC32_HW_RTS_ENABLE);
}
}
@@ -4816,7 +4817,7 @@ void
rtl8xxxu_fill_txdesc_v2(struct ieee80211_hdr *hdr,
struct rtl8xxxu_txdesc32 *tx_desc32, u32 rate,
u16 rate_flag, bool sgi, bool short_preamble,
- bool ampdu_enable)
+ bool ampdu_enable, u32 rts_rate)
{
struct rtl8xxxu_txdesc40 *tx_desc40;
u16 seq_number;
@@ -4849,15 +4850,19 @@ rtl8xxxu_fill_txdesc_v2(struct ieee80211_hdr *hdr,
if (short_preamble)
tx_desc40->txdw5 |= cpu_to_le32(TXDESC40_SHORT_PREAMBLE);
+ tx_desc40->txdw4 |= cpu_to_le32(rts_rate << TXDESC40_RTS_RATE_SHIFT);
+ /*
+ * rts_rate is zero if RTS/CTS or CTS to SELF are not enabled
+ */
if (rate_flag & IEEE80211_TX_RC_USE_RTS_CTS) {
- /*
- * Use RTS rate 24M - does the mac80211 tell
- * us which to use?
- */
- tx_desc40->txdw4 |= cpu_to_le32(DESC_RATE_24M <<
- TXDESC40_RTS_RATE_SHIFT);
tx_desc40->txdw3 |= cpu_to_le32(TXDESC40_RTS_CTS_ENABLE);
tx_desc40->txdw3 |= cpu_to_le32(TXDESC40_HW_RTS_ENABLE);
+ } else if (rate_flag & IEEE80211_TX_RC_USE_CTS_PROTECT) {
+ /*
+ * For some reason the vendor driver doesn't set
+ * TXDESC40_HW_RTS_ENABLE for CTS to SELF
+ */
+ tx_desc40->txdw3 |= cpu_to_le32(TXDESC40_CTS_SELF_ENABLE);
}
}
@@ -4874,7 +4879,7 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw,
struct ieee80211_sta *sta = NULL;
struct ieee80211_vif *vif = tx_info->control.vif;
struct device *dev = &priv->udev->dev;
- u32 queue, rate;
+ u32 queue, rate, rts_rate;
u16 pktlen = skb->len;
u16 seq_number;
u16 rate_flag = tx_info->control.rates[0].flags;
@@ -4974,10 +4979,17 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw,
(sta && vif && vif->bss_conf.use_short_preamble))
short_preamble = true;
+ if (rate_flag & IEEE80211_TX_RC_USE_RTS_CTS)
+ rts_rate = ieee80211_get_rts_cts_rate(hw, tx_info)->hw_value;
+ else if (rate_flag & IEEE80211_TX_RC_USE_CTS_PROTECT)
+ rts_rate = ieee80211_get_rts_cts_rate(hw, tx_info)->hw_value;
+ else
+ rts_rate = 0;
+
seq_number = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl));
- priv->fops->fill_txdesc(hdr, tx_desc, rate, rate_flag,
- sgi, short_preamble, ampdu_enable);
+ priv->fops->fill_txdesc(hdr, tx_desc, rate, rate_flag, sgi,
+ short_preamble, ampdu_enable, rts_rate);
rtl8xxxu_calc_tx_desc_csum(tx_desc);
--
2.7.4
^ permalink raw reply related
* Potential deadlock BUG in drivers/net/wireless/st/cw1200/sta.c (Linux 4.9)
From: Iago Abal @ 2016-11-18 21:58 UTC (permalink / raw)
To: Kalle Valo; +Cc: Solomon Peachy, linux-wireless, netdev
Hi,
With the help of a static bug finder (EBA -
https://github.com/models-team/eba) I have found a potential deadlock
in drivers/net/wireless/st/cw1200/
sta.c. This happens due to a recursive mutex_lock on `priv->conf_mutex'.
If this is indeed a bug, I will be happy to help with a patch.
A quick (not elegant) fix could be to unlock before the call to
`cw1200_do_unjoin' in line 1174, and lock again afterwards. It seems
that `cw1200_join_complete' is always called with `priv->conf_mutex'
held. Another option could be to add a Boolean parameter to
`cw1200_do_unjoin' to choose whether this function should take the
lock itself. Yet another option would be to have a
`__cw1200_do_unjoin' that does not lock, and make `cw1200_do_unjoin' a
wrapper over this that adds the locking; `cw1200_join_complete' would
call `__cw1200_do_unjoin' instead.
Someone who is actually familiar with this code may have a better
proposal though.
The trace is as follows:
1. Function `cw1200_join_complete_work' takes the first lock in line 1189:
// see https://github.com/torvalds/linux/blob/v4.9-rc5/drivers/net/wireless/st/cw1200/sta.c#L1189
mutex_lock(& priv->conf_mutex);
2. and subsequently calls `cw1200_join_complete';
3. which calls `cw1200_do_unjoin' in line 1174;
4. and this latter function takes the lock for the second time in line 1387:
// see https://github.com/torvalds/linux/blob/v4.9-rc5/drivers/net/wireless/st/cw1200/sta.c#L1387
mutex_lock(& priv->conf_mutex);
Hope it helps!
--
iago
^ permalink raw reply
* [PATCH] ath10k: fix monitor vdev for receiving other bss frames
From: Rajkumar Manoharan @ 2016-11-18 23:10 UTC (permalink / raw)
To: ath10k; +Cc: linux-wireless, rmanohar, Rajkumar Manoharan
In order to receive other BSS entries in mesh mode, Monitor vdev
is created whenever filter flag is set with OTHER_BSS. Recently
it is root caused that setting promisc filter for Mesh interface
is causing performance and stability issues. To fix this issue,
firmware will configure appropriate rxfilters by default for mesh
vdev during vdev creation. This change fixes monitor vdev creation
based on firmware IE
Signed-off-by: Rajkumar Manoharan <rmanohar@qca.qualcomm.com>
---
drivers/net/wireless/ath/ath10k/core.c | 1 +
drivers/net/wireless/ath/ath10k/core.h | 7 +++++++
drivers/net/wireless/ath/ath10k/mac.c | 4 +++-
3 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 7005e2a98726..b796be1d6318 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -326,6 +326,7 @@
[ATH10K_FW_FEATURE_PEER_FLOW_CONTROL] = "peer-flow-ctrl",
[ATH10K_FW_FEATURE_BTCOEX_PARAM] = "btcoex-param",
[ATH10K_FW_FEATURE_SKIP_NULL_FUNC_WAR] = "skip-null-func-war",
+ [ATH10K_FW_FEATURE_ALLOWS_MESH_BCAST] = "allows-mesh-bcast",
};
static unsigned int ath10k_core_get_fw_feature_str(char *buf,
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index e8decfaba5b6..179e52fbad55 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -562,6 +562,13 @@ enum ath10k_fw_features {
*/
ATH10K_FW_FEATURE_SKIP_NULL_FUNC_WAR = 15,
+ /* Firmware allow other BSS mesh broadcast/multicast frames without
+ * creating monitor interface. Appropriate rxfilters are programmed for
+ * mesh vdev by firmware itself. This feature flags will be used for
+ * not creating monitor vdev while configuring mesh node.
+ */
+ ATH10K_FW_FEATURE_ALLOWS_MESH_BCAST = 16,
+
/* keep last */
ATH10K_FW_FEATURE_COUNT,
};
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 717b2fad9a8a..56b8859d4a72 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -1167,7 +1167,9 @@ static bool ath10k_mac_monitor_vdev_is_needed(struct ath10k *ar)
return false;
return ar->monitor ||
- ar->filter_flags & FIF_OTHER_BSS ||
+ (!test_bit(ATH10K_FW_FEATURE_ALLOWS_MESH_BCAST,
+ ar->running_fw->fw_file.fw_features) &&
+ (ar->filter_flags & FIF_OTHER_BSS)) ||
test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
}
--
1.9.1
^ permalink raw reply related
* Re: [PATCH] RFC: Universal scan proposal
From: Dmitry Shmidt @ 2016-11-18 23:53 UTC (permalink / raw)
To: Arend Van Spriel; +Cc: linux-wireless
In-Reply-To: <7645397c-c9cd-f7ac-4add-fae580c485e9@broadcom.com>
On Thu, Nov 17, 2016 at 12:56 PM, Arend Van Spriel
<arend.vanspriel@broadcom.com> wrote:
> On 16-11-2016 23:47, dimitrysh@google.com wrote:
>> From 68a9d37a4c7e9dc7a90a6e922cdea52737a98d66 Mon Sep 17 00:00:00 2001
>> From: Dmitry Shmidt <dimitrysh@google.com>
>> Date: Wed, 16 Nov 2016 14:27:26 -0800
>> Subject: [PATCH] RFC: Universal scan proposal
>>
>> Currently we have sched scan with possibility of various
>> intervals. We would like to extend it to support also
>> different types of scan.
>> In case of powerful wlan CPU, all this functionality
>> can be offloaded.
>> In general case FW processes additional scan requests
>> and puts them into queue based on start time and interval.
>> Once current request is fulfilled, FW adds it (if interval != 0)
>> again to the queue with proper interval. If requests are
>> overlapping, new request can be combined with either one before,
>> or one after, assuming that requests are not mutually exclusive.
>> Combining requests is done by combining scan channels, ssids,
>> bssids and types of scan result. Once combined request was fulfilled
>> it will be reinserted as two (or three) different requests based on
>> their type and interval.
>> Each request has attribute:
>> Type: connectivity / location
>
> Don't see this type in the patch below (guess it was a last minute
> addition ;-) ).
>
>> Report: none / batch / immediate
>> Request may have priority and can be inserted into
>> the head of the queue.
>> Types of scans:
>> - Normal scan
>> - Scheduled scan
>> - Hotlist (BSSID scan)
>> - Roaming
>> - AutoJoin
>
> Don't see the last one mentioned in the patch below.
>
>> Change-Id: I9f3e4c975784f1c1c5156887144d80fc5a26bffa
>> Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
>> ---
>> include/net/cfg80211.h | 37 +++++++++++++++++++++++++++++++------
>> 1 file changed, 31 insertions(+), 6 deletions(-)
>>
>> diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
>> index 14b51d7..1ae2570 100644
>> --- a/include/net/cfg80211.h
>> +++ b/include/net/cfg80211.h
>> @@ -1602,8 +1602,10 @@ struct cfg80211_sched_scan_plan {
>> * cycle. The driver may ignore this parameter and start
>> * immediately (or at any other time), if this feature is not
>> * supported.
>
> At the the top of this kernel doc section it probably still refers to
> struct cfg80211_sched_scan_request.
>
>> + * @uscan_type: uscan type (scan, sched_scan, hotlist, scan_history,
>> roaming)
>> + * @uscan_results: uscan results report (none/batch/immediate)
>
> If you change the type to struct cfg80211_uscan_request it seems a bit
> redundant to prefix the field names with 'uscan_'.
>
>> */
>> -struct cfg80211_sched_scan_request {
>> +struct cfg80211_uscan_request {
>> struct cfg80211_ssid *ssids;
>> int n_ssids;
>> u32 n_channels;
>> @@ -1621,6 +1623,10 @@ struct cfg80211_sched_scan_request {
>> u8 mac_addr[ETH_ALEN] __aligned(2);
>> u8 mac_addr_mask[ETH_ALEN] __aligned(2);
>>
>> + /* universal scan fields */
>
> As the struct name changed it seems to me all fields in the struct are
> universal scan fields.
>
>> + u32 uscan_type;
>> + u32 uscan_results;
>> +
>> /* internal */
>> struct wiphy *wiphy;
>> struct net_device *dev;
>> @@ -1633,6 +1639,21 @@ struct cfg80211_sched_scan_request {
>> };
>>
>> /**
>> + * struct cfg80211_uscan_capa - universal scan capabilities
>> + *
>> + * @uscan_type: uscan type (scan, sched_scan, hotlist, scan_history,
>> roaming)
>> + * @max_ssids: max amount of ssids in sched scan
>> + * @max_hotlist: max amount of bssids in hotlist
>> + * @max_history: max amount of records in history
>> + */
>> +struct cfg80211_uscan_capa {
>> + u32 uscan_type;
>> + u32 max_ssids;
>> + u32 max_hotlist;
>> + u32 max_history;
>> +};
>> +
>> +/**
>> * enum cfg80211_signal_type - signal type
>> *
>> * @CFG80211_SIGNAL_TYPE_NONE: no signal strength information available
>> @@ -2898,10 +2919,13 @@ struct cfg80211_ops {
>> int (*set_antenna)(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant);
>> int (*get_antenna)(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant);
>>
>> - int (*sched_scan_start)(struct wiphy *wiphy,
>> + int (*uscan_get_capa)(struct wiphy *wiphy, struct net_device *dev,
>> + struct cfg80211_uscan_capa *uscan_capa);
>
> Not sure if people start worrying about the size of struct wiphy
> already, but for static information like capabilities I think a callback
> is overkill.
>
>> + int (*uscan_start)(struct wiphy *wiphy,
>> struct net_device *dev,
>> - struct cfg80211_sched_scan_request *request);
>> - int (*sched_scan_stop)(struct wiphy *wiphy, struct net_device
>> *dev);
>> + struct cfg80211_uscan_request *request);
>
> So given the two extra fields, what different driver/device behaviour is
> required, eg. if uscan_type == roaming what will be different compared
> to a normal scan.
>
>> + int (*uscan_stop)(struct wiphy *wiphy, struct net_device *dev,
>> + int uscan_id);
>
> The uscan_id is probably referring to a specific scan request started by
> uscan_start. So who hands out that id (wiphy->cookie_counter?) and how
> does the driver know about it. If cfg80211 determines the id it probably
> need to be passed in the uscan request.
>
>> int (*set_rekey_data)(struct wiphy *wiphy, struct net_device *dev,
>> struct cfg80211_gtk_rekey_data *data);
>> @@ -4305,11 +4329,12 @@ void cfg80211_scan_done(struct
>> cfg80211_scan_request *request,
>> struct cfg80211_scan_info *info);
>>
>> /**
>> - * cfg80211_sched_scan_results - notify that new scan results are
>> available
>> + * cfg80211_uscan_results - notify that new uscan results are available
>> *
>> * @wiphy: the wiphy which got scheduled scan results
>> + * @uscan_id: type of scan results
>
> Confused now. What is uscan_id here? Same as in .uscan_stop() callback?
Just to clarify - this is not a patch that is passing compilation.
It is something to discuss as a concept.
And we have several options - either have different set of functions for
different scans or to get id from start and use it for stop and notification.
>> */
>> -void cfg80211_sched_scan_results(struct wiphy *wiphy);
>> +void cfg80211_sched_scan_results(struct wiphy *wiphy, int uscan_id);
>
> should this be renamed to cfg80211_uscan_results().
>
>> /**
>> * cfg80211_sched_scan_stopped - notify that the scheduled scan has
>> stopped
>
> Also change to cfg80211_uscan_stopped()? Does it need an additional
> argument, ie. uscan_id.
Yes
> Regards,
> Arend
^ permalink raw reply
* Re: [PATCH 0/7] rtl8xxxu: Pending patches
From: Barry Day @ 2016-11-19 1:46 UTC (permalink / raw)
To: Jes.Sorensen; +Cc: linux-wireless, kvalo, Larry.Finger
In-Reply-To: <1479505468-29383-1-git-send-email-Jes.Sorensen@redhat.com>
On Fri, Nov 18, 2016 at 04:44:21PM -0500, Jes.Sorensen@redhat.com wrote:
> From: Jes Sorensen <Jes.Sorensen@redhat.com>
>
> Kalle,
>
> Please find attached a number of patches for the rtl8xxxu
> driver.
>
> The issues reported with wpa_supplicant on 8723bu still needs further
> investigation.
>
The patch I posted that you want tested more will also fix the wpa_supplicant
issue.
Currently I'm looking at why the tx rate is not what it should be. I feel
fixing that first will be beneficial for fixing any other issues.
The recent merge has made my local branch of rtl8xxxu-devel 14 commits ahead.
Do I need to do a reset and submit a new patch for the DWA-131 dongle?
Barry
^ permalink raw reply
* Re: [PATCH 0/7] rtl8xxxu: Pending patches
From: Jes Sorensen @ 2016-11-19 2:00 UTC (permalink / raw)
To: Barry Day; +Cc: linux-wireless, kvalo, Larry.Finger
In-Reply-To: <20161119014648.GA7140@testbox>
Barry Day <briselec@gmail.com> writes:
> On Fri, Nov 18, 2016 at 04:44:21PM -0500, Jes.Sorensen@redhat.com wrote:
>> From: Jes Sorensen <Jes.Sorensen@redhat.com>
>>
>> Kalle,
>>
>> Please find attached a number of patches for the rtl8xxxu
>> driver.
>>
>> The issues reported with wpa_supplicant on 8723bu still needs further
>> investigation.
>>
>
> The patch I posted that you want tested more will also fix the
> wpa_supplicant issue. Currently I'm looking at why the tx rate is not
> what it should be. I feel fixing that first will be beneficial for
> fixing any other issues.
Interesting, I was thinking that might be the case. I do want to dig
into this further to understand it better. If we use your solution I
will want to make sure we cover both gen1 and gen2 parts.
> The recent merge has made my local branch of rtl8xxxu-devel 14 commits ahead.
> Do I need to do a reset and submit a new patch for the DWA-131 dongle?
In general you need to use 'git pull --rebase' on my tree. I rebase it
to stay in sync with Kalle's tree.
The DWA-131 is the 8192eu? Sorry a bit behind and my mind is losing
bits. If it's the patch you posted earlier I can dig it out and play
with it - I am still catching up though, so please be patient.
Cheers,
Jes
^ permalink raw reply
* Re: [PATCH 0/7] rtl8xxxu: Pending patches
From: Barry Day @ 2016-11-19 2:38 UTC (permalink / raw)
To: Jes Sorensen; +Cc: linux-wireless, kvalo, Larry.Finger
In-Reply-To: <wrfjbmxcb55h.fsf@redhat.com>
On Fri, Nov 18, 2016 at 09:00:10PM -0500, Jes Sorensen wrote:
> Barry Day <briselec@gmail.com> writes:
> > On Fri, Nov 18, 2016 at 04:44:21PM -0500, Jes.Sorensen@redhat.com wrote:
> >> From: Jes Sorensen <Jes.Sorensen@redhat.com>
> >>
> >> Kalle,
> >>
> >> Please find attached a number of patches for the rtl8xxxu
> >> driver.
> >>
> >> The issues reported with wpa_supplicant on 8723bu still needs further
> >> investigation.
> >>
> >
> > The patch I posted that you want tested more will also fix the
> > wpa_supplicant issue. Currently I'm looking at why the tx rate is not
> > what it should be. I feel fixing that first will be beneficial for
> > fixing any other issues.
>
> Interesting, I was thinking that might be the case. I do want to dig
> into this further to understand it better. If we use your solution I
> will want to make sure we cover both gen1 and gen2 parts.
>
> > The recent merge has made my local branch of rtl8xxxu-devel 14 commits ahead.
> > Do I need to do a reset and submit a new patch for the DWA-131 dongle?
>
> In general you need to use 'git pull --rebase' on my tree. I rebase it
> to stay in sync with Kalle's tree.
>
> The DWA-131 is the 8192eu? Sorry a bit behind and my mind is losing
> bits. If it's the patch you posted earlier I can dig it out and play
> with it - I am still catching up though, so please be patient.
>
> Cheers,
> Jes
>
>
yes it's an 8192eu.
Would you accept a patch that adds a struct device pointer to rtl8xxxu_priv
and used as the device pointer in the logging functions? Then all the messages
will start with the driver name making them easier to find.
Barry
^ permalink raw reply
* Re: [PATCH v8] mwifiex: parse device tree node for PCIe
From: Kalle Valo @ 2016-11-19 6:56 UTC (permalink / raw)
To: Rajat Jain
Cc: Amitkumar Karwar, linux-wireless, Cathy Luo, Nishant Sarmukadam,
Brian Norris, Dmitry Torokhov, devicetree, Xinming Hu
In-Reply-To: <CACK8Z6EAXTd07ZK3Y6Hy=7iGEY0fN91gO9Tz0NMh+shDAJoc+A@mail.gmail.com>
Rajat Jain <rajatja@google.com> writes:
> On Fri, Nov 18, 2016 at 8:21 AM, Kalle Valo <kvalo@codeaurora.org> wrote:
>
> > @@ -10,6 +10,8 @@ Required properties:
> >=C2=A0 =C2=A0 - compatible : should be one of the following:
> >=C2=A0 =C2=A0 =C2=A0 =C2=A0* "marvell,sd8897"
> >=C2=A0 =C2=A0 =C2=A0 =C2=A0* "marvell,sd8997"
> > +=C2=A0 =C2=A0 =C2=A0* "pci11ab,2b42"
> > +=C2=A0 =C2=A0 =C2=A0* "pci1b4b,2b42"
>
> Are these correct? In v6 Rob said:
>
> "I think I already said this, but you have the vendor and product IDs
> reversed."
>
> https://patchwork.kernel.org/patch/9390225/
>=20=20=20=20
>
>
> I had responded to it:
>
>
> Hi Rob,
>
>> I think I already said this, but you have the vendor and product IDs
>> reversed.
>
> Actually Marvell has 2 vendor IDs assigned to it. In include/linux/pci_id=
s.h:
>
> #define PCI_VENDOR_ID_MARVELL 0x11ab
> #define PCI_VENDOR_ID_MARVELL_EXT 0x1b4b
>
> So in this case the compatible property describes a single product ID,
> with both possible vendor IDs.
Ok, I missed that.
BTW, please avoid using HTML. text/plain is very much preferred.
--=20
Kalle Valo
^ permalink raw reply
* Re: [v8] mwifiex: parse device tree node for PCIe
From: Kalle Valo @ 2016-11-19 7:11 UTC (permalink / raw)
To: Amitkumar Karwar
Cc: linux-wireless, Cathy Luo, Nishant Sarmukadam, rajatja,
briannorris, dmitry.torokhov, devicetree, Xinming Hu,
Amitkumar Karwar
In-Reply-To: <1479472624-22340-1-git-send-email-akarwar@marvell.com>
Amitkumar Karwar <akarwar@marvell.com> wrote:
> From: Xinming Hu <huxm@marvell.com>
>
> This patch derives device tree node from pcie bus layer framework.
> Device tree bindings file has been renamed(marvell-sd8xxx.txt ->
> marvell-8xxx.txt) to accommodate PCIe changes.
>
> Signed-off-by: Xinming Hu <huxm@marvell.com>
> Signed-off-by: Rajat Jain <rajatja@google.com>
> Reviewed-by: Brian Norris <briannorris@chromium.org>
> Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
> Acked-by: Rob Herring <robh@kernel.org>
Patch applied to wireless-drivers-next.git, thanks.
6b4480d109b0 mwifiex: parse device tree node for PCIe
--
https://patchwork.kernel.org/patch/9436393/
Documentation about submitting wireless patches and checking status
from patchwork:
https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches
^ permalink raw reply
* Re: [v4,1/3] mwifiex: Allow mwifiex early access to device structure
From: Kalle Valo @ 2016-11-19 7:13 UTC (permalink / raw)
To: Amitkumar Karwar
Cc: linux-wireless, Cathy Luo, Nishant Sarmukadam, rajatja,
briannorris, dmitry.torokhov, Amitkumar Karwar
In-Reply-To: <1479216964-3328-1-git-send-email-akarwar@marvell.com>
Amitkumar Karwar <akarwar@marvell.com> wrote:
> From: Rajat Jain <rajatja@google.com>
>
> Today all the interface drivers (usb/pcie/sdio) assign the
> adapter->dev in the register_dev() callback, although they
> have this piece of info well before hand.
>
> This patch makes the device structure available for mwifiex
> right at the beginning, so that it can be used for early
> initialization if needed.
>
> This is needed for subsequent patches in this patchset that
> intend to unify and consolidate some of the code that would
> otherwise have to be duplicated among the interface drivers
> (sdio, pcie, usb).
>
> Signed-off-by: Rajat Jain <rajatja@google.com>
> Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
3 patches applied to wireless-drivers-next.git, thanks.
2e02b5814217 mwifiex: Allow mwifiex early access to device structure
5e28e5fbdcf0 mwifiex: Introduce mwifiex_probe_of() to parse common properties
853402a00823 mwifiex: Enable WoWLAN for both sdio and pcie
--
https://patchwork.kernel.org/patch/9429835/
Documentation about submitting wireless patches and checking status
from patchwork:
https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches
^ permalink raw reply
* Re: [v4, 01/11] mwifiex: check tx_hw_pending before downloading sleep confirm
From: Kalle Valo @ 2016-11-19 7:19 UTC (permalink / raw)
To: Amitkumar Karwar
Cc: linux-wireless, Cathy Luo, Nishant Sarmukadam, rajatja,
briannorris, dmitry.torokhov, Shengzhen Li, Amitkumar Karwar
In-Reply-To: <1479477634-27841-1-git-send-email-akarwar@marvell.com>
Amitkumar Karwar <akarwar@marvell.com> wrote:
> From: Shengzhen Li <szli@marvell.com>
>
> We may get SLEEP event from firmware even if TXDone interrupt
> for last Tx packet is still pending. In this case, we may
> end up accessing PCIe memory for handling TXDone after power
> save handshake is completed. This causes kernel crash with
> external abort.
>
> This patch will only allow downloading sleep confirm
> when no tx done interrupt is pending in the hardware.
>
> Signed-off-by: Cathy Luo <cluo@marvell.com>
> Signed-off-by: Shengzhen Li <szli@marvell.com>
> Tested-by: Xinming Hu <huxm@marvell.com>
> Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
> Reviewed-by: Brian Norris <briannorris@chromium.org>
11 patches applied to wireless-drivers-next.git, thanks.
eb2428fb1a21 mwifiex: check tx_hw_pending before downloading sleep confirm
6712076883ca mwifiex: complete blocked power save handshake in main process
4a79aa17d53e mwifiex: resolve races between async FW init (failure) and device removal
a1beec4b2c6f mwifiex: remove redundant pdev check in suspend/resume handlers
7ccdf72f9128 mwifiex: don't pretend to resume while remove()'ing
b42dbb27e326 mwifiex: resolve suspend() race with async FW init failure
bcf28a2f2846 mwifiex: reset card->adapter during device unregister
58b7033551cd mwifiex: usb: handle HS failures
6caf34cb3a92 mwifiex: sdio: don't check for NULL sdio_func
e98fb11ffa0a mwifiex: stop checking for NULL drvata/intfdata
97489c284d9f mwifiex: pcie: stop checking for NULL adapter->card
--
https://patchwork.kernel.org/patch/9436513/
Documentation about submitting wireless patches and checking status
from patchwork:
https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches
^ permalink raw reply
* Re: rtlwifi: rtl8723be: Fix bug in ant_sel code from commit c18d8f509571
From: Kalle Valo @ 2016-11-19 7:24 UTC (permalink / raw)
To: Larry Finger; +Cc: devel, linux-wireless, Ping-Ke Shih, Larry Finger, Stable
In-Reply-To: <20161117180533.16136-1-Larry.Finger@lwfinger.net>
Larry Finger <Larry.Finger@lwfinger.net> wrote:
> From: Ping-Ke Shih <pkshih@realtek.com>
>
> In commit c18d8f509571 ("rtlwifi: rtl8723be: Add antenna select module
> parameter"), wifi was fixed for those laptops that have only a single
> antenna but have an incorrectly coded EEPROM. This error causes the
> driver to select the wrong antenna. In that commit, one necessary change
> that affects Bluetooth operation was missed.
>
> Fixes: c18d8f509571 ("rtlwifi: rtl8723be: Add antenna select module parameter")
> Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
> Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net>
> Cc: Stable <stable@vger.kernel.org>
Requested by Larry
Patch set to Changes Requested.
--
https://patchwork.kernel.org/patch/9434985/
Documentation about submitting wireless patches and checking status
from patchwork:
https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches
^ permalink raw reply
* Re: [PATCH 0/7] rtl8xxxu: Pending patches
From: Jes Sorensen @ 2016-11-19 23:53 UTC (permalink / raw)
To: Barry Day; +Cc: linux-wireless, kvalo, Larry.Finger
In-Reply-To: <20161119023830.GA7324@testbox>
Barry Day <briselec@gmail.com> writes:
> On Fri, Nov 18, 2016 at 09:00:10PM -0500, Jes Sorensen wrote:
>> Barry Day <briselec@gmail.com> writes:
>> > On Fri, Nov 18, 2016 at 04:44:21PM -0500, Jes.Sorensen@redhat.com wrote:
>> >> From: Jes Sorensen <Jes.Sorensen@redhat.com>
>> >>
>> >> Kalle,
>> >>
>> >> Please find attached a number of patches for the rtl8xxxu
>> >> driver.
>> >>
>> >> The issues reported with wpa_supplicant on 8723bu still needs further
>> >> investigation.
>> >>
>> >
>> > The patch I posted that you want tested more will also fix the
>> > wpa_supplicant issue. Currently I'm looking at why the tx rate is not
>> > what it should be. I feel fixing that first will be beneficial for
>> > fixing any other issues.
>>
>> Interesting, I was thinking that might be the case. I do want to dig
>> into this further to understand it better. If we use your solution I
>> will want to make sure we cover both gen1 and gen2 parts.
>>
>> > The recent merge has made my local branch of rtl8xxxu-devel 14
>> > commits ahead.
>> > Do I need to do a reset and submit a new patch for the DWA-131 dongle?
>>
>> In general you need to use 'git pull --rebase' on my tree. I rebase it
>> to stay in sync with Kalle's tree.
>>
>> The DWA-131 is the 8192eu? Sorry a bit behind and my mind is losing
>> bits. If it's the patch you posted earlier I can dig it out and play
>> with it - I am still catching up though, so please be patient.
>
> yes it's an 8192eu.
Gotcha - how do you do your testing to reproduce the problem btw? Most
of my testing is using the 8723au as a primary device and the next
device as a follow-on, so I may not see as long a run with the device
active as you see.
> Would you accept a patch that adds a struct device pointer to rtl8xxxu_priv
> and used as the device pointer in the logging functions? Then all the messages
> will start with the driver name making them easier to find.
How do you mean? Right now I have a struct usb_device pointer and
dereference that for ->dev to use with dev_info() messages etc. Do you
want to see more than that?
Cheers,
Jes
^ permalink raw reply
* Re: ath10k stuck in mesh mode
From: Bob Copeland @ 2016-11-20 1:07 UTC (permalink / raw)
To: Matteo Grandi; +Cc: LinuxWireless Mailing List
In-Reply-To: <CAHdg3xaCnAp8rgMs3mVYvV6TFadNjwBtCo+rpHgH_-L8JUo-dw@mail.gmail.com>
On Fri, Nov 18, 2016 at 10:43:06PM +0100, Matteo Grandi wrote:
> So the question is: who decide if use MIMO and higher MCS hopefully on
> 80MHz channel or not? Is the firmware? Is there a way to force the
> interface to use 80MHz and/or MIMO? (iw provide the possibility to
> choose between [HT20/HT40-/HT40+]).
You can specify the channel width like so:
iw dev wlan0 set freq 5745 80 5775
iw dev wlan0 mesh join mesh-vht
--
Bob Copeland %% http://bobcopeland.com/
^ permalink raw reply
* Re: [PATCH 0/7] rtl8xxxu: Pending patches
From: Barry Day @ 2016-11-20 2:42 UTC (permalink / raw)
To: Jes Sorensen; +Cc: linux-wireless, kvalo, Larry.Finger
In-Reply-To: <wrfj37inauwp.fsf@redhat.com>
On Sat, Nov 19, 2016 at 06:53:42PM -0500, Jes Sorensen wrote:
> Barry Day <briselec@gmail.com> writes:
> > On Fri, Nov 18, 2016 at 09:00:10PM -0500, Jes Sorensen wrote:
> >> Barry Day <briselec@gmail.com> writes:
> >> > On Fri, Nov 18, 2016 at 04:44:21PM -0500, Jes.Sorensen@redhat.com wrote:
> >> >> From: Jes Sorensen <Jes.Sorensen@redhat.com>
> >> >>
> >> >> Kalle,
> >> >>
> >> >> Please find attached a number of patches for the rtl8xxxu
> >> >> driver.
> >> >>
> >> >> The issues reported with wpa_supplicant on 8723bu still needs further
> >> >> investigation.
> >> >>
> >> >
> >> > The patch I posted that you want tested more will also fix the
> >> > wpa_supplicant issue. Currently I'm looking at why the tx rate is not
> >> > what it should be. I feel fixing that first will be beneficial for
> >> > fixing any other issues.
> >>
> >> Interesting, I was thinking that might be the case. I do want to dig
> >> into this further to understand it better. If we use your solution I
> >> will want to make sure we cover both gen1 and gen2 parts.
> >>
> >> > The recent merge has made my local branch of rtl8xxxu-devel 14
> >> > commits ahead.
> >> > Do I need to do a reset and submit a new patch for the DWA-131 dongle?
> >>
> >> In general you need to use 'git pull --rebase' on my tree. I rebase it
> >> to stay in sync with Kalle's tree.
> >>
> >> The DWA-131 is the 8192eu? Sorry a bit behind and my mind is losing
> >> bits. If it's the patch you posted earlier I can dig it out and play
> >> with it - I am still catching up though, so please be patient.
> >
> > yes it's an 8192eu.
>
> Gotcha - how do you do your testing to reproduce the problem btw? Most
> of my testing is using the 8723au as a primary device and the next
> device as a follow-on, so I may not see as long a run with the device
> active as you see.
>
Testing is simple. Connect to an AP in the usual manner..disconnect..reconnect.
The 8192eu will fail to reconnect and John Heenan reported the 8723bu also
fails to reconnect. Even though he was directly stopping and restarting
wpa_supplicant, it's the same thing to the driver -
connect..disconnect..reconnect.
> > Would you accept a patch that adds a struct device pointer to rtl8xxxu_priv
> > and used as the device pointer in the logging functions? Then all the messages
> > will start with the driver name making them easier to find.
>
> How do you mean? Right now I have a struct usb_device pointer and
> dereference that for ->dev to use with dev_info() messages etc. Do you
> want to see more than that?
>
> Cheers,
> Jes
With using a usb_device pointer, each message starts with the usb bus address.
Plug it into a different port and that address could change. By using a pointer
to the device associated with the wiphy each message will begin with the driver
name. Just makes it easier for the average user to report what's in the log
because he can just grep for "rtl8xxxu".
Barry
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox