/**
 * Copyright (C) 2010-2011 ARM Limited. All rights reserved.
 * Copyright (C) 2011 STMicroelectronics R&D 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.
 */

/* Even if the knl has no PM we need to configure it here so we have access to ARM's
 * platform driver code implementation. */
#ifndef CONFIG_PM
#define CONFIG_PM
#endif

/**
 * @file mali_kernel_pm.c
 * Implementation of the Linux Power Management for Mali GPU kernel driver
 */

#if USING_MALI_PMM
#include <linux/sched.h>
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
#endif /* CONFIG_HAS_EARLYSUSPEND */

#ifdef CONFIG_PM_RUNTIME
#include <linux/pm_runtime.h>
#endif /* CONFIG_PM_RUNTIME */

#include <linux/platform_device.h>
#include <linux/version.h>
#include <asm/current.h>
#include <asm/delay.h>
#include <linux/suspend.h>

#include "mali_platform.h" 
#include "mali_osk.h"
#include "mali_uk_types.h"
#include "mali_pmm.h"
#include "mali_ukk.h"
#include "mali_kernel_common.h"
#include "mali_kernel_license.h"
#include "mali_kernel_pm.h"
#include "mali_device_pause_resume.h"
#include "mali_linux_pm.h"

#if MALI_GPU_UTILIZATION
#include "mali_kernel_utilization.h"
#endif /* MALI_GPU_UTILIZATION */

#if MALI_POWER_MGMT_TEST_SUITE
#ifdef CONFIG_PM
#include "mali_linux_pm_testsuite.h"
unsigned int pwr_mgmt_status_reg = 0;
#endif /* CONFIG_PM */
#endif /* MALI_POWER_MGMT_TEST_SUITE */

#if MALI_STATE_TRACKING
	int is_os_pmm_thread_waiting = -1;
#endif /* MALI_STATE_TRACKING */

extern struct platform_device *mali_platform_device;

/* kernel should be configured with power management support */
#ifdef CONFIG_PM

/* License should be GPL */
#if MALI_LICENSE_IS_GPL

/* Linux kernel major version */
#define LINUX_KERNEL_MAJOR_VERSION 2

/* Linux kernel minor version */
#define LINUX_KERNEL_MINOR_VERSION 6

/* Linux kernel development version */
#define LINUX_KERNEL_DEVELOPMENT_VERSION 29

#ifdef CONFIG_PM_DEBUG
static const char* const mali_states[_MALI_MAX_DEBUG_OPERATIONS] = {
	[_MALI_DEVICE_SUSPEND] = "suspend",
	[_MALI_DEVICE_RESUME] = "resume",
#ifdef CONFIG_HAS_EARLYSUSPEND
	[_MALI_DEVICE_EARLYSUSPEND_DISABLE_FB] = "early_suspend_level_disable_framebuffer",
	[_MALI_DEVICE_LATERESUME] = "late_resume",
#endif /* CONFIG_HAS_EARLYSUSPEND */
	[_MALI_DVFS_PAUSE_EVENT] = "dvfs_pause",
	[_MALI_DVFS_RESUME_EVENT] = "dvfs_resume",
};

#endif /* CONFIG_PM_DEBUG */

#if MALI_PMM_RUNTIME_JOB_CONTROL_ON
extern void set_mali_parent_power_domain(struct platform_device* dev);
#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */

#ifdef CONFIG_PM_RUNTIME
#if MALI_PMM_RUNTIME_JOB_CONTROL_ON
#ifndef CONFIG_HAS_EARLYSUSPEND
static int mali_pwr_suspend_notifier(struct notifier_block *nb,unsigned long event,void* dummy);

static struct notifier_block mali_pwr_notif_block = {
	.notifier_call = mali_pwr_suspend_notifier
};
#endif /* CONFIG_HAS_EARLYSUSPEND */
#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */
#endif /* CONFIG_PM_RUNTIME */

/* Power management thread pointer */
struct task_struct *pm_thread;

