// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2021 MediaTek Inc.
 */

#ifndef _KBASE_GATOR_API_H_
#define _KBASE_GATOR_API_H_

/**
 * @brief This file describes the API used by Gator to fetch hardware counters.
 */

/* This define is used by the gator kernel module compile to select which DDK
 * API calling convention to use. If not defined (legacy DDK) gator assumes
 * version 1. The version to DDK release mapping is:
 *     Version 1 API: DDK versions m_r1px, m_r2px
 *     Version 2 API: DDK versions m_r3px, m_r4px
 *     Version 3 API: DDK versions m_r5px-m_r28px and b_r1px-b_r16px
 *     Version 4 API: DDK versions b_r17p0 and newer
 *
 * API Usage
 * =========
 *
 * 1] Call kbase_gator_hwcnt_init_names() to return the list of short counter
 * names for the GPU present in this device.
 *
 * 2] Create a kbase_gator_hwcnt_info structure and set the counter enables for
 * the counters you want enabled. The enables can all be set for simplicity in
 * most use cases, but disabling some will let you minimize bandwidth impact.
 *
 * 3] Call kbase_gator_hwcnt_init() using the above structure, to create a
 * counter context. On successful return the DDK will have populated the
 * structure with a variety of useful information.
 *
 * 4] Call kbase_gator_hwcnt_dump_irq() to queue a non-blocking request for a
 * counter dump. If this returns a non-zero value the request has been queued,
 * otherwise the driver has been unable to do so (typically because of another
 * user of the instrumentation exists concurrently).
 *
 * 5] Call kbase_gator_hwcnt_dump_complete() to test whether the  previously
 * requested dump has been succesful. If this returns non-zero the counter dump
 * has resolved, but the value of *success must also be tested as the dump
 * may have not been successful. If it returns zero the counter dump was
 * abandoned due to the device being busy (typically because of another
 * user of the instrumentation exists concurrently).
 *
 * 6] Process the counters stored in the buffer pointed to by ...
 *
 *        kbase_gator_hwcnt_info->kernel_dump_buffer
 *
 *    In pseudo code you can find all of the counters via this approach:
 *
 *
 *        hwcnt_info # pointer to kbase_gator_hwcnt_info structure
 *        hwcnt_name # pointer to name list
 *
 *        u32 * hwcnt_data = (u32*)hwcnt_info->kernel_dump_buffer
 *
 *        # Iterate over each 64-counter block in this GPU configuration
 *        for( i = 0; i < hwcnt_info->nr_hwc_blocks; i++) {
 *            hwc_type type = hwcnt_info->hwc_layout[i];
 *
 *            # Skip reserved type blocks - they contain no counters at all
 *            if( type == RESERVED_BLOCK ) {
 *                continue;
 *            }
 *
 *            size_t name_offset = type * 64;
 *            size_t data_offset = i * 64;
 *
 *            # Iterate over the names of the counters in this block type
 *            for( j = 0; j < 64; j++) {
 *                const char * name = hwcnt_name[name_offset+j];
 *
 *                # Skip empty name strings - there is no counter here
 *                if( name[0] == '\0' ) {
 *                    continue;
 *                }
 *
 *                u32 data = hwcnt_data[data_offset+j];
 *
 *                pr_info( "COUNTER: %s DATA: %u\n", name, data );
 *            }
 *        }
 *
 *
 *     Note that in most implementations you typically want to either SUM or
 *     AVERAGE multiple instances of the same counter if, for example, you have
 *     multiple shader cores or multiple L2 caches. The most sensible view for
 *     analysis is to AVERAGE shader core counters, but SUM L2 cache and MMU
 *     counters.
 *
 * 7] Goto 4, repeating until you want to stop collecting counters.
 *
 * 8] Release the dump resources by calling kbase_gator_hwcnt_term().
 *
 * 9] Release the name table resources by calling
 *    kbase_gator_hwcnt_term_names(). This function must only be called if
 *    init_names() returned a non-NULL value.
 **/

