// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
|
/*
|
*
|
* (C) COPYRIGHT 2018-2021 ARM Limited. All rights reserved.
|
*
|
* This program is free software and is provided to you under the terms of the
|
* GNU General Public License version 2 as published by the Free Software
|
* Foundation, and any use by you of this program is subject to the terms
|
* of such GNU license.
|
*
|
* This program is distributed in the hope that it will be useful,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* GNU General Public License for more details.
|
*
|
* You should have received a copy of the GNU General Public License
|
* along with this program; if not, you can access it online at
|
* http://www.gnu.org/licenses/gpl-2.0.html.
|
*
|
*/
|
|
#include "mali_kbase_hwcnt_backend_jm.h"
|
#include "mali_kbase_hwcnt_gpu.h"
|
#include "mali_kbase_hwcnt_types.h"
|
#include "mali_kbase.h"
|
#include "backend/gpu/mali_kbase_pm_ca.h"
|
#include "mali_kbase_hwaccess_instr.h"
|
#include "mali_kbase_hwaccess_time.h"
|
#include "mali_kbase_ccswe.h"
|
|
#include "backend/gpu/mali_kbase_clk_rate_trace_mgr.h"
|
|
#include "backend/gpu/mali_kbase_pm_internal.h"
|
|
/**
|
* struct kbase_hwcnt_backend_jm_info - Information used to create an instance
|
* of a JM hardware counter backend.
|
* @kbdev: KBase device.
|
* @counter_set: The performance counter set to use.
|
* @metadata: Hardware counter metadata.
|
* @dump_bytes: Bytes of GPU memory required to perform a
|
* hardware counter dump.
|
*/
|
struct kbase_hwcnt_backend_jm_info {
|
struct kbase_device *kbdev;
|
enum kbase_hwcnt_set counter_set;
|
const struct kbase_hwcnt_metadata *metadata;
|
size_t dump_bytes;
|
};
|
|
/**
|
* struct kbase_hwcnt_backend_jm - Instance of a JM hardware counter backend.
|
* @info: Info used to create the backend.
|
* @kctx: KBase context used for GPU memory allocation and
|
* counter dumping.
|
* @gpu_dump_va: GPU hardware counter dump buffer virtual address.
|
* @cpu_dump_va: CPU mapping of gpu_dump_va.
|
* @vmap: Dump buffer vmap.
|
* @enabled: True if dumping has been enabled, else false.
|
* @pm_core_mask: PM state sync-ed shaders core mask for the enabled
|
* dumping.
|
* @curr_config: Current allocated hardware resources to correctly map the src
|
* raw dump buffer to the dst dump buffer.
|
* @clk_enable_map: The enable map specifying enabled clock domains.
|
* @cycle_count_elapsed:
|
* Cycle count elapsed for a given sample period.
|
* The top clock cycle, index 0, is read directly from
|
* hardware, but the other clock domains need to be
|
* calculated with software estimation.
|
* @prev_cycle_count: Previous cycle count to calculate the cycle count for
|
* sample period.
|
* @rate_listener: Clock rate listener callback state.
|
* @ccswe_shader_cores: Shader cores cycle count software estimator.
|
*/
|
struct kbase_hwcnt_backend_jm {
|
const struct kbase_hwcnt_backend_jm_info *info;
|
struct kbase_context *kctx;
|
u64 gpu_dump_va;
|
void *cpu_dump_va;
|
struct kbase_vmap_struct *vmap;
|
bool enabled;
|
u64 pm_core_mask;
|
struct kbase_hwcnt_curr_config curr_config;
|
u64 clk_enable_map;
|
u64 cycle_count_elapsed[BASE_MAX_NR_CLOCKS_REGULATORS];
|
u64 prev_cycle_count[BASE_MAX_NR_CLOCKS_REGULATORS];
|
struct kbase_clk_rate_listener rate_listener;
|
struct kbase_ccswe ccswe_shader_cores;
|
};
|
|
/**
|
* kbasep_hwcnt_backend_jm_gpu_info_init() - Initialise an info structure used
|
* to create the hwcnt metadata.
|
* @kbdev: Non-NULL pointer to kbase device.
|
* @info: Non-NULL pointer to data structure to be filled in.
|
*
|
* The initialised info struct will only be valid for use while kbdev is valid.
|
*/
|
static int
|
kbasep_hwcnt_backend_jm_gpu_info_init(struct kbase_device *kbdev,
|
struct kbase_hwcnt_gpu_info *info)
|
{
|
size_t clk;
|
|
if (!kbdev || !info)
|
return -EINVAL;
|
|
{
|
const struct base_gpu_props *props = &kbdev->gpu_props.props;
|
const size_t l2_count = props->l2_props.num_l2_slices;
|
const size_t core_mask =
|
props->coherency_info.group[0].core_mask;
|
|
info->l2_count = l2_count;
|
info->core_mask = core_mask;
|
info->prfcnt_values_per_block =
|
KBASE_HWCNT_V5_DEFAULT_VALUES_PER_BLOCK;
|
}
|
|
/* Determine the number of available clock domains. */
|
for (clk = 0; clk < BASE_MAX_NR_CLOCKS_REGULATORS; clk++) {
|
if (kbdev->pm.clk_rtm.clks[clk] == NULL)
|
break;
|
}
|
info->clk_cnt = clk;
|
|
return 0;
|
}
|
|
/**
|
* kbasep_hwcnt_backend_jm_on_freq_change() - On freq change callback
|
*
|
* @rate_listener: Callback state
|
* @clk_index: Clock index
|
* @clk_rate_hz: Clock frequency(hz)
|
*/
|
static void kbasep_hwcnt_backend_jm_on_freq_change(
|
struct kbase_clk_rate_listener *rate_listener,
|
u32 clk_index,
|
u32 clk_rate_hz)
|
{
|
struct kbase_hwcnt_backend_jm *backend_jm = container_of(
|
rate_listener, struct kbase_hwcnt_backend_jm, rate_listener);
|
u64 timestamp_ns;
|
|
if (clk_index != KBASE_CLOCK_DOMAIN_SHADER_CORES)
|
return;
|
|
timestamp_ns = ktime_get_raw_ns();
|
kbase_ccswe_freq_change(
|
&backend_jm->ccswe_shader_cores, timestamp_ns, clk_rate_hz);
|
}
|
|
/**
|
* kbasep_hwcnt_backend_jm_cc_enable() - Enable cycle count tracking
|
*
|
* @backend_jm: Non-NULL pointer to backend.
|
* @enable_map: Non-NULL pointer to enable map specifying enabled counters.
|
* @timestamp_ns: Timestamp(ns) when HWCNT were enabled.
|
*/
|
static void kbasep_hwcnt_backend_jm_cc_enable(
|
struct kbase_hwcnt_backend_jm *backend_jm,
|
const struct kbase_hwcnt_enable_map *enable_map,
|
u64 timestamp_ns)
|
{
|
struct kbase_device *kbdev = backend_jm->kctx->kbdev;
|
u64 clk_enable_map = enable_map->clk_enable_map;
|
u64 cycle_count;
|
|
if (kbase_hwcnt_clk_enable_map_enabled(
|
clk_enable_map, KBASE_CLOCK_DOMAIN_TOP)) {
|
/* turn on the cycle counter */
|
kbase_pm_request_gpu_cycle_counter_l2_is_on(kbdev);
|
/* Read cycle count for top clock domain. */
|
kbase_backend_get_gpu_time_norequest(
|
kbdev, &cycle_count, NULL, NULL);
|
|
backend_jm->prev_cycle_count[KBASE_CLOCK_DOMAIN_TOP] =
|
cycle_count;
|
}
|
|
if (kbase_hwcnt_clk_enable_map_enabled(
|
clk_enable_map, KBASE_CLOCK_DOMAIN_SHADER_CORES)) {
|
/* software estimation for non-top clock domains */
|
struct kbase_clk_rate_trace_manager *rtm = &kbdev->pm.clk_rtm;
|
const struct kbase_clk_data *clk_data =
|
rtm->clks[KBASE_CLOCK_DOMAIN_SHADER_CORES];
|
u32 cur_freq;
|
unsigned long flags;
|
|
spin_lock_irqsave(&rtm->lock, flags);
|
|
cur_freq = (u32) clk_data->clock_val;
|
kbase_ccswe_reset(&backend_jm->ccswe_shader_cores);
|
kbase_ccswe_freq_change(
|
&backend_jm->ccswe_shader_cores,
|
timestamp_ns,
|
cur_freq);
|
|
kbase_clk_rate_trace_manager_subscribe_no_lock(
|
rtm, &backend_jm->rate_listener);
|
|
spin_unlock_irqrestore(&rtm->lock, flags);
|
|
/* ccswe was reset. The estimated cycle is zero. */
|
backend_jm->prev_cycle_count[
|
KBASE_CLOCK_DOMAIN_SHADER_CORES] = 0;
|
}
|
|
/* Keep clk_enable_map for dump_request. */
|
backend_jm->clk_enable_map = clk_enable_map;
|
}
|
|
/**
|
* kbasep_hwcnt_backend_jm_cc_disable() - Disable cycle count tracking
|
*
|
* @backend_jm: Non-NULL pointer to backend.
|
*/
|
static void kbasep_hwcnt_backend_jm_cc_disable(
|
struct kbase_hwcnt_backend_jm *backend_jm)
|
{
|
struct kbase_device *kbdev = backend_jm->kctx->kbdev;
|
struct kbase_clk_rate_trace_manager *rtm = &kbdev->pm.clk_rtm;
|
u64 clk_enable_map = backend_jm->clk_enable_map;
|
|
if (kbase_hwcnt_clk_enable_map_enabled(
|
clk_enable_map, KBASE_CLOCK_DOMAIN_TOP)) {
|
/* turn off the cycle counter */
|
kbase_pm_release_gpu_cycle_counter(kbdev);
|
}
|
|
if (kbase_hwcnt_clk_enable_map_enabled(
|
clk_enable_map, KBASE_CLOCK_DOMAIN_SHADER_CORES)) {
|
|
kbase_clk_rate_trace_manager_unsubscribe(
|
rtm, &backend_jm->rate_listener);
|
}
|
}
|
|
|
/**
|
* kbasep_hwcnt_gpu_update_curr_config() - Update the destination buffer with
|
* current config information.
|
* @kbdev: Non-NULL pointer to kbase device.
|
* @curr_config: Non-NULL pointer to return the current configuration of
|
* hardware allocated to the GPU.
|
*
|
* The current configuration information is used for architectures where the
|
* max_config interface is available from the Arbiter. In this case the current
|
* allocated hardware is not always the same, so the current config information
|
* is used to correctly map the current allocated resources to the memory layout
|
* that is copied to the user space.
|
*
|
* Return: 0 on success, else error code.
|
*/
|
static int kbasep_hwcnt_gpu_update_curr_config(
|
struct kbase_device *kbdev,
|
struct kbase_hwcnt_curr_config *curr_config)
|
{
|
if (WARN_ON(!kbdev) || WARN_ON(!curr_config))
|
return -EINVAL;
|
|
lockdep_assert_held(&kbdev->hwaccess_lock);
|
|
curr_config->num_l2_slices =
|
kbdev->gpu_props.curr_config.l2_slices;
|
curr_config->shader_present =
|
kbdev->gpu_props.curr_config.shader_present;
|
return 0;
|
}
|
|
/* JM backend implementation of kbase_hwcnt_backend_timestamp_ns_fn */
|
static u64 kbasep_hwcnt_backend_jm_timestamp_ns(
|
struct kbase_hwcnt_backend *backend)
|
{
|
(void)backend;
|
return ktime_get_raw_ns();
|
}
|
|
/* JM backend implementation of kbase_hwcnt_backend_dump_enable_nolock_fn */
|
static int kbasep_hwcnt_backend_jm_dump_enable_nolock(
|
struct kbase_hwcnt_backend *backend,
|
const struct kbase_hwcnt_enable_map *enable_map)
|
{
|
int errcode;
|
struct kbase_hwcnt_backend_jm *backend_jm =
|
(struct kbase_hwcnt_backend_jm *)backend;
|
struct kbase_context *kctx;
|
struct kbase_device *kbdev;
|
struct kbase_hwcnt_physical_enable_map phys_enable_map;
|
enum kbase_hwcnt_physical_set phys_counter_set;
|
struct kbase_instr_hwcnt_enable enable;
|
u64 timestamp_ns;
|
|
if (!backend_jm || !enable_map || backend_jm->enabled ||
|
(enable_map->metadata != backend_jm->info->metadata))
|
return -EINVAL;
|
|
kctx = backend_jm->kctx;
|
kbdev = backend_jm->kctx->kbdev;
|
|
lockdep_assert_held(&kbdev->hwaccess_lock);
|
|
kbase_hwcnt_gpu_enable_map_to_physical(&phys_enable_map, enable_map);
|
|
kbase_hwcnt_gpu_set_to_physical(&phys_counter_set,
|
backend_jm->info->counter_set);
|
|
enable.fe_bm = phys_enable_map.fe_bm;
|
enable.shader_bm = phys_enable_map.shader_bm;
|
enable.tiler_bm = phys_enable_map.tiler_bm;
|
enable.mmu_l2_bm = phys_enable_map.mmu_l2_bm;
|
enable.counter_set = phys_counter_set;
|
enable.dump_buffer = backend_jm->gpu_dump_va;
|
enable.dump_buffer_bytes = backend_jm->info->dump_bytes;
|
|
timestamp_ns = kbasep_hwcnt_backend_jm_timestamp_ns(backend);
|
|
/* Update the current configuration information. */
|
errcode = kbasep_hwcnt_gpu_update_curr_config(kbdev,
|
&backend_jm->curr_config);
|
if (errcode)
|
goto error;
|
|
errcode = kbase_instr_hwcnt_enable_internal(kbdev, kctx, &enable);
|
if (errcode)
|
goto error;
|
|
backend_jm->pm_core_mask = kbase_pm_ca_get_instr_core_mask(kbdev);
|
|
backend_jm->enabled = true;
|
|
kbasep_hwcnt_backend_jm_cc_enable(backend_jm, enable_map, timestamp_ns);
|
|
return 0;
|
error:
|
return errcode;
|
}
|
|
/* JM backend implementation of kbase_hwcnt_backend_dump_enable_fn */
|
static int kbasep_hwcnt_backend_jm_dump_enable(
|
struct kbase_hwcnt_backend *backend,
|
const struct kbase_hwcnt_enable_map *enable_map)
|
{
|
unsigned long flags;
|
int errcode;
|
struct kbase_hwcnt_backend_jm *backend_jm =
|
(struct kbase_hwcnt_backend_jm *)backend;
|
struct kbase_device *kbdev;
|
|
if (!backend_jm)
|
return -EINVAL;
|
|
kbdev = backend_jm->kctx->kbdev;
|
|
spin_lock_irqsave(&kbdev->hwaccess_lock, flags);
|
|
errcode = kbasep_hwcnt_backend_jm_dump_enable_nolock(
|
backend, enable_map);
|
|
spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
|
|
return errcode;
|
}
|
|
/* JM backend implementation of kbase_hwcnt_backend_dump_disable_fn */
|
static void kbasep_hwcnt_backend_jm_dump_disable(
|
struct kbase_hwcnt_backend *backend)
|
{
|
int errcode;
|
struct kbase_hwcnt_backend_jm *backend_jm =
|
(struct kbase_hwcnt_backend_jm *)backend;
|
|
if (WARN_ON(!backend_jm) || !backend_jm->enabled)
|
return;
|
|
kbasep_hwcnt_backend_jm_cc_disable(backend_jm);
|
|
errcode = kbase_instr_hwcnt_disable_internal(backend_jm->kctx);
|
WARN_ON(errcode);
|
|
backend_jm->enabled = false;
|
}
|
|
/* JM backend implementation of kbase_hwcnt_backend_dump_clear_fn */
|
static int kbasep_hwcnt_backend_jm_dump_clear(
|
struct kbase_hwcnt_backend *backend)
|
{
|
struct kbase_hwcnt_backend_jm *backend_jm =
|
(struct kbase_hwcnt_backend_jm *)backend;
|
|
if (!backend_jm || !backend_jm->enabled)
|
return -EINVAL;
|
|
return kbase_instr_hwcnt_clear(backend_jm->kctx);
|
}
|
|
/* JM backend implementation of kbase_hwcnt_backend_dump_request_fn */
|
static int kbasep_hwcnt_backend_jm_dump_request(
|
struct kbase_hwcnt_backend *backend,
|
u64 *dump_time_ns)
|
{
|
struct kbase_hwcnt_backend_jm *backend_jm =
|
(struct kbase_hwcnt_backend_jm *)backend;
|
struct kbase_device *kbdev;
|
const struct kbase_hwcnt_metadata *metadata;
|
u64 current_cycle_count;
|
size_t clk;
|
int ret;
|
|
if (!backend_jm || !backend_jm->enabled || !dump_time_ns)
|
return -EINVAL;
|
|
kbdev = backend_jm->kctx->kbdev;
|
metadata = backend_jm->info->metadata;
|
|
/* Disable pre-emption, to make the timestamp as accurate as possible */
|
preempt_disable();
|
{
|
*dump_time_ns = kbasep_hwcnt_backend_jm_timestamp_ns(backend);
|
ret = kbase_instr_hwcnt_request_dump(backend_jm->kctx);
|
|
kbase_hwcnt_metadata_for_each_clock(metadata, clk) {
|
if (!kbase_hwcnt_clk_enable_map_enabled(
|
backend_jm->clk_enable_map, clk))
|
continue;
|
|
if (clk == KBASE_CLOCK_DOMAIN_TOP) {
|
/* Read cycle count for top clock domain. */
|
kbase_backend_get_gpu_time_norequest(
|
kbdev, ¤t_cycle_count,
|
NULL, NULL);
|
} else {
|
/*
|
* Estimate cycle count for non-top clock
|
* domain.
|
*/
|
current_cycle_count = kbase_ccswe_cycle_at(
|
&backend_jm->ccswe_shader_cores,
|
*dump_time_ns);
|
}
|
backend_jm->cycle_count_elapsed[clk] =
|
current_cycle_count -
|
backend_jm->prev_cycle_count[clk];
|
|
/*
|
* Keep the current cycle count for later calculation.
|
*/
|
backend_jm->prev_cycle_count[clk] = current_cycle_count;
|
}
|
}
|
preempt_enable();
|
|
return ret;
|
}
|
|
/* JM backend implementation of kbase_hwcnt_backend_dump_wait_fn */
|
static int kbasep_hwcnt_backend_jm_dump_wait(
|
struct kbase_hwcnt_backend *backend)
|
{
|
struct kbase_hwcnt_backend_jm *backend_jm =
|
(struct kbase_hwcnt_backend_jm *)backend;
|
|
if (!backend_jm || !backend_jm->enabled)
|
return -EINVAL;
|
|
return kbase_instr_hwcnt_wait_for_dump(backend_jm->kctx);
|
}
|
|
/* JM backend implementation of kbase_hwcnt_backend_dump_get_fn */
|
static int kbasep_hwcnt_backend_jm_dump_get(
|
struct kbase_hwcnt_backend *backend,
|
struct kbase_hwcnt_dump_buffer *dst,
|
const struct kbase_hwcnt_enable_map *dst_enable_map,
|
bool accumulate)
|
{
|
struct kbase_hwcnt_backend_jm *backend_jm =
|
(struct kbase_hwcnt_backend_jm *)backend;
|
size_t clk;
|
|
if (!backend_jm || !dst || !dst_enable_map ||
|
(backend_jm->info->metadata != dst->metadata) ||
|
(dst_enable_map->metadata != dst->metadata))
|
return -EINVAL;
|
|
/* Invalidate the kernel buffer before reading from it. */
|
kbase_sync_mem_regions(
|
backend_jm->kctx, backend_jm->vmap, KBASE_SYNC_TO_CPU);
|
|
kbase_hwcnt_metadata_for_each_clock(dst_enable_map->metadata, clk) {
|
if (!kbase_hwcnt_clk_enable_map_enabled(
|
dst_enable_map->clk_enable_map, clk))
|
continue;
|
|
/* Extract elapsed cycle count for each clock domain. */
|
dst->clk_cnt_buf[clk] = backend_jm->cycle_count_elapsed[clk];
|
}
|
|
return kbase_hwcnt_jm_dump_get(dst, backend_jm->cpu_dump_va,
|
dst_enable_map, backend_jm->pm_core_mask,
|
&backend_jm->curr_config, accumulate);
|
}
|
|
/**
|
* kbasep_hwcnt_backend_jm_dump_alloc() - Allocate a GPU dump buffer.
|
* @info: Non-NULL pointer to JM backend info.
|
* @kctx: Non-NULL pointer to kbase context.
|
* @gpu_dump_va: Non-NULL pointer to where GPU dump buffer virtual address
|
* is stored on success.
|
*
|
* Return: 0 on success, else error code.
|
*/
|
static int kbasep_hwcnt_backend_jm_dump_alloc(
|
const struct kbase_hwcnt_backend_jm_info *info,
|
struct kbase_context *kctx,
|
u64 *gpu_dump_va)
|
{
|
struct kbase_va_region *reg;
|
u64 flags;
|
u64 nr_pages;
|
|
WARN_ON(!info);
|
WARN_ON(!kctx);
|
WARN_ON(!gpu_dump_va);
|
|
flags = BASE_MEM_PROT_CPU_RD |
|
BASE_MEM_PROT_GPU_WR |
|
BASEP_MEM_PERMANENT_KERNEL_MAPPING |
|
BASE_MEM_CACHED_CPU |
|
BASE_MEM_UNCACHED_GPU;
|
|
nr_pages = PFN_UP(info->dump_bytes);
|
|
reg = kbase_mem_alloc(kctx, nr_pages, nr_pages, 0, &flags, gpu_dump_va);
|
|
if (!reg)
|
return -ENOMEM;
|
|
return 0;
|
}
|
|
/**
|
* kbasep_hwcnt_backend_jm_dump_free() - Free an allocated GPU dump buffer.
|
* @kctx: Non-NULL pointer to kbase context.
|
* @gpu_dump_va: GPU dump buffer virtual address.
|
*/
|
static void kbasep_hwcnt_backend_jm_dump_free(
|
struct kbase_context *kctx,
|
u64 gpu_dump_va)
|
{
|
WARN_ON(!kctx);
|
if (gpu_dump_va)
|
kbase_mem_free(kctx, gpu_dump_va);
|
}
|
|
/**
|
* kbasep_hwcnt_backend_jm_destroy() - Destroy a JM backend.
|
* @backend: Pointer to JM backend to destroy.
|
*
|
* Can be safely called on a backend in any state of partial construction.
|
*/
|
static void kbasep_hwcnt_backend_jm_destroy(
|
struct kbase_hwcnt_backend_jm *backend)
|
{
|
if (!backend)
|
return;
|
|
if (backend->kctx) {
|
struct kbase_context *kctx = backend->kctx;
|
struct kbase_device *kbdev = kctx->kbdev;
|
|
if (backend->cpu_dump_va)
|
kbase_phy_alloc_mapping_put(kctx, backend->vmap);
|
|
if (backend->gpu_dump_va)
|
kbasep_hwcnt_backend_jm_dump_free(
|
kctx, backend->gpu_dump_va);
|
|
kbasep_js_release_privileged_ctx(kbdev, kctx);
|
kbase_destroy_context(kctx);
|
}
|
|
kfree(backend);
|
}
|
|
/**
|
* kbasep_hwcnt_backend_jm_create() - Create a JM backend.
|
* @info: Non-NULL pointer to backend info.
|
* @out_backend: Non-NULL pointer to where backend is stored on success.
|
*
|
* Return: 0 on success, else error code.
|
*/
|
static int kbasep_hwcnt_backend_jm_create(
|
const struct kbase_hwcnt_backend_jm_info *info,
|
struct kbase_hwcnt_backend_jm **out_backend)
|
{
|
int errcode;
|
struct kbase_device *kbdev;
|
struct kbase_hwcnt_backend_jm *backend = NULL;
|
|
WARN_ON(!info);
|
WARN_ON(!out_backend);
|
|
kbdev = info->kbdev;
|
|
backend = kzalloc(sizeof(*backend), GFP_KERNEL);
|
if (!backend)
|
goto alloc_error;
|
|
backend->info = info;
|
|
backend->kctx = kbase_create_context(kbdev, true,
|
BASE_CONTEXT_SYSTEM_MONITOR_SUBMIT_DISABLED, 0, NULL);
|
if (!backend->kctx)
|
goto alloc_error;
|
|
kbasep_js_schedule_privileged_ctx(kbdev, backend->kctx);
|
|
errcode = kbasep_hwcnt_backend_jm_dump_alloc(
|
info, backend->kctx, &backend->gpu_dump_va);
|
if (errcode)
|
goto error;
|
|
backend->cpu_dump_va = kbase_phy_alloc_mapping_get(backend->kctx,
|
backend->gpu_dump_va, &backend->vmap);
|
if (!backend->cpu_dump_va)
|
goto alloc_error;
|
|
kbase_ccswe_init(&backend->ccswe_shader_cores);
|
backend->rate_listener.notify = kbasep_hwcnt_backend_jm_on_freq_change;
|
|
|
*out_backend = backend;
|
return 0;
|
|
alloc_error:
|
errcode = -ENOMEM;
|
error:
|
kbasep_hwcnt_backend_jm_destroy(backend);
|
return errcode;
|
}
|
|
/* JM backend implementation of kbase_hwcnt_backend_metadata_fn */
|
static const struct kbase_hwcnt_metadata *
|
kbasep_hwcnt_backend_jm_metadata(const struct kbase_hwcnt_backend_info *info)
|
{
|
if (!info)
|
return NULL;
|
|
return ((const struct kbase_hwcnt_backend_jm_info *)info)->metadata;
|
}
|
|
/* JM backend implementation of kbase_hwcnt_backend_init_fn */
|
static int kbasep_hwcnt_backend_jm_init(
|
const struct kbase_hwcnt_backend_info *info,
|
struct kbase_hwcnt_backend **out_backend)
|
{
|
int errcode;
|
struct kbase_hwcnt_backend_jm *backend = NULL;
|
|
if (!info || !out_backend)
|
return -EINVAL;
|
|
errcode = kbasep_hwcnt_backend_jm_create(
|
(const struct kbase_hwcnt_backend_jm_info *) info, &backend);
|
if (errcode)
|
return errcode;
|
|
*out_backend = (struct kbase_hwcnt_backend *)backend;
|
|
return 0;
|
}
|
|
/* JM backend implementation of kbase_hwcnt_backend_term_fn */
|
static void kbasep_hwcnt_backend_jm_term(struct kbase_hwcnt_backend *backend)
|
{
|
if (!backend)
|
return;
|
|
kbasep_hwcnt_backend_jm_dump_disable(backend);
|
kbasep_hwcnt_backend_jm_destroy(
|
(struct kbase_hwcnt_backend_jm *)backend);
|
}
|
|
/**
|
* kbasep_hwcnt_backend_jm_info_destroy() - Destroy a JM backend info.
|
* @info: Pointer to info to destroy.
|
*
|
* Can be safely called on a backend info in any state of partial construction.
|
*/
|
static void kbasep_hwcnt_backend_jm_info_destroy(
|
const struct kbase_hwcnt_backend_jm_info *info)
|
{
|
if (!info)
|
return;
|
|
kbase_hwcnt_jm_metadata_destroy(info->metadata);
|
kfree(info);
|
}
|
|
/**
|
* kbasep_hwcnt_backend_jm_info_create() - Create a JM backend info.
|
* @kbdev: Non_NULL pointer to kbase device.
|
* @out_info: Non-NULL pointer to where info is stored on success.
|
*
|
* Return 0 on success, else error code.
|
*/
|
static int kbasep_hwcnt_backend_jm_info_create(
|
struct kbase_device *kbdev,
|
const struct kbase_hwcnt_backend_jm_info **out_info)
|
{
|
int errcode = -ENOMEM;
|
struct kbase_hwcnt_gpu_info hwcnt_gpu_info;
|
struct kbase_hwcnt_backend_jm_info *info = NULL;
|
|
WARN_ON(!kbdev);
|
WARN_ON(!out_info);
|
|
errcode = kbasep_hwcnt_backend_jm_gpu_info_init(kbdev, &hwcnt_gpu_info);
|
if (errcode)
|
return errcode;
|
|
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
if (!info)
|
goto error;
|
|
info->kbdev = kbdev;
|
|
#ifdef CONFIG_MALI_BIFROST_PRFCNT_SET_SECONDARY
|
info->counter_set = KBASE_HWCNT_SET_SECONDARY;
|
#elif defined(CONFIG_MALI_PRFCNT_SET_TERTIARY)
|
info->counter_set = KBASE_HWCNT_SET_TERTIARY;
|
#else
|
/* Default to primary */
|
info->counter_set = KBASE_HWCNT_SET_PRIMARY;
|
#endif
|
|
errcode = kbase_hwcnt_jm_metadata_create(&hwcnt_gpu_info,
|
info->counter_set,
|
&info->metadata,
|
&info->dump_bytes);
|
if (errcode)
|
goto error;
|
|
*out_info = info;
|
|
return 0;
|
error:
|
kbasep_hwcnt_backend_jm_info_destroy(info);
|
return errcode;
|
}
|
|
int kbase_hwcnt_backend_jm_create(
|
struct kbase_device *kbdev,
|
struct kbase_hwcnt_backend_interface *iface)
|
{
|
int errcode;
|
const struct kbase_hwcnt_backend_jm_info *info = NULL;
|
|
if (!kbdev || !iface)
|
return -EINVAL;
|
|
errcode = kbasep_hwcnt_backend_jm_info_create(kbdev, &info);
|
|
if (errcode)
|
return errcode;
|
|
iface->info = (struct kbase_hwcnt_backend_info *)info;
|
iface->metadata = kbasep_hwcnt_backend_jm_metadata;
|
iface->init = kbasep_hwcnt_backend_jm_init;
|
iface->term = kbasep_hwcnt_backend_jm_term;
|
iface->timestamp_ns = kbasep_hwcnt_backend_jm_timestamp_ns;
|
iface->dump_enable = kbasep_hwcnt_backend_jm_dump_enable;
|
iface->dump_enable_nolock = kbasep_hwcnt_backend_jm_dump_enable_nolock;
|
iface->dump_disable = kbasep_hwcnt_backend_jm_dump_disable;
|
iface->dump_clear = kbasep_hwcnt_backend_jm_dump_clear;
|
iface->dump_request = kbasep_hwcnt_backend_jm_dump_request;
|
iface->dump_wait = kbasep_hwcnt_backend_jm_dump_wait;
|
iface->dump_get = kbasep_hwcnt_backend_jm_dump_get;
|
|
return 0;
|
}
|
|
void kbase_hwcnt_backend_jm_destroy(
|
struct kbase_hwcnt_backend_interface *iface)
|
{
|
if (!iface)
|
return;
|
|
kbasep_hwcnt_backend_jm_info_destroy(
|
(const struct kbase_hwcnt_backend_jm_info *)iface->info);
|
memset(iface, 0, sizeof(*iface));
|
}
|