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

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

Merge "rpmsg: glink: Add early reset notifier"

parents 1d31b99f a26b5064
Loading
Loading
Loading
Loading
+37 −12
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@
#include <linux/kthread.h>
#include <linux/mailbox_client.h>
#include <linux/ipc_logging.h>
#include <soc/qcom/subsystem_notif.h>

#include "rpmsg_internal.h"
#include "qcom_glink_native.h"
@@ -1828,6 +1829,23 @@ static void qcom_glink_set_affinity(struct qcom_glink *glink, u32 *arr,
		dev_err(glink->dev, "failed to set task affinity\n");
}

static void qcom_glink_notif_reset(void *data)
{
	struct qcom_glink *glink = data;
	struct glink_channel *channel;
	unsigned long flags;
	int cid;

	if (!glink)
		return;
	atomic_inc(&glink->in_reset);

	spin_lock_irqsave(&glink->idr_lock, flags);
	idr_for_each_entry(&glink->lcids, channel, cid) {
		wake_up(&channel->intent_req_event);
	}
	spin_unlock_irqrestore(&glink->idr_lock, flags);
}

struct qcom_glink *qcom_glink_native_probe(struct device *dev,
					   unsigned long features,
@@ -1886,6 +1904,13 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev,
		return ERR_CAST(glink->task);
	}

	ret = subsys_register_early_notifier(glink->name, XPORT_LAYER_NOTIF,
					     qcom_glink_notif_reset, glink);
	if (ret) {
		dev_err(dev, "failed to register early notif %d\n", ret);
		return ERR_PTR(ret);
	}

	irq = of_irq_get(dev->of_node, 0);
	ret = devm_request_irq(dev, irq,
			       qcom_glink_native_intr,
@@ -1893,7 +1918,7 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev,
			       "glink-native", glink);
	if (ret) {
		dev_err(dev, "failed to request IRQ\n");
		return ERR_PTR(ret);
		goto unregister;
	}

	glink->irq = irq;
