forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 <[email protected]> Janusz Dziedzic <[email protected]> Kalle Valo <[email protected]> Marek Kwaczynski <[email protected]> Marek Puzyniak <[email protected]> Michal Kazior <[email protected]> Sujith Manoharan <[email protected]> Signed-off-by: Kalle Valo <[email protected]>
- Loading branch information
Showing
32 changed files
with
22,094 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
config ATH10K | ||
tristate "Atheros 802.11ac wireless cards support" | ||
depends on MAC80211 | ||
select ATH_COMMON | ||
---help--- | ||
This module adds support for wireless adapters based on | ||
Atheros IEEE 802.11ac family of chipsets. | ||
|
||
If you choose to build a module, it'll be called ath10k. | ||
|
||
config ATH10K_PCI | ||
tristate "Atheros ath10k PCI support" | ||
depends on ATH10K && PCI | ||
---help--- | ||
This module adds support for PCIE bus | ||
|
||
config ATH10K_DEBUG | ||
bool "Atheros ath10k debugging" | ||
depends on ATH10K | ||
---help--- | ||
Enables debug support | ||
|
||
If unsure, say Y to make it easier to debug problems. | ||
|
||
config ATH10K_DEBUGFS | ||
bool "Atheros ath10k debugfs support" | ||
depends on ATH10K | ||
---help--- | ||
Enabled debugfs support | ||
|
||
If unsure, say Y to make it easier to debug problems. | ||
|
||
config ATH10K_TRACING | ||
bool "Atheros ath10k tracing support" | ||
depends on ATH10K | ||
depends on EVENT_TRACING | ||
---help--- | ||
Select this to ath10k use tracing infrastructure. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
obj-$(CONFIG_ATH10K) += ath10k_core.o | ||
ath10k_core-y += mac.o \ | ||
debug.o \ | ||
core.o \ | ||
htc.o \ | ||
htt.o \ | ||
htt_rx.o \ | ||
htt_tx.o \ | ||
txrx.o \ | ||
wmi.o \ | ||
bmi.o | ||
|
||
ath10k_core-$(CONFIG_ATH10K_TRACING) += trace.o | ||
|
||
obj-$(CONFIG_ATH10K_PCI) += ath10k_pci.o | ||
ath10k_pci-y += pci.o \ | ||
ce.o | ||
|
||
# for tracing framework to find trace.h | ||
CFLAGS_trace.o := -I$(src) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
Oops, something went wrong.