soc: pinctrl-lpi: remove pm ops

pm runtime resume and suspend operation uses
linux clock API to vote/unvote lpass hw resources.
When autosuspend funtionality is used, suspend op
is triggered from a worker thread upon timer expiration,
can block if there is an ongoing clock
operation holding global prepare/unprepare mutex
lock in common clock framwork. If the ongoing
clock operation calls get_sync on pinctrl-lpi,
the get_sync gets scheduled out due to ongoing suspend
by worker thread. This leads to dead lock between
get_sync and suspend, waiting for each other to
complete. Avoid using autosuspend functionality in
pinctrl-lpi to avoid race conditions in clock APIs.

As auto suspend functionality is not required, lpass
hw voting/devoting happens in synchronous manner, can
be done directly by calling DSP voting APIs. Hence remove
runtime pm ops in lpi pinctrl driver.

Change-Id: I86df5e8e502c6466feea196fe8649c34e046aa26
Signed-off-by: Abhilash Sathyanarayana <quic_abhisath@quicinc.com>
This commit is contained in:
Abhilash Sathyanarayana 2024-01-16 13:20:24 +05:30
commit f3c67e033c

View file

@ -26,9 +26,6 @@
#include "core.h"
#include "pinctrl-utils.h"
#define LPI_AUTO_SUSPEND_DELAY 100 /* delay in msec */
#define LPI_AUTO_SUSPEND_DELAY_ERROR 1 /* delay in msec */
#define LPI_ADDRESS_SIZE 0x20000
#define LPI_SLEW_ADDRESS_SIZE 0x1000
@ -118,8 +115,6 @@ struct lpi_gpio_state {
struct clk *lpass_core_hw_vote;
struct clk *lpass_audio_hw_vote;
struct mutex slew_access_lock;
bool core_hw_vote_status;
struct mutex core_hw_vote_lock;
};
static const char *const lpi_gpio_groups[] = {
@ -144,12 +139,54 @@ static const char *const lpi_gpio_functions[] = {
[LPI_GPIO_FUNC_INDEX_FUNC5] = LPI_GPIO_FUNC_FUNC5,
};
int lpi_pinctrl_runtime_suspend(struct device *dev);
int lpi_pinctrl_hw_vote(struct device *dev)
{
int ret = 0;
struct lpi_gpio_state *state = dev_get_drvdata(dev);
struct clk *hw_vote = state->lpass_core_hw_vote;
if (state->lpass_core_hw_vote == NULL) {
dev_dbg(dev, "%s: Invalid core hw node\n", __func__);
if (state->lpass_audio_hw_vote == NULL) {
dev_dbg(dev, "%s: Invalid audio hw node\n", __func__);
return -EINVAL;
}
hw_vote = state->lpass_audio_hw_vote;
}
dev_dbg(dev, "%s: lpi_pinctrl_hw_vote\n", __func__);
ret = digital_cdc_rsc_mgr_hw_vote_enable(hw_vote);
if (ret < 0) {
dev_err(dev, "%s:lpass core hw island enable failed\n",
__func__);
}
dev_dbg(dev, "%s: lpi_pinctrl_hw_vote done\n", __func__);
return ret;
}
int lpi_pinctrl_hw_unvote(struct device *dev)
{
struct lpi_gpio_state *state = dev_get_drvdata(dev);
struct clk *hw_vote = state->lpass_core_hw_vote;
if (state->lpass_core_hw_vote == NULL) {
dev_dbg(dev, "%s: Invalid core hw node\n", __func__);
if (state->lpass_audio_hw_vote == NULL) {
dev_dbg(dev, "%s: Invalid audio hw node\n", __func__);
return -EINVAL;
}
hw_vote = state->lpass_audio_hw_vote;
}
dev_dbg(dev, "%s: lpi_pinctrl_hw_unvote\n", __func__);
digital_cdc_rsc_mgr_hw_vote_disable(hw_vote);
dev_dbg(dev, "%s: lpi_pinctrl_hw_unvote done\n", __func__);
return 0;
}
static int lpi_gpio_read(struct lpi_gpio_pad *pad, unsigned int addr)
{
int ret = 0;
struct lpi_gpio_state *state = dev_get_drvdata(lpi_dev);
static DEFINE_RATELIMIT_STATE(rtl, 1 * HZ, 1);
if (!lpi_dev_up) {
@ -158,52 +195,38 @@ static int lpi_gpio_read(struct lpi_gpio_pad *pad, unsigned int addr)
__func__);
return 0;
}
pm_runtime_get_sync(lpi_dev);
mutex_lock(&state->core_hw_vote_lock);
if (!state->core_hw_vote_status) {
if (__ratelimit(&rtl))
pr_err("%s: core hw vote clk is not enabled\n",
__func__);
ret = -EINVAL;
ret = lpi_pinctrl_hw_vote(lpi_dev);
if (ret < 0)
goto err;
}
ret = ioread32(pad->base + pad->offset + addr);
if (ret < 0)
pr_err("%s: read 0x%x failed\n", __func__, addr);
lpi_pinctrl_hw_unvote(lpi_dev);
err:
mutex_unlock(&state->core_hw_vote_lock);
pm_runtime_mark_last_busy(lpi_dev);
pm_runtime_put_autosuspend(lpi_dev);
return ret;
}
static int lpi_gpio_write(struct lpi_gpio_pad *pad, unsigned int addr,
unsigned int val)
{
struct lpi_gpio_state *state = dev_get_drvdata(lpi_dev);
int ret = 0;
static DEFINE_RATELIMIT_STATE(rtl, 1 * HZ, 1);
if (!lpi_dev_up) {
return 0;
}
pm_runtime_get_sync(lpi_dev);
mutex_lock(&state->core_hw_vote_lock);
if (!state->core_hw_vote_status) {
if (__ratelimit(&rtl))
pr_err("%s: core hw vote clk is not enabled\n",
__func__);
ret = -EINVAL;
ret = lpi_pinctrl_hw_vote(lpi_dev);
if (ret < 0)
goto err;
}
iowrite32(val, pad->base + pad->offset + addr);
lpi_pinctrl_hw_unvote(lpi_dev);
err:
mutex_unlock(&state->core_hw_vote_lock);
pm_runtime_mark_last_busy(lpi_dev);
pm_runtime_put_autosuspend(lpi_dev);
return ret;
}
@ -516,39 +539,6 @@ static int lpi_notifier_service_cb(struct notifier_block *this,
return NOTIFY_OK;
}
int lpi_pinctrl_suspend(struct device *dev)
{
int ret = 0;
dev_dbg(dev, "%s: system suspend\n", __func__);
if ((!pm_runtime_enabled(dev) || !pm_runtime_suspended(dev))) {
ret = lpi_pinctrl_runtime_suspend(dev);
if (!ret) {
/*
* Synchronize runtime-pm and system-pm states:
* At this point, we are already suspended. If
* runtime-pm still thinks its active, then
* make sure its status is in sync with HW
* status. The three below calls let the
* runtime-pm know that we are suspended
* already without re-invoking the suspend
* callback
*/
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
pm_runtime_enable(dev);
}
}
return ret;
}
int lpi_pinctrl_resume(struct device *dev)
{
return 0;
}
static struct notifier_block service_nb = {
.notifier_call = lpi_notifier_service_cb,
.priority = -INT_MAX,
@ -557,7 +547,6 @@ static struct notifier_block service_nb = {
static void lpi_pinctrl_ssr_disable(struct device *dev, void *data)
{
lpi_dev_up = false;
lpi_pinctrl_suspend(dev);
}
static const struct snd_event_ops lpi_pinctrl_ssr_ops = {
@ -773,7 +762,6 @@ static int lpi_pinctrl_probe(struct platform_device *pdev)
state->chip.can_sleep = false;
mutex_init(&state->slew_access_lock);
mutex_init(&state->core_hw_vote_lock);
state->ctrl = devm_pinctrl_register(dev, pctrldesc, state);
if (IS_ERR(state->ctrl))
@ -832,12 +820,6 @@ static int lpi_pinctrl_probe(struct platform_device *pdev)
}
state->lpass_audio_hw_vote = lpass_audio_hw_vote;
state->core_hw_vote_status = false;
pm_runtime_set_autosuspend_delay(&pdev->dev, LPI_AUTO_SUSPEND_DELAY);
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
pm_runtime_enable(&pdev->dev);
return 0;
err_snd_evt:
@ -845,7 +827,6 @@ err_snd_evt:
err_range:
gpiochip_remove(&state->chip);
err_chip:
mutex_destroy(&state->core_hw_vote_lock);
mutex_destroy(&state->slew_access_lock);
err_io:
return ret;
@ -855,13 +836,9 @@ static int lpi_pinctrl_remove(struct platform_device *pdev)
{
struct lpi_gpio_state *state = platform_get_drvdata(pdev);
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
snd_event_client_deregister(&pdev->dev);
audio_notifier_deregister("lpi_tlmm");
gpiochip_remove(&state->chip);
mutex_destroy(&state->core_hw_vote_lock);
mutex_destroy(&state->slew_access_lock);
return 0;
@ -874,79 +851,9 @@ static const struct of_device_id lpi_pinctrl_of_match[] = {
MODULE_DEVICE_TABLE(of, lpi_pinctrl_of_match);
int lpi_pinctrl_runtime_resume(struct device *dev)
{
struct lpi_gpio_state *state = dev_get_drvdata(dev);
int ret = 0;
struct clk *hw_vote = state->lpass_core_hw_vote;
if (state->lpass_core_hw_vote == NULL) {
dev_dbg(dev, "%s: Invalid core hw node\n", __func__);
if (state->lpass_audio_hw_vote == NULL) {
dev_dbg(dev, "%s: Invalid audio hw node\n", __func__);
return 0;
}
hw_vote = state->lpass_audio_hw_vote;
}
mutex_lock(&state->core_hw_vote_lock);
ret = digital_cdc_rsc_mgr_hw_vote_enable(hw_vote);
if (ret < 0) {
pm_runtime_set_autosuspend_delay(dev,
LPI_AUTO_SUSPEND_DELAY_ERROR);
dev_err(dev, "%s:lpass core hw island enable failed\n",
__func__);
goto exit;
} else {
state->core_hw_vote_status = true;
}
pm_runtime_set_autosuspend_delay(dev, LPI_AUTO_SUSPEND_DELAY);
exit:
mutex_unlock(&state->core_hw_vote_lock);
return 0;
}
int lpi_pinctrl_runtime_suspend(struct device *dev)
{
struct lpi_gpio_state *state = dev_get_drvdata(dev);
struct clk *hw_vote = state->lpass_core_hw_vote;
if (state->lpass_core_hw_vote == NULL) {
dev_dbg(dev, "%s: Invalid core hw node\n", __func__);
if (state->lpass_audio_hw_vote == NULL) {
dev_dbg(dev, "%s: Invalid audio hw node\n", __func__);
return 0;
}
hw_vote = state->lpass_audio_hw_vote;
}
mutex_lock(&state->core_hw_vote_lock);
if (state->core_hw_vote_status) {
digital_cdc_rsc_mgr_hw_vote_disable(hw_vote);
state->core_hw_vote_status = false;
}
mutex_unlock(&state->core_hw_vote_lock);
return 0;
}
static const struct dev_pm_ops lpi_pinctrl_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(
lpi_pinctrl_suspend,
lpi_pinctrl_resume
)
SET_RUNTIME_PM_OPS(
lpi_pinctrl_runtime_suspend,
lpi_pinctrl_runtime_resume,
NULL
)
};
static struct platform_driver lpi_pinctrl_driver = {
.driver = {
.name = "qcom-lpi-pinctrl",
.pm = &lpi_pinctrl_dev_pm_ops,
.of_match_table = lpi_pinctrl_of_match,
.suppress_bind_attrs = true,
},