aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/mali400/r3p2/mali/common/mali_l2_cache.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/mali400/r3p2/mali/common/mali_l2_cache.c')
-rw-r--r--drivers/gpu/mali400/r3p2/mali/common/mali_l2_cache.c463
1 files changed, 463 insertions, 0 deletions
diff --git a/drivers/gpu/mali400/r3p2/mali/common/mali_l2_cache.c b/drivers/gpu/mali400/r3p2/mali/common/mali_l2_cache.c
new file mode 100644
index 0000000..b5cfcb5
--- /dev/null
+++ b/drivers/gpu/mali400/r3p2/mali/common/mali_l2_cache.c
@@ -0,0 +1,463 @@
+/*
+ * Copyright (C) 2010-2012 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_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);
+
+
+struct mali_l2_cache_core *mali_l2_cache_create(_mali_osk_resource_t *resource)
+{
+ struct mali_l2_cache_core *cache = NULL;
+ _mali_osk_lock_flags_t lock_flags;
+
+#if defined(MALI_UPPER_HALF_SCHEDULING)
+ lock_flags = _MALI_OSK_LOCKFLAG_ORDERED | _MALI_OSK_LOCKFLAG_SPINLOCK_IRQ | _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE;
+#else
+ lock_flags = _MALI_OSK_LOCKFLAG_ORDERED | _MALI_OSK_LOCKFLAG_SPINLOCK | _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE;
+#endif
+
+ MALI_DEBUG_PRINT(2, ("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;
+ if (_MALI_OSK_ERR_OK == mali_hw_core_create(&cache->hw_core, resource, MALI400_L2_CACHE_REGISTERS_SIZE))
+ {
+ cache->command_lock = _mali_osk_lock_init(lock_flags, 0, _MALI_OSK_LOCK_ORDER_L2_COMMAND);
+ if (NULL != cache->command_lock)
+ {
+ cache->counter_lock = _mali_osk_lock_init(lock_flags, 0, _MALI_OSK_LOCK_ORDER_L2_COUNTER);
+ 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));
+ }
+
+ _mali_osk_lock_term(cache->command_lock);
+ }
+ 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);
+
+ _mali_osk_lock_term(cache->counter_lock);
+ _mali_osk_lock_term(cache->command_lock);
+ 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;
+}
+
+mali_bool mali_l2_cache_core_set_counter_src0(struct mali_l2_cache_core *cache, u32 counter)
+{
+ u32 value = 0; /* disabled src */
+ mali_bool core_is_on;
+
+ MALI_DEBUG_ASSERT_POINTER(cache);
+
+ core_is_on = mali_l2_cache_lock_power_state(cache);
+
+ _mali_osk_lock_wait(cache->counter_lock, _MALI_OSK_LOCKMODE_RW);
+
+ cache->counter_src0 = counter;
+
+ if (MALI_HW_CORE_NO_COUNTER != counter)
+ {
+ value = counter;
+ }
+
+ if (MALI_TRUE == core_is_on)
+ {
+ mali_hw_core_register_write(&cache->hw_core, MALI400_L2_CACHE_REGISTER_PERFCNT_SRC0, value);
+ }
+
+ _mali_osk_lock_signal(cache->counter_lock, _MALI_OSK_LOCKMODE_RW);
+
+ mali_l2_cache_unlock_power_state(cache);
+
+ return MALI_TRUE;
+}
+
+mali_bool mali_l2_cache_core_set_counter_src1(struct mali_l2_cache_core *cache, u32 counter)
+{
+ u32 value = 0; /* disabled src */
+ mali_bool core_is_on;
+
+ MALI_DEBUG_ASSERT_POINTER(cache);
+
+ core_is_on = mali_l2_cache_lock_power_state(cache);
+
+ _mali_osk_lock_wait(cache->counter_lock, _MALI_OSK_LOCKMODE_RW);
+
+ cache->counter_src1 = counter;
+
+ if (MALI_HW_CORE_NO_COUNTER != counter)
+ {
+ value = counter;
+ }
+
+ if (MALI_TRUE == core_is_on)
+ {
+ mali_hw_core_register_write(&cache->hw_core, MALI400_L2_CACHE_REGISTER_PERFCNT_SRC1, value);
+ }
+
+ _mali_osk_lock_signal(cache->counter_lock, _MALI_OSK_LOCKMODE_RW);
+
+ mali_l2_cache_unlock_power_state(cache);
+
+ return MALI_TRUE;
+}
+
+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_osk_lock_wait(cache->counter_lock, _MALI_OSK_LOCKMODE_RW);
+
+ *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_osk_lock_signal(cache->counter_lock, _MALI_OSK_LOCKMODE_RW);
+}
+
+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)
+{
+ /* 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);
+
+ /* 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) */
+ _mali_osk_lock_wait(cache->counter_lock, _MALI_OSK_LOCKMODE_RW);
+
+ 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_osk_lock_signal(cache->counter_lock, _MALI_OSK_LOCKMODE_RW);
+}
+
+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_id();
+ 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_id();
+ }
+
+ 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_id();
+ 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_osk_lock_wait(cache->command_lock, _MALI_OSK_LOCKMODE_RW);
+
+ /* 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_osk_lock_signal(cache->command_lock, _MALI_OSK_LOCKMODE_RW);
+ 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_osk_lock_signal(cache->command_lock, _MALI_OSK_LOCKMODE_RW);
+
+ MALI_SUCCESS;
+}