ARM cpufreq updates for 6.9

- General enhancements / cleanups to cpufreq drivers (tianyu2, Nícolas
   F. R. A. Prado, Erick Archer, Arnd Bergmann, Anastasia Belova).
 - Update cpufreq-dt-platdev to block/approve devices (Richard Acayan).
 - scmi: get transition delay from firmware (Pierre Gondois).
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEx73Crsp7f6M6scA70rkcPK6BEhwFAmXu2CMACgkQ0rkcPK6B
 EhxGMQ/+IBFDtHVRzPO6dQRWO1AFgUB3/XAWLPVjvVB3bhsBQS4BGFkkElyfUyfa
 vIQlZnerhgWbhw3pkLwbirN+yW9DWkBIgHwOtGaYaK9LeXEQfZ2+Yezc9uUe6syN
 HtHVLHntGetDP6FRQarTIeWYSE5FJOf1QhREIwBbXCmB4VvudcBuafAgykbKN92T
 p0bVHfKhaHlbYlOui3hJVBfRdDAgHlimawkqzOwqtugsoZ7rQcJDxO1OaE+ovH1E
 5KlXTbugcDkct1g66SPOZxx6APQ4EeXFanlmBaQAmgT1unVRuqMQpLrkxgiFywcm
 ypMz7SGwXsh41J0aZAEhpRvwPu3pGLtdQOTK3Pwa24xKkn17I6yYlyLyCGseXiCY
 wZz4W+Y6WBP4wwUnYBB6TuwfROdY7wy9KTswxd/qWP4Q7LKXyangB6d0pXV6eSUc
 XPyZ21NiTw4BUCkzOzl5+4um2uF2t62sTWh1hg/KOKwnlAkBqnt8/BvkuoNYqRte
 sX3xiWjcF0sq1kZlkHtE8USFqiSioBgQm2KCjnuc5N5rsK9Qmc7fBHEe4sP975HV
 otHYlBai/Y5D5piTOJMVv92+Ghw9jjLPUmn1IUppt8QVLv0RY1nPmp1WSu2yh80/
 2a2d9bR5llDfyt1vh75X7Tdt1Bsf9LSJyYptfxJvge/L/7jrGhI=
 =Hu43
 -----END PGP SIGNATURE-----

Merge tag 'cpufreq-arm-updates-6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm

Merge ARM cpufreq updates for 6.9 from Viresh Kumar:

"- General enhancements / cleanups to cpufreq drivers (tianyu2, Nícolas
   F. R. A. Prado, Erick Archer, Arnd Bergmann, Anastasia Belova).
 - Update cpufreq-dt-platdev to block/approve devices (Richard Acayan).
 - scmi: get transition delay from firmware (Pierre Gondois)."

* tag 'cpufreq-arm-updates-6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm:
  cpufreq: scmi: Set transition_delay_us
  firmware: arm_scmi: Populate fast channel rate_limit
  firmware: arm_scmi: Populate perf commands rate_limit
  cpufreq: qcom-hw: add CONFIG_COMMON_CLK dependency
  cpufreq: dt-platdev: block SDM670 in cpufreq-dt-platdev
  cpufreq: mediatek-hw: Don't error out if supply is not found
  Documentation: power: Use kcalloc() instead of kzalloc()
  cpufreq: mediatek-hw: Wait for CPU supplies before probing
  cpufreq: brcmstb-avs-cpufreq: add check for cpufreq_cpu_get's return value
  cpufreq: imx6: use regmap to read ocotp register
This commit is contained in:
Rafael J. Wysocki 2024-03-11 15:29:20 +01:00
commit 6b7195d305
13 changed files with 136 additions and 42 deletions

View file

