build/patch/kernel/rockchip-rk3588-edge/0023-RK3588-Add-Cpufreq-Support.patch

1286 lines
34 KiB
Diff

From f9c3292a2a99acf817f97a2b312a98e42a5eaa2e Mon Sep 17 00:00:00 2001
From: Sebastian Reichel <sebastian.reichel@collabora.com>
Date: Thu, 18 Aug 2022 14:21:30 +0200
Subject: [PATCH 1/2] cpufreq: rockchip: Introduce driver for rk3588
This is a heavily modified port from the downstream driver.
Downstream used it for multiple rockchip generations, while
upstream just used the generic cpufreq-dt driver so far. For
rk3588 this is no longer good enough, since two regulators
need to be controlled.
Also during shutdown the correct frequency needs to be configured
for the big CPU cores to avoid a system hang when firmware tries
to bring them up at reboot time.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/cpufreq/Kconfig.arm | 10 +
drivers/cpufreq/Makefile | 1 +
drivers/cpufreq/cpufreq-dt-platdev.c | 2 +
drivers/cpufreq/rockchip-cpufreq.c | 645 +++++++++++++++++++++++++++
4 files changed, 658 insertions(+)
create mode 100644 drivers/cpufreq/rockchip-cpufreq.c
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index 123b4bbfcfee..6fdc2f0430bb 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -189,6 +189,16 @@ config ARM_RASPBERRYPI_CPUFREQ
If in doubt, say N.
+config ARM_ROCKCHIP_CPUFREQ
+ tristate "Rockchip CPUfreq driver"
+ depends on ARCH_ROCKCHIP && CPUFREQ_DT
+ select PM_OPP
+ help
+ This adds the CPUFreq driver support for Rockchip SoCs,
+ based on cpufreq-dt.
+
+ If in doubt, say N.
+
config ARM_S3C64XX_CPUFREQ
bool "Samsung S3C64XX"
depends on CPU_S3C6410
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index ef8510774913..c3f8c9cd563f 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -71,6 +71,7 @@ obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o
obj-$(CONFIG_ARM_QCOM_CPUFREQ_HW) += qcom-cpufreq-hw.o
obj-$(CONFIG_ARM_QCOM_CPUFREQ_NVMEM) += qcom-cpufreq-nvmem.o
obj-$(CONFIG_ARM_RASPBERRYPI_CPUFREQ) += raspberrypi-cpufreq.o
+obj-$(CONFIG_ARM_ROCKCHIP_CPUFREQ) += rockchip-cpufreq.o
obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o
obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o
obj-$(CONFIG_ARM_SA1110_CPUFREQ) += sa1110-cpufreq.o
diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c
index e2b20080de3a..7bc19a59be26 100644
--- a/drivers/cpufreq/cpufreq-dt-platdev.c
+++ b/drivers/cpufreq/cpufreq-dt-platdev.c
@@ -159,6 +159,8 @@ static const struct of_device_id blocklist[] __initconst = {
{ .compatible = "qcom,sm8250", },
{ .compatible = "qcom,sm8350", },
+ { .compatible = "rockchip,rk3588", },
+
{ .compatible = "st,stih407", },
{ .compatible = "st,stih410", },
{ .compatible = "st,stih418", },
diff --git a/drivers/cpufreq/rockchip-cpufreq.c b/drivers/cpufreq/rockchip-cpufreq.c
new file mode 100644
index 000000000000..0bf57ac85e60
--- /dev/null
+++ b/drivers/cpufreq/rockchip-cpufreq.c
@@ -0,0 +1,645 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Rockchip CPUFreq Driver. This is similar to the generic DT
+ * cpufreq driver, but handles the following platform specific
+ * quirks:
+ *
+ * * support for two regulators - one for the CPU core and one
+ * for the memory interface
+ * * reboot handler to setup the reboot frequency
+ * * handling of read margin registers
+ *
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd
+ * Copyright (C) 2023 Collabora Ltd.
+ */
+
+#include <linux/cpu.h>
+#include <linux/cpufreq.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
+#include <linux/slab.h>
+#include <linux/reboot.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#include "cpufreq-dt.h"
+
+#define RK3588_MEMCFG_HSSPRF_LOW 0x20
+#define RK3588_MEMCFG_HSDPRF_LOW 0x28
+#define RK3588_MEMCFG_HSDPRF_HIGH 0x2c
+#define RK3588_CPU_CTRL 0x30
+
+#define VOLT_RM_TABLE_END ~1
+
+static struct platform_device *cpufreq_pdev;
+static LIST_HEAD(priv_list);
+
+struct volt_rm_table {
+ uint32_t volt;
+ uint32_t rm;
+};
+
+struct rockchip_opp_info {
+ const struct rockchip_opp_data *data;
+ struct volt_rm_table *volt_rm_tbl;
+ struct regmap *grf;
+ u32 current_rm;
+ u32 reboot_freq;
+};
+
+struct private_data {
+ struct list_head node;
+
+ cpumask_var_t cpus;
+ struct device *cpu_dev;
+ struct cpufreq_frequency_table *freq_table;
+};
+
+struct rockchip_opp_data {
+ int (*set_read_margin)(struct device *dev, struct rockchip_opp_info *opp_info,
+ unsigned long volt);
+};
+
+struct cluster_info {
+ struct list_head list_head;
+ struct rockchip_opp_info opp_info;
+ cpumask_t cpus;
+};
+static LIST_HEAD(cluster_info_list);
+
+static int rk3588_cpu_set_read_margin(struct device *dev, struct rockchip_opp_info *opp_info,
+ unsigned long volt)
+{
+ bool is_found = false;
+ u32 rm;
+ int i;
+
+ if (!opp_info->volt_rm_tbl)
+ return 0;
+
+ for (i = 0; opp_info->volt_rm_tbl[i].rm != VOLT_RM_TABLE_END; i++) {
+ if (volt >= opp_info->volt_rm_tbl[i].volt) {
+ rm = opp_info->volt_rm_tbl[i].rm;
+ is_found = true;
+ break;
+ }
+ }
+
+ if (!is_found)
+ return 0;
+ if (rm == opp_info->current_rm)
+ return 0;
+ if (!opp_info->grf)
+ return 0;
+
+ dev_dbg(dev, "set rm to %d\n", rm);
+ regmap_write(opp_info->grf, RK3588_MEMCFG_HSSPRF_LOW, 0x001c0000 | (rm << 2));
+ regmap_write(opp_info->grf, RK3588_MEMCFG_HSDPRF_LOW, 0x003c0000 | (rm << 2));
+ regmap_write(opp_info->grf, RK3588_MEMCFG_HSDPRF_HIGH, 0x003c0000 | (rm << 2));
+ regmap_write(opp_info->grf, RK3588_CPU_CTRL, 0x00200020);
+ udelay(1);
+ regmap_write(opp_info->grf, RK3588_CPU_CTRL, 0x00200000);
+
+ opp_info->current_rm = rm;
+
+ return 0;
+}
+
+static const struct rockchip_opp_data rk3588_cpu_opp_data = {
+ .set_read_margin = rk3588_cpu_set_read_margin,
+};
+
+static const struct of_device_id rockchip_cpufreq_of_match[] = {
+ {
+ .compatible = "rockchip,rk3588",
+ .data = (void *)&rk3588_cpu_opp_data,
+ },
+ {},
+};
+
+static struct cluster_info *rockchip_cluster_info_lookup(int cpu)
+{
+ struct cluster_info *cluster;
+
+ list_for_each_entry(cluster, &cluster_info_list, list_head) {
+ if (cpumask_test_cpu(cpu, &cluster->cpus))
+ return cluster;
+ }
+
+ return NULL;
+}
+
+static int rockchip_cpufreq_set_volt(struct device *dev,
+ struct regulator *reg,
+ struct dev_pm_opp_supply *supply)
+{
+ int ret;
+
+ ret = regulator_set_voltage_triplet(reg, supply->u_volt_min,
+ supply->u_volt, supply->u_volt_max);
+ if (ret)
+ dev_err(dev, "%s: failed to set voltage (%lu %lu %lu uV): %d\n",
+ __func__, supply->u_volt_min, supply->u_volt,
+ supply->u_volt_max, ret);
+
+ return ret;
+}
+
+static int rockchip_cpufreq_set_read_margin(struct device *dev,
+ struct rockchip_opp_info *opp_info,
+ unsigned long volt)
+{
+ if (opp_info->data && opp_info->data->set_read_margin) {
+ opp_info->data->set_read_margin(dev, opp_info, volt);
+ }
+
+ return 0;
+}
+
+static int rk_opp_config_regulators(struct device *dev,
+ struct dev_pm_opp *old_opp, struct dev_pm_opp *new_opp,
+ struct regulator **regulators, unsigned int count)
+{
+ struct dev_pm_opp_supply old_supplies[2];
+ struct dev_pm_opp_supply new_supplies[2];
+ struct regulator *vdd_reg = regulators[0];
+ struct regulator *mem_reg = regulators[1];
+ struct rockchip_opp_info *opp_info;
+ struct cluster_info *cluster;
+ int ret = 0;
+ unsigned long old_freq = dev_pm_opp_get_freq(old_opp);
+ unsigned long new_freq = dev_pm_opp_get_freq(new_opp);
+
+ /* We must have two regulators here */
+ WARN_ON(count != 2);
+
+ ret = dev_pm_opp_get_supplies(old_opp, old_supplies);
+ if (ret)
+ return ret;
+
+ ret = dev_pm_opp_get_supplies(new_opp, new_supplies);
+ if (ret)
+ return ret;
+
+ cluster = rockchip_cluster_info_lookup(dev->id);
+ if (!cluster)
+ return -EINVAL;
+ opp_info = &cluster->opp_info;
+
+ if (new_freq >= old_freq) {
+ ret = rockchip_cpufreq_set_volt(dev, mem_reg, &new_supplies[1]);
+ if (ret)
+ goto error;
+ ret = rockchip_cpufreq_set_volt(dev, vdd_reg, &new_supplies[0]);
+ if (ret)
+ goto error;
+ rockchip_cpufreq_set_read_margin(dev, opp_info, new_supplies[0].u_volt);
+ } else {
+ rockchip_cpufreq_set_read_margin(dev, opp_info, new_supplies[0].u_volt);
+ ret = rockchip_cpufreq_set_volt(dev, vdd_reg, &new_supplies[0]);
+ if (ret)
+ goto error;
+ ret = rockchip_cpufreq_set_volt(dev, mem_reg, &new_supplies[1]);
+ if (ret)
+ goto error;
+ }
+
+ return 0;
+
+error:
+ rockchip_cpufreq_set_read_margin(dev, opp_info, old_supplies[0].u_volt);
+ rockchip_cpufreq_set_volt(dev, mem_reg, &old_supplies[1]);
+ rockchip_cpufreq_set_volt(dev, vdd_reg, &old_supplies[0]);
+ return ret;
+}
+
+static void rockchip_get_opp_data(const struct of_device_id *matches,
+ struct rockchip_opp_info *info)
+{
+ const struct of_device_id *match;
+ struct device_node *node;
+
+ node = of_find_node_by_path("/");
+ match = of_match_node(matches, node);
+ if (match && match->data)
+ info->data = match->data;
+ of_node_put(node);
+}
+
+static int rockchip_get_volt_rm_table(struct device *dev, struct device_node *np,
+ char *porp_name, struct volt_rm_table **table)
+{
+ struct volt_rm_table *rm_table;
+ const struct property *prop;
+ int count, i;
+
+ prop = of_find_property(np, porp_name, NULL);
+ if (!prop)
+ return -EINVAL;
+
+ if (!prop->value)
+ return -ENODATA;
+
+ count = of_property_count_u32_elems(np, porp_name);
+ if (count < 0)
+ return -EINVAL;
+
+ if (count % 2)
+ return -EINVAL;
+
+ rm_table = devm_kzalloc(dev, sizeof(*rm_table) * (count / 2 + 1),
+ GFP_KERNEL);
+ if (!rm_table)
+ return -ENOMEM;
+
+ for (i = 0; i < count / 2; i++) {
+ of_property_read_u32_index(np, porp_name, 2 * i,
+ &rm_table[i].volt);
+ of_property_read_u32_index(np, porp_name, 2 * i + 1,
+ &rm_table[i].rm);
+ }
+
+ rm_table[i].volt = 0;
+ rm_table[i].rm = VOLT_RM_TABLE_END;
+
+ *table = rm_table;
+
+ return 0;
+}
+
+static int rockchip_cpufreq_reboot(struct notifier_block *notifier, unsigned long event, void *cmd)
+{
+ struct cluster_info *cluster;
+ struct device *dev;
+ int freq, ret, cpu;
+
+ if (event != SYS_RESTART)
+ return NOTIFY_DONE;
+
+ for_each_possible_cpu(cpu) {
+ cluster = rockchip_cluster_info_lookup(cpu);
+ if (!cluster)
+ continue;
+
+ dev = get_cpu_device(cpu);
+ if (!dev)
+ continue;
+
+ freq = cluster->opp_info.reboot_freq;
+
+ if (freq) {
+ ret = dev_pm_opp_set_rate(dev, freq);
+ if (ret)
+ dev_err(dev, "Failed setting reboot freq for cpu %d to %d: %d\n",
+ cpu, freq, ret);
+ dev_pm_opp_remove_table(dev);
+ }
+ }
+
+ return NOTIFY_DONE;
+}
+
+static int rockchip_cpufreq_cluster_init(int cpu, struct cluster_info *cluster)
+{
+ struct rockchip_opp_info *opp_info = &cluster->opp_info;
+ int reg_table_token = -EINVAL;
+ int opp_table_token = -EINVAL;
+ struct device_node *np;
+ struct device *dev;
+ const char * const reg_names[] = { "cpu", "mem", NULL };
+ int ret = 0;
+
+ dev = get_cpu_device(cpu);
+ if (!dev)
+ return -ENODEV;
+
+ if (!of_find_property(dev->of_node, "cpu-supply", NULL))
+ return -ENOENT;
+
+ np = of_parse_phandle(dev->of_node, "operating-points-v2", 0);
+ if (!np) {
+ dev_warn(dev, "OPP-v2 not supported\n");
+ return -ENOENT;
+ }
+
+ reg_table_token = dev_pm_opp_set_regulators(dev, reg_names);
+ if (reg_table_token < 0) {
+ ret = reg_table_token;
+ dev_err_probe(dev, ret, "Failed to set opp regulators\n");
+ goto np_err;
+ }
+
+ ret = dev_pm_opp_of_get_sharing_cpus(dev, &cluster->cpus);
+ if (ret) {
+ dev_err_probe(dev, ret, "Failed to get sharing cpus\n");
+ goto np_err;
+ }
+
+ rockchip_get_opp_data(rockchip_cpufreq_of_match, opp_info);
+ if (opp_info->data && opp_info->data->set_read_margin) {
+ opp_info->current_rm = UINT_MAX;
+ opp_info->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+ if (IS_ERR(opp_info->grf))
+ opp_info->grf = NULL;
+ rockchip_get_volt_rm_table(dev, np, "rockchip,volt-mem-read-margin", &opp_info->volt_rm_tbl);
+
+ of_property_read_u32(np, "rockchip,reboot-freq", &opp_info->reboot_freq);
+ }
+
+ opp_table_token = dev_pm_opp_set_config_regulators(dev, rk_opp_config_regulators);
+ if (opp_table_token < 0) {
+ ret = opp_table_token;
+ dev_err(dev, "Failed to set opp config regulators\n");
+ goto reg_opp_table;
+ }
+
+ of_node_put(np);
+
+ return 0;
+
+reg_opp_table:
+ if (reg_table_token >= 0)
+ dev_pm_opp_put_regulators(reg_table_token);
+np_err:
+ of_node_put(np);
+
+ return ret;
+}
+
+static struct notifier_block rockchip_cpufreq_reboot_notifier = {
+ .notifier_call = rockchip_cpufreq_reboot,
+ .priority = 0,
+};
+
+static struct freq_attr *cpufreq_rockchip_attr[] = {
+ &cpufreq_freq_attr_scaling_available_freqs,
+ NULL,
+};
+
+static int cpufreq_online(struct cpufreq_policy *policy)
+{
+ /* We did light-weight tear down earlier, nothing to do here */
+ return 0;
+}
+
+static int cpufreq_offline(struct cpufreq_policy *policy)
+{
+ /*
+ * Preserve policy->driver_data and don't free resources on light-weight
+ * tear down.
+ */
+ return 0;
+}
+
+static struct private_data *rockchip_cpufreq_find_data(int cpu)
+{
+ struct private_data *priv;
+
+ list_for_each_entry(priv, &priv_list, node) {
+ if (cpumask_test_cpu(cpu, priv->cpus))
+ return priv;
+ }
+
+ return NULL;
+}
+
+static int cpufreq_init(struct cpufreq_policy *policy)
+{
+ struct private_data *priv;
+ struct device *cpu_dev;
+ struct clk *cpu_clk;
+ unsigned int transition_latency;
+ int ret;
+
+ priv = rockchip_cpufreq_find_data(policy->cpu);
+ if (!priv) {
+ pr_err("failed to find data for cpu%d\n", policy->cpu);
+ return -ENODEV;
+ }
+ cpu_dev = priv->cpu_dev;
+
+ cpu_clk = clk_get(cpu_dev, NULL);
+ if (IS_ERR(cpu_clk)) {
+ ret = PTR_ERR(cpu_clk);
+ dev_err(cpu_dev, "%s: failed to get clk: %d\n", __func__, ret);
+ return ret;
+ }
+
+ transition_latency = dev_pm_opp_get_max_transition_latency(cpu_dev);
+ if (!transition_latency)
+ transition_latency = CPUFREQ_ETERNAL;
+
+ cpumask_copy(policy->cpus, priv->cpus);
+ policy->driver_data = priv;
+ policy->clk = cpu_clk;
+ policy->freq_table = priv->freq_table;
+ policy->suspend_freq = dev_pm_opp_get_suspend_opp_freq(cpu_dev) / 1000;
+ policy->cpuinfo.transition_latency = transition_latency;
+ policy->dvfs_possible_from_any_cpu = true;
+
+ return 0;
+}
+
+static int cpufreq_exit(struct cpufreq_policy *policy)
+{
+ clk_put(policy->clk);
+ return 0;
+}
+
+static int set_target(struct cpufreq_policy *policy, unsigned int index)
+{
+ struct private_data *priv = policy->driver_data;
+ unsigned long freq = policy->freq_table[index].frequency;
+
+ return dev_pm_opp_set_rate(priv->cpu_dev, freq * 1000);
+}
+
+static struct cpufreq_driver rockchip_cpufreq_driver = {
+ .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK |
+ CPUFREQ_IS_COOLING_DEV |
+ CPUFREQ_HAVE_GOVERNOR_PER_POLICY,
+ .verify = cpufreq_generic_frequency_table_verify,
+ .target_index = set_target,
+ .get = cpufreq_generic_get,
+ .init = cpufreq_init,
+ .exit = cpufreq_exit,
+ .online = cpufreq_online,
+ .offline = cpufreq_offline,
+ .register_em = cpufreq_register_em_with_opp,
+ .name = "rockchip-cpufreq",
+ .attr = cpufreq_rockchip_attr,
+ .suspend = cpufreq_generic_suspend,
+};
+
+static int rockchip_cpufreq_init(struct device *dev, int cpu)
+{
+ struct private_data *priv;
+ struct device *cpu_dev;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ if (!alloc_cpumask_var(&priv->cpus, GFP_KERNEL))
+ return -ENOMEM;
+
+ cpumask_set_cpu(cpu, priv->cpus);
+
+ cpu_dev = get_cpu_device(cpu);
+ if (!cpu_dev)
+ return -EPROBE_DEFER;
+ priv->cpu_dev = cpu_dev;
+
+ ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, priv->cpus);
+ if (ret)
+ return ret;
+
+ ret = dev_pm_opp_of_cpumask_add_table(priv->cpus);
+ if (ret)
+ return ret;
+
+ ret = dev_pm_opp_get_opp_count(cpu_dev);
+ if (ret <= 0)
+ return dev_err_probe(cpu_dev, -ENODEV, "OPP table can't be empty\n");
+
+ ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &priv->freq_table);
+ if (ret)
+ return dev_err_probe(cpu_dev, ret, "failed to init cpufreq table\n");
+
+ list_add(&priv->node, &priv_list);
+
+ return 0;
+}
+
+static void rockchip_cpufreq_free_list(void *data)
+{
+ struct cluster_info *cluster, *pos;
+
+ list_for_each_entry_safe(cluster, pos, &cluster_info_list, list_head) {
+ list_del(&cluster->list_head);
+ }
+}
+
+static int rockchip_cpufreq_init_list(struct device *dev)
+{
+ struct cluster_info *cluster;
+ int cpu, ret;
+
+ for_each_possible_cpu(cpu) {
+ cluster = rockchip_cluster_info_lookup(cpu);
+ if (cluster)
+ continue;
+
+ cluster = devm_kzalloc(dev, sizeof(*cluster), GFP_KERNEL);
+ if (!cluster) {
+ ret = -ENOMEM;
+ goto release_cluster_info;
+ }
+
+ ret = rockchip_cpufreq_cluster_init(cpu, cluster);
+ if (ret) {
+ dev_err_probe(dev, ret, "Failed to initialize dvfs info cpu%d\n", cpu);
+ goto release_cluster_info;
+ }
+ list_add(&cluster->list_head, &cluster_info_list);
+ }
+
+ return 0;
+
+release_cluster_info:
+ rockchip_cpufreq_free_list(NULL);
+ return ret;
+}
+
+static void rockchip_cpufreq_unregister(void *data)
+{
+ cpufreq_unregister_driver(&rockchip_cpufreq_driver);
+}
+
+static int rockchip_cpufreq_probe(struct platform_device *pdev)
+{
+ int ret, cpu;
+
+ ret = rockchip_cpufreq_init_list(&pdev->dev);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&pdev->dev, rockchip_cpufreq_free_list, NULL);
+ if (ret)
+ return ret;
+
+ ret = devm_register_reboot_notifier(&pdev->dev, &rockchip_cpufreq_reboot_notifier);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to register reboot handler\n");
+
+ for_each_possible_cpu(cpu) {
+ ret = rockchip_cpufreq_init(&pdev->dev, cpu);
+ if (ret)
+ return ret;
+ }
+
+ ret = cpufreq_register_driver(&rockchip_cpufreq_driver);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "failed register driver\n");
+
+ ret = devm_add_action_or_reset(&pdev->dev, rockchip_cpufreq_unregister, NULL);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static struct platform_driver rockchip_cpufreq_platdrv = {
+ .driver = {
+ .name = "rockchip-cpufreq",
+ },
+ .probe = rockchip_cpufreq_probe,
+};
+
+static int __init rockchip_cpufreq_driver_init(void)
+{
+ int ret;
+
+ if (!of_machine_is_compatible("rockchip,rk3588") &&
+ !of_machine_is_compatible("rockchip,rk3588s")) {
+ return -ENODEV;
+ }
+
+ ret = platform_driver_register(&rockchip_cpufreq_platdrv);
+ if (ret)
+ return ret;
+
+ cpufreq_pdev = platform_device_register_data(NULL, "rockchip-cpufreq", -1,
+ NULL, 0);
+ if (IS_ERR(cpufreq_pdev)) {
+ pr_err("failed to register rockchip-cpufreq platform device\n");
+ ret = PTR_ERR(cpufreq_pdev);
+ goto unregister_platform_driver;
+ }
+
+ return 0;
+
+unregister_platform_driver:
+ platform_driver_unregister(&rockchip_cpufreq_platdrv);
+ return ret;
+}
+module_init(rockchip_cpufreq_driver_init);
+
+static void __exit rockchip_cpufreq_driver_exit(void)
+{
+ platform_device_unregister(cpufreq_pdev);
+ platform_driver_unregister(&rockchip_cpufreq_platdrv);
+}
+module_exit(rockchip_cpufreq_driver_exit)
+
+MODULE_AUTHOR("Finley Xiao <finley.xiao@rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip cpufreq driver");
+MODULE_LICENSE("GPL v2");
--
2.41.0
From 1a4f292fcdb4b4a625dda97e8236911b896c8abe Mon Sep 17 00:00:00 2001
From: Sebastian Reichel <sebastian.reichel@collabora.com>
Date: Tue, 4 Apr 2023 17:30:46 +0200
Subject: [PATCH 2/2] arm64: dts: rockchip: rk3588: add cpu frequency scaling
support
Add required bits for CPU frequency scaling to the Rockchip 3588
devicetree. This is missing the 2.4 GHz operating point for the
big cpu clusters, since that does not work well on all SoCs.
Downstream has a driver for PVTM, which reduces the requested
frequencies based on (among other things) silicon quality.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
arch/arm64/boot/dts/rockchip/rk3588s.dtsi | 452 ++++++++++++++++++++++
1 file changed, 452 insertions(+)
diff --git a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi
index a90ec13f50b4..aa78f535b27d 100644
--- a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi
@@ -10,6 +10,7 @@
#include <dt-bindings/reset/rockchip,rk3588-cru.h>
#include <dt-bindings/phy/phy.h>
#include <dt-bindings/ata/ahci.h>
+#include <dt-bindings/thermal/thermal.h>
/ {
compatible = "rockchip,rk3588";
@@ -18,6 +19,215 @@ / {
#address-cells = <2>;
#size-cells = <2>;
+ cluster0_opp_table: opp-table-cluster0 {
+ compatible = "operating-points-v2";
+ opp-shared;
+
+ opp-408000000 {
+ opp-hz = /bits/ 64 <408000000>;
+ opp-microvolt = <750000 750000 950000>,
+ <750000 750000 950000>;
+ clock-latency-ns = <40000>;
+ opp-suspend;
+ };
+ opp-600000000 {
+ opp-hz = /bits/ 64 <600000000>;
+ opp-microvolt = <750000 750000 950000>,
+ <750000 750000 950000>;
+ clock-latency-ns = <40000>;
+ };
+ opp-816000000 {
+ opp-hz = /bits/ 64 <816000000>;
+ opp-microvolt = <750000 750000 950000>,
+ <750000 750000 950000>;
+ clock-latency-ns = <40000>;
+ };
+ opp-1008000000 {
+ opp-hz = /bits/ 64 <1008000000>;
+ opp-microvolt = <750000 750000 950000>,
+ <750000 750000 950000>;
+ clock-latency-ns = <40000>;
+ };
+ opp-1200000000 {
+ opp-hz = /bits/ 64 <1200000000>;
+ opp-microvolt = <775000 775000 950000>,
+ <775000 775000 950000>;
+ clock-latency-ns = <40000>;
+ };
+ opp-1416000000 {
+ opp-hz = /bits/ 64 <1416000000>;
+ opp-microvolt = <825000 825000 950000>,
+ <825000 825000 950000>;
+ clock-latency-ns = <40000>;
+ };
+ opp-1608000000 {
+ opp-hz = /bits/ 64 <1608000000>;
+ opp-microvolt = <875000 875000 950000>,
+ <875000 875000 950000>;
+ clock-latency-ns = <40000>;
+ };
+ opp-1800000000 {
+ opp-hz = /bits/ 64 <1800000000>;
+ opp-microvolt = <950000 950000 950000>,
+ <950000 950000 950000>;
+ clock-latency-ns = <40000>;
+ };
+ };
+
+ cluster1_opp_table: opp-table-cluster1 {
+ compatible = "operating-points-v2";
+ opp-shared;
+
+ rockchip,grf = <&bigcore0_grf>;
+ rockchip,volt-mem-read-margin = <
+ 855000 1
+ 765000 2
+ 675000 3
+ 495000 4
+ >;
+
+ rockchip,reboot-freq = <1800000000>;
+
+ opp-408000000 {
+ opp-hz = /bits/ 64 <408000000>;
+ opp-microvolt = <600000 600000 1000000>,
+ <675000 675000 1000000>;
+ clock-latency-ns = <40000>;
+ opp-suspend;
+ };
+ opp-600000000 {
+ opp-hz = /bits/ 64 <600000000>;
+ opp-microvolt = <600000 600000 1000000>,
+ <675000 675000 1000000>;
+ clock-latency-ns = <40000>;
+ };
+ opp-816000000 {
+ opp-hz = /bits/ 64 <816000000>;
+ opp-microvolt = <600000 600000 1000000>,
+ <675000 675000 1000000>;
+ clock-latency-ns = <40000>;
+ };
+ opp-1008000000 {
+ opp-hz = /bits/ 64 <1008000000>;
+ opp-microvolt = <625000 625000 1000000>,
+ <675000 675000 1000000>;
+ clock-latency-ns = <40000>;
+ };
+ opp-1200000000 {
+ opp-hz = /bits/ 64 <1200000000>;
+ opp-microvolt = <650000 650000 1000000>,
+ <675000 675000 1000000>;
+ clock-latency-ns = <40000>;
+ };
+ opp-1416000000 {
+ opp-hz = /bits/ 64 <1416000000>;
+ opp-microvolt = <675000 675000 1000000>,
+ <675000 675000 1000000>;
+ clock-latency-ns = <40000>;
+ };
+ opp-1608000000 {
+ opp-hz = /bits/ 64 <1608000000>;
+ opp-microvolt = <700000 700000 1000000>,
+ <700000 700000 1000000>;
+ clock-latency-ns = <40000>;
+ };
+ opp-1800000000 {
+ opp-hz = /bits/ 64 <1800000000>;
+ opp-microvolt = <775000 775000 1000000>,
+ <775000 775000 1000000>;
+ clock-latency-ns = <40000>;
+ };
+ opp-2016000000 {
+ opp-hz = /bits/ 64 <2016000000>;
+ opp-microvolt = <850000 850000 1000000>,
+ <850000 850000 1000000>;
+ clock-latency-ns = <40000>;
+ };
+ opp-2208000000 {
+ opp-hz = /bits/ 64 <2208000000>;
+ opp-microvolt = <925000 925000 1000000>,
+ <925000 925000 1000000>;
+ clock-latency-ns = <40000>;
+ };
+ };
+
+ cluster2_opp_table: opp-table-cluster2 {
+ compatible = "operating-points-v2";
+ opp-shared;
+
+ rockchip,grf = <&bigcore1_grf>;
+ rockchip,volt-mem-read-margin = <
+ 855000 1
+ 765000 2
+ 675000 3
+ 495000 4
+ >;
+
+ rockchip,reboot-freq = <1800000000>;
+
+ opp-408000000 {
+ opp-hz = /bits/ 64 <408000000>;
+ opp-microvolt = <600000 600000 1000000>,
+ <675000 675000 1000000>;
+ clock-latency-ns = <40000>;
+ opp-suspend;
+ };
+ opp-600000000 {
+ opp-hz = /bits/ 64 <600000000>;
+ opp-microvolt = <600000 600000 1000000>,
+ <675000 675000 1000000>;
+ clock-latency-ns = <40000>;
+ };
+ opp-816000000 {
+ opp-hz = /bits/ 64 <816000000>;
+ opp-microvolt = <600000 600000 1000000>,
+ <675000 675000 1000000>;
+ clock-latency-ns = <40000>;
+ };
+ opp-1008000000 {
+ opp-hz = /bits/ 64 <1008000000>;
+ opp-microvolt = <625000 625000 1000000>,
+ <675000 675000 1000000>;
+ clock-latency-ns = <40000>;
+ };
+ opp-1200000000 {
+ opp-hz = /bits/ 64 <1200000000>;
+ opp-microvolt = <650000 650000 1000000>,
+ <675000 675000 1000000>;
+ clock-latency-ns = <40000>;
+ };
+ opp-1416000000 {
+ opp-hz = /bits/ 64 <1416000000>;
+ opp-microvolt = <675000 675000 1000000>,
+ <675000 675000 1000000>;
+ clock-latency-ns = <40000>;
+ };
+ opp-1608000000 {
+ opp-hz = /bits/ 64 <1608000000>;
+ opp-microvolt = <700000 700000 1000000>,
+ <700000 700000 1000000>;
+ clock-latency-ns = <40000>;
+ };
+ opp-1800000000 {
+ opp-hz = /bits/ 64 <1800000000>;
+ opp-microvolt = <775000 775000 1000000>,
+ <775000 775000 1000000>;
+ clock-latency-ns = <40000>;
+ };
+ opp-2016000000 {
+ opp-hz = /bits/ 64 <2016000000>;
+ opp-microvolt = <850000 850000 1000000>,
+ <850000 850000 1000000>;
+ clock-latency-ns = <40000>;
+ };
+ opp-2208000000 {
+ opp-hz = /bits/ 64 <2208000000>;
+ opp-microvolt = <925000 925000 1000000>,
+ <925000 925000 1000000>;
+ clock-latency-ns = <40000>;
+ };
+ };
+
cpus {
#address-cells = <1>;
#size-cells = <0>;
@@ -64,6 +274,7 @@ cpu_l0: cpu@0 {
clocks = <&scmi_clk SCMI_CLK_CPUL>;
assigned-clocks = <&scmi_clk SCMI_CLK_CPUL>;
assigned-clock-rates = <816000000>;
+ operating-points-v2 = <&cluster0_opp_table>;
cpu-idle-states = <&CPU_SLEEP>;
i-cache-size = <32768>;
i-cache-line-size = <64>;
@@ -83,6 +294,7 @@ cpu_l1: cpu@100 {
enable-method = "psci";
capacity-dmips-mhz = <530>;
clocks = <&scmi_clk SCMI_CLK_CPUL>;
+ operating-points-v2 = <&cluster0_opp_table>;
cpu-idle-states = <&CPU_SLEEP>;
i-cache-size = <32768>;
i-cache-line-size = <64>;
@@ -102,6 +314,7 @@ cpu_l2: cpu@200 {
enable-method = "psci";
capacity-dmips-mhz = <530>;
clocks = <&scmi_clk SCMI_CLK_CPUL>;
+ operating-points-v2 = <&cluster0_opp_table>;
cpu-idle-states = <&CPU_SLEEP>;
i-cache-size = <32768>;
i-cache-line-size = <64>;
@@ -121,6 +334,7 @@ cpu_l3: cpu@300 {
enable-method = "psci";
capacity-dmips-mhz = <530>;
clocks = <&scmi_clk SCMI_CLK_CPUL>;
+ operating-points-v2 = <&cluster0_opp_table>;
cpu-idle-states = <&CPU_SLEEP>;
i-cache-size = <32768>;
i-cache-line-size = <64>;
@@ -142,6 +356,7 @@ cpu_b0: cpu@400 {
clocks = <&scmi_clk SCMI_CLK_CPUB01>;
assigned-clocks = <&scmi_clk SCMI_CLK_CPUB01>;
assigned-clock-rates = <816000000>;
+ operating-points-v2 = <&cluster1_opp_table>;
cpu-idle-states = <&CPU_SLEEP>;
i-cache-size = <65536>;
i-cache-line-size = <64>;
@@ -161,6 +376,7 @@ cpu_b1: cpu@500 {
enable-method = "psci";
capacity-dmips-mhz = <1024>;
clocks = <&scmi_clk SCMI_CLK_CPUB01>;
+ operating-points-v2 = <&cluster1_opp_table>;
cpu-idle-states = <&CPU_SLEEP>;
i-cache-size = <65536>;
i-cache-line-size = <64>;
@@ -182,6 +398,7 @@ cpu_b2: cpu@600 {
clocks = <&scmi_clk SCMI_CLK_CPUB23>;
assigned-clocks = <&scmi_clk SCMI_CLK_CPUB23>;
assigned-clock-rates = <816000000>;
+ operating-points-v2 = <&cluster2_opp_table>;
cpu-idle-states = <&CPU_SLEEP>;
i-cache-size = <65536>;
i-cache-line-size = <64>;
@@ -201,6 +418,7 @@ cpu_b3: cpu@700 {
enable-method = "psci";
capacity-dmips-mhz = <1024>;
clocks = <&scmi_clk SCMI_CLK_CPUB23>;
+ operating-points-v2 = <&cluster2_opp_table>;
cpu-idle-states = <&CPU_SLEEP>;
i-cache-size = <65536>;
i-cache-line-size = <64>;
@@ -362,6 +580,230 @@ spll: clock-0 {
#clock-cells = <0>;
};
+ thermal_zones: thermal-zones {
+ soc_thermal: soc-thermal {
+ polling-delay-passive = <20>; /* milliseconds */
+ polling-delay = <1000>; /* milliseconds */
+ sustainable-power = <2100>; /* milliwatts */
+
+ thermal-sensors = <&tsadc 0>;
+ trips {
+ trip-point-0 {
+ temperature = <75000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+ soc_target: trip-point-1 {
+ temperature = <85000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+ trip-point-2 {
+ /* millicelsius */
+ temperature = <115000>;
+ /* millicelsius */
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+
+ cooling-maps {
+ map0 {
+ trip = <&soc_target>;
+ cooling-device = <&cpu_l0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu_l1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu_l2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu_l3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu_b0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu_b1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu_b2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu_b3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ contribution = <1024>;
+ };
+ };
+ };
+
+ bigcore0_thermal: bigcore0-thermal {
+ polling-delay-passive = <20>; /* milliseconds */
+ polling-delay = <1000>; /* milliseconds */
+ thermal-sensors = <&tsadc 1>;
+
+ trips {
+ trip-point-0 {
+ temperature = <75000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+ b0_target: trip-point-1 {
+ temperature = <85000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+ trip-point-2 {
+ /* millicelsius */
+ temperature = <115000>;
+ /* millicelsius */
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+
+ cooling-maps {
+ map0 {
+ trip = <&b0_target>;
+ cooling-device = <&cpu_b0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu_b1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ contribution = <1024>;
+ };
+ };
+ };
+
+ bigcore1_thermal: bigcore1-thermal {
+ polling-delay-passive = <20>; /* milliseconds */
+ polling-delay = <1000>; /* milliseconds */
+ thermal-sensors = <&tsadc 2>;
+ trips {
+ trip-point-0 {
+ temperature = <75000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+ b1_target: trip-point-1 {
+ temperature = <85000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+ trip-point-2 {
+ /* millicelsius */
+ temperature = <115000>;
+ /* millicelsius */
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+
+ cooling-maps {
+ map0 {
+ trip = <&b1_target>;
+ cooling-device = <&cpu_b2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu_b3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ contribution = <1024>;
+ };
+ };
+ };
+
+ little_core_thermal: littlecore-thermal {
+ polling-delay-passive = <20>; /* milliseconds */
+ polling-delay = <1000>; /* milliseconds */
+ thermal-sensors = <&tsadc 3>;
+ trips {
+ trip-point-0 {
+ temperature = <75000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+ l0_target: trip-point-1 {
+ temperature = <85000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+ trip-point-2 {
+ /* millicelsius */
+ temperature = <115000>;
+ /* millicelsius */
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+
+ cooling-maps {
+ map0 {
+ trip = <&l0_target>;
+ cooling-device = <&cpu_l0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu_l1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu_l2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
+ <&cpu_l3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ contribution = <1024>;
+ };
+ };
+ };
+
+ center_thermal: center-thermal {
+ polling-delay-passive = <20>; /* milliseconds */
+ polling-delay = <1000>; /* milliseconds */
+ thermal-sensors = <&tsadc 4>;
+ trips {
+ trip-point-0 {
+ temperature = <75000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+ trip-point-1 {
+ temperature = <85000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+ trip-point-2 {
+ /* millicelsius */
+ temperature = <115000>;
+ /* millicelsius */
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+ };
+
+ gpu_thermal: gpu-thermal {
+ polling-delay-passive = <20>; /* milliseconds */
+ polling-delay = <1000>; /* milliseconds */
+ thermal-sensors = <&tsadc 5>;
+ trips {
+ trip-point-0 {
+ temperature = <75000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+ trip-point-1 {
+ temperature = <85000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+ trip-point-2 {
+ /* millicelsius */
+ temperature = <115000>;
+ /* millicelsius */
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+ };
+
+ npu_thermal: npu-thermal {
+ polling-delay-passive = <20>; /* milliseconds */
+ polling-delay = <1000>; /* milliseconds */
+ thermal-sensors = <&tsadc 6>;
+ trips {
+ trip-point-0 {
+ temperature = <75000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+ trip-point-1 {
+ temperature = <85000>;
+ hysteresis = <2000>;
+ type = "passive";
+ };
+ trip-point-2 {
+ /* millicelsius */
+ temperature = <115000>;
+ /* millicelsius */
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+ };
+ };
+
timer {
compatible = "arm,armv8-timer";
interrupts = <GIC_PPI 13 IRQ_TYPE_LEVEL_HIGH 0>,
@@ -491,6 +933,16 @@ sys_grf: syscon@fd58c000 {
reg = <0x0 0xfd58c000 0x0 0x1000>;
};
+ bigcore0_grf: syscon@fd590000 {
+ compatible = "rockchip,rk3588-bigcore0-grf", "syscon";
+ reg = <0x0 0xfd590000 0x0 0x100>;
+ };
+
+ bigcore1_grf: syscon@fd592000 {
+ compatible = "rockchip,rk3588-bigcore1-grf", "syscon";
+ reg = <0x0 0xfd592000 0x0 0x100>;
+ };
+
php_grf: syscon@fd5b0000 {
compatible = "rockchip,rk3588-php-grf", "syscon";
reg = <0x0 0xfd5b0000 0x0 0x1000>;
--
2.41.0