GNU Linux-libre 5.19-rc6-gnu
[releases.git] / drivers / soc / xilinx / xlnx_event_manager.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Xilinx Event Management Driver
4  *
5  *  Copyright (C) 2021 Xilinx, Inc.
6  *
7  *  Abhyuday Godhasara <abhyuday.godhasara@xilinx.com>
8  */
9
10 #include <linux/cpuhotplug.h>
11 #include <linux/firmware/xlnx-event-manager.h>
12 #include <linux/firmware/xlnx-zynqmp.h>
13 #include <linux/hashtable.h>
14 #include <linux/interrupt.h>
15 #include <linux/irq.h>
16 #include <linux/irqdomain.h>
17 #include <linux/module.h>
18 #include <linux/of_irq.h>
19 #include <linux/platform_device.h>
20 #include <linux/slab.h>
21
22 static DEFINE_PER_CPU_READ_MOSTLY(int, cpu_number1);
23
24 static int virq_sgi;
25 static int event_manager_availability = -EACCES;
26
27 /* SGI number used for Event management driver */
28 #define XLNX_EVENT_SGI_NUM      (15)
29
30 /* Max number of driver can register for same event */
31 #define MAX_DRIVER_PER_EVENT    (10U)
32
33 /* Max HashMap Order for PM API feature check (1<<7 = 128) */
34 #define REGISTERED_DRIVER_MAX_ORDER     (7)
35
36 #define MAX_BITS        (32U) /* Number of bits available for error mask */
37
38 #define FIRMWARE_VERSION_MASK                   (0xFFFFU)
39 #define REGISTER_NOTIFIER_FIRMWARE_VERSION      (2U)
40
41 static DEFINE_HASHTABLE(reg_driver_map, REGISTERED_DRIVER_MAX_ORDER);
42 static int sgi_num = XLNX_EVENT_SGI_NUM;
43
44 static bool is_need_to_unregister;
45
46 /**
47  * struct agent_cb - Registered callback function and private data.
48  * @agent_data:         Data passed back to handler function.
49  * @eve_cb:             Function pointer to store the callback function.
50  * @list:               member to create list.
51  */
52 struct agent_cb {
53         void *agent_data;
54         event_cb_func_t eve_cb;
55         struct list_head list;
56 };
57
58 /**
59  * struct registered_event_data - Registered Event Data.
60  * @key:                key is the combine id(Node-Id | Event-Id) of type u64
61  *                      where upper u32 for Node-Id and lower u32 for Event-Id,
62  *                      And this used as key to index into hashmap.
63  * @cb_type:            Type of Api callback, like PM_NOTIFY_CB, etc.
64  * @wake:               If this flag set, firmware will wake up processor if is
65  *                      in sleep or power down state.
66  * @cb_list_head:       Head of call back data list which contain the information
67  *                      about registered handler and private data.
68  * @hentry:             hlist_node that hooks this entry into hashtable.
69  */
70 struct registered_event_data {
71         u64 key;
72         enum pm_api_cb_id cb_type;
73         bool wake;
74         struct list_head cb_list_head;
75         struct hlist_node hentry;
76 };
77
78 static bool xlnx_is_error_event(const u32 node_id)
79 {
80         if (node_id == EVENT_ERROR_PMC_ERR1 ||
81             node_id == EVENT_ERROR_PMC_ERR2 ||
82             node_id == EVENT_ERROR_PSM_ERR1 ||
83             node_id == EVENT_ERROR_PSM_ERR2)
84                 return true;
85
86         return false;
87 }
88
89 static int xlnx_add_cb_for_notify_event(const u32 node_id, const u32 event, const bool wake,
90                                         event_cb_func_t cb_fun, void *data)
91 {
92         u64 key = 0;
93         bool present_in_hash = false;
94         struct registered_event_data *eve_data;
95         struct agent_cb *cb_data;
96         struct agent_cb *cb_pos;
97         struct agent_cb *cb_next;
98
99         key = ((u64)node_id << 32U) | (u64)event;
100         /* Check for existing entry in hash table for given key id */
101         hash_for_each_possible(reg_driver_map, eve_data, hentry, key) {
102                 if (eve_data->key == key) {
103                         present_in_hash = true;
104                         break;
105                 }
106         }
107
108         if (!present_in_hash) {
109                 /* Add new entry if not present in HASH table */
110                 eve_data = kmalloc(sizeof(*eve_data), GFP_KERNEL);
111                 if (!eve_data)
112                         return -ENOMEM;
113                 eve_data->key = key;
114                 eve_data->cb_type = PM_NOTIFY_CB;
115                 eve_data->wake = wake;
116                 INIT_LIST_HEAD(&eve_data->cb_list_head);
117
118                 cb_data = kmalloc(sizeof(*cb_data), GFP_KERNEL);
119                 if (!cb_data)
120                         return -ENOMEM;
121                 cb_data->eve_cb = cb_fun;
122                 cb_data->agent_data = data;
123
124                 /* Add into callback list */
125                 list_add(&cb_data->list, &eve_data->cb_list_head);
126
127                 /* Add into HASH table */
128                 hash_add(reg_driver_map, &eve_data->hentry, key);
129         } else {
130                 /* Search for callback function and private data in list */
131                 list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head, list) {
132                         if (cb_pos->eve_cb == cb_fun &&
133                             cb_pos->agent_data == data) {
134                                 return 0;
135                         }
136                 }
137
138                 /* Add multiple handler and private data in list */
139                 cb_data = kmalloc(sizeof(*cb_data), GFP_KERNEL);
140                 if (!cb_data)
141                         return -ENOMEM;
142                 cb_data->eve_cb = cb_fun;
143                 cb_data->agent_data = data;
144
145                 list_add(&cb_data->list, &eve_data->cb_list_head);
146         }
147
148         return 0;
149 }
150
151 static int xlnx_add_cb_for_suspend(event_cb_func_t cb_fun, void *data)
152 {
153         struct registered_event_data *eve_data;
154         struct agent_cb *cb_data;
155
156         /* Check for existing entry in hash table for given cb_type */
157         hash_for_each_possible(reg_driver_map, eve_data, hentry, PM_INIT_SUSPEND_CB) {
158                 if (eve_data->cb_type == PM_INIT_SUSPEND_CB) {
159                         pr_err("Found as already registered\n");
160                         return -EINVAL;
161                 }
162         }
163
164         /* Add new entry if not present */
165         eve_data = kmalloc(sizeof(*eve_data), GFP_KERNEL);
166         if (!eve_data)
167                 return -ENOMEM;
168
169         eve_data->key = 0;
170         eve_data->cb_type = PM_INIT_SUSPEND_CB;
171         INIT_LIST_HEAD(&eve_data->cb_list_head);
172
173         cb_data = kmalloc(sizeof(*cb_data), GFP_KERNEL);
174         if (!cb_data)
175                 return -ENOMEM;
176         cb_data->eve_cb = cb_fun;
177         cb_data->agent_data = data;
178
179         /* Add into callback list */
180         list_add(&cb_data->list, &eve_data->cb_list_head);
181
182         hash_add(reg_driver_map, &eve_data->hentry, PM_INIT_SUSPEND_CB);
183
184         return 0;
185 }
186
187 static int xlnx_remove_cb_for_suspend(event_cb_func_t cb_fun)
188 {
189         bool is_callback_found = false;
190         struct registered_event_data *eve_data;
191         struct agent_cb *cb_pos;
192         struct agent_cb *cb_next;
193
194         is_need_to_unregister = false;
195
196         /* Check for existing entry in hash table for given cb_type */
197         hash_for_each_possible(reg_driver_map, eve_data, hentry, PM_INIT_SUSPEND_CB) {
198                 if (eve_data->cb_type == PM_INIT_SUSPEND_CB) {
199                         /* Delete the list of callback */
200                         list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head, list) {
201                                 if (cb_pos->eve_cb == cb_fun) {
202                                         is_callback_found = true;
203                                         list_del_init(&cb_pos->list);
204                                         kfree(cb_pos);
205                                 }
206                         }
207                         /* remove an object from a hashtable */
208                         hash_del(&eve_data->hentry);
209                         kfree(eve_data);
210                         is_need_to_unregister = true;
211                 }
212         }
213         if (!is_callback_found) {
214                 pr_warn("Didn't find any registered callback for suspend event\n");
215                 return -EINVAL;
216         }
217
218         return 0;
219 }
220
221 static int xlnx_remove_cb_for_notify_event(const u32 node_id, const u32 event,
222                                            event_cb_func_t cb_fun, void *data)
223 {
224         bool is_callback_found = false;
225         struct registered_event_data *eve_data;
226         u64 key = ((u64)node_id << 32U) | (u64)event;
227         struct agent_cb *cb_pos;
228         struct agent_cb *cb_next;
229
230         is_need_to_unregister = false;
231
232         /* Check for existing entry in hash table for given key id */
233         hash_for_each_possible(reg_driver_map, eve_data, hentry, key) {
234                 if (eve_data->key == key) {
235                         /* Delete the list of callback */
236                         list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head, list) {
237                                 if (cb_pos->eve_cb == cb_fun &&
238                                     cb_pos->agent_data == data) {
239                                         is_callback_found = true;
240                                         list_del_init(&cb_pos->list);
241                                         kfree(cb_pos);
242                                 }
243                         }
244
245                         /* Remove HASH table if callback list is empty */
246                         if (list_empty(&eve_data->cb_list_head)) {
247                                 /* remove an object from a HASH table */
248                                 hash_del(&eve_data->hentry);
249                                 kfree(eve_data);
250                                 is_need_to_unregister = true;
251                         }
252                 }
253         }
254         if (!is_callback_found) {
255                 pr_warn("Didn't find any registered callback for 0x%x 0x%x\n",
256                         node_id, event);
257                 return -EINVAL;
258         }
259
260         return 0;
261 }
262
263 /**
264  * xlnx_register_event() - Register for the event.
265  * @cb_type:    Type of callback from pm_api_cb_id,
266  *                      PM_NOTIFY_CB - for Error Events,
267  *                      PM_INIT_SUSPEND_CB - for suspend callback.
268  * @node_id:    Node-Id related to event.
269  * @event:      Event Mask for the Error Event.
270  * @wake:       Flag specifying whether the subsystem should be woken upon
271  *              event notification.
272  * @cb_fun:     Function pointer to store the callback function.
273  * @data:       Pointer for the driver instance.
274  *
275  * Return:      Returns 0 on successful registration else error code.
276  */
277 int xlnx_register_event(const enum pm_api_cb_id cb_type, const u32 node_id, const u32 event,
278                         const bool wake, event_cb_func_t cb_fun, void *data)
279 {
280         int ret = 0;
281         u32 eve;
282         int pos;
283
284         if (event_manager_availability)
285                 return event_manager_availability;
286
287         if (cb_type != PM_NOTIFY_CB && cb_type != PM_INIT_SUSPEND_CB) {
288                 pr_err("%s() Unsupported Callback 0x%x\n", __func__, cb_type);
289                 return -EINVAL;
290         }
291
292         if (!cb_fun)
293                 return -EFAULT;
294
295         if (cb_type == PM_INIT_SUSPEND_CB) {
296                 ret = xlnx_add_cb_for_suspend(cb_fun, data);
297         } else {
298                 if (!xlnx_is_error_event(node_id)) {
299                         /* Add entry for Node-Id/Event in hash table */
300                         ret = xlnx_add_cb_for_notify_event(node_id, event, wake, cb_fun, data);
301                 } else {
302                         /* Add into Hash table */
303                         for (pos = 0; pos < MAX_BITS; pos++) {
304                                 eve = event & (1 << pos);
305                                 if (!eve)
306                                         continue;
307
308                                 /* Add entry for Node-Id/Eve in hash table */
309                                 ret = xlnx_add_cb_for_notify_event(node_id, eve, wake, cb_fun,
310                                                                    data);
311                                 /* Break the loop if got error */
312                                 if (ret)
313                                         break;
314                         }
315                         if (ret) {
316                                 /* Skip the Event for which got the error */
317                                 pos--;
318                                 /* Remove registered(during this call) event from hash table */
319                                 for ( ; pos >= 0; pos--) {
320                                         eve = event & (1 << pos);
321                                         if (!eve)
322                                                 continue;
323                                         xlnx_remove_cb_for_notify_event(node_id, eve, cb_fun, data);
324                                 }
325                         }
326                 }
327
328                 if (ret) {
329                         pr_err("%s() failed for 0x%x and 0x%x: %d\r\n", __func__, node_id,
330                                event, ret);
331                         return ret;
332                 }
333
334                 /* Register for Node-Id/Event combination in firmware */
335                 ret = zynqmp_pm_register_notifier(node_id, event, wake, true);
336                 if (ret) {
337                         pr_err("%s() failed for 0x%x and 0x%x: %d\r\n", __func__, node_id,
338                                event, ret);
339                         /* Remove already registered event from hash table */
340                         if (xlnx_is_error_event(node_id)) {
341                                 for (pos = 0; pos < MAX_BITS; pos++) {
342                                         eve = event & (1 << pos);
343                                         if (!eve)
344                                                 continue;
345                                         xlnx_remove_cb_for_notify_event(node_id, eve, cb_fun, data);
346                                 }
347                         } else {
348                                 xlnx_remove_cb_for_notify_event(node_id, event, cb_fun, data);
349                         }
350                         return ret;
351                 }
352         }
353
354         return ret;
355 }
356 EXPORT_SYMBOL_GPL(xlnx_register_event);
357
358 /**
359  * xlnx_unregister_event() - Unregister for the event.
360  * @cb_type:    Type of callback from pm_api_cb_id,
361  *                      PM_NOTIFY_CB - for Error Events,
362  *                      PM_INIT_SUSPEND_CB - for suspend callback.
363  * @node_id:    Node-Id related to event.
364  * @event:      Event Mask for the Error Event.
365  * @cb_fun:     Function pointer of callback function.
366  * @data:       Pointer of agent's private data.
367  *
368  * Return:      Returns 0 on successful unregistration else error code.
369  */
370 int xlnx_unregister_event(const enum pm_api_cb_id cb_type, const u32 node_id, const u32 event,
371                           event_cb_func_t cb_fun, void *data)
372 {
373         int ret = 0;
374         u32 eve, pos;
375
376         is_need_to_unregister = false;
377
378         if (event_manager_availability)
379                 return event_manager_availability;
380
381         if (cb_type != PM_NOTIFY_CB && cb_type != PM_INIT_SUSPEND_CB) {
382                 pr_err("%s() Unsupported Callback 0x%x\n", __func__, cb_type);
383                 return -EINVAL;
384         }
385
386         if (!cb_fun)
387                 return -EFAULT;
388
389         if (cb_type == PM_INIT_SUSPEND_CB) {
390                 ret = xlnx_remove_cb_for_suspend(cb_fun);
391         } else {
392                 /* Remove Node-Id/Event from hash table */
393                 if (!xlnx_is_error_event(node_id)) {
394                         xlnx_remove_cb_for_notify_event(node_id, event, cb_fun, data);
395                 } else {
396                         for (pos = 0; pos < MAX_BITS; pos++) {
397                                 eve = event & (1 << pos);
398                                 if (!eve)
399                                         continue;
400
401                                 xlnx_remove_cb_for_notify_event(node_id, eve, cb_fun, data);
402                         }
403                 }
404
405                 /* Un-register if list is empty */
406                 if (is_need_to_unregister) {
407                         /* Un-register for Node-Id/Event combination */
408                         ret = zynqmp_pm_register_notifier(node_id, event, false, false);
409                         if (ret) {
410                                 pr_err("%s() failed for 0x%x and 0x%x: %d\n",
411                                        __func__, node_id, event, ret);
412                                 return ret;
413                         }
414                 }
415         }
416
417         return ret;
418 }
419 EXPORT_SYMBOL_GPL(xlnx_unregister_event);
420
421 static void xlnx_call_suspend_cb_handler(const u32 *payload)
422 {
423         bool is_callback_found = false;
424         struct registered_event_data *eve_data;
425         u32 cb_type = payload[0];
426         struct agent_cb *cb_pos;
427         struct agent_cb *cb_next;
428
429         /* Check for existing entry in hash table for given cb_type */
430         hash_for_each_possible(reg_driver_map, eve_data, hentry, cb_type) {
431                 if (eve_data->cb_type == cb_type) {
432                         list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head, list) {
433                                 cb_pos->eve_cb(&payload[0], cb_pos->agent_data);
434                                 is_callback_found = true;
435                         }
436                 }
437         }
438         if (!is_callback_found)
439                 pr_warn("Didn't find any registered callback for suspend event\n");
440 }
441
442 static void xlnx_call_notify_cb_handler(const u32 *payload)
443 {
444         bool is_callback_found = false;
445         struct registered_event_data *eve_data;
446         u64 key = ((u64)payload[1] << 32U) | (u64)payload[2];
447         int ret;
448         struct agent_cb *cb_pos;
449         struct agent_cb *cb_next;
450
451         /* Check for existing entry in hash table for given key id */
452         hash_for_each_possible(reg_driver_map, eve_data, hentry, key) {
453                 if (eve_data->key == key) {
454                         list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head, list) {
455                                 cb_pos->eve_cb(&payload[0], cb_pos->agent_data);
456                                 is_callback_found = true;
457                         }
458
459                         /* re register with firmware to get future events */
460                         ret = zynqmp_pm_register_notifier(payload[1], payload[2],
461                                                           eve_data->wake, true);
462                         if (ret) {
463                                 pr_err("%s() failed for 0x%x and 0x%x: %d\r\n", __func__,
464                                        payload[1], payload[2], ret);
465                                 list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head,
466                                                          list) {
467                                         /* Remove already registered event from hash table */
468                                         xlnx_remove_cb_for_notify_event(payload[1], payload[2],
469                                                                         cb_pos->eve_cb,
470                                                                         cb_pos->agent_data);
471                                 }
472                         }
473                 }
474         }
475         if (!is_callback_found)
476                 pr_warn("Didn't find any registered callback for 0x%x 0x%x\n",
477                         payload[1], payload[2]);
478 }
479
480 static void xlnx_get_event_callback_data(u32 *buf)
481 {
482         zynqmp_pm_invoke_fn(GET_CALLBACK_DATA, 0, 0, 0, 0, buf);
483 }
484
485 static irqreturn_t xlnx_event_handler(int irq, void *dev_id)
486 {
487         u32 cb_type, node_id, event, pos;
488         u32 payload[CB_MAX_PAYLOAD_SIZE] = {0};
489         u32 event_data[CB_MAX_PAYLOAD_SIZE] = {0};
490
491         /* Get event data */
492         xlnx_get_event_callback_data(payload);
493
494         /* First element is callback type, others are callback arguments */
495         cb_type = payload[0];
496
497         if (cb_type == PM_NOTIFY_CB) {
498                 node_id = payload[1];
499                 event = payload[2];
500                 if (!xlnx_is_error_event(node_id)) {
501                         xlnx_call_notify_cb_handler(payload);
502                 } else {
503                         /*
504                          * Each call back function expecting payload as an input arguments.
505                          * We can get multiple error events as in one call back through error
506                          * mask. So payload[2] may can contain multiple error events.
507                          * In reg_driver_map database we store data in the combination of single
508                          * node_id-error combination.
509                          * So coping the payload message into event_data and update the
510                          * event_data[2] with Error Mask for single error event and use
511                          * event_data as input argument for registered call back function.
512                          *
513                          */
514                         memcpy(event_data, payload, (4 * CB_MAX_PAYLOAD_SIZE));
515                         /* Support Multiple Error Event */
516                         for (pos = 0; pos < MAX_BITS; pos++) {
517                                 if ((0 == (event & (1 << pos))))
518                                         continue;
519                                 event_data[2] = (event & (1 << pos));
520                                 xlnx_call_notify_cb_handler(event_data);
521                         }
522                 }
523         } else if (cb_type == PM_INIT_SUSPEND_CB) {
524                 xlnx_call_suspend_cb_handler(payload);
525         } else {
526                 pr_err("%s() Unsupported Callback %d\n", __func__, cb_type);
527         }
528
529         return IRQ_HANDLED;
530 }
531
532 static int xlnx_event_cpuhp_start(unsigned int cpu)
533 {
534         enable_percpu_irq(virq_sgi, IRQ_TYPE_NONE);
535
536         return 0;
537 }
538
539 static int xlnx_event_cpuhp_down(unsigned int cpu)
540 {
541         disable_percpu_irq(virq_sgi);
542
543         return 0;
544 }
545
546 static void xlnx_disable_percpu_irq(void *data)
547 {
548         disable_percpu_irq(virq_sgi);
549 }
550
551 static int xlnx_event_init_sgi(struct platform_device *pdev)
552 {
553         int ret = 0;
554         int cpu = smp_processor_id();
555         /*
556          * IRQ related structures are used for the following:
557          * for each SGI interrupt ensure its mapped by GIC IRQ domain
558          * and that each corresponding linux IRQ for the HW IRQ has
559          * a handler for when receiving an interrupt from the remote
560          * processor.
561          */
562         struct irq_domain *domain;
563         struct irq_fwspec sgi_fwspec;
564         struct device_node *interrupt_parent = NULL;
565         struct device *parent = pdev->dev.parent;
566
567         /* Find GIC controller to map SGIs. */
568         interrupt_parent = of_irq_find_parent(parent->of_node);
569         if (!interrupt_parent) {
570                 dev_err(&pdev->dev, "Failed to find property for Interrupt parent\n");
571                 return -EINVAL;
572         }
573
574         /* Each SGI needs to be associated with GIC's IRQ domain. */
575         domain = irq_find_host(interrupt_parent);
576         of_node_put(interrupt_parent);
577
578         /* Each mapping needs GIC domain when finding IRQ mapping. */
579         sgi_fwspec.fwnode = domain->fwnode;
580
581         /*
582          * When irq domain looks at mapping each arg is as follows:
583          * 3 args for: interrupt type (SGI), interrupt # (set later), type
584          */
585         sgi_fwspec.param_count = 1;
586
587         /* Set SGI's hwirq */
588         sgi_fwspec.param[0] = sgi_num;
589         virq_sgi = irq_create_fwspec_mapping(&sgi_fwspec);
590
591         per_cpu(cpu_number1, cpu) = cpu;
592         ret = request_percpu_irq(virq_sgi, xlnx_event_handler, "xlnx_event_mgmt",
593                                  &cpu_number1);
594         WARN_ON(ret);
595         if (ret) {
596                 irq_dispose_mapping(virq_sgi);
597                 return ret;
598         }
599
600         irq_to_desc(virq_sgi);
601         irq_set_status_flags(virq_sgi, IRQ_PER_CPU);
602
603         return ret;
604 }
605
606 static void xlnx_event_cleanup_sgi(struct platform_device *pdev)
607 {
608         int cpu = smp_processor_id();
609
610         per_cpu(cpu_number1, cpu) = cpu;
611
612         cpuhp_remove_state(CPUHP_AP_ONLINE_DYN);
613
614         on_each_cpu(xlnx_disable_percpu_irq, NULL, 1);
615
616         irq_clear_status_flags(virq_sgi, IRQ_PER_CPU);
617         free_percpu_irq(virq_sgi, &cpu_number1);
618         irq_dispose_mapping(virq_sgi);
619 }
620
621 static int xlnx_event_manager_probe(struct platform_device *pdev)
622 {
623         int ret;
624
625         ret = zynqmp_pm_feature(PM_REGISTER_NOTIFIER);
626         if (ret < 0) {
627                 dev_err(&pdev->dev, "Feature check failed with %d\n", ret);
628                 return ret;
629         }
630
631         if ((ret & FIRMWARE_VERSION_MASK) <
632             REGISTER_NOTIFIER_FIRMWARE_VERSION) {
633                 dev_err(&pdev->dev, "Register notifier version error. Expected Firmware: v%d - Found: v%d\n",
634                         REGISTER_NOTIFIER_FIRMWARE_VERSION,
635                         ret & FIRMWARE_VERSION_MASK);
636                 return -EOPNOTSUPP;
637         }
638
639         /* Initialize the SGI */
640         ret = xlnx_event_init_sgi(pdev);
641         if (ret) {
642                 dev_err(&pdev->dev, "SGI Init has been failed with %d\n", ret);
643                 return ret;
644         }
645
646         /* Setup function for the CPU hot-plug cases */
647         cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "soc/event:starting",
648                           xlnx_event_cpuhp_start, xlnx_event_cpuhp_down);
649
650         ret = zynqmp_pm_invoke_fn(PM_IOCTL, 0, IOCTL_REGISTER_SGI, sgi_num,
651                                   0, NULL);
652         if (ret) {
653                 dev_err(&pdev->dev, "SGI %d Registration over TF-A failed with %d\n", sgi_num, ret);
654                 xlnx_event_cleanup_sgi(pdev);
655                 return ret;
656         }
657
658         event_manager_availability = 0;
659
660         dev_info(&pdev->dev, "SGI %d Registered over TF-A\n", sgi_num);
661         dev_info(&pdev->dev, "Xilinx Event Management driver probed\n");
662
663         return ret;
664 }
665
666 static int xlnx_event_manager_remove(struct platform_device *pdev)
667 {
668         int i;
669         struct registered_event_data *eve_data;
670         struct hlist_node *tmp;
671         int ret;
672         struct agent_cb *cb_pos;
673         struct agent_cb *cb_next;
674
675         hash_for_each_safe(reg_driver_map, i, tmp, eve_data, hentry) {
676                 list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head, list) {
677                         list_del_init(&cb_pos->list);
678                         kfree(cb_pos);
679                 }
680                 hash_del(&eve_data->hentry);
681                 kfree(eve_data);
682         }
683
684         ret = zynqmp_pm_invoke_fn(PM_IOCTL, 0, IOCTL_REGISTER_SGI, 0, 1, NULL);
685         if (ret)
686                 dev_err(&pdev->dev, "SGI unregistration over TF-A failed with %d\n", ret);
687
688         xlnx_event_cleanup_sgi(pdev);
689
690         event_manager_availability = -EACCES;
691
692         return ret;
693 }
694
695 static struct platform_driver xlnx_event_manager_driver = {
696         .probe = xlnx_event_manager_probe,
697         .remove = xlnx_event_manager_remove,
698         .driver = {
699                 .name = "xlnx_event_manager",
700         },
701 };
702 module_param(sgi_num, uint, 0);
703 module_platform_driver(xlnx_event_manager_driver);