soc: qcom: Add snapshot of smp2p sleepstate driver

This snapshot is taken as of msm-4.19 'commit <dac6e981b55e>
("Merge "icnss: Skip removing WLAN host driver during recovery"")'.

In addition, replace wakeup_source_trash since API is deprecated.

Change-Id: I0bd88adc9a8e604300b610d7f24dae6cd4e57281
Signed-off-by: Chris Lew <clew@codeaurora.org>
This commit is contained in:
Chris Lew 2019-08-06 18:33:11 -07:00 • committed by Gerrit - the friendly Code Review server
commit 51669ff07f
3 changed files with 137 additions and 0 deletions

View file

@ -238,4 +238,14 @@ config QMP_DEBUGFS_CLIENT
to Alway On processor using QMP transport. Users can echo a message
into an exposed debugfs node to send to AOP. The driver expects the
passed in string argument to be formatted correctly for AOP to read.
config QCOM_SMP2P_SLEEPSTATE
tristate "SMP2P Sleepstate notifier"
depends on QCOM_SMP2P
help
When this option is enabled, notifications are sent to remote procs
for the power state changes on the local processor. The notifications
are sent through the smp2p framework. This driver can also receive
notifications from the remote to prevent suspend on the local
processor.
endmenu

View file

@ -30,3 +30,4 @@ obj-$(CONFIG_QCOM_SDM845_LLCC) += llcc-sdm845.o
obj-$(CONFIG_QCOM_RPMHPD) += rpmhpd.o
obj-$(CONFIG_QCOM_RPMPD) += rpmpd.o
obj-$(CONFIG_QMP_DEBUGFS_CLIENT) += qmp-debugfs-client.o
obj-$(CONFIG_QCOM_SMP2P_SLEEPSTATE) += smp2p_sleepstate.o

View file

@ -0,0 +1,126 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2014-2019, The Linux Foundation. All rights reserved.
*/
#include <linux/module.h>
#include <linux/suspend.h>
#include <linux/platform_device.h>
#include <linux/soc/qcom/smem_state.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/of_irq.h>
#include <linux/of.h>
#include <linux/pm_wakeup.h>
#define PROC_AWAKE_ID 12 /* 12th bit */
#define AWAKE_BIT BIT(PROC_AWAKE_ID)
static struct qcom_smem_state *state;
static struct wakeup_source notify_ws;
/**
* sleepstate_pm_notifier() - PM notifier callback function.
* @nb: Pointer to the notifier block.
* @event: Suspend state event from PM module.
* @unused: Null pointer from PM module.
*
* This function is register as callback function to get notifications
* from the PM module on the system suspend state.
*/
static int sleepstate_pm_notifier(struct notifier_block *nb,
unsigned long event, void *unused)
{
switch (event) {
case PM_SUSPEND_PREPARE:
qcom_smem_state_update_bits(state, AWAKE_BIT, 0);
break;
case PM_POST_SUSPEND:
qcom_smem_state_update_bits(state, AWAKE_BIT, AWAKE_BIT);
break;
}
return NOTIFY_DONE;
}
static struct notifier_block sleepstate_pm_nb = {
.notifier_call = sleepstate_pm_notifier,
.priority = INT_MAX,
};
static irqreturn_t smp2p_sleepstate_handler(int irq, void *ctxt)
{
__pm_wakeup_event(&notify_ws, 200);
return IRQ_HANDLED;
}
static int smp2p_sleepstate_probe(struct platform_device *pdev)
{
int ret;
int irq;
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
state = qcom_smem_state_get(&pdev->dev, 0, &ret);
if (IS_ERR(state))
return PTR_ERR(state);
qcom_smem_state_update_bits(state, AWAKE_BIT, AWAKE_BIT);
ret = register_pm_notifier(&sleepstate_pm_nb);
if (ret) {
dev_err(dev, "%s: power state notif error %d\n", __func__, ret);
return ret;
}
wakeup_source_init(&notify_ws, "smp2p-sleepstate");
irq = of_irq_get_byname(node, "smp2p-sleepstate-in");
if (irq <= 0) {
dev_err(dev, "failed to get irq for smp2p_sleep_state\n");
ret = -EPROBE_DEFER;
goto err;
}
dev_dbg(dev, "got smp2p-sleepstate-in irq %d\n", irq);
ret = devm_request_threaded_irq(dev, irq, NULL,
smp2p_sleepstate_handler,
IRQF_ONESHOT | IRQF_TRIGGER_RISING,
"smp2p_sleepstate", dev);
if (ret) {
dev_err(dev, "fail to register smp2p threaded_irq=%d\n", irq);
goto err;
}
return 0;
err:
wakeup_source_remove(&notify_ws);
__pm_relax(&notify_ws);
unregister_pm_notifier(&sleepstate_pm_nb);
return ret;
}
static const struct of_device_id smp2p_slst_match_table[] = {
{.compatible = "qcom,smp2p-sleepstate"},
{},
};
static struct platform_driver smp2p_sleepstate_driver = {
.probe = smp2p_sleepstate_probe,
.driver = {
.name = "smp2p_sleepstate",
.of_match_table = smp2p_slst_match_table,
},
};
static int __init smp2p_sleepstate_init(void)
{
int ret;
ret = platform_driver_register(&smp2p_sleepstate_driver);
if (ret) {
pr_err("%s: register failed %d\n", __func__, ret);
return ret;
}
return 0;
}
module_init(smp2p_sleepstate_init);
MODULE_DESCRIPTION("SMP2P SLEEP STATE");
MODULE_LICENSE("GPL v2");