From f64af1ec50d8dd0f3ac46c7bb080ec630ac73e07 Mon Sep 17 00:00:00 2001 From: Anant Goel Date: Tue, 6 Feb 2018 17:46:38 -0800 Subject: [PATCH] soc: qcom: subsystem_notif_virt: Add virtual subsystem notification driver The guest VM uses this driver to communicate subsystem state related notifications to a backend driver via the virtual device's registers. Change-Id: I612fcb641c4d531c1d2c0fd18f44f7ebff040f2c Signed-off-by: Anant Goel --- .../bindings/arm/msm/subsystem_notif_virt.txt | 35 ++++ drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/subsystem_notif_virt.c | 163 ++++++++++++++++++ 3 files changed, 199 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/msm/subsystem_notif_virt.txt create mode 100644 drivers/soc/qcom/subsystem_notif_virt.c diff --git a/Documentation/devicetree/bindings/arm/msm/subsystem_notif_virt.txt b/Documentation/devicetree/bindings/arm/msm/subsystem_notif_virt.txt new file mode 100644 index 000000000000..50fffbe8284c --- /dev/null +++ b/Documentation/devicetree/bindings/arm/msm/subsystem_notif_virt.txt @@ -0,0 +1,35 @@ +Subsystem Notification Virtual Driver + +The guest VM uses this driver to communicate +subsystem state notifications to a backend driver +via the virtual device's registers. + +[Root level node] +Required Properties: +-compatible : Should be "qcom,subsys-notif-virt" + for notifications regarding state. +-reg : The start and size of the virtual device's + register set. +-reg-names : Should be "vdev_base" for virtual device's + base address. +-subsys-names : The name of the subsystem that the + driver is registering to notifications for. +-offset : The offset from the virtual device's register + base where the subsystem state will be written. + +Example: + + subsys_notif_virt: qcom,subsys_notif_virt@2D000000 { + compatible = "qcom,subsys-notif-virt"; + reg = <0x2D000000 0x10>; + reg-names = "vdev_base"; + adsp { + subsys-name = "adsp"; + offset = <0>; + }; + mpss { + subsys-name = "modem"; + offset = <8>; + }; + }; + diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index ba2ff8326cac..8605b750c11d 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -47,6 +47,7 @@ ifdef CONFIG_MSM_SUBSYSTEM_RESTART obj-y += subsystem_notif.o obj-y += subsystem_restart.o obj-y += ramdump.o + obj-$(CONFIG_MSM_GVM_QUIN) += subsystem_notif_virt.o endif obj-$(CONFIG_QPNP_HAPTIC) += qpnp-haptic.o diff --git a/drivers/soc/qcom/subsystem_notif_virt.c b/drivers/soc/qcom/subsystem_notif_virt.c new file mode 100644 index 000000000000..739600f2498d --- /dev/null +++ b/drivers/soc/qcom/subsystem_notif_virt.c @@ -0,0 +1,163 @@ +/* Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void __iomem *base_reg; + +struct state_notifier_block { + const char *subsystem; + struct notifier_block nb; + u32 offset; + void *handle; + struct list_head notifier_list; +}; + +static LIST_HEAD(notifier_block_list); + +static int subsys_state_callback(struct notifier_block *this, + unsigned long value, void *priv) +{ + struct state_notifier_block *notifier = + container_of(this, struct state_notifier_block, nb); + + writel_relaxed(value, base_reg + notifier->offset); + + return NOTIFY_OK; +} + +static int subsys_notif_virt_probe(struct platform_device *pdev) +{ + struct device_node *node; + struct device_node *child = NULL; + struct resource *res; + struct state_notifier_block *notif_block; + int ret = 0; + + if (!pdev) { + dev_err(&pdev->dev, "pdev is NULL\n"); + return -EINVAL; + } + + node = pdev->dev.of_node; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vdev_base"); + base_reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR_OR_NULL(base_reg)) { + dev_err(&pdev->dev, "Memory mapping failed\n"); + return -ENOMEM; + } + + for_each_child_of_node(node, child) { + + notif_block = devm_kmalloc(&pdev->dev, + sizeof(struct state_notifier_block), + GFP_KERNEL); + if (!notif_block) + return -ENOMEM; + + notif_block->subsystem = + of_get_property(child, "subsys-name", NULL); + if (IS_ERR_OR_NULL(notif_block->subsystem)) { + dev_err(&pdev->dev, "Could not find subsystem name\n"); + ret = -EINVAL; + goto err_nb; + } + + notif_block->nb.notifier_call = subsys_state_callback; + + notif_block->handle = + subsys_notif_register_notifier(notif_block->subsystem, + ¬if_block->nb); + if (IS_ERR_OR_NULL(notif_block->handle)) { + dev_err(&pdev->dev, "Could not register SSR notifier cb\n"); + ret = -EINVAL; + goto err_nb; + } + + ret = of_property_read_u32(child, "offset", + ¬if_block->offset); + if (ret) { + dev_err(&pdev->dev, "offset reading for %s failed\n", + notif_block->subsystem); + ret = -EINVAL; + goto err_offset; + } + + list_add_tail(¬if_block->notifier_list, + ¬ifier_block_list); + + } + return 0; + +err_offset: + subsys_notif_unregister_notifier(notif_block->handle, + ¬if_block->nb); +err_nb: + kfree(notif_block); + return ret; +} + +static int subsys_notif_virt_remove(struct platform_device *pdev) +{ + struct state_notifier_block *notif_block; + + list_for_each_entry(notif_block, ¬ifier_block_list, + notifier_list) { + subsys_notif_unregister_notifier(notif_block->handle, + ¬if_block->nb); + list_del(¬if_block->notifier_list); + } + return 0; +} + +static const struct of_device_id match_table[] = { + { .compatible = "qcom,subsys-notif-virt" }, + {}, +}; + +static struct platform_driver subsys_notif_virt_driver = { + .probe = subsys_notif_virt_probe, + .remove = subsys_notif_virt_remove, + .driver = { + .name = "subsys_notif_virt", + .owner = "THIS_MODULE", + .of_match_table = match_table, + }, +}; + +static int __init subsys_notif_virt_init(void) +{ + return platform_driver_register(&subsys_notif_virt_driver); +} +module_init(subsys_notif_virt_init); + +static void __exit subsys_notif_virt_exit(void) +{ + platform_driver_unregister(&subsys_notif_virt_driver); +} +module_exit(subsys_notif_virt_exit); + +MODULE_DESCRIPTION("Subsystem Notification Virtual Driver"); +MODULE_LICENSE("GPL v2");