xref: /linux/drivers/soc/xilinx/xlnx_event_manager.c (revision 4a95449dd975e2ea6629a034f3e74b46c9634916)
1c7fdb240SAbhyuday Godhasara // SPDX-License-Identifier: GPL-2.0
2c7fdb240SAbhyuday Godhasara /*
3c7fdb240SAbhyuday Godhasara  * Xilinx Event Management Driver
4c7fdb240SAbhyuday Godhasara  *
5c7fdb240SAbhyuday Godhasara  *  Copyright (C) 2021 Xilinx, Inc.
6*4a95449dSJay Buddhabhatti  *  Copyright (C) 2024 Advanced Micro Devices, Inc.
7c7fdb240SAbhyuday Godhasara  *
8c7fdb240SAbhyuday Godhasara  *  Abhyuday Godhasara <abhyuday.godhasara@xilinx.com>
9c7fdb240SAbhyuday Godhasara  */
10c7fdb240SAbhyuday Godhasara 
11c7fdb240SAbhyuday Godhasara #include <linux/cpuhotplug.h>
12c7fdb240SAbhyuday Godhasara #include <linux/firmware/xlnx-event-manager.h>
13c7fdb240SAbhyuday Godhasara #include <linux/firmware/xlnx-zynqmp.h>
14c7fdb240SAbhyuday Godhasara #include <linux/hashtable.h>
15c7fdb240SAbhyuday Godhasara #include <linux/interrupt.h>
16c7fdb240SAbhyuday Godhasara #include <linux/irq.h>
17c7fdb240SAbhyuday Godhasara #include <linux/irqdomain.h>
18c7fdb240SAbhyuday Godhasara #include <linux/module.h>
19c7fdb240SAbhyuday Godhasara #include <linux/of_irq.h>
20c7fdb240SAbhyuday Godhasara #include <linux/platform_device.h>
21c7fdb240SAbhyuday Godhasara #include <linux/slab.h>
22c7fdb240SAbhyuday Godhasara 
23*4a95449dSJay Buddhabhatti static DEFINE_PER_CPU_READ_MOSTLY(int, dummy_cpu_number);
24c7fdb240SAbhyuday Godhasara 
25c7fdb240SAbhyuday Godhasara static int virq_sgi;
26c7fdb240SAbhyuday Godhasara static int event_manager_availability = -EACCES;
27c7fdb240SAbhyuday Godhasara 
28c7fdb240SAbhyuday Godhasara /* SGI number used for Event management driver */
29c7fdb240SAbhyuday Godhasara #define XLNX_EVENT_SGI_NUM	(15)
30c7fdb240SAbhyuday Godhasara 
31c7fdb240SAbhyuday Godhasara /* Max number of driver can register for same event */
32c7fdb240SAbhyuday Godhasara #define MAX_DRIVER_PER_EVENT	(10U)
33c7fdb240SAbhyuday Godhasara 
34c7fdb240SAbhyuday Godhasara /* Max HashMap Order for PM API feature check (1<<7 = 128) */
35c7fdb240SAbhyuday Godhasara #define REGISTERED_DRIVER_MAX_ORDER	(7)
36c7fdb240SAbhyuday Godhasara 
37c7fdb240SAbhyuday Godhasara #define MAX_BITS	(32U) /* Number of bits available for error mask */
38c7fdb240SAbhyuday Godhasara 
39c7fdb240SAbhyuday Godhasara #define FIRMWARE_VERSION_MASK			(0xFFFFU)
40c7fdb240SAbhyuday Godhasara #define REGISTER_NOTIFIER_FIRMWARE_VERSION	(2U)
41c7fdb240SAbhyuday Godhasara 
42c7fdb240SAbhyuday Godhasara static DEFINE_HASHTABLE(reg_driver_map, REGISTERED_DRIVER_MAX_ORDER);
43c7fdb240SAbhyuday Godhasara static int sgi_num = XLNX_EVENT_SGI_NUM;
44c7fdb240SAbhyuday Godhasara 
45e6d3c99aSAbhyuday Godhasara static bool is_need_to_unregister;
46e6d3c99aSAbhyuday Godhasara 
47c7fdb240SAbhyuday Godhasara /**
4805e5ba40SAbhyuday Godhasara  * struct agent_cb - Registered callback function and private data.
4905e5ba40SAbhyuday Godhasara  * @agent_data:		Data passed back to handler function.
5005e5ba40SAbhyuday Godhasara  * @eve_cb:		Function pointer to store the callback function.
5105e5ba40SAbhyuday Godhasara  * @list:		member to create list.
5205e5ba40SAbhyuday Godhasara  */
5305e5ba40SAbhyuday Godhasara struct agent_cb {
5405e5ba40SAbhyuday Godhasara 	void *agent_data;
5505e5ba40SAbhyuday Godhasara 	event_cb_func_t eve_cb;
5605e5ba40SAbhyuday Godhasara 	struct list_head list;
5705e5ba40SAbhyuday Godhasara };
5805e5ba40SAbhyuday Godhasara 
5905e5ba40SAbhyuday Godhasara /**
60c7fdb240SAbhyuday Godhasara  * struct registered_event_data - Registered Event Data.
61c7fdb240SAbhyuday Godhasara  * @key:		key is the combine id(Node-Id | Event-Id) of type u64
62c7fdb240SAbhyuday Godhasara  *			where upper u32 for Node-Id and lower u32 for Event-Id,
63c7fdb240SAbhyuday Godhasara  *			And this used as key to index into hashmap.
64c7fdb240SAbhyuday Godhasara  * @cb_type:		Type of Api callback, like PM_NOTIFY_CB, etc.
65c7fdb240SAbhyuday Godhasara  * @wake:		If this flag set, firmware will wake up processor if is
66c7fdb240SAbhyuday Godhasara  *			in sleep or power down state.
6705e5ba40SAbhyuday Godhasara  * @cb_list_head:	Head of call back data list which contain the information
6805e5ba40SAbhyuday Godhasara  *			about registered handler and private data.
69c7fdb240SAbhyuday Godhasara  * @hentry:		hlist_node that hooks this entry into hashtable.
70c7fdb240SAbhyuday Godhasara  */
71c7fdb240SAbhyuday Godhasara struct registered_event_data {
72c7fdb240SAbhyuday Godhasara 	u64 key;
73c7fdb240SAbhyuday Godhasara 	enum pm_api_cb_id cb_type;
74c7fdb240SAbhyuday Godhasara 	bool wake;
7505e5ba40SAbhyuday Godhasara 	struct list_head cb_list_head;
76c7fdb240SAbhyuday Godhasara 	struct hlist_node hentry;
77c7fdb240SAbhyuday Godhasara };
78c7fdb240SAbhyuday Godhasara 
79c7fdb240SAbhyuday Godhasara static bool xlnx_is_error_event(const u32 node_id)
80c7fdb240SAbhyuday Godhasara {
8197d62760SJay Buddhabhatti 	u32 pm_family_code, pm_sub_family_code;
8297d62760SJay Buddhabhatti 
8397d62760SJay Buddhabhatti 	zynqmp_pm_get_family_info(&pm_family_code, &pm_sub_family_code);
8497d62760SJay Buddhabhatti 
8597d62760SJay Buddhabhatti 	if (pm_sub_family_code == VERSAL_SUB_FAMILY_CODE) {
8697d62760SJay Buddhabhatti 		if (node_id == VERSAL_EVENT_ERROR_PMC_ERR1 ||
8797d62760SJay Buddhabhatti 		    node_id == VERSAL_EVENT_ERROR_PMC_ERR2 ||
8897d62760SJay Buddhabhatti 		    node_id == VERSAL_EVENT_ERROR_PSM_ERR1 ||
8997d62760SJay Buddhabhatti 		    node_id == VERSAL_EVENT_ERROR_PSM_ERR2)
90c7fdb240SAbhyuday Godhasara 			return true;
9197d62760SJay Buddhabhatti 	} else {
9297d62760SJay Buddhabhatti 		if (node_id == VERSAL_NET_EVENT_ERROR_PMC_ERR1 ||
9397d62760SJay Buddhabhatti 		    node_id == VERSAL_NET_EVENT_ERROR_PMC_ERR2 ||
9497d62760SJay Buddhabhatti 		    node_id == VERSAL_NET_EVENT_ERROR_PMC_ERR3 ||
9597d62760SJay Buddhabhatti 		    node_id == VERSAL_NET_EVENT_ERROR_PSM_ERR1 ||
9697d62760SJay Buddhabhatti 		    node_id == VERSAL_NET_EVENT_ERROR_PSM_ERR2 ||
9797d62760SJay Buddhabhatti 		    node_id == VERSAL_NET_EVENT_ERROR_PSM_ERR3 ||
9897d62760SJay Buddhabhatti 		    node_id == VERSAL_NET_EVENT_ERROR_PSM_ERR4)
9997d62760SJay Buddhabhatti 			return true;
10097d62760SJay Buddhabhatti 	}
101c7fdb240SAbhyuday Godhasara 
102c7fdb240SAbhyuday Godhasara 	return false;
103c7fdb240SAbhyuday Godhasara }
104c7fdb240SAbhyuday Godhasara 
105c7fdb240SAbhyuday Godhasara static int xlnx_add_cb_for_notify_event(const u32 node_id, const u32 event, const bool wake,
106c7fdb240SAbhyuday Godhasara 					event_cb_func_t cb_fun,	void *data)
107c7fdb240SAbhyuday Godhasara {
108c7fdb240SAbhyuday Godhasara 	u64 key = 0;
10905e5ba40SAbhyuday Godhasara 	bool present_in_hash = false;
110c7fdb240SAbhyuday Godhasara 	struct registered_event_data *eve_data;
11105e5ba40SAbhyuday Godhasara 	struct agent_cb *cb_data;
11205e5ba40SAbhyuday Godhasara 	struct agent_cb *cb_pos;
11305e5ba40SAbhyuday Godhasara 	struct agent_cb *cb_next;
114c7fdb240SAbhyuday Godhasara 
115c7fdb240SAbhyuday Godhasara 	key = ((u64)node_id << 32U) | (u64)event;
116c7fdb240SAbhyuday Godhasara 	/* Check for existing entry in hash table for given key id */
117c7fdb240SAbhyuday Godhasara 	hash_for_each_possible(reg_driver_map, eve_data, hentry, key) {
118c7fdb240SAbhyuday Godhasara 		if (eve_data->key == key) {
11905e5ba40SAbhyuday Godhasara 			present_in_hash = true;
12005e5ba40SAbhyuday Godhasara 			break;
121c7fdb240SAbhyuday Godhasara 		}
122c7fdb240SAbhyuday Godhasara 	}
123c7fdb240SAbhyuday Godhasara 
12405e5ba40SAbhyuday Godhasara 	if (!present_in_hash) {
12505e5ba40SAbhyuday Godhasara 		/* Add new entry if not present in HASH table */
126c7fdb240SAbhyuday Godhasara 		eve_data = kmalloc(sizeof(*eve_data), GFP_KERNEL);
127c7fdb240SAbhyuday Godhasara 		if (!eve_data)
128c7fdb240SAbhyuday Godhasara 			return -ENOMEM;
129c7fdb240SAbhyuday Godhasara 		eve_data->key = key;
130c7fdb240SAbhyuday Godhasara 		eve_data->cb_type = PM_NOTIFY_CB;
131c7fdb240SAbhyuday Godhasara 		eve_data->wake = wake;
13205e5ba40SAbhyuday Godhasara 		INIT_LIST_HEAD(&eve_data->cb_list_head);
133c7fdb240SAbhyuday Godhasara 
13405e5ba40SAbhyuday Godhasara 		cb_data = kmalloc(sizeof(*cb_data), GFP_KERNEL);
1351bea5349SGaosheng Cui 		if (!cb_data) {
1361bea5349SGaosheng Cui 			kfree(eve_data);
13705e5ba40SAbhyuday Godhasara 			return -ENOMEM;
1381bea5349SGaosheng Cui 		}
13905e5ba40SAbhyuday Godhasara 		cb_data->eve_cb = cb_fun;
14005e5ba40SAbhyuday Godhasara 		cb_data->agent_data = data;
14105e5ba40SAbhyuday Godhasara 
14205e5ba40SAbhyuday Godhasara 		/* Add into callback list */
14305e5ba40SAbhyuday Godhasara 		list_add(&cb_data->list, &eve_data->cb_list_head);
14405e5ba40SAbhyuday Godhasara 
14505e5ba40SAbhyuday Godhasara 		/* Add into HASH table */
146c7fdb240SAbhyuday Godhasara 		hash_add(reg_driver_map, &eve_data->hentry, key);
14705e5ba40SAbhyuday Godhasara 	} else {
14805e5ba40SAbhyuday Godhasara 		/* Search for callback function and private data in list */
14905e5ba40SAbhyuday Godhasara 		list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head, list) {
15005e5ba40SAbhyuday Godhasara 			if (cb_pos->eve_cb == cb_fun &&
15105e5ba40SAbhyuday Godhasara 			    cb_pos->agent_data == data) {
15205e5ba40SAbhyuday Godhasara 				return 0;
15305e5ba40SAbhyuday Godhasara 			}
15405e5ba40SAbhyuday Godhasara 		}
15505e5ba40SAbhyuday Godhasara 
15605e5ba40SAbhyuday Godhasara 		/* Add multiple handler and private data in list */
15705e5ba40SAbhyuday Godhasara 		cb_data = kmalloc(sizeof(*cb_data), GFP_KERNEL);
15805e5ba40SAbhyuday Godhasara 		if (!cb_data)
15905e5ba40SAbhyuday Godhasara 			return -ENOMEM;
16005e5ba40SAbhyuday Godhasara 		cb_data->eve_cb = cb_fun;
16105e5ba40SAbhyuday Godhasara 		cb_data->agent_data = data;
16205e5ba40SAbhyuday Godhasara 
16305e5ba40SAbhyuday Godhasara 		list_add(&cb_data->list, &eve_data->cb_list_head);
16405e5ba40SAbhyuday Godhasara 	}
165c7fdb240SAbhyuday Godhasara 
166c7fdb240SAbhyuday Godhasara 	return 0;
167c7fdb240SAbhyuday Godhasara }
168c7fdb240SAbhyuday Godhasara 
169c7fdb240SAbhyuday Godhasara static int xlnx_add_cb_for_suspend(event_cb_func_t cb_fun, void *data)
170c7fdb240SAbhyuday Godhasara {
171c7fdb240SAbhyuday Godhasara 	struct registered_event_data *eve_data;
17205e5ba40SAbhyuday Godhasara 	struct agent_cb *cb_data;
173c7fdb240SAbhyuday Godhasara 
174c7fdb240SAbhyuday Godhasara 	/* Check for existing entry in hash table for given cb_type */
175c7fdb240SAbhyuday Godhasara 	hash_for_each_possible(reg_driver_map, eve_data, hentry, PM_INIT_SUSPEND_CB) {
176c7fdb240SAbhyuday Godhasara 		if (eve_data->cb_type == PM_INIT_SUSPEND_CB) {
177c7fdb240SAbhyuday Godhasara 			pr_err("Found as already registered\n");
178c7fdb240SAbhyuday Godhasara 			return -EINVAL;
179c7fdb240SAbhyuday Godhasara 		}
180c7fdb240SAbhyuday Godhasara 	}
181c7fdb240SAbhyuday Godhasara 
182c7fdb240SAbhyuday Godhasara 	/* Add new entry if not present */
183c7fdb240SAbhyuday Godhasara 	eve_data = kmalloc(sizeof(*eve_data), GFP_KERNEL);
184c7fdb240SAbhyuday Godhasara 	if (!eve_data)
185c7fdb240SAbhyuday Godhasara 		return -ENOMEM;
186c7fdb240SAbhyuday Godhasara 
187c7fdb240SAbhyuday Godhasara 	eve_data->key = 0;
188c7fdb240SAbhyuday Godhasara 	eve_data->cb_type = PM_INIT_SUSPEND_CB;
18905e5ba40SAbhyuday Godhasara 	INIT_LIST_HEAD(&eve_data->cb_list_head);
19005e5ba40SAbhyuday Godhasara 
19105e5ba40SAbhyuday Godhasara 	cb_data = kmalloc(sizeof(*cb_data), GFP_KERNEL);
19205e5ba40SAbhyuday Godhasara 	if (!cb_data)
19305e5ba40SAbhyuday Godhasara 		return -ENOMEM;
19405e5ba40SAbhyuday Godhasara 	cb_data->eve_cb = cb_fun;
19505e5ba40SAbhyuday Godhasara 	cb_data->agent_data = data;
19605e5ba40SAbhyuday Godhasara 
19705e5ba40SAbhyuday Godhasara 	/* Add into callback list */
19805e5ba40SAbhyuday Godhasara 	list_add(&cb_data->list, &eve_data->cb_list_head);
199c7fdb240SAbhyuday Godhasara 
200c7fdb240SAbhyuday Godhasara 	hash_add(reg_driver_map, &eve_data->hentry, PM_INIT_SUSPEND_CB);
201c7fdb240SAbhyuday Godhasara 
202c7fdb240SAbhyuday Godhasara 	return 0;
203c7fdb240SAbhyuday Godhasara }
204c7fdb240SAbhyuday Godhasara 
205c7fdb240SAbhyuday Godhasara static int xlnx_remove_cb_for_suspend(event_cb_func_t cb_fun)
206c7fdb240SAbhyuday Godhasara {
207c7fdb240SAbhyuday Godhasara 	bool is_callback_found = false;
208c7fdb240SAbhyuday Godhasara 	struct registered_event_data *eve_data;
20905e5ba40SAbhyuday Godhasara 	struct agent_cb *cb_pos;
21005e5ba40SAbhyuday Godhasara 	struct agent_cb *cb_next;
211c58da0baSDan Carpenter 	struct hlist_node *tmp;
212c7fdb240SAbhyuday Godhasara 
213e6d3c99aSAbhyuday Godhasara 	is_need_to_unregister = false;
214e6d3c99aSAbhyuday Godhasara 
215c7fdb240SAbhyuday Godhasara 	/* Check for existing entry in hash table for given cb_type */
216c58da0baSDan Carpenter 	hash_for_each_possible_safe(reg_driver_map, eve_data, tmp, hentry, PM_INIT_SUSPEND_CB) {
21705e5ba40SAbhyuday Godhasara 		if (eve_data->cb_type == PM_INIT_SUSPEND_CB) {
21805e5ba40SAbhyuday Godhasara 			/* Delete the list of callback */
21905e5ba40SAbhyuday Godhasara 			list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head, list) {
22005e5ba40SAbhyuday Godhasara 				if (cb_pos->eve_cb == cb_fun) {
221c7fdb240SAbhyuday Godhasara 					is_callback_found = true;
22205e5ba40SAbhyuday Godhasara 					list_del_init(&cb_pos->list);
22305e5ba40SAbhyuday Godhasara 					kfree(cb_pos);
22405e5ba40SAbhyuday Godhasara 				}
22505e5ba40SAbhyuday Godhasara 			}
226c7fdb240SAbhyuday Godhasara 			/* remove an object from a hashtable */
227c7fdb240SAbhyuday Godhasara 			hash_del(&eve_data->hentry);
228c7fdb240SAbhyuday Godhasara 			kfree(eve_data);
229e6d3c99aSAbhyuday Godhasara 			is_need_to_unregister = true;
230c7fdb240SAbhyuday Godhasara 		}
231c7fdb240SAbhyuday Godhasara 	}
232c7fdb240SAbhyuday Godhasara 	if (!is_callback_found) {
233c7fdb240SAbhyuday Godhasara 		pr_warn("Didn't find any registered callback for suspend event\n");
234c7fdb240SAbhyuday Godhasara 		return -EINVAL;
235c7fdb240SAbhyuday Godhasara 	}
236c7fdb240SAbhyuday Godhasara 
237c7fdb240SAbhyuday Godhasara 	return 0;
238c7fdb240SAbhyuday Godhasara }
239c7fdb240SAbhyuday Godhasara 
240c7fdb240SAbhyuday Godhasara static int xlnx_remove_cb_for_notify_event(const u32 node_id, const u32 event,
241e6d3c99aSAbhyuday Godhasara 					   event_cb_func_t cb_fun, void *data)
242c7fdb240SAbhyuday Godhasara {
243c7fdb240SAbhyuday Godhasara 	bool is_callback_found = false;
244c7fdb240SAbhyuday Godhasara 	struct registered_event_data *eve_data;
245c7fdb240SAbhyuday Godhasara 	u64 key = ((u64)node_id << 32U) | (u64)event;
24605e5ba40SAbhyuday Godhasara 	struct agent_cb *cb_pos;
24705e5ba40SAbhyuday Godhasara 	struct agent_cb *cb_next;
248c58da0baSDan Carpenter 	struct hlist_node *tmp;
249c7fdb240SAbhyuday Godhasara 
250e6d3c99aSAbhyuday Godhasara 	is_need_to_unregister = false;
251e6d3c99aSAbhyuday Godhasara 
252c7fdb240SAbhyuday Godhasara 	/* Check for existing entry in hash table for given key id */
253c58da0baSDan Carpenter 	hash_for_each_possible_safe(reg_driver_map, eve_data, tmp, hentry, key) {
25405e5ba40SAbhyuday Godhasara 		if (eve_data->key == key) {
25505e5ba40SAbhyuday Godhasara 			/* Delete the list of callback */
25605e5ba40SAbhyuday Godhasara 			list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head, list) {
257e6d3c99aSAbhyuday Godhasara 				if (cb_pos->eve_cb == cb_fun &&
258e6d3c99aSAbhyuday Godhasara 				    cb_pos->agent_data == data) {
259c7fdb240SAbhyuday Godhasara 					is_callback_found = true;
26005e5ba40SAbhyuday Godhasara 					list_del_init(&cb_pos->list);
26105e5ba40SAbhyuday Godhasara 					kfree(cb_pos);
26205e5ba40SAbhyuday Godhasara 				}
26305e5ba40SAbhyuday Godhasara 			}
264e6d3c99aSAbhyuday Godhasara 
265e6d3c99aSAbhyuday Godhasara 			/* Remove HASH table if callback list is empty */
266e6d3c99aSAbhyuday Godhasara 			if (list_empty(&eve_data->cb_list_head)) {
26705e5ba40SAbhyuday Godhasara 				/* remove an object from a HASH table */
268c7fdb240SAbhyuday Godhasara 				hash_del(&eve_data->hentry);
269c7fdb240SAbhyuday Godhasara 				kfree(eve_data);
270e6d3c99aSAbhyuday Godhasara 				is_need_to_unregister = true;
271e6d3c99aSAbhyuday Godhasara 			}
272c7fdb240SAbhyuday Godhasara 		}
273c7fdb240SAbhyuday Godhasara 	}
274c7fdb240SAbhyuday Godhasara 	if (!is_callback_found) {
275c7fdb240SAbhyuday Godhasara 		pr_warn("Didn't find any registered callback for 0x%x 0x%x\n",
276c7fdb240SAbhyuday Godhasara 			node_id, event);
277c7fdb240SAbhyuday Godhasara 		return -EINVAL;
278c7fdb240SAbhyuday Godhasara 	}
279c7fdb240SAbhyuday Godhasara 
280c7fdb240SAbhyuday Godhasara 	return 0;
281c7fdb240SAbhyuday Godhasara }
282c7fdb240SAbhyuday Godhasara 
283c7fdb240SAbhyuday Godhasara /**
284c7fdb240SAbhyuday Godhasara  * xlnx_register_event() - Register for the event.
285c7fdb240SAbhyuday Godhasara  * @cb_type:	Type of callback from pm_api_cb_id,
286c7fdb240SAbhyuday Godhasara  *			PM_NOTIFY_CB - for Error Events,
287c7fdb240SAbhyuday Godhasara  *			PM_INIT_SUSPEND_CB - for suspend callback.
288c7fdb240SAbhyuday Godhasara  * @node_id:	Node-Id related to event.
289c7fdb240SAbhyuday Godhasara  * @event:	Event Mask for the Error Event.
290c7fdb240SAbhyuday Godhasara  * @wake:	Flag specifying whether the subsystem should be woken upon
291c7fdb240SAbhyuday Godhasara  *		event notification.
292c7fdb240SAbhyuday Godhasara  * @cb_fun:	Function pointer to store the callback function.
293c7fdb240SAbhyuday Godhasara  * @data:	Pointer for the driver instance.
294c7fdb240SAbhyuday Godhasara  *
295c7fdb240SAbhyuday Godhasara  * Return:	Returns 0 on successful registration else error code.
296c7fdb240SAbhyuday Godhasara  */
297c7fdb240SAbhyuday Godhasara int xlnx_register_event(const enum pm_api_cb_id cb_type, const u32 node_id, const u32 event,
298c7fdb240SAbhyuday Godhasara 			const bool wake, event_cb_func_t cb_fun, void *data)
299c7fdb240SAbhyuday Godhasara {
300c7fdb240SAbhyuday Godhasara 	int ret = 0;
301c7fdb240SAbhyuday Godhasara 	u32 eve;
302c7fdb240SAbhyuday Godhasara 	int pos;
303c7fdb240SAbhyuday Godhasara 
304c7fdb240SAbhyuday Godhasara 	if (event_manager_availability)
305c7fdb240SAbhyuday Godhasara 		return event_manager_availability;
306c7fdb240SAbhyuday Godhasara 
307c7fdb240SAbhyuday Godhasara 	if (cb_type != PM_NOTIFY_CB && cb_type != PM_INIT_SUSPEND_CB) {
308c7fdb240SAbhyuday Godhasara 		pr_err("%s() Unsupported Callback 0x%x\n", __func__, cb_type);
309c7fdb240SAbhyuday Godhasara 		return -EINVAL;
310c7fdb240SAbhyuday Godhasara 	}
311c7fdb240SAbhyuday Godhasara 
312c7fdb240SAbhyuday Godhasara 	if (!cb_fun)
313c7fdb240SAbhyuday Godhasara 		return -EFAULT;
314c7fdb240SAbhyuday Godhasara 
315c7fdb240SAbhyuday Godhasara 	if (cb_type == PM_INIT_SUSPEND_CB) {
316c7fdb240SAbhyuday Godhasara 		ret = xlnx_add_cb_for_suspend(cb_fun, data);
317c7fdb240SAbhyuday Godhasara 	} else {
318c7fdb240SAbhyuday Godhasara 		if (!xlnx_is_error_event(node_id)) {
319c7fdb240SAbhyuday Godhasara 			/* Add entry for Node-Id/Event in hash table */
320c7fdb240SAbhyuday Godhasara 			ret = xlnx_add_cb_for_notify_event(node_id, event, wake, cb_fun, data);
321c7fdb240SAbhyuday Godhasara 		} else {
322c7fdb240SAbhyuday Godhasara 			/* Add into Hash table */
323c7fdb240SAbhyuday Godhasara 			for (pos = 0; pos < MAX_BITS; pos++) {
324c7fdb240SAbhyuday Godhasara 				eve = event & (1 << pos);
325c7fdb240SAbhyuday Godhasara 				if (!eve)
326c7fdb240SAbhyuday Godhasara 					continue;
327c7fdb240SAbhyuday Godhasara 
328c7fdb240SAbhyuday Godhasara 				/* Add entry for Node-Id/Eve in hash table */
329c7fdb240SAbhyuday Godhasara 				ret = xlnx_add_cb_for_notify_event(node_id, eve, wake, cb_fun,
330c7fdb240SAbhyuday Godhasara 								   data);
331c7fdb240SAbhyuday Godhasara 				/* Break the loop if got error */
332c7fdb240SAbhyuday Godhasara 				if (ret)
333c7fdb240SAbhyuday Godhasara 					break;
334c7fdb240SAbhyuday Godhasara 			}
335c7fdb240SAbhyuday Godhasara 			if (ret) {
336c7fdb240SAbhyuday Godhasara 				/* Skip the Event for which got the error */
337c7fdb240SAbhyuday Godhasara 				pos--;
338c7fdb240SAbhyuday Godhasara 				/* Remove registered(during this call) event from hash table */
339c7fdb240SAbhyuday Godhasara 				for ( ; pos >= 0; pos--) {
340c7fdb240SAbhyuday Godhasara 					eve = event & (1 << pos);
341c7fdb240SAbhyuday Godhasara 					if (!eve)
342c7fdb240SAbhyuday Godhasara 						continue;
343e6d3c99aSAbhyuday Godhasara 					xlnx_remove_cb_for_notify_event(node_id, eve, cb_fun, data);
344c7fdb240SAbhyuday Godhasara 				}
345c7fdb240SAbhyuday Godhasara 			}
346c7fdb240SAbhyuday Godhasara 		}
347c7fdb240SAbhyuday Godhasara 
348c7fdb240SAbhyuday Godhasara 		if (ret) {
349c7fdb240SAbhyuday Godhasara 			pr_err("%s() failed for 0x%x and 0x%x: %d\r\n", __func__, node_id,
350c7fdb240SAbhyuday Godhasara 			       event, ret);
351c7fdb240SAbhyuday Godhasara 			return ret;
352c7fdb240SAbhyuday Godhasara 		}
353c7fdb240SAbhyuday Godhasara 
354c7fdb240SAbhyuday Godhasara 		/* Register for Node-Id/Event combination in firmware */
355c7fdb240SAbhyuday Godhasara 		ret = zynqmp_pm_register_notifier(node_id, event, wake, true);
356c7fdb240SAbhyuday Godhasara 		if (ret) {
357c7fdb240SAbhyuday Godhasara 			pr_err("%s() failed for 0x%x and 0x%x: %d\r\n", __func__, node_id,
358c7fdb240SAbhyuday Godhasara 			       event, ret);
359c7fdb240SAbhyuday Godhasara 			/* Remove already registered event from hash table */
360c7fdb240SAbhyuday Godhasara 			if (xlnx_is_error_event(node_id)) {
361c7fdb240SAbhyuday Godhasara 				for (pos = 0; pos < MAX_BITS; pos++) {
362c7fdb240SAbhyuday Godhasara 					eve = event & (1 << pos);
363c7fdb240SAbhyuday Godhasara 					if (!eve)
364c7fdb240SAbhyuday Godhasara 						continue;
365e6d3c99aSAbhyuday Godhasara 					xlnx_remove_cb_for_notify_event(node_id, eve, cb_fun, data);
366c7fdb240SAbhyuday Godhasara 				}
367c7fdb240SAbhyuday Godhasara 			} else {
368e6d3c99aSAbhyuday Godhasara 				xlnx_remove_cb_for_notify_event(node_id, event, cb_fun, data);
369c7fdb240SAbhyuday Godhasara 			}
370c7fdb240SAbhyuday Godhasara 			return ret;
371c7fdb240SAbhyuday Godhasara 		}
372c7fdb240SAbhyuday Godhasara 	}
373c7fdb240SAbhyuday Godhasara 
374c7fdb240SAbhyuday Godhasara 	return ret;
375c7fdb240SAbhyuday Godhasara }
376c7fdb240SAbhyuday Godhasara EXPORT_SYMBOL_GPL(xlnx_register_event);
377c7fdb240SAbhyuday Godhasara 
378c7fdb240SAbhyuday Godhasara /**
379c7fdb240SAbhyuday Godhasara  * xlnx_unregister_event() - Unregister for the event.
380c7fdb240SAbhyuday Godhasara  * @cb_type:	Type of callback from pm_api_cb_id,
381c7fdb240SAbhyuday Godhasara  *			PM_NOTIFY_CB - for Error Events,
382c7fdb240SAbhyuday Godhasara  *			PM_INIT_SUSPEND_CB - for suspend callback.
383c7fdb240SAbhyuday Godhasara  * @node_id:	Node-Id related to event.
384c7fdb240SAbhyuday Godhasara  * @event:	Event Mask for the Error Event.
385c7fdb240SAbhyuday Godhasara  * @cb_fun:	Function pointer of callback function.
386e6d3c99aSAbhyuday Godhasara  * @data:	Pointer of agent's private data.
387c7fdb240SAbhyuday Godhasara  *
388c7fdb240SAbhyuday Godhasara  * Return:	Returns 0 on successful unregistration else error code.
389c7fdb240SAbhyuday Godhasara  */
390c7fdb240SAbhyuday Godhasara int xlnx_unregister_event(const enum pm_api_cb_id cb_type, const u32 node_id, const u32 event,
391e6d3c99aSAbhyuday Godhasara 			  event_cb_func_t cb_fun, void *data)
392c7fdb240SAbhyuday Godhasara {
393e6d3c99aSAbhyuday Godhasara 	int ret = 0;
394c7fdb240SAbhyuday Godhasara 	u32 eve, pos;
395c7fdb240SAbhyuday Godhasara 
396e6d3c99aSAbhyuday Godhasara 	is_need_to_unregister = false;
397e6d3c99aSAbhyuday Godhasara 
398c7fdb240SAbhyuday Godhasara 	if (event_manager_availability)
399c7fdb240SAbhyuday Godhasara 		return event_manager_availability;
400c7fdb240SAbhyuday Godhasara 
401c7fdb240SAbhyuday Godhasara 	if (cb_type != PM_NOTIFY_CB && cb_type != PM_INIT_SUSPEND_CB) {
402c7fdb240SAbhyuday Godhasara 		pr_err("%s() Unsupported Callback 0x%x\n", __func__, cb_type);
403c7fdb240SAbhyuday Godhasara 		return -EINVAL;
404c7fdb240SAbhyuday Godhasara 	}
405c7fdb240SAbhyuday Godhasara 
406c7fdb240SAbhyuday Godhasara 	if (!cb_fun)
407c7fdb240SAbhyuday Godhasara 		return -EFAULT;
408c7fdb240SAbhyuday Godhasara 
409c7fdb240SAbhyuday Godhasara 	if (cb_type == PM_INIT_SUSPEND_CB) {
410c7fdb240SAbhyuday Godhasara 		ret = xlnx_remove_cb_for_suspend(cb_fun);
411c7fdb240SAbhyuday Godhasara 	} else {
412c7fdb240SAbhyuday Godhasara 		/* Remove Node-Id/Event from hash table */
413c7fdb240SAbhyuday Godhasara 		if (!xlnx_is_error_event(node_id)) {
414e6d3c99aSAbhyuday Godhasara 			xlnx_remove_cb_for_notify_event(node_id, event, cb_fun, data);
415c7fdb240SAbhyuday Godhasara 		} else {
416c7fdb240SAbhyuday Godhasara 			for (pos = 0; pos < MAX_BITS; pos++) {
417c7fdb240SAbhyuday Godhasara 				eve = event & (1 << pos);
418c7fdb240SAbhyuday Godhasara 				if (!eve)
419c7fdb240SAbhyuday Godhasara 					continue;
420c7fdb240SAbhyuday Godhasara 
421e6d3c99aSAbhyuday Godhasara 				xlnx_remove_cb_for_notify_event(node_id, eve, cb_fun, data);
422c7fdb240SAbhyuday Godhasara 			}
423c7fdb240SAbhyuday Godhasara 		}
424c7fdb240SAbhyuday Godhasara 
425e6d3c99aSAbhyuday Godhasara 		/* Un-register if list is empty */
426e6d3c99aSAbhyuday Godhasara 		if (is_need_to_unregister) {
427c7fdb240SAbhyuday Godhasara 			/* Un-register for Node-Id/Event combination */
428c7fdb240SAbhyuday Godhasara 			ret = zynqmp_pm_register_notifier(node_id, event, false, false);
429c7fdb240SAbhyuday Godhasara 			if (ret) {
430c7fdb240SAbhyuday Godhasara 				pr_err("%s() failed for 0x%x and 0x%x: %d\n",
431c7fdb240SAbhyuday Godhasara 				       __func__, node_id, event, ret);
432c7fdb240SAbhyuday Godhasara 				return ret;
433c7fdb240SAbhyuday Godhasara 			}
434c7fdb240SAbhyuday Godhasara 		}
435e6d3c99aSAbhyuday Godhasara 	}
436c7fdb240SAbhyuday Godhasara 
437c7fdb240SAbhyuday Godhasara 	return ret;
438c7fdb240SAbhyuday Godhasara }
439c7fdb240SAbhyuday Godhasara EXPORT_SYMBOL_GPL(xlnx_unregister_event);
440c7fdb240SAbhyuday Godhasara 
441c7fdb240SAbhyuday Godhasara static void xlnx_call_suspend_cb_handler(const u32 *payload)
442c7fdb240SAbhyuday Godhasara {
443c7fdb240SAbhyuday Godhasara 	bool is_callback_found = false;
444c7fdb240SAbhyuday Godhasara 	struct registered_event_data *eve_data;
445c7fdb240SAbhyuday Godhasara 	u32 cb_type = payload[0];
44605e5ba40SAbhyuday Godhasara 	struct agent_cb *cb_pos;
44705e5ba40SAbhyuday Godhasara 	struct agent_cb *cb_next;
448c7fdb240SAbhyuday Godhasara 
449c7fdb240SAbhyuday Godhasara 	/* Check for existing entry in hash table for given cb_type */
450c7fdb240SAbhyuday Godhasara 	hash_for_each_possible(reg_driver_map, eve_data, hentry, cb_type) {
451c7fdb240SAbhyuday Godhasara 		if (eve_data->cb_type == cb_type) {
45205e5ba40SAbhyuday Godhasara 			list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head, list) {
45305e5ba40SAbhyuday Godhasara 				cb_pos->eve_cb(&payload[0], cb_pos->agent_data);
454c7fdb240SAbhyuday Godhasara 				is_callback_found = true;
455c7fdb240SAbhyuday Godhasara 			}
456c7fdb240SAbhyuday Godhasara 		}
45705e5ba40SAbhyuday Godhasara 	}
458c7fdb240SAbhyuday Godhasara 	if (!is_callback_found)
459c7fdb240SAbhyuday Godhasara 		pr_warn("Didn't find any registered callback for suspend event\n");
460c7fdb240SAbhyuday Godhasara }
461c7fdb240SAbhyuday Godhasara 
462c7fdb240SAbhyuday Godhasara static void xlnx_call_notify_cb_handler(const u32 *payload)
463c7fdb240SAbhyuday Godhasara {
464c7fdb240SAbhyuday Godhasara 	bool is_callback_found = false;
465c7fdb240SAbhyuday Godhasara 	struct registered_event_data *eve_data;
466c7fdb240SAbhyuday Godhasara 	u64 key = ((u64)payload[1] << 32U) | (u64)payload[2];
467c7fdb240SAbhyuday Godhasara 	int ret;
46805e5ba40SAbhyuday Godhasara 	struct agent_cb *cb_pos;
46905e5ba40SAbhyuday Godhasara 	struct agent_cb *cb_next;
470c7fdb240SAbhyuday Godhasara 
471c7fdb240SAbhyuday Godhasara 	/* Check for existing entry in hash table for given key id */
472c7fdb240SAbhyuday Godhasara 	hash_for_each_possible(reg_driver_map, eve_data, hentry, key) {
473c7fdb240SAbhyuday Godhasara 		if (eve_data->key == key) {
47405e5ba40SAbhyuday Godhasara 			list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head, list) {
47505e5ba40SAbhyuday Godhasara 				cb_pos->eve_cb(&payload[0], cb_pos->agent_data);
476c7fdb240SAbhyuday Godhasara 				is_callback_found = true;
47705e5ba40SAbhyuday Godhasara 			}
478c7fdb240SAbhyuday Godhasara 
479c7fdb240SAbhyuday Godhasara 			/* re register with firmware to get future events */
480c7fdb240SAbhyuday Godhasara 			ret = zynqmp_pm_register_notifier(payload[1], payload[2],
481c7fdb240SAbhyuday Godhasara 							  eve_data->wake, true);
482c7fdb240SAbhyuday Godhasara 			if (ret) {
483c7fdb240SAbhyuday Godhasara 				pr_err("%s() failed for 0x%x and 0x%x: %d\r\n", __func__,
484c7fdb240SAbhyuday Godhasara 				       payload[1], payload[2], ret);
48505e5ba40SAbhyuday Godhasara 				list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head,
48605e5ba40SAbhyuday Godhasara 							 list) {
487c7fdb240SAbhyuday Godhasara 					/* Remove already registered event from hash table */
488c7fdb240SAbhyuday Godhasara 					xlnx_remove_cb_for_notify_event(payload[1], payload[2],
489e6d3c99aSAbhyuday Godhasara 									cb_pos->eve_cb,
490e6d3c99aSAbhyuday Godhasara 									cb_pos->agent_data);
49105e5ba40SAbhyuday Godhasara 				}
492c7fdb240SAbhyuday Godhasara 			}
493c7fdb240SAbhyuday Godhasara 		}
494c7fdb240SAbhyuday Godhasara 	}
495c7fdb240SAbhyuday Godhasara 	if (!is_callback_found)
4969c6724abSTanmay Shah 		pr_warn("Unhandled SGI node 0x%x event 0x%x. Expected with Xen hypervisor\n",
497c7fdb240SAbhyuday Godhasara 			payload[1], payload[2]);
498c7fdb240SAbhyuday Godhasara }
499c7fdb240SAbhyuday Godhasara 
500c7fdb240SAbhyuday Godhasara static void xlnx_get_event_callback_data(u32 *buf)
501c7fdb240SAbhyuday Godhasara {
502f922b16aSJay Buddhabhatti 	zynqmp_pm_invoke_fn(GET_CALLBACK_DATA, buf, 0);
503c7fdb240SAbhyuday Godhasara }
504c7fdb240SAbhyuday Godhasara 
505c7fdb240SAbhyuday Godhasara static irqreturn_t xlnx_event_handler(int irq, void *dev_id)
506c7fdb240SAbhyuday Godhasara {
507c7fdb240SAbhyuday Godhasara 	u32 cb_type, node_id, event, pos;
508c7fdb240SAbhyuday Godhasara 	u32 payload[CB_MAX_PAYLOAD_SIZE] = {0};
509c7fdb240SAbhyuday Godhasara 	u32 event_data[CB_MAX_PAYLOAD_SIZE] = {0};
510c7fdb240SAbhyuday Godhasara 
511c7fdb240SAbhyuday Godhasara 	/* Get event data */
512c7fdb240SAbhyuday Godhasara 	xlnx_get_event_callback_data(payload);
513c7fdb240SAbhyuday Godhasara 
514c7fdb240SAbhyuday Godhasara 	/* First element is callback type, others are callback arguments */
515c7fdb240SAbhyuday Godhasara 	cb_type = payload[0];
516c7fdb240SAbhyuday Godhasara 
517c7fdb240SAbhyuday Godhasara 	if (cb_type == PM_NOTIFY_CB) {
518c7fdb240SAbhyuday Godhasara 		node_id = payload[1];
519c7fdb240SAbhyuday Godhasara 		event = payload[2];
520c7fdb240SAbhyuday Godhasara 		if (!xlnx_is_error_event(node_id)) {
521c7fdb240SAbhyuday Godhasara 			xlnx_call_notify_cb_handler(payload);
522c7fdb240SAbhyuday Godhasara 		} else {
523c7fdb240SAbhyuday Godhasara 			/*
524c7fdb240SAbhyuday Godhasara 			 * Each call back function expecting payload as an input arguments.
525c7fdb240SAbhyuday Godhasara 			 * We can get multiple error events as in one call back through error
526c7fdb240SAbhyuday Godhasara 			 * mask. So payload[2] may can contain multiple error events.
527c7fdb240SAbhyuday Godhasara 			 * In reg_driver_map database we store data in the combination of single
528c7fdb240SAbhyuday Godhasara 			 * node_id-error combination.
529c7fdb240SAbhyuday Godhasara 			 * So coping the payload message into event_data and update the
530c7fdb240SAbhyuday Godhasara 			 * event_data[2] with Error Mask for single error event and use
531c7fdb240SAbhyuday Godhasara 			 * event_data as input argument for registered call back function.
532c7fdb240SAbhyuday Godhasara 			 *
533c7fdb240SAbhyuday Godhasara 			 */
534c7fdb240SAbhyuday Godhasara 			memcpy(event_data, payload, (4 * CB_MAX_PAYLOAD_SIZE));
535c7fdb240SAbhyuday Godhasara 			/* Support Multiple Error Event */
536c7fdb240SAbhyuday Godhasara 			for (pos = 0; pos < MAX_BITS; pos++) {
537c7fdb240SAbhyuday Godhasara 				if ((0 == (event & (1 << pos))))
538c7fdb240SAbhyuday Godhasara 					continue;
539c7fdb240SAbhyuday Godhasara 				event_data[2] = (event & (1 << pos));
540c7fdb240SAbhyuday Godhasara 				xlnx_call_notify_cb_handler(event_data);
541c7fdb240SAbhyuday Godhasara 			}
542c7fdb240SAbhyuday Godhasara 		}
543c7fdb240SAbhyuday Godhasara 	} else if (cb_type == PM_INIT_SUSPEND_CB) {
544c7fdb240SAbhyuday Godhasara 		xlnx_call_suspend_cb_handler(payload);
545c7fdb240SAbhyuday Godhasara 	} else {
546c7fdb240SAbhyuday Godhasara 		pr_err("%s() Unsupported Callback %d\n", __func__, cb_type);
547c7fdb240SAbhyuday Godhasara 	}
548c7fdb240SAbhyuday Godhasara 
549c7fdb240SAbhyuday Godhasara 	return IRQ_HANDLED;
550c7fdb240SAbhyuday Godhasara }
551c7fdb240SAbhyuday Godhasara 
552c7fdb240SAbhyuday Godhasara static int xlnx_event_cpuhp_start(unsigned int cpu)
553c7fdb240SAbhyuday Godhasara {
554c7fdb240SAbhyuday Godhasara 	enable_percpu_irq(virq_sgi, IRQ_TYPE_NONE);
555c7fdb240SAbhyuday Godhasara 
556c7fdb240SAbhyuday Godhasara 	return 0;
557c7fdb240SAbhyuday Godhasara }
558c7fdb240SAbhyuday Godhasara 
559c7fdb240SAbhyuday Godhasara static int xlnx_event_cpuhp_down(unsigned int cpu)
560c7fdb240SAbhyuday Godhasara {
561c7fdb240SAbhyuday Godhasara 	disable_percpu_irq(virq_sgi);
562c7fdb240SAbhyuday Godhasara 
563c7fdb240SAbhyuday Godhasara 	return 0;
564c7fdb240SAbhyuday Godhasara }
565c7fdb240SAbhyuday Godhasara 
566c7fdb240SAbhyuday Godhasara static void xlnx_disable_percpu_irq(void *data)
567c7fdb240SAbhyuday Godhasara {
568c7fdb240SAbhyuday Godhasara 	disable_percpu_irq(virq_sgi);
569c7fdb240SAbhyuday Godhasara }
570c7fdb240SAbhyuday Godhasara 
571c7fdb240SAbhyuday Godhasara static int xlnx_event_init_sgi(struct platform_device *pdev)
572c7fdb240SAbhyuday Godhasara {
573c7fdb240SAbhyuday Godhasara 	int ret = 0;
574c7fdb240SAbhyuday Godhasara 	/*
575c7fdb240SAbhyuday Godhasara 	 * IRQ related structures are used for the following:
576c7fdb240SAbhyuday Godhasara 	 * for each SGI interrupt ensure its mapped by GIC IRQ domain
577c7fdb240SAbhyuday Godhasara 	 * and that each corresponding linux IRQ for the HW IRQ has
578c7fdb240SAbhyuday Godhasara 	 * a handler for when receiving an interrupt from the remote
579c7fdb240SAbhyuday Godhasara 	 * processor.
580c7fdb240SAbhyuday Godhasara 	 */
581c7fdb240SAbhyuday Godhasara 	struct irq_domain *domain;
582c7fdb240SAbhyuday Godhasara 	struct irq_fwspec sgi_fwspec;
583c7fdb240SAbhyuday Godhasara 	struct device_node *interrupt_parent = NULL;
584c7fdb240SAbhyuday Godhasara 	struct device *parent = pdev->dev.parent;
585c7fdb240SAbhyuday Godhasara 
586c7fdb240SAbhyuday Godhasara 	/* Find GIC controller to map SGIs. */
587c7fdb240SAbhyuday Godhasara 	interrupt_parent = of_irq_find_parent(parent->of_node);
588c7fdb240SAbhyuday Godhasara 	if (!interrupt_parent) {
589c7fdb240SAbhyuday Godhasara 		dev_err(&pdev->dev, "Failed to find property for Interrupt parent\n");
590c7fdb240SAbhyuday Godhasara 		return -EINVAL;
591c7fdb240SAbhyuday Godhasara 	}
592c7fdb240SAbhyuday Godhasara 
593c7fdb240SAbhyuday Godhasara 	/* Each SGI needs to be associated with GIC's IRQ domain. */
594c7fdb240SAbhyuday Godhasara 	domain = irq_find_host(interrupt_parent);
595c7fdb240SAbhyuday Godhasara 	of_node_put(interrupt_parent);
596c7fdb240SAbhyuday Godhasara 
597c7fdb240SAbhyuday Godhasara 	/* Each mapping needs GIC domain when finding IRQ mapping. */
598c7fdb240SAbhyuday Godhasara 	sgi_fwspec.fwnode = domain->fwnode;
599c7fdb240SAbhyuday Godhasara 
600c7fdb240SAbhyuday Godhasara 	/*
601c7fdb240SAbhyuday Godhasara 	 * When irq domain looks at mapping each arg is as follows:
602c7fdb240SAbhyuday Godhasara 	 * 3 args for: interrupt type (SGI), interrupt # (set later), type
603c7fdb240SAbhyuday Godhasara 	 */
604c7fdb240SAbhyuday Godhasara 	sgi_fwspec.param_count = 1;
605c7fdb240SAbhyuday Godhasara 
606c7fdb240SAbhyuday Godhasara 	/* Set SGI's hwirq */
607c7fdb240SAbhyuday Godhasara 	sgi_fwspec.param[0] = sgi_num;
608c7fdb240SAbhyuday Godhasara 	virq_sgi = irq_create_fwspec_mapping(&sgi_fwspec);
609c7fdb240SAbhyuday Godhasara 
610c7fdb240SAbhyuday Godhasara 	ret = request_percpu_irq(virq_sgi, xlnx_event_handler, "xlnx_event_mgmt",
611*4a95449dSJay Buddhabhatti 				 &dummy_cpu_number);
612daed80edSHariBabu Gattem 
613c7fdb240SAbhyuday Godhasara 	WARN_ON(ret);
614c7fdb240SAbhyuday Godhasara 	if (ret) {
615c7fdb240SAbhyuday Godhasara 		irq_dispose_mapping(virq_sgi);
616c7fdb240SAbhyuday Godhasara 		return ret;
617c7fdb240SAbhyuday Godhasara 	}
618c7fdb240SAbhyuday Godhasara 
619c7fdb240SAbhyuday Godhasara 	irq_to_desc(virq_sgi);
620c7fdb240SAbhyuday Godhasara 	irq_set_status_flags(virq_sgi, IRQ_PER_CPU);
621c7fdb240SAbhyuday Godhasara 
622c7fdb240SAbhyuday Godhasara 	return ret;
623c7fdb240SAbhyuday Godhasara }
624c7fdb240SAbhyuday Godhasara 
625c7fdb240SAbhyuday Godhasara static void xlnx_event_cleanup_sgi(struct platform_device *pdev)
626c7fdb240SAbhyuday Godhasara {
627c7fdb240SAbhyuday Godhasara 	cpuhp_remove_state(CPUHP_AP_ONLINE_DYN);
628c7fdb240SAbhyuday Godhasara 
629c7fdb240SAbhyuday Godhasara 	on_each_cpu(xlnx_disable_percpu_irq, NULL, 1);
630c7fdb240SAbhyuday Godhasara 
631c7fdb240SAbhyuday Godhasara 	irq_clear_status_flags(virq_sgi, IRQ_PER_CPU);
632*4a95449dSJay Buddhabhatti 	free_percpu_irq(virq_sgi, &dummy_cpu_number);
633c7fdb240SAbhyuday Godhasara 	irq_dispose_mapping(virq_sgi);
634c7fdb240SAbhyuday Godhasara }
635c7fdb240SAbhyuday Godhasara 
636c7fdb240SAbhyuday Godhasara static int xlnx_event_manager_probe(struct platform_device *pdev)
637c7fdb240SAbhyuday Godhasara {
638c7fdb240SAbhyuday Godhasara 	int ret;
639c7fdb240SAbhyuday Godhasara 
640c7fdb240SAbhyuday Godhasara 	ret = zynqmp_pm_feature(PM_REGISTER_NOTIFIER);
641c7fdb240SAbhyuday Godhasara 	if (ret < 0) {
642c7fdb240SAbhyuday Godhasara 		dev_err(&pdev->dev, "Feature check failed with %d\n", ret);
643c7fdb240SAbhyuday Godhasara 		return ret;
644c7fdb240SAbhyuday Godhasara 	}
645c7fdb240SAbhyuday Godhasara 
646c7fdb240SAbhyuday Godhasara 	if ((ret & FIRMWARE_VERSION_MASK) <
647c7fdb240SAbhyuday Godhasara 	    REGISTER_NOTIFIER_FIRMWARE_VERSION) {
648c7fdb240SAbhyuday Godhasara 		dev_err(&pdev->dev, "Register notifier version error. Expected Firmware: v%d - Found: v%d\n",
649c7fdb240SAbhyuday Godhasara 			REGISTER_NOTIFIER_FIRMWARE_VERSION,
650c7fdb240SAbhyuday Godhasara 			ret & FIRMWARE_VERSION_MASK);
651c7fdb240SAbhyuday Godhasara 		return -EOPNOTSUPP;
652c7fdb240SAbhyuday Godhasara 	}
653c7fdb240SAbhyuday Godhasara 
654c7fdb240SAbhyuday Godhasara 	/* Initialize the SGI */
655c7fdb240SAbhyuday Godhasara 	ret = xlnx_event_init_sgi(pdev);
656c7fdb240SAbhyuday Godhasara 	if (ret) {
657c7fdb240SAbhyuday Godhasara 		dev_err(&pdev->dev, "SGI Init has been failed with %d\n", ret);
658c7fdb240SAbhyuday Godhasara 		return ret;
659c7fdb240SAbhyuday Godhasara 	}
660c7fdb240SAbhyuday Godhasara 
661c7fdb240SAbhyuday Godhasara 	/* Setup function for the CPU hot-plug cases */
662c7fdb240SAbhyuday Godhasara 	cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "soc/event:starting",
663c7fdb240SAbhyuday Godhasara 			  xlnx_event_cpuhp_start, xlnx_event_cpuhp_down);
664c7fdb240SAbhyuday Godhasara 
665acd6510dSTanmay Shah 	ret = zynqmp_pm_register_sgi(sgi_num, 0);
666c7fdb240SAbhyuday Godhasara 	if (ret) {
6678c016c80SJay Buddhabhatti 		if (ret == -EOPNOTSUPP)
6688c016c80SJay Buddhabhatti 			dev_err(&pdev->dev, "SGI registration not supported by TF-A or Xen\n");
6698c016c80SJay Buddhabhatti 		else
6708c016c80SJay Buddhabhatti 			dev_err(&pdev->dev, "SGI %d registration failed, err %d\n", sgi_num, ret);
6718c016c80SJay Buddhabhatti 
672c7fdb240SAbhyuday Godhasara 		xlnx_event_cleanup_sgi(pdev);
673c7fdb240SAbhyuday Godhasara 		return ret;
674c7fdb240SAbhyuday Godhasara 	}
675c7fdb240SAbhyuday Godhasara 
676c7fdb240SAbhyuday Godhasara 	event_manager_availability = 0;
677c7fdb240SAbhyuday Godhasara 
678c7fdb240SAbhyuday Godhasara 	dev_info(&pdev->dev, "SGI %d Registered over TF-A\n", sgi_num);
679c7fdb240SAbhyuday Godhasara 	dev_info(&pdev->dev, "Xilinx Event Management driver probed\n");
680c7fdb240SAbhyuday Godhasara 
681c7fdb240SAbhyuday Godhasara 	return ret;
682c7fdb240SAbhyuday Godhasara }
683c7fdb240SAbhyuday Godhasara 
684e8864065SUwe Kleine-König static void xlnx_event_manager_remove(struct platform_device *pdev)
685c7fdb240SAbhyuday Godhasara {
686c7fdb240SAbhyuday Godhasara 	int i;
687c7fdb240SAbhyuday Godhasara 	struct registered_event_data *eve_data;
688c7fdb240SAbhyuday Godhasara 	struct hlist_node *tmp;
689c7fdb240SAbhyuday Godhasara 	int ret;
69005e5ba40SAbhyuday Godhasara 	struct agent_cb *cb_pos;
69105e5ba40SAbhyuday Godhasara 	struct agent_cb *cb_next;
692c7fdb240SAbhyuday Godhasara 
693c7fdb240SAbhyuday Godhasara 	hash_for_each_safe(reg_driver_map, i, tmp, eve_data, hentry) {
69405e5ba40SAbhyuday Godhasara 		list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head, list) {
69505e5ba40SAbhyuday Godhasara 			list_del_init(&cb_pos->list);
69605e5ba40SAbhyuday Godhasara 			kfree(cb_pos);
69705e5ba40SAbhyuday Godhasara 		}
698c7fdb240SAbhyuday Godhasara 		hash_del(&eve_data->hentry);
699c7fdb240SAbhyuday Godhasara 		kfree(eve_data);
700c7fdb240SAbhyuday Godhasara 	}
701c7fdb240SAbhyuday Godhasara 
702acd6510dSTanmay Shah 	ret = zynqmp_pm_register_sgi(0, 1);
703c7fdb240SAbhyuday Godhasara 	if (ret)
704c7fdb240SAbhyuday Godhasara 		dev_err(&pdev->dev, "SGI unregistration over TF-A failed with %d\n", ret);
705c7fdb240SAbhyuday Godhasara 
706c7fdb240SAbhyuday Godhasara 	xlnx_event_cleanup_sgi(pdev);
707c7fdb240SAbhyuday Godhasara 
708c7fdb240SAbhyuday Godhasara 	event_manager_availability = -EACCES;
709c7fdb240SAbhyuday Godhasara }
710c7fdb240SAbhyuday Godhasara 
711c7fdb240SAbhyuday Godhasara static struct platform_driver xlnx_event_manager_driver = {
712c7fdb240SAbhyuday Godhasara 	.probe = xlnx_event_manager_probe,
713e8864065SUwe Kleine-König 	.remove_new = xlnx_event_manager_remove,
714c7fdb240SAbhyuday Godhasara 	.driver = {
715c7fdb240SAbhyuday Godhasara 		.name = "xlnx_event_manager",
716c7fdb240SAbhyuday Godhasara 	},
717c7fdb240SAbhyuday Godhasara };
718c7fdb240SAbhyuday Godhasara module_param(sgi_num, uint, 0);
719c7fdb240SAbhyuday Godhasara module_platform_driver(xlnx_event_manager_driver);
720