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