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:
AngeloGioacchino Del Regno 2020-11-24 20:39:22 +01:00 committed by Jami Kettunen
parent b56256fdb4
commit 9b789ad11d
6 changed files with 2965 additions and 6 deletions

View file

@ -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

View file

@ -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

View file

@ -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);
}

View file

@ -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

File diff suppressed because it is too large Load diff

17
include/soc/qcom/cpr.h Normal file
View 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__ */