/* SPDX-License-Identifier: GPL-2.0 */
#ifndef LINUX_POWERPC_PERF_REQ_GEN_PERF_H_
#define LINUX_POWERPC_PERF_REQ_GEN_PERF_H_

#include <linux/perf_event.h>
#include <linux/stringify.h>

#ifndef REQUEST_FILE
#error "REQUEST_FILE must be defined before including"
#endif

#ifndef NAME_LOWER
#error "NAME_LOWER must be defined before including"
#endif

#ifndef NAME_UPPER
#error "NAME_UPPER must be defined before including"
#endif

#define BE_TYPE_b1 __u8
#define BE_TYPE_b2 __be16
#define BE_TYPE_b4 __be32
#define BE_TYPE_b8 __be64

#define BYTES_TO_BE_TYPE(bytes) \
		BE_TYPE_b##bytes

#define CAT2_(a, b) a ## b
#define CAT2(a, b) CAT2_(a, b)
#define CAT3_(a, b, c) a ## b ## c
#define CAT3(a, b, c) CAT3_(a, b, c)

/*
 * enumerate the request values as
 * <NAME_UPPER>_<request name> = <request value>
 */
#define REQUEST_VALUE__(name_upper, r_name) name_upper ## _ ## r_name
#define REQUEST_VALUE_(name_upper, r_name) REQUEST_VALUE__(name_upper, r_name)
#define REQUEST_VALUE(r_name) REQUEST_VALUE_(NAME_UPPER, r_name)

#include "_clear.h"
#define REQUEST_(r_name, r_value, r_idx_1, r_fields) \
	REQUEST_VALUE(r_name) = r_value,
enum CAT2(NAME_LOWER, _requests) {
#include REQUEST_FILE
};

/*
 * For each request:
 * struct <NAME_LOWER>_<request name> {
 *	r_fields
 * };
 */
#include "_clear.h"
#define STRUCT_NAME__(name_lower, r_name) name_lower ## _ ## r_name
#define STRUCT_NAME_(name_lower, r_name) STRUCT_NAME__(name_lower, r_name)
#define STRUCT_NAME(r_name) STRUCT_NAME_(NAME_LOWER, r_name)
#define REQUEST_(r_name, r_value, r_idx_1, r_fields)	\
struct STRUCT_NAME(r_name) {				\
	r_fields					\
};
#define __field_(r_name, r_value, r_idx_1, f_offset, f_bytes, f_name) \
	BYTES_TO_BE_TYPE(f_bytes) f_name;
#define __count_(r_name, r_value, r_idx_1, f_offset, f_bytes, f_name) \
	__field_(r_name, r_value, r_idx_1, f_offset, f_bytes, f_name)
#define __array_(r_name, r_value, r_idx_1, a_offset, a_bytes, a_name) \
	__u8 a_name[a_bytes];

#include REQUEST_FILE

/*
 * Generate a check of the field offsets
 * <NAME_LOWER>_assert_offsets_correct()
 */
#include "_clear.h"
#define REQUEST_(r_name, r_value, index, r_fields)			\
r_fields
#define __field_(r_name, r_value, r_idx_1, f_offset, f_size, f_name) \
	BUILD_BUG_ON(offsetof(struct STRUCT_NAME(r_name), f_name) != f_offset);
#define __count_(r_name, r_value, r_idx_1, c_offset, c_size, c_name) \
	__field_(r_name, r_value, r_idx_1, c_offset, c_size, c_name)
#define __array_(r_name, r_value, r_idx_1, a_offset, a_size, a_name) \
	__field_(r_name, r_value, r_idx_1, a_offset, a_size, a_name)

static inline void CAT2(NAME_LOWER, _assert_offsets_correct)(void)
{
#include REQUEST_FILE
}

/*
 * Generate event attributes:
 * PMU_EVENT_ATTR_STRING(<request name>_<field name>,
 *	<NAME_LOWER>_event_attr_<request name>_<field name>,
 *		"request=<request value>"
 *		"starting_index=<starting index type>"
 *		"counter_info_version=CURRENT_COUNTER_INFO_VERSION"
 *		"length=<f_size>"
 *		"offset=<f_offset>")
 *
 *	TODO: counter_info_version may need to vary, we should interperate the
 *	value to some extent
 */
#define EVENT_ATTR_NAME__(name, r_name, c_name) \
	name ## _event_attr_ ## r_name ## _ ## c_name
#define EVENT_ATTR_NAME_(name, r_name, c_name) \
	EVENT_ATTR_NAME__(name, r_name, c_name)
#define EVENT_ATTR_NAME(r_name, c_name) \
	EVENT_ATTR_NAME_(NAME_LOWER, r_name, c_name)

#include "_clear.h"
#define __field_(r_name, r_value, r_idx_1, f_offset, f_size, f_name)
#define __array_(r_name, r_value, r_idx_1, a_offset, a_size, a_name)
#define __count_(r_name, r_value, r_idx_1, c_offset, c_size, c_name)	\
PMU_EVENT_ATTR_STRING(							\
		CAT3(r_name, _, c_name),				\
		EVENT_ATTR_NAME(r_name, c_name),			\
		"request=" __stringify(r_value) ","			\
		r_idx_1 ","						\
		"counter_info_version="					\
			__stringify(COUNTER_INFO_VERSION_CURRENT) ","	\
		"length=" #c_size ","					\
		"offset=" #c_offset)
#define REQUEST_(r_name, r_value, r_idx_1, r_fields)			\
	r_fields

#include REQUEST_FILE

/*
 * Define event attribute array
 * static struct attribute *hv_gpci_event_attrs[] = {
 *	&<NAME_LOWER>_event_attr_<request name>_<field name>.attr,
 * };
 */
#include "_clear.h"
#define __field_(r_name, r_value, r_idx_1, f_offset, f_size, f_name)
#define __count_(r_name, r_value, r_idx_1, c_offset, c_size, c_name)	\
	&EVENT_ATTR_NAME(r_name, c_name).attr.attr,
#define __array_(r_name, r_value, r_idx_1, a_offset, a_size, a_name)
#define REQUEST_(r_name, r_value, r_idx_1, r_fields)			\
	r_fields

/* Generate event list for platforms with counter_info_version 0x6 or below */
static __maybe_unused struct attribute *hv_gpci_event_attrs_v6[] = {
#include REQUEST_FILE
	NULL
};

/*
 * Based on getPerfCountInfo v1.018 documentation, some of the hv-gpci
 * events were deprecated for platform firmware that supports
 * counter_info_version 0x8 or above.
 * Those deprecated events are still part of platform firmware that
 * support counter_info_version 0x6 and below. As per the getPerfCountInfo
 * v1.018 documentation there is no counter_info_version 0x7.
 * Undefining macro ENABLE_EVENTS_COUNTERINFO_V6, to disable the addition of
 * deprecated events in "hv_gpci_event_attrs" attribute group, for platforms
 * that supports counter_info_version 0x8 or above.
 */
#undef ENABLE_EVENTS_COUNTERINFO_V6

/* Generate event list for platforms with counter_info_version 0x8 or above*/
static __maybe_unused struct attribute *hv_gpci_event_attrs[] = {
#include REQUEST_FILE
	NULL
};

/* cleanup */
#include "_clear.h"
#undef EVENT_ATTR_NAME
#undef EVENT_ATTR_NAME_
#undef BIT_NAME
#undef BIT_NAME_
#undef STRUCT_NAME
#undef REQUEST_VALUE
#undef REQUEST_VALUE_

#endif
