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

Commit 95bf9f5f authored by Arun Prakash's avatar Arun Prakash Committed by Gerrit - the friendly Code Review server
Browse files

soc: qcom: smp2p: Add support for suspend to disk



Add support in smp2p driver for suspend to disk feature.
Handling SMEM enumeration during resume from hibernation.

Change-Id: I5dd17057cc68a293a1a510ebedafc93defd8c556
Signed-off-by: default avatarArun Prakash <app@codeaurora.org>
parent a2839ca3
Loading
Loading
Loading
Loading
+100 −6
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2015, Sony Mobile Communications AB.
 * Copyright (c) 2012-2013, 2018-2019 The Linux Foundation. All rights reserved.
 * Copyright (c) 2012-2013, 2018-2020 The Linux Foundation. All rights reserved.
 */

#include <linux/interrupt.h>
@@ -632,7 +632,7 @@ static int qcom_smp2p_probe(struct platform_device *pdev)
		goto release_mbox;

	for_each_available_child_of_node(pdev->dev.of_node, node) {
		entry = devm_kzalloc(&pdev->dev, sizeof(*entry), GFP_KERNEL);
		entry = kzalloc(sizeof(*entry), GFP_KERNEL);
		if (!entry) {
			ret = -ENOMEM;
			goto unwind_interfaces;
@@ -686,11 +686,15 @@ static int qcom_smp2p_probe(struct platform_device *pdev)
	wakeup_source_unregister(smp2p->ws);

unwind_interfaces:
	list_for_each_entry(entry, &smp2p->inbound, node)
	list_for_each_entry(entry, &smp2p->inbound, node) {
		irq_domain_remove(entry->domain);
		kfree(entry);
	}

	list_for_each_entry(entry, &smp2p->outbound, node)
	list_for_each_entry(entry, &smp2p->outbound, node) {
		qcom_smem_state_unregister(entry->state);
		kfree(entry);
	}

	smp2p->out->valid_entries = 0;

@@ -711,11 +715,15 @@ static int qcom_smp2p_remove(struct platform_device *pdev)

	wakeup_source_unregister(smp2p->ws);

	list_for_each_entry(entry, &smp2p->inbound, node)
	list_for_each_entry(entry, &smp2p->inbound, node) {
		irq_domain_remove(entry->domain);
		kfree(entry);
	}

	list_for_each_entry(entry, &smp2p->outbound, node)
	list_for_each_entry(entry, &smp2p->outbound, node) {
		qcom_smem_state_unregister(entry->state);
		kfree(entry);
	}

	mbox_free_channel(smp2p->mbox_chan);

@@ -724,6 +732,91 @@ static int qcom_smp2p_remove(struct platform_device *pdev)
	return 0;
}

static int qcom_smp2p_restore(struct device *dev)
{
	int ret = 0;
	struct qcom_smp2p *smp2p = dev_get_drvdata(dev);
	struct smp2p_entry *entry;
	struct device_node *node;
	struct platform_device *pdev = container_of(dev, struct
					platform_device, dev);

	ret = qcom_smp2p_alloc_outbound_item(smp2p);
	if (ret < 0)
		goto print_err;

	for_each_available_child_of_node(pdev->dev.of_node, node) {
		entry = kzalloc(sizeof(*entry), GFP_KERNEL);
		if (!entry) {
			ret = -ENOMEM;
			goto print_err;
		}

		entry->smp2p = smp2p;
		spin_lock_init(&entry->lock);
		ret = of_property_read_string(node, "qcom,entry-name",
								&entry->name);
		if (ret < 0)
			goto rel_entry;

		if (!of_property_read_bool(node, "interrupt-controller")) {
			ret = qcom_smp2p_outbound_entry(smp2p, entry, node);
			if (ret < 0)
				goto rel_entry;
			list_add(&entry->node, &smp2p->outbound);
		} else {
			kfree(entry);
		}
	}

	smp2p->ws = wakeup_source_register(&pdev->dev, "smp2p");
	enable_irq_wake(smp2p->irq);
	/* Kick the outgoing edge after allocating entries */
	qcom_smp2p_kick(smp2p);

rel_entry:
	kfree(entry);

print_err:
	if (ret < 0 && ret != -EEXIST)
		dev_err(dev, "failed to alloc items ret = %d\n", ret);

	return ret;
}

static int qcom_smp2p_freeze(struct device *dev)
{
	struct qcom_smp2p *smp2p = dev_get_drvdata(dev);
	struct smp2p_entry *entry;
	struct smp2p_entry *next_entry;

	disable_irq_wake(smp2p->irq);
	/* Walk through the out bound list and release state and entry */
	list_for_each_entry_safe(entry, next_entry, &smp2p->outbound, node) {
		qcom_smem_state_unregister(entry->state);
		list_del(&entry->node);
		kfree(entry);
	}
	INIT_LIST_HEAD(&smp2p->outbound);

	/* Walk through the in bound list and reset last value */
	list_for_each_entry_safe(entry, next_entry, &smp2p->inbound, node) {
		entry->last_value = 0;
	}
	/* make null to point it to valid smem item during first interrupt */
	smp2p->in = NULL;
	smp2p->valid_entries = 0;
	/* remove wakeup source */
	wakeup_source_unregister(smp2p->ws);
	return 0;
}

static const struct dev_pm_ops qcom_smp2p_pm_ops = {
	.freeze = qcom_smp2p_freeze,
	.restore = qcom_smp2p_restore,
	.thaw = qcom_smp2p_restore,
};

static const struct of_device_id qcom_smp2p_of_match[] = {
	{ .compatible = "qcom,smp2p" },
	{}
@@ -736,6 +829,7 @@ static struct platform_driver qcom_smp2p_driver = {
	.driver  = {
		.name  = "qcom_smp2p",
		.of_match_table = qcom_smp2p_of_match,
		.pm = &qcom_smp2p_pm_ops,
	},
};
module_platform_driver(qcom_smp2p_driver);