Skip to content

Commit

Permalink
sunxi: backport H616 cpufreq support
Browse files Browse the repository at this point in the history
  • Loading branch information
aiamadeus committed Jul 10, 2024
1 parent 33489cf commit 6a9ab8e
Show file tree
Hide file tree
Showing 24 changed files with 1,676 additions and 646 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
From 9cf3415ade2d7598d78d2ce6d35d6d6d06132201 Mon Sep 17 00:00:00 2001
From: Martin Botka <martin.botka@somainline.org>
Date: Thu, 18 Apr 2024 16:44:01 +0100
Subject: [PATCH] firmware: smccc: Export revision soc_id function

The "SoC ID revision" as provided via the SMCCC SOCID interface can be
valuable information for drivers, when certain functionality depends
on a die revision, for instance.
One example is the sun50i-cpufreq-nvmem driver, which needs this
information to determine the speed bin of the SoC.

Export the arm_smccc_get_soc_id_revision() function so that it can be
called by any driver.

Signed-off-by: Martin Botka <martin.botka@somainline.org>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Acked-by: Sudeep Holla <sudeep.holla@arm.com>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
drivers/firmware/smccc/smccc.c | 1 +
1 file changed, 1 insertion(+)

--- a/drivers/firmware/smccc/smccc.c
+++ b/drivers/firmware/smccc/smccc.c
@@ -69,6 +69,7 @@ s32 arm_smccc_get_soc_id_revision(void)
{
return smccc_soc_id_revision;
}
+EXPORT_SYMBOL_GPL(arm_smccc_get_soc_id_revision);

