blob: ee187185bf002f45107166b54b694c35b1a0e68d [file] [log] [blame]
/*
* mali_kbase_runtime_pm.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
//#define DEBUG
#include <mali_kbase.h>
#include <mali_kbase_defs.h>
#include <linux/pm_runtime.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/regulator/consumer.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <backend/gpu/mali_kbase_device_internal.h>
#include "mali_kbase_config_platform.h"
#include "mali_scaling.h"
void *reg_base_reset = NULL;
static int first = 1;
//remove this if sc2 worked fine.
//g12a
#define RESET0_MASK 0x10
#define RESET1_MASK 0x11
#define RESET2_MASK 0x12
#define RESET0_LEVEL 0x20
#define RESET1_LEVEL 0x21
#define RESET2_LEVEL 0x22
//sc2
#define RESETCTRL_RESET1_LEVEL 0x11
#define RESETCTRL_RESET1_MASK 0x21
#define Rd(r) readl((reg_base_reset) + ((r)<<2))
#define Wr(r, v) writel((v), ((reg_base_reset) + ((r)<<2)))
#define Mali_WrReg(regnum, value) writel((value), kbdev->reg + (regnum))
#define Mali_RdReg(regnum) readl(kbdev->reg + (regnum))
extern u64 kbase_pm_get_ready_cores(struct kbase_device *kbdev, enum kbase_pm_core_type type);
//[0]:CG [1]:SC0 [2]:SC2
static void Mali_pwr_on_with_kdev( struct kbase_device *kbdev, uint32_t mask)
{
uint32_t part1_done;
uint32_t shader_present;
uint32_t tiler_present;
uint32_t l2_present;
part1_done = 0;
Mali_WrReg(0x0000024, 0xffffffff); // clear interrupts
shader_present = Mali_RdReg(0x100);
tiler_present = Mali_RdReg(0x110);
l2_present = Mali_RdReg(0x120);
dev_info(kbdev->dev, "shader_present=%d, tiler_present=%d, l2_present=%d\n",
shader_present, tiler_present, l2_present);
if ( mask == 0 ) {
Mali_WrReg(0x00000180, 0xffffffff); // Power on all cores (shader low)
Mali_WrReg(0x00000184, 0xffffffff); // Power on all cores (shader high)
Mali_WrReg(0x00000190, 0xffffffff); // Power on all cores (tiler low)
Mali_WrReg(0x00000194, 0xffffffff); // Power on all cores (tiler high)
Mali_WrReg(0x000001a0, 0xffffffff); // Power on all cores (l2 low)
Mali_WrReg(0x000001a4, 0xffffffff); // Power on all cores (l2 high)
} else {
Mali_WrReg(0x00000180, mask); // Power on all cores (shader low)
Mali_WrReg(0x00000184, 0); // Power on all cores (shader high)
Mali_WrReg(0x00000190, mask); // Power on all cores (tiler low)
Mali_WrReg(0x00000194, 0); // Power on all cores (tiler high)
Mali_WrReg(0x000001a0, mask); // Power on all cores (l2 low)
Mali_WrReg(0x000001a4, 0); // Power on all cores (l2 high)
}
part1_done = Mali_RdReg(0x0000020);
while (0 == part1_done) { part1_done = Mali_RdReg(0x00000020); }
Mali_WrReg(0x0000024, 0xffffffff); // clear interrupts
}
/*reset function before t7 by register*/
static void mali_reset_v0(int reset_g12a)
{
u32 value;
if (reset_g12a) {
value = Rd(RESET0_MASK);
value = value & (~(0x1<<20));
Wr(RESET0_MASK, value);
value = Rd(RESET0_LEVEL);
value = value & (~(0x1<<20));
Wr(RESET0_LEVEL, value);
value = Rd(RESET2_MASK);
value = value & (~(0x1<<14));
Wr(RESET2_MASK, value);
value = Rd(RESET2_LEVEL);
value = value & (~(0x1<<14));
Wr(RESET2_LEVEL, value);
value = Rd(RESET0_LEVEL);
value = value | ((0x1<<20));
Wr(RESET0_LEVEL, value);
value = Rd(RESET2_LEVEL);
value = value | ((0x1<<14));
Wr(RESET2_LEVEL, value);
} else {
/* JOHNT: remove this if sc2 worked fine. */
value = ~(1 << 2);
Wr(RESETCTRL_RESET1_MASK, value);
Wr(RESETCTRL_RESET1_LEVEL, value);
Wr(RESETCTRL_RESET1_LEVEL, 0xFFFFFFFF);
}
}
/*
* reset note:
* there have mali reset and mali_capb bus reset.
* reset step:[vlsi suggestion]
* step1. mali-reset and mali-capb-reset level and mask config to 0
* step2. mali-reset and mali-capb-reset level config to 1
* Todo: sync old ic similar with t7,reset reg config by dts
*/
static void mali_reset(void)
{
mali_plat_info_t* pmali_plat = get_mali_plat_data();
u32 value = 0;
int reset_g12a = pmali_plat->reset_g12a;
if (!pmali_plat->reset_flag) {
mali_reset_v0(reset_g12a);
return;
}
/*
* for t7 , if old ic also sync with the follow method,
* need sync the dts similar with T7
*/
value = Rd(pmali_plat->module_reset.reg_mask);
value = value & (~(0x1<<pmali_plat->module_reset.reg_bit));
Wr(pmali_plat->module_reset.reg_mask, value);
value = Rd(pmali_plat->module_reset.reg_level);
value = value & (~(0x1<<pmali_plat->module_reset.reg_bit));
Wr(pmali_plat->module_reset.reg_level, value);
value = Rd(pmali_plat->apb_reset.reg_mask);
value = value & (~(0x1<<pmali_plat->apb_reset.reg_bit));
Wr(pmali_plat->apb_reset.reg_mask, value);
value = Rd(pmali_plat->apb_reset.reg_level);
value = value & (~(0x1<<pmali_plat->apb_reset.reg_bit));
Wr(pmali_plat->apb_reset.reg_level, value);
value = Rd(pmali_plat->apb_reset.reg_level);
value = value | ((0x1<<pmali_plat->apb_reset.reg_bit));
Wr(pmali_plat->apb_reset.reg_level, value);
value = Rd(pmali_plat->module_reset.reg_level);
value = value | ((0x1<<pmali_plat->module_reset.reg_bit));
Wr(pmali_plat->module_reset.reg_level, value);
}
/*
* 1.mali-reset;2.init pwr_override1;3.pwr_on one core manully
*/
static void mali_hw_init(struct kbase_device *kbdev)
{
mali_reset();
udelay(10); // OR POLL for reset done
Mali_WrReg(GPU_CONTROL_REG(PWR_KEY), 0x2968A819);
Mali_WrReg(GPU_CONTROL_REG(PWR_OVERRIDE1), 0xfff | (0x20<<16));
Mali_pwr_on_with_kdev(kbdev, 0x1);
}
static int pm_callback_power_on(struct kbase_device *kbdev)
{
int ret = 0;
struct mali_plat_info_t *mpdata = (struct mali_plat_info_t *) kbdev->platform_context;
reg_base_reset = mpdata->reg_base_reset;
if (first == 0) goto out;
if (!pm_runtime_enabled(kbdev->dev)) {
pm_runtime_enable(kbdev->dev);
dev_info(kbdev->dev, "pm_runtime not enabled, enable it here\n");
ret = pm_runtime_get_sync(kbdev->dev);
udelay(100);
dev_info(kbdev->dev, "pm_runtime_get_sync returned %d\n", ret);
} else {
dev_info(kbdev->dev, "pm_runtime enabled\n");
}
first = 0;
mali_hw_init(kbdev);
out:
return ret;
}
/*
* pm_callback_power_off will be called when gpu have no job todo.
* the pm_runtime_put_autosuspend will trigger gpu power reset
* which will affect gpu work on T7,so we can't do put here
* the out power of gpu on t7 will be power off by platform when suspend
*/
static void pm_callback_power_off(struct kbase_device *kbdev)
{
dev_dbg(kbdev->dev, "pm_callback_power_off\n");
}
#ifdef KBASE_PM_RUNTIME
static int kbase_device_runtime_init(struct kbase_device *kbdev)
{
int ret = 0;
dev_info(kbdev->dev, "kbase_device_runtime_init\n");
pm_runtime_enable(kbdev->dev);
ret = pm_runtime_get_sync(kbdev->dev);
dev_info(kbdev->dev, "pm_runtime_get_sync ret=%d\n", ret);
return ret;
}
static void kbase_device_runtime_disable(struct kbase_device *kbdev)
{
dev_info(kbdev->dev, "kbase_device_runtime_disable\n");
pm_runtime_disable(kbdev->dev);
}
#endif
static int pm_callback_runtime_on(struct kbase_device *kbdev)
{
dev_info(kbdev->dev, "pm_callback_runtime_on\n");
return 0;
}
static void pm_callback_runtime_off(struct kbase_device *kbdev)
{
dev_info(kbdev->dev, "pm_callback_runtime_off\n");
}
/*
* the out power of gpu on t7[vdd_gpu] will be power off by platform when suspend,
* which will result in the gpu internal register reset to default.
* all of the ic before t7 use the vdd_ee,which will never power down even suspend,
* so we need do once init and check by pwr_override1.
*/
static void pm_callback_resume(struct kbase_device *kbdev)
{
int ret;
u32 pwr_override1;
dev_info(kbdev->dev, "pm_callback_resume in\n");
if (!pm_runtime_enabled(kbdev->dev)) {
pm_runtime_enable(kbdev->dev);
dev_info(kbdev->dev, "pm_runtime not enable, enable here\n");
} else {
dev_info(kbdev->dev, "pm_runtime enabled already\n");
}
ret = pm_runtime_get_sync(kbdev->dev);
dev_info(kbdev->dev, "pm_runtime_get_sync ret=%d\n", ret);
Mali_WrReg(GPU_CONTROL_REG(PWR_KEY), 0x2968A819);
pwr_override1 = Mali_RdReg(GPU_CONTROL_REG(PWR_OVERRIDE1));
if (!pwr_override1) {
dev_info(kbdev->dev, "pwr_override1=0,need do once init\n");
mali_hw_init(kbdev);
}
ret = pm_callback_runtime_on(kbdev);
dev_info(kbdev->dev, "pm_callback_resume out\n");
}
/* the out power of gpu on t7 will be power off by platform when suspend */
static void pm_callback_suspend(struct kbase_device *kbdev)
{
dev_info(kbdev->dev, "pm_callback_suspend in\n");
pm_callback_runtime_off(kbdev);
pm_runtime_put_sync(kbdev->dev);
pm_runtime_disable(kbdev->dev);
dev_info(kbdev->dev, "pm_callback_suspend out\n");
}
struct kbase_pm_callback_conf pm_callbacks = {
.power_on_callback = pm_callback_power_on,
.power_off_callback = pm_callback_power_off,
.power_suspend_callback = pm_callback_suspend,
.power_resume_callback = pm_callback_resume,
#ifdef KBASE_PM_RUNTIME
.power_runtime_init_callback = kbase_device_runtime_init,
.power_runtime_term_callback = kbase_device_runtime_disable,
.power_runtime_on_callback = pm_callback_runtime_on,
.power_runtime_off_callback = pm_callback_runtime_off,
#else /* KBASE_PM_RUNTIME */
.power_runtime_init_callback = NULL,
.power_runtime_term_callback = NULL,
.power_runtime_on_callback = NULL,
.power_runtime_off_callback = NULL,
#endif /* KBASE_PM_RUNTIME */
};