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. 6c7fdb240SAbhyuday Godhasara * 7c7fdb240SAbhyuday Godhasara * Abhyuday Godhasara <abhyuday.godhasara@xilinx.com> 8c7fdb240SAbhyuday Godhasara */ 9c7fdb240SAbhyuday Godhasara 10c7fdb240SAbhyuday Godhasara #include <linux/cpuhotplug.h> 11c7fdb240SAbhyuday Godhasara #include <linux/firmware/xlnx-event-manager.h> 12c7fdb240SAbhyuday Godhasara #include <linux/firmware/xlnx-zynqmp.h> 13c7fdb240SAbhyuday Godhasara #include <linux/hashtable.h> 14c7fdb240SAbhyuday Godhasara #include <linux/interrupt.h> 15c7fdb240SAbhyuday Godhasara #include <linux/irq.h> 16c7fdb240SAbhyuday Godhasara #include <linux/irqdomain.h> 17c7fdb240SAbhyuday Godhasara #include <linux/module.h> 18c7fdb240SAbhyuday Godhasara #include <linux/of_irq.h> 19c7fdb240SAbhyuday Godhasara #include <linux/platform_device.h> 20c7fdb240SAbhyuday Godhasara #include <linux/slab.h> 21c7fdb240SAbhyuday Godhasara 22c7fdb240SAbhyuday Godhasara static DEFINE_PER_CPU_READ_MOSTLY(int, cpu_number1); 23c7fdb240SAbhyuday Godhasara 24c7fdb240SAbhyuday Godhasara static int virq_sgi; 25c7fdb240SAbhyuday Godhasara static int event_manager_availability = -EACCES; 26c7fdb240SAbhyuday Godhasara 27c7fdb240SAbhyuday Godhasara /* SGI number used for Event management driver */ 28c7fdb240SAbhyuday Godhasara #define XLNX_EVENT_SGI_NUM (15) 29c7fdb240SAbhyuday Godhasara 30c7fdb240SAbhyuday Godhasara /* Max number of driver can register for same event */ 31c7fdb240SAbhyuday Godhasara #define MAX_DRIVER_PER_EVENT (10U) 32c7fdb240SAbhyuday Godhasara 33c7fdb240SAbhyuday Godhasara /* Max HashMap Order for PM API feature check (1<<7 = 128) */ 34c7fdb240SAbhyuday Godhasara #define REGISTERED_DRIVER_MAX_ORDER (7) 35c7fdb240SAbhyuday Godhasara 36c7fdb240SAbhyuday Godhasara #define MAX_BITS (32U) /* Number of bits available for error mask */ 37c7fdb240SAbhyuday Godhasara 38c7fdb240SAbhyuday Godhasara #define FIRMWARE_VERSION_MASK (0xFFFFU) 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 78c7fdb240SAbhyuday Godhasara static bool xlnx_is_error_event(const u32 node_id) 79c7fdb240SAbhyuday Godhasara { 80*97d62760SJay Buddhabhatti u32 pm_family_code, pm_sub_family_code; 81*97d62760SJay Buddhabhatti 82*97d62760SJay Buddhabhatti zynqmp_pm_get_family_info(&pm_family_code, &pm_sub_family_code); 83*97d62760SJay Buddhabhatti 84*97d62760SJay Buddhabhatti if (pm_sub_family_code == VERSAL_SUB_FAMILY_CODE) { 85*97d62760SJay Buddhabhatti if (node_id == VERSAL_EVENT_ERROR_PMC_ERR1 || 86*97d62760SJay Buddhabhatti node_id == VERSAL_EVENT_ERROR_PMC_ERR2 || 87*97d62760SJay Buddhabhatti node_id == VERSAL_EVENT_ERROR_PSM_ERR1 || 88*97d62760SJay Buddhabhatti node_id == VERSAL_EVENT_ERROR_PSM_ERR2) 89c7fdb240SAbhyuday Godhasara return true; 90*97d62760SJay Buddhabhatti } else { 91*97d62760SJay Buddhabhatti if (node_id == VERSAL_NET_EVENT_ERROR_PMC_ERR1 || 92*97d62760SJay Buddhabhatti node_id == VERSAL_NET_EVENT_ERROR_PMC_ERR2 || 93*97d62760SJay Buddhabhatti node_id == VERSAL_NET_EVENT_ERROR_PMC_ERR3 || 94*97d62760SJay Buddhabhatti node_id == VERSAL_NET_EVENT_ERROR_PSM_ERR1 || 95*97d62760SJay Buddhabhatti node_id == VERSAL_NET_EVENT_ERROR_PSM_ERR2 || 96*97d62760SJay Buddhabhatti node_id == VERSAL_NET_EVENT_ERROR_PSM_ERR3 || 97*97d62760SJay Buddhabhatti node_id == VERSAL_NET_EVENT_ERROR_PSM_ERR4) 98*97d62760SJay Buddhabhatti return true; 99*97d62760SJay Buddhabhatti } 100c7fdb240SAbhyuday Godhasara 101c7fdb240SAbhyuday Godhasara return false; 102c7fdb240SAbhyuday Godhasara } 103c7fdb240SAbhyuday Godhasara 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 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 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 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 */ 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 */ 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 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 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) 495c7fdb240SAbhyuday Godhasara pr_warn("Didn't find any registered callback for 0x%x 0x%x\n", 496c7fdb240SAbhyuday Godhasara payload[1], payload[2]); 497c7fdb240SAbhyuday Godhasara } 498c7fdb240SAbhyuday Godhasara 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 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 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 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 565c7fdb240SAbhyuday Godhasara static void xlnx_disable_percpu_irq(void *data) 566c7fdb240SAbhyuday Godhasara { 567c7fdb240SAbhyuday Godhasara disable_percpu_irq(virq_sgi); 568c7fdb240SAbhyuday Godhasara } 569c7fdb240SAbhyuday Godhasara 570c7fdb240SAbhyuday Godhasara static int xlnx_event_init_sgi(struct platform_device *pdev) 571c7fdb240SAbhyuday Godhasara { 572c7fdb240SAbhyuday Godhasara int ret = 0; 573c7fdb240SAbhyuday Godhasara int cpu = smp_processor_id(); 574c7fdb240SAbhyuday Godhasara /* 575c7fdb240SAbhyuday Godhasara * IRQ related structures are used for the following: 576c7fdb240SAbhyuday Godhasara * for each SGI interrupt ensure its mapped by GIC IRQ domain 577c7fdb240SAbhyuday Godhasara * and that each corresponding linux IRQ for the HW IRQ has 578c7fdb240SAbhyuday Godhasara * a handler for when receiving an interrupt from the remote 579c7fdb240SAbhyuday Godhasara * processor. 580c7fdb240SAbhyuday Godhasara */ 581c7fdb240SAbhyuday Godhasara struct irq_domain *domain; 582c7fdb240SAbhyuday Godhasara struct irq_fwspec sgi_fwspec; 583c7fdb240SAbhyuday Godhasara struct device_node *interrupt_parent = NULL; 584c7fdb240SAbhyuday Godhasara struct device *parent = pdev->dev.parent; 585c7fdb240SAbhyuday Godhasara 586c7fdb240SAbhyuday Godhasara /* Find GIC controller to map SGIs. */ 587c7fdb240SAbhyuday Godhasara interrupt_parent = of_irq_find_parent(parent->of_node); 588c7fdb240SAbhyuday Godhasara if (!interrupt_parent) { 589c7fdb240SAbhyuday Godhasara dev_err(&pdev->dev, "Failed to find property for Interrupt parent\n"); 590c7fdb240SAbhyuday Godhasara return -EINVAL; 591c7fdb240SAbhyuday Godhasara } 592c7fdb240SAbhyuday Godhasara 593c7fdb240SAbhyuday Godhasara /* Each SGI needs to be associated with GIC's IRQ domain. */ 594c7fdb240SAbhyuday Godhasara domain = irq_find_host(interrupt_parent); 595c7fdb240SAbhyuday Godhasara of_node_put(interrupt_parent); 596c7fdb240SAbhyuday Godhasara 597c7fdb240SAbhyuday Godhasara /* Each mapping needs GIC domain when finding IRQ mapping. */ 598c7fdb240SAbhyuday Godhasara sgi_fwspec.fwnode = domain->fwnode; 599c7fdb240SAbhyuday Godhasara 600c7fdb240SAbhyuday Godhasara /* 601c7fdb240SAbhyuday Godhasara * When irq domain looks at mapping each arg is as follows: 602c7fdb240SAbhyuday Godhasara * 3 args for: interrupt type (SGI), interrupt # (set later), type 603c7fdb240SAbhyuday Godhasara */ 604c7fdb240SAbhyuday Godhasara sgi_fwspec.param_count = 1; 605c7fdb240SAbhyuday Godhasara 606c7fdb240SAbhyuday Godhasara /* Set SGI's hwirq */ 607c7fdb240SAbhyuday Godhasara sgi_fwspec.param[0] = sgi_num; 608c7fdb240SAbhyuday Godhasara virq_sgi = irq_create_fwspec_mapping(&sgi_fwspec); 609c7fdb240SAbhyuday Godhasara 610c7fdb240SAbhyuday Godhasara per_cpu(cpu_number1, cpu) = cpu; 611c7fdb240SAbhyuday Godhasara ret = request_percpu_irq(virq_sgi, xlnx_event_handler, "xlnx_event_mgmt", 612c7fdb240SAbhyuday Godhasara &cpu_number1); 613c7fdb240SAbhyuday Godhasara WARN_ON(ret); 614c7fdb240SAbhyuday Godhasara if (ret) { 615c7fdb240SAbhyuday Godhasara irq_dispose_mapping(virq_sgi); 616c7fdb240SAbhyuday Godhasara return ret; 617c7fdb240SAbhyuday Godhasara } 618c7fdb240SAbhyuday Godhasara 619c7fdb240SAbhyuday Godhasara irq_to_desc(virq_sgi); 620c7fdb240SAbhyuday Godhasara irq_set_status_flags(virq_sgi, IRQ_PER_CPU); 621c7fdb240SAbhyuday Godhasara 622c7fdb240SAbhyuday Godhasara return ret; 623c7fdb240SAbhyuday Godhasara } 624c7fdb240SAbhyuday Godhasara 625c7fdb240SAbhyuday Godhasara static void xlnx_event_cleanup_sgi(struct platform_device *pdev) 626c7fdb240SAbhyuday Godhasara { 627c7fdb240SAbhyuday Godhasara int cpu = smp_processor_id(); 628c7fdb240SAbhyuday Godhasara 629c7fdb240SAbhyuday Godhasara per_cpu(cpu_number1, cpu) = cpu; 630c7fdb240SAbhyuday Godhasara 631c7fdb240SAbhyuday Godhasara cpuhp_remove_state(CPUHP_AP_ONLINE_DYN); 632c7fdb240SAbhyuday Godhasara 633c7fdb240SAbhyuday Godhasara on_each_cpu(xlnx_disable_percpu_irq, NULL, 1); 634c7fdb240SAbhyuday Godhasara 635c7fdb240SAbhyuday Godhasara irq_clear_status_flags(virq_sgi, IRQ_PER_CPU); 636c7fdb240SAbhyuday Godhasara free_percpu_irq(virq_sgi, &cpu_number1); 637c7fdb240SAbhyuday Godhasara irq_dispose_mapping(virq_sgi); 638c7fdb240SAbhyuday Godhasara } 639c7fdb240SAbhyuday Godhasara 640c7fdb240SAbhyuday Godhasara static int xlnx_event_manager_probe(struct platform_device *pdev) 641c7fdb240SAbhyuday Godhasara { 642c7fdb240SAbhyuday Godhasara int ret; 643c7fdb240SAbhyuday Godhasara 644c7fdb240SAbhyuday Godhasara ret = zynqmp_pm_feature(PM_REGISTER_NOTIFIER); 645c7fdb240SAbhyuday Godhasara if (ret < 0) { 646c7fdb240SAbhyuday Godhasara dev_err(&pdev->dev, "Feature check failed with %d\n", ret); 647c7fdb240SAbhyuday Godhasara return ret; 648c7fdb240SAbhyuday Godhasara } 649c7fdb240SAbhyuday Godhasara 650c7fdb240SAbhyuday Godhasara if ((ret & FIRMWARE_VERSION_MASK) < 651c7fdb240SAbhyuday Godhasara REGISTER_NOTIFIER_FIRMWARE_VERSION) { 652c7fdb240SAbhyuday Godhasara dev_err(&pdev->dev, "Register notifier version error. Expected Firmware: v%d - Found: v%d\n", 653c7fdb240SAbhyuday Godhasara REGISTER_NOTIFIER_FIRMWARE_VERSION, 654c7fdb240SAbhyuday Godhasara ret & FIRMWARE_VERSION_MASK); 655c7fdb240SAbhyuday Godhasara return -EOPNOTSUPP; 656c7fdb240SAbhyuday Godhasara } 657c7fdb240SAbhyuday Godhasara 658c7fdb240SAbhyuday Godhasara /* Initialize the SGI */ 659c7fdb240SAbhyuday Godhasara ret = xlnx_event_init_sgi(pdev); 660c7fdb240SAbhyuday Godhasara if (ret) { 661c7fdb240SAbhyuday Godhasara dev_err(&pdev->dev, "SGI Init has been failed with %d\n", ret); 662c7fdb240SAbhyuday Godhasara return ret; 663c7fdb240SAbhyuday Godhasara } 664c7fdb240SAbhyuday Godhasara 665c7fdb240SAbhyuday Godhasara /* Setup function for the CPU hot-plug cases */ 666c7fdb240SAbhyuday Godhasara cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "soc/event:starting", 667c7fdb240SAbhyuday Godhasara xlnx_event_cpuhp_start, xlnx_event_cpuhp_down); 668c7fdb240SAbhyuday Godhasara 669acd6510dSTanmay Shah ret = zynqmp_pm_register_sgi(sgi_num, 0); 670c7fdb240SAbhyuday Godhasara if (ret) { 6718c016c80SJay Buddhabhatti if (ret == -EOPNOTSUPP) 6728c016c80SJay Buddhabhatti dev_err(&pdev->dev, "SGI registration not supported by TF-A or Xen\n"); 6738c016c80SJay Buddhabhatti else 6748c016c80SJay Buddhabhatti dev_err(&pdev->dev, "SGI %d registration failed, err %d\n", sgi_num, ret); 6758c016c80SJay Buddhabhatti 676c7fdb240SAbhyuday Godhasara xlnx_event_cleanup_sgi(pdev); 677c7fdb240SAbhyuday Godhasara return ret; 678c7fdb240SAbhyuday Godhasara } 679c7fdb240SAbhyuday Godhasara 680c7fdb240SAbhyuday Godhasara event_manager_availability = 0; 681c7fdb240SAbhyuday Godhasara 682c7fdb240SAbhyuday Godhasara dev_info(&pdev->dev, "SGI %d Registered over TF-A\n", sgi_num); 683c7fdb240SAbhyuday Godhasara dev_info(&pdev->dev, "Xilinx Event Management driver probed\n"); 684c7fdb240SAbhyuday Godhasara 685c7fdb240SAbhyuday Godhasara return ret; 686c7fdb240SAbhyuday Godhasara } 687c7fdb240SAbhyuday Godhasara 688e8864065SUwe Kleine-König static void xlnx_event_manager_remove(struct platform_device *pdev) 689c7fdb240SAbhyuday Godhasara { 690c7fdb240SAbhyuday Godhasara int i; 691c7fdb240SAbhyuday Godhasara struct registered_event_data *eve_data; 692c7fdb240SAbhyuday Godhasara struct hlist_node *tmp; 693c7fdb240SAbhyuday Godhasara int ret; 69405e5ba40SAbhyuday Godhasara struct agent_cb *cb_pos; 69505e5ba40SAbhyuday Godhasara struct agent_cb *cb_next; 696c7fdb240SAbhyuday Godhasara 697c7fdb240SAbhyuday Godhasara hash_for_each_safe(reg_driver_map, i, tmp, eve_data, hentry) { 69805e5ba40SAbhyuday Godhasara list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head, list) { 69905e5ba40SAbhyuday Godhasara list_del_init(&cb_pos->list); 70005e5ba40SAbhyuday Godhasara kfree(cb_pos); 70105e5ba40SAbhyuday Godhasara } 702c7fdb240SAbhyuday Godhasara hash_del(&eve_data->hentry); 703c7fdb240SAbhyuday Godhasara kfree(eve_data); 704c7fdb240SAbhyuday Godhasara } 705c7fdb240SAbhyuday Godhasara 706acd6510dSTanmay Shah ret = zynqmp_pm_register_sgi(0, 1); 707c7fdb240SAbhyuday Godhasara if (ret) 708c7fdb240SAbhyuday Godhasara dev_err(&pdev->dev, "SGI unregistration over TF-A failed with %d\n", ret); 709c7fdb240SAbhyuday Godhasara 710c7fdb240SAbhyuday Godhasara xlnx_event_cleanup_sgi(pdev); 711c7fdb240SAbhyuday Godhasara 712c7fdb240SAbhyuday Godhasara event_manager_availability = -EACCES; 713c7fdb240SAbhyuday Godhasara } 714c7fdb240SAbhyuday Godhasara 715c7fdb240SAbhyuday Godhasara static struct platform_driver xlnx_event_manager_driver = { 716c7fdb240SAbhyuday Godhasara .probe = xlnx_event_manager_probe, 717e8864065SUwe Kleine-König .remove_new = xlnx_event_manager_remove, 718c7fdb240SAbhyuday Godhasara .driver = { 719c7fdb240SAbhyuday Godhasara .name = "xlnx_event_manager", 720c7fdb240SAbhyuday Godhasara }, 721c7fdb240SAbhyuday Godhasara }; 722c7fdb240SAbhyuday Godhasara module_param(sgi_num, uint, 0); 723c7fdb240SAbhyuday Godhasara module_platform_driver(xlnx_event_manager_driver); 724