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

Commit d175f0e2 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "soc: qcom: Add snapshot for Glink Probe driver"

parents 43274f78 28d4a318
Loading
Loading
Loading
Loading
+19 −0
Original line number Original line Diff line number Diff line
@@ -403,6 +403,25 @@ config QSEE_IPC_IRQ
	  Clients can use this driver to avoid adding common interrupt handling
	  Clients can use this driver to avoid adding common interrupt handling
	  code.
	  code.


config QCOM_GLINK
	tristate "GLINK Probe Helper"
	depends on RPMSG_QCOM_GLINK_SMEM
	help
	  This enables the GLINK Probe module. This is designed to set up
	  other edges in the system. It will initialize all the transports for
	  all the edges present in the device.
	  Say M if you want to enable this module.

config MSM_GLINK_SSR
	tristate "MSM GLINK SSR"
	depends on RPMSG
	help
	  The GLINK RPMSG Plugin is currently designed to plugin with the
	  remote proc framework as a subdev. This module is responsible
	  for creating the glink transports when remote proc is disabled. This
	  will be used for drivers on MSMs.
	  Say M if you want to enable this

config MSM_CDSP_LOADER
config MSM_CDSP_LOADER
	tristate "CDSP loader support"
	tristate "CDSP loader support"
	help
	help
+2 −0
Original line number Original line Diff line number Diff line
@@ -45,3 +45,5 @@ obj-$(CONFIG_QMP_DEBUGFS_CLIENT) += qmp-debugfs-client.o
obj-$(CONFIG_QCOM_SMP2P_SLEEPSTATE) += smp2p_sleepstate.o
obj-$(CONFIG_QCOM_SMP2P_SLEEPSTATE) += smp2p_sleepstate.o
obj-$(CONFIG_SENSORS_SSC) += sensors_ssc.o
obj-$(CONFIG_SENSORS_SSC) += sensors_ssc.o
obj-$(CONFIG_QSEE_IPC_IRQ) += qsee_ipc_irq.o
obj-$(CONFIG_QSEE_IPC_IRQ) += qsee_ipc_irq.o
obj-$(CONFIG_QCOM_GLINK) += glink_probe.o
obj-$(CONFIG_MSM_GLINK_SSR) += msm_glink_ssr.o
+213 −0
Original line number Original line Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
 */

#include <linux/of.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/platform_device.h>
#include <soc/qcom/subsystem_notif.h>
#include <linux/rpmsg/qcom_glink.h>
#include <linux/rpmsg.h>
#include <linux/ipc_logging.h>

#define GLINK_PROBE_LOG_PAGE_CNT 4
static void *glink_ilc;

#define GLINK_INFO(x, ...) ipc_log_string(glink_ilc, x, ##__VA_ARGS__)

