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