Re: [PATCH 5/5] cpufreq: loongson3: Replace IOCSR read/write with MMIO ones

From: Huacai Chen

Date: Wed Aug 26 2026 - 11:05:53 EST


Hi, Zhongqiu,

On Thu, Aug 20, 2026 at 8:57 PM Zhongqiu Han
<zhongqiu.han@xxxxxxxxxxxxxxxx> wrote:
>
> On 8/18/2026 8:39 PM, Huacai Chen wrote:
> > Our server productions (e.g. Loongson-3D6000/3E6000) can have multiple
> > nodes in one package and SMC mailboxes are also per-node. However, IOCSR
> > read/write can only perform on the current node, while sometimes we want
> > to perform on other nodes (e.g. when switch governor, the get and target
> > callbacks are not run on target core). So replace IOCSR read/write with
> > MMIO ones.
> >
> > Cc: stable@xxxxxxxxxxxxxxx
> > Signed-off-by: Huacai Chen <chenhuacai@xxxxxxxxxxx>
> > ---
> > drivers/cpufreq/loongson3_cpufreq.c | 31 ++++++++++++++++++++++-------
> > 1 file changed, 24 insertions(+), 7 deletions(-)
> >
> > diff --git a/drivers/cpufreq/loongson3_cpufreq.c b/drivers/cpufreq/loongson3_cpufreq.c
> > index c75c0e30e881..e5062cd62390 100644
> > --- a/drivers/cpufreq/loongson3_cpufreq.c
> > +++ b/drivers/cpufreq/loongson3_cpufreq.c
> > @@ -164,6 +164,12 @@ union smc_message {
> >
> > #define FREQ_MAX_LEVEL 16
> >
> > +#define MMIO_SMCMBX(node) \
> > + ((void __iomem *)(IO_BASE | (u64)(node) << NODE_ADDRSPACE_SHIFT | LOONGSON_REG_BASE | LOONGARCH_IOCSR_SMCMBX))
> > +
> > +#define MMIO_MISC_FUNC(node) \
> > + ((void __iomem *)(IO_BASE | (u64)(node) << NODE_ADDRSPACE_SHIFT | LOONGSON_REG_BASE | LOONGARCH_IOCSR_MISC_FUNC))
> > +
> > struct loongson3_freq_data {
> > unsigned int def_freq_level;
> > struct cpufreq_frequency_table table[];
> > @@ -176,13 +182,25 @@ 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)
> > {
> > int retries;
> > - unsigned int cpu = raw_smp_processor_id();
> > - unsigned int nid = cpu_to_node(cpu);
> > + unsigned int cpu, nid;
> > union smc_message msg, last;
> >
> > + switch (cmd) {
> > + case CMD_GET_FREQ_INFO:
> > + case CMD_SET_FREQ_INFO:
> > + case CMD_GET_FREQ_LEVEL_NUM:
> > + case CMD_GET_FREQ_LEVEL_INFO:
> > + case CMD_GET_FREQ_BOOST_LEVEL:
> > + cpu = cpu_number_map(id);
>
> cpu_number_map() is undefined when CONFIG_SMP=n. Wouldn't that result in
> a build failure?
Yes, part of the code should be guarded by CONFIG_SMP here.

>
>
> > + break;
> > + default:
> > + cpu = raw_smp_processor_id();
> > + }
> > + nid = cpu_to_node(cpu);
> > +
> > mutex_lock(&cpufreq_mutex[nid]);
> >
> > - last.value = iocsr_read32(LOONGARCH_IOCSR_SMCMBX);
> > + last.value = readl(MMIO_SMCMBX(nid));
> > if (!last.complete) {
> > mutex_unlock(&cpufreq_mutex[nid]);
> > return -EPERM;
> > @@ -195,12 +213,11 @@ static inline int do_service_request(u32 id, u32 info, u32 cmd, u32 val, u32 ext
> > msg.extra = extra;
> > msg.complete = 0;
> >
> > - iocsr_write32(msg.value, LOONGARCH_IOCSR_SMCMBX);
> > - iocsr_write32(iocsr_read32(LOONGARCH_IOCSR_MISC_FUNC) | IOCSR_MISC_FUNC_SOFT_INT,
> > - LOONGARCH_IOCSR_MISC_FUNC);
> > + writel(msg.value, MMIO_SMCMBX(nid));
> > + writel(readl(MMIO_MISC_FUNC(nid)) | IOCSR_MISC_FUNC_SOFT_INT, MMIO_MISC_FUNC(nid));
> >
> > for (retries = 0; retries < 10000; retries++) {
> > - msg.value = iocsr_read32(LOONGARCH_IOCSR_SMCMBX);
> > + msg.value = readl(MMIO_SMCMBX(nid));
> > if (msg.complete)
> > break;
> >
>
>
> Please ignore this comments if you do not think it is worth addressing:
>
> A separate issue, it seems that DVFS and Boost feature is set in
> loongson3_cpufreq_probe(), what if about on other nodes (non-boot cpu
> node) and what if cpu hotplug?
Interesting question. After discussion with the firmware team, They
told me that CMD_SET_FEATURE is a global command, the SMC firmware
will align the settings for all nodes.

Huacai

>
> static int loongson3_cpufreq_probe(struct platform_device *pdev)
> {
> int i, ret;
>
> for (i = 0; i < MAX_PACKAGES; i++) {
> ret = devm_mutex_init(&pdev->dev, &cpufreq_mutex[i]);
> if (ret)
> return ret;
> }
>
> ret = do_service_request(0, 0, CMD_GET_VERSION, 0, 0);
> if (ret <= 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;
>
> ......
>
> }
>
>
> --
> Thx and BRs,
> Zhongqiu Han