@ -305,7 +305,7 @@ dev_pm_opp_get_opp_count
{
/* Do things */
num_available = dev_pm_opp_get_opp_count(dev);
speeds = kzalloc(sizeof(u32) * num_available, GFP_KERNEL);
speeds = kcalloc(num_available, sizeof(u32), GFP_KERNEL);
/* populate the table in increasing order */
freq = 0;
while (!IS_ERR(opp = dev_pm_opp_find_freq_ceil(dev, &freq))) {

View file

@ -274,7 +274,7 @@ dev_pm_opp_get_opp_count
{
/* 做一些事情 */
num_available = dev_pm_opp_get_opp_count(dev);
speeds = kzalloc(sizeof(u32) * num_available, GFP_KERNEL);
speeds = kcalloc(num_available, sizeof(u32), GFP_KERNEL);
/* 按升序填充表 */
freq = 0;
while (!IS_ERR(opp = dev_pm_opp_find_freq_ceil(dev, &freq))) {

View file

@ -173,6 +173,7 @@ config ARM_QCOM_CPUFREQ_NVMEM
config ARM_QCOM_CPUFREQ_HW
tristate "QCOM CPUFreq HW driver"
depends on ARCH_QCOM || COMPILE_TEST
depends on COMMON_CLK
help
Support for the CPUFreq HW driver.
Some QCOM chipsets have a HW engine to offload the steps

View file

@ -481,6 +481,8 @@ static bool brcm_avs_is_firmware_loaded(struct private_data *priv)
static unsigned int brcm_avs_cpufreq_get(unsigned int cpu)
{
struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
if (!policy)
return 0;
struct private_data *priv = policy->driver_data;
cpufreq_cpu_put(policy);

View file

@ -156,6 +156,7 @@ static const struct of_device_id blocklist[] __initconst = {
{ .compatible = "qcom,sc7280", },
{ .compatible = "qcom,sc8180x", },
{ .compatible = "qcom,sc8280xp", },
{ .compatible = "qcom,sdm670", },
{ .compatible = "qcom,sdm845", },
{ .compatible = "qcom,sdx75", },
{ .compatible = "qcom,sm6115", },

View file

@ -14,6 +14,8 @@
#include <linux/pm_opp.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#define PU_SOC_VOLTAGE_NORMAL 1250000
#define PU_SOC_VOLTAGE_HIGH 1275000
@ -225,8 +227,6 @@ static void imx6x_disable_freq_in_opp(struct device *dev, unsigned long freq)
static int imx6q_opp_check_speed_grading(struct device *dev)
{
struct device_node *np;
void __iomem *base;
u32 val;
int ret;
@ -235,16 +235,11 @@ static int imx6q_opp_check_speed_grading(struct device *dev)
if (ret)
return ret;
} else {
np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-ocotp");
if (!np)
return -ENOENT;
struct regmap *ocotp;
base = of_iomap(np, 0);
of_node_put(np);
if (!base) {
dev_err(dev, "failed to map ocotp\n");
return -EFAULT;
}
ocotp = syscon_regmap_lookup_by_compatible("fsl,imx6q-ocotp");
if (IS_ERR(ocotp))
return -ENOENT;
/*
* SPEED_GRADING[1:0] defines the max speed of ARM:
@ -254,8 +249,7 @@ static int imx6q_opp_check_speed_grading(struct device *dev)
* 2b'00: 792000000Hz;
* We need to set the max speed of ARM according to fuse map.
*/
val = readl_relaxed(base + OCOTP_CFG3);
iounmap(base);
regmap_read(ocotp, OCOTP_CFG3, &val);
}
val >>= OCOTP_CFG3_SPEED_SHIFT;
@ -290,25 +284,16 @@ static int imx6ul_opp_check_speed_grading(struct device *dev)
if (ret)
return ret;
} else {
struct device_node *np;
void __iomem *base;
struct regmap *ocotp;
np = of_find_compatible_node(NULL, NULL, "fsl,imx6ul-ocotp");
if (!np)
np = of_find_compatible_node(NULL, NULL,
"fsl,imx6ull-ocotp");
if (!np)
ocotp = syscon_regmap_lookup_by_compatible("fsl,imx6ul-ocotp");
if (IS_ERR(ocotp))
ocotp = syscon_regmap_lookup_by_compatible("fsl,imx6ull-ocotp");
if (IS_ERR(ocotp))
return -ENOENT;
base = of_iomap(np, 0);
of_node_put(np);
if (!base) {
dev_err(dev, "failed to map ocotp\n");
return -EFAULT;
}
val = readl_relaxed(base + OCOTP_CFG3);
iounmap(base);
regmap_read(ocotp, OCOTP_CFG3, &val);
}
/*

View file

@ -13,6 +13,7 @@
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#define LUT_MAX_ENTRIES 32U
@ -300,7 +301,23 @@ static struct cpufreq_driver cpufreq_mtk_hw_driver = {
static int mtk_cpufreq_hw_driver_probe(struct platform_device *pdev)
{
const void *data;
int ret;
int ret, cpu;
struct device *cpu_dev;
struct regulator *cpu_reg;
/* Make sure that all CPU supplies are available before proceeding. */
for_each_possible_cpu(cpu) {
cpu_dev = get_cpu_device(cpu);
if (!cpu_dev)
return dev_err_probe(&pdev->dev, -EPROBE_DEFER,
"Failed to get cpu%d device\n", cpu);
cpu_reg = devm_regulator_get(cpu_dev, "cpu");
if (IS_ERR(cpu_reg))
return dev_err_probe(&pdev->dev, PTR_ERR(cpu_reg),
"CPU%d regulator get failed\n", cpu);
}
data = of_device_get_match_data(&pdev->dev);
if (!data)

View file

@ -144,6 +144,29 @@ scmi_get_cpu_power(struct device *cpu_dev, unsigned long *power,
return 0;
}
static int
scmi_get_rate_limit(u32 domain, bool has_fast_switch)
{
int ret, rate_limit;
if (has_fast_switch) {
/*
* Fast channels are used whenever available,
* so use their rate_limit value if populated.
*/
ret = perf_ops->fast_switch_rate_limit(ph, domain,
&rate_limit);
if (!ret && rate_limit)
return rate_limit;
}
ret = perf_ops->rate_limit_get(ph, domain, &rate_limit);
if (ret)
return 0;
return rate_limit;
}
static int scmi_cpufreq_init(struct cpufreq_policy *policy)
{
int ret, nr_opp, domain;
@ -250,6 +273,9 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
policy->fast_switch_possible =
perf_ops->fast_switch_possible(ph, domain);
policy->transition_delay_us =
scmi_get_rate_limit(domain, policy->fast_switch_possible);
return 0;
out_free_opp:

View file

@ -1617,7 +1617,7 @@ static void
scmi_common_fastchannel_init(const struct scmi_protocol_handle *ph,
u8 describe_id, u32 message_id, u32 valid_size,
u32 domain, void __iomem **p_addr,
struct scmi_fc_db_info **p_db)
struct scmi_fc_db_info **p_db, u32 *rate_limit)
{
int ret;
u32 flags;
@ -1661,6 +1661,9 @@ scmi_common_fastchannel_init(const struct scmi_protocol_handle *ph,
goto err_xfer;
}
if (rate_limit)
*rate_limit = le32_to_cpu(resp->rate_limit) & GENMASK(19, 0);
phys_addr = le32_to_cpu(resp->chan_addr_low);
phys_addr |= (u64)le32_to_cpu(resp->chan_addr_high) << 32;
addr = devm_ioremap(ph->dev, phys_addr, size);

View file

@ -153,6 +153,7 @@ struct perf_dom_info {
bool perf_fastchannels;
bool level_indexing_mode;
u32 opp_count;
u32 rate_limit_us;
u32 sustained_freq_khz;
u32 sustained_perf_level;
unsigned long mult_factor;
@ -266,6 +267,8 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
if (PROTOCOL_REV_MAJOR(version) >= 0x4)
dom_info->level_indexing_mode =
SUPPORTS_LEVEL_INDEXING(flags);
dom_info->rate_limit_us = le32_to_cpu(attr->rate_limit_us) &
GENMASK(19, 0);
dom_info->sustained_freq_khz =
le32_to_cpu(attr->sustained_freq_khz);
dom_info->sustained_perf_level =
@ -786,23 +789,27 @@ static void scmi_perf_domain_init_fc(const struct scmi_protocol_handle *ph,
ph->hops->fastchannel_init(ph, PERF_DESCRIBE_FASTCHANNEL,
PERF_LEVEL_GET, 4, dom->id,
&fc[PERF_FC_LEVEL].get_addr, NULL);
&fc[PERF_FC_LEVEL].get_addr, NULL,
&fc[PERF_FC_LEVEL].rate_limit);
ph->hops->fastchannel_init(ph, PERF_DESCRIBE_FASTCHANNEL,
PERF_LIMITS_GET, 8, dom->id,
&fc[PERF_FC_LIMIT].get_addr, NULL);
&fc[PERF_FC_LIMIT].get_addr, NULL,
&fc[PERF_FC_LIMIT].rate_limit);
if (dom->info.set_perf)
ph->hops->fastchannel_init(ph, PERF_DESCRIBE_FASTCHANNEL,
PERF_LEVEL_SET, 4, dom->id,
&fc[PERF_FC_LEVEL].set_addr,
&fc[PERF_FC_LEVEL].set_db);
&fc[PERF_FC_LEVEL].set_db,
&fc[PERF_FC_LEVEL].rate_limit);
if (dom->set_limits)
ph->hops->fastchannel_init(ph, PERF_DESCRIBE_FASTCHANNEL,
PERF_LIMITS_SET, 8, dom->id,
&fc[PERF_FC_LIMIT].set_addr,
&fc[PERF_FC_LIMIT].set_db);
&fc[PERF_FC_LIMIT].set_db,
&fc[PERF_FC_LIMIT].rate_limit);
dom->fc_info = fc;
}
@ -855,6 +862,23 @@ scmi_dvfs_transition_latency_get(const struct scmi_protocol_handle *ph,
return dom->opp[dom->opp_count - 1].trans_latency_us * 1000;
}
static int
scmi_dvfs_rate_limit_get(const struct scmi_protocol_handle *ph,
u32 domain, u32 *rate_limit)
{
struct perf_dom_info *dom;
if (!rate_limit)
return -EINVAL;
dom = scmi_perf_domain_lookup(ph, domain);
if (IS_ERR(dom))
return PTR_ERR(dom);
*rate_limit = dom->rate_limit_us;
return 0;
}
static int scmi_dvfs_freq_set(const struct scmi_protocol_handle *ph, u32 domain,
unsigned long freq, bool poll)
{
@ -954,6 +978,25 @@ static bool scmi_fast_switch_possible(const struct scmi_protocol_handle *ph,
return dom->fc_info && dom->fc_info[PERF_FC_LEVEL].set_addr;
}
static int scmi_fast_switch_rate_limit(const struct scmi_protocol_handle *ph,
u32 domain, u32 *rate_limit)
{
struct perf_dom_info *dom;
if (!rate_limit)
return -EINVAL;
dom = scmi_perf_domain_lookup(ph, domain);
if (IS_ERR(dom))
return PTR_ERR(dom);
if (!dom->fc_info)
return -EINVAL;
*rate_limit = dom->fc_info[PERF_FC_LEVEL].rate_limit;
return 0;
}
static enum scmi_power_scale
scmi_power_scale_get(const struct scmi_protocol_handle *ph)
{
@ -970,11 +1013,13 @@ static const struct scmi_perf_proto_ops perf_proto_ops = {
.level_set = scmi_perf_level_set,
.level_get = scmi_perf_level_get,
.transition_latency_get = scmi_dvfs_transition_latency_get,
.rate_limit_get = scmi_dvfs_rate_limit_get,
.device_opps_add = scmi_dvfs_device_opps_add,
.freq_set = scmi_dvfs_freq_set,
.freq_get = scmi_dvfs_freq_get,
.est_power_get = scmi_dvfs_est_power_get,
.fast_switch_possible = scmi_fast_switch_possible,
.fast_switch_rate_limit = scmi_fast_switch_rate_limit,
.power_scale_get = scmi_power_scale_get,
};

View file

@ -703,20 +703,24 @@ static void scmi_powercap_domain_init_fc(const struct scmi_protocol_handle *ph,
ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
POWERCAP_CAP_SET, 4, domain,
&fc[POWERCAP_FC_CAP].set_addr,
&fc[POWERCAP_FC_CAP].set_db);
&fc[POWERCAP_FC_CAP].set_db,
&fc[POWERCAP_FC_CAP].rate_limit);
ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
POWERCAP_CAP_GET, 4, domain,
&fc[POWERCAP_FC_CAP].get_addr, NULL);
&fc[POWERCAP_FC_CAP].get_addr, NULL,
&fc[POWERCAP_FC_CAP].rate_limit);
ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
POWERCAP_PAI_SET, 4, domain,
&fc[POWERCAP_FC_PAI].set_addr,
&fc[POWERCAP_FC_PAI].set_db);
&fc[POWERCAP_FC_PAI].set_db,
&fc[POWERCAP_FC_PAI].rate_limit);
ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
POWERCAP_PAI_GET, 4, domain,
&fc[POWERCAP_FC_PAI].get_addr, NULL);
&fc[POWERCAP_FC_PAI].get_addr, NULL,
&fc[POWERCAP_PAI_GET].rate_limit);
*p_fc = fc;
}

