diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 36d551052d4d..0c41bcaad7ac 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -236,6 +236,16 @@ config QCOM_SMSM Say yes here to support the Qualcomm Shared Memory State Machine. The state machine is represented by bits in shared memory. +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. + config QTI_PMIC_GLINK tristate "Enable support for PMIC GLINK" depends on RPMSG diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 90d5ac784d01..b2abc1d79626 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_QCOM_SMEM) += smem.o obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o obj-$(CONFIG_QCOM_SMP2P) += smp2p.o obj-$(CONFIG_QCOM_SMSM) += smsm.o +obj-$(CONFIG_QCOM_SMP2P_SLEEPSTATE) += smp2p_sleepstate.o obj-$(CONFIG_QTI_PMIC_GLINK) += pmic_glink.o obj-$(CONFIG_QTI_BATTERY_GLINK_DEBUG) += qti_battery_debug.o obj-$(CONFIG_QTI_CHARGER_ULOG_GLINK) += charger-ulog-glink.o diff --git a/drivers/soc/qcom/smp2p_sleepstate.c b/drivers/soc/qcom/smp2p_sleepstate.c new file mode 100644 index 000000000000..3db35d02c525 --- /dev/null +++ b/drivers/soc/qcom/smp2p_sleepstate.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014-2022 Qualcomm Innovation Center, Inc. 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(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; + } + + notify_ws = wakeup_source_register(&pdev->dev, "smp2p-sleepstate"); + if (!notify_ws) { + return -ENOMEM; + goto err_ws; + } + + 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_unregister(notify_ws); + __pm_relax(notify_ws); +err_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");