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

Commit 9513df13 authored by Aditya Bavanari's avatar Aditya Bavanari
Browse files

soc: pinctrl-lpi: Fix core hw vote issue in LPI GPIO access



Fix core hw vote issue by adding retry logic in
retrieving clock handle. Since the LPI driver is an active
listener to SSR notifications, it gets an early notification
as soon as DSP is up. So in order to avoid access
of LPASS registers of LPI GPIOs before AVS services are up,
check for AVS up, vote for clock and access GPIOs.

Change-Id: I2aca83154d24a3a97af4b5b0b449257f77ab99c1
Signed-off-by: default avatarAditya Bavanari <abavanar@codeaurora.org>
parent 6bb77f8d
Loading
Loading
Loading
Loading
+18 −2
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#include <linux/platform_device.h>
#include <dt-bindings/clock/qcom,audio-ext-clk.h>
#include <dsp/q6afe-v2.h>
#include <dsp/q6core.h>
#include "audio-ext-clk-up.h"

enum {
@@ -147,14 +148,29 @@ static int lpass_hw_vote_prepare(struct clk_hw *hw)
{
	struct audio_ext_clk_priv *clk_priv = to_audio_clk(hw);
	int ret = 0;
	int32_t avs_state = 0;
	uint32_t *client_handle = &clk_priv->lpass_core_hwvote_client_handle;

	if (clk_priv->clk_src == AUDIO_EXT_CLK_LPASS_CORE_HW_VOTE)  {
		ret = afe_vote_lpass_core_hw(AFE_LPASS_CORE_HW_MACRO_BLOCK,
					     "LPASS_HW_MACRO",
			&clk_priv->lpass_core_hwvote_client_handle);
					     client_handle);
		if (ret < 0) {
			pr_err("%s lpass core hw vote failed %d\n",
				__func__, ret);
			/*
			 * DSP returns -EBUSY when AVS services are not up
			 * Check for AVS state and then retry voting
			 * for core hw clock.
			 */
			if (ret == -EBUSY) {
				q6core_is_avs_up(&avs_state);
				if (avs_state)
					ret = afe_vote_lpass_core_hw(
						AFE_LPASS_CORE_HW_MACRO_BLOCK,
						"LPASS_HW_MACRO",
						client_handle);
			}
			return ret;
		}
	}
+13 −1
Original line number Diff line number Diff line
@@ -8690,6 +8690,12 @@ int afe_vote_lpass_core_hw(uint32_t hw_block_id, char *client_name,
		return -EINVAL;
	}

	ret = afe_q6_interface_prepare();
	if (ret != 0) {
		pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
		return ret;
	}

	mutex_lock(&this_afe.afe_cmd_lock);

