/*
 * Copyright (c) 2017-2022, Arm Limited and Contributors. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <assert.h>

#include <lib/utils.h>

#include "sdei_private.h"

#define MAP_OFF(_map, _mapping) ((_map) - (_mapping)->map)

/*
 * Get SDEI entry with the given mapping: on success, returns pointer to SDEI
 * entry. On error, returns NULL.
 *
 * Both shared and private maps are stored in single-dimensional array. Private
 * event entries are kept for each PE forming a 2D array.
 */
sdei_entry_t *get_event_entry(sdei_ev_map_t *map)
{
	const sdei_mapping_t *mapping;
	sdei_entry_t *cpu_priv_base;
	unsigned int base_idx;
	long int idx;

	if (is_event_private(map)) {
		/*
		 * For a private map, find the index of the mapping in the
		 * array.
		 */
		mapping = SDEI_PRIVATE_MAPPING();
		idx = MAP_OFF(map, mapping);

		/* Base of private mappings for this CPU */
		base_idx = plat_my_core_pos() * ((unsigned int) mapping->num_maps);
		cpu_priv_base = &sdei_private_event_table[base_idx];

		/*
		 * Return the address of the entry at the same index in the
		 * per-CPU event entry.
		 */
		return &cpu_priv_base[idx];
	} else {
		mapping = SDEI_SHARED_MAPPING();
		idx = MAP_OFF(map, mapping);

		return &sdei_shared_event_table[idx];
	}
}

/*
 * Find event mapping for a given interrupt number: On success, returns pointer
 * to the event mapping. On error, returns NULL.
 */
sdei_ev_map_t *find_event_map_by_intr(unsigned int intr_num, bool shared)
{
	const sdei_mapping_t *mapping;
	sdei_ev_map_t *map;
	unsigned int i;

	/*
	 * Look for a match in private and shared mappings, as requested. This
	 * is a linear search. However, if the mappings are required to be
	 * sorted, for large maps, we could consider binary search.
	 */
	mapping = shared ? SDEI_SHARED_MAPPING() : SDEI_PRIVATE_MAPPING();
	iterate_mapping(mapping, i, map) {
		if (map->intr == intr_num)
			return map;
	}

	return NULL;
}

/*
 * Find event mapping for a given event number: On success returns pointer to
 * the event mapping. On error, returns NULL.
 */
sdei_ev_map_t *find_event_map(int ev_num)
{
	const sdei_mapping_t *mapping;
	sdei_ev_map_t *map;
	unsigned int i, j;

	/*
	 * Iterate through mappings to find a match. This is a linear search.
	 * However, if the mappings are required to be sorted, for large maps,
	 * we could consider binary search.
	 */
	for_each_mapping_type(i, mapping) {
		iterate_mapping(mapping, j, map) {
			if (map->ev_num == ev_num)
				return map;
		}
	}

	return NULL;
}

/*
 * Return the total number of currently registered SDEI events.
 */
int sdei_get_registered_event_count(void)
{
	const sdei_mapping_t *mapping;
	sdei_ev_map_t *map;
	unsigned int i;
	unsigned int j;
	int count = 0;

	/* Add up reg counts for each mapping. */
	for_each_mapping_type(i, mapping) {
		iterate_mapping(mapping, j, map) {
			count += map->reg_count;
		}
	}

	return count;
}
