提交 1f807827 编写于 作者: J John W. Linville

Merge tag 'for-linville-20130730' of git://github.com/kvalo/ath6kl

......@@ -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");
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));
if (!ar->otp)
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,77 +637,127 @@ int ath10k_core_register(struct ath10k *ar)
goto err_disconnect_htc;
}
status = ath10k_htt_attach_target(ar->htt);
if (status)
goto err_disconnect_htc;
status = ath10k_mac_register(ar);
status = ath10k_htt_attach_target(&ar->htt);
if (status)
goto err_disconnect_htc;
status = ath10k_debug_create(ar);
if (status) {
ath10k_err("unable to initialize debugfs\n");
goto err_unregister_mac;
}
ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1;
return 0;
err_unregister_mac:
ath10k_mac_unregister(ar);
err_disconnect_htc:
ath10k_htc_stop(ar->htc);
ath10k_htc_stop(&ar->htc);
err_htt_detach:
ath10k_htt_detach(ar->htt);
ath10k_htt_detach(&ar->htt);
err_wmi_detach:
ath10k_wmi_detach(ar);
err_htc_destroy:
ath10k_htc_destroy(ar->htc);
err:
return status;
}
EXPORT_SYMBOL(ath10k_core_register);
EXPORT_SYMBOL(ath10k_core_start);
void ath10k_core_unregister(struct ath10k *ar)
void ath10k_core_stop(struct ath10k *ar)
{
/* We must unregister from mac80211 before we stop HTC and HIF.
* Otherwise we will fail to submit commands to FW and mac80211 will be
* unhappy about callback failures. */
ath10k_mac_unregister(ar);
ath10k_htc_stop(ar->htc);
ath10k_htt_detach(ar->htt);
ath10k_htc_stop(&ar->htc);
ath10k_htt_detach(&ar->htt);
ath10k_wmi_detach(ar);
ath10k_htc_destroy(ar->htc);
}
EXPORT_SYMBOL(ath10k_core_unregister);
EXPORT_SYMBOL(ath10k_core_stop);
int ath10k_core_target_suspend(struct ath10k *ar)
/* 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)
{
int ret;
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;
}
ath10k_dbg(ATH10K_DBG_CORE, "%s: called", __func__);
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;
}
ret = ath10k_wmi_pdev_suspend_target(ar);
if (ret)
ath10k_warn("could not suspend target (%d)\n", ret);
ar->target_version = target_info.version;
ar->hw->wiphy->hw_version = target_info.version;
return ret;
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;
}
EXPORT_SYMBOL(ath10k_core_target_suspend);
int ath10k_core_target_resume(struct ath10k *ar)
int ath10k_core_register(struct ath10k *ar)
{
int ret;
int status;
ath10k_dbg(ATH10K_DBG_CORE, "%s: called", __func__);
status = ath10k_core_probe_fw(ar);
if (status) {
ath10k_err("could not probe fw (%d)\n", status);
return status;
}
ret = ath10k_wmi_pdev_resume_target(ar);
if (ret)
ath10k_warn("could not resume target (%d)\n", ret);
status = ath10k_mac_register(ar);
if (status) {
ath10k_err("could not register to mac80211 (%d)\n", status);
goto err_release_fw;
}
return ret;
status = ath10k_debug_create(ar);
if (status) {
ath10k_err("unable to initialize debugfs\n");
goto err_unregister_mac;
}
return 0;
err_unregister_mac:
ath10k_mac_unregister(ar);
err_release_fw:
ath10k_core_free_firmware_files(ar);
return status;
}
EXPORT_SYMBOL(ath10k_core_register);
void ath10k_core_unregister(struct ath10k *ar)
{
/* We must unregister from mac80211 before we stop HTC and HIF.
* Otherwise we will fail to submit commands to FW and mac80211 will be
* unhappy about callback failures. */
ath10k_mac_unregister(ar);
ath10k_core_free_firmware_files(ar);
}
EXPORT_SYMBOL(ath10k_core_target_resume);
EXPORT_SYMBOL(ath10k_core_unregister);
MODULE_AUTHOR("Qualcomm Atheros");
MODULE_DESCRIPTION("Core module for QCA988X PCIe devices.");
......
......@@ -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 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt,
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;
......
......@@ -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_ */
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册