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