drivers/gpu: merge in Mali GPU drivers from AMLogic

Change-Id: I8ba9513faf15eacb9df45e820bb276be34871a74

Signed-off-by: Dongjin Kim <tobetter@gmail.com>
This commit is contained in:
Mauro Ribeiro
2014-10-15 01:45:26 -03:00
committed by Dongjin Kim
parent 0e31135fba
commit f01201789a
198 changed files with 41573 additions and 0 deletions

284
drivers/gpu/arm/mali/Kbuild Executable file
View File

@@ -0,0 +1,284 @@
#
# Copyright (C) 2010-2011 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.
#
# This file is called by the Linux build system.
############## Kasin Added, for platform. ################
TARGET_PLATFORM:=meson_m450
ifeq ($(CONFIG_ARCH_MESON1),y)
TARGET_PLATFORM:= meson_m400
endif
ifeq ($(CONFIG_ARCH_MESON3),y)
TARGET_PLATFORM:= meson_m400
endif
ifeq ($(CONFIG_ARCH_MESON6),y)
TARGET_PLATFORM:= meson_m400
endif
ifeq ($(CONFIG_ARCH_MESON6TV),y)
TARGET_PLATFORM:= meson_m400
endif
##################### end Kasin Added. ###################
# set up defaults if not defined by the user
TIMESTAMP ?= default
ifeq ($(CONFIG_UMP), m)
USING_UMP ?= 1
else
USING_UMP ?= 0
endif
ifneq ($(KBUILD_SRC),)
ifneq ($(wildcard $(KBUILD_SRC)/$(src)),)
TOP_KBUILD_SRC := $(KBUILD_SRC)/
endif
endif
OS_MEMORY_KERNEL_BUFFER_SIZE_IN_MB ?= 16
USING_GPU_UTILIZATION ?= 1
PROFILING_SKIP_PP_JOBS ?= 0
PROFILING_SKIP_PP_AND_GP_JOBS ?= 0
############## Kasin Added, for platform. ################
ifeq ($(CONFIG_MALI400_DEBUG),y)
BUILD ?= debug
else
BUILD ?= release
ldflags-y += --strip-debug
endif
##################### end Kasin Added. ###################
############## Kasin Added, useless now. ################
ifeq ($(USING_UMP),1)
UMP_SYMVERS_FILE = ../ump/Module.symvers
KBUILD_EXTRA_SYMBOLS = $(srctree)/$(src)/$(UMP_SYMVERS_FILE)
endif
##################### end Kasin Added. ###################
MALI_PP_SCHEDULER_FORCE_NO_JOB_OVERLAP ?= 0
MALI_PP_SCHEDULER_KEEP_SUB_JOB_STARTS_ALIGNED ?= 0
MALI_PP_SCHEDULER_FORCE_NO_JOB_OVERLAP_BETWEEN_APPS ?= 0
MALI_UPPER_HALF_SCHEDULING ?= 1
############## Kasin Added, useless now. ################
# Get path to driver source from Linux build system
DRIVER_DIR=$(src)
##################### end Kasin Added. ###################
MALI_ENABLE_CPU_CYCLES ?= 0
# For customer releases the Linux Device Drivers will be provided as ARM proprietary and GPL releases:
# The ARM proprietary product will only include the license/proprietary directory
# The GPL product will only include the license/gpl directory
ccflags-y += -I$(TOP_KBUILD_SRC)$(DRIVER_DIR)/linux/license/gpl
mali-y += \
linux/mali_osk_atomics.o \
linux/mali_osk_irq.o \
linux/mali_osk_wq.o \
linux/mali_osk_locks.o \
linux/mali_osk_wait_queue.o \
linux/mali_osk_low_level_mem.o \
linux/mali_osk_math.o \
linux/mali_osk_memory.o \
linux/mali_osk_misc.o \
linux/mali_osk_mali.o \
linux/mali_osk_notification.o \
linux/mali_osk_time.o \
linux/mali_osk_timers.o
mali-y += linux/mali_memory.o linux/mali_memory_os_alloc.o
mali-y += linux/mali_memory_external.o
mali-y += linux/mali_memory_block_alloc.o
mali-y += \
linux/mali_ukk_mem.o \
linux/mali_ukk_gp.o \
linux/mali_ukk_pp.o \
linux/mali_ukk_core.o \
linux/mali_ukk_soft_job.o \
linux/mali_ukk_timeline.o
# Source files which always are included in a build
mali-y += \
common/mali_kernel_core.o \
linux/mali_kernel_linux.o \
common/mali_kernel_descriptor_mapping.o \
common/mali_session.o \
linux/mali_device_pause_resume.o \
common/mali_kernel_vsync.o \
linux/mali_ukk_vsync.o \
linux/mali_kernel_sysfs.o \
common/mali_mmu.o \
common/mali_mmu_page_directory.o \
common/mali_mem_validation.o \
common/mali_hw_core.o \
common/mali_gp.o \
common/mali_pp.o \
common/mali_pp_job.o \
common/mali_gp_job.o \
common/mali_soft_job.o \
common/mali_scheduler.o \
common/mali_gp_scheduler.o \
common/mali_pp_scheduler.o \
common/mali_group.o \
common/mali_dlbu.o \
common/mali_broadcast.o \
common/mali_pm.o \
common/mali_pmu.o \
common/mali_user_settings_db.o \
common/mali_kernel_utilization.o \
common/mali_l2_cache.o \
common/mali_dma.o \
common/mali_timeline.o \
common/mali_timeline_fence_wait.o \
common/mali_timeline_sync_fence.o \
common/mali_spinlock_reentrant.o \
common/mali_pm_domain.o \
linux/mali_osk_pm.o \
linux/mali_pmu_power_up_down.o \
__malidrv_build_info.o
############## Kasin Added, for platform. ################
mali-y += \
platform/meson_main.o \
platform/mali_pm_device.o \
platform/mali_clock.o \
platform/mpgpu.o \
ifeq ($(TARGET_PLATFORM),meson_m400)
MALI_PLATFORM_FILES:= \
platform/meson_m400/mali_fix.o \
platform/meson_m400/mali_platform.o \
platform/meson_m400/platform_mx.o
endif
ifeq ($(TARGET_PLATFORM),meson_m450)
ccflags-y += -DCONFIG_MALI450=y
mali-y += \
platform/meson_m450/scaling.o
mali-$(CONFIG_ARCH_MESON8) += \
platform/meson_m450/platform_m8.o
mali-$(CONFIG_ARCH_MESON6TVD) += \
platform/meson_m450/platform_m6tvd.o
mali-$(CONFIG_ARCH_MESON8B) += \
platform/meson_m450/platform_m8b.o
mali-$(CONFIG_ARCH_MESONG9TV) += \
platform/meson_m450/platform_g9tv.o
endif
##################### end Kasin Added. ###################
ifneq ($(MALI_PLATFORM_FILES),)
mali-y += $(MALI_PLATFORM_FILES:.c=.o)
endif
mali-$(CONFIG_MALI400_PROFILING) += linux/mali_ukk_profiling.o
mali-$(CONFIG_MALI400_PROFILING) += linux/mali_osk_profiling.o
mali-$(CONFIG_MALI400_INTERNAL_PROFILING) += linux/mali_profiling_internal.o timestamp-$(TIMESTAMP)/mali_timestamp.o
ccflags-$(CONFIG_MALI400_INTERNAL_PROFILING) += -I$(src)/timestamp-$(TIMESTAMP)
mali-$(CONFIG_DMA_SHARED_BUFFER) += linux/mali_memory_dma_buf.o
mali-$(CONFIG_SYNC) += linux/mali_sync.o
mali-$(CONFIG_MALI400_UMP) += linux/mali_memory_ump.o
mali-$(CONFIG_MALI400_POWER_PERFORMANCE_POLICY) += common/mali_power_performance_policy.o
# Tell the Linux build system from which .o file to create the kernel module
obj-$(CONFIG_MALI400) := mali.o
ccflags-y += $(EXTRA_DEFINES)
# Set up our defines, which will be passed to gcc
ccflags-y += -DPROFILING_SKIP_PP_JOBS=$(PROFILING_SKIP_PP_JOBS)
ccflags-y += -DPROFILING_SKIP_PP_AND_GP_JOBS=$(PROFILING_SKIP_PP_AND_GP_JOBS)
ccflags-y += -DMALI_PP_SCHEDULER_FORCE_NO_JOB_OVERLAP=$(MALI_PP_SCHEDULER_FORCE_NO_JOB_OVERLAP)
ccflags-y += -DMALI_PP_SCHEDULER_KEEP_SUB_JOB_STARTS_ALIGNED=$(MALI_PP_SCHEDULER_KEEP_SUB_JOB_STARTS_ALIGNED)
ccflags-y += -DMALI_PP_SCHEDULER_FORCE_NO_JOB_OVERLAP_BETWEEN_APPS=$(MALI_PP_SCHEDULER_FORCE_NO_JOB_OVERLAP_BETWEEN_APPS)
ccflags-y += -DMALI_STATE_TRACKING=1
ccflags-y += -DMALI_OS_MEMORY_KERNEL_BUFFER_SIZE_IN_MB=$(OS_MEMORY_KERNEL_BUFFER_SIZE_IN_MB)
ccflags-y += -DUSING_GPU_UTILIZATION=$(USING_GPU_UTILIZATION)
ccflags-y += -DMALI_ENABLE_CPU_CYCLES=$(MALI_ENABLE_CPU_CYCLES)
ccflags-y += -DMALI_FAKE_PLATFORM_DEVICE
ifeq ($(MALI_UPPER_HALF_SCHEDULING),1)
ccflags-y += -DMALI_UPPER_HALF_SCHEDULING
endif
ccflags-$(CONFIG_MALI400_UMP) += -I$(src)/../ump/include/ump
ccflags-$(CONFIG_MALI400_DEBUG) += -DDEBUG
# Use our defines when compiling
ccflags-y += -I$(src) -I$(src)/include -I$(src)/common -I$(src)/linux -I$(src)/platform
ccflags-y += -I$(srctree)/drivers/staging/android
# Get subversion revision number, fall back to only ${MALI_RELEASE_NAME} if no svn info is available
MALI_RELEASE_NAME=$(shell cat $(TOP_KBUILD_SRC)$(DRIVER_DIR)/.version 2> /dev/null)
SVN_INFO = (cd $(TOP_KBUILD_SRC)$(DRIVER_DIR); svn info 2>/dev/null)
ifneq ($(shell $(SVN_INFO) 2>/dev/null),)
# SVN detected
SVN_REV := $(shell $(SVN_INFO) | grep '^Revision: '| sed -e 's/^Revision: //' 2>/dev/null)
DRIVER_REV := $(MALI_RELEASE_NAME)-r$(SVN_REV)
CHANGE_DATE := $(shell $(SVN_INFO) | grep '^Last Changed Date: ' | cut -d: -f2- | cut -b2-)
CHANGED_REVISION := $(shell $(SVN_INFO) | grep '^Last Changed Rev: ' | cut -d: -f2- | cut -b2-)
REPO_URL := $(shell $(SVN_INFO) | grep '^URL: ' | cut -d: -f2- | cut -b2-)
else # SVN
GIT_REV := $(shell cd $(TOP_KBUILD_SRC)$(DRIVER_DIR); git describe --always 2>/dev/null)
ifneq ($(GIT_REV),)
# Git detected
DRIVER_REV := $(MALI_RELEASE_NAME)-$(GIT_REV)
CHANGE_DATE := $(shell cd $(TOP_KBUILD_SRC)$(DRIVER_DIR); git log -1 --format="%ci")
CHANGED_REVISION := $(GIT_REV)
REPO_URL := $(shell cd $(TOP_KBUILD_SRC)$(DRIVER_DIR); git describe --all --always 2>/dev/null)
else # Git
# No Git or SVN detected
DRIVER_REV := $(MALI_RELEASE_NAME)
CHANGE_DATE := $(MALI_RELEASE_NAME)
CHANGED_REVISION := $(MALI_RELEASE_NAME)
endif
endif
ccflags-y += -DSVN_REV_STRING=\"$(DRIVER_REV)\"
VERSION_STRINGS :=
VERSION_STRINGS += API_VERSION=$(shell cd $(TOP_KBUILD_SRC)$(DRIVER_DIR); grep "\#define _MALI_API_VERSION" $(FILES_PREFIX)include/linux/mali/mali_utgard_uk_types.h | cut -d' ' -f 3 )
VERSION_STRINGS += REPO_URL=$(REPO_URL)
VERSION_STRINGS += REVISION=$(DRIVER_REV)
VERSION_STRINGS += CHANGED_REVISION=$(CHANGED_REVISION)
VERSION_STRINGS += CHANGE_DATE=$(CHANGE_DATE)
VERSION_STRINGS += BUILD_DATE=$(shell date)
ifdef CONFIG_MALI400_DEBUG
VERSION_STRINGS += BUILD=debug
else
VERSION_STRINGS += BUILD=release
endif
VERSION_STRINGS += TARGET_PLATFORM=$(TARGET_PLATFORM)
VERSION_STRINGS += MALI_PLATFORM=$(MALI_PLATFORM)
VERSION_STRINGS += KDIR=$(KDIR)
VERSION_STRINGS += OS_MEMORY_KERNEL_BUFFER_SIZE_IN_MB=$(OS_MEMORY_KERNEL_BUFFER_SIZE_IN_MB)
VERSION_STRINGS += USING_UMP=$(CONFIG_MALI400_UMP)
VERSION_STRINGS += USING_PROFILING=$(CONFIG_MALI400_PROFILING)
VERSION_STRINGS += USING_INTERNAL_PROFILING=$(CONFIG_MALI400_INTERNAL_PROFILING)
VERSION_STRINGS += USING_GPU_UTILIZATION=$(USING_GPU_UTILIZATION)
VERSION_STRINGS += USING_POWER_PERFORMANCE_POLICY=$(CONFIG_POWER_PERFORMANCE_POLICY)
VERSION_STRINGS += MALI_UPPER_HALF_SCHEDULING=$(MALI_UPPER_HALF_SCHEDULING)
# Create file with Mali driver configuration
$(TOP_KBUILD_SRC)$(DRIVER_DIR)/__malidrv_build_info.c:
@echo 'const char *__malidrv_build_info(void) { return "malidrv: $(VERSION_STRINGS)";}' > $(TOP_KBUILD_SRC)$(DRIVER_DIR)/__malidrv_build_info.c

102
drivers/gpu/arm/mali/Kconfig Executable file
View File

@@ -0,0 +1,102 @@
menu "Mali GPU OpenGL device driver"
config MALI400
tristate "Mali-300/400/450 support"
depends on ARM
depends on m
default m
select DMA_SHARED_BUFFER
---help---
This enables support for the ARM Mali-300, Mali-400, and Mali-450
GPUs.
To compile this driver as a module, choose M here: the module will be
called mali.
config MALI400_DEBUG
bool "Enable debug in Mali driver"
depends on MALI400
---help---
This enabled extra debug checks and messages in the Mali driver.
config MALI400_PROFILING_EXTRA_SUPPORT
bool "Select other items in kernel to support PROFILING."
depends on MALI400_PROFILING
select PROFILING
select FTRACE
select PERF_EVENTS
select ENABLE_DEFAULT_TRACERS
select DEBUG_MUTEXES
select HIGH_RES_TIMERS
select HW_PERF_EVENTS
select CPU_FREQ
config MALI400_PROFILING
bool "Enable Mali profiling"
depends on MALI400
select MALI400_DEBUG
select TRACEPOINTS
default n
---help---
This enables gator profiling of Mali GPU events.
config MALI400_INTERNAL_PROFILING
bool "Enable internal Mali profiling API"
depends on MALI400_PROFILING
default n
---help---
This enables the internal legacy Mali profiling API.
config MALI400_UMP
bool "Enable UMP support"
depends on MALI400
default y
---help---
This enables support for the UMP memory sharing API in the Mali driver.
config MALI400_POWER_PERFORMANCE_POLICY
bool "Enable Mali power performance policy"
depends on ARM
default n
---help---
This enables support for dynamic performance scaling of Mali with the goal of lowering power consumption.
config MALI_DMA_BUF_MAP_ON_ATTACH
bool "Map dma-buf attachments on attach"
depends on MALI400 && DMA_SHARED_BUFFER
default y
---help---
This makes the Mali driver map dma-buf attachments after doing
attach. If this is not set the dma-buf attachments will be mapped for
every time the GPU need to access the buffer.
Mapping for each access can cause lower performance.
config MALI_SHARED_INTERRUPTS
bool "Support for shared interrupts"
depends on MALI400
default n
---help---
Adds functionality required to properly support shared interrupts. Without this support,
the device driver will fail during insmod if it detects shared interrupts. This also
works when the GPU is not using shared interrupts, but might have a slight performance
impact.
if ARCH_MESON6
config MESON6_GPU_EXTRA
bool "M6 fix"
depends on MALI400
default y
select MALI_SHARED_INTERRUPTS
endif
config MALI_PMU_PARALLEL_POWER_UP
bool "Power up Mali PMU domains in parallel"
depends on MALI400
default n
---help---
This makes the Mali driver power up all PMU power domains in parallel, instead of
powering up domains one by one, with a slight delay in between. Powering on all power
domains at the same time may cause peak currents higher than what some systems can handle.
These systems must not enable this option.
endmenu

View File

@@ -0,0 +1,159 @@
#
# Copyright (C) 2010-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.
#
USE_UMPV2=0
USING_PROFILING ?= 1
USING_INTERNAL_PROFILING ?= 0
USING_POWER_PERFORMANCE_POLICY ?= 0
MALI_HEATMAPS_ENABLED ?= 0
MALI_DMA_BUF_MAP_ON_ATTACH ?= 1
MALI_PMU_PARALLEL_POWER_UP ?= 0
# The Makefile sets up "arch" based on the CONFIG, creates the version info
# string and the __malidrv_build_info.c file, and then call the Linux build
# system to actually build the driver. After that point the Kbuild file takes
# over.
# set up defaults if not defined by the user
ARCH ?= arm
OSKOS=linux
FILES_PREFIX=
check_cc2 = \
$(shell if $(1) -S -o /dev/null -xc /dev/null > /dev/null 2>&1; \
then \
echo "$(2)"; \
else \
echo "$(3)"; \
fi ;)
# This conditional makefile exports the global definition ARM_INTERNAL_BUILD. Customer releases will not include arm_internal.mak
-include ../../../arm_internal.mak
# Give warning of old config parameters are used
ifneq ($(CONFIG),)
$(warning "You have specified the CONFIG variable which is no longer in used. Use TARGET_PLATFORM instead.")
endif
ifneq ($(CPU),)
$(warning "You have specified the CPU variable which is no longer in used. Use TARGET_PLATFORM instead.")
endif
# Include the mapping between TARGET_PLATFORM and KDIR + MALI_PLATFORM
-include MALI_CONFIGURATION
export KDIR ?= $(KDIR-$(TARGET_PLATFORM))
export MALI_PLATFORM ?= $(MALI_PLATFORM-$(TARGET_PLATFORM))
ifneq ($(TARGET_PLATFORM),)
ifeq ($(MALI_PLATFORM),)
$(error "Invalid TARGET_PLATFORM: $(TARGET_PLATFORM)")
endif
endif
# validate lookup result
ifeq ($(KDIR),)
$(error No KDIR found for platform $(TARGET_PLATFORM))
endif
ifeq ($(USING_UMP),1)
export CONFIG_MALI400_UMP=y
export EXTRA_DEFINES += -DCONFIG_MALI400_UMP=1
ifeq ($(USE_UMPV2),1)
UMP_SYMVERS_FILE ?= ../umpv2/Module.symvers
else
UMP_SYMVERS_FILE ?= ../ump/Module.symvers
endif
KBUILD_EXTRA_SYMBOLS = $(realpath $(UMP_SYMVERS_FILE))
$(warning $(KBUILD_EXTRA_SYMBOLS))
endif
# Define host system directory
KDIR-$(shell uname -m):=/lib/modules/$(shell uname -r)/build
include $(KDIR)/.config
ifeq ($(ARCH), arm)
# when compiling for ARM we're cross compiling
export CROSS_COMPILE ?= $(call check_cc2, arm-linux-gnueabi-gcc, arm-linux-gnueabi-, arm-none-linux-gnueabi-)
endif
# report detected/selected settings
ifdef ARM_INTERNAL_BUILD
$(warning TARGET_PLATFORM $(TARGET_PLATFORM))
$(warning KDIR $(KDIR))
$(warning MALI_PLATFORM $(MALI_PLATFORM))
endif
# Set up build config
export CONFIG_MALI400=m
export CONFIG_MALI450=y
export EXTRA_DEFINES += -DCONFIG_MALI400=1
export EXTRA_DEFINES += -DCONFIG_MALI450=1
ifneq ($(MALI_PLATFORM),)
export EXTRA_DEFINES += -DMALI_FAKE_PLATFORM_DEVICE=1
export MALI_PLATFORM_FILES = $(wildcard platform/$(MALI_PLATFORM)/*.c)
endif
ifeq ($(USING_PROFILING),1)
ifeq ($(CONFIG_TRACEPOINTS),)
$(warning CONFIG_TRACEPOINTS required for profiling)
else
export CONFIG_MALI400_PROFILING=y
export EXTRA_DEFINES += -DCONFIG_MALI400_PROFILING=1
ifeq ($(USING_INTERNAL_PROFILING),1)
export CONFIG_MALI400_INTERNAL_PROFILING=y
export EXTRA_DEFINES += -DCONFIG_MALI400_INTERNAL_PROFILING=1
endif
ifeq ($(MALI_HEATMAPS_ENABLED),1)
export MALI_HEATMAPS_ENABLED=y
export EXTRA_DEFINES += -DCONFIG_MALI400_HEATMAPS_ENABLED
endif
endif
endif
ifeq ($(MALI_DMA_BUF_MAP_ON_ATTACH),1)
export CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH=y
export EXTRA_DEFINES += -DCONFIG_MALI_DMA_BUF_MAP_ON_ATTACH
endif
ifeq ($(MALI_SHARED_INTERRUPTS),1)
export CONFIG_MALI_SHARED_INTERRUPTS=y
export EXTRA_DEFINES += -DCONFIG_MALI_SHARED_INTERRUPTS
endif
ifeq ($(USING_POWER_PERFORMANCE_POLICY),1)
export CONFIG_MALI400_POWER_PERFORMANCE_POLICY=y
export EXTRA_DEFINES += -DCONFIG_MALI400_POWER_PERFORMANCE_POLICY
endif
ifeq ($(MALI_PMU_PARALLEL_POWER_UP),1)
export CONFIG_MALI_PMU_PARALLEL_POWER_UP=y
export EXTRA_DEFINES += -DCONFIG_MALI_PMU_PARALLEL_POWER_UP
endif
ifneq ($(BUILD),release)
export CONFIG_MALI400_DEBUG=y
endif
all: $(UMP_SYMVERS_FILE)
$(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) modules
@rm $(FILES_PREFIX)__malidrv_build_info.c $(FILES_PREFIX)__malidrv_build_info.o
clean:
$(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) clean
kernelrelease:
$(MAKE) ARCH=$(ARCH) -C $(KDIR) kernelrelease
export CONFIG KBUILD_EXTRA_SYMBOLS

View File

@@ -0,0 +1 @@
const char *__malidrv_build_info(void) { return "malidrv: API_VERSION=401 REPO_URL=heads/odroidc_2014.05 REVISION=-2014.05-3-gf4cc986 CHANGED_REVISION=2014.05-3-gf4cc986 CHANGE_DATE=2014-09-22 15:09:31 +0900 BUILD_DATE=Seg Out 13 07:21:28 BRT 2014 BUILD=release TARGET_PLATFORM=meson_m450 MALI_PLATFORM= KDIR= OS_MEMORY_KERNEL_BUFFER_SIZE_IN_MB=16 USING_UMP=y USING_PROFILING= USING_INTERNAL_PROFILING= USING_GPU_UTILIZATION=1 USING_POWER_PERFORMANCE_POLICY= MALI_UPPER_HALF_SCHEDULING=1";}

View File

@@ -0,0 +1,27 @@
/// ***************************************************************************************
/// - MALI
//$$ MODULE="MALI"
//$$ DEVICE="mali"
//$$ L2 PROP_STR = "status"
mali{
compatible = "arm,mali";
dev_name = "mali";
status = "ok";
//$$ L2 PROP_U32 = "dvfs_id"
//$$ L2 PROP_U32 = "recorde_number"
//$$ L2 PROP_U32 = "dvfs_table"
cfg {
shared_memory = <1024>; /** Mbyte **/
dvfs_size = <5>; /** must be correct count for dvfs_table */
dvfs_table = <
/* NOTE: frequent in this table must be ascending order */
/* freq_idx volage_index min max */
0 0 0 200
1 1 152 205
2 2 180 212
3 3 205 236
4 4 230 256
>;
};
};

View File

@@ -0,0 +1,124 @@
/*
* Copyright (C) 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.
*/
#include "mali_broadcast.h"
#include "mali_kernel_common.h"
#include "mali_osk.h"
static const int bcast_unit_reg_size = 0x1000;
static const int bcast_unit_addr_broadcast_mask = 0x0;
static const int bcast_unit_addr_irq_override_mask = 0x4;
struct mali_bcast_unit {
struct mali_hw_core hw_core;
u32 current_mask;
};
struct mali_bcast_unit *mali_bcast_unit_create(const _mali_osk_resource_t *resource)
{
struct mali_bcast_unit *bcast_unit = NULL;
MALI_DEBUG_ASSERT_POINTER(resource);
MALI_DEBUG_PRINT(2, ("Mali Broadcast unit: Creating Mali Broadcast unit: %s\n", resource->description));
bcast_unit = _mali_osk_malloc(sizeof(struct mali_bcast_unit));
if (NULL == bcast_unit) {
MALI_PRINT_ERROR(("Mali Broadcast unit: Failed to allocate memory for Broadcast unit\n"));
return NULL;
}
if (_MALI_OSK_ERR_OK == mali_hw_core_create(&bcast_unit->hw_core, resource, bcast_unit_reg_size)) {
bcast_unit->current_mask = 0;
mali_bcast_reset(bcast_unit);
return bcast_unit;
} else {
MALI_PRINT_ERROR(("Mali Broadcast unit: Failed map broadcast unit\n"));
}
_mali_osk_free(bcast_unit);
return NULL;
}
void mali_bcast_unit_delete(struct mali_bcast_unit *bcast_unit)
{
MALI_DEBUG_ASSERT_POINTER(bcast_unit);
mali_hw_core_delete(&bcast_unit->hw_core);
_mali_osk_free(bcast_unit);
}
void mali_bcast_add_group(struct mali_bcast_unit *bcast_unit, struct mali_group *group)
{
u32 bcast_id;
u32 broadcast_mask;
MALI_DEBUG_ASSERT_POINTER(bcast_unit);
MALI_DEBUG_ASSERT_POINTER(group);
bcast_id = mali_pp_core_get_bcast_id(mali_group_get_pp_core(group));
broadcast_mask = bcast_unit->current_mask;
broadcast_mask |= (bcast_id); /* add PP core to broadcast */
broadcast_mask |= (bcast_id << 16); /* add MMU to broadcast */
/* store mask so we can restore on reset */
bcast_unit->current_mask = broadcast_mask;
}
void mali_bcast_remove_group(struct mali_bcast_unit *bcast_unit, struct mali_group *group)
{
u32 bcast_id;
u32 broadcast_mask;
MALI_DEBUG_ASSERT_POINTER(bcast_unit);
MALI_DEBUG_ASSERT_POINTER(group);
bcast_id = mali_pp_core_get_bcast_id(mali_group_get_pp_core(group));
broadcast_mask = bcast_unit->current_mask;
broadcast_mask &= ~((bcast_id << 16) | bcast_id);
/* store mask so we can restore on reset */
bcast_unit->current_mask = broadcast_mask;
}
void mali_bcast_reset(struct mali_bcast_unit *bcast_unit)
{
MALI_DEBUG_ASSERT_POINTER(bcast_unit);
/* set broadcast mask */
mali_hw_core_register_write(&bcast_unit->hw_core,
bcast_unit_addr_broadcast_mask,
bcast_unit->current_mask);
/* set IRQ override mask */
mali_hw_core_register_write(&bcast_unit->hw_core,
bcast_unit_addr_irq_override_mask,
bcast_unit->current_mask & 0xFF);
}
void mali_bcast_disable(struct mali_bcast_unit *bcast_unit)
{
MALI_DEBUG_ASSERT_POINTER(bcast_unit);
/* set broadcast mask */
mali_hw_core_register_write(&bcast_unit->hw_core,
bcast_unit_addr_broadcast_mask,
0x0);
/* set IRQ override mask */
mali_hw_core_register_write(&bcast_unit->hw_core,
bcast_unit_addr_irq_override_mask,
0x0);
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (C) 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.
*/
/*
* Interface for the broadcast unit on Mali-450.
*
* - Represents up to 8 × (MMU + PP) pairs.
* - Supports dynamically changing which (MMU + PP) pairs receive the broadcast by
* setting a mask.
*/
#include "mali_hw_core.h"
#include "mali_group.h"
struct mali_bcast_unit;
struct mali_bcast_unit *mali_bcast_unit_create(const _mali_osk_resource_t *resource);
void mali_bcast_unit_delete(struct mali_bcast_unit *bcast_unit);
/* Add a group to the list of (MMU + PP) pairs broadcasts go out to. */
void mali_bcast_add_group(struct mali_bcast_unit *bcast_unit, struct mali_group *group);
/* Remove a group to the list of (MMU + PP) pairs broadcasts go out to. */
void mali_bcast_remove_group(struct mali_bcast_unit *bcast_unit, struct mali_group *group);
/* Re-set cached mask. This needs to be called after having been suspended. */
void mali_bcast_reset(struct mali_bcast_unit *bcast_unit);
/**
* Disable broadcast unit
*
* mali_bcast_enable must be called to re-enable the unit. Cores may not be
* added or removed when the unit is disabled.
*/
void mali_bcast_disable(struct mali_bcast_unit *bcast_unit);
/**
* Re-enable broadcast unit
*
* This resets the masks to include the cores present when mali_bcast_disable was called.
*/
MALI_STATIC_INLINE void mali_bcast_enable(struct mali_bcast_unit *bcast_unit)
{
mali_bcast_reset(bcast_unit);
}

View File

@@ -0,0 +1,209 @@
/*
* Copyright (C) 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.
*/
#include "mali_dlbu.h"
#include "mali_memory.h"
#include "mali_pp.h"
#include "mali_group.h"
#include "mali_osk.h"
#include "mali_hw_core.h"
/**
* Size of DLBU registers in bytes
*/
#define MALI_DLBU_SIZE 0x400
u32 mali_dlbu_phys_addr = 0;
static mali_io_address mali_dlbu_cpu_addr = 0;
/**
* DLBU register numbers
* Used in the register read/write routines.
* See the hardware documentation for more information about each register
*/
typedef enum mali_dlbu_register {
MALI_DLBU_REGISTER_MASTER_TLLIST_PHYS_ADDR = 0x0000, /**< Master tile list physical base address;
31:12 Physical address to the page used for the DLBU
0 DLBU enable - set this bit to 1 enables the AXI bus
between PPs and L2s, setting to 0 disables the router and
no further transactions are sent to DLBU */
MALI_DLBU_REGISTER_MASTER_TLLIST_VADDR = 0x0004, /**< Master tile list virtual base address;
31:12 Virtual address to the page used for the DLBU */
MALI_DLBU_REGISTER_TLLIST_VBASEADDR = 0x0008, /**< Tile list virtual base address;
31:12 Virtual address to the tile list. This address is used when
calculating the call address sent to PP.*/
MALI_DLBU_REGISTER_FB_DIM = 0x000C, /**< Framebuffer dimension;
23:16 Number of tiles in Y direction-1
7:0 Number of tiles in X direction-1 */
MALI_DLBU_REGISTER_TLLIST_CONF = 0x0010, /**< Tile list configuration;
29:28 select the size of each allocated block: 0=128 bytes, 1=256, 2=512, 3=1024
21:16 2^n number of tiles to be binned to one tile list in Y direction
5:0 2^n number of tiles to be binned to one tile list in X direction */
MALI_DLBU_REGISTER_START_TILE_POS = 0x0014, /**< Start tile positions;
31:24 start position in Y direction for group 1
23:16 start position in X direction for group 1
15:8 start position in Y direction for group 0
7:0 start position in X direction for group 0 */
MALI_DLBU_REGISTER_PP_ENABLE_MASK = 0x0018, /**< PP enable mask;
7 enable PP7 for load balancing
6 enable PP6 for load balancing
5 enable PP5 for load balancing
4 enable PP4 for load balancing
3 enable PP3 for load balancing
2 enable PP2 for load balancing
1 enable PP1 for load balancing
0 enable PP0 for load balancing */
} mali_dlbu_register;
typedef enum {
PP0ENABLE = 0,
PP1ENABLE,
PP2ENABLE,
PP3ENABLE,
PP4ENABLE,
PP5ENABLE,
PP6ENABLE,
PP7ENABLE
} mali_dlbu_pp_enable;
struct mali_dlbu_core {
struct mali_hw_core hw_core; /**< Common for all HW cores */
u32 pp_cores_mask; /**< This is a mask for the PP cores whose operation will be controlled by LBU
see MALI_DLBU_REGISTER_PP_ENABLE_MASK register */
};
_mali_osk_errcode_t mali_dlbu_initialize(void)
{
MALI_DEBUG_PRINT(2, ("Mali DLBU: Initializing\n"));
if (_MALI_OSK_ERR_OK == mali_mmu_get_table_page(&mali_dlbu_phys_addr, &mali_dlbu_cpu_addr)) {
MALI_SUCCESS;
}
return _MALI_OSK_ERR_FAULT;
}
void mali_dlbu_terminate(void)
{
MALI_DEBUG_PRINT(3, ("Mali DLBU: terminating\n"));
mali_mmu_release_table_page(mali_dlbu_phys_addr, mali_dlbu_cpu_addr);
}
struct mali_dlbu_core *mali_dlbu_create(const _mali_osk_resource_t * resource)
{
struct mali_dlbu_core *core = NULL;
MALI_DEBUG_PRINT(2, ("Mali DLBU: Creating Mali dynamic load balancing unit: %s\n", resource->description));
core = _mali_osk_malloc(sizeof(struct mali_dlbu_core));
if (NULL != core) {
if (_MALI_OSK_ERR_OK == mali_hw_core_create(&core->hw_core, resource, MALI_DLBU_SIZE)) {
core->pp_cores_mask = 0;
if (_MALI_OSK_ERR_OK == mali_dlbu_reset(core)) {
return core;
}
MALI_PRINT_ERROR(("Failed to reset DLBU %s\n", core->hw_core.description));
mali_hw_core_delete(&core->hw_core);
}
_mali_osk_free(core);
} else {
MALI_PRINT_ERROR(("Mali DLBU: Failed to allocate memory for DLBU core\n"));
}
return NULL;
}
void mali_dlbu_delete(struct mali_dlbu_core *dlbu)
{
MALI_DEBUG_ASSERT_POINTER(dlbu);
mali_dlbu_reset(dlbu);
mali_hw_core_delete(&dlbu->hw_core);
_mali_osk_free(dlbu);
}
_mali_osk_errcode_t mali_dlbu_reset(struct mali_dlbu_core *dlbu)
{
u32 dlbu_registers[7];
_mali_osk_errcode_t err = _MALI_OSK_ERR_FAULT;
MALI_DEBUG_ASSERT_POINTER(dlbu);
MALI_DEBUG_PRINT(4, ("Mali DLBU: mali_dlbu_reset: %s\n", dlbu->hw_core.description));
dlbu_registers[0] = mali_dlbu_phys_addr | 1; /* bit 0 enables the whole core */
dlbu_registers[1] = MALI_DLBU_VIRT_ADDR;
dlbu_registers[2] = 0;
dlbu_registers[3] = 0;
dlbu_registers[4] = 0;
dlbu_registers[5] = 0;
dlbu_registers[6] = dlbu->pp_cores_mask;
/* write reset values to core registers */
mali_hw_core_register_write_array_relaxed(&dlbu->hw_core, MALI_DLBU_REGISTER_MASTER_TLLIST_PHYS_ADDR, dlbu_registers, 7);
err = _MALI_OSK_ERR_OK;
return err;
}
void mali_dlbu_update_mask(struct mali_dlbu_core *dlbu)
{
MALI_DEBUG_ASSERT_POINTER(dlbu);
mali_hw_core_register_write(&dlbu->hw_core, MALI_DLBU_REGISTER_PP_ENABLE_MASK, dlbu->pp_cores_mask);
}
void mali_dlbu_add_group(struct mali_dlbu_core *dlbu, struct mali_group *group)
{
struct mali_pp_core *pp_core;
u32 bcast_id;
MALI_DEBUG_ASSERT_POINTER( dlbu );
MALI_DEBUG_ASSERT_POINTER( group );
pp_core = mali_group_get_pp_core(group);
bcast_id = mali_pp_core_get_bcast_id(pp_core);
dlbu->pp_cores_mask |= bcast_id;
MALI_DEBUG_PRINT(3, ("Mali DLBU: Adding core[%d] New mask= 0x%02x\n", bcast_id , dlbu->pp_cores_mask));
}
/* Remove a group from the DLBU */
void mali_dlbu_remove_group(struct mali_dlbu_core *dlbu, struct mali_group *group)
{
struct mali_pp_core *pp_core;
u32 bcast_id;
MALI_DEBUG_ASSERT_POINTER( dlbu );
MALI_DEBUG_ASSERT_POINTER( group );
pp_core = mali_group_get_pp_core(group);
bcast_id = mali_pp_core_get_bcast_id(pp_core);
dlbu->pp_cores_mask &= ~bcast_id;
MALI_DEBUG_PRINT(3, ("Mali DLBU: Removing core[%d] New mask= 0x%02x\n", bcast_id, dlbu->pp_cores_mask));
}
/* Configure the DLBU for \a job. This needs to be done before the job is started on the groups in the DLBU. */
void mali_dlbu_config_job(struct mali_dlbu_core *dlbu, struct mali_pp_job *job)
{
u32 *registers;
MALI_DEBUG_ASSERT(job);
registers = mali_pp_job_get_dlbu_registers(job);
MALI_DEBUG_PRINT(4, ("Mali DLBU: Starting job\n"));
/* Writing 4 registers:
* DLBU registers except the first two (written once at DLBU initialisation / reset) and the PP_ENABLE_MASK register */
mali_hw_core_register_write_array_relaxed(&dlbu->hw_core, MALI_DLBU_REGISTER_TLLIST_VBASEADDR, registers, 4);
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 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.
*/
#ifndef __MALI_DLBU_H__
#define __MALI_DLBU_H__
#define MALI_DLBU_VIRT_ADDR 0xFFF00000 /* master tile virtual address fixed at this value and mapped into every session */
#include "mali_osk.h"
struct mali_pp_job;
struct mali_group;
extern u32 mali_dlbu_phys_addr;
struct mali_dlbu_core;
_mali_osk_errcode_t mali_dlbu_initialize(void);
void mali_dlbu_terminate(void);
struct mali_dlbu_core *mali_dlbu_create(const _mali_osk_resource_t * resource);
void mali_dlbu_delete(struct mali_dlbu_core *dlbu);
_mali_osk_errcode_t mali_dlbu_reset(struct mali_dlbu_core *dlbu);
void mali_dlbu_add_group(struct mali_dlbu_core *dlbu, struct mali_group *group);
void mali_dlbu_remove_group(struct mali_dlbu_core *dlbu, struct mali_group *group);
/** @brief Called to update HW after DLBU state changed
*
* This function must be called after \a mali_dlbu_add_group or \a
* mali_dlbu_remove_group to write the updated mask to hardware, unless the
* same is accomplished by calling \a mali_dlbu_reset.
*/
void mali_dlbu_update_mask(struct mali_dlbu_core *dlbu);
void mali_dlbu_config_job(struct mali_dlbu_core *dlbu, struct mali_pp_job *job);
#endif /* __MALI_DLBU_H__ */

View File

@@ -0,0 +1,201 @@
/*
* Copyright (C) 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.
*/
#include "mali_kernel_common.h"
#include "mali_osk.h"
#include "mali_hw_core.h"
#include "mali_dma.h"
/**
* Size of the Mali-450 DMA unit registers in bytes.
*/
#define MALI450_DMA_REG_SIZE 0x08
/**
* Value that appears in MEMSIZE if an error occurs when reading the command list.
*/
#define MALI450_DMA_BUS_ERR_VAL 0xffffffff
/**
* Mali DMA registers
* Used in the register read/write routines.
* See the hardware documentation for more information about each register.
*/
typedef enum mali_dma_register {
MALI450_DMA_REG_SOURCE_ADDRESS = 0x0000,
MALI450_DMA_REG_SOURCE_SIZE = 0x0004,
} mali_dma_register;
struct mali_dma_core {
struct mali_hw_core hw_core; /**< Common for all HW cores */
_mali_osk_spinlock_t *lock; /**< Lock protecting access to DMA core */
mali_dma_pool pool; /**< Memory pool for command buffers */
};
static struct mali_dma_core *mali_global_dma_core = NULL;
struct mali_dma_core *mali_dma_create(_mali_osk_resource_t *resource)
{
struct mali_dma_core* dma;
_mali_osk_errcode_t err;
MALI_DEBUG_ASSERT(NULL == mali_global_dma_core);
dma = _mali_osk_malloc(sizeof(struct mali_dma_core));
if (dma == NULL) goto alloc_failed;
dma->lock = _mali_osk_spinlock_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_DMA_COMMAND);
if (NULL == dma->lock) goto lock_init_failed;
dma->pool = mali_dma_pool_create(MALI_DMA_CMD_BUF_SIZE, 4, 0);
if (NULL == dma->pool) goto dma_pool_failed;
err = mali_hw_core_create(&dma->hw_core, resource, MALI450_DMA_REG_SIZE);
if (_MALI_OSK_ERR_OK != err) goto hw_core_failed;
mali_global_dma_core = dma;
MALI_DEBUG_PRINT(2, ("Mali DMA: Created Mali APB DMA unit\n"));
return dma;
/* Error handling */
hw_core_failed:
mali_dma_pool_destroy(dma->pool);
dma_pool_failed:
_mali_osk_spinlock_term(dma->lock);
lock_init_failed:
_mali_osk_free(dma);
alloc_failed:
MALI_DEBUG_PRINT(2, ("Mali DMA: Failed to create APB DMA unit\n"));
return NULL;
}
void mali_dma_delete(struct mali_dma_core *dma)
{
MALI_DEBUG_ASSERT_POINTER(dma);
MALI_DEBUG_PRINT(2, ("Mali DMA: Deleted Mali APB DMA unit\n"));
mali_hw_core_delete(&dma->hw_core);
_mali_osk_spinlock_term(dma->lock);
mali_dma_pool_destroy(dma->pool);
_mali_osk_free(dma);
}
static void mali_dma_bus_error(struct mali_dma_core *dma)
{
u32 addr = mali_hw_core_register_read(&dma->hw_core, MALI450_DMA_REG_SOURCE_ADDRESS);
MALI_PRINT_ERROR(("Mali DMA: Bus error when reading command list from 0x%lx\n", addr));
/* Clear the bus error */
mali_hw_core_register_write(&dma->hw_core, MALI450_DMA_REG_SOURCE_SIZE, 0);
}
static mali_bool mali_dma_is_busy(struct mali_dma_core *dma)
{
u32 val;
mali_bool dma_busy_flag = MALI_FALSE;
MALI_DEBUG_ASSERT_POINTER(dma);
val = mali_hw_core_register_read(&dma->hw_core, MALI450_DMA_REG_SOURCE_SIZE);
if (MALI450_DMA_BUS_ERR_VAL == val) {
/* Bus error reading command list */
mali_dma_bus_error(dma);
return MALI_FALSE;
}
if (val > 0) {
dma_busy_flag = MALI_TRUE;
}
return dma_busy_flag;
}
static void mali_dma_start_transfer(struct mali_dma_core* dma, mali_dma_cmd_buf *buf)
{
u32 memsize = buf->size * 4;
u32 addr = buf->phys_addr;
MALI_DEBUG_ASSERT_POINTER(dma);
MALI_DEBUG_ASSERT(memsize < (1 << 16));
MALI_DEBUG_ASSERT(0 == (memsize & 0x3)); /* 4 byte aligned */
MALI_DEBUG_ASSERT(!mali_dma_is_busy(dma));
/* Writes the physical source memory address of chunk containing command headers and data */
mali_hw_core_register_write(&dma->hw_core, MALI450_DMA_REG_SOURCE_ADDRESS, addr);
/* Writes the length of transfer */
mali_hw_core_register_write(&dma->hw_core, MALI450_DMA_REG_SOURCE_SIZE, memsize);
}
_mali_osk_errcode_t mali_dma_get_cmd_buf(mali_dma_cmd_buf *buf)
{
MALI_DEBUG_ASSERT_POINTER(buf);
buf->virt_addr = (u32*)mali_dma_pool_alloc(mali_global_dma_core->pool, &buf->phys_addr);
if (NULL == buf->virt_addr) {
return _MALI_OSK_ERR_NOMEM;
}
/* size contains the number of words in the buffer and is incremented
* as commands are added to the buffer. */
buf->size = 0;
return _MALI_OSK_ERR_OK;
}
void mali_dma_put_cmd_buf(mali_dma_cmd_buf *buf)
{
MALI_DEBUG_ASSERT_POINTER(buf);
if (NULL == buf->virt_addr) return;
mali_dma_pool_free(mali_global_dma_core->pool, buf->virt_addr, buf->phys_addr);
buf->virt_addr = NULL;
}
_mali_osk_errcode_t mali_dma_start(struct mali_dma_core* dma, mali_dma_cmd_buf *buf)
{
_mali_osk_errcode_t err = _MALI_OSK_ERR_OK;
_mali_osk_spinlock_lock(dma->lock);
if (mali_dma_is_busy(dma)) {
err = _MALI_OSK_ERR_BUSY;
goto out;
}
mali_dma_start_transfer(dma, buf);
out:
_mali_osk_spinlock_unlock(dma->lock);
return err;
}
void mali_dma_debug(struct mali_dma_core *dma)
{
MALI_DEBUG_ASSERT_POINTER(dma);
MALI_DEBUG_PRINT(1, ("DMA unit registers:\n\t%08x, %08x\n",
mali_hw_core_register_read(&dma->hw_core, MALI450_DMA_REG_SOURCE_ADDRESS),
mali_hw_core_register_read(&dma->hw_core, MALI450_DMA_REG_SOURCE_SIZE)
));
}
struct mali_dma_core *mali_dma_get_global_dma_core(void)
{
/* Returns the global dma core object */
return mali_global_dma_core;
}

View File

@@ -0,0 +1,190 @@
/*
* Copyright (C) 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.
*/
#ifndef __MALI_DMA_H__
#define __MALI_DMA_H__
#include "mali_osk.h"
#include "mali_osk_mali.h"
#include "mali_hw_core.h"
#define MALI_DMA_CMD_BUF_SIZE 1024
typedef struct mali_dma_cmd_buf {
u32 *virt_addr; /**< CPU address of command buffer */
u32 phys_addr; /**< Physical address of command buffer */
u32 size; /**< Number of prepared words in command buffer */
} mali_dma_cmd_buf;
/** @brief Create a new DMA unit
*
* This is called from entry point of the driver in order to create and
* intialize the DMA resource
*
* @param resource it will be a pointer to a DMA resource
* @return DMA object on success, NULL on failure
*/
struct mali_dma_core *mali_dma_create(_mali_osk_resource_t *resource);
/** @brief Delete DMA unit
*
* This is called on entry point of driver if the driver initialization fails
* after initialization of the DMA unit. It is also called on the exit of the
* driver to delete the DMA resource
*
* @param dma Pointer to DMA unit object
*/
void mali_dma_delete(struct mali_dma_core *dma);
/** @brief Retrieves the MALI DMA core object (if there is)
*
* @return The Mali DMA object otherwise NULL
*/
struct mali_dma_core *mali_dma_get_global_dma_core(void);
/**
* @brief Run a command buffer on the DMA unit
*
* @param dma Pointer to the DMA unit to use
* @param buf Pointer to the command buffer to use
* @return _MALI_OSK_ERR_OK if the buffer was started successfully,
* _MALI_OSK_ERR_BUSY if the DMA unit is busy.
*/
_mali_osk_errcode_t mali_dma_start(struct mali_dma_core* dma, mali_dma_cmd_buf *buf);
/**
* @brief Create a DMA command
*
* @param core Mali core
* @param reg offset to register of core
* @param n number of registers to write
*/
MALI_STATIC_INLINE u32 mali_dma_command_write(struct mali_hw_core *core, u32 reg, u32 n)
{
u32 core_offset = core->phys_offset;
MALI_DEBUG_ASSERT(reg < 0x2000);
MALI_DEBUG_ASSERT(n < 0x800);
MALI_DEBUG_ASSERT(core_offset < 0x30000);
MALI_DEBUG_ASSERT(0 == ((core_offset + reg) & ~0x7FFFF));
return (n << 20) | (core_offset + reg);
}
/**
* @brief Add a array write to DMA command buffer
*
* @param buf DMA command buffer to fill in
* @param core Core to do DMA to
* @param reg Register on core to start writing to
* @param data Pointer to data to write
* @param count Number of 4 byte words to write
*/
MALI_STATIC_INLINE void mali_dma_write_array(mali_dma_cmd_buf *buf, struct mali_hw_core *core,
u32 reg, u32 *data, u32 count)
{
MALI_DEBUG_ASSERT((buf->size + 1 + count ) < MALI_DMA_CMD_BUF_SIZE / 4);
buf->virt_addr[buf->size++] = mali_dma_command_write(core, reg, count);
_mali_osk_memcpy(buf->virt_addr + buf->size, data, count * sizeof(*buf->virt_addr));
buf->size += count;
}
/**
* @brief Add a conditional array write to DMA command buffer
*
* @param buf DMA command buffer to fill in
* @param core Core to do DMA to
* @param reg Register on core to start writing to
* @param data Pointer to data to write
* @param count Number of 4 byte words to write
* @param ref Pointer to referance data that can be skipped if equal
*/
MALI_STATIC_INLINE void mali_dma_write_array_conditional(mali_dma_cmd_buf *buf, struct mali_hw_core *core,
u32 reg, u32 *data, u32 count, const u32 *ref)
{
/* Do conditional array writes are not yet implemented, fallback to a
* normal array write. */
mali_dma_write_array(buf, core, reg, data, count);
}
/**
* @brief Add a conditional register write to the DMA command buffer
*
* If the data matches the reference the command will be skipped.
*
* @param buf DMA command buffer to fill in
* @param core Core to do DMA to
* @param reg Register on core to start writing to
* @param data Pointer to data to write
* @param ref Pointer to referance data that can be skipped if equal
*/
MALI_STATIC_INLINE void mali_dma_write_conditional(mali_dma_cmd_buf *buf, struct mali_hw_core *core,
u32 reg, u32 data, const u32 ref)
{
/* Skip write if reference value is equal to data. */
if (data == ref) return;
buf->virt_addr[buf->size++] = mali_dma_command_write(core, reg, 1);
buf->virt_addr[buf->size++] = data;
MALI_DEBUG_ASSERT(buf->size < MALI_DMA_CMD_BUF_SIZE / 4);
}
/**
* @brief Add a register write to the DMA command buffer
*
* @param buf DMA command buffer to fill in
* @param core Core to do DMA to
* @param reg Register on core to start writing to
* @param data Pointer to data to write
*/
MALI_STATIC_INLINE void mali_dma_write(mali_dma_cmd_buf *buf, struct mali_hw_core *core,
u32 reg, u32 data)
{
buf->virt_addr[buf->size++] = mali_dma_command_write(core, reg, 1);
buf->virt_addr[buf->size++] = data;
MALI_DEBUG_ASSERT(buf->size < MALI_DMA_CMD_BUF_SIZE / 4);
}
/**
* @brief Prepare DMA command buffer for use
*
* This function allocates the DMA buffer itself.
*
* @param buf The mali_dma_cmd_buf to prepare
* @return _MALI_OSK_ERR_OK if the \a buf is ready to use
*/
_mali_osk_errcode_t mali_dma_get_cmd_buf(mali_dma_cmd_buf *buf);
/**
* @brief Check if a DMA command buffer is ready for use
*
* @param buf The mali_dma_cmd_buf to check
* @return MALI_TRUE if buffer is usable, MALI_FALSE otherwise
*/
MALI_STATIC_INLINE mali_bool mali_dma_cmd_buf_is_valid(mali_dma_cmd_buf *buf)
{
return NULL != buf->virt_addr;
}
/**
* @brief Return a DMA command buffer
*
* @param buf Pointer to DMA command buffer to return
*/
void mali_dma_put_cmd_buf(mali_dma_cmd_buf *buf);
#endif /* __MALI_DMA_H__ */

View File

@@ -0,0 +1,337 @@
/*
* Copyright (C) 2011-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.
*/
#include "mali_gp.h"
#include "mali_hw_core.h"
#include "mali_group.h"
#include "mali_osk.h"
#include "regs/mali_gp_regs.h"
#include "mali_kernel_common.h"
#include "mali_kernel_core.h"
#if defined(CONFIG_MALI400_PROFILING)
#include "mali_osk_profiling.h"
#endif
static struct mali_gp_core *mali_global_gp_core = NULL;
/* Interrupt handlers */
static void mali_gp_irq_probe_trigger(void *data);
static _mali_osk_errcode_t mali_gp_irq_probe_ack(void *data);
struct mali_gp_core *mali_gp_create(const _mali_osk_resource_t * resource, struct mali_group *group)
{
struct mali_gp_core* core = NULL;
MALI_DEBUG_ASSERT(NULL == mali_global_gp_core);
MALI_DEBUG_PRINT(2, ("Mali GP: Creating Mali GP core: %s\n", resource->description));
core = _mali_osk_malloc(sizeof(struct mali_gp_core));
if (NULL != core) {
if (_MALI_OSK_ERR_OK == mali_hw_core_create(&core->hw_core, resource, MALIGP2_REGISTER_ADDRESS_SPACE_SIZE)) {
_mali_osk_errcode_t ret;
ret = mali_gp_reset(core);
if (_MALI_OSK_ERR_OK == ret) {
ret = mali_group_add_gp_core(group, core);
if (_MALI_OSK_ERR_OK == ret) {
/* Setup IRQ handlers (which will do IRQ probing if needed) */
core->irq = _mali_osk_irq_init(resource->irq,
mali_group_upper_half_gp,
group,
mali_gp_irq_probe_trigger,
mali_gp_irq_probe_ack,
core,
resource->description);
if (NULL != core->irq) {
MALI_DEBUG_PRINT(4, ("Mali GP: set global gp core from 0x%08X to 0x%08X\n", mali_global_gp_core, core));
mali_global_gp_core = core;
return core;
} else {
MALI_PRINT_ERROR(("Mali GP: Failed to setup interrupt handlers for GP core %s\n", core->hw_core.description));
}
mali_group_remove_gp_core(group);
} else {
MALI_PRINT_ERROR(("Mali GP: Failed to add core %s to group\n", core->hw_core.description));
}
}
mali_hw_core_delete(&core->hw_core);
}
_mali_osk_free(core);
} else {
MALI_PRINT_ERROR(("Failed to allocate memory for GP core\n"));
}
return NULL;
}
void mali_gp_delete(struct mali_gp_core *core)
{
MALI_DEBUG_ASSERT_POINTER(core);
_mali_osk_irq_term(core->irq);
mali_hw_core_delete(&core->hw_core);
mali_global_gp_core = NULL;
_mali_osk_free(core);
}
void mali_gp_stop_bus(struct mali_gp_core *core)
{
MALI_DEBUG_ASSERT_POINTER(core);
mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_CMD, MALIGP2_REG_VAL_CMD_STOP_BUS);
}
_mali_osk_errcode_t mali_gp_stop_bus_wait(struct mali_gp_core *core)
{
int i;
MALI_DEBUG_ASSERT_POINTER(core);
/* Send the stop bus command. */
mali_gp_stop_bus(core);
/* Wait for bus to be stopped */
for (i = 0; i < MALI_REG_POLL_COUNT_FAST; i++) {
if (mali_hw_core_register_read(&core->hw_core, MALIGP2_REG_ADDR_MGMT_STATUS) & MALIGP2_REG_VAL_STATUS_BUS_STOPPED) {
break;
}
}
if (MALI_REG_POLL_COUNT_FAST == i) {
MALI_PRINT_ERROR(("Mali GP: Failed to stop bus on %s\n", core->hw_core.description));
return _MALI_OSK_ERR_FAULT;
}
return _MALI_OSK_ERR_OK;
}
void mali_gp_hard_reset(struct mali_gp_core *core)
{
const u32 reset_wait_target_register = MALIGP2_REG_ADDR_MGMT_WRITE_BOUND_LOW;
const u32 reset_invalid_value = 0xC0FFE000;
const u32 reset_check_value = 0xC01A0000;
const u32 reset_default_value = 0;
int i;
MALI_DEBUG_ASSERT_POINTER(core);
MALI_DEBUG_PRINT(4, ("Mali GP: Hard reset of core %s\n", core->hw_core.description));
mali_hw_core_register_write(&core->hw_core, reset_wait_target_register, reset_invalid_value);
mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_CMD, MALIGP2_REG_VAL_CMD_RESET);
for (i = 0; i < MALI_REG_POLL_COUNT_FAST; i++) {
mali_hw_core_register_write(&core->hw_core, reset_wait_target_register, reset_check_value);
if (reset_check_value == mali_hw_core_register_read(&core->hw_core, reset_wait_target_register)) {
break;
}
}
if (MALI_REG_POLL_COUNT_FAST == i) {
MALI_PRINT_ERROR(("Mali GP: The hard reset loop didn't work, unable to recover\n"));
}
mali_hw_core_register_write(&core->hw_core, reset_wait_target_register, reset_default_value); /* set it back to the default */
/* Re-enable interrupts */
mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_CLEAR, MALIGP2_REG_VAL_IRQ_MASK_ALL);
mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_MASK, MALIGP2_REG_VAL_IRQ_MASK_USED);
}
void mali_gp_reset_async(struct mali_gp_core *core)
{
MALI_DEBUG_ASSERT_POINTER(core);
MALI_DEBUG_PRINT(4, ("Mali GP: Reset of core %s\n", core->hw_core.description));
mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_MASK, 0); /* disable the IRQs */
mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_CLEAR, MALI400GP_REG_VAL_IRQ_RESET_COMPLETED);
mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_CMD, MALI400GP_REG_VAL_CMD_SOFT_RESET);
}
_mali_osk_errcode_t mali_gp_reset_wait(struct mali_gp_core *core)
{
int i;
u32 rawstat = 0;
MALI_DEBUG_ASSERT_POINTER(core);
for (i = 0; i < MALI_REG_POLL_COUNT_FAST; i++) {
rawstat = mali_hw_core_register_read(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_RAWSTAT);
if (rawstat & MALI400GP_REG_VAL_IRQ_RESET_COMPLETED) {
break;
}
}
if (i == MALI_REG_POLL_COUNT_FAST) {
MALI_PRINT_ERROR(("Mali GP: Failed to reset core %s, rawstat: 0x%08x\n",
core->hw_core.description, rawstat));
return _MALI_OSK_ERR_FAULT;
}
/* Re-enable interrupts */
mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_CLEAR, MALIGP2_REG_VAL_IRQ_MASK_ALL);
mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_MASK, MALIGP2_REG_VAL_IRQ_MASK_USED);
return _MALI_OSK_ERR_OK;
}
_mali_osk_errcode_t mali_gp_reset(struct mali_gp_core *core)
{
mali_gp_reset_async(core);
return mali_gp_reset_wait(core);
}
void mali_gp_job_start(struct mali_gp_core *core, struct mali_gp_job *job)
{
u32 startcmd = 0;
u32 *frame_registers = mali_gp_job_get_frame_registers(job);
u32 counter_src0 = mali_gp_job_get_perf_counter_src0(job);
u32 counter_src1 = mali_gp_job_get_perf_counter_src1(job);
MALI_DEBUG_ASSERT_POINTER(core);
if (mali_gp_job_has_vs_job(job)) {
startcmd |= (u32) MALIGP2_REG_VAL_CMD_START_VS;
}
if (mali_gp_job_has_plbu_job(job)) {
startcmd |= (u32) MALIGP2_REG_VAL_CMD_START_PLBU;
}
MALI_DEBUG_ASSERT(0 != startcmd);
mali_hw_core_register_write_array_relaxed(&core->hw_core, MALIGP2_REG_ADDR_MGMT_VSCL_START_ADDR, frame_registers, MALIGP2_NUM_REGS_FRAME);
if (MALI_HW_CORE_NO_COUNTER != counter_src0) {
mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_SRC, counter_src0);
mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_ENABLE, MALIGP2_REG_VAL_PERF_CNT_ENABLE);
}
if (MALI_HW_CORE_NO_COUNTER != counter_src1) {
mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_SRC, counter_src1);
mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_ENABLE, MALIGP2_REG_VAL_PERF_CNT_ENABLE);
}
MALI_DEBUG_PRINT(3, ("Mali GP: Starting job (0x%08x) on core %s with command 0x%08X\n", job, core->hw_core.description, startcmd));
mali_hw_core_register_write_relaxed(&core->hw_core, MALIGP2_REG_ADDR_MGMT_CMD, MALIGP2_REG_VAL_CMD_UPDATE_PLBU_ALLOC);
/* Barrier to make sure the previous register write is finished */
_mali_osk_write_mem_barrier();
/* This is the command that starts the core. */
mali_hw_core_register_write_relaxed(&core->hw_core, MALIGP2_REG_ADDR_MGMT_CMD, startcmd);
/* Barrier to make sure the previous register write is finished */
_mali_osk_write_mem_barrier();
}
void mali_gp_resume_with_new_heap(struct mali_gp_core *core, u32 start_addr, u32 end_addr)
{
u32 irq_readout;
MALI_DEBUG_ASSERT_POINTER(core);
irq_readout = mali_hw_core_register_read(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_RAWSTAT);
if (irq_readout & MALIGP2_REG_VAL_IRQ_PLBU_OUT_OF_MEM) {
mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_CLEAR, (MALIGP2_REG_VAL_IRQ_PLBU_OUT_OF_MEM | MALIGP2_REG_VAL_IRQ_HANG));
mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_MASK, MALIGP2_REG_VAL_IRQ_MASK_USED); /* re-enable interrupts */
mali_hw_core_register_write_relaxed(&core->hw_core, MALIGP2_REG_ADDR_MGMT_PLBU_ALLOC_START_ADDR, start_addr);
mali_hw_core_register_write_relaxed(&core->hw_core, MALIGP2_REG_ADDR_MGMT_PLBU_ALLOC_END_ADDR, end_addr);
MALI_DEBUG_PRINT(3, ("Mali GP: Resuming job\n"));
mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_CMD, MALIGP2_REG_VAL_CMD_UPDATE_PLBU_ALLOC);
_mali_osk_write_mem_barrier();
}
/*
* else: core has been reset between PLBU_OUT_OF_MEM interrupt and this new heap response.
* A timeout or a page fault on Mali-200 PP core can cause this behaviour.
*/
}
u32 mali_gp_core_get_version(struct mali_gp_core *core)
{
MALI_DEBUG_ASSERT_POINTER(core);
return mali_hw_core_register_read(&core->hw_core, MALIGP2_REG_ADDR_MGMT_VERSION);
}
struct mali_gp_core *mali_gp_get_global_gp_core(void)
{
return mali_global_gp_core;
}
/* ------------- interrupt handling below ------------------ */
static void mali_gp_irq_probe_trigger(void *data)
{
struct mali_gp_core *core = (struct mali_gp_core *)data;
mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_MASK, MALIGP2_REG_VAL_IRQ_MASK_USED);
mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_RAWSTAT, MALIGP2_REG_VAL_CMD_FORCE_HANG);
_mali_osk_mem_barrier();
}
static _mali_osk_errcode_t mali_gp_irq_probe_ack(void *data)
{
struct mali_gp_core *core = (struct mali_gp_core *)data;
u32 irq_readout;
irq_readout = mali_hw_core_register_read(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_STAT);
if (MALIGP2_REG_VAL_IRQ_FORCE_HANG & irq_readout) {
mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_CLEAR, MALIGP2_REG_VAL_IRQ_FORCE_HANG);
_mali_osk_mem_barrier();
return _MALI_OSK_ERR_OK;
}
return _MALI_OSK_ERR_FAULT;
}
/* ------ local helper functions below --------- */
#if MALI_STATE_TRACKING
u32 mali_gp_dump_state(struct mali_gp_core *core, char *buf, u32 size)
{
int n = 0;
n += _mali_osk_snprintf(buf + n, size - n, "\tGP: %s\n", core->hw_core.description);
return n;
}
#endif
void mali_gp_update_performance_counters(struct mali_gp_core *core, struct mali_gp_job *job, mali_bool suspend)
{
u32 val0 = 0;
u32 val1 = 0;
u32 counter_src0 = mali_gp_job_get_perf_counter_src0(job);
u32 counter_src1 = mali_gp_job_get_perf_counter_src1(job);
if (MALI_HW_CORE_NO_COUNTER != counter_src0) {
val0 = mali_hw_core_register_read(&core->hw_core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_VALUE);
mali_gp_job_set_perf_counter_value0(job, val0);
#if defined(CONFIG_MALI400_PROFILING)
_mali_osk_profiling_report_hw_counter(COUNTER_VP_0_C0, val0);
#endif
}
if (MALI_HW_CORE_NO_COUNTER != counter_src1) {
val1 = mali_hw_core_register_read(&core->hw_core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_VALUE);
mali_gp_job_set_perf_counter_value1(job, val1);
#if defined(CONFIG_MALI400_PROFILING)
_mali_osk_profiling_report_hw_counter(COUNTER_VP_0_C1, val1);
#endif
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright (C) 2011-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.
*/
#ifndef __MALI_GP_H__
#define __MALI_GP_H__
#include "mali_osk.h"
#include "mali_gp_job.h"
#include "mali_hw_core.h"
#include "regs/mali_gp_regs.h"
struct mali_group;
/**
* Definition of the GP core struct
* Used to track a GP core in the system.
*/
struct mali_gp_core {
struct mali_hw_core hw_core; /**< Common for all HW cores */
_mali_osk_irq_t *irq; /**< IRQ handler */
};
_mali_osk_errcode_t mali_gp_initialize(void);
void mali_gp_terminate(void);
struct mali_gp_core *mali_gp_create(const _mali_osk_resource_t * resource, struct mali_group *group);
void mali_gp_delete(struct mali_gp_core *core);
void mali_gp_stop_bus(struct mali_gp_core *core);
_mali_osk_errcode_t mali_gp_stop_bus_wait(struct mali_gp_core *core);
void mali_gp_reset_async(struct mali_gp_core *core);
_mali_osk_errcode_t mali_gp_reset_wait(struct mali_gp_core *core);
void mali_gp_hard_reset(struct mali_gp_core *core);
_mali_osk_errcode_t mali_gp_reset(struct mali_gp_core *core);
void mali_gp_job_start(struct mali_gp_core *core, struct mali_gp_job *job);
void mali_gp_resume_with_new_heap(struct mali_gp_core *core, u32 start_addr, u32 end_addr);
u32 mali_gp_core_get_version(struct mali_gp_core *core);
struct mali_gp_core *mali_gp_get_global_gp_core(void);
u32 mali_gp_dump_state(struct mali_gp_core *core, char *buf, u32 size);
void mali_gp_update_performance_counters(struct mali_gp_core *core, struct mali_gp_job *job, mali_bool suspend);
/*** Accessor functions ***/
MALI_STATIC_INLINE const char *mali_gp_get_hw_core_desc(struct mali_gp_core *core)
{
return core->hw_core.description;
}
/*** Register reading/writing functions ***/
MALI_STATIC_INLINE u32 mali_gp_get_int_stat(struct mali_gp_core *core)
{
return mali_hw_core_register_read(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_STAT);
}
MALI_STATIC_INLINE void mali_gp_mask_all_interrupts(struct mali_gp_core *core)
{
mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_MASK, MALIGP2_REG_VAL_IRQ_MASK_NONE);
}
MALI_STATIC_INLINE u32 mali_gp_read_rawstat(struct mali_gp_core *core)
{
return mali_hw_core_register_read(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_RAWSTAT) & MALIGP2_REG_VAL_IRQ_MASK_USED;
}
MALI_STATIC_INLINE u32 mali_gp_read_core_status(struct mali_gp_core *core)
{
return mali_hw_core_register_read(&core->hw_core, MALIGP2_REG_ADDR_MGMT_STATUS);
}
MALI_STATIC_INLINE void mali_gp_enable_interrupts(struct mali_gp_core *core, u32 irq_exceptions)
{
/* Enable all interrupts, except those specified in irq_exceptions */
mali_hw_core_register_write(&core->hw_core, MALIGP2_REG_ADDR_MGMT_INT_MASK,
MALIGP2_REG_VAL_IRQ_MASK_USED & ~irq_exceptions);
}
MALI_STATIC_INLINE u32 mali_gp_read_plbu_alloc_start_addr(struct mali_gp_core *core)
{
return mali_hw_core_register_read(&core->hw_core, MALIGP2_REG_ADDR_MGMT_PLBU_ALLOC_START_ADDR);
}
#endif /* __MALI_GP_H__ */

View File

@@ -0,0 +1,131 @@
/*
* Copyright (C) 2011-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.
*/
#include "mali_gp_job.h"
#include "mali_osk.h"
#include "mali_osk_list.h"
#include "mali_uk_types.h"
static u32 gp_counter_src0 = MALI_HW_CORE_NO_COUNTER; /**< Performance counter 0, MALI_HW_CORE_NO_COUNTER for disabled */
static u32 gp_counter_src1 = MALI_HW_CORE_NO_COUNTER; /**< Performance counter 1, MALI_HW_CORE_NO_COUNTER for disabled */
struct mali_gp_job *mali_gp_job_create(struct mali_session_data *session, _mali_uk_gp_start_job_s *uargs, u32 id, struct mali_timeline_tracker *pp_tracker)
{
struct mali_gp_job *job;
u32 perf_counter_flag;
job = _mali_osk_malloc(sizeof(struct mali_gp_job));
if (NULL != job) {
job->finished_notification = _mali_osk_notification_create(_MALI_NOTIFICATION_GP_FINISHED, sizeof(_mali_uk_gp_job_finished_s));
if (NULL == job->finished_notification) {
_mali_osk_free(job);
return NULL;
}
job->oom_notification = _mali_osk_notification_create(_MALI_NOTIFICATION_GP_STALLED, sizeof(_mali_uk_gp_job_suspended_s));
if (NULL == job->oom_notification) {
_mali_osk_notification_delete(job->finished_notification);
_mali_osk_free(job);
return NULL;
}
if (0 != _mali_osk_copy_from_user(&job->uargs, uargs, sizeof(_mali_uk_gp_start_job_s))) {
_mali_osk_notification_delete(job->finished_notification);
_mali_osk_notification_delete(job->oom_notification);
_mali_osk_free(job);
return NULL;
}
perf_counter_flag = mali_gp_job_get_perf_counter_flag(job);
/* case when no counters came from user space
* so pass the debugfs / DS-5 provided global ones to the job object */
if (!((perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_SRC0_ENABLE) ||
(perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_SRC1_ENABLE))) {
mali_gp_job_set_perf_counter_src0(job, mali_gp_job_get_gp_counter_src0());
mali_gp_job_set_perf_counter_src1(job, mali_gp_job_get_gp_counter_src1());
}
_mali_osk_list_init(&job->list);
job->session = session;
job->id = id;
job->heap_current_addr = job->uargs.frame_registers[4];
job->perf_counter_value0 = 0;
job->perf_counter_value1 = 0;
job->pid = _mali_osk_get_pid();
job->tid = _mali_osk_get_tid();
job->pp_tracker = pp_tracker;
if (NULL != job->pp_tracker) {
/* Take a reference on PP job's tracker that will be released when the GP
job is done. */
mali_timeline_system_tracker_get(session->timeline_system, pp_tracker);
}
mali_timeline_tracker_init(&job->tracker, MALI_TIMELINE_TRACKER_GP, NULL, job);
mali_timeline_fence_copy_uk_fence(&(job->tracker.fence), &(job->uargs.fence));
return job;
}
return NULL;
}
void mali_gp_job_delete(struct mali_gp_job *job)
{
MALI_DEBUG_ASSERT_POINTER(job);
MALI_DEBUG_ASSERT(NULL == job->pp_tracker);
/* de-allocate the pre-allocated oom notifications */
if (NULL != job->oom_notification) {
_mali_osk_notification_delete(job->oom_notification);
job->oom_notification = NULL;
}
if (NULL != job->finished_notification) {
_mali_osk_notification_delete(job->finished_notification);
job->finished_notification = NULL;
}
_mali_osk_free(job);
}
u32 mali_gp_job_get_gp_counter_src0(void)
{
return gp_counter_src0;
}
void mali_gp_job_set_gp_counter_src0(u32 counter)
{
gp_counter_src0 = counter;
}
u32 mali_gp_job_get_gp_counter_src1(void)
{
return gp_counter_src1;
}
void mali_gp_job_set_gp_counter_src1(u32 counter)
{
gp_counter_src1 = counter;
}
mali_scheduler_mask mali_gp_job_signal_pp_tracker(struct mali_gp_job *job, mali_bool success)
{
mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY;
MALI_DEBUG_ASSERT_POINTER(job);
if (NULL != job->pp_tracker) {
schedule_mask |= mali_timeline_system_tracker_put(job->session->timeline_system, job->pp_tracker, MALI_FALSE == success);
job->pp_tracker = NULL;
}
return schedule_mask;
}

View File

@@ -0,0 +1,186 @@
/*
* Copyright (C) 2011-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.
*/
#ifndef __MALI_GP_JOB_H__
#define __MALI_GP_JOB_H__
#include "mali_osk.h"
#include "mali_osk_list.h"
#include "mali_uk_types.h"
#include "mali_session.h"
#include "mali_timeline.h"
#include "mali_scheduler_types.h"
/**
* The structure represents a GP job, including all sub-jobs
* (This struct unfortunately needs to be public because of how the _mali_osk_list_*
* mechanism works)
*/
struct mali_gp_job {
_mali_osk_list_t list; /**< Used to link jobs together in the scheduler queue */
struct mali_session_data *session; /**< Session which submitted this job */
_mali_uk_gp_start_job_s uargs; /**< Arguments from user space */
u32 id; /**< Identifier for this job in kernel space (sequential numbering) */
u32 cache_order; /**< Cache order used for L2 cache flushing (sequential numbering) */
u32 heap_current_addr; /**< Holds the current HEAP address when the job has completed */
u32 perf_counter_value0; /**< Value of performance counter 0 (to be returned to user space) */
u32 perf_counter_value1; /**< Value of performance counter 1 (to be returned to user space) */
u32 pid; /**< Process ID of submitting process */
u32 tid; /**< Thread ID of submitting thread */
_mali_osk_notification_t *finished_notification; /**< Notification sent back to userspace on job complete */
_mali_osk_notification_t *oom_notification; /**< Notification sent back to userspace on OOM */
struct mali_timeline_tracker tracker; /**< Timeline tracker for this job */
struct mali_timeline_tracker *pp_tracker; /**< Pointer to Timeline tracker for PP job that depends on this job. */
};
struct mali_gp_job *mali_gp_job_create(struct mali_session_data *session, _mali_uk_gp_start_job_s *uargs, u32 id, struct mali_timeline_tracker *pp_tracker);
void mali_gp_job_delete(struct mali_gp_job *job);
u32 mali_gp_job_get_gp_counter_src0(void);
void mali_gp_job_set_gp_counter_src0(u32 counter);
u32 mali_gp_job_get_gp_counter_src1(void);
void mali_gp_job_set_gp_counter_src1(u32 counter);
MALI_STATIC_INLINE u32 mali_gp_job_get_id(struct mali_gp_job *job)
{
return (NULL == job) ? 0 : job->id;
}
MALI_STATIC_INLINE u32 mali_gp_job_get_cache_order(struct mali_gp_job *job)
{
return (NULL == job) ? 0 : job->cache_order;
}
MALI_STATIC_INLINE u32 mali_gp_job_get_user_id(struct mali_gp_job *job)
{
return job->uargs.user_job_ptr;
}
MALI_STATIC_INLINE u32 mali_gp_job_get_frame_builder_id(struct mali_gp_job *job)
{
return job->uargs.frame_builder_id;
}
MALI_STATIC_INLINE u32 mali_gp_job_get_flush_id(struct mali_gp_job *job)
{
return job->uargs.flush_id;
}
MALI_STATIC_INLINE u32 mali_gp_job_get_pid(struct mali_gp_job *job)
{
return job->pid;
}
MALI_STATIC_INLINE u32 mali_gp_job_get_tid(struct mali_gp_job *job)
{
return job->tid;
}
MALI_STATIC_INLINE u32* mali_gp_job_get_frame_registers(struct mali_gp_job *job)
{
return job->uargs.frame_registers;
}
MALI_STATIC_INLINE struct mali_session_data *mali_gp_job_get_session(struct mali_gp_job *job)
{
return job->session;
}
MALI_STATIC_INLINE mali_bool mali_gp_job_has_vs_job(struct mali_gp_job *job)
{
return (job->uargs.frame_registers[0] != job->uargs.frame_registers[1]) ? MALI_TRUE : MALI_FALSE;
}
MALI_STATIC_INLINE mali_bool mali_gp_job_has_plbu_job(struct mali_gp_job *job)
{
return (job->uargs.frame_registers[2] != job->uargs.frame_registers[3]) ? MALI_TRUE : MALI_FALSE;
}
MALI_STATIC_INLINE u32 mali_gp_job_get_current_heap_addr(struct mali_gp_job *job)
{
return job->heap_current_addr;
}
MALI_STATIC_INLINE void mali_gp_job_set_current_heap_addr(struct mali_gp_job *job, u32 heap_addr)
{
job->heap_current_addr = heap_addr;
}
MALI_STATIC_INLINE u32 mali_gp_job_get_perf_counter_flag(struct mali_gp_job *job)
{
return job->uargs.perf_counter_flag;
}
MALI_STATIC_INLINE u32 mali_gp_job_get_perf_counter_src0(struct mali_gp_job *job)
{
return job->uargs.perf_counter_src0;
}
MALI_STATIC_INLINE u32 mali_gp_job_get_perf_counter_src1(struct mali_gp_job *job)
{
return job->uargs.perf_counter_src1;
}
MALI_STATIC_INLINE u32 mali_gp_job_get_perf_counter_value0(struct mali_gp_job *job)
{
return job->perf_counter_value0;
}
MALI_STATIC_INLINE u32 mali_gp_job_get_perf_counter_value1(struct mali_gp_job *job)
{
return job->perf_counter_value1;
}
MALI_STATIC_INLINE void mali_gp_job_set_perf_counter_src0(struct mali_gp_job *job, u32 src)
{
job->uargs.perf_counter_src0 = src;
}
MALI_STATIC_INLINE void mali_gp_job_set_perf_counter_src1(struct mali_gp_job *job, u32 src)
{
job->uargs.perf_counter_src1 = src;
}
MALI_STATIC_INLINE void mali_gp_job_set_perf_counter_value0(struct mali_gp_job *job, u32 value)
{
job->perf_counter_value0 = value;
}
MALI_STATIC_INLINE void mali_gp_job_set_perf_counter_value1(struct mali_gp_job *job, u32 value)
{
job->perf_counter_value1 = value;
}
/**
* Returns MALI_TRUE if first job is after the second job, ordered by job ID.
*
* @param first First job.
* @param second Second job.
* @return MALI_TRUE if first job should be ordered after the second job, MALI_FALSE if not.
*/
MALI_STATIC_INLINE mali_bool mali_gp_job_is_after(struct mali_gp_job *first, struct mali_gp_job *second)
{
/* A span is used to handle job ID wrapping. */
return (mali_gp_job_get_id(first) - mali_gp_job_get_id(second)) < MALI_SCHEDULER_JOB_ID_SPAN;
}
/**
* Release reference on tracker for PP job that depends on this GP job.
*
* @note If GP job has a reference on tracker, this function MUST be called before the GP job is
* deleted.
*
* @param job GP job that is done.
* @param success MALI_TRUE if job completed successfully, MALI_FALSE if not.
* @return A scheduling bitmask indicating whether scheduling needs to be done.
*/
mali_scheduler_mask mali_gp_job_signal_pp_tracker(struct mali_gp_job *job, mali_bool success);
#endif /* __MALI_GP_JOB_H__ */

View File

@@ -0,0 +1,701 @@
/*
* Copyright (C) 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.
*/
#include "mali_gp_scheduler.h"
#include "mali_kernel_common.h"
#include "mali_osk.h"
#include "mali_osk_list.h"
#include "mali_scheduler.h"
#include "mali_gp.h"
#include "mali_gp_job.h"
#include "mali_group.h"
#include "mali_timeline.h"
#include "mali_osk_profiling.h"
#include "mali_kernel_utilization.h"
#if defined(CONFIG_GPU_TRACEPOINTS) && defined(CONFIG_TRACEPOINTS)
#include <linux/sched.h>
#include <trace/events/gpu.h>
#endif
enum mali_gp_slot_state {
MALI_GP_SLOT_STATE_IDLE,
MALI_GP_SLOT_STATE_WORKING,
MALI_GP_SLOT_STATE_DISABLED,
};
/* A render slot is an entity which jobs can be scheduled onto */
struct mali_gp_slot {
struct mali_group *group;
/*
* We keep track of the state here as well as in the group object
* so we don't need to take the group lock so often (and also avoid clutter with the working lock)
*/
enum mali_gp_slot_state state;
u32 returned_cookie;
};
static u32 gp_version = 0;
static _MALI_OSK_LIST_HEAD_STATIC_INIT(job_queue); /* List of unscheduled jobs. */
static _MALI_OSK_LIST_HEAD_STATIC_INIT(job_queue_high); /* List of unscheduled high priority jobs. */
static struct mali_gp_slot slot;
/* Variables to allow safe pausing of the scheduler */
static _mali_osk_wait_queue_t *gp_scheduler_working_wait_queue = NULL;
static u32 pause_count = 0;
static mali_bool mali_gp_scheduler_is_suspended(void *data);
static void mali_gp_scheduler_job_queued(void);
static void mali_gp_scheduler_job_completed(void);
#if defined(MALI_UPPER_HALF_SCHEDULING)
static _mali_osk_spinlock_irq_t *gp_scheduler_lock = NULL;
#else
static _mali_osk_spinlock_t *gp_scheduler_lock = NULL;
#endif /* defined(MALI_UPPER_HALF_SCHEDULING) */
_mali_osk_errcode_t mali_gp_scheduler_initialize(void)
{
u32 num_groups;
u32 i;
_mali_osk_errcode_t ret = _MALI_OSK_ERR_OK;
#if defined(MALI_UPPER_HALF_SCHEDULING)
gp_scheduler_lock = _mali_osk_spinlock_irq_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_SCHEDULER);
#else
gp_scheduler_lock = _mali_osk_spinlock_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_SCHEDULER);
#endif /* defined(MALI_UPPER_HALF_SCHEDULING) */
if (NULL == gp_scheduler_lock) {
ret = _MALI_OSK_ERR_NOMEM;
goto cleanup;
}
gp_scheduler_working_wait_queue = _mali_osk_wait_queue_init();
if (NULL == gp_scheduler_working_wait_queue) {
ret = _MALI_OSK_ERR_NOMEM;
goto cleanup;
}
/* Find all the available GP cores */
num_groups = mali_group_get_glob_num_groups();
for (i = 0; i < num_groups; i++) {
struct mali_group *group = mali_group_get_glob_group(i);
MALI_DEBUG_ASSERT(NULL != group);
if (NULL != group) {
struct mali_gp_core *gp_core = mali_group_get_gp_core(group);
if (NULL != gp_core) {
if (0 == gp_version) {
/* Retrieve GP version */
gp_version = mali_gp_core_get_version(gp_core);
}
slot.group = group;
slot.state = MALI_GP_SLOT_STATE_IDLE;
break; /* There is only one GP, no point in looking for more */
}
} else {
ret = _MALI_OSK_ERR_ITEM_NOT_FOUND;
goto cleanup;
}
}
return _MALI_OSK_ERR_OK;
cleanup:
if (NULL != gp_scheduler_working_wait_queue) {
_mali_osk_wait_queue_term(gp_scheduler_working_wait_queue);
gp_scheduler_working_wait_queue = NULL;
}
if (NULL != gp_scheduler_lock) {
#if defined(MALI_UPPER_HALF_SCHEDULING)
_mali_osk_spinlock_irq_term(gp_scheduler_lock);
#else
_mali_osk_spinlock_term(gp_scheduler_lock);
#endif /* defined(MALI_UPPER_HALF_SCHEDULING) */
gp_scheduler_lock = NULL;
}
return ret;
}
void mali_gp_scheduler_terminate(void)
{
MALI_DEBUG_ASSERT( MALI_GP_SLOT_STATE_IDLE == slot.state
|| MALI_GP_SLOT_STATE_DISABLED == slot.state);
MALI_DEBUG_ASSERT_POINTER(slot.group);
mali_group_delete(slot.group);
_mali_osk_wait_queue_term(gp_scheduler_working_wait_queue);
#if defined(MALI_UPPER_HALF_SCHEDULING)
_mali_osk_spinlock_irq_term(gp_scheduler_lock);
#else
_mali_osk_spinlock_term(gp_scheduler_lock);
#endif /* defined(MALI_UPPER_HALF_SCHEDULING) */
}
MALI_STATIC_INLINE void mali_gp_scheduler_lock(void)
{
#if defined(MALI_UPPER_HALF_SCHEDULING)
_mali_osk_spinlock_irq_lock(gp_scheduler_lock);
#else
_mali_osk_spinlock_lock(gp_scheduler_lock);
#endif /* defined(MALI_UPPER_HALF_SCHEDULING) */
MALI_DEBUG_PRINT(5, ("Mali GP scheduler: GP scheduler lock taken\n"));
}
MALI_STATIC_INLINE void mali_gp_scheduler_unlock(void)
{
MALI_DEBUG_PRINT(5, ("Mali GP scheduler: Releasing GP scheduler lock\n"));
#if defined(MALI_UPPER_HALF_SCHEDULING)
_mali_osk_spinlock_irq_unlock(gp_scheduler_lock);
#else
_mali_osk_spinlock_unlock(gp_scheduler_lock);
#endif /* defined(MALI_UPPER_HALF_SCHEDULING) */
}
#if defined(DEBUG)
#define MALI_ASSERT_GP_SCHEDULER_LOCKED() MALI_DEBUG_ASSERT_LOCK_HELD(gp_scheduler_lock)
#else
#define MALI_ASSERT_GP_SCHEDULER_LOCKED() do {} while (0)
#endif /* defined(DEBUG) */
/* Group and scheduler must be locked when entering this function. Both will be unlocked before
* exiting. */
static void mali_gp_scheduler_schedule_internal_and_unlock(void)
{
struct mali_gp_job *job = NULL;
MALI_DEBUG_ASSERT_LOCK_HELD(slot.group->lock);
MALI_DEBUG_ASSERT_LOCK_HELD(gp_scheduler_lock);
if (0 < pause_count || MALI_GP_SLOT_STATE_IDLE != slot.state ||
(_mali_osk_list_empty(&job_queue) && _mali_osk_list_empty(&job_queue_high))) {
mali_gp_scheduler_unlock();
mali_group_unlock(slot.group);
MALI_DEBUG_PRINT(4, ("Mali GP scheduler: Nothing to schedule (paused=%u, idle slots=%u)\n",
pause_count, MALI_GP_SLOT_STATE_IDLE == slot.state ? 1 : 0));
#if defined(CONFIG_GPU_TRACEPOINTS) && defined(CONFIG_TRACEPOINTS)
trace_gpu_sched_switch(mali_gp_get_hw_core_desc(group->gp_core), sched_clock(), 0, 0, 0);
#endif
return; /* Nothing to do, so early out */
}
/* Get next job in queue */
if (!_mali_osk_list_empty(&job_queue_high)) {
job = _MALI_OSK_LIST_ENTRY(job_queue_high.next, struct mali_gp_job, list);
} else {
MALI_DEBUG_ASSERT(!_mali_osk_list_empty(&job_queue));
job = _MALI_OSK_LIST_ENTRY(job_queue.next, struct mali_gp_job, list);
}
MALI_DEBUG_ASSERT_POINTER(job);
/* Remove the job from queue */
_mali_osk_list_del(&job->list);
/* Mark slot as busy */
slot.state = MALI_GP_SLOT_STATE_WORKING;
mali_gp_scheduler_unlock();
MALI_DEBUG_PRINT(3, ("Mali GP scheduler: Starting job %u (0x%08X)\n", mali_gp_job_get_id(job), job));
mali_group_start_gp_job(slot.group, job);
mali_group_unlock(slot.group);
}
void mali_gp_scheduler_schedule(void)
{
mali_group_lock(slot.group);
mali_gp_scheduler_lock();
mali_gp_scheduler_schedule_internal_and_unlock();
}
static void mali_gp_scheduler_return_job_to_user(struct mali_gp_job *job, mali_bool success)
{
_mali_uk_gp_job_finished_s *jobres = job->finished_notification->result_buffer;
_mali_osk_memset(jobres, 0, sizeof(_mali_uk_gp_job_finished_s)); /* @@@@ can be removed once we initialize all members in this struct */
jobres->user_job_ptr = mali_gp_job_get_user_id(job);
if (MALI_TRUE == success) {
jobres->status = _MALI_UK_JOB_STATUS_END_SUCCESS;
} else {
jobres->status = _MALI_UK_JOB_STATUS_END_UNKNOWN_ERR;
}
jobres->heap_current_addr = mali_gp_job_get_current_heap_addr(job);
jobres->perf_counter0 = mali_gp_job_get_perf_counter_value0(job);
jobres->perf_counter1 = mali_gp_job_get_perf_counter_value1(job);
mali_session_send_notification(mali_gp_job_get_session(job), job->finished_notification);
job->finished_notification = NULL;
mali_gp_job_delete(job);
mali_gp_scheduler_job_completed();
}
/* Group must be locked when entering this function. Will be unlocked before exiting. */
void mali_gp_scheduler_job_done(struct mali_group *group, struct mali_gp_job *job, mali_bool success)
{
mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY;
MALI_DEBUG_ASSERT_POINTER(group);
MALI_DEBUG_ASSERT_POINTER(job);
MALI_DEBUG_ASSERT_LOCK_HELD(group->lock);
MALI_DEBUG_ASSERT(slot.group == group);
MALI_DEBUG_PRINT(3, ("Mali GP scheduler: Job %u (0x%08X) completed (%s)\n", mali_gp_job_get_id(job), job, success ? "success" : "failure"));
/* Release tracker. */
schedule_mask |= mali_timeline_tracker_release(&job->tracker);
/* Signal PP job. */
schedule_mask |= mali_gp_job_signal_pp_tracker(job, success);
mali_gp_scheduler_lock();
/* Mark slot as idle again */
slot.state = MALI_GP_SLOT_STATE_IDLE;
/* If paused, then this was the last job, so wake up sleeping workers */
if (pause_count > 0) {
_mali_osk_wait_queue_wake_up(gp_scheduler_working_wait_queue);
}
/* Schedule any queued GP jobs on this group. */
mali_gp_scheduler_schedule_internal_and_unlock();
/* GP is now scheduled, removing it from the mask. */
schedule_mask &= ~MALI_SCHEDULER_MASK_GP;
if (MALI_SCHEDULER_MASK_EMPTY != schedule_mask) {
/* Releasing the tracker activated other jobs that need scheduling. */
mali_scheduler_schedule_from_mask(schedule_mask, MALI_FALSE);
}
/* Sends the job end message to user space and free the job object */
mali_gp_scheduler_return_job_to_user(job, success);
}
void mali_gp_scheduler_oom(struct mali_group *group, struct mali_gp_job *job)
{
_mali_uk_gp_job_suspended_s * jobres;
_mali_osk_notification_t * notification;
mali_gp_scheduler_lock();
notification = job->oom_notification;
job->oom_notification = NULL;
slot.returned_cookie = mali_gp_job_get_id(job);
jobres = (_mali_uk_gp_job_suspended_s *)notification->result_buffer;
jobres->user_job_ptr = mali_gp_job_get_user_id(job);
jobres->cookie = mali_gp_job_get_id(job);
mali_gp_scheduler_unlock();
mali_session_send_notification(mali_gp_job_get_session(job), notification);
/*
* If this function failed, then we could return the job to user space right away,
* but there is a job timer anyway that will do that eventually.
* This is not exactly a common case anyway.
*/
}
void mali_gp_scheduler_suspend(void)
{
mali_gp_scheduler_lock();
pause_count++; /* Increment the pause_count so that no more jobs will be scheduled */
mali_gp_scheduler_unlock();
_mali_osk_wait_queue_wait_event(gp_scheduler_working_wait_queue, mali_gp_scheduler_is_suspended, NULL);
}
void mali_gp_scheduler_resume(void)
{
mali_gp_scheduler_lock();
pause_count--; /* Decrement pause_count to allow scheduling again (if it reaches 0) */
mali_gp_scheduler_unlock();
if (0 == pause_count) {
mali_gp_scheduler_schedule();
}
}
mali_timeline_point mali_gp_scheduler_submit_job(struct mali_session_data *session, struct mali_gp_job *job)
{
mali_timeline_point point;
MALI_DEBUG_ASSERT_POINTER(session);
MALI_DEBUG_ASSERT_POINTER(job);
mali_gp_scheduler_job_queued();
/* Add job to Timeline system. */
point = mali_timeline_system_add_tracker(session->timeline_system, &job->tracker, MALI_TIMELINE_GP);
return point;
}
_mali_osk_errcode_t _mali_ukk_gp_start_job(void *ctx, _mali_uk_gp_start_job_s *uargs)
{
struct mali_session_data *session;
struct mali_gp_job *job;
mali_timeline_point point;
u32 __user *timeline_point_ptr = NULL;
MALI_DEBUG_ASSERT_POINTER(uargs);
MALI_DEBUG_ASSERT_POINTER(ctx);
session = (struct mali_session_data*)ctx;
job = mali_gp_job_create(session, uargs, mali_scheduler_get_new_id(), NULL);
if (NULL == job) {
MALI_PRINT_ERROR(("Failed to create GP job.\n"));
return _MALI_OSK_ERR_NOMEM;
}
timeline_point_ptr = (u32 __user *) job->uargs.timeline_point_ptr;
point = mali_gp_scheduler_submit_job(session, job);
if (0 != _mali_osk_put_user(((u32) point), timeline_point_ptr)) {
/* Let user space know that something failed after the job was started. */
return _MALI_OSK_ERR_ITEM_NOT_FOUND;
}
return _MALI_OSK_ERR_OK;
}
_mali_osk_errcode_t _mali_ukk_get_gp_number_of_cores(_mali_uk_get_gp_number_of_cores_s *args)
{
MALI_DEBUG_ASSERT_POINTER(args);
MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS);
args->number_of_cores = 1;
return _MALI_OSK_ERR_OK;
}
_mali_osk_errcode_t _mali_ukk_get_gp_core_version(_mali_uk_get_gp_core_version_s *args)
{
MALI_DEBUG_ASSERT_POINTER(args);
MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS);
args->version = gp_version;
return _MALI_OSK_ERR_OK;
}
_mali_osk_errcode_t _mali_ukk_gp_suspend_response(_mali_uk_gp_suspend_response_s *args)
{
struct mali_session_data *session;
struct mali_gp_job *resumed_job;
_mali_osk_notification_t *new_notification = 0;
MALI_DEBUG_ASSERT_POINTER(args);
if (NULL == args->ctx) {
return _MALI_OSK_ERR_INVALID_ARGS;
}
session = (struct mali_session_data*)args->ctx;
if (NULL == session) {
return _MALI_OSK_ERR_FAULT;
}
if (_MALIGP_JOB_RESUME_WITH_NEW_HEAP == args->code) {
new_notification = _mali_osk_notification_create(_MALI_NOTIFICATION_GP_STALLED, sizeof(_mali_uk_gp_job_suspended_s));
if (NULL == new_notification) {
MALI_PRINT_ERROR(("Mali GP scheduler: Failed to allocate notification object. Will abort GP job.\n"));
mali_group_lock(slot.group);
mali_group_abort_gp_job(slot.group, args->cookie);
mali_group_unlock(slot.group);
return _MALI_OSK_ERR_FAULT;
}
}
mali_group_lock(slot.group);
if (_MALIGP_JOB_RESUME_WITH_NEW_HEAP == args->code) {
MALI_DEBUG_PRINT(3, ("Mali GP scheduler: Resuming job %u with new heap; 0x%08X - 0x%08X\n", args->cookie, args->arguments[0], args->arguments[1]));
resumed_job = mali_group_resume_gp_with_new_heap(slot.group, args->cookie, args->arguments[0], args->arguments[1]);
if (NULL != resumed_job) {
resumed_job->oom_notification = new_notification;
mali_group_unlock(slot.group);
return _MALI_OSK_ERR_OK;
} else {
mali_group_unlock(slot.group);
_mali_osk_notification_delete(new_notification);
return _MALI_OSK_ERR_FAULT;
}
}
MALI_DEBUG_PRINT(2, ("Mali GP scheduler: Aborting job %u, no new heap provided\n", args->cookie));
mali_group_abort_gp_job(slot.group, args->cookie);
mali_group_unlock(slot.group);
return _MALI_OSK_ERR_OK;
}
void mali_gp_scheduler_abort_session(struct mali_session_data *session)
{
struct mali_gp_job *job, *tmp;
_MALI_OSK_LIST_HEAD_STATIC_INIT(removed_jobs);
MALI_DEBUG_ASSERT_POINTER(session);
MALI_DEBUG_ASSERT(session->is_aborting);
MALI_DEBUG_PRINT(3, ("Mali GP scheduler: Aborting all jobs from session 0x%08X.\n", session));
mali_gp_scheduler_lock();
/* Find all jobs from the aborting session. */
_MALI_OSK_LIST_FOREACHENTRY(job, tmp, &job_queue, struct mali_gp_job, list) {
if (job->session == session) {
MALI_DEBUG_PRINT(3, ("Mali GP scheduler: Removing job %u (0x%08X) from queue.\n", mali_gp_job_get_id(job), job));
_mali_osk_list_move(&job->list, &removed_jobs);
}
}
/* Find all high priority jobs from the aborting session. */
_MALI_OSK_LIST_FOREACHENTRY(job, tmp, &job_queue_high, struct mali_gp_job, list) {
if (job->session == session) {
MALI_DEBUG_PRINT(3, ("Mali GP scheduler: Removing job %u (0x%08X) from queue.\n", mali_gp_job_get_id(job), job));
_mali_osk_list_move(&job->list, &removed_jobs);
}
}
mali_gp_scheduler_unlock();
/* Release and delete all found jobs from the aborting session. */
_MALI_OSK_LIST_FOREACHENTRY(job, tmp, &removed_jobs, struct mali_gp_job, list) {
mali_timeline_tracker_release(&job->tracker);
mali_gp_job_signal_pp_tracker(job, MALI_FALSE);
mali_gp_job_delete(job);
mali_gp_scheduler_job_completed();
}
/* Abort any running jobs from the session. */
mali_group_abort_session(slot.group, session);
}
static mali_bool mali_gp_scheduler_is_suspended(void *data)
{
mali_bool ret;
/* This callback does not use the data pointer. */
MALI_IGNORE(data);
mali_gp_scheduler_lock();
ret = pause_count > 0 && (slot.state == MALI_GP_SLOT_STATE_IDLE || slot.state == MALI_GP_SLOT_STATE_DISABLED);
mali_gp_scheduler_unlock();
return ret;
}
#if MALI_STATE_TRACKING
u32 mali_gp_scheduler_dump_state(char *buf, u32 size)
{
int n = 0;
n += _mali_osk_snprintf(buf + n, size - n, "GP\n");
n += _mali_osk_snprintf(buf + n, size - n, "\tQueue is %s\n", _mali_osk_list_empty(&job_queue) ? "empty" : "not empty");
n += _mali_osk_snprintf(buf + n, size - n, "\tHigh priority queue is %s\n", _mali_osk_list_empty(&job_queue_high) ? "empty" : "not empty");
n += mali_group_dump_state(slot.group, buf + n, size - n);
n += _mali_osk_snprintf(buf + n, size - n, "\n");
return n;
}
#endif
void mali_gp_scheduler_reset_all_groups(void)
{
if (NULL != slot.group) {
mali_group_lock(slot.group);
mali_group_reset(slot.group);
mali_group_unlock(slot.group);
}
}
void mali_gp_scheduler_zap_all_active(struct mali_session_data *session)
{
if (NULL != slot.group) {
mali_group_zap_session(slot.group, session);
}
}
void mali_gp_scheduler_enable_group(struct mali_group *group)
{
MALI_DEBUG_ASSERT_POINTER(group);
MALI_DEBUG_ASSERT(slot.group == group);
MALI_DEBUG_PRINT(2, ("Mali GP scheduler: enabling gp group %p\n", group));
mali_group_lock(group);
if (MALI_GROUP_STATE_DISABLED != group->state) {
mali_group_unlock(group);
MALI_DEBUG_PRINT(2, ("Mali GP scheduler: gp group %p already enabled\n", group));
return;
}
mali_gp_scheduler_lock();
MALI_DEBUG_ASSERT(MALI_GROUP_STATE_DISABLED == group->state);
MALI_DEBUG_ASSERT(MALI_GP_SLOT_STATE_DISABLED == slot.state);
slot.state = MALI_GP_SLOT_STATE_IDLE;
group->state = MALI_GROUP_STATE_IDLE;
mali_group_power_on_group(group);
mali_group_reset(group);
/* Pick up any jobs that might have been queued while the GP group was disabled. */
mali_gp_scheduler_schedule_internal_and_unlock();
}
void mali_gp_scheduler_disable_group(struct mali_group *group)
{
MALI_DEBUG_ASSERT_POINTER(group);
MALI_DEBUG_ASSERT(slot.group == group);
MALI_DEBUG_PRINT(2, ("Mali GP scheduler: disabling gp group %p\n", group));
mali_gp_scheduler_suspend();
mali_group_lock(group);
mali_gp_scheduler_lock();
MALI_DEBUG_ASSERT( MALI_GROUP_STATE_IDLE == group->state
|| MALI_GROUP_STATE_DISABLED == group->state);
if (MALI_GROUP_STATE_DISABLED == group->state) {
MALI_DEBUG_ASSERT(MALI_GP_SLOT_STATE_DISABLED == slot.state);
MALI_DEBUG_PRINT(2, ("Mali GP scheduler: gp group %p already disabled\n", group));
} else {
MALI_DEBUG_ASSERT(MALI_GP_SLOT_STATE_IDLE == slot.state);
slot.state = MALI_GP_SLOT_STATE_DISABLED;
group->state = MALI_GROUP_STATE_DISABLED;
mali_group_power_off_group(group, MALI_TRUE);
}
mali_gp_scheduler_unlock();
mali_group_unlock(group);
mali_gp_scheduler_resume();
}
static mali_scheduler_mask mali_gp_scheduler_queue_job(struct mali_gp_job *job)
{
_mali_osk_list_t *queue = NULL;
mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY;
struct mali_gp_job *iter, *tmp;
MALI_DEBUG_ASSERT_POINTER(job);
MALI_DEBUG_ASSERT_POINTER(job->session);
MALI_DEBUG_ASSERT_LOCK_HELD(gp_scheduler_lock);
_mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_SINGLE | MALI_PROFILING_EVENT_CHANNEL_SOFTWARE | MALI_PROFILING_EVENT_REASON_SINGLE_SW_GP_ENQUEUE, job->pid, job->tid, job->uargs.frame_builder_id, job->uargs.flush_id, 0);
job->cache_order = mali_scheduler_get_new_cache_order();
/* Determine which queue the job should be added to. */
if (job->session->use_high_priority_job_queue) {
queue = &job_queue_high;
} else {
queue = &job_queue;
}
/* Find position in queue where job should be added. */
_MALI_OSK_LIST_FOREACHENTRY_REVERSE(iter, tmp, queue, struct mali_gp_job, list) {
if (mali_gp_job_is_after(job, iter)) {
break;
}
}
/* Add job to queue. */
_mali_osk_list_add(&job->list, &iter->list);
/* Set schedule bitmask if the GP core is idle. */
if (MALI_GP_SLOT_STATE_IDLE == slot.state) {
schedule_mask |= MALI_SCHEDULER_MASK_GP;
}
#if defined(CONFIG_GPU_TRACEPOINTS) && defined(CONFIG_TRACEPOINTS)
trace_gpu_job_enqueue(mali_gp_job_get_tid(job), mali_gp_job_get_id(job), "GP");
#endif
MALI_DEBUG_PRINT(3, ("Mali GP scheduler: Job %u (0x%08X) queued\n", mali_gp_job_get_id(job), job));
return schedule_mask;
}
mali_scheduler_mask mali_gp_scheduler_activate_job(struct mali_gp_job *job)
{
mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY;
MALI_DEBUG_ASSERT_POINTER(job);
MALI_DEBUG_ASSERT_POINTER(job->session);
MALI_DEBUG_PRINT(4, ("Mali GP scheduler: Timeline activation for job %u (0x%08X).\n", mali_gp_job_get_id(job), job));
mali_gp_scheduler_lock();
if (unlikely(job->session->is_aborting)) {
/* Before checking if the session is aborting, the scheduler must be locked. */
MALI_DEBUG_ASSERT_LOCK_HELD(gp_scheduler_lock);
MALI_DEBUG_PRINT(3, ("Mali GP scheduler: Job %u (0x%08X) activated while session is aborting.\n", mali_gp_job_get_id(job), job));
/* This job should not be on any list. */
MALI_DEBUG_ASSERT(_mali_osk_list_empty(&job->list));
mali_gp_scheduler_unlock();
/* Release tracker and delete job. */
mali_timeline_tracker_release(&job->tracker);
mali_gp_job_signal_pp_tracker(job, MALI_FALSE);
mali_gp_job_delete(job);
mali_gp_scheduler_job_completed();
/* Since we are aborting we ignore the scheduler mask. */
return MALI_SCHEDULER_MASK_EMPTY;
}
/* GP job is ready to run, queue it. */
schedule_mask = mali_gp_scheduler_queue_job(job);
mali_gp_scheduler_unlock();
return schedule_mask;
}
static void mali_gp_scheduler_job_queued(void)
{
/* We hold a PM reference for every job we hold queued (and running) */
_mali_osk_pm_dev_ref_add();
if (mali_utilization_enabled()) {
/*
* We cheat a little bit by counting the PP as busy from the time a GP job is queued.
* This will be fine because we only loose the tiny idle gap between jobs, but
* we will instead get less utilization work to do (less locks taken)
*/
mali_utilization_gp_start();
}
}
static void mali_gp_scheduler_job_completed(void)
{
/* Release the PM reference we got in the mali_gp_scheduler_job_queued() function */
_mali_osk_pm_dev_ref_dec();
if (mali_utilization_enabled()) {
mali_utilization_gp_end();
}
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright (C) 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.
*/
#ifndef __MALI_GP_SCHEDULER_H__
#define __MALI_GP_SCHEDULER_H__
#include "mali_osk.h"
#include "mali_gp_job.h"
#include "mali_group.h"
_mali_osk_errcode_t mali_gp_scheduler_initialize(void);
void mali_gp_scheduler_terminate(void);
void mali_gp_scheduler_job_done(struct mali_group *group, struct mali_gp_job *job, mali_bool success);
void mali_gp_scheduler_oom(struct mali_group *group, struct mali_gp_job *job);
u32 mali_gp_scheduler_dump_state(char *buf, u32 size);
void mali_gp_scheduler_suspend(void);
void mali_gp_scheduler_resume(void);
/**
* @brief Abort all running and queued GP jobs from session.
*
* This functions aborts all GP jobs from the specified session. Queued jobs are removed from the
* queue and jobs currently running on a core will be aborted.
*
* @param session Session that is aborting.
*/
void mali_gp_scheduler_abort_session(struct mali_session_data *session);
/**
* @brief Reset all groups
*
* This function resets all groups known by the GP scheuduler. This must be
* called after the Mali HW has been powered on in order to reset the HW.
*/
void mali_gp_scheduler_reset_all_groups(void);
/**
* @brief Zap TLB on all groups with \a session active
*
* The scheculer will zap the session on all groups it owns.
*/
void mali_gp_scheduler_zap_all_active(struct mali_session_data *session);
/**
* @brief Re-enable a group that has been disabled with mali_gp_scheduler_disable_group
*
* If a Mali PMU is present, the group will be powered back on and added back
* into the GP scheduler.
*
* @param group Pointer to the group to enable
*/
void mali_gp_scheduler_enable_group(struct mali_group *group);
/**
* @brief Disable a group
*
* The group will be taken out of the GP scheduler and powered off, if a Mali
* PMU is present.
*
* @param group Pointer to the group to disable
*/
void mali_gp_scheduler_disable_group(struct mali_group *group);
/**
* @brief Used by the Timeline system to queue a GP job.
*
* @note @ref mali_scheduler_schedule_from_mask() should be called if this function returns non-zero.
*
* @param job The GP job that is being activated.
*
* @return A scheduling bitmask that can be used to decide if scheduling is necessary after this
* call.
*/
mali_scheduler_mask mali_gp_scheduler_activate_job(struct mali_gp_job *job);
/**
* @brief Schedule queued jobs on idle cores.
*/
void mali_gp_scheduler_schedule(void);
/**
* @brief Submit a GP job to the GP scheduler.
*
* This will add the GP job to the Timeline system.
*
* @param session Session this job belongs to.
* @param job GP job that will be submitted
* @return Point on GP timeline for job.
*/
mali_timeline_point mali_gp_scheduler_submit_job(struct mali_session_data *session, struct mali_gp_job *job);
#endif /* __MALI_GP_SCHEDULER_H__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,309 @@
/*
* Copyright (C) 2011-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.
*/
#ifndef __MALI_GROUP_H__
#define __MALI_GROUP_H__
#include "linux/jiffies.h"
#include "mali_osk.h"
#include "mali_l2_cache.h"
#include "mali_mmu.h"
#include "mali_gp.h"
#include "mali_pp.h"
#include "mali_session.h"
/**
* @brief Default max runtime [ms] for a core job - used by timeout timers
*/
#define MALI_MAX_JOB_RUNTIME_DEFAULT 4000
/** @brief A mali group object represents a MMU and a PP and/or a GP core.
*
*/
#define MALI_MAX_NUMBER_OF_GROUPS 10
enum mali_group_core_state {
MALI_GROUP_STATE_IDLE,
MALI_GROUP_STATE_WORKING,
MALI_GROUP_STATE_OOM,
MALI_GROUP_STATE_IN_VIRTUAL,
MALI_GROUP_STATE_JOINING_VIRTUAL,
MALI_GROUP_STATE_LEAVING_VIRTUAL,
MALI_GROUP_STATE_DISABLED,
};
/* Forward declaration from mali_pm_domain.h */
struct mali_pm_domain;
/**
* The structure represents a render group
* A render group is defined by all the cores that share the same Mali MMU
*/
struct mali_group {
struct mali_mmu_core *mmu;
struct mali_session_data *session;
mali_bool power_is_on;
enum mali_group_core_state state;
struct mali_gp_core *gp_core;
struct mali_gp_job *gp_running_job;
struct mali_pp_core *pp_core;
struct mali_pp_job *pp_running_job;
u32 pp_running_sub_job;
struct mali_l2_cache_core *l2_cache_core[2];
u32 l2_cache_core_ref_count[2];
struct mali_dlbu_core *dlbu_core;
struct mali_bcast_unit *bcast_core;
#ifdef MALI_UPPER_HALF_SCHEDULING
_mali_osk_spinlock_irq_t *lock;
#else
_mali_osk_spinlock_t *lock;
#endif
_mali_osk_list_t pp_scheduler_list;
/* List used for virtual groups. For a virtual group, the list represents the
* head element. */
_mali_osk_list_t group_list;
struct mali_group *pm_domain_list;
struct mali_pm_domain *pm_domain;
/* Parent virtual group (if any) */
struct mali_group *parent_group;
_mali_osk_wq_work_t *bottom_half_work_mmu;
_mali_osk_wq_work_t *bottom_half_work_gp;
_mali_osk_wq_work_t *bottom_half_work_pp;
_mali_osk_timer_t *timeout_timer;
mali_bool core_timed_out;
};
/** @brief Create a new Mali group object
*
* @param cluster Pointer to the cluster to which the group is connected.
* @param mmu Pointer to the MMU that defines this group
* @return A pointer to a new group object
*/
struct mali_group *mali_group_create(struct mali_l2_cache_core *core,
struct mali_dlbu_core *dlbu,
struct mali_bcast_unit *bcast);
_mali_osk_errcode_t mali_group_add_mmu_core(struct mali_group *group, struct mali_mmu_core* mmu_core);
void mali_group_remove_mmu_core(struct mali_group *group);
_mali_osk_errcode_t mali_group_add_gp_core(struct mali_group *group, struct mali_gp_core* gp_core);
void mali_group_remove_gp_core(struct mali_group *group);
_mali_osk_errcode_t mali_group_add_pp_core(struct mali_group *group, struct mali_pp_core* pp_core);
void mali_group_remove_pp_core(struct mali_group *group);
void mali_group_set_pm_domain(struct mali_group *group, struct mali_pm_domain *domain);
void mali_group_delete(struct mali_group *group);
/** @brief Virtual groups */
void mali_group_add_group(struct mali_group *parent, struct mali_group *child, mali_bool update_hw);
void mali_group_remove_group(struct mali_group *parent, struct mali_group *child);
struct mali_group *mali_group_acquire_group(struct mali_group *parent);
MALI_STATIC_INLINE mali_bool mali_group_is_virtual(struct mali_group *group)
{
#if defined(CONFIG_MALI450)
return (NULL != group->dlbu_core);
#else
return MALI_FALSE;
#endif
}
/** @brief Check if a group is considered as part of a virtual group
*
* @note A group is considered to be "part of" a virtual group also during the transition
* in to / out of the virtual group.
*/
MALI_STATIC_INLINE mali_bool mali_group_is_in_virtual(struct mali_group *group)
{
#if defined(CONFIG_MALI450)
return (MALI_GROUP_STATE_IN_VIRTUAL == group->state ||
MALI_GROUP_STATE_JOINING_VIRTUAL == group->state ||
MALI_GROUP_STATE_LEAVING_VIRTUAL == group->state);
#else
return MALI_FALSE;
#endif
}
/** @brief Reset group
*
* This function will reset the entire group, including all the cores present in the group.
*
* @param group Pointer to the group to reset
*/
void mali_group_reset(struct mali_group *group);
/** @brief Zap MMU TLB on all groups
*
* Zap TLB on group if \a session is active.
*/
void mali_group_zap_session(struct mali_group* group, struct mali_session_data *session);
/** @brief Get pointer to GP core object
*/
struct mali_gp_core* mali_group_get_gp_core(struct mali_group *group);
/** @brief Get pointer to PP core object
*/
struct mali_pp_core* mali_group_get_pp_core(struct mali_group *group);
/** @brief Lock group object
*
* Most group functions will lock the group object themselves. The expection is
* the group_bottom_half which requires the group to be locked on entry.
*
* @param group Pointer to group to lock
*/
void mali_group_lock(struct mali_group *group);
/** @brief Unlock group object
*
* @param group Pointer to group to unlock
*/
void mali_group_unlock(struct mali_group *group);
#ifdef DEBUG
void mali_group_assert_locked(struct mali_group *group);
#define MALI_ASSERT_GROUP_LOCKED(group) mali_group_assert_locked(group)
#else
#define MALI_ASSERT_GROUP_LOCKED(group)
#endif
/** @brief Start GP job
*/
void mali_group_start_gp_job(struct mali_group *group, struct mali_gp_job *job);
/** @brief Start fragment of PP job
*/
void mali_group_start_pp_job(struct mali_group *group, struct mali_pp_job *job, u32 sub_job);
/** @brief Resume GP job that suspended waiting for more heap memory
*/
struct mali_gp_job *mali_group_resume_gp_with_new_heap(struct mali_group *group, u32 job_id, u32 start_addr, u32 end_addr);
/** @brief Abort GP job
*
* Used to abort suspended OOM jobs when user space failed to allocte more memory.
*/
void mali_group_abort_gp_job(struct mali_group *group, u32 job_id);
/** @brief Abort all GP jobs from \a session
*
* Used on session close when terminating all running and queued jobs from \a session.
*/
void mali_group_abort_session(struct mali_group *group, struct mali_session_data *session);
mali_bool mali_group_power_is_on(struct mali_group *group);
void mali_group_power_on_group(struct mali_group *group);
void mali_group_power_off_group(struct mali_group *group, mali_bool power_status);
void mali_group_power_on(void);
/** @brief Prepare group for power off
*
* Update the group's state and prepare for the group to be powered off.
*
* If do_power_change is MALI_FALSE group session will be set to NULL so that
* no more activity will happen to this group, but the power state flag will be
* left unchanged.
*
* @do_power_change MALI_TRUE if power status is to be updated
*/
void mali_group_power_off(mali_bool do_power_change);
struct mali_group *mali_group_get_glob_group(u32 index);
u32 mali_group_get_glob_num_groups(void);
u32 mali_group_dump_state(struct mali_group *group, char *buf, u32 size);
/* MMU-related functions */
_mali_osk_errcode_t mali_group_upper_half_mmu(void * data);
/* GP-related functions */
_mali_osk_errcode_t mali_group_upper_half_gp(void *data);
/* PP-related functions */
_mali_osk_errcode_t mali_group_upper_half_pp(void *data);
/** @brief Check if group is enabled
*
* @param group group to check
* @return MALI_TRUE if enabled, MALI_FALSE if not
*/
mali_bool mali_group_is_enabled(struct mali_group *group);
/** @brief Enable group
*
* An enabled job is put on the idle scheduler list and can be used to handle jobs. Does nothing if
* group is already enabled.
*
* @param group group to enable
*/
void mali_group_enable(struct mali_group *group);
/** @brief Disable group
*
* A disabled group will no longer be used by the scheduler. If part of a virtual group, the group
* will be removed before being disabled. Cores part of a disabled group is safe to power down.
*
* @param group group to disable
*/
void mali_group_disable(struct mali_group *group);
MALI_STATIC_INLINE mali_bool mali_group_virtual_disable_if_empty(struct mali_group *group)
{
mali_bool empty = MALI_FALSE;
MALI_ASSERT_GROUP_LOCKED(group);
MALI_DEBUG_ASSERT(mali_group_is_virtual(group));
if (_mali_osk_list_empty(&group->group_list)) {
group->state = MALI_GROUP_STATE_DISABLED;
group->session = NULL;
empty = MALI_TRUE;
}
return empty;
}
MALI_STATIC_INLINE mali_bool mali_group_virtual_enable_if_empty(struct mali_group *group)
{
mali_bool empty = MALI_FALSE;
MALI_ASSERT_GROUP_LOCKED(group);
MALI_DEBUG_ASSERT(mali_group_is_virtual(group));
if (_mali_osk_list_empty(&group->group_list)) {
MALI_DEBUG_ASSERT(MALI_GROUP_STATE_DISABLED == group->state);
group->state = MALI_GROUP_STATE_IDLE;
empty = MALI_TRUE;
}
return empty;
}
/* Get group used l2 domain and core domain ref */
void mali_group_get_pm_domain_ref(struct mali_group *group);
/* Put group used l2 domain and core domain ref */
void mali_group_put_pm_domain_ref(struct mali_group *group);
#endif /* __MALI_GROUP_H__ */

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2011-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.
*/
#include "mali_hw_core.h"
#include "mali_osk.h"
#include "mali_kernel_common.h"
#include "mali_osk_mali.h"
_mali_osk_errcode_t mali_hw_core_create(struct mali_hw_core *core, const _mali_osk_resource_t *resource, u32 reg_size)
{
core->phys_addr = resource->base;
core->phys_offset = resource->base - _mali_osk_resource_base_address();
core->description = resource->description;
core->size = reg_size;
MALI_DEBUG_ASSERT(core->phys_offset < core->phys_addr);
if (_MALI_OSK_ERR_OK == _mali_osk_mem_reqregion(core->phys_addr, core->size, core->description)) {
core->mapped_registers = _mali_osk_mem_mapioregion(core->phys_addr, core->size, core->description);
if (NULL != core->mapped_registers) {
return _MALI_OSK_ERR_OK;
} else {
MALI_PRINT_ERROR(("Failed to map memory region for core %s at phys_addr 0x%08X\n", core->description, core->phys_addr));
}
_mali_osk_mem_unreqregion(core->phys_addr, core->size);
} else {
MALI_PRINT_ERROR(("Failed to request memory region for core %s at phys_addr 0x%08X\n", core->description, core->phys_addr));
}
return _MALI_OSK_ERR_FAULT;
}
void mali_hw_core_delete(struct mali_hw_core *core)
{
_mali_osk_mem_unmapioregion(core->phys_addr, core->size, core->mapped_registers);
core->mapped_registers = NULL;
_mali_osk_mem_unreqregion(core->phys_addr, core->size);
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright (C) 2011-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.
*/
#ifndef __MALI_HW_CORE_H__
#define __MALI_HW_CORE_H__
#include "mali_osk.h"
#include "mali_kernel_common.h"
/**
* The common parts for all Mali HW cores (GP, PP, MMU, L2 and PMU)
* This struct is embedded inside all core specific structs.
*/
struct mali_hw_core {
u32 phys_addr; /**< Physical address of the registers */
u32 phys_offset; /**< Offset from start of Mali to registers */
u32 size; /**< Size of registers */
mali_io_address mapped_registers; /**< Virtual mapping of the registers */
const char* description; /**< Name of unit (as specified in device configuration) */
};
#define MALI_REG_POLL_COUNT_FAST 1000
#define MALI_REG_POLL_COUNT_SLOW 1000000
_mali_osk_errcode_t mali_hw_core_create(struct mali_hw_core *core, const _mali_osk_resource_t *resource, u32 reg_size);
void mali_hw_core_delete(struct mali_hw_core *core);
MALI_STATIC_INLINE u32 mali_hw_core_register_read(struct mali_hw_core *core, u32 relative_address)
{
u32 read_val;
read_val = _mali_osk_mem_ioread32(core->mapped_registers, relative_address);
MALI_DEBUG_PRINT(6, ("register_read for core %s, relative addr=0x%04X, val=0x%08X\n",
core->description, relative_address, read_val));
return read_val;
}
MALI_STATIC_INLINE void mali_hw_core_register_write_relaxed(struct mali_hw_core *core, u32 relative_address, u32 new_val)
{
MALI_DEBUG_PRINT(6, ("register_write_relaxed for core %s, relative addr=0x%04X, val=0x%08X\n",
core->description, relative_address, new_val));
_mali_osk_mem_iowrite32_relaxed(core->mapped_registers, relative_address, new_val);
}
/* Conditionally write a register.
* The register will only be written if the new value is different from the old_value.
* If the new value is different, the old value will also be updated */
MALI_STATIC_INLINE void mali_hw_core_register_write_relaxed_conditional(struct mali_hw_core *core, u32 relative_address, u32 new_val, const u32 old_val)
{
MALI_DEBUG_PRINT(6, ("register_write_relaxed for core %s, relative addr=0x%04X, val=0x%08X\n",
core->description, relative_address, new_val));
if(old_val != new_val) {
_mali_osk_mem_iowrite32_relaxed(core->mapped_registers, relative_address, new_val);
}
}
MALI_STATIC_INLINE void mali_hw_core_register_write(struct mali_hw_core *core, u32 relative_address, u32 new_val)
{
MALI_DEBUG_PRINT(6, ("register_write for core %s, relative addr=0x%04X, val=0x%08X\n",
core->description, relative_address, new_val));
_mali_osk_mem_iowrite32(core->mapped_registers, relative_address, new_val);
}
MALI_STATIC_INLINE void mali_hw_core_register_write_array_relaxed(struct mali_hw_core *core, u32 relative_address, u32 *write_array, u32 nr_of_regs)
{
u32 i;
MALI_DEBUG_PRINT(6, ("register_write_array: for core %s, relative addr=0x%04X, nr of regs=%u\n",
core->description,relative_address, nr_of_regs));
/* Do not use burst writes against the registers */
for (i = 0; i< nr_of_regs; i++) {
mali_hw_core_register_write_relaxed(core, relative_address + i*4, write_array[i]);
}
}
/* Conditionally write a set of registers.
* The register will only be written if the new value is different from the old_value.
* If the new value is different, the old value will also be updated */
MALI_STATIC_INLINE void mali_hw_core_register_write_array_relaxed_conditional(struct mali_hw_core *core, u32 relative_address, u32 *write_array, u32 nr_of_regs, const u32* old_array)
{
u32 i;
MALI_DEBUG_PRINT(6, ("register_write_array: for core %s, relative addr=0x%04X, nr of regs=%u\n",
core->description,relative_address, nr_of_regs));
/* Do not use burst writes against the registers */
for (i = 0; i< nr_of_regs; i++) {
if(old_array[i] != write_array[i]) {
mali_hw_core_register_write_relaxed(core, relative_address + i*4, write_array[i]);
}
}
}
#endif /* __MALI_HW_CORE_H__ */

View File

@@ -0,0 +1,175 @@
/*
* Copyright (C) 2010, 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.
*/
#ifndef __MALI_KERNEL_COMMON_H__
#define __MALI_KERNEL_COMMON_H__
#include "mali_osk.h"
/* Make sure debug is defined when it should be */
#ifndef DEBUG
#if defined(_DEBUG)
#define DEBUG
#endif
#endif
/* The file include several useful macros for error checking, debugging and printing.
* - MALI_PRINTF(...) Do not use this function: Will be included in Release builds.
* - MALI_DEBUG_PRINT(nr, (X) ) Prints the second argument if nr<=MALI_DEBUG_LEVEL.
* - MALI_DEBUG_ERROR( (X) ) Prints an errortext, a source trace, and the given error message.
* - MALI_DEBUG_ASSERT(exp,(X)) If the asserted expr is false, the program will exit.
* - MALI_DEBUG_ASSERT_POINTER(pointer) Triggers if the pointer is a zero pointer.
* - MALI_DEBUG_CODE( X ) The code inside the macro is only compiled in Debug builds.
*
* The (X) means that you must add an extra parenthesis around the argumentlist.
*
* The printf function: MALI_PRINTF(...) is routed to _mali_osk_debugmsg
*
* Suggested range for the DEBUG-LEVEL is [1:6] where
* [1:2] Is messages with highest priority, indicate possible errors.
* [3:4] Is messages with medium priority, output important variables.
* [5:6] Is messages with low priority, used during extensive debugging.
*/
/**
* Fundamental error macro. Reports an error code. This is abstracted to allow us to
* easily switch to a different error reporting method if we want, and also to allow
* us to search for error returns easily.
*
* Note no closing semicolon - this is supplied in typical usage:
*
* MALI_ERROR(MALI_ERROR_OUT_OF_MEMORY);
*/
#define MALI_ERROR(error_code) return (error_code)
/**
* Basic error macro, to indicate success.
* Note no closing semicolon - this is supplied in typical usage:
*
* MALI_SUCCESS;
*/
#define MALI_SUCCESS MALI_ERROR(_MALI_OSK_ERR_OK)
/**
* Basic error macro. This checks whether the given condition is true, and if not returns
* from this function with the supplied error code. This is a macro so that we can override it
* for stress testing.
*
* Note that this uses the do-while-0 wrapping to ensure that we don't get problems with dangling
* else clauses. Note also no closing semicolon - this is supplied in typical usage:
*
* MALI_CHECK((p!=NULL), ERROR_NO_OBJECT);
*/
#define MALI_CHECK(condition, error_code) do { if(!(condition)) MALI_ERROR(error_code); } while(0)
/**
* Error propagation macro. If the expression given is anything other than _MALI_OSK_NO_ERROR,
* then the value is returned from the enclosing function as an error code. This effectively
* acts as a guard clause, and propagates error values up the call stack. This uses a
* temporary value to ensure that the error expression is not evaluated twice.
* If the counter for forcing a failure has been set using _mali_force_error, this error will be
* returned without evaluating the expression in MALI_CHECK_NO_ERROR
*/
#define MALI_CHECK_NO_ERROR(expression) \
do { _mali_osk_errcode_t _check_no_error_result=(expression); \
if(_check_no_error_result != _MALI_OSK_ERR_OK) \
MALI_ERROR(_check_no_error_result); \
} while(0)
/**
* Pointer check macro. Checks non-null pointer.
*/
#define MALI_CHECK_NON_NULL(pointer, error_code) MALI_CHECK( ((pointer)!=NULL), (error_code) )
/**
* Error macro with goto. This checks whether the given condition is true, and if not jumps
* to the specified label using a goto. The label must therefore be local to the function in
* which this macro appears. This is most usually used to execute some clean-up code before
* exiting with a call to ERROR.
*
* Like the other macros, this is a macro to allow us to override the condition if we wish,
* e.g. to force an error during stress testing.
*/
#define MALI_CHECK_GOTO(condition, label) do { if(!(condition)) goto label; } while(0)
/**
* Explicitly ignore a parameter passed into a function, to suppress compiler warnings.
* Should only be used with parameter names.
*/
#define MALI_IGNORE(x) x=x
#define MALI_PRINTF(args) _mali_osk_dbgmsg args;
#define MALI_PRINT_ERROR(args) do{ \
MALI_PRINTF(("Mali: ERR: %s\n" ,__FILE__)); \
MALI_PRINTF((" %s()%4d\n ", __FUNCTION__, __LINE__)) ; \
MALI_PRINTF(args); \
MALI_PRINTF(("\n")); \
} while(0)
#define MALI_PRINT(args) do{ \
MALI_PRINTF(("Mali: ")); \
MALI_PRINTF(args); \
} while (0)
#ifdef DEBUG
#ifndef mali_debug_level
extern int mali_debug_level;
#endif
#define MALI_DEBUG_CODE(code) code
#define MALI_DEBUG_PRINT(level, args) do { \
if((level) <= mali_debug_level)\
{MALI_PRINTF(("Mali<" #level ">: ")); MALI_PRINTF(args); } \
} while (0)
#define MALI_DEBUG_PRINT_ERROR(args) MALI_PRINT_ERROR(args)
#define MALI_DEBUG_PRINT_IF(level,condition,args) \
if((condition)&&((level) <= mali_debug_level))\
{MALI_PRINTF(("Mali<" #level ">: ")); MALI_PRINTF(args); }
#define MALI_DEBUG_PRINT_ELSE(level, args)\
else if((level) <= mali_debug_level)\
{ MALI_PRINTF(("Mali<" #level ">: ")); MALI_PRINTF(args); }
/**
* @note these variants of DEBUG ASSERTS will cause a debugger breakpoint
* to be entered (see _mali_osk_break() ). An alternative would be to call
* _mali_osk_abort(), on OSs that support it.
*/
#define MALI_DEBUG_PRINT_ASSERT(condition, args) do {if( !(condition)) { MALI_PRINT_ERROR(args); _mali_osk_break(); } } while(0)
#define MALI_DEBUG_ASSERT_POINTER(pointer) do {if( (pointer)== NULL) {MALI_PRINT_ERROR(("NULL pointer " #pointer)); _mali_osk_break();} } while(0)
#define MALI_DEBUG_ASSERT(condition) do {if( !(condition)) {MALI_PRINT_ERROR(("ASSERT failed: " #condition )); _mali_osk_break();} } while(0)
#else /* DEBUG */
#define MALI_DEBUG_CODE(code)
#define MALI_DEBUG_PRINT(string,args) do {} while(0)
#define MALI_DEBUG_PRINT_ERROR(args) do {} while(0)
#define MALI_DEBUG_PRINT_IF(level,condition,args) do {} while(0)
#define MALI_DEBUG_PRINT_ELSE(level,condition,args) do {} while(0)
#define MALI_DEBUG_PRINT_ASSERT(condition,args) do {} while(0)
#define MALI_DEBUG_ASSERT_POINTER(pointer) do {} while(0)
#define MALI_DEBUG_ASSERT(condition) do {} while(0)
#endif /* DEBUG */
/**
* variables from user space cannot be dereferenced from kernel space; tagging them
* with __user allows the GCC compiler to generate a warning. Other compilers may
* not support this so we define it here as an empty macro if the compiler doesn't
* define it.
*/
#ifndef __user
#define __user
#endif
#endif /* __MALI_KERNEL_COMMON_H__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,56 @@
/*
* Copyright (C) 2010-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.
*/
#ifndef __MALI_KERNEL_CORE_H__
#define __MALI_KERNEL_CORE_H__
#include "mali_osk.h"
typedef enum {
_MALI_PRODUCT_ID_UNKNOWN,
_MALI_PRODUCT_ID_MALI200,
_MALI_PRODUCT_ID_MALI300,
_MALI_PRODUCT_ID_MALI400,
_MALI_PRODUCT_ID_MALI450,
} _mali_product_id_t;
extern mali_bool mali_gpu_class_is_mali450;
_mali_osk_errcode_t mali_initialize_subsystems(void);
void mali_terminate_subsystems(void);
_mali_product_id_t mali_kernel_core_get_product_id(void);
u32 mali_kernel_core_get_gpu_major_version(void);
u32 mali_kernel_core_get_gpu_minor_version(void);
u32 _mali_kernel_core_dump_state(char* buf, u32 size);
MALI_STATIC_INLINE mali_bool mali_is_mali450(void)
{
#if defined(CONFIG_MALI450)
return mali_gpu_class_is_mali450;
#else
return MALI_FALSE;
#endif
}
MALI_STATIC_INLINE mali_bool mali_is_mali400(void)
{
#if !defined(CONFIG_MALI450)
return MALI_TRUE;
#else
return !mali_gpu_class_is_mali450;
#endif
}
#endif /* __MALI_KERNEL_CORE_H__ */

View File

@@ -0,0 +1,173 @@
/*
* Copyright (C) 2010, 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.
*/
#include "mali_kernel_common.h"
#include "mali_kernel_descriptor_mapping.h"
#include "mali_osk.h"
#include "mali_osk_bitops.h"
#define MALI_PAD_INT(x) (((x) + (BITS_PER_LONG - 1)) & ~(BITS_PER_LONG - 1))
/**
* Allocate a descriptor table capable of holding 'count' mappings
* @param count Number of mappings in the table
* @return Pointer to a new table, NULL on error
*/
static mali_descriptor_table * descriptor_table_alloc(int count);
/**
* Free a descriptor table
* @param table The table to free
*/
static void descriptor_table_free(mali_descriptor_table * table);
mali_descriptor_mapping * mali_descriptor_mapping_create(int init_entries, int max_entries)
{
mali_descriptor_mapping * map = _mali_osk_calloc(1, sizeof(mali_descriptor_mapping));
init_entries = MALI_PAD_INT(init_entries);
max_entries = MALI_PAD_INT(max_entries);
if (NULL != map) {
map->table = descriptor_table_alloc(init_entries);
if (NULL != map->table) {
map->lock = _mali_osk_mutex_rw_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_DESCRIPTOR_MAP);
if (NULL != map->lock) {
_mali_osk_set_nonatomic_bit(0, map->table->usage); /* reserve bit 0 to prevent NULL/zero logic to kick in */
map->max_nr_mappings_allowed = max_entries;
map->current_nr_mappings = init_entries;
return map;
}
descriptor_table_free(map->table);
}
_mali_osk_free(map);
}
return NULL;
}
void mali_descriptor_mapping_destroy(mali_descriptor_mapping * map)
{
descriptor_table_free(map->table);
_mali_osk_mutex_rw_term(map->lock);
_mali_osk_free(map);
}
_mali_osk_errcode_t mali_descriptor_mapping_allocate_mapping(mali_descriptor_mapping * map, void * target, int *odescriptor)
{
_mali_osk_errcode_t err = _MALI_OSK_ERR_FAULT;
int new_descriptor;
MALI_DEBUG_ASSERT_POINTER(map);
MALI_DEBUG_ASSERT_POINTER(odescriptor);
_mali_osk_mutex_rw_wait(map->lock, _MALI_OSK_LOCKMODE_RW);
new_descriptor = _mali_osk_find_first_zero_bit(map->table->usage, map->current_nr_mappings);
if (new_descriptor == map->current_nr_mappings) {
/* no free descriptor, try to expand the table */
mali_descriptor_table * new_table, * old_table;
if (map->current_nr_mappings >= map->max_nr_mappings_allowed) goto unlock_and_exit;
map->current_nr_mappings += BITS_PER_LONG;
new_table = descriptor_table_alloc(map->current_nr_mappings);
if (NULL == new_table) goto unlock_and_exit;
old_table = map->table;
_mali_osk_memcpy(new_table->usage, old_table->usage, (sizeof(unsigned long)*map->current_nr_mappings) / BITS_PER_LONG);
_mali_osk_memcpy(new_table->mappings, old_table->mappings, map->current_nr_mappings * sizeof(void*));
map->table = new_table;
descriptor_table_free(old_table);
}
/* we have found a valid descriptor, set the value and usage bit */
_mali_osk_set_nonatomic_bit(new_descriptor, map->table->usage);
map->table->mappings[new_descriptor] = target;
*odescriptor = new_descriptor;
err = _MALI_OSK_ERR_OK;
unlock_and_exit:
_mali_osk_mutex_rw_signal(map->lock, _MALI_OSK_LOCKMODE_RW);
MALI_ERROR(err);
}
void mali_descriptor_mapping_call_for_each(mali_descriptor_mapping * map, void (*callback)(int, void*))
{
int i;
MALI_DEBUG_ASSERT_POINTER(map);
MALI_DEBUG_ASSERT_POINTER(callback);
_mali_osk_mutex_rw_wait(map->lock, _MALI_OSK_LOCKMODE_RO);
/* id 0 is skipped as it's an reserved ID not mapping to anything */
for (i = 1; i < map->current_nr_mappings; ++i) {
if (_mali_osk_test_bit(i, map->table->usage)) {
callback(i, map->table->mappings[i]);
}
}
_mali_osk_mutex_rw_signal(map->lock, _MALI_OSK_LOCKMODE_RO);
}
_mali_osk_errcode_t mali_descriptor_mapping_get(mali_descriptor_mapping * map, int descriptor, void** target)
{
_mali_osk_errcode_t result = _MALI_OSK_ERR_FAULT;
MALI_DEBUG_ASSERT_POINTER(map);
_mali_osk_mutex_rw_wait(map->lock, _MALI_OSK_LOCKMODE_RO);
if ( (descriptor >= 0) && (descriptor < map->current_nr_mappings) && _mali_osk_test_bit(descriptor, map->table->usage) ) {
*target = map->table->mappings[descriptor];
result = _MALI_OSK_ERR_OK;
} else *target = NULL;
_mali_osk_mutex_rw_signal(map->lock, _MALI_OSK_LOCKMODE_RO);
MALI_ERROR(result);
}
_mali_osk_errcode_t mali_descriptor_mapping_set(mali_descriptor_mapping * map, int descriptor, void * target)
{
_mali_osk_errcode_t result = _MALI_OSK_ERR_FAULT;
_mali_osk_mutex_rw_wait(map->lock, _MALI_OSK_LOCKMODE_RO);
if ( (descriptor >= 0) && (descriptor < map->current_nr_mappings) && _mali_osk_test_bit(descriptor, map->table->usage) ) {
map->table->mappings[descriptor] = target;
result = _MALI_OSK_ERR_OK;
}
_mali_osk_mutex_rw_signal(map->lock, _MALI_OSK_LOCKMODE_RO);
MALI_ERROR(result);
}
void *mali_descriptor_mapping_free(mali_descriptor_mapping * map, int descriptor)
{
void *old_value = NULL;
_mali_osk_mutex_rw_wait(map->lock, _MALI_OSK_LOCKMODE_RW);
if ( (descriptor >= 0) && (descriptor < map->current_nr_mappings) && _mali_osk_test_bit(descriptor, map->table->usage) ) {
old_value = map->table->mappings[descriptor];
map->table->mappings[descriptor] = NULL;
_mali_osk_clear_nonatomic_bit(descriptor, map->table->usage);
}
_mali_osk_mutex_rw_signal(map->lock, _MALI_OSK_LOCKMODE_RW);
return old_value;
}
static mali_descriptor_table * descriptor_table_alloc(int count)
{
mali_descriptor_table * table;
table = _mali_osk_calloc(1, sizeof(mali_descriptor_table) + ((sizeof(unsigned long) * count)/BITS_PER_LONG) + (sizeof(void*) * count));
if (NULL != table) {
table->usage = (u32*)((u8*)table + sizeof(mali_descriptor_table));
table->mappings = (void**)((u8*)table + sizeof(mali_descriptor_table) + ((sizeof(unsigned long) * count)/BITS_PER_LONG));
}
return table;
}
static void descriptor_table_free(mali_descriptor_table * table)
{
_mali_osk_free(table);
}

View File

@@ -0,0 +1,99 @@
/*
* Copyright (C) 2010, 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 mali_kernel_descriptor_mapping.h
*/
#ifndef __MALI_KERNEL_DESCRIPTOR_MAPPING_H__
#define __MALI_KERNEL_DESCRIPTOR_MAPPING_H__
#include "mali_osk.h"
/**
* The actual descriptor mapping table, never directly accessed by clients
*/
typedef struct mali_descriptor_table {
u32 * usage; /**< Pointer to bitpattern indicating if a descriptor is valid/used or not */
void** mappings; /**< Array of the pointers the descriptors map to */
} mali_descriptor_table;
/**
* The descriptor mapping object
* Provides a separate namespace where we can map an integer to a pointer
*/
typedef struct mali_descriptor_mapping {
_mali_osk_mutex_rw_t *lock; /**< Lock protecting access to the mapping object */
int max_nr_mappings_allowed; /**< Max number of mappings to support in this namespace */
int current_nr_mappings; /**< Current number of possible mappings */
mali_descriptor_table * table; /**< Pointer to the current mapping table */
} mali_descriptor_mapping;
/**
* Create a descriptor mapping object
* Create a descriptor mapping capable of holding init_entries growable to max_entries
* @param init_entries Number of entries to preallocate memory for
* @param max_entries Number of entries to max support
* @return Pointer to a descriptor mapping object, NULL on failure
*/
mali_descriptor_mapping * mali_descriptor_mapping_create(int init_entries, int max_entries);
/**
* Destroy a descriptor mapping object
* @param map The map to free
*/
void mali_descriptor_mapping_destroy(mali_descriptor_mapping * map);
/**
* Allocate a new mapping entry (descriptor ID)
* Allocates a new entry in the map.
* @param map The map to allocate a new entry in
* @param target The value to map to
* @return The descriptor allocated, a negative value on error
*/
_mali_osk_errcode_t mali_descriptor_mapping_allocate_mapping(mali_descriptor_mapping * map, void * target, int *descriptor);
/**
* Get the value mapped to by a descriptor ID
* @param map The map to lookup the descriptor id in
* @param descriptor The descriptor ID to lookup
* @param target Pointer to a pointer which will receive the stored value
* @return 0 on successful lookup, negative on error
*/
_mali_osk_errcode_t mali_descriptor_mapping_get(mali_descriptor_mapping * map, int descriptor, void** target);
/**
* Set the value mapped to by a descriptor ID
* @param map The map to lookup the descriptor id in
* @param descriptor The descriptor ID to lookup
* @param target Pointer to replace the current value with
* @return 0 on successful lookup, negative on error
*/
_mali_osk_errcode_t mali_descriptor_mapping_set(mali_descriptor_mapping * map, int descriptor, void * target);
/**
* Call the specified callback function for each descriptor in map.
* Entire function is mutex protected.
* @param map The map to do callbacks for
* @param callback A callback function which will be calle for each entry in map
*/
void mali_descriptor_mapping_call_for_each(mali_descriptor_mapping * map, void (*callback)(int, void*));
/**
* Free the descriptor ID
* For the descriptor to be reused it has to be freed
* @param map The map to free the descriptor from
* @param descriptor The descriptor ID to free
*
* @return old value of descriptor mapping
*/
void *mali_descriptor_mapping_free(mali_descriptor_mapping * map, int descriptor);
#endif /* __MALI_KERNEL_DESCRIPTOR_MAPPING_H__ */

View File

@@ -0,0 +1,439 @@
/*
* Copyright (C) 2010-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.
*/
#include "mali_kernel_utilization.h"
#include "mali_osk.h"
#include "mali_osk_mali.h"
#include "mali_kernel_common.h"
#include "mali_session.h"
#include "mali_scheduler.h"
/* Thresholds for GP bound detection. */
#define MALI_GP_BOUND_GP_UTILIZATION_THRESHOLD 240
#define MALI_GP_BOUND_PP_UTILIZATION_THRESHOLD 250
/* Define how often to calculate and report GPU utilization, in milliseconds */
static _mali_osk_spinlock_irq_t *time_data_lock;
static u32 num_running_gp_cores;
static u32 num_running_pp_cores;
static u64 work_start_time_gpu = 0;
static u64 work_start_time_gp = 0;
static u64 work_start_time_pp = 0;
static u64 accumulated_work_time_gpu = 0;
static u64 accumulated_work_time_gp = 0;
static u64 accumulated_work_time_pp = 0;
static u64 period_start_time = 0;
static _mali_osk_timer_t *utilization_timer = NULL;
static mali_bool timer_running = MALI_FALSE;
static u32 last_utilization_gpu = 0 ;
static u32 last_utilization_gp = 0 ;
static u32 last_utilization_pp = 0 ;
static u32 mali_utilization_timeout = 1000;
void (*mali_utilization_callback)(struct mali_gpu_utilization_data *data) = NULL;
#if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY)
extern void mali_power_performance_policy_callback(struct mali_gpu_utilization_data *data);
#define NUMBER_OF_NANOSECONDS_PER_SECOND 1000000000ULL
static u32 calculate_window_render_fps(u64 time_period)
{
u32 max_window_number;
u64 tmp;
u64 max = time_period;
u32 leading_zeroes;
u32 shift_val;
u32 time_period_shift;
u32 max_window_number_shift;
u32 ret_val;
max_window_number = mali_session_max_window_num();
/* To avoid float division, extend the dividend to ns unit */
tmp = (u64)max_window_number * NUMBER_OF_NANOSECONDS_PER_SECOND;
if (tmp > time_period) {
max = tmp;
}
/*
* We may have 64-bit values, a dividend or a divisor or both
* To avoid dependencies to a 64-bit divider, we shift down the two values
* equally first.
*/
leading_zeroes = _mali_osk_clz((u32)(max >> 32));
shift_val = 32 - leading_zeroes;
time_period_shift = (u32)(time_period >> shift_val);
max_window_number_shift = (u32)(tmp >> shift_val);
ret_val = max_window_number_shift / time_period_shift;
return ret_val;
}
#endif /* defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY) */
static void calculate_gpu_utilization(void* arg)
{
u64 time_now;
u64 time_period;
u32 leading_zeroes;
u32 shift_val;
u32 work_normalized_gpu;
u32 work_normalized_gp;
u32 work_normalized_pp;
u32 period_normalized;
u32 utilization_gpu;
u32 utilization_gp;
u32 utilization_pp;
#if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY)
u32 window_render_fps;
#endif
_mali_osk_spinlock_irq_lock(time_data_lock);
if (accumulated_work_time_gpu == 0 && work_start_time_gpu == 0) {
/*
* No work done for this period
* - No need to reschedule timer
* - Report zero usage
*/
timer_running = MALI_FALSE;
last_utilization_gpu = 0;
last_utilization_gp = 0;
last_utilization_pp = 0;
_mali_osk_spinlock_irq_unlock(time_data_lock);
if (NULL != mali_utilization_callback) {
struct mali_gpu_utilization_data data = { 0, };
mali_utilization_callback(&data);
}
mali_scheduler_hint_disable(MALI_SCHEDULER_HINT_GP_BOUND);
return;
}
time_now = _mali_osk_time_get_ns();
time_period = time_now - period_start_time;
/* If we are currently busy, update working period up to now */
if (work_start_time_gpu != 0) {
accumulated_work_time_gpu += (time_now - work_start_time_gpu);
work_start_time_gpu = time_now;
/* GP and/or PP will also be busy if the GPU is busy at this point */
if (work_start_time_gp != 0) {
accumulated_work_time_gp += (time_now - work_start_time_gp);
work_start_time_gp = time_now;
}
if (work_start_time_pp != 0) {
accumulated_work_time_pp += (time_now - work_start_time_pp);
work_start_time_pp = time_now;
}
}
/*
* We have two 64-bit values, a dividend and a divisor.
* To avoid dependencies to a 64-bit divider, we shift down the two values
* equally first.
* We shift the dividend up and possibly the divisor down, making the result X in 256.
*/
/* Shift the 64-bit values down so they fit inside a 32-bit integer */
leading_zeroes = _mali_osk_clz((u32)(time_period >> 32));
shift_val = 32 - leading_zeroes;
work_normalized_gpu = (u32)(accumulated_work_time_gpu >> shift_val);
work_normalized_gp = (u32)(accumulated_work_time_gp >> shift_val);
work_normalized_pp = (u32)(accumulated_work_time_pp >> shift_val);
period_normalized = (u32)(time_period >> shift_val);
/*
* Now, we should report the usage in parts of 256
* this means we must shift up the dividend or down the divisor by 8
* (we could do a combination, but we just use one for simplicity,
* but the end result should be good enough anyway)
*/
if (period_normalized > 0x00FFFFFF) {
/* The divisor is so big that it is safe to shift it down */
period_normalized >>= 8;
} else {
/*
* The divisor is so small that we can shift up the dividend, without loosing any data.
* (dividend is always smaller than the divisor)
*/
work_normalized_gpu <<= 8;
work_normalized_gp <<= 8;
work_normalized_pp <<= 8;
}
utilization_gpu = work_normalized_gpu / period_normalized;
utilization_gp = work_normalized_gp / period_normalized;
utilization_pp = work_normalized_pp / period_normalized;
#if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY)
window_render_fps = calculate_window_render_fps(time_period);
#endif
last_utilization_gpu = utilization_gpu;
last_utilization_gp = utilization_gp;
last_utilization_pp = utilization_pp;
if ((MALI_GP_BOUND_GP_UTILIZATION_THRESHOLD < last_utilization_gp) &&
(MALI_GP_BOUND_PP_UTILIZATION_THRESHOLD > last_utilization_pp)) {
mali_scheduler_hint_enable(MALI_SCHEDULER_HINT_GP_BOUND);
} else {
mali_scheduler_hint_disable(MALI_SCHEDULER_HINT_GP_BOUND);
}
/* starting a new period */
accumulated_work_time_gpu = 0;
accumulated_work_time_gp = 0;
accumulated_work_time_pp = 0;
period_start_time = time_now;
_mali_osk_spinlock_irq_unlock(time_data_lock);
_mali_osk_timer_add(utilization_timer, _mali_osk_time_mstoticks(mali_utilization_timeout));
if (NULL != mali_utilization_callback) {
struct mali_gpu_utilization_data data = {
utilization_gpu, utilization_gp, utilization_pp,
#if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY)
window_render_fps, window_render_fps
#endif
};
mali_utilization_callback(&data);
}
}
_mali_osk_errcode_t mali_utilization_init(void)
{
#if USING_GPU_UTILIZATION
struct _mali_osk_device_data data;
if (_MALI_OSK_ERR_OK == _mali_osk_device_data_get(&data)) {
/* Use device specific settings (if defined) */
if (0 != data.utilization_interval) {
mali_utilization_timeout = data.utilization_interval;
}
if (NULL != data.utilization_callback) {
mali_utilization_callback = data.utilization_callback;
MALI_DEBUG_PRINT(2, ("Mali GPU Utilization: Platform has it's own policy \n"));
MALI_DEBUG_PRINT(2, ("Mali GPU Utilization: Utilization handler installed with interval %u\n", mali_utilization_timeout));
}
}
#endif
#if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY)
if (mali_utilization_callback == NULL) {
MALI_DEBUG_PRINT(2, ("Mali GPU Utilization: MALI Power Performance Policy Algorithm \n"));
mali_utilization_callback = mali_power_performance_policy_callback;
}
#endif
if (NULL == mali_utilization_callback) {
MALI_DEBUG_PRINT(2, ("Mali GPU Utilization: No utilization handler installed\n"));
}
time_data_lock = _mali_osk_spinlock_irq_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_UTILIZATION);
if (NULL == time_data_lock) {
return _MALI_OSK_ERR_FAULT;
}
num_running_gp_cores = 0;
num_running_pp_cores = 0;
utilization_timer = _mali_osk_timer_init();
if (NULL == utilization_timer) {
_mali_osk_spinlock_irq_term(time_data_lock);
return _MALI_OSK_ERR_FAULT;
}
_mali_osk_timer_setcallback(utilization_timer, calculate_gpu_utilization, NULL);
return _MALI_OSK_ERR_OK;
}
void mali_utilization_suspend(void)
{
_mali_osk_spinlock_irq_lock(time_data_lock);
if (timer_running == MALI_TRUE) {
timer_running = MALI_FALSE;
_mali_osk_spinlock_irq_unlock(time_data_lock);
_mali_osk_timer_del(utilization_timer);
return;
}
_mali_osk_spinlock_irq_unlock(time_data_lock);
}
void mali_utilization_term(void)
{
if (NULL != utilization_timer) {
_mali_osk_timer_del(utilization_timer);
timer_running = MALI_FALSE;
_mali_osk_timer_term(utilization_timer);
utilization_timer = NULL;
}
_mali_osk_spinlock_irq_term(time_data_lock);
}
void mali_utilization_gp_start(void)
{
_mali_osk_spinlock_irq_lock(time_data_lock);
++num_running_gp_cores;
if (1 == num_running_gp_cores) {
u64 time_now = _mali_osk_time_get_ns();
/* First GP core started, consider GP busy from now and onwards */
work_start_time_gp = time_now;
if (0 == num_running_pp_cores) {
/*
* There are no PP cores running, so this is also the point
* at which we consider the GPU to be busy as well.
*/
work_start_time_gpu = time_now;
}
/* Start a new period (and timer) if needed */
if (timer_running != MALI_TRUE) {
timer_running = MALI_TRUE;
period_start_time = time_now;
/* Clear session->number_of_window_jobs */
#if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY)
mali_session_max_window_num();
#endif
_mali_osk_spinlock_irq_unlock(time_data_lock);
_mali_osk_timer_add(utilization_timer, _mali_osk_time_mstoticks(mali_utilization_timeout));
} else {
_mali_osk_spinlock_irq_unlock(time_data_lock);
}
} else {
/* Nothing to do */
_mali_osk_spinlock_irq_unlock(time_data_lock);
}
}
void mali_utilization_pp_start(void)
{
_mali_osk_spinlock_irq_lock(time_data_lock);
++num_running_pp_cores;
if (1 == num_running_pp_cores) {
u64 time_now = _mali_osk_time_get_ns();
/* First PP core started, consider PP busy from now and onwards */
work_start_time_pp = time_now;
if (0 == num_running_gp_cores) {
/*
* There are no GP cores running, so this is also the point
* at which we consider the GPU to be busy as well.
*/
work_start_time_gpu = time_now;
}
/* Start a new period (and timer) if needed */
if (timer_running != MALI_TRUE) {
timer_running = MALI_TRUE;
period_start_time = time_now;
/* Clear session->number_of_window_jobs */
#if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY)
mali_session_max_window_num();
#endif
_mali_osk_spinlock_irq_unlock(time_data_lock);
_mali_osk_timer_add(utilization_timer, _mali_osk_time_mstoticks(mali_utilization_timeout));
} else {
_mali_osk_spinlock_irq_unlock(time_data_lock);
}
} else {
/* Nothing to do */
_mali_osk_spinlock_irq_unlock(time_data_lock);
}
}
void mali_utilization_gp_end(void)
{
_mali_osk_spinlock_irq_lock(time_data_lock);
--num_running_gp_cores;
if (0 == num_running_gp_cores) {
u64 time_now = _mali_osk_time_get_ns();
/* Last GP core ended, consider GP idle from now and onwards */
accumulated_work_time_gp += (time_now - work_start_time_gp);
work_start_time_gp = 0;
if (0 == num_running_pp_cores) {
/*
* There are no PP cores running, so this is also the point
* at which we consider the GPU to be idle as well.
*/
accumulated_work_time_gpu += (time_now - work_start_time_gpu);
work_start_time_gpu = 0;
}
}
_mali_osk_spinlock_irq_unlock(time_data_lock);
}
void mali_utilization_pp_end(void)
{
_mali_osk_spinlock_irq_lock(time_data_lock);
--num_running_pp_cores;
if (0 == num_running_pp_cores) {
u64 time_now = _mali_osk_time_get_ns();
/* Last PP core ended, consider PP idle from now and onwards */
accumulated_work_time_pp += (time_now - work_start_time_pp);
work_start_time_pp = 0;
if (0 == num_running_gp_cores) {
/*
* There are no GP cores running, so this is also the point
* at which we consider the GPU to be idle as well.
*/
accumulated_work_time_gpu += (time_now - work_start_time_gpu);
work_start_time_gpu = 0;
}
}
_mali_osk_spinlock_irq_unlock(time_data_lock);
}
u32 _mali_ukk_utilization_gp_pp(void)
{
return last_utilization_gpu;
}
u32 _mali_ukk_utilization_gp(void)
{
return last_utilization_gp;
}
u32 _mali_ukk_utilization_pp(void)
{
return last_utilization_pp;
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright (C) 2010-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.
*/
#ifndef __MALI_KERNEL_UTILIZATION_H__
#define __MALI_KERNEL_UTILIZATION_H__
#include <linux/mali/mali_utgard.h>
#include "mali_osk.h"
extern void (*mali_utilization_callback)(struct mali_gpu_utilization_data *data);
/**
* Initialize/start the Mali GPU utilization metrics reporting.
*
* @return _MALI_OSK_ERR_OK on success, otherwise failure.
*/
_mali_osk_errcode_t mali_utilization_init(void);
/**
* Terminate the Mali GPU utilization metrics reporting
*/
void mali_utilization_term(void);
/**
* Check if Mali utilization is enabled
*/
MALI_STATIC_INLINE mali_bool mali_utilization_enabled(void)
{
return (NULL != mali_utilization_callback);
}
/**
* Should be called when a job is about to execute a GP job
*/
void mali_utilization_gp_start(void);
/**
* Should be called when a job has completed executing a GP job
*/
void mali_utilization_gp_end(void);
/**
* Should be called when a job is about to execute a PP job
*/
void mali_utilization_pp_start(void);
/**
* Should be called when a job has completed executing a PP job
*/
void mali_utilization_pp_end(void);
/**
* Should be called to stop the utilization timer during system suspend
*/
void mali_utilization_suspend(void);
#endif /* __MALI_KERNEL_UTILIZATION_H__ */

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2011-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.
*/
#include "mali_kernel_common.h"
#include "mali_osk.h"
#include "mali_ukk.h"
#if defined(CONFIG_MALI400_PROFILING)
#include "mali_osk_profiling.h"
#endif
_mali_osk_errcode_t _mali_ukk_vsync_event_report(_mali_uk_vsync_event_report_s *args)
{
_mali_uk_vsync_event event = (_mali_uk_vsync_event)args->event;
MALI_IGNORE(event); /* event is not used for release code, and that is OK */
#if defined(CONFIG_MALI400_PROFILING)
/*
* Manually generate user space events in kernel space.
* This saves user space from calling kernel space twice in this case.
* We just need to remember to add pid and tid manually.
*/
if ( event==_MALI_UK_VSYNC_EVENT_BEGIN_WAIT) {
_mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_SUSPEND |
MALI_PROFILING_EVENT_CHANNEL_SOFTWARE |
MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_VSYNC,
_mali_osk_get_pid(), _mali_osk_get_tid(), 0, 0, 0);
}
if (event==_MALI_UK_VSYNC_EVENT_END_WAIT) {
_mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_RESUME |
MALI_PROFILING_EVENT_CHANNEL_SOFTWARE |
MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_VSYNC,
_mali_osk_get_pid(), _mali_osk_get_tid(), 0, 0, 0);
}
#endif
MALI_DEBUG_PRINT(4, ("Received VSYNC event: %d\n", event));
MALI_SUCCESS;
}

View File

@@ -0,0 +1,585 @@
/*
* Copyright (C) 2010-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.
*/
#include "mali_kernel_common.h"
#include "mali_osk.h"
#include "mali_l2_cache.h"
#include "mali_hw_core.h"
#include "mali_scheduler.h"
#include "mali_pm_domain.h"
/**
* Size of the Mali L2 cache registers in bytes
*/
#define MALI400_L2_CACHE_REGISTERS_SIZE 0x30
/**
* Mali L2 cache register numbers
* Used in the register read/write routines.
* See the hardware documentation for more information about each register
*/
typedef enum mali_l2_cache_register {
MALI400_L2_CACHE_REGISTER_SIZE = 0x0004,
MALI400_L2_CACHE_REGISTER_STATUS = 0x0008,
/*unused = 0x000C */
MALI400_L2_CACHE_REGISTER_COMMAND = 0x0010, /**< Misc cache commands, e.g. clear */
MALI400_L2_CACHE_REGISTER_CLEAR_PAGE = 0x0014,
MALI400_L2_CACHE_REGISTER_MAX_READS = 0x0018, /**< Limit of outstanding read requests */
MALI400_L2_CACHE_REGISTER_ENABLE = 0x001C, /**< Enable misc cache features */
MALI400_L2_CACHE_REGISTER_PERFCNT_SRC0 = 0x0020,
MALI400_L2_CACHE_REGISTER_PERFCNT_VAL0 = 0x0024,
MALI400_L2_CACHE_REGISTER_PERFCNT_SRC1 = 0x0028,
MALI400_L2_CACHE_REGISTER_PERFCNT_VAL1 = 0x002C,
} mali_l2_cache_register;
/**
* Mali L2 cache commands
* These are the commands that can be sent to the Mali L2 cache unit
*/
typedef enum mali_l2_cache_command {
MALI400_L2_CACHE_COMMAND_CLEAR_ALL = 0x01, /**< Clear the entire cache */
/* Read HW TRM carefully before adding/using other commands than the clear above */
} mali_l2_cache_command;
/**
* Mali L2 cache commands
* These are the commands that can be sent to the Mali L2 cache unit
*/
typedef enum mali_l2_cache_enable {
MALI400_L2_CACHE_ENABLE_DEFAULT = 0x0, /**< Default state of enable register */
MALI400_L2_CACHE_ENABLE_ACCESS = 0x01, /**< Permit cacheable accesses */
MALI400_L2_CACHE_ENABLE_READ_ALLOCATE = 0x02, /**< Permit cache read allocate */
} mali_l2_cache_enable;
/**
* Mali L2 cache status bits
*/
typedef enum mali_l2_cache_status {
MALI400_L2_CACHE_STATUS_COMMAND_BUSY = 0x01, /**< Command handler of L2 cache is busy */
MALI400_L2_CACHE_STATUS_DATA_BUSY = 0x02, /**< L2 cache is busy handling data requests */
} mali_l2_cache_status;
#define MALI400_L2_MAX_READS_DEFAULT 0x1C
static struct mali_l2_cache_core *mali_global_l2_cache_cores[MALI_MAX_NUMBER_OF_L2_CACHE_CORES] = { NULL, };
static u32 mali_global_num_l2_cache_cores = 0;
int mali_l2_max_reads = MALI400_L2_MAX_READS_DEFAULT;
/* Local helper functions */
static _mali_osk_errcode_t mali_l2_cache_send_command(struct mali_l2_cache_core *cache, u32 reg, u32 val);
static void mali_l2_cache_counter_lock(struct mali_l2_cache_core *cache)
{
#ifdef MALI_UPPER_HALF_SCHEDULING
_mali_osk_spinlock_irq_lock(cache->counter_lock);
#else
_mali_osk_spinlock_lock(cache->counter_lock);
#endif
}
static void mali_l2_cache_counter_unlock(struct mali_l2_cache_core *cache)
{
#ifdef MALI_UPPER_HALF_SCHEDULING
_mali_osk_spinlock_irq_unlock(cache->counter_lock);
#else
_mali_osk_spinlock_unlock(cache->counter_lock);
#endif
}
static void mali_l2_cache_command_lock(struct mali_l2_cache_core *cache)
{
#ifdef MALI_UPPER_HALF_SCHEDULING
_mali_osk_spinlock_irq_lock(cache->command_lock);
#else
_mali_osk_spinlock_lock(cache->command_lock);
#endif
}
static void mali_l2_cache_command_unlock(struct mali_l2_cache_core *cache)
{
#ifdef MALI_UPPER_HALF_SCHEDULING
_mali_osk_spinlock_irq_unlock(cache->command_lock);
#else
_mali_osk_spinlock_unlock(cache->command_lock);
#endif
}
struct mali_l2_cache_core *mali_l2_cache_create(_mali_osk_resource_t *resource)
{
struct mali_l2_cache_core *cache = NULL;
MALI_DEBUG_PRINT(4, ("Mali L2 cache: Creating Mali L2 cache: %s\n", resource->description));
if (mali_global_num_l2_cache_cores >= MALI_MAX_NUMBER_OF_L2_CACHE_CORES) {
MALI_PRINT_ERROR(("Mali L2 cache: Too many L2 cache core objects created\n"));
return NULL;
}
cache = _mali_osk_malloc(sizeof(struct mali_l2_cache_core));
if (NULL != cache) {
cache->core_id = mali_global_num_l2_cache_cores;
cache->counter_src0 = MALI_HW_CORE_NO_COUNTER;
cache->counter_src1 = MALI_HW_CORE_NO_COUNTER;
cache->pm_domain = NULL;
cache->mali_l2_status = MALI_L2_NORMAL;
if (_MALI_OSK_ERR_OK == mali_hw_core_create(&cache->hw_core, resource, MALI400_L2_CACHE_REGISTERS_SIZE)) {
MALI_DEBUG_CODE(u32 cache_size = mali_hw_core_register_read(&cache->hw_core, MALI400_L2_CACHE_REGISTER_SIZE));
MALI_DEBUG_PRINT(2, ("Mali L2 cache: Created %s: % 3uK, %u-way, % 2ubyte cache line, % 3ubit external bus\n",
resource->description,
1 << (((cache_size >> 16) & 0xff) - 10),
1 << ((cache_size >> 8) & 0xff),
1 << (cache_size & 0xff),
1 << ((cache_size >> 24) & 0xff)));
#ifdef MALI_UPPER_HALF_SCHEDULING
cache->command_lock = _mali_osk_spinlock_irq_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_L2_COMMAND);
#else
cache->command_lock = _mali_osk_spinlock_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_L2_COMMAND);
#endif
if (NULL != cache->command_lock) {
#ifdef MALI_UPPER_HALF_SCHEDULING
cache->counter_lock = _mali_osk_spinlock_irq_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_L2_COMMAND);
#else
cache->counter_lock = _mali_osk_spinlock_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_L2_COMMAND);
#endif
if (NULL != cache->counter_lock) {
mali_l2_cache_reset(cache);
cache->last_invalidated_id = 0;
mali_global_l2_cache_cores[mali_global_num_l2_cache_cores] = cache;
mali_global_num_l2_cache_cores++;
return cache;
} else {
MALI_PRINT_ERROR(("Mali L2 cache: Failed to create counter lock for L2 cache core %s\n", cache->hw_core.description));
}
#ifdef MALI_UPPER_HALF_SCHEDULING
_mali_osk_spinlock_irq_term(cache->command_lock);
#else
_mali_osk_spinlock_term(cache->command_lock);
#endif
} else {
MALI_PRINT_ERROR(("Mali L2 cache: Failed to create command lock for L2 cache core %s\n", cache->hw_core.description));
}
mali_hw_core_delete(&cache->hw_core);
}
_mali_osk_free(cache);
} else {
MALI_PRINT_ERROR(("Mali L2 cache: Failed to allocate memory for L2 cache core\n"));
}
return NULL;
}
void mali_l2_cache_delete(struct mali_l2_cache_core *cache)
{
u32 i;
/* reset to defaults */
mali_hw_core_register_write(&cache->hw_core, MALI400_L2_CACHE_REGISTER_MAX_READS, (u32)MALI400_L2_MAX_READS_DEFAULT);
mali_hw_core_register_write(&cache->hw_core, MALI400_L2_CACHE_REGISTER_ENABLE, (u32)MALI400_L2_CACHE_ENABLE_DEFAULT);
#ifdef MALI_UPPER_HALF_SCHEDULING
_mali_osk_spinlock_irq_term(cache->counter_lock);
_mali_osk_spinlock_irq_term(cache->command_lock);
#else
_mali_osk_spinlock_term(cache->command_lock);
_mali_osk_spinlock_term(cache->counter_lock);
#endif
mali_hw_core_delete(&cache->hw_core);
for (i = 0; i < mali_global_num_l2_cache_cores; i++) {
if (mali_global_l2_cache_cores[i] == cache) {
mali_global_l2_cache_cores[i] = NULL;
mali_global_num_l2_cache_cores--;
if (i != mali_global_num_l2_cache_cores) {
/* We removed a l2 cache from the middle of the array -- move the last
* l2 cache to the current position to close the gap */
mali_global_l2_cache_cores[i] = mali_global_l2_cache_cores[mali_global_num_l2_cache_cores];
mali_global_l2_cache_cores[mali_global_num_l2_cache_cores] = NULL;
}
break;
}
}
_mali_osk_free(cache);
}
u32 mali_l2_cache_get_id(struct mali_l2_cache_core *cache)
{
return cache->core_id;
}
static void mali_l2_cache_core_set_counter_internal(struct mali_l2_cache_core *cache, u32 source_id, u32 counter)
{
u32 value = 0; /* disabled src */
u32 reg_offset = 0;
mali_bool core_is_on;
MALI_DEBUG_ASSERT_POINTER(cache);
core_is_on = mali_l2_cache_lock_power_state(cache);
mali_l2_cache_counter_lock(cache);
switch (source_id) {
case 0:
cache->counter_src0 = counter;
reg_offset = MALI400_L2_CACHE_REGISTER_PERFCNT_SRC0;
break;
case 1:
cache->counter_src1 = counter;
reg_offset = MALI400_L2_CACHE_REGISTER_PERFCNT_SRC1;
break;
default:
MALI_DEBUG_ASSERT(0);
break;
}
if (MALI_L2_PAUSE == cache->mali_l2_status) {
mali_l2_cache_counter_unlock(cache);
mali_l2_cache_unlock_power_state(cache);
return;
}
if (MALI_HW_CORE_NO_COUNTER != counter) {
value = counter;
}
if (MALI_TRUE == core_is_on) {
mali_hw_core_register_write(&cache->hw_core, reg_offset, value);
}
mali_l2_cache_counter_unlock(cache);
mali_l2_cache_unlock_power_state(cache);
}
void mali_l2_cache_core_set_counter_src0(struct mali_l2_cache_core *cache, u32 counter)
{
mali_l2_cache_core_set_counter_internal(cache, 0, counter);
}
void mali_l2_cache_core_set_counter_src1(struct mali_l2_cache_core *cache, u32 counter)
{
mali_l2_cache_core_set_counter_internal(cache, 1, counter);
}
u32 mali_l2_cache_core_get_counter_src0(struct mali_l2_cache_core *cache)
{
return cache->counter_src0;
}
u32 mali_l2_cache_core_get_counter_src1(struct mali_l2_cache_core *cache)
{
return cache->counter_src1;
}
void mali_l2_cache_core_get_counter_values(struct mali_l2_cache_core *cache, u32 *src0, u32 *value0, u32 *src1, u32 *value1)
{
MALI_DEBUG_ASSERT(NULL != src0);
MALI_DEBUG_ASSERT(NULL != value0);
MALI_DEBUG_ASSERT(NULL != src1);
MALI_DEBUG_ASSERT(NULL != value1);
/* Caller must hold the PM lock and know that we are powered on */
mali_l2_cache_counter_lock(cache);
if (MALI_L2_PAUSE == cache->mali_l2_status) {
mali_l2_cache_counter_unlock(cache);
return;
}
*src0 = cache->counter_src0;
*src1 = cache->counter_src1;
if (cache->counter_src0 != MALI_HW_CORE_NO_COUNTER) {
*value0 = mali_hw_core_register_read(&cache->hw_core, MALI400_L2_CACHE_REGISTER_PERFCNT_VAL0);
}
if (cache->counter_src1 != MALI_HW_CORE_NO_COUNTER) {
*value1 = mali_hw_core_register_read(&cache->hw_core, MALI400_L2_CACHE_REGISTER_PERFCNT_VAL1);
}
mali_l2_cache_counter_unlock(cache);
}
static void mali_l2_cache_reset_counters_all(void)
{
int i;
u32 value;
struct mali_l2_cache_core *cache;
u32 num_cores = mali_l2_cache_core_get_glob_num_l2_cores();
for (i = 0; i < num_cores; i++) {
cache = mali_l2_cache_core_get_glob_l2_core(i);
if (MALI_TRUE == mali_l2_cache_lock_power_state(cache)) {
mali_l2_cache_counter_lock(cache);
if (MALI_L2_PAUSE == cache->mali_l2_status) {
mali_l2_cache_counter_unlock(cache);
mali_l2_cache_unlock_power_state(cache);
return;
}
/* Reset performance counters */
if (MALI_HW_CORE_NO_COUNTER == cache->counter_src0) {
value = 0;
} else {
value = cache->counter_src0;
}
mali_hw_core_register_write(&cache->hw_core,
MALI400_L2_CACHE_REGISTER_PERFCNT_SRC0, value);
if (MALI_HW_CORE_NO_COUNTER == cache->counter_src1) {
value = 0;
} else {
value = cache->counter_src1;
}
mali_hw_core_register_write(&cache->hw_core,
MALI400_L2_CACHE_REGISTER_PERFCNT_SRC1, value);
mali_l2_cache_counter_unlock(cache);
}
mali_l2_cache_unlock_power_state(cache);
}
}
struct mali_l2_cache_core *mali_l2_cache_core_get_glob_l2_core(u32 index)
{
if (mali_global_num_l2_cache_cores > index) {
return mali_global_l2_cache_cores[index];
}
return NULL;
}
u32 mali_l2_cache_core_get_glob_num_l2_cores(void)
{
return mali_global_num_l2_cache_cores;
}
void mali_l2_cache_reset(struct mali_l2_cache_core *cache)
{
if (cache && cache->pm_domain && cache->pm_domain->state == MALI_PM_DOMAIN_OFF) {
return;
}
/* Invalidate cache (just to keep it in a known state at startup) */
mali_l2_cache_send_command(cache, MALI400_L2_CACHE_REGISTER_COMMAND, MALI400_L2_CACHE_COMMAND_CLEAR_ALL);
mali_l2_cache_counter_lock(cache);
if (MALI_L2_PAUSE == cache->mali_l2_status) {
mali_l2_cache_counter_unlock(cache);
return;
}
/* Enable cache */
mali_hw_core_register_write(&cache->hw_core, MALI400_L2_CACHE_REGISTER_ENABLE, (u32)MALI400_L2_CACHE_ENABLE_ACCESS | (u32)MALI400_L2_CACHE_ENABLE_READ_ALLOCATE);
mali_hw_core_register_write(&cache->hw_core, MALI400_L2_CACHE_REGISTER_MAX_READS, (u32)mali_l2_max_reads);
/* Restart any performance counters (if enabled) */
if (cache->counter_src0 != MALI_HW_CORE_NO_COUNTER) {
mali_hw_core_register_write(&cache->hw_core, MALI400_L2_CACHE_REGISTER_PERFCNT_SRC0, cache->counter_src0);
}
if (cache->counter_src1 != MALI_HW_CORE_NO_COUNTER) {
mali_hw_core_register_write(&cache->hw_core, MALI400_L2_CACHE_REGISTER_PERFCNT_SRC1, cache->counter_src1);
}
mali_l2_cache_counter_unlock(cache);
}
void mali_l2_cache_reset_all(void)
{
int i;
u32 num_cores = mali_l2_cache_core_get_glob_num_l2_cores();
for (i = 0; i < num_cores; i++) {
mali_l2_cache_reset(mali_l2_cache_core_get_glob_l2_core(i));
}
}
void mali_l2_cache_invalidate(struct mali_l2_cache_core *cache)
{
MALI_DEBUG_ASSERT_POINTER(cache);
if (NULL != cache) {
cache->last_invalidated_id = mali_scheduler_get_new_cache_order();
mali_l2_cache_send_command(cache, MALI400_L2_CACHE_REGISTER_COMMAND, MALI400_L2_CACHE_COMMAND_CLEAR_ALL);
}
}
mali_bool mali_l2_cache_invalidate_conditional(struct mali_l2_cache_core *cache, u32 id)
{
MALI_DEBUG_ASSERT_POINTER(cache);
if (NULL != cache) {
/* If the last cache invalidation was done by a job with a higher id we
* don't have to flush. Since user space will store jobs w/ their
* corresponding memory in sequence (first job #0, then job #1, ...),
* we don't have to flush for job n-1 if job n has already invalidated
* the cache since we know for sure that job n-1's memory was already
* written when job n was started. */
if (((s32)id) <= ((s32)cache->last_invalidated_id)) {
return MALI_FALSE;
} else {
cache->last_invalidated_id = mali_scheduler_get_new_cache_order();
}
mali_l2_cache_send_command(cache, MALI400_L2_CACHE_REGISTER_COMMAND, MALI400_L2_CACHE_COMMAND_CLEAR_ALL);
}
return MALI_TRUE;
}
void mali_l2_cache_invalidate_all(void)
{
u32 i;
for (i = 0; i < mali_global_num_l2_cache_cores; i++) {
/*additional check*/
if (MALI_TRUE == mali_l2_cache_lock_power_state(mali_global_l2_cache_cores[i])) {
_mali_osk_errcode_t ret;
mali_global_l2_cache_cores[i]->last_invalidated_id = mali_scheduler_get_new_cache_order();
ret = mali_l2_cache_send_command(mali_global_l2_cache_cores[i], MALI400_L2_CACHE_REGISTER_COMMAND, MALI400_L2_CACHE_COMMAND_CLEAR_ALL);
if (_MALI_OSK_ERR_OK != ret) {
MALI_PRINT_ERROR(("Failed to invalidate cache\n"));
}
}
mali_l2_cache_unlock_power_state(mali_global_l2_cache_cores[i]);
}
}
void mali_l2_cache_invalidate_all_pages(u32 *pages, u32 num_pages)
{
u32 i;
for (i = 0; i < mali_global_num_l2_cache_cores; i++) {
/*additional check*/
if (MALI_TRUE == mali_l2_cache_lock_power_state(mali_global_l2_cache_cores[i])) {
u32 j;
for (j = 0; j < num_pages; j++) {
_mali_osk_errcode_t ret;
ret = mali_l2_cache_send_command(mali_global_l2_cache_cores[i], MALI400_L2_CACHE_REGISTER_CLEAR_PAGE, pages[j]);
if (_MALI_OSK_ERR_OK != ret) {
MALI_PRINT_ERROR(("Failed to invalidate page cache\n"));
}
}
}
mali_l2_cache_unlock_power_state(mali_global_l2_cache_cores[i]);
}
}
mali_bool mali_l2_cache_lock_power_state(struct mali_l2_cache_core *cache)
{
return mali_pm_domain_lock_state(cache->pm_domain);
}
void mali_l2_cache_unlock_power_state(struct mali_l2_cache_core *cache)
{
return mali_pm_domain_unlock_state(cache->pm_domain);
}
/* -------- local helper functions below -------- */
static _mali_osk_errcode_t mali_l2_cache_send_command(struct mali_l2_cache_core *cache, u32 reg, u32 val)
{
int i = 0;
const int loop_count = 100000;
/*
* Grab lock in order to send commands to the L2 cache in a serialized fashion.
* The L2 cache will ignore commands if it is busy.
*/
mali_l2_cache_command_lock(cache);
if (MALI_L2_PAUSE == cache->mali_l2_status) {
mali_l2_cache_command_unlock(cache);
MALI_DEBUG_PRINT(1, ( "Mali L2 cache: aborting wait for L2 come back\n"));
MALI_ERROR( _MALI_OSK_ERR_BUSY );
}
/* First, wait for L2 cache command handler to go idle */
for (i = 0; i < loop_count; i++) {
if (!(mali_hw_core_register_read(&cache->hw_core, MALI400_L2_CACHE_REGISTER_STATUS) & (u32)MALI400_L2_CACHE_STATUS_COMMAND_BUSY)) {
break;
}
}
if (i == loop_count) {
mali_l2_cache_command_unlock(cache);
MALI_DEBUG_PRINT(1, ( "Mali L2 cache: aborting wait for command interface to go idle\n"));
MALI_ERROR( _MALI_OSK_ERR_FAULT );
}
/* then issue the command */
mali_hw_core_register_write(&cache->hw_core, reg, val);
mali_l2_cache_command_unlock(cache);
MALI_SUCCESS;
}
void mali_l2_cache_pause_all(mali_bool pause)
{
int i;
struct mali_l2_cache_core * cache;
u32 num_cores = mali_l2_cache_core_get_glob_num_l2_cores();
mali_l2_power_status status = MALI_L2_NORMAL;
if (pause) {
status = MALI_L2_PAUSE;
}
for (i = 0; i < num_cores; i++) {
cache = mali_l2_cache_core_get_glob_l2_core(i);
if (NULL != cache) {
cache->mali_l2_status = status;
/* Take and release the counter and command locks to
* ensure there are no active threads that didn't get
* the status flag update.
*
* The locks will also ensure the necessary memory
* barriers are done on SMP systems.
*/
mali_l2_cache_counter_lock(cache);
mali_l2_cache_counter_unlock(cache);
mali_l2_cache_command_lock(cache);
mali_l2_cache_command_unlock(cache);
}
}
/* Resume from pause: do the cache invalidation here to prevent any
* loss of cache operation during the pause period to make sure the SW
* status is consistent with L2 cache status.
*/
if(!pause) {
mali_l2_cache_invalidate_all();
mali_l2_cache_reset_counters_all();
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright (C) 2010-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.
*/
#ifndef __MALI_KERNEL_L2_CACHE_H__
#define __MALI_KERNEL_L2_CACHE_H__
#include "mali_osk.h"
#include "mali_hw_core.h"
#define MALI_MAX_NUMBER_OF_L2_CACHE_CORES 3
/* Maximum 1 GP and 4 PP for an L2 cache core (Mali-400 Quad-core) */
#define MALI_MAX_NUMBER_OF_GROUPS_PER_L2_CACHE 5
struct mali_group;
struct mali_pm_domain;
/* Flags describing state of the L2 */
typedef enum mali_l2_power_status {
MALI_L2_NORMAL, /**< L2 is in normal state and operational */
MALI_L2_PAUSE, /**< L2 may not be accessed and may be powered off */
} mali_l2_power_status;
/**
* Definition of the L2 cache core struct
* Used to track a L2 cache unit in the system.
* Contains information about the mapping of the registers
*/
struct mali_l2_cache_core {
struct mali_hw_core hw_core; /**< Common for all HW cores */
u32 core_id; /**< Unique core ID */
#ifdef MALI_UPPER_HALF_SCHEDULING
_mali_osk_spinlock_irq_t *command_lock; /**< Serialize all L2 cache commands */
_mali_osk_spinlock_irq_t *counter_lock; /**< Synchronize L2 cache counter access */
#else
_mali_osk_spinlock_t *command_lock;
_mali_osk_spinlock_t *counter_lock;
#endif
u32 counter_src0; /**< Performance counter 0, MALI_HW_CORE_NO_COUNTER for disabled */
u32 counter_src1; /**< Performance counter 1, MALI_HW_CORE_NO_COUNTER for disabled */
u32 last_invalidated_id;
struct mali_pm_domain *pm_domain;
mali_l2_power_status mali_l2_status; /**< Indicate whether the L2 is paused or not */
};
_mali_osk_errcode_t mali_l2_cache_initialize(void);
void mali_l2_cache_terminate(void);
/**
* L2 pause is just a status that the L2 can't be accessed temporarily.
*/
void mali_l2_cache_pause_all(mali_bool pause);
struct mali_l2_cache_core *mali_l2_cache_create(_mali_osk_resource_t * resource);
void mali_l2_cache_delete(struct mali_l2_cache_core *cache);
MALI_STATIC_INLINE void mali_l2_cache_set_pm_domain(struct mali_l2_cache_core *cache, struct mali_pm_domain *domain)
{
cache->pm_domain = domain;
}
u32 mali_l2_cache_get_id(struct mali_l2_cache_core *cache);
void mali_l2_cache_core_set_counter_src0(struct mali_l2_cache_core *cache, u32 counter);
void mali_l2_cache_core_set_counter_src1(struct mali_l2_cache_core *cache, u32 counter);
u32 mali_l2_cache_core_get_counter_src0(struct mali_l2_cache_core *cache);
u32 mali_l2_cache_core_get_counter_src1(struct mali_l2_cache_core *cache);
void mali_l2_cache_core_get_counter_values(struct mali_l2_cache_core *cache, u32 *src0, u32 *value0, u32 *src1, u32 *value1);
struct mali_l2_cache_core *mali_l2_cache_core_get_glob_l2_core(u32 index);
u32 mali_l2_cache_core_get_glob_num_l2_cores(void);
void mali_l2_cache_reset(struct mali_l2_cache_core *cache);
void mali_l2_cache_reset_all(void);
struct mali_group *mali_l2_cache_get_group(struct mali_l2_cache_core *cache, u32 index);
void mali_l2_cache_invalidate(struct mali_l2_cache_core *cache);
mali_bool mali_l2_cache_invalidate_conditional(struct mali_l2_cache_core *cache, u32 id);
void mali_l2_cache_invalidate_all(void);
void mali_l2_cache_invalidate_all_pages(u32 *pages, u32 num_pages);
mali_bool mali_l2_cache_lock_power_state(struct mali_l2_cache_core *cache);
void mali_l2_cache_unlock_power_state(struct mali_l2_cache_core *cache);
#endif /* __MALI_KERNEL_L2_CACHE_H__ */

View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2011-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.
*/
#include "mali_mem_validation.h"
#include "mali_osk.h"
#include "mali_kernel_common.h"
#define MALI_INVALID_MEM_ADDR 0xFFFFFFFF
typedef struct {
u32 phys_base; /**< Mali physical base of the memory, page aligned */
u32 size; /**< size in bytes of the memory, multiple of page size */
} _mali_mem_validation_t;
static _mali_mem_validation_t mali_mem_validator = { MALI_INVALID_MEM_ADDR, MALI_INVALID_MEM_ADDR };
_mali_osk_errcode_t mali_mem_validation_add_range(u32 start, u32 size)
{
/* Check that no other MEM_VALIDATION resources exist */
if (MALI_INVALID_MEM_ADDR != mali_mem_validator.phys_base) {
MALI_PRINT_ERROR(("Failed to add frame buffer memory; another range is already specified\n"));
return _MALI_OSK_ERR_FAULT;
}
/* Check restrictions on page alignment */
if ((0 != (start & (~_MALI_OSK_CPU_PAGE_MASK))) ||
(0 != (size & (~_MALI_OSK_CPU_PAGE_MASK)))) {
MALI_PRINT_ERROR(("Failed to add frame buffer memory; incorrect alignment\n"));
return _MALI_OSK_ERR_FAULT;
}
mali_mem_validator.phys_base = start;
mali_mem_validator.size = size;
MALI_DEBUG_PRINT(2, ("Memory Validator installed for Mali physical address base=0x%08X, size=0x%08X\n",
mali_mem_validator.phys_base, mali_mem_validator.size));
return _MALI_OSK_ERR_OK;
}
_mali_osk_errcode_t mali_mem_validation_check(u32 phys_addr, u32 size)
{
#if 0
if (phys_addr < (phys_addr + size)) { /* Don't allow overflow (or zero size) */
if ((0 == ( phys_addr & (~_MALI_OSK_CPU_PAGE_MASK))) &&
(0 == ( size & (~_MALI_OSK_CPU_PAGE_MASK)))) {
if ((phys_addr >= mali_mem_validator.phys_base) &&
((phys_addr + (size - 1)) >= mali_mem_validator.phys_base) &&
(phys_addr <= (mali_mem_validator.phys_base + (mali_mem_validator.size - 1))) &&
((phys_addr + (size - 1)) <= (mali_mem_validator.phys_base + (mali_mem_validator.size - 1))) ) {
MALI_DEBUG_PRINT(3, ("Accepted range 0x%08X + size 0x%08X (= 0x%08X)\n", phys_addr, size, (phys_addr + size - 1)));
return _MALI_OSK_ERR_OK;
}
}
}
MALI_PRINT_ERROR(("MALI PHYSICAL RANGE VALIDATION ERROR: The range supplied was: phys_base=0x%08X, size=0x%08X\n", phys_addr, size));
return _MALI_OSK_ERR_FAULT;
#else
return _MALI_OSK_ERR_OK;
#endif
}

View File

@@ -0,0 +1,19 @@
/*
* Copyright (C) 2011-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.
*/
#ifndef __MALI_MEM_VALIDATION_H__
#define __MALI_MEM_VALIDATION_H__
#include "mali_osk.h"
_mali_osk_errcode_t mali_mem_validation_add_range(u32 start, u32 size);
_mali_osk_errcode_t mali_mem_validation_check(u32 phys_addr, u32 size);
#endif /* __MALI_MEM_VALIDATION_H__ */

View File

@@ -0,0 +1,431 @@
/*
* Copyright (C) 2010-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.
*/
#include "mali_kernel_common.h"
#include "mali_osk.h"
#include "mali_osk_list.h"
#include "mali_ukk.h"
#include "mali_mmu.h"
#include "mali_hw_core.h"
#include "mali_group.h"
#include "mali_mmu_page_directory.h"
/**
* Size of the MMU registers in bytes
*/
#define MALI_MMU_REGISTERS_SIZE 0x24
/**
* MMU commands
* These are the commands that can be sent
* to the MMU unit.
*/
typedef enum mali_mmu_command {
MALI_MMU_COMMAND_ENABLE_PAGING = 0x00, /**< Enable paging (memory translation) */
MALI_MMU_COMMAND_DISABLE_PAGING = 0x01, /**< Disable paging (memory translation) */
MALI_MMU_COMMAND_ENABLE_STALL = 0x02, /**< Enable stall on page fault */
MALI_MMU_COMMAND_DISABLE_STALL = 0x03, /**< Disable stall on page fault */
MALI_MMU_COMMAND_ZAP_CACHE = 0x04, /**< Zap the entire page table cache */
MALI_MMU_COMMAND_PAGE_FAULT_DONE = 0x05, /**< Page fault processed */
MALI_MMU_COMMAND_HARD_RESET = 0x06 /**< Reset the MMU back to power-on settings */
} mali_mmu_command;
static void mali_mmu_probe_trigger(void *data);
static _mali_osk_errcode_t mali_mmu_probe_ack(void *data);
MALI_STATIC_INLINE _mali_osk_errcode_t mali_mmu_raw_reset(struct mali_mmu_core *mmu);
/* page fault queue flush helper pages
* note that the mapping pointers are currently unused outside of the initialization functions */
static u32 mali_page_fault_flush_page_directory = MALI_INVALID_PAGE;
static mali_io_address mali_page_fault_flush_page_directory_mapping = NULL;
static u32 mali_page_fault_flush_page_table = MALI_INVALID_PAGE;
static mali_io_address mali_page_fault_flush_page_table_mapping = NULL;
static u32 mali_page_fault_flush_data_page = MALI_INVALID_PAGE;
static mali_io_address mali_page_fault_flush_data_page_mapping = NULL;
/* an empty page directory (no address valid) which is active on any MMU not currently marked as in use */
static u32 mali_empty_page_directory_phys = MALI_INVALID_PAGE;
static mali_io_address mali_empty_page_directory_virt = NULL;
_mali_osk_errcode_t mali_mmu_initialize(void)
{
/* allocate the helper pages */
mali_empty_page_directory_phys = mali_allocate_empty_page(&mali_empty_page_directory_virt);
if(0 == mali_empty_page_directory_phys) {
MALI_DEBUG_PRINT_ERROR(("Mali MMU: Could not allocate empty page directory.\n"));
mali_empty_page_directory_phys = MALI_INVALID_PAGE;
return _MALI_OSK_ERR_NOMEM;
}
if (_MALI_OSK_ERR_OK != mali_create_fault_flush_pages(&mali_page_fault_flush_page_directory,
&mali_page_fault_flush_page_directory_mapping,
&mali_page_fault_flush_page_table,
&mali_page_fault_flush_page_table_mapping,
&mali_page_fault_flush_data_page,
&mali_page_fault_flush_data_page_mapping)) {
MALI_DEBUG_PRINT_ERROR(("Mali MMU: Could not allocate fault flush pages\n"));
mali_free_empty_page(mali_empty_page_directory_phys, mali_empty_page_directory_virt);
mali_empty_page_directory_phys = MALI_INVALID_PAGE;
mali_empty_page_directory_virt = NULL;
return _MALI_OSK_ERR_NOMEM;
}
return _MALI_OSK_ERR_OK;
}
void mali_mmu_terminate(void)
{
MALI_DEBUG_PRINT(3, ("Mali MMU: terminating\n"));
/* Free global helper pages */
mali_free_empty_page(mali_empty_page_directory_phys, mali_empty_page_directory_virt);
mali_empty_page_directory_phys = MALI_INVALID_PAGE;
mali_empty_page_directory_virt = NULL;
/* Free the page fault flush pages */
mali_destroy_fault_flush_pages(&mali_page_fault_flush_page_directory, &mali_page_fault_flush_page_directory_mapping,
&mali_page_fault_flush_page_table, &mali_page_fault_flush_page_table_mapping,
&mali_page_fault_flush_data_page, &mali_page_fault_flush_data_page_mapping);
}
struct mali_mmu_core *mali_mmu_create(_mali_osk_resource_t *resource, struct mali_group *group, mali_bool is_virtual)
{
struct mali_mmu_core* mmu = NULL;
MALI_DEBUG_ASSERT_POINTER(resource);
MALI_DEBUG_PRINT(2, ("Mali MMU: Creating Mali MMU: %s\n", resource->description));
mmu = _mali_osk_calloc(1,sizeof(struct mali_mmu_core));
if (NULL != mmu)
{
if (_MALI_OSK_ERR_OK == mali_hw_core_create(&mmu->hw_core, resource, MALI_MMU_REGISTERS_SIZE)) {
if (_MALI_OSK_ERR_OK == mali_group_add_mmu_core(group, mmu)) {
if (is_virtual) {
/* Skip reset and IRQ setup for virtual MMU */
return mmu;
}
if (_MALI_OSK_ERR_OK == mali_mmu_reset(mmu)) {
/* Setup IRQ handlers (which will do IRQ probing if needed) */
mmu->irq = _mali_osk_irq_init(resource->irq,
mali_group_upper_half_mmu,
group,
mali_mmu_probe_trigger,
mali_mmu_probe_ack,
mmu,
resource->description);
if (NULL != mmu->irq) {
return mmu;
} else {
MALI_PRINT_ERROR(("Mali MMU: Failed to setup interrupt handlers for MMU %s\n", mmu->hw_core.description));
}
}
mali_group_remove_mmu_core(group);
} else {
MALI_PRINT_ERROR(("Mali MMU: Failed to add core %s to group\n", mmu->hw_core.description));
}
mali_hw_core_delete(&mmu->hw_core);
}
_mali_osk_free(mmu);
} else {
MALI_PRINT_ERROR(("Failed to allocate memory for MMU\n"));
}
return NULL;
}
void mali_mmu_delete(struct mali_mmu_core *mmu)
{
if (NULL != mmu->irq) {
_mali_osk_irq_term(mmu->irq);
}
mali_hw_core_delete(&mmu->hw_core);
_mali_osk_free(mmu);
}
static void mali_mmu_enable_paging(struct mali_mmu_core *mmu)
{
int i;
mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_ENABLE_PAGING);
for (i = 0; i < MALI_REG_POLL_COUNT_FAST; ++i) {
if (mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_STATUS) & MALI_MMU_STATUS_BIT_PAGING_ENABLED) {
break;
}
}
if (MALI_REG_POLL_COUNT_FAST == i) {
MALI_PRINT_ERROR(("Enable paging request failed, MMU status is 0x%08X\n", mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_STATUS)));
}
}
/**
* Issues the enable stall command to the MMU and waits for HW to complete the request
* @param mmu The MMU to enable paging for
* @return MALI_TRUE if HW stall was successfully engaged, otherwise MALI_FALSE (req timed out)
*/
static mali_bool mali_mmu_enable_stall(struct mali_mmu_core *mmu)
{
int i;
u32 mmu_status = mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_STATUS);
if ( 0 == (mmu_status & MALI_MMU_STATUS_BIT_PAGING_ENABLED) ) {
MALI_DEBUG_PRINT(4, ("MMU stall is implicit when Paging is not enabled.\n"));
return MALI_TRUE;
}
if ( mmu_status & MALI_MMU_STATUS_BIT_PAGE_FAULT_ACTIVE ) {
MALI_DEBUG_PRINT(3, ("Aborting MMU stall request since it is in pagefault state.\n"));
return MALI_FALSE;
}
mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_ENABLE_STALL);
for (i = 0; i < MALI_REG_POLL_COUNT_FAST; ++i) {
mmu_status = mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_STATUS);
if (mmu_status & MALI_MMU_STATUS_BIT_PAGE_FAULT_ACTIVE) {
break;
}
if ((mmu_status & MALI_MMU_STATUS_BIT_STALL_ACTIVE) && (0 == (mmu_status & MALI_MMU_STATUS_BIT_STALL_NOT_ACTIVE))) {
break;
}
if (0 == (mmu_status & ( MALI_MMU_STATUS_BIT_PAGING_ENABLED ))) {
break;
}
}
if (MALI_REG_POLL_COUNT_FAST == i) {
MALI_DEBUG_PRINT(2, ("Enable stall request failed, MMU status is 0x%08X\n", mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_STATUS)));
return MALI_FALSE;
}
if ( mmu_status & MALI_MMU_STATUS_BIT_PAGE_FAULT_ACTIVE ) {
MALI_DEBUG_PRINT(2, ("Aborting MMU stall request since it has a pagefault.\n"));
return MALI_FALSE;
}
return MALI_TRUE;
}
/**
* Issues the disable stall command to the MMU and waits for HW to complete the request
* @param mmu The MMU to enable paging for
*/
static void mali_mmu_disable_stall(struct mali_mmu_core *mmu)
{
int i;
u32 mmu_status = mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_STATUS);
if ( 0 == (mmu_status & MALI_MMU_STATUS_BIT_PAGING_ENABLED )) {
MALI_DEBUG_PRINT(3, ("MMU disable skipped since it was not enabled.\n"));
return;
}
if (mmu_status & MALI_MMU_STATUS_BIT_PAGE_FAULT_ACTIVE) {
MALI_DEBUG_PRINT(2, ("Aborting MMU disable stall request since it is in pagefault state.\n"));
return;
}
mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_DISABLE_STALL);
for (i = 0; i < MALI_REG_POLL_COUNT_FAST; ++i) {
u32 status = mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_STATUS);
if ( 0 == (status & MALI_MMU_STATUS_BIT_STALL_ACTIVE) ) {
break;
}
if ( status & MALI_MMU_STATUS_BIT_PAGE_FAULT_ACTIVE ) {
break;
}
if ( 0 == (mmu_status & MALI_MMU_STATUS_BIT_PAGING_ENABLED )) {
break;
}
}
if (MALI_REG_POLL_COUNT_FAST == i) MALI_DEBUG_PRINT(1,("Disable stall request failed, MMU status is 0x%08X\n", mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_STATUS)));
}
void mali_mmu_page_fault_done(struct mali_mmu_core *mmu)
{
MALI_DEBUG_PRINT(4, ("Mali MMU: %s: Leaving page fault mode\n", mmu->hw_core.description));
mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_PAGE_FAULT_DONE);
}
MALI_STATIC_INLINE _mali_osk_errcode_t mali_mmu_raw_reset(struct mali_mmu_core *mmu)
{
int i;
mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_DTE_ADDR, 0xCAFEBABE);
MALI_DEBUG_ASSERT(0xCAFEB000 == mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_DTE_ADDR));
mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_HARD_RESET);
for (i = 0; i < MALI_REG_POLL_COUNT_FAST; ++i) {
if (mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_DTE_ADDR) == 0) {
break;
}
}
if (MALI_REG_POLL_COUNT_FAST == i) {
MALI_PRINT_ERROR(("Reset request failed, MMU status is 0x%08X\n", mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_STATUS)));
return _MALI_OSK_ERR_FAULT;
}
return _MALI_OSK_ERR_OK;
}
_mali_osk_errcode_t mali_mmu_reset(struct mali_mmu_core *mmu)
{
_mali_osk_errcode_t err = _MALI_OSK_ERR_FAULT;
mali_bool stall_success;
MALI_DEBUG_ASSERT_POINTER(mmu);
stall_success = mali_mmu_enable_stall(mmu);
if (!stall_success) {
err = _MALI_OSK_ERR_BUSY;
}
MALI_DEBUG_PRINT(3, ("Mali MMU: mali_kernel_mmu_reset: %s\n", mmu->hw_core.description));
if (_MALI_OSK_ERR_OK == mali_mmu_raw_reset(mmu)) {
mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_INT_MASK, MALI_MMU_INTERRUPT_PAGE_FAULT | MALI_MMU_INTERRUPT_READ_BUS_ERROR);
/* no session is active, so just activate the empty page directory */
mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_DTE_ADDR, mali_empty_page_directory_phys);
mali_mmu_enable_paging(mmu);
err = _MALI_OSK_ERR_OK;
}
mali_mmu_disable_stall(mmu);
return err;
}
mali_bool mali_mmu_zap_tlb(struct mali_mmu_core *mmu)
{
mali_bool stall_success = mali_mmu_enable_stall(mmu);
mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_ZAP_CACHE);
if (MALI_FALSE == stall_success) {
/* False means that it is in Pagefault state. Not possible to disable_stall then */
return MALI_FALSE;
}
mali_mmu_disable_stall(mmu);
return MALI_TRUE;
}
void mali_mmu_zap_tlb_without_stall(struct mali_mmu_core *mmu)
{
mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_ZAP_CACHE);
}
void mali_mmu_invalidate_page(struct mali_mmu_core *mmu, u32 mali_address)
{
mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_ZAP_ONE_LINE, MALI_MMU_PDE_ENTRY(mali_address));
}
static void mali_mmu_activate_address_space(struct mali_mmu_core *mmu, u32 page_directory)
{
/* The MMU must be in stalled or page fault mode, for this writing to work */
MALI_DEBUG_ASSERT( 0 != ( mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_STATUS)
& (MALI_MMU_STATUS_BIT_STALL_ACTIVE|MALI_MMU_STATUS_BIT_PAGE_FAULT_ACTIVE) ) );
mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_DTE_ADDR, page_directory);
mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_ZAP_CACHE);
}
void mali_mmu_activate_page_directory(struct mali_mmu_core *mmu, struct mali_page_directory *pagedir)
{
mali_bool stall_success;
MALI_DEBUG_ASSERT_POINTER(mmu);
MALI_DEBUG_PRINT(5, ("Asked to activate page directory 0x%x on MMU %s\n", pagedir, mmu->hw_core.description));
stall_success = mali_mmu_enable_stall(mmu);
MALI_DEBUG_ASSERT(stall_success);
MALI_IGNORE(stall_success);
mali_mmu_activate_address_space(mmu, pagedir->page_directory);
mali_mmu_disable_stall(mmu);
}
void mali_mmu_activate_empty_page_directory(struct mali_mmu_core* mmu)
{
mali_bool stall_success;
MALI_DEBUG_ASSERT_POINTER(mmu);
MALI_DEBUG_PRINT(3, ("Activating the empty page directory on MMU %s\n", mmu->hw_core.description));
stall_success = mali_mmu_enable_stall(mmu);
/* This function can only be called when the core is idle, so it could not fail. */
MALI_DEBUG_ASSERT(stall_success);
MALI_IGNORE(stall_success);
mali_mmu_activate_address_space(mmu, mali_empty_page_directory_phys);
mali_mmu_disable_stall(mmu);
}
void mali_mmu_activate_fault_flush_page_directory(struct mali_mmu_core* mmu)
{
mali_bool stall_success;
MALI_DEBUG_ASSERT_POINTER(mmu);
MALI_DEBUG_PRINT(3, ("Activating the page fault flush page directory on MMU %s\n", mmu->hw_core.description));
stall_success = mali_mmu_enable_stall(mmu);
/* This function is expect to fail the stalling, since it might be in PageFault mode when it is called */
mali_mmu_activate_address_space(mmu, mali_page_fault_flush_page_directory);
if ( MALI_TRUE==stall_success ) mali_mmu_disable_stall(mmu);
}
/* Is called when we want the mmu to give an interrupt */
static void mali_mmu_probe_trigger(void *data)
{
struct mali_mmu_core *mmu = (struct mali_mmu_core *)data;
mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_INT_RAWSTAT, MALI_MMU_INTERRUPT_PAGE_FAULT|MALI_MMU_INTERRUPT_READ_BUS_ERROR);
}
/* Is called when the irq probe wants the mmu to acknowledge an interrupt from the hw */
static _mali_osk_errcode_t mali_mmu_probe_ack(void *data)
{
struct mali_mmu_core *mmu = (struct mali_mmu_core *)data;
u32 int_stat;
int_stat = mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_INT_STATUS);
MALI_DEBUG_PRINT(2, ("mali_mmu_probe_irq_acknowledge: intstat 0x%x\n", int_stat));
if (int_stat & MALI_MMU_INTERRUPT_PAGE_FAULT) {
MALI_DEBUG_PRINT(2, ("Probe: Page fault detect: PASSED\n"));
mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_INT_CLEAR, MALI_MMU_INTERRUPT_PAGE_FAULT);
} else {
MALI_DEBUG_PRINT(1, ("Probe: Page fault detect: FAILED\n"));
}
if (int_stat & MALI_MMU_INTERRUPT_READ_BUS_ERROR) {
MALI_DEBUG_PRINT(2, ("Probe: Bus read error detect: PASSED\n"));
mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_INT_CLEAR, MALI_MMU_INTERRUPT_READ_BUS_ERROR);
} else {
MALI_DEBUG_PRINT(1, ("Probe: Bus read error detect: FAILED\n"));
}
if ( (int_stat & (MALI_MMU_INTERRUPT_PAGE_FAULT|MALI_MMU_INTERRUPT_READ_BUS_ERROR)) ==
(MALI_MMU_INTERRUPT_PAGE_FAULT|MALI_MMU_INTERRUPT_READ_BUS_ERROR)) {
return _MALI_OSK_ERR_OK;
}
return _MALI_OSK_ERR_FAULT;
}
#if 0
void mali_mmu_print_state(struct mali_mmu_core *mmu)
{
MALI_DEBUG_PRINT(2, ("MMU: State of %s is 0x%08x\n", mmu->hw_core.description, mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_STATUS)));
}
#endif

View File

@@ -0,0 +1,123 @@
/*
* Copyright (C) 2010-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.
*/
#ifndef __MALI_MMU_H__
#define __MALI_MMU_H__
#include "mali_osk.h"
#include "mali_mmu_page_directory.h"
#include "mali_hw_core.h"
#include <linux/kernel.h>
#include <asm/io.h>
#include <mach/am_regs.h>
#include <linux/module.h>
#if MESON_CPU_TYPE == MESON_CPU_TYPE_MESON6
#include "meson_m400/mali_fix.h"
#endif
/* Forward declaration from mali_group.h */
struct mali_group;
/**
* MMU register numbers
* Used in the register read/write routines.
* See the hardware documentation for more information about each register
*/
typedef enum mali_mmu_register {
MALI_MMU_REGISTER_DTE_ADDR = 0x0000, /**< Current Page Directory Pointer */
MALI_MMU_REGISTER_STATUS = 0x0004, /**< Status of the MMU */
MALI_MMU_REGISTER_COMMAND = 0x0008, /**< Command register, used to control the MMU */
MALI_MMU_REGISTER_PAGE_FAULT_ADDR = 0x000C, /**< Logical address of the last page fault */
MALI_MMU_REGISTER_ZAP_ONE_LINE = 0x010, /**< Used to invalidate the mapping of a single page from the MMU */
MALI_MMU_REGISTER_INT_RAWSTAT = 0x0014, /**< Raw interrupt status, all interrupts visible */
MALI_MMU_REGISTER_INT_CLEAR = 0x0018, /**< Indicate to the MMU that the interrupt has been received */
MALI_MMU_REGISTER_INT_MASK = 0x001C, /**< Enable/disable types of interrupts */
MALI_MMU_REGISTER_INT_STATUS = 0x0020 /**< Interrupt status based on the mask */
} mali_mmu_register;
/**
* MMU interrupt register bits
* Each cause of the interrupt is reported
* through the (raw) interrupt status registers.
* Multiple interrupts can be pending, so multiple bits
* can be set at once.
*/
typedef enum mali_mmu_interrupt {
MALI_MMU_INTERRUPT_PAGE_FAULT = 0x01, /**< A page fault occured */
MALI_MMU_INTERRUPT_READ_BUS_ERROR = 0x02 /**< A bus read error occured */
} mali_mmu_interrupt;
typedef enum mali_mmu_status_bits {
MALI_MMU_STATUS_BIT_PAGING_ENABLED = 1 << 0,
MALI_MMU_STATUS_BIT_PAGE_FAULT_ACTIVE = 1 << 1,
MALI_MMU_STATUS_BIT_STALL_ACTIVE = 1 << 2,
MALI_MMU_STATUS_BIT_IDLE = 1 << 3,
MALI_MMU_STATUS_BIT_REPLAY_BUFFER_EMPTY = 1 << 4,
MALI_MMU_STATUS_BIT_PAGE_FAULT_IS_WRITE = 1 << 5,
MALI_MMU_STATUS_BIT_STALL_NOT_ACTIVE = 1 << 31,
} mali_mmu_status_bits;
/**
* Definition of the MMU struct
* Used to track a MMU unit in the system.
* Contains information about the mapping of the registers
*/
struct mali_mmu_core {
struct mali_hw_core hw_core; /**< Common for all HW cores */
_mali_osk_irq_t *irq; /**< IRQ handler */
};
_mali_osk_errcode_t mali_mmu_initialize(void);
void mali_mmu_terminate(void);
struct mali_mmu_core *mali_mmu_create(_mali_osk_resource_t *resource, struct mali_group *group, mali_bool is_virtual);
void mali_mmu_delete(struct mali_mmu_core *mmu);
_mali_osk_errcode_t mali_mmu_reset(struct mali_mmu_core *mmu);
mali_bool mali_mmu_zap_tlb(struct mali_mmu_core *mmu);
void mali_mmu_zap_tlb_without_stall(struct mali_mmu_core *mmu);
void mali_mmu_invalidate_page(struct mali_mmu_core *mmu, u32 mali_address);
void mali_mmu_activate_page_directory(struct mali_mmu_core* mmu, struct mali_page_directory *pagedir);
void mali_mmu_activate_empty_page_directory(struct mali_mmu_core* mmu);
void mali_mmu_activate_fault_flush_page_directory(struct mali_mmu_core* mmu);
void mali_mmu_page_fault_done(struct mali_mmu_core *mmu);
/*** Register reading/writing functions ***/
MALI_STATIC_INLINE u32 mali_mmu_get_int_status(struct mali_mmu_core *mmu)
{
return mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_INT_STATUS);
}
MALI_STATIC_INLINE u32 mali_mmu_get_rawstat(struct mali_mmu_core *mmu)
{
return mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_INT_RAWSTAT);
}
MALI_STATIC_INLINE void mali_mmu_mask_all_interrupts(struct mali_mmu_core *mmu)
{
mali_hw_core_register_write(&mmu->hw_core, MALI_MMU_REGISTER_INT_MASK, 0);
}
MALI_STATIC_INLINE u32 mali_mmu_get_status(struct mali_mmu_core *mmu)
{
return mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_STATUS);
}
MALI_STATIC_INLINE u32 mali_mmu_get_page_fault_addr(struct mali_mmu_core *mmu)
{
return mali_hw_core_register_read(&mmu->hw_core, MALI_MMU_REGISTER_PAGE_FAULT_ADDR);
}
#endif /* __MALI_MMU_H__ */

View File

@@ -0,0 +1,436 @@
/*
* Copyright (C) 2011-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.
*/
#include "mali_kernel_common.h"
#include "mali_osk.h"
#include "mali_uk_types.h"
#include "mali_mmu_page_directory.h"
#include "mali_memory.h"
#include "mali_l2_cache.h"
static _mali_osk_errcode_t fill_page(mali_io_address mapping, u32 data);
u32 mali_allocate_empty_page(mali_io_address *virt_addr)
{
_mali_osk_errcode_t err;
mali_io_address mapping;
u32 address;
if(_MALI_OSK_ERR_OK != mali_mmu_get_table_page(&address, &mapping)) {
/* Allocation failed */
MALI_DEBUG_PRINT(2, ("Mali MMU: Failed to get table page for empty pgdir\n"));
return 0;
}
MALI_DEBUG_ASSERT_POINTER( mapping );
err = fill_page(mapping, 0);
if (_MALI_OSK_ERR_OK != err) {
mali_mmu_release_table_page(address, mapping);
MALI_DEBUG_PRINT(2, ("Mali MMU: Failed to zero page\n"));
return 0;
}
*virt_addr = mapping;
return address;
}
void mali_free_empty_page(u32 address, mali_io_address virt_addr)
{
if (MALI_INVALID_PAGE != address) {
mali_mmu_release_table_page(address, virt_addr);
}
}
_mali_osk_errcode_t mali_create_fault_flush_pages(u32 *page_directory, mali_io_address *page_directory_mapping,
u32 *page_table, mali_io_address *page_table_mapping,
u32 *data_page, mali_io_address *data_page_mapping)
{
_mali_osk_errcode_t err;
err = mali_mmu_get_table_page(data_page, data_page_mapping);
if (_MALI_OSK_ERR_OK == err) {
err = mali_mmu_get_table_page(page_table, page_table_mapping);
if (_MALI_OSK_ERR_OK == err) {
err = mali_mmu_get_table_page(page_directory, page_directory_mapping);
if (_MALI_OSK_ERR_OK == err) {
fill_page(*data_page_mapping, 0);
fill_page(*page_table_mapping, *data_page | MALI_MMU_FLAGS_DEFAULT);
fill_page(*page_directory_mapping, *page_table | MALI_MMU_FLAGS_PRESENT);
MALI_SUCCESS;
}
mali_mmu_release_table_page(*page_table, *page_table_mapping);
*page_table = MALI_INVALID_PAGE;
}
mali_mmu_release_table_page(*data_page, *data_page_mapping);
*data_page = MALI_INVALID_PAGE;
}
return err;
}
void mali_destroy_fault_flush_pages(u32 *page_directory, mali_io_address *page_directory_mapping,
u32 *page_table, mali_io_address *page_table_mapping,
u32 *data_page, mali_io_address *data_page_mapping)
{
if (MALI_INVALID_PAGE != *page_directory) {
mali_mmu_release_table_page(*page_directory, *page_directory_mapping);
*page_directory = MALI_INVALID_PAGE;
*page_directory_mapping = NULL;
}
if (MALI_INVALID_PAGE != *page_table) {
mali_mmu_release_table_page(*page_table, *page_table_mapping);
*page_table = MALI_INVALID_PAGE;
*page_table_mapping = NULL;
}
if (MALI_INVALID_PAGE != *data_page) {
mali_mmu_release_table_page(*data_page, *data_page_mapping);
*data_page = MALI_INVALID_PAGE;
*data_page_mapping = NULL;
}
}
static _mali_osk_errcode_t fill_page(mali_io_address mapping, u32 data)
{
int i;
MALI_DEBUG_ASSERT_POINTER( mapping );
for(i = 0; i < MALI_MMU_PAGE_SIZE/4; i++) {
_mali_osk_mem_iowrite32_relaxed( mapping, i * sizeof(u32), data);
}
_mali_osk_mem_barrier();
MALI_SUCCESS;
}
_mali_osk_errcode_t mali_mmu_pagedir_map(struct mali_page_directory *pagedir, u32 mali_address, u32 size)
{
const int first_pde = MALI_MMU_PDE_ENTRY(mali_address);
const int last_pde = MALI_MMU_PDE_ENTRY(mali_address + size - 1);
_mali_osk_errcode_t err;
mali_io_address pde_mapping;
u32 pde_phys;
int i;
if (last_pde < first_pde) {
MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS);
}
for(i = first_pde; i <= last_pde; i++) {
if(0 == (_mali_osk_mem_ioread32(pagedir->page_directory_mapped, i*sizeof(u32)) & MALI_MMU_FLAGS_PRESENT)) {
/* Page table not present */
MALI_DEBUG_ASSERT(0 == pagedir->page_entries_usage_count[i]);
MALI_DEBUG_ASSERT(NULL == pagedir->page_entries_mapped[i]);
err = mali_mmu_get_table_page(&pde_phys, &pde_mapping);
if(_MALI_OSK_ERR_OK != err) {
MALI_PRINT_ERROR(("Failed to allocate page table page.\n"));
return err;
}
pagedir->page_entries_mapped[i] = pde_mapping;
/* Update PDE, mark as present */
_mali_osk_mem_iowrite32_relaxed(pagedir->page_directory_mapped, i*sizeof(u32),
pde_phys | MALI_MMU_FLAGS_PRESENT);
MALI_DEBUG_ASSERT(0 == pagedir->page_entries_usage_count[i]);
pagedir->page_entries_usage_count[i] = 1;
} else {
pagedir->page_entries_usage_count[i]++;
}
}
_mali_osk_write_mem_barrier();
MALI_SUCCESS;
}
MALI_STATIC_INLINE void mali_mmu_zero_pte(mali_io_address page_table, u32 mali_address, u32 size)
{
int i;
const int first_pte = MALI_MMU_PTE_ENTRY(mali_address);
const int last_pte = MALI_MMU_PTE_ENTRY(mali_address + size - 1);
for (i = first_pte; i <= last_pte; i++) {
_mali_osk_mem_iowrite32_relaxed(page_table, i * sizeof(u32), 0);
}
}
_mali_osk_errcode_t mali_mmu_pagedir_unmap(struct mali_page_directory *pagedir, u32 mali_address, u32 size)
{
const int first_pde = MALI_MMU_PDE_ENTRY(mali_address);
const int last_pde = MALI_MMU_PDE_ENTRY(mali_address + size - 1);
u32 left = size;
int i;
mali_bool pd_changed = MALI_FALSE;
u32 pages_to_invalidate[3]; /* hard-coded to 3: max two pages from the PT level plus max one page from PD level */
u32 num_pages_inv = 0;
mali_bool invalidate_all = MALI_FALSE; /* safety mechanism in case page_entries_usage_count is unreliable */
/* For all page directory entries in range. */
for (i = first_pde; i <= last_pde; i++) {
u32 size_in_pde, offset;
MALI_DEBUG_ASSERT_POINTER(pagedir->page_entries_mapped[i]);
MALI_DEBUG_ASSERT(0 != pagedir->page_entries_usage_count[i]);
/* Offset into page table, 0 if mali_address is 4MiB aligned */
offset = (mali_address & (MALI_MMU_VIRTUAL_PAGE_SIZE - 1));
if (left < MALI_MMU_VIRTUAL_PAGE_SIZE - offset) {
size_in_pde = left;
} else {
size_in_pde = MALI_MMU_VIRTUAL_PAGE_SIZE - offset;
}
pagedir->page_entries_usage_count[i]--;
/* If entire page table is unused, free it */
if (0 == pagedir->page_entries_usage_count[i]) {
u32 page_phys;
void *page_virt;
MALI_DEBUG_PRINT(4, ("Releasing page table as this is the last reference\n"));
/* last reference removed, no need to zero out each PTE */
page_phys = MALI_MMU_ENTRY_ADDRESS(_mali_osk_mem_ioread32(pagedir->page_directory_mapped, i*sizeof(u32)));
page_virt = pagedir->page_entries_mapped[i];
pagedir->page_entries_mapped[i] = NULL;
_mali_osk_mem_iowrite32_relaxed(pagedir->page_directory_mapped, i*sizeof(u32), 0);
mali_mmu_release_table_page(page_phys, page_virt);
pd_changed = MALI_TRUE;
} else {
MALI_DEBUG_ASSERT(num_pages_inv < 2);
if (num_pages_inv < 2) {
pages_to_invalidate[num_pages_inv] = mali_page_directory_get_phys_address(pagedir, i);
num_pages_inv++;
} else {
invalidate_all = MALI_TRUE;
}
/* If part of the page table is still in use, zero the relevant PTEs */
mali_mmu_zero_pte(pagedir->page_entries_mapped[i], mali_address, size_in_pde);
}
left -= size_in_pde;
mali_address += size_in_pde;
}
_mali_osk_write_mem_barrier();
/* L2 pages invalidation */
if (MALI_TRUE == pd_changed) {
MALI_DEBUG_ASSERT(num_pages_inv < 3);
if (num_pages_inv < 3) {
pages_to_invalidate[num_pages_inv] = pagedir->page_directory;
num_pages_inv++;
} else {
invalidate_all = MALI_TRUE;
}
}
if (invalidate_all) {
mali_l2_cache_invalidate_all();
} else {
mali_l2_cache_invalidate_all_pages(pages_to_invalidate, num_pages_inv);
}
MALI_SUCCESS;
}
struct mali_page_directory *mali_mmu_pagedir_alloc(void)
{
struct mali_page_directory *pagedir;
pagedir = _mali_osk_calloc(1, sizeof(struct mali_page_directory));
if(NULL == pagedir) {
return NULL;
}
if(_MALI_OSK_ERR_OK != mali_mmu_get_table_page(&pagedir->page_directory, &pagedir->page_directory_mapped)) {
_mali_osk_free(pagedir);
return NULL;
}
/* Zero page directory */
fill_page(pagedir->page_directory_mapped, 0);
return pagedir;
}
void mali_mmu_pagedir_free(struct mali_page_directory *pagedir)
{
const int num_page_table_entries = sizeof(pagedir->page_entries_mapped) / sizeof(pagedir->page_entries_mapped[0]);
int i;
/* Free referenced page tables and zero PDEs. */
for (i = 0; i < num_page_table_entries; i++) {
if (pagedir->page_directory_mapped && (_mali_osk_mem_ioread32(pagedir->page_directory_mapped, sizeof(u32)*i) & MALI_MMU_FLAGS_PRESENT)) {
u32 phys = _mali_osk_mem_ioread32(pagedir->page_directory_mapped, i*sizeof(u32)) & ~MALI_MMU_FLAGS_MASK;
_mali_osk_mem_iowrite32_relaxed(pagedir->page_directory_mapped, i * sizeof(u32), 0);
mali_mmu_release_table_page(phys, pagedir->page_entries_mapped[i]);
}
}
_mali_osk_write_mem_barrier();
/* Free the page directory page. */
mali_mmu_release_table_page(pagedir->page_directory, pagedir->page_directory_mapped);
_mali_osk_free(pagedir);
}
void mali_mmu_pagedir_update(struct mali_page_directory *pagedir, u32 mali_address, u32 phys_address, u32 size, u32 permission_bits)
{
u32 end_address = mali_address + size;
/* Map physical pages into MMU page tables */
for ( ; mali_address < end_address; mali_address += MALI_MMU_PAGE_SIZE, phys_address += MALI_MMU_PAGE_SIZE) {
MALI_DEBUG_ASSERT_POINTER(pagedir->page_entries_mapped[MALI_MMU_PDE_ENTRY(mali_address)]);
_mali_osk_mem_iowrite32_relaxed(pagedir->page_entries_mapped[MALI_MMU_PDE_ENTRY(mali_address)],
MALI_MMU_PTE_ENTRY(mali_address) * sizeof(u32),
phys_address | permission_bits);
}
}
u32 mali_page_directory_get_phys_address(struct mali_page_directory *pagedir, u32 index)
{
return (_mali_osk_mem_ioread32(pagedir->page_directory_mapped, index*sizeof(u32)) & ~MALI_MMU_FLAGS_MASK);
}
/* For instrumented */
struct dump_info {
u32 buffer_left;
u32 register_writes_size;
u32 page_table_dump_size;
u32 *buffer;
};
static _mali_osk_errcode_t writereg(u32 where, u32 what, const char *comment, struct dump_info *info)
{
if (NULL != info) {
info->register_writes_size += sizeof(u32)*2; /* two 32-bit words */
if (NULL != info->buffer) {
/* check that we have enough space */
if (info->buffer_left < sizeof(u32)*2) MALI_ERROR(_MALI_OSK_ERR_NOMEM);
*info->buffer = where;
info->buffer++;
*info->buffer = what;
info->buffer++;
info->buffer_left -= sizeof(u32)*2;
}
}
MALI_SUCCESS;
}
static _mali_osk_errcode_t mali_mmu_dump_page(mali_io_address page, u32 phys_addr, struct dump_info * info)
{
if (NULL != info) {
/* 4096 for the page and 4 bytes for the address */
const u32 page_size_in_elements = MALI_MMU_PAGE_SIZE / 4;
const u32 page_size_in_bytes = MALI_MMU_PAGE_SIZE;
const u32 dump_size_in_bytes = MALI_MMU_PAGE_SIZE + 4;
info->page_table_dump_size += dump_size_in_bytes;
if (NULL != info->buffer) {
if (info->buffer_left < dump_size_in_bytes) MALI_ERROR(_MALI_OSK_ERR_NOMEM);
*info->buffer = phys_addr;
info->buffer++;
_mali_osk_memcpy(info->buffer, page, page_size_in_bytes);
info->buffer += page_size_in_elements;
info->buffer_left -= dump_size_in_bytes;
}
}
MALI_SUCCESS;
}
static _mali_osk_errcode_t dump_mmu_page_table(struct mali_page_directory *pagedir, struct dump_info * info)
{
MALI_DEBUG_ASSERT_POINTER(pagedir);
MALI_DEBUG_ASSERT_POINTER(info);
if (NULL != pagedir->page_directory_mapped) {
int i;
MALI_CHECK_NO_ERROR(
mali_mmu_dump_page(pagedir->page_directory_mapped, pagedir->page_directory, info)
);
for (i = 0; i < 1024; i++) {
if (NULL != pagedir->page_entries_mapped[i]) {
MALI_CHECK_NO_ERROR(
mali_mmu_dump_page(pagedir->page_entries_mapped[i],
_mali_osk_mem_ioread32(pagedir->page_directory_mapped,
i * sizeof(u32)) & ~MALI_MMU_FLAGS_MASK, info)
);
}
}
}
MALI_SUCCESS;
}
static _mali_osk_errcode_t dump_mmu_registers(struct mali_page_directory *pagedir, struct dump_info * info)
{
MALI_CHECK_NO_ERROR(writereg(0x00000000, pagedir->page_directory,
"set the page directory address", info));
MALI_CHECK_NO_ERROR(writereg(0x00000008, 4, "zap???", info));
MALI_CHECK_NO_ERROR(writereg(0x00000008, 0, "enable paging", info));
MALI_SUCCESS;
}
_mali_osk_errcode_t _mali_ukk_query_mmu_page_table_dump_size( _mali_uk_query_mmu_page_table_dump_size_s *args )
{
struct dump_info info = { 0, 0, 0, NULL };
struct mali_session_data * session_data;
MALI_DEBUG_ASSERT_POINTER(args);
MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS);
session_data = (struct mali_session_data *)(args->ctx);
MALI_CHECK_NO_ERROR(dump_mmu_registers(session_data->page_directory, &info));
MALI_CHECK_NO_ERROR(dump_mmu_page_table(session_data->page_directory, &info));
args->size = info.register_writes_size + info.page_table_dump_size;
MALI_SUCCESS;
}
_mali_osk_errcode_t _mali_ukk_dump_mmu_page_table( _mali_uk_dump_mmu_page_table_s * args )
{
struct dump_info info = { 0, 0, 0, NULL };
struct mali_session_data * session_data;
MALI_DEBUG_ASSERT_POINTER(args);
MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS);
MALI_CHECK_NON_NULL(args->buffer, _MALI_OSK_ERR_INVALID_ARGS);
session_data = (struct mali_session_data *)(args->ctx);
info.buffer_left = args->size;
info.buffer = args->buffer;
args->register_writes = info.buffer;
MALI_CHECK_NO_ERROR(dump_mmu_registers(session_data->page_directory, &info));
args->page_table_dump = info.buffer;
MALI_CHECK_NO_ERROR(dump_mmu_page_table(session_data->page_directory, &info));
args->register_writes_size = info.register_writes_size;
args->page_table_dump_size = info.page_table_dump_size;
MALI_SUCCESS;
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright (C) 2011-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.
*/
#ifndef __MALI_MMU_PAGE_DIRECTORY_H__
#define __MALI_MMU_PAGE_DIRECTORY_H__
#include "mali_osk.h"
/**
* Size of an MMU page in bytes
*/
#define MALI_MMU_PAGE_SIZE 0x1000
/*
* Size of the address space referenced by a page table page
*/
#define MALI_MMU_VIRTUAL_PAGE_SIZE 0x400000 /* 4 MiB */
/**
* Page directory index from address
* Calculates the page directory index from the given address
*/
#define MALI_MMU_PDE_ENTRY(address) (((address)>>22) & 0x03FF)
/**
* Page table index from address
* Calculates the page table index from the given address
*/
#define MALI_MMU_PTE_ENTRY(address) (((address)>>12) & 0x03FF)
/**
* Extract the memory address from an PDE/PTE entry
*/
#define MALI_MMU_ENTRY_ADDRESS(value) ((value) & 0xFFFFFC00)
#define MALI_INVALID_PAGE ((u32)(~0))
/**
*
*/
typedef enum mali_mmu_entry_flags {
MALI_MMU_FLAGS_PRESENT = 0x01,
MALI_MMU_FLAGS_READ_PERMISSION = 0x02,
MALI_MMU_FLAGS_WRITE_PERMISSION = 0x04,
MALI_MMU_FLAGS_OVERRIDE_CACHE = 0x8,
MALI_MMU_FLAGS_WRITE_CACHEABLE = 0x10,
MALI_MMU_FLAGS_WRITE_ALLOCATE = 0x20,
MALI_MMU_FLAGS_WRITE_BUFFERABLE = 0x40,
MALI_MMU_FLAGS_READ_CACHEABLE = 0x80,
MALI_MMU_FLAGS_READ_ALLOCATE = 0x100,
MALI_MMU_FLAGS_MASK = 0x1FF,
} mali_mmu_entry_flags;
#define MALI_MMU_FLAGS_FORCE_GP_READ_ALLOCATE ( \
MALI_MMU_FLAGS_PRESENT | \
MALI_MMU_FLAGS_READ_PERMISSION | \
MALI_MMU_FLAGS_WRITE_PERMISSION | \
MALI_MMU_FLAGS_OVERRIDE_CACHE | \
MALI_MMU_FLAGS_WRITE_CACHEABLE | \
MALI_MMU_FLAGS_WRITE_BUFFERABLE | \
MALI_MMU_FLAGS_READ_CACHEABLE | \
MALI_MMU_FLAGS_READ_ALLOCATE )
#define MALI_MMU_FLAGS_DEFAULT ( \
MALI_MMU_FLAGS_PRESENT | \
MALI_MMU_FLAGS_READ_PERMISSION | \
MALI_MMU_FLAGS_WRITE_PERMISSION )
struct mali_page_directory {
u32 page_directory; /**< Physical address of the memory session's page directory */
mali_io_address page_directory_mapped; /**< Pointer to the mapped version of the page directory into the kernel's address space */
mali_io_address page_entries_mapped[1024]; /**< Pointers to the page tables which exists in the page directory mapped into the kernel's address space */
u32 page_entries_usage_count[1024]; /**< Tracks usage count of the page table pages, so they can be releases on the last reference */
};
/* Map Mali virtual address space (i.e. ensure page tables exist for the virtual range) */
_mali_osk_errcode_t mali_mmu_pagedir_map(struct mali_page_directory *pagedir, u32 mali_address, u32 size);
_mali_osk_errcode_t mali_mmu_pagedir_unmap(struct mali_page_directory *pagedir, u32 mali_address, u32 size);
/* Back virtual address space with actual pages. Assumes input is contiguous and 4k aligned. */
void mali_mmu_pagedir_update(struct mali_page_directory *pagedir, u32 mali_address, u32 phys_address, u32 size, u32 cache_settings);
u32 mali_page_directory_get_phys_address(struct mali_page_directory *pagedir, u32 index);
u32 mali_allocate_empty_page(mali_io_address *virtual);
void mali_free_empty_page(u32 address, mali_io_address virtual);
_mali_osk_errcode_t mali_create_fault_flush_pages(u32 *page_directory, mali_io_address *page_directory_mapping,
u32 *page_table, mali_io_address *page_table_mapping,
u32 *data_page, mali_io_address *data_page_mapping);
void mali_destroy_fault_flush_pages(u32 *page_directory, mali_io_address *page_directory_mapping,
u32 *page_table, mali_io_address *page_table_mapping,
u32 *data_page, mali_io_address *data_page_mapping);
struct mali_page_directory *mali_mmu_pagedir_alloc(void);
void mali_mmu_pagedir_free(struct mali_page_directory *pagedir);
#endif /* __MALI_MMU_PAGE_DIRECTORY_H__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,162 @@
/*
* Copyright (C) 2010, 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 mali_osk_bitops.h
* Implementation of the OS abstraction layer for the kernel device driver
*/
#ifndef __MALI_OSK_BITOPS_H__
#define __MALI_OSK_BITOPS_H__
#ifdef __cplusplus
extern "C" {
#endif
MALI_STATIC_INLINE void _mali_internal_clear_bit( u32 bit, u32 *addr )
{
MALI_DEBUG_ASSERT( bit < 32 );
MALI_DEBUG_ASSERT( NULL != addr );
(*addr) &= ~(1 << bit);
}
MALI_STATIC_INLINE void _mali_internal_set_bit( u32 bit, u32 *addr )
{
MALI_DEBUG_ASSERT( bit < 32 );
MALI_DEBUG_ASSERT( NULL != addr );
(*addr) |= (1 << bit);
}
MALI_STATIC_INLINE u32 _mali_internal_test_bit( u32 bit, u32 value )
{
MALI_DEBUG_ASSERT( bit < 32 );
return value & (1 << bit);
}
MALI_STATIC_INLINE int _mali_internal_find_first_zero_bit( u32 value )
{
u32 inverted;
u32 negated;
u32 isolated;
u32 leading_zeros;
/* Begin with xxx...x0yyy...y, where ys are 1, number of ys is in range 0..31 */
inverted = ~value; /* zzz...z1000...0 */
/* Using count_trailing_zeros on inverted value -
* See ARM System Developers Guide for details of count_trailing_zeros */
/* Isolate the zero: it is preceeded by a run of 1s, so add 1 to it */
negated = (u32)-inverted ; /* -a == ~a + 1 (mod 2^n) for n-bit numbers */
/* negated = xxx...x1000...0 */
isolated = negated & inverted ; /* xxx...x1000...0 & zzz...z1000...0, zs are ~xs */
/* And so the first zero bit is in the same position as the 1 == number of 1s that preceeded it
* Note that the output is zero if value was all 1s */
leading_zeros = _mali_osk_clz( isolated );
return 31 - leading_zeros;
}
/** @defgroup _mali_osk_bitops OSK Non-atomic Bit-operations
* @{ */
/**
* These bit-operations do not work atomically, and so locks must be used if
* atomicity is required.
*
* Reference implementations for Little Endian are provided, and so it should
* not normally be necessary to re-implement these. Efficient bit-twiddling
* techniques are used where possible, implemented in portable C.
*
* Note that these reference implementations rely on _mali_osk_clz() being
* implemented.
*/
/** @brief Clear a bit in a sequence of 32-bit words
* @param nr bit number to clear, starting from the (Little-endian) least
* significant bit
* @param addr starting point for counting.
*/
MALI_STATIC_INLINE void _mali_osk_clear_nonatomic_bit( u32 nr, u32 *addr )
{
addr += nr >> 5; /* find the correct word */
nr = nr & ((1 << 5)-1); /* The bit number within the word */
_mali_internal_clear_bit( nr, addr );
}
/** @brief Set a bit in a sequence of 32-bit words
* @param nr bit number to set, starting from the (Little-endian) least
* significant bit
* @param addr starting point for counting.
*/
MALI_STATIC_INLINE void _mali_osk_set_nonatomic_bit( u32 nr, u32 *addr )
{
addr += nr >> 5; /* find the correct word */
nr = nr & ((1 << 5)-1); /* The bit number within the word */
_mali_internal_set_bit( nr, addr );
}
/** @brief Test a bit in a sequence of 32-bit words
* @param nr bit number to test, starting from the (Little-endian) least
* significant bit
* @param addr starting point for counting.
* @return zero if bit was clear, non-zero if set. Do not rely on the return
* value being related to the actual word under test.
*/
MALI_STATIC_INLINE u32 _mali_osk_test_bit( u32 nr, u32 *addr )
{
addr += nr >> 5; /* find the correct word */
nr = nr & ((1 << 5)-1); /* The bit number within the word */
return _mali_internal_test_bit( nr, *addr );
}
/* Return maxbit if not found */
/** @brief Find the first zero bit in a sequence of 32-bit words
* @param addr starting point for search.
* @param maxbit the maximum number of bits to search
* @return the number of the first zero bit found, or maxbit if none were found
* in the specified range.
*/
MALI_STATIC_INLINE u32 _mali_osk_find_first_zero_bit( const u32 *addr, u32 maxbit )
{
u32 total;
for ( total = 0; total < maxbit; total += 32, ++addr ) {
int result;
result = _mali_internal_find_first_zero_bit( *addr );
/* non-negative signifies the bit was found */
if ( result >= 0 ) {
total += (u32)result;
break;
}
}
/* Now check if we reached maxbit or above */
if ( total >= maxbit ) {
total = maxbit;
}
return total; /* either the found bit nr, or maxbit if not found */
}
/** @} */ /* end group _mali_osk_bitops */
#ifdef __cplusplus
}
#endif
#endif /* __MALI_OSK_BITOPS_H__ */

View File

@@ -0,0 +1,273 @@
/*
* Copyright (C) 2010-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 mali_osk_list.h
* Implementation of the OS abstraction layer for the kernel device driver
*/
#ifndef __MALI_OSK_LIST_H__
#define __MALI_OSK_LIST_H__
#include "mali_osk.h"
#include "mali_kernel_common.h"
#ifdef __cplusplus
extern "C" {
#endif
MALI_STATIC_INLINE void __mali_osk_list_add(_mali_osk_list_t *new_entry, _mali_osk_list_t *prev, _mali_osk_list_t *next)
{
next->prev = new_entry;
new_entry->next = next;
new_entry->prev = prev;
prev->next = new_entry;
}
MALI_STATIC_INLINE void __mali_osk_list_del(_mali_osk_list_t *prev, _mali_osk_list_t *next)
{
next->prev = prev;
prev->next = next;
}
/** @addtogroup _mali_osk_list OSK Doubly-Linked Circular Lists
* @{ */
/** Reference implementations of Doubly-linked Circular Lists are provided.
* There is often no need to re-implement these.
*
* @note The implementation may differ subtly from any lists the OS provides.
* For this reason, these lists should not be mixed with OS-specific lists
* inside the OSK/UKK implementation. */
/** @brief Initialize a list to be a head of an empty list
* @param exp the list to initialize. */
#define _MALI_OSK_INIT_LIST_HEAD(exp) _mali_osk_list_init(exp)
/** @brief Define a list variable, which is uninitialized.
* @param exp the name of the variable that the list will be defined as. */
#define _MALI_OSK_LIST_HEAD(exp) _mali_osk_list_t exp
/** @brief Define a list variable, which is initialized.
* @param exp the name of the variable that the list will be defined as. */
#define _MALI_OSK_LIST_HEAD_STATIC_INIT(exp) _mali_osk_list_t exp = { &exp, &exp }
/** @brief Initialize a list element.
*
* All list elements must be initialized before use.
*
* Do not use on any list element that is present in a list without using
* _mali_osk_list_del first, otherwise this will break the list.
*
* @param list the list element to initialize
*/
MALI_STATIC_INLINE void _mali_osk_list_init( _mali_osk_list_t *list )
{
list->next = list;
list->prev = list;
}
/** @brief Insert a single list element after an entry in a list
*
* As an example, if this is inserted to the head of a list, then this becomes
* the first element of the list.
*
* Do not use to move list elements from one list to another, as it will break
* the originating list.
*
*
* @param newlist the list element to insert
* @param list the list in which to insert. The new element will be the next
* entry in this list
*/
MALI_STATIC_INLINE void _mali_osk_list_add( _mali_osk_list_t *new_entry, _mali_osk_list_t *list )
{
__mali_osk_list_add(new_entry, list, list->next);
}
/** @brief Insert a single list element before an entry in a list
*
* As an example, if this is inserted to the head of a list, then this becomes
* the last element of the list.
*
* Do not use to move list elements from one list to another, as it will break
* the originating list.
*
* @param newlist the list element to insert
* @param list the list in which to insert. The new element will be the previous
* entry in this list
*/
MALI_STATIC_INLINE void _mali_osk_list_addtail( _mali_osk_list_t *new_entry, _mali_osk_list_t *list )
{
__mali_osk_list_add(new_entry, list->prev, list);
}
/** @brief Remove a single element from a list
*
* The element will no longer be present in the list. The removed list element
* will be uninitialized, and so should not be traversed. It must be
* initialized before further use.
*
* @param list the list element to remove.
*/
MALI_STATIC_INLINE void _mali_osk_list_del( _mali_osk_list_t *list )
{
__mali_osk_list_del(list->prev, list->next);
}
/** @brief Remove a single element from a list, and re-initialize it
*
* The element will no longer be present in the list. The removed list element
* will initialized, and so can be used as normal.
*
* @param list the list element to remove and initialize.
*/
MALI_STATIC_INLINE void _mali_osk_list_delinit( _mali_osk_list_t *list )
{
__mali_osk_list_del(list->prev, list->next);
_mali_osk_list_init(list);
}
/** @brief Determine whether a list is empty.
*
* An empty list is one that contains a single element that points to itself.
*
* @param list the list to check.
* @return non-zero if the list is empty, and zero otherwise.
*/
MALI_STATIC_INLINE mali_bool _mali_osk_list_empty( _mali_osk_list_t *list )
{
return list->next == list;
}
/** @brief Move a list element from one list to another.
*
* The list element must be initialized.
*
* As an example, moving a list item to the head of a new list causes this item
* to be the first element in the new list.
*
* @param move the list element to move
* @param list the new list into which the element will be inserted, as the next
* element in the list.
*/
MALI_STATIC_INLINE void _mali_osk_list_move( _mali_osk_list_t *move_entry, _mali_osk_list_t *list )
{
__mali_osk_list_del(move_entry->prev, move_entry->next);
_mali_osk_list_add(move_entry, list);
}
/** @brief Move an entire list
*
* The list element must be initialized.
*
* Allows you to move a list from one list head to another list head
*
* @param old_list The existing list head
* @param new_list The new list head (must be an empty list)
*/
MALI_STATIC_INLINE void _mali_osk_list_move_list( _mali_osk_list_t *old_list, _mali_osk_list_t *new_list )
{
MALI_DEBUG_ASSERT(_mali_osk_list_empty(new_list));
if (!_mali_osk_list_empty(old_list)) {
new_list->next = old_list->next;
new_list->prev = old_list->prev;
new_list->next->prev = new_list;
new_list->prev->next = new_list;
old_list->next = old_list;
old_list->prev = old_list;
}
}
/** @brief Find the containing structure of a list
*
* When traversing a list, this is used to recover the containing structure,
* given that is contains a _mali_osk_list_t member.
*
* Each list must be of structures of one type, and must link the same members
* together, otherwise it will not be possible to correctly recover the
* sturctures that the lists link.
*
* @note no type or memory checking occurs to ensure that a structure does in
* fact exist for the list entry, and that it is being recovered with respect
* to the correct list member.
*
* @param ptr the pointer to the _mali_osk_list_t member in this structure
* @param type the type of the structure that contains the member
* @param member the member of the structure that ptr points to.
* @return a pointer to a \a type object which contains the _mali_osk_list_t
* \a member, as pointed to by the _mali_osk_list_t \a *ptr.
*/
#define _MALI_OSK_LIST_ENTRY(ptr, type, member) \
_MALI_OSK_CONTAINER_OF(ptr, type, member)
/** @brief Enumerate a list safely
*
* With this macro, lists can be enumerated in a 'safe' manner. That is,
* entries can be deleted from the list without causing an error during
* enumeration. To achieve this, a 'temporary' pointer is required, which must
* be provided to the macro.
*
* Use it like a 'for()', 'while()' or 'do()' construct, and so it must be
* followed by a statement or compound-statement which will be executed for
* each list entry.
*
* Upon loop completion, providing that an early out was not taken in the
* loop body, then it is guaranteed that ptr->member == list, even if the loop
* body never executed.
*
* @param ptr a pointer to an object of type 'type', which points to the
* structure that contains the currently enumerated list entry.
* @param tmp a pointer to an object of type 'type', which must not be used
* inside the list-execution statement.
* @param list a pointer to a _mali_osk_list_t, from which enumeration will
* begin
* @param type the type of the structure that contains the _mali_osk_list_t
* member that is part of the list to be enumerated.
* @param member the _mali_osk_list_t member of the structure that is part of
* the list to be enumerated.
*/
#define _MALI_OSK_LIST_FOREACHENTRY(ptr, tmp, list, type, member) \
for (ptr = _MALI_OSK_LIST_ENTRY((list)->next, type, member), \
tmp = _MALI_OSK_LIST_ENTRY(ptr->member.next, type, member); \
&ptr->member != (list); \
ptr = tmp, \
tmp = _MALI_OSK_LIST_ENTRY(tmp->member.next, type, member))
/** @brief Enumerate a list in reverse order safely
*
* This macro is identical to @ref _MALI_OSK_LIST_FOREACHENTRY, except that
* entries are enumerated in reverse order.
*
* @param ptr a pointer to an object of type 'type', which points to the
* structure that contains the currently enumerated list entry.
* @param tmp a pointer to an object of type 'type', which must not be used
* inside the list-execution statement.
* @param list a pointer to a _mali_osk_list_t, from which enumeration will
* begin
* @param type the type of the structure that contains the _mali_osk_list_t
* member that is part of the list to be enumerated.
* @param member the _mali_osk_list_t member of the structure that is part of
* the list to be enumerated.
*/
#define _MALI_OSK_LIST_FOREACHENTRY_REVERSE(ptr, tmp, list, type, member) \
for (ptr = _MALI_OSK_LIST_ENTRY((list)->prev, type, member), \
tmp = _MALI_OSK_LIST_ENTRY(ptr->member.prev, type, member); \
&ptr->member != (list); \
ptr = tmp, \
tmp = _MALI_OSK_LIST_ENTRY(tmp->member.prev, type, member))
/** @} */ /* end group _mali_osk_list */
#ifdef __cplusplus
}
#endif
#endif /* __MALI_OSK_LIST_H__ */

View File

@@ -0,0 +1,118 @@
/*
* Copyright (C) 2010-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 mali_osk_mali.h
* Defines the OS abstraction layer which is specific for the Mali kernel device driver (OSK)
*/
#ifndef __MALI_OSK_MALI_H__
#define __MALI_OSK_MALI_H__
#include <linux/mali/mali_utgard.h>
#include <mali_osk.h>
#ifdef __cplusplus
extern "C" {
#endif
/** @addtogroup _mali_osk_miscellaneous
* @{ */
/** @brief Struct with device specific configuration data
*/
struct _mali_osk_device_data {
/* Dedicated GPU memory range (physical). */
u32 dedicated_mem_start;
u32 dedicated_mem_size;
/* Shared GPU memory */
u32 shared_mem_size;
/* Frame buffer memory to be accessible by Mali GPU (physical) */
u32 fb_start;
u32 fb_size;
/* Max runtime [ms] for jobs */
int max_job_runtime;
/* Report GPU utilization in this interval (specified in ms) */
u32 utilization_interval;
/* Function that will receive periodic GPU utilization numbers */
void (*utilization_callback)(struct mali_gpu_utilization_data *data);
/*
* Mali PMU switch delay.
* Only needed if the power gates are connected to the PMU in a high fanout
* network. This value is the number of Mali clock cycles it takes to
* enable the power gates and turn on the power mesh.
* This value will have no effect if a daisy chain implementation is used.
*/
u32 pmu_switch_delay;
/* Mali Dynamic power domain configuration in sequence from 0-11
* GP PP0 PP1 PP2 PP3 PP4 PP5 PP6 PP7, L2$0 L2$1 L2$2
*/
u16 pmu_domain_config[12];
/* Fuction that platform callback for freq tunning, needed when MALI400_POWER_PERFORMANCE_POLICY enabled */
int (*set_freq_callback)(unsigned int mhz);
};
/** @brief Find Mali GPU HW resource
*
* @param addr Address of Mali GPU resource to find
* @param res Storage for resource information if resource is found.
* @return _MALI_OSK_ERR_OK on success, _MALI_OSK_ERR_ITEM_NOT_FOUND if resource is not found
*/
_mali_osk_errcode_t _mali_osk_resource_find(u32 addr, _mali_osk_resource_t *res);
/** @brief Find Mali GPU HW base address
*
* @return 0 if resources are found, otherwise the Mali GPU component with lowest address.
*/
u32 _mali_osk_resource_base_address(void);
/** @brief Retrieve the Mali GPU specific data
*
* @return _MALI_OSK_ERR_OK on success, otherwise failure.
*/
_mali_osk_errcode_t _mali_osk_device_data_get(struct _mali_osk_device_data *data);
/** @brief Determines if Mali GPU has been configured with shared interrupts.
*
* @return MALI_TRUE if shared interrupts, MALI_FALSE if not.
*/
mali_bool _mali_osk_shared_interrupts(void);
/** @} */ /* end group _mali_osk_miscellaneous */
/** @addtogroup _mali_osk_low_level_memory
* @{ */
/** @brief Copy as much data as possible from src to dest, do not crash if src or dest isn't available.
*
* @param dest Destination buffer (limited to user space mapped Mali memory)
* @param src Source buffer
* @param size Number of bytes to copy
* @return Number of bytes actually copied
*/
u32 _mali_osk_mem_write_safe(void *dest, const void *src, u32 size);
/** @} */ /* end group _mali_osk_low_level_memory */
#ifdef __cplusplus
}
#endif
#endif /* __MALI_OSK_MALI_H__ */

View File

@@ -0,0 +1,141 @@
/*
* Copyright (C) 2010-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.
*/
#ifndef __MALI_OSK_PROFILING_H__
#define __MALI_OSK_PROFILING_H__
#if defined(CONFIG_MALI400_PROFILING) && defined (CONFIG_TRACEPOINTS)
#include "mali_linux_trace.h"
#include "mali_profiling_events.h"
#include "mali_profiling_gator_api.h"
#define MALI_PROFILING_MAX_BUFFER_ENTRIES 1048576
#define MALI_PROFILING_NO_HW_COUNTER = ((u32)-1)
/** @defgroup _mali_osk_profiling External profiling connectivity
* @{ */
/**
* Initialize the profiling module.
* @return _MALI_OSK_ERR_OK on success, otherwise failure.
*/
_mali_osk_errcode_t _mali_osk_profiling_init(mali_bool auto_start);
/*
* Terminate the profiling module.
*/
void _mali_osk_profiling_term(void);
/**
* Start recording profiling data
*
* The specified limit will determine how large the capture buffer is.
* MALI_PROFILING_MAX_BUFFER_ENTRIES determines the maximum size allowed by the device driver.
*
* @param limit The desired maximum number of events to record on input, the actual maximum on output.
* @return _MALI_OSK_ERR_OK on success, otherwise failure.
*/
_mali_osk_errcode_t _mali_osk_profiling_start(u32 * limit);
/**
* Add an profiling event
*
* @param event_id The event identificator.
* @param data0 First data parameter, depending on event_id specified.
* @param data1 Second data parameter, depending on event_id specified.
* @param data2 Third data parameter, depending on event_id specified.
* @param data3 Fourth data parameter, depending on event_id specified.
* @param data4 Fifth data parameter, depending on event_id specified.
* @return _MALI_OSK_ERR_OK on success, otherwise failure.
*/
/* Call Linux tracepoint directly */
#define _mali_osk_profiling_add_event(event_id, data0, data1, data2, data3, data4) trace_mali_timeline_event((event_id), (data0), (data1), (data2), (data3), (data4))
/**
* Report a hardware counter event.
*
* @param counter_id The ID of the counter.
* @param value The value of the counter.
*/
/* Call Linux tracepoint directly */
#define _mali_osk_profiling_report_hw_counter(counter_id, value) trace_mali_hw_counter(counter_id, value)
/**
* Report SW counters
*
* @param counters array of counter values
*/
void _mali_osk_profiling_report_sw_counters(u32 *counters);
/**
* Stop recording profiling data
*
* @param count Returns the number of recorded events.
* @return _MALI_OSK_ERR_OK on success, otherwise failure.
*/
_mali_osk_errcode_t _mali_osk_profiling_stop(u32 * count);
/**
* Retrieves the number of events that can be retrieved
*
* @return The number of recorded events that can be retrieved.
*/
u32 _mali_osk_profiling_get_count(void);
/**
* Retrieve an event
*
* @param index Event index (start with 0 and continue until this function fails to retrieve all events)
* @param timestamp The timestamp for the retrieved event will be stored here.
* @param event_id The event ID for the retrieved event will be stored here.
* @param data The 5 data values for the retrieved event will be stored here.
* @return _MALI_OSK_ERR_OK on success, otherwise failure.
*/
_mali_osk_errcode_t _mali_osk_profiling_get_event(u32 index, u64* timestamp, u32* event_id, u32 data[5]);
/**
* Clear the recorded buffer.
*
* This is needed in order to start another recording.
*
* @return _MALI_OSK_ERR_OK on success, otherwise failure.
*/
_mali_osk_errcode_t _mali_osk_profiling_clear(void);
/**
* Checks if a recording of profiling data is in progress
*
* @return MALI_TRUE if recording of profiling data is in progress, MALI_FALSE if not
*/
mali_bool _mali_osk_profiling_is_recording(void);
/**
* Checks if profiling data is available for retrival
*
* @return MALI_TRUE if profiling data is avaiable, MALI_FALSE if not
*/
mali_bool _mali_osk_profiling_have_recording(void);
/** @} */ /* end group _mali_osk_profiling */
#else /* defined(CONFIG_MALI400_PROFILING) && defined(CONFIG_TRACEPOINTS) */
/* Dummy add_event, for when profiling is disabled. */
#define _mali_osk_profiling_add_event(event_id, data0, data1, data2, data3, data4)
#endif /* defined(CONFIG_MALI400_PROFILING) && defined(CONFIG_TRACEPOINTS) */
#endif /* __MALI_OSK_PROFILING_H__ */

View File

@@ -0,0 +1,455 @@
/*
* Copyright (C) 2010-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 mali_osk_types.h
* Defines types of the OS abstraction layer for the kernel device driver (OSK)
*/
#ifndef __MALI_OSK_TYPES_H__
#define __MALI_OSK_TYPES_H__
#ifdef __cplusplus
extern "C" {
#endif
/**
* @addtogroup uddapi Unified Device Driver (UDD) APIs
*
* @{
*/
/**
* @addtogroup oskapi UDD OS Abstraction for Kernel-side (OSK) APIs
*
* @{
*/
/** @defgroup _mali_osk_miscellaneous OSK Miscellaneous functions, constants and types
* @{ */
/* Define integer types used by OSK. Note: these currently clash with Linux so we only define them if not defined already */
#ifndef __KERNEL__
typedef unsigned char u8;
typedef signed char s8;
typedef unsigned short u16;
typedef signed short s16;
typedef unsigned int u32;
typedef signed int s32;
typedef unsigned long long u64;
#define BITS_PER_LONG (sizeof(long)*8)
#else
/* Ensure Linux types u32, etc. are defined */
#include <linux/types.h>
#endif
/** @brief Mali Boolean type which uses MALI_TRUE and MALI_FALSE
*/
typedef unsigned long mali_bool;
#ifndef MALI_TRUE
#define MALI_TRUE ((mali_bool)1)
#endif
#ifndef MALI_FALSE
#define MALI_FALSE ((mali_bool)0)
#endif
#define MALI_HW_CORE_NO_COUNTER ((u32)-1)
/**
* @brief OSK Error codes
*
* Each OS may use its own set of error codes, and may require that the
* User/Kernel interface take certain error code. This means that the common
* error codes need to be sufficiently rich to pass the correct error code
* thorugh from the OSK to U/K layer, across all OSs.
*
* The result is that some error codes will appear redundant on some OSs.
* Under all OSs, the OSK layer must translate native OS error codes to
* _mali_osk_errcode_t codes. Similarly, the U/K layer must translate from
* _mali_osk_errcode_t codes to native OS error codes.
*/
typedef enum {
_MALI_OSK_ERR_OK = 0, /**< Success. */
_MALI_OSK_ERR_FAULT = -1, /**< General non-success */
_MALI_OSK_ERR_INVALID_FUNC = -2, /**< Invalid function requested through User/Kernel interface (e.g. bad IOCTL number) */
_MALI_OSK_ERR_INVALID_ARGS = -3, /**< Invalid arguments passed through User/Kernel interface */
_MALI_OSK_ERR_NOMEM = -4, /**< Insufficient memory */
_MALI_OSK_ERR_TIMEOUT = -5, /**< Timeout occurred */
_MALI_OSK_ERR_RESTARTSYSCALL = -6, /**< Special: On certain OSs, must report when an interruptable mutex is interrupted. Ignore otherwise. */
_MALI_OSK_ERR_ITEM_NOT_FOUND = -7, /**< Table Lookup failed */
_MALI_OSK_ERR_BUSY = -8, /**< Device/operation is busy. Try again later */
_MALI_OSK_ERR_UNSUPPORTED = -9, /**< Optional part of the interface used, and is unsupported */
} _mali_osk_errcode_t;
/** @} */ /* end group _mali_osk_miscellaneous */
/** @defgroup _mali_osk_wq OSK work queues
* @{ */
/** @brief Private type for work objects */
typedef struct _mali_osk_wq_work_s _mali_osk_wq_work_t;
typedef struct _mali_osk_wq_delayed_work_s _mali_osk_wq_delayed_work_t;
/** @brief Work queue handler function
*
* This function type is called when the work is scheduled by the work queue,
* e.g. as an IRQ bottom-half handler.
*
* Refer to \ref _mali_osk_wq_schedule_work() for more information on the
* work-queue and work handlers.
*
* @param arg resource-specific data
*/
typedef void (*_mali_osk_wq_work_handler_t)( void * arg );
/* @} */ /* end group _mali_osk_wq */
/** @defgroup _mali_osk_irq OSK IRQ handling
* @{ */
/** @brief Private type for IRQ handling objects */
typedef struct _mali_osk_irq_t_struct _mali_osk_irq_t;
/** @brief Optional function to trigger an irq from a resource
*
* This function is implemented by the common layer to allow probing of a resource's IRQ.
* @param arg resource-specific data */
typedef void (*_mali_osk_irq_trigger_t)( void * arg );
/** @brief Optional function to acknowledge an irq from a resource
*
* This function is implemented by the common layer to allow probing of a resource's IRQ.
* @param arg resource-specific data
* @return _MALI_OSK_ERR_OK if the IRQ was successful, or a suitable _mali_osk_errcode_t on failure. */
typedef _mali_osk_errcode_t (*_mali_osk_irq_ack_t)( void * arg );
/** @brief IRQ 'upper-half' handler callback.
*
* This function is implemented by the common layer to do the initial handling of a
* resource's IRQ. This maps on to the concept of an ISR that does the minimum
* work necessary before handing off to an IST.
*
* The communication of the resource-specific data from the ISR to the IST is
* handled by the OSK implementation.
*
* On most systems, the IRQ upper-half handler executes in IRQ context.
* Therefore, the system may have restrictions about what can be done in this
* context
*
* If an IRQ upper-half handler requires more work to be done than can be
* acheived in an IRQ context, then it may defer the work with
* _mali_osk_wq_schedule_work(). Refer to \ref _mali_osk_wq_create_work() for
* more information.
*
* @param arg resource-specific data
* @return _MALI_OSK_ERR_OK if the IRQ was correctly handled, or a suitable
* _mali_osk_errcode_t otherwise.
*/
typedef _mali_osk_errcode_t (*_mali_osk_irq_uhandler_t)( void * arg );
/** @} */ /* end group _mali_osk_irq */
/** @defgroup _mali_osk_atomic OSK Atomic counters
* @{ */
/** @brief Public type of atomic counters
*
* This is public for allocation on stack. On systems that support it, this is just a single 32-bit value.
* On others, it could be encapsulating an object stored elsewhere.
*
* Regardless of implementation, the \ref _mali_osk_atomic functions \b must be used
* for all accesses to the variable's value, even if atomicity is not required.
* Do not access u.val or u.obj directly.
*/
typedef struct {
union {
u32 val;
void *obj;
} u;
} _mali_osk_atomic_t;
/** @} */ /* end group _mali_osk_atomic */
/** @defgroup _mali_osk_lock OSK Mutual Exclusion Locks
* @{ */
/** @brief OSK Mutual Exclusion Lock ordered list
*
* This lists the various types of locks in the system and is used to check
* that locks are taken in the correct order.
*
* - Holding more than one lock of the same order at the same time is not
* allowed.
* - Taking a lock of a lower order than the highest-order lock currently held
* is not allowed.
*
*/
typedef enum {
/* || Locks || */
/* || must be || */
/* _||_ taken in _||_ */
/* \ / this \ / */
/* \/ order! \/ */
_MALI_OSK_LOCK_ORDER_FIRST = 0,
_MALI_OSK_LOCK_ORDER_SESSIONS,
_MALI_OSK_LOCK_ORDER_MEM_SESSION,
_MALI_OSK_LOCK_ORDER_MEM_INFO,
_MALI_OSK_LOCK_ORDER_MEM_PT_CACHE,
_MALI_OSK_LOCK_ORDER_DESCRIPTOR_MAP,
_MALI_OSK_LOCK_ORDER_GROUP_VIRTUAL,
_MALI_OSK_LOCK_ORDER_GROUP,
_MALI_OSK_LOCK_ORDER_TIMELINE_SYSTEM,
_MALI_OSK_LOCK_ORDER_SCHEDULER,
_MALI_OSK_LOCK_ORDER_SCHEDULER_DEFERRED,
_MALI_OSK_LOCK_ORDER_PM_CORE_STATE,
_MALI_OSK_LOCK_ORDER_L2_COMMAND,
_MALI_OSK_LOCK_ORDER_DMA_COMMAND,
_MALI_OSK_LOCK_ORDER_PROFILING,
_MALI_OSK_LOCK_ORDER_L2_COUNTER,
_MALI_OSK_LOCK_ORDER_UTILIZATION,
_MALI_OSK_LOCK_ORDER_PM_EXECUTE,
_MALI_OSK_LOCK_ORDER_SESSION_PENDING_JOBS,
_MALI_OSK_LOCK_ORDER_PM_DOMAIN,
_MALI_OSK_LOCK_ORDER_PMU,
_MALI_OSK_LOCK_ORDER_LAST,
} _mali_osk_lock_order_t;
/** @brief OSK Mutual Exclusion Lock flags type
*
* - Any lock can use the order parameter.
*/
typedef enum {
_MALI_OSK_LOCKFLAG_UNORDERED = 0x1, /**< Indicate that the order of this lock should not be checked */
_MALI_OSK_LOCKFLAG_ORDERED = 0x2,
/** @enum _mali_osk_lock_flags_t
*
* Flags from 0x10000--0x80000000 are RESERVED for User-mode */
} _mali_osk_lock_flags_t;
/** @brief Mutual Exclusion Lock Mode Optimization hint
*
* The lock mode is used to implement the read/write locking of locks when we call
* functions _mali_osk_mutex_rw_init/wait/signal/term/. In this case, the RO mode can
* be used to allow multiple concurrent readers, but no writers. The RW mode is used for
* writers, and so will wait for all readers to release the lock (if any present).
* Further readers and writers will wait until the writer releases the lock.
*
* The mode is purely an optimization hint: for example, it is permissible for
* all locks to behave in RW mode, regardless of that supplied.
*
* It is an error to attempt to use locks in anything other that RW mode when
* call functions _mali_osk_mutex_rw_wait/signal().
*
*/
typedef enum {
_MALI_OSK_LOCKMODE_UNDEF = -1, /**< Undefined lock mode. For internal use only */
_MALI_OSK_LOCKMODE_RW = 0x0, /**< Read-write mode, default. All readers and writers are mutually-exclusive */
_MALI_OSK_LOCKMODE_RO, /**< Read-only mode, to support multiple concurrent readers, but mutual exclusion in the presence of writers. */
/** @enum _mali_osk_lock_mode_t
*
* Lock modes 0x40--0x7F are RESERVED for User-mode */
} _mali_osk_lock_mode_t;
/** @brief Private types for Mutual Exclusion lock objects */
typedef struct _mali_osk_lock_debug_s _mali_osk_lock_debug_t;
typedef struct _mali_osk_spinlock_s _mali_osk_spinlock_t;
typedef struct _mali_osk_spinlock_irq_s _mali_osk_spinlock_irq_t;
typedef struct _mali_osk_mutex_s _mali_osk_mutex_t;
typedef struct _mali_osk_mutex_rw_s _mali_osk_mutex_rw_t;
/** @} */ /* end group _mali_osk_lock */
/** @defgroup _mali_osk_low_level_memory OSK Low-level Memory Operations
* @{ */
/**
* @brief Private data type for use in IO accesses to/from devices.
*
* This represents some range that is accessible from the device. Examples
* include:
* - Device Registers, which could be readable and/or writeable.
* - Memory that the device has access to, for storing configuration structures.
*
* Access to this range must be made through the _mali_osk_mem_ioread32() and
* _mali_osk_mem_iowrite32() functions.
*/
typedef struct _mali_io_address * mali_io_address;
/** @defgroup _MALI_OSK_CPU_PAGE CPU Physical page size macros.
*
* The order of the page size is supplied for
* ease of use by algorithms that might require it, since it is easier to know
* it ahead of time rather than calculating it.
*
* The Mali Page Mask macro masks off the lower bits of a physical address to
* give the start address of the page for that physical address.
*
* @note The Mali device driver code is designed for systems with 4KB page size.
* Changing these macros will not make the entire Mali device driver work with
* page sizes other than 4KB.
*
* @note The CPU Physical Page Size has been assumed to be the same as the Mali
* Physical Page Size.
*
* @{
*/
/** CPU Page Order, as log to base 2 of the Page size. @see _MALI_OSK_CPU_PAGE_SIZE */
#define _MALI_OSK_CPU_PAGE_ORDER ((u32)12)
/** CPU Page Size, in bytes. */
#define _MALI_OSK_CPU_PAGE_SIZE (((u32)1) << (_MALI_OSK_CPU_PAGE_ORDER))
/** CPU Page Mask, which masks off the offset within a page */
#define _MALI_OSK_CPU_PAGE_MASK (~((((u32)1) << (_MALI_OSK_CPU_PAGE_ORDER)) - ((u32)1)))
/** @} */ /* end of group _MALI_OSK_CPU_PAGE */
/** @defgroup _MALI_OSK_MALI_PAGE Mali Physical Page size macros
*
* Mali Physical page size macros. The order of the page size is supplied for
* ease of use by algorithms that might require it, since it is easier to know
* it ahead of time rather than calculating it.
*
* The Mali Page Mask macro masks off the lower bits of a physical address to
* give the start address of the page for that physical address.
*
* @note The Mali device driver code is designed for systems with 4KB page size.
* Changing these macros will not make the entire Mali device driver work with
* page sizes other than 4KB.
*
* @note The Mali Physical Page Size has been assumed to be the same as the CPU
* Physical Page Size.
*
* @{
*/
/** Mali Page Order, as log to base 2 of the Page size. @see _MALI_OSK_MALI_PAGE_SIZE */
#define _MALI_OSK_MALI_PAGE_ORDER ((u32)12)
/** Mali Page Size, in bytes. */
#define _MALI_OSK_MALI_PAGE_SIZE (((u32)1) << (_MALI_OSK_MALI_PAGE_ORDER))
/** Mali Page Mask, which masks off the offset within a page */
#define _MALI_OSK_MALI_PAGE_MASK (~((((u32)1) << (_MALI_OSK_MALI_PAGE_ORDER)) - ((u32)1)))
/** @} */ /* end of group _MALI_OSK_MALI_PAGE*/
/** @brief flags for mapping a user-accessible memory range
*
* Where a function with prefix '_mali_osk_mem_mapregion' accepts flags as one
* of the function parameters, it will use one of these. These allow per-page
* control over mappings. Compare with the mali_memory_allocation_flag type,
* which acts over an entire range
*
* These may be OR'd together with bitwise OR (|), but must be cast back into
* the type after OR'ing.
*/
typedef enum {
_MALI_OSK_MEM_MAPREGION_FLAG_OS_ALLOCATED_PHYSADDR = 0x1, /**< Physical address is OS Allocated */
} _mali_osk_mem_mapregion_flags_t;
/** @} */ /* end group _mali_osk_low_level_memory */
/** @defgroup _mali_osk_notification OSK Notification Queues
* @{ */
/** @brief Private type for notification queue objects */
typedef struct _mali_osk_notification_queue_t_struct _mali_osk_notification_queue_t;
/** @brief Public notification data object type */
typedef struct _mali_osk_notification_t_struct {
u32 notification_type; /**< The notification type */
u32 result_buffer_size; /**< Size of the result buffer to copy to user space */
void * result_buffer; /**< Buffer containing any type specific data */
} _mali_osk_notification_t;
/** @} */ /* end group _mali_osk_notification */
/** @defgroup _mali_osk_timer OSK Timer Callbacks
* @{ */
/** @brief Function to call when a timer expires
*
* When a timer expires, this function is called. Note that on many systems,
* a timer callback will be executed in IRQ context. Therefore, restrictions
* may apply on what can be done inside the timer callback.
*
* If a timer requires more work to be done than can be acheived in an IRQ
* context, then it may defer the work with a work-queue. For example, it may
* use \ref _mali_osk_wq_schedule_work() to make use of a bottom-half handler
* to carry out the remaining work.
*
* Stopping the timer with \ref _mali_osk_timer_del() blocks on compeletion of
* the callback. Therefore, the callback may not obtain any mutexes also held
* by any callers of _mali_osk_timer_del(). Otherwise, a deadlock may occur.
*
* @param arg Function-specific data */
typedef void (*_mali_osk_timer_callback_t)(void * arg);
/** @brief Private type for Timer Callback Objects */
typedef struct _mali_osk_timer_t_struct _mali_osk_timer_t;
/** @} */ /* end group _mali_osk_timer */
/** @addtogroup _mali_osk_list OSK Doubly-Linked Circular Lists
* @{ */
/** @brief Public List objects.
*
* To use, add a _mali_osk_list_t member to the structure that may become part
* of a list. When traversing the _mali_osk_list_t objects, use the
* _MALI_OSK_CONTAINER_OF() macro to recover the structure from its
*_mali_osk_list_t member
*
* Each structure may have multiple _mali_osk_list_t members, so that the
* structure is part of multiple lists. When traversing lists, ensure that the
* correct _mali_osk_list_t member is used, because type-checking will be
* lost by the compiler.
*/
typedef struct _mali_osk_list_s {
struct _mali_osk_list_s *next;
struct _mali_osk_list_s *prev;
} _mali_osk_list_t;
/** @} */ /* end group _mali_osk_list */
/** @addtogroup _mali_osk_miscellaneous
* @{ */
/** @brief resource description struct
*
* Platform independent representation of a Mali HW resource
*/
typedef struct _mali_osk_resource {
const char * description; /**< short description of the resource */
u32 base; /**< Physical base address of the resource, as seen by Mali resources. */
u32 irq; /**< IRQ number delivered to the CPU, or -1 to tell the driver to probe for it (if possible) */
} _mali_osk_resource_t;
/** @} */ /* end group _mali_osk_miscellaneous */
/** @defgroup _mali_osk_wait_queue OSK Wait Queue functionality
* @{ */
/** @brief Private type for wait queue objects */
typedef struct _mali_osk_wait_queue_t_struct _mali_osk_wait_queue_t;
/** @} */ /* end group _mali_osk_wait_queue */
/** @} */ /* end group osuapi */
/** @} */ /* end group uddapi */
#ifdef __cplusplus
}
#endif
#endif /* __MALI_OSK_TYPES_H__ */

View File

@@ -0,0 +1,122 @@
/*
* Copyright (C) 2011-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.
*/
#include "mali_pm.h"
#include "mali_kernel_common.h"
#include "mali_osk.h"
#include "mali_gp_scheduler.h"
#include "mali_pp_scheduler.h"
#include "mali_scheduler.h"
#include "mali_kernel_utilization.h"
#include "mali_group.h"
#include "mali_pm_domain.h"
#include "mali_pmu.h"
static mali_bool mali_power_on = MALI_FALSE;
_mali_osk_errcode_t mali_pm_initialize(void)
{
_mali_osk_pm_dev_enable();
return _MALI_OSK_ERR_OK;
}
void mali_pm_terminate(void)
{
mali_pm_domain_terminate();
_mali_osk_pm_dev_disable();
}
/* Reset GPU after power up */
static void mali_pm_reset_gpu(void)
{
/* Reset all L2 caches */
mali_l2_cache_reset_all();
/* Reset all groups */
mali_scheduler_reset_all_groups();
}
void mali_pm_os_suspend(void)
{
MALI_DEBUG_PRINT(3, ("Mali PM: OS suspend\n"));
mali_gp_scheduler_suspend();
mali_pp_scheduler_suspend();
mali_utilization_suspend();
mali_group_power_off(MALI_TRUE);
mali_power_on = MALI_FALSE;
}
void mali_pm_os_resume(void)
{
struct mali_pmu_core *pmu = mali_pmu_get_global_pmu_core();
mali_bool do_reset = MALI_FALSE;
MALI_DEBUG_PRINT(3, ("Mali PM: OS resume\n"));
if (MALI_TRUE != mali_power_on) {
do_reset = MALI_TRUE;
}
if (NULL != pmu) {
mali_pmu_reset(pmu);
}
mali_power_on = MALI_TRUE;
_mali_osk_write_mem_barrier();
if (do_reset) {
mali_pm_reset_gpu();
mali_group_power_on();
}
mali_gp_scheduler_resume();
mali_pp_scheduler_resume();
}
void mali_pm_runtime_suspend(void)
{
MALI_DEBUG_PRINT(3, ("Mali PM: Runtime suspend\n"));
mali_group_power_off(MALI_TRUE);
mali_power_on = MALI_FALSE;
}
void mali_pm_runtime_resume(void)
{
struct mali_pmu_core *pmu = mali_pmu_get_global_pmu_core();
mali_bool do_reset = MALI_FALSE;
MALI_DEBUG_PRINT(3, ("Mali PM: Runtime resume\n"));
if (MALI_TRUE != mali_power_on) {
do_reset = MALI_TRUE;
}
if (NULL != pmu) {
mali_pmu_reset(pmu);
}
mali_power_on = MALI_TRUE;
_mali_osk_write_mem_barrier();
if (do_reset) {
mali_pm_reset_gpu();
mali_group_power_on();
}
}
void mali_pm_set_power_is_on(void)
{
mali_power_on = MALI_TRUE;
}
mali_bool mali_pm_is_power_on(void)
{
return mali_power_on;
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2011-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.
*/
#ifndef __MALI_PM_H__
#define __MALI_PM_H__
#include "mali_osk.h"
_mali_osk_errcode_t mali_pm_initialize(void);
void mali_pm_terminate(void);
/* Callback functions registered for the runtime PMM system */
void mali_pm_os_suspend(void);
void mali_pm_os_resume(void);
void mali_pm_runtime_suspend(void);
void mali_pm_runtime_resume(void);
void mali_pm_set_power_is_on(void);
mali_bool mali_pm_is_power_on(void);
#endif /* __MALI_PM_H__ */

View File

@@ -0,0 +1,241 @@
/*
* Copyright (C) 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.
*/
#include "mali_kernel_common.h"
#include "mali_osk.h"
#include "mali_pm_domain.h"
#include "mali_pmu.h"
#include "mali_group.h"
static struct mali_pm_domain *mali_pm_domains[MALI_MAX_NUMBER_OF_DOMAINS] = { NULL, };
static void mali_pm_domain_lock(struct mali_pm_domain *domain)
{
_mali_osk_spinlock_irq_lock(domain->lock);
}
static void mali_pm_domain_unlock(struct mali_pm_domain *domain)
{
_mali_osk_spinlock_irq_unlock(domain->lock);
}
MALI_STATIC_INLINE void mali_pm_domain_state_set(struct mali_pm_domain *domain, mali_pm_domain_state state)
{
domain->state = state;
}
struct mali_pm_domain *mali_pm_domain_create(u32 pmu_mask)
{
struct mali_pm_domain* domain = NULL;
u32 domain_id = 0;
domain = mali_pm_domain_get_from_mask(pmu_mask);
if (NULL != domain) return domain;
MALI_DEBUG_PRINT(2, ("Mali PM domain: Creating Mali PM domain (mask=0x%08X)\n", pmu_mask));
domain = (struct mali_pm_domain *)_mali_osk_malloc(sizeof(struct mali_pm_domain));
if (NULL != domain) {
domain->lock = _mali_osk_spinlock_irq_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_PM_DOMAIN);
if (NULL == domain->lock) {
_mali_osk_free(domain);
return NULL;
}
domain->state = MALI_PM_DOMAIN_ON;
domain->pmu_mask = pmu_mask;
domain->use_count = 0;
domain->group_list = NULL;
domain->group_count = 0;
domain->l2 = NULL;
domain_id = _mali_osk_fls(pmu_mask) - 1;
/* Verify the domain_id */
MALI_DEBUG_ASSERT(MALI_MAX_NUMBER_OF_DOMAINS > domain_id);
/* Verify that pmu_mask only one bit is set */
MALI_DEBUG_ASSERT((1 << domain_id) == pmu_mask);
mali_pm_domains[domain_id] = domain;
return domain;
} else {
MALI_DEBUG_PRINT_ERROR(("Unable to create PM domain\n"));
}
return NULL;
}
void mali_pm_domain_delete(struct mali_pm_domain *domain)
{
if (NULL == domain) {
return;
}
_mali_osk_spinlock_irq_term(domain->lock);
_mali_osk_free(domain);
}
void mali_pm_domain_terminate(void)
{
int i;
/* Delete all domains */
for (i = 0; i < MALI_MAX_NUMBER_OF_DOMAINS; i++) {
mali_pm_domain_delete(mali_pm_domains[i]);
}
}
void mali_pm_domain_add_group(u32 mask, struct mali_group *group)
{
struct mali_pm_domain *domain = mali_pm_domain_get_from_mask(mask);
struct mali_group *next;
if (NULL == domain) return;
MALI_DEBUG_ASSERT_POINTER(group);
++domain->group_count;
next = domain->group_list;
domain->group_list = group;
group->pm_domain_list = next;
mali_group_set_pm_domain(group, domain);
/* Get pm domain ref after mali_group_set_pm_domain */
mali_group_get_pm_domain_ref(group);
}
void mali_pm_domain_add_l2(u32 mask, struct mali_l2_cache_core *l2)
{
struct mali_pm_domain *domain = mali_pm_domain_get_from_mask(mask);
if (NULL == domain) return;
MALI_DEBUG_ASSERT(NULL == domain->l2);
MALI_DEBUG_ASSERT(NULL != l2);
domain->l2 = l2;
mali_l2_cache_set_pm_domain(l2, domain);
}
struct mali_pm_domain *mali_pm_domain_get_from_mask(u32 mask)
{
u32 id = 0;
if (0 == mask) return NULL;
id = _mali_osk_fls(mask)-1;
MALI_DEBUG_ASSERT(MALI_MAX_NUMBER_OF_DOMAINS > id);
/* Verify that pmu_mask only one bit is set */
MALI_DEBUG_ASSERT((1 << id) == mask);
return mali_pm_domains[id];
}
struct mali_pm_domain *mali_pm_domain_get_from_index(u32 id)
{
MALI_DEBUG_ASSERT(MALI_MAX_NUMBER_OF_DOMAINS > id);
return mali_pm_domains[id];
}
void mali_pm_domain_ref_get(struct mali_pm_domain *domain)
{
if (NULL == domain) return;
mali_pm_domain_lock(domain);
++domain->use_count;
if (MALI_PM_DOMAIN_ON != domain->state) {
/* Power on */
struct mali_pmu_core *pmu = mali_pmu_get_global_pmu_core();
MALI_DEBUG_PRINT(3, ("PM Domain: Powering on 0x%08x\n", domain->pmu_mask));
if (NULL != pmu) {
_mali_osk_errcode_t err;
err = mali_pmu_power_up(pmu, domain->pmu_mask);
if (_MALI_OSK_ERR_OK != err && _MALI_OSK_ERR_BUSY != err) {
MALI_PRINT_ERROR(("PM Domain: Failed to power up PM domain 0x%08x\n",
domain->pmu_mask));
}
}
mali_pm_domain_state_set(domain, MALI_PM_DOMAIN_ON);
} else {
MALI_DEBUG_ASSERT(MALI_PM_DOMAIN_ON == mali_pm_domain_state_get(domain));
}
mali_pm_domain_unlock(domain);
}
void mali_pm_domain_ref_put(struct mali_pm_domain *domain)
{
if (NULL == domain) return;
mali_pm_domain_lock(domain);
--domain->use_count;
if (0 == domain->use_count && MALI_PM_DOMAIN_OFF != domain->state) {
/* Power off */
struct mali_pmu_core *pmu = mali_pmu_get_global_pmu_core();
MALI_DEBUG_PRINT(3, ("PM Domain: Powering off 0x%08x\n", domain->pmu_mask));
mali_pm_domain_state_set(domain, MALI_PM_DOMAIN_OFF);
if (NULL != pmu) {
_mali_osk_errcode_t err;
err = mali_pmu_power_down(pmu, domain->pmu_mask);
if (_MALI_OSK_ERR_OK != err && _MALI_OSK_ERR_BUSY != err) {
MALI_PRINT_ERROR(("PM Domain: Failed to power down PM domain 0x%08x\n",
domain->pmu_mask));
}
}
}
mali_pm_domain_unlock(domain);
}
mali_bool mali_pm_domain_lock_state(struct mali_pm_domain *domain)
{
mali_bool is_powered = MALI_TRUE;
/* Take a reference without powering on */
if (NULL != domain) {
mali_pm_domain_lock(domain);
++domain->use_count;
if (MALI_PM_DOMAIN_ON != domain->state) {
is_powered = MALI_FALSE;
}
mali_pm_domain_unlock(domain);
}
if(!_mali_osk_pm_dev_ref_add_no_power_on()) {
is_powered = MALI_FALSE;
}
return is_powered;
}
void mali_pm_domain_unlock_state(struct mali_pm_domain *domain)
{
_mali_osk_pm_dev_ref_dec_no_power_on();
if (NULL != domain) {
mali_pm_domain_ref_put(domain);
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 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.
*/
#ifndef __MALI_PM_DOMAIN_H__
#define __MALI_PM_DOMAIN_H__
#include "mali_kernel_common.h"
#include "mali_osk.h"
#include "mali_l2_cache.h"
#include "mali_group.h"
#include "mali_pmu.h"
typedef enum {
MALI_PM_DOMAIN_ON,
MALI_PM_DOMAIN_OFF,
} mali_pm_domain_state;
struct mali_pm_domain {
mali_pm_domain_state state;
_mali_osk_spinlock_irq_t *lock;
s32 use_count;
u32 pmu_mask;
int group_count;
struct mali_group *group_list;
struct mali_l2_cache_core *l2;
};
struct mali_pm_domain *mali_pm_domain_create(u32 pmu_mask);
void mali_pm_domain_add_group(u32 mask, struct mali_group *group);
void mali_pm_domain_add_l2(u32 mask, struct mali_l2_cache_core *l2);
void mali_pm_domain_delete(struct mali_pm_domain *domain);
void mali_pm_domain_terminate(void);
/** Get PM domain from domain ID
*/
struct mali_pm_domain *mali_pm_domain_get_from_mask(u32 mask);
struct mali_pm_domain *mali_pm_domain_get_from_index(u32 id);
/* Ref counting */
void mali_pm_domain_ref_get(struct mali_pm_domain *domain);
void mali_pm_domain_ref_put(struct mali_pm_domain *domain);
MALI_STATIC_INLINE struct mali_l2_cache_core *mali_pm_domain_l2_get(struct mali_pm_domain *domain)
{
return domain->l2;
}
MALI_STATIC_INLINE mali_pm_domain_state mali_pm_domain_state_get(struct mali_pm_domain *domain)
{
return domain->state;
}
mali_bool mali_pm_domain_lock_state(struct mali_pm_domain *domain);
void mali_pm_domain_unlock_state(struct mali_pm_domain *domain);
#define MALI_PM_DOMAIN_FOR_EACH_GROUP(group, domain) for ((group) = (domain)->group_list;\
NULL != (group); (group) = (group)->pm_domain_list)
#endif /* __MALI_PM_DOMAIN_H__ */

View File

@@ -0,0 +1,444 @@
/*
* Copyright (C) 2010-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 mali_pmu.c
* Mali driver functions for Mali 400 PMU hardware
*/
#include "mali_hw_core.h"
#include "mali_pmu.h"
#include "mali_pp.h"
#include "mali_kernel_common.h"
#include "mali_osk.h"
#include "mali_pm.h"
#include "mali_osk_mali.h"
u16 mali_pmu_global_domain_config[MALI_MAX_NUMBER_OF_DOMAINS]= {0};
static u32 mali_pmu_detect_mask(void);
/** @brief MALI inbuilt PMU hardware info and PMU hardware has knowledge of cores power mask
*/
struct mali_pmu_core {
struct mali_hw_core hw_core;
_mali_osk_spinlock_t *lock;
u32 registered_cores_mask;
u32 active_cores_mask;
u32 switch_delay;
};
static struct mali_pmu_core *mali_global_pmu_core = NULL;
/** @brief Register layout for hardware PMU
*/
typedef enum {
PMU_REG_ADDR_MGMT_POWER_UP = 0x00, /*< Power up register */
PMU_REG_ADDR_MGMT_POWER_DOWN = 0x04, /*< Power down register */
PMU_REG_ADDR_MGMT_STATUS = 0x08, /*< Core sleep status register */
PMU_REG_ADDR_MGMT_INT_MASK = 0x0C, /*< Interrupt mask register */
PMU_REG_ADDR_MGMT_INT_RAWSTAT = 0x10, /*< Interrupt raw status register */
PMU_REG_ADDR_MGMT_INT_CLEAR = 0x18, /*< Interrupt clear register */
PMU_REG_ADDR_MGMT_SW_DELAY = 0x1C, /*< Switch delay register */
PMU_REGISTER_ADDRESS_SPACE_SIZE = 0x28, /*< Size of register space */
} pmu_reg_addr_mgmt_addr;
#define PMU_REG_VAL_IRQ 1
struct mali_pmu_core *mali_pmu_create(_mali_osk_resource_t *resource)
{
struct mali_pmu_core* pmu;
MALI_DEBUG_ASSERT(NULL == mali_global_pmu_core);
MALI_DEBUG_PRINT(2, ("Mali PMU: Creating Mali PMU core\n"));
pmu = (struct mali_pmu_core *)_mali_osk_malloc(sizeof(struct mali_pmu_core));
if (NULL != pmu) {
pmu->lock = _mali_osk_spinlock_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_PMU);
if (NULL != pmu->lock) {
pmu->registered_cores_mask = mali_pmu_detect_mask();
pmu->active_cores_mask = pmu->registered_cores_mask;
if (_MALI_OSK_ERR_OK == mali_hw_core_create(&pmu->hw_core, resource, PMU_REGISTER_ADDRESS_SPACE_SIZE)) {
_mali_osk_errcode_t err;
struct _mali_osk_device_data data = { 0, };
err = _mali_osk_device_data_get(&data);
if (_MALI_OSK_ERR_OK == err) {
pmu->switch_delay = data.pmu_switch_delay;
mali_global_pmu_core = pmu;
return pmu;
}
mali_hw_core_delete(&pmu->hw_core);
}
_mali_osk_spinlock_term(pmu->lock);
}
_mali_osk_free(pmu);
}
return NULL;
}
void mali_pmu_delete(struct mali_pmu_core *pmu)
{
MALI_DEBUG_ASSERT_POINTER(pmu);
MALI_DEBUG_ASSERT(pmu == mali_global_pmu_core);
MALI_DEBUG_PRINT(2, ("Mali PMU: Deleting Mali PMU core\n"));
_mali_osk_spinlock_term(pmu->lock);
mali_hw_core_delete(&pmu->hw_core);
_mali_osk_free(pmu);
mali_global_pmu_core = NULL;
}
static void mali_pmu_lock(struct mali_pmu_core *pmu)
{
_mali_osk_spinlock_lock(pmu->lock);
}
static void mali_pmu_unlock(struct mali_pmu_core *pmu)
{
_mali_osk_spinlock_unlock(pmu->lock);
}
static _mali_osk_errcode_t mali_pmu_wait_for_command_finish(struct mali_pmu_core *pmu)
{
u32 rawstat;
u32 timeout = MALI_REG_POLL_COUNT_SLOW;
MALI_DEBUG_ASSERT(pmu);
/* Wait for the command to complete */
do {
rawstat = mali_hw_core_register_read(&pmu->hw_core, PMU_REG_ADDR_MGMT_INT_RAWSTAT);
--timeout;
} while (0 == (rawstat & PMU_REG_VAL_IRQ) && 0 < timeout);
MALI_DEBUG_ASSERT(0 < timeout);
if (0 == timeout) {
return _MALI_OSK_ERR_TIMEOUT;
}
mali_hw_core_register_write(&pmu->hw_core, PMU_REG_ADDR_MGMT_INT_CLEAR, PMU_REG_VAL_IRQ);
return _MALI_OSK_ERR_OK;
}
static _mali_osk_errcode_t mali_pmu_power_up_internal(struct mali_pmu_core *pmu, const u32 mask)
{
u32 stat;
u32 active_mask;
u32 mask_ck;
u32 swt_dly;
u32 xxd = 1;
_mali_osk_errcode_t err;
#if !defined(CONFIG_MALI_PMU_PARALLEL_POWER_UP)
u32 current_domain;
#endif
MALI_DEBUG_ASSERT_POINTER(pmu);
MALI_DEBUG_ASSERT(0 == (mali_hw_core_register_read(&pmu->hw_core, PMU_REG_ADDR_MGMT_INT_RAWSTAT)
& PMU_REG_VAL_IRQ));
stat = mali_hw_core_register_read(&pmu->hw_core, PMU_REG_ADDR_MGMT_STATUS);
stat &= pmu->registered_cores_mask;
if (0 == mask || 0 == (stat & mask)) return _MALI_OSK_ERR_OK;
#if defined(CONFIG_MALI_PMU_PARALLEL_POWER_UP)
mali_hw_core_register_write(&pmu->hw_core, PMU_REG_ADDR_MGMT_POWER_UP, mask);
err = mali_pmu_wait_for_command_finish(pmu);
if (_MALI_OSK_ERR_OK != err) {
return err;
}
#else
active_mask = mask & stat;
mask_ck = active_mask;
swt_dly = 0xfff;
for (current_domain = 1; current_domain <= pmu->registered_cores_mask; current_domain <<= 1) {
if (current_domain & active_mask) {
if (mask_ck == 1) {
swt_dly = pmu->switch_delay;
xxd = 0;
}
mali_hw_core_register_write_relaxed(&pmu->hw_core, PMU_REG_ADDR_MGMT_SW_DELAY, swt_dly);
mali_hw_core_register_write(&pmu->hw_core, PMU_REG_ADDR_MGMT_POWER_UP, current_domain);
err = mali_pmu_wait_for_command_finish(pmu);
if (_MALI_OSK_ERR_OK != err) {
return err;
}
}
mask_ck = mask_ck >> 1;
}
if (xxd != 0) {
printk("@@@@ warn\n");
printk("mask_ck:%d,active_mask:%d\n", mask_ck, active_mask);
//panic(0);
}
if (swt_dly != pmu->switch_delay)
mali_hw_core_register_write_relaxed(&pmu->hw_core, PMU_REG_ADDR_MGMT_SW_DELAY, pmu->switch_delay);
#endif
#if defined(DEBUG)
/* Get power status of cores */
stat = mali_hw_core_register_read(&pmu->hw_core, PMU_REG_ADDR_MGMT_STATUS);
stat &= pmu->registered_cores_mask;
MALI_DEBUG_ASSERT(0 == (stat & mask));
MALI_DEBUG_ASSERT(0 == (stat & pmu->active_cores_mask));
#endif /* defined(DEBUG) */
return _MALI_OSK_ERR_OK;
}
static _mali_osk_errcode_t mali_pmu_power_down_internal(struct mali_pmu_core *pmu, const u32 mask)
{
u32 stat;
_mali_osk_errcode_t err;
MALI_DEBUG_ASSERT_POINTER(pmu);
MALI_DEBUG_ASSERT(0 == (mali_hw_core_register_read(&pmu->hw_core, PMU_REG_ADDR_MGMT_INT_RAWSTAT)
& PMU_REG_VAL_IRQ));
stat = mali_hw_core_register_read(&pmu->hw_core, PMU_REG_ADDR_MGMT_STATUS);
stat &= pmu->registered_cores_mask;
if (0 == mask || 0 == ((~stat) & mask)) return _MALI_OSK_ERR_OK;
mali_hw_core_register_write(&pmu->hw_core, PMU_REG_ADDR_MGMT_POWER_DOWN, mask);
/* Do not wait for interrupt on Mali-300/400 if all domains are powered off
* by our power down command, because the HW will simply not generate an
* interrupt in this case.*/
if (mali_is_mali450() || pmu->registered_cores_mask != (mask | stat)) {
err = mali_pmu_wait_for_command_finish(pmu);
if (_MALI_OSK_ERR_OK != err) {
return err;
}
} else {
mali_hw_core_register_write(&pmu->hw_core, PMU_REG_ADDR_MGMT_INT_CLEAR, PMU_REG_VAL_IRQ);
}
#if defined(DEBUG)
/* Get power status of cores */
stat = mali_hw_core_register_read(&pmu->hw_core, PMU_REG_ADDR_MGMT_STATUS);
stat &= pmu->registered_cores_mask;
//MALI_DEBUG_ASSERT(mask == (stat & mask));
#endif
return _MALI_OSK_ERR_OK;
}
_mali_osk_errcode_t mali_pmu_reset(struct mali_pmu_core *pmu)
{
_mali_osk_errcode_t err;
u32 cores_off_mask, cores_on_mask, stat;
mali_pmu_lock(pmu);
/* Setup the desired defaults */
mali_hw_core_register_write_relaxed(&pmu->hw_core, PMU_REG_ADDR_MGMT_INT_MASK, 0);
mali_hw_core_register_write_relaxed(&pmu->hw_core, PMU_REG_ADDR_MGMT_SW_DELAY, pmu->switch_delay);
/* Get power status of cores */
stat = mali_hw_core_register_read(&pmu->hw_core, PMU_REG_ADDR_MGMT_STATUS);
cores_off_mask = pmu->registered_cores_mask & ~(stat | pmu->active_cores_mask);
cores_on_mask = pmu->registered_cores_mask & (stat & pmu->active_cores_mask);
if (0 != cores_off_mask) {
err = mali_pmu_power_down_internal(pmu, cores_off_mask);
if (_MALI_OSK_ERR_OK != err) return err;
}
if (0 != cores_on_mask) {
err = mali_pmu_power_up_internal(pmu, cores_on_mask);
if (_MALI_OSK_ERR_OK != err) return err;
}
#if defined(DEBUG)
{
stat = mali_hw_core_register_read(&pmu->hw_core, PMU_REG_ADDR_MGMT_STATUS);
stat &= pmu->registered_cores_mask;
MALI_DEBUG_ASSERT(stat == (pmu->registered_cores_mask & ~pmu->active_cores_mask));
}
#endif /* defined(DEBUG) */
mali_pmu_unlock(pmu);
return _MALI_OSK_ERR_OK;
}
_mali_osk_errcode_t mali_pmu_power_down(struct mali_pmu_core *pmu, u32 mask)
{
_mali_osk_errcode_t err;
MALI_DEBUG_ASSERT_POINTER(pmu);
MALI_DEBUG_ASSERT(pmu->registered_cores_mask != 0 );
/* Make sure we have a valid power domain mask */
if (mask > pmu->registered_cores_mask) {
return _MALI_OSK_ERR_INVALID_ARGS;
}
mali_pmu_lock(pmu);
MALI_DEBUG_PRINT(4, ("Mali PMU: Power down (0x%08X)\n", mask));
pmu->active_cores_mask &= ~mask;
_mali_osk_pm_dev_ref_add_no_power_on();
if (!mali_pm_is_power_on()) {
/* Don't touch hardware if all of Mali is powered off. */
_mali_osk_pm_dev_ref_dec_no_power_on();
mali_pmu_unlock(pmu);
MALI_DEBUG_PRINT(4, ("Mali PMU: Skipping power down (0x%08X) since Mali is off\n", mask));
return _MALI_OSK_ERR_BUSY;
}
err = mali_pmu_power_down_internal(pmu, mask);
_mali_osk_pm_dev_ref_dec_no_power_on();
mali_pmu_unlock(pmu);
return err;
}
_mali_osk_errcode_t mali_pmu_power_up(struct mali_pmu_core *pmu, u32 mask)
{
_mali_osk_errcode_t err;
MALI_DEBUG_ASSERT_POINTER(pmu);
MALI_DEBUG_ASSERT(pmu->registered_cores_mask != 0 );
/* Make sure we have a valid power domain mask */
if (mask & ~pmu->registered_cores_mask) {
return _MALI_OSK_ERR_INVALID_ARGS;
}
mali_pmu_lock(pmu);
MALI_DEBUG_PRINT(4, ("Mali PMU: Power up (0x%08X)\n", mask));
pmu->active_cores_mask |= mask;
_mali_osk_pm_dev_ref_add_no_power_on();
if (!mali_pm_is_power_on()) {
/* Don't touch hardware if all of Mali is powered off. */
_mali_osk_pm_dev_ref_dec_no_power_on();
mali_pmu_unlock(pmu);
MALI_DEBUG_PRINT(4, ("Mali PMU: Skipping power up (0x%08X) since Mali is off\n", mask));
return _MALI_OSK_ERR_BUSY;
}
err = mali_pmu_power_up_internal(pmu, mask);
_mali_osk_pm_dev_ref_dec_no_power_on();
mali_pmu_unlock(pmu);
return err;
}
_mali_osk_errcode_t mali_pmu_power_down_all(struct mali_pmu_core *pmu)
{
_mali_osk_errcode_t err;
MALI_DEBUG_ASSERT_POINTER(pmu);
MALI_DEBUG_ASSERT(pmu->registered_cores_mask != 0);
mali_pmu_lock(pmu);
/* Setup the desired defaults in case we were called before mali_pmu_reset() */
mali_hw_core_register_write_relaxed(&pmu->hw_core, PMU_REG_ADDR_MGMT_INT_MASK, 0);
mali_hw_core_register_write_relaxed(&pmu->hw_core, PMU_REG_ADDR_MGMT_SW_DELAY, pmu->switch_delay);
err = mali_pmu_power_down_internal(pmu, pmu->registered_cores_mask);
mali_pmu_unlock(pmu);
return err;
}
_mali_osk_errcode_t mali_pmu_power_up_all(struct mali_pmu_core *pmu)
{
_mali_osk_errcode_t err;
MALI_DEBUG_ASSERT_POINTER(pmu);
MALI_DEBUG_ASSERT(pmu->registered_cores_mask != 0);
mali_pmu_lock(pmu);
/* Setup the desired defaults in case we were called before mali_pmu_reset() */
mali_hw_core_register_write_relaxed(&pmu->hw_core, PMU_REG_ADDR_MGMT_INT_MASK, 0);
mali_hw_core_register_write_relaxed(&pmu->hw_core, PMU_REG_ADDR_MGMT_SW_DELAY, pmu->switch_delay);
err = mali_pmu_power_up_internal(pmu, pmu->active_cores_mask);
mali_pmu_unlock(pmu);
return err;
}
struct mali_pmu_core *mali_pmu_get_global_pmu_core(void)
{
return mali_global_pmu_core;
}
static u32 mali_pmu_detect_mask(void)
{
int dynamic_config_pp = 0;
int dynamic_config_l2 = 0;
int i = 0;
u32 mask = 0;
/* Check if PM domain compatible with actually pp core and l2 cache and collection info about domain */
mask = mali_pmu_get_domain_mask(MALI_GP_DOMAIN_INDEX);
for (i = MALI_PP0_DOMAIN_INDEX; i <= MALI_PP7_DOMAIN_INDEX; i++) {
mask |= mali_pmu_get_domain_mask(i);
if (0x0 != mali_pmu_get_domain_mask(i)) {
dynamic_config_pp++;
}
}
for (i = MALI_L20_DOMAIN_INDEX; i <= MALI_L22_DOMAIN_INDEX; i++) {
mask |= mali_pmu_get_domain_mask(i);
if (0x0 != mali_pmu_get_domain_mask(i)) {
dynamic_config_l2++;
}
}
MALI_DEBUG_PRINT(2, ("Mali PMU: mask 0x%x, pp_core %d, l2_core %d \n", mask, dynamic_config_pp, dynamic_config_l2));
return mask;
}
/*
*
* kasin.li@amlogic.com.
**/
u32 mali_pmu_get_status(void)
{
u32 ret;
MALI_DEBUG_ASSERT_POINTER(mali_global_pmu_core);
mali_pmu_lock(mali_global_pmu_core);
ret = mali_hw_core_register_read(&mali_global_pmu_core->hw_core, PMU_REG_ADDR_MGMT_STATUS);
mali_pmu_unlock(mali_global_pmu_core);
return ret;
}

View File

@@ -0,0 +1,139 @@
/*
* Copyright (C) 2010-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 mali_platform.h
* Platform specific Mali driver functions
*/
#ifndef __MALI_PMU_H__
#define __MALI_PMU_H__
#include "mali_osk.h"
#define MALI_GP_DOMAIN_INDEX 0
#define MALI_PP0_DOMAIN_INDEX 1
#define MALI_PP1_DOMAIN_INDEX 2
#define MALI_PP2_DOMAIN_INDEX 3
#define MALI_PP3_DOMAIN_INDEX 4
#define MALI_PP4_DOMAIN_INDEX 5
#define MALI_PP5_DOMAIN_INDEX 6
#define MALI_PP6_DOMAIN_INDEX 7
#define MALI_PP7_DOMAIN_INDEX 8
#define MALI_L20_DOMAIN_INDEX 9
#define MALI_L21_DOMAIN_INDEX 10
#define MALI_L22_DOMAIN_INDEX 11
#define MALI_MAX_NUMBER_OF_DOMAINS 12
/* Record the domain config from the customer or default config */
extern u16 mali_pmu_global_domain_config[];
static inline u16 mali_pmu_get_domain_mask(u32 index)
{
MALI_DEBUG_ASSERT(MALI_MAX_NUMBER_OF_DOMAINS > index);
return mali_pmu_global_domain_config[index];
}
static inline void mali_pmu_set_domain_mask(u32 index, u16 value)
{
MALI_DEBUG_ASSERT(MALI_MAX_NUMBER_OF_DOMAINS > index);
mali_pmu_global_domain_config[index] = value;
}
static inline void mali_pmu_copy_domain_mask(void *src, u32 len)
{
_mali_osk_memcpy(mali_pmu_global_domain_config, src, len);
}
struct mali_pmu_core;
/** @brief Initialisation of MALI PMU
*
* This is called from entry point of the driver in order to create and intialize the PMU resource
*
* @param resource it will be a pointer to a PMU resource
* @param number_of_pp_cores Number of found PP resources in configuration
* @param number_of_l2_caches Number of found L2 cache resources in configuration
* @return The created PMU object, or NULL in case of failure.
*/
struct mali_pmu_core *mali_pmu_create(_mali_osk_resource_t *resource);
/** @brief It deallocates the PMU resource
*
* This is called on the exit of the driver to terminate the PMU resource
*
* @param pmu Pointer to PMU core object to delete
*/
void mali_pmu_delete(struct mali_pmu_core *pmu);
/** @brief Reset PMU core
*
* @param pmu Pointer to PMU core object to reset
* @return _MALI_OSK_ERR_OK on success, otherwise failure.
*/
_mali_osk_errcode_t mali_pmu_reset(struct mali_pmu_core *pmu);
/** @brief MALI GPU power down using MALI in-built PMU
*
* Called to power down the specified cores. The mask will be saved so that \a
* mali_pmu_power_up_all will bring the PMU back to the previous state set with
* this function or \a mali_pmu_power_up.
*
* @param pmu Pointer to PMU core object to power down
* @param mask Mask specifying which power domains to power down
* @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error.
*/
_mali_osk_errcode_t mali_pmu_power_down(struct mali_pmu_core *pmu, u32 mask);
/** @brief MALI GPU power up using MALI in-built PMU
*
* Called to power up the specified cores. The mask will be saved so that \a
* mali_pmu_power_up_all will bring the PMU back to the previous state set with
* this function or \a mali_pmu_power_down.
*
* @param pmu Pointer to PMU core object to power up
* @param mask Mask specifying which power domains to power up
* @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error.
*/
_mali_osk_errcode_t mali_pmu_power_up(struct mali_pmu_core *pmu, u32 mask);
/** @brief MALI GPU power down using MALI in-built PMU
*
* called to power down all cores
*
* @param pmu Pointer to PMU core object to power down
* @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error.
*/
_mali_osk_errcode_t mali_pmu_power_down_all(struct mali_pmu_core *pmu);
/** @brief MALI GPU power up using MALI in-built PMU
*
* called to power up all cores
*
* @param pmu Pointer to PMU core object to power up
* @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error.
*/
_mali_osk_errcode_t mali_pmu_power_up_all(struct mali_pmu_core *pmu);
/** @brief Retrieves the Mali PMU core object (if any)
*
* @return The Mali PMU object, or NULL if no PMU exists.
*/
struct mali_pmu_core *mali_pmu_get_global_pmu_core(void);
/** @brief Retrieves the Mali Power Domain status.
*
* @return the Mali Power Domain status 1 off, 0 on.
*/
extern u32 mali_pmu_get_status(void);
#endif /* __MALI_PMU_H__ */

View File

@@ -0,0 +1,573 @@
/*
* Copyright (C) 2011-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.
*/
#include "mali_pp_job.h"
#include "mali_pp.h"
#include "mali_hw_core.h"
#include "mali_group.h"
#include "regs/mali_200_regs.h"
#include "mali_kernel_common.h"
#include "mali_kernel_core.h"
#include "mali_dma.h"
#if defined(CONFIG_MALI400_PROFILING)
#include "mali_osk_profiling.h"
#endif
/* Number of frame registers on Mali-200 */
#define MALI_PP_MALI200_NUM_FRAME_REGISTERS ((0x04C/4)+1)
/* Number of frame registers on Mali-300 and later */
#define MALI_PP_MALI400_NUM_FRAME_REGISTERS ((0x058/4)+1)
static struct mali_pp_core* mali_global_pp_cores[MALI_MAX_NUMBER_OF_PP_CORES] = { NULL };
static u32 mali_global_num_pp_cores = 0;
/* Interrupt handlers */
static void mali_pp_irq_probe_trigger(void *data);
static _mali_osk_errcode_t mali_pp_irq_probe_ack(void *data);
struct mali_pp_core *mali_pp_create(const _mali_osk_resource_t *resource, struct mali_group *group, mali_bool is_virtual, u32 bcast_id)
{
struct mali_pp_core* core = NULL;
MALI_DEBUG_PRINT(2, ("Mali PP: Creating Mali PP core: %s\n", resource->description));
MALI_DEBUG_PRINT(2, ("Mali PP: Base address of PP core: 0x%x\n", resource->base));
if (mali_global_num_pp_cores >= MALI_MAX_NUMBER_OF_PP_CORES) {
MALI_PRINT_ERROR(("Mali PP: Too many PP core objects created\n"));
return NULL;
}
core = _mali_osk_malloc(sizeof(struct mali_pp_core));
if (NULL != core) {
core->core_id = mali_global_num_pp_cores;
core->bcast_id = bcast_id;
if (_MALI_OSK_ERR_OK == mali_hw_core_create(&core->hw_core, resource, MALI200_REG_SIZEOF_REGISTER_BANK)) {
_mali_osk_errcode_t ret;
if (!is_virtual) {
ret = mali_pp_reset(core);
} else {
ret = _MALI_OSK_ERR_OK;
}
if (_MALI_OSK_ERR_OK == ret) {
ret = mali_group_add_pp_core(group, core);
if (_MALI_OSK_ERR_OK == ret) {
/* Setup IRQ handlers (which will do IRQ probing if needed) */
MALI_DEBUG_ASSERT(!is_virtual || -1 != resource->irq);
core->irq = _mali_osk_irq_init(resource->irq,
mali_group_upper_half_pp,
group,
mali_pp_irq_probe_trigger,
mali_pp_irq_probe_ack,
core,
resource->description);
if (NULL != core->irq) {
mali_global_pp_cores[mali_global_num_pp_cores] = core;
mali_global_num_pp_cores++;
return core;
} else {
MALI_PRINT_ERROR(("Mali PP: Failed to setup interrupt handlers for PP core %s\n", core->hw_core.description));
}
mali_group_remove_pp_core(group);
} else {
MALI_PRINT_ERROR(("Mali PP: Failed to add core %s to group\n", core->hw_core.description));
}
}
mali_hw_core_delete(&core->hw_core);
}
_mali_osk_free(core);
} else {
MALI_PRINT_ERROR(("Mali PP: Failed to allocate memory for PP core\n"));
}
return NULL;
}
void mali_pp_delete(struct mali_pp_core *core)
{
u32 i;
MALI_DEBUG_ASSERT_POINTER(core);
_mali_osk_irq_term(core->irq);
mali_hw_core_delete(&core->hw_core);
/* Remove core from global list */
for (i = 0; i < mali_global_num_pp_cores; i++) {
if (mali_global_pp_cores[i] == core) {
mali_global_pp_cores[i] = NULL;
mali_global_num_pp_cores--;
if (i != mali_global_num_pp_cores) {
/* We removed a PP core from the middle of the array -- move the last
* PP core to the current position to close the gap */
mali_global_pp_cores[i] = mali_global_pp_cores[mali_global_num_pp_cores];
mali_global_pp_cores[mali_global_num_pp_cores] = NULL;
}
break;
}
}
_mali_osk_free(core);
}
void mali_pp_stop_bus(struct mali_pp_core *core)
{
MALI_DEBUG_ASSERT_POINTER(core);
/* Will only send the stop bus command, and not wait for it to complete */
mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_CTRL_MGMT, MALI200_REG_VAL_CTRL_MGMT_STOP_BUS);
}
_mali_osk_errcode_t mali_pp_stop_bus_wait(struct mali_pp_core *core)
{
int i;
MALI_DEBUG_ASSERT_POINTER(core);
/* Send the stop bus command. */
mali_pp_stop_bus(core);
/* Wait for bus to be stopped */
for (i = 0; i < MALI_REG_POLL_COUNT_FAST; i++) {
if (mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_STATUS) & MALI200_REG_VAL_STATUS_BUS_STOPPED)
break;
}
if (MALI_REG_POLL_COUNT_FAST == i) {
MALI_PRINT_ERROR(("Mali PP: Failed to stop bus on %s. Status: 0x%08x\n", core->hw_core.description, mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_STATUS)));
return _MALI_OSK_ERR_FAULT;
}
return _MALI_OSK_ERR_OK;
}
/* Frame register reset values.
* Taken from the Mali400 TRM, 3.6. Pixel processor control register summary */
static const u32 mali_frame_registers_reset_values[_MALI_PP_MAX_FRAME_REGISTERS] = {
0x0, /* Renderer List Address Register */
0x0, /* Renderer State Word Base Address Register */
0x0, /* Renderer Vertex Base Register */
0x2, /* Feature Enable Register */
0x0, /* Z Clear Value Register */
0x0, /* Stencil Clear Value Register */
0x0, /* ABGR Clear Value 0 Register */
0x0, /* ABGR Clear Value 1 Register */
0x0, /* ABGR Clear Value 2 Register */
0x0, /* ABGR Clear Value 3 Register */
0x0, /* Bounding Box Left Right Register */
0x0, /* Bounding Box Bottom Register */
0x0, /* FS Stack Address Register */
0x0, /* FS Stack Size and Initial Value Register */
0x0, /* Reserved */
0x0, /* Reserved */
0x0, /* Origin Offset X Register */
0x0, /* Origin Offset Y Register */
0x75, /* Subpixel Specifier Register */
0x0, /* Tiebreak mode Register */
0x0, /* Polygon List Format Register */
0x0, /* Scaling Register */
0x0 /* Tilebuffer configuration Register */
};
/* WBx register reset values */
static const u32 mali_wb_registers_reset_values[_MALI_PP_MAX_WB_REGISTERS] = {
0x0, /* WBx Source Select Register */
0x0, /* WBx Target Address Register */
0x0, /* WBx Target Pixel Format Register */
0x0, /* WBx Target AA Format Register */
0x0, /* WBx Target Layout */
0x0, /* WBx Target Scanline Length */
0x0, /* WBx Target Flags Register */
0x0, /* WBx MRT Enable Register */
0x0, /* WBx MRT Offset Register */
0x0, /* WBx Global Test Enable Register */
0x0, /* WBx Global Test Reference Value Register */
0x0 /* WBx Global Test Compare Function Register */
};
/* Performance Counter 0 Enable Register reset value */
static const u32 mali_perf_cnt_enable_reset_value = 0;
_mali_osk_errcode_t mali_pp_hard_reset(struct mali_pp_core *core)
{
/* Bus must be stopped before calling this function */
const u32 reset_invalid_value = 0xC0FFE000;
const u32 reset_check_value = 0xC01A0000;
int i;
MALI_DEBUG_ASSERT_POINTER(core);
MALI_DEBUG_PRINT(2, ("Mali PP: Hard reset of core %s\n", core->hw_core.description));
/* Set register to a bogus value. The register will be used to detect when reset is complete */
mali_hw_core_register_write_relaxed(&core->hw_core, MALI200_REG_ADDR_MGMT_WRITE_BOUNDARY_LOW, reset_invalid_value);
mali_hw_core_register_write_relaxed(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_MASK, MALI200_REG_VAL_IRQ_MASK_NONE);
/* Force core to reset */
mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_CTRL_MGMT, MALI200_REG_VAL_CTRL_MGMT_FORCE_RESET);
/* Wait for reset to be complete */
for (i = 0; i < MALI_REG_POLL_COUNT_FAST; i++) {
mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_WRITE_BOUNDARY_LOW, reset_check_value);
if (reset_check_value == mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_WRITE_BOUNDARY_LOW)) {
break;
}
}
if (MALI_REG_POLL_COUNT_FAST == i) {
MALI_PRINT_ERROR(("Mali PP: The hard reset loop didn't work, unable to recover\n"));
}
mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_WRITE_BOUNDARY_LOW, 0x00000000); /* set it back to the default */
/* Re-enable interrupts */
mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_CLEAR, MALI200_REG_VAL_IRQ_MASK_ALL);
mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_MASK, MALI200_REG_VAL_IRQ_MASK_USED);
return _MALI_OSK_ERR_OK;
}
void mali_pp_reset_async(struct mali_pp_core *core)
{
MALI_DEBUG_ASSERT_POINTER(core);
MALI_DEBUG_PRINT(4, ("Mali PP: Reset of core %s\n", core->hw_core.description));
mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_MASK, 0); /* disable the IRQs */
mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_RAWSTAT, MALI200_REG_VAL_IRQ_MASK_ALL);
mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_CTRL_MGMT, MALI400PP_REG_VAL_CTRL_MGMT_SOFT_RESET);
}
_mali_osk_errcode_t mali_pp_reset_wait(struct mali_pp_core *core)
{
int i;
u32 rawstat = 0;
for (i = 0; i < MALI_REG_POLL_COUNT_FAST; i++) {
if (!(mali_pp_read_status(core) & MALI200_REG_VAL_STATUS_RENDERING_ACTIVE)) {
rawstat = mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_RAWSTAT);
if (rawstat == MALI400PP_REG_VAL_IRQ_RESET_COMPLETED) {
break;
}
}
}
if (i == MALI_REG_POLL_COUNT_FAST) {
MALI_PRINT_ERROR(("Mali PP: Failed to reset core %s, rawstat: 0x%08x\n",
core->hw_core.description, rawstat));
return _MALI_OSK_ERR_FAULT;
}
/* Re-enable interrupts */
mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_CLEAR, MALI200_REG_VAL_IRQ_MASK_ALL);
mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_MASK, MALI200_REG_VAL_IRQ_MASK_USED);
return _MALI_OSK_ERR_OK;
}
_mali_osk_errcode_t mali_pp_reset(struct mali_pp_core *core)
{
mali_pp_reset_async(core);
return mali_pp_reset_wait(core);
}
void mali_pp_job_dma_cmd_prepare(struct mali_pp_core *core, struct mali_pp_job *job, u32 sub_job,
mali_bool restart_virtual, mali_dma_cmd_buf *buf)
{
u32 relative_address;
u32 start_index;
u32 nr_of_regs;
u32 *frame_registers = mali_pp_job_get_frame_registers(job);
u32 *wb0_registers = mali_pp_job_get_wb0_registers(job);
u32 *wb1_registers = mali_pp_job_get_wb1_registers(job);
u32 *wb2_registers = mali_pp_job_get_wb2_registers(job);
u32 counter_src0 = mali_pp_job_get_perf_counter_src0(job, sub_job);
u32 counter_src1 = mali_pp_job_get_perf_counter_src1(job, sub_job);
MALI_DEBUG_ASSERT_POINTER(core);
/* Write frame registers */
/*
* There are two frame registers which are different for each sub job:
* 1. The Renderer List Address Register (MALI200_REG_ADDR_FRAME)
* 2. The FS Stack Address Register (MALI200_REG_ADDR_STACK)
*/
mali_dma_write_conditional(buf, &core->hw_core, MALI200_REG_ADDR_FRAME, mali_pp_job_get_addr_frame(job, sub_job), mali_frame_registers_reset_values[MALI200_REG_ADDR_FRAME / sizeof(u32)]);
/* For virtual jobs, the stack address shouldn't be broadcast but written individually */
if (!mali_pp_job_is_virtual(job) || restart_virtual) {
mali_dma_write_conditional(buf, &core->hw_core, MALI200_REG_ADDR_STACK, mali_pp_job_get_addr_stack(job, sub_job), mali_frame_registers_reset_values[MALI200_REG_ADDR_STACK / sizeof(u32)]);
}
/* Write registers between MALI200_REG_ADDR_FRAME and MALI200_REG_ADDR_STACK */
relative_address = MALI200_REG_ADDR_RSW;
start_index = MALI200_REG_ADDR_RSW / sizeof(u32);
nr_of_regs = (MALI200_REG_ADDR_STACK - MALI200_REG_ADDR_RSW) / sizeof(u32);
mali_dma_write_array_conditional(buf, &core->hw_core,
relative_address, &frame_registers[start_index],
nr_of_regs, &mali_frame_registers_reset_values[start_index]);
/* MALI200_REG_ADDR_STACK_SIZE */
relative_address = MALI200_REG_ADDR_STACK_SIZE;
start_index = MALI200_REG_ADDR_STACK_SIZE / sizeof(u32);
mali_dma_write_conditional(buf, &core->hw_core,
relative_address, frame_registers[start_index],
mali_frame_registers_reset_values[start_index]);
/* Skip 2 reserved registers */
/* Write remaining registers */
relative_address = MALI200_REG_ADDR_ORIGIN_OFFSET_X;
start_index = MALI200_REG_ADDR_ORIGIN_OFFSET_X / sizeof(u32);
nr_of_regs = MALI_PP_MALI400_NUM_FRAME_REGISTERS - MALI200_REG_ADDR_ORIGIN_OFFSET_X / sizeof(u32);
mali_dma_write_array_conditional(buf, &core->hw_core,
relative_address, &frame_registers[start_index],
nr_of_regs, &mali_frame_registers_reset_values[start_index]);
/* Write WBx registers */
if (wb0_registers[0]) { /* M200_WB0_REG_SOURCE_SELECT register */
mali_dma_write_array_conditional(buf, &core->hw_core, MALI200_REG_ADDR_WB0, wb0_registers, _MALI_PP_MAX_WB_REGISTERS, mali_wb_registers_reset_values);
}
if (wb1_registers[0]) { /* M200_WB1_REG_SOURCE_SELECT register */
mali_dma_write_array_conditional(buf, &core->hw_core, MALI200_REG_ADDR_WB1, wb1_registers, _MALI_PP_MAX_WB_REGISTERS, mali_wb_registers_reset_values);
}
if (wb2_registers[0]) { /* M200_WB2_REG_SOURCE_SELECT register */
mali_dma_write_array_conditional(buf, &core->hw_core, MALI200_REG_ADDR_WB2, wb2_registers, _MALI_PP_MAX_WB_REGISTERS, mali_wb_registers_reset_values);
}
if (MALI_HW_CORE_NO_COUNTER != counter_src0) {
mali_dma_write(buf, &core->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_0_SRC, counter_src0);
mali_dma_write_conditional(buf, &core->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_0_ENABLE, MALI200_REG_VAL_PERF_CNT_ENABLE, mali_perf_cnt_enable_reset_value);
}
if (MALI_HW_CORE_NO_COUNTER != counter_src1) {
mali_dma_write(buf, &core->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_1_SRC, counter_src1);
mali_dma_write_conditional(buf, &core->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_1_ENABLE, MALI200_REG_VAL_PERF_CNT_ENABLE, mali_perf_cnt_enable_reset_value);
}
/* This is the command that starts the core. */
mali_dma_write(buf, &core->hw_core, MALI200_REG_ADDR_MGMT_CTRL_MGMT, MALI200_REG_VAL_CTRL_MGMT_START_RENDERING);
}
void mali_pp_job_start(struct mali_pp_core *core, struct mali_pp_job *job, u32 sub_job, mali_bool restart_virtual)
{
u32 relative_address;
u32 start_index;
u32 nr_of_regs;
u32 *frame_registers = mali_pp_job_get_frame_registers(job);
u32 *wb0_registers = mali_pp_job_get_wb0_registers(job);
u32 *wb1_registers = mali_pp_job_get_wb1_registers(job);
u32 *wb2_registers = mali_pp_job_get_wb2_registers(job);
u32 counter_src0 = mali_pp_job_get_perf_counter_src0(job, sub_job);
u32 counter_src1 = mali_pp_job_get_perf_counter_src1(job, sub_job);
MALI_DEBUG_ASSERT_POINTER(core);
/* Write frame registers */
/*
* There are two frame registers which are different for each sub job:
* 1. The Renderer List Address Register (MALI200_REG_ADDR_FRAME)
* 2. The FS Stack Address Register (MALI200_REG_ADDR_STACK)
*/
mali_hw_core_register_write_relaxed_conditional(&core->hw_core, MALI200_REG_ADDR_FRAME, mali_pp_job_get_addr_frame(job, sub_job), mali_frame_registers_reset_values[MALI200_REG_ADDR_FRAME / sizeof(u32)]);
/* For virtual jobs, the stack address shouldn't be broadcast but written individually */
if (!mali_pp_job_is_virtual(job) || restart_virtual) {
mali_hw_core_register_write_relaxed_conditional(&core->hw_core, MALI200_REG_ADDR_STACK, mali_pp_job_get_addr_stack(job, sub_job), mali_frame_registers_reset_values[MALI200_REG_ADDR_STACK / sizeof(u32)]);
}
/* Write registers between MALI200_REG_ADDR_FRAME and MALI200_REG_ADDR_STACK */
relative_address = MALI200_REG_ADDR_RSW;
start_index = MALI200_REG_ADDR_RSW / sizeof(u32);
nr_of_regs = (MALI200_REG_ADDR_STACK - MALI200_REG_ADDR_RSW) / sizeof(u32);
mali_hw_core_register_write_array_relaxed_conditional(&core->hw_core,
relative_address, &frame_registers[start_index],
nr_of_regs, &mali_frame_registers_reset_values[start_index]);
/* MALI200_REG_ADDR_STACK_SIZE */
relative_address = MALI200_REG_ADDR_STACK_SIZE;
start_index = MALI200_REG_ADDR_STACK_SIZE / sizeof(u32);
mali_hw_core_register_write_relaxed_conditional(&core->hw_core,
relative_address, frame_registers[start_index],
mali_frame_registers_reset_values[start_index]);
/* Skip 2 reserved registers */
/* Write remaining registers */
relative_address = MALI200_REG_ADDR_ORIGIN_OFFSET_X;
start_index = MALI200_REG_ADDR_ORIGIN_OFFSET_X / sizeof(u32);
nr_of_regs = MALI_PP_MALI400_NUM_FRAME_REGISTERS - MALI200_REG_ADDR_ORIGIN_OFFSET_X / sizeof(u32);
mali_hw_core_register_write_array_relaxed_conditional(&core->hw_core,
relative_address, &frame_registers[start_index],
nr_of_regs, &mali_frame_registers_reset_values[start_index]);
/* Write WBx registers */
if (wb0_registers[0]) { /* M200_WB0_REG_SOURCE_SELECT register */
mali_hw_core_register_write_array_relaxed_conditional(&core->hw_core, MALI200_REG_ADDR_WB0, wb0_registers, _MALI_PP_MAX_WB_REGISTERS, mali_wb_registers_reset_values);
}
if (wb1_registers[0]) { /* M200_WB1_REG_SOURCE_SELECT register */
mali_hw_core_register_write_array_relaxed_conditional(&core->hw_core, MALI200_REG_ADDR_WB1, wb1_registers, _MALI_PP_MAX_WB_REGISTERS, mali_wb_registers_reset_values);
}
if (wb2_registers[0]) { /* M200_WB2_REG_SOURCE_SELECT register */
mali_hw_core_register_write_array_relaxed_conditional(&core->hw_core, MALI200_REG_ADDR_WB2, wb2_registers, _MALI_PP_MAX_WB_REGISTERS, mali_wb_registers_reset_values);
}
if (MALI_HW_CORE_NO_COUNTER != counter_src0) {
mali_hw_core_register_write_relaxed(&core->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_0_SRC, counter_src0);
mali_hw_core_register_write_relaxed_conditional(&core->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_0_ENABLE, MALI200_REG_VAL_PERF_CNT_ENABLE, mali_perf_cnt_enable_reset_value);
}
if (MALI_HW_CORE_NO_COUNTER != counter_src1) {
mali_hw_core_register_write_relaxed(&core->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_1_SRC, counter_src1);
mali_hw_core_register_write_relaxed_conditional(&core->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_1_ENABLE, MALI200_REG_VAL_PERF_CNT_ENABLE, mali_perf_cnt_enable_reset_value);
}
#ifdef CONFIG_MALI400_HEATMAPS_ENABLED
if(job->uargs.perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_HEATMAP_ENABLE) {
mali_hw_core_register_write_relaxed(&core->hw_core, MALI200_REG_ADDR_MGMT_PERFMON_CONTR, ((job->uargs.tilesx & 0x3FF) << 16) | 1);
mali_hw_core_register_write_relaxed(&core->hw_core, MALI200_REG_ADDR_MGMT_PERFMON_BASE, job->uargs.heatmap_mem & 0xFFFFFFF8);
}
#endif /* CONFIG_MALI400_HEATMAPS_ENABLED */
MALI_DEBUG_PRINT(3, ("Mali PP: Starting job 0x%08X part %u/%u on PP core %s\n", job, sub_job + 1, mali_pp_job_get_sub_job_count(job), core->hw_core.description));
/* Adding barrier to make sure all rester writes are finished */
_mali_osk_write_mem_barrier();
/* This is the command that starts the core. */
mali_hw_core_register_write_relaxed(&core->hw_core, MALI200_REG_ADDR_MGMT_CTRL_MGMT, MALI200_REG_VAL_CTRL_MGMT_START_RENDERING);
/* Adding barrier to make sure previous rester writes is finished */
_mali_osk_write_mem_barrier();
}
u32 mali_pp_core_get_version(struct mali_pp_core *core)
{
MALI_DEBUG_ASSERT_POINTER(core);
return mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_VERSION);
}
struct mali_pp_core* mali_pp_get_global_pp_core(u32 index)
{
if (mali_global_num_pp_cores > index) {
return mali_global_pp_cores[index];
}
return NULL;
}
u32 mali_pp_get_glob_num_pp_cores(void)
{
return mali_global_num_pp_cores;
}
/* ------------- interrupt handling below ------------------ */
static void mali_pp_irq_probe_trigger(void *data)
{
struct mali_pp_core *core = (struct mali_pp_core *)data;
mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_MASK, MALI200_REG_VAL_IRQ_MASK_USED);
mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_RAWSTAT, MALI200_REG_VAL_IRQ_FORCE_HANG);
_mali_osk_mem_barrier();
}
static _mali_osk_errcode_t mali_pp_irq_probe_ack(void *data)
{
struct mali_pp_core *core = (struct mali_pp_core *)data;
u32 irq_readout;
irq_readout = mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_STATUS);
if (MALI200_REG_VAL_IRQ_FORCE_HANG & irq_readout) {
mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_CLEAR, MALI200_REG_VAL_IRQ_FORCE_HANG);
_mali_osk_mem_barrier();
return _MALI_OSK_ERR_OK;
}
return _MALI_OSK_ERR_FAULT;
}
#if 0
static void mali_pp_print_registers(struct mali_pp_core *core)
{
MALI_DEBUG_PRINT(2, ("Mali PP: Register MALI200_REG_ADDR_MGMT_VERSION = 0x%08X\n", mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_VERSION)));
MALI_DEBUG_PRINT(2, ("Mali PP: Register MALI200_REG_ADDR_MGMT_CURRENT_REND_LIST_ADDR = 0x%08X\n", mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_CURRENT_REND_LIST_ADDR)));
MALI_DEBUG_PRINT(2, ("Mali PP: Register MALI200_REG_ADDR_MGMT_STATUS = 0x%08X\n", mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_STATUS)));
MALI_DEBUG_PRINT(2, ("Mali PP: Register MALI200_REG_ADDR_MGMT_INT_RAWSTAT = 0x%08X\n", mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_RAWSTAT)));
MALI_DEBUG_PRINT(2, ("Mali PP: Register MALI200_REG_ADDR_MGMT_INT_MASK = 0x%08X\n", mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_MASK)));
MALI_DEBUG_PRINT(2, ("Mali PP: Register MALI200_REG_ADDR_MGMT_INT_STATUS = 0x%08X\n", mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_STATUS)));
MALI_DEBUG_PRINT(2, ("Mali PP: Register MALI200_REG_ADDR_MGMT_BUS_ERROR_STATUS = 0x%08X\n", mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_BUS_ERROR_STATUS)));
MALI_DEBUG_PRINT(2, ("Mali PP: Register MALI200_REG_ADDR_MGMT_PERF_CNT_0_ENABLE = 0x%08X\n", mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_0_ENABLE)));
MALI_DEBUG_PRINT(2, ("Mali PP: Register MALI200_REG_ADDR_MGMT_PERF_CNT_0_SRC = 0x%08X\n", mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_0_SRC)));
MALI_DEBUG_PRINT(2, ("Mali PP: Register MALI200_REG_ADDR_MGMT_PERF_CNT_0_VALUE = 0x%08X\n", mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_0_VALUE)));
MALI_DEBUG_PRINT(2, ("Mali PP: Register MALI200_REG_ADDR_MGMT_PERF_CNT_1_ENABLE = 0x%08X\n", mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_1_ENABLE)));
MALI_DEBUG_PRINT(2, ("Mali PP: Register MALI200_REG_ADDR_MGMT_PERF_CNT_1_SRC = 0x%08X\n", mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_1_SRC)));
MALI_DEBUG_PRINT(2, ("Mali PP: Register MALI200_REG_ADDR_MGMT_PERF_CNT_1_VALUE = 0x%08X\n", mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_1_VALUE)));
}
#endif
#if 0
void mali_pp_print_state(struct mali_pp_core *core)
{
MALI_DEBUG_PRINT(2, ("Mali PP: State: 0x%08x\n", mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_STATUS) ));
}
#endif
void mali_pp_update_performance_counters(struct mali_pp_core *parent, struct mali_pp_core *child, struct mali_pp_job *job, u32 subjob)
{
u32 val0 = 0;
u32 val1 = 0;
u32 counter_src0 = mali_pp_job_get_perf_counter_src0(job, subjob);
u32 counter_src1 = mali_pp_job_get_perf_counter_src1(job, subjob);
#if defined(CONFIG_MALI400_PROFILING)
int counter_index = COUNTER_FP_0_C0 + (2 * child->core_id);
#endif
if (MALI_HW_CORE_NO_COUNTER != counter_src0) {
val0 = mali_hw_core_register_read(&child->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_0_VALUE);
mali_pp_job_set_perf_counter_value0(job, subjob, val0);
#if defined(CONFIG_MALI400_PROFILING)
_mali_osk_profiling_report_hw_counter(counter_index, val0);
#endif
}
if (MALI_HW_CORE_NO_COUNTER != counter_src1) {
val1 = mali_hw_core_register_read(&child->hw_core, MALI200_REG_ADDR_MGMT_PERF_CNT_1_VALUE);
mali_pp_job_set_perf_counter_value1(job, subjob, val1);
#if defined(CONFIG_MALI400_PROFILING)
_mali_osk_profiling_report_hw_counter(counter_index + 1, val1);
#endif
}
}
#if MALI_STATE_TRACKING
u32 mali_pp_dump_state(struct mali_pp_core *core, char *buf, u32 size)
{
int n = 0;
n += _mali_osk_snprintf(buf + n, size - n, "\tPP #%d: %s\n", core->core_id, core->hw_core.description);
return n;
}
#endif

View File

@@ -0,0 +1,131 @@
/*
* Copyright (C) 2011-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.
*/
#ifndef __MALI_PP_H__
#define __MALI_PP_H__
#include "mali_osk.h"
#include "mali_pp_job.h"
#include "mali_hw_core.h"
#include "mali_dma.h"
struct mali_group;
#define MALI_MAX_NUMBER_OF_PP_CORES 9
/**
* Definition of the PP core struct
* Used to track a PP core in the system.
*/
struct mali_pp_core {
struct mali_hw_core hw_core; /**< Common for all HW cores */
_mali_osk_irq_t *irq; /**< IRQ handler */
u32 core_id; /**< Unique core ID */
u32 bcast_id; /**< The "flag" value used by the Mali-450 broadcast and DLBU unit */
};
_mali_osk_errcode_t mali_pp_initialize(void);
void mali_pp_terminate(void);
struct mali_pp_core *mali_pp_create(const _mali_osk_resource_t * resource, struct mali_group *group, mali_bool is_virtual, u32 bcast_id);
void mali_pp_delete(struct mali_pp_core *core);
void mali_pp_stop_bus(struct mali_pp_core *core);
_mali_osk_errcode_t mali_pp_stop_bus_wait(struct mali_pp_core *core);
void mali_pp_reset_async(struct mali_pp_core *core);
_mali_osk_errcode_t mali_pp_reset_wait(struct mali_pp_core *core);
_mali_osk_errcode_t mali_pp_reset(struct mali_pp_core *core);
_mali_osk_errcode_t mali_pp_hard_reset(struct mali_pp_core *core);
void mali_pp_job_start(struct mali_pp_core *core, struct mali_pp_job *job, u32 sub_job, mali_bool restart_virtual);
/**
* @brief Add commands to DMA command buffer to start PP job on core.
*/
void mali_pp_job_dma_cmd_prepare(struct mali_pp_core *core, struct mali_pp_job *job, u32 sub_job,
mali_bool restart_virtual, mali_dma_cmd_buf *buf);
u32 mali_pp_core_get_version(struct mali_pp_core *core);
MALI_STATIC_INLINE u32 mali_pp_core_get_id(struct mali_pp_core *core)
{
MALI_DEBUG_ASSERT_POINTER(core);
return core->core_id;
}
MALI_STATIC_INLINE u32 mali_pp_core_get_bcast_id(struct mali_pp_core *core)
{
MALI_DEBUG_ASSERT_POINTER(core);
return core->bcast_id;
}
struct mali_pp_core* mali_pp_get_global_pp_core(u32 index);
u32 mali_pp_get_glob_num_pp_cores(void);
/* Debug */
u32 mali_pp_dump_state(struct mali_pp_core *core, char *buf, u32 size);
/**
* Put instrumented HW counters from the core(s) to the job object (if enabled)
*
* parent and child is always the same, except for virtual jobs on Mali-450.
* In this case, the counters will be enabled on the virtual core (parent),
* but values need to be read from the child cores.
*
* @param parent The core used to see if the counters was enabled
* @param child The core to actually read the values from
* @job Job object to update with counter values (if enabled)
* @subjob Which subjob the counters are applicable for (core ID for virtual jobs)
*/
void mali_pp_update_performance_counters(struct mali_pp_core *parent, struct mali_pp_core *child, struct mali_pp_job *job, u32 subjob);
MALI_STATIC_INLINE const char *mali_pp_get_hw_core_desc(struct mali_pp_core *core)
{
return core->hw_core.description;
}
/*** Register reading/writing functions ***/
MALI_STATIC_INLINE u32 mali_pp_get_int_stat(struct mali_pp_core *core)
{
return mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_STATUS);
}
MALI_STATIC_INLINE u32 mali_pp_read_rawstat(struct mali_pp_core *core)
{
return mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_RAWSTAT) & MALI200_REG_VAL_IRQ_MASK_USED;
}
MALI_STATIC_INLINE u32 mali_pp_read_status(struct mali_pp_core *core)
{
return mali_hw_core_register_read(&core->hw_core, MALI200_REG_ADDR_MGMT_STATUS);
}
MALI_STATIC_INLINE void mali_pp_mask_all_interrupts(struct mali_pp_core *core)
{
mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_MASK, MALI200_REG_VAL_IRQ_MASK_NONE);
}
MALI_STATIC_INLINE void mali_pp_clear_hang_interrupt(struct mali_pp_core *core)
{
mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_CLEAR, MALI200_REG_VAL_IRQ_HANG);
}
MALI_STATIC_INLINE void mali_pp_enable_interrupts(struct mali_pp_core *core)
{
mali_hw_core_register_write(&core->hw_core, MALI200_REG_ADDR_MGMT_INT_MASK, MALI200_REG_VAL_IRQ_MASK_USED);
}
MALI_STATIC_INLINE void mali_pp_write_addr_stack(struct mali_pp_core *core, struct mali_pp_job *job)
{
u32 addr = mali_pp_job_get_addr_stack(job, core->core_id);
mali_hw_core_register_write_relaxed(&core->hw_core, MALI200_REG_ADDR_STACK, addr);
}
#endif /* __MALI_PP_H__ */

View File

@@ -0,0 +1,278 @@
/*
* Copyright (C) 2011-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.
*/
#include "mali_pp.h"
#include "mali_pp_job.h"
#include "mali_dma.h"
#include "mali_osk.h"
#include "mali_osk_list.h"
#include "mali_kernel_common.h"
#include "mali_uk_types.h"
#include "mali_pp_scheduler.h"
#if defined(CONFIG_DMA_SHARED_BUFFER) && !defined(CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH)
#include "linux/mali_memory_dma_buf.h"
#endif
static u32 pp_counter_src0 = MALI_HW_CORE_NO_COUNTER; /**< Performance counter 0, MALI_HW_CORE_NO_COUNTER for disabled */
static u32 pp_counter_src1 = MALI_HW_CORE_NO_COUNTER; /**< Performance counter 1, MALI_HW_CORE_NO_COUNTER for disabled */
static _mali_osk_atomic_t pp_counter_per_sub_job_count; /**< Number of values in the two arrays which is != MALI_HW_CORE_NO_COUNTER */
static u32 pp_counter_per_sub_job_src0[_MALI_PP_MAX_SUB_JOBS] = { MALI_HW_CORE_NO_COUNTER, MALI_HW_CORE_NO_COUNTER, MALI_HW_CORE_NO_COUNTER, MALI_HW_CORE_NO_COUNTER, MALI_HW_CORE_NO_COUNTER, MALI_HW_CORE_NO_COUNTER, MALI_HW_CORE_NO_COUNTER, MALI_HW_CORE_NO_COUNTER };
static u32 pp_counter_per_sub_job_src1[_MALI_PP_MAX_SUB_JOBS] = { MALI_HW_CORE_NO_COUNTER, MALI_HW_CORE_NO_COUNTER, MALI_HW_CORE_NO_COUNTER, MALI_HW_CORE_NO_COUNTER, MALI_HW_CORE_NO_COUNTER, MALI_HW_CORE_NO_COUNTER, MALI_HW_CORE_NO_COUNTER, MALI_HW_CORE_NO_COUNTER };
void mali_pp_job_initialize(void)
{
_mali_osk_atomic_init(&pp_counter_per_sub_job_count, 0);
}
void mali_pp_job_terminate(void)
{
_mali_osk_atomic_term(&pp_counter_per_sub_job_count);
}
struct mali_pp_job *mali_pp_job_create(struct mali_session_data *session, _mali_uk_pp_start_job_s *uargs, u32 id)
{
struct mali_pp_job *job;
u32 perf_counter_flag;
job = _mali_osk_calloc(1, sizeof(struct mali_pp_job));
if (NULL != job) {
if (0 != _mali_osk_copy_from_user(&job->uargs, uargs, sizeof(_mali_uk_pp_start_job_s))) {
goto fail;
}
if (job->uargs.num_cores > _MALI_PP_MAX_SUB_JOBS) {
MALI_PRINT_ERROR(("Mali PP job: Too many sub jobs specified in job object\n"));
goto fail;
}
if (!mali_pp_job_use_no_notification(job)) {
job->finished_notification = _mali_osk_notification_create(_MALI_NOTIFICATION_PP_FINISHED, sizeof(_mali_uk_pp_job_finished_s));
if (NULL == job->finished_notification) goto fail;
}
perf_counter_flag = mali_pp_job_get_perf_counter_flag(job);
/* case when no counters came from user space
* so pass the debugfs / DS-5 provided global ones to the job object */
if (!((perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_SRC0_ENABLE) ||
(perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_SRC1_ENABLE))) {
u32 sub_job_count = _mali_osk_atomic_read(&pp_counter_per_sub_job_count);
/* These counters apply for all virtual jobs, and where no per sub job counter is specified */
job->uargs.perf_counter_src0 = pp_counter_src0;
job->uargs.perf_counter_src1 = pp_counter_src1;
/* We only copy the per sub job array if it is enabled with at least one counter */
if (0 < sub_job_count) {
job->perf_counter_per_sub_job_count = sub_job_count;
_mali_osk_memcpy(job->perf_counter_per_sub_job_src0, pp_counter_per_sub_job_src0, sizeof(pp_counter_per_sub_job_src0));
_mali_osk_memcpy(job->perf_counter_per_sub_job_src1, pp_counter_per_sub_job_src1, sizeof(pp_counter_per_sub_job_src1));
}
}
_mali_osk_list_init(&job->list);
job->session = session;
_mali_osk_list_init(&job->session_list);
job->id = id;
job->sub_jobs_num = job->uargs.num_cores ? job->uargs.num_cores : 1;
job->pid = _mali_osk_get_pid();
job->tid = _mali_osk_get_tid();
job->num_memory_cookies = job->uargs.num_memory_cookies;
if (job->num_memory_cookies > 0) {
u32 size;
if (job->uargs.num_memory_cookies > session->descriptor_mapping->current_nr_mappings) {
MALI_PRINT_ERROR(("Mali PP job: Too many memory cookies specified in job object\n"));
goto fail;
}
size = sizeof(*job->uargs.memory_cookies) * job->num_memory_cookies;
job->memory_cookies = _mali_osk_malloc(size);
if (NULL == job->memory_cookies) {
MALI_PRINT_ERROR(("Mali PP job: Failed to allocate %d bytes of memory cookies!\n", size));
goto fail;
}
if (0 != _mali_osk_copy_from_user(job->memory_cookies, job->uargs.memory_cookies, size)) {
MALI_PRINT_ERROR(("Mali PP job: Failed to copy %d bytes of memory cookies from user!\n", size));
goto fail;
}
#if defined(CONFIG_DMA_SHARED_BUFFER) && !defined(CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH)
job->num_dma_bufs = job->num_memory_cookies;
job->dma_bufs = _mali_osk_calloc(job->num_dma_bufs, sizeof(struct mali_dma_buf_attachment *));
if (NULL == job->dma_bufs) {
MALI_PRINT_ERROR(("Mali PP job: Failed to allocate dma_bufs array!\n"));
goto fail;
}
#endif
}
/* Prepare DMA command buffer to start job, if it is virtual. */
if (mali_pp_job_is_virtual(job)) {
struct mali_pp_core *core;
_mali_osk_errcode_t err = mali_dma_get_cmd_buf(&job->dma_cmd_buf);
if (_MALI_OSK_ERR_OK != err) {
MALI_PRINT_ERROR(("Mali PP job: Failed to allocate DMA command buffer\n"));
goto fail;
}
core = mali_pp_scheduler_get_virtual_pp();
MALI_DEBUG_ASSERT_POINTER(core);
mali_pp_job_dma_cmd_prepare(core, job, 0, MALI_FALSE, &job->dma_cmd_buf);
}
if (_MALI_OSK_ERR_OK != mali_pp_job_check(job)) {
/* Not a valid job. */
goto fail;
}
mali_timeline_tracker_init(&job->tracker, MALI_TIMELINE_TRACKER_PP, NULL, job);
mali_timeline_fence_copy_uk_fence(&(job->tracker.fence), &(job->uargs.fence));
return job;
}
fail:
if (NULL != job) {
mali_pp_job_delete(job);
}
return NULL;
}
void mali_pp_job_delete(struct mali_pp_job *job)
{
mali_dma_put_cmd_buf(&job->dma_cmd_buf);
if (NULL != job->finished_notification) {
_mali_osk_notification_delete(job->finished_notification);
}
_mali_osk_free(job->memory_cookies);
#if defined(CONFIG_DMA_SHARED_BUFFER) && !defined(CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH)
/* Unmap buffers attached to job */
if (0 < job->num_dma_bufs) {
mali_dma_buf_unmap_job(job);
}
_mali_osk_free(job->dma_bufs);
#endif /* CONFIG_DMA_SHARED_BUFFER */
_mali_osk_free(job);
}
u32 mali_pp_job_get_perf_counter_src0(struct mali_pp_job *job, u32 sub_job)
{
/* Virtual jobs always use the global job counter (or if there are per sub job counters at all) */
if (mali_pp_job_is_virtual(job) || 0 == job->perf_counter_per_sub_job_count) {
return job->uargs.perf_counter_src0;
}
/* Use per sub job counter if enabled... */
if (MALI_HW_CORE_NO_COUNTER != job->perf_counter_per_sub_job_src0[sub_job]) {
return job->perf_counter_per_sub_job_src0[sub_job];
}
/* ...else default to global job counter */
return job->uargs.perf_counter_src0;
}
u32 mali_pp_job_get_perf_counter_src1(struct mali_pp_job *job, u32 sub_job)
{
/* Virtual jobs always use the global job counter (or if there are per sub job counters at all) */
if (mali_pp_job_is_virtual(job) || 0 == job->perf_counter_per_sub_job_count) {
/* Virtual jobs always use the global job counter */
return job->uargs.perf_counter_src1;
}
/* Use per sub job counter if enabled... */
if (MALI_HW_CORE_NO_COUNTER != job->perf_counter_per_sub_job_src1[sub_job]) {
return job->perf_counter_per_sub_job_src1[sub_job];
}
/* ...else default to global job counter */
return job->uargs.perf_counter_src1;
}
void mali_pp_job_set_pp_counter_global_src0(u32 counter)
{
pp_counter_src0 = counter;
}
void mali_pp_job_set_pp_counter_global_src1(u32 counter)
{
pp_counter_src1 = counter;
}
void mali_pp_job_set_pp_counter_sub_job_src0(u32 sub_job, u32 counter)
{
MALI_DEBUG_ASSERT(sub_job < _MALI_PP_MAX_SUB_JOBS);
if (MALI_HW_CORE_NO_COUNTER == pp_counter_per_sub_job_src0[sub_job]) {
/* increment count since existing counter was disabled */
_mali_osk_atomic_inc(&pp_counter_per_sub_job_count);
}
if (MALI_HW_CORE_NO_COUNTER == counter) {
/* decrement count since new counter is disabled */
_mali_osk_atomic_dec(&pp_counter_per_sub_job_count);
}
/* PS: A change from MALI_HW_CORE_NO_COUNTER to MALI_HW_CORE_NO_COUNTER will inc and dec, result will be 0 change */
pp_counter_per_sub_job_src0[sub_job] = counter;
}
void mali_pp_job_set_pp_counter_sub_job_src1(u32 sub_job, u32 counter)
{
MALI_DEBUG_ASSERT(sub_job < _MALI_PP_MAX_SUB_JOBS);
if (MALI_HW_CORE_NO_COUNTER == pp_counter_per_sub_job_src1[sub_job]) {
/* increment count since existing counter was disabled */
_mali_osk_atomic_inc(&pp_counter_per_sub_job_count);
}
if (MALI_HW_CORE_NO_COUNTER == counter) {
/* decrement count since new counter is disabled */
_mali_osk_atomic_dec(&pp_counter_per_sub_job_count);
}
/* PS: A change from MALI_HW_CORE_NO_COUNTER to MALI_HW_CORE_NO_COUNTER will inc and dec, result will be 0 change */
pp_counter_per_sub_job_src1[sub_job] = counter;
}
u32 mali_pp_job_get_pp_counter_global_src0(void)
{
return pp_counter_src0;
}
u32 mali_pp_job_get_pp_counter_global_src1(void)
{
return pp_counter_src1;
}
u32 mali_pp_job_get_pp_counter_sub_job_src0(u32 sub_job)
{
MALI_DEBUG_ASSERT(sub_job < _MALI_PP_MAX_SUB_JOBS);
return pp_counter_per_sub_job_src0[sub_job];
}
u32 mali_pp_job_get_pp_counter_sub_job_src1(u32 sub_job)
{
MALI_DEBUG_ASSERT(sub_job < _MALI_PP_MAX_SUB_JOBS);
return pp_counter_per_sub_job_src1[sub_job];
}

View File

@@ -0,0 +1,392 @@
/*
* Copyright (C) 2011-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.
*/
#ifndef __MALI_PP_JOB_H__
#define __MALI_PP_JOB_H__
#include "mali_osk.h"
#include "mali_osk_list.h"
#include "mali_uk_types.h"
#include "mali_session.h"
#include "mali_kernel_common.h"
#include "regs/mali_200_regs.h"
#include "mali_kernel_core.h"
#include <linux/version.h>
#ifdef CONFIG_SYNC
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,10)
#include <sync.h>
#else
#include <linux/sync.h>
#endif
#endif
#include "mali_dma.h"
#include "mali_dlbu.h"
#include "mali_timeline.h"
#if defined(CONFIG_DMA_SHARED_BUFFER) && !defined(CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH)
#include "linux/mali_memory_dma_buf.h"
#endif
/**
* The structure represents a PP job, including all sub-jobs
* (This struct unfortunately needs to be public because of how the _mali_osk_list_*
* mechanism works)
*/
struct mali_pp_job {
_mali_osk_list_t list; /**< Used to link jobs together in the scheduler queue */
struct mali_session_data *session; /**< Session which submitted this job */
_mali_osk_list_t session_list; /**< Used to link jobs together in the session job list */
_mali_osk_list_t session_fb_lookup_list; /**< Used to link jobs together from the same frame builder in the session */
_mali_uk_pp_start_job_s uargs; /**< Arguments from user space */
mali_dma_cmd_buf dma_cmd_buf; /**< Command buffer for starting job using Mali-450 DMA unit */
u32 id; /**< Identifier for this job in kernel space (sequential numbering) */
u32 cache_order; /**< Cache order used for L2 cache flushing (sequential numbering) */
u32 perf_counter_value0[_MALI_PP_MAX_SUB_JOBS]; /**< Value of performance counter 0 (to be returned to user space), one for each sub job */
u32 perf_counter_value1[_MALI_PP_MAX_SUB_JOBS]; /**< Value of performance counter 1 (to be returned to user space), one for each sub job */
u32 sub_jobs_num; /**< Number of subjobs; set to 1 for Mali-450 if DLBU is used, otherwise equals number of PP cores */
u32 sub_jobs_started; /**< Total number of sub-jobs started (always started in ascending order) */
u32 sub_jobs_completed; /**< Number of completed sub-jobs in this superjob */
u32 sub_job_errors; /**< Bitfield with errors (errors for each single sub-job is or'ed together) */
u32 pid; /**< Process ID of submitting process */
u32 tid; /**< Thread ID of submitting thread */
_mali_osk_notification_t *finished_notification; /**< Notification sent back to userspace on job complete */
u32 num_memory_cookies; /**< Number of memory cookies attached to job */
u32 *memory_cookies; /**< Memory cookies attached to job */
#if defined(CONFIG_DMA_SHARED_BUFFER) && !defined(CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH)
struct mali_dma_buf_attachment **dma_bufs; /**< Array of DMA-bufs used by job */
u32 num_dma_bufs; /**< Number of DMA-bufs used by job */
#endif
struct mali_timeline_tracker tracker; /**< Timeline tracker for this job */
u32 perf_counter_per_sub_job_count; /**< Number of values in the two arrays which is != MALI_HW_CORE_NO_COUNTER */
u32 perf_counter_per_sub_job_src0[_MALI_PP_MAX_SUB_JOBS]; /**< Per sub job counters src0 */
u32 perf_counter_per_sub_job_src1[_MALI_PP_MAX_SUB_JOBS]; /**< Per sub job counters src1 */
};
void mali_pp_job_initialize(void);
void mali_pp_job_terminate(void);
struct mali_pp_job *mali_pp_job_create(struct mali_session_data *session, _mali_uk_pp_start_job_s *uargs, u32 id);
void mali_pp_job_delete(struct mali_pp_job *job);
u32 mali_pp_job_get_perf_counter_src0(struct mali_pp_job *job, u32 sub_job);
u32 mali_pp_job_get_perf_counter_src1(struct mali_pp_job *job, u32 sub_job);
void mali_pp_job_set_pp_counter_global_src0(u32 counter);
void mali_pp_job_set_pp_counter_global_src1(u32 counter);
void mali_pp_job_set_pp_counter_sub_job_src0(u32 sub_job, u32 counter);
void mali_pp_job_set_pp_counter_sub_job_src1(u32 sub_job, u32 counter);
u32 mali_pp_job_get_pp_counter_global_src0(void);
u32 mali_pp_job_get_pp_counter_global_src1(void);
u32 mali_pp_job_get_pp_counter_sub_job_src0(u32 sub_job);
u32 mali_pp_job_get_pp_counter_sub_job_src1(u32 sub_job);
MALI_STATIC_INLINE u32 mali_pp_job_get_id(struct mali_pp_job *job)
{
return (NULL == job) ? 0 : job->id;
}
MALI_STATIC_INLINE u32 mali_pp_job_get_cache_order(struct mali_pp_job *job)
{
return (NULL == job) ? 0 : job->cache_order;
}
MALI_STATIC_INLINE u32 mali_pp_job_get_user_id(struct mali_pp_job *job)
{
return job->uargs.user_job_ptr;
}
MALI_STATIC_INLINE u32 mali_pp_job_get_frame_builder_id(struct mali_pp_job *job)
{
return job->uargs.frame_builder_id;
}
MALI_STATIC_INLINE u32 mali_pp_job_get_flush_id(struct mali_pp_job *job)
{
return job->uargs.flush_id;
}
MALI_STATIC_INLINE u32 mali_pp_job_get_pid(struct mali_pp_job *job)
{
return job->pid;
}
MALI_STATIC_INLINE u32 mali_pp_job_get_tid(struct mali_pp_job *job)
{
return job->tid;
}
MALI_STATIC_INLINE u32* mali_pp_job_get_frame_registers(struct mali_pp_job *job)
{
return job->uargs.frame_registers;
}
MALI_STATIC_INLINE u32* mali_pp_job_get_dlbu_registers(struct mali_pp_job *job)
{
return job->uargs.dlbu_registers;
}
MALI_STATIC_INLINE mali_bool mali_pp_job_is_virtual(struct mali_pp_job *job)
{
#if defined(CONFIG_MALI450)
return 0 == job->uargs.num_cores;
#else
return MALI_FALSE;
#endif
}
MALI_STATIC_INLINE u32 mali_pp_job_get_addr_frame(struct mali_pp_job *job, u32 sub_job)
{
if (mali_pp_job_is_virtual(job)) {
return MALI_DLBU_VIRT_ADDR;
} else if (0 == sub_job) {
return job->uargs.frame_registers[MALI200_REG_ADDR_FRAME / sizeof(u32)];
} else if (sub_job < _MALI_PP_MAX_SUB_JOBS) {
return job->uargs.frame_registers_addr_frame[sub_job - 1];
}
return 0;
}
MALI_STATIC_INLINE u32 mali_pp_job_get_addr_stack(struct mali_pp_job *job, u32 sub_job)
{
if (0 == sub_job) {
return job->uargs.frame_registers[MALI200_REG_ADDR_STACK / sizeof(u32)];
} else if (sub_job < _MALI_PP_MAX_SUB_JOBS) {
return job->uargs.frame_registers_addr_stack[sub_job - 1];
}
return 0;
}
MALI_STATIC_INLINE u32* mali_pp_job_get_wb0_registers(struct mali_pp_job *job)
{
return job->uargs.wb0_registers;
}
MALI_STATIC_INLINE u32* mali_pp_job_get_wb1_registers(struct mali_pp_job *job)
{
return job->uargs.wb1_registers;
}
MALI_STATIC_INLINE u32* mali_pp_job_get_wb2_registers(struct mali_pp_job *job)
{
return job->uargs.wb2_registers;
}
MALI_STATIC_INLINE void mali_pp_job_disable_wb0(struct mali_pp_job *job)
{
job->uargs.wb0_registers[MALI200_REG_ADDR_WB_SOURCE_SELECT] = 0;
}
MALI_STATIC_INLINE void mali_pp_job_disable_wb1(struct mali_pp_job *job)
{
job->uargs.wb1_registers[MALI200_REG_ADDR_WB_SOURCE_SELECT] = 0;
}
MALI_STATIC_INLINE void mali_pp_job_disable_wb2(struct mali_pp_job *job)
{
job->uargs.wb2_registers[MALI200_REG_ADDR_WB_SOURCE_SELECT] = 0;
}
MALI_STATIC_INLINE mali_bool mali_pp_job_all_writeback_unit_disabled(struct mali_pp_job *job)
{
MALI_DEBUG_ASSERT_POINTER(job);
if ( job->uargs.wb0_registers[MALI200_REG_ADDR_WB_SOURCE_SELECT] ||
job->uargs.wb1_registers[MALI200_REG_ADDR_WB_SOURCE_SELECT] ||
job->uargs.wb2_registers[MALI200_REG_ADDR_WB_SOURCE_SELECT]
) {
/* At least one output unit active */
return MALI_FALSE;
}
/* All outputs are disabled - we can abort the job */
return MALI_TRUE;
}
MALI_STATIC_INLINE u32 mali_pp_job_get_fb_lookup_id(struct mali_pp_job *job)
{
MALI_DEBUG_ASSERT_POINTER(job);
return MALI_PP_JOB_FB_LOOKUP_LIST_MASK & job->uargs.frame_builder_id;
}
MALI_STATIC_INLINE struct mali_session_data *mali_pp_job_get_session(struct mali_pp_job *job)
{
return job->session;
}
MALI_STATIC_INLINE mali_bool mali_pp_job_has_unstarted_sub_jobs(struct mali_pp_job *job)
{
return (job->sub_jobs_started < job->sub_jobs_num) ? MALI_TRUE : MALI_FALSE;
}
/* Function used when we are terminating a session with jobs. Return TRUE if it has a rendering job.
Makes sure that no new subjobs are started. */
MALI_STATIC_INLINE void mali_pp_job_mark_unstarted_failed(struct mali_pp_job *job)
{
u32 jobs_remaining = job->sub_jobs_num - job->sub_jobs_started;
job->sub_jobs_started += jobs_remaining;
job->sub_jobs_completed += jobs_remaining;
job->sub_job_errors += jobs_remaining;
}
MALI_STATIC_INLINE void mali_pp_job_mark_unstarted_success(struct mali_pp_job *job)
{
u32 jobs_remaining = job->sub_jobs_num - job->sub_jobs_started;
job->sub_jobs_started += jobs_remaining;
job->sub_jobs_completed += jobs_remaining;
}
MALI_STATIC_INLINE mali_bool mali_pp_job_is_complete(struct mali_pp_job *job)
{
return (job->sub_jobs_num == job->sub_jobs_completed) ? MALI_TRUE : MALI_FALSE;
}
MALI_STATIC_INLINE u32 mali_pp_job_get_first_unstarted_sub_job(struct mali_pp_job *job)
{
return job->sub_jobs_started;
}
MALI_STATIC_INLINE u32 mali_pp_job_get_sub_job_count(struct mali_pp_job *job)
{
return job->sub_jobs_num;
}
MALI_STATIC_INLINE mali_bool mali_pp_job_needs_dma_buf_mapping(struct mali_pp_job *job)
{
MALI_DEBUG_ASSERT(job);
if (0 != job->num_memory_cookies) {
return MALI_TRUE;
}
return MALI_FALSE;
}
MALI_STATIC_INLINE void mali_pp_job_mark_sub_job_started(struct mali_pp_job *job, u32 sub_job)
{
MALI_DEBUG_ASSERT_POINTER(job);
/* Assert that we are marking the "first unstarted sub job" as started */
MALI_DEBUG_ASSERT(job->sub_jobs_started == sub_job);
job->sub_jobs_started++;
}
MALI_STATIC_INLINE void mali_pp_job_mark_sub_job_completed(struct mali_pp_job *job, mali_bool success)
{
job->sub_jobs_completed++;
if ( MALI_FALSE == success ) {
job->sub_job_errors++;
}
}
MALI_STATIC_INLINE mali_bool mali_pp_job_was_success(struct mali_pp_job *job)
{
if ( 0 == job->sub_job_errors ) {
return MALI_TRUE;
}
return MALI_FALSE;
}
MALI_STATIC_INLINE mali_bool mali_pp_job_use_no_notification(struct mali_pp_job *job)
{
return job->uargs.flags & _MALI_PP_JOB_FLAG_NO_NOTIFICATION ? MALI_TRUE : MALI_FALSE;
}
MALI_STATIC_INLINE u32 mali_pp_job_get_perf_counter_flag(struct mali_pp_job *job)
{
return job->uargs.perf_counter_flag;
}
MALI_STATIC_INLINE u32 mali_pp_job_get_perf_counter_value0(struct mali_pp_job *job, u32 sub_job)
{
return job->perf_counter_value0[sub_job];
}
MALI_STATIC_INLINE u32 mali_pp_job_get_perf_counter_value1(struct mali_pp_job *job, u32 sub_job)
{
return job->perf_counter_value1[sub_job];
}
MALI_STATIC_INLINE void mali_pp_job_set_perf_counter_value0(struct mali_pp_job *job, u32 sub_job, u32 value)
{
job->perf_counter_value0[sub_job] = value;
}
MALI_STATIC_INLINE void mali_pp_job_set_perf_counter_value1(struct mali_pp_job *job, u32 sub_job, u32 value)
{
job->perf_counter_value1[sub_job] = value;
}
MALI_STATIC_INLINE _mali_osk_errcode_t mali_pp_job_check(struct mali_pp_job *job)
{
if (mali_pp_job_is_virtual(job) && job->sub_jobs_num != 1) {
return _MALI_OSK_ERR_FAULT;
}
return _MALI_OSK_ERR_OK;
}
/**
* Returns MALI_TRUE if first job should be started after second job.
*
* @param first First job.
* @param second Second job.
* @return MALI_TRUE if first job should be started after second job, MALI_FALSE if not.
*/
MALI_STATIC_INLINE mali_bool mali_pp_job_should_start_after(struct mali_pp_job *first, struct mali_pp_job *second)
{
MALI_DEBUG_ASSERT_POINTER(first);
MALI_DEBUG_ASSERT_POINTER(second);
/* First job should be started after second job if second job is in progress. */
if (0 < second->sub_jobs_started) {
return MALI_TRUE;
}
/* First job should be started after second job if first job has a higher job id. A span is
used to handle job id wrapping. */
if ((mali_pp_job_get_id(first) - mali_pp_job_get_id(second)) < MALI_SCHEDULER_JOB_ID_SPAN) {
return MALI_TRUE;
}
/* Second job should be started after first job. */
return MALI_FALSE;
}
/**
* Returns MALI_TRUE if this job has more than two sub jobs and all sub jobs are unstarted.
*
* @param job Job to check.
* @return MALI_TRUE if job has more than two sub jobs and all sub jobs are unstarted, MALI_FALSE if not.
*/
MALI_STATIC_INLINE mali_bool mali_pp_job_is_large_and_unstarted(struct mali_pp_job *job)
{
MALI_DEBUG_ASSERT_POINTER(job);
MALI_DEBUG_ASSERT(!mali_pp_job_is_virtual(job));
return (0 == job->sub_jobs_started && 2 < job->sub_jobs_num);
}
/**
* Get PP job's Timeline tracker.
*
* @param job PP job.
* @return Pointer to Timeline tracker for the job.
*/
MALI_STATIC_INLINE struct mali_timeline_tracker *mali_pp_job_get_tracker(struct mali_pp_job *job)
{
MALI_DEBUG_ASSERT_POINTER(job);
return &(job->tracker);
}
#endif /* __MALI_PP_JOB_H__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,130 @@
/*
* Copyright (C) 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.
*/
#ifndef __MALI_PP_SCHEDULER_H__
#define __MALI_PP_SCHEDULER_H__
#include "mali_osk.h"
#include "mali_pp_job.h"
#include "mali_group.h"
#include "linux/mali/mali_utgard.h"
/** Initalize the HW independent parts of the PP scheduler
*/
_mali_osk_errcode_t mali_pp_scheduler_initialize(void);
void mali_pp_scheduler_terminate(void);
/** Poplulate the PP scheduler with groups
*/
void mali_pp_scheduler_populate(void);
void mali_pp_scheduler_depopulate(void);
/**
* @brief Handle job completion.
*
* Will attempt to start a new job on the locked group.
*
* If all sub jobs have completed the job's tracker will be released, any other resources associated
* with the job will be freed. A notification will also be sent to user space.
*
* Releasing the tracker might activate other jobs, so if appropriate we also schedule them.
*
* @note Group must be locked when entering this function. Will be unlocked before exiting.
*
* @param group The group that completed the job.
* @param job The job that is done.
* @param sub_job Sub job of job.
* @param success MALI_TRUE if job completed successfully, MALI_FALSE if not.
* @param in_upper_half MALI_TRUE if called from upper half, MALI_FALSE if not.
*/
void mali_pp_scheduler_job_done(struct mali_group *group, struct mali_pp_job *job, u32 sub_job, mali_bool success, mali_bool in_upper_half);
void mali_pp_scheduler_suspend(void);
void mali_pp_scheduler_resume(void);
/**
* @brief Abort all running and queued PP jobs from session.
*
* This functions aborts all PP jobs from the specified session. Queued jobs are removed from the
* queue and jobs currently running on a core will be aborted.
*
* @param session Session that is aborting.
*/
void mali_pp_scheduler_abort_session(struct mali_session_data *session);
/**
* @brief Reset all groups
*
* This function resets all groups known by the PP scheuduler. This must be
* called after the Mali HW has been powered on in order to reset the HW.
*
* This function is intended for power on reset of all cores.
* No locking is done, which can only be safe if the scheduler is paused and
* all cores idle. That is always the case on init and power on.
*/
void mali_pp_scheduler_reset_all_groups(void);
/**
* @brief Zap TLB on all groups with \a session active
*
* The scheculer will zap the session on all groups it owns.
*/
void mali_pp_scheduler_zap_all_active(struct mali_session_data *session);
/**
* @brief Get the virtual PP core
*
* The returned PP core may only be used to prepare DMA command buffers for the
* PP core. Other actions must go through the PP scheduler, or the virtual
* group.
*
* @return Pointer to the virtual PP core, NULL if this doesn't exist
*/
struct mali_pp_core *mali_pp_scheduler_get_virtual_pp(void);
u32 mali_pp_scheduler_dump_state(char *buf, u32 size);
void mali_pp_scheduler_enable_group(struct mali_group *group);
void mali_pp_scheduler_disable_group(struct mali_group *group);
/**
* @brief Used by the Timeline system to queue a PP job.
*
* @note @ref mali_scheduler_schedule_from_mask() should be called if this function returns non-zero.
*
* @param job The PP job that is being activated.
*
* @return A scheduling bitmask that can be used to decide if scheduling is necessary after this
* call.
*/
mali_scheduler_mask mali_pp_scheduler_activate_job(struct mali_pp_job *job);
/**
* @brief Schedule queued jobs on idle cores.
*/
void mali_pp_scheduler_schedule(void);
int mali_pp_scheduler_set_perf_level(u32 cores, mali_bool override);
void mali_pp_scheduler_core_scaling_enable(void);
void mali_pp_scheduler_core_scaling_disable(void);
mali_bool mali_pp_scheduler_core_scaling_is_enabled(void);
u32 mali_pp_scheduler_get_num_cores_total(void);
u32 mali_pp_scheduler_get_num_cores_enabled(void);
/**
* @brief Returns the number of Pixel Processors in the system irrespective of the context
*
* @return number of physical Pixel Processor cores in the system
*/
u32 mali_pp_scheduler_get_num_cores_total(void);
#endif /* __MALI_PP_SCHEDULER_H__ */

View File

@@ -0,0 +1,112 @@
/*
* Copyright (C) 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.
*/
#include "mali_scheduler.h"
#include "mali_kernel_common.h"
#include "mali_osk.h"
mali_bool mali_scheduler_hints[MALI_SCHEDULER_HINT_MAX];
static _mali_osk_atomic_t mali_job_id_autonumber;
static _mali_osk_atomic_t mali_job_cache_order_autonumber;
static _mali_osk_wq_work_t *pp_scheduler_wq_high_pri = NULL;
static _mali_osk_wq_work_t *gp_scheduler_wq_high_pri = NULL;
static void mali_scheduler_wq_schedule_pp(void *arg)
{
MALI_IGNORE(arg);
mali_pp_scheduler_schedule();
}
static void mali_scheduler_wq_schedule_gp(void *arg)
{
MALI_IGNORE(arg);
mali_gp_scheduler_schedule();
}
_mali_osk_errcode_t mali_scheduler_initialize(void)
{
if ( _MALI_OSK_ERR_OK != _mali_osk_atomic_init(&mali_job_id_autonumber, 0)) {
MALI_DEBUG_PRINT(1, ("Initialization of atomic job id counter failed.\n"));
return _MALI_OSK_ERR_FAULT;
}
if ( _MALI_OSK_ERR_OK != _mali_osk_atomic_init(&mali_job_cache_order_autonumber, 0)) {
MALI_DEBUG_PRINT(1, ("Initialization of atomic job cache order counter failed.\n"));
_mali_osk_atomic_term(&mali_job_id_autonumber);
return _MALI_OSK_ERR_FAULT;
}
pp_scheduler_wq_high_pri = _mali_osk_wq_create_work_high_pri(mali_scheduler_wq_schedule_pp, NULL);
if (NULL == pp_scheduler_wq_high_pri) {
_mali_osk_atomic_term(&mali_job_cache_order_autonumber);
_mali_osk_atomic_term(&mali_job_id_autonumber);
return _MALI_OSK_ERR_NOMEM;
}
gp_scheduler_wq_high_pri = _mali_osk_wq_create_work_high_pri(mali_scheduler_wq_schedule_gp, NULL);
if (NULL == gp_scheduler_wq_high_pri) {
_mali_osk_wq_delete_work(pp_scheduler_wq_high_pri);
_mali_osk_atomic_term(&mali_job_cache_order_autonumber);
_mali_osk_atomic_term(&mali_job_id_autonumber);
return _MALI_OSK_ERR_NOMEM;
}
return _MALI_OSK_ERR_OK;
}
void mali_scheduler_terminate(void)
{
_mali_osk_wq_delete_work(gp_scheduler_wq_high_pri);
_mali_osk_wq_delete_work(pp_scheduler_wq_high_pri);
_mali_osk_atomic_term(&mali_job_cache_order_autonumber);
_mali_osk_atomic_term(&mali_job_id_autonumber);
}
u32 mali_scheduler_get_new_id(void)
{
u32 job_id = _mali_osk_atomic_inc_return(&mali_job_id_autonumber);
return job_id;
}
u32 mali_scheduler_get_new_cache_order(void)
{
u32 job_cache_order = _mali_osk_atomic_inc_return(&mali_job_cache_order_autonumber);
return job_cache_order;
}
void mali_scheduler_schedule_from_mask(mali_scheduler_mask mask, mali_bool deferred_schedule)
{
if (MALI_SCHEDULER_MASK_GP & mask) {
/* GP needs scheduling. */
if (deferred_schedule) {
/* Schedule GP deferred. */
_mali_osk_wq_schedule_work_high_pri(gp_scheduler_wq_high_pri);
} else {
/* Schedule GP now. */
mali_gp_scheduler_schedule();
}
}
if (MALI_SCHEDULER_MASK_PP & mask) {
/* PP needs scheduling. */
if (deferred_schedule) {
/* Schedule PP deferred. */
_mali_osk_wq_schedule_work_high_pri(pp_scheduler_wq_high_pri);
} else {
/* Schedule PP now. */
mali_pp_scheduler_schedule();
}
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright (C) 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.
*/
#ifndef __MALI_SCHEDULER_H__
#define __MALI_SCHEDULER_H__
#include "mali_osk.h"
#include "mali_scheduler_types.h"
#include "mali_gp_scheduler.h"
#include "mali_pp_scheduler.h"
_mali_osk_errcode_t mali_scheduler_initialize(void);
void mali_scheduler_terminate(void);
u32 mali_scheduler_get_new_id(void);
u32 mali_scheduler_get_new_cache_order(void);
/**
* @brief Reset all groups
*
* This function resets all groups known by the both the PP and GP scheuduler.
* This must be called after the Mali HW has been powered on in order to reset
* the HW.
*/
MALI_STATIC_INLINE void mali_scheduler_reset_all_groups(void)
{
mali_gp_scheduler_reset_all_groups();
mali_pp_scheduler_reset_all_groups();
}
/**
* @brief Zap TLB on all active groups running \a session
*
* @param session Pointer to the session to zap
*/
MALI_STATIC_INLINE void mali_scheduler_zap_all_active(struct mali_session_data *session)
{
mali_gp_scheduler_zap_all_active(session);
mali_pp_scheduler_zap_all_active(session);
}
/**
* Check if bit is set in scheduler mask.
*
* @param mask Scheduler mask to check.
* @param bit Bit to check.
* @return MALI_TRUE if bit is set in scheduler mask, MALI_FALSE if not.
*/
MALI_STATIC_INLINE mali_bool mali_scheduler_mask_is_set(mali_scheduler_mask mask, mali_scheduler_mask bit)
{
return MALI_SCHEDULER_MASK_EMPTY != (bit & mask);
}
/**
* Schedule GP and PP according to bitmask.
*
* @param mask A scheduling bitmask.
* @param deferred_schedule MALI_TRUE if schedule should be deferred, MALI_FALSE if not.
*/
void mali_scheduler_schedule_from_mask(mali_scheduler_mask mask, mali_bool deferred_schedule);
/* Enable or disable scheduler hint. */
extern mali_bool mali_scheduler_hints[MALI_SCHEDULER_HINT_MAX];
MALI_STATIC_INLINE void mali_scheduler_hint_enable(mali_scheduler_hint hint)
{
MALI_DEBUG_ASSERT(hint < MALI_SCHEDULER_HINT_MAX);
mali_scheduler_hints[hint] = MALI_TRUE;
}
MALI_STATIC_INLINE void mali_scheduler_hint_disable(mali_scheduler_hint hint)
{
MALI_DEBUG_ASSERT(hint < MALI_SCHEDULER_HINT_MAX);
mali_scheduler_hints[hint] = MALI_FALSE;
}
MALI_STATIC_INLINE mali_bool mali_scheduler_hint_is_enabled(mali_scheduler_hint hint)
{
MALI_DEBUG_ASSERT(hint < MALI_SCHEDULER_HINT_MAX);
return mali_scheduler_hints[hint];
}
#endif /* __MALI_SCHEDULER_H__ */

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 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.
*/
#ifndef __MALI_SCHEDULER_TYPES_H__
#define __MALI_SCHEDULER_TYPES_H__
#include "mali_osk.h"
#define MALI_SCHEDULER_JOB_ID_SPAN 65535
/**
* Bitmask used for defered scheduling of subsystems.
*/
typedef u32 mali_scheduler_mask;
#define MALI_SCHEDULER_MASK_GP (1<<0)
#define MALI_SCHEDULER_MASK_PP (1<<1)
#define MALI_SCHEDULER_MASK_EMPTY 0
#define MALI_SCHEDULER_MASK_ALL (MALI_SCHEDULER_MASK_GP | MALI_SCHEDULER_MASK_PP)
typedef enum {
MALI_SCHEDULER_HINT_GP_BOUND = 0
#define MALI_SCHEDULER_HINT_MAX 1
} mali_scheduler_hint;
#endif /* __MALI_SCHEDULER_TYPES_H__ */

View File

@@ -0,0 +1,81 @@
/*
* Copyright (C) 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.
*/
#include "mali_osk.h"
#include "mali_osk_list.h"
#include "mali_session.h"
_MALI_OSK_LIST_HEAD(mali_sessions);
static u32 mali_session_count = 0;
_mali_osk_spinlock_irq_t *mali_sessions_lock;
_mali_osk_errcode_t mali_session_initialize(void)
{
_MALI_OSK_INIT_LIST_HEAD(&mali_sessions);
mali_sessions_lock = _mali_osk_spinlock_irq_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_SESSIONS);
if (NULL == mali_sessions_lock) return _MALI_OSK_ERR_NOMEM;
return _MALI_OSK_ERR_OK;
}
void mali_session_terminate(void)
{
_mali_osk_spinlock_irq_term(mali_sessions_lock);
}
void mali_session_add(struct mali_session_data *session)
{
mali_session_lock();
_mali_osk_list_add(&session->link, &mali_sessions);
mali_session_count++;
mali_session_unlock();
}
void mali_session_remove(struct mali_session_data *session)
{
mali_session_lock();
_mali_osk_list_delinit(&session->link);
mali_session_count--;
mali_session_unlock();
}
u32 mali_session_get_count(void)
{
return mali_session_count;
}
/*
* Get the max completed window jobs from all active session,
* which will be used in window render frame per sec calculate
*/
#if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY)
u32 mali_session_max_window_num(void)
{
struct mali_session_data *session, *tmp;
u32 max_window_num = 0;
u32 tmp_number = 0;
mali_session_lock();
MALI_SESSION_FOREACH(session, tmp, link) {
tmp_number = _mali_osk_atomic_xchg(&session->number_of_window_jobs, 0);
if (max_window_num < tmp_number) {
max_window_num = tmp_number;
}
}
mali_session_unlock();
return max_window_num;
}
#endif

View File

@@ -0,0 +1,94 @@
/*
* Copyright (C) 2010-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.
*/
#ifndef __MALI_SESSION_H__
#define __MALI_SESSION_H__
#include "mali_mmu_page_directory.h"
#include "mali_kernel_descriptor_mapping.h"
#include "mali_osk.h"
#include "mali_osk_list.h"
struct mali_timeline_system;
struct mali_soft_system;
/* Number of frame builder job lists per session. */
#define MALI_PP_JOB_FB_LOOKUP_LIST_SIZE 16
#define MALI_PP_JOB_FB_LOOKUP_LIST_MASK (MALI_PP_JOB_FB_LOOKUP_LIST_SIZE - 1)
struct mali_session_data {
_mali_osk_notification_queue_t * ioctl_queue;
_mali_osk_mutex_t *memory_lock; /**< Lock protecting the vm manipulation */
mali_descriptor_mapping * descriptor_mapping; /**< Mapping between userspace descriptors and our pointers */
_mali_osk_list_t memory_head; /**< Track all the memory allocated in this session, for freeing on abnormal termination */
struct mali_page_directory *page_directory; /**< MMU page directory for this session */
_MALI_OSK_LIST_HEAD(link); /**< Link for list of all sessions */
_MALI_OSK_LIST_HEAD(pp_job_list); /**< List of all PP jobs on this session */
#if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY)
_mali_osk_atomic_t number_of_window_jobs; /**< Record the window jobs completed on this session in a period */
#endif
_mali_osk_list_t pp_job_fb_lookup_list[MALI_PP_JOB_FB_LOOKUP_LIST_SIZE]; /**< List of PP job lists per frame builder id. Used to link jobs from same frame builder. */
struct mali_soft_job_system *soft_job_system; /**< Soft job system for this session. */
struct mali_timeline_system *timeline_system; /**< Timeline system for this session. */
mali_bool is_aborting; /**< MALI_TRUE if the session is aborting, MALI_FALSE if not. */
mali_bool use_high_priority_job_queue; /**< If MALI_TRUE, jobs added from this session will use the high priority job queues. */
};
_mali_osk_errcode_t mali_session_initialize(void);
void mali_session_terminate(void);
/* List of all sessions. Actual list head in mali_kernel_core.c */
extern _mali_osk_list_t mali_sessions;
/* Lock to protect modification and access to the mali_sessions list */
extern _mali_osk_spinlock_irq_t *mali_sessions_lock;
MALI_STATIC_INLINE void mali_session_lock(void)
{
_mali_osk_spinlock_irq_lock(mali_sessions_lock);
}
MALI_STATIC_INLINE void mali_session_unlock(void)
{
_mali_osk_spinlock_irq_unlock(mali_sessions_lock);
}
void mali_session_add(struct mali_session_data *session);
void mali_session_remove(struct mali_session_data *session);
u32 mali_session_get_count(void);
#define MALI_SESSION_FOREACH(session, tmp, link) \
_MALI_OSK_LIST_FOREACHENTRY(session, tmp, &mali_sessions, struct mali_session_data, link)
MALI_STATIC_INLINE struct mali_page_directory *mali_session_get_page_directory(struct mali_session_data *session)
{
return session->page_directory;
}
MALI_STATIC_INLINE void mali_session_send_notification(struct mali_session_data *session, _mali_osk_notification_t *object)
{
_mali_osk_notification_queue_send(session->ioctl_queue, object);
}
/*
* Get the max completed window jobs from all active session,
* which will be used in window render frame per sec calculate
*/
#if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY)
u32 mali_session_max_window_num(void);
#endif
#endif /* __MALI_SESSION_H__ */

View File

@@ -0,0 +1,464 @@
/*
* Copyright (C) 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.
*/
#include "mali_soft_job.h"
#include "mali_osk.h"
#include "mali_osk_mali.h"
#include "mali_timeline.h"
#include "mali_session.h"
#include "mali_kernel_common.h"
#include "mali_uk_types.h"
#include "mali_scheduler.h"
MALI_STATIC_INLINE void mali_soft_job_system_lock(struct mali_soft_job_system *system)
{
MALI_DEBUG_ASSERT_POINTER(system);
_mali_osk_spinlock_irq_lock(system->lock);
MALI_DEBUG_PRINT(5, ("Mali Soft Job: soft system %p lock taken\n", system));
MALI_DEBUG_ASSERT(0 == system->lock_owner);
MALI_DEBUG_CODE(system->lock_owner = _mali_osk_get_tid());
}
MALI_STATIC_INLINE void mali_soft_job_system_unlock(struct mali_soft_job_system *system)
{
MALI_DEBUG_ASSERT_POINTER(system);
MALI_DEBUG_PRINT(5, ("Mali Soft Job: releasing soft system %p lock\n", system));
MALI_DEBUG_ASSERT(_mali_osk_get_tid() == system->lock_owner);
MALI_DEBUG_CODE(system->lock_owner = 0);
_mali_osk_spinlock_irq_unlock(system->lock);
}
#if defined(DEBUG)
MALI_STATIC_INLINE void mali_soft_job_system_assert_locked(struct mali_soft_job_system *system)
{
MALI_DEBUG_ASSERT_POINTER(system);
MALI_DEBUG_ASSERT(_mali_osk_get_tid() == system->lock_owner);
}
#define MALI_ASSERT_SOFT_JOB_SYSTEM_LOCKED(system) mali_soft_job_system_assert_locked(system)
#else
#define MALI_ASSERT_SOFT_JOB_SYSTEM_LOCKED(system)
#endif /* defined(DEBUG) */
struct mali_soft_job_system *mali_soft_job_system_create(struct mali_session_data *session)
{
u32 i;
struct mali_soft_job_system *system;
struct mali_soft_job *job;
MALI_DEBUG_ASSERT_POINTER(session);
system = (struct mali_soft_job_system *) _mali_osk_calloc(1, sizeof(struct mali_soft_job_system));
if (NULL == system) {
return NULL;
}
system->session = session;
system->lock = _mali_osk_spinlock_irq_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_SCHEDULER);
if (NULL == system->lock) {
mali_soft_job_system_destroy(system);
return NULL;
}
system->lock_owner = 0;
_MALI_OSK_INIT_LIST_HEAD(&(system->jobs_free));
_MALI_OSK_INIT_LIST_HEAD(&(system->jobs_used));
for (i = 0; i < MALI_MAX_NUM_SOFT_JOBS; ++i) {
job = &(system->jobs[i]);
_mali_osk_list_add(&(job->system_list), &(system->jobs_free));
job->system = system;
job->state = MALI_SOFT_JOB_STATE_FREE;
job->id = i;
}
return system;
}
void mali_soft_job_system_destroy(struct mali_soft_job_system *system)
{
MALI_DEBUG_ASSERT_POINTER(system);
/* All jobs should be free at this point. */
MALI_DEBUG_CODE( {
u32 i;
struct mali_soft_job *job;
for (i = 0; i < MALI_MAX_NUM_SOFT_JOBS; ++i)
{
job = &(system->jobs[i]);
MALI_DEBUG_ASSERT(MALI_SOFT_JOB_STATE_FREE == job->state);
}
});
if (NULL != system) {
if (NULL != system->lock) {
_mali_osk_spinlock_irq_term(system->lock);
}
_mali_osk_free(system);
}
}
static struct mali_soft_job *mali_soft_job_system_alloc_job(struct mali_soft_job_system *system)
{
struct mali_soft_job *job;
MALI_DEBUG_ASSERT_POINTER(system);
MALI_ASSERT_SOFT_JOB_SYSTEM_LOCKED(system);
if (_mali_osk_list_empty(&(system->jobs_free))) {
/* No jobs available. */
return NULL;
}
/* Grab first job and move it to the used list. */
job = _MALI_OSK_LIST_ENTRY(system->jobs_free.next, struct mali_soft_job, system_list);
MALI_DEBUG_ASSERT(MALI_SOFT_JOB_STATE_FREE == job->state);
_mali_osk_list_move(&(job->system_list), &(system->jobs_used));
job->state = MALI_SOFT_JOB_STATE_ALLOCATED;
MALI_DEBUG_ASSERT(MALI_SOFT_JOB_INVALID_ID != job->id);
MALI_DEBUG_ASSERT(system == job->system);
return job;
}
static void mali_soft_job_system_free_job(struct mali_soft_job_system *system, struct mali_soft_job *job)
{
MALI_DEBUG_ASSERT_POINTER(job);
MALI_DEBUG_ASSERT_POINTER(system);
mali_soft_job_system_lock(job->system);
MALI_DEBUG_ASSERT(MALI_SOFT_JOB_STATE_FREE != job->state);
MALI_DEBUG_ASSERT(MALI_SOFT_JOB_INVALID_ID != job->id);
MALI_DEBUG_ASSERT(system == job->system);
job->state = MALI_SOFT_JOB_STATE_FREE;
_mali_osk_list_move(&(job->system_list), &(system->jobs_free));
mali_soft_job_system_unlock(job->system);
}
MALI_STATIC_INLINE struct mali_soft_job *mali_soft_job_system_lookup_job(struct mali_soft_job_system *system, u32 job_id)
{
MALI_DEBUG_ASSERT_POINTER(system);
MALI_ASSERT_SOFT_JOB_SYSTEM_LOCKED(system);
if (job_id < MALI_MAX_NUM_SOFT_JOBS) {
return &system->jobs[job_id];
}
return NULL;
}
void mali_soft_job_destroy(struct mali_soft_job *job)
{
MALI_DEBUG_ASSERT_POINTER(job);
MALI_DEBUG_ASSERT_POINTER(job->system);
MALI_DEBUG_PRINT(4, ("Mali Soft Job: destroying soft job %u (0x%08X)\n", job->id, job));
if (NULL != job) {
if (0 < _mali_osk_atomic_dec_return(&job->refcount)) return;
_mali_osk_atomic_term(&job->refcount);
if (NULL != job->activated_notification) {
_mali_osk_notification_delete(job->activated_notification);
job->activated_notification = NULL;
}
mali_soft_job_system_free_job(job->system, job);
}
}
struct mali_soft_job *mali_soft_job_create(struct mali_soft_job_system *system, mali_soft_job_type type, u32 user_job)
{
struct mali_soft_job *job;
_mali_osk_notification_t *notification = NULL;
MALI_DEBUG_ASSERT_POINTER(system);
MALI_DEBUG_ASSERT(MALI_SOFT_JOB_TYPE_USER_SIGNALED >= type);
if (MALI_SOFT_JOB_TYPE_USER_SIGNALED == type) {
notification = _mali_osk_notification_create(_MALI_NOTIFICATION_SOFT_ACTIVATED, sizeof(_mali_uk_soft_job_activated_s));
if (unlikely(NULL == notification)) {
MALI_PRINT_ERROR(("Mali Soft Job: failed to allocate notification"));
return NULL;
}
}
mali_soft_job_system_lock(system);
job = mali_soft_job_system_alloc_job(system);
if (NULL == job) {
mali_soft_job_system_unlock(system);
MALI_PRINT_ERROR(("Mali Soft Job: failed to allocate job"));
_mali_osk_notification_delete(notification);
return NULL;
}
job->type = type;
job->user_job = user_job;
job->activated = MALI_FALSE;
if (MALI_SOFT_JOB_TYPE_USER_SIGNALED == type) {
job->activated_notification = notification;
}
_mali_osk_atomic_init(&job->refcount, 1);
MALI_DEBUG_ASSERT(MALI_SOFT_JOB_STATE_ALLOCATED == job->state);
MALI_DEBUG_ASSERT(system == job->system);
MALI_DEBUG_ASSERT(MALI_SOFT_JOB_INVALID_ID != job->id);
mali_soft_job_system_unlock(system);
return job;
}
mali_timeline_point mali_soft_job_start(struct mali_soft_job *job, struct mali_timeline_fence *fence)
{
mali_timeline_point point;
struct mali_soft_job_system *system;
MALI_DEBUG_ASSERT_POINTER(job);
MALI_DEBUG_ASSERT_POINTER(fence);
MALI_DEBUG_ASSERT_POINTER(job->system);
system = job->system;
MALI_DEBUG_ASSERT_POINTER(system->session);
MALI_DEBUG_ASSERT_POINTER(system->session->timeline_system);
mali_soft_job_system_lock(system);
MALI_DEBUG_ASSERT(MALI_SOFT_JOB_STATE_ALLOCATED == job->state);
job->state = MALI_SOFT_JOB_STATE_STARTED;
mali_soft_job_system_unlock(system);
MALI_DEBUG_PRINT(4, ("Mali Soft Job: starting soft job %u (0x%08X)\n", job->id, job));
mali_timeline_tracker_init(&job->tracker, MALI_TIMELINE_TRACKER_SOFT, fence, job);
point = mali_timeline_system_add_tracker(system->session->timeline_system, &job->tracker, MALI_TIMELINE_SOFT);
return point;
}
static mali_bool mali_soft_job_is_activated(void *data)
{
struct mali_soft_job *job;
job = (struct mali_soft_job *) data;
MALI_DEBUG_ASSERT_POINTER(job);
return job->activated;
}
_mali_osk_errcode_t mali_soft_job_system_signal_job(struct mali_soft_job_system *system, u32 job_id)
{
struct mali_soft_job *job;
struct mali_timeline_system *timeline_system;
mali_scheduler_mask schedule_mask;
MALI_DEBUG_ASSERT_POINTER(system);
mali_soft_job_system_lock(system);
job = mali_soft_job_system_lookup_job(system, job_id);
if (NULL == job || !(MALI_SOFT_JOB_STATE_STARTED == job->state || MALI_SOFT_JOB_STATE_TIMED_OUT == job->state)) {
mali_soft_job_system_unlock(system);
MALI_PRINT_ERROR(("Mali Soft Job: invalid soft job id %u", job_id));
return _MALI_OSK_ERR_ITEM_NOT_FOUND;
}
if (MALI_SOFT_JOB_STATE_TIMED_OUT == job->state) {
job->state = MALI_SOFT_JOB_STATE_SIGNALED;
mali_soft_job_system_unlock(system);
MALI_DEBUG_ASSERT(MALI_TRUE == job->activated);
MALI_DEBUG_PRINT(4, ("Mali Soft Job: soft job %u (0x%08X) was timed out\n", job->id, job));
mali_soft_job_destroy(job);
return _MALI_OSK_ERR_TIMEOUT;
}
MALI_DEBUG_ASSERT(MALI_SOFT_JOB_STATE_STARTED == job->state);
job->state = MALI_SOFT_JOB_STATE_SIGNALED;
mali_soft_job_system_unlock(system);
/* Since the job now is in signaled state, timeouts from the timeline system will be
* ignored, and it is not possible to signal this job again. */
timeline_system = system->session->timeline_system;
MALI_DEBUG_ASSERT_POINTER(timeline_system);
/* Wait until activated. */
_mali_osk_wait_queue_wait_event(timeline_system->wait_queue, mali_soft_job_is_activated, (void *) job);
MALI_DEBUG_PRINT(4, ("Mali Soft Job: signaling soft job %u (0x%08X)\n", job->id, job));
schedule_mask = mali_timeline_tracker_release(&job->tracker);
mali_scheduler_schedule_from_mask(schedule_mask, MALI_FALSE);
mali_soft_job_destroy(job);
return _MALI_OSK_ERR_OK;
}
static void mali_soft_job_send_activated_notification(struct mali_soft_job *job)
{
if (NULL != job->activated_notification) {
_mali_uk_soft_job_activated_s *res = job->activated_notification->result_buffer;
res->user_job = job->user_job;
mali_session_send_notification(job->system->session, job->activated_notification);
}
job->activated_notification = NULL;
}
void mali_soft_job_system_activate_job(struct mali_soft_job *job)
{
MALI_DEBUG_ASSERT_POINTER(job);
MALI_DEBUG_ASSERT_POINTER(job->system);
MALI_DEBUG_ASSERT_POINTER(job->system->session);
MALI_DEBUG_PRINT(4, ("Mali Soft Job: Timeline activation for soft job %u (0x%08X).\n", job->id, job));
mali_soft_job_system_lock(job->system);
if (unlikely(job->system->session->is_aborting)) {
MALI_DEBUG_PRINT(3, ("Mali Soft Job: Soft job %u (0x%08X) activated while session is aborting.\n", job->id, job));
mali_soft_job_system_unlock(job->system);
/* Since we are in shutdown, we can ignore the scheduling bitmask. */
mali_timeline_tracker_release(&job->tracker);
mali_soft_job_destroy(job);
return;
}
/* Send activated notification. */
mali_soft_job_send_activated_notification(job);
/* Wake up sleeping signaler. */
job->activated = MALI_TRUE;
_mali_osk_wait_queue_wake_up(job->tracker.system->wait_queue);
mali_soft_job_system_unlock(job->system);
}
mali_scheduler_mask mali_soft_job_system_timeout_job(struct mali_soft_job *job)
{
mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY;
MALI_DEBUG_ASSERT_POINTER(job);
MALI_DEBUG_ASSERT_POINTER(job->system);
MALI_DEBUG_ASSERT_POINTER(job->system->session);
MALI_DEBUG_ASSERT(MALI_TRUE == job->activated);
MALI_DEBUG_PRINT(4, ("Mali Soft Job: Timeline timeout for soft job %u (0x%08X).\n", job->id, job));
mali_soft_job_system_lock(job->system);
MALI_DEBUG_ASSERT(MALI_SOFT_JOB_STATE_STARTED == job->state ||
MALI_SOFT_JOB_STATE_SIGNALED == job->state);
if (unlikely(job->system->session->is_aborting)) {
/* The session is aborting. This job will be released and destroyed by @ref
* mali_soft_job_system_abort(). */
mali_soft_job_system_unlock(job->system);
return MALI_SCHEDULER_MASK_EMPTY;
}
if (MALI_SOFT_JOB_STATE_STARTED != job->state) {
MALI_DEBUG_ASSERT(MALI_SOFT_JOB_STATE_SIGNALED == job->state);
/* The job is about to be signaled, ignore timeout. */
MALI_DEBUG_PRINT(4, ("Mali Soft Job: Timeout on soft job %u (0x%08X) in signaled state.\n", job->id, job));
mali_soft_job_system_unlock(job->system);
return schedule_mask;
}
MALI_DEBUG_ASSERT(MALI_SOFT_JOB_STATE_STARTED == job->state);
job->state = MALI_SOFT_JOB_STATE_TIMED_OUT;
_mali_osk_atomic_inc(&job->refcount);
mali_soft_job_system_unlock(job->system);
schedule_mask = mali_timeline_tracker_release(&job->tracker);
mali_soft_job_destroy(job);
return schedule_mask;
}
void mali_soft_job_system_abort(struct mali_soft_job_system *system)
{
u32 i;
struct mali_soft_job *job, *tmp;
_MALI_OSK_LIST_HEAD_STATIC_INIT(jobs);
MALI_DEBUG_ASSERT_POINTER(system);
MALI_DEBUG_ASSERT_POINTER(system->session);
MALI_DEBUG_ASSERT(system->session->is_aborting);
MALI_DEBUG_PRINT(3, ("Mali Soft Job: Aborting soft job system for session 0x%08X.\n", system->session));
mali_soft_job_system_lock(system);
for (i = 0; i < MALI_MAX_NUM_SOFT_JOBS; ++i) {
job = &(system->jobs[i]);
MALI_DEBUG_ASSERT(MALI_SOFT_JOB_STATE_FREE == job->state ||
MALI_SOFT_JOB_STATE_STARTED == job->state ||
MALI_SOFT_JOB_STATE_TIMED_OUT == job->state);
if (MALI_SOFT_JOB_STATE_STARTED == job->state) {
/* If the job has been activated, we have to release the tracker and destroy
* the job. If not, the tracker will be released and the job destroyed when
* it is activated. */
if (MALI_TRUE == job->activated) {
MALI_DEBUG_PRINT(3, ("Mali Soft Job: Aborting unsignaled soft job %u (0x%08X).\n", job->id, job));
job->state = MALI_SOFT_JOB_STATE_SIGNALED;
_mali_osk_list_move(&job->system_list, &jobs);
}
} else if (MALI_SOFT_JOB_STATE_TIMED_OUT == job->state) {
MALI_DEBUG_PRINT(3, ("Mali Soft Job: Aborting timed out soft job %u (0x%08X).\n", job->id, job));
/* We need to destroy this soft job. */
_mali_osk_list_move(&job->system_list, &jobs);
}
}
mali_soft_job_system_unlock(system);
/* Release and destroy jobs. */
_MALI_OSK_LIST_FOREACHENTRY(job, tmp, &jobs, struct mali_soft_job, system_list) {
MALI_DEBUG_ASSERT(MALI_SOFT_JOB_STATE_SIGNALED == job->state ||
MALI_SOFT_JOB_STATE_TIMED_OUT == job->state);
if (MALI_SOFT_JOB_STATE_SIGNALED == job->state) {
mali_timeline_tracker_release(&job->tracker);
}
/* Move job back to used list before destroying. */
_mali_osk_list_move(&job->system_list, &system->jobs_used);
mali_soft_job_destroy(job);
}
}

View File

@@ -0,0 +1,196 @@
/*
* Copyright (C) 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.
*/
#ifndef __MALI_SOFT_JOB_H__
#define __MALI_SOFT_JOB_H__
#include "mali_osk.h"
#include "mali_timeline.h"
struct mali_timeline_fence;
struct mali_session_data;
struct mali_soft_job;
struct mali_soft_job_system;
/**
* Soft job types.
*
* Soft jobs of type MALI_SOFT_JOB_TYPE_USER_SIGNALED will only complete after activation if either
* they are signaled by user-space (@ref mali_soft_job_system_signaled_job) or if they are timed out
* by the Timeline system.
*/
typedef enum mali_soft_job_type {
MALI_SOFT_JOB_TYPE_USER_SIGNALED,
} mali_soft_job_type;
/**
* Soft job state.
*
* All soft jobs in a soft job system will initially be in state MALI_SOFT_JOB_STATE_FREE. On @ref
* mali_soft_job_system_start_job a job will first be allocated. A job in state
* MALI_SOFT_JOB_STATE_FREE will be picked and the state changed to MALI_SOFT_JOB_STATE_ALLOCATED.
* Once the job is added to the timeline system, the state changes to MALI_SOFT_JOB_STATE_STARTED.
*
* For soft jobs of type MALI_SOFT_JOB_TYPE_USER_SIGNALED the state is changed to
* MALI_SOFT_JOB_STATE_SIGNALED when @ref mali_soft_job_system_signal_job is called and the soft
* job's state is MALI_SOFT_JOB_STATE_STARTED or MALI_SOFT_JOB_STATE_TIMED_OUT.
*
* If a soft job of type MALI_SOFT_JOB_TYPE_USER_SIGNALED is timed out before being signaled, the
* state is changed to MALI_SOFT_JOB_STATE_TIMED_OUT. This can only happen to soft jobs in state
* MALI_SOFT_JOB_STATE_STARTED.
*
* When a soft job's reference count reaches zero, it will be freed and the state returns to
* MALI_SOFT_JOB_STATE_FREE.
*/
typedef enum mali_soft_job_state {
MALI_SOFT_JOB_STATE_FREE,
MALI_SOFT_JOB_STATE_ALLOCATED,
MALI_SOFT_JOB_STATE_STARTED,
MALI_SOFT_JOB_STATE_SIGNALED,
MALI_SOFT_JOB_STATE_TIMED_OUT,
} mali_soft_job_state;
#define MALI_SOFT_JOB_INVALID_ID ((u32) -1)
/* Maximum number of soft jobs per soft system. */
#define MALI_MAX_NUM_SOFT_JOBS 20
/**
* Soft job struct.
*
* Soft job can be used to represent any kind of CPU work done in kernel-space.
*/
typedef struct mali_soft_job {
mali_soft_job_type type; /**< Soft job type. Must be one of MALI_SOFT_JOB_TYPE_*. */
u32 user_job; /**< Identifier for soft job in user space. */
_mali_osk_atomic_t refcount; /**< Soft jobs are reference counted to prevent premature deletion. */
struct mali_timeline_tracker tracker; /**< Timeline tracker for soft job. */
mali_bool activated; /**< MALI_TRUE if the job has been activated, MALI_FALSE if not. */
_mali_osk_notification_t *activated_notification; /**< Pre-allocated notification object for ACTIVATED_NOTIFICATION. */
/* Protected by soft job system lock. */
u32 id; /**< Used by user-space to find corresponding soft job in kernel-space. */
mali_soft_job_state state; /**< State of soft job, must be one of MALI_SOFT_JOB_STATE_*. */
struct mali_soft_job_system *system; /**< The soft job system this job is in. */
_mali_osk_list_t system_list; /**< List element used by soft job system. */
} mali_soft_job;
/**
* Per-session soft job system.
*
* The soft job system is used to manage all soft jobs that belongs to a session.
*/
typedef struct mali_soft_job_system {
struct mali_session_data *session; /**< The session this soft job system belongs to. */
struct mali_soft_job jobs[MALI_MAX_NUM_SOFT_JOBS]; /**< Array of all soft jobs in this system. */
_MALI_OSK_LIST_HEAD(jobs_free); /**< List of all free soft jobs. */
_MALI_OSK_LIST_HEAD(jobs_used); /**< List of all allocated soft jobs. */
_mali_osk_spinlock_irq_t *lock; /**< Lock used to protect soft job system and its soft jobs. */
u32 lock_owner; /**< Contains tid of thread that locked the system or 0, if not locked. */
} mali_soft_job_system;
/**
* Create a soft job system.
*
* @param session The session this soft job system will belong to.
* @return The new soft job system, or NULL if unsuccessful.
*/
struct mali_soft_job_system *mali_soft_job_system_create(struct mali_session_data *session);
/**
* Destroy a soft job system.
*
* @note The soft job must not have any started or activated jobs. Call @ref
* mali_soft_job_system_abort first.
*
* @param system The soft job system we are destroying.
*/
void mali_soft_job_system_destroy(struct mali_soft_job_system *system);
/**
* Create a soft job.
*
* @param system Soft job system to create soft job from.
* @param type Type of the soft job.
* @param user_job Identifier for soft job in user space.
* @return New soft job if successful, NULL if not.
*/
struct mali_soft_job *mali_soft_job_create(struct mali_soft_job_system *system, mali_soft_job_type type, u32 user_job);
/**
* Destroy soft job.
*
* @param job Soft job to destroy.
*/
void mali_soft_job_destroy(struct mali_soft_job *job);
/**
* Start a soft job.
*
* The soft job will be added to the Timeline system which will then activate it after all
* dependencies have been resolved.
*
* Create soft jobs with @ref mali_soft_job_create before starting them.
*
* @param job Soft job to start.
* @param fence Fence representing dependencies for this soft job.
* @return Point on soft job timeline.
*/
mali_timeline_point mali_soft_job_start(struct mali_soft_job *job, struct mali_timeline_fence *fence);
/**
* Use by user-space to signal that a soft job has completed.
*
* @note Only valid for soft jobs with type MALI_SOFT_JOB_TYPE_USER_SIGNALED.
*
* @note The soft job must be in state MALI_SOFT_JOB_STATE_STARTED for the signal to be successful.
*
* @note If the soft job was signaled successfully, or it received a time out, the soft job will be
* destroyed after this call and should no longer be used.
*
* @note This function will block until the soft job has been activated.
*
* @param system The soft job system the job was started in.
* @param job_id ID of soft job we are signaling.
*
* @return _MALI_OSK_ERR_ITEM_NOT_FOUND if the soft job ID was invalid, _MALI_OSK_ERR_TIMEOUT if the
* soft job was timed out or _MALI_OSK_ERR_OK if we successfully signaled the soft job.
*/
_mali_osk_errcode_t mali_soft_job_system_signal_job(struct mali_soft_job_system *system, u32 job_id);
/**
* Used by the Timeline system to activate a soft job.
*
* @param job The soft job that is being activated.
*/
void mali_soft_job_system_activate_job(struct mali_soft_job *job);
/**
* Used by the Timeline system to timeout a soft job.
*
* A soft job is timed out if it completes or is signaled later than MALI_TIMELINE_TIMEOUT_HZ after
* activation.
*
* @param job The soft job that is being timed out.
* @return A scheduling bitmask.
*/
mali_scheduler_mask mali_soft_job_system_timeout_job(struct mali_soft_job *job);
/**
* Used to cleanup activated soft jobs in the soft job system on session abort.
*
* @param system The soft job system that is being aborted.
*/
void mali_soft_job_system_abort(struct mali_soft_job_system *system);
#endif /* __MALI_SOFT_JOB_H__ */

View File

@@ -0,0 +1,77 @@
/*
* Copyright (C) 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.
*/
#include "mali_spinlock_reentrant.h"
#include "mali_osk.h"
#include "mali_kernel_common.h"
struct mali_spinlock_reentrant *mali_spinlock_reentrant_init(_mali_osk_lock_order_t lock_order)
{
struct mali_spinlock_reentrant *spinlock;
spinlock = _mali_osk_calloc(1, sizeof(struct mali_spinlock_reentrant));
if (NULL == spinlock) {
return NULL;
}
spinlock->lock = _mali_osk_spinlock_irq_init(_MALI_OSK_LOCKFLAG_ORDERED, lock_order);
if (NULL == spinlock->lock) {
mali_spinlock_reentrant_term(spinlock);
return NULL;
}
return spinlock;
}
void mali_spinlock_reentrant_term(struct mali_spinlock_reentrant *spinlock)
{
MALI_DEBUG_ASSERT_POINTER(spinlock);
MALI_DEBUG_ASSERT(0 == spinlock->counter && 0 == spinlock->owner);
if (NULL != spinlock->lock) {
_mali_osk_spinlock_irq_term(spinlock->lock);
}
_mali_osk_free(spinlock);
}
void mali_spinlock_reentrant_wait(struct mali_spinlock_reentrant *spinlock, u32 tid)
{
MALI_DEBUG_ASSERT_POINTER(spinlock);
MALI_DEBUG_ASSERT_POINTER(spinlock->lock);
MALI_DEBUG_ASSERT(0 != tid);
MALI_DEBUG_PRINT(5, ("%s ^\n", __FUNCTION__));
if (tid != spinlock->owner) {
_mali_osk_spinlock_irq_lock(spinlock->lock);
MALI_DEBUG_ASSERT(0 == spinlock->owner && 0 == spinlock->counter);
spinlock->owner = tid;
}
MALI_DEBUG_PRINT(5, ("%s v\n", __FUNCTION__));
++spinlock->counter;
}
void mali_spinlock_reentrant_signal(struct mali_spinlock_reentrant *spinlock, u32 tid)
{
MALI_DEBUG_ASSERT_POINTER(spinlock);
MALI_DEBUG_ASSERT_POINTER(spinlock->lock);
MALI_DEBUG_ASSERT(0 != tid && tid == spinlock->owner);
--spinlock->counter;
if (0 == spinlock->counter) {
spinlock->owner = 0;
MALI_DEBUG_PRINT(5, ("%s release last\n", __FUNCTION__));
_mali_osk_spinlock_irq_unlock(spinlock->lock);
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (C) 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.
*/
#ifndef __MALI_SPINLOCK_REENTRANT_H__
#define __MALI_SPINLOCK_REENTRANT_H__
#include "mali_osk.h"
#include "mali_kernel_common.h"
/**
* Reentrant spinlock.
*/
struct mali_spinlock_reentrant {
_mali_osk_spinlock_irq_t *lock;
u32 owner;
u32 counter;
};
/**
* Create a new reentrant spinlock.
*
* @param lock_order Lock order.
* @return New reentrant spinlock.
*/
struct mali_spinlock_reentrant *mali_spinlock_reentrant_init(_mali_osk_lock_order_t lock_order);
/**
* Terminate reentrant spinlock and free any associated resources.
*
* @param spinlock Reentrant spinlock to terminate.
*/
void mali_spinlock_reentrant_term(struct mali_spinlock_reentrant *spinlock);
/**
* Wait for reentrant spinlock to be signaled.
*
* @param spinlock Reentrant spinlock.
* @param tid Thread ID.
*/
void mali_spinlock_reentrant_wait(struct mali_spinlock_reentrant *spinlock, u32 tid);
/**
* Signal reentrant spinlock.
*
* @param spinlock Reentrant spinlock.
* @param tid Thread ID.
*/
void mali_spinlock_reentrant_signal(struct mali_spinlock_reentrant *spinlock, u32 tid);
/**
* Check if thread is holding reentrant spinlock.
*
* @param spinlock Reentrant spinlock.
* @param tid Thread ID.
* @return MALI_TRUE if thread is holding spinlock, MALI_FALSE if not.
*/
MALI_STATIC_INLINE mali_bool mali_spinlock_reentrant_is_held(struct mali_spinlock_reentrant *spinlock, u32 tid)
{
MALI_DEBUG_ASSERT_POINTER(spinlock->lock);
return (tid == spinlock->owner && 0 < spinlock->counter);
}
#endif /* __MALI_SPINLOCK_REENTRANT_H__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,494 @@
/*
* Copyright (C) 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.
*/
#ifndef __MALI_TIMELINE_H__
#define __MALI_TIMELINE_H__
#include "mali_osk.h"
#include "mali_ukk.h"
#include "mali_session.h"
#include "mali_kernel_common.h"
#include "mali_spinlock_reentrant.h"
#include "mali_sync.h"
#include "mali_scheduler_types.h"
/**
* Soft job timeout.
*
* Soft jobs have to be signaled as complete after activation. Normally this is done by user space,
* but in order to guarantee that every soft job is completed, we also have a timer.
*/
#define MALI_TIMELINE_TIMEOUT_HZ ((u32) (HZ * 3 / 2)) /* 1500 ms. */
/**
* Timeline type.
*/
typedef enum mali_timeline_id {
MALI_TIMELINE_GP = MALI_UK_TIMELINE_GP, /**< GP job timeline. */
MALI_TIMELINE_PP = MALI_UK_TIMELINE_PP, /**< PP job timeline. */
MALI_TIMELINE_SOFT = MALI_UK_TIMELINE_SOFT, /**< Soft job timeline. */
MALI_TIMELINE_MAX = MALI_UK_TIMELINE_MAX
} mali_timeline_id;
/**
* Used by trackers that should not be added to a timeline (@ref mali_timeline_system_add_tracker).
*/
#define MALI_TIMELINE_NONE MALI_TIMELINE_MAX
/**
* Tracker type.
*/
typedef enum mali_timeline_tracker_type {
MALI_TIMELINE_TRACKER_GP = 0, /**< Tracker used by GP jobs. */
MALI_TIMELINE_TRACKER_PP = 1, /**< Tracker used by PP jobs. */
MALI_TIMELINE_TRACKER_SOFT = 2, /**< Tracker used by soft jobs. */
MALI_TIMELINE_TRACKER_WAIT = 3, /**< Tracker used for fence wait. */
MALI_TIMELINE_TRACKER_SYNC = 4, /**< Tracker used for sync fence. */
MALI_TIMELINE_TRACKER_MAX = 5,
} mali_timeline_tracker_type;
/**
* Tracker activation error.
*/
typedef u32 mali_timeline_activation_error;
#define MALI_TIMELINE_ACTIVATION_ERROR_NONE 0
#define MALI_TIMELINE_ACTIVATION_ERROR_SYNC_BIT (1<<1)
#define MALI_TIMELINE_ACTIVATION_ERROR_FATAL_BIT (1<<0)
/**
* Type used to represent a point on a timeline.
*/
typedef u32 mali_timeline_point;
/**
* Used to represent that no point on a timeline.
*/
#define MALI_TIMELINE_NO_POINT ((mali_timeline_point) 0)
/**
* The maximum span of points on a timeline. A timeline will be considered full if the difference
* between the oldest and newest points is equal or larger to this value.
*/
#define MALI_TIMELINE_MAX_POINT_SPAN 65536
/**
* Magic value used to assert on validity of trackers.
*/
#define MALI_TIMELINE_TRACKER_MAGIC 0xabcdabcd
struct mali_timeline;
struct mali_timeline_waiter;
struct mali_timeline_tracker;
/**
* Timeline fence.
*/
struct mali_timeline_fence {
mali_timeline_point points[MALI_TIMELINE_MAX]; /**< For each timeline, a point or MALI_TIMELINE_NO_POINT. */
s32 sync_fd; /**< A file descriptor representing a sync fence, or -1. */
};
/**
* Timeline system.
*
* The Timeline system has a set of timelines associated with a session.
*/
struct mali_timeline_system {
struct mali_spinlock_reentrant *spinlock; /**< Spin lock protecting the timeline system */
struct mali_timeline *timelines[MALI_TIMELINE_MAX]; /**< The timelines in this system */
/* Single-linked list of unused waiter objects. Uses the tracker_next field in tracker. */
struct mali_timeline_waiter *waiter_empty_list;
struct mali_session_data *session; /**< Session that owns this system. */
mali_bool timer_enabled; /**< Set to MALI_TRUE if soft job timer should be enabled, MALI_FALSE if not. */
_mali_osk_wait_queue_t *wait_queue; /**< Wait queue. */
#if defined(CONFIG_SYNC)
struct sync_timeline *signaled_sync_tl; /**< Special sync timeline used to create pre-signaled sync fences */
#endif /* defined(CONFIG_SYNC) */
};
/**
* Timeline. Each Timeline system will have MALI_TIMELINE_MAX timelines.
*/
struct mali_timeline {
mali_timeline_point point_next; /**< The next available point. */
mali_timeline_point point_oldest; /**< The oldest point not released. */
/* Double-linked list of trackers. Sorted in ascending order by tracker->time_number with
* tail pointing to the tracker with the oldest time. */
struct mali_timeline_tracker *tracker_head;
struct mali_timeline_tracker *tracker_tail;
/* Double-linked list of waiters. Sorted in ascending order by waiter->time_number_wait
* with tail pointing to the waiter with oldest wait time. */
struct mali_timeline_waiter *waiter_head;
struct mali_timeline_waiter *waiter_tail;
struct mali_timeline_system *system; /**< Timeline system this timeline belongs to. */
enum mali_timeline_id id; /**< Timeline type. */
#if defined(CONFIG_SYNC)
struct sync_timeline *sync_tl; /**< Sync timeline that corresponds to this timeline. */
#endif /* defined(CONFIG_SYNC) */
/* The following fields are used to time out soft job trackers. */
_mali_osk_wq_delayed_work_t *delayed_work;
mali_bool timer_active;
};
/**
* Timeline waiter.
*/
struct mali_timeline_waiter {
mali_timeline_point point; /**< Point on timeline we are waiting for to be released. */
struct mali_timeline_tracker *tracker; /**< Tracker that is waiting. */
struct mali_timeline_waiter *timeline_next; /**< Next waiter on timeline's waiter list. */
struct mali_timeline_waiter *timeline_prev; /**< Previous waiter on timeline's waiter list. */
struct mali_timeline_waiter *tracker_next; /**< Next waiter on tracker's waiter list. */
};
/**
* Timeline tracker.
*/
struct mali_timeline_tracker {
MALI_DEBUG_CODE(u32 magic); /**< Should always be MALI_TIMELINE_TRACKER_MAGIC for a valid tracker. */
mali_timeline_point point; /**< Point on timeline for this tracker */
struct mali_timeline_tracker *timeline_next; /**< Next tracker on timeline's tracker list */
struct mali_timeline_tracker *timeline_prev; /**< Previous tracker on timeline's tracker list */
u32 trigger_ref_count; /**< When zero tracker will be activated */
mali_timeline_activation_error activation_error; /**< Activation error. */
struct mali_timeline_fence fence; /**< Fence used to create this tracker */
/* Single-linked list of waiters. Sorted in order of insertions with
* tail pointing to first waiter. */
struct mali_timeline_waiter *waiter_head;
struct mali_timeline_waiter *waiter_tail;
#if defined(CONFIG_SYNC)
/* These are only used if the tracker is waiting on a sync fence. */
struct mali_timeline_waiter *waiter_sync; /**< A direct pointer to timeline waiter representing sync fence. */
struct sync_fence_waiter sync_fence_waiter; /**< Used to connect sync fence and tracker in sync fence wait callback. */
struct sync_fence *sync_fence; /**< The sync fence this tracker is waiting on. */
_mali_osk_list_t sync_fence_cancel_list; /**< List node used to cancel sync fence waiters. */
#endif /* defined(CONFIG_SYNC) */
struct mali_timeline_system *system; /**< Timeline system. */
struct mali_timeline *timeline; /**< Timeline, or NULL if not on a timeline. */
enum mali_timeline_tracker_type type; /**< Type of tracker. */
void *job; /**< Owner of tracker. */
/* The following fields are used to time out soft job trackers. */
u32 os_tick_create;
u32 os_tick_activate;
mali_bool timer_active;
};
/**
* What follows is a set of functions to check the state of a timeline and to determine where on a
* timeline a given point is. Most of these checks will translate the timeline so the oldest point
* on the timeline is aligned with zero. Remember that all of these calculation are done on
* unsigned integers.
*
* The following example illustrates the three different states a point can be in. The timeline has
* been translated to put the oldest point at zero:
*
*
*
* [ point is in forbidden zone ]
* 64k wide
* MALI_TIMELINE_MAX_POINT_SPAN
*
* [ point is on timeline ) ( point is released ]
*
* 0--------------------------##############################--------------------2^32 - 1
* ^ ^
* \ |
* oldest point on timeline |
* \
* next point on timeline
*/
/**
* Compare two timeline points
*
* Returns true if a is after b, false if a is before or equal to b.
*
* This funcion ignores MALI_TIMELINE_MAX_POINT_SPAN. Wrapping is supported and
* the result will be correct if the points is less then UINT_MAX/2 apart.
*
* @param a Point on timeline
* @param b Point on timeline
* @return MALI_TRUE if a is after b
*/
MALI_STATIC_INLINE mali_bool mali_timeline_point_after(mali_timeline_point a, mali_timeline_point b)
{
return 0 > ((s32)b) - ((s32)a);
}
/**
* Check if a point is on timeline. A point is on a timeline if it is greater than, or equal to,
* the oldest point, and less than the next point.
*
* @param timeline Timeline.
* @param point Point on timeline.
* @return MALI_TRUE if point is on timeline, MALI_FALSE if not.
*/
MALI_STATIC_INLINE mali_bool mali_timeline_is_point_on(struct mali_timeline *timeline, mali_timeline_point point)
{
MALI_DEBUG_ASSERT_POINTER(timeline);
MALI_DEBUG_ASSERT(MALI_TIMELINE_NO_POINT != point);
return (point - timeline->point_oldest) < (timeline->point_next - timeline->point_oldest);
}
/**
* Check if a point has been released. A point is released if it is older than the oldest point on
* the timeline, newer than the next point, and also not in the forbidden zone.
*
* @param timeline Timeline.
* @param point Point on timeline.
* @return MALI_TRUE if point has been release, MALI_FALSE if not.
*/
MALI_STATIC_INLINE mali_bool mali_timeline_is_point_released(struct mali_timeline *timeline, mali_timeline_point point)
{
mali_timeline_point point_normalized;
mali_timeline_point next_normalized;
MALI_DEBUG_ASSERT_POINTER(timeline);
MALI_DEBUG_ASSERT(MALI_TIMELINE_NO_POINT != point);
point_normalized = point - timeline->point_oldest;
next_normalized = timeline->point_next - timeline->point_oldest;
return point_normalized > (next_normalized + MALI_TIMELINE_MAX_POINT_SPAN);
}
/**
* Check if a point is valid. A point is valid if is on the timeline or has been released.
*
* @param timeline Timeline.
* @param point Point on timeline.
* @return MALI_TRUE if point is valid, MALI_FALSE if not.
*/
MALI_STATIC_INLINE mali_bool mali_timeline_is_point_valid(struct mali_timeline *timeline, mali_timeline_point point)
{
MALI_DEBUG_ASSERT_POINTER(timeline);
return mali_timeline_is_point_on(timeline, point) || mali_timeline_is_point_released(timeline, point);
}
/**
* Check if timeline is empty (has no points on it). A timeline is empty if next == oldest.
*
* @param timeline Timeline.
* @return MALI_TRUE if timeline is empty, MALI_FALSE if not.
*/
MALI_STATIC_INLINE mali_bool mali_timeline_is_empty(struct mali_timeline *timeline)
{
MALI_DEBUG_ASSERT_POINTER(timeline);
return timeline->point_next == timeline->point_oldest;
}
/**
* Check if timeline is full. A valid timeline cannot span more than 64k points (@ref
* MALI_TIMELINE_MAX_POINT_SPAN).
*
* @param timeline Timeline.
* @return MALI_TRUE if timeline is full, MALI_FALSE if not.
*/
MALI_STATIC_INLINE mali_bool mali_timeline_is_full(struct mali_timeline *timeline)
{
MALI_DEBUG_ASSERT_POINTER(timeline);
return MALI_TIMELINE_MAX_POINT_SPAN <= (timeline->point_next - timeline->point_oldest);
}
/**
* Create a new timeline system.
*
* @param session The session this timeline system will belong to.
* @return New timeline system.
*/
struct mali_timeline_system *mali_timeline_system_create(struct mali_session_data *session);
/**
* Abort timeline system.
*
* This will release all pending waiters in the timeline system causing all trackers to be
* activated.
*
* @param system Timeline system to abort all jobs from.
*/
void mali_timeline_system_abort(struct mali_timeline_system *system);
/**
* Destroy an empty timeline system.
*
* @note @ref mali_timeline_system_abort() should be called prior to this function.
*
* @param system Timeline system to destroy.
*/
void mali_timeline_system_destroy(struct mali_timeline_system *system);
/**
* Stop the soft job timer.
*
* @param system Timeline system
*/
void mali_timeline_system_stop_timer(struct mali_timeline_system *system);
/**
* Add a tracker to a timeline system and optionally also on a timeline.
*
* Once added to the timeline system, the tracker is guaranteed to be activated. The tracker can be
* activated before this function returns. Thus, it is also possible that the tracker is released
* before this function returns, depending on the tracker type.
*
* @note Tracker must be initialized (@ref mali_timeline_tracker_init) before being added to the
* timeline system.
*
* @param system Timeline system the tracker will be added to.
* @param tracker The tracker to be added.
* @param timeline_id Id of the timeline the tracker will be added to, or
* MALI_TIMELINE_NONE if it should not be added on a timeline.
* @return Point on timeline identifying this tracker, or MALI_TIMELINE_NO_POINT if not on timeline.
*/
mali_timeline_point mali_timeline_system_add_tracker(struct mali_timeline_system *system,
struct mali_timeline_tracker *tracker,
enum mali_timeline_id timeline_id);
/**
* Get latest point on timeline.
*
* @param system Timeline system.
* @param timeline_id Id of timeline to get latest point from.
* @return Latest point on timeline, or MALI_TIMELINE_NO_POINT if the timeline is empty.
*/
mali_timeline_point mali_timeline_system_get_latest_point(struct mali_timeline_system *system,
enum mali_timeline_id timeline_id);
/**
* Initialize tracker.
*
* Must be called before tracker is added to timeline system (@ref mali_timeline_system_add_tracker).
*
* @param tracker Tracker to initialize.
* @param type Type of tracker.
* @param fence Fence used to set up dependencies for tracker.
* @param job Pointer to job struct this tracker is associated with.
*/
void mali_timeline_tracker_init(struct mali_timeline_tracker *tracker,
mali_timeline_tracker_type type,
struct mali_timeline_fence *fence,
void *job);
/**
* Grab trigger ref count on tracker.
*
* This will prevent tracker from being activated until the trigger ref count reaches zero.
*
* @note Tracker must have been initialized (@ref mali_timeline_tracker_init).
*
* @param system Timeline system.
* @param tracker Tracker.
*/
void mali_timeline_system_tracker_get(struct mali_timeline_system *system, struct mali_timeline_tracker *tracker);
/**
* Release trigger ref count on tracker.
*
* If the trigger ref count reaches zero, the tracker will be activated.
*
* @param system Timeline system.
* @param tracker Tracker.
* @param activation_error Error bitmask if activated with error, or MALI_TIMELINE_ACTIVATION_ERROR_NONE if no error.
* @return Scheduling bitmask.
*/
mali_scheduler_mask mali_timeline_system_tracker_put(struct mali_timeline_system *system, struct mali_timeline_tracker *tracker, mali_timeline_activation_error activation_error);
/**
* Release a tracker from the timeline system.
*
* This is used to signal that the job being tracker is finished, either due to normal circumstances
* (job complete/abort) or due to a timeout.
*
* We may need to schedule some subsystems after a tracker has been released and the returned
* bitmask will tell us if it is necessary. If the return value is non-zero, this value needs to be
* sent as an input parameter to @ref mali_scheduler_schedule_from_mask() to do the scheduling.
*
* @note Tracker must have been activated before being released.
* @warning Not calling @ref mali_scheduler_schedule_from_mask() after releasing a tracker can lead
* to a deadlock.
*
* @param tracker Tracker being released.
* @return Scheduling bitmask.
*/
mali_scheduler_mask mali_timeline_tracker_release(struct mali_timeline_tracker *tracker);
/**
* Copy data from a UK fence to a Timeline fence.
*
* @param fence Timeline fence.
* @param uk_fence UK fence.
*/
void mali_timeline_fence_copy_uk_fence(struct mali_timeline_fence *fence, _mali_uk_fence_t *uk_fence);
#define MALI_TIMELINE_DEBUG_FUNCTIONS
#if defined(MALI_TIMELINE_DEBUG_FUNCTIONS)
/**
* Tracker state. Used for debug printing.
*/
typedef enum mali_timeline_tracker_state {
MALI_TIMELINE_TS_INIT = 0,
MALI_TIMELINE_TS_WAITING = 1,
MALI_TIMELINE_TS_ACTIVE = 2,
MALI_TIMELINE_TS_FINISH = 3,
} mali_timeline_tracker_state;
/**
* Get tracker state.
*
* @param tracker Tracker to check.
* @return State of tracker.
*/
mali_timeline_tracker_state mali_timeline_debug_get_tracker_state(struct mali_timeline_tracker *tracker);
/**
* Print debug information about tracker.
*
* @param tracker Tracker to print.
*/
void mali_timeline_debug_print_tracker(struct mali_timeline_tracker *tracker);
/**
* Print debug information about timeline.
*
* @param timeline Timeline to print.
*/
void mali_timeline_debug_print_timeline(struct mali_timeline *timeline);
/**
* Print debug information about timeline system.
*
* @param system Timeline system to print.
*/
void mali_timeline_debug_print_system(struct mali_timeline_system *system);
#endif /* defined(MALI_TIMELINE_DEBUG_FUNCTIONS) */
#endif /* __MALI_TIMELINE_H__ */

View File

@@ -0,0 +1,198 @@
/*
* Copyright (C) 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.
*/
#include "mali_timeline_fence_wait.h"
#include "mali_osk.h"
#include "mali_kernel_common.h"
#include "mali_spinlock_reentrant.h"
/**
* Allocate a fence waiter tracker.
*
* @return New fence waiter if successful, NULL if not.
*/
static struct mali_timeline_fence_wait_tracker *mali_timeline_fence_wait_tracker_alloc(void)
{
return (struct mali_timeline_fence_wait_tracker *) _mali_osk_calloc(1, sizeof(struct mali_timeline_fence_wait_tracker));
}
/**
* Free fence waiter tracker.
*
* @param wait Fence wait tracker to free.
*/
static void mali_timeline_fence_wait_tracker_free(struct mali_timeline_fence_wait_tracker *wait)
{
MALI_DEBUG_ASSERT_POINTER(wait);
_mali_osk_atomic_term(&wait->refcount);
_mali_osk_free(wait);
}
/**
* Check if fence wait tracker has been activated. Used as a wait queue condition.
*
* @param data Fence waiter.
* @return MALI_TRUE if tracker has been activated, MALI_FALSE if not.
*/
static mali_bool mali_timeline_fence_wait_tracker_is_activated(void *data)
{
struct mali_timeline_fence_wait_tracker *wait;
wait = (struct mali_timeline_fence_wait_tracker *) data;
MALI_DEBUG_ASSERT_POINTER(wait);
return wait->activated;
}
/**
* Check if fence has been signaled.
*
* @param system Timeline system.
* @param fence Timeline fence.
* @return MALI_TRUE if fence is signaled, MALI_FALSE if not.
*/
static mali_bool mali_timeline_fence_wait_check_status(struct mali_timeline_system *system, struct mali_timeline_fence *fence)
{
int i;
u32 tid = _mali_osk_get_tid();
mali_bool ret = MALI_TRUE;
#if defined(CONFIG_SYNC)
struct sync_fence *sync_fence = NULL;
#endif
MALI_DEBUG_ASSERT_POINTER(system);
MALI_DEBUG_ASSERT_POINTER(fence);
mali_spinlock_reentrant_wait(system->spinlock, tid);
for (i = 0; i < MALI_TIMELINE_MAX; ++i) {
struct mali_timeline *timeline;
mali_timeline_point point;
point = fence->points[i];
if (likely(MALI_TIMELINE_NO_POINT == point)) {
/* Fence contains no point on this timeline. */
continue;
}
timeline = system->timelines[i];
MALI_DEBUG_ASSERT_POINTER(timeline);
if (unlikely(!mali_timeline_is_point_valid(timeline, point))) {
MALI_PRINT_ERROR(("Mali Timeline: point %d is not valid (oldest=%d, next=%d)\n", point, timeline->point_oldest, timeline->point_next));
}
if (!mali_timeline_is_point_released(timeline, point)) {
ret = MALI_FALSE;
goto exit;
}
}
#if defined(CONFIG_SYNC)
if (-1 != fence->sync_fd) {
sync_fence = sync_fence_fdget(fence->sync_fd);
if (likely(NULL != sync_fence)) {
if (0 == sync_fence->status) {
ret = MALI_FALSE;
}
} else {
MALI_PRINT_ERROR(("Mali Timeline: failed to get sync fence from fd %d\n", fence->sync_fd));
}
}
#endif /* defined(CONFIG_SYNC) */
exit:
mali_spinlock_reentrant_signal(system->spinlock, tid);
#if defined(CONFIG_SYNC)
if (NULL != sync_fence) {
sync_fence_put(sync_fence);
}
#endif /* defined(CONFIG_SYNC) */
return ret;
}
mali_bool mali_timeline_fence_wait(struct mali_timeline_system *system, struct mali_timeline_fence *fence, u32 timeout)
{
struct mali_timeline_fence_wait_tracker *wait;
mali_timeline_point point;
mali_bool ret;
MALI_DEBUG_ASSERT_POINTER(system);
MALI_DEBUG_ASSERT_POINTER(fence);
MALI_DEBUG_PRINT(4, ("Mali Timeline: wait on fence\n"));
if (MALI_TIMELINE_FENCE_WAIT_TIMEOUT_IMMEDIATELY == timeout) {
return mali_timeline_fence_wait_check_status(system, fence);
}
wait = mali_timeline_fence_wait_tracker_alloc();
if (unlikely(NULL == wait)) {
MALI_PRINT_ERROR(("Mali Timeline: failed to allocate data for fence wait\n"));
return MALI_FALSE;
}
wait->activated = MALI_FALSE;
wait->system = system;
/* Initialize refcount to two references. The reference first will be released by this
* function after the wait is over. The second reference will be released when the tracker
* is activated. */
_mali_osk_atomic_init(&wait->refcount, 2);
/* Add tracker to timeline system, but not to a timeline. */
mali_timeline_tracker_init(&wait->tracker, MALI_TIMELINE_TRACKER_WAIT, fence, wait);
point = mali_timeline_system_add_tracker(system, &wait->tracker, MALI_TIMELINE_NONE);
MALI_DEBUG_ASSERT(MALI_TIMELINE_NO_POINT == point);
MALI_IGNORE(point);
/* Wait for the tracker to be activated or time out. */
if (MALI_TIMELINE_FENCE_WAIT_TIMEOUT_NEVER == timeout) {
_mali_osk_wait_queue_wait_event(system->wait_queue, mali_timeline_fence_wait_tracker_is_activated, (void *) wait);
} else {
_mali_osk_wait_queue_wait_event_timeout(system->wait_queue, mali_timeline_fence_wait_tracker_is_activated, (void *) wait, timeout);
}
ret = wait->activated;
if (0 == _mali_osk_atomic_dec_return(&wait->refcount)) {
mali_timeline_fence_wait_tracker_free(wait);
}
return ret;
}
void mali_timeline_fence_wait_activate(struct mali_timeline_fence_wait_tracker *wait)
{
mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY;
MALI_DEBUG_ASSERT_POINTER(wait);
MALI_DEBUG_ASSERT_POINTER(wait->system);
MALI_DEBUG_PRINT(4, ("Mali Timeline: activation for fence wait tracker\n"));
MALI_DEBUG_ASSERT(MALI_FALSE == wait->activated);
wait->activated = MALI_TRUE;
_mali_osk_wait_queue_wake_up(wait->system->wait_queue);
/* Nothing can wait on this tracker, so nothing to schedule after release. */
schedule_mask = mali_timeline_tracker_release(&wait->tracker);
MALI_DEBUG_ASSERT(MALI_SCHEDULER_MASK_EMPTY == schedule_mask);
MALI_IGNORE(schedule_mask);
if (0 == _mali_osk_atomic_dec_return(&wait->refcount)) {
mali_timeline_fence_wait_tracker_free(wait);
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (C) 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 mali_timeline_fence_wait.h
*
* This file contains functions used to wait until a Timeline fence is signaled.
*/
#ifndef __MALI_TIMELINE_FENCE_WAIT_H__
#define __MALI_TIMELINE_FENCE_WAIT_H__
#include "mali_osk.h"
#include "mali_timeline.h"
/**
* If used as the timeout argument in @ref mali_timeline_fence_wait, a timer is not used and the
* function only returns when the fence is signaled.
*/
#define MALI_TIMELINE_FENCE_WAIT_TIMEOUT_NEVER ((u32) -1)
/**
* If used as the timeout argument in @ref mali_timeline_fence_wait, the function will return
* immediately with the current state of the fence.
*/
#define MALI_TIMELINE_FENCE_WAIT_TIMEOUT_IMMEDIATELY 0
/**
* Fence wait tracker.
*
* The fence wait tracker is added to the Timeline system with the fence we are waiting on as a
* dependency. We will then perform a blocking wait, possibly with a timeout, until the tracker is
* activated, which happens when the fence is signaled.
*/
struct mali_timeline_fence_wait_tracker {
mali_bool activated; /**< MALI_TRUE if the tracker has been activated, MALI_FALSE if not. */
_mali_osk_atomic_t refcount; /**< Reference count. */
struct mali_timeline_system *system; /**< Timeline system. */
struct mali_timeline_tracker tracker; /**< Timeline tracker. */
};
/**
* Wait for a fence to be signaled, or timeout is reached.
*
* @param system Timeline system.
* @param fence Fence to wait on.
* @param timeout Timeout in ms, or MALI_TIMELINE_FENCE_WAIT_TIMEOUT_NEVER or
* MALI_TIMELINE_FENCE_WAIT_TIMEOUT_IMMEDIATELY.
* @return MALI_TRUE if signaled, MALI_FALSE if timed out.
*/
mali_bool mali_timeline_fence_wait(struct mali_timeline_system *system, struct mali_timeline_fence *fence, u32 timeout);
/**
* Used by the Timeline system to activate a fence wait tracker.
*
* @param fence_wait_tracker Fence waiter tracker.
*/
void mali_timeline_fence_wait_activate(struct mali_timeline_fence_wait_tracker *fence_wait_tracker);
#endif /* __MALI_TIMELINE_FENCE_WAIT_H__ */

View File

@@ -0,0 +1,158 @@
/*
* Copyright (C) 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.
*/
#include "mali_timeline_sync_fence.h"
#include "mali_osk.h"
#include "mali_kernel_common.h"
#include "mali_sync.h"
#if defined(CONFIG_SYNC)
/**
* Creates a sync fence tracker and a sync fence. Adds sync fence tracker to Timeline system and
* returns sync fence. The sync fence will be signaled when the sync fence tracker is activated.
*
* @param timeline Timeline.
* @param point Point on timeline.
* @return Sync fence that will be signaled when tracker is activated.
*/
static struct sync_fence *mali_timeline_sync_fence_create_and_add_tracker(struct mali_timeline *timeline, mali_timeline_point point)
{
struct mali_timeline_sync_fence_tracker *sync_fence_tracker;
struct sync_fence *sync_fence;
struct mali_timeline_fence fence;
MALI_DEBUG_ASSERT_POINTER(timeline);
MALI_DEBUG_ASSERT(MALI_TIMELINE_NO_POINT != point);
/* Allocate sync fence tracker. */
sync_fence_tracker = _mali_osk_calloc(1, sizeof(struct mali_timeline_sync_fence_tracker));
if (NULL == sync_fence_tracker) {
MALI_PRINT_ERROR(("Mali Timeline: sync_fence_tracker allocation failed\n"));
return NULL;
}
/* Create sync flag. */
MALI_DEBUG_ASSERT_POINTER(timeline->sync_tl);
sync_fence_tracker->flag = mali_sync_flag_create(timeline->sync_tl, point);
if (NULL == sync_fence_tracker->flag) {
MALI_PRINT_ERROR(("Mali Timeline: sync_flag creation failed\n"));
_mali_osk_free(sync_fence_tracker);
return NULL;
}
/* Create sync fence from sync flag. */
sync_fence = mali_sync_flag_create_fence(sync_fence_tracker->flag);
if (NULL == sync_fence) {
MALI_PRINT_ERROR(("Mali Timeline: sync_fence creation failed\n"));
mali_sync_flag_put(sync_fence_tracker->flag);
_mali_osk_free(sync_fence_tracker);
return NULL;
}
/* Setup fence for tracker. */
_mali_osk_memset(&fence, 0, sizeof(struct mali_timeline_fence));
fence.sync_fd = -1;
fence.points[timeline->id] = point;
/* Finally, add the tracker to Timeline system. */
mali_timeline_tracker_init(&sync_fence_tracker->tracker, MALI_TIMELINE_TRACKER_SYNC, &fence, sync_fence_tracker);
point = mali_timeline_system_add_tracker(timeline->system, &sync_fence_tracker->tracker, MALI_TIMELINE_NONE);
MALI_DEBUG_ASSERT(MALI_TIMELINE_NO_POINT == point);
return sync_fence;
}
s32 mali_timeline_sync_fence_create(struct mali_timeline_system *system, struct mali_timeline_fence *fence)
{
u32 i;
struct sync_fence *sync_fence_acc = NULL;
MALI_DEBUG_ASSERT_POINTER(system);
MALI_DEBUG_ASSERT_POINTER(fence);
for (i = 0; i < MALI_TIMELINE_MAX; ++i) {
struct mali_timeline *timeline;
struct sync_fence *sync_fence;
if (MALI_TIMELINE_NO_POINT == fence->points[i]) continue;
timeline = system->timelines[i];
MALI_DEBUG_ASSERT_POINTER(timeline);
sync_fence = mali_timeline_sync_fence_create_and_add_tracker(timeline, fence->points[i]);
if (NULL == sync_fence) goto error;
if (NULL != sync_fence_acc) {
/* Merge sync fences. */
sync_fence_acc = mali_sync_fence_merge(sync_fence_acc, sync_fence);
if (NULL == sync_fence_acc) goto error;
} else {
/* This was the first sync fence created. */
sync_fence_acc = sync_fence;
}
}
if (-1 != fence->sync_fd) {
struct sync_fence *sync_fence;
sync_fence = sync_fence_fdget(fence->sync_fd);
if (NULL == sync_fence) goto error;
if (NULL != sync_fence_acc) {
sync_fence_acc = mali_sync_fence_merge(sync_fence_acc, sync_fence);
if (NULL == sync_fence_acc) goto error;
} else {
sync_fence_acc = sync_fence;
}
}
if (NULL == sync_fence_acc) {
MALI_DEBUG_ASSERT_POINTER(system->signaled_sync_tl);
/* There was nothing to wait on, so return an already signaled fence. */
sync_fence_acc = mali_sync_timeline_create_signaled_fence(system->signaled_sync_tl);
if (NULL == sync_fence_acc) goto error;
}
/* Return file descriptor for the accumulated sync fence. */
return mali_sync_fence_fd_alloc(sync_fence_acc);
error:
if (NULL != sync_fence_acc) {
sync_fence_put(sync_fence_acc);
}
return -1;
}
void mali_timeline_sync_fence_activate(struct mali_timeline_sync_fence_tracker *sync_fence_tracker)
{
mali_scheduler_mask schedule_mask = MALI_SCHEDULER_MASK_EMPTY;
MALI_DEBUG_ASSERT_POINTER(sync_fence_tracker);
MALI_DEBUG_ASSERT_POINTER(sync_fence_tracker->flag);
MALI_DEBUG_PRINT(4, ("Mali Timeline: activation for sync fence tracker\n"));
/* Signal flag and release reference. */
mali_sync_flag_signal(sync_fence_tracker->flag, 0);
mali_sync_flag_put(sync_fence_tracker->flag);
/* Nothing can wait on this tracker, so nothing to schedule after release. */
schedule_mask = mali_timeline_tracker_release(&sync_fence_tracker->tracker);
MALI_DEBUG_ASSERT(MALI_SCHEDULER_MASK_EMPTY == schedule_mask);
_mali_osk_free(sync_fence_tracker);
}
#endif /* defined(CONFIG_SYNC) */

View File

@@ -0,0 +1,51 @@
/*
* Copyright (C) 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 mali_timeline_sync_fence.h
*
* This file contains code related to creating sync fences from timeline fences.
*/
#ifndef __MALI_TIMELINE_SYNC_FENCE_H__
#define __MALI_TIMELINE_SYNC_FENCE_H__
#include "mali_timeline.h"
#if defined(CONFIG_SYNC)
/**
* Sync fence tracker.
*/
struct mali_timeline_sync_fence_tracker {
struct mali_sync_flag *flag; /**< Sync flag used to connect tracker and sync fence. */
struct mali_timeline_tracker tracker; /**< Timeline tracker. */
};
/**
* Create a sync fence that will be signaled when @ref fence is signaled.
*
* @param system Timeline system.
* @param fence Fence to create sync fence from.
* @return File descriptor for new sync fence, or -1 on error.
*/
s32 mali_timeline_sync_fence_create(struct mali_timeline_system *system, struct mali_timeline_fence *fence);
/**
* Used by the Timeline system to activate a sync fence tracker.
*
* @param sync_fence_tracker Sync fence tracker.
*
*/
void mali_timeline_sync_fence_activate(struct mali_timeline_sync_fence_tracker *sync_fence_tracker);
#endif /* defined(CONFIG_SYNC) */
#endif /* __MALI_TIMELINE_SYNC_FENCE_H__ */

View File

@@ -0,0 +1,614 @@
/*
* Copyright (C) 2010-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 mali_ukk.h
* Defines the kernel-side interface of the user-kernel interface
*/
#ifndef __MALI_UKK_H__
#define __MALI_UKK_H__
#include "mali_osk.h"
#include "mali_uk_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @addtogroup uddapi Unified Device Driver (UDD) APIs
*
* @{
*/
/**
* @addtogroup u_k_api UDD User/Kernel Interface (U/K) APIs
*
* - The _mali_uk functions are an abstraction of the interface to the device
* driver. On certain OSs, this would be implemented via the IOCTL interface.
* On other OSs, it could be via extension of some Device Driver Class, or
* direct function call for Bare metal/RTOSs.
* - It is important to note that:
* - The Device Driver has implemented the _mali_ukk set of functions
* - The Base Driver calls the corresponding set of _mali_uku functions.
* - What requires porting is solely the calling mechanism from User-side to
* Kernel-side, and propagating back the results.
* - Each U/K function is associated with a (group, number) pair from
* \ref _mali_uk_functions to make it possible for a common function in the
* Base Driver and Device Driver to route User/Kernel calls from/to the
* correct _mali_uk function. For example, in an IOCTL system, the IOCTL number
* would be formed based on the group and number assigned to the _mali_uk
* function, as listed in \ref _mali_uk_functions. On the user-side, each
* _mali_uku function would just make an IOCTL with the IOCTL-code being an
* encoded form of the (group, number) pair. On the kernel-side, the Device
* Driver's IOCTL handler decodes the IOCTL-code back into a (group, number)
* pair, and uses this to determine which corresponding _mali_ukk should be
* called.
* - Refer to \ref _mali_uk_functions for more information about this
* (group, number) pairing.
* - In a system where there is no distinction between user and kernel-side,
* the U/K interface may be implemented as:@code
* MALI_STATIC_INLINE _mali_osk_errcode_t _mali_uku_examplefunction( _mali_uk_examplefunction_s *args )
* {
* return mali_ukk_examplefunction( args );
* }
* @endcode
* - Therefore, all U/K calls behave \em as \em though they were direct
* function calls (but the \b implementation \em need \em not be a direct
* function calls)
*
* @note Naming the _mali_uk functions the same on both User and Kernel sides
* on non-RTOS systems causes debugging issues when setting breakpoints. In
* this case, it is not clear which function the breakpoint is put on.
* Therefore the _mali_uk functions in user space are prefixed with \c _mali_uku
* and in kernel space with \c _mali_ukk. The naming for the argument
* structures is unaffected.
*
* - The _mali_uk functions are synchronous.
* - Arguments to the _mali_uk functions are passed in a structure. The only
* parameter passed to the _mali_uk functions is a pointer to this structure.
* This first member of this structure, ctx, is a pointer to a context returned
* by _mali_uku_open(). For example:@code
* typedef struct
* {
* void *ctx;
* u32 number_of_cores;
* } _mali_uk_get_gp_number_of_cores_s;
* @endcode
*
* - Each _mali_uk function has its own argument structure named after the
* function. The argument is distinguished by the _s suffix.
* - The argument types are defined by the base driver and user-kernel
* interface.
* - All _mali_uk functions return a standard \ref _mali_osk_errcode_t.
* - Only arguments of type input or input/output need be initialized before
* calling a _mali_uk function.
* - Arguments of type output and input/output are only valid when the
* _mali_uk function returns \ref _MALI_OSK_ERR_OK.
* - The \c ctx member is always invalid after it has been used by a
* _mali_uk function, except for the context management functions
*
*
* \b Interface \b restrictions
*
* The requirements of the interface mean that an implementation of the
* User-kernel interface may do no 'real' work. For example, the following are
* illegal in the User-kernel implementation:
* - Calling functions necessary for operation on all systems, which would
* not otherwise get called on RTOS systems.
* - For example, a U/K interface that calls multiple _mali_ukk functions
* during one particular U/K call. This could not be achieved by the same code
* which uses direct function calls for the U/K interface.
* - Writing in values to the args members, when otherwise these members would
* not hold a useful value for a direct function call U/K interface.
* - For example, U/K interface implementation that take NULL members in
* their arguments structure from the user side, but those members are
* replaced with non-NULL values in the kernel-side of the U/K interface
* implementation. A scratch area for writing data is one such example. In this
* case, a direct function call U/K interface would segfault, because no code
* would be present to replace the NULL pointer with a meaningful pointer.
* - Note that we discourage the case where the U/K implementation changes
* a NULL argument member to non-NULL, and then the Device Driver code (outside
* of the U/K layer) re-checks this member for NULL, and corrects it when
* necessary. Whilst such code works even on direct function call U/K
* intefaces, it reduces the testing coverage of the Device Driver code. This
* is because we have no way of testing the NULL == value path on an OS
* implementation.
*
* A number of allowable examples exist where U/K interfaces do 'real' work:
* - The 'pointer switching' technique for \ref _mali_ukk_get_system_info
* - In this case, without the pointer switching on direct function call
* U/K interface, the Device Driver code still sees the same thing: a pointer
* to which it can write memory. This is because such a system has no
* distinction between a user and kernel pointer.
* - Writing an OS-specific value into the ukk_private member for
* _mali_ukk_mem_mmap().
* - In this case, this value is passed around by Device Driver code, but
* its actual value is never checked. Device Driver code simply passes it from
* the U/K layer to the OSK layer, where it can be acted upon. In this case,
* \em some OS implementations of the U/K (_mali_ukk_mem_mmap()) and OSK
* (_mali_osk_mem_mapregion_init()) functions will collaborate on the
* meaning of ukk_private member. On other OSs, it may be unused by both
* U/K and OSK layers
* - Therefore, on error inside the U/K interface implementation itself,
* it will be as though the _mali_ukk function itself had failed, and cleaned
* up after itself.
* - Compare this to a direct function call U/K implementation, where all
* error cleanup is handled by the _mali_ukk function itself. The direct
* function call U/K interface implementation is automatically atomic.
*
* The last example highlights a consequence of all U/K interface
* implementations: they must be atomic with respect to the Device Driver code.
* And therefore, should Device Driver code succeed but the U/K implementation
* fail afterwards (but before return to user-space), then the U/K
* implementation must cause appropriate cleanup actions to preserve the
* atomicity of the interface.
*
* @{
*/
/** @defgroup _mali_uk_context U/K Context management
*
* These functions allow for initialisation of the user-kernel interface once per process.
*
* Generally the context will store the OS specific object to communicate with the kernel device driver and further
* state information required by the specific implementation. The context is shareable among all threads in the caller process.
*
* On IOCTL systems, this is likely to be a file descriptor as a result of opening the kernel device driver.
*
* On a bare-metal/RTOS system with no distinction between kernel and
* user-space, the U/K interface simply calls the _mali_ukk variant of the
* function by direct function call. In this case, the context returned is the
* mali_session_data from _mali_ukk_open().
*
* The kernel side implementations of the U/K interface expect the first member of the argument structure to
* be the context created by _mali_uku_open(). On some OS implementations, the meaning of this context
* will be different between user-side and kernel-side. In which case, the kernel-side will need to replace this context
* with the kernel-side equivalent, because user-side will not have access to kernel-side data. The context parameter
* in the argument structure therefore has to be of type input/output.
*
* It should be noted that the caller cannot reuse the \c ctx member of U/K
* argument structure after a U/K call, because it may be overwritten. Instead,
* the context handle must always be stored elsewhere, and copied into
* the appropriate U/K argument structure for each user-side call to
* the U/K interface. This is not usually a problem, since U/K argument
* structures are usually placed on the stack.
*
* @{ */
/** @brief Begin a new Mali Device Driver session
*
* This is used to obtain a per-process context handle for all future U/K calls.
*
* @param context pointer to storage to return a (void*)context handle.
* @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure.
*/
_mali_osk_errcode_t _mali_ukk_open( void **context );
/** @brief End a Mali Device Driver session
*
* This should be called when the process no longer requires use of the Mali Device Driver.
*
* The context handle must not be used after it has been closed.
*
* @param context pointer to a stored (void*)context handle.
* @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure.
*/
_mali_osk_errcode_t _mali_ukk_close( void **context );
/** @} */ /* end group _mali_uk_context */
/** @addtogroup _mali_uk_core U/K Core
*
* The core functions provide the following functionality:
* - verify that the user and kernel API are compatible
* - retrieve information about the cores and memory banks in the system
* - wait for the result of jobs started on a core
*
* @{ */
/** @brief Waits for a job notification.
*
* Sleeps until notified or a timeout occurs. Returns information about the notification.
*
* @param args see _mali_uk_wait_for_notification_s in "mali_utgard_uk_types.h"
* @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure.
*/
_mali_osk_errcode_t _mali_ukk_wait_for_notification( _mali_uk_wait_for_notification_s *args );
/** @brief Post a notification to the notification queue of this application.
*
* @param args see _mali_uk_post_notification_s in "mali_utgard_uk_types.h"
* @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure.
*/
_mali_osk_errcode_t _mali_ukk_post_notification( _mali_uk_post_notification_s *args );
/** @brief Verifies if the user and kernel side of this API are compatible.
*
* @param args see _mali_uk_get_api_version_s in "mali_utgard_uk_types.h"
* @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure.
*/
_mali_osk_errcode_t _mali_ukk_get_api_version( _mali_uk_get_api_version_s *args );
/** @brief Get the user space settings applicable for calling process.
*
* @param args see _mali_uk_get_user_settings_s in "mali_utgard_uk_types.h"
* @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure.
*/
_mali_osk_errcode_t _mali_ukk_get_user_settings(_mali_uk_get_user_settings_s *args);
/** @brief Get a user space setting applicable for calling process.
*
* @param args see _mali_uk_get_user_setting_s in "mali_utgard_uk_types.h"
* @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure.
*/
_mali_osk_errcode_t _mali_ukk_get_user_setting(_mali_uk_get_user_setting_s *args);
/* @brief Grant or deny high priority scheduling for this session.
*
* @param args see _mali_uk_request_high_priority_s in "mali_utgard_uk_types.h"
* @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure.
*/
_mali_osk_errcode_t _mali_ukk_request_high_priority(_mali_uk_request_high_priority_s *args);
/** @} */ /* end group _mali_uk_core */
/** @addtogroup _mali_uk_memory U/K Memory
*
* The memory functions provide functionality with and without a Mali-MMU present.
*
* For Mali-MMU based systems, the following functionality is provided:
* - Initialize and terminate MALI virtual address space
* - Allocate/deallocate physical memory to a MALI virtual address range and map into/unmap from the
* current process address space
* - Map/unmap external physical memory into the MALI virtual address range
*
* For Mali-nonMMU based systems:
* - Allocate/deallocate MALI memory
*
* @{ */
/** @brief Map Mali Memory into the current user process
*
* Maps Mali memory into the current user process in a generic way.
*
* This function is to be used for Mali-MMU mode. The function is available in both Mali-MMU and Mali-nonMMU modes,
* but should not be called by a user process in Mali-nonMMU mode.
*
* The implementation and operation of _mali_ukk_mem_mmap() is dependant on whether the driver is built for Mali-MMU
* or Mali-nonMMU:
* - In the nonMMU case, _mali_ukk_mem_mmap() requires a physical address to be specified. For this reason, an OS U/K
* implementation should not allow this to be called from user-space. In any case, nonMMU implementations are
* inherently insecure, and so the overall impact is minimal. Mali-MMU mode should be used if security is desired.
* - In the MMU case, _mali_ukk_mem_mmap() the _mali_uk_mem_mmap_s::phys_addr
* member is used for the \em Mali-virtual address desired for the mapping. The
* implementation of _mali_ukk_mem_mmap() will allocate both the CPU-virtual
* and CPU-physical addresses, and can cope with mapping a contiguous virtual
* address range to a sequence of non-contiguous physical pages. In this case,
* the CPU-physical addresses are not communicated back to the user-side, as
* they are unnecsessary; the \em Mali-virtual address range must be used for
* programming Mali structures.
*
* In the second (MMU) case, _mali_ukk_mem_mmap() handles management of
* CPU-virtual and CPU-physical ranges, but the \em caller must manage the
* \em Mali-virtual address range from the user-side.
*
* @note Mali-virtual address ranges are entirely separate between processes.
* It is not possible for a process to accidentally corrupt another process'
* \em Mali-virtual address space.
*
* @param args see _mali_uk_mem_mmap_s in "mali_utgard_uk_types.h"
* @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure.
*/
_mali_osk_errcode_t _mali_ukk_mem_mmap( _mali_uk_mem_mmap_s *args );
/** @brief Unmap Mali Memory from the current user process
*
* Unmaps Mali memory from the current user process in a generic way. This only operates on Mali memory supplied
* from _mali_ukk_mem_mmap().
*
* @param args see _mali_uk_mem_munmap_s in "mali_utgard_uk_types.h"
* @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure.
*/
_mali_osk_errcode_t _mali_ukk_mem_munmap( _mali_uk_mem_munmap_s *args );
/** @brief Determine the buffer size necessary for an MMU page table dump.
* @param args see _mali_uk_query_mmu_page_table_dump_size_s in mali_utgard_uk_types.h
* @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure.
*/
_mali_osk_errcode_t _mali_ukk_query_mmu_page_table_dump_size( _mali_uk_query_mmu_page_table_dump_size_s *args );
/** @brief Dump MMU Page tables.
* @param args see _mali_uk_dump_mmu_page_table_s in mali_utgard_uk_types.h
* @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure.
*/
_mali_osk_errcode_t _mali_ukk_dump_mmu_page_table( _mali_uk_dump_mmu_page_table_s * args );
/** @brief Write user data to specified Mali memory without causing segfaults.
* @param args see _mali_uk_mem_write_safe_s in mali_utgard_uk_types.h
* @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure.
*/
_mali_osk_errcode_t _mali_ukk_mem_write_safe( _mali_uk_mem_write_safe_s *args );
/** @brief Map a physically contiguous range of memory into Mali
* @param args see _mali_uk_map_external_mem_s in mali_utgard_uk_types.h
* @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure.
*/
_mali_osk_errcode_t _mali_ukk_map_external_mem( _mali_uk_map_external_mem_s *args );
/** @brief Unmap a physically contiguous range of memory from Mali
* @param args see _mali_uk_unmap_external_mem_s in mali_utgard_uk_types.h
* @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure.
*/
_mali_osk_errcode_t _mali_ukk_unmap_external_mem( _mali_uk_unmap_external_mem_s *args );
#if defined(CONFIG_MALI400_UMP)
/** @brief Map UMP memory into Mali
* @param args see _mali_uk_attach_ump_mem_s in mali_utgard_uk_types.h
* @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure.
*/
_mali_osk_errcode_t _mali_ukk_attach_ump_mem( _mali_uk_attach_ump_mem_s *args );
/** @brief Unmap UMP memory from Mali
* @param args see _mali_uk_release_ump_mem_s in mali_utgard_uk_types.h
* @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure.
*/
_mali_osk_errcode_t _mali_ukk_release_ump_mem( _mali_uk_release_ump_mem_s *args );
#endif /* CONFIG_MALI400_UMP */
/** @brief Determine virtual-to-physical mapping of a contiguous memory range
* (optional)
*
* This allows the user-side to do a virtual-to-physical address translation.
* In conjunction with _mali_uku_map_external_mem, this can be used to do
* direct rendering.
*
* This function will only succeed on a virtual range that is mapped into the
* current process, and that is contigious.
*
* If va is not page-aligned, then it is rounded down to the next page
* boundary. The remainer is added to size, such that ((u32)va)+size before
* rounding is equal to ((u32)va)+size after rounding. The rounded modified
* va and size will be written out into args on success.
*
* If the supplied size is zero, or not a multiple of the system's PAGE_SIZE,
* then size will be rounded up to the next multiple of PAGE_SIZE before
* translation occurs. The rounded up size will be written out into args on
* success.
*
* On most OSs, virtual-to-physical address translation is a priveledged
* function. Therefore, the implementer must validate the range supplied, to
* ensure they are not providing arbitrary virtual-to-physical address
* translations. While it is unlikely such a mechanism could be used to
* compromise the security of a system on its own, it is possible it could be
* combined with another small security risk to cause a much larger security
* risk.
*
* @note This is an optional part of the interface, and is only used by certain
* implementations of libEGL. If the platform layer in your libEGL
* implementation does not require Virtual-to-Physical address translation,
* then this function need not be implemented. A stub implementation should not
* be required either, as it would only be removed by the compiler's dead code
* elimination.
*
* @note if implemented, this function is entirely platform-dependant, and does
* not exist in common code.
*
* @param args see _mali_uk_va_to_mali_pa_s in "mali_utgard_uk_types.h"
* @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure.
*/
_mali_osk_errcode_t _mali_ukk_va_to_mali_pa( _mali_uk_va_to_mali_pa_s * args );
/** @} */ /* end group _mali_uk_memory */
/** @addtogroup _mali_uk_pp U/K Fragment Processor
*
* The Fragment Processor (aka PP (Pixel Processor)) functions provide the following functionality:
* - retrieving version of the fragment processors
* - determine number of fragment processors
* - starting a job on a fragment processor
*
* @{ */
/** @brief Issue a request to start a new job on a Fragment Processor.
*
* If the request fails args->status is set to _MALI_UK_START_JOB_NOT_STARTED_DO_REQUEUE and you can
* try to start the job again.
*
* An existing job could be returned for requeueing if the new job has a higher priority than a previously started job
* which the hardware hasn't actually started processing yet. In this case the new job will be started instead and the
* existing one returned, otherwise the new job is started and the status field args->status is set to
* _MALI_UK_START_JOB_STARTED.
*
* Job completion can be awaited with _mali_ukk_wait_for_notification().
*
* @param ctx user-kernel context (mali_session)
* @param uargs see _mali_uk_pp_start_job_s in "mali_utgard_uk_types.h". Use _mali_osk_copy_from_user to retrieve data!
* @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure.
*/
_mali_osk_errcode_t _mali_ukk_pp_start_job( void *ctx, _mali_uk_pp_start_job_s *uargs );
/**
* @brief Issue a request to start new jobs on both Vertex Processor and Fragment Processor.
*
* @note Will call into @ref _mali_ukk_pp_start_job and @ref _mali_ukk_gp_start_job.
*
* @param ctx user-kernel context (mali_session)
* @param uargs see _mali_uk_pp_and_gp_start_job_s in "mali_utgard_uk_types.h". Use _mali_osk_copy_from_user to retrieve data!
* @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure.
*/
_mali_osk_errcode_t _mali_ukk_pp_and_gp_start_job( void *ctx, _mali_uk_pp_and_gp_start_job_s *uargs );
/** @brief Returns the number of Fragment Processors in the system
*
* @param args see _mali_uk_get_pp_number_of_cores_s in "mali_utgard_uk_types.h"
* @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure.
*/
_mali_osk_errcode_t _mali_ukk_get_pp_number_of_cores( _mali_uk_get_pp_number_of_cores_s *args );
/** @brief Returns the version that all Fragment Processor cores are compatible with.
*
* This function may only be called when _mali_ukk_get_pp_number_of_cores() indicated at least one Fragment
* Processor core is available.
*
* @param args see _mali_uk_get_pp_core_version_s in "mali_utgard_uk_types.h"
* @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure.
*/
_mali_osk_errcode_t _mali_ukk_get_pp_core_version( _mali_uk_get_pp_core_version_s *args );
/** @brief Disable Write-back unit(s) on specified job
*
* @param args see _mali_uk_get_pp_core_version_s in "mali_utgard_uk_types.h"
*/
void _mali_ukk_pp_job_disable_wb(_mali_uk_pp_disable_wb_s *args);
/** @} */ /* end group _mali_uk_pp */
/** @addtogroup _mali_uk_gp U/K Vertex Processor
*
* The Vertex Processor (aka GP (Geometry Processor)) functions provide the following functionality:
* - retrieving version of the Vertex Processors
* - determine number of Vertex Processors available
* - starting a job on a Vertex Processor
*
* @{ */
/** @brief Issue a request to start a new job on a Vertex Processor.
*
* If the request fails args->status is set to _MALI_UK_START_JOB_NOT_STARTED_DO_REQUEUE and you can
* try to start the job again.
*
* An existing job could be returned for requeueing if the new job has a higher priority than a previously started job
* which the hardware hasn't actually started processing yet. In this case the new job will be started and the
* existing one returned, otherwise the new job is started and the status field args->status is set to
* _MALI_UK_START_JOB_STARTED.
*
* Job completion can be awaited with _mali_ukk_wait_for_notification().
*
* @param ctx user-kernel context (mali_session)
* @param uargs see _mali_uk_gp_start_job_s in "mali_utgard_uk_types.h". Use _mali_osk_copy_from_user to retrieve data!
* @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure.
*/
_mali_osk_errcode_t _mali_ukk_gp_start_job( void *ctx, _mali_uk_gp_start_job_s *uargs );
/** @brief Returns the number of Vertex Processors in the system.
*
* @param args see _mali_uk_get_gp_number_of_cores_s in "mali_utgard_uk_types.h"
* @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure.
*/
_mali_osk_errcode_t _mali_ukk_get_gp_number_of_cores( _mali_uk_get_gp_number_of_cores_s *args );
/** @brief Returns the version that all Vertex Processor cores are compatible with.
*
* This function may only be called when _mali_uk_get_gp_number_of_cores() indicated at least one Vertex
* Processor core is available.
*
* @param args see _mali_uk_get_gp_core_version_s in "mali_utgard_uk_types.h"
* @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure.
*/
_mali_osk_errcode_t _mali_ukk_get_gp_core_version( _mali_uk_get_gp_core_version_s *args );
/** @brief Resume or abort suspended Vertex Processor jobs.
*
* After receiving notification that a Vertex Processor job was suspended from
* _mali_ukk_wait_for_notification() you can use this function to resume or abort the job.
*
* @param args see _mali_uk_gp_suspend_response_s in "mali_utgard_uk_types.h"
* @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure.
*/
_mali_osk_errcode_t _mali_ukk_gp_suspend_response( _mali_uk_gp_suspend_response_s *args );
/** @} */ /* end group _mali_uk_gp */
#if defined(CONFIG_MALI400_PROFILING)
/** @addtogroup _mali_uk_profiling U/K Timeline profiling module
* @{ */
/** @brief Start recording profiling events.
*
* @param args see _mali_uk_profiling_start_s in "mali_utgard_uk_types.h"
*/
_mali_osk_errcode_t _mali_ukk_profiling_start(_mali_uk_profiling_start_s *args);
/** @brief Add event to profiling buffer.
*
* @param args see _mali_uk_profiling_add_event_s in "mali_utgard_uk_types.h"
*/
_mali_osk_errcode_t _mali_ukk_profiling_add_event(_mali_uk_profiling_add_event_s *args);
/** @brief Stop recording profiling events.
*
* @param args see _mali_uk_profiling_stop_s in "mali_utgard_uk_types.h"
*/
_mali_osk_errcode_t _mali_ukk_profiling_stop(_mali_uk_profiling_stop_s *args);
/** @brief Retrieve a recorded profiling event.
*
* @param args see _mali_uk_profiling_get_event_s in "mali_utgard_uk_types.h"
*/
_mali_osk_errcode_t _mali_ukk_profiling_get_event(_mali_uk_profiling_get_event_s *args);
/** @brief Clear recorded profiling events.
*
* @param args see _mali_uk_profiling_clear_s in "mali_utgard_uk_types.h"
*/
_mali_osk_errcode_t _mali_ukk_profiling_clear(_mali_uk_profiling_clear_s *args);
/** @} */ /* end group _mali_uk_profiling */
#endif
/** @addtogroup _mali_uk_vsync U/K VSYNC reporting module
* @{ */
/** @brief Report events related to vsync.
*
* @note Events should be reported when starting to wait for vsync and when the
* waiting is finished. This information can then be used in kernel space to
* complement the GPU utilization metric.
*
* @param args see _mali_uk_vsync_event_report_s in "mali_utgard_uk_types.h"
*/
_mali_osk_errcode_t _mali_ukk_vsync_event_report(_mali_uk_vsync_event_report_s *args);
/** @} */ /* end group _mali_uk_vsync */
/** @addtogroup _mali_sw_counters_report U/K Software counter reporting
* @{ */
/** @brief Report software counters.
*
* @param args see _mali_uk_sw_counters_report_s in "mali_uk_types.h"
*/
_mali_osk_errcode_t _mali_ukk_sw_counters_report(_mali_uk_sw_counters_report_s *args);
/** @} */ /* end group _mali_sw_counters_report */
/** @} */ /* end group u_k_api */
/** @} */ /* end group uddapi */
u32 _mali_ukk_report_memory_usage(void);
u32 _mali_ukk_utilization_gp_pp(void);
u32 _mali_ukk_utilization_gp(void);
u32 _mali_ukk_utilization_pp(void);
#ifdef __cplusplus
}
#endif
#endif /* __MALI_UKK_H__ */

View File

@@ -0,0 +1,146 @@
/**
* Copyright (C) 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.
*/
#include "mali_osk.h"
#include "mali_kernel_common.h"
#include "mali_uk_types.h"
#include "mali_user_settings_db.h"
#include "mali_session.h"
static u32 mali_user_settings[_MALI_UK_USER_SETTING_MAX];
const char *_mali_uk_user_setting_descriptions[] = _MALI_UK_USER_SETTING_DESCRIPTIONS;
static void mali_user_settings_notify(_mali_uk_user_setting_t setting, u32 value)
{
mali_bool done = MALI_FALSE;
/*
* This function gets a bit complicated because we can't hold the session lock while
* allocating notification objects.
*/
while (!done) {
u32 i;
u32 num_sessions_alloc;
u32 num_sessions_with_lock;
u32 used_notification_objects = 0;
_mali_osk_notification_t **notobjs;
/* Pre allocate the number of notifications objects we need right now (might change after lock has been taken) */
num_sessions_alloc = mali_session_get_count();
if (0 == num_sessions_alloc) {
/* No sessions to report to */
return;
}
notobjs = (_mali_osk_notification_t **)_mali_osk_malloc(sizeof(_mali_osk_notification_t *) * num_sessions_alloc);
if (NULL == notobjs) {
MALI_PRINT_ERROR(("Failed to notify user space session about num PP core change (alloc failure)\n"));
return;
}
for (i = 0; i < num_sessions_alloc; i++) {
notobjs[i] = _mali_osk_notification_create(_MALI_NOTIFICATION_SETTINGS_CHANGED,
sizeof(_mali_uk_settings_changed_s));
if (NULL != notobjs[i]) {
_mali_uk_settings_changed_s *data;
data = notobjs[i]->result_buffer;
data->setting = setting;
data->value = value;
} else {
MALI_PRINT_ERROR(("Failed to notify user space session about setting change (alloc failure %u)\n", i));
}
}
mali_session_lock();
/* number of sessions will not change while we hold the lock */
num_sessions_with_lock = mali_session_get_count();
if (num_sessions_alloc >= num_sessions_with_lock) {
/* We have allocated enough notification objects for all the sessions atm */
struct mali_session_data *session, *tmp;
MALI_SESSION_FOREACH(session, tmp, link) {
MALI_DEBUG_ASSERT(used_notification_objects < num_sessions_alloc);
if (NULL != notobjs[used_notification_objects]) {
mali_session_send_notification(session, notobjs[used_notification_objects]);
notobjs[used_notification_objects] = NULL; /* Don't track this notification object any more */
}
used_notification_objects++;
}
done = MALI_TRUE;
}
mali_session_unlock();
/* Delete any remaining/unused notification objects */
for (; used_notification_objects < num_sessions_alloc; used_notification_objects++) {
if (NULL != notobjs[used_notification_objects]) {
_mali_osk_notification_delete(notobjs[used_notification_objects]);
}
}
_mali_osk_free(notobjs);
}
}
void mali_set_user_setting(_mali_uk_user_setting_t setting, u32 value)
{
mali_bool notify = MALI_FALSE;
if (setting >= _MALI_UK_USER_SETTING_MAX) {
MALI_DEBUG_PRINT_ERROR(("Invalid user setting %ud\n"));
return;
}
if (mali_user_settings[setting] != value) {
notify = MALI_TRUE;
}
mali_user_settings[setting] = value;
if (notify) {
mali_user_settings_notify(setting, value);
}
}
u32 mali_get_user_setting(_mali_uk_user_setting_t setting)
{
if (setting >= _MALI_UK_USER_SETTING_MAX) {
return 0;
}
return mali_user_settings[setting];
}
_mali_osk_errcode_t _mali_ukk_get_user_setting(_mali_uk_get_user_setting_s *args)
{
_mali_uk_user_setting_t setting;
MALI_DEBUG_ASSERT_POINTER(args);
setting = args->setting;
if (_MALI_UK_USER_SETTING_MAX > setting) {
args->value = mali_user_settings[setting];
return _MALI_OSK_ERR_OK;
} else {
return _MALI_OSK_ERR_INVALID_ARGS;
}
}
_mali_osk_errcode_t _mali_ukk_get_user_settings(_mali_uk_get_user_settings_s *args)
{
MALI_DEBUG_ASSERT_POINTER(args);
_mali_osk_memcpy(args->settings, mali_user_settings, sizeof(mali_user_settings));
return _MALI_OSK_ERR_OK;
}

View File

@@ -0,0 +1,39 @@
/**
* Copyright (C) 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.
*/
#ifndef __MALI_USER_SETTINGS_DB_H__
#define __MALI_USER_SETTINGS_DB_H__
#ifdef __cplusplus
extern "C" {
#endif
#include "mali_uk_types.h"
/** @brief Set Mali user setting in DB
*
* Update the DB with a new value for \a setting. If the value is different from theprevious set value running sessions will be notified of the change.
*
* @param setting the setting to be changed
* @param value the new value to set
*/
void mali_set_user_setting(_mali_uk_user_setting_t setting, u32 value);
/** @brief Get current Mali user setting value from DB
*
* @param setting the setting to extract
* @return the value of the selected setting
*/
u32 mali_get_user_setting(_mali_uk_user_setting_t setting);
#ifdef __cplusplus
}
#endif
#endif /* __MALI_KERNEL_USER_SETTING__ */

View File

@@ -0,0 +1,418 @@
/*
* Copyright (C) 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 mali_utgard.h
* Defines types and interface exposed by the Mali Utgard device driver
*/
#ifndef __MALI_UTGARD_H__
#define __MALI_UTGARD_H__
#include "mali_osk_types.h"
#define MALI_GPU_NAME_UTGARD "mali-utgard"
/* Mali-200 */
#define MALI_GPU_RESOURCES_MALI200(base_addr, gp_irq, pp_irq, mmu_irq) \
MALI_GPU_RESOURCE_PP(base_addr + 0x0000, pp_irq) \
MALI_GPU_RESOURCE_GP(base_addr + 0x2000, gp_irq) \
MALI_GPU_RESOURCE_MMU(base_addr + 0x3000, mmu_irq)
/* Mali-300 */
#define MALI_GPU_RESOURCES_MALI300(base_addr, gp_irq, gp_mmu_irq, pp_irq, pp_mmu_irq) \
MALI_GPU_RESOURCES_MALI400_MP1(base_addr, gp_irq, gp_mmu_irq, pp_irq, pp_mmu_irq)
#define MALI_GPU_RESOURCES_MALI300_PMU(base_addr, gp_irq, gp_mmu_irq, pp_irq, pp_mmu_irq) \
MALI_GPU_RESOURCES_MALI400_MP1_PMU(base_addr, gp_irq, gp_mmu_irq, pp_irq, pp_mmu_irq)
/* Mali-400 */
#define MALI_GPU_RESOURCES_MALI400_MP1(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq) \
MALI_GPU_RESOURCE_L2(base_addr + 0x1000) \
MALI_GPU_RESOURCE_GP_WITH_MMU(base_addr + 0x0000, gp_irq, base_addr + 0x3000, gp_mmu_irq) \
MALI_GPU_RESOURCE_PP_WITH_MMU(0, base_addr + 0x8000, pp0_irq, base_addr + 0x4000, pp0_mmu_irq)
#define MALI_GPU_RESOURCES_MALI400_MP1_PMU(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq) \
MALI_GPU_RESOURCES_MALI400_MP1(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq) \
MALI_GPU_RESOURCE_PMU(base_addr + 0x2000)
#define MALI_GPU_RESOURCES_MALI400_MP2(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq) \
MALI_GPU_RESOURCE_L2(base_addr + 0x1000) \
MALI_GPU_RESOURCE_GP_WITH_MMU(base_addr + 0x0000, gp_irq, base_addr + 0x3000, gp_mmu_irq) \
MALI_GPU_RESOURCE_PP_WITH_MMU(0, base_addr + 0x8000, pp0_irq, base_addr + 0x4000, pp0_mmu_irq) \
MALI_GPU_RESOURCE_PP_WITH_MMU(1, base_addr + 0xA000, pp1_irq, base_addr + 0x5000, pp1_mmu_irq)
#define MALI_GPU_RESOURCES_MALI400_MP2_PMU(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq) \
MALI_GPU_RESOURCES_MALI400_MP2(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq) \
MALI_GPU_RESOURCE_PMU(base_addr + 0x2000)
#define MALI_GPU_RESOURCES_MALI400_MP3(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq) \
MALI_GPU_RESOURCE_L2(base_addr + 0x1000) \
MALI_GPU_RESOURCE_GP_WITH_MMU(base_addr + 0x0000, gp_irq, base_addr + 0x3000, gp_mmu_irq) \
MALI_GPU_RESOURCE_PP_WITH_MMU(0, base_addr + 0x8000, pp0_irq, base_addr + 0x4000, pp0_mmu_irq) \
MALI_GPU_RESOURCE_PP_WITH_MMU(1, base_addr + 0xA000, pp1_irq, base_addr + 0x5000, pp1_mmu_irq) \
MALI_GPU_RESOURCE_PP_WITH_MMU(2, base_addr + 0xC000, pp2_irq, base_addr + 0x6000, pp2_mmu_irq)
#define MALI_GPU_RESOURCES_MALI400_MP3_PMU(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq) \
MALI_GPU_RESOURCES_MALI400_MP3(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq) \
MALI_GPU_RESOURCE_PMU(base_addr + 0x2000)
#define MALI_GPU_RESOURCES_MALI400_MP4(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq, pp3_irq, pp3_mmu_irq) \
MALI_GPU_RESOURCE_L2(base_addr + 0x1000) \
MALI_GPU_RESOURCE_GP_WITH_MMU(base_addr + 0x0000, gp_irq, base_addr + 0x3000, gp_mmu_irq) \
MALI_GPU_RESOURCE_PP_WITH_MMU(0, base_addr + 0x8000, pp0_irq, base_addr + 0x4000, pp0_mmu_irq) \
MALI_GPU_RESOURCE_PP_WITH_MMU(1, base_addr + 0xA000, pp1_irq, base_addr + 0x5000, pp1_mmu_irq) \
MALI_GPU_RESOURCE_PP_WITH_MMU(2, base_addr + 0xC000, pp2_irq, base_addr + 0x6000, pp2_mmu_irq) \
MALI_GPU_RESOURCE_PP_WITH_MMU(3, base_addr + 0xE000, pp3_irq, base_addr + 0x7000, pp3_mmu_irq)
#define MALI_GPU_RESOURCES_MALI400_MP4_PMU(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq, pp3_irq, pp3_mmu_irq) \
MALI_GPU_RESOURCES_MALI400_MP4(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq, pp3_irq, pp3_mmu_irq) \
MALI_GPU_RESOURCE_PMU(base_addr + 0x2000)
/* Mali-450 */
#define MALI_GPU_RESOURCES_MALI450_MP2(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp_bcast_irq) \
MALI_GPU_RESOURCE_L2(base_addr + 0x10000) \
MALI_GPU_RESOURCE_GP_WITH_MMU(base_addr + 0x00000, gp_irq, base_addr + 0x03000, gp_mmu_irq) \
MALI_GPU_RESOURCE_L2(base_addr + 0x01000) \
MALI_GPU_RESOURCE_PP_WITH_MMU(0, base_addr + 0x08000, pp0_irq, base_addr + 0x04000, pp0_mmu_irq) \
MALI_GPU_RESOURCE_PP_WITH_MMU(1, base_addr + 0x0A000, pp1_irq, base_addr + 0x05000, pp1_mmu_irq) \
MALI_GPU_RESOURCE_BCAST(base_addr + 0x13000) \
MALI_GPU_RESOURCE_DLBU(base_addr + 0x14000) \
MALI_GPU_RESOURCE_PP_BCAST(base_addr + 0x16000, pp_bcast_irq) \
MALI_GPU_RESOURCE_PP_MMU_BCAST(base_addr + 0x15000) \
MALI_GPU_RESOURCE_DMA(base_addr + 0x12000)
#define MALI_GPU_RESOURCES_MALI450_MP2_PMU(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp_bcast_irq) \
MALI_GPU_RESOURCES_MALI450_MP2(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp_bcast_irq) \
MALI_GPU_RESOURCE_PMU(base_addr + 0x2000) \
#define MALI_GPU_RESOURCES_MALI450_MP3(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq, pp_bcast_irq) \
MALI_GPU_RESOURCE_L2(base_addr + 0x10000) \
MALI_GPU_RESOURCE_GP_WITH_MMU(base_addr + 0x00000, gp_irq, base_addr + 0x03000, gp_mmu_irq) \
MALI_GPU_RESOURCE_L2(base_addr + 0x01000) \
MALI_GPU_RESOURCE_PP_WITH_MMU(0, base_addr + 0x08000, pp0_irq, base_addr + 0x04000, pp0_mmu_irq) \
MALI_GPU_RESOURCE_PP_WITH_MMU(1, base_addr + 0x0A000, pp1_irq, base_addr + 0x05000, pp1_mmu_irq) \
MALI_GPU_RESOURCE_PP_WITH_MMU(2, base_addr + 0x0C000, pp2_irq, base_addr + 0x06000, pp2_mmu_irq) \
MALI_GPU_RESOURCE_BCAST(base_addr + 0x13000) \
MALI_GPU_RESOURCE_DLBU(base_addr + 0x14000) \
MALI_GPU_RESOURCE_PP_BCAST(base_addr + 0x16000, pp_bcast_irq) \
MALI_GPU_RESOURCE_PP_MMU_BCAST(base_addr + 0x15000)
#define MALI_GPU_RESOURCES_MALI450_MP3_PMU(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq, pp_bcast_irq) \
MALI_GPU_RESOURCES_MALI450_MP3(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq, pp_bcast_irq) \
MALI_GPU_RESOURCE_PMU(base_addr + 0x2000) \
#define MALI_GPU_RESOURCES_MALI450_MP4(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq, pp3_irq, pp3_mmu_irq, pp_bcast_irq) \
MALI_GPU_RESOURCE_L2(base_addr + 0x10000) \
MALI_GPU_RESOURCE_GP_WITH_MMU(base_addr + 0x00000, gp_irq, base_addr + 0x03000, gp_mmu_irq) \
MALI_GPU_RESOURCE_L2(base_addr + 0x01000) \
MALI_GPU_RESOURCE_PP_WITH_MMU(0, base_addr + 0x08000, pp0_irq, base_addr + 0x04000, pp0_mmu_irq) \
MALI_GPU_RESOURCE_PP_WITH_MMU(1, base_addr + 0x0A000, pp1_irq, base_addr + 0x05000, pp1_mmu_irq) \
MALI_GPU_RESOURCE_PP_WITH_MMU(2, base_addr + 0x0C000, pp2_irq, base_addr + 0x06000, pp2_mmu_irq) \
MALI_GPU_RESOURCE_PP_WITH_MMU(3, base_addr + 0x0E000, pp3_irq, base_addr + 0x07000, pp3_mmu_irq) \
MALI_GPU_RESOURCE_BCAST(base_addr + 0x13000) \
MALI_GPU_RESOURCE_DLBU(base_addr + 0x14000) \
MALI_GPU_RESOURCE_PP_BCAST(base_addr + 0x16000, pp_bcast_irq) \
MALI_GPU_RESOURCE_PP_MMU_BCAST(base_addr + 0x15000) \
MALI_GPU_RESOURCE_DMA(base_addr + 0x12000)
#define MALI_GPU_RESOURCES_MALI450_MP4_PMU(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq, pp3_irq, pp3_mmu_irq, pp_bcast_irq) \
MALI_GPU_RESOURCES_MALI450_MP4(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq, pp3_irq, pp3_mmu_irq, pp_bcast_irq) \
MALI_GPU_RESOURCE_PMU(base_addr + 0x2000) \
#define MALI_GPU_RESOURCES_MALI450_MP6(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq, pp3_irq, pp3_mmu_irq, pp4_irq, pp4_mmu_irq, pp5_irq, pp5_mmu_irq, pp_bcast_irq) \
MALI_GPU_RESOURCE_L2(base_addr + 0x10000) \
MALI_GPU_RESOURCE_GP_WITH_MMU(base_addr + 0x00000, gp_irq, base_addr + 0x03000, gp_mmu_irq) \
MALI_GPU_RESOURCE_L2(base_addr + 0x01000) \
MALI_GPU_RESOURCE_PP_WITH_MMU(0, base_addr + 0x08000, pp0_irq, base_addr + 0x04000, pp0_mmu_irq) \
MALI_GPU_RESOURCE_PP_WITH_MMU(1, base_addr + 0x0A000, pp1_irq, base_addr + 0x05000, pp1_mmu_irq) \
MALI_GPU_RESOURCE_PP_WITH_MMU(2, base_addr + 0x0C000, pp2_irq, base_addr + 0x06000, pp2_mmu_irq) \
MALI_GPU_RESOURCE_L2(base_addr + 0x11000) \
MALI_GPU_RESOURCE_PP_WITH_MMU(3, base_addr + 0x28000, pp3_irq, base_addr + 0x1C000, pp3_mmu_irq) \
MALI_GPU_RESOURCE_PP_WITH_MMU(4, base_addr + 0x2A000, pp4_irq, base_addr + 0x1D000, pp4_mmu_irq) \
MALI_GPU_RESOURCE_PP_WITH_MMU(5, base_addr + 0x2C000, pp5_irq, base_addr + 0x1E000, pp5_mmu_irq) \
MALI_GPU_RESOURCE_BCAST(base_addr + 0x13000) \
MALI_GPU_RESOURCE_DLBU(base_addr + 0x14000) \
MALI_GPU_RESOURCE_PP_BCAST(base_addr + 0x16000, pp_bcast_irq) \
MALI_GPU_RESOURCE_PP_MMU_BCAST(base_addr + 0x15000) \
MALI_GPU_RESOURCE_DMA(base_addr + 0x12000)
#define MALI_GPU_RESOURCES_MALI450_MP6_PMU(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq, pp3_irq, pp3_mmu_irq, pp4_irq, pp4_mmu_irq, pp5_irq, pp5_mmu_irq, pp_bcast_irq) \
MALI_GPU_RESOURCES_MALI450_MP6(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq, pp3_irq, pp3_mmu_irq, pp4_irq, pp4_mmu_irq, pp5_irq, pp5_mmu_irq, pp_bcast_irq) \
MALI_GPU_RESOURCE_PMU(base_addr + 0x2000) \
#define MALI_GPU_RESOURCES_MALI450_MP8(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq, pp3_irq, pp3_mmu_irq, pp4_irq, pp4_mmu_irq, pp5_irq, pp5_mmu_irq, pp6_irq, pp6_mmu_irq, pp7_irq, pp7_mmu_irq, pp_bcast_irq) \
MALI_GPU_RESOURCE_L2(base_addr + 0x10000) \
MALI_GPU_RESOURCE_GP_WITH_MMU(base_addr + 0x00000, gp_irq, base_addr + 0x03000, gp_mmu_irq) \
MALI_GPU_RESOURCE_L2(base_addr + 0x01000) \
MALI_GPU_RESOURCE_PP_WITH_MMU(0, base_addr + 0x08000, pp0_irq, base_addr + 0x04000, pp0_mmu_irq) \
MALI_GPU_RESOURCE_PP_WITH_MMU(1, base_addr + 0x0A000, pp1_irq, base_addr + 0x05000, pp1_mmu_irq) \
MALI_GPU_RESOURCE_PP_WITH_MMU(2, base_addr + 0x0C000, pp2_irq, base_addr + 0x06000, pp2_mmu_irq) \
MALI_GPU_RESOURCE_PP_WITH_MMU(3, base_addr + 0x0E000, pp3_irq, base_addr + 0x07000, pp3_mmu_irq) \
MALI_GPU_RESOURCE_L2(base_addr + 0x11000) \
MALI_GPU_RESOURCE_PP_WITH_MMU(4, base_addr + 0x28000, pp4_irq, base_addr + 0x1C000, pp4_mmu_irq) \
MALI_GPU_RESOURCE_PP_WITH_MMU(5, base_addr + 0x2A000, pp5_irq, base_addr + 0x1D000, pp5_mmu_irq) \
MALI_GPU_RESOURCE_PP_WITH_MMU(6, base_addr + 0x2C000, pp6_irq, base_addr + 0x1E000, pp6_mmu_irq) \
MALI_GPU_RESOURCE_PP_WITH_MMU(7, base_addr + 0x2E000, pp7_irq, base_addr + 0x1F000, pp7_mmu_irq) \
MALI_GPU_RESOURCE_BCAST(base_addr + 0x13000) \
MALI_GPU_RESOURCE_DLBU(base_addr + 0x14000) \
MALI_GPU_RESOURCE_PP_BCAST(base_addr + 0x16000, pp_bcast_irq) \
MALI_GPU_RESOURCE_PP_MMU_BCAST(base_addr + 0x15000) \
MALI_GPU_RESOURCE_DMA(base_addr + 0x12000)
#define MALI_GPU_RESOURCES_MALI450_MP8_PMU(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq, pp3_irq, pp3_mmu_irq, pp4_irq, pp4_mmu_irq, pp5_irq, pp5_mmu_irq, pp6_irq, pp6_mmu_irq, pp7_irq, pp7_mmu_irq, pp_bcast_irq) \
MALI_GPU_RESOURCES_MALI450_MP8(base_addr, gp_irq, gp_mmu_irq, pp0_irq, pp0_mmu_irq, pp1_irq, pp1_mmu_irq, pp2_irq, pp2_mmu_irq, pp3_irq, pp3_mmu_irq, pp4_irq, pp4_mmu_irq, pp5_irq, pp5_mmu_irq, pp6_irq, pp6_mmu_irq, pp7_irq, pp7_mmu_irq, pp_bcast_irq) \
MALI_GPU_RESOURCE_PMU(base_addr + 0x2000) \
#define MALI_GPU_RESOURCE_L2(addr) \
{ \
.name = "Mali_L2", \
.flags = IORESOURCE_MEM, \
.start = addr, \
.end = addr + 0x200, \
},
#define MALI_GPU_RESOURCE_GP(gp_addr, gp_irq) \
{ \
.name = "Mali_GP", \
.flags = IORESOURCE_MEM, \
.start = gp_addr, \
.end = gp_addr + 0x100, \
}, \
{ \
.name = "Mali_GP_IRQ", \
.flags = IORESOURCE_IRQ, \
.start = gp_irq, \
.end = gp_irq, \
}, \
#define MALI_GPU_RESOURCE_GP_WITH_MMU(gp_addr, gp_irq, gp_mmu_addr, gp_mmu_irq) \
{ \
.name = "Mali_GP", \
.flags = IORESOURCE_MEM, \
.start = gp_addr, \
.end = gp_addr + 0x100, \
}, \
{ \
.name = "Mali_GP_IRQ", \
.flags = IORESOURCE_IRQ, \
.start = gp_irq, \
.end = gp_irq, \
}, \
{ \
.name = "Mali_GP_MMU", \
.flags = IORESOURCE_MEM, \
.start = gp_mmu_addr, \
.end = gp_mmu_addr + 0x100, \
}, \
{ \
.name = "Mali_GP_MMU_IRQ", \
.flags = IORESOURCE_IRQ, \
.start = gp_mmu_irq, \
.end = gp_mmu_irq, \
},
#define MALI_GPU_RESOURCE_PP(pp_addr, pp_irq) \
{ \
.name = "Mali_PP", \
.flags = IORESOURCE_MEM, \
.start = pp_addr, \
.end = pp_addr + 0x1100, \
}, \
{ \
.name = "Mali_PP_IRQ", \
.flags = IORESOURCE_IRQ, \
.start = pp_irq, \
.end = pp_irq, \
}, \
#define MALI_GPU_RESOURCE_PP_WITH_MMU(id, pp_addr, pp_irq, pp_mmu_addr, pp_mmu_irq) \
{ \
.name = "Mali_PP" #id, \
.flags = IORESOURCE_MEM, \
.start = pp_addr, \
.end = pp_addr + 0x1100, \
}, \
{ \
.name = "Mali_PP" #id "_IRQ", \
.flags = IORESOURCE_IRQ, \
.start = pp_irq, \
.end = pp_irq, \
}, \
{ \
.name = "Mali_PP" #id "_MMU", \
.flags = IORESOURCE_MEM, \
.start = pp_mmu_addr, \
.end = pp_mmu_addr + 0x100, \
}, \
{ \
.name = "Mali_PP" #id "_MMU_IRQ", \
.flags = IORESOURCE_IRQ, \
.start = pp_mmu_irq, \
.end = pp_mmu_irq, \
},
#define MALI_GPU_RESOURCE_MMU(mmu_addr, mmu_irq) \
{ \
.name = "Mali_MMU", \
.flags = IORESOURCE_MEM, \
.start = mmu_addr, \
.end = mmu_addr + 0x100, \
}, \
{ \
.name = "Mali_MMU_IRQ", \
.flags = IORESOURCE_IRQ, \
.start = mmu_irq, \
.end = mmu_irq, \
},
#define MALI_GPU_RESOURCE_PMU(pmu_addr) \
{ \
.name = "Mali_PMU", \
.flags = IORESOURCE_MEM, \
.start = pmu_addr, \
.end = pmu_addr + 0x100, \
},
#define MALI_GPU_RESOURCE_DMA(dma_addr) \
{ \
.name = "Mali_DMA", \
.flags = IORESOURCE_MEM, \
.start = dma_addr, \
.end = dma_addr + 0x100, \
},
#define MALI_GPU_RESOURCE_DLBU(dlbu_addr) \
{ \
.name = "Mali_DLBU", \
.flags = IORESOURCE_MEM, \
.start = dlbu_addr, \
.end = dlbu_addr + 0x100, \
},
#define MALI_GPU_RESOURCE_BCAST(bcast_addr) \
{ \
.name = "Mali_Broadcast", \
.flags = IORESOURCE_MEM, \
.start = bcast_addr, \
.end = bcast_addr + 0x100, \
},
#define MALI_GPU_RESOURCE_PP_BCAST(pp_addr, pp_irq) \
{ \
.name = "Mali_PP_Broadcast", \
.flags = IORESOURCE_MEM, \
.start = pp_addr, \
.end = pp_addr + 0x1100, \
}, \
{ \
.name = "Mali_PP_Broadcast_IRQ", \
.flags = IORESOURCE_IRQ, \
.start = pp_irq, \
.end = pp_irq, \
}, \
#define MALI_GPU_RESOURCE_PP_MMU_BCAST(pp_mmu_bcast_addr) \
{ \
.name = "Mali_PP_MMU_Broadcast", \
.flags = IORESOURCE_MEM, \
.start = pp_mmu_bcast_addr, \
.end = pp_mmu_bcast_addr + 0x100, \
},
struct mali_gpu_utilization_data {
unsigned int utilization_gpu; /* Utilization for GP and all PP cores combined, 0 = no utilization, 256 = full utilization */
unsigned int utilization_gp; /* Utilization for GP core only, 0 = no utilization, 256 = full utilization */
unsigned int utilization_pp; /* Utilization for all PP cores combined, 0 = no utilization, 256 = full utilization */
#if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY)
unsigned int number_of_window_jobs;
unsigned int number_of_window_jobs_under_pressure;
#endif
};
struct mali_gpu_device_data {
/* Dedicated GPU memory range (physical). */
unsigned long dedicated_mem_start;
unsigned long dedicated_mem_size;
/* Shared GPU memory */
unsigned long shared_mem_size;
/* Frame buffer memory to be accessible by Mali GPU (physical) */
unsigned long fb_start;
unsigned long fb_size;
/* Max runtime [ms] for jobs */
int max_job_runtime;
/* Report GPU utilization in this interval (specified in ms) */
unsigned long utilization_interval;
/* Function that will receive periodic GPU utilization numbers */
void (*utilization_callback)(struct mali_gpu_utilization_data *data);
/*
* Mali PMU switch delay.
* Only needed if the power gates are connected to the PMU in a high fanout
* network. This value is the number of Mali clock cycles it takes to
* enable the power gates and turn on the power mesh.
* This value will have no effect if a daisy chain implementation is used.
*/
u32 pmu_switch_delay;
/* Mali Dynamic power domain configuration in sequence from 0-11
* GP PP0 PP1 PP2 PP3 PP4 PP5 PP6 PP7, L2$0 L2$1 L2$2
*/
u16 pmu_domain_config[12];
/* Fuction that platform callback for freq tunning, needed when POWER_PERFORMANCE_POLICY enabled*/
int (*set_freq_callback)(unsigned int mhz);
};
/** @brief MALI GPU power down using MALI in-built PMU
*
* called to power down all cores
*/
int mali_pmu_powerdown(void);
/** @brief MALI GPU power up using MALI in-built PMU
*
* called to power up all cores
*/
int mali_pmu_powerup(void);
/**
* Pause the scheduling and power state changes of Mali device driver.
* mali_dev_resume() must always be called as soon as possible after this function
* in order to resume normal operation of the Mali driver.
*/
void mali_dev_pause(void);
/**
* Resume scheduling and allow power changes in Mali device driver.
* This must always be called after mali_dev_pause().
*/
void mali_dev_resume(void);
/** @brief Set the desired number of PP cores to use.
*
* The internal Mali PMU will be used, if present, to physically power off the PP cores.
*
* @param num_cores The number of desired cores
* @return 0 on success, otherwise error. -EINVAL means an invalid number of cores was specified.
*/
int mali_perf_set_num_pp_cores(unsigned int num_cores);
#endif

View File

@@ -0,0 +1,261 @@
/*
* Copyright (C) 2010-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.
*/
#ifndef _MALI_UTGARD_COUNTERS_H_
#define _MALI_UTGARD_COUNTERS_H_
typedef struct {
void *unused;
} mali_cinstr_counter_info;
typedef enum {
MALI_CINSTR_COUNTER_SOURCE_EGL = 0,
MALI_CINSTR_COUNTER_SOURCE_OPENGLES = 1000,
MALI_CINSTR_COUNTER_SOURCE_OPENVG = 2000,
MALI_CINSTR_COUNTER_SOURCE_GP = 3000,
MALI_CINSTR_COUNTER_SOURCE_PP = 4000,
} cinstr_counter_source;
#define MALI_CINSTR_EGL_FIRST_COUNTER MALI_CINSTR_COUNTER_SOURCE_EGL
#define MALI_CINSTR_EGL_LAST_COUNTER (MALI_CINSTR_COUNTER_SOURCE_EGL + 999)
#define MALI_CINSTR_GLES_FIRST_COUNTER MALI_CINSTR_COUNTER_SOURCE_OPENGLES
#define MALI_CINSTR_GLES_LAST_COUNTER (MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 999)
#define MALI_CINSTR_VG_FIRST_COUNTER MALI_CINSTR_COUNTER_SOURCE_OPENVG
#define MALI_CINSTR_VG_LAST_COUNTER (MALI_CINSTR_COUNTER_SOURCE_OPENVG + 999)
#define MALI_CINSTR_GP_FIRST_COUNTER MALI_CINSTR_COUNTER_SOURCE_GP
#define MALI_CINSTR_GP_LAST_COUNTER (MALI_CINSTR_COUNTER_SOURCE_GP + 999)
#define MALI_CINSTR_PP_FIRST_COUNTER MALI_CINSTR_COUNTER_SOURCE_PP
#define MALI_CINSTR_PP_LAST_COUNTER (MALI_CINSTR_COUNTER_SOURCE_PP + 999)
typedef enum {
/* EGL counters */
MALI_CINSTR_EGL_BLIT_TIME = MALI_CINSTR_COUNTER_SOURCE_EGL + 0,
/* Last counter in the EGL set */
MALI_CINSTR_EGL_MAX_COUNTER = MALI_CINSTR_COUNTER_SOURCE_EGL + 1,
/* GLES counters */
MALI_CINSTR_GLES_DRAW_ELEMENTS_CALLS = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 0,
MALI_CINSTR_GLES_DRAW_ELEMENTS_NUM_INDICES = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 1,
MALI_CINSTR_GLES_DRAW_ELEMENTS_NUM_TRANSFORMED = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 2,
MALI_CINSTR_GLES_DRAW_ARRAYS_CALLS = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 3,
MALI_CINSTR_GLES_DRAW_ARRAYS_NUM_TRANSFORMED = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 4,
MALI_CINSTR_GLES_DRAW_POINTS = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 5,
MALI_CINSTR_GLES_DRAW_LINES = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 6,
MALI_CINSTR_GLES_DRAW_LINE_LOOP = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 7,
MALI_CINSTR_GLES_DRAW_LINE_STRIP = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 8,
MALI_CINSTR_GLES_DRAW_TRIANGLES = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 9,
MALI_CINSTR_GLES_DRAW_TRIANGLE_STRIP = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 10,
MALI_CINSTR_GLES_DRAW_TRIANGLE_FAN = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 11,
MALI_CINSTR_GLES_NON_VBO_DATA_COPY_TIME = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 12,
MALI_CINSTR_GLES_UNIFORM_BYTES_COPIED_TO_MALI = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 13,
MALI_CINSTR_GLES_UPLOAD_TEXTURE_TIME = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 14,
MALI_CINSTR_GLES_UPLOAD_VBO_TIME = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 15,
MALI_CINSTR_GLES_NUM_FLUSHES = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 16,
MALI_CINSTR_GLES_NUM_VSHADERS_GENERATED = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 17,
MALI_CINSTR_GLES_NUM_FSHADERS_GENERATED = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 18,
MALI_CINSTR_GLES_VSHADER_GEN_TIME = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 19,
MALI_CINSTR_GLES_FSHADER_GEN_TIME = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 20,
MALI_CINSTR_GLES_INPUT_TRIANGLES = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 21,
MALI_CINSTR_GLES_VXCACHE_HIT = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 22,
MALI_CINSTR_GLES_VXCACHE_MISS = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 23,
MALI_CINSTR_GLES_VXCACHE_COLLISION = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 24,
MALI_CINSTR_GLES_CULLED_TRIANGLES = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 25,
MALI_CINSTR_GLES_CULLED_LINES = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 26,
MALI_CINSTR_GLES_BACKFACE_TRIANGLES = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 27,
MALI_CINSTR_GLES_GBCLIP_TRIANGLES = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 28,
MALI_CINSTR_GLES_GBCLIP_LINES = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 29,
MALI_CINSTR_GLES_TRIANGLES_DRAWN = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 30,
MALI_CINSTR_GLES_DRAWCALL_TIME = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 31,
MALI_CINSTR_GLES_TRIANGLES_COUNT = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 32,
MALI_CINSTR_GLES_INDEPENDENT_TRIANGLES_COUNT = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 33,
MALI_CINSTR_GLES_STRIP_TRIANGLES_COUNT = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 34,
MALI_CINSTR_GLES_FAN_TRIANGLES_COUNT = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 35,
MALI_CINSTR_GLES_LINES_COUNT = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 36,
MALI_CINSTR_GLES_INDEPENDENT_LINES_COUNT = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 37,
MALI_CINSTR_GLES_STRIP_LINES_COUNT = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 38,
MALI_CINSTR_GLES_LOOP_LINES_COUNT = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 39,
MALI_CINSTR_GLES_POINTS_COUNT = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 40,
/* Last counter in the GLES set */
MALI_CINSTR_GLES_MAX_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENGLES + 41,
/* OpenVG counters */
MALI_CINSTR_VG_MASK_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 0,
MALI_CINSTR_VG_CLEAR_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 1,
MALI_CINSTR_VG_APPEND_PATH_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 2,
MALI_CINSTR_VG_APPEND_PATH_DATA_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 3,
MALI_CINSTR_VG_MODIFY_PATH_COORDS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 4,
MALI_CINSTR_VG_TRANSFORM_PATH_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 5,
MALI_CINSTR_VG_INTERPOLATE_PATH_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 6,
MALI_CINSTR_VG_PATH_LENGTH_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 7,
MALI_CINSTR_VG_POINT_ALONG_PATH_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 8,
MALI_CINSTR_VG_PATH_BOUNDS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 9,
MALI_CINSTR_VG_PATH_TRANSFORMED_BOUNDS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 10,
MALI_CINSTR_VG_DRAW_PATH_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 11,
MALI_CINSTR_VG_CLEAR_IMAGE_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 12,
MALI_CINSTR_VG_IMAGE_SUB_DATA_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 13,
MALI_CINSTR_VG_GET_IMAGE_SUB_DATA_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 14,
MALI_CINSTR_VG_COPY_IMAGE_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 15,
MALI_CINSTR_VG_DRAW_IMAGE_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 16,
MALI_CINSTR_VG_SET_PIXELS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 17,
MALI_CINSTR_VG_WRITE_PIXELS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 18,
MALI_CINSTR_VG_GET_PIXELS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 19,
MALI_CINSTR_VG_READ_PIXELS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 20,
MALI_CINSTR_VG_COPY_PIXELS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 21,
MALI_CINSTR_VG_COLOR_MATRIX_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 22,
MALI_CINSTR_VG_CONVOLVE_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 23,
MALI_CINSTR_VG_SEPARABLE_CONVOLVE_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 24,
MALI_CINSTR_VG_GAUSSIAN_BLUR_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 25,
MALI_CINSTR_VG_LOOKUP_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 26,
MALI_CINSTR_VG_LOOKUP_SINGLE_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 27,
MALI_CINSTR_VG_CONTEXT_CREATE_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 28,
MALI_CINSTR_VG_STROKED_CUBICS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 29,
MALI_CINSTR_VG_STROKED_QUADS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 30,
MALI_CINSTR_VG_STROKED_ARCS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 31,
MALI_CINSTR_VG_STROKED_LINES_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 32,
MALI_CINSTR_VG_FILLED_CUBICS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 33,
MALI_CINSTR_VG_FILLED_QUADS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 34,
MALI_CINSTR_VG_FILLED_ARCS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 35,
MALI_CINSTR_VG_FILLED_LINES_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 36,
MALI_CINSTR_VG_DRAW_PATH_CALLS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 37,
MALI_CINSTR_VG_TRIANGLES_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 38,
MALI_CINSTR_VG_VERTICES_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 39,
MALI_CINSTR_VG_INDICES_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 40,
MALI_CINSTR_VG_FILLED_PATHS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 41,
MALI_CINSTR_VG_STROKED_PATHS_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 42,
MALI_CINSTR_VG_FILL_EXTRACT_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 43,
MALI_CINSTR_VG_DRAW_FILLED_PATH_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 44,
MALI_CINSTR_VG_STROKE_EXTRACT_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 45,
MALI_CINSTR_VG_DRAW_STROKED_PATH_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 46,
MALI_CINSTR_VG_DRAW_PAINT_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 47,
MALI_CINSTR_VG_DATA_STRUCTURES_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 48,
MALI_CINSTR_VG_MEM_PATH_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 49,
MALI_CINSTR_VG_RSW_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 50,
/* Last counter in the VG set */
MALI_CINSTR_VG_MAX_COUNTER = MALI_CINSTR_COUNTER_SOURCE_OPENVG + 51,
/* Mali GP counters */
MALI_CINSTR_GP_DEPRECATED_0 = MALI_CINSTR_COUNTER_SOURCE_GP + 0,
MALI_CINSTR_GP_ACTIVE_CYCLES_GP = MALI_CINSTR_COUNTER_SOURCE_GP + 1,
MALI_CINSTR_GP_ACTIVE_CYCLES_VERTEX_SHADER = MALI_CINSTR_COUNTER_SOURCE_GP + 2,
MALI_CINSTR_GP_ACTIVE_CYCLES_VERTEX_STORER = MALI_CINSTR_COUNTER_SOURCE_GP + 3,
MALI_CINSTR_GP_ACTIVE_CYCLES_VERTEX_LOADER = MALI_CINSTR_COUNTER_SOURCE_GP + 4,
MALI_CINSTR_GP_CYCLES_VERTEX_LOADER_WAITING_FOR_VERTEX_SHADER = MALI_CINSTR_COUNTER_SOURCE_GP + 5,
MALI_CINSTR_GP_NUMBER_OF_WORDS_READ = MALI_CINSTR_COUNTER_SOURCE_GP + 6,
MALI_CINSTR_GP_NUMBER_OF_WORDS_WRITTEN = MALI_CINSTR_COUNTER_SOURCE_GP + 7,
MALI_CINSTR_GP_NUMBER_OF_READ_BURSTS = MALI_CINSTR_COUNTER_SOURCE_GP + 8,
MALI_CINSTR_GP_NUMBER_OF_WRITE_BURSTS = MALI_CINSTR_COUNTER_SOURCE_GP + 9,
MALI_CINSTR_GP_NUMBER_OF_VERTICES_PROCESSED = MALI_CINSTR_COUNTER_SOURCE_GP + 10,
MALI_CINSTR_GP_NUMBER_OF_VERTICES_FETCHED = MALI_CINSTR_COUNTER_SOURCE_GP + 11,
MALI_CINSTR_GP_NUMBER_OF_PRIMITIVES_FETCHED = MALI_CINSTR_COUNTER_SOURCE_GP + 12,
MALI_CINSTR_GP_RESERVED_13 = MALI_CINSTR_COUNTER_SOURCE_GP + 13,
MALI_CINSTR_GP_NUMBER_OF_BACKFACE_CULLINGS_DONE = MALI_CINSTR_COUNTER_SOURCE_GP + 14,
MALI_CINSTR_GP_NUMBER_OF_COMMANDS_WRITTEN_TO_TILES = MALI_CINSTR_COUNTER_SOURCE_GP + 15,
MALI_CINSTR_GP_NUMBER_OF_MEMORY_BLOCKS_ALLOCATED = MALI_CINSTR_COUNTER_SOURCE_GP + 16,
MALI_CINSTR_GP_RESERVED_17 = MALI_CINSTR_COUNTER_SOURCE_GP + 17,
MALI_CINSTR_GP_RESERVED_18 = MALI_CINSTR_COUNTER_SOURCE_GP + 18,
MALI_CINSTR_GP_NUMBER_OF_VERTEX_LOADER_CACHE_MISSES = MALI_CINSTR_COUNTER_SOURCE_GP + 19,
MALI_CINSTR_GP_RESERVED_20 = MALI_CINSTR_COUNTER_SOURCE_GP + 20,
MALI_CINSTR_GP_RESERVED_21 = MALI_CINSTR_COUNTER_SOURCE_GP + 21,
MALI_CINSTR_GP_ACTIVE_CYCLES_VERTEX_SHADER_COMMAND_PROCESSOR = MALI_CINSTR_COUNTER_SOURCE_GP + 22,
MALI_CINSTR_GP_ACTIVE_CYCLES_PLBU_COMMAND_PROCESSOR = MALI_CINSTR_COUNTER_SOURCE_GP + 23,
MALI_CINSTR_GP_ACTIVE_CYCLES_PLBU_LIST_WRITER = MALI_CINSTR_COUNTER_SOURCE_GP + 24,
MALI_CINSTR_GP_ACTIVE_CYCLES_THROUGH_THE_PREPARE_LIST_COMMANDS = MALI_CINSTR_COUNTER_SOURCE_GP + 25,
MALI_CINSTR_GP_RESERVED_26 = MALI_CINSTR_COUNTER_SOURCE_GP + 26,
MALI_CINSTR_GP_ACTIVE_CYCLES_PRIMITIVE_ASSEMBLY = MALI_CINSTR_COUNTER_SOURCE_GP + 27,
MALI_CINSTR_GP_ACTIVE_CYCLES_PLBU_VERTEX_FETCHER = MALI_CINSTR_COUNTER_SOURCE_GP + 28,
MALI_CINSTR_GP_RESERVED_29 = MALI_CINSTR_COUNTER_SOURCE_GP + 29,
MALI_CINSTR_GP_ACTIVE_CYCLES_BOUNDINGBOX_AND_COMMAND_GENERATOR = MALI_CINSTR_COUNTER_SOURCE_GP + 30,
MALI_CINSTR_GP_RESERVED_31 = MALI_CINSTR_COUNTER_SOURCE_GP + 31,
MALI_CINSTR_GP_ACTIVE_CYCLES_SCISSOR_TILE_ITERATOR = MALI_CINSTR_COUNTER_SOURCE_GP + 32,
MALI_CINSTR_GP_ACTIVE_CYCLES_PLBU_TILE_ITERATOR = MALI_CINSTR_COUNTER_SOURCE_GP + 33,
MALI_CINSTR_GP_JOB_COUNT = MALI_CINSTR_COUNTER_SOURCE_GP + 900,
/* Mali PP counters */
MALI_CINSTR_PP_ACTIVE_CLOCK_CYCLES_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 0,
MALI_CINSTR_PP_TOTAL_CLOCK_CYCLES_COUNT_REMOVED = MALI_CINSTR_COUNTER_SOURCE_PP + 1,
MALI_CINSTR_PP_TOTAL_BUS_READS = MALI_CINSTR_COUNTER_SOURCE_PP + 2,
MALI_CINSTR_PP_TOTAL_BUS_WRITES = MALI_CINSTR_COUNTER_SOURCE_PP + 3,
MALI_CINSTR_PP_BUS_READ_REQUEST_CYCLES_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 4,
MALI_CINSTR_PP_BUS_WRITE_REQUEST_CYCLES_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 5,
MALI_CINSTR_PP_BUS_READ_TRANSACTIONS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 6,
MALI_CINSTR_PP_BUS_WRITE_TRANSACTIONS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 7,
MALI_CINSTR_PP_RESERVED_08 = MALI_CINSTR_COUNTER_SOURCE_PP + 8,
MALI_CINSTR_PP_TILE_WRITEBACK_WRITES = MALI_CINSTR_COUNTER_SOURCE_PP + 9,
MALI_CINSTR_PP_STORE_UNIT_WRITES = MALI_CINSTR_COUNTER_SOURCE_PP + 10,
MALI_CINSTR_PP_RESERVED_11 = MALI_CINSTR_COUNTER_SOURCE_PP + 11,
MALI_CINSTR_PP_PALETTE_CACHE_READS = MALI_CINSTR_COUNTER_SOURCE_PP + 12,
MALI_CINSTR_PP_TEXTURE_CACHE_UNCOMPRESSED_READS = MALI_CINSTR_COUNTER_SOURCE_PP + 13,
MALI_CINSTR_PP_POLYGON_LIST_READS = MALI_CINSTR_COUNTER_SOURCE_PP + 14,
MALI_CINSTR_PP_RSW_READS = MALI_CINSTR_COUNTER_SOURCE_PP + 15,
MALI_CINSTR_PP_VERTEX_CACHE_READS = MALI_CINSTR_COUNTER_SOURCE_PP + 16,
MALI_CINSTR_PP_UNIFORM_REMAPPING_READS = MALI_CINSTR_COUNTER_SOURCE_PP + 17,
MALI_CINSTR_PP_PROGRAM_CACHE_READS = MALI_CINSTR_COUNTER_SOURCE_PP + 18,
MALI_CINSTR_PP_VARYING_READS = MALI_CINSTR_COUNTER_SOURCE_PP + 19,
MALI_CINSTR_PP_TEXTURE_DESCRIPTORS_READS = MALI_CINSTR_COUNTER_SOURCE_PP + 20,
MALI_CINSTR_PP_TEXTURE_DESCRIPTORS_REMAPPING_READS = MALI_CINSTR_COUNTER_SOURCE_PP + 21,
MALI_CINSTR_PP_TEXTURE_CACHE_COMPRESSED_READS = MALI_CINSTR_COUNTER_SOURCE_PP + 22,
MALI_CINSTR_PP_LOAD_UNIT_READS = MALI_CINSTR_COUNTER_SOURCE_PP + 23,
MALI_CINSTR_PP_POLYGON_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 24,
MALI_CINSTR_PP_PIXEL_RECTANGLE_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 25,
MALI_CINSTR_PP_LINES_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 26,
MALI_CINSTR_PP_POINTS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 27,
MALI_CINSTR_PP_STALL_CYCLES_POLYGON_LIST_READER = MALI_CINSTR_COUNTER_SOURCE_PP + 28,
MALI_CINSTR_PP_STALL_CYCLES_TRIANGLE_SETUP = MALI_CINSTR_COUNTER_SOURCE_PP + 29,
MALI_CINSTR_PP_QUAD_RASTERIZED_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 30,
MALI_CINSTR_PP_FRAGMENT_RASTERIZED_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 31,
MALI_CINSTR_PP_FRAGMENT_REJECTED_FRAGMENT_KILL_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 32,
MALI_CINSTR_PP_FRAGMENT_REJECTED_FWD_FRAGMENT_KILL_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 33,
MALI_CINSTR_PP_FRAGMENT_PASSED_ZSTENCIL_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 34,
MALI_CINSTR_PP_PATCHES_REJECTED_EARLY_Z_STENCIL_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 35,
MALI_CINSTR_PP_PATCHES_EVALUATED = MALI_CINSTR_COUNTER_SOURCE_PP + 36,
MALI_CINSTR_PP_INSTRUCTION_COMPLETED_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 37,
MALI_CINSTR_PP_INSTRUCTION_FAILED_RENDEZVOUS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 38,
MALI_CINSTR_PP_INSTRUCTION_FAILED_VARYING_MISS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 39,
MALI_CINSTR_PP_INSTRUCTION_FAILED_TEXTURE_MISS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 40,
MALI_CINSTR_PP_INSTRUCTION_FAILED_LOAD_MISS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 41,
MALI_CINSTR_PP_INSTRUCTION_FAILED_TILE_READ_MISS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 42,
MALI_CINSTR_PP_INSTRUCTION_FAILED_STORE_MISS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 43,
MALI_CINSTR_PP_RENDEZVOUS_BREAKAGE_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 44,
MALI_CINSTR_PP_PIPELINE_BUBBLES_CYCLE_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 45,
MALI_CINSTR_PP_TEXTURE_MAPPER_MULTIPASS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 46,
MALI_CINSTR_PP_TEXTURE_MAPPER_CYCLE_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 47,
MALI_CINSTR_PP_VERTEX_CACHE_HIT_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 48,
MALI_CINSTR_PP_VERTEX_CACHE_MISS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 49,
MALI_CINSTR_PP_VARYING_CACHE_HIT_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 50,
MALI_CINSTR_PP_VARYING_CACHE_MISS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 51,
MALI_CINSTR_PP_VARYING_CACHE_CONFLICT_MISS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 52,
MALI_CINSTR_PP_TEXTURE_CACHE_HIT_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 53,
MALI_CINSTR_PP_TEXTURE_CACHE_MISS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 54,
MALI_CINSTR_PP_TEXTURE_CACHE_CONFLICT_MISS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 55,
MALI_CINSTR_PP_PALETTE_CACHE_HIT_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 56, /* Mali 200 only */
MALI_CINSTR_PP_PALETTE_CACHE_MISS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 57, /* Mali 200 only */
MALI_CINSTR_PP_COMPRESSED_TEXTURE_CACHE_HIT_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 56, /* Mali 400 class only */
MALI_CINSTR_PP_COMPRESSED_TEXTURE_CACHE_MISS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 57, /* Mali 400 class only */
MALI_CINSTR_PP_LOAD_STORE_CACHE_HIT_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 58,
MALI_CINSTR_PP_LOAD_STORE_CACHE_MISS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 59,
MALI_CINSTR_PP_PROGRAM_CACHE_HIT_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 60,
MALI_CINSTR_PP_PROGRAM_CACHE_MISS_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 61,
MALI_CINSTR_PP_JOB_COUNT = MALI_CINSTR_COUNTER_SOURCE_PP + 900,
} cinstr_counters_m200_t;
#endif /*_MALI_UTGARD_COUNTERS_H_*/

View File

@@ -0,0 +1,95 @@
/*
* Copyright (C) 2010-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.
*/
#ifndef __MALI_UTGARD_IOCTL_H__
#define __MALI_UTGARD_IOCTL_H__
#include <linux/types.h>
#include <linux/ioctl.h>
#include <linux/fs.h> /* file system operations */
#ifdef __cplusplus
extern "C" {
#endif
/**
* @file mali_kernel_ioctl.h
* Interface to the Linux device driver.
* This file describes the interface needed to use the Linux device driver.
* Its interface is designed to used by the HAL implementation through a thin arch layer.
*/
/**
* ioctl commands
*/
#define MALI_IOC_BASE 0x82
#define MALI_IOC_CORE_BASE (_MALI_UK_CORE_SUBSYSTEM + MALI_IOC_BASE)
#define MALI_IOC_MEMORY_BASE (_MALI_UK_MEMORY_SUBSYSTEM + MALI_IOC_BASE)
#define MALI_IOC_PP_BASE (_MALI_UK_PP_SUBSYSTEM + MALI_IOC_BASE)
#define MALI_IOC_GP_BASE (_MALI_UK_GP_SUBSYSTEM + MALI_IOC_BASE)
#define MALI_IOC_PROFILING_BASE (_MALI_UK_PROFILING_SUBSYSTEM + MALI_IOC_BASE)
#define MALI_IOC_VSYNC_BASE (_MALI_UK_VSYNC_SUBSYSTEM + MALI_IOC_BASE)
#define MALI_IOC_WAIT_FOR_NOTIFICATION _IOWR(MALI_IOC_CORE_BASE, _MALI_UK_WAIT_FOR_NOTIFICATION, _mali_uk_wait_for_notification_s *)
#define MALI_IOC_GET_API_VERSION _IOWR(MALI_IOC_CORE_BASE, _MALI_UK_GET_API_VERSION, _mali_uk_get_api_version_s *)
#define MALI_IOC_POST_NOTIFICATION _IOWR(MALI_IOC_CORE_BASE, _MALI_UK_POST_NOTIFICATION, _mali_uk_post_notification_s *)
#define MALI_IOC_GET_USER_SETTING _IOWR(MALI_IOC_CORE_BASE, _MALI_UK_GET_USER_SETTING, _mali_uk_get_user_setting_s *)
#define MALI_IOC_GET_USER_SETTINGS _IOWR(MALI_IOC_CORE_BASE, _MALI_UK_GET_USER_SETTINGS, _mali_uk_get_user_settings_s *)
#define MALI_IOC_REQUEST_HIGH_PRIORITY _IOW (MALI_IOC_CORE_BASE, _MALI_UK_REQUEST_HIGH_PRIORITY, _mali_uk_request_high_priority_s *)
#define MALI_IOC_TIMELINE_GET_LATEST_POINT _IOWR(MALI_IOC_CORE_BASE, _MALI_UK_TIMELINE_GET_LATEST_POINT, _mali_uk_timeline_get_latest_point_s *)
#define MALI_IOC_TIMELINE_WAIT _IOWR(MALI_IOC_CORE_BASE, _MALI_UK_TIMELINE_WAIT, _mali_uk_timeline_wait_s *)
#define MALI_IOC_TIMELINE_CREATE_SYNC_FENCE _IOWR(MALI_IOC_CORE_BASE, _MALI_UK_TIMELINE_CREATE_SYNC_FENCE, _mali_uk_timeline_create_sync_fence_s *)
#define MALI_IOC_SOFT_JOB_START _IOWR(MALI_IOC_CORE_BASE, _MALI_UK_SOFT_JOB_START, _mali_uk_soft_job_start_s *)
#define MALI_IOC_SOFT_JOB_SIGNAL _IOWR(MALI_IOC_CORE_BASE, _MALI_UK_SOFT_JOB_SIGNAL, _mali_uk_soft_job_signal_s *)
#define MALI_IOC_MEM_MAP_EXT _IOWR(MALI_IOC_MEMORY_BASE, _MALI_UK_MAP_EXT_MEM, _mali_uk_map_external_mem_s *)
#define MALI_IOC_MEM_UNMAP_EXT _IOW (MALI_IOC_MEMORY_BASE, _MALI_UK_UNMAP_EXT_MEM, _mali_uk_unmap_external_mem_s *)
#define MALI_IOC_MEM_ATTACH_DMA_BUF _IOWR(MALI_IOC_MEMORY_BASE, _MALI_UK_ATTACH_DMA_BUF, _mali_uk_attach_dma_buf_s *)
#define MALI_IOC_MEM_RELEASE_DMA_BUF _IOW(MALI_IOC_MEMORY_BASE, _MALI_UK_RELEASE_DMA_BUF, _mali_uk_release_dma_buf_s *)
#define MALI_IOC_MEM_DMA_BUF_GET_SIZE _IOR(MALI_IOC_MEMORY_BASE, _MALI_UK_DMA_BUF_GET_SIZE, _mali_uk_dma_buf_get_size_s *)
#define MALI_IOC_MEM_ATTACH_UMP _IOWR(MALI_IOC_MEMORY_BASE, _MALI_UK_ATTACH_UMP_MEM, _mali_uk_attach_ump_mem_s *)
#define MALI_IOC_MEM_RELEASE_UMP _IOW(MALI_IOC_MEMORY_BASE, _MALI_UK_RELEASE_UMP_MEM, _mali_uk_release_ump_mem_s *)
#define MALI_IOC_MEM_QUERY_MMU_PAGE_TABLE_DUMP_SIZE _IOR (MALI_IOC_MEMORY_BASE, _MALI_UK_QUERY_MMU_PAGE_TABLE_DUMP_SIZE, _mali_uk_query_mmu_page_table_dump_size_s *)
#define MALI_IOC_MEM_DUMP_MMU_PAGE_TABLE _IOWR(MALI_IOC_MEMORY_BASE, _MALI_UK_DUMP_MMU_PAGE_TABLE, _mali_uk_dump_mmu_page_table_s *)
#define MALI_IOC_MEM_WRITE_SAFE _IOWR(MALI_IOC_MEMORY_BASE, _MALI_UK_MEM_WRITE_SAFE, _mali_uk_mem_write_safe_s *)
#define MALI_IOC_PP_START_JOB _IOWR(MALI_IOC_PP_BASE, _MALI_UK_PP_START_JOB, _mali_uk_pp_start_job_s *)
#define MALI_IOC_PP_AND_GP_START_JOB _IOWR(MALI_IOC_PP_BASE, _MALI_UK_PP_AND_GP_START_JOB, _mali_uk_pp_and_gp_start_job_s *)
#define MALI_IOC_PP_NUMBER_OF_CORES_GET _IOR (MALI_IOC_PP_BASE, _MALI_UK_GET_PP_NUMBER_OF_CORES, _mali_uk_get_pp_number_of_cores_s *)
#define MALI_IOC_PP_CORE_VERSION_GET _IOR (MALI_IOC_PP_BASE, _MALI_UK_GET_PP_CORE_VERSION, _mali_uk_get_pp_core_version_s * )
#define MALI_IOC_PP_DISABLE_WB _IOW (MALI_IOC_PP_BASE, _MALI_UK_PP_DISABLE_WB, _mali_uk_pp_disable_wb_s * )
#define MALI_IOC_GP2_START_JOB _IOWR(MALI_IOC_GP_BASE, _MALI_UK_GP_START_JOB, _mali_uk_gp_start_job_s *)
#define MALI_IOC_GP2_NUMBER_OF_CORES_GET _IOR (MALI_IOC_GP_BASE, _MALI_UK_GET_GP_NUMBER_OF_CORES, _mali_uk_get_gp_number_of_cores_s *)
#define MALI_IOC_GP2_CORE_VERSION_GET _IOR (MALI_IOC_GP_BASE, _MALI_UK_GET_GP_CORE_VERSION, _mali_uk_get_gp_core_version_s *)
#define MALI_IOC_GP2_SUSPEND_RESPONSE _IOW (MALI_IOC_GP_BASE, _MALI_UK_GP_SUSPEND_RESPONSE,_mali_uk_gp_suspend_response_s *)
#define MALI_IOC_PROFILING_START _IOWR(MALI_IOC_PROFILING_BASE, _MALI_UK_PROFILING_START, _mali_uk_profiling_start_s *)
#define MALI_IOC_PROFILING_ADD_EVENT _IOWR(MALI_IOC_PROFILING_BASE, _MALI_UK_PROFILING_ADD_EVENT, _mali_uk_profiling_add_event_s*)
#define MALI_IOC_PROFILING_STOP _IOWR(MALI_IOC_PROFILING_BASE, _MALI_UK_PROFILING_STOP, _mali_uk_profiling_stop_s *)
#define MALI_IOC_PROFILING_GET_EVENT _IOWR(MALI_IOC_PROFILING_BASE, _MALI_UK_PROFILING_GET_EVENT, _mali_uk_profiling_get_event_s *)
#define MALI_IOC_PROFILING_CLEAR _IOWR(MALI_IOC_PROFILING_BASE, _MALI_UK_PROFILING_CLEAR, _mali_uk_profiling_clear_s *)
#define MALI_IOC_PROFILING_GET_CONFIG _IOWR(MALI_IOC_PROFILING_BASE, _MALI_UK_PROFILING_GET_CONFIG, _mali_uk_get_user_settings_s *)
#define MALI_IOC_PROFILING_REPORT_SW_COUNTERS _IOW (MALI_IOC_PROFILING_BASE, _MALI_UK_PROFILING_REPORT_SW_COUNTERS, _mali_uk_sw_counters_report_s *)
#define MALI_IOC_VSYNC_EVENT_REPORT _IOW (MALI_IOC_VSYNC_BASE, _MALI_UK_VSYNC_EVENT_REPORT, _mali_uk_vsync_event_report_s *)
/* Deprecated ioctls */
#define MALI_IOC_MEM_GET_BIG_BLOCK _IOWR(MALI_IOC_MEMORY_BASE, _MALI_UK_GET_BIG_BLOCK, void *)
#define MALI_IOC_MEM_FREE_BIG_BLOCK _IOW (MALI_IOC_MEMORY_BASE, _MALI_UK_FREE_BIG_BLOCK, void *)
#define MALI_IOC_MEM_INIT _IOR (MALI_IOC_MEMORY_BASE, _MALI_UK_INIT_MEM, void *)
#define MALI_IOC_MEM_TERM _IOW (MALI_IOC_MEMORY_BASE, _MALI_UK_TERM_MEM, void *)
#ifdef __cplusplus
}
#endif
#endif /* __MALI_UTGARD_IOCTL_H__ */

View File

@@ -0,0 +1,174 @@
/*
* Copyright (C) 2010-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.
*/
#ifndef _MALI_UTGARD_PROFILING_EVENTS_H_
#define _MALI_UTGARD_PROFILING_EVENTS_H_
/*
* The event ID is a 32 bit value consisting of different fields
* reserved, 4 bits, for future use
* event type, 4 bits, cinstr_profiling_event_type_t
* event channel, 8 bits, the source of the event.
* event data, 16 bit field, data depending on event type
*/
/**
* Specifies what kind of event this is
*/
typedef enum {
MALI_PROFILING_EVENT_TYPE_SINGLE = 0 << 24,
MALI_PROFILING_EVENT_TYPE_START = 1 << 24,
MALI_PROFILING_EVENT_TYPE_STOP = 2 << 24,
MALI_PROFILING_EVENT_TYPE_SUSPEND = 3 << 24,
MALI_PROFILING_EVENT_TYPE_RESUME = 4 << 24,
} cinstr_profiling_event_type_t;
/**
* Secifies the channel/source of the event
*/
typedef enum {
MALI_PROFILING_EVENT_CHANNEL_SOFTWARE = 0 << 16,
MALI_PROFILING_EVENT_CHANNEL_GP0 = 1 << 16,
MALI_PROFILING_EVENT_CHANNEL_PP0 = 5 << 16,
MALI_PROFILING_EVENT_CHANNEL_PP1 = 6 << 16,
MALI_PROFILING_EVENT_CHANNEL_PP2 = 7 << 16,
MALI_PROFILING_EVENT_CHANNEL_PP3 = 8 << 16,
MALI_PROFILING_EVENT_CHANNEL_PP4 = 9 << 16,
MALI_PROFILING_EVENT_CHANNEL_PP5 = 10 << 16,
MALI_PROFILING_EVENT_CHANNEL_PP6 = 11 << 16,
MALI_PROFILING_EVENT_CHANNEL_PP7 = 12 << 16,
MALI_PROFILING_EVENT_CHANNEL_GPU = 21 << 16,
} cinstr_profiling_event_channel_t;
#define MALI_PROFILING_MAKE_EVENT_CHANNEL_GP(num) (((MALI_PROFILING_EVENT_CHANNEL_GP0 >> 16) + (num)) << 16)
#define MALI_PROFILING_MAKE_EVENT_CHANNEL_PP(num) (((MALI_PROFILING_EVENT_CHANNEL_PP0 >> 16) + (num)) << 16)
/**
* These events are applicable when the type MALI_PROFILING_EVENT_TYPE_SINGLE is used from software channel
*/
typedef enum {
MALI_PROFILING_EVENT_REASON_SINGLE_SW_NONE = 0,
MALI_PROFILING_EVENT_REASON_SINGLE_SW_EGL_NEW_FRAME = 1,
MALI_PROFILING_EVENT_REASON_SINGLE_SW_FLUSH = 2,
MALI_PROFILING_EVENT_REASON_SINGLE_SW_EGL_SWAP_BUFFERS = 3,
MALI_PROFILING_EVENT_REASON_SINGLE_SW_FB_EVENT = 4,
MALI_PROFILING_EVENT_REASON_SINGLE_SW_GP_ENQUEUE = 5,
MALI_PROFILING_EVENT_REASON_SINGLE_SW_PP_ENQUEUE = 6,
MALI_PROFILING_EVENT_REASON_SINGLE_SW_READBACK = 7,
MALI_PROFILING_EVENT_REASON_SINGLE_SW_WRITEBACK = 8,
MALI_PROFILING_EVENT_REASON_SINGLE_SW_ENTER_API_FUNC = 10,
MALI_PROFILING_EVENT_REASON_SINGLE_SW_LEAVE_API_FUNC = 11,
MALI_PROFILING_EVENT_REASON_SINGLE_SW_DISCARD_ATTACHMENTS = 13,
MALI_PROFILING_EVENT_REASON_SINGLE_SW_UMP_TRY_LOCK = 53,
MALI_PROFILING_EVENT_REASON_SINGLE_SW_UMP_LOCK = 54,
MALI_PROFILING_EVENT_REASON_SINGLE_SW_UMP_UNLOCK = 55,
MALI_PROFILING_EVENT_REASON_SINGLE_LOCK_CONTENDED = 56,
MALI_PROFILING_EVENT_REASON_SINGLE_SW_EGL_MALI_FENCE_DUP = 57,
MALI_PROFILING_EVENT_REASON_SINGLE_SW_EGL_SET_PP_JOB_FENCE = 58,
MALI_PROFILING_EVENT_REASON_SINGLE_SW_EGL_WAIT_SYNC = 59,
MALI_PROFILING_EVENT_REASON_SINGLE_SW_EGL_CREATE_FENCE_SYNC = 60,
MALI_PROFILING_EVENT_REASON_SINGLE_SW_EGL_CREATE_NATIVE_FENCE_SYNC = 61,
MALI_PROFILING_EVENT_REASON_SINGLE_SW_EGL_FENCE_FLUSH = 62,
MALI_PROFILING_EVENT_REASON_SINGLE_SW_EGL_FLUSH_SERVER_WAITS= 63,
} cinstr_profiling_event_reason_single_sw_t;
/**
* These events are applicable when the type MALI_PROFILING_EVENT_TYPE_START/STOP is used from software channel
* to inform whether the core is physical or virtual
*/
typedef enum {
MALI_PROFILING_EVENT_REASON_START_STOP_HW_PHYSICAL = 0,
MALI_PROFILING_EVENT_REASON_START_STOP_HW_VIRTUAL = 1,
} cinstr_profiling_event_reason_start_stop_hw_t;
/**
* These events are applicable when the type MALI_PROFILING_EVENT_TYPE_START/STOP is used from software channel
*/
typedef enum {
/*MALI_PROFILING_EVENT_REASON_START_STOP_SW_NONE = 0,*/
MALI_PROFILING_EVENT_REASON_START_STOP_SW_MALI = 1,
MALI_PROFILING_EVENT_REASON_START_STOP_SW_CALLBACK_THREAD = 2,
MALI_PROFILING_EVENT_REASON_START_STOP_SW_WORKER_THREAD = 3,
MALI_PROFILING_EVENT_REASON_START_STOP_SW_BOTTOM_HALF = 4,
MALI_PROFILING_EVENT_REASON_START_STOP_SW_UPPER_HALF = 5,
} cinstr_profiling_event_reason_start_stop_sw_t;
/**
* These events are applicable when the type MALI_PROFILING_EVENT_TYPE_SUSPEND/RESUME is used from software channel
*/
typedef enum {
MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_NONE = 0, /* used */
MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_PIPELINE_FULL = 1, /* NOT used */
MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_VSYNC = 26, /* used in some build configurations */
MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_FB_IFRAME_WAIT = 27, /* USED */
MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_FB_IFRAME_SYNC = 28, /* USED */
MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_VG_WAIT_FILTER_CLEANUP = 29, /* used */
MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_VG_WAIT_TEXTURE = 30, /* used */
MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_GLES_WAIT_MIPLEVEL = 31, /* used */
MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_GLES_WAIT_READPIXELS = 32, /* used */
MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_EGL_WAIT_SWAP_IMMEDIATE = 33, /* NOT used */
MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_ICS_QUEUE_BUFFER = 34, /* USED */
MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_ICS_DEQUEUE_BUFFER = 35, /* USED */
MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_UMP_LOCK = 36, /* Not currently used */
MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_X11_GLOBAL_LOCK = 37, /* Not currently used */
MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_X11_SWAP = 38, /* Not currently used */
MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_MALI_EGL_IMAGE_SYNC_WAIT = 39, /* USED */
MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_GP_JOB_HANDLING = 40, /* USED */
MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_PP_JOB_HANDLING = 41, /* USED */
MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_EGL_MALI_FENCE_MERGE = 42, /* USED */
MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_EGL_MALI_FENCE_DUP = 43,
MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_EGL_FLUSH_SERVER_WAITS = 44,
MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_EGL_WAIT_SYNC = 45, /* USED */
} cinstr_profiling_event_reason_suspend_resume_sw_t;
/**
* These events are applicable when the type MALI_PROFILING_EVENT_TYPE_SINGLE is used from a HW channel (GPx+PPx)
*/
typedef enum {
MALI_PROFILING_EVENT_REASON_SINGLE_HW_NONE = 0,
MALI_PROFILING_EVENT_REASON_SINGLE_HW_INTERRUPT = 1,
MALI_PROFILING_EVENT_REASON_SINGLE_HW_FLUSH = 2,
} cinstr_profiling_event_reason_single_hw_t;
/**
* These events are applicable when the type MALI_PROFILING_EVENT_TYPE_SINGLE is used from the GPU channel
*/
typedef enum {
MALI_PROFILING_EVENT_REASON_SINGLE_GPU_NONE = 0,
MALI_PROFILING_EVENT_REASON_SINGLE_GPU_FREQ_VOLT_CHANGE = 1,
MALI_PROFILING_EVENT_REASON_SINGLE_GPU_L20_COUNTERS = 2,
MALI_PROFILING_EVENT_REASON_SINGLE_GPU_L21_COUNTERS = 3,
MALI_PROFILING_EVENT_REASON_SINGLE_GPU_L22_COUNTERS = 4,
} cinstr_profiling_event_reason_single_gpu_t;
/**
* These values are applicable for the 3rd data parameter when
* the type MALI_PROFILING_EVENT_TYPE_START is used from the software channel
* with the MALI_PROFILING_EVENT_REASON_START_STOP_BOTTOM_HALF reason.
*/
typedef enum {
MALI_PROFILING_EVENT_DATA_CORE_GP0 = 1,
MALI_PROFILING_EVENT_DATA_CORE_PP0 = 5,
MALI_PROFILING_EVENT_DATA_CORE_PP1 = 6,
MALI_PROFILING_EVENT_DATA_CORE_PP2 = 7,
MALI_PROFILING_EVENT_DATA_CORE_PP3 = 8,
MALI_PROFILING_EVENT_DATA_CORE_PP4 = 9,
MALI_PROFILING_EVENT_DATA_CORE_PP5 = 10,
MALI_PROFILING_EVENT_DATA_CORE_PP6 = 11,
MALI_PROFILING_EVENT_DATA_CORE_PP7 = 12,
} cinstr_profiling_event_data_core_t;
#define MALI_PROFILING_MAKE_EVENT_DATA_CORE_GP(num) (MALI_PROFILING_EVENT_DATA_CORE_GP0 + (num))
#define MALI_PROFILING_MAKE_EVENT_DATA_CORE_PP(num) (MALI_PROFILING_EVENT_DATA_CORE_PP0 + (num))
#endif /*_MALI_UTGARD_PROFILING_EVENTS_H_*/

View File

@@ -0,0 +1,197 @@
/*
* Copyright (C) 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.
*/
#ifndef __MALI_UTGARD_PROFILING_GATOR_API_H__
#define __MALI_UTGARD_PROFILING_GATOR_API_H__
#ifdef __cplusplus
extern "C" {
#endif
#define MALI_PROFILING_API_VERSION 4
#define MAX_NUM_L2_CACHE_CORES 3
#define MAX_NUM_FP_CORES 8
#define MAX_NUM_VP_CORES 1
/** The list of events supported by the Mali DDK. */
typedef enum {
/* Vertex processor activity */
ACTIVITY_VP_0 = 0,
/* Fragment processor activity */
ACTIVITY_FP_0,
ACTIVITY_FP_1,
ACTIVITY_FP_2,
ACTIVITY_FP_3,
ACTIVITY_FP_4,
ACTIVITY_FP_5,
ACTIVITY_FP_6,
ACTIVITY_FP_7,
/* L2 cache counters */
COUNTER_L2_0_C0,
COUNTER_L2_0_C1,
COUNTER_L2_1_C0,
COUNTER_L2_1_C1,
COUNTER_L2_2_C0,
COUNTER_L2_2_C1,
/* Vertex processor counters */
COUNTER_VP_0_C0,
COUNTER_VP_0_C1,
/* Fragment processor counters */
COUNTER_FP_0_C0,
COUNTER_FP_0_C1,
COUNTER_FP_1_C0,
COUNTER_FP_1_C1,
COUNTER_FP_2_C0,
COUNTER_FP_2_C1,
COUNTER_FP_3_C0,
COUNTER_FP_3_C1,
COUNTER_FP_4_C0,
COUNTER_FP_4_C1,
COUNTER_FP_5_C0,
COUNTER_FP_5_C1,
COUNTER_FP_6_C0,
COUNTER_FP_6_C1,
COUNTER_FP_7_C0,
COUNTER_FP_7_C1,
/*
* If more hardware counters are added, the _mali_osk_hw_counter_table
* below should also be updated.
*/
/* EGL software counters */
COUNTER_EGL_BLIT_TIME,
/* GLES software counters */
COUNTER_GLES_DRAW_ELEMENTS_CALLS,
COUNTER_GLES_DRAW_ELEMENTS_NUM_INDICES,
COUNTER_GLES_DRAW_ELEMENTS_NUM_TRANSFORMED,
COUNTER_GLES_DRAW_ARRAYS_CALLS,
COUNTER_GLES_DRAW_ARRAYS_NUM_TRANSFORMED,
COUNTER_GLES_DRAW_POINTS,
COUNTER_GLES_DRAW_LINES,
COUNTER_GLES_DRAW_LINE_LOOP,
COUNTER_GLES_DRAW_LINE_STRIP,
COUNTER_GLES_DRAW_TRIANGLES,
COUNTER_GLES_DRAW_TRIANGLE_STRIP,
COUNTER_GLES_DRAW_TRIANGLE_FAN,
COUNTER_GLES_NON_VBO_DATA_COPY_TIME,
COUNTER_GLES_UNIFORM_BYTES_COPIED_TO_MALI,
COUNTER_GLES_UPLOAD_TEXTURE_TIME,
COUNTER_GLES_UPLOAD_VBO_TIME,
COUNTER_GLES_NUM_FLUSHES,
COUNTER_GLES_NUM_VSHADERS_GENERATED,
COUNTER_GLES_NUM_FSHADERS_GENERATED,
COUNTER_GLES_VSHADER_GEN_TIME,
COUNTER_GLES_FSHADER_GEN_TIME,
COUNTER_GLES_INPUT_TRIANGLES,
COUNTER_GLES_VXCACHE_HIT,
COUNTER_GLES_VXCACHE_MISS,
COUNTER_GLES_VXCACHE_COLLISION,
COUNTER_GLES_CULLED_TRIANGLES,
COUNTER_GLES_CULLED_LINES,
COUNTER_GLES_BACKFACE_TRIANGLES,
COUNTER_GLES_GBCLIP_TRIANGLES,
COUNTER_GLES_GBCLIP_LINES,
COUNTER_GLES_TRIANGLES_DRAWN,
COUNTER_GLES_DRAWCALL_TIME,
COUNTER_GLES_TRIANGLES_COUNT,
COUNTER_GLES_INDEPENDENT_TRIANGLES_COUNT,
COUNTER_GLES_STRIP_TRIANGLES_COUNT,
COUNTER_GLES_FAN_TRIANGLES_COUNT,
COUNTER_GLES_LINES_COUNT,
COUNTER_GLES_INDEPENDENT_LINES_COUNT,
COUNTER_GLES_STRIP_LINES_COUNT,
COUNTER_GLES_LOOP_LINES_COUNT,
/* Framebuffer capture pseudo-counter */
COUNTER_FILMSTRIP,
NUMBER_OF_EVENTS
} _mali_osk_counter_id;
#define FIRST_ACTIVITY_EVENT ACTIVITY_VP_0
#define LAST_ACTIVITY_EVENT ACTIVITY_FP_7
#define FIRST_HW_COUNTER COUNTER_L2_0_C0
#define LAST_HW_COUNTER COUNTER_FP_7_C1
#define FIRST_SW_COUNTER COUNTER_EGL_BLIT_TIME
#define LAST_SW_COUNTER COUNTER_GLES_LOOP_LINES_COUNT
#define FIRST_SPECIAL_COUNTER COUNTER_FILMSTRIP
#define LAST_SPECIAL_COUNTER COUNTER_FILMSTRIP
/**
* Structure to pass performance counter data of a Mali core
*/
typedef struct _mali_profiling_core_counters {
u32 source0;
u32 value0;
u32 source1;
u32 value1;
} _mali_profiling_core_counters;
/**
* Structure to pass performance counter data of Mali L2 cache cores
*/
typedef struct _mali_profiling_l2_counter_values {
struct _mali_profiling_core_counters cores[MAX_NUM_L2_CACHE_CORES];
} _mali_profiling_l2_counter_values;
/**
* Structure to pass data defining Mali instance in use:
*
* mali_product_id - Mali product id
* mali_version_major - Mali version major number
* mali_version_minor - Mali version minor number
* num_of_l2_cores - number of L2 cache cores
* num_of_fp_cores - number of fragment processor cores
* num_of_vp_cores - number of vertex processor cores
*/
typedef struct _mali_profiling_mali_version {
u32 mali_product_id;
u32 mali_version_major;
u32 mali_version_minor;
u32 num_of_l2_cores;
u32 num_of_fp_cores;
u32 num_of_vp_cores;
} _mali_profiling_mali_version;
/*
* List of possible actions to be controlled by 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.
*/
#define FBDUMP_CONTROL_ENABLE (1)
#define FBDUMP_CONTROL_RATE (2)
#define SW_COUNTER_ENABLE (3)
#define FBDUMP_CONTROL_RESIZE_FACTOR (4)
void _mali_profiling_control(u32 action, u32 value);
u32 _mali_profiling_get_l2_counters(_mali_profiling_l2_counter_values *values);
int _mali_profiling_set_event(u32 counter_id, s32 event_id);
u32 _mali_profiling_get_api_version(void);
void _mali_profiling_get_mali_version(struct _mali_profiling_mali_version *values);
#ifdef __cplusplus
}
#endif
#endif /* __MALI_UTGARD_PROFILING_GATOR_API_H__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2010, 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 mali_kernel_license.h
* Defines for the macro MODULE_LICENSE.
*/
#ifndef __MALI_KERNEL_LICENSE_H__
#define __MALI_KERNEL_LICENSE_H__
#ifdef __cplusplus
extern "C" {
#endif
#define MALI_KERNEL_LINUX_LICENSE "GPL"
#define MALI_LICENSE_IS_GPL 1
#ifdef __cplusplus
}
#endif
#endif /* __MALI_KERNEL_LICENSE_H__ */

View File

@@ -0,0 +1,38 @@
/**
* Copyright (C) 2010-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 mali_device_pause_resume.c
* Implementation of the Mali pause/resume functionality
*/
#include <linux/module.h>
#include <linux/mali/mali_utgard.h>
#include "mali_gp_scheduler.h"
#include "mali_pp_scheduler.h"
void mali_dev_pause(void)
{
mali_gp_scheduler_suspend();
mali_pp_scheduler_suspend();
mali_group_power_off(MALI_FALSE);
mali_l2_cache_pause_all(MALI_TRUE);
}
EXPORT_SYMBOL(mali_dev_pause);
void mali_dev_resume(void)
{
mali_l2_cache_pause_all(MALI_FALSE);
mali_gp_scheduler_resume();
mali_pp_scheduler_resume();
}
EXPORT_SYMBOL(mali_dev_resume);

View File

@@ -0,0 +1,830 @@
/**
* Copyright (C) 2010-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 mali_kernel_linux.c
* Implementation of the Linux device driver entrypoints
*/
#include <linux/module.h> /* kernel module definitions */
#include <linux/fs.h> /* file system operations */
#include <linux/cdev.h> /* character device definitions */
#include <linux/mm.h> /* memory manager definitions */
#include <linux/mali/mali_utgard_ioctl.h>
#include <linux/version.h>
#include <linux/device.h>
#include "mali_kernel_license.h"
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/mali/mali_utgard.h>
#include "mali_kernel_common.h"
#include "mali_session.h"
#include "mali_kernel_core.h"
#include "mali_osk.h"
#include "mali_kernel_linux.h"
#include "mali_ukk.h"
#include "mali_ukk_wrappers.h"
#include "mali_kernel_sysfs.h"
#include "mali_pm.h"
#include "mali_kernel_license.h"
#include "mali_memory.h"
#include "mali_memory_dma_buf.h"
#if defined(CONFIG_MALI400_INTERNAL_PROFILING)
#include "mali_profiling_internal.h"
#endif
/* Streamline support for the Mali driver */
#if defined(CONFIG_TRACEPOINTS) && defined(CONFIG_MALI400_PROFILING)
/* Ask Linux to create the tracepoints */
#define CREATE_TRACE_POINTS
#include "mali_linux_trace.h"
#endif /* CONFIG_TRACEPOINTS */
/* from the __malidrv_build_info.c file that is generated during build */
extern const char *__malidrv_build_info(void);
extern int mali_pdev_dts_init(struct platform_device* mali_gpu_device);
/* Module parameter to control log level */
int mali_debug_level = 2;
module_param(mali_debug_level, int, S_IRUSR | S_IWUSR | S_IWGRP | S_IRGRP | S_IROTH); /* rw-rw-r-- */
MODULE_PARM_DESC(mali_debug_level, "Higher number, more dmesg output");
extern int mali_max_job_runtime;
module_param(mali_max_job_runtime, int, S_IRUSR | S_IWUSR | S_IWGRP | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(mali_max_job_runtime, "Maximum allowed job runtime in msecs.\nJobs will be killed after this no matter what");
extern int mali_l2_max_reads;
module_param(mali_l2_max_reads, int, S_IRUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(mali_l2_max_reads, "Maximum reads for Mali L2 cache");
extern unsigned int mali_dedicated_mem_start;
module_param(mali_dedicated_mem_start, uint, S_IRUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(mali_dedicated_mem_start, "Physical start address of dedicated Mali GPU memory.");
extern unsigned int mali_dedicated_mem_size;
module_param(mali_dedicated_mem_size, uint, S_IRUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(mali_dedicated_mem_size, "Size of dedicated Mali GPU memory.");
extern unsigned int mali_shared_mem_size;
module_param(mali_shared_mem_size, uint, S_IRUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(mali_shared_mem_size, "Size of shared Mali GPU memory.");
#if defined(CONFIG_MALI400_PROFILING)
extern int mali_boot_profiling;
module_param(mali_boot_profiling, int, S_IRUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(mali_boot_profiling, "Start profiling as a part of Mali driver initialization");
#endif
extern int mali_max_pp_cores_group_1;
module_param(mali_max_pp_cores_group_1, int, S_IRUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(mali_max_pp_cores_group_1, "Limit the number of PP cores to use from first PP group.");
extern int mali_max_pp_cores_group_2;
module_param(mali_max_pp_cores_group_2, int, S_IRUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(mali_max_pp_cores_group_2, "Limit the number of PP cores to use from second PP group (Mali-450 only).");
#if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY)
/** the max fps the same as display vsync default 60, can set by module insert parameter */
extern int mali_max_system_fps;
module_param(mali_max_system_fps, int, S_IRUSR | S_IWUSR | S_IWGRP | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(mali_max_system_fps, "Max system fps the same as display VSYNC.");
/** a lower limit on their desired FPS default 58, can set by module insert parameter*/
extern int mali_desired_fps;
module_param(mali_desired_fps, int, S_IRUSR | S_IWUSR | S_IWGRP | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(mali_desired_fps, "A bit lower than max_system_fps which user desired fps");
#endif
#if MALI_ENABLE_CPU_CYCLES
#include <linux/cpumask.h>
#include <linux/timer.h>
#include <asm/smp.h>
static struct timer_list mali_init_cpu_clock_timers[8];
static u32 mali_cpu_clock_last_value[8] = {0,};
#endif
/* Export symbols from common code: mali_user_settings.c */
#include "mali_user_settings_db.h"
EXPORT_SYMBOL(mali_set_user_setting);
EXPORT_SYMBOL(mali_get_user_setting);
static char mali_dev_name[] = "mali"; /* should be const, but the functions we call requires non-cost */
/* This driver only supports one Mali device, and this variable stores this single platform device */
struct platform_device *mali_platform_device = NULL;
/* This driver only supports one Mali device, and this variable stores the exposed misc device (/dev/mali) */
static struct miscdevice mali_miscdevice = { 0, };
static int mali_miscdevice_register(struct platform_device *pdev);
static void mali_miscdevice_unregister(void);
static int mali_open(struct inode *inode, struct file *filp);
static int mali_release(struct inode *inode, struct file *filp);
#ifdef HAVE_UNLOCKED_IOCTL
static long mali_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
#else
static int mali_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
#endif
static int mali_probe(struct platform_device *pdev);
static int mali_remove(struct platform_device *pdev);
static int mali_driver_suspend_scheduler(struct device *dev);
static int mali_driver_resume_scheduler(struct device *dev);
#ifdef CONFIG_PM_RUNTIME
static int mali_driver_runtime_suspend(struct device *dev);
static int mali_driver_runtime_resume(struct device *dev);
static int mali_driver_runtime_idle(struct device *dev);
#endif
#if defined(MALI_FAKE_PLATFORM_DEVICE)
extern int mali_platform_device_register(void);
extern int mali_platform_device_unregister(void);
#endif
/* Linux power management operations provided by the Mali device driver */
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29))
struct pm_ext_ops mali_dev_ext_pm_ops = {
.base =
{
.suspend = mali_driver_suspend_scheduler,
.resume = mali_driver_resume_scheduler,
.freeze = mali_driver_suspend_scheduler,
.thaw = mali_driver_resume_scheduler,
},
};
#else
static const struct dev_pm_ops mali_dev_pm_ops = {
#ifdef CONFIG_PM_RUNTIME
.runtime_suspend = mali_driver_runtime_suspend,
.runtime_resume = mali_driver_runtime_resume,
.runtime_idle = mali_driver_runtime_idle,
#endif
.suspend = mali_driver_suspend_scheduler,
.resume = mali_driver_resume_scheduler,
.freeze = mali_driver_suspend_scheduler,
.thaw = mali_driver_resume_scheduler,
.poweroff = mali_driver_suspend_scheduler,
};
#endif
#ifdef CONFIG_USE_OF
static const struct of_device_id amlogic_mesonstream_dt_match[]={
{ .compatible = "arm,mali",
},
{},
};
#else
#define amlogic_mesonstream_dt_match NULL
#endif
/* The Mali device driver struct */
static struct platform_driver mali_platform_driver = {
.probe = mali_probe,
.remove = mali_remove,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29))
.pm = &mali_dev_ext_pm_ops,
#endif
.driver =
{
.name = MALI_GPU_NAME_UTGARD,
.owner = THIS_MODULE,
.bus = &platform_bus_type,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29))
.pm = &mali_dev_pm_ops,
#endif
#ifdef CONFIG_USE_OF
.of_match_table = amlogic_mesonstream_dt_match,
#endif
},
};
/* Linux misc device operations (/dev/mali) */
struct file_operations mali_fops = {
.owner = THIS_MODULE,
.open = mali_open,
.release = mali_release,
#ifdef HAVE_UNLOCKED_IOCTL
.unlocked_ioctl = mali_ioctl,
#else
.ioctl = mali_ioctl,
#endif
.mmap = mali_mmap
};
#if MALI_ENABLE_CPU_CYCLES
void mali_init_cpu_time_counters(int reset, int enable_divide_by_64)
{
/* The CPU assembly reference used is: ARM Architecture Reference Manual ARMv7-AR C.b */
u32 write_value;
/* See B4.1.116 PMCNTENSET, Performance Monitors Count Enable Set register, VMSA */
/* setting p15 c9 c12 1 to 0x8000000f==CPU_CYCLE_ENABLE |EVENT_3_ENABLE|EVENT_2_ENABLE|EVENT_1_ENABLE|EVENT_0_ENABLE */
asm volatile("mcr p15, 0, %0, c9, c12, 1" :: "r"(0x8000000f));
/* See B4.1.117 PMCR, Performance Monitors Control Register. Writing to p15, c9, c12, 0 */
write_value = 1<<0; /* Bit 0 set. Enable counters */
if (reset) {
write_value |= 1<<1; /* Reset event counters */
write_value |= 1<<2; /* Reset cycle counter */
}
if (enable_divide_by_64) {
write_value |= 1<<3; /* Enable the Clock divider by 64 */
}
write_value |= 1<<4; /* Export enable. Not needed */
asm volatile ("MCR p15, 0, %0, c9, c12, 0\t\n" :: "r"(write_value ));
/* PMOVSR Overflow Flag Status Register - Clear Clock and Event overflows */
asm volatile ("MCR p15, 0, %0, c9, c12, 3\t\n" :: "r"(0x8000000f));
/* See B4.1.124 PMUSERENR - setting p15 c9 c14 to 1" */
/* User mode access to the Performance Monitors enabled. */
/* Lets User space read cpu clock cycles */
asm volatile( "mcr p15, 0, %0, c9, c14, 0" :: "r"(1) );
}
/** A timer function that configures the cycle clock counter on current CPU.
The function \a mali_init_cpu_time_counters_on_all_cpus sets up this function
to trigger on all Cpus during module load. */
static void mali_init_cpu_clock_timer_func(unsigned long data)
{
int reset_counters, enable_divide_clock_counter_by_64;
int current_cpu = raw_smp_processor_id();
unsigned int sample0;
unsigned int sample1;
MALI_IGNORE(data);
reset_counters= 1;
enable_divide_clock_counter_by_64 = 0;
mali_init_cpu_time_counters(reset_counters, enable_divide_clock_counter_by_64);
sample0 = mali_get_cpu_cyclecount();
sample1 = mali_get_cpu_cyclecount();
MALI_DEBUG_PRINT(3, ("Init Cpu %d cycle counter- First two samples: %08x %08x \n", current_cpu, sample0, sample1));
}
/** A timer functions for storing current time on all cpus.
Used for checking if the clocks have similar values or if they are drifting. */
static void mali_print_cpu_clock_timer_func(unsigned long data)
{
int current_cpu = raw_smp_processor_id();
unsigned int sample0;
MALI_IGNORE(data);
sample0 = mali_get_cpu_cyclecount();
if ( current_cpu<8 ) {
mali_cpu_clock_last_value[current_cpu] = sample0;
}
}
/** Init the performance registers on all CPUs to count clock cycles.
For init \a print_only should be 0.
If \a print_only is 1, it will intead print the current clock value of all CPUs.*/
void mali_init_cpu_time_counters_on_all_cpus(int print_only)
{
int i = 0;
int cpu_number;
int jiffies_trigger;
int jiffies_wait;
jiffies_wait = 2;
jiffies_trigger = jiffies + jiffies_wait;
for ( i=0 ; i < 8 ; i++ ) {
init_timer(&mali_init_cpu_clock_timers[i]);
if (print_only) mali_init_cpu_clock_timers[i].function = mali_print_cpu_clock_timer_func;
else mali_init_cpu_clock_timers[i].function = mali_init_cpu_clock_timer_func;
mali_init_cpu_clock_timers[i].expires = jiffies_trigger ;
}
cpu_number = cpumask_first(cpu_online_mask);
for ( i=0 ; i < 8 ; i++ ) {
int next_cpu;
add_timer_on(&mali_init_cpu_clock_timers[i], cpu_number);
next_cpu = cpumask_next(cpu_number, cpu_online_mask);
if (next_cpu >= nr_cpu_ids) break;
cpu_number = next_cpu;
}
while (jiffies_wait) jiffies_wait= schedule_timeout_uninterruptible(jiffies_wait);
for ( i=0 ; i < 8 ; i++ ) {
del_timer_sync(&mali_init_cpu_clock_timers[i]);
}
if (print_only) {
if ( (0==mali_cpu_clock_last_value[2]) && (0==mali_cpu_clock_last_value[3]) ) {
/* Diff can be printed if we want to check if the clocks are in sync
int diff = mali_cpu_clock_last_value[0] - mali_cpu_clock_last_value[1];*/
MALI_DEBUG_PRINT(2, ("CPU cycle counters readout all: %08x %08x\n", mali_cpu_clock_last_value[0], mali_cpu_clock_last_value[1]));
} else {
MALI_DEBUG_PRINT(2, ("CPU cycle counters readout all: %08x %08x %08x %08x\n", mali_cpu_clock_last_value[0], mali_cpu_clock_last_value[1], mali_cpu_clock_last_value[2], mali_cpu_clock_last_value[3] ));
}
}
}
#endif
extern int mpgpu_class_init(void);
extern void mpgpu_class_exit(void);
int mali_module_init(void)
{
int err = 0;
MALI_DEBUG_PRINT(2, ("Inserting Mali v%d device driver. \n",_MALI_API_VERSION));
MALI_DEBUG_PRINT(2, ("Compiled: %s, time: %s.\n", __DATE__, __TIME__));
MALI_DEBUG_PRINT(2, ("Driver revision: %s\n", SVN_REV_STRING));
#if MALI_ENABLE_CPU_CYCLES
mali_init_cpu_time_counters_on_all_cpus(0);
MALI_DEBUG_PRINT(2, ("CPU cycle counter setup complete\n"));
/* Printing the current cpu counters */
mali_init_cpu_time_counters_on_all_cpus(1);
#endif
/* Initialize module wide settings */
#if defined(MALI_FAKE_PLATFORM_DEVICE)
MALI_DEBUG_PRINT(2, ("mali_module_init() registering device\n"));
err = mali_platform_device_register();
if (0 != err) {
return err;
}
#endif
MALI_DEBUG_PRINT(2, ("mali_module_init() registering driver\n"));
err = platform_driver_register(&mali_platform_driver);
if (0 != err) {
MALI_DEBUG_PRINT(2, ("mali_module_init() Failed to register driver (%d)\n", err));
#if defined(MALI_FAKE_PLATFORM_DEVICE)
mali_platform_device_unregister();
#endif
mali_platform_device = NULL;
return err;
}
#if defined(CONFIG_MALI400_INTERNAL_PROFILING)
err = _mali_internal_profiling_init(mali_boot_profiling ? MALI_TRUE : MALI_FALSE);
if (0 != err) {
/* No biggie if we wheren't able to initialize the profiling */
MALI_PRINT_ERROR(("Failed to initialize profiling, feature will be unavailable\n"));
}
#endif
MALI_PRINT(("Mali device driver loaded\n"));
mpgpu_class_init();
return 0; /* Success */
}
void mali_module_exit(void)
{
MALI_DEBUG_PRINT(2, ("Unloading Mali v%d device driver.\n",_MALI_API_VERSION));
MALI_DEBUG_PRINT(2, ("mali_module_exit() unregistering driver\n"));
#if defined(CONFIG_MALI400_INTERNAL_PROFILING)
_mali_internal_profiling_term();
#endif
platform_driver_unregister(&mali_platform_driver);
#if defined(MALI_FAKE_PLATFORM_DEVICE)
MALI_DEBUG_PRINT(2, ("mali_module_exit() unregistering device\n"));
mali_platform_device_unregister();
#endif
mpgpu_class_exit();
MALI_PRINT(("Mali device driver unloaded\n"));
}
static int mali_probe(struct platform_device *pdev)
{
int err;
MALI_DEBUG_PRINT(2, ("mali_probe(): Called for platform device %s\n", pdev->name));
if (NULL != mali_platform_device) {
/* Already connected to a device, return error */
MALI_PRINT_ERROR(("mali_probe(): The Mali driver is already connected with a Mali device."));
return -EEXIST;
}
mali_platform_device = pdev;
#ifndef MALI_FAKE_PLATFORM_DEVICE
if (mali_pdev_dts_init(pdev) < 0)
return -ENOMEM;
#endif
if (_MALI_OSK_ERR_OK == _mali_osk_wq_init()) {
/* Initialize the Mali GPU HW specified by pdev */
if (_MALI_OSK_ERR_OK == mali_initialize_subsystems()) {
/* Register a misc device (so we are accessible from user space) */
err = mali_miscdevice_register(pdev);
if (0 == err) {
/* Setup sysfs entries */
err = mali_sysfs_register(mali_dev_name);
if (0 == err) {
MALI_DEBUG_PRINT(2, ("mali_probe(): Successfully initialized driver for platform device %s\n", pdev->name));
return 0;
} else {
MALI_PRINT_ERROR(("mali_probe(): failed to register sysfs entries"));
}
mali_miscdevice_unregister();
} else {
MALI_PRINT_ERROR(("mali_probe(): failed to register Mali misc device."));
}
mali_terminate_subsystems();
} else {
MALI_PRINT_ERROR(("mali_probe(): Failed to initialize Mali device driver."));
}
_mali_osk_wq_term();
}
mali_platform_device = NULL;
return -EFAULT;
}
static int mali_remove(struct platform_device *pdev)
{
MALI_DEBUG_PRINT(2, ("mali_remove() called for platform device %s\n", pdev->name));
mali_sysfs_unregister();
mali_miscdevice_unregister();
mali_terminate_subsystems();
_mali_osk_wq_term();
mali_platform_device = NULL;
return 0;
}
static int mali_miscdevice_register(struct platform_device *pdev)
{
int err;
mali_miscdevice.minor = MISC_DYNAMIC_MINOR;
mali_miscdevice.name = mali_dev_name;
mali_miscdevice.fops = &mali_fops;
mali_miscdevice.parent = get_device(&pdev->dev);
err = misc_register(&mali_miscdevice);
if (0 != err) {
MALI_PRINT_ERROR(("Failed to register misc device, misc_register() returned %d\n", err));
}
return err;
}
static void mali_miscdevice_unregister(void)
{
misc_deregister(&mali_miscdevice);
}
static int mali_driver_suspend_scheduler(struct device *dev)
{
mali_pm_os_suspend();
return 0;
}
static int mali_driver_resume_scheduler(struct device *dev)
{
mali_pm_os_resume();
return 0;
}
#ifdef CONFIG_PM_RUNTIME
static int mali_driver_runtime_suspend(struct device *dev)
{
mali_pm_runtime_suspend();
return 0;
}
static int mali_driver_runtime_resume(struct device *dev)
{
mali_pm_runtime_resume();
return 0;
}
static int mali_driver_runtime_idle(struct device *dev)
{
/* Nothing to do */
return 0;
}
#endif
static int mali_open(struct inode *inode, struct file *filp)
{
struct mali_session_data * session_data;
_mali_osk_errcode_t err;
/* input validation */
if (mali_miscdevice.minor != iminor(inode)) {
MALI_PRINT_ERROR(("mali_open() Minor does not match\n"));
return -ENODEV;
}
/* allocated struct to track this session */
err = _mali_ukk_open((void **)&session_data);
if (_MALI_OSK_ERR_OK != err) return map_errcode(err);
/* initialize file pointer */
filp->f_pos = 0;
/* link in our session data */
filp->private_data = (void*)session_data;
return 0;
}
static int mali_release(struct inode *inode, struct file *filp)
{
_mali_osk_errcode_t err;
/* input validation */
if (mali_miscdevice.minor != iminor(inode)) {
MALI_PRINT_ERROR(("mali_release() Minor does not match\n"));
return -ENODEV;
}
err = _mali_ukk_close((void **)&filp->private_data);
if (_MALI_OSK_ERR_OK != err) return map_errcode(err);
return 0;
}
int map_errcode( _mali_osk_errcode_t err )
{
switch(err) {
case _MALI_OSK_ERR_OK :
return 0;
case _MALI_OSK_ERR_FAULT:
return -EFAULT;
case _MALI_OSK_ERR_INVALID_FUNC:
return -ENOTTY;
case _MALI_OSK_ERR_INVALID_ARGS:
return -EINVAL;
case _MALI_OSK_ERR_NOMEM:
return -ENOMEM;
case _MALI_OSK_ERR_TIMEOUT:
return -ETIMEDOUT;
case _MALI_OSK_ERR_RESTARTSYSCALL:
return -ERESTARTSYS;
case _MALI_OSK_ERR_ITEM_NOT_FOUND:
return -ENOENT;
default:
return -EFAULT;
}
}
#ifdef HAVE_UNLOCKED_IOCTL
static long mali_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
#else
static int mali_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
#endif
{
int err;
struct mali_session_data *session_data;
#ifndef HAVE_UNLOCKED_IOCTL
/* inode not used */
(void)inode;
#endif
MALI_DEBUG_PRINT(7, ("Ioctl received 0x%08X 0x%08lX\n", cmd, arg));
session_data = (struct mali_session_data *)filp->private_data;
if (NULL == session_data) {
MALI_DEBUG_PRINT(7, ("filp->private_data was NULL\n"));
return -ENOTTY;
}
if (NULL == (void *)arg) {
MALI_DEBUG_PRINT(7, ("arg was NULL\n"));
return -ENOTTY;
}
switch(cmd) {
case MALI_IOC_WAIT_FOR_NOTIFICATION:
err = wait_for_notification_wrapper(session_data, (_mali_uk_wait_for_notification_s __user *)arg);
break;
case MALI_IOC_GET_API_VERSION:
err = get_api_version_wrapper(session_data, (_mali_uk_get_api_version_s __user *)arg);
break;
case MALI_IOC_POST_NOTIFICATION:
err = post_notification_wrapper(session_data, (_mali_uk_post_notification_s __user *)arg);
break;
case MALI_IOC_GET_USER_SETTINGS:
err = get_user_settings_wrapper(session_data, (_mali_uk_get_user_settings_s __user *)arg);
break;
case MALI_IOC_REQUEST_HIGH_PRIORITY:
err = request_high_priority_wrapper(session_data, (_mali_uk_request_high_priority_s __user *)arg);
break;
#if defined(CONFIG_MALI400_PROFILING)
case MALI_IOC_PROFILING_START:
err = profiling_start_wrapper(session_data, (_mali_uk_profiling_start_s __user *)arg);
break;
case MALI_IOC_PROFILING_ADD_EVENT:
err = profiling_add_event_wrapper(session_data, (_mali_uk_profiling_add_event_s __user *)arg);
break;
case MALI_IOC_PROFILING_STOP:
err = profiling_stop_wrapper(session_data, (_mali_uk_profiling_stop_s __user *)arg);
break;
case MALI_IOC_PROFILING_GET_EVENT:
err = profiling_get_event_wrapper(session_data, (_mali_uk_profiling_get_event_s __user *)arg);
break;
case MALI_IOC_PROFILING_CLEAR:
err = profiling_clear_wrapper(session_data, (_mali_uk_profiling_clear_s __user *)arg);
break;
case MALI_IOC_PROFILING_GET_CONFIG:
/* Deprecated: still compatible with get_user_settings */
err = get_user_settings_wrapper(session_data, (_mali_uk_get_user_settings_s __user *)arg);
break;
case MALI_IOC_PROFILING_REPORT_SW_COUNTERS:
err = profiling_report_sw_counters_wrapper(session_data, (_mali_uk_sw_counters_report_s __user *)arg);
break;
#else
case MALI_IOC_PROFILING_START: /* FALL-THROUGH */
case MALI_IOC_PROFILING_ADD_EVENT: /* FALL-THROUGH */
case MALI_IOC_PROFILING_STOP: /* FALL-THROUGH */
case MALI_IOC_PROFILING_GET_EVENT: /* FALL-THROUGH */
case MALI_IOC_PROFILING_CLEAR: /* FALL-THROUGH */
case MALI_IOC_PROFILING_GET_CONFIG: /* FALL-THROUGH */
case MALI_IOC_PROFILING_REPORT_SW_COUNTERS: /* FALL-THROUGH */
MALI_DEBUG_PRINT(2, ("Profiling not supported\n"));
err = -ENOTTY;
break;
#endif
case MALI_IOC_MEM_WRITE_SAFE:
err = mem_write_safe_wrapper(session_data, (_mali_uk_mem_write_safe_s __user *)arg);
break;
case MALI_IOC_MEM_MAP_EXT:
err = mem_map_ext_wrapper(session_data, (_mali_uk_map_external_mem_s __user *)arg);
break;
case MALI_IOC_MEM_UNMAP_EXT:
err = mem_unmap_ext_wrapper(session_data, (_mali_uk_unmap_external_mem_s __user *)arg);
break;
case MALI_IOC_MEM_QUERY_MMU_PAGE_TABLE_DUMP_SIZE:
err = mem_query_mmu_page_table_dump_size_wrapper(session_data, (_mali_uk_query_mmu_page_table_dump_size_s __user *)arg);
break;
case MALI_IOC_MEM_DUMP_MMU_PAGE_TABLE:
err = mem_dump_mmu_page_table_wrapper(session_data, (_mali_uk_dump_mmu_page_table_s __user *)arg);
break;
#if defined(CONFIG_MALI400_UMP)
case MALI_IOC_MEM_ATTACH_UMP:
err = mem_attach_ump_wrapper(session_data, (_mali_uk_attach_ump_mem_s __user *)arg);
break;
case MALI_IOC_MEM_RELEASE_UMP:
err = mem_release_ump_wrapper(session_data, (_mali_uk_release_ump_mem_s __user *)arg);
break;
#else
case MALI_IOC_MEM_ATTACH_UMP:
case MALI_IOC_MEM_RELEASE_UMP: /* FALL-THROUGH */
MALI_DEBUG_PRINT(2, ("UMP not supported\n"));
err = -ENOTTY;
break;
#endif
#ifdef CONFIG_DMA_SHARED_BUFFER
case MALI_IOC_MEM_ATTACH_DMA_BUF:
err = mali_attach_dma_buf(session_data, (_mali_uk_attach_dma_buf_s __user *)arg);
break;
case MALI_IOC_MEM_RELEASE_DMA_BUF:
err = mali_release_dma_buf(session_data, (_mali_uk_release_dma_buf_s __user *)arg);
break;
case MALI_IOC_MEM_DMA_BUF_GET_SIZE:
err = mali_dma_buf_get_size(session_data, (_mali_uk_dma_buf_get_size_s __user *)arg);
break;
#else
case MALI_IOC_MEM_ATTACH_DMA_BUF: /* FALL-THROUGH */
case MALI_IOC_MEM_RELEASE_DMA_BUF: /* FALL-THROUGH */
case MALI_IOC_MEM_DMA_BUF_GET_SIZE: /* FALL-THROUGH */
MALI_DEBUG_PRINT(2, ("DMA-BUF not supported\n"));
err = -ENOTTY;
break;
#endif
case MALI_IOC_PP_START_JOB:
err = pp_start_job_wrapper(session_data, (_mali_uk_pp_start_job_s __user *)arg);
break;
case MALI_IOC_PP_AND_GP_START_JOB:
err = pp_and_gp_start_job_wrapper(session_data, (_mali_uk_pp_and_gp_start_job_s __user *)arg);
break;
case MALI_IOC_PP_NUMBER_OF_CORES_GET:
err = pp_get_number_of_cores_wrapper(session_data, (_mali_uk_get_pp_number_of_cores_s __user *)arg);
break;
case MALI_IOC_PP_CORE_VERSION_GET:
err = pp_get_core_version_wrapper(session_data, (_mali_uk_get_pp_core_version_s __user *)arg);
break;
case MALI_IOC_PP_DISABLE_WB:
err = pp_disable_wb_wrapper(session_data, (_mali_uk_pp_disable_wb_s __user *)arg);
break;
case MALI_IOC_GP2_START_JOB:
err = gp_start_job_wrapper(session_data, (_mali_uk_gp_start_job_s __user *)arg);
break;
case MALI_IOC_GP2_NUMBER_OF_CORES_GET:
err = gp_get_number_of_cores_wrapper(session_data, (_mali_uk_get_gp_number_of_cores_s __user *)arg);
break;
case MALI_IOC_GP2_CORE_VERSION_GET:
err = gp_get_core_version_wrapper(session_data, (_mali_uk_get_gp_core_version_s __user *)arg);
break;
case MALI_IOC_GP2_SUSPEND_RESPONSE:
err = gp_suspend_response_wrapper(session_data, (_mali_uk_gp_suspend_response_s __user *)arg);
break;
case MALI_IOC_VSYNC_EVENT_REPORT:
err = vsync_event_report_wrapper(session_data, (_mali_uk_vsync_event_report_s __user *)arg);
break;
case MALI_IOC_TIMELINE_GET_LATEST_POINT:
err = timeline_get_latest_point_wrapper(session_data, (_mali_uk_timeline_get_latest_point_s __user *)arg);
break;
case MALI_IOC_TIMELINE_WAIT:
err = timeline_wait_wrapper(session_data, (_mali_uk_timeline_wait_s __user *)arg);
break;
case MALI_IOC_TIMELINE_CREATE_SYNC_FENCE:
err = timeline_create_sync_fence_wrapper(session_data, (_mali_uk_timeline_create_sync_fence_s __user *)arg);
break;
case MALI_IOC_SOFT_JOB_START:
err = soft_job_start_wrapper(session_data, (_mali_uk_soft_job_start_s __user *)arg);
break;
case MALI_IOC_SOFT_JOB_SIGNAL:
err = soft_job_signal_wrapper(session_data, (_mali_uk_soft_job_signal_s __user *)arg);
break;
case MALI_IOC_MEM_INIT: /* Fallthrough */
case MALI_IOC_MEM_TERM: /* Fallthrough */
MALI_DEBUG_PRINT(2, ("Deprecated ioctls called\n"));
err = -ENOTTY;
break;
case MALI_IOC_MEM_GET_BIG_BLOCK: /* Fallthrough */
case MALI_IOC_MEM_FREE_BIG_BLOCK:
MALI_PRINT_ERROR(("Non-MMU mode is no longer supported.\n"));
err = -ENOTTY;
break;
default:
MALI_DEBUG_PRINT(2, ("No handler for ioctl 0x%08X 0x%08lX\n", cmd, arg));
err = -ENOTTY;
};
return err;
}
module_init(mali_module_init);
module_exit(mali_module_exit);
MODULE_LICENSE(MALI_KERNEL_LINUX_LICENSE);
MODULE_AUTHOR("ARM Ltd.");
MODULE_VERSION(SVN_REV_STRING);

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2010-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.
*/
#ifndef __MALI_KERNEL_LINUX_H__
#define __MALI_KERNEL_LINUX_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <linux/cdev.h> /* character device definitions */
#include "mali_kernel_license.h"
#include "mali_osk_types.h"
extern struct platform_device *mali_platform_device;
#if MALI_LICENSE_IS_GPL
/* Defined in mali_osk_irq.h */
extern struct workqueue_struct * mali_wq_normal;
#endif
#ifdef __cplusplus
}
#endif
#endif /* __MALI_KERNEL_LINUX_H__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,29 @@
/*
* Copyright (C) 2011-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.
*/
#ifndef __MALI_KERNEL_SYSFS_H__
#define __MALI_KERNEL_SYSFS_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <linux/device.h>
#define MALI_PROC_DIR "driver/mali"
int mali_sysfs_register(const char *mali_dev_name);
int mali_sysfs_unregister(void);
#ifdef __cplusplus
}
#endif
#endif /* __MALI_KERNEL_LINUX_H__ */

View File

@@ -0,0 +1,126 @@
/*
* Copyright (C) 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.
*/
#if !defined (MALI_LINUX_TRACE_H) || defined (TRACE_HEADER_MULTI_READ)
#define MALI_LINUX_TRACE_H
#include <linux/types.h>
#include <linux/stringify.h>
#include <linux/tracepoint.h>
#undef TRACE_SYSTEM
#define TRACE_SYSTEM mali
#define TRACE_SYSTEM_STRING __stringfy(TRACE_SYSTEM)
#define TRACE_INCLUDE_PATH .
#define TRACE_INCLUDE_FILE mali_linux_trace
/**
* Define the tracepoint used to communicate the status of a GPU. Called
* when a GPU turns on or turns off.
*
* @param event_id The type of the event. This parameter is a bitfield
* encoding the type of the event.
*
* @param d0 First data parameter.
* @param d1 Second data parameter.
* @param d2 Third data parameter.
* @param d3 Fourth data parameter.
* @param d4 Fifth data parameter.
*/
TRACE_EVENT(mali_timeline_event,
TP_PROTO(unsigned int event_id, unsigned int d0, unsigned int d1,
unsigned int d2, unsigned int d3, unsigned int d4),
TP_ARGS(event_id, d0, d1, d2, d3, d4),
TP_STRUCT__entry(
__field(unsigned int, event_id)
__field(unsigned int, d0)
__field(unsigned int, d1)
__field(unsigned int, d2)
__field(unsigned int, d3)
__field(unsigned int, d4)
),
TP_fast_assign(
__entry->event_id = event_id;
__entry->d0 = d0;
__entry->d1 = d1;
__entry->d2 = d2;
__entry->d3 = d3;
__entry->d4 = d4;
),
TP_printk("event=%d", __entry->event_id)
);
/**
* Define a tracepoint used to regsiter the value of a hardware counter.
* Hardware counters belonging to the vertex or fragment processor are
* reported via this tracepoint each frame, whilst L2 cache hardware
* counters are reported continuously.
*
* @param counter_id The counter ID.
* @param value The value of the counter.
*/
TRACE_EVENT(mali_hw_counter,
TP_PROTO(unsigned int counter_id, unsigned int value),
TP_ARGS(counter_id, value),
TP_STRUCT__entry(
__field(unsigned int, counter_id)
__field(unsigned int, value)
),
TP_fast_assign(
__entry->counter_id = counter_id;
),
TP_printk("event %d = %d", __entry->counter_id, __entry->value)
);
/**
* Define a tracepoint used to send a bundle of software counters.
*
* @param counters The bundle of counters.
*/
TRACE_EVENT(mali_sw_counters,
TP_PROTO(pid_t pid, pid_t tid, void * surface_id, unsigned int * counters),
TP_ARGS(pid, tid, surface_id, counters),
TP_STRUCT__entry(
__field(pid_t, pid)
__field(pid_t, tid)
__field(void *, surface_id)
__field(unsigned int *, counters)
),
TP_fast_assign(
__entry->pid = pid;
__entry->tid = tid;
__entry->surface_id = surface_id;
__entry->counters = counters;
),
TP_printk("counters were %s", __entry->counters == NULL? "NULL" : "not NULL")
);
#endif /* MALI_LINUX_TRACE_H */
/* This part must exist outside the header guard. */
#include <trace/define_trace.h>

View File

@@ -0,0 +1,353 @@
/*
* Copyright (C) 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.
*/
#include <linux/list.h>
#include <linux/mm.h>
#include <linux/mm_types.h>
#include <linux/fs.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <linux/platform_device.h>
#include "mali_osk.h"
#include "mali_osk_mali.h"
#include "mali_kernel_linux.h"
#include "mali_scheduler.h"
#include "mali_kernel_descriptor_mapping.h"
#include "mali_memory.h"
#include "mali_memory_dma_buf.h"
#include "mali_memory_os_alloc.h"
#include "mali_memory_block_alloc.h"
/* session->memory_lock must be held when calling this function */
static void mali_mem_release(mali_mem_allocation *descriptor)
{
MALI_DEBUG_ASSERT_POINTER(descriptor);
MALI_DEBUG_ASSERT_LOCK_HELD(descriptor->session->memory_lock);
MALI_DEBUG_ASSERT(MALI_MEM_ALLOCATION_VALID_MAGIC == descriptor->magic);
switch (descriptor->type) {
case MALI_MEM_OS:
mali_mem_os_release(descriptor);
break;
case MALI_MEM_DMA_BUF:
#if defined(CONFIG_DMA_SHARED_BUFFER)
mali_mem_dma_buf_release(descriptor);
#endif
break;
case MALI_MEM_UMP:
#if defined(CONFIG_MALI400_UMP)
mali_mem_ump_release(descriptor);
#endif
break;
case MALI_MEM_EXTERNAL:
mali_mem_external_release(descriptor);
break;
case MALI_MEM_BLOCK:
mali_mem_block_release(descriptor);
break;
}
}
static void mali_mem_vma_open(struct vm_area_struct * vma)
{
mali_mem_allocation *descriptor = (mali_mem_allocation*)vma->vm_private_data;
MALI_DEBUG_PRINT(4, ("Open called on vma %p\n", vma));
descriptor->cpu_mapping.ref++;
return;
}
static void mali_mem_vma_close(struct vm_area_struct *vma)
{
mali_mem_allocation *descriptor;
struct mali_session_data *session;
mali_mem_virt_cpu_mapping *mapping;
MALI_DEBUG_PRINT(3, ("Close called on vma %p\n", vma));
descriptor = (mali_mem_allocation*)vma->vm_private_data;
BUG_ON(!descriptor);
MALI_DEBUG_ASSERT(MALI_MEM_ALLOCATION_VALID_MAGIC == descriptor->magic);
mapping = &descriptor->cpu_mapping;
BUG_ON(0 == mapping->ref);
mapping->ref--;
if (0 != mapping->ref) {
MALI_DEBUG_PRINT(3, ("Ignoring this close, %d references still exists\n", mapping->ref));
return;
}
session = descriptor->session;
mali_descriptor_mapping_free(session->descriptor_mapping, descriptor->id);
_mali_osk_mutex_wait(session->memory_lock);
mali_mem_release(descriptor);
_mali_osk_mutex_signal(session->memory_lock);
mali_mem_descriptor_destroy(descriptor);
}
static int mali_kernel_memory_cpu_page_fault_handler(struct vm_area_struct *vma, struct vm_fault *vmf)
{
void __user * address;
mali_mem_allocation *descriptor;
address = vmf->virtual_address;
descriptor = (mali_mem_allocation *)vma->vm_private_data;
MALI_DEBUG_ASSERT(MALI_MEM_ALLOCATION_VALID_MAGIC == descriptor->magic);
/*
* We always fail the call since all memory is pre-faulted when assigned to the process.
* Only the Mali cores can use page faults to extend buffers.
*/
MALI_DEBUG_PRINT(1, ("Page-fault in Mali memory region caused by the CPU.\n"));
MALI_DEBUG_PRINT(1, ("Tried to access %p (process local virtual address) which is not currently mapped to any Mali memory.\n", (void*)address));
MALI_IGNORE(address);
MALI_IGNORE(descriptor);
return VM_FAULT_SIGBUS;
}
struct vm_operations_struct mali_kernel_vm_ops = {
.open = mali_mem_vma_open,
.close = mali_mem_vma_close,
.fault = mali_kernel_memory_cpu_page_fault_handler
};
/** @note munmap handler is done by vma close handler */
int mali_mmap(struct file *filp, struct vm_area_struct *vma)
{
struct mali_session_data *session;
mali_mem_allocation *descriptor;
u32 size = vma->vm_end - vma->vm_start;
u32 mali_addr = vma->vm_pgoff << PAGE_SHIFT;
session = (struct mali_session_data *)filp->private_data;
if (NULL == session) {
MALI_PRINT_ERROR(("mmap called without any session data available\n"));
return -EFAULT;
}
MALI_DEBUG_PRINT(4, ("MMap() handler: start=0x%08X, phys=0x%08X, size=0x%08X vma->flags 0x%08x\n",
(unsigned int)vma->vm_start, (unsigned int)(vma->vm_pgoff << PAGE_SHIFT),
(unsigned int)(vma->vm_end - vma->vm_start), vma->vm_flags));
/* Set some bits which indicate that, the memory is IO memory, meaning
* that no paging is to be performed and the memory should not be
* included in crash dumps. And that the memory is reserved, meaning
* that it's present and can never be paged out (see also previous
* entry)
*/
vma->vm_flags |= VM_IO;
vma->vm_flags |= VM_DONTCOPY;
vma->vm_flags |= VM_PFNMAP;
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)
vma->vm_flags |= VM_RESERVED;
#else
vma->vm_flags |= VM_DONTDUMP;
vma->vm_flags |= VM_DONTEXPAND;
#endif
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
vma->vm_ops = &mali_kernel_vm_ops; /* Operations used on any memory system */
descriptor = mali_mem_block_alloc(mali_addr, size, vma, session);
if (NULL == descriptor) {
descriptor = mali_mem_os_alloc(mali_addr, size, vma, session);
if (NULL == descriptor) {
MALI_DEBUG_PRINT(3, ("MMAP failed\n"));
return -ENOMEM;
}
}
MALI_DEBUG_ASSERT(MALI_MEM_ALLOCATION_VALID_MAGIC == descriptor->magic);
vma->vm_private_data = (void*)descriptor;
/* Put on descriptor map */
if (_MALI_OSK_ERR_OK != mali_descriptor_mapping_allocate_mapping(session->descriptor_mapping, descriptor, &descriptor->id)) {
_mali_osk_mutex_wait(session->memory_lock);
mali_mem_os_release(descriptor);
_mali_osk_mutex_signal(session->memory_lock);
return -EFAULT;
}
return 0;
}
/* Prepare memory descriptor */
mali_mem_allocation *mali_mem_descriptor_create(struct mali_session_data *session, mali_mem_type type)
{
mali_mem_allocation *descriptor;
descriptor = (mali_mem_allocation*)kzalloc(sizeof(mali_mem_allocation), GFP_KERNEL);
if (NULL == descriptor) {
MALI_DEBUG_PRINT(3,("mali_ukk_mem_mmap: descriptor was NULL\n"));
return NULL;
}
MALI_DEBUG_CODE(descriptor->magic = MALI_MEM_ALLOCATION_VALID_MAGIC);
descriptor->flags = 0;
descriptor->type = type;
descriptor->session = session;
return descriptor;
}
void mali_mem_descriptor_destroy(mali_mem_allocation *descriptor)
{
MALI_DEBUG_ASSERT(MALI_MEM_ALLOCATION_VALID_MAGIC == descriptor->magic);
MALI_DEBUG_CODE(descriptor->magic = MALI_MEM_ALLOCATION_FREED_MAGIC);
kfree(descriptor);
}
_mali_osk_errcode_t mali_mem_mali_map_prepare(mali_mem_allocation *descriptor)
{
u32 size = descriptor->size;
struct mali_session_data *session = descriptor->session;
MALI_DEBUG_ASSERT(MALI_MEM_ALLOCATION_VALID_MAGIC == descriptor->magic);
/* Map dma-buf into this session's page tables */
if (descriptor->flags & MALI_MEM_FLAG_MALI_GUARD_PAGE) {
size += MALI_MMU_PAGE_SIZE;
}
return mali_mmu_pagedir_map(session->page_directory, descriptor->mali_mapping.addr, size);
}
void mali_mem_mali_map_free(mali_mem_allocation *descriptor)
{
u32 size = descriptor->size;
struct mali_session_data *session = descriptor->session;
MALI_DEBUG_ASSERT(MALI_MEM_ALLOCATION_VALID_MAGIC == descriptor->magic);
if (descriptor->flags & MALI_MEM_FLAG_MALI_GUARD_PAGE) {
size += MALI_MMU_PAGE_SIZE;
}
/* Umap and flush L2 */
mali_mmu_pagedir_unmap(session->page_directory, descriptor->mali_mapping.addr, descriptor->size);
mali_scheduler_zap_all_active(session);
}
u32 _mali_ukk_report_memory_usage(void)
{
u32 sum = 0;
sum += mali_mem_block_allocator_stat();
sum += mali_mem_os_stat();
return sum;
}
/**
* Per-session memory descriptor mapping table sizes
*/
#define MALI_MEM_DESCRIPTORS_INIT 64
#define MALI_MEM_DESCRIPTORS_MAX 65536
_mali_osk_errcode_t mali_memory_session_begin(struct mali_session_data * session_data)
{
MALI_DEBUG_PRINT(5, ("Memory session begin\n"));
/* Create descriptor mapping table */
session_data->descriptor_mapping = mali_descriptor_mapping_create(MALI_MEM_DESCRIPTORS_INIT, MALI_MEM_DESCRIPTORS_MAX);
if (NULL == session_data->descriptor_mapping) {
MALI_ERROR(_MALI_OSK_ERR_NOMEM);
}
session_data->memory_lock = _mali_osk_mutex_init(_MALI_OSK_LOCKFLAG_ORDERED,
_MALI_OSK_LOCK_ORDER_MEM_SESSION);
if (NULL == session_data->memory_lock) {
mali_descriptor_mapping_destroy(session_data->descriptor_mapping);
_mali_osk_free(session_data);
MALI_ERROR(_MALI_OSK_ERR_FAULT);
}
MALI_DEBUG_PRINT(5, ("MMU session begin: success\n"));
MALI_SUCCESS;
}
/** @brief Callback function that releases memory
*
* session->memory_lock must be held when calling this function.
*/
static void descriptor_table_cleanup_callback(int descriptor_id, void* map_target)
{
mali_mem_allocation *descriptor;
descriptor = (mali_mem_allocation*)map_target;
MALI_DEBUG_ASSERT_LOCK_HELD(descriptor->session->memory_lock);
MALI_DEBUG_PRINT(3, ("Cleanup of descriptor %d mapping to 0x%x in descriptor table\n", descriptor_id, map_target));
MALI_DEBUG_ASSERT(descriptor);
mali_mem_release(descriptor);
mali_mem_descriptor_destroy(descriptor);
}
void mali_memory_session_end(struct mali_session_data *session)
{
MALI_DEBUG_PRINT(3, ("MMU session end\n"));
if (NULL == session) {
MALI_DEBUG_PRINT(1, ("No session data found during session end\n"));
return;
}
/* Lock the session so we can modify the memory list */
_mali_osk_mutex_wait(session->memory_lock);
/* Free all allocations still in the descriptor map, and terminate the map */
if (NULL != session->descriptor_mapping) {
mali_descriptor_mapping_call_for_each(session->descriptor_mapping, descriptor_table_cleanup_callback);
mali_descriptor_mapping_destroy(session->descriptor_mapping);
session->descriptor_mapping = NULL;
}
_mali_osk_mutex_signal(session->memory_lock);
/* Free the lock */
_mali_osk_mutex_term(session->memory_lock);
return;
}
_mali_osk_errcode_t mali_memory_initialize(void)
{
return mali_mem_os_init();
}
void mali_memory_terminate(void)
{
mali_mem_os_term();
mali_mem_block_allocator_destroy(NULL);
}

View File

@@ -0,0 +1,134 @@
/*
* Copyright (C) 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.
*/
#ifndef __MALI_MEMORY_H__
#define __MALI_MEMORY_H__
#include "mali_osk.h"
#include "mali_session.h"
#include <linux/list.h>
#include <linux/mm.h>
#include "mali_memory_types.h"
#include "mali_memory_os_alloc.h"
_mali_osk_errcode_t mali_memory_initialize(void);
void mali_memory_terminate(void);
/** @brief Allocate a page table page
*
* Allocate a page for use as a page directory or page table. The page is
* mapped into kernel space.
*
* @return _MALI_OSK_ERR_OK on success, otherwise an error code
* @param table_page GPU pointer to the allocated page
* @param mapping CPU pointer to the mapping of the allocated page
*/
MALI_STATIC_INLINE _mali_osk_errcode_t mali_mmu_get_table_page(u32 *table_page, mali_io_address *mapping)
{
return mali_mem_os_get_table_page(table_page, mapping);
}
/** @brief Release a page table page
*
* Release a page table page allocated through \a mali_mmu_get_table_page
*
* @param pa the GPU address of the page to release
*/
MALI_STATIC_INLINE void mali_mmu_release_table_page(u32 phys, void *virt)
{
mali_mem_os_release_table_page(phys, virt);
}
/** @brief mmap function
*
* mmap syscalls on the Mali device node will end up here.
*
* This function allocates Mali memory and maps it on CPU and Mali.
*/
int mali_mmap(struct file *filp, struct vm_area_struct *vma);
/** @brief Allocate and initialize a Mali memory descriptor
*
* @param session Pointer to the session allocating the descriptor
* @param type Type of memory the descriptor will represent
*/
mali_mem_allocation *mali_mem_descriptor_create(struct mali_session_data *session, mali_mem_type type);
/** @brief Destroy a Mali memory descriptor
*
* This function will only free the descriptor itself, and not the memory it
* represents.
*
* @param descriptor Pointer to the descriptor to destroy
*/
void mali_mem_descriptor_destroy(mali_mem_allocation *descriptor);
/** @brief Start a new memory session
*
* Called when a process opens the Mali device node.
*
* @param session Pointer to session to initialize
*/
_mali_osk_errcode_t mali_memory_session_begin(struct mali_session_data *session);
/** @brief Close a memory session
*
* Called when a process closes the Mali device node.
*
* Memory allocated by the session will be freed
*
* @param session Pointer to the session to terminate
*/
void mali_memory_session_end(struct mali_session_data *session);
/** @brief Prepare Mali page tables for mapping
*
* This function will prepare the Mali page tables for mapping the memory
* described by \a descriptor.
*
* Page tables will be reference counted and allocated, if not yet present.
*
* @param descriptor Pointer to the memory descriptor to the mapping
*/
_mali_osk_errcode_t mali_mem_mali_map_prepare(mali_mem_allocation *descriptor);
/** @brief Free Mali page tables for mapping
*
* This function will unmap pages from Mali memory and free the page tables
* that are now unused.
*
* The updated pages in the Mali L2 cache will be invalidated, and the MMU TLBs will be zapped if necessary.
*
* @param descriptor Pointer to the memory descriptor to unmap
*/
void mali_mem_mali_map_free(mali_mem_allocation *descriptor);
/** @brief Parse resource and prepare the OS memory allocator
*
* @param size Maximum size to allocate for Mali GPU.
* @return _MALI_OSK_ERR_OK on success, otherwise failure.
*/
_mali_osk_errcode_t mali_memory_core_resource_os_memory(u32 size);
/** @brief Parse resource and prepare the dedicated memory allocator
*
* @param start Physical start address of dedicated Mali GPU memory.
* @param size Size of dedicated Mali GPU memory.
* @return _MALI_OSK_ERR_OK on success, otherwise failure.
*/
_mali_osk_errcode_t mali_memory_core_resource_dedicated_memory(u32 start, u32 size);
void mali_mem_ump_release(mali_mem_allocation *descriptor);
void mali_mem_external_release(mali_mem_allocation *descriptor);
#endif /* __MALI_MEMORY_H__ */

View File

@@ -0,0 +1,319 @@
/*
* Copyright (C) 2010-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.
*/
#include "mali_kernel_common.h"
#include "mali_memory.h"
#include "mali_memory_block_alloc.h"
#include "mali_osk.h"
#include <linux/mutex.h>
#define MALI_BLOCK_SIZE (256UL * 1024UL) /* 256 kB, remember to keep the ()s */
struct block_info {
struct block_info *next;
};
typedef struct block_info block_info;
typedef struct block_allocator {
struct mutex mutex;
block_info *all_blocks;
block_info *first_free;
u32 base;
u32 cpu_usage_adjust;
u32 num_blocks;
u32 free_blocks;
} block_allocator;
static block_allocator *mali_mem_block_gobal_allocator = NULL;
MALI_STATIC_INLINE u32 get_phys(block_allocator *info, block_info *block)
{
return info->base + ((block - info->all_blocks) * MALI_BLOCK_SIZE);
}
mali_mem_allocator *mali_mem_block_allocator_create(u32 base_address, u32 cpu_usage_adjust, u32 size)
{
block_allocator *info;
u32 usable_size;
u32 num_blocks;
usable_size = size & ~(MALI_BLOCK_SIZE - 1);
MALI_DEBUG_PRINT(3, ("Mali block allocator create for region starting at 0x%08X length 0x%08X\n", base_address, size));
MALI_DEBUG_PRINT(4, ("%d usable bytes\n", usable_size));
num_blocks = usable_size / MALI_BLOCK_SIZE;
MALI_DEBUG_PRINT(4, ("which becomes %d blocks\n", num_blocks));
if (usable_size == 0) {
MALI_DEBUG_PRINT(1, ("Memory block of size %d is unusable\n", size));
return NULL;
}
info = _mali_osk_malloc(sizeof(block_allocator));
if (NULL != info) {
mutex_init(&info->mutex);
info->all_blocks = _mali_osk_malloc(sizeof(block_info) * num_blocks);
if (NULL != info->all_blocks) {
u32 i;
info->first_free = NULL;
info->num_blocks = num_blocks;
info->free_blocks = num_blocks;
info->base = base_address;
info->cpu_usage_adjust = cpu_usage_adjust;
for ( i = 0; i < num_blocks; i++) {
info->all_blocks[i].next = info->first_free;
info->first_free = &info->all_blocks[i];
}
return (mali_mem_allocator *)info;
}
_mali_osk_free(info);
}
return NULL;
}
void mali_mem_block_allocator_destroy(mali_mem_allocator *allocator)
{
block_allocator *info = (block_allocator*)allocator;
info = mali_mem_block_gobal_allocator;
if (NULL == info) return;
MALI_DEBUG_ASSERT_POINTER(info);
_mali_osk_free(info->all_blocks);
_mali_osk_free(info);
}
static void mali_mem_block_mali_map(mali_mem_allocation *descriptor, u32 phys, u32 virt, u32 size)
{
struct mali_page_directory *pagedir = descriptor->session->page_directory;
u32 prop = descriptor->mali_mapping.properties;
u32 offset = 0;
while (size) {
mali_mmu_pagedir_update(pagedir, virt + offset, phys + offset, MALI_MMU_PAGE_SIZE, prop);
size -= MALI_MMU_PAGE_SIZE;
offset += MALI_MMU_PAGE_SIZE;
}
}
static int mali_mem_block_cpu_map(mali_mem_allocation *descriptor, struct vm_area_struct *vma, u32 mali_phys, u32 mapping_offset, u32 size, u32 cpu_usage_adjust)
{
u32 virt = vma->vm_start + mapping_offset;
u32 cpu_phys = mali_phys + cpu_usage_adjust;
u32 offset = 0;
int ret;
while (size) {
ret = vm_insert_pfn(vma, virt + offset, __phys_to_pfn(cpu_phys + offset));
if (unlikely(ret)) {
MALI_DEBUG_PRINT(1, ("Block allocator: Failed to insert pfn into vma\n"));
return 1;
}
size -= MALI_MMU_PAGE_SIZE;
offset += MALI_MMU_PAGE_SIZE;
}
return 0;
}
mali_mem_allocation *mali_mem_block_alloc(u32 mali_addr, u32 size, struct vm_area_struct *vma, struct mali_session_data *session)
{
_mali_osk_errcode_t err;
mali_mem_allocation *descriptor;
block_allocator *info;
u32 left;
block_info *last_allocated = NULL;
block_allocator_allocation *ret_allocation;
u32 offset = 0;
size = ALIGN(size, MALI_BLOCK_SIZE);
info = mali_mem_block_gobal_allocator;
if (NULL == info) return NULL;
left = size;
MALI_DEBUG_ASSERT(0 != left);
descriptor = mali_mem_descriptor_create(session, MALI_MEM_BLOCK);
if (NULL == descriptor) {
return NULL;
}
descriptor->mali_mapping.addr = mali_addr;
descriptor->size = size;
descriptor->cpu_mapping.addr = (void __user*)vma->vm_start;
descriptor->cpu_mapping.ref = 1;
if (VM_SHARED == (VM_SHARED & vma->vm_flags)) {
descriptor->mali_mapping.properties = MALI_MMU_FLAGS_DEFAULT;
} else {
/* Cached Mali memory mapping */
descriptor->mali_mapping.properties = MALI_MMU_FLAGS_FORCE_GP_READ_ALLOCATE;
vma->vm_flags |= VM_SHARED;
}
ret_allocation = &descriptor->block_mem.mem;
ret_allocation->mapping_length = 0;
_mali_osk_mutex_wait(session->memory_lock);
mutex_lock(&info->mutex);
if (left > (info->free_blocks * MALI_BLOCK_SIZE)) {
MALI_DEBUG_PRINT(2, ("Mali block allocator: not enough free blocks to service allocation (%u)\n", left));
mutex_unlock(&info->mutex);
_mali_osk_mutex_signal(session->memory_lock);
mali_mem_descriptor_destroy(descriptor);
return NULL;
}
err = mali_mem_mali_map_prepare(descriptor);
if (_MALI_OSK_ERR_OK != err) {
mutex_unlock(&info->mutex);
_mali_osk_mutex_signal(session->memory_lock);
mali_mem_descriptor_destroy(descriptor);
return NULL;
}
while ((left > 0) && (info->first_free)) {
block_info *block;
u32 phys_addr;
u32 current_mapping_size;
block = info->first_free;
info->first_free = info->first_free->next;
block->next = last_allocated;
last_allocated = block;
phys_addr = get_phys(info, block);
if (MALI_BLOCK_SIZE < left) {
current_mapping_size = MALI_BLOCK_SIZE;
} else {
current_mapping_size = left;
}
mali_mem_block_mali_map(descriptor, phys_addr, mali_addr + offset, current_mapping_size);
if (mali_mem_block_cpu_map(descriptor, vma, phys_addr, offset, current_mapping_size, info->cpu_usage_adjust)) {
/* release all memory back to the pool */
while (last_allocated) {
/* This relinks every block we've just allocated back into the free-list */
block = last_allocated->next;
last_allocated->next = info->first_free;
info->first_free = last_allocated;
last_allocated = block;
}
mutex_unlock(&info->mutex);
_mali_osk_mutex_signal(session->memory_lock);
mali_mem_mali_map_free(descriptor);
mali_mem_descriptor_destroy(descriptor);
return NULL;
}
left -= current_mapping_size;
offset += current_mapping_size;
ret_allocation->mapping_length += current_mapping_size;
--info->free_blocks;
}
mutex_unlock(&info->mutex);
_mali_osk_mutex_signal(session->memory_lock);
MALI_DEBUG_ASSERT(0 == left);
/* Record all the information about this allocation */
ret_allocation->last_allocated = last_allocated;
ret_allocation->info = info;
return descriptor;
}
void mali_mem_block_release(mali_mem_allocation *descriptor)
{
block_allocator *info = descriptor->block_mem.mem.info;
block_info *block, *next;
block_allocator_allocation *allocation = &descriptor->block_mem.mem;
MALI_DEBUG_ASSERT(MALI_MEM_BLOCK == descriptor->type);
block = allocation->last_allocated;
MALI_DEBUG_ASSERT_POINTER(block);
/* unmap */
mali_mem_mali_map_free(descriptor);
mutex_lock(&info->mutex);
while (block) {
MALI_DEBUG_ASSERT(!((block < info->all_blocks) || (block > (info->all_blocks + info->num_blocks))));
next = block->next;
/* relink into free-list */
block->next = info->first_free;
info->first_free = block;
/* advance the loop */
block = next;
++info->free_blocks;
}
mutex_unlock(&info->mutex);
}
u32 mali_mem_block_allocator_stat(void)
{
block_allocator *info = (block_allocator *)mali_mem_block_gobal_allocator;
if (NULL == info) return 0;
MALI_DEBUG_ASSERT_POINTER(info);
return (info->num_blocks - info->free_blocks) * MALI_BLOCK_SIZE;
}
_mali_osk_errcode_t mali_memory_core_resource_dedicated_memory(u32 start, u32 size)
{
mali_mem_allocator *allocator;
/* Do the low level linux operation first */
/* Request ownership of the memory */
if (_MALI_OSK_ERR_OK != _mali_osk_mem_reqregion(start, size, "Dedicated Mali GPU memory")) {
MALI_DEBUG_PRINT(1, ("Failed to request memory region for frame buffer (0x%08X - 0x%08X)\n", start, start + size - 1));
return _MALI_OSK_ERR_FAULT;
}
/* Create generic block allocator object to handle it */
allocator = mali_mem_block_allocator_create(start, 0 /* cpu_usage_adjust */, size);
if (NULL == allocator) {
MALI_DEBUG_PRINT(1, ("Memory bank registration failed\n"));
_mali_osk_mem_unreqregion(start, size);
MALI_ERROR(_MALI_OSK_ERR_FAULT);
}
mali_mem_block_gobal_allocator = (block_allocator*)allocator;
return _MALI_OSK_ERR_OK;
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (C) 2010, 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.
*/
#ifndef __MALI_BLOCK_ALLOCATOR_H__
#define __MALI_BLOCK_ALLOCATOR_H__
#include "mali_session.h"
#include "mali_memory.h"
#include "mali_memory_types.h"
typedef struct mali_mem_allocator mali_mem_allocator;
mali_mem_allocator *mali_block_allocator_create(u32 base_address, u32 cpu_usage_adjust, u32 size);
void mali_mem_block_allocator_destroy(mali_mem_allocator *allocator);
mali_mem_allocation *mali_mem_block_alloc(u32 mali_addr, u32 size, struct vm_area_struct *vma, struct mali_session_data *session);
void mali_mem_block_release(mali_mem_allocation *descriptor);
u32 mali_mem_block_allocator_stat(void);
#endif /* __MALI_BLOCK_ALLOCATOR_H__ */

View File

@@ -0,0 +1,434 @@
/*
* Copyright (C) 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.
*/
#include <linux/fs.h> /* file system operations */
#include <asm/uaccess.h> /* user space access */
#include <linux/dma-buf.h>
#include <linux/scatterlist.h>
#include <linux/rbtree.h>
#include <linux/platform_device.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/mutex.h>
#include "mali_ukk.h"
#include "mali_osk.h"
#include "mali_kernel_common.h"
#include "mali_session.h"
#include "mali_kernel_linux.h"
#include "mali_memory.h"
#include "mali_memory_dma_buf.h"
#include "mali_pp_job.h"
static void mali_dma_buf_unmap(struct mali_dma_buf_attachment *mem);
struct mali_dma_buf_attachment {
struct dma_buf *buf;
struct dma_buf_attachment *attachment;
struct sg_table *sgt;
struct mali_session_data *session;
int map_ref;
struct mutex map_lock;
mali_bool is_mapped;
wait_queue_head_t wait_queue;
};
static void mali_dma_buf_release(struct mali_dma_buf_attachment *mem)
{
MALI_DEBUG_PRINT(3, ("Mali DMA-buf: release attachment %p\n", mem));
MALI_DEBUG_ASSERT_POINTER(mem);
MALI_DEBUG_ASSERT_POINTER(mem->attachment);
MALI_DEBUG_ASSERT_POINTER(mem->buf);
#if defined(CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH)
/* We mapped implicitly on attach, so we need to unmap on release */
mali_dma_buf_unmap(mem);
#endif
/* Wait for buffer to become unmapped */
wait_event(mem->wait_queue, !mem->is_mapped);
MALI_DEBUG_ASSERT(!mem->is_mapped);
dma_buf_detach(mem->buf, mem->attachment);
dma_buf_put(mem->buf);
_mali_osk_free(mem);
}
void mali_mem_dma_buf_release(mali_mem_allocation *descriptor)
{
struct mali_dma_buf_attachment *mem = descriptor->dma_buf.attachment;
mali_dma_buf_release(mem);
}
/*
* Map DMA buf attachment \a mem into \a session at virtual address \a virt.
*/
static int mali_dma_buf_map(struct mali_dma_buf_attachment *mem, struct mali_session_data *session, u32 virt, u32 flags)
{
struct mali_page_directory *pagedir;
struct scatterlist *sg;
int i;
MALI_DEBUG_ASSERT_POINTER(mem);
MALI_DEBUG_ASSERT_POINTER(session);
MALI_DEBUG_ASSERT(mem->session == session);
mutex_lock(&mem->map_lock);
mem->map_ref++;
MALI_DEBUG_PRINT(5, ("Mali DMA-buf: map attachment %p, new map_ref = %d\n", mem, mem->map_ref));
if (1 == mem->map_ref) {
/* First reference taken, so we need to map the dma buf */
MALI_DEBUG_ASSERT(!mem->is_mapped);
pagedir = mali_session_get_page_directory(session);
MALI_DEBUG_ASSERT_POINTER(pagedir);
mem->sgt = dma_buf_map_attachment(mem->attachment, DMA_BIDIRECTIONAL);
if (IS_ERR_OR_NULL(mem->sgt)) {
MALI_DEBUG_PRINT_ERROR(("Failed to map dma-buf attachment\n"));
return -EFAULT;
}
for_each_sg(mem->sgt->sgl, sg, mem->sgt->nents, i) {
u32 size = sg_dma_len(sg);
dma_addr_t phys = sg_dma_address(sg);
/* sg must be page aligned. */
MALI_DEBUG_ASSERT(0 == size % MALI_MMU_PAGE_SIZE);
mali_mmu_pagedir_update(pagedir, virt, phys, size, MALI_MMU_FLAGS_DEFAULT);
virt += size;
}
if (flags & MALI_MEM_FLAG_MALI_GUARD_PAGE) {
u32 guard_phys;
MALI_DEBUG_PRINT(7, ("Mapping in extra guard page\n"));
guard_phys = sg_dma_address(mem->sgt->sgl);
mali_mmu_pagedir_update(pagedir, virt, guard_phys, MALI_MMU_PAGE_SIZE, MALI_MMU_FLAGS_DEFAULT);
}
mem->is_mapped = MALI_TRUE;
mutex_unlock(&mem->map_lock);
/* Wake up any thread waiting for buffer to become mapped */
wake_up_all(&mem->wait_queue);
} else {
MALI_DEBUG_ASSERT(mem->is_mapped);
mutex_unlock(&mem->map_lock);
}
return 0;
}
static void mali_dma_buf_unmap(struct mali_dma_buf_attachment *mem)
{
MALI_DEBUG_ASSERT_POINTER(mem);
MALI_DEBUG_ASSERT_POINTER(mem->attachment);
MALI_DEBUG_ASSERT_POINTER(mem->buf);
mutex_lock(&mem->map_lock);
mem->map_ref--;
MALI_DEBUG_PRINT(5, ("Mali DMA-buf: unmap attachment %p, new map_ref = %d\n", mem, mem->map_ref));
if (0 == mem->map_ref) {
dma_buf_unmap_attachment(mem->attachment, mem->sgt, DMA_BIDIRECTIONAL);
mem->is_mapped = MALI_FALSE;
}
mutex_unlock(&mem->map_lock);
/* Wake up any thread waiting for buffer to become unmapped */
wake_up_all(&mem->wait_queue);
}
#if !defined(CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH)
int mali_dma_buf_map_job(struct mali_pp_job *job)
{
mali_mem_allocation *descriptor;
struct mali_dma_buf_attachment *mem;
_mali_osk_errcode_t err;
int i;
int ret = 0;
_mali_osk_mutex_wait(job->session->memory_lock);
for (i = 0; i < job->num_memory_cookies; i++) {
int cookie = job->memory_cookies[i];
if (0 == cookie) {
/* 0 is not a valid cookie */
MALI_DEBUG_ASSERT(NULL == job->dma_bufs[i]);
continue;
}
MALI_DEBUG_ASSERT(0 < cookie);
err = mali_descriptor_mapping_get(job->session->descriptor_mapping,
cookie, (void**)&descriptor);
if (_MALI_OSK_ERR_OK != err) {
MALI_DEBUG_PRINT_ERROR(("Mali DMA-buf: Failed to get descriptor for cookie %d\n", cookie));
ret = -EFAULT;
MALI_DEBUG_ASSERT(NULL == job->dma_bufs[i]);
continue;
}
if (MALI_MEM_DMA_BUF != descriptor->type) {
/* Not a DMA-buf */
MALI_DEBUG_ASSERT(NULL == job->dma_bufs[i]);
continue;
}
mem = descriptor->dma_buf.attachment;
MALI_DEBUG_ASSERT_POINTER(mem);
MALI_DEBUG_ASSERT(mem->session == job->session);
err = mali_dma_buf_map(mem, mem->session, descriptor->mali_mapping.addr, descriptor->flags);
if (0 != err) {
MALI_DEBUG_PRINT_ERROR(("Mali DMA-buf: Failed to map dma-buf for cookie %d at mali address %x\b",
cookie, descriptor->mali_mapping.addr));
ret = -EFAULT;
MALI_DEBUG_ASSERT(NULL == job->dma_bufs[i]);
continue;
}
/* Add mem to list of DMA-bufs mapped for this job */
job->dma_bufs[i] = mem;
}
_mali_osk_mutex_signal(job->session->memory_lock);
return ret;
}
void mali_dma_buf_unmap_job(struct mali_pp_job *job)
{
int i;
for (i = 0; i < job->num_dma_bufs; i++) {
if (NULL == job->dma_bufs[i]) continue;
mali_dma_buf_unmap(job->dma_bufs[i]);
job->dma_bufs[i] = NULL;
}
}
#endif /* !CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH */
int mali_attach_dma_buf(struct mali_session_data *session, _mali_uk_attach_dma_buf_s __user *user_arg)
{
struct dma_buf *buf;
struct mali_dma_buf_attachment *mem;
_mali_uk_attach_dma_buf_s args;
mali_mem_allocation *descriptor;
int md;
int fd;
/* Get call arguments from user space. copy_from_user returns how many bytes which where NOT copied */
if (0 != copy_from_user(&args, (void __user *)user_arg, sizeof(_mali_uk_attach_dma_buf_s))) {
return -EFAULT;
}
if (args.mali_address & ~PAGE_MASK) {
MALI_DEBUG_PRINT_ERROR(("Requested address (0x%08x) is not page aligned\n", args.mali_address));
return -EINVAL;
}
if (args.mali_address >= args.mali_address + args.size) {
MALI_DEBUG_PRINT_ERROR(("Requested address and size (0x%08x + 0x%08x) is too big\n", args.mali_address, args.size));
return -EINVAL;
}
fd = args.mem_fd;
buf = dma_buf_get(fd);
if (IS_ERR_OR_NULL(buf)) {
MALI_DEBUG_PRINT_ERROR(("Failed to get dma-buf from fd: %d\n", fd));
return PTR_RET(buf);
}
/* Currently, mapping of the full buffer are supported. */
if (args.size != buf->size) {
MALI_DEBUG_PRINT_ERROR(("dma-buf size doesn't match mapping size.\n"));
dma_buf_put(buf);
return -EINVAL;
}
mem = _mali_osk_calloc(1, sizeof(struct mali_dma_buf_attachment));
if (NULL == mem) {
MALI_DEBUG_PRINT_ERROR(("Failed to allocate dma-buf tracing struct\n"));
dma_buf_put(buf);
return -ENOMEM;
}
mem->buf = buf;
mem->session = session;
mem->map_ref = 0;
mutex_init(&mem->map_lock);
init_waitqueue_head(&mem->wait_queue);
mem->attachment = dma_buf_attach(mem->buf, &mali_platform_device->dev);
if (NULL == mem->attachment) {
MALI_DEBUG_PRINT_ERROR(("Failed to attach to dma-buf %d\n", fd));
dma_buf_put(mem->buf);
_mali_osk_free(mem);
return -EFAULT;
}
/* Set up Mali memory descriptor */
descriptor = mali_mem_descriptor_create(session, MALI_MEM_DMA_BUF);
if (NULL == descriptor) {
MALI_DEBUG_PRINT_ERROR(("Failed to allocate descriptor dma-buf %d\n", fd));
mali_dma_buf_release(mem);
return -ENOMEM;
}
descriptor->size = args.size;
descriptor->mali_mapping.addr = args.mali_address;
descriptor->dma_buf.attachment = mem;
descriptor->flags |= MALI_MEM_FLAG_DONT_CPU_MAP;
if (args.flags & _MALI_MAP_EXTERNAL_MAP_GUARD_PAGE) {
descriptor->flags = MALI_MEM_FLAG_MALI_GUARD_PAGE;
}
_mali_osk_mutex_wait(session->memory_lock);
/* Map dma-buf into this session's page tables */
if (_MALI_OSK_ERR_OK != mali_mem_mali_map_prepare(descriptor)) {
_mali_osk_mutex_signal(session->memory_lock);
MALI_DEBUG_PRINT_ERROR(("Failed to map dma-buf on Mali\n"));
mali_mem_descriptor_destroy(descriptor);
mali_dma_buf_release(mem);
return -ENOMEM;
}
#if defined(CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH)
/* Map memory into session's Mali virtual address space. */
if (0 != mali_dma_buf_map(mem, session, descriptor->mali_mapping.addr, descriptor->flags)) {
mali_mem_mali_map_free(descriptor);
_mali_osk_mutex_signal(session->memory_lock);
MALI_DEBUG_PRINT_ERROR(("Failed to map dma-buf %d into Mali address space\n", fd));
mali_mem_descriptor_destroy(descriptor);
mali_dma_buf_release(mem);
return -ENOMEM;
}
#endif
_mali_osk_mutex_signal(session->memory_lock);
/* Get descriptor mapping for memory. */
if (_MALI_OSK_ERR_OK != mali_descriptor_mapping_allocate_mapping(session->descriptor_mapping, descriptor, &md)) {
_mali_osk_mutex_wait(session->memory_lock);
mali_mem_mali_map_free(descriptor);
_mali_osk_mutex_signal(session->memory_lock);
MALI_DEBUG_PRINT_ERROR(("Failed to create descriptor mapping for dma-buf %d\n", fd));
mali_mem_descriptor_destroy(descriptor);
mali_dma_buf_release(mem);
return -EFAULT;
}
/* Return stuff to user space */
if (0 != put_user(md, &user_arg->cookie)) {
_mali_osk_mutex_wait(session->memory_lock);
mali_mem_mali_map_free(descriptor);
_mali_osk_mutex_signal(session->memory_lock);
MALI_DEBUG_PRINT_ERROR(("Failed to return descriptor to user space for dma-buf %d\n", fd));
mali_descriptor_mapping_free(session->descriptor_mapping, md);
mali_dma_buf_release(mem);
return -EFAULT;
}
return 0;
}
int mali_release_dma_buf(struct mali_session_data *session, _mali_uk_release_dma_buf_s __user *user_arg)
{
int ret = 0;
_mali_uk_release_dma_buf_s args;
mali_mem_allocation *descriptor;
/* get call arguments from user space. copy_from_user returns how many bytes which where NOT copied */
if ( 0 != copy_from_user(&args, (void __user *)user_arg, sizeof(_mali_uk_release_dma_buf_s)) ) {
return -EFAULT;
}
MALI_DEBUG_PRINT(3, ("Mali DMA-buf: release descriptor cookie %d\n", args.cookie));
_mali_osk_mutex_wait(session->memory_lock);
descriptor = mali_descriptor_mapping_free(session->descriptor_mapping, args.cookie);
if (NULL != descriptor) {
MALI_DEBUG_PRINT(3, ("Mali DMA-buf: Releasing dma-buf at mali address %x\n", descriptor->mali_mapping.addr));
mali_mem_mali_map_free(descriptor);
mali_dma_buf_release(descriptor->dma_buf.attachment);
mali_mem_descriptor_destroy(descriptor);
} else {
MALI_DEBUG_PRINT_ERROR(("Invalid memory descriptor %d used to release dma-buf\n", args.cookie));
ret = -EINVAL;
}
_mali_osk_mutex_signal(session->memory_lock);
/* Return the error that _mali_ukk_map_external_ump_mem produced */
return ret;
}
int mali_dma_buf_get_size(struct mali_session_data *session, _mali_uk_dma_buf_get_size_s __user *user_arg)
{
_mali_uk_dma_buf_get_size_s args;
int fd;
struct dma_buf *buf;
/* get call arguments from user space. copy_from_user returns how many bytes which where NOT copied */
if ( 0 != copy_from_user(&args, (void __user *)user_arg, sizeof(_mali_uk_dma_buf_get_size_s)) ) {
return -EFAULT;
}
/* Do DMA-BUF stuff */
fd = args.mem_fd;
buf = dma_buf_get(fd);
if (IS_ERR_OR_NULL(buf)) {
MALI_DEBUG_PRINT_ERROR(("Failed to get dma-buf from fd: %d\n", fd));
return PTR_RET(buf);
}
if (0 != put_user(buf->size, &user_arg->size)) {
dma_buf_put(buf);
return -EFAULT;
}
dma_buf_put(buf);
return 0;
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2011-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.
*/
#ifndef __MALI_MEMORY_DMA_BUF_H__
#define __MALI_MEMORY_DMA_BUF_H__
#ifdef __cplusplus
extern "C" {
#endif
#include "mali_osk.h"
#include "mali_memory.h"
struct mali_pp_job;
struct mali_dma_buf_attachment;
int mali_attach_dma_buf(struct mali_session_data *session, _mali_uk_attach_dma_buf_s __user *arg);
int mali_release_dma_buf(struct mali_session_data *session, _mali_uk_release_dma_buf_s __user *arg);
int mali_dma_buf_get_size(struct mali_session_data *session, _mali_uk_dma_buf_get_size_s __user *arg);
void mali_mem_dma_buf_release(mali_mem_allocation *descriptor);
#if !defined(CONFIG_MALI_DMA_BUF_MAP_ON_ATTACH)
int mali_dma_buf_map_job(struct mali_pp_job *job);
void mali_dma_buf_unmap_job(struct mali_pp_job *job);
#endif
#ifdef __cplusplus
}
#endif
#endif /* __MALI_MEMORY_DMA_BUF_H__ */

View File

@@ -0,0 +1,127 @@
/*
* Copyright (C) 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.
*/
#include "mali_osk.h"
#include "mali_memory.h"
#include "mali_kernel_descriptor_mapping.h"
#include "mali_mem_validation.h"
#include "mali_uk_types.h"
void mali_mem_external_release(mali_mem_allocation *descriptor)
{
MALI_DEBUG_ASSERT(MALI_MEM_EXTERNAL == descriptor->type);
mali_mem_mali_map_free(descriptor);
}
_mali_osk_errcode_t _mali_ukk_map_external_mem(_mali_uk_map_external_mem_s *args)
{
struct mali_session_data *session;
mali_mem_allocation * descriptor;
int md;
_mali_osk_errcode_t err;
MALI_DEBUG_ASSERT_POINTER(args);
MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS);
session = (struct mali_session_data *)args->ctx;
MALI_CHECK_NON_NULL(session, _MALI_OSK_ERR_INVALID_ARGS);
/* check arguments */
/* NULL might be a valid Mali address */
if (! args->size) MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS);
/* size must be a multiple of the system page size */
if (args->size % _MALI_OSK_MALI_PAGE_SIZE) MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS);
MALI_DEBUG_PRINT(3,
("Requested to map physical memory 0x%x-0x%x into virtual memory 0x%x\n",
(void*)args->phys_addr,
(void*)(args->phys_addr + args->size -1),
(void*)args->mali_address)
);
/* Validate the mali physical range */
if (_MALI_OSK_ERR_OK != mali_mem_validation_check(args->phys_addr, args->size)) {
return _MALI_OSK_ERR_FAULT;
}
descriptor = mali_mem_descriptor_create(session, MALI_MEM_EXTERNAL);
if (NULL == descriptor) MALI_ERROR(_MALI_OSK_ERR_NOMEM);
descriptor->mali_mapping.addr = args->mali_address;
descriptor->size = args->size;
if (args->flags & _MALI_MAP_EXTERNAL_MAP_GUARD_PAGE) {
descriptor->flags = MALI_MEM_FLAG_MALI_GUARD_PAGE;
}
_mali_osk_mutex_wait(session->memory_lock);
{
u32 virt = descriptor->mali_mapping.addr;
u32 phys = args->phys_addr;
u32 size = args->size;
err = mali_mem_mali_map_prepare(descriptor);
if (_MALI_OSK_ERR_OK != err) {
_mali_osk_mutex_signal(session->memory_lock);
mali_mem_descriptor_destroy(descriptor);
return _MALI_OSK_ERR_NOMEM;
}
mali_mmu_pagedir_update(session->page_directory, virt, phys, size, MALI_MMU_FLAGS_DEFAULT);
if (descriptor->flags & MALI_MEM_FLAG_MALI_GUARD_PAGE) {
mali_mmu_pagedir_update(session->page_directory, virt + size, phys, _MALI_OSK_MALI_PAGE_SIZE, MALI_MMU_FLAGS_DEFAULT);
}
}
_mali_osk_mutex_signal(session->memory_lock);
if (_MALI_OSK_ERR_OK != mali_descriptor_mapping_allocate_mapping(session->descriptor_mapping, descriptor, &md)) {
_mali_osk_mutex_wait(session->memory_lock);
mali_mem_external_release(descriptor);
_mali_osk_mutex_signal(session->memory_lock);
mali_mem_descriptor_destroy(descriptor);
MALI_ERROR(_MALI_OSK_ERR_FAULT);
}
args->cookie = md;
MALI_SUCCESS;
}
_mali_osk_errcode_t _mali_ukk_unmap_external_mem( _mali_uk_unmap_external_mem_s *args )
{
mali_mem_allocation * descriptor;
void* old_value;
struct mali_session_data *session;
MALI_DEBUG_ASSERT_POINTER(args);
MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS);
session = (struct mali_session_data *)args->ctx;
MALI_CHECK_NON_NULL(session, _MALI_OSK_ERR_INVALID_ARGS);
if (_MALI_OSK_ERR_OK != mali_descriptor_mapping_get(session->descriptor_mapping, args->cookie, (void**)&descriptor)) {
MALI_DEBUG_PRINT(1, ("Invalid memory descriptor %d used to unmap external memory\n", args->cookie));
MALI_ERROR(_MALI_OSK_ERR_FAULT);
}
old_value = mali_descriptor_mapping_free(session->descriptor_mapping, args->cookie);
if (NULL != old_value) {
_mali_osk_mutex_wait(session->memory_lock);
mali_mem_external_release(descriptor);
_mali_osk_mutex_signal(session->memory_lock);
mali_mem_descriptor_destroy(descriptor);
}
MALI_SUCCESS;
}

View File

@@ -0,0 +1,556 @@
/*
* Copyright (C) 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.
*/
#include <linux/list.h>
#include <linux/mm.h>
#include <linux/mm_types.h>
#include <linux/fs.h>
#include <linux/dma-mapping.h>
#include <linux/version.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include "mali_osk.h"
#include "mali_memory.h"
#include "mali_memory_os_alloc.h"
#include "mali_kernel_linux.h"
/* Minimum size of allocator page pool */
#define MALI_OS_MEMORY_KERNEL_BUFFER_SIZE_IN_PAGES (MALI_OS_MEMORY_KERNEL_BUFFER_SIZE_IN_MB * 256)
#define MALI_OS_MEMORY_POOL_TRIM_JIFFIES (10 * CONFIG_HZ) /* Default to 10s */
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0)
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)
static int mali_mem_os_shrink(int nr_to_scan, gfp_t gfp_mask);
#else
static int mali_mem_os_shrink(struct shrinker *shrinker, int nr_to_scan, gfp_t gfp_mask);
#endif
#else
static int mali_mem_os_shrink(struct shrinker *shrinker, struct shrink_control *sc);
#endif
static void mali_mem_os_trim_pool(struct work_struct *work);
static struct mali_mem_os_allocator {
spinlock_t pool_lock;
struct list_head pool_pages;
size_t pool_count;
atomic_t allocated_pages;
size_t allocation_limit;
struct shrinker shrinker;
struct delayed_work timed_shrinker;
struct workqueue_struct *wq;
} mali_mem_os_allocator = {
.pool_lock = __SPIN_LOCK_UNLOCKED(pool_lock),
.pool_pages = LIST_HEAD_INIT(mali_mem_os_allocator.pool_pages),
.pool_count = 0,
.allocated_pages = ATOMIC_INIT(0),
.allocation_limit = 0,
.shrinker.shrink = mali_mem_os_shrink,
.shrinker.seeks = DEFAULT_SEEKS,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
.timed_shrinker = __DELAYED_WORK_INITIALIZER(mali_mem_os_allocator.timed_shrinker, mali_mem_os_trim_pool, TIMER_DEFERRABLE),
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38)
.timed_shrinker = __DEFERRED_WORK_INITIALIZER(mali_mem_os_allocator.timed_shrinker, mali_mem_os_trim_pool),
#else
.timed_shrinker = __DELAYED_WORK_INITIALIZER(mali_mem_os_allocator.timed_shrinker, mali_mem_os_trim_pool),
#endif
};
static void mali_mem_os_free(mali_mem_allocation *descriptor)
{
LIST_HEAD(pages);
MALI_DEBUG_ASSERT(MALI_MEM_OS == descriptor->type);
atomic_sub(descriptor->os_mem.count, &mali_mem_os_allocator.allocated_pages);
/* Put pages on pool. */
list_cut_position(&pages, &descriptor->os_mem.pages, descriptor->os_mem.pages.prev);
spin_lock(&mali_mem_os_allocator.pool_lock);
list_splice(&pages, &mali_mem_os_allocator.pool_pages);
mali_mem_os_allocator.pool_count += descriptor->os_mem.count;
spin_unlock(&mali_mem_os_allocator.pool_lock);
if (MALI_OS_MEMORY_KERNEL_BUFFER_SIZE_IN_PAGES < mali_mem_os_allocator.pool_count) {
MALI_DEBUG_PRINT(5, ("OS Mem: Starting pool trim timer %u\n", mali_mem_os_allocator.pool_count));
queue_delayed_work(mali_mem_os_allocator.wq, &mali_mem_os_allocator.timed_shrinker, MALI_OS_MEMORY_POOL_TRIM_JIFFIES);
}
}
static int mali_mem_os_alloc_pages(mali_mem_allocation *descriptor, u32 size)
{
struct page *new_page, *tmp;
LIST_HEAD(pages);
size_t page_count = PAGE_ALIGN(size) / _MALI_OSK_MALI_PAGE_SIZE;
size_t remaining = page_count;
u32 i;
MALI_DEBUG_ASSERT_POINTER(descriptor);
MALI_DEBUG_ASSERT(MALI_MEM_OS == descriptor->type);
INIT_LIST_HEAD(&descriptor->os_mem.pages);
descriptor->os_mem.count = page_count;
/* Grab pages from pool. */
{
size_t pool_pages;
spin_lock(&mali_mem_os_allocator.pool_lock);
pool_pages = min(remaining, mali_mem_os_allocator.pool_count);
for (i = pool_pages; i > 0; i--) {
BUG_ON(list_empty(&mali_mem_os_allocator.pool_pages));
list_move(mali_mem_os_allocator.pool_pages.next, &pages);
}
mali_mem_os_allocator.pool_count -= pool_pages;
remaining -= pool_pages;
spin_unlock(&mali_mem_os_allocator.pool_lock);
}
/* Process pages from pool. */
i = 0;
list_for_each_entry_safe(new_page, tmp, &pages, lru) {
BUG_ON(NULL == new_page);
list_move_tail(&new_page->lru, &descriptor->os_mem.pages);
}
/* Allocate new pages, if needed. */
for (i = 0; i < remaining; i++) {
dma_addr_t dma_addr;
new_page = alloc_page(GFP_HIGHUSER | __GFP_ZERO | __GFP_REPEAT | __GFP_NOWARN | __GFP_COLD);
if (unlikely(NULL == new_page)) {
/* Calculate the number of pages actually allocated, and free them. */
descriptor->os_mem.count = (page_count - remaining) + i;
atomic_add(descriptor->os_mem.count, &mali_mem_os_allocator.allocated_pages);
mali_mem_os_free(descriptor);
return -ENOMEM;
}
/* Ensure page is flushed from CPU caches. */
dma_addr = dma_map_page(&mali_platform_device->dev, new_page,
0, _MALI_OSK_MALI_PAGE_SIZE, DMA_TO_DEVICE);
/* Store page phys addr */
SetPagePrivate(new_page);
set_page_private(new_page, dma_addr);
list_add_tail(&new_page->lru, &descriptor->os_mem.pages);
}
atomic_add(page_count, &mali_mem_os_allocator.allocated_pages);
if (MALI_OS_MEMORY_KERNEL_BUFFER_SIZE_IN_PAGES > mali_mem_os_allocator.pool_count) {
MALI_DEBUG_PRINT(4, ("OS Mem: Stopping pool trim timer, only %u pages on pool\n", mali_mem_os_allocator.pool_count));
cancel_delayed_work(&mali_mem_os_allocator.timed_shrinker);
}
return 0;
}
static int mali_mem_os_mali_map(mali_mem_allocation *descriptor, struct mali_session_data *session)
{
struct mali_page_directory *pagedir = session->page_directory;
struct page *page;
_mali_osk_errcode_t err;
u32 virt = descriptor->mali_mapping.addr;
u32 prop = descriptor->mali_mapping.properties;
MALI_DEBUG_ASSERT(MALI_MEM_OS == descriptor->type);
err = mali_mem_mali_map_prepare(descriptor);
if (_MALI_OSK_ERR_OK != err) {
return -ENOMEM;
}
list_for_each_entry(page, &descriptor->os_mem.pages, lru) {
u32 phys = page_private(page);
mali_mmu_pagedir_update(pagedir, virt, phys, MALI_MMU_PAGE_SIZE, prop);
virt += MALI_MMU_PAGE_SIZE;
}
return 0;
}
static void mali_mem_os_mali_unmap(struct mali_session_data *session, mali_mem_allocation *descriptor)
{
mali_mem_mali_map_free(descriptor);
}
static int mali_mem_os_cpu_map(mali_mem_allocation *descriptor, struct vm_area_struct *vma)
{
struct page *page;
int ret;
unsigned long addr = vma->vm_start;
list_for_each_entry(page, &descriptor->os_mem.pages, lru) {
/* We should use vm_insert_page, but it does a dcache
* flush which makes it way slower than remap_pfn_range or vm_insert_pfn.
ret = vm_insert_page(vma, addr, page);
*/
ret = vm_insert_pfn(vma, addr, page_to_pfn(page));
if (unlikely(0 != ret)) {
return -EFAULT;
}
addr += _MALI_OSK_MALI_PAGE_SIZE;
}
return 0;
}
mali_mem_allocation *mali_mem_os_alloc(u32 mali_addr, u32 size, struct vm_area_struct *vma, struct mali_session_data *session)
{
mali_mem_allocation *descriptor;
int err;
if (atomic_read(&mali_mem_os_allocator.allocated_pages) * _MALI_OSK_MALI_PAGE_SIZE + size > mali_mem_os_allocator.allocation_limit) {
MALI_DEBUG_PRINT(2, ("Mali Mem: Unable to allocate %u bytes. Currently allocated: %lu, max limit %lu\n",
size,
atomic_read(&mali_mem_os_allocator.allocated_pages) * _MALI_OSK_MALI_PAGE_SIZE,
mali_mem_os_allocator.allocation_limit));
return NULL;
}
descriptor = mali_mem_descriptor_create(session, MALI_MEM_OS);
if (NULL == descriptor) return NULL;
descriptor->mali_mapping.addr = mali_addr;
descriptor->size = size;
descriptor->cpu_mapping.addr = (void __user*)vma->vm_start;
descriptor->cpu_mapping.ref = 1;
if (VM_SHARED == (VM_SHARED & vma->vm_flags)) {
descriptor->mali_mapping.properties = MALI_MMU_FLAGS_DEFAULT;
} else {
/* Cached Mali memory mapping */
descriptor->mali_mapping.properties = MALI_MMU_FLAGS_FORCE_GP_READ_ALLOCATE;
vma->vm_flags |= VM_SHARED;
}
err = mali_mem_os_alloc_pages(descriptor, size); /* Allocate pages */
if (0 != err) goto alloc_failed;
/* Take session memory lock */
_mali_osk_mutex_wait(session->memory_lock);
err = mali_mem_os_mali_map(descriptor, session); /* Map on Mali */
if (0 != err) goto mali_map_failed;
_mali_osk_mutex_signal(session->memory_lock);
err = mali_mem_os_cpu_map(descriptor, vma); /* Map on CPU */
if (0 != err) goto cpu_map_failed;
return descriptor;
cpu_map_failed:
mali_mem_os_mali_unmap(session, descriptor);
mali_map_failed:
_mali_osk_mutex_signal(session->memory_lock);
mali_mem_os_free(descriptor);
alloc_failed:
mali_mem_descriptor_destroy(descriptor);
MALI_DEBUG_PRINT(2, ("OS allocator: Failed to allocate memory (%d)\n", err));
return NULL;
}
void mali_mem_os_release(mali_mem_allocation *descriptor)
{
struct mali_session_data *session = descriptor->session;
/* Unmap the memory from the mali virtual address space. */
mali_mem_os_mali_unmap(session, descriptor);
/* Free pages */
mali_mem_os_free(descriptor);
}
#define MALI_MEM_OS_PAGE_TABLE_PAGE_POOL_SIZE 128
static struct {
struct {
u32 phys;
mali_io_address mapping;
} page[MALI_MEM_OS_PAGE_TABLE_PAGE_POOL_SIZE];
u32 count;
spinlock_t lock;
} mali_mem_page_table_page_pool = {
.count = 0,
.lock = __SPIN_LOCK_UNLOCKED(pool_lock),
};
_mali_osk_errcode_t mali_mem_os_get_table_page(u32 *phys, mali_io_address *mapping)
{
_mali_osk_errcode_t ret = _MALI_OSK_ERR_NOMEM;
spin_lock(&mali_mem_page_table_page_pool.lock);
if (0 < mali_mem_page_table_page_pool.count) {
u32 i = --mali_mem_page_table_page_pool.count;
*phys = mali_mem_page_table_page_pool.page[i].phys;
*mapping = mali_mem_page_table_page_pool.page[i].mapping;
ret = _MALI_OSK_ERR_OK;
}
spin_unlock(&mali_mem_page_table_page_pool.lock);
if (_MALI_OSK_ERR_OK != ret) {
*mapping = dma_alloc_writecombine(&mali_platform_device->dev, _MALI_OSK_MALI_PAGE_SIZE, phys, GFP_KERNEL);
if (NULL != *mapping) {
ret = _MALI_OSK_ERR_OK;
}
}
return ret;
}
void mali_mem_os_release_table_page(u32 phys, void *virt)
{
spin_lock(&mali_mem_page_table_page_pool.lock);
if (MALI_MEM_OS_PAGE_TABLE_PAGE_POOL_SIZE > mali_mem_page_table_page_pool.count) {
u32 i = mali_mem_page_table_page_pool.count;
mali_mem_page_table_page_pool.page[i].phys = phys;
mali_mem_page_table_page_pool.page[i].mapping = virt;
++mali_mem_page_table_page_pool.count;
spin_unlock(&mali_mem_page_table_page_pool.lock);
} else {
spin_unlock(&mali_mem_page_table_page_pool.lock);
dma_free_writecombine(&mali_platform_device->dev, _MALI_OSK_MALI_PAGE_SIZE, virt, phys);
}
}
static void mali_mem_os_free_page(struct page *page)
{
BUG_ON(page_count(page) != 1);
dma_unmap_page(&mali_platform_device->dev, page_private(page),
_MALI_OSK_MALI_PAGE_SIZE, DMA_TO_DEVICE);
ClearPagePrivate(page);
__free_page(page);
}
/* The maximum number of page table pool pages to free in one go. */
#define MALI_MEM_OS_CHUNK_TO_FREE 64UL
/* Free a certain number of pages from the page table page pool.
* The pool lock must be held when calling the function, and the lock will be
* released before returning.
*/
static void mali_mem_os_page_table_pool_free(size_t nr_to_free)
{
u32 phys_arr[MALI_MEM_OS_CHUNK_TO_FREE];
void *virt_arr[MALI_MEM_OS_CHUNK_TO_FREE];
u32 i;
MALI_DEBUG_ASSERT(nr_to_free <= MALI_MEM_OS_CHUNK_TO_FREE);
/* Remove nr_to_free pages from the pool and store them locally on stack. */
for (i = 0; i < nr_to_free; i++) {
u32 pool_index = mali_mem_page_table_page_pool.count - i - 1;
phys_arr[i] = mali_mem_page_table_page_pool.page[pool_index].phys;
virt_arr[i] = mali_mem_page_table_page_pool.page[pool_index].mapping;
}
mali_mem_page_table_page_pool.count -= nr_to_free;
spin_unlock(&mali_mem_page_table_page_pool.lock);
/* After releasing the spinlock: free the pages we removed from the pool. */
for (i = 0; i < nr_to_free; i++) {
dma_free_writecombine(&mali_platform_device->dev, _MALI_OSK_MALI_PAGE_SIZE, virt_arr[i], phys_arr[i]);
}
}
static void mali_mem_os_trim_page_table_page_pool(void)
{
size_t nr_to_free = 0;
size_t nr_to_keep;
/* Keep 2 page table pages for each 1024 pages in the page cache. */
nr_to_keep = mali_mem_os_allocator.pool_count / 512;
/* And a minimum of eight pages, to accomodate new sessions. */
nr_to_keep += 8;
if (0 == spin_trylock(&mali_mem_page_table_page_pool.lock)) return;
if (nr_to_keep < mali_mem_page_table_page_pool.count) {
nr_to_free = mali_mem_page_table_page_pool.count - nr_to_keep;
nr_to_free = min((size_t)MALI_MEM_OS_CHUNK_TO_FREE, nr_to_free);
}
/* Pool lock will be released by the callee. */
mali_mem_os_page_table_pool_free(nr_to_free);
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0)
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)
static int mali_mem_os_shrink(int nr_to_scan, gfp_t gfp_mask)
#else
static int mali_mem_os_shrink(struct shrinker *shrinker, int nr_to_scan, gfp_t gfp_mask)
#endif
#else
static int mali_mem_os_shrink(struct shrinker *shrinker, struct shrink_control *sc)
#endif
{
struct page *page, *tmp;
unsigned long flags;
struct list_head *le, pages;
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0)
int nr = nr_to_scan;
#else
int nr = sc->nr_to_scan;
#endif
if (0 == nr) {
return mali_mem_os_allocator.pool_count + mali_mem_page_table_page_pool.count;
}
if (0 == mali_mem_os_allocator.pool_count) {
/* No pages availble */
return 0;
}
if (0 == spin_trylock_irqsave(&mali_mem_os_allocator.pool_lock, flags)) {
/* Not able to lock. */
return -1;
}
/* Release from general page pool */
nr = min((size_t)nr, mali_mem_os_allocator.pool_count);
mali_mem_os_allocator.pool_count -= nr;
list_for_each(le, &mali_mem_os_allocator.pool_pages) {
--nr;
if (0 == nr) break;
}
list_cut_position(&pages, &mali_mem_os_allocator.pool_pages, le);
spin_unlock_irqrestore(&mali_mem_os_allocator.pool_lock, flags);
list_for_each_entry_safe(page, tmp, &pages, lru) {
mali_mem_os_free_page(page);
}
/* Release some pages from page table page pool */
mali_mem_os_trim_page_table_page_pool();
if (MALI_OS_MEMORY_KERNEL_BUFFER_SIZE_IN_PAGES > mali_mem_os_allocator.pool_count) {
/* Pools are empty, stop timer */
MALI_DEBUG_PRINT(5, ("Stopping timer, only %u pages on pool\n", mali_mem_os_allocator.pool_count));
cancel_delayed_work(&mali_mem_os_allocator.timed_shrinker);
}
return mali_mem_os_allocator.pool_count + mali_mem_page_table_page_pool.count;
}
static void mali_mem_os_trim_pool(struct work_struct *data)
{
struct page *page, *tmp;
struct list_head *le;
LIST_HEAD(pages);
size_t nr_to_free;
MALI_IGNORE(data);
MALI_DEBUG_PRINT(3, ("OS Mem: Trimming pool %u\n", mali_mem_os_allocator.pool_count));
/* Release from general page pool */
spin_lock(&mali_mem_os_allocator.pool_lock);
if (MALI_OS_MEMORY_KERNEL_BUFFER_SIZE_IN_PAGES < mali_mem_os_allocator.pool_count) {
size_t count = mali_mem_os_allocator.pool_count - MALI_OS_MEMORY_KERNEL_BUFFER_SIZE_IN_PAGES;
/* Free half the pages on the pool above the static limit. Or 64 pages, 256KB. */
nr_to_free = max(count / 2, (size_t)64);
mali_mem_os_allocator.pool_count -= nr_to_free;
list_for_each(le, &mali_mem_os_allocator.pool_pages) {
--nr_to_free;
if (0 == nr_to_free) break;
}
list_cut_position(&pages, &mali_mem_os_allocator.pool_pages, le);
}
spin_unlock(&mali_mem_os_allocator.pool_lock);
list_for_each_entry_safe(page, tmp, &pages, lru) {
mali_mem_os_free_page(page);
}
/* Release some pages from page table page pool */
mali_mem_os_trim_page_table_page_pool();
if (MALI_OS_MEMORY_KERNEL_BUFFER_SIZE_IN_PAGES < mali_mem_os_allocator.pool_count) {
MALI_DEBUG_PRINT(4, ("OS Mem: Starting pool trim timer %u\n", mali_mem_os_allocator.pool_count));
queue_delayed_work(mali_mem_os_allocator.wq, &mali_mem_os_allocator.timed_shrinker, MALI_OS_MEMORY_POOL_TRIM_JIFFIES);
}
}
_mali_osk_errcode_t mali_mem_os_init(void)
{
mali_mem_os_allocator.wq = alloc_workqueue("mali-mem", WQ_UNBOUND, 1);
if (NULL == mali_mem_os_allocator.wq) {
return _MALI_OSK_ERR_NOMEM;
}
register_shrinker(&mali_mem_os_allocator.shrinker);
return _MALI_OSK_ERR_OK;
}
void mali_mem_os_term(void)
{
struct page *page, *tmp;
unregister_shrinker(&mali_mem_os_allocator.shrinker);
cancel_delayed_work_sync(&mali_mem_os_allocator.timed_shrinker);
destroy_workqueue(mali_mem_os_allocator.wq);
spin_lock(&mali_mem_os_allocator.pool_lock);
list_for_each_entry_safe(page, tmp, &mali_mem_os_allocator.pool_pages, lru) {
mali_mem_os_free_page(page);
--mali_mem_os_allocator.pool_count;
}
BUG_ON(mali_mem_os_allocator.pool_count);
spin_unlock(&mali_mem_os_allocator.pool_lock);
/* Release from page table page pool */
do {
u32 nr_to_free;
spin_lock(&mali_mem_page_table_page_pool.lock);
nr_to_free = min((size_t)MALI_MEM_OS_CHUNK_TO_FREE, mali_mem_page_table_page_pool.count);
/* Pool lock will be released by the callee. */
mali_mem_os_page_table_pool_free(nr_to_free);
} while (0 != mali_mem_page_table_page_pool.count);
}
_mali_osk_errcode_t mali_memory_core_resource_os_memory(u32 size)
{
mali_mem_os_allocator.allocation_limit = size;
MALI_SUCCESS;
}
u32 mali_mem_os_stat(void)
{
return atomic_read(&mali_mem_os_allocator.allocated_pages) * _MALI_OSK_MALI_PAGE_SIZE;
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (C) 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.
*/
#ifndef __MALI_MEMORY_OS_ALLOC_H__
#define __MALI_MEMORY_OS_ALLOC_H__
#include "mali_osk.h"
#include "mali_session.h"
#include "mali_memory_types.h"
/* OS memory allocator */
/** @brief Allocate memory from OS
*
* This function will create a descriptor, allocate pages and map these on the CPU and Mali.
*
* @param mali_addr Mali virtual address to use for Mali mapping
* @param size Size to allocate
* @param vma Pointer to vma for CPU mapping
* @param session Pointer to session doing the allocation
*/
mali_mem_allocation *mali_mem_os_alloc(u32 mali_addr, u32 size, struct vm_area_struct *vma, struct mali_session_data *session);
/** @brief Release Mali OS memory
*
* The session memory_lock must be held when calling this function.
*
* @param descriptor Pointer to the descriptor to release
*/
void mali_mem_os_release(mali_mem_allocation *descriptor);
_mali_osk_errcode_t mali_mem_os_get_table_page(u32 *phys, mali_io_address *mapping);
void mali_mem_os_release_table_page(u32 phys, void *virt);
_mali_osk_errcode_t mali_mem_os_init(void);
void mali_mem_os_term(void);
u32 mali_mem_os_stat(void);
#endif /* __MALI_MEMORY_OS_ALLOC_H__ */

View File

@@ -0,0 +1,100 @@
/*
* Copyright (C) 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.
*/
#ifndef __MALI_MEMORY_TYPES_H__
#define __MALI_MEMORY_TYPES_H__
#if defined(CONFIG_MALI400_UMP)
#include "ump_kernel_interface.h"
#endif
typedef u32 mali_address_t;
typedef enum mali_mem_type {
MALI_MEM_OS,
MALI_MEM_EXTERNAL,
MALI_MEM_DMA_BUF,
MALI_MEM_UMP,
MALI_MEM_BLOCK,
} mali_mem_type;
typedef struct mali_mem_os_mem {
struct list_head pages;
u32 count;
} mali_mem_os_mem;
typedef struct mali_mem_dma_buf {
#if defined(CONFIG_DMA_SHARED_BUFFER)
struct mali_dma_buf_attachment *attachment;
#endif
} mali_mem_dma_buf;
typedef struct mali_mem_external {
dma_addr_t phys;
u32 size;
} mali_mem_external;
typedef struct mali_mem_ump {
#if defined(CONFIG_MALI400_UMP)
ump_dd_handle handle;
#endif
} mali_mem_ump;
typedef struct block_allocator_allocation {
/* The list will be released in reverse order */
struct block_info *last_allocated;
u32 mapping_length;
struct block_allocator *info;
} block_allocator_allocation;
typedef struct mali_mem_block_mem {
block_allocator_allocation mem;
} mali_mem_block_mem;
typedef struct mali_mem_virt_mali_mapping {
mali_address_t addr; /* Virtual Mali address */
u32 properties; /* MMU Permissions + cache, must match MMU HW */
} mali_mem_virt_mali_mapping;
typedef struct mali_mem_virt_cpu_mapping {
void __user *addr;
u32 ref;
} mali_mem_virt_cpu_mapping;
#define MALI_MEM_ALLOCATION_VALID_MAGIC 0xdeda110c
#define MALI_MEM_ALLOCATION_FREED_MAGIC 0x10101010
typedef struct mali_mem_allocation {
MALI_DEBUG_CODE(u32 magic);
mali_mem_type type; /**< Type of memory */
int id; /**< ID in the descriptor map for this allocation */
u32 size; /**< Size of the allocation */
u32 flags; /**< Flags for this allocation */
struct mali_session_data *session; /**< Pointer to session that owns the allocation */
/* Union selected by type. */
union {
mali_mem_os_mem os_mem; /**< MALI_MEM_OS */
mali_mem_external ext_mem; /**< MALI_MEM_EXTERNAL */
mali_mem_dma_buf dma_buf; /**< MALI_MEM_DMA_BUF */
mali_mem_ump ump_mem; /**< MALI_MEM_UMP */
mali_mem_block_mem block_mem; /**< MALI_MEM_BLOCK */
};
mali_mem_virt_cpu_mapping cpu_mapping; /**< CPU mapping */
mali_mem_virt_mali_mapping mali_mapping; /**< Mali mapping */
} mali_mem_allocation;
#define MALI_MEM_FLAG_MALI_GUARD_PAGE (1 << 0)
#define MALI_MEM_FLAG_DONT_CPU_MAP (1 << 1)
#endif /* __MALI_MEMORY_TYPES__ */

View File

@@ -0,0 +1,215 @@
/*
* Copyright (C) 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.
*/
#include "mali_ukk.h"
#include "mali_osk.h"
#include "mali_kernel_common.h"
#include "mali_session.h"
#include "mali_kernel_linux.h"
#include "mali_memory.h"
#include "ump_kernel_interface.h"
static int mali_ump_map(struct mali_session_data *session, mali_mem_allocation *descriptor)
{
ump_dd_handle ump_mem;
u32 nr_blocks;
u32 i;
ump_dd_physical_block *ump_blocks;
struct mali_page_directory *pagedir;
u32 offset = 0;
u32 prop;
_mali_osk_errcode_t err;
MALI_DEBUG_ASSERT_POINTER(session);
MALI_DEBUG_ASSERT_POINTER(descriptor);
MALI_DEBUG_ASSERT(MALI_MEM_UMP == descriptor->type);
ump_mem = descriptor->ump_mem.handle;
MALI_DEBUG_ASSERT(UMP_DD_HANDLE_INVALID != ump_mem);
nr_blocks = ump_dd_phys_block_count_get(ump_mem);
if (nr_blocks == 0) {
MALI_DEBUG_PRINT(1, ("No block count\n"));
return -EINVAL;
}
ump_blocks = _mali_osk_malloc(sizeof(*ump_blocks)*nr_blocks);
if (NULL == ump_blocks) {
return -ENOMEM;
}
if (UMP_DD_INVALID == ump_dd_phys_blocks_get(ump_mem, ump_blocks, nr_blocks)) {
_mali_osk_free(ump_blocks);
return -EFAULT;
}
pagedir = session->page_directory;
prop = descriptor->mali_mapping.properties;
err = mali_mem_mali_map_prepare(descriptor);
if (_MALI_OSK_ERR_OK != err) {
MALI_DEBUG_PRINT(1, ("Mapping of UMP memory failed\n"));
_mali_osk_free(ump_blocks);
return -ENOMEM;
}
for(i = 0; i < nr_blocks; ++i) {
u32 virt = descriptor->mali_mapping.addr + offset;
MALI_DEBUG_PRINT(7, ("Mapping in 0x%08x size %d\n", ump_blocks[i].addr , ump_blocks[i].size));
mali_mmu_pagedir_update(pagedir, virt, ump_blocks[i].addr,
ump_blocks[i].size, prop);
offset += ump_blocks[i].size;
}
if (descriptor->flags & _MALI_MAP_EXTERNAL_MAP_GUARD_PAGE) {
u32 virt = descriptor->mali_mapping.addr + offset;
/* Map in an extra virtual guard page at the end of the VMA */
MALI_DEBUG_PRINT(6, ("Mapping in extra guard page\n"));
mali_mmu_pagedir_update(pagedir, virt, ump_blocks[0].addr, _MALI_OSK_MALI_PAGE_SIZE, prop);
offset += _MALI_OSK_MALI_PAGE_SIZE;
}
_mali_osk_free(ump_blocks);
return 0;
}
void mali_ump_unmap(struct mali_session_data *session, mali_mem_allocation *descriptor)
{
ump_dd_handle ump_mem;
struct mali_page_directory *pagedir;
ump_mem = descriptor->ump_mem.handle;
pagedir = session->page_directory;
MALI_DEBUG_ASSERT(UMP_DD_HANDLE_INVALID != ump_mem);
mali_mem_mali_map_free(descriptor);
ump_dd_reference_release(ump_mem);
return;
}
_mali_osk_errcode_t _mali_ukk_attach_ump_mem(_mali_uk_attach_ump_mem_s *args)
{
ump_dd_handle ump_mem;
struct mali_session_data *session;
mali_mem_allocation *descriptor;
int md, ret;
MALI_DEBUG_ASSERT_POINTER(args);
MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS);
session = (struct mali_session_data *)args->ctx;
MALI_CHECK_NON_NULL(session, _MALI_OSK_ERR_INVALID_ARGS);
/* check arguments */
/* NULL might be a valid Mali address */
if (!args->size) MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS);
/* size must be a multiple of the system page size */
if (args->size % _MALI_OSK_MALI_PAGE_SIZE) MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS);
MALI_DEBUG_PRINT(3,
("Requested to map ump memory with secure id %d into virtual memory 0x%08X, size 0x%08X\n",
args->secure_id, args->mali_address, args->size));
ump_mem = ump_dd_handle_create_from_secure_id((int)args->secure_id);
if (UMP_DD_HANDLE_INVALID == ump_mem) MALI_ERROR(_MALI_OSK_ERR_FAULT);
descriptor = mali_mem_descriptor_create(session, MALI_MEM_UMP);
if (NULL == descriptor) {
ump_dd_reference_release(ump_mem);
MALI_ERROR(_MALI_OSK_ERR_NOMEM);
}
descriptor->ump_mem.handle = ump_mem;
descriptor->mali_mapping.addr = args->mali_address;
descriptor->size = args->size;
descriptor->mali_mapping.properties = MALI_MMU_FLAGS_DEFAULT;
descriptor->flags |= MALI_MEM_FLAG_DONT_CPU_MAP;
if (args->flags & _MALI_MAP_EXTERNAL_MAP_GUARD_PAGE) {
descriptor->flags = MALI_MEM_FLAG_MALI_GUARD_PAGE;
}
_mali_osk_mutex_wait(session->memory_lock);
ret = mali_ump_map(session, descriptor);
if (0 != ret) {
_mali_osk_mutex_signal(session->memory_lock);
ump_dd_reference_release(ump_mem);
mali_mem_descriptor_destroy(descriptor);
MALI_ERROR(_MALI_OSK_ERR_NOMEM);
}
_mali_osk_mutex_signal(session->memory_lock);
if (_MALI_OSK_ERR_OK != mali_descriptor_mapping_allocate_mapping(session->descriptor_mapping, descriptor, &md)) {
ump_dd_reference_release(ump_mem);
mali_mem_descriptor_destroy(descriptor);
MALI_ERROR(_MALI_OSK_ERR_FAULT);
}
args->cookie = md;
MALI_DEBUG_PRINT(5,("Returning from UMP attach\n"));
MALI_SUCCESS;
}
void mali_mem_ump_release(mali_mem_allocation *descriptor)
{
struct mali_session_data *session = descriptor->session;
MALI_DEBUG_ASSERT(MALI_MEM_UMP == descriptor->type);
mali_ump_unmap(session, descriptor);
}
_mali_osk_errcode_t _mali_ukk_release_ump_mem(_mali_uk_release_ump_mem_s *args)
{
mali_mem_allocation * descriptor;
struct mali_session_data *session;
MALI_DEBUG_ASSERT_POINTER(args);
MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS);
session = (struct mali_session_data *)args->ctx;
MALI_CHECK_NON_NULL(session, _MALI_OSK_ERR_INVALID_ARGS);
if (_MALI_OSK_ERR_OK != mali_descriptor_mapping_get(session->descriptor_mapping, args->cookie, (void**)&descriptor)) {
MALI_DEBUG_PRINT(1, ("Invalid memory descriptor %d used to release ump memory\n", args->cookie));
MALI_ERROR(_MALI_OSK_ERR_FAULT);
}
descriptor = mali_descriptor_mapping_free(session->descriptor_mapping, args->cookie);
if (NULL != descriptor) {
_mali_osk_mutex_wait(session->memory_lock);
mali_mem_ump_release(descriptor);
_mali_osk_mutex_signal(session->memory_lock);
mali_mem_descriptor_destroy(descriptor);
}
MALI_SUCCESS;
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) 2010, 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 mali_osk_atomics.c
* Implementation of the OS abstraction layer for the kernel device driver
*/
#include "mali_osk.h"
#include <asm/atomic.h>
#include "mali_kernel_common.h"
void _mali_osk_atomic_dec( _mali_osk_atomic_t *atom )
{
atomic_dec((atomic_t *)&atom->u.val);
}
u32 _mali_osk_atomic_dec_return( _mali_osk_atomic_t *atom )
{
return atomic_dec_return((atomic_t *)&atom->u.val);
}
void _mali_osk_atomic_inc( _mali_osk_atomic_t *atom )
{
atomic_inc((atomic_t *)&atom->u.val);
}
u32 _mali_osk_atomic_inc_return( _mali_osk_atomic_t *atom )
{
return atomic_inc_return((atomic_t *)&atom->u.val);
}
_mali_osk_errcode_t _mali_osk_atomic_init( _mali_osk_atomic_t *atom, u32 val )
{
MALI_CHECK_NON_NULL(atom, _MALI_OSK_ERR_INVALID_ARGS);
atomic_set((atomic_t *)&atom->u.val, val);
return _MALI_OSK_ERR_OK;
}
u32 _mali_osk_atomic_read( _mali_osk_atomic_t *atom )
{
return atomic_read((atomic_t *)&atom->u.val);
}
void _mali_osk_atomic_term( _mali_osk_atomic_t *atom )
{
MALI_IGNORE(atom);
}
u32 _mali_osk_atomic_xchg( _mali_osk_atomic_t *atom, u32 val )
{
return atomic_xchg((atomic_t*)&atom->u.val, val);
}

View File

@@ -0,0 +1,216 @@
/*
* Copyright (C) 2010-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 mali_osk_irq.c
* Implementation of the OS abstraction layer for the kernel device driver
*/
#include <linux/types.h>
#include <mach/cpu.h>
#include <linux/slab.h> /* For memory allocation */
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include "mali_osk.h"
#include "mali_kernel_common.h"
typedef struct _mali_osk_irq_t_struct {
u32 irqnum;
void *data;
_mali_osk_irq_uhandler_t uhandler;
} mali_osk_irq_object_t;
typedef irqreturn_t (*irq_handler_func_t)(int, void *, struct pt_regs *);
static irqreturn_t irq_handler_upper_half (int port_name, void* dev_id ); /* , struct pt_regs *regs*/
#if MESON_CPU_TYPE == MESON_CPU_TYPE_MESON6
u32 get_irqnum(struct _mali_osk_irq_t_struct* irq)
{
if (irq)
return irq->irqnum;
else
return 0;
}
#endif
#if defined(DEBUG)
#if 0
struct test_interrupt_data {
_mali_osk_irq_ack_t ack_func;
void *probe_data;
mali_bool interrupt_received;
wait_queue_head_t wq;
};
static irqreturn_t test_interrupt_upper_half(int port_name, void *dev_id)
{
irqreturn_t ret = IRQ_NONE;
struct test_interrupt_data *data = (struct test_interrupt_data *)dev_id;
if (_MALI_OSK_ERR_OK == data->ack_func(data->probe_data)) {
data->interrupt_received = MALI_TRUE;
wake_up(&data->wq);
ret = IRQ_HANDLED;
}
return ret;
}
static _mali_osk_errcode_t test_interrupt(u32 irqnum,
_mali_osk_irq_trigger_t trigger_func,
_mali_osk_irq_ack_t ack_func,
void *probe_data,
const char *description)
{
unsigned long irq_flags = 0;
struct test_interrupt_data data = {
.ack_func = ack_func,
.probe_data = probe_data,
.interrupt_received = MALI_FALSE,
};
#if defined(CONFIG_MALI_SHARED_INTERRUPTS)
irq_flags |= IRQF_SHARED;
#endif /* defined(CONFIG_MALI_SHARED_INTERRUPTS) */
if (0 != request_irq(irqnum, test_interrupt_upper_half, irq_flags, description, &data)) {
MALI_DEBUG_PRINT(2, ("Unable to install test IRQ handler for core '%s'\n", description));
return _MALI_OSK_ERR_FAULT;
}
init_waitqueue_head(&data.wq);
trigger_func(probe_data);
wait_event_timeout(data.wq, data.interrupt_received, 100);
free_irq(irqnum, &data);
if (data.interrupt_received) {
MALI_DEBUG_PRINT(3, ("%s: Interrupt test OK\n", description));
return _MALI_OSK_ERR_OK;
} else {
MALI_PRINT_ERROR(("%s: Failed interrupt test on %u\n", description, irqnum));
return _MALI_OSK_ERR_FAULT;
}
}
#endif
#endif /* defined(DEBUG) */
_mali_osk_irq_t *_mali_osk_irq_init( u32 irqnum, _mali_osk_irq_uhandler_t uhandler, void *int_data, _mali_osk_irq_trigger_t trigger_func, _mali_osk_irq_ack_t ack_func, void *probe_data, const char *description )
{
mali_osk_irq_object_t *irq_object;
unsigned long irq_flags = 0;
#if defined(CONFIG_MALI_SHARED_INTERRUPTS)
irq_flags |= IRQF_SHARED;
#endif /* defined(CONFIG_MALI_SHARED_INTERRUPTS) */
irq_object = kmalloc(sizeof(mali_osk_irq_object_t), GFP_KERNEL);
if (NULL == irq_object) {
return NULL;
}
if (-1 == irqnum) {
/* Probe for IRQ */
if ( (NULL != trigger_func) && (NULL != ack_func) ) {
unsigned long probe_count = 3;
_mali_osk_errcode_t err;
int irq;
MALI_DEBUG_PRINT(2, ("Probing for irq\n"));
do {
unsigned long mask;
mask = probe_irq_on();
trigger_func(probe_data);
_mali_osk_time_ubusydelay(5);
irq = probe_irq_off(mask);
err = ack_func(probe_data);
} while (irq < 0 && (err == _MALI_OSK_ERR_OK) && probe_count--);
if (irq < 0 || (_MALI_OSK_ERR_OK != err)) irqnum = -1;
else irqnum = irq;
} else irqnum = -1; /* no probe functions, fault */
if (-1 != irqnum) {
/* found an irq */
MALI_DEBUG_PRINT(2, ("Found irq %d\n", irqnum));
} else {
MALI_DEBUG_PRINT(2, ("Probe for irq failed\n"));
}
}
irq_object->irqnum = irqnum;
irq_object->uhandler = uhandler;
irq_object->data = int_data;
if (-1 == irqnum) {
MALI_DEBUG_PRINT(2, ("No IRQ for core '%s' found during probe\n", description));
kfree(irq_object);
return NULL;
}
#if defined(DEBUG)
#if 0
/* Verify that the configured interrupt settings are working */
if (_MALI_OSK_ERR_OK != test_interrupt(irqnum, trigger_func, ack_func, probe_data, description)) {
MALI_DEBUG_PRINT(2, ("Test of IRQ handler for core '%s' failed\n", description));
kfree(irq_object);
return NULL;
}
#endif
#endif
if (0 != request_irq(irqnum, irq_handler_upper_half, irq_flags, description, irq_object)) {
MALI_DEBUG_PRINT(2, ("Unable to install IRQ handler for core '%s'\n", description));
kfree(irq_object);
return NULL;
}
return irq_object;
}
void _mali_osk_irq_term( _mali_osk_irq_t *irq )
{
mali_osk_irq_object_t *irq_object = (mali_osk_irq_object_t *)irq;
free_irq(irq_object->irqnum, irq_object);
kfree(irq_object);
}
/** This function is called directly in interrupt context from the OS just after
* the CPU get the hw-irq from mali, or other devices on the same IRQ-channel.
* It is registered one of these function for each mali core. When an interrupt
* arrives this function will be called equal times as registered mali cores.
* That means that we only check one mali core in one function call, and the
* core we check for each turn is given by the \a dev_id variable.
* If we detect an pending interrupt on the given core, we mask the interrupt
* out by settging the core's IRQ_MASK register to zero.
* Then we schedule the mali_core_irq_handler_bottom_half to run as high priority
* work queue job.
*/
static irqreturn_t irq_handler_upper_half (int port_name, void* dev_id ) /* , struct pt_regs *regs*/
{
irqreturn_t ret = IRQ_NONE;
mali_osk_irq_object_t *irq_object = (mali_osk_irq_object_t *)dev_id;
if (_MALI_OSK_ERR_OK == irq_object->uhandler(irq_object->data)) {
ret = IRQ_HANDLED;
}
return ret;
}

View File

@@ -0,0 +1,281 @@
/*
* Copyright (C) 2010-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 mali_osk_locks.c
* Implemenation of the OS abstraction layer for the kernel device driver
*/
#include "mali_osk_locks.h"
#include "mali_kernel_common.h"
#include "mali_osk.h"
#ifdef DEBUG
#ifdef LOCK_ORDER_CHECKING
static DEFINE_SPINLOCK(lock_tracking_lock);
static mali_bool add_lock_to_log_and_check(struct _mali_osk_lock_debug_s *lock, uint32_t tid);
static void remove_lock_from_log(struct _mali_osk_lock_debug_s *lock, uint32_t tid);
static const char * const lock_order_to_string(_mali_osk_lock_order_t order);
#endif /* LOCK_ORDER_CHECKING */
void _mali_osk_locks_debug_init(struct _mali_osk_lock_debug_s *checker, _mali_osk_lock_flags_t flags, _mali_osk_lock_order_t order)
{
checker->orig_flags = flags;
checker->owner = 0;
#ifdef LOCK_ORDER_CHECKING
checker->order = order;
checker->next = NULL;
#endif
}
void _mali_osk_locks_debug_add(struct _mali_osk_lock_debug_s *checker)
{
checker->owner = _mali_osk_get_tid();
#ifdef LOCK_ORDER_CHECKING
if (!(checker->orig_flags & _MALI_OSK_LOCKFLAG_UNORDERED)) {
if (!add_lock_to_log_and_check(checker, _mali_osk_get_tid())) {
printk(KERN_ERR "%d: ERROR lock %p taken while holding a lock of a higher order.\n",
_mali_osk_get_tid(), checker);
dump_stack();
}
}
#endif
}
void _mali_osk_locks_debug_remove(struct _mali_osk_lock_debug_s *checker)
{
#ifdef LOCK_ORDER_CHECKING
if (!(checker->orig_flags & _MALI_OSK_LOCKFLAG_UNORDERED)) {
remove_lock_from_log(checker, _mali_osk_get_tid());
}
#endif
checker->owner = 0;
}
#ifdef LOCK_ORDER_CHECKING
/* Lock order checking
* -------------------
*
* To assure that lock ordering scheme defined by _mali_osk_lock_order_t is strictly adhered to, the
* following function will, together with a linked list and some extra members in _mali_osk_lock_debug_s,
* make sure that a lock that is taken has a higher order than the current highest-order lock a
* thread holds.
*
* This is done in the following manner:
* - A linked list keeps track of locks held by a thread.
* - A `next' pointer is added to each lock. This is used to chain the locks together.
* - When taking a lock, the `add_lock_to_log_and_check' makes sure that taking
* the given lock is legal. It will follow the linked list to find the last
* lock taken by this thread. If the last lock's order was lower than the
* lock that is to be taken, it appends the new lock to the list and returns
* true, if not, it return false. This return value is assert()'ed on in
* _mali_osk_lock_wait().
*/
static struct _mali_osk_lock_debug_s *lock_lookup_list;
static void dump_lock_tracking_list(void)
{
struct _mali_osk_lock_debug_s *l;
u32 n = 1;
/* print list for debugging purposes */
l = lock_lookup_list;
while (NULL != l) {
printk(" [lock: %p, tid_owner: %d, order: %d] ->", l, l->owner, l->order);
l = l->next;
MALI_DEBUG_ASSERT(n++ < 100);
}
printk(" NULL\n");
}
static int tracking_list_length(void)
{
struct _mali_osk_lock_debug_s *l;
u32 n = 0;
l = lock_lookup_list;
while (NULL != l) {
l = l->next;
n++;
MALI_DEBUG_ASSERT(n < 100);
}
return n;
}
static mali_bool add_lock_to_log_and_check(struct _mali_osk_lock_debug_s *lock, uint32_t tid)
{
mali_bool ret = MALI_FALSE;
_mali_osk_lock_order_t highest_order_for_tid = _MALI_OSK_LOCK_ORDER_FIRST;
struct _mali_osk_lock_debug_s *highest_order_lock = (struct _mali_osk_lock_debug_s *)0xbeefbabe;
struct _mali_osk_lock_debug_s *l;
unsigned long local_lock_flag;
u32 len;
spin_lock_irqsave(&lock_tracking_lock, local_lock_flag);
len = tracking_list_length();
l = lock_lookup_list;
if (NULL == l) { /* This is the first lock taken by this thread -- record and return true */
lock_lookup_list = lock;
spin_unlock_irqrestore(&lock_tracking_lock, local_lock_flag);
return MALI_TRUE;
} else {
/* Traverse the locks taken and find the lock of the highest order.
* Since several threads may hold locks, each lock's owner must be
* checked so that locks not owned by this thread can be ignored. */
for(;;) {
MALI_DEBUG_ASSERT_POINTER( l );
if (tid == l->owner && l->order >= highest_order_for_tid) {
highest_order_for_tid = l->order;
highest_order_lock = l;
}
if (NULL != l->next) {
l = l->next;
} else {
break;
}
}
l->next = lock;
l->next = NULL;
}
/* We have now found the highest order lock currently held by this thread and can see if it is
* legal to take the requested lock. */
ret = highest_order_for_tid < lock->order;
if (!ret) {
printk(KERN_ERR "Took lock of order %d (%s) while holding lock of order %d (%s)\n",
lock->order, lock_order_to_string(lock->order),
highest_order_for_tid, lock_order_to_string(highest_order_for_tid));
dump_lock_tracking_list();
}
if (len+1 != tracking_list_length()) {
printk(KERN_ERR "************ lock: %p\n", lock);
printk(KERN_ERR "************ before: %d *** after: %d ****\n", len, tracking_list_length());
dump_lock_tracking_list();
MALI_DEBUG_ASSERT_POINTER(NULL);
}
spin_unlock_irqrestore(&lock_tracking_lock, local_lock_flag);
return ret;
}
static void remove_lock_from_log(struct _mali_osk_lock_debug_s *lock, uint32_t tid)
{
struct _mali_osk_lock_debug_s *curr;
struct _mali_osk_lock_debug_s *prev = NULL;
unsigned long local_lock_flag;
u32 len;
u32 n = 0;
spin_lock_irqsave(&lock_tracking_lock, local_lock_flag);
len = tracking_list_length();
curr = lock_lookup_list;
if (NULL == curr) {
printk(KERN_ERR "Error: Lock tracking list was empty on call to remove_lock_from_log\n");
dump_lock_tracking_list();
}
MALI_DEBUG_ASSERT_POINTER(curr);
while (lock != curr) {
prev = curr;
MALI_DEBUG_ASSERT_POINTER(curr);
curr = curr->next;
MALI_DEBUG_ASSERT(n++ < 100);
}
if (NULL == prev) {
lock_lookup_list = curr->next;
} else {
MALI_DEBUG_ASSERT_POINTER(curr);
MALI_DEBUG_ASSERT_POINTER(prev);
prev->next = curr->next;
}
lock->next = NULL;
if (len-1 != tracking_list_length()) {
printk(KERN_ERR "************ lock: %p\n", lock);
printk(KERN_ERR "************ before: %d *** after: %d ****\n", len, tracking_list_length());
dump_lock_tracking_list();
MALI_DEBUG_ASSERT_POINTER(NULL);
}
spin_unlock_irqrestore(&lock_tracking_lock, local_lock_flag);
}
static const char * const lock_order_to_string(_mali_osk_lock_order_t order)
{
switch (order) {
case _MALI_OSK_LOCK_ORDER_SESSIONS:
return "_MALI_OSK_LOCK_ORDER_SESSIONS";
break;
case _MALI_OSK_LOCK_ORDER_MEM_SESSION:
return "_MALI_OSK_LOCK_ORDER_MEM_SESSION";
break;
case _MALI_OSK_LOCK_ORDER_MEM_INFO:
return "_MALI_OSK_LOCK_ORDER_MEM_INFO";
break;
case _MALI_OSK_LOCK_ORDER_MEM_PT_CACHE:
return "_MALI_OSK_LOCK_ORDER_MEM_PT_CACHE";
break;
case _MALI_OSK_LOCK_ORDER_DESCRIPTOR_MAP:
return "_MALI_OSK_LOCK_ORDER_DESCRIPTOR_MAP";
break;
case _MALI_OSK_LOCK_ORDER_GROUP_VIRTUAL:
return "_MALI_OSK_LOCK_ORDER_GROUP_VIRTUAL";
break;
case _MALI_OSK_LOCK_ORDER_GROUP:
return "_MALI_OSK_LOCK_ORDER_GROUP";
break;
case _MALI_OSK_LOCK_ORDER_SCHEDULER:
return "_MALI_OSK_LOCK_ORDER_SCHEDULER";
break;
case _MALI_OSK_LOCK_ORDER_PM_CORE_STATE:
return "_MALI_OSK_LOCK_ORDER_PM_CORE_STATE";
break;
case _MALI_OSK_LOCK_ORDER_L2_COMMAND:
return "_MALI_OSK_LOCK_ORDER_L2_COMMAND";
break;
case _MALI_OSK_LOCK_ORDER_PROFILING:
return "_MALI_OSK_LOCK_ORDER_PROFILING";
break;
case _MALI_OSK_LOCK_ORDER_L2_COUNTER:
return "_MALI_OSK_LOCK_ORDER_L2_COUNTER";
break;
case _MALI_OSK_LOCK_ORDER_UTILIZATION:
return "_MALI_OSK_LOCK_ORDER_UTILIZATION";
break;
case _MALI_OSK_LOCK_ORDER_PM_EXECUTE:
return "_MALI_OSK_LOCK_ORDER_PM_EXECUTE";
break;
case _MALI_OSK_LOCK_ORDER_SESSION_PENDING_JOBS:
return "_MALI_OSK_LOCK_ORDER_SESSION_PENDING_JOBS";
break;
default:
return "";
}
}
#endif /* LOCK_ORDER_CHECKING */
#endif /* DEBUG */

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