Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Xilinx Event Management Driver
0004  *
0005  *  Copyright (C) 2021 Xilinx, Inc.
0006  *
0007  *  Abhyuday Godhasara <abhyuday.godhasara@xilinx.com>
0008  */
0009 
0010 #include <linux/cpuhotplug.h>
0011 #include <linux/firmware/xlnx-event-manager.h>
0012 #include <linux/firmware/xlnx-zynqmp.h>
0013 #include <linux/hashtable.h>
0014 #include <linux/interrupt.h>
0015 #include <linux/irq.h>
0016 #include <linux/irqdomain.h>
0017 #include <linux/module.h>
0018 #include <linux/of_irq.h>
0019 #include <linux/platform_device.h>
0020 #include <linux/slab.h>
0021 
0022 static DEFINE_PER_CPU_READ_MOSTLY(int, cpu_number1);
0023 
0024 static int virq_sgi;
0025 static int event_manager_availability = -EACCES;
0026 
0027 /* SGI number used for Event management driver */
0028 #define XLNX_EVENT_SGI_NUM  (15)
0029 
0030 /* Max number of driver can register for same event */
0031 #define MAX_DRIVER_PER_EVENT    (10U)
0032 
0033 /* Max HashMap Order for PM API feature check (1<<7 = 128) */
0034 #define REGISTERED_DRIVER_MAX_ORDER (7)
0035 
0036 #define MAX_BITS    (32U) /* Number of bits available for error mask */
0037 
0038 #define FIRMWARE_VERSION_MASK           (0xFFFFU)
0039 #define REGISTER_NOTIFIER_FIRMWARE_VERSION  (2U)
0040 
0041 static DEFINE_HASHTABLE(reg_driver_map, REGISTERED_DRIVER_MAX_ORDER);
0042 static int sgi_num = XLNX_EVENT_SGI_NUM;
0043 
0044 static bool is_need_to_unregister;
0045 
0046 /**
0047  * struct agent_cb - Registered callback function and private data.
0048  * @agent_data:     Data passed back to handler function.
0049  * @eve_cb:     Function pointer to store the callback function.
0050  * @list:       member to create list.
0051  */
0052 struct agent_cb {
0053     void *agent_data;
0054     event_cb_func_t eve_cb;
0055     struct list_head list;
0056 };
0057 
0058 /**
0059  * struct registered_event_data - Registered Event Data.
0060  * @key:        key is the combine id(Node-Id | Event-Id) of type u64
0061  *          where upper u32 for Node-Id and lower u32 for Event-Id,
0062  *          And this used as key to index into hashmap.
0063  * @cb_type:        Type of Api callback, like PM_NOTIFY_CB, etc.
0064  * @wake:       If this flag set, firmware will wake up processor if is
0065  *          in sleep or power down state.
0066  * @cb_list_head:   Head of call back data list which contain the information
0067  *          about registered handler and private data.
0068  * @hentry:     hlist_node that hooks this entry into hashtable.
0069  */
0070 struct registered_event_data {
0071     u64 key;
0072     enum pm_api_cb_id cb_type;
0073     bool wake;
0074     struct list_head cb_list_head;
0075     struct hlist_node hentry;
0076 };
0077 
0078 static bool xlnx_is_error_event(const u32 node_id)
0079 {
0080     if (node_id == EVENT_ERROR_PMC_ERR1 ||
0081         node_id == EVENT_ERROR_PMC_ERR2 ||
0082         node_id == EVENT_ERROR_PSM_ERR1 ||
0083         node_id == EVENT_ERROR_PSM_ERR2)
0084         return true;
0085 
0086     return false;
0087 }
0088 
0089 static int xlnx_add_cb_for_notify_event(const u32 node_id, const u32 event, const bool wake,
0090                     event_cb_func_t cb_fun, void *data)
0091 {
0092     u64 key = 0;
0093     bool present_in_hash = false;
0094     struct registered_event_data *eve_data;
0095     struct agent_cb *cb_data;
0096     struct agent_cb *cb_pos;
0097     struct agent_cb *cb_next;
0098 
0099     key = ((u64)node_id << 32U) | (u64)event;
0100     /* Check for existing entry in hash table for given key id */
0101     hash_for_each_possible(reg_driver_map, eve_data, hentry, key) {
0102         if (eve_data->key == key) {
0103             present_in_hash = true;
0104             break;
0105         }
0106     }
0107 
0108     if (!present_in_hash) {
0109         /* Add new entry if not present in HASH table */
0110         eve_data = kmalloc(sizeof(*eve_data), GFP_KERNEL);
0111         if (!eve_data)
0112             return -ENOMEM;
0113         eve_data->key = key;
0114         eve_data->cb_type = PM_NOTIFY_CB;
0115         eve_data->wake = wake;
0116         INIT_LIST_HEAD(&eve_data->cb_list_head);
0117 
0118         cb_data = kmalloc(sizeof(*cb_data), GFP_KERNEL);
0119         if (!cb_data)
0120             return -ENOMEM;
0121         cb_data->eve_cb = cb_fun;
0122         cb_data->agent_data = data;
0123 
0124         /* Add into callback list */
0125         list_add(&cb_data->list, &eve_data->cb_list_head);
0126 
0127         /* Add into HASH table */
0128         hash_add(reg_driver_map, &eve_data->hentry, key);
0129     } else {
0130         /* Search for callback function and private data in list */
0131         list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head, list) {
0132             if (cb_pos->eve_cb == cb_fun &&
0133                 cb_pos->agent_data == data) {
0134                 return 0;
0135             }
0136         }
0137 
0138         /* Add multiple handler and private data in list */
0139         cb_data = kmalloc(sizeof(*cb_data), GFP_KERNEL);
0140         if (!cb_data)
0141             return -ENOMEM;
0142         cb_data->eve_cb = cb_fun;
0143         cb_data->agent_data = data;
0144 
0145         list_add(&cb_data->list, &eve_data->cb_list_head);
0146     }
0147 
0148     return 0;
0149 }
0150 
0151 static int xlnx_add_cb_for_suspend(event_cb_func_t cb_fun, void *data)
0152 {
0153     struct registered_event_data *eve_data;
0154     struct agent_cb *cb_data;
0155 
0156     /* Check for existing entry in hash table for given cb_type */
0157     hash_for_each_possible(reg_driver_map, eve_data, hentry, PM_INIT_SUSPEND_CB) {
0158         if (eve_data->cb_type == PM_INIT_SUSPEND_CB) {
0159             pr_err("Found as already registered\n");
0160             return -EINVAL;
0161         }
0162     }
0163 
0164     /* Add new entry if not present */
0165     eve_data = kmalloc(sizeof(*eve_data), GFP_KERNEL);
0166     if (!eve_data)
0167         return -ENOMEM;
0168 
0169     eve_data->key = 0;
0170     eve_data->cb_type = PM_INIT_SUSPEND_CB;
0171     INIT_LIST_HEAD(&eve_data->cb_list_head);
0172 
0173     cb_data = kmalloc(sizeof(*cb_data), GFP_KERNEL);
0174     if (!cb_data)
0175         return -ENOMEM;
0176     cb_data->eve_cb = cb_fun;
0177     cb_data->agent_data = data;
0178 
0179     /* Add into callback list */
0180     list_add(&cb_data->list, &eve_data->cb_list_head);
0181 
0182     hash_add(reg_driver_map, &eve_data->hentry, PM_INIT_SUSPEND_CB);
0183 
0184     return 0;
0185 }
0186 
0187 static int xlnx_remove_cb_for_suspend(event_cb_func_t cb_fun)
0188 {
0189     bool is_callback_found = false;
0190     struct registered_event_data *eve_data;
0191     struct agent_cb *cb_pos;
0192     struct agent_cb *cb_next;
0193 
0194     is_need_to_unregister = false;
0195 
0196     /* Check for existing entry in hash table for given cb_type */
0197     hash_for_each_possible(reg_driver_map, eve_data, hentry, PM_INIT_SUSPEND_CB) {
0198         if (eve_data->cb_type == PM_INIT_SUSPEND_CB) {
0199             /* Delete the list of callback */
0200             list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head, list) {
0201                 if (cb_pos->eve_cb == cb_fun) {
0202                     is_callback_found = true;
0203                     list_del_init(&cb_pos->list);
0204                     kfree(cb_pos);
0205                 }
0206             }
0207             /* remove an object from a hashtable */
0208             hash_del(&eve_data->hentry);
0209             kfree(eve_data);
0210             is_need_to_unregister = true;
0211         }
0212     }
0213     if (!is_callback_found) {
0214         pr_warn("Didn't find any registered callback for suspend event\n");
0215         return -EINVAL;
0216     }
0217 
0218     return 0;
0219 }
0220 
0221 static int xlnx_remove_cb_for_notify_event(const u32 node_id, const u32 event,
0222                        event_cb_func_t cb_fun, void *data)
0223 {
0224     bool is_callback_found = false;
0225     struct registered_event_data *eve_data;
0226     u64 key = ((u64)node_id << 32U) | (u64)event;
0227     struct agent_cb *cb_pos;
0228     struct agent_cb *cb_next;
0229 
0230     is_need_to_unregister = false;
0231 
0232     /* Check for existing entry in hash table for given key id */
0233     hash_for_each_possible(reg_driver_map, eve_data, hentry, key) {
0234         if (eve_data->key == key) {
0235             /* Delete the list of callback */
0236             list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head, list) {
0237                 if (cb_pos->eve_cb == cb_fun &&
0238                     cb_pos->agent_data == data) {
0239                     is_callback_found = true;
0240                     list_del_init(&cb_pos->list);
0241                     kfree(cb_pos);
0242                 }
0243             }
0244 
0245             /* Remove HASH table if callback list is empty */
0246             if (list_empty(&eve_data->cb_list_head)) {
0247                 /* remove an object from a HASH table */
0248                 hash_del(&eve_data->hentry);
0249                 kfree(eve_data);
0250                 is_need_to_unregister = true;
0251             }
0252         }
0253     }
0254     if (!is_callback_found) {
0255         pr_warn("Didn't find any registered callback for 0x%x 0x%x\n",
0256             node_id, event);
0257         return -EINVAL;
0258     }
0259 
0260     return 0;
0261 }
0262 
0263 /**
0264  * xlnx_register_event() - Register for the event.
0265  * @cb_type:    Type of callback from pm_api_cb_id,
0266  *          PM_NOTIFY_CB - for Error Events,
0267  *          PM_INIT_SUSPEND_CB - for suspend callback.
0268  * @node_id:    Node-Id related to event.
0269  * @event:  Event Mask for the Error Event.
0270  * @wake:   Flag specifying whether the subsystem should be woken upon
0271  *      event notification.
0272  * @cb_fun: Function pointer to store the callback function.
0273  * @data:   Pointer for the driver instance.
0274  *
0275  * Return:  Returns 0 on successful registration else error code.
0276  */
0277 int xlnx_register_event(const enum pm_api_cb_id cb_type, const u32 node_id, const u32 event,
0278             const bool wake, event_cb_func_t cb_fun, void *data)
0279 {
0280     int ret = 0;
0281     u32 eve;
0282     int pos;
0283 
0284     if (event_manager_availability)
0285         return event_manager_availability;
0286 
0287     if (cb_type != PM_NOTIFY_CB && cb_type != PM_INIT_SUSPEND_CB) {
0288         pr_err("%s() Unsupported Callback 0x%x\n", __func__, cb_type);
0289         return -EINVAL;
0290     }
0291 
0292     if (!cb_fun)
0293         return -EFAULT;
0294 
0295     if (cb_type == PM_INIT_SUSPEND_CB) {
0296         ret = xlnx_add_cb_for_suspend(cb_fun, data);
0297     } else {
0298         if (!xlnx_is_error_event(node_id)) {
0299             /* Add entry for Node-Id/Event in hash table */
0300             ret = xlnx_add_cb_for_notify_event(node_id, event, wake, cb_fun, data);
0301         } else {
0302             /* Add into Hash table */
0303             for (pos = 0; pos < MAX_BITS; pos++) {
0304                 eve = event & (1 << pos);
0305                 if (!eve)
0306                     continue;
0307 
0308                 /* Add entry for Node-Id/Eve in hash table */
0309                 ret = xlnx_add_cb_for_notify_event(node_id, eve, wake, cb_fun,
0310                                    data);
0311                 /* Break the loop if got error */
0312                 if (ret)
0313                     break;
0314             }
0315             if (ret) {
0316                 /* Skip the Event for which got the error */
0317                 pos--;
0318                 /* Remove registered(during this call) event from hash table */
0319                 for ( ; pos >= 0; pos--) {
0320                     eve = event & (1 << pos);
0321                     if (!eve)
0322                         continue;
0323                     xlnx_remove_cb_for_notify_event(node_id, eve, cb_fun, data);
0324                 }
0325             }
0326         }
0327 
0328         if (ret) {
0329             pr_err("%s() failed for 0x%x and 0x%x: %d\r\n", __func__, node_id,
0330                    event, ret);
0331             return ret;
0332         }
0333 
0334         /* Register for Node-Id/Event combination in firmware */
0335         ret = zynqmp_pm_register_notifier(node_id, event, wake, true);
0336         if (ret) {
0337             pr_err("%s() failed for 0x%x and 0x%x: %d\r\n", __func__, node_id,
0338                    event, ret);
0339             /* Remove already registered event from hash table */
0340             if (xlnx_is_error_event(node_id)) {
0341                 for (pos = 0; pos < MAX_BITS; pos++) {
0342                     eve = event & (1 << pos);
0343                     if (!eve)
0344                         continue;
0345                     xlnx_remove_cb_for_notify_event(node_id, eve, cb_fun, data);
0346                 }
0347             } else {
0348                 xlnx_remove_cb_for_notify_event(node_id, event, cb_fun, data);
0349             }
0350             return ret;
0351         }
0352     }
0353 
0354     return ret;
0355 }
0356 EXPORT_SYMBOL_GPL(xlnx_register_event);
0357 
0358 /**
0359  * xlnx_unregister_event() - Unregister for the event.
0360  * @cb_type:    Type of callback from pm_api_cb_id,
0361  *          PM_NOTIFY_CB - for Error Events,
0362  *          PM_INIT_SUSPEND_CB - for suspend callback.
0363  * @node_id:    Node-Id related to event.
0364  * @event:  Event Mask for the Error Event.
0365  * @cb_fun: Function pointer of callback function.
0366  * @data:   Pointer of agent's private data.
0367  *
0368  * Return:  Returns 0 on successful unregistration else error code.
0369  */
0370 int xlnx_unregister_event(const enum pm_api_cb_id cb_type, const u32 node_id, const u32 event,
0371               event_cb_func_t cb_fun, void *data)
0372 {
0373     int ret = 0;
0374     u32 eve, pos;
0375 
0376     is_need_to_unregister = false;
0377 
0378     if (event_manager_availability)
0379         return event_manager_availability;
0380 
0381     if (cb_type != PM_NOTIFY_CB && cb_type != PM_INIT_SUSPEND_CB) {
0382         pr_err("%s() Unsupported Callback 0x%x\n", __func__, cb_type);
0383         return -EINVAL;
0384     }
0385 
0386     if (!cb_fun)
0387         return -EFAULT;
0388 
0389     if (cb_type == PM_INIT_SUSPEND_CB) {
0390         ret = xlnx_remove_cb_for_suspend(cb_fun);
0391     } else {
0392         /* Remove Node-Id/Event from hash table */
0393         if (!xlnx_is_error_event(node_id)) {
0394             xlnx_remove_cb_for_notify_event(node_id, event, cb_fun, data);
0395         } else {
0396             for (pos = 0; pos < MAX_BITS; pos++) {
0397                 eve = event & (1 << pos);
0398                 if (!eve)
0399                     continue;
0400 
0401                 xlnx_remove_cb_for_notify_event(node_id, eve, cb_fun, data);
0402             }
0403         }
0404 
0405         /* Un-register if list is empty */
0406         if (is_need_to_unregister) {
0407             /* Un-register for Node-Id/Event combination */
0408             ret = zynqmp_pm_register_notifier(node_id, event, false, false);
0409             if (ret) {
0410                 pr_err("%s() failed for 0x%x and 0x%x: %d\n",
0411                        __func__, node_id, event, ret);
0412                 return ret;
0413             }
0414         }
0415     }
0416 
0417     return ret;
0418 }
0419 EXPORT_SYMBOL_GPL(xlnx_unregister_event);
0420 
0421 static void xlnx_call_suspend_cb_handler(const u32 *payload)
0422 {
0423     bool is_callback_found = false;
0424     struct registered_event_data *eve_data;
0425     u32 cb_type = payload[0];
0426     struct agent_cb *cb_pos;
0427     struct agent_cb *cb_next;
0428 
0429     /* Check for existing entry in hash table for given cb_type */
0430     hash_for_each_possible(reg_driver_map, eve_data, hentry, cb_type) {
0431         if (eve_data->cb_type == cb_type) {
0432             list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head, list) {
0433                 cb_pos->eve_cb(&payload[0], cb_pos->agent_data);
0434                 is_callback_found = true;
0435             }
0436         }
0437     }
0438     if (!is_callback_found)
0439         pr_warn("Didn't find any registered callback for suspend event\n");
0440 }
0441 
0442 static void xlnx_call_notify_cb_handler(const u32 *payload)
0443 {
0444     bool is_callback_found = false;
0445     struct registered_event_data *eve_data;
0446     u64 key = ((u64)payload[1] << 32U) | (u64)payload[2];
0447     int ret;
0448     struct agent_cb *cb_pos;
0449     struct agent_cb *cb_next;
0450 
0451     /* Check for existing entry in hash table for given key id */
0452     hash_for_each_possible(reg_driver_map, eve_data, hentry, key) {
0453         if (eve_data->key == key) {
0454             list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head, list) {
0455                 cb_pos->eve_cb(&payload[0], cb_pos->agent_data);
0456                 is_callback_found = true;
0457             }
0458 
0459             /* re register with firmware to get future events */
0460             ret = zynqmp_pm_register_notifier(payload[1], payload[2],
0461                               eve_data->wake, true);
0462             if (ret) {
0463                 pr_err("%s() failed for 0x%x and 0x%x: %d\r\n", __func__,
0464                        payload[1], payload[2], ret);
0465                 list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head,
0466                              list) {
0467                     /* Remove already registered event from hash table */
0468                     xlnx_remove_cb_for_notify_event(payload[1], payload[2],
0469                                     cb_pos->eve_cb,
0470                                     cb_pos->agent_data);
0471                 }
0472             }
0473         }
0474     }
0475     if (!is_callback_found)
0476         pr_warn("Didn't find any registered callback for 0x%x 0x%x\n",
0477             payload[1], payload[2]);
0478 }
0479 
0480 static void xlnx_get_event_callback_data(u32 *buf)
0481 {
0482     zynqmp_pm_invoke_fn(GET_CALLBACK_DATA, 0, 0, 0, 0, buf);
0483 }
0484 
0485 static irqreturn_t xlnx_event_handler(int irq, void *dev_id)
0486 {
0487     u32 cb_type, node_id, event, pos;
0488     u32 payload[CB_MAX_PAYLOAD_SIZE] = {0};
0489     u32 event_data[CB_MAX_PAYLOAD_SIZE] = {0};
0490 
0491     /* Get event data */
0492     xlnx_get_event_callback_data(payload);
0493 
0494     /* First element is callback type, others are callback arguments */
0495     cb_type = payload[0];
0496 
0497     if (cb_type == PM_NOTIFY_CB) {
0498         node_id = payload[1];
0499         event = payload[2];
0500         if (!xlnx_is_error_event(node_id)) {
0501             xlnx_call_notify_cb_handler(payload);
0502         } else {
0503             /*
0504              * Each call back function expecting payload as an input arguments.
0505              * We can get multiple error events as in one call back through error
0506              * mask. So payload[2] may can contain multiple error events.
0507              * In reg_driver_map database we store data in the combination of single
0508              * node_id-error combination.
0509              * So coping the payload message into event_data and update the
0510              * event_data[2] with Error Mask for single error event and use
0511              * event_data as input argument for registered call back function.
0512              *
0513              */
0514             memcpy(event_data, payload, (4 * CB_MAX_PAYLOAD_SIZE));
0515             /* Support Multiple Error Event */
0516             for (pos = 0; pos < MAX_BITS; pos++) {
0517                 if ((0 == (event & (1 << pos))))
0518                     continue;
0519                 event_data[2] = (event & (1 << pos));
0520                 xlnx_call_notify_cb_handler(event_data);
0521             }
0522         }
0523     } else if (cb_type == PM_INIT_SUSPEND_CB) {
0524         xlnx_call_suspend_cb_handler(payload);
0525     } else {
0526         pr_err("%s() Unsupported Callback %d\n", __func__, cb_type);
0527     }
0528 
0529     return IRQ_HANDLED;
0530 }
0531 
0532 static int xlnx_event_cpuhp_start(unsigned int cpu)
0533 {
0534     enable_percpu_irq(virq_sgi, IRQ_TYPE_NONE);
0535 
0536     return 0;
0537 }
0538 
0539 static int xlnx_event_cpuhp_down(unsigned int cpu)
0540 {
0541     disable_percpu_irq(virq_sgi);
0542 
0543     return 0;
0544 }
0545 
0546 static void xlnx_disable_percpu_irq(void *data)
0547 {
0548     disable_percpu_irq(virq_sgi);
0549 }
0550 
0551 static int xlnx_event_init_sgi(struct platform_device *pdev)
0552 {
0553     int ret = 0;
0554     int cpu = smp_processor_id();
0555     /*
0556      * IRQ related structures are used for the following:
0557      * for each SGI interrupt ensure its mapped by GIC IRQ domain
0558      * and that each corresponding linux IRQ for the HW IRQ has
0559      * a handler for when receiving an interrupt from the remote
0560      * processor.
0561      */
0562     struct irq_domain *domain;
0563     struct irq_fwspec sgi_fwspec;
0564     struct device_node *interrupt_parent = NULL;
0565     struct device *parent = pdev->dev.parent;
0566 
0567     /* Find GIC controller to map SGIs. */
0568     interrupt_parent = of_irq_find_parent(parent->of_node);
0569     if (!interrupt_parent) {
0570         dev_err(&pdev->dev, "Failed to find property for Interrupt parent\n");
0571         return -EINVAL;
0572     }
0573 
0574     /* Each SGI needs to be associated with GIC's IRQ domain. */
0575     domain = irq_find_host(interrupt_parent);
0576     of_node_put(interrupt_parent);
0577 
0578     /* Each mapping needs GIC domain when finding IRQ mapping. */
0579     sgi_fwspec.fwnode = domain->fwnode;
0580 
0581     /*
0582      * When irq domain looks at mapping each arg is as follows:
0583      * 3 args for: interrupt type (SGI), interrupt # (set later), type
0584      */
0585     sgi_fwspec.param_count = 1;
0586 
0587     /* Set SGI's hwirq */
0588     sgi_fwspec.param[0] = sgi_num;
0589     virq_sgi = irq_create_fwspec_mapping(&sgi_fwspec);
0590 
0591     per_cpu(cpu_number1, cpu) = cpu;
0592     ret = request_percpu_irq(virq_sgi, xlnx_event_handler, "xlnx_event_mgmt",
0593                  &cpu_number1);
0594     WARN_ON(ret);
0595     if (ret) {
0596         irq_dispose_mapping(virq_sgi);
0597         return ret;
0598     }
0599 
0600     irq_to_desc(virq_sgi);
0601     irq_set_status_flags(virq_sgi, IRQ_PER_CPU);
0602 
0603     return ret;
0604 }
0605 
0606 static void xlnx_event_cleanup_sgi(struct platform_device *pdev)
0607 {
0608     int cpu = smp_processor_id();
0609 
0610     per_cpu(cpu_number1, cpu) = cpu;
0611 
0612     cpuhp_remove_state(CPUHP_AP_ONLINE_DYN);
0613 
0614     on_each_cpu(xlnx_disable_percpu_irq, NULL, 1);
0615 
0616     irq_clear_status_flags(virq_sgi, IRQ_PER_CPU);
0617     free_percpu_irq(virq_sgi, &cpu_number1);
0618     irq_dispose_mapping(virq_sgi);
0619 }
0620 
0621 static int xlnx_event_manager_probe(struct platform_device *pdev)
0622 {
0623     int ret;
0624 
0625     ret = zynqmp_pm_feature(PM_REGISTER_NOTIFIER);
0626     if (ret < 0) {
0627         dev_err(&pdev->dev, "Feature check failed with %d\n", ret);
0628         return ret;
0629     }
0630 
0631     if ((ret & FIRMWARE_VERSION_MASK) <
0632         REGISTER_NOTIFIER_FIRMWARE_VERSION) {
0633         dev_err(&pdev->dev, "Register notifier version error. Expected Firmware: v%d - Found: v%d\n",
0634             REGISTER_NOTIFIER_FIRMWARE_VERSION,
0635             ret & FIRMWARE_VERSION_MASK);
0636         return -EOPNOTSUPP;
0637     }
0638 
0639     /* Initialize the SGI */
0640     ret = xlnx_event_init_sgi(pdev);
0641     if (ret) {
0642         dev_err(&pdev->dev, "SGI Init has been failed with %d\n", ret);
0643         return ret;
0644     }
0645 
0646     /* Setup function for the CPU hot-plug cases */
0647     cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "soc/event:starting",
0648               xlnx_event_cpuhp_start, xlnx_event_cpuhp_down);
0649 
0650     ret = zynqmp_pm_register_sgi(sgi_num, 0);
0651     if (ret) {
0652         dev_err(&pdev->dev, "SGI %d Registration over TF-A failed with %d\n", sgi_num, ret);
0653         xlnx_event_cleanup_sgi(pdev);
0654         return ret;
0655     }
0656 
0657     event_manager_availability = 0;
0658 
0659     dev_info(&pdev->dev, "SGI %d Registered over TF-A\n", sgi_num);
0660     dev_info(&pdev->dev, "Xilinx Event Management driver probed\n");
0661 
0662     return ret;
0663 }
0664 
0665 static int xlnx_event_manager_remove(struct platform_device *pdev)
0666 {
0667     int i;
0668     struct registered_event_data *eve_data;
0669     struct hlist_node *tmp;
0670     int ret;
0671     struct agent_cb *cb_pos;
0672     struct agent_cb *cb_next;
0673 
0674     hash_for_each_safe(reg_driver_map, i, tmp, eve_data, hentry) {
0675         list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head, list) {
0676             list_del_init(&cb_pos->list);
0677             kfree(cb_pos);
0678         }
0679         hash_del(&eve_data->hentry);
0680         kfree(eve_data);
0681     }
0682 
0683     ret = zynqmp_pm_register_sgi(0, 1);
0684     if (ret)
0685         dev_err(&pdev->dev, "SGI unregistration over TF-A failed with %d\n", ret);
0686 
0687     xlnx_event_cleanup_sgi(pdev);
0688 
0689     event_manager_availability = -EACCES;
0690 
0691     return ret;
0692 }
0693 
0694 static struct platform_driver xlnx_event_manager_driver = {
0695     .probe = xlnx_event_manager_probe,
0696     .remove = xlnx_event_manager_remove,
0697     .driver = {
0698         .name = "xlnx_event_manager",
0699     },
0700 };
0701 module_param(sgi_num, uint, 0);
0702 module_platform_driver(xlnx_event_manager_driver);