soc: qcom: Add support for Core Power Reduction v3, v4 and Hardened
This commit introduces a new driver, based on the one for cpr v1, to enable support for the newer Qualcomm Core Power Reduction hardware, known downstream as CPR3, CPR4 and CPRh, and support for MSM8998 and SDM630 CPU power reduction. In these new versions of the hardware, support for various new features was introduced, including voltage reduction for the GPU, security hardening and a new way of controlling CPU DVFS, consisting in internal communication between microcontrollers, specifically the CPR-Hardened and the Operating State Manager. The CPR v3, v4 and CPRh are present in a broad range of SoCs, from the mid-range to the high end ones including, but not limited to, MSM8953/8996/8998, SDM630/636/660/845. Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org>
This commit is contained in:
parent
b56256fdb4
commit
9b789ad11d
6 changed files with 2965 additions and 6 deletions
|
|
@ -42,6 +42,23 @@ config QCOM_CPR
|
|||
To compile this driver as a module, choose M here: the module will
|
||||
be called qcom-cpr
|
||||
|
||||
config QCOM_CPR3
|
||||
tristate "QCOM Core Power Reduction (CPR v3/v4/Hardened) support"
|
||||
depends on ARCH_QCOM && HAS_IOMEM
|
||||
select PM_OPP
|
||||
select REGMAP
|
||||
help
|
||||
Say Y here to enable support for the CPR hardware found on a broad
|
||||
variety of Qualcomm SoCs like MSM8996, MSM8998, SDM630, SDM660,
|
||||
SDM845 and others.
|
||||
|
||||
This driver populates OPP tables and makes adjustments to them
|
||||
based on feedback from the CPR hardware. If you want to do CPU
|
||||
and/or GPU frequency scaling say Y here.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called qcom-cpr3
|
||||
|
||||
config QCOM_GENI_SE
|
||||
tristate "QCOM GENI Serial Engine Driver"
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ obj-$(CONFIG_QCOM_AOSS_QMP) += qcom_aoss.o
|
|||
obj-$(CONFIG_QCOM_GENI_SE) += qcom-geni-se.o
|
||||
obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o
|
||||
obj-$(CONFIG_QCOM_CPR) += cpr-common.o cpr.o
|
||||
obj-$(CONFIG_QCOM_CPR3) += cpr-common.o cpr3.o
|
||||
obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o
|
||||
obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o
|
||||
obj-$(CONFIG_QCOM_OCMEM) += ocmem.o
|
||||
|
|
|
|||
|
|
@ -219,6 +219,29 @@ u32 cpr_get_fuse_corner(struct dev_pm_opp *opp, u32 tid)
|
|||
of_node_put(np);
|
||||
|
||||
return fc;
|
||||
|
||||
}
|
||||
|
||||
void cpr_get_corner_post_vadj(struct dev_pm_opp *opp, u32 tid,
|
||||
s32 *open_loop, s32 *closed_loop)
|
||||
{
|
||||
struct device_node *np;
|
||||
|
||||
/*
|
||||
* There is no of_property_read_s32_index, so we just store the
|
||||
* result into a s32 variable. After all, the OF API is doing
|
||||
* the exact same for of_property_read_s32...
|
||||
*/
|
||||
np = dev_pm_opp_get_of_node(opp);
|
||||
if (of_property_read_u32_index(np, "qcom,opp-oloop-vadj", tid,
|
||||
open_loop))
|
||||
*open_loop = 0;
|
||||
|
||||
if (of_property_read_u32_index(np, "qcom,opp-cloop-vadj", tid,
|
||||
closed_loop))
|
||||
*closed_loop = 0;
|
||||
|
||||
of_node_put(np);
|
||||
}
|
||||
|
||||
unsigned long cpr_get_opp_hz_for_req(struct dev_pm_opp *ref,
|
||||
|
|
@ -261,11 +284,10 @@ int cpr_calculate_scaling(const char *quot_offset,
|
|||
const struct fuse_corner_data *fdata,
|
||||
const struct corner *corner)
|
||||
{
|
||||
u32 quot_diff = 0;
|
||||
unsigned long freq_diff;
|
||||
int scaling;
|
||||
u64 freq_diff;
|
||||
const struct fuse_corner *fuse, *prev_fuse;
|
||||
int ret;
|
||||
u32 quot_diff;
|
||||
int scaling, ret;
|
||||
|
||||
fuse = corner->fuse_corner;
|
||||
prev_fuse = fuse - 1;
|
||||
|
|
@ -282,8 +304,9 @@ int cpr_calculate_scaling(const char *quot_offset,
|
|||
}
|
||||
|
||||
freq_diff = fuse->max_freq - prev_fuse->max_freq;
|
||||
freq_diff /= 1000000; /* Convert to MHz */
|
||||
scaling = 1000 * quot_diff / freq_diff;
|
||||
freq_diff = div_u64(freq_diff, 1000000); /* Convert to MHz */
|
||||
scaling = 1000 * quot_diff;
|
||||
do_div(scaling, freq_diff);
|
||||
return min(scaling, fdata->max_quot_scale);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -65,6 +65,8 @@ struct corner {
|
|||
struct corner_data {
|
||||
unsigned int fuse_corner;
|
||||
unsigned long freq;
|
||||
int oloop_vadj;
|
||||
int cloop_vadj;
|
||||
};
|
||||
|
||||
struct acc_desc {
|
||||
|
|
@ -101,6 +103,8 @@ int cpr_populate_fuse_common(struct device *dev,
|
|||
int cpr_find_initial_corner(struct device *dev, struct clk *cpu_clk,
|
||||
struct corner *corners, int num_corners);
|
||||
u32 cpr_get_fuse_corner(struct dev_pm_opp *opp, u32 tid);
|
||||
void cpr_get_corner_post_vadj(struct dev_pm_opp *opp, u32 tid,
|
||||
s32 *open_loop, s32 *closed_loop);
|
||||
unsigned long cpr_get_opp_hz_for_req(struct dev_pm_opp *ref,
|
||||
struct device *cpu_dev);
|
||||
int cpr_calculate_scaling(const char *quot_offset,
|
||||
|
|
|
|||
2897
drivers/soc/qcom/cpr3.c
Normal file
2897
drivers/soc/qcom/cpr3.c
Normal file
File diff suppressed because it is too large
Load diff
17
include/soc/qcom/cpr.h
Normal file
17
include/soc/qcom/cpr.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2013-2020, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2019 Linaro Limited
|
||||
* Copyright (c) 2021, AngeloGioacchino Del Regno
|
||||
* <angelogioacchino.delregno@somainline.org>
|
||||
*/
|
||||
|
||||
#ifndef __CPR_H__
|
||||
#define __CPR_H__
|
||||
|
||||
struct cpr_ext_data {
|
||||
int mem_acc_threshold_uV;
|
||||
int apm_threshold_uV;
|
||||
};
|
||||
|
||||
#endif /* __CPR_H__ */
|
||||
Loading…
Add table
Add a link
Reference in a new issue