Merge tag 'for-linville-20130730' of git://github.com/kvalo/ath6kl
This commit is contained in:
commit
1f80782786
|
@ -20,6 +20,12 @@
|
|||
#include "debug.h"
|
||||
#include "htc.h"
|
||||
|
||||
void ath10k_bmi_start(struct ath10k *ar)
|
||||
{
|
||||
ath10k_dbg(ATH10K_DBG_CORE, "BMI started\n");
|
||||
ar->bmi.done_sent = false;
|
||||
}
|
||||
|
||||
int ath10k_bmi_done(struct ath10k *ar)
|
||||
{
|
||||
struct bmi_cmd cmd;
|
||||
|
@ -105,7 +111,8 @@ int ath10k_bmi_read_memory(struct ath10k *ar,
|
|||
ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen,
|
||||
&resp, &rxlen);
|
||||
if (ret) {
|
||||
ath10k_warn("unable to read from the device\n");
|
||||
ath10k_warn("unable to read from the device (%d)\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -149,7 +156,8 @@ int ath10k_bmi_write_memory(struct ath10k *ar,
|
|||
ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,
|
||||
NULL, NULL);
|
||||
if (ret) {
|
||||
ath10k_warn("unable to write to the device\n");
|
||||
ath10k_warn("unable to write to the device (%d)\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -184,6 +184,7 @@ struct bmi_target_info {
|
|||
#define BMI_CE_NUM_TO_TARG 0
|
||||
#define BMI_CE_NUM_TO_HOST 1
|
||||
|
||||
void ath10k_bmi_start(struct ath10k *ar);
|
||||
int ath10k_bmi_done(struct ath10k *ar);
|
||||
int ath10k_bmi_get_target_info(struct ath10k *ar,
|
||||
struct bmi_target_info *target_info);
|
||||
|
|
|
@ -79,7 +79,7 @@ static inline void ath10k_ce_src_ring_write_index_set(struct ath10k *ar,
|
|||
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)) {
|
||||
if (!test_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features)) {
|
||||
ath10k_pci_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ static int ath10k_init_connect_htc(struct ath10k *ar)
|
|||
goto conn_fail;
|
||||
|
||||
/* Start HTC */
|
||||
status = ath10k_htc_start(ar->htc);
|
||||
status = ath10k_htc_start(&ar->htc);
|
||||
if (status)
|
||||
goto conn_fail;
|
||||
|
||||
|
@ -116,7 +116,7 @@ static int ath10k_init_connect_htc(struct ath10k *ar)
|
|||
return 0;
|
||||
|
||||
timeout:
|
||||
ath10k_htc_stop(ar->htc);
|
||||
ath10k_htc_stop(&ar->htc);
|
||||
conn_fail:
|
||||
return status;
|
||||
}
|
||||
|
@ -247,19 +247,11 @@ static int ath10k_push_board_ext_data(struct ath10k *ar,
|
|||
|
||||
static int ath10k_download_board_data(struct ath10k *ar)
|
||||
{
|
||||
const struct firmware *fw = ar->board_data;
|
||||
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);
|
||||
|
@ -286,32 +278,20 @@ static int ath10k_download_board_data(struct ath10k *ar)
|
|||
}
|
||||
|
||||
exit:
|
||||
release_firmware(fw);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ath10k_download_and_run_otp(struct ath10k *ar)
|
||||
{
|
||||
const struct firmware *fw;
|
||||
u32 address;
|
||||
const struct firmware *fw = ar->otp;
|
||||
u32 address = ar->hw_params.patch_load_addr;
|
||||
u32 exec_param;
|
||||
int ret;
|
||||
|
||||
/* OTP is optional */
|
||||
|
||||
if (ar->hw_params.fw.otp == NULL) {
|
||||
ath10k_info("otp file not defined\n");
|
||||
if (!ar->otp)
|
||||
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) {
|
||||
|
@ -327,28 +307,17 @@ static int ath10k_download_and_run_otp(struct ath10k *ar)
|
|||
}
|
||||
|
||||
exit:
|
||||
release_firmware(fw);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ath10k_download_fw(struct ath10k *ar)
|
||||
{
|
||||
const struct firmware *fw;
|
||||
const struct firmware *fw = ar->firmware;
|
||||
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);
|
||||
|
@ -356,7 +325,74 @@ static int ath10k_download_fw(struct ath10k *ar)
|
|||
}
|
||||
|
||||
exit:
|
||||
release_firmware(fw);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ath10k_core_free_firmware_files(struct ath10k *ar)
|
||||
{
|
||||
if (ar->board_data && !IS_ERR(ar->board_data))
|
||||
release_firmware(ar->board_data);
|
||||
|
||||
if (ar->otp && !IS_ERR(ar->otp))
|
||||
release_firmware(ar->otp);
|
||||
|
||||
if (ar->firmware && !IS_ERR(ar->firmware))
|
||||
release_firmware(ar->firmware);
|
||||
|
||||
ar->board_data = NULL;
|
||||
ar->otp = NULL;
|
||||
ar->firmware = NULL;
|
||||
}
|
||||
|
||||
static int ath10k_core_fetch_firmware_files(struct ath10k *ar)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (ar->hw_params.fw.fw == NULL) {
|
||||
ath10k_err("firmware file not defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ar->hw_params.fw.board == NULL) {
|
||||
ath10k_err("board data file not defined");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ar->board_data = ath10k_fetch_fw_file(ar,
|
||||
ar->hw_params.fw.dir,
|
||||
ar->hw_params.fw.board);
|
||||
if (IS_ERR(ar->board_data)) {
|
||||
ret = PTR_ERR(ar->board_data);
|
||||
ath10k_err("could not fetch board data (%d)\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ar->firmware = ath10k_fetch_fw_file(ar,
|
||||
ar->hw_params.fw.dir,
|
||||
ar->hw_params.fw.fw);
|
||||
if (IS_ERR(ar->firmware)) {
|
||||
ret = PTR_ERR(ar->firmware);
|
||||
ath10k_err("could not fetch firmware (%d)\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* OTP may be undefined. If so, don't fetch it at all */
|
||||
if (ar->hw_params.fw.otp == NULL)
|
||||
return 0;
|
||||
|
||||
ar->otp = ath10k_fetch_fw_file(ar,
|
||||
ar->hw_params.fw.dir,
|
||||
ar->hw_params.fw.otp);
|
||||
if (IS_ERR(ar->otp)) {
|
||||
ret = PTR_ERR(ar->otp);
|
||||
ath10k_err("could not fetch otp (%d)\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
ath10k_core_free_firmware_files(ar);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -440,8 +476,35 @@ static int ath10k_init_hw_params(struct ath10k *ar)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void ath10k_core_restart(struct work_struct *work)
|
||||
{
|
||||
struct ath10k *ar = container_of(work, struct ath10k, restart_work);
|
||||
|
||||
mutex_lock(&ar->conf_mutex);
|
||||
|
||||
switch (ar->state) {
|
||||
case ATH10K_STATE_ON:
|
||||
ath10k_halt(ar);
|
||||
ar->state = ATH10K_STATE_RESTARTING;
|
||||
ieee80211_restart_hw(ar->hw);
|
||||
break;
|
||||
case ATH10K_STATE_OFF:
|
||||
/* this can happen if driver is being unloaded */
|
||||
ath10k_warn("cannot restart a device that hasn't been started\n");
|
||||
break;
|
||||
case ATH10K_STATE_RESTARTING:
|
||||
case ATH10K_STATE_RESTARTED:
|
||||
ar->state = ATH10K_STATE_WEDGED;
|
||||
/* fall through */
|
||||
case ATH10K_STATE_WEDGED:
|
||||
ath10k_warn("device is wedged, will not restart\n");
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&ar->conf_mutex);
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -458,9 +521,6 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *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);
|
||||
|
@ -487,6 +547,8 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
|
|||
|
||||
init_waitqueue_head(&ar->event_queue);
|
||||
|
||||
INIT_WORK(&ar->restart_work, ath10k_core_restart);
|
||||
|
||||
return ar;
|
||||
|
||||
err_wq:
|
||||
|
@ -504,24 +566,11 @@ void ath10k_core_destroy(struct ath10k *ar)
|
|||
}
|
||||
EXPORT_SYMBOL(ath10k_core_destroy);
|
||||
|
||||
|
||||
int ath10k_core_register(struct ath10k *ar)
|
||||
int ath10k_core_start(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;
|
||||
ath10k_bmi_start(ar);
|
||||
|
||||
if (ath10k_init_configure_target(ar)) {
|
||||
status = -EINVAL;
|
||||
|
@ -536,32 +585,32 @@ int ath10k_core_register(struct ath10k *ar)
|
|||
if (status)
|
||||
goto err;
|
||||
|
||||
htc_ops.target_send_suspend_complete = ath10k_send_suspend_complete;
|
||||
ar->htc.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);
|
||||
status = ath10k_htc_init(ar);
|
||||
if (status) {
|
||||
ath10k_err("could not init HTC (%d)\n", status);
|
||||
goto err;
|
||||
}
|
||||
|
||||
status = ath10k_bmi_done(ar);
|
||||
if (status)
|
||||
goto err_htc_destroy;
|
||||
goto err;
|
||||
|
||||
status = ath10k_wmi_attach(ar);
|
||||
if (status) {
|
||||
ath10k_err("WMI attach failed: %d\n", status);
|
||||
goto err_htc_destroy;
|
||||
goto err;
|
||||
}
|
||||
|
||||
status = ath10k_htc_wait_target(ar->htc);
|
||||
status = ath10k_htc_wait_target(&ar->htc);
|
||||
if (status)
|
||||
goto err_wmi_detach;
|
||||
|
||||
ar->htt = ath10k_htt_attach(ar);
|
||||
if (!ar->htt) {
|
||||
status = -ENOMEM;
|
||||
status = ath10k_htt_attach(ar);
|
||||
if (status) {
|
||||
ath10k_err("could not attach htt (%d)\n", status);
|
||||
goto err_wmi_detach;
|
||||
}
|
||||
|
||||
|
@ -588,13 +637,101 @@ int ath10k_core_register(struct ath10k *ar)
|
|||
goto err_disconnect_htc;
|
||||
}
|
||||
|
||||
status = ath10k_htt_attach_target(ar->htt);
|
||||
status = ath10k_htt_attach_target(&ar->htt);
|
||||
if (status)
|
||||
goto err_disconnect_htc;
|
||||
|
||||
ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1;
|
||||
|
||||
return 0;
|
||||
|
||||
err_disconnect_htc:
|
||||
ath10k_htc_stop(&ar->htc);
|
||||
err_htt_detach:
|
||||
ath10k_htt_detach(&ar->htt);
|
||||
err_wmi_detach:
|
||||
ath10k_wmi_detach(ar);
|
||||
err:
|
||||
return status;
|
||||
}
|
||||
EXPORT_SYMBOL(ath10k_core_start);
|
||||
|
||||
void ath10k_core_stop(struct ath10k *ar)
|
||||
{
|
||||
ath10k_htc_stop(&ar->htc);
|
||||
ath10k_htt_detach(&ar->htt);
|
||||
ath10k_wmi_detach(ar);
|
||||
}
|
||||
EXPORT_SYMBOL(ath10k_core_stop);
|
||||
|
||||
/* mac80211 manages fw/hw initialization through start/stop hooks. However in
|
||||
* order to know what hw capabilities should be advertised to mac80211 it is
|
||||
* necessary to load the firmware (and tear it down immediately since start
|
||||
* hook will try to init it again) before registering */
|
||||
static int ath10k_core_probe_fw(struct ath10k *ar)
|
||||
{
|
||||
struct bmi_target_info target_info;
|
||||
int ret = 0;
|
||||
|
||||
ret = ath10k_hif_power_up(ar);
|
||||
if (ret) {
|
||||
ath10k_err("could not start pci hif (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
memset(&target_info, 0, sizeof(target_info));
|
||||
ret = ath10k_bmi_get_target_info(ar, &target_info);
|
||||
if (ret) {
|
||||
ath10k_err("could not get target info (%d)\n", ret);
|
||||
ath10k_hif_power_down(ar);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ar->target_version = target_info.version;
|
||||
ar->hw->wiphy->hw_version = target_info.version;
|
||||
|
||||
ret = ath10k_init_hw_params(ar);
|
||||
if (ret) {
|
||||
ath10k_err("could not get hw params (%d)\n", ret);
|
||||
ath10k_hif_power_down(ar);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ath10k_core_fetch_firmware_files(ar);
|
||||
if (ret) {
|
||||
ath10k_err("could not fetch firmware files (%d)\n", ret);
|
||||
ath10k_hif_power_down(ar);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ath10k_core_start(ar);
|
||||
if (ret) {
|
||||
ath10k_err("could not init core (%d)\n", ret);
|
||||
ath10k_core_free_firmware_files(ar);
|
||||
ath10k_hif_power_down(ar);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ath10k_core_stop(ar);
|
||||
ath10k_hif_power_down(ar);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ath10k_core_register(struct ath10k *ar)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = ath10k_core_probe_fw(ar);
|
||||
if (status) {
|
||||
ath10k_err("could not probe fw (%d)\n", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
status = ath10k_mac_register(ar);
|
||||
if (status)
|
||||
goto err_disconnect_htc;
|
||||
if (status) {
|
||||
ath10k_err("could not register to mac80211 (%d)\n", status);
|
||||
goto err_release_fw;
|
||||
}
|
||||
|
||||
status = ath10k_debug_create(ar);
|
||||
if (status) {
|
||||
|
@ -606,15 +743,8 @@ int ath10k_core_register(struct ath10k *ar)
|
|||
|
||||
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:
|
||||
err_release_fw:
|
||||
ath10k_core_free_firmware_files(ar);
|
||||
return status;
|
||||
}
|
||||
EXPORT_SYMBOL(ath10k_core_register);
|
||||
|
@ -625,41 +755,10 @@ void ath10k_core_unregister(struct ath10k *ar)
|
|||
* 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);
|
||||
ath10k_core_free_firmware_files(ar);
|
||||
}
|
||||
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");
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "htt.h"
|
||||
#include "htc.h"
|
||||
#include "hw.h"
|
||||
#include "targaddrs.h"
|
||||
|
@ -43,10 +44,6 @@
|
|||
|
||||
struct ath10k;
|
||||
|
||||
enum ath10k_bus {
|
||||
ATH10K_BUS_PCI,
|
||||
};
|
||||
|
||||
struct ath10k_skb_cb {
|
||||
dma_addr_t paddr;
|
||||
bool is_mapped;
|
||||
|
@ -250,6 +247,28 @@ struct ath10k_debug {
|
|||
struct completion event_stats_compl;
|
||||
};
|
||||
|
||||
enum ath10k_state {
|
||||
ATH10K_STATE_OFF = 0,
|
||||
ATH10K_STATE_ON,
|
||||
|
||||
/* When doing firmware recovery the device is first powered down.
|
||||
* mac80211 is supposed to call in to start() hook later on. It is
|
||||
* however possible that driver unloading and firmware crash overlap.
|
||||
* mac80211 can wait on conf_mutex in stop() while the device is
|
||||
* stopped in ath10k_core_restart() work holding conf_mutex. The state
|
||||
* RESTARTED means that the device is up and mac80211 has started hw
|
||||
* reconfiguration. Once mac80211 is done with the reconfiguration we
|
||||
* set the state to STATE_ON in restart_complete(). */
|
||||
ATH10K_STATE_RESTARTING,
|
||||
ATH10K_STATE_RESTARTED,
|
||||
|
||||
/* The device has crashed while restarting hw. This state is like ON
|
||||
* but commands are blocked in HTC and -ECOMM response is given. This
|
||||
* prevents completion timeouts and makes the driver more responsive to
|
||||
* userspace commands. This is also prevents recursive recovery. */
|
||||
ATH10K_STATE_WEDGED,
|
||||
};
|
||||
|
||||
struct ath10k {
|
||||
struct ath_common ath_common;
|
||||
struct ieee80211_hw *hw;
|
||||
|
@ -274,19 +293,16 @@ struct ath10k {
|
|||
|
||||
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_wmi wmi;
|
||||
struct ath10k_htc htc;
|
||||
struct ath10k_htt htt;
|
||||
|
||||
struct ath10k_hw_params {
|
||||
u32 id;
|
||||
|
@ -301,6 +317,10 @@ struct ath10k {
|
|||
} fw;
|
||||
} hw_params;
|
||||
|
||||
const struct firmware *board_data;
|
||||
const struct firmware *otp;
|
||||
const struct firmware *firmware;
|
||||
|
||||
struct {
|
||||
struct completion started;
|
||||
struct completion completed;
|
||||
|
@ -350,20 +370,22 @@ struct ath10k {
|
|||
struct completion offchan_tx_completed;
|
||||
struct sk_buff *offchan_tx_skb;
|
||||
|
||||
enum ath10k_state state;
|
||||
|
||||
struct work_struct restart_work;
|
||||
|
||||
#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_start(struct ath10k *ar);
|
||||
void ath10k_core_stop(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_ */
|
||||
|
|
|
@ -161,7 +161,7 @@ void ath10k_debug_read_target_stats(struct ath10k *ar,
|
|||
struct wmi_pdev_stats *ps;
|
||||
int i;
|
||||
|
||||
mutex_lock(&ar->conf_mutex);
|
||||
spin_lock_bh(&ar->data_lock);
|
||||
|
||||
stats = &ar->debug.target_stats;
|
||||
|
||||
|
@ -259,6 +259,7 @@ void ath10k_debug_read_target_stats(struct ath10k *ar,
|
|||
}
|
||||
}
|
||||
|
||||
spin_unlock_bh(&ar->data_lock);
|
||||
mutex_unlock(&ar->conf_mutex);
|
||||
complete(&ar->debug.event_stats_compl);
|
||||
}
|
||||
|
@ -268,35 +269,35 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
|
|||
{
|
||||
struct ath10k *ar = file->private_data;
|
||||
struct ath10k_target_stats *fw_stats;
|
||||
char *buf;
|
||||
char *buf = NULL;
|
||||
unsigned int len = 0, buf_len = 2500;
|
||||
ssize_t ret_cnt;
|
||||
ssize_t ret_cnt = 0;
|
||||
long left;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
fw_stats = &ar->debug.target_stats;
|
||||
|
||||
mutex_lock(&ar->conf_mutex);
|
||||
|
||||
if (ar->state != ATH10K_STATE_ON)
|
||||
goto exit;
|
||||
|
||||
buf = kzalloc(buf_len, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
goto exit;
|
||||
|
||||
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;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
left = wait_for_completion_timeout(&ar->debug.event_stats_compl, 1*HZ);
|
||||
if (left <= 0)
|
||||
goto exit;
|
||||
|
||||
if (left <= 0) {
|
||||
kfree(buf);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
mutex_lock(&ar->conf_mutex);
|
||||
|
||||
spin_lock_bh(&ar->data_lock);
|
||||
len += scnprintf(buf + len, buf_len - len, "\n");
|
||||
len += scnprintf(buf + len, buf_len - len, "%30s\n",
|
||||
"ath10k PDEV stats");
|
||||
|
@ -424,14 +425,15 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
|
|||
fw_stats->peer_stat[i].peer_tx_rate);
|
||||
len += scnprintf(buf + len, buf_len - len, "\n");
|
||||
}
|
||||
spin_unlock_bh(&ar->data_lock);
|
||||
|
||||
if (len > buf_len)
|
||||
len = buf_len;
|
||||
|
||||
ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
|
||||
|
||||
exit:
|
||||
mutex_unlock(&ar->conf_mutex);
|
||||
|
||||
kfree(buf);
|
||||
return ret_cnt;
|
||||
}
|
||||
|
@ -443,6 +445,60 @@ static const struct file_operations fops_fw_stats = {
|
|||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static ssize_t ath10k_read_simulate_fw_crash(struct file *file,
|
||||
char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
const char buf[] = "To simulate firmware crash write the keyword"
|
||||
" `crash` to this file.\nThis will force firmware"
|
||||
" to report a crash to the host system.\n";
|
||||
return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
|
||||
}
|
||||
|
||||
static ssize_t ath10k_write_simulate_fw_crash(struct file *file,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct ath10k *ar = file->private_data;
|
||||
char buf[32] = {};
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ar->conf_mutex);
|
||||
|
||||
simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
|
||||
if (strcmp(buf, "crash") && strcmp(buf, "crash\n")) {
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (ar->state != ATH10K_STATE_ON &&
|
||||
ar->state != ATH10K_STATE_RESTARTED) {
|
||||
ret = -ENETDOWN;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ath10k_info("simulating firmware crash\n");
|
||||
|
||||
ret = ath10k_wmi_force_fw_hang(ar, WMI_FORCE_FW_HANG_ASSERT, 0);
|
||||
if (ret)
|
||||
ath10k_warn("failed to force fw hang (%d)\n", ret);
|
||||
|
||||
if (ret == 0)
|
||||
ret = count;
|
||||
|
||||
exit:
|
||||
mutex_unlock(&ar->conf_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations fops_simulate_fw_crash = {
|
||||
.read = ath10k_read_simulate_fw_crash,
|
||||
.write = ath10k_write_simulate_fw_crash,
|
||||
.open = simple_open,
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
int ath10k_debug_create(struct ath10k *ar)
|
||||
{
|
||||
ar->debug.debugfs_phy = debugfs_create_dir("ath10k",
|
||||
|
@ -459,6 +515,9 @@ int ath10k_debug_create(struct ath10k *ar)
|
|||
debugfs_create_file("wmi_services", S_IRUSR, ar->debug.debugfs_phy, ar,
|
||||
&fops_wmi_services);
|
||||
|
||||
debugfs_create_file("simulate_fw_crash", S_IRUSR, ar->debug.debugfs_phy,
|
||||
ar, &fops_simulate_fw_crash);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_ATH10K_DEBUGFS */
|
||||
|
|
|
@ -46,8 +46,11 @@ struct ath10k_hif_ops {
|
|||
void *request, u32 request_len,
|
||||
void *response, u32 *response_len);
|
||||
|
||||
/* Post BMI phase, after FW is loaded. Starts regular operation */
|
||||
int (*start)(struct ath10k *ar);
|
||||
|
||||
/* Clean up what start() did. This does not revert to BMI phase. If
|
||||
* desired so, call power_down() and power_up() */
|
||||
void (*stop)(struct ath10k *ar);
|
||||
|
||||
int (*map_service_to_pipe)(struct ath10k *ar, u16 service_id,
|
||||
|
@ -66,10 +69,20 @@ struct ath10k_hif_ops {
|
|||
*/
|
||||
void (*send_complete_check)(struct ath10k *ar, u8 pipe_id, int force);
|
||||
|
||||
void (*init)(struct ath10k *ar,
|
||||
struct ath10k_hif_cb *callbacks);
|
||||
void (*set_callbacks)(struct ath10k *ar,
|
||||
struct ath10k_hif_cb *callbacks);
|
||||
|
||||
u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id);
|
||||
|
||||
/* Power up the device and enter BMI transfer mode for FW download */
|
||||
int (*power_up)(struct ath10k *ar);
|
||||
|
||||
/* Power down the device and free up resources. stop() must be called
|
||||
* before this if start() was called earlier */
|
||||
void (*power_down)(struct ath10k *ar);
|
||||
|
||||
int (*suspend)(struct ath10k *ar);
|
||||
int (*resume)(struct ath10k *ar);
|
||||
};
|
||||
|
||||
|
||||
|
@ -122,10 +135,10 @@ static inline void ath10k_hif_send_complete_check(struct ath10k *ar,
|
|||
ar->hif.ops->send_complete_check(ar, pipe_id, force);
|
||||
}
|
||||
|
||||
static inline void ath10k_hif_init(struct ath10k *ar,
|
||||
struct ath10k_hif_cb *callbacks)
|
||||
static inline void ath10k_hif_set_callbacks(struct ath10k *ar,
|
||||
struct ath10k_hif_cb *callbacks)
|
||||
{
|
||||
ar->hif.ops->init(ar, callbacks);
|
||||
ar->hif.ops->set_callbacks(ar, callbacks);
|
||||
}
|
||||
|
||||
static inline u16 ath10k_hif_get_free_queue_number(struct ath10k *ar,
|
||||
|
@ -134,4 +147,30 @@ static inline u16 ath10k_hif_get_free_queue_number(struct ath10k *ar,
|
|||
return ar->hif.ops->get_free_queue_number(ar, pipe_id);
|
||||
}
|
||||
|
||||
static inline int ath10k_hif_power_up(struct ath10k *ar)
|
||||
{
|
||||
return ar->hif.ops->power_up(ar);
|
||||
}
|
||||
|
||||
static inline void ath10k_hif_power_down(struct ath10k *ar)
|
||||
{
|
||||
ar->hif.ops->power_down(ar);
|
||||
}
|
||||
|
||||
static inline int ath10k_hif_suspend(struct ath10k *ar)
|
||||
{
|
||||
if (!ar->hif.ops->suspend)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return ar->hif.ops->suspend(ar);
|
||||
}
|
||||
|
||||
static inline int ath10k_hif_resume(struct ath10k *ar)
|
||||
{
|
||||
if (!ar->hif.ops->resume)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return ar->hif.ops->resume(ar);
|
||||
}
|
||||
|
||||
#endif /* _HIF_H_ */
|
||||
|
|
|
@ -246,15 +246,22 @@ int ath10k_htc_send(struct ath10k_htc *htc,
|
|||
{
|
||||
struct ath10k_htc_ep *ep = &htc->endpoint[eid];
|
||||
|
||||
if (htc->ar->state == ATH10K_STATE_WEDGED)
|
||||
return -ECOMM;
|
||||
|
||||
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);
|
||||
if (htc->stopped) {
|
||||
spin_unlock_bh(&htc->tx_lock);
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
|
||||
__skb_queue_tail(&ep->tx_queue, skb);
|
||||
skb_push(skb, sizeof(struct ath10k_htc_hdr));
|
||||
spin_unlock_bh(&htc->tx_lock);
|
||||
|
||||
queue_work(htc->ar->workqueue, &ep->send_work);
|
||||
|
@ -265,25 +272,19 @@ 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 *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 */
|
||||
|
||||
/* 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 */
|
||||
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
|
||||
*/
|
||||
if (!ep->tx_credit_flow_enabled && !htc->stopped)
|
||||
queue_work(ar->workqueue, &ep->send_work);
|
||||
spin_unlock_bh(&htc->tx_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -414,7 +415,7 @@ static int ath10k_htc_rx_completion_handler(struct ath10k *ar,
|
|||
u8 pipe_id)
|
||||
{
|
||||
int status = 0;
|
||||
struct ath10k_htc *htc = ar->htc;
|
||||
struct ath10k_htc *htc = &ar->htc;
|
||||
struct ath10k_htc_hdr *hdr;
|
||||
struct ath10k_htc_ep *ep;
|
||||
u16 payload_len;
|
||||
|
@ -751,8 +752,9 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc,
|
|||
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));
|
||||
ath10k_dbg(ATH10K_DBG_HTC,
|
||||
"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) {
|
||||
|
@ -947,7 +949,7 @@ void ath10k_htc_stop(struct ath10k_htc *htc)
|
|||
struct ath10k_htc_ep *ep;
|
||||
|
||||
spin_lock_bh(&htc->tx_lock);
|
||||
htc->stopping = true;
|
||||
htc->stopped = true;
|
||||
spin_unlock_bh(&htc->tx_lock);
|
||||
|
||||
for (i = ATH10K_HTC_EP_0; i < ATH10K_HTC_EP_COUNT; i++) {
|
||||
|
@ -956,26 +958,18 @@ void ath10k_htc_stop(struct ath10k_htc *htc)
|
|||
}
|
||||
|
||||
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)
|
||||
int ath10k_htc_init(struct ath10k *ar)
|
||||
{
|
||||
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);
|
||||
struct ath10k_htc *htc = &ar->htc;
|
||||
|
||||
spin_lock_init(&htc->tx_lock);
|
||||
|
||||
memcpy(&htc->htc_ops, htc_ops, sizeof(struct ath10k_htc_ops));
|
||||
|
||||
htc->stopped = false;
|
||||
ath10k_htc_reset_endpoint_states(htc);
|
||||
|
||||
/* setup HIF layer callbacks */
|
||||
|
@ -986,15 +980,10 @@ struct ath10k_htc *ath10k_htc_create(struct ath10k *ar,
|
|||
/* Get HIF default pipe for HTC message exchange */
|
||||
ep = &htc->endpoint[ATH10K_HTC_EP_0];
|
||||
|
||||
ath10k_hif_init(ar, &htc_callbacks);
|
||||
ath10k_hif_set_callbacks(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);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -335,7 +335,7 @@ struct ath10k_htc {
|
|||
struct ath10k *ar;
|
||||
struct ath10k_htc_ep endpoint[ATH10K_HTC_EP_COUNT];
|
||||
|
||||
/* protects endpoint and stopping fields */
|
||||
/* protects endpoint and stopped fields */
|
||||
spinlock_t tx_lock;
|
||||
|
||||
struct ath10k_htc_ops htc_ops;
|
||||
|
@ -349,11 +349,10 @@ struct ath10k_htc {
|
|||
struct ath10k_htc_svc_tx_credits service_tx_alloc[ATH10K_HTC_EP_COUNT];
|
||||
int target_credit_size;
|
||||
|
||||
bool stopping;
|
||||
bool stopped;
|
||||
};
|
||||
|
||||
struct ath10k_htc *ath10k_htc_create(struct ath10k *ar,
|
||||
struct ath10k_htc_ops *htc_ops);
|
||||
int ath10k_htc_init(struct ath10k *ar);
|
||||
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,
|
||||
|
@ -362,7 +361,6 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc,
|
|||
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
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/if_ether.h>
|
||||
|
||||
#include "htt.h"
|
||||
#include "core.h"
|
||||
|
@ -36,7 +37,7 @@ static int ath10k_htt_htc_attach(struct ath10k_htt *htt)
|
|||
/* 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,
|
||||
status = ath10k_htc_connect_service(&htt->ar->htc, &conn_req,
|
||||
&conn_resp);
|
||||
|
||||
if (status)
|
||||
|
@ -47,15 +48,11 @@ static int ath10k_htt_htc_attach(struct ath10k_htt *htt)
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar)
|
||||
int ath10k_htt_attach(struct ath10k *ar)
|
||||
{
|
||||
struct ath10k_htt *htt;
|
||||
struct ath10k_htt *htt = &ar->htt;
|
||||
int ret;
|
||||
|
||||
htt = kzalloc(sizeof(*htt), GFP_KERNEL);
|
||||
if (!htt)
|
||||
return NULL;
|
||||
|
||||
htt->ar = ar;
|
||||
htt->max_throughput_mbps = 800;
|
||||
|
||||
|
@ -65,8 +62,11 @@ struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar)
|
|||
* since ath10k_htt_rx_attach involves sending a rx ring configure
|
||||
* message to the target.
|
||||
*/
|
||||
if (ath10k_htt_htc_attach(htt))
|
||||
ret = ath10k_htt_htc_attach(htt);
|
||||
if (ret) {
|
||||
ath10k_err("could not attach htt htc (%d)\n", ret);
|
||||
goto err_htc_attach;
|
||||
}
|
||||
|
||||
ret = ath10k_htt_tx_attach(htt);
|
||||
if (ret) {
|
||||
|
@ -74,8 +74,11 @@ struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar)
|
|||
goto err_htc_attach;
|
||||
}
|
||||
|
||||
if (ath10k_htt_rx_attach(htt))
|
||||
ret = ath10k_htt_rx_attach(htt);
|
||||
if (ret) {
|
||||
ath10k_err("could not attach htt rx (%d)\n", ret);
|
||||
goto err_rx_attach;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prefetch enough data to satisfy target
|
||||
|
@ -89,13 +92,12 @@ struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar)
|
|||
8 + /* llc snap */
|
||||
2; /* ip4 dscp or ip6 priority */
|
||||
|
||||
return htt;
|
||||
return 0;
|
||||
|
||||
err_rx_attach:
|
||||
ath10k_htt_tx_detach(htt);
|
||||
err_htc_attach:
|
||||
kfree(htt);
|
||||
return NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define HTT_TARGET_VERSION_TIMEOUT_HZ (3*HZ)
|
||||
|
@ -148,5 +150,4 @@ void ath10k_htt_detach(struct ath10k_htt *htt)
|
|||
{
|
||||
ath10k_htt_rx_detach(htt);
|
||||
ath10k_htt_tx_detach(htt);
|
||||
kfree(htt);
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
|
||||
#include <linux/bug.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "htc.h"
|
||||
#include "rx_desc.h"
|
||||
|
||||
|
@ -1317,7 +1316,7 @@ struct htt_rx_desc {
|
|||
#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(struct ath10k *ar);
|
||||
int ath10k_htt_attach_target(struct ath10k_htt *htt);
|
||||
void ath10k_htt_detach(struct ath10k_htt *htt);
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "core.h"
|
||||
#include "htc.h"
|
||||
#include "htt.h"
|
||||
#include "txrx.h"
|
||||
|
@ -1036,7 +1037,7 @@ end:
|
|||
|
||||
void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
|
||||
{
|
||||
struct ath10k_htt *htt = ar->htt;
|
||||
struct ath10k_htt *htt = &ar->htt;
|
||||
struct htt_resp *resp = (struct htt_resp *)skb->data;
|
||||
|
||||
/* confirm alignment */
|
||||
|
|
|
@ -92,7 +92,7 @@ int ath10k_htt_tx_attach(struct ath10k_htt *htt)
|
|||
|
||||
/* At the beginning free queue number should hint us the maximum
|
||||
* queue length */
|
||||
pipe = htt->ar->htc->endpoint[htt->eid].ul_pipe_id;
|
||||
pipe = htt->ar->htc.endpoint[htt->eid].ul_pipe_id;
|
||||
htt->max_num_pending_tx = ath10k_hif_get_free_queue_number(htt->ar,
|
||||
pipe);
|
||||
|
||||
|
@ -153,7 +153,7 @@ void ath10k_htt_tx_detach(struct ath10k_htt *htt)
|
|||
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;
|
||||
struct ath10k_htt *htt = &ar->htt;
|
||||
|
||||
if (skb_cb->htt.is_conf) {
|
||||
dev_kfree_skb_any(skb);
|
||||
|
@ -194,7 +194,7 @@ int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt)
|
|||
|
||||
ATH10K_SKB_CB(skb)->htt.is_conf = true;
|
||||
|
||||
ret = ath10k_htc_send(htt->ar->htc, htt->eid, skb);
|
||||
ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb);
|
||||
if (ret) {
|
||||
dev_kfree_skb_any(skb);
|
||||
return ret;
|
||||
|
@ -281,7 +281,7 @@ int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt)
|
|||
|
||||
ATH10K_SKB_CB(skb)->htt.is_conf = true;
|
||||
|
||||
ret = ath10k_htc_send(htt->ar->htc, htt->eid, skb);
|
||||
ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb);
|
||||
if (ret) {
|
||||
dev_kfree_skb_any(skb);
|
||||
return ret;
|
||||
|
@ -346,7 +346,7 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
|
|||
skb_cb->htt.refcount = 2;
|
||||
skb_cb->htt.msdu = msdu;
|
||||
|
||||
res = ath10k_htc_send(htt->ar->htc, htt->eid, txdesc);
|
||||
res = ath10k_htc_send(&htt->ar->htc, htt->eid, txdesc);
|
||||
if (res)
|
||||
goto err;
|
||||
|
||||
|
@ -486,7 +486,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
|
|||
skb_cb->htt.txfrag = txfrag;
|
||||
skb_cb->htt.msdu = msdu;
|
||||
|
||||
res = ath10k_htc_send(htt->ar->htc, htt->eid, txdesc);
|
||||
res = ath10k_htc_send(&htt->ar->htc, htt->eid, txdesc);
|
||||
if (res)
|
||||
goto err;
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <net/mac80211.h>
|
||||
#include <linux/etherdevice.h>
|
||||
|
||||
#include "hif.h"
|
||||
#include "core.h"
|
||||
#include "debug.h"
|
||||
#include "wmi.h"
|
||||
|
@ -43,6 +44,8 @@ static int ath10k_send_key(struct ath10k_vif *arvif,
|
|||
.macaddr = macaddr,
|
||||
};
|
||||
|
||||
lockdep_assert_held(&arvif->ar->conf_mutex);
|
||||
|
||||
if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
|
||||
arg.key_flags = WMI_KEY_PAIRWISE;
|
||||
else
|
||||
|
@ -87,6 +90,8 @@ static int ath10k_install_key(struct ath10k_vif *arvif,
|
|||
struct ath10k *ar = arvif->ar;
|
||||
int ret;
|
||||
|
||||
lockdep_assert_held(&ar->conf_mutex);
|
||||
|
||||
INIT_COMPLETION(ar->install_key_done);
|
||||
|
||||
ret = ath10k_send_key(arvif, key, cmd, macaddr);
|
||||
|
@ -327,6 +332,29 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value)
|
||||
{
|
||||
if (value != 0xFFFFFFFF)
|
||||
value = min_t(u32, arvif->ar->hw->wiphy->rts_threshold,
|
||||
ATH10K_RTS_MAX);
|
||||
|
||||
return ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id,
|
||||
WMI_VDEV_PARAM_RTS_THRESHOLD,
|
||||
value);
|
||||
}
|
||||
|
||||
static int ath10k_mac_set_frag(struct ath10k_vif *arvif, u32 value)
|
||||
{
|
||||
if (value != 0xFFFFFFFF)
|
||||
value = clamp_t(u32, arvif->ar->hw->wiphy->frag_threshold,
|
||||
ATH10K_FRAGMT_THRESHOLD_MIN,
|
||||
ATH10K_FRAGMT_THRESHOLD_MAX);
|
||||
|
||||
return ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id,
|
||||
WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
|
||||
value);
|
||||
}
|
||||
|
||||
static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr)
|
||||
{
|
||||
int ret;
|
||||
|
@ -364,6 +392,20 @@ static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id)
|
|||
spin_unlock_bh(&ar->data_lock);
|
||||
}
|
||||
|
||||
static void ath10k_peer_cleanup_all(struct ath10k *ar)
|
||||
{
|
||||
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) {
|
||||
list_del(&peer->list);
|
||||
kfree(peer);
|
||||
}
|
||||
spin_unlock_bh(&ar->data_lock);
|
||||
}
|
||||
|
||||
/************************/
|
||||
/* Interface management */
|
||||
/************************/
|
||||
|
@ -372,6 +414,8 @@ static inline int ath10k_vdev_setup_sync(struct ath10k *ar)
|
|||
{
|
||||
int ret;
|
||||
|
||||
lockdep_assert_held(&ar->conf_mutex);
|
||||
|
||||
ret = wait_for_completion_timeout(&ar->vdev_setup_done,
|
||||
ATH10K_VDEV_SETUP_TIMEOUT_HZ);
|
||||
if (ret == 0)
|
||||
|
@ -605,6 +649,8 @@ static void ath10k_control_beaconing(struct ath10k_vif *arvif,
|
|||
{
|
||||
int ret = 0;
|
||||
|
||||
lockdep_assert_held(&arvif->ar->conf_mutex);
|
||||
|
||||
if (!info->enable_beacon) {
|
||||
ath10k_vdev_stop(arvif);
|
||||
return;
|
||||
|
@ -631,6 +677,8 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif,
|
|||
{
|
||||
int ret = 0;
|
||||
|
||||
lockdep_assert_held(&arvif->ar->conf_mutex);
|
||||
|
||||
if (!info->ibss_joined) {
|
||||
ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, self_peer);
|
||||
if (ret)
|
||||
|
@ -680,6 +728,8 @@ static void ath10k_ps_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
|
|||
enum wmi_sta_ps_mode psmode;
|
||||
int ret;
|
||||
|
||||
lockdep_assert_held(&arvif->ar->conf_mutex);
|
||||
|
||||
if (vif->type != NL80211_IFTYPE_STATION)
|
||||
return;
|
||||
|
||||
|
@ -722,6 +772,8 @@ static void ath10k_peer_assoc_h_basic(struct ath10k *ar,
|
|||
struct ieee80211_bss_conf *bss_conf,
|
||||
struct wmi_peer_assoc_complete_arg *arg)
|
||||
{
|
||||
lockdep_assert_held(&ar->conf_mutex);
|
||||
|
||||
memcpy(arg->addr, sta->addr, ETH_ALEN);
|
||||
arg->vdev_id = arvif->vdev_id;
|
||||
arg->peer_aid = sta->aid;
|
||||
|
@ -764,6 +816,8 @@ static void ath10k_peer_assoc_h_crypto(struct ath10k *ar,
|
|||
const u8 *rsnie = NULL;
|
||||
const u8 *wpaie = NULL;
|
||||
|
||||
lockdep_assert_held(&ar->conf_mutex);
|
||||
|
||||
bss = cfg80211_get_bss(ar->hw->wiphy, ar->hw->conf.chandef.chan,
|
||||
info->bssid, NULL, 0, 0, 0);
|
||||
if (bss) {
|
||||
|
@ -804,6 +858,8 @@ static void ath10k_peer_assoc_h_rates(struct ath10k *ar,
|
|||
u32 ratemask;
|
||||
int i;
|
||||
|
||||
lockdep_assert_held(&ar->conf_mutex);
|
||||
|
||||
sband = ar->hw->wiphy->bands[ar->hw->conf.chandef.chan->band];
|
||||
ratemask = sta->supp_rates[ar->hw->conf.chandef.chan->band];
|
||||
rates = sband->bitrates;
|
||||
|
@ -827,6 +883,8 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
|
|||
int smps;
|
||||
int i, n;
|
||||
|
||||
lockdep_assert_held(&ar->conf_mutex);
|
||||
|
||||
if (!ht_cap->ht_supported)
|
||||
return;
|
||||
|
||||
|
@ -905,6 +963,8 @@ static void ath10k_peer_assoc_h_qos_ap(struct ath10k *ar,
|
|||
u32 uapsd = 0;
|
||||
u32 max_sp = 0;
|
||||
|
||||
lockdep_assert_held(&ar->conf_mutex);
|
||||
|
||||
if (sta->wme)
|
||||
arg->peer_flags |= WMI_PEER_QOS;
|
||||
|
||||
|
@ -1056,6 +1116,8 @@ static int ath10k_peer_assoc(struct ath10k *ar,
|
|||
{
|
||||
struct wmi_peer_assoc_complete_arg arg;
|
||||
|
||||
lockdep_assert_held(&ar->conf_mutex);
|
||||
|
||||
memset(&arg, 0, sizeof(struct wmi_peer_assoc_complete_arg));
|
||||
|
||||
ath10k_peer_assoc_h_basic(ar, arvif, sta, bss_conf, &arg);
|
||||
|
@ -1079,6 +1141,8 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
|
|||
struct ieee80211_sta *ap_sta;
|
||||
int ret;
|
||||
|
||||
lockdep_assert_held(&ar->conf_mutex);
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
ap_sta = ieee80211_find_sta(vif, bss_conf->bssid);
|
||||
|
@ -1119,6 +1183,8 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
|
|||
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
|
||||
int ret;
|
||||
|
||||
lockdep_assert_held(&ar->conf_mutex);
|
||||
|
||||
/*
|
||||
* For some reason, calling VDEV-DOWN before VDEV-STOP
|
||||
* makes the FW to send frames via HTT after disassociation.
|
||||
|
@ -1152,6 +1218,8 @@ static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif,
|
|||
{
|
||||
int ret = 0;
|
||||
|
||||
lockdep_assert_held(&ar->conf_mutex);
|
||||
|
||||
ret = ath10k_peer_assoc(ar, arvif, sta, NULL);
|
||||
if (ret) {
|
||||
ath10k_warn("WMI peer assoc failed for %pM\n", sta->addr);
|
||||
|
@ -1172,6 +1240,8 @@ static int ath10k_station_disassoc(struct ath10k *ar, struct ath10k_vif *arvif,
|
|||
{
|
||||
int ret = 0;
|
||||
|
||||
lockdep_assert_held(&ar->conf_mutex);
|
||||
|
||||
ret = ath10k_clear_peer_keys(arvif, sta->addr);
|
||||
if (ret) {
|
||||
ath10k_warn("could not clear all peer wep keys (%d)\n", ret);
|
||||
|
@ -1198,6 +1268,8 @@ static int ath10k_update_channel_list(struct ath10k *ar)
|
|||
int ret;
|
||||
int i;
|
||||
|
||||
lockdep_assert_held(&ar->conf_mutex);
|
||||
|
||||
bands = hw->wiphy->bands;
|
||||
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
|
||||
if (!bands[band])
|
||||
|
@ -1276,21 +1348,19 @@ static int ath10k_update_channel_list(struct ath10k *ar)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void ath10k_reg_notifier(struct wiphy *wiphy,
|
||||
struct regulatory_request *request)
|
||||
static void ath10k_regd_update(struct ath10k *ar)
|
||||
{
|
||||
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);
|
||||
lockdep_assert_held(&ar->conf_mutex);
|
||||
|
||||
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,
|
||||
|
@ -1303,6 +1373,20 @@ static void ath10k_reg_notifier(struct wiphy *wiphy,
|
|||
ath10k_warn("could not set pdev regdomain (%d)\n", ret);
|
||||
}
|
||||
|
||||
static void ath10k_reg_notifier(struct wiphy *wiphy,
|
||||
struct regulatory_request *request)
|
||||
{
|
||||
struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
|
||||
struct ath10k *ar = hw->priv;
|
||||
|
||||
ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory);
|
||||
|
||||
mutex_lock(&ar->conf_mutex);
|
||||
if (ar->state == ATH10K_STATE_ON)
|
||||
ath10k_regd_update(ar);
|
||||
mutex_unlock(&ar->conf_mutex);
|
||||
}
|
||||
|
||||
/***************/
|
||||
/* TX handlers */
|
||||
/***************/
|
||||
|
@ -1397,15 +1481,15 @@ static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb)
|
|||
int ret;
|
||||
|
||||
if (ieee80211_is_mgmt(hdr->frame_control))
|
||||
ret = ath10k_htt_mgmt_tx(ar->htt, skb);
|
||||
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);
|
||||
ret = ath10k_htt_mgmt_tx(&ar->htt, skb);
|
||||
else
|
||||
ret = ath10k_htt_tx(ar->htt, skb);
|
||||
ret = ath10k_htt_tx(&ar->htt, skb);
|
||||
|
||||
if (ret) {
|
||||
ath10k_warn("tx failed (%d). dropping packet.\n", ret);
|
||||
|
@ -1552,6 +1636,10 @@ static int ath10k_abort_scan(struct ath10k *ar)
|
|||
ret = ath10k_wmi_stop_scan(ar, &arg);
|
||||
if (ret) {
|
||||
ath10k_warn("could not submit wmi stop scan (%d)\n", ret);
|
||||
spin_lock_bh(&ar->data_lock);
|
||||
ar->scan.in_progress = false;
|
||||
ath10k_offchan_tx_purge(ar);
|
||||
spin_unlock_bh(&ar->data_lock);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
@ -1645,10 +1733,14 @@ static void ath10k_tx(struct ieee80211_hw *hw,
|
|||
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);
|
||||
/* it makes no sense to process injected frames like that */
|
||||
if (info->control.vif &&
|
||||
info->control.vif->type != NL80211_IFTYPE_MONITOR) {
|
||||
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;
|
||||
|
@ -1673,10 +1765,57 @@ static void ath10k_tx(struct ieee80211_hw *hw,
|
|||
/*
|
||||
* Initialize various parameters with default vaules.
|
||||
*/
|
||||
void ath10k_halt(struct ath10k *ar)
|
||||
{
|
||||
lockdep_assert_held(&ar->conf_mutex);
|
||||
|
||||
del_timer_sync(&ar->scan.timeout);
|
||||
ath10k_offchan_tx_purge(ar);
|
||||
ath10k_peer_cleanup_all(ar);
|
||||
ath10k_core_stop(ar);
|
||||
ath10k_hif_power_down(ar);
|
||||
|
||||
spin_lock_bh(&ar->data_lock);
|
||||
if (ar->scan.in_progress) {
|
||||
del_timer(&ar->scan.timeout);
|
||||
ar->scan.in_progress = false;
|
||||
ieee80211_scan_completed(ar->hw, true);
|
||||
}
|
||||
spin_unlock_bh(&ar->data_lock);
|
||||
}
|
||||
|
||||
static int ath10k_start(struct ieee80211_hw *hw)
|
||||
{
|
||||
struct ath10k *ar = hw->priv;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&ar->conf_mutex);
|
||||
|
||||
if (ar->state != ATH10K_STATE_OFF &&
|
||||
ar->state != ATH10K_STATE_RESTARTING) {
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = ath10k_hif_power_up(ar);
|
||||
if (ret) {
|
||||
ath10k_err("could not init hif (%d)\n", ret);
|
||||
ar->state = ATH10K_STATE_OFF;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = ath10k_core_start(ar);
|
||||
if (ret) {
|
||||
ath10k_err("could not init core (%d)\n", ret);
|
||||
ath10k_hif_power_down(ar);
|
||||
ar->state = ATH10K_STATE_OFF;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (ar->state == ATH10K_STATE_OFF)
|
||||
ar->state = ATH10K_STATE_ON;
|
||||
else if (ar->state == ATH10K_STATE_RESTARTING)
|
||||
ar->state = ATH10K_STATE_RESTARTED;
|
||||
|
||||
ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1);
|
||||
if (ret)
|
||||
|
@ -1688,6 +1827,10 @@ static int ath10k_start(struct ieee80211_hw *hw)
|
|||
ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n",
|
||||
ret);
|
||||
|
||||
ath10k_regd_update(ar);
|
||||
|
||||
exit:
|
||||
mutex_unlock(&ar->conf_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1695,18 +1838,48 @@ static void ath10k_stop(struct ieee80211_hw *hw)
|
|||
{
|
||||
struct ath10k *ar = hw->priv;
|
||||
|
||||
/* avoid leaks in case FW never confirms scan for offchannel */
|
||||
mutex_lock(&ar->conf_mutex);
|
||||
if (ar->state == ATH10K_STATE_ON ||
|
||||
ar->state == ATH10K_STATE_RESTARTED ||
|
||||
ar->state == ATH10K_STATE_WEDGED)
|
||||
ath10k_halt(ar);
|
||||
|
||||
ar->state = ATH10K_STATE_OFF;
|
||||
mutex_unlock(&ar->conf_mutex);
|
||||
|
||||
cancel_work_sync(&ar->offchan_tx_work);
|
||||
ath10k_offchan_tx_purge(ar);
|
||||
cancel_work_sync(&ar->restart_work);
|
||||
}
|
||||
|
||||
static void ath10k_config_ps(struct ath10k *ar)
|
||||
{
|
||||
struct ath10k_generic_iter ar_iter;
|
||||
|
||||
lockdep_assert_held(&ar->conf_mutex);
|
||||
|
||||
/* During HW reconfiguration mac80211 reports all interfaces that were
|
||||
* running until reconfiguration was started. Since FW doesn't have any
|
||||
* vdevs at this point we must not iterate over this interface list.
|
||||
* This setting will be updated upon add_interface(). */
|
||||
if (ar->state == ATH10K_STATE_RESTARTED)
|
||||
return;
|
||||
|
||||
memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
|
||||
ar_iter.ar = ar;
|
||||
|
||||
ieee80211_iterate_active_interfaces_atomic(
|
||||
ar->hw, IEEE80211_IFACE_ITER_NORMAL,
|
||||
ath10k_ps_iter, &ar_iter);
|
||||
|
||||
if (ar_iter.ret)
|
||||
ath10k_warn("failed to set ps config (%d)\n", ar_iter.ret);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
@ -1718,18 +1891,8 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
|
|||
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_PS)
|
||||
ath10k_config_ps(ar);
|
||||
|
||||
if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
|
||||
if (conf->flags & IEEE80211_CONF_MONITOR)
|
||||
|
@ -1738,6 +1901,7 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
|
|||
ret = ath10k_monitor_destroy(ar);
|
||||
}
|
||||
|
||||
ath10k_wmi_flush_tx(ar);
|
||||
mutex_unlock(&ar->conf_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
@ -1859,6 +2023,16 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
|
|||
ath10k_warn("Failed to set PSPOLL count: %d\n", ret);
|
||||
}
|
||||
|
||||
ret = ath10k_mac_set_rts(arvif, ar->hw->wiphy->rts_threshold);
|
||||
if (ret)
|
||||
ath10k_warn("failed to set rts threshold for vdev %d (%d)\n",
|
||||
arvif->vdev_id, ret);
|
||||
|
||||
ret = ath10k_mac_set_frag(arvif, ar->hw->wiphy->frag_threshold);
|
||||
if (ret)
|
||||
ath10k_warn("failed to set frag threshold for vdev %d (%d)\n",
|
||||
arvif->vdev_id, ret);
|
||||
|
||||
if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
|
||||
ar->monitor_present = true;
|
||||
|
||||
|
@ -2363,6 +2537,8 @@ static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif,
|
|||
u32 value = 0;
|
||||
int ret = 0;
|
||||
|
||||
lockdep_assert_held(&ar->conf_mutex);
|
||||
|
||||
if (arvif->vdev_type != WMI_VDEV_TYPE_STA)
|
||||
return 0;
|
||||
|
||||
|
@ -2558,11 +2734,16 @@ static void ath10k_set_rts_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
|
|||
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);
|
||||
lockdep_assert_held(&arvif->ar->conf_mutex);
|
||||
|
||||
ar_iter->ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id,
|
||||
WMI_VDEV_PARAM_RTS_THRESHOLD,
|
||||
rts);
|
||||
/* During HW reconfiguration mac80211 reports all interfaces that were
|
||||
* running until reconfiguration was started. Since FW doesn't have any
|
||||
* vdevs at this point we must not iterate over this interface list.
|
||||
* This setting will be updated upon add_interface(). */
|
||||
if (ar_iter->ar->state == ATH10K_STATE_RESTARTED)
|
||||
return;
|
||||
|
||||
ar_iter->ret = ath10k_mac_set_rts(arvif, rts);
|
||||
if (ar_iter->ret)
|
||||
ath10k_warn("Failed to set RTS threshold for VDEV: %d\n",
|
||||
arvif->vdev_id);
|
||||
|
@ -2581,8 +2762,9 @@ static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
|
|||
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);
|
||||
ieee80211_iterate_active_interfaces_atomic(
|
||||
hw, IEEE80211_IFACE_ITER_NORMAL,
|
||||
ath10k_set_rts_iter, &ar_iter);
|
||||
mutex_unlock(&ar->conf_mutex);
|
||||
|
||||
return ar_iter.ret;
|
||||
|
@ -2593,17 +2775,17 @@ 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);
|
||||
lockdep_assert_held(&arvif->ar->conf_mutex);
|
||||
|
||||
ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id,
|
||||
WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
|
||||
frag);
|
||||
/* During HW reconfiguration mac80211 reports all interfaces that were
|
||||
* running until reconfiguration was started. Since FW doesn't have any
|
||||
* vdevs at this point we must not iterate over this interface list.
|
||||
* This setting will be updated upon add_interface(). */
|
||||
if (ar_iter->ar->state == ATH10K_STATE_RESTARTED)
|
||||
return;
|
||||
|
||||
ar_iter->ret = ret;
|
||||
ar_iter->ret = ath10k_mac_set_frag(arvif, frag);
|
||||
if (ar_iter->ret)
|
||||
ath10k_warn("Failed to set frag threshold for VDEV: %d\n",
|
||||
arvif->vdev_id);
|
||||
|
@ -2622,8 +2804,9 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
|
|||
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);
|
||||
ieee80211_iterate_active_interfaces_atomic(
|
||||
hw, IEEE80211_IFACE_ITER_NORMAL,
|
||||
ath10k_set_frag_iter, &ar_iter);
|
||||
mutex_unlock(&ar->conf_mutex);
|
||||
|
||||
return ar_iter.ret;
|
||||
|
@ -2632,6 +2815,7 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
|
|||
static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
|
||||
{
|
||||
struct ath10k *ar = hw->priv;
|
||||
bool skip;
|
||||
int ret;
|
||||
|
||||
/* mac80211 doesn't care if we really xmit queued frames or not
|
||||
|
@ -2639,16 +2823,29 @@ static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
|
|||
if (drop)
|
||||
return;
|
||||
|
||||
ret = wait_event_timeout(ar->htt->empty_tx_wq, ({
|
||||
mutex_lock(&ar->conf_mutex);
|
||||
|
||||
if (ar->state == ATH10K_STATE_WEDGED)
|
||||
goto skip;
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
skip = (ar->state == ATH10K_STATE_WEDGED);
|
||||
|
||||
(empty || skip);
|
||||
}), ATH10K_FLUSH_TIMEOUT_HZ);
|
||||
if (ret <= 0)
|
||||
|
||||
if (ret <= 0 || skip)
|
||||
ath10k_warn("tx not flushed\n");
|
||||
|
||||
skip:
|
||||
mutex_unlock(&ar->conf_mutex);
|
||||
}
|
||||
|
||||
/* TODO: Implement this function properly
|
||||
|
@ -2660,6 +2857,83 @@ static int ath10k_tx_last_beacon(struct ieee80211_hw *hw)
|
|||
return 1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int ath10k_suspend(struct ieee80211_hw *hw,
|
||||
struct cfg80211_wowlan *wowlan)
|
||||
{
|
||||
struct ath10k *ar = hw->priv;
|
||||
int ret;
|
||||
|
||||
ar->is_target_paused = false;
|
||||
|
||||
ret = ath10k_wmi_pdev_suspend_target(ar);
|
||||
if (ret) {
|
||||
ath10k_warn("could not suspend target (%d)\n", ret);
|
||||
return 1;
|
||||
}
|
||||
|
||||
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);
|
||||
goto resume;
|
||||
} else if (ret == 0) {
|
||||
ath10k_warn("suspend timed out - target pause event never came\n");
|
||||
goto resume;
|
||||
}
|
||||
|
||||
ret = ath10k_hif_suspend(ar);
|
||||
if (ret) {
|
||||
ath10k_warn("could not suspend hif (%d)\n", ret);
|
||||
goto resume;
|
||||
}
|
||||
|
||||
return 0;
|
||||
resume:
|
||||
ret = ath10k_wmi_pdev_resume_target(ar);
|
||||
if (ret)
|
||||
ath10k_warn("could not resume target (%d)\n", ret);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ath10k_resume(struct ieee80211_hw *hw)
|
||||
{
|
||||
struct ath10k *ar = hw->priv;
|
||||
int ret;
|
||||
|
||||
ret = ath10k_hif_resume(ar);
|
||||
if (ret) {
|
||||
ath10k_warn("could not resume hif (%d)\n", ret);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ret = ath10k_wmi_pdev_resume_target(ar);
|
||||
if (ret) {
|
||||
ath10k_warn("could not resume target (%d)\n", ret);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ath10k_restart_complete(struct ieee80211_hw *hw)
|
||||
{
|
||||
struct ath10k *ar = hw->priv;
|
||||
|
||||
mutex_lock(&ar->conf_mutex);
|
||||
|
||||
/* If device failed to restart it will be in a different state, e.g.
|
||||
* ATH10K_STATE_WEDGED */
|
||||
if (ar->state == ATH10K_STATE_RESTARTED) {
|
||||
ath10k_info("device successfully recovered\n");
|
||||
ar->state = ATH10K_STATE_ON;
|
||||
}
|
||||
|
||||
mutex_unlock(&ar->conf_mutex);
|
||||
}
|
||||
|
||||
static const struct ieee80211_ops ath10k_ops = {
|
||||
.tx = ath10k_tx,
|
||||
.start = ath10k_start,
|
||||
|
@ -2680,6 +2954,11 @@ static const struct ieee80211_ops ath10k_ops = {
|
|||
.set_frag_threshold = ath10k_set_frag_threshold,
|
||||
.flush = ath10k_flush,
|
||||
.tx_last_beacon = ath10k_tx_last_beacon,
|
||||
.restart_complete = ath10k_restart_complete,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = ath10k_suspend,
|
||||
.resume = ath10k_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
#define RATETAB_ENT(_rate, _rateid, _flags) { \
|
||||
|
@ -2948,8 +3227,10 @@ int ath10k_mac_register(struct ath10k *ar)
|
|||
channels = kmemdup(ath10k_2ghz_channels,
|
||||
sizeof(ath10k_2ghz_channels),
|
||||
GFP_KERNEL);
|
||||
if (!channels)
|
||||
return -ENOMEM;
|
||||
if (!channels) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
band = &ar->mac.sbands[IEEE80211_BAND_2GHZ];
|
||||
band->n_channels = ARRAY_SIZE(ath10k_2ghz_channels);
|
||||
|
@ -2968,11 +3249,8 @@ int ath10k_mac_register(struct ath10k *ar)
|
|||
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;
|
||||
ret = -ENOMEM;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
band = &ar->mac.sbands[IEEE80211_BAND_5GHZ];
|
||||
|
@ -3036,25 +3314,30 @@ int ath10k_mac_register(struct ath10k *ar)
|
|||
ath10k_reg_notifier);
|
||||
if (ret) {
|
||||
ath10k_err("Regulatory initialization failed\n");
|
||||
return ret;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
ret = ieee80211_register_hw(ar->hw);
|
||||
if (ret) {
|
||||
ath10k_err("ieee80211 registration failed: %d\n", ret);
|
||||
return ret;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
if (!ath_is_world_regd(&ar->ath_common.regulatory)) {
|
||||
ret = regulatory_hint(ar->hw->wiphy,
|
||||
ar->ath_common.regulatory.alpha2);
|
||||
if (ret)
|
||||
goto exit;
|
||||
goto err_unregister;
|
||||
}
|
||||
|
||||
return 0;
|
||||
exit:
|
||||
|
||||
err_unregister:
|
||||
ieee80211_unregister_hw(ar->hw);
|
||||
err_free:
|
||||
kfree(ar->mac.sbands[IEEE80211_BAND_2GHZ].channels);
|
||||
kfree(ar->mac.sbands[IEEE80211_BAND_5GHZ].channels);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ 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);
|
||||
void ath10k_halt(struct ath10k *ar);
|
||||
|
||||
static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif)
|
||||
{
|
||||
|
|
|
@ -54,6 +54,8 @@ 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 void ath10k_pci_device_reset(struct ath10k *ar);
|
||||
static int ath10k_pci_reset_target(struct ath10k *ar);
|
||||
|
||||
static const struct ce_attr host_ce_config_wlan[] = {
|
||||
/* host->target HTC control and raw streams */
|
||||
|
@ -718,6 +720,8 @@ static void ath10k_pci_hif_dump_area(struct ath10k *ar)
|
|||
reg_dump_values[i + 1],
|
||||
reg_dump_values[i + 2],
|
||||
reg_dump_values[i + 3]);
|
||||
|
||||
ieee80211_queue_work(ar->hw, &ar->restart_work);
|
||||
}
|
||||
|
||||
static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe,
|
||||
|
@ -744,8 +748,8 @@ static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe,
|
|||
ath10k_ce_per_engine_service(ar, pipe);
|
||||
}
|
||||
|
||||
static void ath10k_pci_hif_post_init(struct ath10k *ar,
|
||||
struct ath10k_hif_cb *callbacks)
|
||||
static void ath10k_pci_hif_set_callbacks(struct ath10k *ar,
|
||||
struct ath10k_hif_cb *callbacks)
|
||||
{
|
||||
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
|
||||
|
||||
|
@ -1263,7 +1267,6 @@ static void ath10k_pci_hif_stop(struct ath10k *ar)
|
|||
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,
|
||||
|
@ -1735,6 +1738,115 @@ static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar)
|
|||
ath10k_pci_sleep(ar);
|
||||
}
|
||||
|
||||
static int ath10k_pci_hif_power_up(struct ath10k *ar)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
ret = ath10k_pci_reset_target(ar);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
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_ps;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_ce:
|
||||
ath10k_pci_ce_deinit(ar);
|
||||
err_ps:
|
||||
if (!ath10k_target_ps)
|
||||
ath10k_do_pci_sleep(ar);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ath10k_pci_hif_power_down(struct ath10k *ar)
|
||||
{
|
||||
ath10k_pci_ce_deinit(ar);
|
||||
if (!ath10k_target_ps)
|
||||
ath10k_do_pci_sleep(ar);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
#define ATH10K_PCI_PM_CONTROL 0x44
|
||||
|
||||
static int ath10k_pci_hif_suspend(struct ath10k *ar)
|
||||
{
|
||||
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
|
||||
struct pci_dev *pdev = ar_pci->pdev;
|
||||
u32 val;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static int ath10k_pci_hif_resume(struct ath10k *ar)
|
||||
{
|
||||
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
|
||||
struct pci_dev *pdev = ar_pci->pdev;
|
||||
u32 val;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
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,
|
||||
|
@ -1743,8 +1855,14 @@ static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
|
|||
.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,
|
||||
.set_callbacks = ath10k_pci_hif_set_callbacks,
|
||||
.get_free_queue_number = ath10k_pci_hif_get_free_queue_number,
|
||||
.power_up = ath10k_pci_hif_power_up,
|
||||
.power_down = ath10k_pci_hif_power_down,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = ath10k_pci_hif_suspend,
|
||||
.resume = ath10k_pci_hif_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
static void ath10k_pci_ce_tasklet(unsigned long ptr)
|
||||
|
@ -2059,9 +2177,9 @@ static int ath10k_pci_reset_target(struct ath10k *ar)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void ath10k_pci_device_reset(struct ath10k_pci *ar_pci)
|
||||
static void ath10k_pci_device_reset(struct ath10k *ar)
|
||||
{
|
||||
struct ath10k *ar = ar_pci->ar;
|
||||
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
|
||||
void __iomem *mem = ar_pci->mem;
|
||||
int i;
|
||||
u32 val;
|
||||
|
@ -2118,7 +2236,7 @@ static void ath10k_pci_dump_features(struct ath10k_pci *ar_pci)
|
|||
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:
|
||||
case ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND:
|
||||
ath10k_dbg(ATH10K_DBG_PCI, "QCA988X_1.0 workaround enabled\n");
|
||||
break;
|
||||
}
|
||||
|
@ -2145,7 +2263,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
|
|||
|
||||
switch (pci_dev->device) {
|
||||
case QCA988X_1_0_DEVICE_ID:
|
||||
set_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features);
|
||||
set_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features);
|
||||
break;
|
||||
case QCA988X_2_0_DEVICE_ID:
|
||||
set_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features);
|
||||
|
@ -2158,8 +2276,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
|
|||
|
||||
ath10k_pci_dump_features(ar_pci);
|
||||
|
||||
ar = ath10k_core_create(ar_pci, ar_pci->dev, ATH10K_BUS_PCI,
|
||||
&ath10k_pci_hif_ops);
|
||||
ar = ath10k_core_create(ar_pci, ar_pci->dev, &ath10k_pci_hif_ops);
|
||||
if (!ar) {
|
||||
ath10k_err("ath10k_core_create failed!\n");
|
||||
ret = -EINVAL;
|
||||
|
@ -2167,7 +2284,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
|
|||
}
|
||||
|
||||
/* Enable QCA988X_1.0 HW workarounds */
|
||||
if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features))
|
||||
if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features))
|
||||
spin_lock_init(&ar_pci->hw_v1_workaround_lock);
|
||||
|
||||
ar_pci->ar = ar;
|
||||
|
@ -2247,54 +2364,14 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
|
|||
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;
|
||||
goto err_intr;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_ce:
|
||||
ath10k_pci_ce_deinit(ar);
|
||||
err_intr:
|
||||
ath10k_pci_stop_intr(ar);
|
||||
err_iomap:
|
||||
|
@ -2345,128 +2422,6 @@ static void ath10k_pci_remove(struct pci_dev *pdev)
|
|||
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 = {
|
||||
|
@ -2474,7 +2429,6 @@ static struct pci_driver ath10k_pci_driver = {
|
|||
.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)
|
||||
|
|
|
@ -152,7 +152,7 @@ struct service_to_pipe {
|
|||
|
||||
enum ath10k_pci_features {
|
||||
ATH10K_PCI_FEATURE_MSI_X = 0,
|
||||
ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND = 1,
|
||||
ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND = 1,
|
||||
|
||||
/* keep last */
|
||||
ATH10K_PCI_FEATURE_COUNT
|
||||
|
@ -311,7 +311,7 @@ static inline void ath10k_pci_write32(struct ath10k *ar, u32 offset,
|
|||
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)) {
|
||||
if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features)) {
|
||||
unsigned long irq_flags;
|
||||
|
||||
spin_lock_irqsave(&ar_pci->hw_v1_workaround_lock, irq_flags);
|
||||
|
|
|
@ -27,6 +27,13 @@ void ath10k_wmi_flush_tx(struct ath10k *ar)
|
|||
{
|
||||
int ret;
|
||||
|
||||
lockdep_assert_held(&ar->conf_mutex);
|
||||
|
||||
if (ar->state == ATH10K_STATE_WEDGED) {
|
||||
ath10k_warn("wmi flush skipped - device is wedged anyway\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ret = wait_event_timeout(ar->wmi.wq,
|
||||
atomic_read(&ar->wmi.pending_tx_count) == 0,
|
||||
5*HZ);
|
||||
|
@ -111,7 +118,7 @@ static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb,
|
|||
|
||||
trace_ath10k_wmi_cmd(cmd_id, skb->data, skb->len);
|
||||
|
||||
status = ath10k_htc_send(ar->htc, ar->wmi.eid, skb);
|
||||
status = ath10k_htc_send(&ar->htc, ar->wmi.eid, skb);
|
||||
if (status) {
|
||||
dev_kfree_skb_any(skb);
|
||||
atomic_dec(&ar->wmi.pending_tx_count);
|
||||
|
@ -501,8 +508,8 @@ static void ath10k_wmi_update_tim(struct ath10k *ar,
|
|||
ie = (u8 *)cfg80211_find_ie(WLAN_EID_TIM, ies,
|
||||
(u8 *)skb_tail_pointer(bcn) - ies);
|
||||
if (!ie) {
|
||||
/* highly unlikely for mac80211 */
|
||||
ath10k_warn("no tim ie found;\n");
|
||||
if (arvif->vdev_type != WMI_VDEV_TYPE_IBSS)
|
||||
ath10k_warn("no tim ie found;\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1114,7 +1121,7 @@ int ath10k_wmi_connect_htc_service(struct ath10k *ar)
|
|||
/* connect to control service */
|
||||
conn_req.service_id = ATH10K_HTC_SVC_ID_WMI_CONTROL;
|
||||
|
||||
status = ath10k_htc_connect_service(ar->htc, &conn_req, &conn_resp);
|
||||
status = ath10k_htc_connect_service(&ar->htc, &conn_req, &conn_resp);
|
||||
if (status) {
|
||||
ath10k_warn("failed to connect to WMI CONTROL service status: %d\n",
|
||||
status);
|
||||
|
@ -1748,6 +1755,9 @@ int ath10k_wmi_vdev_install_key(struct ath10k *ar,
|
|||
if (arg->key_data)
|
||||
memcpy(cmd->key_data, arg->key_data, arg->key_len);
|
||||
|
||||
ath10k_dbg(ATH10K_DBG_WMI,
|
||||
"wmi vdev install key idx %d cipher %d len %d\n",
|
||||
arg->key_idx, arg->key_cipher, arg->key_len);
|
||||
return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_INSTALL_KEY_CMDID);
|
||||
}
|
||||
|
||||
|
@ -2011,6 +2021,9 @@ int ath10k_wmi_peer_assoc(struct ath10k *ar,
|
|||
cmd->peer_vht_rates.tx_mcs_set =
|
||||
__cpu_to_le32(arg->peer_vht_rates.tx_mcs_set);
|
||||
|
||||
ath10k_dbg(ATH10K_DBG_WMI,
|
||||
"wmi peer assoc vdev %d addr %pM\n",
|
||||
arg->vdev_id, arg->addr);
|
||||
return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_ASSOC_CMDID);
|
||||
}
|
||||
|
||||
|
@ -2079,3 +2092,22 @@ int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id)
|
|||
ath10k_dbg(ATH10K_DBG_WMI, "wmi request stats %d\n", (int)stats_id);
|
||||
return ath10k_wmi_cmd_send(ar, skb, WMI_REQUEST_STATS_CMDID);
|
||||
}
|
||||
|
||||
int ath10k_wmi_force_fw_hang(struct ath10k *ar,
|
||||
enum wmi_force_fw_hang_type type, u32 delay_ms)
|
||||
{
|
||||
struct wmi_force_fw_hang_cmd *cmd;
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
cmd = (struct wmi_force_fw_hang_cmd *)skb->data;
|
||||
cmd->type = __cpu_to_le32(type);
|
||||
cmd->delay_ms = __cpu_to_le32(delay_ms);
|
||||
|
||||
ath10k_dbg(ATH10K_DBG_WMI, "wmi force fw hang %d delay %d\n",
|
||||
type, delay_ms);
|
||||
return ath10k_wmi_cmd_send(ar, skb, WMI_FORCE_FW_HANG_CMDID);
|
||||
}
|
||||
|
|
|
@ -416,6 +416,7 @@ enum wmi_cmd_id {
|
|||
WMI_PDEV_FTM_INTG_CMDID,
|
||||
WMI_VDEV_SET_KEEPALIVE_CMDID,
|
||||
WMI_VDEV_GET_KEEPALIVE_CMDID,
|
||||
WMI_FORCE_FW_HANG_CMDID,
|
||||
|
||||
/* GPIO Configuration */
|
||||
WMI_GPIO_CONFIG_CMDID = WMI_CMD_GRP(WMI_GRP_GPIO),
|
||||
|
@ -2972,6 +2973,22 @@ struct wmi_sta_keepalive_cmd {
|
|||
struct wmi_sta_keepalive_arp_resp arp_resp;
|
||||
} __packed;
|
||||
|
||||
enum wmi_force_fw_hang_type {
|
||||
WMI_FORCE_FW_HANG_ASSERT = 1,
|
||||
WMI_FORCE_FW_HANG_NO_DETECT,
|
||||
WMI_FORCE_FW_HANG_CTRL_EP_FULL,
|
||||
WMI_FORCE_FW_HANG_EMPTY_POINT,
|
||||
WMI_FORCE_FW_HANG_STACK_OVERFLOW,
|
||||
WMI_FORCE_FW_HANG_INFINITE_LOOP,
|
||||
};
|
||||
|
||||
#define WMI_FORCE_FW_HANG_RANDOM_TIME 0xFFFFFFFF
|
||||
|
||||
struct wmi_force_fw_hang_cmd {
|
||||
__le32 type;
|
||||
__le32 delay_ms;
|
||||
} __packed;
|
||||
|
||||
#define ATH10K_RTS_MAX 2347
|
||||
#define ATH10K_FRAGMT_THRESHOLD_MIN 540
|
||||
#define ATH10K_FRAGMT_THRESHOLD_MAX 2346
|
||||
|
@ -3048,5 +3065,7 @@ int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg);
|
|||
int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar,
|
||||
const struct wmi_pdev_set_wmm_params_arg *arg);
|
||||
int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id);
|
||||
int ath10k_wmi_force_fw_hang(struct ath10k *ar,
|
||||
enum wmi_force_fw_hang_type type, u32 delay_ms);
|
||||
|
||||
#endif /* _WMI_H_ */
|
||||
|
|
Loading…
Reference in New Issue