static DEFINE_HASHTABLE(reg_driver_map, REGISTERED_DRIVER_MAX_ORDER);
static int sgi_num = XLNX_EVENT_SGI_NUM;
+static bool is_need_to_unregister;
+
/**
* struct agent_cb - Registered callback function and private data.
* @agent_data: Data passed back to handler function.
struct agent_cb *cb_pos;
struct agent_cb *cb_next;
+ is_need_to_unregister = false;
+
/* Check for existing entry in hash table for given cb_type */
hash_for_each_possible(reg_driver_map, eve_data, hentry, PM_INIT_SUSPEND_CB) {
if (eve_data->cb_type == PM_INIT_SUSPEND_CB) {
/* remove an object from a hashtable */
hash_del(&eve_data->hentry);
kfree(eve_data);
+ is_need_to_unregister = true;
}
}
if (!is_callback_found) {
}
static int xlnx_remove_cb_for_notify_event(const u32 node_id, const u32 event,
- event_cb_func_t cb_fun)
+ event_cb_func_t cb_fun, void *data)
{
bool is_callback_found = false;
struct registered_event_data *eve_data;
struct agent_cb *cb_pos;
struct agent_cb *cb_next;
+ is_need_to_unregister = false;
+
/* Check for existing entry in hash table for given key id */
hash_for_each_possible(reg_driver_map, eve_data, hentry, key) {
if (eve_data->key == key) {
/* Delete the list of callback */
list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head, list) {
- if (cb_pos->eve_cb == cb_fun) {
+ if (cb_pos->eve_cb == cb_fun &&
+ cb_pos->agent_data == data) {
is_callback_found = true;
list_del_init(&cb_pos->list);
kfree(cb_pos);
}
}
- /* remove an object from a HASH table */
- hash_del(&eve_data->hentry);
- kfree(eve_data);
+
+ /* Remove HASH table if callback list is empty */
+ if (list_empty(&eve_data->cb_list_head)) {
+ /* remove an object from a HASH table */
+ hash_del(&eve_data->hentry);
+ kfree(eve_data);
+ is_need_to_unregister = true;
+ }
}
}
if (!is_callback_found) {
eve = event & (1 << pos);
if (!eve)
continue;
- xlnx_remove_cb_for_notify_event(node_id, eve, cb_fun);
+ xlnx_remove_cb_for_notify_event(node_id, eve, cb_fun, data);
}
}
}
eve = event & (1 << pos);
if (!eve)
continue;
- xlnx_remove_cb_for_notify_event(node_id, eve, cb_fun);
+ xlnx_remove_cb_for_notify_event(node_id, eve, cb_fun, data);
}
} else {
- xlnx_remove_cb_for_notify_event(node_id, event, cb_fun);
+ xlnx_remove_cb_for_notify_event(node_id, event, cb_fun, data);
}
return ret;
}
* @node_id: Node-Id related to event.
* @event: Event Mask for the Error Event.
* @cb_fun: Function pointer of callback function.
+ * @data: Pointer of agent's private data.
*
* Return: Returns 0 on successful unregistration else error code.
*/
int xlnx_unregister_event(const enum pm_api_cb_id cb_type, const u32 node_id, const u32 event,
- event_cb_func_t cb_fun)
+ event_cb_func_t cb_fun, void *data)
{
- int ret;
+ int ret = 0;
u32 eve, pos;
+ is_need_to_unregister = false;
+
if (event_manager_availability)
return event_manager_availability;
} else {
/* Remove Node-Id/Event from hash table */
if (!xlnx_is_error_event(node_id)) {
- xlnx_remove_cb_for_notify_event(node_id, event, cb_fun);
+ xlnx_remove_cb_for_notify_event(node_id, event, cb_fun, data);
} else {
for (pos = 0; pos < MAX_BITS; pos++) {
eve = event & (1 << pos);
if (!eve)
continue;
- xlnx_remove_cb_for_notify_event(node_id, eve, cb_fun);
+ xlnx_remove_cb_for_notify_event(node_id, eve, cb_fun, data);
}
}
- /* Un-register for Node-Id/Event combination */
- ret = zynqmp_pm_register_notifier(node_id, event, false, false);
- if (ret) {
- pr_err("%s() failed for 0x%x and 0x%x: %d\n",
- __func__, node_id, event, ret);
- return ret;
+ /* Un-register if list is empty */
+ if (is_need_to_unregister) {
+ /* Un-register for Node-Id/Event combination */
+ ret = zynqmp_pm_register_notifier(node_id, event, false, false);
+ if (ret) {
+ pr_err("%s() failed for 0x%x and 0x%x: %d\n",
+ __func__, node_id, event, ret);
+ return ret;
+ }
}
}
list) {
/* Remove already registered event from hash table */
xlnx_remove_cb_for_notify_event(payload[1], payload[2],
- cb_pos->eve_cb);
+ cb_pos->eve_cb,
+ cb_pos->agent_data);
}
}
}
GFP_KERNEL);
if (!zynqmp_pm_init_suspend_work) {
xlnx_unregister_event(PM_INIT_SUSPEND_CB, 0, 0,
- suspend_event_callback);
+ suspend_event_callback, NULL);
return -ENOMEM;
}
event_registered = true;
ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_suspend_mode.attr);
if (ret) {
if (event_registered) {
- xlnx_unregister_event(PM_INIT_SUSPEND_CB, 0, 0, suspend_event_callback);
+ xlnx_unregister_event(PM_INIT_SUSPEND_CB, 0, 0, suspend_event_callback,
+ NULL);
event_registered = false;
}
dev_err(&pdev->dev, "unable to create sysfs interface\n");
{
sysfs_remove_file(&pdev->dev.kobj, &dev_attr_suspend_mode.attr);
if (event_registered)
- xlnx_unregister_event(PM_INIT_SUSPEND_CB, 0, 0, suspend_event_callback);
+ xlnx_unregister_event(PM_INIT_SUSPEND_CB, 0, 0, suspend_event_callback, NULL);
if (!rx_chan)
mbox_free_channel(rx_chan);