Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 15608bf6 authored by Govind Singh's avatar Govind Singh Committed by Gerrit - the friendly Code Review server
Browse files

ath10k: Enable Subsystem Restart for ath10k WCN3990 driver



Add service registry and PD indication support
Add support for USER PD service discovery and
associated notifications.
Enable recovery when USER PD DOWN indication is
received.

Change-Id: I548cd231f3d2090c5aa0ed328fbfb909d17467e8
Signed-off-by: default avatarGovind Singh <govinds@codeaurora.org>
parent 27489753
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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
+6 −0
Original line number Diff line number Diff line
@@ -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
+0 −1
Original line number Diff line number Diff line
@@ -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.
+2 −1
Original line number Diff line number Diff line
@@ -4450,6 +4450,7 @@ static int ath10k_start(struct ieee80211_hw *hw)
		ar->state = ATH10K_STATE_ON;
		break;
	case ATH10K_STATE_RESTARTING:
		if (!test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
			ath10k_halt(ar);
		ar->state = ATH10K_STATE_RESTARTED;
		break;
+230 −0
Original line number Diff line number Diff line
/* 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;
}
Loading