Linux wireless drivers development
 help / color / mirror / Atom feed
* [PATCH v2 00/29] ath10k: mac80211 driver for Qualcomm Atheros 802.11ac CQA98xx devices
From: Kalle Valo @ 2013-06-01 11:24 UTC (permalink / raw)
  To: linux-wireless; +Cc: ath10k-devel

Here's a new mac80211 driver for Qualcomm Atheros 802.11ac QCA98xx devices.
A major difference from ath9k is that there's now a firmware and that's why we
had to implement a new driver.

We received very little comments in the first review round. This is the second,
and hopefully the final, version of the driver for review. I'll first send the
driver in smaller patches, one file per patch, to make it easier to review. The
final version will be one big commit, which I'm planning to submit through
ath6kl.git together with ath6kl patches. And no need to panic, I will rename
the git tree to something better in the future :)

The firmware is temporarily available here:

https://github.com/kvalo/ath10k-firmware

This is the commit log I'll use with the big commit:

---------------------------------------------------------------------
ath10k: mac80211 driver for Qualcomm Atheros 802.11ac CQA98xx devices

Here's a new mac80211 driver for Qualcomm Atheros 802.11ac QCA98xx devices.
A major difference from ath9k is that there's now a firmware and
that's why we had to implement a new driver.

The wiki page for the driver is:

http://wireless.kernel.org/en/users/Drivers/ath10k

The driver has had many authors, they are listed here alphabetically:

Bartosz Markowski <bartosz.markowski@tieto.com>
Janusz Dziedzic <janusz.dziedzic@tieto.com>
Kalle Valo <kvalo@qca.qualcomm.com>
Marek Kwaczynski <marek.kwaczynski@tieto.com>
Marek Puzyniak <marek.puzyniak@tieto.com>
Michal Kazior <michal.kazior@tieto.com>
Sujith Manoharan <c_manoha@qca.qualcomm.com>

Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
---------------------------------------------------------------------

And these are the changes from v1:

Kalle Valo (6):
      ath10k: set vht_nss correctly
      ath10k: change chan_to_phymode() use chandef
      ath10k: remove band_center_freq()
      ath10k: include all management frames to ATH10K_DBG_BEACON
      ath10k: add ATH10K_DBG_DATA
      ath10k: preliminary VHT support

Michal Kazior (14):
      ath10k: remove CTRY_DEFAULT assignment
      ath10k: remove static mac address mask
      ath10k: remove unnecessary include
      ath10k: program FW with appropriate regdomain
      ath10k: fix hw scan cancelling
      ath10k: sync WMI interface to FW 636
      ath10k: change errno if we run out of msdu_ids
      ath10k: ath10k_htc_prepare_tx_skb() never fails
      ath10k: add lockdep asserts to htc skb dequeuing
      ath10k: simplify htc flow control
      ath10k: remove unused queue limit
      ath10k: introduce proper htt tx flow control
      ath10k: detect htt pending tx limit at runtime
      ath10k: fix sparse warning

The actual commits are available from ath10k.git:

https://github.com/kvalo/ath10k

Please review and comment. I'm hoping to get this driver to 3.11.

---

Kalle Valo (29):
      ath10k: add bmi.c
      ath10k: add bmi.h
      ath10k: add ce.c
      ath10k: add ce.h
      ath10k: add core.c
      ath10k: add core.h
      ath10k: add debug.c
      ath10k: add debug.h
      ath10k: add hif.h
      ath10k: add htc.c
      ath10k: add htc.h
      ath10k: add htt.c
      ath10k: add htt.h
      ath10k: add htt_rx.c
      ath10k: add htt_tx.c
      ath10k: add hw.h
      ath10k: add mac.c
      ath10k: add mac.h
      ath10k: add pci.c
      ath10k: add pci.h
      ath10k: add rx_desc.h
      ath10k: add targaddrs.h
      ath10k: add trace.c
      ath10k: add trace.h
      ath10k: add txrx.c
      ath10k: add txrx.h
      ath10k: add wmi.c
      ath10k: add wmi.h
      ath10k: add Kconfig and Makefile


 drivers/net/wireless/ath/ath10k/Kconfig     |   39 
 drivers/net/wireless/ath/ath10k/Makefile    |   20 
 drivers/net/wireless/ath/ath10k/bmi.c       |  295 +++
 drivers/net/wireless/ath/ath10k/bmi.h       |  224 ++
 drivers/net/wireless/ath/ath10k/ce.c        | 1189 ++++++++++
 drivers/net/wireless/ath/ath10k/ce.h        |  516 +++++
 drivers/net/wireless/ath/ath10k/core.c      |  665 ++++++
 drivers/net/wireless/ath/ath10k/core.h      |  369 +++
 drivers/net/wireless/ath/ath10k/debug.c     |  503 ++++
 drivers/net/wireless/ath/ath10k/debug.h     |   90 +
 drivers/net/wireless/ath/ath10k/hif.h       |  137 +
 drivers/net/wireless/ath/ath10k/htc.c       | 1000 +++++++++
 drivers/net/wireless/ath/ath10k/htc.h       |  368 +++
 drivers/net/wireless/ath/ath10k/htt.c       |  152 +
 drivers/net/wireless/ath/ath10k/htt.h       | 1338 ++++++++++++
 drivers/net/wireless/ath/ath10k/htt_rx.c    | 1167 ++++++++++
 drivers/net/wireless/ath/ath10k/htt_tx.c    |  510 ++++
 drivers/net/wireless/ath/ath10k/hw.h        |  304 +++
 drivers/net/wireless/ath/ath10k/mac.c       | 3066 +++++++++++++++++++++++++++
 drivers/net/wireless/ath/ath10k/mac.h       |   61 +
 drivers/net/wireless/ath/ath10k/pci.c       | 2506 ++++++++++++++++++++++
 drivers/net/wireless/ath/ath10k/pci.h       |  355 +++
 drivers/net/wireless/ath/ath10k/rx_desc.h   |  990 +++++++++
 drivers/net/wireless/ath/ath10k/targaddrs.h |  449 ++++
 drivers/net/wireless/ath/ath10k/trace.c     |   20 
 drivers/net/wireless/ath/ath10k/trace.h     |  170 +
 drivers/net/wireless/ath/ath10k/txrx.c      |  417 ++++
 drivers/net/wireless/ath/ath10k/txrx.h      |   39 
 drivers/net/wireless/ath/ath10k/wmi.c       | 2081 ++++++++++++++++++
 drivers/net/wireless/ath/ath10k/wmi.h       | 3052 +++++++++++++++++++++++++++
 30 files changed, 22092 insertions(+)
 create mode 100644 drivers/net/wireless/ath/ath10k/Kconfig
 create mode 100644 drivers/net/wireless/ath/ath10k/Makefile
 create mode 100644 drivers/net/wireless/ath/ath10k/bmi.c
 create mode 100644 drivers/net/wireless/ath/ath10k/bmi.h
 create mode 100644 drivers/net/wireless/ath/ath10k/ce.c
 create mode 100644 drivers/net/wireless/ath/ath10k/ce.h
 create mode 100644 drivers/net/wireless/ath/ath10k/core.c
 create mode 100644 drivers/net/wireless/ath/ath10k/core.h
 create mode 100644 drivers/net/wireless/ath/ath10k/debug.c
 create mode 100644 drivers/net/wireless/ath/ath10k/debug.h
 create mode 100644 drivers/net/wireless/ath/ath10k/hif.h
 create mode 100644 drivers/net/wireless/ath/ath10k/htc.c
 create mode 100644 drivers/net/wireless/ath/ath10k/htc.h
 create mode 100644 drivers/net/wireless/ath/ath10k/htt.c
 create mode 100644 drivers/net/wireless/ath/ath10k/htt.h
 create mode 100644 drivers/net/wireless/ath/ath10k/htt_rx.c
 create mode 100644 drivers/net/wireless/ath/ath10k/htt_tx.c
 create mode 100644 drivers/net/wireless/ath/ath10k/hw.h
 create mode 100644 drivers/net/wireless/ath/ath10k/mac.c
 create mode 100644 drivers/net/wireless/ath/ath10k/mac.h
 create mode 100644 drivers/net/wireless/ath/ath10k/pci.c
 create mode 100644 drivers/net/wireless/ath/ath10k/pci.h
 create mode 100644 drivers/net/wireless/ath/ath10k/rx_desc.h
 create mode 100644 drivers/net/wireless/ath/ath10k/targaddrs.h
 create mode 100644 drivers/net/wireless/ath/ath10k/trace.c
 create mode 100644 drivers/net/wireless/ath/ath10k/trace.h
 create mode 100644 drivers/net/wireless/ath/ath10k/txrx.c
 create mode 100644 drivers/net/wireless/ath/ath10k/txrx.h
 create mode 100644 drivers/net/wireless/ath/ath10k/wmi.c
 create mode 100644 drivers/net/wireless/ath/ath10k/wmi.h


^ permalink raw reply

* [PATCH v2 01/29] ath10k: add bmi.c
From: Kalle Valo @ 2013-06-01 11:24 UTC (permalink / raw)
  To: linux-wireless; +Cc: ath10k-devel
In-Reply-To: <20130601111820.32443.46794.stgit@localhost6.localdomain6>

Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
---
 drivers/net/wireless/ath/ath10k/bmi.c |  295 +++++++++++++++++++++++++++++++++
 1 file changed, 295 insertions(+)
 create mode 100644 drivers/net/wireless/ath/ath10k/bmi.c

diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c
new file mode 100644
index 0000000..1a2ef51
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/bmi.c
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * 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 "bmi.h"
+#include "hif.h"
+#include "debug.h"
+#include "htc.h"
+
+int ath10k_bmi_done(struct ath10k *ar)
+{
+	struct bmi_cmd cmd;
+	u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.done);
+	int ret;
+
+	if (ar->bmi.done_sent) {
+		ath10k_dbg(ATH10K_DBG_CORE, "%s skipped\n", __func__);
+		return 0;
+	}
+
+	ar->bmi.done_sent = true;
+	cmd.id = __cpu_to_le32(BMI_DONE);
+
+	ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
+	if (ret) {
+		ath10k_warn("unable to write to the device: %d\n", ret);
+		return ret;
+	}
+
+	ath10k_dbg(ATH10K_DBG_CORE, "BMI done\n");
+	return 0;
+}
+
+int ath10k_bmi_get_target_info(struct ath10k *ar,
+			       struct bmi_target_info *target_info)
+{
+	struct bmi_cmd cmd;
+	union bmi_resp resp;
+	u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.get_target_info);
+	u32 resplen = sizeof(resp.get_target_info);
+	int ret;
+
+	if (ar->bmi.done_sent) {
+		ath10k_warn("BMI Get Target Info Command disallowed\n");
+		return -EBUSY;
+	}
+
+	cmd.id = __cpu_to_le32(BMI_GET_TARGET_INFO);
+
+	ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);
+	if (ret) {
+		ath10k_warn("unable to get target info from device\n");
+		return ret;
+	}
+
+	if (resplen < sizeof(resp.get_target_info)) {
+		ath10k_warn("invalid get_target_info response length (%d)\n",
+			    resplen);
+		return -EIO;
+	}
+
+	target_info->version = __le32_to_cpu(resp.get_target_info.version);
+	target_info->type    = __le32_to_cpu(resp.get_target_info.type);
+	return 0;
+}
+
+int ath10k_bmi_read_memory(struct ath10k *ar,
+			   u32 address, void *buffer, u32 length)
+{
+	struct bmi_cmd cmd;
+	union bmi_resp resp;
+	u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.read_mem);
+	u32 rxlen;
+	int ret;
+
+	if (ar->bmi.done_sent) {
+		ath10k_warn("command disallowed\n");
+		return -EBUSY;
+	}
+
+	ath10k_dbg(ATH10K_DBG_CORE,
+		   "%s: (device: 0x%p, address: 0x%x, length: %d)\n",
+		   __func__, ar, address, length);
+
+	while (length) {
+		rxlen = min_t(u32, length, BMI_MAX_DATA_SIZE);
+
+		cmd.id            = __cpu_to_le32(BMI_READ_MEMORY);
+		cmd.read_mem.addr = __cpu_to_le32(address);
+		cmd.read_mem.len  = __cpu_to_le32(rxlen);
+
+		ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen,
+						  &resp, &rxlen);
+		if (ret) {
+			ath10k_warn("unable to read from the device\n");
+			return ret;
+		}
+
+		memcpy(buffer, resp.read_mem.payload, rxlen);
+		address += rxlen;
+		buffer  += rxlen;
+		length  -= rxlen;
+	}
+
+	return 0;
+}
+
+int ath10k_bmi_write_memory(struct ath10k *ar,
+			    u32 address, const void *buffer, u32 length)
+{
+	struct bmi_cmd cmd;
+	u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.write_mem);
+	u32 txlen;
+	int ret;
+
+	if (ar->bmi.done_sent) {
+		ath10k_warn("command disallowed\n");
+		return -EBUSY;
+	}
+
+	ath10k_dbg(ATH10K_DBG_CORE,
+		   "%s: (device: 0x%p, address: 0x%x, length: %d)\n",
+		   __func__, ar, address, length);
+
+	while (length) {
+		txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen);
+
+		/* copy before roundup to avoid reading beyond buffer*/
+		memcpy(cmd.write_mem.payload, buffer, txlen);
+		txlen = roundup(txlen, 4);
+
+		cmd.id             = __cpu_to_le32(BMI_WRITE_MEMORY);
+		cmd.write_mem.addr = __cpu_to_le32(address);
+		cmd.write_mem.len  = __cpu_to_le32(txlen);
+
+		ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,
+						  NULL, NULL);
+		if (ret) {
+			ath10k_warn("unable to write to the device\n");
+			return ret;
+		}
+
+		/* fixup roundup() so `length` zeroes out for last chunk */
+		txlen = min(txlen, length);
+
+		address += txlen;
+		buffer  += txlen;
+		length  -= txlen;
+	}
+
+	return 0;
+}
+
+int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param)
+{
+	struct bmi_cmd cmd;
+	union bmi_resp resp;
+	u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.execute);
+	u32 resplen = sizeof(resp.execute);
+	int ret;
+
+	if (ar->bmi.done_sent) {
+		ath10k_warn("command disallowed\n");
+		return -EBUSY;
+	}
+
+	ath10k_dbg(ATH10K_DBG_CORE,
+		   "%s: (device: 0x%p, address: 0x%x, param: %d)\n",
+		   __func__, ar, address, *param);
+
+	cmd.id            = __cpu_to_le32(BMI_EXECUTE);
+	cmd.execute.addr  = __cpu_to_le32(address);
+	cmd.execute.param = __cpu_to_le32(*param);
+
+	ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);
+	if (ret) {
+		ath10k_warn("unable to read from the device\n");
+		return ret;
+	}
+
+	if (resplen < sizeof(resp.execute)) {
+		ath10k_warn("invalid execute response length (%d)\n",
+			    resplen);
+		return ret;
+	}
+
+	*param = __le32_to_cpu(resp.execute.result);
+	return 0;
+}
+
+int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length)
+{
+	struct bmi_cmd cmd;
+	u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.lz_data);
+	u32 txlen;
+	int ret;
+
+	if (ar->bmi.done_sent) {
+		ath10k_warn("command disallowed\n");
+		return -EBUSY;
+	}
+
+	while (length) {
+		txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen);
+
+		WARN_ON_ONCE(txlen & 3);
+
+		cmd.id          = __cpu_to_le32(BMI_LZ_DATA);
+		cmd.lz_data.len = __cpu_to_le32(txlen);
+		memcpy(cmd.lz_data.payload, buffer, txlen);
+
+		ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,
+						  NULL, NULL);
+		if (ret) {
+			ath10k_warn("unable to write to the device\n");
+			return ret;
+		}
+
+		buffer += txlen;
+		length -= txlen;
+	}
+
+	return 0;
+}
+
+int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address)
+{
+	struct bmi_cmd cmd;
+	u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.lz_start);
+	int ret;
+
+	if (ar->bmi.done_sent) {
+		ath10k_warn("command disallowed\n");
+		return -EBUSY;
+	}
+
+	cmd.id            = __cpu_to_le32(BMI_LZ_STREAM_START);
+	cmd.lz_start.addr = __cpu_to_le32(address);
+
+	ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
+	if (ret) {
+		ath10k_warn("unable to Start LZ Stream to the device\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+int ath10k_bmi_fast_download(struct ath10k *ar,
+			     u32 address, const void *buffer, u32 length)
+{
+	u8 trailer[4] = {};
+	u32 head_len = rounddown(length, 4);
+	u32 trailer_len = length - head_len;
+	int ret;
+
+	ret = ath10k_bmi_lz_stream_start(ar, address);
+	if (ret)
+		return ret;
+
+	/* copy the last word into a zero padded buffer */
+	if (trailer_len > 0)
+		memcpy(trailer, buffer + head_len, trailer_len);
+
+	ret = ath10k_bmi_lz_data(ar, buffer, head_len);
+	if (ret)
+		return ret;
+
+	if (trailer_len > 0)
+		ret = ath10k_bmi_lz_data(ar, trailer, 4);
+
+	if (ret != 0)
+		return ret;
+
+	/*
+	 * Close compressed stream and open a new (fake) one.
+	 * This serves mainly to flush Target caches.
+	 */
+	ret = ath10k_bmi_lz_stream_start(ar, 0x00);
+
+	return ret;
+}


^ permalink raw reply related

* [PATCH v2 02/29] ath10k: add bmi.h
From: Kalle Valo @ 2013-06-01 11:24 UTC (permalink / raw)
  To: linux-wireless; +Cc: ath10k-devel
In-Reply-To: <20130601111820.32443.46794.stgit@localhost6.localdomain6>

Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
---
 drivers/net/wireless/ath/ath10k/bmi.h |  224 +++++++++++++++++++++++++++++++++
 1 file changed, 224 insertions(+)
 create mode 100644 drivers/net/wireless/ath/ath10k/bmi.h

diff --git a/drivers/net/wireless/ath/ath10k/bmi.h b/drivers/net/wireless/ath/ath10k/bmi.h
new file mode 100644
index 0000000..32c56aa
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/bmi.h
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * 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 _BMI_H_
+#define _BMI_H_
+
+#include "core.h"
+
+/*
+ * Bootloader Messaging Interface (BMI)
+ *
+ * BMI is a very simple messaging interface used during initialization
+ * to read memory, write memory, execute code, and to define an
+ * application entry PC.
+ *
+ * It is used to download an application to QCA988x, to provide
+ * patches to code that is already resident on QCA988x, and generally
+ * to examine and modify state.  The Host has an opportunity to use
+ * BMI only once during bootup.  Once the Host issues a BMI_DONE
+ * command, this opportunity ends.
+ *
+ * The Host writes BMI requests to mailbox0, and reads BMI responses
+ * from mailbox0.   BMI requests all begin with a command
+ * (see below for specific commands), and are followed by
+ * command-specific data.
+ *
+ * Flow control:
+ * The Host can only issue a command once the Target gives it a
+ * "BMI Command Credit", using AR8K Counter #4.  As soon as the
+ * Target has completed a command, it issues another BMI Command
+ * Credit (so the Host can issue the next command).
+ *
+ * BMI handles all required Target-side cache flushing.
+ */
+
+/* Maximum data size used for BMI transfers */
+#define BMI_MAX_DATA_SIZE	256
+
+/* len = cmd + addr + length */
+#define BMI_MAX_CMDBUF_SIZE (BMI_MAX_DATA_SIZE + \
+			sizeof(u32) + \
+			sizeof(u32) + \
+			sizeof(u32))
+
+/* BMI Commands */
+
+enum bmi_cmd_id {
+	BMI_NO_COMMAND          = 0,
+	BMI_DONE                = 1,
+	BMI_READ_MEMORY         = 2,
+	BMI_WRITE_MEMORY        = 3,
+	BMI_EXECUTE             = 4,
+	BMI_SET_APP_START       = 5,
+	BMI_READ_SOC_REGISTER   = 6,
+	BMI_READ_SOC_WORD       = 6,
+	BMI_WRITE_SOC_REGISTER  = 7,
+	BMI_WRITE_SOC_WORD      = 7,
+	BMI_GET_TARGET_ID       = 8,
+	BMI_GET_TARGET_INFO     = 8,
+	BMI_ROMPATCH_INSTALL    = 9,
+	BMI_ROMPATCH_UNINSTALL  = 10,
+	BMI_ROMPATCH_ACTIVATE   = 11,
+	BMI_ROMPATCH_DEACTIVATE = 12,
+	BMI_LZ_STREAM_START     = 13, /* should be followed by LZ_DATA */
+	BMI_LZ_DATA             = 14,
+	BMI_NVRAM_PROCESS       = 15,
+};
+
+#define BMI_NVRAM_SEG_NAME_SZ 16
+
+struct bmi_cmd {
+	__le32 id; /* enum bmi_cmd_id */
+	union {
+		struct {
+		} done;
+		struct {
+			__le32 addr;
+			__le32 len;
+		} read_mem;
+		struct {
+			__le32 addr;
+			__le32 len;
+			u8 payload[0];
+		} write_mem;
+		struct {
+			__le32 addr;
+			__le32 param;
+		} execute;
+		struct {
+			__le32 addr;
+		} set_app_start;
+		struct {
+			__le32 addr;
+		} read_soc_reg;
+		struct {
+			__le32 addr;
+			__le32 value;
+		} write_soc_reg;
+		struct {
+		} get_target_info;
+		struct {
+			__le32 rom_addr;
+			__le32 ram_addr; /* or value */
+			__le32 size;
+			__le32 activate; /* 0=install, but dont activate */
+		} rompatch_install;
+		struct {
+			__le32 patch_id;
+		} rompatch_uninstall;
+		struct {
+			__le32 count;
+			__le32 patch_ids[0]; /* length of @count */
+		} rompatch_activate;
+		struct {
+			__le32 count;
+			__le32 patch_ids[0]; /* length of @count */
+		} rompatch_deactivate;
+		struct {
+			__le32 addr;
+		} lz_start;
+		struct {
+			__le32 len; /* max BMI_MAX_DATA_SIZE */
+			u8 payload[0]; /* length of @len */
+		} lz_data;
+		struct {
+			u8 name[BMI_NVRAM_SEG_NAME_SZ];
+		} nvram_process;
+		u8 payload[BMI_MAX_CMDBUF_SIZE];
+	};
+} __packed;
+
+union bmi_resp {
+	struct {
+		u8 payload[0];
+	} read_mem;
+	struct {
+		__le32 result;
+	} execute;
+	struct {
+		__le32 value;
+	} read_soc_reg;
+	struct {
+		__le32 len;
+		__le32 version;
+		__le32 type;
+	} get_target_info;
+	struct {
+		__le32 patch_id;
+	} rompatch_install;
+	struct {
+		__le32 patch_id;
+	} rompatch_uninstall;
+	struct {
+		/* 0 = nothing executed
+		 * otherwise = NVRAM segment return value */
+		__le32 result;
+	} nvram_process;
+	u8 payload[BMI_MAX_CMDBUF_SIZE];
+} __packed;
+
+struct bmi_target_info {
+	u32 version;
+	u32 type;
+};
+
+
+/* in msec */
+#define BMI_COMMUNICATION_TIMEOUT_HZ (1*HZ)
+
+#define BMI_CE_NUM_TO_TARG 0
+#define BMI_CE_NUM_TO_HOST 1
+
+int ath10k_bmi_done(struct ath10k *ar);
+int ath10k_bmi_get_target_info(struct ath10k *ar,
+			       struct bmi_target_info *target_info);
+int ath10k_bmi_read_memory(struct ath10k *ar, u32 address,
+			   void *buffer, u32 length);
+int ath10k_bmi_write_memory(struct ath10k *ar, u32 address,
+			    const void *buffer, u32 length);
+
+#define ath10k_bmi_read32(ar, item, val)				\
+	({								\
+		int ret;						\
+		u32 addr;						\
+		__le32 tmp;						\
+									\
+		addr = host_interest_item_address(HI_ITEM(item));	\
+		ret = ath10k_bmi_read_memory(ar, addr, (u8 *)&tmp, 4); \
+		*val = __le32_to_cpu(tmp);				\
+		ret;							\
+	 })
+
+#define ath10k_bmi_write32(ar, item, val)				\
+	({								\
+		int ret;						\
+		u32 address;						\
+		__le32 v = __cpu_to_le32(val);				\
+									\
+		address = host_interest_item_address(HI_ITEM(item));	\
+		ret = ath10k_bmi_write_memory(ar, address,		\
+					      (u8 *)&v, sizeof(v));	\
+		ret;							\
+	})
+
+int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param);
+int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address);
+int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length);
+int ath10k_bmi_fast_download(struct ath10k *ar, u32 address,
+			     const void *buffer, u32 length);
+#endif /* _BMI_H_ */


^ permalink raw reply related

* [PATCH v2 03/29] ath10k: add ce.c
From: Kalle Valo @ 2013-06-01 11:24 UTC (permalink / raw)
  To: linux-wireless; +Cc: ath10k-devel
In-Reply-To: <20130601111820.32443.46794.stgit@localhost6.localdomain6>

Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
---
 drivers/net/wireless/ath/ath10k/ce.c | 1189 ++++++++++++++++++++++++++++++++++
 1 file changed, 1189 insertions(+)
 create mode 100644 drivers/net/wireless/ath/ath10k/ce.c

diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c
new file mode 100644
index 0000000..61a8ac7
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/ce.c
@@ -0,0 +1,1189 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * 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 "hif.h"
+#include "pci.h"
+#include "ce.h"
+#include "debug.h"
+
+/*
+ * Support for Copy Engine hardware, which is mainly used for
+ * communication between Host and Target over a PCIe interconnect.
+ */
+
+/*
+ * A single CopyEngine (CE) comprises two "rings":
+ *   a source ring
+ *   a destination ring
+ *
+ * Each ring consists of a number of descriptors which specify
+ * an address, length, and meta-data.
+ *
+ * Typically, one side of the PCIe interconnect (Host or Target)
+ * controls one ring and the other side controls the other ring.
+ * The source side chooses when to initiate a transfer and it
+ * chooses what to send (buffer address, length). The destination
+ * side keeps a supply of "anonymous receive buffers" available and
+ * it handles incoming data as it arrives (when the destination
+ * recieves an interrupt).
+ *
+ * The sender may send a simple buffer (address/length) or it may
+ * send a small list of buffers.  When a small list is sent, hardware
+ * "gathers" these and they end up in a single destination buffer
+ * with a single interrupt.
+ *
+ * There are several "contexts" managed by this layer -- more, it
+ * may seem -- than should be needed. These are provided mainly for
+ * maximum flexibility and especially to facilitate a simpler HIF
+ * implementation. There are per-CopyEngine recv, send, and watermark
+ * contexts. These are supplied by the caller when a recv, send,
+ * or watermark handler is established and they are echoed back to
+ * the caller when the respective callbacks are invoked. There is
+ * also a per-transfer context supplied by the caller when a buffer
+ * (or sendlist) is sent and when a buffer is enqueued for recv.
+ * These per-transfer contexts are echoed back to the caller when
+ * the buffer is sent/received.
+ */
+
+static inline void ath10k_ce_dest_ring_write_index_set(struct ath10k *ar,
+						       u32 ce_ctrl_addr,
+						       unsigned int n)
+{
+	ath10k_pci_write32(ar, ce_ctrl_addr + DST_WR_INDEX_ADDRESS, n);
+}
+
+static inline u32 ath10k_ce_dest_ring_write_index_get(struct ath10k *ar,
+						      u32 ce_ctrl_addr)
+{
+	return ath10k_pci_read32(ar, ce_ctrl_addr + DST_WR_INDEX_ADDRESS);
+}
+
+static inline void ath10k_ce_src_ring_write_index_set(struct ath10k *ar,
+						      u32 ce_ctrl_addr,
+						      unsigned int n)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	void __iomem *indicator_addr;
+
+	if (!test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features)) {
+		ath10k_pci_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n);
+		return;
+	}
+
+	/* workaround for QCA988x_1.0 HW CE */
+	indicator_addr = ar_pci->mem + ce_ctrl_addr + DST_WATERMARK_ADDRESS;
+
+	if (ce_ctrl_addr == ath10k_ce_base_address(CDC_WAR_DATA_CE)) {
+		iowrite32((CDC_WAR_MAGIC_STR | n), indicator_addr);
+	} else {
+		unsigned long irq_flags;
+		local_irq_save(irq_flags);
+		iowrite32(1, indicator_addr);
+
+		/*
+		 * PCIE write waits for ACK in IPQ8K, there is no
+		 * need to read back value.
+		 */
+		(void)ioread32(indicator_addr);
+		(void)ioread32(indicator_addr); /* conservative */
+
+		ath10k_pci_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n);
+
+		iowrite32(0, indicator_addr);
+		local_irq_restore(irq_flags);
+	}
+}
+
+static inline u32 ath10k_ce_src_ring_write_index_get(struct ath10k *ar,
+						     u32 ce_ctrl_addr)
+{
+	return ath10k_pci_read32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS);
+}
+
+static inline u32 ath10k_ce_src_ring_read_index_get(struct ath10k *ar,
+						    u32 ce_ctrl_addr)
+{
+	return ath10k_pci_read32(ar, ce_ctrl_addr + CURRENT_SRRI_ADDRESS);
+}
+
+static inline void ath10k_ce_src_ring_base_addr_set(struct ath10k *ar,
+						    u32 ce_ctrl_addr,
+						    unsigned int addr)
+{
+	ath10k_pci_write32(ar, ce_ctrl_addr + SR_BA_ADDRESS, addr);
+}
+
+static inline void ath10k_ce_src_ring_size_set(struct ath10k *ar,
+					       u32 ce_ctrl_addr,
+					       unsigned int n)
+{
+	ath10k_pci_write32(ar, ce_ctrl_addr + SR_SIZE_ADDRESS, n);
+}
+
+static inline void ath10k_ce_src_ring_dmax_set(struct ath10k *ar,
+					       u32 ce_ctrl_addr,
+					       unsigned int n)
+{
+	u32 ctrl1_addr = ath10k_pci_read32((ar),
+					   (ce_ctrl_addr) + CE_CTRL1_ADDRESS);
+
+	ath10k_pci_write32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS,
+			   (ctrl1_addr &  ~CE_CTRL1_DMAX_LENGTH_MASK) |
+			   CE_CTRL1_DMAX_LENGTH_SET(n));
+}
+
+static inline void ath10k_ce_src_ring_byte_swap_set(struct ath10k *ar,
+						    u32 ce_ctrl_addr,
+						    unsigned int n)
+{
+	u32 ctrl1_addr = ath10k_pci_read32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS);
+
+	ath10k_pci_write32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS,
+			   (ctrl1_addr & ~CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK) |
+			   CE_CTRL1_SRC_RING_BYTE_SWAP_EN_SET(n));
+}
+
+static inline void ath10k_ce_dest_ring_byte_swap_set(struct ath10k *ar,
+						     u32 ce_ctrl_addr,
+						     unsigned int n)
+{
+	u32 ctrl1_addr = ath10k_pci_read32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS);
+
+	ath10k_pci_write32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS,
+			   (ctrl1_addr & ~CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK) |
+			   CE_CTRL1_DST_RING_BYTE_SWAP_EN_SET(n));
+}
+
+static inline u32 ath10k_ce_dest_ring_read_index_get(struct ath10k *ar,
+						     u32 ce_ctrl_addr)
+{
+	return ath10k_pci_read32(ar, ce_ctrl_addr + CURRENT_DRRI_ADDRESS);
+}
+
+static inline void ath10k_ce_dest_ring_base_addr_set(struct ath10k *ar,
+						     u32 ce_ctrl_addr,
+						     u32 addr)
+{
+	ath10k_pci_write32(ar, ce_ctrl_addr + DR_BA_ADDRESS, addr);
+}
+
+static inline void ath10k_ce_dest_ring_size_set(struct ath10k *ar,
+						u32 ce_ctrl_addr,
+						unsigned int n)
+{
+	ath10k_pci_write32(ar, ce_ctrl_addr + DR_SIZE_ADDRESS, n);
+}
+
+static inline void ath10k_ce_src_ring_highmark_set(struct ath10k *ar,
+						   u32 ce_ctrl_addr,
+						   unsigned int n)
+{
+	u32 addr = ath10k_pci_read32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS);
+
+	ath10k_pci_write32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS,
+			   (addr & ~SRC_WATERMARK_HIGH_MASK) |
+			   SRC_WATERMARK_HIGH_SET(n));
+}
+
+static inline void ath10k_ce_src_ring_lowmark_set(struct ath10k *ar,
+						  u32 ce_ctrl_addr,
+						  unsigned int n)
+{
+	u32 addr = ath10k_pci_read32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS);
+
+	ath10k_pci_write32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS,
+			   (addr & ~SRC_WATERMARK_LOW_MASK) |
+			   SRC_WATERMARK_LOW_SET(n));
+}
+
+static inline void ath10k_ce_dest_ring_highmark_set(struct ath10k *ar,
+						    u32 ce_ctrl_addr,
+						    unsigned int n)
+{
+	u32 addr = ath10k_pci_read32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS);
+
+	ath10k_pci_write32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS,
+			   (addr & ~DST_WATERMARK_HIGH_MASK) |
+			   DST_WATERMARK_HIGH_SET(n));
+}
+
+static inline void ath10k_ce_dest_ring_lowmark_set(struct ath10k *ar,
+						   u32 ce_ctrl_addr,
+						   unsigned int n)
+{
+	u32 addr = ath10k_pci_read32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS);
+
+	ath10k_pci_write32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS,
+			   (addr & ~DST_WATERMARK_LOW_MASK) |
+			   DST_WATERMARK_LOW_SET(n));
+}
+
+static inline void ath10k_ce_copy_complete_inter_enable(struct ath10k *ar,
+							u32 ce_ctrl_addr)
+{
+	u32 host_ie_addr = ath10k_pci_read32(ar,
+					     ce_ctrl_addr + HOST_IE_ADDRESS);
+
+	ath10k_pci_write32(ar, ce_ctrl_addr + HOST_IE_ADDRESS,
+			   host_ie_addr | HOST_IE_COPY_COMPLETE_MASK);
+}
+
+static inline void ath10k_ce_copy_complete_intr_disable(struct ath10k *ar,
+							u32 ce_ctrl_addr)
+{
+	u32 host_ie_addr = ath10k_pci_read32(ar,
+					     ce_ctrl_addr + HOST_IE_ADDRESS);
+
+	ath10k_pci_write32(ar, ce_ctrl_addr + HOST_IE_ADDRESS,
+			   host_ie_addr & ~HOST_IE_COPY_COMPLETE_MASK);
+}
+
+static inline void ath10k_ce_watermark_intr_disable(struct ath10k *ar,
+						    u32 ce_ctrl_addr)
+{
+	u32 host_ie_addr = ath10k_pci_read32(ar,
+					     ce_ctrl_addr + HOST_IE_ADDRESS);
+
+	ath10k_pci_write32(ar, ce_ctrl_addr + HOST_IE_ADDRESS,
+			   host_ie_addr & ~CE_WATERMARK_MASK);
+}
+
+static inline void ath10k_ce_error_intr_enable(struct ath10k *ar,
+					       u32 ce_ctrl_addr)
+{
+	u32 misc_ie_addr = ath10k_pci_read32(ar,
+					     ce_ctrl_addr + MISC_IE_ADDRESS);
+
+	ath10k_pci_write32(ar, ce_ctrl_addr + MISC_IE_ADDRESS,
+			   misc_ie_addr | CE_ERROR_MASK);
+}
+
+static inline void ath10k_ce_engine_int_status_clear(struct ath10k *ar,
+						     u32 ce_ctrl_addr,
+						     unsigned int mask)
+{
+	ath10k_pci_write32(ar, ce_ctrl_addr + HOST_IS_ADDRESS, mask);
+}
+
+
+/*
+ * Guts of ath10k_ce_send, used by both ath10k_ce_send and
+ * ath10k_ce_sendlist_send.
+ * The caller takes responsibility for any needed locking.
+ */
+static int ath10k_ce_send_nolock(struct ce_state *ce_state,
+				 void *per_transfer_context,
+				 u32 buffer,
+				 unsigned int nbytes,
+				 unsigned int transfer_id,
+				 unsigned int flags)
+{
+	struct ath10k *ar = ce_state->ar;
+	struct ce_ring_state *src_ring = ce_state->src_ring;
+	struct ce_desc *desc, *sdesc;
+	unsigned int nentries_mask = src_ring->nentries_mask;
+	unsigned int sw_index = src_ring->sw_index;
+	unsigned int write_index = src_ring->write_index;
+	u32 ctrl_addr = ce_state->ctrl_addr;
+	u32 desc_flags = 0;
+	int ret = 0;
+
+	if (nbytes > ce_state->src_sz_max)
+		ath10k_warn("%s: send more we can (nbytes: %d, max: %d)\n",
+			    __func__, nbytes, ce_state->src_sz_max);
+
+	ath10k_pci_wake(ar);
+
+	if (unlikely(CE_RING_DELTA(nentries_mask,
+				   write_index, sw_index - 1) <= 0)) {
+		ret = -EIO;
+		goto exit;
+	}
+
+	desc = CE_SRC_RING_TO_DESC(src_ring->base_addr_owner_space,
+				   write_index);
+	sdesc = CE_SRC_RING_TO_DESC(src_ring->shadow_base, write_index);
+
+	desc_flags |= SM(transfer_id, CE_DESC_FLAGS_META_DATA);
+
+	if (flags & CE_SEND_FLAG_GATHER)
+		desc_flags |= CE_DESC_FLAGS_GATHER;
+	if (flags & CE_SEND_FLAG_BYTE_SWAP)
+		desc_flags |= CE_DESC_FLAGS_BYTE_SWAP;
+
+	sdesc->addr   = __cpu_to_le32(buffer);
+	sdesc->nbytes = __cpu_to_le16(nbytes);
+	sdesc->flags  = __cpu_to_le16(desc_flags);
+
+	*desc = *sdesc;
+
+	src_ring->per_transfer_context[write_index] = per_transfer_context;
+
+	/* Update Source Ring Write Index */
+	write_index = CE_RING_IDX_INCR(nentries_mask, write_index);
+
+	/* WORKAROUND */
+	if (!(flags & CE_SEND_FLAG_GATHER))
+		ath10k_ce_src_ring_write_index_set(ar, ctrl_addr, write_index);
+
+	src_ring->write_index = write_index;
+exit:
+	ath10k_pci_sleep(ar);
+	return ret;
+}
+
+int ath10k_ce_send(struct ce_state *ce_state,
+		   void *per_transfer_context,
+		   u32 buffer,
+		   unsigned int nbytes,
+		   unsigned int transfer_id,
+		   unsigned int flags)
+{
+	struct ath10k *ar = ce_state->ar;
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	int ret;
+
+	spin_lock_bh(&ar_pci->ce_lock);
+	ret = ath10k_ce_send_nolock(ce_state, per_transfer_context,
+				    buffer, nbytes, transfer_id, flags);
+	spin_unlock_bh(&ar_pci->ce_lock);
+
+	return ret;
+}
+
+void ath10k_ce_sendlist_buf_add(struct ce_sendlist *sendlist, u32 buffer,
+				unsigned int nbytes, u32 flags)
+{
+	unsigned int num_items = sendlist->num_items;
+	struct ce_sendlist_item *item;
+
+	item = &sendlist->item[num_items];
+	item->data = buffer;
+	item->u.nbytes = nbytes;
+	item->flags = flags;
+	sendlist->num_items++;
+}
+
+int ath10k_ce_sendlist_send(struct ce_state *ce_state,
+			    void *per_transfer_context,
+			    struct ce_sendlist *sendlist,
+			    unsigned int transfer_id)
+{
+	struct ce_ring_state *src_ring = ce_state->src_ring;
+	struct ce_sendlist_item *item;
+	struct ath10k *ar = ce_state->ar;
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	unsigned int nentries_mask = src_ring->nentries_mask;
+	unsigned int num_items = sendlist->num_items;
+	unsigned int sw_index;
+	unsigned int write_index;
+	int i, delta, ret = -ENOMEM;
+
+	spin_lock_bh(&ar_pci->ce_lock);
+
+	sw_index = src_ring->sw_index;
+	write_index = src_ring->write_index;
+
+	delta = CE_RING_DELTA(nentries_mask, write_index, sw_index - 1);
+
+	if (delta >= num_items) {
+		/*
+		 * Handle all but the last item uniformly.
+		 */
+		for (i = 0; i < num_items - 1; i++) {
+			item = &sendlist->item[i];
+			ret = ath10k_ce_send_nolock(ce_state,
+						    CE_SENDLIST_ITEM_CTXT,
+						    (u32) item->data,
+						    item->u.nbytes, transfer_id,
+						    item->flags |
+						    CE_SEND_FLAG_GATHER);
+			if (ret)
+				ath10k_warn("CE send failed for item: %d\n", i);
+		}
+		/*
+		 * Provide valid context pointer for final item.
+		 */
+		item = &sendlist->item[i];
+		ret = ath10k_ce_send_nolock(ce_state, per_transfer_context,
+					    (u32) item->data, item->u.nbytes,
+					    transfer_id, item->flags);
+		if (ret)
+			ath10k_warn("CE send failed for last item: %d\n", i);
+	}
+
+	spin_unlock_bh(&ar_pci->ce_lock);
+
+	return ret;
+}
+
+int ath10k_ce_recv_buf_enqueue(struct ce_state *ce_state,
+			       void *per_recv_context,
+			       u32 buffer)
+{
+	struct ce_ring_state *dest_ring = ce_state->dest_ring;
+	u32 ctrl_addr = ce_state->ctrl_addr;
+	struct ath10k *ar = ce_state->ar;
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	unsigned int nentries_mask = dest_ring->nentries_mask;
+	unsigned int write_index;
+	unsigned int sw_index;
+	int ret;
+
+	spin_lock_bh(&ar_pci->ce_lock);
+	write_index = dest_ring->write_index;
+	sw_index = dest_ring->sw_index;
+
+	ath10k_pci_wake(ar);
+
+	if (CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) > 0) {
+		struct ce_desc *base = dest_ring->base_addr_owner_space;
+		struct ce_desc *desc = CE_DEST_RING_TO_DESC(base, write_index);
+
+		/* Update destination descriptor */
+		desc->addr    = __cpu_to_le32(buffer);
+		desc->nbytes = 0;
+
+		dest_ring->per_transfer_context[write_index] =
+							per_recv_context;
+
+		/* Update Destination Ring Write Index */
+		write_index = CE_RING_IDX_INCR(nentries_mask, write_index);
+		ath10k_ce_dest_ring_write_index_set(ar, ctrl_addr, write_index);
+		dest_ring->write_index = write_index;
+		ret = 0;
+	} else {
+		ret = -EIO;
+	}
+	ath10k_pci_sleep(ar);
+	spin_unlock_bh(&ar_pci->ce_lock);
+
+	return ret;
+}
+
+/*
+ * Guts of ath10k_ce_completed_recv_next.
+ * The caller takes responsibility for any necessary locking.
+ */
+static int ath10k_ce_completed_recv_next_nolock(struct ce_state *ce_state,
+						void **per_transfer_contextp,
+						u32 *bufferp,
+						unsigned int *nbytesp,
+						unsigned int *transfer_idp,
+						unsigned int *flagsp)
+{
+	struct ce_ring_state *dest_ring = ce_state->dest_ring;
+	unsigned int nentries_mask = dest_ring->nentries_mask;
+	unsigned int sw_index = dest_ring->sw_index;
+
+	struct ce_desc *base = dest_ring->base_addr_owner_space;
+	struct ce_desc *desc = CE_DEST_RING_TO_DESC(base, sw_index);
+	struct ce_desc sdesc;
+	u16 nbytes;
+
+	/* Copy in one go for performance reasons */
+	sdesc = *desc;
+
+	nbytes = __le16_to_cpu(sdesc.nbytes);
+	if (nbytes == 0) {
+		/*
+		 * This closes a relatively unusual race where the Host
+		 * sees the updated DRRI before the update to the
+		 * corresponding descriptor has completed. We treat this
+		 * as a descriptor that is not yet done.
+		 */
+		return -EIO;
+	}
+
+	desc->nbytes = 0;
+
+	/* Return data from completed destination descriptor */
+	*bufferp = __le32_to_cpu(sdesc.addr);
+	*nbytesp = nbytes;
+	*transfer_idp = MS(__le16_to_cpu(sdesc.flags), CE_DESC_FLAGS_META_DATA);
+
+	if (__le16_to_cpu(sdesc.flags) & CE_DESC_FLAGS_BYTE_SWAP)
+		*flagsp = CE_RECV_FLAG_SWAPPED;
+	else
+		*flagsp = 0;
+
+	if (per_transfer_contextp)
+		*per_transfer_contextp =
+			dest_ring->per_transfer_context[sw_index];
+
+	/* sanity */
+	dest_ring->per_transfer_context[sw_index] = NULL;
+
+	/* Update sw_index */
+	sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index);
+	dest_ring->sw_index = sw_index;
+
+	return 0;
+}
+
+int ath10k_ce_completed_recv_next(struct ce_state *ce_state,
+				  void **per_transfer_contextp,
+				  u32 *bufferp,
+				  unsigned int *nbytesp,
+				  unsigned int *transfer_idp,
+				  unsigned int *flagsp)
+{
+	struct ath10k *ar = ce_state->ar;
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	int ret;
+
+	spin_lock_bh(&ar_pci->ce_lock);
+	ret = ath10k_ce_completed_recv_next_nolock(ce_state,
+						   per_transfer_contextp,
+						   bufferp, nbytesp,
+						   transfer_idp, flagsp);
+	spin_unlock_bh(&ar_pci->ce_lock);
+
+	return ret;
+}
+
+int ath10k_ce_revoke_recv_next(struct ce_state *ce_state,
+			       void **per_transfer_contextp,
+			       u32 *bufferp)
+{
+	struct ce_ring_state *dest_ring;
+	unsigned int nentries_mask;
+	unsigned int sw_index;
+	unsigned int write_index;
+	int ret;
+	struct ath10k *ar;
+	struct ath10k_pci *ar_pci;
+
+	dest_ring = ce_state->dest_ring;
+
+	if (!dest_ring)
+		return -EIO;
+
+	ar = ce_state->ar;
+	ar_pci = ath10k_pci_priv(ar);
+
+	spin_lock_bh(&ar_pci->ce_lock);
+
+	nentries_mask = dest_ring->nentries_mask;
+	sw_index = dest_ring->sw_index;
+	write_index = dest_ring->write_index;
+	if (write_index != sw_index) {
+		struct ce_desc *base = dest_ring->base_addr_owner_space;
+		struct ce_desc *desc = CE_DEST_RING_TO_DESC(base, sw_index);
+
+		/* Return data from completed destination descriptor */
+		*bufferp = __le32_to_cpu(desc->addr);
+
+		if (per_transfer_contextp)
+			*per_transfer_contextp =
+				dest_ring->per_transfer_context[sw_index];
+
+		/* sanity */
+		dest_ring->per_transfer_context[sw_index] = NULL;
+
+		/* Update sw_index */
+		sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index);
+		dest_ring->sw_index = sw_index;
+		ret = 0;
+	} else {
+		ret = -EIO;
+	}
+
+	spin_unlock_bh(&ar_pci->ce_lock);
+
+	return ret;
+}
+
+/*
+ * Guts of ath10k_ce_completed_send_next.
+ * The caller takes responsibility for any necessary locking.
+ */
+static int ath10k_ce_completed_send_next_nolock(struct ce_state *ce_state,
+						void **per_transfer_contextp,
+						u32 *bufferp,
+						unsigned int *nbytesp,
+						unsigned int *transfer_idp)
+{
+	struct ce_ring_state *src_ring = ce_state->src_ring;
+	u32 ctrl_addr = ce_state->ctrl_addr;
+	struct ath10k *ar = ce_state->ar;
+	unsigned int nentries_mask = src_ring->nentries_mask;
+	unsigned int sw_index = src_ring->sw_index;
+	unsigned int read_index;
+	int ret = -EIO;
+
+	if (src_ring->hw_index == sw_index) {
+		/*
+		 * The SW completion index has caught up with the cached
+		 * version of the HW completion index.
+		 * Update the cached HW completion index to see whether
+		 * the SW has really caught up to the HW, or if the cached
+		 * value of the HW index has become stale.
+		 */
+		ath10k_pci_wake(ar);
+		src_ring->hw_index =
+			ath10k_ce_src_ring_read_index_get(ar, ctrl_addr);
+		ath10k_pci_sleep(ar);
+	}
+	read_index = src_ring->hw_index;
+
+	if ((read_index != sw_index) && (read_index != 0xffffffff)) {
+		struct ce_desc *sbase = src_ring->shadow_base;
+		struct ce_desc *sdesc = CE_SRC_RING_TO_DESC(sbase, sw_index);
+
+		/* Return data from completed source descriptor */
+		*bufferp = __le32_to_cpu(sdesc->addr);
+		*nbytesp = __le16_to_cpu(sdesc->nbytes);
+		*transfer_idp = MS(__le16_to_cpu(sdesc->flags),
+						CE_DESC_FLAGS_META_DATA);
+
+		if (per_transfer_contextp)
+			*per_transfer_contextp =
+				src_ring->per_transfer_context[sw_index];
+
+		/* sanity */
+		src_ring->per_transfer_context[sw_index] = NULL;
+
+		/* Update sw_index */
+		sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index);
+		src_ring->sw_index = sw_index;
+		ret = 0;
+	}
+
+	return ret;
+}
+
+/* NB: Modeled after ath10k_ce_completed_send_next */
+int ath10k_ce_cancel_send_next(struct ce_state *ce_state,
+			       void **per_transfer_contextp,
+			       u32 *bufferp,
+			       unsigned int *nbytesp,
+			       unsigned int *transfer_idp)
+{
+	struct ce_ring_state *src_ring;
+	unsigned int nentries_mask;
+	unsigned int sw_index;
+	unsigned int write_index;
+	int ret;
+	struct ath10k *ar;
+	struct ath10k_pci *ar_pci;
+
+	src_ring = ce_state->src_ring;
+
+	if (!src_ring)
+		return -EIO;
+
+	ar = ce_state->ar;
+	ar_pci = ath10k_pci_priv(ar);
+
+	spin_lock_bh(&ar_pci->ce_lock);
+
+	nentries_mask = src_ring->nentries_mask;
+	sw_index = src_ring->sw_index;
+	write_index = src_ring->write_index;
+
+	if (write_index != sw_index) {
+		struct ce_desc *base = src_ring->base_addr_owner_space;
+		struct ce_desc *desc = CE_SRC_RING_TO_DESC(base, sw_index);
+
+		/* Return data from completed source descriptor */
+		*bufferp = __le32_to_cpu(desc->addr);
+		*nbytesp = __le16_to_cpu(desc->nbytes);
+		*transfer_idp = MS(__le16_to_cpu(desc->flags),
+						CE_DESC_FLAGS_META_DATA);
+
+		if (per_transfer_contextp)
+			*per_transfer_contextp =
+				src_ring->per_transfer_context[sw_index];
+
+		/* sanity */
+		src_ring->per_transfer_context[sw_index] = NULL;
+
+		/* Update sw_index */
+		sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index);
+		src_ring->sw_index = sw_index;
+		ret = 0;
+	} else {
+		ret = -EIO;
+	}
+
+	spin_unlock_bh(&ar_pci->ce_lock);
+
+	return ret;
+}
+
+int ath10k_ce_completed_send_next(struct ce_state *ce_state,
+				  void **per_transfer_contextp,
+				  u32 *bufferp,
+				  unsigned int *nbytesp,
+				  unsigned int *transfer_idp)
+{
+	struct ath10k *ar = ce_state->ar;
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	int ret;
+
+	spin_lock_bh(&ar_pci->ce_lock);
+	ret = ath10k_ce_completed_send_next_nolock(ce_state,
+						   per_transfer_contextp,
+						   bufferp, nbytesp,
+						   transfer_idp);
+	spin_unlock_bh(&ar_pci->ce_lock);
+
+	return ret;
+}
+
+/*
+ * Guts of interrupt handler for per-engine interrupts on a particular CE.
+ *
+ * Invokes registered callbacks for recv_complete,
+ * send_complete, and watermarks.
+ */
+void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	struct ce_state *ce_state = ar_pci->ce_id_to_state[ce_id];
+	u32 ctrl_addr = ce_state->ctrl_addr;
+	void *transfer_context;
+	u32 buf;
+	unsigned int nbytes;
+	unsigned int id;
+	unsigned int flags;
+
+	ath10k_pci_wake(ar);
+	spin_lock_bh(&ar_pci->ce_lock);
+
+	/* Clear the copy-complete interrupts that will be handled here. */
+	ath10k_ce_engine_int_status_clear(ar, ctrl_addr,
+					  HOST_IS_COPY_COMPLETE_MASK);
+
+	if (ce_state->recv_cb) {
+		/*
+		 * Pop completed recv buffers and call the registered
+		 * recv callback for each
+		 */
+		while (ath10k_ce_completed_recv_next_nolock(ce_state,
+							    &transfer_context,
+							    &buf, &nbytes,
+							    &id, &flags) == 0) {
+			spin_unlock_bh(&ar_pci->ce_lock);
+			ce_state->recv_cb(ce_state, transfer_context, buf,
+					  nbytes, id, flags);
+			spin_lock_bh(&ar_pci->ce_lock);
+		}
+	}
+
+	if (ce_state->send_cb) {
+		/*
+		 * Pop completed send buffers and call the registered
+		 * send callback for each
+		 */
+		while (ath10k_ce_completed_send_next_nolock(ce_state,
+							    &transfer_context,
+							    &buf,
+							    &nbytes,
+							    &id) == 0) {
+			spin_unlock_bh(&ar_pci->ce_lock);
+			ce_state->send_cb(ce_state, transfer_context,
+					  buf, nbytes, id);
+			spin_lock_bh(&ar_pci->ce_lock);
+		}
+	}
+
+	/*
+	 * Misc CE interrupts are not being handled, but still need
+	 * to be cleared.
+	 */
+	ath10k_ce_engine_int_status_clear(ar, ctrl_addr, CE_WATERMARK_MASK);
+
+	spin_unlock_bh(&ar_pci->ce_lock);
+	ath10k_pci_sleep(ar);
+}
+
+/*
+ * Handler for per-engine interrupts on ALL active CEs.
+ * This is used in cases where the system is sharing a
+ * single interrput for all CEs
+ */
+
+void ath10k_ce_per_engine_service_any(struct ath10k *ar)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	int ce_id;
+	u32 intr_summary;
+
+	ath10k_pci_wake(ar);
+	intr_summary = CE_INTERRUPT_SUMMARY(ar);
+
+	for (ce_id = 0; intr_summary && (ce_id < ar_pci->ce_count); ce_id++) {
+		if (intr_summary & (1 << ce_id))
+			intr_summary &= ~(1 << ce_id);
+		else
+			/* no intr pending on this CE */
+			continue;
+
+		ath10k_ce_per_engine_service(ar, ce_id);
+	}
+
+	ath10k_pci_sleep(ar);
+}
+
+/*
+ * Adjust interrupts for the copy complete handler.
+ * If it's needed for either send or recv, then unmask
+ * this interrupt; otherwise, mask it.
+ *
+ * Called with ce_lock held.
+ */
+static void ath10k_ce_per_engine_handler_adjust(struct ce_state *ce_state,
+						int disable_copy_compl_intr)
+{
+	u32 ctrl_addr = ce_state->ctrl_addr;
+	struct ath10k *ar = ce_state->ar;
+
+	ath10k_pci_wake(ar);
+
+	if ((!disable_copy_compl_intr) &&
+	    (ce_state->send_cb || ce_state->recv_cb))
+		ath10k_ce_copy_complete_inter_enable(ar, ctrl_addr);
+	else
+		ath10k_ce_copy_complete_intr_disable(ar, ctrl_addr);
+
+	ath10k_ce_watermark_intr_disable(ar, ctrl_addr);
+
+	ath10k_pci_sleep(ar);
+}
+
+void ath10k_ce_disable_interrupts(struct ath10k *ar)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	int ce_id;
+
+	ath10k_pci_wake(ar);
+	for (ce_id = 0; ce_id < ar_pci->ce_count; ce_id++) {
+		struct ce_state *ce_state = ar_pci->ce_id_to_state[ce_id];
+		u32 ctrl_addr = ce_state->ctrl_addr;
+
+		ath10k_ce_copy_complete_intr_disable(ar, ctrl_addr);
+	}
+	ath10k_pci_sleep(ar);
+}
+
+void ath10k_ce_send_cb_register(struct ce_state *ce_state,
+				void (*send_cb) (struct ce_state *ce_state,
+						 void *transfer_context,
+						 u32 buffer,
+						 unsigned int nbytes,
+						 unsigned int transfer_id),
+				int disable_interrupts)
+{
+	struct ath10k *ar = ce_state->ar;
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+	spin_lock_bh(&ar_pci->ce_lock);
+	ce_state->send_cb = send_cb;
+	ath10k_ce_per_engine_handler_adjust(ce_state, disable_interrupts);
+	spin_unlock_bh(&ar_pci->ce_lock);
+}
+
+void ath10k_ce_recv_cb_register(struct ce_state *ce_state,
+				void (*recv_cb) (struct ce_state *ce_state,
+						 void *transfer_context,
+						 u32 buffer,
+						 unsigned int nbytes,
+						 unsigned int transfer_id,
+						 unsigned int flags))
+{
+	struct ath10k *ar = ce_state->ar;
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+	spin_lock_bh(&ar_pci->ce_lock);
+	ce_state->recv_cb = recv_cb;
+	ath10k_ce_per_engine_handler_adjust(ce_state, 0);
+	spin_unlock_bh(&ar_pci->ce_lock);
+}
+
+static int ath10k_ce_init_src_ring(struct ath10k *ar,
+				   unsigned int ce_id,
+				   struct ce_state *ce_state,
+				   const struct ce_attr *attr)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	struct ce_ring_state *src_ring;
+	unsigned int nentries = attr->src_nentries;
+	unsigned int ce_nbytes;
+	u32 ctrl_addr = ath10k_ce_base_address(ce_id);
+	dma_addr_t base_addr;
+	char *ptr;
+
+	nentries = roundup_pow_of_two(nentries);
+
+	if (ce_state->src_ring) {
+		WARN_ON(ce_state->src_ring->nentries != nentries);
+		return 0;
+	}
+
+	ce_nbytes = sizeof(struct ce_ring_state) + (nentries * sizeof(void *));
+	ptr = kzalloc(ce_nbytes, GFP_KERNEL);
+	if (ptr == NULL)
+		return -ENOMEM;
+
+	ce_state->src_ring = (struct ce_ring_state *)ptr;
+	src_ring = ce_state->src_ring;
+
+	ptr += sizeof(struct ce_ring_state);
+	src_ring->nentries = nentries;
+	src_ring->nentries_mask = nentries - 1;
+
+	ath10k_pci_wake(ar);
+	src_ring->sw_index = ath10k_ce_src_ring_read_index_get(ar, ctrl_addr);
+	src_ring->hw_index = src_ring->sw_index;
+
+	src_ring->write_index =
+		ath10k_ce_src_ring_write_index_get(ar, ctrl_addr);
+	ath10k_pci_sleep(ar);
+
+	src_ring->per_transfer_context = (void **)ptr;
+
+	/*
+	 * Legacy platforms that do not support cache
+	 * coherent DMA are unsupported
+	 */
+	src_ring->base_addr_owner_space_unaligned =
+		pci_alloc_consistent(ar_pci->pdev,
+				     (nentries * sizeof(struct ce_desc) +
+				      CE_DESC_RING_ALIGN),
+				     &base_addr);
+	src_ring->base_addr_ce_space_unaligned = base_addr;
+
+	src_ring->base_addr_owner_space = PTR_ALIGN(
+			src_ring->base_addr_owner_space_unaligned,
+			CE_DESC_RING_ALIGN);
+	src_ring->base_addr_ce_space = ALIGN(
+			src_ring->base_addr_ce_space_unaligned,
+			CE_DESC_RING_ALIGN);
+
+	/*
+	 * Also allocate a shadow src ring in regular
+	 * mem to use for faster access.
+	 */
+	src_ring->shadow_base_unaligned =
+		kmalloc((nentries * sizeof(struct ce_desc) +
+			 CE_DESC_RING_ALIGN), GFP_KERNEL);
+
+	src_ring->shadow_base = PTR_ALIGN(
+			src_ring->shadow_base_unaligned,
+			CE_DESC_RING_ALIGN);
+
+	ath10k_pci_wake(ar);
+	ath10k_ce_src_ring_base_addr_set(ar, ctrl_addr,
+					 src_ring->base_addr_ce_space);
+	ath10k_ce_src_ring_size_set(ar, ctrl_addr, nentries);
+	ath10k_ce_src_ring_dmax_set(ar, ctrl_addr, attr->src_sz_max);
+	ath10k_ce_src_ring_byte_swap_set(ar, ctrl_addr, 0);
+	ath10k_ce_src_ring_lowmark_set(ar, ctrl_addr, 0);
+	ath10k_ce_src_ring_highmark_set(ar, ctrl_addr, nentries);
+	ath10k_pci_sleep(ar);
+
+	return 0;
+}
+
+static int ath10k_ce_init_dest_ring(struct ath10k *ar,
+				    unsigned int ce_id,
+				    struct ce_state *ce_state,
+				    const struct ce_attr *attr)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	struct ce_ring_state *dest_ring;
+	unsigned int nentries = attr->dest_nentries;
+	unsigned int ce_nbytes;
+	u32 ctrl_addr = ath10k_ce_base_address(ce_id);
+	dma_addr_t base_addr;
+	char *ptr;
+
+	nentries = roundup_pow_of_two(nentries);
+
+	if (ce_state->dest_ring) {
+		WARN_ON(ce_state->dest_ring->nentries != nentries);
+		return 0;
+	}
+
+	ce_nbytes = sizeof(struct ce_ring_state) + (nentries * sizeof(void *));
+	ptr = kzalloc(ce_nbytes, GFP_KERNEL);
+	if (ptr == NULL)
+		return -ENOMEM;
+
+	ce_state->dest_ring = (struct ce_ring_state *)ptr;
+	dest_ring = ce_state->dest_ring;
+
+	ptr += sizeof(struct ce_ring_state);
+	dest_ring->nentries = nentries;
+	dest_ring->nentries_mask = nentries - 1;
+
+	ath10k_pci_wake(ar);
+	dest_ring->sw_index = ath10k_ce_dest_ring_read_index_get(ar, ctrl_addr);
+	dest_ring->write_index =
+		ath10k_ce_dest_ring_write_index_get(ar, ctrl_addr);
+	ath10k_pci_sleep(ar);
+
+	dest_ring->per_transfer_context = (void **)ptr;
+
+	/*
+	 * Legacy platforms that do not support cache
+	 * coherent DMA are unsupported
+	 */
+	dest_ring->base_addr_owner_space_unaligned =
+		pci_alloc_consistent(ar_pci->pdev,
+				     (nentries * sizeof(struct ce_desc) +
+				      CE_DESC_RING_ALIGN),
+				     &base_addr);
+	dest_ring->base_addr_ce_space_unaligned = base_addr;
+
+	/*
+	 * Correctly initialize memory to 0 to prevent garbage
+	 * data crashing system when download firmware
+	 */
+	memset(dest_ring->base_addr_owner_space_unaligned, 0,
+	       nentries * sizeof(struct ce_desc) + CE_DESC_RING_ALIGN);
+
+	dest_ring->base_addr_owner_space = PTR_ALIGN(
+			dest_ring->base_addr_owner_space_unaligned,
+			CE_DESC_RING_ALIGN);
+	dest_ring->base_addr_ce_space = ALIGN(
+			dest_ring->base_addr_ce_space_unaligned,
+			CE_DESC_RING_ALIGN);
+
+	ath10k_pci_wake(ar);
+	ath10k_ce_dest_ring_base_addr_set(ar, ctrl_addr,
+					  dest_ring->base_addr_ce_space);
+	ath10k_ce_dest_ring_size_set(ar, ctrl_addr, nentries);
+	ath10k_ce_dest_ring_byte_swap_set(ar, ctrl_addr, 0);
+	ath10k_ce_dest_ring_lowmark_set(ar, ctrl_addr, 0);
+	ath10k_ce_dest_ring_highmark_set(ar, ctrl_addr, nentries);
+	ath10k_pci_sleep(ar);
+
+	return 0;
+}
+
+static struct ce_state *ath10k_ce_init_state(struct ath10k *ar,
+					     unsigned int ce_id,
+					     const struct ce_attr *attr)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	struct ce_state *ce_state = NULL;
+	u32 ctrl_addr = ath10k_ce_base_address(ce_id);
+
+	spin_lock_bh(&ar_pci->ce_lock);
+
+	if (!ar_pci->ce_id_to_state[ce_id]) {
+		ce_state = kzalloc(sizeof(*ce_state), GFP_ATOMIC);
+		if (ce_state == NULL) {
+			spin_unlock_bh(&ar_pci->ce_lock);
+			return NULL;
+		}
+
+		ar_pci->ce_id_to_state[ce_id] = ce_state;
+		ce_state->ar = ar;
+		ce_state->id = ce_id;
+		ce_state->ctrl_addr = ctrl_addr;
+		ce_state->state = CE_RUNNING;
+		/* Save attribute flags */
+		ce_state->attr_flags = attr->flags;
+		ce_state->src_sz_max = attr->src_sz_max;
+	}
+
+	spin_unlock_bh(&ar_pci->ce_lock);
+
+	return ce_state;
+}
+
+/*
+ * Initialize a Copy Engine based on caller-supplied attributes.
+ * This may be called once to initialize both source and destination
+ * rings or it may be called twice for separate source and destination
+ * initialization. It may be that only one side or the other is
+ * initialized by software/firmware.
+ */
+struct ce_state *ath10k_ce_init(struct ath10k *ar,
+				unsigned int ce_id,
+				const struct ce_attr *attr)
+{
+	struct ce_state *ce_state;
+	u32 ctrl_addr = ath10k_ce_base_address(ce_id);
+
+	ce_state = ath10k_ce_init_state(ar, ce_id, attr);
+	if (!ce_state) {
+		ath10k_err("Failed to initialize CE state for ID: %d\n", ce_id);
+		return NULL;
+	}
+
+	if (attr->src_nentries) {
+		if (ath10k_ce_init_src_ring(ar, ce_id, ce_state, attr)) {
+			ath10k_err("Failed to initialize CE src ring for ID: %d\n",
+				   ce_id);
+			ath10k_ce_deinit(ce_state);
+			return NULL;
+		}
+	}
+
+	if (attr->dest_nentries) {
+		if (ath10k_ce_init_dest_ring(ar, ce_id, ce_state, attr)) {
+			ath10k_err("Failed to initialize CE dest ring for ID: %d\n",
+				   ce_id);
+			ath10k_ce_deinit(ce_state);
+			return NULL;
+		}
+	}
+
+	/* Enable CE error interrupts */
+	ath10k_pci_wake(ar);
+	ath10k_ce_error_intr_enable(ar, ctrl_addr);
+	ath10k_pci_sleep(ar);
+
+	return ce_state;
+}
+
+void ath10k_ce_deinit(struct ce_state *ce_state)
+{
+	unsigned int ce_id = ce_state->id;
+	struct ath10k *ar = ce_state->ar;
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+	ce_state->state = CE_UNUSED;
+	ar_pci->ce_id_to_state[ce_id] = NULL;
+
+	if (ce_state->src_ring) {
+		kfree(ce_state->src_ring->shadow_base_unaligned);
+		pci_free_consistent(ar_pci->pdev,
+				    (ce_state->src_ring->nentries *
+				     sizeof(struct ce_desc) +
+				     CE_DESC_RING_ALIGN),
+				    ce_state->src_ring->base_addr_owner_space,
+				    ce_state->src_ring->base_addr_ce_space);
+		kfree(ce_state->src_ring);
+	}
+
+	if (ce_state->dest_ring) {
+		pci_free_consistent(ar_pci->pdev,
+				    (ce_state->dest_ring->nentries *
+				     sizeof(struct ce_desc) +
+				     CE_DESC_RING_ALIGN),
+				    ce_state->dest_ring->base_addr_owner_space,
+				    ce_state->dest_ring->base_addr_ce_space);
+		kfree(ce_state->dest_ring);
+	}
+	kfree(ce_state);
+}


^ permalink raw reply related

* [PATCH v2 04/29] ath10k: add ce.h
From: Kalle Valo @ 2013-06-01 11:24 UTC (permalink / raw)
  To: linux-wireless; +Cc: ath10k-devel
In-Reply-To: <20130601111820.32443.46794.stgit@localhost6.localdomain6>

Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
---
 drivers/net/wireless/ath/ath10k/ce.h |  516 ++++++++++++++++++++++++++++++++++
 1 file changed, 516 insertions(+)
 create mode 100644 drivers/net/wireless/ath/ath10k/ce.h

diff --git a/drivers/net/wireless/ath/ath10k/ce.h b/drivers/net/wireless/ath/ath10k/ce.h
new file mode 100644
index 0000000..c17f07c
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/ce.h
@@ -0,0 +1,516 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * 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 _CE_H_
+#define _CE_H_
+
+#include "hif.h"
+
+
+/* Maximum number of Copy Engine's supported */
+#define CE_COUNT_MAX 8
+#define CE_HTT_H2T_MSG_SRC_NENTRIES 2048
+
+/* Descriptor rings must be aligned to this boundary */
+#define CE_DESC_RING_ALIGN	8
+#define CE_SENDLIST_ITEMS_MAX	12
+#define CE_SEND_FLAG_GATHER	0x00010000
+
+/*
+ * Copy Engine support: low-level Target-side Copy Engine API.
+ * This is a hardware access layer used by code that understands
+ * how to use copy engines.
+ */
+
+struct ce_state;
+
+
+/* Copy Engine operational state */
+enum ce_op_state {
+	CE_UNUSED,
+	CE_PAUSED,
+	CE_RUNNING,
+};
+
+#define CE_DESC_FLAGS_GATHER         (1 << 0)
+#define CE_DESC_FLAGS_BYTE_SWAP      (1 << 1)
+#define CE_DESC_FLAGS_META_DATA_MASK 0xFFFC
+#define CE_DESC_FLAGS_META_DATA_LSB  3
+
+struct ce_desc {
+	__le32 addr;
+	__le16 nbytes;
+	__le16 flags; /* %CE_DESC_FLAGS_ */
+};
+
+/* Copy Engine Ring internal state */
+struct ce_ring_state {
+	/* Number of entries in this ring; must be power of 2 */
+	unsigned int nentries;
+	unsigned int nentries_mask;
+
+	/*
+	 * For dest ring, this is the next index to be processed
+	 * by software after it was/is received into.
+	 *
+	 * For src ring, this is the last descriptor that was sent
+	 * and completion processed by software.
+	 *
+	 * Regardless of src or dest ring, this is an invariant
+	 * (modulo ring size):
+	 *     write index >= read index >= sw_index
+	 */
+	unsigned int sw_index;
+	/* cached copy */
+	unsigned int write_index;
+	/*
+	 * For src ring, this is the next index not yet processed by HW.
+	 * This is a cached copy of the real HW index (read index), used
+	 * for avoiding reading the HW index register more often than
+	 * necessary.
+	 * This extends the invariant:
+	 *     write index >= read index >= hw_index >= sw_index
+	 *
+	 * For dest ring, this is currently unused.
+	 */
+	/* cached copy */
+	unsigned int hw_index;
+
+	/* Start of DMA-coherent area reserved for descriptors */
+	/* Host address space */
+	void *base_addr_owner_space_unaligned;
+	/* CE address space */
+	u32 base_addr_ce_space_unaligned;
+
+	/*
+	 * Actual start of descriptors.
+	 * Aligned to descriptor-size boundary.
+	 * Points into reserved DMA-coherent area, above.
+	 */
+	/* Host address space */
+	void *base_addr_owner_space;
+
+	/* CE address space */
+	u32 base_addr_ce_space;
+	/*
+	 * Start of shadow copy of descriptors, within regular memory.
+	 * Aligned to descriptor-size boundary.
+	 */
+	void *shadow_base_unaligned;
+	struct ce_desc *shadow_base;
+
+	void **per_transfer_context;
+};
+
+/* Copy Engine internal state */
+struct ce_state {
+	struct ath10k *ar;
+	unsigned int id;
+
+	unsigned int attr_flags;
+
+	u32 ctrl_addr;
+	enum ce_op_state state;
+
+	void (*send_cb) (struct ce_state *ce_state,
+			 void *per_transfer_send_context,
+			 u32 buffer,
+			 unsigned int nbytes,
+			 unsigned int transfer_id);
+	void (*recv_cb) (struct ce_state *ce_state,
+			 void *per_transfer_recv_context,
+			 u32 buffer,
+			 unsigned int nbytes,
+			 unsigned int transfer_id,
+			 unsigned int flags);
+
+	unsigned int src_sz_max;
+	struct ce_ring_state *src_ring;
+	struct ce_ring_state *dest_ring;
+};
+
+struct ce_sendlist_item {
+	/* e.g. buffer or desc list */
+	dma_addr_t data;
+	union {
+		/* simple buffer */
+		unsigned int nbytes;
+		/* Rx descriptor list */
+		unsigned int ndesc;
+	} u;
+	/* externally-specified flags; OR-ed with internal flags */
+	u32 flags;
+};
+
+struct ce_sendlist {
+	unsigned int num_items;
+	struct ce_sendlist_item item[CE_SENDLIST_ITEMS_MAX];
+};
+
+/* Copy Engine settable attributes */
+struct ce_attr;
+
+/*==================Send====================*/
+
+/* ath10k_ce_send flags */
+#define CE_SEND_FLAG_BYTE_SWAP 1
+
+/*
+ * Queue a source buffer to be sent to an anonymous destination buffer.
+ *   ce         - which copy engine to use
+ *   buffer          - address of buffer
+ *   nbytes          - number of bytes to send
+ *   transfer_id     - arbitrary ID; reflected to destination
+ *   flags           - CE_SEND_FLAG_* values
+ * Returns 0 on success; otherwise an error status.
+ *
+ * Note: If no flags are specified, use CE's default data swap mode.
+ *
+ * Implementation note: pushes 1 buffer to Source ring
+ */
+int ath10k_ce_send(struct ce_state *ce_state,
+		   void *per_transfer_send_context,
+		   u32 buffer,
+		   unsigned int nbytes,
+		   /* 14 bits */
+		   unsigned int transfer_id,
+		   unsigned int flags);
+
+void ath10k_ce_send_cb_register(struct ce_state *ce_state,
+				void (*send_cb) (struct ce_state *ce_state,
+						 void *transfer_context,
+						 u32 buffer,
+						 unsigned int nbytes,
+						 unsigned int transfer_id),
+				int disable_interrupts);
+
+/* Append a simple buffer (address/length) to a sendlist. */
+void ath10k_ce_sendlist_buf_add(struct ce_sendlist *sendlist,
+				u32 buffer,
+				unsigned int nbytes,
+				/* OR-ed with internal flags */
+				u32 flags);
+
+/*
+ * Queue a "sendlist" of buffers to be sent using gather to a single
+ * anonymous destination buffer
+ *   ce         - which copy engine to use
+ *   sendlist        - list of simple buffers to send using gather
+ *   transfer_id     - arbitrary ID; reflected to destination
+ * Returns 0 on success; otherwise an error status.
+ *
+ * Implemenation note: Pushes multiple buffers with Gather to Source ring.
+ */
+int ath10k_ce_sendlist_send(struct ce_state *ce_state,
+			    void *per_transfer_send_context,
+			    struct ce_sendlist *sendlist,
+			    /* 14 bits */
+			    unsigned int transfer_id);
+
+/*==================Recv=======================*/
+
+/*
+ * Make a buffer available to receive. The buffer must be at least of a
+ * minimal size appropriate for this copy engine (src_sz_max attribute).
+ *   ce                    - which copy engine to use
+ *   per_transfer_recv_context  - context passed back to caller's recv_cb
+ *   buffer                     - address of buffer in CE space
+ * Returns 0 on success; otherwise an error status.
+ *
+ * Implemenation note: Pushes a buffer to Dest ring.
+ */
+int ath10k_ce_recv_buf_enqueue(struct ce_state *ce_state,
+			       void *per_transfer_recv_context,
+			       u32 buffer);
+
+void ath10k_ce_recv_cb_register(struct ce_state *ce_state,
+				void (*recv_cb) (struct ce_state *ce_state,
+						 void *transfer_context,
+						 u32 buffer,
+						 unsigned int nbytes,
+						 unsigned int transfer_id,
+						 unsigned int flags));
+
+/* recv flags */
+/* Data is byte-swapped */
+#define CE_RECV_FLAG_SWAPPED	1
+
+/*
+ * Supply data for the next completed unprocessed receive descriptor.
+ * Pops buffer from Dest ring.
+ */
+int ath10k_ce_completed_recv_next(struct ce_state *ce_state,
+				  void **per_transfer_contextp,
+				  u32 *bufferp,
+				  unsigned int *nbytesp,
+				  unsigned int *transfer_idp,
+				  unsigned int *flagsp);
+/*
+ * Supply data for the next completed unprocessed send descriptor.
+ * Pops 1 completed send buffer from Source ring.
+ */
+int ath10k_ce_completed_send_next(struct ce_state *ce_state,
+			   void **per_transfer_contextp,
+			   u32 *bufferp,
+			   unsigned int *nbytesp,
+			   unsigned int *transfer_idp);
+
+/*==================CE Engine Initialization=======================*/
+
+/* Initialize an instance of a CE */
+struct ce_state *ath10k_ce_init(struct ath10k *ar,
+				unsigned int ce_id,
+				const struct ce_attr *attr);
+
+/*==================CE Engine Shutdown=======================*/
+/*
+ * Support clean shutdown by allowing the caller to revoke
+ * receive buffers.  Target DMA must be stopped before using
+ * this API.
+ */
+int ath10k_ce_revoke_recv_next(struct ce_state *ce_state,
+			       void **per_transfer_contextp,
+			       u32 *bufferp);
+
+/*
+ * Support clean shutdown by allowing the caller to cancel
+ * pending sends.  Target DMA must be stopped before using
+ * this API.
+ */
+int ath10k_ce_cancel_send_next(struct ce_state *ce_state,
+			       void **per_transfer_contextp,
+			       u32 *bufferp,
+			       unsigned int *nbytesp,
+			       unsigned int *transfer_idp);
+
+void ath10k_ce_deinit(struct ce_state *ce_state);
+
+/*==================CE Interrupt Handlers====================*/
+void ath10k_ce_per_engine_service_any(struct ath10k *ar);
+void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id);
+void ath10k_ce_disable_interrupts(struct ath10k *ar);
+
+/* ce_attr.flags values */
+/* Use NonSnooping PCIe accesses? */
+#define CE_ATTR_NO_SNOOP		1
+
+/* Byte swap data words */
+#define CE_ATTR_BYTE_SWAP_DATA		2
+
+/* Swizzle descriptors? */
+#define CE_ATTR_SWIZZLE_DESCRIPTORS	4
+
+/* no interrupt on copy completion */
+#define CE_ATTR_DIS_INTR		8
+
+/* Attributes of an instance of a Copy Engine */
+struct ce_attr {
+	/* CE_ATTR_* values */
+	unsigned int flags;
+
+	/* currently not in use */
+	unsigned int priority;
+
+	/* #entries in source ring - Must be a power of 2 */
+	unsigned int src_nentries;
+
+	/*
+	 * Max source send size for this CE.
+	 * This is also the minimum size of a destination buffer.
+	 */
+	unsigned int src_sz_max;
+
+	/* #entries in destination ring - Must be a power of 2 */
+	unsigned int dest_nentries;
+
+	/* Future use */
+	void *reserved;
+};
+
+/*
+ * When using sendlist_send to transfer multiple buffer fragments, the
+ * transfer context of each fragment, except last one, will be filled
+ * with CE_SENDLIST_ITEM_CTXT. ce_completed_send will return success for
+ * each fragment done with send and the transfer context would be
+ * CE_SENDLIST_ITEM_CTXT. Upper layer could use this to identify the
+ * status of a send completion.
+ */
+#define CE_SENDLIST_ITEM_CTXT	((void *)0xcecebeef)
+
+#define SR_BA_ADDRESS		0x0000
+#define SR_SIZE_ADDRESS		0x0004
+#define DR_BA_ADDRESS		0x0008
+#define DR_SIZE_ADDRESS		0x000c
+#define CE_CMD_ADDRESS		0x0018
+
+#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_MSB	17
+#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_LSB	17
+#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK	0x00020000
+#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_SET(x) \
+	(((0 | (x)) << CE_CTRL1_DST_RING_BYTE_SWAP_EN_LSB) & \
+	CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK)
+
+#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MSB	16
+#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB	16
+#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK	0x00010000
+#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_GET(x) \
+	(((x) & CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK) >> \
+	 CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB)
+#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_SET(x) \
+	(((0 | (x)) << CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB) & \
+	 CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK)
+
+#define CE_CTRL1_DMAX_LENGTH_MSB		15
+#define CE_CTRL1_DMAX_LENGTH_LSB		0
+#define CE_CTRL1_DMAX_LENGTH_MASK		0x0000ffff
+#define CE_CTRL1_DMAX_LENGTH_GET(x) \
+	(((x) & CE_CTRL1_DMAX_LENGTH_MASK) >> CE_CTRL1_DMAX_LENGTH_LSB)
+#define CE_CTRL1_DMAX_LENGTH_SET(x) \
+	(((0 | (x)) << CE_CTRL1_DMAX_LENGTH_LSB) & CE_CTRL1_DMAX_LENGTH_MASK)
+
+#define CE_CTRL1_ADDRESS			0x0010
+#define CE_CTRL1_HW_MASK			0x0007ffff
+#define CE_CTRL1_SW_MASK			0x0007ffff
+#define CE_CTRL1_HW_WRITE_MASK			0x00000000
+#define CE_CTRL1_SW_WRITE_MASK			0x0007ffff
+#define CE_CTRL1_RSTMASK			0xffffffff
+#define CE_CTRL1_RESET				0x00000080
+
+#define CE_CMD_HALT_STATUS_MSB			3
+#define CE_CMD_HALT_STATUS_LSB			3
+#define CE_CMD_HALT_STATUS_MASK			0x00000008
+#define CE_CMD_HALT_STATUS_GET(x) \
+	(((x) & CE_CMD_HALT_STATUS_MASK) >> CE_CMD_HALT_STATUS_LSB)
+#define CE_CMD_HALT_STATUS_SET(x) \
+	(((0 | (x)) << CE_CMD_HALT_STATUS_LSB) & CE_CMD_HALT_STATUS_MASK)
+#define CE_CMD_HALT_STATUS_RESET		0
+#define CE_CMD_HALT_MSB				0
+#define CE_CMD_HALT_MASK			0x00000001
+
+#define HOST_IE_COPY_COMPLETE_MSB		0
+#define HOST_IE_COPY_COMPLETE_LSB		0
+#define HOST_IE_COPY_COMPLETE_MASK		0x00000001
+#define HOST_IE_COPY_COMPLETE_GET(x) \
+	(((x) & HOST_IE_COPY_COMPLETE_MASK) >> HOST_IE_COPY_COMPLETE_LSB)
+#define HOST_IE_COPY_COMPLETE_SET(x) \
+	(((0 | (x)) << HOST_IE_COPY_COMPLETE_LSB) & HOST_IE_COPY_COMPLETE_MASK)
+#define HOST_IE_COPY_COMPLETE_RESET		0
+#define HOST_IE_ADDRESS				0x002c
+
+#define HOST_IS_DST_RING_LOW_WATERMARK_MASK	0x00000010
+#define HOST_IS_DST_RING_HIGH_WATERMARK_MASK	0x00000008
+#define HOST_IS_SRC_RING_LOW_WATERMARK_MASK	0x00000004
+#define HOST_IS_SRC_RING_HIGH_WATERMARK_MASK	0x00000002
+#define HOST_IS_COPY_COMPLETE_MASK		0x00000001
+#define HOST_IS_ADDRESS				0x0030
+
+#define MISC_IE_ADDRESS				0x0034
+
+#define MISC_IS_AXI_ERR_MASK			0x00000400
+
+#define MISC_IS_DST_ADDR_ERR_MASK		0x00000200
+#define MISC_IS_SRC_LEN_ERR_MASK		0x00000100
+#define MISC_IS_DST_MAX_LEN_VIO_MASK		0x00000080
+#define MISC_IS_DST_RING_OVERFLOW_MASK		0x00000040
+#define MISC_IS_SRC_RING_OVERFLOW_MASK		0x00000020
+
+#define MISC_IS_ADDRESS				0x0038
+
+#define SR_WR_INDEX_ADDRESS			0x003c
+
+#define DST_WR_INDEX_ADDRESS			0x0040
+
+#define CURRENT_SRRI_ADDRESS			0x0044
+
+#define CURRENT_DRRI_ADDRESS			0x0048
+
+#define SRC_WATERMARK_LOW_MSB			31
+#define SRC_WATERMARK_LOW_LSB			16
+#define SRC_WATERMARK_LOW_MASK			0xffff0000
+#define SRC_WATERMARK_LOW_GET(x) \
+	(((x) & SRC_WATERMARK_LOW_MASK) >> SRC_WATERMARK_LOW_LSB)
+#define SRC_WATERMARK_LOW_SET(x) \
+	(((0 | (x)) << SRC_WATERMARK_LOW_LSB) & SRC_WATERMARK_LOW_MASK)
+#define SRC_WATERMARK_LOW_RESET			0
+#define SRC_WATERMARK_HIGH_MSB			15
+#define SRC_WATERMARK_HIGH_LSB			0
+#define SRC_WATERMARK_HIGH_MASK			0x0000ffff
+#define SRC_WATERMARK_HIGH_GET(x) \
+	(((x) & SRC_WATERMARK_HIGH_MASK) >> SRC_WATERMARK_HIGH_LSB)
+#define SRC_WATERMARK_HIGH_SET(x) \
+	(((0 | (x)) << SRC_WATERMARK_HIGH_LSB) & SRC_WATERMARK_HIGH_MASK)
+#define SRC_WATERMARK_HIGH_RESET		0
+#define SRC_WATERMARK_ADDRESS			0x004c
+
+#define DST_WATERMARK_LOW_LSB			16
+#define DST_WATERMARK_LOW_MASK			0xffff0000
+#define DST_WATERMARK_LOW_SET(x) \
+	(((0 | (x)) << DST_WATERMARK_LOW_LSB) & DST_WATERMARK_LOW_MASK)
+#define DST_WATERMARK_LOW_RESET			0
+#define DST_WATERMARK_HIGH_MSB			15
+#define DST_WATERMARK_HIGH_LSB			0
+#define DST_WATERMARK_HIGH_MASK			0x0000ffff
+#define DST_WATERMARK_HIGH_GET(x) \
+	(((x) & DST_WATERMARK_HIGH_MASK) >> DST_WATERMARK_HIGH_LSB)
+#define DST_WATERMARK_HIGH_SET(x) \
+	(((0 | (x)) << DST_WATERMARK_HIGH_LSB) & DST_WATERMARK_HIGH_MASK)
+#define DST_WATERMARK_HIGH_RESET		0
+#define DST_WATERMARK_ADDRESS			0x0050
+
+
+static inline u32 ath10k_ce_base_address(unsigned int ce_id)
+{
+	return CE0_BASE_ADDRESS + (CE1_BASE_ADDRESS - CE0_BASE_ADDRESS) * ce_id;
+}
+
+#define CE_WATERMARK_MASK (HOST_IS_SRC_RING_LOW_WATERMARK_MASK  | \
+			   HOST_IS_SRC_RING_HIGH_WATERMARK_MASK | \
+			   HOST_IS_DST_RING_LOW_WATERMARK_MASK  | \
+			   HOST_IS_DST_RING_HIGH_WATERMARK_MASK)
+
+#define CE_ERROR_MASK	(MISC_IS_AXI_ERR_MASK           | \
+			 MISC_IS_DST_ADDR_ERR_MASK      | \
+			 MISC_IS_SRC_LEN_ERR_MASK       | \
+			 MISC_IS_DST_MAX_LEN_VIO_MASK   | \
+			 MISC_IS_DST_RING_OVERFLOW_MASK | \
+			 MISC_IS_SRC_RING_OVERFLOW_MASK)
+
+#define CE_SRC_RING_TO_DESC(baddr, idx) \
+	(&(((struct ce_desc *)baddr)[idx]))
+
+#define CE_DEST_RING_TO_DESC(baddr, idx) \
+	(&(((struct ce_desc *)baddr)[idx]))
+
+/* Ring arithmetic (modulus number of entries in ring, which is a pwr of 2). */
+#define CE_RING_DELTA(nentries_mask, fromidx, toidx) \
+	(((int)(toidx)-(int)(fromidx)) & (nentries_mask))
+
+#define CE_RING_IDX_INCR(nentries_mask, idx) (((idx) + 1) & (nentries_mask))
+
+#define CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_LSB		8
+#define CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_MASK		0x0000ff00
+#define CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_GET(x) \
+	(((x) & CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_MASK) >> \
+		CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_LSB)
+#define CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS			0x0000
+
+#define CE_INTERRUPT_SUMMARY(ar) \
+	CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_GET( \
+		ath10k_pci_read32((ar), CE_WRAPPER_BASE_ADDRESS + \
+		CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS))
+
+#endif /* _CE_H_ */


^ permalink raw reply related

* [PATCH v2 05/29] ath10k: add core.c
From: Kalle Valo @ 2013-06-01 11:24 UTC (permalink / raw)
  To: linux-wireless; +Cc: ath10k-devel
In-Reply-To: <20130601111820.32443.46794.stgit@localhost6.localdomain6>

Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
---
 drivers/net/wireless/ath/ath10k/core.c |  665 ++++++++++++++++++++++++++++++++
 1 file changed, 665 insertions(+)
 create mode 100644 drivers/net/wireless/ath/ath10k/core.c

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
new file mode 100644
index 0000000..2b3426b
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -0,0 +1,665 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * 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/firmware.h>
+
+#include "core.h"
+#include "mac.h"
+#include "htc.h"
+#include "hif.h"
+#include "wmi.h"
+#include "bmi.h"
+#include "debug.h"
+#include "htt.h"
+
+unsigned int ath10k_debug_mask;
+static bool uart_print;
+static unsigned int ath10k_p2p;
+module_param_named(debug_mask, ath10k_debug_mask, uint, 0644);
+module_param(uart_print, bool, 0644);
+module_param_named(p2p, ath10k_p2p, uint, 0644);
+MODULE_PARM_DESC(debug_mask, "Debugging mask");
+MODULE_PARM_DESC(uart_print, "Uart target debugging");
+MODULE_PARM_DESC(p2p, "Enable ath10k P2P support");
+
+static const struct ath10k_hw_params ath10k_hw_params_list[] = {
+	{
+		.id = QCA988X_HW_1_0_VERSION,
+		.name = "qca988x hw1.0",
+		.patch_load_addr = QCA988X_HW_1_0_PATCH_LOAD_ADDR,
+		.fw = {
+			.dir = QCA988X_HW_1_0_FW_DIR,
+			.fw = QCA988X_HW_1_0_FW_FILE,
+			.otp = QCA988X_HW_1_0_OTP_FILE,
+			.board = QCA988X_HW_1_0_BOARD_DATA_FILE,
+		},
+	},
+	{
+		.id = QCA988X_HW_2_0_VERSION,
+		.name = "qca988x hw2.0",
+		.patch_load_addr = QCA988X_HW_2_0_PATCH_LOAD_ADDR,
+		.fw = {
+			.dir = QCA988X_HW_2_0_FW_DIR,
+			.fw = QCA988X_HW_2_0_FW_FILE,
+			.otp = QCA988X_HW_2_0_OTP_FILE,
+			.board = QCA988X_HW_2_0_BOARD_DATA_FILE,
+		},
+	},
+};
+
+static void ath10k_send_suspend_complete(struct ath10k *ar)
+{
+	ath10k_dbg(ATH10K_DBG_CORE, "%s\n", __func__);
+
+	ar->is_target_paused = true;
+	wake_up(&ar->event_queue);
+}
+
+static int ath10k_check_fw_version(struct ath10k *ar)
+{
+	char version[32];
+
+	if (ar->fw_version_major >= SUPPORTED_FW_MAJOR &&
+	    ar->fw_version_minor >= SUPPORTED_FW_MINOR &&
+	    ar->fw_version_release >= SUPPORTED_FW_RELEASE &&
+	    ar->fw_version_build >= SUPPORTED_FW_BUILD)
+		return 0;
+
+	snprintf(version, sizeof(version), "%u.%u.%u.%u",
+		 SUPPORTED_FW_MAJOR, SUPPORTED_FW_MINOR,
+		 SUPPORTED_FW_RELEASE, SUPPORTED_FW_BUILD);
+
+	ath10k_warn("WARNING: Firmware version %s is not officially supported.\n",
+		    ar->hw->wiphy->fw_version);
+	ath10k_warn("Please upgrade to version %s (or newer)\n", version);
+
+	return 0;
+}
+
+static int ath10k_init_connect_htc(struct ath10k *ar)
+{
+	int status;
+
+	status = ath10k_wmi_connect_htc_service(ar);
+	if (status)
+		goto conn_fail;
+
+	/* Start HTC */
+	status = ath10k_htc_start(ar->htc);
+	if (status)
+		goto conn_fail;
+
+	/* Wait for WMI event to be ready */
+	status = ath10k_wmi_wait_for_service_ready(ar);
+	if (status <= 0) {
+		ath10k_warn("wmi service ready event not received");
+		status = -ETIMEDOUT;
+		goto timeout;
+	}
+
+	ath10k_dbg(ATH10K_DBG_CORE, "core wmi ready\n");
+	return 0;
+
+timeout:
+	ath10k_htc_stop(ar->htc);
+conn_fail:
+	return status;
+}
+
+static int ath10k_init_configure_target(struct ath10k *ar)
+{
+	u32 param_host;
+	int ret;
+
+	/* tell target which HTC version it is used*/
+	ret = ath10k_bmi_write32(ar, hi_app_host_interest,
+				 HTC_PROTOCOL_VERSION);
+	if (ret) {
+		ath10k_err("settings HTC version failed\n");
+		return ret;
+	}
+
+	/* set the firmware mode to STA/IBSS/AP */
+	ret = ath10k_bmi_read32(ar, hi_option_flag, &param_host);
+	if (ret) {
+		ath10k_err("setting firmware mode (1/2) failed\n");
+		return ret;
+	}
+
+	/* TODO following parameters need to be re-visited. */
+	/* num_device */
+	param_host |= (1 << HI_OPTION_NUM_DEV_SHIFT);
+	/* Firmware mode */
+	/* FIXME: Why FW_MODE_AP ??.*/
+	param_host |= (HI_OPTION_FW_MODE_AP << HI_OPTION_FW_MODE_SHIFT);
+	/* mac_addr_method */
+	param_host |= (1 << HI_OPTION_MAC_ADDR_METHOD_SHIFT);
+	/* firmware_bridge */
+	param_host |= (0 << HI_OPTION_FW_BRIDGE_SHIFT);
+	/* fwsubmode */
+	param_host |= (0 << HI_OPTION_FW_SUBMODE_SHIFT);
+
+	ret = ath10k_bmi_write32(ar, hi_option_flag, param_host);
+	if (ret) {
+		ath10k_err("setting firmware mode (2/2) failed\n");
+		return ret;
+	}
+
+	/* We do all byte-swapping on the host */
+	ret = ath10k_bmi_write32(ar, hi_be, 0);
+	if (ret) {
+		ath10k_err("setting host CPU BE mode failed\n");
+		return ret;
+	}
+
+	/* FW descriptor/Data swap flags */
+	ret = ath10k_bmi_write32(ar, hi_fw_swap, 0);
+
+	if (ret) {
+		ath10k_err("setting FW data/desc swap flags failed\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct firmware *ath10k_fetch_fw_file(struct ath10k *ar,
+						   const char *dir,
+						   const char *file)
+{
+	char filename[100];
+	const struct firmware *fw;
+	int ret;
+
+	if (file == NULL)
+		return ERR_PTR(-ENOENT);
+
+	if (dir == NULL)
+		dir = ".";
+
+	snprintf(filename, sizeof(filename), "%s/%s", dir, file);
+	ret = request_firmware(&fw, filename, ar->dev);
+	if (ret)
+		return ERR_PTR(ret);
+
+	return fw;
+}
+
+static int ath10k_push_board_ext_data(struct ath10k *ar,
+				      const struct firmware *fw)
+{
+	u32 board_data_size = QCA988X_BOARD_DATA_SZ;
+	u32 board_ext_data_size = QCA988X_BOARD_EXT_DATA_SZ;
+	u32 board_ext_data_addr;
+	int ret;
+
+	ret = ath10k_bmi_read32(ar, hi_board_ext_data, &board_ext_data_addr);
+	if (ret) {
+		ath10k_err("could not read board ext data addr (%d)\n", ret);
+		return ret;
+	}
+
+	ath10k_dbg(ATH10K_DBG_CORE,
+		   "ath10k: Board extended Data download addr: 0x%x\n",
+		   board_ext_data_addr);
+
+	if (board_ext_data_addr == 0)
+		return 0;
+
+	if (fw->size != (board_data_size + board_ext_data_size)) {
+		ath10k_err("invalid board (ext) data sizes %zu != %d+%d\n",
+			   fw->size, board_data_size, board_ext_data_size);
+		return -EINVAL;
+	}
+
+	ret = ath10k_bmi_write_memory(ar, board_ext_data_addr,
+				      fw->data + board_data_size,
+				      board_ext_data_size);
+	if (ret) {
+		ath10k_err("could not write board ext data (%d)\n", ret);
+		return ret;
+	}
+
+	ret = ath10k_bmi_write32(ar, hi_board_ext_data_config,
+				 (board_ext_data_size << 16) | 1);
+	if (ret) {
+		ath10k_err("could not write board ext data bit (%d)\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ath10k_download_board_data(struct ath10k *ar)
+{
+	u32 board_data_size = QCA988X_BOARD_DATA_SZ;
+	u32 address;
+	const struct firmware *fw;
+	int ret;
+
+	fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
+				  ar->hw_params.fw.board);
+	if (IS_ERR(fw)) {
+		ath10k_err("could not fetch board data fw file (%ld)\n",
+			   PTR_ERR(fw));
+		return PTR_ERR(fw);
+	}
+
+	ret = ath10k_push_board_ext_data(ar, fw);
+	if (ret) {
+		ath10k_err("could not push board ext data (%d)\n", ret);
+		goto exit;
+	}
+
+	ret = ath10k_bmi_read32(ar, hi_board_data, &address);
+	if (ret) {
+		ath10k_err("could not read board data addr (%d)\n", ret);
+		goto exit;
+	}
+
+	ret = ath10k_bmi_write_memory(ar, address, fw->data,
+				      min_t(u32, board_data_size, fw->size));
+	if (ret) {
+		ath10k_err("could not write board data (%d)\n", ret);
+		goto exit;
+	}
+
+	ret = ath10k_bmi_write32(ar, hi_board_data_initialized, 1);
+	if (ret) {
+		ath10k_err("could not write board data bit (%d)\n", ret);
+		goto exit;
+	}
+
+exit:
+	release_firmware(fw);
+	return ret;
+}
+
+static int ath10k_download_and_run_otp(struct ath10k *ar)
+{
+	const struct firmware *fw;
+	u32 address;
+	u32 exec_param;
+	int ret;
+
+	/* OTP is optional */
+
+	if (ar->hw_params.fw.otp == NULL) {
+		ath10k_info("otp file not defined\n");
+		return 0;
+	}
+
+	address = ar->hw_params.patch_load_addr;
+
+	fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
+				  ar->hw_params.fw.otp);
+	if (IS_ERR(fw)) {
+		ath10k_warn("could not fetch otp (%ld)\n", PTR_ERR(fw));
+		return 0;
+	}
+
+	ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size);
+	if (ret) {
+		ath10k_err("could not write otp (%d)\n", ret);
+		goto exit;
+	}
+
+	exec_param = 0;
+	ret = ath10k_bmi_execute(ar, address, &exec_param);
+	if (ret) {
+		ath10k_err("could not execute otp (%d)\n", ret);
+		goto exit;
+	}
+
+exit:
+	release_firmware(fw);
+	return ret;
+}
+
+static int ath10k_download_fw(struct ath10k *ar)
+{
+	const struct firmware *fw;
+	u32 address;
+	int ret;
+
+	if (ar->hw_params.fw.fw == NULL)
+		return -EINVAL;
+
+	address = ar->hw_params.patch_load_addr;
+
+	fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
+				  ar->hw_params.fw.fw);
+	if (IS_ERR(fw)) {
+		ath10k_err("could not fetch fw (%ld)\n", PTR_ERR(fw));
+		return PTR_ERR(fw);
+	}
+
+	ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size);
+	if (ret) {
+		ath10k_err("could not write fw (%d)\n", ret);
+		goto exit;
+	}
+
+exit:
+	release_firmware(fw);
+	return ret;
+}
+
+static int ath10k_init_download_firmware(struct ath10k *ar)
+{
+	int ret;
+
+	ret = ath10k_download_board_data(ar);
+	if (ret)
+		return ret;
+
+	ret = ath10k_download_and_run_otp(ar);
+	if (ret)
+		return ret;
+
+	ret = ath10k_download_fw(ar);
+	if (ret)
+		return ret;
+
+	return ret;
+}
+
+static int ath10k_init_uart(struct ath10k *ar)
+{
+	int ret;
+
+	/*
+	 * Explicitly setting UART prints to zero as target turns it on
+	 * based on scratch registers.
+	 */
+	ret = ath10k_bmi_write32(ar, hi_serial_enable, 0);
+	if (ret) {
+		ath10k_warn("could not disable UART prints (%d)\n", ret);
+		return ret;
+	}
+
+	if (!uart_print) {
+		ath10k_info("UART prints disabled\n");
+		return 0;
+	}
+
+	ret = ath10k_bmi_write32(ar, hi_dbg_uart_txpin, 7);
+	if (ret) {
+		ath10k_warn("could not enable UART prints (%d)\n", ret);
+		return ret;
+	}
+
+	ret = ath10k_bmi_write32(ar, hi_serial_enable, 1);
+	if (ret) {
+		ath10k_warn("could not enable UART prints (%d)\n", ret);
+		return ret;
+	}
+
+	ath10k_info("UART prints enabled\n");
+	return 0;
+}
+
+static int ath10k_init_hw_params(struct ath10k *ar)
+{
+	const struct ath10k_hw_params *uninitialized_var(hw_params);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ath10k_hw_params_list); i++) {
+		hw_params = &ath10k_hw_params_list[i];
+
+		if (hw_params->id == ar->target_version)
+			break;
+	}
+
+	if (i == ARRAY_SIZE(ath10k_hw_params_list)) {
+		ath10k_err("Unsupported hardware version: 0x%x\n",
+			   ar->target_version);
+		return -EINVAL;
+	}
+
+	ar->hw_params = *hw_params;
+
+	ath10k_info("Hardware name %s version 0x%x\n",
+		    ar->hw_params.name, ar->target_version);
+
+	return 0;
+}
+
+struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
+				  enum ath10k_bus bus,
+				  const struct ath10k_hif_ops *hif_ops)
+{
+	struct ath10k *ar;
+
+	ar = ath10k_mac_create();
+	if (!ar)
+		return NULL;
+
+	ar->ath_common.priv = ar;
+	ar->ath_common.hw = ar->hw;
+
+	ar->p2p = !!ath10k_p2p;
+	ar->dev = dev;
+
+	ar->hif.priv = hif_priv;
+	ar->hif.ops = hif_ops;
+	ar->hif.bus = bus;
+
+	ar->free_vdev_map = 0xFF; /* 8 vdevs */
+
+	init_completion(&ar->scan.started);
+	init_completion(&ar->scan.completed);
+	init_completion(&ar->scan.on_channel);
+
+	init_completion(&ar->install_key_done);
+	init_completion(&ar->vdev_setup_done);
+
+	setup_timer(&ar->scan.timeout, ath10k_reset_scan, (unsigned long)ar);
+
+	ar->workqueue = create_singlethread_workqueue("ath10k_wq");
+	if (!ar->workqueue)
+		goto err_wq;
+
+	mutex_init(&ar->conf_mutex);
+	spin_lock_init(&ar->data_lock);
+
+	INIT_LIST_HEAD(&ar->peers);
+	init_waitqueue_head(&ar->peer_mapping_wq);
+
+	init_completion(&ar->offchan_tx_completed);
+	INIT_WORK(&ar->offchan_tx_work, ath10k_offchan_tx_work);
+	skb_queue_head_init(&ar->offchan_tx_queue);
+
+	init_waitqueue_head(&ar->event_queue);
+
+	return ar;
+
+err_wq:
+	ath10k_mac_destroy(ar);
+	return NULL;
+}
+EXPORT_SYMBOL(ath10k_core_create);
+
+void ath10k_core_destroy(struct ath10k *ar)
+{
+	flush_workqueue(ar->workqueue);
+	destroy_workqueue(ar->workqueue);
+
+	ath10k_mac_destroy(ar);
+}
+EXPORT_SYMBOL(ath10k_core_destroy);
+
+
+int ath10k_core_register(struct ath10k *ar)
+{
+	struct ath10k_htc_ops htc_ops;
+	struct bmi_target_info target_info;
+	int status;
+
+	memset(&target_info, 0, sizeof(target_info));
+	status = ath10k_bmi_get_target_info(ar, &target_info);
+	if (status)
+		goto err;
+
+	ar->target_version = target_info.version;
+	ar->hw->wiphy->hw_version = target_info.version;
+
+	status = ath10k_init_hw_params(ar);
+	if (status)
+		goto err;
+
+	if (ath10k_init_configure_target(ar)) {
+		status = -EINVAL;
+		goto err;
+	}
+
+	status = ath10k_init_download_firmware(ar);
+	if (status)
+		goto err;
+
+	status = ath10k_init_uart(ar);
+	if (status)
+		goto err;
+
+	htc_ops.target_send_suspend_complete = ath10k_send_suspend_complete;
+
+	ar->htc = ath10k_htc_create(ar, &htc_ops);
+	if (IS_ERR(ar->htc)) {
+		status = PTR_ERR(ar->htc);
+		ath10k_err("could not create HTC (%d)\n", status);
+		goto err;
+	}
+
+	status = ath10k_bmi_done(ar);
+	if (status)
+		goto err_htc_destroy;
+
+	status = ath10k_wmi_attach(ar);
+	if (status) {
+		ath10k_err("WMI attach failed: %d\n", status);
+		goto err_htc_destroy;
+	}
+
+	status = ath10k_htc_wait_target(ar->htc);
+	if (status)
+		goto err_wmi_detach;
+
+	ar->htt = ath10k_htt_attach(ar);
+	if (!ar->htt) {
+		status = -ENOMEM;
+		goto err_wmi_detach;
+	}
+
+	status = ath10k_init_connect_htc(ar);
+	if (status)
+		goto err_htt_detach;
+
+	ath10k_info("firmware %s booted\n", ar->hw->wiphy->fw_version);
+
+	status = ath10k_check_fw_version(ar);
+	if (status)
+		goto err_disconnect_htc;
+
+	status = ath10k_wmi_cmd_init(ar);
+	if (status) {
+		ath10k_err("could not send WMI init command (%d)\n", status);
+		goto err_disconnect_htc;
+	}
+
+	status = ath10k_wmi_wait_for_unified_ready(ar);
+	if (status <= 0) {
+		ath10k_err("wmi unified ready event not received\n");
+		status = -ETIMEDOUT;
+		goto err_disconnect_htc;
+	}
+
+	status = ath10k_htt_attach_target(ar->htt);
+	if (status)
+		goto err_disconnect_htc;
+
+	status = ath10k_mac_register(ar);
+	if (status)
+		goto err_disconnect_htc;
+
+	status = ath10k_debug_create(ar);
+	if (status) {
+		ath10k_err("unable to initialize debugfs\n");
+		goto err_unregister_mac;
+	}
+
+	return 0;
+
+err_unregister_mac:
+	ath10k_mac_unregister(ar);
+err_disconnect_htc:
+	ath10k_htc_stop(ar->htc);
+err_htt_detach:
+	ath10k_htt_detach(ar->htt);
+err_wmi_detach:
+	ath10k_wmi_detach(ar);
+err_htc_destroy:
+	ath10k_htc_destroy(ar->htc);
+err:
+	return status;
+}
+EXPORT_SYMBOL(ath10k_core_register);
+
+void ath10k_core_unregister(struct ath10k *ar)
+{
+	/* We must unregister from mac80211 before we stop HTC and HIF.
+	 * Otherwise we will fail to submit commands to FW and mac80211 will be
+	 * unhappy about callback failures. */
+	ath10k_mac_unregister(ar);
+	ath10k_htc_stop(ar->htc);
+	ath10k_htt_detach(ar->htt);
+	ath10k_wmi_detach(ar);
+	ath10k_htc_destroy(ar->htc);
+}
+EXPORT_SYMBOL(ath10k_core_unregister);
+
+int ath10k_core_target_suspend(struct ath10k *ar)
+{
+	int ret;
+
+	ath10k_dbg(ATH10K_DBG_CORE, "%s: called", __func__);
+
+	ret = ath10k_wmi_pdev_suspend_target(ar);
+	if (ret)
+		ath10k_warn("could not suspend target (%d)\n", ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(ath10k_core_target_suspend);
+
+int ath10k_core_target_resume(struct ath10k *ar)
+{
+	int ret;
+
+	ath10k_dbg(ATH10K_DBG_CORE, "%s: called", __func__);
+
+	ret = ath10k_wmi_pdev_resume_target(ar);
+	if (ret)
+		ath10k_warn("could not resume target (%d)\n", ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(ath10k_core_target_resume);
+
+MODULE_AUTHOR("Qualcomm Atheros");
+MODULE_DESCRIPTION("Core module for QCA988X PCIe devices.");
+MODULE_LICENSE("Dual BSD/GPL");


^ permalink raw reply related

* [PATCH v2 06/29] ath10k: add core.h
From: Kalle Valo @ 2013-06-01 11:24 UTC (permalink / raw)
  To: linux-wireless; +Cc: ath10k-devel
In-Reply-To: <20130601111820.32443.46794.stgit@localhost6.localdomain6>

Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
---
 drivers/net/wireless/ath/ath10k/core.h |  369 ++++++++++++++++++++++++++++++++
 1 file changed, 369 insertions(+)
 create mode 100644 drivers/net/wireless/ath/ath10k/core.h

diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
new file mode 100644
index 0000000..539336d
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -0,0 +1,369 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * 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 _CORE_H_
+#define _CORE_H_
+
+#include <linux/completion.h>
+#include <linux/if_ether.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+
+#include "htc.h"
+#include "hw.h"
+#include "targaddrs.h"
+#include "wmi.h"
+#include "../ath.h"
+#include "../regd.h"
+
+#define MS(_v, _f) (((_v) & _f##_MASK) >> _f##_LSB)
+#define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK)
+#define WO(_f)      ((_f##_OFFSET) >> 2)
+
+#define ATH10K_SCAN_ID 0
+#define WMI_READY_TIMEOUT (5 * HZ)
+#define ATH10K_FLUSH_TIMEOUT_HZ (5*HZ)
+
+/* Antenna noise floor */
+#define ATH10K_DEFAULT_NOISE_FLOOR -95
+
+struct ath10k;
+
+enum ath10k_bus {
+	ATH10K_BUS_PCI,
+};
+
+struct ath10k_skb_cb {
+	dma_addr_t paddr;
+	bool is_mapped;
+	bool is_aborted;
+
+	struct {
+		u8 vdev_id;
+		u16 msdu_id;
+		u8 tid;
+		bool is_offchan;
+		bool is_conf;
+		bool discard;
+		bool no_ack;
+		u8 refcount;
+		struct sk_buff *txfrag;
+		struct sk_buff *msdu;
+	} __packed htt;
+
+	/* 4 bytes left on 64bit arch */
+} __packed;
+
+static inline struct ath10k_skb_cb *ATH10K_SKB_CB(struct sk_buff *skb)
+{
+	BUILD_BUG_ON(sizeof(struct ath10k_skb_cb) >
+		     IEEE80211_TX_INFO_DRIVER_DATA_SIZE);
+	return (struct ath10k_skb_cb *)&IEEE80211_SKB_CB(skb)->driver_data;
+}
+
+static inline int ath10k_skb_map(struct device *dev, struct sk_buff *skb)
+{
+	if (ATH10K_SKB_CB(skb)->is_mapped)
+		return -EINVAL;
+
+	ATH10K_SKB_CB(skb)->paddr = dma_map_single(dev, skb->data, skb->len,
+						   DMA_TO_DEVICE);
+
+	if (unlikely(dma_mapping_error(dev, ATH10K_SKB_CB(skb)->paddr)))
+		return -EIO;
+
+	ATH10K_SKB_CB(skb)->is_mapped = true;
+	return 0;
+}
+
+static inline int ath10k_skb_unmap(struct device *dev, struct sk_buff *skb)
+{
+	if (!ATH10K_SKB_CB(skb)->is_mapped)
+		return -EINVAL;
+
+	dma_unmap_single(dev, ATH10K_SKB_CB(skb)->paddr, skb->len,
+			 DMA_TO_DEVICE);
+	ATH10K_SKB_CB(skb)->is_mapped = false;
+	return 0;
+}
+
+static inline u32 host_interest_item_address(u32 item_offset)
+{
+	return QCA988X_HOST_INTEREST_ADDRESS + item_offset;
+}
+
+struct ath10k_bmi {
+	bool done_sent;
+};
+
+struct ath10k_wmi {
+	enum ath10k_htc_ep_id eid;
+	struct completion service_ready;
+	struct completion unified_ready;
+	atomic_t pending_tx_count;
+	wait_queue_head_t wq;
+
+	struct sk_buff_head wmi_event_list;
+	struct work_struct wmi_event_work;
+};
+
+struct ath10k_peer_stat {
+	u8 peer_macaddr[ETH_ALEN];
+	u32 peer_rssi;
+	u32 peer_tx_rate;
+};
+
+struct ath10k_target_stats {
+	/* PDEV stats */
+	s32 ch_noise_floor;
+	u32 tx_frame_count;
+	u32 rx_frame_count;
+	u32 rx_clear_count;
+	u32 cycle_count;
+	u32 phy_err_count;
+	u32 chan_tx_power;
+
+	/* PDEV TX stats */
+	s32 comp_queued;
+	s32 comp_delivered;
+	s32 msdu_enqued;
+	s32 mpdu_enqued;
+	s32 wmm_drop;
+	s32 local_enqued;
+	s32 local_freed;
+	s32 hw_queued;
+	s32 hw_reaped;
+	s32 underrun;
+	s32 tx_abort;
+	s32 mpdus_requed;
+	u32 tx_ko;
+	u32 data_rc;
+	u32 self_triggers;
+	u32 sw_retry_failure;
+	u32 illgl_rate_phy_err;
+	u32 pdev_cont_xretry;
+	u32 pdev_tx_timeout;
+	u32 pdev_resets;
+	u32 phy_underrun;
+	u32 txop_ovf;
+
+	/* PDEV RX stats */
+	s32 mid_ppdu_route_change;
+	s32 status_rcvd;
+	s32 r0_frags;
+	s32 r1_frags;
+	s32 r2_frags;
+	s32 r3_frags;
+	s32 htt_msdus;
+	s32 htt_mpdus;
+	s32 loc_msdus;
+	s32 loc_mpdus;
+	s32 oversize_amsdu;
+	s32 phy_errs;
+	s32 phy_err_drop;
+	s32 mpdu_errs;
+
+	/* VDEV STATS */
+
+	/* PEER STATS */
+	u8 peers;
+	struct ath10k_peer_stat peer_stat[TARGET_NUM_PEERS];
+
+	/* TODO: Beacon filter stats */
+
+};
+
+#define ATH10K_MAX_NUM_PEER_IDS (1 << 11) /* htt rx_desc limit */
+
+struct ath10k_peer {
+	struct list_head list;
+	int vdev_id;
+	u8 addr[ETH_ALEN];
+	DECLARE_BITMAP(peer_ids, ATH10K_MAX_NUM_PEER_IDS);
+	struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1];
+};
+
+#define ATH10K_VDEV_SETUP_TIMEOUT_HZ (5*HZ)
+
+struct ath10k_vif {
+	u32 vdev_id;
+	enum wmi_vdev_type vdev_type;
+	enum wmi_vdev_subtype vdev_subtype;
+	u32 beacon_interval;
+	u32 dtim_period;
+
+	struct ath10k *ar;
+	struct ieee80211_vif *vif;
+
+	struct ieee80211_key_conf *wep_keys[WMI_MAX_KEY_INDEX + 1];
+	u8 def_wep_key_index;
+
+	u16 tx_seq_no;
+
+	union {
+		struct {
+			u8 bssid[ETH_ALEN];
+			u32 uapsd;
+		} sta;
+		struct {
+			/* 127 stations; wmi limit */
+			u8 tim_bitmap[16];
+			u8 tim_len;
+			u32 ssid_len;
+			u8 ssid[IEEE80211_MAX_SSID_LEN];
+			bool hidden_ssid;
+			/* P2P_IE with NoA attribute for P2P_GO case */
+			u32 noa_len;
+			u8 *noa_data;
+		} ap;
+		struct {
+			u8 bssid[ETH_ALEN];
+		} ibss;
+	} u;
+};
+
+struct ath10k_vif_iter {
+	u32 vdev_id;
+	struct ath10k_vif *arvif;
+};
+
+struct ath10k_debug {
+	struct dentry *debugfs_phy;
+
+	struct ath10k_target_stats target_stats;
+	u32 wmi_service_bitmap[WMI_SERVICE_BM_SIZE];
+
+	struct completion event_stats_compl;
+};
+
+struct ath10k {
+	struct ath_common ath_common;
+	struct ieee80211_hw *hw;
+	struct device *dev;
+	u8 mac_addr[ETH_ALEN];
+
+	u32 target_version;
+	u8 fw_version_major;
+	u32 fw_version_minor;
+	u16 fw_version_release;
+	u16 fw_version_build;
+	u32 phy_capability;
+	u32 hw_min_tx_power;
+	u32 hw_max_tx_power;
+	u32 ht_cap_info;
+	u32 vht_cap_info;
+
+	struct targetdef *targetdef;
+	struct hostdef *hostdef;
+
+	bool p2p;
+
+	struct {
+		void *priv;
+		enum ath10k_bus bus;
+		const struct ath10k_hif_ops *ops;
+	} hif;
+
+	struct ath10k_wmi wmi;
+
+	wait_queue_head_t event_queue;
+	bool is_target_paused;
+
+	struct ath10k_bmi bmi;
+
+	struct ath10k_htc *htc;
+	struct ath10k_htt *htt;
+
+	struct ath10k_hw_params {
+		u32 id;
+		const char *name;
+		u32 patch_load_addr;
+
+		struct ath10k_hw_params_fw {
+			const char *dir;
+			const char *fw;
+			const char *otp;
+			const char *board;
+		} fw;
+	} hw_params;
+
+	struct {
+		struct completion started;
+		struct completion completed;
+		struct completion on_channel;
+		struct timer_list timeout;
+		bool is_roc;
+		bool in_progress;
+		bool aborting;
+		int vdev_id;
+		int roc_freq;
+	} scan;
+
+	struct {
+		struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
+	} mac;
+
+	/* should never be NULL; needed for regular htt rx */
+	struct ieee80211_channel *rx_channel;
+
+	/* valid during scan; needed for mgmt rx during scan */
+	struct ieee80211_channel *scan_channel;
+
+	int free_vdev_map;
+	int monitor_vdev_id;
+	bool monitor_enabled;
+	bool monitor_present;
+	unsigned int filter_flags;
+
+	struct wmi_pdev_set_wmm_params_arg wmm_params;
+	struct completion install_key_done;
+
+	struct completion vdev_setup_done;
+
+	struct workqueue_struct *workqueue;
+
+	/* prevents concurrent FW reconfiguration */
+	struct mutex conf_mutex;
+
+	/* protects shared structure data */
+	spinlock_t data_lock;
+
+	struct list_head peers;
+	wait_queue_head_t peer_mapping_wq;
+
+	struct work_struct offchan_tx_work;
+	struct sk_buff_head offchan_tx_queue;
+	struct completion offchan_tx_completed;
+	struct sk_buff *offchan_tx_skb;
+
+#ifdef CONFIG_ATH10K_DEBUGFS
+	struct ath10k_debug debug;
+#endif
+};
+
+struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
+				  enum ath10k_bus bus,
+				  const struct ath10k_hif_ops *hif_ops);
+void ath10k_core_destroy(struct ath10k *ar);
+
+int ath10k_core_register(struct ath10k *ar);
+void ath10k_core_unregister(struct ath10k *ar);
+
+int ath10k_core_target_suspend(struct ath10k *ar);
+int ath10k_core_target_resume(struct ath10k *ar);
+
+#endif /* _CORE_H_ */


^ permalink raw reply related

* [PATCH v2 07/29] ath10k: add debug.c
From: Kalle Valo @ 2013-06-01 11:24 UTC (permalink / raw)
  To: linux-wireless; +Cc: ath10k-devel
In-Reply-To: <20130601111820.32443.46794.stgit@localhost6.localdomain6>

Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
---
 drivers/net/wireless/ath/ath10k/debug.c |  503 +++++++++++++++++++++++++++++++
 1 file changed, 503 insertions(+)
 create mode 100644 drivers/net/wireless/ath/ath10k/debug.c

diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
new file mode 100644
index 0000000..499034b
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -0,0 +1,503 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * 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/debugfs.h>
+
+#include "core.h"
+#include "debug.h"
+
+static int ath10k_printk(const char *level, const char *fmt, ...)
+{
+	struct va_format vaf;
+	va_list args;
+	int rtn;
+
+	va_start(args, fmt);
+
+	vaf.fmt = fmt;
+	vaf.va = &args;
+
+	rtn = printk("%sath10k: %pV", level, &vaf);
+
+	va_end(args);
+
+	return rtn;
+}
+
+int ath10k_info(const char *fmt, ...)
+{
+	struct va_format vaf = {
+		.fmt = fmt,
+	};
+	va_list args;
+	int ret;
+
+	va_start(args, fmt);
+	vaf.va = &args;
+	ret = ath10k_printk(KERN_INFO, "%pV", &vaf);
+	trace_ath10k_log_info(&vaf);
+	va_end(args);
+
+	return ret;
+}
+EXPORT_SYMBOL(ath10k_info);
+
+int ath10k_err(const char *fmt, ...)
+{
+	struct va_format vaf = {
+		.fmt = fmt,
+	};
+	va_list args;
+	int ret;
+
+	va_start(args, fmt);
+	vaf.va = &args;
+	ret = ath10k_printk(KERN_ERR, "%pV", &vaf);
+	trace_ath10k_log_err(&vaf);
+	va_end(args);
+
+	return ret;
+}
+EXPORT_SYMBOL(ath10k_err);
+
+int ath10k_warn(const char *fmt, ...)
+{
+	struct va_format vaf = {
+		.fmt = fmt,
+	};
+	va_list args;
+	int ret = 0;
+
+	va_start(args, fmt);
+	vaf.va = &args;
+
+	if (net_ratelimit())
+		ret = ath10k_printk(KERN_WARNING, "%pV", &vaf);
+
+	trace_ath10k_log_warn(&vaf);
+
+	va_end(args);
+
+	return ret;
+}
+EXPORT_SYMBOL(ath10k_warn);
+
+#ifdef CONFIG_ATH10K_DEBUGFS
+
+void ath10k_debug_read_service_map(struct ath10k *ar,
+				   void *service_map,
+				   size_t map_size)
+{
+	memcpy(ar->debug.wmi_service_bitmap, service_map, map_size);
+}
+
+static ssize_t ath10k_read_wmi_services(struct file *file,
+					char __user *user_buf,
+					size_t count, loff_t *ppos)
+{
+	struct ath10k *ar = file->private_data;
+	char *buf;
+	unsigned int len = 0, buf_len = 1500;
+	const char *status;
+	ssize_t ret_cnt;
+	int i;
+
+	buf = kzalloc(buf_len, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	mutex_lock(&ar->conf_mutex);
+
+	if (len > buf_len)
+		len = buf_len;
+
+	for (i = 0; i < WMI_SERVICE_LAST; i++) {
+		if (WMI_SERVICE_IS_ENABLED(ar->debug.wmi_service_bitmap, i))
+			status = "enabled";
+		else
+			status = "disabled";
+
+		len += scnprintf(buf + len, buf_len - len,
+				 "0x%02x - %20s - %s\n",
+				 i, wmi_service_name(i), status);
+	}
+
+	ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+	mutex_unlock(&ar->conf_mutex);
+
+	kfree(buf);
+	return ret_cnt;
+}
+
+static const struct file_operations fops_wmi_services = {
+	.read = ath10k_read_wmi_services,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
+void ath10k_debug_read_target_stats(struct ath10k *ar,
+				    struct wmi_stats_event *ev)
+{
+	u8 *tmp = ev->data;
+	struct ath10k_target_stats *stats;
+	int num_pdev_stats, num_vdev_stats, num_peer_stats;
+	struct wmi_pdev_stats *ps;
+	int i;
+
+	mutex_lock(&ar->conf_mutex);
+
+	stats = &ar->debug.target_stats;
+
+	num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats); /* 0 or 1 */
+	num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats); /* 0 or max vdevs */
+	num_peer_stats = __le32_to_cpu(ev->num_peer_stats); /* 0 or max peers */
+
+	if (num_pdev_stats) {
+		ps = (struct wmi_pdev_stats *)tmp;
+
+		stats->ch_noise_floor = __le32_to_cpu(ps->chan_nf);
+		stats->tx_frame_count = __le32_to_cpu(ps->tx_frame_count);
+		stats->rx_frame_count = __le32_to_cpu(ps->rx_frame_count);
+		stats->rx_clear_count = __le32_to_cpu(ps->rx_clear_count);
+		stats->cycle_count = __le32_to_cpu(ps->cycle_count);
+		stats->phy_err_count = __le32_to_cpu(ps->phy_err_count);
+		stats->chan_tx_power = __le32_to_cpu(ps->chan_tx_pwr);
+
+		stats->comp_queued = __le32_to_cpu(ps->wal.tx.comp_queued);
+		stats->comp_delivered =
+			__le32_to_cpu(ps->wal.tx.comp_delivered);
+		stats->msdu_enqued = __le32_to_cpu(ps->wal.tx.msdu_enqued);
+		stats->mpdu_enqued = __le32_to_cpu(ps->wal.tx.mpdu_enqued);
+		stats->wmm_drop = __le32_to_cpu(ps->wal.tx.wmm_drop);
+		stats->local_enqued = __le32_to_cpu(ps->wal.tx.local_enqued);
+		stats->local_freed = __le32_to_cpu(ps->wal.tx.local_freed);
+		stats->hw_queued = __le32_to_cpu(ps->wal.tx.hw_queued);
+		stats->hw_reaped = __le32_to_cpu(ps->wal.tx.hw_reaped);
+		stats->underrun = __le32_to_cpu(ps->wal.tx.underrun);
+		stats->tx_abort = __le32_to_cpu(ps->wal.tx.tx_abort);
+		stats->mpdus_requed = __le32_to_cpu(ps->wal.tx.mpdus_requed);
+		stats->tx_ko = __le32_to_cpu(ps->wal.tx.tx_ko);
+		stats->data_rc = __le32_to_cpu(ps->wal.tx.data_rc);
+		stats->self_triggers = __le32_to_cpu(ps->wal.tx.self_triggers);
+		stats->sw_retry_failure =
+			__le32_to_cpu(ps->wal.tx.sw_retry_failure);
+		stats->illgl_rate_phy_err =
+			__le32_to_cpu(ps->wal.tx.illgl_rate_phy_err);
+		stats->pdev_cont_xretry =
+			__le32_to_cpu(ps->wal.tx.pdev_cont_xretry);
+		stats->pdev_tx_timeout =
+			__le32_to_cpu(ps->wal.tx.pdev_tx_timeout);
+		stats->pdev_resets = __le32_to_cpu(ps->wal.tx.pdev_resets);
+		stats->phy_underrun = __le32_to_cpu(ps->wal.tx.phy_underrun);
+		stats->txop_ovf = __le32_to_cpu(ps->wal.tx.txop_ovf);
+
+		stats->mid_ppdu_route_change =
+			__le32_to_cpu(ps->wal.rx.mid_ppdu_route_change);
+		stats->status_rcvd = __le32_to_cpu(ps->wal.rx.status_rcvd);
+		stats->r0_frags = __le32_to_cpu(ps->wal.rx.r0_frags);
+		stats->r1_frags = __le32_to_cpu(ps->wal.rx.r1_frags);
+		stats->r2_frags = __le32_to_cpu(ps->wal.rx.r2_frags);
+		stats->r3_frags = __le32_to_cpu(ps->wal.rx.r3_frags);
+		stats->htt_msdus = __le32_to_cpu(ps->wal.rx.htt_msdus);
+		stats->htt_mpdus = __le32_to_cpu(ps->wal.rx.htt_mpdus);
+		stats->loc_msdus = __le32_to_cpu(ps->wal.rx.loc_msdus);
+		stats->loc_mpdus = __le32_to_cpu(ps->wal.rx.loc_mpdus);
+		stats->oversize_amsdu =
+			__le32_to_cpu(ps->wal.rx.oversize_amsdu);
+		stats->phy_errs = __le32_to_cpu(ps->wal.rx.phy_errs);
+		stats->phy_err_drop = __le32_to_cpu(ps->wal.rx.phy_err_drop);
+		stats->mpdu_errs = __le32_to_cpu(ps->wal.rx.mpdu_errs);
+
+		tmp += sizeof(struct wmi_pdev_stats);
+	}
+
+	/* 0 or max vdevs */
+	/* Currently firmware does not support VDEV stats */
+	if (num_vdev_stats) {
+		struct wmi_vdev_stats *vdev_stats;
+
+		for (i = 0; i < num_vdev_stats; i++) {
+			vdev_stats = (struct wmi_vdev_stats *)tmp;
+			tmp += sizeof(struct wmi_vdev_stats);
+		}
+	}
+
+	if (num_peer_stats) {
+		struct wmi_peer_stats *peer_stats;
+		struct ath10k_peer_stat *s;
+
+		stats->peers = num_peer_stats;
+
+		for (i = 0; i < num_peer_stats; i++) {
+			peer_stats = (struct wmi_peer_stats *)tmp;
+			s = &stats->peer_stat[i];
+
+			WMI_MAC_ADDR_TO_CHAR_ARRAY(&peer_stats->peer_macaddr,
+						   s->peer_macaddr);
+			s->peer_rssi = __le32_to_cpu(peer_stats->peer_rssi);
+			s->peer_tx_rate =
+				__le32_to_cpu(peer_stats->peer_tx_rate);
+
+			tmp += sizeof(struct wmi_peer_stats);
+		}
+	}
+
+	mutex_unlock(&ar->conf_mutex);
+	complete(&ar->debug.event_stats_compl);
+}
+
+static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
+				    size_t count, loff_t *ppos)
+{
+	struct ath10k *ar = file->private_data;
+	struct ath10k_target_stats *fw_stats;
+	char *buf;
+	unsigned int len = 0, buf_len = 2500;
+	ssize_t ret_cnt;
+	long left;
+	int i;
+	int ret;
+
+	fw_stats = &ar->debug.target_stats;
+
+	buf = kzalloc(buf_len, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	ret = ath10k_wmi_request_stats(ar, WMI_REQUEST_PEER_STAT);
+	if (ret) {
+		ath10k_warn("could not request stats (%d)\n", ret);
+		kfree(buf);
+		return -EIO;
+	}
+
+	left = wait_for_completion_timeout(&ar->debug.event_stats_compl, 1*HZ);
+
+	if (left <= 0) {
+		kfree(buf);
+		return -ETIMEDOUT;
+	}
+
+	mutex_lock(&ar->conf_mutex);
+
+	len += scnprintf(buf + len, buf_len - len, "\n");
+	len += scnprintf(buf + len, buf_len - len, "%30s\n",
+			 "ath10k PDEV stats");
+	len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+				 "=================");
+
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "Channel noise floor", fw_stats->ch_noise_floor);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+			 "Channel TX power", fw_stats->chan_tx_power);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+			 "TX frame count", fw_stats->tx_frame_count);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+			 "RX frame count", fw_stats->rx_frame_count);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+			 "RX clear count", fw_stats->rx_clear_count);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+			 "Cycle count", fw_stats->cycle_count);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+			 "PHY error count", fw_stats->phy_err_count);
+
+	len += scnprintf(buf + len, buf_len - len, "\n");
+	len += scnprintf(buf + len, buf_len - len, "%30s\n",
+			 "ath10k PDEV TX stats");
+	len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+				 "=================");
+
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "HTT cookies queued", fw_stats->comp_queued);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "HTT cookies disp.", fw_stats->comp_delivered);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "MSDU queued", fw_stats->msdu_enqued);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "MPDU queued", fw_stats->mpdu_enqued);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "MSDUs dropped", fw_stats->wmm_drop);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "Local enqued", fw_stats->local_enqued);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "Local freed", fw_stats->local_freed);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "HW queued", fw_stats->hw_queued);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "PPDUs reaped", fw_stats->hw_reaped);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "Num underruns", fw_stats->underrun);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "PPDUs cleaned", fw_stats->tx_abort);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "MPDUs requed", fw_stats->mpdus_requed);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "Excessive retries", fw_stats->tx_ko);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "HW rate", fw_stats->data_rc);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "Sched self tiggers", fw_stats->self_triggers);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "Dropped due to SW retries",
+			 fw_stats->sw_retry_failure);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "Illegal rate phy errors",
+			 fw_stats->illgl_rate_phy_err);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "Pdev continous xretry", fw_stats->pdev_cont_xretry);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "TX timeout", fw_stats->pdev_tx_timeout);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "PDEV resets", fw_stats->pdev_resets);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "PHY underrun", fw_stats->phy_underrun);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "MPDU is more than txop limit", fw_stats->txop_ovf);
+
+	len += scnprintf(buf + len, buf_len - len, "\n");
+	len += scnprintf(buf + len, buf_len - len, "%30s\n",
+			 "ath10k PDEV RX stats");
+	len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+				 "=================");
+
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "Mid PPDU route change",
+			 fw_stats->mid_ppdu_route_change);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "Tot. number of statuses", fw_stats->status_rcvd);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "Extra frags on rings 0", fw_stats->r0_frags);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "Extra frags on rings 1", fw_stats->r1_frags);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "Extra frags on rings 2", fw_stats->r2_frags);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "Extra frags on rings 3", fw_stats->r3_frags);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "MSDUs delivered to HTT", fw_stats->htt_msdus);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "MPDUs delivered to HTT", fw_stats->htt_mpdus);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "MSDUs delivered to stack", fw_stats->loc_msdus);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "MPDUs delivered to stack", fw_stats->loc_mpdus);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "Oversized AMSUs", fw_stats->oversize_amsdu);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "PHY errors", fw_stats->phy_errs);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "PHY errors drops", fw_stats->phy_err_drop);
+	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+			 "MPDU errors (FCS, MIC, ENC)", fw_stats->mpdu_errs);
+
+	len += scnprintf(buf + len, buf_len - len, "\n");
+	len += scnprintf(buf + len, buf_len - len, "%30s\n",
+			 "ath10k PEER stats");
+	len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+				 "=================");
+
+	for (i = 0; i < fw_stats->peers; i++) {
+		len += scnprintf(buf + len, buf_len - len, "%30s %pM\n",
+				 "Peer MAC address",
+				 fw_stats->peer_stat[i].peer_macaddr);
+		len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+				 "Peer RSSI", fw_stats->peer_stat[i].peer_rssi);
+		len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+				 "Peer TX rate",
+				 fw_stats->peer_stat[i].peer_tx_rate);
+		len += scnprintf(buf + len, buf_len - len, "\n");
+	}
+
+	if (len > buf_len)
+		len = buf_len;
+
+	ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+	mutex_unlock(&ar->conf_mutex);
+
+	kfree(buf);
+	return ret_cnt;
+}
+
+static const struct file_operations fops_fw_stats = {
+	.read = ath10k_read_fw_stats,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
+int ath10k_debug_create(struct ath10k *ar)
+{
+	ar->debug.debugfs_phy = debugfs_create_dir("ath10k",
+						   ar->hw->wiphy->debugfsdir);
+
+	if (!ar->debug.debugfs_phy)
+		return -ENOMEM;
+
+	init_completion(&ar->debug.event_stats_compl);
+
+	debugfs_create_file("fw_stats", S_IRUSR, ar->debug.debugfs_phy, ar,
+			    &fops_fw_stats);
+
+	debugfs_create_file("wmi_services", S_IRUSR, ar->debug.debugfs_phy, ar,
+			    &fops_wmi_services);
+
+	return 0;
+}
+#endif /* CONFIG_ATH10K_DEBUGFS */
+
+#ifdef CONFIG_ATH10K_DEBUG
+void ath10k_dbg(enum ath10k_debug_mask mask, const char *fmt, ...)
+{
+	struct va_format vaf;
+	va_list args;
+
+	va_start(args, fmt);
+
+	vaf.fmt = fmt;
+	vaf.va = &args;
+
+	if (ath10k_debug_mask & mask)
+		ath10k_printk(KERN_DEBUG, "%pV", &vaf);
+
+	trace_ath10k_log_dbg(mask, &vaf);
+
+	va_end(args);
+}
+EXPORT_SYMBOL(ath10k_dbg);
+
+void ath10k_dbg_dump(enum ath10k_debug_mask mask,
+		     const char *msg, const char *prefix,
+		     const void *buf, size_t len)
+{
+	if (ath10k_debug_mask & mask) {
+		if (msg)
+			ath10k_dbg(mask, "%s\n", msg);
+
+		print_hex_dump_bytes(prefix, DUMP_PREFIX_OFFSET, buf, len);
+	}
+
+	/* tracing code doesn't like null strings :/ */
+	trace_ath10k_log_dbg_dump(msg ? msg : "", prefix ? prefix : "",
+				  buf, len);
+}
+EXPORT_SYMBOL(ath10k_dbg_dump);
+
+#endif /* CONFIG_ATH10K_DEBUG */


^ permalink raw reply related

* [PATCH v2 08/29] ath10k: add debug.h
From: Kalle Valo @ 2013-06-01 11:25 UTC (permalink / raw)
  To: linux-wireless; +Cc: ath10k-devel
In-Reply-To: <20130601111820.32443.46794.stgit@localhost6.localdomain6>

Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
---
 drivers/net/wireless/ath/ath10k/debug.h |   90 +++++++++++++++++++++++++++++++
 1 file changed, 90 insertions(+)
 create mode 100644 drivers/net/wireless/ath/ath10k/debug.h

diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h
new file mode 100644
index 0000000..168140c
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/debug.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * 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 _DEBUG_H_
+#define _DEBUG_H_
+
+#include <linux/types.h>
+#include "trace.h"
+
+enum ath10k_debug_mask {
+	ATH10K_DBG_PCI		= 0x00000001,
+	ATH10K_DBG_WMI		= 0x00000002,
+	ATH10K_DBG_HTC		= 0x00000004,
+	ATH10K_DBG_HTT		= 0x00000008,
+	ATH10K_DBG_MAC		= 0x00000010,
+	ATH10K_DBG_CORE		= 0x00000020,
+	ATH10K_DBG_PCI_DUMP	= 0x00000040,
+	ATH10K_DBG_HTT_DUMP	= 0x00000080,
+	ATH10K_DBG_MGMT		= 0x00000100,
+	ATH10K_DBG_DATA		= 0x00000200,
+	ATH10K_DBG_ANY		= 0xffffffff,
+};
+
+extern unsigned int ath10k_debug_mask;
+
+extern __printf(1, 2) int ath10k_info(const char *fmt, ...);
+extern __printf(1, 2) int ath10k_err(const char *fmt, ...);
+extern __printf(1, 2) int ath10k_warn(const char *fmt, ...);
+
+#ifdef CONFIG_ATH10K_DEBUGFS
+int ath10k_debug_create(struct ath10k *ar);
+void ath10k_debug_read_service_map(struct ath10k *ar,
+				   void *service_map,
+				   size_t map_size);
+void ath10k_debug_read_target_stats(struct ath10k *ar,
+				    struct wmi_stats_event *ev);
+
+#else
+static inline int ath10k_debug_create(struct ath10k *ar)
+{
+	return 0;
+}
+
+static inline void ath10k_debug_read_service_map(struct ath10k *ar,
+						 void *service_map,
+						 size_t map_size)
+{
+}
+
+static inline void ath10k_debug_read_target_stats(struct ath10k *ar,
+						  struct wmi_stats_event *ev)
+{
+}
+#endif /* CONFIG_ATH10K_DEBUGFS */
+
+#ifdef CONFIG_ATH10K_DEBUG
+extern __printf(2, 3) void ath10k_dbg(enum ath10k_debug_mask mask,
+				      const char *fmt, ...);
+void ath10k_dbg_dump(enum ath10k_debug_mask mask,
+		     const char *msg, const char *prefix,
+		     const void *buf, size_t len);
+#else /* CONFIG_ATH10K_DEBUG */
+
+static inline int ath10k_dbg(enum ath10k_debug_mask dbg_mask,
+			     const char *fmt, ...)
+{
+	return 0;
+}
+
+static inline void ath10k_dbg_dump(enum ath10k_debug_mask mask,
+				   const char *msg, const char *prefix,
+				   const void *buf, size_t len)
+{
+}
+#endif /* CONFIG_ATH10K_DEBUG */
+#endif /* _DEBUG_H_ */


^ permalink raw reply related

* [PATCH v2 09/29] ath10k: add hif.h
From: Kalle Valo @ 2013-06-01 11:25 UTC (permalink / raw)
  To: linux-wireless; +Cc: ath10k-devel
In-Reply-To: <20130601111820.32443.46794.stgit@localhost6.localdomain6>

Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
---
 drivers/net/wireless/ath/ath10k/hif.h |  137 +++++++++++++++++++++++++++++++++
 1 file changed, 137 insertions(+)
 create mode 100644 drivers/net/wireless/ath/ath10k/hif.h

diff --git a/drivers/net/wireless/ath/ath10k/hif.h b/drivers/net/wireless/ath/ath10k/hif.h
new file mode 100644
index 0000000..73a24d4
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/hif.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * 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 _HIF_H_
+#define _HIF_H_
+
+#include <linux/kernel.h>
+#include "core.h"
+
+struct ath10k_hif_cb {
+	int (*tx_completion)(struct ath10k *ar,
+			     struct sk_buff *wbuf,
+			     unsigned transfer_id);
+	int (*rx_completion)(struct ath10k *ar,
+			     struct sk_buff *wbuf,
+			     u8 pipe_id);
+};
+
+struct ath10k_hif_ops {
+	/* Send the head of a buffer to HIF for transmission to the target. */
+	int (*send_head)(struct ath10k *ar, u8 pipe_id,
+			 unsigned int transfer_id,
+			 unsigned int nbytes,
+			 struct sk_buff *buf);
+
+	/*
+	 * API to handle HIF-specific BMI message exchanges, this API is
+	 * synchronous and only allowed to be called from a context that
+	 * can block (sleep)
+	 */
+	int (*exchange_bmi_msg)(struct ath10k *ar,
+				void *request, u32 request_len,
+				void *response, u32 *response_len);
+
+	int (*start)(struct ath10k *ar);
+
+	void (*stop)(struct ath10k *ar);
+
+	int (*map_service_to_pipe)(struct ath10k *ar, u16 service_id,
+				   u8 *ul_pipe, u8 *dl_pipe,
+				   int *ul_is_polled, int *dl_is_polled);
+
+	void (*get_default_pipe)(struct ath10k *ar, u8 *ul_pipe, u8 *dl_pipe);
+
+	/*
+	 * Check if prior sends have completed.
+	 *
+	 * Check whether the pipe in question has any completed
+	 * sends that have not yet been processed.
+	 * This function is only relevant for HIF pipes that are configured
+	 * to be polled rather than interrupt-driven.
+	 */
+	void (*send_complete_check)(struct ath10k *ar, u8 pipe_id, int force);
+
+	void (*init)(struct ath10k *ar,
+		     struct ath10k_hif_cb *callbacks);
+
+	u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id);
+};
+
+
+static inline int ath10k_hif_send_head(struct ath10k *ar, u8 pipe_id,
+				       unsigned int transfer_id,
+				       unsigned int nbytes,
+				       struct sk_buff *buf)
+{
+	return ar->hif.ops->send_head(ar, pipe_id, transfer_id, nbytes, buf);
+}
+
+static inline int ath10k_hif_exchange_bmi_msg(struct ath10k *ar,
+					      void *request, u32 request_len,
+					      void *response, u32 *response_len)
+{
+	return ar->hif.ops->exchange_bmi_msg(ar, request, request_len,
+					     response, response_len);
+}
+
+static inline int ath10k_hif_start(struct ath10k *ar)
+{
+	return ar->hif.ops->start(ar);
+}
+
+static inline void ath10k_hif_stop(struct ath10k *ar)
+{
+	return ar->hif.ops->stop(ar);
+}
+
+static inline int ath10k_hif_map_service_to_pipe(struct ath10k *ar,
+						 u16 service_id,
+						 u8 *ul_pipe, u8 *dl_pipe,
+						 int *ul_is_polled,
+						 int *dl_is_polled)
+{
+	return ar->hif.ops->map_service_to_pipe(ar, service_id,
+						ul_pipe, dl_pipe,
+						ul_is_polled, dl_is_polled);
+}
+
+static inline void ath10k_hif_get_default_pipe(struct ath10k *ar,
+					       u8 *ul_pipe, u8 *dl_pipe)
+{
+	ar->hif.ops->get_default_pipe(ar, ul_pipe, dl_pipe);
+}
+
+static inline void ath10k_hif_send_complete_check(struct ath10k *ar,
+						  u8 pipe_id, int force)
+{
+	ar->hif.ops->send_complete_check(ar, pipe_id, force);
+}
+
+static inline void ath10k_hif_init(struct ath10k *ar,
+				   struct ath10k_hif_cb *callbacks)
+{
+	ar->hif.ops->init(ar, callbacks);
+}
+
+static inline u16 ath10k_hif_get_free_queue_number(struct ath10k *ar,
+						   u8 pipe_id)
+{
+	return ar->hif.ops->get_free_queue_number(ar, pipe_id);
+}
+
+#endif /* _HIF_H_ */


^ permalink raw reply related

* [PATCH v2 10/29] ath10k: add htc.c
From: Kalle Valo @ 2013-06-01 11:25 UTC (permalink / raw)
  To: linux-wireless; +Cc: ath10k-devel
In-Reply-To: <20130601111820.32443.46794.stgit@localhost6.localdomain6>

Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
---
 drivers/net/wireless/ath/ath10k/htc.c | 1000 +++++++++++++++++++++++++++++++++
 1 file changed, 1000 insertions(+)
 create mode 100644 drivers/net/wireless/ath/ath10k/htc.c

diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c
new file mode 100644
index 0000000..74363c9
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/htc.c
@@ -0,0 +1,1000 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * 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 "core.h"
+#include "hif.h"
+#include "debug.h"
+
+/********/
+/* Send */
+/********/
+
+static inline void ath10k_htc_send_complete_check(struct ath10k_htc_ep *ep,
+						  int force)
+{
+	/*
+	 * Check whether HIF has any prior sends that have finished,
+	 * have not had the post-processing done.
+	 */
+	ath10k_hif_send_complete_check(ep->htc->ar, ep->ul_pipe_id, force);
+}
+
+static void ath10k_htc_control_tx_complete(struct ath10k *ar,
+					   struct sk_buff *skb)
+{
+	kfree_skb(skb);
+}
+
+static struct sk_buff *ath10k_htc_build_tx_ctrl_skb(void *ar)
+{
+	struct sk_buff *skb;
+	struct ath10k_skb_cb *skb_cb;
+
+	skb = dev_alloc_skb(ATH10K_HTC_CONTROL_BUFFER_SIZE);
+	if (!skb) {
+		ath10k_warn("Unable to allocate ctrl skb\n");
+		return NULL;
+	}
+
+	skb_reserve(skb, 20); /* FIXME: why 20 bytes? */
+	WARN_ONCE((unsigned long)skb->data & 3, "unaligned skb");
+
+	skb_cb = ATH10K_SKB_CB(skb);
+	memset(skb_cb, 0, sizeof(*skb_cb));
+
+	ath10k_dbg(ATH10K_DBG_HTC, "%s: skb %p\n", __func__, skb);
+	return skb;
+}
+
+static inline void ath10k_htc_restore_tx_skb(struct ath10k_htc *htc,
+					     struct sk_buff *skb)
+{
+	ath10k_skb_unmap(htc->ar->dev, skb);
+	skb_pull(skb, sizeof(struct ath10k_htc_hdr));
+}
+
+static void ath10k_htc_notify_tx_completion(struct ath10k_htc_ep *ep,
+					    struct sk_buff *skb)
+{
+	ath10k_dbg(ATH10K_DBG_HTC, "%s: ep %d skb %p\n", __func__,
+		   ep->eid, skb);
+
+	ath10k_htc_restore_tx_skb(ep->htc, skb);
+
+	if (!ep->ep_ops.ep_tx_complete) {
+		ath10k_warn("no tx handler for eid %d\n", ep->eid);
+		dev_kfree_skb_any(skb);
+		return;
+	}
+
+	ep->ep_ops.ep_tx_complete(ep->htc->ar, skb);
+}
+
+/* assumes tx_lock is held */
+static bool ath10k_htc_ep_need_credit_update(struct ath10k_htc_ep *ep)
+{
+	if (!ep->tx_credit_flow_enabled)
+		return false;
+	if (ep->tx_credits >= ep->tx_credits_per_max_message)
+		return false;
+
+	ath10k_dbg(ATH10K_DBG_HTC, "HTC: endpoint %d needs credit update\n",
+		   ep->eid);
+	return true;
+}
+
+static void ath10k_htc_prepare_tx_skb(struct ath10k_htc_ep *ep,
+				      struct sk_buff *skb)
+{
+	struct ath10k_htc_hdr *hdr;
+
+	hdr = (struct ath10k_htc_hdr *)skb->data;
+	memset(hdr, 0, sizeof(*hdr));
+
+	hdr->eid = ep->eid;
+	hdr->len = __cpu_to_le16(skb->len - sizeof(*hdr));
+
+	spin_lock_bh(&ep->htc->tx_lock);
+	hdr->seq_no = ep->seq_no++;
+
+	if (ath10k_htc_ep_need_credit_update(ep))
+		hdr->flags |= ATH10K_HTC_FLAG_NEED_CREDIT_UPDATE;
+
+	spin_unlock_bh(&ep->htc->tx_lock);
+}
+
+static int ath10k_htc_issue_skb(struct ath10k_htc *htc,
+				struct ath10k_htc_ep *ep,
+				struct sk_buff *skb,
+				u8 credits)
+{
+	struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
+	int ret;
+
+	ath10k_dbg(ATH10K_DBG_HTC, "%s: ep %d skb %p\n", __func__,
+		   ep->eid, skb);
+
+	ath10k_htc_prepare_tx_skb(ep, skb);
+
+	ret = ath10k_skb_map(htc->ar->dev, skb);
+	if (ret)
+		goto err;
+
+	ret = ath10k_hif_send_head(htc->ar,
+				   ep->ul_pipe_id,
+				   ep->eid,
+				   skb->len,
+				   skb);
+	if (unlikely(ret))
+		goto err;
+
+	return 0;
+err:
+	ath10k_warn("HTC issue failed: %d\n", ret);
+
+	spin_lock_bh(&htc->tx_lock);
+	ep->tx_credits += credits;
+	spin_unlock_bh(&htc->tx_lock);
+
+	/* this is the simplest way to handle out-of-resources for non-credit
+	 * based endpoints. credit based endpoints can still get -ENOSR, but
+	 * this is highly unlikely as credit reservation should prevent that */
+	if (ret == -ENOSR) {
+		spin_lock_bh(&htc->tx_lock);
+		__skb_queue_head(&ep->tx_queue, skb);
+		spin_unlock_bh(&htc->tx_lock);
+
+		return ret;
+	}
+
+	skb_cb->is_aborted = true;
+	ath10k_htc_notify_tx_completion(ep, skb);
+
+	return ret;
+}
+
+static struct sk_buff *ath10k_htc_get_skb_credit_based(struct ath10k_htc *htc,
+						       struct ath10k_htc_ep *ep,
+						       u8 *credits)
+{
+	struct sk_buff *skb;
+	struct ath10k_skb_cb *skb_cb;
+	int credits_required;
+	int remainder;
+	unsigned int transfer_len;
+
+	lockdep_assert_held(&htc->tx_lock);
+
+	skb = __skb_dequeue(&ep->tx_queue);
+	if (!skb)
+		return NULL;
+
+	skb_cb = ATH10K_SKB_CB(skb);
+	transfer_len = skb->len;
+
+	if (likely(transfer_len <= htc->target_credit_size)) {
+		credits_required = 1;
+	} else {
+		/* figure out how many credits this message requires */
+		credits_required = transfer_len / htc->target_credit_size;
+		remainder = transfer_len % htc->target_credit_size;
+
+		if (remainder)
+			credits_required++;
+	}
+
+	ath10k_dbg(ATH10K_DBG_HTC, "Credits required %d got %d\n",
+		   credits_required, ep->tx_credits);
+
+	if (ep->tx_credits < credits_required) {
+		__skb_queue_head(&ep->tx_queue, skb);
+		return NULL;
+	}
+
+	ep->tx_credits -= credits_required;
+	*credits = credits_required;
+	return skb;
+}
+
+static void ath10k_htc_send_work(struct work_struct *work)
+{
+	struct ath10k_htc_ep *ep = container_of(work,
+					struct ath10k_htc_ep, send_work);
+	struct ath10k_htc *htc = ep->htc;
+	struct sk_buff *skb;
+	u8 credits = 0;
+	int ret;
+
+	while (true) {
+		if (ep->ul_is_polled)
+			ath10k_htc_send_complete_check(ep, 0);
+
+		spin_lock_bh(&htc->tx_lock);
+		if (ep->tx_credit_flow_enabled)
+			skb = ath10k_htc_get_skb_credit_based(htc, ep,
+							      &credits);
+		else
+			skb = __skb_dequeue(&ep->tx_queue);
+		spin_unlock_bh(&htc->tx_lock);
+
+		if (!skb)
+			break;
+
+		ret = ath10k_htc_issue_skb(htc, ep, skb, credits);
+		if (ret == -ENOSR)
+			break;
+	}
+}
+
+int ath10k_htc_send(struct ath10k_htc *htc,
+		    enum ath10k_htc_ep_id eid,
+		    struct sk_buff *skb)
+{
+	struct ath10k_htc_ep *ep = &htc->endpoint[eid];
+
+	if (eid >= ATH10K_HTC_EP_COUNT) {
+		ath10k_warn("Invalid endpoint id: %d\n", eid);
+		return -ENOENT;
+	}
+
+	skb_push(skb, sizeof(struct ath10k_htc_hdr));
+
+	spin_lock_bh(&htc->tx_lock);
+	__skb_queue_tail(&ep->tx_queue, skb);
+	spin_unlock_bh(&htc->tx_lock);
+
+	queue_work(htc->ar->workqueue, &ep->send_work);
+	return 0;
+}
+
+static int ath10k_htc_tx_completion_handler(struct ath10k *ar,
+					    struct sk_buff *skb,
+					    unsigned int eid)
+{
+	struct ath10k_htc *htc = ar->htc;
+	struct ath10k_htc_ep *ep = &htc->endpoint[eid];
+	bool stopping;
+
+	ath10k_htc_notify_tx_completion(ep, skb);
+	/* the skb now belongs to the completion handler */
+
+	spin_lock_bh(&htc->tx_lock);
+	stopping = htc->stopping;
+	spin_unlock_bh(&htc->tx_lock);
+
+	if (!ep->tx_credit_flow_enabled && !stopping)
+		/*
+		 * note: when using TX credit flow, the re-checking of
+		 * queues happens when credits flow back from the target.
+		 * in the non-TX credit case, we recheck after the packet
+		 * completes
+		 */
+		queue_work(ar->workqueue, &ep->send_work);
+
+	return 0;
+}
+
+/* flush endpoint TX queue */
+static void ath10k_htc_flush_endpoint_tx(struct ath10k_htc *htc,
+					 struct ath10k_htc_ep *ep)
+{
+	struct sk_buff *skb;
+	struct ath10k_skb_cb *skb_cb;
+
+	spin_lock_bh(&htc->tx_lock);
+	for (;;) {
+		skb = __skb_dequeue(&ep->tx_queue);
+		if (!skb)
+			break;
+
+		skb_cb = ATH10K_SKB_CB(skb);
+		skb_cb->is_aborted = true;
+		ath10k_htc_notify_tx_completion(ep, skb);
+	}
+	spin_unlock_bh(&htc->tx_lock);
+
+	cancel_work_sync(&ep->send_work);
+}
+
+/***********/
+/* Receive */
+/***********/
+
+static void
+ath10k_htc_process_credit_report(struct ath10k_htc *htc,
+				 const struct ath10k_htc_credit_report *report,
+				 int len,
+				 enum ath10k_htc_ep_id eid)
+{
+	struct ath10k_htc_ep *ep;
+	int i, n_reports;
+
+	if (len % sizeof(*report))
+		ath10k_warn("Uneven credit report len %d", len);
+
+	n_reports = len / sizeof(*report);
+
+	spin_lock_bh(&htc->tx_lock);
+	for (i = 0; i < n_reports; i++, report++) {
+		if (report->eid >= ATH10K_HTC_EP_COUNT)
+			break;
+
+		ath10k_dbg(ATH10K_DBG_HTC, "ep %d got %d credits\n",
+			   report->eid, report->credits);
+
+		ep = &htc->endpoint[report->eid];
+		ep->tx_credits += report->credits;
+
+		if (ep->tx_credits && !skb_queue_empty(&ep->tx_queue))
+			queue_work(htc->ar->workqueue, &ep->send_work);
+	}
+	spin_unlock_bh(&htc->tx_lock);
+}
+
+static int ath10k_htc_process_trailer(struct ath10k_htc *htc,
+				      u8 *buffer,
+				      int length,
+				      enum ath10k_htc_ep_id src_eid)
+{
+	int status = 0;
+	struct ath10k_htc_record *record;
+	u8 *orig_buffer;
+	int orig_length;
+	size_t len;
+
+	orig_buffer = buffer;
+	orig_length = length;
+
+	while (length > 0) {
+		record = (struct ath10k_htc_record *)buffer;
+
+		if (length < sizeof(record->hdr)) {
+			status = -EINVAL;
+			break;
+		}
+
+		if (record->hdr.len > length) {
+			/* no room left in buffer for record */
+			ath10k_warn("Invalid record length: %d\n",
+				    record->hdr.len);
+			status = -EINVAL;
+			break;
+		}
+
+		switch (record->hdr.id) {
+		case ATH10K_HTC_RECORD_CREDITS:
+			len = sizeof(struct ath10k_htc_credit_report);
+			if (record->hdr.len < len) {
+				ath10k_warn("Credit report too long\n");
+				status = -EINVAL;
+				break;
+			}
+			ath10k_htc_process_credit_report(htc,
+							 record->credit_report,
+							 record->hdr.len,
+							 src_eid);
+			break;
+		default:
+			ath10k_warn("Unhandled record: id:%d length:%d\n",
+				    record->hdr.id, record->hdr.len);
+			break;
+		}
+
+		if (status)
+			break;
+
+		/* multiple records may be present in a trailer */
+		buffer += sizeof(record->hdr) + record->hdr.len;
+		length -= sizeof(record->hdr) + record->hdr.len;
+	}
+
+	if (status)
+		ath10k_dbg_dump(ATH10K_DBG_HTC, "htc rx bad trailer", "",
+				orig_buffer, orig_length);
+
+	return status;
+}
+
+static int ath10k_htc_rx_completion_handler(struct ath10k *ar,
+					    struct sk_buff *skb,
+					    u8 pipe_id)
+{
+	int status = 0;
+	struct ath10k_htc *htc = ar->htc;
+	struct ath10k_htc_hdr *hdr;
+	struct ath10k_htc_ep *ep;
+	u16 payload_len;
+	u32 trailer_len = 0;
+	size_t min_len;
+	u8 eid;
+	bool trailer_present;
+
+	hdr = (struct ath10k_htc_hdr *)skb->data;
+	skb_pull(skb, sizeof(*hdr));
+
+	eid = hdr->eid;
+
+	if (eid >= ATH10K_HTC_EP_COUNT) {
+		ath10k_warn("HTC Rx: invalid eid %d\n", eid);
+		ath10k_dbg_dump(ATH10K_DBG_HTC, "htc bad header", "",
+				hdr, sizeof(*hdr));
+		status = -EINVAL;
+		goto out;
+	}
+
+	ep = &htc->endpoint[eid];
+
+	/*
+	 * If this endpoint that received a message from the target has
+	 * a to-target HIF pipe whose send completions are polled rather
+	 * than interrupt-driven, this is a good point to ask HIF to check
+	 * whether it has any completed sends to handle.
+	 */
+	if (ep->ul_is_polled)
+		ath10k_htc_send_complete_check(ep, 1);
+
+	payload_len = __le16_to_cpu(hdr->len);
+
+	if (payload_len + sizeof(*hdr) > ATH10K_HTC_MAX_LEN) {
+		ath10k_warn("HTC rx frame too long, len: %zu\n",
+			    payload_len + sizeof(*hdr));
+		ath10k_dbg_dump(ATH10K_DBG_HTC, "htc bad rx pkt len", "",
+				hdr, sizeof(*hdr));
+		status = -EINVAL;
+		goto out;
+	}
+
+	if (skb->len < payload_len) {
+		ath10k_dbg(ATH10K_DBG_HTC,
+			   "HTC Rx: insufficient length, got %d, expected %d\n",
+			   skb->len, payload_len);
+		ath10k_dbg_dump(ATH10K_DBG_HTC, "htc bad rx pkt len",
+				"", hdr, sizeof(*hdr));
+		status = -EINVAL;
+		goto out;
+	}
+
+	/* get flags to check for trailer */
+	trailer_present = hdr->flags & ATH10K_HTC_FLAG_TRAILER_PRESENT;
+	if (trailer_present) {
+		u8 *trailer;
+
+		trailer_len = hdr->trailer_len;
+		min_len = sizeof(struct ath10k_ath10k_htc_record_hdr);
+
+		if ((trailer_len < min_len) ||
+		    (trailer_len > payload_len)) {
+			ath10k_warn("Invalid trailer length: %d\n",
+				    trailer_len);
+			status = -EPROTO;
+			goto out;
+		}
+
+		trailer = (u8 *)hdr;
+		trailer += sizeof(*hdr);
+		trailer += payload_len;
+		trailer -= trailer_len;
+		status = ath10k_htc_process_trailer(htc, trailer,
+						    trailer_len, hdr->eid);
+		if (status)
+			goto out;
+
+		skb_trim(skb, skb->len - trailer_len);
+	}
+
+	if (((int)payload_len - (int)trailer_len) <= 0)
+		/* zero length packet with trailer data, just drop these */
+		goto out;
+
+	if (eid == ATH10K_HTC_EP_0) {
+		struct ath10k_htc_msg *msg = (struct ath10k_htc_msg *)skb->data;
+
+		switch (__le16_to_cpu(msg->hdr.message_id)) {
+		default:
+			/* handle HTC control message */
+			if (completion_done(&htc->ctl_resp)) {
+				/*
+				 * this is a fatal error, target should not be
+				 * sending unsolicited messages on the ep 0
+				 */
+				ath10k_warn("HTC rx ctrl still processing\n");
+				status = -EINVAL;
+				complete(&htc->ctl_resp);
+				goto out;
+			}
+
+			htc->control_resp_len =
+				min_t(int, skb->len,
+				      ATH10K_HTC_MAX_CTRL_MSG_LEN);
+
+			memcpy(htc->control_resp_buffer, skb->data,
+			       htc->control_resp_len);
+
+			complete(&htc->ctl_resp);
+			break;
+		case ATH10K_HTC_MSG_SEND_SUSPEND_COMPLETE:
+			htc->htc_ops.target_send_suspend_complete(ar);
+		}
+		goto out;
+	}
+
+	ath10k_dbg(ATH10K_DBG_HTC, "htc rx completion ep %d skb %p\n",
+		   eid, skb);
+	ep->ep_ops.ep_rx_complete(ar, skb);
+
+	/* skb is now owned by the rx completion handler */
+	skb = NULL;
+out:
+	kfree_skb(skb);
+
+	return status;
+}
+
+static void ath10k_htc_control_rx_complete(struct ath10k *ar,
+					   struct sk_buff *skb)
+{
+	/* This is unexpected. FW is not supposed to send regular rx on this
+	 * endpoint. */
+	ath10k_warn("unexpected htc rx\n");
+	kfree_skb(skb);
+}
+
+/***************/
+/* Init/Deinit */
+/***************/
+
+static const char *htc_service_name(enum ath10k_htc_svc_id id)
+{
+	switch (id) {
+	case ATH10K_HTC_SVC_ID_RESERVED:
+		return "Reserved";
+	case ATH10K_HTC_SVC_ID_RSVD_CTRL:
+		return "Control";
+	case ATH10K_HTC_SVC_ID_WMI_CONTROL:
+		return "WMI";
+	case ATH10K_HTC_SVC_ID_WMI_DATA_BE:
+		return "DATA BE";
+	case ATH10K_HTC_SVC_ID_WMI_DATA_BK:
+		return "DATA BK";
+	case ATH10K_HTC_SVC_ID_WMI_DATA_VI:
+		return "DATA VI";
+	case ATH10K_HTC_SVC_ID_WMI_DATA_VO:
+		return "DATA VO";
+	case ATH10K_HTC_SVC_ID_NMI_CONTROL:
+		return "NMI Control";
+	case ATH10K_HTC_SVC_ID_NMI_DATA:
+		return "NMI Data";
+	case ATH10K_HTC_SVC_ID_HTT_DATA_MSG:
+		return "HTT Data";
+	case ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS:
+		return "RAW";
+	}
+
+	return "Unknown";
+}
+
+static void ath10k_htc_reset_endpoint_states(struct ath10k_htc *htc)
+{
+	struct ath10k_htc_ep *ep;
+	int i;
+
+	for (i = ATH10K_HTC_EP_0; i < ATH10K_HTC_EP_COUNT; i++) {
+		ep = &htc->endpoint[i];
+		ep->service_id = ATH10K_HTC_SVC_ID_UNUSED;
+		ep->max_ep_message_len = 0;
+		ep->max_tx_queue_depth = 0;
+		ep->eid = i;
+		skb_queue_head_init(&ep->tx_queue);
+		ep->htc = htc;
+		ep->tx_credit_flow_enabled = true;
+		INIT_WORK(&ep->send_work, ath10k_htc_send_work);
+	}
+}
+
+static void ath10k_htc_setup_target_buffer_assignments(struct ath10k_htc *htc)
+{
+	struct ath10k_htc_svc_tx_credits *entry;
+
+	entry = &htc->service_tx_alloc[0];
+
+	/*
+	 * for PCIE allocate all credists/HTC buffers to WMI.
+	 * no buffers are used/required for data. data always
+	 * remains on host.
+	 */
+	entry++;
+	entry->service_id = ATH10K_HTC_SVC_ID_WMI_CONTROL;
+	entry->credit_allocation = htc->total_transmit_credits;
+}
+
+static u8 ath10k_htc_get_credit_allocation(struct ath10k_htc *htc,
+					   u16 service_id)
+{
+	u8 allocation = 0;
+	int i;
+
+	for (i = 0; i < ATH10K_HTC_EP_COUNT; i++) {
+		if (htc->service_tx_alloc[i].service_id == service_id)
+			allocation =
+			    htc->service_tx_alloc[i].credit_allocation;
+	}
+
+	return allocation;
+}
+
+int ath10k_htc_wait_target(struct ath10k_htc *htc)
+{
+	int status = 0;
+	struct ath10k_htc_svc_conn_req conn_req;
+	struct ath10k_htc_svc_conn_resp conn_resp;
+	struct ath10k_htc_msg *msg;
+	u16 message_id;
+	u16 credit_count;
+	u16 credit_size;
+
+	INIT_COMPLETION(htc->ctl_resp);
+
+	status = ath10k_hif_start(htc->ar);
+	if (status) {
+		ath10k_err("could not start HIF (%d)\n", status);
+		goto err_start;
+	}
+
+	status = wait_for_completion_timeout(&htc->ctl_resp,
+					     ATH10K_HTC_WAIT_TIMEOUT_HZ);
+	if (status <= 0) {
+		if (status == 0)
+			status = -ETIMEDOUT;
+
+		ath10k_err("ctl_resp never came in (%d)\n", status);
+		goto err_target;
+	}
+
+	if (htc->control_resp_len < sizeof(msg->hdr) + sizeof(msg->ready)) {
+		ath10k_err("Invalid HTC ready msg len:%d\n",
+			   htc->control_resp_len);
+
+		status = -ECOMM;
+		goto err_target;
+	}
+
+	msg = (struct ath10k_htc_msg *)htc->control_resp_buffer;
+	message_id   = __le16_to_cpu(msg->hdr.message_id);
+	credit_count = __le16_to_cpu(msg->ready.credit_count);
+	credit_size  = __le16_to_cpu(msg->ready.credit_size);
+
+	if (message_id != ATH10K_HTC_MSG_READY_ID) {
+		ath10k_err("Invalid HTC ready msg: 0x%x\n", message_id);
+		status = -ECOMM;
+		goto err_target;
+	}
+
+	htc->total_transmit_credits = credit_count;
+	htc->target_credit_size = credit_size;
+
+	ath10k_dbg(ATH10K_DBG_HTC,
+		   "Target ready! transmit resources: %d size:%d\n",
+		   htc->total_transmit_credits,
+		   htc->target_credit_size);
+
+	if ((htc->total_transmit_credits == 0) ||
+	    (htc->target_credit_size == 0)) {
+		status = -ECOMM;
+		ath10k_err("Invalid credit size received\n");
+		goto err_target;
+	}
+
+	ath10k_htc_setup_target_buffer_assignments(htc);
+
+	/* setup our pseudo HTC control endpoint connection */
+	memset(&conn_req, 0, sizeof(conn_req));
+	memset(&conn_resp, 0, sizeof(conn_resp));
+	conn_req.ep_ops.ep_tx_complete = ath10k_htc_control_tx_complete;
+	conn_req.ep_ops.ep_rx_complete = ath10k_htc_control_rx_complete;
+	conn_req.max_send_queue_depth = ATH10K_NUM_CONTROL_TX_BUFFERS;
+	conn_req.service_id = ATH10K_HTC_SVC_ID_RSVD_CTRL;
+
+	/* connect fake service */
+	status = ath10k_htc_connect_service(htc, &conn_req, &conn_resp);
+	if (status) {
+		ath10k_err("could not connect to htc service (%d)\n", status);
+		goto err_target;
+	}
+
+	return 0;
+err_target:
+	ath10k_hif_stop(htc->ar);
+err_start:
+	return status;
+}
+
+int ath10k_htc_connect_service(struct ath10k_htc *htc,
+			       struct ath10k_htc_svc_conn_req *conn_req,
+			       struct ath10k_htc_svc_conn_resp *conn_resp)
+{
+	struct ath10k_htc_msg *msg;
+	struct ath10k_htc_conn_svc *req_msg;
+	struct ath10k_htc_conn_svc_response resp_msg_dummy;
+	struct ath10k_htc_conn_svc_response *resp_msg = &resp_msg_dummy;
+	enum ath10k_htc_ep_id assigned_eid = ATH10K_HTC_EP_COUNT;
+	struct ath10k_htc_ep *ep;
+	struct sk_buff *skb;
+	unsigned int max_msg_size = 0;
+	int length, status;
+	bool disable_credit_flow_ctrl = false;
+	u16 message_id, service_id, flags = 0;
+	u8 tx_alloc = 0;
+
+	/* special case for HTC pseudo control service */
+	if (conn_req->service_id == ATH10K_HTC_SVC_ID_RSVD_CTRL) {
+		disable_credit_flow_ctrl = true;
+		assigned_eid = ATH10K_HTC_EP_0;
+		max_msg_size = ATH10K_HTC_MAX_CTRL_MSG_LEN;
+		memset(&resp_msg_dummy, 0, sizeof(resp_msg_dummy));
+		goto setup;
+	}
+
+	tx_alloc = ath10k_htc_get_credit_allocation(htc,
+						    conn_req->service_id);
+	if (!tx_alloc)
+		ath10k_warn("HTC Service %s does not allocate target credits\n",
+			    htc_service_name(conn_req->service_id));
+
+	skb = ath10k_htc_build_tx_ctrl_skb(htc->ar);
+	if (!skb) {
+		ath10k_err("Failed to allocate HTC packet\n");
+		return -ENOMEM;
+	}
+
+	length = sizeof(msg->hdr) + sizeof(msg->connect_service);
+	skb_put(skb, length);
+	memset(skb->data, 0, length);
+
+	msg = (struct ath10k_htc_msg *)skb->data;
+	msg->hdr.message_id =
+		__cpu_to_le16(ATH10K_HTC_MSG_CONNECT_SERVICE_ID);
+
+	flags |= SM(tx_alloc, ATH10K_HTC_CONN_FLAGS_RECV_ALLOC);
+
+	req_msg = &msg->connect_service;
+	req_msg->flags = __cpu_to_le16(flags);
+	req_msg->service_id = __cpu_to_le16(conn_req->service_id);
+
+	/* Only enable credit flow control for WMI ctrl service */
+	if (conn_req->service_id != ATH10K_HTC_SVC_ID_WMI_CONTROL) {
+		flags |= ATH10K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL;
+		disable_credit_flow_ctrl = true;
+	}
+
+	INIT_COMPLETION(htc->ctl_resp);
+
+	status = ath10k_htc_send(htc, ATH10K_HTC_EP_0, skb);
+	if (status) {
+		kfree_skb(skb);
+		return status;
+	}
+
+	/* wait for response */
+	status = wait_for_completion_timeout(&htc->ctl_resp,
+					     ATH10K_HTC_CONN_SVC_TIMEOUT_HZ);
+	if (status <= 0) {
+		if (status == 0)
+			status = -ETIMEDOUT;
+		ath10k_err("Service connect timeout: %d\n", status);
+		return status;
+	}
+
+	/* we controlled the buffer creation, it's aligned */
+	msg = (struct ath10k_htc_msg *)htc->control_resp_buffer;
+	resp_msg = &msg->connect_service_response;
+	message_id = __le16_to_cpu(msg->hdr.message_id);
+	service_id = __le16_to_cpu(resp_msg->service_id);
+
+	if ((message_id != ATH10K_HTC_MSG_CONNECT_SERVICE_RESP_ID) ||
+	    (htc->control_resp_len < sizeof(msg->hdr) +
+	     sizeof(msg->connect_service_response))) {
+		ath10k_err("Invalid resp message ID 0x%x", message_id);
+		return -EPROTO;
+	}
+
+	ath10k_dbg(ATH10K_DBG_HTC,
+		   "HTC Service %s connect response: status: 0x%x, assigned ep: 0x%x\n",
+		   htc_service_name(service_id),
+		   resp_msg->status, resp_msg->eid);
+
+	conn_resp->connect_resp_code = resp_msg->status;
+
+	/* check response status */
+	if (resp_msg->status != ATH10K_HTC_CONN_SVC_STATUS_SUCCESS) {
+		ath10k_err("HTC Service %s connect request failed: 0x%x)\n",
+			   htc_service_name(service_id),
+			   resp_msg->status);
+		return -EPROTO;
+	}
+
+	assigned_eid = (enum ath10k_htc_ep_id)resp_msg->eid;
+	max_msg_size = __le16_to_cpu(resp_msg->max_msg_size);
+
+setup:
+
+	if (assigned_eid >= ATH10K_HTC_EP_COUNT)
+		return -EPROTO;
+
+	if (max_msg_size == 0)
+		return -EPROTO;
+
+	ep = &htc->endpoint[assigned_eid];
+	ep->eid = assigned_eid;
+
+	if (ep->service_id != ATH10K_HTC_SVC_ID_UNUSED)
+		return -EPROTO;
+
+	/* return assigned endpoint to caller */
+	conn_resp->eid = assigned_eid;
+	conn_resp->max_msg_len = __le16_to_cpu(resp_msg->max_msg_size);
+
+	/* setup the endpoint */
+	ep->service_id = conn_req->service_id;
+	ep->max_tx_queue_depth = conn_req->max_send_queue_depth;
+	ep->max_ep_message_len = __le16_to_cpu(resp_msg->max_msg_size);
+	ep->tx_credits = tx_alloc;
+	ep->tx_credit_size = htc->target_credit_size;
+	ep->tx_credits_per_max_message = ep->max_ep_message_len /
+					 htc->target_credit_size;
+
+	if (ep->max_ep_message_len % htc->target_credit_size)
+		ep->tx_credits_per_max_message++;
+
+	/* copy all the callbacks */
+	ep->ep_ops = conn_req->ep_ops;
+
+	status = ath10k_hif_map_service_to_pipe(htc->ar,
+						ep->service_id,
+						&ep->ul_pipe_id,
+						&ep->dl_pipe_id,
+						&ep->ul_is_polled,
+						&ep->dl_is_polled);
+	if (status)
+		return status;
+
+	ath10k_dbg(ATH10K_DBG_HTC,
+		   "HTC service: %s UL pipe: %d DL pipe: %d eid: %d ready\n",
+		   htc_service_name(ep->service_id), ep->ul_pipe_id,
+		   ep->dl_pipe_id, ep->eid);
+
+	ath10k_dbg(ATH10K_DBG_HTC,
+		   "EP %d UL polled: %d, DL polled: %d\n",
+		   ep->eid, ep->ul_is_polled, ep->dl_is_polled);
+
+	if (disable_credit_flow_ctrl && ep->tx_credit_flow_enabled) {
+		ep->tx_credit_flow_enabled = false;
+		ath10k_dbg(ATH10K_DBG_HTC,
+			   "HTC service: %s eid: %d TX flow control disabled\n",
+			   htc_service_name(ep->service_id), assigned_eid);
+	}
+
+	return status;
+}
+
+struct sk_buff *ath10k_htc_alloc_skb(int size)
+{
+	struct sk_buff *skb;
+
+	skb = dev_alloc_skb(size + sizeof(struct ath10k_htc_hdr));
+	if (!skb) {
+		ath10k_warn("could not allocate HTC tx skb\n");
+		return NULL;
+	}
+
+	skb_reserve(skb, sizeof(struct ath10k_htc_hdr));
+
+	/* FW/HTC requires 4-byte aligned streams */
+	if (!IS_ALIGNED((unsigned long)skb->data, 4))
+		ath10k_warn("Unaligned HTC tx skb\n");
+
+	return skb;
+}
+
+int ath10k_htc_start(struct ath10k_htc *htc)
+{
+	struct sk_buff *skb;
+	int status = 0;
+	struct ath10k_htc_msg *msg;
+
+	skb = ath10k_htc_build_tx_ctrl_skb(htc->ar);
+	if (!skb)
+		return -ENOMEM;
+
+	skb_put(skb, sizeof(msg->hdr) + sizeof(msg->setup_complete_ext));
+	memset(skb->data, 0, skb->len);
+
+	msg = (struct ath10k_htc_msg *)skb->data;
+	msg->hdr.message_id =
+		__cpu_to_le16(ATH10K_HTC_MSG_SETUP_COMPLETE_EX_ID);
+
+	ath10k_dbg(ATH10K_DBG_HTC, "HTC is using TX credit flow control\n");
+
+	status = ath10k_htc_send(htc, ATH10K_HTC_EP_0, skb);
+	if (status) {
+		kfree_skb(skb);
+		return status;
+	}
+
+	return 0;
+}
+
+/*
+ * stop HTC communications, i.e. stop interrupt reception, and flush all
+ * queued buffers
+ */
+void ath10k_htc_stop(struct ath10k_htc *htc)
+{
+	int i;
+	struct ath10k_htc_ep *ep;
+
+	spin_lock_bh(&htc->tx_lock);
+	htc->stopping = true;
+	spin_unlock_bh(&htc->tx_lock);
+
+	for (i = ATH10K_HTC_EP_0; i < ATH10K_HTC_EP_COUNT; i++) {
+		ep = &htc->endpoint[i];
+		ath10k_htc_flush_endpoint_tx(htc, ep);
+	}
+
+	ath10k_hif_stop(htc->ar);
+	ath10k_htc_reset_endpoint_states(htc);
+}
+
+/* registered target arrival callback from the HIF layer */
+struct ath10k_htc *ath10k_htc_create(struct ath10k *ar,
+				     struct ath10k_htc_ops *htc_ops)
+{
+	struct ath10k_hif_cb htc_callbacks;
+	struct ath10k_htc_ep *ep = NULL;
+	struct ath10k_htc *htc = NULL;
+
+	/* FIXME: use struct ath10k instead */
+	htc = kzalloc(sizeof(struct ath10k_htc), GFP_KERNEL);
+	if (!htc)
+		return ERR_PTR(-ENOMEM);
+
+	spin_lock_init(&htc->tx_lock);
+
+	memcpy(&htc->htc_ops, htc_ops, sizeof(struct ath10k_htc_ops));
+
+	ath10k_htc_reset_endpoint_states(htc);
+
+	/* setup HIF layer callbacks */
+	htc_callbacks.rx_completion = ath10k_htc_rx_completion_handler;
+	htc_callbacks.tx_completion = ath10k_htc_tx_completion_handler;
+	htc->ar = ar;
+
+	/* Get HIF default pipe for HTC message exchange */
+	ep = &htc->endpoint[ATH10K_HTC_EP_0];
+
+	ath10k_hif_init(ar, &htc_callbacks);
+	ath10k_hif_get_default_pipe(ar, &ep->ul_pipe_id, &ep->dl_pipe_id);
+
+	init_completion(&htc->ctl_resp);
+
+	return htc;
+}
+
+void ath10k_htc_destroy(struct ath10k_htc *htc)
+{
+	kfree(htc);
+}


^ permalink raw reply related

* [PATCH v2 11/29] ath10k: add htc.h
From: Kalle Valo @ 2013-06-01 11:25 UTC (permalink / raw)
  To: linux-wireless; +Cc: ath10k-devel
In-Reply-To: <20130601111820.32443.46794.stgit@localhost6.localdomain6>

Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
---
 drivers/net/wireless/ath/ath10k/htc.h |  368 +++++++++++++++++++++++++++++++++
 1 file changed, 368 insertions(+)
 create mode 100644 drivers/net/wireless/ath/ath10k/htc.h

diff --git a/drivers/net/wireless/ath/ath10k/htc.h b/drivers/net/wireless/ath/ath10k/htc.h
new file mode 100644
index 0000000..fa45844
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/htc.h
@@ -0,0 +1,368 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * 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 _HTC_H_
+#define _HTC_H_
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/bug.h>
+#include <linux/skbuff.h>
+#include <linux/semaphore.h>
+#include <linux/timer.h>
+
+struct ath10k;
+
+/****************/
+/* HTC protocol */
+/****************/
+
+/*
+ * HTC - host-target control protocol
+ *
+ * tx packets are generally <htc_hdr><payload>
+ * rx packets are more complex: <htc_hdr><payload><trailer>
+ *
+ * The payload + trailer length is stored in len.
+ * To get payload-only length one needs to payload - trailer_len.
+ *
+ * Trailer contains (possibly) multiple <htc_record>.
+ * Each record is a id-len-value.
+ *
+ * HTC header flags, control_byte0, control_byte1
+ * have different meaning depending whether its tx
+ * or rx.
+ *
+ * Alignment: htc_hdr, payload and trailer are
+ * 4-byte aligned.
+ */
+
+enum ath10k_htc_tx_flags {
+	ATH10K_HTC_FLAG_NEED_CREDIT_UPDATE = 0x01,
+	ATH10K_HTC_FLAG_SEND_BUNDLE        = 0x02
+};
+
+enum ath10k_htc_rx_flags {
+	ATH10K_HTC_FLAG_TRAILER_PRESENT = 0x02,
+	ATH10K_HTC_FLAG_BUNDLE_MASK     = 0xF0
+};
+
+struct ath10k_htc_hdr {
+	u8 eid; /* @enum ath10k_htc_ep_id */
+	u8 flags; /* @enum ath10k_htc_tx_flags, ath10k_htc_rx_flags */
+	__le16 len;
+	union {
+		u8 trailer_len; /* for rx */
+		u8 control_byte0;
+	} __packed;
+	union {
+		u8 seq_no; /* for tx */
+		u8 control_byte1;
+	} __packed;
+	u8 pad0;
+	u8 pad1;
+} __packed __aligned(4);
+
+enum ath10k_ath10k_htc_msg_id {
+	ATH10K_HTC_MSG_READY_ID                = 1,
+	ATH10K_HTC_MSG_CONNECT_SERVICE_ID      = 2,
+	ATH10K_HTC_MSG_CONNECT_SERVICE_RESP_ID = 3,
+	ATH10K_HTC_MSG_SETUP_COMPLETE_ID       = 4,
+	ATH10K_HTC_MSG_SETUP_COMPLETE_EX_ID    = 5,
+	ATH10K_HTC_MSG_SEND_SUSPEND_COMPLETE   = 6
+};
+
+enum ath10k_htc_version {
+	ATH10K_HTC_VERSION_2P0 = 0x00, /* 2.0 */
+	ATH10K_HTC_VERSION_2P1 = 0x01, /* 2.1 */
+};
+
+enum ath10k_htc_conn_flags {
+	ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_ONE_FOURTH    = 0x0,
+	ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_ONE_HALF      = 0x1,
+	ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_THREE_FOURTHS = 0x2,
+	ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_UNITY         = 0x3,
+#define ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_MASK 0x3
+	ATH10K_HTC_CONN_FLAGS_REDUCE_CREDIT_DRIBBLE    = 1 << 2,
+	ATH10K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL = 1 << 3
+#define ATH10K_HTC_CONN_FLAGS_RECV_ALLOC_MASK 0xFF00
+#define ATH10K_HTC_CONN_FLAGS_RECV_ALLOC_LSB  8
+};
+
+enum ath10k_htc_conn_svc_status {
+	ATH10K_HTC_CONN_SVC_STATUS_SUCCESS      = 0,
+	ATH10K_HTC_CONN_SVC_STATUS_NOT_FOUND    = 1,
+	ATH10K_HTC_CONN_SVC_STATUS_FAILED       = 2,
+	ATH10K_HTC_CONN_SVC_STATUS_NO_RESOURCES = 3,
+	ATH10K_HTC_CONN_SVC_STATUS_NO_MORE_EP   = 4
+};
+
+struct ath10k_ath10k_htc_msg_hdr {
+	__le16 message_id; /* @enum htc_message_id */
+} __packed;
+
+struct ath10k_htc_unknown {
+	u8 pad0;
+	u8 pad1;
+} __packed;
+
+struct ath10k_htc_ready {
+	__le16 credit_count;
+	__le16 credit_size;
+	u8 max_endpoints;
+	u8 pad0;
+} __packed;
+
+struct ath10k_htc_ready_extended {
+	struct ath10k_htc_ready base;
+	u8 htc_version; /* @enum ath10k_htc_version */
+	u8 max_msgs_per_htc_bundle;
+	u8 pad0;
+	u8 pad1;
+} __packed;
+
+struct ath10k_htc_conn_svc {
+	__le16 service_id;
+	__le16 flags; /* @enum ath10k_htc_conn_flags */
+	u8 pad0;
+	u8 pad1;
+} __packed;
+
+struct ath10k_htc_conn_svc_response {
+	__le16 service_id;
+	u8 status; /* @enum ath10k_htc_conn_svc_status */
+	u8 eid;
+	__le16 max_msg_size;
+} __packed;
+
+struct ath10k_htc_setup_complete_extended {
+	u8 pad0;
+	u8 pad1;
+	__le32 flags; /* @enum htc_setup_complete_flags */
+	u8 max_msgs_per_bundled_recv;
+	u8 pad2;
+	u8 pad3;
+	u8 pad4;
+} __packed;
+
+struct ath10k_htc_msg {
+	struct ath10k_ath10k_htc_msg_hdr hdr;
+	union {
+		/* host-to-target */
+		struct ath10k_htc_conn_svc connect_service;
+		struct ath10k_htc_ready ready;
+		struct ath10k_htc_ready_extended ready_ext;
+		struct ath10k_htc_unknown unknown;
+		struct ath10k_htc_setup_complete_extended setup_complete_ext;
+
+		/* target-to-host */
+		struct ath10k_htc_conn_svc_response connect_service_response;
+	};
+} __packed __aligned(4);
+
+enum ath10k_ath10k_htc_record_id {
+	ATH10K_HTC_RECORD_NULL    = 0,
+	ATH10K_HTC_RECORD_CREDITS = 1
+};
+
+struct ath10k_ath10k_htc_record_hdr {
+	u8 id; /* @enum ath10k_ath10k_htc_record_id */
+	u8 len;
+	u8 pad0;
+	u8 pad1;
+} __packed;
+
+struct ath10k_htc_credit_report {
+	u8 eid; /* @enum ath10k_htc_ep_id */
+	u8 credits;
+	u8 pad0;
+	u8 pad1;
+} __packed;
+
+struct ath10k_htc_record {
+	struct ath10k_ath10k_htc_record_hdr hdr;
+	union {
+		struct ath10k_htc_credit_report credit_report[0];
+		u8 pauload[0];
+	};
+} __packed __aligned(4);
+
+/*
+ * note: the trailer offset is dynamic depending
+ * on payload length. this is only a struct layout draft
+ */
+struct ath10k_htc_frame {
+	struct ath10k_htc_hdr hdr;
+	union {
+		struct ath10k_htc_msg msg;
+		u8 payload[0];
+	};
+	struct ath10k_htc_record trailer[0];
+} __packed __aligned(4);
+
+
+/*******************/
+/* Host-side stuff */
+/*******************/
+
+enum ath10k_htc_svc_gid {
+	ATH10K_HTC_SVC_GRP_RSVD = 0,
+	ATH10K_HTC_SVC_GRP_WMI = 1,
+	ATH10K_HTC_SVC_GRP_NMI = 2,
+	ATH10K_HTC_SVC_GRP_HTT = 3,
+
+	ATH10K_HTC_SVC_GRP_TEST = 254,
+	ATH10K_HTC_SVC_GRP_LAST = 255,
+};
+
+#define SVC(group, idx) \
+	(int)(((int)(group) << 8) | (int)(idx))
+
+enum ath10k_htc_svc_id {
+	/* NOTE: service ID of 0x0000 is reserved and should never be used */
+	ATH10K_HTC_SVC_ID_RESERVED	= 0x0000,
+	ATH10K_HTC_SVC_ID_UNUSED	= ATH10K_HTC_SVC_ID_RESERVED,
+
+	ATH10K_HTC_SVC_ID_RSVD_CTRL	= SVC(ATH10K_HTC_SVC_GRP_RSVD, 1),
+	ATH10K_HTC_SVC_ID_WMI_CONTROL	= SVC(ATH10K_HTC_SVC_GRP_WMI, 0),
+	ATH10K_HTC_SVC_ID_WMI_DATA_BE	= SVC(ATH10K_HTC_SVC_GRP_WMI, 1),
+	ATH10K_HTC_SVC_ID_WMI_DATA_BK	= SVC(ATH10K_HTC_SVC_GRP_WMI, 2),
+	ATH10K_HTC_SVC_ID_WMI_DATA_VI	= SVC(ATH10K_HTC_SVC_GRP_WMI, 3),
+	ATH10K_HTC_SVC_ID_WMI_DATA_VO	= SVC(ATH10K_HTC_SVC_GRP_WMI, 4),
+
+	ATH10K_HTC_SVC_ID_NMI_CONTROL	= SVC(ATH10K_HTC_SVC_GRP_NMI, 0),
+	ATH10K_HTC_SVC_ID_NMI_DATA	= SVC(ATH10K_HTC_SVC_GRP_NMI, 1),
+
+	ATH10K_HTC_SVC_ID_HTT_DATA_MSG	= SVC(ATH10K_HTC_SVC_GRP_HTT, 0),
+
+	/* raw stream service (i.e. flash, tcmd, calibration apps) */
+	ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS = SVC(ATH10K_HTC_SVC_GRP_TEST, 0),
+};
+
+#undef SVC
+
+enum ath10k_htc_ep_id {
+	ATH10K_HTC_EP_UNUSED = -1,
+	ATH10K_HTC_EP_0 = 0,
+	ATH10K_HTC_EP_1 = 1,
+	ATH10K_HTC_EP_2,
+	ATH10K_HTC_EP_3,
+	ATH10K_HTC_EP_4,
+	ATH10K_HTC_EP_5,
+	ATH10K_HTC_EP_6,
+	ATH10K_HTC_EP_7,
+	ATH10K_HTC_EP_8,
+	ATH10K_HTC_EP_COUNT,
+};
+
+struct ath10k_htc_ops {
+	void (*target_send_suspend_complete)(struct ath10k *ar);
+};
+
+struct ath10k_htc_ep_ops {
+	void (*ep_tx_complete)(struct ath10k *, struct sk_buff *);
+	void (*ep_rx_complete)(struct ath10k *, struct sk_buff *);
+};
+
+/* service connection information */
+struct ath10k_htc_svc_conn_req {
+	u16 service_id;
+	struct ath10k_htc_ep_ops ep_ops;
+	int max_send_queue_depth;
+};
+
+/* service connection response information */
+struct ath10k_htc_svc_conn_resp {
+	u8 buffer_len;
+	u8 actual_len;
+	enum ath10k_htc_ep_id eid;
+	unsigned int max_msg_len;
+	u8 connect_resp_code;
+};
+
+#define ATH10K_NUM_CONTROL_TX_BUFFERS 2
+#define ATH10K_HTC_MAX_LEN 4096
+#define ATH10K_HTC_MAX_CTRL_MSG_LEN 256
+#define ATH10K_HTC_WAIT_TIMEOUT_HZ (1*HZ)
+#define ATH10K_HTC_CONTROL_BUFFER_SIZE (ATH10K_HTC_MAX_CTRL_MSG_LEN + \
+					sizeof(struct ath10k_htc_hdr))
+#define ATH10K_HTC_CONN_SVC_TIMEOUT_HZ (1*HZ)
+
+struct ath10k_htc_ep {
+	struct ath10k_htc *htc;
+	enum ath10k_htc_ep_id eid;
+	enum ath10k_htc_svc_id service_id;
+	struct ath10k_htc_ep_ops ep_ops;
+
+	int max_tx_queue_depth;
+	int max_ep_message_len;
+	u8 ul_pipe_id;
+	u8 dl_pipe_id;
+	int ul_is_polled; /* call HIF to get tx completions */
+	int dl_is_polled; /* call HIF to fetch rx (not implemented) */
+
+	struct sk_buff_head tx_queue;
+
+	u8 seq_no; /* for debugging */
+	int tx_credits;
+	int tx_credit_size;
+	int tx_credits_per_max_message;
+	bool tx_credit_flow_enabled;
+
+	struct work_struct send_work;
+};
+
+struct ath10k_htc_svc_tx_credits {
+	u16 service_id;
+	u8  credit_allocation;
+};
+
+struct ath10k_htc {
+	struct ath10k *ar;
+	struct ath10k_htc_ep endpoint[ATH10K_HTC_EP_COUNT];
+
+	/* protects endpoint and stopping fields */
+	spinlock_t tx_lock;
+
+	struct ath10k_htc_ops htc_ops;
+
+	u8 control_resp_buffer[ATH10K_HTC_MAX_CTRL_MSG_LEN];
+	int control_resp_len;
+
+	struct completion ctl_resp;
+
+	int total_transmit_credits;
+	struct ath10k_htc_svc_tx_credits service_tx_alloc[ATH10K_HTC_EP_COUNT];
+	int target_credit_size;
+
+	bool stopping;
+};
+
+struct ath10k_htc *ath10k_htc_create(struct ath10k *ar,
+				     struct ath10k_htc_ops *htc_ops);
+int ath10k_htc_wait_target(struct ath10k_htc *htc);
+int ath10k_htc_start(struct ath10k_htc *htc);
+int ath10k_htc_connect_service(struct ath10k_htc *htc,
+			       struct ath10k_htc_svc_conn_req  *conn_req,
+			       struct ath10k_htc_svc_conn_resp *conn_resp);
+int ath10k_htc_send(struct ath10k_htc *htc, enum ath10k_htc_ep_id eid,
+		    struct sk_buff *packet);
+void ath10k_htc_stop(struct ath10k_htc *htc);
+void ath10k_htc_destroy(struct ath10k_htc *htc);
+struct sk_buff *ath10k_htc_alloc_skb(int size);
+
+#endif


^ permalink raw reply related

* [PATCH v2 12/29] ath10k: add htt.c
From: Kalle Valo @ 2013-06-01 11:25 UTC (permalink / raw)
  To: linux-wireless; +Cc: ath10k-devel
In-Reply-To: <20130601111820.32443.46794.stgit@localhost6.localdomain6>

Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
---
 drivers/net/wireless/ath/ath10k/htt.c |  152 +++++++++++++++++++++++++++++++++
 1 file changed, 152 insertions(+)
 create mode 100644 drivers/net/wireless/ath/ath10k/htt.c

diff --git a/drivers/net/wireless/ath/ath10k/htt.c b/drivers/net/wireless/ath/ath10k/htt.c
new file mode 100644
index 0000000..185a546
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/htt.c
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * 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/slab.h>
+
+#include "htt.h"
+#include "core.h"
+#include "debug.h"
+
+static int ath10k_htt_htc_attach(struct ath10k_htt *htt)
+{
+	struct ath10k_htc_svc_conn_req conn_req;
+	struct ath10k_htc_svc_conn_resp conn_resp;
+	int status;
+
+	memset(&conn_req, 0, sizeof(conn_req));
+	memset(&conn_resp, 0, sizeof(conn_resp));
+
+	conn_req.ep_ops.ep_tx_complete = ath10k_htt_htc_tx_complete;
+	conn_req.ep_ops.ep_rx_complete = ath10k_htt_t2h_msg_handler;
+
+	/* connect to control service */
+	conn_req.service_id = ATH10K_HTC_SVC_ID_HTT_DATA_MSG;
+
+	status = ath10k_htc_connect_service(htt->ar->htc, &conn_req,
+					    &conn_resp);
+
+	if (status)
+		return status;
+
+	htt->eid = conn_resp.eid;
+
+	return 0;
+}
+
+struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar)
+{
+	struct ath10k_htt *htt;
+	int ret;
+
+	htt = kzalloc(sizeof(*htt), GFP_KERNEL);
+	if (!htt)
+		return NULL;
+
+	htt->ar = ar;
+	htt->max_throughput_mbps = 800;
+
+	/*
+	 * Connect to HTC service.
+	 * This has to be done before calling ath10k_htt_rx_attach,
+	 * since ath10k_htt_rx_attach involves sending a rx ring configure
+	 * message to the target.
+	 */
+	if (ath10k_htt_htc_attach(htt))
+		goto err_htc_attach;
+
+	ret = ath10k_htt_tx_attach(htt);
+	if (ret) {
+		ath10k_err("could not attach htt tx (%d)\n", ret);
+		goto err_htc_attach;
+	}
+
+	if (ath10k_htt_rx_attach(htt))
+		goto err_rx_attach;
+
+	/*
+	 * Prefetch enough data to satisfy target
+	 * classification engine.
+	 * This is for LL chips. HL chips will probably
+	 * transfer all frame in the tx fragment.
+	 */
+	htt->prefetch_len =
+		36 + /* 802.11 + qos + ht */
+		4 + /* 802.1q */
+		8 + /* llc snap */
+		2; /* ip4 dscp or ip6 priority */
+
+	return htt;
+
+err_rx_attach:
+	ath10k_htt_tx_detach(htt);
+err_htc_attach:
+	kfree(htt);
+	return NULL;
+}
+
+#define HTT_TARGET_VERSION_TIMEOUT_HZ (3*HZ)
+
+static int ath10k_htt_verify_version(struct ath10k_htt *htt)
+{
+	ath10k_dbg(ATH10K_DBG_HTT,
+		   "htt target version %d.%d; host version %d.%d\n",
+		    htt->target_version_major,
+		    htt->target_version_minor,
+		    HTT_CURRENT_VERSION_MAJOR,
+		    HTT_CURRENT_VERSION_MINOR);
+
+	if (htt->target_version_major != HTT_CURRENT_VERSION_MAJOR) {
+		ath10k_err("htt major versions are incompatible!\n");
+		return -ENOTSUPP;
+	}
+
+	if (htt->target_version_minor != HTT_CURRENT_VERSION_MINOR)
+		ath10k_warn("htt minor version differ but still compatible\n");
+
+	return 0;
+}
+
+int ath10k_htt_attach_target(struct ath10k_htt *htt)
+{
+	int status;
+
+	init_completion(&htt->target_version_received);
+
+	status = ath10k_htt_h2t_ver_req_msg(htt);
+	if (status)
+		return status;
+
+	status = wait_for_completion_timeout(&htt->target_version_received,
+						HTT_TARGET_VERSION_TIMEOUT_HZ);
+	if (status <= 0) {
+		ath10k_warn("htt version request timed out\n");
+		return -ETIMEDOUT;
+	}
+
+	status = ath10k_htt_verify_version(htt);
+	if (status)
+		return status;
+
+	return ath10k_htt_send_rx_ring_cfg_ll(htt);
+}
+
+void ath10k_htt_detach(struct ath10k_htt *htt)
+{
+	ath10k_htt_rx_detach(htt);
+	ath10k_htt_tx_detach(htt);
+	kfree(htt);
+}


^ permalink raw reply related

* [PATCH v2 13/29] ath10k: add htt.h
From: Kalle Valo @ 2013-06-01 11:25 UTC (permalink / raw)
  To: linux-wireless; +Cc: ath10k-devel
In-Reply-To: <20130601111820.32443.46794.stgit@localhost6.localdomain6>

Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
---
 drivers/net/wireless/ath/ath10k/htt.h | 1338 +++++++++++++++++++++++++++++++++
 1 file changed, 1338 insertions(+)
 create mode 100644 drivers/net/wireless/ath/ath10k/htt.h

diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h
new file mode 100644
index 0000000..a7a7aa0
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/htt.h
@@ -0,0 +1,1338 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * 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 _HTT_H_
+#define _HTT_H_
+
+#include <linux/bug.h>
+
+#include "core.h"
+#include "htc.h"
+#include "rx_desc.h"
+
+#define HTT_CURRENT_VERSION_MAJOR	2
+#define HTT_CURRENT_VERSION_MINOR	1
+
+enum htt_dbg_stats_type {
+	HTT_DBG_STATS_WAL_PDEV_TXRX = 1 << 0,
+	HTT_DBG_STATS_RX_REORDER    = 1 << 1,
+	HTT_DBG_STATS_RX_RATE_INFO  = 1 << 2,
+	HTT_DBG_STATS_TX_PPDU_LOG   = 1 << 3,
+	HTT_DBG_STATS_TX_RATE_INFO  = 1 << 4,
+	/* bits 5-23 currently reserved */
+
+	HTT_DBG_NUM_STATS /* keep this last */
+};
+
+enum htt_h2t_msg_type { /* host-to-target */
+	HTT_H2T_MSG_TYPE_VERSION_REQ        = 0,
+	HTT_H2T_MSG_TYPE_TX_FRM             = 1,
+	HTT_H2T_MSG_TYPE_RX_RING_CFG        = 2,
+	HTT_H2T_MSG_TYPE_STATS_REQ          = 3,
+	HTT_H2T_MSG_TYPE_SYNC               = 4,
+	HTT_H2T_MSG_TYPE_AGGR_CFG           = 5,
+	HTT_H2T_MSG_TYPE_FRAG_DESC_BANK_CFG = 6,
+	HTT_H2T_MSG_TYPE_MGMT_TX            = 7,
+
+	HTT_H2T_NUM_MSGS /* keep this last */
+};
+
+struct htt_cmd_hdr {
+	u8 msg_type;
+} __packed;
+
+struct htt_ver_req {
+	u8 pad[sizeof(u32) - sizeof(struct htt_cmd_hdr)];
+} __packed;
+
+/*
+ * HTT tx MSDU descriptor
+ *
+ * The HTT tx MSDU descriptor is created by the host HTT SW for each
+ * tx MSDU.  The HTT tx MSDU descriptor contains the information that
+ * the target firmware needs for the FW's tx processing, particularly
+ * for creating the HW msdu descriptor.
+ * The same HTT tx descriptor is used for HL and LL systems, though
+ * a few fields within the tx descriptor are used only by LL or
+ * only by HL.
+ * The HTT tx descriptor is defined in two manners: by a struct with
+ * bitfields, and by a series of [dword offset, bit mask, bit shift]
+ * definitions.
+ * The target should use the struct def, for simplicitly and clarity,
+ * but the host shall use the bit-mast + bit-shift defs, to be endian-
+ * neutral.  Specifically, the host shall use the get/set macros built
+ * around the mask + shift defs.
+ */
+struct htt_data_tx_desc_frag {
+	__le32 paddr;
+	__le32 len;
+} __packed;
+
+enum htt_data_tx_desc_flags0 {
+	HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT = 1 << 0,
+	HTT_DATA_TX_DESC_FLAGS0_NO_AGGR         = 1 << 1,
+	HTT_DATA_TX_DESC_FLAGS0_NO_ENCRYPT      = 1 << 2,
+	HTT_DATA_TX_DESC_FLAGS0_NO_CLASSIFY     = 1 << 3,
+	HTT_DATA_TX_DESC_FLAGS0_RSVD0           = 1 << 4
+#define HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE_MASK 0xE0
+#define HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE_LSB 5
+};
+
+enum htt_data_tx_desc_flags1 {
+#define HTT_DATA_TX_DESC_FLAGS1_VDEV_ID_BITS 6
+#define HTT_DATA_TX_DESC_FLAGS1_VDEV_ID_MASK 0x003F
+#define HTT_DATA_TX_DESC_FLAGS1_VDEV_ID_LSB  0
+#define HTT_DATA_TX_DESC_FLAGS1_EXT_TID_BITS 5
+#define HTT_DATA_TX_DESC_FLAGS1_EXT_TID_MASK 0x07C0
+#define HTT_DATA_TX_DESC_FLAGS1_EXT_TID_LSB  6
+	HTT_DATA_TX_DESC_FLAGS1_POSTPONED        = 1 << 11,
+	HTT_DATA_TX_DESC_FLAGS1_MORE_IN_BATCH    = 1 << 12,
+	HTT_DATA_TX_DESC_FLAGS1_CKSUM_L3_OFFLOAD = 1 << 13,
+	HTT_DATA_TX_DESC_FLAGS1_CKSUM_L4_OFFLOAD = 1 << 14,
+	HTT_DATA_TX_DESC_FLAGS1_RSVD1            = 1 << 15
+};
+
+enum htt_data_tx_ext_tid {
+	HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST = 16,
+	HTT_DATA_TX_EXT_TID_MGMT                = 17,
+	HTT_DATA_TX_EXT_TID_INVALID             = 31
+};
+
+#define HTT_INVALID_PEERID 0xFFFF
+
+/*
+ * htt_data_tx_desc - used for data tx path
+ *
+ * Note: vdev_id irrelevant for pkt_type == raw and no_classify == 1.
+ *       ext_tid: for qos-data frames (0-15), see %HTT_DATA_TX_EXT_TID_
+ *                for special kinds of tids
+ *       postponed: only for HL hosts. indicates if this is a resend
+ *                  (HL hosts manage queues on the host )
+ *       more_in_batch: only for HL hosts. indicates if more packets are
+ *                      pending. this allows target to wait and aggregate
+ */
+struct htt_data_tx_desc {
+	u8 flags0; /* %HTT_DATA_TX_DESC_FLAGS0_ */
+	__le16 flags1; /* %HTT_DATA_TX_DESC_FLAGS1_ */
+	__le16 len;
+	__le16 id;
+	__le32 frags_paddr;
+	__le32 peerid;
+	u8 prefetch[0]; /* start of frame, for FW classification engine */
+} __packed;
+
+enum htt_rx_ring_flags {
+	HTT_RX_RING_FLAGS_MAC80211_HDR = 1 << 0,
+	HTT_RX_RING_FLAGS_MSDU_PAYLOAD = 1 << 1,
+	HTT_RX_RING_FLAGS_PPDU_START   = 1 << 2,
+	HTT_RX_RING_FLAGS_PPDU_END     = 1 << 3,
+	HTT_RX_RING_FLAGS_MPDU_START   = 1 << 4,
+	HTT_RX_RING_FLAGS_MPDU_END     = 1 << 5,
+	HTT_RX_RING_FLAGS_MSDU_START   = 1 << 6,
+	HTT_RX_RING_FLAGS_MSDU_END     = 1 << 7,
+	HTT_RX_RING_FLAGS_RX_ATTENTION = 1 << 8,
+	HTT_RX_RING_FLAGS_FRAG_INFO    = 1 << 9,
+	HTT_RX_RING_FLAGS_UNICAST_RX   = 1 << 10,
+	HTT_RX_RING_FLAGS_MULTICAST_RX = 1 << 11,
+	HTT_RX_RING_FLAGS_CTRL_RX      = 1 << 12,
+	HTT_RX_RING_FLAGS_MGMT_RX      = 1 << 13,
+	HTT_RX_RING_FLAGS_NULL_RX      = 1 << 14,
+	HTT_RX_RING_FLAGS_PHY_DATA_RX  = 1 << 15
+};
+
+struct htt_rx_ring_setup_ring {
+	__le32 fw_idx_shadow_reg_paddr;
+	__le32 rx_ring_base_paddr;
+	__le16 rx_ring_len; /* in 4-byte words */
+	__le16 rx_ring_bufsize; /* rx skb size - in bytes */
+	__le16 flags; /* %HTT_RX_RING_FLAGS_ */
+	__le16 fw_idx_init_val;
+
+	/* the following offsets are in 4-byte units */
+	__le16 mac80211_hdr_offset;
+	__le16 msdu_payload_offset;
+	__le16 ppdu_start_offset;
+	__le16 ppdu_end_offset;
+	__le16 mpdu_start_offset;
+	__le16 mpdu_end_offset;
+	__le16 msdu_start_offset;
+	__le16 msdu_end_offset;
+	__le16 rx_attention_offset;
+	__le16 frag_info_offset;
+} __packed;
+
+struct htt_rx_ring_setup_hdr {
+	u8 num_rings; /* supported values: 1, 2 */
+	__le16 rsvd0;
+} __packed;
+
+struct htt_rx_ring_setup {
+	struct htt_rx_ring_setup_hdr hdr;
+	struct htt_rx_ring_setup_ring rings[0];
+} __packed;
+
+/*
+ * htt_stats_req - request target to send specified statistics
+ *
+ * @msg_type: hardcoded %HTT_H2T_MSG_TYPE_STATS_REQ
+ * @upload_types: see %htt_dbg_stats_type. this is 24bit field actually
+ *	so make sure its little-endian.
+ * @reset_types: see %htt_dbg_stats_type. this is 24bit field actually
+ *	so make sure its little-endian.
+ * @cfg_val: stat_type specific configuration
+ * @stat_type: see %htt_dbg_stats_type
+ * @cookie_lsb: used for confirmation message from target->host
+ * @cookie_msb: ditto as %cookie
+ */
+struct htt_stats_req {
+	u8 upload_types[3];
+	u8 rsvd0;
+	u8 reset_types[3];
+	struct {
+		u8 mpdu_bytes;
+		u8 mpdu_num_msdus;
+		u8 msdu_bytes;
+	} __packed;
+	u8 stat_type;
+	__le32 cookie_lsb;
+	__le32 cookie_msb;
+} __packed;
+
+#define HTT_STATS_REQ_CFG_STAT_TYPE_INVALID 0xff
+
+/*
+ * htt_oob_sync_req - request out-of-band sync
+ *
+ * The HTT SYNC tells the target to suspend processing of subsequent
+ * HTT host-to-target messages until some other target agent locally
+ * informs the target HTT FW that the current sync counter is equal to
+ * or greater than (in a modulo sense) the sync counter specified in
+ * the SYNC message.
+ *
+ * This allows other host-target components to synchronize their operation
+ * with HTT, e.g. to ensure that tx frames don't get transmitted until a
+ * security key has been downloaded to and activated by the target.
+ * In the absence of any explicit synchronization counter value
+ * specification, the target HTT FW will use zero as the default current
+ * sync value.
+ *
+ * The HTT target FW will suspend its host->target message processing as long
+ * as 0 < (in-band sync counter - out-of-band sync counter) & 0xff < 128.
+ */
+struct htt_oob_sync_req {
+	u8 sync_count;
+	__le16 rsvd0;
+} __packed;
+
+#define HTT_AGGR_CONF_MAX_NUM_AMSDU_SUBFRAMES_MASK 0x1F
+#define HTT_AGGR_CONF_MAX_NUM_AMSDU_SUBFRAMES_LSB  0
+
+struct htt_aggr_conf {
+	u8 max_num_ampdu_subframes;
+	union {
+		/* dont use bitfields; undefined behaviour */
+		u8 flags; /* see %HTT_AGGR_CONF_MAX_NUM_AMSDU_SUBFRAMES_ */
+		u8 max_num_amsdu_subframes:5;
+	} __packed;
+} __packed;
+
+#define HTT_MGMT_FRM_HDR_DOWNLOAD_LEN 32
+
+struct htt_mgmt_tx_desc {
+	u8 pad[sizeof(u32) - sizeof(struct htt_cmd_hdr)];
+	__le32 msdu_paddr;
+	__le32 desc_id;
+	__le32 len;
+	__le32 vdev_id;
+	u8 hdr[HTT_MGMT_FRM_HDR_DOWNLOAD_LEN];
+} __packed;
+
+enum htt_mgmt_tx_status {
+	HTT_MGMT_TX_STATUS_OK    = 0,
+	HTT_MGMT_TX_STATUS_RETRY = 1,
+	HTT_MGMT_TX_STATUS_DROP  = 2
+};
+
+/*=== target -> host messages ===============================================*/
+
+
+enum htt_t2h_msg_type {
+	HTT_T2H_MSG_TYPE_VERSION_CONF		= 0x0,
+	HTT_T2H_MSG_TYPE_RX_IND			= 0x1,
+	HTT_T2H_MSG_TYPE_RX_FLUSH		= 0x2,
+	HTT_T2H_MSG_TYPE_PEER_MAP		= 0x3,
+	HTT_T2H_MSG_TYPE_PEER_UNMAP		= 0x4,
+	HTT_T2H_MSG_TYPE_RX_ADDBA		= 0x5,
+	HTT_T2H_MSG_TYPE_RX_DELBA		= 0x6,
+	HTT_T2H_MSG_TYPE_TX_COMPL_IND		= 0x7,
+	HTT_T2H_MSG_TYPE_PKTLOG			= 0x8,
+	HTT_T2H_MSG_TYPE_STATS_CONF		= 0x9,
+	HTT_T2H_MSG_TYPE_RX_FRAG_IND		= 0xa,
+	HTT_T2H_MSG_TYPE_SEC_IND		= 0xb,
+	HTT_T2H_MSG_TYPE_RC_UPDATE_IND		= 0xc,
+	HTT_T2H_MSG_TYPE_TX_INSPECT_IND		= 0xd,
+	HTT_T2H_MSG_TYPE_MGMT_TX_COMPLETION	= 0xe,
+	HTT_T2H_MSG_TYPE_TEST,
+	/* keep this last */
+	HTT_T2H_NUM_MSGS
+};
+
+/*
+ * htt_resp_hdr - header for target-to-host messages
+ *
+ * msg_type: see htt_t2h_msg_type
+ */
+struct htt_resp_hdr {
+	u8 msg_type;
+} __packed;
+
+#define HTT_RESP_HDR_MSG_TYPE_OFFSET 0
+#define HTT_RESP_HDR_MSG_TYPE_MASK   0xff
+#define HTT_RESP_HDR_MSG_TYPE_LSB    0
+
+/* htt_ver_resp - response sent for htt_ver_req */
+struct htt_ver_resp {
+	u8 minor;
+	u8 major;
+	u8 rsvd0;
+} __packed;
+
+struct htt_mgmt_tx_completion {
+	u8 rsvd0;
+	u8 rsvd1;
+	u8 rsvd2;
+	__le32 desc_id;
+	__le32 status;
+} __packed;
+
+#define HTT_RX_INDICATION_INFO0_EXT_TID_MASK  (0x3F)
+#define HTT_RX_INDICATION_INFO0_EXT_TID_LSB   (0)
+#define HTT_RX_INDICATION_INFO0_FLUSH_VALID   (1 << 6)
+#define HTT_RX_INDICATION_INFO0_RELEASE_VALID (1 << 7)
+
+#define HTT_RX_INDICATION_INFO1_FLUSH_START_SEQNO_MASK   0x0000003F
+#define HTT_RX_INDICATION_INFO1_FLUSH_START_SEQNO_LSB    0
+#define HTT_RX_INDICATION_INFO1_FLUSH_END_SEQNO_MASK     0x00000FC0
+#define HTT_RX_INDICATION_INFO1_FLUSH_END_SEQNO_LSB      6
+#define HTT_RX_INDICATION_INFO1_RELEASE_START_SEQNO_MASK 0x0003F000
+#define HTT_RX_INDICATION_INFO1_RELEASE_START_SEQNO_LSB  12
+#define HTT_RX_INDICATION_INFO1_RELEASE_END_SEQNO_MASK   0x00FC0000
+#define HTT_RX_INDICATION_INFO1_RELEASE_END_SEQNO_LSB    18
+#define HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES_MASK     0xFF000000
+#define HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES_LSB      24
+
+struct htt_rx_indication_hdr {
+	u8 info0; /* %HTT_RX_INDICATION_INFO0_ */
+	__le16 peer_id;
+	__le32 info1; /* %HTT_RX_INDICATION_INFO1_ */
+} __packed;
+
+#define HTT_RX_INDICATION_INFO0_PHY_ERR_VALID    (1 << 0)
+#define HTT_RX_INDICATION_INFO0_LEGACY_RATE_MASK (0x1E)
+#define HTT_RX_INDICATION_INFO0_LEGACY_RATE_LSB  (1)
+#define HTT_RX_INDICATION_INFO0_LEGACY_RATE_CCK  (1 << 5)
+#define HTT_RX_INDICATION_INFO0_END_VALID        (1 << 6)
+#define HTT_RX_INDICATION_INFO0_START_VALID      (1 << 7)
+
+#define HTT_RX_INDICATION_INFO1_VHT_SIG_A1_MASK    0x00FFFFFF
+#define HTT_RX_INDICATION_INFO1_VHT_SIG_A1_LSB     0
+#define HTT_RX_INDICATION_INFO1_PREAMBLE_TYPE_MASK 0xFF000000
+#define HTT_RX_INDICATION_INFO1_PREAMBLE_TYPE_LSB  24
+
+#define HTT_RX_INDICATION_INFO2_VHT_SIG_A1_MASK 0x00FFFFFF
+#define HTT_RX_INDICATION_INFO2_VHT_SIG_A1_LSB  0
+#define HTT_RX_INDICATION_INFO2_SERVICE_MASK    0xFF000000
+#define HTT_RX_INDICATION_INFO2_SERVICE_LSB     24
+
+enum htt_rx_legacy_rate {
+	HTT_RX_OFDM_48 = 0,
+	HTT_RX_OFDM_24 = 1,
+	HTT_RX_OFDM_12,
+	HTT_RX_OFDM_6,
+	HTT_RX_OFDM_54,
+	HTT_RX_OFDM_36,
+	HTT_RX_OFDM_18,
+	HTT_RX_OFDM_9,
+
+	/* long preamble */
+	HTT_RX_CCK_11_LP = 0,
+	HTT_RX_CCK_5_5_LP = 1,
+	HTT_RX_CCK_2_LP,
+	HTT_RX_CCK_1_LP,
+	/* short preamble */
+	HTT_RX_CCK_11_SP,
+	HTT_RX_CCK_5_5_SP,
+	HTT_RX_CCK_2_SP
+};
+
+enum htt_rx_legacy_rate_type {
+	HTT_RX_LEGACY_RATE_OFDM = 0,
+	HTT_RX_LEGACY_RATE_CCK
+};
+
+enum htt_rx_preamble_type {
+	HTT_RX_LEGACY        = 0x4,
+	HTT_RX_HT            = 0x8,
+	HTT_RX_HT_WITH_TXBF  = 0x9,
+	HTT_RX_VHT           = 0xC,
+	HTT_RX_VHT_WITH_TXBF = 0xD,
+};
+
+/*
+ * Fields: phy_err_valid, phy_err_code, tsf,
+ * usec_timestamp, sub_usec_timestamp
+ * ..are valid only if end_valid == 1.
+ *
+ * Fields: rssi_chains, legacy_rate_type,
+ * legacy_rate_cck, preamble_type, service,
+ * vht_sig_*
+ * ..are valid only if start_valid == 1;
+ */
+struct htt_rx_indication_ppdu {
+	u8 combined_rssi;
+	u8 sub_usec_timestamp;
+	u8 phy_err_code;
+	u8 info0; /* HTT_RX_INDICATION_INFO0_ */
+	struct {
+		u8 pri20_db;
+		u8 ext20_db;
+		u8 ext40_db;
+		u8 ext80_db;
+	} __packed rssi_chains[4];
+	__le32 tsf;
+	__le32 usec_timestamp;
+	__le32 info1; /* HTT_RX_INDICATION_INFO1_ */
+	__le32 info2; /* HTT_RX_INDICATION_INFO2_ */
+} __packed;
+
+enum htt_rx_mpdu_status {
+	HTT_RX_IND_MPDU_STATUS_UNKNOWN = 0x0,
+	HTT_RX_IND_MPDU_STATUS_OK,
+	HTT_RX_IND_MPDU_STATUS_ERR_FCS,
+	HTT_RX_IND_MPDU_STATUS_ERR_DUP,
+	HTT_RX_IND_MPDU_STATUS_ERR_REPLAY,
+	HTT_RX_IND_MPDU_STATUS_ERR_INV_PEER,
+	/* only accept EAPOL frames */
+	HTT_RX_IND_MPDU_STATUS_UNAUTH_PEER,
+	HTT_RX_IND_MPDU_STATUS_OUT_OF_SYNC,
+	/* Non-data in promiscous mode */
+	HTT_RX_IND_MPDU_STATUS_MGMT_CTRL,
+	HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR,
+	HTT_RX_IND_MPDU_STATUS_DECRYPT_ERR,
+	HTT_RX_IND_MPDU_STATUS_MPDU_LENGTH_ERR,
+	HTT_RX_IND_MPDU_STATUS_ENCRYPT_REQUIRED_ERR,
+	HTT_RX_IND_MPDU_STATUS_PRIVACY_ERR,
+
+	/*
+	 * MISC: discard for unspecified reasons.
+	 * Leave this enum value last.
+	 */
+	HTT_RX_IND_MPDU_STATUS_ERR_MISC = 0xFF
+};
+
+struct htt_rx_indication_mpdu_range {
+	u8 mpdu_count;
+	u8 mpdu_range_status; /* %htt_rx_mpdu_status */
+	u8 pad0;
+	u8 pad1;
+} __packed;
+
+struct htt_rx_indication_prefix {
+	__le16 fw_rx_desc_bytes;
+	u8 pad0;
+	u8 pad1;
+};
+
+struct htt_rx_indication {
+	struct htt_rx_indication_hdr hdr;
+	struct htt_rx_indication_ppdu ppdu;
+	struct htt_rx_indication_prefix prefix;
+
+	/*
+	 * the following fields are both dynamically sized, so
+	 * take care addressing them
+	 */
+
+	/* the size of this is %fw_rx_desc_bytes */
+	struct fw_rx_desc_base fw_desc;
+
+	/*
+	 * %mpdu_ranges starts after &%prefix + roundup(%fw_rx_desc_bytes, 4)
+	 * and has %num_mpdu_ranges elements.
+	 */
+	struct htt_rx_indication_mpdu_range mpdu_ranges[0];
+} __packed;
+
+static inline struct htt_rx_indication_mpdu_range *
+		htt_rx_ind_get_mpdu_ranges(struct htt_rx_indication *rx_ind)
+{
+	void *ptr = rx_ind;
+
+	ptr += sizeof(rx_ind->hdr)
+	     + sizeof(rx_ind->ppdu)
+	     + sizeof(rx_ind->prefix)
+	     + roundup(__le16_to_cpu(rx_ind->prefix.fw_rx_desc_bytes), 4);
+	return ptr;
+}
+
+enum htt_rx_flush_mpdu_status {
+	HTT_RX_FLUSH_MPDU_DISCARD = 0,
+	HTT_RX_FLUSH_MPDU_REORDER = 1,
+};
+
+/*
+ * htt_rx_flush - discard or reorder given range of mpdus
+ *
+ * Note: host must check if all sequence numbers between
+ *	[seq_num_start, seq_num_end-1] are valid.
+ */
+struct htt_rx_flush {
+	__le16 peer_id;
+	u8 tid;
+	u8 rsvd0;
+	u8 mpdu_status; /* %htt_rx_flush_mpdu_status */
+	u8 seq_num_start; /* it is 6 LSBs of 802.11 seq no */
+	u8 seq_num_end; /* it is 6 LSBs of 802.11 seq no */
+};
+
+struct htt_rx_peer_map {
+	u8 vdev_id;
+	__le16 peer_id;
+	u8 addr[6];
+	u8 rsvd0;
+	u8 rsvd1;
+} __packed;
+
+struct htt_rx_peer_unmap {
+	u8 rsvd0;
+	__le16 peer_id;
+} __packed;
+
+enum htt_security_types {
+	HTT_SECURITY_NONE,
+	HTT_SECURITY_WEP128,
+	HTT_SECURITY_WEP104,
+	HTT_SECURITY_WEP40,
+	HTT_SECURITY_TKIP,
+	HTT_SECURITY_TKIP_NOMIC,
+	HTT_SECURITY_AES_CCMP,
+	HTT_SECURITY_WAPI,
+
+	HTT_NUM_SECURITY_TYPES /* keep this last! */
+};
+
+enum htt_security_flags {
+#define HTT_SECURITY_TYPE_MASK 0x7F
+#define HTT_SECURITY_TYPE_LSB  0
+	HTT_SECURITY_IS_UNICAST = 1 << 7
+};
+
+struct htt_security_indication {
+	union {
+		/* dont use bitfields; undefined behaviour */
+		u8 flags; /* %htt_security_flags */
+		struct {
+			u8 security_type:7, /* %htt_security_types */
+			   is_unicast:1;
+		} __packed;
+	} __packed;
+	__le16 peer_id;
+	u8 michael_key[8];
+	u8 wapi_rsc[16];
+} __packed;
+
+#define HTT_RX_BA_INFO0_TID_MASK     0x000F
+#define HTT_RX_BA_INFO0_TID_LSB      0
+#define HTT_RX_BA_INFO0_PEER_ID_MASK 0xFFF0
+#define HTT_RX_BA_INFO0_PEER_ID_LSB  4
+
+struct htt_rx_addba {
+	u8 window_size;
+	__le16 info0; /* %HTT_RX_BA_INFO0_ */
+} __packed;
+
+struct htt_rx_delba {
+	u8 rsvd0;
+	__le16 info0; /* %HTT_RX_BA_INFO0_ */
+} __packed;
+
+enum htt_data_tx_status {
+	HTT_DATA_TX_STATUS_OK            = 0,
+	HTT_DATA_TX_STATUS_DISCARD       = 1,
+	HTT_DATA_TX_STATUS_NO_ACK        = 2,
+	HTT_DATA_TX_STATUS_POSTPONE      = 3, /* HL only */
+	HTT_DATA_TX_STATUS_DOWNLOAD_FAIL = 128
+};
+
+enum htt_data_tx_flags {
+#define HTT_DATA_TX_STATUS_MASK 0x07
+#define HTT_DATA_TX_STATUS_LSB  0
+#define HTT_DATA_TX_TID_MASK    0x78
+#define HTT_DATA_TX_TID_LSB     3
+	HTT_DATA_TX_TID_INVALID = 1 << 7
+};
+
+#define HTT_TX_COMPL_INV_MSDU_ID 0xFFFF
+
+struct htt_data_tx_completion {
+	union {
+		u8 flags;
+		struct {
+			u8 status:3,
+			   tid:4,
+			   tid_invalid:1;
+		} __packed;
+	} __packed;
+	u8 num_msdus;
+	u8 rsvd0;
+	__le16 msdus[0]; /* variable length based on %num_msdus */
+} __packed;
+
+struct htt_tx_compl_ind_base {
+	u32 hdr;
+	u16 payload[1/*or more*/];
+} __packed;
+
+struct htt_rc_tx_done_params {
+	u32 rate_code;
+	u32 rate_code_flags;
+	u32 flags;
+	u32 num_enqued; /* 1 for non-AMPDU */
+	u32 num_retries;
+	u32 num_failed; /* for AMPDU */
+	u32 ack_rssi;
+	u32 time_stamp;
+	u32 is_probe;
+};
+
+struct htt_rc_update {
+	u8 vdev_id;
+	__le16 peer_id;
+	u8 addr[6];
+	u8 num_elems;
+	u8 rsvd0;
+	struct htt_rc_tx_done_params params[0]; /* variable length %num_elems */
+} __packed;
+
+/* see htt_rx_indication for similar fields and descriptions */
+struct htt_rx_fragment_indication {
+	union {
+		u8 info0; /* %HTT_RX_FRAG_IND_INFO0_ */
+		struct {
+			u8 ext_tid:5,
+			   flush_valid:1;
+		} __packed;
+	} __packed;
+	__le16 peer_id;
+	__le32 info1; /* %HTT_RX_FRAG_IND_INFO1_ */
+	__le16 fw_rx_desc_bytes;
+	__le16 rsvd0;
+
+	u8 fw_msdu_rx_desc[0];
+} __packed;
+
+#define HTT_RX_FRAG_IND_INFO0_EXT_TID_MASK     0x1F
+#define HTT_RX_FRAG_IND_INFO0_EXT_TID_LSB      0
+#define HTT_RX_FRAG_IND_INFO0_FLUSH_VALID_MASK 0x20
+#define HTT_RX_FRAG_IND_INFO0_FLUSH_VALID_LSB  5
+
+#define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_START_MASK 0x0000003F
+#define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_START_LSB  0
+#define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_END_MASK   0x00000FC0
+#define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_END_LSB    6
+
+/*
+ * target -> host test message definition
+ *
+ * The following field definitions describe the format of the test
+ * message sent from the target to the host.
+ * The message consists of a 4-octet header, followed by a variable
+ * number of 32-bit integer values, followed by a variable number
+ * of 8-bit character values.
+ *
+ * |31                         16|15           8|7            0|
+ * |-----------------------------------------------------------|
+ * |          num chars          |   num ints   |   msg type   |
+ * |-----------------------------------------------------------|
+ * |                           int 0                           |
+ * |-----------------------------------------------------------|
+ * |                           int 1                           |
+ * |-----------------------------------------------------------|
+ * |                            ...                            |
+ * |-----------------------------------------------------------|
+ * |    char 3    |    char 2    |    char 1    |    char 0    |
+ * |-----------------------------------------------------------|
+ * |              |              |      ...     |    char 4    |
+ * |-----------------------------------------------------------|
+ *   - MSG_TYPE
+ *     Bits 7:0
+ *     Purpose: identifies this as a test message
+ *     Value: HTT_MSG_TYPE_TEST
+ *   - NUM_INTS
+ *     Bits 15:8
+ *     Purpose: indicate how many 32-bit integers follow the message header
+ *   - NUM_CHARS
+ *     Bits 31:16
+ *     Purpose: indicate how many 8-bit charaters follow the series of integers
+ */
+struct htt_rx_test {
+	u8 num_ints;
+	__le16 num_chars;
+
+	/* payload consists of 2 lists:
+	 *  a) num_ints * sizeof(__le32)
+	 *  b) num_chars * sizeof(u8) aligned to 4bytes */
+	u8 payload[0];
+} __packed;
+
+static inline __le32 *htt_rx_test_get_ints(struct htt_rx_test *rx_test)
+{
+	return (__le32 *)rx_test->payload;
+}
+
+static inline u8 *htt_rx_test_get_chars(struct htt_rx_test *rx_test)
+{
+	return rx_test->payload + (rx_test->num_ints * sizeof(__le32));
+}
+
+/*
+ * target -> host packet log message
+ *
+ * The following field definitions describe the format of the packet log
+ * message sent from the target to the host.
+ * The message consists of a 4-octet header,followed by a variable number
+ * of 32-bit character values.
+ *
+ * |31          24|23          16|15           8|7            0|
+ * |-----------------------------------------------------------|
+ * |              |              |              |   msg type   |
+ * |-----------------------------------------------------------|
+ * |                        payload                            |
+ * |-----------------------------------------------------------|
+ *   - MSG_TYPE
+ *     Bits 7:0
+ *     Purpose: identifies this as a test message
+ *     Value: HTT_MSG_TYPE_PACKETLOG
+ */
+struct htt_pktlog_msg {
+	u8 pad[3];
+	__le32 payload[1 /* or more */];
+} __packed;
+
+struct htt_dbg_stats_rx_reorder_stats {
+	/* Non QoS MPDUs received */
+	__le32 deliver_non_qos;
+
+	/* MPDUs received in-order */
+	__le32 deliver_in_order;
+
+	/* Flush due to reorder timer expired */
+	__le32 deliver_flush_timeout;
+
+	/* Flush due to move out of window */
+	__le32 deliver_flush_oow;
+
+	/* Flush due to DELBA */
+	__le32 deliver_flush_delba;
+
+	/* MPDUs dropped due to FCS error */
+	__le32 fcs_error;
+
+	/* MPDUs dropped due to monitor mode non-data packet */
+	__le32 mgmt_ctrl;
+
+	/* MPDUs dropped due to invalid peer */
+	__le32 invalid_peer;
+
+	/* MPDUs dropped due to duplication (non aggregation) */
+	__le32 dup_non_aggr;
+
+	/* MPDUs dropped due to processed before */
+	__le32 dup_past;
+
+	/* MPDUs dropped due to duplicate in reorder queue */
+	__le32 dup_in_reorder;
+
+	/* Reorder timeout happened */
+	__le32 reorder_timeout;
+
+	/* invalid bar ssn */
+	__le32 invalid_bar_ssn;
+
+	/* reorder reset due to bar ssn */
+	__le32 ssn_reset;
+};
+
+struct htt_dbg_stats_wal_tx_stats {
+	/* Num HTT cookies queued to dispatch list */
+	__le32 comp_queued;
+
+	/* Num HTT cookies dispatched */
+	__le32 comp_delivered;
+
+	/* Num MSDU queued to WAL */
+	__le32 msdu_enqued;
+
+	/* Num MPDU queue to WAL */
+	__le32 mpdu_enqued;
+
+	/* Num MSDUs dropped by WMM limit */
+	__le32 wmm_drop;
+
+	/* Num Local frames queued */
+	__le32 local_enqued;
+
+	/* Num Local frames done */
+	__le32 local_freed;
+
+	/* Num queued to HW */
+	__le32 hw_queued;
+
+	/* Num PPDU reaped from HW */
+	__le32 hw_reaped;
+
+	/* Num underruns */
+	__le32 underrun;
+
+	/* Num PPDUs cleaned up in TX abort */
+	__le32 tx_abort;
+
+	/* Num MPDUs requed by SW */
+	__le32 mpdus_requed;
+
+	/* excessive retries */
+	__le32 tx_ko;
+
+	/* data hw rate code */
+	__le32 data_rc;
+
+	/* Scheduler self triggers */
+	__le32 self_triggers;
+
+	/* frames dropped due to excessive sw retries */
+	__le32 sw_retry_failure;
+
+	/* illegal rate phy errors  */
+	__le32 illgl_rate_phy_err;
+
+	/* wal pdev continous xretry */
+	__le32 pdev_cont_xretry;
+
+	/* wal pdev continous xretry */
+	__le32 pdev_tx_timeout;
+
+	/* wal pdev resets  */
+	__le32 pdev_resets;
+
+	__le32 phy_underrun;
+
+	/* MPDU is more than txop limit */
+	__le32 txop_ovf;
+} __packed;
+
+struct htt_dbg_stats_wal_rx_stats {
+	/* Cnts any change in ring routing mid-ppdu */
+	__le32 mid_ppdu_route_change;
+
+	/* Total number of statuses processed */
+	__le32 status_rcvd;
+
+	/* Extra frags on rings 0-3 */
+	__le32 r0_frags;
+	__le32 r1_frags;
+	__le32 r2_frags;
+	__le32 r3_frags;
+
+	/* MSDUs / MPDUs delivered to HTT */
+	__le32 htt_msdus;
+	__le32 htt_mpdus;
+
+	/* MSDUs / MPDUs delivered to local stack */
+	__le32 loc_msdus;
+	__le32 loc_mpdus;
+
+	/* AMSDUs that have more MSDUs than the status ring size */
+	__le32 oversize_amsdu;
+
+	/* Number of PHY errors */
+	__le32 phy_errs;
+
+	/* Number of PHY errors drops */
+	__le32 phy_err_drop;
+
+	/* Number of mpdu errors - FCS, MIC, ENC etc. */
+	__le32 mpdu_errs;
+} __packed;
+
+struct htt_dbg_stats_wal_peer_stats {
+	__le32 dummy; /* REMOVE THIS ONCE REAL PEER STAT COUNTERS ARE ADDED */
+} __packed;
+
+struct htt_dbg_stats_wal_pdev_txrx {
+	struct htt_dbg_stats_wal_tx_stats tx_stats;
+	struct htt_dbg_stats_wal_rx_stats rx_stats;
+	struct htt_dbg_stats_wal_peer_stats peer_stats;
+} __packed;
+
+struct htt_dbg_stats_rx_rate_info {
+	__le32 mcs[10];
+	__le32 sgi[10];
+	__le32 nss[4];
+	__le32 stbc[10];
+	__le32 bw[3];
+	__le32 pream[6];
+	__le32 ldpc;
+	__le32 txbf;
+};
+
+/*
+ * htt_dbg_stats_status -
+ * present -     The requested stats have been delivered in full.
+ *               This indicates that either the stats information was contained
+ *               in its entirety within this message, or else this message
+ *               completes the delivery of the requested stats info that was
+ *               partially delivered through earlier STATS_CONF messages.
+ * partial -     The requested stats have been delivered in part.
+ *               One or more subsequent STATS_CONF messages with the same
+ *               cookie value will be sent to deliver the remainder of the
+ *               information.
+ * error -       The requested stats could not be delivered, for example due
+ *               to a shortage of memory to construct a message holding the
+ *               requested stats.
+ * invalid -     The requested stat type is either not recognized, or the
+ *               target is configured to not gather the stats type in question.
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * series_done - This special value indicates that no further stats info
+ *               elements are present within a series of stats info elems
+ *               (within a stats upload confirmation message).
+ */
+enum htt_dbg_stats_status {
+	HTT_DBG_STATS_STATUS_PRESENT     = 0,
+	HTT_DBG_STATS_STATUS_PARTIAL     = 1,
+	HTT_DBG_STATS_STATUS_ERROR       = 2,
+	HTT_DBG_STATS_STATUS_INVALID     = 3,
+	HTT_DBG_STATS_STATUS_SERIES_DONE = 7
+};
+
+/*
+ * target -> host statistics upload
+ *
+ * The following field definitions describe the format of the HTT target
+ * to host stats upload confirmation message.
+ * The message contains a cookie echoed from the HTT host->target stats
+ * upload request, which identifies which request the confirmation is
+ * for, and a series of tag-length-value stats information elements.
+ * The tag-length header for each stats info element also includes a
+ * status field, to indicate whether the request for the stat type in
+ * question was fully met, partially met, unable to be met, or invalid
+ * (if the stat type in question is disabled in the target).
+ * A special value of all 1's in this status field is used to indicate
+ * the end of the series of stats info elements.
+ *
+ *
+ * |31                         16|15           8|7   5|4       0|
+ * |------------------------------------------------------------|
+ * |                  reserved                  |    msg type   |
+ * |------------------------------------------------------------|
+ * |                        cookie LSBs                         |
+ * |------------------------------------------------------------|
+ * |                        cookie MSBs                         |
+ * |------------------------------------------------------------|
+ * |      stats entry length     |   reserved   |  S  |stat type|
+ * |------------------------------------------------------------|
+ * |                                                            |
+ * |                  type-specific stats info                  |
+ * |                                                            |
+ * |------------------------------------------------------------|
+ * |      stats entry length     |   reserved   |  S  |stat type|
+ * |------------------------------------------------------------|
+ * |                                                            |
+ * |                  type-specific stats info                  |
+ * |                                                            |
+ * |------------------------------------------------------------|
+ * |              n/a            |   reserved   | 111 |   n/a   |
+ * |------------------------------------------------------------|
+ * Header fields:
+ *  - MSG_TYPE
+ *    Bits 7:0
+ *    Purpose: identifies this is a statistics upload confirmation message
+ *    Value: 0x9
+ *  - COOKIE_LSBS
+ *    Bits 31:0
+ *    Purpose: Provide a mechanism to match a target->host stats confirmation
+ *        message with its preceding host->target stats request message.
+ *    Value: LSBs of the opaque cookie specified by the host-side requestor
+ *  - COOKIE_MSBS
+ *    Bits 31:0
+ *    Purpose: Provide a mechanism to match a target->host stats confirmation
+ *        message with its preceding host->target stats request message.
+ *    Value: MSBs of the opaque cookie specified by the host-side requestor
+ *
+ * Stats Information Element tag-length header fields:
+ *  - STAT_TYPE
+ *    Bits 4:0
+ *    Purpose: identifies the type of statistics info held in the
+ *        following information element
+ *    Value: htt_dbg_stats_type
+ *  - STATUS
+ *    Bits 7:5
+ *    Purpose: indicate whether the requested stats are present
+ *    Value: htt_dbg_stats_status, including a special value (0x7) to mark
+ *        the completion of the stats entry series
+ *  - LENGTH
+ *    Bits 31:16
+ *    Purpose: indicate the stats information size
+ *    Value: This field specifies the number of bytes of stats information
+ *       that follows the element tag-length header.
+ *       It is expected but not required that this length is a multiple of
+ *       4 bytes.  Even if the length is not an integer multiple of 4, the
+ *       subsequent stats entry header will begin on a 4-byte aligned
+ *       boundary.
+ */
+
+#define HTT_STATS_CONF_ITEM_INFO_STAT_TYPE_MASK 0x1F
+#define HTT_STATS_CONF_ITEM_INFO_STAT_TYPE_LSB  0
+#define HTT_STATS_CONF_ITEM_INFO_STATUS_MASK    0xE0
+#define HTT_STATS_CONF_ITEM_INFO_STATUS_LSB     5
+
+struct htt_stats_conf_item {
+	union {
+		u8 info;
+		struct {
+			u8 stat_type:5; /* %HTT_DBG_STATS_ */
+			u8 status:3; /* %HTT_DBG_STATS_STATUS_ */
+		} __packed;
+	} __packed;
+	u8 pad;
+	__le16 length;
+	u8 payload[0]; /* roundup(length, 4) long */
+} __packed;
+
+struct htt_stats_conf {
+	u8 pad[3];
+	__le32 cookie_lsb;
+	__le32 cookie_msb;
+
+	/* each item has variable length! */
+	struct htt_stats_conf_item items[0];
+} __packed;
+
+static inline struct htt_stats_conf_item *htt_stats_conf_next_item(
+					const struct htt_stats_conf_item *item)
+{
+	return (void *)item + sizeof(*item) + roundup(item->length, 4);
+}
+/*
+ * host -> target FRAG DESCRIPTOR/MSDU_EXT DESC bank
+ *
+ * The following field definitions describe the format of the HTT host
+ * to target frag_desc/msdu_ext bank configuration message.
+ * The message contains the based address and the min and max id of the
+ * MSDU_EXT/FRAG_DESC that will be used by the HTT to map MSDU DESC and
+ * MSDU_EXT/FRAG_DESC.
+ * HTT will use id in HTT descriptor instead sending the frag_desc_ptr.
+ * For QCA988X HW the firmware will use fragment_desc_ptr but in WIFI2.0
+ * the hardware does the mapping/translation.
+ *
+ * Total banks that can be configured is configured to 16.
+ *
+ * This should be called before any TX has be initiated by the HTT
+ *
+ * |31                         16|15           8|7   5|4       0|
+ * |------------------------------------------------------------|
+ * | DESC_SIZE    |  NUM_BANKS   | RES |SWP|pdev|    msg type   |
+ * |------------------------------------------------------------|
+ * |                     BANK0_BASE_ADDRESS                     |
+ * |------------------------------------------------------------|
+ * |                            ...                             |
+ * |------------------------------------------------------------|
+ * |                    BANK15_BASE_ADDRESS                     |
+ * |------------------------------------------------------------|
+ * |       BANK0_MAX_ID          |       BANK0_MIN_ID           |
+ * |------------------------------------------------------------|
+ * |                            ...                             |
+ * |------------------------------------------------------------|
+ * |       BANK15_MAX_ID         |       BANK15_MIN_ID          |
+ * |------------------------------------------------------------|
+ * Header fields:
+ *  - MSG_TYPE
+ *    Bits 7:0
+ *    Value: 0x6
+ *  - BANKx_BASE_ADDRESS
+ *    Bits 31:0
+ *    Purpose: Provide a mechanism to specify the base address of the MSDU_EXT
+ *         bank physical/bus address.
+ *  - BANKx_MIN_ID
+ *    Bits 15:0
+ *    Purpose: Provide a mechanism to specify the min index that needs to
+ *          mapped.
+ *  - BANKx_MAX_ID
+ *    Bits 31:16
+ *    Purpose: Provide a mechanism to specify the max index that needs to
+ *
+ */
+struct htt_frag_desc_bank_id {
+	__le16 bank_min_id;
+	__le16 bank_max_id;
+} __packed;
+
+/* real is 16 but it wouldn't fit in the max htt message size
+ * so we use a conservatively safe value for now */
+#define HTT_FRAG_DESC_BANK_MAX 4
+
+#define HTT_FRAG_DESC_BANK_CFG_INFO_PDEV_ID_MASK 0x03
+#define HTT_FRAG_DESC_BANK_CFG_INFO_PDEV_ID_LSB  0
+#define HTT_FRAG_DESC_BANK_CFG_INFO_SWAP         (1 << 2)
+
+struct htt_frag_desc_bank_cfg {
+	u8 info; /* HTT_FRAG_DESC_BANK_CFG_INFO_ */
+	u8 num_banks;
+	u8 desc_size;
+	__le32 bank_base_addrs[HTT_FRAG_DESC_BANK_MAX];
+	struct htt_frag_desc_bank_id bank_id[HTT_FRAG_DESC_BANK_MAX];
+} __packed;
+
+union htt_rx_pn_t {
+	/* WEP: 24-bit PN */
+	u32 pn24;
+
+	/* TKIP or CCMP: 48-bit PN */
+	u_int64_t pn48;
+
+	/* WAPI: 128-bit PN */
+	u_int64_t pn128[2];
+};
+
+struct htt_cmd {
+	struct htt_cmd_hdr hdr;
+	union {
+		struct htt_ver_req ver_req;
+		struct htt_mgmt_tx_desc mgmt_tx;
+		struct htt_data_tx_desc data_tx;
+		struct htt_rx_ring_setup rx_setup;
+		struct htt_stats_req stats_req;
+		struct htt_oob_sync_req oob_sync_req;
+		struct htt_aggr_conf aggr_conf;
+		struct htt_frag_desc_bank_cfg frag_desc_bank_cfg;
+	};
+} __packed;
+
+struct htt_resp {
+	struct htt_resp_hdr hdr;
+	union {
+		struct htt_ver_resp ver_resp;
+		struct htt_mgmt_tx_completion mgmt_tx_completion;
+		struct htt_data_tx_completion data_tx_completion;
+		struct htt_rx_indication rx_ind;
+		struct htt_rx_fragment_indication rx_frag_ind;
+		struct htt_rx_peer_map peer_map;
+		struct htt_rx_peer_unmap peer_unmap;
+		struct htt_rx_flush rx_flush;
+		struct htt_rx_addba rx_addba;
+		struct htt_rx_delba rx_delba;
+		struct htt_security_indication security_indication;
+		struct htt_rc_update rc_update;
+		struct htt_rx_test rx_test;
+		struct htt_pktlog_msg pktlog_msg;
+		struct htt_stats_conf stats_conf;
+	};
+} __packed;
+
+
+/*** host side structures follow ***/
+
+struct htt_tx_done {
+	u32 msdu_id;
+	bool discard;
+	bool no_ack;
+};
+
+struct htt_peer_map_event {
+	u8 vdev_id;
+	u16 peer_id;
+	u8 addr[ETH_ALEN];
+};
+
+struct htt_peer_unmap_event {
+	u16 peer_id;
+};
+
+struct htt_rx_info {
+	struct sk_buff *skb;
+	enum htt_rx_mpdu_status status;
+	enum htt_rx_mpdu_encrypt_type encrypt_type;
+	s8 signal;
+	struct {
+		u8 info0;
+		u32 info1;
+		u32 info2;
+	} rate;
+	bool fcs_err;
+};
+
+struct ath10k_htt {
+	struct ath10k *ar;
+	enum ath10k_htc_ep_id eid;
+
+	int max_throughput_mbps;
+	u8 target_version_major;
+	u8 target_version_minor;
+	struct completion target_version_received;
+
+	struct {
+		/*
+		 * Ring of network buffer objects - This ring is
+		 * used exclusively by the host SW. This ring
+		 * mirrors the dev_addrs_ring that is shared
+		 * between the host SW and the MAC HW. The host SW
+		 * uses this netbufs ring to locate the network
+		 * buffer objects whose data buffers the HW has
+		 * filled.
+		 */
+		struct sk_buff **netbufs_ring;
+		/*
+		 * Ring of buffer addresses -
+		 * This ring holds the "physical" device address of the
+		 * rx buffers the host SW provides for the MAC HW to
+		 * fill.
+		 */
+		__le32 *paddrs_ring;
+
+		/*
+		 * Base address of ring, as a "physical" device address
+		 * rather than a CPU address.
+		 */
+		dma_addr_t base_paddr;
+
+		/* how many elems in the ring (power of 2) */
+		int size;
+
+		/* size - 1 */
+		unsigned size_mask;
+
+		/* how many rx buffers to keep in the ring */
+		int fill_level;
+
+		/* how many rx buffers (full+empty) are in the ring */
+		int fill_cnt;
+
+		/*
+		 * alloc_idx - where HTT SW has deposited empty buffers
+		 * This is allocated in consistent mem, so that the FW can
+		 * read this variable, and program the HW's FW_IDX reg with
+		 * the value of this shadow register.
+		 */
+		struct {
+			__le32 *vaddr;
+			dma_addr_t paddr;
+		} alloc_idx;
+
+		/* where HTT SW has processed bufs filled by rx MAC DMA */
+		struct {
+			unsigned msdu_payld;
+		} sw_rd_idx;
+
+		/*
+		 * refill_retry_timer - timer triggered when the ring is
+		 * not refilled to the level expected
+		 */
+		struct timer_list refill_retry_timer;
+
+		/* Protects access to all rx ring buffer state variables */
+		spinlock_t lock;
+	} rx_ring;
+
+	unsigned int prefetch_len;
+
+	/* Protects access to %pending_tx, %used_msdu_ids */
+	spinlock_t tx_lock;
+	int max_num_pending_tx;
+	int num_pending_tx;
+	struct sk_buff **pending_tx;
+	unsigned long *used_msdu_ids; /* bitmap */
+	wait_queue_head_t empty_tx_wq;
+
+	/* set if host-fw communication goes haywire
+	 * used to avoid further failures */
+	bool rx_confused;
+};
+
+#define RX_HTT_HDR_STATUS_LEN 64
+
+/* This structure layout is programmed via rx ring setup
+ * so that FW knows how to transfer the rx descriptor to the host.
+ * Buffers like this are placed on the rx ring. */
+struct htt_rx_desc {
+	union {
+		/* This field is filled on the host using the msdu buffer
+		 * from htt_rx_indication */
+		struct fw_rx_desc_base fw_desc;
+		u32 pad;
+	} __packed;
+	struct {
+		struct rx_attention attention;
+		struct rx_frag_info frag_info;
+		struct rx_mpdu_start mpdu_start;
+		struct rx_msdu_start msdu_start;
+		struct rx_msdu_end msdu_end;
+		struct rx_mpdu_end mpdu_end;
+		struct rx_ppdu_start ppdu_start;
+		struct rx_ppdu_end ppdu_end;
+	} __packed;
+	u8 rx_hdr_status[RX_HTT_HDR_STATUS_LEN];
+	u8 msdu_payload[0];
+};
+
+#define HTT_RX_DESC_ALIGN 8
+
+#define HTT_MAC_ADDR_LEN 6
+
+/*
+ * FIX THIS
+ * Should be: sizeof(struct htt_host_rx_desc) + max rx MSDU size,
+ * rounded up to a cache line size.
+ */
+#define HTT_RX_BUF_SIZE 1920
+#define HTT_RX_MSDU_SIZE (HTT_RX_BUF_SIZE - (int)sizeof(struct htt_rx_desc))
+
+/*
+ * DMA_MAP expects the buffer to be an integral number of cache lines.
+ * Rather than checking the actual cache line size, this code makes a
+ * conservative estimate of what the cache line size could be.
+ */
+#define HTT_LOG2_MAX_CACHE_LINE_SIZE 7	/* 2^7 = 128 */
+#define HTT_MAX_CACHE_LINE_SIZE_MASK ((1 << HTT_LOG2_MAX_CACHE_LINE_SIZE) - 1)
+
+struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar);
+int ath10k_htt_attach_target(struct ath10k_htt *htt);
+void ath10k_htt_detach(struct ath10k_htt *htt);
+
+int ath10k_htt_tx_attach(struct ath10k_htt *htt);
+void ath10k_htt_tx_detach(struct ath10k_htt *htt);
+int ath10k_htt_rx_attach(struct ath10k_htt *htt);
+void ath10k_htt_rx_detach(struct ath10k_htt *htt);
+void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb);
+void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb);
+int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt);
+int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt);
+
+void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt);
+int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt);
+void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id);
+int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *);
+int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *);
+#endif


^ permalink raw reply related

* [PATCH v2 14/29] ath10k: add htt_rx.c
From: Kalle Valo @ 2013-06-01 11:25 UTC (permalink / raw)
  To: linux-wireless; +Cc: ath10k-devel
In-Reply-To: <20130601111820.32443.46794.stgit@localhost6.localdomain6>

Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
---
 drivers/net/wireless/ath/ath10k/htt_rx.c | 1167 ++++++++++++++++++++++++++++++
 1 file changed, 1167 insertions(+)
 create mode 100644 drivers/net/wireless/ath/ath10k/htt_rx.c

diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
new file mode 100644
index 0000000..de058d7
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -0,0 +1,1167 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * 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 "htc.h"
+#include "htt.h"
+#include "txrx.h"
+#include "debug.h"
+
+#include <linux/log2.h>
+
+/* slightly larger than one large A-MPDU */
+#define HTT_RX_RING_SIZE_MIN 128
+
+/* roughly 20 ms @ 1 Gbps of 1500B MSDUs */
+#define HTT_RX_RING_SIZE_MAX 2048
+
+#define HTT_RX_AVG_FRM_BYTES 1000
+
+/* ms, very conservative */
+#define HTT_RX_HOST_LATENCY_MAX_MS 20
+
+/* ms, conservative */
+#define HTT_RX_HOST_LATENCY_WORST_LIKELY_MS 10
+
+/* when under memory pressure rx ring refill may fail and needs a retry */
+#define HTT_RX_RING_REFILL_RETRY_MS 50
+
+static int ath10k_htt_rx_ring_size(struct ath10k_htt *htt)
+{
+	int size;
+
+	/*
+	 * It is expected that the host CPU will typically be able to
+	 * service the rx indication from one A-MPDU before the rx
+	 * indication from the subsequent A-MPDU happens, roughly 1-2 ms
+	 * later. However, the rx ring should be sized very conservatively,
+	 * to accomodate the worst reasonable delay before the host CPU
+	 * services a rx indication interrupt.
+	 *
+	 * The rx ring need not be kept full of empty buffers. In theory,
+	 * the htt host SW can dynamically track the low-water mark in the
+	 * rx ring, and dynamically adjust the level to which the rx ring
+	 * is filled with empty buffers, to dynamically meet the desired
+	 * low-water mark.
+	 *
+	 * In contrast, it's difficult to resize the rx ring itself, once
+	 * it's in use. Thus, the ring itself should be sized very
+	 * conservatively, while the degree to which the ring is filled
+	 * with empty buffers should be sized moderately conservatively.
+	 */
+
+	/* 1e6 bps/mbps / 1e3 ms per sec = 1000 */
+	size =
+	    htt->max_throughput_mbps +
+	    1000  /
+	    (8 * HTT_RX_AVG_FRM_BYTES) * HTT_RX_HOST_LATENCY_MAX_MS;
+
+	if (size < HTT_RX_RING_SIZE_MIN)
+		size = HTT_RX_RING_SIZE_MIN;
+
+	if (size > HTT_RX_RING_SIZE_MAX)
+		size = HTT_RX_RING_SIZE_MAX;
+
+	size = roundup_pow_of_two(size);
+
+	return size;
+}
+
+static int ath10k_htt_rx_ring_fill_level(struct ath10k_htt *htt)
+{
+	int size;
+
+	/* 1e6 bps/mbps / 1e3 ms per sec = 1000 */
+	size =
+	    htt->max_throughput_mbps *
+	    1000  /
+	    (8 * HTT_RX_AVG_FRM_BYTES) * HTT_RX_HOST_LATENCY_WORST_LIKELY_MS;
+
+	/*
+	 * Make sure the fill level is at least 1 less than the ring size.
+	 * Leaving 1 element empty allows the SW to easily distinguish
+	 * between a full ring vs. an empty ring.
+	 */
+	if (size >= htt->rx_ring.size)
+		size = htt->rx_ring.size - 1;
+
+	return size;
+}
+
+static void ath10k_htt_rx_ring_free(struct ath10k_htt *htt)
+{
+	struct sk_buff *skb;
+	struct ath10k_skb_cb *cb;
+	int i;
+
+	for (i = 0; i < htt->rx_ring.fill_cnt; i++) {
+		skb = htt->rx_ring.netbufs_ring[i];
+		cb = ATH10K_SKB_CB(skb);
+		dma_unmap_single(htt->ar->dev, cb->paddr,
+				 skb->len + skb_tailroom(skb),
+				 DMA_FROM_DEVICE);
+		dev_kfree_skb_any(skb);
+	}
+
+	htt->rx_ring.fill_cnt = 0;
+}
+
+static int __ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num)
+{
+	struct htt_rx_desc *rx_desc;
+	struct sk_buff *skb;
+	dma_addr_t paddr;
+	int ret = 0, idx;
+
+	idx = __le32_to_cpu(*(htt->rx_ring.alloc_idx.vaddr));
+	while (num > 0) {
+		skb = dev_alloc_skb(HTT_RX_BUF_SIZE + HTT_RX_DESC_ALIGN);
+		if (!skb) {
+			ret = -ENOMEM;
+			goto fail;
+		}
+
+		if (!IS_ALIGNED((unsigned long)skb->data, HTT_RX_DESC_ALIGN))
+			skb_pull(skb,
+				 PTR_ALIGN(skb->data, HTT_RX_DESC_ALIGN) -
+				 skb->data);
+
+		/* Clear rx_desc attention word before posting to Rx ring */
+		rx_desc = (struct htt_rx_desc *)skb->data;
+		rx_desc->attention.flags = __cpu_to_le32(0);
+
+		paddr = dma_map_single(htt->ar->dev, skb->data,
+				       skb->len + skb_tailroom(skb),
+				       DMA_FROM_DEVICE);
+
+		if (unlikely(dma_mapping_error(htt->ar->dev, paddr))) {
+			dev_kfree_skb_any(skb);
+			ret = -ENOMEM;
+			goto fail;
+		}
+
+		ATH10K_SKB_CB(skb)->paddr = paddr;
+		htt->rx_ring.netbufs_ring[idx] = skb;
+		htt->rx_ring.paddrs_ring[idx] = __cpu_to_le32(paddr);
+		htt->rx_ring.fill_cnt++;
+
+		num--;
+		idx++;
+		idx &= htt->rx_ring.size_mask;
+	}
+
+fail:
+	*(htt->rx_ring.alloc_idx.vaddr) = __cpu_to_le32(idx);
+	return ret;
+}
+
+static int ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num)
+{
+	lockdep_assert_held(&htt->rx_ring.lock);
+	return __ath10k_htt_rx_ring_fill_n(htt, num);
+}
+
+static void ath10k_htt_rx_msdu_buff_replenish(struct ath10k_htt *htt)
+{
+	int ret, num_to_fill;
+
+	spin_lock_bh(&htt->rx_ring.lock);
+	num_to_fill = htt->rx_ring.fill_level - htt->rx_ring.fill_cnt;
+	ret = ath10k_htt_rx_ring_fill_n(htt, num_to_fill);
+	if (ret == -ENOMEM) {
+		/*
+		 * Failed to fill it to the desired level -
+		 * we'll start a timer and try again next time.
+		 * As long as enough buffers are left in the ring for
+		 * another A-MPDU rx, no special recovery is needed.
+		 */
+		mod_timer(&htt->rx_ring.refill_retry_timer, jiffies +
+			  msecs_to_jiffies(HTT_RX_RING_REFILL_RETRY_MS));
+	}
+	spin_unlock_bh(&htt->rx_ring.lock);
+}
+
+static void ath10k_htt_rx_ring_refill_retry(unsigned long arg)
+{
+	struct ath10k_htt *htt = (struct ath10k_htt *)arg;
+	ath10k_htt_rx_msdu_buff_replenish(htt);
+}
+
+static unsigned ath10k_htt_rx_ring_elems(struct ath10k_htt *htt)
+{
+	return (__le32_to_cpu(*htt->rx_ring.alloc_idx.vaddr) -
+		htt->rx_ring.sw_rd_idx.msdu_payld) & htt->rx_ring.size_mask;
+}
+
+void ath10k_htt_rx_detach(struct ath10k_htt *htt)
+{
+	int sw_rd_idx = htt->rx_ring.sw_rd_idx.msdu_payld;
+
+	del_timer_sync(&htt->rx_ring.refill_retry_timer);
+
+	while (sw_rd_idx != __le32_to_cpu(*(htt->rx_ring.alloc_idx.vaddr))) {
+		struct sk_buff *skb =
+				htt->rx_ring.netbufs_ring[sw_rd_idx];
+		struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb);
+
+		dma_unmap_single(htt->ar->dev, cb->paddr,
+				 skb->len + skb_tailroom(skb),
+				 DMA_FROM_DEVICE);
+		dev_kfree_skb_any(htt->rx_ring.netbufs_ring[sw_rd_idx]);
+		sw_rd_idx++;
+		sw_rd_idx &= htt->rx_ring.size_mask;
+	}
+
+	dma_free_coherent(htt->ar->dev,
+			  (htt->rx_ring.size *
+			   sizeof(htt->rx_ring.paddrs_ring)),
+			  htt->rx_ring.paddrs_ring,
+			  htt->rx_ring.base_paddr);
+
+	dma_free_coherent(htt->ar->dev,
+			  sizeof(*htt->rx_ring.alloc_idx.vaddr),
+			  htt->rx_ring.alloc_idx.vaddr,
+			  htt->rx_ring.alloc_idx.paddr);
+
+	kfree(htt->rx_ring.netbufs_ring);
+}
+
+static inline struct sk_buff *ath10k_htt_rx_netbuf_pop(struct ath10k_htt *htt)
+{
+	int idx;
+	struct sk_buff *msdu;
+
+	spin_lock_bh(&htt->rx_ring.lock);
+
+	if (ath10k_htt_rx_ring_elems(htt) == 0)
+		ath10k_warn("htt rx ring is empty!\n");
+
+	idx = htt->rx_ring.sw_rd_idx.msdu_payld;
+	msdu = htt->rx_ring.netbufs_ring[idx];
+
+	idx++;
+	idx &= htt->rx_ring.size_mask;
+	htt->rx_ring.sw_rd_idx.msdu_payld = idx;
+	htt->rx_ring.fill_cnt--;
+
+	spin_unlock_bh(&htt->rx_ring.lock);
+	return msdu;
+}
+
+static void ath10k_htt_rx_free_msdu_chain(struct sk_buff *skb)
+{
+	struct sk_buff *next;
+
+	while (skb) {
+		next = skb->next;
+		dev_kfree_skb_any(skb);
+		skb = next;
+	}
+}
+
+static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
+				   u8 **fw_desc, int *fw_desc_len,
+				   struct sk_buff **head_msdu,
+				   struct sk_buff **tail_msdu)
+{
+	int msdu_len, msdu_chaining = 0;
+	struct sk_buff *msdu;
+	struct htt_rx_desc *rx_desc;
+
+	if (ath10k_htt_rx_ring_elems(htt) == 0)
+		ath10k_warn("htt rx ring is empty!\n");
+
+	if (htt->rx_confused) {
+		ath10k_warn("htt is confused. refusing rx\n");
+		return 0;
+	}
+
+	msdu = *head_msdu = ath10k_htt_rx_netbuf_pop(htt);
+	while (msdu) {
+		int last_msdu, msdu_len_invalid, msdu_chained;
+
+		dma_unmap_single(htt->ar->dev,
+				 ATH10K_SKB_CB(msdu)->paddr,
+				 msdu->len + skb_tailroom(msdu),
+				 DMA_FROM_DEVICE);
+
+		ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx: ",
+				msdu->data, msdu->len + skb_tailroom(msdu));
+
+		rx_desc = (struct htt_rx_desc *)msdu->data;
+
+		/* FIXME: we must report msdu payload since this is what caller
+		 *        expects now */
+		skb_put(msdu, offsetof(struct htt_rx_desc, msdu_payload));
+		skb_pull(msdu, offsetof(struct htt_rx_desc, msdu_payload));
+
+		/*
+		 * Sanity check - confirm the HW is finished filling in the
+		 * rx data.
+		 * If the HW and SW are working correctly, then it's guaranteed
+		 * that the HW's MAC DMA is done before this point in the SW.
+		 * To prevent the case that we handle a stale Rx descriptor,
+		 * just assert for now until we have a way to recover.
+		 */
+		if (!(__le32_to_cpu(rx_desc->attention.flags)
+				& RX_ATTENTION_FLAGS_MSDU_DONE)) {
+			ath10k_htt_rx_free_msdu_chain(*head_msdu);
+			*head_msdu = NULL;
+			msdu = NULL;
+			ath10k_err("htt rx stopped. cannot recover\n");
+			htt->rx_confused = true;
+			break;
+		}
+
+		/*
+		 * Copy the FW rx descriptor for this MSDU from the rx
+		 * indication message into the MSDU's netbuf. HL uses the
+		 * same rx indication message definition as LL, and simply
+		 * appends new info (fields from the HW rx desc, and the
+		 * MSDU payload itself). So, the offset into the rx
+		 * indication message only has to account for the standard
+		 * offset of the per-MSDU FW rx desc info within the
+		 * message, and how many bytes of the per-MSDU FW rx desc
+		 * info have already been consumed. (And the endianness of
+		 * the host, since for a big-endian host, the rx ind
+		 * message contents, including the per-MSDU rx desc bytes,
+		 * were byteswapped during upload.)
+		 */
+		if (*fw_desc_len > 0) {
+			rx_desc->fw_desc.info0 = **fw_desc;
+			/*
+			 * The target is expected to only provide the basic
+			 * per-MSDU rx descriptors. Just to be sure, verify
+			 * that the target has not attached extension data
+			 * (e.g. LRO flow ID).
+			 */
+
+			/* or more, if there's extension data */
+			(*fw_desc)++;
+			(*fw_desc_len)--;
+		} else {
+			/*
+			 * When an oversized AMSDU happened, FW will lost
+			 * some of MSDU status - in this case, the FW
+			 * descriptors provided will be less than the
+			 * actual MSDUs inside this MPDU. Mark the FW
+			 * descriptors so that it will still deliver to
+			 * upper stack, if no CRC error for this MPDU.
+			 *
+			 * FIX THIS - the FW descriptors are actually for
+			 * MSDUs in the end of this A-MSDU instead of the
+			 * beginning.
+			 */
+			rx_desc->fw_desc.info0 = 0;
+		}
+
+		msdu_len_invalid = !!(__le32_to_cpu(rx_desc->attention.flags)
+					& (RX_ATTENTION_FLAGS_MPDU_LENGTH_ERR |
+					   RX_ATTENTION_FLAGS_MSDU_LENGTH_ERR));
+		msdu_len = MS(__le32_to_cpu(rx_desc->msdu_start.info0),
+			      RX_MSDU_START_INFO0_MSDU_LENGTH);
+		msdu_chained = rx_desc->frag_info.ring2_more_count;
+
+		if (msdu_len_invalid)
+			msdu_len = 0;
+
+		skb_trim(msdu, 0);
+		skb_put(msdu, min(msdu_len, HTT_RX_MSDU_SIZE));
+		msdu_len -= msdu->len;
+
+		/* FIXME: Do chained buffers include htt_rx_desc or not? */
+		while (msdu_chained--) {
+			struct sk_buff *next = ath10k_htt_rx_netbuf_pop(htt);
+
+			dma_unmap_single(htt->ar->dev,
+					 ATH10K_SKB_CB(next)->paddr,
+					 next->len + skb_tailroom(next),
+					 DMA_FROM_DEVICE);
+
+			ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx: ",
+					next->data,
+					next->len + skb_tailroom(next));
+
+			skb_trim(next, 0);
+			skb_put(next, min(msdu_len, HTT_RX_BUF_SIZE));
+			msdu_len -= next->len;
+
+			msdu->next = next;
+			msdu = next;
+			msdu_chaining = 1;
+		}
+
+		if (msdu_len > 0) {
+			/* This may suggest FW bug? */
+			ath10k_warn("htt rx msdu len not consumed (%d)\n",
+				    msdu_len);
+		}
+
+		last_msdu = __le32_to_cpu(rx_desc->msdu_end.info0) &
+				RX_MSDU_END_INFO0_LAST_MSDU;
+
+		if (last_msdu) {
+			msdu->next = NULL;
+			break;
+		} else {
+			struct sk_buff *next = ath10k_htt_rx_netbuf_pop(htt);
+			msdu->next = next;
+			msdu = next;
+		}
+	}
+	*tail_msdu = msdu;
+
+	/*
+	 * Don't refill the ring yet.
+	 *
+	 * First, the elements popped here are still in use - it is not
+	 * safe to overwrite them until the matching call to
+	 * mpdu_desc_list_next. Second, for efficiency it is preferable to
+	 * refill the rx ring with 1 PPDU's worth of rx buffers (something
+	 * like 32 x 3 buffers), rather than one MPDU's worth of rx buffers
+	 * (something like 3 buffers). Consequently, we'll rely on the txrx
+	 * SW to tell us when it is done pulling all the PPDU's rx buffers
+	 * out of the rx ring, and then refill it just once.
+	 */
+
+	return msdu_chaining;
+}
+
+int ath10k_htt_rx_attach(struct ath10k_htt *htt)
+{
+	dma_addr_t paddr;
+	void *vaddr;
+	struct timer_list *timer = &htt->rx_ring.refill_retry_timer;
+
+	htt->rx_ring.size = ath10k_htt_rx_ring_size(htt);
+	if (!is_power_of_2(htt->rx_ring.size)) {
+		ath10k_warn("htt rx ring size is not power of 2\n");
+		return -EINVAL;
+	}
+
+	htt->rx_ring.size_mask = htt->rx_ring.size - 1;
+
+	/*
+	 * Set the initial value for the level to which the rx ring
+	 * should be filled, based on the max throughput and the
+	 * worst likely latency for the host to fill the rx ring
+	 * with new buffers. In theory, this fill level can be
+	 * dynamically adjusted from the initial value set here, to
+	 * reflect the actual host latency rather than a
+	 * conservative assumption about the host latency.
+	 */
+	htt->rx_ring.fill_level = ath10k_htt_rx_ring_fill_level(htt);
+
+	htt->rx_ring.netbufs_ring =
+		kmalloc(htt->rx_ring.size * sizeof(struct sk_buff *),
+			GFP_KERNEL);
+	if (!htt->rx_ring.netbufs_ring)
+		goto err_netbuf;
+
+	vaddr = dma_alloc_coherent(htt->ar->dev,
+		   (htt->rx_ring.size * sizeof(htt->rx_ring.paddrs_ring)),
+		   &paddr, GFP_DMA);
+	if (!vaddr)
+		goto err_dma_ring;
+
+	htt->rx_ring.paddrs_ring = vaddr;
+	htt->rx_ring.base_paddr = paddr;
+
+	vaddr = dma_alloc_coherent(htt->ar->dev,
+				   sizeof(*htt->rx_ring.alloc_idx.vaddr),
+				   &paddr, GFP_DMA);
+	if (!vaddr)
+		goto err_dma_idx;
+
+	htt->rx_ring.alloc_idx.vaddr = vaddr;
+	htt->rx_ring.alloc_idx.paddr = paddr;
+	htt->rx_ring.sw_rd_idx.msdu_payld = 0;
+	*htt->rx_ring.alloc_idx.vaddr = 0;
+
+	/* Initialize the Rx refill retry timer */
+	setup_timer(timer, ath10k_htt_rx_ring_refill_retry, (unsigned long)htt);
+
+	spin_lock_init(&htt->rx_ring.lock);
+
+	htt->rx_ring.fill_cnt = 0;
+	if (__ath10k_htt_rx_ring_fill_n(htt, htt->rx_ring.fill_level))
+		goto err_fill_ring;
+
+	ath10k_dbg(ATH10K_DBG_HTT, "HTT RX ring size: %d, fill_level: %d\n",
+		   htt->rx_ring.size, htt->rx_ring.fill_level);
+	return 0;
+
+err_fill_ring:
+	ath10k_htt_rx_ring_free(htt);
+	dma_free_coherent(htt->ar->dev,
+			  sizeof(*htt->rx_ring.alloc_idx.vaddr),
+			  htt->rx_ring.alloc_idx.vaddr,
+			  htt->rx_ring.alloc_idx.paddr);
+err_dma_idx:
+	dma_free_coherent(htt->ar->dev,
+			  (htt->rx_ring.size *
+			   sizeof(htt->rx_ring.paddrs_ring)),
+			  htt->rx_ring.paddrs_ring,
+			  htt->rx_ring.base_paddr);
+err_dma_ring:
+	kfree(htt->rx_ring.netbufs_ring);
+err_netbuf:
+	return -ENOMEM;
+}
+
+static int ath10k_htt_rx_crypto_param_len(enum htt_rx_mpdu_encrypt_type type)
+{
+	switch (type) {
+	case HTT_RX_MPDU_ENCRYPT_WEP40:
+	case HTT_RX_MPDU_ENCRYPT_WEP104:
+		return 4;
+	case HTT_RX_MPDU_ENCRYPT_TKIP_WITHOUT_MIC:
+	case HTT_RX_MPDU_ENCRYPT_WEP128: /* not tested */
+	case HTT_RX_MPDU_ENCRYPT_TKIP_WPA:
+	case HTT_RX_MPDU_ENCRYPT_WAPI: /* not tested */
+	case HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2:
+		return 8;
+	case HTT_RX_MPDU_ENCRYPT_NONE:
+		return 0;
+	}
+
+	ath10k_warn("unknown encryption type %d\n", type);
+	return 0;
+}
+
+static int ath10k_htt_rx_crypto_tail_len(enum htt_rx_mpdu_encrypt_type type)
+{
+	switch (type) {
+	case HTT_RX_MPDU_ENCRYPT_NONE:
+	case HTT_RX_MPDU_ENCRYPT_WEP40:
+	case HTT_RX_MPDU_ENCRYPT_WEP104:
+	case HTT_RX_MPDU_ENCRYPT_WEP128:
+	case HTT_RX_MPDU_ENCRYPT_WAPI:
+		return 0;
+	case HTT_RX_MPDU_ENCRYPT_TKIP_WITHOUT_MIC:
+	case HTT_RX_MPDU_ENCRYPT_TKIP_WPA:
+		return 4;
+	case HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2:
+		return 8;
+	}
+
+	ath10k_warn("unknown encryption type %d\n", type);
+	return 0;
+}
+
+/* Applies for first msdu in chain, before altering it. */
+static struct ieee80211_hdr *ath10k_htt_rx_skb_get_hdr(struct sk_buff *skb)
+{
+	struct htt_rx_desc *rxd;
+	enum rx_msdu_decap_format fmt;
+
+	rxd = (void *)skb->data - sizeof(*rxd);
+	fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
+			RX_MSDU_START_INFO1_DECAP_FORMAT);
+
+	if (fmt == RX_MSDU_DECAP_RAW)
+		return (void *)skb->data;
+	else
+		return (void *)skb->data - RX_HTT_HDR_STATUS_LEN;
+}
+
+/* This function only applies for first msdu in an msdu chain */
+static bool ath10k_htt_rx_hdr_is_amsdu(struct ieee80211_hdr *hdr)
+{
+	if (ieee80211_is_data_qos(hdr->frame_control)) {
+		u8 *qc = ieee80211_get_qos_ctl(hdr);
+		if (qc[0] & 0x80)
+			return true;
+	}
+	return false;
+}
+
+static int ath10k_htt_rx_amsdu(struct ath10k_htt *htt,
+			struct htt_rx_info *info)
+{
+	struct htt_rx_desc *rxd;
+	struct sk_buff *amsdu;
+	struct sk_buff *first;
+	struct ieee80211_hdr *hdr;
+	struct sk_buff *skb = info->skb;
+	enum rx_msdu_decap_format fmt;
+	enum htt_rx_mpdu_encrypt_type enctype;
+	unsigned int hdr_len;
+	int crypto_len;
+
+	rxd = (void *)skb->data - sizeof(*rxd);
+	fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
+			RX_MSDU_START_INFO1_DECAP_FORMAT);
+	enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
+			RX_MPDU_START_INFO0_ENCRYPT_TYPE);
+
+	/* FIXME: No idea what assumptions are safe here. Need logs */
+	if ((fmt == RX_MSDU_DECAP_RAW && skb->next) ||
+	    (fmt == RX_MSDU_DECAP_8023_SNAP_LLC)) {
+		ath10k_htt_rx_free_msdu_chain(skb->next);
+		skb->next = NULL;
+		return -ENOTSUPP;
+	}
+
+	/* A-MSDU max is a little less than 8K */
+	amsdu = dev_alloc_skb(8*1024);
+	if (!amsdu) {
+		ath10k_warn("A-MSDU allocation failed\n");
+		ath10k_htt_rx_free_msdu_chain(skb->next);
+		skb->next = NULL;
+		return -ENOMEM;
+	}
+
+	if (fmt >= RX_MSDU_DECAP_NATIVE_WIFI) {
+		int hdrlen;
+
+		hdr = (void *)rxd->rx_hdr_status;
+		hdrlen = ieee80211_hdrlen(hdr->frame_control);
+		memcpy(skb_put(amsdu, hdrlen), hdr, hdrlen);
+	}
+
+	first = skb;
+	while (skb) {
+		void *decap_hdr;
+		int decap_len = 0;
+
+		rxd = (void *)skb->data - sizeof(*rxd);
+		fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
+				RX_MSDU_START_INFO1_DECAP_FORMAT);
+		decap_hdr = (void *)rxd->rx_hdr_status;
+
+		if (skb == first) {
+			/* We receive linked A-MSDU subframe skbuffs. The
+			 * first one contains the original 802.11 header (and
+			 * possible crypto param) in the RX descriptor. The
+			 * A-MSDU subframe header follows that. Each part is
+			 * aligned to 4 byte boundary. */
+
+			hdr = (void *)amsdu->data;
+			hdr_len = ieee80211_hdrlen(hdr->frame_control);
+			crypto_len = ath10k_htt_rx_crypto_param_len(enctype);
+
+			decap_hdr += roundup(hdr_len, 4);
+			decap_hdr += roundup(crypto_len, 4);
+		}
+
+		if (fmt == RX_MSDU_DECAP_ETHERNET2_DIX) {
+			/* Ethernet2 decap inserts ethernet header in place of
+			 * A-MSDU subframe header. */
+			skb_pull(skb, 6 + 6 + 2);
+
+			/* A-MSDU subframe header length */
+			decap_len += 6 + 6 + 2;
+
+			/* Ethernet2 decap also strips the LLC/SNAP so we need
+			 * to re-insert it. The LLC/SNAP follows A-MSDU
+			 * subframe header. */
+			/* FIXME: Not all LLCs are 8 bytes long */
+			decap_len += 8;
+
+			memcpy(skb_put(amsdu, decap_len), decap_hdr, decap_len);
+		}
+
+		if (fmt == RX_MSDU_DECAP_NATIVE_WIFI) {
+			/* Native Wifi decap inserts regular 802.11 header
+			 * in place of A-MSDU subframe header. */
+			hdr = (struct ieee80211_hdr *)skb->data;
+			skb_pull(skb, ieee80211_hdrlen(hdr->frame_control));
+
+			/* A-MSDU subframe header length */
+			decap_len += 6 + 6 + 2;
+
+			memcpy(skb_put(amsdu, decap_len), decap_hdr, decap_len);
+		}
+
+		if (fmt == RX_MSDU_DECAP_RAW)
+			skb_trim(skb, skb->len - 4); /* remove FCS */
+
+		memcpy(skb_put(amsdu, skb->len), skb->data, skb->len);
+
+		/* A-MSDU subframes are padded to 4bytes
+		 * but relative to first subframe, not the whole MPDU */
+		if (skb->next && ((decap_len + skb->len) & 3)) {
+			int padlen = 4 - ((decap_len + skb->len) & 3);
+			memset(skb_put(amsdu, padlen), 0, padlen);
+		}
+
+		skb = skb->next;
+	}
+
+	info->skb = amsdu;
+	info->encrypt_type = enctype;
+
+	ath10k_htt_rx_free_msdu_chain(first);
+
+	return 0;
+}
+
+static int ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info)
+{
+	struct sk_buff *skb = info->skb;
+	struct htt_rx_desc *rxd;
+	struct ieee80211_hdr *hdr;
+	enum rx_msdu_decap_format fmt;
+	enum htt_rx_mpdu_encrypt_type enctype;
+
+	/* This shouldn't happen. If it does than it may be a FW bug. */
+	if (skb->next) {
+		ath10k_warn("received chained non A-MSDU frame\n");
+		ath10k_htt_rx_free_msdu_chain(skb->next);
+		skb->next = NULL;
+	}
+
+	rxd = (void *)skb->data - sizeof(*rxd);
+	fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
+			RX_MSDU_START_INFO1_DECAP_FORMAT);
+	enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
+			RX_MPDU_START_INFO0_ENCRYPT_TYPE);
+	hdr = (void *)skb->data - RX_HTT_HDR_STATUS_LEN;
+
+	switch (fmt) {
+	case RX_MSDU_DECAP_RAW:
+		/* remove trailing FCS */
+		skb_trim(skb, skb->len - 4);
+		break;
+	case RX_MSDU_DECAP_NATIVE_WIFI:
+		/* nothing to do here */
+		break;
+	case RX_MSDU_DECAP_ETHERNET2_DIX:
+		/* macaddr[6] + macaddr[6] + ethertype[2] */
+		skb_pull(skb, 6 + 6 + 2);
+		break;
+	case RX_MSDU_DECAP_8023_SNAP_LLC:
+		/* macaddr[6] + macaddr[6] + len[2] */
+		/* we don't need this for non-A-MSDU */
+		skb_pull(skb, 6 + 6 + 2);
+		break;
+	}
+
+	if (fmt == RX_MSDU_DECAP_ETHERNET2_DIX) {
+		void *llc;
+		int llclen;
+
+		llclen = 8;
+		llc  = hdr;
+		llc += roundup(ieee80211_hdrlen(hdr->frame_control), 4);
+		llc += roundup(ath10k_htt_rx_crypto_param_len(enctype), 4);
+
+		skb_push(skb, llclen);
+		memcpy(skb->data, llc, llclen);
+	}
+
+	if (fmt >= RX_MSDU_DECAP_ETHERNET2_DIX) {
+		int len = ieee80211_hdrlen(hdr->frame_control);
+		skb_push(skb, len);
+		memcpy(skb->data, hdr, len);
+	}
+
+	info->skb = skb;
+	info->encrypt_type = enctype;
+	return 0;
+}
+
+static bool ath10k_htt_rx_has_decrypt_err(struct sk_buff *skb)
+{
+	struct htt_rx_desc *rxd;
+	u32 flags;
+
+	rxd = (void *)skb->data - sizeof(*rxd);
+	flags = __le32_to_cpu(rxd->attention.flags);
+
+	if (flags & RX_ATTENTION_FLAGS_DECRYPT_ERR)
+		return true;
+
+	return false;
+}
+
+static bool ath10k_htt_rx_has_fcs_err(struct sk_buff *skb)
+{
+	struct htt_rx_desc *rxd;
+	u32 flags;
+
+	rxd = (void *)skb->data - sizeof(*rxd);
+	flags = __le32_to_cpu(rxd->attention.flags);
+
+	if (flags & RX_ATTENTION_FLAGS_FCS_ERR)
+		return true;
+
+	return false;
+}
+
+static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
+				  struct htt_rx_indication *rx)
+{
+	struct htt_rx_info info;
+	struct htt_rx_indication_mpdu_range *mpdu_ranges;
+	struct ieee80211_hdr *hdr;
+	int num_mpdu_ranges;
+	int fw_desc_len;
+	u8 *fw_desc;
+	int i, j;
+	int ret;
+
+	memset(&info, 0, sizeof(info));
+
+	fw_desc_len = __le16_to_cpu(rx->prefix.fw_rx_desc_bytes);
+	fw_desc = (u8 *)&rx->fw_desc;
+
+	num_mpdu_ranges = MS(__le32_to_cpu(rx->hdr.info1),
+			     HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES);
+	mpdu_ranges = htt_rx_ind_get_mpdu_ranges(rx);
+
+	ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx ind: ",
+			rx, sizeof(*rx) +
+			(sizeof(struct htt_rx_indication_mpdu_range) *
+				num_mpdu_ranges));
+
+	for (i = 0; i < num_mpdu_ranges; i++) {
+		info.status = mpdu_ranges[i].mpdu_range_status;
+
+		for (j = 0; j < mpdu_ranges[i].mpdu_count; j++) {
+			struct sk_buff *msdu_head, *msdu_tail;
+			enum htt_rx_mpdu_status status;
+			int msdu_chaining;
+
+			msdu_head = NULL;
+			msdu_tail = NULL;
+			msdu_chaining = ath10k_htt_rx_amsdu_pop(htt,
+							 &fw_desc,
+							 &fw_desc_len,
+							 &msdu_head,
+							 &msdu_tail);
+
+			if (!msdu_head) {
+				ath10k_warn("htt rx no data!\n");
+				continue;
+			}
+
+			if (msdu_head->len == 0) {
+				ath10k_dbg(ATH10K_DBG_HTT,
+					   "htt rx dropping due to zero-len\n");
+				ath10k_htt_rx_free_msdu_chain(msdu_head);
+				continue;
+			}
+
+			if (ath10k_htt_rx_has_decrypt_err(msdu_head)) {
+				ath10k_htt_rx_free_msdu_chain(msdu_head);
+				continue;
+			}
+
+			status = info.status;
+
+			/* Skip mgmt frames while we handle this in WMI */
+			if (status == HTT_RX_IND_MPDU_STATUS_MGMT_CTRL) {
+				ath10k_htt_rx_free_msdu_chain(msdu_head);
+				continue;
+			}
+
+			if (status != HTT_RX_IND_MPDU_STATUS_OK &&
+			    status != HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR &&
+			    !htt->ar->monitor_enabled) {
+				ath10k_dbg(ATH10K_DBG_HTT,
+					   "htt rx ignoring frame w/ status %d\n",
+					   status);
+				ath10k_htt_rx_free_msdu_chain(msdu_head);
+				continue;
+			}
+
+			/* FIXME: we do not support chaining yet.
+			 * this needs investigation */
+			if (msdu_chaining) {
+				ath10k_warn("msdu_chaining is true\n");
+				ath10k_htt_rx_free_msdu_chain(msdu_head);
+				continue;
+			}
+
+			info.skb     = msdu_head;
+			info.fcs_err = ath10k_htt_rx_has_fcs_err(msdu_head);
+			info.signal  = ATH10K_DEFAULT_NOISE_FLOOR;
+			info.signal += rx->ppdu.combined_rssi;
+
+			info.rate.info0 = rx->ppdu.info0;
+			info.rate.info1 = __le32_to_cpu(rx->ppdu.info1);
+			info.rate.info2 = __le32_to_cpu(rx->ppdu.info2);
+
+			hdr = ath10k_htt_rx_skb_get_hdr(msdu_head);
+
+			if (ath10k_htt_rx_hdr_is_amsdu(hdr))
+				ret = ath10k_htt_rx_amsdu(htt, &info);
+			else
+				ret = ath10k_htt_rx_msdu(htt, &info);
+
+			if (ret && !info.fcs_err) {
+				ath10k_warn("error processing msdus %d\n", ret);
+				dev_kfree_skb_any(info.skb);
+				continue;
+			}
+
+			if (ath10k_htt_rx_hdr_is_amsdu((void *)info.skb->data))
+				ath10k_dbg(ATH10K_DBG_HTT, "htt mpdu is amsdu\n");
+
+			ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt mpdu: ",
+					info.skb->data, info.skb->len);
+			ath10k_process_rx(htt->ar, &info);
+		}
+	}
+
+	ath10k_htt_rx_msdu_buff_replenish(htt);
+}
+
+static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt,
+				struct htt_rx_fragment_indication *frag)
+{
+	struct sk_buff *msdu_head, *msdu_tail;
+	struct htt_rx_desc *rxd;
+	enum rx_msdu_decap_format fmt;
+	struct htt_rx_info info = {};
+	struct ieee80211_hdr *hdr;
+	int msdu_chaining;
+	bool tkip_mic_err;
+	bool decrypt_err;
+	u8 *fw_desc;
+	int fw_desc_len, hdrlen, paramlen;
+	int trim;
+
+	fw_desc_len = __le16_to_cpu(frag->fw_rx_desc_bytes);
+	fw_desc = (u8 *)frag->fw_msdu_rx_desc;
+
+	msdu_head = NULL;
+	msdu_tail = NULL;
+	msdu_chaining = ath10k_htt_rx_amsdu_pop(htt, &fw_desc, &fw_desc_len,
+						&msdu_head, &msdu_tail);
+
+	ath10k_dbg(ATH10K_DBG_HTT_DUMP, "htt rx frag ahead\n");
+
+	if (!msdu_head) {
+		ath10k_warn("htt rx frag no data\n");
+		return;
+	}
+
+	if (msdu_chaining || msdu_head != msdu_tail) {
+		ath10k_warn("aggregation with fragmentation?!\n");
+		ath10k_htt_rx_free_msdu_chain(msdu_head);
+		return;
+	}
+
+	/* FIXME: implement signal strength */
+
+	hdr = (struct ieee80211_hdr *)msdu_head->data;
+	rxd = (void *)msdu_head->data - sizeof(*rxd);
+	tkip_mic_err = !!(__le32_to_cpu(rxd->attention.flags) &
+				RX_ATTENTION_FLAGS_TKIP_MIC_ERR);
+	decrypt_err = !!(__le32_to_cpu(rxd->attention.flags) &
+				RX_ATTENTION_FLAGS_DECRYPT_ERR);
+	fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
+			RX_MSDU_START_INFO1_DECAP_FORMAT);
+
+	if (fmt != RX_MSDU_DECAP_RAW) {
+		ath10k_warn("we dont support non-raw fragmented rx yet\n");
+		dev_kfree_skb_any(msdu_head);
+		goto end;
+	}
+
+	info.skb = msdu_head;
+	info.status = HTT_RX_IND_MPDU_STATUS_OK;
+	info.encrypt_type = MS(__le32_to_cpu(rxd->mpdu_start.info0),
+				RX_MPDU_START_INFO0_ENCRYPT_TYPE);
+
+	if (tkip_mic_err) {
+		ath10k_warn("tkip mic error\n");
+		info.status = HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR;
+	}
+
+	if (decrypt_err) {
+		ath10k_warn("decryption err in fragmented rx\n");
+		dev_kfree_skb_any(info.skb);
+		goto end;
+	}
+
+	if (info.encrypt_type != HTT_RX_MPDU_ENCRYPT_NONE) {
+		hdrlen = ieee80211_hdrlen(hdr->frame_control);
+		paramlen = ath10k_htt_rx_crypto_param_len(info.encrypt_type);
+
+		/* It is more efficient to move the header than the payload */
+		memmove((void *)info.skb->data + paramlen,
+			(void *)info.skb->data,
+			hdrlen);
+		skb_pull(info.skb, paramlen);
+		hdr = (struct ieee80211_hdr *)info.skb->data;
+	}
+
+	/* remove trailing FCS */
+	trim  = 4;
+
+	/* remove crypto trailer */
+	trim += ath10k_htt_rx_crypto_tail_len(info.encrypt_type);
+
+	/* last fragment of TKIP frags has MIC */
+	if (!ieee80211_has_morefrags(hdr->frame_control) &&
+	    info.encrypt_type == HTT_RX_MPDU_ENCRYPT_TKIP_WPA)
+		trim += 8;
+
+	if (trim > info.skb->len) {
+		ath10k_warn("htt rx fragment: trailer longer than the frame itself? drop\n");
+		dev_kfree_skb_any(info.skb);
+		goto end;
+	}
+
+	skb_trim(info.skb, info.skb->len - trim);
+
+	ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt frag mpdu: ",
+			info.skb->data, info.skb->len);
+	ath10k_process_rx(htt->ar, &info);
+
+end:
+	if (fw_desc_len > 0) {
+		ath10k_dbg(ATH10K_DBG_HTT,
+			   "expecting more fragmented rx in one indication %d\n",
+			   fw_desc_len);
+	}
+}
+
+void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
+{
+	struct ath10k_htt *htt = ar->htt;
+	struct htt_resp *resp = (struct htt_resp *)skb->data;
+
+	/* confirm alignment */
+	if (!IS_ALIGNED((unsigned long)skb->data, 4))
+		ath10k_warn("unaligned htt message, expect trouble\n");
+
+	ath10k_dbg(ATH10K_DBG_HTT, "HTT RX, msg_type: 0x%0X\n",
+		   resp->hdr.msg_type);
+	switch (resp->hdr.msg_type) {
+	case HTT_T2H_MSG_TYPE_VERSION_CONF: {
+		htt->target_version_major = resp->ver_resp.major;
+		htt->target_version_minor = resp->ver_resp.minor;
+		complete(&htt->target_version_received);
+		break;
+	}
+	case HTT_T2H_MSG_TYPE_RX_IND: {
+		ath10k_htt_rx_handler(htt, &resp->rx_ind);
+		break;
+	}
+	case HTT_T2H_MSG_TYPE_PEER_MAP: {
+		struct htt_peer_map_event ev = {
+			.vdev_id = resp->peer_map.vdev_id,
+			.peer_id = __le16_to_cpu(resp->peer_map.peer_id),
+		};
+		memcpy(ev.addr, resp->peer_map.addr, sizeof(ev.addr));
+		ath10k_peer_map_event(htt, &ev);
+		break;
+	}
+	case HTT_T2H_MSG_TYPE_PEER_UNMAP: {
+		struct htt_peer_unmap_event ev = {
+			.peer_id = __le16_to_cpu(resp->peer_unmap.peer_id),
+		};
+		ath10k_peer_unmap_event(htt, &ev);
+		break;
+	}
+	case HTT_T2H_MSG_TYPE_MGMT_TX_COMPLETION: {
+		struct htt_tx_done tx_done = {};
+		int status = __le32_to_cpu(resp->mgmt_tx_completion.status);
+
+		tx_done.msdu_id =
+			__le32_to_cpu(resp->mgmt_tx_completion.desc_id);
+
+		switch (status) {
+		case HTT_MGMT_TX_STATUS_OK:
+			break;
+		case HTT_MGMT_TX_STATUS_RETRY:
+			tx_done.no_ack = true;
+			break;
+		case HTT_MGMT_TX_STATUS_DROP:
+			tx_done.discard = true;
+			break;
+		}
+
+		ath10k_txrx_tx_completed(htt, &tx_done);
+		break;
+	}
+	case HTT_T2H_MSG_TYPE_TX_COMPL_IND: {
+		struct htt_tx_done tx_done = {};
+		int status = MS(resp->data_tx_completion.flags,
+				HTT_DATA_TX_STATUS);
+		__le16 msdu_id;
+		int i;
+
+		switch (status) {
+		case HTT_DATA_TX_STATUS_NO_ACK:
+			tx_done.no_ack = true;
+			break;
+		case HTT_DATA_TX_STATUS_OK:
+			break;
+		case HTT_DATA_TX_STATUS_DISCARD:
+		case HTT_DATA_TX_STATUS_POSTPONE:
+		case HTT_DATA_TX_STATUS_DOWNLOAD_FAIL:
+			tx_done.discard = true;
+			break;
+		default:
+			ath10k_warn("unhandled tx completion status %d\n",
+				    status);
+			tx_done.discard = true;
+			break;
+		}
+
+		ath10k_dbg(ATH10K_DBG_HTT, "htt tx completion num_msdus %d\n",
+			   resp->data_tx_completion.num_msdus);
+
+		for (i = 0; i < resp->data_tx_completion.num_msdus; i++) {
+			msdu_id = resp->data_tx_completion.msdus[i];
+			tx_done.msdu_id = __le16_to_cpu(msdu_id);
+			ath10k_txrx_tx_completed(htt, &tx_done);
+		}
+		break;
+	}
+	case HTT_T2H_MSG_TYPE_SEC_IND: {
+		struct ath10k *ar = htt->ar;
+		struct htt_security_indication *ev = &resp->security_indication;
+
+		ath10k_dbg(ATH10K_DBG_HTT,
+			   "sec ind peer_id %d unicast %d type %d\n",
+			  __le16_to_cpu(ev->peer_id),
+			  !!(ev->flags & HTT_SECURITY_IS_UNICAST),
+			  MS(ev->flags, HTT_SECURITY_TYPE));
+		complete(&ar->install_key_done);
+		break;
+	}
+	case HTT_T2H_MSG_TYPE_RX_FRAG_IND: {
+		ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt event: ",
+				skb->data, skb->len);
+		ath10k_htt_rx_frag_handler(htt, &resp->rx_frag_ind);
+		break;
+	}
+	case HTT_T2H_MSG_TYPE_TEST:
+		/* FIX THIS */
+		break;
+	case HTT_T2H_MSG_TYPE_TX_INSPECT_IND:
+	case HTT_T2H_MSG_TYPE_STATS_CONF:
+	case HTT_T2H_MSG_TYPE_RX_ADDBA:
+	case HTT_T2H_MSG_TYPE_RX_DELBA:
+	case HTT_T2H_MSG_TYPE_RX_FLUSH:
+	default:
+		ath10k_dbg(ATH10K_DBG_HTT, "htt event (%d) not handled\n",
+			   resp->hdr.msg_type);
+		ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt event: ",
+				skb->data, skb->len);
+		break;
+	};
+
+	/* Free the indication buffer */
+	dev_kfree_skb_any(skb);
+}


^ permalink raw reply related

* [PATCH v2 15/29] ath10k: add htt_tx.c
From: Kalle Valo @ 2013-06-01 11:26 UTC (permalink / raw)
  To: linux-wireless; +Cc: ath10k-devel
In-Reply-To: <20130601111820.32443.46794.stgit@localhost6.localdomain6>

Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
---
 drivers/net/wireless/ath/ath10k/htt_tx.c |  510 ++++++++++++++++++++++++++++++
 1 file changed, 510 insertions(+)
 create mode 100644 drivers/net/wireless/ath/ath10k/htt_tx.c

diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c
new file mode 100644
index 0000000..ef79106
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
@@ -0,0 +1,510 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * 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/etherdevice.h>
+#include "htt.h"
+#include "mac.h"
+#include "hif.h"
+#include "txrx.h"
+#include "debug.h"
+
+void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt)
+{
+	htt->num_pending_tx--;
+	if (htt->num_pending_tx == htt->max_num_pending_tx - 1)
+		ieee80211_wake_queues(htt->ar->hw);
+}
+
+static void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt)
+{
+	spin_lock_bh(&htt->tx_lock);
+	__ath10k_htt_tx_dec_pending(htt);
+	spin_unlock_bh(&htt->tx_lock);
+}
+
+static int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt)
+{
+	int ret = 0;
+
+	spin_lock_bh(&htt->tx_lock);
+
+	if (htt->num_pending_tx >= htt->max_num_pending_tx) {
+		ret = -EBUSY;
+		goto exit;
+	}
+
+	htt->num_pending_tx++;
+	if (htt->num_pending_tx == htt->max_num_pending_tx)
+		ieee80211_stop_queues(htt->ar->hw);
+
+exit:
+	spin_unlock_bh(&htt->tx_lock);
+	return ret;
+}
+
+int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt)
+{
+	int msdu_id;
+
+	lockdep_assert_held(&htt->tx_lock);
+
+	msdu_id = find_first_zero_bit(htt->used_msdu_ids,
+				      htt->max_num_pending_tx);
+	if (msdu_id == htt->max_num_pending_tx)
+		return -ENOBUFS;
+
+	ath10k_dbg(ATH10K_DBG_HTT, "htt tx alloc msdu_id %d\n", msdu_id);
+	__set_bit(msdu_id, htt->used_msdu_ids);
+	return msdu_id;
+}
+
+void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id)
+{
+	lockdep_assert_held(&htt->tx_lock);
+
+	if (!test_bit(msdu_id, htt->used_msdu_ids))
+		ath10k_warn("trying to free unallocated msdu_id %d\n", msdu_id);
+
+	ath10k_dbg(ATH10K_DBG_HTT, "htt tx free msdu_id %hu\n", msdu_id);
+	__clear_bit(msdu_id, htt->used_msdu_ids);
+}
+
+int ath10k_htt_tx_attach(struct ath10k_htt *htt)
+{
+	u8 pipe;
+
+	spin_lock_init(&htt->tx_lock);
+	init_waitqueue_head(&htt->empty_tx_wq);
+
+	/* At the beginning free queue number should hint us the maximum
+	 * queue length */
+	pipe = htt->ar->htc->endpoint[htt->eid].ul_pipe_id;
+	htt->max_num_pending_tx = ath10k_hif_get_free_queue_number(htt->ar,
+								   pipe);
+
+	ath10k_dbg(ATH10K_DBG_HTT, "htt tx max num pending tx %d\n",
+		   htt->max_num_pending_tx);
+
+	htt->pending_tx = kzalloc(sizeof(*htt->pending_tx) *
+				  htt->max_num_pending_tx, GFP_KERNEL);
+	if (!htt->pending_tx)
+		return -ENOMEM;
+
+	htt->used_msdu_ids = kzalloc(sizeof(unsigned long) *
+				     BITS_TO_LONGS(htt->max_num_pending_tx),
+				     GFP_KERNEL);
+	if (!htt->used_msdu_ids) {
+		kfree(htt->pending_tx);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void ath10k_htt_tx_cleanup_pending(struct ath10k_htt *htt)
+{
+	struct sk_buff *txdesc;
+	int msdu_id;
+
+	/* No locks needed. Called after communication with the device has
+	 * been stopped. */
+
+	for (msdu_id = 0; msdu_id < htt->max_num_pending_tx; msdu_id++) {
+		if (!test_bit(msdu_id, htt->used_msdu_ids))
+			continue;
+
+		txdesc = htt->pending_tx[msdu_id];
+		if (!txdesc)
+			continue;
+
+		ath10k_dbg(ATH10K_DBG_HTT, "force cleanup msdu_id %hu\n",
+			   msdu_id);
+
+		if (ATH10K_SKB_CB(txdesc)->htt.refcount > 0)
+			ATH10K_SKB_CB(txdesc)->htt.refcount = 1;
+
+		ATH10K_SKB_CB(txdesc)->htt.discard = true;
+		ath10k_txrx_tx_unref(htt, txdesc);
+	}
+}
+
+void ath10k_htt_tx_detach(struct ath10k_htt *htt)
+{
+	ath10k_htt_tx_cleanup_pending(htt);
+	kfree(htt->pending_tx);
+	kfree(htt->used_msdu_ids);
+	return;
+}
+
+void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb)
+{
+	struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
+	struct ath10k_htt *htt = ar->htt;
+
+	if (skb_cb->htt.is_conf) {
+		dev_kfree_skb_any(skb);
+		return;
+	}
+
+	if (skb_cb->is_aborted) {
+		skb_cb->htt.discard = true;
+
+		/* if the skbuff is aborted we need to make sure we'll free up
+		 * the tx resources, we can't simply run tx_unref() 2 times
+		 * because if htt tx completion came in earlier we'd access
+		 * unallocated memory */
+		if (skb_cb->htt.refcount > 1)
+			skb_cb->htt.refcount = 1;
+	}
+
+	ath10k_txrx_tx_unref(htt, skb);
+}
+
+int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt)
+{
+	struct sk_buff *skb;
+	struct htt_cmd *cmd;
+	int len = 0;
+	int ret;
+
+	len += sizeof(cmd->hdr);
+	len += sizeof(cmd->ver_req);
+
+	skb = ath10k_htc_alloc_skb(len);
+	if (!skb)
+		return -ENOMEM;
+
+	skb_put(skb, len);
+	cmd = (struct htt_cmd *)skb->data;
+	cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_VERSION_REQ;
+
+	ATH10K_SKB_CB(skb)->htt.is_conf = true;
+
+	ret = ath10k_htc_send(htt->ar->htc, htt->eid, skb);
+	if (ret) {
+		dev_kfree_skb_any(skb);
+		return ret;
+	}
+
+	return 0;
+}
+
+int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt)
+{
+	struct sk_buff *skb;
+	struct htt_cmd *cmd;
+	struct htt_rx_ring_setup_ring *ring;
+	const int num_rx_ring = 1;
+	u16 flags;
+	u32 fw_idx;
+	int len;
+	int ret;
+
+	/*
+	 * the HW expects the buffer to be an integral number of 4-byte
+	 * "words"
+	 */
+	BUILD_BUG_ON(!IS_ALIGNED(HTT_RX_BUF_SIZE, 4));
+	BUILD_BUG_ON((HTT_RX_BUF_SIZE & HTT_MAX_CACHE_LINE_SIZE_MASK) != 0);
+
+	len = sizeof(cmd->hdr) + sizeof(cmd->rx_setup.hdr)
+	    + (sizeof(*ring) * num_rx_ring);
+	skb = ath10k_htc_alloc_skb(len);
+	if (!skb)
+		return -ENOMEM;
+
+	skb_put(skb, len);
+
+	cmd = (struct htt_cmd *)skb->data;
+	ring = &cmd->rx_setup.rings[0];
+
+	cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_RX_RING_CFG;
+	cmd->rx_setup.hdr.num_rings = 1;
+
+	/* FIXME: do we need all of this? */
+	flags = 0;
+	flags |= HTT_RX_RING_FLAGS_MAC80211_HDR;
+	flags |= HTT_RX_RING_FLAGS_MSDU_PAYLOAD;
+	flags |= HTT_RX_RING_FLAGS_PPDU_START;
+	flags |= HTT_RX_RING_FLAGS_PPDU_END;
+	flags |= HTT_RX_RING_FLAGS_MPDU_START;
+	flags |= HTT_RX_RING_FLAGS_MPDU_END;
+	flags |= HTT_RX_RING_FLAGS_MSDU_START;
+	flags |= HTT_RX_RING_FLAGS_MSDU_END;
+	flags |= HTT_RX_RING_FLAGS_RX_ATTENTION;
+	flags |= HTT_RX_RING_FLAGS_FRAG_INFO;
+	flags |= HTT_RX_RING_FLAGS_UNICAST_RX;
+	flags |= HTT_RX_RING_FLAGS_MULTICAST_RX;
+	flags |= HTT_RX_RING_FLAGS_CTRL_RX;
+	flags |= HTT_RX_RING_FLAGS_MGMT_RX;
+	flags |= HTT_RX_RING_FLAGS_NULL_RX;
+	flags |= HTT_RX_RING_FLAGS_PHY_DATA_RX;
+
+	fw_idx = __le32_to_cpu(*htt->rx_ring.alloc_idx.vaddr);
+
+	ring->fw_idx_shadow_reg_paddr =
+		__cpu_to_le32(htt->rx_ring.alloc_idx.paddr);
+	ring->rx_ring_base_paddr = __cpu_to_le32(htt->rx_ring.base_paddr);
+	ring->rx_ring_len = __cpu_to_le16(htt->rx_ring.size);
+	ring->rx_ring_bufsize = __cpu_to_le16(HTT_RX_BUF_SIZE);
+	ring->flags = __cpu_to_le16(flags);
+	ring->fw_idx_init_val = __cpu_to_le16(fw_idx);
+
+#define desc_offset(x) (offsetof(struct htt_rx_desc, x) / 4)
+
+	ring->mac80211_hdr_offset = __cpu_to_le16(desc_offset(rx_hdr_status));
+	ring->msdu_payload_offset = __cpu_to_le16(desc_offset(msdu_payload));
+	ring->ppdu_start_offset = __cpu_to_le16(desc_offset(ppdu_start));
+	ring->ppdu_end_offset = __cpu_to_le16(desc_offset(ppdu_end));
+	ring->mpdu_start_offset = __cpu_to_le16(desc_offset(mpdu_start));
+	ring->mpdu_end_offset = __cpu_to_le16(desc_offset(mpdu_end));
+	ring->msdu_start_offset = __cpu_to_le16(desc_offset(msdu_start));
+	ring->msdu_end_offset = __cpu_to_le16(desc_offset(msdu_end));
+	ring->rx_attention_offset = __cpu_to_le16(desc_offset(attention));
+	ring->frag_info_offset = __cpu_to_le16(desc_offset(frag_info));
+
+#undef desc_offset
+
+	ATH10K_SKB_CB(skb)->htt.is_conf = true;
+
+	ret = ath10k_htc_send(htt->ar->htc, htt->eid, skb);
+	if (ret) {
+		dev_kfree_skb_any(skb);
+		return ret;
+	}
+
+	return 0;
+}
+
+int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
+{
+	struct device *dev = htt->ar->dev;
+	struct ath10k_skb_cb *skb_cb;
+	struct sk_buff *txdesc = NULL;
+	struct htt_cmd *cmd;
+	u8 vdev_id = ATH10K_SKB_CB(msdu)->htt.vdev_id;
+	int len = 0;
+	int msdu_id = -1;
+	int res;
+
+
+	res = ath10k_htt_tx_inc_pending(htt);
+	if (res)
+		return res;
+
+	len += sizeof(cmd->hdr);
+	len += sizeof(cmd->mgmt_tx);
+
+	txdesc = ath10k_htc_alloc_skb(len);
+	if (!txdesc) {
+		res = -ENOMEM;
+		goto err;
+	}
+
+	spin_lock_bh(&htt->tx_lock);
+	msdu_id = ath10k_htt_tx_alloc_msdu_id(htt);
+	if (msdu_id < 0) {
+		spin_unlock_bh(&htt->tx_lock);
+		res = msdu_id;
+		goto err;
+	}
+	htt->pending_tx[msdu_id] = txdesc;
+	spin_unlock_bh(&htt->tx_lock);
+
+	res = ath10k_skb_map(dev, msdu);
+	if (res)
+		goto err;
+
+	skb_put(txdesc, len);
+	cmd = (struct htt_cmd *)txdesc->data;
+	cmd->hdr.msg_type         = HTT_H2T_MSG_TYPE_MGMT_TX;
+	cmd->mgmt_tx.msdu_paddr = __cpu_to_le32(ATH10K_SKB_CB(msdu)->paddr);
+	cmd->mgmt_tx.len        = __cpu_to_le32(msdu->len);
+	cmd->mgmt_tx.desc_id    = __cpu_to_le32(msdu_id);
+	cmd->mgmt_tx.vdev_id    = __cpu_to_le32(vdev_id);
+	memcpy(cmd->mgmt_tx.hdr, msdu->data,
+	       min_t(int, msdu->len, HTT_MGMT_FRM_HDR_DOWNLOAD_LEN));
+
+	/* refcount is decremented by HTC and HTT completions until it reaches
+	 * zero and is freed */
+	skb_cb = ATH10K_SKB_CB(txdesc);
+	skb_cb->htt.msdu_id = msdu_id;
+	skb_cb->htt.refcount = 2;
+	skb_cb->htt.msdu = msdu;
+
+	res = ath10k_htc_send(htt->ar->htc, htt->eid, txdesc);
+	if (res)
+		goto err;
+
+	return 0;
+
+err:
+	ath10k_skb_unmap(dev, msdu);
+
+	if (txdesc)
+		dev_kfree_skb_any(txdesc);
+	if (msdu_id >= 0) {
+		spin_lock_bh(&htt->tx_lock);
+		htt->pending_tx[msdu_id] = NULL;
+		ath10k_htt_tx_free_msdu_id(htt, msdu_id);
+		spin_unlock_bh(&htt->tx_lock);
+	}
+	ath10k_htt_tx_dec_pending(htt);
+	return res;
+}
+
+int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
+{
+	struct device *dev = htt->ar->dev;
+	struct htt_cmd *cmd;
+	struct htt_data_tx_desc_frag *tx_frags;
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data;
+	struct ath10k_skb_cb *skb_cb;
+	struct sk_buff *txdesc = NULL;
+	struct sk_buff *txfrag = NULL;
+	u8 vdev_id = ATH10K_SKB_CB(msdu)->htt.vdev_id;
+	u8 tid;
+	int prefetch_len, desc_len, frag_len;
+	dma_addr_t frags_paddr;
+	int msdu_id = -1;
+	int res;
+	u8 flags0;
+	u16 flags1;
+
+	res = ath10k_htt_tx_inc_pending(htt);
+	if (res)
+		return res;
+
+	prefetch_len = min(htt->prefetch_len, msdu->len);
+	prefetch_len = roundup(prefetch_len, 4);
+
+	desc_len = sizeof(cmd->hdr) + sizeof(cmd->data_tx) + prefetch_len;
+	frag_len = sizeof(*tx_frags) * 2;
+
+	txdesc = ath10k_htc_alloc_skb(desc_len);
+	if (!txdesc) {
+		res = -ENOMEM;
+		goto err;
+	}
+
+	txfrag = dev_alloc_skb(frag_len);
+	if (!txfrag) {
+		res = -ENOMEM;
+		goto err;
+	}
+
+	if (!IS_ALIGNED((unsigned long)txdesc->data, 4)) {
+		ath10k_warn("htt alignment check failed. dropping packet.\n");
+		res = -EIO;
+		goto err;
+	}
+
+	spin_lock_bh(&htt->tx_lock);
+	msdu_id = ath10k_htt_tx_alloc_msdu_id(htt);
+	if (msdu_id < 0) {
+		spin_unlock_bh(&htt->tx_lock);
+		res = msdu_id;
+		goto err;
+	}
+	htt->pending_tx[msdu_id] = txdesc;
+	spin_unlock_bh(&htt->tx_lock);
+
+	res = ath10k_skb_map(dev, msdu);
+	if (res)
+		goto err;
+
+	/* tx fragment list must be terminated with zero-entry */
+	skb_put(txfrag, frag_len);
+	tx_frags = (struct htt_data_tx_desc_frag *)txfrag->data;
+	tx_frags[0].paddr = __cpu_to_le32(ATH10K_SKB_CB(msdu)->paddr);
+	tx_frags[0].len   = __cpu_to_le32(msdu->len);
+	tx_frags[1].paddr = __cpu_to_le32(0);
+	tx_frags[1].len   = __cpu_to_le32(0);
+
+	res = ath10k_skb_map(dev, txfrag);
+	if (res)
+		goto err;
+
+	ath10k_dbg(ATH10K_DBG_HTT, "txfrag 0x%llx msdu 0x%llx\n",
+		   (unsigned long long) ATH10K_SKB_CB(txfrag)->paddr,
+		   (unsigned long long) ATH10K_SKB_CB(msdu)->paddr);
+	ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "txfrag: ",
+			txfrag->data, frag_len);
+	ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "msdu: ",
+			msdu->data, msdu->len);
+
+	skb_put(txdesc, desc_len);
+	cmd = (struct htt_cmd *)txdesc->data;
+	memset(cmd, 0, desc_len);
+
+	tid = ATH10K_SKB_CB(msdu)->htt.tid;
+
+	ath10k_dbg(ATH10K_DBG_HTT, "htt data tx using tid %hhu\n", tid);
+
+	flags0  = 0;
+	if (!ieee80211_has_protected(hdr->frame_control))
+		flags0 |= HTT_DATA_TX_DESC_FLAGS0_NO_ENCRYPT;
+	flags0 |= HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT;
+	flags0 |= SM(ATH10K_HW_TXRX_NATIVE_WIFI,
+		     HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE);
+
+	flags1  = 0;
+	flags1 |= SM((u16)vdev_id, HTT_DATA_TX_DESC_FLAGS1_VDEV_ID);
+	flags1 |= SM((u16)tid, HTT_DATA_TX_DESC_FLAGS1_EXT_TID);
+
+	frags_paddr = ATH10K_SKB_CB(txfrag)->paddr;
+
+	cmd->hdr.msg_type        = HTT_H2T_MSG_TYPE_TX_FRM;
+	cmd->data_tx.flags0      = flags0;
+	cmd->data_tx.flags1      = __cpu_to_le16(flags1);
+	cmd->data_tx.len         = __cpu_to_le16(msdu->len);
+	cmd->data_tx.id          = __cpu_to_le16(msdu_id);
+	cmd->data_tx.frags_paddr = __cpu_to_le32(frags_paddr);
+	cmd->data_tx.peerid      = __cpu_to_le32(HTT_INVALID_PEERID);
+
+	memcpy(cmd->data_tx.prefetch, msdu->data, prefetch_len);
+
+	/* refcount is decremented by HTC and HTT completions until it reaches
+	 * zero and is freed */
+	skb_cb = ATH10K_SKB_CB(txdesc);
+	skb_cb->htt.msdu_id = msdu_id;
+	skb_cb->htt.refcount = 2;
+	skb_cb->htt.txfrag = txfrag;
+	skb_cb->htt.msdu = msdu;
+
+	res = ath10k_htc_send(htt->ar->htc, htt->eid, txdesc);
+	if (res)
+		goto err;
+
+	return 0;
+err:
+	if (txfrag)
+		ath10k_skb_unmap(dev, txfrag);
+	if (txdesc)
+		dev_kfree_skb_any(txdesc);
+	if (txfrag)
+		dev_kfree_skb_any(txfrag);
+	if (msdu_id >= 0) {
+		spin_lock_bh(&htt->tx_lock);
+		htt->pending_tx[msdu_id] = NULL;
+		ath10k_htt_tx_free_msdu_id(htt, msdu_id);
+		spin_unlock_bh(&htt->tx_lock);
+	}
+	ath10k_htt_tx_dec_pending(htt);
+	ath10k_skb_unmap(dev, msdu);
+	return res;
+}


^ permalink raw reply related

* [PATCH v2 16/29] ath10k: add hw.h
From: Kalle Valo @ 2013-06-01 11:26 UTC (permalink / raw)
  To: linux-wireless; +Cc: ath10k-devel
In-Reply-To: <20130601111820.32443.46794.stgit@localhost6.localdomain6>

Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
---
 drivers/net/wireless/ath/ath10k/hw.h |  304 ++++++++++++++++++++++++++++++++++
 1 file changed, 304 insertions(+)
 create mode 100644 drivers/net/wireless/ath/ath10k/hw.h

diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h
new file mode 100644
index 0000000..44ed5af
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/hw.h
@@ -0,0 +1,304 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * 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 _HW_H_
+#define _HW_H_
+
+#include "targaddrs.h"
+
+/* Supported FW version */
+#define SUPPORTED_FW_MAJOR	1
+#define SUPPORTED_FW_MINOR	0
+#define SUPPORTED_FW_RELEASE	0
+#define SUPPORTED_FW_BUILD	629
+
+/* QCA988X 1.0 definitions */
+#define QCA988X_HW_1_0_VERSION		0x4000002c
+#define QCA988X_HW_1_0_FW_DIR		"ath10k/QCA988X/hw1.0"
+#define QCA988X_HW_1_0_FW_FILE		"firmware.bin"
+#define QCA988X_HW_1_0_OTP_FILE		"otp.bin"
+#define QCA988X_HW_1_0_BOARD_DATA_FILE	"board.bin"
+#define QCA988X_HW_1_0_PATCH_LOAD_ADDR	0x1234
+
+/* QCA988X 2.0 definitions */
+#define QCA988X_HW_2_0_VERSION		0x4100016c
+#define QCA988X_HW_2_0_FW_DIR		"ath10k/QCA988X/hw2.0"
+#define QCA988X_HW_2_0_FW_FILE		"firmware.bin"
+#define QCA988X_HW_2_0_OTP_FILE		"otp.bin"
+#define QCA988X_HW_2_0_BOARD_DATA_FILE	"board.bin"
+#define QCA988X_HW_2_0_PATCH_LOAD_ADDR	0x1234
+
+/* Known pecularities:
+ *  - current FW doesn't support raw rx mode (last tested v599)
+ *  - current FW dumps upon raw tx mode (last tested v599)
+ *  - raw appears in nwifi decap, raw and nwifi appear in ethernet decap
+ *  - raw have FCS, nwifi doesn't
+ *  - ethernet frames have 802.11 header decapped and parts (base hdr, cipher
+ *    param, llc/snap) are aligned to 4byte boundaries each */
+enum ath10k_hw_txrx_mode {
+	ATH10K_HW_TXRX_RAW = 0,
+	ATH10K_HW_TXRX_NATIVE_WIFI = 1,
+	ATH10K_HW_TXRX_ETHERNET = 2,
+};
+
+enum ath10k_mcast2ucast_mode {
+	ATH10K_MCAST2UCAST_DISABLED = 0,
+	ATH10K_MCAST2UCAST_ENABLED = 1,
+};
+
+#define TARGET_NUM_VDEVS			8
+#define TARGET_NUM_PEER_AST			2
+#define TARGET_NUM_WDS_ENTRIES			32
+#define TARGET_DMA_BURST_SIZE			0
+#define TARGET_MAC_AGGR_DELIM			0
+#define TARGET_AST_SKID_LIMIT			16
+#define TARGET_NUM_PEERS			16
+#define TARGET_NUM_OFFLOAD_PEERS		0
+#define TARGET_NUM_OFFLOAD_REORDER_BUFS         0
+#define TARGET_NUM_PEER_KEYS			2
+#define TARGET_NUM_TIDS		(2 * ((TARGET_NUM_PEERS) + (TARGET_NUM_VDEVS)))
+#define TARGET_TX_CHAIN_MASK			(BIT(0) | BIT(1) | BIT(2))
+#define TARGET_RX_CHAIN_MASK			(BIT(0) | BIT(1) | BIT(2))
+#define TARGET_RX_TIMEOUT_LO_PRI		100
+#define TARGET_RX_TIMEOUT_HI_PRI		40
+#define TARGET_RX_DECAP_MODE			ATH10K_HW_TXRX_ETHERNET
+#define TARGET_SCAN_MAX_PENDING_REQS		4
+#define TARGET_BMISS_OFFLOAD_MAX_VDEV		3
+#define TARGET_ROAM_OFFLOAD_MAX_VDEV		3
+#define TARGET_ROAM_OFFLOAD_MAX_AP_PROFILES	8
+#define TARGET_GTK_OFFLOAD_MAX_VDEV		3
+#define TARGET_NUM_MCAST_GROUPS			0
+#define TARGET_NUM_MCAST_TABLE_ELEMS		0
+#define TARGET_MCAST2UCAST_MODE			ATH10K_MCAST2UCAST_DISABLED
+#define TARGET_TX_DBG_LOG_SIZE			1024
+#define TARGET_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK 0
+#define TARGET_VOW_CONFIG			0
+#define TARGET_NUM_MSDU_DESC			(1024 + 400)
+#define TARGET_MAX_FRAG_ENTRIES			0
+
+
+/* Number of Copy Engines supported */
+#define CE_COUNT 8
+
+/*
+ * Total number of PCIe MSI interrupts requested for all interrupt sources.
+ * PCIe standard forces this to be a power of 2.
+ * Some Host OS's limit MSI requests that can be granted to 8
+ * so for now we abide by this limit and avoid requesting more
+ * than that.
+ */
+#define MSI_NUM_REQUEST_LOG2	3
+#define MSI_NUM_REQUEST		(1<<MSI_NUM_REQUEST_LOG2)
+
+/*
+ * Granted MSIs are assigned as follows:
+ * Firmware uses the first
+ * Remaining MSIs, if any, are used by Copy Engines
+ * This mapping is known to both Target firmware and Host software.
+ * It may be changed as long as Host and Target are kept in sync.
+ */
+/* MSI for firmware (errors, etc.) */
+#define MSI_ASSIGN_FW		0
+
+/* MSIs for Copy Engines */
+#define MSI_ASSIGN_CE_INITIAL	1
+#define MSI_ASSIGN_CE_MAX	7
+
+/* as of IP3.7.1 */
+#define RTC_STATE_V_ON				3
+
+#define RTC_STATE_COLD_RESET_MASK		0x00000400
+#define RTC_STATE_V_LSB				0
+#define RTC_STATE_V_MASK			0x00000007
+#define RTC_STATE_ADDRESS			0x0000
+#define PCIE_SOC_WAKE_V_MASK			0x00000001
+#define PCIE_SOC_WAKE_ADDRESS			0x0004
+#define PCIE_SOC_WAKE_RESET			0x00000000
+#define SOC_GLOBAL_RESET_ADDRESS		0x0008
+
+#define RTC_SOC_BASE_ADDRESS			0x00004000
+#define RTC_WMAC_BASE_ADDRESS			0x00005000
+#define MAC_COEX_BASE_ADDRESS			0x00006000
+#define BT_COEX_BASE_ADDRESS			0x00007000
+#define SOC_PCIE_BASE_ADDRESS			0x00008000
+#define SOC_CORE_BASE_ADDRESS			0x00009000
+#define WLAN_UART_BASE_ADDRESS			0x0000c000
+#define WLAN_SI_BASE_ADDRESS			0x00010000
+#define WLAN_GPIO_BASE_ADDRESS			0x00014000
+#define WLAN_ANALOG_INTF_BASE_ADDRESS		0x0001c000
+#define WLAN_MAC_BASE_ADDRESS			0x00020000
+#define EFUSE_BASE_ADDRESS			0x00030000
+#define FPGA_REG_BASE_ADDRESS			0x00039000
+#define WLAN_UART2_BASE_ADDRESS			0x00054c00
+#define CE_WRAPPER_BASE_ADDRESS			0x00057000
+#define CE0_BASE_ADDRESS			0x00057400
+#define CE1_BASE_ADDRESS			0x00057800
+#define CE2_BASE_ADDRESS			0x00057c00
+#define CE3_BASE_ADDRESS			0x00058000
+#define CE4_BASE_ADDRESS			0x00058400
+#define CE5_BASE_ADDRESS			0x00058800
+#define CE6_BASE_ADDRESS			0x00058c00
+#define CE7_BASE_ADDRESS			0x00059000
+#define DBI_BASE_ADDRESS			0x00060000
+#define WLAN_ANALOG_INTF_PCIE_BASE_ADDRESS	0x0006c000
+#define PCIE_LOCAL_BASE_ADDRESS			0x00080000
+
+#define SOC_RESET_CONTROL_OFFSET		0x00000000
+#define SOC_RESET_CONTROL_SI0_RST_MASK		0x00000001
+#define SOC_CPU_CLOCK_OFFSET			0x00000020
+#define SOC_CPU_CLOCK_STANDARD_LSB		0
+#define SOC_CPU_CLOCK_STANDARD_MASK		0x00000003
+#define SOC_CLOCK_CONTROL_OFFSET		0x00000028
+#define SOC_CLOCK_CONTROL_SI0_CLK_MASK		0x00000001
+#define SOC_SYSTEM_SLEEP_OFFSET			0x000000c4
+#define SOC_LPO_CAL_OFFSET			0x000000e0
+#define SOC_LPO_CAL_ENABLE_LSB			20
+#define SOC_LPO_CAL_ENABLE_MASK			0x00100000
+
+#define WLAN_RESET_CONTROL_COLD_RST_MASK	0x00000008
+#define WLAN_RESET_CONTROL_WARM_RST_MASK	0x00000004
+#define WLAN_SYSTEM_SLEEP_DISABLE_LSB		0
+#define WLAN_SYSTEM_SLEEP_DISABLE_MASK		0x00000001
+
+#define WLAN_GPIO_PIN0_ADDRESS			0x00000028
+#define WLAN_GPIO_PIN0_CONFIG_MASK		0x00007800
+#define WLAN_GPIO_PIN1_ADDRESS			0x0000002c
+#define WLAN_GPIO_PIN1_CONFIG_MASK		0x00007800
+#define WLAN_GPIO_PIN10_ADDRESS			0x00000050
+#define WLAN_GPIO_PIN11_ADDRESS			0x00000054
+#define WLAN_GPIO_PIN12_ADDRESS			0x00000058
+#define WLAN_GPIO_PIN13_ADDRESS			0x0000005c
+
+#define CLOCK_GPIO_OFFSET			0xffffffff
+#define CLOCK_GPIO_BT_CLK_OUT_EN_LSB		0
+#define CLOCK_GPIO_BT_CLK_OUT_EN_MASK		0
+
+#define SI_CONFIG_OFFSET			0x00000000
+#define SI_CONFIG_BIDIR_OD_DATA_LSB		18
+#define SI_CONFIG_BIDIR_OD_DATA_MASK		0x00040000
+#define SI_CONFIG_I2C_LSB			16
+#define SI_CONFIG_I2C_MASK			0x00010000
+#define SI_CONFIG_POS_SAMPLE_LSB		7
+#define SI_CONFIG_POS_SAMPLE_MASK		0x00000080
+#define SI_CONFIG_INACTIVE_DATA_LSB		5
+#define SI_CONFIG_INACTIVE_DATA_MASK		0x00000020
+#define SI_CONFIG_INACTIVE_CLK_LSB		4
+#define SI_CONFIG_INACTIVE_CLK_MASK		0x00000010
+#define SI_CONFIG_DIVIDER_LSB			0
+#define SI_CONFIG_DIVIDER_MASK			0x0000000f
+#define SI_CS_OFFSET				0x00000004
+#define SI_CS_DONE_ERR_MASK			0x00000400
+#define SI_CS_DONE_INT_MASK			0x00000200
+#define SI_CS_START_LSB				8
+#define SI_CS_START_MASK			0x00000100
+#define SI_CS_RX_CNT_LSB			4
+#define SI_CS_RX_CNT_MASK			0x000000f0
+#define SI_CS_TX_CNT_LSB			0
+#define SI_CS_TX_CNT_MASK			0x0000000f
+
+#define SI_TX_DATA0_OFFSET			0x00000008
+#define SI_TX_DATA1_OFFSET			0x0000000c
+#define SI_RX_DATA0_OFFSET			0x00000010
+#define SI_RX_DATA1_OFFSET			0x00000014
+
+#define CORE_CTRL_CPU_INTR_MASK			0x00002000
+#define CORE_CTRL_ADDRESS			0x0000
+#define PCIE_INTR_ENABLE_ADDRESS		0x0008
+#define PCIE_INTR_CLR_ADDRESS			0x0014
+#define SCRATCH_3_ADDRESS			0x0030
+
+/* Firmware indications to the Host via SCRATCH_3 register. */
+#define FW_INDICATOR_ADDRESS	(SOC_CORE_BASE_ADDRESS + SCRATCH_3_ADDRESS)
+#define FW_IND_EVENT_PENDING			1
+#define FW_IND_INITIALIZED			2
+
+/* HOST_REG interrupt from firmware */
+#define PCIE_INTR_FIRMWARE_MASK			0x00000400
+#define PCIE_INTR_CE_MASK_ALL			0x0007f800
+
+#define DRAM_BASE_ADDRESS			0x00400000
+
+#define MISSING 0
+
+#define SYSTEM_SLEEP_OFFSET			SOC_SYSTEM_SLEEP_OFFSET
+#define WLAN_SYSTEM_SLEEP_OFFSET		SOC_SYSTEM_SLEEP_OFFSET
+#define WLAN_RESET_CONTROL_OFFSET		SOC_RESET_CONTROL_OFFSET
+#define CLOCK_CONTROL_OFFSET			SOC_CLOCK_CONTROL_OFFSET
+#define CLOCK_CONTROL_SI0_CLK_MASK		SOC_CLOCK_CONTROL_SI0_CLK_MASK
+#define RESET_CONTROL_MBOX_RST_MASK		MISSING
+#define RESET_CONTROL_SI0_RST_MASK		SOC_RESET_CONTROL_SI0_RST_MASK
+#define GPIO_BASE_ADDRESS			WLAN_GPIO_BASE_ADDRESS
+#define GPIO_PIN0_OFFSET			WLAN_GPIO_PIN0_ADDRESS
+#define GPIO_PIN1_OFFSET			WLAN_GPIO_PIN1_ADDRESS
+#define GPIO_PIN0_CONFIG_MASK			WLAN_GPIO_PIN0_CONFIG_MASK
+#define GPIO_PIN1_CONFIG_MASK			WLAN_GPIO_PIN1_CONFIG_MASK
+#define SI_BASE_ADDRESS				WLAN_SI_BASE_ADDRESS
+#define SCRATCH_BASE_ADDRESS			SOC_CORE_BASE_ADDRESS
+#define LOCAL_SCRATCH_OFFSET			0x18
+#define CPU_CLOCK_OFFSET			SOC_CPU_CLOCK_OFFSET
+#define LPO_CAL_OFFSET				SOC_LPO_CAL_OFFSET
+#define GPIO_PIN10_OFFSET			WLAN_GPIO_PIN10_ADDRESS
+#define GPIO_PIN11_OFFSET			WLAN_GPIO_PIN11_ADDRESS
+#define GPIO_PIN12_OFFSET			WLAN_GPIO_PIN12_ADDRESS
+#define GPIO_PIN13_OFFSET			WLAN_GPIO_PIN13_ADDRESS
+#define CPU_CLOCK_STANDARD_LSB			SOC_CPU_CLOCK_STANDARD_LSB
+#define CPU_CLOCK_STANDARD_MASK			SOC_CPU_CLOCK_STANDARD_MASK
+#define LPO_CAL_ENABLE_LSB			SOC_LPO_CAL_ENABLE_LSB
+#define LPO_CAL_ENABLE_MASK			SOC_LPO_CAL_ENABLE_MASK
+#define ANALOG_INTF_BASE_ADDRESS		WLAN_ANALOG_INTF_BASE_ADDRESS
+#define MBOX_BASE_ADDRESS			MISSING
+#define INT_STATUS_ENABLE_ERROR_LSB		MISSING
+#define INT_STATUS_ENABLE_ERROR_MASK		MISSING
+#define INT_STATUS_ENABLE_CPU_LSB		MISSING
+#define INT_STATUS_ENABLE_CPU_MASK		MISSING
+#define INT_STATUS_ENABLE_COUNTER_LSB		MISSING
+#define INT_STATUS_ENABLE_COUNTER_MASK		MISSING
+#define INT_STATUS_ENABLE_MBOX_DATA_LSB		MISSING
+#define INT_STATUS_ENABLE_MBOX_DATA_MASK	MISSING
+#define ERROR_STATUS_ENABLE_RX_UNDERFLOW_LSB	MISSING
+#define ERROR_STATUS_ENABLE_RX_UNDERFLOW_MASK	MISSING
+#define ERROR_STATUS_ENABLE_TX_OVERFLOW_LSB	MISSING
+#define ERROR_STATUS_ENABLE_TX_OVERFLOW_MASK	MISSING
+#define COUNTER_INT_STATUS_ENABLE_BIT_LSB	MISSING
+#define COUNTER_INT_STATUS_ENABLE_BIT_MASK	MISSING
+#define INT_STATUS_ENABLE_ADDRESS		MISSING
+#define CPU_INT_STATUS_ENABLE_BIT_LSB		MISSING
+#define CPU_INT_STATUS_ENABLE_BIT_MASK		MISSING
+#define HOST_INT_STATUS_ADDRESS			MISSING
+#define CPU_INT_STATUS_ADDRESS			MISSING
+#define ERROR_INT_STATUS_ADDRESS		MISSING
+#define ERROR_INT_STATUS_WAKEUP_MASK		MISSING
+#define ERROR_INT_STATUS_WAKEUP_LSB		MISSING
+#define ERROR_INT_STATUS_RX_UNDERFLOW_MASK	MISSING
+#define ERROR_INT_STATUS_RX_UNDERFLOW_LSB	MISSING
+#define ERROR_INT_STATUS_TX_OVERFLOW_MASK	MISSING
+#define ERROR_INT_STATUS_TX_OVERFLOW_LSB	MISSING
+#define COUNT_DEC_ADDRESS			MISSING
+#define HOST_INT_STATUS_CPU_MASK		MISSING
+#define HOST_INT_STATUS_CPU_LSB			MISSING
+#define HOST_INT_STATUS_ERROR_MASK		MISSING
+#define HOST_INT_STATUS_ERROR_LSB		MISSING
+#define HOST_INT_STATUS_COUNTER_MASK		MISSING
+#define HOST_INT_STATUS_COUNTER_LSB		MISSING
+#define RX_LOOKAHEAD_VALID_ADDRESS		MISSING
+#define WINDOW_DATA_ADDRESS			MISSING
+#define WINDOW_READ_ADDR_ADDRESS		MISSING
+#define WINDOW_WRITE_ADDR_ADDRESS		MISSING
+
+#define RTC_STATE_V_GET(x) (((x) & RTC_STATE_V_MASK) >> RTC_STATE_V_LSB)
+
+#endif /* _HW_H_ */


^ permalink raw reply related

* [PATCH v2 17/29] ath10k: add mac.c
From: Kalle Valo @ 2013-06-01 11:26 UTC (permalink / raw)
  To: linux-wireless; +Cc: ath10k-devel
In-Reply-To: <20130601111820.32443.46794.stgit@localhost6.localdomain6>

Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
---
 drivers/net/wireless/ath/ath10k/mac.c | 3066 +++++++++++++++++++++++++++++++++
 1 file changed, 3066 insertions(+)
 create mode 100644 drivers/net/wireless/ath/ath10k/mac.c

diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
new file mode 100644
index 0000000..1285554
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -0,0 +1,3066 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * 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 "mac.h"
+
+#include <net/mac80211.h>
+#include <linux/etherdevice.h>
+
+#include "core.h"
+#include "debug.h"
+#include "wmi.h"
+#include "htt.h"
+#include "txrx.h"
+
+/**********/
+/* Crypto */
+/**********/
+
+static int ath10k_send_key(struct ath10k_vif *arvif,
+			   struct ieee80211_key_conf *key,
+			   enum set_key_cmd cmd,
+			   const u8 *macaddr)
+{
+	struct wmi_vdev_install_key_arg arg = {
+		.vdev_id = arvif->vdev_id,
+		.key_idx = key->keyidx,
+		.key_len = key->keylen,
+		.key_data = key->key,
+		.macaddr = macaddr,
+	};
+
+	if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
+		arg.key_flags = WMI_KEY_PAIRWISE;
+	else
+		arg.key_flags = WMI_KEY_GROUP;
+
+	switch (key->cipher) {
+	case WLAN_CIPHER_SUITE_CCMP:
+		arg.key_cipher = WMI_CIPHER_AES_CCM;
+		key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
+		break;
+	case WLAN_CIPHER_SUITE_TKIP:
+		key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+		arg.key_cipher = WMI_CIPHER_TKIP;
+		arg.key_txmic_len = 8;
+		arg.key_rxmic_len = 8;
+		break;
+	case WLAN_CIPHER_SUITE_WEP40:
+	case WLAN_CIPHER_SUITE_WEP104:
+		arg.key_cipher = WMI_CIPHER_WEP;
+		/* AP/IBSS mode requires self-key to be groupwise
+		 * Otherwise pairwise key must be set */
+		if (memcmp(macaddr, arvif->vif->addr, ETH_ALEN))
+			arg.key_flags = WMI_KEY_PAIRWISE;
+		break;
+	default:
+		ath10k_warn("cipher %d is not supported\n", key->cipher);
+		return -EOPNOTSUPP;
+	}
+
+	if (cmd == DISABLE_KEY) {
+		arg.key_cipher = WMI_CIPHER_NONE;
+		arg.key_data = NULL;
+	}
+
+	return ath10k_wmi_vdev_install_key(arvif->ar, &arg);
+}
+
+static int ath10k_install_key(struct ath10k_vif *arvif,
+			      struct ieee80211_key_conf *key,
+			      enum set_key_cmd cmd,
+			      const u8 *macaddr)
+{
+	struct ath10k *ar = arvif->ar;
+	int ret;
+
+	INIT_COMPLETION(ar->install_key_done);
+
+	ret = ath10k_send_key(arvif, key, cmd, macaddr);
+	if (ret)
+		return ret;
+
+	ret = wait_for_completion_timeout(&ar->install_key_done, 3*HZ);
+	if (ret == 0)
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+static int ath10k_install_peer_wep_keys(struct ath10k_vif *arvif,
+					const u8 *addr)
+{
+	struct ath10k *ar = arvif->ar;
+	struct ath10k_peer *peer;
+	int ret;
+	int i;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	spin_lock_bh(&ar->data_lock);
+	peer = ath10k_peer_find(ar, arvif->vdev_id, addr);
+	spin_unlock_bh(&ar->data_lock);
+
+	if (!peer)
+		return -ENOENT;
+
+	for (i = 0; i < ARRAY_SIZE(arvif->wep_keys); i++) {
+		if (arvif->wep_keys[i] == NULL)
+			continue;
+
+		ret = ath10k_install_key(arvif, arvif->wep_keys[i], SET_KEY,
+					 addr);
+		if (ret)
+			return ret;
+
+		peer->keys[i] = arvif->wep_keys[i];
+	}
+
+	return 0;
+}
+
+static int ath10k_clear_peer_keys(struct ath10k_vif *arvif,
+				  const u8 *addr)
+{
+	struct ath10k *ar = arvif->ar;
+	struct ath10k_peer *peer;
+	int first_errno = 0;
+	int ret;
+	int i;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	spin_lock_bh(&ar->data_lock);
+	peer = ath10k_peer_find(ar, arvif->vdev_id, addr);
+	spin_unlock_bh(&ar->data_lock);
+
+	if (!peer)
+		return -ENOENT;
+
+	for (i = 0; i < ARRAY_SIZE(peer->keys); i++) {
+		if (peer->keys[i] == NULL)
+			continue;
+
+		ret = ath10k_install_key(arvif, peer->keys[i],
+					 DISABLE_KEY, addr);
+		if (ret && first_errno == 0)
+			first_errno = ret;
+
+		if (ret)
+			ath10k_warn("could not remove peer wep key %d (%d)\n",
+				    i, ret);
+
+		peer->keys[i] = NULL;
+	}
+
+	return first_errno;
+}
+
+static int ath10k_clear_vdev_key(struct ath10k_vif *arvif,
+				 struct ieee80211_key_conf *key)
+{
+	struct ath10k *ar = arvif->ar;
+	struct ath10k_peer *peer;
+	u8 addr[ETH_ALEN];
+	int first_errno = 0;
+	int ret;
+	int i;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	for (;;) {
+		/* since ath10k_install_key we can't hold data_lock all the
+		 * time, so we try to remove the keys incrementally */
+		spin_lock_bh(&ar->data_lock);
+		i = 0;
+		list_for_each_entry(peer, &ar->peers, list) {
+			for (i = 0; i < ARRAY_SIZE(peer->keys); i++) {
+				if (peer->keys[i] == key) {
+					memcpy(addr, peer->addr, ETH_ALEN);
+					peer->keys[i] = NULL;
+					break;
+				}
+			}
+
+			if (i < ARRAY_SIZE(peer->keys))
+				break;
+		}
+		spin_unlock_bh(&ar->data_lock);
+
+		if (i == ARRAY_SIZE(peer->keys))
+			break;
+
+		ret = ath10k_install_key(arvif, key, DISABLE_KEY, addr);
+		if (ret && first_errno == 0)
+			first_errno = ret;
+
+		if (ret)
+			ath10k_warn("could not remove key for %pM\n", addr);
+	}
+
+	return first_errno;
+}
+
+
+/*********************/
+/* General utilities */
+/*********************/
+
+static inline enum wmi_phy_mode
+chan_to_phymode(const struct cfg80211_chan_def *chandef)
+{
+	enum wmi_phy_mode phymode = MODE_UNKNOWN;
+
+	switch (chandef->chan->band) {
+	case IEEE80211_BAND_2GHZ:
+		switch (chandef->width) {
+		case NL80211_CHAN_WIDTH_20_NOHT:
+			phymode = MODE_11G;
+			break;
+		case NL80211_CHAN_WIDTH_20:
+			phymode = MODE_11NG_HT20;
+			break;
+		case NL80211_CHAN_WIDTH_40:
+			phymode = MODE_11NG_HT40;
+			break;
+		case NL80211_CHAN_WIDTH_80:
+		case NL80211_CHAN_WIDTH_80P80:
+		case NL80211_CHAN_WIDTH_160:
+			phymode = MODE_UNKNOWN;
+			break;
+		}
+		break;
+	case IEEE80211_BAND_5GHZ:
+		switch (chandef->width) {
+		case NL80211_CHAN_WIDTH_20_NOHT:
+			phymode = MODE_11A;
+			break;
+		case NL80211_CHAN_WIDTH_20:
+			phymode = MODE_11NA_HT20;
+			break;
+		case NL80211_CHAN_WIDTH_40:
+			phymode = MODE_11NA_HT40;
+			break;
+		case NL80211_CHAN_WIDTH_80:
+			phymode = MODE_11AC_VHT80;
+			break;
+		case NL80211_CHAN_WIDTH_80P80:
+		case NL80211_CHAN_WIDTH_160:
+			phymode = MODE_UNKNOWN;
+			break;
+		}
+		break;
+	default:
+		break;
+	}
+
+	WARN_ON(phymode == MODE_UNKNOWN);
+	return phymode;
+}
+
+static u8 ath10k_parse_mpdudensity(u8 mpdudensity)
+{
+/*
+ * 802.11n D2.0 defined values for "Minimum MPDU Start Spacing":
+ *   0 for no restriction
+ *   1 for 1/4 us
+ *   2 for 1/2 us
+ *   3 for 1 us
+ *   4 for 2 us
+ *   5 for 4 us
+ *   6 for 8 us
+ *   7 for 16 us
+ */
+	switch (mpdudensity) {
+	case 0:
+		return 0;
+	case 1:
+	case 2:
+	case 3:
+	/* Our lower layer calculations limit our precision to
+	   1 microsecond */
+		return 1;
+	case 4:
+		return 2;
+	case 5:
+		return 4;
+	case 6:
+		return 8;
+	case 7:
+		return 16;
+	default:
+		return 0;
+	}
+}
+
+static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr)
+{
+	int ret;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	ret = ath10k_wmi_peer_create(ar, vdev_id, addr);
+	if (ret)
+		return ret;
+
+	ret = ath10k_wait_for_peer_created(ar, vdev_id, addr);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr)
+{
+	int ret;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	ret = ath10k_wmi_peer_delete(ar, vdev_id, addr);
+	if (ret)
+		return ret;
+
+	ret = ath10k_wait_for_peer_deleted(ar, vdev_id, addr);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id)
+{
+	struct ath10k_peer *peer, *tmp;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	spin_lock_bh(&ar->data_lock);
+	list_for_each_entry_safe(peer, tmp, &ar->peers, list) {
+		if (peer->vdev_id != vdev_id)
+			continue;
+
+		ath10k_warn("removing stale peer %pM from vdev_id %d\n",
+			    peer->addr, vdev_id);
+
+		list_del(&peer->list);
+		kfree(peer);
+	}
+	spin_unlock_bh(&ar->data_lock);
+}
+
+/************************/
+/* Interface management */
+/************************/
+
+static inline int ath10k_vdev_setup_sync(struct ath10k *ar)
+{
+	int ret;
+
+	ret = wait_for_completion_timeout(&ar->vdev_setup_done,
+					  ATH10K_VDEV_SETUP_TIMEOUT_HZ);
+	if (ret == 0)
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+static int ath10k_vdev_start(struct ath10k_vif *arvif)
+{
+	struct ath10k *ar = arvif->ar;
+	struct ieee80211_conf *conf = &ar->hw->conf;
+	struct ieee80211_channel *channel = conf->chandef.chan;
+	struct wmi_vdev_start_request_arg arg = {};
+	int ret = 0;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	INIT_COMPLETION(ar->vdev_setup_done);
+
+	arg.vdev_id = arvif->vdev_id;
+	arg.dtim_period = arvif->dtim_period;
+	arg.bcn_intval = arvif->beacon_interval;
+
+	arg.channel.freq = channel->center_freq;
+
+	arg.channel.band_center_freq1 = conf->chandef.center_freq1;
+
+	arg.channel.mode = chan_to_phymode(&conf->chandef);
+
+	arg.channel.min_power = channel->max_power * 3;
+	arg.channel.max_power = channel->max_power * 4;
+	arg.channel.max_reg_power = channel->max_reg_power * 4;
+	arg.channel.max_antenna_gain = channel->max_antenna_gain;
+
+	if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
+		arg.ssid = arvif->u.ap.ssid;
+		arg.ssid_len = arvif->u.ap.ssid_len;
+		arg.hidden_ssid = arvif->u.ap.hidden_ssid;
+	} else if (arvif->vdev_type == WMI_VDEV_TYPE_IBSS) {
+		arg.ssid = arvif->vif->bss_conf.ssid;
+		arg.ssid_len = arvif->vif->bss_conf.ssid_len;
+	}
+
+	ret = ath10k_wmi_vdev_start(ar, &arg);
+	if (ret) {
+		ath10k_warn("WMI vdev start failed: ret %d\n", ret);
+		return ret;
+	}
+
+	ret = ath10k_vdev_setup_sync(ar);
+	if (ret) {
+		ath10k_warn("vdev setup failed %d\n", ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+static int ath10k_vdev_stop(struct ath10k_vif *arvif)
+{
+	struct ath10k *ar = arvif->ar;
+	int ret;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	INIT_COMPLETION(ar->vdev_setup_done);
+
+	ret = ath10k_wmi_vdev_stop(ar, arvif->vdev_id);
+	if (ret) {
+		ath10k_warn("WMI vdev stop failed: ret %d\n", ret);
+		return ret;
+	}
+
+	ret = ath10k_vdev_setup_sync(ar);
+	if (ret) {
+		ath10k_warn("vdev setup failed %d\n", ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+static int ath10k_monitor_start(struct ath10k *ar, int vdev_id)
+{
+	struct ieee80211_channel *channel = ar->hw->conf.chandef.chan;
+	struct wmi_vdev_start_request_arg arg = {};
+	enum nl80211_channel_type type;
+	int ret = 0;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	type = cfg80211_get_chandef_type(&ar->hw->conf.chandef);
+
+	arg.vdev_id = vdev_id;
+	arg.channel.freq = channel->center_freq;
+	arg.channel.band_center_freq1 = ar->hw->conf.chandef.center_freq1;
+
+	/* TODO setup this dynamically, what in case we
+	   don't have any vifs? */
+	arg.channel.mode = chan_to_phymode(&ar->hw->conf.chandef);
+
+	arg.channel.min_power = channel->max_power * 3;
+	arg.channel.max_power = channel->max_power * 4;
+	arg.channel.max_reg_power = channel->max_reg_power * 4;
+	arg.channel.max_antenna_gain = channel->max_antenna_gain;
+
+	ret = ath10k_wmi_vdev_start(ar, &arg);
+	if (ret) {
+		ath10k_warn("Monitor vdev start failed: ret %d\n", ret);
+		return ret;
+	}
+
+	ret = ath10k_vdev_setup_sync(ar);
+	if (ret) {
+		ath10k_warn("Monitor vdev setup failed %d\n", ret);
+		return ret;
+	}
+
+	ret = ath10k_wmi_vdev_up(ar, vdev_id, 0, ar->mac_addr);
+	if (ret) {
+		ath10k_warn("Monitor vdev up failed: %d\n", ret);
+		goto vdev_stop;
+	}
+
+	ar->monitor_vdev_id = vdev_id;
+	ar->monitor_enabled = true;
+
+	return 0;
+
+vdev_stop:
+	ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id);
+	if (ret)
+		ath10k_warn("Monitor vdev stop failed: %d\n", ret);
+
+	return ret;
+}
+
+static int ath10k_monitor_stop(struct ath10k *ar)
+{
+	int ret = 0;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	/* For some reasons, ath10k_wmi_vdev_down() here couse
+	 * often ath10k_wmi_vdev_stop() to fail. Next we could
+	 * not run monitor vdev and driver reload
+	 * required. Don't see such problems we skip
+	 * ath10k_wmi_vdev_down() here.
+	 */
+
+	ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id);
+	if (ret)
+		ath10k_warn("Monitor vdev stop failed: %d\n", ret);
+
+	ret = ath10k_vdev_setup_sync(ar);
+	if (ret)
+		ath10k_warn("Monitor_down sync failed: %d\n", ret);
+
+	ar->monitor_enabled = false;
+	return ret;
+}
+
+static int ath10k_monitor_create(struct ath10k *ar)
+{
+	int bit, ret = 0;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	if (ar->monitor_present) {
+		ath10k_warn("Monitor mode already enabled\n");
+		return 0;
+	}
+
+	bit = ffs(ar->free_vdev_map);
+	if (bit == 0) {
+		ath10k_warn("No free VDEV slots\n");
+		return -ENOMEM;
+	}
+
+	ar->monitor_vdev_id = bit - 1;
+	ar->free_vdev_map &= ~(1 << ar->monitor_vdev_id);
+
+	ret = ath10k_wmi_vdev_create(ar, ar->monitor_vdev_id,
+				     WMI_VDEV_TYPE_MONITOR,
+				     0, ar->mac_addr);
+	if (ret) {
+		ath10k_warn("WMI vdev monitor create failed: ret %d\n", ret);
+		goto vdev_fail;
+	}
+
+	ath10k_dbg(ATH10K_DBG_MAC, "Monitor interface created, vdev id: %d\n",
+		   ar->monitor_vdev_id);
+
+	ar->monitor_present = true;
+	return 0;
+
+vdev_fail:
+	/*
+	 * Restore the ID to the global map.
+	 */
+	ar->free_vdev_map |= 1 << (ar->monitor_vdev_id);
+	return ret;
+}
+
+static int ath10k_monitor_destroy(struct ath10k *ar)
+{
+	int ret = 0;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	if (!ar->monitor_present)
+		return 0;
+
+	ret = ath10k_wmi_vdev_delete(ar, ar->monitor_vdev_id);
+	if (ret) {
+		ath10k_warn("WMI vdev monitor delete failed: %d\n", ret);
+		return ret;
+	}
+
+	ar->free_vdev_map |= 1 << (ar->monitor_vdev_id);
+	ar->monitor_present = false;
+
+	ath10k_dbg(ATH10K_DBG_MAC, "Monitor interface destroyed, vdev id: %d\n",
+		   ar->monitor_vdev_id);
+	return ret;
+}
+
+static void ath10k_control_beaconing(struct ath10k_vif *arvif,
+				struct ieee80211_bss_conf *info)
+{
+	int ret = 0;
+
+	if (!info->enable_beacon) {
+		ath10k_vdev_stop(arvif);
+		return;
+	}
+
+	arvif->tx_seq_no = 0x1000;
+
+	ret = ath10k_vdev_start(arvif);
+	if (ret)
+		return;
+
+	ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, 0, info->bssid);
+	if (ret) {
+		ath10k_warn("Failed to bring up VDEV: %d\n",
+			    arvif->vdev_id);
+		return;
+	}
+	ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d up\n", arvif->vdev_id);
+}
+
+static void ath10k_control_ibss(struct ath10k_vif *arvif,
+				struct ieee80211_bss_conf *info,
+				const u8 self_peer[ETH_ALEN])
+{
+	int ret = 0;
+
+	if (!info->ibss_joined) {
+		ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, self_peer);
+		if (ret)
+			ath10k_warn("Failed to delete IBSS self peer:%pM for VDEV:%d ret:%d\n",
+				    self_peer, arvif->vdev_id, ret);
+
+		if (is_zero_ether_addr(arvif->u.ibss.bssid))
+			return;
+
+		ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id,
+					 arvif->u.ibss.bssid);
+		if (ret) {
+			ath10k_warn("Failed to delete IBSS BSSID peer:%pM for VDEV:%d ret:%d\n",
+				    arvif->u.ibss.bssid, arvif->vdev_id, ret);
+			return;
+		}
+
+		memset(arvif->u.ibss.bssid, 0, ETH_ALEN);
+
+		return;
+	}
+
+	ret = ath10k_peer_create(arvif->ar, arvif->vdev_id, self_peer);
+	if (ret) {
+		ath10k_warn("Failed to create IBSS self peer:%pM for VDEV:%d ret:%d\n",
+			    self_peer, arvif->vdev_id, ret);
+		return;
+	}
+
+	ret = ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id,
+					WMI_VDEV_PARAM_ATIM_WINDOW,
+					ATH10K_DEFAULT_ATIM);
+	if (ret)
+		ath10k_warn("Failed to set IBSS ATIM for VDEV:%d ret:%d\n",
+			    arvif->vdev_id, ret);
+}
+
+/*
+ * Review this when mac80211 gains per-interface powersave support.
+ */
+static void ath10k_ps_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
+{
+	struct ath10k_generic_iter *ar_iter = data;
+	struct ieee80211_conf *conf = &ar_iter->ar->hw->conf;
+	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+	enum wmi_sta_powersave_param param;
+	enum wmi_sta_ps_mode psmode;
+	int ret;
+
+	if (vif->type != NL80211_IFTYPE_STATION)
+		return;
+
+	if (conf->flags & IEEE80211_CONF_PS) {
+		psmode = WMI_STA_PS_MODE_ENABLED;
+		param = WMI_STA_PS_PARAM_INACTIVITY_TIME;
+
+		ret = ath10k_wmi_set_sta_ps_param(ar_iter->ar,
+						  arvif->vdev_id,
+						  param,
+						  conf->dynamic_ps_timeout);
+		if (ret) {
+			ath10k_warn("Failed to set inactivity time for VDEV: %d\n",
+				    arvif->vdev_id);
+			return;
+		}
+
+		ar_iter->ret = ret;
+	} else {
+		psmode = WMI_STA_PS_MODE_DISABLED;
+	}
+
+	ar_iter->ret = ath10k_wmi_set_psmode(ar_iter->ar, arvif->vdev_id,
+					     psmode);
+	if (ar_iter->ret)
+		ath10k_warn("Failed to set PS Mode: %d for VDEV: %d\n",
+			    psmode, arvif->vdev_id);
+	else
+		ath10k_dbg(ATH10K_DBG_MAC, "Set PS Mode: %d for VDEV: %d\n",
+			   psmode, arvif->vdev_id);
+}
+
+/**********************/
+/* Station management */
+/**********************/
+
+static void ath10k_peer_assoc_h_basic(struct ath10k *ar,
+				      struct ath10k_vif *arvif,
+				      struct ieee80211_sta *sta,
+				      struct ieee80211_bss_conf *bss_conf,
+				      struct wmi_peer_assoc_complete_arg *arg)
+{
+	memcpy(arg->addr, sta->addr, ETH_ALEN);
+	arg->vdev_id = arvif->vdev_id;
+	arg->peer_aid = sta->aid;
+	arg->peer_flags |= WMI_PEER_AUTH;
+
+	if (arvif->vdev_type == WMI_VDEV_TYPE_STA)
+		/*
+		 * Seems FW have problems with Power Save in STA
+		 * mode when we setup this parameter to high (eg. 5).
+		 * Often we see that FW don't send NULL (with clean P flags)
+		 * frame even there is info about buffered frames in beacons.
+		 * Sometimes we have to wait more than 10 seconds before FW
+		 * will wakeup. Often sending one ping from AP to our device
+		 * just fail (more than 50%).
+		 *
+		 * Seems setting this FW parameter to 1 couse FW
+		 * will check every beacon and will wakup immediately
+		 * after detection buffered data.
+		 */
+		arg->peer_listen_intval = 1;
+	else
+		arg->peer_listen_intval = ar->hw->conf.listen_interval;
+
+	arg->peer_num_spatial_streams = 1;
+
+	/*
+	 * The assoc capabilities are available only in managed mode.
+	 */
+	if (arvif->vdev_type == WMI_VDEV_TYPE_STA && bss_conf)
+		arg->peer_caps = bss_conf->assoc_capability;
+}
+
+static void ath10k_peer_assoc_h_crypto(struct ath10k *ar,
+				       struct ath10k_vif *arvif,
+				       struct wmi_peer_assoc_complete_arg *arg)
+{
+	struct ieee80211_vif *vif = arvif->vif;
+	struct ieee80211_bss_conf *info = &vif->bss_conf;
+	struct cfg80211_bss *bss;
+	const u8 *rsnie = NULL;
+	const u8 *wpaie = NULL;
+
+	bss = cfg80211_get_bss(ar->hw->wiphy, ar->hw->conf.chandef.chan,
+			       info->bssid, NULL, 0, 0, 0);
+	if (bss) {
+		const struct cfg80211_bss_ies *ies;
+
+		rcu_read_lock();
+		rsnie = ieee80211_bss_get_ie(bss, WLAN_EID_RSN);
+
+		ies = rcu_dereference(bss->ies);
+
+		wpaie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+				WLAN_OUI_TYPE_MICROSOFT_WPA,
+				ies->data,
+				ies->len);
+		rcu_read_unlock();
+		cfg80211_put_bss(ar->hw->wiphy, bss);
+	}
+
+	/* FIXME: base on RSN IE/WPA IE is a correct idea? */
+	if (rsnie || wpaie) {
+		ath10k_dbg(ATH10K_DBG_WMI, "%s: rsn ie found\n", __func__);
+		arg->peer_flags |= WMI_PEER_NEED_PTK_4_WAY;
+	}
+
+	if (wpaie) {
+		ath10k_dbg(ATH10K_DBG_WMI, "%s: wpa ie found\n", __func__);
+		arg->peer_flags |= WMI_PEER_NEED_GTK_2_WAY;
+	}
+}
+
+static void ath10k_peer_assoc_h_rates(struct ath10k *ar,
+				      struct ieee80211_sta *sta,
+				      struct wmi_peer_assoc_complete_arg *arg)
+{
+	struct wmi_rate_set_arg *rateset = &arg->peer_legacy_rates;
+	const struct ieee80211_supported_band *sband;
+	const struct ieee80211_rate *rates;
+	u32 ratemask;
+	int i;
+
+	sband = ar->hw->wiphy->bands[ar->hw->conf.chandef.chan->band];
+	ratemask = sta->supp_rates[ar->hw->conf.chandef.chan->band];
+	rates = sband->bitrates;
+
+	rateset->num_rates = 0;
+
+	for (i = 0; i < 32; i++, ratemask >>= 1, rates++) {
+		if (!(ratemask & 1))
+			continue;
+
+		rateset->rates[rateset->num_rates] = rates->hw_value;
+		rateset->num_rates++;
+	}
+}
+
+static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
+				   struct ieee80211_sta *sta,
+				   struct wmi_peer_assoc_complete_arg *arg)
+{
+	const struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
+	int smps;
+	int i, n;
+
+	if (!ht_cap->ht_supported)
+		return;
+
+	arg->peer_flags |= WMI_PEER_HT;
+	arg->peer_max_mpdu = (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR +
+				    ht_cap->ampdu_factor)) - 1;
+
+	arg->peer_mpdu_density =
+		ath10k_parse_mpdudensity(ht_cap->ampdu_density);
+
+	arg->peer_ht_caps = ht_cap->cap;
+	arg->peer_rate_caps |= WMI_RC_HT_FLAG;
+
+	if (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING)
+		arg->peer_flags |= WMI_PEER_LDPC;
+
+	if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) {
+		arg->peer_flags |= WMI_PEER_40MHZ;
+		arg->peer_rate_caps |= WMI_RC_CW40_FLAG;
+	}
+
+	if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20)
+		arg->peer_rate_caps |= WMI_RC_SGI_FLAG;
+
+	if (ht_cap->cap & IEEE80211_HT_CAP_SGI_40)
+		arg->peer_rate_caps |= WMI_RC_SGI_FLAG;
+
+	if (ht_cap->cap & IEEE80211_HT_CAP_TX_STBC) {
+		arg->peer_rate_caps |= WMI_RC_TX_STBC_FLAG;
+		arg->peer_flags |= WMI_PEER_STBC;
+	}
+
+	if (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC) {
+		u32 stbc;
+		stbc = ht_cap->cap & IEEE80211_HT_CAP_RX_STBC;
+		stbc = stbc >> IEEE80211_HT_CAP_RX_STBC_SHIFT;
+		stbc = stbc << WMI_RC_RX_STBC_FLAG_S;
+		arg->peer_rate_caps |= stbc;
+		arg->peer_flags |= WMI_PEER_STBC;
+	}
+
+	smps = ht_cap->cap & IEEE80211_HT_CAP_SM_PS;
+	smps >>= IEEE80211_HT_CAP_SM_PS_SHIFT;
+
+	if (smps == WLAN_HT_CAP_SM_PS_STATIC) {
+		arg->peer_flags |= WMI_PEER_SPATIAL_MUX;
+		arg->peer_flags |= WMI_PEER_STATIC_MIMOPS;
+	} else if (smps == WLAN_HT_CAP_SM_PS_DYNAMIC) {
+		arg->peer_flags |= WMI_PEER_SPATIAL_MUX;
+		arg->peer_flags |= WMI_PEER_DYN_MIMOPS;
+	}
+
+	if (ht_cap->mcs.rx_mask[1] && ht_cap->mcs.rx_mask[2])
+		arg->peer_rate_caps |= WMI_RC_TS_FLAG;
+	else if (ht_cap->mcs.rx_mask[1])
+		arg->peer_rate_caps |= WMI_RC_DS_FLAG;
+
+	for (i = 0, n = 0; i < IEEE80211_HT_MCS_MASK_LEN*8; i++)
+		if (ht_cap->mcs.rx_mask[i/8] & (1 << i%8))
+			arg->peer_ht_rates.rates[n++] = i;
+
+	arg->peer_ht_rates.num_rates = n;
+	arg->peer_num_spatial_streams = max((n+7) / 8, 1);
+
+	ath10k_dbg(ATH10K_DBG_MAC, "mcs cnt %d nss %d\n",
+		   arg->peer_ht_rates.num_rates,
+		   arg->peer_num_spatial_streams);
+}
+
+static void ath10k_peer_assoc_h_qos_ap(struct ath10k *ar,
+				       struct ath10k_vif *arvif,
+				       struct ieee80211_sta *sta,
+				       struct ieee80211_bss_conf *bss_conf,
+				       struct wmi_peer_assoc_complete_arg *arg)
+{
+	u32 uapsd = 0;
+	u32 max_sp = 0;
+
+	if (sta->wme)
+		arg->peer_flags |= WMI_PEER_QOS;
+
+	if (sta->wme && sta->uapsd_queues) {
+		ath10k_dbg(ATH10K_DBG_MAC, "uapsd_queues: 0x%X, max_sp: %d\n",
+			   sta->uapsd_queues, sta->max_sp);
+
+		arg->peer_flags |= WMI_PEER_APSD;
+		arg->peer_flags |= WMI_RC_UAPSD_FLAG;
+
+		if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
+			uapsd |= WMI_AP_PS_UAPSD_AC3_DELIVERY_EN |
+				 WMI_AP_PS_UAPSD_AC3_TRIGGER_EN;
+		if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI)
+			uapsd |= WMI_AP_PS_UAPSD_AC2_DELIVERY_EN |
+				 WMI_AP_PS_UAPSD_AC2_TRIGGER_EN;
+		if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK)
+			uapsd |= WMI_AP_PS_UAPSD_AC1_DELIVERY_EN |
+				 WMI_AP_PS_UAPSD_AC1_TRIGGER_EN;
+		if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE)
+			uapsd |= WMI_AP_PS_UAPSD_AC0_DELIVERY_EN |
+				 WMI_AP_PS_UAPSD_AC0_TRIGGER_EN;
+
+
+		if (sta->max_sp < MAX_WMI_AP_PS_PEER_PARAM_MAX_SP)
+			max_sp = sta->max_sp;
+
+		ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
+					   sta->addr,
+					   WMI_AP_PS_PEER_PARAM_UAPSD,
+					   uapsd);
+
+		ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
+					   sta->addr,
+					   WMI_AP_PS_PEER_PARAM_MAX_SP,
+					   max_sp);
+
+		/* TODO setup this based on STA listen interval and
+		   beacon interval. Currently we don't know
+		   sta->listen_interval - mac80211 patch required.
+		   Currently use 10 seconds */
+		ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
+					   sta->addr,
+					   WMI_AP_PS_PEER_PARAM_AGEOUT_TIME,
+					   10);
+	}
+}
+
+static void ath10k_peer_assoc_h_qos_sta(struct ath10k *ar,
+					struct ath10k_vif *arvif,
+					struct ieee80211_sta *sta,
+					struct ieee80211_bss_conf *bss_conf,
+					struct wmi_peer_assoc_complete_arg *arg)
+{
+	if (bss_conf->qos)
+		arg->peer_flags |= WMI_PEER_QOS;
+}
+
+static void ath10k_peer_assoc_h_vht(struct ath10k *ar,
+				    struct ieee80211_sta *sta,
+				    struct wmi_peer_assoc_complete_arg *arg)
+{
+	const struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
+
+	if (!vht_cap->vht_supported)
+		return;
+
+	arg->peer_flags |= WMI_PEER_VHT;
+
+	arg->peer_vht_caps = vht_cap->cap;
+
+	if (sta->bandwidth == IEEE80211_STA_RX_BW_80)
+		arg->peer_flags |= WMI_PEER_80MHZ;
+
+	arg->peer_vht_rates.rx_max_rate =
+		__le16_to_cpu(vht_cap->vht_mcs.rx_highest);
+	arg->peer_vht_rates.rx_mcs_set =
+		__le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map);
+	arg->peer_vht_rates.tx_max_rate =
+		__le16_to_cpu(vht_cap->vht_mcs.tx_highest);
+	arg->peer_vht_rates.tx_mcs_set =
+		__le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map);
+
+	ath10k_dbg(ATH10K_DBG_MAC, "mac vht peer\n");
+}
+
+static void ath10k_peer_assoc_h_qos(struct ath10k *ar,
+				    struct ath10k_vif *arvif,
+				    struct ieee80211_sta *sta,
+				    struct ieee80211_bss_conf *bss_conf,
+				    struct wmi_peer_assoc_complete_arg *arg)
+{
+	switch (arvif->vdev_type) {
+	case WMI_VDEV_TYPE_AP:
+		ath10k_peer_assoc_h_qos_ap(ar, arvif, sta, bss_conf, arg);
+		break;
+	case WMI_VDEV_TYPE_STA:
+		ath10k_peer_assoc_h_qos_sta(ar, arvif, sta, bss_conf, arg);
+		break;
+	default:
+		break;
+	}
+}
+
+static void ath10k_peer_assoc_h_phymode(struct ath10k *ar,
+					struct ath10k_vif *arvif,
+					struct ieee80211_sta *sta,
+					struct wmi_peer_assoc_complete_arg *arg)
+{
+	enum wmi_phy_mode phymode = MODE_UNKNOWN;
+
+	/* FIXME: add VHT */
+
+	switch (ar->hw->conf.chandef.chan->band) {
+	case IEEE80211_BAND_2GHZ:
+		if (sta->ht_cap.ht_supported) {
+			if (sta->bandwidth == IEEE80211_STA_RX_BW_40)
+				phymode = MODE_11NG_HT40;
+			else
+				phymode = MODE_11NG_HT20;
+		} else {
+			phymode = MODE_11G;
+		}
+
+		break;
+	case IEEE80211_BAND_5GHZ:
+		if (sta->ht_cap.ht_supported) {
+			if (sta->bandwidth == IEEE80211_STA_RX_BW_40)
+				phymode = MODE_11NA_HT40;
+			else
+				phymode = MODE_11NA_HT20;
+		} else {
+			phymode = MODE_11A;
+		}
+
+		break;
+	default:
+		break;
+	}
+
+	arg->peer_phymode = phymode;
+	WARN_ON(phymode == MODE_UNKNOWN);
+}
+
+static int ath10k_peer_assoc(struct ath10k *ar,
+			     struct ath10k_vif *arvif,
+			     struct ieee80211_sta *sta,
+			     struct ieee80211_bss_conf *bss_conf)
+{
+	struct wmi_peer_assoc_complete_arg arg;
+
+	memset(&arg, 0, sizeof(struct wmi_peer_assoc_complete_arg));
+
+	ath10k_peer_assoc_h_basic(ar, arvif, sta, bss_conf, &arg);
+	ath10k_peer_assoc_h_crypto(ar, arvif, &arg);
+	ath10k_peer_assoc_h_rates(ar, sta, &arg);
+	ath10k_peer_assoc_h_ht(ar, sta, &arg);
+	ath10k_peer_assoc_h_vht(ar, sta, &arg);
+	ath10k_peer_assoc_h_qos(ar, arvif, sta, bss_conf, &arg);
+	ath10k_peer_assoc_h_phymode(ar, arvif, sta, &arg);
+
+	return ath10k_wmi_peer_assoc(ar, &arg);
+}
+
+/* can be called only in mac80211 callbacks due to `key_count` usage */
+static void ath10k_bss_assoc(struct ieee80211_hw *hw,
+			     struct ieee80211_vif *vif,
+			     struct ieee80211_bss_conf *bss_conf)
+{
+	struct ath10k *ar = hw->priv;
+	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+	struct ieee80211_sta *ap_sta;
+	int ret;
+
+	rcu_read_lock();
+
+	ap_sta = ieee80211_find_sta(vif, bss_conf->bssid);
+	if (!ap_sta) {
+		ath10k_warn("Failed to find station entry for %pM\n",
+			    bss_conf->bssid);
+		rcu_read_unlock();
+		return;
+	}
+
+	ret = ath10k_peer_assoc(ar, arvif, ap_sta, bss_conf);
+	if (ret) {
+		ath10k_warn("Peer assoc failed for %pM\n", bss_conf->bssid);
+		rcu_read_unlock();
+		return;
+	}
+
+	rcu_read_unlock();
+
+	ret = ath10k_wmi_vdev_up(ar, arvif->vdev_id, bss_conf->aid,
+				 bss_conf->bssid);
+	if (ret)
+		ath10k_warn("VDEV: %d up failed: ret %d\n",
+			    arvif->vdev_id, ret);
+	else
+		ath10k_dbg(ATH10K_DBG_MAC,
+			   "VDEV: %d associated, BSSID: %pM, AID: %d\n",
+			   arvif->vdev_id, bss_conf->bssid, bss_conf->aid);
+}
+
+/*
+ * FIXME: flush TIDs
+ */
+static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
+				struct ieee80211_vif *vif)
+{
+	struct ath10k *ar = hw->priv;
+	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+	int ret;
+
+	/*
+	 * For some reason, calling VDEV-DOWN before VDEV-STOP
+	 * makes the FW to send frames via HTT after disassociation.
+	 * No idea why this happens, even though VDEV-DOWN is supposed
+	 * to be analogous to link down, so just stop the VDEV.
+	 */
+	ret = ath10k_vdev_stop(arvif);
+	if (!ret)
+		ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d stopped\n",
+			   arvif->vdev_id);
+
+	/*
+	 * If we don't call VDEV-DOWN after VDEV-STOP FW will remain active and
+	 * report beacons from previously associated network through HTT.
+	 * This in turn would spam mac80211 WARN_ON if we bring down all
+	 * interfaces as it expects there is no rx when no interface is
+	 * running.
+	 */
+	ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id);
+	if (ret)
+		ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d ath10k_wmi_vdev_down failed (%d)\n",
+			   arvif->vdev_id, ret);
+
+	ath10k_wmi_flush_tx(ar);
+
+	arvif->def_wep_key_index = 0;
+}
+
+static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif,
+				struct ieee80211_sta *sta)
+{
+	int ret = 0;
+
+	ret = ath10k_peer_assoc(ar, arvif, sta, NULL);
+	if (ret) {
+		ath10k_warn("WMI peer assoc failed for %pM\n", sta->addr);
+		return ret;
+	}
+
+	ret = ath10k_install_peer_wep_keys(arvif, sta->addr);
+	if (ret) {
+		ath10k_warn("could not install peer wep keys (%d)\n", ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+static int ath10k_station_disassoc(struct ath10k *ar, struct ath10k_vif *arvif,
+				   struct ieee80211_sta *sta)
+{
+	int ret = 0;
+
+	ret = ath10k_clear_peer_keys(arvif, sta->addr);
+	if (ret) {
+		ath10k_warn("could not clear all peer wep keys (%d)\n", ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+/**************/
+/* Regulatory */
+/**************/
+
+static int ath10k_update_channel_list(struct ath10k *ar)
+{
+	struct ieee80211_hw *hw = ar->hw;
+	struct ieee80211_supported_band **bands;
+	enum ieee80211_band band;
+	struct ieee80211_channel *channel;
+	struct wmi_scan_chan_list_arg arg = {0};
+	struct wmi_channel_arg *ch;
+	bool passive;
+	int len;
+	int ret;
+	int i;
+
+	bands = hw->wiphy->bands;
+	for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+		if (!bands[band])
+			continue;
+
+		for (i = 0; i < bands[band]->n_channels; i++) {
+			if (bands[band]->channels[i].flags &
+			    IEEE80211_CHAN_DISABLED)
+				continue;
+
+			arg.n_channels++;
+		}
+	}
+
+	len = sizeof(struct wmi_channel_arg) * arg.n_channels;
+	arg.channels = kzalloc(len, GFP_KERNEL);
+	if (!arg.channels)
+		return -ENOMEM;
+
+	ch = arg.channels;
+	for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+		if (!bands[band])
+			continue;
+
+		for (i = 0; i < bands[band]->n_channels; i++) {
+			channel = &bands[band]->channels[i];
+
+			if (channel->flags & IEEE80211_CHAN_DISABLED)
+				continue;
+
+			ch->allow_ht   = true;
+
+			/* FIXME: when should we really allow VHT? */
+			ch->allow_vht = true;
+
+			ch->allow_ibss =
+				!(channel->flags & IEEE80211_CHAN_NO_IBSS);
+
+			ch->ht40plus =
+				!(channel->flags & IEEE80211_CHAN_NO_HT40PLUS);
+
+			passive = channel->flags & IEEE80211_CHAN_PASSIVE_SCAN;
+			ch->passive = passive;
+
+			ch->freq = channel->center_freq;
+			ch->min_power = channel->max_power * 3;
+			ch->max_power = channel->max_power * 4;
+			ch->max_reg_power = channel->max_reg_power * 4;
+			ch->max_antenna_gain = channel->max_antenna_gain;
+			ch->reg_class_id = 0; /* FIXME */
+
+			/* FIXME: why use only legacy modes, why not any
+			 * HT/VHT modes? Would that even make any
+			 * difference? */
+			if (channel->band == IEEE80211_BAND_2GHZ)
+				ch->mode = MODE_11G;
+			else
+				ch->mode = MODE_11A;
+
+			if (WARN_ON_ONCE(ch->mode == MODE_UNKNOWN))
+				continue;
+
+			ath10k_dbg(ATH10K_DBG_WMI,
+				   "%s: [%zd/%d] freq %d maxpower %d regpower %d antenna %d mode %d\n",
+				   __func__, ch - arg.channels, arg.n_channels,
+				   ch->freq, ch->max_power, ch->max_reg_power,
+				   ch->max_antenna_gain, ch->mode);
+
+			ch++;
+		}
+	}
+
+	ret = ath10k_wmi_scan_chan_list(ar, &arg);
+	kfree(arg.channels);
+
+	return ret;
+}
+
+static void ath10k_reg_notifier(struct wiphy *wiphy,
+				struct regulatory_request *request)
+{
+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+	struct reg_dmn_pair_mapping *regpair;
+	struct ath10k *ar = hw->priv;
+	int ret;
+
+	ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory);
+
+	ret = ath10k_update_channel_list(ar);
+	if (ret)
+		ath10k_warn("could not update channel list (%d)\n", ret);
+
+	regpair = ar->ath_common.regulatory.regpair;
+	/* Target allows setting up per-band regdomain but ath_common provides
+	 * a combined one only */
+	ret = ath10k_wmi_pdev_set_regdomain(ar,
+					    regpair->regDmnEnum,
+					    regpair->regDmnEnum, /* 2ghz */
+					    regpair->regDmnEnum, /* 5ghz */
+					    regpair->reg_2ghz_ctl,
+					    regpair->reg_5ghz_ctl);
+	if (ret)
+		ath10k_warn("could not set pdev regdomain (%d)\n", ret);
+}
+
+/***************/
+/* TX handlers */
+/***************/
+
+/*
+ * Frames sent to the FW have to be in "Native Wifi" format.
+ * Strip the QoS field from the 802.11 header.
+ */
+static void ath10k_tx_h_qos_workaround(struct ieee80211_hw *hw,
+				       struct ieee80211_tx_control *control,
+				       struct sk_buff *skb)
+{
+	struct ieee80211_hdr *hdr = (void *)skb->data;
+	u8 *qos_ctl;
+
+	if (!ieee80211_is_data_qos(hdr->frame_control))
+		return;
+
+	qos_ctl = ieee80211_get_qos_ctl(hdr);
+	memmove(qos_ctl, qos_ctl + IEEE80211_QOS_CTL_LEN,
+		skb->len - ieee80211_hdrlen(hdr->frame_control));
+	skb_trim(skb, skb->len - IEEE80211_QOS_CTL_LEN);
+}
+
+static void ath10k_tx_h_update_wep_key(struct sk_buff *skb)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_vif *vif = info->control.vif;
+	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+	struct ath10k *ar = arvif->ar;
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+	struct ieee80211_key_conf *key = info->control.hw_key;
+	int ret;
+
+	/* TODO AP mode should be implemented */
+	if (vif->type != NL80211_IFTYPE_STATION)
+		return;
+
+	if (!ieee80211_has_protected(hdr->frame_control))
+		return;
+
+	if (!key)
+		return;
+
+	if (key->cipher != WLAN_CIPHER_SUITE_WEP40 &&
+	    key->cipher != WLAN_CIPHER_SUITE_WEP104)
+		return;
+
+	if (key->keyidx == arvif->def_wep_key_index)
+		return;
+
+	ath10k_dbg(ATH10K_DBG_MAC, "new wep keyidx will be %d\n", key->keyidx);
+
+	ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+					WMI_VDEV_PARAM_DEF_KEYID,
+					key->keyidx);
+	if (ret) {
+		ath10k_warn("could not update wep keyidx (%d)\n", ret);
+		return;
+	}
+
+	arvif->def_wep_key_index = key->keyidx;
+}
+
+static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar, struct sk_buff *skb)
+{
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_vif *vif = info->control.vif;
+	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+
+	/* This is case only for P2P_GO */
+	if (arvif->vdev_type != WMI_VDEV_TYPE_AP ||
+	    arvif->vdev_subtype != WMI_VDEV_SUBTYPE_P2P_GO)
+		return;
+
+	if (unlikely(ieee80211_is_probe_resp(hdr->frame_control))) {
+		spin_lock_bh(&ar->data_lock);
+		if (arvif->u.ap.noa_data)
+			if (!pskb_expand_head(skb, 0, arvif->u.ap.noa_len,
+					      GFP_ATOMIC))
+				memcpy(skb_put(skb, arvif->u.ap.noa_len),
+				       arvif->u.ap.noa_data,
+				       arvif->u.ap.noa_len);
+		spin_unlock_bh(&ar->data_lock);
+	}
+}
+
+static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb)
+{
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+	int ret;
+
+	if (ieee80211_is_mgmt(hdr->frame_control))
+		ret = ath10k_htt_mgmt_tx(ar->htt, skb);
+	else if (ieee80211_is_nullfunc(hdr->frame_control))
+		/* FW does not report tx status properly for NullFunc frames
+		 * unless they are sent through mgmt tx path. mac80211 sends
+		 * those frames when it detects link/beacon loss and depends on
+		 * the tx status to be correct. */
+		ret = ath10k_htt_mgmt_tx(ar->htt, skb);
+	else
+		ret = ath10k_htt_tx(ar->htt, skb);
+
+	if (ret) {
+		ath10k_warn("tx failed (%d). dropping packet.\n", ret);
+		ieee80211_free_txskb(ar->hw, skb);
+	}
+}
+
+void ath10k_offchan_tx_purge(struct ath10k *ar)
+{
+	struct sk_buff *skb;
+
+	for (;;) {
+		skb = skb_dequeue(&ar->offchan_tx_queue);
+		if (!skb)
+			break;
+
+		ieee80211_free_txskb(ar->hw, skb);
+	}
+}
+
+void ath10k_offchan_tx_work(struct work_struct *work)
+{
+	struct ath10k *ar = container_of(work, struct ath10k, offchan_tx_work);
+	struct ath10k_peer *peer;
+	struct ieee80211_hdr *hdr;
+	struct sk_buff *skb;
+	const u8 *peer_addr;
+	int vdev_id;
+	int ret;
+
+	/* FW requirement: We must create a peer before FW will send out
+	 * an offchannel frame. Otherwise the frame will be stuck and
+	 * never transmitted. We delete the peer upon tx completion.
+	 * It is unlikely that a peer for offchannel tx will already be
+	 * present. However it may be in some rare cases so account for that.
+	 * Otherwise we might remove a legitimate peer and break stuff. */
+
+	for (;;) {
+		skb = skb_dequeue(&ar->offchan_tx_queue);
+		if (!skb)
+			break;
+
+		mutex_lock(&ar->conf_mutex);
+
+		ath10k_dbg(ATH10K_DBG_MAC, "processing offchannel skb %p\n",
+			   skb);
+
+		hdr = (struct ieee80211_hdr *)skb->data;
+		peer_addr = ieee80211_get_DA(hdr);
+		vdev_id = ATH10K_SKB_CB(skb)->htt.vdev_id;
+
+		spin_lock_bh(&ar->data_lock);
+		peer = ath10k_peer_find(ar, vdev_id, peer_addr);
+		spin_unlock_bh(&ar->data_lock);
+
+		if (peer)
+			ath10k_dbg(ATH10K_DBG_MAC, "peer %pM on vdev %d already present\n",
+				   peer_addr, vdev_id);
+
+		if (!peer) {
+			ret = ath10k_peer_create(ar, vdev_id, peer_addr);
+			if (ret)
+				ath10k_warn("peer %pM on vdev %d not created (%d)\n",
+					    peer_addr, vdev_id, ret);
+		}
+
+		spin_lock_bh(&ar->data_lock);
+		INIT_COMPLETION(ar->offchan_tx_completed);
+		ar->offchan_tx_skb = skb;
+		spin_unlock_bh(&ar->data_lock);
+
+		ath10k_tx_htt(ar, skb);
+
+		ret = wait_for_completion_timeout(&ar->offchan_tx_completed,
+						  3 * HZ);
+		if (ret <= 0)
+			ath10k_warn("timed out waiting for offchannel skb %p\n",
+				    skb);
+
+		if (!peer) {
+			ret = ath10k_peer_delete(ar, vdev_id, peer_addr);
+			if (ret)
+				ath10k_warn("peer %pM on vdev %d not deleted (%d)\n",
+					    peer_addr, vdev_id, ret);
+		}
+
+		mutex_unlock(&ar->conf_mutex);
+	}
+}
+
+/************/
+/* Scanning */
+/************/
+
+/*
+ * This gets called if we dont get a heart-beat during scan.
+ * This may indicate the FW has hung and we need to abort the
+ * scan manually to prevent cancel_hw_scan() from deadlocking
+ */
+void ath10k_reset_scan(unsigned long ptr)
+{
+	struct ath10k *ar = (struct ath10k *)ptr;
+
+	spin_lock_bh(&ar->data_lock);
+	if (!ar->scan.in_progress) {
+		spin_unlock_bh(&ar->data_lock);
+		return;
+	}
+
+	ath10k_warn("scan timeout. resetting. fw issue?\n");
+
+	if (ar->scan.is_roc)
+		ieee80211_remain_on_channel_expired(ar->hw);
+	else
+		ieee80211_scan_completed(ar->hw, 1 /* aborted */);
+
+	ar->scan.in_progress = false;
+	complete_all(&ar->scan.completed);
+	spin_unlock_bh(&ar->data_lock);
+}
+
+static int ath10k_abort_scan(struct ath10k *ar)
+{
+	struct wmi_stop_scan_arg arg = {
+		.req_id = 1, /* FIXME */
+		.req_type = WMI_SCAN_STOP_ONE,
+		.u.scan_id = ATH10K_SCAN_ID,
+	};
+	int ret;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	del_timer_sync(&ar->scan.timeout);
+
+	spin_lock_bh(&ar->data_lock);
+	if (!ar->scan.in_progress) {
+		spin_unlock_bh(&ar->data_lock);
+		return 0;
+	}
+
+	ar->scan.aborting = true;
+	spin_unlock_bh(&ar->data_lock);
+
+	ret = ath10k_wmi_stop_scan(ar, &arg);
+	if (ret) {
+		ath10k_warn("could not submit wmi stop scan (%d)\n", ret);
+		return -EIO;
+	}
+
+	ath10k_wmi_flush_tx(ar);
+
+	ret = wait_for_completion_timeout(&ar->scan.completed, 3*HZ);
+	if (ret == 0)
+		ath10k_warn("timed out while waiting for scan to stop\n");
+
+	/* scan completion may be done right after we timeout here, so let's
+	 * check the in_progress and tell mac80211 scan is completed. if we
+	 * don't do that and FW fails to send us scan completion indication
+	 * then userspace won't be able to scan anymore */
+	ret = 0;
+
+	spin_lock_bh(&ar->data_lock);
+	if (ar->scan.in_progress) {
+		ath10k_warn("could not stop scan. its still in progress\n");
+		ar->scan.in_progress = false;
+		ath10k_offchan_tx_purge(ar);
+		ret = -ETIMEDOUT;
+	}
+	spin_unlock_bh(&ar->data_lock);
+
+	return ret;
+}
+
+static int ath10k_start_scan(struct ath10k *ar,
+			     const struct wmi_start_scan_arg *arg)
+{
+	int ret;
+
+	lockdep_assert_held(&ar->conf_mutex);
+
+	ret = ath10k_wmi_start_scan(ar, arg);
+	if (ret)
+		return ret;
+
+	/* make sure we submit the command so the completion
+	* timeout makes sense */
+	ath10k_wmi_flush_tx(ar);
+
+	ret = wait_for_completion_timeout(&ar->scan.started, 1*HZ);
+	if (ret == 0) {
+		ath10k_abort_scan(ar);
+		return ret;
+	}
+
+	/* the scan can complete earlier, before we even
+	 * start the timer. in that case the timer handler
+	 * checks ar->scan.in_progress and bails out if its
+	 * false. Add a 200ms margin to account event/command
+	 * processing. */
+	mod_timer(&ar->scan.timeout, jiffies +
+		  msecs_to_jiffies(arg->max_scan_time+200));
+	return 0;
+}
+
+/**********************/
+/* mac80211 callbacks */
+/**********************/
+
+static void ath10k_tx(struct ieee80211_hw *hw,
+		      struct ieee80211_tx_control *control,
+		      struct sk_buff *skb)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+	struct ath10k *ar = hw->priv;
+	struct ath10k_vif *arvif = NULL;
+	u32 vdev_id = 0;
+	u8 tid;
+
+	if (info->control.vif) {
+		arvif = ath10k_vif_to_arvif(info->control.vif);
+		vdev_id = arvif->vdev_id;
+	} else if (ar->monitor_enabled) {
+		vdev_id = ar->monitor_vdev_id;
+	}
+
+	/* We should disable CCK RATE due to P2P */
+	if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)
+		ath10k_dbg(ATH10K_DBG_MAC, "IEEE80211_TX_CTL_NO_CCK_RATE\n");
+
+	/* we must calculate tid before we apply qos workaround
+	 * as we'd lose the qos control field */
+	tid = HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST;
+	if (ieee80211_is_data_qos(hdr->frame_control) &&
+	    is_unicast_ether_addr(ieee80211_get_DA(hdr))) {
+		u8 *qc = ieee80211_get_qos_ctl(hdr);
+		tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
+	}
+
+	ath10k_tx_h_qos_workaround(hw, control, skb);
+	ath10k_tx_h_update_wep_key(skb);
+	ath10k_tx_h_add_p2p_noa_ie(ar, skb);
+	ath10k_tx_h_seq_no(skb);
+
+	memset(ATH10K_SKB_CB(skb), 0, sizeof(*ATH10K_SKB_CB(skb)));
+	ATH10K_SKB_CB(skb)->htt.vdev_id = vdev_id;
+	ATH10K_SKB_CB(skb)->htt.tid = tid;
+
+	if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
+		spin_lock_bh(&ar->data_lock);
+		ATH10K_SKB_CB(skb)->htt.is_offchan = true;
+		ATH10K_SKB_CB(skb)->htt.vdev_id = ar->scan.vdev_id;
+		spin_unlock_bh(&ar->data_lock);
+
+		ath10k_dbg(ATH10K_DBG_MAC, "queued offchannel skb %p\n", skb);
+
+		skb_queue_tail(&ar->offchan_tx_queue, skb);
+		ieee80211_queue_work(hw, &ar->offchan_tx_work);
+		return;
+	}
+
+	ath10k_tx_htt(ar, skb);
+}
+
+/*
+ * Initialize various parameters with default vaules.
+ */
+static int ath10k_start(struct ieee80211_hw *hw)
+{
+	struct ath10k *ar = hw->priv;
+	int ret;
+
+	ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1);
+	if (ret)
+		ath10k_warn("could not enable WMI_PDEV_PARAM_PMF_QOS (%d)\n",
+			    ret);
+
+	ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_DYNAMIC_BW, 0);
+	if (ret)
+		ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n",
+			    ret);
+
+	return 0;
+}
+
+static void ath10k_stop(struct ieee80211_hw *hw)
+{
+	struct ath10k *ar = hw->priv;
+
+	/* avoid leaks in case FW never confirms scan for offchannel */
+	cancel_work_sync(&ar->offchan_tx_work);
+	ath10k_offchan_tx_purge(ar);
+}
+
+static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
+{
+	struct ath10k_generic_iter ar_iter;
+	struct ath10k *ar = hw->priv;
+	struct ieee80211_conf *conf = &hw->conf;
+	int ret = 0;
+	u32 flags;
+
+	mutex_lock(&ar->conf_mutex);
+
+	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+		ath10k_dbg(ATH10K_DBG_MAC, "Config channel %d mhz\n",
+			   conf->chandef.chan->center_freq);
+		spin_lock_bh(&ar->data_lock);
+		ar->rx_channel = conf->chandef.chan;
+		spin_unlock_bh(&ar->data_lock);
+	}
+
+	if (changed & IEEE80211_CONF_CHANGE_PS) {
+		memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
+		ar_iter.ar = ar;
+		flags = IEEE80211_IFACE_ITER_RESUME_ALL;
+
+		ieee80211_iterate_active_interfaces_atomic(hw,
+							   flags,
+							   ath10k_ps_iter,
+							   &ar_iter);
+
+		ret = ar_iter.ret;
+	}
+
+	if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
+		if (conf->flags & IEEE80211_CONF_MONITOR)
+			ret = ath10k_monitor_create(ar);
+		else
+			ret = ath10k_monitor_destroy(ar);
+	}
+
+	mutex_unlock(&ar->conf_mutex);
+	return ret;
+}
+
+/*
+ * TODO:
+ * Figure out how to handle WMI_VDEV_SUBTYPE_P2P_DEVICE,
+ * because we will send mgmt frames without CCK. This requirement
+ * for P2P_FIND/GO_NEG should be handled by checking CCK flag
+ * in the TX packet.
+ */
+static int ath10k_add_interface(struct ieee80211_hw *hw,
+				struct ieee80211_vif *vif)
+{
+	struct ath10k *ar = hw->priv;
+	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+	enum wmi_sta_powersave_param param;
+	int ret = 0;
+	u32 value;
+	int bit;
+
+	mutex_lock(&ar->conf_mutex);
+
+	arvif->ar = ar;
+	arvif->vif = vif;
+
+	if ((vif->type == NL80211_IFTYPE_MONITOR) && ar->monitor_present) {
+		ath10k_warn("Only one monitor interface allowed\n");
+		ret = -EBUSY;
+		goto exit;
+	}
+
+	bit = ffs(ar->free_vdev_map);
+	if (bit == 0) {
+		ret = -EBUSY;
+		goto exit;
+	}
+
+	arvif->vdev_id = bit - 1;
+	arvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE;
+	ar->free_vdev_map &= ~(1 << arvif->vdev_id);
+
+	if (ar->p2p)
+		arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_DEVICE;
+
+	switch (vif->type) {
+	case NL80211_IFTYPE_UNSPECIFIED:
+	case NL80211_IFTYPE_STATION:
+		arvif->vdev_type = WMI_VDEV_TYPE_STA;
+		if (vif->p2p)
+			arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_CLIENT;
+		break;
+	case NL80211_IFTYPE_ADHOC:
+		arvif->vdev_type = WMI_VDEV_TYPE_IBSS;
+		break;
+	case NL80211_IFTYPE_AP:
+		arvif->vdev_type = WMI_VDEV_TYPE_AP;
+
+		if (vif->p2p)
+			arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_GO;
+		break;
+	case NL80211_IFTYPE_MONITOR:
+		arvif->vdev_type = WMI_VDEV_TYPE_MONITOR;
+		break;
+	default:
+		WARN_ON(1);
+		break;
+	}
+
+	ath10k_dbg(ATH10K_DBG_MAC, "Add interface: id %d type %d subtype %d\n",
+		   arvif->vdev_id, arvif->vdev_type, arvif->vdev_subtype);
+
+	ret = ath10k_wmi_vdev_create(ar, arvif->vdev_id, arvif->vdev_type,
+				     arvif->vdev_subtype, vif->addr);
+	if (ret) {
+		ath10k_warn("WMI vdev create failed: ret %d\n", ret);
+		goto exit;
+	}
+
+	ret = ath10k_wmi_vdev_set_param(ar, 0, WMI_VDEV_PARAM_DEF_KEYID,
+					arvif->def_wep_key_index);
+	if (ret)
+		ath10k_warn("Failed to set default keyid: %d\n", ret);
+
+	ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+					WMI_VDEV_PARAM_TX_ENCAP_TYPE,
+					ATH10K_HW_TXRX_NATIVE_WIFI);
+	if (ret)
+		ath10k_warn("Failed to set TX encap: %d\n", ret);
+
+	if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
+		ret = ath10k_peer_create(ar, arvif->vdev_id, vif->addr);
+		if (ret) {
+			ath10k_warn("Failed to create peer for AP: %d\n", ret);
+			goto exit;
+		}
+	}
+
+	if (arvif->vdev_type == WMI_VDEV_TYPE_STA) {
+		param = WMI_STA_PS_PARAM_RX_WAKE_POLICY;
+		value = WMI_STA_PS_RX_WAKE_POLICY_WAKE;
+		ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
+						  param, value);
+		if (ret)
+			ath10k_warn("Failed to set RX wake policy: %d\n", ret);
+
+		param = WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD;
+		value = WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS;
+		ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
+						  param, value);
+		if (ret)
+			ath10k_warn("Failed to set TX wake thresh: %d\n", ret);
+
+		param = WMI_STA_PS_PARAM_PSPOLL_COUNT;
+		value = WMI_STA_PS_PSPOLL_COUNT_NO_MAX;
+		ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
+						  param, value);
+		if (ret)
+			ath10k_warn("Failed to set PSPOLL count: %d\n", ret);
+	}
+
+	if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
+		ar->monitor_present = true;
+
+exit:
+	mutex_unlock(&ar->conf_mutex);
+	return ret;
+}
+
+static void ath10k_remove_interface(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif)
+{
+	struct ath10k *ar = hw->priv;
+	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+	int ret;
+
+	mutex_lock(&ar->conf_mutex);
+
+	ath10k_dbg(ATH10K_DBG_MAC, "Remove interface: id %d\n", arvif->vdev_id);
+
+	ar->free_vdev_map |= 1 << (arvif->vdev_id);
+
+	if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
+		ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, vif->addr);
+		if (ret)
+			ath10k_warn("Failed to remove peer for AP: %d\n", ret);
+
+		kfree(arvif->u.ap.noa_data);
+	}
+
+	ret = ath10k_wmi_vdev_delete(ar, arvif->vdev_id);
+	if (ret)
+		ath10k_warn("WMI vdev delete failed: %d\n", ret);
+
+	if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
+		ar->monitor_present = false;
+
+	ath10k_peer_cleanup(ar, arvif->vdev_id);
+
+	mutex_unlock(&ar->conf_mutex);
+}
+
+/*
+ * FIXME: Has to be verified.
+ */
+#define SUPPORTED_FILTERS			\
+	(FIF_PROMISC_IN_BSS |			\
+	FIF_ALLMULTI |				\
+	FIF_CONTROL |				\
+	FIF_PSPOLL |				\
+	FIF_OTHER_BSS |				\
+	FIF_BCN_PRBRESP_PROMISC |		\
+	FIF_PROBE_REQ |				\
+	FIF_FCSFAIL)
+
+static void ath10k_configure_filter(struct ieee80211_hw *hw,
+				    unsigned int changed_flags,
+				    unsigned int *total_flags,
+				    u64 multicast)
+{
+	struct ath10k *ar = hw->priv;
+	int ret;
+
+	mutex_lock(&ar->conf_mutex);
+
+	changed_flags &= SUPPORTED_FILTERS;
+	*total_flags &= SUPPORTED_FILTERS;
+	ar->filter_flags = *total_flags;
+
+	if ((ar->filter_flags & FIF_PROMISC_IN_BSS) &&
+	    !ar->monitor_enabled) {
+		ret = ath10k_monitor_start(ar, ar->monitor_vdev_id);
+		if (ret)
+			ath10k_warn("Unable to start monitor mode\n");
+		else
+			ath10k_dbg(ATH10K_DBG_MAC, "Monitor mode started\n");
+	} else if (!(ar->filter_flags & FIF_PROMISC_IN_BSS) &&
+		   ar->monitor_enabled) {
+		ret = ath10k_monitor_stop(ar);
+		if (ret)
+			ath10k_warn("Unable to stop monitor mode\n");
+		else
+			ath10k_dbg(ATH10K_DBG_MAC, "Monitor mode stopped\n");
+	}
+
+	mutex_unlock(&ar->conf_mutex);
+}
+
+static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif,
+				    struct ieee80211_bss_conf *info,
+				    u32 changed)
+{
+	struct ath10k *ar = hw->priv;
+	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+	int ret = 0;
+
+	mutex_lock(&ar->conf_mutex);
+
+	if (changed & BSS_CHANGED_IBSS)
+		ath10k_control_ibss(arvif, info, vif->addr);
+
+	if (changed & BSS_CHANGED_BEACON_INT) {
+		arvif->beacon_interval = info->beacon_int;
+		ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+						WMI_VDEV_PARAM_BEACON_INTERVAL,
+						arvif->beacon_interval);
+		if (ret)
+			ath10k_warn("Failed to set beacon interval for VDEV: %d\n",
+				    arvif->vdev_id);
+		else
+			ath10k_dbg(ATH10K_DBG_MAC,
+				   "Beacon interval: %d set for VDEV: %d\n",
+				   arvif->beacon_interval, arvif->vdev_id);
+	}
+
+	if (changed & BSS_CHANGED_BEACON) {
+		ret = ath10k_wmi_pdev_set_param(ar,
+						WMI_PDEV_PARAM_BEACON_TX_MODE,
+						WMI_BEACON_STAGGERED_MODE);
+		if (ret)
+			ath10k_warn("Failed to set beacon mode for VDEV: %d\n",
+				    arvif->vdev_id);
+		else
+			ath10k_dbg(ATH10K_DBG_MAC,
+				   "Set staggered beacon mode for VDEV: %d\n",
+				   arvif->vdev_id);
+	}
+
+	if (changed & BSS_CHANGED_DTIM_PERIOD) {
+		arvif->dtim_period = info->dtim_period;
+
+		ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+						WMI_VDEV_PARAM_DTIM_PERIOD,
+						arvif->dtim_period);
+		if (ret)
+			ath10k_warn("Failed to set dtim period for VDEV: %d\n",
+				    arvif->vdev_id);
+		else
+			ath10k_dbg(ATH10K_DBG_MAC,
+				   "Set dtim period: %d for VDEV: %d\n",
+				   arvif->dtim_period, arvif->vdev_id);
+	}
+
+	if (changed & BSS_CHANGED_SSID &&
+	    vif->type == NL80211_IFTYPE_AP) {
+		arvif->u.ap.ssid_len = info->ssid_len;
+		if (info->ssid_len)
+			memcpy(arvif->u.ap.ssid, info->ssid, info->ssid_len);
+		arvif->u.ap.hidden_ssid = info->hidden_ssid;
+	}
+
+	if (changed & BSS_CHANGED_BSSID) {
+		if (!is_zero_ether_addr(info->bssid)) {
+			ret = ath10k_peer_create(ar, arvif->vdev_id,
+						 info->bssid);
+			if (ret)
+				ath10k_warn("Failed to add peer: %pM for VDEV: %d\n",
+					    info->bssid, arvif->vdev_id);
+			else
+				ath10k_dbg(ATH10K_DBG_MAC,
+					   "Added peer: %pM for VDEV: %d\n",
+					   info->bssid, arvif->vdev_id);
+
+
+			if (vif->type == NL80211_IFTYPE_STATION) {
+				/*
+				 * this is never erased as we it for crypto key
+				 * clearing; this is FW requirement
+				 */
+				memcpy(arvif->u.sta.bssid, info->bssid,
+				       ETH_ALEN);
+
+				ret = ath10k_vdev_start(arvif);
+				if (!ret)
+					ath10k_dbg(ATH10K_DBG_MAC,
+						   "VDEV: %d started with BSSID: %pM\n",
+						   arvif->vdev_id, info->bssid);
+			}
+
+			/*
+			 * Mac80211 does not keep IBSS bssid when leaving IBSS,
+			 * so driver need to store it. It is needed when leaving
+			 * IBSS in order to remove BSSID peer.
+			 */
+			if (vif->type == NL80211_IFTYPE_ADHOC)
+				memcpy(arvif->u.ibss.bssid, info->bssid,
+				       ETH_ALEN);
+		}
+	}
+
+	if (changed & BSS_CHANGED_BEACON_ENABLED)
+		ath10k_control_beaconing(arvif, info);
+
+	if (changed & BSS_CHANGED_ERP_CTS_PROT) {
+		u32 cts_prot;
+		if (info->use_cts_prot)
+			cts_prot = 1;
+		else
+			cts_prot = 0;
+
+		ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+						WMI_VDEV_PARAM_ENABLE_RTSCTS,
+						cts_prot);
+		if (ret)
+			ath10k_warn("Failed to set CTS prot for VDEV: %d\n",
+				    arvif->vdev_id);
+		else
+			ath10k_dbg(ATH10K_DBG_MAC,
+				   "Set CTS prot: %d for VDEV: %d\n",
+				   cts_prot, arvif->vdev_id);
+	}
+
+	if (changed & BSS_CHANGED_ERP_SLOT) {
+		u32 slottime;
+		if (info->use_short_slot)
+			slottime = WMI_VDEV_SLOT_TIME_SHORT; /* 9us */
+
+		else
+			slottime = WMI_VDEV_SLOT_TIME_LONG; /* 20us */
+
+		ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+						WMI_VDEV_PARAM_SLOT_TIME,
+						slottime);
+		if (ret)
+			ath10k_warn("Failed to set erp slot for VDEV: %d\n",
+				    arvif->vdev_id);
+		else
+			ath10k_dbg(ATH10K_DBG_MAC,
+				   "Set slottime: %d for VDEV: %d\n",
+				   slottime, arvif->vdev_id);
+	}
+
+	if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+		u32 preamble;
+		if (info->use_short_preamble)
+			preamble = WMI_VDEV_PREAMBLE_SHORT;
+		else
+			preamble = WMI_VDEV_PREAMBLE_LONG;
+
+		ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+						WMI_VDEV_PARAM_PREAMBLE,
+						preamble);
+		if (ret)
+			ath10k_warn("Failed to set preamble for VDEV: %d\n",
+				    arvif->vdev_id);
+		else
+			ath10k_dbg(ATH10K_DBG_MAC,
+				   "Set preamble: %d for VDEV: %d\n",
+				   preamble, arvif->vdev_id);
+	}
+
+	if (changed & BSS_CHANGED_ASSOC) {
+		if (info->assoc)
+			ath10k_bss_assoc(hw, vif, info);
+	}
+
+	mutex_unlock(&ar->conf_mutex);
+}
+
+static int ath10k_hw_scan(struct ieee80211_hw *hw,
+			  struct ieee80211_vif *vif,
+			  struct cfg80211_scan_request *req)
+{
+	struct ath10k *ar = hw->priv;
+	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+	struct wmi_start_scan_arg arg;
+	int ret = 0;
+	int i;
+
+	mutex_lock(&ar->conf_mutex);
+
+	spin_lock_bh(&ar->data_lock);
+	if (ar->scan.in_progress) {
+		spin_unlock_bh(&ar->data_lock);
+		ret = -EBUSY;
+		goto exit;
+	}
+
+	INIT_COMPLETION(ar->scan.started);
+	INIT_COMPLETION(ar->scan.completed);
+	ar->scan.in_progress = true;
+	ar->scan.aborting = false;
+	ar->scan.is_roc = false;
+	ar->scan.vdev_id = arvif->vdev_id;
+	spin_unlock_bh(&ar->data_lock);
+
+	memset(&arg, 0, sizeof(arg));
+	ath10k_wmi_start_scan_init(ar, &arg);
+	arg.vdev_id = arvif->vdev_id;
+	arg.scan_id = ATH10K_SCAN_ID;
+
+	if (!req->no_cck)
+		arg.scan_ctrl_flags |= WMI_SCAN_ADD_CCK_RATES;
+
+	if (req->ie_len) {
+		arg.ie_len = req->ie_len;
+		memcpy(arg.ie, req->ie, arg.ie_len);
+	}
+
+	if (req->n_ssids) {
+		arg.n_ssids = req->n_ssids;
+		for (i = 0; i < arg.n_ssids; i++) {
+			arg.ssids[i].len  = req->ssids[i].ssid_len;
+			arg.ssids[i].ssid = req->ssids[i].ssid;
+		}
+	}
+
+	if (req->n_channels) {
+		arg.n_channels = req->n_channels;
+		for (i = 0; i < arg.n_channels; i++)
+			arg.channels[i] = req->channels[i]->center_freq;
+	}
+
+	ret = ath10k_start_scan(ar, &arg);
+	if (ret) {
+		ath10k_warn("could not start hw scan (%d)\n", ret);
+		spin_lock_bh(&ar->data_lock);
+		ar->scan.in_progress = false;
+		spin_unlock_bh(&ar->data_lock);
+	}
+
+exit:
+	mutex_unlock(&ar->conf_mutex);
+	return ret;
+}
+
+static void ath10k_cancel_hw_scan(struct ieee80211_hw *hw,
+				  struct ieee80211_vif *vif)
+{
+	struct ath10k *ar = hw->priv;
+	int ret;
+
+	mutex_lock(&ar->conf_mutex);
+	ret = ath10k_abort_scan(ar);
+	if (ret) {
+		ath10k_warn("couldn't abort scan (%d). forcefully sending scan completion to mac80211\n",
+			    ret);
+		ieee80211_scan_completed(hw, 1 /* aborted */);
+	}
+	mutex_unlock(&ar->conf_mutex);
+}
+
+static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+			  struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+			  struct ieee80211_key_conf *key)
+{
+	struct ath10k *ar = hw->priv;
+	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+	struct ath10k_peer *peer;
+	const u8 *peer_addr;
+	bool is_wep = key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+		      key->cipher == WLAN_CIPHER_SUITE_WEP104;
+	int ret = 0;
+
+	if (key->keyidx > WMI_MAX_KEY_INDEX)
+		return -ENOSPC;
+
+	mutex_lock(&ar->conf_mutex);
+
+	if (sta)
+		peer_addr = sta->addr;
+	else if (arvif->vdev_type == WMI_VDEV_TYPE_STA)
+		peer_addr = vif->bss_conf.bssid;
+	else
+		peer_addr = vif->addr;
+
+	key->hw_key_idx = key->keyidx;
+
+	/* the peer should not disappear in mid-way (unless FW goes awry) since
+	 * we already hold conf_mutex. we just make sure its there now. */
+	spin_lock_bh(&ar->data_lock);
+	peer = ath10k_peer_find(ar, arvif->vdev_id, peer_addr);
+	spin_unlock_bh(&ar->data_lock);
+
+	if (!peer) {
+		if (cmd == SET_KEY) {
+			ath10k_warn("cannot install key for non-existent peer %pM\n",
+				    peer_addr);
+			ret = -EOPNOTSUPP;
+			goto exit;
+		} else {
+			/* if the peer doesn't exist there is no key to disable
+			 * anymore */
+			goto exit;
+		}
+	}
+
+	if (is_wep) {
+		if (cmd == SET_KEY)
+			arvif->wep_keys[key->keyidx] = key;
+		else
+			arvif->wep_keys[key->keyidx] = NULL;
+
+		if (cmd == DISABLE_KEY)
+			ath10k_clear_vdev_key(arvif, key);
+	}
+
+	ret = ath10k_install_key(arvif, key, cmd, peer_addr);
+	if (ret) {
+		ath10k_warn("ath10k_install_key failed (%d)\n", ret);
+		goto exit;
+	}
+
+	spin_lock_bh(&ar->data_lock);
+	peer = ath10k_peer_find(ar, arvif->vdev_id, peer_addr);
+	if (peer && cmd == SET_KEY)
+		peer->keys[key->keyidx] = key;
+	else if (peer && cmd == DISABLE_KEY)
+		peer->keys[key->keyidx] = NULL;
+	else if (peer == NULL)
+		/* impossible unless FW goes crazy */
+		ath10k_warn("peer %pM disappeared!\n", peer_addr);
+	spin_unlock_bh(&ar->data_lock);
+
+exit:
+	mutex_unlock(&ar->conf_mutex);
+	return ret;
+}
+
+static int ath10k_sta_state(struct ieee80211_hw *hw,
+			    struct ieee80211_vif *vif,
+			    struct ieee80211_sta *sta,
+			    enum ieee80211_sta_state old_state,
+			    enum ieee80211_sta_state new_state)
+{
+	struct ath10k *ar = hw->priv;
+	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+	int ret = 0;
+
+	mutex_lock(&ar->conf_mutex);
+
+	if (old_state == IEEE80211_STA_NOTEXIST &&
+	    new_state == IEEE80211_STA_NONE &&
+	    vif->type != NL80211_IFTYPE_STATION) {
+		/*
+		 * New station addition.
+		 */
+		ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr);
+		if (ret)
+			ath10k_warn("Failed to add peer: %pM for VDEV: %d\n",
+				    sta->addr, arvif->vdev_id);
+		else
+			ath10k_dbg(ATH10K_DBG_MAC,
+				   "Added peer: %pM for VDEV: %d\n",
+				   sta->addr, arvif->vdev_id);
+	} else if ((old_state == IEEE80211_STA_NONE &&
+		    new_state == IEEE80211_STA_NOTEXIST)) {
+		/*
+		 * Existing station deletion.
+		 */
+		ret = ath10k_peer_delete(ar, arvif->vdev_id, sta->addr);
+		if (ret)
+			ath10k_warn("Failed to delete peer: %pM for VDEV: %d\n",
+				    sta->addr, arvif->vdev_id);
+		else
+			ath10k_dbg(ATH10K_DBG_MAC,
+				   "Removed peer: %pM for VDEV: %d\n",
+				   sta->addr, arvif->vdev_id);
+
+		if (vif->type == NL80211_IFTYPE_STATION)
+			ath10k_bss_disassoc(hw, vif);
+	} else if (old_state == IEEE80211_STA_AUTH &&
+		   new_state == IEEE80211_STA_ASSOC &&
+		   (vif->type == NL80211_IFTYPE_AP ||
+		    vif->type == NL80211_IFTYPE_ADHOC)) {
+		/*
+		 * New association.
+		 */
+		ret = ath10k_station_assoc(ar, arvif, sta);
+		if (ret)
+			ath10k_warn("Failed to associate station: %pM\n",
+				    sta->addr);
+		else
+			ath10k_dbg(ATH10K_DBG_MAC,
+				   "Station %pM moved to assoc state\n",
+				   sta->addr);
+	} else if (old_state == IEEE80211_STA_ASSOC &&
+		   new_state == IEEE80211_STA_AUTH &&
+		   (vif->type == NL80211_IFTYPE_AP ||
+		    vif->type == NL80211_IFTYPE_ADHOC)) {
+		/*
+		 * Disassociation.
+		 */
+		ret = ath10k_station_disassoc(ar, arvif, sta);
+		if (ret)
+			ath10k_warn("Failed to disassociate station: %pM\n",
+				    sta->addr);
+		else
+			ath10k_dbg(ATH10K_DBG_MAC,
+				   "Station %pM moved to disassociated state\n",
+				   sta->addr);
+	}
+
+	mutex_unlock(&ar->conf_mutex);
+	return ret;
+}
+
+static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif,
+				 u16 ac, bool enable)
+{
+	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+	u32 value = 0;
+	int ret = 0;
+
+	if (arvif->vdev_type != WMI_VDEV_TYPE_STA)
+		return 0;
+
+	switch (ac) {
+	case IEEE80211_AC_VO:
+		value = WMI_STA_PS_UAPSD_AC3_DELIVERY_EN |
+			WMI_STA_PS_UAPSD_AC3_TRIGGER_EN;
+		break;
+	case IEEE80211_AC_VI:
+		value = WMI_STA_PS_UAPSD_AC2_DELIVERY_EN |
+			WMI_STA_PS_UAPSD_AC2_TRIGGER_EN;
+		break;
+	case IEEE80211_AC_BE:
+		value = WMI_STA_PS_UAPSD_AC1_DELIVERY_EN |
+			WMI_STA_PS_UAPSD_AC1_TRIGGER_EN;
+		break;
+	case IEEE80211_AC_BK:
+		value = WMI_STA_PS_UAPSD_AC0_DELIVERY_EN |
+			WMI_STA_PS_UAPSD_AC0_TRIGGER_EN;
+		break;
+	}
+
+	if (enable)
+		arvif->u.sta.uapsd |= value;
+	else
+		arvif->u.sta.uapsd &= ~value;
+
+	ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
+					  WMI_STA_PS_PARAM_UAPSD,
+					  arvif->u.sta.uapsd);
+	if (ret) {
+		ath10k_warn("could not set uapsd params %d\n", ret);
+		goto exit;
+	}
+
+	if (arvif->u.sta.uapsd)
+		value = WMI_STA_PS_RX_WAKE_POLICY_POLL_UAPSD;
+	else
+		value = WMI_STA_PS_RX_WAKE_POLICY_WAKE;
+
+	ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
+					  WMI_STA_PS_PARAM_RX_WAKE_POLICY,
+					  value);
+	if (ret)
+		ath10k_warn("could not set rx wake param %d\n", ret);
+
+exit:
+	return ret;
+}
+
+static int ath10k_conf_tx(struct ieee80211_hw *hw,
+			  struct ieee80211_vif *vif, u16 ac,
+			  const struct ieee80211_tx_queue_params *params)
+{
+	struct ath10k *ar = hw->priv;
+	struct wmi_wmm_params_arg *p = NULL;
+	int ret;
+
+	mutex_lock(&ar->conf_mutex);
+
+	switch (ac) {
+	case IEEE80211_AC_VO:
+		p = &ar->wmm_params.ac_vo;
+		break;
+	case IEEE80211_AC_VI:
+		p = &ar->wmm_params.ac_vi;
+		break;
+	case IEEE80211_AC_BE:
+		p = &ar->wmm_params.ac_be;
+		break;
+	case IEEE80211_AC_BK:
+		p = &ar->wmm_params.ac_bk;
+		break;
+	}
+
+	if (WARN_ON(!p)) {
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	p->cwmin = params->cw_min;
+	p->cwmax = params->cw_max;
+	p->aifs = params->aifs;
+
+	/*
+	 * The channel time duration programmed in the HW is in absolute
+	 * microseconds, while mac80211 gives the txop in units of
+	 * 32 microseconds.
+	 */
+	p->txop = params->txop * 32;
+
+	/* FIXME: FW accepts wmm params per hw, not per vif */
+	ret = ath10k_wmi_pdev_set_wmm_params(ar, &ar->wmm_params);
+	if (ret) {
+		ath10k_warn("could not set wmm params %d\n", ret);
+		goto exit;
+	}
+
+	ret = ath10k_conf_tx_uapsd(ar, vif, ac, params->uapsd);
+	if (ret)
+		ath10k_warn("could not set sta uapsd %d\n", ret);
+
+exit:
+	mutex_unlock(&ar->conf_mutex);
+	return ret;
+}
+
+#define ATH10K_ROC_TIMEOUT_HZ (2*HZ)
+
+static int ath10k_remain_on_channel(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif,
+				    struct ieee80211_channel *chan,
+				    int duration,
+				    enum ieee80211_roc_type type)
+{
+	struct ath10k *ar = hw->priv;
+	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+	struct wmi_start_scan_arg arg;
+	int ret;
+
+	mutex_lock(&ar->conf_mutex);
+
+	spin_lock_bh(&ar->data_lock);
+	if (ar->scan.in_progress) {
+		spin_unlock_bh(&ar->data_lock);
+		ret = -EBUSY;
+		goto exit;
+	}
+
+	INIT_COMPLETION(ar->scan.started);
+	INIT_COMPLETION(ar->scan.completed);
+	INIT_COMPLETION(ar->scan.on_channel);
+	ar->scan.in_progress = true;
+	ar->scan.aborting = false;
+	ar->scan.is_roc = true;
+	ar->scan.vdev_id = arvif->vdev_id;
+	ar->scan.roc_freq = chan->center_freq;
+	spin_unlock_bh(&ar->data_lock);
+
+	memset(&arg, 0, sizeof(arg));
+	ath10k_wmi_start_scan_init(ar, &arg);
+	arg.vdev_id = arvif->vdev_id;
+	arg.scan_id = ATH10K_SCAN_ID;
+	arg.n_channels = 1;
+	arg.channels[0] = chan->center_freq;
+	arg.dwell_time_active = duration;
+	arg.dwell_time_passive = duration;
+	arg.max_scan_time = 2 * duration;
+	arg.scan_ctrl_flags |= WMI_SCAN_FLAG_PASSIVE;
+	arg.scan_ctrl_flags |= WMI_SCAN_FILTER_PROBE_REQ;
+
+	ret = ath10k_start_scan(ar, &arg);
+	if (ret) {
+		ath10k_warn("could not start roc scan (%d)\n", ret);
+		spin_lock_bh(&ar->data_lock);
+		ar->scan.in_progress = false;
+		spin_unlock_bh(&ar->data_lock);
+		goto exit;
+	}
+
+	ret = wait_for_completion_timeout(&ar->scan.on_channel, 3*HZ);
+	if (ret == 0) {
+		ath10k_warn("could not switch to channel for roc scan\n");
+		ath10k_abort_scan(ar);
+		ret = -ETIMEDOUT;
+		goto exit;
+	}
+
+	ret = 0;
+exit:
+	mutex_unlock(&ar->conf_mutex);
+	return ret;
+}
+
+static int ath10k_cancel_remain_on_channel(struct ieee80211_hw *hw)
+{
+	struct ath10k *ar = hw->priv;
+
+	mutex_lock(&ar->conf_mutex);
+	ath10k_abort_scan(ar);
+	mutex_unlock(&ar->conf_mutex);
+
+	return 0;
+}
+
+/*
+ * Both RTS and Fragmentation threshold are interface-specific
+ * in ath10k, but device-specific in mac80211.
+ */
+static void ath10k_set_rts_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
+{
+	struct ath10k_generic_iter *ar_iter = data;
+	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+	u32 rts = ar_iter->ar->hw->wiphy->rts_threshold;
+
+	rts = min_t(u32, rts, ATH10K_RTS_MAX);
+
+	ar_iter->ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id,
+						 WMI_VDEV_PARAM_RTS_THRESHOLD,
+						 rts);
+	if (ar_iter->ret)
+		ath10k_warn("Failed to set RTS threshold for VDEV: %d\n",
+			    arvif->vdev_id);
+	else
+		ath10k_dbg(ATH10K_DBG_MAC,
+			   "Set RTS threshold: %d for VDEV: %d\n",
+			   rts, arvif->vdev_id);
+}
+
+static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+	struct ath10k_generic_iter ar_iter;
+	struct ath10k *ar = hw->priv;
+
+	memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
+	ar_iter.ar = ar;
+
+	mutex_lock(&ar->conf_mutex);
+	ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+					    ath10k_set_rts_iter, &ar_iter);
+	mutex_unlock(&ar->conf_mutex);
+
+	return ar_iter.ret;
+}
+
+static void ath10k_set_frag_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
+{
+	struct ath10k_generic_iter *ar_iter = data;
+	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+	u32 frag = ar_iter->ar->hw->wiphy->frag_threshold;
+	int ret;
+
+	frag = clamp_t(u32, frag,
+		       ATH10K_FRAGMT_THRESHOLD_MIN,
+		       ATH10K_FRAGMT_THRESHOLD_MAX);
+
+	ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id,
+					WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
+					frag);
+
+	ar_iter->ret = ret;
+	if (ar_iter->ret)
+		ath10k_warn("Failed to set frag threshold for VDEV: %d\n",
+			    arvif->vdev_id);
+	else
+		ath10k_dbg(ATH10K_DBG_MAC,
+			   "Set frag threshold: %d for VDEV: %d\n",
+			   frag, arvif->vdev_id);
+}
+
+static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
+{
+	struct ath10k_generic_iter ar_iter;
+	struct ath10k *ar = hw->priv;
+
+	memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
+	ar_iter.ar = ar;
+
+	mutex_lock(&ar->conf_mutex);
+	ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+					    ath10k_set_frag_iter, &ar_iter);
+	mutex_unlock(&ar->conf_mutex);
+
+	return ar_iter.ret;
+}
+
+static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+{
+	struct ath10k *ar = hw->priv;
+	int ret;
+
+	/* mac80211 doesn't care if we really xmit queued frames or not
+	 * we'll collect those frames either way if we stop/delete vdevs */
+	if (drop)
+		return;
+
+	ret = wait_event_timeout(ar->htt->empty_tx_wq, ({
+			bool empty;
+			spin_lock_bh(&ar->htt->tx_lock);
+			empty = bitmap_empty(ar->htt->used_msdu_ids,
+					     ar->htt->max_num_pending_tx);
+			spin_unlock_bh(&ar->htt->tx_lock);
+			(empty);
+		}), ATH10K_FLUSH_TIMEOUT_HZ);
+	if (ret <= 0)
+		ath10k_warn("tx not flushed\n");
+}
+
+/* TODO: Implement this function properly
+ * For now it is needed to reply to Probe Requests in IBSS mode.
+ * Propably we need this information from FW.
+ */
+static int ath10k_tx_last_beacon(struct ieee80211_hw *hw)
+{
+	return 1;
+}
+
+static const struct ieee80211_ops ath10k_ops = {
+	.tx				= ath10k_tx,
+	.start				= ath10k_start,
+	.stop				= ath10k_stop,
+	.config				= ath10k_config,
+	.add_interface			= ath10k_add_interface,
+	.remove_interface		= ath10k_remove_interface,
+	.configure_filter		= ath10k_configure_filter,
+	.bss_info_changed		= ath10k_bss_info_changed,
+	.hw_scan			= ath10k_hw_scan,
+	.cancel_hw_scan			= ath10k_cancel_hw_scan,
+	.set_key			= ath10k_set_key,
+	.sta_state			= ath10k_sta_state,
+	.conf_tx			= ath10k_conf_tx,
+	.remain_on_channel		= ath10k_remain_on_channel,
+	.cancel_remain_on_channel	= ath10k_cancel_remain_on_channel,
+	.set_rts_threshold		= ath10k_set_rts_threshold,
+	.set_frag_threshold		= ath10k_set_frag_threshold,
+	.flush				= ath10k_flush,
+	.tx_last_beacon			= ath10k_tx_last_beacon,
+};
+
+#define RATETAB_ENT(_rate, _rateid, _flags) { \
+	.bitrate		= (_rate), \
+	.flags			= (_flags), \
+	.hw_value		= (_rateid), \
+}
+
+#define CHAN2G(_channel, _freq, _flags) { \
+	.band			= IEEE80211_BAND_2GHZ, \
+	.hw_value		= (_channel), \
+	.center_freq		= (_freq), \
+	.flags			= (_flags), \
+	.max_antenna_gain	= 0, \
+	.max_power		= 30, \
+}
+
+#define CHAN5G(_channel, _freq, _flags) { \
+	.band			= IEEE80211_BAND_5GHZ, \
+	.hw_value		= (_channel), \
+	.center_freq		= (_freq), \
+	.flags			= (_flags), \
+	.max_antenna_gain	= 0, \
+	.max_power		= 30, \
+}
+
+static const struct ieee80211_channel ath10k_2ghz_channels[] = {
+	CHAN2G(1, 2412, 0),
+	CHAN2G(2, 2417, 0),
+	CHAN2G(3, 2422, 0),
+	CHAN2G(4, 2427, 0),
+	CHAN2G(5, 2432, 0),
+	CHAN2G(6, 2437, 0),
+	CHAN2G(7, 2442, 0),
+	CHAN2G(8, 2447, 0),
+	CHAN2G(9, 2452, 0),
+	CHAN2G(10, 2457, 0),
+	CHAN2G(11, 2462, 0),
+	CHAN2G(12, 2467, 0),
+	CHAN2G(13, 2472, 0),
+	CHAN2G(14, 2484, 0),
+};
+
+static const struct ieee80211_channel ath10k_5ghz_channels[] = {
+	CHAN5G(36, 5180, 14),
+	CHAN5G(40, 5200, 15),
+	CHAN5G(44, 5220, 16),
+	CHAN5G(48, 5240, 17),
+	CHAN5G(52, 5260, 18),
+	CHAN5G(56, 5280, 19),
+	CHAN5G(60, 5300, 20),
+	CHAN5G(64, 5320, 21),
+	CHAN5G(100, 5500, 22),
+	CHAN5G(104, 5520, 23),
+	CHAN5G(108, 5540, 24),
+	CHAN5G(112, 5560, 25),
+	CHAN5G(116, 5580, 26),
+	CHAN5G(120, 5600, 27),
+	CHAN5G(124, 5620, 28),
+	CHAN5G(128, 5640, 29),
+	CHAN5G(132, 5660, 30),
+	CHAN5G(136, 5680, 31),
+	CHAN5G(140, 5700, 32),
+	CHAN5G(149, 5745, 33),
+	CHAN5G(153, 5765, 34),
+	CHAN5G(157, 5785, 35),
+	CHAN5G(161, 5805, 36),
+	CHAN5G(165, 5825, 37),
+};
+
+static struct ieee80211_rate ath10k_rates[] = {
+	/* CCK */
+	RATETAB_ENT(10,  0x82, 0),
+	RATETAB_ENT(20,  0x84, 0),
+	RATETAB_ENT(55,  0x8b, 0),
+	RATETAB_ENT(110, 0x96, 0),
+	/* OFDM */
+	RATETAB_ENT(60,  0x0c, 0),
+	RATETAB_ENT(90,  0x12, 0),
+	RATETAB_ENT(120, 0x18, 0),
+	RATETAB_ENT(180, 0x24, 0),
+	RATETAB_ENT(240, 0x30, 0),
+	RATETAB_ENT(360, 0x48, 0),
+	RATETAB_ENT(480, 0x60, 0),
+	RATETAB_ENT(540, 0x6c, 0),
+};
+
+#define ath10k_a_rates (ath10k_rates + 4)
+#define ath10k_a_rates_size (ARRAY_SIZE(ath10k_rates) - 4)
+#define ath10k_g_rates (ath10k_rates + 0)
+#define ath10k_g_rates_size (ARRAY_SIZE(ath10k_rates))
+
+struct ath10k *ath10k_mac_create(void)
+{
+	struct ieee80211_hw *hw;
+	struct ath10k *ar;
+
+	hw = ieee80211_alloc_hw(sizeof(struct ath10k), &ath10k_ops);
+	if (!hw)
+		return NULL;
+
+	ar = hw->priv;
+	ar->hw = hw;
+
+	return ar;
+}
+
+void ath10k_mac_destroy(struct ath10k *ar)
+{
+	ieee80211_free_hw(ar->hw);
+}
+
+static const struct ieee80211_iface_limit ath10k_if_limits[] = {
+	{
+	.max	= 8,
+	.types	= BIT(NL80211_IFTYPE_STATION)
+		| BIT(NL80211_IFTYPE_P2P_CLIENT)
+		| BIT(NL80211_IFTYPE_P2P_GO)
+		| BIT(NL80211_IFTYPE_AP)
+	}
+};
+
+static const struct ieee80211_iface_combination ath10k_if_comb = {
+	.limits = ath10k_if_limits,
+	.n_limits = ARRAY_SIZE(ath10k_if_limits),
+	.max_interfaces = 8,
+	.num_different_channels = 1,
+	.beacon_int_infra_match = true,
+};
+
+static struct ieee80211_sta_vht_cap ath10k_create_vht_cap(struct ath10k *ar)
+{
+	struct ieee80211_sta_vht_cap vht_cap = {0};
+	u16 mcs_map;
+
+	vht_cap.vht_supported = 1;
+	vht_cap.cap = ar->vht_cap_info;
+
+	/* FIXME: check dynamically how many streams board supports */
+	mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 |
+		IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 |
+		IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 |
+		IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 |
+		IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 |
+		IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 |
+		IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 |
+		IEEE80211_VHT_MCS_NOT_SUPPORTED << 14;
+
+	vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(mcs_map);
+	vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(mcs_map);
+
+	return vht_cap;
+}
+
+static struct ieee80211_sta_ht_cap ath10k_get_ht_cap(struct ath10k *ar)
+{
+	int i;
+	struct ieee80211_sta_ht_cap ht_cap = {0};
+
+	if (!(ar->ht_cap_info & WMI_HT_CAP_ENABLED))
+		return ht_cap;
+
+	ht_cap.ht_supported = 1;
+	ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
+	ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_8;
+	ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+	ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
+	ht_cap.cap |= WLAN_HT_CAP_SM_PS_STATIC << IEEE80211_HT_CAP_SM_PS_SHIFT;
+
+	if (ar->ht_cap_info & WMI_HT_CAP_HT20_SGI)
+		ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
+
+	if (ar->ht_cap_info & WMI_HT_CAP_HT40_SGI)
+		ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
+
+	if (ar->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS) {
+		u32 smps;
+
+		smps   = WLAN_HT_CAP_SM_PS_DYNAMIC;
+		smps <<= IEEE80211_HT_CAP_SM_PS_SHIFT;
+
+		ht_cap.cap |= smps;
+	}
+
+	if (ar->ht_cap_info & WMI_HT_CAP_TX_STBC)
+		ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC;
+
+	if (ar->ht_cap_info & WMI_HT_CAP_RX_STBC) {
+		u32 stbc;
+
+		stbc   = ar->ht_cap_info;
+		stbc  &= WMI_HT_CAP_RX_STBC;
+		stbc >>= WMI_HT_CAP_RX_STBC_MASK_SHIFT;
+		stbc <<= IEEE80211_HT_CAP_RX_STBC_SHIFT;
+		stbc  &= IEEE80211_HT_CAP_RX_STBC;
+
+		ht_cap.cap |= stbc;
+	}
+
+	if (ar->ht_cap_info & WMI_HT_CAP_LDPC)
+		ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING;
+
+	if (ar->ht_cap_info & WMI_HT_CAP_L_SIG_TXOP_PROT)
+		ht_cap.cap |= IEEE80211_HT_CAP_LSIG_TXOP_PROT;
+
+	/* max AMSDU is implicitly taken from vht_cap_info */
+	if (ar->vht_cap_info & WMI_VHT_CAP_MAX_MPDU_LEN_MASK)
+		ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU;
+
+	for (i = 0; i < WMI_MAX_SPATIAL_STREAM; i++)
+		ht_cap.mcs.rx_mask[i] = 0xFF;
+
+	ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED;
+
+	return ht_cap;
+}
+
+
+static void ath10k_get_arvif_iter(void *data, u8 *mac,
+				  struct ieee80211_vif *vif)
+{
+	struct ath10k_vif_iter *arvif_iter = data;
+	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+
+	if (arvif->vdev_id == arvif_iter->vdev_id)
+		arvif_iter->arvif = arvif;
+}
+
+struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id)
+{
+	struct ath10k_vif_iter arvif_iter;
+	u32 flags;
+
+	memset(&arvif_iter, 0, sizeof(struct ath10k_vif_iter));
+	arvif_iter.vdev_id = vdev_id;
+
+	flags = IEEE80211_IFACE_ITER_RESUME_ALL;
+	ieee80211_iterate_active_interfaces_atomic(ar->hw,
+						   flags,
+						   ath10k_get_arvif_iter,
+						   &arvif_iter);
+	if (!arvif_iter.arvif) {
+		ath10k_warn("No VIF found for VDEV: %d\n", vdev_id);
+		return NULL;
+	}
+
+	return arvif_iter.arvif;
+}
+
+int ath10k_mac_register(struct ath10k *ar)
+{
+	struct ieee80211_supported_band *band;
+	struct ieee80211_sta_vht_cap vht_cap;
+	struct ieee80211_sta_ht_cap ht_cap;
+	void *channels;
+	int ret;
+
+	SET_IEEE80211_PERM_ADDR(ar->hw, ar->mac_addr);
+
+	SET_IEEE80211_DEV(ar->hw, ar->dev);
+
+	ht_cap = ath10k_get_ht_cap(ar);
+	vht_cap = ath10k_create_vht_cap(ar);
+
+	if (ar->phy_capability & WHAL_WLAN_11G_CAPABILITY) {
+		channels = kmemdup(ath10k_2ghz_channels,
+				   sizeof(ath10k_2ghz_channels),
+				   GFP_KERNEL);
+		if (!channels)
+			return -ENOMEM;
+
+		band = &ar->mac.sbands[IEEE80211_BAND_2GHZ];
+		band->n_channels = ARRAY_SIZE(ath10k_2ghz_channels);
+		band->channels = channels;
+		band->n_bitrates = ath10k_g_rates_size;
+		band->bitrates = ath10k_g_rates;
+		band->ht_cap = ht_cap;
+
+		/* vht is not supported in 2.4 GHz */
+
+		ar->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = band;
+	}
+
+	if (ar->phy_capability & WHAL_WLAN_11A_CAPABILITY) {
+		channels = kmemdup(ath10k_5ghz_channels,
+				   sizeof(ath10k_5ghz_channels),
+				   GFP_KERNEL);
+		if (!channels) {
+			if (ar->phy_capability & WHAL_WLAN_11G_CAPABILITY) {
+				band = &ar->mac.sbands[IEEE80211_BAND_2GHZ];
+				kfree(band->channels);
+			}
+			return -ENOMEM;
+		}
+
+		band = &ar->mac.sbands[IEEE80211_BAND_5GHZ];
+		band->n_channels = ARRAY_SIZE(ath10k_5ghz_channels);
+		band->channels = channels;
+		band->n_bitrates = ath10k_a_rates_size;
+		band->bitrates = ath10k_a_rates;
+		band->ht_cap = ht_cap;
+		band->vht_cap = vht_cap;
+		ar->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = band;
+	}
+
+	ar->hw->wiphy->interface_modes =
+		BIT(NL80211_IFTYPE_STATION) |
+		BIT(NL80211_IFTYPE_ADHOC) |
+		BIT(NL80211_IFTYPE_AP) |
+		BIT(NL80211_IFTYPE_P2P_CLIENT) |
+		BIT(NL80211_IFTYPE_P2P_GO);
+
+	ar->hw->flags = IEEE80211_HW_SIGNAL_DBM |
+			IEEE80211_HW_SUPPORTS_PS |
+			IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
+			IEEE80211_HW_SUPPORTS_UAPSD |
+			IEEE80211_HW_MFP_CAPABLE |
+			IEEE80211_HW_REPORTS_TX_ACK_STATUS |
+			IEEE80211_HW_HAS_RATE_CONTROL |
+			IEEE80211_HW_SUPPORTS_STATIC_SMPS |
+			IEEE80211_HW_WANT_MONITOR_VIF |
+			IEEE80211_HW_AP_LINK_PS;
+
+	if (ar->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS)
+		ar->hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS;
+
+	if (ar->ht_cap_info & WMI_HT_CAP_ENABLED) {
+		ar->hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;
+		ar->hw->flags |= IEEE80211_HW_TX_AMPDU_SETUP_IN_HW;
+	}
+
+	ar->hw->wiphy->max_scan_ssids = WLAN_SCAN_PARAMS_MAX_SSID;
+	ar->hw->wiphy->max_scan_ie_len = WLAN_SCAN_PARAMS_MAX_IE_LEN;
+
+	ar->hw->vif_data_size = sizeof(struct ath10k_vif);
+
+	ar->hw->channel_change_time = 5000;
+	ar->hw->max_listen_interval = ATH10K_MAX_HW_LISTEN_INTERVAL;
+
+	ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+	ar->hw->wiphy->max_remain_on_channel_duration = 5000;
+
+	ar->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
+	/*
+	 * on LL hardware queues are managed entirely by the FW
+	 * so we only advertise to mac we can do the queues thing
+	 */
+	ar->hw->queues = 4;
+
+	ar->hw->wiphy->iface_combinations = &ath10k_if_comb;
+	ar->hw->wiphy->n_iface_combinations = 1;
+
+	ret = ath_regd_init(&ar->ath_common.regulatory, ar->hw->wiphy,
+			    ath10k_reg_notifier);
+	if (ret) {
+		ath10k_err("Regulatory initialization failed\n");
+		return ret;
+	}
+
+	ret = ieee80211_register_hw(ar->hw);
+	if (ret) {
+		ath10k_err("ieee80211 registration failed: %d\n", ret);
+		return ret;
+	}
+
+	if (!ath_is_world_regd(&ar->ath_common.regulatory)) {
+		ret = regulatory_hint(ar->hw->wiphy,
+				      ar->ath_common.regulatory.alpha2);
+		if (ret)
+			goto exit;
+	}
+
+	return 0;
+exit:
+	ieee80211_unregister_hw(ar->hw);
+	return ret;
+}
+
+void ath10k_mac_unregister(struct ath10k *ar)
+{
+	ieee80211_unregister_hw(ar->hw);
+
+	kfree(ar->mac.sbands[IEEE80211_BAND_2GHZ].channels);
+	kfree(ar->mac.sbands[IEEE80211_BAND_5GHZ].channels);
+
+	SET_IEEE80211_DEV(ar->hw, NULL);
+}


^ permalink raw reply related

* [PATCH v2 18/29] ath10k: add mac.h
From: Kalle Valo @ 2013-06-01 11:26 UTC (permalink / raw)
  To: linux-wireless; +Cc: ath10k-devel
In-Reply-To: <20130601111820.32443.46794.stgit@localhost6.localdomain6>

Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
---
 drivers/net/wireless/ath/ath10k/mac.h |   61 +++++++++++++++++++++++++++++++++
 1 file changed, 61 insertions(+)
 create mode 100644 drivers/net/wireless/ath/ath10k/mac.h

diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h
new file mode 100644
index 0000000..27fc92e
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/mac.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * 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 _MAC_H_
+#define _MAC_H_
+
+#include <net/mac80211.h>
+#include "core.h"
+
+struct ath10k_generic_iter {
+	struct ath10k *ar;
+	int ret;
+};
+
+struct ath10k *ath10k_mac_create(void);
+void ath10k_mac_destroy(struct ath10k *ar);
+int ath10k_mac_register(struct ath10k *ar);
+void ath10k_mac_unregister(struct ath10k *ar);
+struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id);
+void ath10k_reset_scan(unsigned long ptr);
+void ath10k_offchan_tx_purge(struct ath10k *ar);
+void ath10k_offchan_tx_work(struct work_struct *work);
+
+static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif)
+{
+	return (struct ath10k_vif *)vif->drv_priv;
+}
+
+static inline void ath10k_tx_h_seq_no(struct sk_buff *skb)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+	struct ieee80211_vif *vif = info->control.vif;
+	struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+
+	if (info->flags  & IEEE80211_TX_CTL_ASSIGN_SEQ) {
+		if (arvif->tx_seq_no == 0)
+			arvif->tx_seq_no = 0x1000;
+
+		if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
+			arvif->tx_seq_no += 0x10;
+		hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
+		hdr->seq_ctrl |= cpu_to_le16(arvif->tx_seq_no);
+	}
+}
+
+#endif /* _MAC_H_ */


^ permalink raw reply related

* [PATCH v2 19/29] ath10k: add pci.c
From: Kalle Valo @ 2013-06-01 11:26 UTC (permalink / raw)
  To: linux-wireless; +Cc: ath10k-devel
In-Reply-To: <20130601111820.32443.46794.stgit@localhost6.localdomain6>

Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
---
 drivers/net/wireless/ath/ath10k/pci.c | 2506 +++++++++++++++++++++++++++++++++
 1 file changed, 2506 insertions(+)
 create mode 100644 drivers/net/wireless/ath/ath10k/pci.c

diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
new file mode 100644
index 0000000..8e4e832
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -0,0 +1,2506 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * 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/pci.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+
+#include "core.h"
+#include "debug.h"
+
+#include "targaddrs.h"
+#include "bmi.h"
+
+#include "hif.h"
+#include "htc.h"
+
+#include "ce.h"
+#include "pci.h"
+
+unsigned int ath10k_target_ps;
+module_param(ath10k_target_ps, uint, 0644);
+MODULE_PARM_DESC(ath10k_target_ps, "Enable ath10k Target (SoC) PS option");
+
+#define QCA988X_1_0_DEVICE_ID	(0xabcd)
+#define QCA988X_2_0_DEVICE_ID	(0x003c)
+
+static DEFINE_PCI_DEVICE_TABLE(ath10k_pci_id_table) = {
+	{ PCI_VDEVICE(ATHEROS, QCA988X_1_0_DEVICE_ID) }, /* PCI-E QCA988X V1 */
+	{ PCI_VDEVICE(ATHEROS, QCA988X_2_0_DEVICE_ID) }, /* PCI-E QCA988X V2 */
+	{0}
+};
+
+static int ath10k_pci_diag_read_access(struct ath10k *ar, u32 address,
+				       u32 *data);
+
+static void ath10k_pci_process_ce(struct ath10k *ar);
+static int ath10k_pci_post_rx(struct ath10k *ar);
+static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info,
+					     int num);
+static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info);
+static void ath10k_pci_stop_ce(struct ath10k *ar);
+
+static const struct ce_attr host_ce_config_wlan[] = {
+	/* host->target HTC control and raw streams */
+	{ /* CE0 */ CE_ATTR_FLAGS, 0, 16, 256, 0, NULL,},
+	/* could be moved to share CE3 */
+	/* target->host HTT + HTC control */
+	{ /* CE1 */ CE_ATTR_FLAGS, 0, 0, 512, 512, NULL,},
+	/* target->host WMI */
+	{ /* CE2 */ CE_ATTR_FLAGS, 0, 0, 2048, 32, NULL,},
+	/* host->target WMI */
+	{ /* CE3 */ CE_ATTR_FLAGS, 0, 32, 2048, 0, NULL,},
+	/* host->target HTT */
+	{ /* CE4 */ CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, 0,
+		    CE_HTT_H2T_MSG_SRC_NENTRIES, 256, 0, NULL,},
+	/* unused */
+	{ /* CE5 */ CE_ATTR_FLAGS, 0, 0, 0, 0, NULL,},
+	/* Target autonomous hif_memcpy */
+	{ /* CE6 */ CE_ATTR_FLAGS, 0, 0, 0, 0, NULL,},
+	/* ce_diag, the Diagnostic Window */
+	{ /* CE7 */ CE_ATTR_FLAGS, 0, 2, DIAG_TRANSFER_LIMIT, 2, NULL,},
+};
+
+/* Target firmware's Copy Engine configuration. */
+static const struct ce_pipe_config target_ce_config_wlan[] = {
+	/* host->target HTC control and raw streams */
+	{ /* CE0 */ 0, PIPEDIR_OUT, 32, 256, CE_ATTR_FLAGS, 0,},
+	/* target->host HTT + HTC control */
+	{ /* CE1 */ 1, PIPEDIR_IN, 32, 512, CE_ATTR_FLAGS, 0,},
+	/* target->host WMI */
+	{ /* CE2 */ 2, PIPEDIR_IN, 32, 2048, CE_ATTR_FLAGS, 0,},
+	/* host->target WMI */
+	{ /* CE3 */ 3, PIPEDIR_OUT, 32, 2048, CE_ATTR_FLAGS, 0,},
+	/* host->target HTT */
+	{ /* CE4 */ 4, PIPEDIR_OUT, 256, 256, CE_ATTR_FLAGS, 0,},
+	/* NB: 50% of src nentries, since tx has 2 frags */
+	/* unused */
+	{ /* CE5 */ 5, PIPEDIR_OUT, 32, 2048, CE_ATTR_FLAGS, 0,},
+	/* Reserved for target autonomous hif_memcpy */
+	{ /* CE6 */ 6, PIPEDIR_INOUT, 32, 4096, CE_ATTR_FLAGS, 0,},
+	/* CE7 used only by Host */
+};
+
+/*
+ * Diagnostic read/write access is provided for startup/config/debug usage.
+ * Caller must guarantee proper alignment, when applicable, and single user
+ * at any moment.
+ */
+static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data,
+				    int nbytes)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	int ret = 0;
+	u32 buf;
+	unsigned int completed_nbytes, orig_nbytes, remaining_bytes;
+	unsigned int id;
+	unsigned int flags;
+	struct ce_state *ce_diag;
+	/* Host buffer address in CE space */
+	u32 ce_data;
+	dma_addr_t ce_data_base = 0;
+	void *data_buf = NULL;
+	int i;
+
+	/*
+	 * This code cannot handle reads to non-memory space. Redirect to the
+	 * register read fn but preserve the multi word read capability of
+	 * this fn
+	 */
+	if (address < DRAM_BASE_ADDRESS) {
+		if (!IS_ALIGNED(address, 4) ||
+		    !IS_ALIGNED((unsigned long)data, 4))
+			return -EIO;
+
+		while ((nbytes >= 4) &&  ((ret = ath10k_pci_diag_read_access(
+					   ar, address, (u32 *)data)) == 0)) {
+			nbytes -= sizeof(u32);
+			address += sizeof(u32);
+			data += sizeof(u32);
+		}
+		return ret;
+	}
+
+	ce_diag = ar_pci->ce_diag;
+
+	/*
+	 * Allocate a temporary bounce buffer to hold caller's data
+	 * to be DMA'ed from Target. This guarantees
+	 *   1) 4-byte alignment
+	 *   2) Buffer in DMA-able space
+	 */
+	orig_nbytes = nbytes;
+	data_buf = (unsigned char *)pci_alloc_consistent(ar_pci->pdev,
+							 orig_nbytes,
+							 &ce_data_base);
+
+	if (!data_buf) {
+		ret = -ENOMEM;
+		goto done;
+	}
+	memset(data_buf, 0, orig_nbytes);
+
+	remaining_bytes = orig_nbytes;
+	ce_data = ce_data_base;
+	while (remaining_bytes) {
+		nbytes = min_t(unsigned int, remaining_bytes,
+			       DIAG_TRANSFER_LIMIT);
+
+		ret = ath10k_ce_recv_buf_enqueue(ce_diag, NULL, ce_data);
+		if (ret != 0)
+			goto done;
+
+		/* Request CE to send from Target(!) address to Host buffer */
+		/*
+		 * The address supplied by the caller is in the
+		 * Target CPU virtual address space.
+		 *
+		 * In order to use this address with the diagnostic CE,
+		 * convert it from Target CPU virtual address space
+		 * to CE address space
+		 */
+		ath10k_pci_wake(ar);
+		address = TARG_CPU_SPACE_TO_CE_SPACE(ar, ar_pci->mem,
+						     address);
+		ath10k_pci_sleep(ar);
+
+		ret = ath10k_ce_send(ce_diag, NULL, (u32)address, nbytes, 0,
+				 0);
+		if (ret)
+			goto done;
+
+		i = 0;
+		while (ath10k_ce_completed_send_next(ce_diag, NULL, &buf,
+						     &completed_nbytes,
+						     &id) != 0) {
+			mdelay(1);
+			if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
+				ret = -EBUSY;
+				goto done;
+			}
+		}
+
+		if (nbytes != completed_nbytes) {
+			ret = -EIO;
+			goto done;
+		}
+
+		if (buf != (u32) address) {
+			ret = -EIO;
+			goto done;
+		}
+
+		i = 0;
+		while (ath10k_ce_completed_recv_next(ce_diag, NULL, &buf,
+						     &completed_nbytes,
+						     &id, &flags) != 0) {
+			mdelay(1);
+
+			if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
+				ret = -EBUSY;
+				goto done;
+			}
+		}
+
+		if (nbytes != completed_nbytes) {
+			ret = -EIO;
+			goto done;
+		}
+
+		if (buf != ce_data) {
+			ret = -EIO;
+			goto done;
+		}
+
+		remaining_bytes -= nbytes;
+		address += nbytes;
+		ce_data += nbytes;
+	}
+
+done:
+	if (ret == 0) {
+		/* Copy data from allocated DMA buf to caller's buf */
+		WARN_ON_ONCE(orig_nbytes & 3);
+		for (i = 0; i < orig_nbytes / sizeof(__le32); i++) {
+			((u32 *)data)[i] =
+				__le32_to_cpu(((__le32 *)data_buf)[i]);
+		}
+	} else
+		ath10k_dbg(ATH10K_DBG_PCI, "%s failure (0x%x)\n",
+			   __func__, address);
+
+	if (data_buf)
+		pci_free_consistent(ar_pci->pdev, orig_nbytes,
+				    data_buf, ce_data_base);
+
+	return ret;
+}
+
+/* Read 4-byte aligned data from Target memory or register */
+static int ath10k_pci_diag_read_access(struct ath10k *ar, u32 address,
+				       u32 *data)
+{
+	/* Assume range doesn't cross this boundary */
+	if (address >= DRAM_BASE_ADDRESS)
+		return ath10k_pci_diag_read_mem(ar, address, data, sizeof(u32));
+
+	ath10k_pci_wake(ar);
+	*data = ath10k_pci_read32(ar, address);
+	ath10k_pci_sleep(ar);
+	return 0;
+}
+
+static int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address,
+				     const void *data, int nbytes)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	int ret = 0;
+	u32 buf;
+	unsigned int completed_nbytes, orig_nbytes, remaining_bytes;
+	unsigned int id;
+	unsigned int flags;
+	struct ce_state *ce_diag;
+	void *data_buf = NULL;
+	u32 ce_data;	/* Host buffer address in CE space */
+	dma_addr_t ce_data_base = 0;
+	int i;
+
+	ce_diag = ar_pci->ce_diag;
+
+	/*
+	 * Allocate a temporary bounce buffer to hold caller's data
+	 * to be DMA'ed to Target. This guarantees
+	 *   1) 4-byte alignment
+	 *   2) Buffer in DMA-able space
+	 */
+	orig_nbytes = nbytes;
+	data_buf = (unsigned char *)pci_alloc_consistent(ar_pci->pdev,
+							 orig_nbytes,
+							 &ce_data_base);
+	if (!data_buf) {
+		ret = -ENOMEM;
+		goto done;
+	}
+
+	/* Copy caller's data to allocated DMA buf */
+	WARN_ON_ONCE(orig_nbytes & 3);
+	for (i = 0; i < orig_nbytes / sizeof(__le32); i++)
+		((__le32 *)data_buf)[i] = __cpu_to_le32(((u32 *)data)[i]);
+
+	/*
+	 * The address supplied by the caller is in the
+	 * Target CPU virtual address space.
+	 *
+	 * In order to use this address with the diagnostic CE,
+	 * convert it from
+	 *    Target CPU virtual address space
+	 * to
+	 *    CE address space
+	 */
+	ath10k_pci_wake(ar);
+	address = TARG_CPU_SPACE_TO_CE_SPACE(ar, ar_pci->mem, address);
+	ath10k_pci_sleep(ar);
+
+	remaining_bytes = orig_nbytes;
+	ce_data = ce_data_base;
+	while (remaining_bytes) {
+		/* FIXME: check cast */
+		nbytes = min_t(int, remaining_bytes, DIAG_TRANSFER_LIMIT);
+
+		/* Set up to receive directly into Target(!) address */
+		ret = ath10k_ce_recv_buf_enqueue(ce_diag, NULL, address);
+		if (ret != 0)
+			goto done;
+
+		/*
+		 * Request CE to send caller-supplied data that
+		 * was copied to bounce buffer to Target(!) address.
+		 */
+		ret = ath10k_ce_send(ce_diag, NULL, (u32) ce_data,
+				     nbytes, 0, 0);
+		if (ret != 0)
+			goto done;
+
+		i = 0;
+		while (ath10k_ce_completed_send_next(ce_diag, NULL, &buf,
+						     &completed_nbytes,
+						     &id) != 0) {
+			mdelay(1);
+
+			if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
+				ret = -EBUSY;
+				goto done;
+			}
+		}
+
+		if (nbytes != completed_nbytes) {
+			ret = -EIO;
+			goto done;
+		}
+
+		if (buf != ce_data) {
+			ret = -EIO;
+			goto done;
+		}
+
+		i = 0;
+		while (ath10k_ce_completed_recv_next(ce_diag, NULL, &buf,
+						     &completed_nbytes,
+						     &id, &flags) != 0) {
+			mdelay(1);
+
+			if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
+				ret = -EBUSY;
+				goto done;
+			}
+		}
+
+		if (nbytes != completed_nbytes) {
+			ret = -EIO;
+			goto done;
+		}
+
+		if (buf != address) {
+			ret = -EIO;
+			goto done;
+		}
+
+		remaining_bytes -= nbytes;
+		address += nbytes;
+		ce_data += nbytes;
+	}
+
+done:
+	if (data_buf) {
+		pci_free_consistent(ar_pci->pdev, orig_nbytes, data_buf,
+				    ce_data_base);
+	}
+
+	if (ret != 0)
+		ath10k_dbg(ATH10K_DBG_PCI, "%s failure (0x%x)\n", __func__,
+			   address);
+
+	return ret;
+}
+
+/* Write 4B data to Target memory or register */
+static int ath10k_pci_diag_write_access(struct ath10k *ar, u32 address,
+					u32 data)
+{
+	/* Assume range doesn't cross this boundary */
+	if (address >= DRAM_BASE_ADDRESS)
+		return ath10k_pci_diag_write_mem(ar, address, &data,
+						 sizeof(u32));
+
+	ath10k_pci_wake(ar);
+	ath10k_pci_write32(ar, address, data);
+	ath10k_pci_sleep(ar);
+	return 0;
+}
+
+static bool ath10k_pci_target_is_awake(struct ath10k *ar)
+{
+	void __iomem *mem = ath10k_pci_priv(ar)->mem;
+	u32 val;
+	val = ioread32(mem + PCIE_LOCAL_BASE_ADDRESS +
+		       RTC_STATE_ADDRESS);
+	return (RTC_STATE_V_GET(val) == RTC_STATE_V_ON);
+}
+
+static void ath10k_pci_wait(struct ath10k *ar)
+{
+	int n = 100;
+
+	while (n-- && !ath10k_pci_target_is_awake(ar))
+		msleep(10);
+
+	if (n < 0)
+		ath10k_warn("Unable to wakeup target\n");
+}
+
+void ath10k_do_pci_wake(struct ath10k *ar)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	void __iomem *pci_addr = ar_pci->mem;
+	int tot_delay = 0;
+	int curr_delay = 5;
+
+	if (atomic_read(&ar_pci->keep_awake_count) == 0) {
+		/* Force AWAKE */
+		iowrite32(PCIE_SOC_WAKE_V_MASK,
+			  pci_addr + PCIE_LOCAL_BASE_ADDRESS +
+			  PCIE_SOC_WAKE_ADDRESS);
+	}
+	atomic_inc(&ar_pci->keep_awake_count);
+
+	if (ar_pci->verified_awake)
+		return;
+
+	for (;;) {
+		if (ath10k_pci_target_is_awake(ar)) {
+			ar_pci->verified_awake = true;
+			break;
+		}
+
+		if (tot_delay > PCIE_WAKE_TIMEOUT) {
+			ath10k_warn("target takes too long to wake up (awake count %d)\n",
+				    atomic_read(&ar_pci->keep_awake_count));
+			break;
+		}
+
+		udelay(curr_delay);
+		tot_delay += curr_delay;
+
+		if (curr_delay < 50)
+			curr_delay += 5;
+	}
+}
+
+void ath10k_do_pci_sleep(struct ath10k *ar)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	void __iomem *pci_addr = ar_pci->mem;
+
+	if (atomic_dec_and_test(&ar_pci->keep_awake_count)) {
+		/* Allow sleep */
+		ar_pci->verified_awake = false;
+		iowrite32(PCIE_SOC_WAKE_RESET,
+			  pci_addr + PCIE_LOCAL_BASE_ADDRESS +
+			  PCIE_SOC_WAKE_ADDRESS);
+	}
+}
+
+/*
+ * FIXME: Handle OOM properly.
+ */
+static inline
+struct ath10k_pci_compl *get_free_compl(struct hif_ce_pipe_info *pipe_info)
+{
+	struct ath10k_pci_compl *compl = NULL;
+
+	spin_lock_bh(&pipe_info->pipe_lock);
+	if (list_empty(&pipe_info->compl_free)) {
+		ath10k_warn("Completion buffers are full\n");
+		goto exit;
+	}
+	compl = list_first_entry(&pipe_info->compl_free,
+				 struct ath10k_pci_compl, list);
+	list_del(&compl->list);
+exit:
+	spin_unlock_bh(&pipe_info->pipe_lock);
+	return compl;
+}
+
+/* Called by lower (CE) layer when a send to Target completes. */
+static void ath10k_pci_ce_send_done(struct ce_state *ce_state,
+				    void *transfer_context,
+				    u32 ce_data,
+				    unsigned int nbytes,
+				    unsigned int transfer_id)
+{
+	struct ath10k *ar = ce_state->ar;
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	struct hif_ce_pipe_info *pipe_info =  &ar_pci->pipe_info[ce_state->id];
+	struct ath10k_pci_compl *compl;
+	bool process = false;
+
+	do {
+		/*
+		 * For the send completion of an item in sendlist, just
+		 * increment num_sends_allowed. The upper layer callback will
+		 * be triggered when last fragment is done with send.
+		 */
+		if (transfer_context == CE_SENDLIST_ITEM_CTXT) {
+			spin_lock_bh(&pipe_info->pipe_lock);
+			pipe_info->num_sends_allowed++;
+			spin_unlock_bh(&pipe_info->pipe_lock);
+			continue;
+		}
+
+		compl = get_free_compl(pipe_info);
+		if (!compl)
+			break;
+
+		compl->send_or_recv = HIF_CE_COMPLETE_SEND;
+		compl->ce_state = ce_state;
+		compl->pipe_info = pipe_info;
+		compl->transfer_context = transfer_context;
+		compl->nbytes = nbytes;
+		compl->transfer_id = transfer_id;
+		compl->flags = 0;
+
+		/*
+		 * Add the completion to the processing queue.
+		 */
+		spin_lock_bh(&ar_pci->compl_lock);
+		list_add_tail(&compl->list, &ar_pci->compl_process);
+		spin_unlock_bh(&ar_pci->compl_lock);
+
+		process = true;
+	} while (ath10k_ce_completed_send_next(ce_state,
+							   &transfer_context,
+							   &ce_data, &nbytes,
+							   &transfer_id) == 0);
+
+	/*
+	 * If only some of the items within a sendlist have completed,
+	 * don't invoke completion processing until the entire sendlist
+	 * has been sent.
+	 */
+	if (!process)
+		return;
+
+	ath10k_pci_process_ce(ar);
+}
+
+/* Called by lower (CE) layer when data is received from the Target. */
+static void ath10k_pci_ce_recv_data(struct ce_state *ce_state,
+				    void *transfer_context, u32 ce_data,
+				    unsigned int nbytes,
+				    unsigned int transfer_id,
+				    unsigned int flags)
+{
+	struct ath10k *ar = ce_state->ar;
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	struct hif_ce_pipe_info *pipe_info =  &ar_pci->pipe_info[ce_state->id];
+	struct ath10k_pci_compl *compl;
+	struct sk_buff *skb;
+
+	do {
+		compl = get_free_compl(pipe_info);
+		if (!compl)
+			break;
+
+		compl->send_or_recv = HIF_CE_COMPLETE_RECV;
+		compl->ce_state = ce_state;
+		compl->pipe_info = pipe_info;
+		compl->transfer_context = transfer_context;
+		compl->nbytes = nbytes;
+		compl->transfer_id = transfer_id;
+		compl->flags = flags;
+
+		skb = transfer_context;
+		dma_unmap_single(ar->dev, ATH10K_SKB_CB(skb)->paddr,
+				 skb->len + skb_tailroom(skb),
+				 DMA_FROM_DEVICE);
+		/*
+		 * Add the completion to the processing queue.
+		 */
+		spin_lock_bh(&ar_pci->compl_lock);
+		list_add_tail(&compl->list, &ar_pci->compl_process);
+		spin_unlock_bh(&ar_pci->compl_lock);
+
+	} while (ath10k_ce_completed_recv_next(ce_state,
+							   &transfer_context,
+							   &ce_data, &nbytes,
+							   &transfer_id,
+							   &flags) == 0);
+
+	ath10k_pci_process_ce(ar);
+}
+
+/* Send the first nbytes bytes of the buffer */
+static int ath10k_pci_hif_send_head(struct ath10k *ar, u8 pipe_id,
+				    unsigned int transfer_id,
+				    unsigned int bytes, struct sk_buff *nbuf)
+{
+	struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(nbuf);
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	struct hif_ce_pipe_info *pipe_info = &(ar_pci->pipe_info[pipe_id]);
+	struct ce_state *ce_hdl = pipe_info->ce_hdl;
+	struct ce_sendlist sendlist;
+	unsigned int len;
+	u32 flags = 0;
+	int ret;
+
+	memset(&sendlist, 0, sizeof(struct ce_sendlist));
+
+	len = min(bytes, nbuf->len);
+	bytes -= len;
+
+	if (len & 3)
+		ath10k_warn("skb not aligned to 4-byte boundary (%d)\n", len);
+
+	ath10k_dbg(ATH10K_DBG_PCI,
+		   "pci send data vaddr %p paddr 0x%llx len %d as %d bytes\n",
+		   nbuf->data, (unsigned long long) skb_cb->paddr,
+		   nbuf->len, len);
+	ath10k_dbg_dump(ATH10K_DBG_PCI_DUMP, NULL,
+			"ath10k tx: data: ",
+			nbuf->data, nbuf->len);
+
+	ath10k_ce_sendlist_buf_add(&sendlist, skb_cb->paddr, len, flags);
+
+	/* Make sure we have resources to handle this request */
+	spin_lock_bh(&pipe_info->pipe_lock);
+	if (!pipe_info->num_sends_allowed) {
+		ath10k_warn("Pipe: %d is full\n", pipe_id);
+		spin_unlock_bh(&pipe_info->pipe_lock);
+		return -ENOSR;
+	}
+	pipe_info->num_sends_allowed--;
+	spin_unlock_bh(&pipe_info->pipe_lock);
+
+	ret = ath10k_ce_sendlist_send(ce_hdl, nbuf, &sendlist, transfer_id);
+	if (ret)
+		ath10k_warn("CE send failed: %p\n", nbuf);
+
+	return ret;
+}
+
+static u16 ath10k_pci_hif_get_free_queue_number(struct ath10k *ar, u8 pipe)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	struct hif_ce_pipe_info *pipe_info = &(ar_pci->pipe_info[pipe]);
+	int ret;
+
+	spin_lock_bh(&pipe_info->pipe_lock);
+	ret = pipe_info->num_sends_allowed;
+	spin_unlock_bh(&pipe_info->pipe_lock);
+
+	return ret;
+}
+
+static void ath10k_pci_hif_dump_area(struct ath10k *ar)
+{
+	u32 reg_dump_area = 0;
+	u32 reg_dump_values[REG_DUMP_COUNT_QCA988X] = {};
+	u32 host_addr;
+	int ret;
+	u32 i;
+
+	ath10k_err("firmware crashed!\n");
+	ath10k_err("hardware name %s version 0x%x\n",
+		   ar->hw_params.name, ar->target_version);
+	ath10k_err("firmware version: %u.%u.%u.%u\n", ar->fw_version_major,
+		   ar->fw_version_minor, ar->fw_version_release,
+		   ar->fw_version_build);
+
+	host_addr = host_interest_item_address(HI_ITEM(hi_failure_state));
+	if (ath10k_pci_diag_read_mem(ar, host_addr,
+				     &reg_dump_area, sizeof(u32)) != 0) {
+		ath10k_warn("could not read hi_failure_state\n");
+		return;
+	}
+
+	ath10k_err("target register Dump Location: 0x%08X\n", reg_dump_area);
+
+	ret = ath10k_pci_diag_read_mem(ar, reg_dump_area,
+				       &reg_dump_values[0],
+				       REG_DUMP_COUNT_QCA988X * sizeof(u32));
+	if (ret != 0) {
+		ath10k_err("could not dump FW Dump Area\n");
+		return;
+	}
+
+	BUILD_BUG_ON(REG_DUMP_COUNT_QCA988X % 4);
+
+	ath10k_err("target Register Dump\n");
+	for (i = 0; i < REG_DUMP_COUNT_QCA988X; i += 4)
+		ath10k_err("[%02d]: 0x%08X 0x%08X 0x%08X 0x%08X\n",
+			   i,
+			   reg_dump_values[i],
+			   reg_dump_values[i + 1],
+			   reg_dump_values[i + 2],
+			   reg_dump_values[i + 3]);
+}
+
+static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe,
+					       int force)
+{
+	if (!force) {
+		int resources;
+		/*
+		 * Decide whether to actually poll for completions, or just
+		 * wait for a later chance.
+		 * If there seem to be plenty of resources left, then just wait
+		 * since checking involves reading a CE register, which is a
+		 * relatively expensive operation.
+		 */
+		resources = ath10k_pci_hif_get_free_queue_number(ar, pipe);
+
+		/*
+		 * If at least 50% of the total resources are still available,
+		 * don't bother checking again yet.
+		 */
+		if (resources > (host_ce_config_wlan[pipe].src_nentries >> 1))
+			return;
+	}
+	ath10k_ce_per_engine_service(ar, pipe);
+}
+
+static void ath10k_pci_hif_post_init(struct ath10k *ar,
+				     struct ath10k_hif_cb *callbacks)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+	ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
+
+	memcpy(&ar_pci->msg_callbacks_current, callbacks,
+	       sizeof(ar_pci->msg_callbacks_current));
+}
+
+static int ath10k_pci_start_ce(struct ath10k *ar)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	struct ce_state *ce_diag = ar_pci->ce_diag;
+	const struct ce_attr *attr;
+	struct hif_ce_pipe_info *pipe_info;
+	struct ath10k_pci_compl *compl;
+	int i, pipe_num, completions, disable_interrupts;
+
+	spin_lock_init(&ar_pci->compl_lock);
+	INIT_LIST_HEAD(&ar_pci->compl_process);
+
+	for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
+		pipe_info = &ar_pci->pipe_info[pipe_num];
+
+		spin_lock_init(&pipe_info->pipe_lock);
+		INIT_LIST_HEAD(&pipe_info->compl_free);
+
+		/* Handle Diagnostic CE specially */
+		if (pipe_info->ce_hdl == ce_diag)
+			continue;
+
+		attr = &host_ce_config_wlan[pipe_num];
+		completions = 0;
+
+		if (attr->src_nentries) {
+			disable_interrupts = attr->flags & CE_ATTR_DIS_INTR;
+			ath10k_ce_send_cb_register(pipe_info->ce_hdl,
+						   ath10k_pci_ce_send_done,
+						   disable_interrupts);
+			completions += attr->src_nentries;
+			pipe_info->num_sends_allowed = attr->src_nentries - 1;
+		}
+
+		if (attr->dest_nentries) {
+			ath10k_ce_recv_cb_register(pipe_info->ce_hdl,
+						   ath10k_pci_ce_recv_data);
+			completions += attr->dest_nentries;
+		}
+
+		if (completions == 0)
+			continue;
+
+		for (i = 0; i < completions; i++) {
+			compl = kmalloc(sizeof(struct ath10k_pci_compl),
+					GFP_KERNEL);
+			if (!compl) {
+				ath10k_warn("No memory for completion state\n");
+				ath10k_pci_stop_ce(ar);
+				return -ENOMEM;
+			}
+
+			compl->send_or_recv = HIF_CE_COMPLETE_FREE;
+			list_add_tail(&compl->list, &pipe_info->compl_free);
+		}
+	}
+
+	return 0;
+}
+
+static void ath10k_pci_stop_ce(struct ath10k *ar)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	struct ath10k_pci_compl *compl;
+	struct sk_buff *skb;
+	int i;
+
+	ath10k_ce_disable_interrupts(ar);
+
+	/* Cancel the pending tasklet */
+	tasklet_kill(&ar_pci->intr_tq);
+
+	for (i = 0; i < CE_COUNT; i++)
+		tasklet_kill(&ar_pci->pipe_info[i].intr);
+
+	/* Mark pending completions as aborted, so that upper layers free up
+	 * their associated resources */
+	spin_lock_bh(&ar_pci->compl_lock);
+	list_for_each_entry(compl, &ar_pci->compl_process, list) {
+		skb = (struct sk_buff *)compl->transfer_context;
+		ATH10K_SKB_CB(skb)->is_aborted = true;
+	}
+	spin_unlock_bh(&ar_pci->compl_lock);
+}
+
+static void ath10k_pci_cleanup_ce(struct ath10k *ar)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	struct ath10k_pci_compl *compl, *tmp;
+	struct hif_ce_pipe_info *pipe_info;
+	struct sk_buff *netbuf;
+	int pipe_num;
+
+	/* Free pending completions. */
+	spin_lock_bh(&ar_pci->compl_lock);
+	if (!list_empty(&ar_pci->compl_process))
+		ath10k_warn("pending completions still present! possible memory leaks.\n");
+
+	list_for_each_entry_safe(compl, tmp, &ar_pci->compl_process, list) {
+		list_del(&compl->list);
+		netbuf = (struct sk_buff *)compl->transfer_context;
+		dev_kfree_skb_any(netbuf);
+		kfree(compl);
+	}
+	spin_unlock_bh(&ar_pci->compl_lock);
+
+	/* Free unused completions for each pipe. */
+	for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
+		pipe_info = &ar_pci->pipe_info[pipe_num];
+
+		spin_lock_bh(&pipe_info->pipe_lock);
+		list_for_each_entry_safe(compl, tmp,
+					 &pipe_info->compl_free, list) {
+			list_del(&compl->list);
+			kfree(compl);
+		}
+		spin_unlock_bh(&pipe_info->pipe_lock);
+	}
+}
+
+static void ath10k_pci_process_ce(struct ath10k *ar)
+{
+	struct ath10k_pci *ar_pci = ar->hif.priv;
+	struct ath10k_hif_cb *cb = &ar_pci->msg_callbacks_current;
+	struct ath10k_pci_compl *compl;
+	struct sk_buff *skb;
+	unsigned int nbytes;
+	int ret, send_done = 0;
+
+	/* Upper layers aren't ready to handle tx/rx completions in parallel so
+	 * we must serialize all completion processing. */
+
+	spin_lock_bh(&ar_pci->compl_lock);
+	if (ar_pci->compl_processing) {
+		spin_unlock_bh(&ar_pci->compl_lock);
+		return;
+	}
+	ar_pci->compl_processing = true;
+	spin_unlock_bh(&ar_pci->compl_lock);
+
+	for (;;) {
+		spin_lock_bh(&ar_pci->compl_lock);
+		if (list_empty(&ar_pci->compl_process)) {
+			spin_unlock_bh(&ar_pci->compl_lock);
+			break;
+		}
+		compl = list_first_entry(&ar_pci->compl_process,
+					 struct ath10k_pci_compl, list);
+		list_del(&compl->list);
+		spin_unlock_bh(&ar_pci->compl_lock);
+
+		if (compl->send_or_recv == HIF_CE_COMPLETE_SEND) {
+			cb->tx_completion(ar,
+					  compl->transfer_context,
+					  compl->transfer_id);
+			send_done = 1;
+		} else {
+			ret = ath10k_pci_post_rx_pipe(compl->pipe_info, 1);
+			if (ret) {
+				ath10k_warn("Unable to post recv buffer for pipe: %d\n",
+					    compl->pipe_info->pipe_num);
+				break;
+			}
+
+			skb = (struct sk_buff *)compl->transfer_context;
+			nbytes = compl->nbytes;
+
+			ath10k_dbg(ATH10K_DBG_PCI,
+				   "ath10k_pci_ce_recv_data netbuf=%p  nbytes=%d\n",
+				   skb, nbytes);
+			ath10k_dbg_dump(ATH10K_DBG_PCI_DUMP, NULL,
+					"ath10k rx: ", skb->data, nbytes);
+
+			if (skb->len + skb_tailroom(skb) >= nbytes) {
+				skb_trim(skb, 0);
+				skb_put(skb, nbytes);
+				cb->rx_completion(ar, skb,
+						  compl->pipe_info->pipe_num);
+			} else {
+				ath10k_warn("rxed more than expected (nbytes %d, max %d)",
+					    nbytes,
+					    skb->len + skb_tailroom(skb));
+			}
+		}
+
+		compl->send_or_recv = HIF_CE_COMPLETE_FREE;
+
+		/*
+		 * Add completion back to the pipe's free list.
+		 */
+		spin_lock_bh(&compl->pipe_info->pipe_lock);
+		list_add_tail(&compl->list, &compl->pipe_info->compl_free);
+		compl->pipe_info->num_sends_allowed += send_done;
+		spin_unlock_bh(&compl->pipe_info->pipe_lock);
+	}
+
+	spin_lock_bh(&ar_pci->compl_lock);
+	ar_pci->compl_processing = false;
+	spin_unlock_bh(&ar_pci->compl_lock);
+}
+
+/* TODO - temporary mapping while we have too few CE's */
+static int ath10k_pci_hif_map_service_to_pipe(struct ath10k *ar,
+					      u16 service_id, u8 *ul_pipe,
+					      u8 *dl_pipe, int *ul_is_polled,
+					      int *dl_is_polled)
+{
+	int ret = 0;
+
+	/* polling for received messages not supported */
+	*dl_is_polled = 0;
+
+	switch (service_id) {
+	case ATH10K_HTC_SVC_ID_HTT_DATA_MSG:
+		/*
+		 * Host->target HTT gets its own pipe, so it can be polled
+		 * while other pipes are interrupt driven.
+		 */
+		*ul_pipe = 4;
+		/*
+		 * Use the same target->host pipe for HTC ctrl, HTC raw
+		 * streams, and HTT.
+		 */
+		*dl_pipe = 1;
+		break;
+
+	case ATH10K_HTC_SVC_ID_RSVD_CTRL:
+	case ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS:
+		/*
+		 * Note: HTC_RAW_STREAMS_SVC is currently unused, and
+		 * HTC_CTRL_RSVD_SVC could share the same pipe as the
+		 * WMI services.  So, if another CE is needed, change
+		 * this to *ul_pipe = 3, which frees up CE 0.
+		 */
+		/* *ul_pipe = 3; */
+		*ul_pipe = 0;
+		*dl_pipe = 1;
+		break;
+
+	case ATH10K_HTC_SVC_ID_WMI_DATA_BK:
+	case ATH10K_HTC_SVC_ID_WMI_DATA_BE:
+	case ATH10K_HTC_SVC_ID_WMI_DATA_VI:
+	case ATH10K_HTC_SVC_ID_WMI_DATA_VO:
+
+	case ATH10K_HTC_SVC_ID_WMI_CONTROL:
+		*ul_pipe = 3;
+		*dl_pipe = 2;
+		break;
+
+		/* pipe 5 unused   */
+		/* pipe 6 reserved */
+		/* pipe 7 reserved */
+
+	default:
+		ret = -1;
+		break;
+	}
+	*ul_is_polled =
+		(host_ce_config_wlan[*ul_pipe].flags & CE_ATTR_DIS_INTR) != 0;
+
+	return ret;
+}
+
+static void ath10k_pci_hif_get_default_pipe(struct ath10k *ar,
+						u8 *ul_pipe, u8 *dl_pipe)
+{
+	int ul_is_polled, dl_is_polled;
+
+	(void)ath10k_pci_hif_map_service_to_pipe(ar,
+						 ATH10K_HTC_SVC_ID_RSVD_CTRL,
+						 ul_pipe,
+						 dl_pipe,
+						 &ul_is_polled,
+						 &dl_is_polled);
+}
+
+static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info,
+				   int num)
+{
+	struct ath10k *ar = pipe_info->hif_ce_state;
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	struct ce_state *ce_state = pipe_info->ce_hdl;
+	struct sk_buff *skb;
+	dma_addr_t ce_data;
+	int i, ret = 0;
+
+	if (pipe_info->buf_sz == 0)
+		return 0;
+
+	for (i = 0; i < num; i++) {
+		skb = dev_alloc_skb(pipe_info->buf_sz);
+		if (!skb) {
+			ath10k_warn("could not allocate skbuff for pipe %d\n",
+				    num);
+			ret = -ENOMEM;
+			goto err;
+		}
+
+		WARN_ONCE((unsigned long)skb->data & 3, "unaligned skb");
+
+		ce_data = dma_map_single(ar->dev, skb->data,
+					 skb->len + skb_tailroom(skb),
+					 DMA_FROM_DEVICE);
+
+		if (unlikely(dma_mapping_error(ar->dev, ce_data))) {
+			ath10k_warn("could not dma map skbuff\n");
+			dev_kfree_skb_any(skb);
+			ret = -EIO;
+			goto err;
+		}
+
+		ATH10K_SKB_CB(skb)->paddr = ce_data;
+
+		pci_dma_sync_single_for_device(ar_pci->pdev, ce_data,
+					       pipe_info->buf_sz,
+					       PCI_DMA_FROMDEVICE);
+
+		ret = ath10k_ce_recv_buf_enqueue(ce_state, (void *)skb,
+						 ce_data);
+		if (ret) {
+			ath10k_warn("could not enqueue to pipe %d (%d)\n",
+				    num, ret);
+			goto err;
+		}
+	}
+
+	return ret;
+
+err:
+	ath10k_pci_rx_pipe_cleanup(pipe_info);
+	return ret;
+}
+
+static int ath10k_pci_post_rx(struct ath10k *ar)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	struct hif_ce_pipe_info *pipe_info;
+	const struct ce_attr *attr;
+	int pipe_num, ret = 0;
+
+	for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
+		pipe_info = &ar_pci->pipe_info[pipe_num];
+		attr = &host_ce_config_wlan[pipe_num];
+
+		if (attr->dest_nentries == 0)
+			continue;
+
+		ret = ath10k_pci_post_rx_pipe(pipe_info,
+					      attr->dest_nentries - 1);
+		if (ret) {
+			ath10k_warn("Unable to replenish recv buffers for pipe: %d\n",
+				    pipe_num);
+
+			for (; pipe_num >= 0; pipe_num--) {
+				pipe_info = &ar_pci->pipe_info[pipe_num];
+				ath10k_pci_rx_pipe_cleanup(pipe_info);
+			}
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int ath10k_pci_hif_start(struct ath10k *ar)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	int ret;
+
+	ret = ath10k_pci_start_ce(ar);
+	if (ret) {
+		ath10k_warn("could not start CE (%d)\n", ret);
+		return ret;
+	}
+
+	/* Post buffers once to start things off. */
+	ret = ath10k_pci_post_rx(ar);
+	if (ret) {
+		ath10k_warn("could not post rx pipes (%d)\n", ret);
+		return ret;
+	}
+
+	ar_pci->started = 1;
+	return 0;
+}
+
+static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info)
+{
+	struct ath10k *ar;
+	struct ath10k_pci *ar_pci;
+	struct ce_state *ce_hdl;
+	u32 buf_sz;
+	struct sk_buff *netbuf;
+	u32 ce_data;
+
+	buf_sz = pipe_info->buf_sz;
+
+	/* Unused Copy Engine */
+	if (buf_sz == 0)
+		return;
+
+	ar = pipe_info->hif_ce_state;
+	ar_pci = ath10k_pci_priv(ar);
+
+	if (!ar_pci->started)
+		return;
+
+	ce_hdl = pipe_info->ce_hdl;
+
+	while (ath10k_ce_revoke_recv_next(ce_hdl, (void **)&netbuf,
+					  &ce_data) == 0) {
+		dma_unmap_single(ar->dev, ATH10K_SKB_CB(netbuf)->paddr,
+				 netbuf->len + skb_tailroom(netbuf),
+				 DMA_FROM_DEVICE);
+		dev_kfree_skb_any(netbuf);
+	}
+}
+
+static void ath10k_pci_tx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info)
+{
+	struct ath10k *ar;
+	struct ath10k_pci *ar_pci;
+	struct ce_state *ce_hdl;
+	struct sk_buff *netbuf;
+	u32 ce_data;
+	unsigned int nbytes;
+	unsigned int id;
+	u32 buf_sz;
+
+	buf_sz = pipe_info->buf_sz;
+
+	/* Unused Copy Engine */
+	if (buf_sz == 0)
+		return;
+
+	ar = pipe_info->hif_ce_state;
+	ar_pci = ath10k_pci_priv(ar);
+
+	if (!ar_pci->started)
+		return;
+
+	ce_hdl = pipe_info->ce_hdl;
+
+	while (ath10k_ce_cancel_send_next(ce_hdl, (void **)&netbuf,
+					  &ce_data, &nbytes, &id) == 0) {
+		if (netbuf != CE_SENDLIST_ITEM_CTXT)
+			/*
+			 * Indicate the completion to higer layer to free
+			 * the buffer
+			 */
+			ATH10K_SKB_CB(netbuf)->is_aborted = true;
+			ar_pci->msg_callbacks_current.tx_completion(ar,
+								    netbuf,
+								    id);
+	}
+}
+
+/*
+ * Cleanup residual buffers for device shutdown:
+ *    buffers that were enqueued for receive
+ *    buffers that were to be sent
+ * Note: Buffers that had completed but which were
+ * not yet processed are on a completion queue. They
+ * are handled when the completion thread shuts down.
+ */
+static void ath10k_pci_buffer_cleanup(struct ath10k *ar)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	int pipe_num;
+
+	for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
+		struct hif_ce_pipe_info *pipe_info;
+
+		pipe_info = &ar_pci->pipe_info[pipe_num];
+		ath10k_pci_rx_pipe_cleanup(pipe_info);
+		ath10k_pci_tx_pipe_cleanup(pipe_info);
+	}
+}
+
+static void ath10k_pci_ce_deinit(struct ath10k *ar)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	struct hif_ce_pipe_info *pipe_info;
+	int pipe_num;
+
+	for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
+		pipe_info = &ar_pci->pipe_info[pipe_num];
+		if (pipe_info->ce_hdl) {
+			ath10k_ce_deinit(pipe_info->ce_hdl);
+			pipe_info->ce_hdl = NULL;
+			pipe_info->buf_sz = 0;
+		}
+	}
+}
+
+static void ath10k_pci_hif_stop(struct ath10k *ar)
+{
+	ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
+
+	ath10k_pci_stop_ce(ar);
+
+	/* At this point, asynchronous threads are stopped, the target should
+	 * not DMA nor interrupt. We process the leftovers and then free
+	 * everything else up. */
+
+	ath10k_pci_process_ce(ar);
+	ath10k_pci_cleanup_ce(ar);
+	ath10k_pci_buffer_cleanup(ar);
+	ath10k_pci_ce_deinit(ar);
+}
+
+static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar,
+					   void *req, u32 req_len,
+					   void *resp, u32 *resp_len)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	struct ce_state *ce_tx = ar_pci->pipe_info[BMI_CE_NUM_TO_TARG].ce_hdl;
+	struct ce_state *ce_rx = ar_pci->pipe_info[BMI_CE_NUM_TO_HOST].ce_hdl;
+	dma_addr_t req_paddr = 0;
+	dma_addr_t resp_paddr = 0;
+	struct bmi_xfer xfer = {};
+	void *treq, *tresp = NULL;
+	int ret = 0;
+
+	if (resp && !resp_len)
+		return -EINVAL;
+
+	if (resp && resp_len && *resp_len == 0)
+		return -EINVAL;
+
+	treq = kmemdup(req, req_len, GFP_KERNEL);
+	if (!treq)
+		return -ENOMEM;
+
+	req_paddr = dma_map_single(ar->dev, treq, req_len, DMA_TO_DEVICE);
+	ret = dma_mapping_error(ar->dev, req_paddr);
+	if (ret)
+		goto err_dma;
+
+	if (resp && resp_len) {
+		tresp = kzalloc(*resp_len, GFP_KERNEL);
+		if (!tresp) {
+			ret = -ENOMEM;
+			goto err_req;
+		}
+
+		resp_paddr = dma_map_single(ar->dev, tresp, *resp_len,
+					    DMA_FROM_DEVICE);
+		ret = dma_mapping_error(ar->dev, resp_paddr);
+		if (ret)
+			goto err_req;
+
+		xfer.wait_for_resp = true;
+		xfer.resp_len = 0;
+
+		ath10k_ce_recv_buf_enqueue(ce_rx, &xfer, resp_paddr);
+	}
+
+	init_completion(&xfer.done);
+
+	ret = ath10k_ce_send(ce_tx, &xfer, req_paddr, req_len, -1, 0);
+	if (ret)
+		goto err_resp;
+
+	ret = wait_for_completion_timeout(&xfer.done,
+					  BMI_COMMUNICATION_TIMEOUT_HZ);
+	if (ret <= 0) {
+		u32 unused_buffer;
+		unsigned int unused_nbytes;
+		unsigned int unused_id;
+
+		ret = -ETIMEDOUT;
+		ath10k_ce_cancel_send_next(ce_tx, NULL, &unused_buffer,
+					   &unused_nbytes, &unused_id);
+	} else {
+		/* non-zero means we did not time out */
+		ret = 0;
+	}
+
+err_resp:
+	if (resp) {
+		u32 unused_buffer;
+
+		ath10k_ce_revoke_recv_next(ce_rx, NULL, &unused_buffer);
+		dma_unmap_single(ar->dev, resp_paddr,
+				 *resp_len, DMA_FROM_DEVICE);
+	}
+err_req:
+	dma_unmap_single(ar->dev, req_paddr, req_len, DMA_TO_DEVICE);
+
+	if (ret == 0 && resp_len) {
+		*resp_len = min(*resp_len, xfer.resp_len);
+		memcpy(resp, tresp, xfer.resp_len);
+	}
+err_dma:
+	kfree(treq);
+	kfree(tresp);
+
+	return ret;
+}
+
+static void ath10k_pci_bmi_send_done(struct ce_state *ce_state,
+				     void *transfer_context,
+				     u32 data,
+				     unsigned int nbytes,
+				     unsigned int transfer_id)
+{
+	struct bmi_xfer *xfer = transfer_context;
+
+	if (xfer->wait_for_resp)
+		return;
+
+	complete(&xfer->done);
+}
+
+static void ath10k_pci_bmi_recv_data(struct ce_state *ce_state,
+				     void *transfer_context,
+				     u32 data,
+				     unsigned int nbytes,
+				     unsigned int transfer_id,
+				     unsigned int flags)
+{
+	struct bmi_xfer *xfer = transfer_context;
+
+	if (!xfer->wait_for_resp) {
+		ath10k_warn("unexpected: BMI data received; ignoring\n");
+		return;
+	}
+
+	xfer->resp_len = nbytes;
+	complete(&xfer->done);
+}
+
+/*
+ * Map from service/endpoint to Copy Engine.
+ * This table is derived from the CE_PCI TABLE, above.
+ * It is passed to the Target at startup for use by firmware.
+ */
+static const struct service_to_pipe target_service_to_ce_map_wlan[] = {
+	{
+		 ATH10K_HTC_SVC_ID_WMI_DATA_VO,
+		 PIPEDIR_OUT,		/* out = UL = host -> target */
+		 3,
+	},
+	{
+		 ATH10K_HTC_SVC_ID_WMI_DATA_VO,
+		 PIPEDIR_IN,		/* in = DL = target -> host */
+		 2,
+	},
+	{
+		 ATH10K_HTC_SVC_ID_WMI_DATA_BK,
+		 PIPEDIR_OUT,		/* out = UL = host -> target */
+		 3,
+	},
+	{
+		 ATH10K_HTC_SVC_ID_WMI_DATA_BK,
+		 PIPEDIR_IN,		/* in = DL = target -> host */
+		 2,
+	},
+	{
+		 ATH10K_HTC_SVC_ID_WMI_DATA_BE,
+		 PIPEDIR_OUT,		/* out = UL = host -> target */
+		 3,
+	},
+	{
+		 ATH10K_HTC_SVC_ID_WMI_DATA_BE,
+		 PIPEDIR_IN,		/* in = DL = target -> host */
+		 2,
+	},
+	{
+		 ATH10K_HTC_SVC_ID_WMI_DATA_VI,
+		 PIPEDIR_OUT,		/* out = UL = host -> target */
+		 3,
+	},
+	{
+		 ATH10K_HTC_SVC_ID_WMI_DATA_VI,
+		 PIPEDIR_IN,		/* in = DL = target -> host */
+		 2,
+	},
+	{
+		 ATH10K_HTC_SVC_ID_WMI_CONTROL,
+		 PIPEDIR_OUT,		/* out = UL = host -> target */
+		 3,
+	},
+	{
+		 ATH10K_HTC_SVC_ID_WMI_CONTROL,
+		 PIPEDIR_IN,		/* in = DL = target -> host */
+		 2,
+	},
+	{
+		 ATH10K_HTC_SVC_ID_RSVD_CTRL,
+		 PIPEDIR_OUT,		/* out = UL = host -> target */
+		 0,		/* could be moved to 3 (share with WMI) */
+	},
+	{
+		 ATH10K_HTC_SVC_ID_RSVD_CTRL,
+		 PIPEDIR_IN,		/* in = DL = target -> host */
+		 1,
+	},
+	{
+		 ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS,	/* not currently used */
+		 PIPEDIR_OUT,		/* out = UL = host -> target */
+		 0,
+	},
+	{
+		 ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS,	/* not currently used */
+		 PIPEDIR_IN,		/* in = DL = target -> host */
+		 1,
+	},
+	{
+		 ATH10K_HTC_SVC_ID_HTT_DATA_MSG,
+		 PIPEDIR_OUT,		/* out = UL = host -> target */
+		 4,
+	},
+	{
+		 ATH10K_HTC_SVC_ID_HTT_DATA_MSG,
+		 PIPEDIR_IN,		/* in = DL = target -> host */
+		 1,
+	},
+
+	/* (Additions here) */
+
+	{				/* Must be last */
+		 0,
+		 0,
+		 0,
+	},
+};
+
+/*
+ * Send an interrupt to the device to wake up the Target CPU
+ * so it has an opportunity to notice any changed state.
+ */
+static int ath10k_pci_wake_target_cpu(struct ath10k *ar)
+{
+	int ret;
+	u32 core_ctrl;
+
+	ret = ath10k_pci_diag_read_access(ar, SOC_CORE_BASE_ADDRESS |
+					      CORE_CTRL_ADDRESS,
+					  &core_ctrl);
+	if (ret) {
+		ath10k_warn("Unable to read core ctrl\n");
+		return ret;
+	}
+
+	/* A_INUM_FIRMWARE interrupt to Target CPU */
+	core_ctrl |= CORE_CTRL_CPU_INTR_MASK;
+
+	ret = ath10k_pci_diag_write_access(ar, SOC_CORE_BASE_ADDRESS |
+					       CORE_CTRL_ADDRESS,
+					   core_ctrl);
+	if (ret)
+		ath10k_warn("Unable to set interrupt mask\n");
+
+	return ret;
+}
+
+static int ath10k_pci_init_config(struct ath10k *ar)
+{
+	u32 interconnect_targ_addr;
+	u32 pcie_state_targ_addr = 0;
+	u32 pipe_cfg_targ_addr = 0;
+	u32 svc_to_pipe_map = 0;
+	u32 pcie_config_flags = 0;
+	u32 ealloc_value;
+	u32 ealloc_targ_addr;
+	u32 flag2_value;
+	u32 flag2_targ_addr;
+	int ret = 0;
+
+	/* Download to Target the CE Config and the service-to-CE map */
+	interconnect_targ_addr =
+		host_interest_item_address(HI_ITEM(hi_interconnect_state));
+
+	/* Supply Target-side CE configuration */
+	ret = ath10k_pci_diag_read_access(ar, interconnect_targ_addr,
+					  &pcie_state_targ_addr);
+	if (ret != 0) {
+		ath10k_err("Failed to get pcie state addr: %d\n", ret);
+		return ret;
+	}
+
+	if (pcie_state_targ_addr == 0) {
+		ret = -EIO;
+		ath10k_err("Invalid pcie state addr\n");
+		return ret;
+	}
+
+	ret = ath10k_pci_diag_read_access(ar, pcie_state_targ_addr +
+					  offsetof(struct pcie_state,
+						   pipe_cfg_addr),
+					  &pipe_cfg_targ_addr);
+	if (ret != 0) {
+		ath10k_err("Failed to get pipe cfg addr: %d\n", ret);
+		return ret;
+	}
+
+	if (pipe_cfg_targ_addr == 0) {
+		ret = -EIO;
+		ath10k_err("Invalid pipe cfg addr\n");
+		return ret;
+	}
+
+	ret = ath10k_pci_diag_write_mem(ar, pipe_cfg_targ_addr,
+				 target_ce_config_wlan,
+				 sizeof(target_ce_config_wlan));
+
+	if (ret != 0) {
+		ath10k_err("Failed to write pipe cfg: %d\n", ret);
+		return ret;
+	}
+
+	ret = ath10k_pci_diag_read_access(ar, pcie_state_targ_addr +
+					  offsetof(struct pcie_state,
+						   svc_to_pipe_map),
+					  &svc_to_pipe_map);
+	if (ret != 0) {
+		ath10k_err("Failed to get svc/pipe map: %d\n", ret);
+		return ret;
+	}
+
+	if (svc_to_pipe_map == 0) {
+		ret = -EIO;
+		ath10k_err("Invalid svc_to_pipe map\n");
+		return ret;
+	}
+
+	ret = ath10k_pci_diag_write_mem(ar, svc_to_pipe_map,
+				 target_service_to_ce_map_wlan,
+				 sizeof(target_service_to_ce_map_wlan));
+	if (ret != 0) {
+		ath10k_err("Failed to write svc/pipe map: %d\n", ret);
+		return ret;
+	}
+
+	ret = ath10k_pci_diag_read_access(ar, pcie_state_targ_addr +
+					  offsetof(struct pcie_state,
+						   config_flags),
+					  &pcie_config_flags);
+	if (ret != 0) {
+		ath10k_err("Failed to get pcie config_flags: %d\n", ret);
+		return ret;
+	}
+
+	pcie_config_flags &= ~PCIE_CONFIG_FLAG_ENABLE_L1;
+
+	ret = ath10k_pci_diag_write_mem(ar, pcie_state_targ_addr +
+				 offsetof(struct pcie_state, config_flags),
+				 &pcie_config_flags,
+				 sizeof(pcie_config_flags));
+	if (ret != 0) {
+		ath10k_err("Failed to write pcie config_flags: %d\n", ret);
+		return ret;
+	}
+
+	/* configure early allocation */
+	ealloc_targ_addr = host_interest_item_address(HI_ITEM(hi_early_alloc));
+
+	ret = ath10k_pci_diag_read_access(ar, ealloc_targ_addr, &ealloc_value);
+	if (ret != 0) {
+		ath10k_err("Faile to get early alloc val: %d\n", ret);
+		return ret;
+	}
+
+	/* first bank is switched to IRAM */
+	ealloc_value |= ((HI_EARLY_ALLOC_MAGIC << HI_EARLY_ALLOC_MAGIC_SHIFT) &
+			 HI_EARLY_ALLOC_MAGIC_MASK);
+	ealloc_value |= ((1 << HI_EARLY_ALLOC_IRAM_BANKS_SHIFT) &
+			 HI_EARLY_ALLOC_IRAM_BANKS_MASK);
+
+	ret = ath10k_pci_diag_write_access(ar, ealloc_targ_addr, ealloc_value);
+	if (ret != 0) {
+		ath10k_err("Failed to set early alloc val: %d\n", ret);
+		return ret;
+	}
+
+	/* Tell Target to proceed with initialization */
+	flag2_targ_addr = host_interest_item_address(HI_ITEM(hi_option_flag2));
+
+	ret = ath10k_pci_diag_read_access(ar, flag2_targ_addr, &flag2_value);
+	if (ret != 0) {
+		ath10k_err("Failed to get option val: %d\n", ret);
+		return ret;
+	}
+
+	flag2_value |= HI_OPTION_EARLY_CFG_DONE;
+
+	ret = ath10k_pci_diag_write_access(ar, flag2_targ_addr, flag2_value);
+	if (ret != 0) {
+		ath10k_err("Failed to set option val: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+
+
+static int ath10k_pci_ce_init(struct ath10k *ar)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	struct hif_ce_pipe_info *pipe_info;
+	const struct ce_attr *attr;
+	int pipe_num;
+
+	for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) {
+		pipe_info = &ar_pci->pipe_info[pipe_num];
+		pipe_info->pipe_num = pipe_num;
+		pipe_info->hif_ce_state = ar;
+		attr = &host_ce_config_wlan[pipe_num];
+
+		pipe_info->ce_hdl = ath10k_ce_init(ar, pipe_num, attr);
+		if (pipe_info->ce_hdl == NULL) {
+			ath10k_err("Unable to initialize CE for pipe: %d\n",
+				   pipe_num);
+
+			/* It is safe to call it here. It checks if ce_hdl is
+			 * valid for each pipe */
+			ath10k_pci_ce_deinit(ar);
+			return -1;
+		}
+
+		if (pipe_num == ar_pci->ce_count - 1) {
+			/*
+			 * Reserve the ultimate CE for
+			 * diagnostic Window support
+			 */
+			ar_pci->ce_diag =
+			ar_pci->pipe_info[ar_pci->ce_count - 1].ce_hdl;
+			continue;
+		}
+
+		pipe_info->buf_sz = (size_t) (attr->src_sz_max);
+	}
+
+	/*
+	 * Initially, establish CE completion handlers for use with BMI.
+	 * These are overwritten with generic handlers after we exit BMI phase.
+	 */
+	pipe_info = &ar_pci->pipe_info[BMI_CE_NUM_TO_TARG];
+	ath10k_ce_send_cb_register(pipe_info->ce_hdl,
+				   ath10k_pci_bmi_send_done, 0);
+
+	pipe_info = &ar_pci->pipe_info[BMI_CE_NUM_TO_HOST];
+	ath10k_ce_recv_cb_register(pipe_info->ce_hdl,
+				   ath10k_pci_bmi_recv_data);
+
+	return 0;
+}
+
+static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	u32 fw_indicator_address, fw_indicator;
+
+	ath10k_pci_wake(ar);
+
+	fw_indicator_address = ar_pci->fw_indicator_address;
+	fw_indicator = ath10k_pci_read32(ar, fw_indicator_address);
+
+	if (fw_indicator & FW_IND_EVENT_PENDING) {
+		/* ACK: clear Target-side pending event */
+		ath10k_pci_write32(ar, fw_indicator_address,
+				   fw_indicator & ~FW_IND_EVENT_PENDING);
+
+		if (ar_pci->started) {
+			ath10k_pci_hif_dump_area(ar);
+		} else {
+			/*
+			 * Probable Target failure before we're prepared
+			 * to handle it.  Generally unexpected.
+			 */
+			ath10k_warn("early firmware event indicated\n");
+		}
+	}
+
+	ath10k_pci_sleep(ar);
+}
+
+static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
+	.send_head		= ath10k_pci_hif_send_head,
+	.exchange_bmi_msg	= ath10k_pci_hif_exchange_bmi_msg,
+	.start			= ath10k_pci_hif_start,
+	.stop			= ath10k_pci_hif_stop,
+	.map_service_to_pipe	= ath10k_pci_hif_map_service_to_pipe,
+	.get_default_pipe	= ath10k_pci_hif_get_default_pipe,
+	.send_complete_check	= ath10k_pci_hif_send_complete_check,
+	.init			= ath10k_pci_hif_post_init,
+	.get_free_queue_number	= ath10k_pci_hif_get_free_queue_number,
+};
+
+static void ath10k_pci_ce_tasklet(unsigned long ptr)
+{
+	struct hif_ce_pipe_info *pipe = (struct hif_ce_pipe_info *)ptr;
+	struct ath10k_pci *ar_pci = pipe->ar_pci;
+
+	ath10k_ce_per_engine_service(ar_pci->ar, pipe->pipe_num);
+}
+
+static void ath10k_msi_err_tasklet(unsigned long data)
+{
+	struct ath10k *ar = (struct ath10k *)data;
+
+	ath10k_pci_fw_interrupt_handler(ar);
+}
+
+/*
+ * Handler for a per-engine interrupt on a PARTICULAR CE.
+ * This is used in cases where each CE has a private MSI interrupt.
+ */
+static irqreturn_t ath10k_pci_per_engine_handler(int irq, void *arg)
+{
+	struct ath10k *ar = arg;
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	int ce_id = irq - ar_pci->pdev->irq - MSI_ASSIGN_CE_INITIAL;
+
+	if (ce_id < 0 || ce_id > ARRAY_SIZE(ar_pci->pipe_info)) {
+		ath10k_warn("unexpected/invalid irq %d ce_id %d\n", irq, ce_id);
+		return IRQ_HANDLED;
+	}
+
+	/*
+	 * NOTE: We are able to derive ce_id from irq because we
+	 * use a one-to-one mapping for CE's 0..5.
+	 * CE's 6 & 7 do not use interrupts at all.
+	 *
+	 * This mapping must be kept in sync with the mapping
+	 * used by firmware.
+	 */
+	tasklet_schedule(&ar_pci->pipe_info[ce_id].intr);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t ath10k_pci_msi_fw_handler(int irq, void *arg)
+{
+	struct ath10k *ar = arg;
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+	tasklet_schedule(&ar_pci->msi_fw_err);
+	return IRQ_HANDLED;
+}
+
+/*
+ * Top-level interrupt handler for all PCI interrupts from a Target.
+ * When a block of MSI interrupts is allocated, this top-level handler
+ * is not used; instead, we directly call the correct sub-handler.
+ */
+static irqreturn_t ath10k_pci_interrupt_handler(int irq, void *arg)
+{
+	struct ath10k *ar = arg;
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+	if (ar_pci->num_msi_intrs == 0) {
+		/*
+		 * IMPORTANT: INTR_CLR regiser has to be set after
+		 * INTR_ENABLE is set to 0, otherwise interrupt can not be
+		 * really cleared.
+		 */
+		iowrite32(0, ar_pci->mem +
+			  (SOC_CORE_BASE_ADDRESS |
+			   PCIE_INTR_ENABLE_ADDRESS));
+		iowrite32(PCIE_INTR_FIRMWARE_MASK |
+			  PCIE_INTR_CE_MASK_ALL,
+			  ar_pci->mem + (SOC_CORE_BASE_ADDRESS |
+					 PCIE_INTR_CLR_ADDRESS));
+		/*
+		 * IMPORTANT: this extra read transaction is required to
+		 * flush the posted write buffer.
+		 */
+		(void) ioread32(ar_pci->mem +
+				(SOC_CORE_BASE_ADDRESS |
+				 PCIE_INTR_ENABLE_ADDRESS));
+	}
+
+	tasklet_schedule(&ar_pci->intr_tq);
+
+	return IRQ_HANDLED;
+}
+
+static void ath10k_pci_tasklet(unsigned long data)
+{
+	struct ath10k *ar = (struct ath10k *)data;
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+	ath10k_pci_fw_interrupt_handler(ar); /* FIXME: Handle FW error */
+	ath10k_ce_per_engine_service_any(ar);
+
+	if (ar_pci->num_msi_intrs == 0) {
+		/* Enable Legacy PCI line interrupts */
+		iowrite32(PCIE_INTR_FIRMWARE_MASK |
+			  PCIE_INTR_CE_MASK_ALL,
+			  ar_pci->mem + (SOC_CORE_BASE_ADDRESS |
+					 PCIE_INTR_ENABLE_ADDRESS));
+		/*
+		 * IMPORTANT: this extra read transaction is required to
+		 * flush the posted write buffer
+		 */
+		(void) ioread32(ar_pci->mem +
+				(SOC_CORE_BASE_ADDRESS |
+				 PCIE_INTR_ENABLE_ADDRESS));
+	}
+}
+
+static int ath10k_pci_start_intr_msix(struct ath10k *ar, int num)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	int ret;
+	int i;
+
+	ret = pci_enable_msi_block(ar_pci->pdev, num);
+	if (ret)
+		return ret;
+
+	ret = request_irq(ar_pci->pdev->irq + MSI_ASSIGN_FW,
+			  ath10k_pci_msi_fw_handler,
+			  IRQF_SHARED, "ath10k_pci", ar);
+	if (ret)
+		return ret;
+
+	for (i = MSI_ASSIGN_CE_INITIAL; i <= MSI_ASSIGN_CE_MAX; i++) {
+		ret = request_irq(ar_pci->pdev->irq + i,
+				  ath10k_pci_per_engine_handler,
+				  IRQF_SHARED, "ath10k_pci", ar);
+		if (ret) {
+			ath10k_warn("request_irq(%d) failed %d\n",
+				    ar_pci->pdev->irq + i, ret);
+
+			for (; i >= MSI_ASSIGN_CE_INITIAL; i--)
+				free_irq(ar_pci->pdev->irq, ar);
+
+			pci_disable_msi(ar_pci->pdev);
+			return ret;
+		}
+	}
+
+	ath10k_info("MSI-X interrupt handling (%d intrs)\n", num);
+	return 0;
+}
+
+static int ath10k_pci_start_intr_msi(struct ath10k *ar)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	int ret;
+
+	ret = pci_enable_msi(ar_pci->pdev);
+	if (ret < 0)
+		return ret;
+
+	ret = request_irq(ar_pci->pdev->irq,
+			  ath10k_pci_interrupt_handler,
+			  IRQF_SHARED, "ath10k_pci", ar);
+	if (ret < 0) {
+		pci_disable_msi(ar_pci->pdev);
+		return ret;
+	}
+
+	ath10k_info("MSI interrupt handling\n");
+	return 0;
+}
+
+static int ath10k_pci_start_intr_legacy(struct ath10k *ar)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	int ret;
+
+	ret = request_irq(ar_pci->pdev->irq,
+			  ath10k_pci_interrupt_handler,
+			  IRQF_SHARED, "ath10k_pci", ar);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * Make sure to wake the Target before enabling Legacy
+	 * Interrupt.
+	 */
+	iowrite32(PCIE_SOC_WAKE_V_MASK,
+		  ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS +
+		  PCIE_SOC_WAKE_ADDRESS);
+
+	ath10k_pci_wait(ar);
+
+	/*
+	 * A potential race occurs here: The CORE_BASE write
+	 * depends on target correctly decoding AXI address but
+	 * host won't know when target writes BAR to CORE_CTRL.
+	 * This write might get lost if target has NOT written BAR.
+	 * For now, fix the race by repeating the write in below
+	 * synchronization checking.
+	 */
+	iowrite32(PCIE_INTR_FIRMWARE_MASK |
+		  PCIE_INTR_CE_MASK_ALL,
+		  ar_pci->mem + (SOC_CORE_BASE_ADDRESS |
+				 PCIE_INTR_ENABLE_ADDRESS));
+	iowrite32(PCIE_SOC_WAKE_RESET,
+		  ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS +
+		  PCIE_SOC_WAKE_ADDRESS);
+
+	ath10k_info("legacy interrupt handling\n");
+	return 0;
+}
+
+static int ath10k_pci_start_intr(struct ath10k *ar)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	int num = MSI_NUM_REQUEST;
+	int ret;
+	int i;
+
+	tasklet_init(&ar_pci->intr_tq, ath10k_pci_tasklet, (unsigned long) ar);
+	tasklet_init(&ar_pci->msi_fw_err, ath10k_msi_err_tasklet,
+		     (unsigned long) ar);
+
+	for (i = 0; i < CE_COUNT; i++) {
+		ar_pci->pipe_info[i].ar_pci = ar_pci;
+		tasklet_init(&ar_pci->pipe_info[i].intr,
+			     ath10k_pci_ce_tasklet,
+			     (unsigned long)&ar_pci->pipe_info[i]);
+	}
+
+	if (!test_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features))
+		num = 1;
+
+	if (num > 1) {
+		ret = ath10k_pci_start_intr_msix(ar, num);
+		if (ret == 0)
+			goto exit;
+
+		ath10k_warn("MSI-X didn't succeed (%d), trying MSI\n", ret);
+		num = 1;
+	}
+
+	if (num == 1) {
+		ret = ath10k_pci_start_intr_msi(ar);
+		if (ret == 0)
+			goto exit;
+
+		ath10k_warn("MSI didn't succeed (%d), trying legacy INTR\n",
+			    ret);
+		num = 0;
+	}
+
+	ret = ath10k_pci_start_intr_legacy(ar);
+
+exit:
+	ar_pci->num_msi_intrs = num;
+	ar_pci->ce_count = CE_COUNT;
+	return ret;
+}
+
+static void ath10k_pci_stop_intr(struct ath10k *ar)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	int i;
+
+	/* There's at least one interrupt irregardless whether its legacy INTR
+	 * or MSI or MSI-X */
+	for (i = 0; i < max(1, ar_pci->num_msi_intrs); i++)
+		free_irq(ar_pci->pdev->irq + i, ar);
+
+	if (ar_pci->num_msi_intrs > 0)
+		pci_disable_msi(ar_pci->pdev);
+}
+
+static int ath10k_pci_reset_target(struct ath10k *ar)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	int wait_limit = 300; /* 3 sec */
+
+	/* Wait for Target to finish initialization before we proceed. */
+	iowrite32(PCIE_SOC_WAKE_V_MASK,
+		  ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS +
+		  PCIE_SOC_WAKE_ADDRESS);
+
+	ath10k_pci_wait(ar);
+
+	while (wait_limit-- &&
+	       !(ioread32(ar_pci->mem + FW_INDICATOR_ADDRESS) &
+		 FW_IND_INITIALIZED)) {
+		if (ar_pci->num_msi_intrs == 0)
+			/* Fix potential race by repeating CORE_BASE writes */
+			iowrite32(PCIE_INTR_FIRMWARE_MASK |
+				  PCIE_INTR_CE_MASK_ALL,
+				  ar_pci->mem + (SOC_CORE_BASE_ADDRESS |
+						 PCIE_INTR_ENABLE_ADDRESS));
+		mdelay(10);
+	}
+
+	if (wait_limit < 0) {
+		ath10k_err("Target stalled\n");
+		iowrite32(PCIE_SOC_WAKE_RESET,
+			  ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS +
+			  PCIE_SOC_WAKE_ADDRESS);
+		return -EIO;
+	}
+
+	iowrite32(PCIE_SOC_WAKE_RESET,
+		  ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS +
+		  PCIE_SOC_WAKE_ADDRESS);
+
+	return 0;
+}
+
+static void ath10k_pci_device_reset(struct ath10k_pci *ar_pci)
+{
+	struct ath10k *ar = ar_pci->ar;
+	void __iomem *mem = ar_pci->mem;
+	int i;
+	u32 val;
+
+	if (!SOC_GLOBAL_RESET_ADDRESS)
+		return;
+
+	if (!mem)
+		return;
+
+	ath10k_pci_reg_write32(mem, PCIE_SOC_WAKE_ADDRESS,
+			       PCIE_SOC_WAKE_V_MASK);
+	for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) {
+		if (ath10k_pci_target_is_awake(ar))
+			break;
+		msleep(1);
+	}
+
+	/* Put Target, including PCIe, into RESET. */
+	val = ath10k_pci_reg_read32(mem, SOC_GLOBAL_RESET_ADDRESS);
+	val |= 1;
+	ath10k_pci_reg_write32(mem, SOC_GLOBAL_RESET_ADDRESS, val);
+
+	for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) {
+		if (ath10k_pci_reg_read32(mem, RTC_STATE_ADDRESS) &
+					  RTC_STATE_COLD_RESET_MASK)
+			break;
+		msleep(1);
+	}
+
+	/* Pull Target, including PCIe, out of RESET. */
+	val &= ~1;
+	ath10k_pci_reg_write32(mem, SOC_GLOBAL_RESET_ADDRESS, val);
+
+	for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) {
+		if (!(ath10k_pci_reg_read32(mem, RTC_STATE_ADDRESS) &
+					    RTC_STATE_COLD_RESET_MASK))
+			break;
+		msleep(1);
+	}
+
+	ath10k_pci_reg_write32(mem, PCIE_SOC_WAKE_ADDRESS, PCIE_SOC_WAKE_RESET);
+}
+
+static void ath10k_pci_dump_features(struct ath10k_pci *ar_pci)
+{
+	int i;
+
+	for (i = 0; i < ATH10K_PCI_FEATURE_COUNT; i++) {
+		if (!test_bit(i, ar_pci->features))
+			continue;
+
+		switch (i) {
+		case ATH10K_PCI_FEATURE_MSI_X:
+			ath10k_dbg(ATH10K_DBG_PCI, "device supports MSI-X\n");
+			break;
+		case ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND:
+			ath10k_dbg(ATH10K_DBG_PCI, "QCA988X_1.0 workaround enabled\n");
+			break;
+		}
+	}
+}
+
+static int ath10k_pci_probe(struct pci_dev *pdev,
+			    const struct pci_device_id *pci_dev)
+{
+	void __iomem *mem;
+	int ret = 0;
+	struct ath10k *ar;
+	struct ath10k_pci *ar_pci;
+	u32 lcr_val;
+
+	ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
+
+	ar_pci = kzalloc(sizeof(*ar_pci), GFP_KERNEL);
+	if (ar_pci == NULL)
+		return -ENOMEM;
+
+	ar_pci->pdev = pdev;
+	ar_pci->dev = &pdev->dev;
+
+	switch (pci_dev->device) {
+	case QCA988X_1_0_DEVICE_ID:
+		set_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features);
+		break;
+	case QCA988X_2_0_DEVICE_ID:
+		set_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features);
+		break;
+	default:
+		ret = -ENODEV;
+		ath10k_err("Unkown device ID: %d\n", pci_dev->device);
+		goto err_ar_pci;
+	}
+
+	ath10k_pci_dump_features(ar_pci);
+
+	ar = ath10k_core_create(ar_pci, ar_pci->dev, ATH10K_BUS_PCI,
+				&ath10k_pci_hif_ops);
+	if (!ar) {
+		ath10k_err("ath10k_core_create failed!\n");
+		ret = -EINVAL;
+		goto err_ar_pci;
+	}
+
+	/* Enable QCA988X_1.0 HW workarounds */
+	if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features))
+		spin_lock_init(&ar_pci->hw_v1_workaround_lock);
+
+	ar_pci->ar = ar;
+	ar_pci->fw_indicator_address = FW_INDICATOR_ADDRESS;
+	atomic_set(&ar_pci->keep_awake_count, 0);
+
+	pci_set_drvdata(pdev, ar);
+
+	/*
+	 * Without any knowledge of the Host, the Target may have been reset or
+	 * power cycled and its Config Space may no longer reflect the PCI
+	 * address space that was assigned earlier by the PCI infrastructure.
+	 * Refresh it now.
+	 */
+	ret = pci_assign_resource(pdev, BAR_NUM);
+	if (ret) {
+		ath10k_err("cannot assign PCI space: %d\n", ret);
+		goto err_ar;
+	}
+
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		ath10k_err("cannot enable PCI device: %d\n", ret);
+		goto err_ar;
+	}
+
+	/* Request MMIO resources */
+	ret = pci_request_region(pdev, BAR_NUM, "ath");
+	if (ret) {
+		ath10k_err("PCI MMIO reservation error: %d\n", ret);
+		goto err_device;
+	}
+
+	/*
+	 * Target structures have a limit of 32 bit DMA pointers.
+	 * DMA pointers can be wider than 32 bits by default on some systems.
+	 */
+	ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+	if (ret) {
+		ath10k_err("32-bit DMA not available: %d\n", ret);
+		goto err_region;
+	}
+
+	ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+	if (ret) {
+		ath10k_err("cannot enable 32-bit consistent DMA\n");
+		goto err_region;
+	}
+
+	/* Set bus master bit in PCI_COMMAND to enable DMA */
+	pci_set_master(pdev);
+
+	/*
+	 * Temporary FIX: disable ASPM
+	 * Will be removed after the OTP is programmed
+	 */
+	pci_read_config_dword(pdev, 0x80, &lcr_val);
+	pci_write_config_dword(pdev, 0x80, (lcr_val & 0xffffff00));
+
+	/* Arrange for access to Target SoC registers. */
+	mem = pci_iomap(pdev, BAR_NUM, 0);
+	if (!mem) {
+		ath10k_err("PCI iomap error\n");
+		ret = -EIO;
+		goto err_master;
+	}
+
+	ar_pci->mem = mem;
+
+	spin_lock_init(&ar_pci->ce_lock);
+
+	ar_pci->cacheline_sz = dma_get_cache_alignment();
+
+	ret = ath10k_pci_start_intr(ar);
+	if (ret) {
+		ath10k_err("could not start interrupt handling (%d)\n", ret);
+		goto err_iomap;
+	}
+
+	/*
+	 * Bring the target up cleanly.
+	 *
+	 * The target may be in an undefined state with an AUX-powered Target
+	 * and a Host in WoW mode. If the Host crashes, loses power, or is
+	 * restarted (without unloading the driver) then the Target is left
+	 * (aux) powered and running. On a subsequent driver load, the Target
+	 * is in an unexpected state. We try to catch that here in order to
+	 * reset the Target and retry the probe.
+	 */
+	ath10k_pci_device_reset(ar_pci);
+
+	ret = ath10k_pci_reset_target(ar);
+	if (ret)
+		goto err_intr;
+
+	if (ath10k_target_ps) {
+		ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save enabled\n");
+	} else {
+		/* Force AWAKE forever */
+		ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save disabled\n");
+		ath10k_do_pci_wake(ar);
+	}
+
+	ret = ath10k_pci_ce_init(ar);
+	if (ret)
+		goto err_intr;
+
+	ret = ath10k_pci_init_config(ar);
+	if (ret)
+		goto err_ce;
+
+	ret = ath10k_pci_wake_target_cpu(ar);
+	if (ret) {
+		ath10k_err("could not wake up target CPU (%d)\n", ret);
+		goto err_ce;
+	}
+
+	ret = ath10k_core_register(ar);
+	if (ret) {
+		ath10k_err("could not register driver core (%d)\n", ret);
+		goto err_ce;
+	}
+
+	return 0;
+
+err_ce:
+	ath10k_pci_ce_deinit(ar);
+err_intr:
+	ath10k_pci_stop_intr(ar);
+err_iomap:
+	pci_iounmap(pdev, mem);
+err_master:
+	pci_clear_master(pdev);
+err_region:
+	pci_release_region(pdev, BAR_NUM);
+err_device:
+	pci_disable_device(pdev);
+err_ar:
+	pci_set_drvdata(pdev, NULL);
+	ath10k_core_destroy(ar);
+err_ar_pci:
+	/* call HIF PCI free here */
+	kfree(ar_pci);
+
+	return ret;
+}
+
+static void ath10k_pci_remove(struct pci_dev *pdev)
+{
+	struct ath10k *ar = pci_get_drvdata(pdev);
+	struct ath10k_pci *ar_pci;
+
+	ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
+
+	if (!ar)
+		return;
+
+	ar_pci = ath10k_pci_priv(ar);
+
+	if (!ar_pci)
+		return;
+
+	tasklet_kill(&ar_pci->msi_fw_err);
+
+	ath10k_core_unregister(ar);
+	ath10k_pci_stop_intr(ar);
+
+	pci_set_drvdata(pdev, NULL);
+	pci_iounmap(pdev, ar_pci->mem);
+	pci_release_region(pdev, BAR_NUM);
+	pci_clear_master(pdev);
+	pci_disable_device(pdev);
+
+	ath10k_core_destroy(ar);
+	kfree(ar_pci);
+}
+
+#if defined(CONFIG_PM_SLEEP)
+
+#define ATH10K_PCI_PM_CONTROL 0x44
+
+static int ath10k_pci_suspend(struct device *device)
+{
+	struct pci_dev *pdev = to_pci_dev(device);
+	struct ath10k *ar = pci_get_drvdata(pdev);
+	struct ath10k_pci *ar_pci;
+	u32 val;
+	int ret, retval;
+
+	ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
+
+	if (!ar)
+		return -ENODEV;
+
+	ar_pci = ath10k_pci_priv(ar);
+	if (!ar_pci)
+		return -ENODEV;
+
+	if (ath10k_core_target_suspend(ar))
+		return -EBUSY;
+
+	ret = wait_event_interruptible_timeout(ar->event_queue,
+						ar->is_target_paused == true,
+						1 * HZ);
+	if (ret < 0) {
+		ath10k_warn("suspend interrupted (%d)\n", ret);
+		retval = ret;
+		goto resume;
+	} else if (ret == 0) {
+		ath10k_warn("suspend timed out - target pause event never came\n");
+		retval = EIO;
+		goto resume;
+	}
+
+	/*
+	 * reset is_target_paused and host can check that in next time,
+	 * or it will always be TRUE and host just skip the waiting
+	 * condition, it causes target assert due to host already
+	 * suspend
+	 */
+	ar->is_target_paused = false;
+
+	pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val);
+
+	if ((val & 0x000000ff) != 0x3) {
+		pci_save_state(pdev);
+		pci_disable_device(pdev);
+		pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL,
+				       (val & 0xffffff00) | 0x03);
+	}
+
+	return 0;
+resume:
+	ret = ath10k_core_target_resume(ar);
+	if (ret)
+		ath10k_warn("could not resume (%d)\n", ret);
+
+	return retval;
+}
+
+static int ath10k_pci_resume(struct device *device)
+{
+	struct pci_dev *pdev = to_pci_dev(device);
+	struct ath10k *ar = pci_get_drvdata(pdev);
+	struct ath10k_pci *ar_pci;
+	int ret;
+	u32 val;
+
+	ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
+
+	if (!ar)
+		return -ENODEV;
+	ar_pci = ath10k_pci_priv(ar);
+
+	if (!ar_pci)
+		return -ENODEV;
+
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		ath10k_warn("cannot enable PCI device: %d\n", ret);
+		return ret;
+	}
+
+	pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val);
+
+	if ((val & 0x000000ff) != 0) {
+		pci_restore_state(pdev);
+		pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL,
+				       val & 0xffffff00);
+		/*
+		 * Suspend/Resume resets the PCI configuration space,
+		 * so we have to re-disable the RETRY_TIMEOUT register (0x41)
+		 * to keep PCI Tx retries from interfering with C3 CPU state
+		 */
+		pci_read_config_dword(pdev, 0x40, &val);
+
+		if ((val & 0x0000ff00) != 0)
+			pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
+	}
+
+	ret = ath10k_core_target_resume(ar);
+	if (ret)
+		ath10k_warn("target resume failed: %d\n", ret);
+
+	return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(ath10k_dev_pm_ops,
+			 ath10k_pci_suspend,
+			 ath10k_pci_resume);
+
+#define ATH10K_PCI_PM_OPS (&ath10k_dev_pm_ops)
+
+#else
+
+#define ATH10K_PCI_PM_OPS NULL
+
+#endif /* CONFIG_PM_SLEEP */
+
+MODULE_DEVICE_TABLE(pci, ath10k_pci_id_table);
+
+static struct pci_driver ath10k_pci_driver = {
+	.name = "ath10k_pci",
+	.id_table = ath10k_pci_id_table,
+	.probe = ath10k_pci_probe,
+	.remove = ath10k_pci_remove,
+	.driver.pm = ATH10K_PCI_PM_OPS,
+};
+
+static int __init ath10k_pci_init(void)
+{
+	int ret;
+
+	ret = pci_register_driver(&ath10k_pci_driver);
+	if (ret)
+		ath10k_err("pci_register_driver failed [%d]\n", ret);
+
+	return ret;
+}
+module_init(ath10k_pci_init);
+
+static void __exit ath10k_pci_exit(void)
+{
+	pci_unregister_driver(&ath10k_pci_driver);
+}
+
+module_exit(ath10k_pci_exit);
+
+MODULE_AUTHOR("Qualcomm Atheros");
+MODULE_DESCRIPTION("Driver support for Atheros QCA988X PCIe devices");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_FIRMWARE(QCA988X_HW_1_0_FW_DIR "/" QCA988X_HW_1_0_FW_FILE);
+MODULE_FIRMWARE(QCA988X_HW_1_0_FW_DIR "/" QCA988X_HW_1_0_OTP_FILE);
+MODULE_FIRMWARE(QCA988X_HW_1_0_FW_DIR "/" QCA988X_HW_1_0_BOARD_DATA_FILE);
+MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_FW_FILE);
+MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_OTP_FILE);
+MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_BOARD_DATA_FILE);


^ permalink raw reply related

* [PATCH v2 20/29] ath10k: add pci.h
From: Kalle Valo @ 2013-06-01 11:26 UTC (permalink / raw)
  To: linux-wireless; +Cc: ath10k-devel
In-Reply-To: <20130601111820.32443.46794.stgit@localhost6.localdomain6>

Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
---
 drivers/net/wireless/ath/ath10k/pci.h |  355 +++++++++++++++++++++++++++++++++
 1 file changed, 355 insertions(+)
 create mode 100644 drivers/net/wireless/ath/ath10k/pci.h

diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h
new file mode 100644
index 0000000..d2a055a
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/pci.h
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * 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 _PCI_H_
+#define _PCI_H_
+
+#include <linux/interrupt.h>
+
+#include "hw.h"
+#include "ce.h"
+
+/* FW dump area */
+#define REG_DUMP_COUNT_QCA988X 60
+
+/*
+ * maximum number of bytes that can be handled atomically by DiagRead/DiagWrite
+ */
+#define DIAG_TRANSFER_LIMIT 2048
+
+/*
+ * maximum number of bytes that can be
+ * handled atomically by DiagRead/DiagWrite
+ */
+#define DIAG_TRANSFER_LIMIT 2048
+
+struct bmi_xfer {
+	struct completion done;
+	bool wait_for_resp;
+	u32 resp_len;
+};
+
+struct ath10k_pci_compl {
+	struct list_head list;
+	int send_or_recv;
+	struct ce_state *ce_state;
+	struct hif_ce_pipe_info *pipe_info;
+	void *transfer_context;
+	unsigned int nbytes;
+	unsigned int transfer_id;
+	unsigned int flags;
+};
+
+/* compl_state.send_or_recv */
+#define HIF_CE_COMPLETE_FREE 0
+#define HIF_CE_COMPLETE_SEND 1
+#define HIF_CE_COMPLETE_RECV 2
+
+/*
+ * PCI-specific Target state
+ *
+ * NOTE: Structure is shared between Host software and Target firmware!
+ *
+ * Much of this may be of interest to the Host so
+ * HOST_INTEREST->hi_interconnect_state points here
+ * (and all members are 32-bit quantities in order to
+ * facilitate Host access). In particular, Host software is
+ * required to initialize pipe_cfg_addr and svc_to_pipe_map.
+ */
+struct pcie_state {
+	/* Pipe configuration Target address */
+	/* NB: ce_pipe_config[CE_COUNT] */
+	u32 pipe_cfg_addr;
+
+	/* Service to pipe map Target address */
+	/* NB: service_to_pipe[PIPE_TO_CE_MAP_CN] */
+	u32 svc_to_pipe_map;
+
+	/* number of MSI interrupts requested */
+	u32 msi_requested;
+
+	/* number of MSI interrupts granted */
+	u32 msi_granted;
+
+	/* Message Signalled Interrupt address */
+	u32 msi_addr;
+
+	/* Base data */
+	u32 msi_data;
+
+	/*
+	 * Data for firmware interrupt;
+	 * MSI data for other interrupts are
+	 * in various SoC registers
+	 */
+	u32 msi_fw_intr_data;
+
+	/* PCIE_PWR_METHOD_* */
+	u32 power_mgmt_method;
+
+	/* PCIE_CONFIG_FLAG_* */
+	u32 config_flags;
+};
+
+/* PCIE_CONFIG_FLAG definitions */
+#define PCIE_CONFIG_FLAG_ENABLE_L1  0x0000001
+
+/* Host software's Copy Engine configuration. */
+#define CE_ATTR_FLAGS 0
+
+/*
+ * Configuration information for a Copy Engine pipe.
+ * Passed from Host to Target during startup (one per CE).
+ *
+ * NOTE: Structure is shared between Host software and Target firmware!
+ */
+struct ce_pipe_config {
+	u32 pipenum;
+	u32 pipedir;
+	u32 nentries;
+	u32 nbytes_max;
+	u32 flags;
+	u32 reserved;
+};
+
+/*
+ * Directions for interconnect pipe configuration.
+ * These definitions may be used during configuration and are shared
+ * between Host and Target.
+ *
+ * Pipe Directions are relative to the Host, so PIPEDIR_IN means
+ * "coming IN over air through Target to Host" as with a WiFi Rx operation.
+ * Conversely, PIPEDIR_OUT means "going OUT from Host through Target over air"
+ * as with a WiFi Tx operation. This is somewhat awkward for the "middle-man"
+ * Target since things that are "PIPEDIR_OUT" are coming IN to the Target
+ * over the interconnect.
+ */
+#define PIPEDIR_NONE    0
+#define PIPEDIR_IN      1  /* Target-->Host, WiFi Rx direction */
+#define PIPEDIR_OUT     2  /* Host->Target, WiFi Tx direction */
+#define PIPEDIR_INOUT   3  /* bidirectional */
+
+/* Establish a mapping between a service/direction and a pipe. */
+struct service_to_pipe {
+	u32 service_id;
+	u32 pipedir;
+	u32 pipenum;
+};
+
+enum ath10k_pci_features {
+	ATH10K_PCI_FEATURE_MSI_X		= 0,
+	ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND	= 1,
+
+	/* keep last */
+	ATH10K_PCI_FEATURE_COUNT
+};
+
+/* Per-pipe state. */
+struct hif_ce_pipe_info {
+	/* Handle of underlying Copy Engine */
+	struct ce_state *ce_hdl;
+
+	/* Our pipe number; facilitiates use of pipe_info ptrs. */
+	u8 pipe_num;
+
+	/* Convenience back pointer to hif_ce_state. */
+	struct ath10k *hif_ce_state;
+
+	size_t buf_sz;
+
+	/* protects compl_free and num_send_allowed */
+	spinlock_t pipe_lock;
+
+	/* List of free CE completion slots */
+	struct list_head compl_free;
+
+	/* Limit the number of outstanding send requests. */
+	int num_sends_allowed;
+
+	struct ath10k_pci *ar_pci;
+	struct tasklet_struct intr;
+};
+
+struct ath10k_pci {
+	struct pci_dev *pdev;
+	struct device *dev;
+	struct ath10k *ar;
+	void __iomem *mem;
+	int cacheline_sz;
+
+	DECLARE_BITMAP(features, ATH10K_PCI_FEATURE_COUNT);
+
+	/*
+	 * Number of MSI interrupts granted, 0 --> using legacy PCI line
+	 * interrupts.
+	 */
+	int num_msi_intrs;
+
+	struct tasklet_struct intr_tq;
+	struct tasklet_struct msi_fw_err;
+
+	/* Number of Copy Engines supported */
+	unsigned int ce_count;
+
+	int started;
+
+	atomic_t keep_awake_count;
+	bool verified_awake;
+
+	/* List of CE completions to be processed */
+	struct list_head compl_process;
+
+	/* protects compl_processing and compl_process */
+	spinlock_t compl_lock;
+
+	bool compl_processing;
+
+	struct hif_ce_pipe_info pipe_info[CE_COUNT_MAX];
+
+	struct ath10k_hif_cb msg_callbacks_current;
+
+	/* Target address used to signal a pending firmware event */
+	u32 fw_indicator_address;
+
+	/* Copy Engine used for Diagnostic Accesses */
+	struct ce_state *ce_diag;
+
+	/* FIXME: document what this really protects */
+	spinlock_t ce_lock;
+
+	/* Map CE id to ce_state */
+	struct ce_state *ce_id_to_state[CE_COUNT_MAX];
+
+	/* makes sure that dummy reads are atomic */
+	spinlock_t hw_v1_workaround_lock;
+};
+
+static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar)
+{
+	return ar->hif.priv;
+}
+
+static inline u32 ath10k_pci_reg_read32(void __iomem *mem, u32 addr)
+{
+	return ioread32(mem + PCIE_LOCAL_BASE_ADDRESS + addr);
+}
+
+static inline void ath10k_pci_reg_write32(void __iomem *mem, u32 addr, u32 val)
+{
+	iowrite32(val, mem + PCIE_LOCAL_BASE_ADDRESS + addr);
+}
+
+#define ATH_PCI_RESET_WAIT_MAX 10 /* ms */
+#define PCIE_WAKE_TIMEOUT 5000	/* 5ms */
+
+#define BAR_NUM 0
+
+#define CDC_WAR_MAGIC_STR   0xceef0000
+#define CDC_WAR_DATA_CE     4
+
+/*
+ * TODO: Should be a function call specific to each Target-type.
+ * This convoluted macro converts from Target CPU Virtual Address Space to CE
+ * Address Space. As part of this process, we conservatively fetch the current
+ * PCIE_BAR. MOST of the time, this should match the upper bits of PCI space
+ * for this device; but that's not guaranteed.
+ */
+#define TARG_CPU_SPACE_TO_CE_SPACE(ar, pci_addr, addr)			\
+	(((ioread32((pci_addr)+(SOC_CORE_BASE_ADDRESS|			\
+	  CORE_CTRL_ADDRESS)) & 0x7ff) << 21) |				\
+	 0x100000 | ((addr) & 0xfffff))
+
+/* Wait up to this many Ms for a Diagnostic Access CE operation to complete */
+#define DIAG_ACCESS_CE_TIMEOUT_MS 10
+
+/*
+ * This API allows the Host to access Target registers directly
+ * and relatively efficiently over PCIe.
+ * This allows the Host to avoid extra overhead associated with
+ * sending a message to firmware and waiting for a response message
+ * from firmware, as is done on other interconnects.
+ *
+ * Yet there is some complexity with direct accesses because the
+ * Target's power state is not known a priori. The Host must issue
+ * special PCIe reads/writes in order to explicitly wake the Target
+ * and to verify that it is awake and will remain awake.
+ *
+ * Usage:
+ *
+ *   Use ath10k_pci_read32 and ath10k_pci_write32 to access Target space.
+ *   These calls must be bracketed by ath10k_pci_wake and
+ *   ath10k_pci_sleep.  A single BEGIN/END pair is adequate for
+ *   multiple READ/WRITE operations.
+ *
+ *   Use ath10k_pci_wake to put the Target in a state in
+ *   which it is legal for the Host to directly access it. This
+ *   may involve waking the Target from a low power state, which
+ *   may take up to 2Ms!
+ *
+ *   Use ath10k_pci_sleep to tell the Target that as far as
+ *   this code path is concerned, it no longer needs to remain
+ *   directly accessible.  BEGIN/END is under a reference counter;
+ *   multiple code paths may issue BEGIN/END on a single targid.
+ */
+static inline void ath10k_pci_write32(struct ath10k *ar, u32 offset,
+				      u32 value)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+	void __iomem *addr = ar_pci->mem;
+
+	if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features)) {
+		unsigned long irq_flags;
+
+		spin_lock_irqsave(&ar_pci->hw_v1_workaround_lock, irq_flags);
+
+		ioread32(addr+offset+4); /* 3rd read prior to write */
+		ioread32(addr+offset+4); /* 2nd read prior to write */
+		ioread32(addr+offset+4); /* 1st read prior to write */
+		iowrite32(value, addr+offset);
+
+		spin_unlock_irqrestore(&ar_pci->hw_v1_workaround_lock,
+				       irq_flags);
+	} else {
+		iowrite32(value, addr+offset);
+	}
+}
+
+static inline u32 ath10k_pci_read32(struct ath10k *ar, u32 offset)
+{
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+	return ioread32(ar_pci->mem + offset);
+}
+
+extern unsigned int ath10k_target_ps;
+
+void ath10k_do_pci_wake(struct ath10k *ar);
+void ath10k_do_pci_sleep(struct ath10k *ar);
+
+static inline void ath10k_pci_wake(struct ath10k *ar)
+{
+	if (ath10k_target_ps)
+		ath10k_do_pci_wake(ar);
+}
+
+static inline void ath10k_pci_sleep(struct ath10k *ar)
+{
+	if (ath10k_target_ps)
+		ath10k_do_pci_sleep(ar);
+}
+
+#endif /* _PCI_H_ */


^ permalink raw reply related

* [PATCH v2 21/29] ath10k: add rx_desc.h
From: Kalle Valo @ 2013-06-01 11:26 UTC (permalink / raw)
  To: linux-wireless; +Cc: ath10k-devel
In-Reply-To: <20130601111820.32443.46794.stgit@localhost6.localdomain6>

Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
---
 drivers/net/wireless/ath/ath10k/rx_desc.h |  990 +++++++++++++++++++++++++++++
 1 file changed, 990 insertions(+)
 create mode 100644 drivers/net/wireless/ath/ath10k/rx_desc.h

diff --git a/drivers/net/wireless/ath/ath10k/rx_desc.h b/drivers/net/wireless/ath/ath10k/rx_desc.h
new file mode 100644
index 0000000..bfec6c8
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/rx_desc.h
@@ -0,0 +1,990 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * 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 _RX_DESC_H_
+#define _RX_DESC_H_
+
+enum rx_attention_flags {
+	RX_ATTENTION_FLAGS_FIRST_MPDU          = 1 << 0,
+	RX_ATTENTION_FLAGS_LAST_MPDU           = 1 << 1,
+	RX_ATTENTION_FLAGS_MCAST_BCAST         = 1 << 2,
+	RX_ATTENTION_FLAGS_PEER_IDX_INVALID    = 1 << 3,
+	RX_ATTENTION_FLAGS_PEER_IDX_TIMEOUT    = 1 << 4,
+	RX_ATTENTION_FLAGS_POWER_MGMT          = 1 << 5,
+	RX_ATTENTION_FLAGS_NON_QOS             = 1 << 6,
+	RX_ATTENTION_FLAGS_NULL_DATA           = 1 << 7,
+	RX_ATTENTION_FLAGS_MGMT_TYPE           = 1 << 8,
+	RX_ATTENTION_FLAGS_CTRL_TYPE           = 1 << 9,
+	RX_ATTENTION_FLAGS_MORE_DATA           = 1 << 10,
+	RX_ATTENTION_FLAGS_EOSP                = 1 << 11,
+	RX_ATTENTION_FLAGS_U_APSD_TRIGGER      = 1 << 12,
+	RX_ATTENTION_FLAGS_FRAGMENT            = 1 << 13,
+	RX_ATTENTION_FLAGS_ORDER               = 1 << 14,
+	RX_ATTENTION_FLAGS_CLASSIFICATION      = 1 << 15,
+	RX_ATTENTION_FLAGS_OVERFLOW_ERR        = 1 << 16,
+	RX_ATTENTION_FLAGS_MSDU_LENGTH_ERR     = 1 << 17,
+	RX_ATTENTION_FLAGS_TCP_UDP_CHKSUM_FAIL = 1 << 18,
+	RX_ATTENTION_FLAGS_IP_CHKSUM_FAIL      = 1 << 19,
+	RX_ATTENTION_FLAGS_SA_IDX_INVALID      = 1 << 20,
+	RX_ATTENTION_FLAGS_DA_IDX_INVALID      = 1 << 21,
+	RX_ATTENTION_FLAGS_SA_IDX_TIMEOUT      = 1 << 22,
+	RX_ATTENTION_FLAGS_DA_IDX_TIMEOUT      = 1 << 23,
+	RX_ATTENTION_FLAGS_ENCRYPT_REQUIRED    = 1 << 24,
+	RX_ATTENTION_FLAGS_DIRECTED            = 1 << 25,
+	RX_ATTENTION_FLAGS_BUFFER_FRAGMENT     = 1 << 26,
+	RX_ATTENTION_FLAGS_MPDU_LENGTH_ERR     = 1 << 27,
+	RX_ATTENTION_FLAGS_TKIP_MIC_ERR        = 1 << 28,
+	RX_ATTENTION_FLAGS_DECRYPT_ERR         = 1 << 29,
+	RX_ATTENTION_FLAGS_FCS_ERR             = 1 << 30,
+	RX_ATTENTION_FLAGS_MSDU_DONE           = 1 << 31,
+};
+
+struct rx_attention {
+	__le32 flags; /* %RX_ATTENTION_FLAGS_ */
+} __packed;
+
+/*
+ * first_mpdu
+ *		Indicates the first MSDU of the PPDU.  If both first_mpdu
+ *		and last_mpdu are set in the MSDU then this is a not an
+ *		A-MPDU frame but a stand alone MPDU.  Interior MPDU in an
+ *		A-MPDU shall have both first_mpdu and last_mpdu bits set to
+ *		0.  The PPDU start status will only be valid when this bit
+ *		is set.
+ *
+ * last_mpdu
+ *		Indicates the last MSDU of the last MPDU of the PPDU.  The
+ *		PPDU end status will only be valid when this bit is set.
+ *
+ * mcast_bcast
+ *		Multicast / broadcast indicator.  Only set when the MAC
+ *		address 1 bit 0 is set indicating mcast/bcast and the BSSID
+ *		matches one of the 4 BSSID registers. Only set when
+ *		first_msdu is set.
+ *
+ * peer_idx_invalid
+ *		Indicates no matching entries within the the max search
+ *		count.  Only set when first_msdu is set.
+ *
+ * peer_idx_timeout
+ *		Indicates an unsuccessful search for the peer index due to
+ *		timeout.  Only set when first_msdu is set.
+ *
+ * power_mgmt
+ *		Power management bit set in the 802.11 header.  Only set
+ *		when first_msdu is set.
+ *
+ * non_qos
+ *		Set if packet is not a non-QoS data frame.  Only set when
+ *		first_msdu is set.
+ *
+ * null_data
+ *		Set if frame type indicates either null data or QoS null
+ *		data format.  Only set when first_msdu is set.
+ *
+ * mgmt_type
+ *		Set if packet is a management packet.  Only set when
+ *		first_msdu is set.
+ *
+ * ctrl_type
+ *		Set if packet is a control packet.  Only set when first_msdu
+ *		is set.
+ *
+ * more_data
+ *		Set if more bit in frame control is set.  Only set when
+ *		first_msdu is set.
+ *
+ * eosp
+ *		Set if the EOSP (end of service period) bit in the QoS
+ *		control field is set.  Only set when first_msdu is set.
+ *
+ * u_apsd_trigger
+ *		Set if packet is U-APSD trigger.  Key table will have bits
+ *		per TID to indicate U-APSD trigger.
+ *
+ * fragment
+ *		Indicates that this is an 802.11 fragment frame.  This is
+ *		set when either the more_frag bit is set in the frame
+ *		control or the fragment number is not zero.  Only set when
+ *		first_msdu is set.
+ *
+ * order
+ *		Set if the order bit in the frame control is set.  Only set
+ *		when first_msdu is set.
+ *
+ * classification
+ *		Indicates that this status has a corresponding MSDU that
+ *		requires FW processing.  The OLE will have classification
+ *		ring mask registers which will indicate the ring(s) for
+ *		packets and descriptors which need FW attention.
+ *
+ * overflow_err
+ *		PCU Receive FIFO does not have enough space to store the
+ *		full receive packet.  Enough space is reserved in the
+ *		receive FIFO for the status is written.  This MPDU remaining
+ *		packets in the PPDU will be filtered and no Ack response
+ *		will be transmitted.
+ *
+ * msdu_length_err
+ *		Indicates that the MSDU length from the 802.3 encapsulated
+ *		length field extends beyond the MPDU boundary.
+ *
+ * tcp_udp_chksum_fail
+ *		Indicates that the computed checksum (tcp_udp_chksum) did
+ *		not match the checksum in the TCP/UDP header.
+ *
+ * ip_chksum_fail
+ *		Indicates that the computed checksum did not match the
+ *		checksum in the IP header.
+ *
+ * sa_idx_invalid
+ *		Indicates no matching entry was found in the address search
+ *		table for the source MAC address.
+ *
+ * da_idx_invalid
+ *		Indicates no matching entry was found in the address search
+ *		table for the destination MAC address.
+ *
+ * sa_idx_timeout
+ *		Indicates an unsuccessful search for the source MAC address
+ *		due to the expiring of the search timer.
+ *
+ * da_idx_timeout
+ *		Indicates an unsuccessful search for the destination MAC
+ *		address due to the expiring of the search timer.
+ *
+ * encrypt_required
+ *		Indicates that this data type frame is not encrypted even if
+ *		the policy for this MPDU requires encryption as indicated in
+ *		the peer table key type.
+ *
+ * directed
+ *		MPDU is a directed packet which means that the RA matched
+ *		our STA addresses.  In proxySTA it means that the TA matched
+ *		an entry in our address search table with the corresponding
+ *		'no_ack' bit is the address search entry cleared.
+ *
+ * buffer_fragment
+ *		Indicates that at least one of the rx buffers has been
+ *		fragmented.  If set the FW should look at the rx_frag_info
+ *		descriptor described below.
+ *
+ * mpdu_length_err
+ *		Indicates that the MPDU was pre-maturely terminated
+ *		resulting in a truncated MPDU.  Don't trust the MPDU length
+ *		field.
+ *
+ * tkip_mic_err
+ *		Indicates that the MPDU Michael integrity check failed
+ *
+ * decrypt_err
+ *		Indicates that the MPDU decrypt integrity check failed
+ *
+ * fcs_err
+ *		Indicates that the MPDU FCS check failed
+ *
+ * msdu_done
+ *		If set indicates that the RX packet data, RX header data, RX
+ *		PPDU start descriptor, RX MPDU start/end descriptor, RX MSDU
+ *		start/end descriptors and RX Attention descriptor are all
+ *		valid.  This bit must be in the last octet of the
+ *		descriptor.
+ */
+
+struct rx_frag_info {
+	u8 ring0_more_count;
+	u8 ring1_more_count;
+	u8 ring2_more_count;
+	u8 ring3_more_count;
+} __packed;
+
+/*
+ * ring0_more_count
+ *		Indicates the number of more buffers associated with RX DMA
+ *		ring 0.  Field is filled in by the RX_DMA.
+ *
+ * ring1_more_count
+ *		Indicates the number of more buffers associated with RX DMA
+ *		ring 1. Field is filled in by the RX_DMA.
+ *
+ * ring2_more_count
+ *		Indicates the number of more buffers associated with RX DMA
+ *		ring 2. Field is filled in by the RX_DMA.
+ *
+ * ring3_more_count
+ *		Indicates the number of more buffers associated with RX DMA
+ *		ring 3. Field is filled in by the RX_DMA.
+ */
+
+enum htt_rx_mpdu_encrypt_type {
+	HTT_RX_MPDU_ENCRYPT_WEP40            = 0,
+	HTT_RX_MPDU_ENCRYPT_WEP104           = 1,
+	HTT_RX_MPDU_ENCRYPT_TKIP_WITHOUT_MIC = 2,
+	HTT_RX_MPDU_ENCRYPT_WEP128           = 3,
+	HTT_RX_MPDU_ENCRYPT_TKIP_WPA         = 4,
+	HTT_RX_MPDU_ENCRYPT_WAPI             = 5,
+	HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2     = 6,
+	HTT_RX_MPDU_ENCRYPT_NONE             = 7,
+};
+
+#define RX_MPDU_START_INFO0_PEER_IDX_MASK     0x000007ff
+#define RX_MPDU_START_INFO0_PEER_IDX_LSB      0
+#define RX_MPDU_START_INFO0_SEQ_NUM_MASK      0x0fff0000
+#define RX_MPDU_START_INFO0_SEQ_NUM_LSB       16
+#define RX_MPDU_START_INFO0_ENCRYPT_TYPE_MASK 0xf0000000
+#define RX_MPDU_START_INFO0_ENCRYPT_TYPE_LSB  28
+#define RX_MPDU_START_INFO0_FROM_DS           (1 << 11)
+#define RX_MPDU_START_INFO0_TO_DS             (1 << 12)
+#define RX_MPDU_START_INFO0_ENCRYPTED         (1 << 13)
+#define RX_MPDU_START_INFO0_RETRY             (1 << 14)
+#define RX_MPDU_START_INFO0_TXBF_H_INFO       (1 << 15)
+
+#define RX_MPDU_START_INFO1_TID_MASK 0xf0000000
+#define RX_MPDU_START_INFO1_TID_LSB  28
+#define RX_MPDU_START_INFO1_DIRECTED (1 << 16)
+
+struct rx_mpdu_start {
+	__le32 info0;
+	union {
+		struct {
+			__le32 pn31_0;
+			__le32 info1; /* %RX_MPDU_START_INFO1_ */
+		} __packed;
+		struct {
+			u8 pn[6];
+		} __packed;
+	} __packed;
+} __packed;
+
+/*
+ * peer_idx
+ *		The index of the address search table which associated with
+ *		the peer table entry corresponding to this MPDU.  Only valid
+ *		when first_msdu is set.
+ *
+ * fr_ds
+ *		Set if the from DS bit is set in the frame control.  Only
+ *		valid when first_msdu is set.
+ *
+ * to_ds
+ *		Set if the to DS bit is set in the frame control.  Only
+ *		valid when first_msdu is set.
+ *
+ * encrypted
+ *		Protected bit from the frame control.  Only valid when
+ *		first_msdu is set.
+ *
+ * retry
+ *		Retry bit from the frame control.  Only valid when
+ *		first_msdu is set.
+ *
+ * txbf_h_info
+ *		The MPDU data will contain H information.  Primarily used
+ *		for debug.
+ *
+ * seq_num
+ *		The sequence number from the 802.11 header.  Only valid when
+ *		first_msdu is set.
+ *
+ * encrypt_type
+ *		Indicates type of decrypt cipher used (as defined in the
+ *		peer table)
+ *		0: WEP40
+ *		1: WEP104
+ *		2: TKIP without MIC
+ *		3: WEP128
+ *		4: TKIP (WPA)
+ *		5: WAPI
+ *		6: AES-CCM (WPA2)
+ *		7: No cipher
+ *		Only valid when first_msdu_is set
+ *
+ * pn_31_0
+ *		Bits [31:0] of the PN number extracted from the IV field
+ *		WEP: IV = {key_id_octet, pn2, pn1, pn0}.  Only pn[23:0] is
+ *		valid.
+ *		TKIP: IV = {pn5, pn4, pn3, pn2, key_id_octet, pn0,
+ *		WEPSeed[1], pn1}.  Only pn[47:0] is valid.
+ *		AES-CCM: IV = {pn5, pn4, pn3, pn2, key_id_octet, 0x0, pn1,
+ *		pn0}.  Only pn[47:0] is valid.
+ *		WAPI: IV = {key_id_octet, 0x0, pn15, pn14, pn13, pn12, pn11,
+ *		pn10, pn9, pn8, pn7, pn6, pn5, pn4, pn3, pn2, pn1, pn0}.
+ *		The ext_wapi_pn[127:48] in the rx_msdu_misc descriptor and
+ *		pn[47:0] are valid.
+ *		Only valid when first_msdu is set.
+ *
+ * pn_47_32
+ *		Bits [47:32] of the PN number.   See description for
+ *		pn_31_0.  The remaining PN fields are in the rx_msdu_end
+ *		descriptor
+ *
+ * pn
+ *		Use this field to access the pn without worrying about
+ *		byte-order and bitmasking/bitshifting.
+ *
+ * directed
+ *		See definition in RX attention descriptor
+ *
+ * reserved_2
+ *		Reserved: HW should fill with zero.  FW should ignore.
+ *
+ * tid
+ *		The TID field in the QoS control field
+ */
+
+#define RX_MPDU_END_INFO0_RESERVED_0_MASK     0x00001fff
+#define RX_MPDU_END_INFO0_RESERVED_0_LSB      0
+#define RX_MPDU_END_INFO0_POST_DELIM_CNT_MASK 0x0fff0000
+#define RX_MPDU_END_INFO0_POST_DELIM_CNT_LSB  16
+#define RX_MPDU_END_INFO0_OVERFLOW_ERR        (1 << 13)
+#define RX_MPDU_END_INFO0_LAST_MPDU           (1 << 14)
+#define RX_MPDU_END_INFO0_POST_DELIM_ERR      (1 << 15)
+#define RX_MPDU_END_INFO0_MPDU_LENGTH_ERR     (1 << 28)
+#define RX_MPDU_END_INFO0_TKIP_MIC_ERR        (1 << 29)
+#define RX_MPDU_END_INFO0_DECRYPT_ERR         (1 << 30)
+#define RX_MPDU_END_INFO0_FCS_ERR             (1 << 31)
+
+struct rx_mpdu_end {
+	__le32 info0;
+} __packed;
+
+/*
+ * reserved_0
+ *		Reserved
+ *
+ * overflow_err
+ *		PCU Receive FIFO does not have enough space to store the
+ *		full receive packet.  Enough space is reserved in the
+ *		receive FIFO for the status is written.  This MPDU remaining
+ *		packets in the PPDU will be filtered and no Ack response
+ *		will be transmitted.
+ *
+ * last_mpdu
+ *		Indicates that this is the last MPDU of a PPDU.
+ *
+ * post_delim_err
+ *		Indicates that a delimiter FCS error occurred after this
+ *		MPDU before the next MPDU.  Only valid when last_msdu is
+ *		set.
+ *
+ * post_delim_cnt
+ *		Count of the delimiters after this MPDU.  This requires the
+ *		last MPDU to be held until all the EOF descriptors have been
+ *		received.  This may be inefficient in the future when
+ *		ML-MIMO is used.  Only valid when last_mpdu is set.
+ *
+ * mpdu_length_err
+ *		See definition in RX attention descriptor
+ *
+ * tkip_mic_err
+ *		See definition in RX attention descriptor
+ *
+ * decrypt_err
+ *		See definition in RX attention descriptor
+ *
+ * fcs_err
+ *		See definition in RX attention descriptor
+ */
+
+#define RX_MSDU_START_INFO0_MSDU_LENGTH_MASK    0x00003fff
+#define RX_MSDU_START_INFO0_MSDU_LENGTH_LSB     0
+#define RX_MSDU_START_INFO0_IP_OFFSET_MASK      0x000fc000
+#define RX_MSDU_START_INFO0_IP_OFFSET_LSB       14
+#define RX_MSDU_START_INFO0_RING_MASK_MASK      0x00f00000
+#define RX_MSDU_START_INFO0_RING_MASK_LSB       20
+#define RX_MSDU_START_INFO0_TCP_UDP_OFFSET_MASK 0x7f000000
+#define RX_MSDU_START_INFO0_TCP_UDP_OFFSET_LSB  24
+
+#define RX_MSDU_START_INFO1_MSDU_NUMBER_MASK    0x000000ff
+#define RX_MSDU_START_INFO1_MSDU_NUMBER_LSB     0
+#define RX_MSDU_START_INFO1_DECAP_FORMAT_MASK   0x00000300
+#define RX_MSDU_START_INFO1_DECAP_FORMAT_LSB    8
+#define RX_MSDU_START_INFO1_SA_IDX_MASK         0x07ff0000
+#define RX_MSDU_START_INFO1_SA_IDX_LSB          16
+#define RX_MSDU_START_INFO1_IPV4_PROTO          (1 << 10)
+#define RX_MSDU_START_INFO1_IPV6_PROTO          (1 << 11)
+#define RX_MSDU_START_INFO1_TCP_PROTO           (1 << 12)
+#define RX_MSDU_START_INFO1_UDP_PROTO           (1 << 13)
+#define RX_MSDU_START_INFO1_IP_FRAG             (1 << 14)
+#define RX_MSDU_START_INFO1_TCP_ONLY_ACK        (1 << 15)
+
+enum rx_msdu_decap_format {
+	RX_MSDU_DECAP_RAW           = 0,
+	RX_MSDU_DECAP_NATIVE_WIFI   = 1,
+	RX_MSDU_DECAP_ETHERNET2_DIX = 2,
+	RX_MSDU_DECAP_8023_SNAP_LLC = 3
+};
+
+struct rx_msdu_start {
+	__le32 info0; /* %RX_MSDU_START_INFO0_ */
+	__le32 flow_id_crc;
+	__le32 info1; /* %RX_MSDU_START_INFO1_ */
+} __packed;
+
+/*
+ * msdu_length
+ *		MSDU length in bytes after decapsulation.  This field is
+ *		still valid for MPDU frames without A-MSDU.  It still
+ *		represents MSDU length after decapsulation
+ *
+ * ip_offset
+ *		Indicates the IP offset in bytes from the start of the
+ *		packet after decapsulation.  Only valid if ipv4_proto or
+ *		ipv6_proto is set.
+ *
+ * ring_mask
+ *		Indicates the destination RX rings for this MSDU.
+ *
+ * tcp_udp_offset
+ *		Indicates the offset in bytes to the start of TCP or UDP
+ *		header from the start of the IP header after decapsulation.
+ *		Only valid if tcp_prot or udp_prot is set.  The value 0
+ *		indicates that the offset is longer than 127 bytes.
+ *
+ * reserved_0c
+ *		Reserved: HW should fill with zero.  FW should ignore.
+ *
+ * flow_id_crc
+ *		The flow_id_crc runs CRC32 on the following information:
+ *		IPv4 option: dest_addr[31:0], src_addr [31:0], {24'b0,
+ *		protocol[7:0]}.
+ *		IPv6 option: dest_addr[127:0], src_addr [127:0], {24'b0,
+ *		next_header[7:0]}
+ *		UDP case: sort_port[15:0], dest_port[15:0]
+ *		TCP case: sort_port[15:0], dest_port[15:0],
+ *		{header_length[3:0], 6'b0, flags[5:0], window_size[15:0]},
+ *		{16'b0, urgent_ptr[15:0]}, all options except 32-bit
+ *		timestamp.
+ *
+ * msdu_number
+ *		Indicates the MSDU number within a MPDU.  This value is
+ *		reset to zero at the start of each MPDU.  If the number of
+ *		MSDU exceeds 255 this number will wrap using modulo 256.
+ *
+ * decap_format
+ *		Indicates the format after decapsulation:
+ *		0: RAW: No decapsulation
+ *		1: Native WiFi
+ *		2: Ethernet 2 (DIX)
+ *		3: 802.3 (SNAP/LLC)
+ *
+ * ipv4_proto
+ *		Set if L2 layer indicates IPv4 protocol.
+ *
+ * ipv6_proto
+ *		Set if L2 layer indicates IPv6 protocol.
+ *
+ * tcp_proto
+ *		Set if the ipv4_proto or ipv6_proto are set and the IP
+ *		protocol indicates TCP.
+ *
+ * udp_proto
+ *		Set if the ipv4_proto or ipv6_proto are set and the IP
+ *			protocol indicates UDP.
+ *
+ * ip_frag
+ *		Indicates that either the IP More frag bit is set or IP frag
+ *		number is non-zero.  If set indicates that this is a
+ *		fragmented IP packet.
+ *
+ * tcp_only_ack
+ *		Set if only the TCP Ack bit is set in the TCP flags and if
+ *		the TCP payload is 0.
+ *
+ * sa_idx
+ *		The offset in the address table which matches the MAC source
+ *		address.
+ *
+ * reserved_2b
+ *		Reserved: HW should fill with zero.  FW should ignore.
+ */
+
+#define RX_MSDU_END_INFO0_REPORTED_MPDU_LENGTH_MASK 0x00003fff
+#define RX_MSDU_END_INFO0_REPORTED_MPDU_LENGTH_LSB  0
+#define RX_MSDU_END_INFO0_FIRST_MSDU                (1 << 14)
+#define RX_MSDU_END_INFO0_LAST_MSDU                 (1 << 15)
+#define RX_MSDU_END_INFO0_PRE_DELIM_ERR             (1 << 30)
+#define RX_MSDU_END_INFO0_RESERVED_3B               (1 << 31)
+
+struct rx_msdu_end {
+	__le16 ip_hdr_cksum;
+	__le16 tcp_hdr_cksum;
+	u8 key_id_octet;
+	u8 classification_filter;
+	u8 wapi_pn[10];
+	__le32 info0;
+} __packed;
+
+/*
+ *ip_hdr_chksum
+ *		This can include the IP header checksum or the pseudo header
+ *		checksum used by TCP/UDP checksum.
+ *
+ *tcp_udp_chksum
+ *		The value of the computed TCP/UDP checksum.  A mode bit
+ *		selects whether this checksum is the full checksum or the
+ *		partial checksum which does not include the pseudo header.
+ *
+ *key_id_octet
+ *		The key ID octet from the IV.  Only valid when first_msdu is
+ *		set.
+ *
+ *classification_filter
+ *		Indicates the number classification filter rule
+ *
+ *ext_wapi_pn_63_48
+ *		Extension PN (packet number) which is only used by WAPI.
+ *		This corresponds to WAPI PN bits [63:48] (pn6 and pn7).  The
+ *		WAPI PN bits [63:0] are in the pn field of the rx_mpdu_start
+ *		descriptor.
+ *
+ *ext_wapi_pn_95_64
+ *		Extension PN (packet number) which is only used by WAPI.
+ *		This corresponds to WAPI PN bits [95:64] (pn8, pn9, pn10 and
+ *		pn11).
+ *
+ *ext_wapi_pn_127_96
+ *		Extension PN (packet number) which is only used by WAPI.
+ *		This corresponds to WAPI PN bits [127:96] (pn12, pn13, pn14,
+ *		pn15).
+ *
+ *reported_mpdu_length
+ *		MPDU length before decapsulation.  Only valid when
+ *		first_msdu is set.  This field is taken directly from the
+ *		length field of the A-MPDU delimiter or the preamble length
+ *		field for non-A-MPDU frames.
+ *
+ *first_msdu
+ *		Indicates the first MSDU of A-MSDU.  If both first_msdu and
+ *		last_msdu are set in the MSDU then this is a non-aggregated
+ *		MSDU frame: normal MPDU.  Interior MSDU in an A-MSDU shall
+ *		have both first_mpdu and last_mpdu bits set to 0.
+ *
+ *last_msdu
+ *		Indicates the last MSDU of the A-MSDU.  MPDU end status is
+ *		only valid when last_msdu is set.
+ *
+ *reserved_3a
+ *		Reserved: HW should fill with zero.  FW should ignore.
+ *
+ *pre_delim_err
+ *		Indicates that the first delimiter had a FCS failure.  Only
+ *		valid when first_mpdu and first_msdu are set.
+ *
+ *reserved_3b
+ *		Reserved: HW should fill with zero.  FW should ignore.
+ */
+
+#define RX_PPDU_START_SIG_RATE_SELECT_OFDM 0
+#define RX_PPDU_START_SIG_RATE_SELECT_CCK  1
+
+#define RX_PPDU_START_SIG_RATE_OFDM_48 0
+#define RX_PPDU_START_SIG_RATE_OFDM_24 1
+#define RX_PPDU_START_SIG_RATE_OFDM_12 2
+#define RX_PPDU_START_SIG_RATE_OFDM_6  3
+#define RX_PPDU_START_SIG_RATE_OFDM_54 4
+#define RX_PPDU_START_SIG_RATE_OFDM_36 5
+#define RX_PPDU_START_SIG_RATE_OFDM_18 6
+#define RX_PPDU_START_SIG_RATE_OFDM_9  7
+
+#define RX_PPDU_START_SIG_RATE_CCK_LP_11  0
+#define RX_PPDU_START_SIG_RATE_CCK_LP_5_5 1
+#define RX_PPDU_START_SIG_RATE_CCK_LP_2   2
+#define RX_PPDU_START_SIG_RATE_CCK_LP_1   3
+#define RX_PPDU_START_SIG_RATE_CCK_SP_11  4
+#define RX_PPDU_START_SIG_RATE_CCK_SP_5_5 5
+#define RX_PPDU_START_SIG_RATE_CCK_SP_2   6
+
+#define HTT_RX_PPDU_START_PREAMBLE_LEGACY        0x04
+#define HTT_RX_PPDU_START_PREAMBLE_HT            0x08
+#define HTT_RX_PPDU_START_PREAMBLE_HT_WITH_TXBF  0x09
+#define HTT_RX_PPDU_START_PREAMBLE_VHT           0x0C
+#define HTT_RX_PPDU_START_PREAMBLE_VHT_WITH_TXBF 0x0D
+
+#define RX_PPDU_START_INFO0_IS_GREENFIELD (1 << 0)
+
+#define RX_PPDU_START_INFO1_L_SIG_RATE_MASK    0x0000000f
+#define RX_PPDU_START_INFO1_L_SIG_RATE_LSB     0
+#define RX_PPDU_START_INFO1_L_SIG_LENGTH_MASK  0x0001ffe0
+#define RX_PPDU_START_INFO1_L_SIG_LENGTH_LSB   5
+#define RX_PPDU_START_INFO1_L_SIG_TAIL_MASK    0x00fc0000
+#define RX_PPDU_START_INFO1_L_SIG_TAIL_LSB     18
+#define RX_PPDU_START_INFO1_PREAMBLE_TYPE_MASK 0xff000000
+#define RX_PPDU_START_INFO1_PREAMBLE_TYPE_LSB  24
+#define RX_PPDU_START_INFO1_L_SIG_RATE_SELECT  (1 << 4)
+#define RX_PPDU_START_INFO1_L_SIG_PARITY       (1 << 17)
+
+#define RX_PPDU_START_INFO2_HT_SIG_VHT_SIG_A_1_MASK 0x00ffffff
+#define RX_PPDU_START_INFO2_HT_SIG_VHT_SIG_A_1_LSB  0
+
+#define RX_PPDU_START_INFO3_HT_SIG_VHT_SIG_A_2_MASK 0x00ffffff
+#define RX_PPDU_START_INFO3_HT_SIG_VHT_SIG_A_2_LSB  0
+#define RX_PPDU_START_INFO3_TXBF_H_INFO             (1 << 24)
+
+#define RX_PPDU_START_INFO4_VHT_SIG_B_MASK 0x1fffffff
+#define RX_PPDU_START_INFO4_VHT_SIG_B_LSB  0
+
+#define RX_PPDU_START_INFO5_SERVICE_MASK 0x0000ffff
+#define RX_PPDU_START_INFO5_SERVICE_LSB  0
+
+struct rx_ppdu_start {
+	struct {
+		u8 pri20_mhz;
+		u8 ext20_mhz;
+		u8 ext40_mhz;
+		u8 ext80_mhz;
+	} rssi_chains[4];
+	u8 rssi_comb;
+	__le16 rsvd0;
+	u8 info0; /* %RX_PPDU_START_INFO0_ */
+	__le32 info1; /* %RX_PPDU_START_INFO1_ */
+	__le32 info2; /* %RX_PPDU_START_INFO2_ */
+	__le32 info3; /* %RX_PPDU_START_INFO3_ */
+	__le32 info4; /* %RX_PPDU_START_INFO4_ */
+	__le32 info5; /* %RX_PPDU_START_INFO5_ */
+} __packed;
+
+/*
+ * rssi_chain0_pri20
+ *		RSSI of RX PPDU on chain 0 of primary 20 MHz bandwidth.
+ *		Value of 0x80 indicates invalid.
+ *
+ * rssi_chain0_sec20
+ *		RSSI of RX PPDU on chain 0 of secondary 20 MHz bandwidth.
+ *		Value of 0x80 indicates invalid.
+ *
+ * rssi_chain0_sec40
+ *		RSSI of RX PPDU on chain 0 of secondary 40 MHz bandwidth.
+ *		Value of 0x80 indicates invalid.
+ *
+ * rssi_chain0_sec80
+ *		RSSI of RX PPDU on chain 0 of secondary 80 MHz bandwidth.
+ *		Value of 0x80 indicates invalid.
+ *
+ * rssi_chain1_pri20
+ *		RSSI of RX PPDU on chain 1 of primary 20 MHz bandwidth.
+ *		Value of 0x80 indicates invalid.
+ *
+ * rssi_chain1_sec20
+ *		RSSI of RX PPDU on chain 1 of secondary 20 MHz bandwidth.
+ *		Value of 0x80 indicates invalid.
+ *
+ * rssi_chain1_sec40
+ *		RSSI of RX PPDU on chain 1 of secondary 40 MHz bandwidth.
+ *		Value of 0x80 indicates invalid.
+ *
+ * rssi_chain1_sec80
+ *		RSSI of RX PPDU on chain 1 of secondary 80 MHz bandwidth.
+ *		Value of 0x80 indicates invalid.
+ *
+ * rssi_chain2_pri20
+ *		RSSI of RX PPDU on chain 2 of primary 20 MHz bandwidth.
+ *		Value of 0x80 indicates invalid.
+ *
+ * rssi_chain2_sec20
+ *		RSSI of RX PPDU on chain 2 of secondary 20 MHz bandwidth.
+ *		Value of 0x80 indicates invalid.
+ *
+ * rssi_chain2_sec40
+ *		RSSI of RX PPDU on chain 2 of secondary 40 MHz bandwidth.
+ *		Value of 0x80 indicates invalid.
+ *
+ * rssi_chain2_sec80
+ *		RSSI of RX PPDU on chain 2 of secondary 80 MHz bandwidth.
+ *		Value of 0x80 indicates invalid.
+ *
+ * rssi_chain3_pri20
+ *		RSSI of RX PPDU on chain 3 of primary 20 MHz bandwidth.
+ *		Value of 0x80 indicates invalid.
+ *
+ * rssi_chain3_sec20
+ *		RSSI of RX PPDU on chain 3 of secondary 20 MHz bandwidth.
+ *		Value of 0x80 indicates invalid.
+ *
+ * rssi_chain3_sec40
+ *		RSSI of RX PPDU on chain 3 of secondary 40 MHz bandwidth.
+ *		Value of 0x80 indicates invalid.
+ *
+ * rssi_chain3_sec80
+ *		RSSI of RX PPDU on chain 3 of secondary 80 MHz bandwidth.
+ *		Value of 0x80 indicates invalid.
+ *
+ * rssi_comb
+ *		The combined RSSI of RX PPDU of all active chains and
+ *		bandwidths.  Value of 0x80 indicates invalid.
+ *
+ * reserved_4a
+ *		Reserved: HW should fill with 0, FW should ignore.
+ *
+ * is_greenfield
+ *		Do we really support this?
+ *
+ * reserved_4b
+ *		Reserved: HW should fill with 0, FW should ignore.
+ *
+ * l_sig_rate
+ *		If l_sig_rate_select is 0:
+ *		0x8: OFDM 48 Mbps
+ *		0x9: OFDM 24 Mbps
+ *		0xA: OFDM 12 Mbps
+ *		0xB: OFDM 6 Mbps
+ *		0xC: OFDM 54 Mbps
+ *		0xD: OFDM 36 Mbps
+ *		0xE: OFDM 18 Mbps
+ *		0xF: OFDM 9 Mbps
+ *		If l_sig_rate_select is 1:
+ *		0x8: CCK 11 Mbps long preamble
+ *		0x9: CCK 5.5 Mbps long preamble
+ *		0xA: CCK 2 Mbps long preamble
+ *		0xB: CCK 1 Mbps long preamble
+ *		0xC: CCK 11 Mbps short preamble
+ *		0xD: CCK 5.5 Mbps short preamble
+ *		0xE: CCK 2 Mbps short preamble
+ *
+ * l_sig_rate_select
+ *		Legacy signal rate select.  If set then l_sig_rate indicates
+ *		CCK rates.  If clear then l_sig_rate indicates OFDM rates.
+ *
+ * l_sig_length
+ *		Length of legacy frame in octets.
+ *
+ * l_sig_parity
+ *		Odd parity over l_sig_rate and l_sig_length
+ *
+ * l_sig_tail
+ *		Tail bits for Viterbi decoder
+ *
+ * preamble_type
+ *		Indicates the type of preamble ahead:
+ *		0x4: Legacy (OFDM/CCK)
+ *		0x8: HT
+ *		0x9: HT with TxBF
+ *		0xC: VHT
+ *		0xD: VHT with TxBF
+ *		0x80 - 0xFF: Reserved for special baseband data types such
+ *		as radar and spectral scan.
+ *
+ * ht_sig_vht_sig_a_1
+ *		If preamble_type == 0x8 or 0x9
+ *		HT-SIG (first 24 bits)
+ *		If preamble_type == 0xC or 0xD
+ *		VHT-SIG A (first 24 bits)
+ *		Else
+ *		Reserved
+ *
+ * reserved_6
+ *		Reserved: HW should fill with 0, FW should ignore.
+ *
+ * ht_sig_vht_sig_a_2
+ *		If preamble_type == 0x8 or 0x9
+ *		HT-SIG (last 24 bits)
+ *		If preamble_type == 0xC or 0xD
+ *		VHT-SIG A (last 24 bits)
+ *		Else
+ *		Reserved
+ *
+ * txbf_h_info
+ *		Indicates that the packet data carries H information which
+ *		is used for TxBF debug.
+ *
+ * reserved_7
+ *		Reserved: HW should fill with 0, FW should ignore.
+ *
+ * vht_sig_b
+ *		WiFi 1.0 and WiFi 2.0 will likely have this field to be all
+ *		0s since the BB does not plan on decoding VHT SIG-B.
+ *
+ * reserved_8
+ *		Reserved: HW should fill with 0, FW should ignore.
+ *
+ * service
+ *		Service field from BB for OFDM, HT and VHT packets.  CCK
+ *		packets will have service field of 0.
+ *
+ * reserved_9
+ *		Reserved: HW should fill with 0, FW should ignore.
+*/
+
+
+#define RX_PPDU_END_FLAGS_PHY_ERR             (1 << 0)
+#define RX_PPDU_END_FLAGS_RX_LOCATION         (1 << 1)
+#define RX_PPDU_END_FLAGS_TXBF_H_INFO         (1 << 2)
+
+#define RX_PPDU_END_INFO0_RX_ANTENNA_MASK     0x00ffffff
+#define RX_PPDU_END_INFO0_RX_ANTENNA_LSB      0
+#define RX_PPDU_END_INFO0_FLAGS_TX_HT_VHT_ACK (1 << 24)
+#define RX_PPDU_END_INFO0_BB_CAPTURED_CHANNEL (1 << 25)
+
+#define RX_PPDU_END_INFO1_PPDU_DONE (1 << 15)
+
+struct rx_ppdu_end {
+	__le32 evm_p0;
+	__le32 evm_p1;
+	__le32 evm_p2;
+	__le32 evm_p3;
+	__le32 evm_p4;
+	__le32 evm_p5;
+	__le32 evm_p6;
+	__le32 evm_p7;
+	__le32 evm_p8;
+	__le32 evm_p9;
+	__le32 evm_p10;
+	__le32 evm_p11;
+	__le32 evm_p12;
+	__le32 evm_p13;
+	__le32 evm_p14;
+	__le32 evm_p15;
+	__le32 tsf_timestamp;
+	__le32 wb_timestamp;
+	u8 locationing_timestamp;
+	u8 phy_err_code;
+	__le16 flags; /* %RX_PPDU_END_FLAGS_ */
+	__le32 info0; /* %RX_PPDU_END_INFO0_ */
+	__le16 bb_length;
+	__le16 info1; /* %RX_PPDU_END_INFO1_ */
+} __packed;
+
+/*
+ * evm_p0
+ *		EVM for pilot 0.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p1
+ *		EVM for pilot 1.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p2
+ *		EVM for pilot 2.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p3
+ *		EVM for pilot 3.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p4
+ *		EVM for pilot 4.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p5
+ *		EVM for pilot 5.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p6
+ *		EVM for pilot 6.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p7
+ *		EVM for pilot 7.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p8
+ *		EVM for pilot 8.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p9
+ *		EVM for pilot 9.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p10
+ *		EVM for pilot 10.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p11
+ *		EVM for pilot 11.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p12
+ *		EVM for pilot 12.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p13
+ *		EVM for pilot 13.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p14
+ *		EVM for pilot 14.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * evm_p15
+ *		EVM for pilot 15.  Contain EVM for streams: 0, 1, 2 and 3.
+ *
+ * tsf_timestamp
+ *		Receive TSF timestamp sampled on the rising edge of
+ *		rx_clear.  For PHY errors this may be the current TSF when
+ *		phy_error is asserted if the rx_clear does not assert before
+ *		the end of the PHY error.
+ *
+ * wb_timestamp
+ *		WLAN/BT timestamp is a 1 usec resolution timestamp which
+ *		does not get updated based on receive beacon like TSF.  The
+ *		same rules for capturing tsf_timestamp are used to capture
+ *		the wb_timestamp.
+ *
+ * locationing_timestamp
+ *		Timestamp used for locationing.  This timestamp is used to
+ *		indicate fractions of usec.  For example if the MAC clock is
+ *		running at 80 MHz, the timestamp will increment every 12.5
+ *		nsec.  The value starts at 0 and increments to 79 and
+ *		returns to 0 and repeats.  This information is valid for
+ *		every PPDU.  This information can be used in conjunction
+ *		with wb_timestamp to capture large delta times.
+ *
+ * phy_err_code
+ *		See the 1.10.8.1.2 for the list of the PHY error codes.
+ *
+ * phy_err
+ *		Indicates a PHY error was detected for this PPDU.
+ *
+ * rx_location
+ *		Indicates that location information was requested.
+ *
+ * txbf_h_info
+ *		Indicates that the packet data carries H information which
+ *		is used for TxBF debug.
+ *
+ * reserved_18
+ *		Reserved: HW should fill with 0, FW should ignore.
+ *
+ * rx_antenna
+ *		Receive antenna value
+ *
+ * tx_ht_vht_ack
+ *		Indicates that a HT or VHT Ack/BA frame was transmitted in
+ *		response to this receive packet.
+ *
+ * bb_captured_channel
+ *		Indicates that the BB has captured a channel dump.  FW can
+ *		then read the channel dump memory.  This may indicate that
+ *		the channel was captured either based on PCU setting the
+ *		capture_channel bit  BB descriptor or FW setting the
+ *		capture_channel mode bit.
+ *
+ * reserved_19
+ *		Reserved: HW should fill with 0, FW should ignore.
+ *
+ * bb_length
+ *		Indicates the number of bytes of baseband information for
+ *		PPDUs where the BB descriptor preamble type is 0x80 to 0xFF
+ *		which indicates that this is not a normal PPDU but rather
+ *		contains baseband debug information.
+ *
+ * reserved_20
+ *		Reserved: HW should fill with 0, FW should ignore.
+ *
+ * ppdu_done
+ *		PPDU end status is only valid when ppdu_done bit is set.
+ *		Every time HW sets this bit in memory FW/SW must clear this
+ *		bit in memory.  FW will initialize all the ppdu_done dword
+ *		to 0.
+*/
+
+#define FW_RX_DESC_INFO0_DISCARD  (1 << 0)
+#define FW_RX_DESC_INFO0_FORWARD  (1 << 1)
+#define FW_RX_DESC_INFO0_INSPECT  (1 << 5)
+#define FW_RX_DESC_INFO0_EXT_MASK 0xC0
+#define FW_RX_DESC_INFO0_EXT_LSB  6
+
+struct fw_rx_desc_base {
+	u8 info0;
+} __packed;
+
+#endif /* _RX_DESC_H_ */


^ permalink raw reply related

* [PATCH v2 22/29] ath10k: add targaddrs.h
From: Kalle Valo @ 2013-06-01 11:26 UTC (permalink / raw)
  To: linux-wireless; +Cc: ath10k-devel
In-Reply-To: <20130601111820.32443.46794.stgit@localhost6.localdomain6>

Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
---
 drivers/net/wireless/ath/ath10k/targaddrs.h |  449 +++++++++++++++++++++++++++
 1 file changed, 449 insertions(+)
 create mode 100644 drivers/net/wireless/ath/ath10k/targaddrs.h

diff --git a/drivers/net/wireless/ath/ath10k/targaddrs.h b/drivers/net/wireless/ath/ath10k/targaddrs.h
new file mode 100644
index 0000000..be7ba1e
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/targaddrs.h
@@ -0,0 +1,449 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * 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 __TARGADDRS_H__
+#define __TARGADDRS_H__
+
+/*
+ * xxx_HOST_INTEREST_ADDRESS is the address in Target RAM of the
+ * host_interest structure.  It must match the address of the _host_interest
+ * symbol (see linker script).
+ *
+ * Host Interest is shared between Host and Target in order to coordinate
+ * between the two, and is intended to remain constant (with additions only
+ * at the end) across software releases.
+ *
+ * All addresses are available here so that it's possible to
+ * write a single binary that works with all Target Types.
+ * May be used in assembler code as well as C.
+ */
+#define QCA988X_HOST_INTEREST_ADDRESS    0x00400800
+#define HOST_INTEREST_MAX_SIZE          0x200
+
+/*
+ * These are items that the Host may need to access via BMI or via the
+ * Diagnostic Window. The position of items in this structure must remain
+ * constant across firmware revisions! Types for each item must be fixed
+ * size across target and host platforms. More items may be added at the end.
+ */
+struct host_interest {
+	/*
+	 * Pointer to application-defined area, if any.
+	 * Set by Target application during startup.
+	 */
+	u32 hi_app_host_interest;			/* 0x00 */
+
+	/* Pointer to register dump area, valid after Target crash. */
+	u32 hi_failure_state;				/* 0x04 */
+
+	/* Pointer to debug logging header */
+	u32 hi_dbglog_hdr;				/* 0x08 */
+
+	u32 hi_unused0c;				/* 0x0c */
+
+	/*
+	 * General-purpose flag bits, similar to SOC_OPTION_* flags.
+	 * Can be used by application rather than by OS.
+	 */
+	u32 hi_option_flag;				/* 0x10 */
+
+	/*
+	 * Boolean that determines whether or not to
+	 * display messages on the serial port.
+	 */
+	u32 hi_serial_enable;				/* 0x14 */
+
+	/* Start address of DataSet index, if any */
+	u32 hi_dset_list_head;				/* 0x18 */
+
+	/* Override Target application start address */
+	u32 hi_app_start;				/* 0x1c */
+
+	/* Clock and voltage tuning */
+	u32 hi_skip_clock_init;				/* 0x20 */
+	u32 hi_core_clock_setting;			/* 0x24 */
+	u32 hi_cpu_clock_setting;			/* 0x28 */
+	u32 hi_system_sleep_setting;			/* 0x2c */
+	u32 hi_xtal_control_setting;			/* 0x30 */
+	u32 hi_pll_ctrl_setting_24ghz;			/* 0x34 */
+	u32 hi_pll_ctrl_setting_5ghz;			/* 0x38 */
+	u32 hi_ref_voltage_trim_setting;		/* 0x3c */
+	u32 hi_clock_info;				/* 0x40 */
+
+	/* Host uses BE CPU or not */
+	u32 hi_be;					/* 0x44 */
+
+	u32 hi_stack;	/* normal stack */			/* 0x48 */
+	u32 hi_err_stack; /* error stack */		/* 0x4c */
+	u32 hi_desired_cpu_speed_hz;			/* 0x50 */
+
+	/* Pointer to Board Data  */
+	u32 hi_board_data;				/* 0x54 */
+
+	/*
+	 * Indication of Board Data state:
+	 *    0: board data is not yet initialized.
+	 *    1: board data is initialized; unknown size
+	 *   >1: number of bytes of initialized board data
+	 */
+	u32 hi_board_data_initialized;			/* 0x58 */
+
+	u32 hi_dset_ram_index_table;			/* 0x5c */
+
+	u32 hi_desired_baud_rate;			/* 0x60 */
+	u32 hi_dbglog_config;				/* 0x64 */
+	u32 hi_end_ram_reserve_sz;			/* 0x68 */
+	u32 hi_mbox_io_block_sz;			/* 0x6c */
+
+	u32 hi_num_bpatch_streams;			/* 0x70 -- unused */
+	u32 hi_mbox_isr_yield_limit;			/* 0x74 */
+
+	u32 hi_refclk_hz;				/* 0x78 */
+	u32 hi_ext_clk_detected;			/* 0x7c */
+	u32 hi_dbg_uart_txpin;				/* 0x80 */
+	u32 hi_dbg_uart_rxpin;				/* 0x84 */
+	u32 hi_hci_uart_baud;				/* 0x88 */
+	u32 hi_hci_uart_pin_assignments;		/* 0x8C */
+
+	u32 hi_hci_uart_baud_scale_val;			/* 0x90 */
+	u32 hi_hci_uart_baud_step_val;			/* 0x94 */
+
+	u32 hi_allocram_start;				/* 0x98 */
+	u32 hi_allocram_sz;				/* 0x9c */
+	u32 hi_hci_bridge_flags;			/* 0xa0 */
+	u32 hi_hci_uart_support_pins;			/* 0xa4 */
+
+	u32 hi_hci_uart_pwr_mgmt_params;		/* 0xa8 */
+
+	/*
+	 * 0xa8 - [1]: 0 = UART FC active low, 1 = UART FC active high
+	 *        [31:16]: wakeup timeout in ms
+	 */
+	/* Pointer to extended board Data  */
+	u32 hi_board_ext_data;				/* 0xac */
+	u32 hi_board_ext_data_config;			/* 0xb0 */
+	/*
+	 * Bit [0]  :   valid
+	 * Bit[31:16:   size
+	 */
+	/*
+	 * hi_reset_flag is used to do some stuff when target reset.
+	 * such as restore app_start after warm reset or
+	 * preserve host Interest area, or preserve ROM data, literals etc.
+	 */
+	u32  hi_reset_flag;				/* 0xb4 */
+	/* indicate hi_reset_flag is valid */
+	u32  hi_reset_flag_valid;			/* 0xb8 */
+	u32 hi_hci_uart_pwr_mgmt_params_ext;		/* 0xbc */
+	/* 0xbc - [31:0]: idle timeout in ms */
+	/* ACS flags */
+	u32 hi_acs_flags;				/* 0xc0 */
+	u32 hi_console_flags;				/* 0xc4 */
+	u32 hi_nvram_state;				/* 0xc8 */
+	u32 hi_option_flag2;				/* 0xcc */
+
+	/* If non-zero, override values sent to Host in WMI_READY event. */
+	u32 hi_sw_version_override;			/* 0xd0 */
+	u32 hi_abi_version_override;			/* 0xd4 */
+
+	/*
+	 * Percentage of high priority RX traffic to total expected RX traffic
+	 * applicable only to ar6004
+	 */
+	u32 hi_hp_rx_traffic_ratio;			/* 0xd8 */
+
+	/* test applications flags */
+	u32 hi_test_apps_related;			/* 0xdc */
+	/* location of test script */
+	u32 hi_ota_testscript;				/* 0xe0 */
+	/* location of CAL data */
+	u32 hi_cal_data;				/* 0xe4 */
+
+	/* Number of packet log buffers */
+	u32 hi_pktlog_num_buffers;			/* 0xe8 */
+
+	/* wow extension configuration */
+	u32 hi_wow_ext_config;				/* 0xec */
+	u32 hi_pwr_save_flags;				/* 0xf0 */
+
+	/* Spatial Multiplexing Power Save (SMPS) options */
+	u32 hi_smps_options;				/* 0xf4 */
+
+	/* Interconnect-specific state */
+	u32 hi_interconnect_state;			/* 0xf8 */
+
+	/* Coex configuration flags */
+	u32 hi_coex_config;				/* 0xfc */
+
+	/* Early allocation support */
+	u32 hi_early_alloc;				/* 0x100 */
+	/* FW swap field */
+	/*
+	 * Bits of this 32bit word will be used to pass specific swap
+	 * instruction to FW
+	 */
+	/*
+	 * Bit 0 -- AP Nart descriptor no swap. When this bit is set
+	 * FW will not swap TX descriptor. Meaning packets are formed
+	 * on the target processor.
+	 */
+	/* Bit 1 - unused */
+	u32 hi_fw_swap;					/* 0x104 */
+} __packed;
+
+#define HI_ITEM(item)  offsetof(struct host_interest, item)
+
+/* Bits defined in hi_option_flag */
+
+/* Enable timer workaround */
+#define HI_OPTION_TIMER_WAR         0x01
+/* Limit BMI command credits */
+#define HI_OPTION_BMI_CRED_LIMIT    0x02
+/* Relay Dot11 hdr to/from host */
+#define HI_OPTION_RELAY_DOT11_HDR   0x04
+/* MAC addr method 0-locally administred 1-globally unique addrs */
+#define HI_OPTION_MAC_ADDR_METHOD   0x08
+/* Firmware Bridging */
+#define HI_OPTION_FW_BRIDGE         0x10
+/* Enable CPU profiling */
+#define HI_OPTION_ENABLE_PROFILE    0x20
+/* Disable debug logging */
+#define HI_OPTION_DISABLE_DBGLOG    0x40
+/* Skip Era Tracking */
+#define HI_OPTION_SKIP_ERA_TRACKING 0x80
+/* Disable PAPRD (debug) */
+#define HI_OPTION_PAPRD_DISABLE     0x100
+#define HI_OPTION_NUM_DEV_LSB       0x200
+#define HI_OPTION_NUM_DEV_MSB       0x800
+#define HI_OPTION_DEV_MODE_LSB      0x1000
+#define HI_OPTION_DEV_MODE_MSB      0x8000000
+/* Disable LowFreq Timer Stabilization */
+#define HI_OPTION_NO_LFT_STBL       0x10000000
+/* Skip regulatory scan */
+#define HI_OPTION_SKIP_REG_SCAN     0x20000000
+/*
+ * Do regulatory scan during init before
+ * sending WMI ready event to host
+ */
+#define HI_OPTION_INIT_REG_SCAN     0x40000000
+
+/* REV6: Do not adjust memory map */
+#define HI_OPTION_SKIP_MEMMAP       0x80000000
+
+#define HI_OPTION_MAC_ADDR_METHOD_SHIFT 3
+
+/* 2 bits of hi_option_flag are used to represent 3 modes */
+#define HI_OPTION_FW_MODE_IBSS    0x0 /* IBSS Mode */
+#define HI_OPTION_FW_MODE_BSS_STA 0x1 /* STA Mode */
+#define HI_OPTION_FW_MODE_AP      0x2 /* AP Mode */
+#define HI_OPTION_FW_MODE_BT30AMP 0x3 /* BT30 AMP Mode */
+
+/* 2 bits of hi_option flag are usedto represent 4 submodes */
+#define HI_OPTION_FW_SUBMODE_NONE    0x0  /* Normal mode */
+#define HI_OPTION_FW_SUBMODE_P2PDEV  0x1  /* p2p device mode */
+#define HI_OPTION_FW_SUBMODE_P2PCLIENT 0x2 /* p2p client mode */
+#define HI_OPTION_FW_SUBMODE_P2PGO   0x3 /* p2p go mode */
+
+/* Num dev Mask */
+#define HI_OPTION_NUM_DEV_MASK    0x7
+#define HI_OPTION_NUM_DEV_SHIFT   0x9
+
+/* firmware bridging */
+#define HI_OPTION_FW_BRIDGE_SHIFT 0x04
+
+/*
+Fw Mode/SubMode Mask
+|-----------------------------------------------------------------------------|
+|  SUB   |   SUB   |   SUB   |  SUB    |         |         |         |        |
+|MODE[3] | MODE[2] | MODE[1] | MODE[0] | MODE[3] | MODE[2] | MODE[1] | MODE[0]|
+|  (2)   |   (2)   |   (2)   |   (2)   |   (2)   |   (2)   |   (2)   |   (2)  |
+|-----------------------------------------------------------------------------|
+*/
+#define HI_OPTION_FW_MODE_BITS         0x2
+#define HI_OPTION_FW_MODE_MASK         0x3
+#define HI_OPTION_FW_MODE_SHIFT        0xC
+#define HI_OPTION_ALL_FW_MODE_MASK     0xFF
+
+#define HI_OPTION_FW_SUBMODE_BITS      0x2
+#define HI_OPTION_FW_SUBMODE_MASK      0x3
+#define HI_OPTION_FW_SUBMODE_SHIFT     0x14
+#define HI_OPTION_ALL_FW_SUBMODE_MASK  0xFF00
+#define HI_OPTION_ALL_FW_SUBMODE_SHIFT 0x8
+
+
+/* hi_option_flag2 options */
+#define HI_OPTION_OFFLOAD_AMSDU     0x01
+#define HI_OPTION_DFS_SUPPORT       0x02 /* Enable DFS support */
+#define HI_OPTION_ENABLE_RFKILL     0x04 /* RFKill Enable Feature*/
+#define HI_OPTION_RADIO_RETENTION_DISABLE 0x08 /* Disable radio retention */
+#define HI_OPTION_EARLY_CFG_DONE    0x10 /* Early configuration is complete */
+
+#define HI_OPTION_RF_KILL_SHIFT     0x2
+#define HI_OPTION_RF_KILL_MASK      0x1
+
+/* hi_reset_flag */
+/* preserve App Start address */
+#define HI_RESET_FLAG_PRESERVE_APP_START         0x01
+/* preserve host interest */
+#define HI_RESET_FLAG_PRESERVE_HOST_INTEREST     0x02
+/* preserve ROM data */
+#define HI_RESET_FLAG_PRESERVE_ROMDATA           0x04
+#define HI_RESET_FLAG_PRESERVE_NVRAM_STATE       0x08
+#define HI_RESET_FLAG_PRESERVE_BOOT_INFO         0x10
+#define HI_RESET_FLAG_WARM_RESET	0x20
+
+/* define hi_fw_swap bits */
+#define HI_DESC_IN_FW_BIT	0x01
+
+/* indicate the reset flag is valid */
+#define HI_RESET_FLAG_IS_VALID  0x12345678
+
+/* ACS is enabled */
+#define HI_ACS_FLAGS_ENABLED        (1 << 0)
+/* Use physical WWAN device */
+#define HI_ACS_FLAGS_USE_WWAN       (1 << 1)
+/* Use test VAP */
+#define HI_ACS_FLAGS_TEST_VAP       (1 << 2)
+
+/*
+ * CONSOLE FLAGS
+ *
+ * Bit Range  Meaning
+ * ---------  --------------------------------
+ *   2..0     UART ID (0 = Default)
+ *    3       Baud Select (0 = 9600, 1 = 115200)
+ *   30..4    Reserved
+ *    31      Enable Console
+ *
+ */
+
+#define HI_CONSOLE_FLAGS_ENABLE       (1 << 31)
+#define HI_CONSOLE_FLAGS_UART_MASK    (0x7)
+#define HI_CONSOLE_FLAGS_UART_SHIFT   0
+#define HI_CONSOLE_FLAGS_BAUD_SELECT  (1 << 3)
+
+/* SM power save options */
+#define HI_SMPS_ALLOW_MASK            (0x00000001)
+#define HI_SMPS_MODE_MASK             (0x00000002)
+#define HI_SMPS_MODE_STATIC           (0x00000000)
+#define HI_SMPS_MODE_DYNAMIC          (0x00000002)
+#define HI_SMPS_DISABLE_AUTO_MODE     (0x00000004)
+#define HI_SMPS_DATA_THRESH_MASK      (0x000007f8)
+#define HI_SMPS_DATA_THRESH_SHIFT     (3)
+#define HI_SMPS_RSSI_THRESH_MASK      (0x0007f800)
+#define HI_SMPS_RSSI_THRESH_SHIFT     (11)
+#define HI_SMPS_LOWPWR_CM_MASK        (0x00380000)
+#define HI_SMPS_LOWPWR_CM_SHIFT       (15)
+#define HI_SMPS_HIPWR_CM_MASK         (0x03c00000)
+#define HI_SMPS_HIPWR_CM_SHIFT        (19)
+
+/*
+ * WOW Extension configuration
+ *
+ * Bit Range  Meaning
+ * ---------  --------------------------------
+ *   8..0     Size of each WOW pattern (max 511)
+ *   15..9    Number of patterns per list (max 127)
+ *   17..16   Number of lists (max 4)
+ *   30..18   Reserved
+ *   31       Enabled
+ *
+ *  set values (except enable) to zeros for default settings
+ */
+
+#define HI_WOW_EXT_ENABLED_MASK        (1 << 31)
+#define HI_WOW_EXT_NUM_LIST_SHIFT      16
+#define HI_WOW_EXT_NUM_LIST_MASK       (0x3 << HI_WOW_EXT_NUM_LIST_SHIFT)
+#define HI_WOW_EXT_NUM_PATTERNS_SHIFT  9
+#define HI_WOW_EXT_NUM_PATTERNS_MASK   (0x7F << HI_WOW_EXT_NUM_PATTERNS_SHIFT)
+#define HI_WOW_EXT_PATTERN_SIZE_SHIFT  0
+#define HI_WOW_EXT_PATTERN_SIZE_MASK   (0x1FF << HI_WOW_EXT_PATTERN_SIZE_SHIFT)
+
+#define HI_WOW_EXT_MAKE_CONFIG(num_lists, count, size) \
+	((((num_lists) << HI_WOW_EXT_NUM_LIST_SHIFT) & \
+		HI_WOW_EXT_NUM_LIST_MASK) | \
+	(((count) << HI_WOW_EXT_NUM_PATTERNS_SHIFT) & \
+		HI_WOW_EXT_NUM_PATTERNS_MASK) | \
+	(((size) << HI_WOW_EXT_PATTERN_SIZE_SHIFT) & \
+		HI_WOW_EXT_PATTERN_SIZE_MASK))
+
+#define HI_WOW_EXT_GET_NUM_LISTS(config) \
+	(((config) & HI_WOW_EXT_NUM_LIST_MASK) >> HI_WOW_EXT_NUM_LIST_SHIFT)
+#define HI_WOW_EXT_GET_NUM_PATTERNS(config) \
+	(((config) & HI_WOW_EXT_NUM_PATTERNS_MASK) >> \
+		HI_WOW_EXT_NUM_PATTERNS_SHIFT)
+#define HI_WOW_EXT_GET_PATTERN_SIZE(config) \
+	(((config) & HI_WOW_EXT_PATTERN_SIZE_MASK) >> \
+		HI_WOW_EXT_PATTERN_SIZE_SHIFT)
+
+/*
+ * Early allocation configuration
+ * Support RAM bank configuration before BMI done and this eases the memory
+ * allocation at very early stage
+ * Bit Range  Meaning
+ * ---------  ----------------------------------
+ * [0:3]      number of bank assigned to be IRAM
+ * [4:15]     reserved
+ * [16:31]    magic number
+ *
+ * Note:
+ * 1. target firmware would check magic number and if it's a match, firmware
+ *    would consider the bits[0:15] are valid and base on that to calculate
+ *    the end of DRAM. Early allocation would be located at that area and
+ *    may be reclaimed when necesary
+ * 2. if no magic number is found, early allocation would happen at "_end"
+ *    symbol of ROM which is located before the app-data and might NOT be
+ *    re-claimable. If this is adopted, link script should keep this in
+ *    mind to avoid data corruption.
+ */
+#define HI_EARLY_ALLOC_MAGIC		0x6d8a
+#define HI_EARLY_ALLOC_MAGIC_MASK	0xffff0000
+#define HI_EARLY_ALLOC_MAGIC_SHIFT	16
+#define HI_EARLY_ALLOC_IRAM_BANKS_MASK	0x0000000f
+#define HI_EARLY_ALLOC_IRAM_BANKS_SHIFT	0
+
+#define HI_EARLY_ALLOC_VALID() \
+	((((HOST_INTEREST->hi_early_alloc) & HI_EARLY_ALLOC_MAGIC_MASK) >> \
+	HI_EARLY_ALLOC_MAGIC_SHIFT) == (HI_EARLY_ALLOC_MAGIC))
+#define HI_EARLY_ALLOC_GET_IRAM_BANKS() \
+	(((HOST_INTEREST->hi_early_alloc) & HI_EARLY_ALLOC_IRAM_BANKS_MASK) \
+	>> HI_EARLY_ALLOC_IRAM_BANKS_SHIFT)
+
+/*power save flag bit definitions*/
+#define HI_PWR_SAVE_LPL_ENABLED   0x1
+/*b1-b3 reserved*/
+/*b4-b5 : dev0 LPL type : 0 - none
+			  1- Reduce Pwr Search
+			  2- Reduce Pwr Listen*/
+/*b6-b7 : dev1 LPL type and so on for Max 8 devices*/
+#define HI_PWR_SAVE_LPL_DEV0_LSB   4
+#define HI_PWR_SAVE_LPL_DEV_MASK   0x3
+/*power save related utility macros*/
+#define HI_LPL_ENABLED() \
+	((HOST_INTEREST->hi_pwr_save_flags & HI_PWR_SAVE_LPL_ENABLED))
+#define HI_DEV_LPL_TYPE_GET(_devix) \
+	(HOST_INTEREST->hi_pwr_save_flags & ((HI_PWR_SAVE_LPL_DEV_MASK) << \
+	 (HI_PWR_SAVE_LPL_DEV0_LSB + (_devix)*2)))
+
+#define HOST_INTEREST_SMPS_IS_ALLOWED() \
+	((HOST_INTEREST->hi_smps_options & HI_SMPS_ALLOW_MASK))
+
+/* Reserve 1024 bytes for extended board data */
+#define QCA988X_BOARD_DATA_SZ     7168
+#define QCA988X_BOARD_EXT_DATA_SZ 0
+
+#endif /* __TARGADDRS_H__ */


^ permalink raw reply related

* [PATCH v2 23/29] ath10k: add trace.c
From: Kalle Valo @ 2013-06-01 11:27 UTC (permalink / raw)
  To: linux-wireless; +Cc: ath10k-devel
In-Reply-To: <20130601111820.32443.46794.stgit@localhost6.localdomain6>

Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
---
 drivers/net/wireless/ath/ath10k/trace.c |   20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)
 create mode 100644 drivers/net/wireless/ath/ath10k/trace.c

diff --git a/drivers/net/wireless/ath/ath10k/trace.c b/drivers/net/wireless/ath/ath10k/trace.c
new file mode 100644
index 0000000..4a31e2c
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/trace.c
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2012 Qualcomm Atheros, Inc.
+ *
+ * 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>
+
+#define CREATE_TRACE_POINTS
+#include "trace.h"


^ permalink raw reply related

* [PATCH v2 24/29] ath10k: add trace.h
From: Kalle Valo @ 2013-06-01 11:27 UTC (permalink / raw)
  To: linux-wireless; +Cc: ath10k-devel
In-Reply-To: <20130601111820.32443.46794.stgit@localhost6.localdomain6>

Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
---
 drivers/net/wireless/ath/ath10k/trace.h |  170 +++++++++++++++++++++++++++++++
 1 file changed, 170 insertions(+)
 create mode 100644 drivers/net/wireless/ath/ath10k/trace.h

diff --git a/drivers/net/wireless/ath/ath10k/trace.h b/drivers/net/wireless/ath/ath10k/trace.h
new file mode 100644
index 0000000..85e806b
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/trace.h
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * 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.
+ */
+
+#if !defined(_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ)
+
+#include <linux/tracepoint.h>
+
+#define _TRACE_H_
+
+/* create empty functions when tracing is disabled */
+#if !defined(CONFIG_ATH10K_TRACING)
+#undef TRACE_EVENT
+#define TRACE_EVENT(name, proto, ...) \
+static inline void trace_ ## name(proto) {}
+#undef DECLARE_EVENT_CLASS
+#define DECLARE_EVENT_CLASS(...)
+#undef DEFINE_EVENT
+#define DEFINE_EVENT(evt_class, name, proto, ...) \
+static inline void trace_ ## name(proto) {}
+#endif /* !CONFIG_ATH10K_TRACING || __CHECKER__ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM ath10k
+
+#define ATH10K_MSG_MAX 200
+
+DECLARE_EVENT_CLASS(ath10k_log_event,
+	TP_PROTO(struct va_format *vaf),
+	TP_ARGS(vaf),
+	TP_STRUCT__entry(
+		__dynamic_array(char, msg, ATH10K_MSG_MAX)
+	),
+	TP_fast_assign(
+		WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
+				       ATH10K_MSG_MAX,
+				       vaf->fmt,
+				       *vaf->va) >= ATH10K_MSG_MAX);
+	),
+	TP_printk("%s", __get_str(msg))
+);
+
+DEFINE_EVENT(ath10k_log_event, ath10k_log_err,
+	     TP_PROTO(struct va_format *vaf),
+	     TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(ath10k_log_event, ath10k_log_warn,
+	     TP_PROTO(struct va_format *vaf),
+	     TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(ath10k_log_event, ath10k_log_info,
+	     TP_PROTO(struct va_format *vaf),
+	     TP_ARGS(vaf)
+);
+
+TRACE_EVENT(ath10k_log_dbg,
+	TP_PROTO(unsigned int level, struct va_format *vaf),
+	TP_ARGS(level, vaf),
+	TP_STRUCT__entry(
+		__field(unsigned int, level)
+		__dynamic_array(char, msg, ATH10K_MSG_MAX)
+	),
+	TP_fast_assign(
+		__entry->level = level;
+		WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
+				       ATH10K_MSG_MAX,
+				       vaf->fmt,
+				       *vaf->va) >= ATH10K_MSG_MAX);
+	),
+	TP_printk("%s", __get_str(msg))
+);
+
+TRACE_EVENT(ath10k_log_dbg_dump,
+	TP_PROTO(const char *msg, const char *prefix,
+		 const void *buf, size_t buf_len),
+
+	TP_ARGS(msg, prefix, buf, buf_len),
+
+	TP_STRUCT__entry(
+		__string(msg, msg)
+		__string(prefix, prefix)
+		__field(size_t, buf_len)
+		__dynamic_array(u8, buf, buf_len)
+	),
+
+	TP_fast_assign(
+		__assign_str(msg, msg);
+		__assign_str(prefix, prefix);
+		__entry->buf_len = buf_len;
+		memcpy(__get_dynamic_array(buf), buf, buf_len);
+	),
+
+	TP_printk(
+		"%s/%s\n", __get_str(prefix), __get_str(msg)
+	)
+);
+
+TRACE_EVENT(ath10k_wmi_cmd,
+	TP_PROTO(int id, void *buf, size_t buf_len),
+
+	TP_ARGS(id, buf, buf_len),
+
+	TP_STRUCT__entry(
+		__field(unsigned int, id)
+		__field(size_t, buf_len)
+		__dynamic_array(u8, buf, buf_len)
+	),
+
+	TP_fast_assign(
+		__entry->id = id;
+		__entry->buf_len = buf_len;
+		memcpy(__get_dynamic_array(buf), buf, buf_len);
+	),
+
+	TP_printk(
+		"id %d len %zu",
+		__entry->id,
+		__entry->buf_len
+	)
+);
+
+TRACE_EVENT(ath10k_wmi_event,
+	TP_PROTO(int id, void *buf, size_t buf_len),
+
+	TP_ARGS(id, buf, buf_len),
+
+	TP_STRUCT__entry(
+		__field(unsigned int, id)
+		__field(size_t, buf_len)
+		__dynamic_array(u8, buf, buf_len)
+	),
+
+	TP_fast_assign(
+		__entry->id = id;
+		__entry->buf_len = buf_len;
+		memcpy(__get_dynamic_array(buf), buf, buf_len);
+	),
+
+	TP_printk(
+		"id %d len %zu",
+		__entry->id,
+		__entry->buf_len
+	)
+);
+
+#endif /* _TRACE_H_ || TRACE_HEADER_MULTI_READ*/
+
+/* we don't want to use include/trace/events */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE trace
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox