summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnant Goel <anantg@codeaurora.org>2018-02-06 17:46:38 -0800
committerAnant Goel <anantg@codeaurora.org>2018-03-07 12:05:28 -0800
commitf64af1ec50d8dd0f3ac46c7bb080ec630ac73e07 (patch)
tree68c138be753dae7146d790ff8eb4f0dbee8b9e51
parenta337c17cebeadebddd57cfae9d7953567969be06 (diff)
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 <anantg@codeaurora.org>
-rw-r--r--Documentation/devicetree/bindings/arm/msm/subsystem_notif_virt.txt35
-rw-r--r--drivers/soc/qcom/Makefile1
-rw-r--r--drivers/soc/qcom/subsystem_notif_virt.c163
3 files changed, 199 insertions, 0 deletions
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 <linux/err.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/notifier.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/io.h>
+#include <linux/of_platform.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <soc/qcom/subsystem_notif.h>
+
+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,
+ &notif_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",
+ &notif_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(&notif_block->notifier_list,
+ &notifier_block_list);
+
+ }
+ return 0;
+
+err_offset:
+ subsys_notif_unregister_notifier(notif_block->handle,
+ &notif_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, &notifier_block_list,
+ notifier_list) {
+ subsys_notif_unregister_notifier(notif_block->handle,
+ &notif_block->nb);
+ list_del(&notif_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");