/* dvfs power management thread */
struct task_struct *dvfs_pm_thread;

/* is wake up needed */
short is_wake_up_needed = 0;
int timeout_fired = 2;
unsigned int is_mali_pmm_testsuite_enabled = 0;

_mali_device_power_states mali_device_state = _MALI_DEVICE_RESUME;
_mali_device_power_states mali_dvfs_device_state = _MALI_DEVICE_RESUME;
_mali_osk_lock_t *lock;

#if MALI_POWER_MGMT_TEST_SUITE

const char* const mali_pmm_recording_events[_MALI_DEVICE_MAX_PMM_EVENTS] = {
	[_MALI_DEVICE_PMM_TIMEOUT_EVENT] = "timeout",
	[_MALI_DEVICE_PMM_JOB_SCHEDULING_EVENTS] = "job_scheduling",
	[_MALI_DEVICE_PMM_REGISTERED_CORES] = "cores",

};

unsigned int mali_timeout_event_recording_on = 0;
unsigned int mali_job_scheduling_events_recording_on = 0;
unsigned int is_mali_pmu_present = 0;
#endif /* MALI_POWER_MGMT_TEST_SUITE */

/* Function prototypes */
static int mali_pm_probe(struct platform_device *pdev);
static int mali_pm_remove(struct platform_device *pdev);

/* Mali device suspend function */
static int mali_pm_suspend(struct device *dev);

/* Mali device resume function */
static int mali_pm_resume(struct device *dev);

/* Run time suspend and resume functions */
#ifdef CONFIG_PM_RUNTIME
#if MALI_PMM_RUNTIME_JOB_CONTROL_ON
static int mali_device_runtime_suspend(struct device *dev);
static int mali_device_runtime_resume(struct device *dev);
#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */
#endif /* CONFIG_PM_RUNTIME */

/* Early suspend functions */
#ifdef CONFIG_HAS_EARLYSUSPEND
static void mali_pm_early_suspend(struct early_suspend *mali_dev);
static void mali_pm_late_resume(struct early_suspend *mali_dev);
#endif /* CONFIG_HAS_EARLYSUSPEND */

/* OS suspend and resume callbacks */
#if !MALI_PMM_RUNTIME_JOB_CONTROL_ON
#ifndef CONFIG_PM_RUNTIME
#if (LINUX_VERSION_CODE < KERNEL_VERSION(LINUX_KERNEL_MAJOR_VERSION,LINUX_KERNEL_MINOR_VERSION,LINUX_KERNEL_DEVELOPMENT_VERSION))
static int mali_pm_os_suspend(struct platform_device *pdev, pm_message_t state);
#else
static int mali_pm_os_suspend(struct device *dev);
#endif

#if (LINUX_VERSION_CODE < KERNEL_VERSION(LINUX_KERNEL_MAJOR_VERSION,LINUX_KERNEL_MINOR_VERSION,LINUX_KERNEL_DEVELOPMENT_VERSION))
static int mali_pm_os_resume(struct platform_device *pdev);
#else
static int mali_pm_os_resume(struct device *dev);
#endif
#endif /* CONFIG_PM_RUNTIME */
#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */

/* OS Hibernation suspend callback */
static int mali_pm_os_suspend_on_hibernation(struct device *dev);

/* OS Hibernation resume callback */
static int mali_pm_os_resume_on_hibernation(struct device *dev);

static void _mali_release_pm(struct device* device);

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(LINUX_KERNEL_MAJOR_VERSION,LINUX_KERNEL_MINOR_VERSION,LINUX_KERNEL_DEVELOPMENT_VERSION))
static const struct dev_pm_ops mali_dev_pm_ops = {

#ifdef CONFIG_PM_RUNTIME
#if MALI_PMM_RUNTIME_JOB_CONTROL_ON
	.runtime_suspend = mali_device_runtime_suspend,
	.runtime_resume = mali_device_runtime_resume,
#endif  /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */
#endif  /* CONFIG_PM_RUNTIME */

#ifndef CONFIG_PM_RUNTIME
#if !MALI_PMM_RUNTIME_JOB_CONTROL_ON
	.suspend = mali_pm_os_suspend,
	.resume = mali_pm_os_resume,
#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */
#endif /* CONFIG_PM_RUNTIME */
	.freeze = mali_pm_os_suspend_on_hibernation,
	.poweroff = mali_pm_os_suspend_on_hibernation,
	.thaw = mali_pm_os_resume_on_hibernation,
	.restore = mali_pm_os_resume_on_hibernation,
};
#endif

