diff options
Diffstat (limited to 'drivers/gpu/arm/mali/platform')
-rw-r--r-- | drivers/gpu/arm/mali/platform/mach-origen/mali_devfreq.c | 422 | ||||
-rw-r--r-- | drivers/gpu/arm/mali/platform/mach-origen/mali_devfreq.h | 49 | ||||
-rw-r--r-- | drivers/gpu/arm/mali/platform/mach-origen/mali_platform.c | 365 | ||||
-rw-r--r-- | drivers/gpu/arm/mali/platform/mali_platform.h | 116 |
4 files changed, 921 insertions, 31 deletions
diff --git a/drivers/gpu/arm/mali/platform/mach-origen/mali_devfreq.c b/drivers/gpu/arm/mali/platform/mach-origen/mali_devfreq.c new file mode 100644 index 000000000000..b9d23d61f600 --- /dev/null +++ b/drivers/gpu/arm/mali/platform/mach-origen/mali_devfreq.c @@ -0,0 +1,422 @@ + /* + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * Vikas Sajjan <vikas.sajjan@samsung.com> + * + * EXYNOS4 - MALI frequency/voltage scaling support in DEVFREQ framework + * This version supports only EXYNOS4412 only. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/mutex.h> +#include <linux/suspend.h> +#include <linux/opp.h> +#include <linux/devfreq.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/module.h> + +#include <mach/regs-clock.h> +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_platform.h" +#include "mali_devfreq.h" +#include <plat/map-s5p.h> + +/* dvfs status */ +struct mali_dvfs_status maliDvfsStatus; + +#define MAX_MALI_DVFS_STEPS 4 +#define MALI_DVFS_STEPS 4 + +int mali_dvfs_control; + +struct mali_dvfs_threshold_tbl { + unsigned int downthreshold; + unsigned int upthreshold; +}; + +struct mali_dvfs_staycount { + unsigned int staycount; +}; + +struct mali_dvfs_threshold_tbl mali_dvfs_threshold[MALI_DVFS_STEPS] = { + {0, 70}, + {50, 70}, + {50, 85}, + {75, 100} +}; + +struct mali_dvfs_staycount mali_dvfs_staycount[MALI_DVFS_STEPS] = { + /*step 0*/{1}, + /*step 1*/{1}, + /*step 2*/{1}, + /*step 3*/{1} +}; + +/* dvfs information */ +/* L0 = 440Mhz, 1.025V */ +/* L1 = 350Mhz, 0.95V */ +/* L2 = 266Mhz, 0.90V */ +/* L3 = 160Mhz, 0.875V */ + +int step0_clk = 160; +int step0_vol = 875000; +int step1_clk = 266; +int step1_vol = 900000; +int step0_up = 70; +int step1_down = 50; +int step2_clk = 350; +int step2_vol = 950000; +int step1_up = 70; +int step2_down = 50; +int step3_clk = 440; +int step3_vol = 1025000; +int step2_up = 85; +int step3_down = 75; + +struct mali_dvfs_stp { + int clk; + int vol; +}; + +struct mali_dvfs_tbl mali_dvfs_value[MALI_DVFS_STEPS] = { + { 160, 1000000, 875000 }, + { 266, 1000000, 900000 }, + { 350, 1000000, 950000 }, + { 440, 1000000, 1025000} }; + +struct mali_dvfs_stp step[MALI_DVFS_STEPS] = { + /* step 0 clk */ { 160, 875000 }, + /* step 1 clk */ { 266, 900000 }, + /* step 2 clk */ { 350, 950000 }, + /* step 3 clk */ { 440, 1025000 } +}; + +int change_dvfs_tableset(int change_clk, int change_step) +{ + if (change_clk < mali_dvfs_value[1].clock) { + mali_dvfs_value[change_step].clock = mali_dvfs_value[0].clock; + mali_dvfs_value[change_step].vol = mali_dvfs_value[0].vol; + } else if (change_clk < mali_dvfs_value[2].clock && change_clk >= + mali_dvfs_value[1].clock) { + mali_dvfs_value[change_step].clock = mali_dvfs_value[1].clock; + mali_dvfs_value[change_step].vol = mali_dvfs_value[1].vol; + } else if (change_clk < mali_dvfs_value[3].clock && change_clk >= + mali_dvfs_value[2].clock) { + mali_dvfs_value[change_step].clock = mali_dvfs_value[2].clock; + mali_dvfs_value[change_step].vol = mali_dvfs_value[2].vol; + } else { + mali_dvfs_value[change_step].clock = mali_dvfs_value[3].clock; + mali_dvfs_value[change_step].vol = mali_dvfs_value[3].vol; + } + + if (maliDvfsStatus.currentStep == change_step) { + /* change the voltage */ + mali_regulator_set_voltage(mali_dvfs_value[change_step].vol, + mali_dvfs_value[change_step].vol); + /* change the clock */ + mali_clk_set_rate(mali_dvfs_value[change_step].clock, + mali_dvfs_value[change_step].freq); + } + + return mali_dvfs_value[change_step].clock; +} + +mali_bool set_mali_dvfs_current_step(unsigned int step) +{ + _mali_osk_lock_wait(mali_dvfs_lock, _MALI_OSK_LOCKMODE_RW); + maliDvfsStatus.currentStep = step; + _mali_osk_lock_signal(mali_dvfs_lock, _MALI_OSK_LOCKMODE_RW); + return MALI_TRUE; +} + +static mali_bool set_mali_dvfs_status(u32 step, mali_bool boostup) +{ + u32 validatedStep = step; + + if (mali_regulator_get_usecount() == 0) + return MALI_FALSE; + + if (boostup) { + /* change the voltage */ + mali_regulator_set_voltage(mali_dvfs_value[step].vol, + mali_dvfs_value[step].vol); + /* change the clock */ + mali_clk_set_rate(mali_dvfs_value[step].clock, + mali_dvfs_value[step].freq); + } else { + /* change the clock */ + mali_clk_set_rate(mali_dvfs_value[step].clock, + mali_dvfs_value[step].freq); + /* change the voltage */ + mali_regulator_set_voltage(mali_dvfs_value[step].vol, + mali_dvfs_value[step].vol); + } + + set_mali_dvfs_current_step(validatedStep); + + /* for future use */ + maliDvfsStatus.pCurrentDvfs = &mali_dvfs_value[validatedStep]; + + return MALI_TRUE; +} + +static void mali_platform_waiting(u32 msec) +{ + unsigned int read_val; + while (1) { + read_val = _mali_osk_mem_ioread32( + clk_register_map, + 0x00); + if ((read_val & 0x8000) == 0x0000) + break; + /* 1000 -> 100 : 20101218 */ + _mali_osk_time_ubusydelay(100); + } +} + +static mali_bool change_mali_dvfs_status(u32 step, mali_bool boostup) +{ + if (!set_mali_dvfs_status(step, boostup)) + return MALI_FALSE; + + /* wait until clock and voltage is stablized */ + mali_platform_waiting(MALI_DVFS_WAITING); /* msec */ + return MALI_TRUE; +} + +static unsigned int decideNextStatus(unsigned int mali_dvfs_freq) +{ + static unsigned int level; /* 0:stay, 1:up */ + static int mali_dvfs_clk; + + if (!mali_dvfs_control && level == maliDvfsStatus.currentStep) { + if (mali_dvfs_freq > + (int)((mali_dvfs_value[maliDvfsStatus.currentStep].clock * + mali_dvfs_value[maliDvfsStatus.currentStep].freq)) + && level < MALI_DVFS_STEPS - 1) { + level++; + } + if (mali_dvfs_freq < + (int)((mali_dvfs_value[maliDvfsStatus.currentStep].clock * + mali_dvfs_value[maliDvfsStatus.currentStep].freq)) + && level > 0) { + level--; + } + } else if (mali_dvfs_control == 999) { + int i = 0; + for (i = 0; i < MALI_DVFS_STEPS; i++) + step[i].clk = mali_dvfs_value[i].clock; +#ifdef EXYNOS4_ASV_ENABLED + mali_dvfs_table_update(); +#endif + i = 0; + for (i = 0; i < MALI_DVFS_STEPS; i++) + mali_dvfs_value[i].clock = step[i].clk; + + mali_dvfs_control = 0; + level = 0; + + step0_clk = step[0].clk; + change_dvfs_tableset(step0_clk, 0); + + step1_clk = step[1].clk; + change_dvfs_tableset(step1_clk, 1); + + step2_clk = step[2].clk; + change_dvfs_tableset(step2_clk, 2); + + step3_clk = step[3].clk; + change_dvfs_tableset(step3_clk, 3); + + } else if (mali_dvfs_control != mali_dvfs_clk && mali_dvfs_control + != 999) { + if (mali_dvfs_control < mali_dvfs_value[1].clock + && mali_dvfs_control > 0) { + int i = 0; + for (i = 0; i < MALI_DVFS_STEPS; i++) + step[i].clk = mali_dvfs_value[0].clock; + } else if (mali_dvfs_control < mali_dvfs_value[2].clock + && mali_dvfs_control >= mali_dvfs_value[1].clock) { + int i = 0; + for (i = 0; i < MALI_DVFS_STEPS; i++) + step[i].clk = mali_dvfs_value[1].clock; + } else if (mali_dvfs_control < mali_dvfs_value[3].clock + && mali_dvfs_control >= mali_dvfs_value[2].clock) { + int i = 0; + for (i = 0; i < MALI_DVFS_STEPS; i++) + step[i].clk = mali_dvfs_value[2].clock; + } else { + int i = 0; + for (i = 0; i < MALI_DVFS_STEPS; i++) + step[i].clk = mali_dvfs_value[3].clock; + } + + step0_clk = step[0].clk; + change_dvfs_tableset(step0_clk, 0); + step1_clk = step[1].clk; + change_dvfs_tableset(step1_clk, 1); + step2_clk = step[2].clk; + change_dvfs_tableset(step2_clk, 2); + step3_clk = step[3].clk; + change_dvfs_tableset(step3_clk, 3); + level = maliDvfsStatus.currentStep; + } + + mali_dvfs_clk = mali_dvfs_control; + return level; +} + +static unsigned int get_mali_dvfs_status(void) +{ + return maliDvfsStatus.currentStep; +} + +static mali_bool mali_dvfs_status(u32 mali_dvfs_freq) +{ + unsigned int nextStatus = 0; + unsigned int curStatus = 0; + mali_bool boostup = MALI_FALSE; + static int stay_count; +#ifdef EXYNOS4_ASV_ENABLED + static mali_bool asv_applied = MALI_FALSE; +#endif +#ifdef EXYNOS4_ASV_ENABLED + if (asv_applied == MALI_FALSE) { + mali_dvfs_table_update(); + change_mali_dvfs_status(1, 0); + asv_applied = MALI_TRUE; + return MALI_TRUE; + } +#endif + /* decide next step */ + curStatus = get_mali_dvfs_status(); + nextStatus = decideNextStatus(mali_dvfs_freq); + + /* if next status is same with current status, don't change anything */ + if ((curStatus != nextStatus && stay_count == 0)) { + /* check if boost up or not */ + if (nextStatus > maliDvfsStatus.currentStep) + boostup = 1; + /* change mali dvfs status */ + if (!change_mali_dvfs_status(nextStatus, boostup)) + return MALI_FALSE; + stay_count = + mali_dvfs_staycount[maliDvfsStatus.currentStep].staycount; + } else { + if (stay_count > 0) + stay_count--; + } + return MALI_TRUE; +} + +mali_bool init_mali_dvfs_status(int step) +{ + set_mali_dvfs_current_step(step); + return MALI_TRUE; +} + +void deinit_mali_dvfs_status(void) +{ + if (clk_register_map) { + _mali_osk_mem_unmapioregion(CLK_DIV_STAT_G3D, + 0x20, + clk_register_map); + clk_register_map = 0; + } +} + +void mali_default_step_set(int step, mali_bool boostup) +{ + mali_clk_set_rate(mali_dvfs_value[step].clock, + mali_dvfs_value[step].freq); + if (maliDvfsStatus.currentStep == 1) + set_mali_dvfs_status(step, boostup); +} + +void mali_gpu_utilization_handler(u32 mali_dvfs_freq) +{ + int change_clk = 0; + int change_step = 0; + + /* dvfs table change when clock was changed */ + if (step0_clk != mali_dvfs_value[0].clock) { + MALI_PRINT(("::: step0_clk change to %d Mhz\n", step0_clk)); + change_clk = step0_clk; + change_step = 0; + step0_clk = change_dvfs_tableset(change_clk, change_step); + } + if (step1_clk != mali_dvfs_value[1].clock) { + MALI_PRINT(("::: step1_clk change to %d Mhz\n", step1_clk)); + change_clk = step1_clk; + change_step = 1; + step1_clk = change_dvfs_tableset(change_clk, change_step); + } + if (step0_up != mali_dvfs_threshold[0].upthreshold) { + MALI_PRINT(("::: step0_up change to %d %\n", step0_up)); + mali_dvfs_threshold[0].upthreshold = step0_up; + } + if (step1_down != mali_dvfs_threshold[1].downthreshold) { + MALI_PRINT((":::step1_down change to %d %\n", step1_down)); + mali_dvfs_threshold[1].downthreshold = step1_down; + } + if (step2_clk != mali_dvfs_value[2].clock) { + MALI_PRINT(("::: step2_clk change to %d Mhz\n", step2_clk)); + change_clk = step2_clk; + change_step = 2; + step2_clk = change_dvfs_tableset(change_clk, change_step); + } + if (step1_up != mali_dvfs_threshold[1].upthreshold) { + MALI_PRINT((":::step1_up change to %d %\n", step1_up)); + mali_dvfs_threshold[1].upthreshold = step1_up; + } + if (step2_down != mali_dvfs_threshold[2].downthreshold) { + MALI_PRINT((":::step2_down change to %d %\n", step2_down)); + mali_dvfs_threshold[2].downthreshold = step2_down; + } + if (step3_clk != mali_dvfs_value[3].clock) { + MALI_PRINT(("::: step3_clk change to %d Mhz\n", step3_clk)); + change_clk = step3_clk; + change_step = 3; + step3_clk = change_dvfs_tableset(change_clk, change_step); + } + if (step2_up != mali_dvfs_threshold[2].upthreshold) { + MALI_PRINT((":::step2_up change to %d %\n", step2_up)); + mali_dvfs_threshold[2].upthreshold = step2_up; + } + if (step3_down != mali_dvfs_threshold[3].downthreshold) { + MALI_PRINT((":::step3_down change to %d %\n", step3_down)); + mali_dvfs_threshold[3].downthreshold = step3_down; + } +#ifdef DEBUG + mali_dvfs_value[0].vol = step0_vol; + mali_dvfs_value[1].vol = step1_vol; + mali_dvfs_value[2].vol = step2_vol; + mali_dvfs_value[3].vol = step3_vol; +#endif + MALI_DEBUG_PRINT(3, ("=== mali_dvfs_work_handler\n")); + + if (!mali_dvfs_status(mali_dvfs_freq)) + MALI_DEBUG_PRINT(1, ("error on mali dvfs status" + "in mali_dvfs_work_handler")); + +} +/** @brief Get MALI current running frequency + * + * This function gets the current running frequency of MALI + * + * @return frequency in Hz + */ +unsigned long get_mali_platform_cur_freq(void) +{ + unsigned long rate = 0; + rate = mali_clk_get_rate(); + return rate; +} diff --git a/drivers/gpu/arm/mali/platform/mach-origen/mali_devfreq.h b/drivers/gpu/arm/mali/platform/mach-origen/mali_devfreq.h new file mode 100644 index 000000000000..47e4a8f888dc --- /dev/null +++ b/drivers/gpu/arm/mali/platform/mach-origen/mali_devfreq.h @@ -0,0 +1,49 @@ +/* + * 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. + */ + +#ifndef __MALI_DEVFREQ_H__ +#define __MALI_DEVFREQ_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define MALI_DVFS_STAY_AFTER_CHANGE 1 /* stay count after clock change */ +#define MALI_DVFS_DEFAULT_STEP 0 /* 134Mhz default */ +#define GPU_DVFS_UP_THRESHOLD ((int)((255*65)/100)) /* 60% */ +#define GPU_DVFS_DOWN_THRESHOLD ((int)((255*30)/100)) /* 30% */ +#define MALI_DVFS_WAITING 10 /* msec */ + +extern struct regulator *g3d_regulator; +extern mali_io_address clk_register_map; +extern _mali_osk_lock_t *mali_dvfs_lock; + +struct mali_dvfs_tbl { + unsigned int clock; + unsigned int freq; + unsigned int vol; +}; + +struct mali_dvfs_status { + unsigned int currentStep; + struct mali_dvfs_tbl *pCurrentDvfs; +}; + +mali_bool init_mali_dvfs_status(int step); + +void deinit_mali_dvfs_status(void); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/drivers/gpu/arm/mali/platform/mach-origen/mali_platform.c b/drivers/gpu/arm/mali/platform/mach-origen/mali_platform.c new file mode 100644 index 000000000000..c6f9513b5532 --- /dev/null +++ b/drivers/gpu/arm/mali/platform/mach-origen/mali_platform.c @@ -0,0 +1,365 @@ +/* + * 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 <linux/clk.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/regulator/driver.h> +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_platform.h" +#include "mali_devfreq.h" +#include "mali_linux_pm.h" + +#define MALI_DVFS_ENABLED 1 + +#define EXTXTALCLK_NAME "ext_xtal" +#define VPLLSRCCLK_NAME "vpll_src" +#define FOUTVPLLCLK_NAME "fout_vpll" +#define SCLVPLLCLK_NAME "sclk_vpll" +#define GPUMOUT1CLK_NAME "mout_g3d1" + +static struct clk *ext_xtal_clock; +static struct clk *vpll_src_clock; +static struct clk *fout_vpll_clock; +static struct clk *sclk_vpll_clock; + +int gpu_power_state; +static struct clk *mpll_clock; +static struct clk *mali_parent_clock; +struct regulator *g3d_regulator; +static struct clk *mali_clock; +static unsigned int GPU_MHZ = 1000000; +int mali_gpu_clk = 266; +int mali_gpu_vol = 900000; + +mali_io_address clk_register_map; +_mali_osk_lock_t *mali_dvfs_lock; + +mali_bool mali_clk_get(mali_bool bis_vpll); + +unsigned long mali_clk_get_rate(void) +{ + return clk_get_rate(mali_clock); +} + +mali_bool mali_clk_set_rate(unsigned int clk, unsigned int mhz) +{ + unsigned long rate = 0; + mali_bool bis_vpll = MALI_TRUE; + + _mali_osk_lock_wait(mali_dvfs_lock, _MALI_OSK_LOCKMODE_RW); + if (mali_clk_get(bis_vpll) == MALI_FALSE) + return MALI_FALSE; + rate = (unsigned long)clk * (unsigned long)mhz; + if (bis_vpll) { + clk_set_rate(fout_vpll_clock, (unsigned int)clk * GPU_MHZ); + clk_set_parent(vpll_src_clock, ext_xtal_clock); + clk_set_parent(sclk_vpll_clock, fout_vpll_clock); + clk_set_parent(mali_parent_clock, sclk_vpll_clock); + clk_set_parent(mali_clock, mali_parent_clock); + } else { + clk_set_parent(mali_parent_clock, mpll_clock); + clk_set_parent(mali_clock, mali_parent_clock); + } + + if (clk_enable(mali_clock) < 0) + return MALI_FALSE; + + clk_set_rate(mali_clock, rate); + rate = clk_get_rate(mali_clock); + + if (bis_vpll) + mali_gpu_clk = (int)(rate / mhz); + else + mali_gpu_clk = (int)((rate + 500000) / mhz); + + GPU_MHZ = mhz; + + mali_clk_put(MALI_FALSE); + + _mali_osk_lock_signal(mali_dvfs_lock, _MALI_OSK_LOCKMODE_RW); + return MALI_TRUE; +} + +int mali_regulator_get_usecount(void) +{ + struct regulator_dev *rdev; + if (g3d_regulator == NULL || g3d_regulator->rdev == NULL) + return 0; + rdev = g3d_regulator->rdev; + return rdev->use_count; +} + +void mali_regulator_disable(void) +{ + if (g3d_regulator == NULL) + return; + regulator_disable(g3d_regulator); +} + +void mali_regulator_enable(void) +{ + if (g3d_regulator == NULL) + return; + regulator_enable(g3d_regulator); +} + +void mali_regulator_set_voltage(int min_uV, int max_uV) +{ + int voltage; + if (g3d_regulator == NULL) + return; + regulator_set_voltage(g3d_regulator, min_uV, max_uV); + voltage = regulator_get_voltage(g3d_regulator); + mali_gpu_vol = voltage; +} + +mali_bool mali_clk_get(mali_bool bis_vpll) +{ + if (bis_vpll == MALI_TRUE) { + if (ext_xtal_clock == NULL) { + ext_xtal_clock = clk_get(NULL, EXTXTALCLK_NAME); + if (IS_ERR(ext_xtal_clock)) + return MALI_FALSE; + } + + if (vpll_src_clock == NULL) { + vpll_src_clock = clk_get(NULL, VPLLSRCCLK_NAME); + if (IS_ERR(vpll_src_clock)) + return MALI_FALSE; + } + + if (fout_vpll_clock == NULL) { + fout_vpll_clock = clk_get(NULL, FOUTVPLLCLK_NAME); + if (IS_ERR(fout_vpll_clock)) + return MALI_FALSE; + } + + if (sclk_vpll_clock == NULL) { + sclk_vpll_clock = clk_get(NULL, SCLVPLLCLK_NAME); + if (IS_ERR(sclk_vpll_clock)) + return MALI_FALSE; + } + + if (mali_parent_clock == NULL) { + mali_parent_clock = clk_get(NULL, GPUMOUT1CLK_NAME); + if (IS_ERR(mali_parent_clock)) + return MALI_FALSE; + } /* mpll */ + } else { + if (mpll_clock == NULL) { + mpll_clock = clk_get(NULL, MPLLCLK_NAME); + + if (IS_ERR(mpll_clock)) + return MALI_FALSE; + } + + if (mali_parent_clock == NULL) { + mali_parent_clock = clk_get(NULL, GPUMOUT0CLK_NAME); + if (IS_ERR(mali_parent_clock)) + return MALI_FALSE; + } + } + /* mali clock get always */ + if (mali_clock == NULL) { + mali_clock = clk_get(NULL, GPUCLK_NAME); + if (IS_ERR(mali_clock)) + return MALI_FALSE; + } + return MALI_TRUE; +} + +void mali_clk_put(mali_bool binc_mali_clock) +{ + if (mali_parent_clock) { + clk_put(mali_parent_clock); + mali_parent_clock = 0; + } + if (mpll_clock) { + clk_put(mpll_clock); + mpll_clock = 0; + } + if (sclk_vpll_clock) { + clk_put(sclk_vpll_clock); + sclk_vpll_clock = 0; + } + if (fout_vpll_clock) { + clk_put(fout_vpll_clock); + fout_vpll_clock = 0; + } + if (vpll_src_clock) { + clk_put(vpll_src_clock); + vpll_src_clock = 0; + } + if (ext_xtal_clock) { + clk_put(ext_xtal_clock); + ext_xtal_clock = 0; + } + if (binc_mali_clock == MALI_TRUE && mali_clock) { + clk_put(mali_clock); + mali_clock = 0; + } +} + +static mali_bool init_mali_clock(void) +{ + mali_bool ret = MALI_TRUE; + unsigned long rate = 0; + gpu_power_state = 0; + + if (mali_clock != 0) + return ret; /* already initialized */ + + mali_dvfs_lock = _mali_osk_lock_init( + _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE | + _MALI_OSK_LOCKFLAG_ONELOCK, 0, 0); + if (mali_dvfs_lock == NULL) + return _MALI_OSK_ERR_FAULT; + + if (mali_clk_set_rate(mali_gpu_clk, GPU_MHZ) == MALI_FALSE) { + ret = MALI_FALSE; + goto err_clock_get; + } + + rate = clk_get_rate(mali_clock); + + return MALI_TRUE; + +err_clock_get: + mali_clk_put(MALI_TRUE); + return ret; +} + +static mali_bool deinit_mali_clock(void) +{ + if (mali_clock == 0) + return MALI_TRUE; + + if (g3d_regulator) { + regulator_put(g3d_regulator); + g3d_regulator = NULL; + } + mali_clk_put(MALI_TRUE); + return MALI_TRUE; +} + +static _mali_osk_errcode_t disable_mali_clocks(void) +{ + clk_disable(mali_clock); + MALI_SUCCESS; +} + +static _mali_osk_errcode_t enable_mali_clocks(void) +{ + int err; + err = clk_enable(mali_clock); + mali_clk_set_rate(mali_gpu_clk, GPU_MHZ); + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_platform_init(void) +{ +#if MALI_DVFS_ENABLED + MALI_CHECK(init_mali_clock(), _MALI_OSK_ERR_FAULT); + init_mali_regulator(); + if (!clk_register_map) + clk_register_map = + _mali_osk_mem_mapioregion(CLK_DIV_STAT_G3D, 0x20, CLK_DESC); + if (!init_mali_dvfs_status(MALI_DVFS_DEFAULT_STEP)) + MALI_DEBUG_PRINT(1, ("mali_platform_init failed\n")); +#endif + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_platform_deinit(void) +{ + deinit_mali_dvfs_status(); + + deinit_mali_clock(); + + if (clk_register_map) { + _mali_osk_mem_unmapioregion(CLK_DIV_STAT_G3D, + 0x20, + clk_register_map); + clk_register_map = 0; + } + deinit_mali_regulator(); + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_platform_power_mode_change(mali_power_mode power_mode) +{ + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_platform_powerdown(u32 cores) +{ + /* power down after state is 0 */ + if (gpu_power_state != 0) { + gpu_power_state = gpu_power_state & (~cores); + if (gpu_power_state == 0) { + MALI_DEBUG_PRINT(3, ("disable clock\n")); + disable_mali_clocks(); + } + } else { + } + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_platform_powerup(u32 cores) +{ + /* power down after state is 0 */ + if (gpu_power_state == 0) { + gpu_power_state = gpu_power_state | cores; + if (gpu_power_state != 0) + enable_mali_clocks(); + } else { + gpu_power_state = gpu_power_state | cores; + } + MALI_SUCCESS; +} + +void set_mali_parent_power_domain(void *dev) +{ + return; +} + +mali_bool init_mali_regulator(void) +{ + mali_bool ret; + g3d_regulator = regulator_get(NULL, "vdd_g3d"); + if (IS_ERR(g3d_regulator)) { + ret = MALI_FALSE; + goto err_regulator; + } + + regulator_enable(g3d_regulator); + mali_regulator_set_voltage(mali_gpu_vol, mali_gpu_vol); + return MALI_TRUE; + +err_regulator: + regulator_put(g3d_regulator); + return ret; +} + +mali_bool deinit_mali_regulator(void) +{ + if (g3d_regulator) { + regulator_put(g3d_regulator); + g3d_regulator = NULL; + } + + return MALI_TRUE; +} diff --git a/drivers/gpu/arm/mali/platform/mali_platform.h b/drivers/gpu/arm/mali/platform/mali_platform.h index f1bb2b5c7ad4..3d2a4ee56cb3 100644 --- a/drivers/gpu/arm/mali/platform/mali_platform.h +++ b/drivers/gpu/arm/mali/platform/mali_platform.h @@ -1,25 +1,31 @@ /* * 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. + * This program is free software and is provided to you under the terms of + * the GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms of + * such GNU licence. * - * A copy of the licence is included with the program, and can also be obtained from Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * A 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_PLATFORM_H__ #define __MALI_PLATFORM_H__ +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/regulator/driver.h> + + #include "mali_osk.h" #if !USING_MALI_PMM -/* @brief System power up/down cores that can be passed into mali_platform_powerdown/up() */ +/* @brief System power up/down cores that can be passed into + * mali_platform_powerdown/up() + */ #define MALI_PLATFORM_SYSTEM 0 #endif @@ -27,8 +33,14 @@ extern "C" { #endif -/** @brief description of power change reasons - */ +#define MPLLCLK_NAME "mout_mpll" +#define GPUMOUT0CLK_NAME "mout_g3d0" +#define GPUCLK_NAME "sclk_g3d" +#define CLK_DIV_STAT_G3D 0x1003C62C +#define CLK_DESC "clk-divider-status" + +extern struct platform_device mali_gpu_device; +/* @brief description of power change reasons */ typedef enum mali_power_mode_tag { MALI_POWER_MODE_ON, @@ -36,11 +48,45 @@ typedef enum mali_power_mode_tag MALI_POWER_MODE_DEEP_SLEEP, } mali_power_mode; +struct regulator { + struct device *dev; + struct list_head list; + unsigned int always_on:1; + unsigned int bypass:1; + int uA_load; + int min_uV; + int max_uV; + char *supply_name; + struct device_attribute dev_attr; + struct regulator_dev *rdev; + struct dentry *debugfs; +}; + +int mali_regulator_get_usecount(void); + +void mali_regulator_disable(void); + +void mali_regulator_enable(void); + +void mali_regulator_set_voltage(int min_uV, int max_uV); + +mali_bool mali_clk_set_rate(unsigned int clk, unsigned int mhz); + +unsigned long mali_clk_get_rate(void); + +void mali_clk_put(mali_bool binc_mali_clock); + +mali_bool init_mali_regulator(void); + +mali_bool deinit_mali_regulator(void); + + /** @brief Platform specific setup and initialisation of MALI * * This is called from the entrypoint of the driver to initialize the platform * - * @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error. + * @return _MALI_OSK_ERR_OK on success otherwise, a suitable + * _mali_osk_errcode_t error. */ _mali_osk_errcode_t mali_platform_init(void); @@ -48,48 +94,56 @@ _mali_osk_errcode_t mali_platform_init(void); * * This is called on the exit of the driver to terminate the platform * - * @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error. + * @return _MALI_OSK_ERR_OK on success otherwise, a suitable + * _mali_osk_errcode_t error. */ _mali_osk_errcode_t mali_platform_deinit(void); /** @brief Platform specific powerdown sequence of MALI * * Call as part of platform init if there is no PMM support, else the - * PMM will call it. + * PMM will call it. * There are three power modes defined: * 1) MALI_POWER_MODE_ON * 2) MALI_POWER_MODE_LIGHT_SLEEP * 3) MALI_POWER_MODE_DEEP_SLEEP - * MALI power management module transitions to MALI_POWER_MODE_LIGHT_SLEEP mode when MALI is idle - * for idle timer (software timer defined in mali_pmm_policy_jobcontrol.h) duration, MALI transitions - * to MALI_POWER_MODE_LIGHT_SLEEP mode during timeout if there are no more jobs queued. - * MALI power management module transitions to MALI_POWER_MODE_DEEP_SLEEP mode when OS does system power - * off. - * Customer has to add power down code when MALI transitions to MALI_POWER_MODE_LIGHT_SLEEP or MALI_POWER_MODE_DEEP_SLEEP + * MALI power management module transitions to MALI_POWER_MODE_LIGHT_SLEEP mode + * when MALI is idle for idle timer (software timer defined in + * mali_pmm_policy_jobcontrol.h) duration, MALI transitions to + * MALI_POWER_MODE_LIGHT_SLEEP mode during timeout if there are no more + * jobs queued. + * MALI power management module transitions to MALI_POWER_MODE_DEEP_SLEEP mode + * when OS does system power off. + * Customer has to add power down code when MALI transitions to + * MALI_POWER_MODE_LIGHT_SLEEP or MALI_POWER_MODE_DEEP_SLEEP * mode. - * MALI_POWER_MODE_ON mode is entered when the MALI is to powered up. Some customers want to control voltage regulators during - * the whole system powers on/off. Customer can track in this function whether the MALI is powered up from - * MALI_POWER_MODE_LIGHT_SLEEP or MALI_POWER_MODE_DEEP_SLEEP mode and manage the voltage regulators as well. + * MALI_POWER_MODE_ON mode is entered when the MALI is to powered up. + * Some customers want to control voltage regulators during the whole system + * powers on/off. Customer can track in this function whether the MALI is + * powered up from MALI_POWER_MODE_LIGHT_SLEEP or MALI_POWER_MODE_DEEP_SLEEP + * mode and manage the voltage regulators as well. * @param power_mode defines the power modes - * @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error. + * @return _MALI_OSK_ERR_OK on success otherwise, a suitable + * _mali_osk_errcode_t error. */ _mali_osk_errcode_t mali_platform_power_mode_change(mali_power_mode power_mode); - /** @brief Platform specific handling of GPU utilization data * * When GPU utilization data is enabled, this function will be * periodically called. * - * @param utilization The workload utilization of the Mali GPU. 0 = no utilization, 256 = full utilization. + * @param utilization The workload utilization of the Mali GPU. + * 0 = no utilization, 256 = full utilization. */ void mali_gpu_utilization_handler(u32 utilization); /** @brief Setting the power domain of MALI * - * This function sets the power domain of MALI if Linux run time power management is enabled - * - * @param dev Reference to struct platform_device (defined in linux) used by MALI GPU + * This function sets the power domain of MALI if Linux run time power + * management is enabled + * @param dev Reference to struct platform_device (defined in linux) used by + * MALI GPU */ void set_mali_parent_power_domain(void* dev); |