From c704f4a556a37ab2bca48a8e37085e9ec595c07e Mon Sep 17 00:00:00 2001 From: Cheng Yulai Date: Thu, 7 May 2026 17:37:05 +0800 Subject: [PATCH 01/38] codec: phytium: Use different clocks for playback and recording Recording immediately after playing music will result in the latter part of the recording have no sound. the reason as follows: Playback and recording share the same clock. After playback stops, the audio framework will shutdown clock after 5-second. if recording within this 5-second period, the recording clock will be turned off. Therefore, the CONTROL1 register should be modified so that playback and recording use different clocks. Signed-off-by: Cheng Yulai Signed-off-by: Zhou Zheng Signed-off-by: Wang Yinfeng --- sound/soc/codecs/es8388.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/es8388.c b/sound/soc/codecs/es8388.c index d9b1c80e3c38e..aab9c358137c8 100644 --- a/sound/soc/codecs/es8388.c +++ b/sound/soc/codecs/es8388.c @@ -23,7 +23,7 @@ #include #include -#define ES8388_V1_VERSION "1.0.0" +#define ES8388_V1_VERSION "1.0.1" static const unsigned int rates_12288[] = { 8000, 12000, 16000, 24000, 32000, 48000, 96000, @@ -721,7 +721,7 @@ static int es8388_resume(struct snd_soc_component *component) static int es8388_component_probe(struct snd_soc_component *component) { snd_soc_component_write(component, ES8388_ADCPOWER, 0xf0); - snd_soc_component_write(component, ES8388_CONTROL1, 0x30); + snd_soc_component_write(component, ES8388_CONTROL1, 0x20); snd_soc_component_write(component, ES8388_DACCONTROL21, 0x80); snd_soc_component_write(component, ES8388_ADCCONTROL10, 0xda); From fa16ef6d96e9bb80638a3baf0c7a1496b7a65f47 Mon Sep 17 00:00:00 2001 From: Cheng Yulai Date: Thu, 7 May 2026 17:59:32 +0800 Subject: [PATCH 02/38] codec:phytium: Add Kconfig option dependency This driver is exclusively for the PHYTIUM platform and is not compatible with other SoCs. This restriction avoids compiling this driver on other platforms. Signed-off-by: Cheng Yulai Signed-off-by: Zhou Zheng Signed-off-by: Wang Yinfeng --- sound/soc/codecs/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 4a22fb36ca367..3c00c149df39c 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1381,6 +1381,7 @@ config SND_SOC_PEB2466 config SND_SOC_PHYTIUM_CODEC_V2 tristate "Phytium Codec V2 driver" + depends on ARCH_PHYTIUM help Select Y if you want to add Phytium codec V2 driver. From f4648e6ac3b4401931e485425cf8dacc36a9cb5d Mon Sep 17 00:00:00 2001 From: Cheng Yulai Date: Thu, 7 May 2026 18:02:57 +0800 Subject: [PATCH 03/38] codec-v2: phytium: Add controller status code Add a controller status code for no initialzation error. Signed-off-by: Cheng Yulai Signed-off-by: Zhou Zheng Signed-off-by: Wang Yinfeng --- sound/soc/codecs/phytium-codec-v2.c | 13 ++++++++----- sound/soc/codecs/phytium-codec-v2.h | 9 +++++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/phytium-codec-v2.c b/sound/soc/codecs/phytium-codec-v2.c index ab63ceb2c826f..eaadd099c9498 100644 --- a/sound/soc/codecs/phytium-codec-v2.c +++ b/sound/soc/codecs/phytium-codec-v2.c @@ -101,17 +101,20 @@ static const struct snd_soc_dapm_route phyt_dapm_routes[] = { static void phyt_codec_show_status(uint8_t status) { switch (status) { - case 0: + case ERR_CODEC_SUCCESS: pr_err("success\n"); break; - case 2: + case ERR_CODEC_DEV_BUSY: pr_err("device busy\n"); break; - case 3: + case ERR_CODEC_RW_ERROR: pr_err("read/write error\n"); break; - case 4: - pr_err("no device\n"); + case ERR_CODEC_NODEV: + pr_err("no hw device\n"); + break; + case ERR_CODEC_NO_INIT: + pr_err("no init\n"); break; default: pr_err("unknown error: %d\n", status); diff --git a/sound/soc/codecs/phytium-codec-v2.h b/sound/soc/codecs/phytium-codec-v2.h index f0515d0cfabbb..e9b20079975cc 100644 --- a/sound/soc/codecs/phytium-codec-v2.h +++ b/sound/soc/codecs/phytium-codec-v2.h @@ -95,6 +95,15 @@ enum phytcodec_complete { PHYTCODEC_COMPLETE_INVALID_PARAMETERS, }; +enum phytcodec_status { + ERR_CODEC_SUCCESS = 0, + ERR_CODEC_BUS_BUSY, + ERR_CODEC_DEV_BUSY, + ERR_CODEC_RW_ERROR, + ERR_CODEC_NODEV, //HW + ERR_CODEC_NO_INIT, //SW +}; + struct phytcodec_rw_data { uint8_t addr; uint8_t reg; From fde1f34c8d1b04d47e5f8ae371e92982d94e15a8 Mon Sep 17 00:00:00 2001 From: Cheng Yulai Date: Thu, 7 May 2026 18:05:18 +0800 Subject: [PATCH 04/38] codec-v2: phytium: initialize share memory and channels Initialize channels and share memory before sending command to prevent unknown errors. Signed-off-by: Cheng Yulai Signed-off-by: Zhou Zheng Signed-off-by: Wang Yinfeng --- sound/soc/codecs/phytium-codec-v2.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/phytium-codec-v2.c b/sound/soc/codecs/phytium-codec-v2.c index eaadd099c9498..88dd9e81a89e8 100644 --- a/sound/soc/codecs/phytium-codec-v2.c +++ b/sound/soc/codecs/phytium-codec-v2.c @@ -328,6 +328,9 @@ static int phyt_get_cmd(struct phytium_codec *priv, unsigned int cmd) static int phyt_probe(struct snd_soc_component *component) { struct phytium_codec *priv = snd_soc_component_get_drvdata(component); + struct phytcodec_cmd *msg = priv->sharemem_base; + + memset(msg, 0, sizeof(struct phytcodec_cmd)); return phyt_set_cmd(priv, PHYTCODEC_MSG_CMD_SET_PROBE); } @@ -335,6 +338,9 @@ static int phyt_probe(struct snd_soc_component *component) static void phyt_remove(struct snd_soc_component *component) { struct phytium_codec *priv = snd_soc_component_get_drvdata(component); + struct phytcodec_cmd *msg = priv->sharemem_base; + + memset(msg, 0, sizeof(struct phytcodec_cmd)); phyt_set_cmd(priv, PHYTCODEC_MSG_CMD_SET_REMOVE); } @@ -342,6 +348,9 @@ static void phyt_remove(struct snd_soc_component *component) static int phyt_suspend(struct snd_soc_component *component) { struct phytium_codec *priv = snd_soc_component_get_drvdata(component); + struct phytcodec_cmd *msg = priv->sharemem_base; + + memset(msg, 0, sizeof(struct phytcodec_cmd)); return phyt_pm_cmd(priv, PHYTCODEC_MSG_CMD_SET_SUSPEND); } @@ -349,6 +358,9 @@ static int phyt_suspend(struct snd_soc_component *component) static int phyt_resume(struct snd_soc_component *component) { struct phytium_codec *priv = snd_soc_component_get_drvdata(component); + struct phytcodec_cmd *msg = priv->sharemem_base; + + memset(msg, 0, sizeof(struct phytcodec_cmd)); return phyt_pm_cmd(priv, PHYTCODEC_MSG_CMD_SET_RESUME); } @@ -428,6 +440,9 @@ static int phyt_startup(struct snd_pcm_substream *substream, int ret; struct snd_soc_component *component = dai->component; struct phytium_codec *priv = snd_soc_component_get_drvdata(component); + struct phytcodec_cmd *msg = priv->sharemem_base; + + memset(msg, 0, sizeof(struct phytcodec_cmd)); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ret = phyt_set_cmd(priv, PHYTCODEC_MSG_CMD_SET_STARTUP); @@ -443,6 +458,9 @@ static void phyt_shutdown(struct snd_pcm_substream *substream, int ret; struct snd_soc_component *component = dai->component; struct phytium_codec *priv = snd_soc_component_get_drvdata(component); + struct phytcodec_cmd *msg = priv->sharemem_base; + + memset(msg, 0, sizeof(struct phytcodec_cmd)); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ret = phyt_set_cmd(priv, PHYTCODEC_MSG_CMD_SET_SHUTDOWN); @@ -495,6 +513,9 @@ static int phyt_set_dai_fmt(struct snd_soc_dai *codec_dai, int ret; struct snd_soc_component *component = codec_dai->component; struct phytium_codec *priv = snd_soc_component_get_drvdata(component); + struct phytcodec_cmd *msg = priv->sharemem_base; + + memset(msg, 0, sizeof(struct phytcodec_cmd)); if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) return -EINVAL; @@ -745,6 +766,7 @@ static ssize_t debug_store(struct device *dev, struct device_attribute *da, dev_err(dev, "dump command requires one argument\n"); goto error; } + memset(priv->sharemem_base, 0, sizeof(struct phytcodec_cmd)); phyt_get_cmd(priv, PHYTCODEC_MSG_CMD_GET_ALL_REGS); } else if (strcmp(cmd, "help") == 0) { dev_info(dev, "Available commands:\n" @@ -804,8 +826,9 @@ static void phyt_codec_init(struct phytium_codec *priv) if (sysfs_create_group(&priv->dev->kobj, &phyt_codec_device_group)) dev_warn(priv->dev, "failed to create sysfs\n"); - phyt_dai.playback.channels_max = phyt_get_channels(priv); - phyt_dai.capture.channels_max = phyt_dai.playback.channels_max; + priv->channels = phyt_get_channels(priv); + phyt_dai.playback.channels_max = priv->channels; + phyt_dai.capture.channels_max = priv->channels; phyt_writel_reg(priv->regfile_base, PHYTIUM_CODEC_INT_MASK, 0x0); phyt_writel_reg(priv->regfile_base, PHYTIUM_CODEC_INT_ENABLE, 0x1); From 1e907b54663af4f260730b940fd339b104e4ba1d Mon Sep 17 00:00:00 2001 From: Cheng Yulai Date: Thu, 7 May 2026 18:08:43 +0800 Subject: [PATCH 05/38] codec: phytium: Fix probe bug without judgment of return value When executing the probe interface, the driver shound return actual error code instead of zero to avoid creating sound card successfully when hardware is not present. Signed-off-by: Dai Jingtao Signed-off-by: Wang Yinfeng --- sound/soc/codecs/es8388.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/es8388.c b/sound/soc/codecs/es8388.c index aab9c358137c8..f086e2c52ad2e 100644 --- a/sound/soc/codecs/es8388.c +++ b/sound/soc/codecs/es8388.c @@ -720,7 +720,12 @@ static int es8388_resume(struct snd_soc_component *component) static int es8388_component_probe(struct snd_soc_component *component) { - snd_soc_component_write(component, ES8388_ADCPOWER, 0xf0); + int ret = 0; + + ret = snd_soc_component_write(component, ES8388_ADCPOWER, 0xf0); + if (ret) + return ret; + snd_soc_component_write(component, ES8388_CONTROL1, 0x20); snd_soc_component_write(component, ES8388_DACCONTROL21, 0x80); snd_soc_component_write(component, ES8388_ADCCONTROL10, 0xda); From a9063382dc61a96abb448630a5841903cfc24f91 Mon Sep 17 00:00:00 2001 From: Cheng Yulai Date: Fri, 8 May 2026 14:04:11 +0800 Subject: [PATCH 06/38] i2s: phytium: Add audio control node Add audio control node to disable/enable I2S and DMA function. The node is used for dp-i2s to control audio whether it should stop or continue. Such as changing resolution when playing. Signed-off-by: Li Bing Signed-off-by: Dai Jingtao Signed-off-by: Wang Yinfeng --- sound/soc/phytium/phytium-i2s-v2.c | 49 +++++++++++++++++++++++++- sound/soc/phytium/phytium-machine-v2.c | 1 + sound/soc/phytium/pmdk_dp.c | 12 +++++-- 3 files changed, 58 insertions(+), 4 deletions(-) diff --git a/sound/soc/phytium/phytium-i2s-v2.c b/sound/soc/phytium/phytium-i2s-v2.c index 83c4ee1ae65d1..853d4750c2a4a 100644 --- a/sound/soc/phytium/phytium-i2s-v2.c +++ b/sound/soc/phytium/phytium-i2s-v2.c @@ -30,7 +30,7 @@ #include #include "phytium-i2s-v2.h" -#define PHYT_I2S_V2_VERSION "1.0.6" +#define PHYT_I2S_V2_VERSION "1.0.8" static struct snd_soc_jack hs_jack; static irqreturn_t phyt_i2s_gpio_interrupt(int irq, void *dev_id); @@ -539,6 +539,7 @@ static int phyt_pcm_component_probe(struct snd_soc_component *component) static const struct snd_soc_component_driver phytium_i2s_component = { .name = "phytium-i2s", + .use_dai_pcm_id = true, .pcm_construct = phyt_pcm_new, .pcm_destruct = phyt_pcm_free, .suspend = phyt_pcm_suspend, @@ -951,8 +952,54 @@ static ssize_t phyt_i2s_debug_store(struct device *dev, static DEVICE_ATTR_RW(phyt_i2s_debug); +static ssize_t phyt_i2s_control_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct phytium_i2s *priv = dev_get_drvdata(dev); + char *p, *token; + u8 loc; + int ret; + long value; + + p = kmalloc(size, GFP_KERNEL); + if (p == NULL) + return -EINVAL; + strscpy(p, buf, sizeof(p)); + + token = strsep(&p, " "); + if (!token) { + ret = -EINVAL; + goto error; + } + + ret = kstrtol(token, 0, &value); + if (ret) + goto error; + loc = (u8)value; + + if (loc == 1) { + //Enable I2S and DMA + phyt_writel_reg(priv->dma_reg_base, PHYTIUM_DMA_CTL, 1); + phyt_writel_reg(priv->regfile_base, PHYTIUM_REGFILE_ITER, TX_EN); + } else if (loc == 0) { + //Disable I2S and DMA + phyt_writel_reg(priv->regfile_base, PHYTIUM_REGFILE_ITER, TX_DIS); + phyt_writel_reg(priv->dma_reg_base, PHYTIUM_DMA_CTL, 0); + } + + kfree(p); + return size; +error: + kfree(p); + return ret; +} + +static DEVICE_ATTR_WO(phyt_i2s_control); + static struct attribute *phyt_i2s_device_attrs[] = { &dev_attr_phyt_i2s_debug.attr, + &dev_attr_phyt_i2s_control.attr, NULL, }; diff --git a/sound/soc/phytium/phytium-machine-v2.c b/sound/soc/phytium/phytium-machine-v2.c index d02ae022e5591..12f08b4ba445a 100644 --- a/sound/soc/phytium/phytium-machine-v2.c +++ b/sound/soc/phytium/phytium-machine-v2.c @@ -48,6 +48,7 @@ SND_SOC_DAILINK_DEFS(phyt_machine, static struct snd_soc_dai_link phyt_machine_dai[] = { { .name = "PHYTIUM HIFI V2", + .id = 0, .stream_name = "PHYTIUM HIFT V2", .dai_fmt = PMDK_DAI_FMT, SND_SOC_DAILINK_REG(phyt_machine), diff --git a/sound/soc/phytium/pmdk_dp.c b/sound/soc/phytium/pmdk_dp.c index c0760d3a7cd5f..c00b2c7069950 100644 --- a/sound/soc/phytium/pmdk_dp.c +++ b/sound/soc/phytium/pmdk_dp.c @@ -67,7 +67,8 @@ static int pmdk_dp0_init(struct snd_soc_pcm_runtime *runtime) dev_err(card->dev, "Jack creation failed %d\n", ret); return ret; } - snd_soc_component_set_jack(component, &priv->jack0, NULL); + ret = snd_soc_component_set_jack(component, &priv->jack0, NULL); + return ret; } @@ -85,7 +86,8 @@ static int pmdk_dp1_init(struct snd_soc_pcm_runtime *runtime) dev_err(card->dev, "Jack creation failed %d\n", ret); return ret; } - snd_soc_component_set_jack(component, &priv->jack1, NULL); + ret = snd_soc_component_set_jack(component, &priv->jack1, NULL); + return ret; } @@ -103,7 +105,8 @@ static int pmdk_dp2_init(struct snd_soc_pcm_runtime *runtime) dev_err(card->dev, "Jack creation failed %d\n", ret); return ret; } - snd_soc_component_set_jack(component, &priv->jack2, NULL); + ret = snd_soc_component_set_jack(component, &priv->jack2, NULL); + return ret; } @@ -124,6 +127,7 @@ SND_SOC_DAILINK_DEFS(pmdk_dp2_dai, static struct snd_soc_dai_link pmdk_dai0 = { .name = "Phytium dp0-audio", + .id = 0, .stream_name = "Playback", .dai_fmt = SMDK_DAI_FMT, .init = pmdk_dp0_init, @@ -133,6 +137,7 @@ static struct snd_soc_dai_link pmdk_dai0 = { static struct snd_soc_dai_link pmdk_dai1 = { .name = "Phytium dp1-audio", + .id = 1, .stream_name = "Playback", .dai_fmt = SMDK_DAI_FMT, .init = pmdk_dp1_init, @@ -142,6 +147,7 @@ static struct snd_soc_dai_link pmdk_dai1 = { static struct snd_soc_dai_link pmdk_dai2 = { .name = "Phytium dp2-audio", + .id = 2, .stream_name = "Playback", .dai_fmt = SMDK_DAI_FMT, .init = pmdk_dp2_init, From da3406945f76a9ad871a9c5c77d9807cfdcd57dc Mon Sep 17 00:00:00 2001 From: Cheng Yulai Date: Fri, 8 May 2026 14:07:56 +0800 Subject: [PATCH 07/38] i2s-v2: phytium: Restricted adaptation platform This driver is exclusively for the PHYTIUM platform and is not compatible with other SoCs. This restriction prevents errors on unsupported platform. Signed-off-by: Li Bing Signed-off-by: Dai Jingtao Signed-off-by: Wang Yinfeng --- sound/soc/phytium/Kconfig | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/sound/soc/phytium/Kconfig b/sound/soc/phytium/Kconfig index c567699bcb568..a30c865d4af53 100644 --- a/sound/soc/phytium/Kconfig +++ b/sound/soc/phytium/Kconfig @@ -27,18 +27,19 @@ config SND_PMDK_ES8336 ES8336 codecs. config SND_SOC_PHYTIUM_I2S_V2 - tristate "Phytium I2S V2 Device Driver" - help - Say Y or M if you want to add support for I2S v2 driver for - Phytium I2S device . The device supports 2 channels each - for play and record. + tristate "Phytium I2S V2 Device Driver" + depends on ARCH_PHYTIUM + help + Say Y or M if you want to add support for I2S v2 driver for + Phytium I2S device . The device supports 2 channels each + for play and record. config SND_SOC_PHYTIUM_MACHINE_V2 - tristate "Phytium Machine V2 Driver" - depends on SND_SOC_PHYTIUM_I2S_V2 - help - Say Y or M if you want to add Phytium machine v2 support for - codecs. + tristate "Phytium Machine V2 Driver" + depends on SND_SOC_PHYTIUM_I2S_V2 + help + Say Y or M if you want to add Phytium machine v2 support for + codecs. config SND_PMDK_DP tristate "Phytium machine support with DP" From 196a6da37fd07c1ea24b38e75d7a5de484526c73 Mon Sep 17 00:00:00 2001 From: Cheng Yulai Date: Fri, 8 May 2026 14:09:34 +0800 Subject: [PATCH 08/38] i2s-v2: phytium: Defer I2S probe procedure The problem is that when the hardware card is not inserted, it causes sound card loading to fail due to undefined behavior from headphone detection. This detection is in the I2S driver's probe function, but I2S cannot detect whether a daughter card actually exists. Therefore, the codec's probe should execute first and return directly if not daughter card is found. Signed-off-by: Li Bing Signed-off-by: Dai Jingtao Signed-off-by: Wang Yinfeng --- sound/soc/phytium/phytium-i2s-v2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/phytium/phytium-i2s-v2.c b/sound/soc/phytium/phytium-i2s-v2.c index 853d4750c2a4a..d0074a81a874d 100644 --- a/sound/soc/phytium/phytium-i2s-v2.c +++ b/sound/soc/phytium/phytium-i2s-v2.c @@ -30,7 +30,7 @@ #include #include "phytium-i2s-v2.h" -#define PHYT_I2S_V2_VERSION "1.0.8" +#define PHYT_I2S_V2_VERSION "1.0.9" static struct snd_soc_jack hs_jack; static irqreturn_t phyt_i2s_gpio_interrupt(int irq, void *dev_id); @@ -540,6 +540,7 @@ static int phyt_pcm_component_probe(struct snd_soc_component *component) static const struct snd_soc_component_driver phytium_i2s_component = { .name = "phytium-i2s", .use_dai_pcm_id = true, + .probe_order = SND_SOC_COMP_ORDER_LATE, .pcm_construct = phyt_pcm_new, .pcm_destruct = phyt_pcm_free, .suspend = phyt_pcm_suspend, From 5809be05828d52bdb959c9be581e8cb51fc7063b Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Thu, 7 May 2026 19:21:32 +0800 Subject: [PATCH 09/38] arm64: phytium: Add support of reading cpu type for Phytium Socs This patch provides three methods for reading cpu type for Phytium Socs, with priority from high to low as follows: - read socid by arm-smccc - read system register of SYS_AIDR_EL1 - read system register of MPIDR_EL1 Signed-off-by: Zhang Fuxiang Signed-off-by: Feng Jun Signed-off-by: Wang Yinfeng --- MAINTAINERS | 1 + arch/arm64/include/asm/phytium_cputype.h | 198 +++++++++++++++++++++++ 2 files changed, 199 insertions(+) create mode 100644 arch/arm64/include/asm/phytium_cputype.h diff --git a/MAINTAINERS b/MAINTAINERS index f2c5a9ee20ad8..c7ebccca27be8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17551,6 +17551,7 @@ F: drivers/usb/phytium/* F: drivers/usb/phytium/phytium_usb_v2* F: drivers/usb/typec/role-switch-phytium.c F: arch/arm64/boot/dts/phytium/* +F: arch/arm64/include/asm/phytium_cputype.h F: drivers/gpio/gpio-phytium* QAT DRIVER diff --git a/arch/arm64/include/asm/phytium_cputype.h b/arch/arm64/include/asm/phytium_cputype.h new file mode 100644 index 0000000000000..c30a7d6b1b42d --- /dev/null +++ b/arch/arm64/include/asm/phytium_cputype.h @@ -0,0 +1,198 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2025 ARM Ltd. + */ +#ifndef __ASM_PHYTIUM_CPUTYPE_H +#define __ASM_PHYTIUM_CPUTYPE_H + +#include +#include + +#define SOC_ID_PE1702 0x1 +#define SOC_ID_PS17064 0x2 +#define SOC_ID_PD1904 0x3 +#define SOC_ID_PS20064 0x4 + +#define SOC_ID_PD2008 0x5 +#define SOC_ID_PS21064 0x6 +#define SOC_ID_PE220X 0x7 +#define SOC_ID_PS23064 0x8 +#define SOC_ID_PD2308 0x9 +#define SOC_ID_PS2480 0xa +#define SOC_ID_PD2408 0xb + +#define SYS_REG_VAL_PS24080 0x6 +#define SYS_REG_VAL_PS23064 0x8 + +#define SMCCC_SUCCESS 0 +#define SMCCC_FAILURE (-1) +#define FUNC_ID_GET_CPU_VERSION 0xc2000002 + +enum phyt_cpu_type { + PE1702 = 1, + PS17064, + PD1904, + PS20064, + PD2008, + PS21064, + PE220X, + PS23064, + PD2308, + PS24080, + PD2408, + PS15016, + UNKNOWN_SOC +}; + +static enum phyt_cpu_type do_smccc_res(struct arm_smccc_res res) +{ + unsigned long smc_cpu_ver = res.a1; + + smc_cpu_ver = (smc_cpu_ver >> 8); + switch (smc_cpu_ver) { + case SOC_ID_PE1702: + return PE1702; + case SOC_ID_PS17064: + return PS17064; + case SOC_ID_PD1904: + return PD1904; + case SOC_ID_PS20064: + return PS20064; + case SOC_ID_PD2008: + return PD2008; + case SOC_ID_PS21064: + return PS21064; + case SOC_ID_PE220X: + return PE220X; + case SOC_ID_PS23064: + return PS23064; + case SOC_ID_PD2308: + return PD2308; + case SOC_ID_PS2480: + return PS24080; + case SOC_ID_PD2408: + return PD2408; + default: + return UNKNOWN_SOC; + } +} + +static enum phyt_cpu_type do_read_sysreg(void) +{ + u32 aidr_reg_val = read_sysreg(aidr_el1); + + switch (aidr_reg_val) { + case SYS_REG_VAL_PS24080: + return PS24080; + case SYS_REG_VAL_PS23064: + return PS23064; + default: + return UNKNOWN_SOC; + } +} + +static enum phyt_cpu_type do_read_mpidr(void) +{ + u32 part_id = read_cpuid_part_number(); + + switch (part_id) { + case PHYTIUM_CPU_PART_FTC303: + return PE220X; + case PHYTIUM_CPU_PART_FTC660: + return PS15016; + case PHYTIUM_CPU_PART_FTC661: + return PE1702; + case PHYTIUM_CPU_PART_FTC662: + return PS17064; + case PHYTIUM_CPU_PART_FTC663: + return PD1904; + case PHYTIUM_CPU_PART_FTC862: + return PD2408; + default: + return UNKNOWN_SOC; + } +} + +static inline bool is_phytium_soc(void) +{ + if (read_cpuid_implementor() == ARM_CPU_IMP_PHYTIUM) + return true; + return false; +} + +static inline enum phyt_cpu_type phyt_read_cpu_type(void) +{ + enum phyt_cpu_type ctype; + struct arm_smccc_res res; + + if (!is_phytium_soc()) + return UNKNOWN_SOC; + + arm_smccc_smc(FUNC_ID_GET_CPU_VERSION, 0, 0, 0, 0, 0, 0, 0, &res); + switch (res.a0) { + case SMCCC_SUCCESS: + ctype = do_smccc_res(res); + if (ctype != UNKNOWN_SOC) + break; + fallthrough; + case SMCCC_FAILURE: + ctype = do_read_sysreg(); + if (ctype != UNKNOWN_SOC) + break; + fallthrough; + default: + ctype = do_read_mpidr(); + break; + } + return ctype; +} + +static inline bool is_pd2408(void) +{ + enum phyt_cpu_type ctype = phyt_read_cpu_type(); + + if (ctype == PD2408) + return true; + return false; +} + +static inline bool is_ps23064(void) +{ + enum phyt_cpu_type ctype = phyt_read_cpu_type(); + + if (ctype == PS23064) + return true; + return false; +} + +static inline bool is_ps24080(void) +{ + enum phyt_cpu_type ctype = phyt_read_cpu_type(); + + if (ctype == PS24080) + return true; + return false; +} + +static inline bool is_pd2308(void) +{ + enum phyt_cpu_type ctype = phyt_read_cpu_type(); + + if (ctype == PD2308) + return true; + return false; +} + +static inline bool is_pe220x(void) +{ + enum phyt_cpu_type ctype = phyt_read_cpu_type(); + + if (ctype == PE220X) + return true; + + return false; +} + +#endif + + From c584546dc0ef8dbb407e096b044e24586a4eb208 Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Thu, 7 May 2026 19:23:53 +0800 Subject: [PATCH 10/38] arm64: phytium: Update the method to obtain CPU type for Phytium SoCs This patch adjusts the machanism of obtaining the CPU type for Phytium Socs. It can directly return current CPU type when external interface calls the function. Signed-off-by: Zhang Fuxiang Signed-off-by: Feng Jun Signed-off-by: Wang Yinfeng --- arch/arm64/include/asm/phytium_cputype.h | 77 ++++++++++++++++++------ arch/arm64/kernel/setup.c | 9 +++ 2 files changed, 69 insertions(+), 17 deletions(-) diff --git a/arch/arm64/include/asm/phytium_cputype.h b/arch/arm64/include/asm/phytium_cputype.h index c30a7d6b1b42d..6a2724f0b4517 100644 --- a/arch/arm64/include/asm/phytium_cputype.h +++ b/arch/arm64/include/asm/phytium_cputype.h @@ -28,7 +28,7 @@ #define SMCCC_FAILURE (-1) #define FUNC_ID_GET_CPU_VERSION 0xc2000002 -enum phyt_cpu_type { +enum phyt_soc_type { PE1702 = 1, PS17064, PD1904, @@ -44,7 +44,9 @@ enum phyt_cpu_type { UNKNOWN_SOC }; -static enum phyt_cpu_type do_smccc_res(struct arm_smccc_res res) +extern enum phyt_soc_type phyt_soc_type_t; + +static enum phyt_soc_type do_smccc_res(struct arm_smccc_res res) { unsigned long smc_cpu_ver = res.a1; @@ -77,7 +79,7 @@ static enum phyt_cpu_type do_smccc_res(struct arm_smccc_res res) } } -static enum phyt_cpu_type do_read_sysreg(void) +static enum phyt_soc_type do_read_sysreg(void) { u32 aidr_reg_val = read_sysreg(aidr_el1); @@ -91,7 +93,7 @@ static enum phyt_cpu_type do_read_sysreg(void) } } -static enum phyt_cpu_type do_read_mpidr(void) +static enum phyt_soc_type do_read_mpidr(void) { u32 part_id = read_cpuid_part_number(); @@ -120,9 +122,9 @@ static inline bool is_phytium_soc(void) return false; } -static inline enum phyt_cpu_type phyt_read_cpu_type(void) +static inline enum phyt_soc_type phyt_read_soc_type(void) { - enum phyt_cpu_type ctype; + enum phyt_soc_type ctype; struct arm_smccc_res res; if (!is_phytium_soc()) @@ -147,49 +149,90 @@ static inline enum phyt_cpu_type phyt_read_cpu_type(void) return ctype; } +static inline void phyt_soc_type_init(void) +{ + enum phyt_soc_type ctype = phyt_read_soc_type(); + + switch (ctype) { + case PE1702: + phyt_soc_type_t = PE1702; + break; + case PS17064: + phyt_soc_type_t = PS17064; + break; + case PD1904: + phyt_soc_type_t = PD1904; + break; + case PS20064: + phyt_soc_type_t = PS20064; + break; + case PD2008: + phyt_soc_type_t = PD2008; + break; + case PS21064: + phyt_soc_type_t = PS21064; + break; + case PE220X: + phyt_soc_type_t = PE220X; + break; + case PS23064: + phyt_soc_type_t = PS23064; + break; + case PD2308: + phyt_soc_type_t = PD2308; + break; + case PS24080: + phyt_soc_type_t = PS24080; + break; + case PD2408: + phyt_soc_type_t = PD2408; + break; + case PS15016: + phyt_soc_type_t = PS15016; + break; + default: + phyt_soc_type_t = UNKNOWN_SOC; + break; + } +} + static inline bool is_pd2408(void) { - enum phyt_cpu_type ctype = phyt_read_cpu_type(); - if (ctype == PD2408) + if (phyt_soc_type_t == PD2408) return true; return false; } static inline bool is_ps23064(void) { - enum phyt_cpu_type ctype = phyt_read_cpu_type(); - if (ctype == PS23064) + if (phyt_soc_type_t == PS23064) return true; return false; } static inline bool is_ps24080(void) { - enum phyt_cpu_type ctype = phyt_read_cpu_type(); - if (ctype == PS24080) + if (phyt_soc_type_t == PS24080) return true; return false; } static inline bool is_pd2308(void) { - enum phyt_cpu_type ctype = phyt_read_cpu_type(); - if (ctype == PD2308) + if (phyt_soc_type_t == PD2308) return true; return false; } static inline bool is_pe220x(void) { - enum phyt_cpu_type ctype = phyt_read_cpu_type(); - if (ctype == PE220X) + if (phyt_soc_type_t == PE220X) return true; - return false; } diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index 3a2d9d822d12d..c6adf753e6d00 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -57,6 +57,12 @@ #include #endif +#ifdef CONFIG_ARCH_PHYTIUM +#include +enum phyt_soc_type phyt_soc_type_t; +EXPORT_SYMBOL(phyt_soc_type_t); +#endif + static int num_standard_resources; static struct resource *standard_resources; @@ -349,6 +355,9 @@ void __init __no_sanitize_address setup_arch(char **cmdline_p) FW_BUG "Booted with MMU enabled!"); } +#ifdef CONFIG_ARCH_PHYTIUM + phyt_soc_type_init(); +#endif arm64_memblock_init(); paging_init(); From b688dce88ef359a53ad21d6b36499f1bbbe7523f Mon Sep 17 00:00:00 2001 From: Ma Mingrui Date: Tue, 12 May 2026 18:20:44 +0800 Subject: [PATCH 11/38] arm64: phytium: Modify the definition for PE220x CPU name Modify the definition of PE220x CPU name from PHYTIUM_CPU_PART_FTC303 to PHYTIUM_CPU_PART_FTC310 to support initialization and features for the FTC310 processor. Signed-off-by: Li Mingzhe Signed-off-by: Wang Yinfeng Signed-off-by: Ma Mingrui --- arch/arm64/include/asm/cputype.h | 4 ++-- arch/arm64/include/asm/phytium_cputype.h | 4 ++-- arch/arm64/kernel/cpufeature.c | 2 +- arch/arm64/kernel/proton-pack.c | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/arm64/include/asm/cputype.h b/arch/arm64/include/asm/cputype.h index 410bb0b5a0308..8c8d8558c6700 100644 --- a/arch/arm64/include/asm/cputype.h +++ b/arch/arm64/include/asm/cputype.h @@ -156,7 +156,7 @@ #define MICROSOFT_CPU_PART_AZURE_COBALT_100 0xD49 /* Based on r0p0 of ARM Neoverse N2 */ -#define PHYTIUM_CPU_PART_FTC303 0x303 +#define PHYTIUM_CPU_PART_FTC310 0x303 #define PHYTIUM_CPU_PART_FTC660 0x660 #define PHYTIUM_CPU_PART_FTC661 0x661 #define PHYTIUM_CPU_PART_FTC662 0x662 @@ -240,7 +240,7 @@ #define MIDR_APPLE_M2_AVALANCHE_MAX MIDR_CPU_MODEL(ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M2_AVALANCHE_MAX) #define MIDR_AMPERE1 MIDR_CPU_MODEL(ARM_CPU_IMP_AMPERE, AMPERE_CPU_PART_AMPERE1) #define MIDR_AMPERE1A MIDR_CPU_MODEL(ARM_CPU_IMP_AMPERE, AMPERE_CPU_PART_AMPERE1A) -#define MIDR_PHYTIUM_FTC303 MIDR_CPU_MODEL(ARM_CPU_IMP_PHYTIUM, PHYTIUM_CPU_PART_FTC303) +#define MIDR_PHYTIUM_FTC310 MIDR_CPU_MODEL(ARM_CPU_IMP_PHYTIUM, PHYTIUM_CPU_PART_FTC310) #define MIDR_PHYTIUM_FTC660 MIDR_CPU_MODEL(ARM_CPU_IMP_PHYTIUM, PHYTIUM_CPU_PART_FTC660) #define MIDR_PHYTIUM_FTC661 MIDR_CPU_MODEL(ARM_CPU_IMP_PHYTIUM, PHYTIUM_CPU_PART_FTC661) #define MIDR_PHYTIUM_PS17064 MIDR_CPU_MODEL(ARM_CPU_IMP_PHYTIUM, PHYTIUM_CPU_PART_FTC662) diff --git a/arch/arm64/include/asm/phytium_cputype.h b/arch/arm64/include/asm/phytium_cputype.h index 6a2724f0b4517..c4edec454597d 100644 --- a/arch/arm64/include/asm/phytium_cputype.h +++ b/arch/arm64/include/asm/phytium_cputype.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (C) 2025 ARM Ltd. + * Copyright (C) 2025, Phytium Technology Co., Ltd. */ #ifndef __ASM_PHYTIUM_CPUTYPE_H #define __ASM_PHYTIUM_CPUTYPE_H @@ -98,7 +98,7 @@ static enum phyt_soc_type do_read_mpidr(void) u32 part_id = read_cpuid_part_number(); switch (part_id) { - case PHYTIUM_CPU_PART_FTC303: + case PHYTIUM_CPU_PART_FTC310: return PE220X; case PHYTIUM_CPU_PART_FTC660: return PS15016; diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 9ea67d6565a78..6e7926724866d 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -1722,7 +1722,7 @@ static bool unmap_kernel_at_el0(const struct arm64_cpu_capabilities *entry, MIDR_ALL_VERSIONS(MIDR_CORTEX_A73), MIDR_ALL_VERSIONS(MIDR_HISI_TSV110), MIDR_ALL_VERSIONS(MIDR_NVIDIA_CARMEL), - MIDR_ALL_VERSIONS(MIDR_PHYTIUM_FTC303), + MIDR_ALL_VERSIONS(MIDR_PHYTIUM_FTC310), MIDR_ALL_VERSIONS(MIDR_PHYTIUM_FTC660), MIDR_ALL_VERSIONS(MIDR_PHYTIUM_FTC661), MIDR_ALL_VERSIONS(MIDR_PHYTIUM_PS17064), diff --git a/arch/arm64/kernel/proton-pack.c b/arch/arm64/kernel/proton-pack.c index d31a13664b3a1..af04a1739aab4 100644 --- a/arch/arm64/kernel/proton-pack.c +++ b/arch/arm64/kernel/proton-pack.c @@ -163,7 +163,7 @@ static enum mitigation_state spectre_v2_get_cpu_hw_mitigation_state(void) MIDR_ALL_VERSIONS(MIDR_CORTEX_A55), MIDR_ALL_VERSIONS(MIDR_BRAHMA_B53), MIDR_ALL_VERSIONS(MIDR_HISI_TSV110), - MIDR_ALL_VERSIONS(MIDR_PHYTIUM_FTC303), + MIDR_ALL_VERSIONS(MIDR_PHYTIUM_FTC310), MIDR_ALL_VERSIONS(MIDR_QCOM_KRYO_2XX_SILVER), MIDR_ALL_VERSIONS(MIDR_QCOM_KRYO_3XX_SILVER), MIDR_ALL_VERSIONS(MIDR_QCOM_KRYO_4XX_SILVER), @@ -474,7 +474,7 @@ static enum mitigation_state spectre_v4_get_cpu_hw_mitigation_state(void) MIDR_ALL_VERSIONS(MIDR_CORTEX_A53), MIDR_ALL_VERSIONS(MIDR_CORTEX_A55), MIDR_ALL_VERSIONS(MIDR_BRAHMA_B53), - MIDR_ALL_VERSIONS(MIDR_PHYTIUM_FTC303), + MIDR_ALL_VERSIONS(MIDR_PHYTIUM_FTC310), MIDR_ALL_VERSIONS(MIDR_QCOM_KRYO_3XX_SILVER), MIDR_ALL_VERSIONS(MIDR_QCOM_KRYO_4XX_SILVER), { /* sentinel */ }, From 24f677380a7740b43999cf4bbb86813a8964cab0 Mon Sep 17 00:00:00 2001 From: leoliu-oc Date: Tue, 12 May 2026 20:39:15 +0800 Subject: [PATCH 12/38] hwmon: zhaoxin-cputemp: Update for KX-8000 zhaoxin inclusion category: feature -------------------- This patch extends temperature monitoring support to include the new Zhaoxin KX-8000 FMS CPU family by: 1. Adding model 0x8b to the MSR register mapping condition, so it uses the same temperature critical and maximum MSR addresses (0x175b and 0x175a) as the existing 0x6b and 0x7b models. 2. Registering both CENTAUR and ZHAOXIN vendor variants of the 0x8b model in the CPU ID matching table to enable driver probe on these systems. Signed-off-by: leoliu-oc --- drivers/hwmon/zhaoxin-cputemp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/zhaoxin-cputemp.c b/drivers/hwmon/zhaoxin-cputemp.c index 8286665f2c55d..9eca3a3efaa19 100644 --- a/drivers/hwmon/zhaoxin-cputemp.c +++ b/drivers/hwmon/zhaoxin-cputemp.c @@ -122,7 +122,7 @@ static int zhaoxin_cputemp_probe(struct platform_device *pdev) data->id = pdev->id; data->name = "zhaoxin_cputemp"; data->msr_temp = 0x1423; - if (c->x86_model == 0x6b || c->x86_model == 0x7b) { + if (c->x86_model == 0x6b || c->x86_model == 0x7b || c->x86_model == 0x8b) { data->msr_crit = 0x175b; data->msr_max = 0x175a; } else { @@ -265,6 +265,8 @@ static const struct x86_cpu_id __initconst cputemp_ids[] = { X86_MATCH_VENDOR_FAM_MODEL(ZHAOXIN, 7, 0x6b, NULL), X86_MATCH_VENDOR_FAM_MODEL(CENTAUR, 7, 0x7b, NULL), X86_MATCH_VENDOR_FAM_MODEL(ZHAOXIN, 7, 0x7b, NULL), + X86_MATCH_VENDOR_FAM_MODEL(CENTAUR, 7, 0x8b, NULL), + X86_MATCH_VENDOR_FAM_MODEL(ZHAOXIN, 7, 0x8b, NULL), {} }; MODULE_DEVICE_TABLE(x86cpu, cputemp_ids); From 5b73948f7a3ab40b6d0b4ad9db26fb7d3b161e46 Mon Sep 17 00:00:00 2001 From: Tony W Wang-oc Date: Wed, 28 Jan 2026 10:52:16 +0800 Subject: [PATCH 13/38] ACPI: APEI: GHES: Add ghes_edac support for __ZX__ and _BYO_ systems Let ghes_edac be the preferred driver to load on __ZX__ and _BYO_ systems by extending the platform detection list in ghes.c Signed-off-by: Tony W Wang-oc Tested-by: Lyle Li Acked-by: Borislav Petkov (AMD) [ rjw: Subject and changelog edits ] Link: https://patch.msgid.link/20260128025216.12564-1-TonyWWang-oc@zhaoxin.com Signed-off-by: Rafael J. Wysocki Signed-off-by: LeoLiu-oc --- drivers/acpi/apei/ghes.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 14c3add985264..6c27bc5df4a9f 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -1611,6 +1611,8 @@ void __init acpi_ghes_init(void) */ static struct acpi_platform_list plat_list[] = { {"HPE ", "Server ", 0, ACPI_SIG_FADT, all_versions}, + {"__ZX__", "EDK2 ", 3, ACPI_SIG_FADT, greater_than_or_equal}, + {"_BYO_ ", "BYOSOFT ", 3, ACPI_SIG_FADT, greater_than_or_equal}, { } /* End */ }; From 2e6f1e4771dc9e7d567130c159a80444415103e1 Mon Sep 17 00:00:00 2001 From: LeoLiu-oc Date: Tue, 12 May 2026 21:36:40 +0800 Subject: [PATCH 14/38] =?UTF-8?q?pinctrl/zhaoxin:=20kh50000:=20add=20multi?= =?UTF-8?q?=E2=80=91socket=20support?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Introduce socket‑aware pin definition macros for multi‑socket platforms * Split global pin table into per‑socket pin tables with UID soc_data * Use UID‑based probe to match multi‑socket instances * Dynamically acquire PMIO IO resource instead of hard‑coding address * Fix PMIO offset for multi‑socket compatibility Signed-off-by: LeoLiu-oc --- drivers/pinctrl/zhaoxin/pinctrl-kh50000.c | 402 +++++++++++++--------- drivers/pinctrl/zhaoxin/pinctrl-zhaoxin.h | 2 + 2 files changed, 238 insertions(+), 166 deletions(-) diff --git a/drivers/pinctrl/zhaoxin/pinctrl-kh50000.c b/drivers/pinctrl/zhaoxin/pinctrl-kh50000.c index 8c607953eaf30..279110cd56d3d 100644 --- a/drivers/pinctrl/zhaoxin/pinctrl-kh50000.c +++ b/drivers/pinctrl/zhaoxin/pinctrl-kh50000.c @@ -16,166 +16,184 @@ #include "pinctrl-zhaoxin.h" +#define KH50000_SOCKET_PINS(sock) \ + SOCKET_PINCTRL_PIN(sock, 0, "IOD_CLK27M_G0"), \ + SOCKET_PINCTRL_PIN(sock, 1, "IOD_CLK27M_G1"), \ + SOCKET_PINCTRL_PIN(sock, 2, "IOD_CLK27M_G2"), \ + SOCKET_PINCTRL_PIN(sock, 3, "IOD_CLK27M_G3"), \ + SOCKET_PINCTRL_PIN(sock, 4, "IOD_CPURST_G0"), \ + SOCKET_PINCTRL_PIN(sock, 5, "IOD_CPURST_G1"), \ + SOCKET_PINCTRL_PIN(sock, 6, "IOD_CPURST_G2"), \ + SOCKET_PINCTRL_PIN(sock, 7, "IOD_CPURST_G3"), \ + SOCKET_PINCTRL_PIN(sock, 8, "IOD_RSMRST_G0"), \ + SOCKET_PINCTRL_PIN(sock, 9, "IOD_RSMRST_G1"), \ + SOCKET_PINCTRL_PIN(sock, 10, "IOD_RSMRST_G2"), \ + SOCKET_PINCTRL_PIN(sock, 11, "IOD_RSMRST_G3"), \ + SOCKET_PINCTRL_PIN(sock, 12, "IOD_PWROK_G0"), \ + SOCKET_PINCTRL_PIN(sock, 13, "IOD_PWROK_G1"), \ + SOCKET_PINCTRL_PIN(sock, 14, "IOD_PWROK_G2"), \ + SOCKET_PINCTRL_PIN(sock, 15, "IOD_PWROK_G3"), \ + SOCKET_PINCTRL_PIN(sock, 16, "IOD_THRMTRIP_G0"), \ + SOCKET_PINCTRL_PIN(sock, 17, "IOD_THRMTRIP_G1"), \ + SOCKET_PINCTRL_PIN(sock, 18, "IOD_THRMTRIP_G2"), \ + SOCKET_PINCTRL_PIN(sock, 19, "IOD_THRMTRIP_G3"), \ + SOCKET_PINCTRL_PIN(sock, 20, "IOD_CLK50M_G0"), \ + SOCKET_PINCTRL_PIN(sock, 21, "IOD_CLK50M_G1"), \ + SOCKET_PINCTRL_PIN(sock, 22, "IOD_CLK50M_G2"), \ + SOCKET_PINCTRL_PIN(sock, 23, "IOD_CLK50M_G3"), \ + /*GPIO range 0 */ \ + SOCKET_PINCTRL_PIN(sock, 24, "USBHOC0"), /*PGPIO0------gpio36*/ \ + SOCKET_PINCTRL_PIN(sock, 25, "USBHOC1"), /*PGPIO1------gpio37*/ \ + SOCKET_PINCTRL_PIN(sock, 26, "USBHOC2"), /*PGPIO2------gpio38*/ \ + SOCKET_PINCTRL_PIN(sock, 27, "USBHOC3"), /*PGPIO3------gpio39*/ \ + SOCKET_PINCTRL_PIN(sock, 28, "I3C0DT"), \ + SOCKET_PINCTRL_PIN(sock, 29, "I3C0CK"), \ + SOCKET_PINCTRL_PIN(sock, 30, "I3C1DT"), \ + SOCKET_PINCTRL_PIN(sock, 31, "I3C1CK"), \ + SOCKET_PINCTRL_PIN(sock, 32, "I3C2DT"), \ + SOCKET_PINCTRL_PIN(sock, 33, "I3C2CK"), \ + SOCKET_PINCTRL_PIN(sock, 34, "I3C3DT"), \ + SOCKET_PINCTRL_PIN(sock, 35, "I3C3CK"), \ + SOCKET_PINCTRL_PIN(sock, 36, "SMBDT0"), \ + /*GPIO range 1*/ \ + SOCKET_PINCTRL_PIN(sock, 37, "SMBCK0"), /*PGPIO11------gpio47*/ \ + SOCKET_PINCTRL_PIN(sock, 38, "SMBDT1"), /*PGPIO12------gpio48*/ \ + SOCKET_PINCTRL_PIN(sock, 39, "SMBCK1"), /*PGPIO13------gpio49*/ \ + SOCKET_PINCTRL_PIN(sock, 40, "SMBDT2"), /*PGPIO7------gpio43*/ \ + SOCKET_PINCTRL_PIN(sock, 41, "SMBCK2"), /*PGPIO8------gpio44*/ \ + SOCKET_PINCTRL_PIN(sock, 42, "SMBALRT"), /*PGPIO14------gpio50*/ \ + SOCKET_PINCTRL_PIN(sock, 43, "SME_I2CDT_S"), \ + SOCKET_PINCTRL_PIN(sock, 44, "SME_I2CCK_S"), \ + /*GPIO range 2*/ \ + SOCKET_PINCTRL_PIN(sock, 45, "GPIO0"), /*GPIO0--------gpio0*/ \ + SOCKET_PINCTRL_PIN(sock, 46, "GPIO1"), /*GPIO1--------gpio1*/ \ + SOCKET_PINCTRL_PIN(sock, 47, "GPIO2"), /*GPIO2--------gpio2*/ \ + SOCKET_PINCTRL_PIN(sock, 48, "GPIO3"), /*GPIO3--------gpio3*/ \ + SOCKET_PINCTRL_PIN(sock, 49, "GPIO4"), /*GPIO4--------gpio4*/ \ + SOCKET_PINCTRL_PIN(sock, 50, "GPIO5"), /*GPIO5--------gpio5*/ \ + SOCKET_PINCTRL_PIN(sock, 51, "GPIO6"), /*GPIO6--------gpio6*/ \ + SOCKET_PINCTRL_PIN(sock, 52, "GPIO7"), /*GPIO7--------gpio7*/ \ + SOCKET_PINCTRL_PIN(sock, 53, "GPIO8"), /*GPIO8--------gpio8*/ \ + SOCKET_PINCTRL_PIN(sock, 54, "GPIO9"), /*GPIO9--------gpio9*/ \ + SOCKET_PINCTRL_PIN(sock, 55, "GPIO10"), /*GPIO10-------gpio10*/ \ + SOCKET_PINCTRL_PIN(sock, 56, "GPIO11"), /*GPIO11-------gpio11*/ \ + SOCKET_PINCTRL_PIN(sock, 57, "GPIO12"), /*GPIO12-------gpio12*/ \ + SOCKET_PINCTRL_PIN(sock, 58, "GPIO13"), /*GPIO13-------gpio13*/ \ + SOCKET_PINCTRL_PIN(sock, 59, "GPIO14"), /*GPIO14-------gpio14*/ \ + SOCKET_PINCTRL_PIN(sock, 60, "GPIO15"), /*GPIO15-------gpio15*/ \ + SOCKET_PINCTRL_PIN(sock, 61, "GPIO16"), /*GPIO16-------gpio16*/ \ + SOCKET_PINCTRL_PIN(sock, 62, "GPIO17"), /*GPIO17-------gpio17*/ \ + SOCKET_PINCTRL_PIN(sock, 63, "GPIO18"), /*GPIO18-------gpio18*/ \ + SOCKET_PINCTRL_PIN(sock, 64, "GPIO19"), /*GPIO19-------gpio19*/ \ + SOCKET_PINCTRL_PIN(sock, 65, "GPIO20"), /*GPIO20-------gpio20*/ \ + SOCKET_PINCTRL_PIN(sock, 66, "GPIO21"), /*GPIO21-------gpio21*/ \ + SOCKET_PINCTRL_PIN(sock, 67, "GPIO22"), /*GPIO22-------gpio22*/ \ + SOCKET_PINCTRL_PIN(sock, 68, "GPIO23"), /*GPIO23-------gpio23*/ \ + SOCKET_PINCTRL_PIN(sock, 69, "GPIO24"), /*GPIO24-------gpio24*/ \ + SOCKET_PINCTRL_PIN(sock, 70, "GPIO25"), /*GPIO25-------gpio25*/ \ + SOCKET_PINCTRL_PIN(sock, 71, "GPIO26"), /*GPIO26-------gpio26*/ \ + SOCKET_PINCTRL_PIN(sock, 72, "GPIO27"), /*GPIO27-------gpio27*/ \ + SOCKET_PINCTRL_PIN(sock, 73, "GPIO28"), /*GPIO28-------gpio28*/ \ + SOCKET_PINCTRL_PIN(sock, 74, "GPIO29"), /*GPIO29-------gpio29*/ \ + SOCKET_PINCTRL_PIN(sock, 75, "GPIO30"), /*GPIO30-------gpio30*/ \ + SOCKET_PINCTRL_PIN(sock, 76, "GPIO31"), /*GPIO31-------gpio31*/ \ + SOCKET_PINCTRL_PIN(sock, 77, "GPIO32"), /*GPIO32-------gpio32*/ \ + SOCKET_PINCTRL_PIN(sock, 78, "GPIO33"), /*GPIO33-------gpio33*/ \ + SOCKET_PINCTRL_PIN(sock, 79, "GPIO34"), /*GPIO34-------gpio34*/ \ + SOCKET_PINCTRL_PIN(sock, 80, "GPIO35"), /*GPIO35-------gpio35*/ \ + /*GPIO range 3*/ \ + SOCKET_PINCTRL_PIN(sock, 81, "LPCCLK"), /*PGPIO16------gpio52*/ \ + SOCKET_PINCTRL_PIN(sock, 82, "LPCDRQ1"), /*PGPIO17------gpio53*/ \ + SOCKET_PINCTRL_PIN(sock, 83, "LPCDRQ0"), /*PGPIO18------gpio54*/ \ + SOCKET_PINCTRL_PIN(sock, 84, "LPCFRAME"), /*PGPIO19------gpio55*/ \ + SOCKET_PINCTRL_PIN(sock, 85, "LPCAD3"), /*PGPIO20------gpio56*/ \ + SOCKET_PINCTRL_PIN(sock, 86, "LPCAD2"), /*PGPIO21------gpio57*/ \ + SOCKET_PINCTRL_PIN(sock, 87, "LPCAD1"), /*PGPIO22------gpio58*/ \ + SOCKET_PINCTRL_PIN(sock, 88, "LPCAD0"), /*PGPIO23------gpio59*/ \ + SOCKET_PINCTRL_PIN(sock, 89, "SERIRQ"), /*PGPIO24------gpio60*/ \ + /*GPIO range 4*/ \ + SOCKET_PINCTRL_PIN(sock, 90, "ESPICLK"), /*PGPIO15------gpio51*/ \ + /*GPIO range 5*/ \ + SOCKET_PINCTRL_PIN(sock, 91, "ESPIRST"), /*PGPIO29------gpio65*/ \ + SOCKET_PINCTRL_PIN(sock, 92, "ESPICS"), /*PGPIO30------gpio66*/ \ + SOCKET_PINCTRL_PIN(sock, 93, "ESPIIO3"), /*PGPIO31------gpio67*/ \ + /*GPIO range 6*/ \ + SOCKET_PINCTRL_PIN(sock, 94, "ESPIIO2"), /*PGPIO4------gpio40*/ \ + SOCKET_PINCTRL_PIN(sock, 95, "ESPIIO1"), /*PGPIO5------gpio41*/ \ + SOCKET_PINCTRL_PIN(sock, 96, "ESPIIO0"), /*PGPIO6------gpio42*/ \ + /* jump */ \ + SOCKET_PINCTRL_PIN(sock, 97, "SPIDI"), \ + SOCKET_PINCTRL_PIN(sock, 98, "SPIDO"), \ + SOCKET_PINCTRL_PIN(sock, 99, "SPICLK"), \ + SOCKET_PINCTRL_PIN(sock, 100, "SPISS"), \ + SOCKET_PINCTRL_PIN(sock, 101, "TPMRST"), \ + SOCKET_PINCTRL_PIN(sock, 102, "TPMIRQ"), \ + SOCKET_PINCTRL_PIN(sock, 103, "MSPIDI"), \ + SOCKET_PINCTRL_PIN(sock, 104, "MSPIDO"), \ + SOCKET_PINCTRL_PIN(sock, 105, "MSPIIO2"), \ + SOCKET_PINCTRL_PIN(sock, 106, "MSPIIO3"), \ + SOCKET_PINCTRL_PIN(sock, 107, "MSPICLK"), \ + SOCKET_PINCTRL_PIN(sock, 108, "MSPISS0"), \ + /*GPIO range 7*/ \ + SOCKET_PINCTRL_PIN(sock, 109, "MSPISS1"), /*PGPIO9------gpio45*/ \ + /*GPIO range 8 */ \ + SOCKET_PINCTRL_PIN(sock, 110, "MSPISS2"), /*PGPIO22------gpio58*/ \ + /*GPIO range 9*/ \ + SOCKET_PINCTRL_PIN(sock, 111, "SPIDEVINT"), /*PGPIO25------gpio61*/ \ + /*jump*/ \ + SOCKET_PINCTRL_PIN(sock, 112, "ZLSDATA_TX_P0"), \ + SOCKET_PINCTRL_PIN(sock, 113, "ZLSDATA_RX_P0"), \ + SOCKET_PINCTRL_PIN(sock, 114, "ZLSDATA_TX_P1"), \ + SOCKET_PINCTRL_PIN(sock, 115, "ZLSDATA_RX_P1"), \ + SOCKET_PINCTRL_PIN(sock, 116, "ZLSDATA_TX_P2"), \ + SOCKET_PINCTRL_PIN(sock, 117, "ZLSDATA_RX_P2"), \ + SOCKET_PINCTRL_PIN(sock, 118, "BOOT_EN"), \ + SOCKET_PINCTRL_PIN(sock, 119, "BOOT_DONE"), \ + SOCKET_PINCTRL_PIN(sock, 120, "MST_SKT"), \ + SOCKET_PINCTRL_PIN(sock, 121, "HRX_BEVO_CLK"), \ + SOCKET_PINCTRL_PIN(sock, 122, "HRX_BEVO_DATA"), \ + SOCKET_PINCTRL_PIN(sock, 123, "HTX_BEVO_CLK"), \ + SOCKET_PINCTRL_PIN(sock, 124, "HTX_BEVO_DATA"), \ + SOCKET_PINCTRL_PIN(sock, 125, "THRMTRIP_I"), \ + SOCKET_PINCTRL_PIN(sock, 126, "CLK50M_I"), \ + SOCKET_PINCTRL_PIN(sock, 127, "CLK50M_O"), \ + SOCKET_PINCTRL_PIN(sock, 128, "PCIRST_IO"), \ + SOCKET_PINCTRL_PIN(sock, 129, "RSMRST_IO"), \ + SOCKET_PINCTRL_PIN(sock, 130, "PWRGD_IO"), \ + SOCKET_PINCTRL_PIN(sock, 131, "CLK32K_IO"), \ + SOCKET_PINCTRL_PIN(sock, 132, "BIOSSEL"), \ + SOCKET_PINCTRL_PIN(sock, 133, "THRMRIP"), \ + /*GPIO range 10 */ \ + SOCKET_PINCTRL_PIN(sock, 134, "THRM"), /*PGPIO26------gpio62*/ \ + /*GPIO range 11*/ \ + SOCKET_PINCTRL_PIN(sock, 135, "PEXWAKE"), /*PGPIO10------gpio46*/ \ + /*jump*/ \ + SOCKET_PINCTRL_PIN(sock, 136, "PWRBTN"), \ + SOCKET_PINCTRL_PIN(sock, 137, "PCIRST"), \ + /*GPIO range 12*/ \ + SOCKET_PINCTRL_PIN(sock, 138, "SPKR"), /*PGPIO27------gpio63*/ \ + SOCKET_PINCTRL_PIN(sock, 139, "PME"), /*PGPIO28------gpio64*/ \ + SOCKET_PINCTRL_PIN(sock, 140, "SUSA"), \ + SOCKET_PINCTRL_PIN(sock, 141, "SUSB"), \ + SOCKET_PINCTRL_PIN(sock, 142, "SUSC"), \ + SOCKET_PINCTRL_PIN(sock, 143, "SVID0_VREN"), \ + SOCKET_PINCTRL_PIN(sock, 144, "SVID1_VREN"), + /* kh50000 pin define */ -static const struct pinctrl_pin_desc kh50000_pins[] = { - PINCTRL_PIN(0, "IOD_CLK27M_G0"), - PINCTRL_PIN(1, "IOD_CLK27M_G1"), - PINCTRL_PIN(2, "IOD_CLK27M_G2"), - PINCTRL_PIN(3, "IOD_CLK27M_G3"), - PINCTRL_PIN(4, "IOD_CPURST_G0"), - PINCTRL_PIN(5, "IOD_CPURST_G1"), - PINCTRL_PIN(6, "IOD_CPURST_G2"), - PINCTRL_PIN(7, "IOD_CPURST_G3"), - PINCTRL_PIN(8, "IOD_RSMRST_G0"), - PINCTRL_PIN(9, "IOD_RSMRST_G1"), - PINCTRL_PIN(10, "IOD_RSMRST_G2"), - PINCTRL_PIN(11, "IOD_RSMRST_G3"), - PINCTRL_PIN(12, "IOD_PWROK_G0"), - PINCTRL_PIN(13, "IOD_PWROK_G1"), - PINCTRL_PIN(14, "IOD_PWROK_G2"), - PINCTRL_PIN(15, "IOD_PWROK_G3"), - PINCTRL_PIN(16, "IOD_THRMTRIP_G0"), - PINCTRL_PIN(17, "IOD_THRMTRIP_G1"), - PINCTRL_PIN(18, "IOD_THRMTRIP_G2"), - PINCTRL_PIN(19, "IOD_THRMTRIP_G3"), - PINCTRL_PIN(20, "IOD_CLK50M_G0"), - PINCTRL_PIN(21, "IOD_CLK50M_G1"), - PINCTRL_PIN(22, "IOD_CLK50M_G2"), - PINCTRL_PIN(23, "IOD_CLK50M_G3"), - /* GPIO range 0 */ - PINCTRL_PIN(24, "USBHOC0"), - PINCTRL_PIN(25, "USBHOC1"), - PINCTRL_PIN(26, "USBHOC2"), - PINCTRL_PIN(27, "USBHOC3"), - PINCTRL_PIN(28, "I3C0DT"), - PINCTRL_PIN(29, "I3C0CK"), - PINCTRL_PIN(30, "I3C1DT"), - PINCTRL_PIN(31, "I3C1CK"), - PINCTRL_PIN(32, "I3C2DT"), - PINCTRL_PIN(33, "I3C2CK"), - PINCTRL_PIN(34, "I3C3DT"), - PINCTRL_PIN(35, "I3C3CK"), - PINCTRL_PIN(36, "SMBDT0"), - /* GPIO range 1 */ - PINCTRL_PIN(37, "SMBCK0"), - PINCTRL_PIN(38, "SMBDT1"), - PINCTRL_PIN(39, "SMBCK1"), - PINCTRL_PIN(40, "SMBDT2"), - PINCTRL_PIN(41, "SMBCK2"), - PINCTRL_PIN(42, "SMBALRT"), - PINCTRL_PIN(43, "SME_I2CDT_S"), - PINCTRL_PIN(44, "SME_I2CCK_S"), - /* GPIO range 2 */ - PINCTRL_PIN(45, "GPIO0"), - PINCTRL_PIN(46, "GPIO1"), - PINCTRL_PIN(47, "GPIO2"), - PINCTRL_PIN(48, "GPIO3"), - PINCTRL_PIN(49, "GPIO4"), - PINCTRL_PIN(50, "GPIO5"), - PINCTRL_PIN(51, "GPIO6"), - PINCTRL_PIN(52, "GPIO7"), - PINCTRL_PIN(53, "GPIO8"), - PINCTRL_PIN(54, "GPIO9"), - PINCTRL_PIN(55, "GPIO10"), - PINCTRL_PIN(56, "GPIO11"), - PINCTRL_PIN(57, "GPIO12"), - PINCTRL_PIN(58, "GPIO13"), - PINCTRL_PIN(59, "GPIO14"), - PINCTRL_PIN(60, "GPIO15"), - PINCTRL_PIN(61, "GPIO16"), - PINCTRL_PIN(62, "GPIO17"), - PINCTRL_PIN(63, "GPIO18"), - PINCTRL_PIN(64, "GPIO19"), - PINCTRL_PIN(65, "GPIO20"), - PINCTRL_PIN(66, "GPIO21"), - PINCTRL_PIN(67, "GPIO22"), - PINCTRL_PIN(68, "GPIO23"), - PINCTRL_PIN(69, "GPIO24"), - PINCTRL_PIN(70, "GPIO25"), - PINCTRL_PIN(71, "GPIO26"), - PINCTRL_PIN(72, "GPIO27"), - PINCTRL_PIN(73, "GPIO28"), - PINCTRL_PIN(74, "GPIO29"), - PINCTRL_PIN(75, "GPIO30"), - PINCTRL_PIN(76, "GPIO31"), - PINCTRL_PIN(77, "GPIO32"), - PINCTRL_PIN(78, "GPIO33"), - PINCTRL_PIN(79, "GPIO34"), - PINCTRL_PIN(80, "GPIO35"), - /* GPIO range 3 */ - PINCTRL_PIN(81, "LPCCLK"), - PINCTRL_PIN(82, "LPCDRQ1"), - PINCTRL_PIN(83, "LPCDRQ0"), - PINCTRL_PIN(84, "LPCFRAME"), - PINCTRL_PIN(85, "LPCAD3"), - PINCTRL_PIN(86, "LPCAD2"), - PINCTRL_PIN(87, "LPCAD1"), - PINCTRL_PIN(88, "LPCAD0"), - PINCTRL_PIN(89, "SERIRQ"), - /* GPIO range 4 */ - PINCTRL_PIN(90, "ESPICLK"), - /* GPIO range 5 */ - PINCTRL_PIN(91, "ESPIRST"), - PINCTRL_PIN(92, "ESPICS"), - PINCTRL_PIN(93, "ESPIIO3"), - /* GPIO range 6 */ - PINCTRL_PIN(94, "ESPIIO2"), - PINCTRL_PIN(95, "ESPIIO1"), - PINCTRL_PIN(96, "ESPIIO0"), - PINCTRL_PIN(97, "SPIDI"), - PINCTRL_PIN(98, "SPIDO"), - PINCTRL_PIN(99, "SPICLK"), - PINCTRL_PIN(100, "SPISS"), - PINCTRL_PIN(101, "TPMRST"), - PINCTRL_PIN(102, "TPMIRQ"), - PINCTRL_PIN(103, "MSPIDI"), - PINCTRL_PIN(104, "MSPIDO"), - PINCTRL_PIN(105, "MSPIIO2"), - PINCTRL_PIN(106, "MSPIIO3"), - PINCTRL_PIN(107, "MSPICLK"), - PINCTRL_PIN(108, "MSPISS0"), - /* GPIO range 7 */ - PINCTRL_PIN(109, "MSPISS1"), - /* GPIO range 8 */ - PINCTRL_PIN(110, "MSPISS2"), - /* GPIO range 9 */ - PINCTRL_PIN(111, "SPIDEVINT"), - PINCTRL_PIN(112, "ZLSDATA_TX_P0"), - PINCTRL_PIN(113, "ZLSDATA_RX_P0"), - PINCTRL_PIN(114, "ZLSDATA_TX_P1"), - PINCTRL_PIN(115, "ZLSDATA_RX_P1"), - PINCTRL_PIN(116, "ZLSDATA_TX_P2"), - PINCTRL_PIN(117, "ZLSDATA_RX_P2"), - PINCTRL_PIN(118, "BOOT_EN"), - PINCTRL_PIN(119, "BOOT_DONE"), - PINCTRL_PIN(120, "MST_SKT"), - PINCTRL_PIN(121, "HRX_BEVO_CLK"), - PINCTRL_PIN(122, "HRX_BEVO_DATA"), - PINCTRL_PIN(123, "HTX_BEVO_CLK"), - PINCTRL_PIN(124, "HTX_BEVO_DATA"), - PINCTRL_PIN(125, "THRMTRIP_I"), - PINCTRL_PIN(126, "CLK50M_I"), - PINCTRL_PIN(127, "CLK50M_O"), - PINCTRL_PIN(128, "PCIRST_IO"), - PINCTRL_PIN(129, "RSMRST_IO"), - PINCTRL_PIN(130, "PWRGD_IO"), - PINCTRL_PIN(131, "CLK32K_IO"), - PINCTRL_PIN(132, "BIOSSEL"), - PINCTRL_PIN(133, "THRMRIP"), - /* GPIO range 10 */ - PINCTRL_PIN(134, "THRM"), - /* GPIO range 11 */ - PINCTRL_PIN(135, "PEXWAKE"), - PINCTRL_PIN(136, "PWRBTN"), - PINCTRL_PIN(137, "PCIRST"), - /* GPIO range 12 */ - PINCTRL_PIN(138, "SPKR"), - PINCTRL_PIN(139, "PME"), - PINCTRL_PIN(140, "SUSA"), - PINCTRL_PIN(141, "SUSB"), - PINCTRL_PIN(142, "SUSC"), - PINCTRL_PIN(143, "SVID0_VREN"), - PINCTRL_PIN(144, "SVID1_VREN"), +static const struct pinctrl_pin_desc kh50000_pins_0[] = { + KH50000_SOCKET_PINS(0) +}; + +static const struct pinctrl_pin_desc kh50000_pins_1[] = { + KH50000_SOCKET_PINS(1) +}; + +static const struct pinctrl_pin_desc kh50000_pins_2[] = { + KH50000_SOCKET_PINS(2) +}; + +static const struct pinctrl_pin_desc kh50000_pins_3[] = { + KH50000_SOCKET_PINS(3) }; #define NOT_DEFINE -30000 @@ -326,23 +344,75 @@ static zx_gpio_type kh50000_gpio_type(struct zhaoxin_pinctrl *pctrl, unsigned in static void kh50000_gpio_init(struct zhaoxin_pinctrl *pctrl) { - pctrl->pmio_base = 0x800; - pctrl->pmio_rx90 = 0x90; - pctrl->pmio_rx8c = 0x8c; + struct resource *res_pmio; + struct platform_device *pdev = to_platform_device(pctrl->dev); + + res_pmio = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res_pmio) { + dev_err(&pdev->dev, "can't fetch device pmio resource info\n"); + return; + } + + if (!request_region(res_pmio->start, resource_size(res_pmio), pdev->name)) { + dev_err(&pdev->dev, "can't request region\n"); + return; + } + pctrl->pmio_base = res_pmio->start; + pctrl->pmio_rx90 = 4; + pctrl->pmio_rx8c = 0; zx_pad_write16(pctrl, 0xF8, 0x7F); dev_info(pctrl->dev, "KH50000 private init\n"); } -static const struct zhaoxin_pinctrl_soc_data kh50000_soc_data = { - .pins = kh50000_pins, - .npins = ARRAY_SIZE(kh50000_pins), +static const struct zhaoxin_pinctrl_soc_data socket_0_soc_data = { + .uid = "0", + .pins = kh50000_pins_0, + .npins = ARRAY_SIZE(kh50000_pins_0), .pin_topologys = kh50000_pin_topologys, .gpio_type = kh50000_gpio_type, .private_init = kh50000_gpio_init, .zhaoxin_pin_maps = kh50000_pinmap_gpps, .pin_map_size = ARRAY_SIZE(kh50000_pinmap_gpps), }; +static const struct zhaoxin_pinctrl_soc_data socket_1_soc_data = { + .uid = "1", + .pins = kh50000_pins_1, + .npins = ARRAY_SIZE(kh50000_pins_1), + .pin_topologys = kh50000_pin_topologys, + .gpio_type = kh50000_gpio_type, + .private_init = kh50000_gpio_init, + .zhaoxin_pin_maps = kh50000_pinmap_gpps, + .pin_map_size = ARRAY_SIZE(kh50000_pinmap_gpps), +}; +static const struct zhaoxin_pinctrl_soc_data socket_2_soc_data = { + .uid = "2", + .pins = kh50000_pins_2, + .npins = ARRAY_SIZE(kh50000_pins_2), + .pin_topologys = kh50000_pin_topologys, + .gpio_type = kh50000_gpio_type, + .private_init = kh50000_gpio_init, + .zhaoxin_pin_maps = kh50000_pinmap_gpps, + .pin_map_size = ARRAY_SIZE(kh50000_pinmap_gpps), +}; +static const struct zhaoxin_pinctrl_soc_data socket_3_soc_data = { + .uid = "3", + .pins = kh50000_pins_3, + .npins = ARRAY_SIZE(kh50000_pins_3), + .pin_topologys = kh50000_pin_topologys, + .gpio_type = kh50000_gpio_type, + .private_init = kh50000_gpio_init, + .zhaoxin_pin_maps = kh50000_pinmap_gpps, + .pin_map_size = ARRAY_SIZE(kh50000_pinmap_gpps), +}; + +static const struct zhaoxin_pinctrl_soc_data *kh50000_soc_data[] = { + &socket_0_soc_data, + &socket_1_soc_data, + &socket_2_soc_data, + &socket_3_soc_data, + NULL, +}; static const struct acpi_device_id kh50000_pinctrl_acpi_match[] = { { "KH8344B", (kernel_ulong_t)&kh50000_soc_data }, @@ -355,7 +425,7 @@ static const struct dev_pm_ops kh50000_pinctrl_pm_ops = { }; static struct platform_driver kh50000_pinctrl_driver = { - .probe = zhaoxin_pinctrl_probe_by_hid, + .probe = zhaoxin_pinctrl_probe_by_uid, .driver = { .name = "kh50000-pinctrl", .acpi_match_table = kh50000_pinctrl_acpi_match, diff --git a/drivers/pinctrl/zhaoxin/pinctrl-zhaoxin.h b/drivers/pinctrl/zhaoxin/pinctrl-zhaoxin.h index c0516daa5897a..1525d10cc53f5 100644 --- a/drivers/pinctrl/zhaoxin/pinctrl-zhaoxin.h +++ b/drivers/pinctrl/zhaoxin/pinctrl-zhaoxin.h @@ -20,6 +20,8 @@ struct platform_device; struct device; struct zhaoxin_pinctrl; +#define SOCKET_PINCTRL_PIN(sock, a, b) PINCTRL_PIN(a, b"_"#sock) + #define PMIO_RX90 100 #define PMIO_RX8C 200 From 3decc7d778f67e1a54075e950711dddf41f3c466 Mon Sep 17 00:00:00 2001 From: Wentao Guan Date: Wed, 13 May 2026 13:14:32 +0800 Subject: [PATCH 15/38] CI: arm64: disable debuginfo to speed up build test Signed-off-by: Wentao Guan --- .github/workflows/build-kernel-arm64.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/build-kernel-arm64.yml b/.github/workflows/build-kernel-arm64.yml index 5ee82c85d87bc..14f0a5929ebb4 100644 --- a/.github/workflows/build-kernel-arm64.yml +++ b/.github/workflows/build-kernel-arm64.yml @@ -30,10 +30,16 @@ jobs: run: | # .config make deepin_arm64_desktop_defconfig + scripts/config --enable CONFIG_DEBUG_INFO_NONE + scripts/config --disable CONFIG_DEBUG_INFO_DWARF5 + make olddefconfig make -j$(nproc) - name: 'Clang build kernel' run: | # .config make LLVM=-18 deepin_arm64_desktop_defconfig + scripts/config --enable CONFIG_DEBUG_INFO_NONE + scripts/config --disable CONFIG_DEBUG_INFO_DWARF5 + make LLVM=-18 olddefconfig make LLVM=-18 -j$(nproc) From ebe68fc7f4a7c8ab79f39c74df88034069926922 Mon Sep 17 00:00:00 2001 From: Yazen Ghannam Date: Thu, 22 Aug 2024 19:24:22 -0500 Subject: [PATCH 16/38] efi/cper: Print correctable AER information Currently, cper_print_pcie() only logs Uncorrectable Error Status, Mask and Severity registers along with the TLP header. If a correctable error is received immediately preceding or following an Uncorrectable Fatal Error, its information is lost since Correctable Error Status and Mask registers are not logged. As such, to avoid skipping any possible error information, Correctable Error Status and Mask registers should also be logged. Additionally, ensure that AER information is also available through cper_print_pcie() for Correctable and Uncorrectable Non-Fatal Errors. Signed-off-by: Yazen Ghannam Tested-by: Avadhut Naik Signed-off-by: Avadhut Naik Signed-off-by: Ard Biesheuvel Signed-off-by: LeoLiu-oc --- drivers/firmware/efi/cper.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c index 511309542d245..5d31b602ef42c 100644 --- a/drivers/firmware/efi/cper.c +++ b/drivers/firmware/efi/cper.c @@ -547,12 +547,17 @@ static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie, "%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n", pfx, pcie->bridge.secondary_status, pcie->bridge.control); - /* Fatal errors call __ghes_panic() before AER handler prints this */ - if ((pcie->validation_bits & CPER_PCIE_VALID_AER_INFO) && - (gdata->error_severity & CPER_SEV_FATAL)) { + /* + * Print all valid AER info. Record may be from BERT (boot-time) or GHES (run-time). + * + * Fatal errors call __ghes_panic() before AER handler prints this. + */ + if (pcie->validation_bits & CPER_PCIE_VALID_AER_INFO) { struct aer_capability_regs *aer; aer = (struct aer_capability_regs *)pcie->aer_info; + printk("%saer_cor_status: 0x%08x, aer_cor_mask: 0x%08x\n", + pfx, aer->cor_status, aer->cor_mask); printk("%saer_uncor_status: 0x%08x, aer_uncor_mask: 0x%08x\n", pfx, aer->uncor_status, aer->uncor_mask); printk("%saer_uncor_severity: 0x%08x\n", From 56c01ef1371dd2501cf4272c402a2ab3b09376c4 Mon Sep 17 00:00:00 2001 From: Ma Mingrui Date: Tue, 12 May 2026 11:46:21 +0800 Subject: [PATCH 17/38] devfreq: Add phytium noc devfreq driver This adds the DEVFREQ driver for Phytium Net On Chip.It adjusts frequency for noc based on load bandwidth obtained from register. Signed-off-by: Li Jiayi Signed-off-by: Li Mingzhe Signed-off-by: Wang Yinfeng Signed-off-by: Ma Mingrui --- drivers/devfreq/Kconfig | 9 + drivers/devfreq/Makefile | 1 + drivers/devfreq/phytium_noc.c | 428 ++++++++++++++++++++++++++++++++++ 3 files changed, 438 insertions(+) create mode 100644 drivers/devfreq/phytium_noc.c diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index 3c4862a752b5a..858893f41f577 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -150,6 +150,15 @@ config ARM_SUN8I_A33_MBUS_DEVFREQ This adds the DEVFREQ driver for the MBUS controller in some Allwinner sun8i (A33 through H3) and sun50i (A64 and H5) SoCs. +config ARM_PHYTIUM_NOC_DEVFREQ + tristate "ARM PHYTIUM NOC DEVFREQ Driver" + depends on ARCH_PHYTIUM || COMPILE_TEST + depends on ACPI + select DEVFREQ_GOV_SIMPLE_ONDEMAND + help + This adds the DEVFREQ driver for Phytium Net On Chip. + It adjusts frequency for noc based on load bandwidth obtained from register. + source "drivers/devfreq/event/Kconfig" endif # PM_DEVFREQ diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index bf40d04928d03..5d6275c0f8893 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o obj-$(CONFIG_ARM_IMX_BUS_DEVFREQ) += imx-bus.o obj-$(CONFIG_ARM_IMX8M_DDRC_DEVFREQ) += imx8m-ddrc.o obj-$(CONFIG_ARM_MEDIATEK_CCI_DEVFREQ) += mtk-cci-devfreq.o +obj-$(CONFIG_ARM_PHYTIUM_NOC_DEVFREQ) += phytium_noc.o obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o obj-$(CONFIG_ARM_SUN8I_A33_MBUS_DEVFREQ) += sun8i-a33-mbus.o obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra30-devfreq.o diff --git a/drivers/devfreq/phytium_noc.c b/drivers/devfreq/phytium_noc.c new file mode 100644 index 0000000000000..39eb12cd0692a --- /dev/null +++ b/drivers/devfreq/phytium_noc.c @@ -0,0 +1,428 @@ +// SPDX-License-Identifier: GPL-1.0 +/* + *phytium_noc.c - Phytium Processor noc Frequency Driver + * + *Copyright (C) 2024,Phytium Technology Co.,Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MIX_AMINI6 0x26eab800 +#define MINI_SIZE 0x400 +#define CNT_ENABLE 0x000 +#define WORK_STATE 0X004 +#define CLR_EN 0X010 +#define SNAPSHOT_EN 0X014 +#define INT_CTRL_CLR 0x024 +#define WR_NOLAST_HANDSHARK_NUM 0x44 + +#define DEBUG +#define DEVICE_TYPE 7 + +#define NOCFREQ_DRIVER_VERSION "1.0.0" + +struct phytium_nocfreq { + struct device *dev; + + struct devfreq *devfreq; + struct devfreq_dev_profile profile; + struct devfreq_simple_ondemand_data ondemand_data; + + void __iomem *reg_noc; + struct mutex lock; + + unsigned long rate, target_rate; + unsigned int freq_count; + unsigned long freq_table[]; +}; + +static u32 phytium_nocfreq_get_peak_bw(struct phytium_nocfreq *priv) +{ + /*Returns the peak number of dmu read/write commands on the axi bus.*/ + unsigned long peak_bw, bw_0, bw_1, bw_2, bw_3; + + bw_0 = readl_relaxed(priv->reg_noc + WR_NOLAST_HANDSHARK_NUM); + bw_1 = readl_relaxed(priv->reg_noc + MINI_SIZE*1 + WR_NOLAST_HANDSHARK_NUM); + bw_2 = readl_relaxed(priv->reg_noc + MINI_SIZE*2 + WR_NOLAST_HANDSHARK_NUM); + bw_3 = readl_relaxed(priv->reg_noc + MINI_SIZE*3 + WR_NOLAST_HANDSHARK_NUM); + + peak_bw = bw_0; + if (bw_1 > peak_bw) + peak_bw = bw_1; + if (bw_2 > peak_bw) + peak_bw = bw_2; + if (bw_3 > peak_bw) + peak_bw = bw_3; + + return peak_bw; +} + +static void phytium_nocfreq_restart_handshark_counters(struct phytium_nocfreq *priv) +{ + + /*clear interrupt*/ + + writel_relaxed(0x80000000, priv->reg_noc + INT_CTRL_CLR); + writel_relaxed(0x80000000, priv->reg_noc + MINI_SIZE*1 + INT_CTRL_CLR); + writel_relaxed(0x80000000, priv->reg_noc + MINI_SIZE*2 + INT_CTRL_CLR); + writel_relaxed(0x80000000, priv->reg_noc + MINI_SIZE*3 + INT_CTRL_CLR); + + /*clear counters*/ + writel_relaxed(0x1, priv->reg_noc + CLR_EN); + writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*1 + CLR_EN); + writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*2 + CLR_EN); + writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*3 + CLR_EN); +} + + +static int phytium_noc_set_freq(struct device *dev, unsigned long freq) +{ + struct phytium_nocfreq *priv = dev_get_drvdata(dev); + acpi_handle handle = ACPI_HANDLE(dev); + union acpi_object args[4]; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + acpi_status status; + unsigned long long ret; + + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = DEVICE_TYPE; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = freq; + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = 0; + args[3].type = ACPI_TYPE_INTEGER; + args[3].integer.value = 0; + + mutex_lock(&priv->lock); + status = acpi_evaluate_integer(handle, "PSCF", &arg_list, &ret); + mutex_unlock(&priv->lock); + + if (ACPI_FAILURE(status)) { + dev_err(dev, "No PSCF method\n"); + return -EIO; + } + + if (ret) { + dev_err(dev, "Failed to set the freq to %lu\n", freq); + return -EIO; + } + dev_info(dev, "set target_freq = %lu khz\n", freq); + return 0; +} + +static int phytium_noc_target(struct device *dev, unsigned long *freq, u32 flags) +{ + struct phytium_nocfreq *priv = dev_get_drvdata(dev); + unsigned long old_freq = priv->rate; + unsigned long target_rate; + struct dev_pm_opp *opp; + int ret; + + opp = devfreq_recommended_opp(dev, freq, flags); + if (IS_ERR(opp)) + return PTR_ERR(opp); + + target_rate = dev_pm_opp_get_freq(opp); + dev_pm_opp_put(opp); + + if (target_rate == old_freq) + return 0; + /* + * Read back the clk rate to verify switch was correct and so that + * we can report it on all error paths. + */ + ret = phytium_noc_set_freq(dev, target_rate); + + if (ret) { + dev_warn(dev, "failed to set noc frequency: %d\n", ret); + *freq = old_freq; + } + priv->rate = target_rate; + return ret; + +} + +static int phytium_noc_get_cur_freq(struct device *dev, unsigned long *freq) +{ + struct phytium_nocfreq *priv = dev_get_drvdata(dev); + acpi_handle handle = ACPI_HANDLE(dev); + union acpi_object args[3]; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + acpi_status status; + unsigned long long ret; + + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = DEVICE_TYPE; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = 0; + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = 0; + + mutex_lock(&priv->lock); + status = acpi_evaluate_integer(handle, "PGCF", &arg_list, &ret); + mutex_unlock(&priv->lock); + if (ACPI_FAILURE(status)) { + dev_err(dev, "No PGCF method\n"); + return -EIO; + } + + if (ret < 0) { + dev_err(dev, "Failed to get the freq\n"); + return -EIO; + } + *freq = ret; + + return 0; +} + +static int phytium_noc_get_freq_info(struct device *dev, u32 flags) +{ + struct phytium_nocfreq *priv = dev_get_drvdata(dev); + + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object args[3], *package, *element; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + acpi_handle handle = ACPI_HANDLE(dev); + acpi_status status; + int i; + + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = flags; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = 0; + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = 0; + + status = acpi_evaluate_object(handle, "PGCL", &arg_list, &buffer); + if (ACPI_FAILURE(status)) { + dev_err(dev, "No PGCL method\n"); + return -EIO; + } + if (!buffer.length) { + dev_err(dev, "buffer is NULL\n"); + return -EINVAL; + } + + package = buffer.pointer; + + element = &package->package.elements[1]; + priv->freq_count = element->integer.value; + + for (i = 0; i < priv->freq_count; i++) { + element = &package->package.elements[i+2]; + priv->freq_table[i] = element->integer.value; + dev_dbg(dev, "freq_table[%d] = %llu\n", i, element->integer.value); + } + + return 0; + +} + +static int get_freq_count(struct device *dev) +{ + int freq_count = -1; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object args[3], *package, *element; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + acpi_handle handle = ACPI_HANDLE(dev); + acpi_status status; + + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = DEVICE_TYPE; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = 0; + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = 0; + + status = acpi_evaluate_object(handle, "PGCL", &arg_list, &buffer); + if (ACPI_FAILURE(status)) { + dev_err(dev, "No PGCL method\n"); + return -EIO; + } + if (!buffer.length) { + dev_err(dev, "buffer is NULL\n"); + return -EINVAL; + } + + package = buffer.pointer; + + element = &package->package.elements[1]; + freq_count = element->integer.value; + dev_dbg(dev, "freq_count = %d\n", freq_count); + + return freq_count; +} + +static int phytium_noc_get_dev_status(struct device *dev, + struct devfreq_dev_status *stat) +{ + struct phytium_nocfreq *priv = dev_get_drvdata(dev); + unsigned int val; + + writel_relaxed(0x1, priv->reg_noc + SNAPSHOT_EN); + writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*1 + SNAPSHOT_EN); + writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*2 + SNAPSHOT_EN); + writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*3 + SNAPSHOT_EN); + + val = DIV_ROUND_CLOSEST(priv->rate * 100, priv->profile.initial_freq); + stat->busy_time = phytium_nocfreq_get_peak_bw(priv); + stat->total_time = 320000 * val; + stat->current_frequency = priv->rate; + + phytium_nocfreq_restart_handshark_counters(priv); + dev_info(dev, "Using %lu/%lu (%lu%%) at %lu KHz\n", + stat->busy_time, stat->total_time, + DIV_ROUND_CLOSEST(stat->busy_time * 100, stat->total_time), + stat->current_frequency); + + return 0; +} + + +static int phytium_nocfreq_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phytium_nocfreq *priv; + const char *gov = DEVFREQ_GOV_SIMPLE_ONDEMAND; + int i, ret; + unsigned int max_state; + + max_state = get_freq_count(dev); + + priv = devm_kzalloc(dev, struct_size(priv, freq_table, max_state), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + mutex_init(&priv->lock); + platform_set_drvdata(pdev, priv); + + priv->reg_noc = ioremap(MIX_AMINI6, 1028*4); + if (!priv->reg_noc) { + dev_err(dev, "failed to ioremap reg_noc\n"); + return -EIO; + } + + ret = phytium_noc_get_freq_info(dev, DEVICE_TYPE); + if (ret) { + dev_err(dev, "failed to get noc frequency info\n"); + return -EIO; + } + + priv->profile.initial_freq = priv->freq_table[0]; + priv->profile.polling_ms = 100; + priv->profile.target = phytium_noc_target; + priv->profile.get_cur_freq = phytium_noc_get_cur_freq; + priv->profile.get_dev_status = phytium_noc_get_dev_status; + priv->profile.freq_table = priv->freq_table; + priv->profile.max_state = priv->freq_count; + priv->rate = priv->freq_table[0]; + priv->ondemand_data.upthreshold = 80; + priv->ondemand_data.downdifferential = 10; + priv->profile.max_state = priv->freq_count; + + for (i = 0; i < max_state; ++i) { + ret = dev_pm_opp_add(dev, priv->freq_table[i], 0); + if (ret < 0) { + dev_err(dev, "failed to get OPP table\n"); + goto err; + } + } + priv->devfreq = devm_devfreq_add_device(dev, &priv->profile, + gov, &priv->ondemand_data); + if (IS_ERR(priv->devfreq)) { + ret = PTR_ERR(priv->devfreq); + dev_err(dev, "failed to add devfreq device: %d\n", ret); + goto err; + } + + ret = phytium_noc_set_freq(dev, priv->profile.initial_freq); + if (ret) + dev_warn(dev, "failed to init noc frequency: %d\n", ret); + + writel_relaxed(0x02, priv->reg_noc + WORK_STATE); + writel_relaxed(0x02, priv->reg_noc + MINI_SIZE*1 + WORK_STATE); + writel_relaxed(0x02, priv->reg_noc + MINI_SIZE*2 + WORK_STATE); + writel_relaxed(0x02, priv->reg_noc + MINI_SIZE*3 + WORK_STATE); + + writel_relaxed(0x3f, priv->reg_noc + CNT_ENABLE); + writel_relaxed(0x3f, priv->reg_noc + MINI_SIZE*1 + CNT_ENABLE); + writel_relaxed(0x3f, priv->reg_noc + MINI_SIZE*2 + CNT_ENABLE); + writel_relaxed(0x3f, priv->reg_noc + MINI_SIZE*3 + CNT_ENABLE); + return 0; + +err: + dev_pm_opp_of_remove_table(dev); + return ret; +} + +static int phytium_nocfreq_remove(struct platform_device *pdev) +{ + struct phytium_nocfreq *priv = platform_get_drvdata(pdev); + unsigned long initial_freq = priv->profile.initial_freq; + struct device *dev = &pdev->dev; + int ret; + + writel_relaxed(0x0, priv->reg_noc + CNT_ENABLE); + writel_relaxed(0x0, priv->reg_noc + MINI_SIZE*1 + CNT_ENABLE); + writel_relaxed(0x0, priv->reg_noc + MINI_SIZE*2 + CNT_ENABLE); + writel_relaxed(0x0, priv->reg_noc + MINI_SIZE*3 + CNT_ENABLE); + + writel_relaxed(0x1, priv->reg_noc + CLR_EN); + writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*1 + CLR_EN); + writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*2 + CLR_EN); + writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*3 + CLR_EN); + + ret = phytium_noc_set_freq(dev, initial_freq); + if (ret) + dev_warn(dev, "failed to restore NOC frequency: %d\n", ret); + + iounmap(priv->reg_noc); + + devfreq_remove_device(priv->devfreq); + dev_pm_opp_remove_all_dynamic(dev); + + return 0; +} + +static const struct acpi_device_id phytium_noc_acpi_ids[] = { + {"PHYT0047"}, + {}, +}; + +MODULE_DEVICE_TABLE(acpi, phytium_noc_acpi_ids); + +static struct platform_driver phytium_nocfreq_driver = { + .probe = phytium_nocfreq_probe, + .remove = phytium_nocfreq_remove, + .driver = { + .name = "phytium_nocfreq", + .acpi_match_table = ACPI_PTR(phytium_noc_acpi_ids), + .suppress_bind_attrs = true, + }, +}; +module_platform_driver(phytium_nocfreq_driver); + +MODULE_DESCRIPTION("Phytium NOC Controller frequency driver"); +MODULE_AUTHOR("Li Jiayi "); +MODULE_LICENSE("GPL"); +MODULE_VERSION(NOCFREQ_DRIVER_VERSION); From 2c83091441d673e2ba167fd5a03ba5e7576abf57 Mon Sep 17 00:00:00 2001 From: Ma Mingrui Date: Tue, 12 May 2026 11:50:23 +0800 Subject: [PATCH 18/38] devfreq: Add phytium dmu devfreq driver This adds the DEVFREQ driver for Phytium DDR Memory Unit.It adjusts frequency for dmu based on load bandwidth obtained from register. Signed-off-by: Li Jiayi Signed-off-by: Li Mingzhe Signed-off-by: Wang Yinfeng Signed-off-by: Ma Mingrui --- drivers/devfreq/Kconfig | 9 + drivers/devfreq/Makefile | 1 + drivers/devfreq/phytium_dmu.c | 368 ++++++++++++++++++++++++++++++++++ 3 files changed, 378 insertions(+) create mode 100644 drivers/devfreq/phytium_dmu.c diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index 858893f41f577..c90e8fa9d7335 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -159,6 +159,15 @@ config ARM_PHYTIUM_NOC_DEVFREQ This adds the DEVFREQ driver for Phytium Net On Chip. It adjusts frequency for noc based on load bandwidth obtained from register. +config ARM_PHYTIUM_DMU_DEVFREQ + tristate "ARM PHYTIUM DMU DEVFREQ Driver" + depends on ARCH_PHYTIUM || COMPILE_TEST + depends on ACPI + select DEVFREQ_GOV_SIMPLE_ONDEMAND + help + This adds the DEVFREQ driver for Phytium DDR Memory Unit. + It adjusts frequency for dmu based on load bandwidth obtained from register. + source "drivers/devfreq/event/Kconfig" endif # PM_DEVFREQ diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index 5d6275c0f8893..c57455f9b4818 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o obj-$(CONFIG_ARM_IMX_BUS_DEVFREQ) += imx-bus.o obj-$(CONFIG_ARM_IMX8M_DDRC_DEVFREQ) += imx8m-ddrc.o obj-$(CONFIG_ARM_MEDIATEK_CCI_DEVFREQ) += mtk-cci-devfreq.o +obj-$(CONFIG_ARM_PHYTIUM_DMU_DEVFREQ) += phytium_dmu.o obj-$(CONFIG_ARM_PHYTIUM_NOC_DEVFREQ) += phytium_noc.o obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o obj-$(CONFIG_ARM_SUN8I_A33_MBUS_DEVFREQ) += sun8i-a33-mbus.o diff --git a/drivers/devfreq/phytium_dmu.c b/drivers/devfreq/phytium_dmu.c new file mode 100644 index 0000000000000..cf5898034baaf --- /dev/null +++ b/drivers/devfreq/phytium_dmu.c @@ -0,0 +1,368 @@ +// SPDX-License-Identifier: GPL-1.0 +/* + *phytium_dmu.c - Phytium Processor dmu Frequency Driver + * + *Copyright (C) 2024,Phytium Technology Co.,Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG + +#define DEVICE_TYPE 9 //DMU ID + +#define UPDATE_INTERVAL_MS 10 + +#define DMUFREQ_DRIVER_VERSION "1.0.0" + +struct phytium_dmufreq { + struct device *dev; + + struct devfreq *devfreq; + struct devfreq_dev_profile profile; + struct devfreq_simple_ondemand_data ondemand_data; + + unsigned long rate, target_rate; + unsigned long bandwidth; + + struct timer_list sampling; + struct work_struct work; + + unsigned int freq_count; + unsigned long freq_table[]; +}; + +static ktime_t stop; + +static int phytium_dmu_set_freq(struct device *dev, unsigned long freq) +{ + acpi_handle handle = ACPI_HANDLE(dev); + union acpi_object args[4]; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + acpi_status status; + unsigned long long ret; + + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = DEVICE_TYPE; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = freq; + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = 0; + args[3].type = ACPI_TYPE_INTEGER; + args[3].integer.value = 0; + + status = acpi_evaluate_integer(handle, "PSCF", &arg_list, &ret); + if (ACPI_FAILURE(status)) { + dev_err(dev, "No PSCF method\n"); + return -EIO; + } + + return 0; +} + +static int phytium_dmu_target(struct device *dev, unsigned long *freq, u32 flags) +{ + struct phytium_dmufreq *priv = dev_get_drvdata(dev); + unsigned long old_freq = priv->rate; + unsigned long target_rate; + struct dev_pm_opp *opp; + int ret; + + opp = devfreq_recommended_opp(dev, freq, flags); + if (IS_ERR(opp)) + return PTR_ERR(opp); + + target_rate = dev_pm_opp_get_freq(opp); + + dev_pm_opp_put(opp); + + if (target_rate == old_freq) + return 0; + + dev_dbg(dev, "target_rate = %lu\n", target_rate); + /* + * Read back the clk rate to verify switch was correct and so that + * we can report it on all error paths. + */ + ret = phytium_dmu_set_freq(dev, target_rate); + if (ret) { + dev_warn(dev, "failed to set DRAM frequency: %lu\n", target_rate); + return ret; + } + priv->rate = target_rate; + + return ret; + +} + +static int phytium_dmu_get_cur_freq(struct device *dev, unsigned long *freq) +{ + struct phytium_dmufreq *priv = dev_get_drvdata(dev); + + *freq = priv->rate; + + return 0; +} + +static int phytium_read_perf_counter(struct device *dev) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *package, *elements; + acpi_handle handle = ACPI_HANDLE(dev); + acpi_status status; + + status = acpi_evaluate_object(handle, "PDMU", NULL, &buffer); + if (ACPI_FAILURE(status)) { + dev_err(dev, "No PGCL method\n"); + return -EIO; + } + + package = buffer.pointer; + + elements = package->package.elements; + + return elements[0].integer.value + elements[1].integer.value; +} + +static void sampling_timer_callback(struct timer_list *t) +{ + struct phytium_dmufreq *priv = from_timer(priv, t, sampling); + + schedule_work(&priv->work); +} + +static void sampling_work_handle(struct work_struct *work) +{ + struct phytium_dmufreq *priv = container_of(work, struct phytium_dmufreq, work); + struct device *dev = priv->dev; + static unsigned long load_counter; + static int count; + unsigned long current_load; + + current_load = phytium_read_perf_counter(dev); + + load_counter += current_load; + count += 1; + + if (ktime_after(ktime_get(), stop)) { + priv->bandwidth = load_counter / count; + load_counter = 0; + count = 0; + stop = ktime_add_ms(ktime_get(), priv->profile.polling_ms); + mod_timer(&priv->sampling, jiffies + msecs_to_jiffies(UPDATE_INTERVAL_MS)); + } else + mod_timer(&priv->sampling, jiffies + msecs_to_jiffies(UPDATE_INTERVAL_MS)); +} + +static int phytium_dmu_get_dev_status(struct device *dev, + struct devfreq_dev_status *stat) +{ + struct phytium_dmufreq *priv = dev_get_drvdata(dev); + + stat->busy_time = priv->bandwidth; + stat->total_time = (500000 * priv->rate) / priv->freq_table[0]; + dev_dbg(dev, "busy_time = %lu, total_time = %lu\n", + stat->busy_time, stat->total_time); + + stat->current_frequency = priv->rate; + + return 0; +} + +static int phytium_dmu_get_freq_info(struct device *dev) +{ + struct phytium_dmufreq *priv = dev_get_drvdata(dev); + + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object args[3], *package, *element; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + acpi_handle handle = ACPI_HANDLE(dev); + acpi_status status; + int i; + + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = DEVICE_TYPE; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = 0; + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = 0; + + status = acpi_evaluate_object(handle, "PGCL", &arg_list, &buffer); + if (ACPI_FAILURE(status)) { + dev_err(dev, "No PGCL method\n"); + return -EIO; + } + + package = buffer.pointer; + + element = &package->package.elements[1]; + priv->freq_count = element->integer.value; + + for (i = 0; i < priv->freq_count; i++) { + element = &package->package.elements[i+2]; + priv->freq_table[i] = element->integer.value; + dev_dbg(dev, "freq_table[%d] = %llu\n", i, element->integer.value); + } + + return 0; + +} + +static int get_freq_count(struct device *dev) +{ + int freq_count = -1; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object args[3], *package, *element; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + acpi_handle handle = ACPI_HANDLE(dev); + acpi_status status; + + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = DEVICE_TYPE; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = 0; + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = 0; + + status = acpi_evaluate_object(handle, "PGCL", &arg_list, &buffer); + if (ACPI_FAILURE(status)) { + dev_err(dev, "No PGCL method, status = %d\n", status); + return -EIO; + } + + package = buffer.pointer; + + element = &package->package.elements[1]; + freq_count = element->integer.value; + dev_dbg(dev, "freq_count = %d\n", freq_count); + + return freq_count; +} + +static int phytium_dmufreq_probe(struct platform_device *pdev) +{ + struct phytium_dmufreq *priv; + struct device *dev = &pdev->dev; + const char *gov = DEVFREQ_GOV_SIMPLE_ONDEMAND; + int i, ret; + unsigned int max_state = get_freq_count(dev); + + if (max_state <= 0) + return max_state; + + dev->init_name = "dmufreq"; + + priv = kzalloc(sizeof(struct phytium_dmufreq) + + max_state * sizeof(unsigned long), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + + ret = phytium_dmu_get_freq_info(dev); + if (ret) { + dev_err(dev, "failed to get ddr frequency info\n"); + return -EIO; + } + + priv->profile.initial_freq = priv->freq_table[0]; + priv->profile.polling_ms = 100; + priv->profile.timer = DEVFREQ_TIMER_DELAYED; + priv->profile.target = phytium_dmu_target; + priv->profile.get_cur_freq = phytium_dmu_get_cur_freq; + priv->profile.get_dev_status = phytium_dmu_get_dev_status; + priv->profile.freq_table = priv->freq_table; + priv->rate = priv->profile.initial_freq; + priv->profile.max_state = priv->freq_count; + priv->ondemand_data.upthreshold = 80; + priv->ondemand_data.downdifferential = 10; + + for (i = 0; i < max_state; ++i) { + ret = dev_pm_opp_add(dev, priv->freq_table[i], 0); + if (ret < 0) { + dev_err(dev, "failed to get OPP table\n"); + goto err; + } + } + + priv->devfreq = devm_devfreq_add_device(dev, &priv->profile, + gov, &priv->ondemand_data); + if (IS_ERR(priv->devfreq)) { + ret = PTR_ERR(priv->devfreq); + dev_err(dev, "failed to add devfreq device: %d\n", ret); + goto err; + } + + INIT_WORK(&priv->work, sampling_work_handle); + timer_setup(&priv->sampling, sampling_timer_callback, 0); + stop = ktime_add_ms(ktime_get(), priv->profile.polling_ms); + mod_timer(&priv->sampling, jiffies + msecs_to_jiffies(UPDATE_INTERVAL_MS)); + + priv->dev = dev; + + return 0; + +err: + dev_pm_opp_of_remove_table(dev); + return ret; +} + +static int phytium_dmufreq_remove(struct platform_device *pdev) +{ + struct phytium_dmufreq *priv = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + devfreq_remove_device(priv->devfreq); + + dev_pm_opp_remove_all_dynamic(dev); + + return 0; +} + +#ifdef CONFIG_ACPI +static const struct acpi_device_id phytium_dmufreq_acpi_ids[] = { + {"PHYT0063"}, + {}, +}; + +MODULE_DEVICE_TABLE(acpi, phytium_dmufreq_acpi_ids); +#else +#define phytium_dmu_acpi_ids NULL +#endif + +static struct platform_driver phytium_dmufreq_driver = { + .probe = phytium_dmufreq_probe, + .remove = phytium_dmufreq_remove, + .driver = { + .name = "phytium_dmufreq", + .acpi_match_table = ACPI_PTR(phytium_dmufreq_acpi_ids), + .suppress_bind_attrs = true, + }, +}; +module_platform_driver(phytium_dmufreq_driver); + +MODULE_DESCRIPTION("Phytium DDR Memory Unit frequency driver"); +MODULE_AUTHOR("Li Jiayi "); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DMUFREQ_DRIVER_VERSION); From 9b555f1e256ecb40301af7ac3add152708d8bd23 Mon Sep 17 00:00:00 2001 From: Ma Mingrui Date: Tue, 12 May 2026 11:52:38 +0800 Subject: [PATCH 19/38] devfreq: Phytium: Bugfix dmu/noc driver memory leak issue The patch fixed dmu/noc devfreq driver some memory leak problem. Signed-off-by: Li Mingzhe Signed-off-by: Wang Yinfeng Signed-off-by: Ma Mingrui --- drivers/devfreq/phytium_dmu.c | 9 ++++++++- drivers/devfreq/phytium_noc.c | 12 +++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/devfreq/phytium_dmu.c b/drivers/devfreq/phytium_dmu.c index cf5898034baaf..171805f3564f4 100644 --- a/drivers/devfreq/phytium_dmu.c +++ b/drivers/devfreq/phytium_dmu.c @@ -325,6 +325,7 @@ static int phytium_dmufreq_probe(struct platform_device *pdev) err: dev_pm_opp_of_remove_table(dev); + kfree(priv); return ret; } @@ -333,10 +334,16 @@ static int phytium_dmufreq_remove(struct platform_device *pdev) struct phytium_dmufreq *priv = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; - devfreq_remove_device(priv->devfreq); + + if (!priv->devfreq) + return 0; + flush_work(&priv->work); + del_timer_sync(&priv->sampling); dev_pm_opp_remove_all_dynamic(dev); + kfree(priv); + return 0; } diff --git a/drivers/devfreq/phytium_noc.c b/drivers/devfreq/phytium_noc.c index 39eb12cd0692a..43b16b33b7632 100644 --- a/drivers/devfreq/phytium_noc.c +++ b/drivers/devfreq/phytium_noc.c @@ -117,7 +117,7 @@ static int phytium_noc_set_freq(struct device *dev, unsigned long freq) dev_err(dev, "Failed to set the freq to %lu\n", freq); return -EIO; } - dev_info(dev, "set target_freq = %lu khz\n", freq); + dev_dbg(dev, "set target_freq = %lu khz\n", freq); return 0; } @@ -290,7 +290,7 @@ static int phytium_noc_get_dev_status(struct device *dev, stat->current_frequency = priv->rate; phytium_nocfreq_restart_handshark_counters(priv); - dev_info(dev, "Using %lu/%lu (%lu%%) at %lu KHz\n", + dev_dbg(dev, "Using %lu/%lu (%lu%%) at %lu KHz\n", stat->busy_time, stat->total_time, DIV_ROUND_CLOSEST(stat->busy_time * 100, stat->total_time), stat->current_frequency); @@ -309,6 +309,8 @@ static int phytium_nocfreq_probe(struct platform_device *pdev) max_state = get_freq_count(dev); + dev->init_name = "nocfreq"; + priv = devm_kzalloc(dev, struct_size(priv, freq_table, max_state), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -372,6 +374,7 @@ static int phytium_nocfreq_probe(struct platform_device *pdev) err: dev_pm_opp_of_remove_table(dev); + kfree(priv); return ret; } @@ -398,9 +401,12 @@ static int phytium_nocfreq_remove(struct platform_device *pdev) iounmap(priv->reg_noc); - devfreq_remove_device(priv->devfreq); + if (!priv->devfreq) + return 0; + dev_pm_opp_remove_all_dynamic(dev); + kfree(priv); return 0; } From b3b6f8df85de67db989a6767e39e5f478469de5d Mon Sep 17 00:00:00 2001 From: Ma Mingrui Date: Tue, 12 May 2026 11:54:34 +0800 Subject: [PATCH 20/38] devfreq: Phytium: Obtain the base address from the ACPI table The patch retrieves the base address from the ACPI table instead of being directly exposed inside the driver. Signed-off-by: Li Mingzhe Signed-off-by: Wang Yinfeng Signed-off-by: Ma Mingrui --- drivers/devfreq/phytium_noc.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/devfreq/phytium_noc.c b/drivers/devfreq/phytium_noc.c index 43b16b33b7632..6679ac3cb8d0d 100644 --- a/drivers/devfreq/phytium_noc.c +++ b/drivers/devfreq/phytium_noc.c @@ -15,7 +15,6 @@ #include #include -#define MIX_AMINI6 0x26eab800 #define MINI_SIZE 0x400 #define CNT_ENABLE 0x000 #define WORK_STATE 0X004 @@ -306,6 +305,7 @@ static int phytium_nocfreq_probe(struct platform_device *pdev) const char *gov = DEVFREQ_GOV_SIMPLE_ONDEMAND; int i, ret; unsigned int max_state; + struct resource *mem; max_state = get_freq_count(dev); @@ -318,10 +318,16 @@ static int phytium_nocfreq_probe(struct platform_device *pdev) mutex_init(&priv->lock); platform_set_drvdata(pdev, priv); - priv->reg_noc = ioremap(MIX_AMINI6, 1028*4); + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "no mem resource"); + return -EINVAL; + } + + priv->reg_noc = devm_ioremap_resource(&pdev->dev, mem); if (!priv->reg_noc) { - dev_err(dev, "failed to ioremap reg_noc\n"); - return -EIO; + dev_err(dev, "NOC region map failed\n"); + return PTR_ERR(priv->reg_noc); } ret = phytium_noc_get_freq_info(dev, DEVICE_TYPE); From 8f8e1f31925009267dd60e402b01708d20c0590d Mon Sep 17 00:00:00 2001 From: Ma Mingrui Date: Tue, 12 May 2026 14:04:33 +0800 Subject: [PATCH 21/38] devfreq: Phytium: Update some new functions for DMU driver This patch modifies and adds the following functions: 1). On account of DMU and DDR PMU drivers operate PMU registers at the same time, which will result in conflict. So the register operation of se in dmufreq is transferred to the upper driver. 2). The notification chain of dmufreq to DDR PMU is added in order to suspend dmufreq's register action and maintain the rate at the current frequency when the PMU driver is loaded. 3). Add suspend and resume features. Signed-off-by: Li Mingzhe Signed-off-by: Wang Yinfeng Signed-off-by: Ma Mingrui --- drivers/devfreq/phytium_dmu.c | 332 +++++++++++++++++++++++++++++++--- 1 file changed, 308 insertions(+), 24 deletions(-) diff --git a/drivers/devfreq/phytium_dmu.c b/drivers/devfreq/phytium_dmu.c index 171805f3564f4..5bfdd7f67e574 100644 --- a/drivers/devfreq/phytium_dmu.c +++ b/drivers/devfreq/phytium_dmu.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #define DEBUG @@ -25,6 +27,20 @@ #define UPDATE_INTERVAL_MS 10 +#define DMU_PMU_STRIDE 0x80000 + +#define AXI_MONITOR2_L 0x084 +#define AXI_MONITOR3_L 0x08c +#define AXI_MONITOR_EN 0X01c +#define TIMER_START 0X000 +#define TIMER_STOP 0X004 +#define CLEAR_EVENT 0X008 + +#define MCU_STRIDE 0x00080000 +/* PMU notifier event */ +#define DDR_PMU_NOTICE_START 0x0 +#define DDR_PMU_NOTICE_STOP 0x1 + #define DMUFREQ_DRIVER_VERSION "1.0.0" struct phytium_dmufreq { @@ -36,14 +52,72 @@ struct phytium_dmufreq { unsigned long rate, target_rate; unsigned long bandwidth; + int max_count; + int cnt; + + void __iomem **base; + + unsigned long *read_bw; + unsigned long *write_bw; struct timer_list sampling; struct work_struct work; + struct notifier_block nb; + + /*dmu to pmu operation status identification 0: not operable, 1: operable*/ + bool pmu_active; + + unsigned long last_bust_time; + unsigned int freq_count; unsigned long freq_table[]; }; +struct acpi_result { + int status; + unsigned long long value; +}; + +static inline void dmu_write32(struct phytium_dmufreq *priv, int dmu, + unsigned long offest, unsigned long value) +{ + writel_relaxed(value, priv->base[dmu] + offest); +} + +static inline unsigned long dmu_read32(struct phytium_dmufreq *priv, int dmu, + unsigned long offest) +{ + return readl_relaxed(priv->base[dmu] + offest); +} + +#if IS_ENABLED(CONFIG_PHYT_DMU_PMU_PD2408) +BLOCKING_NOTIFIER_HEAD(dmu_pmu_notifier_chain); +EXPORT_SYMBOL(dmu_pmu_notifier_chain); + +static int dmu_pmu_notifier_call(struct notifier_block *nb, unsigned long event, void *data) +{ + struct phytium_dmufreq *priv = container_of(nb, struct phytium_dmufreq, nb); + struct device *dev = priv->dev; + + switch (event) { + case DDR_PMU_NOTICE_START: + priv->pmu_active = false; + dev_dbg(dev, "DDR PMU START: Stopping monitoring\n"); + break; + case DDR_PMU_NOTICE_STOP: + priv->cnt = 0; + priv->pmu_active = true; + dev_dbg(dev, "DDR PMU STOP: Resuming monitoring\n"); + break; + default: + break; + } + + return NOTIFY_OK; +} +#endif + static ktime_t stop; static int phytium_dmu_set_freq(struct device *dev, unsigned long freq) @@ -119,24 +193,104 @@ static int phytium_dmu_get_cur_freq(struct device *dev, unsigned long *freq) return 0; } -static int phytium_read_perf_counter(struct device *dev) +struct acpi_result phytium_current_enabled_channels(struct device *dev) { - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *package, *elements; acpi_handle handle = ACPI_HANDLE(dev); acpi_status status; + unsigned long long enabled_channels; + struct acpi_result result; - status = acpi_evaluate_object(handle, "PDMU", NULL, &buffer); + status = acpi_evaluate_integer(handle, "CHAN", NULL, &enabled_channels); if (ACPI_FAILURE(status)) { - dev_err(dev, "No PGCL method\n"); - return -EIO; + dev_err(dev, "Failed to evaluate CHAN method: ACPI status 0x%x\n", status); + result.status = -EIO; + result.value = 0; + return result; } + dev_dbg(dev, "enabled_channels = %lld\n", enabled_channels); + result.status = 0; + result.value = enabled_channels; + return result; +} - package = buffer.pointer; +struct acpi_result phytium_controller_bit_width(struct device *dev) +{ + acpi_handle handle = ACPI_HANDLE(dev); + acpi_status status; + unsigned long long single_bit_width; + struct acpi_result result; - elements = package->package.elements; + status = acpi_evaluate_integer(handle, "BITW", NULL, &single_bit_width); + if (ACPI_FAILURE(status)) { + dev_err(dev, "Failed to evaluate BITW method: ACPI status 0x%x\n", status); + result.status = -EIO; + result.value = 0; + return result; + } + dev_dbg(dev, "single_bit_width = %lld(MB/s)\n", single_bit_width); + result.status = 0; + result.value = single_bit_width; + return result; +} - return elements[0].integer.value + elements[1].integer.value; +struct acpi_result phytium_dmufreq_state(struct device *dev) +{ + struct acpi_result result; + acpi_handle handle = ACPI_HANDLE(dev); + acpi_status status; + unsigned long long dmufreq_state; + + status = acpi_evaluate_integer(handle, "STAT", NULL, &dmufreq_state); + if (ACPI_FAILURE(status)) { + dev_err(dev, "Failed to evaluate STAT method: ACPI status 0x%x\n", status); + result.status = -EIO; + result.value = 0; + return result; + } + dev_dbg(dev, "dmufreq_state = %lld\n", dmufreq_state); + result.status = 0; + result.value = dmufreq_state; + return result; +} + +struct acpi_result phytium_read_threshold_value(struct device *dev) +{ + acpi_handle handle = ACPI_HANDLE(dev); + acpi_status status; + unsigned long long single_threshold_value; + struct acpi_result result; + + status = acpi_evaluate_integer(handle, "BAND", NULL, &single_threshold_value); + if (ACPI_FAILURE(status)) { + dev_err(dev, "Failed to evaluate BAND method: ACPI status 0x%x\n", status); + result.status = -EIO; + result.value = 0; + return result; + } + dev_dbg(dev, "single_threshold_value = %llu\n", single_threshold_value); + result.status = 0; + result.value = single_threshold_value; + return result; +} + +static u64 phytium_dmufreq_get_real_bw(struct phytium_dmufreq *priv) +{ + unsigned long peak_bw = 0; + unsigned long sum_peak_bw = 0; + + for (int i = 0; i < priv->max_count; i++) { + priv->read_bw[i] = dmu_read32(priv, i, AXI_MONITOR2_L); + priv->write_bw[i] = dmu_read32(priv, i, AXI_MONITOR3_L); + + /*clear the counter(only pmu_reg active)*/ + dmu_write32(priv, i, CLEAR_EVENT, 0x1); + dmu_write32(priv, i, TIMER_START, 0x1); + sum_peak_bw = priv->read_bw[i] + priv->write_bw[i]; + if (sum_peak_bw > peak_bw) + peak_bw = sum_peak_bw; + } + dev_dbg(priv->dev, "peak_bw = %lu\n", peak_bw); + return peak_bw; } static void sampling_timer_callback(struct timer_list *t) @@ -149,18 +303,28 @@ static void sampling_timer_callback(struct timer_list *t) static void sampling_work_handle(struct work_struct *work) { struct phytium_dmufreq *priv = container_of(work, struct phytium_dmufreq, work); - struct device *dev = priv->dev; static unsigned long load_counter; static int count; unsigned long current_load; - current_load = phytium_read_perf_counter(dev); - - load_counter += current_load; - count += 1; - + /*if the pmu_reg is not active, return the last busy time(pmu_reg not work)*/ + if (!priv->pmu_active) { + priv->bandwidth = priv->last_bust_time; + mod_timer(&priv->sampling, jiffies + msecs_to_jiffies(UPDATE_INTERVAL_MS)); + return; + } + if (priv->cnt > 0) { + for (int i = 0; i < priv->max_count ; i++) { + dmu_write32(priv, i, AXI_MONITOR_EN, 0x101); + dmu_write32(priv, i, TIMER_STOP, 0x1); + } + current_load = phytium_dmufreq_get_real_bw(priv); + load_counter += current_load; + count += 1; + } + priv->cnt = 1; if (ktime_after(ktime_get(), stop)) { - priv->bandwidth = load_counter / count; + priv->bandwidth = div64_u64(load_counter, count); load_counter = 0; count = 0; stop = ktime_add_ms(ktime_get(), priv->profile.polling_ms); @@ -173,14 +337,24 @@ static int phytium_dmu_get_dev_status(struct device *dev, struct devfreq_dev_status *stat) { struct phytium_dmufreq *priv = dev_get_drvdata(dev); + struct acpi_result result; + unsigned long long single_threshold_value; + + result = phytium_read_threshold_value(dev); + if (result.status) { + dev_err(dev, "Failed to get threshold value\n"); + return -EINVAL; + } + single_threshold_value = result.value; + single_threshold_value = (single_threshold_value * 1024 * 1024) / 100; stat->busy_time = priv->bandwidth; - stat->total_time = (500000 * priv->rate) / priv->freq_table[0]; - dev_dbg(dev, "busy_time = %lu, total_time = %lu\n", - stat->busy_time, stat->total_time); + stat->total_time = (single_threshold_value * priv->rate) / priv->freq_table[0]; + priv->last_bust_time = priv->bandwidth; + dev_dbg(dev, "busy_time = %lu, total_time = %lu,single_threshold_value = %llu\n", + stat->busy_time, stat->total_time, single_threshold_value); stat->current_frequency = priv->rate; - return 0; } @@ -260,6 +434,49 @@ static int get_freq_count(struct device *dev) return freq_count; } +static __maybe_unused int phytium_dmufreq_suspend(struct device *dev) +{ + struct phytium_dmufreq *priv = dev_get_drvdata(dev); + int ret = 0; + + dev_dbg(dev, "DMU is being suspended\n"); + + del_timer_sync(&priv->sampling); + flush_work(&priv->work); + + ret = devfreq_suspend_device(priv->devfreq); + if (ret < 0) { + dev_err(dev, "failed to suspend the devfreq devices\n"); + return ret; + } + + return 0; +} + +static __maybe_unused int phytium_dmufreq_resume(struct device *dev) +{ + struct phytium_dmufreq *priv = dev_get_drvdata(dev); + int ret = 0; + + dev_dbg(dev, "DMU is being resumed\n"); + + ret = devfreq_resume_device(priv->devfreq); + if (ret < 0) { + dev_err(dev, "failed to resume the devfreq devices\n"); + return ret; + } + + if (!timer_pending(&priv->sampling)) + mod_timer(&priv->sampling, jiffies + msecs_to_jiffies(UPDATE_INTERVAL_MS)); + else + dev_warn(dev, "Sampling timer already active ,skipping reinitialization\n"); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(phytium_dmufreq_pm, phytium_dmufreq_suspend, + phytium_dmufreq_resume); + static int phytium_dmufreq_probe(struct platform_device *pdev) { struct phytium_dmufreq *priv; @@ -267,25 +484,67 @@ static int phytium_dmufreq_probe(struct platform_device *pdev) const char *gov = DEVFREQ_GOV_SIMPLE_ONDEMAND; int i, ret; unsigned int max_state = get_freq_count(dev); + struct acpi_result result; + struct resource *res; + + result = phytium_dmufreq_state(dev); + if (result.value == 0) { + dev_err(dev, "DMUFREQ is not enabled\n"); + return -ENODEV; + } + + priv = devm_kzalloc(dev, struct_size(priv, freq_table, max_state), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + result = phytium_current_enabled_channels(dev); + if (result.status) { + dev_err(dev, "Failed to get enabled channels\n"); + return -EINVAL; + } + + priv->max_count = result.value; if (max_state <= 0) return max_state; dev->init_name = "dmufreq"; - priv = kzalloc(sizeof(struct phytium_dmufreq) + - max_state * sizeof(unsigned long), GFP_KERNEL); - if (!priv) + priv->base = devm_kcalloc(dev, priv->max_count, sizeof(void __iomem *), GFP_KERNEL); + priv->read_bw = devm_kcalloc(dev, priv->max_count, sizeof(unsigned long), GFP_KERNEL); + priv->write_bw = devm_kcalloc(dev, priv->max_count, sizeof(unsigned long), GFP_KERNEL); + if (!priv->base || !priv->read_bw || !priv->write_bw) { + dev_err(dev, "failed to allocate memory\n"); return -ENOMEM; - + } platform_set_drvdata(pdev, priv); +#if IS_ENABLED(CONFIG_PHYT_DMU_PMU_PD2408) + /* Register the notifier */ + priv->nb.notifier_call = dmu_pmu_notifier_call; + ret = blocking_notifier_chain_register(&dmu_pmu_notifier_chain, &priv->nb); + if (ret) { + dev_err(dev, "Failed to register notifier\n"); + return ret; + } +#endif + + /* Get the base address of the DMU PMU */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + for (int i = 0; i < priv->max_count; i++) { + priv->base[i] = ioremap(res->start + i * DMU_PMU_STRIDE, resource_size(res)); + if (!priv->base[i]) + return -ENOMEM; + } + ret = phytium_dmu_get_freq_info(dev); if (ret) { dev_err(dev, "failed to get ddr frequency info\n"); return -EIO; } + priv->pmu_active = true; + priv->cnt = 1; priv->profile.initial_freq = priv->freq_table[0]; priv->profile.polling_ms = 100; priv->profile.timer = DEVFREQ_TIMER_DELAYED; @@ -314,6 +573,15 @@ static int phytium_dmufreq_probe(struct platform_device *pdev) goto err; } + /*Enable PMU*/ + if (priv->pmu_active) { + for (int i = 0; i < priv->max_count; i++) { + dmu_write32(priv, i, AXI_MONITOR_EN, 0x101); + dmu_write32(priv, i, CLEAR_EVENT, 0x1); + dmu_write32(priv, i, TIMER_START, 0x1); + } + } + INIT_WORK(&priv->work, sampling_work_handle); timer_setup(&priv->sampling, sampling_timer_callback, 0); stop = ktime_add_ms(ktime_get(), priv->profile.polling_ms); @@ -334,6 +602,15 @@ static int phytium_dmufreq_remove(struct platform_device *pdev) struct phytium_dmufreq *priv = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; + for (int i = 0; i < priv->max_count; i++) { + dmu_write32(priv, i, AXI_MONITOR_EN, 0x0); + dmu_write32(priv, i, TIMER_STOP, 0x1); + } + +#if IS_ENABLED(CONFIG_PHYT_DMU_PMU_PD2408) + /*Unregister the notifier*/ + blocking_notifier_chain_unregister(&dmu_pmu_notifier_chain, &priv->nb); +#endif if (!priv->devfreq) return 0; @@ -358,11 +635,18 @@ MODULE_DEVICE_TABLE(acpi, phytium_dmufreq_acpi_ids); #define phytium_dmu_acpi_ids NULL #endif +#if IS_ENABLED(CONFIG_PHYT_DMU_PMU_PD2408) +struct notifier_block nb = { + .notifier_call = dmu_pmu_notifier_call, +}; +#endif + static struct platform_driver phytium_dmufreq_driver = { .probe = phytium_dmufreq_probe, .remove = phytium_dmufreq_remove, .driver = { .name = "phytium_dmufreq", + .pm = &phytium_dmufreq_pm, .acpi_match_table = ACPI_PTR(phytium_dmufreq_acpi_ids), .suppress_bind_attrs = true, }, From 75fe17b46a893118c34624c19b903e75a0eeae3d Mon Sep 17 00:00:00 2001 From: Ma Mingrui Date: Tue, 12 May 2026 14:07:15 +0800 Subject: [PATCH 22/38] devfreq: Phytium: Modify the default strategy for DMU freq driver Change the default strategy for the DMU freq driver from simple demand to the performance mode. Signed-off-by: Li Mingzhe Signed-off-by: Wang Yinfeng Signed-off-by: Ma Mingrui --- drivers/devfreq/phytium_dmu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/devfreq/phytium_dmu.c b/drivers/devfreq/phytium_dmu.c index 5bfdd7f67e574..d57e730ab8d1c 100644 --- a/drivers/devfreq/phytium_dmu.c +++ b/drivers/devfreq/phytium_dmu.c @@ -481,7 +481,7 @@ static int phytium_dmufreq_probe(struct platform_device *pdev) { struct phytium_dmufreq *priv; struct device *dev = &pdev->dev; - const char *gov = DEVFREQ_GOV_SIMPLE_ONDEMAND; + const char *gov = DEVFREQ_GOV_PERFORMANCE; int i, ret; unsigned int max_state = get_freq_count(dev); struct acpi_result result; @@ -566,7 +566,7 @@ static int phytium_dmufreq_probe(struct platform_device *pdev) } priv->devfreq = devm_devfreq_add_device(dev, &priv->profile, - gov, &priv->ondemand_data); + gov, NULL); if (IS_ERR(priv->devfreq)) { ret = PTR_ERR(priv->devfreq); dev_err(dev, "failed to add devfreq device: %d\n", ret); From ebfa1008041a08877871af8f13a0c1029d90bc8d Mon Sep 17 00:00:00 2001 From: Ma Mingrui Date: Tue, 12 May 2026 14:09:33 +0800 Subject: [PATCH 23/38] devfreq: Phytium: Modify some issues related to the dmu driver Firstly, replace ioremap with devm_ioremap. The advantage of this approach is that it can be automatically managed during the unloading stage, eliminating the need for manual resource cleanup, thus preventing resource leakage. Secondly, resolve the repeated printing issues. Signed-off-by: Li Mingzhe Signed-off-by: Wang Yinfeng Signed-off-by: Ma Mingrui --- drivers/devfreq/phytium_dmu.c | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/drivers/devfreq/phytium_dmu.c b/drivers/devfreq/phytium_dmu.c index d57e730ab8d1c..5d103aab00498 100644 --- a/drivers/devfreq/phytium_dmu.c +++ b/drivers/devfreq/phytium_dmu.c @@ -41,7 +41,7 @@ #define DDR_PMU_NOTICE_START 0x0 #define DDR_PMU_NOTICE_STOP 0x1 -#define DMUFREQ_DRIVER_VERSION "1.0.0" +#define DMUFREQ_DRIVER_VERSION "1.0.1" struct phytium_dmufreq { struct device *dev; @@ -91,7 +91,6 @@ static inline unsigned long dmu_read32(struct phytium_dmufreq *priv, int dmu, return readl_relaxed(priv->base[dmu] + offest); } -#if IS_ENABLED(CONFIG_PHYT_DMU_PMU_PD2408) BLOCKING_NOTIFIER_HEAD(dmu_pmu_notifier_chain); EXPORT_SYMBOL(dmu_pmu_notifier_chain); @@ -116,7 +115,6 @@ static int dmu_pmu_notifier_call(struct notifier_block *nb, unsigned long event, return NOTIFY_OK; } -#endif static ktime_t stop; @@ -262,7 +260,7 @@ struct acpi_result phytium_read_threshold_value(struct device *dev) status = acpi_evaluate_integer(handle, "BAND", NULL, &single_threshold_value); if (ACPI_FAILURE(status)) { - dev_err(dev, "Failed to evaluate BAND method: ACPI status 0x%x\n", status); + WARN_ONCE(1, "Failed to evaluate BAND method: ACPI status 0x%x\n", status); result.status = -EIO; result.value = 0; return result; @@ -342,7 +340,7 @@ static int phytium_dmu_get_dev_status(struct device *dev, result = phytium_read_threshold_value(dev); if (result.status) { - dev_err(dev, "Failed to get threshold value\n"); + WARN_ONCE(1, "Failed to get threshold value\n"); return -EINVAL; } single_threshold_value = result.value; @@ -519,7 +517,6 @@ static int phytium_dmufreq_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, priv); -#if IS_ENABLED(CONFIG_PHYT_DMU_PMU_PD2408) /* Register the notifier */ priv->nb.notifier_call = dmu_pmu_notifier_call; ret = blocking_notifier_chain_register(&dmu_pmu_notifier_chain, &priv->nb); @@ -527,14 +524,17 @@ static int phytium_dmufreq_probe(struct platform_device *pdev) dev_err(dev, "Failed to register notifier\n"); return ret; } -#endif /* Get the base address of the DMU PMU */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - for (int i = 0; i < priv->max_count; i++) { - priv->base[i] = ioremap(res->start + i * DMU_PMU_STRIDE, resource_size(res)); - if (!priv->base[i]) - return -ENOMEM; + for (i = 0; i < priv->max_count; i++) { + resource_size_t offset = res->start + i * DMU_PMU_STRIDE; + + priv->base[i] = devm_ioremap(&pdev->dev, offset, resource_size(res)); + if (IS_ERR(priv->base[i])) { + dev_err(dev, "Ioremap failed for dmu base resource\n"); + return PTR_ERR(priv->base); + } } ret = phytium_dmu_get_freq_info(dev); @@ -593,7 +593,6 @@ static int phytium_dmufreq_probe(struct platform_device *pdev) err: dev_pm_opp_of_remove_table(dev); - kfree(priv); return ret; } @@ -603,24 +602,19 @@ static int phytium_dmufreq_remove(struct platform_device *pdev) struct device *dev = &pdev->dev; for (int i = 0; i < priv->max_count; i++) { - dmu_write32(priv, i, AXI_MONITOR_EN, 0x0); dmu_write32(priv, i, TIMER_STOP, 0x1); + dmu_write32(priv, i, AXI_MONITOR_EN, 0x0); } -#if IS_ENABLED(CONFIG_PHYT_DMU_PMU_PD2408) /*Unregister the notifier*/ blocking_notifier_chain_unregister(&dmu_pmu_notifier_chain, &priv->nb); -#endif if (!priv->devfreq) return 0; - flush_work(&priv->work); del_timer_sync(&priv->sampling); - + cancel_work_sync(&priv->work); dev_pm_opp_remove_all_dynamic(dev); - kfree(priv); - return 0; } @@ -635,11 +629,9 @@ MODULE_DEVICE_TABLE(acpi, phytium_dmufreq_acpi_ids); #define phytium_dmu_acpi_ids NULL #endif -#if IS_ENABLED(CONFIG_PHYT_DMU_PMU_PD2408) struct notifier_block nb = { .notifier_call = dmu_pmu_notifier_call, }; -#endif static struct platform_driver phytium_dmufreq_driver = { .probe = phytium_dmufreq_probe, @@ -655,5 +647,6 @@ module_platform_driver(phytium_dmufreq_driver); MODULE_DESCRIPTION("Phytium DDR Memory Unit frequency driver"); MODULE_AUTHOR("Li Jiayi "); +MODULE_AUTHOR("Li Mingzhe "); MODULE_LICENSE("GPL"); MODULE_VERSION(DMUFREQ_DRIVER_VERSION); From 5a42dc2ba3e163da7b55cd3e2e51da0affc96adb Mon Sep 17 00:00:00 2001 From: Ma Mingrui Date: Tue, 12 May 2026 14:11:08 +0800 Subject: [PATCH 24/38] devfreq: Phytium: Delete the unnecessary release of resources Delete the unnecessary release of resources when using devm_kzalloc function to allocate memory. Signed-off-by: Li Mingzhe Signed-off-by: Wang Yinfeng Signed-off-by: Ma Mingrui --- drivers/devfreq/phytium_noc.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/devfreq/phytium_noc.c b/drivers/devfreq/phytium_noc.c index 6679ac3cb8d0d..f064ffc8fca90 100644 --- a/drivers/devfreq/phytium_noc.c +++ b/drivers/devfreq/phytium_noc.c @@ -26,7 +26,7 @@ #define DEBUG #define DEVICE_TYPE 7 -#define NOCFREQ_DRIVER_VERSION "1.0.0" +#define NOCFREQ_DRIVER_VERSION "1.0.1" struct phytium_nocfreq { struct device *dev; @@ -380,7 +380,6 @@ static int phytium_nocfreq_probe(struct platform_device *pdev) err: dev_pm_opp_of_remove_table(dev); - kfree(priv); return ret; } @@ -405,14 +404,12 @@ static int phytium_nocfreq_remove(struct platform_device *pdev) if (ret) dev_warn(dev, "failed to restore NOC frequency: %d\n", ret); - iounmap(priv->reg_noc); if (!priv->devfreq) return 0; dev_pm_opp_remove_all_dynamic(dev); - kfree(priv); return 0; } From c56898d70f6166366b2c1df46fbc826ef0444fe2 Mon Sep 17 00:00:00 2001 From: Ma Mingrui Date: Tue, 12 May 2026 14:12:48 +0800 Subject: [PATCH 25/38] devfreq: Phytium: Add power mangement interface for noc devfreq This patch adds power mangement interface, Specifically adds the phytium_nocfreq_suspend/phytium_nocfreq_resume functions so that the frequency can be restored upon waking up. Signed-off-by: Li Mingzhe Signed-off-by: Wang Yinfeng Signed-off-by: Ma Mingrui --- drivers/devfreq/phytium_noc.c | 74 ++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/drivers/devfreq/phytium_noc.c b/drivers/devfreq/phytium_noc.c index f064ffc8fca90..01b56398943ad 100644 --- a/drivers/devfreq/phytium_noc.c +++ b/drivers/devfreq/phytium_noc.c @@ -14,6 +14,7 @@ #include #include #include +#include #define MINI_SIZE 0x400 #define CNT_ENABLE 0x000 @@ -26,7 +27,7 @@ #define DEBUG #define DEVICE_TYPE 7 -#define NOCFREQ_DRIVER_VERSION "1.0.1" +#define NOCFREQ_DRIVER_VERSION "1.0.2" struct phytium_nocfreq { struct device *dev; @@ -38,7 +39,7 @@ struct phytium_nocfreq { void __iomem *reg_noc; struct mutex lock; - unsigned long rate, target_rate; + unsigned long rate, target_rate, suspend_freq; unsigned int freq_count; unsigned long freq_table[]; }; @@ -297,6 +298,74 @@ static int phytium_noc_get_dev_status(struct device *dev, return 0; } +static __maybe_unused int phytium_nocfreq_suspend(struct device *dev) +{ + struct phytium_nocfreq *priv = dev_get_drvdata(dev); + int ret = 0; + + dev_dbg(dev, "NOCfreq is being suspended\n"); + + ret = phytium_noc_get_cur_freq(dev, &priv->suspend_freq); + if (ret) + dev_warn(dev, "failed to get suspend freq\n"); + else + dev_info(dev, "saved suspend freq = %lu\n", priv->suspend_freq); + + ret = devfreq_suspend_device(priv->devfreq); + if (ret < 0) { + dev_err(dev, "failed to suspend the devfreq devices\n"); + return ret; + } + priv->devfreq->stop_polling = true; + + writel_relaxed(0x0, priv->reg_noc + CNT_ENABLE); + writel_relaxed(0x0, priv->reg_noc + MINI_SIZE*1+CNT_ENABLE); + writel_relaxed(0x0, priv->reg_noc + MINI_SIZE*2+CNT_ENABLE); + writel_relaxed(0x0, priv->reg_noc + MINI_SIZE*3+CNT_ENABLE); + + return ret; +} + +static __maybe_unused int phytium_nocfreq_resume(struct device *dev) +{ + struct phytium_nocfreq *priv = dev_get_drvdata(dev); + int ret = 0; + + dev_dbg(dev, "NOCfreq is being resumed\n"); + + ret = devfreq_resume_device(priv->devfreq); + if (ret < 0) { + dev_err(dev, "failed to resume the devfreq devices\n"); + return ret; + } + if (!delayed_work_pending(&priv->devfreq->work) && priv->devfreq->profile->polling_ms) { + dev_info(dev, "Polling work not pending, manually restarting polling\n"); + priv->devfreq->stop_polling = true; + } + writel_relaxed(0x02, priv->reg_noc + WORK_STATE); + writel_relaxed(0x02, priv->reg_noc + MINI_SIZE*1 + WORK_STATE); + writel_relaxed(0x02, priv->reg_noc + MINI_SIZE*2 + WORK_STATE); + writel_relaxed(0x02, priv->reg_noc + MINI_SIZE*3 + WORK_STATE); + + writel_relaxed(0x3f, priv->reg_noc + CNT_ENABLE); + writel_relaxed(0x3f, priv->reg_noc + MINI_SIZE*1+CNT_ENABLE); + writel_relaxed(0x3f, priv->reg_noc + MINI_SIZE*2+CNT_ENABLE); + writel_relaxed(0x3f, priv->reg_noc + MINI_SIZE*3+CNT_ENABLE); + + if (priv->suspend_freq) { + ret = phytium_noc_set_freq(dev, priv->suspend_freq); + if (ret < 0) + dev_warn(dev, "failed to restore suspend freq %lu\n", priv->suspend_freq); + else { + dev_info(dev, "restored suspend freq = %lu\n", priv->suspend_freq); + priv->rate = priv->suspend_freq; + } + } + return ret; +} + +static SIMPLE_DEV_PM_OPS(phytium_nocfreq_pm, phytium_nocfreq_suspend, + phytium_nocfreq_resume); static int phytium_nocfreq_probe(struct platform_device *pdev) { @@ -425,6 +494,7 @@ static struct platform_driver phytium_nocfreq_driver = { .remove = phytium_nocfreq_remove, .driver = { .name = "phytium_nocfreq", + .pm = &phytium_nocfreq_pm, .acpi_match_table = ACPI_PTR(phytium_noc_acpi_ids), .suppress_bind_attrs = true, }, From 5f451df7b367340088146191b1d7a5d29df9a81f Mon Sep 17 00:00:00 2001 From: Ma Mingrui Date: Tue, 12 May 2026 14:14:41 +0800 Subject: [PATCH 26/38] devfreq: Phytium: Modify the sampling logic of DMU driver Optimize the timer logic for sampling, with the aim of reducing the frequent calls made by processes within the system. It is very helpfull to reduce power consumption. Signed-off-by: Li Mingzhe Signed-off-by: Wang Yinfeng Signed-off-by: Ma Mingrui --- drivers/devfreq/phytium_dmu.c | 90 ++++++++++------------------------- 1 file changed, 25 insertions(+), 65 deletions(-) diff --git a/drivers/devfreq/phytium_dmu.c b/drivers/devfreq/phytium_dmu.c index 5d103aab00498..960c5ee5f30ea 100644 --- a/drivers/devfreq/phytium_dmu.c +++ b/drivers/devfreq/phytium_dmu.c @@ -25,8 +25,6 @@ #define DEVICE_TYPE 9 //DMU ID -#define UPDATE_INTERVAL_MS 10 - #define DMU_PMU_STRIDE 0x80000 #define AXI_MONITOR2_L 0x084 @@ -41,7 +39,7 @@ #define DDR_PMU_NOTICE_START 0x0 #define DDR_PMU_NOTICE_STOP 0x1 -#define DMUFREQ_DRIVER_VERSION "1.0.1" +#define DMUFREQ_DRIVER_VERSION "1.0.2" struct phytium_dmufreq { struct device *dev; @@ -52,6 +50,7 @@ struct phytium_dmufreq { unsigned long rate, target_rate; unsigned long bandwidth; + unsigned long single_threshold_value; int max_count; int cnt; @@ -60,9 +59,6 @@ struct phytium_dmufreq { unsigned long *read_bw; unsigned long *write_bw; - struct timer_list sampling; - struct work_struct work; - struct notifier_block nb; /*dmu to pmu operation status identification 0: not operable, 1: operable*/ @@ -116,7 +112,6 @@ static int dmu_pmu_notifier_call(struct notifier_block *nb, unsigned long event, return NOTIFY_OK; } -static ktime_t stop; static int phytium_dmu_set_freq(struct device *dev, unsigned long freq) { @@ -275,8 +270,9 @@ static u64 phytium_dmufreq_get_real_bw(struct phytium_dmufreq *priv) { unsigned long peak_bw = 0; unsigned long sum_peak_bw = 0; + int i; - for (int i = 0; i < priv->max_count; i++) { + for (i = 0; i < priv->max_count; i++) { priv->read_bw[i] = dmu_read32(priv, i, AXI_MONITOR2_L); priv->write_bw[i] = dmu_read32(priv, i, AXI_MONITOR3_L); @@ -291,66 +287,36 @@ static u64 phytium_dmufreq_get_real_bw(struct phytium_dmufreq *priv) return peak_bw; } -static void sampling_timer_callback(struct timer_list *t) -{ - struct phytium_dmufreq *priv = from_timer(priv, t, sampling); - - schedule_work(&priv->work); -} - -static void sampling_work_handle(struct work_struct *work) +static void polling_handle(struct phytium_dmufreq *priv) { - struct phytium_dmufreq *priv = container_of(work, struct phytium_dmufreq, work); - static unsigned long load_counter; - static int count; - unsigned long current_load; + int i; /*if the pmu_reg is not active, return the last busy time(pmu_reg not work)*/ if (!priv->pmu_active) { priv->bandwidth = priv->last_bust_time; - mod_timer(&priv->sampling, jiffies + msecs_to_jiffies(UPDATE_INTERVAL_MS)); return; } if (priv->cnt > 0) { - for (int i = 0; i < priv->max_count ; i++) { + for (i = 0; i < priv->max_count ; i++) { dmu_write32(priv, i, AXI_MONITOR_EN, 0x101); dmu_write32(priv, i, TIMER_STOP, 0x1); } - current_load = phytium_dmufreq_get_real_bw(priv); - load_counter += current_load; - count += 1; + priv->bandwidth = phytium_dmufreq_get_real_bw(priv); } priv->cnt = 1; - if (ktime_after(ktime_get(), stop)) { - priv->bandwidth = div64_u64(load_counter, count); - load_counter = 0; - count = 0; - stop = ktime_add_ms(ktime_get(), priv->profile.polling_ms); - mod_timer(&priv->sampling, jiffies + msecs_to_jiffies(UPDATE_INTERVAL_MS)); - } else - mod_timer(&priv->sampling, jiffies + msecs_to_jiffies(UPDATE_INTERVAL_MS)); } static int phytium_dmu_get_dev_status(struct device *dev, struct devfreq_dev_status *stat) { struct phytium_dmufreq *priv = dev_get_drvdata(dev); - struct acpi_result result; - unsigned long long single_threshold_value; - result = phytium_read_threshold_value(dev); - if (result.status) { - WARN_ONCE(1, "Failed to get threshold value\n"); - return -EINVAL; - } - single_threshold_value = result.value; - single_threshold_value = (single_threshold_value * 1024 * 1024) / 100; + polling_handle(priv); + priv->last_bust_time = stat->busy_time = priv->bandwidth; + stat->total_time = (priv->single_threshold_value * priv->rate) / priv->freq_table[0]; - stat->busy_time = priv->bandwidth; - stat->total_time = (single_threshold_value * priv->rate) / priv->freq_table[0]; - priv->last_bust_time = priv->bandwidth; dev_dbg(dev, "busy_time = %lu, total_time = %lu,single_threshold_value = %llu\n", - stat->busy_time, stat->total_time, single_threshold_value); + stat->busy_time, stat->total_time, priv->single_threshold_value); stat->current_frequency = priv->rate; return 0; @@ -439,9 +405,6 @@ static __maybe_unused int phytium_dmufreq_suspend(struct device *dev) dev_dbg(dev, "DMU is being suspended\n"); - del_timer_sync(&priv->sampling); - flush_work(&priv->work); - ret = devfreq_suspend_device(priv->devfreq); if (ret < 0) { dev_err(dev, "failed to suspend the devfreq devices\n"); @@ -464,11 +427,6 @@ static __maybe_unused int phytium_dmufreq_resume(struct device *dev) return ret; } - if (!timer_pending(&priv->sampling)) - mod_timer(&priv->sampling, jiffies + msecs_to_jiffies(UPDATE_INTERVAL_MS)); - else - dev_warn(dev, "Sampling timer already active ,skipping reinitialization\n"); - return 0; } @@ -485,6 +443,9 @@ static int phytium_dmufreq_probe(struct platform_device *pdev) struct acpi_result result; struct resource *res; + if (max_state <= 0) + return -EINVAL; + result = phytium_dmufreq_state(dev); if (result.value == 0) { dev_err(dev, "DMUFREQ is not enabled\n"); @@ -503,8 +464,13 @@ static int phytium_dmufreq_probe(struct platform_device *pdev) priv->max_count = result.value; - if (max_state <= 0) - return max_state; + result = phytium_read_threshold_value(dev); + if (result.status) { + dev_err(dev, "Failed to get threshold value\n"); + return -EINVAL; + } + priv->single_threshold_value = result.value; + priv->single_threshold_value = (priv->single_threshold_value * 1024 * 1024) / 10; dev->init_name = "dmufreq"; @@ -575,18 +541,13 @@ static int phytium_dmufreq_probe(struct platform_device *pdev) /*Enable PMU*/ if (priv->pmu_active) { - for (int i = 0; i < priv->max_count; i++) { + for (i = 0; i < priv->max_count; i++) { dmu_write32(priv, i, AXI_MONITOR_EN, 0x101); dmu_write32(priv, i, CLEAR_EVENT, 0x1); dmu_write32(priv, i, TIMER_START, 0x1); } } - INIT_WORK(&priv->work, sampling_work_handle); - timer_setup(&priv->sampling, sampling_timer_callback, 0); - stop = ktime_add_ms(ktime_get(), priv->profile.polling_ms); - mod_timer(&priv->sampling, jiffies + msecs_to_jiffies(UPDATE_INTERVAL_MS)); - priv->dev = dev; return 0; @@ -600,8 +561,9 @@ static int phytium_dmufreq_remove(struct platform_device *pdev) { struct phytium_dmufreq *priv = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; + int i; - for (int i = 0; i < priv->max_count; i++) { + for (i = 0; i < priv->max_count; i++) { dmu_write32(priv, i, TIMER_STOP, 0x1); dmu_write32(priv, i, AXI_MONITOR_EN, 0x0); } @@ -611,8 +573,6 @@ static int phytium_dmufreq_remove(struct platform_device *pdev) if (!priv->devfreq) return 0; - del_timer_sync(&priv->sampling); - cancel_work_sync(&priv->work); dev_pm_opp_remove_all_dynamic(dev); return 0; From 9684cdf25bb776cf743706cba1f9d770dd291f9d Mon Sep 17 00:00:00 2001 From: Ma Mingrui Date: Tue, 12 May 2026 14:39:46 +0800 Subject: [PATCH 27/38] devfreq: Phytium: Fix memory leak in ACPI evaluate helper of DMU uring driver probe, functions like get_freq_count() and phytium_noc_get_freq_info() call acpi_evaluate_object() with ACPI_ALLOCATE_BUFFER. This interface allocates memory via kmalloc to store the returned ACPI package, but the allocated buffer was never released after use. kmemleak reports unreferenced objects coming from acpi_ut_initialize_buffer() when probing the Phytium DMU freq drivers. Fix this by calling kfree(buffer.pointer) after the ACPI package has been parsed. Signed-off-by: Li Mingzhe Signed-off-by: Wang Yinfeng Signed-off-by: Ma Mingrui --- drivers/devfreq/phytium_dmu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/devfreq/phytium_dmu.c b/drivers/devfreq/phytium_dmu.c index 960c5ee5f30ea..995a0e6231332 100644 --- a/drivers/devfreq/phytium_dmu.c +++ b/drivers/devfreq/phytium_dmu.c @@ -359,7 +359,7 @@ static int phytium_dmu_get_freq_info(struct device *dev) priv->freq_table[i] = element->integer.value; dev_dbg(dev, "freq_table[%d] = %llu\n", i, element->integer.value); } - + kfree(buffer.pointer); return 0; } @@ -395,6 +395,7 @@ static int get_freq_count(struct device *dev) freq_count = element->integer.value; dev_dbg(dev, "freq_count = %d\n", freq_count); + kfree(buffer.pointer); return freq_count; } From 5b19e0ad3f25ce3ddf0a3f3dc4077cbab86b6148 Mon Sep 17 00:00:00 2001 From: Ma Mingrui Date: Tue, 12 May 2026 14:43:33 +0800 Subject: [PATCH 28/38] devfreq: Phytium: Fix memory leak in ACPI evaluate helper of NOC During driver probe, functions like get_freq_count() and phytium_noc_get_freq_info() call acpi_evaluate_object() with ACPI_ALLOCATE_BUFFER. This interface allocates memory via kmalloc to store the returned ACPI package, but the allocated buffer was never released after use. kmemleak reports unreferenced objects coming from acpi_ut_initialize_buffer() when probing the Phytium NOC freq drivers. Fix this by calling kfree(buffer.pointer) after the ACPI package has been parsed. Signed-off-by: Li Mingzhe Signed-off-by: Wang Yinfeng Signed-off-by: Ma Mingrui --- drivers/devfreq/phytium_noc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/devfreq/phytium_noc.c b/drivers/devfreq/phytium_noc.c index 01b56398943ad..6df68f31a0117 100644 --- a/drivers/devfreq/phytium_noc.c +++ b/drivers/devfreq/phytium_noc.c @@ -231,6 +231,7 @@ static int phytium_noc_get_freq_info(struct device *dev, u32 flags) dev_dbg(dev, "freq_table[%d] = %llu\n", i, element->integer.value); } + kfree(buffer.pointer); return 0; } @@ -270,6 +271,7 @@ static int get_freq_count(struct device *dev) freq_count = element->integer.value; dev_dbg(dev, "freq_count = %d\n", freq_count); + kfree(buffer.pointer); return freq_count; } From 9747ebd165a19bcc7ddba4ddc11d7f3a9eca90e8 Mon Sep 17 00:00:00 2001 From: Ma Mingrui Date: Tue, 12 May 2026 14:46:32 +0800 Subject: [PATCH 29/38] devfreq: Phytium:Add DMU DEVFREQ Support for PS260xxx SoCs Add DMU DEVFREQ driver Support for Phytium PS260xxx SoCs, and complatible with PD2408. Signed-off-by: Li Mingzhe Signed-off-by: Wang Yinfeng Signed-off-by: Ma Mingrui --- drivers/devfreq/Kconfig | 2 +- drivers/devfreq/phytium_dmu.c | 651 ++++++++++++++++++---------------- 2 files changed, 347 insertions(+), 306 deletions(-) diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index c90e8fa9d7335..20df7fb0c5b0a 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -161,7 +161,7 @@ config ARM_PHYTIUM_NOC_DEVFREQ config ARM_PHYTIUM_DMU_DEVFREQ tristate "ARM PHYTIUM DMU DEVFREQ Driver" - depends on ARCH_PHYTIUM || COMPILE_TEST + depends on ARCH_PHYTIUM depends on ACPI select DEVFREQ_GOV_SIMPLE_ONDEMAND help diff --git a/drivers/devfreq/phytium_dmu.c b/drivers/devfreq/phytium_dmu.c index 995a0e6231332..54f1febdbcd92 100644 --- a/drivers/devfreq/phytium_dmu.c +++ b/drivers/devfreq/phytium_dmu.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-1.0 /* - *phytium_dmu.c - Phytium Processor dmu Frequency Driver + * Phytium Processor DMU Frequency Driver (v1/v2 unified) * - *Copyright (C) 2024,Phytium Technology Co.,Ltd. + * Copyright (C) 2024, Phytium Technology Co.,Ltd. */ #include #include @@ -10,64 +10,81 @@ #include #include #include -#include -#include -#include #include -#include -#include #include -#include +#include #include +#include #include -#define DEBUG - -#define DEVICE_TYPE 9 //DMU ID - -#define DMU_PMU_STRIDE 0x80000 - -#define AXI_MONITOR2_L 0x084 -#define AXI_MONITOR3_L 0x08c -#define AXI_MONITOR_EN 0X01c -#define TIMER_START 0X000 -#define TIMER_STOP 0X004 -#define CLEAR_EVENT 0X008 - -#define MCU_STRIDE 0x00080000 -/* PMU notifier event */ -#define DDR_PMU_NOTICE_START 0x0 -#define DDR_PMU_NOTICE_STOP 0x1 +#define DEVICE_TYPE 9 + +#define DMUFREQ_DRIVER_VERSION "1.1.0" + +/* v1 Register definition */ +#define DMU_V1_PMU_STRIDE 0x80000 +#define DMU_V1_AXI_MONITOR2_L 0x084 +#define DMU_V1_AXI_MONITOR3_L 0x08c +#define DMU_V1_AXI_MONITOR_EN 0x01c +#define DMU_V1_TIMER_START 0x000 +#define DMU_V1_TIMER_STOP 0x004 +#define DMU_V1_CLEAR_EVENT 0x008 + +#define DDR_PMU_NOTICE_START 0x0 +#define DDR_PMU_NOTICE_STOP 0x1 + +/* v2 Register definition */ +#define DMU_V2_PDM_START 0xC0000 +#define DMU_V2_PDM_STRIDE 0x1000 +#define DMU_V2_MONITOR_START 0x304 +#define DMU_V2_MONITOR_SNAPSHOT 0x30c +#define DMU_V2_EVENT_CLEAR 0x308 +#define DMU_V2_EVENT_L_CNT 0x310 +#define DMU_V2_EVENT_H_CNT 0x314 + +enum phytium_dmu_type { + PHYTIUM_DMU_V1, + PHYTIUM_DMU_V2, +}; -#define DMUFREQ_DRIVER_VERSION "1.0.2" +struct phytium_dmufreq_info { + enum phytium_dmu_type type; + const char *name; +}; struct phytium_dmufreq { struct device *dev; + const struct phytium_dmufreq_info *info; struct devfreq *devfreq; struct devfreq_dev_profile profile; struct devfreq_simple_ondemand_data ondemand_data; - unsigned long rate, target_rate; + unsigned long rate; unsigned long bandwidth; unsigned long single_threshold_value; int max_count; int cnt; - void __iomem **base; - - unsigned long *read_bw; - unsigned long *write_bw; + /* v1: multichannel,v2: Single-channel multiple instances */ + union { + void __iomem **basev1; + void __iomem *basev2; + }; + /* v1 only */ + unsigned long *read_bw; + unsigned long *write_bw; struct notifier_block nb; - - /*dmu to pmu operation status identification 0: not operable, 1: operable*/ bool pmu_active; - unsigned long last_bust_time; - unsigned int freq_count; - unsigned long freq_table[]; + /* v2 only */ + unsigned int uid; + struct mutex lock; + + unsigned int freq_count; + unsigned long freq_table[]; }; struct acpi_result { @@ -75,18 +92,52 @@ struct acpi_result { unsigned long long value; }; -static inline void dmu_write32(struct phytium_dmufreq *priv, int dmu, - unsigned long offest, unsigned long value) +/* v1/v2 Distinguishing-type register access */ +static inline void dmu_v1_write32(struct phytium_dmufreq *priv, + int dmu, unsigned long offset, unsigned long value) +{ + writel_relaxed(value, priv->basev1[dmu] + offset); +} + +static inline unsigned long dmu_v1_read32(struct phytium_dmufreq *priv, + int dmu, unsigned long offset) +{ + return readl_relaxed(priv->basev1[dmu] + offset); +} + +static inline void dmu_v2_write32(struct phytium_dmufreq *priv, + unsigned long offset, unsigned long value) { - writel_relaxed(value, priv->base[dmu] + offest); + writel_relaxed(value, priv->basev2 + offset); } -static inline unsigned long dmu_read32(struct phytium_dmufreq *priv, int dmu, - unsigned long offest) +static inline unsigned long dmu_v2_read32(struct phytium_dmufreq *priv, + unsigned long offset) { - return readl_relaxed(priv->base[dmu] + offest); + return readl_relaxed(priv->basev2 + offset); } +/* ACPI/BIOS Related general methods */ +static struct acpi_result phytium_acpi_eval_integer(struct device *dev, const char *method) +{ + acpi_handle handle = ACPI_HANDLE(dev); + acpi_status status; + unsigned long long val; + struct acpi_result result; + + status = acpi_evaluate_integer(handle, (char *)method, NULL, &val); + if (ACPI_FAILURE(status)) { + dev_err(dev, "Failed to evaluate %s: ACPI status 0x%x\n", method, status); + result.status = -EIO; + result.value = 0; + return result; + } + result.status = 0; + result.value = val; + return result; +} + +/* V1 PMU notification chain, only V1 is registered */ BLOCKING_NOTIFIER_HEAD(dmu_pmu_notifier_chain); EXPORT_SYMBOL(dmu_pmu_notifier_chain); @@ -108,14 +159,14 @@ static int dmu_pmu_notifier_call(struct notifier_block *nb, unsigned long event, default: break; } - return NOTIFY_OK; } - +/* v1/v2 General ACPI Frequency Settings */ static int phytium_dmu_set_freq(struct device *dev, unsigned long freq) { acpi_handle handle = ACPI_HANDLE(dev); + struct phytium_dmufreq *priv = dev_get_drvdata(dev); union acpi_object args[4]; struct acpi_object_list arg_list = { .pointer = args, @@ -128,17 +179,24 @@ static int phytium_dmu_set_freq(struct device *dev, unsigned long freq) args[0].integer.value = DEVICE_TYPE; args[1].type = ACPI_TYPE_INTEGER; args[1].integer.value = freq; + if (priv->info->type == PHYTIUM_DMU_V2) + args[2].integer.value = priv->uid; + else + args[2].integer.value = 0; args[2].type = ACPI_TYPE_INTEGER; - args[2].integer.value = 0; args[3].type = ACPI_TYPE_INTEGER; args[3].integer.value = 0; + if (priv->info->type == PHYTIUM_DMU_V2) + mutex_lock(&priv->lock); status = acpi_evaluate_integer(handle, "PSCF", &arg_list, &ret); + if (priv->info->type == PHYTIUM_DMU_V2) + mutex_unlock(&priv->lock); + if (ACPI_FAILURE(status)) { dev_err(dev, "No PSCF method\n"); return -EIO; } - return 0; } @@ -155,26 +213,20 @@ static int phytium_dmu_target(struct device *dev, unsigned long *freq, u32 flags return PTR_ERR(opp); target_rate = dev_pm_opp_get_freq(opp); - dev_pm_opp_put(opp); if (target_rate == old_freq) return 0; dev_dbg(dev, "target_rate = %lu\n", target_rate); - /* - * Read back the clk rate to verify switch was correct and so that - * we can report it on all error paths. - */ ret = phytium_dmu_set_freq(dev, target_rate); + if (ret) { dev_warn(dev, "failed to set DRAM frequency: %lu\n", target_rate); return ret; } priv->rate = target_rate; - return ret; - } static int phytium_dmu_get_cur_freq(struct device *dev, unsigned long *freq) @@ -182,103 +234,108 @@ static int phytium_dmu_get_cur_freq(struct device *dev, unsigned long *freq) struct phytium_dmufreq *priv = dev_get_drvdata(dev); *freq = priv->rate; - return 0; } -struct acpi_result phytium_current_enabled_channels(struct device *dev) +/* v1/v2 General ACPI Method */ +static int get_freq_count(struct device *dev) { + struct acpi_result result; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object args[3], *package, *element; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; acpi_handle handle = ACPI_HANDLE(dev); acpi_status status; - unsigned long long enabled_channels; - struct acpi_result result; - status = acpi_evaluate_integer(handle, "CHAN", NULL, &enabled_channels); + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = DEVICE_TYPE; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = 0; + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = 0; + + status = acpi_evaluate_object(handle, "PGCL", &arg_list, &buffer); if (ACPI_FAILURE(status)) { - dev_err(dev, "Failed to evaluate CHAN method: ACPI status 0x%x\n", status); - result.status = -EIO; - result.value = 0; - return result; + dev_err(dev, "No PGCL method, status = %d\n", status); + return -EIO; } - dev_dbg(dev, "enabled_channels = %lld\n", enabled_channels); - result.status = 0; - result.value = enabled_channels; - return result; + package = buffer.pointer; + element = &package->package.elements[1]; + result.value = element->integer.value; + + kfree(buffer.pointer); + return result.value; } -struct acpi_result phytium_controller_bit_width(struct device *dev) +static int phytium_dmu_get_freq_info(struct device *dev) { + struct phytium_dmufreq *priv = dev_get_drvdata(dev); + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object args[3], *package, *element; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; acpi_handle handle = ACPI_HANDLE(dev); acpi_status status; - unsigned long long single_bit_width; - struct acpi_result result; + int i; + + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = DEVICE_TYPE; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = 0; + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = 0; - status = acpi_evaluate_integer(handle, "BITW", NULL, &single_bit_width); + status = acpi_evaluate_object(handle, "PGCL", &arg_list, &buffer); if (ACPI_FAILURE(status)) { - dev_err(dev, "Failed to evaluate BITW method: ACPI status 0x%x\n", status); - result.status = -EIO; - result.value = 0; - return result; + dev_err(dev, "No PGCL method\n"); + return -EIO; } - dev_dbg(dev, "single_bit_width = %lld(MB/s)\n", single_bit_width); - result.status = 0; - result.value = single_bit_width; - return result; -} -struct acpi_result phytium_dmufreq_state(struct device *dev) -{ - struct acpi_result result; - acpi_handle handle = ACPI_HANDLE(dev); - acpi_status status; - unsigned long long dmufreq_state; + package = buffer.pointer; + element = &package->package.elements[1]; + priv->freq_count = element->integer.value; - status = acpi_evaluate_integer(handle, "STAT", NULL, &dmufreq_state); - if (ACPI_FAILURE(status)) { - dev_err(dev, "Failed to evaluate STAT method: ACPI status 0x%x\n", status); - result.status = -EIO; - result.value = 0; - return result; + for (i = 0; i < priv->freq_count; i++) { + element = &package->package.elements[i+2]; + priv->freq_table[i] = element->integer.value; + dev_dbg(dev, "freq_table[%d] = %llu\n", i, element->integer.value); } - dev_dbg(dev, "dmufreq_state = %lld\n", dmufreq_state); - result.status = 0; - result.value = dmufreq_state; - return result; + kfree(buffer.pointer); + return 0; } -struct acpi_result phytium_read_threshold_value(struct device *dev) +/* v1/v2 General ACPI State */ +static struct acpi_result phytium_dmufreq_state(struct device *dev) { - acpi_handle handle = ACPI_HANDLE(dev); - acpi_status status; - unsigned long long single_threshold_value; - struct acpi_result result; - - status = acpi_evaluate_integer(handle, "BAND", NULL, &single_threshold_value); - if (ACPI_FAILURE(status)) { - WARN_ONCE(1, "Failed to evaluate BAND method: ACPI status 0x%x\n", status); - result.status = -EIO; - result.value = 0; - return result; - } - dev_dbg(dev, "single_threshold_value = %llu\n", single_threshold_value); - result.status = 0; - result.value = single_threshold_value; - return result; + return phytium_acpi_eval_integer(dev, "STAT"); +} +static struct acpi_result phytium_current_enabled_channels(struct device *dev) +{ + return phytium_acpi_eval_integer(dev, "CHAN"); +} +static struct acpi_result phytium_read_threshold_value(struct device *dev) +{ + return phytium_acpi_eval_integer(dev, "BAND"); } -static u64 phytium_dmufreq_get_real_bw(struct phytium_dmufreq *priv) +/* v1 Bandwidth acquisition with PMU notification chain */ +static u64 phytium_dmufreq_get_real_bw_v1(struct phytium_dmufreq *priv) { unsigned long peak_bw = 0; unsigned long sum_peak_bw = 0; int i; for (i = 0; i < priv->max_count; i++) { - priv->read_bw[i] = dmu_read32(priv, i, AXI_MONITOR2_L); - priv->write_bw[i] = dmu_read32(priv, i, AXI_MONITOR3_L); + priv->read_bw[i] = dmu_v1_read32(priv, i, DMU_V1_AXI_MONITOR2_L); + priv->write_bw[i] = dmu_v1_read32(priv, i, DMU_V1_AXI_MONITOR3_L); - /*clear the counter(only pmu_reg active)*/ - dmu_write32(priv, i, CLEAR_EVENT, 0x1); - dmu_write32(priv, i, TIMER_START, 0x1); + dmu_v1_write32(priv, i, DMU_V1_CLEAR_EVENT, 0x1); + dmu_v1_write32(priv, i, DMU_V1_TIMER_START, 0x1); sum_peak_bw = priv->read_bw[i] + priv->write_bw[i]; if (sum_peak_bw > peak_bw) peak_bw = sum_peak_bw; @@ -287,163 +344,132 @@ static u64 phytium_dmufreq_get_real_bw(struct phytium_dmufreq *priv) return peak_bw; } -static void polling_handle(struct phytium_dmufreq *priv) +static void polling_handle_v1(struct phytium_dmufreq *priv) { int i; - - /*if the pmu_reg is not active, return the last busy time(pmu_reg not work)*/ if (!priv->pmu_active) { priv->bandwidth = priv->last_bust_time; return; } if (priv->cnt > 0) { for (i = 0; i < priv->max_count ; i++) { - dmu_write32(priv, i, AXI_MONITOR_EN, 0x101); - dmu_write32(priv, i, TIMER_STOP, 0x1); + dmu_v1_write32(priv, i, DMU_V1_AXI_MONITOR_EN, 0x101); + dmu_v1_write32(priv, i, DMU_V1_TIMER_STOP, 0x1); } - priv->bandwidth = phytium_dmufreq_get_real_bw(priv); + priv->bandwidth = phytium_dmufreq_get_real_bw_v1(priv); } priv->cnt = 1; } -static int phytium_dmu_get_dev_status(struct device *dev, - struct devfreq_dev_status *stat) +/* v2 Multi-instance bandwidth acquisition */ +static void phytium_dmufreq_restart_clear_timer_v2(struct phytium_dmufreq *priv) { - struct phytium_dmufreq *priv = dev_get_drvdata(dev); - - polling_handle(priv); - priv->last_bust_time = stat->busy_time = priv->bandwidth; - stat->total_time = (priv->single_threshold_value * priv->rate) / priv->freq_table[0]; - - dev_dbg(dev, "busy_time = %lu, total_time = %lu,single_threshold_value = %llu\n", - stat->busy_time, stat->total_time, priv->single_threshold_value); - - stat->current_frequency = priv->rate; - return 0; + dmu_v2_write32(priv, DMU_V2_PDM_START + DMU_V2_EVENT_CLEAR, 0x1); + dmu_v2_write32(priv, DMU_V2_PDM_START + + DMU_V2_PDM_STRIDE + DMU_V2_EVENT_CLEAR, 0x1); + dmu_v2_write32(priv, DMU_V2_PDM_START + DMU_V2_EVENT_CLEAR, 0x0); + dmu_v2_write32(priv, DMU_V2_PDM_START + + DMU_V2_PDM_STRIDE + DMU_V2_EVENT_CLEAR, 0x0); } -static int phytium_dmu_get_freq_info(struct device *dev) +static u64 phytium_dmufreq_get_real_bw_v2(struct phytium_dmufreq *priv) { - struct phytium_dmufreq *priv = dev_get_drvdata(dev); - - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object args[3], *package, *element; - struct acpi_object_list arg_list = { - .pointer = args, - .count = ARRAY_SIZE(args), - }; - acpi_handle handle = ACPI_HANDLE(dev); - acpi_status status; - int i; - - args[0].type = ACPI_TYPE_INTEGER; - args[0].integer.value = DEVICE_TYPE; - args[1].type = ACPI_TYPE_INTEGER; - args[1].integer.value = 0; - args[2].type = ACPI_TYPE_INTEGER; - args[2].integer.value = 0; - - status = acpi_evaluate_object(handle, "PGCL", &arg_list, &buffer); - if (ACPI_FAILURE(status)) { - dev_err(dev, "No PGCL method\n"); - return -EIO; - } - - package = buffer.pointer; - - element = &package->package.elements[1]; - priv->freq_count = element->integer.value; - - for (i = 0; i < priv->freq_count; i++) { - element = &package->package.elements[i+2]; - priv->freq_table[i] = element->integer.value; - dev_dbg(dev, "freq_table[%d] = %llu\n", i, element->integer.value); - } - kfree(buffer.pointer); - return 0; - + u32 lo, hi; + u64 cnt_0, cnt_1, peak_bw; + + lo = dmu_v2_read32(priv, DMU_V2_PDM_START + DMU_V2_EVENT_L_CNT); + hi = dmu_v2_read32(priv, DMU_V2_PDM_START + DMU_V2_EVENT_H_CNT); + cnt_0 = ((u64)hi << 32) | lo; + lo = dmu_v2_read32(priv, DMU_V2_PDM_START + + DMU_V2_PDM_STRIDE + DMU_V2_EVENT_L_CNT); + hi = dmu_v2_read32(priv, DMU_V2_PDM_START + + DMU_V2_PDM_STRIDE + DMU_V2_EVENT_H_CNT); + cnt_1 = ((u64)hi << 32) | lo; + peak_bw = (cnt_0 + cnt_1) * 64UL; + + dev_dbg(priv->dev, "peak_bw = %llu\n", peak_bw); + return peak_bw; } -static int get_freq_count(struct device *dev) +static void polling_handle_v2(struct phytium_dmufreq *priv) { - int freq_count = -1; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object args[3], *package, *element; - struct acpi_object_list arg_list = { - .pointer = args, - .count = ARRAY_SIZE(args), - }; - acpi_handle handle = ACPI_HANDLE(dev); - acpi_status status; + dmu_v2_write32(priv, DMU_V2_PDM_START + DMU_V2_MONITOR_SNAPSHOT, 0x1); + dmu_v2_write32(priv, DMU_V2_PDM_START + + DMU_V2_PDM_STRIDE + DMU_V2_MONITOR_SNAPSHOT, 0x1); + priv->bandwidth = phytium_dmufreq_get_real_bw_v2(priv); + dmu_v2_write32(priv, DMU_V2_PDM_START + DMU_V2_MONITOR_SNAPSHOT, 0x0); + dmu_v2_write32(priv, DMU_V2_PDM_START + + DMU_V2_PDM_STRIDE + DMU_V2_MONITOR_SNAPSHOT, 0x0); +} - args[0].type = ACPI_TYPE_INTEGER; - args[0].integer.value = DEVICE_TYPE; - args[1].type = ACPI_TYPE_INTEGER; - args[1].integer.value = 0; - args[2].type = ACPI_TYPE_INTEGER; - args[2].integer.value = 0; +/* devfreq Framework interface */ +static int phytium_dmu_get_dev_status(struct device *dev, struct devfreq_dev_status *stat) +{ + struct phytium_dmufreq *priv = dev_get_drvdata(dev); - status = acpi_evaluate_object(handle, "PGCL", &arg_list, &buffer); - if (ACPI_FAILURE(status)) { - dev_err(dev, "No PGCL method, status = %d\n", status); - return -EIO; + if (priv->info->type == PHYTIUM_DMU_V1) { + polling_handle_v1(priv); + priv->last_bust_time = stat->busy_time = priv->bandwidth; + } else { + polling_handle_v2(priv); + stat->busy_time = priv->bandwidth; + phytium_dmufreq_restart_clear_timer_v2(priv); } - - package = buffer.pointer; - - element = &package->package.elements[1]; - freq_count = element->integer.value; - dev_dbg(dev, "freq_count = %d\n", freq_count); - - kfree(buffer.pointer); - return freq_count; + stat->total_time = (priv->single_threshold_value * priv->rate) / priv->freq_table[0]; + stat->current_frequency = priv->rate; + return 0; } +/* Power Management */ static __maybe_unused int phytium_dmufreq_suspend(struct device *dev) { struct phytium_dmufreq *priv = dev_get_drvdata(dev); - int ret = 0; - - dev_dbg(dev, "DMU is being suspended\n"); + int ret = devfreq_suspend_device(priv->devfreq); - ret = devfreq_suspend_device(priv->devfreq); - if (ret < 0) { + if (ret < 0) dev_err(dev, "failed to suspend the devfreq devices\n"); - return ret; - } - - return 0; + return ret; } static __maybe_unused int phytium_dmufreq_resume(struct device *dev) { struct phytium_dmufreq *priv = dev_get_drvdata(dev); - int ret = 0; - - dev_dbg(dev, "DMU is being resumed\n"); + int ret = devfreq_resume_device(priv->devfreq); - ret = devfreq_resume_device(priv->devfreq); - if (ret < 0) { + if (ret < 0) dev_err(dev, "failed to resume the devfreq devices\n"); - return ret; - } - - return 0; + return ret; } -static SIMPLE_DEV_PM_OPS(phytium_dmufreq_pm, phytium_dmufreq_suspend, - phytium_dmufreq_resume); +static SIMPLE_DEV_PM_OPS(phytium_dmufreq_pm, phytium_dmufreq_suspend, phytium_dmufreq_resume); static int phytium_dmufreq_probe(struct platform_device *pdev) { struct phytium_dmufreq *priv; struct device *dev = &pdev->dev; - const char *gov = DEVFREQ_GOV_PERFORMANCE; - int i, ret; - unsigned int max_state = get_freq_count(dev); + const struct phytium_dmufreq_info *info; struct acpi_result result; struct resource *res; + const char *gov; + void *gov_data = NULL; + int i, ret; + unsigned int max_state; + + info = device_get_match_data(dev); + if (!info) { + dev_err(dev, "No match data for this device\n"); + return -ENODEV; + } + + if (info->type == PHYTIUM_DMU_V1) { + gov = DEVFREQ_GOV_PERFORMANCE; + gov_data = &priv->ondemand_data; + } else { + gov = DEVFREQ_GOV_SIMPLE_ONDEMAND; + gov_data = NULL; + } + max_state = get_freq_count(dev); if (max_state <= 0) return -EINVAL; @@ -456,6 +482,7 @@ static int phytium_dmufreq_probe(struct platform_device *pdev) priv = devm_kzalloc(dev, struct_size(priv, freq_table, max_state), GFP_KERNEL); if (!priv) return -ENOMEM; + priv->info = info; result = phytium_current_enabled_channels(dev); if (result.status) { @@ -464,7 +491,6 @@ static int phytium_dmufreq_probe(struct platform_device *pdev) } priv->max_count = result.value; - result = phytium_read_threshold_value(dev); if (result.status) { dev_err(dev, "Failed to get threshold value\n"); @@ -472,87 +498,97 @@ static int phytium_dmufreq_probe(struct platform_device *pdev) } priv->single_threshold_value = result.value; priv->single_threshold_value = (priv->single_threshold_value * 1024 * 1024) / 10; - - dev->init_name = "dmufreq"; - - priv->base = devm_kcalloc(dev, priv->max_count, sizeof(void __iomem *), GFP_KERNEL); - priv->read_bw = devm_kcalloc(dev, priv->max_count, sizeof(unsigned long), GFP_KERNEL); - priv->write_bw = devm_kcalloc(dev, priv->max_count, sizeof(unsigned long), GFP_KERNEL); - if (!priv->base || !priv->read_bw || !priv->write_bw) { - dev_err(dev, "failed to allocate memory\n"); - return -ENOMEM; - } platform_set_drvdata(pdev, priv); - /* Register the notifier */ - priv->nb.notifier_call = dmu_pmu_notifier_call; - ret = blocking_notifier_chain_register(&dmu_pmu_notifier_chain, &priv->nb); - if (ret) { - dev_err(dev, "Failed to register notifier\n"); - return ret; - } - - /* Get the base address of the DMU PMU */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - for (i = 0; i < priv->max_count; i++) { - resource_size_t offset = res->start + i * DMU_PMU_STRIDE; - priv->base[i] = devm_ioremap(&pdev->dev, offset, resource_size(res)); - if (IS_ERR(priv->base[i])) { - dev_err(dev, "Ioremap failed for dmu base resource\n"); - return PTR_ERR(priv->base); - } + if (info->type == PHYTIUM_DMU_V1) { + /* V1: Multi-channel, allocation base/read_bw/write_bw */ + priv->basev1 = devm_kcalloc(dev, priv->max_count, + sizeof(void __iomem *), GFP_KERNEL); + priv->read_bw = devm_kcalloc(dev, priv->max_count, + sizeof(unsigned long), GFP_KERNEL); + priv->write_bw = devm_kcalloc(dev, priv->max_count, + sizeof(unsigned long), GFP_KERNEL); + if (!priv->basev1 || !priv->read_bw || !priv->write_bw) + return -ENOMEM; + for (i = 0; i < priv->max_count; i++) { + resource_size_t offset = res->start + i * DMU_V1_PMU_STRIDE; + + priv->basev1[i] = devm_ioremap(dev, offset, resource_size(res)); + if (IS_ERR(priv->basev1[i])) + return PTR_ERR(priv->basev1[i]); + } + /* Register PMU notification chain */ + priv->nb.notifier_call = dmu_pmu_notifier_call; + ret = blocking_notifier_chain_register(&dmu_pmu_notifier_chain, &priv->nb); + if (ret) { + dev_err(dev, "Failed to register notifier\n"); + return ret; + } + priv->pmu_active = true; + priv->cnt = 1; + dev->init_name = "dmufreq"; + } else { + /* V2: Single-channel multi-instance, allocate base, get uid */ + unsigned long long uid; + + mutex_init(&priv->lock); + acpi_evaluate_integer(ACPI_HANDLE(dev), "_UID", NULL, &uid); + priv->uid = uid; + priv->basev2 = devm_ioremap(dev, res->start, resource_size(res)); + if (IS_ERR(priv->basev2)) + return PTR_ERR(priv->basev2); + dev_set_name(dev, "dmu%u", priv->uid); } ret = phytium_dmu_get_freq_info(dev); - if (ret) { - dev_err(dev, "failed to get ddr frequency info\n"); - return -EIO; - } + if (ret) + return ret; - priv->pmu_active = true; - priv->cnt = 1; - priv->profile.initial_freq = priv->freq_table[0]; - priv->profile.polling_ms = 100; - priv->profile.timer = DEVFREQ_TIMER_DELAYED; - priv->profile.target = phytium_dmu_target; - priv->profile.get_cur_freq = phytium_dmu_get_cur_freq; - priv->profile.get_dev_status = phytium_dmu_get_dev_status; - priv->profile.freq_table = priv->freq_table; - priv->rate = priv->profile.initial_freq; - priv->profile.max_state = priv->freq_count; - priv->ondemand_data.upthreshold = 80; - priv->ondemand_data.downdifferential = 10; + priv->profile.initial_freq = priv->freq_table[0]; + priv->profile.polling_ms = 100; + priv->profile.timer = DEVFREQ_TIMER_DELAYED; + priv->profile.target = phytium_dmu_target; + priv->profile.get_cur_freq = phytium_dmu_get_cur_freq; + priv->profile.get_dev_status = phytium_dmu_get_dev_status; + priv->profile.freq_table = priv->freq_table; + priv->rate = priv->profile.initial_freq; + priv->profile.max_state = priv->freq_count; + priv->ondemand_data.upthreshold = 80; + priv->ondemand_data.downdifferential = 10; for (i = 0; i < max_state; ++i) { ret = dev_pm_opp_add(dev, priv->freq_table[i], 0); - if (ret < 0) { - dev_err(dev, "failed to get OPP table\n"); + if (ret < 0) goto err; - } } - priv->devfreq = devm_devfreq_add_device(dev, &priv->profile, - gov, NULL); + priv->devfreq = devm_devfreq_add_device(dev, &priv->profile, gov, gov_data); if (IS_ERR(priv->devfreq)) { ret = PTR_ERR(priv->devfreq); - dev_err(dev, "failed to add devfreq device: %d\n", ret); goto err; } - /*Enable PMU*/ - if (priv->pmu_active) { + /* v1: Enable PMU */ + if (info->type == PHYTIUM_DMU_V1 && priv->pmu_active) { for (i = 0; i < priv->max_count; i++) { - dmu_write32(priv, i, AXI_MONITOR_EN, 0x101); - dmu_write32(priv, i, CLEAR_EVENT, 0x1); - dmu_write32(priv, i, TIMER_START, 0x1); + dmu_v1_write32(priv, i, DMU_V1_AXI_MONITOR_EN, 0x101); + dmu_v1_write32(priv, i, DMU_V1_CLEAR_EVENT, 0x1); + dmu_v1_write32(priv, i, DMU_V1_TIMER_START, 0x1); } } - priv->dev = dev; + /* v2: Enable Monitoring */ + if (info->type == PHYTIUM_DMU_V2) { + dmu_v2_write32(priv, DMU_V2_PDM_START + + DMU_V2_MONITOR_START, 0x1); + dmu_v2_write32(priv, DMU_V2_PDM_START + + DMU_V2_PDM_STRIDE + DMU_V2_MONITOR_START, 0x1); + } + priv->dev = dev; return 0; - err: dev_pm_opp_of_remove_table(dev); return ret; @@ -564,50 +600,55 @@ static int phytium_dmufreq_remove(struct platform_device *pdev) struct device *dev = &pdev->dev; int i; - for (i = 0; i < priv->max_count; i++) { - dmu_write32(priv, i, TIMER_STOP, 0x1); - dmu_write32(priv, i, AXI_MONITOR_EN, 0x0); + if (priv->info->type == PHYTIUM_DMU_V1) { + for (i = 0; i < priv->max_count; i++) { + dmu_v1_write32(priv, i, DMU_V1_TIMER_STOP, 0x1); + dmu_v1_write32(priv, i, DMU_V1_AXI_MONITOR_EN, 0x0); + } + blocking_notifier_chain_unregister(&dmu_pmu_notifier_chain, &priv->nb); } - - /*Unregister the notifier*/ - blocking_notifier_chain_unregister(&dmu_pmu_notifier_chain, &priv->nb); - if (!priv->devfreq) return 0; dev_pm_opp_remove_all_dynamic(dev); - return 0; } +/* Matching Type Table */ +static const struct phytium_dmufreq_info phytium_dmu_v1_info = { + .type = PHYTIUM_DMU_V1, + .name = "phytium_dmu_v1", +}; + +static const struct phytium_dmufreq_info phytium_dmu_v2_info = { + .type = PHYTIUM_DMU_V2, + .name = "phytium_dmu_v2", +}; + #ifdef CONFIG_ACPI static const struct acpi_device_id phytium_dmufreq_acpi_ids[] = { - {"PHYT0063"}, + { "PHYT0063", (kernel_ulong_t)&phytium_dmu_v1_info }, + { "PHYT3011", (kernel_ulong_t)&phytium_dmu_v2_info }, {}, }; - MODULE_DEVICE_TABLE(acpi, phytium_dmufreq_acpi_ids); -#else -#define phytium_dmu_acpi_ids NULL #endif -struct notifier_block nb = { - .notifier_call = dmu_pmu_notifier_call, -}; - static struct platform_driver phytium_dmufreq_driver = { .probe = phytium_dmufreq_probe, .remove = phytium_dmufreq_remove, .driver = { .name = "phytium_dmufreq", .pm = &phytium_dmufreq_pm, - .acpi_match_table = ACPI_PTR(phytium_dmufreq_acpi_ids), + +#ifdef CONFIG_ACPI + .acpi_match_table = phytium_dmufreq_acpi_ids, +#endif .suppress_bind_attrs = true, - }, + } }; module_platform_driver(phytium_dmufreq_driver); -MODULE_DESCRIPTION("Phytium DDR Memory Unit frequency driver"); -MODULE_AUTHOR("Li Jiayi "); +MODULE_DESCRIPTION("Phytium DDR Memory Unit frequency driver (v1/v2 unified)"); MODULE_AUTHOR("Li Mingzhe "); MODULE_LICENSE("GPL"); MODULE_VERSION(DMUFREQ_DRIVER_VERSION); From 520768278e411ad4038d83c1dc720fefb5661551 Mon Sep 17 00:00:00 2001 From: Ma Mingrui Date: Tue, 12 May 2026 14:49:43 +0800 Subject: [PATCH 30/38] devfreq: Phytium:Add NOC DEVFREQ Support for PS260xxx SoCs Add NOC DEVFREQ driver Support for Phytium PS260xxx SoCs, and complatible with PD2408. Signed-off-by: Li Mingzhe Signed-off-by: Wang Yinfeng Signed-off-by: Ma Mingrui --- drivers/devfreq/Kconfig | 2 +- drivers/devfreq/phytium_noc.c | 353 +++++++++++++++++++--------------- 2 files changed, 194 insertions(+), 161 deletions(-) diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index 20df7fb0c5b0a..043ec397de524 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -152,7 +152,7 @@ config ARM_SUN8I_A33_MBUS_DEVFREQ config ARM_PHYTIUM_NOC_DEVFREQ tristate "ARM PHYTIUM NOC DEVFREQ Driver" - depends on ARCH_PHYTIUM || COMPILE_TEST + depends on ARCH_PHYTIUM depends on ACPI select DEVFREQ_GOV_SIMPLE_ONDEMAND help diff --git a/drivers/devfreq/phytium_noc.c b/drivers/devfreq/phytium_noc.c index 6df68f31a0117..28fde44b4dcd7 100644 --- a/drivers/devfreq/phytium_noc.c +++ b/drivers/devfreq/phytium_noc.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-1.0 /* - *phytium_noc.c - Phytium Processor noc Frequency Driver + * Phytium Processor NOC Frequency Driver (v1/v2 unified) * - *Copyright (C) 2024,Phytium Technology Co.,Ltd. + * Copyright (C) 2024, Phytium Technology Co.,Ltd. */ #include #include @@ -10,33 +10,54 @@ #include #include #include -#include -#include -#include +#include #include #include +#include +#include -#define MINI_SIZE 0x400 -#define CNT_ENABLE 0x000 -#define WORK_STATE 0X004 -#define CLR_EN 0X010 -#define SNAPSHOT_EN 0X014 -#define INT_CTRL_CLR 0x024 -#define WR_NOLAST_HANDSHARK_NUM 0x44 +#define NOCFREQ_DRIVER_VERSION "1.1.0" -#define DEBUG -#define DEVICE_TYPE 7 +enum phytium_noc_type { + PHYTIUM_NOC_V1, + PHYTIUM_NOC_V2, +}; + +struct phytium_nocfreq_info { + enum phytium_noc_type type; + const char *name; +}; + +#define DEVICE_TYPE_V1 7 +#define DEVICE_TYPE_V2 7 + +/* v1 Register definition */ +#define V1_MINI_SIZE 0x400 +#define V1_CNT_ENABLE 0x000 +#define V1_WORK_STATE 0X004 +#define V1_CLR_EN 0X010 +#define V1_SNAPSHOT_EN 0X014 +#define V1_INT_CTRL_CLR 0x024 +#define V1_WR_NOLAST_HANDSHARK_NUM 0x44 -#define NOCFREQ_DRIVER_VERSION "1.0.2" +/* v2 Register definition */ +#define V2_REG_NOC_STATUS 0x0 struct phytium_nocfreq { struct device *dev; + const struct phytium_nocfreq_info *info; struct devfreq *devfreq; struct devfreq_dev_profile profile; struct devfreq_simple_ondemand_data ondemand_data; - void __iomem *reg_noc; + /* v1 only */ + void __iomem *reg_noc_v1; + + /* v2 only */ + void __iomem *reg_noc_v2; + unsigned int uid; + struct mutex lock; unsigned long rate, target_rate, suspend_freq; @@ -44,15 +65,18 @@ struct phytium_nocfreq { unsigned long freq_table[]; }; -static u32 phytium_nocfreq_get_peak_bw(struct phytium_nocfreq *priv) +/* v1 Bandwidth acquisition */ +static u32 phytium_nocfreq_get_peak_bw_v1(struct phytium_nocfreq *priv) { - /*Returns the peak number of dmu read/write commands on the axi bus.*/ unsigned long peak_bw, bw_0, bw_1, bw_2, bw_3; - bw_0 = readl_relaxed(priv->reg_noc + WR_NOLAST_HANDSHARK_NUM); - bw_1 = readl_relaxed(priv->reg_noc + MINI_SIZE*1 + WR_NOLAST_HANDSHARK_NUM); - bw_2 = readl_relaxed(priv->reg_noc + MINI_SIZE*2 + WR_NOLAST_HANDSHARK_NUM); - bw_3 = readl_relaxed(priv->reg_noc + MINI_SIZE*3 + WR_NOLAST_HANDSHARK_NUM); + bw_0 = readl_relaxed(priv->reg_noc_v1 + V1_WR_NOLAST_HANDSHARK_NUM); + bw_1 = readl_relaxed(priv->reg_noc_v1 + V1_MINI_SIZE*1 + + V1_WR_NOLAST_HANDSHARK_NUM); + bw_2 = readl_relaxed(priv->reg_noc_v1 + V1_MINI_SIZE*2 + + V1_WR_NOLAST_HANDSHARK_NUM); + bw_3 = readl_relaxed(priv->reg_noc_v1 + V1_MINI_SIZE*3 + + V1_WR_NOLAST_HANDSHARK_NUM); peak_bw = bw_0; if (bw_1 > peak_bw) @@ -61,28 +85,29 @@ static u32 phytium_nocfreq_get_peak_bw(struct phytium_nocfreq *priv) peak_bw = bw_2; if (bw_3 > peak_bw) peak_bw = bw_3; - return peak_bw; } -static void phytium_nocfreq_restart_handshark_counters(struct phytium_nocfreq *priv) +static void phytium_nocfreq_restart_handshark_counters_v1(struct phytium_nocfreq *priv) { - - /*clear interrupt*/ - - writel_relaxed(0x80000000, priv->reg_noc + INT_CTRL_CLR); - writel_relaxed(0x80000000, priv->reg_noc + MINI_SIZE*1 + INT_CTRL_CLR); - writel_relaxed(0x80000000, priv->reg_noc + MINI_SIZE*2 + INT_CTRL_CLR); - writel_relaxed(0x80000000, priv->reg_noc + MINI_SIZE*3 + INT_CTRL_CLR); - - /*clear counters*/ - writel_relaxed(0x1, priv->reg_noc + CLR_EN); - writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*1 + CLR_EN); - writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*2 + CLR_EN); - writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*3 + CLR_EN); + writel_relaxed(0x80000000, priv->reg_noc_v1 + V1_INT_CTRL_CLR); + writel_relaxed(0x80000000, priv->reg_noc_v1 + V1_MINI_SIZE*1 + V1_INT_CTRL_CLR); + writel_relaxed(0x80000000, priv->reg_noc_v1 + V1_MINI_SIZE*2 + V1_INT_CTRL_CLR); + writel_relaxed(0x80000000, priv->reg_noc_v1 + V1_MINI_SIZE*3 + V1_INT_CTRL_CLR); + + writel_relaxed(0x1, priv->reg_noc_v1 + V1_CLR_EN); + writel_relaxed(0x1, priv->reg_noc_v1 + V1_MINI_SIZE*1 + V1_CLR_EN); + writel_relaxed(0x1, priv->reg_noc_v1 + V1_MINI_SIZE*2 + V1_CLR_EN); + writel_relaxed(0x1, priv->reg_noc_v1 + V1_MINI_SIZE*3 + V1_CLR_EN); } +/* v2 Bandwidth acquisition */ +static u32 phytium_nocfreq_get_peak_bw_v2(struct phytium_nocfreq *priv) +{ + return readl_relaxed(priv->reg_noc_v2 + V2_REG_NOC_STATUS); +} +/* v1/v2 General frequency setting */ static int phytium_noc_set_freq(struct device *dev, unsigned long freq) { struct phytium_nocfreq *priv = dev_get_drvdata(dev); @@ -96,11 +121,17 @@ static int phytium_noc_set_freq(struct device *dev, unsigned long freq) unsigned long long ret; args[0].type = ACPI_TYPE_INTEGER; - args[0].integer.value = DEVICE_TYPE; + args[0].integer.value = DEVICE_TYPE_V1; args[1].type = ACPI_TYPE_INTEGER; args[1].integer.value = freq; - args[2].type = ACPI_TYPE_INTEGER; - args[2].integer.value = 0; + + if (priv->info->type == PHYTIUM_NOC_V2) { + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = priv->uid; + } else { + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = 0; + } args[3].type = ACPI_TYPE_INTEGER; args[3].integer.value = 0; @@ -112,7 +143,6 @@ static int phytium_noc_set_freq(struct device *dev, unsigned long freq) dev_err(dev, "No PSCF method\n"); return -EIO; } - if (ret) { dev_err(dev, "Failed to set the freq to %lu\n", freq); return -EIO; @@ -138,19 +168,14 @@ static int phytium_noc_target(struct device *dev, unsigned long *freq, u32 flags if (target_rate == old_freq) return 0; - /* - * Read back the clk rate to verify switch was correct and so that - * we can report it on all error paths. - */ - ret = phytium_noc_set_freq(dev, target_rate); + ret = phytium_noc_set_freq(dev, target_rate); if (ret) { dev_warn(dev, "failed to set noc frequency: %d\n", ret); *freq = old_freq; } priv->rate = target_rate; return ret; - } static int phytium_noc_get_cur_freq(struct device *dev, unsigned long *freq) @@ -166,7 +191,7 @@ static int phytium_noc_get_cur_freq(struct device *dev, unsigned long *freq) unsigned long long ret; args[0].type = ACPI_TYPE_INTEGER; - args[0].integer.value = DEVICE_TYPE; + args[0].integer.value = DEVICE_TYPE_V1; args[1].type = ACPI_TYPE_INTEGER; args[1].integer.value = 0; args[2].type = ACPI_TYPE_INTEGER; @@ -179,20 +204,17 @@ static int phytium_noc_get_cur_freq(struct device *dev, unsigned long *freq) dev_err(dev, "No PGCF method\n"); return -EIO; } - if (ret < 0) { dev_err(dev, "Failed to get the freq\n"); return -EIO; } *freq = ret; - return 0; } static int phytium_noc_get_freq_info(struct device *dev, u32 flags) { struct phytium_nocfreq *priv = dev_get_drvdata(dev); - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object args[3], *package, *element; struct acpi_object_list arg_list = { @@ -221,7 +243,6 @@ static int phytium_noc_get_freq_info(struct device *dev, u32 flags) } package = buffer.pointer; - element = &package->package.elements[1]; priv->freq_count = element->integer.value; @@ -230,10 +251,8 @@ static int phytium_noc_get_freq_info(struct device *dev, u32 flags) priv->freq_table[i] = element->integer.value; dev_dbg(dev, "freq_table[%d] = %llu\n", i, element->integer.value); } - kfree(buffer.pointer); return 0; - } static int get_freq_count(struct device *dev) @@ -242,14 +261,14 @@ static int get_freq_count(struct device *dev) struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object args[3], *package, *element; struct acpi_object_list arg_list = { - .pointer = args, + .pointer = args, .count = ARRAY_SIZE(args), }; acpi_handle handle = ACPI_HANDLE(dev); acpi_status status; args[0].type = ACPI_TYPE_INTEGER; - args[0].integer.value = DEVICE_TYPE; + args[0].integer.value = DEVICE_TYPE_V1; args[1].type = ACPI_TYPE_INTEGER; args[1].integer.value = 0; args[2].type = ACPI_TYPE_INTEGER; @@ -266,37 +285,34 @@ static int get_freq_count(struct device *dev) } package = buffer.pointer; - element = &package->package.elements[1]; freq_count = element->integer.value; dev_dbg(dev, "freq_count = %d\n", freq_count); - kfree(buffer.pointer); return freq_count; } -static int phytium_noc_get_dev_status(struct device *dev, - struct devfreq_dev_status *stat) +static int phytium_noc_get_dev_status(struct device *dev, struct devfreq_dev_status *stat) { struct phytium_nocfreq *priv = dev_get_drvdata(dev); - unsigned int val; - - writel_relaxed(0x1, priv->reg_noc + SNAPSHOT_EN); - writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*1 + SNAPSHOT_EN); - writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*2 + SNAPSHOT_EN); - writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*3 + SNAPSHOT_EN); - - val = DIV_ROUND_CLOSEST(priv->rate * 100, priv->profile.initial_freq); - stat->busy_time = phytium_nocfreq_get_peak_bw(priv); - stat->total_time = 320000 * val; - stat->current_frequency = priv->rate; - - phytium_nocfreq_restart_handshark_counters(priv); - dev_dbg(dev, "Using %lu/%lu (%lu%%) at %lu KHz\n", - stat->busy_time, stat->total_time, - DIV_ROUND_CLOSEST(stat->busy_time * 100, stat->total_time), - stat->current_frequency); + if (priv->info->type == PHYTIUM_NOC_V1) { + writel_relaxed(0x1, priv->reg_noc_v1 + V1_SNAPSHOT_EN); + writel_relaxed(0x1, priv->reg_noc_v1 + V1_MINI_SIZE*1 + V1_SNAPSHOT_EN); + writel_relaxed(0x1, priv->reg_noc_v1 + V1_MINI_SIZE*2 + V1_SNAPSHOT_EN); + writel_relaxed(0x1, priv->reg_noc_v1 + V1_MINI_SIZE*3 + V1_SNAPSHOT_EN); + + stat->busy_time = phytium_nocfreq_get_peak_bw_v1(priv); + stat->total_time = 320000 * DIV_ROUND_CLOSEST(priv->rate * 100, + priv->profile.initial_freq); + stat->current_frequency = priv->rate; + + phytium_nocfreq_restart_handshark_counters_v1(priv); + } else { + stat->busy_time = phytium_nocfreq_get_peak_bw_v2(priv); + stat->total_time = 15 * DIV_ROUND_CLOSEST(priv->rate, priv->freq_table[0]); + stat->current_frequency = priv->rate; + } return 0; } @@ -305,26 +321,21 @@ static __maybe_unused int phytium_nocfreq_suspend(struct device *dev) struct phytium_nocfreq *priv = dev_get_drvdata(dev); int ret = 0; - dev_dbg(dev, "NOCfreq is being suspended\n"); - ret = phytium_noc_get_cur_freq(dev, &priv->suspend_freq); if (ret) dev_warn(dev, "failed to get suspend freq\n"); - else - dev_info(dev, "saved suspend freq = %lu\n", priv->suspend_freq); ret = devfreq_suspend_device(priv->devfreq); - if (ret < 0) { + if (ret < 0) dev_err(dev, "failed to suspend the devfreq devices\n"); - return ret; - } priv->devfreq->stop_polling = true; - writel_relaxed(0x0, priv->reg_noc + CNT_ENABLE); - writel_relaxed(0x0, priv->reg_noc + MINI_SIZE*1+CNT_ENABLE); - writel_relaxed(0x0, priv->reg_noc + MINI_SIZE*2+CNT_ENABLE); - writel_relaxed(0x0, priv->reg_noc + MINI_SIZE*3+CNT_ENABLE); - + if (priv->info->type == PHYTIUM_NOC_V1) { + writel_relaxed(0x0, priv->reg_noc_v1 + V1_CNT_ENABLE); + writel_relaxed(0x0, priv->reg_noc_v1 + V1_MINI_SIZE*1+V1_CNT_ENABLE); + writel_relaxed(0x0, priv->reg_noc_v1 + V1_MINI_SIZE*2+V1_CNT_ENABLE); + writel_relaxed(0x0, priv->reg_noc_v1 + V1_MINI_SIZE*3+V1_CNT_ENABLE); + } return ret; } @@ -333,80 +344,88 @@ static __maybe_unused int phytium_nocfreq_resume(struct device *dev) struct phytium_nocfreq *priv = dev_get_drvdata(dev); int ret = 0; - dev_dbg(dev, "NOCfreq is being resumed\n"); - ret = devfreq_resume_device(priv->devfreq); - if (ret < 0) { + if (ret < 0) dev_err(dev, "failed to resume the devfreq devices\n"); - return ret; - } - if (!delayed_work_pending(&priv->devfreq->work) && priv->devfreq->profile->polling_ms) { - dev_info(dev, "Polling work not pending, manually restarting polling\n"); + if (!delayed_work_pending(&priv->devfreq->work) && priv->devfreq->profile->polling_ms) priv->devfreq->stop_polling = true; - } - writel_relaxed(0x02, priv->reg_noc + WORK_STATE); - writel_relaxed(0x02, priv->reg_noc + MINI_SIZE*1 + WORK_STATE); - writel_relaxed(0x02, priv->reg_noc + MINI_SIZE*2 + WORK_STATE); - writel_relaxed(0x02, priv->reg_noc + MINI_SIZE*3 + WORK_STATE); - writel_relaxed(0x3f, priv->reg_noc + CNT_ENABLE); - writel_relaxed(0x3f, priv->reg_noc + MINI_SIZE*1+CNT_ENABLE); - writel_relaxed(0x3f, priv->reg_noc + MINI_SIZE*2+CNT_ENABLE); - writel_relaxed(0x3f, priv->reg_noc + MINI_SIZE*3+CNT_ENABLE); + if (priv->info->type == PHYTIUM_NOC_V1) { + writel_relaxed(0x02, priv->reg_noc_v1 + V1_WORK_STATE); + writel_relaxed(0x02, priv->reg_noc_v1 + V1_MINI_SIZE*1 + V1_WORK_STATE); + writel_relaxed(0x02, priv->reg_noc_v1 + V1_MINI_SIZE*2 + V1_WORK_STATE); + writel_relaxed(0x02, priv->reg_noc_v1 + V1_MINI_SIZE*3 + V1_WORK_STATE); + writel_relaxed(0x3f, priv->reg_noc_v1 + V1_CNT_ENABLE); + writel_relaxed(0x3f, priv->reg_noc_v1 + V1_MINI_SIZE*1+V1_CNT_ENABLE); + writel_relaxed(0x3f, priv->reg_noc_v1 + V1_MINI_SIZE*2+V1_CNT_ENABLE); + writel_relaxed(0x3f, priv->reg_noc_v1 + V1_MINI_SIZE*3+V1_CNT_ENABLE); + } if (priv->suspend_freq) { ret = phytium_noc_set_freq(dev, priv->suspend_freq); if (ret < 0) dev_warn(dev, "failed to restore suspend freq %lu\n", priv->suspend_freq); - else { - dev_info(dev, "restored suspend freq = %lu\n", priv->suspend_freq); + else priv->rate = priv->suspend_freq; - } } return ret; } -static SIMPLE_DEV_PM_OPS(phytium_nocfreq_pm, phytium_nocfreq_suspend, - phytium_nocfreq_resume); +static SIMPLE_DEV_PM_OPS(phytium_nocfreq_pm, phytium_nocfreq_suspend, phytium_nocfreq_resume); static int phytium_nocfreq_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct phytium_nocfreq *priv; + const struct phytium_nocfreq_info *info; const char *gov = DEVFREQ_GOV_SIMPLE_ONDEMAND; int i, ret; unsigned int max_state; struct resource *mem; - max_state = get_freq_count(dev); - - dev->init_name = "nocfreq"; + info = device_get_match_data(dev); + if (!info) { + dev_err(dev, "No match data for this device\n"); + return -ENODEV; + } + max_state = get_freq_count(dev); + if (max_state <= 0) + return -EINVAL; priv = devm_kzalloc(dev, struct_size(priv, freq_table, max_state), GFP_KERNEL); if (!priv) return -ENOMEM; - + priv->info = info; mutex_init(&priv->lock); + platform_set_drvdata(pdev, priv); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem) { - dev_err(&pdev->dev, "no mem resource"); + dev_err(dev, "no mem resource"); return -EINVAL; } - priv->reg_noc = devm_ioremap_resource(&pdev->dev, mem); - if (!priv->reg_noc) { - dev_err(dev, "NOC region map failed\n"); - return PTR_ERR(priv->reg_noc); - } - - ret = phytium_noc_get_freq_info(dev, DEVICE_TYPE); - if (ret) { - dev_err(dev, "failed to get noc frequency info\n"); - return -EIO; + if (info->type == PHYTIUM_NOC_V1) { + priv->reg_noc_v1 = devm_ioremap_resource(dev, mem); + if (!priv->reg_noc_v1) + return PTR_ERR(priv->reg_noc_v1); + dev->init_name = "nocfreq"; + } else { + acpi_handle handle = ACPI_HANDLE(dev); + unsigned long long uid; + + ret = acpi_evaluate_integer(handle, "_UID", NULL, &uid); + priv->uid = uid; + priv->reg_noc_v2 = devm_ioremap_resource(dev, mem); + if (IS_ERR(priv->reg_noc_v2)) + return PTR_ERR(priv->reg_noc_v2); + dev_set_name(dev, "noc-%u", priv->uid); } + ret = phytium_noc_get_freq_info(dev, DEVICE_TYPE_V1); + if (ret) + return ret; priv->profile.initial_freq = priv->freq_table[0]; priv->profile.polling_ms = 100; priv->profile.target = phytium_noc_target; @@ -415,40 +434,41 @@ static int phytium_nocfreq_probe(struct platform_device *pdev) priv->profile.freq_table = priv->freq_table; priv->profile.max_state = priv->freq_count; priv->rate = priv->freq_table[0]; - priv->ondemand_data.upthreshold = 80; - priv->ondemand_data.downdifferential = 10; - priv->profile.max_state = priv->freq_count; + + if (info->type == PHYTIUM_NOC_V1) { + priv->ondemand_data.upthreshold = 80; + priv->ondemand_data.downdifferential = 10; + } else { + priv->ondemand_data.upthreshold = 95; + priv->ondemand_data.downdifferential = 5; + } for (i = 0; i < max_state; ++i) { ret = dev_pm_opp_add(dev, priv->freq_table[i], 0); - if (ret < 0) { - dev_err(dev, "failed to get OPP table\n"); + if (ret < 0) goto err; - } } - priv->devfreq = devm_devfreq_add_device(dev, &priv->profile, - gov, &priv->ondemand_data); + priv->devfreq = devm_devfreq_add_device(dev, &priv->profile, gov, &priv->ondemand_data); if (IS_ERR(priv->devfreq)) { ret = PTR_ERR(priv->devfreq); - dev_err(dev, "failed to add devfreq device: %d\n", ret); goto err; } - ret = phytium_noc_set_freq(dev, priv->profile.initial_freq); if (ret) dev_warn(dev, "failed to init noc frequency: %d\n", ret); - writel_relaxed(0x02, priv->reg_noc + WORK_STATE); - writel_relaxed(0x02, priv->reg_noc + MINI_SIZE*1 + WORK_STATE); - writel_relaxed(0x02, priv->reg_noc + MINI_SIZE*2 + WORK_STATE); - writel_relaxed(0x02, priv->reg_noc + MINI_SIZE*3 + WORK_STATE); + if (info->type == PHYTIUM_NOC_V1) { + writel_relaxed(0x02, priv->reg_noc_v1 + V1_WORK_STATE); + writel_relaxed(0x02, priv->reg_noc_v1 + V1_MINI_SIZE*1 + V1_WORK_STATE); + writel_relaxed(0x02, priv->reg_noc_v1 + V1_MINI_SIZE*2 + V1_WORK_STATE); + writel_relaxed(0x02, priv->reg_noc_v1 + V1_MINI_SIZE*3 + V1_WORK_STATE); - writel_relaxed(0x3f, priv->reg_noc + CNT_ENABLE); - writel_relaxed(0x3f, priv->reg_noc + MINI_SIZE*1 + CNT_ENABLE); - writel_relaxed(0x3f, priv->reg_noc + MINI_SIZE*2 + CNT_ENABLE); - writel_relaxed(0x3f, priv->reg_noc + MINI_SIZE*3 + CNT_ENABLE); + writel_relaxed(0x3f, priv->reg_noc_v1 + V1_CNT_ENABLE); + writel_relaxed(0x3f, priv->reg_noc_v1 + V1_MINI_SIZE*1 + V1_CNT_ENABLE); + writel_relaxed(0x3f, priv->reg_noc_v1 + V1_MINI_SIZE*2 + V1_CNT_ENABLE); + writel_relaxed(0x3f, priv->reg_noc_v1 + V1_MINI_SIZE*3 + V1_CNT_ENABLE); + } return 0; - err: dev_pm_opp_of_remove_table(dev); return ret; @@ -461,35 +481,46 @@ static int phytium_nocfreq_remove(struct platform_device *pdev) struct device *dev = &pdev->dev; int ret; - writel_relaxed(0x0, priv->reg_noc + CNT_ENABLE); - writel_relaxed(0x0, priv->reg_noc + MINI_SIZE*1 + CNT_ENABLE); - writel_relaxed(0x0, priv->reg_noc + MINI_SIZE*2 + CNT_ENABLE); - writel_relaxed(0x0, priv->reg_noc + MINI_SIZE*3 + CNT_ENABLE); + if (priv->info->type == PHYTIUM_NOC_V1) { + writel_relaxed(0x0, priv->reg_noc_v1 + V1_CNT_ENABLE); + writel_relaxed(0x0, priv->reg_noc_v1 + V1_MINI_SIZE*1 + V1_CNT_ENABLE); + writel_relaxed(0x0, priv->reg_noc_v1 + V1_MINI_SIZE*2 + V1_CNT_ENABLE); + writel_relaxed(0x0, priv->reg_noc_v1 + V1_MINI_SIZE*3 + V1_CNT_ENABLE); - writel_relaxed(0x1, priv->reg_noc + CLR_EN); - writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*1 + CLR_EN); - writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*2 + CLR_EN); - writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*3 + CLR_EN); + writel_relaxed(0x1, priv->reg_noc_v1 + V1_CLR_EN); + writel_relaxed(0x1, priv->reg_noc_v1 + V1_MINI_SIZE*1 + V1_CLR_EN); + writel_relaxed(0x1, priv->reg_noc_v1 + V1_MINI_SIZE*2 + V1_CLR_EN); + writel_relaxed(0x1, priv->reg_noc_v1 + V1_MINI_SIZE*3 + V1_CLR_EN); + } ret = phytium_noc_set_freq(dev, initial_freq); if (ret) dev_warn(dev, "failed to restore NOC frequency: %d\n", ret); - if (!priv->devfreq) return 0; - dev_pm_opp_remove_all_dynamic(dev); - return 0; } +/* Matching Type Table */ +static const struct phytium_nocfreq_info phytium_noc_v1_info = { + .type = PHYTIUM_NOC_V1, + .name = "phytium_noc_v1", +}; +static const struct phytium_nocfreq_info phytium_noc_v2_info = { + .type = PHYTIUM_NOC_V2, + .name = "phytium_noc_v2", +}; + +#ifdef CONFIG_ACPI static const struct acpi_device_id phytium_noc_acpi_ids[] = { - {"PHYT0047"}, + { "PHYT0047", (kernel_ulong_t)&phytium_noc_v1_info }, + { "PHYT3010", (kernel_ulong_t)&phytium_noc_v2_info }, {}, }; - MODULE_DEVICE_TABLE(acpi, phytium_noc_acpi_ids); +#endif static struct platform_driver phytium_nocfreq_driver = { .probe = phytium_nocfreq_probe, @@ -497,13 +528,15 @@ static struct platform_driver phytium_nocfreq_driver = { .driver = { .name = "phytium_nocfreq", .pm = &phytium_nocfreq_pm, - .acpi_match_table = ACPI_PTR(phytium_noc_acpi_ids), +#ifdef CONFIG_ACPI + .acpi_match_table = phytium_noc_acpi_ids, +#endif .suppress_bind_attrs = true, }, }; module_platform_driver(phytium_nocfreq_driver); -MODULE_DESCRIPTION("Phytium NOC Controller frequency driver"); -MODULE_AUTHOR("Li Jiayi "); +MODULE_DESCRIPTION("Phytium NOC Controller frequency driver (v1/v2 unified)"); +MODULE_AUTHOR("Li Mingzhe "); MODULE_LICENSE("GPL"); MODULE_VERSION(NOCFREQ_DRIVER_VERSION); From 2bc52895a67da60c6347a2e9cc6a3d5a58123e22 Mon Sep 17 00:00:00 2001 From: Ma Mingrui Date: Tue, 12 May 2026 14:52:01 +0800 Subject: [PATCH 31/38] devfreq: Phytium: Adjust data handing logic in DMU devfreq probe path Move governor selection after priv allocation and adjust data handing logic. Signed-off-by: Li Mingzhe Signed-off-by: Wang Yinfeng Signed-off-by: Ma Mingrui --- drivers/devfreq/phytium_dmu.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/devfreq/phytium_dmu.c b/drivers/devfreq/phytium_dmu.c index 54f1febdbcd92..e85cd43f9511f 100644 --- a/drivers/devfreq/phytium_dmu.c +++ b/drivers/devfreq/phytium_dmu.c @@ -461,14 +461,6 @@ static int phytium_dmufreq_probe(struct platform_device *pdev) return -ENODEV; } - if (info->type == PHYTIUM_DMU_V1) { - gov = DEVFREQ_GOV_PERFORMANCE; - gov_data = &priv->ondemand_data; - } else { - gov = DEVFREQ_GOV_SIMPLE_ONDEMAND; - gov_data = NULL; - } - max_state = get_freq_count(dev); if (max_state <= 0) return -EINVAL; @@ -484,6 +476,14 @@ static int phytium_dmufreq_probe(struct platform_device *pdev) return -ENOMEM; priv->info = info; + if (info->type == PHYTIUM_DMU_V1) { + gov = DEVFREQ_GOV_PERFORMANCE; + gov_data = NULL; + } else { + gov = DEVFREQ_GOV_SIMPLE_ONDEMAND; + gov_data = &priv->ondemand_data; + } + result = phytium_current_enabled_channels(dev); if (result.status) { dev_err(dev, "Failed to get enabled channels\n"); From 6a32d12ee0c2c05d5cf969c710b8cd34b9bc54b2 Mon Sep 17 00:00:00 2001 From: Ma Mingrui Date: Tue, 12 May 2026 14:55:33 +0800 Subject: [PATCH 32/38] devfreq: Phytium: Tune scaling with ramp-up and hysteresis Improves NoC-V2 devfreq behavior by fixing low-load oscillation, speeding up ramp-up under rising traffic, and adding hysteresis to reduce frequent back-and-forth transitions between adjacent levels. Signed-off-by: Li Mingzhe Signed-off-by: Wang Yinfeng Signed-off-by: Ma Mingrui --- drivers/devfreq/phytium_noc.c | 101 ++++++++++++++++++++++++++++++++-- 1 file changed, 97 insertions(+), 4 deletions(-) diff --git a/drivers/devfreq/phytium_noc.c b/drivers/devfreq/phytium_noc.c index 28fde44b4dcd7..e9a60aea0ca4c 100644 --- a/drivers/devfreq/phytium_noc.c +++ b/drivers/devfreq/phytium_noc.c @@ -42,6 +42,18 @@ struct phytium_nocfreq_info { /* v2 Register definition */ #define V2_REG_NOC_STATUS 0x0 +#define V2_BUSY_CODE_MASK 0x1f +#define V2_BUSY_CODE_MAX 16 + +/* Fast ramp-up thresholds on each level (busy code out of 16) */ +#define V2_UP_225_TO_450_BUSY 4 +#define V2_UP_450_TO_900_BUSY 8 +#define V2_UP_900_TO_1800_BUSY 12 + +/* Hysteresis hold/down thresholds */ +#define V2_DOWN_450_TO_225_BUSY 2 +#define V2_DOWN_900_TO_450_BUSY 6 +#define V2_DOWN_1800_TO_900_BUSY 10 struct phytium_nocfreq { struct device *dev; @@ -57,6 +69,7 @@ struct phytium_nocfreq { /* v2 only */ void __iomem *reg_noc_v2; unsigned int uid; + unsigned int v2_busy_code; struct mutex lock; @@ -107,6 +120,49 @@ static u32 phytium_nocfreq_get_peak_bw_v2(struct phytium_nocfreq *priv) return readl_relaxed(priv->reg_noc_v2 + V2_REG_NOC_STATUS); } +static unsigned long phytium_nocfreq_min_rate(struct phytium_nocfreq *priv) +{ + unsigned long min_rate = priv->freq_table[0]; + int i; + + for (i = 1; i < priv->freq_count; i++) { + if (priv->freq_table[i] < min_rate) + min_rate = priv->freq_table[i]; + } + + return min_rate; +} + +static unsigned long phytium_nocfreq_max_rate(struct phytium_nocfreq *priv) +{ + unsigned long max_rate = priv->freq_table[0]; + int i; + + for (i = 1; i < priv->freq_count; i++) { + if (priv->freq_table[i] > max_rate) + max_rate = priv->freq_table[i]; + } + + return max_rate; +} + +static unsigned long phytium_nocfreq_next_higher_rate(struct phytium_nocfreq *priv, + unsigned long rate) +{ + unsigned long next_rate = ~0UL; + int i; + + for (i = 0; i < priv->freq_count; i++) { + if (priv->freq_table[i] > rate && priv->freq_table[i] < next_rate) + next_rate = priv->freq_table[i]; + } + + if (next_rate == ~0UL) + return rate; + + return next_rate; +} + /* v1/v2 General frequency setting */ static int phytium_noc_set_freq(struct device *dev, unsigned long freq) { @@ -166,6 +222,36 @@ static int phytium_noc_target(struct device *dev, unsigned long *freq, u32 flags target_rate = dev_pm_opp_get_freq(opp); dev_pm_opp_put(opp); + if (priv->info->type == PHYTIUM_NOC_V2) { + unsigned long low = phytium_nocfreq_min_rate(priv); + unsigned long mid1 = phytium_nocfreq_next_higher_rate(priv, low); + unsigned long mid2 = phytium_nocfreq_next_higher_rate(priv, mid1); + unsigned long high = phytium_nocfreq_max_rate(priv); + + if (old_freq == low) { + if (priv->v2_busy_code >= V2_UP_225_TO_450_BUSY && mid1 > low && + target_rate < mid1) + target_rate = mid1; + } else if (old_freq == mid1) { + if (priv->v2_busy_code >= V2_UP_450_TO_900_BUSY && mid2 > mid1 && + target_rate < mid2) + target_rate = mid2; + else if (priv->v2_busy_code > V2_DOWN_450_TO_225_BUSY && + priv->v2_busy_code < V2_UP_450_TO_900_BUSY) + target_rate = mid1; + } else if (old_freq == mid2) { + if (priv->v2_busy_code >= V2_UP_900_TO_1800_BUSY && high > mid2 && + target_rate < high) + target_rate = high; + else if (priv->v2_busy_code > V2_DOWN_900_TO_450_BUSY && + priv->v2_busy_code < V2_UP_900_TO_1800_BUSY) + target_rate = mid2; + } else if (old_freq == high) { + if (priv->v2_busy_code > V2_DOWN_1800_TO_900_BUSY) + target_rate = high; + } + } + if (target_rate == old_freq) return 0; @@ -309,8 +395,15 @@ static int phytium_noc_get_dev_status(struct device *dev, struct devfreq_dev_sta phytium_nocfreq_restart_handshark_counters_v1(priv); } else { - stat->busy_time = phytium_nocfreq_get_peak_bw_v2(priv); - stat->total_time = 15 * DIV_ROUND_CLOSEST(priv->rate, priv->freq_table[0]); + u32 raw_busy = phytium_nocfreq_get_peak_bw_v2(priv); + u32 busy_code = raw_busy & V2_BUSY_CODE_MASK; + + if (busy_code > V2_BUSY_CODE_MAX) + busy_code = V2_BUSY_CODE_MAX; + + priv->v2_busy_code = busy_code; + stat->busy_time = busy_code; + stat->total_time = V2_BUSY_CODE_MAX; stat->current_frequency = priv->rate; } return 0; @@ -439,8 +532,8 @@ static int phytium_nocfreq_probe(struct platform_device *pdev) priv->ondemand_data.upthreshold = 80; priv->ondemand_data.downdifferential = 10; } else { - priv->ondemand_data.upthreshold = 95; - priv->ondemand_data.downdifferential = 5; + priv->ondemand_data.upthreshold = 80; + priv->ondemand_data.downdifferential = 10; } for (i = 0; i < max_state; ++i) { From 6bc7865d204ef0a1060b03d3deca260315947d29 Mon Sep 17 00:00:00 2001 From: Wentao Guan Date: Wed, 13 May 2026 14:52:54 +0800 Subject: [PATCH 33/38] deepin: config: arm64: enable phytium noc/dmu devfreq Signed-off-by: Wentao Guan --- arch/arm64/configs/deepin_arm64_desktop_defconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/configs/deepin_arm64_desktop_defconfig b/arch/arm64/configs/deepin_arm64_desktop_defconfig index 3de1e5f7c8c9c..e76edbbc314b8 100644 --- a/arch/arm64/configs/deepin_arm64_desktop_defconfig +++ b/arch/arm64/configs/deepin_arm64_desktop_defconfig @@ -4089,6 +4089,8 @@ CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y CONFIG_DEVFREQ_GOV_PERFORMANCE=m CONFIG_DEVFREQ_GOV_POWERSAVE=m CONFIG_DEVFREQ_GOV_USERSPACE=m +CONFIG_ARM_PHYTIUM_NOC_DEVFREQ=m +CONFIG_ARM_PHYTIUM_DMU_DEVFREQ=m CONFIG_PM_DEVFREQ_EVENT=y CONFIG_EXTCON_GPIO=m CONFIG_EXTCON_MAX14577=m From f8c9c3cc03552544d9ba05f4b129ba6c83394684 Mon Sep 17 00:00:00 2001 From: leoliu-oc Date: Thu, 14 May 2026 11:06:21 +0800 Subject: [PATCH 34/38] ALSA: HDA: Add Centaur HDMI Controller and Codec support zhaoxin inclusion category: feature -------------------- Add newer Centaur HD Audio PCI IDs, and HDMI codec vendor IDs. Signed-off-by: LeoLiu-oc --- sound/pci/hda/hda_intel.c | 15 +++++++++++++++ sound/pci/hda/patch_hdmi.c | 11 +++++++++++ 2 files changed, 26 insertions(+) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index d6587e42518b8..787cb6393fc28 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2846,6 +2846,21 @@ static const struct pci_device_id azx_ids[] = { { PCI_VDEVICE(VIA, 0x9170), .driver_data = AZX_DRIVER_GENERIC }, /* VIA GFX VT6122/VX11 */ { PCI_VDEVICE(VIA, 0x9140), .driver_data = AZX_DRIVER_GENERIC }, + { PCI_VDEVICE(VIA, 0x9141), + .driver_data = AZX_DRIVER_ZHAOXINHDMI | AZX_DCAPS_POSFIX_LPIB | + AZX_DCAPS_NO_MSI | AZX_DCAPS_NO_64BIT }, + { PCI_VDEVICE(VIA, 0x9142), + .driver_data = AZX_DRIVER_ZHAOXINHDMI | AZX_DCAPS_POSFIX_LPIB | + AZX_DCAPS_NO_MSI | AZX_DCAPS_NO_64BIT }, + { PCI_VDEVICE(VIA, 0x9144), + .driver_data = AZX_DRIVER_ZHAOXINHDMI | AZX_DCAPS_POSFIX_LPIB | + AZX_DCAPS_NO_MSI | AZX_DCAPS_NO_64BIT }, + { PCI_VDEVICE(VIA, 0x9145), + .driver_data = AZX_DRIVER_ZHAOXINHDMI | AZX_DCAPS_POSFIX_LPIB | + AZX_DCAPS_NO_MSI | AZX_DCAPS_NO_64BIT }, + { PCI_VDEVICE(VIA, 0x9146), + .driver_data = AZX_DRIVER_ZHAOXINHDMI | AZX_DCAPS_POSFIX_LPIB | + AZX_DCAPS_NO_MSI | AZX_DCAPS_NO_64BIT }, /* SIS966 */ { PCI_VDEVICE(SI, 0x7502), .driver_data = AZX_DRIVER_SIS }, /* ULI M5461 */ diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index d679154a6d8a8..aa8fc88cdf769 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -4641,6 +4641,17 @@ HDA_CODEC_ENTRY(0x11069f80, "VX900 HDMI/DP", patch_via_hdmi), HDA_CODEC_ENTRY(0x11069f81, "VX900 HDMI/DP", patch_via_hdmi), HDA_CODEC_ENTRY(0x11069f84, "VX11 HDMI/DP", patch_generic_hdmi), HDA_CODEC_ENTRY(0x11069f85, "VX11 HDMI/DP", patch_generic_hdmi), +HDA_CODEC_ENTRY(0x11069f86, "ZX-100S HDMI/DP", patch_gf_hdmi), +HDA_CODEC_ENTRY(0x11069f87, "ZX-100S HDMI/DP", patch_gf_hdmi), +HDA_CODEC_ENTRY(0x11069f88, "KX-5000 HDMI/DP", patch_gf_hdmi), +HDA_CODEC_ENTRY(0x11069f89, "KX-5000 HDMI/DP", patch_gf_hdmi), +HDA_CODEC_ENTRY(0x11069f8a, "KX-6000 HDMI/DP", patch_gf_hdmi), +HDA_CODEC_ENTRY(0x11069f8b, "KX-6000 HDMI/DP", patch_gf_hdmi), +HDA_CODEC_ENTRY(0x11069f8c, "KX-6000G HDMI/DP", patch_gf_hdmi), +HDA_CODEC_ENTRY(0x11069f8d, "KX-6000G HDMI/DP", patch_gf_hdmi), +HDA_CODEC_ENTRY(0x11069f8e, "KX-7000 HDMI/DP", patch_gf_hdmi), +HDA_CODEC_ENTRY(0x11069f8f, "KX-7000 HDMI/DP", patch_gf_hdmi), +HDA_CODEC_ENTRY(0x11069f90, "KX-7000 HDMI/DP", patch_gf_hdmi), HDA_CODEC_ENTRY(0x1d179f86, "ZX-100S HDMI/DP", patch_gf_hdmi), HDA_CODEC_ENTRY(0x1d179f87, "ZX-100S HDMI/DP", patch_gf_hdmi), HDA_CODEC_ENTRY(0x1d179f88, "KX-5000 HDMI/DP", patch_gf_hdmi), From 8cd3029fec6dafe430595d531500531509441c4a Mon Sep 17 00:00:00 2001 From: LeoLiu-oc Date: Thu, 14 May 2026 11:03:16 +0800 Subject: [PATCH 35/38] x86/microcode: Simplify Zhaoxin microcode patch saving This patch simplifies the save_microcode_patch() function in the Zhaoxin microcode driver by removing unnecessary variables and streamlining the memory allocation and copying logic. The changes include: - Remove the unused local variable 'mc'. - Directly handle memory allocation failure without redundant checks. - Simplify the memcpy and assignment to zhaoxin_ucode_patch. This improves code readability and reduces complexity without changing functionality. Signed-off-by: LeoLiu-oc --- arch/x86/kernel/cpu/microcode/zhaoxin.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/arch/x86/kernel/cpu/microcode/zhaoxin.c b/arch/x86/kernel/cpu/microcode/zhaoxin.c index ec00451f21bc5..6ad4c432332a3 100644 --- a/arch/x86/kernel/cpu/microcode/zhaoxin.c +++ b/arch/x86/kernel/cpu/microcode/zhaoxin.c @@ -176,7 +176,6 @@ static int zhaoxin_microcode_sanity_check(void *mc, bool print_err, int hdr_type static void save_microcode_patch(struct microcode_zhaoxin *patch) { unsigned int size = patch->hdr.total_size; - struct microcode_zhaoxin *mc = NULL; struct page *pg = NULL; void *dst = NULL; @@ -186,18 +185,14 @@ static void save_microcode_patch(struct microcode_zhaoxin *patch) * the memory allocation to this range. */ pg = alloc_pages(GFP_DMA32 | GFP_KERNEL, get_order(size)); - - if (pg) { - dst = page_address(pg); - memcpy(dst, patch, size); - mc = dst; - if (mc) { - zhaoxin_ucode_patch = mc; - return; - } + if (!pg) { + pr_err("Unable to allocate microcode memory size: %u\n", size); + return; } - pr_err("Unable to allocate microcode memory size: %u\n", size); + dst = page_address(pg); + memcpy(dst, patch, size); + zhaoxin_ucode_patch = dst; } static inline u32 From e26c6bc8f7362f6269a9de0fac2c2663891d6c04 Mon Sep 17 00:00:00 2001 From: leoliu-oc Date: Thu, 14 May 2026 11:14:10 +0800 Subject: [PATCH 36/38] ALSA: hda: Add support of Zhaoxin SB HDAC zhaoxin inclusion category: feature ------------------- Add some special initialization for Zhaoxin SB HDAC. Signed-off-by: LeoLiu-oc --- sound/pci/hda/hda_intel.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 787cb6393fc28..0775d3fdc11d3 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1646,7 +1646,8 @@ static int check_position_fix(struct azx *chip, int fix) } /* Check VIA/ATI HD Audio Controller exist */ - if (chip->driver_type == AZX_DRIVER_VIA) { + if (chip->driver_type == AZX_DRIVER_VIA || + chip->driver_type == AZX_DRIVER_ZHAOXIN) { dev_dbg(chip->card->dev, "Using VIACOMBO position fix\n"); return POS_FIX_VIACOMBO; } @@ -1800,7 +1801,8 @@ static void azx_check_snoop_available(struct azx *chip) snoop = true; if (azx_get_snoop_type(chip) == AZX_SNOOP_TYPE_NONE && - chip->driver_type == AZX_DRIVER_VIA) { + (chip->driver_type == AZX_DRIVER_VIA || + chip->driver_type == AZX_DRIVER_ZHAOXIN)) { /* force to non-snoop mode for a new VIA controller * when BIOS is set */ From a7805784171472e8f73f0809ed72bf893ba8cc38 Mon Sep 17 00:00:00 2001 From: leoliu-oc Date: Thu, 14 May 2026 10:43:09 +0800 Subject: [PATCH 37/38] x86/microcode: Serialize cpu startup during early updates zhaoxin inclusion category: feature -------------------- Due to hardware limitations, it is necessary to avoid situations where some cores are waiting to receive startup while other cores are updating microcode. Therefore, cpu startup must be serialized when microcode updates are required. Reviewed-by: Tony W. Wang Signed-off-by: Lyle Li Signed-off-by: LeoLiu-oc --- arch/x86/kernel/cpu/microcode/zhaoxin.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/kernel/cpu/microcode/zhaoxin.c b/arch/x86/kernel/cpu/microcode/zhaoxin.c index 6ad4c432332a3..5a925ef04a055 100644 --- a/arch/x86/kernel/cpu/microcode/zhaoxin.c +++ b/arch/x86/kernel/cpu/microcode/zhaoxin.c @@ -490,6 +490,7 @@ void __init load_ucode_zhaoxin_bsp(struct early_load_data *ed) if (uci.mc && apply_microcode_early(&uci) == UCODE_UPDATED) { zhaoxin_ucode_patch = UCODE_BSP_LOADED; + x86_cpuinit.parallel_bringup = false; ed->new_rev = uci.cpu_sig.rev; } else if (uci.mc) { pr_debug("%s: BSP CPU %d early update failed due to application failure\n", From 30ea88da519dfce9ac158e861986506413a42acf Mon Sep 17 00:00:00 2001 From: OrbisAI Security Date: Mon, 18 May 2026 11:09:32 +0530 Subject: [PATCH 38/38] security/lockdown: prevent buffer overflow in lockdown_read() Replace sprintf() with scnprintf() in lockdown_read() to prevent a potential stack buffer overflow when building the lockdown reasons string. sprintf() does not enforce bounds on bytes written, so if lockdown reason labels are longer than expected, the cumulative offset can exceed the fixed-size stack buffer, overwriting adjacent memory. Switch to scnprintf() which returns the number of bytes actually written (capped at buffer capacity), and add an early-break guard when offset reaches buffer capacity to prevent size_t underflow in the remaining-size calculation. Assisted-by: Claude:claude-opus-4-6-v1 Signed-off-by: OrbisAI Security --- security/lockdown/lockdown.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/security/lockdown/lockdown.c b/security/lockdown/lockdown.c index 67cc9839952f8..4b509af6d3439 100644 --- a/security/lockdown/lockdown.c +++ b/security/lockdown/lockdown.c @@ -99,10 +99,12 @@ static ssize_t lockdown_read(struct file *filp, char __user *buf, size_t count, if (lockdown_reasons[level]) { const char *label = lockdown_reasons[level]; + if (offset >= sizeof(temp) - 1) + break; if (kernel_locked_down == level) - offset += sprintf(temp+offset, "[%s] ", label); + offset += scnprintf(temp + offset, sizeof(temp) - (size_t)offset, "[%s] ", label); else - offset += sprintf(temp+offset, "%s ", label); + offset += scnprintf(temp + offset, sizeof(temp) - (size_t)offset, "%s ", label); } }