#define MALI_DDK_GATOR_API_VERSION 4

enum hwc_type {
	JM_BLOCK = 0,
	TILER_BLOCK,
	SHADER_BLOCK,
	MMU_L2_BLOCK,
	RESERVED_BLOCK
};

struct kbase_gator_hwcnt_info {
	/* Passed from Gator to kbase */

	/* the bitmask of enabled hardware counters for each counter block */
	uint16_t bitmask[4];

	/* Passed from kbase to Gator */

	/* ptr to counter dump memory */
	void *kernel_dump_buffer;

	/* size of counter dump memory */
	uint32_t size;

	/* the ID of the Mali device */
	uint32_t gpu_id;

	/* the number of shader cores in the GPU */
	uint32_t nr_cores;

	/* the number of core groups */
	uint32_t nr_core_groups;

	/* the memory layout of the performance counters */
	enum hwc_type *hwc_layout;

	/* the total number of hardware couter blocks */
	uint32_t nr_hwc_blocks;
};

/**
 * @brief Opaque block of Mali data which Gator needs to return to the API later.
 */
struct kbase_gator_hwcnt_handles;

/**
 * @brief Initialize the resources Gator needs for performance profiling.
 *
 * @param in_out_info   A pointer to a structure containing the enabled counters passed from Gator and all the Mali
 *                      specific information that will be returned to Gator. On entry Gator must have populated the
 *                      'bitmask' field with the counters it wishes to enable for each class of counter block.
 *                      Each entry in the array corresponds to a single counter class based on the "hwc_type"
 *                      enumeration, and each bit corresponds to an enable for 4 sequential counters (LSB enables
 *                      the first 4 counters in the block, and so on). See the GPU counter array as returned by
 *                      kbase_gator_hwcnt_get_names() for the index values of each counter for the curernt GPU.
 *
 * @return              Pointer to an opaque handle block on success, NULL on error.
 */
extern struct kbase_gator_hwcnt_handles *kbase_gator_hwcnt_init(struct kbase_gator_hwcnt_info *in_out_info);

/**
 * @brief Free all resources once Gator has finished using performance counters.
 *
 * @param in_out_info       A pointer to a structure containing the enabled counters passed from Gator and all the
 *                          Mali specific information that will be returned to Gator.
 * @param opaque_handles    A wrapper structure for kbase structures.
 */
extern void kbase_gator_hwcnt_term(struct kbase_gator_hwcnt_info *in_out_info, struct kbase_gator_hwcnt_handles *opaque_handles);

/**
 * @brief Poll whether a counter dump is successful.
 *
 * @param opaque_handles    A wrapper structure for kbase structures.
 * @param[out] success      Non-zero on success, zero on failure.
 *
 * @return                  Zero if the dump is still pending, non-zero if the dump has completed. Note that a
 *                          completed dump may not have dumped succesfully, so the caller must test for both
 *                          a completed and successful dump before processing counters.
 */
extern uint32_t kbase_gator_instr_hwcnt_dump_complete(struct kbase_gator_hwcnt_handles *opaque_handles, uint32_t * const success);

/**
 * @brief Request the generation of a new counter dump.
 *
 * @param opaque_handles    A wrapper structure for kbase structures.
 *
 * @return                  Zero if the hardware device is busy and cannot handle the request, non-zero otherwise.
 */
extern uint32_t kbase_gator_instr_hwcnt_dump_irq(struct kbase_gator_hwcnt_handles *opaque_handles);

/**
 * @brief This function is used to fetch the names table based on the Mali device in use.
 *
 * @param[out] total_counters The total number of counters short names in the Mali devices' list.
 *
 * @return                    Pointer to an array of strings of length *total_counters.
 */
extern const char * const *kbase_gator_hwcnt_init_names(uint32_t *total_counters);

/**
 * @brief This function is used to terminate the use of the names table.
 *
 * This function must only be called if the initial call to kbase_gator_hwcnt_init_names returned a non-NULL value.
 */
extern void kbase_gator_hwcnt_term_names(void);

#endif