#define GLINK_ERR(dev, x, ...)					       \
do {								       \
	dev_err(dev, "[%s]: "x, __func__, ##__VA_ARGS__);	       \
	ipc_log_string(glink_ilc, "[%s]: "x, __func__, ##__VA_ARGS__); \
} while (0)

struct edge_info {
	struct list_head list;
	struct device *dev;
	struct device_node *node;

	const char *glink_label;
	const char *ssr_label;
	void *glink;

	int (*register_fn)(struct edge_info *einfo);
	void (*unregister_fn)(struct edge_info *einfo);
	struct notifier_block nb;
	void *notifier_handle;
};
static LIST_HEAD(edge_infos);

static int glink_probe_ssr_cb(struct notifier_block *this,
			      unsigned long code, void *data)
{
	struct edge_info *einfo = container_of(this, struct edge_info, nb);

	GLINK_INFO("received %ld for %s\n", code, einfo->ssr_label);

	switch (code) {
	case SUBSYS_AFTER_POWERUP:
		einfo->register_fn(einfo);
		break;
	case SUBSYS_AFTER_SHUTDOWN:
		einfo->unregister_fn(einfo);
		break;
	default:
		break;
	}

	return NOTIFY_DONE;
}

static int glink_probe_smem_reg(struct edge_info *einfo)
{
	struct device *dev = einfo->dev;

	einfo->glink = qcom_glink_smem_register(dev, einfo->node);
	if (IS_ERR_OR_NULL(einfo->glink)) {
		GLINK_ERR(dev, "register failed for %s\n", einfo->ssr_label);
		einfo->glink = NULL;
	}
	GLINK_INFO("register successful for %s\n", einfo->ssr_label);

	return 0;
}

static void glink_probe_smem_unreg(struct edge_info *einfo)
{
	if (einfo->glink)
		qcom_glink_smem_unregister(einfo->glink);

	einfo->glink = NULL;
	GLINK_INFO("unregister for %s\n", einfo->ssr_label);
}

static void probe_subsystem(struct device *dev, struct device_node *np)
{
	struct edge_info *einfo;
	const char *transport;
	int ret;
	void *handle;

	einfo = devm_kzalloc(dev, sizeof(*einfo), GFP_KERNEL);
	if (!einfo)
		return;

	ret = of_property_read_string(np, "label", &einfo->ssr_label);
	if (ret < 0)
		einfo->ssr_label = np->name;

	ret = of_property_read_string(np, "qcom,glink-label",
				      &einfo->glink_label);
	if (ret < 0) {
		GLINK_ERR(dev, "no qcom,glink-label for %s\n",
			  einfo->ssr_label);
		goto free_einfo;
	}

	einfo->dev = dev;
	einfo->node = np;

	ret = of_property_read_string(np, "transport", &transport);
	if (ret < 0) {
		GLINK_ERR(dev, "%s missing transport\n", einfo->ssr_label);
		goto free_einfo;
	}

	if (!strcmp(transport, "smem")) {
		einfo->register_fn = glink_probe_smem_reg;
		einfo->unregister_fn = glink_probe_smem_unreg;
	}

	einfo->nb.notifier_call = glink_probe_ssr_cb;

	handle = subsys_notif_register_notifier(einfo->ssr_label, &einfo->nb);
	if (IS_ERR_OR_NULL(handle)) {
		GLINK_ERR(dev, "could not register for SSR notifier for %s\n",
			  einfo->ssr_label);
		goto free_einfo;
	}
	einfo->notifier_handle = handle;

	list_add_tail(&einfo->list, &edge_infos);
	GLINK_INFO("probe successful for %s\n", einfo->ssr_label);

	return;

free_einfo:
	devm_kfree(dev, einfo);
}

static int glink_probe(struct platform_device *pdev)
{
	struct device_node *pn = pdev->dev.of_node;
	struct device_node *cn;

	for_each_available_child_of_node(pn, cn) {
		probe_subsystem(&pdev->dev, cn);
	}
	return 0;
}

static int glink_remove(struct platform_device *pdev)
{
	struct device_node *pn = pdev->dev.of_node;
	struct device_node *cn;
	struct edge_info *einfo, *tmp;

	for_each_available_child_of_node(pn, cn) {
		list_for_each_entry_safe(einfo, tmp, &edge_infos, list) {
			subsys_notif_unregister_notifier(einfo->notifier_handle,
							 &einfo->nb);
			list_del(&einfo->list);
		}
	}
	return 0;
}

static const struct of_device_id glink_match_table[] = {
	{ .compatible = "qcom,glink" },
	{},
};

static struct platform_driver glink_probe_driver = {
	.probe = glink_probe,
	.remove = glink_remove,
	.driver = {
		.name = "msm_glink",
		.of_match_table = glink_match_table,
	},
};

static int __init glink_probe_init(void)
{
	int ret;

	glink_ilc = ipc_log_context_create(GLINK_PROBE_LOG_PAGE_CNT,
					   "glink_probe", 0);

	ret = platform_driver_register(&glink_probe_driver);
	if (ret) {
		pr_err("%s: glink_probe register failed %d\n",
			__func__, ret);
		return ret;
	}

	return 0;
}
arch_initcall(glink_probe_init);

static void __exit glink_probe_exit(void)
{
	if (glink_ilc)
		ipc_log_context_destroy(glink_ilc);

	platform_driver_unregister(&glink_probe_driver);
}
module_exit(glink_probe_exit);

MODULE_DESCRIPTION("Qualcomm Technologies, Inc. GLINK probe helper driver");
MODULE_LICENSE("GPL v2");
+244 −0
Original line number Original line Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
 */

#include <linux/of.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/platform_device.h>
#include <soc/qcom/subsystem_notif.h>
#include <linux/rpmsg/qcom_glink.h>
#include <linux/rpmsg.h>
#include <linux/ipc_logging.h>

#define MSM_SSR_LOG_PAGE_CNT 4
static void *ssr_ilc;

#define MSM_SSR_INFO(x, ...) ipc_log_string(ssr_ilc, x, ##__VA_ARGS__)

#define MSM_SSR_ERR(dev, x, ...)				       \
do {								       \
	dev_err(dev, "[%s]: "x, __func__, ##__VA_ARGS__);	       \
	ipc_log_string(ssr_ilc, "[%s]: "x, __func__, ##__VA_ARGS__);   \
} while (0)

#define GLINK_SSR_DO_CLEANUP	0
#define GLINK_SSR_CLEANUP_DONE	1
#define GLINK_SSR_PRIORITY	1
#define GLINK_SSR_REPLY_TIMEOUT	HZ

struct do_cleanup_msg {
	__le32 version;
	__le32 command;
	__le32 seq_num;
	__le32 name_len;
	char name[32];
};

struct cleanup_done_msg {
	__le32 version;
	__le32 response;
	__le32 seq_num;
};

struct glink_ssr_nb {
	struct list_head list;
	struct glink_ssr *ssr;
	void *ssr_register_handle;

	const char *glink_label;
	const char *ssr_label;

	struct notifier_block nb;
};

struct glink_ssr {
	struct device *dev;
	struct rpmsg_endpoint *ept;

	struct list_head notify_list;

	u32 seq_num;
	struct completion completion;
};

static int glink_ssr_ssr_cb(struct notifier_block *this,
			    unsigned long code, void *data)
{
	struct glink_ssr_nb *nb = container_of(this, struct glink_ssr_nb, nb);
	struct glink_ssr *ssr = nb->ssr;
	struct device *dev = ssr->dev;
	struct do_cleanup_msg msg;
	int ret;

	if (code == SUBSYS_AFTER_SHUTDOWN) {
		ssr->seq_num++;
		reinit_completion(&ssr->completion);

		memset(&msg, 0, sizeof(msg));
		msg.command = cpu_to_le32(GLINK_SSR_DO_CLEANUP);
		msg.seq_num = cpu_to_le32(ssr->seq_num);
		msg.name_len = cpu_to_le32(strlen(nb->glink_label));
		strlcpy(msg.name, nb->glink_label, sizeof(msg.name));

		MSM_SSR_INFO("%s: notify of %s seq_num:%d\n",
			   dev->parent->of_node->name, nb->glink_label,
			   ssr->seq_num);

		ret = rpmsg_send(ssr->ept, &msg, sizeof(msg));
		if (ret) {
			MSM_SSR_ERR(dev, "fail to send do cleanup to %s %d\n",
				  nb->ssr_label, ret);
			return NOTIFY_DONE;
		}

		ret = wait_for_completion_timeout(&ssr->completion, HZ);
		if (!ret)
			MSM_SSR_ERR(dev, "timeout waiting for cleanup resp\n");
	}
	return NOTIFY_DONE;
}

static int glink_ssr_callback(struct rpmsg_device *rpdev,
			      void *data, int len, void *priv, u32 addr)
{
	struct cleanup_done_msg *msg = data;
	struct glink_ssr *ssr = dev_get_drvdata(&rpdev->dev);

	if (len < sizeof(*msg)) {
		MSM_SSR_ERR(ssr->dev, "message too short\n");
		return -EINVAL;
	}

	if (le32_to_cpu(msg->version) != 0) {
		MSM_SSR_ERR(ssr->dev, "invalid version\n");
		return -EINVAL;
	}

	if (le32_to_cpu(msg->response) != GLINK_SSR_CLEANUP_DONE)
		return 0;

	if (le32_to_cpu(msg->seq_num) != ssr->seq_num) {
		MSM_SSR_ERR(ssr->dev, "invalid response sequence number %d\n",
			  msg->seq_num);
		return -EINVAL;
	}

	complete(&ssr->completion);

	MSM_SSR_INFO("%s: received seq_num:%d\n",
		    ssr->dev->parent->of_node->name,
		    le32_to_cpu(msg->seq_num));

	return 0;
}

static void glink_ssr_init_notify(struct glink_ssr *ssr)
{
	struct device *dev = ssr->dev;
	struct device_node *node;
	struct glink_ssr_nb *nb;
	void *handle;
	int ret;
	struct of_phandle_iterator it;
	int err;

	of_for_each_phandle(&it, err, dev->of_node, "qcom,notify-edges",
			    NULL, 0) {
		node = it.node;

		nb = devm_kzalloc(dev, sizeof(*nb), GFP_KERNEL);
		if (!nb) {
			of_node_put(node);
			return;
		}

		ret = of_property_read_string(node, "label", &nb->ssr_label);
		if (ret < 0)
			nb->ssr_label = node->name;

		ret = of_property_read_string(node, "qcom,glink-label",
					      &nb->glink_label);
		if (ret < 0) {
			MSM_SSR_ERR(dev, "no qcom,glink-label for %s\n",
				  nb->ssr_label);
			of_node_put(node);
			continue;
		}

		nb->nb.notifier_call = glink_ssr_ssr_cb;
		nb->nb.priority = GLINK_SSR_PRIORITY;
		nb->ssr = ssr;

		handle = subsys_notif_register_notifier(nb->ssr_label, &nb->nb);
		if (IS_ERR_OR_NULL(handle)) {
			MSM_SSR_ERR(dev, "register fail for %s SSR notifier\n",
				  nb->ssr_label);
			of_node_put(node);
			continue;
		}

		nb->ssr_register_handle = handle;
		list_add_tail(&nb->list, &ssr->notify_list);
	}
}

static int glink_ssr_probe(struct rpmsg_device *rpdev)
{
	struct glink_ssr *ssr;

	ssr = devm_kzalloc(&rpdev->dev, sizeof(*ssr), GFP_KERNEL);
	if (!ssr)
		return -ENOMEM;

	INIT_LIST_HEAD(&ssr->notify_list);
	init_completion(&ssr->completion);

	ssr->dev = &rpdev->dev;
	ssr->ept = rpdev->ept;

	ssr_ilc = ipc_log_context_create(MSM_SSR_LOG_PAGE_CNT,
				   "glink_ssr", 0);

	glink_ssr_init_notify(ssr);

	dev_set_drvdata(&rpdev->dev, ssr);

	return 0;
}

static void glink_ssr_remove(struct rpmsg_device *rpdev)
{
	struct glink_ssr *ssr = dev_get_drvdata(&rpdev->dev);
	struct glink_ssr_nb *nb;

	list_for_each_entry(nb, &ssr->notify_list, list) {
		subsys_notif_unregister_notifier(nb->ssr_register_handle,
						 &nb->nb);
	}

	dev_set_drvdata(&rpdev->dev, NULL);
}

static const struct rpmsg_device_id glink_ssr_match[] = {
	{ "glink_ssr" },
	{}
};

static struct rpmsg_driver glink_ssr_driver = {
	.probe = glink_ssr_probe,
	.remove = glink_ssr_remove,
	.callback = glink_ssr_callback,
	.id_table = glink_ssr_match,
	.drv = {
		.name = "msm_glink_ssr",
	},
};
module_rpmsg_driver(glink_ssr_driver);

MODULE_ALIAS("rpmsg:msm_glink_ssr");
MODULE_DESCRIPTION("MSM GLINK SSR notifier");
MODULE_LICENSE("GPL v2");