[PATCH v3 09/13] wifi: ath12k: Power up root PD

Raj Kumar Bhagat
2024-12-09T16:57:45Z

From: Sowmiya Sree Elavalagan <quic_ssreeela@quicinc.com>

Q6 processor acts as rootPD, other hardware like IPQ5332 which are
attached to Q6 act as userPDs. WCSS driver handles loading and booting
of rootPD, while the ath12k driver boots the userPD.
Get the rproc handle from the DTS entry and boot the rootPD if it
is not already powered on. Register to the rproc notifier to monitor
the rproc state, this allows ath12k driver to know power up/down
sequence of the rootPD. Power up the rootPD and wait for a power-up
notification from the notifier callback before powering up the userPDs.

Tested-on: IPQ5332 hw1.0 AHB WLAN.WBE.1.3.1-00130-QCAHKSWPL_SILICONZ-1

Signed-off-by: Sowmiya Sree Elavalagan <quic_ssreeela@quicinc.com>
Signed-off-by: Raj Kumar Bhagat <quic_rajkbhag@quicinc.com>
---
drivers/net/wireless/ath/ath12k/ahb.c | 128 ++++++++++++++++++++++++++
drivers/net/wireless/ath/ath12k/ahb.h | 7 ++
2 files changed, 135 insertions(+)

diff --git a/drivers/net/wireless/ath/ath12k/ahb.c b/drivers/net/wireless/ath/ath12k/ahb.c
index fcd949faea9f..82bfd675b45b 100644
--- a/drivers/net/wireless/ath/ath12k/ahb.c
+++ b/drivers/net/wireless/ath/ath12k/ahb.c
@@ -554,6 +554,124 @@ static const struct ath12k_hif_ops ath12k_ahb_hif_ops_ipq5332 = {
.map_service_to_pipe = ath12k_ahb_map_service_to_pipe,
};

