#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
+#include <linux/remoteproc.h>
#include "ahb.h"
#include "debug.h"
#include "hif.h"
.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 (IS_ERR(ab_ahb->root_pd_notifier))
+ return PTR_ERR(ab_ahb->root_pd_notifier);
+
+ 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);
+ ab_ahb->root_pd_notifier = NULL;
+}
+
+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 device_node *np;
+ struct rproc *prproc;
+
+ np = of_parse_phandle(dev->of_node, "qcom,rproc", 0);
+ if (!np) {
+ ath12k_err(ab, "failed to get q6_rproc handle\n");
+ return -ENOENT;
+ }
+
+ prproc = rproc_get_by_phandle(np->phandle);
+ of_node_put(np);
+ if (!prproc)
+ return dev_err_probe(&ab->pdev->dev, -EPROBE_DEFER,
+ "failed to get rproc\n");
+
+ 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)
+ return ret;
+
+ ret = ath12k_ahb_register_rproc_notifier(ab);
+ if (ret < 0) {
+ ret = dev_err_probe(&ab->pdev->dev, ret,
+ "failed to register rproc notifier\n");
+ goto err_put_rproc;
+ }
+
+ 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 err_unreg_notifier;
+ }
+ }
+ return 0;
+
+err_unreg_notifier:
+ ath12k_ahb_unregister_rproc_notifier(ab);
+
+err_put_rproc:
+ rproc_put(ab_ahb->tgt_rproc);
+ return ret;
+}
+
+static void ath12k_ahb_deconfigure_rproc(struct ath12k_base *ab)
+{
+ struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);
+
+ ath12k_ahb_unregister_rproc_notifier(ab);
+ rproc_put(ab_ahb->tgt_rproc);
+}
+
static int ath12k_ahb_resource_init(struct ath12k_base *ab)
{
struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);
{
struct ath12k_base *ab;
const struct ath12k_hif_ops *hif_ops;
+ struct ath12k_ahb *ab_ahb;
enum ath12k_hw_rev hw_rev;
u32 addr;
int ret;
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
ath12k_ahb_init_qmi_ce_config(ab);
+ ret = ath12k_ahb_configure_rproc(ab);
+ if (ret)
+ goto err_ce_free;
+
ret = ath12k_ahb_config_irq(ab);
if (ret) {
ath12k_err(ab, "failed to configure irq: %d\n", ret);
- goto err_ce_free;
+ goto err_rproc_deconfigure;
}
ret = ath12k_core_init(ab);
if (ret) {
ath12k_err(ab, "failed to init core: %d\n", ret);
- goto err_ce_free;
+ goto err_rproc_deconfigure;
}
return 0;
+err_rproc_deconfigure:
+ ath12k_ahb_deconfigure_rproc(ab);
+
err_ce_free:
ath12k_ce_free_pipes(ab);
ath12k_hal_srng_deinit(ab);
ath12k_ce_free_pipes(ab);
ath12k_ahb_resource_deinit(ab);
+ ath12k_ahb_deconfigure_rproc(ab);
ath12k_core_free(ab);
platform_set_drvdata(pdev, NULL);
}