summaryrefslogtreecommitdiff
path: root/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/ux500
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/ux500')
-rw-r--r--drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/ux500/mali_platform.c190
-rw-r--r--drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/ux500/ump_kernel_api_hwmem.c159
2 files changed, 349 insertions, 0 deletions
diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/ux500/mali_platform.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/ux500/mali_platform.c
new file mode 100644
index 00000000000..481e6e76c54
--- /dev/null
+++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/ux500/mali_platform.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Mali related Ux500 platform initialization
+ *
+ * Author: Marta Lofstedt <marta.lofstedt@stericsson.com> for
+ * ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2.
+ */
+
+/**
+ * @file mali_platform.c
+ * Platform specific Mali driver functions for ST-Ericsson's Ux500 platforms
+ */
+#include "mali_kernel_common.h"
+#include "mali_osk.h"
+#include "mali_platform.h"
+
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/workqueue.h>
+
+#include <linux/mfd/dbx500-prcmu.h>
+
+#define MALI_HIGH_TO_LOW_LEVEL_UTILIZATION_LIMIT 64
+#define MALI_LOW_TO_HIGH_LEVEL_UTILIZATION_LIMIT 192
+
+static int is_running;
+static struct regulator *regulator;
+static struct clk *clk_sga;
+static u32 last_utilization;
+static struct work_struct mali_utilization_work;
+static struct workqueue_struct *mali_utilization_workqueue;
+
+/* Rationale behind the values for:
+* MALI_HIGH_LEVEL_UTILIZATION_LIMIT and MALI_LOW_LEVEL_UTILIZATION_LIMIT
+* When operating at half clock frequency a faster clock is requested when
+* reaching 75% utilization. When operating at full clock frequency a slower
+* clock is requested when reaching 25% utilization. There is a margin of 25%
+* at the high range of the slow clock to avoid complete saturation of the
+* hardware and there is some overlap to avoid an oscillating situation where
+* the clock goes back and forth from high to low.
+*
+* Utilization on full speed clock
+* 0 64 128 192 255
+* |---------------|---------------|---------------|---------------|
+* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+* | ^
+* V |
+* XXXXXXXXXXXXXXXXXXXXXXXXX
+* 0 64 128 192 255
+* |-------|-------|-------|-------|
+* Utilization on half speed clock
+*/
+void mali_utilization_function(struct work_struct *ptr)
+{
+ /*By default, platform start with 50% APE OPP and 25% DDR OPP*/
+ static u32 has_requested_low = 1;
+
+ if (last_utilization > MALI_LOW_TO_HIGH_LEVEL_UTILIZATION_LIMIT) {
+ if (has_requested_low) {
+ MALI_DEBUG_PRINT(5, ("MALI GPU utilization: %u SIGNAL_HIGH\n", last_utilization));
+ /*Request 100% APE_OPP.*/
+ if (prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, "mali", 100)) {
+ MALI_DEBUG_PRINT(2, ("MALI 100% APE_OPP failed\n"));
+ return;
+ }
+ /*
+ * Since the utilization values will be reported higher
+ * if DDR_OPP is lowered, we also request 100% DDR_OPP.
+ */
+ if (prcmu_qos_add_requirement(PRCMU_QOS_DDR_OPP, "mali", 100)) {
+ MALI_DEBUG_PRINT(2, ("MALI 100% DDR_OPP failed\n"));
+ return;
+ }
+ has_requested_low = 0;
+ }
+ } else {
+ if (last_utilization < MALI_HIGH_TO_LOW_LEVEL_UTILIZATION_LIMIT) {
+ if (!has_requested_low) {
+ /*Remove APE_OPP and DDR_OPP requests*/
+ prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, "mali");
+ prcmu_qos_remove_requirement(PRCMU_QOS_DDR_OPP, "mali");
+ MALI_DEBUG_PRINT(5, ("MALI GPU utilization: %u SIGNAL_LOW\n", last_utilization));
+ has_requested_low = 1;
+ }
+ }
+ }
+ MALI_DEBUG_PRINT(5, ("MALI GPU utilization: %u\n", last_utilization));
+}
+
+_mali_osk_errcode_t mali_platform_init(_mali_osk_resource_t *resource)
+{
+ is_running = 0;
+ last_utilization = 0;
+
+ mali_utilization_workqueue = create_singlethread_workqueue("mali_utilization_workqueue");
+ if (NULL == mali_utilization_workqueue) {
+ MALI_DEBUG_PRINT(2, ("%s: Failed to setup workqueue %s\n", __func__, "v-mali"));
+ goto error;
+ }
+ INIT_WORK(&mali_utilization_work, mali_utilization_function);
+
+ regulator = regulator_get(NULL, "v-mali");
+ if (IS_ERR(regulator)) {
+ MALI_DEBUG_PRINT(2, ("%s: Failed to get regulator %s\n", __func__, "v-mali"));
+ goto error;
+ }
+
+ clk_sga = clk_get_sys("mali", NULL);
+ if (IS_ERR(clk_sga)) {
+ regulator_put(regulator);
+ MALI_DEBUG_PRINT(2, ("%s: Failed to get clock %s\n", __func__, "mali"));
+ goto error;
+ }
+
+ MALI_SUCCESS;
+error:
+ MALI_DEBUG_PRINT(1, ("SGA initialization failed.\n"));
+ MALI_ERROR(_MALI_OSK_ERR_FAULT);
+}
+
+_mali_osk_errcode_t mali_platform_deinit(_mali_osk_resource_type_t *type)
+{
+ destroy_workqueue(mali_utilization_workqueue);
+ regulator_put(regulator);
+ clk_put(clk_sga);
+ MALI_DEBUG_PRINT(2, ("SGA terminated.\n"));
+ MALI_SUCCESS;
+}
+
+_mali_osk_errcode_t mali_platform_powerdown(u32 cores)
+{
+ if (is_running) {
+ clk_disable(clk_sga);
+ if (regulator) {
+ int ret = regulator_disable(regulator);
+ if (ret < 0) {
+ MALI_DEBUG_PRINT(2, ("%s: Failed to disable regulator %s\n", __func__, "v-mali"));
+ is_running = 0;
+ MALI_ERROR(_MALI_OSK_ERR_FAULT);
+ }
+ }
+ is_running = 0;
+ }
+ MALI_DEBUG_PRINT(4, ("mali_platform_powerdown is_running: %u cores: %u\n", is_running, cores));
+ MALI_SUCCESS;
+}
+
+_mali_osk_errcode_t mali_platform_powerup(u32 cores)
+{
+ if (!is_running) {
+ int ret = regulator_enable(regulator);
+ if (ret < 0) {
+ MALI_DEBUG_PRINT(2, ("%s: Failed to enable regulator %s\n", __func__, "v-mali"));
+ goto error;
+ }
+
+ ret = clk_enable(clk_sga);
+ if (ret < 0) {
+ regulator_disable(regulator);
+ MALI_DEBUG_PRINT(2, ("%s: Failed to enable clock %s\n", __func__, "mali"));
+ goto error;
+ }
+ is_running = 1;
+ }
+ MALI_DEBUG_PRINT(4, ("mali_platform_powerup is_running:%u cores: %u\n", is_running, cores));
+ MALI_SUCCESS;
+error:
+ MALI_DEBUG_PRINT(1, ("Failed to power up.\n"));
+ MALI_ERROR(_MALI_OSK_ERR_FAULT);
+}
+
+void mali_gpu_utilization_handler(u32 utilization)
+{
+ last_utilization = utilization;
+ /*
+ * We should not cancel the potentially not yet run old work
+ * in favor of a new work.
+ * Since the utilization value will change,
+ * the mali_utilization_function will evaluate based on
+ * what is the utilization now and not on what it was
+ * when it was scheduled.
+ */
+ queue_work(mali_utilization_workqueue, &mali_utilization_work);
+}
+
+
diff --git a/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/ux500/ump_kernel_api_hwmem.c b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/ux500/ump_kernel_api_hwmem.c
new file mode 100644
index 00000000000..b321b504839
--- /dev/null
+++ b/drivers/gpu/mali/mali400ko/driver/src/devicedrv/mali/platform/ux500/ump_kernel_api_hwmem.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ * Author: Magnus Wendt <magnus.wendt@stericsson.com> for
+ * ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include "ump_kernel_types.h"
+#include "mali_kernel_common.h"
+
+#include <linux/hwmem.h>
+#include <linux/err.h>
+
+
+/* The UMP kernel API for hwmem has been mapped so that
+ * ump_dd_handle == hwmem_alloc
+ * ump_secure_id == hwmem global name
+ *
+ * The current implementation is limited to contiguous memory
+ */
+
+ump_secure_id ump_dd_secure_id_get(ump_dd_handle memh)
+{
+ int hwmem_name = hwmem_get_name((struct hwmem_alloc *) memh);
+
+ if (unlikely(hwmem_name < 0)) {
+ MALI_DEBUG_PRINT(1, ("%s: Invalid Alloc 0x%x\n",__func__, memh));
+ return UMP_INVALID_SECURE_ID;
+ }
+
+ return (ump_secure_id)hwmem_name;
+}
+
+
+
+ump_dd_handle ump_dd_handle_create_from_secure_id(ump_secure_id secure_id)
+{
+ struct hwmem_alloc *alloc;
+ enum hwmem_mem_type mem_type;
+ enum hwmem_access access;
+
+ alloc = hwmem_resolve_by_name((int) secure_id);
+
+ if (IS_ERR(alloc)) {
+ MALI_DEBUG_PRINT(1, ("%s: Invalid UMP id %d\n",__func__, secure_id));
+ return UMP_DD_HANDLE_INVALID;
+ }
+
+ hwmem_get_info(alloc, NULL, &mem_type, &access);
+
+ if (unlikely((access & (HWMEM_ACCESS_READ | HWMEM_ACCESS_WRITE | HWMEM_ACCESS_IMPORT)) !=
+ (HWMEM_ACCESS_READ | HWMEM_ACCESS_WRITE | HWMEM_ACCESS_IMPORT))) {
+ MALI_DEBUG_PRINT(1, ("%s: Access denied on UMP id %d, (access==%d)\n",
+ __func__, secure_id, access));
+ hwmem_release(alloc);
+ return UMP_DD_HANDLE_INVALID;
+ }
+
+ if (unlikely(HWMEM_MEM_CONTIGUOUS_SYS != mem_type)) {
+ MALI_DEBUG_PRINT(1, ("%s: UMP id %d is non-contiguous! (not supported)\n",
+ __func__, secure_id));
+ hwmem_release(alloc);
+ return UMP_DD_HANDLE_INVALID;
+ }
+
+ return (ump_dd_handle)alloc;
+}
+
+
+
+unsigned long ump_dd_phys_block_count_get(ump_dd_handle memh)
+{
+ return 1;
+}
+
+
+
+ump_dd_status_code ump_dd_phys_blocks_get(ump_dd_handle memh,
+ ump_dd_physical_block * blocks,
+ unsigned long num_blocks)
+{
+ struct hwmem_mem_chunk hwmem_mem_chunk;
+ size_t hwmem_mem_chunk_length = 1;
+
+ int hwmem_result;
+ struct hwmem_alloc *alloc = (struct hwmem_alloc *)memh;
+
+ if (unlikely(blocks == NULL)) {
+ MALI_DEBUG_PRINT(1, ("%s: blocks == NULL\n",__func__));
+ return UMP_DD_INVALID;
+ }
+
+ if (unlikely(1 != num_blocks)) {
+ MALI_DEBUG_PRINT(1, ("%s: num_blocks == %d (!= 1)\n",__func__, num_blocks));
+ return UMP_DD_INVALID;
+ }
+
+ MALI_DEBUG_PRINT(5, ("Returning physical block information. Alloc: 0x%x\n", memh));
+
+ /* It might not look natural to pin here, but it matches the usage by the mali kernel module */
+ hwmem_result = hwmem_pin(alloc, &hwmem_mem_chunk, &hwmem_mem_chunk_length);
+
+ if (unlikely(hwmem_result < 0)) {
+ MALI_DEBUG_PRINT(1, ("%s: Pin failed. Alloc: 0x%x\n",__func__, memh));
+ return UMP_DD_INVALID;
+ }
+
+ blocks[0].addr = hwmem_mem_chunk.paddr;
+ blocks[0].size = hwmem_mem_chunk.size;
+
+ hwmem_set_domain(alloc, HWMEM_ACCESS_READ | HWMEM_ACCESS_WRITE,
+ HWMEM_DOMAIN_SYNC, NULL);
+
+ return UMP_DD_SUCCESS;
+}
+
+
+
+ump_dd_status_code ump_dd_phys_block_get(ump_dd_handle memh,
+ unsigned long index,
+ ump_dd_physical_block * block)
+{
+ if (unlikely(0 != index)) {
+ MALI_DEBUG_PRINT(1, ("%s: index == %d (!= 0)\n",__func__, index));
+ return UMP_DD_INVALID;
+ }
+ return ump_dd_phys_blocks_get(memh, block, 1);
+}
+
+
+
+unsigned long ump_dd_size_get(ump_dd_handle memh)
+{
+ struct hwmem_alloc *alloc = (struct hwmem_alloc *)memh;
+ int size;
+
+ hwmem_get_info(alloc, &size, NULL, NULL);
+
+ return size;
+}
+
+
+
+void ump_dd_reference_add(ump_dd_handle memh)
+{
+ /* This is never called from tha mali kernel driver */
+}
+
+
+
+void ump_dd_reference_release(ump_dd_handle memh)
+{
+ struct hwmem_alloc *alloc = (struct hwmem_alloc *)memh;
+
+ hwmem_unpin(alloc);
+ hwmem_release(alloc);
+
+ return;
+}