brintos

brintos / linux-shallow public Read only

0
0
Text · 19.7 KiB · f529e13 Raw
719 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Xilinx Event Management Driver4 *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 23static DEFINE_PER_CPU_READ_MOSTLY(int, dummy_cpu_number);24 25static int virq_sgi;26static 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 41static DEFINE_HASHTABLE(reg_driver_map, REGISTERED_DRIVER_MAX_ORDER);42static int sgi_num = XLNX_EVENT_SGI_NUM;43 44static 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 */52struct 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 u6461 *			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 is65 *			in sleep or power down state.66 * @cb_list_head:	Head of call back data list which contain the information67 *			about registered handler and private data.68 * @hentry:		hlist_node that hooks this entry into hashtable.69 */70struct 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 78static 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 104static 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 168static 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		return -ENOMEM;193	cb_data->eve_cb = cb_fun;194	cb_data->agent_data = data;195 196	/* Add into callback list */197	list_add(&cb_data->list, &eve_data->cb_list_head);198 199	hash_add(reg_driver_map, &eve_data->hentry, PM_INIT_SUSPEND_CB);200 201	return 0;202}203 204static int xlnx_remove_cb_for_suspend(event_cb_func_t cb_fun)205{206	bool is_callback_found = false;207	struct registered_event_data *eve_data;208	struct agent_cb *cb_pos;209	struct agent_cb *cb_next;210	struct hlist_node *tmp;211 212	is_need_to_unregister = false;213 214	/* Check for existing entry in hash table for given cb_type */215	hash_for_each_possible_safe(reg_driver_map, eve_data, tmp, hentry, PM_INIT_SUSPEND_CB) {216		if (eve_data->cb_type == PM_INIT_SUSPEND_CB) {217			/* Delete the list of callback */218			list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head, list) {219				if (cb_pos->eve_cb == cb_fun) {220					is_callback_found = true;221					list_del_init(&cb_pos->list);222					kfree(cb_pos);223				}224			}225			/* remove an object from a hashtable */226			hash_del(&eve_data->hentry);227			kfree(eve_data);228			is_need_to_unregister = true;229		}230	}231	if (!is_callback_found) {232		pr_warn("Didn't find any registered callback for suspend event\n");233		return -EINVAL;234	}235 236	return 0;237}238 239static int xlnx_remove_cb_for_notify_event(const u32 node_id, const u32 event,240					   event_cb_func_t cb_fun, void *data)241{242	bool is_callback_found = false;243	struct registered_event_data *eve_data;244	u64 key = ((u64)node_id << 32U) | (u64)event;245	struct agent_cb *cb_pos;246	struct agent_cb *cb_next;247	struct hlist_node *tmp;248 249	is_need_to_unregister = false;250 251	/* Check for existing entry in hash table for given key id */252	hash_for_each_possible_safe(reg_driver_map, eve_data, tmp, hentry, key) {253		if (eve_data->key == key) {254			/* Delete the list of callback */255			list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head, list) {256				if (cb_pos->eve_cb == cb_fun &&257				    cb_pos->agent_data == data) {258					is_callback_found = true;259					list_del_init(&cb_pos->list);260					kfree(cb_pos);261				}262			}263 264			/* Remove HASH table if callback list is empty */265			if (list_empty(&eve_data->cb_list_head)) {266				/* remove an object from a HASH table */267				hash_del(&eve_data->hentry);268				kfree(eve_data);269				is_need_to_unregister = true;270			}271		}272	}273	if (!is_callback_found) {274		pr_warn("Didn't find any registered callback for 0x%x 0x%x\n",275			node_id, event);276		return -EINVAL;277	}278 279	return 0;280}281 282/**283 * xlnx_register_event() - Register for the event.284 * @cb_type:	Type of callback from pm_api_cb_id,285 *			PM_NOTIFY_CB - for Error Events,286 *			PM_INIT_SUSPEND_CB - for suspend callback.287 * @node_id:	Node-Id related to event.288 * @event:	Event Mask for the Error Event.289 * @wake:	Flag specifying whether the subsystem should be woken upon290 *		event notification.291 * @cb_fun:	Function pointer to store the callback function.292 * @data:	Pointer for the driver instance.293 *294 * Return:	Returns 0 on successful registration else error code.295 */296int xlnx_register_event(const enum pm_api_cb_id cb_type, const u32 node_id, const u32 event,297			const bool wake, event_cb_func_t cb_fun, void *data)298{299	int ret = 0;300	u32 eve;301	int pos;302 303	if (event_manager_availability)304		return event_manager_availability;305 306	if (cb_type != PM_NOTIFY_CB && cb_type != PM_INIT_SUSPEND_CB) {307		pr_err("%s() Unsupported Callback 0x%x\n", __func__, cb_type);308		return -EINVAL;309	}310 311	if (!cb_fun)312		return -EFAULT;313 314	if (cb_type == PM_INIT_SUSPEND_CB) {315		ret = xlnx_add_cb_for_suspend(cb_fun, data);316	} else {317		if (!xlnx_is_error_event(node_id)) {318			/* Add entry for Node-Id/Event in hash table */319			ret = xlnx_add_cb_for_notify_event(node_id, event, wake, cb_fun, data);320		} else {321			/* Add into Hash table */322			for (pos = 0; pos < MAX_BITS; pos++) {323				eve = event & (1 << pos);324				if (!eve)325					continue;326 327				/* Add entry for Node-Id/Eve in hash table */328				ret = xlnx_add_cb_for_notify_event(node_id, eve, wake, cb_fun,329								   data);330				/* Break the loop if got error */331				if (ret)332					break;333			}334			if (ret) {335				/* Skip the Event for which got the error */336				pos--;337				/* Remove registered(during this call) event from hash table */338				for ( ; pos >= 0; pos--) {339					eve = event & (1 << pos);340					if (!eve)341						continue;342					xlnx_remove_cb_for_notify_event(node_id, eve, cb_fun, data);343				}344			}345		}346 347		if (ret) {348			pr_err("%s() failed for 0x%x and 0x%x: %d\r\n", __func__, node_id,349			       event, ret);350			return ret;351		}352 353		/* Register for Node-Id/Event combination in firmware */354		ret = zynqmp_pm_register_notifier(node_id, event, wake, true);355		if (ret) {356			pr_err("%s() failed for 0x%x and 0x%x: %d\r\n", __func__, node_id,357			       event, ret);358			/* Remove already registered event from hash table */359			if (xlnx_is_error_event(node_id)) {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			} else {367				xlnx_remove_cb_for_notify_event(node_id, event, cb_fun, data);368			}369			return ret;370		}371	}372 373	return ret;374}375EXPORT_SYMBOL_GPL(xlnx_register_event);376 377/**378 * xlnx_unregister_event() - Unregister for the event.379 * @cb_type:	Type of callback from pm_api_cb_id,380 *			PM_NOTIFY_CB - for Error Events,381 *			PM_INIT_SUSPEND_CB - for suspend callback.382 * @node_id:	Node-Id related to event.383 * @event:	Event Mask for the Error Event.384 * @cb_fun:	Function pointer of callback function.385 * @data:	Pointer of agent's private data.386 *387 * Return:	Returns 0 on successful unregistration else error code.388 */389int xlnx_unregister_event(const enum pm_api_cb_id cb_type, const u32 node_id, const u32 event,390			  event_cb_func_t cb_fun, void *data)391{392	int ret = 0;393	u32 eve, pos;394 395	is_need_to_unregister = false;396 397	if (event_manager_availability)398		return event_manager_availability;399 400	if (cb_type != PM_NOTIFY_CB && cb_type != PM_INIT_SUSPEND_CB) {401		pr_err("%s() Unsupported Callback 0x%x\n", __func__, cb_type);402		return -EINVAL;403	}404 405	if (!cb_fun)406		return -EFAULT;407 408	if (cb_type == PM_INIT_SUSPEND_CB) {409		ret = xlnx_remove_cb_for_suspend(cb_fun);410	} else {411		/* Remove Node-Id/Event from hash table */412		if (!xlnx_is_error_event(node_id)) {413			xlnx_remove_cb_for_notify_event(node_id, event, cb_fun, data);414		} else {415			for (pos = 0; pos < MAX_BITS; pos++) {416				eve = event & (1 << pos);417				if (!eve)418					continue;419 420				xlnx_remove_cb_for_notify_event(node_id, eve, cb_fun, data);421			}422		}423 424		/* Un-register if list is empty */425		if (is_need_to_unregister) {426			/* Un-register for Node-Id/Event combination */427			ret = zynqmp_pm_register_notifier(node_id, event, false, false);428			if (ret) {429				pr_err("%s() failed for 0x%x and 0x%x: %d\n",430				       __func__, node_id, event, ret);431				return ret;432			}433		}434	}435 436	return ret;437}438EXPORT_SYMBOL_GPL(xlnx_unregister_event);439 440static void xlnx_call_suspend_cb_handler(const u32 *payload)441{442	bool is_callback_found = false;443	struct registered_event_data *eve_data;444	u32 cb_type = payload[0];445	struct agent_cb *cb_pos;446	struct agent_cb *cb_next;447 448	/* Check for existing entry in hash table for given cb_type */449	hash_for_each_possible(reg_driver_map, eve_data, hentry, cb_type) {450		if (eve_data->cb_type == cb_type) {451			list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head, list) {452				cb_pos->eve_cb(&payload[0], cb_pos->agent_data);453				is_callback_found = true;454			}455		}456	}457	if (!is_callback_found)458		pr_warn("Didn't find any registered callback for suspend event\n");459}460 461static void xlnx_call_notify_cb_handler(const u32 *payload)462{463	bool is_callback_found = false;464	struct registered_event_data *eve_data;465	u64 key = ((u64)payload[1] << 32U) | (u64)payload[2];466	int ret;467	struct agent_cb *cb_pos;468	struct agent_cb *cb_next;469 470	/* Check for existing entry in hash table for given key id */471	hash_for_each_possible(reg_driver_map, eve_data, hentry, key) {472		if (eve_data->key == key) {473			list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head, list) {474				cb_pos->eve_cb(&payload[0], cb_pos->agent_data);475				is_callback_found = true;476			}477 478			/* re register with firmware to get future events */479			ret = zynqmp_pm_register_notifier(payload[1], payload[2],480							  eve_data->wake, true);481			if (ret) {482				pr_err("%s() failed for 0x%x and 0x%x: %d\r\n", __func__,483				       payload[1], payload[2], ret);484				list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head,485							 list) {486					/* Remove already registered event from hash table */487					xlnx_remove_cb_for_notify_event(payload[1], payload[2],488									cb_pos->eve_cb,489									cb_pos->agent_data);490				}491			}492		}493	}494	if (!is_callback_found)495		pr_warn("Unhandled SGI node 0x%x event 0x%x. Expected with Xen hypervisor\n",496			payload[1], payload[2]);497}498 499static void xlnx_get_event_callback_data(u32 *buf)500{501	zynqmp_pm_invoke_fn(GET_CALLBACK_DATA, buf, 0);502}503 504static irqreturn_t xlnx_event_handler(int irq, void *dev_id)505{506	u32 cb_type, node_id, event, pos;507	u32 payload[CB_MAX_PAYLOAD_SIZE] = {0};508	u32 event_data[CB_MAX_PAYLOAD_SIZE] = {0};509 510	/* Get event data */511	xlnx_get_event_callback_data(payload);512 513	/* First element is callback type, others are callback arguments */514	cb_type = payload[0];515 516	if (cb_type == PM_NOTIFY_CB) {517		node_id = payload[1];518		event = payload[2];519		if (!xlnx_is_error_event(node_id)) {520			xlnx_call_notify_cb_handler(payload);521		} else {522			/*523			 * Each call back function expecting payload as an input arguments.524			 * We can get multiple error events as in one call back through error525			 * mask. So payload[2] may can contain multiple error events.526			 * In reg_driver_map database we store data in the combination of single527			 * node_id-error combination.528			 * So coping the payload message into event_data and update the529			 * event_data[2] with Error Mask for single error event and use530			 * event_data as input argument for registered call back function.531			 *532			 */533			memcpy(event_data, payload, (4 * CB_MAX_PAYLOAD_SIZE));534			/* Support Multiple Error Event */535			for (pos = 0; pos < MAX_BITS; pos++) {536				if ((0 == (event & (1 << pos))))537					continue;538				event_data[2] = (event & (1 << pos));539				xlnx_call_notify_cb_handler(event_data);540			}541		}542	} else if (cb_type == PM_INIT_SUSPEND_CB) {543		xlnx_call_suspend_cb_handler(payload);544	} else {545		pr_err("%s() Unsupported Callback %d\n", __func__, cb_type);546	}547 548	return IRQ_HANDLED;549}550 551static int xlnx_event_cpuhp_start(unsigned int cpu)552{553	enable_percpu_irq(virq_sgi, IRQ_TYPE_NONE);554 555	return 0;556}557 558static int xlnx_event_cpuhp_down(unsigned int cpu)559{560	disable_percpu_irq(virq_sgi);561 562	return 0;563}564 565static void xlnx_disable_percpu_irq(void *data)566{567	disable_percpu_irq(virq_sgi);568}569 570static int xlnx_event_init_sgi(struct platform_device *pdev)571{572	int ret = 0;573	/*574	 * IRQ related structures are used for the following:575	 * for each SGI interrupt ensure its mapped by GIC IRQ domain576	 * and that each corresponding linux IRQ for the HW IRQ has577	 * a handler for when receiving an interrupt from the remote578	 * processor.579	 */580	struct irq_domain *domain;581	struct irq_fwspec sgi_fwspec;582	struct device_node *interrupt_parent = NULL;583	struct device *parent = pdev->dev.parent;584 585	/* Find GIC controller to map SGIs. */586	interrupt_parent = of_irq_find_parent(parent->of_node);587	if (!interrupt_parent) {588		dev_err(&pdev->dev, "Failed to find property for Interrupt parent\n");589		return -EINVAL;590	}591 592	/* Each SGI needs to be associated with GIC's IRQ domain. */593	domain = irq_find_host(interrupt_parent);594	of_node_put(interrupt_parent);595 596	/* Each mapping needs GIC domain when finding IRQ mapping. */597	sgi_fwspec.fwnode = domain->fwnode;598 599	/*600	 * When irq domain looks at mapping each arg is as follows:601	 * 3 args for: interrupt type (SGI), interrupt # (set later), type602	 */603	sgi_fwspec.param_count = 1;604 605	/* Set SGI's hwirq */606	sgi_fwspec.param[0] = sgi_num;607	virq_sgi = irq_create_fwspec_mapping(&sgi_fwspec);608 609	ret = request_percpu_irq(virq_sgi, xlnx_event_handler, "xlnx_event_mgmt",610				 &dummy_cpu_number);611 612	WARN_ON(ret);613	if (ret) {614		irq_dispose_mapping(virq_sgi);615		return ret;616	}617 618	irq_to_desc(virq_sgi);619	irq_set_status_flags(virq_sgi, IRQ_PER_CPU);620 621	return ret;622}623 624static void xlnx_event_cleanup_sgi(struct platform_device *pdev)625{626	cpuhp_remove_state(CPUHP_AP_ONLINE_DYN);627 628	on_each_cpu(xlnx_disable_percpu_irq, NULL, 1);629 630	irq_clear_status_flags(virq_sgi, IRQ_PER_CPU);631	free_percpu_irq(virq_sgi, &dummy_cpu_number);632	irq_dispose_mapping(virq_sgi);633}634 635static int xlnx_event_manager_probe(struct platform_device *pdev)636{637	int ret;638 639	ret = zynqmp_pm_feature(PM_REGISTER_NOTIFIER);640	if (ret < 0) {641		dev_err(&pdev->dev, "Feature check failed with %d\n", ret);642		return ret;643	}644 645	if ((ret & FIRMWARE_VERSION_MASK) <646	    REGISTER_NOTIFIER_FIRMWARE_VERSION) {647		dev_err(&pdev->dev, "Register notifier version error. Expected Firmware: v%d - Found: v%d\n",648			REGISTER_NOTIFIER_FIRMWARE_VERSION,649			ret & FIRMWARE_VERSION_MASK);650		return -EOPNOTSUPP;651	}652 653	/* Initialize the SGI */654	ret = xlnx_event_init_sgi(pdev);655	if (ret) {656		dev_err(&pdev->dev, "SGI Init has been failed with %d\n", ret);657		return ret;658	}659 660	/* Setup function for the CPU hot-plug cases */661	cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "soc/event:starting",662			  xlnx_event_cpuhp_start, xlnx_event_cpuhp_down);663 664	ret = zynqmp_pm_register_sgi(sgi_num, 0);665	if (ret) {666		if (ret == -EOPNOTSUPP)667			dev_err(&pdev->dev, "SGI registration not supported by TF-A or Xen\n");668		else669			dev_err(&pdev->dev, "SGI %d registration failed, err %d\n", sgi_num, ret);670 671		xlnx_event_cleanup_sgi(pdev);672		return ret;673	}674 675	event_manager_availability = 0;676 677	dev_info(&pdev->dev, "SGI %d Registered over TF-A\n", sgi_num);678	dev_info(&pdev->dev, "Xilinx Event Management driver probed\n");679 680	return ret;681}682 683static void xlnx_event_manager_remove(struct platform_device *pdev)684{685	int i;686	struct registered_event_data *eve_data;687	struct hlist_node *tmp;688	int ret;689	struct agent_cb *cb_pos;690	struct agent_cb *cb_next;691 692	hash_for_each_safe(reg_driver_map, i, tmp, eve_data, hentry) {693		list_for_each_entry_safe(cb_pos, cb_next, &eve_data->cb_list_head, list) {694			list_del_init(&cb_pos->list);695			kfree(cb_pos);696		}697		hash_del(&eve_data->hentry);698		kfree(eve_data);699	}700 701	ret = zynqmp_pm_register_sgi(0, 1);702	if (ret)703		dev_err(&pdev->dev, "SGI unregistration over TF-A failed with %d\n", ret);704 705	xlnx_event_cleanup_sgi(pdev);706 707	event_manager_availability = -EACCES;708}709 710static struct platform_driver xlnx_event_manager_driver = {711	.probe = xlnx_event_manager_probe,712	.remove_new = xlnx_event_manager_remove,713	.driver = {714		.name = "xlnx_event_manager",715	},716};717module_param(sgi_num, uint, 0);718module_platform_driver(xlnx_event_manager_driver);719