#if (LINUX_VERSION_CODE < KERNEL_VERSION(LINUX_KERNEL_MAJOR_VERSION,LINUX_KERNEL_MINOR_VERSION,LINUX_KERNEL_DEVELOPMENT_VERSION))
struct pm_ext_ops mali_pm_operations = {
	.base = {
		.freeze = mali_pm_os_suspend_on_hibernation,
		.thaw =   mali_pm_os_resume_on_hibernation,
		.poweroff = mali_pm_os_resume_on_hibernation,
		.restore = mali_pm_os_resume_on_hibernation,
	},
};
#endif

static struct platform_driver mali_plat_driver = {
	.probe  = mali_pm_probe,
	.remove = mali_pm_remove,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(LINUX_KERNEL_MAJOR_VERSION,LINUX_KERNEL_MINOR_VERSION,LINUX_KERNEL_DEVELOPMENT_VERSION))
#ifndef CONFIG_PM_RUNTIME
#if !MALI_PMM_RUNTIME_JOB_CONTROL_ON
	.suspend = mali_pm_os_suspend,
	.resume  = mali_pm_os_resume,
#endif /* CONFIG_PM_RUNTIME */
#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */
	.pm = &mali_pm_operations,
#endif

	.driver         = {
		.name   = "mali",
		.owner  = THIS_MODULE,
		.bus = &platform_bus_type,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(LINUX_KERNEL_MAJOR_VERSION,LINUX_KERNEL_MINOR_VERSION,LINUX_KERNEL_DEVELOPMENT_VERSION))
		.pm = &mali_dev_pm_ops,
#endif
		},
};

#ifdef CONFIG_HAS_EARLYSUSPEND
/* Early suspend hooks */
static struct early_suspend mali_dev_early_suspend = {
	.suspend = mali_pm_early_suspend,
	.resume = mali_pm_late_resume,
	.level = EARLY_SUSPEND_LEVEL_DISABLE_FB,
};
#endif /* CONFIG_HAS_EARLYSUSPEND */

/* Mali GPU platform device */
struct platform_device mali_gpu_device = {
	.name = "mali_dev",
	.id = 0,
	.dev.release = _mali_release_pm
};

/** This function is called when platform device is unregistered. This function
 * is necessary when the platform device is unregistered.
 */
static void _mali_release_pm(struct device *device)
{
	MALI_DEBUG_PRINT(4, ("OSPMM: MALI Platform device removed\n" ));
}

#if MALI_POWER_MGMT_TEST_SUITE
void mali_is_pmu_present(void)
{
	int temp = 0;
	temp = pmu_get_power_up_down_info();
	if (4095 == temp)
	{
		is_mali_pmu_present = 0;
	}
	else
	{
		is_mali_pmu_present = 1;
	}
}
#endif /* MALI_POWER_MGMT_TEST_SUITE */
#endif /* MALI_LICENSE_IS_GPL */

#if MALI_LICENSE_IS_GPL

static int mali_wait_for_power_management_policy_event(void)
{
	int err = 0;
	for (; ;)
	{
		set_current_state(TASK_INTERRUPTIBLE);
		if (signal_pending(current))
		{
			err = -EINTR;
			break;
		}
		if (is_wake_up_needed == 1)
		{
			break;
		}
		schedule();
	}
	__set_current_state(TASK_RUNNING);
	is_wake_up_needed =0;
	return err;
}

/** This function is invoked when mali device is suspended
 */
