Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

View file

@ -0,0 +1,39 @@
#
# (C) COPYRIGHT 2012-2013 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 licence.
#
# A copy of the licence is included with the program, and can also be obtained
# from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
#
#
menuconfig MALI_T72X
tristate "Mali-T72X support"
default n
help
Enable this option to build support for the ARM Mali-T72x GPU.
To compile this driver as a module, choose M here:
this will generate a single module, called mali_kbase.
choice
prompt "version Configuration"
depends on MALI_T72X
default MALI_R5P0_REL
help
Select the gpu support version.
config MALI_R7P0_T72X
depends on MALI_T72X
bool "r7p0 driver"
endchoice
if MALI_R7P0_T72X
source "drivers/gpu/arm/t72x/r7p0/Kconfig"
endif

View file

@ -0,0 +1,193 @@
#
# (C) COPYRIGHT 2012,2014 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 licence.
#
# A copy of the licence is included with the program, and can also be obtained
# from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
#
#
# Driver version string which is returned to userspace via an ioctl
MALI_RELEASE_NAME ?= "r7p0-03rel0"
# Paths required for build
KBASE_PATH = $(src)
KBASE_PLATFORM_PATH = $(KBASE_PATH)/platform_dummy
UMP_PATH = $(src)/../../../base
ifeq ($(CONFIG_MALI_ERROR_INJECTION),y)
MALI_ERROR_INJECT_ON = 1
endif
# Set up defaults if not defined by build system
MALI_CUSTOMER_RELEASE ?= 1
MALI_UNIT_TEST ?= 0
MALI_KERNEL_TEST_API ?= 0
MALI_ERROR_INJECT_ON ?= 0
MALI_MOCK_TEST ?= 0
MALI_COVERAGE ?= 0
MALI_INSTRUMENTATION_LEVEL ?= 0
# MALI_SEC_SECURE_RENDERING
MALI_SUPPORT_ASP_SECURE ?= 0
# This workaround is for what seems to be a compiler bug we observed in
# GCC 4.7 on AOSP 4.3. The bug caused an intermittent failure compiling
# the "_Pragma" syntax, where an error message is returned:
#
# "internal compiler error: unspellable token PRAGMA"
#
# This regression has thus far only been seen on the GCC 4.7 compiler bundled
# with AOSP 4.3.0. So this makefile, intended for in-tree kernel builds
# which are not known to be used with AOSP, is hardcoded to disable the
# workaround, i.e. set the define to 0.
MALI_GCC_WORKAROUND_MIDCOM_4598 ?= 0
# Set up our defines, which will be passed to gcc
DEFINES = \
-DMALI_CUSTOMER_RELEASE=$(MALI_CUSTOMER_RELEASE) \
-DMALI_KERNEL_TEST_API=$(MALI_KERNEL_TEST_API) \
-DMALI_UNIT_TEST=$(MALI_UNIT_TEST) \
-DMALI_ERROR_INJECT_ON=$(MALI_ERROR_INJECT_ON) \
-DMALI_MOCK_TEST=$(MALI_MOCK_TEST) \
-DMALI_COVERAGE=$(MALI_COVERAGE) \
-DMALI_INSTRUMENTATION_LEVEL=$(MALI_INSTRUMENTATION_LEVEL) \
-DMALI_RELEASE_NAME=\"$(MALI_RELEASE_NAME)\" \
-DMALI_GCC_WORKAROUND_MIDCOM_4598=$(MALI_GCC_WORKAROUND_MIDCOM_4598) \
-DMALI_SEC_CL_BOOST \
-DMALI_SEC_FENCE_INTEGRATION \
-DMALI_SEC_UTILIZATION \
-DKBASE_FENCE_TIMEOUT_FAKE_SIGNAL \
-DMALI_SEC_ASP_SECURE_RENDERING=$(MALI_SUPPORT_ASP_SECURE)
ifeq ($(CONFIG_MALI_GATOR_SUPPORT_TIMELINE_T72X),y)
DEFINES +=-DCONFIG_MALI_GATOR_SUPPORT
endif
ifeq ($(KBUILD_EXTMOD),)
# in-tree
DEFINES +=-DMALI_KBASE_THIRDPARTY_PATH=../../$(src)/platform/$(CONFIG_MALI_PLATFORM_THIRDPARTY_NAME)
else
# out-of-tree
DEFINES +=-DMALI_KBASE_THIRDPARTY_PATH=$(src)/platform/$(CONFIG_MALI_PLATFORM_THIRDPARTY_NAME)
endif
DEFINES += -I$(srctree)/drivers/staging/android
ifeq ($(CONFIG_MALI_SEC_HWCNT),y)
DEFINES +=-DMALI_SEC_HWCNT
endif
DEFINES += -I$(srctree)/drivers/soc/samsung/pwrcal
# Use our defines when compiling
ccflags-y += $(DEFINES) -I$(KBASE_PATH) -I$(KBASE_PLATFORM_PATH) -I$(UMP_PATH) -I$(srctree)/include/linux
subdir-ccflags-y += $(DEFINES) -I$(KBASE_PATH) -I$(KBASE_PLATFORM_PATH) -I$(OSK_PATH) -I$(UMP_PATH) -I$(srctree)/include/linux
SRC := \
mali_kbase_device.c \
mali_kbase_cache_policy.c \
mali_kbase_mem.c \
mali_kbase_mmu.c \
mali_kbase_ipa.c \
mali_kbase_jd.c \
mali_kbase_jd_debugfs.c \
mali_kbase_jm.c \
mali_kbase_gpuprops.c \
mali_kbase_js.c \
mali_kbase_js_ctx_attr.c \
mali_kbase_event.c \
mali_kbase_context.c \
mali_kbase_pm.c \
mali_kbase_config.c \
mali_kbase_security.c \
mali_kbase_instr.c \
mali_kbase_vinstr.c \
mali_kbase_softjobs.c \
mali_kbase_10969_workaround.c \
mali_kbase_hw.c \
mali_kbase_utility.c \
mali_kbase_debug.c \
mali_kbase_trace_timeline.c \
mali_kbase_gpu_memory_debugfs.c \
mali_kbase_mem_linux.c \
mali_kbase_core_linux.c \
mali_kbase_sync.c \
mali_kbase_sync_user.c \
mali_kbase_replay.c \
mali_kbase_mem_profile_debugfs.c \
mali_kbase_mmu_mode_lpae.c \
mali_kbase_disjoint_events.c \
mali_kbase_gator_api.c \
mali_kbase_debug_mem_view.c \
mali_kbase_debug_job_fault.c \
mali_kbase_smc.c \
mali_kbase_mem_pool.c \
mali_kbase_mem_pool_debugfs.c
ifeq ($(CONFIG_MALI_MIPE_ENABLED_T72X),y)
SRC += mali_kbase_tlstream.c
ifeq ($(MALI_UNIT_TEST),1)
SRC += mali_kbase_tlstream_test.c
endif
endif
# Job Scheduler Policy: Completely Fair Scheduler
SRC += mali_kbase_js_policy_cfs.c
ccflags-y += -I$(KBASE_PATH)
ifeq ($(CONFIG_MALI_PLATFORM_THIRDPARTY),y)
# remove begin and end quotes from the Kconfig string type
platform_name := $(shell echo $(CONFIG_MALI_PLATFORM_THIRDPARTY_NAME))
MALI_PLATFORM_THIRDPARTY_DIR := platform/exynos
ccflags-y += -I$(src)/$(MALI_PLATFORM_THIRDPARTY_DIR)
ifeq ($(CONFIG_MALI_R7P0_T72X),m)
include $(src)/platform/$(platform_name)/Kbuild
else ifeq ($(CONFIG_MALI_R7P0_T72X),y)
obj-$(CONFIG_MALI_R7P0_T72X) += platform/
endif
endif
ifeq ($(CONFIG_MALI_PLATFORM_DEVICETREE),y)
SRC += platform/devicetree/mali_kbase_runtime_pm.c
SRC += platform/devicetree/mali_kbase_config_devicetree.c
ccflags-y += -I$(src)/platform/devicetree
endif
# Tell the Linux build system from which .o file to create the kernel module
obj-$(CONFIG_MALI_R7P0_T72X) += mali_kbase.o
# Tell the Linux build system to enable building of our .c files
mali_kbase-y := $(SRC:.c=.o)
ifneq ($(wildcard $(src)/internal/Kbuild),)
ifeq ($(MALI_CUSTOMER_RELEASE),0)
# This include may set MALI_BACKEND_PATH and CONFIG_MALI_BACKEND_REAL
include $(src)/internal/Kbuild
mali_kbase-y += $(INTERNAL:.c=.o)
endif
endif
MALI_BACKEND_PATH ?= backend
CONFIG_MALI_BACKEND ?= gpu
CONFIG_MALI_BACKEND_REAL ?= $(CONFIG_MALI_BACKEND)
ifeq ($(MALI_MOCK_TEST),1)
ifeq ($(CONFIG_MALI_BACKEND_REAL),gpu)
# Test functionality
mali_kbase-y += tests/internal/src/mock/mali_kbase_pm_driver_mock.o
endif
endif
include $(src)/$(MALI_BACKEND_PATH)/$(CONFIG_MALI_BACKEND_REAL)/Kbuild
mali_kbase-y += $(BACKEND:.c=.o)
ccflags-y += -I$(src)/$(MALI_BACKEND_PATH)/$(CONFIG_MALI_BACKEND_REAL)
subdir-ccflags-y += -I$(src)/$(MALI_BACKEND_PATH)/$(CONFIG_MALI_BACKEND_REAL)

View file

@ -0,0 +1,216 @@
#
# (C) COPYRIGHT 2012-2015 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 licence.
#
# A copy of the licence is included with the program, and can also be obtained
# from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
#
#
choice
prompt "Streamline support"
depends on MALI_R7P0_T72X
default MALI_TIMELINE_DISABLED_T72X
help
Select streamline support configuration.
config MALI_TIMELINE_DISABLED_T72X
bool "Streamline support disabled"
depends on MALI_R7P0_T72X
help
Disable support for ARM Streamline Performance Analyzer.
Timeline support will not be included in
kernel code.
Debug stream will not be generated.
config MALI_GATOR_SUPPORT_TIMELINE_T72X
bool "Streamline support via Gator"
depends on MALI_R7P0_T72X
help
Adds diagnostic support for use with the ARM Streamline Performance Analyzer.
You will need the Gator device driver already loaded before loading this driver when enabling
Streamline debug support.
config MALI_MIPE_ENABLED_T72X
bool "Streamline support via MIPE"
depends on MALI_R7P0_T72X
help
Adds diagnostic support for use with the ARM Streamline Performance Analyzer.
Stream will be transmitted directly to Mali GPU library.
Compatible version of the library is required to read debug stream generated by kernel.
endchoice
# { SRUK-MALI_SYSTRACE_SUPPORT
config MALI_SYSTRACE_SUPPORT
bool "systrace Debug support"
depends on MALI_T72X
default y
help
Enable systrace marker in kernel
# SRUK-MALI_SYSTRACE_SUPPORT }
config MALI_DVFS
bool "Enable DVFS"
depends on MALI_T72X
default n
help
Choose this option to enable DVFS in the Mali Midgard DDK.
config MALI_DVFS_USER
bool "Enable DVFS USER"
depends on MALI_T72X && MALI_DVFS
default n
help
Choose this option to enable DVFS_USER on MALI t8xx DDK.
config MALI_RT_PM
bool "Enable Runtime power management"
depends on MALI_T72X
depends on PM_RUNTIME
default y
help
Choose this option to enable runtime power management in the Mali Midgard DDK.
config MALI_MIDGARD_ENABLE_TRACE
bool "Enable kbase tracing"
depends on MALI_T72X
default n
help
Enables tracing in kbase. Trace log available through
the "mali_trace" debugfs file, when the CONFIG_DEBUG_FS is enabled
config MALI_EXYNOS_TRACE
bool "Enable kbase tracing"
depends on MALI_T72X
default y
help
Enables tracing in kbase. Trace log available through
the "mali_trace" debugfs file, when the CONFIG_DEBUG_FS is enabled
config MALI_DEBUG_SYS
bool "Enable sysfs for the Mali Midgard DDK "
depends on MALI_T72X && SYSFS
default n
help
Enables sysfs for the Mali Midgard DDK. Set/Monitor the Mali Midgard DDK
config MALI_DEVFREQ
bool "devfreq support for Mali"
depends on MALI_T72X && PM_DEVFREQ
help
Support devfreq for Mali.
Using the devfreq framework and, by default, the simpleondemand
governor, the frequency of Mali will be dynamically selected from the
available OPPs.
# MALI_EXPERT configuration options
menuconfig MALI_EXPERT
depends on MALI_T72X
bool "Enable Expert Settings"
default n
help
Enabling this option and modifying the default settings may produce a driver with performance or
other limitations.
config MALI_DEBUG_SHADER_SPLIT_FS
bool "Allow mapping of shader cores via sysfs"
depends on MALI_T72X && MALI_DEBUG_SYS && MALI_EXPERT
default n
help
Select this option to provide a sysfs entry for runtime configuration of shader
core affinity masks.
config MALI_PLATFORM_FAKE
bool "Enable fake platform device support"
depends on MALI_T72X && MALI_EXPERT
default n
help
When you start to work with the Mali Midgard series device driver the platform-specific code of
the Linux kernel for your platform may not be complete. In this situation the kernel device driver
supports creating the platform device outside of the Linux platform-specific code.
Enable this option if would like to use a platform device configuration from within the device driver.
config MALI_PLATFORM_THIRDPARTY
depends on MALI_T72X
bool "Third Party Platform"
default y
help
Select the SOC platform that contains a Mali-T8XX
config MALI_PLATFORM_THIRDPARTY_NAME
depends on MALI_T72X && MALI_PLATFORM_THIRDPARTY && MALI_EXPERT
string "Third party platform name"
help
Enter the name of a third party platform that is supported. The third part configuration
file must be in midgard/config/tpip/mali_kbase_config_xxx.c where xxx is the name
specified here.
config MALI_DEBUG
bool "Debug build"
depends on MALI_T72X && MALI_EXPERT
default n
help
Select this option for increased checking and reporting of errors.
config MALI_NO_MALI
bool "No Mali"
depends on MALI_T72X && MALI_EXPERT
default n
help
This can be used to test the driver in a simulated environment
whereby the hardware is not physically present. If the hardware is physically
present it will not be used. This can be used to test the majority of the
driver without needing actual hardware or for software benchmarking.
All calls to the simulated hardware will complete immediately as if the hardware
completed the task.
config MALI_ERROR_INJECT
bool "Error injection"
depends on MALI_T72X && MALI_EXPERT && MALI_NO_MALI
default n
help
Enables insertion of errors to test module failure and recovery mechanisms.
config MALI_TRACE_TIMELINE
bool "Timeline tracing"
depends on MALI_T72X && MALI_EXPERT
default n
help
Enables timeline tracing through the kernel tracepoint system.
config MALI_SYSTEM_TRACE
bool "Enable system event tracing support"
depends on MALI_T72X && MALI_EXPERT
default n
help
Choose this option to enable system trace events for each
kbase event. This is typically used for debugging but has
minimal overhead when not in use. Enable only if you know what
you are doing.
config MALI_GPU_TRACEPOINTS
bool "Enable GPU tracepoints"
depends on MALI_T72X && ANDROID
select GPU_TRACEPOINTS
help
Enables GPU tracepoints using Android trace event definitions.
config MALI_SEC_HWCNT
bool "Enable sec hwcnt feature"
depends on MALI_T72X
default n
help
Enable sec hwcnt feature.
source "drivers/gpu/arm/t8xx/r7p0/platform/Kconfig"

View file

@ -0,0 +1,46 @@
#
# (C) COPYRIGHT 2010-2015 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 licence.
#
# A copy of the licence is included with the program, and can also be obtained
# from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
#
#
KDIR ?= /lib/modules/$(shell uname -r)/build
BUSLOG_PATH_RELATIVE = $(CURDIR)/../../../..
UMP_PATH_RELATIVE = $(CURDIR)/../../../base/ump
KBASE_PATH_RELATIVE = $(CURDIR)
KDS_PATH_RELATIVE = $(CURDIR)/../../../..
EXTRA_SYMBOLS = $(UMP_PATH_RELATIVE)/src/Module.symvers
ifeq ($(MALI_UNIT_TEST), 1)
EXTRA_SYMBOLS += $(KBASE_PATH_RELATIVE)/tests/internal/src/kernel_assert_module/linux/Module.symvers
endif
ifneq ($(wildcard $(CURDIR)/internal/Makefile.in),)
include $(CURDIR)/internal/Makefile.in
endif
ifeq ($(MALI_BUS_LOG), 1)
#Add bus logger symbols
EXTRA_SYMBOLS += $(BUSLOG_PATH_RELATIVE)/drivers/base/bus_logger/Module.symvers
endif
# GPL driver supports KDS
EXTRA_SYMBOLS += $(KDS_PATH_RELATIVE)/drivers/base/kds/Module.symvers
# we get the symbols from modules using KBUILD_EXTRA_SYMBOLS to prevent warnings about unknown functions
all:
$(MAKE) -C $(KDIR) M=$(CURDIR) EXTRA_CFLAGS="-I$(CURDIR)/../../../../include -I$(CURDIR)/../../../../tests/include $(SCONS_CFLAGS)" $(SCONS_CONFIGS) KBUILD_EXTRA_SYMBOLS="$(EXTRA_SYMBOLS)" modules
clean:
$(MAKE) -C $(KDIR) M=$(CURDIR) clean

View file

@ -0,0 +1,58 @@
#
# (C) COPYRIGHT 2014 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 licence.
#
# A copy of the licence is included with the program, and can also be obtained
# from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
#
#
BACKEND += \
backend/gpu/mali_kbase_cache_policy_backend.c \
backend/gpu/mali_kbase_device_hw.c \
backend/gpu/mali_kbase_gpu.c \
backend/gpu/mali_kbase_gpuprops_backend.c \
backend/gpu/mali_kbase_debug_job_fault_backend.c \
backend/gpu/mali_kbase_irq_linux.c \
backend/gpu/mali_kbase_instr_backend.c \
backend/gpu/mali_kbase_jm_as.c \
backend/gpu/mali_kbase_jm_hw.c \
backend/gpu/mali_kbase_jm_rb.c \
backend/gpu/mali_kbase_js_affinity.c \
backend/gpu/mali_kbase_js_backend.c \
backend/gpu/mali_kbase_mmu_hw_direct.c \
backend/gpu/mali_kbase_pm_backend.c \
backend/gpu/mali_kbase_pm_driver.c \
backend/gpu/mali_kbase_pm_metrics.c \
backend/gpu/mali_kbase_pm_ca.c \
backend/gpu/mali_kbase_pm_ca_fixed.c \
backend/gpu/mali_kbase_pm_always_on.c \
backend/gpu/mali_kbase_pm_coarse_demand.c \
backend/gpu/mali_kbase_pm_demand.c \
backend/gpu/mali_kbase_pm_policy.c \
backend/gpu/mali_kbase_time.c
ifeq ($(MALI_CUSTOMER_RELEASE),0)
BACKEND += \
backend/gpu/mali_kbase_pm_ca_random.c \
backend/gpu/mali_kbase_pm_demand_always_powered.c \
backend/gpu/mali_kbase_pm_fast_start.c
endif
ifeq ($(CONFIG_MALI_DEVFREQ),y)
BACKEND += backend/gpu/mali_kbase_devfreq.c
endif
ifeq ($(CONFIG_MALI_NO_MALI),y)
# Dummy model
BACKEND += backend/gpu/mali_kbase_model_dummy.c
BACKEND += backend/gpu/mali_kbase_model_linux.c
# HW error simulation
BACKEND += backend/gpu/mali_kbase_model_error_generator.c
endif

View file

@ -0,0 +1,29 @@
/*
*
* (C) COPYRIGHT 2014-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Backend specific configuration
*/
#ifndef _KBASE_BACKEND_CONFIG_H_
#define _KBASE_BACKEND_CONFIG_H_
/* Enable GPU reset API */
#define KBASE_GPU_RESET_EN 1
#endif /* _KBASE_BACKEND_CONFIG_H_ */

View file

@ -0,0 +1,22 @@
/*
*
* (C) COPYRIGHT 2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include "backend/gpu/mali_kbase_cache_policy_backend.h"
#include <backend/gpu/mali_kbase_pm_internal.h>
#include <backend/gpu/mali_kbase_device_internal.h>

View file

@ -0,0 +1,26 @@
/*
*
* (C) COPYRIGHT 2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#ifndef _KBASE_CACHE_POLICY_BACKEND_H_
#define _KBASE_CACHE_POLICY_BACKEND_H_
#include "mali_kbase.h"
#include "mali_base_kernel.h"
#endif /* _KBASE_CACHE_POLICY_H_ */

View file

@ -0,0 +1,157 @@
/*
*
* (C) COPYRIGHT 2012-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include <mali_kbase.h>
#include <backend/gpu/mali_kbase_device_internal.h>
#include "mali_kbase_debug_job_fault.h"
#ifdef CONFIG_DEBUG_FS
/*GPU_CONTROL_REG(r)*/
static int gpu_control_reg_snapshot[] = {
GPU_ID,
SHADER_READY_LO,
SHADER_READY_HI,
TILER_READY_LO,
TILER_READY_HI,
L2_READY_LO,
L2_READY_HI
};
/* JOB_CONTROL_REG(r) */
static int job_control_reg_snapshot[] = {
JOB_IRQ_MASK,
JOB_IRQ_STATUS
};
/* JOB_SLOT_REG(n,r) */
static int job_slot_reg_snapshot[] = {
JS_HEAD_LO,
JS_HEAD_HI,
JS_TAIL_LO,
JS_TAIL_HI,
JS_AFFINITY_LO,
JS_AFFINITY_HI,
JS_CONFIG,
JS_STATUS,
JS_HEAD_NEXT_LO,
JS_HEAD_NEXT_HI,
JS_AFFINITY_NEXT_LO,
JS_AFFINITY_NEXT_HI,
JS_CONFIG_NEXT
};
/*MMU_REG(r)*/
static int mmu_reg_snapshot[] = {
MMU_IRQ_MASK,
MMU_IRQ_STATUS
};
/* MMU_AS_REG(n,r) */
static int as_reg_snapshot[] = {
AS_TRANSTAB_LO,
AS_TRANSTAB_HI,
AS_MEMATTR_LO,
AS_MEMATTR_HI,
AS_FAULTSTATUS,
AS_FAULTADDRESS_LO,
AS_FAULTADDRESS_HI,
AS_STATUS
};
bool kbase_debug_job_fault_reg_snapshot_init(struct kbase_context *kctx,
int reg_range)
{
int i, j;
int offset = 0;
int slot_number;
int as_number;
if (kctx->reg_dump == NULL)
return false;
slot_number = kctx->kbdev->gpu_props.num_job_slots;
as_number = kctx->kbdev->gpu_props.num_address_spaces;
/* get the GPU control registers*/
for (i = 0; i < sizeof(gpu_control_reg_snapshot)/4; i++) {
kctx->reg_dump[offset] =
GPU_CONTROL_REG(gpu_control_reg_snapshot[i]);
offset += 2;
}
/* get the Job control registers*/
for (i = 0; i < sizeof(job_control_reg_snapshot)/4; i++) {
kctx->reg_dump[offset] =
JOB_CONTROL_REG(job_control_reg_snapshot[i]);
offset += 2;
}
/* get the Job Slot registers*/
for (j = 0; j < slot_number; j++) {
for (i = 0; i < sizeof(job_slot_reg_snapshot)/4; i++) {
kctx->reg_dump[offset] =
JOB_SLOT_REG(j, job_slot_reg_snapshot[i]);
offset += 2;
}
}
/* get the MMU registers*/
for (i = 0; i < sizeof(mmu_reg_snapshot)/4; i++) {
kctx->reg_dump[offset] = MMU_REG(mmu_reg_snapshot[i]);
offset += 2;
}
/* get the Address space registers*/
for (j = 0; j < as_number; j++) {
for (i = 0; i < sizeof(as_reg_snapshot)/4; i++) {
kctx->reg_dump[offset] =
MMU_AS_REG(j, as_reg_snapshot[i]);
offset += 2;
}
}
WARN_ON(offset >= (reg_range*2/4));
/* set the termination flag*/
kctx->reg_dump[offset] = REGISTER_DUMP_TERMINATION_FLAG;
kctx->reg_dump[offset + 1] = REGISTER_DUMP_TERMINATION_FLAG;
dev_dbg(kctx->kbdev->dev, "kbase_job_fault_reg_snapshot_init:%d\n",
offset);
return true;
}
bool kbase_job_fault_get_reg_snapshot(struct kbase_context *kctx)
{
int offset = 0;
if (kctx->reg_dump == NULL)
return false;
while (kctx->reg_dump[offset] != REGISTER_DUMP_TERMINATION_FLAG) {
kctx->reg_dump[offset+1] =
kbase_reg_read(kctx->kbdev,
kctx->reg_dump[offset], NULL);
offset += 2;
}
return true;
}
#endif

View file

@ -0,0 +1,284 @@
/*
*
* (C) COPYRIGHT 2014 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include <mali_kbase.h>
#include <mali_kbase_config_defaults.h>
#include <backend/gpu/mali_kbase_pm_internal.h>
#include <linux/clk.h>
#include <linux/devfreq.h>
#ifdef CONFIG_DEVFREQ_THERMAL
#include <linux/devfreq_cooling.h>
#endif
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)
#include <linux/pm_opp.h>
#else /* Linux >= 3.13 */
/* In 3.13 the OPP include header file, types, and functions were all
* renamed. Use the old filename for the include, and define the new names to
* the old, when an old kernel is detected.
*/
#include <linux/opp.h>
#define dev_pm_opp opp
#define dev_pm_opp_get_voltage opp_get_voltage
#define dev_pm_opp_get_opp_count opp_get_opp_count
#define dev_pm_opp_find_freq_ceil opp_find_freq_ceil
#endif /* Linux >= 3.13 */
static int
kbase_devfreq_target(struct device *dev, unsigned long *target_freq, u32 flags)
{
struct kbase_device *kbdev = dev_get_drvdata(dev);
struct dev_pm_opp *opp;
unsigned long freq = 0;
unsigned long voltage;
int err;
freq = *target_freq;
rcu_read_lock();
opp = devfreq_recommended_opp(dev, &freq, flags);
voltage = dev_pm_opp_get_voltage(opp);
rcu_read_unlock();
if (IS_ERR_OR_NULL(opp)) {
dev_err(dev, "Failed to get opp (%ld)\n", PTR_ERR(opp));
return PTR_ERR(opp);
}
/*
* Only update if there is a change of frequency
*/
if (kbdev->current_freq == freq) {
*target_freq = freq;
return 0;
}
#ifdef CONFIG_REGULATOR
if (kbdev->regulator && kbdev->current_voltage != voltage
&& kbdev->current_freq < freq) {
err = regulator_set_voltage(kbdev->regulator, voltage, voltage);
if (err) {
dev_err(dev, "Failed to increase voltage (%d)\n", err);
return err;
}
}
#endif
err = clk_set_rate(kbdev->clock, freq);
if (err) {
dev_err(dev, "Failed to set clock %lu (target %lu)\n",
freq, *target_freq);
return err;
}
#ifdef CONFIG_REGULATOR
if (kbdev->regulator && kbdev->current_voltage != voltage
&& kbdev->current_freq > freq) {
err = regulator_set_voltage(kbdev->regulator, voltage, voltage);
if (err) {
dev_err(dev, "Failed to decrease voltage (%d)\n", err);
return err;
}
}
#endif
*target_freq = freq;
kbdev->current_voltage = voltage;
kbdev->current_freq = freq;
kbase_pm_reset_dvfs_utilisation(kbdev);
return err;
}
static int
kbase_devfreq_cur_freq(struct device *dev, unsigned long *freq)
{
struct kbase_device *kbdev = dev_get_drvdata(dev);
*freq = kbdev->current_freq;
return 0;
}
static int
kbase_devfreq_status(struct device *dev, struct devfreq_dev_status *stat)
{
struct kbase_device *kbdev = dev_get_drvdata(dev);
stat->current_frequency = kbdev->current_freq;
kbase_pm_get_dvfs_utilisation(kbdev,
&stat->total_time, &stat->busy_time);
stat->private_data = NULL;
#ifdef CONFIG_DEVFREQ_THERMAL
memcpy(&kbdev->devfreq_cooling->last_status, stat, sizeof(*stat));
#endif
return 0;
}
static int kbase_devfreq_init_freq_table(struct kbase_device *kbdev,
struct devfreq_dev_profile *dp)
{
int count;
int i = 0;
unsigned long freq = 0;
struct dev_pm_opp *opp;
rcu_read_lock();
count = dev_pm_opp_get_opp_count(kbdev->dev);
if (count < 0) {
rcu_read_unlock();
return count;
}
rcu_read_unlock();
dp->freq_table = kmalloc_array(count, sizeof(dp->freq_table[0]),
GFP_KERNEL);
if (!dp->freq_table)
return -ENOMEM;
rcu_read_lock();
for (i = 0; i < count; i++, freq++) {
opp = dev_pm_opp_find_freq_ceil(kbdev->dev, &freq);
if (IS_ERR(opp))
break;
dp->freq_table[i] = freq;
}
rcu_read_unlock();
if (count != i)
dev_warn(kbdev->dev, "Unable to enumerate all OPPs (%d!=%d\n",
count, i);
dp->max_state = i;
return 0;
}
static void kbase_devfreq_term_freq_table(struct kbase_device *kbdev)
{
struct devfreq_dev_profile *dp = kbdev->devfreq->profile;
kfree(dp->freq_table);
}
static void kbase_devfreq_exit(struct device *dev)
{
struct kbase_device *kbdev = dev_get_drvdata(dev);
kbase_devfreq_term_freq_table(kbdev);
}
int kbase_devfreq_init(struct kbase_device *kbdev)
{
#ifdef CONFIG_DEVFREQ_THERMAL
struct devfreq_cooling_ops *callbacks = POWER_MODEL_CALLBACKS;
#endif
struct devfreq_dev_profile *dp;
int err;
dev_dbg(kbdev->dev, "Init Mali devfreq\n");
if (!kbdev->clock)
return -ENODEV;
kbdev->current_freq = clk_get_rate(kbdev->clock);
dp = &kbdev->devfreq_profile;
dp->initial_freq = kbdev->current_freq;
dp->polling_ms = 100;
dp->target = kbase_devfreq_target;
dp->get_dev_status = kbase_devfreq_status;
dp->get_cur_freq = kbase_devfreq_cur_freq;
dp->exit = kbase_devfreq_exit;
if (kbase_devfreq_init_freq_table(kbdev, dp))
return -EFAULT;
kbdev->devfreq = devfreq_add_device(kbdev->dev, dp,
"simple_ondemand", NULL);
if (IS_ERR(kbdev->devfreq)) {
kbase_devfreq_term_freq_table(kbdev);
return PTR_ERR(kbdev->devfreq);
}
err = devfreq_register_opp_notifier(kbdev->dev, kbdev->devfreq);
if (err) {
dev_err(kbdev->dev,
"Failed to register OPP notifier (%d)\n", err);
goto opp_notifier_failed;
}
#ifdef CONFIG_DEVFREQ_THERMAL
if (callbacks) {
kbdev->devfreq_cooling = of_devfreq_cooling_register_power(
kbdev->dev->of_node,
kbdev->devfreq,
callbacks);
if (IS_ERR_OR_NULL(kbdev->devfreq_cooling)) {
err = PTR_ERR(kbdev->devfreq_cooling);
dev_err(kbdev->dev,
"Failed to register cooling device (%d)\n",
err);
goto cooling_failed;
}
}
#endif
return 0;
#ifdef CONFIG_DEVFREQ_THERMAL
cooling_failed:
devfreq_unregister_opp_notifier(kbdev->dev, kbdev->devfreq);
#endif /* CONFIG_DEVFREQ_THERMAL */
opp_notifier_failed:
err = devfreq_remove_device(kbdev->devfreq);
if (err)
dev_err(kbdev->dev, "Failed to terminate devfreq (%d)\n", err);
else
kbdev->devfreq = NULL;
return err;
}
void kbase_devfreq_term(struct kbase_device *kbdev)
{
int err;
dev_dbg(kbdev->dev, "Term Mali devfreq\n");
#ifdef CONFIG_DEVFREQ_THERMAL
devfreq_cooling_unregister(kbdev->devfreq_cooling);
#endif
devfreq_unregister_opp_notifier(kbdev->dev, kbdev->devfreq);
err = devfreq_remove_device(kbdev->devfreq);
if (err)
dev_err(kbdev->dev, "Failed to terminate devfreq (%d)\n", err);
else
kbdev->devfreq = NULL;
}

View file

@ -0,0 +1,24 @@
/*
*
* (C) COPYRIGHT 2014 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#ifndef _BASE_DEVFREQ_H_
#define _BASE_DEVFREQ_H_
int kbase_devfreq_init(struct kbase_device *kbdev);
void kbase_devfreq_term(struct kbase_device *kbdev);
#endif /* _BASE_DEVFREQ_H_ */

View file

@ -0,0 +1,116 @@
/*
*
* (C) COPYRIGHT 2014-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
*
*/
#include <mali_kbase.h>
#include <backend/gpu/mali_kbase_instr_internal.h>
#include <backend/gpu/mali_kbase_pm_internal.h>
#include <backend/gpu/mali_kbase_device_internal.h>
#if !defined(CONFIG_MALI_NO_MALI)
void kbase_reg_write(struct kbase_device *kbdev, u16 offset, u32 value,
struct kbase_context *kctx)
{
KBASE_DEBUG_ASSERT(kbdev->pm.backend.gpu_powered);
KBASE_DEBUG_ASSERT(kctx == NULL || kctx->as_nr != KBASEP_AS_NR_INVALID);
KBASE_DEBUG_ASSERT(kbdev->dev != NULL);
dev_dbg(kbdev->dev, "w: reg %04x val %08x", offset, value);
writel(value, kbdev->reg + offset);
if (kctx && kctx->jctx.tb)
kbase_device_trace_register_access(kctx, REG_WRITE, offset,
value);
}
KBASE_EXPORT_TEST_API(kbase_reg_write);
u32 kbase_reg_read(struct kbase_device *kbdev, u16 offset,
struct kbase_context *kctx)
{
u32 val;
KBASE_DEBUG_ASSERT(kbdev->pm.backend.gpu_powered);
KBASE_DEBUG_ASSERT(kctx == NULL || kctx->as_nr != KBASEP_AS_NR_INVALID);
KBASE_DEBUG_ASSERT(kbdev->dev != NULL);
val = readl(kbdev->reg + offset);
dev_dbg(kbdev->dev, "r: reg %04x val %08x", offset, val);
if (kctx && kctx->jctx.tb)
kbase_device_trace_register_access(kctx, REG_READ, offset, val);
return val;
}
KBASE_EXPORT_TEST_API(kbase_reg_read);
#endif /* !defined(CONFIG_MALI_NO_MALI) */
/**
* kbase_report_gpu_fault - Report a GPU fault.
* @kbdev: Kbase device pointer
* @multiple: Zero if only GPU_FAULT was raised, non-zero if MULTIPLE_GPU_FAULTS
* was also set
*
* This function is called from the interrupt handler when a GPU fault occurs.
* It reports the details of the fault using dev_warn().
*/
static void kbase_report_gpu_fault(struct kbase_device *kbdev, int multiple)
{
u32 status;
u64 address;
status = kbase_reg_read(kbdev, GPU_CONTROL_REG(GPU_FAULTSTATUS), NULL);
address = (u64) kbase_reg_read(kbdev,
GPU_CONTROL_REG(GPU_FAULTADDRESS_HI), NULL) << 32;
address |= kbase_reg_read(kbdev,
GPU_CONTROL_REG(GPU_FAULTADDRESS_LO), NULL);
dev_warn(kbdev->dev, "GPU Fault 0x%08x (%s) at 0x%016llx",
status & 0xFF,
kbase_exception_name(kbdev, status),
address);
if (multiple)
dev_warn(kbdev->dev, "There were multiple GPU faults - some have not been reported\n");
}
void kbase_gpu_interrupt(struct kbase_device *kbdev, u32 val)
{
KBASE_TRACE_ADD(kbdev, CORE_GPU_IRQ, NULL, NULL, 0u, val);
if (val & GPU_FAULT)
kbase_report_gpu_fault(kbdev, val & MULTIPLE_GPU_FAULTS);
if (val & RESET_COMPLETED)
kbase_pm_reset_done(kbdev);
if (val & PRFCNT_SAMPLE_COMPLETED)
kbase_instr_hwcnt_sample_done(kbdev);
if (val & CLEAN_CACHES_COMPLETED)
kbase_clean_caches_done(kbdev);
KBASE_TRACE_ADD(kbdev, CORE_GPU_IRQ_CLEAR, NULL, NULL, 0u, val);
kbase_reg_write(kbdev, GPU_CONTROL_REG(GPU_IRQ_CLEAR), val, NULL);
/* kbase_pm_check_transitions must be called after the IRQ has been
* cleared. This is because it might trigger further power transitions
* and we don't want to miss the interrupt raised to notify us that
* these further transitions have finished.
*/
if (val & POWER_CHANGED_ALL)
kbase_pm_power_changed(kbdev);
KBASE_TRACE_ADD(kbdev, CORE_GPU_IRQ_DONE, NULL, NULL, 0u, val);
}

View file

@ -0,0 +1,67 @@
/*
*
* (C) COPYRIGHT 2014 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Backend-specific HW access device APIs
*/
#ifndef _KBASE_DEVICE_INTERNAL_H_
#define _KBASE_DEVICE_INTERNAL_H_
/**
* kbase_reg_write - write to GPU register
* @kbdev: Kbase device pointer
* @offset: Offset of register
* @value: Value to write
* @kctx: Kbase context pointer. May be NULL
*
* Caller must ensure the GPU is powered (@kbdev->pm.gpu_powered != false). If
* @kctx is not NULL then the caller must ensure it is scheduled (@kctx->as_nr
* != KBASEP_AS_NR_INVALID).
*/
void kbase_reg_write(struct kbase_device *kbdev, u16 offset, u32 value,
struct kbase_context *kctx);
/**
* kbase_reg_read - read from GPU register
* @kbdev: Kbase device pointer
* @offset: Offset of register
* @kctx: Kbase context pointer. May be NULL
*
* Caller must ensure the GPU is powered (@kbdev->pm.gpu_powered != false). If
* @kctx is not NULL then the caller must ensure it is scheduled (@kctx->as_nr
* != KBASEP_AS_NR_INVALID).
*
* Return: Value in desired register
*/
u32 kbase_reg_read(struct kbase_device *kbdev, u16 offset,
struct kbase_context *kctx);
/**
* kbase_gpu_interrupt - GPU interrupt handler
* @kbdev: Kbase device pointer
* @val: The value of the GPU IRQ status register which triggered the call
*
* This function is called from the interrupt handler when a GPU irq is to be
* handled.
*/
void kbase_gpu_interrupt(struct kbase_device *kbdev, u32 val);
#endif /* _KBASE_DEVICE_INTERNAL_H_ */

View file

@ -0,0 +1,125 @@
/*
*
* (C) COPYRIGHT 2014-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Register-based HW access backend APIs
*/
#include <mali_kbase.h>
#include <mali_kbase_hwaccess_jm.h>
#include <mali_kbase_hwaccess_backend.h>
#include <backend/gpu/mali_kbase_irq_internal.h>
#include <backend/gpu/mali_kbase_jm_internal.h>
#include <backend/gpu/mali_kbase_js_internal.h>
#include <backend/gpu/mali_kbase_pm_internal.h>
int kbase_backend_early_init(struct kbase_device *kbdev)
{
int err;
err = kbasep_platform_device_init(kbdev);
if (err)
return err;
/* Ensure we can access the GPU registers */
kbase_pm_register_access_enable(kbdev);
/* Find out GPU properties based on the GPU feature registers */
kbase_gpuprops_set(kbdev);
/* We're done accessing the GPU registers for now. */
kbase_pm_register_access_disable(kbdev);
err = kbase_hwaccess_pm_init(kbdev);
if (err)
goto fail_pm;
err = kbase_install_interrupts(kbdev);
if (err)
goto fail_interrupts;
return 0;
fail_interrupts:
kbase_hwaccess_pm_term(kbdev);
fail_pm:
kbasep_platform_device_term(kbdev);
return err;
}
void kbase_backend_early_term(struct kbase_device *kbdev)
{
kbase_release_interrupts(kbdev);
kbase_hwaccess_pm_term(kbdev);
kbasep_platform_device_term(kbdev);
}
int kbase_backend_late_init(struct kbase_device *kbdev)
{
int err;
err = kbase_backend_timer_init(kbdev);
if (err)
goto fail_timer;
/* MALI_SEC_INTEGRATION - timer_init, powerup switching location for sec_hwcnt */
err = kbase_hwaccess_pm_powerup(kbdev, PM_HW_ISSUES_DETECT);
if (err)
return err;
/* Currently disabled on the prototype */
#ifdef CONFIG_MALI_DEBUG
#ifndef CONFIG_MALI_NO_MALI
if (kbasep_common_test_interrupt_handlers(kbdev) != 0) {
dev_err(kbdev->dev, "Interrupt assigment check failed.\n");
err = -EINVAL;
goto fail_interrupt_test;
}
#endif /* !CONFIG_MALI_NO_MALI */
#endif /* CONFIG_MALI_DEBUG */
err = kbase_job_slot_init(kbdev);
if (err)
goto fail_job_slot;
init_waitqueue_head(&kbdev->hwaccess.backend.reset_wait);
return 0;
fail_job_slot:
/* Currently disabled on the prototype */
#ifdef CONFIG_MALI_DEBUG
#ifndef CONFIG_MALI_NO_MALI
fail_interrupt_test:
#endif /* !CONFIG_MALI_NO_MALI */
#endif /* CONFIG_MALI_DEBUG */
kbase_backend_timer_term(kbdev);
fail_timer:
kbase_hwaccess_pm_halt(kbdev);
return err;
}
void kbase_backend_late_term(struct kbase_device *kbdev)
{
kbase_job_slot_halt(kbdev);
kbase_job_slot_term(kbdev);
kbase_backend_timer_term(kbdev);
kbase_hwaccess_pm_halt(kbdev);
}

View file

@ -0,0 +1,85 @@
/*
*
* (C) COPYRIGHT 2014-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Base kernel property query backend APIs
*/
#include <mali_kbase.h>
#include <backend/gpu/mali_kbase_device_internal.h>
#include <backend/gpu/mali_kbase_pm_internal.h>
#include <mali_kbase_hwaccess_gpuprops.h>
void kbase_backend_gpuprops_get(struct kbase_device *kbdev,
struct kbase_gpuprops_regdump *regdump)
{
int i;
/* Fill regdump with the content of the relevant registers */
regdump->gpu_id = kbase_reg_read(kbdev, GPU_CONTROL_REG(GPU_ID), NULL);
regdump->l2_features = kbase_reg_read(kbdev,
GPU_CONTROL_REG(L2_FEATURES), NULL);
regdump->suspend_size = kbase_reg_read(kbdev,
GPU_CONTROL_REG(SUSPEND_SIZE), NULL);
regdump->tiler_features = kbase_reg_read(kbdev,
GPU_CONTROL_REG(TILER_FEATURES), NULL);
regdump->mem_features = kbase_reg_read(kbdev,
GPU_CONTROL_REG(MEM_FEATURES), NULL);
regdump->mmu_features = kbase_reg_read(kbdev,
GPU_CONTROL_REG(MMU_FEATURES), NULL);
regdump->as_present = kbase_reg_read(kbdev,
GPU_CONTROL_REG(AS_PRESENT), NULL);
regdump->js_present = kbase_reg_read(kbdev,
GPU_CONTROL_REG(JS_PRESENT), NULL);
for (i = 0; i < GPU_MAX_JOB_SLOTS; i++)
regdump->js_features[i] = kbase_reg_read(kbdev,
GPU_CONTROL_REG(JS_FEATURES_REG(i)), NULL);
for (i = 0; i < BASE_GPU_NUM_TEXTURE_FEATURES_REGISTERS; i++)
regdump->texture_features[i] = kbase_reg_read(kbdev,
GPU_CONTROL_REG(TEXTURE_FEATURES_REG(i)), NULL);
regdump->thread_max_threads = kbase_reg_read(kbdev,
GPU_CONTROL_REG(THREAD_MAX_THREADS), NULL);
regdump->thread_max_workgroup_size = kbase_reg_read(kbdev,
GPU_CONTROL_REG(THREAD_MAX_WORKGROUP_SIZE),
NULL);
regdump->thread_max_barrier_size = kbase_reg_read(kbdev,
GPU_CONTROL_REG(THREAD_MAX_BARRIER_SIZE), NULL);
regdump->thread_features = kbase_reg_read(kbdev,
GPU_CONTROL_REG(THREAD_FEATURES), NULL);
regdump->shader_present_lo = kbase_reg_read(kbdev,
GPU_CONTROL_REG(SHADER_PRESENT_LO), NULL);
regdump->shader_present_hi = kbase_reg_read(kbdev,
GPU_CONTROL_REG(SHADER_PRESENT_HI), NULL);
regdump->tiler_present_lo = kbase_reg_read(kbdev,
GPU_CONTROL_REG(TILER_PRESENT_LO), NULL);
regdump->tiler_present_hi = kbase_reg_read(kbdev,
GPU_CONTROL_REG(TILER_PRESENT_HI), NULL);
regdump->l2_present_lo = kbase_reg_read(kbdev,
GPU_CONTROL_REG(L2_PRESENT_LO), NULL);
regdump->l2_present_hi = kbase_reg_read(kbdev,
GPU_CONTROL_REG(L2_PRESENT_HI), NULL);
}

View file

@ -0,0 +1,637 @@
/*
*
* (C) COPYRIGHT 2014-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* GPU backend instrumentation APIs.
*/
#include <mali_kbase.h>
#include <mali_midg_regmap.h>
#include <backend/gpu/mali_kbase_device_internal.h>
#include <backend/gpu/mali_kbase_pm_internal.h>
#include <backend/gpu/mali_kbase_instr_internal.h>
/**
* kbasep_instr_hwcnt_cacheclean - Issue Cache Clean & Invalidate command to
* hardware
*
* @kbdev: Kbase device
*/
static void kbasep_instr_hwcnt_cacheclean(struct kbase_device *kbdev)
{
unsigned long flags;
unsigned long pm_flags;
u32 irq_mask;
spin_lock_irqsave(&kbdev->hwcnt.lock, flags);
/* Wait for any reset to complete */
while (kbdev->hwcnt.backend.state == KBASE_INSTR_STATE_RESETTING) {
spin_unlock_irqrestore(&kbdev->hwcnt.lock, flags);
#ifdef MALI_SEC_HWCNT
if (kbdev->hwcnt.is_hwcnt_attach) {
int ret = wait_event_timeout(kbdev->hwcnt.backend.cache_clean_wait,
kbdev->hwcnt.backend.state !=
KBASE_INSTR_STATE_RESETTING, kbdev->hwcnt.timeout);
if (ret == 0)
kbdev->hwcnt.backend.state = KBASE_INSTR_STATE_IDLE;
}
else
#endif
wait_event(kbdev->hwcnt.backend.cache_clean_wait,
kbdev->hwcnt.backend.state !=
KBASE_INSTR_STATE_RESETTING);
spin_lock_irqsave(&kbdev->hwcnt.lock, flags);
}
KBASE_DEBUG_ASSERT(kbdev->hwcnt.backend.state ==
KBASE_INSTR_STATE_REQUEST_CLEAN);
#ifdef MALI_SEC_HWCNT
/* consider hwcnt state before register access */
if (kbdev->hwcnt.backend.state != KBASE_INSTR_STATE_REQUEST_CLEAN) {
spin_unlock_irqrestore(&kbdev->hwcnt.lock, flags);
return;
}
#endif
/* Enable interrupt */
spin_lock_irqsave(&kbdev->pm.power_change_lock, pm_flags);
irq_mask = kbase_reg_read(kbdev, GPU_CONTROL_REG(GPU_IRQ_MASK), NULL);
kbase_reg_write(kbdev, GPU_CONTROL_REG(GPU_IRQ_MASK),
irq_mask | CLEAN_CACHES_COMPLETED, NULL);
spin_unlock_irqrestore(&kbdev->pm.power_change_lock, pm_flags);
/* clean&invalidate the caches so we're sure the mmu tables for the dump
* buffer is valid */
KBASE_TRACE_ADD(kbdev, CORE_GPU_CLEAN_INV_CACHES, NULL, NULL, 0u, 0);
kbase_reg_write(kbdev, GPU_CONTROL_REG(GPU_COMMAND),
GPU_COMMAND_CLEAN_INV_CACHES, NULL);
kbdev->hwcnt.backend.state = KBASE_INSTR_STATE_CLEANING;
spin_unlock_irqrestore(&kbdev->hwcnt.lock, flags);
}
int kbase_instr_hwcnt_enable_internal(struct kbase_device *kbdev,
struct kbase_context *kctx,
struct kbase_uk_hwcnt_setup *setup)
{
unsigned long flags, pm_flags;
int err = -EINVAL;
struct kbasep_js_device_data *js_devdata;
u32 irq_mask;
int ret;
u64 shader_cores_needed;
KBASE_DEBUG_ASSERT(NULL == kbdev->hwcnt.suspended_kctx);
shader_cores_needed = kbase_pm_get_present_cores(kbdev,
KBASE_PM_CORE_SHADER);
js_devdata = &kbdev->js_data;
/* alignment failure */
if ((setup->dump_buffer == 0ULL) || (setup->dump_buffer & (2048 - 1)))
goto out_err;
/* Override core availability policy to ensure all cores are available
*/
kbase_pm_ca_instr_enable(kbdev);
/* Request the cores early on synchronously - we'll release them on any
* errors (e.g. instrumentation already active) */
#ifndef MALI_SEC_HWCNT
kbase_pm_request_cores_sync(kbdev, true, shader_cores_needed);
#endif
spin_lock_irqsave(&kbdev->hwcnt.lock, flags);
if (kbdev->hwcnt.backend.state == KBASE_INSTR_STATE_RESETTING) {
/* GPU is being reset */
spin_unlock_irqrestore(&kbdev->hwcnt.lock, flags);
#ifdef MALI_SEC_HWCNT
{
int ret = wait_event_timeout(kbdev->hwcnt.backend.wait, kbdev->hwcnt.backend.triggered != 0, kbdev->hwcnt.timeout);
if (ret == 0) {
kbdev->hwcnt.backend.state = KBASE_INSTR_STATE_IDLE;
err = -EINVAL;
GPU_LOG(DVFS_WARNING, DUMMY, 0u, 0u, "skip enable hwcnt %d \n", __LINE__);
return err;
}
}
#else
wait_event(kbdev->hwcnt.backend.wait,
kbdev->hwcnt.backend.triggered != 0);
#endif
spin_lock_irqsave(&kbdev->hwcnt.lock, flags);
}
if (kbdev->hwcnt.backend.state != KBASE_INSTR_STATE_DISABLED) {
/* Instrumentation is already enabled */
spin_unlock_irqrestore(&kbdev->hwcnt.lock, flags);
goto out_unrequest_cores;
}
/* Enable interrupt */
spin_lock_irqsave(&kbdev->pm.power_change_lock, pm_flags);
irq_mask = kbase_reg_read(kbdev, GPU_CONTROL_REG(GPU_IRQ_MASK), NULL);
kbase_reg_write(kbdev, GPU_CONTROL_REG(GPU_IRQ_MASK), irq_mask |
PRFCNT_SAMPLE_COMPLETED, NULL);
spin_unlock_irqrestore(&kbdev->pm.power_change_lock, pm_flags);
/* In use, this context is the owner */
kbdev->hwcnt.kctx = kctx;
/* Remember the dump address so we can reprogram it later */
kbdev->hwcnt.addr = setup->dump_buffer;
/* Remember all the settings for suspend/resume */
if (&kbdev->hwcnt.suspended_state != setup)
memcpy(&kbdev->hwcnt.suspended_state, setup,
sizeof(kbdev->hwcnt.suspended_state));
/* Request the clean */
kbdev->hwcnt.backend.state = KBASE_INSTR_STATE_REQUEST_CLEAN;
kbdev->hwcnt.backend.triggered = 0;
/* Clean&invalidate the caches so we're sure the mmu tables for the dump
* buffer is valid */
ret = queue_work(kbdev->hwcnt.backend.cache_clean_wq,
&kbdev->hwcnt.backend.cache_clean_work);
KBASE_DEBUG_ASSERT(ret);
spin_unlock_irqrestore(&kbdev->hwcnt.lock, flags);
#ifdef MALI_SEC_HWCNT
{
int ret = wait_event_timeout(kbdev->hwcnt.backend.wait, kbdev->hwcnt.backend.triggered != 0, kbdev->hwcnt.timeout);
if (ret == 0) {
kbdev->hwcnt.backend.state = KBASE_INSTR_STATE_IDLE;
err = -EINVAL;
GPU_LOG(DVFS_WARNING, DUMMY, 0u, 0u, "skip enable hwcnt %d \n", __LINE__);
return err;
}
}
#else
/* Wait for cacheclean to complete */
wait_event(kbdev->hwcnt.backend.wait,
kbdev->hwcnt.backend.triggered != 0);
#endif
KBASE_DEBUG_ASSERT(kbdev->hwcnt.backend.state ==
KBASE_INSTR_STATE_IDLE);
kbase_pm_request_l2_caches(kbdev);
/* Configure */
kbase_reg_write(kbdev, GPU_CONTROL_REG(PRFCNT_CONFIG),
(kctx->as_nr << PRFCNT_CONFIG_AS_SHIFT)
| PRFCNT_CONFIG_MODE_OFF, kctx);
kbase_reg_write(kbdev, GPU_CONTROL_REG(PRFCNT_BASE_LO),
setup->dump_buffer & 0xFFFFFFFF, kctx);
kbase_reg_write(kbdev, GPU_CONTROL_REG(PRFCNT_BASE_HI),
setup->dump_buffer >> 32, kctx);
kbase_reg_write(kbdev, GPU_CONTROL_REG(PRFCNT_JM_EN),
setup->jm_bm, kctx);
kbase_reg_write(kbdev, GPU_CONTROL_REG(PRFCNT_SHADER_EN),
setup->shader_bm, kctx);
kbase_reg_write(kbdev, GPU_CONTROL_REG(PRFCNT_MMU_L2_EN),
setup->mmu_l2_bm, kctx);
/* Due to PRLAM-8186 we need to disable the Tiler before we enable the
* HW counter dump. */
if (kbase_hw_has_issue(kbdev, BASE_HW_ISSUE_8186))
kbase_reg_write(kbdev, GPU_CONTROL_REG(PRFCNT_TILER_EN), 0,
kctx);
else
kbase_reg_write(kbdev, GPU_CONTROL_REG(PRFCNT_TILER_EN),
setup->tiler_bm, kctx);
kbase_reg_write(kbdev, GPU_CONTROL_REG(PRFCNT_CONFIG),
(kctx->as_nr << PRFCNT_CONFIG_AS_SHIFT) |
PRFCNT_CONFIG_MODE_MANUAL, kctx);
/* If HW has PRLAM-8186 we can now re-enable the tiler HW counters dump
*/
if (kbase_hw_has_issue(kbdev, BASE_HW_ISSUE_8186))
kbase_reg_write(kbdev, GPU_CONTROL_REG(PRFCNT_TILER_EN),
setup->tiler_bm, kctx);
spin_lock_irqsave(&kbdev->hwcnt.lock, flags);
if (kbdev->hwcnt.backend.state == KBASE_INSTR_STATE_RESETTING) {
/* GPU is being reset */
spin_unlock_irqrestore(&kbdev->hwcnt.lock, flags);
#ifdef MALI_SEC_HWCNT
{
int ret = wait_event_timeout(kbdev->hwcnt.backend.wait, kbdev->hwcnt.backend.triggered != 0, kbdev->hwcnt.timeout);
if (ret == 0) {
kbdev->hwcnt.backend.state = KBASE_INSTR_STATE_IDLE;
err = -EINVAL;
GPU_LOG(DVFS_WARNING, DUMMY, 0u, 0u, "skip enable hwcnt %d \n", __LINE__);
return err;
}
}
#else
wait_event(kbdev->hwcnt.backend.wait,
kbdev->hwcnt.backend.triggered != 0);
#endif
spin_lock_irqsave(&kbdev->hwcnt.lock, flags);
}
kbdev->hwcnt.backend.state = KBASE_INSTR_STATE_IDLE;
kbdev->hwcnt.backend.triggered = 1;
wake_up(&kbdev->hwcnt.backend.wait);
spin_unlock_irqrestore(&kbdev->hwcnt.lock, flags);
err = 0;
dev_dbg(kbdev->dev, "HW counters dumping set-up for context %p", kctx);
return err;
out_unrequest_cores:
kbase_pm_unrequest_cores(kbdev, true, shader_cores_needed);
out_err:
return err;
}
int kbase_instr_hwcnt_disable_internal(struct kbase_context *kctx)
{
unsigned long flags, pm_flags;
int err = -EINVAL;
u32 irq_mask;
struct kbase_device *kbdev = kctx->kbdev;
while (1) {
spin_lock_irqsave(&kbdev->hwcnt.lock, flags);
if (kbdev->hwcnt.backend.state == KBASE_INSTR_STATE_DISABLED) {
/* Instrumentation is not enabled */
spin_unlock_irqrestore(&kbdev->hwcnt.lock, flags);
goto out;
}
if (kbdev->hwcnt.kctx != kctx) {
/* Instrumentation has been setup for another context */
spin_unlock_irqrestore(&kbdev->hwcnt.lock, flags);
goto out;
}
if (kbdev->hwcnt.backend.state == KBASE_INSTR_STATE_IDLE)
break;
spin_unlock_irqrestore(&kbdev->hwcnt.lock, flags);
/* Ongoing dump/setup - wait for its completion */
#ifdef MALI_SEC_HWCNT
if (kbdev->hwcnt.is_hwcnt_attach) {
int ret = wait_event_timeout(kbdev->hwcnt.backend.wait,
kbdev->hwcnt.backend.triggered != 0, kbdev->hwcnt.timeout);
if (ret == 0)
kbdev->hwcnt.backend.state = KBASE_INSTR_STATE_IDLE;
}
else
#endif
wait_event(kbdev->hwcnt.backend.wait,
kbdev->hwcnt.backend.triggered != 0);
}
kbdev->hwcnt.backend.state = KBASE_INSTR_STATE_DISABLED;
kbdev->hwcnt.backend.triggered = 0;
/* Disable interrupt */
spin_lock_irqsave(&kbdev->pm.power_change_lock, pm_flags);
irq_mask = kbase_reg_read(kbdev, GPU_CONTROL_REG(GPU_IRQ_MASK), NULL);
kbase_reg_write(kbdev, GPU_CONTROL_REG(GPU_IRQ_MASK),
irq_mask & ~PRFCNT_SAMPLE_COMPLETED, NULL);
spin_unlock_irqrestore(&kbdev->pm.power_change_lock, pm_flags);
/* Disable the counters */
kbase_reg_write(kbdev, GPU_CONTROL_REG(PRFCNT_CONFIG), 0, kctx);
kbdev->hwcnt.kctx = NULL;
kbdev->hwcnt.addr = 0ULL;
kbase_pm_ca_instr_disable(kbdev);
#ifndef MALI_SEC_HWCNT
kbase_pm_unrequest_cores(kbdev, true,
kbase_pm_get_present_cores(kbdev, KBASE_PM_CORE_SHADER));
#endif
spin_unlock_irqrestore(&kbdev->hwcnt.lock, flags);
kbase_pm_release_l2_caches(kbdev);
dev_dbg(kbdev->dev, "HW counters dumping disabled for context %p",
kctx);
err = 0;
out:
return err;
}
int kbase_instr_hwcnt_request_dump(struct kbase_context *kctx)
{
unsigned long flags;
int err = -EINVAL;
struct kbase_device *kbdev = kctx->kbdev;
spin_lock_irqsave(&kbdev->hwcnt.lock, flags);
if (kbdev->hwcnt.kctx != kctx) {
/* The instrumentation has been setup for another context */
goto unlock;
}
if (kbdev->hwcnt.backend.state != KBASE_INSTR_STATE_IDLE) {
/* HW counters are disabled or another dump is ongoing, or we're
* resetting */
goto unlock;
}
kbdev->hwcnt.backend.triggered = 0;
/* Mark that we're dumping - the PF handler can signal that we faulted
*/
kbdev->hwcnt.backend.state = KBASE_INSTR_STATE_DUMPING;
/* Reconfigure the dump address */
kbase_reg_write(kbdev, GPU_CONTROL_REG(PRFCNT_BASE_LO),
kbdev->hwcnt.addr & 0xFFFFFFFF, NULL);
kbase_reg_write(kbdev, GPU_CONTROL_REG(PRFCNT_BASE_HI),
kbdev->hwcnt.addr >> 32, NULL);
/* Start dumping */
KBASE_TRACE_ADD(kbdev, CORE_GPU_PRFCNT_SAMPLE, NULL, NULL,
kbdev->hwcnt.addr, 0);
kbase_reg_write(kbdev, GPU_CONTROL_REG(GPU_COMMAND),
GPU_COMMAND_PRFCNT_SAMPLE, kctx);
dev_dbg(kbdev->dev, "HW counters dumping done for context %p", kctx);
err = 0;
unlock:
spin_unlock_irqrestore(&kbdev->hwcnt.lock, flags);
return err;
}
KBASE_EXPORT_SYMBOL(kbase_instr_hwcnt_request_dump);
bool kbase_instr_hwcnt_dump_complete(struct kbase_context *kctx,
bool * const success)
{
unsigned long flags;
bool complete = false;
struct kbase_device *kbdev = kctx->kbdev;
spin_lock_irqsave(&kbdev->hwcnt.lock, flags);
if (kbdev->hwcnt.backend.state == KBASE_INSTR_STATE_IDLE) {
*success = true;
complete = true;
} else if (kbdev->hwcnt.backend.state == KBASE_INSTR_STATE_FAULT) {
*success = false;
complete = true;
kbdev->hwcnt.backend.state = KBASE_INSTR_STATE_IDLE;
}
spin_unlock_irqrestore(&kbdev->hwcnt.lock, flags);
return complete;
}
KBASE_EXPORT_SYMBOL(kbase_instr_hwcnt_dump_complete);
void kbasep_cache_clean_worker(struct work_struct *data)
{
struct kbase_device *kbdev;
unsigned long flags;
kbdev = container_of(data, struct kbase_device,
hwcnt.backend.cache_clean_work);
mutex_lock(&kbdev->cacheclean_lock);
kbasep_instr_hwcnt_cacheclean(kbdev);
spin_lock_irqsave(&kbdev->hwcnt.lock, flags);
/* Wait for our condition, and any reset to complete */
while (kbdev->hwcnt.backend.state == KBASE_INSTR_STATE_RESETTING ||
kbdev->hwcnt.backend.state ==
KBASE_INSTR_STATE_CLEANING) {
spin_unlock_irqrestore(&kbdev->hwcnt.lock, flags);
#ifdef MALI_SEC_HWCNT
if (kbdev->hwcnt.is_hwcnt_attach) {
int ret = wait_event_timeout(kbdev->hwcnt.backend.cache_clean_wait,
(kbdev->hwcnt.backend.state !=
KBASE_INSTR_STATE_RESETTING &&
kbdev->hwcnt.backend.state !=
KBASE_INSTR_STATE_CLEANING), kbdev->hwcnt.timeout);
if (ret == 0)
kbdev->hwcnt.backend.state = KBASE_INSTR_STATE_IDLE;
}
else
#endif
wait_event(kbdev->hwcnt.backend.cache_clean_wait,
(kbdev->hwcnt.backend.state !=
KBASE_INSTR_STATE_RESETTING &&
kbdev->hwcnt.backend.state !=
KBASE_INSTR_STATE_CLEANING));
spin_lock_irqsave(&kbdev->hwcnt.lock, flags);
}
KBASE_DEBUG_ASSERT(kbdev->hwcnt.backend.state ==
KBASE_INSTR_STATE_CLEANED);
/* All finished and idle */
kbdev->hwcnt.backend.state = KBASE_INSTR_STATE_IDLE;
kbdev->hwcnt.backend.triggered = 1;
wake_up(&kbdev->hwcnt.backend.wait);
spin_unlock_irqrestore(&kbdev->hwcnt.lock, flags);
mutex_unlock(&kbdev->cacheclean_lock);
}
void kbase_instr_hwcnt_sample_done(struct kbase_device *kbdev)
{
unsigned long flags;
spin_lock_irqsave(&kbdev->hwcnt.lock, flags);
if (kbdev->hwcnt.backend.state == KBASE_INSTR_STATE_FAULT) {
kbdev->hwcnt.backend.triggered = 1;
wake_up(&kbdev->hwcnt.backend.wait);
} else if (kbdev->hwcnt.backend.state == KBASE_INSTR_STATE_DUMPING) {
int ret;
/* Always clean and invalidate the cache after a successful dump
*/
kbdev->hwcnt.backend.state = KBASE_INSTR_STATE_REQUEST_CLEAN;
ret = queue_work(kbdev->hwcnt.backend.cache_clean_wq,
&kbdev->hwcnt.backend.cache_clean_work);
KBASE_DEBUG_ASSERT(ret);
}
/* NOTE: In the state KBASE_INSTR_STATE_RESETTING, We're in a reset,
* and the instrumentation state hasn't been restored yet -
* kbasep_reset_timeout_worker() will do the rest of the work */
spin_unlock_irqrestore(&kbdev->hwcnt.lock, flags);
}
void kbase_clean_caches_done(struct kbase_device *kbdev)
{
u32 irq_mask;
if (kbdev->hwcnt.backend.state != KBASE_INSTR_STATE_DISABLED) {
unsigned long flags;
unsigned long pm_flags;
spin_lock_irqsave(&kbdev->hwcnt.lock, flags);
/* Disable interrupt */
spin_lock_irqsave(&kbdev->pm.power_change_lock, pm_flags);
irq_mask = kbase_reg_read(kbdev, GPU_CONTROL_REG(GPU_IRQ_MASK),
NULL);
kbase_reg_write(kbdev, GPU_CONTROL_REG(GPU_IRQ_MASK),
irq_mask & ~CLEAN_CACHES_COMPLETED, NULL);
spin_unlock_irqrestore(&kbdev->pm.power_change_lock, pm_flags);
/* Wakeup... */
if (kbdev->hwcnt.backend.state == KBASE_INSTR_STATE_CLEANING) {
/* Only wake if we weren't resetting */
kbdev->hwcnt.backend.state = KBASE_INSTR_STATE_CLEANED;
wake_up(&kbdev->hwcnt.backend.cache_clean_wait);
}
/* NOTE: In the state KBASE_INSTR_STATE_RESETTING, We're in a
* reset, and the instrumentation state hasn't been restored yet
* - kbasep_reset_timeout_worker() will do the rest of the work
*/
spin_unlock_irqrestore(&kbdev->hwcnt.lock, flags);
}
}
int kbase_instr_hwcnt_wait_for_dump(struct kbase_context *kctx)
{
struct kbase_device *kbdev = kctx->kbdev;
unsigned long flags;
int err;
/* Wait for dump & cacheclean to complete */
#ifdef MALI_SEC_HWCNT
if (kbdev->hwcnt.is_hwcnt_attach) {
int ret = wait_event_timeout(kbdev->hwcnt.backend.wait, kbdev->hwcnt.backend.triggered != 0, kbdev->hwcnt.timeout);
if (ret == 0) {
kbdev->hwcnt.backend.state = KBASE_INSTR_STATE_IDLE;
err = -EINVAL;
GPU_LOG(DVFS_WARNING, DUMMY, 0u, 0u, "skip dump hwcnt %d \n", __LINE__);
return err;
}
} else
#endif
wait_event(kbdev->hwcnt.backend.wait,
kbdev->hwcnt.backend.triggered != 0);
spin_lock_irqsave(&kbdev->hwcnt.lock, flags);
if (kbdev->hwcnt.backend.state == KBASE_INSTR_STATE_RESETTING) {
/* GPU is being reset */
spin_unlock_irqrestore(&kbdev->hwcnt.lock, flags);
#ifdef MALI_SEC_HWCNT
if (kbdev->hwcnt.is_hwcnt_attach)
wait_event_timeout(kbdev->hwcnt.backend.wait, kbdev->hwcnt.backend.triggered != 0, kbdev->hwcnt.timeout);
else
#endif
wait_event(kbdev->hwcnt.backend.wait,
kbdev->hwcnt.backend.triggered != 0);
spin_lock_irqsave(&kbdev->hwcnt.lock, flags);
}
if (kbdev->hwcnt.backend.state == KBASE_INSTR_STATE_FAULT) {
err = -EINVAL;
kbdev->hwcnt.backend.state = KBASE_INSTR_STATE_IDLE;
} else {
/* Dump done */
KBASE_DEBUG_ASSERT(kbdev->hwcnt.backend.state ==
KBASE_INSTR_STATE_IDLE);
err = 0;
}
spin_unlock_irqrestore(&kbdev->hwcnt.lock, flags);
return err;
}
int kbase_instr_hwcnt_clear(struct kbase_context *kctx)
{
unsigned long flags;
int err = -EINVAL;
struct kbase_device *kbdev = kctx->kbdev;
spin_lock_irqsave(&kbdev->hwcnt.lock, flags);
if (kbdev->hwcnt.backend.state == KBASE_INSTR_STATE_RESETTING) {
/* GPU is being reset */
spin_unlock_irqrestore(&kbdev->hwcnt.lock, flags);
#ifdef MALI_SEC_HWCNT
if (kbdev->hwcnt.is_hwcnt_attach)
wait_event_timeout(kbdev->hwcnt.backend.wait,
kbdev->hwcnt.backend.triggered != 0, kbdev->hwcnt.timeout);
else
#endif
wait_event(kbdev->hwcnt.backend.wait,
kbdev->hwcnt.backend.triggered != 0);
spin_lock_irqsave(&kbdev->hwcnt.lock, flags);
}
/* Check it's the context previously set up and we're not already
* dumping */
if (kbdev->hwcnt.kctx != kctx || kbdev->hwcnt.backend.state !=
KBASE_INSTR_STATE_IDLE)
goto out;
/* Clear the counters */
KBASE_TRACE_ADD(kbdev, CORE_GPU_PRFCNT_CLEAR, NULL, NULL, 0u, 0);
kbase_reg_write(kbdev, GPU_CONTROL_REG(GPU_COMMAND),
GPU_COMMAND_PRFCNT_CLEAR, kctx);
err = 0;
out:
spin_unlock_irqrestore(&kbdev->hwcnt.lock, flags);
return err;
}
KBASE_EXPORT_SYMBOL(kbase_instr_hwcnt_clear);
int kbase_instr_backend_init(struct kbase_device *kbdev)
{
int ret = 0;
kbdev->hwcnt.backend.state = KBASE_INSTR_STATE_DISABLED;
init_waitqueue_head(&kbdev->hwcnt.backend.wait);
init_waitqueue_head(&kbdev->hwcnt.backend.cache_clean_wait);
INIT_WORK(&kbdev->hwcnt.backend.cache_clean_work,
kbasep_cache_clean_worker);
kbdev->hwcnt.backend.triggered = 0;
kbdev->hwcnt.backend.cache_clean_wq =
alloc_workqueue("Mali cache cleaning workqueue", 0, 1);
if (NULL == kbdev->hwcnt.backend.cache_clean_wq)
ret = -EINVAL;
return ret;
}
void kbase_instr_backend_term(struct kbase_device *kbdev)
{
destroy_workqueue(kbdev->hwcnt.backend.cache_clean_wq);
}

View file

@ -0,0 +1,62 @@
/*
*
* (C) COPYRIGHT 2014 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Backend-specific instrumentation definitions
*/
#ifndef _KBASE_INSTR_DEFS_H_
#define _KBASE_INSTR_DEFS_H_
/*
* Instrumentation State Machine States
*/
enum kbase_instr_state {
/* State where instrumentation is not active */
KBASE_INSTR_STATE_DISABLED = 0,
/* State machine is active and ready for a command. */
KBASE_INSTR_STATE_IDLE,
/* Hardware is currently dumping a frame. */
KBASE_INSTR_STATE_DUMPING,
/* We've requested a clean to occur on a workqueue */
KBASE_INSTR_STATE_REQUEST_CLEAN,
/* Hardware is currently cleaning and invalidating caches. */
KBASE_INSTR_STATE_CLEANING,
/* Cache clean completed, and either a) a dump is complete, or
* b) instrumentation can now be setup. */
KBASE_INSTR_STATE_CLEANED,
/* kbasep_reset_timeout_worker() has started (but not compelted) a
* reset. This generally indicates the current action should be aborted,
* and kbasep_reset_timeout_worker() will handle the cleanup */
KBASE_INSTR_STATE_RESETTING,
/* An error has occured during DUMPING (page fault). */
KBASE_INSTR_STATE_FAULT
};
/* Structure used for instrumentation and HW counters dumping */
struct kbase_instr_backend {
wait_queue_head_t wait;
int triggered;
enum kbase_instr_state state;
wait_queue_head_t cache_clean_wait;
struct workqueue_struct *cache_clean_wq;
struct work_struct cache_clean_work;
};
#endif /* _KBASE_INSTR_DEFS_H_ */

View file

@ -0,0 +1,45 @@
/*
*
* (C) COPYRIGHT 2014 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Backend-specific HW access instrumentation APIs
*/
#ifndef _KBASE_INSTR_INTERNAL_H_
#define _KBASE_INSTR_INTERNAL_H_
/**
* kbasep_cache_clean_worker() - Workqueue for handling cache cleaning
* @data: a &struct work_struct
*/
void kbasep_cache_clean_worker(struct work_struct *data);
/**
* kbase_clean_caches_done() - Cache clean interrupt received
* @kbdev: Kbase device
*/
void kbase_clean_caches_done(struct kbase_device *kbdev);
/**
* kbase_instr_hwcnt_sample_done() - Dump complete interrupt received
* @kbdev: Kbase device
*/
void kbase_instr_hwcnt_sample_done(struct kbase_device *kbdev);
#endif /* _KBASE_INSTR_INTERNAL_H_ */

View file

@ -0,0 +1,39 @@
/*
*
* (C) COPYRIGHT 2014-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Backend specific IRQ APIs
*/
#ifndef _KBASE_IRQ_INTERNAL_H_
#define _KBASE_IRQ_INTERNAL_H_
int kbase_install_interrupts(struct kbase_device *kbdev);
void kbase_release_interrupts(struct kbase_device *kbdev);
/**
* kbase_synchronize_irqs - Ensure that all IRQ handlers have completed
* execution
* @kbdev: The kbase device
*/
void kbase_synchronize_irqs(struct kbase_device *kbdev);
int kbasep_common_test_interrupt_handlers(
struct kbase_device * const kbdev);
#endif /* _KBASE_IRQ_INTERNAL_H_ */

View file

@ -0,0 +1,471 @@
/*
*
* (C) COPYRIGHT 2014-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include <mali_kbase.h>
#include <backend/gpu/mali_kbase_device_internal.h>
#include <backend/gpu/mali_kbase_irq_internal.h>
#include <linux/interrupt.h>
#if !defined(CONFIG_MALI_NO_MALI)
/* GPU IRQ Tags */
#define JOB_IRQ_TAG 0
#define MMU_IRQ_TAG 1
#define GPU_IRQ_TAG 2
static void *kbase_tag(void *ptr, u32 tag)
{
return (void *)(((uintptr_t) ptr) | tag);
}
static void *kbase_untag(void *ptr)
{
return (void *)(((uintptr_t) ptr) & ~3);
}
static irqreturn_t kbase_job_irq_handler(int irq, void *data)
{
unsigned long flags;
struct kbase_device *kbdev = kbase_untag(data);
u32 val;
spin_lock_irqsave(&kbdev->pm.backend.gpu_powered_lock, flags);
if (!kbdev->pm.backend.gpu_powered) {
/* GPU is turned off - IRQ is not for us */
spin_unlock_irqrestore(&kbdev->pm.backend.gpu_powered_lock,
flags);
return IRQ_NONE;
}
val = kbase_reg_read(kbdev, JOB_CONTROL_REG(JOB_IRQ_STATUS), NULL);
#ifdef CONFIG_MALI_DEBUG
if (!kbdev->pm.backend.driver_ready_for_irqs)
dev_warn(kbdev->dev, "%s: irq %d irqstatus 0x%x before driver is ready\n",
__func__, irq, val);
#endif /* CONFIG_MALI_DEBUG */
spin_unlock_irqrestore(&kbdev->pm.backend.gpu_powered_lock, flags);
if (!val)
return IRQ_NONE;
dev_dbg(kbdev->dev, "%s: irq %d irqstatus 0x%x\n", __func__, irq, val);
kbase_job_done(kbdev, val);
return IRQ_HANDLED;
}
KBASE_EXPORT_TEST_API(kbase_job_irq_handler);
static irqreturn_t kbase_mmu_irq_handler(int irq, void *data)
{
unsigned long flags;
struct kbase_device *kbdev = kbase_untag(data);
u32 val;
spin_lock_irqsave(&kbdev->pm.backend.gpu_powered_lock, flags);
if (!kbdev->pm.backend.gpu_powered) {
/* GPU is turned off - IRQ is not for us */
spin_unlock_irqrestore(&kbdev->pm.backend.gpu_powered_lock,
flags);
return IRQ_NONE;
}
atomic_inc(&kbdev->faults_pending);
val = kbase_reg_read(kbdev, MMU_REG(MMU_IRQ_STATUS), NULL);
#ifdef CONFIG_MALI_DEBUG
if (!kbdev->pm.backend.driver_ready_for_irqs)
dev_warn(kbdev->dev, "%s: irq %d irqstatus 0x%x before driver is ready\n",
__func__, irq, val);
#endif /* CONFIG_MALI_DEBUG */
spin_unlock_irqrestore(&kbdev->pm.backend.gpu_powered_lock, flags);
if (!val) {
atomic_dec(&kbdev->faults_pending);
return IRQ_NONE;
}
dev_dbg(kbdev->dev, "%s: irq %d irqstatus 0x%x\n", __func__, irq, val);
kbase_mmu_interrupt(kbdev, val);
atomic_dec(&kbdev->faults_pending);
return IRQ_HANDLED;
}
static irqreturn_t kbase_gpu_irq_handler(int irq, void *data)
{
unsigned long flags;
struct kbase_device *kbdev = kbase_untag(data);
u32 val;
spin_lock_irqsave(&kbdev->pm.backend.gpu_powered_lock, flags);
if (!kbdev->pm.backend.gpu_powered) {
/* GPU is turned off - IRQ is not for us */
spin_unlock_irqrestore(&kbdev->pm.backend.gpu_powered_lock,
flags);
return IRQ_NONE;
}
val = kbase_reg_read(kbdev, GPU_CONTROL_REG(GPU_IRQ_STATUS), NULL);
#ifdef CONFIG_MALI_DEBUG
if (!kbdev->pm.backend.driver_ready_for_irqs)
dev_dbg(kbdev->dev, "%s: irq %d irqstatus 0x%x before driver is ready\n",
__func__, irq, val);
#endif /* CONFIG_MALI_DEBUG */
spin_unlock_irqrestore(&kbdev->pm.backend.gpu_powered_lock, flags);
if (!val)
return IRQ_NONE;
dev_dbg(kbdev->dev, "%s: irq %d irqstatus 0x%x\n", __func__, irq, val);
kbase_gpu_interrupt(kbdev, val);
return IRQ_HANDLED;
}
static irq_handler_t kbase_handler_table[] = {
[JOB_IRQ_TAG] = kbase_job_irq_handler,
[MMU_IRQ_TAG] = kbase_mmu_irq_handler,
[GPU_IRQ_TAG] = kbase_gpu_irq_handler,
};
#ifdef CONFIG_MALI_DEBUG
#define JOB_IRQ_HANDLER JOB_IRQ_TAG
#define MMU_IRQ_HANDLER MMU_IRQ_TAG
#define GPU_IRQ_HANDLER GPU_IRQ_TAG
/**
* kbase_set_custom_irq_handler - Set a custom IRQ handler
* @kbdev: Device for which the handler is to be registered
* @custom_handler: Handler to be registered
* @irq_type: Interrupt type
*
* Registers given interrupt handler for requested interrupt type
* In the case where irq handler is not specified, the default handler shall be
* registered
*
* Return: 0 case success, error code otherwise
*/
int kbase_set_custom_irq_handler(struct kbase_device *kbdev,
irq_handler_t custom_handler,
int irq_type)
{
int result = 0;
irq_handler_t requested_irq_handler = NULL;
KBASE_DEBUG_ASSERT((JOB_IRQ_HANDLER <= irq_type) &&
(GPU_IRQ_HANDLER >= irq_type));
/* Release previous handler */
if (kbdev->irqs[irq_type].irq)
free_irq(kbdev->irqs[irq_type].irq, kbase_tag(kbdev, irq_type));
requested_irq_handler = (NULL != custom_handler) ? custom_handler :
kbase_handler_table[irq_type];
if (0 != request_irq(kbdev->irqs[irq_type].irq,
requested_irq_handler,
kbdev->irqs[irq_type].flags | IRQF_SHARED,
dev_name(kbdev->dev), kbase_tag(kbdev, irq_type))) {
result = -EINVAL;
dev_err(kbdev->dev, "Can't request interrupt %d (index %d)\n",
kbdev->irqs[irq_type].irq, irq_type);
#ifdef CONFIG_SPARSE_IRQ
dev_err(kbdev->dev, "You have CONFIG_SPARSE_IRQ support enabled - is the interrupt number correct for this configuration?\n");
#endif /* CONFIG_SPARSE_IRQ */
}
return result;
}
KBASE_EXPORT_TEST_API(kbase_set_custom_irq_handler);
/* test correct interrupt assigment and reception by cpu */
struct kbasep_irq_test {
struct hrtimer timer;
wait_queue_head_t wait;
int triggered;
u32 timeout;
};
static struct kbasep_irq_test kbasep_irq_test_data;
#define IRQ_TEST_TIMEOUT 500
static irqreturn_t kbase_job_irq_test_handler(int irq, void *data)
{
unsigned long flags;
struct kbase_device *kbdev = kbase_untag(data);
u32 val;
spin_lock_irqsave(&kbdev->pm.backend.gpu_powered_lock, flags);
if (!kbdev->pm.backend.gpu_powered) {
/* GPU is turned off - IRQ is not for us */
spin_unlock_irqrestore(&kbdev->pm.backend.gpu_powered_lock,
flags);
return IRQ_NONE;
}
val = kbase_reg_read(kbdev, JOB_CONTROL_REG(JOB_IRQ_STATUS), NULL);
spin_unlock_irqrestore(&kbdev->pm.backend.gpu_powered_lock, flags);
if (!val)
return IRQ_NONE;
dev_dbg(kbdev->dev, "%s: irq %d irqstatus 0x%x\n", __func__, irq, val);
kbasep_irq_test_data.triggered = 1;
wake_up(&kbasep_irq_test_data.wait);
kbase_reg_write(kbdev, JOB_CONTROL_REG(JOB_IRQ_CLEAR), val, NULL);
return IRQ_HANDLED;
}
static irqreturn_t kbase_mmu_irq_test_handler(int irq, void *data)
{
unsigned long flags;
struct kbase_device *kbdev = kbase_untag(data);
u32 val;
spin_lock_irqsave(&kbdev->pm.backend.gpu_powered_lock, flags);
if (!kbdev->pm.backend.gpu_powered) {
/* GPU is turned off - IRQ is not for us */
spin_unlock_irqrestore(&kbdev->pm.backend.gpu_powered_lock,
flags);
return IRQ_NONE;
}
val = kbase_reg_read(kbdev, MMU_REG(MMU_IRQ_STATUS), NULL);
spin_unlock_irqrestore(&kbdev->pm.backend.gpu_powered_lock, flags);
if (!val)
return IRQ_NONE;
dev_dbg(kbdev->dev, "%s: irq %d irqstatus 0x%x\n", __func__, irq, val);
kbasep_irq_test_data.triggered = 1;
wake_up(&kbasep_irq_test_data.wait);
kbase_reg_write(kbdev, MMU_REG(MMU_IRQ_CLEAR), val, NULL);
return IRQ_HANDLED;
}
static enum hrtimer_restart kbasep_test_interrupt_timeout(struct hrtimer *timer)
{
struct kbasep_irq_test *test_data = container_of(timer,
struct kbasep_irq_test, timer);
test_data->timeout = 1;
test_data->triggered = 1;
wake_up(&test_data->wait);
return HRTIMER_NORESTART;
}
static int kbasep_common_test_interrupt(
struct kbase_device * const kbdev, u32 tag)
{
int err = 0;
irq_handler_t test_handler;
u32 old_mask_val;
u16 mask_offset;
u16 rawstat_offset;
switch (tag) {
case JOB_IRQ_TAG:
test_handler = kbase_job_irq_test_handler;
rawstat_offset = JOB_CONTROL_REG(JOB_IRQ_RAWSTAT);
mask_offset = JOB_CONTROL_REG(JOB_IRQ_MASK);
break;
case MMU_IRQ_TAG:
test_handler = kbase_mmu_irq_test_handler;
rawstat_offset = MMU_REG(MMU_IRQ_RAWSTAT);
mask_offset = MMU_REG(MMU_IRQ_MASK);
break;
case GPU_IRQ_TAG:
/* already tested by pm_driver - bail out */
default:
return 0;
}
/* store old mask */
old_mask_val = kbase_reg_read(kbdev, mask_offset, NULL);
/* mask interrupts */
kbase_reg_write(kbdev, mask_offset, 0x0, NULL);
if (kbdev->irqs[tag].irq) {
/* release original handler and install test handler */
if (kbase_set_custom_irq_handler(kbdev, test_handler, tag) != 0) {
err = -EINVAL;
} else {
kbasep_irq_test_data.timeout = 0;
hrtimer_init(&kbasep_irq_test_data.timer,
CLOCK_MONOTONIC, HRTIMER_MODE_REL);
kbasep_irq_test_data.timer.function =
kbasep_test_interrupt_timeout;
/* trigger interrupt */
kbase_reg_write(kbdev, mask_offset, 0x1, NULL);
kbase_reg_write(kbdev, rawstat_offset, 0x1, NULL);
hrtimer_start(&kbasep_irq_test_data.timer,
HR_TIMER_DELAY_MSEC(IRQ_TEST_TIMEOUT),
HRTIMER_MODE_REL);
wait_event(kbasep_irq_test_data.wait,
kbasep_irq_test_data.triggered != 0);
if (kbasep_irq_test_data.timeout != 0) {
dev_err(kbdev->dev, "Interrupt %d (index %d) didn't reach CPU.\n",
kbdev->irqs[tag].irq, tag);
err = -EINVAL;
} else {
dev_dbg(kbdev->dev, "Interrupt %d (index %d) reached CPU.\n",
kbdev->irqs[tag].irq, tag);
}
hrtimer_cancel(&kbasep_irq_test_data.timer);
kbasep_irq_test_data.triggered = 0;
/* mask interrupts */
kbase_reg_write(kbdev, mask_offset, 0x0, NULL);
/* release test handler */
free_irq(kbdev->irqs[tag].irq, kbase_tag(kbdev, tag));
}
/* restore original interrupt */
if (request_irq(kbdev->irqs[tag].irq, kbase_handler_table[tag],
kbdev->irqs[tag].flags | IRQF_SHARED,
dev_name(kbdev->dev), kbase_tag(kbdev, tag))) {
dev_err(kbdev->dev, "Can't restore original interrupt %d (index %d)\n",
kbdev->irqs[tag].irq, tag);
err = -EINVAL;
}
}
/* restore old mask */
kbase_reg_write(kbdev, mask_offset, old_mask_val, NULL);
return err;
}
int kbasep_common_test_interrupt_handlers(
struct kbase_device * const kbdev)
{
int err;
init_waitqueue_head(&kbasep_irq_test_data.wait);
kbasep_irq_test_data.triggered = 0;
/* A suspend won't happen during startup/insmod */
kbase_pm_context_active(kbdev);
err = kbasep_common_test_interrupt(kbdev, JOB_IRQ_TAG);
if (err) {
dev_err(kbdev->dev, "Interrupt JOB_IRQ didn't reach CPU. Check interrupt assignments.\n");
goto out;
}
err = kbasep_common_test_interrupt(kbdev, MMU_IRQ_TAG);
if (err) {
dev_err(kbdev->dev, "Interrupt MMU_IRQ didn't reach CPU. Check interrupt assignments.\n");
goto out;
}
dev_dbg(kbdev->dev, "Interrupts are correctly assigned.\n");
out:
kbase_pm_context_idle(kbdev);
return err;
}
#endif /* CONFIG_MALI_DEBUG */
int kbase_install_interrupts(struct kbase_device *kbdev)
{
u32 nr = ARRAY_SIZE(kbase_handler_table);
int err;
u32 i;
for (i = 0; i < nr; i++) {
err = request_irq(kbdev->irqs[i].irq, kbase_handler_table[i],
kbdev->irqs[i].flags | IRQF_SHARED,
dev_name(kbdev->dev),
kbase_tag(kbdev, i));
if (err) {
dev_err(kbdev->dev, "Can't request interrupt %d (index %d)\n",
kbdev->irqs[i].irq, i);
#ifdef CONFIG_SPARSE_IRQ
dev_err(kbdev->dev, "You have CONFIG_SPARSE_IRQ support enabled - is the interrupt number correct for this configuration?\n");
#endif /* CONFIG_SPARSE_IRQ */
goto release;
}
}
return 0;
release:
while (i-- > 0)
free_irq(kbdev->irqs[i].irq, kbase_tag(kbdev, i));
return err;
}
void kbase_release_interrupts(struct kbase_device *kbdev)
{
u32 nr = ARRAY_SIZE(kbase_handler_table);
u32 i;
for (i = 0; i < nr; i++) {
if (kbdev->irqs[i].irq)
free_irq(kbdev->irqs[i].irq, kbase_tag(kbdev, i));
}
}
void kbase_synchronize_irqs(struct kbase_device *kbdev)
{
u32 nr = ARRAY_SIZE(kbase_handler_table);
u32 i;
for (i = 0; i < nr; i++) {
if (kbdev->irqs[i].irq)
synchronize_irq(kbdev->irqs[i].irq);
}
}
#endif /* !defined(CONFIG_MALI_NO_MALI) */

View file

@ -0,0 +1,385 @@
/*
*
* (C) COPYRIGHT 2014-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Register backend context / address space management
*/
#include <mali_kbase.h>
#include <mali_kbase_hwaccess_jm.h>
/**
* assign_and_activate_kctx_addr_space - Assign an AS to a context
* @kbdev: Kbase device
* @kctx: Kbase context
* @current_as: Address Space to assign
*
* Assign an Address Space (AS) to a context, and add the context to the Policy.
*
* This includes
* setting up the global runpool_irq structure and the context on the AS,
* Activating the MMU on the AS,
* Allowing jobs to be submitted on the AS.
*
* Context:
* kbasep_js_kctx_info.jsctx_mutex held,
* kbasep_js_device_data.runpool_mutex held,
* AS transaction mutex held,
* Runpool IRQ lock held
*/
static void assign_and_activate_kctx_addr_space(struct kbase_device *kbdev,
struct kbase_context *kctx,
struct kbase_as *current_as)
{
struct kbasep_js_device_data *js_devdata = &kbdev->js_data;
struct kbasep_js_per_as_data *js_per_as_data;
int as_nr = current_as->number;
lockdep_assert_held(&kctx->jctx.sched_info.ctx.jsctx_mutex);
lockdep_assert_held(&js_devdata->runpool_mutex);
lockdep_assert_held(&current_as->transaction_mutex);
lockdep_assert_held(&js_devdata->runpool_irq.lock);
js_per_as_data = &js_devdata->runpool_irq.per_as_data[as_nr];
/* Attribute handling */
kbasep_js_ctx_attr_runpool_retain_ctx(kbdev, kctx);
/* Assign addr space */
kctx->as_nr = as_nr;
/* If the GPU is currently powered, activate this address space on the
* MMU */
if (kbdev->pm.backend.gpu_powered)
kbase_mmu_update(kctx);
/* If the GPU was not powered then the MMU will be reprogrammed on the
* next pm_context_active() */
/* Allow it to run jobs */
kbasep_js_set_submit_allowed(js_devdata, kctx);
/* Book-keeping */
js_per_as_data->kctx = kctx;
js_per_as_data->as_busy_refcount = 0;
kbase_js_runpool_inc_context_count(kbdev, kctx);
}
/**
* release_addr_space - Release an address space
* @kbdev: Kbase device
* @kctx_as_nr: Address space of context to release
* @kctx: Context being released
*
* Context: kbasep_js_device_data.runpool_mutex must be held
*
* Release an address space, making it available for being picked again.
*/
static void release_addr_space(struct kbase_device *kbdev, int kctx_as_nr,
struct kbase_context *kctx)
{
struct kbasep_js_device_data *js_devdata;
u16 as_bit = (1u << kctx_as_nr);
js_devdata = &kbdev->js_data;
lockdep_assert_held(&js_devdata->runpool_mutex);
/* The address space must not already be free */
KBASE_DEBUG_ASSERT(!(js_devdata->as_free & as_bit));
js_devdata->as_free |= as_bit;
kbase_js_runpool_dec_context_count(kbdev, kctx);
}
bool kbase_backend_use_ctx_sched(struct kbase_device *kbdev,
struct kbase_context *kctx)
{
int i;
if (kbdev->hwaccess.active_kctx == kctx) {
/* Context is already active */
return true;
}
for (i = 0; i < kbdev->nr_hw_address_spaces; i++) {
struct kbasep_js_per_as_data *js_per_as_data =
&kbdev->js_data.runpool_irq.per_as_data[i];
if (js_per_as_data->kctx == kctx) {
/* Context already has ASID - mark as active */
return true;
}
}
/* Context does not have address space assigned */
return false;
}
void kbase_backend_release_ctx_irq(struct kbase_device *kbdev,
struct kbase_context *kctx)
{
struct kbasep_js_per_as_data *js_per_as_data;
int as_nr = kctx->as_nr;
if (as_nr == KBASEP_AS_NR_INVALID) {
WARN(1, "Attempting to release context without ASID\n");
return;
}
lockdep_assert_held(&kbdev->as[as_nr].transaction_mutex);
lockdep_assert_held(&kbdev->js_data.runpool_irq.lock);
js_per_as_data = &kbdev->js_data.runpool_irq.per_as_data[kctx->as_nr];
if (js_per_as_data->as_busy_refcount != 0) {
WARN(1, "Attempting to release active ASID\n");
return;
}
/* Release context from address space */
js_per_as_data->kctx = NULL;
kbasep_js_clear_submit_allowed(&kbdev->js_data, kctx);
/* If the GPU is currently powered, de-activate this address space on
* the MMU */
if (kbdev->pm.backend.gpu_powered)
kbase_mmu_disable(kctx);
/* If the GPU was not powered then the MMU will be reprogrammed on the
* next pm_context_active() */
release_addr_space(kbdev, as_nr, kctx);
kctx->as_nr = KBASEP_AS_NR_INVALID;
}
void kbase_backend_release_ctx_noirq(struct kbase_device *kbdev,
struct kbase_context *kctx)
{
}
void kbase_backend_release_free_address_space(struct kbase_device *kbdev,
int as_nr)
{
struct kbasep_js_device_data *js_devdata;
js_devdata = &kbdev->js_data;
lockdep_assert_held(&js_devdata->runpool_mutex);
js_devdata->as_free |= (1 << as_nr);
}
/**
* check_is_runpool_full - check whether the runpool is full for a specified
* context
* @kbdev: Kbase device
* @kctx: Kbase context
*
* If kctx == NULL, then this makes the least restrictive check on the
* runpool. A specific context that is supplied immediately after could fail
* the check, even under the same conditions.
*
* Therefore, once a context is obtained you \b must re-check it with this
* function, since the return value could change to false.
*
* Context:
* In all cases, the caller must hold kbasep_js_device_data.runpool_mutex.
* When kctx != NULL the caller must hold the
* kbasep_js_kctx_info.ctx.jsctx_mutex.
* When kctx == NULL, then the caller need not hold any jsctx_mutex locks (but
* it doesn't do any harm to do so).
*
* Return: true if the runpool is full
*/
static bool check_is_runpool_full(struct kbase_device *kbdev,
struct kbase_context *kctx)
{
struct kbasep_js_device_data *js_devdata;
bool is_runpool_full;
js_devdata = &kbdev->js_data;
lockdep_assert_held(&js_devdata->runpool_mutex);
/* Regardless of whether a context is submitting or not, can't have more
* than there are HW address spaces */
is_runpool_full = (bool) (js_devdata->nr_all_contexts_running >=
kbdev->nr_hw_address_spaces);
if (kctx != NULL && (kctx->jctx.sched_info.ctx.flags &
KBASE_CTX_FLAG_SUBMIT_DISABLED) == 0) {
lockdep_assert_held(&kctx->jctx.sched_info.ctx.jsctx_mutex);
/* Contexts that submit might use less of the address spaces
* available, due to HW workarounds. In which case, the runpool
* is also full when the number of submitting contexts exceeds
* the number of submittable address spaces.
*
* Both checks must be made: can have nr_user_address_spaces ==
* nr_hw_address spaces, and at the same time can have
* nr_user_contexts_running < nr_all_contexts_running. */
is_runpool_full |= (bool)
(js_devdata->nr_user_contexts_running >=
kbdev->nr_user_address_spaces);
}
return is_runpool_full;
}
int kbase_backend_find_free_address_space(struct kbase_device *kbdev,
struct kbase_context *kctx)
{
struct kbasep_js_device_data *js_devdata;
struct kbasep_js_kctx_info *js_kctx_info;
unsigned long flags;
int i;
js_devdata = &kbdev->js_data;
js_kctx_info = &kctx->jctx.sched_info;
mutex_lock(&js_kctx_info->ctx.jsctx_mutex);
mutex_lock(&js_devdata->runpool_mutex);
/* First try to find a free address space */
if (check_is_runpool_full(kbdev, kctx))
i = -1;
else
i = ffs(js_devdata->as_free) - 1;
if (i >= 0 && i < kbdev->nr_hw_address_spaces) {
js_devdata->as_free &= ~(1 << i);
mutex_unlock(&js_devdata->runpool_mutex);
mutex_unlock(&js_kctx_info->ctx.jsctx_mutex);
return i;
}
spin_lock_irqsave(&js_devdata->runpool_irq.lock, flags);
/* No address space currently free, see if we can release one */
for (i = 0; i < kbdev->nr_hw_address_spaces; i++) {
struct kbasep_js_per_as_data *js_per_as_data;
struct kbasep_js_kctx_info *as_js_kctx_info;
struct kbase_context *as_kctx;
js_per_as_data = &kbdev->js_data.runpool_irq.per_as_data[i];
as_kctx = js_per_as_data->kctx;
as_js_kctx_info = &as_kctx->jctx.sched_info;
/* Don't release privileged or active contexts, or contexts with
* jobs running */
if (as_kctx && !(as_kctx->jctx.sched_info.ctx.flags &
KBASE_CTX_FLAG_PRIVILEGED) &&
js_per_as_data->as_busy_refcount == 0) {
if (!kbasep_js_runpool_retain_ctx_nolock(kbdev,
as_kctx)) {
WARN(1, "Failed to retain active context\n");
spin_unlock_irqrestore(
&js_devdata->runpool_irq.lock,
flags);
mutex_unlock(&js_devdata->runpool_mutex);
mutex_unlock(&js_kctx_info->ctx.jsctx_mutex);
return KBASEP_AS_NR_INVALID;
}
kbasep_js_clear_submit_allowed(js_devdata, as_kctx);
/* Drop and retake locks to take the jsctx_mutex on the
* context we're about to release without violating lock
* ordering
*/
spin_unlock_irqrestore(&js_devdata->runpool_irq.lock,
flags);
mutex_unlock(&js_devdata->runpool_mutex);
mutex_unlock(&js_kctx_info->ctx.jsctx_mutex);
/* Release context from address space */
mutex_lock(&as_js_kctx_info->ctx.jsctx_mutex);
mutex_lock(&js_devdata->runpool_mutex);
kbasep_js_runpool_release_ctx_nolock(kbdev, as_kctx);
if (!as_js_kctx_info->ctx.is_scheduled) {
kbasep_js_runpool_requeue_or_kill_ctx(kbdev,
as_kctx,
true);
js_devdata->as_free &= ~(1 << i);
mutex_unlock(&js_devdata->runpool_mutex);
mutex_unlock(&as_js_kctx_info->ctx.jsctx_mutex);
return i;
}
/* Context was retained while locks were dropped,
* continue looking for free AS */
mutex_unlock(&js_devdata->runpool_mutex);
mutex_unlock(&as_js_kctx_info->ctx.jsctx_mutex);
mutex_lock(&js_kctx_info->ctx.jsctx_mutex);
mutex_lock(&js_devdata->runpool_mutex);
spin_lock_irqsave(&js_devdata->runpool_irq.lock, flags);
}
}
spin_unlock_irqrestore(&js_devdata->runpool_irq.lock, flags);
mutex_unlock(&js_devdata->runpool_mutex);
mutex_unlock(&js_kctx_info->ctx.jsctx_mutex);
return KBASEP_AS_NR_INVALID;
}
bool kbase_backend_use_ctx(struct kbase_device *kbdev,
struct kbase_context *kctx,
int as_nr)
{
struct kbasep_js_device_data *js_devdata;
struct kbasep_js_kctx_info *js_kctx_info;
struct kbase_as *new_address_space = NULL;
js_devdata = &kbdev->js_data;
js_kctx_info = &kctx->jctx.sched_info;
if (kbdev->hwaccess.active_kctx == kctx ||
kctx->as_nr != KBASEP_AS_NR_INVALID ||
as_nr == KBASEP_AS_NR_INVALID) {
WARN(1, "Invalid parameters to use_ctx()\n");
return false;
}
new_address_space = &kbdev->as[as_nr];
lockdep_assert_held(&js_devdata->runpool_mutex);
lockdep_assert_held(&new_address_space->transaction_mutex);
lockdep_assert_held(&js_devdata->runpool_irq.lock);
assign_and_activate_kctx_addr_space(kbdev, kctx, new_address_space);
if ((js_kctx_info->ctx.flags & KBASE_CTX_FLAG_PRIVILEGED) != 0) {
/* We need to retain it to keep the corresponding address space
*/
kbasep_js_runpool_retain_ctx_nolock(kbdev, kctx);
}
return true;
}

View file

@ -0,0 +1,115 @@
/*
*
* (C) COPYRIGHT 2014-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Register-based HW access backend specific definitions
*/
#ifndef _KBASE_HWACCESS_GPU_DEFS_H_
#define _KBASE_HWACCESS_GPU_DEFS_H_
/* SLOT_RB_SIZE must be < 256 */
#define SLOT_RB_SIZE 2
#define SLOT_RB_MASK (SLOT_RB_SIZE - 1)
/**
* struct rb_entry - Ringbuffer entry
* @katom: Atom associated with this entry
*/
struct rb_entry {
struct kbase_jd_atom *katom;
};
/**
* struct slot_rb - Slot ringbuffer
* @entries: Ringbuffer entries
* @last_context: The last context to submit a job on this slot
* @read_idx: Current read index of buffer
* @write_idx: Current write index of buffer
* @job_chain_flag: Flag used to implement jobchain disambiguation
*/
struct slot_rb {
struct rb_entry entries[SLOT_RB_SIZE];
struct kbase_context *last_context;
u8 read_idx;
u8 write_idx;
u8 job_chain_flag;
};
/**
* struct kbase_backend_data - GPU backend specific data for HW access layer
* @slot_rb: Slot ringbuffers
* @rmu_workaround_flag: When PRLAM-8987 is present, this flag determines
* whether slots 0/1 or slot 2 are currently being
* pulled from
* @scheduling_timer: The timer tick used for rescheduling jobs
* @timer_running: Is the timer running? The runpool_mutex must be
* held whilst modifying this.
* @reset_gpu: Set to a KBASE_RESET_xxx value (see comments)
* @reset_workq: Work queue for performing the reset
* @reset_work: Work item for performing the reset
* @reset_wait: Wait event signalled when the reset is complete
* @reset_timer: Timeout for soft-stops before the reset
*
* The kbasep_js_device_data::runpool_irq::lock (a spinlock) must be held when
* accessing this structure
*/
struct kbase_backend_data {
struct slot_rb slot_rb[BASE_JM_MAX_NR_SLOTS];
bool rmu_workaround_flag;
struct hrtimer scheduling_timer;
bool timer_running;
atomic_t reset_gpu;
/* The GPU reset isn't pending */
#define KBASE_RESET_GPU_NOT_PENDING 0
/* kbase_prepare_to_reset_gpu has been called */
#define KBASE_RESET_GPU_PREPARED 1
/* kbase_reset_gpu has been called - the reset will now definitely happen
* within the timeout period */
#define KBASE_RESET_GPU_COMMITTED 2
/* The GPU reset process is currently occuring (timeout has expired or
* kbasep_try_reset_gpu_early was called) */
#define KBASE_RESET_GPU_HAPPENING 3
struct workqueue_struct *reset_workq;
struct work_struct reset_work;
wait_queue_head_t reset_wait;
struct hrtimer reset_timer;
};
/**
* struct kbase_jd_atom_backend - GPU backend specific katom data
*/
struct kbase_jd_atom_backend {
};
/**
* struct kbase_context_backend - GPU backend specific context data
*/
struct kbase_context_backend {
};
#endif /* _KBASE_HWACCESS_GPU_DEFS_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,155 @@
/*
*
* (C) COPYRIGHT 2011-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Job Manager backend-specific low-level APIs.
*/
#ifndef _KBASE_JM_HWACCESS_H_
#define _KBASE_JM_HWACCESS_H_
#include <mali_kbase_hw.h>
#include <mali_kbase_debug.h>
#include <linux/atomic.h>
#include <backend/gpu/mali_kbase_jm_rb.h>
/**
* kbase_job_submit_nolock() - Submit a job to a certain job-slot
* @kbdev: Device pointer
* @katom: Atom to submit
* @js: Job slot to submit on
*
* The caller must check kbasep_jm_is_submit_slots_free() != false before
* calling this.
*
* The following locking conditions are made on the caller:
* - it must hold the kbasep_js_device_data::runpoool_irq::lock
*/
void kbase_job_submit_nolock(struct kbase_device *kbdev,
struct kbase_jd_atom *katom, int js);
/**
* kbase_job_done_slot() - Complete the head job on a particular job-slot
* @kbdev: Device pointer
* @s: Job slot
* @completion_code: Completion code of job reported by GPU
* @job_tail: Job tail address reported by GPU
* @end_timestamp: Timestamp of job completion
*/
void kbase_job_done_slot(struct kbase_device *kbdev, int s, u32 completion_code,
u64 job_tail, ktime_t *end_timestamp);
#ifdef CONFIG_GPU_TRACEPOINTS
static inline char *kbasep_make_job_slot_string(int js, char *js_string)
{
sprintf(js_string, "job_slot_%i", js);
return js_string;
}
#endif
/**
* kbase_job_hw_submit() - Submit a job to the GPU
* @kbdev: Device pointer
* @katom: Atom to submit
* @js: Job slot to submit on
*
* The caller must check kbasep_jm_is_submit_slots_free() != false before
* calling this.
*
* The following locking conditions are made on the caller:
* - it must hold the kbasep_js_device_data::runpoool_irq::lock
*/
void kbase_job_hw_submit(struct kbase_device *kbdev,
struct kbase_jd_atom *katom,
int js);
/**
* kbasep_job_slot_soft_or_hard_stop_do_action() - Perform a soft or hard stop
* on the specified atom
* @kbdev: Device pointer
* @js: Job slot to stop on
* @action: The action to perform, either JSn_COMMAND_HARD_STOP or
* JSn_COMMAND_SOFT_STOP
* @core_reqs: Core requirements of atom to stop
* @target_katom: Atom to stop
*
* The following locking conditions are made on the caller:
* - it must hold the kbasep_js_device_data::runpool_irq::lock
*/
void kbasep_job_slot_soft_or_hard_stop_do_action(struct kbase_device *kbdev,
int js,
u32 action,
u16 core_reqs,
struct kbase_jd_atom *target_katom);
/**
* kbase_backend_soft_hard_stop_slot() - Soft or hard stop jobs on a given job
* slot belonging to a given context.
* @kbdev: Device pointer
* @kctx: Context pointer. May be NULL
* @katom: Specific atom to stop. May be NULL
* @js: Job slot to hard stop
* @action: The action to perform, either JSn_COMMAND_HARD_STOP or
* JSn_COMMAND_SOFT_STOP
*
* If no context is provided then all jobs on the slot will be soft or hard
* stopped.
*
* If a katom is provided then only that specific atom will be stopped. In this
* case the kctx parameter is ignored.
*
* Jobs that are on the slot but are not yet on the GPU will be unpulled and
* returned to the job scheduler.
*
* Return: true if an atom was stopped, false otherwise
*/
bool kbase_backend_soft_hard_stop_slot(struct kbase_device *kbdev,
struct kbase_context *kctx,
int js,
struct kbase_jd_atom *katom,
u32 action);
/**
* kbase_job_slot_init - Initialise job slot framework
* @kbdev: Device pointer
*
* Called on driver initialisation
*
* Return: 0 on success
*/
int kbase_job_slot_init(struct kbase_device *kbdev);
/**
* kbase_job_slot_halt - Halt the job slot framework
* @kbdev: Device pointer
*
* Should prevent any further job slot processing
*/
void kbase_job_slot_halt(struct kbase_device *kbdev);
/**
* kbase_job_slot_term - Terminate job slot framework
* @kbdev: Device pointer
*
* Called on driver termination
*/
void kbase_job_slot_term(struct kbase_device *kbdev);
#endif /* _KBASE_JM_HWACCESS_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,87 @@
/*
*
* (C) COPYRIGHT 2014-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Register-based HW access backend specific APIs
*/
#ifndef _KBASE_HWACCESS_GPU_H_
#define _KBASE_HWACCESS_GPU_H_
#include <backend/gpu/mali_kbase_pm_internal.h>
/**
* kbase_gpu_irq_evict - Evict an atom from a NEXT slot
*
* @kbdev: Device pointer
* @js: Job slot to evict from
*
* Evict the atom in the NEXT slot for the specified job slot. This function is
* called from the job complete IRQ handler when the previous job has failed.
*
* Return: true if job evicted from NEXT registers, false otherwise
*/
bool kbase_gpu_irq_evict(struct kbase_device *kbdev, int js);
/**
* kbase_gpu_complete_hw - Complete an atom on job slot js
*
* @kbdev: Device pointer
* @js: Job slot that has completed
* @completion_code: Event code from job that has completed
* @job_tail: The tail address from the hardware if the job has partially
* completed
* @end_timestamp: Time of completion
*/
void kbase_gpu_complete_hw(struct kbase_device *kbdev, int js,
u32 completion_code,
u64 job_tail,
ktime_t *end_timestamp);
/**
* kbase_gpu_inspect - Inspect the contents of the HW access ringbuffer
*
* @kbdev: Device pointer
* @js: Job slot to inspect
* @idx: Index into ringbuffer. 0 is the job currently running on
* the slot, 1 is the job waiting, all other values are invalid.
* Return: The atom at that position in the ringbuffer
* or NULL if no atom present
*/
struct kbase_jd_atom *kbase_gpu_inspect(struct kbase_device *kbdev, int js,
int idx);
/**
* kbase_gpu_slot_update - Update state based on slot ringbuffers
*
* @kbdev: Device pointer
*
* Inspect the jobs in the slot ringbuffers and update state.
*
* This will cause jobs to be submitted to hardware if they are unblocked
*/
void kbase_gpu_slot_update(struct kbase_device *kbdev);
/**
* kbase_gpu_dump_slots - Print the contents of the slot ringbuffers
*
* @kbdev: Device pointer
*/
void kbase_gpu_dump_slots(struct kbase_device *kbdev);
#endif /* _KBASE_HWACCESS_GPU_H_ */

View file

@ -0,0 +1,295 @@
/*
*
* (C) COPYRIGHT 2010-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Base kernel affinity manager APIs
*/
#include <mali_kbase.h>
#include "mali_kbase_js_affinity.h"
#include <backend/gpu/mali_kbase_pm_internal.h>
bool kbase_js_can_run_job_on_slot_no_lock(struct kbase_device *kbdev,
int js)
{
/*
* Here are the reasons for using job slot 2:
* - BASE_HW_ISSUE_8987 (which is entirely used for that purpose)
* - In absence of the above, then:
* - Atoms with BASE_JD_REQ_COHERENT_GROUP
* - But, only when there aren't contexts with
* KBASEP_JS_CTX_ATTR_COMPUTE_ALL_CORES, because the atoms that run on
* all cores on slot 1 could be blocked by those using a coherent group
* on slot 2
* - And, only when you actually have 2 or more coregroups - if you
* only have 1 coregroup, then having jobs for slot 2 implies they'd
* also be for slot 1, meaning you'll get interference from them. Jobs
* able to run on slot 2 could also block jobs that can only run on
* slot 1 (tiler jobs)
*/
if (kbase_hw_has_issue(kbdev, BASE_HW_ISSUE_8987))
return true;
if (js != 2)
return true;
/* Only deal with js==2 now: */
if (kbdev->gpu_props.num_core_groups > 1) {
/* Only use slot 2 in the 2+ coregroup case */
if (kbasep_js_ctx_attr_is_attr_on_runpool(kbdev,
KBASEP_JS_CTX_ATTR_COMPUTE_ALL_CORES) ==
false) {
/* ...But only when we *don't* have atoms that run on
* all cores */
/* No specific check for BASE_JD_REQ_COHERENT_GROUP
* atoms - the policy will sort that out */
return true;
}
}
/* Above checks failed mean we shouldn't use slot 2 */
return false;
}
/*
* As long as it has been decided to have a deeper modification of
* what job scheduler, power manager and affinity manager will
* implement, this function is just an intermediate step that
* assumes:
* - all working cores will be powered on when this is called.
* - largest current configuration is 2 core groups.
* - It has been decided not to have hardcoded values so the low
* and high cores in a core split will be evently distributed.
* - Odd combinations of core requirements have been filtered out
* and do not get to this function (e.g. CS+T+NSS is not
* supported here).
* - This function is frequently called and can be optimized,
* (see notes in loops), but as the functionallity will likely
* be modified, optimization has not been addressed.
*/
bool kbase_js_choose_affinity(u64 * const affinity,
struct kbase_device *kbdev,
struct kbase_jd_atom *katom, int js)
{
base_jd_core_req core_req = katom->core_req;
unsigned int num_core_groups = kbdev->gpu_props.num_core_groups;
u64 core_availability_mask;
unsigned long flags;
spin_lock_irqsave(&kbdev->pm.power_change_lock, flags);
core_availability_mask = kbase_pm_ca_get_core_mask(kbdev);
/*
* If no cores are currently available (core availability policy is
* transitioning) then fail.
*/
if (0 == core_availability_mask) {
spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags);
*affinity = 0;
return false;
}
KBASE_DEBUG_ASSERT(js >= 0);
if ((core_req & (BASE_JD_REQ_FS | BASE_JD_REQ_CS | BASE_JD_REQ_T)) ==
BASE_JD_REQ_T) {
spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags);
/* Tiler only job, bit 0 needed to enable tiler but no shader
* cores required */
*affinity = 1;
return true;
}
if (1 == kbdev->gpu_props.num_cores) {
/* trivial case only one core, nothing to do */
*affinity = core_availability_mask;
} else {
if ((core_req & (BASE_JD_REQ_COHERENT_GROUP |
BASE_JD_REQ_SPECIFIC_COHERENT_GROUP))) {
if (js == 0 || num_core_groups == 1) {
/* js[0] and single-core-group systems just get
* the first core group */
*affinity =
kbdev->gpu_props.props.coherency_info.group[0].core_mask
& core_availability_mask;
} else {
/* js[1], js[2] use core groups 0, 1 for
* dual-core-group systems */
u32 core_group_idx = ((u32) js) - 1;
KBASE_DEBUG_ASSERT(core_group_idx <
num_core_groups);
*affinity =
kbdev->gpu_props.props.coherency_info.group[core_group_idx].core_mask
& core_availability_mask;
/* If the job is specifically targeting core
* group 1 and the core availability policy is
* keeping that core group off, then fail */
if (*affinity == 0 && core_group_idx == 1 &&
kbdev->pm.backend.cg1_disabled
== true)
katom->event_code =
BASE_JD_EVENT_PM_EVENT;
}
} else {
/* All cores are available when no core split is
* required */
*affinity = core_availability_mask;
}
}
spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags);
/*
* If no cores are currently available in the desired core group(s)
* (core availability policy is transitioning) then fail.
*/
if (*affinity == 0)
return false;
/* Enable core 0 if tiler required */
if (core_req & BASE_JD_REQ_T)
*affinity = *affinity | 1;
return true;
}
static inline bool kbase_js_affinity_is_violating(
struct kbase_device *kbdev,
u64 *affinities)
{
/* This implementation checks whether the two slots involved in Generic
* thread creation have intersecting affinity. This is due to micro-
* architectural issues where a job in slot A targetting cores used by
* slot B could prevent the job in slot B from making progress until the
* job in slot A has completed.
*/
u64 affinity_set_left;
u64 affinity_set_right;
u64 intersection;
KBASE_DEBUG_ASSERT(affinities != NULL);
affinity_set_left = affinities[1];
affinity_set_right = affinities[2];
/* A violation occurs when any bit in the left_set is also in the
* right_set */
intersection = affinity_set_left & affinity_set_right;
return (bool) (intersection != (u64) 0u);
}
bool kbase_js_affinity_would_violate(struct kbase_device *kbdev, int js,
u64 affinity)
{
struct kbasep_js_device_data *js_devdata;
u64 new_affinities[BASE_JM_MAX_NR_SLOTS];
KBASE_DEBUG_ASSERT(kbdev != NULL);
KBASE_DEBUG_ASSERT(js < BASE_JM_MAX_NR_SLOTS);
js_devdata = &kbdev->js_data;
memcpy(new_affinities, js_devdata->runpool_irq.slot_affinities,
sizeof(js_devdata->runpool_irq.slot_affinities));
new_affinities[js] |= affinity;
return kbase_js_affinity_is_violating(kbdev, new_affinities);
}
void kbase_js_affinity_retain_slot_cores(struct kbase_device *kbdev, int js,
u64 affinity)
{
struct kbasep_js_device_data *js_devdata;
u64 cores;
KBASE_DEBUG_ASSERT(kbdev != NULL);
KBASE_DEBUG_ASSERT(js < BASE_JM_MAX_NR_SLOTS);
js_devdata = &kbdev->js_data;
KBASE_DEBUG_ASSERT(kbase_js_affinity_would_violate(kbdev, js, affinity)
== false);
cores = affinity;
while (cores) {
int bitnum = fls64(cores) - 1;
u64 bit = 1ULL << bitnum;
s8 cnt;
cnt =
++(js_devdata->runpool_irq.slot_affinity_refcount[js][bitnum]);
if (cnt == 1)
js_devdata->runpool_irq.slot_affinities[js] |= bit;
cores &= ~bit;
}
}
void kbase_js_affinity_release_slot_cores(struct kbase_device *kbdev, int js,
u64 affinity)
{
struct kbasep_js_device_data *js_devdata;
u64 cores;
KBASE_DEBUG_ASSERT(kbdev != NULL);
KBASE_DEBUG_ASSERT(js < BASE_JM_MAX_NR_SLOTS);
js_devdata = &kbdev->js_data;
cores = affinity;
while (cores) {
int bitnum = fls64(cores) - 1;
u64 bit = 1ULL << bitnum;
s8 cnt;
KBASE_DEBUG_ASSERT(
js_devdata->runpool_irq.slot_affinity_refcount[js][bitnum] > 0);
cnt =
--(js_devdata->runpool_irq.slot_affinity_refcount[js][bitnum]);
if (0 == cnt)
js_devdata->runpool_irq.slot_affinities[js] &= ~bit;
cores &= ~bit;
}
}
#if KBASE_TRACE_ENABLE
void kbase_js_debug_log_current_affinities(struct kbase_device *kbdev)
{
struct kbasep_js_device_data *js_devdata;
int slot_nr;
KBASE_DEBUG_ASSERT(kbdev != NULL);
js_devdata = &kbdev->js_data;
for (slot_nr = 0; slot_nr < 3; ++slot_nr)
KBASE_TRACE_ADD_SLOT_INFO(kbdev, JS_AFFINITY_CURRENT, NULL,
NULL, 0u, slot_nr,
(u32) js_devdata->runpool_irq.slot_affinities[slot_nr]);
}
#endif /* KBASE_TRACE_ENABLE */

View file

@ -0,0 +1,138 @@
/*
*
* (C) COPYRIGHT 2011-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Affinity Manager internal APIs.
*/
#ifndef _KBASE_JS_AFFINITY_H_
#define _KBASE_JS_AFFINITY_H_
#ifdef CONFIG_MALI_DEBUG_SHADER_SPLIT_FS
/* Import the external affinity mask variables */
extern u64 mali_js0_affinity_mask;
extern u64 mali_js1_affinity_mask;
extern u64 mali_js2_affinity_mask;
#endif /* CONFIG_MALI_DEBUG_SHADER_SPLIT_FS */
/**
* kbase_js_can_run_job_on_slot_no_lock - Decide whether it is possible to
* submit a job to a particular job slot in the current status
*
* @kbdev: The kbase device structure of the device
* @js: Job slot number to check for allowance
*
* Will check if submitting to the given job slot is allowed in the current
* status. For example using job slot 2 while in soft-stoppable state and only
* having 1 coregroup is not allowed by the policy. This function should be
* called prior to submitting a job to a slot to make sure policy rules are not
* violated.
*
* The following locking conditions are made on the caller
* - it must hold kbasep_js_device_data.runpool_irq.lock
*/
bool kbase_js_can_run_job_on_slot_no_lock(struct kbase_device *kbdev,
int js);
/**
* kbase_js_choose_affinity - Compute affinity for a given job.
*
* @affinity: Affinity bitmap computed
* @kbdev: The kbase device structure of the device
* @katom: Job chain of which affinity is going to be found
* @js: Slot the job chain is being submitted
*
* Currently assumes an all-on/all-off power management policy.
* Also assumes there is at least one core with tiler available.
*
* Returns true if a valid affinity was chosen, false if
* no cores were available.
*/
bool kbase_js_choose_affinity(u64 * const affinity,
struct kbase_device *kbdev,
struct kbase_jd_atom *katom,
int js);
/**
* kbase_js_affinity_would_violate - Determine whether a proposed affinity on
* job slot @js would cause a violation of affinity restrictions.
*
* @kbdev: Kbase device structure
* @js: The job slot to test
* @affinity: The affinity mask to test
*
* The following locks must be held by the caller
* - kbasep_js_device_data.runpool_irq.lock
*
* Return: true if the affinity would violate the restrictions
*/
bool kbase_js_affinity_would_violate(struct kbase_device *kbdev, int js,
u64 affinity);
/**
* kbase_js_affinity_retain_slot_cores - Affinity tracking: retain cores used by
* a slot
*
* @kbdev: Kbase device structure
* @js: The job slot retaining the cores
* @affinity: The cores to retain
*
* The following locks must be held by the caller
* - kbasep_js_device_data.runpool_irq.lock
*/
void kbase_js_affinity_retain_slot_cores(struct kbase_device *kbdev, int js,
u64 affinity);
/**
* kbase_js_affinity_release_slot_cores - Affinity tracking: release cores used
* by a slot
*
* @kbdev: Kbase device structure
* @js: Job slot
* @affinity: Bit mask of core to be released
*
* Cores must be released as soon as a job is dequeued from a slot's 'submit
* slots', and before another job is submitted to those slots. Otherwise, the
* refcount could exceed the maximum number submittable to a slot,
* %BASE_JM_SUBMIT_SLOTS.
*
* The following locks must be held by the caller
* - kbasep_js_device_data.runpool_irq.lock
*/
void kbase_js_affinity_release_slot_cores(struct kbase_device *kbdev, int js,
u64 affinity);
/**
* kbase_js_debug_log_current_affinities - log the current affinities
*
* @kbdev: Kbase device structure
*
* Output to the Trace log the current tracked affinities on all slots
*/
#if KBASE_TRACE_ENABLE
void kbase_js_debug_log_current_affinities(struct kbase_device *kbdev);
#else /* KBASE_TRACE_ENABLE */
static inline void
kbase_js_debug_log_current_affinities(struct kbase_device *kbdev)
{
}
#endif /* KBASE_TRACE_ENABLE */
#endif /* _KBASE_JS_AFFINITY_H_ */

View file

@ -0,0 +1,319 @@
/*
*
* (C) COPYRIGHT 2014-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Register-based HW access backend specific job scheduler APIs
*/
#include <mali_kbase.h>
#include <mali_kbase_hwaccess_jm.h>
#include <backend/gpu/mali_kbase_jm_internal.h>
#include <backend/gpu/mali_kbase_js_internal.h>
/*
* Define for when dumping is enabled.
* This should not be based on the instrumentation level as whether dumping is
* enabled for a particular level is down to the integrator. However this is
* being used for now as otherwise the cinstr headers would be needed.
*/
#define CINSTR_DUMPING_ENABLED (2 == MALI_INSTRUMENTATION_LEVEL)
/*
* Hold the runpool_mutex for this
*/
static inline bool timer_callback_should_run(struct kbase_device *kbdev)
{
s8 nr_running_ctxs;
lockdep_assert_held(&kbdev->js_data.runpool_mutex);
/* nr_contexts_pullable is updated with the runpool_mutex. However, the
* locking in the caller gives us a barrier that ensures
* nr_contexts_pullable is up-to-date for reading */
nr_running_ctxs = atomic_read(&kbdev->js_data.nr_contexts_runnable);
#ifdef CONFIG_MALI_DEBUG
if (kbdev->js_data.softstop_always) {
/* Debug support for allowing soft-stop on a single context */
return true;
}
#endif /* CONFIG_MALI_DEBUG */
if (kbase_hw_has_issue(kbdev, BASE_HW_ISSUE_9435)) {
/* Timeouts would have to be 4x longer (due to micro-
* architectural design) to support OpenCL conformance tests, so
* only run the timer when there's:
* - 2 or more CL contexts
* - 1 or more GLES contexts
*
* NOTE: We will treat a context that has both Compute and Non-
* Compute jobs will be treated as an OpenCL context (hence, we
* don't check KBASEP_JS_CTX_ATTR_NON_COMPUTE).
*/
{
s8 nr_compute_ctxs =
kbasep_js_ctx_attr_count_on_runpool(kbdev,
KBASEP_JS_CTX_ATTR_COMPUTE);
s8 nr_noncompute_ctxs = nr_running_ctxs -
nr_compute_ctxs;
return (bool) (nr_compute_ctxs >= 2 ||
nr_noncompute_ctxs > 0);
}
} else {
/* Run the timer callback whenever you have at least 1 context
*/
return (bool) (nr_running_ctxs > 0);
}
}
static enum hrtimer_restart timer_callback(struct hrtimer *timer)
{
unsigned long flags;
struct kbase_device *kbdev;
struct kbasep_js_device_data *js_devdata;
struct kbase_backend_data *backend;
int s;
bool reset_needed = false;
KBASE_DEBUG_ASSERT(timer != NULL);
backend = container_of(timer, struct kbase_backend_data,
scheduling_timer);
kbdev = container_of(backend, struct kbase_device, hwaccess.backend);
js_devdata = &kbdev->js_data;
/* Loop through the slots */
spin_lock_irqsave(&js_devdata->runpool_irq.lock, flags);
for (s = 0; s < kbdev->gpu_props.num_job_slots; s++) {
struct kbase_jd_atom *atom = NULL;
if (kbase_backend_nr_atoms_on_slot(kbdev, s) > 0) {
atom = kbase_gpu_inspect(kbdev, s, 0);
KBASE_DEBUG_ASSERT(atom != NULL);
}
if (atom != NULL) {
/* The current version of the model doesn't support
* Soft-Stop */
if (!kbase_hw_has_issue(kbdev, BASE_HW_ISSUE_5736)) {
u32 ticks = atom->sched_info.cfs.ticks++;
#if !CINSTR_DUMPING_ENABLED
u32 soft_stop_ticks, hard_stop_ticks,
gpu_reset_ticks;
if (atom->core_req & BASE_JD_REQ_ONLY_COMPUTE) {
soft_stop_ticks =
js_devdata->soft_stop_ticks_cl;
hard_stop_ticks =
js_devdata->hard_stop_ticks_cl;
gpu_reset_ticks =
js_devdata->gpu_reset_ticks_cl;
} else {
soft_stop_ticks =
js_devdata->soft_stop_ticks;
hard_stop_ticks =
js_devdata->hard_stop_ticks_ss;
gpu_reset_ticks =
js_devdata->gpu_reset_ticks_ss;
}
/* Job is Soft-Stoppable */
if (ticks == soft_stop_ticks) {
int disjoint_threshold =
KBASE_DISJOINT_STATE_INTERLEAVED_CONTEXT_COUNT_THRESHOLD;
u32 softstop_flags = 0u;
/* Job has been scheduled for at least
* js_devdata->soft_stop_ticks ticks.
* Soft stop the slot so we can run
* other jobs.
*/
dev_dbg(kbdev->dev, "Soft-stop");
#if !KBASE_DISABLE_SCHEDULING_SOFT_STOPS
/* nr_user_contexts_running is updated
* with the runpool_mutex, but we can't
* take that here.
*
* However, if it's about to be
* increased then the new context can't
* run any jobs until they take the
* runpool_irq lock, so it's OK to
* observe the older value.
*
* Similarly, if it's about to be
* decreased, the last job from another
* context has already finished, so it's
* not too bad that we observe the older
* value and register a disjoint event
* when we try soft-stopping */
if (js_devdata->nr_user_contexts_running
>= disjoint_threshold)
softstop_flags |=
JS_COMMAND_SW_CAUSES_DISJOINT;
kbase_job_slot_softstop_swflags(kbdev,
s, atom, softstop_flags);
#endif
} else if (ticks == hard_stop_ticks) {
/* Job has been scheduled for at least
* js_devdata->hard_stop_ticks_ss ticks.
* It should have been soft-stopped by
* now. Hard stop the slot.
*/
#if !KBASE_DISABLE_SCHEDULING_HARD_STOPS
int ms =
js_devdata->scheduling_period_ns
/ 1000000u;
dev_warn(kbdev->dev, "JS: Job Hard-Stopped (took more than %lu ticks at %lu ms/tick)",
(unsigned long)ticks,
(unsigned long)ms);
kbase_job_slot_hardstop(atom->kctx, s,
atom);
#endif
} else if (ticks == gpu_reset_ticks) {
/* Job has been scheduled for at least
* js_devdata->gpu_reset_ticks_ss ticks.
* It should have left the GPU by now.
* Signal that the GPU needs to be
* reset.
*/
reset_needed = true;
}
#else /* !CINSTR_DUMPING_ENABLED */
/* NOTE: During CINSTR_DUMPING_ENABLED, we use
* the alternate timeouts, which makes the hard-
* stop and GPU reset timeout much longer. We
* also ensure that we don't soft-stop at all.
*/
if (ticks == js_devdata->soft_stop_ticks) {
/* Job has been scheduled for at least
* js_devdata->soft_stop_ticks. We do
* not soft-stop during
* CINSTR_DUMPING_ENABLED, however.
*/
dev_dbg(kbdev->dev, "Soft-stop");
} else if (ticks ==
js_devdata->hard_stop_ticks_dumping) {
/* Job has been scheduled for at least
* js_devdata->hard_stop_ticks_dumping
* ticks. Hard stop the slot.
*/
#if !KBASE_DISABLE_SCHEDULING_HARD_STOPS
int ms =
js_devdata->scheduling_period_ns
/ 1000000u;
dev_warn(kbdev->dev, "JS: Job Hard-Stopped (took more than %lu ticks at %lu ms/tick)",
(unsigned long)ticks,
(unsigned long)ms);
kbase_job_slot_hardstop(atom->kctx, s,
atom);
#endif
} else if (ticks ==
js_devdata->gpu_reset_ticks_dumping) {
/* Job has been scheduled for at least
* js_devdata->gpu_reset_ticks_dumping
* ticks. It should have left the GPU by
* now. Signal that the GPU needs to be
* reset.
*/
reset_needed = true;
}
#endif /* !CINSTR_DUMPING_ENABLED */
}
}
}
#if KBASE_GPU_RESET_EN
if (reset_needed) {
dev_err(kbdev->dev, "JS: Job has been on the GPU for too long (JS_RESET_TICKS_SS/DUMPING timeout hit). Issueing GPU soft-reset to resolve.");
if (kbase_prepare_to_reset_gpu_locked(kbdev))
kbase_reset_gpu_locked(kbdev);
}
#endif /* KBASE_GPU_RESET_EN */
/* the timer is re-issued if there is contexts in the run-pool */
if (backend->timer_running)
hrtimer_start(&backend->scheduling_timer,
HR_TIMER_DELAY_NSEC(js_devdata->scheduling_period_ns),
HRTIMER_MODE_REL);
spin_unlock_irqrestore(&js_devdata->runpool_irq.lock, flags);
return HRTIMER_NORESTART;
}
void kbase_backend_ctx_count_changed(struct kbase_device *kbdev)
{
struct kbasep_js_device_data *js_devdata = &kbdev->js_data;
struct kbase_backend_data *backend = &kbdev->hwaccess.backend;
unsigned long flags;
lockdep_assert_held(&js_devdata->runpool_mutex);
if (!timer_callback_should_run(kbdev)) {
/* Take spinlock to force synchronisation with timer */
spin_lock_irqsave(&js_devdata->runpool_irq.lock, flags);
backend->timer_running = false;
spin_unlock_irqrestore(&js_devdata->runpool_irq.lock, flags);
/* From now on, return value of timer_callback_should_run() will
* also cause the timer to not requeue itself. Its return value
* cannot change, because it depends on variables updated with
* the runpool_mutex held, which the caller of this must also
* hold */
hrtimer_cancel(&backend->scheduling_timer);
}
if (timer_callback_should_run(kbdev) && !backend->timer_running) {
/* Take spinlock to force synchronisation with timer */
spin_lock_irqsave(&js_devdata->runpool_irq.lock, flags);
backend->timer_running = true;
spin_unlock_irqrestore(&js_devdata->runpool_irq.lock, flags);
hrtimer_start(&backend->scheduling_timer,
HR_TIMER_DELAY_NSEC(js_devdata->scheduling_period_ns),
HRTIMER_MODE_REL);
KBASE_TRACE_ADD(kbdev, JS_POLICY_TIMER_START, NULL, NULL, 0u,
0u);
}
}
int kbase_backend_timer_init(struct kbase_device *kbdev)
{
struct kbase_backend_data *backend = &kbdev->hwaccess.backend;
hrtimer_init(&backend->scheduling_timer, CLOCK_MONOTONIC,
HRTIMER_MODE_REL);
backend->scheduling_timer.function = timer_callback;
#ifdef CONFIG_SCHED_HMP
backend->scheduling_timer.bounded_to_boot_cluster = true;
#endif
backend->timer_running = false;
return 0;
}
void kbase_backend_timer_term(struct kbase_device *kbdev)
{
struct kbase_backend_data *backend = &kbdev->hwaccess.backend;
hrtimer_cancel(&backend->scheduling_timer);
}

View file

@ -0,0 +1,44 @@
/*
*
* (C) COPYRIGHT 2014-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Register-based HW access backend specific job scheduler APIs
*/
#ifndef _KBASE_JS_BACKEND_H_
#define _KBASE_JS_BACKEND_H_
/**
* kbase_backend_timer_init() - Initialise the JS scheduling timer
* @kbdev: Device pointer
*
* This function should be called at driver initialisation
*
* Return: 0 on success
*/
int kbase_backend_timer_init(struct kbase_device *kbdev);
/**
* kbase_backend_timer_term() - Terminate the JS scheduling timer
* @kbdev: Device pointer
*
* This function should be called at driver termination
*/
void kbase_backend_timer_term(struct kbase_device *kbdev);
#endif /* _KBASE_JS_BACKEND_H_ */

View file

@ -0,0 +1,316 @@
/*
*
* (C) COPYRIGHT 2014-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include <linux/bitops.h>
#include <mali_kbase.h>
#include <mali_kbase_mem.h>
#include <mali_kbase_mmu_hw.h>
#if defined(CONFIG_MALI_MIPE_ENABLED)
#include <mali_kbase_tlstream.h>
#endif
#include <backend/gpu/mali_kbase_mmu_hw_direct.h>
#include <backend/gpu/mali_kbase_device_internal.h>
static inline u64 lock_region(struct kbase_device *kbdev, u64 pfn,
u32 num_pages)
{
u64 region;
/* can't lock a zero sized range */
KBASE_DEBUG_ASSERT(num_pages);
region = pfn << PAGE_SHIFT;
/*
* fls returns (given the ASSERT above):
* 1 .. 32
*
* 10 + fls(num_pages)
* results in the range (11 .. 42)
*/
/* gracefully handle num_pages being zero */
if (0 == num_pages) {
region |= 11;
} else {
u8 region_width;
region_width = 10 + fls(num_pages);
if (num_pages != (1ul << (region_width - 11))) {
/* not pow2, so must go up to the next pow2 */
region_width += 1;
}
KBASE_DEBUG_ASSERT(region_width <= KBASE_LOCK_REGION_MAX_SIZE);
KBASE_DEBUG_ASSERT(region_width >= KBASE_LOCK_REGION_MIN_SIZE);
region |= region_width;
}
return region;
}
static int wait_ready(struct kbase_device *kbdev,
unsigned int as_nr, struct kbase_context *kctx)
{
unsigned int max_loops = KBASE_AS_INACTIVE_MAX_LOOPS;
u32 val = kbase_reg_read(kbdev, MMU_AS_REG(as_nr, AS_STATUS), kctx);
/* Wait for the MMU status to indicate there is no active command, in
* case one is pending. Do not log remaining register accesses. */
while (--max_loops && (val & AS_STATUS_AS_ACTIVE))
val = kbase_reg_read(kbdev, MMU_AS_REG(as_nr, AS_STATUS), NULL);
if (max_loops == 0) {
dev_err(kbdev->dev, "AS_ACTIVE bit stuck\n");
return -1;
}
/* If waiting in loop was performed, log last read value. */
if (KBASE_AS_INACTIVE_MAX_LOOPS - 1 > max_loops)
kbase_reg_read(kbdev, MMU_AS_REG(as_nr, AS_STATUS), kctx);
return 0;
}
static int write_cmd(struct kbase_device *kbdev, int as_nr, u32 cmd,
struct kbase_context *kctx)
{
int status;
/* write AS_COMMAND when MMU is ready to accept another command */
status = wait_ready(kbdev, as_nr, kctx);
if (status == 0)
kbase_reg_write(kbdev, MMU_AS_REG(as_nr, AS_COMMAND), cmd,
kctx);
return status;
}
void kbase_mmu_interrupt(struct kbase_device *kbdev, u32 irq_stat)
{
const int num_as = 16;
const int busfault_shift = MMU_PAGE_FAULT_FLAGS;
const int pf_shift = 0;
const unsigned long as_bit_mask = (1UL << num_as) - 1;
unsigned long flags;
u32 new_mask;
u32 tmp;
/* bus faults */
u32 bf_bits = (irq_stat >> busfault_shift) & as_bit_mask;
/* page faults (note: Ignore ASes with both pf and bf) */
u32 pf_bits = ((irq_stat >> pf_shift) & as_bit_mask) & ~bf_bits;
KBASE_DEBUG_ASSERT(NULL != kbdev);
/* remember current mask */
spin_lock_irqsave(&kbdev->mmu_mask_change, flags);
new_mask = kbase_reg_read(kbdev, MMU_REG(MMU_IRQ_MASK), NULL);
/* mask interrupts for now */
kbase_reg_write(kbdev, MMU_REG(MMU_IRQ_MASK), 0, NULL);
spin_unlock_irqrestore(&kbdev->mmu_mask_change, flags);
while (bf_bits | pf_bits) {
struct kbase_as *as;
int as_no;
struct kbase_context *kctx;
/*
* the while logic ensures we have a bit set, no need to check
* for not-found here
*/
as_no = ffs(bf_bits | pf_bits) - 1;
as = &kbdev->as[as_no];
/*
* Refcount the kctx ASAP - it shouldn't disappear anyway, since
* Bus/Page faults _should_ only occur whilst jobs are running,
* and a job causing the Bus/Page fault shouldn't complete until
* the MMU is updated
*/
kctx = kbasep_js_runpool_lookup_ctx(kbdev, as_no);
/* find faulting address */
as->fault_addr = kbase_reg_read(kbdev,
MMU_AS_REG(as_no,
AS_FAULTADDRESS_HI),
kctx);
as->fault_addr <<= 32;
as->fault_addr |= kbase_reg_read(kbdev,
MMU_AS_REG(as_no,
AS_FAULTADDRESS_LO),
kctx);
/* record the fault status */
as->fault_status = kbase_reg_read(kbdev,
MMU_AS_REG(as_no,
AS_FAULTSTATUS),
kctx);
/* find the fault type */
as->fault_type = (bf_bits & (1 << as_no)) ?
KBASE_MMU_FAULT_TYPE_BUS :
KBASE_MMU_FAULT_TYPE_PAGE;
if (kbase_as_has_bus_fault(as)) {
/* Mark bus fault as handled.
* Note that a bus fault is processed first in case
* where both a bus fault and page fault occur.
*/
bf_bits &= ~(1UL << as_no);
/* remove the queued BF (and PF) from the mask */
new_mask &= ~(MMU_BUS_ERROR(as_no) |
MMU_PAGE_FAULT(as_no));
} else {
/* Mark page fault as handled */
pf_bits &= ~(1UL << as_no);
/* remove the queued PF from the mask */
new_mask &= ~MMU_PAGE_FAULT(as_no);
}
/* Process the interrupt for this address space */
spin_lock_irqsave(&kbdev->js_data.runpool_irq.lock, flags);
kbase_mmu_interrupt_process(kbdev, kctx, as);
spin_unlock_irqrestore(&kbdev->js_data.runpool_irq.lock,
flags);
}
/* reenable interrupts */
spin_lock_irqsave(&kbdev->mmu_mask_change, flags);
tmp = kbase_reg_read(kbdev, MMU_REG(MMU_IRQ_MASK), NULL);
new_mask |= tmp;
kbase_reg_write(kbdev, MMU_REG(MMU_IRQ_MASK), new_mask, NULL);
spin_unlock_irqrestore(&kbdev->mmu_mask_change, flags);
}
void kbase_mmu_hw_configure(struct kbase_device *kbdev, struct kbase_as *as,
struct kbase_context *kctx)
{
struct kbase_mmu_setup *current_setup = &as->current_setup;
#if defined(CONFIG_MALI_MIPE_ENABLED) || \
(defined(MALI_INCLUDE_TMIX) && \
defined(CONFIG_MALI_COH_PAGES) && \
defined(CONFIG_MALI_GPU_MMU_AARCH64))
u32 transcfg = 0;
#endif
kbase_reg_write(kbdev, MMU_AS_REG(as->number, AS_TRANSTAB_LO),
current_setup->transtab & 0xFFFFFFFFUL, kctx);
kbase_reg_write(kbdev, MMU_AS_REG(as->number, AS_TRANSTAB_HI),
(current_setup->transtab >> 32) & 0xFFFFFFFFUL, kctx);
kbase_reg_write(kbdev, MMU_AS_REG(as->number, AS_MEMATTR_LO),
current_setup->memattr & 0xFFFFFFFFUL, kctx);
kbase_reg_write(kbdev, MMU_AS_REG(as->number, AS_MEMATTR_HI),
(current_setup->memattr >> 32) & 0xFFFFFFFFUL, kctx);
#if defined(CONFIG_MALI_MIPE_ENABLED)
kbase_tlstream_tl_attrib_as_config(as,
current_setup->transtab,
current_setup->memattr,
transcfg);
#endif
write_cmd(kbdev, as->number, AS_COMMAND_UPDATE, kctx);
}
int kbase_mmu_hw_do_operation(struct kbase_device *kbdev, struct kbase_as *as,
struct kbase_context *kctx, u64 vpfn, u32 nr, u32 op,
unsigned int handling_irq)
{
int ret;
if (op == AS_COMMAND_UNLOCK) {
/* Unlock doesn't require a lock first */
ret = write_cmd(kbdev, as->number, AS_COMMAND_UNLOCK, kctx);
} else {
u64 lock_addr = lock_region(kbdev, vpfn, nr);
/* Lock the region that needs to be updated */
kbase_reg_write(kbdev, MMU_AS_REG(as->number, AS_LOCKADDR_LO),
lock_addr & 0xFFFFFFFFUL, kctx);
kbase_reg_write(kbdev, MMU_AS_REG(as->number, AS_LOCKADDR_HI),
(lock_addr >> 32) & 0xFFFFFFFFUL, kctx);
write_cmd(kbdev, as->number, AS_COMMAND_LOCK, kctx);
/* Run the MMU operation */
write_cmd(kbdev, as->number, op, kctx);
/* Wait for the flush to complete */
ret = wait_ready(kbdev, as->number, kctx);
if (kbase_hw_has_issue(kbdev, BASE_HW_ISSUE_9630)) {
/* Issue an UNLOCK command to ensure that valid page
tables are re-read by the GPU after an update.
Note that, the FLUSH command should perform all the
actions necessary, however the bus logs show that if
multiple page faults occur within an 8 page region
the MMU does not always re-read the updated page
table entries for later faults or is only partially
read, it subsequently raises the page fault IRQ for
the same addresses, the unlock ensures that the MMU
cache is flushed, so updates can be re-read. As the
region is now unlocked we need to issue 2 UNLOCK
commands in order to flush the MMU/uTLB,
see PRLAM-8812.
*/
write_cmd(kbdev, as->number, AS_COMMAND_UNLOCK, kctx);
write_cmd(kbdev, as->number, AS_COMMAND_UNLOCK, kctx);
}
}
return ret;
}
void kbase_mmu_hw_clear_fault(struct kbase_device *kbdev, struct kbase_as *as,
struct kbase_context *kctx, enum kbase_mmu_fault_type type)
{
u32 pf_bf_mask;
/* Clear the page (and bus fault IRQ as well in case one occurred) */
pf_bf_mask = MMU_PAGE_FAULT(as->number);
if (type == KBASE_MMU_FAULT_TYPE_BUS ||
type == KBASE_MMU_FAULT_TYPE_BUS_UNEXPECTED)
pf_bf_mask |= MMU_BUS_ERROR(as->number);
kbase_reg_write(kbdev, MMU_REG(MMU_IRQ_CLEAR), pf_bf_mask, kctx);
}
void kbase_mmu_hw_enable_fault(struct kbase_device *kbdev, struct kbase_as *as,
struct kbase_context *kctx, enum kbase_mmu_fault_type type)
{
unsigned long flags;
u32 irq_mask;
/* Enable the page fault IRQ (and bus fault IRQ as well in case one
* occurred) */
spin_lock_irqsave(&kbdev->mmu_mask_change, flags);
irq_mask = kbase_reg_read(kbdev, MMU_REG(MMU_IRQ_MASK), kctx) |
MMU_PAGE_FAULT(as->number);
if (type == KBASE_MMU_FAULT_TYPE_BUS ||
type == KBASE_MMU_FAULT_TYPE_BUS_UNEXPECTED)
irq_mask |= MMU_BUS_ERROR(as->number);
kbase_reg_write(kbdev, MMU_REG(MMU_IRQ_MASK), irq_mask, kctx);
spin_unlock_irqrestore(&kbdev->mmu_mask_change, flags);
}

View file

@ -0,0 +1,42 @@
/*
*
* (C) COPYRIGHT 2014-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Interface file for the direct implementation for MMU hardware access
*
* Direct MMU hardware interface
*
* This module provides the interface(s) that are required by the direct
* register access implementation of the MMU hardware interface
*/
#ifndef _MALI_KBASE_MMU_HW_DIRECT_H_
#define _MALI_KBASE_MMU_HW_DIRECT_H_
#include <mali_kbase_defs.h>
/**
* kbase_mmu_interrupt - Process an MMU interrupt.
*
* Process the MMU interrupt that was reported by the &kbase_device.
*
* @kbdev: kbase context to clear the fault from.
* @irq_stat: Value of the MMU_IRQ_STATUS register
*/
void kbase_mmu_interrupt(struct kbase_device *kbdev, u32 irq_stat);
#endif /* _MALI_KBASE_MMU_HW_DIRECT_H_ */

View file

@ -0,0 +1,63 @@
/*
*
* (C) COPYRIGHT 2010-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* "Always on" power management policy
*/
#include <mali_kbase.h>
#include <mali_kbase_pm.h>
static u64 always_on_get_core_mask(struct kbase_device *kbdev)
{
return kbdev->gpu_props.props.raw_props.shader_present;
}
static bool always_on_get_core_active(struct kbase_device *kbdev)
{
return true;
}
static void always_on_init(struct kbase_device *kbdev)
{
CSTD_UNUSED(kbdev);
}
static void always_on_term(struct kbase_device *kbdev)
{
CSTD_UNUSED(kbdev);
}
/*
* The struct kbase_pm_policy structure for the demand power policy.
*
* This is the static structure that defines the demand power policy's callback
* and name.
*/
const struct kbase_pm_policy kbase_pm_always_on_policy_ops = {
"always_on", /* name */
always_on_init, /* init */
always_on_term, /* term */
always_on_get_core_mask, /* get_core_mask */
always_on_get_core_active, /* get_core_active */
0u, /* flags */
KBASE_PM_POLICY_ID_ALWAYS_ON, /* id */
};
KBASE_EXPORT_TEST_API(kbase_pm_always_on_policy_ops);

View file

@ -0,0 +1,77 @@
/*
*
* (C) COPYRIGHT 2011-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* "Always on" power management policy
*/
#ifndef MALI_KBASE_PM_ALWAYS_ON_H
#define MALI_KBASE_PM_ALWAYS_ON_H
/**
* DOC:
* The "Always on" power management policy has the following
* characteristics:
*
* - When KBase indicates that the GPU will be powered up, but we don't yet
* know which Job Chains are to be run:
* All Shader Cores are powered up, regardless of whether or not they will
* be needed later.
*
* - When KBase indicates that a set of Shader Cores are needed to submit the
* currently queued Job Chains:
* All Shader Cores are kept powered, regardless of whether or not they will
* be needed
*
* - When KBase indicates that the GPU need not be powered:
* The Shader Cores are kept powered, regardless of whether or not they will
* be needed. The GPU itself is also kept powered, even though it is not
* needed.
*
* This policy is automatically overridden during system suspend: the desired
* core state is ignored, and the cores are forced off regardless of what the
* policy requests. After resuming from suspend, new changes to the desired
* core state made by the policy are honored.
*
* Note:
*
* - KBase indicates the GPU will be powered up when it has a User Process that
* has just started to submit Job Chains.
*
* - KBase indicates the GPU need not be powered when all the Job Chains from
* User Processes have finished, and it is waiting for a User Process to
* submit some more Job Chains.
*/
/**
* struct kbasep_pm_policy_always_on - Private struct for policy instance data
* @dummy: unused dummy variable
*
* This contains data that is private to the particular power policy that is
* active.
*/
struct kbasep_pm_policy_always_on {
int dummy;
};
extern const struct kbase_pm_policy kbase_pm_always_on_policy_ops;
#endif /* MALI_KBASE_PM_ALWAYS_ON_H */

View file

@ -0,0 +1,398 @@
/*
*
* (C) COPYRIGHT 2010-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* GPU backend implementation of base kernel power management APIs
*/
#include <mali_kbase.h>
#include <mali_midg_regmap.h>
#include <mali_kbase_config_defaults.h>
#ifdef CONFIG_MALI_PLATFORM_DEVICETREE
#include <linux/pm_runtime.h>
#endif /* CONFIG_MALI_PLATFORM_DEVICETREE */
#include <mali_kbase_pm.h>
#include <backend/gpu/mali_kbase_jm_internal.h>
#include <backend/gpu/mali_kbase_pm_internal.h>
void kbase_pm_register_access_enable(struct kbase_device *kbdev)
{
struct kbase_pm_callback_conf *callbacks;
#ifdef CONFIG_MALI_PLATFORM_DEVICETREE
pm_runtime_enable(kbdev->dev);
#endif /* CONFIG_MALI_PLATFORM_DEVICETREE */
callbacks = (struct kbase_pm_callback_conf *)POWER_MANAGEMENT_CALLBACKS;
if (callbacks)
callbacks->power_on_callback(kbdev);
kbdev->pm.backend.gpu_powered = true;
}
void kbase_pm_register_access_disable(struct kbase_device *kbdev)
{
struct kbase_pm_callback_conf *callbacks;
callbacks = (struct kbase_pm_callback_conf *)POWER_MANAGEMENT_CALLBACKS;
if (callbacks)
callbacks->power_off_callback(kbdev);
kbdev->pm.backend.gpu_powered = false;
#ifdef CONFIG_MALI_PLATFORM_DEVICETREE
pm_runtime_disable(kbdev->dev);
#endif
}
int kbase_hwaccess_pm_init(struct kbase_device *kbdev)
{
int ret = 0;
struct kbase_pm_callback_conf *callbacks;
KBASE_DEBUG_ASSERT(kbdev != NULL);
mutex_init(&kbdev->pm.lock);
kbdev->pm.backend.gpu_powered = false;
kbdev->pm.suspending = false;
/* MALI_SEC_INTEGRATION */
init_waitqueue_head(&kbdev->pm.suspending_wait);
#ifdef CONFIG_MALI_DEBUG
kbdev->pm.backend.driver_ready_for_irqs = false;
#endif /* CONFIG_MALI_DEBUG */
kbdev->pm.backend.gpu_in_desired_state = true;
init_waitqueue_head(&kbdev->pm.backend.gpu_in_desired_state_wait);
callbacks = (struct kbase_pm_callback_conf *)POWER_MANAGEMENT_CALLBACKS;
if (callbacks) {
kbdev->pm.backend.callback_power_on =
callbacks->power_on_callback;
kbdev->pm.backend.callback_power_off =
callbacks->power_off_callback;
kbdev->pm.backend.callback_power_suspend =
callbacks->power_suspend_callback;
kbdev->pm.backend.callback_power_resume =
callbacks->power_resume_callback;
kbdev->pm.callback_power_runtime_init =
callbacks->power_runtime_init_callback;
kbdev->pm.callback_power_runtime_term =
callbacks->power_runtime_term_callback;
kbdev->pm.backend.callback_power_runtime_on =
callbacks->power_runtime_on_callback;
kbdev->pm.backend.callback_power_runtime_off =
callbacks->power_runtime_off_callback;
/* MALI_SEC_INTEGRATION */
kbdev->pm.backend.callback_power_dvfs_on =
callbacks->power_dvfs_on_callback;
kbdev->pm.backend.callback_power_change_dvfs_level =
callbacks->power_change_dvfs_level_callback;
} else {
kbdev->pm.backend.callback_power_on = NULL;
kbdev->pm.backend.callback_power_off = NULL;
kbdev->pm.backend.callback_power_suspend = NULL;
kbdev->pm.backend.callback_power_resume = NULL;
kbdev->pm.callback_power_runtime_init = NULL;
kbdev->pm.callback_power_runtime_term = NULL;
kbdev->pm.backend.callback_power_runtime_on = NULL;
kbdev->pm.backend.callback_power_runtime_off = NULL;
/* MALI_SEC_INTEGRATION */
kbdev->pm.backend.callback_power_dvfs_on = NULL;
kbdev->pm.backend.callback_power_change_dvfs_level = NULL;
}
/* Initialise the metrics subsystem */
ret = kbasep_pm_metrics_init(kbdev);
if (ret)
return ret;
init_waitqueue_head(&kbdev->pm.backend.l2_powered_wait);
kbdev->pm.backend.l2_powered = 0;
init_waitqueue_head(&kbdev->pm.backend.reset_done_wait);
kbdev->pm.backend.reset_done = false;
init_waitqueue_head(&kbdev->pm.zero_active_count_wait);
kbdev->pm.active_count = 0;
spin_lock_init(&kbdev->pm.power_change_lock);
spin_lock_init(&kbdev->pm.backend.gpu_cycle_counter_requests_lock);
spin_lock_init(&kbdev->pm.backend.gpu_powered_lock);
if (kbase_pm_ca_init(kbdev) != 0)
goto workq_fail;
if (kbase_pm_policy_init(kbdev) != 0)
goto pm_policy_fail;
return 0;
pm_policy_fail:
kbase_pm_ca_term(kbdev);
workq_fail:
kbasep_pm_metrics_term(kbdev);
return -EINVAL;
}
void kbase_pm_do_poweron(struct kbase_device *kbdev, bool is_resume)
{
lockdep_assert_held(&kbdev->pm.lock);
/* Turn clocks and interrupts on - no-op if we haven't done a previous
* kbase_pm_clock_off() */
kbase_pm_clock_on(kbdev, is_resume);
/* Update core status as required by the policy */
KBASE_TIMELINE_PM_CHECKTRANS(kbdev,
SW_FLOW_PM_CHECKTRANS_PM_DO_POWERON_START);
kbase_pm_update_cores_state(kbdev);
KBASE_TIMELINE_PM_CHECKTRANS(kbdev,
SW_FLOW_PM_CHECKTRANS_PM_DO_POWERON_END);
/* NOTE: We don't wait to reach the desired state, since running atoms
* will wait for that state to be reached anyway */
}
bool kbase_pm_do_poweroff(struct kbase_device *kbdev, bool is_suspend)
{
unsigned long flags;
bool cores_are_available;
lockdep_assert_held(&kbdev->pm.lock);
spin_lock_irqsave(&kbdev->pm.power_change_lock, flags);
/* Force all cores off */
kbdev->pm.backend.desired_shader_state = 0;
/* Force all cores to be unavailable, in the situation where
* transitions are in progress for some cores but not others,
* and kbase_pm_check_transitions_nolock can not immediately
* power off the cores */
kbdev->shader_available_bitmap = 0;
kbdev->tiler_available_bitmap = 0;
kbdev->l2_available_bitmap = 0;
KBASE_TIMELINE_PM_CHECKTRANS(kbdev,
SW_FLOW_PM_CHECKTRANS_PM_DO_POWEROFF_START);
cores_are_available = kbase_pm_check_transitions_nolock(kbdev);
KBASE_TIMELINE_PM_CHECKTRANS(kbdev,
SW_FLOW_PM_CHECKTRANS_PM_DO_POWEROFF_END);
/* Don't need 'cores_are_available', because we don't return anything */
CSTD_UNUSED(cores_are_available);
spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags);
/* NOTE: We won't wait to reach the core's desired state, even if we're
* powering off the GPU itself too. It's safe to cut the power whilst
* they're transitioning to off, because the cores should be idle and
* all cache flushes should already have occurred */
/* Consume any change-state events */
kbase_timeline_pm_check_handle_event(kbdev,
KBASE_TIMELINE_PM_EVENT_GPU_STATE_CHANGED);
/* Disable interrupts and turn the clock off */
return kbase_pm_clock_off(kbdev, is_suspend);
}
int kbase_hwaccess_pm_powerup(struct kbase_device *kbdev,
unsigned int flags)
{
struct kbasep_js_device_data *js_devdata = &kbdev->js_data;
unsigned long irq_flags;
int ret;
KBASE_DEBUG_ASSERT(kbdev != NULL);
mutex_lock(&js_devdata->runpool_mutex);
mutex_lock(&kbdev->pm.lock);
/* MALI_SEC_INTEGRATION */
/* while the GPU initialization, vendor desired gpu log will be out by set_power_dbg(FALSE) calls */
if(kbdev->vendor_callbacks->set_poweron_dbg)
kbdev->vendor_callbacks->set_poweron_dbg(false);
/* A suspend won't happen during startup/insmod */
KBASE_DEBUG_ASSERT(!kbase_pm_is_suspending(kbdev));
/* Power up the GPU, don't enable IRQs as we are not ready to receive
* them. */
ret = kbase_pm_init_hw(kbdev, flags);
if (ret) {
mutex_unlock(&kbdev->pm.lock);
mutex_unlock(&js_devdata->runpool_mutex);
return ret;
}
kbasep_pm_read_present_cores(kbdev);
kbdev->pm.debug_core_mask =
kbdev->gpu_props.props.raw_props.shader_present;
/* Pretend the GPU is active to prevent a power policy turning the GPU
* cores off */
kbdev->pm.active_count = 1;
spin_lock_irqsave(&kbdev->pm.backend.gpu_cycle_counter_requests_lock,
irq_flags);
/* Ensure cycle counter is off */
kbdev->pm.backend.gpu_cycle_counter_requests = 0;
spin_unlock_irqrestore(
&kbdev->pm.backend.gpu_cycle_counter_requests_lock,
irq_flags);
/* We are ready to receive IRQ's now as power policy is set up, so
* enable them now. */
#ifdef CONFIG_MALI_DEBUG
spin_lock_irqsave(&kbdev->pm.backend.gpu_powered_lock, irq_flags);
kbdev->pm.backend.driver_ready_for_irqs = true;
spin_unlock_irqrestore(&kbdev->pm.backend.gpu_powered_lock, irq_flags);
#endif
kbase_pm_enable_interrupts(kbdev);
/* Turn on the GPU and any cores needed by the policy */
kbase_pm_do_poweron(kbdev, false);
mutex_unlock(&kbdev->pm.lock);
mutex_unlock(&js_devdata->runpool_mutex);
#ifdef MALI_SEC_HWCNT
if (kbdev->hwcnt.is_hwcnt_attach == false)
if (kbdev->vendor_callbacks->hwcnt_attach)
kbdev->vendor_callbacks->hwcnt_attach(kbdev);
#endif
/* Idle the GPU and/or cores, if the policy wants it to */
kbase_pm_context_idle(kbdev);
return 0;
}
void kbase_hwaccess_pm_halt(struct kbase_device *kbdev)
{
KBASE_DEBUG_ASSERT(kbdev != NULL);
mutex_lock(&kbdev->pm.lock);
kbase_pm_cancel_deferred_poweroff(kbdev);
if (!kbase_pm_do_poweroff(kbdev, false)) {
/* Page/bus faults are pending, must drop pm.lock to process.
* Interrupts are disabled so no more faults should be
* generated at this point */
mutex_unlock(&kbdev->pm.lock);
kbase_flush_mmu_wqs(kbdev);
mutex_lock(&kbdev->pm.lock);
WARN_ON(!kbase_pm_do_poweroff(kbdev, false));
}
mutex_unlock(&kbdev->pm.lock);
}
KBASE_EXPORT_TEST_API(kbase_hwaccess_pm_halt);
void kbase_hwaccess_pm_term(struct kbase_device *kbdev)
{
KBASE_DEBUG_ASSERT(kbdev != NULL);
KBASE_DEBUG_ASSERT(kbdev->pm.active_count == 0);
KBASE_DEBUG_ASSERT(kbdev->pm.backend.gpu_cycle_counter_requests == 0);
/* Free any resources the policy allocated */
kbase_pm_policy_term(kbdev);
kbase_pm_ca_term(kbdev);
/* Shut down the metrics subsystem */
kbasep_pm_metrics_term(kbdev);
}
void kbase_pm_power_changed(struct kbase_device *kbdev)
{
bool cores_are_available;
unsigned long flags;
KBASE_TIMELINE_PM_CHECKTRANS(kbdev,
SW_FLOW_PM_CHECKTRANS_GPU_INTERRUPT_START);
spin_lock_irqsave(&kbdev->pm.power_change_lock, flags);
cores_are_available = kbase_pm_check_transitions_nolock(kbdev);
spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags);
KBASE_TIMELINE_PM_CHECKTRANS(kbdev,
SW_FLOW_PM_CHECKTRANS_GPU_INTERRUPT_END);
if (cores_are_available) {
/* Log timelining information that a change in state has
* completed */
kbase_timeline_pm_handle_event(kbdev,
KBASE_TIMELINE_PM_EVENT_GPU_STATE_CHANGED);
spin_lock_irqsave(&kbdev->js_data.runpool_irq.lock, flags);
kbase_gpu_slot_update(kbdev);
spin_unlock_irqrestore(&kbdev->js_data.runpool_irq.lock, flags);
}
}
void kbase_pm_set_debug_core_mask(struct kbase_device *kbdev, u64 new_core_mask)
{
kbdev->pm.debug_core_mask = new_core_mask;
kbase_pm_update_cores_state_nolock(kbdev);
}
void kbase_hwaccess_pm_gpu_active(struct kbase_device *kbdev)
{
kbase_pm_update_active(kbdev);
}
void kbase_hwaccess_pm_gpu_idle(struct kbase_device *kbdev)
{
kbase_pm_update_active(kbdev);
}
void kbase_hwaccess_pm_suspend(struct kbase_device *kbdev)
{
struct kbasep_js_device_data *js_devdata = &kbdev->js_data;
/* Force power off the GPU and all cores (regardless of policy), only
* after the PM active count reaches zero (otherwise, we risk turning it
* off prematurely) */
mutex_lock(&js_devdata->runpool_mutex);
mutex_lock(&kbdev->pm.lock);
kbase_pm_cancel_deferred_poweroff(kbdev);
if (!kbase_pm_do_poweroff(kbdev, true)) {
/* Page/bus faults are pending, must drop pm.lock to process.
* Interrupts are disabled so no more faults should be
* generated at this point */
mutex_unlock(&kbdev->pm.lock);
kbase_flush_mmu_wqs(kbdev);
mutex_lock(&kbdev->pm.lock);
WARN_ON(!kbase_pm_do_poweroff(kbdev, false));
}
mutex_unlock(&kbdev->pm.lock);
mutex_unlock(&js_devdata->runpool_mutex);
}
void kbase_hwaccess_pm_resume(struct kbase_device *kbdev)
{
struct kbasep_js_device_data *js_devdata = &kbdev->js_data;
mutex_lock(&js_devdata->runpool_mutex);
mutex_lock(&kbdev->pm.lock);
kbdev->pm.suspending = false;
kbase_pm_do_poweron(kbdev, true);
/* MALI_SEC_INTEGRATION */
wake_up(&kbdev->pm.suspending_wait);
mutex_unlock(&kbdev->pm.lock);
mutex_unlock(&js_devdata->runpool_mutex);
}

View file

@ -0,0 +1,182 @@
/*
*
* (C) COPYRIGHT 2013-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Base kernel core availability APIs
*/
#include <mali_kbase.h>
#include <mali_kbase_pm.h>
#include <backend/gpu/mali_kbase_pm_internal.h>
static const struct kbase_pm_ca_policy *const policy_list[] = {
&kbase_pm_ca_fixed_policy_ops,
#if !MALI_CUSTOMER_RELEASE
&kbase_pm_ca_random_policy_ops
#endif
};
/**
* POLICY_COUNT - The number of policies available in the system.
*
* This is derived from the number of functions listed in policy_list.
*/
#define POLICY_COUNT (sizeof(policy_list)/sizeof(*policy_list))
int kbase_pm_ca_init(struct kbase_device *kbdev)
{
KBASE_DEBUG_ASSERT(kbdev != NULL);
kbdev->pm.backend.ca_current_policy = policy_list[0];
kbdev->pm.backend.ca_current_policy->init(kbdev);
return 0;
}
void kbase_pm_ca_term(struct kbase_device *kbdev)
{
kbdev->pm.backend.ca_current_policy->term(kbdev);
}
int kbase_pm_ca_list_policies(const struct kbase_pm_ca_policy * const **list)
{
if (!list)
return POLICY_COUNT;
*list = policy_list;
return POLICY_COUNT;
}
KBASE_EXPORT_TEST_API(kbase_pm_ca_list_policies);
const struct kbase_pm_ca_policy
*kbase_pm_ca_get_policy(struct kbase_device *kbdev)
{
KBASE_DEBUG_ASSERT(kbdev != NULL);
return kbdev->pm.backend.ca_current_policy;
}
KBASE_EXPORT_TEST_API(kbase_pm_ca_get_policy);
void kbase_pm_ca_set_policy(struct kbase_device *kbdev,
const struct kbase_pm_ca_policy *new_policy)
{
const struct kbase_pm_ca_policy *old_policy;
unsigned long flags;
KBASE_DEBUG_ASSERT(kbdev != NULL);
KBASE_DEBUG_ASSERT(new_policy != NULL);
KBASE_TRACE_ADD(kbdev, PM_CA_SET_POLICY, NULL, NULL, 0u,
new_policy->id);
/* During a policy change we pretend the GPU is active */
/* A suspend won't happen here, because we're in a syscall from a
* userspace thread */
kbase_pm_context_active(kbdev);
mutex_lock(&kbdev->pm.lock);
/* Remove the policy to prevent IRQ handlers from working on it */
spin_lock_irqsave(&kbdev->pm.power_change_lock, flags);
old_policy = kbdev->pm.backend.ca_current_policy;
kbdev->pm.backend.ca_current_policy = NULL;
spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags);
if (old_policy->term)
old_policy->term(kbdev);
if (new_policy->init)
new_policy->init(kbdev);
spin_lock_irqsave(&kbdev->pm.power_change_lock, flags);
kbdev->pm.backend.ca_current_policy = new_policy;
/* If any core power state changes were previously attempted, but
* couldn't be made because the policy was changing (current_policy was
* NULL), then re-try them here. */
kbase_pm_update_cores_state_nolock(kbdev);
kbdev->pm.backend.ca_current_policy->update_core_status(kbdev,
kbdev->shader_ready_bitmap,
kbdev->shader_transitioning_bitmap);
spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags);
mutex_unlock(&kbdev->pm.lock);
/* Now the policy change is finished, we release our fake context active
* reference */
kbase_pm_context_idle(kbdev);
}
KBASE_EXPORT_TEST_API(kbase_pm_ca_set_policy);
u64 kbase_pm_ca_get_core_mask(struct kbase_device *kbdev)
{
lockdep_assert_held(&kbdev->pm.power_change_lock);
/* All cores must be enabled when instrumentation is in use */
if (kbdev->pm.backend.instr_enabled)
return kbdev->gpu_props.props.raw_props.shader_present &
kbdev->pm.debug_core_mask;
if (kbdev->pm.backend.ca_current_policy == NULL)
return kbdev->gpu_props.props.raw_props.shader_present &
kbdev->pm.debug_core_mask;
return kbdev->pm.backend.ca_current_policy->get_core_mask(kbdev) &
kbdev->pm.debug_core_mask;
}
KBASE_EXPORT_TEST_API(kbase_pm_ca_get_core_mask);
void kbase_pm_ca_update_core_status(struct kbase_device *kbdev, u64 cores_ready,
u64 cores_transitioning)
{
lockdep_assert_held(&kbdev->pm.power_change_lock);
if (kbdev->pm.backend.ca_current_policy != NULL)
kbdev->pm.backend.ca_current_policy->update_core_status(kbdev,
cores_ready,
cores_transitioning);
}
void kbase_pm_ca_instr_enable(struct kbase_device *kbdev)
{
unsigned long flags;
spin_lock_irqsave(&kbdev->pm.power_change_lock, flags);
kbdev->pm.backend.instr_enabled = true;
kbase_pm_update_cores_state_nolock(kbdev);
spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags);
}
void kbase_pm_ca_instr_disable(struct kbase_device *kbdev)
{
unsigned long flags;
spin_lock_irqsave(&kbdev->pm.power_change_lock, flags);
kbdev->pm.backend.instr_enabled = false;
kbase_pm_update_cores_state_nolock(kbdev);
spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags);
}

View file

@ -0,0 +1,92 @@
/*
*
* (C) COPYRIGHT 2011-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Base kernel core availability APIs
*/
#ifndef _KBASE_PM_CA_H_
#define _KBASE_PM_CA_H_
/**
* kbase_pm_ca_init - Initialize core availability framework
*
* Must be called before calling any other core availability function
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
*
* Return: 0 if the core availability framework was successfully initialized,
* -errno otherwise
*/
int kbase_pm_ca_init(struct kbase_device *kbdev);
/**
* kbase_pm_ca_term - Terminate core availability framework
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
*/
void kbase_pm_ca_term(struct kbase_device *kbdev);
/**
* kbase_pm_ca_get_core_mask - Get currently available shaders core mask
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
*
* Returns a mask of the currently available shader cores.
* Calls into the core availability policy
*
* Return: The bit mask of available cores
*/
u64 kbase_pm_ca_get_core_mask(struct kbase_device *kbdev);
/**
* kbase_pm_ca_update_core_status - Update core status
*
* @kbdev: The kbase device structure for the device (must be
* a valid pointer)
* @cores_ready: The bit mask of cores ready for job submission
* @cores_transitioning: The bit mask of cores that are transitioning power
* state
*
* Update core availability policy with current core power status
*
* Calls into the core availability policy
*/
void kbase_pm_ca_update_core_status(struct kbase_device *kbdev, u64 cores_ready,
u64 cores_transitioning);
/**
* kbase_pm_ca_instr_enable - Enable override for instrumentation
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
*
* This overrides the output of the core availability policy, ensuring that all
* cores are available
*/
void kbase_pm_ca_instr_enable(struct kbase_device *kbdev);
/**
* kbase_pm_ca_instr_disable - Disable override for instrumentation
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
*
* This disables any previously enabled override, and resumes normal policy
* functionality
*/
void kbase_pm_ca_instr_disable(struct kbase_device *kbdev);
#endif /* _KBASE_PM_CA_H_ */

View file

@ -0,0 +1,65 @@
/*
*
* (C) COPYRIGHT 2013-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* A power policy implementing fixed core availability
*/
#include <mali_kbase.h>
#include <mali_kbase_pm.h>
static void fixed_init(struct kbase_device *kbdev)
{
kbdev->pm.backend.ca_in_transition = false;
}
static void fixed_term(struct kbase_device *kbdev)
{
CSTD_UNUSED(kbdev);
}
static u64 fixed_get_core_mask(struct kbase_device *kbdev)
{
return kbdev->gpu_props.props.raw_props.shader_present;
}
static void fixed_update_core_status(struct kbase_device *kbdev,
u64 cores_ready,
u64 cores_transitioning)
{
CSTD_UNUSED(kbdev);
CSTD_UNUSED(cores_ready);
CSTD_UNUSED(cores_transitioning);
}
/*
* The struct kbase_pm_policy structure for the fixed power policy.
*
* This is the static structure that defines the fixed power policy's callback
* and name.
*/
const struct kbase_pm_ca_policy kbase_pm_ca_fixed_policy_ops = {
"fixed", /* name */
fixed_init, /* init */
fixed_term, /* term */
fixed_get_core_mask, /* get_core_mask */
fixed_update_core_status, /* update_core_status */
0u, /* flags */
KBASE_PM_CA_POLICY_ID_FIXED, /* id */
};
KBASE_EXPORT_TEST_API(kbase_pm_ca_fixed_policy_ops);

View file

@ -0,0 +1,40 @@
/*
*
* (C) COPYRIGHT 2013-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* A power policy implementing fixed core availability
*/
#ifndef MALI_KBASE_PM_CA_FIXED_H
#define MALI_KBASE_PM_CA_FIXED_H
/**
* struct kbasep_pm_ca_policy_fixed - Private structure for policy instance data
*
* @dummy: Dummy member - no state is needed
*
* This contains data that is private to the particular power policy that is
* active.
*/
struct kbasep_pm_ca_policy_fixed {
int dummy;
};
extern const struct kbase_pm_ca_policy kbase_pm_ca_fixed_policy_ops;
#endif /* MALI_KBASE_PM_CA_FIXED_H */

View file

@ -0,0 +1,69 @@
/*
*
* (C) COPYRIGHT 2012-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* "Coarse Demand" power management policy
*/
#include <mali_kbase.h>
#include <mali_kbase_pm.h>
static u64 coarse_demand_get_core_mask(struct kbase_device *kbdev)
{
if (kbdev->pm.active_count == 0)
return 0;
return kbdev->gpu_props.props.raw_props.shader_present;
}
static bool coarse_demand_get_core_active(struct kbase_device *kbdev)
{
if (0 == kbdev->pm.active_count && !(kbdev->shader_needed_bitmap |
kbdev->shader_inuse_bitmap))
return false;
return true;
}
static void coarse_demand_init(struct kbase_device *kbdev)
{
CSTD_UNUSED(kbdev);
}
static void coarse_demand_term(struct kbase_device *kbdev)
{
CSTD_UNUSED(kbdev);
}
/* The struct kbase_pm_policy structure for the demand power policy.
*
* This is the static structure that defines the demand power policy's callback
* and name.
*/
const struct kbase_pm_policy kbase_pm_coarse_demand_policy_ops = {
"coarse_demand", /* name */
coarse_demand_init, /* init */
coarse_demand_term, /* term */
coarse_demand_get_core_mask, /* get_core_mask */
coarse_demand_get_core_active, /* get_core_active */
0u, /* flags */
KBASE_PM_POLICY_ID_COARSE_DEMAND, /* id */
};
KBASE_EXPORT_TEST_API(kbase_pm_coarse_demand_policy_ops);

View file

@ -0,0 +1,64 @@
/*
*
* (C) COPYRIGHT 2012-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* "Coarse Demand" power management policy
*/
#ifndef MALI_KBASE_PM_COARSE_DEMAND_H
#define MALI_KBASE_PM_COARSE_DEMAND_H
/**
* DOC:
* The "Coarse" demand power management policy has the following
* characteristics:
* - When KBase indicates that the GPU will be powered up, but we don't yet
* know which Job Chains are to be run:
* - All Shader Cores are powered up, regardless of whether or not they will
* be needed later.
* - When KBase indicates that a set of Shader Cores are needed to submit the
* currently queued Job Chains:
* - All Shader Cores are kept powered, regardless of whether or not they will
* be needed
* - When KBase indicates that the GPU need not be powered:
* - The Shader Cores are powered off, and the GPU itself is powered off too.
*
* @note:
* - KBase indicates the GPU will be powered up when it has a User Process that
* has just started to submit Job Chains.
* - KBase indicates the GPU need not be powered when all the Job Chains from
* User Processes have finished, and it is waiting for a User Process to
* submit some more Job Chains.
*/
/**
* struct kbasep_pm_policy_coarse_demand - Private structure for coarse demand
* policy
*
* This contains data that is private to the coarse demand power policy.
*
* @dummy: Dummy member - no state needed
*/
struct kbasep_pm_policy_coarse_demand {
int dummy;
};
extern const struct kbase_pm_policy kbase_pm_coarse_demand_policy_ops;
#endif /* MALI_KBASE_PM_COARSE_DEMAND_H */

View file

@ -0,0 +1,485 @@
/*
*
* (C) COPYRIGHT 2014-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Backend-specific Power Manager definitions
*/
#ifndef _KBASE_PM_HWACCESS_DEFS_H_
#define _KBASE_PM_HWACCESS_DEFS_H_
#include "mali_kbase_pm_ca_fixed.h"
#if !MALI_CUSTOMER_RELEASE
#include "mali_kbase_pm_ca_random.h"
#endif
#include "mali_kbase_pm_always_on.h"
#include "mali_kbase_pm_coarse_demand.h"
#include "mali_kbase_pm_demand.h"
#if !MALI_CUSTOMER_RELEASE
#include "mali_kbase_pm_demand_always_powered.h"
#include "mali_kbase_pm_fast_start.h"
#endif
/* Forward definition - see mali_kbase.h */
struct kbase_device;
struct kbase_jd_atom;
/**
* enum kbase_pm_core_type - The types of core in a GPU.
*
* These enumerated values are used in calls to
* - kbase_pm_get_present_cores()
* - kbase_pm_get_active_cores()
* - kbase_pm_get_trans_cores()
* - kbase_pm_get_ready_cores().
*
* They specify which type of core should be acted on. These values are set in
* a manner that allows core_type_to_reg() function to be simpler and more
* efficient.
*
* @KBASE_PM_CORE_L2: The L2 cache
* @KBASE_PM_CORE_SHADER: Shader cores
* @KBASE_PM_CORE_TILER: Tiler cores
*/
enum kbase_pm_core_type {
KBASE_PM_CORE_L2 = L2_PRESENT_LO,
KBASE_PM_CORE_SHADER = SHADER_PRESENT_LO,
KBASE_PM_CORE_TILER = TILER_PRESENT_LO
};
/**
* struct kbasep_pm_metrics_data - Metrics data collected for use by the power
* management framework.
*
* @time_period_start: time at which busy/idle measurements started
* @time_busy: number of ns the GPU was busy executing jobs since the
* @time_period_start timestamp.
* @time_idle: number of ns since time_period_start the GPU was not executing
* jobs since the @time_period_start timestamp.
* @prev_busy: busy time in ns of previous time period.
* Updated when metrics are reset.
* @prev_idle: idle time in ns of previous time period
* Updated when metrics are reset.
* @gpu_active: true when the GPU is executing jobs. false when
* not. Updated when the job scheduler informs us a job in submitted
* or removed from a GPU slot.
* @busy_cl: number of ns the GPU was busy executing CL jobs. Note that
* if two CL jobs were active for 400ns, this value would be updated
* with 800.
* @busy_gl: number of ns the GPU was busy executing GL jobs. Note that
* if two GL jobs were active for 400ns, this value would be updated
* with 800.
* @active_cl_ctx: number of CL jobs active on the GPU. Array is per-device.
* @active_gl_ctx: number of GL jobs active on the GPU. Array is per-slot. As
* GL jobs never run on slot 2 this slot is not recorded.
* @lock: spinlock protecting the kbasep_pm_metrics_data structure
* @timer: timer to regularly make DVFS decisions based on the power
* management metrics.
* @timer_active: boolean indicating @timer is running
* @platform_data: pointer to data controlled by platform specific code
* @kbdev: pointer to kbase device for which metrics are collected
*
*/
struct kbasep_pm_metrics_data {
ktime_t time_period_start;
u32 time_busy;
u32 time_idle;
u32 prev_busy;
u32 prev_idle;
bool gpu_active;
u32 busy_cl[2];
u32 busy_gl;
u32 active_cl_ctx[2];
u32 active_gl_ctx[2]; /* GL jobs can only run on 2 of the 3 job slots */
spinlock_t lock;
/* MALI_SEC_INTEGRATION */
/* #ifdef CONFIG_MALI_MIDGARD_DVFS */
struct hrtimer timer;
bool timer_active;
/* MALI_SEC_INTEGRATION */
struct delayed_work work;
/* #endif */
void *platform_data;
struct kbase_device *kbdev;
#ifdef MALI_SEC_CL_BOOST
atomic_t time_compute_jobs, time_vertex_jobs, time_fragment_jobs;
#endif
};
union kbase_pm_policy_data {
struct kbasep_pm_policy_always_on always_on;
struct kbasep_pm_policy_coarse_demand coarse_demand;
struct kbasep_pm_policy_demand demand;
#if !MALI_CUSTOMER_RELEASE
struct kbasep_pm_policy_demand_always_powered demand_always_powered;
struct kbasep_pm_policy_fast_start fast_start;
#endif
};
union kbase_pm_ca_policy_data {
struct kbasep_pm_ca_policy_fixed fixed;
#if !MALI_CUSTOMER_RELEASE
struct kbasep_pm_ca_policy_random random;
#endif
};
/**
* struct kbase_pm_backend_data - Data stored per device for power management.
*
* This structure contains data for the power management framework. There is one
* instance of this structure per device in the system.
*
* @ca_current_policy: The policy that is currently actively controlling core
* availability.
* @pm_current_policy: The policy that is currently actively controlling the
* power state.
* @ca_policy_data: Private data for current CA policy
* @pm_policy_data: Private data for current PM policy
* @ca_in_transition: Flag indicating when core availability policy is
* transitioning cores. The core availability policy must
* set this when a change in core availability is occurring.
* power_change_lock must be held when accessing this.
* @reset_done: Flag when a reset is complete
* @reset_done_wait: Wait queue to wait for changes to @reset_done
* @l2_powered_wait: Wait queue for whether the l2 cache has been powered as
* requested
* @l2_powered: State indicating whether all the l2 caches are powered.
* Non-zero indicates they're *all* powered
* Zero indicates that some (or all) are not powered
* @gpu_cycle_counter_requests: The reference count of active gpu cycle counter
* users
* @gpu_cycle_counter_requests_lock: Lock to protect @gpu_cycle_counter_requests
* @desired_shader_state: A bit mask identifying the shader cores that the
* power policy would like to be on. The current state
* of the cores may be different, but there should be
* transitions in progress that will eventually achieve
* this state (assuming that the policy doesn't change
* its mind in the mean time).
* @powering_on_shader_state: A bit mask indicating which shader cores are
* currently in a power-on transition
* @desired_tiler_state: A bit mask identifying the tiler cores that the power
* policy would like to be on. See @desired_shader_state
* @powering_on_tiler_state: A bit mask indicating which tiler core are
* currently in a power-on transition
* @powering_on_l2_state: A bit mask indicating which l2-caches are currently
* in a power-on transition
* @gpu_in_desired_state: This flag is set if the GPU is powered as requested
* by the desired_xxx_state variables
* @gpu_in_desired_state_wait: Wait queue set when @gpu_in_desired_state != 0
* @gpu_powered: Set to true when the GPU is powered and register
* accesses are possible, false otherwise
* @instr_enabled: Set to true when instrumentation is enabled,
* false otherwise
* @cg1_disabled: Set if the policy wants to keep the second core group
* powered off
* @driver_ready_for_irqs: Debug state indicating whether sufficient
* initialization of the driver has occurred to handle
* IRQs
* @gpu_powered_lock: Spinlock that must be held when writing @gpu_powered or
* accessing @driver_ready_for_irqs
* @metrics: Structure to hold metrics for the GPU
* @gpu_poweroff_pending: number of poweroff timer ticks until the GPU is
* powered off
* @shader_poweroff_pending_time: number of poweroff timer ticks until shaders
* are powered off
* @gpu_poweroff_timer: Timer for powering off GPU
* @gpu_poweroff_wq: Workqueue to power off GPU on when timer fires
* @gpu_poweroff_work: Workitem used on @gpu_poweroff_wq
* @shader_poweroff_pending: Bit mask of shaders to be powered off on next
* timer callback
* @poweroff_timer_needed: true if the poweroff timer is currently running,
* false otherwise
* @callback_power_on: Callback when the GPU needs to be turned on. See
* &struct kbase_pm_callback_conf
* @callback_power_off: Callback when the GPU may be turned off. See
* &struct kbase_pm_callback_conf
* @callback_power_suspend: Callback when a suspend occurs and the GPU needs to
* be turned off. See &struct kbase_pm_callback_conf
* @callback_power_resume: Callback when a resume occurs and the GPU needs to
* be turned on. See &struct kbase_pm_callback_conf
* @callback_power_runtime_on: Callback when the GPU needs to be turned on. See
* &struct kbase_pm_callback_conf
* @callback_power_runtime_off: Callback when the GPU may be turned off. See
* &struct kbase_pm_callback_conf
* @callback_cci_snoop_ctrl: Callback when the GPU L2 power may transition.
* If enable is set then snoops should be enabled
* otherwise snoops should be disabled
*
* Note:
* During an IRQ, @ca_current_policy or @pm_current_policy can be NULL when the
* policy is being changed with kbase_pm_ca_set_policy() or
* kbase_pm_set_policy(). The change is protected under
* kbase_device.pm.power_change_lock. Direct access to this
* from IRQ context must therefore check for NULL. If NULL, then
* kbase_pm_ca_set_policy() or kbase_pm_set_policy() will re-issue the policy
* functions that would have been done under IRQ.
*/
struct kbase_pm_backend_data {
const struct kbase_pm_ca_policy *ca_current_policy;
const struct kbase_pm_policy *pm_current_policy;
union kbase_pm_ca_policy_data ca_policy_data;
union kbase_pm_policy_data pm_policy_data;
bool ca_in_transition;
bool reset_done;
wait_queue_head_t reset_done_wait;
wait_queue_head_t l2_powered_wait;
int l2_powered;
int gpu_cycle_counter_requests;
spinlock_t gpu_cycle_counter_requests_lock;
u64 desired_shader_state;
u64 powering_on_shader_state;
u64 desired_tiler_state;
u64 powering_on_tiler_state;
u64 powering_on_l2_state;
bool gpu_in_desired_state;
wait_queue_head_t gpu_in_desired_state_wait;
bool gpu_powered;
bool instr_enabled;
bool cg1_disabled;
#ifdef CONFIG_MALI_DEBUG
bool driver_ready_for_irqs;
#endif /* CONFIG_MALI_DEBUG */
spinlock_t gpu_powered_lock;
struct kbasep_pm_metrics_data metrics;
int gpu_poweroff_pending;
int shader_poweroff_pending_time;
struct hrtimer gpu_poweroff_timer;
struct workqueue_struct *gpu_poweroff_wq;
struct work_struct gpu_poweroff_work;
u64 shader_poweroff_pending;
bool poweroff_timer_needed;
int (*callback_power_on)(struct kbase_device *kbdev);
void (*callback_power_off)(struct kbase_device *kbdev);
void (*callback_power_suspend)(struct kbase_device *kbdev);
void (*callback_power_resume)(struct kbase_device *kbdev);
int (*callback_power_runtime_on)(struct kbase_device *kbdev);
void (*callback_power_runtime_off)(struct kbase_device *kbdev);
/* MALI_SEC_INTEGRATION */
int (*callback_power_dvfs_on)(struct kbase_device *kbdev);
int (*callback_power_change_dvfs_level)(struct kbase_device *kbdev);
};
/* List of policy IDs */
enum kbase_pm_policy_id {
KBASE_PM_POLICY_ID_DEMAND = 1,
KBASE_PM_POLICY_ID_ALWAYS_ON,
KBASE_PM_POLICY_ID_COARSE_DEMAND,
#if !MALI_CUSTOMER_RELEASE
KBASE_PM_POLICY_ID_DEMAND_ALWAYS_POWERED,
KBASE_PM_POLICY_ID_FAST_START
#endif
};
typedef u32 kbase_pm_policy_flags;
/**
* struct kbase_pm_policy - Power policy structure.
*
* Each power policy exposes a (static) instance of this structure which
* contains function pointers to the policy's methods.
*
* @name: The name of this policy
* @init: Function called when the policy is selected
* @term: Function called when the policy is unselected
* @get_core_mask: Function called to get the current shader core mask
* @get_core_active: Function called to get the current overall GPU power
* state
* @flags: Field indicating flags for this policy
* @id: Field indicating an ID for this policy. This is not
* necessarily the same as its index in the list returned
* by kbase_pm_list_policies().
* It is used purely for debugging.
*/
struct kbase_pm_policy {
char *name;
/**
* Function called when the policy is selected
*
* This should initialize the kbdev->pm.pm_policy_data structure. It
* should not attempt to make any changes to hardware state.
*
* It is undefined what state the cores are in when the function is
* called.
*
* @kbdev: The kbase device structure for the device (must be a
* valid pointer)
*/
void (*init)(struct kbase_device *kbdev);
/**
* Function called when the policy is unselected.
*
* @kbdev: The kbase device structure for the device (must be a
* valid pointer)
*/
void (*term)(struct kbase_device *kbdev);
/**
* Function called to get the current shader core mask
*
* The returned mask should meet or exceed (kbdev->shader_needed_bitmap
* | kbdev->shader_inuse_bitmap).
*
* @kbdev: The kbase device structure for the device (must be a
* valid pointer)
*
* Return: The mask of shader cores to be powered
*/
u64 (*get_core_mask)(struct kbase_device *kbdev);
/**
* Function called to get the current overall GPU power state
*
* This function should consider the state of kbdev->pm.active_count. If
* this count is greater than 0 then there is at least one active
* context on the device and the GPU should be powered. If it is equal
* to 0 then there are no active contexts and the GPU could be powered
* off if desired.
*
* @kbdev: The kbase device structure for the device (must be a
* valid pointer)
*
* Return: true if the GPU should be powered, false otherwise
*/
bool (*get_core_active)(struct kbase_device *kbdev);
kbase_pm_policy_flags flags;
enum kbase_pm_policy_id id;
};
enum kbase_pm_ca_policy_id {
KBASE_PM_CA_POLICY_ID_FIXED = 1,
KBASE_PM_CA_POLICY_ID_RANDOM
};
typedef u32 kbase_pm_ca_policy_flags;
/**
* struct kbase_pm_ca_policy - Core availability policy structure.
*
* Each core availability policy exposes a (static) instance of this structure
* which contains function pointers to the policy's methods.
*
* @name: The name of this policy
* @init: Function called when the policy is selected
* @term: Function called when the policy is unselected
* @get_core_mask: Function called to get the current shader core
* availability mask
* @update_core_status: Function called to update the current core status
* @flags: Field indicating flags for this policy
* @id: Field indicating an ID for this policy. This is not
* necessarily the same as its index in the list returned
* by kbase_pm_list_policies().
* It is used purely for debugging.
*/
struct kbase_pm_ca_policy {
char *name;
/**
* Function called when the policy is selected
*
* This should initialize the kbdev->pm.ca_policy_data structure. It
* should not attempt to make any changes to hardware state.
*
* It is undefined what state the cores are in when the function is
* called.
*
* @kbdev The kbase device structure for the device (must be a
* valid pointer)
*/
void (*init)(struct kbase_device *kbdev);
/**
* Function called when the policy is unselected.
*
* @kbdev The kbase device structure for the device (must be a
* valid pointer)
*/
void (*term)(struct kbase_device *kbdev);
/**
* Function called to get the current shader core availability mask
*
* When a change in core availability is occurring, the policy must set
* kbdev->pm.ca_in_transition to true. This is to indicate that
* reporting changes in power state cannot be optimized out, even if
* kbdev->pm.desired_shader_state remains unchanged. This must be done
* by any functions internal to the Core Availability Policy that change
* the return value of kbase_pm_ca_policy::get_core_mask.
*
* @kbdev The kbase device structure for the device (must be a
* valid pointer)
*
* Return: The current core availability mask
*/
u64 (*get_core_mask)(struct kbase_device *kbdev);
/**
* Function called to update the current core status
*
* If none of the cores in core group 0 are ready or transitioning, then
* the policy must ensure that the next call to get_core_mask does not
* return 0 for all cores in core group 0. It is an error to disable
* core group 0 through the core availability policy.
*
* When a change in core availability has finished, the policy must set
* kbdev->pm.ca_in_transition to false. This is to indicate that
* changes in power state can once again be optimized out when
* kbdev->pm.desired_shader_state is unchanged.
*
* @kbdev: The kbase device structure for the device
* (must be a valid pointer)
* @cores_ready: The mask of cores currently powered and
* ready to run jobs
* @cores_transitioning: The mask of cores currently transitioning
* power state
*/
void (*update_core_status)(struct kbase_device *kbdev, u64 cores_ready,
u64 cores_transitioning);
kbase_pm_ca_policy_flags flags;
/**
* Field indicating an ID for this policy. This is not necessarily the
* same as its index in the list returned by kbase_pm_list_policies().
* It is used purely for debugging.
*/
enum kbase_pm_ca_policy_id id;
};
#endif /* _KBASE_PM_HWACCESS_DEFS_H_ */

View file

@ -0,0 +1,72 @@
/*
*
* (C) COPYRIGHT 2010-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* A simple demand based power management policy
*/
#include <mali_kbase.h>
#include <mali_kbase_pm.h>
static u64 demand_get_core_mask(struct kbase_device *kbdev)
{
u64 desired = kbdev->shader_needed_bitmap | kbdev->shader_inuse_bitmap;
if (0 == kbdev->pm.active_count)
return 0;
return desired;
}
static bool demand_get_core_active(struct kbase_device *kbdev)
{
if (0 == kbdev->pm.active_count && !(kbdev->shader_needed_bitmap |
kbdev->shader_inuse_bitmap))
return false;
return true;
}
static void demand_init(struct kbase_device *kbdev)
{
CSTD_UNUSED(kbdev);
}
static void demand_term(struct kbase_device *kbdev)
{
CSTD_UNUSED(kbdev);
}
/*
* The struct kbase_pm_policy structure for the demand power policy.
*
* This is the static structure that defines the demand power policy's callback
* and name.
*/
const struct kbase_pm_policy kbase_pm_demand_policy_ops = {
"demand", /* name */
demand_init, /* init */
demand_term, /* term */
demand_get_core_mask, /* get_core_mask */
demand_get_core_active, /* get_core_active */
0u, /* flags */
KBASE_PM_POLICY_ID_DEMAND, /* id */
};
KBASE_EXPORT_TEST_API(kbase_pm_demand_policy_ops);

View file

@ -0,0 +1,64 @@
/*
*
* (C) COPYRIGHT 2011-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* A simple demand based power management policy
*/
#ifndef MALI_KBASE_PM_DEMAND_H
#define MALI_KBASE_PM_DEMAND_H
/**
* DOC: Demand power management policy
*
* The demand power management policy has the following characteristics:
* - When KBase indicates that the GPU will be powered up, but we don't yet
* know which Job Chains are to be run:
* - The Shader Cores are not powered up
*
* - When KBase indicates that a set of Shader Cores are needed to submit the
* currently queued Job Chains:
* - Only those Shader Cores are powered up
*
* - When KBase indicates that the GPU need not be powered:
* - The Shader Cores are powered off, and the GPU itself is powered off too.
*
* Note:
* - KBase indicates the GPU will be powered up when it has a User Process that
* has just started to submit Job Chains.
*
* - KBase indicates the GPU need not be powered when all the Job Chains from
* User Processes have finished, and it is waiting for a User Process to
* submit some more Job Chains.
*/
/**
* struct kbasep_pm_policy_demand - Private structure for policy instance data
*
* @dummy: No state is needed, a dummy variable
*
* This contains data that is private to the demand power policy.
*/
struct kbasep_pm_policy_demand {
int dummy;
};
extern const struct kbase_pm_policy kbase_pm_demand_policy_ops;
#endif /* MALI_KBASE_PM_DEMAND_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,516 @@
/*
*
* (C) COPYRIGHT 2010-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Power management API definitions used internally by GPU backend
*/
#ifndef _KBASE_BACKEND_PM_INTERNAL_H_
#define _KBASE_BACKEND_PM_INTERNAL_H_
#include <mali_kbase_hwaccess_pm.h>
#include "mali_kbase_pm_ca.h"
#include "mali_kbase_pm_policy.h"
/**
* kbase_pm_dev_idle - The GPU is idle.
*
* The OS may choose to turn off idle devices
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
*/
void kbase_pm_dev_idle(struct kbase_device *kbdev);
/**
* kbase_pm_dev_activate - The GPU is active.
*
* The OS should avoid opportunistically turning off the GPU while it is active
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
*/
void kbase_pm_dev_activate(struct kbase_device *kbdev);
/**
* kbase_pm_get_present_cores - Get details of the cores that are present in
* the device.
*
* This function can be called by the active power policy to return a bitmask of
* the cores (of a specified type) present in the GPU device and also a count of
* the number of cores.
*
* @kbdev: The kbase device structure for the device (must be a valid
* pointer)
* @type: The type of core (see the enum kbase_pm_core_type enumeration)
*
* Return: The bit mask of cores present
*/
u64 kbase_pm_get_present_cores(struct kbase_device *kbdev,
enum kbase_pm_core_type type);
/**
* kbase_pm_get_active_cores - Get details of the cores that are currently
* active in the device.
*
* This function can be called by the active power policy to return a bitmask of
* the cores (of a specified type) that are actively processing work (i.e.
* turned on *and* busy).
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
* @type: The type of core (see the enum kbase_pm_core_type enumeration)
*
* Return: The bit mask of active cores
*/
u64 kbase_pm_get_active_cores(struct kbase_device *kbdev,
enum kbase_pm_core_type type);
/**
* kbase_pm_get_trans_cores - Get details of the cores that are currently
* transitioning between power states.
*
* This function can be called by the active power policy to return a bitmask of
* the cores (of a specified type) that are currently transitioning between
* power states.
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
* @type: The type of core (see the enum kbase_pm_core_type enumeration)
*
* Return: The bit mask of transitioning cores
*/
u64 kbase_pm_get_trans_cores(struct kbase_device *kbdev,
enum kbase_pm_core_type type);
/**
* kbase_pm_get_ready_cores - Get details of the cores that are currently
* powered and ready for jobs.
*
* This function can be called by the active power policy to return a bitmask of
* the cores (of a specified type) that are powered and ready for jobs (they may
* or may not be currently executing jobs).
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
* @type: The type of core (see the enum kbase_pm_core_type enumeration)
*
* Return: The bit mask of ready cores
*/
u64 kbase_pm_get_ready_cores(struct kbase_device *kbdev,
enum kbase_pm_core_type type);
/**
* kbase_pm_clock_on - Turn the clock for the device on, and enable device
* interrupts.
*
* This function can be used by a power policy to turn the clock for the GPU on.
* It should be modified during integration to perform the necessary actions to
* ensure that the GPU is fully powered and clocked.
*
* @kbdev: The kbase device structure for the device (must be a valid
* pointer)
* @is_resume: true if clock on due to resume after suspend, false otherwise
*/
void kbase_pm_clock_on(struct kbase_device *kbdev, bool is_resume);
/**
* kbase_pm_clock_off - Disable device interrupts, and turn the clock for the
* device off.
*
* This function can be used by a power policy to turn the clock for the GPU
* off. It should be modified during integration to perform the necessary
* actions to turn the clock off (if this is possible in the integration).
*
* @kbdev: The kbase device structure for the device (must be a valid
* pointer)
* @is_suspend: true if clock off due to suspend, false otherwise
*
* Return: true if clock was turned off, or
* false if clock can not be turned off due to pending page/bus fault
* workers. Caller must flush MMU workqueues and retry
*/
bool kbase_pm_clock_off(struct kbase_device *kbdev, bool is_suspend);
/**
* kbase_pm_enable_interrupts - Enable interrupts on the device.
*
* Interrupts are also enabled after a call to kbase_pm_clock_on().
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
*/
void kbase_pm_enable_interrupts(struct kbase_device *kbdev);
/**
* kbase_pm_enable_interrupts_mmu_mask - Enable interrupts on the device, using
* the provided mask to set MMU_IRQ_MASK.
*
* Interrupts are also enabled after a call to kbase_pm_clock_on().
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
* @mask: The mask to use for MMU_IRQ_MASK
*/
void kbase_pm_enable_interrupts_mmu_mask(struct kbase_device *kbdev, u32 mask);
/**
* kbase_pm_disable_interrupts - Disable interrupts on the device.
*
* This prevents delivery of Power Management interrupts to the CPU so that
* kbase_pm_check_transitions_nolock() will not be called from the IRQ handler
* until kbase_pm_enable_interrupts() or kbase_pm_clock_on() is called.
*
* Interrupts are also disabled after a call to kbase_pm_clock_off().
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
*/
void kbase_pm_disable_interrupts(struct kbase_device *kbdev);
/**
* kbase_pm_init_hw - Initialize the hardware.
* @kbdev: The kbase device structure for the device (must be a valid pointer)
* @flags: Flags specifying the type of PM init
*
* This function checks the GPU ID register to ensure that the GPU is supported
* by the driver and performs a reset on the device so that it is in a known
* state before the device is used.
*
* Return: 0 if the device is supported and successfully reset.
*/
int kbase_pm_init_hw(struct kbase_device *kbdev, unsigned int flags);
/**
* kbase_pm_reset_done - The GPU has been reset successfully.
*
* This function must be called by the GPU interrupt handler when the
* RESET_COMPLETED bit is set. It signals to the power management initialization
* code that the GPU has been successfully reset.
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
*/
void kbase_pm_reset_done(struct kbase_device *kbdev);
/**
* kbase_pm_check_transitions_nolock - Check if there are any power transitions
* to make, and if so start them.
*
* This function will check the desired_xx_state members of
* struct kbase_pm_device_data and the actual status of the hardware to see if
* any power transitions can be made at this time to make the hardware state
* closer to the state desired by the power policy.
*
* The return value can be used to check whether all the desired cores are
* available, and so whether it's worth submitting a job (e.g. from a Power
* Management IRQ).
*
* Note that this still returns true when desired_xx_state has no
* cores. That is: of the no cores desired, none were *un*available. In
* this case, the caller may still need to try submitting jobs. This is because
* the Core Availability Policy might have taken us to an intermediate state
* where no cores are powered, before powering on more cores (e.g. for core
* rotation)
*
* The caller must hold kbase_device.pm.power_change_lock
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
*
* Return: non-zero when all desired cores are available. That is,
* it's worthwhile for the caller to submit a job.
* false otherwise
*/
bool kbase_pm_check_transitions_nolock(struct kbase_device *kbdev);
/**
* kbase_pm_check_transitions_sync - Synchronous and locking variant of
* kbase_pm_check_transitions_nolock()
*
* On returning, the desired state at the time of the call will have been met.
*
* There is nothing to stop the core being switched off by calls to
* kbase_pm_release_cores() or kbase_pm_unrequest_cores(). Therefore, the
* caller must have already made a call to
* kbase_pm_request_cores()/kbase_pm_request_cores_sync() previously.
*
* The usual use-case for this is to ensure cores are 'READY' after performing
* a GPU Reset.
*
* Unlike kbase_pm_check_transitions_nolock(), the caller must not hold
* kbase_device.pm.power_change_lock, because this function will take that
* lock itself.
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
*/
void kbase_pm_check_transitions_sync(struct kbase_device *kbdev);
/**
* kbase_pm_update_cores_state_nolock - Variant of kbase_pm_update_cores_state()
* where the caller must hold
* kbase_device.pm.power_change_lock
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
*/
void kbase_pm_update_cores_state_nolock(struct kbase_device *kbdev);
/**
* kbase_pm_update_cores_state - Update the desired state of shader cores from
* the Power Policy, and begin any power
* transitions.
*
* This function will update the desired_xx_state members of
* struct kbase_pm_device_data by calling into the current Power Policy. It will
* then begin power transitions to make the hardware acheive the desired shader
* core state.
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
*/
void kbase_pm_update_cores_state(struct kbase_device *kbdev);
/**
* kbase_pm_cancel_deferred_poweroff - Cancel any pending requests to power off
* the GPU and/or shader cores.
*
* This should be called by any functions which directly power off the GPU.
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
*/
void kbase_pm_cancel_deferred_poweroff(struct kbase_device *kbdev);
/**
* kbasep_pm_read_present_cores - Read the bitmasks of present cores.
*
* This information is cached to avoid having to perform register reads whenever
* the information is required.
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
*/
void kbasep_pm_read_present_cores(struct kbase_device *kbdev);
/**
* kbasep_pm_metrics_init - Initialize the metrics gathering framework.
*
* This must be called before other metric gathering APIs are called.
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
*
* Return: 0 on success, error code on error
*/
int kbasep_pm_metrics_init(struct kbase_device *kbdev);
/**
* kbasep_pm_metrics_term - Terminate the metrics gathering framework.
*
* This must be called when metric gathering is no longer required. It is an
* error to call any metrics gathering function (other than
* kbasep_pm_metrics_init()) after calling this function.
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
*/
void kbasep_pm_metrics_term(struct kbase_device *kbdev);
/**
* kbase_pm_report_vsync - Function to be called by the frame buffer driver to
* update the vsync metric.
*
* This function should be called by the frame buffer driver to update whether
* the system is hitting the vsync target or not. buffer_updated should be true
* if the vsync corresponded with a new frame being displayed, otherwise it
* should be false. This function does not need to be called every vsync, but
* only when the value of @buffer_updated differs from a previous call.
*
* @kbdev: The kbase device structure for the device (must be a
* valid pointer)
* @buffer_updated: True if the buffer has been updated on this VSync,
* false otherwise
*/
void kbase_pm_report_vsync(struct kbase_device *kbdev, int buffer_updated);
/**
* kbase_pm_get_dvfs_action - Determine whether the DVFS system should change
* the clock speed of the GPU.
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
*
* This function should be called regularly by the DVFS system to check whether
* the clock speed of the GPU needs updating.
*/
void kbase_pm_get_dvfs_action(struct kbase_device *kbdev);
/**
* kbase_pm_request_gpu_cycle_counter - Mark that the GPU cycle counter is
* needed
*
* If the caller is the first caller then the GPU cycle counters will be enabled
* along with the l2 cache
*
* The GPU must be powered when calling this function (i.e.
* kbase_pm_context_active() must have been called).
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
*/
void kbase_pm_request_gpu_cycle_counter(struct kbase_device *kbdev);
/**
* kbase_pm_request_gpu_cycle_counter_l2_is_on - Mark GPU cycle counter is
* needed (l2 cache already on)
*
* This is a version of the above function
* (kbase_pm_request_gpu_cycle_counter()) suitable for being called when the
* l2 cache is known to be on and assured to be on until the subsequent call of
* kbase_pm_release_gpu_cycle_counter() such as when a job is submitted. It does
* not sleep and can be called from atomic functions.
*
* The GPU must be powered when calling this function (i.e.
* kbase_pm_context_active() must have been called) and the l2 cache must be
* powered on.
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
*/
void kbase_pm_request_gpu_cycle_counter_l2_is_on(struct kbase_device *kbdev);
/**
* kbase_pm_release_gpu_cycle_counter - Mark that the GPU cycle counter is no
* longer in use
*
* If the caller is the
* last caller then the GPU cycle counters will be disabled. A request must have
* been made before a call to this.
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
*/
void kbase_pm_release_gpu_cycle_counter(struct kbase_device *kbdev);
/**
* kbase_pm_register_access_enable - Enable access to GPU registers
*
* Enables access to the GPU registers before power management has powered up
* the GPU with kbase_pm_powerup().
*
* Access to registers should be done using kbase_os_reg_read()/write() at this
* stage, not kbase_reg_read()/write().
*
* This results in the power management callbacks provided in the driver
* configuration to get called to turn on power and/or clocks to the GPU. See
* kbase_pm_callback_conf.
*
* This should only be used before power management is powered up with
* kbase_pm_powerup()
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
*/
void kbase_pm_register_access_enable(struct kbase_device *kbdev);
/**
* kbase_pm_register_access_disable - Disable early register access
*
* Disables access to the GPU registers enabled earlier by a call to
* kbase_pm_register_access_enable().
*
* This results in the power management callbacks provided in the driver
* configuration to get called to turn off power and/or clocks to the GPU. See
* kbase_pm_callback_conf
*
* This should only be used before power management is powered up with
* kbase_pm_powerup()
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
*/
void kbase_pm_register_access_disable(struct kbase_device *kbdev);
/* NOTE: kbase_pm_is_suspending is in mali_kbase.h, because it is an inline
* function */
/**
* kbase_pm_metrics_is_active - Check if the power management metrics
* collection is active.
*
* Note that this returns if the power management metrics collection was
* active at the time of calling, it is possible that after the call the metrics
* collection enable may have changed state.
*
* The caller must handle the consequence that the state may have changed.
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
* Return: true if metrics collection was active else false.
*/
bool kbase_pm_metrics_is_active(struct kbase_device *kbdev);
/**
* kbase_pm_do_poweron - Power on the GPU, and any cores that are requested.
*
* @kbdev: The kbase device structure for the device (must be a valid
* pointer)
* @is_resume: true if power on due to resume after suspend,
* false otherwise
*/
void kbase_pm_do_poweron(struct kbase_device *kbdev, bool is_resume);
/**
* kbase_pm_do_poweroff - Power off the GPU, and any cores that have been
* requested.
*
* @kbdev: The kbase device structure for the device (must be a valid
* pointer)
* @is_suspend: true if power off due to suspend,
* false otherwise
* Return:
* true if power was turned off, else
* false if power can not be turned off due to pending page/bus
* fault workers. Caller must flush MMU workqueues and retry
*/
bool kbase_pm_do_poweroff(struct kbase_device *kbdev, bool is_suspend);
#ifdef CONFIG_PM_DEVFREQ
void kbase_pm_get_dvfs_utilisation(struct kbase_device *kbdev,
unsigned long *total, unsigned long *busy);
void kbase_pm_reset_dvfs_utilisation(struct kbase_device *kbdev);
#endif
#ifdef CONFIG_MALI_MIDGARD_DVFS
/**
* kbase_platform_dvfs_event - Report utilisation to DVFS code
*
* Function provided by platform specific code when DVFS is enabled to allow
* the power management metrics system to report utilisation.
*
* @kbdev: The kbase device structure for the device (must be a
* valid pointer)
* @utilisation: The current calculated utilisation by the metrics system.
* @util_gl_share: The current calculated gl share of utilisation.
* @util_cl_share: The current calculated cl share of utilisation per core
* group.
* Return: Returns 0 on failure and non zero on success.
*/
int kbase_platform_dvfs_event(struct kbase_device *kbdev, u32 utilisation,
u32 util_gl_share, u32 util_cl_share[2]);
#endif
void kbase_pm_power_changed(struct kbase_device *kbdev);
/**
* kbase_pm_metrics_update - Inform the metrics system that an atom is either
* about to be run or has just completed.
* @kbdev: The kbase device structure for the device (must be a valid pointer)
* @now: Pointer to the timestamp of the change, or NULL to use current time
*
* Caller must hold runpool_irq.lock
*/
void kbase_pm_metrics_update(struct kbase_device *kbdev,
ktime_t *now);
#endif /* _KBASE_BACKEND_PM_INTERNAL_H_ */

View file

@ -0,0 +1,422 @@
/*
*
* (C) COPYRIGHT 2011-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Metrics for power management
*/
#include <mali_kbase.h>
#include <mali_kbase_pm.h>
#include <backend/gpu/mali_kbase_pm_internal.h>
#include <backend/gpu/mali_kbase_jm_rb.h>
/* When VSync is being hit aim for utilisation between 70-90% */
#define KBASE_PM_VSYNC_MIN_UTILISATION 70
#define KBASE_PM_VSYNC_MAX_UTILISATION 90
/* Otherwise aim for 10-40% */
#define KBASE_PM_NO_VSYNC_MIN_UTILISATION 10
#define KBASE_PM_NO_VSYNC_MAX_UTILISATION 40
/* Shift used for kbasep_pm_metrics_data.time_busy/idle - units of (1 << 8) ns
* This gives a maximum period between samples of 2^(32+8)/100 ns = slightly
* under 11s. Exceeding this will cause overflow */
#define KBASE_PM_TIME_SHIFT 8
/* Maximum time between sampling of utilization data, without resetting the
* counters. */
#define MALI_UTILIZATION_MAX_PERIOD 100000 /* ns = 100ms */
#ifdef CONFIG_MALI_MIDGARD_DVFS
static enum hrtimer_restart dvfs_callback(struct hrtimer *timer)
{
unsigned long flags;
struct kbasep_pm_metrics_data *metrics;
KBASE_DEBUG_ASSERT(timer != NULL);
metrics = container_of(timer, struct kbasep_pm_metrics_data, timer);
kbase_pm_get_dvfs_action(metrics->kbdev);
spin_lock_irqsave(&metrics->lock, flags);
if (metrics->timer_active)
hrtimer_start(timer,
HR_TIMER_DELAY_MSEC(metrics->kbdev->pm.dvfs_period),
HRTIMER_MODE_REL);
spin_unlock_irqrestore(&metrics->lock, flags);
return HRTIMER_NORESTART;
}
#endif /* CONFIG_MALI_MIDGARD_DVFS */
int kbasep_pm_metrics_init(struct kbase_device *kbdev)
{
KBASE_DEBUG_ASSERT(kbdev != NULL);
kbdev->pm.backend.metrics.kbdev = kbdev;
kbdev->pm.backend.metrics.time_period_start = ktime_get();
kbdev->pm.backend.metrics.time_busy = 0;
kbdev->pm.backend.metrics.time_idle = 0;
kbdev->pm.backend.metrics.prev_busy = 0;
kbdev->pm.backend.metrics.prev_idle = 0;
kbdev->pm.backend.metrics.gpu_active = false;
kbdev->pm.backend.metrics.active_cl_ctx[0] = 0;
kbdev->pm.backend.metrics.active_cl_ctx[1] = 0;
kbdev->pm.backend.metrics.active_gl_ctx[0] = 0;
kbdev->pm.backend.metrics.active_gl_ctx[1] = 0;
kbdev->pm.backend.metrics.busy_cl[0] = 0;
kbdev->pm.backend.metrics.busy_cl[1] = 0;
kbdev->pm.backend.metrics.busy_gl = 0;
spin_lock_init(&kbdev->pm.backend.metrics.lock);
/* MALI_SEC_INTEGRATION */
if(kbdev->vendor_callbacks->pm_metrics_init)
kbdev->vendor_callbacks->pm_metrics_init(kbdev);
else
{
#ifdef CONFIG_MALI_MIDGARD_DVFS
kbdev->pm.backend.metrics.timer_active = true;
hrtimer_init(&kbdev->pm.backend.metrics.timer, CLOCK_MONOTONIC,
HRTIMER_MODE_REL);
kbdev->pm.backend.metrics.timer.function = dvfs_callback;
hrtimer_start(&kbdev->pm.backend.metrics.timer,
HR_TIMER_DELAY_MSEC(kbdev->pm.dvfs_period),
HRTIMER_MODE_REL);
#endif /* CONFIG_MALI_MIDGARD_DVFS */
}
/* MALI_SEC_INTEGRATION */
if(kbdev->vendor_callbacks->cl_boost_init)
kbdev->vendor_callbacks->cl_boost_init(kbdev);
return 0;
}
KBASE_EXPORT_TEST_API(kbasep_pm_metrics_init);
void kbasep_pm_metrics_term(struct kbase_device *kbdev)
{
#ifdef CONFIG_MALI_MIDGARD_DVFS
unsigned long flags;
KBASE_DEBUG_ASSERT(kbdev != NULL);
spin_lock_irqsave(&kbdev->pm.backend.metrics.lock, flags);
kbdev->pm.backend.metrics.timer_active = false;
spin_unlock_irqrestore(&kbdev->pm.backend.metrics.lock, flags);
hrtimer_cancel(&kbdev->pm.backend.metrics.timer);
#endif /* CONFIG_MALI_MIDGARD_DVFS */
/* MALI_SEC_INTEGRATION */
if(kbdev->vendor_callbacks->pm_metrics_term)
kbdev->vendor_callbacks->pm_metrics_term(kbdev);
}
KBASE_EXPORT_TEST_API(kbasep_pm_metrics_term);
/* caller needs to hold kbdev->pm.backend.metrics.lock before calling this
* function
*/
static void kbase_pm_get_dvfs_utilisation_calc(struct kbase_device *kbdev,
ktime_t now)
{
ktime_t diff;
lockdep_assert_held(&kbdev->pm.backend.metrics.lock);
diff = ktime_sub(now, kbdev->pm.backend.metrics.time_period_start);
if (ktime_to_ns(diff) < 0)
return;
if (kbdev->pm.backend.metrics.gpu_active) {
u32 ns_time = (u32) (ktime_to_ns(diff) >> KBASE_PM_TIME_SHIFT);
kbdev->pm.backend.metrics.time_busy += ns_time;
if (kbdev->pm.backend.metrics.active_cl_ctx[0])
kbdev->pm.backend.metrics.busy_cl[0] += ns_time;
if (kbdev->pm.backend.metrics.active_cl_ctx[1])
kbdev->pm.backend.metrics.busy_cl[1] += ns_time;
if (kbdev->pm.backend.metrics.active_gl_ctx[0])
kbdev->pm.backend.metrics.busy_gl += ns_time;
if (kbdev->pm.backend.metrics.active_gl_ctx[1])
kbdev->pm.backend.metrics.busy_gl += ns_time;
} else {
kbdev->pm.backend.metrics.time_idle += (u32) (ktime_to_ns(diff)
>> KBASE_PM_TIME_SHIFT);
}
kbdev->pm.backend.metrics.time_period_start = now;
}
#if defined(CONFIG_PM_DEVFREQ) || defined(CONFIG_MALI_MIDGARD_DVFS)
/* Caller needs to hold kbdev->pm.backend.metrics.lock before calling this
* function.
*/
static void kbase_pm_reset_dvfs_utilisation_unlocked(struct kbase_device *kbdev,
ktime_t now)
{
/* Store previous value */
kbdev->pm.backend.metrics.prev_idle =
kbdev->pm.backend.metrics.time_idle;
kbdev->pm.backend.metrics.prev_busy =
kbdev->pm.backend.metrics.time_busy;
/* Reset current values */
kbdev->pm.backend.metrics.time_period_start = now;
kbdev->pm.backend.metrics.time_idle = 0;
kbdev->pm.backend.metrics.time_busy = 0;
kbdev->pm.backend.metrics.busy_cl[0] = 0;
kbdev->pm.backend.metrics.busy_cl[1] = 0;
kbdev->pm.backend.metrics.busy_gl = 0;
}
void kbase_pm_reset_dvfs_utilisation(struct kbase_device *kbdev)
{
unsigned long flags;
spin_lock_irqsave(&kbdev->pm.backend.metrics.lock, flags);
kbase_pm_reset_dvfs_utilisation_unlocked(kbdev, ktime_get());
spin_unlock_irqrestore(&kbdev->pm.backend.metrics.lock, flags);
}
void kbase_pm_get_dvfs_utilisation(struct kbase_device *kbdev,
unsigned long *total_out, unsigned long *busy_out)
{
ktime_t now = ktime_get();
unsigned long flags, busy, total;
spin_lock_irqsave(&kbdev->pm.backend.metrics.lock, flags);
kbase_pm_get_dvfs_utilisation_calc(kbdev, now);
busy = kbdev->pm.backend.metrics.time_busy;
total = busy + kbdev->pm.backend.metrics.time_idle;
/* Reset stats if older than MALI_UTILIZATION_MAX_PERIOD (default
* 100ms) */
if (total >= MALI_UTILIZATION_MAX_PERIOD) {
kbase_pm_reset_dvfs_utilisation_unlocked(kbdev, now);
} else if (total < (MALI_UTILIZATION_MAX_PERIOD / 2)) {
total += kbdev->pm.backend.metrics.prev_idle +
kbdev->pm.backend.metrics.prev_busy;
busy += kbdev->pm.backend.metrics.prev_busy;
}
*total_out = total;
*busy_out = busy;
spin_unlock_irqrestore(&kbdev->pm.backend.metrics.lock, flags);
}
#endif
#ifdef CONFIG_MALI_MIDGARD_DVFS
/* caller needs to hold kbdev->pm.backend.metrics.lock before calling this
* function
*/
int kbase_pm_get_dvfs_utilisation_old(struct kbase_device *kbdev,
int *util_gl_share,
int util_cl_share[2],
ktime_t now)
{
int utilisation;
int busy;
kbase_pm_get_dvfs_utilisation_calc(kbdev, now);
if (kbdev->pm.backend.metrics.time_idle +
kbdev->pm.backend.metrics.time_busy == 0) {
/* No data - so we return NOP */
utilisation = -1;
if (util_gl_share)
*util_gl_share = -1;
if (util_cl_share) {
util_cl_share[0] = -1;
util_cl_share[1] = -1;
}
goto out;
}
utilisation = (100 * kbdev->pm.backend.metrics.time_busy) /
(kbdev->pm.backend.metrics.time_idle +
kbdev->pm.backend.metrics.time_busy);
busy = kbdev->pm.backend.metrics.busy_gl +
kbdev->pm.backend.metrics.busy_cl[0] +
kbdev->pm.backend.metrics.busy_cl[1];
if (busy != 0) {
if (util_gl_share)
*util_gl_share =
(100 * kbdev->pm.backend.metrics.busy_gl) /
busy;
if (util_cl_share) {
util_cl_share[0] =
(100 * kbdev->pm.backend.metrics.busy_cl[0]) /
busy;
util_cl_share[1] =
(100 * kbdev->pm.backend.metrics.busy_cl[1]) /
busy;
}
} else {
if (util_gl_share)
*util_gl_share = -1;
if (util_cl_share) {
util_cl_share[0] = -1;
util_cl_share[1] = -1;
}
}
out:
return utilisation;
}
void kbase_pm_get_dvfs_action(struct kbase_device *kbdev)
{
unsigned long flags;
int utilisation, util_gl_share;
int util_cl_share[2];
ktime_t now;
KBASE_DEBUG_ASSERT(kbdev != NULL);
spin_lock_irqsave(&kbdev->pm.backend.metrics.lock, flags);
now = ktime_get();
utilisation = kbase_pm_get_dvfs_utilisation_old(kbdev, &util_gl_share,
util_cl_share, now);
if (utilisation < 0 || util_gl_share < 0 || util_cl_share[0] < 0 ||
util_cl_share[1] < 0) {
utilisation = 0;
util_gl_share = 0;
util_cl_share[0] = 0;
util_cl_share[1] = 0;
goto out;
}
out:
#ifdef CONFIG_MALI_MIDGARD_DVFS
kbase_platform_dvfs_event(kbdev, utilisation, util_gl_share,
util_cl_share);
#endif /*CONFIG_MALI_MIDGARD_DVFS */
kbase_pm_reset_dvfs_utilisation_unlocked(kbdev, now);
spin_unlock_irqrestore(&kbdev->pm.backend.metrics.lock, flags);
}
bool kbase_pm_metrics_is_active(struct kbase_device *kbdev)
{
bool isactive;
unsigned long flags;
KBASE_DEBUG_ASSERT(kbdev != NULL);
spin_lock_irqsave(&kbdev->pm.backend.metrics.lock, flags);
isactive = kbdev->pm.backend.metrics.timer_active;
spin_unlock_irqrestore(&kbdev->pm.backend.metrics.lock, flags);
return isactive;
}
KBASE_EXPORT_TEST_API(kbase_pm_metrics_is_active);
#endif /* CONFIG_MALI_MIDGARD_DVFS */
/**
* kbase_pm_metrics_active_calc - Update PM active counts based on currently
* running atoms
* @kbdev: Device pointer
*
* The caller must hold kbdev->pm.backend.metrics.lock
*/
static void kbase_pm_metrics_active_calc(struct kbase_device *kbdev)
{
int js;
lockdep_assert_held(&kbdev->pm.backend.metrics.lock);
kbdev->pm.backend.metrics.active_gl_ctx[0] = 0;
kbdev->pm.backend.metrics.active_gl_ctx[1] = 0;
kbdev->pm.backend.metrics.active_cl_ctx[0] = 0;
kbdev->pm.backend.metrics.active_cl_ctx[1] = 0;
#ifdef MALI_SEC_UTILIZATION
/*Setting gpu_active here may show real GPU utilization but it can't make full utilization(100%) */
#else
kbdev->pm.backend.metrics.gpu_active = false;
#endif
for (js = 0; js < BASE_JM_MAX_NR_SLOTS; js++) {
struct kbase_jd_atom *katom = kbase_gpu_inspect(kbdev, js, 0);
/* Head atom may have just completed, so if it isn't running
* then try the next atom */
if (katom && katom->gpu_rb_state != KBASE_ATOM_GPU_RB_SUBMITTED)
katom = kbase_gpu_inspect(kbdev, js, 1);
if (katom && katom->gpu_rb_state ==
KBASE_ATOM_GPU_RB_SUBMITTED) {
if (katom->core_req & BASE_JD_REQ_ONLY_COMPUTE) {
int device_nr = (katom->core_req &
BASE_JD_REQ_SPECIFIC_COHERENT_GROUP)
? katom->device_nr : 0;
WARN_ON(device_nr >= 2);
kbdev->pm.backend.metrics.active_cl_ctx[
device_nr] = 1;
} else {
/* Slot 2 should not be running non-compute
* atoms */
WARN_ON(js >= 2);
kbdev->pm.backend.metrics.active_gl_ctx[js] = 1;
}
#ifdef MALI_SEC_UTILIZATION
/*Setting gpu_active here may show real GPU utilization but it can't make full utilization(100%) */
#else
kbdev->pm.backend.metrics.gpu_active = true;
#endif
}
}
}
/* called when job is submitted to or removed from a GPU slot */
void kbase_pm_metrics_update(struct kbase_device *kbdev, ktime_t *timestamp)
{
unsigned long flags;
ktime_t now;
lockdep_assert_held(&kbdev->js_data.runpool_irq.lock);
spin_lock_irqsave(&kbdev->pm.backend.metrics.lock, flags);
if (!timestamp) {
now = ktime_get();
timestamp = &now;
}
/* Track how long CL and/or GL jobs have been busy for */
kbase_pm_get_dvfs_utilisation_calc(kbdev, *timestamp);
kbase_pm_metrics_active_calc(kbdev);
spin_unlock_irqrestore(&kbdev->pm.backend.metrics.lock, flags);
}

View file

@ -0,0 +1,38 @@
/*
*
* (C) COPYRIGHT 2011-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Dummy Metrics for power management.
*/
#include <mali_kbase.h>
#include <mali_kbase_pm.h>
void kbase_pm_register_vsync_callback(struct kbase_device *kbdev)
{
KBASE_DEBUG_ASSERT(kbdev != NULL);
/* no VSync metrics will be available */
kbdev->pm.backend.metrics.platform_data = NULL;
}
void kbase_pm_unregister_vsync_callback(struct kbase_device *kbdev)
{
KBASE_DEBUG_ASSERT(kbdev != NULL);
}

View file

@ -0,0 +1,936 @@
/*
*
* (C) COPYRIGHT 2010-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Power policy API implementations
*/
#include <mali_kbase.h>
#include <mali_midg_regmap.h>
#include <mali_kbase_gator.h>
#include <mali_kbase_pm.h>
#include <mali_kbase_config_defaults.h>
#include <backend/gpu/mali_kbase_pm_internal.h>
static const struct kbase_pm_policy *const policy_list[] = {
#ifdef CONFIG_MALI_NO_MALI
&kbase_pm_always_on_policy_ops,
&kbase_pm_demand_policy_ops,
&kbase_pm_coarse_demand_policy_ops,
#if !MALI_CUSTOMER_RELEASE
&kbase_pm_demand_always_powered_policy_ops,
&kbase_pm_fast_start_policy_ops,
#endif
#else /* CONFIG_MALI_NO_MALI */
&kbase_pm_demand_policy_ops,
&kbase_pm_always_on_policy_ops,
&kbase_pm_coarse_demand_policy_ops,
#if !MALI_CUSTOMER_RELEASE
&kbase_pm_demand_always_powered_policy_ops,
&kbase_pm_fast_start_policy_ops,
#endif
#endif /* CONFIG_MALI_NO_MALI */
};
/* The number of policies available in the system.
* This is derived from the number of functions listed in policy_get_functions.
*/
#define POLICY_COUNT (sizeof(policy_list)/sizeof(*policy_list))
/* Function IDs for looking up Timeline Trace codes in
* kbase_pm_change_state_trace_code */
enum kbase_pm_func_id {
KBASE_PM_FUNC_ID_REQUEST_CORES_START,
KBASE_PM_FUNC_ID_REQUEST_CORES_END,
KBASE_PM_FUNC_ID_RELEASE_CORES_START,
KBASE_PM_FUNC_ID_RELEASE_CORES_END,
/* Note: kbase_pm_unrequest_cores() is on the slow path, and we neither
* expect to hit it nor tend to hit it very much anyway. We can detect
* whether we need more instrumentation by a difference between
* PM_CHECKTRANS events and PM_SEND/HANDLE_EVENT. */
/* Must be the last */
KBASE_PM_FUNC_ID_COUNT
};
/* State changes during request/unrequest/release-ing cores */
enum {
KBASE_PM_CHANGE_STATE_SHADER = (1u << 0),
KBASE_PM_CHANGE_STATE_TILER = (1u << 1),
/* These two must be last */
KBASE_PM_CHANGE_STATE_MASK = (KBASE_PM_CHANGE_STATE_TILER |
KBASE_PM_CHANGE_STATE_SHADER),
KBASE_PM_CHANGE_STATE_COUNT = KBASE_PM_CHANGE_STATE_MASK + 1
};
typedef u32 kbase_pm_change_state;
#ifdef CONFIG_MALI_TRACE_TIMELINE
/* Timeline Trace code lookups for each function */
static u32 kbase_pm_change_state_trace_code[KBASE_PM_FUNC_ID_COUNT]
[KBASE_PM_CHANGE_STATE_COUNT] = {
/* kbase_pm_request_cores */
[KBASE_PM_FUNC_ID_REQUEST_CORES_START][0] = 0,
[KBASE_PM_FUNC_ID_REQUEST_CORES_START][KBASE_PM_CHANGE_STATE_SHADER] =
SW_FLOW_PM_CHECKTRANS_PM_REQUEST_CORES_SHADER_START,
[KBASE_PM_FUNC_ID_REQUEST_CORES_START][KBASE_PM_CHANGE_STATE_TILER] =
SW_FLOW_PM_CHECKTRANS_PM_REQUEST_CORES_TILER_START,
[KBASE_PM_FUNC_ID_REQUEST_CORES_START][KBASE_PM_CHANGE_STATE_SHADER |
KBASE_PM_CHANGE_STATE_TILER] =
SW_FLOW_PM_CHECKTRANS_PM_REQUEST_CORES_SHADER_TILER_START,
[KBASE_PM_FUNC_ID_REQUEST_CORES_END][0] = 0,
[KBASE_PM_FUNC_ID_REQUEST_CORES_END][KBASE_PM_CHANGE_STATE_SHADER] =
SW_FLOW_PM_CHECKTRANS_PM_REQUEST_CORES_SHADER_END,
[KBASE_PM_FUNC_ID_REQUEST_CORES_END][KBASE_PM_CHANGE_STATE_TILER] =
SW_FLOW_PM_CHECKTRANS_PM_REQUEST_CORES_TILER_END,
[KBASE_PM_FUNC_ID_REQUEST_CORES_END][KBASE_PM_CHANGE_STATE_SHADER |
KBASE_PM_CHANGE_STATE_TILER] =
SW_FLOW_PM_CHECKTRANS_PM_REQUEST_CORES_SHADER_TILER_END,
/* kbase_pm_release_cores */
[KBASE_PM_FUNC_ID_RELEASE_CORES_START][0] = 0,
[KBASE_PM_FUNC_ID_RELEASE_CORES_START][KBASE_PM_CHANGE_STATE_SHADER] =
SW_FLOW_PM_CHECKTRANS_PM_RELEASE_CORES_SHADER_START,
[KBASE_PM_FUNC_ID_RELEASE_CORES_START][KBASE_PM_CHANGE_STATE_TILER] =
SW_FLOW_PM_CHECKTRANS_PM_RELEASE_CORES_TILER_START,
[KBASE_PM_FUNC_ID_RELEASE_CORES_START][KBASE_PM_CHANGE_STATE_SHADER |
KBASE_PM_CHANGE_STATE_TILER] =
SW_FLOW_PM_CHECKTRANS_PM_RELEASE_CORES_SHADER_TILER_START,
[KBASE_PM_FUNC_ID_RELEASE_CORES_END][0] = 0,
[KBASE_PM_FUNC_ID_RELEASE_CORES_END][KBASE_PM_CHANGE_STATE_SHADER] =
SW_FLOW_PM_CHECKTRANS_PM_RELEASE_CORES_SHADER_END,
[KBASE_PM_FUNC_ID_RELEASE_CORES_END][KBASE_PM_CHANGE_STATE_TILER] =
SW_FLOW_PM_CHECKTRANS_PM_RELEASE_CORES_TILER_END,
[KBASE_PM_FUNC_ID_RELEASE_CORES_END][KBASE_PM_CHANGE_STATE_SHADER |
KBASE_PM_CHANGE_STATE_TILER] =
SW_FLOW_PM_CHECKTRANS_PM_RELEASE_CORES_SHADER_TILER_END
};
static inline void kbase_timeline_pm_cores_func(struct kbase_device *kbdev,
enum kbase_pm_func_id func_id,
kbase_pm_change_state state)
{
int trace_code;
KBASE_DEBUG_ASSERT(func_id >= 0 && func_id < KBASE_PM_FUNC_ID_COUNT);
KBASE_DEBUG_ASSERT(state != 0 && (state & KBASE_PM_CHANGE_STATE_MASK) ==
state);
trace_code = kbase_pm_change_state_trace_code[func_id][state];
KBASE_TIMELINE_PM_CHECKTRANS(kbdev, trace_code);
}
#else /* CONFIG_MALI_TRACE_TIMELINE */
static inline void kbase_timeline_pm_cores_func(struct kbase_device *kbdev,
enum kbase_pm_func_id func_id, kbase_pm_change_state state)
{
}
#endif /* CONFIG_MALI_TRACE_TIMELINE */
/**
* kbasep_pm_do_poweroff_cores - Process a poweroff request and power down any
* requested shader cores
* @kbdev: Device pointer
*/
static void kbasep_pm_do_poweroff_cores(struct kbase_device *kbdev)
{
u64 prev_shader_state = kbdev->pm.backend.desired_shader_state;
lockdep_assert_held(&kbdev->pm.power_change_lock);
kbdev->pm.backend.desired_shader_state &=
~kbdev->pm.backend.shader_poweroff_pending;
kbdev->pm.backend.shader_poweroff_pending = 0;
if (prev_shader_state != kbdev->pm.backend.desired_shader_state
|| kbdev->pm.backend.ca_in_transition) {
bool cores_are_available;
KBASE_TIMELINE_PM_CHECKTRANS(kbdev,
SW_FLOW_PM_CHECKTRANS_PM_RELEASE_CORES_DEFERRED_START);
cores_are_available = kbase_pm_check_transitions_nolock(kbdev);
KBASE_TIMELINE_PM_CHECKTRANS(kbdev,
SW_FLOW_PM_CHECKTRANS_PM_RELEASE_CORES_DEFERRED_END);
/* Don't need 'cores_are_available',
* because we don't return anything */
CSTD_UNUSED(cores_are_available);
}
}
static enum hrtimer_restart
kbasep_pm_do_gpu_poweroff_callback(struct hrtimer *timer)
{
struct kbase_device *kbdev;
kbdev = container_of(timer, struct kbase_device,
pm.backend.gpu_poweroff_timer);
/* It is safe for this call to do nothing if the work item is already
* queued. The worker function will read the must up-to-date state of
* kbdev->pm.backend.gpu_poweroff_pending under lock.
*
* If a state change occurs while the worker function is processing,
* this call will succeed as a work item can be requeued once it has
* started processing.
*/
if (kbdev->pm.backend.gpu_poweroff_pending)
queue_work(kbdev->pm.backend.gpu_poweroff_wq,
&kbdev->pm.backend.gpu_poweroff_work);
if (kbdev->pm.backend.shader_poweroff_pending) {
unsigned long flags;
spin_lock_irqsave(&kbdev->pm.power_change_lock, flags);
if (kbdev->pm.backend.shader_poweroff_pending) {
kbdev->pm.backend.shader_poweroff_pending_time--;
KBASE_DEBUG_ASSERT(
kbdev->pm.backend.shader_poweroff_pending_time
>= 0);
if (!kbdev->pm.backend.shader_poweroff_pending_time)
kbasep_pm_do_poweroff_cores(kbdev);
}
spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags);
}
if (kbdev->pm.backend.poweroff_timer_needed) {
hrtimer_add_expires(timer, kbdev->pm.gpu_poweroff_time);
return HRTIMER_RESTART;
}
return HRTIMER_NORESTART;
}
static void kbasep_pm_do_gpu_poweroff_wq(struct work_struct *data)
{
unsigned long flags;
struct kbase_device *kbdev;
bool do_poweroff = false;
kbdev = container_of(data, struct kbase_device,
pm.backend.gpu_poweroff_work);
mutex_lock(&kbdev->pm.lock);
if (kbdev->pm.backend.gpu_poweroff_pending == 0) {
mutex_unlock(&kbdev->pm.lock);
return;
}
kbdev->pm.backend.gpu_poweroff_pending--;
if (kbdev->pm.backend.gpu_poweroff_pending > 0) {
mutex_unlock(&kbdev->pm.lock);
return;
}
KBASE_DEBUG_ASSERT(kbdev->pm.backend.gpu_poweroff_pending == 0);
spin_lock_irqsave(&kbdev->pm.power_change_lock, flags);
/* Only power off the GPU if a request is still pending */
if (!kbdev->pm.backend.pm_current_policy->get_core_active(kbdev))
do_poweroff = true;
spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags);
if (do_poweroff) {
kbdev->pm.backend.poweroff_timer_needed = false;
hrtimer_cancel(&kbdev->pm.backend.gpu_poweroff_timer);
/* Power off the GPU */
if (!kbase_pm_do_poweroff(kbdev, false)) {
/* GPU can not be powered off at present */
kbdev->pm.backend.poweroff_timer_needed = true;
hrtimer_start(&kbdev->pm.backend.gpu_poweroff_timer,
kbdev->pm.gpu_poweroff_time,
HRTIMER_MODE_REL);
}
}
mutex_unlock(&kbdev->pm.lock);
}
int kbase_pm_policy_init(struct kbase_device *kbdev)
{
struct workqueue_struct *wq;
wq = alloc_workqueue("kbase_pm_do_poweroff",
/* MALI_SEC_INTEGRATION */
// WQ_HIGHPRI | WQ_UNBOUND, 1);
WQ_HIGHPRI , 1);
if (!wq)
return -ENOMEM;
kbdev->pm.backend.gpu_poweroff_wq = wq;
INIT_WORK(&kbdev->pm.backend.gpu_poweroff_work,
kbasep_pm_do_gpu_poweroff_wq);
hrtimer_init(&kbdev->pm.backend.gpu_poweroff_timer,
CLOCK_MONOTONIC, HRTIMER_MODE_REL);
kbdev->pm.backend.gpu_poweroff_timer.function =
kbasep_pm_do_gpu_poweroff_callback;
kbdev->pm.backend.pm_current_policy = policy_list[2];
kbdev->pm.backend.pm_current_policy->init(kbdev);
kbdev->pm.gpu_poweroff_time =
HR_TIMER_DELAY_NSEC(DEFAULT_PM_GPU_POWEROFF_TICK_NS);
kbdev->pm.poweroff_shader_ticks = DEFAULT_PM_POWEROFF_TICK_SHADER;
kbdev->pm.poweroff_gpu_ticks = DEFAULT_PM_POWEROFF_TICK_GPU;
return 0;
}
void kbase_pm_policy_term(struct kbase_device *kbdev)
{
kbdev->pm.backend.pm_current_policy->term(kbdev);
destroy_workqueue(kbdev->pm.backend.gpu_poweroff_wq);
}
void kbase_pm_cancel_deferred_poweroff(struct kbase_device *kbdev)
{
unsigned long flags;
lockdep_assert_held(&kbdev->pm.lock);
kbdev->pm.backend.poweroff_timer_needed = false;
hrtimer_cancel(&kbdev->pm.backend.gpu_poweroff_timer);
/* If wq is already running but is held off by pm.lock, make sure it has
* no effect */
kbdev->pm.backend.gpu_poweroff_pending = 0;
spin_lock_irqsave(&kbdev->pm.power_change_lock, flags);
kbdev->pm.backend.shader_poweroff_pending = 0;
kbdev->pm.backend.shader_poweroff_pending_time = 0;
spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags);
}
void kbase_pm_update_active(struct kbase_device *kbdev)
{
unsigned long flags;
bool active;
lockdep_assert_held(&kbdev->pm.lock);
/* pm_current_policy will never be NULL while pm.lock is held */
KBASE_DEBUG_ASSERT(kbdev->pm.backend.pm_current_policy);
spin_lock_irqsave(&kbdev->pm.power_change_lock, flags);
active = kbdev->pm.backend.pm_current_policy->get_core_active(kbdev);
if (active) {
spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags);
if (kbdev->pm.backend.gpu_poweroff_pending) {
/* Cancel any pending power off request */
kbdev->pm.backend.gpu_poweroff_pending = 0;
/* If a request was pending then the GPU was still
* powered, so no need to continue */
if (!kbdev->poweroff_pending)
return;
}
if (!kbdev->pm.backend.poweroff_timer_needed &&
!kbdev->pm.backend.gpu_powered &&
(kbdev->pm.poweroff_gpu_ticks ||
kbdev->pm.poweroff_shader_ticks)) {
kbdev->pm.backend.poweroff_timer_needed = true;
hrtimer_start(&kbdev->pm.backend.gpu_poweroff_timer,
kbdev->pm.gpu_poweroff_time,
HRTIMER_MODE_REL);
}
/* Power on the GPU and any cores requested by the policy */
kbase_pm_do_poweron(kbdev, false);
} else {
/* It is an error for the power policy to power off the GPU
* when there are contexts active */
KBASE_DEBUG_ASSERT(kbdev->pm.active_count == 0);
if (kbdev->pm.backend.shader_poweroff_pending) {
kbdev->pm.backend.shader_poweroff_pending = 0;
kbdev->pm.backend.shader_poweroff_pending_time = 0;
}
spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags);
/* Request power off */
if (kbdev->pm.backend.gpu_powered) {
if (kbdev->pm.poweroff_gpu_ticks) {
kbdev->pm.backend.gpu_poweroff_pending =
kbdev->pm.poweroff_gpu_ticks;
if (!kbdev->pm.backend.poweroff_timer_needed) {
/* Start timer if not running (eg if
* power policy has been changed from
* always_on to something else). This
* will ensure the GPU is actually
* powered off */
kbdev->pm.backend.poweroff_timer_needed
= true;
hrtimer_start(
&kbdev->pm.backend.gpu_poweroff_timer,
kbdev->pm.gpu_poweroff_time,
HRTIMER_MODE_REL);
}
} else {
/* Power off the GPU immediately */
if (!kbase_pm_do_poweroff(kbdev, false)) {
/* GPU can not be powered off at present
*/
kbdev->pm.backend.poweroff_timer_needed
= true;
hrtimer_start(
&kbdev->pm.backend.gpu_poweroff_timer,
kbdev->pm.gpu_poweroff_time,
HRTIMER_MODE_REL);
}
}
}
}
}
void kbase_pm_update_cores_state_nolock(struct kbase_device *kbdev)
{
u64 desired_bitmap;
bool cores_are_available;
bool do_poweroff = false;
lockdep_assert_held(&kbdev->pm.power_change_lock);
if (kbdev->pm.backend.pm_current_policy == NULL)
return;
desired_bitmap =
kbdev->pm.backend.pm_current_policy->get_core_mask(kbdev);
desired_bitmap &= kbase_pm_ca_get_core_mask(kbdev);
/* Enable core 0 if tiler required, regardless of core availability */
if (kbdev->tiler_needed_cnt > 0 || kbdev->tiler_inuse_cnt > 0)
desired_bitmap |= 1;
if (kbdev->pm.backend.desired_shader_state != desired_bitmap)
KBASE_TRACE_ADD(kbdev, PM_CORES_CHANGE_DESIRED, NULL, NULL, 0u,
(u32)desired_bitmap);
/* Are any cores being powered on? */
if (~kbdev->pm.backend.desired_shader_state & desired_bitmap ||
kbdev->pm.backend.ca_in_transition) {
/* Check if we are powering off any cores before updating shader
* state */
if (kbdev->pm.backend.desired_shader_state & ~desired_bitmap) {
/* Start timer to power off cores */
kbdev->pm.backend.shader_poweroff_pending |=
(kbdev->pm.backend.desired_shader_state &
~desired_bitmap);
if (kbdev->pm.poweroff_shader_ticks)
kbdev->pm.backend.shader_poweroff_pending_time =
kbdev->pm.poweroff_shader_ticks;
else
do_poweroff = true;
}
kbdev->pm.backend.desired_shader_state = desired_bitmap;
/* If any cores are being powered on, transition immediately */
cores_are_available = kbase_pm_check_transitions_nolock(kbdev);
} else if (kbdev->pm.backend.desired_shader_state & ~desired_bitmap) {
/* Start timer to power off cores */
kbdev->pm.backend.shader_poweroff_pending |=
(kbdev->pm.backend.desired_shader_state &
~desired_bitmap);
if (kbdev->pm.poweroff_shader_ticks)
kbdev->pm.backend.shader_poweroff_pending_time =
kbdev->pm.poweroff_shader_ticks;
else
kbasep_pm_do_poweroff_cores(kbdev);
} else if (kbdev->pm.active_count == 0 && desired_bitmap != 0 &&
kbdev->pm.backend.poweroff_timer_needed) {
/* If power policy is keeping cores on despite there being no
* active contexts then disable poweroff timer as it isn't
* required.
* Only reset poweroff_timer_needed if we're not in the middle
* of the power off callback */
kbdev->pm.backend.poweroff_timer_needed = false;
hrtimer_try_to_cancel(&kbdev->pm.backend.gpu_poweroff_timer);
}
/* Ensure timer does not power off wanted cores and make sure to power
* off unwanted cores */
if (kbdev->pm.backend.shader_poweroff_pending != 0) {
kbdev->pm.backend.shader_poweroff_pending &=
~(kbdev->pm.backend.desired_shader_state &
desired_bitmap);
if (kbdev->pm.backend.shader_poweroff_pending == 0)
kbdev->pm.backend.shader_poweroff_pending_time = 0;
}
/* Shader poweroff is deferred to the end of the function, to eliminate
* issues caused by the core availability policy recursing into this
* function */
if (do_poweroff)
kbasep_pm_do_poweroff_cores(kbdev);
/* Don't need 'cores_are_available', because we don't return anything */
CSTD_UNUSED(cores_are_available);
}
void kbase_pm_update_cores_state(struct kbase_device *kbdev)
{
unsigned long flags;
spin_lock_irqsave(&kbdev->pm.power_change_lock, flags);
kbase_pm_update_cores_state_nolock(kbdev);
spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags);
}
int kbase_pm_list_policies(const struct kbase_pm_policy * const **list)
{
if (!list)
return POLICY_COUNT;
*list = policy_list;
return POLICY_COUNT;
}
KBASE_EXPORT_TEST_API(kbase_pm_list_policies);
const struct kbase_pm_policy *kbase_pm_get_policy(struct kbase_device *kbdev)
{
KBASE_DEBUG_ASSERT(kbdev != NULL);
return kbdev->pm.backend.pm_current_policy;
}
KBASE_EXPORT_TEST_API(kbase_pm_get_policy);
void kbase_pm_set_policy(struct kbase_device *kbdev,
const struct kbase_pm_policy *new_policy)
{
struct kbasep_js_device_data *js_devdata = &kbdev->js_data;
const struct kbase_pm_policy *old_policy;
unsigned long flags;
KBASE_DEBUG_ASSERT(kbdev != NULL);
KBASE_DEBUG_ASSERT(new_policy != NULL);
KBASE_TRACE_ADD(kbdev, PM_SET_POLICY, NULL, NULL, 0u, new_policy->id);
/* During a policy change we pretend the GPU is active */
/* A suspend won't happen here, because we're in a syscall from a
* userspace thread */
kbase_pm_context_active(kbdev);
mutex_lock(&js_devdata->runpool_mutex);
mutex_lock(&kbdev->pm.lock);
/* Remove the policy to prevent IRQ handlers from working on it */
spin_lock_irqsave(&kbdev->pm.power_change_lock, flags);
old_policy = kbdev->pm.backend.pm_current_policy;
kbdev->pm.backend.pm_current_policy = NULL;
spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags);
KBASE_TRACE_ADD(kbdev, PM_CURRENT_POLICY_TERM, NULL, NULL, 0u,
old_policy->id);
if (old_policy->term)
old_policy->term(kbdev);
KBASE_TRACE_ADD(kbdev, PM_CURRENT_POLICY_INIT, NULL, NULL, 0u,
new_policy->id);
if (new_policy->init)
new_policy->init(kbdev);
spin_lock_irqsave(&kbdev->pm.power_change_lock, flags);
kbdev->pm.backend.pm_current_policy = new_policy;
spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags);
/* If any core power state changes were previously attempted, but
* couldn't be made because the policy was changing (current_policy was
* NULL), then re-try them here. */
kbase_pm_update_active(kbdev);
kbase_pm_update_cores_state(kbdev);
mutex_unlock(&kbdev->pm.lock);
mutex_unlock(&js_devdata->runpool_mutex);
/* Now the policy change is finished, we release our fake context active
* reference */
kbase_pm_context_idle(kbdev);
}
KBASE_EXPORT_TEST_API(kbase_pm_set_policy);
/* Check whether a state change has finished, and trace it as completed */
static void
kbase_pm_trace_check_and_finish_state_change(struct kbase_device *kbdev)
{
if ((kbdev->shader_available_bitmap &
kbdev->pm.backend.desired_shader_state)
== kbdev->pm.backend.desired_shader_state &&
(kbdev->tiler_available_bitmap &
kbdev->pm.backend.desired_tiler_state)
== kbdev->pm.backend.desired_tiler_state)
kbase_timeline_pm_check_handle_event(kbdev,
KBASE_TIMELINE_PM_EVENT_GPU_STATE_CHANGED);
}
void kbase_pm_request_cores(struct kbase_device *kbdev,
bool tiler_required, u64 shader_cores)
{
unsigned long flags;
u64 cores;
kbase_pm_change_state change_gpu_state = 0u;
KBASE_DEBUG_ASSERT(kbdev != NULL);
spin_lock_irqsave(&kbdev->pm.power_change_lock, flags);
cores = shader_cores;
while (cores) {
int bitnum = fls64(cores) - 1;
u64 bit = 1ULL << bitnum;
/* It should be almost impossible for this to overflow. It would
* require 2^32 atoms to request a particular core, which would
* require 2^24 contexts to submit. This would require an amount
* of memory that is impossible on a 32-bit system and extremely
* unlikely on a 64-bit system. */
int cnt = ++kbdev->shader_needed_cnt[bitnum];
if (1 == cnt) {
kbdev->shader_needed_bitmap |= bit;
change_gpu_state |= KBASE_PM_CHANGE_STATE_SHADER;
}
cores &= ~bit;
}
if (tiler_required) {
int cnt = ++kbdev->tiler_needed_cnt;
if (1 == cnt)
change_gpu_state |= KBASE_PM_CHANGE_STATE_TILER;
KBASE_DEBUG_ASSERT(kbdev->tiler_needed_cnt != 0);
}
if (change_gpu_state) {
KBASE_TRACE_ADD(kbdev, PM_REQUEST_CHANGE_SHADER_NEEDED, NULL,
NULL, 0u, (u32) kbdev->shader_needed_bitmap);
kbase_timeline_pm_cores_func(kbdev,
KBASE_PM_FUNC_ID_REQUEST_CORES_START,
change_gpu_state);
kbase_pm_update_cores_state_nolock(kbdev);
kbase_timeline_pm_cores_func(kbdev,
KBASE_PM_FUNC_ID_REQUEST_CORES_END,
change_gpu_state);
}
spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags);
}
KBASE_EXPORT_TEST_API(kbase_pm_request_cores);
void kbase_pm_unrequest_cores(struct kbase_device *kbdev,
bool tiler_required, u64 shader_cores)
{
unsigned long flags;
kbase_pm_change_state change_gpu_state = 0u;
KBASE_DEBUG_ASSERT(kbdev != NULL);
spin_lock_irqsave(&kbdev->pm.power_change_lock, flags);
while (shader_cores) {
int bitnum = fls64(shader_cores) - 1;
u64 bit = 1ULL << bitnum;
int cnt;
KBASE_DEBUG_ASSERT(kbdev->shader_needed_cnt[bitnum] > 0);
cnt = --kbdev->shader_needed_cnt[bitnum];
if (0 == cnt) {
kbdev->shader_needed_bitmap &= ~bit;
change_gpu_state |= KBASE_PM_CHANGE_STATE_SHADER;
}
shader_cores &= ~bit;
}
if (tiler_required) {
int cnt;
KBASE_DEBUG_ASSERT(kbdev->tiler_needed_cnt > 0);
cnt = --kbdev->tiler_needed_cnt;
if (0 == cnt)
change_gpu_state |= KBASE_PM_CHANGE_STATE_TILER;
}
if (change_gpu_state) {
KBASE_TRACE_ADD(kbdev, PM_UNREQUEST_CHANGE_SHADER_NEEDED, NULL,
NULL, 0u, (u32) kbdev->shader_needed_bitmap);
kbase_pm_update_cores_state_nolock(kbdev);
/* Trace that any state change effectively completes immediately
* - no-one will wait on the state change */
kbase_pm_trace_check_and_finish_state_change(kbdev);
}
spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags);
}
KBASE_EXPORT_TEST_API(kbase_pm_unrequest_cores);
enum kbase_pm_cores_ready
kbase_pm_register_inuse_cores(struct kbase_device *kbdev,
bool tiler_required, u64 shader_cores)
{
unsigned long flags;
u64 prev_shader_needed; /* Just for tracing */
u64 prev_shader_inuse; /* Just for tracing */
spin_lock_irqsave(&kbdev->pm.power_change_lock, flags);
prev_shader_needed = kbdev->shader_needed_bitmap;
prev_shader_inuse = kbdev->shader_inuse_bitmap;
/* If desired_shader_state does not contain the requested cores, then
* power management is not attempting to powering those cores (most
* likely due to core availability policy) and a new job affinity must
* be chosen */
if ((kbdev->pm.backend.desired_shader_state & shader_cores) !=
shader_cores) {
spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags);
return KBASE_NEW_AFFINITY;
}
if ((kbdev->shader_available_bitmap & shader_cores) != shader_cores ||
(tiler_required && !kbdev->tiler_available_bitmap)) {
/* Trace ongoing core transition */
kbase_timeline_pm_l2_transition_start(kbdev);
spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags);
return KBASE_CORES_NOT_READY;
}
/* If we started to trace a state change, then trace it has being
* finished by now, at the very latest */
kbase_pm_trace_check_and_finish_state_change(kbdev);
/* Trace core transition done */
kbase_timeline_pm_l2_transition_done(kbdev);
while (shader_cores) {
int bitnum = fls64(shader_cores) - 1;
u64 bit = 1ULL << bitnum;
int cnt;
KBASE_DEBUG_ASSERT(kbdev->shader_needed_cnt[bitnum] > 0);
cnt = --kbdev->shader_needed_cnt[bitnum];
if (0 == cnt)
kbdev->shader_needed_bitmap &= ~bit;
/* shader_inuse_cnt should not overflow because there can only
* be a very limited number of jobs on the h/w at one time */
kbdev->shader_inuse_cnt[bitnum]++;
kbdev->shader_inuse_bitmap |= bit;
shader_cores &= ~bit;
}
if (tiler_required) {
KBASE_DEBUG_ASSERT(kbdev->tiler_needed_cnt > 0);
--kbdev->tiler_needed_cnt;
kbdev->tiler_inuse_cnt++;
KBASE_DEBUG_ASSERT(kbdev->tiler_inuse_cnt != 0);
}
if (prev_shader_needed != kbdev->shader_needed_bitmap)
KBASE_TRACE_ADD(kbdev, PM_REGISTER_CHANGE_SHADER_NEEDED, NULL,
NULL, 0u, (u32) kbdev->shader_needed_bitmap);
if (prev_shader_inuse != kbdev->shader_inuse_bitmap)
KBASE_TRACE_ADD(kbdev, PM_REGISTER_CHANGE_SHADER_INUSE, NULL,
NULL, 0u, (u32) kbdev->shader_inuse_bitmap);
spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags);
return KBASE_CORES_READY;
}
KBASE_EXPORT_TEST_API(kbase_pm_register_inuse_cores);
void kbase_pm_release_cores(struct kbase_device *kbdev,
bool tiler_required, u64 shader_cores)
{
unsigned long flags;
kbase_pm_change_state change_gpu_state = 0u;
KBASE_DEBUG_ASSERT(kbdev != NULL);
spin_lock_irqsave(&kbdev->pm.power_change_lock, flags);
while (shader_cores) {
int bitnum = fls64(shader_cores) - 1;
u64 bit = 1ULL << bitnum;
int cnt;
KBASE_DEBUG_ASSERT(kbdev->shader_inuse_cnt[bitnum] > 0);
cnt = --kbdev->shader_inuse_cnt[bitnum];
if (0 == cnt) {
kbdev->shader_inuse_bitmap &= ~bit;
change_gpu_state |= KBASE_PM_CHANGE_STATE_SHADER;
}
shader_cores &= ~bit;
}
if (tiler_required) {
int cnt;
KBASE_DEBUG_ASSERT(kbdev->tiler_inuse_cnt > 0);
cnt = --kbdev->tiler_inuse_cnt;
if (0 == cnt)
change_gpu_state |= KBASE_PM_CHANGE_STATE_TILER;
}
if (change_gpu_state) {
KBASE_TRACE_ADD(kbdev, PM_RELEASE_CHANGE_SHADER_INUSE, NULL,
NULL, 0u, (u32) kbdev->shader_inuse_bitmap);
kbase_timeline_pm_cores_func(kbdev,
KBASE_PM_FUNC_ID_RELEASE_CORES_START,
change_gpu_state);
kbase_pm_update_cores_state_nolock(kbdev);
kbase_timeline_pm_cores_func(kbdev,
KBASE_PM_FUNC_ID_RELEASE_CORES_END,
change_gpu_state);
/* Trace that any state change completed immediately */
kbase_pm_trace_check_and_finish_state_change(kbdev);
}
spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags);
}
KBASE_EXPORT_TEST_API(kbase_pm_release_cores);
void kbase_pm_request_cores_sync(struct kbase_device *kbdev,
bool tiler_required,
u64 shader_cores)
{
kbase_pm_request_cores(kbdev, tiler_required, shader_cores);
kbase_pm_check_transitions_sync(kbdev);
}
KBASE_EXPORT_TEST_API(kbase_pm_request_cores_sync);
void kbase_pm_request_l2_caches(struct kbase_device *kbdev)
{
unsigned long flags;
u32 prior_l2_users_count;
spin_lock_irqsave(&kbdev->pm.power_change_lock, flags);
prior_l2_users_count = kbdev->l2_users_count++;
KBASE_DEBUG_ASSERT(kbdev->l2_users_count != 0);
/* if the GPU is reset while the l2 is on, l2 will be off but
* prior_l2_users_count will be > 0. l2_available_bitmap will have been
* set to 0 though by kbase_pm_init_hw */
if (!prior_l2_users_count || !kbdev->l2_available_bitmap)
kbase_pm_check_transitions_nolock(kbdev);
spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags);
wait_event(kbdev->pm.backend.l2_powered_wait,
kbdev->pm.backend.l2_powered == 1);
/* Trace that any state change completed immediately */
kbase_pm_trace_check_and_finish_state_change(kbdev);
}
KBASE_EXPORT_TEST_API(kbase_pm_request_l2_caches);
void kbase_pm_request_l2_caches_l2_is_on(struct kbase_device *kbdev)
{
unsigned long flags;
spin_lock_irqsave(&kbdev->pm.power_change_lock, flags);
kbdev->l2_users_count++;
spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags);
}
KBASE_EXPORT_TEST_API(kbase_pm_request_l2_caches_l2_is_on);
void kbase_pm_release_l2_caches(struct kbase_device *kbdev)
{
unsigned long flags;
spin_lock_irqsave(&kbdev->pm.power_change_lock, flags);
KBASE_DEBUG_ASSERT(kbdev->l2_users_count > 0);
--kbdev->l2_users_count;
if (!kbdev->l2_users_count) {
kbase_pm_check_transitions_nolock(kbdev);
/* Trace that any state change completed immediately */
kbase_pm_trace_check_and_finish_state_change(kbdev);
}
spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags);
}
KBASE_EXPORT_TEST_API(kbase_pm_release_l2_caches);

View file

@ -0,0 +1,227 @@
/*
*
* (C) COPYRIGHT 2010-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Power policy API definitions
*/
#ifndef _KBASE_PM_POLICY_H_
#define _KBASE_PM_POLICY_H_
/**
* kbase_pm_policy_init - Initialize power policy framework
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
*
* Must be called before calling any other policy function
*
* Return: 0 if the power policy framework was successfully
* initialized, -errno otherwise.
*/
int kbase_pm_policy_init(struct kbase_device *kbdev);
/**
* kbase_pm_policy_term - Terminate power policy framework
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
*/
void kbase_pm_policy_term(struct kbase_device *kbdev);
/**
* kbase_pm_update_active - Update the active power state of the GPU
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
*
* Calls into the current power policy
*/
void kbase_pm_update_active(struct kbase_device *kbdev);
/**
* kbase_pm_update_cores - Update the desired core state of the GPU
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
*
* Calls into the current power policy
*/
void kbase_pm_update_cores(struct kbase_device *kbdev);
enum kbase_pm_cores_ready {
KBASE_CORES_NOT_READY = 0,
KBASE_NEW_AFFINITY = 1,
KBASE_CORES_READY = 2
};
/**
* kbase_pm_request_cores_sync - Synchronous variant of kbase_pm_request_cores()
*
* @kbdev: The kbase device structure for the device
* @tiler_required: true if the tiler is required, false otherwise
* @shader_cores: A bitmask of shader cores which are necessary for the job
*
* When this function returns, the @shader_cores will be in the READY state.
*
* This is safe variant of kbase_pm_check_transitions_sync(): it handles the
* work of ensuring the requested cores will remain powered until a matching
* call to kbase_pm_unrequest_cores()/kbase_pm_release_cores() (as appropriate)
* is made.
*/
void kbase_pm_request_cores_sync(struct kbase_device *kbdev,
bool tiler_required, u64 shader_cores);
/**
* kbase_pm_request_cores - Mark one or more cores as being required
* for jobs to be submitted
*
* @kbdev: The kbase device structure for the device
* @tiler_required: true if the tiler is required, false otherwise
* @shader_cores: A bitmask of shader cores which are necessary for the job
*
* This function is called by the job scheduler to mark one or more cores as
* being required to submit jobs that are ready to run.
*
* The cores requested are reference counted and a subsequent call to
* kbase_pm_register_inuse_cores() or kbase_pm_unrequest_cores() should be
* made to dereference the cores as being 'needed'.
*
* The active power policy will meet or exceed the requirements of the
* requested cores in the system. Any core transitions needed will be begun
* immediately, but they might not complete/the cores might not be available
* until a Power Management IRQ.
*
* Return: 0 if the cores were successfully requested, or -errno otherwise.
*/
void kbase_pm_request_cores(struct kbase_device *kbdev,
bool tiler_required, u64 shader_cores);
/**
* kbase_pm_unrequest_cores - Unmark one or more cores as being required for
* jobs to be submitted.
*
* @kbdev: The kbase device structure for the device
* @tiler_required: true if the tiler is required, false otherwise
* @shader_cores: A bitmask of shader cores (as given to
* kbase_pm_request_cores() )
*
* This function undoes the effect of kbase_pm_request_cores(). It should be
* used when a job is not going to be submitted to the hardware (e.g. the job is
* cancelled before it is enqueued).
*
* The active power policy will meet or exceed the requirements of the
* requested cores in the system. Any core transitions needed will be begun
* immediately, but they might not complete until a Power Management IRQ.
*
* The policy may use this as an indication that it can power down cores.
*/
void kbase_pm_unrequest_cores(struct kbase_device *kbdev,
bool tiler_required, u64 shader_cores);
/**
* kbase_pm_register_inuse_cores - Register a set of cores as in use by a job
*
* @kbdev: The kbase device structure for the device
* @tiler_required: true if the tiler is required, false otherwise
* @shader_cores: A bitmask of shader cores (as given to
* kbase_pm_request_cores() )
*
* This function should be called after kbase_pm_request_cores() when the job
* is about to be submitted to the hardware. It will check that the necessary
* cores are available and if so update the 'needed' and 'inuse' bitmasks to
* reflect that the job is now committed to being run.
*
* If the necessary cores are not currently available then the function will
* return %KBASE_CORES_NOT_READY and have no effect.
*
* Return: %KBASE_CORES_NOT_READY if the cores are not immediately ready,
*
* %KBASE_NEW_AFFINITY if the affinity requested is not allowed,
*
* %KBASE_CORES_READY if the cores requested are already available
*/
enum kbase_pm_cores_ready kbase_pm_register_inuse_cores(
struct kbase_device *kbdev,
bool tiler_required,
u64 shader_cores);
/**
* kbase_pm_release_cores - Release cores after a job has run
*
* @kbdev: The kbase device structure for the device
* @tiler_required: true if the tiler is required, false otherwise
* @shader_cores: A bitmask of shader cores (as given to
* kbase_pm_register_inuse_cores() )
*
* This function should be called when a job has finished running on the
* hardware. A call to kbase_pm_register_inuse_cores() must have previously
* occurred. The reference counts of the specified cores will be decremented
* which may cause the bitmask of 'inuse' cores to be reduced. The power policy
* may then turn off any cores which are no longer 'inuse'.
*/
void kbase_pm_release_cores(struct kbase_device *kbdev,
bool tiler_required, u64 shader_cores);
/**
* kbase_pm_request_l2_caches - Request l2 caches
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
*
* Request the use of l2 caches for all core groups, power up, wait and prevent
* the power manager from powering down the l2 caches.
*
* This tells the power management that the caches should be powered up, and
* they should remain powered, irrespective of the usage of shader cores. This
* does not return until the l2 caches are powered up.
*
* The caller must call kbase_pm_release_l2_caches() when they are finished
* to allow normal power management of the l2 caches to resume.
*
* This should only be used when power management is active.
*/
void kbase_pm_request_l2_caches(struct kbase_device *kbdev);
/**
* kbase_pm_request_l2_caches_l2_is_on - Request l2 caches but don't power on
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
*
* Increment the count of l2 users but do not attempt to power on the l2
*
* It is the callers responsibility to ensure that the l2 is already powered up
* and to eventually call kbase_pm_release_l2_caches()
*/
void kbase_pm_request_l2_caches_l2_is_on(struct kbase_device *kbdev);
/**
* kbase_pm_request_l2_caches - Release l2 caches
*
* @kbdev: The kbase device structure for the device (must be a valid pointer)
*
* Release the use of l2 caches for all core groups and allow the power manager
* to power them down when necessary.
*
* This tells the power management that the caches can be powered down if
* necessary, with respect to the usage of shader cores.
*
* The caller must have called kbase_pm_request_l2_caches() prior to a call
* to this.
*
* This should only be used when power management is active.
*/
void kbase_pm_release_l2_caches(struct kbase_device *kbdev);
#endif /* _KBASE_PM_POLICY_H_ */

View file

@ -0,0 +1,102 @@
/*
*
* (C) COPYRIGHT 2014-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include <mali_kbase.h>
#include <mali_kbase_hwaccess_time.h>
#include <backend/gpu/mali_kbase_device_internal.h>
#include <backend/gpu/mali_kbase_pm_internal.h>
void kbase_backend_get_gpu_time(struct kbase_device *kbdev, u64 *cycle_counter,
u64 *system_time, struct timespec *ts)
{
u32 hi1, hi2;
kbase_pm_request_gpu_cycle_counter(kbdev);
/* Read hi, lo, hi to ensure that overflow from lo to hi is handled
* correctly */
do {
hi1 = kbase_reg_read(kbdev, GPU_CONTROL_REG(CYCLE_COUNT_HI),
NULL);
*cycle_counter = kbase_reg_read(kbdev,
GPU_CONTROL_REG(CYCLE_COUNT_LO), NULL);
hi2 = kbase_reg_read(kbdev, GPU_CONTROL_REG(CYCLE_COUNT_HI),
NULL);
*cycle_counter |= (((u64) hi1) << 32);
} while (hi1 != hi2);
/* Read hi, lo, hi to ensure that overflow from lo to hi is handled
* correctly */
do {
hi1 = kbase_reg_read(kbdev, GPU_CONTROL_REG(TIMESTAMP_HI),
NULL);
*system_time = kbase_reg_read(kbdev,
GPU_CONTROL_REG(TIMESTAMP_LO), NULL);
hi2 = kbase_reg_read(kbdev, GPU_CONTROL_REG(TIMESTAMP_HI),
NULL);
*system_time |= (((u64) hi1) << 32);
} while (hi1 != hi2);
/* Record the CPU's idea of current time */
getrawmonotonic(ts);
kbase_pm_release_gpu_cycle_counter(kbdev);
}
/**
* kbase_wait_write_flush - Wait for GPU write flush
* @kctx: Context pointer
*
* Wait 1000 GPU clock cycles. This delay is known to give the GPU time to flush
* its write buffer.
*
* Only in use for BASE_HW_ISSUE_6367
*
* Note : If GPU resets occur then the counters are reset to zero, the delay may
* not be as expected.
*/
#ifndef CONFIG_MALI_NO_MALI
void kbase_wait_write_flush(struct kbase_context *kctx)
{
u32 base_count = 0;
/* A suspend won't happen here, because we're in a syscall from a
* userspace thread */
kbase_pm_context_active(kctx->kbdev);
kbase_pm_request_gpu_cycle_counter(kctx->kbdev);
while (true) {
u32 new_count;
new_count = kbase_reg_read(kctx->kbdev,
GPU_CONTROL_REG(CYCLE_COUNT_LO), NULL);
/* First time around, just store the count. */
if (base_count == 0) {
base_count = new_count;
continue;
}
/* No need to handle wrapping, unsigned maths works for this. */
if ((new_count - base_count) > 1000)
break;
}
kbase_pm_release_gpu_cycle_counter(kctx->kbdev);
kbase_pm_context_idle(kctx->kbdev);
}
#endif /* CONFIG_MALI_NO_MALI */

View file

@ -0,0 +1,52 @@
/*
*
* (C) COPYRIGHT 2014-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#ifndef _KBASE_BACKEND_TIME_H_
#define _KBASE_BACKEND_TIME_H_
/**
* kbase_backend_get_gpu_time() - Get current GPU time
* @kbdev: Device pointer
* @cycle_counter: Pointer to u64 to store cycle counter in
* @system_time: Pointer to u64 to store system time in
* @ts: Pointer to struct timespec to store current monotonic
* time in
*/
void kbase_backend_get_gpu_time(struct kbase_device *kbdev, u64 *cycle_counter,
u64 *system_time, struct timespec *ts);
/**
* kbase_wait_write_flush() - Wait for GPU write flush
* @kctx: Context pointer
*
* Wait 1000 GPU clock cycles. This delay is known to give the GPU time to flush
* its write buffer.
*
* If GPU resets occur then the counters are reset to zero, the delay may not be
* as expected.
*
* This function is only in use for BASE_HW_ISSUE_6367
*/
#ifdef CONFIG_MALI_NO_MALI
static inline void kbase_wait_write_flush(struct kbase_context *kctx)
{
}
#else
void kbase_wait_write_flush(struct kbase_context *kctx);
#endif
#endif /* _KBASE_BACKEND_TIME_H_ */

View file

@ -0,0 +1,63 @@
/*
*
* (C) COPYRIGHT 2010 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
digraph policy_objects_diagram {
rankdir=LR
size="6,6"
compound=true;
node [ shape = box ];
call_enqueue [ shape=plaintext label="enqueue ctx" ];
policy_queue [ label="Policy's Queue" ];
{
rank=same;
runpool [ label="Policy's Run Pool" ];
ctx_finish [ label="ctx finished" ];
}
{
rank=same;
jobslots [ shape=record label="Jobslots: | <0>js[0] | <1>js[1] | <2>js[2]" ];
job_finish [ label="Job finished" ];
}
/*
* Edges
*/
call_enqueue -> policy_queue;
policy_queue->runpool [label="dequeue ctx" weight=0.1];
runpool->policy_queue [label="requeue ctx" weight=0.1];
runpool->ctx_finish [ style=dotted ];
runpool->jobslots [label="dequeue job" weight=0.1];
jobslots->runpool [label="requeue job" weight=0.1];
jobslots->job_finish [ style=dotted ];
}

View file

@ -0,0 +1,192 @@
/*
* (C) COPYRIGHT 2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/* AUTOMATICALLY GENERATED FILE. If you want to amend the issues/features,
* please update base/tools/hwconfig_generator/hwc_{issues,features}.py
* For more information see base/tools/hwconfig_generator/README
*/
#ifndef _BASE_HWCONFIG_FEATURES_H_
#define _BASE_HWCONFIG_FEATURES_H_
enum base_hw_feature {
BASE_HW_FEATURE_JOBCHAIN_DISAMBIGUATION,
BASE_HW_FEATURE_PWRON_DURING_PWROFF_TRANS,
BASE_HW_FEATURE_33BIT_VA,
BASE_HW_FEATURE_OUT_OF_ORDER_EXEC,
BASE_HW_FEATURE_MRT,
BASE_HW_FEATURE_BRNDOUT_CC,
BASE_HW_FEATURE_INTERPIPE_REG_ALIASING,
BASE_HW_FEATURE_LD_ST_TILEBUFFER,
BASE_HW_FEATURE_MSAA_16X,
BASE_HW_FEATURE_32_BIT_UNIFORM_ADDRESS,
BASE_HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL,
BASE_HW_FEATURE_OPTIMIZED_COVERAGE_MASK,
BASE_HW_FEATURE_T7XX_PAIRING_RULES,
BASE_HW_FEATURE_LD_ST_LEA_TEX,
BASE_HW_FEATURE_LINEAR_FILTER_FLOAT,
BASE_HW_FEATURE_WORKGROUP_ROUND_MULTIPLE_OF_4,
BASE_HW_FEATURE_IMAGES_IN_FRAGMENT_SHADERS,
BASE_HW_FEATURE_TEST4_DATUM_MODE,
BASE_HW_FEATURE_NEXT_INSTRUCTION_TYPE,
BASE_HW_FEATURE_BRNDOUT_KILL,
BASE_HW_FEATURE_WARPING,
BASE_HW_FEATURE_FLUSH_REDUCTION,
BASE_HW_FEATURE_V4,
BASE_HW_FEATURE_PROTECTED_MODE,
#ifdef MALI_INCLUDE_TMIX
BASE_HW_FEATURE_COHERENCY_REG,
#endif /* MALI_INCLUDE_TMIX */
BASE_HW_FEATURE_END
};
static const enum base_hw_feature base_hw_features_generic[] = {
BASE_HW_FEATURE_END
};
static const enum base_hw_feature base_hw_features_t60x[] = {
BASE_HW_FEATURE_LD_ST_LEA_TEX,
BASE_HW_FEATURE_LINEAR_FILTER_FLOAT,
BASE_HW_FEATURE_V4,
BASE_HW_FEATURE_END
};
static const enum base_hw_feature base_hw_features_t62x[] = {
BASE_HW_FEATURE_LD_ST_LEA_TEX,
BASE_HW_FEATURE_LINEAR_FILTER_FLOAT,
BASE_HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL,
BASE_HW_FEATURE_V4,
BASE_HW_FEATURE_END
};
static const enum base_hw_feature base_hw_features_t72x[] = {
BASE_HW_FEATURE_33BIT_VA,
BASE_HW_FEATURE_32_BIT_UNIFORM_ADDRESS,
BASE_HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL,
BASE_HW_FEATURE_INTERPIPE_REG_ALIASING,
BASE_HW_FEATURE_OPTIMIZED_COVERAGE_MASK,
BASE_HW_FEATURE_T7XX_PAIRING_RULES,
BASE_HW_FEATURE_WORKGROUP_ROUND_MULTIPLE_OF_4,
BASE_HW_FEATURE_WARPING,
BASE_HW_FEATURE_V4,
BASE_HW_FEATURE_END
};
static const enum base_hw_feature base_hw_features_t76x[] = {
BASE_HW_FEATURE_JOBCHAIN_DISAMBIGUATION,
BASE_HW_FEATURE_PWRON_DURING_PWROFF_TRANS,
BASE_HW_FEATURE_32_BIT_UNIFORM_ADDRESS,
BASE_HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL,
BASE_HW_FEATURE_BRNDOUT_CC,
BASE_HW_FEATURE_LD_ST_LEA_TEX,
BASE_HW_FEATURE_LD_ST_TILEBUFFER,
BASE_HW_FEATURE_LINEAR_FILTER_FLOAT,
BASE_HW_FEATURE_MRT,
BASE_HW_FEATURE_MSAA_16X,
BASE_HW_FEATURE_OUT_OF_ORDER_EXEC,
BASE_HW_FEATURE_T7XX_PAIRING_RULES,
BASE_HW_FEATURE_TEST4_DATUM_MODE,
BASE_HW_FEATURE_END
};
static const enum base_hw_feature base_hw_features_tFxx[] = {
BASE_HW_FEATURE_JOBCHAIN_DISAMBIGUATION,
BASE_HW_FEATURE_PWRON_DURING_PWROFF_TRANS,
BASE_HW_FEATURE_32_BIT_UNIFORM_ADDRESS,
BASE_HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL,
BASE_HW_FEATURE_BRNDOUT_CC,
BASE_HW_FEATURE_BRNDOUT_KILL,
BASE_HW_FEATURE_LD_ST_LEA_TEX,
BASE_HW_FEATURE_LD_ST_TILEBUFFER,
BASE_HW_FEATURE_LINEAR_FILTER_FLOAT,
BASE_HW_FEATURE_MRT,
BASE_HW_FEATURE_MSAA_16X,
BASE_HW_FEATURE_NEXT_INSTRUCTION_TYPE,
BASE_HW_FEATURE_OUT_OF_ORDER_EXEC,
BASE_HW_FEATURE_T7XX_PAIRING_RULES,
BASE_HW_FEATURE_TEST4_DATUM_MODE,
BASE_HW_FEATURE_END
};
static const enum base_hw_feature base_hw_features_t83x[] = {
BASE_HW_FEATURE_33BIT_VA,
BASE_HW_FEATURE_JOBCHAIN_DISAMBIGUATION,
BASE_HW_FEATURE_PWRON_DURING_PWROFF_TRANS,
BASE_HW_FEATURE_WARPING,
BASE_HW_FEATURE_INTERPIPE_REG_ALIASING,
BASE_HW_FEATURE_32_BIT_UNIFORM_ADDRESS,
BASE_HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL,
BASE_HW_FEATURE_BRNDOUT_CC,
BASE_HW_FEATURE_BRNDOUT_KILL,
BASE_HW_FEATURE_LD_ST_LEA_TEX,
BASE_HW_FEATURE_LD_ST_TILEBUFFER,
BASE_HW_FEATURE_LINEAR_FILTER_FLOAT,
BASE_HW_FEATURE_MRT,
BASE_HW_FEATURE_NEXT_INSTRUCTION_TYPE,
BASE_HW_FEATURE_OUT_OF_ORDER_EXEC,
BASE_HW_FEATURE_T7XX_PAIRING_RULES,
BASE_HW_FEATURE_TEST4_DATUM_MODE,
BASE_HW_FEATURE_END
};
static const enum base_hw_feature base_hw_features_t82x[] = {
BASE_HW_FEATURE_33BIT_VA,
BASE_HW_FEATURE_JOBCHAIN_DISAMBIGUATION,
BASE_HW_FEATURE_PWRON_DURING_PWROFF_TRANS,
BASE_HW_FEATURE_WARPING,
BASE_HW_FEATURE_INTERPIPE_REG_ALIASING,
BASE_HW_FEATURE_32_BIT_UNIFORM_ADDRESS,
BASE_HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL,
BASE_HW_FEATURE_BRNDOUT_CC,
BASE_HW_FEATURE_BRNDOUT_KILL,
BASE_HW_FEATURE_LD_ST_LEA_TEX,
BASE_HW_FEATURE_LD_ST_TILEBUFFER,
BASE_HW_FEATURE_LINEAR_FILTER_FLOAT,
BASE_HW_FEATURE_MRT,
BASE_HW_FEATURE_NEXT_INSTRUCTION_TYPE,
BASE_HW_FEATURE_OUT_OF_ORDER_EXEC,
BASE_HW_FEATURE_T7XX_PAIRING_RULES,
BASE_HW_FEATURE_TEST4_DATUM_MODE,
BASE_HW_FEATURE_END
};
#if defined(MALI_INCLUDE_TMIX)
static const enum base_hw_feature base_hw_features_tMIx[] = {
BASE_HW_FEATURE_JOBCHAIN_DISAMBIGUATION,
BASE_HW_FEATURE_PWRON_DURING_PWROFF_TRANS,
BASE_HW_FEATURE_WARPING,
BASE_HW_FEATURE_INTERPIPE_REG_ALIASING,
BASE_HW_FEATURE_32_BIT_UNIFORM_ADDRESS,
BASE_HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL,
BASE_HW_FEATURE_BRNDOUT_CC,
BASE_HW_FEATURE_BRNDOUT_KILL,
BASE_HW_FEATURE_LD_ST_LEA_TEX,
BASE_HW_FEATURE_LD_ST_TILEBUFFER,
BASE_HW_FEATURE_LINEAR_FILTER_FLOAT,
BASE_HW_FEATURE_MRT,
BASE_HW_FEATURE_MSAA_16X,
BASE_HW_FEATURE_NEXT_INSTRUCTION_TYPE,
BASE_HW_FEATURE_OUT_OF_ORDER_EXEC,
BASE_HW_FEATURE_T7XX_PAIRING_RULES,
BASE_HW_FEATURE_TEST4_DATUM_MODE,
BASE_HW_FEATURE_FLUSH_REDUCTION,
BASE_HW_FEATURE_PROTECTED_MODE,
#ifdef MALI_INCLUDE_TMIX
BASE_HW_FEATURE_COHERENCY_REG,
#endif /* MALI_INCLUDE_TMIX */
BASE_HW_FEATURE_END
};
#endif /* defined(MALI_INCLUDE_TMIX) */
#endif /* _BASE_HWCONFIG_FEATURES_H_ */

View file

@ -0,0 +1,775 @@
/*
* (C) COPYRIGHT 2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/* AUTOMATICALLY GENERATED FILE. If you want to amend the issues/features,
* please update base/tools/hwconfig_generator/hwc_{issues,features}.py
* For more information see base/tools/hwconfig_generator/README
*/
#ifndef _BASE_HWCONFIG_ISSUES_H_
#define _BASE_HWCONFIG_ISSUES_H_
enum base_hw_issue {
BASE_HW_ISSUE_5736,
BASE_HW_ISSUE_6367,
BASE_HW_ISSUE_6398,
BASE_HW_ISSUE_6402,
BASE_HW_ISSUE_6787,
BASE_HW_ISSUE_7027,
BASE_HW_ISSUE_7144,
BASE_HW_ISSUE_7304,
BASE_HW_ISSUE_8073,
BASE_HW_ISSUE_8186,
BASE_HW_ISSUE_8215,
BASE_HW_ISSUE_8245,
BASE_HW_ISSUE_8250,
BASE_HW_ISSUE_8260,
BASE_HW_ISSUE_8280,
BASE_HW_ISSUE_8316,
BASE_HW_ISSUE_8381,
BASE_HW_ISSUE_8394,
BASE_HW_ISSUE_8401,
BASE_HW_ISSUE_8408,
BASE_HW_ISSUE_8443,
BASE_HW_ISSUE_8456,
BASE_HW_ISSUE_8564,
BASE_HW_ISSUE_8634,
BASE_HW_ISSUE_8778,
BASE_HW_ISSUE_8791,
BASE_HW_ISSUE_8833,
BASE_HW_ISSUE_8879,
BASE_HW_ISSUE_8896,
BASE_HW_ISSUE_8975,
BASE_HW_ISSUE_8986,
BASE_HW_ISSUE_8987,
BASE_HW_ISSUE_9010,
BASE_HW_ISSUE_9275,
BASE_HW_ISSUE_9418,
BASE_HW_ISSUE_9423,
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_9510,
BASE_HW_ISSUE_9566,
BASE_HW_ISSUE_9630,
BASE_HW_ISSUE_10127,
BASE_HW_ISSUE_10327,
BASE_HW_ISSUE_10410,
BASE_HW_ISSUE_10471,
BASE_HW_ISSUE_10472,
BASE_HW_ISSUE_10487,
BASE_HW_ISSUE_10607,
BASE_HW_ISSUE_10632,
BASE_HW_ISSUE_10676,
BASE_HW_ISSUE_10682,
BASE_HW_ISSUE_10684,
BASE_HW_ISSUE_10797,
BASE_HW_ISSUE_10817,
BASE_HW_ISSUE_10821,
BASE_HW_ISSUE_10883,
BASE_HW_ISSUE_10931,
BASE_HW_ISSUE_10946,
BASE_HW_ISSUE_10959,
BASE_HW_ISSUE_10969,
BASE_HW_ISSUE_10984,
BASE_HW_ISSUE_10995,
BASE_HW_ISSUE_11012,
BASE_HW_ISSUE_11020,
BASE_HW_ISSUE_11024,
BASE_HW_ISSUE_11035,
BASE_HW_ISSUE_11042,
BASE_HW_ISSUE_T76X_26,
BASE_HW_ISSUE_T76X_1909,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_T76X_3086,
BASE_HW_ISSUE_T76X_3542,
BASE_HW_ISSUE_T76X_3556,
BASE_HW_ISSUE_T76X_3700,
BASE_HW_ISSUE_T76X_3793,
BASE_HW_ISSUE_T76X_3953,
BASE_HW_ISSUE_T76X_3960,
BASE_HW_ISSUE_T76X_3966,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_generic[] = {
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_t60x_r0p0_15dev0[] = {
BASE_HW_ISSUE_6367,
BASE_HW_ISSUE_6398,
BASE_HW_ISSUE_6402,
BASE_HW_ISSUE_6787,
BASE_HW_ISSUE_7027,
BASE_HW_ISSUE_7144,
BASE_HW_ISSUE_7304,
BASE_HW_ISSUE_8073,
BASE_HW_ISSUE_8186,
BASE_HW_ISSUE_8215,
BASE_HW_ISSUE_8245,
BASE_HW_ISSUE_8250,
BASE_HW_ISSUE_8260,
BASE_HW_ISSUE_8280,
BASE_HW_ISSUE_8316,
BASE_HW_ISSUE_8381,
BASE_HW_ISSUE_8394,
BASE_HW_ISSUE_8401,
BASE_HW_ISSUE_8408,
BASE_HW_ISSUE_8443,
BASE_HW_ISSUE_8456,
BASE_HW_ISSUE_8564,
BASE_HW_ISSUE_8634,
BASE_HW_ISSUE_8778,
BASE_HW_ISSUE_8791,
BASE_HW_ISSUE_8833,
BASE_HW_ISSUE_8896,
BASE_HW_ISSUE_8975,
BASE_HW_ISSUE_8986,
BASE_HW_ISSUE_8987,
BASE_HW_ISSUE_9010,
BASE_HW_ISSUE_9275,
BASE_HW_ISSUE_9418,
BASE_HW_ISSUE_9423,
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_9510,
BASE_HW_ISSUE_9566,
BASE_HW_ISSUE_9630,
BASE_HW_ISSUE_10410,
BASE_HW_ISSUE_10471,
BASE_HW_ISSUE_10472,
BASE_HW_ISSUE_10487,
BASE_HW_ISSUE_10607,
BASE_HW_ISSUE_10632,
BASE_HW_ISSUE_10676,
BASE_HW_ISSUE_10682,
BASE_HW_ISSUE_10684,
BASE_HW_ISSUE_10883,
BASE_HW_ISSUE_10931,
BASE_HW_ISSUE_10946,
BASE_HW_ISSUE_10969,
BASE_HW_ISSUE_10984,
BASE_HW_ISSUE_10995,
BASE_HW_ISSUE_11012,
BASE_HW_ISSUE_11020,
BASE_HW_ISSUE_11035,
BASE_HW_ISSUE_T76X_1909,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_t60x_r0p0_eac[] = {
BASE_HW_ISSUE_6367,
BASE_HW_ISSUE_6402,
BASE_HW_ISSUE_6787,
BASE_HW_ISSUE_7027,
BASE_HW_ISSUE_7304,
BASE_HW_ISSUE_8408,
BASE_HW_ISSUE_8564,
BASE_HW_ISSUE_8778,
BASE_HW_ISSUE_8975,
BASE_HW_ISSUE_9010,
BASE_HW_ISSUE_9275,
BASE_HW_ISSUE_9418,
BASE_HW_ISSUE_9423,
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_9510,
BASE_HW_ISSUE_10410,
BASE_HW_ISSUE_10471,
BASE_HW_ISSUE_10472,
BASE_HW_ISSUE_10487,
BASE_HW_ISSUE_10607,
BASE_HW_ISSUE_10632,
BASE_HW_ISSUE_10676,
BASE_HW_ISSUE_10682,
BASE_HW_ISSUE_10684,
BASE_HW_ISSUE_10883,
BASE_HW_ISSUE_10931,
BASE_HW_ISSUE_10946,
BASE_HW_ISSUE_10969,
BASE_HW_ISSUE_11012,
BASE_HW_ISSUE_11020,
BASE_HW_ISSUE_11035,
BASE_HW_ISSUE_T76X_1909,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_t60x_r0p1[] = {
BASE_HW_ISSUE_6367,
BASE_HW_ISSUE_6402,
BASE_HW_ISSUE_6787,
BASE_HW_ISSUE_7027,
BASE_HW_ISSUE_7304,
BASE_HW_ISSUE_8408,
BASE_HW_ISSUE_8564,
BASE_HW_ISSUE_8778,
BASE_HW_ISSUE_8975,
BASE_HW_ISSUE_9010,
BASE_HW_ISSUE_9275,
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_9510,
BASE_HW_ISSUE_10410,
BASE_HW_ISSUE_10471,
BASE_HW_ISSUE_10472,
BASE_HW_ISSUE_10487,
BASE_HW_ISSUE_10607,
BASE_HW_ISSUE_10632,
BASE_HW_ISSUE_10676,
BASE_HW_ISSUE_10682,
BASE_HW_ISSUE_10684,
BASE_HW_ISSUE_10883,
BASE_HW_ISSUE_10931,
BASE_HW_ISSUE_10946,
BASE_HW_ISSUE_11012,
BASE_HW_ISSUE_11020,
BASE_HW_ISSUE_11035,
BASE_HW_ISSUE_T76X_1909,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_t62x_r0p1[] = {
BASE_HW_ISSUE_6402,
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_10127,
BASE_HW_ISSUE_10327,
BASE_HW_ISSUE_10410,
BASE_HW_ISSUE_10471,
BASE_HW_ISSUE_10472,
BASE_HW_ISSUE_10487,
BASE_HW_ISSUE_10607,
BASE_HW_ISSUE_10632,
BASE_HW_ISSUE_10676,
BASE_HW_ISSUE_10682,
BASE_HW_ISSUE_10684,
BASE_HW_ISSUE_10817,
BASE_HW_ISSUE_10821,
BASE_HW_ISSUE_10883,
BASE_HW_ISSUE_10931,
BASE_HW_ISSUE_10946,
BASE_HW_ISSUE_10959,
BASE_HW_ISSUE_11012,
BASE_HW_ISSUE_11020,
BASE_HW_ISSUE_11024,
BASE_HW_ISSUE_11035,
BASE_HW_ISSUE_11042,
BASE_HW_ISSUE_T76X_1909,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_t62x_r1p0[] = {
BASE_HW_ISSUE_6402,
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_10471,
BASE_HW_ISSUE_10472,
BASE_HW_ISSUE_10684,
BASE_HW_ISSUE_10821,
BASE_HW_ISSUE_10883,
BASE_HW_ISSUE_10931,
BASE_HW_ISSUE_10946,
BASE_HW_ISSUE_10959,
BASE_HW_ISSUE_11012,
BASE_HW_ISSUE_11020,
BASE_HW_ISSUE_11024,
BASE_HW_ISSUE_11042,
BASE_HW_ISSUE_T76X_1909,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_t62x_r1p1[] = {
BASE_HW_ISSUE_6402,
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_10471,
BASE_HW_ISSUE_10472,
BASE_HW_ISSUE_10684,
BASE_HW_ISSUE_10821,
BASE_HW_ISSUE_10883,
BASE_HW_ISSUE_10931,
BASE_HW_ISSUE_10946,
BASE_HW_ISSUE_10959,
BASE_HW_ISSUE_11012,
BASE_HW_ISSUE_11042,
BASE_HW_ISSUE_T76X_1909,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_t76x_r0p0[] = {
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_10821,
BASE_HW_ISSUE_10883,
BASE_HW_ISSUE_10946,
BASE_HW_ISSUE_11020,
BASE_HW_ISSUE_11024,
BASE_HW_ISSUE_11042,
BASE_HW_ISSUE_T76X_26,
BASE_HW_ISSUE_T76X_1909,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_T76X_3086,
BASE_HW_ISSUE_T76X_3542,
BASE_HW_ISSUE_T76X_3556,
BASE_HW_ISSUE_T76X_3700,
BASE_HW_ISSUE_T76X_3793,
BASE_HW_ISSUE_T76X_3953,
BASE_HW_ISSUE_T76X_3960,
BASE_HW_ISSUE_T76X_3966,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_t76x_r0p1[] = {
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_10821,
BASE_HW_ISSUE_10883,
BASE_HW_ISSUE_10946,
BASE_HW_ISSUE_11020,
BASE_HW_ISSUE_11024,
BASE_HW_ISSUE_11042,
BASE_HW_ISSUE_T76X_26,
BASE_HW_ISSUE_T76X_1909,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_T76X_3086,
BASE_HW_ISSUE_T76X_3542,
BASE_HW_ISSUE_T76X_3556,
BASE_HW_ISSUE_T76X_3700,
BASE_HW_ISSUE_T76X_3793,
BASE_HW_ISSUE_T76X_3953,
BASE_HW_ISSUE_T76X_3960,
BASE_HW_ISSUE_T76X_3966,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_t76x_r0p1_50rel0[] = {
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_10821,
BASE_HW_ISSUE_10883,
BASE_HW_ISSUE_10946,
BASE_HW_ISSUE_11042,
BASE_HW_ISSUE_T76X_26,
BASE_HW_ISSUE_T76X_1909,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_T76X_3086,
BASE_HW_ISSUE_T76X_3542,
BASE_HW_ISSUE_T76X_3556,
BASE_HW_ISSUE_T76X_3700,
BASE_HW_ISSUE_T76X_3793,
BASE_HW_ISSUE_T76X_3953,
BASE_HW_ISSUE_T76X_3960,
BASE_HW_ISSUE_T76X_3966,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_t76x_r0p2[] = {
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_10821,
BASE_HW_ISSUE_10883,
BASE_HW_ISSUE_10946,
BASE_HW_ISSUE_11020,
BASE_HW_ISSUE_11024,
BASE_HW_ISSUE_11042,
BASE_HW_ISSUE_T76X_26,
BASE_HW_ISSUE_T76X_1909,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_T76X_3086,
BASE_HW_ISSUE_T76X_3542,
BASE_HW_ISSUE_T76X_3556,
BASE_HW_ISSUE_T76X_3700,
BASE_HW_ISSUE_T76X_3793,
BASE_HW_ISSUE_T76X_3953,
BASE_HW_ISSUE_T76X_3960,
BASE_HW_ISSUE_T76X_3966,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_t76x_r0p3[] = {
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_10821,
BASE_HW_ISSUE_10883,
BASE_HW_ISSUE_10946,
BASE_HW_ISSUE_11042,
BASE_HW_ISSUE_T76X_26,
BASE_HW_ISSUE_T76X_1909,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_T76X_3086,
BASE_HW_ISSUE_T76X_3542,
BASE_HW_ISSUE_T76X_3556,
BASE_HW_ISSUE_T76X_3700,
BASE_HW_ISSUE_T76X_3793,
BASE_HW_ISSUE_T76X_3953,
BASE_HW_ISSUE_T76X_3960,
BASE_HW_ISSUE_T76X_3966,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_t76x_r1p0[] = {
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_10821,
BASE_HW_ISSUE_10883,
BASE_HW_ISSUE_10946,
BASE_HW_ISSUE_11042,
BASE_HW_ISSUE_T76X_1909,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_T76X_3086,
BASE_HW_ISSUE_T76X_3700,
BASE_HW_ISSUE_T76X_3793,
BASE_HW_ISSUE_T76X_3953,
BASE_HW_ISSUE_T76X_3960,
BASE_HW_ISSUE_T76X_3966,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_t72x_r0p0[] = {
BASE_HW_ISSUE_6402,
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_10471,
BASE_HW_ISSUE_10684,
BASE_HW_ISSUE_10797,
BASE_HW_ISSUE_10821,
BASE_HW_ISSUE_10883,
BASE_HW_ISSUE_10946,
BASE_HW_ISSUE_11042,
BASE_HW_ISSUE_T76X_1909,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_t72x_r1p0[] = {
BASE_HW_ISSUE_6402,
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_10471,
BASE_HW_ISSUE_10684,
BASE_HW_ISSUE_10797,
BASE_HW_ISSUE_10821,
BASE_HW_ISSUE_10883,
BASE_HW_ISSUE_10946,
BASE_HW_ISSUE_11042,
BASE_HW_ISSUE_T76X_1909,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_t72x_r1p1[] = {
BASE_HW_ISSUE_6402,
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_10471,
BASE_HW_ISSUE_10684,
BASE_HW_ISSUE_10797,
BASE_HW_ISSUE_10821,
BASE_HW_ISSUE_10883,
BASE_HW_ISSUE_10946,
BASE_HW_ISSUE_11042,
BASE_HW_ISSUE_T76X_1909,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_model_t72x[] = {
BASE_HW_ISSUE_5736,
BASE_HW_ISSUE_6402,
BASE_HW_ISSUE_9275,
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_10471,
BASE_HW_ISSUE_10797,
BASE_HW_ISSUE_11042,
BASE_HW_ISSUE_T76X_1909,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_model_t76x[] = {
BASE_HW_ISSUE_5736,
BASE_HW_ISSUE_9275,
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_11020,
BASE_HW_ISSUE_11024,
BASE_HW_ISSUE_11042,
BASE_HW_ISSUE_T76X_1909,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_T76X_3086,
BASE_HW_ISSUE_T76X_3700,
BASE_HW_ISSUE_T76X_3793,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_model_t60x[] = {
BASE_HW_ISSUE_5736,
BASE_HW_ISSUE_6402,
BASE_HW_ISSUE_8778,
BASE_HW_ISSUE_9275,
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_10472,
BASE_HW_ISSUE_10931,
BASE_HW_ISSUE_11012,
BASE_HW_ISSUE_11020,
BASE_HW_ISSUE_11024,
BASE_HW_ISSUE_T76X_1909,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_model_t62x[] = {
BASE_HW_ISSUE_5736,
BASE_HW_ISSUE_6402,
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_10472,
BASE_HW_ISSUE_10931,
BASE_HW_ISSUE_11012,
BASE_HW_ISSUE_11020,
BASE_HW_ISSUE_11024,
BASE_HW_ISSUE_11042,
BASE_HW_ISSUE_T76X_1909,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_tFRx_r0p1[] = {
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_10821,
BASE_HW_ISSUE_10883,
BASE_HW_ISSUE_10946,
BASE_HW_ISSUE_T76X_1909,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_T76X_3086,
BASE_HW_ISSUE_T76X_3700,
BASE_HW_ISSUE_T76X_3793,
BASE_HW_ISSUE_T76X_3953,
BASE_HW_ISSUE_T76X_3960,
BASE_HW_ISSUE_T76X_3966,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_tFRx_r0p2[] = {
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_10821,
BASE_HW_ISSUE_10883,
BASE_HW_ISSUE_10946,
BASE_HW_ISSUE_T76X_1909,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_T76X_3086,
BASE_HW_ISSUE_T76X_3700,
BASE_HW_ISSUE_T76X_3793,
BASE_HW_ISSUE_T76X_3953,
BASE_HW_ISSUE_T76X_3966,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_tFRx_r1p0[] = {
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_10821,
BASE_HW_ISSUE_10883,
BASE_HW_ISSUE_10946,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_T76X_3086,
BASE_HW_ISSUE_T76X_3700,
BASE_HW_ISSUE_T76X_3793,
BASE_HW_ISSUE_T76X_3953,
BASE_HW_ISSUE_T76X_3966,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_tFRx_r2p0[] = {
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_10821,
BASE_HW_ISSUE_10883,
BASE_HW_ISSUE_10946,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_T76X_3086,
BASE_HW_ISSUE_T76X_3700,
BASE_HW_ISSUE_T76X_3793,
BASE_HW_ISSUE_T76X_3953,
BASE_HW_ISSUE_T76X_3966,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_model_tFRx[] = {
BASE_HW_ISSUE_5736,
BASE_HW_ISSUE_9275,
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_T76X_3086,
BASE_HW_ISSUE_T76X_3700,
BASE_HW_ISSUE_T76X_3793,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_t86x_r0p2[] = {
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_10821,
BASE_HW_ISSUE_10883,
BASE_HW_ISSUE_10946,
BASE_HW_ISSUE_T76X_1909,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_T76X_3086,
BASE_HW_ISSUE_T76X_3700,
BASE_HW_ISSUE_T76X_3793,
BASE_HW_ISSUE_T76X_3953,
BASE_HW_ISSUE_T76X_3966,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_t86x_r1p0[] = {
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_10821,
BASE_HW_ISSUE_10883,
BASE_HW_ISSUE_10946,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_T76X_3086,
BASE_HW_ISSUE_T76X_3700,
BASE_HW_ISSUE_T76X_3793,
BASE_HW_ISSUE_T76X_3953,
BASE_HW_ISSUE_T76X_3966,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_t86x_r2p0[] = {
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_10821,
BASE_HW_ISSUE_10883,
BASE_HW_ISSUE_10946,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_T76X_3086,
BASE_HW_ISSUE_T76X_3700,
BASE_HW_ISSUE_T76X_3793,
BASE_HW_ISSUE_T76X_3953,
BASE_HW_ISSUE_T76X_3966,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_model_t86x[] = {
BASE_HW_ISSUE_5736,
BASE_HW_ISSUE_9275,
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_T76X_3086,
BASE_HW_ISSUE_T76X_3700,
BASE_HW_ISSUE_T76X_3793,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_t83x_r0p1[] = {
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_10821,
BASE_HW_ISSUE_10883,
BASE_HW_ISSUE_10946,
BASE_HW_ISSUE_T76X_1909,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_T76X_3086,
BASE_HW_ISSUE_T76X_3700,
BASE_HW_ISSUE_T76X_3793,
BASE_HW_ISSUE_T76X_3953,
BASE_HW_ISSUE_T76X_3960,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_t83x_r1p0[] = {
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_10821,
BASE_HW_ISSUE_10883,
BASE_HW_ISSUE_10946,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_T76X_3086,
BASE_HW_ISSUE_T76X_3700,
BASE_HW_ISSUE_T76X_3793,
BASE_HW_ISSUE_T76X_3953,
BASE_HW_ISSUE_T76X_3960,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_model_t83x[] = {
BASE_HW_ISSUE_5736,
BASE_HW_ISSUE_9275,
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_T76X_1909,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_T76X_3086,
BASE_HW_ISSUE_T76X_3700,
BASE_HW_ISSUE_T76X_3793,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_t82x_r0p0[] = {
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_10821,
BASE_HW_ISSUE_10883,
BASE_HW_ISSUE_10946,
BASE_HW_ISSUE_T76X_1909,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_T76X_3086,
BASE_HW_ISSUE_T76X_3700,
BASE_HW_ISSUE_T76X_3793,
BASE_HW_ISSUE_T76X_3953,
BASE_HW_ISSUE_T76X_3960,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_t82x_r0p1[] = {
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_10821,
BASE_HW_ISSUE_10883,
BASE_HW_ISSUE_10946,
BASE_HW_ISSUE_T76X_1909,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_T76X_3086,
BASE_HW_ISSUE_T76X_3700,
BASE_HW_ISSUE_T76X_3793,
BASE_HW_ISSUE_T76X_3953,
BASE_HW_ISSUE_T76X_3960,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_t82x_r1p0[] = {
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_10821,
BASE_HW_ISSUE_10883,
BASE_HW_ISSUE_10946,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_T76X_3086,
BASE_HW_ISSUE_T76X_3700,
BASE_HW_ISSUE_T76X_3793,
BASE_HW_ISSUE_T76X_3953,
BASE_HW_ISSUE_T76X_3960,
BASE_HW_ISSUE_END
};
static const enum base_hw_issue base_hw_issues_model_t82x[] = {
BASE_HW_ISSUE_5736,
BASE_HW_ISSUE_9275,
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_T76X_1909,
BASE_HW_ISSUE_T76X_1963,
BASE_HW_ISSUE_T76X_3086,
BASE_HW_ISSUE_T76X_3700,
BASE_HW_ISSUE_T76X_3793,
BASE_HW_ISSUE_END
};
#if defined(MALI_INCLUDE_TMIX)
static const enum base_hw_issue base_hw_issues_tMIx_r0p0[] = {
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_10821,
BASE_HW_ISSUE_T76X_3700,
BASE_HW_ISSUE_END
};
#endif /* defined(MALI_INCLUDE_TMIX) */
#if defined(MALI_INCLUDE_TMIX)
static const enum base_hw_issue base_hw_issues_model_tMIx[] = {
BASE_HW_ISSUE_5736,
BASE_HW_ISSUE_9275,
BASE_HW_ISSUE_9435,
BASE_HW_ISSUE_T76X_3700,
BASE_HW_ISSUE_END
};
#endif /* defined(MALI_INCLUDE_TMIX) */
#endif /* _BASE_HWCONFIG_ISSUES_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,47 @@
/*
*
* (C) COPYRIGHT 2012-2013 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/**
* @file
* Base cross-proccess sync API.
*/
#ifndef _BASE_KERNEL_SYNC_H_
#define _BASE_KERNEL_SYNC_H_
#include <linux/ioctl.h>
#define STREAM_IOC_MAGIC '~'
/* Fence insert.
*
* Inserts a fence on the stream operated on.
* Fence can be waited via a base fence wait soft-job
* or triggered via a base fence trigger soft-job.
*
* Fences must be cleaned up with close when no longer needed.
*
* No input/output arguments.
* Returns
* >=0 fd
* <0 error code
*/
#define STREAM_IOC_FENCE_INSERT _IO(STREAM_IOC_MAGIC, 0)
#endif /* _BASE_KERNEL_SYNC_H_ */

View file

@ -0,0 +1,52 @@
/*
*
* (C) COPYRIGHT 2010-2014 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#ifndef _BASE_MEM_PRIV_H_
#define _BASE_MEM_PRIV_H_
#define BASE_SYNCSET_OP_MSYNC (1U << 0)
#define BASE_SYNCSET_OP_CSYNC (1U << 1)
/*
* This structure describe a basic memory coherency operation.
* It can either be:
* @li a sync from CPU to Memory:
* - type = ::BASE_SYNCSET_OP_MSYNC
* - mem_handle = a handle to the memory object on which the operation
* is taking place
* - user_addr = the address of the range to be synced
* - size = the amount of data to be synced, in bytes
* - offset is ignored.
* @li a sync from Memory to CPU:
* - type = ::BASE_SYNCSET_OP_CSYNC
* - mem_handle = a handle to the memory object on which the operation
* is taking place
* - user_addr = the address of the range to be synced
* - size = the amount of data to be synced, in bytes.
* - offset is ignored.
*/
struct basep_syncset {
base_mem_handle mem_handle;
u64 user_addr;
u64 size;
u8 type;
u8 padding[7];
};
#endif

View file

@ -0,0 +1,24 @@
/*
*
* (C) COPYRIGHT 2010, 2012-2013, 2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#ifndef _BASE_VENDOR_SPEC_FUNC_H_
#define _BASE_VENDOR_SPEC_FUNC_H_
int kbase_get_vendor_specific_cpu_clock_speed(u32 * const);
#endif /*_BASE_VENDOR_SPEC_FUNC_H_*/

View file

@ -0,0 +1,585 @@
/*
*
* (C) COPYRIGHT 2010-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#ifndef _KBASE_H_
#define _KBASE_H_
#include <mali_malisw.h>
#include <mali_kbase_debug.h>
#include <asm/page.h>
#include <linux/atomic.h>
#include <linux/highmem.h>
#include <linux/hrtimer.h>
#include <linux/ktime.h>
#include <linux/list.h>
#include <linux/mm_types.h>
#include <linux/mutex.h>
#include <linux/rwsem.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include "mali_base_kernel.h"
#include <mali_kbase_uku.h>
#include <mali_kbase_linux.h>
#include "mali_kbase_pm.h"
#include "mali_kbase_mem_lowlevel.h"
#include "mali_kbase_defs.h"
#include "mali_kbase_trace_timeline.h"
#include "mali_kbase_js.h"
#include "mali_kbase_mem.h"
#include "mali_kbase_security.h"
#include "mali_kbase_utility.h"
#include "mali_kbase_gpu_memory_debugfs.h"
#include "mali_kbase_mem_profile_debugfs.h"
#include "mali_kbase_debug_job_fault.h"
#include "mali_kbase_jd_debugfs.h"
#include "mali_kbase_gpuprops.h"
#include "mali_kbase_jm.h"
#include "mali_kbase_vinstr.h"
#include "mali_kbase_ipa.h"
#ifdef CONFIG_GPU_TRACEPOINTS
#include <trace/events/gpu.h>
#endif
/*{ SRUK-MALI_SYSTRACE_SUPPORT*/
#ifdef CONFIG_MALI_SYSTRACE_SUPPORT
/**
* @SRUK-android-graphics
* Events definition for MALI kernel level systrace stupport
* Current implementation only tracks job start/stop events
*/
#define SYSTRACE_EVENT_TYPE_SINGLE 0
#define SYSTRACE_EVENT_TYPE_START 1
#define SYSTRACE_EVENT_TYPE_STOP 2
#define SYSTRACE_EVENT_TYPE_SUSPEND 3
#define SYSTRACE_EVENT_TYPE_RESUME 4
enum {
GPU_UNIT_NONE = 0,
GPU_UNIT_VP,
GPU_UNIT_FP,
GPU_UNIT_CL,
NUMBER_OF_GPU_UNITS
};
void kbase_systrace_mali_job_slots_event(u8 job_event, u8 job_slot, const struct kbase_context *kctx, u8 atom_id, u64 start_timestamp, u8 dep_0_id, u8 dep_0_type, u8 dep_1_id, u8 dep_1_type, u32 gles_ctx_handle);
#endif //CONFIG_MALI_SYSTRACE_SUPPORT
/* SRUK-MALI_SYSTRACE_SUPPORT }*/
/**
* @page page_base_kernel_main Kernel-side Base (KBase) APIs
*
* The Kernel-side Base (KBase) APIs are divided up as follows:
* - @subpage page_kbase_js_policy
*/
/**
* @defgroup base_kbase_api Kernel-side Base (KBase) APIs
*/
struct kbase_device *kbase_device_alloc(void);
/*
* note: configuration attributes member of kbdev needs to have
* been setup before calling kbase_device_init
*/
/*
* API to acquire device list semaphone and return pointer
* to the device list head
*/
const struct list_head *kbase_dev_list_get(void);
/* API to release the device list semaphore */
void kbase_dev_list_put(const struct list_head *dev_list);
int kbase_device_init(struct kbase_device * const kbdev);
void kbase_device_term(struct kbase_device *kbdev);
void kbase_device_free(struct kbase_device *kbdev);
int kbase_device_has_feature(struct kbase_device *kbdev, u32 feature);
/* Needed for gator integration and for reporting vsync information */
struct kbase_device *kbase_find_device(int minor);
void kbase_release_device(struct kbase_device *kbdev);
void kbase_set_profiling_control(struct kbase_device *kbdev, u32 control, u32 value);
u32 kbase_get_profiling_control(struct kbase_device *kbdev, u32 control);
struct kbase_context *
kbase_create_context(struct kbase_device *kbdev, bool is_compat);
void kbase_destroy_context(struct kbase_context *kctx);
int kbase_context_set_create_flags(struct kbase_context *kctx, u32 flags);
int kbase_jd_init(struct kbase_context *kctx);
void kbase_jd_exit(struct kbase_context *kctx);
#ifdef BASE_LEGACY_UK6_SUPPORT
int kbase_jd_submit(struct kbase_context *kctx,
const struct kbase_uk_job_submit *submit_data,
int uk6_atom);
#else
int kbase_jd_submit(struct kbase_context *kctx,
const struct kbase_uk_job_submit *submit_data);
#endif
/**
* kbase_jd_done_worker - Handle a job completion
* @data: a &struct work_struct
*
* This function requeues the job from the runpool (if it was soft-stopped or
* removed from NEXT registers).
*
* Removes it from the system if it finished/failed/was cancelled.
*
* Resolves dependencies to add dependent jobs to the context, potentially
* starting them if necessary (which may add more references to the context)
*
* Releases the reference to the context from the no-longer-running job.
*
* Handles retrying submission outside of IRQ context if it failed from within
* IRQ context.
*/
void kbase_jd_done_worker(struct work_struct *data);
void kbase_jd_done(struct kbase_jd_atom *katom, int slot_nr, ktime_t *end_timestamp,
kbasep_js_atom_done_code done_code);
void kbase_jd_cancel(struct kbase_device *kbdev, struct kbase_jd_atom *katom);
void kbase_jd_evict(struct kbase_device *kbdev, struct kbase_jd_atom *katom);
void kbase_jd_zap_context(struct kbase_context *kctx);
bool jd_done_nolock(struct kbase_jd_atom *katom,
struct list_head *completed_jobs_ctx);
void kbase_jd_free_external_resources(struct kbase_jd_atom *katom);
bool jd_submit_atom(struct kbase_context *kctx,
const struct base_jd_atom_v2 *user_atom,
struct kbase_jd_atom *katom);
void kbase_job_done(struct kbase_device *kbdev, u32 done);
void kbase_gpu_cacheclean(struct kbase_device *kbdev,
struct kbase_jd_atom *katom);
/**
* kbase_job_slot_ctx_priority_check_locked(): - Check for lower priority atoms
* and soft stop them
* @kctx: Pointer to context to check.
* @katom: Pointer to priority atom.
*
* Atoms from @kctx on the same job slot as @katom, which have lower priority
* than @katom will be soft stopped and put back in the queue, so that atoms
* with higher priority can run.
*
* The js_data.runpool_irq.lock must be held when calling this function.
*/
void kbase_job_slot_ctx_priority_check_locked(struct kbase_context *kctx,
struct kbase_jd_atom *katom);
void kbase_job_slot_softstop(struct kbase_device *kbdev, int js,
struct kbase_jd_atom *target_katom);
void kbase_job_slot_softstop_swflags(struct kbase_device *kbdev, int js,
struct kbase_jd_atom *target_katom, u32 sw_flags);
void kbase_job_slot_hardstop(struct kbase_context *kctx, int js,
struct kbase_jd_atom *target_katom);
void kbase_job_check_enter_disjoint(struct kbase_device *kbdev, u32 action,
u16 core_reqs, struct kbase_jd_atom *target_katom);
void kbase_job_check_leave_disjoint(struct kbase_device *kbdev,
struct kbase_jd_atom *target_katom);
void kbase_event_post(struct kbase_context *ctx, struct kbase_jd_atom *event);
int kbase_event_dequeue(struct kbase_context *ctx, struct base_jd_event_v2 *uevent);
int kbase_event_pending(struct kbase_context *ctx);
int kbase_event_init(struct kbase_context *kctx);
void kbase_event_close(struct kbase_context *kctx);
void kbase_event_cleanup(struct kbase_context *kctx);
void kbase_event_wakeup(struct kbase_context *kctx);
int kbase_process_soft_job(struct kbase_jd_atom *katom);
int kbase_prepare_soft_job(struct kbase_jd_atom *katom);
void kbase_finish_soft_job(struct kbase_jd_atom *katom);
void kbase_cancel_soft_job(struct kbase_jd_atom *katom);
void kbase_resume_suspended_soft_jobs(struct kbase_device *kbdev);
bool kbase_replay_process(struct kbase_jd_atom *katom);
/* api used internally for register access. Contains validation and tracing */
void kbase_device_trace_register_access(struct kbase_context *kctx, enum kbase_reg_access_type type, u16 reg_offset, u32 reg_value);
void kbase_device_trace_buffer_install(struct kbase_context *kctx, u32 *tb, size_t size);
void kbase_device_trace_buffer_uninstall(struct kbase_context *kctx);
/* api to be ported per OS, only need to do the raw register access */
void kbase_os_reg_write(struct kbase_device *kbdev, u16 offset, u32 value);
u32 kbase_os_reg_read(struct kbase_device *kbdev, u16 offset);
void kbasep_as_do_poke(struct work_struct *work);
/** Returns the name associated with a Mali exception code
*
* This function is called from the interrupt handler when a GPU fault occurs.
* It reports the details of the fault using KBASE_DEBUG_PRINT_WARN.
*
* @param[in] kbdev The kbase device that the GPU fault occurred from.
* @param[in] exception_code exception code
* @return name associated with the exception code
*/
const char *kbase_exception_name(struct kbase_device *kbdev,
u32 exception_code);
/**
* Check whether a system suspend is in progress, or has already been suspended
*
* The caller should ensure that either kbdev->pm.active_count_lock is held, or
* a dmb was executed recently (to ensure the value is most
* up-to-date). However, without a lock the value could change afterwards.
*
* @return false if a suspend is not in progress
* @return !=false otherwise
*/
static inline bool kbase_pm_is_suspending(struct kbase_device *kbdev)
{
return kbdev->pm.suspending;
}
/**
* Return the atom's ID, as was originally supplied by userspace in
* base_jd_atom_v2::atom_number
*/
static inline int kbase_jd_atom_id(struct kbase_context *kctx, struct kbase_jd_atom *katom)
{
int result;
KBASE_DEBUG_ASSERT(kctx);
KBASE_DEBUG_ASSERT(katom);
KBASE_DEBUG_ASSERT(katom->kctx == kctx);
result = katom - &kctx->jctx.atoms[0];
KBASE_DEBUG_ASSERT(result >= 0 && result <= BASE_JD_ATOM_COUNT);
return result;
}
/**
* kbase_jd_atom_from_id - Return the atom structure for the given atom ID
* @kctx: Context pointer
* @id: ID of atom to retrieve
*
* Return: Pointer to struct kbase_jd_atom associated with the supplied ID
*/
static inline struct kbase_jd_atom *kbase_jd_atom_from_id(
struct kbase_context *kctx, int id)
{
return &kctx->jctx.atoms[id];
}
/**
* Initialize the disjoint state
*
* The disjoint event count and state are both set to zero.
*
* Disjoint functions usage:
*
* The disjoint event count should be incremented whenever a disjoint event occurs.
*
* There are several cases which are regarded as disjoint behavior. Rather than just increment
* the counter during disjoint events we also increment the counter when jobs may be affected
* by what the GPU is currently doing. To facilitate this we have the concept of disjoint state.
*
* Disjoint state is entered during GPU reset and for the entire time that an atom is replaying
* (as part of the replay workaround). Increasing the disjoint state also increases the count of
* disjoint events.
*
* The disjoint state is then used to increase the count of disjoint events during job submission
* and job completion. Any atom submitted or completed while the disjoint state is greater than
* zero is regarded as a disjoint event.
*
* The disjoint event counter is also incremented immediately whenever a job is soft stopped
* and during context creation.
*
* @param kbdev The kbase device
*/
void kbase_disjoint_init(struct kbase_device *kbdev);
/**
* Increase the count of disjoint events
* called when a disjoint event has happened
*
* @param kbdev The kbase device
*/
void kbase_disjoint_event(struct kbase_device *kbdev);
/**
* Increase the count of disjoint events only if the GPU is in a disjoint state
*
* This should be called when something happens which could be disjoint if the GPU
* is in a disjoint state. The state refcount keeps track of this.
*
* @param kbdev The kbase device
*/
void kbase_disjoint_event_potential(struct kbase_device *kbdev);
/**
* Returns the count of disjoint events
*
* @param kbdev The kbase device
* @return the count of disjoint events
*/
u32 kbase_disjoint_event_get(struct kbase_device *kbdev);
/**
* Increment the refcount state indicating that the GPU is in a disjoint state.
*
* Also Increment the disjoint event count (calls @ref kbase_disjoint_event)
* eventually after the disjoint state has completed @ref kbase_disjoint_state_down
* should be called
*
* @param kbdev The kbase device
*/
void kbase_disjoint_state_up(struct kbase_device *kbdev);
/**
* Decrement the refcount state
*
* Also Increment the disjoint event count (calls @ref kbase_disjoint_event)
*
* Called after @ref kbase_disjoint_state_up once the disjoint state is over
*
* @param kbdev The kbase device
*/
void kbase_disjoint_state_down(struct kbase_device *kbdev);
/**
* If a job is soft stopped and the number of contexts is >= this value
* it is reported as a disjoint event
*/
#define KBASE_DISJOINT_STATE_INTERLEAVED_CONTEXT_COUNT_THRESHOLD 2
#if KBASE_TRACE_ENABLE
void kbasep_trace_debugfs_init(struct kbase_device *kbdev);
#ifndef CONFIG_MALI_SYSTEM_TRACE
/** Add trace values about a job-slot
*
* @note Any functions called through this macro will still be evaluated in
* Release builds (CONFIG_MALI_DEBUG not defined). Therefore, when KBASE_TRACE_ENABLE == 0 any
* functions called to get the parameters supplied to this macro must:
* - be static or static inline
* - must just return 0 and have no other statements present in the body.
*/
#define KBASE_TRACE_ADD_SLOT(kbdev, code, ctx, katom, gpu_addr, jobslot) \
kbasep_trace_add(kbdev, KBASE_TRACE_CODE(code), ctx, katom, gpu_addr, \
KBASE_TRACE_FLAG_JOBSLOT, 0, jobslot, 0)
/** Add trace values about a job-slot, with info
*
* @note Any functions called through this macro will still be evaluated in
* Release builds (CONFIG_MALI_DEBUG not defined). Therefore, when KBASE_TRACE_ENABLE == 0 any
* functions called to get the parameters supplied to this macro must:
* - be static or static inline
* - must just return 0 and have no other statements present in the body.
*/
#define KBASE_TRACE_ADD_SLOT_INFO(kbdev, code, ctx, katom, gpu_addr, jobslot, info_val) \
kbasep_trace_add(kbdev, KBASE_TRACE_CODE(code), ctx, katom, gpu_addr, \
KBASE_TRACE_FLAG_JOBSLOT, 0, jobslot, info_val)
/** Add trace values about a ctx refcount
*
* @note Any functions called through this macro will still be evaluated in
* Release builds (CONFIG_MALI_DEBUG not defined). Therefore, when KBASE_TRACE_ENABLE == 0 any
* functions called to get the parameters supplied to this macro must:
* - be static or static inline
* - must just return 0 and have no other statements present in the body.
*/
#define KBASE_TRACE_ADD_REFCOUNT(kbdev, code, ctx, katom, gpu_addr, refcount) \
kbasep_trace_add(kbdev, KBASE_TRACE_CODE(code), ctx, katom, gpu_addr, \
KBASE_TRACE_FLAG_REFCOUNT, refcount, 0, 0)
/** Add trace values about a ctx refcount, and info
*
* @note Any functions called through this macro will still be evaluated in
* Release builds (CONFIG_MALI_DEBUG not defined). Therefore, when KBASE_TRACE_ENABLE == 0 any
* functions called to get the parameters supplied to this macro must:
* - be static or static inline
* - must just return 0 and have no other statements present in the body.
*/
#define KBASE_TRACE_ADD_REFCOUNT_INFO(kbdev, code, ctx, katom, gpu_addr, refcount, info_val) \
kbasep_trace_add(kbdev, KBASE_TRACE_CODE(code), ctx, katom, gpu_addr, \
KBASE_TRACE_FLAG_REFCOUNT, refcount, 0, info_val)
/** Add trace values (no slot or refcount)
*
* @note Any functions called through this macro will still be evaluated in
* Release builds (CONFIG_MALI_DEBUG not defined). Therefore, when KBASE_TRACE_ENABLE == 0 any
* functions called to get the parameters supplied to this macro must:
* - be static or static inline
* - must just return 0 and have no other statements present in the body.
*/
#define KBASE_TRACE_ADD(kbdev, code, ctx, katom, gpu_addr, info_val) \
kbasep_trace_add(kbdev, KBASE_TRACE_CODE(code), ctx, katom, gpu_addr, \
0, 0, 0, info_val)
/* MALI_SEC_INTEGRATION */
#define KBASE_TRACE_ADD_EXYNOS(kbdev, code, ctx, katom, gpu_addr, info_val) \
kbasep_trace_add(kbdev, KBASE_TRACE_CODE(code), ctx, katom, gpu_addr, \
0, 0, 0, info_val)
/** Clear the trace */
#define KBASE_TRACE_CLEAR(kbdev) \
kbasep_trace_clear(kbdev)
/** Dump the slot trace */
#define KBASE_TRACE_DUMP(kbdev) \
kbasep_trace_dump(kbdev)
/** PRIVATE - do not use directly. Use KBASE_TRACE_ADD() instead */
void kbasep_trace_add(struct kbase_device *kbdev, enum kbase_trace_code code, void *ctx, struct kbase_jd_atom *katom, u64 gpu_addr, u8 flags, int refcount, int jobslot, unsigned long info_val);
/** PRIVATE - do not use directly. Use KBASE_TRACE_CLEAR() instead */
void kbasep_trace_clear(struct kbase_device *kbdev);
#else /* #ifndef CONFIG_MALI_SYSTEM_TRACE */
/* Dispatch kbase trace events as system trace events */
#include <mali_linux_kbase_trace.h>
#define KBASE_TRACE_ADD_SLOT(kbdev, code, ctx, katom, gpu_addr, jobslot)\
trace_mali_##code(jobslot, 0)
#define KBASE_TRACE_ADD_SLOT_INFO(kbdev, code, ctx, katom, gpu_addr, jobslot, info_val)\
trace_mali_##code(jobslot, info_val)
#define KBASE_TRACE_ADD_REFCOUNT(kbdev, code, ctx, katom, gpu_addr, refcount)\
trace_mali_##code(refcount, 0)
#define KBASE_TRACE_ADD_REFCOUNT_INFO(kbdev, code, ctx, katom, gpu_addr, refcount, info_val)\
trace_mali_##code(refcount, info_val)
#define KBASE_TRACE_ADD(kbdev, code, ctx, katom, gpu_addr, info_val)\
trace_mali_##code(gpu_addr, info_val)
#define KBASE_TRACE_CLEAR(kbdev)\
do {\
CSTD_UNUSED(kbdev);\
CSTD_NOP(0);\
} while (0)
#define KBASE_TRACE_DUMP(kbdev)\
do {\
CSTD_UNUSED(kbdev);\
CSTD_NOP(0);\
} while (0)
#endif /* #ifndef CONFIG_MALI_SYSTEM_TRACE */
#else
#define KBASE_TRACE_ADD_SLOT(kbdev, code, ctx, katom, gpu_addr, jobslot)\
do {\
CSTD_UNUSED(kbdev);\
CSTD_NOP(code);\
CSTD_UNUSED(ctx);\
CSTD_UNUSED(katom);\
CSTD_UNUSED(gpu_addr);\
CSTD_UNUSED(jobslot);\
} while (0)
#define KBASE_TRACE_ADD_SLOT_INFO(kbdev, code, ctx, katom, gpu_addr, jobslot, info_val)\
do {\
CSTD_UNUSED(kbdev);\
CSTD_NOP(code);\
CSTD_UNUSED(ctx);\
CSTD_UNUSED(katom);\
CSTD_UNUSED(gpu_addr);\
CSTD_UNUSED(jobslot);\
CSTD_UNUSED(info_val);\
CSTD_NOP(0);\
} while (0)
#define KBASE_TRACE_ADD_REFCOUNT(kbdev, code, ctx, katom, gpu_addr, refcount)\
do {\
CSTD_UNUSED(kbdev);\
CSTD_NOP(code);\
CSTD_UNUSED(ctx);\
CSTD_UNUSED(katom);\
CSTD_UNUSED(gpu_addr);\
CSTD_UNUSED(refcount);\
CSTD_NOP(0);\
} while (0)
#define KBASE_TRACE_ADD_REFCOUNT_INFO(kbdev, code, ctx, katom, gpu_addr, refcount, info_val)\
do {\
CSTD_UNUSED(kbdev);\
CSTD_NOP(code);\
CSTD_UNUSED(ctx);\
CSTD_UNUSED(katom);\
CSTD_UNUSED(gpu_addr);\
CSTD_UNUSED(info_val);\
CSTD_NOP(0);\
} while (0)
#define KBASE_TRACE_ADD(kbdev, code, subcode, ctx, katom, val)\
do {\
CSTD_UNUSED(kbdev);\
CSTD_NOP(code);\
CSTD_UNUSED(subcode);\
CSTD_UNUSED(ctx);\
CSTD_UNUSED(katom);\
CSTD_UNUSED(val);\
CSTD_NOP(0);\
} while (0)
/* MALI_SEC_INTEGRATION */
#define KBASE_TRACE_ADD_EXYNOS(kbdev, code, subcode, ctx, katom, val)\
do {\
CSTD_UNUSED(kbdev);\
CSTD_NOP(code);\
CSTD_UNUSED(subcode);\
CSTD_UNUSED(ctx);\
CSTD_UNUSED(katom);\
CSTD_UNUSED(val);\
CSTD_NOP(0);\
} while (0)
#define KBASE_TRACE_CLEAR(kbdev)\
do {\
CSTD_UNUSED(kbdev);\
CSTD_NOP(0);\
} while (0)
#define KBASE_TRACE_DUMP(kbdev)\
do {\
CSTD_UNUSED(kbdev);\
CSTD_NOP(0);\
} while (0)
#endif /* KBASE_TRACE_ENABLE */
/** PRIVATE - do not use directly. Use KBASE_TRACE_DUMP() instead */
void kbasep_trace_dump(struct kbase_device *kbdev);
#ifdef CONFIG_MALI_DEBUG
/**
* kbase_set_driver_inactive - Force driver to go inactive
* @kbdev: Device pointer
* @inactive: true if driver should go inactive, false otherwise
*
* Forcing the driver inactive will cause all future IOCTLs to wait until the
* driver is made active again. This is intended solely for the use of tests
* which require that no jobs are running while the test executes.
*/
void kbase_set_driver_inactive(struct kbase_device *kbdev, bool inactive);
#endif /* CONFIG_MALI_DEBUG */
/* MALI_SEC_INTEGRATION */
void gpu_dump_register_hooks(struct kbase_device *kbdev);
#endif

View file

@ -0,0 +1,209 @@
/*
*
* (C) COPYRIGHT 2013-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include <linux/dma-mapping.h>
#include <mali_kbase.h>
#include <mali_kbase_10969_workaround.h>
/* This function is used to solve an HW issue with single iterator GPUs.
* If a fragment job is soft-stopped on the edge of its bounding box, can happen that the
* restart index is out of bounds and the rerun causes a tile range fault. If this happens
* we try to clamp the restart index to a correct value and rerun the job.
*/
/* Mask of X and Y coordinates for the coordinates words in the descriptors*/
#define X_COORDINATE_MASK 0x00000FFF
#define Y_COORDINATE_MASK 0x0FFF0000
/* Max number of words needed from the fragment shader job descriptor */
#define JOB_HEADER_SIZE_IN_WORDS 10
#define JOB_HEADER_SIZE (JOB_HEADER_SIZE_IN_WORDS*sizeof(u32))
/* Word 0: Status Word */
#define JOB_DESC_STATUS_WORD 0
/* Word 1: Restart Index */
#define JOB_DESC_RESTART_INDEX_WORD 1
/* Word 2: Fault address low word */
#define JOB_DESC_FAULT_ADDR_LOW_WORD 2
/* Word 8: Minimum Tile Coordinates */
#define FRAG_JOB_DESC_MIN_TILE_COORD_WORD 8
/* Word 9: Maximum Tile Coordinates */
#define FRAG_JOB_DESC_MAX_TILE_COORD_WORD 9
int kbasep_10969_workaround_clamp_coordinates(struct kbase_jd_atom *katom)
{
struct device *dev = katom->kctx->kbdev->dev;
u32 clamped = 0;
struct kbase_va_region *region;
phys_addr_t *page_array;
u64 page_index;
u32 offset = katom->jc & (~PAGE_MASK);
u32 *page_1 = NULL;
u32 *page_2 = NULL;
u32 job_header[JOB_HEADER_SIZE_IN_WORDS];
void *dst = job_header;
u32 minX, minY, maxX, maxY;
u32 restartX, restartY;
struct page *p;
u32 copy_size;
dev_warn(dev, "Called TILE_RANGE_FAULT workaround clamping function.\n");
if (!(katom->core_req & BASE_JD_REQ_FS))
return 0;
kbase_gpu_vm_lock(katom->kctx);
region = kbase_region_tracker_find_region_enclosing_address(katom->kctx,
katom->jc);
if (!region || (region->flags & KBASE_REG_FREE))
goto out_unlock;
page_array = kbase_get_cpu_phy_pages(region);
if (!page_array)
goto out_unlock;
page_index = (katom->jc >> PAGE_SHIFT) - region->start_pfn;
p = pfn_to_page(PFN_DOWN(page_array[page_index]));
/* we need the first 10 words of the fragment shader job descriptor.
* We need to check that the offset + 10 words is less that the page
* size otherwise we need to load the next page.
* page_size_overflow will be equal to 0 in case the whole descriptor
* is within the page > 0 otherwise.
*/
copy_size = MIN(PAGE_SIZE - offset, JOB_HEADER_SIZE);
page_1 = kmap_atomic(p);
/* page_1 is a u32 pointer, offset is expressed in bytes */
page_1 += offset>>2;
kbase_sync_single_for_cpu(katom->kctx->kbdev,
kbase_dma_addr(p) + offset,
copy_size, DMA_BIDIRECTIONAL);
memcpy(dst, page_1, copy_size);
/* The data needed overflows page the dimension,
* need to map the subsequent page */
if (copy_size < JOB_HEADER_SIZE) {
p = pfn_to_page(PFN_DOWN(page_array[page_index + 1]));
page_2 = kmap_atomic(p);
kbase_sync_single_for_cpu(katom->kctx->kbdev,
kbase_dma_addr(p),
JOB_HEADER_SIZE - copy_size, DMA_BIDIRECTIONAL);
memcpy(dst + copy_size, page_2, JOB_HEADER_SIZE - copy_size);
}
/* We managed to correctly map one or two pages (in case of overflow) */
/* Get Bounding Box data and restart index from fault address low word */
minX = job_header[FRAG_JOB_DESC_MIN_TILE_COORD_WORD] & X_COORDINATE_MASK;
minY = job_header[FRAG_JOB_DESC_MIN_TILE_COORD_WORD] & Y_COORDINATE_MASK;
maxX = job_header[FRAG_JOB_DESC_MAX_TILE_COORD_WORD] & X_COORDINATE_MASK;
maxY = job_header[FRAG_JOB_DESC_MAX_TILE_COORD_WORD] & Y_COORDINATE_MASK;
restartX = job_header[JOB_DESC_FAULT_ADDR_LOW_WORD] & X_COORDINATE_MASK;
restartY = job_header[JOB_DESC_FAULT_ADDR_LOW_WORD] & Y_COORDINATE_MASK;
dev_warn(dev, "Before Clamping:\n"
"Jobstatus: %08x\n"
"restartIdx: %08x\n"
"Fault_addr_low: %08x\n"
"minCoordsX: %08x minCoordsY: %08x\n"
"maxCoordsX: %08x maxCoordsY: %08x\n",
job_header[JOB_DESC_STATUS_WORD],
job_header[JOB_DESC_RESTART_INDEX_WORD],
job_header[JOB_DESC_FAULT_ADDR_LOW_WORD],
minX, minY,
maxX, maxY);
/* Set the restart index to the one which generated the fault*/
job_header[JOB_DESC_RESTART_INDEX_WORD] =
job_header[JOB_DESC_FAULT_ADDR_LOW_WORD];
if (restartX < minX) {
job_header[JOB_DESC_RESTART_INDEX_WORD] = (minX) | restartY;
dev_warn(dev,
"Clamping restart X index to minimum. %08x clamped to %08x\n",
restartX, minX);
clamped = 1;
}
if (restartY < minY) {
job_header[JOB_DESC_RESTART_INDEX_WORD] = (minY) | restartX;
dev_warn(dev,
"Clamping restart Y index to minimum. %08x clamped to %08x\n",
restartY, minY);
clamped = 1;
}
if (restartX > maxX) {
job_header[JOB_DESC_RESTART_INDEX_WORD] = (maxX) | restartY;
dev_warn(dev,
"Clamping restart X index to maximum. %08x clamped to %08x\n",
restartX, maxX);
clamped = 1;
}
if (restartY > maxY) {
job_header[JOB_DESC_RESTART_INDEX_WORD] = (maxY) | restartX;
dev_warn(dev,
"Clamping restart Y index to maximum. %08x clamped to %08x\n",
restartY, maxY);
clamped = 1;
}
if (clamped) {
/* Reset the fault address low word
* and set the job status to STOPPED */
job_header[JOB_DESC_FAULT_ADDR_LOW_WORD] = 0x0;
job_header[JOB_DESC_STATUS_WORD] = BASE_JD_EVENT_STOPPED;
dev_warn(dev, "After Clamping:\n"
"Jobstatus: %08x\n"
"restartIdx: %08x\n"
"Fault_addr_low: %08x\n"
"minCoordsX: %08x minCoordsY: %08x\n"
"maxCoordsX: %08x maxCoordsY: %08x\n",
job_header[JOB_DESC_STATUS_WORD],
job_header[JOB_DESC_RESTART_INDEX_WORD],
job_header[JOB_DESC_FAULT_ADDR_LOW_WORD],
minX, minY,
maxX, maxY);
/* Flush CPU cache to update memory for future GPU reads*/
memcpy(page_1, dst, copy_size);
p = pfn_to_page(PFN_DOWN(page_array[page_index]));
kbase_sync_single_for_device(katom->kctx->kbdev,
kbase_dma_addr(p) + offset,
copy_size, DMA_TO_DEVICE);
if (copy_size < JOB_HEADER_SIZE) {
memcpy(page_2, dst + copy_size,
JOB_HEADER_SIZE - copy_size);
p = pfn_to_page(PFN_DOWN(page_array[page_index + 1]));
kbase_sync_single_for_device(katom->kctx->kbdev,
kbase_dma_addr(p),
JOB_HEADER_SIZE - copy_size,
DMA_TO_DEVICE);
}
}
if (copy_size < JOB_HEADER_SIZE)
kunmap_atomic(page_2);
kunmap_atomic(page_1);
out_unlock:
kbase_gpu_vm_unlock(katom->kctx);
return clamped;
}

View file

@ -0,0 +1,23 @@
/*
*
* (C) COPYRIGHT 2013-2014 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#ifndef _KBASE_10969_WORKAROUND_
#define _KBASE_10969_WORKAROUND_
int kbasep_10969_workaround_clamp_coordinates(struct kbase_jd_atom *katom);
#endif /* _KBASE_10969_WORKAROUND_ */

View file

@ -0,0 +1,54 @@
/*
*
* (C) COPYRIGHT 2012-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Cache Policy API.
*/
#include "mali_kbase_cache_policy.h"
/*
* The output flags should be a combination of the following values:
* KBASE_REG_CPU_CACHED: CPU cache should be enabled.
*/
u32 kbase_cache_enabled(u32 flags, u32 nr_pages)
{
u32 cache_flags = 0;
CSTD_UNUSED(nr_pages);
if (flags & BASE_MEM_CACHED_CPU)
cache_flags |= KBASE_REG_CPU_CACHED;
return cache_flags;
}
void kbase_sync_single_for_device(struct kbase_device *kbdev, dma_addr_t handle,
size_t size, enum dma_data_direction dir)
{
dma_sync_single_for_device(kbdev->dev, handle, size, dir);
}
void kbase_sync_single_for_cpu(struct kbase_device *kbdev, dma_addr_t handle,
size_t size, enum dma_data_direction dir)
{
dma_sync_single_for_cpu(kbdev->dev, handle, size, dir);
}

View file

@ -0,0 +1,45 @@
/*
*
* (C) COPYRIGHT 2012-2013, 2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Cache Policy API.
*/
#ifndef _KBASE_CACHE_POLICY_H_
#define _KBASE_CACHE_POLICY_H_
#include "mali_kbase.h"
#include "mali_base_kernel.h"
/**
* kbase_cache_enabled - Choose the cache policy for a specific region
* @flags: flags describing attributes of the region
* @nr_pages: total number of pages (backed or not) for the region
*
* Tells whether the CPU and GPU caches should be enabled or not for a specific
* region.
* This function can be modified to customize the cache policy depending on the
* flags and size of the region.
*
* Return: a combination of %KBASE_REG_CPU_CACHED and %KBASE_REG_GPU_CACHED
* depending on the cache policy
*/
u32 kbase_cache_enabled(u32 flags, u32 nr_pages);
#endif /* _KBASE_CACHE_POLICY_H_ */

View file

@ -0,0 +1,51 @@
/*
*
* (C) COPYRIGHT 2011-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include <mali_kbase.h>
#include <mali_kbase_defs.h>
#include <mali_kbase_config_defaults.h>
int kbasep_platform_device_init(struct kbase_device *kbdev)
{
struct kbase_platform_funcs_conf *platform_funcs_p;
platform_funcs_p = (struct kbase_platform_funcs_conf *)PLATFORM_FUNCS;
if (platform_funcs_p && platform_funcs_p->platform_init_func)
return platform_funcs_p->platform_init_func(kbdev);
return 0;
}
void kbasep_platform_device_term(struct kbase_device *kbdev)
{
struct kbase_platform_funcs_conf *platform_funcs_p;
platform_funcs_p = (struct kbase_platform_funcs_conf *)PLATFORM_FUNCS;
if (platform_funcs_p && platform_funcs_p->platform_term_func)
platform_funcs_p->platform_term_func(kbdev);
}
int kbase_cpuprops_get_default_clock_speed(u32 * const clock_speed)
{
KBASE_DEBUG_ASSERT(NULL != clock_speed);
*clock_speed = 100;
return 0;
}

View file

@ -0,0 +1,337 @@
/*
*
* (C) COPYRIGHT 2010-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/**
* @file mali_kbase_config.h
* Configuration API and Attributes for KBase
*/
#ifndef _KBASE_CONFIG_H_
#define _KBASE_CONFIG_H_
#include <asm/page.h>
#include <mali_malisw.h>
#include <mali_kbase_backend_config.h>
/**
* @addtogroup base_api
* @{
*/
/**
* @addtogroup base_kbase_api
* @{
*/
/**
* @addtogroup kbase_config Configuration API and Attributes
* @{
*/
#if !MALI_CUSTOMER_RELEASE
/* This flag is set for internal builds so we can run tests without credentials. */
#define KBASE_HWCNT_DUMP_BYPASS_ROOT 1
#else
#define KBASE_HWCNT_DUMP_BYPASS_ROOT 0
#endif
#include <linux/rbtree.h>
/* Forward declaration of struct kbase_device */
struct kbase_device;
/**
* kbase_platform_funcs_conf - Specifies platform init/term function pointers
*
* Specifies the functions pointers for platform specific initialization and
* termination. By default no functions are required. No additional platform
* specific control is necessary.
*/
struct kbase_platform_funcs_conf {
/**
* platform_init_func - platform specific init function pointer
* @kbdev - kbase_device pointer
*
* Returns 0 on success, negative error code otherwise.
*
* Function pointer for platform specific initialization or NULL if no
* initialization function is required. At the point this the GPU is
* not active and its power and clocks are in unknown (platform specific
* state) as kbase doesn't yet have control of power and clocks.
*
* The platform specific private pointer kbase_device::platform_context
* can be accessed (and possibly initialized) in here.
*/
int (*platform_init_func)(struct kbase_device *kbdev);
/**
* platform_term_func - platform specific termination function pointer
* @kbdev - kbase_device pointer
*
* Function pointer for platform specific termination or NULL if no
* termination function is required. At the point this the GPU will be
* idle but still powered and clocked.
*
* The platform specific private pointer kbase_device::platform_context
* can be accessed (and possibly terminated) in here.
*/
void (*platform_term_func)(struct kbase_device *kbdev);
};
/*
* @brief Specifies the callbacks for power management
*
* By default no callbacks will be made and the GPU must not be powered off.
*/
struct kbase_pm_callback_conf {
/** Callback for when the GPU is idle and the power to it can be switched off.
*
* The system integrator can decide whether to either do nothing, just switch off
* the clocks to the GPU, or to completely power down the GPU.
* The platform specific private pointer kbase_device::platform_context can be accessed and modified in here. It is the
* platform \em callbacks responsiblity to initialize and terminate this pointer if used (see @ref kbase_platform_funcs_conf).
*/
void (*power_off_callback)(struct kbase_device *kbdev);
/** Callback for when the GPU is about to become active and power must be supplied.
*
* This function must not return until the GPU is powered and clocked sufficiently for register access to
* succeed. The return value specifies whether the GPU was powered down since the call to power_off_callback.
* If the GPU state has been lost then this function must return 1, otherwise it should return 0.
* The platform specific private pointer kbase_device::platform_context can be accessed and modified in here. It is the
* platform \em callbacks responsiblity to initialize and terminate this pointer if used (see @ref kbase_platform_funcs_conf).
*
* The return value of the first call to this function is ignored.
*
* @return 1 if the GPU state may have been lost, 0 otherwise.
*/
int (*power_on_callback)(struct kbase_device *kbdev);
/** Callback for when the system is requesting a suspend and GPU power
* must be switched off.
*
* Note that if this callback is present, then this may be called
* without a preceding call to power_off_callback. Therefore this
* callback must be able to take any action that might otherwise happen
* in power_off_callback.
*
* The platform specific private pointer kbase_device::platform_context
* can be accessed and modified in here. It is the platform \em
* callbacks responsibility to initialize and terminate this pointer if
* used (see @ref kbase_platform_funcs_conf).
*/
void (*power_suspend_callback)(struct kbase_device *kbdev);
/** Callback for when the system is resuming from a suspend and GPU
* power must be switched on.
*
* Note that if this callback is present, then this may be called
* without a following call to power_on_callback. Therefore this
* callback must be able to take any action that might otherwise happen
* in power_on_callback.
*
* The platform specific private pointer kbase_device::platform_context
* can be accessed and modified in here. It is the platform \em
* callbacks responsibility to initialize and terminate this pointer if
* used (see @ref kbase_platform_funcs_conf).
*/
void (*power_resume_callback)(struct kbase_device *kbdev);
/** Callback for handling runtime power management initialization.
*
* The runtime power management callbacks @ref power_runtime_off_callback and @ref power_runtime_on_callback
* will become active from calls made to the OS from within this function.
* The runtime calls can be triggered by calls from @ref power_off_callback and @ref power_on_callback.
* Note: for linux the kernel must have CONFIG_PM_RUNTIME enabled to use this feature.
*
* @return 0 on success, else int erro code.
*/
int (*power_runtime_init_callback)(struct kbase_device *kbdev);
/** Callback for handling runtime power management termination.
*
* The runtime power management callbacks @ref power_runtime_off_callback and @ref power_runtime_on_callback
* should no longer be called by the OS on completion of this function.
* Note: for linux the kernel must have CONFIG_PM_RUNTIME enabled to use this feature.
*/
void (*power_runtime_term_callback)(struct kbase_device *kbdev);
/** Callback for runtime power-off power management callback
*
* For linux this callback will be called by the kernel runtime_suspend callback.
* Note: for linux the kernel must have CONFIG_PM_RUNTIME enabled to use this feature.
*
* @return 0 on success, else OS error code.
*/
void (*power_runtime_off_callback)(struct kbase_device *kbdev);
/** Callback for runtime power-on power management callback
*
* For linux this callback will be called by the kernel runtime_resume callback.
* Note: for linux the kernel must have CONFIG_PM_RUNTIME enabled to use this feature.
*/
int (*power_runtime_on_callback)(struct kbase_device *kbdev);
/* MALI_SEC_INTEGRATION */
/** Callback for GPU DVFS handler start/stop
**/
int (*power_dvfs_on_callback)(struct kbase_device *kbdev);
/* MALI_SEC_INTEGRATION */
/** Callback for change DVFS level as fixed clock
**/
int (*power_change_dvfs_level_callback)(struct kbase_device *kbdev);
};
/**
* kbase_cpuprops_get_default_clock_speed - default for CPU_SPEED_FUNC
* @clock_speed - see kbase_cpu_clk_speed_func for details on the parameters
*
* Returns 0 on success, negative error code otherwise.
*
* Default implementation of CPU_SPEED_FUNC. This function sets clock_speed
* to 100, so will be an underestimate for any real system.
*/
int kbase_cpuprops_get_default_clock_speed(u32 * const clock_speed);
/**
* kbase_cpu_clk_speed_func - Type of the function pointer for CPU_SPEED_FUNC
* @param clock_speed - pointer to store the current CPU clock speed in MHz
*
* Returns 0 on success, otherwise negative error code.
*
* This is mainly used to implement OpenCL's clGetDeviceInfo().
*/
typedef int (*kbase_cpu_clk_speed_func) (u32 *clock_speed);
/**
* kbase_gpu_clk_speed_func - Type of the function pointer for GPU_SPEED_FUNC
* @param clock_speed - pointer to store the current GPU clock speed in MHz
*
* Returns 0 on success, otherwise negative error code.
* When an error is returned the caller assumes maximum GPU speed stored in
* gpu_freq_khz_max.
*
* If the system timer is not available then this function is required
* for the OpenCL queue profiling to return correct timing information.
*
*/
typedef int (*kbase_gpu_clk_speed_func) (u32 *clock_speed);
#ifdef CONFIG_OF
struct kbase_platform_config {
};
#else
/*
* @brief Specifies start and end of I/O memory region.
*/
struct kbase_io_memory_region {
u64 start;
u64 end;
};
/*
* @brief Specifies I/O related resources like IRQs and memory region for I/O operations.
*/
struct kbase_io_resources {
u32 job_irq_number;
u32 mmu_irq_number;
u32 gpu_irq_number;
struct kbase_io_memory_region io_memory_region;
};
struct kbase_platform_config {
const struct kbase_io_resources *io_resources;
};
#endif /* CONFIG_OF */
/**
* @brief Gets the pointer to platform config.
*
* @return Pointer to the platform config
*/
struct kbase_platform_config *kbase_get_platform_config(void);
/**
* kbasep_platform_device_init: - Platform specific call to initialize hardware
* @kbdev: kbase device pointer
*
* Function calls a platform defined routine if specified in the configuration
* attributes. The routine can initialize any hardware and context state that
* is required for the GPU block to function.
*
* Return: 0 if no errors have been found in the config.
* Negative error code otherwise.
*/
int kbasep_platform_device_init(struct kbase_device *kbdev);
/**
* kbasep_platform_device_term - Platform specific call to terminate hardware
* @kbdev: Kbase device pointer
*
* Function calls a platform defined routine if specified in the configuration
* attributes. The routine can destroy any platform specific context state and
* shut down any hardware functionality that are outside of the Power Management
* callbacks.
*
*/
void kbasep_platform_device_term(struct kbase_device *kbdev);
/**
* kbase_platform_early_init - Early initialisation of the platform code
*
* This function will be called when the module is loaded to perform any
* early initialisation required by the platform code. Such as reading
* platform specific device tree entries for the GPU.
*
* Return: 0 for success, any other fail causes module initialisation to fail
*/
int kbase_platform_early_init(void);
#ifndef CONFIG_OF
#ifdef CONFIG_MALI_PLATFORM_FAKE
/**
* kbase_platform_fake_register - Register a platform device for the GPU
*
* This can be used to register a platform device on systems where device tree
* is not enabled and the platform initialisation code in the kernel doesn't
* create the GPU device. Where possible device tree should be used instead.
*
* Return: 0 for success, any other fail causes module initialisation to fail
*/
int kbase_platform_fake_register(void);
/**
* kbase_platform_fake_unregister - Unregister a fake platform device
*
* Unregister the platform device created with kbase_platform_fake_register()
*/
void kbase_platform_fake_unregister(void);
#endif
#endif
/** @} *//* end group kbase_config */
/** @} *//* end group base_kbase_api */
/** @} *//* end group base_api */
#endif /* _KBASE_CONFIG_H_ */

View file

@ -0,0 +1,263 @@
/*
*
* (C) COPYRIGHT 2013-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/**
* @file mali_kbase_config_defaults.h
*
* Default values for configuration settings
*
*/
#ifndef _KBASE_CONFIG_DEFAULTS_H_
#define _KBASE_CONFIG_DEFAULTS_H_
/* Include mandatory definitions per platform */
#include <mali_kbase_config_platform.h>
/**
* Irq throttle. It is the minimum desired time in between two
* consecutive gpu interrupts (given in 'us'). The irq throttle
* gpu register will be configured after this, taking into
* account the configured max frequency.
*
* Attached value: number in micro seconds
*/
#define DEFAULT_IRQ_THROTTLE_TIME_US 20
/**
* Default Job Scheduler initial runtime of a context for the CFS Policy,
* in time-slices.
*
* This value is relative to that of the least-run context, and defines
* where in the CFS queue a new context is added. A value of 1 means 'after
* the least-run context has used its timeslice'. Therefore, when all
* contexts consistently use the same amount of time, a value of 1 models a
* FIFO. A value of 0 would model a LIFO.
*
* The value is represented in "numbers of time slices". Multiply this
* value by that defined in @ref DEFAULT_JS_CTX_TIMESLICE_NS to get
* the time value for this in nanoseconds.
*/
#define DEFAULT_JS_CFS_CTX_RUNTIME_INIT_SLICES 1
/**
* Default Job Scheduler minimum runtime value of a context for CFS, in
* time_slices relative to that of the least-run context.
*
* This is a measure of how much preferrential treatment is given to a
* context that is not run very often.
*
* Specficially, this value defines how many timeslices such a context is
* (initially) allowed to use at once. Such contexts (e.g. 'interactive'
* processes) will appear near the front of the CFS queue, and can initially
* use more time than contexts that run continuously (e.g. 'batch'
* processes).
*
* This limit \b prevents a "stored-up timeslices" DoS attack, where a ctx
* not run for a long time attacks the system by using a very large initial
* number of timeslices when it finally does run.
*
* @note A value of zero allows not-run-often contexts to get scheduled in
* quickly, but to only use a single timeslice when they get scheduled in.
*/
#define DEFAULT_JS_CFS_CTX_RUNTIME_MIN_SLICES 2
/**
* Boolean indicating whether the driver is configured to be secure at
* a potential loss of performance.
*
* This currently affects only r0p0-15dev0 HW and earlier.
*
* On r0p0-15dev0 HW and earlier, there are tradeoffs between security and
* performance:
*
* - When this is set to true, the driver remains fully secure,
* but potentially loses performance compared with setting this to
* false.
* - When set to false, the driver is open to certain security
* attacks.
*
* From r0p0-00rel0 and onwards, there is no security loss by setting
* this to false, and no performance loss by setting it to
* true.
*/
#define DEFAULT_SECURE_BUT_LOSS_OF_PERFORMANCE false
enum {
/**
* Use unrestricted Address ID width on the AXI bus.
*/
KBASE_AID_32 = 0x0,
/**
* Restrict GPU to a half of maximum Address ID count.
* This will reduce performance, but reduce bus load due to GPU.
*/
KBASE_AID_16 = 0x3,
/**
* Restrict GPU to a quarter of maximum Address ID count.
* This will reduce performance, but reduce bus load due to GPU.
*/
KBASE_AID_8 = 0x2,
/**
* Restrict GPU to an eighth of maximum Address ID count.
* This will reduce performance, but reduce bus load due to GPU.
*/
KBASE_AID_4 = 0x1
};
/**
* Default setting for read Address ID limiting on AXI bus.
*
* Attached value: u32 register value
* KBASE_AID_32 - use the full 32 IDs (5 ID bits)
* KBASE_AID_16 - use 16 IDs (4 ID bits)
* KBASE_AID_8 - use 8 IDs (3 ID bits)
* KBASE_AID_4 - use 4 IDs (2 ID bits)
* Default value: KBASE_AID_32 (no limit). Note hardware implementation
* may limit to a lower value.
*/
#define DEFAULT_ARID_LIMIT KBASE_AID_32
/**
* Default setting for write Address ID limiting on AXI.
*
* Attached value: u32 register value
* KBASE_AID_32 - use the full 32 IDs (5 ID bits)
* KBASE_AID_16 - use 16 IDs (4 ID bits)
* KBASE_AID_8 - use 8 IDs (3 ID bits)
* KBASE_AID_4 - use 4 IDs (2 ID bits)
* Default value: KBASE_AID_32 (no limit). Note hardware implementation
* may limit to a lower value.
*/
#define DEFAULT_AWID_LIMIT KBASE_AID_32
/**
* Default setting for using alternative hardware counters.
*/
#define DEFAULT_ALTERNATIVE_HWC false
/**
* Default UMP device mapping. A UMP_DEVICE_<device>_SHIFT value which
* defines which UMP device this GPU should be mapped to.
*/
#define DEFAULT_UMP_GPU_DEVICE_SHIFT UMP_DEVICE_Z_SHIFT
/*
* Default period for DVFS sampling
*/
#define DEFAULT_PM_DVFS_PERIOD 100 /* 100ms */
/*
* Power Management poweroff tick granuality. This is in nanoseconds to
* allow HR timer support.
*
* On each scheduling tick, the power manager core may decide to:
* -# Power off one or more shader cores
* -# Power off the entire GPU
*/
#define DEFAULT_PM_GPU_POWEROFF_TICK_NS (400000) /* 400us */
/*
* Power Manager number of ticks before shader cores are powered off
*/
/* MALI_SEC_INTEGRATION */
#define DEFAULT_PM_POWEROFF_TICK_SHADER (0) /* 400-800us */
/*
* Power Manager number of ticks before GPU is powered off
*/
/* MALI_SEC_INTEGRATION */
#define DEFAULT_PM_POWEROFF_TICK_GPU (0) /* 400-800us */
/*
* Default scheduling tick granuality
*/
#define DEFAULT_JS_SCHEDULING_PERIOD_NS (100000000u) /* 100ms */
/*
* Default minimum number of scheduling ticks before jobs are soft-stopped.
*
* This defines the time-slice for a job (which may be different from that of a
* context)
*/
/* MALI_SEC_INTEGRATION */
#define DEFAULT_JS_SOFT_STOP_TICKS (20) /* 2000ms */
/*
* Default minimum number of scheduling ticks before CL jobs are soft-stopped.
*/
/* MALI_SEC_INTEGRATION */
#define DEFAULT_JS_SOFT_STOP_TICKS_CL (20) /* 2000ms */
/*
* Default minimum number of scheduling ticks before jobs are hard-stopped
*/
#define DEFAULT_JS_HARD_STOP_TICKS_SS (50) /* 5s */
#define DEFAULT_JS_HARD_STOP_TICKS_SS_8408 (300) /* 30s */
/*
* Default minimum number of scheduling ticks before CL jobs are hard-stopped.
*/
#define DEFAULT_JS_HARD_STOP_TICKS_CL (50) /* 5s */
/*
* Default minimum number of scheduling ticks before jobs are hard-stopped
* during dumping
*/
#define DEFAULT_JS_HARD_STOP_TICKS_DUMPING (15000) /* 1500s */
/*
* Default minimum number of scheduling ticks before the GPU is reset to clear a
* "stuck" job
*/
#define DEFAULT_JS_RESET_TICKS_SS (55) /* 5.5s */
#define DEFAULT_JS_RESET_TICKS_SS_8408 (450) /* 45s */
/*
* Default minimum number of scheduling ticks before the GPU is reset to clear a
* "stuck" CL job.
*/
#define DEFAULT_JS_RESET_TICKS_CL (55) /* 5.5s */
/*
* Default minimum number of scheduling ticks before the GPU is reset to clear a
* "stuck" job during dumping.
*/
#define DEFAULT_JS_RESET_TICKS_DUMPING (15020) /* 1502s */
/*
* Default number of milliseconds given for other jobs on the GPU to be
* soft-stopped when the GPU needs to be reset.
*/
#define DEFAULT_RESET_TIMEOUT_MS (3000) /* 3s */
/*
* Default timeslice that a context is scheduled in for, in nanoseconds.
*
* When a context has used up this amount of time across its jobs, it is
* scheduled out to let another run.
*
* @note the resolution is nanoseconds (ns) here, because that's the format
* often used by the OS.
*/
#define DEFAULT_JS_CTX_TIMESLICE_NS (50000000) /* 50ms */
#endif /* _KBASE_CONFIG_DEFAULTS_H_ */

View file

@ -0,0 +1,321 @@
/*
*
* (C) COPYRIGHT 2010-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Base kernel context APIs
*/
#include <mali_kbase.h>
#include <mali_midg_regmap.h>
#include <mali_kbase_instr.h>
/**
* kbase_create_context() - Create a kernel base context.
* @kbdev: Kbase device
* @is_compat: Force creation of a 32-bit context
*
* Allocate and init a kernel base context.
*
* Return: new kbase context
*/
struct kbase_context *
kbase_create_context(struct kbase_device *kbdev, bool is_compat)
{
struct kbase_context *kctx;
int err;
KBASE_DEBUG_ASSERT(kbdev != NULL);
/* zero-inited as lot of code assume it's zero'ed out on create */
kctx = vzalloc(sizeof(*kctx));
if (!kctx)
goto out;
/* creating a context is considered a disjoint event */
kbase_disjoint_event(kbdev);
kctx->kbdev = kbdev;
kctx->as_nr = KBASEP_AS_NR_INVALID;
kctx->is_compat = is_compat;
#ifdef CONFIG_MALI_TRACE_TIMELINE
kctx->timeline.owner_tgid = task_tgid_nr(current);
#endif
atomic_set(&kctx->setup_complete, 0);
atomic_set(&kctx->setup_in_progress, 0);
kctx->infinite_cache_active = 0;
spin_lock_init(&kctx->mm_update_lock);
kctx->process_mm = NULL;
atomic_set(&kctx->nonmapped_pages, 0);
kctx->slots_pullable = 0;
err = kbase_mem_pool_init(&kctx->mem_pool,
kbdev->mem_pool_max_size_default,
kctx->kbdev, &kbdev->mem_pool);
if (err)
goto free_kctx;
atomic_set(&kctx->used_pages, 0);
err = kbase_jd_init(kctx);
if (err)
goto free_pool;
err = kbasep_js_kctx_init(kctx);
if (err)
goto free_jd; /* safe to call kbasep_js_kctx_term in this case */
err = kbase_event_init(kctx);
if (err)
goto free_jd;
mutex_init(&kctx->reg_lock);
INIT_LIST_HEAD(&kctx->waiting_soft_jobs);
#ifdef CONFIG_KDS
INIT_LIST_HEAD(&kctx->waiting_kds_resource);
#endif
err = kbase_mmu_init(kctx);
if (err)
goto free_event;
kctx->pgd = kbase_mmu_alloc_pgd(kctx);
if (!kctx->pgd)
goto free_mmu;
kctx->aliasing_sink_page = kbase_mem_pool_alloc(&kctx->mem_pool);
if (!kctx->aliasing_sink_page)
goto no_sink_page;
kctx->tgid = current->tgid;
kctx->pid = current->pid;
init_waitqueue_head(&kctx->event_queue);
kctx->cookies = KBASE_COOKIE_MASK;
/* Make sure page 0 is not used... */
err = kbase_region_tracker_init(kctx);
if (err)
goto no_region_tracker;
#ifdef CONFIG_GPU_TRACEPOINTS
atomic_set(&kctx->jctx.work_id, 0);
#endif
#ifdef CONFIG_MALI_TRACE_TIMELINE
atomic_set(&kctx->timeline.jd_atoms_in_flight, 0);
#endif
kctx->id = atomic_add_return(1, &(kbdev->ctx_num)) - 1;
mutex_init(&kctx->vinstr_cli_lock);
/* MALI_SEC_INTEGRATION */
if (kbdev->vendor_callbacks->create_context)
kbdev->vendor_callbacks->create_context(kctx);
/* MALI_SEC_INTEGRATION */
atomic_set(&kctx->mem_profile_showing_state, 0);
init_waitqueue_head(&kctx->mem_profile_wait);
return kctx;
no_region_tracker:
kbase_mem_pool_free(&kctx->mem_pool, kctx->aliasing_sink_page, false);
no_sink_page:
/* VM lock needed for the call to kbase_mmu_free_pgd */
kbase_gpu_vm_lock(kctx);
kbase_mmu_free_pgd(kctx);
kbase_gpu_vm_unlock(kctx);
free_mmu:
kbase_mmu_term(kctx);
free_event:
kbase_event_cleanup(kctx);
free_jd:
/* Safe to call this one even when didn't initialize (assuming kctx was sufficiently zeroed) */
kbasep_js_kctx_term(kctx);
kbase_jd_exit(kctx);
free_pool:
kbase_mem_pool_term(&kctx->mem_pool);
free_kctx:
vfree(kctx);
out:
return NULL;
}
KBASE_EXPORT_SYMBOL(kbase_create_context);
static void kbase_reg_pending_dtor(struct kbase_va_region *reg)
{
dev_dbg(reg->kctx->kbdev->dev, "Freeing pending unmapped region\n");
kbase_mem_phy_alloc_put(reg->cpu_alloc);
kbase_mem_phy_alloc_put(reg->gpu_alloc);
kfree(reg);
}
/**
* kbase_destroy_context - Destroy a kernel base context.
* @kctx: Context to destroy
*
* Calls kbase_destroy_os_context() to free OS specific structures.
* Will release all outstanding regions.
*/
void kbase_destroy_context(struct kbase_context *kctx)
{
struct kbase_device *kbdev;
int pages;
unsigned long pending_regions_to_clean;
/* MALI_SEC_INTEGRATION */
int profile_count;
/* MALI_SEC_INTEGRATION */
if (!kctx) {
printk("An uninitialized or destroyed context is tried to be destroyed. kctx is null\n");
return ;
}
else if (kctx->ctx_status != CTX_INITIALIZED) {
printk("An uninitialized or destroyed context is tried to be destroyed\n");
printk("kctx: 0x%p, kctx->tgid: %d, kctx->ctx_status: 0x%x\n", kctx, kctx->tgid, kctx->ctx_status);
return ;
}
KBASE_DEBUG_ASSERT(NULL != kctx);
kbdev = kctx->kbdev;
KBASE_DEBUG_ASSERT(NULL != kbdev);
/* MALI_SEC_INTEGRATION */
for (profile_count = 0; profile_count < 3; profile_count++) {
if (wait_event_timeout(kctx->mem_profile_wait, atomic_read(&kctx->mem_profile_showing_state) == 0, (unsigned int) msecs_to_jiffies(1000)))
break;
else
printk("[G3D] waiting for memory profile\n");
}
/* MALI_SEC_INTEGRATION */
while (wait_event_timeout(kbdev->pm.suspending_wait, kbdev->pm.suspending == false, (unsigned int) msecs_to_jiffies(1000)) == 0)
printk("[G3D] Waiting for resuming the device\n");
KBASE_TRACE_ADD(kbdev, CORE_CTX_DESTROY, kctx, NULL, 0u, 0u);
/* Ensure the core is powered up for the destroy process */
/* A suspend won't happen here, because we're in a syscall from a userspace
* thread. */
kbase_pm_context_active(kbdev);
kbase_jd_zap_context(kctx);
kbase_event_cleanup(kctx);
kbase_gpu_vm_lock(kctx);
/* MMU is disabled as part of scheduling out the context */
kbase_mmu_free_pgd(kctx);
/* drop the aliasing sink page now that it can't be mapped anymore */
kbase_mem_pool_free(&kctx->mem_pool, kctx->aliasing_sink_page, false);
/* free pending region setups */
pending_regions_to_clean = (~kctx->cookies) & KBASE_COOKIE_MASK;
while (pending_regions_to_clean) {
unsigned int cookie = __ffs(pending_regions_to_clean);
BUG_ON(!kctx->pending_regions[cookie]);
kbase_reg_pending_dtor(kctx->pending_regions[cookie]);
kctx->pending_regions[cookie] = NULL;
pending_regions_to_clean &= ~(1UL << cookie);
}
kbase_region_tracker_term(kctx);
kbase_gpu_vm_unlock(kctx);
/* Safe to call this one even when didn't initialize (assuming kctx was sufficiently zeroed) */
kbasep_js_kctx_term(kctx);
kbase_jd_exit(kctx);
kbase_pm_context_idle(kbdev);
kbase_mmu_term(kctx);
pages = atomic_read(&kctx->used_pages);
if (pages != 0)
dev_warn(kbdev->dev, "%s: %d pages in use!\n", __func__, pages);
kbase_mem_pool_term(&kctx->mem_pool);
WARN_ON(atomic_read(&kctx->nonmapped_pages) != 0);
/* MALI_SEC_INTEGRATION */
if(kbdev->vendor_callbacks->destroy_context)
kbdev->vendor_callbacks->destroy_context(kctx);
if (kctx->ctx_need_qos) {
kctx->ctx_need_qos = false;
}
vfree(kctx);
/* MALI_SEC_INTEGRATION */
kctx = NULL;
}
KBASE_EXPORT_SYMBOL(kbase_destroy_context);
/**
* kbase_context_set_create_flags - Set creation flags on a context
* @kctx: Kbase context
* @flags: Flags to set
*
* Return: 0 on success
*/
int kbase_context_set_create_flags(struct kbase_context *kctx, u32 flags)
{
int err = 0;
struct kbasep_js_kctx_info *js_kctx_info;
unsigned long irq_flags;
KBASE_DEBUG_ASSERT(NULL != kctx);
js_kctx_info = &kctx->jctx.sched_info;
/* Validate flags */
if (flags != (flags & BASE_CONTEXT_CREATE_KERNEL_FLAGS)) {
err = -EINVAL;
goto out;
}
mutex_lock(&js_kctx_info->ctx.jsctx_mutex);
spin_lock_irqsave(&kctx->kbdev->js_data.runpool_irq.lock, irq_flags);
/* Translate the flags */
if ((flags & BASE_CONTEXT_SYSTEM_MONITOR_SUBMIT_DISABLED) == 0)
js_kctx_info->ctx.flags &= ~((u32) KBASE_CTX_FLAG_SUBMIT_DISABLED);
if ((flags & BASE_CONTEXT_HINT_ONLY_COMPUTE) != 0)
js_kctx_info->ctx.flags |= (u32) KBASE_CTX_FLAG_HINT_ONLY_COMPUTE;
/* Latch the initial attributes into the Job Scheduler */
kbasep_js_ctx_attr_set_initial_attrs(kctx->kbdev, kctx);
spin_unlock_irqrestore(&kctx->kbdev->js_data.runpool_irq.lock,
irq_flags);
mutex_unlock(&js_kctx_info->ctx.jsctx_mutex);
out:
return err;
}
KBASE_EXPORT_SYMBOL(kbase_context_set_create_flags);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,125 @@
/*
*
* (C) COPYRIGHT 2011-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Base kernel property query APIs
*/
#include "mali_kbase.h"
#ifdef BASE_LEGACY_UK7_SUPPORT
#include "mali_kbase_cpuprops.h"
#include "mali_kbase_uku.h"
#include <mali_kbase_config.h>
#include <mali_kbase_config_defaults.h>
#include <linux/cache.h>
#include <linux/cpufreq.h>
#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
#include <asm/cputype.h>
#endif
#define KBASE_DEFAULT_CPU_NUM 0
#define L1_DCACHE_LINE_SIZE_LOG2 L1_CACHE_SHIFT
/*
* Macros used to extract cpu id info
* see documentation for Main ID register
*/
#define KBASE_CPUPROPS_ID_GET_REV(cpuid) ((cpuid) & 0x0F) /* [3:0] Revision */
#define KBASE_CPUPROPS_ID_GET_PART_NR(cpuid)(((cpuid) >> 4) & 0xFFF) /* [15:4] Part number */
#define KBASE_CPUPROPS_ID_GET_ARCH(cpuid) (((cpuid) >> 16) & 0x0F) /* [19:16] Architecture */
#define KBASE_CPUPROPS_ID_GET_VARIANT(cpuid)(((cpuid) >> 20) & 0x0F) /* [23:20] Variant */
#define KBASE_CPUPROPS_ID_GET_CODE(cpuid) (((cpuid) >> 24) & 0xFF) /* [31:23] ASCII code of implementer trademark */
/*Below value sourced from OSK*/
#define L1_DCACHE_SIZE ((u32)0x00008000)
/**
* kbasep_cpuprops_uk_get_cpu_id_info - Retrieves detailed CPU info from given
* cpu_val ( ID reg )
* @kbase_props: CPU props to be filled-in with cpu id info
*
*/
#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
static void kbasep_cpuprops_uk_get_cpu_id_info(struct kbase_uk_cpuprops * const kbase_props)
{
kbase_props->props.cpu_id.id = read_cpuid_id();
kbase_props->props.cpu_id.valid = 1;
kbase_props->props.cpu_id.rev = KBASE_CPUPROPS_ID_GET_REV(kbase_props->props.cpu_id.id);
kbase_props->props.cpu_id.part = KBASE_CPUPROPS_ID_GET_PART_NR(kbase_props->props.cpu_id.id);
kbase_props->props.cpu_id.arch = KBASE_CPUPROPS_ID_GET_ARCH(kbase_props->props.cpu_id.id);
kbase_props->props.cpu_id.variant = KBASE_CPUPROPS_ID_GET_VARIANT(kbase_props->props.cpu_id.id);
kbase_props->props.cpu_id.implementer = KBASE_CPUPROPS_ID_GET_CODE(kbase_props->props.cpu_id.id);
}
#else
static void kbasep_cpuprops_uk_get_cpu_id_info(struct kbase_uk_cpuprops * const kbase_props)
{
kbase_props->props.cpu_id.id = 0;
kbase_props->props.cpu_id.valid = 0;
kbase_props->props.cpu_id.rev = 0;
kbase_props->props.cpu_id.part = 0;
kbase_props->props.cpu_id.arch = 0;
kbase_props->props.cpu_id.variant = 0;
kbase_props->props.cpu_id.implementer = 'N';
}
#endif
/*
* This function (and file!) is kept for the backward compatibility reasons.
* It shall be removed as soon as KBASE_FUNC_CPU_PROPS_REG_DUMP_OBSOLETE
* (previously KBASE_FUNC_CPU_PROPS_REG_DUMP) ioctl call
* is removed. Removal of KBASE_FUNC_CPU_PROPS_REG_DUMP is part of having
* the function for reading cpu properties moved from base to osu.
*/
int kbase_cpuprops_uk_get_props(struct kbase_context *kctx,
struct kbase_uk_cpuprops * const props)
{
unsigned int max_cpu_freq;
props->props.cpu_l1_dcache_line_size_log2 = L1_DCACHE_LINE_SIZE_LOG2;
props->props.cpu_l1_dcache_size = L1_DCACHE_SIZE;
props->props.cpu_flags = BASE_CPU_PROPERTY_FLAG_LITTLE_ENDIAN;
props->props.nr_cores = num_possible_cpus();
props->props.cpu_page_size_log2 = PAGE_SHIFT;
props->props.available_memory_size = totalram_pages << PAGE_SHIFT;
kbasep_cpuprops_uk_get_cpu_id_info(props);
/* check if kernel supports dynamic frequency scaling */
max_cpu_freq = cpufreq_quick_get_max(KBASE_DEFAULT_CPU_NUM);
if (max_cpu_freq != 0) {
/* convert from kHz to mHz */
props->props.max_cpu_clock_speed_mhz = max_cpu_freq / 1000;
} else {
/* fallback if CONFIG_CPU_FREQ turned off */
int err;
kbase_cpu_clk_speed_func get_clock_speed;
get_clock_speed = (kbase_cpu_clk_speed_func) CPU_SPEED_FUNC;
err = get_clock_speed(&props->props.max_cpu_clock_speed_mhz);
if (err)
return err;
}
return 0;
}
#endif /* BASE_LEGACY_UK7_SUPPORT */

View file

@ -0,0 +1,53 @@
/*
*
* (C) COPYRIGHT 2011-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include "mali_kbase.h"
#ifdef BASE_LEGACY_UK7_SUPPORT
/**
* @file mali_kbase_cpuprops.h
* Base kernel property query APIs
*/
#ifndef _KBASE_CPUPROPS_H_
#define _KBASE_CPUPROPS_H_
/* Forward declarations */
struct kbase_uk_cpuprops;
/**
* This file is kept for the backward compatibility reasons.
* It shall be removed as soon as KBASE_FUNC_CPU_PROPS_REG_DUMP_OBSOLETE
* (previously KBASE_FUNC_CPU_PROPS_REG_DUMP) ioctl call
* is removed. Removal of KBASE_FUNC_CPU_PROPS_REG_DUMP is part of having
* the function for reading cpu properties moved from base to osu.
*/
/**
* @brief Provides CPU properties data.
*
* Fill the struct kbase_uk_cpuprops with values from CPU configuration.
*
* @param kctx The kbase context
* @param kbase_props A copy of the struct kbase_uk_cpuprops structure from userspace
*
* @return 0 on success. Any other value indicates failure.
*/
int kbase_cpuprops_uk_get_props(struct kbase_context *kctx, struct kbase_uk_cpuprops * const kbase_props);
#endif /*_KBASE_CPUPROPS_H_*/
#endif /* BASE_LEGACY_UK7_SUPPORT */

View file

@ -0,0 +1,39 @@
/*
*
* (C) COPYRIGHT 2012-2014 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include <mali_kbase.h>
static struct kbasep_debug_assert_cb kbasep_debug_assert_registered_cb = {
NULL,
NULL
};
void kbase_debug_assert_register_hook(kbase_debug_assert_hook *func, void *param)
{
kbasep_debug_assert_registered_cb.func = func;
kbasep_debug_assert_registered_cb.param = param;
}
void kbasep_debug_assert_call_hook(void)
{
if (kbasep_debug_assert_registered_cb.func != NULL)
kbasep_debug_assert_registered_cb.func(kbasep_debug_assert_registered_cb.param);
}
KBASE_EXPORT_SYMBOL(kbasep_debug_assert_call_hook);

View file

@ -0,0 +1,164 @@
/*
*
* (C) COPYRIGHT 2012-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#ifndef _KBASE_DEBUG_H
#define _KBASE_DEBUG_H
#include <linux/bug.h>
/** @brief If equals to 0, a trace containing the file, line, and function will be displayed before each message. */
#define KBASE_DEBUG_SKIP_TRACE 0
/** @brief If different from 0, the trace will only contain the file and line. */
#define KBASE_DEBUG_SKIP_FUNCTION_NAME 0
/** @brief Disable the asserts tests if set to 1. Default is to disable the asserts in release. */
#ifndef KBASE_DEBUG_DISABLE_ASSERTS
#ifdef CONFIG_MALI_DEBUG
#define KBASE_DEBUG_DISABLE_ASSERTS 0
#else
#define KBASE_DEBUG_DISABLE_ASSERTS 1
#endif
#endif /* KBASE_DEBUG_DISABLE_ASSERTS */
/** Function type that is called on an KBASE_DEBUG_ASSERT() or KBASE_DEBUG_ASSERT_MSG() */
typedef void (kbase_debug_assert_hook) (void *);
struct kbasep_debug_assert_cb {
kbase_debug_assert_hook *func;
void *param;
};
/**
* @def KBASEP_DEBUG_PRINT_TRACE
* @brief Private macro containing the format of the trace to display before every message
* @sa KBASE_DEBUG_SKIP_TRACE, KBASE_DEBUG_SKIP_FUNCTION_NAME
*/
#if !KBASE_DEBUG_SKIP_TRACE
#define KBASEP_DEBUG_PRINT_TRACE \
"In file: " __FILE__ " line: " CSTD_STR2(__LINE__)
#if !KBASE_DEBUG_SKIP_FUNCTION_NAME
#define KBASEP_DEBUG_PRINT_FUNCTION __func__
#else
#define KBASEP_DEBUG_PRINT_FUNCTION ""
#endif
#else
#define KBASEP_DEBUG_PRINT_TRACE ""
#endif
/**
* @def KBASEP_DEBUG_ASSERT_OUT(trace, function, ...)
* @brief (Private) system printing function associated to the @see KBASE_DEBUG_ASSERT_MSG event.
* @param trace location in the code from where the message is printed
* @param function function from where the message is printed
* @param ... Format string followed by format arguments.
* @note function parameter cannot be concatenated with other strings
*/
/* Select the correct system output function*/
#ifdef CONFIG_MALI_DEBUG
#define KBASEP_DEBUG_ASSERT_OUT(trace, function, ...)\
do { \
pr_err("Mali<ASSERT>: %s function:%s ", trace, function);\
pr_err(__VA_ARGS__);\
pr_err("\n");\
} while (false)
#else
#define KBASEP_DEBUG_ASSERT_OUT(trace, function, ...) CSTD_NOP()
#endif
#ifdef CONFIG_MALI_DEBUG
#define KBASE_CALL_ASSERT_HOOK() kbasep_debug_assert_call_hook()
#else
#define KBASE_CALL_ASSERT_HOOK() CSTD_NOP()
#endif
/**
* @def KBASE_DEBUG_ASSERT(expr)
* @brief Calls @see KBASE_PRINT_ASSERT and prints the expression @a expr if @a expr is false
*
* @note This macro does nothing if the flag @see KBASE_DEBUG_DISABLE_ASSERTS is set to 1
*
* @param expr Boolean expression
*/
#define KBASE_DEBUG_ASSERT(expr) \
KBASE_DEBUG_ASSERT_MSG(expr, #expr)
#if KBASE_DEBUG_DISABLE_ASSERTS
#define KBASE_DEBUG_ASSERT_MSG(expr, ...) CSTD_NOP()
#else
/**
* @def KBASE_DEBUG_ASSERT_MSG(expr, ...)
* @brief Calls @see KBASEP_DEBUG_ASSERT_OUT and prints the given message if @a expr is false
*
* @note This macro does nothing if the flag @see KBASE_DEBUG_DISABLE_ASSERTS is set to 1
*
* @param expr Boolean expression
* @param ... Message to display when @a expr is false, as a format string followed by format arguments.
*/
#define KBASE_DEBUG_ASSERT_MSG(expr, ...) \
do { \
if (!(expr)) { \
KBASEP_DEBUG_ASSERT_OUT(KBASEP_DEBUG_PRINT_TRACE, KBASEP_DEBUG_PRINT_FUNCTION, __VA_ARGS__);\
KBASE_CALL_ASSERT_HOOK();\
BUG();\
} \
} while (false)
#endif /* KBASE_DEBUG_DISABLE_ASSERTS */
/**
* @def KBASE_DEBUG_CODE( X )
* @brief Executes the code inside the macro only in debug mode
*
* @param X Code to compile only in debug mode.
*/
#ifdef CONFIG_MALI_DEBUG
#define KBASE_DEBUG_CODE(X) X
#else
#define KBASE_DEBUG_CODE(X) CSTD_NOP()
#endif /* CONFIG_MALI_DEBUG */
/** @} */
/**
* @brief Register a function to call on ASSERT
*
* Such functions will \b only be called during Debug mode, and for debugging
* features \b only. Do not rely on them to be called in general use.
*
* To disable the hook, supply NULL to \a func.
*
* @note This function is not thread-safe, and should only be used to
* register/deregister once in the module's lifetime.
*
* @param[in] func the function to call when an assert is triggered.
* @param[in] param the parameter to pass to \a func when calling it
*/
void kbase_debug_assert_register_hook(kbase_debug_assert_hook *func, void *param);
/**
* @brief Call a debug assert hook previously registered with kbase_debug_assert_register_hook()
*
* @note This function is not thread-safe with respect to multiple threads
* registering functions and parameters with
* kbase_debug_assert_register_hook(). Otherwise, thread safety is the
* responsibility of the registered hook.
*/
void kbasep_debug_assert_call_hook(void);
#endif /* _KBASE_DEBUG_H */

View file

@ -0,0 +1,447 @@
/*
*
* (C) COPYRIGHT 2012-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include "mali_kbase_debug_job_fault.h"
#ifdef CONFIG_DEBUG_FS
static bool kbase_is_job_fault_event_pending(struct list_head *event_list)
{
bool ret;
ret = (!list_empty(event_list));
return ret;
}
static bool kbase_ctx_has_no_event_pending(
struct kbase_context *kctx, struct list_head *event_list)
{
struct base_job_fault_event *event;
if (list_empty(event_list))
return true;
list_for_each_entry(event, event_list, head) {
if (event->katom->kctx == kctx)
return false;
}
return false;
}
/* wait until the fault happen and copy the event */
static int kbase_job_fault_event_wait(struct kbase_device *kbdev,
struct list_head *event_list,
struct base_job_fault_event *event)
{
struct base_job_fault_event *event_in;
if (list_empty(event_list)) {
if (wait_event_interruptible(kbdev->job_fault_wq,
kbase_is_job_fault_event_pending(event_list)))
return -ERESTARTSYS;
}
event_in = list_entry(event_list->next,
struct base_job_fault_event, head);
event->event_code = event_in->event_code;
event->katom = event_in->katom;
return 0;
}
/* remove the event from the queue */
static struct base_job_fault_event *kbase_job_fault_event_dequeue(
struct kbase_device *kbdev, struct list_head *event_list)
{
struct base_job_fault_event *event;
event = list_entry(event_list->next,
struct base_job_fault_event, head);
list_del(event_list->next);
return event;
}
/* Remove all the following atoms after the failed atom in the same context
* Call the postponed bottom half of job done.
* Then, this context could be rescheduled.
*/
static void kbase_job_fault_resume_event_cleanup(struct kbase_context *kctx)
{
struct list_head *event_list = &kctx->job_fault_resume_event_list;
while (!list_empty(event_list)) {
struct base_job_fault_event *event;
event = kbase_job_fault_event_dequeue(kctx->kbdev,
&kctx->job_fault_resume_event_list);
kbase_jd_done_worker(&event->katom->work);
}
}
/* Remove all the failed atoms that belong to different contexts
* Resume all the contexts that were suspend due to failed job
*/
static void kbase_job_fault_event_cleanup(struct kbase_device *kbdev)
{
struct list_head *event_list = &kbdev->job_fault_event_list;
while (!list_empty(event_list)) {
kbase_job_fault_event_dequeue(kbdev, event_list);
wake_up(&kbdev->job_fault_resume_wq);
}
}
static void kbase_job_fault_resume_worker(struct work_struct *data)
{
struct base_job_fault_event *event = container_of(data,
struct base_job_fault_event, job_fault_work);
struct kbase_context *kctx;
struct kbase_jd_atom *katom;
katom = event->katom;
kctx = katom->kctx;
dev_info(kctx->kbdev->dev, "Job dumping wait\n");
/* When it was waked up, it need to check if queue is empty or the
* failed atom belongs to different context. If yes, wake up. Both
* of them mean the failed job has been dumped. Please note, it
* should never happen that the job_fault_event_list has the two
* atoms belong to the same context.
*/
wait_event(kctx->kbdev->job_fault_resume_wq,
kbase_ctx_has_no_event_pending(kctx,
&kctx->kbdev->job_fault_event_list));
atomic_set(&kctx->job_fault_count, 0);
kbase_jd_done_worker(&katom->work);
/* In case the following atoms were scheduled during failed job dump
* the job_done_worker was held. We need to rerun it after the dump
* was finished
*/
kbase_job_fault_resume_event_cleanup(kctx);
dev_info(kctx->kbdev->dev, "Job dumping finish, resume scheduler\n");
}
static struct base_job_fault_event *kbase_job_fault_event_queue(
struct list_head *event_list,
struct kbase_jd_atom *atom,
u32 completion_code)
{
struct base_job_fault_event *event;
event = &atom->fault_event;
event->katom = atom;
event->event_code = completion_code;
list_add_tail(&event->head, event_list);
return event;
}
static void kbase_job_fault_event_post(struct kbase_device *kbdev,
struct kbase_jd_atom *katom, u32 completion_code)
{
struct base_job_fault_event *event;
event = kbase_job_fault_event_queue(&kbdev->job_fault_event_list,
katom, completion_code);
wake_up_interruptible(&kbdev->job_fault_wq);
INIT_WORK(&event->job_fault_work, kbase_job_fault_resume_worker);
queue_work(kbdev->job_fault_resume_workq, &event->job_fault_work);
dev_info(katom->kctx->kbdev->dev, "Job fault happen, start dump: %d_%d",
katom->kctx->tgid, katom->kctx->id);
}
/*
* This function will process the job fault
* Get the register copy
* Send the failed job dump event
* Create a Wait queue to wait until the job dump finish
*/
bool kbase_debug_job_fault_process(struct kbase_jd_atom *katom,
u32 completion_code)
{
struct kbase_context *kctx = katom->kctx;
/* Check if dumping is in the process
* only one atom of each context can be dumped at the same time
* If the atom belongs to different context, it can be dumped
*/
if (atomic_read(&kctx->job_fault_count) > 0) {
kbase_job_fault_event_queue(
&kctx->job_fault_resume_event_list,
katom, completion_code);
dev_info(kctx->kbdev->dev, "queue:%d\n",
kbase_jd_atom_id(kctx, katom));
return true;
}
if (kctx->kbdev->job_fault_debug == true) {
if (completion_code != BASE_JD_EVENT_DONE) {
if (kbase_job_fault_get_reg_snapshot(kctx) == false) {
dev_warn(kctx->kbdev->dev, "get reg dump failed\n");
return false;
}
kbase_job_fault_event_post(kctx->kbdev, katom,
completion_code);
atomic_inc(&kctx->job_fault_count);
dev_info(kctx->kbdev->dev, "post:%d\n",
kbase_jd_atom_id(kctx, katom));
return true;
}
}
return false;
}
static int debug_job_fault_show(struct seq_file *m, void *v)
{
struct kbase_device *kbdev = m->private;
struct base_job_fault_event *event = (struct base_job_fault_event *)v;
struct kbase_context *kctx = event->katom->kctx;
int i;
dev_info(kbdev->dev, "debug job fault seq show:%d_%d, %d",
kctx->tgid, kctx->id, event->reg_offset);
if (kctx->reg_dump == NULL) {
dev_warn(kbdev->dev, "reg dump is NULL");
return -1;
}
if (kctx->reg_dump[event->reg_offset] ==
REGISTER_DUMP_TERMINATION_FLAG) {
/* Return the error here to stop the read. And the
* following next() will not be called. The stop can
* get the real event resource and release it
*/
return -1;
}
if (event->reg_offset == 0)
seq_printf(m, "%d_%d\n", kctx->tgid, kctx->id);
for (i = 0; i < 50; i++) {
if (kctx->reg_dump[event->reg_offset] ==
REGISTER_DUMP_TERMINATION_FLAG) {
break;
}
seq_printf(m, "%08x: %08x\n",
kctx->reg_dump[event->reg_offset],
kctx->reg_dump[1+event->reg_offset]);
event->reg_offset += 2;
}
return 0;
}
static void *debug_job_fault_next(struct seq_file *m, void *v, loff_t *pos)
{
struct kbase_device *kbdev = m->private;
struct base_job_fault_event *event = (struct base_job_fault_event *)v;
dev_info(kbdev->dev, "debug job fault seq next:%d, %d",
event->reg_offset, (int)*pos);
return event;
}
static void *debug_job_fault_start(struct seq_file *m, loff_t *pos)
{
struct kbase_device *kbdev = m->private;
struct base_job_fault_event *event;
dev_info(kbdev->dev, "fault job seq start:%d", (int)*pos);
/* The condition is trick here. It needs make sure the
* fault hasn't happened and the dumping hasn't been started,
* or the dumping has finished
*/
if (*pos == 0) {
event = kmalloc(sizeof(*event), GFP_KERNEL);
event->reg_offset = 0;
if (kbase_job_fault_event_wait(kbdev,
&kbdev->job_fault_event_list, event)) {
kfree(event);
return NULL;
}
/* The cache flush workaround is called in bottom half of
* job done but we delayed it. Now we should clean cache
* earlier. Then the GPU memory dump should be correct.
*/
if (event->katom->need_cache_flush_cores_retained) {
kbase_gpu_cacheclean(kbdev, event->katom);
event->katom->need_cache_flush_cores_retained = 0;
}
} else
return NULL;
return event;
}
static void debug_job_fault_stop(struct seq_file *m, void *v)
{
struct kbase_device *kbdev = m->private;
/* here we wake up the kbase_jd_done_worker after stop, it needs
* get the memory dump before the register dump in debug daemon,
* otherwise, the memory dump may be incorrect.
*/
if (v != NULL) {
kfree(v);
dev_info(kbdev->dev, "debug job fault seq stop stage 1");
} else {
if (!list_empty(&kbdev->job_fault_event_list)) {
kbase_job_fault_event_dequeue(kbdev,
&kbdev->job_fault_event_list);
wake_up(&kbdev->job_fault_resume_wq);
}
dev_info(kbdev->dev, "debug job fault seq stop stage 2");
}
}
static const struct seq_operations ops = {
.start = debug_job_fault_start,
.next = debug_job_fault_next,
.stop = debug_job_fault_stop,
.show = debug_job_fault_show,
};
static int debug_job_fault_open(struct inode *in, struct file *file)
{
struct kbase_device *kbdev = in->i_private;
seq_open(file, &ops);
((struct seq_file *)file->private_data)->private = kbdev;
dev_info(kbdev->dev, "debug job fault seq open");
kbdev->job_fault_debug = true;
return 0;
}
static int debug_job_fault_release(struct inode *in, struct file *file)
{
struct kbase_device *kbdev = in->i_private;
seq_release(in, file);
kbdev->job_fault_debug = false;
/* Clean the unprocessed job fault. After that, all the suspended
* contexts could be rescheduled.
*/
kbase_job_fault_event_cleanup(kbdev);
dev_info(kbdev->dev, "debug job fault seq close");
return 0;
}
static const struct file_operations kbasep_debug_job_fault_fops = {
.open = debug_job_fault_open,
.read = seq_read,
.llseek = seq_lseek,
.release = debug_job_fault_release,
};
static int kbase_job_fault_event_init(struct kbase_device *kbdev)
{
INIT_LIST_HEAD(&kbdev->job_fault_event_list);
init_waitqueue_head(&(kbdev->job_fault_wq));
init_waitqueue_head(&(kbdev->job_fault_resume_wq));
kbdev->job_fault_resume_workq = alloc_workqueue(
"kbase_job_fault_resume_work_queue", WQ_MEM_RECLAIM, 1);
return 0;
}
/*
* Initialize debugfs entry for job fault dump
*/
void kbase_debug_job_fault_dev_init(struct kbase_device *kbdev)
{
debugfs_create_file("job_fault", S_IRUGO,
kbdev->mali_debugfs_directory, kbdev,
&kbasep_debug_job_fault_fops);
kbase_job_fault_event_init(kbdev);
kbdev->job_fault_debug = false;
}
/*
* Initialize the relevant data structure per context
*/
void kbase_debug_job_fault_context_init(struct kbase_context *kctx)
{
/* We need allocate double size register range
* Because this memory will keep the register address and value
*/
kctx->reg_dump = kmalloc(0x4000 * 2, GFP_KERNEL);
if (kctx->reg_dump == NULL)
return;
if (kbase_debug_job_fault_reg_snapshot_init(kctx, 0x4000) == false) {
kfree(kctx->reg_dump);
kctx->reg_dump = NULL;
}
INIT_LIST_HEAD(&kctx->job_fault_resume_event_list);
atomic_set(&kctx->job_fault_count, 0);
}
/*
* release the relevant resource per context
*/
void kbase_debug_job_fault_context_exit(struct kbase_context *kctx)
{
kfree(kctx->reg_dump);
}
#endif

View file

@ -0,0 +1,82 @@
/*
*
* (C) COPYRIGHT 2012-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#ifndef _KBASE_DEBUG_JOB_FAULT_H
#define _KBASE_DEBUG_JOB_FAULT_H
#include <mali_kbase.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#define REGISTER_DUMP_TERMINATION_FLAG 0xFFFFFFFF
/**
* kbase_debug_job_fault_dev_init - Initialize job fault debug sysfs
* and create the fault event wait queue per device
* @kbdev: Device pointer
*/
void kbase_debug_job_fault_dev_init(struct kbase_device *kbdev);
/**
* kbase_debug_job_fault_context_init - Initialize the relevant
* data structure per context
* @kctx: KBase context pointer
*/
void kbase_debug_job_fault_context_init(struct kbase_context *kctx);
/**
* kbase_debug_job_fault_context_exit - Release the relevant
* resource per context
* @kctx: KBase context pointer
*/
void kbase_debug_job_fault_context_exit(struct kbase_context *kctx);
/**
* kbase_debug_job_fault_process - Process the failed job.
* It will send a event and wake up the job fault waiting queue
* Then create a work queue to wait for job dump finish
* This function should be called in the interrupt handler and before
* jd_done that make sure the jd_done_worker will be delayed until the
* job dump finish
* @katom: The failed atom pointer
* @completion_code: the job status
* @return true if dump is going on
*/
bool kbase_debug_job_fault_process(struct kbase_jd_atom *katom,
u32 completion_code);
/**
* kbase_debug_job_fault_reg_snapshot_init - Set the interested registers
* address during the job fault process, the relevant registers will
* be saved when a job fault happen
* @kctx: KBase context pointer
* @reg_range: Maximum register address space
* @return true if initializing successfully
*/
bool kbase_debug_job_fault_reg_snapshot_init(struct kbase_context *kctx,
int reg_range);
/**
* kbase_job_fault_get_reg_snapshot - Read the interested registers for
* failed job dump
* @kctx: KBase context pointer
* @return true if getting registers successfully
*/
bool kbase_job_fault_get_reg_snapshot(struct kbase_context *kctx);
#endif /*_KBASE_DEBUG_JOB_FAULT_H*/

View file

@ -0,0 +1,251 @@
/*
*
* (C) COPYRIGHT 2013-2014 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Debugfs interface to dump the memory visible to the GPU
*/
#include "mali_kbase_debug_mem_view.h"
#include "mali_kbase.h"
#include <linux/list.h>
#include <linux/file.h>
#ifdef CONFIG_DEBUG_FS
struct debug_mem_mapping {
struct list_head node;
struct kbase_mem_phy_alloc *alloc;
unsigned long flags;
u64 start_pfn;
size_t nr_pages;
};
struct debug_mem_data {
struct list_head mapping_list;
struct kbase_context *kctx;
};
struct debug_mem_seq_off {
struct list_head *lh;
size_t offset;
};
static void *debug_mem_start(struct seq_file *m, loff_t *_pos)
{
struct debug_mem_data *mem_data = m->private;
struct debug_mem_seq_off *data;
struct debug_mem_mapping *map;
loff_t pos = *_pos;
list_for_each_entry(map, &mem_data->mapping_list, node) {
if (pos >= map->nr_pages) {
pos -= map->nr_pages;
} else {
data = kmalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return NULL;
data->lh = &map->node;
data->offset = pos;
return data;
}
}
/* Beyond the end */
return NULL;
}
static void debug_mem_stop(struct seq_file *m, void *v)
{
kfree(v);
}
static void *debug_mem_next(struct seq_file *m, void *v, loff_t *pos)
{
struct debug_mem_data *mem_data = m->private;
struct debug_mem_seq_off *data = v;
struct debug_mem_mapping *map;
map = list_entry(data->lh, struct debug_mem_mapping, node);
if (data->offset < map->nr_pages - 1) {
data->offset++;
++*pos;
return data;
}
if (list_is_last(data->lh, &mem_data->mapping_list))
return NULL;
data->lh = data->lh->next;
data->offset = 0;
++*pos;
return data;
}
static int debug_mem_show(struct seq_file *m, void *v)
{
struct debug_mem_data *mem_data = m->private;
struct debug_mem_seq_off *data = v;
struct debug_mem_mapping *map;
int i, j;
struct page *page;
uint32_t *mapping;
pgprot_t prot = PAGE_KERNEL;
map = list_entry(data->lh, struct debug_mem_mapping, node);
kbase_gpu_vm_lock(mem_data->kctx);
if (data->offset >= map->alloc->nents) {
seq_printf(m, "%016llx: Unbacked page\n\n", (map->start_pfn +
data->offset) << PAGE_SHIFT);
goto out;
}
if (!(map->flags & KBASE_REG_CPU_CACHED))
prot = pgprot_writecombine(prot);
page = pfn_to_page(PFN_DOWN(map->alloc->pages[data->offset]));
mapping = vmap(&page, 1, VM_MAP, prot);
for (i = 0; i < PAGE_SIZE; i += 4*sizeof(*mapping)) {
seq_printf(m, "%016llx:", i + ((map->start_pfn +
data->offset) << PAGE_SHIFT));
for (j = 0; j < 4*sizeof(*mapping); j += sizeof(*mapping))
seq_printf(m, " %08x", mapping[(i+j)/sizeof(*mapping)]);
seq_putc(m, '\n');
}
vunmap(mapping);
seq_putc(m, '\n');
out:
kbase_gpu_vm_unlock(mem_data->kctx);
return 0;
}
static const struct seq_operations ops = {
.start = debug_mem_start,
.next = debug_mem_next,
.stop = debug_mem_stop,
.show = debug_mem_show,
};
static int debug_mem_open(struct inode *i, struct file *file)
{
struct file *kctx_file = i->i_private;
struct kbase_context *kctx = kctx_file->private_data;
struct rb_node *p;
struct debug_mem_data *mem_data;
int ret;
ret = seq_open(file, &ops);
if (ret)
return ret;
mem_data = kmalloc(sizeof(*mem_data), GFP_KERNEL);
mem_data->kctx = kctx;
INIT_LIST_HEAD(&mem_data->mapping_list);
get_file(kctx_file);
kbase_gpu_vm_lock(kctx);
for (p = rb_first(&kctx->reg_rbtree); p; p = rb_next(p)) {
struct kbase_va_region *reg;
struct debug_mem_mapping *mapping;
reg = rb_entry(p, struct kbase_va_region, rblink);
if (reg->gpu_alloc == NULL)
/* Empty region - ignore */
continue;
mapping = kmalloc(sizeof(*mapping), GFP_KERNEL);
mapping->alloc = kbase_mem_phy_alloc_get(reg->gpu_alloc);
mapping->start_pfn = reg->start_pfn;
mapping->nr_pages = reg->nr_pages;
mapping->flags = reg->flags;
list_add_tail(&mapping->node, &mem_data->mapping_list);
}
kbase_gpu_vm_unlock(kctx);
((struct seq_file *)file->private_data)->private = mem_data;
return 0;
}
static int debug_mem_release(struct inode *inode, struct file *file)
{
struct file *kctx_file = inode->i_private;
struct seq_file *sfile = file->private_data;
struct debug_mem_data *mem_data = sfile->private;
struct debug_mem_mapping *mapping;
seq_release(inode, file);
while (!list_empty(&mem_data->mapping_list)) {
mapping = list_first_entry(&mem_data->mapping_list,
struct debug_mem_mapping, node);
kbase_mem_phy_alloc_put(mapping->alloc);
list_del(&mapping->node);
kfree(mapping);
}
kfree(mem_data);
fput(kctx_file);
return 0;
}
static const struct file_operations kbase_debug_mem_view_fops = {
.open = debug_mem_open,
.release = debug_mem_release,
.read = seq_read,
.llseek = seq_lseek
};
/**
* kbase_debug_mem_view_init - Initialise the mem_view sysfs file
* @kctx_file: The /dev/mali0 file instance for the context
*
* This function creates a "mem_view" file which can be used to get a view of
* the context's memory as the GPU sees it (i.e. using the GPU's page tables).
*
* The file is cleaned up by a call to debugfs_remove_recursive() deleting the
* parent directory.
*/
void kbase_debug_mem_view_init(struct file *kctx_file)
{
struct kbase_context *kctx = kctx_file->private_data;
debugfs_create_file("mem_view", S_IRUGO, kctx->kctx_dentry, kctx_file,
&kbase_debug_mem_view_fops);
}
#endif

View file

@ -0,0 +1,25 @@
/*
*
* (C) COPYRIGHT 2013-2014 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#ifndef _KBASE_DEBUG_MEM_VIEW_H
#define _KBASE_DEBUG_MEM_VIEW_H
#include <mali_kbase.h>
void kbase_debug_mem_view_init(struct file *kctx_file);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,739 @@
/*
*
* (C) COPYRIGHT 2010-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Base kernel device APIs
*/
#include <linux/debugfs.h>
#include <linux/dma-mapping.h>
#include <linux/seq_file.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <mali_kbase.h>
#include <mali_kbase_defs.h>
#include <mali_kbase_hw.h>
#include <mali_kbase_config_defaults.h>
#include <mali_kbase_profiling_gator_api.h>
/* MALI_SEC_INTEGRATION */
#include "gpu_control.h"
/* NOTE: Magic - 0x45435254 (TRCE in ASCII).
* Supports tracing feature provided in the base module.
* Please keep it in sync with the value of base module.
*/
#define TRACE_BUFFER_HEADER_SPECIAL 0x45435254
#if KBASE_TRACE_ENABLE
static const char *kbasep_trace_code_string[] = {
/* IMPORTANT: USE OF SPECIAL #INCLUDE OF NON-STANDARD HEADER FILE
* THIS MUST BE USED AT THE START OF THE ARRAY */
#define KBASE_TRACE_CODE_MAKE_CODE(X) # X
#include "mali_kbase_trace_defs.h"
#undef KBASE_TRACE_CODE_MAKE_CODE
};
#endif
/* MALI_SEC_INTEGRATION */
//#define DEBUG_MESSAGE_SIZE 256
#define DEBUG_MESSAGE_SIZE KBASE_TRACE_SIZE
static int kbasep_trace_init(struct kbase_device *kbdev);
static void kbasep_trace_term(struct kbase_device *kbdev);
static void kbasep_trace_hook_wrapper(void *param);
struct kbase_device *kbase_device_alloc(void)
{
return kzalloc(sizeof(struct kbase_device), GFP_KERNEL);
}
static int kbase_device_as_init(struct kbase_device *kbdev, int i)
{
const char format[] = "mali_mmu%d";
char name[sizeof(format)];
const char poke_format[] = "mali_mmu%d_poker";
char poke_name[sizeof(poke_format)];
if (kbase_hw_has_issue(kbdev, BASE_HW_ISSUE_8316))
snprintf(poke_name, sizeof(poke_name), poke_format, i);
snprintf(name, sizeof(name), format, i);
kbdev->as[i].number = i;
kbdev->as[i].fault_addr = 0ULL;
kbdev->as[i].pf_wq = alloc_workqueue(name, 0, 1);
if (!kbdev->as[i].pf_wq)
return -EINVAL;
mutex_init(&kbdev->as[i].transaction_mutex);
INIT_WORK(&kbdev->as[i].work_pagefault, page_fault_worker);
INIT_WORK(&kbdev->as[i].work_busfault, bus_fault_worker);
if (kbase_hw_has_issue(kbdev, BASE_HW_ISSUE_8316)) {
struct hrtimer *poke_timer = &kbdev->as[i].poke_timer;
struct work_struct *poke_work = &kbdev->as[i].poke_work;
kbdev->as[i].poke_wq = alloc_workqueue(poke_name, 0, 1);
if (!kbdev->as[i].poke_wq) {
destroy_workqueue(kbdev->as[i].pf_wq);
return -EINVAL;
}
KBASE_DEBUG_ASSERT(!object_is_on_stack(poke_work));
INIT_WORK(poke_work, kbasep_as_do_poke);
hrtimer_init(poke_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
poke_timer->function = kbasep_as_poke_timer_callback;
kbdev->as[i].poke_refcount = 0;
kbdev->as[i].poke_state = 0u;
}
return 0;
}
static void kbase_device_as_term(struct kbase_device *kbdev, int i)
{
destroy_workqueue(kbdev->as[i].pf_wq);
if (kbase_hw_has_issue(kbdev, BASE_HW_ISSUE_8316))
destroy_workqueue(kbdev->as[i].poke_wq);
}
static int kbase_device_all_as_init(struct kbase_device *kbdev)
{
int i, err;
for (i = 0; i < kbdev->nr_hw_address_spaces; i++) {
err = kbase_device_as_init(kbdev, i);
if (err)
goto free_workqs;
}
return 0;
free_workqs:
for (; i > 0; i--)
kbase_device_as_term(kbdev, i);
return err;
}
static void kbase_device_all_as_term(struct kbase_device *kbdev)
{
int i;
for (i = 0; i < kbdev->nr_hw_address_spaces; i++)
kbase_device_as_term(kbdev, i);
}
int kbase_device_init(struct kbase_device * const kbdev)
{
int i, err;
spin_lock_init(&kbdev->mmu_mask_change);
/* Get the list of workarounds for issues on the current HW
* (identified by the GPU_ID register)
*/
err = kbase_hw_set_issues_mask(kbdev);
if (err)
goto fail;
/* Set the list of features available on the current HW
* (identified by the GPU_ID register)
*/
kbase_hw_set_features_mask(kbdev);
/* On Linux 4.0+, dma coherency is determined from device tree */
#if defined(CONFIG_ARM64) && LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)
set_dma_ops(kbdev->dev, &noncoherent_swiotlb_dma_ops);
#endif
/* Workaround a pre-3.13 Linux issue, where dma_mask is NULL when our
* device structure was created by device-tree
*/
if (!kbdev->dev->dma_mask)
kbdev->dev->dma_mask = &kbdev->dev->coherent_dma_mask;
err = dma_set_mask(kbdev->dev,
DMA_BIT_MASK(kbdev->gpu_props.mmu.pa_bits));
if (err)
goto dma_set_mask_failed;
err = dma_set_coherent_mask(kbdev->dev,
DMA_BIT_MASK(kbdev->gpu_props.mmu.pa_bits));
if (err)
goto dma_set_mask_failed;
kbdev->nr_hw_address_spaces = kbdev->gpu_props.num_address_spaces;
err = kbase_device_all_as_init(kbdev);
if (err)
goto as_init_failed;
spin_lock_init(&kbdev->hwcnt.lock);
err = kbasep_trace_init(kbdev);
if (err)
goto term_as;
mutex_init(&kbdev->cacheclean_lock);
#ifdef CONFIG_MALI_TRACE_TIMELINE
for (i = 0; i < BASE_JM_MAX_NR_SLOTS; ++i)
kbdev->timeline.slot_atoms_submitted[i] = 0;
for (i = 0; i <= KBASEP_TIMELINE_PM_EVENT_LAST; ++i)
atomic_set(&kbdev->timeline.pm_event_uid[i], 0);
#endif /* CONFIG_MALI_TRACE_TIMELINE */
/* fbdump profiling controls set to 0 - fbdump not enabled until changed by gator */
for (i = 0; i < FBDUMP_CONTROL_MAX; i++)
kbdev->kbase_profiling_controls[i] = 0;
kbase_debug_assert_register_hook(&kbasep_trace_hook_wrapper, kbdev);
atomic_set(&kbdev->ctx_num, 0);
err = kbase_instr_backend_init(kbdev);
if (err)
goto term_trace;
kbdev->pm.dvfs_period = DEFAULT_PM_DVFS_PERIOD;
kbdev->reset_timeout_ms = DEFAULT_RESET_TIMEOUT_MS;
kbdev->mmu_mode = kbase_mmu_mode_get_lpae();
#ifdef CONFIG_MALI_DEBUG
init_waitqueue_head(&kbdev->driver_inactive_wait);
#endif /* CONFIG_MALI_DEBUG */
return 0;
term_trace:
kbasep_trace_term(kbdev);
term_as:
kbase_device_all_as_term(kbdev);
as_init_failed:
dma_set_mask_failed:
fail:
return err;
}
void kbase_device_term(struct kbase_device *kbdev)
{
KBASE_DEBUG_ASSERT(kbdev);
#if KBASE_TRACE_ENABLE
kbase_debug_assert_register_hook(NULL, NULL);
#endif
kbase_instr_backend_term(kbdev);
kbasep_trace_term(kbdev);
kbase_device_all_as_term(kbdev);
}
void kbase_device_free(struct kbase_device *kbdev)
{
kfree(kbdev);
}
void kbase_device_trace_buffer_install(struct kbase_context *kctx, u32 *tb, size_t size)
{
unsigned long flags;
KBASE_DEBUG_ASSERT(kctx);
KBASE_DEBUG_ASSERT(tb);
/* set up the header */
/* magic number in the first 4 bytes */
tb[0] = TRACE_BUFFER_HEADER_SPECIAL;
/* Store (write offset = 0, wrap counter = 0, transaction active = no)
* write offset 0 means never written.
* Offsets 1 to (wrap_offset - 1) used to store values when trace started
*/
tb[1] = 0;
/* install trace buffer */
spin_lock_irqsave(&kctx->jctx.tb_lock, flags);
kctx->jctx.tb_wrap_offset = size / 8;
kctx->jctx.tb = tb;
spin_unlock_irqrestore(&kctx->jctx.tb_lock, flags);
}
void kbase_device_trace_buffer_uninstall(struct kbase_context *kctx)
{
unsigned long flags;
KBASE_DEBUG_ASSERT(kctx);
spin_lock_irqsave(&kctx->jctx.tb_lock, flags);
kctx->jctx.tb = NULL;
kctx->jctx.tb_wrap_offset = 0;
spin_unlock_irqrestore(&kctx->jctx.tb_lock, flags);
}
void kbase_device_trace_register_access(struct kbase_context *kctx, enum kbase_reg_access_type type, u16 reg_offset, u32 reg_value)
{
unsigned long flags;
spin_lock_irqsave(&kctx->jctx.tb_lock, flags);
if (kctx->jctx.tb) {
u16 wrap_count;
u16 write_offset;
u32 *tb = kctx->jctx.tb;
u32 header_word;
header_word = tb[1];
KBASE_DEBUG_ASSERT(0 == (header_word & 0x1));
wrap_count = (header_word >> 1) & 0x7FFF;
write_offset = (header_word >> 16) & 0xFFFF;
/* mark as transaction in progress */
tb[1] |= 0x1;
mb();
/* calculate new offset */
write_offset++;
if (write_offset == kctx->jctx.tb_wrap_offset) {
/* wrap */
write_offset = 1;
wrap_count++;
wrap_count &= 0x7FFF; /* 15bit wrap counter */
}
/* store the trace entry at the selected offset */
tb[write_offset * 2 + 0] = (reg_offset & ~0x3) | ((type == REG_WRITE) ? 0x1 : 0x0);
tb[write_offset * 2 + 1] = reg_value;
mb();
/* new header word */
header_word = (write_offset << 16) | (wrap_count << 1) | 0x0; /* transaction complete */
tb[1] = header_word;
}
spin_unlock_irqrestore(&kctx->jctx.tb_lock, flags);
}
/*
* Device trace functions
*/
#if KBASE_TRACE_ENABLE
static int kbasep_trace_init(struct kbase_device *kbdev)
{
/* MALI_SEC_INTEGRATION */
#ifndef CONFIG_MALI_EXYNOS_TRACE
struct kbase_trace *rbuf;
rbuf = kmalloc_array(KBASE_TRACE_SIZE, sizeof(*rbuf), GFP_KERNEL);
if (!rbuf)
return -EINVAL;
kbdev->trace_rbuf = rbuf;
spin_lock_init(&kbdev->trace_lock);
#endif
return 0;
}
static void kbasep_trace_term(struct kbase_device *kbdev)
{
/* MALI_SEC_INTEGRATION */
#ifndef CONFIG_MALI_EXYNOS_TRACE
kfree(kbdev->trace_rbuf);
#endif
}
/* MALI_SEC_INTEGRATION */
//static
void kbasep_trace_format_msg(struct kbase_trace *trace_msg, char *buffer, int len)
{
s32 written = 0;
/* Initial part of message */
written += MAX(snprintf(buffer + written, MAX(len - written, 0), "%d.%.6d,%d,%d,%s,%p,", (int)trace_msg->timestamp.tv_sec, (int)(trace_msg->timestamp.tv_nsec / 1000), trace_msg->thread_id, trace_msg->cpu, kbasep_trace_code_string[trace_msg->code], trace_msg->ctx), 0);
if (trace_msg->katom)
written += MAX(snprintf(buffer + written, MAX(len - written, 0), "atom %d (ud: 0x%llx 0x%llx)", trace_msg->atom_number, trace_msg->atom_udata[0], trace_msg->atom_udata[1]), 0);
written += MAX(snprintf(buffer + written, MAX(len - written, 0), ",%.8llx,", trace_msg->gpu_addr), 0);
/* NOTE: Could add function callbacks to handle different message types */
/* Jobslot present */
if (trace_msg->flags & KBASE_TRACE_FLAG_JOBSLOT)
written += MAX(snprintf(buffer + written, MAX(len - written, 0), "%d", trace_msg->jobslot), 0);
written += MAX(snprintf(buffer + written, MAX(len - written, 0), ","), 0);
/* Refcount present */
if (trace_msg->flags & KBASE_TRACE_FLAG_REFCOUNT)
written += MAX(snprintf(buffer + written, MAX(len - written, 0), "%d", trace_msg->refcount), 0);
written += MAX(snprintf(buffer + written, MAX(len - written, 0), ","), 0);
/* Rest of message */
written += MAX(snprintf(buffer + written, MAX(len - written, 0), "0x%.8lx", trace_msg->info_val), 0);
}
static void kbasep_trace_dump_msg(struct kbase_device *kbdev, struct kbase_trace *trace_msg)
{
char buffer[DEBUG_MESSAGE_SIZE];
kbasep_trace_format_msg(trace_msg, buffer, DEBUG_MESSAGE_SIZE);
dev_dbg(kbdev->dev, "%s", buffer);
}
/* MALI_SEC_INTEGRATION */
#ifdef CONFIG_MALI_EXYNOS_TRACE
bool check_trace_code(enum kbase_trace_code code)
{
unsigned int temp = code;
switch(temp) {
case KBASE_TRACE_CODE(CORE_CTX_DESTROY):
case KBASE_TRACE_CODE(CORE_GPU_SOFT_RESET):
case KBASE_TRACE_CODE(CORE_GPU_HARD_RESET):
case KBASE_TRACE_CODE(JM_SOFTSTOP):
case KBASE_TRACE_CODE(JM_HARDSTOP):
case KBASE_TRACE_CODE(LSI_GPU_ON):
case KBASE_TRACE_CODE(LSI_GPU_OFF):
case KBASE_TRACE_CODE(LSI_SUSPEND):
case KBASE_TRACE_CODE(LSI_RESUME):
case KBASE_TRACE_CODE(LSI_CLOCK_VALUE):
case KBASE_TRACE_CODE(LSI_TMU_VALUE):
case KBASE_TRACE_CODE(LSI_VOL_VALUE):
case KBASE_TRACE_CODE(LSI_REGISTER_DUMP):
case KBASE_TRACE_CODE(LSI_CLOCK_ON):
case KBASE_TRACE_CODE(LSI_CLOCK_OFF):
case KBASE_TRACE_CODE(LSI_HWCNT_ON_DVFS):
case KBASE_TRACE_CODE(LSI_HWCNT_OFF_DVFS):
case KBASE_TRACE_CODE(LSI_HWCNT_ON_GPR):
case KBASE_TRACE_CODE(LSI_HWCNT_OFF_GPR):
case KBASE_TRACE_CODE(LSI_HWCNT_BT_ON):
case KBASE_TRACE_CODE(LSI_HWCNT_BT_OFF):
case KBASE_TRACE_CODE(LSI_HWCNT_VSYNC_SKIP):
case KBASE_TRACE_CODE(LSI_CHECKSUM):
case KBASE_TRACE_CODE(LSI_GPU_MAX_LOCK):
case KBASE_TRACE_CODE(LSI_GPU_MIN_LOCK):
return true;
default:
return false;
}
return true;
}
#endif
void kbasep_trace_add(struct kbase_device *kbdev, enum kbase_trace_code code, void *ctx, struct kbase_jd_atom *katom, u64 gpu_addr, u8 flags, int refcount, int jobslot, unsigned long info_val)
{
unsigned long irqflags;
struct kbase_trace *trace_msg;
/* MALI_SEC_INTEGRATION */
#ifdef CONFIG_MALI_EXYNOS_TRACE
u64 time;
unsigned long rem_nsec;
if (!check_trace_code(code))
return;
if (code == KBASE_TRACE_CODE(JM_SOFTSTOP) || code == KBASE_TRACE_CODE(JM_HARDSTOP)) {
if (gpu_is_power_on())
gpu_dump_register_hooks(kbdev);
else
dev_err(kbdev->dev, "GPU raized JM_SOFTSTOP or JM_HARDSTOP. but GPU power is down. Skip the GPU dump\n");
}
#endif
spin_lock_irqsave(&kbdev->trace_lock, irqflags);
trace_msg = &kbdev->trace_rbuf[kbdev->trace_next_in];
/* Fill the message */
trace_msg->thread_id = task_pid_nr(current);
trace_msg->cpu = task_cpu(current);
/* MALI_SEC_INTEGRATION */
#ifdef CONFIG_MALI_EXYNOS_TRACE
time = local_clock();
rem_nsec = do_div(time, 1000000000);
trace_msg->timestamp.tv_sec = time;
trace_msg->timestamp.tv_nsec = rem_nsec;
#else
getnstimeofday(&trace_msg->timestamp);
#endif
trace_msg->code = code;
trace_msg->ctx = ctx;
if (NULL == katom) {
trace_msg->katom = false;
} else {
trace_msg->katom = true;
trace_msg->atom_number = kbase_jd_atom_id(katom->kctx, katom);
trace_msg->atom_udata[0] = katom->udata.blob[0];
trace_msg->atom_udata[1] = katom->udata.blob[1];
}
trace_msg->gpu_addr = gpu_addr;
trace_msg->jobslot = jobslot;
trace_msg->refcount = MIN((unsigned int)refcount, 0xFF);
trace_msg->info_val = info_val;
trace_msg->flags = flags;
/* Update the ringbuffer indices */
kbdev->trace_next_in = (kbdev->trace_next_in + 1) & KBASE_TRACE_MASK;
if (kbdev->trace_next_in == kbdev->trace_first_out)
kbdev->trace_first_out = (kbdev->trace_first_out + 1) & KBASE_TRACE_MASK;
/* Done */
spin_unlock_irqrestore(&kbdev->trace_lock, irqflags);
}
void kbasep_trace_clear(struct kbase_device *kbdev)
{
unsigned long flags;
spin_lock_irqsave(&kbdev->trace_lock, flags);
kbdev->trace_first_out = kbdev->trace_next_in;
spin_unlock_irqrestore(&kbdev->trace_lock, flags);
}
void kbasep_trace_dump(struct kbase_device *kbdev)
{
unsigned long flags;
u32 start;
u32 end;
dev_dbg(kbdev->dev, "Dumping trace:\nsecs,nthread,cpu,code,ctx,katom,gpu_addr,jobslot,refcount,info_val");
spin_lock_irqsave(&kbdev->trace_lock, flags);
start = kbdev->trace_first_out;
end = kbdev->trace_next_in;
while (start != end) {
struct kbase_trace *trace_msg = &kbdev->trace_rbuf[start];
kbasep_trace_dump_msg(kbdev, trace_msg);
start = (start + 1) & KBASE_TRACE_MASK;
}
dev_dbg(kbdev->dev, "TRACE_END");
spin_unlock_irqrestore(&kbdev->trace_lock, flags);
KBASE_TRACE_CLEAR(kbdev);
}
static void kbasep_trace_hook_wrapper(void *param)
{
struct kbase_device *kbdev = (struct kbase_device *)param;
kbasep_trace_dump(kbdev);
}
#ifdef CONFIG_DEBUG_FS
struct trace_seq_state {
struct kbase_trace trace_buf[KBASE_TRACE_SIZE];
u32 start;
u32 end;
};
static void *kbasep_trace_seq_start(struct seq_file *s, loff_t *pos)
{
struct trace_seq_state *state = s->private;
int i;
if (*pos > KBASE_TRACE_SIZE)
return NULL;
i = state->start + *pos;
if ((state->end >= state->start && i >= state->end) ||
i >= state->end + KBASE_TRACE_SIZE)
return NULL;
i &= KBASE_TRACE_MASK;
return &state->trace_buf[i];
}
static void kbasep_trace_seq_stop(struct seq_file *s, void *data)
{
}
static void *kbasep_trace_seq_next(struct seq_file *s, void *data, loff_t *pos)
{
struct trace_seq_state *state = s->private;
int i;
(*pos)++;
i = (state->start + *pos) & KBASE_TRACE_MASK;
if (i == state->end)
return NULL;
return &state->trace_buf[i];
}
static int kbasep_trace_seq_show(struct seq_file *s, void *data)
{
struct kbase_trace *trace_msg = data;
char buffer[DEBUG_MESSAGE_SIZE];
kbasep_trace_format_msg(trace_msg, buffer, DEBUG_MESSAGE_SIZE);
seq_printf(s, "%s\n", buffer);
return 0;
}
static const struct seq_operations kbasep_trace_seq_ops = {
.start = kbasep_trace_seq_start,
.next = kbasep_trace_seq_next,
.stop = kbasep_trace_seq_stop,
.show = kbasep_trace_seq_show,
};
static int kbasep_trace_debugfs_open(struct inode *inode, struct file *file)
{
struct kbase_device *kbdev = inode->i_private;
unsigned long flags;
struct trace_seq_state *state;
state = __seq_open_private(file, &kbasep_trace_seq_ops, sizeof(*state));
if (!state)
return -ENOMEM;
spin_lock_irqsave(&kbdev->trace_lock, flags);
state->start = kbdev->trace_first_out;
state->end = kbdev->trace_next_in;
memcpy(state->trace_buf, kbdev->trace_rbuf, sizeof(state->trace_buf));
spin_unlock_irqrestore(&kbdev->trace_lock, flags);
return 0;
}
/* MALI_SEC_INTEGRATION */
//static
const struct file_operations kbasep_trace_debugfs_fops = {
.open = kbasep_trace_debugfs_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release_private,
};
void kbasep_trace_debugfs_init(struct kbase_device *kbdev)
{
/* MALI_SEC_INTEGRATION */
#ifndef CONFIG_MALI_EXYNOS_TRACE
debugfs_create_file("mali_trace", S_IRUGO,
kbdev->mali_debugfs_directory, kbdev,
&kbasep_trace_debugfs_fops);
#endif
}
#else
void kbasep_trace_debugfs_init(struct kbase_device *kbdev)
{
}
#endif /* CONFIG_DEBUG_FS */
#else /* KBASE_TRACE_ENABLE */
static int kbasep_trace_init(struct kbase_device *kbdev)
{
CSTD_UNUSED(kbdev);
return 0;
}
static void kbasep_trace_term(struct kbase_device *kbdev)
{
CSTD_UNUSED(kbdev);
}
static void kbasep_trace_hook_wrapper(void *param)
{
CSTD_UNUSED(param);
}
void kbasep_trace_dump(struct kbase_device *kbdev)
{
CSTD_UNUSED(kbdev);
}
#endif /* KBASE_TRACE_ENABLE */
void kbase_set_profiling_control(struct kbase_device *kbdev, u32 control, u32 value)
{
switch (control) {
case FBDUMP_CONTROL_ENABLE:
/* fall through */
case FBDUMP_CONTROL_RATE:
/* fall through */
case SW_COUNTER_ENABLE:
/* fall through */
case FBDUMP_CONTROL_RESIZE_FACTOR:
kbdev->kbase_profiling_controls[control] = value;
break;
default:
dev_err(kbdev->dev, "Profiling control %d not found\n", control);
break;
}
}
u32 kbase_get_profiling_control(struct kbase_device *kbdev, u32 control)
{
u32 ret_value = 0;
switch (control) {
case FBDUMP_CONTROL_ENABLE:
/* fall through */
case FBDUMP_CONTROL_RATE:
/* fall through */
case SW_COUNTER_ENABLE:
/* fall through */
case FBDUMP_CONTROL_RESIZE_FACTOR:
ret_value = kbdev->kbase_profiling_controls[control];
break;
default:
dev_err(kbdev->dev, "Profiling control %d not found\n", control);
break;
}
return ret_value;
}
/*
* Called by gator to control the production of
* profiling information at runtime
* */
void _mali_profiling_control(u32 action, u32 value)
{
struct kbase_device *kbdev = NULL;
/* find the first i.e. call with -1 */
kbdev = kbase_find_device(-1);
if (NULL != kbdev)
kbase_set_profiling_control(kbdev, action, value);
}
KBASE_EXPORT_SYMBOL(_mali_profiling_control);

View file

@ -0,0 +1,76 @@
/*
*
* (C) COPYRIGHT 2014 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Base kernel disjoint events helper functions
*/
#include <mali_kbase.h>
void kbase_disjoint_init(struct kbase_device *kbdev)
{
KBASE_DEBUG_ASSERT(kbdev != NULL);
atomic_set(&kbdev->disjoint_event.count, 0);
atomic_set(&kbdev->disjoint_event.state, 0);
}
/* increment the disjoint event count */
void kbase_disjoint_event(struct kbase_device *kbdev)
{
KBASE_DEBUG_ASSERT(kbdev != NULL);
atomic_inc(&kbdev->disjoint_event.count);
}
/* increment the state and the event counter */
void kbase_disjoint_state_up(struct kbase_device *kbdev)
{
KBASE_DEBUG_ASSERT(kbdev != NULL);
atomic_inc(&kbdev->disjoint_event.state);
kbase_disjoint_event(kbdev);
}
/* decrement the state */
void kbase_disjoint_state_down(struct kbase_device *kbdev)
{
KBASE_DEBUG_ASSERT(kbdev != NULL);
KBASE_DEBUG_ASSERT(atomic_read(&kbdev->disjoint_event.state) > 0);
kbase_disjoint_event(kbdev);
atomic_dec(&kbdev->disjoint_event.state);
}
/* increments the count only if the state is > 0 */
void kbase_disjoint_event_potential(struct kbase_device *kbdev)
{
KBASE_DEBUG_ASSERT(kbdev != NULL);
if (atomic_read(&kbdev->disjoint_event.state))
kbase_disjoint_event(kbdev);
}
u32 kbase_disjoint_event_get(struct kbase_device *kbdev)
{
KBASE_DEBUG_ASSERT(kbdev != NULL);
return atomic_read(&kbdev->disjoint_event.count);
}
KBASE_EXPORT_TEST_API(kbase_disjoint_event_get);

View file

@ -0,0 +1,272 @@
/*
*
* (C) COPYRIGHT 2010-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include <mali_kbase.h>
#include <mali_kbase_debug.h>
#ifdef CONFIG_DEBUG_LOCK_ALLOC
#include <lockdep.h>
#endif
#if defined(CONFIG_MALI_MIPE_ENABLED)
#include <mali_kbase_tlstream.h>
#endif
/* MALI_SEC_INTEGRATION */
static bool kbase_event_check_error(struct kbase_context *kctx, struct kbase_jd_atom *katom, base_jd_udata *data)
{
pgd_t *pgd;
struct mm_struct *mm;
memset(data->blob, 0, sizeof(data->blob));
if (!kctx || !katom) {
printk("kctx: 0x%p, katom: 0x%p\n", kctx, katom);
return false;
}
if (katom->status != KBASE_JD_ATOM_STATE_COMPLETED) {
printk("Abnormal situation\n");
printk("kctx: 0x%p, katom: 0x%p, katom->status: 0x%x\n", kctx, katom, katom->status);
return false;
}
mm = katom->kctx->process_mm;
pgd = pgd_offset(mm, (unsigned long)&katom->completed);
if (pgd_none(*pgd) || pgd_bad(*pgd)) {
printk("Abnormal katom\n");
printk("katom->kctx: 0x%p, katom->kctx->tgid: %d, katom->kctx->process_mm: 0x%p, pgd: 0x%px\n", katom->kctx, katom->kctx->tgid, katom->kctx->process_mm, pgd);
return false;
}
#ifdef CONFIG_DEBUG_LOCK_ALLOC
if (katom->completed.lock.dep_map.key) {
pgd = pgd_offset(mm, (unsigned long)&katom->completed.lock.dep_map.key);
if (pgd_none(*pgd) || pgd_bad(*pgd)) {
printk("Abnormal katom 2\n");
printk("katom->kctx: 0x%p, katom->kctx->tgid: %d, katom->kctx->process_mm: 0x%p, pgd: 0x%px\n", katom->kctx, katom->kctx->tgid, katom->kctx->process_mm, pgd);
return false;
}
}
#endif
return true;
}
static struct base_jd_udata kbase_event_process(struct kbase_context *kctx, struct kbase_jd_atom *katom)
{
struct base_jd_udata data;
lockdep_assert_held(&kctx->jctx.lock);
KBASE_DEBUG_ASSERT(kctx != NULL);
KBASE_DEBUG_ASSERT(katom != NULL);
KBASE_DEBUG_ASSERT(katom->status == KBASE_JD_ATOM_STATE_COMPLETED);
/* MALI_SEC_INTEGRATION */
if(kbase_event_check_error(kctx, katom, &data) == false)
return data;
data = katom->udata;
KBASE_TIMELINE_ATOMS_IN_FLIGHT(kctx, atomic_sub_return(1, &kctx->timeline.jd_atoms_in_flight));
#if defined(CONFIG_MALI_MIPE_ENABLED)
kbase_tlstream_tl_nret_atom_ctx(katom, kctx);
kbase_tlstream_tl_del_atom(katom);
#endif
katom->status = KBASE_JD_ATOM_STATE_UNUSED;
wake_up(&katom->completed);
return data;
}
int kbase_event_pending(struct kbase_context *ctx)
{
int ret;
KBASE_DEBUG_ASSERT(ctx);
mutex_lock(&ctx->event_mutex);
ret = (!list_empty(&ctx->event_list)) || (true == ctx->event_closed);
mutex_unlock(&ctx->event_mutex);
return ret;
}
KBASE_EXPORT_TEST_API(kbase_event_pending);
int kbase_event_dequeue(struct kbase_context *ctx, struct base_jd_event_v2 *uevent)
{
struct kbase_jd_atom *atom;
KBASE_DEBUG_ASSERT(ctx);
mutex_lock(&ctx->event_mutex);
if (list_empty(&ctx->event_list)) {
if (!ctx->event_closed) {
mutex_unlock(&ctx->event_mutex);
return -1;
}
/* generate the BASE_JD_EVENT_DRV_TERMINATED message on the fly */
mutex_unlock(&ctx->event_mutex);
uevent->event_code = BASE_JD_EVENT_DRV_TERMINATED;
memset(&uevent->udata, 0, sizeof(uevent->udata));
dev_dbg(ctx->kbdev->dev,
"event system closed, returning BASE_JD_EVENT_DRV_TERMINATED(0x%X)\n",
BASE_JD_EVENT_DRV_TERMINATED);
return 0;
}
/* normal event processing */
atom = list_entry(ctx->event_list.next, struct kbase_jd_atom, dep_item[0]);
list_del(ctx->event_list.next);
mutex_unlock(&ctx->event_mutex);
dev_dbg(ctx->kbdev->dev, "event dequeuing %p\n", (void *)atom);
uevent->event_code = atom->event_code;
uevent->atom_number = (atom - ctx->jctx.atoms);
if (atom->core_req & BASE_JD_REQ_EXTERNAL_RESOURCES)
kbase_jd_free_external_resources(atom);
mutex_lock(&ctx->jctx.lock);
uevent->udata = kbase_event_process(ctx, atom);
mutex_unlock(&ctx->jctx.lock);
return 0;
}
KBASE_EXPORT_TEST_API(kbase_event_dequeue);
/**
* kbase_event_process_noreport_worker - Worker for processing atoms that do not
* return an event but do have external
* resources
* @data: Work structure
*/
static void kbase_event_process_noreport_worker(struct work_struct *data)
{
struct kbase_jd_atom *katom = container_of(data, struct kbase_jd_atom,
work);
struct kbase_context *kctx = katom->kctx;
if (katom->core_req & BASE_JD_REQ_EXTERNAL_RESOURCES)
kbase_jd_free_external_resources(katom);
mutex_lock(&kctx->jctx.lock);
kbase_event_process(kctx, katom);
mutex_unlock(&kctx->jctx.lock);
}
/**
* kbase_event_process_noreport - Process atoms that do not return an event
* @kctx: Context pointer
* @katom: Atom to be processed
*
* Atoms that do not have external resources will be processed immediately.
* Atoms that do have external resources will be processed on a workqueue, in
* order to avoid locking issues.
*/
static void kbase_event_process_noreport(struct kbase_context *kctx,
struct kbase_jd_atom *katom)
{
if (katom->core_req & BASE_JD_REQ_EXTERNAL_RESOURCES) {
INIT_WORK(&katom->work, kbase_event_process_noreport_worker);
queue_work(kctx->event_workq, &katom->work);
} else {
kbase_event_process(kctx, katom);
}
}
void kbase_event_post(struct kbase_context *ctx, struct kbase_jd_atom *atom)
{
if (atom->core_req & BASE_JD_REQ_EVENT_ONLY_ON_FAILURE) {
if (atom->event_code == BASE_JD_EVENT_DONE) {
/* Don't report the event */
kbase_event_process_noreport(ctx, atom);
return;
}
}
if (atom->core_req & BASEP_JD_REQ_EVENT_NEVER) {
/* Don't report the event */
kbase_event_process_noreport(ctx, atom);
return;
}
mutex_lock(&ctx->event_mutex);
list_add_tail(&atom->dep_item[0], &ctx->event_list);
mutex_unlock(&ctx->event_mutex);
kbase_event_wakeup(ctx);
}
KBASE_EXPORT_TEST_API(kbase_event_post);
void kbase_event_close(struct kbase_context *kctx)
{
mutex_lock(&kctx->event_mutex);
kctx->event_closed = true;
mutex_unlock(&kctx->event_mutex);
kbase_event_wakeup(kctx);
}
int kbase_event_init(struct kbase_context *kctx)
{
KBASE_DEBUG_ASSERT(kctx);
INIT_LIST_HEAD(&kctx->event_list);
mutex_init(&kctx->event_mutex);
kctx->event_closed = false;
kctx->event_workq = alloc_workqueue("kbase_event", WQ_MEM_RECLAIM, 1);
if (NULL == kctx->event_workq)
return -EINVAL;
return 0;
}
KBASE_EXPORT_TEST_API(kbase_event_init);
void kbase_event_cleanup(struct kbase_context *kctx)
{
KBASE_DEBUG_ASSERT(kctx);
KBASE_DEBUG_ASSERT(kctx->event_workq);
flush_workqueue(kctx->event_workq);
destroy_workqueue(kctx->event_workq);
/* We use kbase_event_dequeue to remove the remaining events as that
* deals with all the cleanup needed for the atoms.
*
* Note: use of kctx->event_list without a lock is safe because this must be the last
* thread using it (because we're about to terminate the lock)
*/
while (!list_empty(&kctx->event_list)) {
struct base_jd_event_v2 event;
kbase_event_dequeue(kctx, &event);
}
}
KBASE_EXPORT_TEST_API(kbase_event_cleanup);

View file

@ -0,0 +1,45 @@
/*
*
* (C) COPYRIGHT 2011-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/* NB taken from gator */
/*
* List of possible actions to be controlled by DS-5 Streamline.
* The following numbers are used by gator to control the frame buffer dumping
* and s/w counter reporting. We cannot use the enums in mali_uk_types.h because
* they are unknown inside gator.
*/
#ifndef _KBASE_GATOR_H_
#define _KBASE_GATOR_H_
#ifdef CONFIG_MALI_GATOR_SUPPORT
#define GATOR_MAKE_EVENT(type, number) (((type) << 24) | ((number) << 16))
#define GATOR_JOB_SLOT_START 1
#define GATOR_JOB_SLOT_STOP 2
#define GATOR_JOB_SLOT_SOFT_STOPPED 3
void kbase_trace_mali_job_slots_event(u32 event, const struct kbase_context *kctx, u8 atom_id);
void kbase_trace_mali_pm_status(u32 event, u64 value);
void kbase_trace_mali_pm_power_off(u32 event, u64 value);
void kbase_trace_mali_pm_power_on(u32 event, u64 value);
void kbase_trace_mali_page_fault_insert_pages(int event, u32 value);
void kbase_trace_mali_mmu_as_in_use(int event);
void kbase_trace_mali_mmu_as_released(int event);
void kbase_trace_mali_total_alloc_pages_change(long long int event);
#endif /* CONFIG_MALI_GATOR_SUPPORT */
#endif /* _KBASE_GATOR_H_ */

View file

@ -0,0 +1,322 @@
/*
*
* (C) COPYRIGHT 2014-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include "mali_kbase.h"
#include "mali_kbase_hw.h"
#include "mali_kbase_mem_linux.h"
#include "mali_kbase_gator_api.h"
#include "mali_kbase_gator_hwcnt_names.h"
#include "mali_kbase_instr.h"
#define MALI_MAX_CORES_PER_GROUP 4
#define MALI_MAX_NUM_BLOCKS_PER_GROUP 8
#define MALI_COUNTERS_PER_BLOCK 64
#define MALI_BYTES_PER_COUNTER 4
struct kbase_gator_hwcnt_handles {
struct kbase_device *kbdev;
struct kbase_context *kctx;
u64 hwcnt_gpu_va;
void *hwcnt_cpu_va;
struct kbase_vmap_struct hwcnt_map;
};
const char * const *kbase_gator_hwcnt_init_names(uint32_t *total_counters)
{
uint32_t gpu_id;
const char * const *hardware_counters;
struct kbase_device *kbdev;
if (!total_counters)
return NULL;
/* Get the first device - it doesn't matter in this case */
kbdev = kbase_find_device(-1);
if (!kbdev)
return NULL;
gpu_id = kbdev->gpu_props.props.core_props.product_id;
switch (gpu_id) {
/* If we are using a Mali-T60x device */
case GPU_ID_PI_T60X:
hardware_counters = hardware_counters_mali_t60x;
*total_counters = ARRAY_SIZE(hardware_counters_mali_t60x);
break;
/* If we are using a Mali-T62x device */
case GPU_ID_PI_T62X:
hardware_counters = hardware_counters_mali_t62x;
*total_counters = ARRAY_SIZE(hardware_counters_mali_t62x);
break;
/* If we are using a Mali-T72x device */
case GPU_ID_PI_T72X:
hardware_counters = hardware_counters_mali_t72x;
*total_counters = ARRAY_SIZE(hardware_counters_mali_t72x);
break;
/* If we are using a Mali-T76x device */
case GPU_ID_PI_T76X:
hardware_counters = hardware_counters_mali_t76x;
*total_counters = ARRAY_SIZE(hardware_counters_mali_t76x);
break;
/* If we are using a Mali-T82x device */
case GPU_ID_PI_T82X:
hardware_counters = hardware_counters_mali_t82x;
*total_counters = ARRAY_SIZE(hardware_counters_mali_t82x);
break;
/* If we are using a Mali-T83x device */
case GPU_ID_PI_T83X:
hardware_counters = hardware_counters_mali_t83x;
*total_counters = ARRAY_SIZE(hardware_counters_mali_t83x);
break;
/* If we are using a Mali-T86x device */
case GPU_ID_PI_T86X:
hardware_counters = hardware_counters_mali_t86x;
*total_counters = ARRAY_SIZE(hardware_counters_mali_t86x);
break;
/* If we are using a Mali-T88x device */
case GPU_ID_PI_TFRX:
hardware_counters = hardware_counters_mali_t88x;
*total_counters = ARRAY_SIZE(hardware_counters_mali_t88x);
break;
default:
hardware_counters = NULL;
*total_counters = 0;
dev_err(kbdev->dev, "Unrecognized gpu ID: %u\n", gpu_id);
break;
}
/* Release the kbdev reference. */
kbase_release_device(kbdev);
/* If we return a string array take a reference on the module (or fail). */
if (hardware_counters && !try_module_get(THIS_MODULE))
return NULL;
return hardware_counters;
}
KBASE_EXPORT_SYMBOL(kbase_gator_hwcnt_init_names);
void kbase_gator_hwcnt_term_names(void)
{
/* Release the module reference. */
module_put(THIS_MODULE);
}
KBASE_EXPORT_SYMBOL(kbase_gator_hwcnt_term_names);
struct kbase_gator_hwcnt_handles *kbase_gator_hwcnt_init(struct kbase_gator_hwcnt_info *in_out_info)
{
struct kbase_gator_hwcnt_handles *hand;
struct kbase_uk_hwcnt_setup setup;
int err;
uint32_t dump_size = 0, i = 0;
struct kbase_va_region *reg;
u64 flags;
u64 nr_pages;
u16 va_alignment = 0;
if (!in_out_info)
return NULL;
hand = kzalloc(sizeof(*hand), GFP_KERNEL);
if (!hand)
return NULL;
/* Get the first device */
hand->kbdev = kbase_find_device(-1);
if (!hand->kbdev)
goto free_hand;
/* Create a kbase_context */
hand->kctx = kbase_create_context(hand->kbdev, true);
if (!hand->kctx)
goto release_device;
in_out_info->nr_cores = hand->kbdev->gpu_props.num_cores;
in_out_info->nr_core_groups = hand->kbdev->gpu_props.num_core_groups;
in_out_info->gpu_id = hand->kbdev->gpu_props.props.core_props.product_id;
/* If we are using a v4 device (Mali-T6xx or Mali-T72x) */
if (kbase_hw_has_feature(hand->kbdev, BASE_HW_FEATURE_V4)) {
uint32_t cg, j;
uint64_t core_mask;
/* There are 8 hardware counters blocks per core group */
in_out_info->hwc_layout = kmalloc(sizeof(enum hwc_type) *
MALI_MAX_NUM_BLOCKS_PER_GROUP *
in_out_info->nr_core_groups, GFP_KERNEL);
if (!in_out_info->hwc_layout)
goto destroy_context;
dump_size = in_out_info->nr_core_groups *
MALI_MAX_NUM_BLOCKS_PER_GROUP *
MALI_COUNTERS_PER_BLOCK *
MALI_BYTES_PER_COUNTER;
for (cg = 0; cg < in_out_info->nr_core_groups; cg++) {
core_mask = hand->kbdev->gpu_props.props.coherency_info.group[cg].core_mask;
for (j = 0; j < MALI_MAX_CORES_PER_GROUP; j++) {
if (core_mask & (1u << j))
in_out_info->hwc_layout[i++] = SHADER_BLOCK;
else
in_out_info->hwc_layout[i++] = RESERVED_BLOCK;
}
in_out_info->hwc_layout[i++] = TILER_BLOCK;
in_out_info->hwc_layout[i++] = MMU_L2_BLOCK;
in_out_info->hwc_layout[i++] = RESERVED_BLOCK;
if (0 == cg)
in_out_info->hwc_layout[i++] = JM_BLOCK;
else
in_out_info->hwc_layout[i++] = RESERVED_BLOCK;
}
/* If we are using any other device */
} else {
uint32_t nr_l2, nr_sc, j;
uint64_t core_mask;
nr_l2 = hand->kbdev->gpu_props.props.l2_props.num_l2_slices;
core_mask = hand->kbdev->gpu_props.props.coherency_info.group[0].core_mask;
nr_sc = hand->kbdev->gpu_props.props.coherency_info.group[0].num_cores;
/* The job manager and tiler sets of counters
* are always present */
in_out_info->hwc_layout = kmalloc(sizeof(enum hwc_type) * (2 + nr_sc + nr_l2), GFP_KERNEL);
if (!in_out_info->hwc_layout)
goto destroy_context;
dump_size = (2 + nr_sc + nr_l2) * MALI_COUNTERS_PER_BLOCK * MALI_BYTES_PER_COUNTER;
in_out_info->hwc_layout[i++] = JM_BLOCK;
in_out_info->hwc_layout[i++] = TILER_BLOCK;
for (j = 0; j < nr_l2; j++)
in_out_info->hwc_layout[i++] = MMU_L2_BLOCK;
while (core_mask != 0ull) {
if ((core_mask & 1ull) != 0ull)
in_out_info->hwc_layout[i++] = SHADER_BLOCK;
else
in_out_info->hwc_layout[i++] = RESERVED_BLOCK;
core_mask >>= 1;
}
}
in_out_info->nr_hwc_blocks = i;
in_out_info->size = dump_size;
flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_CPU_WR | BASE_MEM_PROT_GPU_WR;
nr_pages = PFN_UP(dump_size);
reg = kbase_mem_alloc(hand->kctx, nr_pages, nr_pages, 0,
&flags, &hand->hwcnt_gpu_va, &va_alignment);
if (!reg)
goto free_layout;
hand->hwcnt_cpu_va = kbase_vmap(hand->kctx, hand->hwcnt_gpu_va,
dump_size, &hand->hwcnt_map);
if (!hand->hwcnt_cpu_va)
goto free_buffer;
in_out_info->kernel_dump_buffer = hand->hwcnt_cpu_va;
memset(in_out_info->kernel_dump_buffer, 0, nr_pages * PAGE_SIZE);
/*setup.dump_buffer = (uintptr_t)in_out_info->kernel_dump_buffer;*/
setup.dump_buffer = hand->hwcnt_gpu_va;
setup.jm_bm = in_out_info->bitmask[0];
setup.tiler_bm = in_out_info->bitmask[1];
setup.shader_bm = in_out_info->bitmask[2];
setup.mmu_l2_bm = in_out_info->bitmask[3];
err = kbase_instr_hwcnt_enable(hand->kctx, &setup);
if (err)
goto free_unmap;
kbase_instr_hwcnt_clear(hand->kctx);
return hand;
free_unmap:
kbase_vunmap(hand->kctx, &hand->hwcnt_map);
free_buffer:
kbase_mem_free(hand->kctx, hand->hwcnt_gpu_va);
free_layout:
kfree(in_out_info->hwc_layout);
destroy_context:
kbase_destroy_context(hand->kctx);
release_device:
kbase_release_device(hand->kbdev);
free_hand:
kfree(hand);
return NULL;
}
KBASE_EXPORT_SYMBOL(kbase_gator_hwcnt_init);
void kbase_gator_hwcnt_term(struct kbase_gator_hwcnt_info *in_out_info, struct kbase_gator_hwcnt_handles *opaque_handles)
{
if (in_out_info)
kfree(in_out_info->hwc_layout);
if (opaque_handles) {
kbase_instr_hwcnt_disable(opaque_handles->kctx);
kbase_vunmap(opaque_handles->kctx, &opaque_handles->hwcnt_map);
kbase_mem_free(opaque_handles->kctx, opaque_handles->hwcnt_gpu_va);
kbase_destroy_context(opaque_handles->kctx);
kbase_release_device(opaque_handles->kbdev);
kfree(opaque_handles);
}
}
KBASE_EXPORT_SYMBOL(kbase_gator_hwcnt_term);
uint32_t kbase_gator_instr_hwcnt_dump_complete(
struct kbase_gator_hwcnt_handles *opaque_handles,
uint32_t * const success)
{
bool ret_res, success_res;
if (opaque_handles && success) {
ret_res = kbase_instr_hwcnt_dump_complete(opaque_handles->kctx,
&success_res);
*success = (uint32_t)success_res;
return (uint32_t)(ret_res != 0);
}
return 0;
}
KBASE_EXPORT_SYMBOL(kbase_gator_instr_hwcnt_dump_complete);
uint32_t kbase_gator_instr_hwcnt_dump_irq(struct kbase_gator_hwcnt_handles *opaque_handles)
{
if (opaque_handles)
return (kbase_instr_hwcnt_request_dump(
opaque_handles->kctx) == 0);
return 0;
}
KBASE_EXPORT_SYMBOL(kbase_gator_instr_hwcnt_dump_irq);

View file

@ -0,0 +1,219 @@
/*
*
* (C) COPYRIGHT 2014-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#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 r1px, r2px
* Version 2 API: DDK versions r3px, r4px
* Version 3 API: DDK version r5p0 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];
*
* printk( "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 3
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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,99 @@
/*
*
* (C) COPYRIGHT 2012-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include <mali_kbase_gpu_memory_debugfs.h>
#ifdef CONFIG_DEBUG_FS
/** Show callback for the @c gpu_memory debugfs file.
*
* This function is called to get the contents of the @c gpu_memory debugfs
* file. This is a report of current gpu memory usage.
*
* @param sfile The debugfs entry
* @param data Data associated with the entry
*
* @return 0 if successfully prints data in debugfs entry file
* -1 if it encountered an error
*/
extern int gpu_memory_seq_show(struct seq_file *sfile, void *data);
#ifdef MALI_SEC_INTEGRATION
static int kbasep_gpu_memory_seq_show(struct seq_file *sfile, void *data)
{
ssize_t ret = 0;
struct list_head *entry;
const struct list_head *kbdev_list;
kbdev_list = kbase_dev_list_get();
list_for_each(entry, kbdev_list) {
struct kbase_device *kbdev = NULL;
struct kbasep_kctx_list_element *element;
kbdev = list_entry(entry, struct kbase_device, entry);
/* output the total memory usage and cap for this device */
ret = seq_printf(sfile, "%-16s %10u\n",
kbdev->devname,
atomic_read(&(kbdev->memdev.used_pages)));
mutex_lock(&kbdev->kctx_list_lock);
list_for_each_entry(element, &kbdev->kctx_list, link) {
/* output the memory usage and cap for each kctx
* opened on this device */
ret = seq_printf(sfile, " %s-0x%p %10u\n",
"kctx",
element->kctx,
atomic_read(&(element->kctx->used_pages)));
}
mutex_unlock(&kbdev->kctx_list_lock);
}
kbase_dev_list_put(kbdev_list);
return ret;
}
#endif
/*
* File operations related to debugfs entry for gpu_memory
*/
static int kbasep_gpu_memory_debugfs_open(struct inode *in, struct file *file)
{
return single_open(file, gpu_memory_seq_show , NULL);
}
static const struct file_operations kbasep_gpu_memory_debugfs_fops = {
.open = kbasep_gpu_memory_debugfs_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/*
* Initialize debugfs entry for gpu_memory
*/
void kbasep_gpu_memory_debugfs_init(struct kbase_device *kbdev)
{
debugfs_create_file("gpu_memory", S_IRUGO,
kbdev->mali_debugfs_directory, NULL,
&kbasep_gpu_memory_debugfs_fops);
return;
}
#else
/*
* Stub functions for when debugfs is disabled
*/
void kbasep_gpu_memory_debugfs_init(struct kbase_device *kbdev)
{
return;
}
#endif

View file

@ -0,0 +1,38 @@
/*
*
* (C) COPYRIGHT 2012-2014 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/**
* @file mali_kbase_gpu_memory_debugfs.h
* Header file for gpu_memory entry in debugfs
*
*/
#ifndef _KBASE_GPU_MEMORY_H
#define _KBASE_GPU_MEMORY_H
#include <mali_kbase.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
/**
* @brief Initialize gpu_memory debugfs entry
*/
void kbasep_gpu_memory_debugfs_init(struct kbase_device *kbdev);
#endif /*_KBASE_GPU_MEMORY_H*/

View file

@ -0,0 +1,299 @@
/*
*
* (C) COPYRIGHT 2011-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Base kernel property query APIs
*/
#include <mali_kbase.h>
#include <mali_midg_regmap.h>
#include <mali_kbase_gpuprops.h>
#include <mali_kbase_config_defaults.h>
#include <mali_kbase_hwaccess_gpuprops.h>
#include <linux/clk.h>
/**
* KBASE_UBFX32 - Extracts bits from a 32-bit bitfield.
* @value: The value from which to extract bits.
* @offset: The first bit to extract (0 being the LSB).
* @size: The number of bits to extract.
*
* Context: @offset + @size <= 32.
*
* Return: Bits [@offset, @offset + @size) from @value.
*/
/* from mali_cdsb.h */
#define KBASE_UBFX32(value, offset, size) \
(((u32)(value) >> (u32)(offset)) & (u32)((1ULL << (u32)(size)) - 1))
int kbase_gpuprops_uk_get_props(struct kbase_context *kctx, struct kbase_uk_gpuprops * const kbase_props)
{
kbase_gpu_clk_speed_func get_gpu_speed_mhz;
u32 gpu_speed_mhz;
int rc = 1;
KBASE_DEBUG_ASSERT(NULL != kctx);
KBASE_DEBUG_ASSERT(NULL != kbase_props);
/* Current GPU speed is requested from the system integrator via the GPU_SPEED_FUNC function.
* If that function fails, or the function is not provided by the system integrator, we report the maximum
* GPU speed as specified by GPU_FREQ_KHZ_MAX.
*/
get_gpu_speed_mhz = (kbase_gpu_clk_speed_func) GPU_SPEED_FUNC;
if (get_gpu_speed_mhz != NULL) {
rc = get_gpu_speed_mhz(&gpu_speed_mhz);
#ifdef CONFIG_MALI_DEBUG
/* Issue a warning message when the reported GPU speed falls outside the min/max range */
if (rc == 0) {
u32 gpu_speed_khz = gpu_speed_mhz * 1000;
if (gpu_speed_khz < kctx->kbdev->gpu_props.props.core_props.gpu_freq_khz_min ||
gpu_speed_khz > kctx->kbdev->gpu_props.props.core_props.gpu_freq_khz_max)
dev_warn(kctx->kbdev->dev, "GPU Speed is outside of min/max range (got %lu Khz, min %lu Khz, max %lu Khz)\n",
(unsigned long)gpu_speed_khz,
(unsigned long)kctx->kbdev->gpu_props.props.core_props.gpu_freq_khz_min,
(unsigned long)kctx->kbdev->gpu_props.props.core_props.gpu_freq_khz_max);
}
#endif /* CONFIG_MALI_DEBUG */
}
if (kctx->kbdev->clock) {
gpu_speed_mhz = clk_get_rate(kctx->kbdev->clock) / 1000000;
rc = 0;
}
if (rc != 0)
gpu_speed_mhz = kctx->kbdev->gpu_props.props.core_props.gpu_freq_khz_max / 1000;
kctx->kbdev->gpu_props.props.core_props.gpu_speed_mhz = gpu_speed_mhz;
memcpy(&kbase_props->props, &kctx->kbdev->gpu_props.props, sizeof(kbase_props->props));
/* Before API 8.2 they expect L3 cache info here, which was always 0 */
if (kctx->api_version < KBASE_API_VERSION(8, 2))
kbase_props->props.raw_props.suspend_size = 0;
return 0;
}
static void kbase_gpuprops_construct_coherent_groups(base_gpu_props * const props)
{
struct mali_base_gpu_coherent_group *current_group;
u64 group_present;
u64 group_mask;
u64 first_set, first_set_prev;
u32 num_groups = 0;
KBASE_DEBUG_ASSERT(NULL != props);
props->coherency_info.coherency = props->raw_props.mem_features;
props->coherency_info.num_core_groups = hweight64(props->raw_props.l2_present);
if (props->coherency_info.coherency & GROUPS_L2_COHERENT) {
/* Group is l2 coherent */
group_present = props->raw_props.l2_present;
} else {
/* Group is l1 coherent */
group_present = props->raw_props.shader_present;
}
/*
* The coherent group mask can be computed from the l2 present
* register.
*
* For the coherent group n:
* group_mask[n] = (first_set[n] - 1) & ~(first_set[n-1] - 1)
* where first_set is group_present with only its nth set-bit kept
* (i.e. the position from where a new group starts).
*
* For instance if the groups are l2 coherent and l2_present=0x0..01111:
* The first mask is:
* group_mask[1] = (first_set[1] - 1) & ~(first_set[0] - 1)
* = (0x0..010 - 1) & ~(0x0..01 - 1)
* = 0x0..00f
* The second mask is:
* group_mask[2] = (first_set[2] - 1) & ~(first_set[1] - 1)
* = (0x0..100 - 1) & ~(0x0..010 - 1)
* = 0x0..0f0
* And so on until all the bits from group_present have been cleared
* (i.e. there is no group left).
*/
current_group = props->coherency_info.group;
first_set = group_present & ~(group_present - 1);
while (group_present != 0 && num_groups < BASE_MAX_COHERENT_GROUPS) {
group_present -= first_set; /* Clear the current group bit */
first_set_prev = first_set;
first_set = group_present & ~(group_present - 1);
group_mask = (first_set - 1) & ~(first_set_prev - 1);
/* Populate the coherent_group structure for each group */
current_group->core_mask = group_mask & props->raw_props.shader_present;
current_group->num_cores = hweight64(current_group->core_mask);
num_groups++;
current_group++;
}
if (group_present != 0)
pr_warn("Too many coherent groups (keeping only %d groups).\n", BASE_MAX_COHERENT_GROUPS);
props->coherency_info.num_groups = num_groups;
}
/**
* kbase_gpuprops_get_props - Get the GPU configuration
* @gpu_props: The &base_gpu_props structure
* @kbdev: The &struct kbase_device structure for the device
*
* Fill the &base_gpu_props structure with values from the GPU configuration
* registers. Only the raw properties are filled in this function
*/
static void kbase_gpuprops_get_props(base_gpu_props * const gpu_props, struct kbase_device *kbdev)
{
struct kbase_gpuprops_regdump regdump;
int i;
KBASE_DEBUG_ASSERT(NULL != kbdev);
KBASE_DEBUG_ASSERT(NULL != gpu_props);
/* Dump relevant registers */
kbase_backend_gpuprops_get(kbdev, &regdump);
gpu_props->raw_props.gpu_id = regdump.gpu_id;
gpu_props->raw_props.tiler_features = regdump.tiler_features;
gpu_props->raw_props.mem_features = regdump.mem_features;
gpu_props->raw_props.mmu_features = regdump.mmu_features;
gpu_props->raw_props.l2_features = regdump.l2_features;
gpu_props->raw_props.suspend_size = regdump.suspend_size;
gpu_props->raw_props.as_present = regdump.as_present;
gpu_props->raw_props.js_present = regdump.js_present;
gpu_props->raw_props.shader_present = ((u64) regdump.shader_present_hi << 32) + regdump.shader_present_lo;
gpu_props->raw_props.tiler_present = ((u64) regdump.tiler_present_hi << 32) + regdump.tiler_present_lo;
gpu_props->raw_props.l2_present = ((u64) regdump.l2_present_hi << 32) + regdump.l2_present_lo;
for (i = 0; i < GPU_MAX_JOB_SLOTS; i++)
gpu_props->raw_props.js_features[i] = regdump.js_features[i];
for (i = 0; i < BASE_GPU_NUM_TEXTURE_FEATURES_REGISTERS; i++)
gpu_props->raw_props.texture_features[i] = regdump.texture_features[i];
gpu_props->raw_props.thread_max_barrier_size = regdump.thread_max_barrier_size;
gpu_props->raw_props.thread_max_threads = regdump.thread_max_threads;
gpu_props->raw_props.thread_max_workgroup_size = regdump.thread_max_workgroup_size;
gpu_props->raw_props.thread_features = regdump.thread_features;
}
/**
* kbase_gpuprops_calculate_props - Calculate the derived properties
* @gpu_props: The &base_gpu_props structure
* @kbdev: The &struct kbase_device structure for the device
*
* Fill the &base_gpu_props structure with values derived from the GPU
* configuration registers
*/
static void kbase_gpuprops_calculate_props(base_gpu_props * const gpu_props, struct kbase_device *kbdev)
{
int i;
/* Populate the base_gpu_props structure */
gpu_props->core_props.version_status = KBASE_UBFX32(gpu_props->raw_props.gpu_id, 0U, 4);
gpu_props->core_props.minor_revision = KBASE_UBFX32(gpu_props->raw_props.gpu_id, 4U, 8);
gpu_props->core_props.major_revision = KBASE_UBFX32(gpu_props->raw_props.gpu_id, 12U, 4);
gpu_props->core_props.product_id = KBASE_UBFX32(gpu_props->raw_props.gpu_id, 16U, 16);
gpu_props->core_props.log2_program_counter_size = KBASE_GPU_PC_SIZE_LOG2;
gpu_props->core_props.gpu_available_memory_size = totalram_pages << PAGE_SHIFT;
for (i = 0; i < BASE_GPU_NUM_TEXTURE_FEATURES_REGISTERS; i++)
gpu_props->core_props.texture_features[i] = gpu_props->raw_props.texture_features[i];
gpu_props->l2_props.log2_line_size = KBASE_UBFX32(gpu_props->raw_props.l2_features, 0U, 8);
gpu_props->l2_props.log2_cache_size = KBASE_UBFX32(gpu_props->raw_props.l2_features, 16U, 8);
/* Field with number of l2 slices is added to MEM_FEATURES register
* since t76x. Below code assumes that for older GPU reserved bits will
* be read as zero. */
gpu_props->l2_props.num_l2_slices =
KBASE_UBFX32(gpu_props->raw_props.mem_features, 8U, 4) + 1;
gpu_props->tiler_props.bin_size_bytes = 1 << KBASE_UBFX32(gpu_props->raw_props.tiler_features, 0U, 6);
gpu_props->tiler_props.max_active_levels = KBASE_UBFX32(gpu_props->raw_props.tiler_features, 8U, 4);
if (gpu_props->raw_props.thread_max_threads == 0)
gpu_props->thread_props.max_threads = THREAD_MT_DEFAULT;
else
gpu_props->thread_props.max_threads = gpu_props->raw_props.thread_max_threads;
if (gpu_props->raw_props.thread_max_workgroup_size == 0)
gpu_props->thread_props.max_workgroup_size = THREAD_MWS_DEFAULT;
else
gpu_props->thread_props.max_workgroup_size = gpu_props->raw_props.thread_max_workgroup_size;
if (gpu_props->raw_props.thread_max_barrier_size == 0)
gpu_props->thread_props.max_barrier_size = THREAD_MBS_DEFAULT;
else
gpu_props->thread_props.max_barrier_size = gpu_props->raw_props.thread_max_barrier_size;
gpu_props->thread_props.max_registers = KBASE_UBFX32(gpu_props->raw_props.thread_features, 0U, 16);
gpu_props->thread_props.max_task_queue = KBASE_UBFX32(gpu_props->raw_props.thread_features, 16U, 8);
gpu_props->thread_props.max_thread_group_split = KBASE_UBFX32(gpu_props->raw_props.thread_features, 24U, 6);
gpu_props->thread_props.impl_tech = KBASE_UBFX32(gpu_props->raw_props.thread_features, 30U, 2);
/* If values are not specified, then use defaults */
if (gpu_props->thread_props.max_registers == 0) {
gpu_props->thread_props.max_registers = THREAD_MR_DEFAULT;
gpu_props->thread_props.max_task_queue = THREAD_MTQ_DEFAULT;
gpu_props->thread_props.max_thread_group_split = THREAD_MTGS_DEFAULT;
}
/* Initialize the coherent_group structure for each group */
kbase_gpuprops_construct_coherent_groups(gpu_props);
}
void kbase_gpuprops_set(struct kbase_device *kbdev)
{
struct kbase_gpu_props *gpu_props;
struct gpu_raw_gpu_props *raw;
KBASE_DEBUG_ASSERT(NULL != kbdev);
gpu_props = &kbdev->gpu_props;
raw = &gpu_props->props.raw_props;
/* Initialize the base_gpu_props structure from the hardware */
kbase_gpuprops_get_props(&gpu_props->props, kbdev);
/* Populate the derived properties */
kbase_gpuprops_calculate_props(&gpu_props->props, kbdev);
/* Populate kbase-only fields */
gpu_props->l2_props.associativity = KBASE_UBFX32(raw->l2_features, 8U, 8);
gpu_props->l2_props.external_bus_width = KBASE_UBFX32(raw->l2_features, 24U, 8);
gpu_props->mem.core_group = KBASE_UBFX32(raw->mem_features, 0U, 1);
gpu_props->mmu.va_bits = KBASE_UBFX32(raw->mmu_features, 0U, 8);
gpu_props->mmu.pa_bits = KBASE_UBFX32(raw->mmu_features, 8U, 8);
gpu_props->num_cores = hweight64(raw->shader_present);
gpu_props->num_core_groups = hweight64(raw->l2_present);
gpu_props->num_address_spaces = hweight32(raw->as_present);
gpu_props->num_job_slots = hweight32(raw->js_present);
}

View file

@ -0,0 +1,54 @@
/*
*
* (C) COPYRIGHT 2011-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/**
* @file mali_kbase_gpuprops.h
* Base kernel property query APIs
*/
#ifndef _KBASE_GPUPROPS_H_
#define _KBASE_GPUPROPS_H_
#include "mali_kbase_gpuprops_types.h"
/* Forward definition - see mali_kbase.h */
struct kbase_device;
/**
* @brief Set up Kbase GPU properties.
*
* Set up Kbase GPU properties with information from the GPU registers
*
* @param kbdev The struct kbase_device structure for the device
*/
void kbase_gpuprops_set(struct kbase_device *kbdev);
/**
* @brief Provide GPU properties to userside through UKU call.
*
* Fill the struct kbase_uk_gpuprops with values from GPU configuration registers.
*
* @param kctx The struct kbase_context structure
* @param kbase_props A copy of the struct kbase_uk_gpuprops structure from userspace
*
* @return 0 on success. Any other value indicates failure.
*/
int kbase_gpuprops_uk_get_props(struct kbase_context *kctx, struct kbase_uk_gpuprops * const kbase_props);
#endif /* _KBASE_GPUPROPS_H_ */

View file

@ -0,0 +1,91 @@
/*
*
* (C) COPYRIGHT 2011-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/**
* @file mali_kbase_gpuprops_types.h
* Base kernel property query APIs
*/
#ifndef _KBASE_GPUPROPS_TYPES_H_
#define _KBASE_GPUPROPS_TYPES_H_
#include "mali_base_kernel.h"
#define KBASE_GPU_SPEED_MHZ 123
#define KBASE_GPU_PC_SIZE_LOG2 24U
struct kbase_gpuprops_regdump {
u32 gpu_id;
u32 l2_features;
u32 suspend_size; /* API 8.2+ */
u32 tiler_features;
u32 mem_features;
u32 mmu_features;
u32 as_present;
u32 js_present;
u32 thread_max_threads;
u32 thread_max_workgroup_size;
u32 thread_max_barrier_size;
u32 thread_features;
u32 texture_features[BASE_GPU_NUM_TEXTURE_FEATURES_REGISTERS];
u32 js_features[GPU_MAX_JOB_SLOTS];
u32 shader_present_lo;
u32 shader_present_hi;
u32 tiler_present_lo;
u32 tiler_present_hi;
u32 l2_present_lo;
u32 l2_present_hi;
};
struct kbase_gpu_cache_props {
u8 associativity;
u8 external_bus_width;
};
struct kbase_gpu_mem_props {
u8 core_group;
};
struct kbase_gpu_mmu_props {
u8 va_bits;
u8 pa_bits;
};
struct kbase_gpu_props {
/* kernel-only properties */
u8 num_cores;
u8 num_core_groups;
u8 num_address_spaces;
u8 num_job_slots;
struct kbase_gpu_cache_props l2_props;
struct kbase_gpu_mem_props mem;
struct kbase_gpu_mmu_props mmu;
/**
* Implementation specific irq throttle value (us), should be adjusted during integration.
*/
int irq_throttle_time_us;
/* Properties shared with userspace */
base_gpu_props props;
};
#endif /* _KBASE_GPUPROPS_TYPES_H_ */

View file

@ -0,0 +1,214 @@
/*
*
* (C) COPYRIGHT 2012-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* Run-time work-arounds helpers
*/
#include <mali_base_hwconfig_features.h>
#include <mali_base_hwconfig_issues.h>
#include <mali_midg_regmap.h>
#include "mali_kbase.h"
#include "mali_kbase_hw.h"
void kbase_hw_set_features_mask(struct kbase_device *kbdev)
{
const enum base_hw_feature *features;
u32 gpu_id;
gpu_id = kbdev->gpu_props.props.raw_props.gpu_id;
gpu_id &= GPU_ID_VERSION_PRODUCT_ID;
gpu_id = gpu_id >> GPU_ID_VERSION_PRODUCT_ID_SHIFT;
switch (gpu_id) {
case GPU_ID_PI_TFRX:
/* FALLTHROUGH */
case GPU_ID_PI_T86X:
features = base_hw_features_tFxx;
break;
case GPU_ID_PI_T83X:
features = base_hw_features_t83x;
break;
case GPU_ID_PI_T82X:
features = base_hw_features_t82x;
break;
case GPU_ID_PI_T76X:
features = base_hw_features_t76x;
break;
case GPU_ID_PI_T72X:
features = base_hw_features_t72x;
break;
case GPU_ID_PI_T62X:
features = base_hw_features_t62x;
break;
case GPU_ID_PI_T60X:
features = base_hw_features_t60x;
break;
default:
features = base_hw_features_generic;
break;
}
for (; *features != BASE_HW_FEATURE_END; features++)
set_bit(*features, &kbdev->hw_features_mask[0]);
}
int kbase_hw_set_issues_mask(struct kbase_device *kbdev)
{
const enum base_hw_issue *issues;
u32 gpu_id;
u32 impl_tech;
gpu_id = kbdev->gpu_props.props.raw_props.gpu_id;
impl_tech = kbdev->gpu_props.props.thread_props.impl_tech;
if (impl_tech != IMPLEMENTATION_MODEL) {
switch (gpu_id) {
case GPU_ID_MAKE(GPU_ID_PI_T60X, 0, 0, GPU_ID_S_15DEV0):
issues = base_hw_issues_t60x_r0p0_15dev0;
break;
case GPU_ID_MAKE(GPU_ID_PI_T60X, 0, 0, GPU_ID_S_EAC):
issues = base_hw_issues_t60x_r0p0_eac;
break;
case GPU_ID_MAKE(GPU_ID_PI_T60X, 0, 1, 0):
issues = base_hw_issues_t60x_r0p1;
break;
case GPU_ID_MAKE(GPU_ID_PI_T62X, 0, 1, 0):
issues = base_hw_issues_t62x_r0p1;
break;
case GPU_ID_MAKE(GPU_ID_PI_T62X, 1, 0, 0):
case GPU_ID_MAKE(GPU_ID_PI_T62X, 1, 0, 1):
issues = base_hw_issues_t62x_r1p0;
break;
case GPU_ID_MAKE(GPU_ID_PI_T62X, 1, 1, 0):
issues = base_hw_issues_t62x_r1p1;
break;
case GPU_ID_MAKE(GPU_ID_PI_T76X, 0, 0, 1):
issues = base_hw_issues_t76x_r0p0;
break;
case GPU_ID_MAKE(GPU_ID_PI_T76X, 0, 1, 1):
issues = base_hw_issues_t76x_r0p1;
break;
case GPU_ID_MAKE(GPU_ID_PI_T76X, 0, 1, 9):
issues = base_hw_issues_t76x_r0p1_50rel0;
break;
case GPU_ID_MAKE(GPU_ID_PI_T76X, 0, 2, 1):
issues = base_hw_issues_t76x_r0p2;
break;
case GPU_ID_MAKE(GPU_ID_PI_T76X, 0, 3, 1):
issues = base_hw_issues_t76x_r0p3;
break;
case GPU_ID_MAKE(GPU_ID_PI_T76X, 1, 0, 0):
issues = base_hw_issues_t76x_r1p0;
break;
case GPU_ID_MAKE(GPU_ID_PI_T72X, 0, 0, 0):
case GPU_ID_MAKE(GPU_ID_PI_T72X, 0, 0, 1):
case GPU_ID_MAKE(GPU_ID_PI_T72X, 0, 0, 2):
issues = base_hw_issues_t72x_r0p0;
break;
case GPU_ID_MAKE(GPU_ID_PI_T72X, 1, 0, 0):
issues = base_hw_issues_t72x_r1p0;
break;
case GPU_ID_MAKE(GPU_ID_PI_T72X, 1, 1, 0):
issues = base_hw_issues_t72x_r1p1;
break;
case GPU_ID_MAKE(GPU_ID_PI_TFRX, 0, 1, 2):
issues = base_hw_issues_tFRx_r0p1;
break;
case GPU_ID_MAKE(GPU_ID_PI_TFRX, 0, 2, 0):
issues = base_hw_issues_tFRx_r0p2;
break;
case GPU_ID_MAKE(GPU_ID_PI_TFRX, 1, 0, 0):
case GPU_ID_MAKE(GPU_ID_PI_TFRX, 1, 0, 8):
issues = base_hw_issues_tFRx_r1p0;
break;
case GPU_ID_MAKE(GPU_ID_PI_TFRX, 2, 0, 0):
issues = base_hw_issues_tFRx_r2p0;
break;
case GPU_ID_MAKE(GPU_ID_PI_T86X, 0, 2, 0):
issues = base_hw_issues_t86x_r0p2;
break;
case GPU_ID_MAKE(GPU_ID_PI_T86X, 1, 0, 0):
case GPU_ID_MAKE(GPU_ID_PI_T86X, 1, 0, 8):
issues = base_hw_issues_t86x_r1p0;
break;
case GPU_ID_MAKE(GPU_ID_PI_T86X, 2, 0, 0):
issues = base_hw_issues_t86x_r2p0;
break;
case GPU_ID_MAKE(GPU_ID_PI_T83X, 0, 1, 0):
issues = base_hw_issues_t83x_r0p1;
break;
case GPU_ID_MAKE(GPU_ID_PI_T83X, 1, 0, 0):
case GPU_ID_MAKE(GPU_ID_PI_T83X, 1, 0, 8):
issues = base_hw_issues_t83x_r1p0;
break;
case GPU_ID_MAKE(GPU_ID_PI_T82X, 0, 0, 0):
issues = base_hw_issues_t82x_r0p0;
break;
case GPU_ID_MAKE(GPU_ID_PI_T82X, 0, 1, 0):
issues = base_hw_issues_t82x_r0p1;
break;
case GPU_ID_MAKE(GPU_ID_PI_T82X, 1, 0, 0):
case GPU_ID_MAKE(GPU_ID_PI_T82X, 1, 0, 8):
issues = base_hw_issues_t82x_r1p0;
break;
default:
dev_err(kbdev->dev, "Unknown GPU ID %x", gpu_id);
return -EINVAL;
}
} else {
/* Software model */
switch (gpu_id >> GPU_ID_VERSION_PRODUCT_ID_SHIFT) {
case GPU_ID_PI_T60X:
issues = base_hw_issues_model_t60x;
break;
case GPU_ID_PI_T62X:
issues = base_hw_issues_model_t62x;
break;
case GPU_ID_PI_T72X:
issues = base_hw_issues_model_t72x;
break;
case GPU_ID_PI_T76X:
issues = base_hw_issues_model_t76x;
break;
case GPU_ID_PI_TFRX:
issues = base_hw_issues_model_tFRx;
break;
case GPU_ID_PI_T86X:
issues = base_hw_issues_model_t86x;
break;
case GPU_ID_PI_T83X:
issues = base_hw_issues_model_t83x;
break;
case GPU_ID_PI_T82X:
issues = base_hw_issues_model_t82x;
break;
default:
dev_err(kbdev->dev, "Unknown GPU ID %x", gpu_id);
return -EINVAL;
}
}
dev_info(kbdev->dev, "GPU identified as 0x%04x r%dp%d status %d", (gpu_id & GPU_ID_VERSION_PRODUCT_ID) >> GPU_ID_VERSION_PRODUCT_ID_SHIFT, (gpu_id & GPU_ID_VERSION_MAJOR) >> GPU_ID_VERSION_MAJOR_SHIFT, (gpu_id & GPU_ID_VERSION_MINOR) >> GPU_ID_VERSION_MINOR_SHIFT, (gpu_id & GPU_ID_VERSION_STATUS) >> GPU_ID_VERSION_STATUS_SHIFT);
for (; *issues != BASE_HW_ISSUE_END; issues++)
set_bit(*issues, &kbdev->hw_issues_mask[0]);
return 0;
}

View file

@ -0,0 +1,52 @@
/*
*
* (C) COPYRIGHT 2012-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/**
* @file
* Run-time work-arounds helpers
*/
#ifndef _KBASE_HW_H_
#define _KBASE_HW_H_
#include "mali_kbase_defs.h"
/**
* @brief Tell whether a work-around should be enabled
*/
#define kbase_hw_has_issue(kbdev, issue)\
test_bit(issue, &(kbdev)->hw_issues_mask[0])
/**
* @brief Tell whether a feature is supported
*/
#define kbase_hw_has_feature(kbdev, feature)\
test_bit(feature, &(kbdev)->hw_features_mask[0])
/**
* @brief Set the HW issues mask depending on the GPU ID
*/
int kbase_hw_set_issues_mask(struct kbase_device *kbdev);
/**
* @brief Set the features mask depending on the GPU ID
*/
void kbase_hw_set_features_mask(struct kbase_device *kbdev);
#endif /* _KBASE_HW_H_ */

View file

@ -0,0 +1,54 @@
/*
*
* (C) COPYRIGHT 2014-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* HW access backend common APIs
*/
#ifndef _KBASE_HWACCESS_BACKEND_H_
#define _KBASE_HWACCESS_BACKEND_H_
/**
* kbase_backend_early_init - Perform any backend-specific initialization.
* @kbdev: Device pointer
*
* Return: 0 on success, or an error code on failure.
*/
int kbase_backend_early_init(struct kbase_device *kbdev);
/**
* kbase_backend_late_init - Perform any backend-specific initialization.
* @kbdev: Device pointer
*
* Return: 0 on success, or an error code on failure.
*/
int kbase_backend_late_init(struct kbase_device *kbdev);
/**
* kbase_backend_early_term - Perform any backend-specific termination.
* @kbdev: Device pointer
*/
void kbase_backend_early_term(struct kbase_device *kbdev);
/**
* kbase_backend_late_term - Perform any backend-specific termination.
* @kbdev: Device pointer
*/
void kbase_backend_late_term(struct kbase_device *kbdev);
#endif /* _KBASE_HWACCESS_BACKEND_H_ */

View file

@ -0,0 +1,37 @@
/*
*
* (C) COPYRIGHT 2014 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/**
* @file mali_kbase_hwaccess_gpu_defs.h
* HW access common definitions
*/
#ifndef _KBASE_HWACCESS_DEFS_H_
#define _KBASE_HWACCESS_DEFS_H_
#include <mali_kbase_jm_defs.h>
/* The kbasep_js_device_data::runpool_irq::lock (a spinlock) must be held when
* accessing this structure */
struct kbase_hwaccess_data {
struct kbase_context *active_kctx;
struct kbase_backend_data backend;
};
#endif /* _KBASE_HWACCESS_DEFS_H_ */

View file

@ -0,0 +1,35 @@
/*
*
* (C) COPYRIGHT 2014 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/**
* Base kernel property query backend APIs
*/
#ifndef _KBASE_HWACCESS_GPUPROPS_H_
#define _KBASE_HWACCESS_GPUPROPS_H_
/**
* kbase_backend_gpuprops_get() - Fill @regdump with GPU properties read from
* GPU
* @kbdev: Device pointer
* @regdump: Pointer to struct kbase_gpuprops_regdump structure
*/
void kbase_backend_gpuprops_get(struct kbase_device *kbdev,
struct kbase_gpuprops_regdump *regdump);
#endif /* _KBASE_HWACCESS_GPUPROPS_H_ */

View file

@ -0,0 +1,116 @@
/*
*
* (C) COPYRIGHT 2014-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* HW Access instrumentation common APIs
*/
#ifndef _KBASE_HWACCESS_INSTR_H_
#define _KBASE_HWACCESS_INSTR_H_
#include <mali_kbase_instr_defs.h>
/**
* kbase_instr_hwcnt_enable_internal - Enable HW counters collection
* @kbdev: Kbase device
* @kctx: Kbase context
* @setup: HW counter setup parameters
*
* Context: might sleep, waiting for reset to complete
*
* Return: 0 on success
*/
int kbase_instr_hwcnt_enable_internal(struct kbase_device *kbdev,
struct kbase_context *kctx,
struct kbase_uk_hwcnt_setup *setup);
/**
* kbase_instr_hwcnt_disable_internal - Disable HW counters collection
* @kctx: Kbase context
*
* Context: might sleep, waiting for an ongoing dump to complete
*
* Return: 0 on success
*/
int kbase_instr_hwcnt_disable_internal(struct kbase_context *kctx);
/**
* kbase_instr_hwcnt_request_dump() - Request HW counter dump from GPU
* @kctx: Kbase context
*
* Caller must either wait for kbase_instr_hwcnt_dump_complete() to return true,
* of call kbase_instr_hwcnt_wait_for_dump().
*
* Return: 0 on success
*/
int kbase_instr_hwcnt_request_dump(struct kbase_context *kctx);
/**
* kbase_instr_hwcnt_wait_for_dump() - Wait until pending HW counter dump has
* completed.
* @kctx: Kbase context
*
* Context: will sleep, waiting for dump to complete
*
* Return: 0 on success
*/
int kbase_instr_hwcnt_wait_for_dump(struct kbase_context *kctx);
/**
* kbase_instr_hwcnt_dump_complete - Tell whether the HW counters dump has
* completed
* @kctx: Kbase context
* @success: Set to true if successful
*
* Context: does not sleep.
*
* Return: true if the dump is complete
*/
bool kbase_instr_hwcnt_dump_complete(struct kbase_context *kctx,
bool * const success);
/**
* kbase_instr_hwcnt_clear() - Clear HW counters
* @kctx: Kbase context
*
* Context: might sleep, waiting for reset to complete
*
* Return: 0 on success
*/
int kbase_instr_hwcnt_clear(struct kbase_context *kctx);
/**
* kbase_instr_backend_init() - Initialise the instrumentation backend
* @kbdev: Kbase device
*
* This function should be called during driver initialization.
*
* Return: 0 on success
*/
int kbase_instr_backend_init(struct kbase_device *kbdev);
/**
* kbase_instr_backend_init() - Terminate the instrumentation backend
* @kbdev: Kbase device
*
* This function should be called during driver termination.
*/
void kbase_instr_backend_term(struct kbase_device *kbdev);
#endif /* _KBASE_HWACCESS_INSTR_H_ */

View file

@ -0,0 +1,328 @@
/*
*
* (C) COPYRIGHT 2014-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/*
* HW access job manager common APIs
*/
#ifndef _KBASE_HWACCESS_JM_H_
#define _KBASE_HWACCESS_JM_H_
/**
* kbase_backend_run_atom() - Run an atom on the GPU
* @kbdev: Device pointer
* @atom: Atom to run
*
* Caller must hold the HW access lock
*/
void kbase_backend_run_atom(struct kbase_device *kbdev,
struct kbase_jd_atom *katom);
/**
* kbase_backend_find_free_address_space() - Find a free address space.
* @kbdev: Device pointer
* @kctx: Context pointer
*
* If no address spaces are currently free, then this function can evict an
* idle context from the runpool, freeing up the address space it was using.
*
* The address space is marked as in use. The caller must either assign a
* context using kbase_gpu_use_ctx(), or release it using
* kbase_gpu_release_free_address_space()
*
* Return: Number of free address space, or KBASEP_AS_NR_INVALID if none
* available
*/
int kbase_backend_find_free_address_space(struct kbase_device *kbdev,
struct kbase_context *kctx);
/**
* kbase_backend_release_free_address_space() - Release an address space.
* @kbdev: Device pointer
* @as_nr: Address space to release
*
* The address space must have been returned by
* kbase_gpu_find_free_address_space().
*/
void kbase_backend_release_free_address_space(struct kbase_device *kbdev,
int as_nr);
/**
* kbase_backend_use_ctx() - Activate a currently unscheduled context, using the
* provided address space.
* @kbdev: Device pointer
* @kctx: Context pointer. May be NULL
* @as_nr: Free address space to use
*
* kbase_gpu_next_job() will pull atoms from the active context.
*
* Return: true if successful, false if ASID not assigned. If kctx->as_pending
* is true then ASID assignment will complete at some point in the
* future and will re-start scheduling, otherwise no ASIDs are available
*/
bool kbase_backend_use_ctx(struct kbase_device *kbdev,
struct kbase_context *kctx,
int as_nr);
/**
* kbase_backend_use_ctx_sched() - Activate a context.
* @kbdev: Device pointer
* @kctx: Context pointer
*
* kbase_gpu_next_job() will pull atoms from the active context.
*
* The context must already be scheduled and assigned to an address space. If
* the context is not scheduled, then kbase_gpu_use_ctx() should be used
* instead.
*
* Caller must hold runpool_irq.lock
*
* Return: true if context is now active, false otherwise (ie if context does
* not have an address space assigned)
*/
bool kbase_backend_use_ctx_sched(struct kbase_device *kbdev,
struct kbase_context *kctx);
/**
* kbase_backend_release_ctx_irq - Release a context from the GPU. This will
* de-assign the assigned address space.
* @kbdev: Device pointer
* @kctx: Context pointer
*
* Caller must hold as->transaction_mutex and runpool_irq.lock
*/
void kbase_backend_release_ctx_irq(struct kbase_device *kbdev,
struct kbase_context *kctx);
/**
* kbase_backend_release_ctx_noirq - Release a context from the GPU. This will
* de-assign the assigned address space.
* @kbdev: Device pointer
* @kctx: Context pointer
*
* Caller must hold as->transaction_mutex
*
* This function must perform any operations that could not be performed in IRQ
* context by kbase_backend_release_ctx_irq().
*/
void kbase_backend_release_ctx_noirq(struct kbase_device *kbdev,
struct kbase_context *kctx);
/**
* kbase_backend_complete_wq() - Perform backend-specific actions required on
* completing an atom.
* @kbdev: Device pointer
* @katom: Pointer to the atom to complete
*
* This function should only be called from kbase_jd_done_worker() or
* js_return_worker().
*
* Return: true if atom has completed, false if atom should be re-submitted
*/
void kbase_backend_complete_wq(struct kbase_device *kbdev,
struct kbase_jd_atom *katom);
/**
* kbase_backend_complete_wq_post_sched - Perform backend-specific actions
* required on completing an atom, after
* any scheduling has taken place.
* @kbdev: Device pointer
* @core_req: Core requirements of atom
* @affinity: Affinity of atom
* @coreref_state: Coreref state of atom
*
* This function should only be called from kbase_jd_done_worker() or
* js_return_worker().
*/
void kbase_backend_complete_wq_post_sched(struct kbase_device *kbdev,
base_jd_core_req core_req, u64 affinity,
enum kbase_atom_coreref_state coreref_state);
/**
* kbase_backend_reset() - The GPU is being reset. Cancel all jobs on the GPU
* and remove any others from the ringbuffers.
* @kbdev: Device pointer
* @end_timestamp: Timestamp of reset
*/
void kbase_backend_reset(struct kbase_device *kbdev, ktime_t *end_timestamp);
/**
* kbase_backend_inspect_head() - Return the atom currently at the head of slot
* @js
* @kbdev: Device pointer
* @js: Job slot to inspect
*
* Return : Atom currently at the head of slot @js, or NULL
*/
struct kbase_jd_atom *kbase_backend_inspect_head(struct kbase_device *kbdev,
int js);
/**
* kbase_backend_inspect_tail - Return the atom currently at the tail of slot
* @js
* @kbdev: Device pointer
* @js: Job slot to inspect
*
* Return : Atom currently at the head of slot @js, or NULL
*/
struct kbase_jd_atom *kbase_backend_inspect_tail(struct kbase_device *kbdev,
int js);
/**
* kbase_backend_nr_atoms_on_slot() - Return the number of atoms currently on a
* slot.
* @kbdev: Device pointer
* @js: Job slot to inspect
*
* Return : Number of atoms currently on slot
*/
int kbase_backend_nr_atoms_on_slot(struct kbase_device *kbdev, int js);
/**
* kbase_backend_nr_atoms_submitted() - Return the number of atoms on a slot
* that are currently on the GPU.
* @kbdev: Device pointer
* @js: Job slot to inspect
*
* Return : Number of atoms currently on slot @js that are currently on the GPU.
*/
int kbase_backend_nr_atoms_submitted(struct kbase_device *kbdev, int js);
/**
* kbase_backend_ctx_count_changed() - Number of contexts ready to submit jobs
* has changed.
* @kbdev: Device pointer
*
* Perform any required backend-specific actions (eg starting/stopping
* scheduling timers).
*/
void kbase_backend_ctx_count_changed(struct kbase_device *kbdev);
/**
* kbase_backend_slot_free() - Return the number of jobs that can be currently
* submitted to slot @js.
* @kbdev: Device pointer
* @js: Job slot to inspect
*
* Return : Number of jobs that can be submitted.
*/
int kbase_backend_slot_free(struct kbase_device *kbdev, int js);
/**
* kbase_job_check_enter_disjoint - potentially leave disjoint state
* @kbdev: kbase device
* @target_katom: atom which is finishing
*
* Work out whether to leave disjoint state when finishing an atom that was
* originated by kbase_job_check_enter_disjoint().
*/
void kbase_job_check_leave_disjoint(struct kbase_device *kbdev,
struct kbase_jd_atom *target_katom);
/**
* kbase_backend_jm_kill_jobs_from_kctx - Kill all jobs that are currently
* running from a context
* @kctx: Context pointer
*
* This is used in response to a page fault to remove all jobs from the faulting
* context from the hardware.
*/
void kbase_backend_jm_kill_jobs_from_kctx(struct kbase_context *kctx);
/**
* kbase_jm_wait_for_zero_jobs - Wait for context to have zero jobs running, and
* to be descheduled.
* @kctx: Context pointer
*
* This should be called following kbase_js_zap_context(), to ensure the context
* can be safely destroyed.
*/
void kbase_jm_wait_for_zero_jobs(struct kbase_context *kctx);
#if KBASE_GPU_RESET_EN
/**
* kbase_prepare_to_reset_gpu - Prepare for resetting the GPU.
* @kbdev: Device pointer
*
* This function just soft-stops all the slots to ensure that as many jobs as
* possible are saved.
*
* Return: a boolean which should be interpreted as follows:
* - true - Prepared for reset, kbase_reset_gpu should be called.
* - false - Another thread is performing a reset, kbase_reset_gpu should
* not be called.
*/
bool kbase_prepare_to_reset_gpu(struct kbase_device *kbdev);
/**
* kbase_reset_gpu - Reset the GPU
* @kbdev: Device pointer
*
* This function should be called after kbase_prepare_to_reset_gpu if it returns
* true. It should never be called without a corresponding call to
* kbase_prepare_to_reset_gpu.
*
* After this function is called (or not called if kbase_prepare_to_reset_gpu
* returned false), the caller should wait for kbdev->reset_waitq to be
* signalled to know when the reset has completed.
*/
void kbase_reset_gpu(struct kbase_device *kbdev);
/**
* kbase_prepare_to_reset_gpu_locked - Prepare for resetting the GPU.
* @kbdev: Device pointer
*
* This function just soft-stops all the slots to ensure that as many jobs as
* possible are saved.
*
* Return: a boolean which should be interpreted as follows:
* - true - Prepared for reset, kbase_reset_gpu should be called.
* - false - Another thread is performing a reset, kbase_reset_gpu should
* not be called.
*/
bool kbase_prepare_to_reset_gpu_locked(struct kbase_device *kbdev);
/**
* kbase_reset_gpu_locked - Reset the GPU
* @kbdev: Device pointer
*
* This function should be called after kbase_prepare_to_reset_gpu if it
* returns true. It should never be called without a corresponding call to
* kbase_prepare_to_reset_gpu.
*
* After this function is called (or not called if kbase_prepare_to_reset_gpu
* returned false), the caller should wait for kbdev->reset_waitq to be
* signalled to know when the reset has completed.
*/
void kbase_reset_gpu_locked(struct kbase_device *kbdev);
#endif
/**
* kbase_job_slot_hardstop - Hard-stop the specified job slot
* @kctx: The kbase context that contains the job(s) that should
* be hard-stopped
* @js: The job slot to hard-stop
* @target_katom: The job that should be hard-stopped (or NULL for all
* jobs from the context)
* Context:
* The job slot lock must be held when calling this function.
*/
void kbase_job_slot_hardstop(struct kbase_context *kctx, int js,
struct kbase_jd_atom *target_katom);
#endif /* _KBASE_HWACCESS_JM_H_ */

View file

@ -0,0 +1,206 @@
/*
*
* (C) COPYRIGHT 2014-2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/**
* @file mali_kbase_hwaccess_pm.h
* HW access power manager common APIs
*/
#ifndef _KBASE_HWACCESS_PM_H_
#define _KBASE_HWACCESS_PM_H_
#include <mali_midg_regmap.h>
#include <linux/atomic.h>
#include <mali_kbase_pm_defs.h>
/* Forward definition - see mali_kbase.h */
struct kbase_device;
/* Functions common to all HW access backends */
/**
* Initialize the power management framework.
*
* Must be called before any other power management function
*
* @param kbdev The kbase device structure for the device (must be a valid
* pointer)
*
* @return 0 if the power management framework was successfully
* initialized.
*/
int kbase_hwaccess_pm_init(struct kbase_device *kbdev);
/**
* Terminate the power management framework.
*
* No power management functions may be called after this (except
* @ref kbase_pm_init)
*
* @param kbdev The kbase device structure for the device (must be a valid
* pointer)
*/
void kbase_hwaccess_pm_term(struct kbase_device *kbdev);
/**
* kbase_hwaccess_pm_powerup - Power up the GPU.
* @kbdev: The kbase device structure for the device (must be a valid pointer)
* @flags: Flags to pass on to kbase_pm_init_hw
*
* Power up GPU after all modules have been initialized and interrupt handlers
* installed.
*
* Return: 0 if powerup was successful.
*/
int kbase_hwaccess_pm_powerup(struct kbase_device *kbdev,
unsigned int flags);
/**
* Halt the power management framework.
*
* Should ensure that no new interrupts are generated, but allow any currently
* running interrupt handlers to complete successfully. The GPU is forced off by
* the time this function returns, regardless of whether or not the active power
* policy asks for the GPU to be powered off.
*
* @param kbdev The kbase device structure for the device (must be a valid
* pointer)
*/
void kbase_hwaccess_pm_halt(struct kbase_device *kbdev);
/**
* Perform any backend-specific actions to suspend the GPU
*
* @param kbdev The kbase device structure for the device (must be a valid
* pointer)
*/
void kbase_hwaccess_pm_suspend(struct kbase_device *kbdev);
/**
* Perform any backend-specific actions to resume the GPU from a suspend
*
* @param kbdev The kbase device structure for the device (must be a valid
* pointer)
*/
void kbase_hwaccess_pm_resume(struct kbase_device *kbdev);
/**
* Perform any required actions for activating the GPU. Called when the first
* context goes active.
*
* @param kbdev The kbase device structure for the device (must be a valid
* pointer)
*/
void kbase_hwaccess_pm_gpu_active(struct kbase_device *kbdev);
/**
* Perform any required actions for idling the GPU. Called when the last
* context goes idle.
*
* @param kbdev The kbase device structure for the device (must be a valid
* pointer)
*/
void kbase_hwaccess_pm_gpu_idle(struct kbase_device *kbdev);
/**
* Set the debug core mask.
*
* This determines which cores the power manager is allowed to use.
*
* @param kbdev The kbase device structure for the device (must be a
* valid pointer)
* @param new_core_mask The core mask to use
*/
void kbase_pm_set_debug_core_mask(struct kbase_device *kbdev,
u64 new_core_mask);
/**
* Get the current policy.
*
* Returns the policy that is currently active.
*
* @param kbdev The kbase device structure for the device (must be a valid
* pointer)
*
* @return The current policy
*/
const struct kbase_pm_ca_policy
*kbase_pm_ca_get_policy(struct kbase_device *kbdev);
/**
* Change the policy to the one specified.
*
* @param kbdev The kbase device structure for the device (must be a valid
* pointer)
* @param policy The policy to change to (valid pointer returned from
* @ref kbase_pm_ca_list_policies)
*/
void kbase_pm_ca_set_policy(struct kbase_device *kbdev,
const struct kbase_pm_ca_policy *policy);
/**
* Retrieve a static list of the available policies.
*
* @param[out] policies An array pointer to take the list of policies. This may
* be NULL. The contents of this array must not be
* modified.
*
* @return The number of policies
*/
int
kbase_pm_ca_list_policies(const struct kbase_pm_ca_policy * const **policies);
/**
* Get the current policy.
*
* Returns the policy that is currently active.
*
* @param kbdev The kbase device structure for the device (must be a valid
* pointer)
*
* @return The current policy
*/
const struct kbase_pm_policy *kbase_pm_get_policy(struct kbase_device *kbdev);
/**
* Change the policy to the one specified.
*
* @param kbdev The kbase device structure for the device (must be a valid
* pointer)
* @param policy The policy to change to (valid pointer returned from
* @ref kbase_pm_list_policies)
*/
void kbase_pm_set_policy(struct kbase_device *kbdev,
const struct kbase_pm_policy *policy);
/**
* Retrieve a static list of the available policies.
*
* @param[out] policies An array pointer to take the list of policies. This may
* be NULL. The contents of this array must not be
* modified.
*
* @return The number of policies
*/
int kbase_pm_list_policies(const struct kbase_pm_policy * const **policies);
#endif /* _KBASE_HWACCESS_PM_H_ */

View file

@ -0,0 +1,53 @@
/*
*
* (C) COPYRIGHT 2014 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/**
*
*/
#ifndef _KBASE_BACKEND_TIME_H_
#define _KBASE_BACKEND_TIME_H_
/**
* kbase_backend_get_gpu_time() - Get current GPU time
* @kbdev: Device pointer
* @cycle_counter: Pointer to u64 to store cycle counter in
* @system_time: Pointer to u64 to store system time in
* @ts: Pointer to struct timespec to store current monotonic
* time in
*/
void kbase_backend_get_gpu_time(struct kbase_device *kbdev, u64 *cycle_counter,
u64 *system_time, struct timespec *ts);
/**
* kbase_wait_write_flush() - Wait for GPU write flush
* @kctx: Context pointer
*
* Wait 1000 GPU clock cycles. This delay is known to give the GPU time to flush
* its write buffer.
*
* If GPU resets occur then the counters are reset to zero, the delay may not be
* as expected.
*
* This function is only in use for BASE_HW_ISSUE_6367
*/
#ifndef CONFIG_MALI_NO_MALI
void kbase_wait_write_flush(struct kbase_context *kctx);
#endif
#endif /* _KBASE_BACKEND_TIME_H_ */

View file

@ -0,0 +1,66 @@
/*
*
* (C) COPYRIGHT 2015 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 licence.
*
* A copy of the licence is included with the program, and can also be obtained
* from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#ifndef _KBASE_HWCNT_READER_H_
#define _KBASE_HWCNT_READER_H_
/* The ids of ioctl commands. */
#define KBASE_HWCNT_READER 0xBE
#define KBASE_HWCNT_READER_GET_HWVER _IOR(KBASE_HWCNT_READER, 0x00, u32)
#define KBASE_HWCNT_READER_GET_BUFFER_SIZE _IOR(KBASE_HWCNT_READER, 0x01, u32)
#define KBASE_HWCNT_READER_DUMP _IOW(KBASE_HWCNT_READER, 0x10, u32)
#define KBASE_HWCNT_READER_CLEAR _IOW(KBASE_HWCNT_READER, 0x11, u32)
#define KBASE_HWCNT_READER_GET_BUFFER _IOR(KBASE_HWCNT_READER, 0x20,\
struct kbase_hwcnt_reader_metadata)
#define KBASE_HWCNT_READER_PUT_BUFFER _IOW(KBASE_HWCNT_READER, 0x21,\
struct kbase_hwcnt_reader_metadata)
#define KBASE_HWCNT_READER_SET_INTERVAL _IOW(KBASE_HWCNT_READER, 0x30, u32)
#define KBASE_HWCNT_READER_ENABLE_EVENT _IOW(KBASE_HWCNT_READER, 0x40, u32)
#define KBASE_HWCNT_READER_DISABLE_EVENT _IOW(KBASE_HWCNT_READER, 0x41, u32)
#define KBASE_HWCNT_READER_GET_API_VERSION _IOW(KBASE_HWCNT_READER, 0xFF, u32)
/**
* struct kbase_hwcnt_reader_metadata - hwcnt reader sample buffer metadata
* @timestamp: time when sample was collected
* @event_id: id of an event that triggered sample collection
* @buffer_idx: position in sampling area where sample buffer was stored
*/
struct kbase_hwcnt_reader_metadata {
u64 timestamp;
u32 event_id;
u32 buffer_idx;
};
/**
* enum base_hwcnt_reader_event - hwcnt dumping events
* @BASE_HWCNT_READER_EVENT_MANUAL: manual request for dump
* @BASE_HWCNT_READER_EVENT_PERIODIC: periodic dump
* @BASE_HWCNT_READER_EVENT_PREJOB: prejob dump request
* @BASE_HWCNT_READER_EVENT_POSTJOB: postjob dump request
* @BASE_HWCNT_READER_EVENT_COUNT: number of supported events
*/
enum base_hwcnt_reader_event {
BASE_HWCNT_READER_EVENT_MANUAL,
BASE_HWCNT_READER_EVENT_PERIODIC,
BASE_HWCNT_READER_EVENT_PREJOB,
BASE_HWCNT_READER_EVENT_POSTJOB,
BASE_HWCNT_READER_EVENT_COUNT
};
#endif /* _KBASE_HWCNT_READER_H_ */

Some files were not shown because too many files have changed in this diff Show more