View file

@ -234,6 +234,7 @@ struct scmi_fc_info {
void __iomem *set_addr;
void __iomem *get_addr;
struct scmi_fc_db_info *set_db;
u32 rate_limit;
};
/**
@ -268,7 +269,8 @@ struct scmi_proto_helpers_ops {
u8 describe_id, u32 message_id,
u32 valid_size, u32 domain,
void __iomem **p_addr,
struct scmi_fc_db_info **p_db);
struct scmi_fc_db_info **p_db,
u32 *rate_limit);
void (*fastchannel_db_ring)(struct scmi_fc_db_info *db);
};

View file

@ -128,6 +128,8 @@ struct scmi_perf_domain_info {
* @level_set: sets the performance level of a domain
* @level_get: gets the performance level of a domain
* @transition_latency_get: gets the DVFS transition latency for a given device
* @rate_limit_get: gets the minimum time (us) required between successive
* requests
* @device_opps_add: adds all the OPPs for a given device
* @freq_set: sets the frequency for a given device using sustained frequency
* to sustained performance level mapping
@ -137,6 +139,8 @@ struct scmi_perf_domain_info {
* at a given frequency
* @fast_switch_possible: indicates if fast DVFS switching is possible or not
* for a given device
* @fast_switch_rate_limit: gets the minimum time (us) required between
* successive fast_switching requests
* @power_scale_mw_get: indicates if the power values provided are in milliWatts
* or in some other (abstract) scale
*/
@ -154,6 +158,8 @@ struct scmi_perf_proto_ops {
u32 *level, bool poll);
int (*transition_latency_get)(const struct scmi_protocol_handle *ph,
u32 domain);
int (*rate_limit_get)(const struct scmi_protocol_handle *ph,
u32 domain, u32 *rate_limit);
int (*device_opps_add)(const struct scmi_protocol_handle *ph,
struct device *dev, u32 domain);
int (*freq_set)(const struct scmi_protocol_handle *ph, u32 domain,
@ -164,6 +170,8 @@ struct scmi_perf_proto_ops {
unsigned long *rate, unsigned long *power);
bool (*fast_switch_possible)(const struct scmi_protocol_handle *ph,
u32 domain);
int (*fast_switch_rate_limit)(const struct scmi_protocol_handle *ph,
u32 domain, u32 *rate_limit);
enum scmi_power_scale (*power_scale_get)(const struct scmi_protocol_handle *ph);
};