int mali_device_suspend(unsigned int event_id, struct task_struct **pwr_mgmt_thread)
{
	int err = 0;	
	_mali_uk_pmm_message_s event = {
	                               NULL,
	                               event_id,
	                               timeout_fired};
	*pwr_mgmt_thread = current;
	MALI_DEBUG_PRINT(4, ("OSPMM: MALI device is being suspended\n" ));
	_mali_ukk_pmm_event_message(&event);
#if MALI_STATE_TRACKING
	is_os_pmm_thread_waiting = 1;
#endif /* MALI_STATE_TRACKING */
	err = mali_wait_for_power_management_policy_event();
#if MALI_STATE_TRACKING
	is_os_pmm_thread_waiting = 0;
#endif /* MALI_STATE_TRACKING */
	return err;
}

/** This function is called when Operating system wants to power down
 * the mali GPU device.
 */
static int mali_pm_suspend(struct device *dev)
{
	int err = 0;
	_mali_osk_lock_wait(lock, _MALI_OSK_LOCKMODE_RW);
#if MALI_GPU_UTILIZATION
	mali_utilization_suspend();
#endif /* MALI_GPU_UTILIZATION */
	if ((mali_device_state == _MALI_DEVICE_SUSPEND) 
#ifdef CONFIG_HAS_EARLYSUSPEND
	    || mali_device_state ==  (_MALI_DEVICE_EARLYSUSPEND_DISABLE_FB))
#else
	)
#endif /* CONFIG_HAS_EARLYSUSPEND */
	{
		_mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW);
		return err;
	}
	mali_device_state = _MALI_DEVICE_SUSPEND_IN_PROGRESS;
	err = mali_device_suspend(MALI_PMM_EVENT_OS_POWER_DOWN, &pm_thread);
	mali_device_state = _MALI_DEVICE_SUSPEND;
	_mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW);
	return err;
}

#ifndef CONFIG_PM_RUNTIME
#if !MALI_PMM_RUNTIME_JOB_CONTROL_ON
#if (LINUX_VERSION_CODE < KERNEL_VERSION(LINUX_KERNEL_MAJOR_VERSION,LINUX_KERNEL_MINOR_VERSION,LINUX_KERNEL_DEVELOPMENT_VERSION))
static int mali_pm_os_suspend(struct platform_device *pdev, pm_message_t state)
#else
static int mali_pm_os_suspend(struct device *dev)
#endif
{
	int err = 0;
	err = mali_pm_suspend(NULL);
	return err;
}
#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */
#endif /* CONFIG_PM_RUNTIME */

#ifdef CONFIG_PM_RUNTIME
#if MALI_PMM_RUNTIME_JOB_CONTROL_ON
#ifndef CONFIG_HAS_EARLYSUSPEND
static int mali_pwr_suspend_notifier(struct notifier_block *nb,unsigned long event,void* dummy)
{
	int err = 0;
	switch (event)
	{
		case PM_SUSPEND_PREPARE:
			err = mali_pm_suspend(NULL);
		break;

		case PM_POST_SUSPEND:
			err = mali_pm_resume(NULL);
		break;
		default:
		break;
	}
	return 0;
}
#endif /* CONFIG_HAS_EARLYSUSPEND */
#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */
#endif /* CONFIG_PM_RUNTIME */

/** This function is called when mali GPU device is to be resumed.
 */
int mali_device_resume(unsigned int event_id, struct task_struct **pwr_mgmt_thread)
{
	int err = 0;
	_mali_uk_pmm_message_s event = {
	                               NULL,
	                               event_id,
	                               timeout_fired};
	*pwr_mgmt_thread = current;
	MALI_DEBUG_PRINT(4, ("OSPMM: MALI device is being resumed\n" ));
	_mali_ukk_pmm_event_message(&event);
	MALI_DEBUG_PRINT(4, ("OSPMM: MALI Power up  event is scheduled\n" ));

#if MALI_STATE_TRACKING
	is_os_pmm_thread_waiting = 1;
#endif /* MALI_STATE_TRACKING */

	err = mali_wait_for_power_management_policy_event();

#if MALI_STATE_TRACKING
	is_os_pmm_thread_waiting = 0;
#endif /* MALI_STATE_TRACKING */

	return err;
}