static int __init smccc_devices_init(void)
{
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
From 6ae07744cf334b750762ba881492c0cfba524b38 Mon Sep 17 00:00:00 2001
From: Martin Botka <martin.botka@somainline.org>
Date: Thu, 18 Apr 2024 16:44:02 +0100
Subject: [PATCH] cpufreq: dt-platdev: Blocklist Allwinner H616/618 SoCs

The AllWinner H616 SoC will use the (extended) H6 OPP driver, so add
them to the cpufreq-dt blocklist, to not create the device twice.
This also affects the closely related sibling SoCs H618 and H700.

Signed-off-by: Martin Botka <martin.botka@somainline.org>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Reviewed-by: Jernej Skrabec <jernej.skrabec@gmail.com>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
drivers/cpufreq/cpufreq-dt-platdev.c | 3 +++
1 file changed, 3 insertions(+)

--- a/drivers/cpufreq/cpufreq-dt-platdev.c
+++ b/drivers/cpufreq/cpufreq-dt-platdev.c
@@ -102,6 +102,9 @@ static const struct of_device_id allowli
*/
static const struct of_device_id blocklist[] __initconst = {
{ .compatible = "allwinner,sun50i-h6", },
+ { .compatible = "allwinner,sun50i-h616", },
+ { .compatible = "allwinner,sun50i-h618", },
+ { .compatible = "allwinner,sun50i-h700", },

{ .compatible = "arm,vexpress", },

Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
From 6cc4bcceff9af0e6be9738096d95e4ba75e75123 Mon Sep 17 00:00:00 2001
From: Brandon Cheo Fusi <fusibrandon13@gmail.com>
Date: Thu, 18 Apr 2024 16:44:04 +0100
Subject: [PATCH] cpufreq: sun50i: Refactor speed bin decoding

Make converting the speed bin value into a speed grade generic and
determined by a platform specific callback. Also change the prototypes
involved to encode the speed bin directly in the return value.

This allows to extend the driver more easily to support more SoCs.

Signed-off-by: Brandon Cheo Fusi <fusibrandon13@gmail.com>
[Andre: merge output into return value]
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Reviewed-by: Jernej Skrabec <jernej.skrabec@gmail.com>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
drivers/cpufreq/sun50i-cpufreq-nvmem.c | 74 +++++++++++++++++---------
1 file changed, 49 insertions(+), 25 deletions(-)

--- a/drivers/cpufreq/sun50i-cpufreq-nvmem.c
+++ b/drivers/cpufreq/sun50i-cpufreq-nvmem.c
@@ -24,19 +24,52 @@

static struct platform_device *cpufreq_dt_pdev, *sun50i_cpufreq_pdev;

+struct sunxi_cpufreq_data {
+ u32 (*efuse_xlate)(u32 speedbin);
+};
+
+static u32 sun50i_h6_efuse_xlate(u32 speedbin)
+{
+ u32 efuse_value;
+
+ efuse_value = (speedbin >> NVMEM_SHIFT) & NVMEM_MASK;
+
+ /*
+ * We treat unexpected efuse values as if the SoC was from
+ * the slowest bin. Expected efuse values are 1-3, slowest
+ * to fastest.
+ */
+ if (efuse_value >= 1 && efuse_value <= 3)
+ return efuse_value - 1;
+ else
+ return 0;
+}
+
+static struct sunxi_cpufreq_data sun50i_h6_cpufreq_data = {
+ .efuse_xlate = sun50i_h6_efuse_xlate,
+};
+
+static const struct of_device_id cpu_opp_match_list[] = {
+ { .compatible = "allwinner,sun50i-h6-operating-points",
+ .data = &sun50i_h6_cpufreq_data,
+ },
+ {}
+};
+
/**
* sun50i_cpufreq_get_efuse() - Determine speed grade from efuse value
- * @versions: Set to the value parsed from efuse
*
- * Returns 0 if success.
+ * Returns non-negative speed bin index on success, a negative error
+ * value otherwise.
*/
-static int sun50i_cpufreq_get_efuse(u32 *versions)
+static int sun50i_cpufreq_get_efuse(void)
{
+ const struct sunxi_cpufreq_data *opp_data;
struct nvmem_cell *speedbin_nvmem;
+ const struct of_device_id *match;
struct device_node *np;
struct device *cpu_dev;
- u32 *speedbin, efuse_value;
- size_t len;
+ u32 *speedbin;
int ret;

cpu_dev = get_cpu_device(0);
@@ -47,12 +80,12 @@ static int sun50i_cpufreq_get_efuse(u32
if (!np)
return -ENOENT;

- ret = of_device_is_compatible(np,
- "allwinner,sun50i-h6-operating-points");
- if (!ret) {
+ match = of_match_node(cpu_opp_match_list, np);
+ if (!match) {
of_node_put(np);
return -ENOENT;
}
+ opp_data = match->data;

speedbin_nvmem = of_nvmem_cell_get(np, NULL);
of_node_put(np);
@@ -60,25 +93,16 @@ static int sun50i_cpufreq_get_efuse(u32
return dev_err_probe(cpu_dev, PTR_ERR(speedbin_nvmem),
"Could not get nvmem cell\n");

- speedbin = nvmem_cell_read(speedbin_nvmem, &len);
+ speedbin = nvmem_cell_read(speedbin_nvmem, NULL);
nvmem_cell_put(speedbin_nvmem);
if (IS_ERR(speedbin))
return PTR_ERR(speedbin);

- efuse_value = (*speedbin >> NVMEM_SHIFT) & NVMEM_MASK;
-
- /*
- * We treat unexpected efuse values as if the SoC was from
- * the slowest bin. Expected efuse values are 1-3, slowest
- * to fastest.
- */
- if (efuse_value >= 1 && efuse_value <= 3)
- *versions = efuse_value - 1;
- else
- *versions = 0;
+ ret = opp_data->efuse_xlate(*speedbin);

kfree(speedbin);
- return 0;
+
+ return ret;
};

static int sun50i_cpufreq_nvmem_probe(struct platform_device *pdev)
@@ -86,7 +110,7 @@ static int sun50i_cpufreq_nvmem_probe(st
int *opp_tokens;
char name[MAX_NAME_LEN];
unsigned int cpu;
- u32 speed = 0;
+ int speed;
int ret;

opp_tokens = kcalloc(num_possible_cpus(), sizeof(*opp_tokens),
@@ -94,10 +118,10 @@ static int sun50i_cpufreq_nvmem_probe(st
if (!opp_tokens)
return -ENOMEM;

- ret = sun50i_cpufreq_get_efuse(&speed);
- if (ret) {
+ speed = sun50i_cpufreq_get_efuse();
+ if (speed < 0) {
kfree(opp_tokens);
- return ret;
+ return speed;
}

snprintf(name, MAX_NAME_LEN, "speed%d", speed);
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
From fa5aec9561cfc4f4370983ca5818c90227c9d90e Mon Sep 17 00:00:00 2001
From: Andre Przywara <andre.przywara@arm.com>
Date: Thu, 18 Apr 2024 16:44:05 +0100
Subject: [PATCH] cpufreq: sun50i: Add support for opp_supported_hw

The opp_supported_hw DT property allows the DT to specify a mask of chip
revisions that a certain OPP is eligible for. This allows for easy
limiting of maximum frequencies, for instance.

Add support for that in the sun50i-cpufreq-nvmem driver. We support both
the existing opp-microvolt suffix properties as well as the
opp-supported-hw property, the generic code figures out which is needed
automatically.
However if none of the DT OPP nodes contain an opp-supported-hw
property, the core code will ignore all OPPs and the driver will fail
probing. So check the DT's eligibility first before using that feature.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Reviewed-by: Jernej Skrabec <jernej.skrabec@gmail.com>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
drivers/cpufreq/sun50i-cpufreq-nvmem.c | 62 ++++++++++++++++++++++----
1 file changed, 54 insertions(+), 8 deletions(-)

--- a/drivers/cpufreq/sun50i-cpufreq-nvmem.c
+++ b/drivers/cpufreq/sun50i-cpufreq-nvmem.c
@@ -57,6 +57,41 @@ static const struct of_device_id cpu_opp
};

/**
+ * dt_has_supported_hw() - Check if any OPPs use opp-supported-hw
+ *
+ * If we ask the cpufreq framework to use the opp-supported-hw feature, it
+ * will ignore every OPP node without that DT property. If none of the OPPs
+ * have it, the driver will fail probing, due to the lack of OPPs.
+ *
+ * Returns true if we have at least one OPP with the opp-supported-hw property.
+ */
+static bool dt_has_supported_hw(void)
+{
+ bool has_opp_supported_hw = false;
+ struct device_node *np, *opp;
+ struct device *cpu_dev;
+
+ cpu_dev = get_cpu_device(0);
+ if (!cpu_dev)
+ return -ENODEV;
+
+ np = dev_pm_opp_of_get_opp_desc_node(cpu_dev);
+ if (!np)
+ return -ENOENT;
+
+ for_each_child_of_node(np, opp) {
+ if (of_find_property(opp, "opp-supported-hw", NULL)) {
+ has_opp_supported_hw = true;
+ break;
+ }
+ }
+
+ of_node_put(np);
+
+ return has_opp_supported_hw;
+}
+
+/**
* sun50i_cpufreq_get_efuse() - Determine speed grade from efuse value
*
* Returns non-negative speed bin index on success, a negative error
@@ -109,7 +144,8 @@ static int sun50i_cpufreq_nvmem_probe(st
{
int *opp_tokens;
char name[MAX_NAME_LEN];
- unsigned int cpu;
+ unsigned int cpu, supported_hw;
+ struct dev_pm_opp_config config = {};
int speed;
int ret;

@@ -124,7 +160,18 @@ static int sun50i_cpufreq_nvmem_probe(st
return speed;
}

+ /*
+ * We need at least one OPP with the "opp-supported-hw" property,
+ * or else the upper layers will ignore every OPP and will bail out.
+ */
+ if (dt_has_supported_hw()) {
+ supported_hw = 1U << speed;
+ config.supported_hw = &supported_hw;
+ config.supported_hw_count = 1;
+ }
+
snprintf(name, MAX_NAME_LEN, "speed%d", speed);
+ config.prop_name = name;

for_each_possible_cpu(cpu) {
struct device *cpu_dev = get_cpu_device(cpu);
@@ -134,12 +181,11 @@ static int sun50i_cpufreq_nvmem_probe(st
goto free_opp;
}

- opp_tokens[cpu] = dev_pm_opp_set_prop_name(cpu_dev, name);
- if (opp_tokens[cpu] < 0) {
- ret = opp_tokens[cpu];
- pr_err("Failed to set prop name\n");
+ ret = dev_pm_opp_set_config(cpu_dev, &config);
+ if (ret < 0)
goto free_opp;
- }
+
+ opp_tokens[cpu] = ret;
}

cpufreq_dt_pdev = platform_device_register_simple("cpufreq-dt", -1,
@@ -154,7 +200,7 @@ static int sun50i_cpufreq_nvmem_probe(st

free_opp:
for_each_possible_cpu(cpu)
- dev_pm_opp_put_prop_name(opp_tokens[cpu]);
+ dev_pm_opp_clear_config(opp_tokens[cpu]);
kfree(opp_tokens);

return ret;
@@ -168,7 +214,7 @@ static int sun50i_cpufreq_nvmem_remove(s
platform_device_unregister(cpufreq_dt_pdev);

for_each_possible_cpu(cpu)
- dev_pm_opp_put_prop_name(opp_tokens[cpu]);
+ dev_pm_opp_clear_config(opp_tokens[cpu]);

kfree(opp_tokens);

Loading

0 comments on commit 6a9ab8e

Please sign in to comment.