summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/wireless/ath/ath10k/Makefile1
-rw-r--r--drivers/net/wireless/ath/ath10k/ce.c6
-rw-r--r--drivers/net/wireless/ath/ath10k/core.c1
-rw-r--r--drivers/net/wireless/ath/ath10k/mac.c3
-rw-r--r--drivers/net/wireless/ath/ath10k/qmi.c230
-rw-r--r--drivers/net/wireless/ath/ath10k/qmi.h19
-rw-r--r--drivers/net/wireless/ath/ath10k/snoc.c48
-rw-r--r--drivers/net/wireless/ath/ath10k/snoc.h33
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi.c3
9 files changed, 338 insertions, 6 deletions
diff --git a/drivers/net/wireless/ath/ath10k/Makefile b/drivers/net/wireless/ath/ath10k/Makefile
index 25b23bf2c8e6..5fe8bc184868 100644
--- a/drivers/net/wireless/ath/ath10k/Makefile
+++ b/drivers/net/wireless/ath/ath10k/Makefile
@@ -26,6 +26,7 @@ ath10k_pci-y += pci.o \
ce.o
obj-$(CONFIG_ATH10K_TARGET_SNOC) += ath10k_snoc.o
ath10k_snoc-y += snoc.o \
+ qmi.o \
ce.o
ath10k_pci-$(CONFIG_ATH10K_AHB) += ahb.o
diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c
index b8a3a1ecabaa..9cda1303c9e1 100644
--- a/drivers/net/wireless/ath/ath10k/ce.c
+++ b/drivers/net/wireless/ath/ath10k/ce.c
@@ -455,6 +455,9 @@ int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state,
u32 desc_flags = 0;
int ret = 0;
+ if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
+ return -ESHUTDOWN;
+
if (nbytes > ce_state->src_sz_max)
ath10k_warn(ar, "%s: send more we can (nbytes: %d, max: %d)\n",
__func__, nbytes, ce_state->src_sz_max);
@@ -942,6 +945,9 @@ void ath10k_ce_per_engine_service_any(struct ath10k *ar)
struct ath10k_ce_pipe *ce_state;
struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
+ return;
+
if (ar->target_version == ATH10K_HW_WCN3990)
intr_summary = 0xFFF;
else
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index d37ed66d767b..c7ac209afae1 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -1536,7 +1536,6 @@ static void ath10k_core_restart(struct work_struct *work)
struct ath10k *ar = container_of(work, struct ath10k, restart_work);
set_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags);
- ath10k_gen_set_base_mac_addr(ar, ar->base_mac_addr);
/* Place a barrier to make sure the compiler doesn't reorder
* CRASH_FLUSH and calling other functions.
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 265744c75f82..35e5d980ed49 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -4450,7 +4450,8 @@ static int ath10k_start(struct ieee80211_hw *hw)
ar->state = ATH10K_STATE_ON;
break;
case ATH10K_STATE_RESTARTING:
- ath10k_halt(ar);
+ if (!test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
+ ath10k_halt(ar);
ar->state = ATH10K_STATE_RESTARTED;
break;
case ATH10K_STATE_ON:
diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c
new file mode 100644
index 000000000000..c65cceb2e4cb
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/qmi.c
@@ -0,0 +1,230 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <soc/qcom/subsystem_notif.h>
+#include <soc/qcom/subsystem_restart.h>
+#include <soc/qcom/service-notifier.h>
+#include "core.h"
+#include "qmi.h"
+#include "snoc.h"
+#include <soc/qcom/icnss.h>
+
+static int
+ath10k_snoc_service_notifier_notify(struct notifier_block *nb,
+ unsigned long notification, void *data)
+{
+ struct ath10k_snoc *ar_snoc = container_of(nb, struct ath10k_snoc,
+ service_notifier_nb);
+ enum pd_subsys_state *state = data;
+ struct ath10k *ar = ar_snoc->ar;
+
+ switch (notification) {
+ case SERVREG_NOTIF_SERVICE_STATE_DOWN_V01:
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "Service down, data: 0x%pK\n",
+ data);
+
+ if (!state || *state != ROOT_PD_SHUTDOWN)
+ atomic_set(&ar_snoc->fw_crashed, 1);
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "PD went down %d\n",
+ ar_snoc->fw_crashed);
+ break;
+ case SERVREG_NOTIF_SERVICE_STATE_UP_V01:
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "Service up\n");
+ queue_work(ar->workqueue, &ar->restart_work);
+ break;
+ default:
+ ath10k_dbg(ar, ATH10K_DBG_SNOC,
+ "Service state Unknown, notification: 0x%lx\n",
+ notification);
+ return NOTIFY_DONE;
+ }
+ return NOTIFY_OK;
+}
+
+static int ath10k_snoc_get_service_location_notify(struct notifier_block *nb,
+ unsigned long opcode,
+ void *data)
+{
+ struct ath10k_snoc *ar_snoc = container_of(nb, struct ath10k_snoc,
+ get_service_nb);
+ struct ath10k *ar = ar_snoc->ar;
+ struct pd_qmi_client_data *pd = data;
+ int curr_state;
+ int ret;
+ int i;
+ struct ath10k_service_notifier_context *notifier;
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "Get service notify opcode: %lu\n",
+ opcode);
+
+ if (opcode != LOCATOR_UP)
+ return NOTIFY_DONE;
+
+ if (!pd->total_domains) {
+ ath10k_err(ar, "Did not find any domains\n");
+ ret = -ENOENT;
+ goto out;
+ }
+
+ notifier = kcalloc(pd->total_domains,
+ sizeof(struct ath10k_service_notifier_context),
+ GFP_KERNEL);
+ if (!notifier) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ar_snoc->service_notifier_nb.notifier_call =
+ ath10k_snoc_service_notifier_notify;
+
+ for (i = 0; i < pd->total_domains; i++) {
+ ath10k_dbg(ar, ATH10K_DBG_SNOC,
+ "%d: domain_name: %s, instance_id: %d\n", i,
+ pd->domain_list[i].name,
+ pd->domain_list[i].instance_id);
+
+ notifier[i].handle =
+ service_notif_register_notifier(
+ pd->domain_list[i].name,
+ pd->domain_list[i].instance_id,
+ &ar_snoc->service_notifier_nb,
+ &curr_state);
+ notifier[i].instance_id = pd->domain_list[i].instance_id;
+ strlcpy(notifier[i].name, pd->domain_list[i].name,
+ QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1);
+
+ if (IS_ERR(notifier[i].handle)) {
+ ath10k_err(ar, "%d: Unable to register notifier for %s(0x%x)\n",
+ i, pd->domain_list->name,
+ pd->domain_list->instance_id);
+ ret = PTR_ERR(notifier[i].handle);
+ goto free_handle;
+ }
+ }
+
+ ar_snoc->service_notifier = notifier;
+ ar_snoc->total_domains = pd->total_domains;
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "PD restart enabled\n");
+
+ return NOTIFY_OK;
+
+free_handle:
+ for (i = 0; i < pd->total_domains; i++) {
+ if (notifier[i].handle) {
+ service_notif_unregister_notifier(
+ notifier[i].handle,
+ &ar_snoc->service_notifier_nb);
+ }
+ }
+ kfree(notifier);
+
+out:
+ ath10k_err(ar, "PD restart not enabled: %d\n", ret);
+
+ return NOTIFY_OK;
+}
+
+int ath10k_snoc_pd_restart_enable(struct ath10k *ar)
+{
+ int ret;
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "Get service location\n");
+
+ ar_snoc->get_service_nb.notifier_call =
+ ath10k_snoc_get_service_location_notify;
+ ret = get_service_location(ATH10K_SERVICE_LOCATION_CLIENT_NAME,
+ ATH10K_WLAN_SERVICE_NAME,
+ &ar_snoc->get_service_nb);
+ if (ret) {
+ ath10k_err(ar, "Get service location failed: %d\n", ret);
+ goto out;
+ }
+
+ return 0;
+out:
+ ath10k_err(ar, "PD restart not enabled: %d\n", ret);
+ return ret;
+}
+
+int ath10k_snoc_pdr_unregister_notifier(struct ath10k *ar)
+{
+ int i;
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+
+ for (i = 0; i < ar_snoc->total_domains; i++) {
+ if (ar_snoc->service_notifier[i].handle)
+ service_notif_unregister_notifier(
+ ar_snoc->service_notifier[i].handle,
+ &ar_snoc->service_notifier_nb);
+ }
+
+ kfree(ar_snoc->service_notifier);
+
+ ar_snoc->service_notifier = NULL;
+
+ return 0;
+}
+
+static int ath10k_snoc_modem_notifier_nb(struct notifier_block *nb,
+ unsigned long code,
+ void *data)
+{
+ struct notif_data *notif = data;
+ struct ath10k_snoc *ar_snoc = container_of(nb, struct ath10k_snoc,
+ modem_ssr_nb);
+ struct ath10k *ar = ar_snoc->ar;
+
+ if (code != SUBSYS_BEFORE_SHUTDOWN)
+ return NOTIFY_OK;
+
+ if (notif->crashed)
+ atomic_set(&ar_snoc->fw_crashed, 1);
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "Modem went down %d\n",
+ ar_snoc->fw_crashed);
+ if (notif->crashed)
+ queue_work(ar->workqueue, &ar->restart_work);
+
+ return NOTIFY_OK;
+}
+
+int ath10k_snoc_modem_ssr_register_notifier(struct ath10k *ar)
+{
+ int ret = 0;
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+
+ ar_snoc->modem_ssr_nb.notifier_call = ath10k_snoc_modem_notifier_nb;
+
+ ar_snoc->modem_notify_handler =
+ subsys_notif_register_notifier("modem", &ar_snoc->modem_ssr_nb);
+
+ if (IS_ERR(ar_snoc->modem_notify_handler)) {
+ ret = PTR_ERR(ar_snoc->modem_notify_handler);
+ ath10k_err(ar, "Modem register notifier failed: %d\n", ret);
+ }
+
+ return ret;
+}
+
+int ath10k_snoc_modem_ssr_unregister_notifier(struct ath10k *ar)
+{
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+
+ subsys_notif_unregister_notifier(ar_snoc->modem_notify_handler,
+ &ar_snoc->modem_ssr_nb);
+ ar_snoc->modem_notify_handler = NULL;
+
+ return 0;
+}
+
diff --git a/drivers/net/wireless/ath/ath10k/qmi.h b/drivers/net/wireless/ath/ath10k/qmi.h
new file mode 100644
index 000000000000..f8ba3288753b
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/qmi.h
@@ -0,0 +1,19 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef _QMI_H_
+#define _QMI_H_
+int ath10k_snoc_pd_restart_enable(struct ath10k *ar);
+int ath10k_snoc_modem_ssr_register_notifier(struct ath10k *ar);
+int ath10k_snoc_modem_ssr_unregister_notifier(struct ath10k *ar);
+int ath10k_snoc_pdr_unregister_notifier(struct ath10k *ar);
+
+#endif
diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c
index dc5f6fdaa9dc..89042dcf70a0 100644
--- a/drivers/net/wireless/ath/ath10k/snoc.c
+++ b/drivers/net/wireless/ath/ath10k/snoc.c
@@ -24,6 +24,7 @@
#include "htc.h"
#include "ce.h"
#include "snoc.h"
+#include "qmi.h"
#include <soc/qcom/icnss.h>
#include <linux/of.h>
#include <linux/platform_device.h>
@@ -413,6 +414,20 @@ static struct ath10k_shadow_reg_cfg target_shadow_reg_cfg_map[] = {
{ 11, WCN3990_DST_WR_INDEX_OFFSET},
};
+static bool ath10k_snoc_has_fw_crashed(struct ath10k *ar)
+{
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+
+ return atomic_read(&ar_snoc->fw_crashed);
+}
+
+static void ath10k_snoc_fw_crashed_clear(struct ath10k *ar)
+{
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+
+ atomic_set(&ar_snoc->fw_crashed, 0);
+}
+
void ath10k_snoc_write32(struct ath10k *ar, u32 offset, u32 value)
{
struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
@@ -655,6 +670,9 @@ static int ath10k_snoc_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
"snoc tx item %d paddr %pad len %d n_items %d\n",
i, &items[i].paddr, items[i].len, n_items);
+ if (ath10k_snoc_has_fw_crashed(ar))
+ return -EINVAL;
+
err = ath10k_ce_send_nolock(ce_pipe,
items[i].transfer_context,
items[i].paddr,
@@ -867,11 +885,17 @@ static void ath10k_snoc_hif_stop(struct ath10k *ar)
{
if (!ar)
return;
- ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif stop\n");
- ath10k_snoc_irq_disable(ar);
+ if (ath10k_snoc_has_fw_crashed(ar) ||
+ test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) {
+ ath10k_snoc_free_irq(ar);
+ } else {
+ ath10k_snoc_irq_disable(ar);
+ }
+
ath10k_snoc_flush(ar);
napi_synchronize(&ar->napi);
napi_disable(&ar->napi);
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif stop\n");
}
static int ath10k_snoc_alloc_pipes(struct ath10k *ar)
@@ -1087,9 +1111,14 @@ static int ath10k_snoc_bus_configure(struct ath10k *ar)
static int ath10k_snoc_hif_start(struct ath10k *ar)
{
- ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif start\n");
+ if (ath10k_snoc_has_fw_crashed(ar)) {
+ ath10k_snoc_request_irq(ar);
+ ath10k_snoc_fw_crashed_clear(ar);
+ }
ath10k_snoc_irq_enable(ar);
ath10k_snoc_rx_post(ar);
+
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif start\n");
return 0;
}
@@ -1110,7 +1139,8 @@ static int ath10k_snoc_hif_power_up(struct ath10k *ar)
ath10k_dbg(ar, ATH10K_DBG_SNOC, "%s:WCN3990 driver state = %d\n",
__func__, ar->state);
- if (ar->state == ATH10K_STATE_ON) {
+ if (ar->state == ATH10K_STATE_ON ||
+ test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) {
ret = ath10k_snoc_bus_configure(ar);
if (ret)
ath10k_err(ar, "failed to configure bus: %d\n", ret);
@@ -1133,6 +1163,10 @@ static int ath10k_snoc_napi_poll(struct napi_struct *ctx, int budget)
struct ath10k *ar = container_of(ctx, struct ath10k, napi);
int done = 0;
+ if (ath10k_snoc_has_fw_crashed(ar)) {
+ napi_complete(ctx);
+ return done;
+ }
ath10k_ce_per_engine_service_any(ar);
done = ath10k_htt_txrx_compl_task(ar, budget);
@@ -1254,6 +1288,10 @@ static int ath10k_snoc_probe(struct platform_device *pdev)
ath10k_err(ar, "failed to register driver core: %d\n", ret);
goto err_free_irq;
}
+
+ ath10k_snoc_modem_ssr_register_notifier(ar);
+ ath10k_snoc_pd_restart_enable(ar);
+
ath10k_dbg(ar, ATH10K_DBG_SNOC, "%s:WCN3990 probed\n", __func__);
return 0;
@@ -1282,6 +1320,8 @@ static int ath10k_snoc_remove(struct platform_device *pdev)
return -EINVAL;
ath10k_core_unregister(ar);
+ ath10k_snoc_pdr_unregister_notifier(ar);
+ ath10k_snoc_modem_ssr_unregister_notifier(ar);
ath10k_snoc_free_irq(ar);
ath10k_snoc_release_resource(ar);
ath10k_snoc_free_pipes(ar);
diff --git a/drivers/net/wireless/ath/ath10k/snoc.h b/drivers/net/wireless/ath/ath10k/snoc.h
index 0a5f5bff37ec..c62519b2a340 100644
--- a/drivers/net/wireless/ath/ath10k/snoc.h
+++ b/drivers/net/wireless/ath/ath10k/snoc.h
@@ -16,8 +16,11 @@
#include "hw.h"
#include "ce.h"
#include "pci.h"
+#include <soc/qcom/service-locator.h>
#define ATH10K_SNOC_RX_POST_RETRY_MS 50
#define CE_POLL_PIPE 4
+#define ATH10K_SERVICE_LOCATION_CLIENT_NAME "ATH10K-WLAN"
+#define ATH10K_WLAN_SERVICE_NAME "wlan/fw"
/* struct snoc_state: SNOC target state
* @pipe_cfg_addr: pipe configuration address
@@ -88,6 +91,17 @@ struct ath10k_target_info {
u32 soc_version;
};
+/* struct ath10k_service_notifier_context: service notification context
+ * @handle: notifier handle
+ * @instance_id: domain instance id
+ * @name: domain name
+ */
+struct ath10k_service_notifier_context {
+ void *handle;
+ u32 instance_id;
+ char name[QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1];
+};
+
/* struct ath10k_snoc: SNOC info struct
* @dev: device structure
* @ar:ath10k base structure
@@ -101,6 +115,13 @@ struct ath10k_target_info {
* @rx_post_retry: rx buffer post processing timer
* @vaddr_rri_on_ddr: virtual address for RRI
* @is_driver_probed: flag to indicate driver state
+ * @modem_ssr_nb: notifier callback for modem notification
+ * @modem_notify_handler: modem notification handler
+ * @service_notifier: notifier context for service notification
+ * @service_notifier_nb: notifier callback for service notification
+ * @total_domains: no of service domains
+ * @get_service_nb: notifier callback for service discovery
+ * @fw_crashed: fw state flag
*/
struct ath10k_snoc {
struct bus_opaque opaque_ctx;
@@ -115,6 +136,13 @@ struct ath10k_snoc {
u32 ce_irqs[CE_COUNT_MAX];
u32 *vaddr_rri_on_ddr;
bool is_driver_probed;
+ struct notifier_block modem_ssr_nb;
+ void *modem_notify_handler;
+ struct ath10k_service_notifier_context *service_notifier;
+ struct notifier_block service_notifier_nb;
+ int total_domains;
+ struct notifier_block get_service_nb;
+ atomic_t fw_crashed;
};
/* struct ath10k_ce_tgt_pipe_cfg: target pipe configuration
@@ -171,6 +199,11 @@ struct ath10k_wlan_enable_cfg {
struct ath10k_shadow_reg_cfg *shadow_reg_cfg;
};
+struct ath10k_event_pd_down_data {
+ bool crashed;
+ bool fw_rejuvenate;
+};
+
/* enum ath10k_driver_mode: ath10k driver mode
* @ATH10K_MISSION: mission mode
* @ATH10K_FTM: ftm mode
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index 4dd60b74853d..cbbba8d79e6b 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -1799,6 +1799,9 @@ int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id)
{
int ret = -EOPNOTSUPP;
+ if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
+ return -ESHUTDOWN;
+
might_sleep();
if (cmd_id == WMI_CMD_UNSUPPORTED) {