diff --git a/drivers/soc/qcom/smp2p.c b/drivers/soc/qcom/smp2p.c index f87af11fde98..83cfd0f5947b 100644 --- a/drivers/soc/qcom/smp2p.c +++ b/drivers/soc/qcom/smp2p.c @@ -1,7 +1,7 @@ // 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 @@ -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 @@ unreg_ws: 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);