@@ -1901,8 +1926,10 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev,
	size = of_property_count_u32_elems(dev->of_node, "cpu-affinity");
	if (size > 0) {
		arr = kmalloc_array(size, sizeof(u32), GFP_KERNEL);
		if (!arr)
			return ERR_PTR(-ENOMEM);
		if (!arr) {
			ret = -ENOMEM;
			goto unregister;
		}
		ret = of_property_read_u32_array(dev->of_node, "cpu-affinity",
						 arr, size);
		if (!ret)
@@ -1913,7 +1940,7 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev,
	ret = qcom_glink_send_version(glink);
	if (ret) {
		dev_err(dev, "failed to send version %d\n", ret);
		return ERR_PTR(ret);
		goto unregister;
	}

	ret = qcom_glink_create_chrdev(glink);
@@ -1923,6 +1950,10 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev,
	glink->ilc = ipc_log_context_create(GLINK_LOG_PAGE_CNT, glink->name, 0);

	return glink;

unregister:
	subsys_unregister_early_notifier(glink->name, XPORT_LAYER_NOTIF);
	return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(qcom_glink_native_probe);

@@ -1940,17 +1971,11 @@ void qcom_glink_native_remove(struct qcom_glink *glink)
	int ret;
	unsigned long flags;

	atomic_inc(&glink->in_reset);
	subsys_unregister_early_notifier(glink->name, XPORT_LAYER_NOTIF);
	qcom_glink_notif_reset(glink);
	disable_irq(glink->irq);
	cancel_work_sync(&glink->rx_work);

	/* Signal all threads to cancel tx */
	spin_lock_irqsave(&glink->idr_lock, flags);
	idr_for_each_entry(&glink->lcids, channel, cid) {
		wake_up(&channel->intent_req_event);
	}
	spin_unlock_irqrestore(&glink->idr_lock, flags);

	ret = device_for_each_child(glink->dev, NULL, qcom_glink_remove_device);
	if (ret)
		dev_warn(glink->dev, "Can't remove GLINK devices: %d\n", ret);
+118 −1
Original line number Diff line number Diff line
/* Copyright (c) 2011, 2013, 2016-2017, The Linux Foundation. All rights reserved.
/* Copyright (c) 2011, 2013, 2016-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
@@ -28,10 +28,25 @@
#include <linux/slab.h>
#include <soc/qcom/subsystem_notif.h>

/**
 * The callbacks that are registered in this data structure as early
 * notification callbacks will be called as soon as the SSR framework is
 * informed that the subsystem has crashed. This means that these functions will
 * be invoked as part of an IRQ handler, and thus, will be called in an atomic
 * context. Therefore, functions that are registered as early notification
 * callback must obey to the same constraints as interrupt handlers
 * (i.e. these functions must not sleep or block, etc).
 */
struct subsys_early_notif_info {
	spinlock_t cb_lock;
	void (*early_notif_cb[NUM_EARLY_NOTIFS])(void *);
	void *data[NUM_EARLY_NOTIFS];
};

struct subsys_notif_info {
	char name[50];
	struct srcu_notifier_head subsys_notif_rcvr_list;
	struct subsys_early_notif_info early_notif_info;
	struct list_head list;
};

@@ -101,6 +116,105 @@ int subsys_notif_unregister_notifier(void *subsys_handle,
}
EXPORT_SYMBOL(subsys_notif_unregister_notifier);

void send_early_notifications(void *early_notif_handle)
{
	struct subsys_early_notif_info *early_info = early_notif_handle;
	unsigned long flags;
	unsigned int i;
	void (*notif_cb)(void *);

	if (!early_notif_handle)
		return;

	spin_lock_irqsave(&early_info->cb_lock, flags);
	for (i = 0; i < NUM_EARLY_NOTIFS; i++) {
		notif_cb = early_info->early_notif_cb[i];
		if (notif_cb)
			notif_cb(early_info->data[i]);
	}
	spin_unlock_irqrestore(&early_info->cb_lock, flags);
}
EXPORT_SYMBOL(send_early_notifications);

static bool valid_early_notif(enum early_subsys_notif_type notif_type)
{
	return  notif_type >= 0 && notif_type < NUM_EARLY_NOTIFS;
}

/**
 * The early_notif_cb parameter must point to a function that conforms to the
 * same constraints placed upon interrupt handlers, as the function will be
 * called in an atomic context (i.e. these functions must not sleep or block).
 */
int subsys_register_early_notifier(const char *subsys_name,
				   enum early_subsys_notif_type notif_type,
				   void (*early_notif_cb)(void *), void *data)
{
	struct subsys_notif_info *subsys;
	struct subsys_early_notif_info *early_notif_info;
	unsigned long flags;
	int rc = 0;

	if (!subsys_name || !early_notif_cb || !valid_early_notif(notif_type))
		return -EINVAL;

	subsys = _notif_find_subsys(subsys_name);
	if (!subsys)
		return -EINVAL;

	early_notif_info = &subsys->early_notif_info;
	spin_lock_irqsave(&early_notif_info->cb_lock, flags);
	if (early_notif_info->early_notif_cb[notif_type]) {
		rc = -EEXIST;
		goto out;
	}
	early_notif_info->early_notif_cb[notif_type] = early_notif_cb;
	early_notif_info->data[notif_type] = data;
out:
	spin_unlock_irqrestore(&early_notif_info->cb_lock, flags);
	return rc;
}
EXPORT_SYMBOL(subsys_register_early_notifier);

int subsys_unregister_early_notifier(const char *subsys_name, enum
				     early_subsys_notif_type notif_type)
{
	struct subsys_notif_info *subsys;
	struct subsys_early_notif_info *early_notif_info;
	unsigned long flags;

	if (!subsys_name || !valid_early_notif(notif_type))
		return -EINVAL;

	subsys = _notif_find_subsys(subsys_name);
	if (!subsys)
		return -EINVAL;

	early_notif_info = &subsys->early_notif_info;
	spin_lock_irqsave(&early_notif_info->cb_lock, flags);
	early_notif_info->early_notif_cb[notif_type] = NULL;
	early_notif_info->data[notif_type] = NULL;
	spin_unlock_irqrestore(&early_notif_info->cb_lock, flags);
	return 0;
}
EXPORT_SYMBOL(subsys_unregister_early_notifier);

void *subsys_get_early_notif_info(const char *subsys_name)
{
	struct subsys_notif_info *subsys;

	if (!subsys_name)
		return ERR_PTR(-EINVAL);

	subsys = _notif_find_subsys(subsys_name);

	if (!subsys)
		return ERR_PTR(-EINVAL);

	return &subsys->early_notif_info;
}
EXPORT_SYMBOL(subsys_get_early_notif_info);

void *subsys_notif_add_subsys(const char *subsys_name)
{
	struct subsys_notif_info *subsys = NULL;
@@ -128,6 +242,9 @@ void *subsys_notif_add_subsys(const char *subsys_name)

	srcu_init_notifier_head(&subsys->subsys_notif_rcvr_list);

	memset(&subsys->early_notif_info, 0, sizeof(struct
						    subsys_early_notif_info));
	spin_lock_init(&subsys->early_notif_info.cb_lock);
	INIT_LIST_HEAD(&subsys->list);

	mutex_lock(&notif_lock);
+4 −0
Original line number Diff line number Diff line
@@ -194,6 +194,7 @@ struct subsys_device {
	struct subsys_tracking track;

	void *notify;
	void *early_notify;
	struct device dev;
	struct module *owner;
	int count;
@@ -1227,6 +1228,8 @@ int subsystem_restart_dev(struct subsys_device *dev)

	name = dev->desc->name;

	send_early_notifications(dev->early_notify);

	/*
	 * If a system reboot/shutdown is underway, ignore subsystem errors.
	 * However, print a message so that we know that a subsystem behaved
@@ -1801,6 +1804,7 @@ struct subsys_device *subsys_register(struct subsys_desc *desc)
			sizeof(subsys->desc->fw_name));

	subsys->notify = subsys_notif_add_subsys(desc->name);
	subsys->early_notify = subsys_get_early_notif_info(desc->name);

	snprintf(subsys->wlname, sizeof(subsys->wlname), "ssr(%s)", desc->name);
	wakeup_source_init(&subsys->ssr_wlock, subsys->wlname);
+38 −1
Original line number Diff line number Diff line
/* Copyright (c) 2011, 2013 - 2014, The Linux Foundation. All rights reserved.
/* Copyright (c) 2011, 2013 - 2014, 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
@@ -32,6 +32,11 @@ enum subsys_notif_type {
	SUBSYS_NOTIF_TYPE_COUNT
};

enum early_subsys_notif_type {
	XPORT_LAYER_NOTIF,
	NUM_EARLY_NOTIFS
};

#if defined(CONFIG_MSM_SUBSYSTEM_RESTART)
/* Use the subsys_notif_register_notifier API to register for notifications for
 * a particular subsystem. This API will return a handle that can be used to
@@ -57,6 +62,14 @@ void *subsys_notif_add_subsys(const char *subsys_name);
int subsys_notif_queue_notification(void *subsys_handle,
					enum subsys_notif_type notif_type,
					void *data);
void *subsys_get_early_notif_info(const char *subsys_name);
int subsys_register_early_notifier(const char *subsys_name,
				   enum early_subsys_notif_type notif_type,
				   void (*early_notif_cb)(void *),
				   void *data);
int subsys_unregister_early_notifier(const char *subsys_name, enum
				     early_subsys_notif_type notif_type);
void send_early_notifications(void *early_notif_handle);
#else

static inline void *subsys_notif_register_notifier(
@@ -82,6 +95,30 @@ static inline int subsys_notif_queue_notification(void *subsys_handle,
{
	return 0;
}

static inline void *subsys_get_early_notif_info(const char *subsys_name)
{
	return NULL;
}

static inline int subsys_register_early_notifier(const char *subsys_name,
				   enum early_subsys_notif_type notif_type,
				   void (*early_notif_cb)(void *),
				   void *data)
{
	return -ENOTSUPP;
}

static inline int subsys_unregister_early_notifier(const char *subsys_name,
						   enum early_subsys_notif_type
						   notif_type)
{
	return -ENOTSUPP;
}

static inline void send_early_notifications(void *early_notif_handle)
{
}
#endif /* CONFIG_MSM_SUBSYSTEM_RESTART */

#endif