* [PATCH] cpufreq: loongson3: Support older SMC firmware
@ 2025-06-23 12:33 Yao Zi
2025-06-23 14:59 ` Xi Ruoyao
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Yao Zi @ 2025-06-23 12:33 UTC (permalink / raw)
To: Huacai Chen, WANG Xuerui, Rafael J. Wysocki, Viresh Kumar
Cc: Jiaxun Yang, Mingcong Bai, Kexy Biscuit, loongarch, linux-pm,
linux-kernel, Yao Zi
SMC firmware found on many on-market LoongArch devices implement a
different ABI than what has been implemented in the current upstream
driver. They significantly differ in the following ways:
- CMD_GET_VERSION returns 0.
- There is no known SMC call to obtain corresponding frequencies for
each frequency level. The frequency table must therefore be calculated
with CPU clock frequency from scratch.
- There is no known SMC call to obtain the current frequency level.
- The main processor must determine the set of cores able to run at
boost frequency and enable boosting manually.
- SMC call response format may vary between commands.
This patch adds support for the SMC firmware found on these devices,
which I denoted as "SMC-0" in the driver. Boost support is omitted,
since determination of cores able to boost requires the driver to couple
tightly with micro-architecture details.
For coexistence, I prefixed all SMC-call constants with their SMC
versions, and introduced "SMC-0"-specific initialization and
frequency-level-setup rountines.
Signed-off-by: Yao Zi <ziyao@disroot.org>
---
drivers/cpufreq/loongson3_cpufreq.c | 287 ++++++++++++++++++++--------
1 file changed, 211 insertions(+), 76 deletions(-)
diff --git a/drivers/cpufreq/loongson3_cpufreq.c b/drivers/cpufreq/loongson3_cpufreq.c
index 1e8715ea1b77..b61b2e712694 100644
--- a/drivers/cpufreq/loongson3_cpufreq.c
+++ b/drivers/cpufreq/loongson3_cpufreq.c
@@ -16,10 +16,13 @@
#include <asm/idle.h>
#include <asm/loongarch.h>
#include <asm/loongson.h>
+#include <asm/time.h>
/* Message */
union smc_message {
u32 value;
+
+ /* Generic message type */
struct {
u32 id : 4;
u32 info : 4;
@@ -28,6 +31,15 @@ union smc_message {
u32 extra : 1;
u32 complete : 1;
};
+
+ /* Returned by SMC0_GET_DVFS_INFO */
+ struct {
+ u32 min_level : 4;
+ u32 max_level : 4;
+ u32 boost_freq : 16;
+ u32 normal_core_limit : 4;
+ u32 boost_cores : 4;
+ };
};
/* Command return values */
@@ -44,136 +56,153 @@ union smc_message {
*/
#define CMD_GET_VERSION 0x1
+/* SMC version 0 service calls */
+/*
+ * SMC0_CMD_SET_FREQ_LEVEL - Set frequency level
+ * Input: CPU ID, level ID
+ * Output: none
+ */
+#define SMC0_CMD_SET_FREQ_LEVEL 0x21
+
+/*
+ * SMC0_CMD_GET_DVFS_INFO - Get DVFS information
+ * Input: CPU ID
+ * Output: DVFS Information
+ */
+#define SMC0_CMD_GET_DVFS_INFO 0x22
+
+/* SMC version 1 service calls */
/* Feature commands */
/*
- * CMD_GET_FEATURE - Get feature state
+ * SMC1_CMD_GET_FEATURE - Get feature state
* Input: feature ID
* Output: feature flag
*/
-#define CMD_GET_FEATURE 0x2
+#define SMC1_CMD_GET_FEATURE 0x2
/*
- * CMD_SET_FEATURE - Set feature state
+ * SMC1_CMD_SET_FEATURE - Set feature state
* Input: feature ID, feature flag
* output: none
*/
-#define CMD_SET_FEATURE 0x3
+#define SMC1_CMD_SET_FEATURE 0x3
/* Feature IDs */
-#define FEATURE_SENSOR 0
-#define FEATURE_FAN 1
-#define FEATURE_DVFS 2
+#define SMC1_FEATURE_SENSOR 0
+#define SMC1_FEATURE_FAN 1
+#define SMC1_FEATURE_DVFS 2
/* Sensor feature flags */
-#define FEATURE_SENSOR_ENABLE BIT(0)
-#define FEATURE_SENSOR_SAMPLE BIT(1)
+#define SMC1_FEATURE_SENSOR_ENABLE BIT(0)
+#define SMC1_FEATURE_SENSOR_SAMPLE BIT(1)
/* Fan feature flags */
-#define FEATURE_FAN_ENABLE BIT(0)
-#define FEATURE_FAN_AUTO BIT(1)
+#define SMC1_FEATURE_FAN_ENABLE BIT(0)
+#define SMC1_FEATURE_FAN_AUTO BIT(1)
/* DVFS feature flags */
-#define FEATURE_DVFS_ENABLE BIT(0)
-#define FEATURE_DVFS_BOOST BIT(1)
-#define FEATURE_DVFS_AUTO BIT(2)
-#define FEATURE_DVFS_SINGLE_BOOST BIT(3)
+#define SMC1_FEATURE_DVFS_ENABLE BIT(0)
+#define SMC1_FEATURE_DVFS_BOOST BIT(1)
+#define SMC1_FEATURE_DVFS_AUTO BIT(2)
+#define SMC1_FEATURE_DVFS_SINGLE_BOOST BIT(3)
/* Sensor commands */
/*
- * CMD_GET_SENSOR_NUM - Get number of sensors
+ * SMC1_CMD_GET_SENSOR_NUM - Get number of sensors
* Input: none
* Output: number
*/
-#define CMD_GET_SENSOR_NUM 0x4
+#define SMC1_CMD_GET_SENSOR_NUM 0x4
/*
- * CMD_GET_SENSOR_STATUS - Get sensor status
+ * SMC1_CMD_GET_SENSOR_STATUS - Get sensor status
* Input: sensor ID, type
* Output: sensor status
*/
-#define CMD_GET_SENSOR_STATUS 0x5
+#define SMC1_CMD_GET_SENSOR_STATUS 0x5
/* Sensor types */
-#define SENSOR_INFO_TYPE 0
-#define SENSOR_INFO_TYPE_TEMP 1
+#define SMC1_SENSOR_INFO_TYPE 0
+#define SMC1_SENSOR_INFO_TYPE_TEMP 1
/* Fan commands */
/*
- * CMD_GET_FAN_NUM - Get number of fans
+ * SMC1_CMD_GET_FAN_NUM - Get number of fans
* Input: none
* Output: number
*/
-#define CMD_GET_FAN_NUM 0x6
+#define SMC1_CMD_GET_FAN_NUM 0x6
/*
- * CMD_GET_FAN_INFO - Get fan status
+ * SMC1_CMD_GET_FAN_INFO - Get fan status
* Input: fan ID, type
* Output: fan info
*/
-#define CMD_GET_FAN_INFO 0x7
+#define SMC1_CMD_GET_FAN_INFO 0x7
/*
- * CMD_SET_FAN_INFO - Set fan status
+ * SMC1_CMD_SET_FAN_INFO - Set fan status
* Input: fan ID, type, value
* Output: none
*/
-#define CMD_SET_FAN_INFO 0x8
+#define SMC1_CMD_SET_FAN_INFO 0x8
/* Fan types */
-#define FAN_INFO_TYPE_LEVEL 0
+#define SMC1_FAN_INFO_TYPE_LEVEL 0
/* DVFS commands */
/*
- * CMD_GET_FREQ_LEVEL_NUM - Get number of freq levels
+ * SMC1_CMD_GET_FREQ_LEVEL_NUM - Get number of freq levels
* Input: CPU ID
* Output: number
*/
-#define CMD_GET_FREQ_LEVEL_NUM 0x9
+#define SMC1_CMD_GET_FREQ_LEVEL_NUM 0x9
/*
- * CMD_GET_FREQ_BOOST_LEVEL - Get the first boost level
+ * SMC1_CMD_GET_FREQ_BOOST_LEVEL - Get the first boost level
* Input: CPU ID
* Output: number
*/
-#define CMD_GET_FREQ_BOOST_LEVEL 0x10
+#define SMC1_CMD_GET_FREQ_BOOST_LEVEL 0x10
/*
- * CMD_GET_FREQ_LEVEL_INFO - Get freq level info
+ * SMC1_CMD_GET_FREQ_LEVEL_INFO - Get freq level info
* Input: CPU ID, level ID
* Output: level info
*/
-#define CMD_GET_FREQ_LEVEL_INFO 0x11
+#define SMC1_CMD_GET_FREQ_LEVEL_INFO 0x11
/*
- * CMD_GET_FREQ_INFO - Get freq info
+ * SMC1_CMD_GET_FREQ_INFO - Get freq info
* Input: CPU ID, type
* Output: freq info
*/
-#define CMD_GET_FREQ_INFO 0x12
+#define SMC1_CMD_GET_FREQ_INFO 0x12
/*
- * CMD_SET_FREQ_INFO - Set freq info
+ * SMC1_CMD_SET_FREQ_INFO - Set freq info
* Input: CPU ID, type, value
* Output: none
*/
-#define CMD_SET_FREQ_INFO 0x13
+#define SMC1_CMD_SET_FREQ_INFO 0x13
/* Freq types */
-#define FREQ_INFO_TYPE_FREQ 0
-#define FREQ_INFO_TYPE_LEVEL 1
+#define SMC1_FREQ_INFO_TYPE_FREQ 0
+#define SMC1_FREQ_INFO_TYPE_LEVEL 1
-#define FREQ_MAX_LEVEL 16
+#define SMC1_FREQ_MAX_LEVEL 16
struct loongson3_freq_data {
- unsigned int def_freq_level;
+ unsigned int min_freq_level, def_freq_level;
struct cpufreq_frequency_table table[];
};
static struct mutex cpufreq_mutex[MAX_PACKAGES];
-static struct cpufreq_driver loongson3_cpufreq_driver;
+static struct cpufreq_driver *loongson3_cpufreq_current_driver;
static DEFINE_PER_CPU(struct loongson3_freq_data *, freq_data);
-static inline int do_service_request(u32 id, u32 info, u32 cmd, u32 val, u32 extra)
+static inline int do_service_request_raw(u32 id, u32 info, u32 cmd, u32 val,
+ u32 extra, union smc_message *raw)
{
int retries;
unsigned int cpu = raw_smp_processor_id();
@@ -214,29 +243,90 @@ static inline int do_service_request(u32 id, u32 info, u32 cmd, u32 val, u32 ext
mutex_unlock(&cpufreq_mutex[package]);
+ if (raw)
+ *raw = msg;
+
return msg.val;
}
-static unsigned int loongson3_cpufreq_get(unsigned int cpu)
+#define do_service_request(id, info, cmd, val, extra) \
+ do_service_request_raw(id, info, cmd, val, extra, NULL)
+
+static unsigned int loongson3_cpufreq_smc1_get(unsigned int cpu)
{
int ret;
- ret = do_service_request(cpu, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_INFO, 0, 0);
+ ret = do_service_request(cpu, SMC1_FREQ_INFO_TYPE_FREQ,
+ SMC1_CMD_GET_FREQ_INFO, 0, 0);
return ret * KILO;
}
-static int loongson3_cpufreq_target(struct cpufreq_policy *policy, unsigned int index)
+static int loongson3_cpufreq_smc0_target(struct cpufreq_policy *policy, unsigned int index)
+{
+ unsigned int cpu = policy->cpu;
+ int ret;
+
+ index += per_cpu(freq_data, cpu)->min_freq_level;
+
+ ret = do_service_request(cpu_data[cpu].core, index, SMC0_CMD_SET_FREQ_LEVEL,
+ 0, 0);
+
+ return (ret >= 0) ? 0 : ret;
+}
+
+static int loongson3_cpufreq_smc1_target(struct cpufreq_policy *policy, unsigned int index)
{
int ret;
ret = do_service_request(cpu_data[policy->cpu].core,
- FREQ_INFO_TYPE_LEVEL, CMD_SET_FREQ_INFO, index, 0);
+ SMC1_FREQ_INFO_TYPE_LEVEL, SMC1_CMD_SET_FREQ_INFO, index, 0);
return (ret >= 0) ? 0 : ret;
}
-static int configure_freq_table(int cpu)
+static int configure_smc0_freq_table(int cpu)
+{
+ struct platform_device *pdev = cpufreq_get_driver_data();
+ struct loongson3_freq_data *data;
+ int ret, freq_level, i;
+ union smc_message msg;
+
+ if (per_cpu(freq_data, cpu))
+ return 0;
+
+ ret = do_service_request_raw(cpu, 0, SMC0_CMD_GET_DVFS_INFO, 0, 0, &msg);
+ if (ret < 0)
+ return ret;
+
+ freq_level = msg.max_level - msg.min_level + 1;
+ data = devm_kzalloc(&pdev->dev, struct_size(data, table, freq_level + 1),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->def_freq_level = 0;
+ data->min_freq_level = msg.min_level;
+
+ for (i = 0; i < freq_level; i++) {
+ unsigned long frequency;
+
+ frequency = cpu_clock_freq / KILO;
+ frequency = frequency * (freq_level - i) / freq_level;
+
+ data->table[i].frequency = frequency;
+ data->table[i].flags = 0;
+ }
+
+ data->table[freq_level].frequency = CPUFREQ_TABLE_END;
+ data->table[freq_level].flags = 0;
+
+ per_cpu(freq_data, cpu) = data;
+
+ return 0;
+}
+
+static int configure_smc1_freq_table(int cpu)
{
int i, ret, boost_level, max_level, freq_level;
struct platform_device *pdev = cpufreq_get_driver_data();
@@ -245,17 +335,17 @@ static int configure_freq_table(int cpu)
if (per_cpu(freq_data, cpu))
return 0;
- ret = do_service_request(cpu, 0, CMD_GET_FREQ_LEVEL_NUM, 0, 0);
+ ret = do_service_request(cpu, 0, SMC1_CMD_GET_FREQ_LEVEL_NUM, 0, 0);
if (ret < 0)
return ret;
max_level = ret;
- ret = do_service_request(cpu, 0, CMD_GET_FREQ_BOOST_LEVEL, 0, 0);
+ ret = do_service_request(cpu, 0, SMC1_CMD_GET_FREQ_BOOST_LEVEL, 0, 0);
if (ret < 0)
return ret;
boost_level = ret;
- freq_level = min(max_level, FREQ_MAX_LEVEL);
+ freq_level = min(max_level, SMC1_FREQ_MAX_LEVEL);
data = devm_kzalloc(&pdev->dev, struct_size(data, table, freq_level + 1), GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -263,7 +353,8 @@ static int configure_freq_table(int cpu)
data->def_freq_level = boost_level - 1;
for (i = 0; i < freq_level; i++) {
- ret = do_service_request(cpu, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_LEVEL_INFO, i, 0);
+ ret = do_service_request(cpu, SMC1_FREQ_INFO_TYPE_FREQ,
+ SMC1_CMD_GET_FREQ_LEVEL_INFO, i, 0);
if (ret < 0) {
devm_kfree(&pdev->dev, data);
return ret;
@@ -281,32 +372,56 @@ static int configure_freq_table(int cpu)
return 0;
}
-static int loongson3_cpufreq_cpu_init(struct cpufreq_policy *policy)
+static void loongson3_cpufreq_init_data(struct cpufreq_policy *policy)
{
- int i, ret, cpu = policy->cpu;
-
- ret = configure_freq_table(cpu);
- if (ret < 0)
- return ret;
+ struct loongson3_freq_data *data;
+ int i, cpu = policy->cpu;
+ data = per_cpu(freq_data, cpu);
policy->cpuinfo.transition_latency = 10000;
- policy->freq_table = per_cpu(freq_data, cpu)->table;
- policy->suspend_freq = policy->freq_table[per_cpu(freq_data, cpu)->def_freq_level].frequency;
+ policy->freq_table = data->table;
+ policy->suspend_freq = data->table[data->def_freq_level].frequency;
cpumask_copy(policy->cpus, topology_sibling_cpumask(cpu));
for_each_cpu(i, policy->cpus) {
if (i != cpu)
per_cpu(freq_data, i) = per_cpu(freq_data, cpu);
}
+}
+
+static int loongson3_cpufreq_cpu_smc0_init(struct cpufreq_policy *policy)
+{
+ int ret, cpu = policy->cpu;
+
+ ret = configure_smc0_freq_table(cpu);
+ if (ret < 0)
+ return ret;
+
+ loongson3_cpufreq_init_data(policy);
+
+ return 0;
+}
+
+static int loongson3_cpufreq_cpu_smc1_init(struct cpufreq_policy *policy)
+{
+ int ret, cpu = policy->cpu;
+
+ ret = configure_smc1_freq_table(cpu);
+ if (ret < 0)
+ return ret;
+
+ loongson3_cpufreq_init_data(policy);
return 0;
}
static void loongson3_cpufreq_cpu_exit(struct cpufreq_policy *policy)
{
+ unsigned int def_freq_level;
int cpu = policy->cpu;
- loongson3_cpufreq_target(policy, per_cpu(freq_data, cpu)->def_freq_level);
+ def_freq_level = per_cpu(freq_data, cpu)->def_freq_level;
+ loongson3_cpufreq_current_driver->target_index(policy, def_freq_level);
}
static int loongson3_cpufreq_cpu_online(struct cpufreq_policy *policy)
@@ -319,15 +434,27 @@ static int loongson3_cpufreq_cpu_offline(struct cpufreq_policy *policy)
return 0;
}
-static struct cpufreq_driver loongson3_cpufreq_driver = {
+static struct cpufreq_driver loongson3_cpufreq_smc0_driver = {
.name = "loongson3",
.flags = CPUFREQ_CONST_LOOPS,
- .init = loongson3_cpufreq_cpu_init,
+ .init = loongson3_cpufreq_cpu_smc0_init,
.exit = loongson3_cpufreq_cpu_exit,
.online = loongson3_cpufreq_cpu_online,
.offline = loongson3_cpufreq_cpu_offline,
- .get = loongson3_cpufreq_get,
- .target_index = loongson3_cpufreq_target,
+ .target_index = loongson3_cpufreq_smc0_target,
+ .verify = cpufreq_generic_frequency_table_verify,
+ .suspend = cpufreq_generic_suspend,
+};
+
+static struct cpufreq_driver loongson3_cpufreq_smc1_driver = {
+ .name = "loongson3",
+ .flags = CPUFREQ_CONST_LOOPS,
+ .init = loongson3_cpufreq_cpu_smc1_init,
+ .exit = loongson3_cpufreq_cpu_exit,
+ .online = loongson3_cpufreq_cpu_online,
+ .offline = loongson3_cpufreq_cpu_offline,
+ .get = loongson3_cpufreq_smc1_get,
+ .target_index = loongson3_cpufreq_smc1_target,
.verify = cpufreq_generic_frequency_table_verify,
.set_boost = cpufreq_boost_set_sw,
.suspend = cpufreq_generic_suspend,
@@ -335,7 +462,7 @@ static struct cpufreq_driver loongson3_cpufreq_driver = {
static int loongson3_cpufreq_probe(struct platform_device *pdev)
{
- int i, ret;
+ int i, ret, version;
for (i = 0; i < MAX_PACKAGES; i++) {
ret = devm_mutex_init(&pdev->dev, &cpufreq_mutex[i]);
@@ -343,18 +470,26 @@ static int loongson3_cpufreq_probe(struct platform_device *pdev)
return ret;
}
- ret = do_service_request(0, 0, CMD_GET_VERSION, 0, 0);
- if (ret <= 0)
+ version = do_service_request(0, 0, CMD_GET_VERSION, 0, 0);
+ if (version < 0)
return -EPERM;
- ret = do_service_request(FEATURE_DVFS, 0, CMD_SET_FEATURE,
- FEATURE_DVFS_ENABLE | FEATURE_DVFS_BOOST, 0);
- if (ret < 0)
- return -EPERM;
+ pr_info("loongson3_cpufreq: firmware version %d\n", version);
+
+ if (version == 0) {
+ loongson3_cpufreq_current_driver = &loongson3_cpufreq_smc0_driver;
+ } else {
+ ret = do_service_request(SMC1_FEATURE_DVFS, 0, SMC1_CMD_SET_FEATURE,
+ SMC1_FEATURE_DVFS_ENABLE | SMC1_FEATURE_DVFS_BOOST, 0);
+ if (ret < 0)
+ return -EPERM;
+
+ loongson3_cpufreq_current_driver = &loongson3_cpufreq_smc1_driver;
+ }
- loongson3_cpufreq_driver.driver_data = pdev;
+ loongson3_cpufreq_current_driver->driver_data = pdev;
- ret = cpufreq_register_driver(&loongson3_cpufreq_driver);
+ ret = cpufreq_register_driver(loongson3_cpufreq_current_driver);
if (ret)
return ret;
@@ -365,7 +500,7 @@ static int loongson3_cpufreq_probe(struct platform_device *pdev)
static void loongson3_cpufreq_remove(struct platform_device *pdev)
{
- cpufreq_unregister_driver(&loongson3_cpufreq_driver);
+ cpufreq_unregister_driver(loongson3_cpufreq_current_driver);
}
static struct platform_device_id cpufreq_id_table[] = {
--
2.49.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH] cpufreq: loongson3: Support older SMC firmware
2025-06-23 12:33 [PATCH] cpufreq: loongson3: Support older SMC firmware Yao Zi
@ 2025-06-23 14:59 ` Xi Ruoyao
2025-06-24 1:42 ` Yao Zi
2025-06-24 2:43 ` Mingcong Bai
2025-06-25 3:47 ` Yao Zi
2 siblings, 1 reply; 5+ messages in thread
From: Xi Ruoyao @ 2025-06-23 14:59 UTC (permalink / raw)
To: Yao Zi, Huacai Chen, WANG Xuerui, Rafael J. Wysocki, Viresh Kumar
Cc: Jiaxun Yang, Mingcong Bai, Kexy Biscuit, loongarch, linux-pm,
linux-kernel
On Mon, 2025-06-23 at 12:33 +0000, Yao Zi wrote:
/* snip */
> -static struct cpufreq_driver loongson3_cpufreq_driver = {
> +static struct cpufreq_driver loongson3_cpufreq_smc0_driver = {
> .name = "loongson3",
How about using different names for the drivers?
> .flags = CPUFREQ_CONST_LOOPS,
> - .init = loongson3_cpufreq_cpu_init,
> + .init = loongson3_cpufreq_cpu_smc0_init,
> .exit = loongson3_cpufreq_cpu_exit,
> .online = loongson3_cpufreq_cpu_online,
> .offline = loongson3_cpufreq_cpu_offline,
> - .get = loongson3_cpufreq_get,
> - .target_index = loongson3_cpufreq_target,
> + .target_index = loongson3_cpufreq_smc0_target,
> + .verify = cpufreq_generic_frequency_table_verify,
> + .suspend = cpufreq_generic_suspend,
> +};
> +
> +static struct cpufreq_driver loongson3_cpufreq_smc1_driver = {
> + .name = "loongson3",
Tested on a 3A6000 laptop with the schedutil policy. On idle one core
is at 2000MHz and other cores are at 250MHz, when building the kernel
all cores are at 2000MHz.
Tested-by: Xi Ruoyao <xry111@xry111.site>
--
Xi Ruoyao <xry111@xry111.site>
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] cpufreq: loongson3: Support older SMC firmware
2025-06-23 14:59 ` Xi Ruoyao
@ 2025-06-24 1:42 ` Yao Zi
0 siblings, 0 replies; 5+ messages in thread
From: Yao Zi @ 2025-06-24 1:42 UTC (permalink / raw)
To: Xi Ruoyao, Huacai Chen, WANG Xuerui, Rafael J. Wysocki,
Viresh Kumar
Cc: Jiaxun Yang, Mingcong Bai, Kexy Biscuit, loongarch, linux-pm,
linux-kernel
On Mon, Jun 23, 2025 at 10:59:50PM +0800, Xi Ruoyao wrote:
> On Mon, 2025-06-23 at 12:33 +0000, Yao Zi wrote:
>
> /* snip */
>
> > -static struct cpufreq_driver loongson3_cpufreq_driver = {
> > +static struct cpufreq_driver loongson3_cpufreq_smc0_driver = {
> > .name = "loongson3",
>
> How about using different names for the drivers?
I don't have a strong opinion on the name since it could be considered
in both ways,
- SMCv0 and SMCv1 provide similar functions and are implemented in the
same driver. The firmware version is already printed in the probe
callback, which should be enough for debugging purpose.
- Meanwhile, loongson3_cpufreq_smc0_driver and
loongson3_cpufreq_smc1_driver ARE different sets of callbacks,
different names may avoid future confusion.
I'm willing to provide different names in v2 unless you change your mind
or there're further objections, thanks.
> > .flags = CPUFREQ_CONST_LOOPS,
> > - .init = loongson3_cpufreq_cpu_init,
> > + .init = loongson3_cpufreq_cpu_smc0_init,
> > .exit = loongson3_cpufreq_cpu_exit,
> > .online = loongson3_cpufreq_cpu_online,
> > .offline = loongson3_cpufreq_cpu_offline,
> > - .get = loongson3_cpufreq_get,
> > - .target_index = loongson3_cpufreq_target,
> > + .target_index = loongson3_cpufreq_smc0_target,
> > + .verify = cpufreq_generic_frequency_table_verify,
> > + .suspend = cpufreq_generic_suspend,
> > +};
> > +
> > +static struct cpufreq_driver loongson3_cpufreq_smc1_driver = {
> > + .name = "loongson3",
>
> Tested on a 3A6000 laptop with the schedutil policy. On idle one core
> is at 2000MHz and other cores are at 250MHz, when building the kernel
> all cores are at 2000MHz.
>
> Tested-by: Xi Ruoyao <xry111@xry111.site>
>
> --
> Xi Ruoyao <xry111@xry111.site>
>
Regards,
Yao Zi
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] cpufreq: loongson3: Support older SMC firmware
2025-06-23 12:33 [PATCH] cpufreq: loongson3: Support older SMC firmware Yao Zi
2025-06-23 14:59 ` Xi Ruoyao
@ 2025-06-24 2:43 ` Mingcong Bai
2025-06-25 3:47 ` Yao Zi
2 siblings, 0 replies; 5+ messages in thread
From: Mingcong Bai @ 2025-06-24 2:43 UTC (permalink / raw)
To: Yao Zi, Huacai Chen, WANG Xuerui, Rafael J. Wysocki, Viresh Kumar
Cc: Jiaxun Yang, Kexy Biscuit, loongarch, linux-pm, linux-kernel
Greetings,
<snip>
> This patch adds support for the SMC firmware found on these devices,
> which I denoted as "SMC-0" in the driver. Boost support is omitted,
> since determination of cores able to boost requires the driver to couple
> tightly with micro-architecture details.
>
> For coexistence, I prefixed all SMC-call constants with their SMC
> versions, and introduced "SMC-0"-specific initialization and
> frequency-level-setup rountines.
>
> Signed-off-by: Yao Zi <ziyao@disroot.org>
> ---
> drivers/cpufreq/loongson3_cpufreq.c | 287 ++++++++++++++++++++--------
> 1 file changed, 211 insertions(+), 76 deletions(-)
Tested good on an IPASON NL38-N11 (Loongson 3A6000) laptop, core
frequencies fluctuate between 250MHz and 2000MHz individually as
expected. CPU frequency scaling governors works as expected (performance
pins all cores to 2000MHz, the max frequency).
Tested-by: Mingcong Bai <jeffbai@aosc.io>
Best Regards,
Mingcong Bai
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] cpufreq: loongson3: Support older SMC firmware
2025-06-23 12:33 [PATCH] cpufreq: loongson3: Support older SMC firmware Yao Zi
2025-06-23 14:59 ` Xi Ruoyao
2025-06-24 2:43 ` Mingcong Bai
@ 2025-06-25 3:47 ` Yao Zi
2 siblings, 0 replies; 5+ messages in thread
From: Yao Zi @ 2025-06-25 3:47 UTC (permalink / raw)
To: Huacai Chen, WANG Xuerui, Rafael J. Wysocki, Viresh Kumar
Cc: Jiaxun Yang, Mingcong Bai, Kexy Biscuit, loongarch, linux-pm,
linux-kernel
On Mon, Jun 23, 2025 at 12:33:21PM +0000, Yao Zi wrote:
> SMC firmware found on many on-market LoongArch devices implement a
> different ABI than what has been implemented in the current upstream
> driver. They significantly differ in the following ways:
>
> - CMD_GET_VERSION returns 0.
> - There is no known SMC call to obtain corresponding frequencies for
> each frequency level. The frequency table must therefore be calculated
> with CPU clock frequency from scratch.
> - There is no known SMC call to obtain the current frequency level.
> - The main processor must determine the set of cores able to run at
> boost frequency and enable boosting manually.
> - SMC call response format may vary between commands.
>
> This patch adds support for the SMC firmware found on these devices,
> which I denoted as "SMC-0" in the driver. Boost support is omitted,
> since determination of cores able to boost requires the driver to couple
> tightly with micro-architecture details.
>
> For coexistence, I prefixed all SMC-call constants with their SMC
> versions, and introduced "SMC-0"-specific initialization and
> frequency-level-setup rountines.
>
> Signed-off-by: Yao Zi <ziyao@disroot.org>
While self-testing the patch on a 3A5000 workstation, I observed the
processor cannot run stably at the low frequency levels, it's required
to keep it above half of the maximum frequency for stability.
This type of instability seems to depend on the silicon or firmware
revision, my 3A5000 laptop, Tongfang L860-T2, could run stably at any
frequency level allowed by the SMC firmware. And with the driver it
gains extra 30 minutes of battery life when idle, which looks valuable
to me.
To avoid breaking existing users, I'll disable SMCv0 frequency scaling
by default and add a module parameter to enable it in v2.
Regards,
Yao Zi
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2025-06-25 3:48 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-06-23 12:33 [PATCH] cpufreq: loongson3: Support older SMC firmware Yao Zi
2025-06-23 14:59 ` Xi Ruoyao
2025-06-24 1:42 ` Yao Zi
2025-06-24 2:43 ` Mingcong Bai
2025-06-25 3:47 ` Yao Zi
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).