/** This function is called when mali GPU device is to be resumed
 */

static int mali_pm_resume(struct device *dev)
{
	int err = 0;
	_mali_osk_lock_wait(lock, _MALI_OSK_LOCKMODE_RW);
	if (mali_device_state == _MALI_DEVICE_RESUME)
	{
		_mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW);
		return err;
	}
	err = mali_device_resume(MALI_PMM_EVENT_OS_POWER_UP, &pm_thread);
	mali_device_state = _MALI_DEVICE_RESUME;
	mali_dvfs_device_state = _MALI_DEVICE_RESUME;
	_mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW);
	return err;
}

#ifndef CONFIG_PM_RUNTIME
#if !MALI_PMM_RUNTIME_JOB_CONTROL_ON
#if (LINUX_VERSION_CODE < KERNEL_VERSION(LINUX_KERNEL_MAJOR_VERSION,LINUX_KERNEL_MINOR_VERSION,LINUX_KERNEL_DEVELOPMENT_VERSION))
static int mali_pm_os_resume(struct platform_device *pdev)
#else
static int mali_pm_os_resume(struct device *dev)
#endif
{
	int err = 0;
	err = mali_pm_resume(NULL);
	return err;
}
#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */
#endif /* CONFIG_PM_RUNTIME */

static int mali_pm_os_suspend_on_hibernation(struct device *dev)
{
	int err = 0;
	err = mali_pm_suspend(NULL);
	return err;
}

static int mali_pm_os_resume_on_hibernation(struct device *dev)
{
	int err = 0;
	err = mali_pm_resume(NULL);
	return err;
}

#ifdef CONFIG_PM_RUNTIME
#if MALI_PMM_RUNTIME_JOB_CONTROL_ON
/** This function is called when runtime suspend of mali device is required.
 */
static int mali_device_runtime_suspend(struct device *dev)
{
	MALI_DEBUG_PRINT(4, ("PMMDEBUG: Mali device Run time suspended \n" ));
	return 0;
}

/** This function is called when runtime resume of mali device is required.
 */
static int mali_device_runtime_resume(struct device *dev)
{
	MALI_DEBUG_PRINT(4, ("PMMDEBUG: Mali device Run time Resumed \n" ));
	return 0;
}
#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */
#endif /*  CONFIG_PM_RUNTIME */

#ifdef CONFIG_HAS_EARLYSUSPEND

/* This function is called from android framework.
 */
static void mali_pm_early_suspend(struct early_suspend *mali_dev)
{
	switch(mali_dev->level)
	{
		/* Screen should be turned off but framebuffer will be accessible */
		case EARLY_SUSPEND_LEVEL_BLANK_SCREEN:
			MALI_DEBUG_PRINT(4, ("PMMDEBUG: Screen is off\n" ));
		break;

		case EARLY_SUSPEND_LEVEL_STOP_DRAWING:
			MALI_DEBUG_PRINT(4, ("PMMDEBUG: Suspend level stop drawing\n" ));
		break;

		/* Turn off the framebuffer. In our case No Mali GPU operation */
		case EARLY_SUSPEND_LEVEL_DISABLE_FB:
			MALI_DEBUG_PRINT(4, ("PMMDEBUG: Suspend level Disable framebuffer\n" ));
			_mali_osk_lock_wait(lock, _MALI_OSK_LOCKMODE_RW);
#if MALI_GPU_UTILIZATION
			mali_utilization_suspend();
#endif /* MALI_GPU_UTILIZATION */
			if ((mali_device_state == _MALI_DEVICE_SUSPEND) || (mali_device_state == _MALI_DEVICE_EARLYSUSPEND_DISABLE_FB))
			{
				_mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW);
				return;
			}
			mali_device_suspend(MALI_PMM_EVENT_OS_POWER_DOWN, &pm_thread);
			mali_device_state =  _MALI_DEVICE_EARLYSUSPEND_DISABLE_FB;
			_mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW);
		break;

		default:
			MALI_DEBUG_PRINT(4, ("PMMDEBUG: Invalid Suspend Mode\n" ));
		break;
	}
}

