/**
- * Copyright (C) 2010-2015 ARM Limited. All rights reserved.
+ * Copyright (C) 2010-2016 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.
#include <linux/miscdevice.h>
#include <linux/bug.h>
#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
#include <linux/mali/mali_utgard.h>
#include "mali_kernel_common.h"
EXPORT_TRACEPOINT_SYMBOL_GPL(mali_sw_counters);
#endif /* CONFIG_TRACEPOINTS */
+#ifdef CONFIG_MALI_DEVFREQ
+#include "mali_devfreq.h"
+#include "mali_osk_mali.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
+#include <linux/pm_opp.h>
+#else
+/* In 3.13 the OPP include header file, types, and functions were all
+ * renamed. Use the old filename for the include, and define the new names to
+ * the old, when an old kernel is detected.
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)
+#include <linux/pm_opp.h>
+#else
+#include <linux/opp.h>
+#endif /* Linux >= 3.13*/
+#define dev_pm_opp_of_add_table of_init_opp_table
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
+#define dev_pm_opp_of_remove_table of_free_opp_table
+#endif /* Linux >= 3.19 */
+#endif /* Linux >= 4.4.0 */
+#endif
+
/* from the __malidrv_build_info.c file that is generated during build */
extern const char *__malidrv_build_info(void);
MALI_PRINT(("Mali device driver unloaded\n"));
}
+#ifdef CONFIG_MALI_DEVFREQ
+struct mali_device *mali_device_alloc(void)
+{
+ return kzalloc(sizeof(struct mali_device), GFP_KERNEL);
+}
+
+void mali_device_free(struct mali_device *mdev)
+{
+ kfree(mdev);
+}
+#endif
+
static int mali_probe(struct platform_device *pdev)
{
int err;
+#ifdef CONFIG_MALI_DEVFREQ
+ struct mali_device *mdev;
+#endif
MALI_DEBUG_PRINT(2, ("mali_probe(): Called for platform device %s\n", pdev->name));
err = mali_platform_device_init(mali_platform_device);
if (0 != err) {
MALI_PRINT_ERROR(("mali_probe(): Failed to initialize platform device."));
+ mali_platform_device = NULL;
return -EFAULT;
}
#endif
+#ifdef CONFIG_MALI_DEVFREQ
+ mdev = mali_device_alloc();
+ if (!mdev) {
+ MALI_PRINT_ERROR(("Can't allocate mali device private data\n"));
+ return -ENOMEM;
+ }
+
+ mdev->dev = &pdev->dev;
+ dev_set_drvdata(mdev->dev, mdev);
+
+ /*Initilization clock and regulator*/
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) && defined(CONFIG_OF) \
+ && defined(CONFIG_REGULATOR)
+ mdev->regulator = regulator_get_optional(mdev->dev, "mali");
+ if (IS_ERR_OR_NULL(mdev->regulator)) {
+ MALI_DEBUG_PRINT(2, ("Continuing without Mali regulator control\n"));
+ mdev->regulator = NULL;
+ /* Allow probe to continue without regulator */
+ }
+#endif /* LINUX_VERSION_CODE >= 3, 12, 0 */
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0)) && defined(CONFIG_OF) \
+ && defined(CONFIG_PM_OPP)
+ /* Register the OPPs if they are available in device tree */
+ if (dev_pm_opp_of_add_table(mdev->dev) < 0)
+ MALI_DEBUG_PRINT(3, ("OPP table not found\n"));
+#endif
+
+ /* Need to name the gpu clock "clk_mali" in the device tree */
+ mdev->clock = clk_get(mdev->dev, "clk_mali");
+ if (IS_ERR_OR_NULL(mdev->clock)) {
+ MALI_DEBUG_PRINT(2, ("Continuing without Mali clock control\n"));
+ mdev->clock = NULL;
+ /* Allow probe to continue without clock. */
+ } else {
+ err = clk_prepare_enable(mdev->clock);
+ if (err) {
+ MALI_PRINT_ERROR(("Failed to prepare and enable clock (%d)\n", err));
+ goto clock_prepare_failed;
+ }
+ }
+
+ /* initilize pm metrics related */
+ if (mali_pm_metrics_init(mdev) < 0) {
+ MALI_DEBUG_PRINT(2, ("mali pm metrics init failed\n"));
+ goto pm_metrics_init_failed;
+ }
+
+ if (mali_devfreq_init(mdev) < 0) {
+ MALI_DEBUG_PRINT(2, ("mali devfreq init failed\n"));
+ goto devfreq_init_failed;
+ }
+#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()) {
_mali_osk_wq_term();
}
+#ifdef CONFIG_MALI_DEVFREQ
+ mali_devfreq_term(mdev);
+devfreq_init_failed:
+ mali_pm_metrics_term(mdev);
+pm_metrics_init_failed:
+ clk_disable_unprepare(mdev->clock);
+clock_prepare_failed:
+ clk_put(mdev->clock);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)) && defined(CONFIG_OF) \
+ && defined(CONFIG_PM_OPP)
+ dev_pm_opp_of_remove_table(mdev->dev);
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) && defined(CONFIG_OF) \
+ && defined(CONFIG_REGULATOR)
+ regulator_put(mdev->regulator);
+#endif /* LINUX_VERSION_CODE >= 3, 12, 0 */
+ mali_device_free(mdev);
+#endif
+
+#ifdef CONFIG_MALI_DT
+ mali_platform_device_deinit(mali_platform_device);
+#endif
mali_platform_device = NULL;
return -EFAULT;
}
static int mali_remove(struct platform_device *pdev)
{
+#ifdef CONFIG_MALI_DEVFREQ
+ struct mali_device *mdev = dev_get_drvdata(&pdev->dev);
+#endif
+
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();
+
+#ifdef CONFIG_MALI_DEVFREQ
+ mali_devfreq_term(mdev);
+
+ mali_pm_metrics_term(mdev);
+
+ if (mdev->clock) {
+ clk_disable_unprepare(mdev->clock);
+ clk_put(mdev->clock);
+ mdev->clock = NULL;
+ }
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)) && defined(CONFIG_OF) \
+ && defined(CONFIG_PM_OPP)
+ dev_pm_opp_of_remove_table(mdev->dev);
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) && defined(CONFIG_OF) \
+ && defined(CONFIG_REGULATOR)
+ regulator_put(mdev->regulator);
+#endif /* LINUX_VERSION_CODE >= 3, 12, 0 */
+ mali_device_free(mdev);
+#endif
+
#ifdef CONFIG_MALI_DT
mali_platform_device_deinit(mali_platform_device);
#endif
static int mali_driver_suspend_scheduler(struct device *dev)
{
+#ifdef CONFIG_MALI_DEVFREQ
+ struct mali_device *mdev = dev_get_drvdata(dev);
+ if (!mdev)
+ return -ENODEV;
+#endif
+
+#if defined(CONFIG_MALI_DEVFREQ) && \
+ (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0))
+ devfreq_suspend_device(mdev->devfreq);
+#endif
+
mali_pm_os_suspend(MALI_TRUE);
/* Tracing the frequency and voltage after mali is suspended */
_mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_SINGLE |
static int mali_driver_resume_scheduler(struct device *dev)
{
+#ifdef CONFIG_MALI_DEVFREQ
+ struct mali_device *mdev = dev_get_drvdata(dev);
+ if (!mdev)
+ return -ENODEV;
+#endif
+
/* Tracing the frequency and voltage after mali is resumed */
#if defined(CONFIG_MALI400_PROFILING) && defined(CONFIG_MALI_DVFS)
/* Just call mali_get_current_gpu_clk_item() once,to record current clk info.*/
0, 0, 0);
#endif
mali_pm_os_resume();
+
+#if defined(CONFIG_MALI_DEVFREQ) && \
+ (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0))
+ devfreq_resume_device(mdev->devfreq);
+#endif
+
return 0;
}
#ifdef CONFIG_PM_RUNTIME
static int mali_driver_runtime_suspend(struct device *dev)
{
+#ifdef CONFIG_MALI_DEVFREQ
+ struct mali_device *mdev = dev_get_drvdata(dev);
+ if (!mdev)
+ return -ENODEV;
+#endif
+
if (MALI_TRUE == mali_pm_runtime_suspend()) {
/* Tracing the frequency and voltage after mali is suspended */
_mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_SINGLE |
0,
0, 0, 0);
+#if defined(CONFIG_MALI_DEVFREQ) && \
+ (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0))
+ MALI_DEBUG_PRINT(4, ("devfreq_suspend_device: stop devfreq monitor\n"));
+ devfreq_suspend_device(mdev->devfreq);
+#endif
+
return 0;
} else {
return -EBUSY;
static int mali_driver_runtime_resume(struct device *dev)
{
+#ifdef CONFIG_MALI_DEVFREQ
+ struct mali_device *mdev = dev_get_drvdata(dev);
+ if (!mdev)
+ return -ENODEV;
+#endif
+
/* Tracing the frequency and voltage after mali is resumed */
#if defined(CONFIG_MALI400_PROFILING) && defined(CONFIG_MALI_DVFS)
/* Just call mali_get_current_gpu_clk_item() once,to record current clk info.*/
#endif
mali_pm_runtime_resume();
+
+#if defined(CONFIG_MALI_DEVFREQ) && \
+ (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0))
+ MALI_DEBUG_PRINT(4, ("devfreq_resume_device: start devfreq monitor\n"));
+ devfreq_resume_device(mdev->devfreq);
+#endif
return 0;
}