From 51669ff07fa3677181dd123f5117e074e65b6c4a Mon Sep 17 00:00:00 2001 From: Chris Lew Date: Tue, 6 Aug 2019 18:33:11 -0700 Subject: [PATCH] soc: qcom: Add snapshot of smp2p sleepstate driver This snapshot is taken as of msm-4.19 'commit ("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 --- drivers/soc/qcom/Kconfig | 10 +++ drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/smp2p_sleepstate.c | 126 ++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 drivers/soc/qcom/smp2p_sleepstate.c diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index d32190f295e6..669bc96ad601 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -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 diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index cce65a5925a4..826950744ef6 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -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 diff --git a/drivers/soc/qcom/smp2p_sleepstate.c b/drivers/soc/qcom/smp2p_sleepstate.c new file mode 100644 index 000000000000..7d70cf09291e --- /dev/null +++ b/drivers/soc/qcom/smp2p_sleepstate.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014-2019, The Linux Foundation. All rights reserved. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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(¬ify_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(¬ify_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(¬ify_ws); + __pm_relax(¬ify_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");