/* This function is invoked from android framework when mali device needs to be
 * resumed.
 */
static void mali_pm_late_resume(struct early_suspend *mali_dev)
{
	_mali_osk_lock_wait(lock, _MALI_OSK_LOCKMODE_RW);
	if (mali_device_state == _MALI_DEVICE_RESUME)
	{
		_mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW);
		return;
	}
	if (mali_device_state ==  _MALI_DEVICE_EARLYSUSPEND_DISABLE_FB)
	{
		mali_device_resume(MALI_PMM_EVENT_OS_POWER_UP, &pm_thread);
		mali_dvfs_device_state = _MALI_DEVICE_RESUME;
		mali_device_state =  _MALI_DEVICE_RESUME;
	}
	_mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW);
	
}

#endif /* CONFIG_HAS_EARLYSUSPEND */

#ifdef CONFIG_PM_DEBUG

/** This function is used for debugging purposes when the user want to see
 * which power management operations are supported for
 * mali device.
 */
static ssize_t show_file(struct device *dev, struct device_attribute *attr, char *buf)
{
	char *str = buf;
#if !MALI_POWER_MGMT_TEST_SUITE
	int pm_counter = 0;
	for (pm_counter = 0; pm_counter<_MALI_MAX_DEBUG_OPERATIONS; pm_counter++) 
	{
		str += sprintf(str, "%s  ", mali_states[pm_counter]);
	}
#else
	str += sprintf(str, "%d  ",pwr_mgmt_status_reg);
#endif
	if (str != buf)
	{
		*(str-1) = '\n';
	}
	return (str-buf);
}

/** This function is called when user wants to suspend the mali GPU device in order
 * to simulate the power up and power down events.
 */
