MALI: bifrost: Avoid crash when set rate and read margin

1. change clock rate and read margin only when pd is on.
2. change clock source to normal pll before power down pd.
3. change clock source back to pvtpll and restore clock rate
and read margin after power on pd.

Signed-off-by: Finley Xiao <finley.xiao@rock-chips.com>
Change-Id: I2028866fdd70909814aaba7ef6d3af3bf764eb87
This commit is contained in:
Finley Xiao
2021-12-30 15:55:43 +08:00
committed by Tao Huang
parent f8e4af5776
commit 5b7d19ca43
3 changed files with 72 additions and 12 deletions

View File

@@ -28,6 +28,7 @@
#include <linux/of.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/devfreq.h>
#if IS_ENABLED(CONFIG_DEVFREQ_THERMAL)
#include <linux/devfreq_cooling.h>
@@ -35,6 +36,7 @@
#include <linux/version.h>
#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
#include "mali_kbase_devfreq.h"
#include <soc/rockchip/rockchip_ipa.h>
@@ -125,6 +127,20 @@ void kbase_devfreq_opp_translate(struct kbase_device *kbdev, unsigned long freq,
}
}
static int kbase_devfreq_set_read_margin(struct device *dev,
struct rockchip_opp_info *opp_info,
unsigned long volt,
bool is_set_rm)
{
if (opp_info->data && opp_info->data->set_read_margin) {
if (is_set_rm)
opp_info->data->set_read_margin(dev, opp_info, volt);
opp_info->volt_rm = volt;
}
return 0;
}
int kbase_devfreq_opp_helper(struct dev_pm_set_opp_data *data)
{
struct device *dev = data->dev;
@@ -139,8 +155,16 @@ int kbase_devfreq_opp_helper(struct dev_pm_set_opp_data *data)
struct rockchip_opp_info *opp_info = &kbdev->opp_info;
unsigned long old_freq = data->old_opp.rate;
unsigned long new_freq = data->new_opp.rate;
bool is_set_rm = true;
bool is_set_clk = true;
int ret = 0;
if (!pm_runtime_active(dev)) {
is_set_rm = false;
if (kbdev->scmi_clk)
is_set_clk = false;
}
ret = clk_bulk_prepare_enable(opp_info->num_clks, opp_info->clks);
if (ret) {
dev_err(dev, "failed to enable opp clks\n");
@@ -163,24 +187,23 @@ int kbase_devfreq_opp_helper(struct dev_pm_set_opp_data *data)
new_supply_vdd->u_volt);
goto restore_voltage;
}
if (opp_info->data->set_read_margin)
opp_info->data->set_read_margin(dev, opp_info,
new_supply_vdd->u_volt);
kbase_devfreq_set_read_margin(dev, opp_info,
new_supply_vdd->u_volt,
is_set_rm);
}
/* Change frequency */
dev_dbg(dev, "switching OPP: %lu Hz --> %lu Hz\n", old_freq, new_freq);
ret = clk_set_rate(clk, new_freq);
if (ret) {
if (is_set_clk && clk_set_rate(clk, new_freq)) {
dev_err(dev, "failed to set clk rate: %d\n", ret);
goto restore_rm;
}
/* Scaling down? Scale voltage after frequency */
if (new_freq < old_freq) {
if (opp_info->data->set_read_margin)
opp_info->data->set_read_margin(dev, opp_info,
new_supply_vdd->u_volt);
kbase_devfreq_set_read_margin(dev, opp_info,
new_supply_vdd->u_volt,
is_set_rm);
ret = regulator_set_voltage(vdd_reg, new_supply_vdd->u_volt,
INT_MAX);
if (ret) {
@@ -202,12 +225,11 @@ int kbase_devfreq_opp_helper(struct dev_pm_set_opp_data *data)
return 0;
restore_freq:
if (clk_set_rate(clk, old_freq))
if (is_set_clk && clk_set_rate(clk, old_freq))
dev_err(dev, "failed to restore old-freq %lu Hz\n", old_freq);
restore_rm:
if (opp_info->data->set_read_margin)
opp_info->data->set_read_margin(dev, opp_info,
old_supply_vdd->u_volt);
kbase_devfreq_set_read_margin(dev, opp_info, old_supply_vdd->u_volt,
is_set_rm);
restore_voltage:
regulator_set_voltage(mem_reg, old_supply_mem->u_volt, INT_MAX);
regulator_set_voltage(vdd_reg, old_supply_vdd->u_volt, INT_MAX);
@@ -673,6 +695,8 @@ int kbase_devfreq_init(struct kbase_device *kbdev)
else
kbdev->current_freqs[i] = 0;
}
if (strstr(__clk_get_name(kbdev->clocks[0]), "scmi"))
kbdev->scmi_clk = kbdev->clocks[0];
kbdev->current_nominal_freq = kbdev->current_freqs[0];
opp = devfreq_recommended_opp(kbdev->dev, &kbdev->current_nominal_freq, 0);

View File

@@ -670,6 +670,7 @@ struct kbase_process {
* @irqs.flags: irq flags
* @clocks: Pointer to the input clock resources referenced by
* the GPU device node.
* @scmi_clk: Pointer to the input scmi clock resources
* @nr_clocks: Number of clocks set in the clocks array.
* @regulators: Pointer to the structs corresponding to the
* regulators referenced by the GPU device node.
@@ -971,6 +972,7 @@ struct kbase_device {
} irqs[3];
struct clk *clocks[BASE_MAX_NR_CLOCKS_REGULATORS];
struct clk *scmi_clk;
unsigned int nr_clocks;
#if IS_ENABLED(CONFIG_REGULATOR)
struct regulator *regulators[BASE_MAX_NR_CLOCKS_REGULATORS];

View File

@@ -32,9 +32,12 @@
#include <linux/soc/rockchip/pvtm.h>
#include <linux/thermal.h>
#include <soc/rockchip/rockchip_opp_select.h>
#include <soc/rockchip/rockchip_system_monitor.h>
#include "mali_kbase_rk.h"
#define POWER_DOWN_FREQ 200000000
/**
* @file mali_kbase_config_rk.c
* platform_config_of_rk .
@@ -92,10 +95,12 @@ static void rk_pm_power_off_delay_work(struct work_struct *work)
return;
}
rockchip_monitor_volt_adjust_lock(kbdev->mdev_info);
if (pm_runtime_enabled(kbdev->dev)) {
D("to put_sync_suspend mali_dev.");
pm_runtime_put_sync_suspend(kbdev->dev);
}
rockchip_monitor_volt_adjust_unlock(kbdev->mdev_info);
rk_pm_disable_clk(kbdev);
@@ -186,11 +191,38 @@ struct kbase_platform_funcs_conf platform_funcs = {
static int rk_pm_callback_runtime_on(struct kbase_device *kbdev)
{
struct rockchip_opp_info *opp_info = &kbdev->opp_info;
int ret = 0;
if (!kbdev->current_nominal_freq)
return 0;
ret = clk_bulk_prepare_enable(opp_info->num_clks, opp_info->clks);
if (ret) {
dev_err(kbdev->dev, "failed to enable opp clks\n");
return ret;
}
if (kbdev->scmi_clk) {
if (clk_set_rate(kbdev->scmi_clk, kbdev->current_nominal_freq))
dev_err(kbdev->dev, "failed to restore clk rate\n");
}
if (opp_info->data && opp_info->data->set_read_margin)
opp_info->data->set_read_margin(kbdev->dev, opp_info,
opp_info->volt_rm);
clk_bulk_disable_unprepare(opp_info->num_clks, opp_info->clks);
return 0;
}
static void rk_pm_callback_runtime_off(struct kbase_device *kbdev)
{
struct rockchip_opp_info *opp_info = &kbdev->opp_info;
if (kbdev->scmi_clk) {
if (clk_set_rate(kbdev->scmi_clk, POWER_DOWN_FREQ))
dev_err(kbdev->dev, "failed to set power down rate\n");
}
opp_info->current_rm = UINT_MAX;
}
static int rk_pm_callback_power_on(struct kbase_device *kbdev)
@@ -224,6 +256,7 @@ static int rk_pm_callback_power_on(struct kbase_device *kbdev)
goto out;
}
rockchip_monitor_volt_adjust_lock(kbdev->mdev_info);
/* 若 mali_dev 的 runtime_pm 是 enabled 的, 则... */
if (pm_runtime_enabled(kbdev->dev)) {
D("to resume mali_dev syncly.");
@@ -240,6 +273,7 @@ static int rk_pm_callback_power_on(struct kbase_device *kbdev)
ret = 0;
}
}
rockchip_monitor_volt_adjust_unlock(kbdev->mdev_info);
platform->is_powered = true;
wake_lock(&platform->wake_lock);