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