static ssize_t store_file(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
	int err = 0;
#ifdef CONFIG_HAS_EARLYSUSPEND
	struct early_suspend mali_dev;
#endif /* CONFIG_HAS_EARLYSUSPEND */

#if MALI_POWER_MGMT_TEST_SUITE
	int test_flag_dvfs = 0;
        pwr_mgmt_status_reg = 0;
        mali_is_pmu_present();

#endif
	if (!strncmp(buf,mali_states[_MALI_DEVICE_SUSPEND],strlen(mali_states[_MALI_DEVICE_SUSPEND])))
	{
		MALI_DEBUG_PRINT(4, ("PMMDEBUG: MALI suspend Power operation is scheduled\n" ));
		err = mali_pm_suspend(NULL);
	}

#if MALI_POWER_MGMT_TEST_SUITE
	else if (!strncmp(buf,mali_pmm_recording_events[_MALI_DEVICE_PMM_REGISTERED_CORES],strlen(mali_pmm_recording_events[_MALI_DEVICE_PMM_REGISTERED_CORES])))
	{
		MALI_DEBUG_PRINT(4, ("PMMDEBUG: MALI Device get number of registerd cores\n" ));
		pwr_mgmt_status_reg = _mali_pmm_cores_list();
		return count;
	}
	else if (!strncmp(buf,mali_pmm_recording_events[_MALI_DEVICE_PMM_TIMEOUT_EVENT],strlen(mali_pmm_recording_events[_MALI_DEVICE_PMM_TIMEOUT_EVENT])))
	{
		MALI_DEBUG_PRINT(4, ("PMMDEBUG: MALI timeout event recording is enabled\n" ));
		mali_timeout_event_recording_on = 1;
	}
	else if (!strncmp(buf,mali_pmm_recording_events[_MALI_DEVICE_PMM_JOB_SCHEDULING_EVENTS],strlen(mali_pmm_recording_events[_MALI_DEVICE_PMM_JOB_SCHEDULING_EVENTS])))
	{
		MALI_DEBUG_PRINT(4, ("PMMDEBUG: MALI Job scheduling  events recording is enabled\n" ));
		mali_job_scheduling_events_recording_on = 1;
	}
#endif /* MALI_POWER_MGMT_TEST_SUITE */

	else if (!strncmp(buf,mali_states[_MALI_DEVICE_RESUME],strlen(mali_states[_MALI_DEVICE_RESUME]))) 
	{
		MALI_DEBUG_PRINT(4, ("PMMDEBUG: MALI Resume Power operation is scheduled\n" ));
		err = mali_pm_resume(NULL);
	}
#ifdef CONFIG_HAS_EARLYSUSPEND
	else if (!strncmp(buf,mali_states[_MALI_DEVICE_EARLYSUSPEND_DISABLE_FB],strlen(mali_states[_MALI_DEVICE_EARLYSUSPEND_DISABLE_FB]))) 
	{
		mali_dev.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
		MALI_DEBUG_PRINT(4, ("PMMDEBUG: Android early suspend operation is scheduled\n" ));
		mali_pm_early_suspend(&mali_dev);
	}
	else if (!strncmp(buf,mali_states[_MALI_DEVICE_LATERESUME],strlen(mali_states[_MALI_DEVICE_LATERESUME]))) 
	{
		MALI_DEBUG_PRINT(4, ("PMMDEBUG: MALI Resume Power operation is scheduled\n" ));
		mali_pm_late_resume(NULL);
	}
#endif /* CONFIG_HAS_EARLYSUSPEND */
	else if (!strncmp(buf,mali_states[_MALI_DVFS_PAUSE_EVENT],strlen(mali_states[_MALI_DVFS_PAUSE_EVENT])))
	{
		MALI_DEBUG_PRINT(4, ("PMMDEBUG: MALI DVFS Pause Power operation is scheduled\n" ));
		err = mali_dev_pause();
#if MALI_POWER_MGMT_TEST_SUITE
		test_flag_dvfs = 1;
#endif /* MALI_POWER_MGMT_TEST_SUITE */
	}
	else if (!strncmp(buf,mali_states[_MALI_DVFS_RESUME_EVENT],strlen(mali_states[_MALI_DVFS_RESUME_EVENT])))
	{
		MALI_DEBUG_PRINT(4, ("PMMDEBUG: MALI DVFS Resume Power operation is scheduled\n" ));
		err = mali_dev_resume();
#if MALI_POWER_MGMT_TEST_SUITE
		test_flag_dvfs = 1;
#endif /* MALI_POWER_MGMT_TEST_SUITE */
	}
	else 
	{
		MALI_DEBUG_PRINT(4, ("PMMDEBUG: Invalid Power Mode Operation selected\n" ));
	}
#if MALI_POWER_MGMT_TEST_SUITE
	if (test_flag_dvfs == 1)
	{
		if (err)
		{
			pwr_mgmt_status_reg = 2;
		}
		else
		{
			pwr_mgmt_status_reg = 1;
		}
	}
	else
	{
		if (1 == is_mali_pmu_present)
		{
			pwr_mgmt_status_reg = pmu_get_power_up_down_info();
		}
	}
#endif /* MALI_POWER_MGMT_TEST_SUITE */
	return count;
}

/* Device attribute file */
static DEVICE_ATTR(file, 0644, show_file, store_file);
#endif /* CONFIG_PM_DEBUG */

static int mali_pm_remove(struct platform_device *pdev)
{
#ifdef CONFIG_PM_DEBUG
	device_remove_file(&mali_gpu_device.dev, &dev_attr_file);
#endif /* CONFIG_PM_DEBUG */
#ifdef CONFIG_PM_RUNTIME
#if MALI_PMM_RUNTIME_JOB_CONTROL_ON
	pm_runtime_disable(&pdev->dev);
#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */
#endif /* CONFIG_PM_RUNTIME */
	return 0;
}