	memset(cmd_ptr, 0, sizeof(hw_vote_cfg));
@@ -8752,7 +8758,7 @@ EXPORT_SYMBOL(afe_vote_lpass_core_hw);

/*
 * afe_unvote_lpass_core_hw -
 *        Voting for lpass core hardware
 *        unvoting for lpass core hardware
 *
 * @hw_block_id: ID of hw block to vote for
 * @client_handle: Handle for the client
@@ -8765,6 +8771,12 @@ int afe_unvote_lpass_core_hw(uint32_t hw_block_id, uint32_t client_handle)
						&hw_vote_cfg;
	int ret = 0;

	ret = afe_q6_interface_prepare();
	if (ret != 0) {
		pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
		return ret;
	}

	mutex_lock(&this_afe.afe_cmd_lock);

	memset(cmd_ptr, 0, sizeof(hw_vote_cfg));
+3 −2
Original line number Diff line number Diff line
/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2012-2019, 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
@@ -1433,7 +1433,7 @@ static int q6core_init_cal_data(void)
	return ret;
}

static int q6core_is_avs_up(int32_t *avs_state)
int q6core_is_avs_up(int32_t *avs_state)
{
	unsigned long timeout;
	int32_t adsp_ready = 0;
@@ -1471,6 +1471,7 @@ static int q6core_is_avs_up(int32_t *avs_state)

	return ret;
}
EXPORT_SYMBOL(q6core_is_avs_up);

static int q6core_ssr_enable(struct device *dev, void *data)
{
+2 −1
Original line number Diff line number Diff line
/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2012-2019, 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
@@ -207,6 +207,7 @@ int32_t core_get_license_status(uint32_t module_id);

int32_t q6core_load_unload_topo_modules(uint32_t topology_id,
			bool preload_type);
int q6core_is_avs_up(int32_t *avs_state);

#if IS_ENABLED(CONFIG_USE_Q6_32CH_SUPPORT)
static inline bool q6core_use_Q6_32ch_support(void)
+34 −15
Original line number Diff line number Diff line
@@ -501,6 +501,25 @@ static const struct gpio_chip lpi_gpio_template = {
	.dbg_show		= lpi_gpio_dbg_show,
};

static int lpi_get_lpass_core_hw_clk(struct device *dev,
				     struct lpi_gpio_state *state)
{
	struct clk *lpass_core_hw_vote = NULL;
	int ret = 0;

	if (state->lpass_core_hw_vote == NULL) {
		lpass_core_hw_vote = devm_clk_get(dev, "lpass_core_hw_vote");
		if (IS_ERR(lpass_core_hw_vote) || lpass_core_hw_vote == NULL) {
			ret = PTR_ERR(lpass_core_hw_vote);
			dev_dbg(dev, "%s: clk get %s failed %d\n",
				__func__, "lpass_core_hw_vote", ret);
			return ret;
		}
		state->lpass_core_hw_vote = lpass_core_hw_vote;
	}
	return 0;
}

static int lpi_pinctrl_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
@@ -511,7 +530,6 @@ static int lpi_pinctrl_probe(struct platform_device *pdev)
	int ret, npins, i;
	char __iomem *lpi_base;
	u32 reg;
	struct clk *lpass_core_hw_vote = NULL;

	ret = of_property_read_u32(dev->of_node, "reg", &reg);
	if (ret < 0) {
@@ -622,15 +640,10 @@ static int lpi_pinctrl_probe(struct platform_device *pdev)
	}

	/* Register LPASS core hw vote */
	lpass_core_hw_vote = devm_clk_get(&pdev->dev, "lpass_core_hw_vote");
	if (IS_ERR(lpass_core_hw_vote)) {
		ret = PTR_ERR(lpass_core_hw_vote);
		dev_dbg(&pdev->dev, "%s: clk get %s failed %d\n",
			__func__, "lpass_core_hw_vote", ret);
		lpass_core_hw_vote = NULL;
		ret = 0;
	}
	state->lpass_core_hw_vote = lpass_core_hw_vote;
	ret = lpi_get_lpass_core_hw_clk(dev, state);
	if (ret)
		dev_dbg(dev, "%s: unable to get core clk handle %d\n",
			__func__, ret);

	pm_runtime_set_autosuspend_delay(&pdev->dev, LPI_AUTO_SUSPEND_DELAY);
	pm_runtime_use_autosuspend(&pdev->dev);
@@ -671,14 +684,16 @@ static int lpi_pinctrl_runtime_resume(struct device *dev)
	struct lpi_gpio_state *state = dev_get_drvdata(dev);
	int ret = 0;

	if (state->lpass_core_hw_vote == NULL) {
		dev_dbg(dev, "%s: Invalid core hw node\n", __func__);
	ret = lpi_get_lpass_core_hw_clk(dev, state);
	if (ret) {
		dev_err(dev, "%s: unable to get core clk handle %d\n",
			__func__, ret);
		return 0;
	}

	ret = clk_prepare_enable(state->lpass_core_hw_vote);
	if (ret < 0)
		dev_dbg(dev, "%s:lpass core hw enable failed\n",
		dev_err(dev, "%s: lpass core hw enable failed\n",
			__func__);

	pm_runtime_set_autosuspend_delay(dev, LPI_AUTO_SUSPEND_DELAY);
@@ -688,11 +703,15 @@ static int lpi_pinctrl_runtime_resume(struct device *dev)
static int lpi_pinctrl_runtime_suspend(struct device *dev)
{
	struct lpi_gpio_state *state = dev_get_drvdata(dev);
	int ret = 0;

	if (state->lpass_core_hw_vote == NULL) {
		dev_dbg(dev, "%s: Invalid lpass core hw node\n", __func__);
	ret = lpi_get_lpass_core_hw_clk(dev, state);
	if (ret) {
		dev_err(dev, "%s: unable to get core clk handle %d\n",
			__func__, ret);
		return 0;
	}

	clk_disable_unprepare(state->lpass_core_hw_vote);
	return 0;
}