xlnx_event_manager.c 20 KB

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