+static int ath12k_ahb_root_pd_state_notifier(struct notifier_block *nb,
+ const unsigned long event, void *data)
+{
+ struct ath12k_ahb *ab_ahb = container_of(nb, struct ath12k_ahb, root_pd_nb);
+ struct ath12k_base *ab = ab_ahb->ab;
+
+ if (event == ATH12K_RPROC_AFTER_POWERUP) {
+ ath12k_dbg(ab, ATH12K_DBG_AHB, "Root PD is UP\n");
+ complete(&ab_ahb->rootpd_ready);
+ }
+
+ return 0;
+}
+
+static int ath12k_ahb_register_rproc_notifier(struct ath12k_base *ab)
+{
+ struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);
+
+ ab_ahb->root_pd_nb.notifier_call = ath12k_ahb_root_pd_state_notifier;
+ init_completion(&ab_ahb->rootpd_ready);
+
+ ab_ahb->root_pd_notifier = qcom_register_ssr_notifier(ab_ahb->tgt_rproc->name,
+ &ab_ahb->root_pd_nb);
+
+ if (!ab_ahb->root_pd_notifier)
+ return -EINVAL;
+
+ return 0;
+}
+
+static void ath12k_ahb_unregister_rproc_notifier(struct ath12k_base *ab)
+{
+ struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);
+
+ if (!ab_ahb->root_pd_notifier) {
+ ath12k_err(ab, "Rproc notifier not registered\n");
+ return;
+ }
+
+ qcom_unregister_ssr_notifier(ab_ahb->root_pd_notifier,
+ &ab_ahb->root_pd_nb);
+}
+
+static int ath12k_ahb_get_rproc(struct ath12k_base *ab)
+{
+ struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);
+ struct device *dev = ab->dev;
+ struct rproc *prproc;
+ phandle rproc_phandle;
+
+ if (of_property_read_u32(dev->of_node, "qcom,rproc", &rproc_phandle)) {
+ ath12k_err(ab, "failed to get q6_rproc handle\n");
+ return -ENOENT;
+ }
+
+ prproc = rproc_get_by_phandle(rproc_phandle);
+ if (!prproc) {
+ ath12k_err(ab, "failed to get rproc\n");
+ return -EINVAL;
+ }
+ ab_ahb->tgt_rproc = prproc;
+
+ return 0;
+}
+
+static int ath12k_ahb_boot_root_pd(struct ath12k_base *ab)
+{
+ struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);
+ unsigned long time_left;
+ int ret;
+
+ ret = rproc_boot(ab_ahb->tgt_rproc);
+ if (ret < 0) {
+ ath12k_err(ab, "RootPD boot failed\n");
+ return ret;
+ }
+
+ time_left = wait_for_completion_timeout(&ab_ahb->rootpd_ready,
+ ATH12K_ROOTPD_READY_TIMEOUT);
+ if (!time_left) {
+ ath12k_err(ab, "RootPD ready wait timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int ath12k_ahb_configure_rproc(struct ath12k_base *ab)
+{
+ struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);
+ int ret;
+
+ ret = ath12k_ahb_get_rproc(ab);
+ if (ret < 0) {
+ ath12k_err(ab, "failed to get rproc: %d\n", ret);
+ return ret;
+ }
+
+ ret = ath12k_ahb_register_rproc_notifier(ab);
+ if (ret < 0) {
+ ath12k_err(ab, "failed to register rproc notifier\n");
+ return ret;
+ }
+
+ if (ab_ahb->tgt_rproc->state != RPROC_RUNNING) {
+ ret = ath12k_ahb_boot_root_pd(ab);
+ if (ret < 0) {
+ ath12k_err(ab, "failed to boot the remote processor Q6\n");
+ goto unreg_notifier;
+ }
+ }
+ return 0;
+
+unreg_notifier:
+ ath12k_ahb_unregister_rproc_notifier(ab);
+ return ret;
+}
+
static int ath12k_ahb_clock_init(struct ath12k_base *ab)
{
struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);
@@ -695,6 +813,7 @@ static int ath12k_ahb_probe(struct platform_device *pdev)
struct ath12k_base *ab;
const struct ath12k_hif_ops *hif_ops;
struct device_node *mem_node;
+ struct ath12k_ahb *ab_ahb;
enum ath12k_hw_rev hw_rev;
u32 addr;
int ret;
@@ -726,6 +845,8 @@ static int ath12k_ahb_probe(struct platform_device *pdev)
ab->pdev = pdev;
ab->hw_rev = hw_rev;
platform_set_drvdata(pdev, ab);
+ ab_ahb = ath12k_ab_to_ahb(ab);
+ ab_ahb->ab = ab;

/* Set fixed_mem_region to true for platforms that support fixed memory
* reservation from DT. If memory is reserved from DT for FW, ath12k driver
@@ -765,6 +886,12 @@ static int ath12k_ahb_probe(struct platform_device *pdev)

ath12k_ahb_init_qmi_ce_config(ab);

+ ret = ath12k_ahb_configure_rproc(ab);
+ if (ret) {
+ ath12k_err(ab, "failed to configure rproc: %d\n", ret);
+ goto err_ce_free;
+ }
+
ret = ath12k_ahb_config_irq(ab);
if (ret) {
ath12k_err(ab, "failed to configure irq: %d\n", ret);
@@ -809,6 +936,7 @@ static void ath12k_ahb_remove_prepare(struct ath12k_base *ab)
set_bit(ATH12K_FLAG_UNREGISTERING, &ab->dev_flags);
cancel_work_sync(&ab->restart_work);
cancel_work_sync(&ab->qmi.event_work);
+ ath12k_ahb_unregister_rproc_notifier(ab);
}

static void ath12k_ahb_free_resources(struct ath12k_base *ab)
diff --git a/drivers/net/wireless/ath/ath12k/ahb.h b/drivers/net/wireless/ath/ath12k/ahb.h
index bd0366a79587..d1fc63091fb0 100644
--- a/drivers/net/wireless/ath/ath12k/ahb.h
+++ b/drivers/net/wireless/ath/ath12k/ahb.h
@@ -7,6 +7,7 @@
#define ATH12K_AHB_H

#include <linux/clk.h>
+#include <linux/remoteproc/qcom_rproc.h>
#include "core.h"

#define ATH12K_AHB_RECOVERY_TIMEOUT (3 * HZ)
@@ -16,6 +17,8 @@
#define ATH12K_AHB_SMP2P_SMEM_VALUE_MASK 0xFFFFFFFF
#define ATH12K_PCI_CE_WAKE_IRQ 2
#define ATH12K_PCI_IRQ_CE0_OFFSET 3
+#define ATH12K_ROOTPD_READY_TIMEOUT (5 * HZ)
+#define ATH12K_RPROC_AFTER_POWERUP QCOM_SSR_AFTER_POWERUP

enum ath12k_ahb_smp2p_msg_id {
ATH12K_AHB_POWER_SAVE_ENTER = 1,
@@ -25,8 +28,12 @@ enum ath12k_ahb_smp2p_msg_id {
struct ath12k_base;

struct ath12k_ahb {
+ struct ath12k_base *ab;
struct rproc *tgt_rproc;
struct clk *xo_clk;
+ struct completion rootpd_ready;
+ struct notifier_block root_pd_nb;
+ void *root_pd_notifier;
};

static inline struct ath12k_ahb *ath12k_ab_to_ahb(struct ath12k_base *ab)
--
2.34.1