/** This function is called when the device is probed */
static int mali_pm_probe(struct platform_device *pdev)
{
	MALI_DEBUG_PRINT(2, ("Mali Platform device probe id = %d num_resources = %d resource = %p.\n",pdev->id,pdev->num_resources,pdev->resource));
	/*
	 * As OS init has already created the platform device we record it in the global
	 */
	mali_platform_device = pdev;

#ifdef CONFIG_PM_DEBUG
	int err;
	err = device_create_file(&mali_gpu_device.dev, &dev_attr_file);
	if (err)
	{
		MALI_DEBUG_PRINT(4, ("PMMDEBUG: Error in creating device file\n" ));
	}
#endif /* CONFIG_PM_DEBUG */
	return 0;
}

/** This function is called when Mali GPU device is initialized
 */
int _mali_dev_platform_register(void)
{
	int err;
#if MALI_PMM_RUNTIME_JOB_CONTROL_ON	
	set_mali_parent_power_domain(&mali_gpu_device);
#endif

#ifdef CONFIG_PM_RUNTIME
#ifndef CONFIG_HAS_EARLYSUSPEND
#if MALI_PMM_RUNTIME_JOB_CONTROL_ON
	err = register_pm_notifier(&mali_pwr_notif_block);
	if (err)
	{
		return err;
	}
#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */
#endif /* CONFIG_HAS_EARLYSUSPEND */
#endif /* CONFIG_PM_RUNTIME */

	/* The platform device is actually registered by the OS startup code so we don't
	 * need to do it here. All we do is register the platform driver
	 */
	MALI_DEBUG_PRINT(3, ("[%s] Not registering platform device\n", __FUNCTION__));

	lock = _mali_osk_lock_init((_mali_osk_lock_flags_t)( _MALI_OSK_LOCKFLAG_READERWRITER | _MALI_OSK_LOCKFLAG_ORDERED), 0, 0);
	err = platform_driver_register(&mali_plat_driver);
	if (!err)
	{
#ifdef CONFIG_HAS_EARLYSUSPEND
		register_early_suspend(&mali_dev_early_suspend);
#endif /* CONFIG_HAS_EARLYSUSPEND */
	}
	else
	{
		_mali_osk_lock_term(lock);
#ifdef CONFIG_PM_RUNTIME
#ifndef CONFIG_HAS_EARLYSUSPEND
#if MALI_PMM_RUNTIME_JOB_CONTROL_ON
			unregister_pm_notifier(&mali_pwr_notif_block);
#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */
#endif /* CONFIG_HAS_EARLYSUSPEND */
#endif /* CONFIG_PM_RUNTIME */
	}
	return err;
}

/** This function is called when Mali GPU device is unloaded
 */
void _mali_dev_platform_unregister(void)
{
	_mali_osk_lock_term(lock);

#ifdef CONFIG_HAS_EARLYSUSPEND
	unregister_early_suspend(&mali_dev_early_suspend);
#endif /* CONFIG_HAS_EARLYSUSPEND */

#ifdef CONFIG_PM_RUNTIME
#ifndef CONFIG_HAS_EARLYSUSPEND
#if MALI_PMM_RUNTIME_JOB_CONTROL_ON
	unregister_pm_notifier(&mali_pwr_notif_block);
#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */
#endif /* CONFIG_HAS_EARLYSUSPEND */
#endif /* CONFIG_PM_RUNTIME */

	platform_driver_unregister(&mali_plat_driver);
	/* We dont unregister the platform_device as it was registered by the OS not us */
}

#endif /* MALI_LICENSE_IS_GPL */
#endif /* CONFIG_PM */

#if MALI_STATE_TRACKING
u32 mali_pmm_dump_os_thread_state( char *buf, u32 size )
{
	return snprintf(buf, size, "OSPMM: OS PMM thread is waiting: %s\n", is_os_pmm_thread_waiting ? "true" : "false");
}
#endif /* MALI_STATE_TRACKING */
#endif /* USING_MALI_PMM */