mirror of
https://github.com/revyos/thead-kernel.git
synced 2026-06-21 09:12:26 +02:00
sync: npu: ax3386 sdk 1.4.2
Signed-off-by: Han Gao <gaohan@iscas.ac.cn>
This commit is contained in:
committed by
Han Gao/Revy/Rabenda
parent
bcb9ecc2c3
commit
7989ccb780
@@ -8,6 +8,7 @@ obj-$(CONFIG_VHA_INFO) += vha_info.o
|
||||
|
||||
# Common files
|
||||
vha-y := vha_api.o vha_common.o
|
||||
vha-y += vha_devfreq.o
|
||||
vha-y += vha_dbg.o vha_pdump.o
|
||||
|
||||
ifeq ($(CONFIG_HW_AX3_MC), y)
|
||||
|
||||
@@ -285,6 +285,12 @@ static int vha_plat_runtime_suspend(struct device *dev)
|
||||
container_of(dev, struct platform_device, dev);
|
||||
int ret = 0;
|
||||
|
||||
#ifdef CONFIG_PM_DEVFREQ
|
||||
ret = vha_devfreq_suspend(dev);
|
||||
if (ret)
|
||||
dev_err(dev, "%s: Failed to suspend the vha devfreq!\n", __func__);
|
||||
#endif
|
||||
|
||||
ret = vha_plat_dt_hw_suspend(ofdev);
|
||||
if (ret)
|
||||
dev_err(dev, "failed to suspend platform-specific hw!\n");
|
||||
@@ -302,6 +308,12 @@ static int vha_plat_runtime_resume(struct device *dev)
|
||||
if (ret)
|
||||
dev_err(dev, "failed to resume platform-specific hw!\n");
|
||||
|
||||
#ifdef CONFIG_PM_DEVFREQ
|
||||
ret = vha_devfreq_resume(dev);
|
||||
if (ret)
|
||||
dev_err(dev, "%s: Failed to resume the vha devfreq!\n", __func__);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -1532,6 +1532,37 @@ uint64_t vha_dbg_rtm_read(struct vha_dev *vha, uint64_t addr)
|
||||
return IOREAD64(vha->reg_base, VHA_CR_RTM_DATA);
|
||||
}
|
||||
|
||||
int vha_currcmd_exetime_req(struct vha_dev *vha, uint64_t *proc_us)
|
||||
{
|
||||
uint64_t proc_time = 0;
|
||||
struct vha_cmd *cmd = NULL;
|
||||
struct TIMESPEC to;
|
||||
|
||||
cmd = vha->pendcmd[VHA_CNN_CMD].cmd;
|
||||
if(!cmd || !cmd->in_hw) {
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
{
|
||||
struct TIMESPEC from = vha->stats.hw_proc_start;
|
||||
GETNSTIMEOFDAY(&to);
|
||||
|
||||
if (cmd->subsegs_completed == cmd->subseg_current) {
|
||||
*proc_us = 0;
|
||||
} else if (get_timespan_us(&from, &to, &proc_time)) {
|
||||
*proc_us = proc_time;
|
||||
} else {
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
*proc_us = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* List of predefined registers to be shown in debugfs */
|
||||
const struct vha_reg vha_regs[] = {
|
||||
#define REG_DESC(reg) VHA_CR_##reg, VHA_CR_##reg##_MASKFULL
|
||||
|
||||
@@ -1207,6 +1207,14 @@ int vha_add_dev(struct device *dev,
|
||||
__func__);
|
||||
goto out_alloc_common;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_DEVFREQ
|
||||
ret = vha_devfreq_init(vha->dev);
|
||||
if (ret) {
|
||||
dev_err(vha->dev, "failed to add vha dev to devfreq!\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
pm_runtime_put_sync_autosuspend(vha->dev);
|
||||
|
||||
/* Add device to driver context */
|
||||
@@ -1321,6 +1329,11 @@ void vha_rm_dev(struct device *dev)
|
||||
pm_runtime_put_sync_suspend(vha->dev);
|
||||
pm_runtime_dont_use_autosuspend(vha->dev);
|
||||
pm_runtime_disable(vha->dev);
|
||||
|
||||
#ifdef CONFIG_PM_DEVFREQ
|
||||
vha_devfreq_term(dev);
|
||||
#endif
|
||||
|
||||
vha_free_common(vha);
|
||||
#ifdef CONFIG_HW_MULTICORE
|
||||
vha_dev_scheduler_deinit(vha);
|
||||
@@ -2482,6 +2495,13 @@ int vha_suspend_dev(struct device *dev)
|
||||
struct vha_dev *vha = vha_dev_get_drvdata(dev);
|
||||
int ret;
|
||||
mutex_lock(&vha->lock);
|
||||
|
||||
#ifdef CONFIG_PM_DEVFREQ
|
||||
ret = vha_devfreq_suspend(dev);
|
||||
if (ret)
|
||||
dev_err(dev, "%s: Failed to suspend the vha devfreq!\n", __func__);
|
||||
#endif
|
||||
|
||||
dev_dbg(dev, "%s: taking a nap!\n", __func__);
|
||||
|
||||
ret = vha_dev_suspend_work(vha);
|
||||
@@ -2494,12 +2514,19 @@ int vha_suspend_dev(struct device *dev)
|
||||
int vha_resume_dev(struct device *dev)
|
||||
{
|
||||
struct vha_dev *vha = vha_dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&vha->lock);
|
||||
dev_dbg(dev, "%s: waking up!\n", __func__);
|
||||
/* Call the worker */
|
||||
vha_chk_cmd_queues(vha, true);
|
||||
|
||||
#ifdef CONFIG_PM_DEVFREQ
|
||||
ret = vha_devfreq_resume(dev);
|
||||
if (ret)
|
||||
dev_err(dev, "%s: Failed to resume the vha devfreq!\n", __func__);
|
||||
#endif
|
||||
|
||||
mutex_unlock(&vha->lock);
|
||||
|
||||
return 0;
|
||||
@@ -2536,6 +2563,20 @@ void vha_dump_digest(struct vha_session *session, struct vha_buffer *buf,
|
||||
}
|
||||
}
|
||||
|
||||
int vha_get_cnntotal_proc_us(struct device *dev, uint64_t *proc_us, uint64_t *cur_proc_us)
|
||||
{
|
||||
struct vha_dev *vha = vha_dev_get_drvdata(dev);
|
||||
if (!vha)
|
||||
return -EFAULT;
|
||||
|
||||
*proc_us = vha->stats.cnn_total_proc_us;
|
||||
|
||||
vha_currcmd_exetime_req(vha, cur_proc_us);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* register event observers.
|
||||
* only a SINGLE observer for each type of event.
|
||||
|
||||
@@ -572,6 +572,7 @@ struct vha_dev {
|
||||
void __iomem *reg_base;
|
||||
uint64_t reg_size;
|
||||
void *plat_data;
|
||||
void *devfreq_data;
|
||||
|
||||
struct miscdevice miscdev; /* UM interface */
|
||||
void *dbgfs_ctx;
|
||||
@@ -962,6 +963,21 @@ static inline void *vha_get_plat_data(struct device *dev)
|
||||
return vha->plat_data;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void *vha_devfreq_get_drvdata(struct device* dev)
|
||||
{
|
||||
struct vha_dev *vha = vha_dev_get_drvdata(dev);
|
||||
if (vha)
|
||||
return vha->devfreq_data;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void vha_dev_add_devfreq(struct device *dev, void *vha_devfreq)
|
||||
{
|
||||
struct vha_dev *vha = vha_dev_get_drvdata(dev);
|
||||
vha->devfreq_data = vha_devfreq;
|
||||
}
|
||||
|
||||
int vha_api_add_dev(struct device *dev, struct vha_dev *vha, unsigned int id);
|
||||
int vha_api_rm_dev(struct device *dev, struct vha_dev *vha);
|
||||
|
||||
@@ -1069,6 +1085,14 @@ void vha_pdump_ldb_buf(struct vha_session *session, uint32_t pdump_num,
|
||||
void vha_pdump_sab_buf(struct vha_session *session, uint32_t pdump_num,
|
||||
struct vha_buffer *buffer, uint32_t offset, uint32_t len);
|
||||
|
||||
/* devfreq support */
|
||||
int vha_devfreq_init(struct device *dev);
|
||||
void vha_devfreq_term(struct device *dev);
|
||||
int vha_devfreq_suspend(struct device *dev);
|
||||
int vha_devfreq_resume(struct device *dev);
|
||||
int vha_get_cnntotal_proc_us(struct device *dev, uint64_t *proc_us, uint64_t *cur_proc_us);
|
||||
int vha_currcmd_exetime_req(struct vha_dev *vha, uint64_t *proc_us);
|
||||
|
||||
/*
|
||||
* register event observers, notified when significant events occur
|
||||
* Only a single observer per event!
|
||||
|
||||
541
drivers/nna/vha/vha_devfreq.c
Normal file
541
drivers/nna/vha/vha_devfreq.c
Normal file
@@ -0,0 +1,541 @@
|
||||
/*!
|
||||
*****************************************************************************
|
||||
*
|
||||
* @File vha_devfreq.c
|
||||
* ---------------------------------------------------------------------------
|
||||
*
|
||||
* Copyright (C) 2020 Alibaba Group Holding Limited
|
||||
*
|
||||
*****************************************************************************/
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/devfreq.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/delay.h>
|
||||
#include "vha_common.h"
|
||||
#include <../drivers/devfreq/governor.h>
|
||||
|
||||
#ifdef CONFIG_DEVFREQ_THERMAL
|
||||
#include <linux/devfreq_cooling.h>
|
||||
#endif
|
||||
|
||||
/* Default constants for DevFreq-Simple-Ondemand (DFSO) */
|
||||
#define VHA_DEVFREQ_GOVERNOR_NAME "vha_ondemand"
|
||||
#define DFSO_UPTHRESHOLD (90)
|
||||
#define DFSO_DOWNDIFFERENCTIAL (5)
|
||||
#define LIGHT_NPUFREQ_PCLKNUM 3
|
||||
|
||||
struct governor_vhaondemand_date {
|
||||
unsigned int upthreshold;
|
||||
unsigned int downdifferential;
|
||||
};
|
||||
|
||||
struct vhadevfreq_load_data {
|
||||
struct TIMESPEC old_mark;
|
||||
struct TIMESPEC new_mark;
|
||||
uint64_t total_proc_us;
|
||||
};
|
||||
|
||||
struct vha_devfreq_device {
|
||||
struct devfreq *devfreq;
|
||||
struct device *dev;
|
||||
struct regulator *vdd;
|
||||
|
||||
struct governor_vhaondemand_date vhademand_date;
|
||||
struct vhadevfreq_load_data vha_load_data;
|
||||
unsigned long current_freq;
|
||||
|
||||
struct mutex lock;
|
||||
|
||||
#ifdef CONFIG_DEVFREQ_THERMAL
|
||||
struct thermal_cooling_device *devfreq_cooling;
|
||||
#endif
|
||||
};
|
||||
|
||||
enum LIGHT_NPUFREQ_PARENT_CLKS {
|
||||
NPU_CCLK,
|
||||
GMAC_PLL_FOUTPOSTDIV,
|
||||
NPU_CCLK_OUT_DIV,
|
||||
};
|
||||
|
||||
static int num_clks;
|
||||
static struct clk_bulk_data clks[] = {
|
||||
{ .id = "cclk" },
|
||||
{ .id = "gmac_pll_foutpostdiv" },
|
||||
{ .id = "npu_cclk_out_div" },
|
||||
};
|
||||
|
||||
static int vha_devfreq_opp_helper(struct dev_pm_set_opp_data *data)
|
||||
{
|
||||
struct device *dev = data->dev;
|
||||
struct clk *clk_vha = data->clk;
|
||||
unsigned long freq = data->new_opp.rate;
|
||||
unsigned long old_freq = data->old_opp.rate;
|
||||
unsigned long curr_freq;
|
||||
int ret = 0;
|
||||
|
||||
if (freq == old_freq) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = strcmp(__clk_get_name(clk_get_parent(clk_vha)),
|
||||
__clk_get_name(clks[NPU_CCLK_OUT_DIV].clk));
|
||||
|
||||
if (!ret && freq < clk_get_rate(clks[GMAC_PLL_FOUTPOSTDIV].clk))
|
||||
{
|
||||
clk_set_parent(clk_vha, clks[GMAC_PLL_FOUTPOSTDIV].clk);
|
||||
|
||||
ret = clk_set_rate(clks[NPU_CCLK_OUT_DIV].clk, freq);
|
||||
if (ret) {
|
||||
dev_err(dev, "%s: Failed to set NPU_CCLK_OUT_DIV freq: %d.\n",
|
||||
__func__, ret);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
udelay(1);
|
||||
|
||||
clk_set_parent(clk_vha, clks[NPU_CCLK_OUT_DIV].clk);
|
||||
|
||||
goto check_clk;
|
||||
}
|
||||
|
||||
ret = clk_set_rate(clk_vha, freq);
|
||||
if (ret) {
|
||||
dev_err(dev, "%s: Failed to set freq: %d.\n", __func__, ret);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
check_clk:
|
||||
curr_freq = clk_get_rate(clk_vha);
|
||||
if (curr_freq != freq) {
|
||||
dev_err(dev, "Get wrong frequency, Request %lu, Current %lu.\n",
|
||||
freq, curr_freq);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vhafreq_target(struct device *dev, unsigned long *freq,
|
||||
u32 flags)
|
||||
{
|
||||
struct vha_devfreq_device *vhafreq_dev = vha_devfreq_get_drvdata(dev);
|
||||
struct dev_pm_opp *opp;
|
||||
int ret = 0;
|
||||
|
||||
opp = devfreq_recommended_opp(dev, freq, flags);
|
||||
if (IS_ERR(opp)) {
|
||||
dev_err(dev, "Failed to find opp for %lu Hz.\n", *freq);
|
||||
return PTR_ERR(opp);
|
||||
}
|
||||
dev_pm_opp_put(opp);
|
||||
|
||||
mutex_lock(&vhafreq_dev->lock);
|
||||
|
||||
ret = dev_pm_opp_set_rate(dev, *freq);
|
||||
if (!ret) {
|
||||
if (vhafreq_dev->devfreq)
|
||||
vhafreq_dev->devfreq->last_status.current_frequency = *freq;
|
||||
} else {
|
||||
dev_err(dev, "Failed to set opp for %lu Hz.\n", *freq);
|
||||
}
|
||||
|
||||
mutex_unlock(&vhafreq_dev->lock);
|
||||
|
||||
dev_dbg(dev, "%s: set the target freq : %lu.\n", __func__, *freq);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vhafreq_get_dev_status(struct device *dev,
|
||||
struct devfreq_dev_status *stat)
|
||||
{
|
||||
struct vha_devfreq_device *vhafreq_dev = vha_devfreq_get_drvdata(dev);
|
||||
uint64_t busytime, proc_total_time, proc_cur_time;
|
||||
|
||||
mutex_lock(&vhafreq_dev->lock);
|
||||
|
||||
vha_get_cnntotal_proc_us(dev, &proc_total_time, &proc_cur_time);
|
||||
|
||||
/* Galculate the busy_time */
|
||||
busytime = proc_total_time + proc_cur_time;
|
||||
if (busytime < vhafreq_dev->vha_load_data.total_proc_us){
|
||||
busytime = 0;
|
||||
} else {
|
||||
busytime = busytime - vhafreq_dev->vha_load_data.total_proc_us;
|
||||
}
|
||||
|
||||
vhafreq_dev->vha_load_data.total_proc_us = proc_total_time + proc_cur_time;
|
||||
|
||||
/* Galculate the total_time */
|
||||
GETNSTIMEOFDAY(&vhafreq_dev->vha_load_data.new_mark);
|
||||
if (!get_timespan_us(&vhafreq_dev->vha_load_data.old_mark,
|
||||
&vhafreq_dev->vha_load_data.new_mark, &proc_cur_time))
|
||||
return -EINVAL;
|
||||
|
||||
vhafreq_dev->vha_load_data.old_mark = vhafreq_dev->vha_load_data.new_mark;
|
||||
|
||||
/* correct the busytime */
|
||||
if (busytime > proc_cur_time) {
|
||||
dev_dbg(dev,"busytime :%lu bigger, totaltime :%lu .\n", busytime, proc_cur_time);
|
||||
busytime = proc_cur_time;
|
||||
} else if (busytime < 0) {
|
||||
busytime = 0;
|
||||
}
|
||||
|
||||
stat->busy_time = busytime;
|
||||
stat->total_time = proc_cur_time;
|
||||
|
||||
mutex_unlock(&vhafreq_dev->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vhafreq_get_cur_freq(struct device *dev, unsigned long *freq)
|
||||
{
|
||||
unsigned long current_freq;
|
||||
|
||||
current_freq = clk_get_rate(clks[NPU_CCLK].clk);
|
||||
*freq = current_freq;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct devfreq_dev_profile devfreq_vha_profile = {
|
||||
.polling_ms = 5,
|
||||
.target = vhafreq_target,
|
||||
.get_dev_status = vhafreq_get_dev_status,
|
||||
.get_cur_freq = vhafreq_get_cur_freq,
|
||||
};
|
||||
|
||||
static int devfreq_vha_ondemand_func(struct devfreq *df, unsigned long *freq)
|
||||
{
|
||||
int err;
|
||||
struct devfreq_dev_status *stat;
|
||||
unsigned long long a, b;
|
||||
struct governor_vhaondemand_date *data = df->data;
|
||||
unsigned int dfso_upthreshold = DFSO_UPTHRESHOLD;
|
||||
unsigned int dfso_downdifferential = DFSO_DOWNDIFFERENCTIAL;
|
||||
|
||||
if (data) {
|
||||
if (data->upthreshold)
|
||||
dfso_upthreshold = data->upthreshold;
|
||||
if (data->downdifferential)
|
||||
dfso_downdifferential = data->downdifferential;
|
||||
}
|
||||
|
||||
if (dfso_upthreshold > 100 ||
|
||||
dfso_upthreshold < dfso_downdifferential)
|
||||
return -EINVAL;
|
||||
|
||||
err = devfreq_update_stats(df);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
stat = &df->last_status;
|
||||
|
||||
/* Assume MAX if it is going to be divided by zero */
|
||||
if (stat->total_time == 0) {
|
||||
*freq = DEVFREQ_MAX_FREQ;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Prevent overflow */
|
||||
if (stat->busy_time >= (1 << 24) || stat->total_time >= (1 << 24)) {
|
||||
stat->busy_time >>= 7;
|
||||
stat->total_time >>= 7;
|
||||
}
|
||||
|
||||
/* Set MAX if it's busy enough */
|
||||
if (stat->busy_time * 100 >
|
||||
stat->total_time * dfso_upthreshold) {
|
||||
*freq = DEVFREQ_MAX_FREQ;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set MAX if we do not know the initial frequency */
|
||||
if (stat->current_frequency == 0) {
|
||||
*freq = DEVFREQ_MAX_FREQ;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Keep the current frequency */
|
||||
if (stat->busy_time * 100 >
|
||||
stat->total_time * (dfso_upthreshold - dfso_downdifferential)) {
|
||||
*freq = stat->current_frequency;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set the desired frequency based on the load */
|
||||
a = stat->busy_time;
|
||||
a *= stat->current_frequency;
|
||||
b = div_u64(a, stat->total_time);
|
||||
b *= 100;
|
||||
b = div_u64(b, (dfso_upthreshold - dfso_downdifferential / 2));
|
||||
*freq = (unsigned long) b;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void devfreq_status_update(struct device *dev) {
|
||||
struct vha_devfreq_device *vhafreq_dev = vha_devfreq_get_drvdata(dev);
|
||||
struct vhadevfreq_load_data *load_date = &vhafreq_dev->vha_load_data;
|
||||
uint64_t cur_proc_us, cnntotal_time;
|
||||
|
||||
GETNSTIMEOFDAY(&load_date->new_mark);
|
||||
load_date->old_mark = load_date->new_mark;
|
||||
|
||||
vha_get_cnntotal_proc_us(dev, &cnntotal_time, &cur_proc_us);
|
||||
|
||||
load_date->total_proc_us = cnntotal_time + cur_proc_us;
|
||||
}
|
||||
|
||||
static int devfreq_vha_ondemand_handler(struct devfreq *devfreq,
|
||||
unsigned int event, void *data)
|
||||
{
|
||||
struct device *dev = devfreq->dev.parent;
|
||||
|
||||
switch (event) {
|
||||
case DEVFREQ_GOV_START:
|
||||
devfreq_status_update(dev);
|
||||
devfreq_monitor_start(devfreq);
|
||||
break;
|
||||
|
||||
case DEVFREQ_GOV_STOP:
|
||||
devfreq_monitor_stop(devfreq);
|
||||
break;
|
||||
|
||||
case DEVFREQ_GOV_UPDATE_INTERVAL:
|
||||
devfreq_update_interval(devfreq, (unsigned int *)data);
|
||||
break;
|
||||
|
||||
case DEVFREQ_GOV_SUSPEND:
|
||||
devfreq_monitor_suspend(devfreq);
|
||||
break;
|
||||
|
||||
case DEVFREQ_GOV_RESUME:
|
||||
devfreq_status_update(dev);
|
||||
devfreq_monitor_resume(devfreq);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct devfreq_governor devfreq_vha_ondemand = {
|
||||
.name = "vha_ondemand",
|
||||
.get_target_freq = devfreq_vha_ondemand_func,
|
||||
.event_handler = devfreq_vha_ondemand_handler,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DEVFREQ_THERMAL
|
||||
static struct devfreq_cooling_power vha_cooling_power = {
|
||||
.get_static_power = NULL,
|
||||
.dyn_power_coeff = 1000,
|
||||
};
|
||||
#endif
|
||||
|
||||
static int vha_devfreq_opp_init(struct device *dev)
|
||||
{
|
||||
struct opp_table *opp_table = NULL, *reg_opp_table = NULL, *clk_opp_table = NULL;
|
||||
const char * const reg_names[] = {"soc_dvdd08_ap"};
|
||||
int ret;
|
||||
|
||||
clk_opp_table = dev_pm_opp_set_clkname(dev, "cclk");
|
||||
if (IS_ERR(clk_opp_table)) {
|
||||
dev_err(dev, "Failed to set opp clkname.\n");
|
||||
return PTR_ERR(clk_opp_table);
|
||||
}
|
||||
|
||||
reg_opp_table = dev_pm_opp_set_regulators(dev, reg_names, 1);
|
||||
if (IS_ERR(reg_opp_table)) {
|
||||
dev_err(dev, "Failed to set regulators.\n");
|
||||
ret = PTR_ERR(reg_opp_table);
|
||||
goto clk_opp_table_put;
|
||||
}
|
||||
|
||||
opp_table = dev_pm_opp_register_set_opp_helper(dev, vha_devfreq_opp_helper);
|
||||
if (IS_ERR(opp_table)) {
|
||||
dev_err(dev, "Failed to set vha opp helper.\n");
|
||||
ret = PTR_ERR(opp_table);
|
||||
goto reg_opp_table_put;
|
||||
}
|
||||
|
||||
ret = dev_pm_opp_of_add_table(dev);
|
||||
if(ret){
|
||||
dev_err(dev, "Failed to add vha opp table.\n");
|
||||
goto opp_helper_unregist;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
opp_helper_unregist:
|
||||
if(opp_table)
|
||||
dev_pm_opp_unregister_set_opp_helper(opp_table);
|
||||
reg_opp_table_put:
|
||||
if(reg_opp_table)
|
||||
dev_pm_opp_put_regulators(reg_opp_table);
|
||||
clk_opp_table_put:
|
||||
if(clk_opp_table)
|
||||
dev_pm_opp_put_clkname(clk_opp_table);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int vha_devfreq_init(struct device *dev)
|
||||
{
|
||||
struct vha_devfreq_device *devfreq_vhadev;
|
||||
struct devfreq_dev_profile *dp = &devfreq_vha_profile;
|
||||
int ret = 0;
|
||||
|
||||
devfreq_vhadev = devm_kzalloc(dev, sizeof(struct vha_devfreq_device), GFP_KERNEL);
|
||||
if (!devfreq_vhadev)
|
||||
return -ENOMEM;
|
||||
|
||||
devfreq_vhadev->dev = dev;
|
||||
mutex_init(&devfreq_vhadev->lock);
|
||||
vha_dev_add_devfreq(dev, devfreq_vhadev);
|
||||
|
||||
ret = devfreq_add_governor(&devfreq_vha_ondemand);
|
||||
if (ret) {
|
||||
dev_err(dev, "%s: Failed to add vha_ondemand governor.\n", __func__);
|
||||
goto free_freqdev_dev;
|
||||
}
|
||||
|
||||
num_clks = LIGHT_NPUFREQ_PCLKNUM;
|
||||
ret = clk_bulk_get(dev, num_clks, clks);
|
||||
if (ret) {
|
||||
dev_err(dev, "%s: Failed to register clk_bulk_get.\n", __func__);
|
||||
goto free_freqdev_dev;
|
||||
}
|
||||
|
||||
dp->initial_freq = clk_get_rate(clks[NPU_CCLK].clk);
|
||||
|
||||
devfreq_vhadev->vdd = devm_regulator_get(dev, "soc_dvdd08_ap");
|
||||
if (IS_ERR_OR_NULL(devfreq_vhadev->vdd)) {
|
||||
dev_err(dev, "%s: Failed to devm_regulator_get\n", __func__);
|
||||
ret = PTR_ERR(devfreq_vhadev->vdd);
|
||||
devfreq_vhadev->vdd = NULL;
|
||||
goto vha_clks_put;
|
||||
}
|
||||
|
||||
ret = vha_devfreq_opp_init(dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "%s: Failed to vha_devfreq_opp_init.\n", __func__);
|
||||
goto vha_regulator_put;
|
||||
}
|
||||
|
||||
devfreq_vhadev->devfreq = devm_devfreq_add_device(dev, dp,
|
||||
VHA_DEVFREQ_GOVERNOR_NAME,
|
||||
&devfreq_vhadev->vhademand_date);
|
||||
if (IS_ERR_OR_NULL(devfreq_vhadev->devfreq)) {
|
||||
dev_err(dev, "%s: Failed to register vha to devfreq.\n", __func__);
|
||||
ret = PTR_ERR(devfreq_vhadev->devfreq);
|
||||
goto free_opp_table;
|
||||
}
|
||||
|
||||
ret = devm_devfreq_register_opp_notifier(dev, devfreq_vhadev->devfreq);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s: Failed to register vha to opp notifier.\n", __func__);
|
||||
goto opp_notifier_failed;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEVFREQ_THERMAL
|
||||
if (of_property_read_u32(dev->of_node, "dynamic-power-coefficient",
|
||||
(u32 *)&vha_cooling_power.dyn_power_coeff))
|
||||
pr_err("Failed to read dynamic power coefficient property.\n");
|
||||
|
||||
devfreq_vhadev->devfreq_cooling = of_devfreq_cooling_register_power(
|
||||
dev->of_node, devfreq_vhadev->devfreq, &vha_cooling_power);
|
||||
if (IS_ERR_OR_NULL(devfreq_vhadev->devfreq_cooling)){
|
||||
dev_err(dev, "%s: Failed to register vha to devfreq_cooling.\n", __func__);
|
||||
goto cooling_failed;
|
||||
}
|
||||
#endif
|
||||
|
||||
dev_info(dev, "%s: Success to register the NPU to DevFreq.\n", __func__);
|
||||
|
||||
return 0;
|
||||
|
||||
#ifdef CONFIG_DEVFREQ_THERMAL
|
||||
cooling_failed:
|
||||
devfreq_unregister_opp_notifier(dev, devfreq_vhadev->devfreq);
|
||||
#endif
|
||||
opp_notifier_failed:
|
||||
devm_devfreq_remove_device(dev, devfreq_vhadev->devfreq);
|
||||
free_opp_table:
|
||||
dev_pm_opp_of_remove_table(dev);
|
||||
vha_regulator_put:
|
||||
devm_regulator_put(devfreq_vhadev->vdd);
|
||||
vha_clks_put:
|
||||
clk_bulk_put(num_clks, clks);
|
||||
free_freqdev_dev:
|
||||
devm_kfree(dev, devfreq_vhadev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void vha_devfreq_term(struct device *dev)
|
||||
{
|
||||
struct vha_devfreq_device *devfreq_vhadev = vha_devfreq_get_drvdata(dev);
|
||||
|
||||
if (devfreq_vhadev){
|
||||
#ifdef CONFIG_DEVFREQ_THERMAL
|
||||
if (devfreq_vhadev->devfreq_cooling){
|
||||
devfreq_cooling_unregister(devfreq_vhadev->devfreq_cooling);
|
||||
}
|
||||
#endif
|
||||
if (devfreq_vhadev->devfreq) {
|
||||
devfreq_unregister_opp_notifier(dev, devfreq_vhadev->devfreq);
|
||||
|
||||
devm_devfreq_remove_device(dev, devfreq_vhadev->devfreq);
|
||||
|
||||
dev_pm_opp_of_remove_table(devfreq_vhadev->dev);
|
||||
|
||||
devfreq_remove_governor(&devfreq_vha_ondemand);
|
||||
}
|
||||
|
||||
if (devfreq_vhadev->vdd)
|
||||
devm_regulator_put(devfreq_vhadev->vdd);
|
||||
|
||||
clk_bulk_put(num_clks, clks);
|
||||
|
||||
devm_kfree(dev, devfreq_vhadev);
|
||||
}
|
||||
}
|
||||
|
||||
int vha_devfreq_suspend(struct device *dev)
|
||||
{
|
||||
struct vha_devfreq_device *devfreq_vhadev = vha_devfreq_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (devfreq_vhadev){
|
||||
ret = devfreq_suspend_device(devfreq_vhadev->devfreq);
|
||||
if (ret < 0){
|
||||
dev_err(dev, "%s: Failed to suspend the vha_devfreq.\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int vha_devfreq_resume(struct device *dev)
|
||||
{
|
||||
struct vha_devfreq_device *devfreq_vhadev = vha_devfreq_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (devfreq_vhadev){
|
||||
ret = devfreq_resume_device(devfreq_vhadev->devfreq);
|
||||
if (ret < 0){
|
||||
dev_err(dev, "%s: Failed to resume the vha_devfreq.\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
Reference in New Issue
Block a user