From c704f4a556a37ab2bca48a8e37085e9ec595c07e Mon Sep 17 00:00:00 2001 From: Cheng Yulai Date: Thu, 7 May 2026 17:37:05 +0800 Subject: [PATCH 01/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] =?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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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 801f0326d2a9d31f312d928214937ffa1ec422d3 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 28 Jan 2026 10:58:54 +0100 Subject: [PATCH 38/81] gpiolib: acpi: Fix potential out-of-boundary left shift mainline inclusion from mainline-v6.19-rc8 commit e64d1cb21a1c6ecd51bc1c94c83f6fc656f7c94d category: bugfix bugzilla: https://atomgit.com/openeuler/kernel/issues/9158 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=e64d1cb21a1c6ecd51bc1c94c83f6fc656f7c94d -------------------------------- GPIO Address Space handler gets a pointer to the in or out value. This value is supposed to be at least 64-bit, but it's not limited to be exactly 64-bit. When ACPI tables are being parsed, for the bigger Connection():s ACPICA creates a Buffer instead of regular Integer object. The Buffer exists as long as Namespace holds the certain Connection(). Hence we can access the necessary bits without worrying. On the other hand, the left shift, used in the code, is limited by 31 (on 32-bit platforms) and otherwise considered to be Undefined Behaviour. Also the code uses only the first 64-bit word for the value, and anything bigger than 63 will be also subject to UB. Fix all this by modifying the code to correctly set or clear the respective bit in the bitmap constructed of 64-bit words. Fixes: 59084c564c41 ("gpiolib: acpi: use BIT_ULL() for u64 mask in address space handler") Fixes: 2c4d00cb8fc5 ("gpiolib: acpi: Use BIT() macro to increase readability") Cc: stable@vger.kernel.org Reviewed-by: Mika Westerberg Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20260128095918.4157491-1-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Wentao Guan --- drivers/gpio/gpiolib-acpi.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 4d4757eac9e5f..4841766ca8b68 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -1173,6 +1173,7 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, unsigned int pin = agpio->pin_table[i]; struct acpi_gpio_connection *conn; struct gpio_desc *desc; + u16 word, shift; bool found; mutex_lock(&achip->conn_lock); @@ -1227,10 +1228,22 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, mutex_unlock(&achip->conn_lock); - if (function == ACPI_WRITE) - gpiod_set_raw_value_cansleep(desc, !!(*value & BIT_ULL(i))); - else - *value |= (u64)gpiod_get_raw_value_cansleep(desc) << i; + /* + * For the cases when OperationRegion() consists of more than + * 64 bits calculate the word and bit shift to use that one to + * access the value. + */ + word = i / 64; + shift = i % 64; + + if (function == ACPI_WRITE) { + gpiod_set_raw_value_cansleep(desc, value[word] & BIT_ULL(shift)); + } else { + if (gpiod_get_raw_value_cansleep(desc)) + value[word] |= BIT_ULL(shift); + else + value[word] &= ~BIT_ULL(shift); + } } out: From d203c1fb87d5fda17e08291c7c70d904cfd6bc85 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 16 Jun 2025 13:51:47 -0400 Subject: [PATCH 39/81] xfs: remove unused trace event xfs_attr_remove_iter_return mainline inclusion from mainline-v6.17-rc1 category: bugfix When the function xfs_attri_remove_iter was removed, it did not remove the trace event that it called. As a trace event can take up to 5K of memory for text and meta data regardless of if it is used or not, remove this unused trace event. Fixes: 59782a236b62 ("xfs: remove xfs_attri_remove_iter") Reviewed-by: Christoph Hellwig Signed-off-by: Steven Rostedt (Google) Signed-off-by: Carlos Maiolino (cherry picked from commit 091e9451d0bdd5d98ad4fcbd55f7c4554bf9e60f) Signed-off-by: Wentao Guan --- fs/xfs/xfs_trace.h | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index 5c481d0df8518..cf256bd77bdb3 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -4418,7 +4418,6 @@ DEFINE_DAS_STATE_EVENT(xfs_attr_sf_addname_return); DEFINE_DAS_STATE_EVENT(xfs_attr_set_iter_return); DEFINE_DAS_STATE_EVENT(xfs_attr_leaf_addname_return); DEFINE_DAS_STATE_EVENT(xfs_attr_node_addname_return); -DEFINE_DAS_STATE_EVENT(xfs_attr_remove_iter_return); DEFINE_DAS_STATE_EVENT(xfs_attr_rmtval_alloc); DEFINE_DAS_STATE_EVENT(xfs_attr_rmtval_remove_return); DEFINE_DAS_STATE_EVENT(xfs_attr_defer_add); From 3e15f0e9f0927390306447918762fbae9122008a Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 16 Jun 2025 13:51:48 -0400 Subject: [PATCH 40/81] xfs: remove unused event xlog_iclog_want_sync mainline inclusion from mainline-v6.17-rc1 category: bugfix The trace event xlog_iclog_want_sync was added but never used. As trace events can take up around 5K of memory in text and meta data regardless if they are used or not, remove this unused event. Fixes: 956f6daa84bf ("xfs: add iclog state trace events") Reviewed-by: Christoph Hellwig Signed-off-by: Steven Rostedt (Google) Signed-off-by: Carlos Maiolino (cherry picked from commit 32177ab8ba5f4002173f4ba2cf595aabf0c6c008) Signed-off-by: Wentao Guan --- fs/xfs/xfs_trace.h | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index cf256bd77bdb3..26c3fa3d393c7 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -4369,7 +4369,6 @@ DEFINE_ICLOG_EVENT(xlog_iclog_switch); DEFINE_ICLOG_EVENT(xlog_iclog_sync); DEFINE_ICLOG_EVENT(xlog_iclog_syncing); DEFINE_ICLOG_EVENT(xlog_iclog_sync_done); -DEFINE_ICLOG_EVENT(xlog_iclog_want_sync); DEFINE_ICLOG_EVENT(xlog_iclog_wait_on); DEFINE_ICLOG_EVENT(xlog_iclog_write); From 77643ffd4dcb28c199ee4fca410e098cd5fc296b Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 16 Jun 2025 13:51:49 -0400 Subject: [PATCH 41/81] xfs: remove unused event xfs_ioctl_clone mainline inclusion from mainline-v6.17-rc1 category: bugfix The trace event xfs_ioctl_clone was added but never used. As trace events can take up to 5K of memory in text and meta data regardless if they are used or not, remove the unused trace event. Fixes: 53aa1c34f4eb ("xfs: define tracepoints for reflink activities") Reviewed-by: Christoph Hellwig Signed-off-by: Steven Rostedt (Google) Signed-off-by: Carlos Maiolino (cherry picked from commit 6f7080bd932f63d3390cefa8571def5d49b82068) Signed-off-by: Wentao Guan --- fs/xfs/xfs_trace.h | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index 26c3fa3d393c7..451786b329926 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -3545,32 +3545,6 @@ DEFINE_INODE_IREC_EVENT(xfs_reflink_remap_extent_dest); DEFINE_DOUBLE_IO_EVENT(xfs_reflink_compare_extents); DEFINE_INODE_ERROR_EVENT(xfs_reflink_compare_extents_error); -/* ioctl tracepoints */ -TRACE_EVENT(xfs_ioctl_clone, - TP_PROTO(struct inode *src, struct inode *dest), - TP_ARGS(src, dest), - TP_STRUCT__entry( - __field(dev_t, dev) - __field(unsigned long, src_ino) - __field(loff_t, src_isize) - __field(unsigned long, dest_ino) - __field(loff_t, dest_isize) - ), - TP_fast_assign( - __entry->dev = src->i_sb->s_dev; - __entry->src_ino = src->i_ino; - __entry->src_isize = i_size_read(src); - __entry->dest_ino = dest->i_ino; - __entry->dest_isize = i_size_read(dest); - ), - TP_printk("dev %d:%d ino 0x%lx isize 0x%llx -> ino 0x%lx isize 0x%llx", - MAJOR(__entry->dev), MINOR(__entry->dev), - __entry->src_ino, - __entry->src_isize, - __entry->dest_ino, - __entry->dest_isize) -); - /* unshare tracepoints */ DEFINE_SIMPLE_IO_EVENT(xfs_reflink_unshare); DEFINE_INODE_ERROR_EVENT(xfs_reflink_unshare_error); From fdc531b4c35eb28e29a66fdde92415094c1351ce Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 16 Jun 2025 13:51:50 -0400 Subject: [PATCH 42/81] xfs: remove unused xfs_reflink_compare_extents events mainline inclusion from mainline-v6.17-rc1 category: bugfix When the clone/dedupe_file_rang common functions were refactored, it removed the calls to the xfs_reflink_compare_extents and xfs_reflink_compare_extents_error events. As each event can take up to 5K in memory for text and meta data regardless if they are used or not, they should not be created if they are not used. Remove these unused events. Fixes: 876bec6f9bbf ("vfs: refactor clone/dedupe_file_range common functions") Reviewed-by: Christoph Hellwig Signed-off-by: Steven Rostedt (Google) Signed-off-by: Carlos Maiolino (cherry picked from commit 8c54845c3a02b40bb76916a94a50267cded68d2b) Signed-off-by: Wentao Guan --- fs/xfs/xfs_trace.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index 451786b329926..4da77b31cf8e9 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -3541,10 +3541,6 @@ DEFINE_INODE_ERROR_EVENT(xfs_reflink_remap_extent_error); DEFINE_INODE_IREC_EVENT(xfs_reflink_remap_extent_src); DEFINE_INODE_IREC_EVENT(xfs_reflink_remap_extent_dest); -/* dedupe tracepoints */ -DEFINE_DOUBLE_IO_EVENT(xfs_reflink_compare_extents); -DEFINE_INODE_ERROR_EVENT(xfs_reflink_compare_extents_error); - /* unshare tracepoints */ DEFINE_SIMPLE_IO_EVENT(xfs_reflink_unshare); DEFINE_INODE_ERROR_EVENT(xfs_reflink_unshare_error); From 8b15024033f23d3bbd7f7cd9c8b137bebcbfbd69 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 16 Jun 2025 13:51:51 -0400 Subject: [PATCH 43/81] xfs: remove unused trace event xfs_attr_rmtval_set mainline inclusion from mainline-v6.17-rc1 category: bugfix When the function xfs_attr_rmtval_set() was removed, the call to the corresponding trace event was also removed but the trace event itself was not. As trace events can take up to 5K of memory in text and meta data regardless if they are used or not they should not be created when not used. Remove the unused trace event. Fixes: 0e6acf29db6f ("xfs: Remove xfs_attr_rmtval_set") Reviewed-by: Christoph Hellwig Signed-off-by: Steven Rostedt (Google) Signed-off-by: Carlos Maiolino (cherry picked from commit b3b5015d3454ae347644c99ec94bec7cba664716) Signed-off-by: Wentao Guan --- fs/xfs/xfs_trace.h | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index 4da77b31cf8e9..cea3039d4256f 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -2085,7 +2085,6 @@ DEFINE_ATTR_EVENT(xfs_attr_fillstate); DEFINE_ATTR_EVENT(xfs_attr_refillstate); DEFINE_ATTR_EVENT(xfs_attr_rmtval_get); -DEFINE_ATTR_EVENT(xfs_attr_rmtval_set); #define DEFINE_DA_EVENT(name) \ DEFINE_EVENT(xfs_da_class, name, \ From d507d85daf15547ba988400002d763fb19a2eeb1 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 16 Jun 2025 13:51:52 -0400 Subject: [PATCH 44/81] xfs: remove unused xfs_attr events mainline inclusion from mainline-v6.17-rc1 category: bugfix Trace events can take up to 5K in memory for text and meta data per event regardless if they are used or not, so they should not be defined when not used. The events xfs_attr_fillstate and xfs_attr_refillstate are only called in code that is #ifdef out and exists only for future reference. Remove these unused events. If the code is needed again, then git history can recover what the events were. Suggested-by: Christoph Hellwig Fixes: 59782a236b622 ("xfs: remove xfs_attri_remove_iter") Signed-off-by: Steven Rostedt (Google) Signed-off-by: Carlos Maiolino (cherry picked from commit b54480c3b10d4f617e20c51f749015729db74228) Signed-off-by: Wentao Guan --- fs/xfs/xfs_trace.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index cea3039d4256f..b82aaafc205b1 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -2081,9 +2081,6 @@ DEFINE_ATTR_EVENT(xfs_attr_node_get); DEFINE_ATTR_EVENT(xfs_attr_node_replace); DEFINE_ATTR_EVENT(xfs_attr_node_removename); -DEFINE_ATTR_EVENT(xfs_attr_fillstate); -DEFINE_ATTR_EVENT(xfs_attr_refillstate); - DEFINE_ATTR_EVENT(xfs_attr_rmtval_get); #define DEFINE_DA_EVENT(name) \ From 6af7b072a2b8f586eba77a0dfdd49985760df12b Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 16 Jun 2025 13:51:53 -0400 Subject: [PATCH 45/81] xfs: remove unused event xfs_attr_node_removename mainline inclusion from mainline-v6.17-rc1 category: bugfix When xfs_attri_remove_iter() was removed, so was the call to the trace event xfs_attr_node_removename. As trace events can take up to 5K in memory for text and meta data regardless if they are used or not, they should not be created when unused. Remove the unused event. Fixes: 59782a236b622 ("xfs: remove xfs_attri_remove_iter") Reviewed-by: Christoph Hellwig Signed-off-by: Steven Rostedt (Google) Signed-off-by: Carlos Maiolino (cherry picked from commit ea26bbc7795b6ef49134231bdfc34569ed07f144) Signed-off-by: Wentao Guan --- fs/xfs/xfs_trace.h | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index b82aaafc205b1..e4a3abd8f5220 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -2079,7 +2079,6 @@ DEFINE_ATTR_EVENT(xfs_attr_leaf_toosmall); DEFINE_ATTR_EVENT(xfs_attr_node_addname); DEFINE_ATTR_EVENT(xfs_attr_node_get); DEFINE_ATTR_EVENT(xfs_attr_node_replace); -DEFINE_ATTR_EVENT(xfs_attr_node_removename); DEFINE_ATTR_EVENT(xfs_attr_rmtval_get); From fc3cb7d8eb34942df08f7493b54e4dd5ae9ef23b Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 16 Jun 2025 13:51:54 -0400 Subject: [PATCH 46/81] xfs: remove unused event xfs_alloc_near_error mainline inclusion from mainline-v6.17-rc1 category: bugfix Trace events take up to 5K of memory in text and meta data regardless if they are used or not. The call to the event xfs_alloc_near_error was removed when the cursor data structure allocation was introduced. Remove it as it is no longer used and is just wasting memory. Fixes: f5e7dbea1e3e ("xfs: introduce allocation cursor data structure") Reviewed-by: Christoph Hellwig Signed-off-by: Steven Rostedt (Google) Signed-off-by: Carlos Maiolino (cherry picked from commit 237f8e885136a073b2a1a70768aa6f538fa634bd) Signed-off-by: Wentao Guan --- fs/xfs/xfs_trace.h | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index e4a3abd8f5220..dd1e0076d302a 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -1882,7 +1882,6 @@ DEFINE_ALLOC_EVENT(xfs_alloc_cur_right); DEFINE_ALLOC_EVENT(xfs_alloc_cur_left); DEFINE_ALLOC_EVENT(xfs_alloc_cur_lookup); DEFINE_ALLOC_EVENT(xfs_alloc_cur_lookup_done); -DEFINE_ALLOC_EVENT(xfs_alloc_near_error); DEFINE_ALLOC_EVENT(xfs_alloc_near_noentry); DEFINE_ALLOC_EVENT(xfs_alloc_near_busy); DEFINE_ALLOC_EVENT(xfs_alloc_size_neither); From 24b10282553b841394e02cc35add98b9015ef25b Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 16 Jun 2025 13:51:55 -0400 Subject: [PATCH 47/81] xfs: remove unused event xfs_alloc_near_nominleft mainline inclusion from mainline-v6.17-rc1 category: bugfix When the function xfs_alloc_space_available() was restructured, it removed the only calls to the trace event xfs_alloc_near_nominleft. As trace events take up to 5K of memory for text and meta data for each event, they should not be created when not used. Remove this unused event. Fixes: 54fee133ad59 ("xfs: adjust allocation length in xfs_alloc_space_available") Reviewed-by: Christoph Hellwig Signed-off-by: Steven Rostedt (Google) Signed-off-by: Carlos Maiolino (cherry picked from commit f1100605590a13d362efe20f734d882305eb6e64) Signed-off-by: Wentao Guan --- fs/xfs/xfs_trace.h | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index dd1e0076d302a..72ce8a779e692 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -1875,7 +1875,6 @@ DEFINE_EVENT(xfs_alloc_class, name, \ DEFINE_ALLOC_EVENT(xfs_alloc_exact_done); DEFINE_ALLOC_EVENT(xfs_alloc_exact_notfound); DEFINE_ALLOC_EVENT(xfs_alloc_exact_error); -DEFINE_ALLOC_EVENT(xfs_alloc_near_nominleft); DEFINE_ALLOC_EVENT(xfs_alloc_near_first); DEFINE_ALLOC_EVENT(xfs_alloc_cur); DEFINE_ALLOC_EVENT(xfs_alloc_cur_right); From c7c8325c80ffc1dd8889d51e00de75d4f3479f31 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 16 Jun 2025 13:51:56 -0400 Subject: [PATCH 48/81] xfs: remove unused event xfs_pagecache_inval mainline incluion from mainline-v6.17-rc1 category: bugfix When the function xfs_flushinval_pages() was removed, it removed the only caller to the trace event xfs_pagecache_inval. As trace events can take up to 5K of memory in text and meta data each regardless if they are used or not, they should not be created when unused. Remove the unused event. Fixes: fb59581404ab ("xfs: remove xfs_flushinval_pages") Reviewed-by: Christoph Hellwig Signed-off-by: Steven Rostedt (Google) Signed-off-by: Carlos Maiolino (cherry picked from commit 88fd451594a6b42f37aa2b3c3647b5d8973e2e5f) Signed-off-by: Wentao Guan --- fs/xfs/xfs_trace.h | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index 72ce8a779e692..3fe6a7dc0695f 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -1583,31 +1583,6 @@ DEFINE_EVENT(xfs_itrunc_class, name, \ DEFINE_ITRUNC_EVENT(xfs_itruncate_extents_start); DEFINE_ITRUNC_EVENT(xfs_itruncate_extents_end); -TRACE_EVENT(xfs_pagecache_inval, - TP_PROTO(struct xfs_inode *ip, xfs_off_t start, xfs_off_t finish), - TP_ARGS(ip, start, finish), - TP_STRUCT__entry( - __field(dev_t, dev) - __field(xfs_ino_t, ino) - __field(xfs_fsize_t, size) - __field(xfs_off_t, start) - __field(xfs_off_t, finish) - ), - TP_fast_assign( - __entry->dev = VFS_I(ip)->i_sb->s_dev; - __entry->ino = ip->i_ino; - __entry->size = ip->i_disk_size; - __entry->start = start; - __entry->finish = finish; - ), - TP_printk("dev %d:%d ino 0x%llx disize 0x%llx start 0x%llx finish 0x%llx", - MAJOR(__entry->dev), MINOR(__entry->dev), - __entry->ino, - __entry->size, - __entry->start, - __entry->finish) -); - TRACE_EVENT(xfs_bunmap, TP_PROTO(struct xfs_inode *ip, xfs_fileoff_t fileoff, xfs_filblks_t len, int flags, unsigned long caller_ip), From 5b717e86a598991a31b97ad8d62894bf30559974 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 16 Jun 2025 13:51:57 -0400 Subject: [PATCH 49/81] xfs: remove usused xfs_end_io_direct events mainline inclusion from mainline-v6.17-rc1 category: bugfix When the use of iomap_dio_rw was added, the calls to the trace events xfs_end_io_direct_unwritten and xfs_end_io_direct_append were removed but those trace events were not. As trace events can take up to 5K in memory for text and meta data regardless if they are used or not, they should not be created when not used. Remove the unused events. Fixes: acdda3aae146 ("xfs: use iomap_dio_rw") Reviewed-by: Christoph Hellwig Signed-off-by: Steven Rostedt (Google) Signed-off-by: Carlos Maiolino (cherry picked from commit 9a8a536fe5a803d5323cb8d830db5551e78a5a22) Signed-off-by: Wentao Guan --- fs/xfs/xfs_trace.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index 3fe6a7dc0695f..ba2856522a56e 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -1550,8 +1550,6 @@ DEFINE_SIMPLE_IO_EVENT(xfs_unwritten_convert); DEFINE_SIMPLE_IO_EVENT(xfs_setfilesize); DEFINE_SIMPLE_IO_EVENT(xfs_zero_eof); DEFINE_SIMPLE_IO_EVENT(xfs_end_io_direct_write); -DEFINE_SIMPLE_IO_EVENT(xfs_end_io_direct_write_unwritten); -DEFINE_SIMPLE_IO_EVENT(xfs_end_io_direct_write_append); DEFINE_SIMPLE_IO_EVENT(xfs_file_splice_read); DECLARE_EVENT_CLASS(xfs_itrunc_class, From a2681655f633a607d2d4b5cf64958c9d95a75b19 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 16 Jun 2025 13:51:58 -0400 Subject: [PATCH 50/81] xfs: only create event xfs_file_compat_ioctl when CONFIG_COMPAT is configure mainline inclusion from mainline-v6.17-rc1 category: bugfix The trace event xfs_file_compat_ioctl is only used when CONFIG_COMPAT is configured in the build. As trace events can take up to 5K in memory for text and meta data regardless if they are used, they should not be created when unused. Add #ifdef CONFIG_COMPAT around the event so that it is only created when that is configured. Fixes: cca28fb83d9e6 ("xfs: split xfs_itrace_entry") Reviewed-by: Christoph Hellwig Signed-off-by: Steven Rostedt (Google) Signed-off-by: Carlos Maiolino (cherry picked from commit 31b98ef2403fc38ba3bbe7663bdd034bfb0456c7) Signed-off-by: Wentao Guan --- fs/xfs/xfs_trace.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index ba2856522a56e..109a5620a5cb1 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -774,7 +774,9 @@ DEFINE_INODE_EVENT(xfs_get_acl); #endif DEFINE_INODE_EVENT(xfs_vm_bmap); DEFINE_INODE_EVENT(xfs_file_ioctl); +#ifdef CONFIG_COMPAT DEFINE_INODE_EVENT(xfs_file_compat_ioctl); +#endif DEFINE_INODE_EVENT(xfs_ioctl_setattr); DEFINE_INODE_EVENT(xfs_dir_fsync); DEFINE_INODE_EVENT(xfs_file_fsync); From fdf7b2a75e3a429243eb67205e9872ba178a4627 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 22 Jul 2025 16:19:11 -0400 Subject: [PATCH 51/81] xfs: remove unused trace event xfs_reflink_cow_enospc mainline inclusion from mainline-v6.17-rc1 categroy: bugfix The call to the event xfs_reflink_cow_enospc was removed when the COW handling was merged into xfs_file_iomap_begin_delay, but the trace event itself was not. Remove it. Fixes: db46e604adf8 ("xfs: merge COW handling into xfs_file_iomap_begin_delay") Signed-off-by: Steven Rostedt (Google) Reviewed-by: Christoph Hellwig Reviewed-by: Darrick J. Wong Signed-off-by: Carlos Maiolino (cherry picked from commit 75fe259ff7f67d5072f9b5b282be75e159e5e6c7) Signed-off-by: Wentao Guan --- fs/xfs/xfs_trace.h | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index 109a5620a5cb1..7f68005d52c7f 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -3516,7 +3516,6 @@ DEFINE_INODE_ERROR_EVENT(xfs_reflink_unshare_error); /* copy on write */ DEFINE_INODE_IREC_EVENT(xfs_reflink_trim_around_shared); DEFINE_INODE_IREC_EVENT(xfs_reflink_cow_found); -DEFINE_INODE_IREC_EVENT(xfs_reflink_cow_enospc); DEFINE_INODE_IREC_EVENT(xfs_reflink_convert_cow); DEFINE_SIMPLE_IO_EVENT(xfs_reflink_cancel_cow_range); From be86fc51d92c73a7fd58596182cad86640fcaab2 Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Thu, 7 May 2026 20:53:53 +0800 Subject: [PATCH 52/81] Revert drivers/perf:phytium: Added some functional improvements for PCIe PMU. This reverts commit c019dd510742274e7faadf2ccf934902638c7199. Reason for revert: Signed-off-by: Zhang Fuxiang Signed-off-by: Tan Rui Signed-off-by: Hu Xianghua --- drivers/perf/phytium/phytium_pcie_pmu.c | 409 ++++++++++-------------- 1 file changed, 169 insertions(+), 240 deletions(-) diff --git a/drivers/perf/phytium/phytium_pcie_pmu.c b/drivers/perf/phytium/phytium_pcie_pmu.c index 9172c152756b2..b8aaee0048f7c 100644 --- a/drivers/perf/phytium/phytium_pcie_pmu.c +++ b/drivers/perf/phytium/phytium_pcie_pmu.c @@ -23,7 +23,6 @@ #include #include #include -#include #if IS_ENABLED(CONFIG_ARM || CONFIG_ARM64) #include @@ -33,72 +32,51 @@ #undef pr_fmt #define pr_fmt(fmt) "phytium_pcie_pmu: " fmt -#define PHYTIUM_PCIE_MAX_COUNTERS 18 -#define PCIE_PERF_DRIVER_VERSION "1.3.0" - -#define PCIE_START_TIMER 0x000 -#define PCIE_STOP_TIMER 0x004 -#define PCIE_CLEAR_EVENT 0x008 - -#define PCIE_EVENT_CYCLES 0x0e4 -#define PCIE_TPOINT_END_L 0x0e4 -#define PCIE_TPOINT_END_H 0x0e8 -#define PCIE_STATE_STOP 0x0ec - -#define PCIE_EVENT_AW 0x100 -#define PCIE_EVENT_W_LAST 0x104 -#define PCIE_EVENT_B 0x108 -#define PCIE_EVENT_AR 0x10c -#define PCIE_EVENT_R_LAST 0x110 -#define PCIE_EVENT_R_FULL 0x114 -#define PCIE_EVENT_R_ERR 0x118 -#define PCIE_EVENT_W_ERR 0x11c -#define PCIE_EVENT_DELAY_RD 0x120 -#define PCIE_EVENT_DELAY_WR 0x124 -#define PCIE_EVENT_RD_MAX 0x128 -#define PCIE_EVENT_RD_MIN 0x12c -#define PCIE_EVENT_WR_MAX 0x130 -#define PCIE_EVENT_WR_MIN 0x134 - -#define PCIE_EVENT_W_DATA 0x200 -#define PCIE_W_DATA_BASE 0x200 - -#define PCIE_EVENT_RDELAY_TIME 0x300 -#define PCIE_RDELAY_TIME_BASE 0x300 - -#define PCIE_EVENT_WDELAY_TIME 0x700 -#define PCIE_WDELAY_TIME_BASE 0x700 - -#define PCIE_DATA_WIDTH 0xe04 - -#define PCIE_PMU_OFL_STOP_TYPE_VAL 0x10 - -#define SYS_AIDR_EL1 sys_reg(3, 1, 0, 0, 7) -#define SOC_ID_PS230XX 0x8 -#define SOC_ID_PS240XX 0x6 -#define MIDR_PSXX 0x700f8620 - -#define to_phytium_pcie_pmu(p) (container_of(p, struct phytium_pcie_pmu, pmu)) - -enum { - PS230XX = 0x1, - PS240XX = 0x2, -}; +#define PCIE_PERF_DRIVER_VERSION "1.2.1" -static inline int phytium_socs_type(void) -{ - unsigned int soc_id, cpu_id; +#define PHYTIUM_PCIE_MAX_COUNTERS 18 - soc_id = read_sysreg_s(SYS_AIDR_EL1); - cpu_id = read_cpuid_id(); +#define PCIE_START_TIMER 0x000 +#define PCIE_STOP_TIMER 0x004 +#define PCIE_CLEAR_EVENT 0x008 +#define PCIE_SET_TIMER_L 0x00c +#define PCIE_SET_TIMER_H 0x010 +#define PCIE_TRIG_MODE 0x014 + +#define PCIE_NOW_STATE 0x0e0 +#define PCIE_EVENT_CYCLES 0x0e4 +#define PCIE_TPOINT_END_L 0x0e4 +#define PCIE_TPOINT_END_H 0x0e8 +#define PCIE_STATE_STOP 0x0ec + +#define PCIE_EVENT_AW 0x100 +#define PCIE_EVENT_W_LAST 0x104 +#define PCIE_EVENT_B 0x108 +#define PCIE_EVENT_AR 0x10c +#define PCIE_EVENT_R_LAST 0x110 +#define PCIE_EVENT_R_FULL 0x114 +#define PCIE_EVENT_R_ERR 0x118 +#define PCIE_EVENT_W_ERR 0x11c +#define PCIE_EVENT_DELAY_RD 0x120 +#define PCIE_EVENT_DELAY_WR 0x124 +#define PCIE_EVENT_RD_MAX 0x128 +#define PCIE_EVENT_RD_MIN 0x12c +#define PCIE_EVENT_WR_MAX 0x130 +#define PCIE_EVENT_WR_MIN 0x134 + +#define PCIE_EVENT_W_DATA 0x200 +#define PCIE_W_DATA_BASE 0x200 + +#define PCIE_EVENT_RDELAY_TIME 0x300 +#define PCIE_RDELAY_TIME_BASE 0x300 + +#define PCIE_EVENT_WDELAY_TIME 0x700 +#define PCIE_WDELAY_TIME_BASE 0x700 + +#define PCIE_CLK_FRE 0xe00 +#define PCIE_DATA_WIDTH 0xe04 - if ((soc_id == SOC_ID_PS230XX) && (cpu_id == MIDR_PSXX)) - return PS230XX; - else if ((soc_id == SOC_ID_PS240XX) && (cpu_id == MIDR_PSXX)) - return PS240XX; - else - return 0; -} +#define to_phytium_pcie_pmu(p) (container_of(p, struct phytium_pcie_pmu, pmu)) static int phytium_pcie_pmu_hp_state; @@ -110,21 +88,18 @@ struct phytium_pcie_pmu_hwevents { struct phytium_pcie_pmu { struct device *dev; void __iomem *base; - void __iomem *cfg_base; + void __iomem *csr_base; void __iomem *irq_reg; struct pmu pmu; struct phytium_pcie_pmu_hwevents pmu_events; u32 die_id; - u32 pcie_id; u32 pmu_id; int on_cpu; int irq; - int irq_bit; struct hlist_node node; int ctrler_id; int real_ctrler; u32 clk_bits; - u32 soc_version; }; #define GET_PCIE_EVENTID(hwc) (hwc->config_base & 0x1F) @@ -187,6 +162,7 @@ static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, static struct attribute *phytium_pcie_pmu_format_attr[] = { PHYTIUM_PCIE_PMU_FORMAT_ATTR(event, "config:0-4"), PHYTIUM_PCIE_PMU_FORMAT_ATTR(ctrler, "config:8-10"), + PHYTIUM_PCIE_PMU_FORMAT_ATTR(timer, "config1:0-31"), NULL, }; @@ -245,6 +221,11 @@ static u32 phytium_pcie_pmu_get_event_ctrler(struct perf_event *event) return FIELD_GET(GENMASK(10, 8), event->attr.config); } +static u32 phytium_pcie_pmu_get_event_timer(struct perf_event *event) +{ + return FIELD_GET(GENMASK(31, 0), event->attr.config1); +} + static u64 phytium_pcie_pmu_read_counter(struct phytium_pcie_pmu *pcie_pmu, struct hw_perf_event *hwc) { @@ -254,16 +235,12 @@ static u64 phytium_pcie_pmu_read_counter(struct phytium_pcie_pmu *pcie_pmu, u64 val64 = 0; int i; u32 counter_offset = pcie_counter_reg_offset[idx]; - u32 rdelay_num = 127; if (!EVENT_VALID(idx)) { dev_err(pcie_pmu->dev, "Unsupported event index:%d!\n", idx); return 0; } - if (pcie_pmu->soc_version == PS240XX && pcie_pmu->pmu_id == 3) - rdelay_num = 63; - switch (idx) { case 0: cycle_l = readl(pcie_pmu->base + counter_offset); @@ -278,7 +255,7 @@ static u64 phytium_pcie_pmu_read_counter(struct phytium_pcie_pmu *pcie_pmu, } break; case 16: - for (i = 0; i <= rdelay_num; i = i + 2) { + for (i = 0; i <= 127; i = i + 2) { rdelay_l = readl(pcie_pmu->base + counter_offset + 4 * i); rdelay_h = readl(pcie_pmu->base + counter_offset + @@ -306,40 +283,38 @@ static void phytium_pcie_pmu_enable_clk(struct phytium_pcie_pmu *pcie_pmu) { u32 val; - val = readl(pcie_pmu->cfg_base); + val = readl(pcie_pmu->csr_base); val |= (pcie_pmu->clk_bits); - writel(val, pcie_pmu->cfg_base); + writel(val, pcie_pmu->csr_base); } static void phytium_pcie_pmu_disable_clk(struct phytium_pcie_pmu *pcie_pmu) { u32 val; - val = readl(pcie_pmu->cfg_base); + val = readl(pcie_pmu->csr_base); val &= ~(pcie_pmu->clk_bits); - writel(val, pcie_pmu->cfg_base); + writel(val, pcie_pmu->csr_base); } static void phytium_pcie_pmu_select_ctrler(struct phytium_pcie_pmu *pcie_pmu) { - u32 val, offset; - u32 mask = 0xfffffffc; + u32 val, offset = 0; - if (pcie_pmu->soc_version == PS230XX) { - if (pcie_pmu->pmu_id == 2) { - mask = 0xffffffcf; - offset = 0x0; - } else - offset = 0xc; + if (pcie_pmu->pmu_id != 2) + offset = 0xc; + + val = readl(pcie_pmu->csr_base + offset); + + if (pcie_pmu->pmu_id == 2) { + val &= 0xffffffcf; + val |= pcie_pmu->real_ctrler; } else { - offset = 0x170; + val &= 0xfffffffc; + val |= pcie_pmu->real_ctrler; } - val = readl(pcie_pmu->cfg_base + offset); - val &= mask; - val |= pcie_pmu->real_ctrler; - writel(val, pcie_pmu->cfg_base + offset); - + writel(val, pcie_pmu->csr_base + offset); } static void @@ -360,6 +335,29 @@ phytium_pcie_pmu_stop_all_counters(struct phytium_pcie_pmu *pcie_pmu) writel(0x1, pcie_pmu->base + PCIE_STOP_TIMER); } +static void phytium_pcie_pmu_set_timer(struct phytium_pcie_pmu *pcie_pmu, + u32 th_val) +{ + u32 val; + + val = readl(pcie_pmu->base + PCIE_SET_TIMER_L); + val = readl(pcie_pmu->base + PCIE_SET_TIMER_H); + + writel(th_val, pcie_pmu->base + PCIE_SET_TIMER_L); + writel(0, pcie_pmu->base + PCIE_SET_TIMER_H); +} + +static void phytium_pcie_pmu_reset_timer(struct phytium_pcie_pmu *pcie_pmu) +{ + u32 val; + + val = readl(pcie_pmu->base + PCIE_SET_TIMER_L); + val = readl(pcie_pmu->base + PCIE_SET_TIMER_H); + + writel(0xFFFFFFFF, pcie_pmu->base + PCIE_SET_TIMER_L); + writel(0xFFFFFFFF, pcie_pmu->base + PCIE_SET_TIMER_H); +} + static unsigned long phytium_pcie_pmu_get_stop_state(struct phytium_pcie_pmu *pcie_pmu) { @@ -409,7 +407,7 @@ int phytium_pcie_pmu_event_init(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; struct phytium_pcie_pmu *pcie_pmu; - u32 event_ctrler; + u32 event_ctrler, event_timer; if (event->attr.type != event->pmu->type) return -ENOENT; @@ -430,83 +428,57 @@ int phytium_pcie_pmu_event_init(struct perf_event *event) if (pcie_pmu->on_cpu == -1) return -EINVAL; - if (pcie_pmu->soc_version == PS240XX) { - event_ctrler = phytium_pcie_pmu_get_event_ctrler(event); - if (pcie_pmu->pmu_id == 2) { - if (event_ctrler == 0) - event_ctrler = 2; - else if ((event_ctrler < 2) || (event_ctrler > 3)) { - dev_warn(pcie_pmu->dev, "Wrong ctrler id(%d) for pcie-pmu2!\n", - event_ctrler); - return -EINVAL; - } - if (pcie_pmu->ctrler_id != event_ctrler) { - pcie_pmu->ctrler_id = event_ctrler; - pcie_pmu->real_ctrler = pcie_pmu->ctrler_id; - phytium_pcie_pmu_select_ctrler(pcie_pmu); - } - } else { - if (event_ctrler != 0) { - dev_warn(pcie_pmu->dev, "Don't need to set ctrler id(%d) for pcie-pmu%d!\n", - event_ctrler, pcie_pmu->pmu_id); - return -EINVAL; - } - pcie_pmu->ctrler_id = pcie_pmu->pmu_id; - pcie_pmu->real_ctrler = pcie_pmu->ctrler_id; + event_timer = phytium_pcie_pmu_get_event_timer(event); + if (event_timer != 0) + phytium_pcie_pmu_set_timer(pcie_pmu, event_timer); + + event_ctrler = phytium_pcie_pmu_get_event_ctrler(event); + switch (pcie_pmu->pmu_id) { + case 0: + if (event_ctrler != 0) { + dev_warn(pcie_pmu->dev, + "Wrong ctrler id(%d) for pcie-pmu0!\n", + event_ctrler); + return -EINVAL; } - } else { - event_ctrler = phytium_pcie_pmu_get_event_ctrler(event); - switch (pcie_pmu->pmu_id) { - case 0: - if (event_ctrler != 0) { - dev_warn(pcie_pmu->dev, - "Wrong ctrler id(%d) for pcie-pmu0!\n", - event_ctrler); - return -EINVAL; - } - break; - case 1: - if (event_ctrler == 0) - event_ctrler = 1; - else if ((event_ctrler < 1) || (event_ctrler > 3)) { - dev_warn(pcie_pmu->dev, - "Wrong ctrler id(%d) for pcie-pmu1!\n", - event_ctrler); - return -EINVAL; - } - break; - case 2: - if (event_ctrler == 0) - event_ctrler = 4; - else if ((event_ctrler < 4) || (event_ctrler > 7)) { - dev_warn(pcie_pmu->dev, - "Wrong ctrler id(%d) for pcie-pmu2!\n", - event_ctrler); - return -EINVAL; - } - break; - default: - dev_err(pcie_pmu->dev, "Unsupported pmu id:%d!\n", - pcie_pmu->pmu_id); + break; + case 1: + if ((event_ctrler < 1) || (event_ctrler > 3)) { + dev_warn(pcie_pmu->dev, + "Wrong ctrler id(%d) for pcie-pmu1!\n", + event_ctrler); return -EINVAL; } - - pcie_pmu->ctrler_id = event_ctrler; - switch (pcie_pmu->pmu_id) { - case 0: - case 1: - pcie_pmu->real_ctrler = pcie_pmu->ctrler_id; - break; - case 2: - pcie_pmu->real_ctrler = (pcie_pmu->ctrler_id - 4) * 16; - break; - default: - dev_err(pcie_pmu->dev, "Unsupported pmu id:%d!\n", - pcie_pmu->pmu_id); + break; + case 2: + if ((event_ctrler < 4) || (event_ctrler > 7)) { + dev_warn(pcie_pmu->dev, + "Wrong ctrler id(%d) for pcie-pmu2!\n", + event_ctrler); return -EINVAL; } - phytium_pcie_pmu_select_ctrler(pcie_pmu); + break; + default: + dev_err(pcie_pmu->dev, "Unsupported pmu id:%d!\n", + pcie_pmu->pmu_id); + return -EINVAL; + } + + pcie_pmu->ctrler_id = event_ctrler; + switch (pcie_pmu->pmu_id) { + case 0: + case 1: + pcie_pmu->real_ctrler = pcie_pmu->ctrler_id; + break; + case 2: + pcie_pmu->real_ctrler = (pcie_pmu->ctrler_id - 4) * 16; + break; + default: + dev_err(pcie_pmu->dev, "Unsupported pmu id:%d!\n", + pcie_pmu->pmu_id); + return -EINVAL; } + phytium_pcie_pmu_select_ctrler(pcie_pmu); hwc->idx = -1; hwc->config_base = event->attr.config; @@ -566,12 +538,17 @@ void phytium_pcie_pmu_event_del(struct perf_event *event, int flags) struct phytium_pcie_pmu *pcie_pmu = to_phytium_pcie_pmu(event->pmu); struct hw_perf_event *hwc = &event->hw; unsigned long val; + u32 event_timer; phytium_pcie_pmu_event_stop(event, PERF_EF_UPDATE); val = phytium_pcie_pmu_get_irq_flag(pcie_pmu); val = phytium_pcie_pmu_get_stop_state(pcie_pmu); phytium_pcie_pmu_unmark_event(pcie_pmu, hwc->idx); + event_timer = phytium_pcie_pmu_get_event_timer(event); + if (event_timer != 0) + phytium_pcie_pmu_reset_timer(pcie_pmu); + perf_event_update_userpage(event); pcie_pmu->pmu_events.hw_events[hwc->idx] = NULL; } @@ -598,12 +575,6 @@ void phytium_pcie_pmu_disable(struct pmu *pmu) phytium_pcie_pmu_stop_all_counters(pcie_pmu); } -void phytium_pcie_pmu_reset(struct phytium_pcie_pmu *pcie_pmu) -{ - phytium_pcie_pmu_disable_clk(pcie_pmu); - phytium_pcie_pmu_clear_all_counters(pcie_pmu); -} - static const struct acpi_device_id phytium_pcie_pmu_acpi_match[] = { { "PHYT0044", @@ -623,7 +594,7 @@ static irqreturn_t phytium_pcie_pmu_overflow_handler(int irq, void *dev_id) overflown = phytium_pcie_pmu_get_irq_flag(pcie_pmu); - if (!test_bit(pcie_pmu->irq_bit, &overflown)) + if (!test_bit(pcie_pmu->pmu_id + 4, &overflown)) return IRQ_NONE; stop_state = phytium_pcie_pmu_get_stop_state(pcie_pmu); @@ -636,8 +607,7 @@ static irqreturn_t phytium_pcie_pmu_overflow_handler(int irq, void *dev_id) phytium_pcie_pmu_event_update(event); } phytium_pcie_pmu_clear_all_counters(pcie_pmu); - if ((stop_state & PCIE_PMU_OFL_STOP_TYPE_VAL) == 0) - phytium_pcie_pmu_start_all_counters(pcie_pmu); + phytium_pcie_pmu_start_all_counters(pcie_pmu); return IRQ_HANDLED; } @@ -673,14 +643,9 @@ static int phytium_pcie_pmu_init_irq(struct phytium_pcie_pmu *pcie_pmu, } static int phytium_pcie_pmu_init_data(struct platform_device *pdev, - struct phytium_pcie_pmu *pcie_pmu) + struct phytium_pcie_pmu *pcie_pmu) { struct resource *res, *clkres, *irqres; - pcie_pmu->soc_version = phytium_socs_type(); - if (pcie_pmu->soc_version == 0) { - dev_err(&pdev->dev, "The PCIe PMU driver can't be installed in this SoC.\n"); - return -EINVAL; - } if (device_property_read_u32(&pdev->dev, "phytium,die-id", &pcie_pmu->die_id)) { @@ -694,46 +659,20 @@ static int phytium_pcie_pmu_init_data(struct platform_device *pdev, return -EINVAL; } - if (pcie_pmu->soc_version == PS230XX) { - switch (pcie_pmu->pmu_id) { - case 0: - pcie_pmu->clk_bits = 0x1; - break; - case 1: - pcie_pmu->clk_bits = 0xe; - break; - case 2: - pcie_pmu->clk_bits = 0xf; - break; - default: - dev_err(&pdev->dev, "Unsupported pmu id:%d!\n", pcie_pmu->pmu_id); - break; - } - - pcie_pmu->irq_bit = pcie_pmu->pmu_id + 4; - } else { - if (device_property_read_u32(&pdev->dev, "phytium,pcie-id", &pcie_pmu->pcie_id)) { - dev_err(&pdev->dev, "Can not read phytium,pcie-id!\n"); - return -EINVAL; - } - - switch (pcie_pmu->pmu_id) { - case 0: - case 3: - pcie_pmu->clk_bits = 0x1; - break; - case 1: - pcie_pmu->clk_bits = 0x2; - break; - case 2: - pcie_pmu->clk_bits = 0xc; - break; - default: - dev_err(&pdev->dev, "Unsupported pmu id:%d!\n", pcie_pmu->pmu_id); - break; - } - - pcie_pmu->irq_bit = pcie_pmu->pcie_id * 4 + pcie_pmu->pmu_id + 16; + switch (pcie_pmu->pmu_id) { + case 0: + pcie_pmu->clk_bits = 0x1; + break; + case 1: + pcie_pmu->clk_bits = 0xe; + break; + case 2: + pcie_pmu->clk_bits = 0xf; + break; + default: + dev_err(&pdev->dev, "Unsupported pmu id:%d!\n", + pcie_pmu->pmu_id); + break; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -749,12 +688,12 @@ static int phytium_pcie_pmu_init_data(struct platform_device *pdev, return -EINVAL; } - pcie_pmu->cfg_base = + pcie_pmu->csr_base = devm_ioremap(&pdev->dev, clkres->start, resource_size(clkres)); - if (IS_ERR(pcie_pmu->cfg_base)) { + if (IS_ERR(pcie_pmu->csr_base)) { dev_err(&pdev->dev, "ioremap failed for pcie_pmu csr resource\n"); - return PTR_ERR(pcie_pmu->cfg_base); + return PTR_ERR(pcie_pmu->csr_base); } irqres = platform_get_resource(pdev, IORESOURCE_MEM, 2); @@ -772,13 +711,11 @@ static int phytium_pcie_pmu_init_data(struct platform_device *pdev, return PTR_ERR(pcie_pmu->irq_reg); } - phytium_pcie_pmu_reset(pcie_pmu); - return 0; } static int phytium_pcie_pmu_dev_probe(struct platform_device *pdev, - struct phytium_pcie_pmu *pcie_pmu) + struct phytium_pcie_pmu *pcie_pmu) { int ret; @@ -819,12 +756,8 @@ static int phytium_pcie_pmu_probe(struct platform_device *pdev) return ret; } - if (pcie_pmu->soc_version == PS230XX) - name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "phyt%u_pcie_pmu%u", - pcie_pmu->die_id, pcie_pmu->pmu_id); - else - name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "phyt%u_pcie%u_pmu%u", - pcie_pmu->die_id, pcie_pmu->pcie_id, pcie_pmu->pmu_id); + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "phyt%u_pcie_pmu%u", + pcie_pmu->die_id, pcie_pmu->pmu_id); pcie_pmu->pmu = (struct pmu){ .name = name, .module = THIS_MODULE, @@ -838,6 +771,7 @@ static int phytium_pcie_pmu_probe(struct platform_device *pdev) .stop = phytium_pcie_pmu_event_stop, .read = phytium_pcie_pmu_event_update, .attr_groups = phytium_pcie_pmu_attr_groups, + .capabilities = PERF_PMU_CAP_NO_EXCLUDE, }; ret = perf_pmu_register(&pcie_pmu->pmu, name, -1); @@ -850,12 +784,9 @@ static int phytium_pcie_pmu_probe(struct platform_device *pdev) phytium_pcie_pmu_enable_clk(pcie_pmu); - if (pcie_pmu->soc_version == PS230XX) - pr_info("die%d_pcie_pmu%d on cpu%d.\n", - pcie_pmu->die_id, pcie_pmu->pmu_id, pcie_pmu->on_cpu); - else - pr_info("die%d_pcie%d_pmu%d on cpu%d.\n", - pcie_pmu->die_id, pcie_pmu->pcie_id, pcie_pmu->pmu_id, pcie_pmu->on_cpu); + pr_info("Phytium PCIe PMU: "); + pr_info("die_id = %d pmu_id = %d.\n", pcie_pmu->die_id, + pcie_pmu->pmu_id); return ret; } @@ -897,7 +828,6 @@ int phytium_pcie_pmu_online_cpu(unsigned int cpu, struct hlist_node *node) pcie_pmu->on_cpu = cpu; WARN_ON(irq_set_affinity_hint(pcie_pmu->irq, cpumask_of(cpu))); } - return 0; } @@ -944,8 +874,8 @@ static int __init phytium_pcie_pmu_module_init(void) phytium_pcie_pmu_hp_state = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, - "perf/phytium/pciepmu:online", phytium_pcie_pmu_online_cpu, - phytium_pcie_pmu_offline_cpu); + "perf/phytium/pciepmu:online", + phytium_pcie_pmu_online_cpu, phytium_pcie_pmu_offline_cpu); if (phytium_pcie_pmu_hp_state < 0) { pr_err("PCIE PMU: setup hotplug, ret = %d\n", phytium_pcie_pmu_hp_state); @@ -972,4 +902,3 @@ MODULE_DESCRIPTION("Phytium PCIe PMU driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(PCIE_PERF_DRIVER_VERSION); MODULE_AUTHOR("Hu Xianghua "); -MODULE_AUTHOR("Tan Rui "); From 68010f2c301417b3ad84c4f24d67d1f19dff262c Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Fri, 8 May 2026 11:39:09 +0800 Subject: [PATCH 53/81] Revert drivers/perf:phytium: Added some functional improvements for DDR PMU. This reverts commit 6b95559ae805afcc01cd1960da56c96214ef3522. Reason for revert: Signed-off-by: Zhang Fuxiang Signed-off-by: Tan Rui Signed-off-by: Hu Xianghua --- drivers/perf/phytium/phytium_ddr_pmu.c | 228 +++++++++++-------------- 1 file changed, 100 insertions(+), 128 deletions(-) diff --git a/drivers/perf/phytium/phytium_ddr_pmu.c b/drivers/perf/phytium/phytium_ddr_pmu.c index 64fc031e0e011..54f9069bfea51 100644 --- a/drivers/perf/phytium/phytium_ddr_pmu.c +++ b/drivers/perf/phytium/phytium_ddr_pmu.c @@ -23,70 +23,38 @@ #include #include #include -#include -#if IS_ENABLED(CONFIG_ARM || CONFIG_ARM64) #include -#endif /* CONFIG_ARM || CONFIG_ARM64 */ #include #undef pr_fmt #define pr_fmt(fmt) "phytium_ddr_pmu: " fmt - +#define DDR_PERF_DRIVER_VERSION "1.2.1" #define PHYTIUM_DDR_MAX_COUNTERS 8 -#define DDR_PERF_DRIVER_VERSION "1.3.0" - -#define DDR_START_TIMER 0x000 -#define DDR_STOP_TIMER 0x004 -#define DDR_CLEAR_EVENT 0x008 -#define DDR_SET_TIMER_L 0x00c -#define DDR_SET_TIMER_H 0x010 -#define DDR_TRIG_MODE 0x014 -#define DDR_NOW_STATE 0x0e0 -#define DDR_EVENT_CYCLES 0x0e4 -#define DDR_TPOINT_END_L 0x0e4 -#define DDR_TPOINT_END_H 0x0e8 -#define DDR_STATE_STOP 0x0ec -#define DDR_EVENT_RXREQ 0x100 -#define DDR_EVENT_RXDAT 0x104 -#define DDR_EVENT_TXDAT 0x108 -#define DDR_EVENT_RXREQ_RNS 0x10c -#define DDR_EVENT_RXREQ_WNSP 0x110 -#define DDR_EVENT_RXREQ_WNSF 0x114 -#define DDR_EVENT_BANDWIDTH 0x200 -#define DDR_W_DATA_BASE 0x200 -#define DDR_CLK_FRE 0xe00 -#define DDR_DATA_WIDTH 0xe04 - -#define DDR_PMU_OFL_STOP_TYPE_VAL 0x10 - -#define SOC_ID_PS230XX 0x8 -#define SOC_ID_PS240XX 0x6 -#define MIDR_PSXX 0x700F8620 +#define DDR_START_TIMER 0x000 +#define DDR_STOP_TIMER 0x004 +#define DDR_CLEAR_EVENT 0x008 +#define DDR_SET_TIMER_L 0x00c +#define DDR_SET_TIMER_H 0x010 +#define DDR_TRIG_MODE 0x014 +#define DDR_NOW_STATE 0x0e0 +#define DDR_EVENT_CYCLES 0x0e4 +#define DDR_TPOINT_END_L 0x0e4 +#define DDR_TPOINT_END_H 0x0e8 +#define DDR_STATE_STOP 0x0ec +#define DDR_EVENT_RXREQ 0x100 +#define DDR_EVENT_RXDAT 0x104 +#define DDR_EVENT_TXDAT 0x108 +#define DDR_EVENT_RXREQ_RNS 0x10c +#define DDR_EVENT_RXREQ_WNSP 0x110 +#define DDR_EVENT_RXREQ_WNSF 0x114 +#define DDR_EVENT_BANDWIDTH 0x200 +#define DDR_W_DATA_BASE 0x200 +#define DDR_CLK_FRE 0xe00 +#define DDR_DATA_WIDTH 0xe04 #define to_phytium_ddr_pmu(p) (container_of(p, struct phytium_ddr_pmu, pmu)) - -enum { - PS230XX = 0x01, - PS240XX = 0x02, -}; - -static inline int phytium_socs_type(void) -{ - unsigned int soc_id, cpu_id; - - soc_id = read_sysreg_s(SYS_AIDR_EL1); - cpu_id = read_cpuid_id(); - - if ((soc_id == SOC_ID_PS230XX) && (cpu_id == MIDR_PSXX)) - return PS230XX; - else if ((soc_id == SOC_ID_PS240XX) && (cpu_id == MIDR_PSXX)) - return PS240XX; - else - return 0; -} - static int phytium_ddr_pmu_hp_state; struct phytium_ddr_pmu_hwevents { @@ -97,17 +65,15 @@ struct phytium_ddr_pmu_hwevents { struct phytium_ddr_pmu { struct device *dev; void __iomem *base; - void __iomem *cfg_base; - void __iomem *irq_reg; + void __iomem *csr_base; struct pmu pmu; struct phytium_ddr_pmu_hwevents pmu_events; u32 die_id; u32 ddr_id; u32 pmu_id; - int irq_bit; + int bit_idx; int on_cpu; int irq; - int soc_version; struct hlist_node node; }; @@ -151,21 +117,22 @@ static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, return cpumap_print_to_pagebuf(true, buf, cpumask_of(ddr_pmu->on_cpu)); } -#define PHYTIUM_PMU_ATTR(_name, _func, _config) \ - (&((struct dev_ext_attribute[]){ \ - { __ATTR(_name, 0444, _func, NULL), (void *)_config } })[0] \ - .attr.attr) +#define PHYTIUM_PMU_ATTR(_name, _func, _config) \ + (&((struct dev_ext_attribute[]){ \ + { __ATTR(_name, 0444, _func, NULL), (void *)_config } })[0] \ + .attr.attr) -#define PHYTIUM_DDR_PMU_FORMAT_ATTR(_name, _config) \ - PHYTIUM_PMU_ATTR(_name, phytium_ddr_pmu_format_sysfs_show, \ - (void *)_config) +#define PHYTIUM_DDR_PMU_FORMAT_ATTR(_name, _config) \ + PHYTIUM_PMU_ATTR(_name, phytium_ddr_pmu_format_sysfs_show, \ + (void *)_config) -#define PHYTIUM_DDR_PMU_EVENT_ATTR(_name, _config) \ - PHYTIUM_PMU_ATTR(_name, phytium_ddr_pmu_event_sysfs_show, \ - (unsigned long)_config) +#define PHYTIUM_DDR_PMU_EVENT_ATTR(_name, _config) \ + PHYTIUM_PMU_ATTR(_name, phytium_ddr_pmu_event_sysfs_show, \ + (unsigned long)_config) static struct attribute *phytium_ddr_pmu_format_attr[] = { PHYTIUM_DDR_PMU_FORMAT_ATTR(event, "config:0-2"), + PHYTIUM_DDR_PMU_FORMAT_ATTR(timer, "config1:0-31"), NULL, }; @@ -209,6 +176,11 @@ static const struct attribute_group *phytium_ddr_pmu_attr_groups[] = { NULL, }; +static u32 phytium_ddr_pmu_get_event_timer(struct perf_event *event) +{ + return FIELD_GET(GENMASK(31, 0), event->attr.config1); +} + static u64 phytium_ddr_pmu_read_counter(struct phytium_ddr_pmu *ddr_pmu, struct hw_perf_event *hwc) { @@ -248,24 +220,18 @@ static void phytium_ddr_pmu_enable_clk(struct phytium_ddr_pmu *ddr_pmu) { u32 val; - if (ddr_pmu->soc_version == PS240XX) - return; - - val = readl(ddr_pmu->cfg_base); + val = readl(ddr_pmu->csr_base); val |= 0xF; - writel(val, ddr_pmu->cfg_base); + writel(val, ddr_pmu->csr_base); } static void phytium_ddr_pmu_disable_clk(struct phytium_ddr_pmu *ddr_pmu) { u32 val; - if (ddr_pmu->soc_version == PS240XX) - return; - - val = readl(ddr_pmu->cfg_base); + val = readl(ddr_pmu->csr_base); val &= ~(0xF); - writel(val, ddr_pmu->cfg_base); + writel(val, ddr_pmu->csr_base); } static void phytium_ddr_pmu_clear_all_counters(struct phytium_ddr_pmu *ddr_pmu) @@ -283,6 +249,29 @@ static void phytium_ddr_pmu_stop_all_counters(struct phytium_ddr_pmu *ddr_pmu) writel(0x1, ddr_pmu->base + DDR_STOP_TIMER); } +static void phytium_ddr_pmu_set_timer(struct phytium_ddr_pmu *ddr_pmu, + u32 th_val) +{ + u32 val; + + val = readl(ddr_pmu->base + DDR_SET_TIMER_L); + val = readl(ddr_pmu->base + DDR_SET_TIMER_H); + + writel(th_val, ddr_pmu->base + DDR_SET_TIMER_L); + writel(0, ddr_pmu->base + DDR_SET_TIMER_H); +} + +static void phytium_ddr_pmu_reset_timer(struct phytium_ddr_pmu *ddr_pmu) +{ + u32 val; + + val = readl(ddr_pmu->base + DDR_SET_TIMER_L); + val = readl(ddr_pmu->base + DDR_SET_TIMER_H); + + writel(0xFFFFFFFF, ddr_pmu->base + DDR_SET_TIMER_L); + writel(0xFFFFFFFF, ddr_pmu->base + DDR_SET_TIMER_H); +} + static unsigned long phytium_ddr_pmu_get_stop_state(struct phytium_ddr_pmu *ddr_pmu) { @@ -297,7 +286,7 @@ phytium_ddr_pmu_get_irq_flag(struct phytium_ddr_pmu *ddr_pmu) { unsigned long val; - val = (unsigned long)readl(ddr_pmu->irq_reg); + val = (unsigned long)readl(ddr_pmu->csr_base + 4); return val; } @@ -332,6 +321,7 @@ int phytium_ddr_pmu_event_init(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; struct phytium_ddr_pmu *ddr_pmu; + u32 event_timer; if (event->attr.type != event->pmu->type) return -ENOENT; @@ -352,6 +342,10 @@ int phytium_ddr_pmu_event_init(struct perf_event *event) if (ddr_pmu->on_cpu == -1) return -EINVAL; + event_timer = phytium_ddr_pmu_get_event_timer(event); + if (event_timer != 0) + phytium_ddr_pmu_set_timer(ddr_pmu, event_timer); + hwc->idx = -1; hwc->config_base = event->attr.config; @@ -411,12 +405,17 @@ void phytium_ddr_pmu_event_del(struct perf_event *event, int flags) struct phytium_ddr_pmu *ddr_pmu = to_phytium_ddr_pmu(event->pmu); struct hw_perf_event *hwc = &event->hw; unsigned long val; + u32 event_timer; phytium_ddr_pmu_event_stop(event, PERF_EF_UPDATE); val = phytium_ddr_pmu_get_irq_flag(ddr_pmu); val = phytium_ddr_pmu_get_stop_state(ddr_pmu); phytium_ddr_pmu_unmark_event(ddr_pmu, hwc->idx); + event_timer = phytium_ddr_pmu_get_event_timer(event); + if (event_timer != 0) + phytium_ddr_pmu_reset_timer(ddr_pmu); + perf_event_update_userpage(event); ddr_pmu->pmu_events.hw_events[hwc->idx] = NULL; } @@ -443,12 +442,6 @@ void phytium_ddr_pmu_disable(struct pmu *pmu) phytium_ddr_pmu_stop_all_counters(ddr_pmu); } -void phytium_ddr_pmu_reset(struct phytium_ddr_pmu *ddr_pmu) -{ - phytium_ddr_pmu_disable_clk(ddr_pmu); - phytium_ddr_pmu_clear_all_counters(ddr_pmu); -} - static const struct acpi_device_id phytium_ddr_pmu_acpi_match[] = { { "PHYT0043", @@ -462,13 +455,14 @@ static irqreturn_t phytium_ddr_pmu_overflow_handler(int irq, void *dev_id) struct phytium_ddr_pmu *ddr_pmu = dev_id; struct perf_event *event; unsigned long overflown, stop_state; - unsigned long *used_mask = ddr_pmu->pmu_events.used_mask; int idx; + unsigned long *used_mask = ddr_pmu->pmu_events.used_mask; + int event_added = bitmap_weight(used_mask, PHYTIUM_DDR_MAX_COUNTERS); overflown = phytium_ddr_pmu_get_irq_flag(ddr_pmu); - if (!test_bit(ddr_pmu->irq_bit, &overflown)) + if (!test_bit(ddr_pmu->bit_idx, &overflown)) return IRQ_NONE; stop_state = phytium_ddr_pmu_get_stop_state(ddr_pmu); @@ -480,10 +474,8 @@ static irqreturn_t phytium_ddr_pmu_overflow_handler(int irq, void *dev_id) continue; phytium_ddr_pmu_event_update(event); } - phytium_ddr_pmu_clear_all_counters(ddr_pmu); - if ((stop_state & DDR_PMU_OFL_STOP_TYPE_VAL) == 0) - phytium_ddr_pmu_start_all_counters(ddr_pmu); + phytium_ddr_pmu_start_all_counters(ddr_pmu); return IRQ_HANDLED; } @@ -523,15 +515,7 @@ static int phytium_ddr_pmu_init_irq(struct phytium_ddr_pmu *ddr_pmu, static int phytium_ddr_pmu_init_data(struct platform_device *pdev, struct phytium_ddr_pmu *ddr_pmu) { - struct resource *res, *clkres, *irqres; - - ddr_pmu->soc_version = phytium_socs_type(); - - - if (ddr_pmu->soc_version == 0) { - dev_err(&pdev->dev, "The DDR PMU driver can't be installed in this SoC!\n"); - return -EINVAL; - } + struct resource *res, *clkres; if (device_property_read_u32(&pdev->dev, "phytium,die-id", &ddr_pmu->die_id)) { @@ -551,7 +535,7 @@ static int phytium_ddr_pmu_init_data(struct platform_device *pdev, return -EINVAL; } - ddr_pmu->irq_bit = ddr_pmu->ddr_id * 2 + ddr_pmu->pmu_id; + ddr_pmu->bit_idx = ddr_pmu->ddr_id * 2 + ddr_pmu->pmu_id; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ddr_pmu->base = devm_ioremap_resource(&pdev->dev, res); @@ -567,29 +551,15 @@ static int phytium_ddr_pmu_init_data(struct platform_device *pdev, dev_err(&pdev->dev, "failed for get ddr_pmu clk resource.\n"); return -EINVAL; } - ddr_pmu->cfg_base = devm_ioremap(&pdev->dev, clkres->start, resource_size(clkres)); - if (IS_ERR(ddr_pmu->cfg_base)) { - dev_err(&pdev->dev, "ioremap failed for ddr_pmu clk resource\n"); - return PTR_ERR(ddr_pmu->cfg_base); - } - if (ddr_pmu->soc_version == PS240XX) { - irqres = platform_get_resource(pdev, IORESOURCE_MEM, 2); - if (!irqres) { - dev_err(&pdev->dev, "failed for get ddr_pmu irq resource.\n"); - return -EINVAL; - } - ddr_pmu->irq_reg = devm_ioremap(&pdev->dev, irqres->start, resource_size(irqres)); - if (IS_ERR(ddr_pmu->irq_reg)) { - dev_err(&pdev->dev, "ioremap failed for ddr_pmu irq resource\n"); - return PTR_ERR(ddr_pmu->irq_reg); - } - } else { - ddr_pmu->irq_reg = ddr_pmu->cfg_base + 0x4; + ddr_pmu->csr_base = + devm_ioremap(&pdev->dev, clkres->start, resource_size(clkres)); + if (IS_ERR(ddr_pmu->csr_base)) { + dev_err(&pdev->dev, + "ioremap failed for ddr_pmu csr resource\n"); + return PTR_ERR(ddr_pmu->csr_base); } - phytium_ddr_pmu_reset(ddr_pmu); - return 0; } @@ -628,8 +598,8 @@ static int phytium_ddr_pmu_probe(struct platform_device *pdev) if (ret) return ret; - ret = cpuhp_state_add_instance(phytium_ddr_pmu_hp_state, - &ddr_pmu->node); + ret = cpuhp_state_add_instance( + phytium_ddr_pmu_hp_state, &ddr_pmu->node); if (ret) { dev_err(&pdev->dev, "Error %d registering hotplug;\n", ret); return ret; @@ -651,19 +621,22 @@ static int phytium_ddr_pmu_probe(struct platform_device *pdev) .stop = phytium_ddr_pmu_event_stop, .read = phytium_ddr_pmu_event_update, .attr_groups = phytium_ddr_pmu_attr_groups, + .capabilities = PERF_PMU_CAP_NO_EXCLUDE, }; ret = perf_pmu_register(&ddr_pmu->pmu, name, -1); if (ret) { dev_err(ddr_pmu->dev, "DDR PMU register failed!\n"); - cpuhp_state_remove_instance_nocalls(phytium_ddr_pmu_hp_state, + cpuhp_state_remove_instance_nocalls( + phytium_ddr_pmu_hp_state, &ddr_pmu->node); } phytium_ddr_pmu_enable_clk(ddr_pmu); - pr_info("die%d_ddr%d_pmu%d on cpu%d.\n", ddr_pmu->die_id, - ddr_pmu->ddr_id, ddr_pmu->pmu_id, ddr_pmu->on_cpu); + pr_info("Phytium DDR PMU: "); + pr_info(" die_id = %d ddr_id = %d pmu_id = %d.\n", ddr_pmu->die_id, + ddr_pmu->ddr_id, ddr_pmu->pmu_id); return ret; } @@ -675,8 +648,8 @@ static int phytium_ddr_pmu_remove(struct platform_device *pdev) phytium_ddr_pmu_disable_clk(ddr_pmu); perf_pmu_unregister(&ddr_pmu->pmu); - cpuhp_state_remove_instance_nocalls(phytium_ddr_pmu_hp_state, - &ddr_pmu->node); + cpuhp_state_remove_instance_nocalls( + phytium_ddr_pmu_hp_state, &ddr_pmu->node); return 0; } @@ -778,4 +751,3 @@ MODULE_DESCRIPTION("Phytium DDR PMU driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DDR_PERF_DRIVER_VERSION); MODULE_AUTHOR("Hu Xianghua "); -MODULE_AUTHOR("Tan Rui "); From 429271af23611e3f3d9c23f074263c26cd32b575 Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Fri, 8 May 2026 13:33:41 +0800 Subject: [PATCH 54/81] drivers/perf:phytium: Add DDR PMU Support for PS240XX. Add a new HID to differentiate between PS230XX and PS240XX DDR PMU and add support for PS240XX platforms. Signed-off-by: Zhang Fuxiang Signed-off-by: Tan Rui Signed-off-by: Hu Xianghua --- drivers/perf/phytium/phytium_ddr_pmu.c | 227 +++++++++++++------------ 1 file changed, 121 insertions(+), 106 deletions(-) diff --git a/drivers/perf/phytium/phytium_ddr_pmu.c b/drivers/perf/phytium/phytium_ddr_pmu.c index 54f9069bfea51..93372681e4f6c 100644 --- a/drivers/perf/phytium/phytium_ddr_pmu.c +++ b/drivers/perf/phytium/phytium_ddr_pmu.c @@ -23,38 +23,46 @@ #include #include #include +#include #include #include #undef pr_fmt #define pr_fmt(fmt) "phytium_ddr_pmu: " fmt -#define DDR_PERF_DRIVER_VERSION "1.2.1" - #define PHYTIUM_DDR_MAX_COUNTERS 8 -#define DDR_START_TIMER 0x000 -#define DDR_STOP_TIMER 0x004 -#define DDR_CLEAR_EVENT 0x008 -#define DDR_SET_TIMER_L 0x00c -#define DDR_SET_TIMER_H 0x010 -#define DDR_TRIG_MODE 0x014 -#define DDR_NOW_STATE 0x0e0 -#define DDR_EVENT_CYCLES 0x0e4 -#define DDR_TPOINT_END_L 0x0e4 -#define DDR_TPOINT_END_H 0x0e8 -#define DDR_STATE_STOP 0x0ec -#define DDR_EVENT_RXREQ 0x100 -#define DDR_EVENT_RXDAT 0x104 -#define DDR_EVENT_TXDAT 0x108 -#define DDR_EVENT_RXREQ_RNS 0x10c -#define DDR_EVENT_RXREQ_WNSP 0x110 -#define DDR_EVENT_RXREQ_WNSF 0x114 -#define DDR_EVENT_BANDWIDTH 0x200 -#define DDR_W_DATA_BASE 0x200 -#define DDR_CLK_FRE 0xe00 -#define DDR_DATA_WIDTH 0xe04 +#define DDR_PERF_DRIVER_VERSION "1.3.0" + +#define DDR_START_TIMER 0x000 +#define DDR_STOP_TIMER 0x004 +#define DDR_CLEAR_EVENT 0x008 +#define DDR_SET_TIMER_L 0x00c +#define DDR_SET_TIMER_H 0x010 +#define DDR_TRIG_MODE 0x014 +#define DDR_NOW_STATE 0x0e0 +#define DDR_EVENT_CYCLES 0x0e4 +#define DDR_TPOINT_END_L 0x0e4 +#define DDR_TPOINT_END_H 0x0e8 +#define DDR_STATE_STOP 0x0ec +#define DDR_EVENT_RXREQ 0x100 +#define DDR_EVENT_RXDAT 0x104 +#define DDR_EVENT_TXDAT 0x108 +#define DDR_EVENT_RXREQ_RNS 0x10c +#define DDR_EVENT_RXREQ_WNSP 0x110 +#define DDR_EVENT_RXREQ_WNSF 0x114 +#define DDR_EVENT_BANDWIDTH 0x200 +#define DDR_W_DATA_BASE 0x200 +#define DDR_CLK_FRE 0xe00 +#define DDR_DATA_WIDTH 0xe04 + +#define DDR_PMU_OFL_STOP_TYPE_VAL 0x10 #define to_phytium_ddr_pmu(p) (container_of(p, struct phytium_ddr_pmu, pmu)) +enum { + DDRV1P0 = 0x01, + DDRV1P5 = 0x02, +}; + static int phytium_ddr_pmu_hp_state; struct phytium_ddr_pmu_hwevents { @@ -65,15 +73,17 @@ struct phytium_ddr_pmu_hwevents { struct phytium_ddr_pmu { struct device *dev; void __iomem *base; - void __iomem *csr_base; + void __iomem *cfg_base; + void __iomem *irq_reg; struct pmu pmu; struct phytium_ddr_pmu_hwevents pmu_events; u32 die_id; u32 ddr_id; u32 pmu_id; - int bit_idx; + int irq_bit; int on_cpu; int irq; + int ver; struct hlist_node node; }; @@ -86,7 +96,6 @@ static const u32 ddr_counter_reg_offset[] = { DDR_EVENT_RXREQ_WNSF, DDR_EVENT_BANDWIDTH }; - ssize_t phytium_ddr_pmu_format_sysfs_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -94,6 +103,7 @@ ssize_t phytium_ddr_pmu_format_sysfs_show(struct device *dev, struct dev_ext_attribute *eattr; eattr = container_of(attr, struct dev_ext_attribute, attr); + return sprintf(buf, "%s\n", (char *)eattr->var); } @@ -109,7 +119,7 @@ ssize_t phytium_ddr_pmu_event_sysfs_show(struct device *dev, } static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, - char *buf) + char *buf) { struct phytium_ddr_pmu *ddr_pmu = to_phytium_ddr_pmu(dev_get_drvdata(dev)); @@ -117,22 +127,21 @@ static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, return cpumap_print_to_pagebuf(true, buf, cpumask_of(ddr_pmu->on_cpu)); } -#define PHYTIUM_PMU_ATTR(_name, _func, _config) \ - (&((struct dev_ext_attribute[]){ \ +#define PHYTIUM_PMU_ATTR(_name, _func, _config) \ + (&((struct dev_ext_attribute[]){ \ { __ATTR(_name, 0444, _func, NULL), (void *)_config } })[0] \ .attr.attr) -#define PHYTIUM_DDR_PMU_FORMAT_ATTR(_name, _config) \ - PHYTIUM_PMU_ATTR(_name, phytium_ddr_pmu_format_sysfs_show, \ +#define PHYTIUM_DDR_PMU_FORMAT_ATTR(_name, _config) \ + PHYTIUM_PMU_ATTR(_name, phytium_ddr_pmu_format_sysfs_show, \ (void *)_config) -#define PHYTIUM_DDR_PMU_EVENT_ATTR(_name, _config) \ - PHYTIUM_PMU_ATTR(_name, phytium_ddr_pmu_event_sysfs_show, \ - (unsigned long)_config) +#define PHYTIUM_DDR_PMU_EVENT_ATTR(_name, _config) \ + PHYTIUM_PMU_ATTR(_name, phytium_ddr_pmu_event_sysfs_show, \ + (unsigned long)_config) static struct attribute *phytium_ddr_pmu_format_attr[] = { PHYTIUM_DDR_PMU_FORMAT_ATTR(event, "config:0-2"), - PHYTIUM_DDR_PMU_FORMAT_ATTR(timer, "config1:0-31"), NULL, }; @@ -176,11 +185,6 @@ static const struct attribute_group *phytium_ddr_pmu_attr_groups[] = { NULL, }; -static u32 phytium_ddr_pmu_get_event_timer(struct perf_event *event) -{ - return FIELD_GET(GENMASK(31, 0), event->attr.config1); -} - static u64 phytium_ddr_pmu_read_counter(struct phytium_ddr_pmu *ddr_pmu, struct hw_perf_event *hwc) { @@ -220,18 +224,24 @@ static void phytium_ddr_pmu_enable_clk(struct phytium_ddr_pmu *ddr_pmu) { u32 val; - val = readl(ddr_pmu->csr_base); + if (ddr_pmu->ver == DDRV1P5) + return; + + val = readl(ddr_pmu->cfg_base); val |= 0xF; - writel(val, ddr_pmu->csr_base); + writel(val, ddr_pmu->cfg_base); } static void phytium_ddr_pmu_disable_clk(struct phytium_ddr_pmu *ddr_pmu) { u32 val; - val = readl(ddr_pmu->csr_base); + if (ddr_pmu->ver == DDRV1P5) + return; + + val = readl(ddr_pmu->cfg_base); val &= ~(0xF); - writel(val, ddr_pmu->csr_base); + writel(val, ddr_pmu->cfg_base); } static void phytium_ddr_pmu_clear_all_counters(struct phytium_ddr_pmu *ddr_pmu) @@ -249,29 +259,6 @@ static void phytium_ddr_pmu_stop_all_counters(struct phytium_ddr_pmu *ddr_pmu) writel(0x1, ddr_pmu->base + DDR_STOP_TIMER); } -static void phytium_ddr_pmu_set_timer(struct phytium_ddr_pmu *ddr_pmu, - u32 th_val) -{ - u32 val; - - val = readl(ddr_pmu->base + DDR_SET_TIMER_L); - val = readl(ddr_pmu->base + DDR_SET_TIMER_H); - - writel(th_val, ddr_pmu->base + DDR_SET_TIMER_L); - writel(0, ddr_pmu->base + DDR_SET_TIMER_H); -} - -static void phytium_ddr_pmu_reset_timer(struct phytium_ddr_pmu *ddr_pmu) -{ - u32 val; - - val = readl(ddr_pmu->base + DDR_SET_TIMER_L); - val = readl(ddr_pmu->base + DDR_SET_TIMER_H); - - writel(0xFFFFFFFF, ddr_pmu->base + DDR_SET_TIMER_L); - writel(0xFFFFFFFF, ddr_pmu->base + DDR_SET_TIMER_H); -} - static unsigned long phytium_ddr_pmu_get_stop_state(struct phytium_ddr_pmu *ddr_pmu) { @@ -286,7 +273,7 @@ phytium_ddr_pmu_get_irq_flag(struct phytium_ddr_pmu *ddr_pmu) { unsigned long val; - val = (unsigned long)readl(ddr_pmu->csr_base + 4); + val = (unsigned long)readl(ddr_pmu->irq_reg); return val; } @@ -321,7 +308,6 @@ int phytium_ddr_pmu_event_init(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; struct phytium_ddr_pmu *ddr_pmu; - u32 event_timer; if (event->attr.type != event->pmu->type) return -ENOENT; @@ -342,10 +328,6 @@ int phytium_ddr_pmu_event_init(struct perf_event *event) if (ddr_pmu->on_cpu == -1) return -EINVAL; - event_timer = phytium_ddr_pmu_get_event_timer(event); - if (event_timer != 0) - phytium_ddr_pmu_set_timer(ddr_pmu, event_timer); - hwc->idx = -1; hwc->config_base = event->attr.config; @@ -404,17 +386,10 @@ void phytium_ddr_pmu_event_del(struct perf_event *event, int flags) { struct phytium_ddr_pmu *ddr_pmu = to_phytium_ddr_pmu(event->pmu); struct hw_perf_event *hwc = &event->hw; - unsigned long val; - u32 event_timer; phytium_ddr_pmu_event_stop(event, PERF_EF_UPDATE); - val = phytium_ddr_pmu_get_irq_flag(ddr_pmu); - val = phytium_ddr_pmu_get_stop_state(ddr_pmu); - phytium_ddr_pmu_unmark_event(ddr_pmu, hwc->idx); - event_timer = phytium_ddr_pmu_get_event_timer(event); - if (event_timer != 0) - phytium_ddr_pmu_reset_timer(ddr_pmu); + phytium_ddr_pmu_unmark_event(ddr_pmu, hwc->idx); perf_event_update_userpage(event); ddr_pmu->pmu_events.hw_events[hwc->idx] = NULL; @@ -442,10 +417,15 @@ void phytium_ddr_pmu_disable(struct pmu *pmu) phytium_ddr_pmu_stop_all_counters(ddr_pmu); } +void phytium_ddr_pmu_reset(struct phytium_ddr_pmu *ddr_pmu) +{ + phytium_ddr_pmu_disable_clk(ddr_pmu); + phytium_ddr_pmu_clear_all_counters(ddr_pmu); +} + static const struct acpi_device_id phytium_ddr_pmu_acpi_match[] = { - { - "PHYT0043", - }, + { "PHYT0043", }, + { "PHYT0067", }, {}, }; MODULE_DEVICE_TABLE(acpi, phytium_ddr_pmu_acpi_match); @@ -455,14 +435,13 @@ static irqreturn_t phytium_ddr_pmu_overflow_handler(int irq, void *dev_id) struct phytium_ddr_pmu *ddr_pmu = dev_id; struct perf_event *event; unsigned long overflown, stop_state; - int idx; unsigned long *used_mask = ddr_pmu->pmu_events.used_mask; - + int idx; int event_added = bitmap_weight(used_mask, PHYTIUM_DDR_MAX_COUNTERS); overflown = phytium_ddr_pmu_get_irq_flag(ddr_pmu); - if (!test_bit(ddr_pmu->bit_idx, &overflown)) + if (!test_bit(ddr_pmu->irq_bit, &overflown)) return IRQ_NONE; stop_state = phytium_ddr_pmu_get_stop_state(ddr_pmu); @@ -474,8 +453,10 @@ static irqreturn_t phytium_ddr_pmu_overflow_handler(int irq, void *dev_id) continue; phytium_ddr_pmu_event_update(event); } + phytium_ddr_pmu_clear_all_counters(ddr_pmu); - phytium_ddr_pmu_start_all_counters(ddr_pmu); + if ((stop_state & DDR_PMU_OFL_STOP_TYPE_VAL) == 0) + phytium_ddr_pmu_start_all_counters(ddr_pmu); return IRQ_HANDLED; } @@ -488,6 +469,24 @@ static irqreturn_t phytium_ddr_pmu_overflow_handler(int irq, void *dev_id) return IRQ_NONE; } +static int phytium_ddr_pmu_version(struct platform_device *pdev, + struct phytium_ddr_pmu *ddr_pmu) +{ + struct acpi_device *acpi_dev; + + acpi_dev = ACPI_COMPANION(&pdev->dev); + if (!strcmp(acpi_device_hid(acpi_dev), "PHYT0043")) { + ddr_pmu->ver = DDRV1P0; + } else if (!strcmp(acpi_device_hid(acpi_dev), "PHYT0067")) { + ddr_pmu->ver = DDRV1P5; + } else { + dev_err(&pdev->dev, "The current driver does not support this device.\n"); + return -ENODEV; + } + + return 0; +} + static int phytium_ddr_pmu_init_irq(struct phytium_ddr_pmu *ddr_pmu, struct platform_device *pdev) { @@ -515,7 +514,7 @@ static int phytium_ddr_pmu_init_irq(struct phytium_ddr_pmu *ddr_pmu, static int phytium_ddr_pmu_init_data(struct platform_device *pdev, struct phytium_ddr_pmu *ddr_pmu) { - struct resource *res, *clkres; + struct resource *res, *clkres, *irqres; if (device_property_read_u32(&pdev->dev, "phytium,die-id", &ddr_pmu->die_id)) { @@ -535,7 +534,7 @@ static int phytium_ddr_pmu_init_data(struct platform_device *pdev, return -EINVAL; } - ddr_pmu->bit_idx = ddr_pmu->ddr_id * 2 + ddr_pmu->pmu_id; + ddr_pmu->irq_bit = ddr_pmu->ddr_id * 2 + ddr_pmu->pmu_id; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ddr_pmu->base = devm_ioremap_resource(&pdev->dev, res); @@ -551,15 +550,29 @@ static int phytium_ddr_pmu_init_data(struct platform_device *pdev, dev_err(&pdev->dev, "failed for get ddr_pmu clk resource.\n"); return -EINVAL; } + ddr_pmu->cfg_base = devm_ioremap(&pdev->dev, clkres->start, resource_size(clkres)); + if (IS_ERR(ddr_pmu->cfg_base)) { + dev_err(&pdev->dev, "ioremap failed for ddr_pmu clk resource\n"); + return PTR_ERR(ddr_pmu->cfg_base); + } - ddr_pmu->csr_base = - devm_ioremap(&pdev->dev, clkres->start, resource_size(clkres)); - if (IS_ERR(ddr_pmu->csr_base)) { - dev_err(&pdev->dev, - "ioremap failed for ddr_pmu csr resource\n"); - return PTR_ERR(ddr_pmu->csr_base); + if (ddr_pmu->ver == DDRV1P5) { + irqres = platform_get_resource(pdev, IORESOURCE_MEM, 2); + if (!irqres) { + dev_err(&pdev->dev, "failed for get ddr_pmu irq resource.\n"); + return -EINVAL; + } + ddr_pmu->irq_reg = devm_ioremap(&pdev->dev, irqres->start, resource_size(irqres)); + if (IS_ERR(ddr_pmu->irq_reg)) { + dev_err(&pdev->dev, "ioremap failed for ddr_pmu irq resource\n"); + return PTR_ERR(ddr_pmu->irq_reg); + } + } else { + ddr_pmu->irq_reg = ddr_pmu->cfg_base + 0x4; } + phytium_ddr_pmu_reset(ddr_pmu); + return 0; } @@ -568,6 +581,10 @@ static int phytium_ddr_pmu_dev_probe(struct platform_device *pdev, { int ret; + ret = phytium_ddr_pmu_version(pdev, ddr_pmu); + if (ret) + return ret; + ret = phytium_ddr_pmu_init_data(pdev, ddr_pmu); if (ret) return ret; @@ -598,8 +615,8 @@ static int phytium_ddr_pmu_probe(struct platform_device *pdev) if (ret) return ret; - ret = cpuhp_state_add_instance( - phytium_ddr_pmu_hp_state, &ddr_pmu->node); + ret = cpuhp_state_add_instance(phytium_ddr_pmu_hp_state, + &ddr_pmu->node); if (ret) { dev_err(&pdev->dev, "Error %d registering hotplug;\n", ret); return ret; @@ -621,22 +638,19 @@ static int phytium_ddr_pmu_probe(struct platform_device *pdev) .stop = phytium_ddr_pmu_event_stop, .read = phytium_ddr_pmu_event_update, .attr_groups = phytium_ddr_pmu_attr_groups, - .capabilities = PERF_PMU_CAP_NO_EXCLUDE, }; ret = perf_pmu_register(&ddr_pmu->pmu, name, -1); if (ret) { dev_err(ddr_pmu->dev, "DDR PMU register failed!\n"); - cpuhp_state_remove_instance_nocalls( - phytium_ddr_pmu_hp_state, + cpuhp_state_remove_instance_nocalls(phytium_ddr_pmu_hp_state, &ddr_pmu->node); } phytium_ddr_pmu_enable_clk(ddr_pmu); - pr_info("Phytium DDR PMU: "); - pr_info(" die_id = %d ddr_id = %d pmu_id = %d.\n", ddr_pmu->die_id, - ddr_pmu->ddr_id, ddr_pmu->pmu_id); + pr_info("die%d_ddr%d_pmu%d on cpu%d.\n", ddr_pmu->die_id, + ddr_pmu->ddr_id, ddr_pmu->pmu_id, ddr_pmu->on_cpu); return ret; } @@ -648,8 +662,8 @@ static int phytium_ddr_pmu_remove(struct platform_device *pdev) phytium_ddr_pmu_disable_clk(ddr_pmu); perf_pmu_unregister(&ddr_pmu->pmu); - cpuhp_state_remove_instance_nocalls( - phytium_ddr_pmu_hp_state, &ddr_pmu->node); + cpuhp_state_remove_instance_nocalls(phytium_ddr_pmu_hp_state, + &ddr_pmu->node); return 0; } @@ -751,3 +765,4 @@ MODULE_DESCRIPTION("Phytium DDR PMU driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DDR_PERF_DRIVER_VERSION); MODULE_AUTHOR("Hu Xianghua "); +MODULE_AUTHOR("Tan Rui "); From b4bda1c3257d626fdd27e685e0d2baaa0436e92a Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Fri, 8 May 2026 13:51:29 +0800 Subject: [PATCH 55/81] drivers/perf:phytium: Add PCIe PMU Support for PS240XX. Add a new HID to differentiate between PS230XX and PS240XX PCIe PMU and add support for PS240XX platforms. Signed-off-by: Zhang Fuxiang Signed-off-by: Tan Rui Signed-off-by: Wang Yinfeng --- drivers/perf/phytium/phytium_pcie_pmu.c | 413 ++++++++++++++---------- 1 file changed, 240 insertions(+), 173 deletions(-) diff --git a/drivers/perf/phytium/phytium_pcie_pmu.c b/drivers/perf/phytium/phytium_pcie_pmu.c index b8aaee0048f7c..b280ced0aecf2 100644 --- a/drivers/perf/phytium/phytium_pcie_pmu.c +++ b/drivers/perf/phytium/phytium_pcie_pmu.c @@ -23,6 +23,7 @@ #include #include #include +#include #if IS_ENABLED(CONFIG_ARM || CONFIG_ARM64) #include @@ -32,52 +33,54 @@ #undef pr_fmt #define pr_fmt(fmt) "phytium_pcie_pmu: " fmt -#define PCIE_PERF_DRIVER_VERSION "1.2.1" +#define PCIE_PERF_DRIVER_VERSION "1.3.0" #define PHYTIUM_PCIE_MAX_COUNTERS 18 -#define PCIE_START_TIMER 0x000 -#define PCIE_STOP_TIMER 0x004 -#define PCIE_CLEAR_EVENT 0x008 -#define PCIE_SET_TIMER_L 0x00c -#define PCIE_SET_TIMER_H 0x010 -#define PCIE_TRIG_MODE 0x014 - -#define PCIE_NOW_STATE 0x0e0 -#define PCIE_EVENT_CYCLES 0x0e4 -#define PCIE_TPOINT_END_L 0x0e4 -#define PCIE_TPOINT_END_H 0x0e8 -#define PCIE_STATE_STOP 0x0ec - -#define PCIE_EVENT_AW 0x100 -#define PCIE_EVENT_W_LAST 0x104 -#define PCIE_EVENT_B 0x108 -#define PCIE_EVENT_AR 0x10c -#define PCIE_EVENT_R_LAST 0x110 -#define PCIE_EVENT_R_FULL 0x114 -#define PCIE_EVENT_R_ERR 0x118 -#define PCIE_EVENT_W_ERR 0x11c -#define PCIE_EVENT_DELAY_RD 0x120 -#define PCIE_EVENT_DELAY_WR 0x124 -#define PCIE_EVENT_RD_MAX 0x128 -#define PCIE_EVENT_RD_MIN 0x12c -#define PCIE_EVENT_WR_MAX 0x130 -#define PCIE_EVENT_WR_MIN 0x134 - -#define PCIE_EVENT_W_DATA 0x200 -#define PCIE_W_DATA_BASE 0x200 - -#define PCIE_EVENT_RDELAY_TIME 0x300 -#define PCIE_RDELAY_TIME_BASE 0x300 - -#define PCIE_EVENT_WDELAY_TIME 0x700 -#define PCIE_WDELAY_TIME_BASE 0x700 - -#define PCIE_CLK_FRE 0xe00 -#define PCIE_DATA_WIDTH 0xe04 +#define PCIE_START_TIMER 0x000 +#define PCIE_STOP_TIMER 0x004 +#define PCIE_CLEAR_EVENT 0x008 + +#define PCIE_EVENT_CYCLES 0x0e4 +#define PCIE_TPOINT_END_L 0x0e4 +#define PCIE_TPOINT_END_H 0x0e8 +#define PCIE_STATE_STOP 0x0ec + +#define PCIE_EVENT_AW 0x100 +#define PCIE_EVENT_W_LAST 0x104 +#define PCIE_EVENT_B 0x108 +#define PCIE_EVENT_AR 0x10c +#define PCIE_EVENT_R_LAST 0x110 +#define PCIE_EVENT_R_FULL 0x114 +#define PCIE_EVENT_R_ERR 0x118 +#define PCIE_EVENT_W_ERR 0x11c +#define PCIE_EVENT_DELAY_RD 0x120 +#define PCIE_EVENT_DELAY_WR 0x124 +#define PCIE_EVENT_RD_MAX 0x128 +#define PCIE_EVENT_RD_MIN 0x12c +#define PCIE_EVENT_WR_MAX 0x130 +#define PCIE_EVENT_WR_MIN 0x134 + +#define PCIE_EVENT_W_DATA 0x200 +#define PCIE_W_DATA_BASE 0x200 + +#define PCIE_EVENT_RDELAY_TIME 0x300 +#define PCIE_RDELAY_TIME_BASE 0x300 + +#define PCIE_EVENT_WDELAY_TIME 0x700 +#define PCIE_WDELAY_TIME_BASE 0x700 + +#define PCIE_DATA_WIDTH 0xe04 +#define PCIE_PMU_OFL_STOP_TYPE_VAL 0x10 #define to_phytium_pcie_pmu(p) (container_of(p, struct phytium_pcie_pmu, pmu)) +enum { + PCIEV1P0 = 0x1, + PCIEV1P5 = 0x2, +}; + + static int phytium_pcie_pmu_hp_state; struct phytium_pcie_pmu_hwevents { @@ -88,17 +91,20 @@ struct phytium_pcie_pmu_hwevents { struct phytium_pcie_pmu { struct device *dev; void __iomem *base; - void __iomem *csr_base; + void __iomem *cfg_base; void __iomem *irq_reg; struct pmu pmu; struct phytium_pcie_pmu_hwevents pmu_events; u32 die_id; + u32 pcie_id; u32 pmu_id; int on_cpu; int irq; + int irq_bit; struct hlist_node node; int ctrler_id; int real_ctrler; + int ver; u32 clk_bits; }; @@ -162,7 +168,6 @@ static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, static struct attribute *phytium_pcie_pmu_format_attr[] = { PHYTIUM_PCIE_PMU_FORMAT_ATTR(event, "config:0-4"), PHYTIUM_PCIE_PMU_FORMAT_ATTR(ctrler, "config:8-10"), - PHYTIUM_PCIE_PMU_FORMAT_ATTR(timer, "config1:0-31"), NULL, }; @@ -221,11 +226,6 @@ static u32 phytium_pcie_pmu_get_event_ctrler(struct perf_event *event) return FIELD_GET(GENMASK(10, 8), event->attr.config); } -static u32 phytium_pcie_pmu_get_event_timer(struct perf_event *event) -{ - return FIELD_GET(GENMASK(31, 0), event->attr.config1); -} - static u64 phytium_pcie_pmu_read_counter(struct phytium_pcie_pmu *pcie_pmu, struct hw_perf_event *hwc) { @@ -235,12 +235,16 @@ static u64 phytium_pcie_pmu_read_counter(struct phytium_pcie_pmu *pcie_pmu, u64 val64 = 0; int i; u32 counter_offset = pcie_counter_reg_offset[idx]; + u32 rdelay_num = 127; if (!EVENT_VALID(idx)) { dev_err(pcie_pmu->dev, "Unsupported event index:%d!\n", idx); return 0; } + if (pcie_pmu->ver == PCIEV1P5 && pcie_pmu->pmu_id == 3) + rdelay_num = 63; + switch (idx) { case 0: cycle_l = readl(pcie_pmu->base + counter_offset); @@ -255,7 +259,7 @@ static u64 phytium_pcie_pmu_read_counter(struct phytium_pcie_pmu *pcie_pmu, } break; case 16: - for (i = 0; i <= 127; i = i + 2) { + for (i = 0; i <= rdelay_num; i = i + 2) { rdelay_l = readl(pcie_pmu->base + counter_offset + 4 * i); rdelay_h = readl(pcie_pmu->base + counter_offset + @@ -283,38 +287,40 @@ static void phytium_pcie_pmu_enable_clk(struct phytium_pcie_pmu *pcie_pmu) { u32 val; - val = readl(pcie_pmu->csr_base); + val = readl(pcie_pmu->cfg_base); val |= (pcie_pmu->clk_bits); - writel(val, pcie_pmu->csr_base); + writel(val, pcie_pmu->cfg_base); } static void phytium_pcie_pmu_disable_clk(struct phytium_pcie_pmu *pcie_pmu) { u32 val; - val = readl(pcie_pmu->csr_base); + val = readl(pcie_pmu->cfg_base); val &= ~(pcie_pmu->clk_bits); - writel(val, pcie_pmu->csr_base); + writel(val, pcie_pmu->cfg_base); } static void phytium_pcie_pmu_select_ctrler(struct phytium_pcie_pmu *pcie_pmu) { - u32 val, offset = 0; - - if (pcie_pmu->pmu_id != 2) - offset = 0xc; + u32 val, offset; + u32 mask = 0xfffffffc; - val = readl(pcie_pmu->csr_base + offset); - - if (pcie_pmu->pmu_id == 2) { - val &= 0xffffffcf; - val |= pcie_pmu->real_ctrler; + if (pcie_pmu->ver == PCIEV1P0) { + if (pcie_pmu->pmu_id == 2) { + mask = 0xffffffcf; + offset = 0x0; + } else + offset = 0xc; } else { - val &= 0xfffffffc; - val |= pcie_pmu->real_ctrler; + offset = 0x170; } - writel(val, pcie_pmu->csr_base + offset); + val = readl(pcie_pmu->cfg_base + offset); + val &= mask; + val |= pcie_pmu->real_ctrler; + writel(val, pcie_pmu->cfg_base + offset); + } static void @@ -335,29 +341,6 @@ phytium_pcie_pmu_stop_all_counters(struct phytium_pcie_pmu *pcie_pmu) writel(0x1, pcie_pmu->base + PCIE_STOP_TIMER); } -static void phytium_pcie_pmu_set_timer(struct phytium_pcie_pmu *pcie_pmu, - u32 th_val) -{ - u32 val; - - val = readl(pcie_pmu->base + PCIE_SET_TIMER_L); - val = readl(pcie_pmu->base + PCIE_SET_TIMER_H); - - writel(th_val, pcie_pmu->base + PCIE_SET_TIMER_L); - writel(0, pcie_pmu->base + PCIE_SET_TIMER_H); -} - -static void phytium_pcie_pmu_reset_timer(struct phytium_pcie_pmu *pcie_pmu) -{ - u32 val; - - val = readl(pcie_pmu->base + PCIE_SET_TIMER_L); - val = readl(pcie_pmu->base + PCIE_SET_TIMER_H); - - writel(0xFFFFFFFF, pcie_pmu->base + PCIE_SET_TIMER_L); - writel(0xFFFFFFFF, pcie_pmu->base + PCIE_SET_TIMER_H); -} - static unsigned long phytium_pcie_pmu_get_stop_state(struct phytium_pcie_pmu *pcie_pmu) { @@ -407,7 +390,7 @@ int phytium_pcie_pmu_event_init(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; struct phytium_pcie_pmu *pcie_pmu; - u32 event_ctrler, event_timer; + u32 event_ctrler; if (event->attr.type != event->pmu->type) return -ENOENT; @@ -428,57 +411,83 @@ int phytium_pcie_pmu_event_init(struct perf_event *event) if (pcie_pmu->on_cpu == -1) return -EINVAL; - event_timer = phytium_pcie_pmu_get_event_timer(event); - if (event_timer != 0) - phytium_pcie_pmu_set_timer(pcie_pmu, event_timer); - - event_ctrler = phytium_pcie_pmu_get_event_ctrler(event); - switch (pcie_pmu->pmu_id) { - case 0: - if (event_ctrler != 0) { - dev_warn(pcie_pmu->dev, - "Wrong ctrler id(%d) for pcie-pmu0!\n", - event_ctrler); - return -EINVAL; + if (pcie_pmu->ver == PCIEV1P5) { + event_ctrler = phytium_pcie_pmu_get_event_ctrler(event); + if (pcie_pmu->pmu_id == 2) { + if (event_ctrler == 0) + event_ctrler = 2; + else if ((event_ctrler < 2) || (event_ctrler > 3)) { + dev_warn(pcie_pmu->dev, "Wrong ctrler id(%d) for pcie-pmu2!\n", + event_ctrler); + return -EINVAL; + } + if (pcie_pmu->ctrler_id != event_ctrler) { + pcie_pmu->ctrler_id = event_ctrler; + pcie_pmu->real_ctrler = pcie_pmu->ctrler_id; + phytium_pcie_pmu_select_ctrler(pcie_pmu); + } + } else { + if (event_ctrler != 0) { + dev_warn(pcie_pmu->dev, "Don't need to set ctrler id(%d) for pcie-pmu%d!\n", + event_ctrler, pcie_pmu->pmu_id); + return -EINVAL; + } + pcie_pmu->ctrler_id = pcie_pmu->pmu_id; + pcie_pmu->real_ctrler = pcie_pmu->ctrler_id; } - break; - case 1: - if ((event_ctrler < 1) || (event_ctrler > 3)) { - dev_warn(pcie_pmu->dev, - "Wrong ctrler id(%d) for pcie-pmu1!\n", - event_ctrler); + } else { + event_ctrler = phytium_pcie_pmu_get_event_ctrler(event); + switch (pcie_pmu->pmu_id) { + case 0: + if (event_ctrler != 0) { + dev_warn(pcie_pmu->dev, + "Wrong ctrler id(%d) for pcie-pmu0!\n", + event_ctrler); + return -EINVAL; + } + break; + case 1: + if (event_ctrler == 0) + event_ctrler = 1; + else if ((event_ctrler < 1) || (event_ctrler > 3)) { + dev_warn(pcie_pmu->dev, + "Wrong ctrler id(%d) for pcie-pmu1!\n", + event_ctrler); + return -EINVAL; + } + break; + case 2: + if (event_ctrler == 0) + event_ctrler = 4; + else if ((event_ctrler < 4) || (event_ctrler > 7)) { + dev_warn(pcie_pmu->dev, + "Wrong ctrler id(%d) for pcie-pmu2!\n", + event_ctrler); + return -EINVAL; + } + break; + default: + dev_err(pcie_pmu->dev, "Unsupported pmu id:%d!\n", + pcie_pmu->pmu_id); return -EINVAL; } - break; - case 2: - if ((event_ctrler < 4) || (event_ctrler > 7)) { - dev_warn(pcie_pmu->dev, - "Wrong ctrler id(%d) for pcie-pmu2!\n", - event_ctrler); + + pcie_pmu->ctrler_id = event_ctrler; + switch (pcie_pmu->pmu_id) { + case 0: + case 1: + pcie_pmu->real_ctrler = pcie_pmu->ctrler_id; + break; + case 2: + pcie_pmu->real_ctrler = (pcie_pmu->ctrler_id - 4) * 16; + break; + default: + dev_err(pcie_pmu->dev, "Unsupported pmu id:%d!\n", + pcie_pmu->pmu_id); return -EINVAL; } - break; - default: - dev_err(pcie_pmu->dev, "Unsupported pmu id:%d!\n", - pcie_pmu->pmu_id); - return -EINVAL; - } - - pcie_pmu->ctrler_id = event_ctrler; - switch (pcie_pmu->pmu_id) { - case 0: - case 1: - pcie_pmu->real_ctrler = pcie_pmu->ctrler_id; - break; - case 2: - pcie_pmu->real_ctrler = (pcie_pmu->ctrler_id - 4) * 16; - break; - default: - dev_err(pcie_pmu->dev, "Unsupported pmu id:%d!\n", - pcie_pmu->pmu_id); - return -EINVAL; + phytium_pcie_pmu_select_ctrler(pcie_pmu); } - phytium_pcie_pmu_select_ctrler(pcie_pmu); hwc->idx = -1; hwc->config_base = event->attr.config; @@ -537,17 +546,10 @@ void phytium_pcie_pmu_event_del(struct perf_event *event, int flags) { struct phytium_pcie_pmu *pcie_pmu = to_phytium_pcie_pmu(event->pmu); struct hw_perf_event *hwc = &event->hw; - unsigned long val; - u32 event_timer; phytium_pcie_pmu_event_stop(event, PERF_EF_UPDATE); - val = phytium_pcie_pmu_get_irq_flag(pcie_pmu); - val = phytium_pcie_pmu_get_stop_state(pcie_pmu); - phytium_pcie_pmu_unmark_event(pcie_pmu, hwc->idx); - event_timer = phytium_pcie_pmu_get_event_timer(event); - if (event_timer != 0) - phytium_pcie_pmu_reset_timer(pcie_pmu); + phytium_pcie_pmu_unmark_event(pcie_pmu, hwc->idx); perf_event_update_userpage(event); pcie_pmu->pmu_events.hw_events[hwc->idx] = NULL; @@ -575,10 +577,15 @@ void phytium_pcie_pmu_disable(struct pmu *pmu) phytium_pcie_pmu_stop_all_counters(pcie_pmu); } +void phytium_pcie_pmu_reset(struct phytium_pcie_pmu *pcie_pmu) +{ + phytium_pcie_pmu_disable_clk(pcie_pmu); + phytium_pcie_pmu_clear_all_counters(pcie_pmu); +} + static const struct acpi_device_id phytium_pcie_pmu_acpi_match[] = { - { - "PHYT0044", - }, + { "PHYT0044", }, + { "PHYT0068", }, {}, }; MODULE_DEVICE_TABLE(acpi, phytium_pcie_pmu_acpi_match); @@ -594,7 +601,7 @@ static irqreturn_t phytium_pcie_pmu_overflow_handler(int irq, void *dev_id) overflown = phytium_pcie_pmu_get_irq_flag(pcie_pmu); - if (!test_bit(pcie_pmu->pmu_id + 4, &overflown)) + if (!test_bit(pcie_pmu->irq_bit, &overflown)) return IRQ_NONE; stop_state = phytium_pcie_pmu_get_stop_state(pcie_pmu); @@ -607,7 +614,8 @@ static irqreturn_t phytium_pcie_pmu_overflow_handler(int irq, void *dev_id) phytium_pcie_pmu_event_update(event); } phytium_pcie_pmu_clear_all_counters(pcie_pmu); - phytium_pcie_pmu_start_all_counters(pcie_pmu); + if ((stop_state & PCIE_PMU_OFL_STOP_TYPE_VAL) == 0) + phytium_pcie_pmu_start_all_counters(pcie_pmu); return IRQ_HANDLED; } @@ -618,6 +626,25 @@ static irqreturn_t phytium_pcie_pmu_overflow_handler(int irq, void *dev_id) return IRQ_NONE; } +static int phytium_pcie_pmu_version(struct platform_device *pdev, + struct phytium_pcie_pmu *pcie_pmu) +{ + struct acpi_device *acpi_dev; + + acpi_dev = ACPI_COMPANION(&pdev->dev); + if (!strcmp(acpi_device_hid(acpi_dev), "PHYT0044")) { + pcie_pmu->ver = PCIEV1P0; + } else if (!strcmp(acpi_device_hid(acpi_dev), "PHYT0068")) { + pcie_pmu->ver = PCIEV1P5; + } else { + dev_err(&pdev->dev, "The current driver does not support this device.\n"); + return -ENODEV; + + } + + return 0; +} + static int phytium_pcie_pmu_init_irq(struct phytium_pcie_pmu *pcie_pmu, struct platform_device *pdev) { @@ -643,7 +670,7 @@ static int phytium_pcie_pmu_init_irq(struct phytium_pcie_pmu *pcie_pmu, } static int phytium_pcie_pmu_init_data(struct platform_device *pdev, - struct phytium_pcie_pmu *pcie_pmu) + struct phytium_pcie_pmu *pcie_pmu) { struct resource *res, *clkres, *irqres; @@ -659,20 +686,46 @@ static int phytium_pcie_pmu_init_data(struct platform_device *pdev, return -EINVAL; } - switch (pcie_pmu->pmu_id) { - case 0: - pcie_pmu->clk_bits = 0x1; - break; - case 1: - pcie_pmu->clk_bits = 0xe; - break; - case 2: - pcie_pmu->clk_bits = 0xf; - break; - default: - dev_err(&pdev->dev, "Unsupported pmu id:%d!\n", - pcie_pmu->pmu_id); - break; + if (pcie_pmu->ver == PCIEV1P0) { + switch (pcie_pmu->pmu_id) { + case 0: + pcie_pmu->clk_bits = 0x1; + break; + case 1: + pcie_pmu->clk_bits = 0xe; + break; + case 2: + pcie_pmu->clk_bits = 0xf; + break; + default: + dev_err(&pdev->dev, "Unsupported pmu id:%d!\n", pcie_pmu->pmu_id); + break; + } + + pcie_pmu->irq_bit = pcie_pmu->pmu_id + 4; + } else { + if (device_property_read_u32(&pdev->dev, "phytium,pcie-id", &pcie_pmu->pcie_id)) { + dev_err(&pdev->dev, "Can not read phytium,pcie-id!\n"); + return -EINVAL; + } + + switch (pcie_pmu->pmu_id) { + case 0: + case 3: + pcie_pmu->clk_bits = 0x1; + break; + case 1: + pcie_pmu->clk_bits = 0x2; + break; + case 2: + pcie_pmu->clk_bits = 0xc; + break; + default: + dev_err(&pdev->dev, "Unsupported pmu id:%d!\n", pcie_pmu->pmu_id); + break; + } + + pcie_pmu->irq_bit = pcie_pmu->pcie_id * 4 + pcie_pmu->pmu_id + 16; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -688,12 +741,12 @@ static int phytium_pcie_pmu_init_data(struct platform_device *pdev, return -EINVAL; } - pcie_pmu->csr_base = + pcie_pmu->cfg_base = devm_ioremap(&pdev->dev, clkres->start, resource_size(clkres)); - if (IS_ERR(pcie_pmu->csr_base)) { + if (IS_ERR(pcie_pmu->cfg_base)) { dev_err(&pdev->dev, "ioremap failed for pcie_pmu csr resource\n"); - return PTR_ERR(pcie_pmu->csr_base); + return PTR_ERR(pcie_pmu->cfg_base); } irqres = platform_get_resource(pdev, IORESOURCE_MEM, 2); @@ -711,14 +764,20 @@ static int phytium_pcie_pmu_init_data(struct platform_device *pdev, return PTR_ERR(pcie_pmu->irq_reg); } + phytium_pcie_pmu_reset(pcie_pmu); + return 0; } static int phytium_pcie_pmu_dev_probe(struct platform_device *pdev, - struct phytium_pcie_pmu *pcie_pmu) + struct phytium_pcie_pmu *pcie_pmu) { int ret; + ret = phytium_pcie_pmu_version(pdev, pcie_pmu); + if (ret) + return ret; + ret = phytium_pcie_pmu_init_data(pdev, pcie_pmu); if (ret) return ret; @@ -756,8 +815,12 @@ static int phytium_pcie_pmu_probe(struct platform_device *pdev) return ret; } - name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "phyt%u_pcie_pmu%u", - pcie_pmu->die_id, pcie_pmu->pmu_id); + if (pcie_pmu->ver == PCIEV1P0) + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "phyt%u_pcie_pmu%u", + pcie_pmu->die_id, pcie_pmu->pmu_id); + else + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "phyt%u_pcie%u_pmu%u", + pcie_pmu->die_id, pcie_pmu->pcie_id, pcie_pmu->pmu_id); pcie_pmu->pmu = (struct pmu){ .name = name, .module = THIS_MODULE, @@ -771,7 +834,6 @@ static int phytium_pcie_pmu_probe(struct platform_device *pdev) .stop = phytium_pcie_pmu_event_stop, .read = phytium_pcie_pmu_event_update, .attr_groups = phytium_pcie_pmu_attr_groups, - .capabilities = PERF_PMU_CAP_NO_EXCLUDE, }; ret = perf_pmu_register(&pcie_pmu->pmu, name, -1); @@ -784,9 +846,12 @@ static int phytium_pcie_pmu_probe(struct platform_device *pdev) phytium_pcie_pmu_enable_clk(pcie_pmu); - pr_info("Phytium PCIe PMU: "); - pr_info("die_id = %d pmu_id = %d.\n", pcie_pmu->die_id, - pcie_pmu->pmu_id); + if (pcie_pmu->ver == PCIEV1P0) + pr_info("die%d_pcie_pmu%d on cpu%d.\n", + pcie_pmu->die_id, pcie_pmu->pmu_id, pcie_pmu->on_cpu); + else + pr_info("die%d_pcie%d_pmu%d on cpu%d.\n", + pcie_pmu->die_id, pcie_pmu->pcie_id, pcie_pmu->pmu_id, pcie_pmu->on_cpu); return ret; } @@ -828,6 +893,7 @@ int phytium_pcie_pmu_online_cpu(unsigned int cpu, struct hlist_node *node) pcie_pmu->on_cpu = cpu; WARN_ON(irq_set_affinity_hint(pcie_pmu->irq, cpumask_of(cpu))); } + return 0; } @@ -874,8 +940,8 @@ static int __init phytium_pcie_pmu_module_init(void) phytium_pcie_pmu_hp_state = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, - "perf/phytium/pciepmu:online", - phytium_pcie_pmu_online_cpu, phytium_pcie_pmu_offline_cpu); + "perf/phytium/pciepmu:online", phytium_pcie_pmu_online_cpu, + phytium_pcie_pmu_offline_cpu); if (phytium_pcie_pmu_hp_state < 0) { pr_err("PCIE PMU: setup hotplug, ret = %d\n", phytium_pcie_pmu_hp_state); @@ -902,3 +968,4 @@ MODULE_DESCRIPTION("Phytium PCIe PMU driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(PCIE_PERF_DRIVER_VERSION); MODULE_AUTHOR("Hu Xianghua "); +MODULE_AUTHOR("Tan Rui "); From a2ec294559ece73d6c7ced26632a15ac0eec2094 Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Fri, 8 May 2026 14:00:37 +0800 Subject: [PATCH 56/81] drivers/perf: phytium: Add DDR PMU Support for PD2408 SoCs Add DDR controller performance monitoring unit (PMU) Support for Phytium PD2408 SoCs. Signed-off-by: Zhang Fuxiang Signed-off-by: Tan Rui Signed-off-by: Wang Yinfeng --- MAINTAINERS | 1 + drivers/perf/phytium/Kconfig | 8 + drivers/perf/phytium/Makefile | 1 + drivers/perf/phytium/phytium_dmu_pmu_pd2408.c | 708 ++++++++++++++++++ 4 files changed, 718 insertions(+) create mode 100644 drivers/perf/phytium/phytium_dmu_pmu_pd2408.c diff --git a/MAINTAINERS b/MAINTAINERS index c7ebccca27be8..4eeb9659fc464 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17548,6 +17548,7 @@ F: Documentation/devicetree/bindings/usb/phytium,usb2-2.0.yaml F: Documentation/devicetree/bindings/usb/phytium,usb2.yaml F: Documentation/devicetree/bindings/usb/phytium.role-sw.yaml F: drivers/usb/phytium/* +F: drivers/perf/phytium/phytium_dmu_pmu_pd2408.c F: drivers/usb/phytium/phytium_usb_v2* F: drivers/usb/typec/role-switch-phytium.c F: arch/arm64/boot/dts/phytium/* diff --git a/drivers/perf/phytium/Kconfig b/drivers/perf/phytium/Kconfig index e9dabba7227eb..c1a9d7b52cd41 100644 --- a/drivers/perf/phytium/Kconfig +++ b/drivers/perf/phytium/Kconfig @@ -7,6 +7,14 @@ menuconfig PHYTIUM_PMU if PHYTIUM_PMU +config PHYT_DMU_PMU_PD2408 + tristate "Phytium PD2408 SoC DDR PMU driver" + depends on (ARCH_PHYTIUM && ACPI && ARM_PHYTIUM_DMU_DEVFREQ) || COMPILE_TEST || (ARCH_PHYTIUM && ACPI && !ARM_PHYTIUM_DMU_DEVFREQ) + default m + help + Provides support for Phytium PD2408 SoC DDR Controller performance + monitoring unit (PMU). + config PHYT_DDR_PMU tristate "Phytium SoC DDR PMU driver" depends on (ARCH_PHYTIUM && ACPI) || COMPILE_TEST diff --git a/drivers/perf/phytium/Makefile b/drivers/perf/phytium/Makefile index af37afc6920c5..1f9f1e5bf47d4 100644 --- a/drivers/perf/phytium/Makefile +++ b/drivers/perf/phytium/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 # +obj-$(CONFIG_PHYT_DMU_PMU_PD2408) += phytium_dmu_pmu_pd2408.o obj-$(CONFIG_PHYT_DDR_PMU) += phytium_ddr_pmu.o obj-$(CONFIG_PHYT_PCIE_PMU) += phytium_pcie_pmu.o diff --git a/drivers/perf/phytium/phytium_dmu_pmu_pd2408.c b/drivers/perf/phytium/phytium_dmu_pmu_pd2408.c new file mode 100644 index 0000000000000..df873b018c0e4 --- /dev/null +++ b/drivers/perf/phytium/phytium_dmu_pmu_pd2408.c @@ -0,0 +1,708 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium PD2408 DMU performance monitoring unit support + * + * Copyright (c) 2025, Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef pr_fmt +#define pr_fmt(fmt) "pd2408_dmu_pmu: " fmt + +#define DMU_PERF_DRIVER_VERSION "1.0.0" + +#define DMU_PMU_TIMER_START 0x0 +#define DMU_PMU_TIMER_STOP 0x4 +#define DMU_PMU_CLEAR_EVENT 0x8 +#define DMU_PMU_SET_TIMER_L 0xc +#define DMU_PMU_SET_TIMER_H 0x10 +#define DMU_PMU_AXI_MONITOR_EN 0x1c +#define DMU_PMU_TIMER_INT_CLEAR 0x2c +#define DMU_PMU_AXI_MONITOR_INT_CLEAR 0x30 +#define DMU_PMU_TIMER_INT_STA 0x40 +#define DMU_PMU_AXI_MONITOR_INT_STA 0x44 +#define DMU_PMU_TIMER_INT_MASK 0x54 +#define DMU_PMU_AXI_MONITOR_INT_MASK 0x58 + +#define DMU_PMU_EVENT_CYCLES 0x208 + +#define DMU_PMU_EVENT_AXI_READ_CMD_CNT 0x074 +#define DMU_PMU_EVENT_AXI_WRITE_CMD_CNT 0x07c +#define DMU_PMU_EVENT_AXI_READ_FLUX_CNT 0x084 +#define DMU_PMU_EVENT_AXI_WRITE_FLUX_CNT 0x08c + +#define DMU_PMU_MAX_COUNTERS 5 +#define DMU_PMU_MAX_COUNTERS_TIMER 1 +#define DMU_PMU_MAX_COUNTERS_AXI 4 + +#define ALL_EVENT_CLEAR_BIT 0x1 +#define DMU_PMU_TIMER_OPT_BIT 0x1 +#define AXI_MONITOR_OPT_BIT 0x01010101 + +#define DMU_PMU_NOTICE_START 0x0 +#define DMU_PMU_NOTICE_STOP 0x1 + +#define to_pd2408_dmu_pmu(p) (container_of(p, struct pd2408_dmu_pmu, pmu)) + +#define GET_DMU_EVENTID(hwc) (hwc->config_base & 0x7) +#define EVENT_VALID(idx) ((idx >= 0) && (idx < DMU_PMU_MAX_COUNTERS)) + +static int pd2408_dmu_pmu_hp_state; +int used_event; + +struct pd2408_dmu_pmu_hwevents { + struct perf_event *hw_events[DMU_PMU_MAX_COUNTERS]; + DECLARE_BITMAP(used_event_mask, DMU_PMU_MAX_COUNTERS); +}; + +struct pd2408_dmu_pmu { + struct device *dev; + void __iomem *base; + struct pmu pmu; + struct pd2408_dmu_pmu_hwevents pmu_events; + struct hlist_node node; + int on_cpu; + int irq; + u32 soc_version; + u32 dmu_id; + bool used_flag; +}; + +static const u32 dmu_counter_reg_offset[] = { + DMU_PMU_EVENT_CYCLES, + DMU_PMU_EVENT_AXI_WRITE_FLUX_CNT, DMU_PMU_EVENT_AXI_READ_FLUX_CNT, + DMU_PMU_EVENT_AXI_WRITE_CMD_CNT, DMU_PMU_EVENT_AXI_READ_CMD_CNT +}; + +ssize_t pd2408_dmu_pmu_format_sysfs_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct dev_ext_attribute *eattr; + + eattr = container_of(attr, struct dev_ext_attribute, attr); + + return sprintf(buf, "%s\n", (char *)eattr->var); +} + +ssize_t pd2408_dmu_pmu_event_sysfs_show(struct device *dev, + struct device_attribute *attr, + char *page) +{ + struct dev_ext_attribute *eattr; + + eattr = container_of(attr, struct dev_ext_attribute, attr); + + return sprintf(page, "config=0x%lx\n", (unsigned long)eattr->var); +} + +static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct pd2408_dmu_pmu *dmu_pmu = + to_pd2408_dmu_pmu(dev_get_drvdata(dev)); + + return cpumap_print_to_pagebuf(true, buf, cpumask_of(dmu_pmu->on_cpu)); +} + +#define PHYTIUM_PMU_ATTR(_name, _func, _config) \ + (&((struct dev_ext_attribute[]){ \ + { __ATTR(_name, 0444, _func, NULL), (void *)_config } })[0] \ + .attr.attr) + +#define PHYTIUM_DMU_PMU_FORMAT_ATTR(_name, _config) \ + PHYTIUM_PMU_ATTR(_name, pd2408_dmu_pmu_format_sysfs_show, \ + (void *)_config) + +#define PHYTIUM_DMU_PMU_EVENT_ATTR(_name, _config) \ + PHYTIUM_PMU_ATTR(_name, pd2408_dmu_pmu_event_sysfs_show, \ + (unsigned long)_config) + +static struct attribute *pd2408_dmu_pmu_format_attr[] = { + PHYTIUM_DMU_PMU_FORMAT_ATTR(event, "config:0-2"), + NULL, +}; + +static const struct attribute_group pd2408_dmu_pmu_format_group = { + .name = "format", + .attrs = pd2408_dmu_pmu_format_attr, +}; + +static struct attribute *pd2408_dmu_pmu_events_attr[] = { + PHYTIUM_DMU_PMU_EVENT_ATTR(cycles, 0x00), + PHYTIUM_DMU_PMU_EVENT_ATTR(axi_write_flux, 0x01), + PHYTIUM_DMU_PMU_EVENT_ATTR(axi_read_flux, 0x02), + PHYTIUM_DMU_PMU_EVENT_ATTR(axi_write_cmd, 0x03), + PHYTIUM_DMU_PMU_EVENT_ATTR(axi_read_cmd, 0x04), + NULL, +}; + +static const struct attribute_group pd2408_dmu_pmu_events_group = { + .name = "events", + .attrs = pd2408_dmu_pmu_events_attr, +}; + +static DEVICE_ATTR_RO(cpumask); + +static struct attribute *pd2408_dmu_pmu_cpumask_attrs[] = { + &dev_attr_cpumask.attr, + NULL, +}; + +static const struct attribute_group pd2408_dmu_pmu_cpumask_attr_group = { + .attrs = pd2408_dmu_pmu_cpumask_attrs, +}; + +static const struct attribute_group *pd2408_dmu_pmu_attr_groups[] = { + &pd2408_dmu_pmu_format_group, + &pd2408_dmu_pmu_events_group, + &pd2408_dmu_pmu_cpumask_attr_group, + NULL, +}; + +#if IS_ENABLED(CONFIG_ARM_PHYTIUM_DMU_DEVFREQ) +extern struct blocking_notifier_head dmu_pmu_notifier_chain; + +void pd2408_dmu_pmu_notifier_chain_trigger(struct pd2408_dmu_pmu *dmu_pmu, int event) +{ + static bool start_flag; + + if ((event == DMU_PMU_NOTICE_START) && (start_flag == false)) { + blocking_notifier_call_chain(&dmu_pmu_notifier_chain, event, NULL); + start_flag = true; + dmu_pmu->used_flag = true; + } else if ((event == DMU_PMU_NOTICE_STOP) && (start_flag == true)) { + blocking_notifier_call_chain(&dmu_pmu_notifier_chain, event, NULL); + start_flag = false; + dmu_pmu->used_flag = false; + } +} +#endif + +static u64 pd2408_dmu_pmu_read_counter(struct pd2408_dmu_pmu *dmu_pmu, + struct hw_perf_event *hwc) +{ + u32 val32_l, val32_h, idx, counter_offset; + u64 val64; + + idx = GET_DMU_EVENTID(hwc); + counter_offset = dmu_counter_reg_offset[idx]; + + if (!EVENT_VALID(idx)) { + dev_err(dmu_pmu->dev, "Unsupported event index:%d!\n", idx); + return 0; + } + + val32_l = readl(dmu_pmu->base + counter_offset); + val32_h = readl(dmu_pmu->base + counter_offset + 4); + val64 = (u64)val32_h << 32 | (u64)val32_l; + + return val64; +} + +static void pd2408_dmu_pmu_clear_all_counters(struct pd2408_dmu_pmu *dmu_pmu) +{ + writel(BIT(0), dmu_pmu->base + DMU_PMU_CLEAR_EVENT); +} + +static void pd2408_dmu_pmu_disable_axi_cmd_events(struct pd2408_dmu_pmu *dmu_pmu) +{ + writel(0x0, dmu_pmu->base + DMU_PMU_AXI_MONITOR_EN); +} + +static void pd2408_dmu_pmu_mask_all_irq(struct pd2408_dmu_pmu *dmu_pmu) +{ + writel(DMU_PMU_TIMER_OPT_BIT, dmu_pmu->base + DMU_PMU_TIMER_INT_MASK); + writel(AXI_MONITOR_OPT_BIT, dmu_pmu->base + DMU_PMU_AXI_MONITOR_INT_MASK); +} + +static void pd2408_dmu_pmu_start_all_counters(struct pd2408_dmu_pmu *dmu_pmu) +{ + writel(BIT(0), dmu_pmu->base + DMU_PMU_TIMER_START); +} + +static void pd2408_dmu_pmu_stop_all_counters(struct pd2408_dmu_pmu *dmu_pmu) +{ + writel(BIT(0), dmu_pmu->base + DMU_PMU_TIMER_STOP); +} + +static void pd2408_dmu_pmu_reset_timer(struct pd2408_dmu_pmu *dmu_pmu) +{ + writel(0xFFFFFFFF, dmu_pmu->base + DMU_PMU_SET_TIMER_L); + writel(0xFFFFFFFF, dmu_pmu->base + DMU_PMU_SET_TIMER_H); +} + +static void pd2408_dmu_pmu_enable_events(struct pd2408_dmu_pmu *dmu_pmu, int idx) +{ + u8 en_bit; + u32 en_offset, irq_offset, val; + + if (idx == 0) { + en_bit = 0; + en_offset = 0; + irq_offset = DMU_PMU_TIMER_INT_MASK; + } else { + en_bit = (idx - 1) * 8; + en_offset = DMU_PMU_AXI_MONITOR_EN; + irq_offset = DMU_PMU_AXI_MONITOR_INT_MASK; + } + + if (en_offset) { + val = readl(dmu_pmu->base + en_offset); + val |= BIT(en_bit); + writel(val, dmu_pmu->base + en_offset); + } + + val = readl(dmu_pmu->base + irq_offset); + val &= ~BIT(en_bit); + writel(val, dmu_pmu->base + irq_offset); +} + +static int pd2408_dmu_pmu_mark_event(struct perf_event *event) +{ + struct pd2408_dmu_pmu *dmu_pmu = to_pd2408_dmu_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + unsigned long *used_mask = dmu_pmu->pmu_events.used_event_mask; + + int idx = GET_DMU_EVENTID(hwc); + + if (test_bit(idx, used_mask)) + return -EAGAIN; + + set_bit(idx, used_mask); + + return idx; +} + +static void pd2408_dmu_pmu_unmark_event(struct pd2408_dmu_pmu *dmu_pmu, + int idx) +{ + if (!EVENT_VALID(idx)) { + dev_err(dmu_pmu->dev, "Unsupported event index:%d!\n", idx); + return; + } + + clear_bit(idx, dmu_pmu->pmu_events.used_event_mask); +} + +int pd2408_dmu_pmu_event_init(struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; + struct pd2408_dmu_pmu *dmu_pmu; + + if (event->attr.type != event->pmu->type) + return -ENOENT; + + if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK) + return -EOPNOTSUPP; + + dmu_pmu = to_pd2408_dmu_pmu(event->pmu); + + if (event->cpu < 0) { + dev_warn(dmu_pmu->dev, "Can't provide per-task data!\n"); + return -EINVAL; + } + + if (event->attr.config > DMU_PMU_MAX_COUNTERS) + return -EINVAL; + + if (dmu_pmu->on_cpu == -1) + return -EINVAL; + + hwc->idx = -1; + hwc->config_base = event->attr.config; + + event->cpu = dmu_pmu->on_cpu; + used_event = 0; + + return 0; +} + +void pd2408_dmu_pmu_event_update(struct perf_event *event) +{ + struct pd2408_dmu_pmu *dmu_pmu = to_pd2408_dmu_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + u64 delta; + + pd2408_dmu_pmu_stop_all_counters(dmu_pmu); + delta = pd2408_dmu_pmu_read_counter(dmu_pmu, hwc); + local64_add(delta, &event->count); + +} + +void pd2408_dmu_pmu_event_start(struct perf_event *event, int flags) +{ + struct hw_perf_event *hwc = &event->hw; + + hwc->state = 0; + perf_event_update_userpage(event); +} + +void pd2408_dmu_pmu_event_stop(struct perf_event *event, int flags) +{ + struct hw_perf_event *hwc = &event->hw; + + hwc->state |= PERF_HES_STOPPED; + + if (flags & PERF_EF_UPDATE) + pd2408_dmu_pmu_event_update(event); +} + +int pd2408_dmu_pmu_event_add(struct perf_event *event, int flags) +{ + struct pd2408_dmu_pmu *dmu_pmu = to_pd2408_dmu_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + int idx; + + hwc->state |= PERF_HES_STOPPED; + + idx = pd2408_dmu_pmu_mark_event(event); + if (!EVENT_VALID(idx)) { + dev_err(dmu_pmu->dev, "Unsupported event index:%d!\n", idx); + return idx; + } +#if IS_ENABLED(CONFIG_ARM_PHYTIUM_DMU_DEVFREQ) + pd2408_dmu_pmu_notifier_chain_trigger(dmu_pmu, DMU_PMU_NOTICE_START); +#endif + event->hw.idx = idx; + dmu_pmu->pmu_events.hw_events[idx] = event; + + pd2408_dmu_pmu_enable_events(dmu_pmu, idx); + used_event += 1; + return 0; +} + +void pd2408_dmu_pmu_event_del(struct perf_event *event, int flags) +{ + struct pd2408_dmu_pmu *dmu_pmu = to_pd2408_dmu_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + + used_event -= 1; + pd2408_dmu_pmu_event_stop(event, PERF_EF_UPDATE); + + pd2408_dmu_pmu_unmark_event(dmu_pmu, hwc->idx); + + perf_event_update_userpage(event); + dmu_pmu->pmu_events.hw_events[hwc->idx] = NULL; +#if IS_ENABLED(CONFIG_ARM_PHYTIUM_DMU_DEVFREQ) + if (used_event == 0) + pd2408_dmu_pmu_notifier_chain_trigger(dmu_pmu, DMU_PMU_NOTICE_STOP); +#endif + +} + +void pd2408_dmu_pmu_enable(struct pmu *pmu) +{ + struct pd2408_dmu_pmu *dmu_pmu = to_pd2408_dmu_pmu(pmu); + int event_added; + + event_added = bitmap_weight(dmu_pmu->pmu_events.used_event_mask, + DMU_PMU_MAX_COUNTERS); + + if (event_added) { + pd2408_dmu_pmu_stop_all_counters(dmu_pmu); + pd2408_dmu_pmu_clear_all_counters(dmu_pmu); + pd2408_dmu_pmu_reset_timer(dmu_pmu); + pd2408_dmu_pmu_start_all_counters(dmu_pmu); + } +} + +void pd2408_dmu_pmu_disable(struct pmu *pmu) +{ + struct pd2408_dmu_pmu *dmu_pmu = to_pd2408_dmu_pmu(pmu); + int event_added; + + event_added = bitmap_weight(dmu_pmu->pmu_events.used_event_mask, + DMU_PMU_MAX_COUNTERS); + if (event_added && dmu_pmu->used_flag) { + pd2408_dmu_pmu_mask_all_irq(dmu_pmu); + pd2408_dmu_pmu_disable_axi_cmd_events(dmu_pmu); + } +} + +static const struct acpi_device_id pd2408_dmu_pmu_acpi_match[] = { + { "PHYT0069", }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, pd2408_dmu_pmu_acpi_match); + +static irqreturn_t pd2408_dmu_pmu_overflow_handler(int irq, void *dev_id) +{ + struct pd2408_dmu_pmu *dmu_pmu = dev_id; + struct perf_event *event; + int idx; + unsigned long *used_mask = dmu_pmu->pmu_events.used_event_mask; + u32 timer_int_sta, axi_int_sta; + + timer_int_sta = readl(dmu_pmu->base + DMU_PMU_TIMER_INT_STA); + axi_int_sta = readl(dmu_pmu->base + DMU_PMU_AXI_MONITOR_INT_STA); + + if ((timer_int_sta + axi_int_sta) == 0) + return IRQ_NONE; + + if (timer_int_sta) + writel(0x1, dmu_pmu->base + DMU_PMU_TIMER_INT_CLEAR); + + if (axi_int_sta) + writel(axi_int_sta, dmu_pmu->base + DMU_PMU_AXI_MONITOR_INT_CLEAR); + + if (!dmu_pmu->used_flag) { + pd2408_dmu_pmu_mask_all_irq(dmu_pmu); + return IRQ_HANDLED; + } + + for_each_set_bit(idx, used_mask, DMU_PMU_MAX_COUNTERS) { + event = dmu_pmu->pmu_events.hw_events[idx]; + if (!event) + continue; + pd2408_dmu_pmu_event_update(event); + } + writel(ALL_EVENT_CLEAR_BIT, dmu_pmu->base + DMU_PMU_CLEAR_EVENT); + pd2408_dmu_pmu_start_all_counters(dmu_pmu); + + return IRQ_HANDLED; +} + +static int pd2408_dmu_pmu_init_irq(struct pd2408_dmu_pmu *dmu_pmu, + struct platform_device *pdev) +{ + int irq, ret; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(&pdev->dev, irq, + pd2408_dmu_pmu_overflow_handler, + IRQF_NOBALANCING | IRQF_NO_THREAD | IRQF_SHARED, + dev_name(&pdev->dev), dmu_pmu); + if (ret < 0) { + dev_err(&pdev->dev, "Fail to request IRQ:%d ret:%d\n", irq, + ret); + return ret; + } + + dmu_pmu->irq = irq; + + return 0; +} + +static int pd2408_dmu_pmu_init_data(struct platform_device *pdev, + struct pd2408_dmu_pmu *dmu_pmu) +{ + struct resource *res; + + if (device_property_read_u32(&pdev->dev, "phytium,ddr-id", + &dmu_pmu->dmu_id)) { + dev_err(&pdev->dev, "Can not read phytium,ddr-id!\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dmu_pmu->base = devm_ioremap_resource(&pdev->dev, res); + + if (IS_ERR(dmu_pmu->base)) { + dev_err(&pdev->dev, + "ioremap failed for dmu_pmu base resource\n"); + return PTR_ERR(dmu_pmu->base); + } + + dmu_pmu->used_flag = true; + pd2408_dmu_pmu_mask_all_irq(dmu_pmu); + + return 0; +} + +static int pd2408_dmu_pmu_dev_probe(struct platform_device *pdev, + struct pd2408_dmu_pmu *dmu_pmu) +{ + int ret; + + ret = pd2408_dmu_pmu_init_data(pdev, dmu_pmu); + if (ret) + return ret; + + ret = pd2408_dmu_pmu_init_irq(dmu_pmu, pdev); + if (ret) + return ret; + + dmu_pmu->dev = &pdev->dev; + dmu_pmu->on_cpu = -1; + + return 0; +} + +static int pd2408_dmu_pmu_probe(struct platform_device *pdev) +{ + struct pd2408_dmu_pmu *dmu_pmu; + char *name; + int ret; + + dmu_pmu = devm_kzalloc(&pdev->dev, sizeof(*dmu_pmu), GFP_KERNEL); + if (!dmu_pmu) + return -ENOMEM; + + platform_set_drvdata(pdev, dmu_pmu); + + ret = pd2408_dmu_pmu_dev_probe(pdev, dmu_pmu); + if (ret) + return ret; + + ret = cpuhp_state_add_instance(pd2408_dmu_pmu_hp_state, + &dmu_pmu->node); + if (ret) { + dev_err(&pdev->dev, "Error %d registering hotplug;\n", ret); + return ret; + } + + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "phyt_dmu%u_pmu", dmu_pmu->dmu_id); + dmu_pmu->pmu = (struct pmu) { + .name = name, + .module = THIS_MODULE, + .task_ctx_nr = perf_invalid_context, + .event_init = pd2408_dmu_pmu_event_init, + .pmu_enable = pd2408_dmu_pmu_enable, + .pmu_disable = pd2408_dmu_pmu_disable, + .add = pd2408_dmu_pmu_event_add, + .del = pd2408_dmu_pmu_event_del, + .start = pd2408_dmu_pmu_event_start, + .stop = pd2408_dmu_pmu_event_stop, + .read = pd2408_dmu_pmu_event_update, + .attr_groups = pd2408_dmu_pmu_attr_groups, + }; + + ret = perf_pmu_register(&dmu_pmu->pmu, name, -1); + if (ret) { + dev_err(dmu_pmu->dev, "DMU PMU register failed!\n"); + cpuhp_state_remove_instance_nocalls(pd2408_dmu_pmu_hp_state, + &dmu_pmu->node); + } + + pr_info("die_dmu%d_pmu on cpu%d.\n", dmu_pmu->dmu_id, dmu_pmu->on_cpu); + + return ret; +} + +static int pd2408_dmu_pmu_remove(struct platform_device *pdev) +{ + struct pd2408_dmu_pmu *dmu_pmu = platform_get_drvdata(pdev); + + pd2408_dmu_pmu_mask_all_irq(dmu_pmu); + perf_pmu_unregister(&dmu_pmu->pmu); + cpuhp_state_remove_instance_nocalls(pd2408_dmu_pmu_hp_state, + &dmu_pmu->node); + + return 0; +} + +static struct platform_driver pd2408_dmu_pmu_driver = { + .driver = { + .name = "pd2408_dmu_pmu", + .acpi_match_table = ACPI_PTR(pd2408_dmu_pmu_acpi_match), + .suppress_bind_attrs = true, + }, + .probe = pd2408_dmu_pmu_probe, + .remove = pd2408_dmu_pmu_remove, +}; + +int pd2408_dmu_pmu_online_cpu(unsigned int cpu, struct hlist_node *node) +{ + struct pd2408_dmu_pmu *dmu_pmu = + hlist_entry_safe(node, struct pd2408_dmu_pmu, node); + + if (dmu_pmu->on_cpu != -1) { + if (!cpumask_test_cpu(dmu_pmu->on_cpu, cpu_online_mask)) { + perf_pmu_migrate_context(&dmu_pmu->pmu, dmu_pmu->on_cpu, cpu); + dmu_pmu->on_cpu = cpu; + WARN_ON(irq_set_affinity_hint(dmu_pmu->irq, cpumask_of(cpu))); + } + return 0; + } + + dmu_pmu->on_cpu = cpu; + WARN_ON(irq_set_affinity_hint(dmu_pmu->irq, cpumask_of(cpu))); + + return 0; +} + +int pd2408_dmu_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node) +{ + struct pd2408_dmu_pmu *dmu_pmu = + hlist_entry_safe(node, struct pd2408_dmu_pmu, node); + unsigned int target; + + if (dmu_pmu->on_cpu != cpu) + return 0; + + target = cpumask_last(cpu_online_mask); + + if (target >= nr_cpu_ids) { + dev_err(dmu_pmu->dev, "offline cpu%d with no target to migrate.\n", + cpu); + return 0; + } + + perf_pmu_migrate_context(&dmu_pmu->pmu, cpu, target); + WARN_ON(irq_set_affinity_hint(dmu_pmu->irq, cpumask_of(target))); + dmu_pmu->on_cpu = target; + + return 0; +} + +static int __init pd2408_dmu_pmu_module_init(void) +{ + int ret; + + pd2408_dmu_pmu_hp_state = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, + "perf/phytium/dmupmu:online", + pd2408_dmu_pmu_online_cpu, pd2408_dmu_pmu_offline_cpu); + if (pd2408_dmu_pmu_hp_state < 0) { + pr_err("DMU PMU: setup hotplug, pd2408_dmu_pmu_hp_state = %d\n", + pd2408_dmu_pmu_hp_state); + return pd2408_dmu_pmu_hp_state; + } + + ret = platform_driver_register(&pd2408_dmu_pmu_driver); + if (ret) + cpuhp_remove_multi_state( + pd2408_dmu_pmu_hp_state); + + return ret; +} +module_init(pd2408_dmu_pmu_module_init); + +static void __exit pd2408_dmu_pmu_module_exit(void) +{ + platform_driver_unregister(&pd2408_dmu_pmu_driver); + cpuhp_remove_multi_state(pd2408_dmu_pmu_hp_state); +} +module_exit(pd2408_dmu_pmu_module_exit); + +MODULE_DESCRIPTION("Phytium DMU PMU driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DMU_PERF_DRIVER_VERSION); +MODULE_AUTHOR("Hu Xianghua "); +MODULE_AUTHOR("Tan Rui "); + From 99ddff5292692218dd5e6eff5a32b2fda8bf01d2 Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Fri, 8 May 2026 14:02:47 +0800 Subject: [PATCH 57/81] drivers/perf: phytium: Modify the name of DMU PMU cycles event. Add a prefix before the name of the pd2408 dmu pmu cycles event. Signed-off-by: Zhang Fuxiang Signed-off-by: Tan Rui Signed-off-by: Wang Yinfeng --- drivers/perf/phytium/phytium_dmu_pmu_pd2408.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/perf/phytium/phytium_dmu_pmu_pd2408.c b/drivers/perf/phytium/phytium_dmu_pmu_pd2408.c index df873b018c0e4..4f0049c74c8fc 100644 --- a/drivers/perf/phytium/phytium_dmu_pmu_pd2408.c +++ b/drivers/perf/phytium/phytium_dmu_pmu_pd2408.c @@ -31,7 +31,7 @@ #undef pr_fmt #define pr_fmt(fmt) "pd2408_dmu_pmu: " fmt -#define DMU_PERF_DRIVER_VERSION "1.0.0" +#define DMU_PERF_DRIVER_VERSION "1.0.1" #define DMU_PMU_TIMER_START 0x0 #define DMU_PMU_TIMER_STOP 0x4 @@ -151,7 +151,7 @@ static const struct attribute_group pd2408_dmu_pmu_format_group = { }; static struct attribute *pd2408_dmu_pmu_events_attr[] = { - PHYTIUM_DMU_PMU_EVENT_ATTR(cycles, 0x00), + PHYTIUM_DMU_PMU_EVENT_ATTR(dmu_axi_cycles, 0x00), PHYTIUM_DMU_PMU_EVENT_ATTR(axi_write_flux, 0x01), PHYTIUM_DMU_PMU_EVENT_ATTR(axi_read_flux, 0x02), PHYTIUM_DMU_PMU_EVENT_ATTR(axi_write_cmd, 0x03), From 7eb1616a92edc4d765b732fdb7d020bbdd50b220 Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Fri, 8 May 2026 14:07:22 +0800 Subject: [PATCH 58/81] drivers/perf: phytium: Modify the name of DDR PMU cycles event. Add a prefix before the name of the ddr pmu cycles event. Signed-off-by: Zhang Fuxiang Signed-off-by: Tan Rui Signed-off-by: Wang Yinfeng --- drivers/perf/phytium/phytium_ddr_pmu.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/perf/phytium/phytium_ddr_pmu.c b/drivers/perf/phytium/phytium_ddr_pmu.c index 93372681e4f6c..62ffd3712d58a 100644 --- a/drivers/perf/phytium/phytium_ddr_pmu.c +++ b/drivers/perf/phytium/phytium_ddr_pmu.c @@ -31,8 +31,7 @@ #undef pr_fmt #define pr_fmt(fmt) "phytium_ddr_pmu: " fmt #define PHYTIUM_DDR_MAX_COUNTERS 8 - -#define DDR_PERF_DRIVER_VERSION "1.3.0" +#define DDR_PERF_DRIVER_VERSION "1.3.1" #define DDR_START_TIMER 0x000 #define DDR_STOP_TIMER 0x004 @@ -151,7 +150,7 @@ static const struct attribute_group phytium_ddr_pmu_format_group = { }; static struct attribute *phytium_ddr_pmu_events_attr[] = { - PHYTIUM_DDR_PMU_EVENT_ATTR(cycles, 0x00), + PHYTIUM_DDR_PMU_EVENT_ATTR(ddr_cycles, 0x00), PHYTIUM_DDR_PMU_EVENT_ATTR(rxreq, 0x01), PHYTIUM_DDR_PMU_EVENT_ATTR(rxdat, 0x02), PHYTIUM_DDR_PMU_EVENT_ATTR(txdat, 0x03), From a2e09fa6bebaa5222ba65d91e1ddc5912788e9e9 Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Fri, 8 May 2026 14:09:26 +0800 Subject: [PATCH 59/81] drivers/perf: phytium: Modify the name of PCIe PMU cycles event. Add a prefix before the name of the pcie pmu cycles event. Signed-off-by: Zhang Fuxiang Signed-off-by: Tan Rui Signed-off-by: Wang Yinfeng --- drivers/perf/phytium/phytium_pcie_pmu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/perf/phytium/phytium_pcie_pmu.c b/drivers/perf/phytium/phytium_pcie_pmu.c index b280ced0aecf2..d64de56f98ee5 100644 --- a/drivers/perf/phytium/phytium_pcie_pmu.c +++ b/drivers/perf/phytium/phytium_pcie_pmu.c @@ -33,7 +33,7 @@ #undef pr_fmt #define pr_fmt(fmt) "phytium_pcie_pmu: " fmt -#define PCIE_PERF_DRIVER_VERSION "1.3.0" +#define PCIE_PERF_DRIVER_VERSION "1.3.1" #define PHYTIUM_PCIE_MAX_COUNTERS 18 @@ -177,7 +177,7 @@ static const struct attribute_group phytium_pcie_pmu_format_group = { }; static struct attribute *phytium_pcie_pmu_events_attr[] = { - PHYTIUM_PCIE_PMU_EVENT_ATTR(cycles, 0x00), + PHYTIUM_PCIE_PMU_EVENT_ATTR(pcie_cycles, 0x00), PHYTIUM_PCIE_PMU_EVENT_ATTR(aw, 0x01), PHYTIUM_PCIE_PMU_EVENT_ATTR(w_last, 0x02), PHYTIUM_PCIE_PMU_EVENT_ATTR(b, 0x03), From 3aa83e133f9266ae1caae085d420cc97034a7f4c Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Fri, 8 May 2026 14:13:04 +0800 Subject: [PATCH 60/81] drivers/perf: phytium: Fix issue with PS23X00 DDR PMU on PBF < 1.20. Fix a system panic issue caused by the PS23X00 DDR PMU on Phytium PBF firmware version below 1.20. Signed-off-by: Zhang Fuxiang Signed-off-by: Tan Rui Signed-off-by: Wang Yinfeng --- drivers/perf/phytium/phytium_ddr_pmu.c | 35 +++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/drivers/perf/phytium/phytium_ddr_pmu.c b/drivers/perf/phytium/phytium_ddr_pmu.c index 62ffd3712d58a..466fe8ad7b20f 100644 --- a/drivers/perf/phytium/phytium_ddr_pmu.c +++ b/drivers/perf/phytium/phytium_ddr_pmu.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -31,7 +32,7 @@ #undef pr_fmt #define pr_fmt(fmt) "phytium_ddr_pmu: " fmt #define PHYTIUM_DDR_MAX_COUNTERS 8 -#define DDR_PERF_DRIVER_VERSION "1.3.1" +#define DDR_PERF_DRIVER_VERSION "1.3.2" #define DDR_START_TIMER 0x000 #define DDR_STOP_TIMER 0x004 @@ -56,6 +57,8 @@ #define DDR_DATA_WIDTH 0xe04 #define DDR_PMU_OFL_STOP_TYPE_VAL 0x10 +#define PBFVER_FUNC_ID 0x82000001 + #define to_phytium_ddr_pmu(p) (container_of(p, struct phytium_ddr_pmu, pmu)) enum { DDRV1P0 = 0x01, @@ -468,6 +471,30 @@ static irqreturn_t phytium_ddr_pmu_overflow_handler(int irq, void *dev_id) return IRQ_NONE; } +static int phytium_verify_pbf_version(struct platform_device *pdev) +{ + struct arm_smccc_res res; + unsigned long major_ver, minor_ver; + + arm_smccc_smc(PBFVER_FUNC_ID, 0, 0, 0, 0, 0, 0, 0, &res); + if (res.a0 <= 0) { + dev_warn(&pdev->dev, "Can not recognize PBF Firmware version!\n"); + return -EINVAL; + } + + minor_ver = res.a0 & 0xFFFF; + major_ver = (res.a0 >> 16) & 0xFFFF; + + if (major_ver < 1 || (major_ver == 1 && minor_ver < 20)) { + dev_err(&pdev->dev, + "Driver load failed, Please upgrade PBF Firmware version to 1.20 or later!\n"); + return -EINVAL; + } + + return 0; + +} + static int phytium_ddr_pmu_version(struct platform_device *pdev, struct phytium_ddr_pmu *ddr_pmu) { @@ -584,6 +611,12 @@ static int phytium_ddr_pmu_dev_probe(struct platform_device *pdev, if (ret) return ret; + if (ddr_pmu->ver == DDRV1P0) { + ret = phytium_verify_pbf_version(pdev); + if (ret) + return ret; + } + ret = phytium_ddr_pmu_init_data(pdev, ddr_pmu); if (ret) return ret; From 08fc1382542eda97b885d6ac6087dba19e5e24b5 Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Fri, 8 May 2026 14:14:50 +0800 Subject: [PATCH 61/81] drivers/perf: phytium: Fix issue in dmu pmu driver when cpu is offlined When the dmu pmu driver is migrated to the target cpu, offlining the target cpu will cause the system panic. We fix this issue. Signed-off-by: Zhang Fuxiang Signed-off-by: Tan Rui Signed-off-by: Wang Yinfeng --- drivers/perf/phytium/phytium_dmu_pmu_pd2408.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/perf/phytium/phytium_dmu_pmu_pd2408.c b/drivers/perf/phytium/phytium_dmu_pmu_pd2408.c index 4f0049c74c8fc..b4a9ff59b202b 100644 --- a/drivers/perf/phytium/phytium_dmu_pmu_pd2408.c +++ b/drivers/perf/phytium/phytium_dmu_pmu_pd2408.c @@ -31,7 +31,7 @@ #undef pr_fmt #define pr_fmt(fmt) "pd2408_dmu_pmu: " fmt -#define DMU_PERF_DRIVER_VERSION "1.0.1" +#define DMU_PERF_DRIVER_VERSION "1.0.2" #define DMU_PMU_TIMER_START 0x0 #define DMU_PMU_TIMER_STOP 0x4 @@ -652,11 +652,13 @@ int pd2408_dmu_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node) struct pd2408_dmu_pmu *dmu_pmu = hlist_entry_safe(node, struct pd2408_dmu_pmu, node); unsigned int target; + cpumask_t available_cpus; if (dmu_pmu->on_cpu != cpu) return 0; - target = cpumask_last(cpu_online_mask); + cpumask_andnot(&available_cpus, cpu_online_mask, cpumask_of(cpu)); + target = cpumask_last(&available_cpus); if (target >= nr_cpu_ids) { dev_err(dmu_pmu->dev, "offline cpu%d with no target to migrate.\n", From d26528ecaa2070eb0da4faa2761bdaef10925b88 Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Fri, 8 May 2026 14:16:43 +0800 Subject: [PATCH 62/81] drivers/perf: phytium: Rename DDR PMU event names Standarize event naming to use all lowercase and avoid mixing uppercase mixing uppercase and lowercase letters. Signed-off-by: Zhang Fuxiang Signed-off-by: Tan Rui Signed-off-by: Wang Yinfeng --- drivers/perf/phytium/phytium_ddr_pmu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/perf/phytium/phytium_ddr_pmu.c b/drivers/perf/phytium/phytium_ddr_pmu.c index 466fe8ad7b20f..124c7ad9bdd04 100644 --- a/drivers/perf/phytium/phytium_ddr_pmu.c +++ b/drivers/perf/phytium/phytium_ddr_pmu.c @@ -32,7 +32,7 @@ #undef pr_fmt #define pr_fmt(fmt) "phytium_ddr_pmu: " fmt #define PHYTIUM_DDR_MAX_COUNTERS 8 -#define DDR_PERF_DRIVER_VERSION "1.3.2" +#define DDR_PERF_DRIVER_VERSION "1.3.3" #define DDR_START_TIMER 0x000 #define DDR_STOP_TIMER 0x004 @@ -157,9 +157,9 @@ static struct attribute *phytium_ddr_pmu_events_attr[] = { PHYTIUM_DDR_PMU_EVENT_ATTR(rxreq, 0x01), PHYTIUM_DDR_PMU_EVENT_ATTR(rxdat, 0x02), PHYTIUM_DDR_PMU_EVENT_ATTR(txdat, 0x03), - PHYTIUM_DDR_PMU_EVENT_ATTR(rxreq_RNS, 0x04), - PHYTIUM_DDR_PMU_EVENT_ATTR(rxreq_WNSP, 0x05), - PHYTIUM_DDR_PMU_EVENT_ATTR(rxreq_WNSF, 0x06), + PHYTIUM_DDR_PMU_EVENT_ATTR(rxreq_rns, 0x04), + PHYTIUM_DDR_PMU_EVENT_ATTR(rxreq_wnsp, 0x05), + PHYTIUM_DDR_PMU_EVENT_ATTR(rxreq_wnsf, 0x06), PHYTIUM_DDR_PMU_EVENT_ATTR(bandwidth, 0x07), NULL, }; From 10dfbf8f58cf8edf2d0b08cce5f50a6790cb63ea Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Fri, 8 May 2026 14:18:54 +0800 Subject: [PATCH 63/81] drivers/perf: phytium: Add driver support for PCIe PMU v2.0 Introduce driver support for the PCIe PMU v2p0. The driver enables discovery and registration of PMU capabilities in PCIe devices. Signed-off-by: Zhang Fuxiang Signed-off-by: Tan Rui Signed-off-by: Wang Yinfeng --- drivers/perf/phytium/phytium_pcie_pmu.c | 1469 +++++++++++++++++++---- 1 file changed, 1206 insertions(+), 263 deletions(-) diff --git a/drivers/perf/phytium/phytium_pcie_pmu.c b/drivers/perf/phytium/phytium_pcie_pmu.c index d64de56f98ee5..b04fcb8f31608 100644 --- a/drivers/perf/phytium/phytium_pcie_pmu.c +++ b/drivers/perf/phytium/phytium_pcie_pmu.c @@ -33,54 +33,236 @@ #undef pr_fmt #define pr_fmt(fmt) "phytium_pcie_pmu: " fmt -#define PCIE_PERF_DRIVER_VERSION "1.3.1" - -#define PHYTIUM_PCIE_MAX_COUNTERS 18 - -#define PCIE_START_TIMER 0x000 -#define PCIE_STOP_TIMER 0x004 -#define PCIE_CLEAR_EVENT 0x008 - -#define PCIE_EVENT_CYCLES 0x0e4 -#define PCIE_TPOINT_END_L 0x0e4 -#define PCIE_TPOINT_END_H 0x0e8 -#define PCIE_STATE_STOP 0x0ec - -#define PCIE_EVENT_AW 0x100 -#define PCIE_EVENT_W_LAST 0x104 -#define PCIE_EVENT_B 0x108 -#define PCIE_EVENT_AR 0x10c -#define PCIE_EVENT_R_LAST 0x110 -#define PCIE_EVENT_R_FULL 0x114 -#define PCIE_EVENT_R_ERR 0x118 -#define PCIE_EVENT_W_ERR 0x11c -#define PCIE_EVENT_DELAY_RD 0x120 -#define PCIE_EVENT_DELAY_WR 0x124 -#define PCIE_EVENT_RD_MAX 0x128 -#define PCIE_EVENT_RD_MIN 0x12c -#define PCIE_EVENT_WR_MAX 0x130 -#define PCIE_EVENT_WR_MIN 0x134 - -#define PCIE_EVENT_W_DATA 0x200 -#define PCIE_W_DATA_BASE 0x200 - +#define PCIE_PERF_DRIVER_VERSION "1.4.0" + +#define PHYTIUM_PCIE_MAX_COUNTERS 112 +#define PHYTIUM_PCIE_V1_COUNTERS_NUM 18 +#define PHYTIUM_PCIE_V2_COUNTERS_NUM 112 +#define PHYTIUM_PCIE_EVENTS_MAX_MASK 0x7F + +#define PCIE_START_TIMER 0x000 +#define PCIE_STOP_TIMER 0x004 +#define PCIE_CLEAR_EVENT 0x008 +#define PCIE_SET_TIMER_L 0x00C +#define PCIE_SET_TIMER_H 0x010 +#define PCIE_TRIG_MODE 0x014 +#define PCIE_NOW_STATE 0x0E0 +#define PCIE_STATE_STOP 0x0EC +#define PCIE_EVENT_THRESHOLD_EN 0x01C +#define PCIE_EVENT_THRESHOLD_0 0x020 +#define PCIE_EVENT_THRESHOLD_1 0x024 +#define PCIE_PMU_OVER_STATE 0x2 + +#define PCIE_TPOINT_END_L 0x0E4 +#define PCIE_TPOINT_END_H 0x0E8 + +#define PCIE_V2_PMU_TIME_RG_THD_0 0x030 +#define PCIE_V2_PMU_TIME_RG_THD_1 0x034 +#define PCIE_V2_PMU_TIME_RG_THD_2 0x038 +#define PCIE_V2_PMU_SORT_MODE 0x080 +#define PCIE_V2_PMU_REQID_CFG 0x084 +#define PCIE_V2_PMU_AT_CFG 0x090 +#define PCIE_V2_PMU_MEM_STOP_EN 0x094 +#define PCIE_V2_PMU_NOW_STATE 0x0E0 +#define PCIE_V2_PMU_TPOINT_END_L 0x0E4 +#define PCIE_V2_PMU_TPOINT_END_H 0x0E8 +#define PCIE_V2_PMU_STATE_STOP 0x0EC + +#define PCIE_V2_TIMEOUT_EN 0x600 +#define PCIE_V2_TIMEOUT_THRESHOLD 0x604 + +#define PCIE_V2_PMU_CLK_EN 0x650 +#define PCIE_V2_PMU_CHANNEL_SEL 0x654 + +#define PCIEA_V2P0_CHANNEL_NUM 8 +#define PCIEB_V2P0_CHANNEL_NUM 6 +#define PCIEC_V2P0_CHANNEL_NUM 5 + +#define PCIE_EVENT_CYCLES 0x0E4 +#define PCIE_EVENT_AW 0x100 +#define PCIE_EVENT_W_LAST 0x104 +#define PCIE_EVENT_B 0x108 +#define PCIE_EVENT_AR 0x10c +#define PCIE_EVENT_R_LAST 0x110 +#define PCIE_EVENT_R_FULL 0x114 +#define PCIE_EVENT_R_ERR 0x118 +#define PCIE_EVENT_W_ERR 0x11c +#define PCIE_EVENT_W_DATA 0x200 +#define PCIE_W_DATA_BASE 0x200 + +#define PCIE_EVENT_DELAY_RD 0x120 +#define PCIE_EVENT_DELAY_WR 0x124 +#define PCIE_EVENT_RD_MAX 0x128 +#define PCIE_EVENT_RD_MIN 0x12c +#define PCIE_EVENT_WR_MAX 0x130 +#define PCIE_EVENT_WR_MIN 0x134 #define PCIE_EVENT_RDELAY_TIME 0x300 -#define PCIE_RDELAY_TIME_BASE 0x300 - -#define PCIE_EVENT_WDELAY_TIME 0x700 -#define PCIE_WDELAY_TIME_BASE 0x700 - -#define PCIE_DATA_WIDTH 0xe04 -#define PCIE_PMU_OFL_STOP_TYPE_VAL 0x10 +#define PCIE_EVENT_WDELAY_TIME 0x700 + +#define PCIE_V2_G0_EVENT_AW 0x100 +#define PCIE_V2_G0_EVENT_W_LAST 0x104 +#define PCIE_V2_G0_EVENT_B 0x108 +#define PCIE_V2_G0_EVENT_AR 0x10C +#define PCIE_V2_G0_EVENT_R_LAST 0x110 +#define PCIE_V2_G0_EVENT_R_FULL 0x114 +#define PCIE_V2_G0_EVENT_R_ERR 0x118 +#define PCIE_V2_G0_EVENT_W_ERR 0x11C +#define PCIE_V2_G0_EVENT_ATOMIC_AW 0x120 +#define PCIE_V2_G0_EVENT_R_DISCARDED 0x124 +#define PCIE_V2_G0_EVENT_AT_AW 0x128 +#define PCIE_V2_G0_EVENT_AT_AR 0x12C +#define PCIE_V2_G0_EVENT_W_DATA_AW_L 0x140 +#define PCIE_V2_G0_EVENT_W_DATA_AW_H 0x144 +#define PCIE_V2_EVENT_W_DATA 0x200 +#define PCIE_V2_G0_EVENT_DELAY_RD 0x400 +#define PCIE_V2_G0_EVENT_DELAY_WR 0x404 +#define PCIE_V2_G0_EVENT_DELAY_RD_MAX 0x408 +#define PCIE_V2_G0_EVENT_DELAY_RD_MIN 0x40C +#define PCIE_V2_G0_EVENT_DELAY_WR_MAX 0x410 +#define PCIE_V2_G0_EVENT_DELAY_WR_MIN 0x414 +#define PCIE_V2_G0_EVENT_DELAY_RD_TOTAL_H 0x418 +#define PCIE_V2_G0_EVENT_DELAY_RD_TOTAL_L 0x41C +#define PCIE_V2_G0_EVENT_DELAY_WR_TOTAL_H 0x420 +#define PCIE_V2_G0_EVENT_DELAY_WR_TOTAL_L 0x424 +#define PCIE_V2_G0_EVENT_DELAY_RD_0 0x428 +#define PCIE_V2_G0_EVENT_DELAY_RD_1 0x42C +#define PCIE_V2_G0_EVENT_DELAY_RD_2 0x430 +#define PCIE_V2_G0_EVENT_DELAY_RD_3 0x434 +#define PCIE_V2_G0_EVENT_DELAY_WR_0 0x438 +#define PCIE_V2_G0_EVENT_DELAY_WR_1 0x43C +#define PCIE_V2_G0_EVENT_DELAY_WR_2 0x440 +#define PCIE_V2_G0_EVENT_DELAY_WR_3 0x444 + +#define PCIE_V2_G1_EVENT_AW 0x900 +#define PCIE_V2_G1_EVENT_B 0x908 +#define PCIE_V2_G1_EVENT_AR 0x90C +#define PCIE_V2_G1_EVENT_R_LAST 0x910 +#define PCIE_V2_G1_EVENT_R_FULL 0x914 +#define PCIE_V2_G1_EVENT_R_ERR 0x918 +#define PCIE_V2_G1_EVENT_W_ERR 0x91C +#define PCIE_V2_G1_EVENT_ATOMIC_AW 0x920 +#define PCIE_V2_G1_EVENT_AT_AW 0x928 +#define PCIE_V2_G1_EVENT_AT_AR 0x92C +#define PCIE_V2_G1_EVENT_W_DATA_AW_L 0x940 +#define PCIE_V2_G1_EVENT_W_DATA_AW_H 0x944 +#define PCIE_V2_G1_EVENT_DELAY_RD 0xa00 +#define PCIE_V2_G1_EVENT_DELAY_WR 0xa04 +#define PCIE_V2_G1_EVENT_DELAY_RD_MAX 0xa08 +#define PCIE_V2_G1_EVENT_DELAY_RD_MIN 0xa0C +#define PCIE_V2_G1_EVENT_DELAY_WR_MAX 0xa10 +#define PCIE_V2_G1_EVENT_DELAY_WR_MIN 0xa14 +#define PCIE_V2_G1_EVENT_DELAY_RD_TOTAL_H 0xa18 +#define PCIE_V2_G1_EVENT_DELAY_RD_TOTAL_L 0xa1C +#define PCIE_V2_G1_EVENT_DELAY_WR_TOTAL_H 0xa20 +#define PCIE_V2_G1_EVENT_DELAY_WR_TOTAL_L 0xa24 +#define PCIE_V2_G1_EVENT_DELAY_RD_0 0xa28 +#define PCIE_V2_G1_EVENT_DELAY_RD_1 0xa2C +#define PCIE_V2_G1_EVENT_DELAY_RD_2 0xa30 +#define PCIE_V2_G1_EVENT_DELAY_RD_3 0xa34 +#define PCIE_V2_G1_EVENT_DELAY_WR_0 0xa38 +#define PCIE_V2_G1_EVENT_DELAY_WR_1 0xa3C +#define PCIE_V2_G1_EVENT_DELAY_WR_2 0xa40 +#define PCIE_V2_G1_EVENT_DELAY_WR_3 0xa44 + +#define PCIE_V2_G2_EVENT_AW 0xb00 +#define PCIE_V2_G2_EVENT_B 0xb08 +#define PCIE_V2_G2_EVENT_AR 0xb0C +#define PCIE_V2_G2_EVENT_R_LAST 0xb10 +#define PCIE_V2_G2_EVENT_R_FULL 0xb14 +#define PCIE_V2_G2_EVENT_R_ERR 0xb18 +#define PCIE_V2_G2_EVENT_W_ERR 0xb1C +#define PCIE_V2_G2_EVENT_ATOMIC_AW 0xb20 +#define PCIE_V2_G2_EVENT_AT_AW 0xb28 +#define PCIE_V2_G2_EVENT_AT_AR 0xb2C +#define PCIE_V2_G2_EVENT_W_DATA_AW_L 0xb40 +#define PCIE_V2_G2_EVENT_W_DATA_AW_H 0xb44 +#define PCIE_V2_G2_EVENT_DELAY_RD 0xc00 +#define PCIE_V2_G2_EVENT_DELAY_WR 0xc04 +#define PCIE_V2_G2_EVENT_DELAY_RD_MAX 0xc08 +#define PCIE_V2_G2_EVENT_DELAY_RD_MIN 0xc0C +#define PCIE_V2_G2_EVENT_DELAY_WR_MAX 0xc10 +#define PCIE_V2_G2_EVENT_DELAY_WR_MIN 0xc14 +#define PCIE_V2_G2_EVENT_DELAY_RD_TOTAL_H 0xc18 +#define PCIE_V2_G2_EVENT_DELAY_RD_TOTAL_L 0xc1C +#define PCIE_V2_G2_EVENT_DELAY_WR_TOTAL_H 0xc20 +#define PCIE_V2_G2_EVENT_DELAY_WR_TOTAL_L 0xc24 +#define PCIE_V2_G2_EVENT_DELAY_RD_0 0xc28 +#define PCIE_V2_G2_EVENT_DELAY_RD_1 0xc2C +#define PCIE_V2_G2_EVENT_DELAY_RD_2 0xc30 +#define PCIE_V2_G2_EVENT_DELAY_RD_3 0xc34 +#define PCIE_V2_G2_EVENT_DELAY_WR_0 0xc38 +#define PCIE_V2_G2_EVENT_DELAY_WR_1 0xc3C +#define PCIE_V2_G2_EVENT_DELAY_WR_2 0xc40 +#define PCIE_V2_G2_EVENT_DELAY_WR_3 0xc44 + +#define PCIE_V2_G3_EVENT_AW 0xd00 +#define PCIE_V2_G3_EVENT_B 0xd08 +#define PCIE_V2_G3_EVENT_AR 0xd0C +#define PCIE_V2_G3_EVENT_R_LAST 0xd10 +#define PCIE_V2_G3_EVENT_R_FULL 0xd14 +#define PCIE_V2_G3_EVENT_R_ERR 0xd18 +#define PCIE_V2_G3_EVENT_W_ERR 0xd1C +#define PCIE_V2_G3_EVENT_ATOMIC_AW 0xd20 +#define PCIE_V2_G3_EVENT_AT_AW 0xd28 +#define PCIE_V2_G3_EVENT_AT_AR 0xd2C +#define PCIE_V2_G3_EVENT_W_DATA_AW_L 0xd40 +#define PCIE_V2_G3_EVENT_W_DATA_AW_H 0xd44 +#define PCIE_V2_G3_EVENT_DELAY_RD 0xe00 +#define PCIE_V2_G3_EVENT_DELAY_WR 0xe04 +#define PCIE_V2_G3_EVENT_DELAY_RD_MAX 0xe08 +#define PCIE_V2_G3_EVENT_DELAY_RD_MIN 0xe0C +#define PCIE_V2_G3_EVENT_DELAY_WR_MAX 0xe10 +#define PCIE_V2_G3_EVENT_DELAY_WR_MIN 0xe14 +#define PCIE_V2_G3_EVENT_DELAY_RD_TOTAL_H 0xe18 +#define PCIE_V2_G3_EVENT_DELAY_RD_TOTAL_L 0xe1C +#define PCIE_V2_G3_EVENT_DELAY_WR_TOTAL_H 0xe20 +#define PCIE_V2_G3_EVENT_DELAY_WR_TOTAL_L 0xe24 +#define PCIE_V2_G3_EVENT_DELAY_RD_0 0xe28 +#define PCIE_V2_G3_EVENT_DELAY_RD_1 0xe2C +#define PCIE_V2_G3_EVENT_DELAY_RD_2 0xe30 +#define PCIE_V2_G3_EVENT_DELAY_RD_3 0xe34 +#define PCIE_V2_G3_EVENT_DELAY_WR_0 0xe38 +#define PCIE_V2_G3_EVENT_DELAY_WR_1 0xe3C +#define PCIE_V2_G3_EVENT_DELAY_WR_2 0xe40 +#define PCIE_V2_G3_EVENT_DELAY_WR_3 0xe44 + +#define PCIE_V1_AXI_CLK_FRE 0xE00 +#define PCIE_V1_DATA_WIDTH 0xE04 + +#define PCIE_V2_AXI_CLK_FRE 0xF00 +#define PCIE_V2_DATA_WIDTH 0xF04 + +#define PCIE_PMU_PIDR 0xFE0 +#define PCIE_PMU_VER_BIT GENMASK(7, 0) +#define PCIE_PMU_PART_BIT GENMASK(11, 8) +#define PMU_CHANNEL_SEL_BIT GENMASK(2, 0) + +#define PCIEV2_PMU_CLK_EN_BIT BIT(0) +#define PCIE_EVENT_AW_THD_ENBIT BIT(0) +#define PCIE_EVENT_AR_THD_ENBIT BIT(1) + +#define PCIE_EVENT_THD0_STOP_TYPE 0x1 +#define PCIE_EVENT_THD1_STOP_TYPE 0x2 +#define PCIE_TIME_STOP_TYPE 0x10 + +#define PCIE_PMU_DEFAULT_THD0 1000000 +#define PCIE_PMU_DEFAULT_THD1 10000000 +#define PCIE_PMU_DEFAULT_THD2 100000000 #define to_phytium_pcie_pmu(p) (container_of(p, struct phytium_pcie_pmu, pmu)) +#define GET_PCIE_EVENTID(hwc) (hwc->config_base & PHYTIUM_PCIE_EVENTS_MAX_MASK) +#define EVENT_VALID_V1(idx) ((idx >= 0) && (idx < PHYTIUM_PCIE_V1_COUNTERS_NUM)) +#define EVENT_VALID_V2(idx) ((idx >= 0) && (idx < PHYTIUM_PCIE_V2_COUNTERS_NUM)) + enum { - PCIEV1P0 = 0x1, - PCIEV1P5 = 0x2, + PCIEA_V1P0 = 1, + PCIEA_V1P5 = 3, + PCIEA_V2P0 = 5, + PCIEB_V2P0, + PCIEC_V2P0, }; - static int phytium_pcie_pmu_hp_state; struct phytium_pcie_pmu_hwevents { @@ -88,31 +270,71 @@ struct phytium_pcie_pmu_hwevents { DECLARE_BITMAP(used_mask, PHYTIUM_PCIE_MAX_COUNTERS); }; +struct phytium_pcie_pmu; + +struct phytium_pcie_pmu_ops { + void (*get_event_config)(struct perf_event *event, struct phytium_pcie_pmu *pmu); + int (*set_event_config)(struct perf_event *event, struct phytium_pcie_pmu *pmu); + void (*reset_event_config)(struct phytium_pcie_pmu *pmu); + u64 (*read_counter)(struct phytium_pcie_pmu *pmu, struct hw_perf_event *event); + void (*enable_clk)(struct phytium_pcie_pmu *pmu); + void (*disable_clk)(struct phytium_pcie_pmu *pmu); + void (*clear_all_counters)(struct phytium_pcie_pmu *pmu); + void (*start_all_counters)(struct phytium_pcie_pmu *pmu); + void (*stop_all_counters)(struct phytium_pcie_pmu *pmu); + unsigned long (*get_stop_state)(struct phytium_pcie_pmu *pmu); + u32 (*get_counter_offset)(u32 idx); + void (*reset_pmu)(struct phytium_pcie_pmu *pmu); +}; + +struct phytium_pcie_pmu_event_cfg { + u32 chansel; + u32 by_timer; + u32 by_trig_mode; + u32 delay_thd0_us; + u32 delay_thd1_us; + u32 delay_thd2_us; + u32 by_evthod; + u32 aw_threshold; + u32 ar_threshold; + u32 by_reqid; + u32 bdf_id; + u32 sort_mode; + u32 byargs0; + u32 byargs1; + u64 timer_64; +}; + struct phytium_pcie_pmu { struct device *dev; void __iomem *base; void __iomem *cfg_base; void __iomem *irq_reg; struct pmu pmu; + const struct phytium_pcie_pmu_ops *ops; struct phytium_pcie_pmu_hwevents pmu_events; + struct phytium_pcie_pmu_event_cfg event_cfg; u32 die_id; + u32 dev_id; u32 pcie_id; u32 pmu_id; int on_cpu; int irq; int irq_bit; + int cnts_num; + int channel_num; struct hlist_node node; int ctrler_id; int real_ctrler; - int ver; u32 clk_bits; + u32 ver; }; -#define GET_PCIE_EVENTID(hwc) (hwc->config_base & 0x1F) - -#define EVENT_VALID(idx) ((idx >= 0) && (idx < PHYTIUM_PCIE_MAX_COUNTERS)) +static const char pcie_type_part_name[] = { + 'a', 'b', 'c' +}; -static const u32 pcie_counter_reg_offset[] = { +static const u32 pcie_v1_counter_reg_offset[] = { PCIE_EVENT_CYCLES, PCIE_EVENT_AW, PCIE_EVENT_W_LAST, PCIE_EVENT_B, PCIE_EVENT_AR, PCIE_EVENT_R_LAST, PCIE_EVENT_R_FULL, PCIE_EVENT_R_ERR, PCIE_EVENT_W_ERR, @@ -121,9 +343,63 @@ static const u32 pcie_counter_reg_offset[] = { PCIE_EVENT_W_DATA, PCIE_EVENT_RDELAY_TIME, PCIE_EVENT_WDELAY_TIME }; +static const u32 pcie_v2_counter_reg_offset[] = { + PCIE_EVENT_CYCLES, + PCIE_V2_G0_EVENT_AW, PCIE_V2_G0_EVENT_B, PCIE_V2_G0_EVENT_AR, + PCIE_V2_G0_EVENT_R_LAST, PCIE_V2_G0_EVENT_R_FULL, + PCIE_V2_G0_EVENT_R_ERR, PCIE_V2_G0_EVENT_W_ERR, + PCIE_V2_G0_EVENT_ATOMIC_AW, PCIE_V2_G0_EVENT_AT_AW, PCIE_V2_G0_EVENT_AT_AR, + PCIE_V2_G0_EVENT_W_DATA_AW_L, PCIE_V2_G0_EVENT_DELAY_RD, PCIE_V2_G0_EVENT_DELAY_WR, + PCIE_V2_G0_EVENT_DELAY_RD_MAX, PCIE_V2_G0_EVENT_DELAY_RD_MIN, + PCIE_V2_G0_EVENT_DELAY_WR_MAX, PCIE_V2_G0_EVENT_DELAY_WR_MIN, + PCIE_V2_G0_EVENT_DELAY_RD_TOTAL_H, PCIE_V2_G0_EVENT_DELAY_WR_TOTAL_H, + PCIE_V2_G0_EVENT_DELAY_RD_0, PCIE_V2_G0_EVENT_DELAY_RD_1, + PCIE_V2_G0_EVENT_DELAY_RD_2, PCIE_V2_G0_EVENT_DELAY_RD_3, + PCIE_V2_G0_EVENT_DELAY_WR_0, PCIE_V2_G0_EVENT_DELAY_WR_1, + PCIE_V2_G0_EVENT_DELAY_WR_2, PCIE_V2_G0_EVENT_DELAY_WR_3, + PCIE_V2_EVENT_W_DATA, PCIE_V2_G0_EVENT_W_LAST, PCIE_V2_G0_EVENT_R_DISCARDED, + PCIE_V2_G1_EVENT_AW, PCIE_V2_G1_EVENT_B, PCIE_V2_G1_EVENT_AR, + PCIE_V2_G1_EVENT_R_LAST, PCIE_V2_G1_EVENT_R_FULL, + PCIE_V2_G1_EVENT_R_ERR, PCIE_V2_G1_EVENT_W_ERR, + PCIE_V2_G1_EVENT_ATOMIC_AW, PCIE_V2_G1_EVENT_AT_AW, PCIE_V2_G1_EVENT_AT_AR, + PCIE_V2_G1_EVENT_W_DATA_AW_L, PCIE_V2_G1_EVENT_DELAY_RD, PCIE_V2_G1_EVENT_DELAY_WR, + PCIE_V2_G1_EVENT_DELAY_RD_MAX, PCIE_V2_G1_EVENT_DELAY_RD_MIN, + PCIE_V2_G1_EVENT_DELAY_WR_MAX, PCIE_V2_G1_EVENT_DELAY_WR_MIN, + PCIE_V2_G1_EVENT_DELAY_RD_TOTAL_H, PCIE_V2_G1_EVENT_DELAY_WR_TOTAL_H, + PCIE_V2_G1_EVENT_DELAY_RD_0, PCIE_V2_G1_EVENT_DELAY_RD_1, + PCIE_V2_G1_EVENT_DELAY_RD_2, PCIE_V2_G1_EVENT_DELAY_RD_3, + PCIE_V2_G1_EVENT_DELAY_WR_0, PCIE_V2_G1_EVENT_DELAY_WR_1, + PCIE_V2_G1_EVENT_DELAY_WR_2, PCIE_V2_G1_EVENT_DELAY_WR_3, + PCIE_V2_G2_EVENT_AW, PCIE_V2_G2_EVENT_B, PCIE_V2_G2_EVENT_AR, + PCIE_V2_G2_EVENT_R_LAST, PCIE_V2_G2_EVENT_R_FULL, + PCIE_V2_G2_EVENT_R_ERR, PCIE_V2_G2_EVENT_W_ERR, + PCIE_V2_G2_EVENT_ATOMIC_AW, PCIE_V2_G2_EVENT_AT_AW, PCIE_V2_G2_EVENT_AT_AR, + PCIE_V2_G2_EVENT_W_DATA_AW_L, PCIE_V2_G2_EVENT_DELAY_RD, PCIE_V2_G2_EVENT_DELAY_WR, + PCIE_V2_G2_EVENT_DELAY_RD_MAX, PCIE_V2_G2_EVENT_DELAY_RD_MIN, + PCIE_V2_G2_EVENT_DELAY_WR_MAX, PCIE_V2_G2_EVENT_DELAY_WR_MIN, + PCIE_V2_G2_EVENT_DELAY_RD_TOTAL_H, PCIE_V2_G2_EVENT_DELAY_WR_TOTAL_H, + PCIE_V2_G2_EVENT_DELAY_RD_0, PCIE_V2_G2_EVENT_DELAY_RD_1, + PCIE_V2_G2_EVENT_DELAY_RD_2, PCIE_V2_G2_EVENT_DELAY_RD_3, + PCIE_V2_G2_EVENT_DELAY_WR_0, PCIE_V2_G2_EVENT_DELAY_WR_1, + PCIE_V2_G2_EVENT_DELAY_WR_2, PCIE_V2_G2_EVENT_DELAY_WR_3, + PCIE_V2_G3_EVENT_AW, PCIE_V2_G3_EVENT_B, PCIE_V2_G3_EVENT_AR, + PCIE_V2_G3_EVENT_R_LAST, PCIE_V2_G3_EVENT_R_FULL, + PCIE_V2_G3_EVENT_R_ERR, PCIE_V2_G3_EVENT_W_ERR, + PCIE_V2_G3_EVENT_ATOMIC_AW, PCIE_V2_G3_EVENT_AT_AW, + PCIE_V2_G3_EVENT_AT_AR, PCIE_V2_G3_EVENT_W_DATA_AW_L, + PCIE_V2_G3_EVENT_DELAY_RD, PCIE_V2_G3_EVENT_DELAY_WR, + PCIE_V2_G3_EVENT_DELAY_RD_MAX, PCIE_V2_G3_EVENT_DELAY_RD_MIN, + PCIE_V2_G3_EVENT_DELAY_WR_MAX, PCIE_V2_G3_EVENT_DELAY_WR_MIN, + PCIE_V2_G3_EVENT_DELAY_RD_TOTAL_H, PCIE_V2_G3_EVENT_DELAY_WR_TOTAL_H, + PCIE_V2_G3_EVENT_DELAY_RD_0, PCIE_V2_G3_EVENT_DELAY_RD_1, + PCIE_V2_G3_EVENT_DELAY_RD_2, PCIE_V2_G3_EVENT_DELAY_RD_3, + PCIE_V2_G3_EVENT_DELAY_WR_0, PCIE_V2_G3_EVENT_DELAY_WR_1, + PCIE_V2_G3_EVENT_DELAY_WR_2, PCIE_V2_G3_EVENT_DELAY_WR_3 +}; + ssize_t phytium_pcie_pmu_format_sysfs_show(struct device *dev, - struct device_attribute *attr, - char *buf) + struct device_attribute *attr, + char *buf) { struct dev_ext_attribute *eattr; @@ -133,8 +409,8 @@ ssize_t phytium_pcie_pmu_format_sysfs_show(struct device *dev, } ssize_t phytium_pcie_pmu_event_sysfs_show(struct device *dev, - struct device_attribute *attr, - char *page) + struct device_attribute *attr, + char *page) { struct dev_ext_attribute *eattr; @@ -157,7 +433,7 @@ static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, { __ATTR(_name, 0444, _func, NULL), (void *)_config } })[0] \ .attr.attr) -#define PHYTIUM_PCIE_PMU_FORMAT_ATTR(_name, _config) \ +#define PHYTIUM_PCIE_PMU_FORMAT_ATTR(_name, _config) \ PHYTIUM_PMU_ATTR(_name, phytium_pcie_pmu_format_sysfs_show, \ (void *)_config) @@ -165,18 +441,78 @@ static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, PHYTIUM_PMU_ATTR(_name, phytium_pcie_pmu_event_sysfs_show, \ (unsigned long)_config) -static struct attribute *phytium_pcie_pmu_format_attr[] = { +#define PCIE_PMU_V1_EVENT_ATTR_EXTRACTOR_U32(_name, _config, _start, _end) \ +static inline u32 pcie_pmu_v1_get_u32_##_name(struct perf_event *event) \ +{ \ + return FIELD_GET(GENMASK_ULL(_end, _start), \ + event->attr._config); \ +} + +#define PCIE_PMU_V1_EVENT_ATTR_EXTRACTOR_U64(_name, _config, _start, _end) \ +static inline u64 pcie_pmu_v1_get_u64_##_name(struct perf_event *event) \ +{ \ + return FIELD_GET(GENMASK_ULL(_end, _start), \ + event->attr._config); \ +} + +#define PCIE_PMU_V2_EVENT_ATTR_EXTRACTOR(_name, _config, _start, _end) \ +static inline u32 pcie_pmu_v2_get_##_name(struct perf_event *event) \ +{ \ + return FIELD_GET(GENMASK_ULL(_end, _start), \ + event->attr._config); \ +} + +PCIE_PMU_V1_EVENT_ATTR_EXTRACTOR_U32(event, config, 0, 4); +PCIE_PMU_V1_EVENT_ATTR_EXTRACTOR_U32(ctrler, config, 8, 10); +PCIE_PMU_V1_EVENT_ATTR_EXTRACTOR_U64(timer, config1, 0, 63); + +static struct attribute *phytium_pcie_pmu_v1_format_attr[] = { PHYTIUM_PCIE_PMU_FORMAT_ATTR(event, "config:0-4"), PHYTIUM_PCIE_PMU_FORMAT_ATTR(ctrler, "config:8-10"), + PHYTIUM_PCIE_PMU_FORMAT_ATTR(timer, "config1:0-63"), + NULL, +}; + +PCIE_PMU_V2_EVENT_ATTR_EXTRACTOR(event, config, 0, 6); +PCIE_PMU_V2_EVENT_ATTR_EXTRACTOR(chansel, config, 7, 9); +PCIE_PMU_V2_EVENT_ATTR_EXTRACTOR(by_timer, config, 10, 10); +PCIE_PMU_V2_EVENT_ATTR_EXTRACTOR(by_evthod, config, 11, 11); +PCIE_PMU_V2_EVENT_ATTR_EXTRACTOR(by_trig_mode, config, 12, 12); +PCIE_PMU_V2_EVENT_ATTR_EXTRACTOR(sort_mode, config, 13, 14); +PCIE_PMU_V2_EVENT_ATTR_EXTRACTOR(bdf_id, config, 16, 31); +PCIE_PMU_V2_EVENT_ATTR_EXTRACTOR(delay_thd0_us, config, 32, 63); +PCIE_PMU_V2_EVENT_ATTR_EXTRACTOR(delay_thd1_us, config1, 0, 31); +PCIE_PMU_V2_EVENT_ATTR_EXTRACTOR(delay_thd2_us, config1, 32, 63); +PCIE_PMU_V2_EVENT_ATTR_EXTRACTOR(byargs0, config2, 0, 31); +PCIE_PMU_V2_EVENT_ATTR_EXTRACTOR(byargs1, config2, 32, 63); + +static struct attribute *phytium_pcie_pmu_v2_format_attr[] = { + PHYTIUM_PCIE_PMU_FORMAT_ATTR(event, "config:0-6"), + PHYTIUM_PCIE_PMU_FORMAT_ATTR(chansel, "config:7-9"), + PHYTIUM_PCIE_PMU_FORMAT_ATTR(by_timer, "config:10-10"), + PHYTIUM_PCIE_PMU_FORMAT_ATTR(by_evthod, "config:11-11"), + PHYTIUM_PCIE_PMU_FORMAT_ATTR(by_trig_mode, "config:12-12"), + PHYTIUM_PCIE_PMU_FORMAT_ATTR(sort_mode, "config:13-14"), + PHYTIUM_PCIE_PMU_FORMAT_ATTR(bdf_id, "config:16-31"), + PHYTIUM_PCIE_PMU_FORMAT_ATTR(delay_thd0_us, "config:32-63"), + PHYTIUM_PCIE_PMU_FORMAT_ATTR(delay_thd1_us, "config1:0-31"), + PHYTIUM_PCIE_PMU_FORMAT_ATTR(delay_thd2_us, "config1:32-63"), + PHYTIUM_PCIE_PMU_FORMAT_ATTR(byargs0, "config2:0-31"), + PHYTIUM_PCIE_PMU_FORMAT_ATTR(byargs1, "config2:32-63"), NULL, }; -static const struct attribute_group phytium_pcie_pmu_format_group = { +static const struct attribute_group phytium_pcie_pmu_v1_format_group = { .name = "format", - .attrs = phytium_pcie_pmu_format_attr, + .attrs = phytium_pcie_pmu_v1_format_attr, }; -static struct attribute *phytium_pcie_pmu_events_attr[] = { +static const struct attribute_group phytium_pcie_pmu_v2_format_group = { + .name = "format", + .attrs = phytium_pcie_pmu_v2_format_attr, +}; + +static struct attribute *phytium_pcie_pmu_v1_events_attr[] = { PHYTIUM_PCIE_PMU_EVENT_ATTR(pcie_cycles, 0x00), PHYTIUM_PCIE_PMU_EVENT_ATTR(aw, 0x01), PHYTIUM_PCIE_PMU_EVENT_ATTR(w_last, 0x02), @@ -198,9 +534,130 @@ static struct attribute *phytium_pcie_pmu_events_attr[] = { NULL, }; -static const struct attribute_group phytium_pcie_pmu_events_group = { +static struct attribute *phytium_pcie_pmu_v2_events_attr[] = { + PHYTIUM_PCIE_PMU_EVENT_ATTR(pcie_cycles, 0x0), + PHYTIUM_PCIE_PMU_EVENT_ATTR(aw_g0, 0x1), + PHYTIUM_PCIE_PMU_EVENT_ATTR(b_g0, 0x2), + PHYTIUM_PCIE_PMU_EVENT_ATTR(ar_g0, 0x3), + PHYTIUM_PCIE_PMU_EVENT_ATTR(r_last_g0, 0x4), + PHYTIUM_PCIE_PMU_EVENT_ATTR(r_full_g0, 0x5), + PHYTIUM_PCIE_PMU_EVENT_ATTR(r_err_g0, 0x6), + PHYTIUM_PCIE_PMU_EVENT_ATTR(w_err_g0, 0x7), + PHYTIUM_PCIE_PMU_EVENT_ATTR(atomic_aw_g0, 0x8), + PHYTIUM_PCIE_PMU_EVENT_ATTR(at_aw_g0, 0x9), + PHYTIUM_PCIE_PMU_EVENT_ATTR(at_ar_g0, 0xA), + PHYTIUM_PCIE_PMU_EVENT_ATTR(w_data_aw_g0, 0xB), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_g0, 0xC), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_g0, 0xD), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_max_g0, 0xE), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_min_g0, 0xF), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_max_g0, 0x10), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_min_g0, 0x11), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_total_g0, 0x12), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_total_g0, 0x13), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_0_g0, 0x14), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_1_g0, 0x15), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_2_g0, 0x16), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_3_g0, 0x17), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_0_g0, 0x18), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_1_g0, 0x19), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_2_g0, 0x1A), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_3_g0, 0x1B), + PHYTIUM_PCIE_PMU_EVENT_ATTR(w_data_g0, 0x1C), + PHYTIUM_PCIE_PMU_EVENT_ATTR(w_last_g0, 0x1D), + PHYTIUM_PCIE_PMU_EVENT_ATTR(r_discarded_g0, 0x1E), + PHYTIUM_PCIE_PMU_EVENT_ATTR(aw_g1, 0x1F), + PHYTIUM_PCIE_PMU_EVENT_ATTR(b_g1, 0x20), + PHYTIUM_PCIE_PMU_EVENT_ATTR(ar_g1, 0x21), + PHYTIUM_PCIE_PMU_EVENT_ATTR(r_last_g1, 0x22), + PHYTIUM_PCIE_PMU_EVENT_ATTR(r_full_g1, 0x23), + PHYTIUM_PCIE_PMU_EVENT_ATTR(r_err_g1, 0x24), + PHYTIUM_PCIE_PMU_EVENT_ATTR(w_err_g1, 0x25), + PHYTIUM_PCIE_PMU_EVENT_ATTR(atomic_aw_g1, 0x26), + PHYTIUM_PCIE_PMU_EVENT_ATTR(at_aw_g1, 0x27), + PHYTIUM_PCIE_PMU_EVENT_ATTR(at_ar_g1, 0x28), + PHYTIUM_PCIE_PMU_EVENT_ATTR(w_data_aw_g1, 0x29), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_g1, 0x2A), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_g1, 0x2B), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_max_g1, 0x2C), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_min_g1, 0x2D), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_max_g1, 0x2E), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_min_g1, 0x2F), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_total_g1, 0x30), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_total_g1, 0x31), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_0_g1, 0x32), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_1_g1, 0x33), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_2_g1, 0x34), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_3_g1, 0x35), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_0_g1, 0x36), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_1_g1, 0x37), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_2_g1, 0x38), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_3_g1, 0x39), + PHYTIUM_PCIE_PMU_EVENT_ATTR(aw_g2, 0x3A), + PHYTIUM_PCIE_PMU_EVENT_ATTR(b_g2, 0x3B), + PHYTIUM_PCIE_PMU_EVENT_ATTR(ar_g2, 0x3C), + PHYTIUM_PCIE_PMU_EVENT_ATTR(r_last_g2, 0x3D), + PHYTIUM_PCIE_PMU_EVENT_ATTR(r_full_g2, 0x3E), + PHYTIUM_PCIE_PMU_EVENT_ATTR(r_err_g2, 0x3F), + PHYTIUM_PCIE_PMU_EVENT_ATTR(w_err_g2, 0x40), + PHYTIUM_PCIE_PMU_EVENT_ATTR(atomic_aw_g2, 0x41), + PHYTIUM_PCIE_PMU_EVENT_ATTR(at_aw_g2, 0x42), + PHYTIUM_PCIE_PMU_EVENT_ATTR(at_ar_g2, 0x43), + PHYTIUM_PCIE_PMU_EVENT_ATTR(w_data_aw_g2, 0x44), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_g2, 0x45), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_g2, 0x46), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_max_g2, 0x47), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_min_g2, 0x48), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_max_g2, 0x49), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_min_g2, 0x4A), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_total_g2, 0x4B), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_total_g2, 0x4C), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_0_g2, 0x4D), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_1_g2, 0x4E), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_2_g2, 0x4F), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_3_g2, 0x50), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_0_g2, 0x51), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_1_g2, 0x52), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_2_g2, 0x53), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_3_g2, 0x54), + PHYTIUM_PCIE_PMU_EVENT_ATTR(aw_g3, 0x55), + PHYTIUM_PCIE_PMU_EVENT_ATTR(b_g3, 0x56), + PHYTIUM_PCIE_PMU_EVENT_ATTR(ar_g3, 0x57), + PHYTIUM_PCIE_PMU_EVENT_ATTR(r_last_g3, 0x58), + PHYTIUM_PCIE_PMU_EVENT_ATTR(r_full_g3, 0x59), + PHYTIUM_PCIE_PMU_EVENT_ATTR(r_err_g3, 0x5A), + PHYTIUM_PCIE_PMU_EVENT_ATTR(w_err_g3, 0x5B), + PHYTIUM_PCIE_PMU_EVENT_ATTR(atomic_aw_g3, 0x5C), + PHYTIUM_PCIE_PMU_EVENT_ATTR(at_aw_g3, 0x5D), + PHYTIUM_PCIE_PMU_EVENT_ATTR(at_ar_g3, 0x5E), + PHYTIUM_PCIE_PMU_EVENT_ATTR(w_data_aw_g3, 0x5F), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_g3, 0x60), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_g3, 0x61), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_max_g3, 0x62), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_min_g3, 0x63), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_max_g3, 0x64), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_min_g3, 0x65), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_total_g3, 0x66), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_total_g3, 0x67), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_0_g3, 0x68), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_1_g3, 0x69), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_2_g3, 0x6A), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_rd_3_g3, 0x6B), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_0_g3, 0x6C), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_1_g3, 0x6D), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_2_g3, 0x6E), + PHYTIUM_PCIE_PMU_EVENT_ATTR(delay_wr_3_g3, 0x6F), + NULL, +}; + +static const struct attribute_group phytium_pcie_pmu_v1_events_group = { + .name = "events", + .attrs = phytium_pcie_pmu_v1_events_attr, +}; + +static const struct attribute_group phytium_pcie_pmu_v2_events_group = { .name = "events", - .attrs = phytium_pcie_pmu_events_attr, + .attrs = phytium_pcie_pmu_v2_events_attr, }; static DEVICE_ATTR_RO(cpumask); @@ -214,19 +671,374 @@ static const struct attribute_group phytium_pcie_pmu_cpumask_attr_group = { .attrs = phytium_pcie_pmu_cpumask_attrs, }; -static const struct attribute_group *phytium_pcie_pmu_attr_groups[] = { - &phytium_pcie_pmu_format_group, - &phytium_pcie_pmu_events_group, +static const struct attribute_group *phytium_pcie_pmu_v1_attr_groups[] = { + &phytium_pcie_pmu_v1_format_group, + &phytium_pcie_pmu_v1_events_group, + &phytium_pcie_pmu_cpumask_attr_group, + NULL, +}; + +static const struct attribute_group *phytium_pcie_pmu_v2_attr_groups[] = { + &phytium_pcie_pmu_v2_format_group, + &phytium_pcie_pmu_v2_events_group, &phytium_pcie_pmu_cpumask_attr_group, NULL, }; -static u32 phytium_pcie_pmu_get_event_ctrler(struct perf_event *event) +static void phytium_pcie_pmu_v1_set_timer(struct phytium_pcie_pmu *pcie_pmu, u64 th_val) +{ + u32 val_l, val_h; + + val_l = th_val & 0xFFFFFFFF; + val_h = (th_val >> 32) & 0xFFFFFFFF; + writel(val_l, pcie_pmu->base + PCIE_SET_TIMER_L); + writel(val_h, pcie_pmu->base + PCIE_SET_TIMER_H); +} + +static void phytium_pcie_pmu_v1_reset_timer(struct phytium_pcie_pmu *pcie_pmu) +{ + writel(0xFFFFFFFF, pcie_pmu->base + PCIE_SET_TIMER_L); + writel(0xFFFFFFFF, pcie_pmu->base + PCIE_SET_TIMER_H); +} + +static void phytium_pcie_pmu_v1_select_ctrler(struct phytium_pcie_pmu *pcie_pmu) +{ + u32 val, offset; + u32 mask = 0xfffffffc; + + if (pcie_pmu->ver == PCIEA_V1P0) { + if (pcie_pmu->pmu_id == 2) { + mask = 0xffffffcf; + offset = 0x0; + } else + offset = 0xc; + } else { + offset = 0x170; + } + + val = readl(pcie_pmu->cfg_base + offset); + val &= mask; + val |= pcie_pmu->real_ctrler; + writel(val, pcie_pmu->cfg_base + offset); +} + +static void phytium_pcie_pmu_v2_set_timer(struct phytium_pcie_pmu *pcie_pmu, + u64 th_val) +{ + u32 val_l, val_h; + + val_l = th_val & 0xFFFFFFFF; + val_h = (th_val >> 32) & 0xFFFFFFFF; + + writel(val_l, pcie_pmu->base + PCIE_SET_TIMER_L); + writel(val_h, pcie_pmu->base + PCIE_SET_TIMER_H); + +} + +static void phytium_pcie_pmu_v2_reset_timer(struct phytium_pcie_pmu *pcie_pmu) +{ + writel(0xFFFFFFFF, pcie_pmu->base + PCIE_SET_TIMER_L); + writel(0xFFFFFFFF, pcie_pmu->base + PCIE_SET_TIMER_H); + +} + +static void phytium_pcie_pmu_v2_set_delay_threshold(struct phytium_pcie_pmu *pcie_pmu) +{ + u32 clk_fre; + u32 thd0_val, thd1_val, thd2_val; + + // MHz + clk_fre = readl(pcie_pmu->base + PCIE_V2_AXI_CLK_FRE); + + thd0_val = clk_fre * pcie_pmu->event_cfg.delay_thd0_us; + thd1_val = clk_fre * pcie_pmu->event_cfg.delay_thd1_us; + thd2_val = clk_fre * pcie_pmu->event_cfg.delay_thd2_us; + + writel(thd0_val, pcie_pmu->base + PCIE_V2_PMU_TIME_RG_THD_0); + writel(thd1_val, pcie_pmu->base + PCIE_V2_PMU_TIME_RG_THD_1); + writel(thd1_val, pcie_pmu->base + PCIE_V2_PMU_TIME_RG_THD_2); +} + +static void phytium_pcie_pmu_v2_reset_delay_threshold(struct phytium_pcie_pmu *pcie_pmu) +{ + writel(PCIE_PMU_DEFAULT_THD0, pcie_pmu->base + PCIE_V2_PMU_TIME_RG_THD_0); + writel(PCIE_PMU_DEFAULT_THD1, pcie_pmu->base + PCIE_V2_PMU_TIME_RG_THD_1); + writel(PCIE_PMU_DEFAULT_THD2, pcie_pmu->base + PCIE_V2_PMU_TIME_RG_THD_2); +} + +static void phytium_pcie_pmu_v2_set_event_trig_mode(struct phytium_pcie_pmu *pcie_pmu) +{ + writel(0x1, pcie_pmu->base + PCIE_TRIG_MODE); +} + +static void phytium_pcie_pmu_v2_reset_event_trig_mode(struct phytium_pcie_pmu *pcie_pmu) +{ + writel(0, pcie_pmu->base + PCIE_TRIG_MODE); +} + +static void phytium_pcie_pmu_v2_set_event_threshold(struct phytium_pcie_pmu *pcie_pmu, + u32 val, u32 en_bit, u32 offset) +{ + writel(en_bit, pcie_pmu->base + PCIE_EVENT_THRESHOLD_EN); + writel(val, pcie_pmu->base + offset); +} + +static void phytium_pcie_pmu_v2_reset_event_threshold(struct phytium_pcie_pmu *pcie_pmu) +{ + writel(0x0, pcie_pmu->base + PCIE_EVENT_THRESHOLD_EN); + writel(0xFFFFFFFF, pcie_pmu->base + PCIE_EVENT_THRESHOLD_0); + writel(0xFFFFFFFF, pcie_pmu->base + PCIE_EVENT_THRESHOLD_1); +} + +static void phytium_pcie_pmu_v2_select_monitor_channel(struct phytium_pcie_pmu *pcie_pmu) +{ + /* + *peu: 0:c0, 1:c1, 2:c2, 3:c3, 4:iommu_tbu, 5:qtw, 6:pio_in, 7:vdm_in + *pxu: 0:c0, 1:c1, 2:vdm, 3:pio, 4:qtw, 5:iommu_out + *pcu: 0:pio_in, 2:ncc dma_out, 3:iommu_out,4:c0 dma + */ + writel(pcie_pmu->real_ctrler, pcie_pmu->cfg_base + PCIE_V2_PMU_CHANNEL_SEL); +} + +static void phytium_pcie_pmu_v2_set_event_group_sort_mode(struct phytium_pcie_pmu *pcie_pmu) +{ + writel(pcie_pmu->event_cfg.sort_mode, pcie_pmu->base + PCIE_V2_PMU_SORT_MODE); +} + +static void phytium_pcie_pmu_v2_reset_event_group_sort_mode(struct phytium_pcie_pmu *pcie_pmu) +{ + writel(0, pcie_pmu->base + PCIE_V2_PMU_SORT_MODE); +} + +static void phytium_pcie_pmu_v2_set_bdf_id_cfg(struct phytium_pcie_pmu *pcie_pmu, u32 bdf_id) +{ + bdf_id &= 0xFFFF; + writel(bdf_id, pcie_pmu->base + PCIE_V2_PMU_REQID_CFG); +} + +static void phytium_pcie_pmu_v2_reset_bdf_id_cfg(struct phytium_pcie_pmu *pcie_pmu) +{ + writel(0, pcie_pmu->base + PCIE_V2_PMU_REQID_CFG); +} + +static void phytium_pcie_pmu_v1_get_event_config(struct perf_event *event, + struct phytium_pcie_pmu *pcie_pmu) +{ + pcie_pmu->event_cfg.chansel = pcie_pmu_v1_get_u32_ctrler(event); + pcie_pmu->event_cfg.timer_64 = pcie_pmu_v1_get_u64_timer(event); +} + +static void phytium_pcie_pmu_v2_get_event_config(struct perf_event *event, + struct phytium_pcie_pmu *pcie_pmu) { - return FIELD_GET(GENMASK(10, 8), event->attr.config); + u32 delay_thd0_us, delay_thd1_us, delay_thd2_us; + + pcie_pmu->event_cfg.chansel = pcie_pmu_v2_get_chansel(event); + pcie_pmu->event_cfg.by_timer = pcie_pmu_v2_get_by_timer(event); + pcie_pmu->event_cfg.by_evthod = pcie_pmu_v2_get_by_evthod(event); + pcie_pmu->event_cfg.by_trig_mode = pcie_pmu_v2_get_by_trig_mode(event); + pcie_pmu->event_cfg.sort_mode = pcie_pmu_v2_get_sort_mode(event); + pcie_pmu->event_cfg.bdf_id = pcie_pmu_v2_get_bdf_id(event); + pcie_pmu->event_cfg.byargs0 = pcie_pmu_v2_get_byargs0(event); + pcie_pmu->event_cfg.byargs1 = pcie_pmu_v2_get_byargs1(event); + delay_thd0_us = pcie_pmu_v2_get_delay_thd0_us(event); + delay_thd1_us = pcie_pmu_v2_get_delay_thd1_us(event); + delay_thd2_us = pcie_pmu_v2_get_delay_thd2_us(event); + if (delay_thd0_us + delay_thd1_us + delay_thd2_us == 0) { + pcie_pmu->event_cfg.delay_thd0_us = PCIE_PMU_DEFAULT_THD0; + pcie_pmu->event_cfg.delay_thd1_us = PCIE_PMU_DEFAULT_THD1; + pcie_pmu->event_cfg.delay_thd2_us = PCIE_PMU_DEFAULT_THD2; + } else { + pcie_pmu->event_cfg.delay_thd0_us = delay_thd0_us; + pcie_pmu->event_cfg.delay_thd1_us = delay_thd1_us; + pcie_pmu->event_cfg.delay_thd2_us = delay_thd2_us; + } + +} + +static int phytium_pcie_pmu_v1_set_event_config(struct perf_event *event, + struct phytium_pcie_pmu *pcie_pmu) +{ + if (pcie_pmu->event_cfg.timer_64 > 0) + phytium_pcie_pmu_v1_set_timer(pcie_pmu, pcie_pmu->event_cfg.timer_64); + + if (pcie_pmu->ver == PCIEA_V1P5) { + if (pcie_pmu->pmu_id == 2) { + if (pcie_pmu->event_cfg.chansel == 0) + pcie_pmu->event_cfg.chansel = 2; + else if ((pcie_pmu->event_cfg.chansel < 2) || + (pcie_pmu->event_cfg.chansel > 3)) { + dev_warn(pcie_pmu->dev, "Wrong ctrler id(%d) for pciea%u-pmu2!\n", + pcie_pmu->event_cfg.chansel, pcie_pmu->pcie_id); + return -EINVAL; + } + if (pcie_pmu->ctrler_id != pcie_pmu->event_cfg.chansel) { + pcie_pmu->ctrler_id = pcie_pmu->event_cfg.chansel; + pcie_pmu->real_ctrler = pcie_pmu->ctrler_id; + phytium_pcie_pmu_v1_select_ctrler(pcie_pmu); + } + } else { + if (pcie_pmu->event_cfg.chansel != 0) { + dev_warn(pcie_pmu->dev, "Don't set ctrler id(%d) for pciea%u-pmu%d!\n", + pcie_pmu->event_cfg.chansel, pcie_pmu->pcie_id, + pcie_pmu->pmu_id); + return -EINVAL; + } + pcie_pmu->ctrler_id = pcie_pmu->pmu_id; + pcie_pmu->real_ctrler = pcie_pmu->ctrler_id; + } + } else if (pcie_pmu->ver == PCIEA_V1P0) { + switch (pcie_pmu->pmu_id) { + case 0: + if (pcie_pmu->event_cfg.chansel != 0) { + dev_warn(pcie_pmu->dev, + "Wrong ctrler id(%d) for pciea%u-pmu0!\n", + pcie_pmu->event_cfg.chansel, pcie_pmu->pcie_id); + return -EINVAL; + } + break; + case 1: + if (pcie_pmu->event_cfg.chansel == 0) + pcie_pmu->event_cfg.chansel = 1; + else if ((pcie_pmu->event_cfg.chansel < 1) || + (pcie_pmu->event_cfg.chansel > 3)) { + dev_warn(pcie_pmu->dev, + "Wrong ctrler id(%d) for pciea%u-pmu1!\n", + pcie_pmu->event_cfg.chansel, pcie_pmu->pcie_id); + return -EINVAL; + } + break; + case 2: + if (pcie_pmu->event_cfg.chansel == 0) + pcie_pmu->event_cfg.chansel = 4; + else if ((pcie_pmu->event_cfg.chansel < 4) || + (pcie_pmu->event_cfg.chansel > 7)) { + dev_warn(pcie_pmu->dev, + "Wrong ctrler id(%d) for pciea%u-pmu2!\n", + pcie_pmu->event_cfg.chansel, pcie_pmu->pcie_id); + return -EINVAL; + } + break; + default: + dev_err(pcie_pmu->dev, "Unsupported pmu id:%d!\n", + pcie_pmu->pmu_id); + return -EINVAL; + } + + pcie_pmu->ctrler_id = pcie_pmu->event_cfg.chansel; + switch (pcie_pmu->pmu_id) { + case 0: + case 1: + pcie_pmu->real_ctrler = pcie_pmu->ctrler_id; + break; + case 2: + pcie_pmu->real_ctrler = (pcie_pmu->ctrler_id - 4) * 16; + break; + default: + dev_err(pcie_pmu->dev, "Unsupported pmu id:%d!\n", + pcie_pmu->pmu_id); + return -EINVAL; + } + phytium_pcie_pmu_v1_select_ctrler(pcie_pmu); + } + + return 0; } -static u64 phytium_pcie_pmu_read_counter(struct phytium_pcie_pmu *pcie_pmu, +static int phytium_pcie_pmu_v2_set_event_config(struct perf_event *event, + struct phytium_pcie_pmu *pcie_pmu) +{ + pcie_pmu->ctrler_id = pcie_pmu->event_cfg.chansel; + + if (pcie_pmu->event_cfg.chansel >= pcie_pmu->channel_num) { + dev_err(pcie_pmu->dev, "Wrong ctrler id(%d) for pcie-pmu!\n", + pcie_pmu->event_cfg.chansel); + return -EINVAL; + } + + pcie_pmu->real_ctrler = pcie_pmu->ctrler_id; + phytium_pcie_pmu_v2_select_monitor_channel(pcie_pmu); + + if ((pcie_pmu->event_cfg.delay_thd0_us < pcie_pmu->event_cfg.delay_thd1_us) + && (pcie_pmu->event_cfg.delay_thd1_us < pcie_pmu->event_cfg.delay_thd2_us)) { + phytium_pcie_pmu_v2_set_delay_threshold(pcie_pmu); + } else { + dev_err(pcie_pmu->dev, "Wrong delay threshold value for pcie%u-pmu%d!\n", + pcie_pmu->pcie_id, pcie_pmu->pmu_id); + + dev_err(pcie_pmu->dev, "Wrong value:(delay_thd0_us:%d,delay_thd1_us:%d,delay_thd2_us:%d).\n", + pcie_pmu->event_cfg.delay_thd0_us, pcie_pmu->event_cfg.delay_thd1_us, + pcie_pmu->event_cfg.delay_thd2_us); + return -EINVAL; + } + + if (pcie_pmu->event_cfg.by_trig_mode) + phytium_pcie_pmu_v2_set_event_trig_mode(pcie_pmu); + + if (pcie_pmu->event_cfg.by_evthod) { + if (pcie_pmu->event_cfg.by_timer) { + dev_err(pcie_pmu->dev, "The by_ethod and by_timer are set incorrectly!"); + dev_err(pcie_pmu->dev, "Cannot be set for pcie%u-pmu%d at the same time!", + pcie_pmu->pcie_id, pcie_pmu->pmu_id); + return -EINVAL; + } + + if (pcie_pmu->event_cfg.byargs0 > 0) { + pcie_pmu->event_cfg.aw_threshold = pcie_pmu->event_cfg.byargs0; + phytium_pcie_pmu_v2_set_event_threshold(pcie_pmu, + pcie_pmu->event_cfg.aw_threshold, + PCIE_EVENT_AW_THD_ENBIT, PCIE_EVENT_THRESHOLD_0); + } else if (pcie_pmu->event_cfg.byargs1 > 0) { + pcie_pmu->event_cfg.ar_threshold = pcie_pmu->event_cfg.byargs1; + phytium_pcie_pmu_v2_set_event_threshold(pcie_pmu, + pcie_pmu->event_cfg.ar_threshold, + PCIE_EVENT_AR_THD_ENBIT, PCIE_EVENT_THRESHOLD_1); + } + } else if (pcie_pmu->event_cfg.by_timer == 1 && pcie_pmu->event_cfg.byargs0 > 0) { + pcie_pmu->event_cfg.timer_64 = pcie_pmu->event_cfg.byargs0; + phytium_pcie_pmu_v2_set_timer(pcie_pmu, pcie_pmu->event_cfg.timer_64); + } + + if (pcie_pmu->event_cfg.sort_mode > 1) + phytium_pcie_pmu_v2_set_bdf_id_cfg(pcie_pmu, pcie_pmu->event_cfg.bdf_id); + + phytium_pcie_pmu_v2_set_event_group_sort_mode(pcie_pmu); + + return 0; +} + +static void phytium_pcie_pmu_v1_reset_event_config(struct phytium_pcie_pmu *pcie_pmu) +{ + phytium_pcie_pmu_v1_reset_timer(pcie_pmu); + pcie_pmu->event_cfg.timer_64 = 0; + pcie_pmu->event_cfg.chansel = -1; +} + +static void phytium_pcie_pmu_v2_reset_event_config(struct phytium_pcie_pmu *pcie_pmu) +{ + phytium_pcie_pmu_v2_reset_timer(pcie_pmu); + phytium_pcie_pmu_v2_reset_event_trig_mode(pcie_pmu); + phytium_pcie_pmu_v2_reset_delay_threshold(pcie_pmu); + phytium_pcie_pmu_v2_reset_event_threshold(pcie_pmu); + phytium_pcie_pmu_v2_reset_event_group_sort_mode(pcie_pmu); + phytium_pcie_pmu_v2_reset_bdf_id_cfg(pcie_pmu); + pcie_pmu->event_cfg.by_timer = 0; + pcie_pmu->event_cfg.by_evthod = 0; + pcie_pmu->event_cfg.by_trig_mode = 0; + pcie_pmu->event_cfg.sort_mode = 0; + pcie_pmu->event_cfg.bdf_id = 0; + pcie_pmu->event_cfg.byargs0 = 0; + pcie_pmu->event_cfg.byargs1 = 0; + pcie_pmu->event_cfg.timer_64 = 0; + pcie_pmu->event_cfg.chansel = -1; + pcie_pmu->event_cfg.aw_threshold = 0; + pcie_pmu->event_cfg.ar_threshold = 0; + pcie_pmu->event_cfg.delay_thd0_us = PCIE_PMU_DEFAULT_THD0; + pcie_pmu->event_cfg.delay_thd1_us = PCIE_PMU_DEFAULT_THD1; + pcie_pmu->event_cfg.delay_thd2_us = PCIE_PMU_DEFAULT_THD2; +} + +static u64 phytium_pcie_pmu_v1_read_counter(struct phytium_pcie_pmu *pcie_pmu, struct hw_perf_event *hwc) { u32 idx = GET_PCIE_EVENTID(hwc); @@ -234,15 +1046,15 @@ static u64 phytium_pcie_pmu_read_counter(struct phytium_pcie_pmu *pcie_pmu, pcie_data_width; u64 val64 = 0; int i; - u32 counter_offset = pcie_counter_reg_offset[idx]; + u32 counter_offset = pcie_pmu->ops->get_counter_offset(idx); u32 rdelay_num = 127; - if (!EVENT_VALID(idx)) { + if (!EVENT_VALID_V1(idx)) { dev_err(pcie_pmu->dev, "Unsupported event index:%d!\n", idx); return 0; } - if (pcie_pmu->ver == PCIEV1P5 && pcie_pmu->pmu_id == 3) + if (pcie_pmu->ver == PCIEA_V1P5 && pcie_pmu->pmu_id == 3) rdelay_num = 63; switch (idx) { @@ -252,7 +1064,7 @@ static u64 phytium_pcie_pmu_read_counter(struct phytium_pcie_pmu *pcie_pmu, val64 = (u64)cycle_h << 32 | (u64)cycle_l; break; case 15: - pcie_data_width = readl(pcie_pmu->base + PCIE_DATA_WIDTH); + pcie_data_width = readl(pcie_pmu->base + PCIE_V1_DATA_WIDTH); for (i = 0; i < (pcie_data_width / 8); i++) { w_data = readl(pcie_pmu->base + counter_offset + 4 * i); val64 += w_data; @@ -269,10 +1081,8 @@ static u64 phytium_pcie_pmu_read_counter(struct phytium_pcie_pmu *pcie_pmu, break; case 17: for (i = 0; i <= 63; i++) { - wdelay_l = - readl(pcie_pmu->base + counter_offset + 4 * i); - wdelay_h = readl(pcie_pmu->base + counter_offset + - 4 * i + 4); + wdelay_l = readl(pcie_pmu->base + counter_offset + 4 * i); + wdelay_h = readl(pcie_pmu->base + counter_offset + 4 * i + 4); val64 += (u64)wdelay_h << 32 | (u64)wdelay_l; } break; @@ -280,10 +1090,65 @@ static u64 phytium_pcie_pmu_read_counter(struct phytium_pcie_pmu *pcie_pmu, val64 = readl(pcie_pmu->base + counter_offset); break; } + + return val64; +} + +static u64 phytium_pcie_pmu_v2_read_counter(struct phytium_pcie_pmu *pcie_pmu, + struct hw_perf_event *hwc) +{ + int i; + u64 val64; + u32 w_data, pcie_data_width, val_l, val_h; + u32 idx = GET_PCIE_EVENTID(hwc); + u32 counter_offset = pcie_pmu->ops->get_counter_offset(idx); + + if (!EVENT_VALID_V2(idx)) { + dev_err(pcie_pmu->dev, "Unsupported event index:%d!\n", idx); + return 0; + } + + switch (idx) { + case 0x0: + case 0xb: + case 0x29: + case 0x44: + case 0x5f: + // cycles、w_data_aw_g* + val_l = readl(pcie_pmu->base + counter_offset); + val_h = readl(pcie_pmu->base + counter_offset + 4); + val64 = (u64)val_h << 32 | (u64)val_l; + break; + case 0x1C: + // w_data_g0 + pcie_data_width = readl(pcie_pmu->base + PCIE_V2_DATA_WIDTH); + val64 = 0; + for (i = 0; i < (pcie_data_width / 8); i++) { + w_data = readl(pcie_pmu->base + counter_offset + 4 * i); + val64 += w_data; + } + break; + case 0x12: + case 0x13: + case 0x30: + case 0x31: + case 0x4b: + case 0x4c: + case 0x66: + case 0x67: + // rd_total_g*、wr_total_g* + val_l = readl(pcie_pmu->base + counter_offset + 4); + val_h = readl(pcie_pmu->base + counter_offset); + val64 = (u64)val_h << 32 | (u64)val_l; + break; + default: + val64 = readl(pcie_pmu->base + counter_offset); + break; + } return val64; } -static void phytium_pcie_pmu_enable_clk(struct phytium_pcie_pmu *pcie_pmu) +static void phytium_pcie_pmu_v1_enable_clk(struct phytium_pcie_pmu *pcie_pmu) { u32 val; @@ -292,35 +1157,34 @@ static void phytium_pcie_pmu_enable_clk(struct phytium_pcie_pmu *pcie_pmu) writel(val, pcie_pmu->cfg_base); } -static void phytium_pcie_pmu_disable_clk(struct phytium_pcie_pmu *pcie_pmu) +static void phytium_pcie_pmu_v1_disable_clk(struct phytium_pcie_pmu *pcie_pmu) { u32 val; + writel(0x1, pcie_pmu->base + PCIE_CLEAR_EVENT); + val = readl(pcie_pmu->cfg_base); val &= ~(pcie_pmu->clk_bits); + writel(val, pcie_pmu->cfg_base); } -static void phytium_pcie_pmu_select_ctrler(struct phytium_pcie_pmu *pcie_pmu) +static void phytium_pcie_pmu_v2_enable_clk(struct phytium_pcie_pmu *pcie_pmu) { - u32 val, offset; - u32 mask = 0xfffffffc; + u32 val; - if (pcie_pmu->ver == PCIEV1P0) { - if (pcie_pmu->pmu_id == 2) { - mask = 0xffffffcf; - offset = 0x0; - } else - offset = 0xc; - } else { - offset = 0x170; - } + val = readl(pcie_pmu->cfg_base + PCIE_V2_PMU_CLK_EN); + val |= (pcie_pmu->clk_bits); + writel(val, pcie_pmu->cfg_base + PCIE_V2_PMU_CLK_EN); +} - val = readl(pcie_pmu->cfg_base + offset); - val &= mask; - val |= pcie_pmu->real_ctrler; - writel(val, pcie_pmu->cfg_base + offset); +static void phytium_pcie_pmu_v2_disable_clk(struct phytium_pcie_pmu *pcie_pmu) +{ + u32 val; + val = readl(pcie_pmu->cfg_base + PCIE_V2_PMU_CLK_EN); + val &= ~(pcie_pmu->clk_bits); + writel(val, pcie_pmu->cfg_base + PCIE_V2_PMU_CLK_EN); } static void @@ -350,6 +1214,65 @@ phytium_pcie_pmu_get_stop_state(struct phytium_pcie_pmu *pcie_pmu) return val; } +static unsigned long +phytium_pcie_pmu_get_now_state(struct phytium_pcie_pmu *pcie_pmu) +{ + unsigned long val; + + val = (unsigned long)readl(pcie_pmu->base + PCIE_NOW_STATE); + return val; +} + +static u32 phytium_pcie_pmu_v1_get_counter_offset(u32 idx) +{ + return pcie_v1_counter_reg_offset[idx]; +} + +static u32 phytium_pcie_pmu_v2_get_counter_offset(u32 idx) +{ + return pcie_v2_counter_reg_offset[idx]; +} + +void phytium_pcie_pmu_v1_reset(struct phytium_pcie_pmu *pcie_pmu) +{ + phytium_pcie_pmu_clear_all_counters(pcie_pmu); +} + +void phytium_pcie_pmu_v2_reset(struct phytium_pcie_pmu *pcie_pmu) +{ + phytium_pcie_pmu_clear_all_counters(pcie_pmu); +} + +static const struct phytium_pcie_pmu_ops phytium_pcie_pmu_v1_ops = { + .get_event_config = phytium_pcie_pmu_v1_get_event_config, + .set_event_config = phytium_pcie_pmu_v1_set_event_config, + .reset_event_config = phytium_pcie_pmu_v1_reset_event_config, + .read_counter = phytium_pcie_pmu_v1_read_counter, + .enable_clk = phytium_pcie_pmu_v1_enable_clk, + .disable_clk = phytium_pcie_pmu_v1_disable_clk, + .clear_all_counters = phytium_pcie_pmu_clear_all_counters, + .start_all_counters = phytium_pcie_pmu_start_all_counters, + .stop_all_counters = phytium_pcie_pmu_stop_all_counters, + .get_stop_state = phytium_pcie_pmu_get_stop_state, + .get_counter_offset = phytium_pcie_pmu_v1_get_counter_offset, + .reset_pmu = phytium_pcie_pmu_v1_reset, +}; + +static const struct phytium_pcie_pmu_ops phytium_pcie_pmu_v2_ops = { + .get_event_config = phytium_pcie_pmu_v2_get_event_config, + .set_event_config = phytium_pcie_pmu_v2_set_event_config, + .reset_event_config = phytium_pcie_pmu_v2_reset_event_config, + .read_counter = phytium_pcie_pmu_v2_read_counter, + .enable_clk = phytium_pcie_pmu_v2_enable_clk, + .disable_clk = phytium_pcie_pmu_v2_disable_clk, + .clear_all_counters = phytium_pcie_pmu_clear_all_counters, + .start_all_counters = phytium_pcie_pmu_start_all_counters, + .stop_all_counters = phytium_pcie_pmu_stop_all_counters, + .get_stop_state = phytium_pcie_pmu_get_stop_state, + .get_counter_offset = phytium_pcie_pmu_v2_get_counter_offset, + .reset_pmu = phytium_pcie_pmu_v2_reset, +}; + static unsigned long phytium_pcie_pmu_get_irq_flag(struct phytium_pcie_pmu *pcie_pmu) { @@ -367,6 +1290,18 @@ static int phytium_pcie_pmu_mark_event(struct perf_event *event) int idx = GET_PCIE_EVENTID(hwc); + if (pcie_pmu->ver < PCIEA_V2P0) { + if (!EVENT_VALID_V1(idx)) { + dev_err(pcie_pmu->dev, "Unsupported event index:%d!\n", idx); + return -ENODEV; + } + } else { + if (!EVENT_VALID_V2(idx)) { + dev_err(pcie_pmu->dev, "Unsupported event index:%d!\n", idx); + return -ENODEV; + } + } + if (test_bit(idx, used_mask)) return -EAGAIN; @@ -378,9 +1313,16 @@ static int phytium_pcie_pmu_mark_event(struct perf_event *event) static void phytium_pcie_pmu_unmark_event(struct phytium_pcie_pmu *pcie_pmu, int idx) { - if (!EVENT_VALID(idx)) { - dev_err(pcie_pmu->dev, "Unsupported event index:%d!\n", idx); - return; + if (pcie_pmu->ver < PCIEA_V2P0) { + if (!EVENT_VALID_V1(idx)) { + dev_err(pcie_pmu->dev, "Unsupported event index:%d!\n", idx); + return; + } + } else { + if (!EVENT_VALID_V2(idx)) { + dev_err(pcie_pmu->dev, "Unsupported event index:%d!\n", idx); + return; + } } clear_bit(idx, pcie_pmu->pmu_events.used_mask); @@ -390,7 +1332,6 @@ int phytium_pcie_pmu_event_init(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; struct phytium_pcie_pmu *pcie_pmu; - u32 event_ctrler; if (event->attr.type != event->pmu->type) return -ENOENT; @@ -405,90 +1346,12 @@ int phytium_pcie_pmu_event_init(struct perf_event *event) return -EINVAL; } - if ((event->attr.config & 0x1F) > PHYTIUM_PCIE_MAX_COUNTERS) + if ((event->attr.config & PHYTIUM_PCIE_EVENTS_MAX_MASK) > pcie_pmu->cnts_num) return -EINVAL; if (pcie_pmu->on_cpu == -1) return -EINVAL; - if (pcie_pmu->ver == PCIEV1P5) { - event_ctrler = phytium_pcie_pmu_get_event_ctrler(event); - if (pcie_pmu->pmu_id == 2) { - if (event_ctrler == 0) - event_ctrler = 2; - else if ((event_ctrler < 2) || (event_ctrler > 3)) { - dev_warn(pcie_pmu->dev, "Wrong ctrler id(%d) for pcie-pmu2!\n", - event_ctrler); - return -EINVAL; - } - if (pcie_pmu->ctrler_id != event_ctrler) { - pcie_pmu->ctrler_id = event_ctrler; - pcie_pmu->real_ctrler = pcie_pmu->ctrler_id; - phytium_pcie_pmu_select_ctrler(pcie_pmu); - } - } else { - if (event_ctrler != 0) { - dev_warn(pcie_pmu->dev, "Don't need to set ctrler id(%d) for pcie-pmu%d!\n", - event_ctrler, pcie_pmu->pmu_id); - return -EINVAL; - } - pcie_pmu->ctrler_id = pcie_pmu->pmu_id; - pcie_pmu->real_ctrler = pcie_pmu->ctrler_id; - } - } else { - event_ctrler = phytium_pcie_pmu_get_event_ctrler(event); - switch (pcie_pmu->pmu_id) { - case 0: - if (event_ctrler != 0) { - dev_warn(pcie_pmu->dev, - "Wrong ctrler id(%d) for pcie-pmu0!\n", - event_ctrler); - return -EINVAL; - } - break; - case 1: - if (event_ctrler == 0) - event_ctrler = 1; - else if ((event_ctrler < 1) || (event_ctrler > 3)) { - dev_warn(pcie_pmu->dev, - "Wrong ctrler id(%d) for pcie-pmu1!\n", - event_ctrler); - return -EINVAL; - } - break; - case 2: - if (event_ctrler == 0) - event_ctrler = 4; - else if ((event_ctrler < 4) || (event_ctrler > 7)) { - dev_warn(pcie_pmu->dev, - "Wrong ctrler id(%d) for pcie-pmu2!\n", - event_ctrler); - return -EINVAL; - } - break; - default: - dev_err(pcie_pmu->dev, "Unsupported pmu id:%d!\n", - pcie_pmu->pmu_id); - return -EINVAL; - } - - pcie_pmu->ctrler_id = event_ctrler; - switch (pcie_pmu->pmu_id) { - case 0: - case 1: - pcie_pmu->real_ctrler = pcie_pmu->ctrler_id; - break; - case 2: - pcie_pmu->real_ctrler = (pcie_pmu->ctrler_id - 4) * 16; - break; - default: - dev_err(pcie_pmu->dev, "Unsupported pmu id:%d!\n", - pcie_pmu->pmu_id); - return -EINVAL; - } - phytium_pcie_pmu_select_ctrler(pcie_pmu); - } - hwc->idx = -1; hwc->config_base = event->attr.config; @@ -502,7 +1365,7 @@ void phytium_pcie_pmu_event_update(struct perf_event *event) struct hw_perf_event *hwc = &event->hw; u64 delta; - delta = phytium_pcie_pmu_read_counter(pcie_pmu, hwc); + delta = pcie_pmu->ops->read_counter(pcie_pmu, hwc); local64_add(delta, &event->count); } @@ -528,9 +1391,13 @@ int phytium_pcie_pmu_event_add(struct perf_event *event, int flags) { struct phytium_pcie_pmu *pcie_pmu = to_phytium_pcie_pmu(event->pmu); struct hw_perf_event *hwc = &event->hw; - int idx; + int idx, ret; hwc->state |= PERF_HES_STOPPED; + pcie_pmu->ops->get_event_config(event, pcie_pmu); + ret = pcie_pmu->ops->set_event_config(event, pcie_pmu); + if (ret < 0) + return ret; idx = phytium_pcie_pmu_mark_event(event); if (idx < 0) @@ -548,9 +1415,10 @@ void phytium_pcie_pmu_event_del(struct perf_event *event, int flags) struct hw_perf_event *hwc = &event->hw; phytium_pcie_pmu_event_stop(event, PERF_EF_UPDATE); - phytium_pcie_pmu_unmark_event(pcie_pmu, hwc->idx); + pcie_pmu->ops->reset_event_config(pcie_pmu); + perf_event_update_userpage(event); pcie_pmu->pmu_events.hw_events[hwc->idx] = NULL; } @@ -559,33 +1427,32 @@ void phytium_pcie_pmu_enable(struct pmu *pmu) { struct phytium_pcie_pmu *pcie_pmu = to_phytium_pcie_pmu(pmu); int event_added = bitmap_weight(pcie_pmu->pmu_events.used_mask, - PHYTIUM_PCIE_MAX_COUNTERS); + pcie_pmu->cnts_num); if (event_added) { - phytium_pcie_pmu_clear_all_counters(pcie_pmu); - phytium_pcie_pmu_start_all_counters(pcie_pmu); + pcie_pmu->ops->clear_all_counters(pcie_pmu); + pcie_pmu->ops->enable_clk(pcie_pmu); + pcie_pmu->ops->start_all_counters(pcie_pmu); } } void phytium_pcie_pmu_disable(struct pmu *pmu) { struct phytium_pcie_pmu *pcie_pmu = to_phytium_pcie_pmu(pmu); - int event_added = bitmap_weight(pcie_pmu->pmu_events.used_mask, - PHYTIUM_PCIE_MAX_COUNTERS); + int event_added; + event_added = bitmap_weight(pcie_pmu->pmu_events.used_mask, + pcie_pmu->cnts_num); if (event_added) - phytium_pcie_pmu_stop_all_counters(pcie_pmu); -} - -void phytium_pcie_pmu_reset(struct phytium_pcie_pmu *pcie_pmu) -{ - phytium_pcie_pmu_disable_clk(pcie_pmu); - phytium_pcie_pmu_clear_all_counters(pcie_pmu); + pcie_pmu->ops->stop_all_counters(pcie_pmu); } static const struct acpi_device_id phytium_pcie_pmu_acpi_match[] = { { "PHYT0044", }, { "PHYT0068", }, + { "PHYT3009", }, + { "PHYT300B", }, + { "PHYT300A", }, {}, }; MODULE_DEVICE_TABLE(acpi, phytium_pcie_pmu_acpi_match); @@ -594,54 +1461,114 @@ static irqreturn_t phytium_pcie_pmu_overflow_handler(int irq, void *dev_id) { struct phytium_pcie_pmu *pcie_pmu = dev_id; struct perf_event *event; - unsigned long overflown, stop_state; + unsigned long overflown, stop_state, now_state; int idx; - unsigned long *used_mask = pcie_pmu->pmu_events.used_mask; - int event_added = bitmap_weight(used_mask, PHYTIUM_PCIE_MAX_COUNTERS); - overflown = phytium_pcie_pmu_get_irq_flag(pcie_pmu); + unsigned long *used_mask = pcie_pmu->pmu_events.used_mask; + int event_added = bitmap_weight(used_mask, pcie_pmu->cnts_num); - if (!test_bit(pcie_pmu->irq_bit, &overflown)) + now_state = phytium_pcie_pmu_get_now_state(pcie_pmu); + if (now_state != PCIE_PMU_OVER_STATE) return IRQ_NONE; + if (!event_added) { + pcie_pmu->ops->clear_all_counters(pcie_pmu); + return IRQ_HANDLED; + } + overflown = phytium_pcie_pmu_get_irq_flag(pcie_pmu); stop_state = phytium_pcie_pmu_get_stop_state(pcie_pmu); + dev_dbg(pcie_pmu->dev, "%s, pcie_pmu->irq_bit=%d,overflown=%lu, stop_state=%lu, now_state=%lu, event_added=%u.\n", + __func__, pcie_pmu->irq_bit, overflown, stop_state, now_state, event_added); + if (bitmap_weight(&stop_state, 6)) { - for_each_set_bit(idx, used_mask, PHYTIUM_PCIE_MAX_COUNTERS) { + for_each_set_bit(idx, used_mask, pcie_pmu->cnts_num) { event = pcie_pmu->pmu_events.hw_events[idx]; if (!event) continue; phytium_pcie_pmu_event_update(event); } - phytium_pcie_pmu_clear_all_counters(pcie_pmu); - if ((stop_state & PCIE_PMU_OFL_STOP_TYPE_VAL) == 0) - phytium_pcie_pmu_start_all_counters(pcie_pmu); + pcie_pmu->ops->clear_all_counters(pcie_pmu); + + if (stop_state & PCIE_TIME_STOP_TYPE) + dev_info(pcie_pmu->dev, "Setting time has been reached, pmu stopped!"); + else if (stop_state & PCIE_EVENT_THD0_STOP_TYPE) + dev_info(pcie_pmu->dev, "Setting event(aw) threshold has been reached, pmu stopped!"); + else if (stop_state & PCIE_EVENT_THD1_STOP_TYPE) + dev_info(pcie_pmu->dev, "Setting event(ar) threshold has been reached, pmu stopped!"); + else + pcie_pmu->ops->start_all_counters(pcie_pmu); return IRQ_HANDLED; } - if (!event_added) { - phytium_pcie_pmu_clear_all_counters(pcie_pmu); - return IRQ_HANDLED; - } + return IRQ_NONE; } static int phytium_pcie_pmu_version(struct platform_device *pdev, - struct phytium_pcie_pmu *pcie_pmu) + struct phytium_pcie_pmu *pcie_pmu) { struct acpi_device *acpi_dev; + const char *hid; + u32 pidr, clkfre, data_width; acpi_dev = ACPI_COMPANION(&pdev->dev); + hid = acpi_device_hid(acpi_dev); if (!strcmp(acpi_device_hid(acpi_dev), "PHYT0044")) { - pcie_pmu->ver = PCIEV1P0; + pcie_pmu->ver = PCIEA_V1P0; + pcie_pmu->dev_id = 0; } else if (!strcmp(acpi_device_hid(acpi_dev), "PHYT0068")) { - pcie_pmu->ver = PCIEV1P5; + pcie_pmu->ver = PCIEA_V1P5; + pcie_pmu->dev_id = 0; + } else if (!strcmp(acpi_device_hid(acpi_dev), "PHYT3009")) { + pidr = readl(pcie_pmu->base + PCIE_PMU_PIDR); + clkfre = readl(pcie_pmu->base + PCIE_V2_AXI_CLK_FRE); + data_width = readl(pcie_pmu->base + PCIE_V2_DATA_WIDTH); + dev_dbg(pcie_pmu->dev, "PCIE0 PIDR=%#x,VER=%#lx,clkfre=%u, data_width=%u.\n", + pidr, (pidr & PCIE_PMU_VER_BIT), clkfre, data_width); + pidr &= PCIE_PMU_VER_BIT; + if (pidr == 0x1) { + pcie_pmu->ver = PCIEA_V2P0; + pcie_pmu->dev_id = 0; + pcie_pmu->channel_num = PCIEA_V2P0_CHANNEL_NUM; + } else { + dev_err(&pdev->dev, "The current driver does not support this device.\n"); + return -ENODEV; + } + } else if (!strcmp(acpi_device_hid(acpi_dev), "PHYT300B")) { + pidr = readl(pcie_pmu->base + PCIE_PMU_PIDR); + clkfre = readl(pcie_pmu->base + PCIE_V2_AXI_CLK_FRE); + data_width = readl(pcie_pmu->base + PCIE_V2_DATA_WIDTH); + dev_dbg(pcie_pmu->dev, "PCIE1 PIDR=%#x,VER=%#lx,clkfre=%u, data_width=%u.\n", + pidr, (pidr & PCIE_PMU_VER_BIT), clkfre, data_width); + pidr &= PCIE_PMU_VER_BIT; + if (pidr == 0x1) { + pcie_pmu->ver = PCIEB_V2P0; + pcie_pmu->channel_num = PCIEB_V2P0_CHANNEL_NUM; + pcie_pmu->dev_id = 1; + } else { + dev_err(&pdev->dev, "The current driver does not support this device.\n"); + return -ENODEV; + } + } else if (!strcmp(acpi_device_hid(acpi_dev), "PHYT300A")) { + pidr = readl(pcie_pmu->base + PCIE_PMU_PIDR); + clkfre = readl(pcie_pmu->base + PCIE_V2_AXI_CLK_FRE); + data_width = readl(pcie_pmu->base + PCIE_V2_DATA_WIDTH); + dev_dbg(pcie_pmu->dev, "PCIE1 PIDR=%#x,VER=%#lx,clkfre=%u, data_width=%u.\n", + pidr, (pidr & PCIE_PMU_VER_BIT), clkfre, data_width); + pidr &= PCIE_PMU_VER_BIT; + if (pidr == 0x1) { + pcie_pmu->ver = PCIEC_V2P0; + pcie_pmu->channel_num = PCIEC_V2P0_CHANNEL_NUM; + pcie_pmu->dev_id = 2; + } else { + dev_err(&pdev->dev, "The current driver does not support this device.\n"); + return -ENODEV; + } } else { dev_err(&pdev->dev, "The current driver does not support this device.\n"); return -ENODEV; - } - return 0; } @@ -654,6 +1581,8 @@ static int phytium_pcie_pmu_init_irq(struct phytium_pcie_pmu *pcie_pmu, if (irq < 0) return irq; + phytium_pcie_pmu_clear_all_counters(pcie_pmu); + ret = devm_request_irq(&pdev->dev, irq, phytium_pcie_pmu_overflow_handler, IRQF_NOBALANCING | IRQF_NO_THREAD | IRQF_SHARED, @@ -672,21 +1601,63 @@ static int phytium_pcie_pmu_init_irq(struct phytium_pcie_pmu *pcie_pmu, static int phytium_pcie_pmu_init_data(struct platform_device *pdev, struct phytium_pcie_pmu *pcie_pmu) { - struct resource *res, *clkres, *irqres; + struct resource *res, *csr, *irqres; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pcie_pmu->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pcie_pmu->base)) { + dev_err(&pdev->dev, "ioremap failed for pcie_pmu resource\n"); + return PTR_ERR(pcie_pmu->base); + } + + csr = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!csr) { + dev_err(&pdev->dev, "failed for get pcie_pmu csr resource.\n"); + return -EINVAL; + } + pcie_pmu->cfg_base = + devm_ioremap(&pdev->dev, csr->start, resource_size(csr)); + if (IS_ERR(pcie_pmu->cfg_base)) { + dev_err(&pdev->dev, + "ioremap failed for pcie_pmu csr resource\n"); + return PTR_ERR(pcie_pmu->cfg_base); + } + + irqres = platform_get_resource(pdev, IORESOURCE_MEM, 2); + if (!irqres) { + dev_err(&pdev->dev, + "failed for get pcie_pmu irq reg resource.\n"); + return -EINVAL; + } + pcie_pmu->irq_reg = + devm_ioremap(&pdev->dev, irqres->start, resource_size(irqres)); + if (IS_ERR(pcie_pmu->irq_reg)) { + dev_err(&pdev->dev, + "ioremap failed for pcie_pmu irq resource\n"); + return PTR_ERR(pcie_pmu->irq_reg); + } + + ret = phytium_pcie_pmu_version(pdev, pcie_pmu); + if (ret) + return ret; if (device_property_read_u32(&pdev->dev, "phytium,die-id", - &pcie_pmu->die_id)) { + &pcie_pmu->die_id)) { dev_err(&pdev->dev, "Can not read phytium,die-id!\n"); return -EINVAL; } if (device_property_read_u32(&pdev->dev, "phytium,pmu-id", - &pcie_pmu->pmu_id)) { + &pcie_pmu->pmu_id)) { dev_err(&pdev->dev, "Can not read phytium,pmu-id!\n"); return -EINVAL; } - if (pcie_pmu->ver == PCIEV1P0) { + if (pcie_pmu->ver == PCIEA_V1P0) { + pcie_pmu->cnts_num = PHYTIUM_PCIE_V1_COUNTERS_NUM; + pcie_pmu->ops = &phytium_pcie_pmu_v1_ops; + switch (pcie_pmu->pmu_id) { case 0: pcie_pmu->clk_bits = 0x1; @@ -703,7 +1674,10 @@ static int phytium_pcie_pmu_init_data(struct platform_device *pdev, } pcie_pmu->irq_bit = pcie_pmu->pmu_id + 4; - } else { + } else if (pcie_pmu->ver == PCIEA_V1P5) { + pcie_pmu->cnts_num = PHYTIUM_PCIE_V1_COUNTERS_NUM; + pcie_pmu->ops = &phytium_pcie_pmu_v1_ops; + if (device_property_read_u32(&pdev->dev, "phytium,pcie-id", &pcie_pmu->pcie_id)) { dev_err(&pdev->dev, "Can not read phytium,pcie-id!\n"); return -EINVAL; @@ -724,47 +1698,21 @@ static int phytium_pcie_pmu_init_data(struct platform_device *pdev, dev_err(&pdev->dev, "Unsupported pmu id:%d!\n", pcie_pmu->pmu_id); break; } - pcie_pmu->irq_bit = pcie_pmu->pcie_id * 4 + pcie_pmu->pmu_id + 16; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pcie_pmu->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(pcie_pmu->base)) { - dev_err(&pdev->dev, "ioremap failed for pcie_pmu resource\n"); - return PTR_ERR(pcie_pmu->base); - } - - clkres = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!clkres) { - dev_err(&pdev->dev, "failed for get pcie_pmu clk resource.\n"); - return -EINVAL; - } - - pcie_pmu->cfg_base = - devm_ioremap(&pdev->dev, clkres->start, resource_size(clkres)); - if (IS_ERR(pcie_pmu->cfg_base)) { - dev_err(&pdev->dev, - "ioremap failed for pcie_pmu csr resource\n"); - return PTR_ERR(pcie_pmu->cfg_base); - } + } else { + pcie_pmu->cnts_num = PHYTIUM_PCIE_V2_COUNTERS_NUM; + pcie_pmu->ops = &phytium_pcie_pmu_v2_ops; - irqres = platform_get_resource(pdev, IORESOURCE_MEM, 2); - if (!irqres) { - dev_err(&pdev->dev, - "failed for get pcie_pmu irq reg resource.\n"); - return -EINVAL; - } + if (device_property_read_u32(&pdev->dev, "phytium,pcie-id", &pcie_pmu->pcie_id)) { + dev_err(&pdev->dev, "Can not read phytium,pcie-id!\n"); + return -EINVAL; + } - pcie_pmu->irq_reg = - devm_ioremap(&pdev->dev, irqres->start, resource_size(irqres)); - if (IS_ERR(pcie_pmu->irq_reg)) { - dev_err(&pdev->dev, - "ioremap failed for pcie_pmu irq resource\n"); - return PTR_ERR(pcie_pmu->irq_reg); + pcie_pmu->clk_bits = PCIEV2_PMU_CLK_EN_BIT; + pcie_pmu->irq_bit = 2 - pcie_pmu->dev_id; } - phytium_pcie_pmu_reset(pcie_pmu); + pcie_pmu->ops->reset_pmu(pcie_pmu); return 0; } @@ -774,10 +1722,6 @@ static int phytium_pcie_pmu_dev_probe(struct platform_device *pdev, { int ret; - ret = phytium_pcie_pmu_version(pdev, pcie_pmu); - if (ret) - return ret; - ret = phytium_pcie_pmu_init_data(pdev, pcie_pmu); if (ret) return ret; @@ -795,6 +1739,7 @@ static int phytium_pcie_pmu_dev_probe(struct platform_device *pdev, static int phytium_pcie_pmu_probe(struct platform_device *pdev) { struct phytium_pcie_pmu *pcie_pmu; + char *name; int ret; @@ -815,13 +1760,14 @@ static int phytium_pcie_pmu_probe(struct platform_device *pdev) return ret; } - if (pcie_pmu->ver == PCIEV1P0) + if (pcie_pmu->ver == PCIEA_V1P0) name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "phyt%u_pcie_pmu%u", pcie_pmu->die_id, pcie_pmu->pmu_id); else name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "phyt%u_pcie%u_pmu%u", pcie_pmu->die_id, pcie_pmu->pcie_id, pcie_pmu->pmu_id); - pcie_pmu->pmu = (struct pmu){ + + pcie_pmu->pmu = (struct pmu) { .name = name, .module = THIS_MODULE, .task_ctx_nr = perf_invalid_context, @@ -833,25 +1779,23 @@ static int phytium_pcie_pmu_probe(struct platform_device *pdev) .start = phytium_pcie_pmu_event_start, .stop = phytium_pcie_pmu_event_stop, .read = phytium_pcie_pmu_event_update, - .attr_groups = phytium_pcie_pmu_attr_groups, }; + if (pcie_pmu->ver < PCIEA_V2P0) + pcie_pmu->pmu.attr_groups = phytium_pcie_pmu_v1_attr_groups; + else + pcie_pmu->pmu.attr_groups = phytium_pcie_pmu_v2_attr_groups; ret = perf_pmu_register(&pcie_pmu->pmu, name, -1); if (ret) { - dev_err(pcie_pmu->dev, "PCIE PMU register failed!\n"); + dev_err(pcie_pmu->dev, "%s register failed!\n", name); cpuhp_state_remove_instance_nocalls( phytium_pcie_pmu_hp_state, &pcie_pmu->node); } - phytium_pcie_pmu_enable_clk(pcie_pmu); + pcie_pmu->ops->enable_clk(pcie_pmu); - if (pcie_pmu->ver == PCIEV1P0) - pr_info("die%d_pcie_pmu%d on cpu%d.\n", - pcie_pmu->die_id, pcie_pmu->pmu_id, pcie_pmu->on_cpu); - else - pr_info("die%d_pcie%d_pmu%d on cpu%d.\n", - pcie_pmu->die_id, pcie_pmu->pcie_id, pcie_pmu->pmu_id, pcie_pmu->on_cpu); + dev_info(pcie_pmu->dev, "%s on cpu%d.\n", name, pcie_pmu->on_cpu); return ret; } @@ -860,7 +1804,7 @@ static int phytium_pcie_pmu_remove(struct platform_device *pdev) { struct phytium_pcie_pmu *pcie_pmu = platform_get_drvdata(pdev); - phytium_pcie_pmu_disable_clk(pcie_pmu); + pcie_pmu->ops->disable_clk(pcie_pmu); perf_pmu_unregister(&pcie_pmu->pmu); cpuhp_state_remove_instance_nocalls( @@ -893,7 +1837,6 @@ int phytium_pcie_pmu_online_cpu(unsigned int cpu, struct hlist_node *node) pcie_pmu->on_cpu = cpu; WARN_ON(irq_set_affinity_hint(pcie_pmu->irq, cpumask_of(cpu))); } - return 0; } From 44b9d5a9545616dfff31232ad82bac70126bd7bc Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Fri, 8 May 2026 15:02:00 +0800 Subject: [PATCH 64/81] drivers/perf: phytium: Add a unified driver support for DDR PMU Introduce a unified driver support for the DDR performance monitoring unit. The driver enables discovery and registration of PMU capabilities in DDR devices. Signed-off-by: Zhang Fuxiang Signed-off-by: Fu Boyi Signed-off-by: Tan Rui Signed-off-by: Wang Yinfeng --- MAINTAINERS | 2 +- drivers/perf/phytium/Kconfig | 26 +- drivers/perf/phytium/Makefile | 1 - drivers/perf/phytium/phytium_ddr_pmu.c | 1758 +++++++++++++++-- drivers/perf/phytium/phytium_dmu_pmu_pd2408.c | 710 ------- 5 files changed, 1637 insertions(+), 860 deletions(-) delete mode 100644 drivers/perf/phytium/phytium_dmu_pmu_pd2408.c diff --git a/MAINTAINERS b/MAINTAINERS index 4eeb9659fc464..9cc7a14058b7f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17548,7 +17548,7 @@ F: Documentation/devicetree/bindings/usb/phytium,usb2-2.0.yaml F: Documentation/devicetree/bindings/usb/phytium,usb2.yaml F: Documentation/devicetree/bindings/usb/phytium.role-sw.yaml F: drivers/usb/phytium/* -F: drivers/perf/phytium/phytium_dmu_pmu_pd2408.c +F: drivers/perf/phytium/phytium_ddr_pmu.c F: drivers/usb/phytium/phytium_usb_v2* F: drivers/usb/typec/role-switch-phytium.c F: arch/arm64/boot/dts/phytium/* diff --git a/drivers/perf/phytium/Kconfig b/drivers/perf/phytium/Kconfig index c1a9d7b52cd41..634afdabfc4a6 100644 --- a/drivers/perf/phytium/Kconfig +++ b/drivers/perf/phytium/Kconfig @@ -7,21 +7,27 @@ menuconfig PHYTIUM_PMU if PHYTIUM_PMU -config PHYT_DMU_PMU_PD2408 - tristate "Phytium PD2408 SoC DDR PMU driver" - depends on (ARCH_PHYTIUM && ACPI && ARM_PHYTIUM_DMU_DEVFREQ) || COMPILE_TEST || (ARCH_PHYTIUM && ACPI && !ARM_PHYTIUM_DMU_DEVFREQ) +config PHYT_DDR_PMU + tristate "Phytium SoC DDR PMU driver" + depends on ARCH_PHYTIUM && ACPI default m - help - Provides support for Phytium PD2408 SoC DDR Controller performance + help + Provides support and unified interface for Phytium SoC DDR Controller performance monitoring unit (PMU). -config PHYT_DDR_PMU - tristate "Phytium SoC DDR PMU driver" - depends on (ARCH_PHYTIUM && ACPI) || COMPILE_TEST +if PHYT_DDR_PMU + +config PHYT_DMU_PMU_PD2408 + tristate "Provide support for phytium PD2408 SoC DDR PMU driver" + depends on (ARCH_PHYTIUM && ACPI && ARM_PHYTIUM_DMU_DEVFREQ) || (ARCH_PHYTIUM && ACPI && !ARM_PHYTIUM_DMU_DEVFREQ) default m - help - Provides support for Phytium SoC DDR Controller performance + help + Provides support for Phytium PD2408 SoC DDR Controller performance monitoring unit (PMU). + When PHYT_DDR_PMU or ARM_PHYTIUM_DMU_DEVFREQ is built as a modules (=m), + this option can only be built as a module (=m) or disabled (=n). + +endif config PHYT_PCIE_PMU tristate "Phytium SoC PCIE PMU driver" diff --git a/drivers/perf/phytium/Makefile b/drivers/perf/phytium/Makefile index 1f9f1e5bf47d4..af37afc6920c5 100644 --- a/drivers/perf/phytium/Makefile +++ b/drivers/perf/phytium/Makefile @@ -1,5 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 # -obj-$(CONFIG_PHYT_DMU_PMU_PD2408) += phytium_dmu_pmu_pd2408.o obj-$(CONFIG_PHYT_DDR_PMU) += phytium_ddr_pmu.o obj-$(CONFIG_PHYT_PCIE_PMU) += phytium_pcie_pmu.o diff --git a/drivers/perf/phytium/phytium_ddr_pmu.c b/drivers/perf/phytium/phytium_ddr_pmu.c index 124c7ad9bdd04..8e3f7de911843 100644 --- a/drivers/perf/phytium/phytium_ddr_pmu.c +++ b/drivers/perf/phytium/phytium_ddr_pmu.c @@ -31,38 +31,235 @@ #undef pr_fmt #define pr_fmt(fmt) "phytium_ddr_pmu: " fmt -#define PHYTIUM_DDR_MAX_COUNTERS 8 -#define DDR_PERF_DRIVER_VERSION "1.3.3" - -#define DDR_START_TIMER 0x000 -#define DDR_STOP_TIMER 0x004 -#define DDR_CLEAR_EVENT 0x008 -#define DDR_SET_TIMER_L 0x00c -#define DDR_SET_TIMER_H 0x010 -#define DDR_TRIG_MODE 0x014 -#define DDR_NOW_STATE 0x0e0 -#define DDR_EVENT_CYCLES 0x0e4 -#define DDR_TPOINT_END_L 0x0e4 -#define DDR_TPOINT_END_H 0x0e8 -#define DDR_STATE_STOP 0x0ec -#define DDR_EVENT_RXREQ 0x100 -#define DDR_EVENT_RXDAT 0x104 -#define DDR_EVENT_TXDAT 0x108 -#define DDR_EVENT_RXREQ_RNS 0x10c -#define DDR_EVENT_RXREQ_WNSP 0x110 -#define DDR_EVENT_RXREQ_WNSF 0x114 -#define DDR_EVENT_BANDWIDTH 0x200 -#define DDR_W_DATA_BASE 0x200 -#define DDR_CLK_FRE 0xe00 -#define DDR_DATA_WIDTH 0xe04 - -#define DDR_PMU_OFL_STOP_TYPE_VAL 0x10 + +#define DDR_PERF_DRIVER_VERSION "1.4.0" + +#define DDR_PMUV1_START_TIMER 0x000 +#define DDR_PMUV1_STOP_TIMER 0x004 +#define DDR_PMUV1_CLEAR_EVENT 0x008 +#define DDR_PMUV1_SET_TIMER_L 0x00c +#define DDR_PMUV1_SET_TIMER_H 0x010 +#define DDR_PMUV1_TRIG_MODE 0x014 +#define DDR_PMUV1_NOW_STATE 0x0e0 +#define DDR_PMUV1_EVENT_CYCLES 0x0e4 +#define DDR_PMUV1_TPOINT_END_L 0x0e4 +#define DDR_PMUV1_TPOINT_END_H 0x0e8 +#define DDR_PMUV1_STATE_STOP 0x0ec +#define DDR_PMUV1_EVENT_RXREQ 0x100 +#define DDR_PMUV1_EVENT_RXDAT 0x104 +#define DDR_PMUV1_EVENT_TXDAT 0x108 +#define DDR_PMUV1_EVENT_RXREQ_RNS 0x10c +#define DDR_PMUV1_EVENT_RXREQ_WNSP 0x110 +#define DDR_PMUV1_EVENT_RXREQ_WNSF 0x114 +#define DDR_PMUV1_EVENT_BANDWIDTH 0x200 +#define DDR_PMUV1_W_DATA_BASE 0x200 +#define DDR_PMUV1_CLK_FRE 0xe00 +#define DDR_PMUV1_DATA_WIDTH 0xe04 + +#define DDR_PMUV1_OFL_STOP_TYPE_VAL 0x10 + #define PBFVER_FUNC_ID 0x82000001 +#define DDR_PMUV2_TIMER_START 0x0 +#define DDR_PMUV2_TIMER_STOP 0x4 +#define DDR_PMUV2_CLEAR_EVENT 0x8 +#define DDR_PMUV2_SET_TIMER_L 0xc +#define DDR_PMUV2_SET_TIMER_H 0x10 +#define DDR_PMUV2_AXI_MONITOR_EN 0x1c + +#define DDR_PMUV2_TIMER_INT_CLEAR 0x2c +#define DDR_PMUV2_AXI_MONITOR_INT_CLEAR 0x30 +#define DDR_PMUV2_TIMER_INT_STA 0x40 +#define DDR_PMUV2_AXI_MONITOR_INT_STA 0x44 +#define DDR_PMUV2_TIMER_INT_MASK 0x54 +#define DDR_PMUV2_AXI_MONITOR_INT_MASK 0x58 + +#define DDR_PMUV2_EVENT_CYCLES 0x208 +#define DDR_PMUV2_EVENT_AXI_READ_CMD_CNT 0x074 +#define DDR_PMUV2_EVENT_AXI_WRITE_CMD_CNT 0x07c +#define DDR_PMUV2_EVENT_AXI_READ_FLUX_CNT 0x084 +#define DDR_PMUV2_EVENT_AXI_WRITE_FLUX_CNT 0x08c + +#define DDR_PMUV2_ALL_EVENT_CLEAR_BIT 0x1 +#define DDR_PMUV2_TIMER_OPT_BIT 0x1 +#define DDR_PMUV2_AXI_MONITOR_OPT_BIT 0x01010101 + +#define DDR_PMUV2_NOTICE_START 0x0 +#define DDR_PMUV2_NOTICE_STOP 0x1 + +#define DDR_PMUV3_SNAPSHOT_PMU 0x000 +#define DDR_PMUV3_START_TIMER 0x008 +#define DDR_PMUV3_CLEAR_TIMER 0x00C +#define DDR_PMUV3_CLK_EN 0x288 +#define DDR_PMUV3_PIDR0 0xFE0 +#define DDR_PMUV3_PMU_VER_BIT GENMASK(7, 0) +#define DDR_PMUV3_PMU_PART_BIT GENMASK(11, 8) + +#define DDR_PMUV3_HPR_FIFO_THRE 0x010 +#define DDR_PMUV3_LPR_FIFO_THRE 0x014 +#define DDR_PMUV3_WR_DCQ_THRE 0x018 +#define DDR_PMUV3_WDP_BUFFER_THRE 0x01C +#define DDR_PMUV3_RDAT_INFO_THRE 0x020 +#define DDR_PMUV3_RDAT_FIFO_THRE 0x024 +#define DDR_PMUV3_RSP_COMP_THRE 0x028 +#define DDR_PMUV3_RSP_DBID_THRE 0x02C +#define DDR_PMUV3_RSP_REQ_THRE 0x030 +#define DDR_PMUV3_RSP_CRQ_THRE 0x034 +#define DDR_PMUV3_RSP_RTQ_THRE 0x038 + +#define DDR_PMUV3_MC_DFI_CMD_CNT_SEL 0x03C +#define DDR_PMUV3_MC_UIF_CNT_SEL 0x040 +#define DDR_PMUV3_MC_CAM_OCCU_CNT_SEL 0x044 +#define DDR_PMUV3_MC_CMD_SCHEDULING_CNT_SEL 0x048 +#define DDR_PMUV3_PORT_CMD_CNT_SEL 0x04C +#define DDR_PMUV3_PORT_CMD_OPCODE_CNT_SEL 0x050 +#define DDR_PMUV3_PORT_CMD_RETRY_CNT_SEL 0x054 +#define DDR_PMUV3_PORT_PREF_STATUS_CNT_SEL 0x058 +#define DDR_PMUV3_PORT_CQ_OCCU_CNT_SEL 0x05C +#define DDR_PMUV3_PORT_DATA_OCCU_CNT_SEL 0x060 +#define DDR_PMUV3_PORT_RSP_OCCU_CNT_SEL 0x064 +#define DDR_PMUV3_MC_RDWR_SWITCH_CNT_SEL 0x2f0 + +#define DDR_PMUV3_MC_DFI_CMD_CNT_L 0x070 +#define DDR_PMUV3_MC_UIF_CNT_L 0x074 +#define DDR_PMUV3_MC_BANK_MAGT_CNT_L 0x078 +#define DDR_PMUV3_MC_CAM_OCCU_CNT_L 0x07C +#define DDR_PMUV3_MC_CAM_OCCU_CNT_H 0x080 +#define DDR_PMUV3_MC_CMD_SCHE_CNT_L 0x084 +#define DDR_PMUV3_MC_CMD_SCHE_CNT_H 0x088 +#define DDR_PMUV3_MC_T_CMD_SCHE_CNT_L 0x08C +#define DDR_PMUV3_MC_T_CMD_SCHE_CNT_H 0x090 +#define DDR_PMUV3_PORT_CMD_CNT_L 0x0B8 +#define DDR_PMUV3_PORT_CMD_OPCODE_CNT_L 0x0BC +#define DDR_PMUV3_PORT_RETRY_CNT_L 0x0C0 +#define DDR_PMUV3_PORT_PREF_STATUS_CNT_L 0x0C4 +#define DDR_PMUV3_PORT_CQ_OCCU_CNT_L 0x0C8 +#define DDR_PMUV3_PORT_CQ_OCCU_CNT_H 0x0CC +#define DDR_PMUV3_PORT_DATA_OCCU_CNT_L 0x0D0 +#define DDR_PMUV3_PORT_DATA_OCCU_CNT_H 0x0D4 +#define DDR_PMUV3_PORT_RSP_OCCU_CNT_L 0x0D8 +#define DDR_PMUV3_PORT_RSP_OCCU_CNT_H 0x0DC +#define DDR_PMUV3_MC_RDWR_SWITCH_CNT_L 0x2f4 +#define DDR_PMUV3_MC_RDWR_SWITCH_CNT_H 0x2f8 +#define DDR_PMUV3_MC_T_RDWR_SWITCH_CNT_L 0x2fc +#define DDR_PMUV3_MC_T_RDWR_SWITCH_CNT_H 0x300 + +#define DDR_PMUV3_GLOBAL_CNT_L 0x068 +#define DDR_PMUV3_GLOBAL_CNT_H 0x06C +#define DDR_PMUV3_MC_CQ_IDLE_CNT_L 0x094 +#define DDR_PMUV3_MC_EXP_GPR_CNT_L 0x098 +#define DDR_PMUV3_MC_EXP_GPW_CNT_L 0x09C +#define DDR_PMUV3_MC_ADDR_COLLISION_CNT_L 0x0A0 +#define DDR_PMUV3_MC_RDCAM_CRITICAL_CNT_L 0x0A4 +#define DDR_PMUV3_MC_RDCAM_CRITICAL_CNT_H 0x0A8 +#define DDR_PMUV3_MC_WRCAM_CRITICAL_CNT_L 0x0AC +#define DDR_PMUV3_MC_WECAM_CRITICAL_CNT_H 0x0B0 +#define DDR_PMUV3_MC_RETRY_CMD_CNT_L 0x0B4 +#define DDR_PMUV3_PORT_WDAT_BE_CNT_L 0x0E0 +#define DDR_PMUV3_PORT_WDAT_RM_BUFFER_DEALLOC_CNT_L 0x0E4 +#define DDR_PMUV3_PORT_RD_CMD_DELAY1 0x2BC +#define DDR_PMUV3_PORT_RD_CMD_DELAY2 0x2C0 +#define DDR_PMUV3_PORT_WR_CMD_DELAY1 0x2C4 +#define DDR_PMUV3_PORT_WR_CMD_DELAY2 0x2C8 + +#define DDR_PMUV3_CNT_OVERFLOW_FLAG 0x0E8 +#define DDR_PMUV3_MC_DFI_CMD_CNT_OFL_BIT BIT(0) +#define DDR_PMUV3_MC_UFI_CMD_CNT_OFL_BIT BIT(1) +#define DDR_PMUV3_MC_BANK_MAGT_CNT_OFL_BIT BIT(2) +#define DDR_PMUV3_MC_CQ_IDLE_CNT_OFL_BIT BIT(3) +#define DDR_PMUV3_MC_EXP_GPR_CNT_OFL_BIT BIT(4) +#define DDR_PMUV3_MC_EXP_GPW_CNT_OFL_BIT BIT(5) +#define DDR_PMUV3_MC_ADDR_COLLISION_CNT_OFL_BIT BIT(6) + +#define DDR_PMUV3_PORT_CMD_CNT_OFL_BIT BIT(7) +#define DDR_PMUV3_PORT_CMD_OPCODE_CNT_OFL_BIT BIT(8) +#define DDR_PMUV3_PORT_RETRY_CNT_OFL_BIT BIT(9) +#define DDR_PMUV3_PORT_PREF_STATUS_CNT_OFL_BIT BIT(10) +#define DDR_PMUV3_PORT_WDAT_BE_CNT_OFL_BIT BIT(11) +#define DDR_PMUV3_PORT_WDAT_RM_BUFFER_DEALLOC_CNT_OFL_BIT BIT(12) + +#define DDR_PMUV3_MC_DFI_CMD_SEL_MASK GENMASK(3, 0) +#define DDR_PMUV3_MC_UIF_CMD_SEL_MASK GENMASK(2, 0) +#define DDR_PMUV3_MC_BANK_SRC_SEL_MASK GENMASK(6, 5) +#define DDR_PMUV3_MC_CAM_OCCU_SEL_MASK GENMASK(4, 0) +#define DDR_PMUV3_MC_T_CMD_SCHE_SEL_MASK GENMASK(7, 4) +#define DDR_PMUV3_MC_CMD_SCHE_SEL_MASK GENMASK(3, 0) +#define DDR_PMUV3_PORT_CMD_SEL_MASK GENMASK(2, 0) +#define DDR_PMUV3_PORT_CMD_OPCODE_SEL_MASK GENMASK(2, 0) +#define DDR_PMUV3_PORT_CMD_RETRY_SEL_MASK GENMASK(2, 0) +#define DDR_PMUV3_PORT_PREF_STATUS_SEL_MASK GENMASK(2, 0) + +#define DDR_PMUV3_LPR_FIFO_OCCU_SEL_MASK GENMASK(3, 0) +#define DDR_PMUV3_HPR_FIFO_OCCU_SEL_MASK GENMASK(3, 0) +#define DDR_PMUV3_WR_DCQ_OCCU_SEL_MASK GENMASK(3, 0) + +#define DDR_PMUV3_RDAT_INFO_OCCU_SEL_MASK GENMASK(3, 0) +#define DDR_PMUV3_RDAT_FIFO_OCCU_SEL_MASK GENMASK(3, 0) +#define DDR_PMUV3_WDP_BUFFER_OCCU_SEL_MASK GENMASK(3, 0) + +#define DDR_PMUV3_RSP_COMP_OCCU_SEL_MASK GENMASK(3, 0) +#define DDR_PMUV3_REP_DBID_OCCU_SEL_MASK GENMASK(3, 0) +#define DDR_PMUV3_RSP_CRQ_OCCU_SEL_MASK GENMASK(3, 0) +#define DDR_PMUV3_RSP_REQ_OCCU_SEL_MASK GENMASK(3, 0) +#define DDR_PMUV3_RSP_RTQ_OCCU_SEL_MASK GENMASK(3, 0) + +#define DDR_PMUV3_MC_T_RDWR_SWITCH_CNT_SEL_MASK GENMASK(7, 4) +#define DDR_PMUV3_MC_RDWR_SWITCH_CNT_SEL_MASK GENMASK(3, 0) + +#define DDR_PMUV3_HPR_FIFO_THRE_BIT_SIZE 6 +#define DDR_PMUV3_LPR_FIFO_THRE_BIT_SIZE 6 +#define DDR_PMUV3_WR_DCQ_THRE_BIT_SIZE 6 +#define DDR_PMUV3_WDP_BUFFER_THRE_BIT_SIZE 6 +#define DDR_PMUV3_RDAT_INFO_THRE_BIT_SIZE 7 +#define DDR_PMUV3_RDAT_FIFO_THRE_BIT_SIZE 7 +#define DDR_PMUV3_RSP_COMP_THRE_BIT_SIZE 5 +#define DDR_PMUV3_RSP_DBID_THRE_BIT_SIZE 5 +#define DDR_PMUV3_RSP_REQ_THRE_BIT_SIZE 6 +#define DDR_PMUV3_RSP_CRQ_THRE_BIT_SIZE 6 +#define DDR_PMUV3_RSP_RTQ_THRE_BIT_SIZE 6 + +#define DDR_PMUV3_MC_DFI_CMD_EVENT_NUM 15 +#define DDR_PMUV3_MC_UIF_CMD_EVENT_NUM 5 +#define DDR_PMUV3_MC_BANK_SRC_EVENT_NUM 3 +#define DDR_PMUV3_MC_CAM_OCCU_EVENT_NUM 18 +#define DDR_PMUV3_MC_T_CMD_SCHE_EVENT_NUM 16 +#define DDR_PMUV3_MC_CMD_SCHE_EVENT_NUM 16 +#define DDR_PMUV3_PORT_CMD_EVENT_NUM 7 +#define DDR_PMUV3_PORT_CMD_OPCODE_EVENT_NUM 6 +#define DDR_PMUV3_PORT_CMD_RETRY_EVENT_NUM 5 +#define DDR_PMUV3_PORT_PREF_STATUS_EVENT_NUM 5 +#define DDR_PMUV3_LPR_FIFO_OCCU_EVENT_NUM 3 +#define DDR_PMUV3_HPR_FIFO_OCCU_EVENT_NUM 3 +#define DDR_PMUV3_WR_DCQ_OCCU_EVENT_NUM 3 +#define DDR_PMUV3_RDAT_INFO_OCCU_EVENT_NUM 3 +#define DDR_PMUV3_RDAT_FIFO_OCCU_EVENT_NUM 3 +#define DDR_PMUV3_WDP_BUFFER_OCCU_EVENT_NUM 3 +#define DDR_PMUV3_RSP_COMP_OCCU_EVENT_NUM 3 +#define DDR_PMUV3_RSP_DBID_OCCU_EVENT_NUM 3 +#define DDR_PMUV3_RSP_CRQ_OCCU_EVENT_NUM 3 +#define DDR_PMUV3_RSP_REQ_OCCU_EVENT_NUM 3 +#define DDR_PMUV3_RSP_RTQ_OCCU_EVENT_NUM 3 +#define DDR_PMUV3_MC_RDWR_SWTICH_EVENT_NUM 12 +#define DDR_PMUV3_MC_T_RDWR_SWTICH_EVENT_NUM 12 +#define DDR_PMUV3_SPECIAL_EVENT_NUM 12 + +#define PHYTIUM_DDR_MAX_COUNTERS 165 +#define PHYTIUM_DDR_PMUV1_COUNTERS_NUM 8 +#define PHYTIUM_DDR_PMUV2_COUNTERS_NUM 5 +#define PHYTIUM_DDR_PMUV3_COUNTERS_NUM 165 +#define PHYTIUM_DDR_EVENTS_MAX_MASKi 0x7 + +#define GET_DDR_PMUV1V2_EVENTID(hwc) (hwc->config_base & PHYTIUM_DDR_EVENTS_MAX_MASK) +#define EVENT_VALID_V1(idx) ((idx >= 0) && (idx < PHYTIUM_DDR_PMUV1_COUNTERS_NUM)) +#define EVENT_VALID_V2(idx) ((idx >= 0) && (idx < PHYTIUM_DDR_PMUV2_COUNTERS_NUM)) +#define EVENT_VALID_V3(idx) ((idx >= 0) && (idx < PHYTIUM_DDR_PMUV3_COUNTERS_NUM)) #define to_phytium_ddr_pmu(p) (container_of(p, struct phytium_ddr_pmu, pmu)) enum { - DDRV1P0 = 0x01, - DDRV1P5 = 0x02, + DDR_PMUV1P0 = 0x01, + DDR_PMUV1P5 = 0x02, + DDR_PMUV2P0, + DDR_PMUV3P0, + }; static int phytium_ddr_pmu_hp_state; @@ -72,13 +269,85 @@ struct phytium_ddr_pmu_hwevents { DECLARE_BITMAP(used_mask, PHYTIUM_DDR_MAX_COUNTERS); }; +int used_event_v2; + +struct phytium_ddr_pmu_v3_port_occu_thre { + u32 lpr_fifo_low_thre; + u32 lpr_fifo_hight_thre; + u32 hpr_fifo_low_thre; + u32 hpr_fifo_hight_thre; + u32 wr_dcq_low_thre; + u32 wr_dcq_hight_thre; + + u32 rdat_info_low_thre; + u32 rdat_info_hight_thre; + u32 rdat_fifo_low_thre; + u32 rdat_fifo_hight_thre; + u32 wdp_buffer_low_thre; + u32 wdp_buffer_hight_thre; + + u32 rsp_comp_low_thre; + u32 rsp_comp_hight_thre; + + u32 rsp_dbid_low_thre; + u32 rsp_dbid_hight_thre; + u32 rsp_crq_low_thre; + u32 rsp_crq_hight_thre; + u32 rsp_req_low_thre; + u32 rsp_req_hight_thre; + u32 rsp_rtq_low_thre; + u32 rsp_rtq_hight_thre; +}; + +enum ddr_pmu_v3_event_type { + DDR_PMUV3_MC_DFI_CMD_EVTYPE, + DDR_PMUV3_MC_UIF_CMD_EVTYPE, + DDR_PMUV3_MC_BANK_SRC_EVTYPE, + DDR_PMUV3_MC_CAM_OCCU_EVTYPE, + DDR_PMUV3_MC_T_CMD_SCHE_EVTYPE, + DDR_PMUV3_MC_CMD_SCHE_EVTYPE, + DDR_PMUV3_PORT_CMD_EVTYPE, + DDR_PMUV3_PORT_CMD_OPCODE_EVTYPE, + DDR_PMUV3_PORT_CMD_RETRY_EVTYPE, + DDR_PMUV3_PORT_PREF_STATUS_EVTYPE, + DDR_PMUV3_MC_RDWR_SWTICH_EVTYPE, + DDR_PMUV3_MC_T_RDWR_SWTICH_EVTYPE, + + DDR_PMUV3_LPR_FIFO_OCCU_EVTYPE, + DDR_PMUV3_HPR_FIFO_OCCU_EVTYPE, + DDR_PMUV3_WR_DCQ_OCCU_EVTYPE, + + DDR_PMUV3_RDAT_INFO_OCCU_EVTYPE, + DDR_PMUV3_RDAT_FIFO_OCCU_EVTYPE, + DDR_PMUV3_WDP_BUFFER_OCCU_EVTYPE, + + DDR_PMUV3_RSP_COMP_OCCU_EVTYPE, + DDR_PMUV3_RSP_DBID_OCCU_EVTYPE, + DDR_PMUV3_RSP_CRQ_OCCU_EVTYPE, + DDR_PMUV3_RSP_REQ_OCCU_EVTYPE, + DDR_PMUV3_RSP_RTQ_OCCU_EVTYPE, + + DDR_PMUV3_SPECIAL_EVTYPE +}; + +struct phytium_ddr_pmu_v3_event_attr { + struct device_attribute attr; + enum ddr_pmu_v3_event_type type; + u8 eventid; + u8 occupid; + u8 cntsize; + u8 occuflag; +}; + struct phytium_ddr_pmu { struct device *dev; void __iomem *base; void __iomem *cfg_base; void __iomem *irq_reg; struct pmu pmu; + const struct phytium_ddr_pmu_ops *ops; struct phytium_ddr_pmu_hwevents pmu_events; + struct phytium_ddr_pmu_v3_port_occu_thre port_occu_thre; u32 die_id; u32 ddr_id; u32 pmu_id; @@ -86,16 +355,21 @@ struct phytium_ddr_pmu { int on_cpu; int irq; int ver; + int cnts_num; + bool used_flag; struct hlist_node node; }; -#define GET_DDR_EVENTID(hwc) (hwc->config_base & 0x7) -#define EVENT_VALID(idx) ((idx >= 0) && (idx < PHYTIUM_DDR_MAX_COUNTERS)) - -static const u32 ddr_counter_reg_offset[] = { - DDR_EVENT_CYCLES, DDR_EVENT_RXREQ, DDR_EVENT_RXDAT, - DDR_EVENT_TXDAT, DDR_EVENT_RXREQ_RNS, DDR_EVENT_RXREQ_WNSP, - DDR_EVENT_RXREQ_WNSF, DDR_EVENT_BANDWIDTH +struct phytium_ddr_pmu_ops { + irqreturn_t (*overflow_handler)(int irq, void *dev_id); + u64 (*read_counter)(struct phytium_ddr_pmu *pmu, struct perf_event *event); + void (*clear_all_counters)(struct phytium_ddr_pmu *pmu); + void (*start_all_counters)(struct phytium_ddr_pmu *pmu); + void (*stop_all_counters)(struct phytium_ddr_pmu *pmu); + void (*enable_counters)(struct phytium_ddr_pmu *pmu); + void (*disable_counters)(struct phytium_ddr_pmu *pmu); + void (*enable_clk)(struct phytium_ddr_pmu *pmu); + void (*disable_clk)(struct phytium_ddr_pmu *pmu); }; ssize_t phytium_ddr_pmu_format_sysfs_show(struct device *dev, @@ -120,6 +394,28 @@ ssize_t phytium_ddr_pmu_event_sysfs_show(struct device *dev, return sprintf(page, "config=0x%lx\n", (unsigned long)eattr->var); } +static bool phytium_ddr_pmu_v3_is_port_occup_event(enum ddr_pmu_v3_event_type type, + unsigned int occuflag) +{ + bool ret = (type != DDR_PMUV3_MC_CAM_OCCU_EVTYPE) && (occuflag == 1); + + return ret; +} + +static ssize_t phytium_ddr_pmu_v3_event_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct phytium_ddr_pmu_v3_event_attr *eattr; + + eattr = container_of(attr, typeof(*eattr), attr); + + if (phytium_ddr_pmu_v3_is_port_occup_event(eattr->type, eattr->occuflag)) + return snprintf(buf, PAGE_SIZE, "type=0x%x,eventid=0x%x,low_thre=?,hight_thre=?\n", + eattr->type, eattr->eventid); + + return snprintf(buf, PAGE_SIZE, "type=0x%x,eventid=0x%x\n", eattr->type, eattr->eventid); +} + static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -138,35 +434,517 @@ static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, PHYTIUM_PMU_ATTR(_name, phytium_ddr_pmu_format_sysfs_show, \ (void *)_config) -#define PHYTIUM_DDR_PMU_EVENT_ATTR(_name, _config) \ +static const u32 ddr_pmu_v1_counter_reg_offset[] = { + DDR_PMUV1_EVENT_CYCLES, DDR_PMUV1_EVENT_RXREQ, DDR_PMUV1_EVENT_RXDAT, + DDR_PMUV1_EVENT_TXDAT, DDR_PMUV1_EVENT_RXREQ_RNS, DDR_PMUV1_EVENT_RXREQ_WNSP, + DDR_PMUV1_EVENT_RXREQ_WNSF, DDR_PMUV1_EVENT_BANDWIDTH +}; + +static const u32 ddr_pmu_v2_counter_reg_offset[] = { + DDR_PMUV2_EVENT_CYCLES, + DDR_PMUV2_EVENT_AXI_WRITE_FLUX_CNT, DDR_PMUV2_EVENT_AXI_READ_FLUX_CNT, + DDR_PMUV2_EVENT_AXI_WRITE_CMD_CNT, DDR_PMUV2_EVENT_AXI_READ_CMD_CNT +}; + +#define PHYTIUM_DDR_PMUV1V2_EVENT_ATTR(_name, _config) \ PHYTIUM_PMU_ATTR(_name, phytium_ddr_pmu_event_sysfs_show, \ (unsigned long)_config) -static struct attribute *phytium_ddr_pmu_format_attr[] = { +static struct attribute *phytium_ddr_pmu_v1v2_format_attr[] = { PHYTIUM_DDR_PMU_FORMAT_ATTR(event, "config:0-2"), NULL, }; -static const struct attribute_group phytium_ddr_pmu_format_group = { - .name = "format", - .attrs = phytium_ddr_pmu_format_attr, -}; - -static struct attribute *phytium_ddr_pmu_events_attr[] = { - PHYTIUM_DDR_PMU_EVENT_ATTR(ddr_cycles, 0x00), - PHYTIUM_DDR_PMU_EVENT_ATTR(rxreq, 0x01), - PHYTIUM_DDR_PMU_EVENT_ATTR(rxdat, 0x02), - PHYTIUM_DDR_PMU_EVENT_ATTR(txdat, 0x03), - PHYTIUM_DDR_PMU_EVENT_ATTR(rxreq_rns, 0x04), - PHYTIUM_DDR_PMU_EVENT_ATTR(rxreq_wnsp, 0x05), - PHYTIUM_DDR_PMU_EVENT_ATTR(rxreq_wnsf, 0x06), - PHYTIUM_DDR_PMU_EVENT_ATTR(bandwidth, 0x07), +static struct attribute *phytium_ddr_pmu_v1_events_attr[] = { + PHYTIUM_DDR_PMUV1V2_EVENT_ATTR(ddr_cycles, 0x00), + PHYTIUM_DDR_PMUV1V2_EVENT_ATTR(rxreq, 0x01), + PHYTIUM_DDR_PMUV1V2_EVENT_ATTR(rxdat, 0x02), + PHYTIUM_DDR_PMUV1V2_EVENT_ATTR(txdat, 0x03), + PHYTIUM_DDR_PMUV1V2_EVENT_ATTR(rxreq_rns, 0x04), + PHYTIUM_DDR_PMUV1V2_EVENT_ATTR(rxreq_wnsp, 0x05), + PHYTIUM_DDR_PMUV1V2_EVENT_ATTR(rxreq_wnsf, 0x06), + PHYTIUM_DDR_PMUV1V2_EVENT_ATTR(bandwidth, 0x07), + NULL, +}; + +static struct attribute *phytium_ddr_pmu_v2_events_attr[] = { + PHYTIUM_DDR_PMUV1V2_EVENT_ATTR(ddr_axi_cycles, 0x00), + PHYTIUM_DDR_PMUV1V2_EVENT_ATTR(axi_write_flux, 0x01), + PHYTIUM_DDR_PMUV1V2_EVENT_ATTR(axi_read_flux, 0x02), + PHYTIUM_DDR_PMUV1V2_EVENT_ATTR(axi_write_cmd, 0x03), + PHYTIUM_DDR_PMUV1V2_EVENT_ATTR(axi_read_cmd, 0x04), + NULL, +}; + +static const u32 phytium_ddr_pmu_v3_event_num[] = { + DDR_PMUV3_MC_DFI_CMD_EVENT_NUM, + DDR_PMUV3_MC_UIF_CMD_EVENT_NUM, + DDR_PMUV3_MC_BANK_SRC_EVENT_NUM, + DDR_PMUV3_MC_CAM_OCCU_EVENT_NUM, + DDR_PMUV3_MC_T_CMD_SCHE_EVENT_NUM, + DDR_PMUV3_MC_CMD_SCHE_EVENT_NUM, + DDR_PMUV3_PORT_CMD_EVENT_NUM, + DDR_PMUV3_PORT_CMD_OPCODE_EVENT_NUM, + DDR_PMUV3_PORT_CMD_RETRY_EVENT_NUM, + DDR_PMUV3_PORT_PREF_STATUS_EVENT_NUM, + DDR_PMUV3_MC_RDWR_SWTICH_EVENT_NUM, + DDR_PMUV3_MC_T_RDWR_SWTICH_EVENT_NUM, + DDR_PMUV3_LPR_FIFO_OCCU_EVENT_NUM, + DDR_PMUV3_HPR_FIFO_OCCU_EVENT_NUM, + DDR_PMUV3_WR_DCQ_OCCU_EVENT_NUM, + DDR_PMUV3_RDAT_INFO_OCCU_EVENT_NUM, + DDR_PMUV3_RDAT_FIFO_OCCU_EVENT_NUM, + DDR_PMUV3_WDP_BUFFER_OCCU_EVENT_NUM, + DDR_PMUV3_RSP_COMP_OCCU_EVENT_NUM, + DDR_PMUV3_RSP_DBID_OCCU_EVENT_NUM, + DDR_PMUV3_RSP_CRQ_OCCU_EVENT_NUM, + DDR_PMUV3_RSP_REQ_OCCU_EVENT_NUM, + DDR_PMUV3_RSP_RTQ_OCCU_EVENT_NUM, + DDR_PMUV3_SPECIAL_EVENT_NUM +}; + +static const u32 phytium_ddr_pmu_v3_event_sel_reg_offset[] = { + DDR_PMUV3_MC_DFI_CMD_CNT_SEL, + DDR_PMUV3_MC_UIF_CNT_SEL, + DDR_PMUV3_MC_CAM_OCCU_CNT_SEL, + DDR_PMUV3_MC_CAM_OCCU_CNT_SEL, + DDR_PMUV3_MC_CMD_SCHEDULING_CNT_SEL, + DDR_PMUV3_MC_CMD_SCHEDULING_CNT_SEL, + DDR_PMUV3_PORT_CMD_CNT_SEL, + DDR_PMUV3_PORT_CMD_OPCODE_CNT_SEL, + DDR_PMUV3_PORT_CMD_RETRY_CNT_SEL, + DDR_PMUV3_PORT_PREF_STATUS_CNT_SEL, + DDR_PMUV3_MC_RDWR_SWITCH_CNT_SEL, + DDR_PMUV3_MC_RDWR_SWITCH_CNT_SEL, + DDR_PMUV3_PORT_CQ_OCCU_CNT_SEL, + DDR_PMUV3_PORT_CQ_OCCU_CNT_SEL, + DDR_PMUV3_PORT_CQ_OCCU_CNT_SEL, + DDR_PMUV3_PORT_DATA_OCCU_CNT_SEL, + DDR_PMUV3_PORT_DATA_OCCU_CNT_SEL, + DDR_PMUV3_PORT_DATA_OCCU_CNT_SEL, + DDR_PMUV3_PORT_RSP_OCCU_CNT_SEL, + DDR_PMUV3_PORT_RSP_OCCU_CNT_SEL, + DDR_PMUV3_PORT_RSP_OCCU_CNT_SEL, + DDR_PMUV3_PORT_RSP_OCCU_CNT_SEL, + DDR_PMUV3_PORT_RSP_OCCU_CNT_SEL +}; + +static const u32 phytium_ddr_pmu_v3_cnt_sel_mask_offset[] = { + DDR_PMUV3_MC_DFI_CMD_SEL_MASK, + DDR_PMUV3_MC_UIF_CMD_SEL_MASK, + DDR_PMUV3_MC_BANK_SRC_SEL_MASK, + DDR_PMUV3_MC_CAM_OCCU_SEL_MASK, + DDR_PMUV3_MC_T_CMD_SCHE_SEL_MASK, + DDR_PMUV3_MC_CMD_SCHE_SEL_MASK, + DDR_PMUV3_PORT_CMD_SEL_MASK, + DDR_PMUV3_PORT_CMD_OPCODE_SEL_MASK, + DDR_PMUV3_PORT_CMD_RETRY_SEL_MASK, + DDR_PMUV3_PORT_PREF_STATUS_SEL_MASK, + DDR_PMUV3_MC_RDWR_SWITCH_CNT_SEL_MASK, + DDR_PMUV3_MC_T_RDWR_SWITCH_CNT_SEL_MASK, + DDR_PMUV3_LPR_FIFO_OCCU_SEL_MASK, + DDR_PMUV3_HPR_FIFO_OCCU_SEL_MASK, + DDR_PMUV3_WR_DCQ_OCCU_SEL_MASK, + DDR_PMUV3_RDAT_INFO_OCCU_SEL_MASK, + DDR_PMUV3_RDAT_FIFO_OCCU_SEL_MASK, + DDR_PMUV3_WDP_BUFFER_OCCU_SEL_MASK, + DDR_PMUV3_RSP_COMP_OCCU_SEL_MASK, + DDR_PMUV3_REP_DBID_OCCU_SEL_MASK, + DDR_PMUV3_RSP_CRQ_OCCU_SEL_MASK, + DDR_PMUV3_RSP_REQ_OCCU_SEL_MASK, + DDR_PMUV3_RSP_RTQ_OCCU_SEL_MASK +}; + +static const u32 phytium_ddr_pmu_v3_thre_reg_offset[] = { + DDR_PMUV3_LPR_FIFO_THRE, + DDR_PMUV3_HPR_FIFO_THRE, + DDR_PMUV3_WR_DCQ_THRE, + DDR_PMUV3_WDP_BUFFER_THRE, + DDR_PMUV3_RDAT_INFO_THRE, + DDR_PMUV3_RDAT_FIFO_THRE, + DDR_PMUV3_RSP_COMP_THRE, + DDR_PMUV3_RSP_DBID_THRE, + DDR_PMUV3_RSP_REQ_THRE, + DDR_PMUV3_RSP_CRQ_THRE, + DDR_PMUV3_RSP_RTQ_THRE +}; + +static const u32 phytium_ddr_pmu_v3_thre_bit_size[] = { + DDR_PMUV3_LPR_FIFO_THRE_BIT_SIZE, + DDR_PMUV3_HPR_FIFO_THRE_BIT_SIZE, + DDR_PMUV3_WR_DCQ_THRE_BIT_SIZE, + DDR_PMUV3_WDP_BUFFER_THRE_BIT_SIZE, + DDR_PMUV3_RDAT_INFO_THRE_BIT_SIZE, + DDR_PMUV3_RDAT_FIFO_THRE_BIT_SIZE, + DDR_PMUV3_RSP_COMP_THRE_BIT_SIZE, + DDR_PMUV3_RSP_DBID_THRE_BIT_SIZE, + DDR_PMUV3_RSP_REQ_THRE_BIT_SIZE, + DDR_PMUV3_RSP_CRQ_THRE_BIT_SIZE, + DDR_PMUV3_RSP_RTQ_THRE_BIT_SIZE +}; + +static const u32 phytium_ddr_pmu_v3_genc_cnt_reg_offset[] = { + DDR_PMUV3_MC_DFI_CMD_CNT_L, + DDR_PMUV3_MC_UIF_CNT_L, + DDR_PMUV3_MC_BANK_MAGT_CNT_L, + DDR_PMUV3_MC_CAM_OCCU_CNT_L, + DDR_PMUV3_MC_T_CMD_SCHE_CNT_L, + DDR_PMUV3_MC_CMD_SCHE_CNT_L, + DDR_PMUV3_PORT_CMD_CNT_L, + DDR_PMUV3_PORT_CMD_OPCODE_CNT_L, + DDR_PMUV3_PORT_RETRY_CNT_L, + DDR_PMUV3_PORT_PREF_STATUS_CNT_L, + DDR_PMUV3_MC_RDWR_SWITCH_CNT_L, + DDR_PMUV3_MC_T_RDWR_SWITCH_CNT_L, + DDR_PMUV3_PORT_CQ_OCCU_CNT_L, + DDR_PMUV3_PORT_CQ_OCCU_CNT_L, + DDR_PMUV3_PORT_CQ_OCCU_CNT_L, + DDR_PMUV3_PORT_DATA_OCCU_CNT_L, + DDR_PMUV3_PORT_DATA_OCCU_CNT_L, + DDR_PMUV3_PORT_DATA_OCCU_CNT_L, + DDR_PMUV3_PORT_RSP_OCCU_CNT_L, + DDR_PMUV3_PORT_RSP_OCCU_CNT_L, + DDR_PMUV3_PORT_RSP_OCCU_CNT_L, + DDR_PMUV3_PORT_RSP_OCCU_CNT_L, + DDR_PMUV3_PORT_RSP_OCCU_CNT_L +}; + +static const u32 phytium_ddr_pmu_v3_spec_cnt_reg_offset[] = { + DDR_PMUV3_GLOBAL_CNT_L, + DDR_PMUV3_MC_CQ_IDLE_CNT_L, + DDR_PMUV3_MC_EXP_GPR_CNT_L, + DDR_PMUV3_MC_EXP_GPW_CNT_L, + DDR_PMUV3_MC_ADDR_COLLISION_CNT_L, + DDR_PMUV3_MC_RDCAM_CRITICAL_CNT_L, + DDR_PMUV3_MC_WRCAM_CRITICAL_CNT_L, + DDR_PMUV3_MC_RETRY_CMD_CNT_L, + DDR_PMUV3_PORT_WDAT_BE_CNT_L, + DDR_PMUV3_PORT_WDAT_RM_BUFFER_DEALLOC_CNT_L, + DDR_PMUV3_PORT_RD_CMD_DELAY1, + DDR_PMUV3_PORT_WR_CMD_DELAY1 +}; + +static const u32 phytium_ddr_pmu_v3_genc_cnt_overflow_bit[] = { + 0, // DDR_PMUV3_MC_DFI_CMD_CNT_L, + 1, // DDR_PMUV3_MC_UIF_CNT_L, + 2, // DDR_PMUV3_MC_BANK_MAGT_CNT_L, + -1, // DDR_PMUV3_MC_CAM_OCCU_CNT_L, + -1, // DDR_PMUV3_MC_T_CMD_SCHE_CNT_L, + -1, // DDR_PMUV3_MC_CMD_SCHE_CNT_L, + 7, // DDR_PMUV3_PORT_CMD_CNT_L, + 8, // DDR_PMUV3_PORT_CMD_OPCODE_CNT_L, + 9, // DDR_PMUV3_PORT_RETRY_CNT_L, + 10, // DDR_PMUV3_PORT_PREF_STATUS_CNT_L, + -1, // DDR_PMUV3_MC_RDWR_SWITCH_CNT_L + -1, // DDR_PMUV3_MC_T_RDWR_SWITCH_CNT_L + -1, // DDR_PMUV3_PORT_CQ_OCCU_CNT_L, + -1, // DDR_PMUV3_PORT_CQ_OCCU_CNT_L, + -1, // DDR_PMUV3_PORT_CQ_OCCU_CNT_L, + -1, // DDR_PMUV3_PORT_DATA_OCCU_CNT_L, + -1, // DDR_PMUV3_PORT_DATA_OCCU_CNT_L, + -1, // DDR_PMUV3_PORT_DATA_OCCU_CNT_L, + -1, // DDR_PMUV3_PORT_RSP_OCCU_CNT_L, + -1, // DDR_PMUV3_PORT_RSP_OCCU_CNT_L, + -1, // DDR_PMUV3_PORT_RSP_OCCU_CNT_L, + -1, // DDR_PMUV3_PORT_RSP_OCCU_CNT_L, + -1 // DDR_PMUV3_PORT_RSP_OCCU_CNT_L +}; + +static const int phytium_ddr_pmu_v3_spec_cnt_overflow_bit[] = { + -1, + 3, // DDR_PMUV3_MC_CQ_IDLE_CNT_L, + 4, // DDR_PMUV3_MC_EXP_GPR_CNT_L, + 5, // DDR_PMUV3_MC_EXP_GPW_CNT_L, + 6, // DDR_PMUV3_MC_ADDR_COLLISION_CNT_L, + -1, // DDR_PMUV3_MC_RDCAM_CRITICAL_CNT_L, + -1, // DDR_PMUV3_MC_WRCAM_CRITICAL_CNT_L, + -1, // DDR_PMUV3_MC_RETRY_CMD_CNT_L, + 11, // DDR_PMUV3_PORT_WDAT_BE_CNT_L, + 12, // DDR_PMUV3_PORT_WDAT_RM_BUFFER_DEALLOC_CNT_L, + -1, // DDR_PMUV3_PORT_RD_CMD_DELAY1, + -1 // DDR_PMUV3_PORT_WR_CMD_DELAY1 +}; + +#define DDR_PMUV3_EVENT_ATTR(_name, _type, _eventid, _cntsize, _occuflag) \ + (&((struct phytium_ddr_pmu_v3_event_attr[]){{ \ + .attr = __ATTR(_name, 0444, phytium_ddr_pmu_v3_event_show, NULL), \ + .type = _type, \ + .eventid = _eventid, \ + .cntsize = _cntsize, \ + .occuflag = _occuflag, \ + }})[0] \ + .attr.attr) + +#define DDR_PMUV3_SPECIAL_EVENT(_name, _event, _cntsize) \ + DDR_PMUV3_EVENT_ATTR(_name, DDR_PMUV3_SPECIAL_EVTYPE, _event, _cntsize, 0) +#define DDR_PMUV3_MC_DFI_CMD_EVENT(_name, _event) \ + DDR_PMUV3_EVENT_ATTR(mc_dfi_##_name, DDR_PMUV3_MC_DFI_CMD_EVTYPE, _event, 32, 0) +#define DDR_PMUV3_MC_UIF_CMD_EVENT(_name, _event) \ + DDR_PMUV3_EVENT_ATTR(mc_uif_##_name, DDR_PMUV3_MC_UIF_CMD_EVTYPE, _event, 32, 0) +#define DDR_PMUV3_MC_BANK_SRC_EVENT(_name, _event) \ + DDR_PMUV3_EVENT_ATTR(mc_bank_mgt_##_name, DDR_PMUV3_MC_BANK_SRC_EVTYPE, _event, 32, 0) +#define DDR_PMUV3_MC_CAM_OCCU_EVENT(_name, _event) \ + DDR_PMUV3_EVENT_ATTR(mc_occu_##_name, DDR_PMUV3_MC_CAM_OCCU_EVTYPE, _event, 64, 1) +#define DDR_PMUV3_MC_T_CMD_SCHE_EVENT(_name, _event) \ + DDR_PMUV3_EVENT_ATTR(mc_t_cmd_sche_##_name, DDR_PMUV3_MC_T_CMD_SCHE_EVTYPE, _event, 64, 0) +#define DDR_PMUV3_MC_CMD_SCHE_EVENT(_name, _event) \ + DDR_PMUV3_EVENT_ATTR(mc_cmd_sche_##_name, DDR_PMUV3_MC_CMD_SCHE_EVTYPE, _event, 64, 0) +#define DDR_PMUV3_PORT_CMD_EVENT(_name, _event) \ + DDR_PMUV3_EVENT_ATTR(port_##_name, DDR_PMUV3_PORT_CMD_EVTYPE, _event, 32, 0) +#define DDR_PMUV3_PORT_CMD_OPCODE_EVENT(_name, _event) \ + DDR_PMUV3_EVENT_ATTR(port_opcode_##_name, DDR_PMUV3_PORT_CMD_OPCODE_EVTYPE, _event, 32, 0) +#define DDR_PMUV3_PORT_CMD_RETRY_EVENT(_name, _event) \ + DDR_PMUV3_EVENT_ATTR(port_retry_##_name, DDR_PMUV3_PORT_CMD_RETRY_EVTYPE, _event, 32, 0) +#define DDR_PMUV3_PORT_PREF_STATUS_EVENT(_name, _event) \ + DDR_PMUV3_EVENT_ATTR(port_pref_##_name, DDR_PMUV3_PORT_PREF_STATUS_EVTYPE, _event, 32, 0) + +#define DDR_PMUV3_LPR_FIFO_OCCU_EVENT(_name, _event) \ + DDR_PMUV3_EVENT_ATTR(port_cq_occu_lpr_##_name, DDR_PMUV3_LPR_FIFO_OCCU_EVTYPE, _event, \ + 64, 1) +#define DDR_PMUV3_HPR_FIFO_OCCU_EVENT(_name, _event) \ + DDR_PMUV3_EVENT_ATTR(port_cq_occu_hpr_##_name, DDR_PMUV3_HPR_FIFO_OCCU_EVTYPE, _event, \ + 64, 1) +#define DDR_PMUV3_WR_DCQ_OCCU_EVENT(_name, _event) \ + DDR_PMUV3_EVENT_ATTR(port_cq_occu_dwr_cq_##_name, DDR_PMUV3_WR_DCQ_OCCU_EVTYPE, _event, \ + 64, 1) + +#define DDR_PMUV3_RDAT_INFO_OCCU_EVENT(_name, _event) \ + DDR_PMUV3_EVENT_ATTR(port_data_occu_rdatinfo_##_name, DDR_PMUV3_RDAT_INFO_OCCU_EVTYPE, \ + _event, 64, 1) +#define DDR_PMUV3_RDAT_FIFO_OCCU_EVENT(_name, _event) \ + DDR_PMUV3_EVENT_ATTR(port_data_occu_rdatfifo_##_name, DDR_PMUV3_RDAT_FIFO_OCCU_EVTYPE, \ + _event, 64, 1) +#define DDR_PMUV3_WDP_BUFFER_OCCU_EVENT(_name, _event) \ + DDR_PMUV3_EVENT_ATTR(port_data_occu_wdp_buffer_##_name, DDR_PMUV3_WDP_BUFFER_OCCU_EVTYPE,\ + _event, 64, 1) + +#define DDR_PMUV3_RSP_COMP_OCCU_EVENT(_name, _event) \ + DDR_PMUV3_EVENT_ATTR(port_rsp_occu_rspcomp_##_name, DDR_PMUV3_RSP_COMP_OCCU_EVTYPE, \ + _event, 64, 1) +#define DDR_PMUV3_RSP_DBID_OCCU_EVENT(_name, _event) \ + DDR_PMUV3_EVENT_ATTR(port_rsp_occu_rspdbid_##_name, DDR_PMUV3_RSP_DBID_OCCU_EVTYPE, \ + _event, 64, 1) +#define DDR_PMUV3_RSP_CRQ_OCCU_EVENT(_name, _event) \ + DDR_PMUV3_EVENT_ATTR(port_rsp_occu_rspcrq_##_name, DDR_PMUV3_RSP_CRQ_OCCU_EVTYPE, _event, \ + 64, 1) +#define DDR_PMUV3_RSP_REQ_OCCU_EVENT(_name, _event) \ + DDR_PMUV3_EVENT_ATTR(port_rsp_occu_rspreq_##_name, DDR_PMUV3_RSP_REQ_OCCU_EVTYPE, _event, \ + 64, 1) +#define DDR_PMUV3_RSP_RTQ_OCCU_EVENT(_name, _event) \ + DDR_PMUV3_EVENT_ATTR(port_rsp_occu_rsprtq_##_name, DDR_PMUV3_RSP_RTQ_OCCU_EVTYPE, _event, \ + 64, 1) +#define DDR_PMUV3_MC_RDWR_SWTICH_EVENT(_name, _event) \ + DDR_PMUV3_EVENT_ATTR(mc_switch_##_name, DDR_PMUV3_MC_RDWR_SWTICH_EVTYPE, _event, 64, 0) +#define DDR_PMUV3_MC_T_RDWR_SWTICH_EVENT(_name, _event) \ + DDR_PMUV3_EVENT_ATTR(mc_switch_t_##_name, DDR_PMUV3_MC_T_RDWR_SWTICH_EVTYPE, _event, 64, 0) + +#define PHYTIUM_DDR_PMUV3_EVENT_ATTR_EXTRACTOR(_name, _config, _start, _end) \ +static inline u32 get_##_name(struct perf_event *event) \ +{ \ + return FIELD_GET(GENMASK_ULL(_end, _start), \ + event->attr._config); \ +} + +PHYTIUM_DDR_PMUV3_EVENT_ATTR_EXTRACTOR(eventid, config, 0, 4); +PHYTIUM_DDR_PMUV3_EVENT_ATTR_EXTRACTOR(type, config, 5, 9); +PHYTIUM_DDR_PMUV3_EVENT_ATTR_EXTRACTOR(cntsize, config, 10, 16); +PHYTIUM_DDR_PMUV3_EVENT_ATTR_EXTRACTOR(occuflag, config, 17, 17); +PHYTIUM_DDR_PMUV3_EVENT_ATTR_EXTRACTOR(low_thre, config, 18, 24); +PHYTIUM_DDR_PMUV3_EVENT_ATTR_EXTRACTOR(hight_thre, config, 25, 31); + + +static struct attribute *phytium_ddr_pmu_v3_format_attr[] = { + PHYTIUM_DDR_PMU_FORMAT_ATTR(eventid, "config:0-4"), + PHYTIUM_DDR_PMU_FORMAT_ATTR(type, "config:5-9"), + PHYTIUM_DDR_PMU_FORMAT_ATTR(cntsize, "config:10-16"), + PHYTIUM_DDR_PMU_FORMAT_ATTR(occuflag, "config:17"), + PHYTIUM_DDR_PMU_FORMAT_ATTR(low_thre, "config:18-24"), + PHYTIUM_DDR_PMU_FORMAT_ATTR(hight_thre, "config:25-31"), + NULL, +}; + +static struct attribute *phytium_ddr_pmu_v3_events_attr[] = { + DDR_PMUV3_MC_DFI_CMD_EVENT(act_cnt, 0x0), DDR_PMUV3_MC_DFI_CMD_EVENT(pdx_cnt, 0x1), + DDR_PMUV3_MC_DFI_CMD_EVENT(pde_cnt, 0x2), DDR_PMUV3_MC_DFI_CMD_EVENT(mrs_cnt, 0x3), + DDR_PMUV3_MC_DFI_CMD_EVENT(rd_cnt, 0x4), DDR_PMUV3_MC_DFI_CMD_EVENT(rda_cnt, 0x5), + DDR_PMUV3_MC_DFI_CMD_EVENT(wr_cnt, 0x6), DDR_PMUV3_MC_DFI_CMD_EVENT(wra_cnt, 0x7), + DDR_PMUV3_MC_DFI_CMD_EVENT(pre_cnt, 0x8), DDR_PMUV3_MC_DFI_CMD_EVENT(refpb_cnt, 0x9), + DDR_PMUV3_MC_DFI_CMD_EVENT(refab_cnt, 0xa), DDR_PMUV3_MC_DFI_CMD_EVENT(rfmab_cnt, 0xb), + DDR_PMUV3_MC_DFI_CMD_EVENT(rfmpb_cnt, 0xc), DDR_PMUV3_MC_DFI_CMD_EVENT(ctrlupd_cnt, 0xd), + DDR_PMUV3_MC_DFI_CMD_EVENT(phyupd_cnt, 0xe), + + DDR_PMUV3_MC_UIF_CMD_EVENT(hpr_cnt, 0x0), DDR_PMUV3_MC_UIF_CMD_EVENT(lpr_cnt, 0x1), + DDR_PMUV3_MC_UIF_CMD_EVENT(gpr_cnt, 0x2), DDR_PMUV3_MC_UIF_CMD_EVENT(tpw_cnt, 0x3), + DDR_PMUV3_MC_UIF_CMD_EVENT(gpw_cnt, 0x4), + + DDR_PMUV3_MC_BANK_SRC_EVENT(new_cnt, 0x0), DDR_PMUV3_MC_BANK_SRC_EVENT(hit_cnt, 0x1), + DDR_PMUV3_MC_BANK_SRC_EVENT(reallocate_cnt, 0x2), + + DDR_PMUV3_MC_CAM_OCCU_EVENT(lgpr_thre16_cnt, 0x0), + DDR_PMUV3_MC_CAM_OCCU_EVENT(lgpr_thre24_cnt, 0x1), + DDR_PMUV3_MC_CAM_OCCU_EVENT(lgpr_thre32_cnt, 0x2), + DDR_PMUV3_MC_CAM_OCCU_EVENT(lgpr_thre40_cnt, 0x3), + DDR_PMUV3_MC_CAM_OCCU_EVENT(lgpr_thre48_cnt, 0x4), + DDR_PMUV3_MC_CAM_OCCU_EVENT(lgpr_thre64_cnt, 0x5), + DDR_PMUV3_MC_CAM_OCCU_EVENT(hpr_thre16_cnt, 0x6), + DDR_PMUV3_MC_CAM_OCCU_EVENT(hpr_thre24_cnt, 0x7), + DDR_PMUV3_MC_CAM_OCCU_EVENT(hpr_thre32_cnt, 0x8), + DDR_PMUV3_MC_CAM_OCCU_EVENT(hpr_thre40_cnt, 0x9), + DDR_PMUV3_MC_CAM_OCCU_EVENT(hpr_thre48_cnt, 0xa), + DDR_PMUV3_MC_CAM_OCCU_EVENT(hpr_thre64_cnt, 0xb), + DDR_PMUV3_MC_CAM_OCCU_EVENT(tpw_thre16_cnt, 0xc), + DDR_PMUV3_MC_CAM_OCCU_EVENT(tpw_thre24_cnt, 0xd), + DDR_PMUV3_MC_CAM_OCCU_EVENT(tpw_thre32_cnt, 0xe), + DDR_PMUV3_MC_CAM_OCCU_EVENT(tpw_thre40_cnt, 0xf), + DDR_PMUV3_MC_CAM_OCCU_EVENT(tpw_thre48_cnt, 0x10), + DDR_PMUV3_MC_CAM_OCCU_EVENT(tpw_thre64_cnt, 0x11), + + DDR_PMUV3_MC_T_CMD_SCHE_EVENT(rd2rd_diff_rank_cnt, 0x0), + DDR_PMUV3_MC_T_CMD_SCHE_EVENT(rd2rd_diff_bg_cnt, 0x1), + DDR_PMUV3_MC_T_CMD_SCHE_EVENT(rd2rd_diff_page_cnt, 0x2), + DDR_PMUV3_MC_T_CMD_SCHE_EVENT(rd2rd_same_page_cnt, 0x3), + DDR_PMUV3_MC_T_CMD_SCHE_EVENT(rd2wr_diff_rank_cnt, 0x4), + DDR_PMUV3_MC_T_CMD_SCHE_EVENT(rd2wr_diff_bg_cnt, 0x5), + DDR_PMUV3_MC_T_CMD_SCHE_EVENT(rd2wr_diff_page_cnt, 0x6), + DDR_PMUV3_MC_T_CMD_SCHE_EVENT(rd2wr_same_page_cnt, 0x7), + DDR_PMUV3_MC_T_CMD_SCHE_EVENT(wr2wr_diff_rank_cnt, 0x8), + DDR_PMUV3_MC_T_CMD_SCHE_EVENT(wr2wr_diff_bg_cnt, 0x9), + DDR_PMUV3_MC_T_CMD_SCHE_EVENT(wr2wr_diff_page_cnt, 0xa), + DDR_PMUV3_MC_T_CMD_SCHE_EVENT(wr2wr_same_page_cnt, 0xb), + DDR_PMUV3_MC_T_CMD_SCHE_EVENT(wr2rd_diff_rank_cnt, 0xc), + DDR_PMUV3_MC_T_CMD_SCHE_EVENT(wr2rd_diff_bg_cnt, 0xd), + DDR_PMUV3_MC_T_CMD_SCHE_EVENT(wr2rd_diff_page_cnt, 0xe), + DDR_PMUV3_MC_T_CMD_SCHE_EVENT(wr2rd_same_page_cnt, 0xf), + + DDR_PMUV3_MC_CMD_SCHE_EVENT(rd2rd_diff_rank_cnt, 0x0), + DDR_PMUV3_MC_CMD_SCHE_EVENT(rd2rd_diff_bg_cnt, 0x1), + DDR_PMUV3_MC_CMD_SCHE_EVENT(rd2rd_diff_page_cnt, 0x2), + DDR_PMUV3_MC_CMD_SCHE_EVENT(rd2rd_same_page_cnt, 0x3), + DDR_PMUV3_MC_CMD_SCHE_EVENT(rd2wr_diff_rank_cnt, 0x4), + DDR_PMUV3_MC_CMD_SCHE_EVENT(rd2wr_diff_bg_cnt, 0x5), + DDR_PMUV3_MC_CMD_SCHE_EVENT(rd2wr_diff_page_cnt, 0x6), + DDR_PMUV3_MC_CMD_SCHE_EVENT(rd2wr_same_page_cnt, 0x7), + DDR_PMUV3_MC_CMD_SCHE_EVENT(wr2wr_diff_rank_cnt, 0x8), + DDR_PMUV3_MC_CMD_SCHE_EVENT(wr2wr_diff_bg_cnt, 0x9), + DDR_PMUV3_MC_CMD_SCHE_EVENT(wr2wr_diff_page_cnt, 0xa), + DDR_PMUV3_MC_CMD_SCHE_EVENT(wr2wr_same_page_cnt, 0xb), + DDR_PMUV3_MC_CMD_SCHE_EVENT(wr2rd_diff_rank_cnt, 0xc), + DDR_PMUV3_MC_CMD_SCHE_EVENT(wr2rd_diff_bg_cnt, 0xd), + DDR_PMUV3_MC_CMD_SCHE_EVENT(wr2rd_diff_page_cnt, 0xe), + DDR_PMUV3_MC_CMD_SCHE_EVENT(wr2rd_same_page_cnt, 0xf), + + DDR_PMUV3_PORT_CMD_EVENT(hpr_cnt, 0x0), DDR_PMUV3_PORT_CMD_EVENT(lpr_cnt, 0x1), + DDR_PMUV3_PORT_CMD_EVENT(gpr_cnt, 0x2), DDR_PMUV3_PORT_CMD_EVENT(tpw_cnt, 0x3), + DDR_PMUV3_PORT_CMD_EVENT(gpw_cnt, 0x4), DDR_PMUV3_PORT_CMD_EVENT(gpr_expired_cnt, 0x5), + DDR_PMUV3_PORT_CMD_EVENT(gpw_expired_cnt, 0x6), + + DDR_PMUV3_PORT_CMD_OPCODE_EVENT(pcrd_return_cnt, 0x0), + DDR_PMUV3_PORT_CMD_OPCODE_EVENT(read_no_snp_cnt, 0x1), + DDR_PMUV3_PORT_CMD_OPCODE_EVENT(write_no_snpfull_cnt, 0x2), + DDR_PMUV3_PORT_CMD_OPCODE_EVENT(write_no_snp_ptl_cnt, 0x3), + DDR_PMUV3_PORT_CMD_OPCODE_EVENT(prefetch_tgt_cnt, 0x4), + DDR_PMUV3_PORT_CMD_OPCODE_EVENT(cleansharepersist_cnt, 0x5), + + DDR_PMUV3_PORT_CMD_RETRY_EVENT(hpr_cnt, 0x0), DDR_PMUV3_PORT_CMD_RETRY_EVENT(lpr_cnt, 0x1), + DDR_PMUV3_PORT_CMD_RETRY_EVENT(gpr_cnt, 0x2), DDR_PMUV3_PORT_CMD_RETRY_EVENT(tpw_cnt, 0x3), + DDR_PMUV3_PORT_CMD_RETRY_EVENT(gpw_cnt, 0x4), + + DDR_PMUV3_PORT_PREF_STATUS_EVENT(hit_cnt, 0x0), + DDR_PMUV3_PORT_PREF_STATUS_EVENT(invalid_cnt, 0x1), + DDR_PMUV3_PORT_PREF_STATUS_EVENT(replace_cnt, 0x2), + DDR_PMUV3_PORT_PREF_STATUS_EVENT(discard_bcs_resource_cnt, 0x3), + DDR_PMUV3_PORT_PREF_STATUS_EVENT(discard_bcs_addr_coll_cnt, 0x4), + + DDR_PMUV3_MC_RDWR_SWTICH_EVENT(rdw_1cmd_cnt, 0x0), + DDR_PMUV3_MC_RDWR_SWTICH_EVENT(rdw_2cmd_cnt, 0x1), + DDR_PMUV3_MC_RDWR_SWTICH_EVENT(rdw_3to4cmd_cnt, 0x2), + DDR_PMUV3_MC_RDWR_SWTICH_EVENT(rdw_5to8cmd_cnt, 0x3), + DDR_PMUV3_MC_RDWR_SWTICH_EVENT(rdw_9to12cmd_cnt, 0x4), + DDR_PMUV3_MC_RDWR_SWTICH_EVENT(rdw_13cmd_cnt, 0x5), + DDR_PMUV3_MC_RDWR_SWTICH_EVENT(wdr_1cmd_cnt, 0x6), + DDR_PMUV3_MC_RDWR_SWTICH_EVENT(wdr_2cmd_cnt, 0x7), + DDR_PMUV3_MC_RDWR_SWTICH_EVENT(wdr_3to4cmd_cnt, 0x8), + DDR_PMUV3_MC_RDWR_SWTICH_EVENT(wdr_5to8cmd_cnt, 0x9), + DDR_PMUV3_MC_RDWR_SWTICH_EVENT(wdr_9to12cmd_cnt, 0xa), + DDR_PMUV3_MC_RDWR_SWTICH_EVENT(wdr_ge13cmd_cnt, 0xb), + + DDR_PMUV3_MC_T_RDWR_SWTICH_EVENT(rdw_1cmd_cnt, 0x0), + DDR_PMUV3_MC_T_RDWR_SWTICH_EVENT(rdw_2cmd_cnt, 0x1), + DDR_PMUV3_MC_T_RDWR_SWTICH_EVENT(rdw_3to4cmd_cnt, 0x2), + DDR_PMUV3_MC_T_RDWR_SWTICH_EVENT(rdw_5to8cmd_cnt, 0x3), + DDR_PMUV3_MC_T_RDWR_SWTICH_EVENT(rdw_9to12cmd_cnt, 0x4), + DDR_PMUV3_MC_T_RDWR_SWTICH_EVENT(rdw_13cmd_cnt, 0x5), + DDR_PMUV3_MC_T_RDWR_SWTICH_EVENT(wdr_1cmd_cnt, 0x6), + DDR_PMUV3_MC_T_RDWR_SWTICH_EVENT(wdr_2cmd_cnt, 0x7), + DDR_PMUV3_MC_T_RDWR_SWTICH_EVENT(wdr_3to4cmd_cnt, 0x8), + DDR_PMUV3_MC_T_RDWR_SWTICH_EVENT(wdr_5to8cmd_cnt, 0x9), + DDR_PMUV3_MC_T_RDWR_SWTICH_EVENT(wdr_9to12cmd_cnt, 0xa), + DDR_PMUV3_MC_T_RDWR_SWTICH_EVENT(wdr_ge13cmd_cnt, 0xb), + + DDR_PMUV3_LPR_FIFO_OCCU_EVENT(low_thre, 0x0), DDR_PMUV3_LPR_FIFO_OCCU_EVENT(med_thre, 0x1), + DDR_PMUV3_LPR_FIFO_OCCU_EVENT(high_thre, 0x2), + DDR_PMUV3_HPR_FIFO_OCCU_EVENT(low_thre, 0x3), DDR_PMUV3_HPR_FIFO_OCCU_EVENT(med_thre, 0x4), + DDR_PMUV3_HPR_FIFO_OCCU_EVENT(high_thre, 0x5), + DDR_PMUV3_WR_DCQ_OCCU_EVENT(low_thre, 0x6), DDR_PMUV3_WR_DCQ_OCCU_EVENT(med_thre, 0x7), + DDR_PMUV3_WR_DCQ_OCCU_EVENT(high_thre, 0x8), + + DDR_PMUV3_RDAT_INFO_OCCU_EVENT(low_thre, 0x0), + DDR_PMUV3_RDAT_INFO_OCCU_EVENT(med_thre, 0x1), + DDR_PMUV3_RDAT_INFO_OCCU_EVENT(high_thre, 0x2), + DDR_PMUV3_RDAT_FIFO_OCCU_EVENT(low_thre, 0x3), + DDR_PMUV3_RDAT_FIFO_OCCU_EVENT(med_thre, 0x4), + DDR_PMUV3_RDAT_FIFO_OCCU_EVENT(high_thre, 0x5), + DDR_PMUV3_WDP_BUFFER_OCCU_EVENT(low_thre, 0x6), + DDR_PMUV3_WDP_BUFFER_OCCU_EVENT(med_thre, 0x7), + DDR_PMUV3_WDP_BUFFER_OCCU_EVENT(high_thre, 0x8), + + DDR_PMUV3_RSP_COMP_OCCU_EVENT(low_thre, 0x0), DDR_PMUV3_RSP_COMP_OCCU_EVENT(med_thre, 0x1), + DDR_PMUV3_RSP_COMP_OCCU_EVENT(high_thre, 0x2), + DDR_PMUV3_RSP_DBID_OCCU_EVENT(low_thre, 0x3), DDR_PMUV3_RSP_DBID_OCCU_EVENT(med_thre, 0x4), + DDR_PMUV3_RSP_DBID_OCCU_EVENT(high_thre, 0x5), + DDR_PMUV3_RSP_CRQ_OCCU_EVENT(low_thre, 0x6), DDR_PMUV3_RSP_CRQ_OCCU_EVENT(med_thre, 0x7), + DDR_PMUV3_RSP_CRQ_OCCU_EVENT(high_thre, 0x8), + DDR_PMUV3_RSP_REQ_OCCU_EVENT(low_thre, 0x9), DDR_PMUV3_RSP_REQ_OCCU_EVENT(med_thre, 0xa), + DDR_PMUV3_RSP_REQ_OCCU_EVENT(high_thre, 0xb), + DDR_PMUV3_RSP_RTQ_OCCU_EVENT(low_thre, 0xc), DDR_PMUV3_RSP_RTQ_OCCU_EVENT(med_thre, 0xd), + DDR_PMUV3_RSP_RTQ_OCCU_EVENT(high_thre, 0xe), + + DDR_PMUV3_SPECIAL_EVENT(dmu_cycles, 0x0, 64), DDR_PMUV3_SPECIAL_EVENT(mc_cq_idle, 0x1, 32), + DDR_PMUV3_SPECIAL_EVENT(mc_exp_gpr, 0x2, 32), DDR_PMUV3_SPECIAL_EVENT(mc_exp_gpw, 0x3, 32), + DDR_PMUV3_SPECIAL_EVENT(mc_addr_collision, 0x4, 32), + DDR_PMUV3_SPECIAL_EVENT(mc_rdcam_critical, 0x5, 64), + DDR_PMUV3_SPECIAL_EVENT(mc_wrcam_critical, 0x6, 64), + DDR_PMUV3_SPECIAL_EVENT(mc_retry_cmd, 0x7, 32), + DDR_PMUV3_SPECIAL_EVENT(port_wdat_be, 0x8, 32), + DDR_PMUV3_SPECIAL_EVENT(port_wdat_rm_buffer_dealloc, 0x9, 32), + DDR_PMUV3_SPECIAL_EVENT(port_rd_cmd_delay, 0xa, 64), + DDR_PMUV3_SPECIAL_EVENT(port_wr_cmd_delay, 0xb, 64), NULL, }; -static const struct attribute_group phytium_ddr_pmu_events_group = { +static const struct attribute_group phytium_ddr_pmu_v1v2_format_group = { + .name = "format", + .attrs = phytium_ddr_pmu_v1v2_format_attr, +}; + +static const struct attribute_group phytium_ddr_pmu_v3_format_group = { + .name = "format", + .attrs = phytium_ddr_pmu_v3_format_attr, +}; + +static const struct attribute_group phytium_ddr_pmu_v1_events_group = { .name = "events", - .attrs = phytium_ddr_pmu_events_attr, + .attrs = phytium_ddr_pmu_v1_events_attr, +}; + +static const struct attribute_group phytium_ddr_pmu_v2_events_group = { + .name = "events", + .attrs = phytium_ddr_pmu_v2_events_attr, +}; + +static const struct attribute_group phytium_ddr_pmu_v3_events_group = { + .name = "events", + .attrs = phytium_ddr_pmu_v3_events_attr, }; static DEVICE_ATTR_RO(cpumask); @@ -180,23 +958,57 @@ static const struct attribute_group phytium_ddr_pmu_cpumask_attr_group = { .attrs = phytium_ddr_pmu_cpumask_attrs, }; -static const struct attribute_group *phytium_ddr_pmu_attr_groups[] = { - &phytium_ddr_pmu_format_group, - &phytium_ddr_pmu_events_group, +static const struct attribute_group *phytium_ddr_pmu_v1_attr_groups[] = { + &phytium_ddr_pmu_v1v2_format_group, + &phytium_ddr_pmu_v1_events_group, &phytium_ddr_pmu_cpumask_attr_group, NULL, }; -static u64 phytium_ddr_pmu_read_counter(struct phytium_ddr_pmu *ddr_pmu, - struct hw_perf_event *hwc) +static const struct attribute_group *phytium_ddr_pmu_v2_attr_groups[] = { + &phytium_ddr_pmu_v1v2_format_group, + &phytium_ddr_pmu_v2_events_group, + &phytium_ddr_pmu_cpumask_attr_group, + NULL, +}; + +static const struct attribute_group *phytium_ddr_pmu_v3_attr_groups[] = { + &phytium_ddr_pmu_v3_format_group, + &phytium_ddr_pmu_v3_events_group, + &phytium_ddr_pmu_cpumask_attr_group, + NULL, +}; + +#if IS_ENABLED(CONFIG_ARM_PHYTIUM_DMU_DEVFREQ) +extern struct blocking_notifier_head dmu_pmu_notifier_chain; + +void phytium_ddr_pmu_v2_notifier_chain_trigger(struct phytium_ddr_pmu *ddr_pmu, int event) +{ + static bool start_flag; + + if ((event == DDR_PMUV2_NOTICE_START) && (start_flag == false)) { + blocking_notifier_call_chain(&dmu_pmu_notifier_chain, event, NULL); + start_flag = true; + ddr_pmu->used_flag = true; + } else if ((event == DDR_PMUV2_NOTICE_STOP) && (start_flag == true)) { + blocking_notifier_call_chain(&dmu_pmu_notifier_chain, event, NULL); + start_flag = false; + ddr_pmu->used_flag = false; + } +} +#endif + +static u64 phytium_ddr_pmu_v1_read_counter(struct phytium_ddr_pmu *ddr_pmu, + struct perf_event *event) { - u32 idx = GET_DDR_EVENTID(hwc); + struct hw_perf_event *hwc = &event->hw; + u32 idx = GET_DDR_PMUV1V2_EVENTID(hwc); u32 cycle_l, cycle_h, w_data, ddr_data_width; + u32 counter_offset = ddr_pmu_v1_counter_reg_offset[idx]; u64 val64 = 0; int i; - u32 counter_offset = ddr_counter_reg_offset[idx]; - if (!EVENT_VALID(idx)) { + if (!EVENT_VALID_V1(idx)) { dev_err(ddr_pmu->dev, "Unsupported event index:%d!\n", idx); return 0; } @@ -208,7 +1020,7 @@ static u64 phytium_ddr_pmu_read_counter(struct phytium_ddr_pmu *ddr_pmu, val64 = (u64)cycle_h << 32 | (u64)cycle_l; break; case 7: - ddr_data_width = readl(ddr_pmu->base + DDR_DATA_WIDTH); + ddr_data_width = readl(ddr_pmu->base + DDR_PMUV1_DATA_WIDTH); for (i = 0; i < (ddr_data_width / 8); i++) { w_data = readl(ddr_pmu->base + counter_offset + 4 * i); val64 += w_data; @@ -222,11 +1034,99 @@ static u64 phytium_ddr_pmu_read_counter(struct phytium_ddr_pmu *ddr_pmu, return val64; } -static void phytium_ddr_pmu_enable_clk(struct phytium_ddr_pmu *ddr_pmu) +static u64 phytium_ddr_pmu_v2_read_counter(struct phytium_ddr_pmu *ddr_pmu, + struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; + u32 val32_l, val32_h, idx, counter_offset; + u64 val64; + + idx = GET_DDR_PMUV1V2_EVENTID(hwc); + counter_offset = ddr_pmu_v2_counter_reg_offset[idx]; + + if (!EVENT_VALID_V2(idx)) { + dev_err(ddr_pmu->dev, "Unsupported event index:%d!\n", idx); + return 0; + } + + val32_l = readl(ddr_pmu->base + counter_offset); + val32_h = readl(ddr_pmu->base + counter_offset + 4); + val64 = (u64)val32_h << 32 | (u64)val32_l; + + return val64; +} + +static u64 phytium_ddr_pmu_v3_read_counter(struct phytium_ddr_pmu *ddr_pmu, + struct perf_event *event) +{ + u32 evid, evtype, cntsize; + u32 cnt_offset, sel_offset, sel_mask; + u32 val32_l, val32_h; + u32 overflow_flag; + u64 val64; + int overflow_flag_bit; + unsigned long overflow_mask; + + evid = get_eventid(event); + evtype = get_type(event); + cntsize = get_cntsize(event); + val64 = 0; + switch (evtype) { + case DDR_PMUV3_SPECIAL_EVTYPE: + cnt_offset = phytium_ddr_pmu_v3_spec_cnt_reg_offset[evid]; + overflow_flag_bit = phytium_ddr_pmu_v3_spec_cnt_overflow_bit[evid]; + + overflow_mask = readl(ddr_pmu->base + DDR_PMUV3_CNT_OVERFLOW_FLAG); + if (overflow_flag_bit > 0) + overflow_flag = (u32)(overflow_mask & (1 << overflow_flag_bit)); + break; + default: + cnt_offset = phytium_ddr_pmu_v3_genc_cnt_reg_offset[evtype]; + sel_offset = phytium_ddr_pmu_v3_event_sel_reg_offset[evtype]; + sel_mask = phytium_ddr_pmu_v3_cnt_sel_mask_offset[evtype]; + overflow_flag_bit = phytium_ddr_pmu_v3_genc_cnt_overflow_bit[evtype]; + + if (evtype == DDR_PMUV3_MC_BANK_SRC_EVTYPE) + sel_mask &= (evid << 5); + else if (evtype == DDR_PMUV3_MC_T_CMD_SCHE_EVTYPE) + sel_mask &= (evid << 4); + else if (evtype == DDR_PMUV3_MC_T_RDWR_SWTICH_EVTYPE) + sel_mask &= (evid << 4); + else + sel_mask &= evid; + + writel(sel_mask, ddr_pmu->base + sel_offset); + + overflow_mask = readl(ddr_pmu->base + DDR_PMUV3_CNT_OVERFLOW_FLAG); + if (overflow_flag_bit > 0) + overflow_flag = (u32)(overflow_mask & (1 << overflow_flag_bit)); + + break; + } + + if (cntsize == 64) { + val32_l = readl(ddr_pmu->base + cnt_offset); + val32_h = readl(ddr_pmu->base + cnt_offset + 4); + val64 = (u64)val32_h << 32 | (u64)val32_l; + } else { + val32_l = readl(ddr_pmu->base + cnt_offset); + val64 = (u64)val32_l; + } + + if (overflow_flag == 1) { + dev_warn(ddr_pmu->dev, + "The Event(type=%u,eventid=%u) counter value has overflowed.\n", + evtype, evid); + return 0; + } + return val64; +} + +static void phytium_ddr_pmu_v1_enable_clk(struct phytium_ddr_pmu *ddr_pmu) { u32 val; - if (ddr_pmu->ver == DDRV1P5) + if (ddr_pmu->ver == DDR_PMUV1P5) return; val = readl(ddr_pmu->cfg_base); @@ -234,11 +1134,16 @@ static void phytium_ddr_pmu_enable_clk(struct phytium_ddr_pmu *ddr_pmu) writel(val, ddr_pmu->cfg_base); } -static void phytium_ddr_pmu_disable_clk(struct phytium_ddr_pmu *ddr_pmu) +static void phytium_ddr_pmu_v3_enable_clk(struct phytium_ddr_pmu *ddr_pmu) +{ + writel(0x0, ddr_pmu->base + DDR_PMUV3_CLK_EN); +} + +static void phytium_ddr_pmu_v1_disable_clk(struct phytium_ddr_pmu *ddr_pmu) { u32 val; - if (ddr_pmu->ver == DDRV1P5) + if (ddr_pmu->ver == DDR_PMUV1P5) return; val = readl(ddr_pmu->cfg_base); @@ -246,32 +1151,80 @@ static void phytium_ddr_pmu_disable_clk(struct phytium_ddr_pmu *ddr_pmu) writel(val, ddr_pmu->cfg_base); } -static void phytium_ddr_pmu_clear_all_counters(struct phytium_ddr_pmu *ddr_pmu) +static void phytium_ddr_pmu_v3_disable_clk(struct phytium_ddr_pmu *ddr_pmu) +{ + writel(0x1, ddr_pmu->base + DDR_PMUV3_CLK_EN); +} + +static void phytium_ddr_pmu_v3_snapshot_counters(struct phytium_ddr_pmu *ddr_pmu) +{ + writel(0x0, ddr_pmu->base + DDR_PMUV3_SNAPSHOT_PMU); + writel(BIT(0), ddr_pmu->base + DDR_PMUV3_SNAPSHOT_PMU); +} + +static void phytium_ddr_pmu_v1_clear_all_counters(struct phytium_ddr_pmu *ddr_pmu) +{ + writel(0x1, ddr_pmu->base + DDR_PMUV1_CLEAR_EVENT); +} + +static void phytium_ddr_pmu_v2_clear_all_counters(struct phytium_ddr_pmu *ddr_pmu) +{ + writel(0x1, ddr_pmu->base + DDR_PMUV2_CLEAR_EVENT); +} + +static void phytium_ddr_pmu_v3_clear_all_counters(struct phytium_ddr_pmu *ddr_pmu) +{ + writel(0x1, ddr_pmu->base + DDR_PMUV3_CLEAR_TIMER); + writel(0x0, ddr_pmu->base + DDR_PMUV3_CLEAR_TIMER); +} + +static void phytium_ddr_pmu_v1_start_all_counters(struct phytium_ddr_pmu *ddr_pmu) +{ + writel(0x1, ddr_pmu->base + DDR_PMUV1_START_TIMER); +} + +static void phytium_ddr_pmu_v2_start_all_counters(struct phytium_ddr_pmu *ddr_pmu) +{ + writel(BIT(0), ddr_pmu->base + DDR_PMUV2_TIMER_START); +} + +static void phytium_ddr_pmu_v3_start_all_counters(struct phytium_ddr_pmu *ddr_pmu) +{ + writel(BIT(0), ddr_pmu->base + DDR_PMUV3_START_TIMER); +} + +static void phytium_ddr_pmu_v1_stop_all_counters(struct phytium_ddr_pmu *ddr_pmu) { - writel(0x1, ddr_pmu->base + DDR_CLEAR_EVENT); + writel(0x1, ddr_pmu->base + DDR_PMUV1_STOP_TIMER); } -static void phytium_ddr_pmu_start_all_counters(struct phytium_ddr_pmu *ddr_pmu) +static void phytium_ddr_pmu_v2_stop_all_counters(struct phytium_ddr_pmu *ddr_pmu) { - writel(0x1, ddr_pmu->base + DDR_START_TIMER); + writel(BIT(0), ddr_pmu->base + DDR_PMUV2_TIMER_STOP); } -static void phytium_ddr_pmu_stop_all_counters(struct phytium_ddr_pmu *ddr_pmu) +static void phytium_ddr_pmu_v3_stop_all_counters(struct phytium_ddr_pmu *ddr_pmu) { - writel(0x1, ddr_pmu->base + DDR_STOP_TIMER); + writel(0x0, ddr_pmu->base + DDR_PMUV3_START_TIMER); +} + +static void phytium_ddr_pmu_v2_reset_timer(struct phytium_ddr_pmu *ddr_pmu) +{ + writel(0xFFFFFFFF, ddr_pmu->base + DDR_PMUV2_SET_TIMER_L); + writel(0xFFFFFFFF, ddr_pmu->base + DDR_PMUV2_SET_TIMER_H); } static unsigned long -phytium_ddr_pmu_get_stop_state(struct phytium_ddr_pmu *ddr_pmu) +phytium_ddr_pmu_v1_get_stop_state(struct phytium_ddr_pmu *ddr_pmu) { unsigned long val; - val = (unsigned long)readl(ddr_pmu->base + DDR_STATE_STOP); + val = (unsigned long)readl(ddr_pmu->base + DDR_PMUV1_STATE_STOP); return val; } static unsigned long -phytium_ddr_pmu_get_irq_flag(struct phytium_ddr_pmu *ddr_pmu) +phytium_ddr_pmu_v1_get_irq_flag(struct phytium_ddr_pmu *ddr_pmu) { unsigned long val; @@ -279,13 +1232,63 @@ phytium_ddr_pmu_get_irq_flag(struct phytium_ddr_pmu *ddr_pmu) return val; } +static void phytium_ddr_pmu_v2_enable_events(struct phytium_ddr_pmu *ddr_pmu, int idx) +{ + u8 en_bit; + u32 en_offset, irq_offset, val; + + if (idx == 0) { + en_bit = 0; + en_offset = 0; + irq_offset = DDR_PMUV2_TIMER_INT_MASK; + } else { + en_bit = (idx - 1) * 8; + en_offset = DDR_PMUV2_AXI_MONITOR_EN; + irq_offset = DDR_PMUV2_AXI_MONITOR_INT_MASK; + } + + if (en_offset) { + val = readl(ddr_pmu->base + en_offset); + val |= BIT(en_bit); + writel(val, ddr_pmu->base + en_offset); + } + + val = readl(ddr_pmu->base + irq_offset); + val &= ~BIT(en_bit); + writel(val, ddr_pmu->base + irq_offset); +} + +static int phytium_ddr_pmu_v3_get_event_idx(struct perf_event *event) +{ + int i, idx; + u32 event_type, event_id; + + event_type = get_type(event); + event_id = get_eventid(event); + idx = 0; + + for (i = 0; i < event_type; i++) + idx += phytium_ddr_pmu_v3_event_num[i]; + + if (event_type < DDR_PMUV3_LPR_FIFO_OCCU_EVTYPE || event_type == DDR_PMUV3_SPECIAL_EVTYPE) + idx += event_id; + else + idx += (event_id % 3); + + return idx; +} + static int phytium_ddr_pmu_mark_event(struct perf_event *event) { struct phytium_ddr_pmu *ddr_pmu = to_phytium_ddr_pmu(event->pmu); unsigned long *used_mask = ddr_pmu->pmu_events.used_mask; struct hw_perf_event *hwc = &event->hw; + int idx; - int idx = GET_DDR_EVENTID(hwc); + if (ddr_pmu->ver == DDR_PMUV3P0) + idx = phytium_ddr_pmu_v3_get_event_idx(event); + else + idx = GET_DDR_PMUV1V2_EVENTID(hwc); if (test_bit(idx, used_mask)) return -EAGAIN; @@ -295,15 +1298,199 @@ static int phytium_ddr_pmu_mark_event(struct perf_event *event) return idx; } -static void phytium_ddr_pmu_unmark_event(struct phytium_ddr_pmu *ddr_pmu, - int idx) +static void phytium_ddr_pmu_unmark_event(struct perf_event *event) { - if (!EVENT_VALID(idx)) { - dev_err(ddr_pmu->dev, "Unsupported event index:%d!\n", idx); - return; + struct phytium_ddr_pmu *ddr_pmu = to_phytium_ddr_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + u32 idx = GET_DDR_PMUV1V2_EVENTID(hwc); + + if (ddr_pmu->ver <= DDR_PMUV1P5) { + if (!EVENT_VALID_V1(idx)) { + dev_err(ddr_pmu->dev, "Unsupported event index:%d!\n", idx); + return; + } + } else if (ddr_pmu->ver == DDR_PMUV2P0) { + if (!EVENT_VALID_V2(idx)) { + dev_err(ddr_pmu->dev, "Unsupported event index:%d!\n", idx); + return; + } + } else if (ddr_pmu->ver == DDR_PMUV3P0) { + if (!EVENT_VALID_V3(phytium_ddr_pmu_v3_get_event_idx(event))) { + dev_err(ddr_pmu->dev, "Unsupported event index:%d!\n", idx); + return; + } + + int idx, i; + + u32 event_type, event_id; + + event_type = get_type(event); + event_id = get_eventid(event); + idx = 0; + + for (i = 0; i < event_type; i++) + idx += phytium_ddr_pmu_v3_event_num[i]; + + if (event_type < DDR_PMUV3_LPR_FIFO_OCCU_EVTYPE || + event_type == DDR_PMUV3_SPECIAL_EVTYPE) + idx += event_id; + else + idx += (event_id % 3); } clear_bit(idx, ddr_pmu->pmu_events.used_mask); + ddr_pmu->pmu_events.hw_events[hwc->idx] = NULL; +} + +static int phytium_ddr_pmu_v3_verify_port_occu_threshold(u32 low_thre, u32 hight_thre, + u32 *in_low_thre, u32 *in_hight_thre) +{ + if (((*in_low_thre) == 0) || ((*in_hight_thre) == 0)) { + *in_low_thre = low_thre; + *in_hight_thre = hight_thre; + } else if (((*in_low_thre) != low_thre) || ((*in_hight_thre) != hight_thre)) { + *in_low_thre = 0; + *in_hight_thre = 0; + return -EINVAL; + } + return 0; +} + +static int phytium_ddr_pmu_v3_get_port_occu_threshold(struct phytium_ddr_pmu *ddr_pmu, + struct perf_event *event) +{ + u32 low_thre, hight_thre, evtype; + u32 thre_reg_idx, thre_max_val; + int ret; + + low_thre = get_low_thre(event); + hight_thre = get_hight_thre(event); + evtype = get_type(event); + + thre_reg_idx = evtype - DDR_PMUV3_LPR_FIFO_OCCU_EVTYPE; + thre_max_val = (1 << phytium_ddr_pmu_v3_thre_bit_size[thre_reg_idx]) - 1; + + if ((hight_thre <= low_thre) || thre_max_val < low_thre) + return -EINVAL; + switch (evtype) { + case DDR_PMUV3_LPR_FIFO_OCCU_EVTYPE: + ret = phytium_ddr_pmu_v3_verify_port_occu_threshold(low_thre, hight_thre, + &ddr_pmu->port_occu_thre.lpr_fifo_low_thre, + &ddr_pmu->port_occu_thre.lpr_fifo_hight_thre); + break; + case DDR_PMUV3_HPR_FIFO_OCCU_EVTYPE: + ret = phytium_ddr_pmu_v3_verify_port_occu_threshold(low_thre, hight_thre, + &ddr_pmu->port_occu_thre.hpr_fifo_low_thre, + &ddr_pmu->port_occu_thre.hpr_fifo_hight_thre); + break; + case DDR_PMUV3_WR_DCQ_OCCU_EVTYPE: + ret = phytium_ddr_pmu_v3_verify_port_occu_threshold(low_thre, hight_thre, + &ddr_pmu->port_occu_thre.wr_dcq_low_thre, + &ddr_pmu->port_occu_thre.wr_dcq_hight_thre); + break; + case DDR_PMUV3_RDAT_INFO_OCCU_EVTYPE: + ret = phytium_ddr_pmu_v3_verify_port_occu_threshold(low_thre, hight_thre, + &ddr_pmu->port_occu_thre.rdat_info_low_thre, + &ddr_pmu->port_occu_thre.rdat_info_hight_thre); + break; + case DDR_PMUV3_RDAT_FIFO_OCCU_EVTYPE: + ret = phytium_ddr_pmu_v3_verify_port_occu_threshold(low_thre, hight_thre, + &ddr_pmu->port_occu_thre.rdat_fifo_low_thre, + &ddr_pmu->port_occu_thre.rdat_fifo_hight_thre); + break; + case DDR_PMUV3_WDP_BUFFER_OCCU_EVTYPE: + ret = phytium_ddr_pmu_v3_verify_port_occu_threshold(low_thre, hight_thre, + &ddr_pmu->port_occu_thre.wdp_buffer_low_thre, + &ddr_pmu->port_occu_thre.wdp_buffer_hight_thre); + break; + case DDR_PMUV3_RSP_COMP_OCCU_EVTYPE: + ret = phytium_ddr_pmu_v3_verify_port_occu_threshold(low_thre, hight_thre, + &ddr_pmu->port_occu_thre.rsp_comp_low_thre, + &ddr_pmu->port_occu_thre.rsp_comp_hight_thre); + break; + case DDR_PMUV3_RSP_DBID_OCCU_EVTYPE: + ret = phytium_ddr_pmu_v3_verify_port_occu_threshold(low_thre, hight_thre, + &ddr_pmu->port_occu_thre.rsp_dbid_low_thre, + &ddr_pmu->port_occu_thre.rsp_dbid_hight_thre); + break; + case DDR_PMUV3_RSP_CRQ_OCCU_EVTYPE: + ret = phytium_ddr_pmu_v3_verify_port_occu_threshold(low_thre, hight_thre, + &ddr_pmu->port_occu_thre.rsp_crq_low_thre, + &ddr_pmu->port_occu_thre.rsp_crq_hight_thre); + break; + case DDR_PMUV3_RSP_REQ_OCCU_EVTYPE: + ret = phytium_ddr_pmu_v3_verify_port_occu_threshold(low_thre, hight_thre, + &ddr_pmu->port_occu_thre.rsp_req_low_thre, + &ddr_pmu->port_occu_thre.rsp_req_hight_thre); + break; + case DDR_PMUV3_RSP_RTQ_OCCU_EVTYPE: + ret = phytium_ddr_pmu_v3_verify_port_occu_threshold(low_thre, hight_thre, + &ddr_pmu->port_occu_thre.rsp_rtq_low_thre, + &ddr_pmu->port_occu_thre.rsp_rtq_hight_thre); + break; + default: + ret = 0; + break; + } + + return ret; +} + +static void phytium_ddr_pmu_v3_set_port_occu_threshold(struct phytium_ddr_pmu *ddr_pmu, + u32 evtype, u32 low_thre, u32 hight_thre) +{ + u32 thre_reg_offset, thre_reg_idx, thre_bit_size; + u32 val; + + thre_reg_idx = evtype - DDR_PMUV3_LPR_FIFO_OCCU_EVTYPE; + thre_reg_offset = phytium_ddr_pmu_v3_thre_reg_offset[thre_reg_idx]; + thre_bit_size = phytium_ddr_pmu_v3_thre_bit_size[thre_reg_idx]; + + val = low_thre | (hight_thre << thre_bit_size); + + writel(val, ddr_pmu->base + thre_reg_offset); +} + +static void phytium_ddr_pmu_v3_reset_port_occu_threshold(struct phytium_ddr_pmu *ddr_pmu) +{ + u32 thre_reg_offset, thre_bit_size; + u32 val; + int i = 0; + + for (i = 0; i < 11; i++) { + thre_reg_offset = phytium_ddr_pmu_v3_thre_reg_offset[i]; + thre_bit_size = phytium_ddr_pmu_v3_thre_bit_size[i]; + + val = readl(ddr_pmu->base + thre_reg_offset); + val &= ~((1 << thre_bit_size) - 1); + writel(val, ddr_pmu->base + thre_reg_offset); + } + + ddr_pmu->port_occu_thre.lpr_fifo_low_thre = 0; + ddr_pmu->port_occu_thre.lpr_fifo_hight_thre = 0; + ddr_pmu->port_occu_thre.hpr_fifo_low_thre = 0; + ddr_pmu->port_occu_thre.hpr_fifo_hight_thre = 0; + ddr_pmu->port_occu_thre.wr_dcq_low_thre = 0; + ddr_pmu->port_occu_thre.wr_dcq_hight_thre = 0; + + ddr_pmu->port_occu_thre.rdat_info_low_thre = 0; + ddr_pmu->port_occu_thre.rdat_info_hight_thre = 0; + ddr_pmu->port_occu_thre.rdat_fifo_low_thre = 0; + ddr_pmu->port_occu_thre.rdat_fifo_hight_thre = 0; + ddr_pmu->port_occu_thre.wdp_buffer_low_thre = 0; + ddr_pmu->port_occu_thre.wdp_buffer_hight_thre = 0; + + ddr_pmu->port_occu_thre.rsp_comp_low_thre = 0; + ddr_pmu->port_occu_thre.rsp_comp_hight_thre = 0; + + ddr_pmu->port_occu_thre.rsp_dbid_low_thre = 0; + ddr_pmu->port_occu_thre.rsp_dbid_hight_thre = 0; + ddr_pmu->port_occu_thre.rsp_crq_low_thre = 0; + ddr_pmu->port_occu_thre.rsp_crq_hight_thre = 0; + ddr_pmu->port_occu_thre.rsp_req_low_thre = 0; + ddr_pmu->port_occu_thre.rsp_req_hight_thre = 0; + ddr_pmu->port_occu_thre.rsp_rtq_low_thre = 0; + ddr_pmu->port_occu_thre.rsp_rtq_hight_thre = 0; } int phytium_ddr_pmu_event_init(struct perf_event *event) @@ -324,8 +1511,13 @@ int phytium_ddr_pmu_event_init(struct perf_event *event) return -EINVAL; } - if (event->attr.config > PHYTIUM_DDR_MAX_COUNTERS) - return -EINVAL; + if (ddr_pmu->ver <= DDR_PMUV2P0) { + if ((event->attr.config & PHYTIUM_DDR_EVENTS_MAX_MASK) > ddr_pmu->cnts_num) + return -EINVAL; + } else if (ddr_pmu->ver == DDR_PMUV3P0) { + if (event->attr.config > PHYTIUM_DDR_MAX_COUNTERS) + return -EINVAL; + } if (ddr_pmu->on_cpu == -1) return -EINVAL; @@ -335,16 +1527,48 @@ int phytium_ddr_pmu_event_init(struct perf_event *event) event->cpu = ddr_pmu->on_cpu; + if (ddr_pmu->ver == DDR_PMUV2P0) + used_event_v2 = 0; + else if (ddr_pmu->ver == DDR_PMUV3P0) { + int ret; + u32 event_type, occu_flag; + u32 low_thre, hight_thre; + int idx; + struct attribute *attr; + struct device_attribute *dev_attr; + struct phytium_ddr_pmu_v3_event_attr *eattr; + + event_type = get_type(event); + low_thre = get_low_thre(event); + hight_thre = get_hight_thre(event); + idx = phytium_ddr_pmu_v3_get_event_idx(event); + + attr = phytium_ddr_pmu_v3_events_attr[idx]; + dev_attr = container_of(attr, struct device_attribute, attr); + eattr = container_of(dev_attr, struct phytium_ddr_pmu_v3_event_attr, attr); + occu_flag = eattr->occuflag; + + if (phytium_ddr_pmu_v3_is_port_occup_event(event_type, occu_flag)) { + ret = phytium_ddr_pmu_v3_get_port_occu_threshold(ddr_pmu, event); + if (ret < 0) + return ret; + phytium_ddr_pmu_v3_set_port_occu_threshold(ddr_pmu, event_type, low_thre, + hight_thre); + } + } + return 0; } void phytium_ddr_pmu_event_update(struct perf_event *event) { struct phytium_ddr_pmu *ddr_pmu = to_phytium_ddr_pmu(event->pmu); - struct hw_perf_event *hwc = &event->hw; u64 delta; - delta = phytium_ddr_pmu_read_counter(ddr_pmu, hwc); + if (ddr_pmu->ver == DDR_PMUV2P0) + ddr_pmu->ops->stop_all_counters(ddr_pmu); + + delta = ddr_pmu->ops->read_counter(ddr_pmu, event); local64_add(delta, &event->count); } @@ -375,103 +1599,255 @@ int phytium_ddr_pmu_event_add(struct perf_event *event, int flags) hwc->state |= PERF_HES_STOPPED; idx = phytium_ddr_pmu_mark_event(event); - if (idx < 0) - return idx; - + if (ddr_pmu->ver <= DDR_PMUV1P5) { + if (!EVENT_VALID_V1(idx)) { + dev_err(ddr_pmu->dev, "Unsupported event index:%d!\n", idx); + return idx; + } + } else if (ddr_pmu->ver == DDR_PMUV2P0) { + if (!EVENT_VALID_V2(idx)) { + dev_err(ddr_pmu->dev, "Unsupported event index:%d!\n", idx); + return idx; + } + #if IS_ENABLED(CONFIG_ARM_PHYTIUM_DMU_DEVFREQ) + phytium_ddr_pmu_v2_notifier_chain_trigger(ddr_pmu, DDR_PMUV2_NOTICE_START); + #endif + } else if (ddr_pmu->ver == DDR_PMUV3P0) { + if (!EVENT_VALID_V3(idx)) { + dev_err(ddr_pmu->dev, "Unsupported event index:%d!\n", idx); + return idx; + } + } event->hw.idx = idx; ddr_pmu->pmu_events.hw_events[idx] = event; + if (ddr_pmu->ver == DDR_PMUV2P0) { + phytium_ddr_pmu_v2_enable_events(ddr_pmu, idx); + used_event_v2 += 1; + } return 0; } void phytium_ddr_pmu_event_del(struct perf_event *event, int flags) { struct phytium_ddr_pmu *ddr_pmu = to_phytium_ddr_pmu(event->pmu); - struct hw_perf_event *hwc = &event->hw; + + if (ddr_pmu->ver == DDR_PMUV2P0) + used_event_v2 -= 1; phytium_ddr_pmu_event_stop(event, PERF_EF_UPDATE); - phytium_ddr_pmu_unmark_event(ddr_pmu, hwc->idx); + if (ddr_pmu->ver == DDR_PMUV3P0) + phytium_ddr_pmu_v3_reset_port_occu_threshold(ddr_pmu); + + phytium_ddr_pmu_unmark_event(event); perf_event_update_userpage(event); - ddr_pmu->pmu_events.hw_events[hwc->idx] = NULL; + + if (ddr_pmu->ver == DDR_PMUV2P0) { +#if IS_ENABLED(CONFIG_ARM_PHYTIUM_DMU_DEVFREQ) + if (used_event_v2 == 0) + phytium_ddr_pmu_v2_notifier_chain_trigger(ddr_pmu, DDR_PMUV2_NOTICE_STOP); +#endif + } +} + +void phytium_ddr_pmu_v1_enable_counters(struct phytium_ddr_pmu *ddr_pmu) +{ + ddr_pmu->ops->clear_all_counters(ddr_pmu); + ddr_pmu->ops->start_all_counters(ddr_pmu); +} + +void phytium_ddr_pmu_v2_enable_counters(struct phytium_ddr_pmu *ddr_pmu) +{ + ddr_pmu->ops->stop_all_counters(ddr_pmu); + ddr_pmu->ops->clear_all_counters(ddr_pmu); + phytium_ddr_pmu_v2_reset_timer(ddr_pmu); + ddr_pmu->ops->start_all_counters(ddr_pmu); +} + +void phytium_ddr_pmu_v3_enable_counters(struct phytium_ddr_pmu *ddr_pmu) +{ + ddr_pmu->ops->enable_clk(ddr_pmu); + ddr_pmu->ops->clear_all_counters(ddr_pmu); + ddr_pmu->ops->start_all_counters(ddr_pmu); } void phytium_ddr_pmu_enable(struct pmu *pmu) { struct phytium_ddr_pmu *ddr_pmu = to_phytium_ddr_pmu(pmu); int event_added = bitmap_weight(ddr_pmu->pmu_events.used_mask, - PHYTIUM_DDR_MAX_COUNTERS); + ddr_pmu->cnts_num); if (event_added) { - phytium_ddr_pmu_clear_all_counters(ddr_pmu); - phytium_ddr_pmu_start_all_counters(ddr_pmu); + ddr_pmu->ops->enable_counters(ddr_pmu); } } +static void phytium_ddr_pmu_v2_mask_all_irq(struct phytium_ddr_pmu *ddr_pmu) +{ + writel(DDR_PMUV2_TIMER_OPT_BIT, ddr_pmu->base + DDR_PMUV2_TIMER_INT_MASK); + writel(DDR_PMUV2_AXI_MONITOR_OPT_BIT, ddr_pmu->base + DDR_PMUV2_AXI_MONITOR_INT_MASK); +} + +static void phytium_ddr_pmu_v2_disable_axi_cmd_events(struct phytium_ddr_pmu *ddr_pmu) +{ + writel(0x0, ddr_pmu->base + DDR_PMUV2_AXI_MONITOR_EN); +} + +void phytium_ddr_pmu_v1_disable_counters(struct phytium_ddr_pmu *ddr_pmu) +{ + ddr_pmu->ops->stop_all_counters(ddr_pmu); +} + +void phytium_ddr_pmu_v2_disable_counters(struct phytium_ddr_pmu *ddr_pmu) +{ + if (ddr_pmu->used_flag) { + phytium_ddr_pmu_v2_mask_all_irq(ddr_pmu); + phytium_ddr_pmu_v2_disable_axi_cmd_events(ddr_pmu); + } +} + +void phytium_ddr_pmu_v3_disable_counters(struct phytium_ddr_pmu *ddr_pmu) +{ + ddr_pmu->ops->stop_all_counters(ddr_pmu); + phytium_ddr_pmu_v3_snapshot_counters(ddr_pmu); +} + void phytium_ddr_pmu_disable(struct pmu *pmu) { struct phytium_ddr_pmu *ddr_pmu = to_phytium_ddr_pmu(pmu); int event_added = bitmap_weight(ddr_pmu->pmu_events.used_mask, - PHYTIUM_DDR_MAX_COUNTERS); + ddr_pmu->cnts_num); if (event_added) - phytium_ddr_pmu_stop_all_counters(ddr_pmu); + ddr_pmu->ops->disable_counters(ddr_pmu); } -void phytium_ddr_pmu_reset(struct phytium_ddr_pmu *ddr_pmu) +void phytium_ddr_pmu_v1_reset(struct phytium_ddr_pmu *ddr_pmu) { - phytium_ddr_pmu_disable_clk(ddr_pmu); - phytium_ddr_pmu_clear_all_counters(ddr_pmu); + ddr_pmu->ops->disable_clk(ddr_pmu); + ddr_pmu->ops->clear_all_counters(ddr_pmu); } static const struct acpi_device_id phytium_ddr_pmu_acpi_match[] = { { "PHYT0043", }, { "PHYT0067", }, + { "PHYT0069", }, {}, }; MODULE_DEVICE_TABLE(acpi, phytium_ddr_pmu_acpi_match); -static irqreturn_t phytium_ddr_pmu_overflow_handler(int irq, void *dev_id) +static irqreturn_t phytium_ddr_pmu_v1_overflow_handler(int irq, void *dev_id) { struct phytium_ddr_pmu *ddr_pmu = dev_id; struct perf_event *event; unsigned long overflown, stop_state; unsigned long *used_mask = ddr_pmu->pmu_events.used_mask; int idx; - int event_added = bitmap_weight(used_mask, PHYTIUM_DDR_MAX_COUNTERS); + int event_added = bitmap_weight(used_mask, ddr_pmu->cnts_num); - overflown = phytium_ddr_pmu_get_irq_flag(ddr_pmu); + overflown = phytium_ddr_pmu_v1_get_irq_flag(ddr_pmu); if (!test_bit(ddr_pmu->irq_bit, &overflown)) return IRQ_NONE; - stop_state = phytium_ddr_pmu_get_stop_state(ddr_pmu); + stop_state = phytium_ddr_pmu_v1_get_stop_state(ddr_pmu); if (bitmap_weight(&stop_state, 6)) { - for_each_set_bit(idx, used_mask, PHYTIUM_DDR_MAX_COUNTERS) { + for_each_set_bit(idx, used_mask, ddr_pmu->cnts_num) { event = ddr_pmu->pmu_events.hw_events[idx]; if (!event) continue; phytium_ddr_pmu_event_update(event); } - phytium_ddr_pmu_clear_all_counters(ddr_pmu); - if ((stop_state & DDR_PMU_OFL_STOP_TYPE_VAL) == 0) - phytium_ddr_pmu_start_all_counters(ddr_pmu); + ddr_pmu->ops->clear_all_counters(ddr_pmu); + if ((stop_state & DDR_PMUV1_OFL_STOP_TYPE_VAL) == 0) + ddr_pmu->ops->start_all_counters(ddr_pmu); return IRQ_HANDLED; } if (!event_added) { - phytium_ddr_pmu_clear_all_counters(ddr_pmu); + ddr_pmu->ops->clear_all_counters(ddr_pmu); return IRQ_HANDLED; } return IRQ_NONE; } -static int phytium_verify_pbf_version(struct platform_device *pdev) +static irqreturn_t phytium_ddr_pmu_v2_overflow_handler(int irq, void *dev_id) +{ + struct phytium_ddr_pmu *ddr_pmu = dev_id; + struct perf_event *event; + int idx; + unsigned long *used_mask = ddr_pmu->pmu_events.used_mask; + u32 timer_int_sta, axi_int_sta; + + timer_int_sta = readl(ddr_pmu->base + DDR_PMUV2_TIMER_INT_STA); + axi_int_sta = readl(ddr_pmu->base + DDR_PMUV2_AXI_MONITOR_INT_STA); + + if ((timer_int_sta + axi_int_sta) == 0) + return IRQ_NONE; + + if (timer_int_sta) + writel(0x1, ddr_pmu->base + DDR_PMUV2_TIMER_INT_CLEAR); + + if (axi_int_sta) + writel(axi_int_sta, ddr_pmu->base + ddr_pmu->cnts_num); + + if (!ddr_pmu->used_flag) { + phytium_ddr_pmu_v2_mask_all_irq(ddr_pmu); + return IRQ_HANDLED; + } + + for_each_set_bit(idx, used_mask, PHYTIUM_DDR_PMUV2_COUNTERS_NUM) { + event = ddr_pmu->pmu_events.hw_events[idx]; + if (!event) + continue; + phytium_ddr_pmu_event_update(event); + } + writel(DDR_PMUV2_ALL_EVENT_CLEAR_BIT, ddr_pmu->base + DDR_PMUV2_CLEAR_EVENT); + ddr_pmu->ops->start_all_counters(ddr_pmu); + + return IRQ_HANDLED; +} + +static const struct phytium_ddr_pmu_ops phytium_ddr_pmu_v1_ops = { + .overflow_handler = phytium_ddr_pmu_v1_overflow_handler, + .read_counter = phytium_ddr_pmu_v1_read_counter, + .clear_all_counters = phytium_ddr_pmu_v1_clear_all_counters, + .start_all_counters = phytium_ddr_pmu_v1_start_all_counters, + .stop_all_counters = phytium_ddr_pmu_v1_stop_all_counters, + .enable_counters = phytium_ddr_pmu_v1_enable_counters, + .disable_counters = phytium_ddr_pmu_v1_disable_counters, + .enable_clk = phytium_ddr_pmu_v1_enable_clk, + .disable_clk = phytium_ddr_pmu_v1_disable_clk, +}; + +static const struct phytium_ddr_pmu_ops phytium_ddr_pmu_v2_ops = { + .overflow_handler = phytium_ddr_pmu_v2_overflow_handler, + .read_counter = phytium_ddr_pmu_v2_read_counter, + .clear_all_counters = phytium_ddr_pmu_v2_clear_all_counters, + .start_all_counters = phytium_ddr_pmu_v2_start_all_counters, + .stop_all_counters = phytium_ddr_pmu_v2_stop_all_counters, + .enable_counters = phytium_ddr_pmu_v2_enable_counters, + .disable_counters = phytium_ddr_pmu_v2_disable_counters, +}; + +static const struct phytium_ddr_pmu_ops phytium_ddr_pmu_v3_ops = { + .read_counter = phytium_ddr_pmu_v3_read_counter, + .clear_all_counters = phytium_ddr_pmu_v3_clear_all_counters, + .start_all_counters = phytium_ddr_pmu_v3_start_all_counters, + .stop_all_counters = phytium_ddr_pmu_v3_stop_all_counters, + .enable_clk = phytium_ddr_pmu_v3_enable_clk, + .disable_clk = phytium_ddr_pmu_v3_disable_clk, + .enable_counters = phytium_ddr_pmu_v3_enable_counters, + .disable_counters = phytium_ddr_pmu_v3_disable_counters, +}; + +static int phytium_ddr_pmu_v1_verify_pbf_version(struct platform_device *pdev) { struct arm_smccc_res res; unsigned long major_ver, minor_ver; @@ -502,9 +1878,17 @@ static int phytium_ddr_pmu_version(struct platform_device *pdev, acpi_dev = ACPI_COMPANION(&pdev->dev); if (!strcmp(acpi_device_hid(acpi_dev), "PHYT0043")) { - ddr_pmu->ver = DDRV1P0; + ddr_pmu->ver = DDR_PMUV1P0; } else if (!strcmp(acpi_device_hid(acpi_dev), "PHYT0067")) { - ddr_pmu->ver = DDRV1P5; + ddr_pmu->ver = DDR_PMUV1P5; + } else if (!strcmp(acpi_device_hid(acpi_dev), "PHYT0069")) { + ddr_pmu->ver = DDR_PMUV2P0; + #if !IS_ENABLED(CONFIG_PHYT_DMU_PMU_PD2408) + dev_err(&pdev->dev, "CONFIG_PHYT_DMU_PMU_PD2408 not enabled\n"); + return -ENODEV; + #endif + } else if (!strcmp(acpi_device_hid(acpi_dev), "PHYT3003")) { + ddr_pmu->ver = DDR_PMUV3P0; } else { dev_err(&pdev->dev, "The current driver does not support this device.\n"); return -ENODEV; @@ -523,9 +1907,10 @@ static int phytium_ddr_pmu_init_irq(struct phytium_ddr_pmu *ddr_pmu, return irq; ret = devm_request_irq(&pdev->dev, irq, - phytium_ddr_pmu_overflow_handler, + ddr_pmu->ops->overflow_handler, IRQF_NOBALANCING | IRQF_NO_THREAD | IRQF_SHARED, dev_name(&pdev->dev), ddr_pmu); + if (ret < 0) { dev_err(&pdev->dev, "Fail to request IRQ:%d ret:%d\n", irq, ret); @@ -537,31 +1922,29 @@ static int phytium_ddr_pmu_init_irq(struct phytium_ddr_pmu *ddr_pmu, return 0; } -static int phytium_ddr_pmu_init_data(struct platform_device *pdev, +static int phytium_ddr_pmu_v1_init_data(struct platform_device *pdev, struct phytium_ddr_pmu *ddr_pmu) { struct resource *res, *clkres, *irqres; if (device_property_read_u32(&pdev->dev, "phytium,die-id", - &ddr_pmu->die_id)) { + &ddr_pmu->die_id)) { dev_err(&pdev->dev, "Can not read phytium,die-id!\n"); return -EINVAL; } if (device_property_read_u32(&pdev->dev, "phytium,ddr-id", - &ddr_pmu->ddr_id)) { + &ddr_pmu->ddr_id)) { dev_err(&pdev->dev, "Can not read phytium,ddr-id!\n"); return -EINVAL; } if (device_property_read_u32(&pdev->dev, "phytium,pmu-id", - &ddr_pmu->pmu_id)) { + &ddr_pmu->pmu_id)) { dev_err(&pdev->dev, "Can not read ddr pmu-id!\n"); return -EINVAL; } - ddr_pmu->irq_bit = ddr_pmu->ddr_id * 2 + ddr_pmu->pmu_id; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ddr_pmu->base = devm_ioremap_resource(&pdev->dev, res); @@ -571,24 +1954,28 @@ static int phytium_ddr_pmu_init_data(struct platform_device *pdev, return PTR_ERR(ddr_pmu->base); } + ddr_pmu->irq_bit = ddr_pmu->ddr_id * 2 + ddr_pmu->pmu_id; + clkres = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!clkres) { dev_err(&pdev->dev, "failed for get ddr_pmu clk resource.\n"); return -EINVAL; } - ddr_pmu->cfg_base = devm_ioremap(&pdev->dev, clkres->start, resource_size(clkres)); + ddr_pmu->cfg_base = devm_ioremap(&pdev->dev, clkres->start, + resource_size(clkres)); if (IS_ERR(ddr_pmu->cfg_base)) { dev_err(&pdev->dev, "ioremap failed for ddr_pmu clk resource\n"); return PTR_ERR(ddr_pmu->cfg_base); } - if (ddr_pmu->ver == DDRV1P5) { + if (ddr_pmu->ver == DDR_PMUV1P5) { irqres = platform_get_resource(pdev, IORESOURCE_MEM, 2); if (!irqres) { dev_err(&pdev->dev, "failed for get ddr_pmu irq resource.\n"); return -EINVAL; } - ddr_pmu->irq_reg = devm_ioremap(&pdev->dev, irqres->start, resource_size(irqres)); + ddr_pmu->irq_reg = devm_ioremap(&pdev->dev, irqres->start, + resource_size(irqres)); if (IS_ERR(ddr_pmu->irq_reg)) { dev_err(&pdev->dev, "ioremap failed for ddr_pmu irq resource\n"); return PTR_ERR(ddr_pmu->irq_reg); @@ -597,7 +1984,77 @@ static int phytium_ddr_pmu_init_data(struct platform_device *pdev, ddr_pmu->irq_reg = ddr_pmu->cfg_base + 0x4; } - phytium_ddr_pmu_reset(ddr_pmu); + ddr_pmu->cnts_num = PHYTIUM_DDR_PMUV1_COUNTERS_NUM; + ddr_pmu->ops = &phytium_ddr_pmu_v1_ops; + + phytium_ddr_pmu_v1_reset(ddr_pmu); + + return 0; +} + +static int phytium_ddr_pmu_v2_init_data(struct platform_device *pdev, + struct phytium_ddr_pmu *ddr_pmu) +{ + struct resource *res; + + if (device_property_read_u32(&pdev->dev, "phytium,ddr-id", + &ddr_pmu->ddr_id)) { + dev_err(&pdev->dev, "Can not read phytium,ddr-id!\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ddr_pmu->base = devm_ioremap_resource(&pdev->dev, res); + + if (IS_ERR(ddr_pmu->base)) { + dev_err(&pdev->dev, + "ioremap failed for ddr_pmu base resource\n"); + return PTR_ERR(ddr_pmu->base); + } + + ddr_pmu->used_flag = true; + phytium_ddr_pmu_v2_mask_all_irq(ddr_pmu); + + ddr_pmu->cnts_num = PHYTIUM_DDR_PMUV2_COUNTERS_NUM; + ddr_pmu->ops = &phytium_ddr_pmu_v2_ops; + + return 0; +} + +static int phytium_ddr_pmu_v3_init_data(struct platform_device *pdev, + struct phytium_ddr_pmu *ddr_pmu) +{ + struct resource *res; + + if (device_property_read_u32(&pdev->dev, "phytium,die-id", + &ddr_pmu->die_id)) { + dev_err(&pdev->dev, "Can not read phytium,die-id!\n"); + return -EINVAL; + } + + if (device_property_read_u32(&pdev->dev, "phytium,ddr-id", + &ddr_pmu->ddr_id)) { + dev_err(&pdev->dev, "Can not read phytium,ddr-id!\n"); + return -EINVAL; + } + + if (device_property_read_u32(&pdev->dev, "phytium,pmu-id", + &ddr_pmu->pmu_id)) { + dev_err(&pdev->dev, "Can not read ddr pmu-id!\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ddr_pmu->base = devm_ioremap_resource(&pdev->dev, res); + + if (IS_ERR(ddr_pmu->base)) { + dev_err(&pdev->dev, + "ioremap failed for ddr_pmu base resource\n"); + return PTR_ERR(ddr_pmu->base); + } + + ddr_pmu->cnts_num = PHYTIUM_DDR_PMUV3_COUNTERS_NUM; + ddr_pmu->ops = &phytium_ddr_pmu_v3_ops; return 0; } @@ -611,19 +2068,26 @@ static int phytium_ddr_pmu_dev_probe(struct platform_device *pdev, if (ret) return ret; - if (ddr_pmu->ver == DDRV1P0) { - ret = phytium_verify_pbf_version(pdev); + if (ddr_pmu->ver == DDR_PMUV1P0) { + ret = phytium_ddr_pmu_v1_verify_pbf_version(pdev); if (ret) return ret; } - ret = phytium_ddr_pmu_init_data(pdev, ddr_pmu); + if (ddr_pmu->ver <= DDR_PMUV1P5) + ret = phytium_ddr_pmu_v1_init_data(pdev, ddr_pmu); + else if (ddr_pmu->ver == DDR_PMUV2P0) + ret = phytium_ddr_pmu_v2_init_data(pdev, ddr_pmu); + else if (ddr_pmu->ver == DDR_PMUV3P0) + ret = phytium_ddr_pmu_v3_init_data(pdev, ddr_pmu); if (ret) return ret; - ret = phytium_ddr_pmu_init_irq(ddr_pmu, pdev); - if (ret) - return ret; + if (ddr_pmu->ver <= DDR_PMUV2P0) { + ret = phytium_ddr_pmu_init_irq(ddr_pmu, pdev); + if (ret) + return ret; + } ddr_pmu->dev = &pdev->dev; ddr_pmu->on_cpu = -1; @@ -654,9 +2118,13 @@ static int phytium_ddr_pmu_probe(struct platform_device *pdev) return ret; } - name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "phyt%u_ddr%u_pmu%u", - ddr_pmu->die_id, ddr_pmu->ddr_id, - ddr_pmu->pmu_id); + if (ddr_pmu->ver == DDR_PMUV2P0) + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "phyt_ddr%u_pmu", + ddr_pmu->ddr_id); + else + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "phyt%u_ddr%u_pmu%u", + ddr_pmu->die_id, ddr_pmu->ddr_id, + ddr_pmu->pmu_id); ddr_pmu->pmu = (struct pmu){ .name = name, .module = THIS_MODULE, @@ -669,9 +2137,24 @@ static int phytium_ddr_pmu_probe(struct platform_device *pdev) .start = phytium_ddr_pmu_event_start, .stop = phytium_ddr_pmu_event_stop, .read = phytium_ddr_pmu_event_update, - .attr_groups = phytium_ddr_pmu_attr_groups, }; + if (ddr_pmu->ver == DDR_PMUV2P0) { + ddr_pmu->pmu.attr_groups = phytium_ddr_pmu_v2_attr_groups; + dev_info(ddr_pmu->dev, "%s on cpu%d.\n", + name, ddr_pmu->on_cpu); + } else { + if (ddr_pmu->ver <= DDR_PMUV1P5) + ddr_pmu->pmu.attr_groups = phytium_ddr_pmu_v1_attr_groups; + else if (ddr_pmu->ver == DDR_PMUV3P0) + ddr_pmu->pmu.attr_groups = phytium_ddr_pmu_v3_attr_groups; + + ddr_pmu->ops->enable_clk(ddr_pmu); + + dev_info(ddr_pmu->dev, "die%d_ddr%d_pmu%d on cpu%d.\n", ddr_pmu->die_id, + ddr_pmu->ddr_id, ddr_pmu->pmu_id, ddr_pmu->on_cpu); + } + ret = perf_pmu_register(&ddr_pmu->pmu, name, -1); if (ret) { dev_err(ddr_pmu->dev, "DDR PMU register failed!\n"); @@ -679,11 +2162,6 @@ static int phytium_ddr_pmu_probe(struct platform_device *pdev) &ddr_pmu->node); } - phytium_ddr_pmu_enable_clk(ddr_pmu); - - pr_info("die%d_ddr%d_pmu%d on cpu%d.\n", ddr_pmu->die_id, - ddr_pmu->ddr_id, ddr_pmu->pmu_id, ddr_pmu->on_cpu); - return ret; } @@ -691,7 +2169,10 @@ static int phytium_ddr_pmu_remove(struct platform_device *pdev) { struct phytium_ddr_pmu *ddr_pmu = platform_get_drvdata(pdev); - phytium_ddr_pmu_disable_clk(ddr_pmu); + if (ddr_pmu->ver <= DDR_PMUV1P5 || ddr_pmu->ver == DDR_PMUV3P0) + ddr_pmu->ops->disable_clk(ddr_pmu); + else if (ddr_pmu->ver == DDR_PMUV2P0) + phytium_ddr_pmu_v2_mask_all_irq(ddr_pmu); perf_pmu_unregister(&ddr_pmu->pmu); cpuhp_state_remove_instance_nocalls(phytium_ddr_pmu_hp_state, @@ -798,3 +2279,4 @@ MODULE_LICENSE("GPL"); MODULE_VERSION(DDR_PERF_DRIVER_VERSION); MODULE_AUTHOR("Hu Xianghua "); MODULE_AUTHOR("Tan Rui "); +MODULE_AUTHOR("Fu Boyi "); diff --git a/drivers/perf/phytium/phytium_dmu_pmu_pd2408.c b/drivers/perf/phytium/phytium_dmu_pmu_pd2408.c deleted file mode 100644 index b4a9ff59b202b..0000000000000 --- a/drivers/perf/phytium/phytium_dmu_pmu_pd2408.c +++ /dev/null @@ -1,710 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Phytium PD2408 DMU performance monitoring unit support - * - * Copyright (c) 2025, Phytium Technology Co., Ltd. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#undef pr_fmt -#define pr_fmt(fmt) "pd2408_dmu_pmu: " fmt - -#define DMU_PERF_DRIVER_VERSION "1.0.2" - -#define DMU_PMU_TIMER_START 0x0 -#define DMU_PMU_TIMER_STOP 0x4 -#define DMU_PMU_CLEAR_EVENT 0x8 -#define DMU_PMU_SET_TIMER_L 0xc -#define DMU_PMU_SET_TIMER_H 0x10 -#define DMU_PMU_AXI_MONITOR_EN 0x1c -#define DMU_PMU_TIMER_INT_CLEAR 0x2c -#define DMU_PMU_AXI_MONITOR_INT_CLEAR 0x30 -#define DMU_PMU_TIMER_INT_STA 0x40 -#define DMU_PMU_AXI_MONITOR_INT_STA 0x44 -#define DMU_PMU_TIMER_INT_MASK 0x54 -#define DMU_PMU_AXI_MONITOR_INT_MASK 0x58 - -#define DMU_PMU_EVENT_CYCLES 0x208 - -#define DMU_PMU_EVENT_AXI_READ_CMD_CNT 0x074 -#define DMU_PMU_EVENT_AXI_WRITE_CMD_CNT 0x07c -#define DMU_PMU_EVENT_AXI_READ_FLUX_CNT 0x084 -#define DMU_PMU_EVENT_AXI_WRITE_FLUX_CNT 0x08c - -#define DMU_PMU_MAX_COUNTERS 5 -#define DMU_PMU_MAX_COUNTERS_TIMER 1 -#define DMU_PMU_MAX_COUNTERS_AXI 4 - -#define ALL_EVENT_CLEAR_BIT 0x1 -#define DMU_PMU_TIMER_OPT_BIT 0x1 -#define AXI_MONITOR_OPT_BIT 0x01010101 - -#define DMU_PMU_NOTICE_START 0x0 -#define DMU_PMU_NOTICE_STOP 0x1 - -#define to_pd2408_dmu_pmu(p) (container_of(p, struct pd2408_dmu_pmu, pmu)) - -#define GET_DMU_EVENTID(hwc) (hwc->config_base & 0x7) -#define EVENT_VALID(idx) ((idx >= 0) && (idx < DMU_PMU_MAX_COUNTERS)) - -static int pd2408_dmu_pmu_hp_state; -int used_event; - -struct pd2408_dmu_pmu_hwevents { - struct perf_event *hw_events[DMU_PMU_MAX_COUNTERS]; - DECLARE_BITMAP(used_event_mask, DMU_PMU_MAX_COUNTERS); -}; - -struct pd2408_dmu_pmu { - struct device *dev; - void __iomem *base; - struct pmu pmu; - struct pd2408_dmu_pmu_hwevents pmu_events; - struct hlist_node node; - int on_cpu; - int irq; - u32 soc_version; - u32 dmu_id; - bool used_flag; -}; - -static const u32 dmu_counter_reg_offset[] = { - DMU_PMU_EVENT_CYCLES, - DMU_PMU_EVENT_AXI_WRITE_FLUX_CNT, DMU_PMU_EVENT_AXI_READ_FLUX_CNT, - DMU_PMU_EVENT_AXI_WRITE_CMD_CNT, DMU_PMU_EVENT_AXI_READ_CMD_CNT -}; - -ssize_t pd2408_dmu_pmu_format_sysfs_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct dev_ext_attribute *eattr; - - eattr = container_of(attr, struct dev_ext_attribute, attr); - - return sprintf(buf, "%s\n", (char *)eattr->var); -} - -ssize_t pd2408_dmu_pmu_event_sysfs_show(struct device *dev, - struct device_attribute *attr, - char *page) -{ - struct dev_ext_attribute *eattr; - - eattr = container_of(attr, struct dev_ext_attribute, attr); - - return sprintf(page, "config=0x%lx\n", (unsigned long)eattr->var); -} - -static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct pd2408_dmu_pmu *dmu_pmu = - to_pd2408_dmu_pmu(dev_get_drvdata(dev)); - - return cpumap_print_to_pagebuf(true, buf, cpumask_of(dmu_pmu->on_cpu)); -} - -#define PHYTIUM_PMU_ATTR(_name, _func, _config) \ - (&((struct dev_ext_attribute[]){ \ - { __ATTR(_name, 0444, _func, NULL), (void *)_config } })[0] \ - .attr.attr) - -#define PHYTIUM_DMU_PMU_FORMAT_ATTR(_name, _config) \ - PHYTIUM_PMU_ATTR(_name, pd2408_dmu_pmu_format_sysfs_show, \ - (void *)_config) - -#define PHYTIUM_DMU_PMU_EVENT_ATTR(_name, _config) \ - PHYTIUM_PMU_ATTR(_name, pd2408_dmu_pmu_event_sysfs_show, \ - (unsigned long)_config) - -static struct attribute *pd2408_dmu_pmu_format_attr[] = { - PHYTIUM_DMU_PMU_FORMAT_ATTR(event, "config:0-2"), - NULL, -}; - -static const struct attribute_group pd2408_dmu_pmu_format_group = { - .name = "format", - .attrs = pd2408_dmu_pmu_format_attr, -}; - -static struct attribute *pd2408_dmu_pmu_events_attr[] = { - PHYTIUM_DMU_PMU_EVENT_ATTR(dmu_axi_cycles, 0x00), - PHYTIUM_DMU_PMU_EVENT_ATTR(axi_write_flux, 0x01), - PHYTIUM_DMU_PMU_EVENT_ATTR(axi_read_flux, 0x02), - PHYTIUM_DMU_PMU_EVENT_ATTR(axi_write_cmd, 0x03), - PHYTIUM_DMU_PMU_EVENT_ATTR(axi_read_cmd, 0x04), - NULL, -}; - -static const struct attribute_group pd2408_dmu_pmu_events_group = { - .name = "events", - .attrs = pd2408_dmu_pmu_events_attr, -}; - -static DEVICE_ATTR_RO(cpumask); - -static struct attribute *pd2408_dmu_pmu_cpumask_attrs[] = { - &dev_attr_cpumask.attr, - NULL, -}; - -static const struct attribute_group pd2408_dmu_pmu_cpumask_attr_group = { - .attrs = pd2408_dmu_pmu_cpumask_attrs, -}; - -static const struct attribute_group *pd2408_dmu_pmu_attr_groups[] = { - &pd2408_dmu_pmu_format_group, - &pd2408_dmu_pmu_events_group, - &pd2408_dmu_pmu_cpumask_attr_group, - NULL, -}; - -#if IS_ENABLED(CONFIG_ARM_PHYTIUM_DMU_DEVFREQ) -extern struct blocking_notifier_head dmu_pmu_notifier_chain; - -void pd2408_dmu_pmu_notifier_chain_trigger(struct pd2408_dmu_pmu *dmu_pmu, int event) -{ - static bool start_flag; - - if ((event == DMU_PMU_NOTICE_START) && (start_flag == false)) { - blocking_notifier_call_chain(&dmu_pmu_notifier_chain, event, NULL); - start_flag = true; - dmu_pmu->used_flag = true; - } else if ((event == DMU_PMU_NOTICE_STOP) && (start_flag == true)) { - blocking_notifier_call_chain(&dmu_pmu_notifier_chain, event, NULL); - start_flag = false; - dmu_pmu->used_flag = false; - } -} -#endif - -static u64 pd2408_dmu_pmu_read_counter(struct pd2408_dmu_pmu *dmu_pmu, - struct hw_perf_event *hwc) -{ - u32 val32_l, val32_h, idx, counter_offset; - u64 val64; - - idx = GET_DMU_EVENTID(hwc); - counter_offset = dmu_counter_reg_offset[idx]; - - if (!EVENT_VALID(idx)) { - dev_err(dmu_pmu->dev, "Unsupported event index:%d!\n", idx); - return 0; - } - - val32_l = readl(dmu_pmu->base + counter_offset); - val32_h = readl(dmu_pmu->base + counter_offset + 4); - val64 = (u64)val32_h << 32 | (u64)val32_l; - - return val64; -} - -static void pd2408_dmu_pmu_clear_all_counters(struct pd2408_dmu_pmu *dmu_pmu) -{ - writel(BIT(0), dmu_pmu->base + DMU_PMU_CLEAR_EVENT); -} - -static void pd2408_dmu_pmu_disable_axi_cmd_events(struct pd2408_dmu_pmu *dmu_pmu) -{ - writel(0x0, dmu_pmu->base + DMU_PMU_AXI_MONITOR_EN); -} - -static void pd2408_dmu_pmu_mask_all_irq(struct pd2408_dmu_pmu *dmu_pmu) -{ - writel(DMU_PMU_TIMER_OPT_BIT, dmu_pmu->base + DMU_PMU_TIMER_INT_MASK); - writel(AXI_MONITOR_OPT_BIT, dmu_pmu->base + DMU_PMU_AXI_MONITOR_INT_MASK); -} - -static void pd2408_dmu_pmu_start_all_counters(struct pd2408_dmu_pmu *dmu_pmu) -{ - writel(BIT(0), dmu_pmu->base + DMU_PMU_TIMER_START); -} - -static void pd2408_dmu_pmu_stop_all_counters(struct pd2408_dmu_pmu *dmu_pmu) -{ - writel(BIT(0), dmu_pmu->base + DMU_PMU_TIMER_STOP); -} - -static void pd2408_dmu_pmu_reset_timer(struct pd2408_dmu_pmu *dmu_pmu) -{ - writel(0xFFFFFFFF, dmu_pmu->base + DMU_PMU_SET_TIMER_L); - writel(0xFFFFFFFF, dmu_pmu->base + DMU_PMU_SET_TIMER_H); -} - -static void pd2408_dmu_pmu_enable_events(struct pd2408_dmu_pmu *dmu_pmu, int idx) -{ - u8 en_bit; - u32 en_offset, irq_offset, val; - - if (idx == 0) { - en_bit = 0; - en_offset = 0; - irq_offset = DMU_PMU_TIMER_INT_MASK; - } else { - en_bit = (idx - 1) * 8; - en_offset = DMU_PMU_AXI_MONITOR_EN; - irq_offset = DMU_PMU_AXI_MONITOR_INT_MASK; - } - - if (en_offset) { - val = readl(dmu_pmu->base + en_offset); - val |= BIT(en_bit); - writel(val, dmu_pmu->base + en_offset); - } - - val = readl(dmu_pmu->base + irq_offset); - val &= ~BIT(en_bit); - writel(val, dmu_pmu->base + irq_offset); -} - -static int pd2408_dmu_pmu_mark_event(struct perf_event *event) -{ - struct pd2408_dmu_pmu *dmu_pmu = to_pd2408_dmu_pmu(event->pmu); - struct hw_perf_event *hwc = &event->hw; - unsigned long *used_mask = dmu_pmu->pmu_events.used_event_mask; - - int idx = GET_DMU_EVENTID(hwc); - - if (test_bit(idx, used_mask)) - return -EAGAIN; - - set_bit(idx, used_mask); - - return idx; -} - -static void pd2408_dmu_pmu_unmark_event(struct pd2408_dmu_pmu *dmu_pmu, - int idx) -{ - if (!EVENT_VALID(idx)) { - dev_err(dmu_pmu->dev, "Unsupported event index:%d!\n", idx); - return; - } - - clear_bit(idx, dmu_pmu->pmu_events.used_event_mask); -} - -int pd2408_dmu_pmu_event_init(struct perf_event *event) -{ - struct hw_perf_event *hwc = &event->hw; - struct pd2408_dmu_pmu *dmu_pmu; - - if (event->attr.type != event->pmu->type) - return -ENOENT; - - if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK) - return -EOPNOTSUPP; - - dmu_pmu = to_pd2408_dmu_pmu(event->pmu); - - if (event->cpu < 0) { - dev_warn(dmu_pmu->dev, "Can't provide per-task data!\n"); - return -EINVAL; - } - - if (event->attr.config > DMU_PMU_MAX_COUNTERS) - return -EINVAL; - - if (dmu_pmu->on_cpu == -1) - return -EINVAL; - - hwc->idx = -1; - hwc->config_base = event->attr.config; - - event->cpu = dmu_pmu->on_cpu; - used_event = 0; - - return 0; -} - -void pd2408_dmu_pmu_event_update(struct perf_event *event) -{ - struct pd2408_dmu_pmu *dmu_pmu = to_pd2408_dmu_pmu(event->pmu); - struct hw_perf_event *hwc = &event->hw; - u64 delta; - - pd2408_dmu_pmu_stop_all_counters(dmu_pmu); - delta = pd2408_dmu_pmu_read_counter(dmu_pmu, hwc); - local64_add(delta, &event->count); - -} - -void pd2408_dmu_pmu_event_start(struct perf_event *event, int flags) -{ - struct hw_perf_event *hwc = &event->hw; - - hwc->state = 0; - perf_event_update_userpage(event); -} - -void pd2408_dmu_pmu_event_stop(struct perf_event *event, int flags) -{ - struct hw_perf_event *hwc = &event->hw; - - hwc->state |= PERF_HES_STOPPED; - - if (flags & PERF_EF_UPDATE) - pd2408_dmu_pmu_event_update(event); -} - -int pd2408_dmu_pmu_event_add(struct perf_event *event, int flags) -{ - struct pd2408_dmu_pmu *dmu_pmu = to_pd2408_dmu_pmu(event->pmu); - struct hw_perf_event *hwc = &event->hw; - int idx; - - hwc->state |= PERF_HES_STOPPED; - - idx = pd2408_dmu_pmu_mark_event(event); - if (!EVENT_VALID(idx)) { - dev_err(dmu_pmu->dev, "Unsupported event index:%d!\n", idx); - return idx; - } -#if IS_ENABLED(CONFIG_ARM_PHYTIUM_DMU_DEVFREQ) - pd2408_dmu_pmu_notifier_chain_trigger(dmu_pmu, DMU_PMU_NOTICE_START); -#endif - event->hw.idx = idx; - dmu_pmu->pmu_events.hw_events[idx] = event; - - pd2408_dmu_pmu_enable_events(dmu_pmu, idx); - used_event += 1; - return 0; -} - -void pd2408_dmu_pmu_event_del(struct perf_event *event, int flags) -{ - struct pd2408_dmu_pmu *dmu_pmu = to_pd2408_dmu_pmu(event->pmu); - struct hw_perf_event *hwc = &event->hw; - - used_event -= 1; - pd2408_dmu_pmu_event_stop(event, PERF_EF_UPDATE); - - pd2408_dmu_pmu_unmark_event(dmu_pmu, hwc->idx); - - perf_event_update_userpage(event); - dmu_pmu->pmu_events.hw_events[hwc->idx] = NULL; -#if IS_ENABLED(CONFIG_ARM_PHYTIUM_DMU_DEVFREQ) - if (used_event == 0) - pd2408_dmu_pmu_notifier_chain_trigger(dmu_pmu, DMU_PMU_NOTICE_STOP); -#endif - -} - -void pd2408_dmu_pmu_enable(struct pmu *pmu) -{ - struct pd2408_dmu_pmu *dmu_pmu = to_pd2408_dmu_pmu(pmu); - int event_added; - - event_added = bitmap_weight(dmu_pmu->pmu_events.used_event_mask, - DMU_PMU_MAX_COUNTERS); - - if (event_added) { - pd2408_dmu_pmu_stop_all_counters(dmu_pmu); - pd2408_dmu_pmu_clear_all_counters(dmu_pmu); - pd2408_dmu_pmu_reset_timer(dmu_pmu); - pd2408_dmu_pmu_start_all_counters(dmu_pmu); - } -} - -void pd2408_dmu_pmu_disable(struct pmu *pmu) -{ - struct pd2408_dmu_pmu *dmu_pmu = to_pd2408_dmu_pmu(pmu); - int event_added; - - event_added = bitmap_weight(dmu_pmu->pmu_events.used_event_mask, - DMU_PMU_MAX_COUNTERS); - if (event_added && dmu_pmu->used_flag) { - pd2408_dmu_pmu_mask_all_irq(dmu_pmu); - pd2408_dmu_pmu_disable_axi_cmd_events(dmu_pmu); - } -} - -static const struct acpi_device_id pd2408_dmu_pmu_acpi_match[] = { - { "PHYT0069", }, - {}, -}; -MODULE_DEVICE_TABLE(acpi, pd2408_dmu_pmu_acpi_match); - -static irqreturn_t pd2408_dmu_pmu_overflow_handler(int irq, void *dev_id) -{ - struct pd2408_dmu_pmu *dmu_pmu = dev_id; - struct perf_event *event; - int idx; - unsigned long *used_mask = dmu_pmu->pmu_events.used_event_mask; - u32 timer_int_sta, axi_int_sta; - - timer_int_sta = readl(dmu_pmu->base + DMU_PMU_TIMER_INT_STA); - axi_int_sta = readl(dmu_pmu->base + DMU_PMU_AXI_MONITOR_INT_STA); - - if ((timer_int_sta + axi_int_sta) == 0) - return IRQ_NONE; - - if (timer_int_sta) - writel(0x1, dmu_pmu->base + DMU_PMU_TIMER_INT_CLEAR); - - if (axi_int_sta) - writel(axi_int_sta, dmu_pmu->base + DMU_PMU_AXI_MONITOR_INT_CLEAR); - - if (!dmu_pmu->used_flag) { - pd2408_dmu_pmu_mask_all_irq(dmu_pmu); - return IRQ_HANDLED; - } - - for_each_set_bit(idx, used_mask, DMU_PMU_MAX_COUNTERS) { - event = dmu_pmu->pmu_events.hw_events[idx]; - if (!event) - continue; - pd2408_dmu_pmu_event_update(event); - } - writel(ALL_EVENT_CLEAR_BIT, dmu_pmu->base + DMU_PMU_CLEAR_EVENT); - pd2408_dmu_pmu_start_all_counters(dmu_pmu); - - return IRQ_HANDLED; -} - -static int pd2408_dmu_pmu_init_irq(struct pd2408_dmu_pmu *dmu_pmu, - struct platform_device *pdev) -{ - int irq, ret; - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - ret = devm_request_irq(&pdev->dev, irq, - pd2408_dmu_pmu_overflow_handler, - IRQF_NOBALANCING | IRQF_NO_THREAD | IRQF_SHARED, - dev_name(&pdev->dev), dmu_pmu); - if (ret < 0) { - dev_err(&pdev->dev, "Fail to request IRQ:%d ret:%d\n", irq, - ret); - return ret; - } - - dmu_pmu->irq = irq; - - return 0; -} - -static int pd2408_dmu_pmu_init_data(struct platform_device *pdev, - struct pd2408_dmu_pmu *dmu_pmu) -{ - struct resource *res; - - if (device_property_read_u32(&pdev->dev, "phytium,ddr-id", - &dmu_pmu->dmu_id)) { - dev_err(&pdev->dev, "Can not read phytium,ddr-id!\n"); - return -EINVAL; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - dmu_pmu->base = devm_ioremap_resource(&pdev->dev, res); - - if (IS_ERR(dmu_pmu->base)) { - dev_err(&pdev->dev, - "ioremap failed for dmu_pmu base resource\n"); - return PTR_ERR(dmu_pmu->base); - } - - dmu_pmu->used_flag = true; - pd2408_dmu_pmu_mask_all_irq(dmu_pmu); - - return 0; -} - -static int pd2408_dmu_pmu_dev_probe(struct platform_device *pdev, - struct pd2408_dmu_pmu *dmu_pmu) -{ - int ret; - - ret = pd2408_dmu_pmu_init_data(pdev, dmu_pmu); - if (ret) - return ret; - - ret = pd2408_dmu_pmu_init_irq(dmu_pmu, pdev); - if (ret) - return ret; - - dmu_pmu->dev = &pdev->dev; - dmu_pmu->on_cpu = -1; - - return 0; -} - -static int pd2408_dmu_pmu_probe(struct platform_device *pdev) -{ - struct pd2408_dmu_pmu *dmu_pmu; - char *name; - int ret; - - dmu_pmu = devm_kzalloc(&pdev->dev, sizeof(*dmu_pmu), GFP_KERNEL); - if (!dmu_pmu) - return -ENOMEM; - - platform_set_drvdata(pdev, dmu_pmu); - - ret = pd2408_dmu_pmu_dev_probe(pdev, dmu_pmu); - if (ret) - return ret; - - ret = cpuhp_state_add_instance(pd2408_dmu_pmu_hp_state, - &dmu_pmu->node); - if (ret) { - dev_err(&pdev->dev, "Error %d registering hotplug;\n", ret); - return ret; - } - - name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "phyt_dmu%u_pmu", dmu_pmu->dmu_id); - dmu_pmu->pmu = (struct pmu) { - .name = name, - .module = THIS_MODULE, - .task_ctx_nr = perf_invalid_context, - .event_init = pd2408_dmu_pmu_event_init, - .pmu_enable = pd2408_dmu_pmu_enable, - .pmu_disable = pd2408_dmu_pmu_disable, - .add = pd2408_dmu_pmu_event_add, - .del = pd2408_dmu_pmu_event_del, - .start = pd2408_dmu_pmu_event_start, - .stop = pd2408_dmu_pmu_event_stop, - .read = pd2408_dmu_pmu_event_update, - .attr_groups = pd2408_dmu_pmu_attr_groups, - }; - - ret = perf_pmu_register(&dmu_pmu->pmu, name, -1); - if (ret) { - dev_err(dmu_pmu->dev, "DMU PMU register failed!\n"); - cpuhp_state_remove_instance_nocalls(pd2408_dmu_pmu_hp_state, - &dmu_pmu->node); - } - - pr_info("die_dmu%d_pmu on cpu%d.\n", dmu_pmu->dmu_id, dmu_pmu->on_cpu); - - return ret; -} - -static int pd2408_dmu_pmu_remove(struct platform_device *pdev) -{ - struct pd2408_dmu_pmu *dmu_pmu = platform_get_drvdata(pdev); - - pd2408_dmu_pmu_mask_all_irq(dmu_pmu); - perf_pmu_unregister(&dmu_pmu->pmu); - cpuhp_state_remove_instance_nocalls(pd2408_dmu_pmu_hp_state, - &dmu_pmu->node); - - return 0; -} - -static struct platform_driver pd2408_dmu_pmu_driver = { - .driver = { - .name = "pd2408_dmu_pmu", - .acpi_match_table = ACPI_PTR(pd2408_dmu_pmu_acpi_match), - .suppress_bind_attrs = true, - }, - .probe = pd2408_dmu_pmu_probe, - .remove = pd2408_dmu_pmu_remove, -}; - -int pd2408_dmu_pmu_online_cpu(unsigned int cpu, struct hlist_node *node) -{ - struct pd2408_dmu_pmu *dmu_pmu = - hlist_entry_safe(node, struct pd2408_dmu_pmu, node); - - if (dmu_pmu->on_cpu != -1) { - if (!cpumask_test_cpu(dmu_pmu->on_cpu, cpu_online_mask)) { - perf_pmu_migrate_context(&dmu_pmu->pmu, dmu_pmu->on_cpu, cpu); - dmu_pmu->on_cpu = cpu; - WARN_ON(irq_set_affinity_hint(dmu_pmu->irq, cpumask_of(cpu))); - } - return 0; - } - - dmu_pmu->on_cpu = cpu; - WARN_ON(irq_set_affinity_hint(dmu_pmu->irq, cpumask_of(cpu))); - - return 0; -} - -int pd2408_dmu_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node) -{ - struct pd2408_dmu_pmu *dmu_pmu = - hlist_entry_safe(node, struct pd2408_dmu_pmu, node); - unsigned int target; - cpumask_t available_cpus; - - if (dmu_pmu->on_cpu != cpu) - return 0; - - cpumask_andnot(&available_cpus, cpu_online_mask, cpumask_of(cpu)); - target = cpumask_last(&available_cpus); - - if (target >= nr_cpu_ids) { - dev_err(dmu_pmu->dev, "offline cpu%d with no target to migrate.\n", - cpu); - return 0; - } - - perf_pmu_migrate_context(&dmu_pmu->pmu, cpu, target); - WARN_ON(irq_set_affinity_hint(dmu_pmu->irq, cpumask_of(target))); - dmu_pmu->on_cpu = target; - - return 0; -} - -static int __init pd2408_dmu_pmu_module_init(void) -{ - int ret; - - pd2408_dmu_pmu_hp_state = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, - "perf/phytium/dmupmu:online", - pd2408_dmu_pmu_online_cpu, pd2408_dmu_pmu_offline_cpu); - if (pd2408_dmu_pmu_hp_state < 0) { - pr_err("DMU PMU: setup hotplug, pd2408_dmu_pmu_hp_state = %d\n", - pd2408_dmu_pmu_hp_state); - return pd2408_dmu_pmu_hp_state; - } - - ret = platform_driver_register(&pd2408_dmu_pmu_driver); - if (ret) - cpuhp_remove_multi_state( - pd2408_dmu_pmu_hp_state); - - return ret; -} -module_init(pd2408_dmu_pmu_module_init); - -static void __exit pd2408_dmu_pmu_module_exit(void) -{ - platform_driver_unregister(&pd2408_dmu_pmu_driver); - cpuhp_remove_multi_state(pd2408_dmu_pmu_hp_state); -} -module_exit(pd2408_dmu_pmu_module_exit); - -MODULE_DESCRIPTION("Phytium DMU PMU driver"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DMU_PERF_DRIVER_VERSION); -MODULE_AUTHOR("Hu Xianghua "); -MODULE_AUTHOR("Tan Rui "); - From c166ef17ce52fd4b2c2c61023c17695d5328a542 Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Fri, 8 May 2026 15:12:27 +0800 Subject: [PATCH 65/81] drivers/perf: phytium: Add driver support for PCIe Link Introduce driver support for the PCIe Link performance monitoring unit. The driver enables discovery and registration of PMU capabilities in PCIe Link devices. Signed-off-by: Zhang Fuxiang Signed-off-by: Fu Boyi Signed-off-by: Tan Rui Signed-off-by: Wang Yinfeng --- MAINTAINERS | 1 + drivers/perf/phytium/Kconfig | 8 + drivers/perf/phytium/Makefile | 1 + drivers/perf/phytium/phytium_pcie_link_pmu.c | 1066 ++++++++++++++++++ 4 files changed, 1076 insertions(+) create mode 100644 drivers/perf/phytium/phytium_pcie_link_pmu.c diff --git a/MAINTAINERS b/MAINTAINERS index 9cc7a14058b7f..ef22d236ac471 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17549,6 +17549,7 @@ F: Documentation/devicetree/bindings/usb/phytium,usb2.yaml F: Documentation/devicetree/bindings/usb/phytium.role-sw.yaml F: drivers/usb/phytium/* F: drivers/perf/phytium/phytium_ddr_pmu.c +F: drivers/perf/phytium/phytium_pcie_link_pmu.c F: drivers/usb/phytium/phytium_usb_v2* F: drivers/usb/typec/role-switch-phytium.c F: arch/arm64/boot/dts/phytium/* diff --git a/drivers/perf/phytium/Kconfig b/drivers/perf/phytium/Kconfig index 634afdabfc4a6..a4f100bc52eb8 100644 --- a/drivers/perf/phytium/Kconfig +++ b/drivers/perf/phytium/Kconfig @@ -37,5 +37,13 @@ config PHYT_PCIE_PMU Provides support for Phytium SoC PCIe Controller performance monitoring unit (PMU). +config PHYT_PCIE_LINK_PMU + tristate "Phytium SoC PCIE LINK PMU driver" + depends on ARCH_PHYTIUM && ACPI + default m + help + Provides support for Phytium SoC PCIe LINK performance + monitoring unit (PMU). + endif diff --git a/drivers/perf/phytium/Makefile b/drivers/perf/phytium/Makefile index af37afc6920c5..67612a54bfe3e 100644 --- a/drivers/perf/phytium/Makefile +++ b/drivers/perf/phytium/Makefile @@ -2,3 +2,4 @@ # obj-$(CONFIG_PHYT_DDR_PMU) += phytium_ddr_pmu.o obj-$(CONFIG_PHYT_PCIE_PMU) += phytium_pcie_pmu.o +obj-$(CONFIG_PHYT_PCIE_LINK_PMU) += phytium_pcie_link_pmu.o diff --git a/drivers/perf/phytium/phytium_pcie_link_pmu.c b/drivers/perf/phytium/phytium_pcie_link_pmu.c new file mode 100644 index 0000000000000..9243414c2fee6 --- /dev/null +++ b/drivers/perf/phytium/phytium_pcie_link_pmu.c @@ -0,0 +1,1066 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium SoC PCIe Link performance monitoring unit support + * + * Copyright (c) 2025, Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#undef pr_fmt +#define pr_fmt(fmt) "phytium_pcie_link_pmu: " fmt +#define PCIE_LINK_PERF_DRIVER_VERSION "1.0.0" +#define REG_ENABLE 0x000 +#define REG_SIG_CLEAR 0x00C + +// TLP/DLLP Package +#define REG_XTLH_XDLH_SOT_CNT 0x02c +#define REG_XTLH_XDLH_EOT_CNT 0x030 +#define REG_RDLH_RTLH_TLP_SOT_CNT 0x034 +#define REG_RDLH_RTLH_TLP_EOT_CNT 0x038 +#define REG_XDLH_XPLH_SDP_CNT 0x03c +#define REG_XDLH_XPLH_STP_CNT 0x040 +#define REG_XDLH_XPLH_EOT_CNT 0x044 +// TLP/PL/DLLP ERR +#define REG_ERRO_SIG_SEL 0x008 +#define REG_TLP_ERR_CNT 0x048 +#define REG_DLLP_ERR_CNT 0x04C +#define REG_PL_ERR_CNT 0x050 +#define REG_TLP_ERR_MASK GENMASK(23, 16) +#define REG_DLLP_ERR_MASK GENMASK(15, 8) +#define REG_PL_ERR_MASK GENMASK(7, 0) +#define TLP_ERR_IDX 0x07 +#define DLLP_ERR_IDX 0x08 +#define PL_ERR_IDX 0x09 +#define PL_DLLP_ERR_NUM 0x05 +#define TLP_ERR_NUM 0x08 + +// TLP TYPE +#define REG_TLP_TYPE_CNT_ENABLE 0x0e0 +#define REG_TLP_TYPE_CNT_CLEAR 0x0e4 + +#define REG_CONTROL_TIME_MODE_TYPE 0x0e8 +#define REG_START_TYPE_CNT 0x0ec +#define REG_TLP_TIMER_TYPE_CNT_0 0x0f4 +#define REG_TLP_TIMER_TYPE_CNT_1 0x0f8 + +#define REG_TX_MRD_CNT 0x19c +#define REG_TX_MRDLK_CNT 0x1a0 +#define REG_TX_MWR_CNT 0x1a4 +#define REG_TX_IORD_CNT 0x1a8 +#define REG_TX_IOWR_CNT 0x1ac +#define REG_TX_CFGRD0_CNT 0x1b0 +#define REG_TX_CFGWR0_CNT 0x1b4 +#define REG_TX_CFGRD1_CNT 0x1b8 +#define REG_TX_CFGWR1_CNT 0x1bc +#define REG_TX_MSG_CNT 0x1c0 +#define REG_TX_MSGD_CNT 0x1c4 +#define REG_TX_CPL_CNT 0x1c8 +#define REG_TX_CPLD_CNT 0x1cc +#define REG_TX_CPLLK_CNT 0x1d0 +#define REG_TX_CPLDLK_CNT 0x1d4 +#define REG_TX_FAA_REQ_CNT 0x1d8 +#define REG_TX_USA_REQ_CNT 0x1dc +#define REG_TX_CSA_REQ_CNT 0x1e0 +#define REG_TX_LOCAL_TLP_PREFIX_CNT 0x1e4 +#define REG_TX_ENDTOEND_TLP_PREFIX_CNT 0x1e8 +#define REG_TX_TCFGRD_CNT 0x1ec +#define REG_TX_TCFGWR_CNT 0x1f0 +#define REG_RX_MRD_CNT 0x1f8 +#define REG_RX_MRDLK_CNT 0x1fc +#define REG_RX_MWR_CNT 0x200 +#define REG_RX_IORD_CNT 0x204 +#define REG_RX_IOWR_CNT 0x208 +#define REG_RX_CFGRD0_CNT 0x20c +#define REG_RX_CFGWR0_CNT 0x210 +#define REG_RX_CFGRD1_CNT 0x214 +#define REG_RX_CFGWR1_CNT 0x218 +#define REG_RX_MSG_CNT 0x21c +#define REG_RX_MSGD_CNT 0x220 +#define REG_RX_CPL_CNT 0x224 +#define REG_RX_CPLD_CNT 0x228 +#define REG_RX_CPLLK_CNT 0x22c +#define REG_RX_CPLDLK_CNT 0x230 +#define REG_RX_FAA_REQ_CNT 0x234 +#define REG_RX_USA_REQ_CNT 0x238 +#define REG_RX_CSA_REQ_CNT 0x23c +#define REG_RX_LOCAL_TLP_PREFIX_CNT 0x240 +#define REG_RX_ENDTOEND_TLP_PREFIX_CNT 0x244 +#define REG_RX_TCFGRD_CNT 0x248 +#define REG_RX_TCFGWR_CNT 0x24c +// window time (start-top) +#define REG_WINDOW_TIME_TLP_RX_TYPE_CNT_1 0x254 +#define REG_WINDOW_TIME_TLP_RX_TYPE_CNT_0 0x258 +#define REG_WINDOW_TIME_TLP_TX_TYPE_CNT_1 0x25c +#define REG_WINDOW_TIME_TLP_TX_TYPE_CNT_0 0x260 + +// tlp performance +#define REG_ENABLE_TLP_PERFORMANCE 0x0fc +#define REG_CLEAR_TLP_PERFORMANCE 0x100 +#define REG_CONTROL_TIME_MODE_TLP_PERFORMANCE 0x104 +#define REG_START_TLP_PERFORMANCE 0x108 +#define REG_TIMER_TLP_PERFORMANCE_0 0x110 +#define REG_TIMER_TLP_PERFORMANCE_1 0x114 + +#define REG_WINDOW_TIME_TLP_PERFORMANCE_0 0x264 +#define REG_WINDOW_TIME_TLP_PERFORMANCE_1 0x268 +#define REG_TX_PAYLOAD_TOTAL_0 0x134 +#define REG_TX_PAYLOAD_TOTAL_1 0x138 +#define REG_TX_HEADER_TOTAL_0 0x13c +#define REG_TX_HEADER_TOTAL_1 0x140 +#define REG_RX_PAYLOAD_TOTAL_0 0x144 +#define REG_RX_PAYLOAD_TOTAL_1 0x148 +#define REG_RX_HEADER_TOTAL_0 0x14c +#define REG_RX_HEADER_TOTAL_1 0x150 + +// buffer usage +#define REG_ENABLE_BUFFER 0x118 +#define REG_CLEAR_BUFFER 0x11c +#define REG_CONTROL_TIME_MODE_BUFFER 0x120 +#define REG_START_BUFFER 0x124 +#define REG_TIMER_BUFFER_0 0x12c +#define REG_TIMER_BUFFER_1 0x130 + +#define REG_BUFFER_USAGE_LN0 0x158 +#define REG_BUFFER_USAGE_LN1 0x15c +#define REG_BUFFER_USAGE_LN2 0x160 +#define REG_BUFFER_USAGE_LN3 0x164 +#define REG_BUFFER_USAGE_LN4 0x168 +#define REG_BUFFER_USAGE_LN5 0x16c +#define REG_BUFFER_USAGE_LN6 0x170 +#define REG_BUFFER_USAGE_LN7 0x174 +#define REG_BUFFER_USAGE_LN8 0x178 +#define REG_BUFFER_USAGE_LN9 0x17c +#define REG_BUFFER_USAGE_LN10 0x180 +#define REG_BUFFER_USAGE_LN11 0x184 +#define REG_BUFFER_USAGE_LN12 0x18c +#define REG_BUFFER_USAGE_LN13 0x190 +#define REG_BUFFER_USAGE_LN14 0x194 +#define REG_BUFFER_USAGE_LN15 0x198 +#define REG_BUFFER_CURR_USAGE_MASK GENMASK(15, 8) +#define REG_BUFFER_PEAK_USAGE_MASK GENMASK(7, 0) + +#define TLP_TYPE_CLASSIFY_0 0x0a +#define TLP_TYPE_CLASSIFY_1 0x37 +#define TLP_PERFORM_TYPE_1 0x38 +#define BUFFER_USAGE_TYPE_0 0x3d +#define TLP_WINDOW_TIME_RX_TYPE 0x36 +#define TLP_WINDOW_TIME_TX_TYPE 0x37 +#define TLP_RX_HEADER_TOTAL_TYPE 0x3c + +#define PCIE_LINK_DEVCH 0xFE0 +#define PCIE_LINK_PMU_VER_BIT GENMASK(7, 0) +#define PHYTIUM_PCIE_LINK_MAX_COUNTERS 93 + +#define to_phytium_pcie_link_pmu(p) (container_of(p, struct phytium_pcie_link_pmu, pmu)) + +enum { + PCIEV1P0 = 0x01, + PCIEV1P5 = 0x02, + PCIEV2P0 = 0x03, +}; + +static int phytium_pcie_link_pmu_hp_state; + +struct phytium_pcie_link_pmu_hwevents { + struct perf_event *hw_events[PHYTIUM_PCIE_LINK_MAX_COUNTERS]; + DECLARE_BITMAP(used_mask_pcie_link, PHYTIUM_PCIE_LINK_MAX_COUNTERS); +}; + +struct phytium_pcie_link_pmu { + struct device *dev; + void __iomem *base; + struct pmu pmu; + struct phytium_pcie_link_pmu_hwevents pmu_events; + u32 die_id; + u32 pcie_id; + u32 pmu_id; + int on_cpu; + int ver; + struct hlist_node node; +}; + +#define EVENT_VALID(idx) ((idx >= 0) && (idx < PHYTIUM_PCIE_LINK_MAX_COUNTERS)) + +static const u32 pcie_link_counter_reg_offset[] = { + REG_XTLH_XDLH_SOT_CNT, + REG_XTLH_XDLH_EOT_CNT, + REG_RDLH_RTLH_TLP_SOT_CNT, + REG_RDLH_RTLH_TLP_EOT_CNT, + REG_XDLH_XPLH_SDP_CNT, + REG_XDLH_XPLH_STP_CNT, + REG_XDLH_XPLH_EOT_CNT, + + REG_TLP_ERR_CNT, + REG_DLLP_ERR_CNT, + REG_PL_ERR_CNT, + + REG_TX_MRD_CNT, + REG_TX_MRDLK_CNT, + REG_TX_MWR_CNT, + REG_TX_IORD_CNT, + REG_TX_IOWR_CNT, + REG_TX_CFGRD0_CNT, + REG_TX_CFGWR0_CNT, + REG_TX_CFGRD1_CNT, + REG_TX_CFGWR1_CNT, + REG_TX_MSG_CNT, + REG_TX_MSGD_CNT, + REG_TX_CPL_CNT, + REG_TX_CPLD_CNT, + REG_TX_CPLLK_CNT, + REG_TX_CPLDLK_CNT, + REG_TX_FAA_REQ_CNT, + REG_TX_USA_REQ_CNT, + REG_TX_CSA_REQ_CNT, + REG_TX_LOCAL_TLP_PREFIX_CNT, + REG_TX_ENDTOEND_TLP_PREFIX_CNT, + REG_TX_TCFGRD_CNT, + REG_TX_TCFGWR_CNT, + REG_RX_MRD_CNT, + REG_RX_MRDLK_CNT, + REG_RX_MWR_CNT, + REG_RX_IORD_CNT, + REG_RX_IOWR_CNT, + REG_RX_CFGRD0_CNT, + REG_RX_CFGWR0_CNT, + REG_RX_CFGRD1_CNT, + REG_RX_CFGWR1_CNT, + REG_RX_MSG_CNT, + REG_RX_MSGD_CNT, + REG_RX_CPL_CNT, + REG_RX_CPLD_CNT, + REG_RX_CPLLK_CNT, + REG_RX_CPLDLK_CNT, + REG_RX_FAA_REQ_CNT, + REG_RX_USA_REQ_CNT, + REG_RX_CSA_REQ_CNT, + REG_RX_LOCAL_TLP_PREFIX_CNT, + REG_RX_ENDTOEND_TLP_PREFIX_CNT, + REG_RX_TCFGRD_CNT, + REG_RX_TCFGWR_CNT, + REG_WINDOW_TIME_TLP_RX_TYPE_CNT_0, + REG_WINDOW_TIME_TLP_TX_TYPE_CNT_0, + + REG_WINDOW_TIME_TLP_PERFORMANCE_0, + REG_TX_PAYLOAD_TOTAL_0, + REG_TX_HEADER_TOTAL_0, + REG_RX_PAYLOAD_TOTAL_0, + REG_RX_HEADER_TOTAL_0, + + REG_BUFFER_USAGE_LN0, + REG_BUFFER_USAGE_LN0, + REG_BUFFER_USAGE_LN1, + REG_BUFFER_USAGE_LN1, + REG_BUFFER_USAGE_LN2, + REG_BUFFER_USAGE_LN2, + REG_BUFFER_USAGE_LN3, + REG_BUFFER_USAGE_LN3, + REG_BUFFER_USAGE_LN4, + REG_BUFFER_USAGE_LN4, + REG_BUFFER_USAGE_LN5, + REG_BUFFER_USAGE_LN5, + REG_BUFFER_USAGE_LN6, + REG_BUFFER_USAGE_LN6, + REG_BUFFER_USAGE_LN7, + REG_BUFFER_USAGE_LN7, + REG_BUFFER_USAGE_LN8, + REG_BUFFER_USAGE_LN8, + REG_BUFFER_USAGE_LN9, + REG_BUFFER_USAGE_LN9, + REG_BUFFER_USAGE_LN10, + REG_BUFFER_USAGE_LN10, + REG_BUFFER_USAGE_LN11, + REG_BUFFER_USAGE_LN11, + REG_BUFFER_USAGE_LN12, + REG_BUFFER_USAGE_LN12, + REG_BUFFER_USAGE_LN13, + REG_BUFFER_USAGE_LN13, + REG_BUFFER_USAGE_LN14, + REG_BUFFER_USAGE_LN14, + REG_BUFFER_USAGE_LN15, + REG_BUFFER_USAGE_LN15 +}; + +ssize_t phytium_pcie_link_pmu_format_sysfs_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct dev_ext_attribute *eattr; + + eattr = container_of(attr, struct dev_ext_attribute, attr); + + return sprintf(buf, "%s\n", (char *)eattr->var); +} + +ssize_t phytium_pcie_link_pmu_event_sysfs_show(struct device *dev, + struct device_attribute *attr, + char *page) +{ + struct dev_ext_attribute *eattr; + + eattr = container_of(attr, struct dev_ext_attribute, attr); + unsigned long var_val = (unsigned long)eattr->var; + + if (var_val >= 7 && var_val <= 9) + return sprintf(page, "config=0x%lx,err_sig=?\n", var_val); + + return sprintf(page, "config=0x%lx\n", var_val); +} + +static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct phytium_pcie_link_pmu *pcie_link_pmu = + to_phytium_pcie_link_pmu(dev_get_drvdata(dev)); + + return cpumap_print_to_pagebuf(true, buf, cpumask_of(pcie_link_pmu->on_cpu)); +} + +#define PHYTIUM_PMU_ATTR(_name, _func, _config) \ + (&((struct dev_ext_attribute[]){ \ + { __ATTR(_name, 0444, _func, NULL), (void *)_config } })[0] \ + .attr.attr) + +#define PHYTIUM_PCIE_LINK_PMU_FORMAT_ATTR(_name, _config) \ + PHYTIUM_PMU_ATTR(_name, phytium_pcie_link_pmu_format_sysfs_show, \ + (void *)_config) + +#define PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(_name, _config) \ + PHYTIUM_PMU_ATTR(_name, phytium_pcie_link_pmu_event_sysfs_show, \ + (unsigned long)_config) + +static struct attribute *phytium_pcie_link_pmu_events_attr[] = { + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_xtlh_xdlh_sot, 0x00), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_xtlh_xdlh_eot, 0x01), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_rdlh_rtlh_sot, 0x02), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_rdlh_rtlh_eot, 0x03), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(dllp_xdlh_xplh_sdp, 0x04), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_xdlh_xplh_stp, 0x05), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_dllp_xdlh_xplh_eot, 0x06), + + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_type_err, 0x07), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(dllp_type_err, 0x08), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(pl_type_err, 0x09), + + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_tx_mrd, 0x0a), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_tx_mrdlk, 0x0b), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_tx_mwr, 0x0c), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_tx_iord, 0x0d), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_tx_iowr, 0x0e), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_tx_cfgrd0, 0x0f), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_tx_cfgwr0, 0x10), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_tx_cfgrd1, 0x11), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_tx_cfgwr1, 0x12), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_tx_msg, 0x13), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_tx_msgd, 0x14), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_tx_cpl, 0x15), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_tx_cpld, 0x16), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_tx_cpllk, 0x17), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_tx_cpldlk, 0x18), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_tx_faa_req, 0x19), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_tx_usa_req, 0x1a), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_tx_csa_req, 0x1b), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_tx_loal_tlp_prefix, 0x1c), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_tx_endtoend_tlp_prefix, 0x1d), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_tx_tcfgrd, 0x1e), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_tx_tcfgwr, 0x1f), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_rx_mrd, 0x20), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_rx_mrdlk, 0x21), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_rx_mwr, 0x22), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_rx_iord, 0x23), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_rx_iowr, 0x24), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_rx_cfgrd0, 0x25), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_rx_cfgwr0, 0x26), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_rx_cfgrd1, 0x27), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_rx_cfgwr1, 0x28), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_rx_msg, 0x29), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_rx_msgd, 0x2a), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_rx_cpl, 0x2b), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_rx_cpld, 0x2c), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_rx_cpllk, 0x2d), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_rx_cpldlk, 0x2e), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_rx_faa_req, 0x2f), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_rx_usa_req, 0x30), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_rx_csa_req, 0x31), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_rx_local_tlp_prefix, 0x32), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_rx_endtoend_tlp_prefix, 0x33), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_rx_tcfgrd, 0x34), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_rx_tcfgwr, 0x35), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_window_time_rx_type, 0x36), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_window_time_tx_type, 0x37), + + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_window_time_performance, 0x38), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_tx_payload_total, 0x39), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_tx_header_total, 0x3a), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_rx_payload_total, 0x3b), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(tlp_rx_header_total, 0x3c), + + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_curr_usage_ln0, 0x3d), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_peak_usage_ln0, 0x3e), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_curr_usage_ln1, 0x3f), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_peak_usage_ln1, 0x40), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_curr_usage_ln2, 0x41), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_peak_usage_ln2, 0x42), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_curr_usage_ln3, 0x43), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_peak_usage_ln3, 0x44), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_curr_usage_ln4, 0x45), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_peak_usage_ln4, 0x46), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_curr_usage_ln5, 0x47), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_peak_usage_ln5, 0x48), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_curr_usage_ln6, 0x49), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_peak_usage_ln6, 0x4a), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_curr_usage_ln7, 0x4b), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_peak_usage_ln7, 0x4c), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_curr_usage_ln8, 0x4d), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_peak_usage_ln8, 0x4e), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_curr_usage_ln9, 0x4f), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_peak_usage_ln9, 0x50), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_curr_usage_ln10, 0x51), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_peak_usage_ln10, 0x52), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_curr_usage_ln11, 0x53), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_peak_usage_ln11, 0x54), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_curr_usage_ln12, 0x55), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_peak_usage_ln12, 0x56), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_curr_usage_ln13, 0x57), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_peak_usage_ln13, 0x58), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_curr_usage_ln14, 0x59), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_peak_usage_ln14, 0x5a), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_curr_usage_ln15, 0x5b), + PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR(buffer_peak_usage_ln15, 0x5c), + NULL +}; + +#define PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR_EXTRACTOR(_name, _config, _start, _end) \ +static inline u32 get_##_name(struct perf_event *event) \ +{ \ + return FIELD_GET(GENMASK_ULL(_end, _start), \ + event->attr._config); \ +} \ + +PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR_EXTRACTOR(eventid, config, 0, 6); +PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR_EXTRACTOR(err_sig, config, 7, 10); +PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR_EXTRACTOR(time_mode, config, 11, 11); +PHYTIUM_PCIE_LINK_PMU_EVENT_ATTR_EXTRACTOR(timer, config1, 0, 63); + +static struct attribute *phytium_pcie_link_pmu_format_attr[] = { + PHYTIUM_PCIE_LINK_PMU_FORMAT_ATTR(event, "config:0-6"), + PHYTIUM_PCIE_LINK_PMU_FORMAT_ATTR(err_sig, "config:7-10"), + PHYTIUM_PCIE_LINK_PMU_FORMAT_ATTR(time_mode, "config:11-11"), + PHYTIUM_PCIE_LINK_PMU_FORMAT_ATTR(timer, "config1:0-63"), + NULL, +}; + +static const struct attribute_group phytium_pcie_link_pmu_format_group = { + .name = "format", + .attrs = phytium_pcie_link_pmu_format_attr, +}; + +static const struct attribute_group phytium_pcie_link_pmu_events_group = { + .name = "events", + .attrs = phytium_pcie_link_pmu_events_attr, +}; + +static DEVICE_ATTR_RO(cpumask); + +static struct attribute *phytium_pcie_link_pmu_cpumask_attrs[] = { + &dev_attr_cpumask.attr, + NULL, +}; + +static const struct attribute_group phytium_pcie_link_pmu_cpumask_attr_group = { + .attrs = phytium_pcie_link_pmu_cpumask_attrs, +}; + +static const struct attribute_group *phytium_pcie_link_pmu_attr_groups[] = { + &phytium_pcie_link_pmu_format_group, + &phytium_pcie_link_pmu_events_group, + &phytium_pcie_link_pmu_cpumask_attr_group, + NULL, +}; + + +static const u32 phytium_pcie_link_err_reg_mask[] = { + REG_TLP_ERR_MASK, + REG_DLLP_ERR_MASK, + REG_PL_ERR_MASK +}; + +static u64 phytium_pcie_link_pmu_read_counter(struct phytium_pcie_link_pmu *pcie_link_pmu, + struct perf_event *event) +{ + u32 val64, val32_l, val32_h; + u32 idx = get_eventid(event); + u32 counter_offset = pcie_link_counter_reg_offset[idx]; + + if (!EVENT_VALID(idx)) { + dev_err(pcie_link_pmu->dev, "Unsupported event index:%d!\n", idx); + return 0; + } + + if (idx >= BUFFER_USAGE_TYPE_0) { + val32_l = readl(pcie_link_pmu->base + counter_offset); + if (idx % 2 == 0) + val32_l = (val32_l & REG_BUFFER_CURR_USAGE_MASK) >> 8; + else + val32_l = val32_l & REG_BUFFER_PEAK_USAGE_MASK; + val64 = (u64)val32_l; + } else if (idx == TLP_WINDOW_TIME_RX_TYPE || idx == TLP_WINDOW_TIME_TX_TYPE) { + val32_l = readl(pcie_link_pmu->base + counter_offset); + val32_h = readl(pcie_link_pmu->base + counter_offset - 4); + val64 = (u64)val32_h << 32 | (u64)val32_l; + } else if (idx > TLP_WINDOW_TIME_TX_TYPE && idx <= TLP_RX_HEADER_TOTAL_TYPE) { + val32_l = readl(pcie_link_pmu->base + counter_offset); + val32_h = readl(pcie_link_pmu->base + counter_offset + 4); + val64 = (u64)val32_h << 32 | (u64)val32_l; + } else { + val32_l = readl(pcie_link_pmu->base + counter_offset); + val64 = (u64)val32_l; + } + return val64; +} + +static void phytium_pcie_link_pmu_clear_all_counters(struct phytium_pcie_link_pmu *pcie_link_pmu) +{ + writel(0xffffffff, pcie_link_pmu->base + REG_SIG_CLEAR); + writel(0x00000000, pcie_link_pmu->base + REG_SIG_CLEAR); + + writel(0x3, pcie_link_pmu->base + REG_TLP_TYPE_CNT_CLEAR); + writel(0x0, pcie_link_pmu->base + REG_TLP_TYPE_CNT_CLEAR); + writel(0x1, pcie_link_pmu->base + REG_CLEAR_TLP_PERFORMANCE); + writel(0x0, pcie_link_pmu->base + REG_CLEAR_TLP_PERFORMANCE); + writel(0x1, pcie_link_pmu->base + REG_CLEAR_BUFFER); + writel(0x0, pcie_link_pmu->base + REG_CLEAR_BUFFER); +} + +static void phytium_pcie_link_pmu_enable_all_counters(struct phytium_pcie_link_pmu *pcie_link_pmu) +{ + writel(0xffffffff, pcie_link_pmu->base + REG_ENABLE); + writel(0x3, pcie_link_pmu->base + REG_TLP_TYPE_CNT_ENABLE); + writel(0x1, pcie_link_pmu->base + REG_ENABLE_TLP_PERFORMANCE); + writel(0x1, pcie_link_pmu->base + REG_ENABLE_BUFFER); +} + +static void phytium_pcie_link_pmu_disable_all_counters(struct phytium_pcie_link_pmu *pcie_link_pmu) +{ + writel(0x00000000, pcie_link_pmu->base + REG_ENABLE); + writel(0x00, pcie_link_pmu->base + REG_TLP_TYPE_CNT_ENABLE); + writel(0x0, pcie_link_pmu->base + REG_ENABLE_TLP_PERFORMANCE); + writel(0x0, pcie_link_pmu->base + REG_ENABLE_BUFFER); +} + +static void phytium_pcie_link_pmu_start_all_counters(struct phytium_pcie_link_pmu *pcie_link_pmu) +{ + writel(0x1, pcie_link_pmu->base + REG_START_TYPE_CNT); + writel(0x1, pcie_link_pmu->base + REG_START_TLP_PERFORMANCE); + writel(0x1, pcie_link_pmu->base + REG_START_BUFFER); +} + +static void phytium_pcie_link_pmu_stop_all_counters(struct phytium_pcie_link_pmu *pcie_link_pmu) +{ + writel(0x0, pcie_link_pmu->base + REG_START_TYPE_CNT); + writel(0x0, pcie_link_pmu->base + REG_START_TLP_PERFORMANCE); + writel(0x0, pcie_link_pmu->base + REG_START_BUFFER); +} + +static void phytium_pcie_link_pmu_set_timer(struct perf_event *event, u64 th_val) +{ + struct phytium_pcie_link_pmu *pcie_link_pmu = to_phytium_pcie_link_pmu(event->pmu); + + u32 val_l, val_h; + + val_l = th_val & 0xFFFFFFFF; + val_h = (th_val >> 32) & 0xFFFFFFFF; + u32 idx = get_eventid(event); + + if (idx >= TLP_TYPE_CLASSIFY_0 && idx <= TLP_TYPE_CLASSIFY_1) { + writel(0x1, pcie_link_pmu->base + REG_CONTROL_TIME_MODE_TYPE); + writel(val_l, pcie_link_pmu->base + REG_TLP_TIMER_TYPE_CNT_0); + writel(val_h, pcie_link_pmu->base + REG_TLP_TIMER_TYPE_CNT_1); + } else if (idx >= TLP_PERFORM_TYPE_1 && idx < BUFFER_USAGE_TYPE_0) { + writel(0x1, pcie_link_pmu->base + REG_CONTROL_TIME_MODE_TLP_PERFORMANCE); + writel(val_l, pcie_link_pmu->base + REG_TIMER_TLP_PERFORMANCE_0); + writel(val_h, pcie_link_pmu->base + REG_TIMER_TLP_PERFORMANCE_1); + } else if (idx >= BUFFER_USAGE_TYPE_0) { + writel(0x1, pcie_link_pmu->base + REG_CONTROL_TIME_MODE_BUFFER); + writel(val_l, pcie_link_pmu->base + REG_TIMER_BUFFER_0); + writel(val_h, pcie_link_pmu->base + REG_TIMER_BUFFER_0); + } +} + +static void phytium_pcie_link_pmu_reset_timer(struct perf_event *event) +{ + struct phytium_pcie_link_pmu *pcie_link_pmu = to_phytium_pcie_link_pmu(event->pmu); + + u32 idx = get_eventid(event); + + if (idx >= TLP_TYPE_CLASSIFY_0 && idx <= TLP_TYPE_CLASSIFY_1) { + writel(0x0, pcie_link_pmu->base + REG_CONTROL_TIME_MODE_TYPE); + writel(0xFFFFFFFF, pcie_link_pmu->base + REG_TLP_TIMER_TYPE_CNT_0); + writel(0xFFFFFFFF, pcie_link_pmu->base + REG_TLP_TIMER_TYPE_CNT_1); + } else if (idx >= TLP_PERFORM_TYPE_1 && idx < BUFFER_USAGE_TYPE_0) { + writel(0x0, pcie_link_pmu->base + REG_CONTROL_TIME_MODE_TLP_PERFORMANCE); + writel(0xFFFFFFFF, pcie_link_pmu->base + REG_TIMER_TLP_PERFORMANCE_0); + writel(0xFFFFFFFF, pcie_link_pmu->base + REG_TIMER_TLP_PERFORMANCE_1); + } else if (idx >= BUFFER_USAGE_TYPE_0) { + writel(0x0, pcie_link_pmu->base + REG_CONTROL_TIME_MODE_BUFFER); + writel(0xFFFFFFFF, pcie_link_pmu->base + REG_TIMER_BUFFER_0); + writel(0xFFFFFFFF, pcie_link_pmu->base + REG_TIMER_BUFFER_0); + } +} + +static int phytium_pcie_link_pmu_mark_event(struct perf_event *event) +{ + struct phytium_pcie_link_pmu *pcie_link_pmu = to_phytium_pcie_link_pmu(event->pmu); + unsigned long *used_mask; + int idx = get_eventid(event); + + used_mask = pcie_link_pmu->pmu_events.used_mask_pcie_link; + + if (test_bit(idx, used_mask)) + return -EAGAIN; + + set_bit(idx, used_mask); + + return idx; +} + +static void phytium_pcie_link_pmu_unmark_event(struct phytium_pcie_link_pmu *pcie_link_pmu, int idx) +{ + unsigned long *used_mask; + + if (!EVENT_VALID(idx)) { + dev_err(pcie_link_pmu->dev, "Unsupported event index:%d!\n", idx); + return; + } + + used_mask = pcie_link_pmu->pmu_events.used_mask_pcie_link; + + clear_bit(idx, used_mask); +} + +static bool phytium_pcie_link_pmu_is_reg_err_sig(u32 event_id, u32 err_id) +{ + if (event_id == PL_ERR_IDX || event_id == DLLP_ERR_IDX) { + if (err_id > PL_DLLP_ERR_NUM) + return false; + } else if (event_id == TLP_ERR_IDX) { + if (err_id > TLP_ERR_NUM) + return false; + } + return true; +} + +static void phytium_pcie_link_pmu_set_reg_err_sig(struct phytium_pcie_link_pmu *pcie_link_pmu, + u32 event_id, u32 err_id) +{ + int err_mask_id = event_id - 7; + + u32 err_reg_mask = phytium_pcie_link_err_reg_mask[err_mask_id]; + u32 val = readl(pcie_link_pmu->base + REG_ERRO_SIG_SEL); + + if (event_id == PL_ERR_IDX) + err_reg_mask &= (err_id << 16); + else if (event_id == DLLP_ERR_IDX) + err_reg_mask &= (err_id << 8); + else + err_reg_mask &= err_id; + val |= err_reg_mask; + writel(val, pcie_link_pmu->base + REG_ERRO_SIG_SEL); +} + +int phytium_pcie_link_pmu_event_init(struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; + struct phytium_pcie_link_pmu *pcie_link_pmu; + + if (event->attr.type != event->pmu->type) + return -ENOENT; + + if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK) + return -EOPNOTSUPP; + + pcie_link_pmu = to_phytium_pcie_link_pmu(event->pmu); + + if (event->cpu < 0) { + dev_warn(pcie_link_pmu->dev, "Can't provide per-task data!\n"); + return -EINVAL; + } + + if (pcie_link_pmu->on_cpu == -1) + return -EINVAL; + + hwc->idx = -1; + hwc->config_base = event->attr.config; + + event->cpu = pcie_link_pmu->on_cpu; + + return 0; +} + +void phytium_pcie_link_pmu_event_update(struct perf_event *event) +{ + struct phytium_pcie_link_pmu *pcie_link_pmu = to_phytium_pcie_link_pmu(event->pmu); + u64 delta; + + delta = phytium_pcie_link_pmu_read_counter(pcie_link_pmu, event); + local64_add(delta, &event->count); +} + +void phytium_pcie_link_pmu_event_start(struct perf_event *event, int flags) +{ + struct hw_perf_event *hwc = &event->hw; + + hwc->state = 0; + perf_event_update_userpage(event); +} + +void phytium_pcie_link_pmu_event_stop(struct perf_event *event, int flags) +{ + struct hw_perf_event *hwc = &event->hw; + + hwc->state |= PERF_HES_STOPPED; + + if (flags & PERF_EF_UPDATE) + phytium_pcie_link_pmu_event_update(event); +} + +int phytium_pcie_link_pmu_event_add(struct perf_event *event, int flags) +{ + struct phytium_pcie_link_pmu *pcie_link_pmu = to_phytium_pcie_link_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + int idx; + u32 event_id = get_eventid(event); + u32 err_id = get_err_sig(event); + u64 event_timer = get_timer(event); + int time_mode = get_time_mode(event); + + if (event_id >= TLP_ERR_IDX && event_id <= PL_ERR_IDX) { + if (phytium_pcie_link_pmu_is_reg_err_sig(event_id, err_id)) + phytium_pcie_link_pmu_set_reg_err_sig(pcie_link_pmu, event_id, err_id); + else { + dev_err(pcie_link_pmu->dev, "Register's err type is incorrect!\n"); + return -EINVAL; + } + } + + if (event_timer != 0 && time_mode != 0) + phytium_pcie_link_pmu_set_timer(event, event_timer); + + hwc->state |= PERF_HES_STOPPED; + + idx = phytium_pcie_link_pmu_mark_event(event); + if (idx < 0) + return idx; + + event->hw.idx = idx; + pcie_link_pmu->pmu_events.hw_events[idx] = event; + + return 0; +} + +void phytium_pcie_link_pmu_event_del(struct perf_event *event, int flags) +{ + struct phytium_pcie_link_pmu *pcie_link_pmu = to_phytium_pcie_link_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + u32 event_timer = get_timer(event); + + phytium_pcie_link_pmu_event_stop(event, PERF_EF_UPDATE); + if (event_timer != 0) + phytium_pcie_link_pmu_reset_timer(event); + + phytium_pcie_link_pmu_unmark_event(pcie_link_pmu, hwc->idx); + + perf_event_update_userpage(event); + pcie_link_pmu->pmu_events.hw_events[hwc->idx] = NULL; +} + +void phytium_pcie_link_pmu_enable(struct pmu *pmu) +{ + struct phytium_pcie_link_pmu *pcie_link_pmu = to_phytium_pcie_link_pmu(pmu); + int event_added = 0; + + event_added = bitmap_weight(pcie_link_pmu->pmu_events.used_mask_pcie_link, + PHYTIUM_PCIE_LINK_MAX_COUNTERS); + + if (event_added) { + phytium_pcie_link_pmu_clear_all_counters(pcie_link_pmu); + phytium_pcie_link_pmu_enable_all_counters(pcie_link_pmu); + phytium_pcie_link_pmu_start_all_counters(pcie_link_pmu); + } +} + +void phytium_pcie_link_pmu_disable(struct pmu *pmu) +{ + struct phytium_pcie_link_pmu *pcie_link_pmu = to_phytium_pcie_link_pmu(pmu); + int event_added = 0; + + event_added = bitmap_weight(pcie_link_pmu->pmu_events.used_mask_pcie_link, + PHYTIUM_PCIE_LINK_MAX_COUNTERS); + + if (event_added) + phytium_pcie_link_pmu_stop_all_counters(pcie_link_pmu); + else + phytium_pcie_link_pmu_disable_all_counters(pcie_link_pmu); +} + +static const struct acpi_device_id phytium_pcie_link_pmu_acpi_match[] = { + { "PHYT300E", }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, phytium_pcie_link_pmu_acpi_match); + +static int phytium_pcie_link_pmu_version(struct phytium_pcie_link_pmu *pcie_link_pmu, + struct platform_device *pdev) +{ + u32 pidr; + + pidr = readl(pcie_link_pmu->base + PCIE_LINK_DEVCH); + dev_info(pcie_link_pmu->dev, "PIDR=%#x,VER=%#lx.\n", pidr, (pidr & PCIE_LINK_PMU_VER_BIT)); + pidr &= PCIE_LINK_PMU_VER_BIT; + + if (pidr == 0x1) { + pcie_link_pmu->ver = PCIEV1P0; + } else { + dev_err(&pdev->dev, "The current driver does not support this device.\n"); + return -ENODEV; + } + + return 0; +} + +static int phytium_pcie_link_pmu_init_data(struct platform_device *pdev, + struct phytium_pcie_link_pmu *pcie_link_pmu) +{ + int ret; + struct resource *res; + + if (device_property_read_u32(&pdev->dev, "phytium,die-id", + &pcie_link_pmu->die_id)) { + dev_err(&pdev->dev, "Can not read phytium,die-id!\n"); + return -EINVAL; + } + if (device_property_read_u32(&pdev->dev, "phytium,pcie-id", + &pcie_link_pmu->pcie_id)) { + dev_err(&pdev->dev, "Can not read phytium,pcie-id!\n"); + return -EINVAL; + } + if (device_property_read_u32(&pdev->dev, "phytium,pmu-id", + &pcie_link_pmu->pmu_id)) { + dev_err(&pdev->dev, "Can not read pcie link pmu-id!\n"); +return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pcie_link_pmu->base = devm_ioremap_resource(&pdev->dev, res); + + if (IS_ERR(pcie_link_pmu->base)) { + dev_err(&pdev->dev, + "ioremap failed for pcie_link_pmu base resource\n"); + return PTR_ERR(pcie_link_pmu->base); + } + + ret = phytium_pcie_link_pmu_version(pcie_link_pmu, pdev); + if (ret) + return ret; + + return 0; +} + +static int phytium_pcie_link_pmu_dev_probe(struct platform_device *pdev, + struct phytium_pcie_link_pmu *pcie_link_pmu) +{ + int ret; + + ret = phytium_pcie_link_pmu_init_data(pdev, pcie_link_pmu); + if (ret) + return ret; + + pcie_link_pmu->dev = &pdev->dev; + pcie_link_pmu->on_cpu = -1; + + return 0; +} + +static int phytium_pcie_link_pmu_probe(struct platform_device *pdev) +{ + struct phytium_pcie_link_pmu *pcie_link_pmu; + char *name; + int ret; + + pcie_link_pmu = devm_kzalloc(&pdev->dev, sizeof(*pcie_link_pmu), GFP_KERNEL); + if (!pcie_link_pmu) + return -ENOMEM; + + platform_set_drvdata(pdev, pcie_link_pmu); + + ret = phytium_pcie_link_pmu_dev_probe(pdev, pcie_link_pmu); + if (ret) + return ret; + + ret = cpuhp_state_add_instance(phytium_pcie_link_pmu_hp_state, + &pcie_link_pmu->node); + if (ret) { + dev_err(&pdev->dev, "Error %d registering hotplug;\n", ret); + return ret; + } + + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "phyt%u_pcie%u_link_pmu%u", + pcie_link_pmu->die_id, pcie_link_pmu->pcie_id, + pcie_link_pmu->pmu_id); + pcie_link_pmu->pmu = (struct pmu){ + .name = name, + .module = THIS_MODULE, + .task_ctx_nr = perf_invalid_context, + .event_init = phytium_pcie_link_pmu_event_init, + .pmu_enable = phytium_pcie_link_pmu_enable, + .pmu_disable = phytium_pcie_link_pmu_disable, + .add = phytium_pcie_link_pmu_event_add, + .del = phytium_pcie_link_pmu_event_del, + .start = phytium_pcie_link_pmu_event_start, + .stop = phytium_pcie_link_pmu_event_stop, + .read = phytium_pcie_link_pmu_event_update, + .attr_groups = phytium_pcie_link_pmu_attr_groups, + .capabilities = PERF_PMU_CAP_NO_EXCLUDE, + }; + + ret = perf_pmu_register(&pcie_link_pmu->pmu, name, -1); + if (ret) { + dev_err(pcie_link_pmu->dev, "PCIE LINK PMU register failed!\n"); + cpuhp_state_remove_instance_nocalls(phytium_pcie_link_pmu_hp_state, + &pcie_link_pmu->node); + } + + dev_info(pcie_link_pmu->dev, "%s on cpu%d.\n", name, pcie_link_pmu->on_cpu); + return ret; +} + +static int phytium_pcie_link_pmu_remove(struct platform_device *pdev) +{ + struct phytium_pcie_link_pmu *pcie_link_pmu = platform_get_drvdata(pdev); + + phytium_pcie_link_pmu_disable_all_counters(pcie_link_pmu); + + perf_pmu_unregister(&pcie_link_pmu->pmu); + cpuhp_state_remove_instance_nocalls(phytium_pcie_link_pmu_hp_state, + &pcie_link_pmu->node); + + return 0; +} + +static struct platform_driver phytium_pcie_link_pmu_driver = { + .driver = { + .name = "phytium_pcie_link_pmu", + .acpi_match_table = ACPI_PTR(phytium_pcie_link_pmu_acpi_match), + .suppress_bind_attrs = true, + }, + .probe = phytium_pcie_link_pmu_probe, + .remove = phytium_pcie_link_pmu_remove, +}; + +int phytium_pcie_link_pmu_online_cpu(unsigned int cpu, struct hlist_node *node) +{ + struct phytium_pcie_link_pmu *pcie_link_pmu = + hlist_entry_safe(node, struct phytium_pcie_link_pmu, node); + + if (!cpumask_test_cpu(cpu, cpumask_of_node(pcie_link_pmu->die_id))) + return 0; + + if (pcie_link_pmu->on_cpu != -1) { + if (!cpumask_test_cpu(pcie_link_pmu->on_cpu, + cpumask_of_node(pcie_link_pmu->die_id))) { + perf_pmu_migrate_context(&pcie_link_pmu->pmu, pcie_link_pmu->on_cpu, cpu); + pcie_link_pmu->on_cpu = cpu; + } + return 0; + } + + pcie_link_pmu->on_cpu = cpu; + + return 0; +} + +int phytium_pcie_link_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node) +{ + struct phytium_pcie_link_pmu *pcie_link_pmu = + hlist_entry_safe(node, struct phytium_pcie_link_pmu, node); + unsigned int target; + cpumask_t available_cpus; + + if (pcie_link_pmu->on_cpu != cpu) + return 0; + + if (cpumask_and(&available_cpus, cpumask_of_node(pcie_link_pmu->die_id), cpu_online_mask) && + cpumask_andnot(&available_cpus, &available_cpus, cpumask_of(cpu))) + target = cpumask_last(&available_cpus); + else { + cpumask_andnot(&available_cpus, cpu_online_mask, cpumask_of(cpu)); + target = cpumask_last(&available_cpus); + } + + if (target >= nr_cpu_ids) { + dev_err(pcie_link_pmu->dev, "offline cpu%d with no target to migrate.\n", cpu); + return 0; + } + + perf_pmu_migrate_context(&pcie_link_pmu->pmu, cpu, target); + pcie_link_pmu->on_cpu = target; + + return 0; +} + +static int __init phytium_pcie_link_pmu_module_init(void) +{ + int ret; + + phytium_pcie_link_pmu_hp_state = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, + "perf/phytium/pcielinkpmu:online", + phytium_pcie_link_pmu_online_cpu, + phytium_pcie_link_pmu_offline_cpu); + if (phytium_pcie_link_pmu_hp_state < 0) { + pr_err("PCIE LINK PMU: setup hotplug, phytium_pcie_link_pmu_hp_state = %d\n", + phytium_pcie_link_pmu_hp_state); + return phytium_pcie_link_pmu_hp_state; + } + + ret = platform_driver_register(&phytium_pcie_link_pmu_driver); + if (ret) + cpuhp_remove_multi_state(phytium_pcie_link_pmu_hp_state); + + return ret; +} +module_init(phytium_pcie_link_pmu_module_init); + +static void __exit phytium_pcie_link_pmu_module_exit(void) +{ + platform_driver_unregister(&phytium_pcie_link_pmu_driver); + cpuhp_remove_multi_state(phytium_pcie_link_pmu_hp_state); +} +module_exit(phytium_pcie_link_pmu_module_exit); + +MODULE_DESCRIPTION("Phytium PCIE LINK PMU driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(PCIE_LINK_PERF_DRIVER_VERSION); +MODULE_AUTHOR("Fu Boyi "); From 84a07c2c9fcee80058315f929b5bcb5624232da7 Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Fri, 8 May 2026 15:18:54 +0800 Subject: [PATCH 66/81] drivers/perf: phytium: Add driver support for MSI PMU Introduce driver support for the MSI performance monitoring unit. The driver enables discovery and registration of PMU capabilities in MSI devices. Signed-off-by: Zhang Fuxiang Signed-off-by: Fu Boyi Signed-off-by: Tan Rui Signed-off-by: Wang Yinfeng --- MAINTAINERS | 1 + drivers/perf/phytium/Kconfig | 8 + drivers/perf/phytium/Makefile | 1 + drivers/perf/phytium/phytium_msi_pmu.c | 1206 ++++++++++++++++++++++++ 4 files changed, 1216 insertions(+) create mode 100644 drivers/perf/phytium/phytium_msi_pmu.c diff --git a/MAINTAINERS b/MAINTAINERS index ef22d236ac471..6e154688d6bf1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17549,6 +17549,7 @@ F: Documentation/devicetree/bindings/usb/phytium,usb2.yaml F: Documentation/devicetree/bindings/usb/phytium.role-sw.yaml F: drivers/usb/phytium/* F: drivers/perf/phytium/phytium_ddr_pmu.c +F: drivers/perf/phytium/phytium_msi_pmu.c F: drivers/perf/phytium/phytium_pcie_link_pmu.c F: drivers/usb/phytium/phytium_usb_v2* F: drivers/usb/typec/role-switch-phytium.c diff --git a/drivers/perf/phytium/Kconfig b/drivers/perf/phytium/Kconfig index a4f100bc52eb8..0150ac7bec8b3 100644 --- a/drivers/perf/phytium/Kconfig +++ b/drivers/perf/phytium/Kconfig @@ -45,5 +45,13 @@ config PHYT_PCIE_LINK_PMU Provides support for Phytium SoC PCIe LINK performance monitoring unit (PMU). +config PHYT_MSI_PMU + tristate "Phytium SoC MSI PMU driver" + depends on ARCH_PHYTIUM && ACPI + default m + help + Provides support for Phytium SoC MSI performance + monitoring unit (PMU). + endif diff --git a/drivers/perf/phytium/Makefile b/drivers/perf/phytium/Makefile index 67612a54bfe3e..a13af49ee95ce 100644 --- a/drivers/perf/phytium/Makefile +++ b/drivers/perf/phytium/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_PHYT_DDR_PMU) += phytium_ddr_pmu.o obj-$(CONFIG_PHYT_PCIE_PMU) += phytium_pcie_pmu.o obj-$(CONFIG_PHYT_PCIE_LINK_PMU) += phytium_pcie_link_pmu.o +obj-$(CONFIG_PHYT_MSI_PMU) += phytium_msi_pmu.o diff --git a/drivers/perf/phytium/phytium_msi_pmu.c b/drivers/perf/phytium/phytium_msi_pmu.c new file mode 100644 index 0000000000000..5b73a35ad6bbc --- /dev/null +++ b/drivers/perf/phytium/phytium_msi_pmu.c @@ -0,0 +1,1206 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium SoC MSI performance monitoring unit support + * + * Copyright (c) 2025, Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef pr_fmt +#define pr_fmt(fmt) "phytium_msi_pmu: " fmt + +#define MISC_PERF_DRIVER_VERSION "1.0.0" + +#define PHYTIUM_MISC_MAX_COUNTERS 15 +#define PHYTIUM_MSI_PMU_EVENT_MASK 0xF + +#define MISC_MSI_MON_CLR_REG 0x1F8 +#define MISC_MSI_MON_CLKEN_REG 0x1FC +#define MISC_MSI_MON_STOP_REG 0x200 +#define MISC_MSI_MON_START_REG 0x204 +#define MISC_MSI_MON_OPT_MASK GENMASK(2, 0) +#define MISC_PCIE0_MSI_MON_OPT_BIT BIT(2) +#define MISC_PCIE1_MSI_MON_OPT_BIT BIT(1) +#define MISC_PCIE2_MSI_MON_OPT_BIT BIT(0) + +#define MISC_PCIE0_MON_SET_TIME_H 0x208 +#define MISC_PCIE0_MON_SET_TIME_L 0x20C +#define MISC_PCIE1_MON_SET_TIME_H 0x210 +#define MISC_PCIE1_MON_SET_TIME_L 0x214 +#define MISC_PCIE2_MON_SET_TIME_H 0x218 +#define MISC_PCIE2_MON_SET_TIME_L 0x21C + +#define MISC_MSI_MON_MODE 0x220 +#define MISC_MSI_MON_MODE_MASK 0x15 +#define MISC_PCIE0_MSI_BYREQID_MODE_BIT BIT(0) +#define MISC_PCIE1_MSI_BYREQID_MODE_BIT BIT(2) +#define MISC_PCIE2_MSI_BYREQID_MODE_BIT BIT(4) +#define MISC_MSI_MON_TRIG_MODE_MASK 0x2A +#define MISC_PCIE0_MSI_TRIG_MODE_BIT BIT(1) +#define MISC_PCIE1_MSI_TRIG_MODE_BIT BIT(3) +#define MISC_PCIE2_MSI_TRIG_MODE_BIT BIT(5) + +#define MISC_PCIE0_MSI_REG_ID_CFG_REG_23 0x250 +#define MISC_PCIE0_MSI_REG_ID_CFG_REG_01 0x254 +#define MISC_PCIE1_MSI_REG_ID_CFG_REG_23 0x258 +#define MISC_PCIE1_MSI_REG_ID_CFG_REG_01 0x25C +#define MISC_PCIE2_MSI_REG_ID_CFG_REG_23 0x260 +#define MISC_PCIE2_MSI_REG_ID_CFG_REG_01 0x264 +#define MISC_MSI_MON_STATE 0x268 + +#define MISC_MSI_MON_STATE_STOP 0x284 +#define MISC_PCIE0_MON_STATE_STOP_MASK GENMASK(13, 12) +#define MISC_PCIE1_MON_STATE_STOP_MASK GENMASK(5, 4) +#define MISC_PCIE2_MON_STATE_STOP_MASK GENMASK(1, 0) +#define MISC_MSI_MON_OVFL_STATE_MASK 0x2 +#define MISC_MSI_MON_COUNT_FULL_MASK 0x2022 + +#define MISC_PCIE0_REQ_ID_RECORD_23 0x224 +#define MISC_PCIE0_REQ_ID_RECORD_01 0x228 +#define MISC_PCIE0_REQ_ID_CNT 0x22C + +#define MISC_PCIE1_REQ_ID_RECORD_23 0x230 +#define MISC_PCIE1_REQ_ID_RECORD_01 0x234 +#define MISC_PCIE1_REQ_ID_CNT 0x238 + +#define MISC_PCIE2_REQ_ID_RECORD_23 0x23C +#define MISC_PCIE2_REQ_ID_RECORD_01 0x240 +#define MISC_PCIE2_REQ_ID_CNT 0x24C + +#define MISC_PCIE0_MON_TPOINT_END_H 0x26C +#define MISC_PCIE0_MON_TPOINT_END_L 0x270 +#define MISC_PCIE1_MON_TPOINT_END_H 0x274 +#define MISC_PCIE1_MON_TPOINT_END_L 0x278 +#define MISC_PCIE2_MON_TPOINT_END_H 0x27C +#define MISC_PCIE2_MON_TPOINT_END_L 0x280 + +#define MISC_MON_PIDR0 0xFE0 +#define MISC_PMU_VER_BIT GENMASK(7, 0) +#define MISC_PMU_PART_BIT GENMASK(11, 8) + +#define to_phytium_msi_pmu(p) (container_of(p, struct phytium_msi_pmu, pmu)) + +static int phytium_msi_pmu_hp_state; + +enum { + MISCV1P0 = 0x01, +}; + +struct phytium_msi_pmu_hwevents { + struct perf_event *hw_events[PHYTIUM_MISC_MAX_COUNTERS]; + DECLARE_BITMAP(used_mask, PHYTIUM_MISC_MAX_COUNTERS); + DECLARE_BITMAP(dev_mask, 3); +}; + +struct phytium_msi_pmu_event_cfg { + int by_timer; + int trig_mode; + int pcie0_by_reqid; + int pcie1_by_reqid; + int pcie2_by_reqid; + int pcie0_reqid0; + int pcie0_reqid1; + int pcie0_reqid2; + int pcie0_reqid3; + int pcie1_reqid0; + int pcie1_reqid1; + int pcie1_reqid2; + int pcie1_reqid3; + int pcie2_reqid0; + int pcie2_reqid1; + int pcie2_reqid2; + int pcie2_reqid3; + u64 timer; +}; + +struct phytium_msi_pmu { + struct device *dev; + void __iomem *base; + struct pmu pmu; + struct phytium_msi_pmu_hwevents pmu_events; + struct phytium_msi_pmu_event_cfg event_cfg; + struct hlist_node node; + u32 die_id; + u32 ver; + int on_cpu; + int irq; +}; + +#define GET_MISC_EVENTID(hwc) (hwc->config_base & PHYTIUM_MSI_PMU_EVENT_MASK) +#define EVENT_VALID(idx) ((idx >= 0) && (idx < PHYTIUM_MISC_MAX_COUNTERS)) + +static const u32 misc_counter_reg_offset[] = { + MISC_PCIE0_REQ_ID_CNT, MISC_PCIE0_REQ_ID_CNT, MISC_PCIE0_REQ_ID_CNT, + MISC_PCIE0_REQ_ID_CNT, MISC_PCIE0_MON_TPOINT_END_H, + MISC_PCIE1_REQ_ID_CNT, MISC_PCIE1_REQ_ID_CNT, MISC_PCIE1_REQ_ID_CNT, + MISC_PCIE1_REQ_ID_CNT, MISC_PCIE1_MON_TPOINT_END_H, + MISC_PCIE2_REQ_ID_CNT, MISC_PCIE2_REQ_ID_CNT, MISC_PCIE2_REQ_ID_CNT, + MISC_PCIE2_REQ_ID_CNT, MISC_PCIE2_MON_TPOINT_END_H +}; + +static const u32 misc_reqid_record_reg_offset[] = { + MISC_PCIE0_REQ_ID_RECORD_01, MISC_PCIE0_REQ_ID_RECORD_01, + MISC_PCIE0_REQ_ID_RECORD_23, MISC_PCIE0_REQ_ID_RECORD_23, 0, + MISC_PCIE1_REQ_ID_RECORD_01, MISC_PCIE1_REQ_ID_RECORD_01, + MISC_PCIE2_REQ_ID_RECORD_01, MISC_PCIE2_REQ_ID_RECORD_01, 0, + MISC_PCIE1_REQ_ID_RECORD_23, MISC_PCIE1_REQ_ID_RECORD_23, + MISC_PCIE2_REQ_ID_RECORD_23, MISC_PCIE2_REQ_ID_RECORD_23, 0 +}; + +static const unsigned long pcie_dev_msi_mon_stop_mask[] = { + MISC_PCIE0_MON_STATE_STOP_MASK, + MISC_PCIE1_MON_STATE_STOP_MASK, + MISC_PCIE2_MON_STATE_STOP_MASK +}; + +static const unsigned long pcie_dev_msi_mon_opt_bits[] = { + MISC_PCIE0_MSI_MON_OPT_BIT, + MISC_PCIE1_MSI_MON_OPT_BIT, + MISC_PCIE2_MSI_MON_OPT_BIT +}; + +ssize_t phytium_msi_pmu_format_sysfs_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct dev_ext_attribute *eattr; + + eattr = container_of(attr, struct dev_ext_attribute, attr); + return sprintf(buf, "%s\n", (char *)eattr->var); +} + +ssize_t phytium_msi_pmu_event_sysfs_show(struct device *dev, struct device_attribute *attr, + char *page) +{ + struct dev_ext_attribute *eattr; + + eattr = container_of(attr, struct dev_ext_attribute, attr); + return sprintf(page, "config=0x%lx\n", (unsigned long)eattr->var); +} + +static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct phytium_msi_pmu *misc_pmu = + to_phytium_msi_pmu(dev_get_drvdata(dev)); + + return cpumap_print_to_pagebuf(true, buf, cpumask_of(misc_pmu->on_cpu)); +} + +#define PHYTIUM_PMU_ATTR(_name, _func, _config) \ + (&((struct dev_ext_attribute[]) \ + {{__ATTR(_name, 0444, _func, NULL), (void *)_config}})[0] \ + .attr.attr) + +#define PHYTIUM_MSI_PMU_FORMAT_ATTR(_name, _config) \ + PHYTIUM_PMU_ATTR(_name, phytium_msi_pmu_format_sysfs_show, (void *)_config) + +#define PHYTIUM_MSI_PMU_EVENT_ATTR(_name, _config) \ + PHYTIUM_PMU_ATTR(_name, phytium_msi_pmu_event_sysfs_show, (unsigned long)_config) + +#define MISC_PMU_EVENT_ATTR_EXTRACTOR(_name, _config, _start, _end) \ + static inline int misc_pmu_get_##_name(struct perf_event *event) \ + { \ + return FIELD_GET(GENMASK_ULL(_end, _start), \ + event->attr._config); \ + } + +MISC_PMU_EVENT_ATTR_EXTRACTOR(event, config, 0, 3); +MISC_PMU_EVENT_ATTR_EXTRACTOR(trig_mode, config, 4, 4); +MISC_PMU_EVENT_ATTR_EXTRACTOR(by_reqid, config, 5, 5); +MISC_PMU_EVENT_ATTR_EXTRACTOR(timer, config2, 0, 63); +MISC_PMU_EVENT_ATTR_EXTRACTOR(reqid0, config1, 0, 15); +MISC_PMU_EVENT_ATTR_EXTRACTOR(reqid1, config1, 16, 31); +MISC_PMU_EVENT_ATTR_EXTRACTOR(reqid2, config1, 32, 47); +MISC_PMU_EVENT_ATTR_EXTRACTOR(reqid3, config1, 48, 63); + +static struct attribute *PHYTIUM_MSI_PMU_FORMAT_ATTR[] = { + PHYTIUM_MSI_PMU_FORMAT_ATTR(event, "config:0-3"), + PHYTIUM_MSI_PMU_FORMAT_ATTR(trig_mode, "config:4-4"), + PHYTIUM_MSI_PMU_FORMAT_ATTR(by_reqid, "config:5-5"), + PHYTIUM_MSI_PMU_FORMAT_ATTR(timer, "config2:0-63"), + PHYTIUM_MSI_PMU_FORMAT_ATTR(reqid0, "config1:0-15"), + PHYTIUM_MSI_PMU_FORMAT_ATTR(reqid1, "config1:16-31"), + PHYTIUM_MSI_PMU_FORMAT_ATTR(reqid2, "config1:32-47"), + PHYTIUM_MSI_PMU_FORMAT_ATTR(reqid3, "config1:48-63"), + NULL, +}; + +static const struct attribute_group phytium_msi_pmu_format_group = { + .name = "format", + .attrs = PHYTIUM_MSI_PMU_FORMAT_ATTR, +}; + +static struct attribute *phytium_msi_pmu_events_attr[] = { + PHYTIUM_MSI_PMU_EVENT_ATTR(pcie0_msi_cnt0, 0x00), + PHYTIUM_MSI_PMU_EVENT_ATTR(pcie0_msi_cnt1, 0x01), + PHYTIUM_MSI_PMU_EVENT_ATTR(pcie0_msi_cnt2, 0x02), + PHYTIUM_MSI_PMU_EVENT_ATTR(pcie0_msi_cnt3, 0x03), + PHYTIUM_MSI_PMU_EVENT_ATTR(pcie0_msi_cycles, 0x4), + + PHYTIUM_MSI_PMU_EVENT_ATTR(pcie1_msi_cnt0, 0x05), + PHYTIUM_MSI_PMU_EVENT_ATTR(pcie1_msi_cnt1, 0x06), + PHYTIUM_MSI_PMU_EVENT_ATTR(pcie1_msi_cnt2, 0x07), + PHYTIUM_MSI_PMU_EVENT_ATTR(pcie1_msi_cnt3, 0x08), + PHYTIUM_MSI_PMU_EVENT_ATTR(pcie1_msi_cycles, 0x09), + + PHYTIUM_MSI_PMU_EVENT_ATTR(pcie2_msi_cnt0, 0x0a), + PHYTIUM_MSI_PMU_EVENT_ATTR(pcie2_msi_cnt1, 0x0b), + PHYTIUM_MSI_PMU_EVENT_ATTR(pcie2_msi_cnt2, 0x0c), + PHYTIUM_MSI_PMU_EVENT_ATTR(pcie2_msi_cnt3, 0x0d), + PHYTIUM_MSI_PMU_EVENT_ATTR(pcie2_msi_cycles, 0x0e), + + NULL, +}; + +static const struct attribute_group phytium_msi_pmu_events_group = { + .name = "events", + .attrs = phytium_msi_pmu_events_attr, +}; + +static DEVICE_ATTR_RO(cpumask); + +static struct attribute *phytium_msi_pmu_cpumask_attrs[] = { + &dev_attr_cpumask.attr, + NULL, +}; + +static const struct attribute_group phytium_msi_pmu_cpumask_attr_group = { + .attrs = phytium_msi_pmu_cpumask_attrs, +}; + +static const struct attribute_group *phytium_msi_pmu_attr_groups[] = { + &phytium_msi_pmu_format_group, + &phytium_msi_pmu_events_group, + &phytium_msi_pmu_cpumask_attr_group, + NULL, +}; + +int phytium_msi_pmu_get_event_type(struct perf_event *event) +{ + int idx, event_type; + + idx = (int)misc_pmu_get_event(event); + event_type = (int)(idx / 5); + + return event_type; +} + +static void phytium_msi_pmu_enable_clk(struct phytium_msi_pmu *misc_pmu) +{ + u32 val; + + val = readl(misc_pmu->base + MISC_MSI_MON_CLKEN_REG); + val |= MISC_MSI_MON_OPT_MASK; + writel(val, misc_pmu->base + MISC_MSI_MON_CLKEN_REG); +} + +static void phytium_msi_pmu_disable_clk(struct phytium_msi_pmu *misc_pmu) +{ + writel(0, misc_pmu->base + MISC_MSI_MON_CLKEN_REG); +} + +static void phytium_msi_pmu_start_counters(struct phytium_msi_pmu *misc_pmu, u32 st_val) +{ + writel(st_val, misc_pmu->base + MISC_MSI_MON_START_REG); +} + +static void phytium_msi_pmu_start_all_counters(struct phytium_msi_pmu *misc_pmu) +{ + u32 val; + + val = MISC_MSI_MON_OPT_MASK; + writel(val, misc_pmu->base + MISC_MSI_MON_START_REG); +} + +static void phytium_msi_pmu_stop_all_counters(struct phytium_msi_pmu *misc_pmu) +{ + u32 val; + + val = MISC_PCIE0_MSI_MON_OPT_BIT | MISC_PCIE1_MSI_MON_OPT_BIT | MISC_PCIE2_MSI_MON_OPT_BIT; + writel(val, misc_pmu->base + MISC_MSI_MON_STOP_REG); +} + +static void phytium_msi_pmu_clean_mon_mode(struct phytium_msi_pmu *misc_pmu) +{ + writel(0, misc_pmu->base + MISC_MSI_MON_MODE); +} + +static void phytium_msi_pmu_set_trig_mode(struct phytium_msi_pmu *misc_pmu, + u32 trig_mode) +{ + u32 val; + + val = readl(misc_pmu->base + MISC_MSI_MON_MODE); + if (trig_mode) + val |= MISC_MSI_MON_TRIG_MODE_MASK; + else + val &= MISC_MSI_MON_MODE_MASK; + + writel(val, misc_pmu->base + MISC_MSI_MON_MODE); +} + +// config by_reqid +static void phytium_msi_pmu_by_reqid_mode(struct phytium_msi_pmu *misc_pmu, + u32 opt_bit, u32 by_reqid) +{ + u32 val, mask; + + if (by_reqid) { + mask = MISC_MSI_MON_MODE_MASK & opt_bit; + val = readl(misc_pmu->base + MISC_MSI_MON_MODE); + val |= mask; + writel(val, misc_pmu->base + MISC_MSI_MON_MODE); + } else { + val = readl(misc_pmu->base + MISC_MSI_MON_MODE); + val &= ~opt_bit; + writel(val, misc_pmu->base + MISC_MSI_MON_MODE); + } +} + +// config 4 reqids +static void phytium_msi_pmu_set_req_id(struct phytium_msi_pmu *misc_pmu, + u32 offset, u32 req_id_val) +{ + u32 val; + + val = readl(misc_pmu->base + offset); + val |= req_id_val; + + writel(val, misc_pmu->base + offset); +} + + +// overflow stop status +static unsigned long phytium_msi_pmu_get_now_status(struct phytium_msi_pmu *misc_pmu) +{ + unsigned long val; + + val = (unsigned long)readl(misc_pmu->base + MISC_MSI_MON_STATE); + return val; +} + +// overflow stop reason +static unsigned long phytium_msi_pmu_get_stop_status(struct phytium_msi_pmu *misc_pmu) +{ + unsigned long val; + + val = (unsigned long)readl(misc_pmu->base + MISC_MSI_MON_STATE_STOP); + return val; +} + +static void phytium_msi_pmu_clear_all_counters(struct phytium_msi_pmu *misc_pmu) +{ + u32 val; + + val = MISC_MSI_MON_OPT_MASK; + writel(val, misc_pmu->base + MISC_MSI_MON_CLR_REG); +} + +static void phytium_msi_pmu_clear_counters(struct phytium_msi_pmu *misc_pmu, + u32 clr_val) +{ + writel(clr_val, misc_pmu->base + MISC_MSI_MON_CLR_REG); +} + +static u64 phytium_msi_pmu_read_counter(struct phytium_msi_pmu *misc_pmu, + struct hw_perf_event *hwc) +{ + u32 cycles_l, cycles_h, req_cnt, reqid_val; + u32 counter_offset, offset_id, mov_bits, cnt_mov_bits, record_mov_bits; + u32 reqid[4], reqcnt[4]; + u64 val64; + int idx, event_type, i, j; + struct perf_event *event; + + idx = GET_MISC_EVENTID(hwc); + event = misc_pmu->pmu_events.hw_events[idx]; + if (!EVENT_VALID(idx)) { + dev_err(misc_pmu->dev, "Unsupported event index:%d!\n", idx); + return 0; + } + + counter_offset = misc_counter_reg_offset[idx]; + event_type = phytium_msi_pmu_get_event_type(event); + + if ((idx % 5) == 4) { + cycles_l = readl(misc_pmu->base + counter_offset + 4); + cycles_h = readl(misc_pmu->base + counter_offset); + val64 = (u64)cycles_h << 32 | (u64)cycles_l; + } else { + offset_id = (u32)(idx % 5); + req_cnt = readl(misc_pmu->base + counter_offset); + mov_bits = offset_id * 4; + val64 = (u64)(0xF & (req_cnt >> mov_bits)); + + j = event_type * 5; + for (i = 0; i < 4; i++) { + reqid_val = readl(misc_pmu->base + misc_reqid_record_reg_offset[j]); + req_cnt = readl(misc_pmu->base + misc_counter_reg_offset[j]); + record_mov_bits = (i % 2) * 16; + reqid[i] = (u32)(0xFFFF & (reqid_val >> record_mov_bits)); + cnt_mov_bits = i * 4; + reqcnt[i] = (u32)(0xF & (req_cnt >> cnt_mov_bits)); + dev_info(misc_pmu->dev, "reqid(%u),cnt=%u\n", reqid[i], reqcnt[i]); + j += 1; + } + } + return val64; +} + +static void phytium_msi_pmu_set_timer(struct perf_event *event, u64 th_val) +{ + struct phytium_msi_pmu *misc_pmu = to_phytium_msi_pmu(event->pmu); + u32 val_l, val_h; + + val_l = th_val & 0xFFFFFFFF; + val_h = (th_val >> 32) & 0xFFFFFFFF; + + writel(val_l, misc_pmu->base + MISC_PCIE0_MON_SET_TIME_L); + writel(val_h, misc_pmu->base + MISC_PCIE0_MON_SET_TIME_H); + writel(val_l, misc_pmu->base + MISC_PCIE1_MON_SET_TIME_L); + writel(val_h, misc_pmu->base + MISC_PCIE1_MON_SET_TIME_H); + writel(val_l, misc_pmu->base + MISC_PCIE2_MON_SET_TIME_L); + writel(val_h, misc_pmu->base + MISC_PCIE2_MON_SET_TIME_H); +} + +static void phytium_msi_pmu_reset_timer(struct phytium_msi_pmu *misc_pmu) +{ + writel(0xFFFFFFFF, misc_pmu->base + MISC_PCIE0_MON_SET_TIME_L); + writel(0xFFFFFFFF, misc_pmu->base + MISC_PCIE0_MON_SET_TIME_H); + + writel(0xFFFFFFFF, misc_pmu->base + MISC_PCIE1_MON_SET_TIME_L); + writel(0xFFFFFFFF, misc_pmu->base + MISC_PCIE1_MON_SET_TIME_H); + + writel(0xFFFFFFFF, misc_pmu->base + MISC_PCIE2_MON_SET_TIME_L); + writel(0xFFFFFFFF, misc_pmu->base + MISC_PCIE2_MON_SET_TIME_H); +} + +void phytium_msi_pmu_clean_event_config(struct phytium_msi_pmu *misc_pmu) +{ + misc_pmu->event_cfg.pcie0_by_reqid = -1; + misc_pmu->event_cfg.pcie1_by_reqid = -1; + misc_pmu->event_cfg.pcie2_by_reqid = -1; + + misc_pmu->event_cfg.pcie0_reqid0 = -1; + misc_pmu->event_cfg.pcie0_reqid1 = -1; + misc_pmu->event_cfg.pcie0_reqid2 = -1; + misc_pmu->event_cfg.pcie0_reqid3 = -1; + + misc_pmu->event_cfg.pcie1_reqid0 = -1; + misc_pmu->event_cfg.pcie1_reqid1 = -1; + misc_pmu->event_cfg.pcie1_reqid2 = -1; + misc_pmu->event_cfg.pcie1_reqid3 = -1; + + misc_pmu->event_cfg.pcie2_reqid0 = -1; + misc_pmu->event_cfg.pcie2_reqid1 = -1; + misc_pmu->event_cfg.pcie2_reqid2 = -1; + misc_pmu->event_cfg.pcie2_reqid3 = -1; + + misc_pmu->event_cfg.trig_mode = -1; + + phytium_msi_pmu_set_trig_mode(misc_pmu, 0); + phytium_msi_pmu_clean_mon_mode(misc_pmu); +} + +static int phytium_msi_pmu_mark_event(struct perf_event *event) +{ + struct phytium_msi_pmu *misc_pmu = to_phytium_msi_pmu(event->pmu); + unsigned long *used_mask = misc_pmu->pmu_events.used_mask; + unsigned long *dev_mask = misc_pmu->pmu_events.dev_mask; + int idx = (int)misc_pmu_get_event(event); + int event_type = phytium_msi_pmu_get_event_type(event); + + if (test_bit(idx, used_mask)) + return -EAGAIN; + set_bit(idx, used_mask); + + if (!test_bit(event_type, dev_mask)) + set_bit(event_type, dev_mask); + + return idx; +} + +static void phytium_msi_pmu_unmark_event(struct perf_event *event) +{ + struct phytium_msi_pmu *misc_pmu = to_phytium_msi_pmu(event->pmu); + int idx = (int)misc_pmu_get_event(event); + int event_type = phytium_msi_pmu_get_event_type(event); + + if (!EVENT_VALID(idx)) { + dev_err(misc_pmu->dev, "Unsupported event index:%d!\n", idx); + return; + } + + clear_bit(idx, misc_pmu->pmu_events.used_mask); + clear_bit(event_type, misc_pmu->pmu_events.dev_mask); +} + +int phytium_msi_pmu_event_init(struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; + struct phytium_msi_pmu *misc_pmu; + + if (event->attr.type != event->pmu->type) + return -ENOENT; + + if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK) + return -EOPNOTSUPP; + + misc_pmu = to_phytium_msi_pmu(event->pmu); + + if (event->cpu < 0) { + dev_warn(misc_pmu->dev, "Can't provide per-task data!\n"); + return -EINVAL; + } + + if ((event->attr.config & PHYTIUM_MSI_PMU_EVENT_MASK) > PHYTIUM_MISC_MAX_COUNTERS) + return -EINVAL; + + if (misc_pmu->on_cpu == -1) + return -EINVAL; + + hwc->idx = -1; + hwc->config_base = event->attr.config; + + event->cpu = misc_pmu->on_cpu; + + return 0; +} + +void phytium_msi_pmu_event_update(struct perf_event *event) +{ + struct phytium_msi_pmu *misc_pmu = to_phytium_msi_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + u64 delta; + + delta = phytium_msi_pmu_read_counter(misc_pmu, hwc); + local64_add(delta, &event->count); +} + +void phytium_msi_pmu_event_start(struct perf_event *event, int flags) +{ + struct hw_perf_event *hwc = &event->hw; + + hwc->state = 0; + perf_event_update_userpage(event); +} + +void phytium_msi_pmu_event_stop(struct perf_event *event, int flags) +{ + struct hw_perf_event *hwc = &event->hw; + + hwc->state |= PERF_HES_STOPPED; + + if (flags & PERF_EF_UPDATE) + phytium_msi_pmu_event_update(event); +} + +int phytium_msi_pmu_set_event_config(struct phytium_msi_pmu *misc_pmu, int idx) +{ + int event_type; + int dec0, dec1, dec2, dec3; + u32 trig_mode, by_reqid, reqid0, reqid1, reqid2, reqid3; + u32 by_reqid_val, reqid_cfg_offset01, reqid_cfg_offset23; + struct perf_event *event = misc_pmu->pmu_events.hw_events[idx]; + + trig_mode = misc_pmu_get_trig_mode(event); + if (misc_pmu->event_cfg.trig_mode < 0) { + phytium_msi_pmu_set_trig_mode(misc_pmu, trig_mode); + misc_pmu->event_cfg.trig_mode = trig_mode; + } else if (misc_pmu->event_cfg.trig_mode != trig_mode) { + dev_err(misc_pmu->dev, + "Incorrect trig_mode parameter for the same PMU"); + dev_err(misc_pmu->dev, "The event parameters should be the same!"); + return -EINVAL; + } + + event_type = phytium_msi_pmu_get_event_type(event); + switch (event_type) { + case 0: + /* peu */ + by_reqid = misc_pmu_get_by_reqid(event); + reqid0 = misc_pmu_get_reqid0(event); + reqid1 = misc_pmu_get_reqid1(event); + reqid2 = misc_pmu_get_reqid2(event); + reqid3 = misc_pmu_get_reqid3(event); + if (misc_pmu->event_cfg.pcie0_by_reqid < 0) { + // first read + misc_pmu->event_cfg.pcie0_by_reqid = by_reqid; + if (by_reqid) { + misc_pmu->event_cfg.pcie0_reqid0 = reqid0; + misc_pmu->event_cfg.pcie0_reqid1 = reqid1; + misc_pmu->event_cfg.pcie0_reqid2 = reqid2; + misc_pmu->event_cfg.pcie0_reqid3 = reqid3; + } + } else if (misc_pmu->event_cfg.pcie0_by_reqid == 1) { + dec0 = reqid0 - misc_pmu->event_cfg.pcie0_reqid0; + dec1 = reqid1 - misc_pmu->event_cfg.pcie0_reqid1; + dec2 = reqid2 - misc_pmu->event_cfg.pcie0_reqid2; + dec3 = reqid3 - misc_pmu->event_cfg.pcie0_reqid3; + if (dec0 || dec1 || dec2 || dec3 || (by_reqid == 0)) { + dev_err(misc_pmu->dev, + "Incorrect reqid parameter of pcie0 for the same PMU!"); + dev_err(misc_pmu->dev, "The event parameters should be the same!"); + return -EINVAL; + } + } else { + if (by_reqid) { + dev_err(misc_pmu->dev, + "Incorrect by_reqid parameter of pcie0 for the same PMU"); + dev_err(misc_pmu->dev, "The event parameters should be the same!"); + return -EINVAL; + } + } + by_reqid_val = MISC_PCIE0_MSI_BYREQID_MODE_BIT; + reqid_cfg_offset01 = MISC_PCIE0_MSI_REG_ID_CFG_REG_01; + reqid_cfg_offset23 = MISC_PCIE0_MSI_REG_ID_CFG_REG_23; + break; + case 1: + /* pxu */ + by_reqid = misc_pmu_get_by_reqid(event); + reqid0 = misc_pmu_get_reqid0(event); + reqid1 = misc_pmu_get_reqid1(event); + reqid2 = misc_pmu_get_reqid2(event); + reqid3 = misc_pmu_get_reqid3(event); + if (misc_pmu->event_cfg.pcie1_by_reqid < 0) { + misc_pmu->event_cfg.pcie1_by_reqid = by_reqid; + if (by_reqid) { + misc_pmu->event_cfg.pcie1_reqid0 = reqid0; + misc_pmu->event_cfg.pcie1_reqid1 = reqid1; + misc_pmu->event_cfg.pcie1_reqid2 = reqid2; + misc_pmu->event_cfg.pcie1_reqid3 = reqid3; + } + } else if (misc_pmu->event_cfg.pcie1_by_reqid == 1) { + dec0 = reqid0 - misc_pmu->event_cfg.pcie1_reqid0; + dec1 = reqid1 - misc_pmu->event_cfg.pcie1_reqid1; + dec2 = reqid2 - misc_pmu->event_cfg.pcie1_reqid2; + dec3 = reqid3 - misc_pmu->event_cfg.pcie1_reqid3; + if (dec0 || dec1 || dec2 || dec3 || (by_reqid == 0)) { + dev_err(misc_pmu->dev, + "Incorrect reqid parameter of pcie1 for the same PMU!"); + dev_err(misc_pmu->dev, "The event parameters should be the same!"); + return -EINVAL; + } + } else { + if (by_reqid) { + dev_err(misc_pmu->dev, + "Incorrect by_reqid parameter of pcie1 for the same PMU"); + dev_err(misc_pmu->dev, "The event parameters should be the same!"); + return -EINVAL; + } + } + by_reqid_val = MISC_PCIE1_MSI_BYREQID_MODE_BIT; + reqid_cfg_offset01 = MISC_PCIE1_MSI_REG_ID_CFG_REG_01; + reqid_cfg_offset23 = MISC_PCIE1_MSI_REG_ID_CFG_REG_23; + break; + case 2: + /* pcu */ + by_reqid = misc_pmu_get_by_reqid(event); + reqid0 = misc_pmu_get_reqid0(event); + reqid1 = misc_pmu_get_reqid1(event); + reqid2 = misc_pmu_get_reqid2(event); + reqid3 = misc_pmu_get_reqid3(event); + if (misc_pmu->event_cfg.pcie2_by_reqid < 0) { + misc_pmu->event_cfg.pcie2_by_reqid = by_reqid; + if (by_reqid) { + misc_pmu->event_cfg.pcie2_reqid0 = reqid0; + misc_pmu->event_cfg.pcie2_reqid1 = reqid1; + misc_pmu->event_cfg.pcie2_reqid2 = reqid2; + misc_pmu->event_cfg.pcie2_reqid3 = reqid3; + } + } else if (misc_pmu->event_cfg.pcie2_by_reqid == 1) { + dec0 = reqid0 - misc_pmu->event_cfg.pcie2_reqid0; + dec1 = reqid1 - misc_pmu->event_cfg.pcie2_reqid1; + dec2 = reqid2 - misc_pmu->event_cfg.pcie2_reqid2; + dec3 = reqid3 - misc_pmu->event_cfg.pcie2_reqid3; + if (dec0 || dec1 || dec2 || dec3 || (by_reqid == 0)) { + dev_err(misc_pmu->dev, + "Incorrect reqid parameter of pcie2 for the same PMU!"); + dev_err(misc_pmu->dev, "The event parameters should be the same!"); + return -EINVAL; + } + } else { + if (by_reqid) { + dev_err(misc_pmu->dev, + "Incorrect by_reqid parameter of pcie2 for the same PMU"); + dev_err(misc_pmu->dev, "The event parameters should be the same!"); + return -EINVAL; + } + } + by_reqid_val = MISC_PCIE2_MSI_BYREQID_MODE_BIT; + reqid_cfg_offset01 = MISC_PCIE2_MSI_REG_ID_CFG_REG_01; + reqid_cfg_offset23 = MISC_PCIE2_MSI_REG_ID_CFG_REG_23; + break; + default: + return 0; + } + + phytium_msi_pmu_by_reqid_mode(misc_pmu, by_reqid_val, by_reqid); + if (by_reqid) { + phytium_msi_pmu_set_req_id(misc_pmu, reqid_cfg_offset01, + (reqid0 | (reqid1 << 16))); + phytium_msi_pmu_set_req_id(misc_pmu, reqid_cfg_offset23, + (reqid2 | (reqid3 << 16))); + } + + return 0; +} + +void phytium_msi_pmu_reset_event_config(struct phytium_msi_pmu *misc_pmu, + unsigned long dev_mask) +{ + int idx; + u32 by_reqid, reqid_cfg_offset01, reqid_cfg_offset23, by_reqid_val; + u32 reqid0, reqid1, reqid2, reqid3; + + for_each_set_bit(idx, &dev_mask, 3) { + switch (idx) { + case 0: + by_reqid = misc_pmu->event_cfg.pcie0_by_reqid; + reqid0 = misc_pmu->event_cfg.pcie0_reqid0; + reqid1 = misc_pmu->event_cfg.pcie0_reqid1; + reqid2 = misc_pmu->event_cfg.pcie0_reqid2; + reqid3 = misc_pmu->event_cfg.pcie0_reqid3; + by_reqid_val = MISC_PCIE0_MSI_BYREQID_MODE_BIT; + reqid_cfg_offset01 = MISC_PCIE0_MSI_REG_ID_CFG_REG_01; + reqid_cfg_offset23 = MISC_PCIE0_MSI_REG_ID_CFG_REG_23; + break; + case 1: + by_reqid = misc_pmu->event_cfg.pcie1_by_reqid; + reqid0 = misc_pmu->event_cfg.pcie1_reqid0; + reqid1 = misc_pmu->event_cfg.pcie1_reqid1; + reqid2 = misc_pmu->event_cfg.pcie1_reqid2; + reqid3 = misc_pmu->event_cfg.pcie1_reqid3; + by_reqid_val = MISC_PCIE1_MSI_BYREQID_MODE_BIT; + reqid_cfg_offset01 = MISC_PCIE1_MSI_REG_ID_CFG_REG_01; + reqid_cfg_offset23 = MISC_PCIE1_MSI_REG_ID_CFG_REG_23; + break; + case 2: + by_reqid = misc_pmu->event_cfg.pcie2_by_reqid; + reqid0 = misc_pmu->event_cfg.pcie2_reqid0; + reqid1 = misc_pmu->event_cfg.pcie2_reqid1; + reqid2 = misc_pmu->event_cfg.pcie2_reqid2; + reqid3 = misc_pmu->event_cfg.pcie2_reqid3; + by_reqid_val = MISC_PCIE2_MSI_BYREQID_MODE_BIT; + reqid_cfg_offset01 = MISC_PCIE2_MSI_REG_ID_CFG_REG_01; + reqid_cfg_offset23 = MISC_PCIE2_MSI_REG_ID_CFG_REG_23; + break; + default: + return; + } + + phytium_msi_pmu_by_reqid_mode(misc_pmu, by_reqid_val, by_reqid); + if (by_reqid) { + phytium_msi_pmu_set_req_id(misc_pmu, reqid_cfg_offset01, + (reqid0 | (reqid1 << 16))); + phytium_msi_pmu_set_req_id(misc_pmu, reqid_cfg_offset23, + (reqid2 | (reqid3 << 16))); + } + } + + if (misc_pmu->event_cfg.trig_mode >= 0) + phytium_msi_pmu_set_trig_mode(misc_pmu, misc_pmu->event_cfg.trig_mode); + +} + +int phytium_msi_pmu_event_add(struct perf_event *event, int flags) +{ + struct phytium_msi_pmu *misc_pmu = to_phytium_msi_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + int idx, ret; + u64 event_timer = misc_pmu_get_timer(event); + + phytium_msi_pmu_enable_clk(misc_pmu); + + hwc->state |= PERF_HES_STOPPED; + + idx = phytium_msi_pmu_mark_event(event); + if (idx < 0) + return idx; + + event->hw.idx = idx; + misc_pmu->pmu_events.hw_events[idx] = event; + + ret = phytium_msi_pmu_set_event_config(misc_pmu, idx); + if (ret) + return ret; + + if (event_timer != 0) + phytium_msi_pmu_set_timer(event, event_timer); + return 0; +} + +void phytium_msi_pmu_event_del(struct perf_event *event, int flags) +{ + struct phytium_msi_pmu *misc_pmu = to_phytium_msi_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + u32 event_timer; + + phytium_msi_pmu_event_stop(event, PERF_EF_UPDATE); + + event_timer = misc_pmu_get_timer(event); + if (event_timer != 0) + phytium_msi_pmu_reset_timer(misc_pmu); + + phytium_msi_pmu_unmark_event(event); + + perf_event_update_userpage(event); + misc_pmu->pmu_events.hw_events[hwc->idx] = NULL; +} + +void phytium_msi_pmu_enable(struct pmu *pmu) +{ + struct phytium_msi_pmu *misc_pmu = to_phytium_msi_pmu(pmu); + int event_added = bitmap_weight(misc_pmu->pmu_events.used_mask, PHYTIUM_MISC_MAX_COUNTERS); + + if (event_added) { + phytium_msi_pmu_clear_all_counters(misc_pmu); + phytium_msi_pmu_start_all_counters(misc_pmu); + } +} + +void phytium_msi_pmu_disable(struct pmu *pmu) +{ + struct phytium_msi_pmu *misc_pmu = to_phytium_msi_pmu(pmu); + int event_added = bitmap_weight(misc_pmu->pmu_events.used_mask, PHYTIUM_MISC_MAX_COUNTERS); + + if (event_added) + phytium_msi_pmu_stop_all_counters(misc_pmu); + else + phytium_msi_pmu_clean_event_config(misc_pmu); +} + +static const struct acpi_device_id phytium_msi_pmu_acpi_match[] = { + { "PHYT300D", }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, phytium_msi_pmu_acpi_match); + +static int phytium_msi_pmu_version(struct platform_device *pdev, + struct phytium_msi_pmu *misc_pmu) +{ + u32 pidr; + + pidr = readl(misc_pmu->base + MISC_MON_PIDR0); + dev_info(&pdev->dev, "PIDR=%#x,VER=%#lx.\n", pidr, (pidr & MISC_PMU_VER_BIT)); + pidr &= MISC_PMU_VER_BIT; + if (pidr == 0x1) { + misc_pmu->ver = MISCV1P0; + } else { + dev_err(&pdev->dev, "The current driver does not support this device.\n"); + return -ENODEV; + } + return 0; +} + +bool is_interrupt_state(unsigned long now_state) +{ + const int shifts[] = {0, 4, 8}; + + for (int i = 0; i < 3; i++) { + unsigned int group_val = (now_state >> shifts[i]) & 0x0F; + + if (group_val == MISC_MSI_MON_OVFL_STATE_MASK) + return true; + } + + return false; +} + +static irqreturn_t phytium_msi_pmu_overflow_handler(int irq, void *dev_id) +{ + struct phytium_msi_pmu *misc_pmu = dev_id; + struct perf_event *event; + unsigned long now_state, stop_state; + int idx, event_type; + unsigned long dev_stop_mask, dev_mask; + unsigned long *used_mask = misc_pmu->pmu_events.used_mask; + u32 opt_val = 0; + int event_added = bitmap_weight(used_mask, PHYTIUM_MISC_MAX_COUNTERS); + + // 0:pcu 1:pxu 2:peu + now_state = phytium_msi_pmu_get_now_status(misc_pmu); + + if (!is_interrupt_state(now_state)) + return IRQ_NONE; + + if (!event_added) { + phytium_msi_pmu_clear_counters(misc_pmu, MISC_MSI_MON_OPT_MASK); + return IRQ_HANDLED; + } + + stop_state = phytium_msi_pmu_get_stop_status(misc_pmu); + if (stop_state & MISC_MSI_MON_COUNT_FULL_MASK) { + for_each_set_bit(idx, used_mask, PHYTIUM_MISC_MAX_COUNTERS) { + event = misc_pmu->pmu_events.hw_events[idx]; + if (!event) + continue; + event_type = phytium_msi_pmu_get_event_type(event); + dev_stop_mask = pcie_dev_msi_mon_stop_mask[event_type]; + + if (stop_state & dev_stop_mask & MISC_MSI_MON_COUNT_FULL_MASK) { + phytium_msi_pmu_event_update(event); + opt_val |= pcie_dev_msi_mon_opt_bits[event_type]; + set_bit(event_type, &dev_mask); + } + } + phytium_msi_pmu_clear_counters(misc_pmu, opt_val); + phytium_msi_pmu_start_counters(misc_pmu, opt_val); + } else { + for_each_set_bit(idx, used_mask, PHYTIUM_MISC_MAX_COUNTERS) { + event = misc_pmu->pmu_events.hw_events[idx]; + if (!event) + continue; + phytium_msi_pmu_event_update(event); + } + phytium_msi_pmu_clear_all_counters(misc_pmu); + } + + return IRQ_HANDLED; +} + +static int phytium_msi_pmu_init_irq(struct platform_device *pdev, + struct phytium_msi_pmu *misc_pmu) +{ + int irq, ret; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(&pdev->dev, irq, phytium_msi_pmu_overflow_handler, + IRQF_NOBALANCING | IRQF_NO_THREAD | IRQF_SHARED, + dev_name(&pdev->dev), misc_pmu); + if (ret < 0) { + dev_err(&pdev->dev, "Fail to request IRQ:%d ret:%d\n", irq, ret); + return ret; + } + + misc_pmu->irq = irq; + + return 0; +} + +static int phytium_msi_pmu_init_data(struct platform_device *pdev, + struct phytium_msi_pmu *misc_pmu) +{ + struct resource *res; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + misc_pmu->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(misc_pmu->base)) { + dev_err(&pdev->dev, "ioremap failed for misc_pmu base resource\n"); + return PTR_ERR(misc_pmu->base); + } + + ret = phytium_msi_pmu_version(pdev, misc_pmu); + if (ret) + return ret; + if (device_property_read_u32(&pdev->dev, "phytium,die-id", &misc_pmu->die_id)) { + dev_err(&pdev->dev, "Can not read phytium,die-id!\n"); + return -EINVAL; + } + + return 0; +} + +static int phytium_msi_pmu_dev_probe(struct platform_device *pdev, + struct phytium_msi_pmu *misc_pmu) +{ + int ret; + + ret = phytium_msi_pmu_init_data(pdev, misc_pmu); + if (ret) + return ret; + + ret = phytium_msi_pmu_init_irq(pdev, misc_pmu); + if (ret) + return ret; + + misc_pmu->dev = &pdev->dev; + misc_pmu->on_cpu = -1; + + return 0; +} + +static int phytium_msi_pmu_probe(struct platform_device *pdev) +{ + struct phytium_msi_pmu *misc_pmu; + char *name; + int ret; + + misc_pmu = devm_kzalloc(&pdev->dev, sizeof(*misc_pmu), GFP_KERNEL); + if (!misc_pmu) + return -ENOMEM; + + platform_set_drvdata(pdev, misc_pmu); + + ret = phytium_msi_pmu_dev_probe(pdev, misc_pmu); + if (ret) + return ret; + + ret = cpuhp_state_add_instance(phytium_msi_pmu_hp_state, &misc_pmu->node); + if (ret) { + dev_err(&pdev->dev, "Error %d registering hotplug;\n", ret); + return ret; + } + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "phyt%u_msi_pmu", misc_pmu->die_id); + + misc_pmu->pmu = (struct pmu){ + .name = name, + .module = THIS_MODULE, + .task_ctx_nr = perf_invalid_context, + .event_init = phytium_msi_pmu_event_init, + .pmu_enable = phytium_msi_pmu_enable, + .pmu_disable = phytium_msi_pmu_disable, + .add = phytium_msi_pmu_event_add, + .del = phytium_msi_pmu_event_del, + .start = phytium_msi_pmu_event_start, + .stop = phytium_msi_pmu_event_stop, + .read = phytium_msi_pmu_event_update, + .attr_groups = phytium_msi_pmu_attr_groups, + .capabilities = PERF_PMU_CAP_NO_EXCLUDE, + }; + + ret = perf_pmu_register(&misc_pmu->pmu, name, -1); + if (ret) { + dev_err(misc_pmu->dev, "MISC PMU register failed!\n"); + cpuhp_state_remove_instance_nocalls(phytium_msi_pmu_hp_state, &misc_pmu->node); + } + + phytium_msi_pmu_enable_clk(misc_pmu); + + pr_info("%s on cpu%d.\n", name, misc_pmu->on_cpu); + + return ret; +} + +static int phytium_msi_pmu_remove(struct platform_device *pdev) +{ + struct phytium_msi_pmu *misc_pmu = platform_get_drvdata(pdev); + + phytium_msi_pmu_disable_clk(misc_pmu); + perf_pmu_unregister(&misc_pmu->pmu); + cpuhp_state_remove_instance_nocalls(phytium_msi_pmu_hp_state, &misc_pmu->node); + + return 0; +} + +static struct platform_driver phytium_msi_pmu_driver = { + .driver = { + .name = "phytium_msi_pmu", + .acpi_match_table = ACPI_PTR(phytium_msi_pmu_acpi_match), + .suppress_bind_attrs = true, + }, + .probe = phytium_msi_pmu_probe, + .remove = phytium_msi_pmu_remove, +}; + +int phytium_msi_pmu_online_cpu(unsigned int cpu, struct hlist_node *node) +{ + struct phytium_msi_pmu *misc_pmu = + hlist_entry_safe(node, struct phytium_msi_pmu, node); + + if (!cpumask_test_cpu(cpu, cpumask_of_node(misc_pmu->die_id))) + return 0; + + if (misc_pmu->on_cpu != -1) { + if (!cpumask_test_cpu(misc_pmu->on_cpu, cpumask_of_node(misc_pmu->die_id))) { + perf_pmu_migrate_context(&misc_pmu->pmu, misc_pmu->on_cpu, cpu); + misc_pmu->on_cpu = cpu; + WARN_ON(irq_set_affinity_hint(misc_pmu->irq, cpumask_of(cpu))); + } + return 0; + } + + misc_pmu->on_cpu = cpu; + + return 0; +} + +int phytium_msi_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node) +{ + struct phytium_msi_pmu *misc_pmu = hlist_entry_safe(node, + struct phytium_msi_pmu, node); + unsigned int target; + cpumask_t available_cpus; + + if (misc_pmu->on_cpu != cpu) + return 0; + + if (cpumask_and(&available_cpus, cpumask_of_node(misc_pmu->die_id), cpu_online_mask) && + cpumask_andnot(&available_cpus, &available_cpus, cpumask_of(cpu))) + target = cpumask_last(&available_cpus); + else { + cpumask_andnot(&available_cpus, cpu_online_mask, cpumask_of(cpu)); + target = cpumask_last(&available_cpus); + } + + if (target >= nr_cpu_ids) { + dev_err(misc_pmu->dev, "offline cpu%d with no target to migrate.\n", cpu); + return 0; + } + + perf_pmu_migrate_context(&misc_pmu->pmu, cpu, target); + misc_pmu->on_cpu = target; + + return 0; +} + +static int __init phytium_msi_pmu_module_init(void) +{ + int ret; + + phytium_msi_pmu_hp_state = + cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, + "perf/phytium/miscpmu:online", + phytium_msi_pmu_online_cpu, + phytium_msi_pmu_offline_cpu); + if (phytium_msi_pmu_hp_state < 0) { + pr_err("MISC PMU: setup hotplug, ret = %d\n", phytium_msi_pmu_hp_state); + return phytium_msi_pmu_hp_state; + } + + ret = platform_driver_register(&phytium_msi_pmu_driver); + if (ret) + cpuhp_remove_multi_state(phytium_msi_pmu_hp_state); + + return ret; +} +module_init(phytium_msi_pmu_module_init); + +static void __exit phytium_msi_pmu_module_exit(void) +{ + platform_driver_unregister(&phytium_msi_pmu_driver); + cpuhp_remove_multi_state(phytium_msi_pmu_hp_state); +} +module_exit(phytium_msi_pmu_module_exit); + +MODULE_DESCRIPTION("Phytium MISC PMU driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(MISC_PERF_DRIVER_VERSION); +MODULE_AUTHOR("Tan Rui "); +MODULE_AUTHOR("Fu Boyi "); From 34fc5cfc1065b9012f8d5e17b0979c57b3d652a0 Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Fri, 8 May 2026 16:03:38 +0800 Subject: [PATCH 67/81] drivers/perf: phytium: Bugfix DDR PMU driver count error Resolve the count error issue caused by acquiring event index error for DDR PMU drivers. Signed-off-by: Zhang Fuxiang Signed-off-by: Fu Boyi Signed-off-by: Wang Yinfeng --- drivers/perf/phytium/phytium_ddr_pmu.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/perf/phytium/phytium_ddr_pmu.c b/drivers/perf/phytium/phytium_ddr_pmu.c index 8e3f7de911843..cb7ff0fa8f961 100644 --- a/drivers/perf/phytium/phytium_ddr_pmu.c +++ b/drivers/perf/phytium/phytium_ddr_pmu.c @@ -246,9 +246,10 @@ #define PHYTIUM_DDR_PMUV1_COUNTERS_NUM 8 #define PHYTIUM_DDR_PMUV2_COUNTERS_NUM 5 #define PHYTIUM_DDR_PMUV3_COUNTERS_NUM 165 -#define PHYTIUM_DDR_EVENTS_MAX_MASKi 0x7 +#define PHYTIUM_DDR_PMUV1V2_EVENTS_MAX_MASK 0x7 +#define PHYTIUM_DDR_PMUV3_EVENTS_MAX_MASK 0xA5 -#define GET_DDR_PMUV1V2_EVENTID(hwc) (hwc->config_base & PHYTIUM_DDR_EVENTS_MAX_MASK) +#define GET_DDR_PMUV1V2_EVENTID(hwc) (hwc->config_base & PHYTIUM_DDR_PMUV1V2_EVENTS_MAX_MASK) #define EVENT_VALID_V1(idx) ((idx >= 0) && (idx < PHYTIUM_DDR_PMUV1_COUNTERS_NUM)) #define EVENT_VALID_V2(idx) ((idx >= 0) && (idx < PHYTIUM_DDR_PMUV2_COUNTERS_NUM)) #define EVENT_VALID_V3(idx) ((idx >= 0) && (idx < PHYTIUM_DDR_PMUV3_COUNTERS_NUM)) @@ -1512,10 +1513,10 @@ int phytium_ddr_pmu_event_init(struct perf_event *event) } if (ddr_pmu->ver <= DDR_PMUV2P0) { - if ((event->attr.config & PHYTIUM_DDR_EVENTS_MAX_MASK) > ddr_pmu->cnts_num) + if ((event->attr.config & PHYTIUM_DDR_PMUV1V2_EVENTS_MAX_MASK) > ddr_pmu->cnts_num) return -EINVAL; } else if (ddr_pmu->ver == DDR_PMUV3P0) { - if (event->attr.config > PHYTIUM_DDR_MAX_COUNTERS) + if ((event->attr.config & PHYTIUM_DDR_PMUV3_EVENTS_MAX_MASK) > ddr_pmu->cnts_num) return -EINVAL; } @@ -1613,7 +1614,7 @@ int phytium_ddr_pmu_event_add(struct perf_event *event, int flags) phytium_ddr_pmu_v2_notifier_chain_trigger(ddr_pmu, DDR_PMUV2_NOTICE_START); #endif } else if (ddr_pmu->ver == DDR_PMUV3P0) { - if (!EVENT_VALID_V3(idx)) { + if (!EVENT_VALID_V3(phytium_ddr_pmu_v3_get_event_idx(event))) { dev_err(ddr_pmu->dev, "Unsupported event index:%d!\n", idx); return idx; } From 2d870337c9c2b4aa09fc1f8b07e50ef28908c1d8 Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Fri, 8 May 2026 16:05:47 +0800 Subject: [PATCH 68/81] drivers/perf: phytium: Fix PCIe PMU driver compile failure When CONFIG_ARCH_PHYTIUM and CONFIG_ACPI are not set, but CONFIG_COMPILE_TEST is enabled, the pcie pmu driver fails to compile. Signed-off-by: Zhang Fuxiang Signed-off-by: Tan Rui Signed-off-by: Wang Yinfeng --- drivers/perf/phytium/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/perf/phytium/Kconfig b/drivers/perf/phytium/Kconfig index 0150ac7bec8b3..6deae721f422c 100644 --- a/drivers/perf/phytium/Kconfig +++ b/drivers/perf/phytium/Kconfig @@ -31,7 +31,7 @@ endif config PHYT_PCIE_PMU tristate "Phytium SoC PCIE PMU driver" - depends on (ARCH_PHYTIUM && ACPI) || COMPILE_TEST + depends on ARCH_PHYTIUM && ACPI default m help Provides support for Phytium SoC PCIe Controller performance From 984d067545caaab30c477dab7282adade0b2ef1c Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Fri, 8 May 2026 16:07:53 +0800 Subject: [PATCH 69/81] drivers/perf: phytium: Modify the enable logic of time mode Modify the enable logic in PCIe Link PMU driver to properly set up time mode and start-stop mode. Signed-off-by: Zhang Fuxiang Signed-off-by: Fu Boyi Signed-off-by: Wang Yinfeng --- drivers/perf/phytium/phytium_pcie_link_pmu.c | 42 ++++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/drivers/perf/phytium/phytium_pcie_link_pmu.c b/drivers/perf/phytium/phytium_pcie_link_pmu.c index 9243414c2fee6..60f0568024bea 100644 --- a/drivers/perf/phytium/phytium_pcie_link_pmu.c +++ b/drivers/perf/phytium/phytium_pcie_link_pmu.c @@ -571,18 +571,30 @@ static void phytium_pcie_link_pmu_disable_all_counters(struct phytium_pcie_link_ writel(0x0, pcie_link_pmu->base + REG_ENABLE_BUFFER); } -static void phytium_pcie_link_pmu_start_all_counters(struct phytium_pcie_link_pmu *pcie_link_pmu) +static void phytium_pcie_link_pmu_start_all_counters(struct perf_event *event) { - writel(0x1, pcie_link_pmu->base + REG_START_TYPE_CNT); - writel(0x1, pcie_link_pmu->base + REG_START_TLP_PERFORMANCE); - writel(0x1, pcie_link_pmu->base + REG_START_BUFFER); + struct phytium_pcie_link_pmu *pcie_link_pmu = to_phytium_pcie_link_pmu(event->pmu); + u32 idx = get_eventid(event); + + if (idx >= TLP_TYPE_CLASSIFY_0 && idx <= TLP_TYPE_CLASSIFY_1) + writel(0x1, pcie_link_pmu->base + REG_START_TYPE_CNT); + else if (idx >= TLP_PERFORM_TYPE_1 && idx < BUFFER_USAGE_TYPE_0) + writel(0x1, pcie_link_pmu->base + REG_START_TLP_PERFORMANCE); + else if (idx >= BUFFER_USAGE_TYPE_0) + writel(0x1, pcie_link_pmu->base + REG_START_BUFFER); } -static void phytium_pcie_link_pmu_stop_all_counters(struct phytium_pcie_link_pmu *pcie_link_pmu) +static void phytium_pcie_link_pmu_stop_all_counters(struct perf_event *event) { - writel(0x0, pcie_link_pmu->base + REG_START_TYPE_CNT); - writel(0x0, pcie_link_pmu->base + REG_START_TLP_PERFORMANCE); - writel(0x0, pcie_link_pmu->base + REG_START_BUFFER); + struct phytium_pcie_link_pmu *pcie_link_pmu = to_phytium_pcie_link_pmu(event->pmu); + u32 idx = get_eventid(event); + + if (idx >= TLP_TYPE_CLASSIFY_0 && idx <= TLP_TYPE_CLASSIFY_1) + writel(0x0, pcie_link_pmu->base + REG_START_TYPE_CNT); + else if (idx >= TLP_PERFORM_TYPE_1 && idx < BUFFER_USAGE_TYPE_0) + writel(0x0, pcie_link_pmu->base + REG_START_TLP_PERFORMANCE); + else if (idx >= BUFFER_USAGE_TYPE_0) + writel(0x0, pcie_link_pmu->base + REG_START_BUFFER); } static void phytium_pcie_link_pmu_set_timer(struct perf_event *event, u64 th_val) @@ -606,7 +618,7 @@ static void phytium_pcie_link_pmu_set_timer(struct perf_event *event, u64 th_val } else if (idx >= BUFFER_USAGE_TYPE_0) { writel(0x1, pcie_link_pmu->base + REG_CONTROL_TIME_MODE_BUFFER); writel(val_l, pcie_link_pmu->base + REG_TIMER_BUFFER_0); - writel(val_h, pcie_link_pmu->base + REG_TIMER_BUFFER_0); + writel(val_h, pcie_link_pmu->base + REG_TIMER_BUFFER_1); } } @@ -627,7 +639,7 @@ static void phytium_pcie_link_pmu_reset_timer(struct perf_event *event) } else if (idx >= BUFFER_USAGE_TYPE_0) { writel(0x0, pcie_link_pmu->base + REG_CONTROL_TIME_MODE_BUFFER); writel(0xFFFFFFFF, pcie_link_pmu->base + REG_TIMER_BUFFER_0); - writel(0xFFFFFFFF, pcie_link_pmu->base + REG_TIMER_BUFFER_0); + writel(0xFFFFFFFF, pcie_link_pmu->base + REG_TIMER_BUFFER_1); } } @@ -688,6 +700,7 @@ static void phytium_pcie_link_pmu_set_reg_err_sig(struct phytium_pcie_link_pmu * else err_reg_mask &= err_id; val |= err_reg_mask; + writel(val, pcie_link_pmu->base + REG_ERRO_SIG_SEL); } @@ -768,6 +781,8 @@ int phytium_pcie_link_pmu_event_add(struct perf_event *event, int flags) if (event_timer != 0 && time_mode != 0) phytium_pcie_link_pmu_set_timer(event, event_timer); + else + phytium_pcie_link_pmu_start_all_counters(event); hwc->state |= PERF_HES_STOPPED; @@ -790,6 +805,8 @@ void phytium_pcie_link_pmu_event_del(struct perf_event *event, int flags) phytium_pcie_link_pmu_event_stop(event, PERF_EF_UPDATE); if (event_timer != 0) phytium_pcie_link_pmu_reset_timer(event); + else + phytium_pcie_link_pmu_stop_all_counters(event); phytium_pcie_link_pmu_unmark_event(pcie_link_pmu, hwc->idx); @@ -808,7 +825,6 @@ void phytium_pcie_link_pmu_enable(struct pmu *pmu) if (event_added) { phytium_pcie_link_pmu_clear_all_counters(pcie_link_pmu); phytium_pcie_link_pmu_enable_all_counters(pcie_link_pmu); - phytium_pcie_link_pmu_start_all_counters(pcie_link_pmu); } } @@ -821,8 +837,6 @@ void phytium_pcie_link_pmu_disable(struct pmu *pmu) PHYTIUM_PCIE_LINK_MAX_COUNTERS); if (event_added) - phytium_pcie_link_pmu_stop_all_counters(pcie_link_pmu); - else phytium_pcie_link_pmu_disable_all_counters(pcie_link_pmu); } @@ -862,11 +876,13 @@ static int phytium_pcie_link_pmu_init_data(struct platform_device *pdev, dev_err(&pdev->dev, "Can not read phytium,die-id!\n"); return -EINVAL; } + if (device_property_read_u32(&pdev->dev, "phytium,pcie-id", &pcie_link_pmu->pcie_id)) { dev_err(&pdev->dev, "Can not read phytium,pcie-id!\n"); return -EINVAL; } + if (device_property_read_u32(&pdev->dev, "phytium,pmu-id", &pcie_link_pmu->pmu_id)) { dev_err(&pdev->dev, "Can not read pcie link pmu-id!\n"); From b3956ec4e1e711304b7307b2fc76b4b32b5775ff Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Sat, 9 May 2026 14:42:16 +0800 Subject: [PATCH 70/81] pcie: phytium: Fix PCIe IO space access restriction issue Some PCI devices have IO space access limit and only supporting 16 bit addresses. We update upper 16 bits of I/O base/limit according to the bridge and the RC root port. Signed-off-by: Zhang Fuxiang Signed-off-by: Xiao Cong Signed-off-by: Wang Yinfeng --- drivers/pci/setup-bus.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index d07c1d9ed0620..195bb8d1388a0 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -580,6 +580,9 @@ static void pci_setup_bridge_io(struct pci_dev *bridge) u8 io_base_lo, io_limit_lo; u16 l; u32 io_upper16; +#ifdef CONFIG_ARCH_PHYTIUM + u32 io_base_limit = 0; +#endif io_mask = PCI_IO_RANGE_MASK; if (bridge->io_window_1k) @@ -608,6 +611,19 @@ static void pci_setup_bridge_io(struct pci_dev *bridge) pci_write_config_word(bridge, PCI_IO_BASE, l); /* Update upper 16 bits of I/O base/limit */ pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, io_upper16); + +#ifdef CONFIG_ARCH_PHYTIUM + if (bridge->dev.parent) { + if (!fwnode_property_read_u32(dev_fwnode(bridge->dev.parent), + "io-upper", &io_base_limit)) + pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, io_base_limit); + } + if (bridge->dev.parent->parent) { + if (!fwnode_property_read_u32(dev_fwnode(bridge->dev.parent->parent), + "io-upper", &io_base_limit)) + pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, io_base_limit); + } +#endif } static void pci_setup_bridge_mmio(struct pci_dev *bridge) From 2edb997c5dfeeb7395f625d7aa73688732ff6283 Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Sat, 9 May 2026 14:49:44 +0800 Subject: [PATCH 71/81] PCI: phytium: Add hotplug and hotreset workaround patch for phytium SoCs When the PCIe device is unplugged or under hotreset, the PCIe controller's protrction mechanism is triggered, which will make the link inaccessible. This patch disables the protection after the link is up and makes the PCIe hotplug or hotreset process work well. Signed-off-by: Zhang Fuxiang Signed-off-by: Long Shixiang Signed-off-by: Wang Yinfeng --- drivers/pci/pci.h | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 8033f9056a801..b75d8bf7bba3a 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -914,4 +914,46 @@ static inline void phytium_clear_ctrl_prot(struct pci_dev *pdev, int op) } #endif +#ifdef CONFIG_ARCH_PHYTIUM +#include + +#define PHYTIUM_PCIE_HOTRESET 0 +#define PHYTIUM_PCIE_HOTPLUG 1 +#define PHYTIUM_PCI_VENDOR_ID 0x1DB7 +#define PHYTIUM_PCI_CTRL_ID 0x0100 +#define PHYTIUM_PCIE_CLEAR_CTRL_PROT_SMC_FUNC_ID 0xC2000020 + +static inline void phytium_clear_ctrl_prot(struct pci_dev *pdev, int op) +{ + int socket; + u8 bus = pdev->bus->number; + u8 device = PCI_SLOT(pdev->devfn); + u8 function = PCI_FUNC(pdev->devfn); + u16 vendor_id = pdev->vendor; + u16 device_id = pdev->device; + struct arm_smccc_res res; + u32 arg; + + if (vendor_id != PHYTIUM_PCI_VENDOR_ID || + device_id != PHYTIUM_PCI_CTRL_ID || + pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) + return; + + socket = dev_to_node(&pdev->dev); + if (socket < 0) { + pci_err(pdev, "Cannot find socket, stop clean pcie protection\n"); + return; + } + + arg = (socket << 16) | (bus << 8) | (device << 3) | function; + arm_smccc_smc(PHYTIUM_PCIE_CLEAR_CTRL_PROT_SMC_FUNC_ID, arg, op, 0, 0, 0, 0, 0, &res); + if (res.a0 != 0) + pci_err(pdev, "Error: Firmware call PCIE protection clear Failed: %d, sbdf: 0x%x\n", + (int)res.a0, arg); + else + pci_info(pdev, "%s : Clear pcie protection successfully\n", + op ? "HotPlug" : "HotReset"); +} +#endif + #endif /* DRIVERS_PCI_H */ From f6bb370378737f352e66fb06c37c30931512002f Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Sat, 9 May 2026 14:56:27 +0800 Subject: [PATCH 72/81] PCI: Add ACS quirk for phytium root and switch ports The phytium PCIe root ports and X100 switch do not support ACS at this point. However, the hardware provides isolation and source validation through the SMMU. The stream ID generated by the PCIe ports contain both the bus/device/function number as well as the port ID in its 3 most significant bits. Turn on ACS but disable all the peer-to-peer features. Signed-off-by: Zhang Fuxiang Signed-off-by: Xiao Cong Signed-off-by: Wang Yinfeng --- drivers/pci/quirks.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index cf8dc19954481..4f583c8c679af 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -5391,11 +5391,9 @@ static const struct pci_dev_acs_enabled { { PCI_VENDOR_ID_MUCSE, 0x1061, pci_quirk_mf_endpoint_acs }, { PCI_VENDOR_ID_MUCSE, 0x1c61, pci_quirk_mf_endpoint_acs }, /* Phytium Technology */ -#ifdef CONFIG_ARCH_PHYTIUM - { PCI_VENDOR_ID_PLX, PCI_ANY_ID, pci_quirk_xgene_acs }, - { PCI_VENDOR_ID_CDNS, PCI_ANY_ID, pci_quirk_xgene_acs }, -#endif /* CONFIG_ARCH_PHYTIUM */ - { PCI_VENDOR_ID_PHYTIUM, PCI_ANY_ID, pci_quirk_xgene_acs }, + { 0x10b5, PCI_ANY_ID, pci_quirk_xgene_acs }, + { 0x17cd, PCI_ANY_ID, pci_quirk_xgene_acs }, + { 0x1db7, PCI_ANY_ID, pci_quirk_xgene_acs }, { 0 } }; From b2a52d244a9e3430cd5d4525e1e2de98ea798679 Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Sat, 9 May 2026 14:59:03 +0800 Subject: [PATCH 73/81] pci: phytium: Restore PCIe config space after hotrest In the phytium ps2308 platforms, when host PCIe bridge operate hotreset, then host pcie bridge config register will restore default value. So before hotreset, it need save host PCIe bridge config register. Then after hotreset, it need restore save host PCIe bridge config register. Signed-off-by: Zhang Fuxiang Signed-off-by: Xiao Cong Signed-off-by: Wang Yinfeng --- drivers/pci/pci.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index cd18c4b955882..967d20dd4159a 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -48,6 +48,9 @@ EXPORT_SYMBOL_GPL(pci_power_names); int isa_dma_bridge_buggy; EXPORT_SYMBOL(isa_dma_bridge_buggy); #endif +#ifdef CONFIG_ARCH_PHYTIUM +#include +#endif int pci_pci_problems; EXPORT_SYMBOL(pci_pci_problems); @@ -5358,6 +5361,17 @@ void __weak pcibios_reset_secondary_bus(struct pci_dev *dev) */ int pci_bridge_secondary_bus_reset(struct pci_dev *dev) { +#ifdef CONFIG_ARCH_PHYTIUM + if (is_pd2308()) { + int ret = 0; + + pci_save_state(dev); + pcibios_reset_secondary_bus(dev); + ret = pci_bridge_wait_for_secondary_bus(dev, "bus reset"); + pci_restore_state(dev); + return ret; + } +#endif pcibios_reset_secondary_bus(dev); return pci_bridge_wait_for_secondary_bus(dev, "bus reset"); From 14a04085386fe1956ffc7a2cdf664999454dfb5b Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Sat, 9 May 2026 15:01:12 +0800 Subject: [PATCH 74/81] pci: phytium: Retry secondary-bus reset when link degrades Some Phytium-based systems can come up with reduced PCIe link speed or lane count after a hotreset. A second reset attempt often restores the original link characteristics. Signed-off-by: Zhang Fuxiang Signed-off-by: Li Tongfeng Signed-off-by: Wang Yinfeng --- drivers/pci/pci.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 967d20dd4159a..740fa8ca91247 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -5352,6 +5352,55 @@ void __weak pcibios_reset_secondary_bus(struct pci_dev *dev) pci_reset_secondary_bus(dev); } +#ifdef CONFIG_ARCH_PHYTIUM +/** + * phytium_pci_bridge_secondary_bus_reset - Reset the secondary bus + * on Phytium bridges. + * @dev: Bridge device + * + * Workaround: Perform a PCIe secondary-bus reset again when + * the link is degraded on the Phytium platform. + */ +static int phytium_pci_bridge_secondary_bus_reset(struct pci_dev *dev) +{ + u16 lnksta, reset_cnt = 0; + u16 cur_speed, cur_width; + u16 next_speed, next_width; + int ret; + + pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta); + cur_speed = lnksta & PCI_EXP_LNKSTA_CLS; + cur_width = (lnksta & PCI_EXP_LNKSTA_NLW) >> + PCI_EXP_LNKSTA_NLW_SHIFT; + +retry: + reset_cnt++; + pcibios_reset_secondary_bus(dev); + ret = pci_bridge_wait_for_secondary_bus(dev, "bus reset"); + + pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta); + next_speed = lnksta & PCI_EXP_LNKSTA_CLS; + next_width = (lnksta & PCI_EXP_LNKSTA_NLW) >> + PCI_EXP_LNKSTA_NLW_SHIFT; + + /* if link degraded, allow one more retry */ + if ((next_speed < cur_speed) || (next_width < cur_width)) { + if (reset_cnt >= 2) { + pci_err(dev, "phytium: link degraded - pre Gen%u/x%u post Gen%u/x%u\n", + cur_speed, cur_width, next_speed, next_width); + goto out; + } + + pci_info(dev, "phytium: link degraded - pre Gen%u/x%u post Gen%u/x%u, reset again\n", + cur_speed, cur_width, next_speed, next_width); + goto retry; + } + +out: + return ret; +} +#endif + /** * pci_bridge_secondary_bus_reset - Reset the secondary bus on a PCI bridge. * @dev: Bridge device @@ -5370,6 +5419,8 @@ int pci_bridge_secondary_bus_reset(struct pci_dev *dev) ret = pci_bridge_wait_for_secondary_bus(dev, "bus reset"); pci_restore_state(dev); return ret; + } else if (is_ps24080()) { + return phytium_pci_bridge_secondary_bus_reset(dev); } #endif pcibios_reset_secondary_bus(dev); From fd845f584468accd19e82bd88f53cdb03fb485e4 Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Sat, 9 May 2026 15:03:34 +0800 Subject: [PATCH 75/81] pci: phytium: Fix incorrect LNKSTA_DLLLA status on Phytium 24080 Soc On Phytium 24080 platform, reading the PCIe Link Status (LNKSTA) register through the standard PCIe capability interface may return an incorrect value for the Data Link Layer Link Active (DLLLA) bit. This causes the kernel to report wrong link state after a device is hot-unplugged. Signed-off-by: Zhang Fuxiang Signed-off-by: Li Tongfeng --- drivers/pci/hotplug/pciehp_ctrl.c | 2 +- drivers/pci/hotplug/pciehp_hpc.c | 15 ++++++++ drivers/pci/pci.c | 2 +- drivers/pci/pci.h | 60 +++++++++++++++++++++++-------- 4 files changed, 63 insertions(+), 16 deletions(-) diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index 4b105878aa044..1f3890ded6ad4 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -287,7 +287,7 @@ void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events) slot_name(ctrl)); #ifdef CONFIG_ARCH_PHYTIUM if (present && link_active) - phytium_clear_ctrl_prot(ctrl->pcie->port, PHYTIUM_PCIE_HOTPLUG); + phytium_pcie_ctrl_smc_op(ctrl->pcie->port, PHYTIUM_PCIE_HOTPLUG); #endif ctrl->request_result = pciehp_enable_slot(ctrl); break; diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index b0bccc4d0da28..4aa22caec02c1 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -26,6 +26,9 @@ #include "../pci.h" #include "pciehp.h" +#ifdef CONFIG_ARCH_PHYTIUM +#include +#endif static const struct dmi_system_id inband_presence_disabled_dmi_table[] = { /* @@ -241,6 +244,18 @@ int pciehp_check_link_active(struct controller *ctrl) if (ret == PCIBIOS_DEVICE_NOT_FOUND || PCI_POSSIBLE_ERROR(lnk_status)) return -ENODEV; +#ifdef CONFIG_ARCH_PHYTIUM + if (is_ps24080()) { + /* PS24080 sometimes reports link up even when it's down */ + static u16 lnksta; + + lnksta = phytium_pcie_ctrl_smc_op(pdev, PHYTIUM_PCIE_GET_LNKSTA); + if (lnksta >= 0) + lnk_status = (lnk_status & ~PCI_EXP_LNKSTA_DLLLA) | + (lnksta & PCI_EXP_LNKSTA_DLLLA); + } +#endif + ret = !!(lnk_status & PCI_EXP_LNKSTA_DLLLA); ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 740fa8ca91247..d0f2f8f0363cd 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -5343,7 +5343,7 @@ void pci_reset_secondary_bus(struct pci_dev *dev) pci_write_config_word(dev, PCI_BRIDGE_CONTROL, ctrl); #ifdef CONFIG_ARCH_PHYTIUM - phytium_clear_ctrl_prot(dev, PHYTIUM_PCIE_HOTRESET); + phytium_pcie_ctrl_smc_op(dev, PHYTIUM_PCIE_HOTRESET); #endif } diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index b75d8bf7bba3a..ffc03439b23a9 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -877,11 +877,14 @@ static inline pci_power_t mid_pci_get_power_state(struct pci_dev *pdev) #define PHYTIUM_PCIE_HOTRESET 0 #define PHYTIUM_PCIE_HOTPLUG 1 -#define PHYTIUM_PCI_VENDOR_ID 0x1DB7 +#define PHYTIUM_PCIE_GET_LNKSTA 2 #define PHYTIUM_PCI_CTRL_ID 0x0100 -#define PHYTIUM_PCIE_CLEAR_CTRL_PROT_SMC_FUNC_ID 0xC2000020 +#define PHYTIUM_PCIE_CTRL_SMC_FUNC_ID 0xC2000020 -static inline void phytium_clear_ctrl_prot(struct pci_dev *pdev, int op) +#define PHYTIUM_PCIE_LINKUP 3 +#define PHYTIUM_PCIE_LINKDOWN 0 + +static inline int phytium_pcie_ctrl_smc_op(struct pci_dev *pdev, int op) { int socket; u8 bus = pdev->bus->number; @@ -932,27 +935,56 @@ static inline void phytium_clear_ctrl_prot(struct pci_dev *pdev, int op) u16 vendor_id = pdev->vendor; u16 device_id = pdev->device; struct arm_smccc_res res; + const char *op_str; + u16 link_status = 0; u32 arg; - if (vendor_id != PHYTIUM_PCI_VENDOR_ID || + switch (op) { + case PHYTIUM_PCIE_HOTRESET: + op_str = "hotreset"; + break; + case PHYTIUM_PCIE_HOTPLUG: + op_str = "hotplug"; + break; + case PHYTIUM_PCIE_GET_LNKSTA: + op_str = "get-link-status"; + break; + default: + op_str = "unknown"; + break; + } + + if (vendor_id != PCI_VENDOR_ID_PHYTIUM || device_id != PHYTIUM_PCI_CTRL_ID || pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) - return; + return -ENOENT; socket = dev_to_node(&pdev->dev); if (socket < 0) { - pci_err(pdev, "Cannot find socket, stop clean pcie protection\n"); - return; + pci_err(pdev, "no socket found, stop calling SMC 0x%x (%s)\n", + PHYTIUM_PCIE_CTRL_SMC_FUNC_ID, op_str); + return -ENOENT; } arg = (socket << 16) | (bus << 8) | (device << 3) | function; - arm_smccc_smc(PHYTIUM_PCIE_CLEAR_CTRL_PROT_SMC_FUNC_ID, arg, op, 0, 0, 0, 0, 0, &res); - if (res.a0 != 0) - pci_err(pdev, "Error: Firmware call PCIE protection clear Failed: %d, sbdf: 0x%x\n", - (int)res.a0, arg); - else - pci_info(pdev, "%s : Clear pcie protection successfully\n", - op ? "HotPlug" : "HotReset"); + arm_smccc_smc(PHYTIUM_PCIE_CTRL_SMC_FUNC_ID, arg, op, 0, 0, 0, 0, 0, &res); + if (res.a0 != 0) { + pci_err(pdev, "Error: %s call SMC 0x%x failed (%d), sbdf: 0x%x\n", + op_str, PHYTIUM_PCIE_CTRL_SMC_FUNC_ID, + (int)res.a0, arg); + return -ENOENT; + } + pci_info(pdev, "%s: call SMC completed successfully\n", op_str); + + /* Update link_status bit if op == get-link-status */ + if (op == PHYTIUM_PCIE_GET_LNKSTA) { + if (res.a1 == PHYTIUM_PCIE_LINKUP) + link_status |= PCI_EXP_LNKSTA_DLLLA; + else if (res.a1 == PHYTIUM_PCIE_LINKDOWN) + link_status &= ~PCI_EXP_LNKSTA_DLLLA; + } + + return link_status; } #endif From e9bd8270e226f94476934ff60b37783914bfc2ba Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Sat, 9 May 2026 15:11:54 +0800 Subject: [PATCH 76/81] dt-bindings: add phytium,pe2201-pcie-ep.yaml file Add the device tree binding schema for phytium,pe2201-pcie-ep. Signed-off-by: Zhang Fuxiang Signed-off-by: Li Tongfeng Signed-off-by: Wang Yinfeng --- .../bindings/pci/phytium,pe2201-pcie-ep.yaml | 42 +++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 43 insertions(+) create mode 100644 Documentation/devicetree/bindings/pci/phytium,pe2201-pcie-ep.yaml diff --git a/Documentation/devicetree/bindings/pci/phytium,pe2201-pcie-ep.yaml b/Documentation/devicetree/bindings/pci/phytium,pe2201-pcie-ep.yaml new file mode 100644 index 0000000000000..49ec3e7ae7767 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/phytium,pe2201-pcie-ep.yaml @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/phytium,pe2201-pcie-ep.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Phytium PCIe endpoint controller + +maintainers: + - Li Tongfeng + +allOf: + - $ref: "pci-ep.yaml#" + +properties: + compatible: + const: phytium,pe2201-pcie-ep + + reg: + maxItems: 2 + + reg-names: + items: + - const: reg + - const: mem + +required: + - compatible + - reg + - reg-names + +examples: + - | + ep0: ep@0x31000000 { + compatible = "phytium,pe2201-pcie-ep"; + interrupts = <0x00000000 0x0000000a 0x00000004 0x00000000 0x0000000b 0x00000004>; + reg = <0x00000000 0x31000000 0x00000000 0x00010000 0x00000011 0x00000000 0x00000001 0x00000000 0x00000000 0x31100000 0x00000000 0x00001000>; + reg-names = "reg", "mem", "hpb"; + max-outbound-regions = <0x00000003>; + max-functions = [02]; + status = "disabled"; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 6e154688d6bf1..3574dace0c589 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17547,6 +17547,7 @@ F: Documentation/devicetree/bindings/gpio/phytium,gpio.yaml F: Documentation/devicetree/bindings/usb/phytium,usb2-2.0.yaml F: Documentation/devicetree/bindings/usb/phytium,usb2.yaml F: Documentation/devicetree/bindings/usb/phytium.role-sw.yaml +F: Documentation/devicetree/bindings/pci/phytium,pe2201-pcie-ep.yaml F: drivers/usb/phytium/* F: drivers/perf/phytium/phytium_ddr_pmu.c F: drivers/perf/phytium/phytium_msi_pmu.c From f9154e85d4c59e9d29634e63a2b31dafc5e7b6ed Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Sat, 9 May 2026 15:28:58 +0800 Subject: [PATCH 77/81] pci/pcie_phytium_ep: add EPC support for virtual network devices Add phytium ep driver DMA Controller support and add function_num_map to the configfs of EPC. Signed-off-by: Zhang Fuxiang Signed-off-by: Li Tongfeng Signed-off-by: Wang Yinfeng --- drivers/pci/controller/Kconfig | 1 + drivers/pci/controller/pcie-phytium-ep.c | 179 ++++++++++++++---- drivers/pci/controller/pcie-phytium-ep.h | 2 + .../pci/controller/pcie-phytium-register.h | 20 +- drivers/pci/endpoint/pci-ep-cfs.c | 37 ++++ drivers/pci/endpoint/pci-epc-core.c | 51 +++++ include/linux/pci-epc.h | 14 ++ 7 files changed, 262 insertions(+), 42 deletions(-) diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index 398259dd942f2..e0ce86d257ec3 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -217,6 +217,7 @@ config PCIE_MT7621 config PCIE_PHYTIUM_EP tristate "Phytium PCIe endpoint controller" + depends on ARCH_PHYTIUM depends on OF depends on PCI_ENDPOINT help diff --git a/drivers/pci/controller/pcie-phytium-ep.c b/drivers/pci/controller/pcie-phytium-ep.c index 7a9d6a4ab39cd..a00d8cd9db0d9 100644 --- a/drivers/pci/controller/pcie-phytium-ep.c +++ b/drivers/pci/controller/pcie-phytium-ep.c @@ -19,17 +19,23 @@ #include "pcie-phytium-ep.h" #include "pcie-phytium-register.h" -#define PHYTIUM_PCIE_RP_DRIVER_VERSION "1.1.1" +#define PHYTIUM_PCIE_EP_DRIVER_VERSION "1.1.1" #define PHYTIUM_PCIE_EP_IRQ_PCI_ADDR_NONE 0x0 #define PHYTIUM_PCIE_EP_IRQ_PCI_ADDR_LEGACY 0x1 +#define PHYTIUM_PCIE_EP_ADDR_LO_MASK 0xffffffff +#define PHYTIUM_PCIE_EP_DMA_CONTROL_VALUE 0x18017e1 +#define PHYTIUM_PCIE_EP_PCIE_INTERFACE_ID 0x0 +#define PHYTIUM_PCIE_EP_AXI_MASTER_INTERFACE_ID 0x4 +#define PHYTIUM_PCIE_EP_DMA_SHARE_ACCESS 0x3 + static int phytium_pcie_ep_write_header(struct pci_epc *epc, unsigned char fn, u8 vfn, struct pci_epf_header *hdr) { struct phytium_pcie_ep *priv = epc_get_drvdata(epc); u16 tmp = 0; - + fn++; phytium_pcie_writew(priv, fn, PHYTIUM_PCI_VENDOR_ID, hdr->vendorid); phytium_pcie_writew(priv, fn, PHYTIUM_PCI_DEVICE_ID, hdr->deviceid); phytium_pcie_writeb(priv, fn, PHYTIUM_PCI_REVISION_ID, hdr->revid); @@ -53,71 +59,75 @@ static int phytium_pcie_ep_write_header(struct pci_epc *epc, unsigned char fn, u } static int phytium_pcie_ep_set_bar(struct pci_epc *epc, u8 fn, u8 vfn, - struct pci_epf_bar *epf_bar) + struct pci_epf_bar *epf_bar) { struct phytium_pcie_ep *priv = epc_get_drvdata(epc); - u64 sz = 0, sz_mask, atr_size; + u64 base, sz_mask, atr_size, sz = 0; int flags = epf_bar->flags; - u32 setting, src_addr0, src_addr1, trsl_addr0, trsl_addr1, trsl_param; enum pci_barno barno = epf_bar->barno; struct pci_epc_mem *mem = epc->mem; + u32 setting, src_addr0, trsl_param; + fn++; if ((flags & PCI_BASE_ADDRESS_MEM_TYPE_64) && (barno & 1)) { dev_err(&epc->dev, "bar %d do not support mem64\n", barno); return -EINVAL; } - if (barno & 1) { - dev_err(&epc->dev, "not support bar 1/3/5\n"); - return -EINVAL; - } - dev_dbg(epc->dev.parent, "set bar%d mapping address 0x%pa size 0x%lx\n", + if (barno == 0 || barno == 3 || barno == 5) + return 0; + + dev_dbg(&epc->dev, "set bar%d mapping address 0x%pa size 0x%zx\n", barno, &(epf_bar->phys_addr), epf_bar->size); if ((flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) { setting = BAR_IO_TYPE; - sz = max_t(size_t, epf_bar->size, BAR_IO_MIN_APERTURE); - sz = 1 << fls64(sz - 1); - sz_mask = ~(sz - 1); + sz_mask = ~(epf_bar->size / 2 - 1); setting |= sz_mask; trsl_param = TRSL_ID_IO; } else { setting = BAR_MEM_TYPE; - sz = max_t(size_t, epf_bar->size, BAR_MEM_MIN_APERTURE); - sz = 1 << fls64(sz - 1); - sz_mask = ~(sz - 1); - setting |= lower_32_bits(sz_mask); if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) setting |= BAR_MEM_64BIT; if (flags & PCI_BASE_ADDRESS_MEM_PREFETCH) setting |= BAR_MEM_PREFETCHABLE; - + sz_mask = ~(epf_bar->size / 2 - 1); + setting |= lower_32_bits(sz_mask); trsl_param = TRSL_ID_MASTER; } + sz = max_t(size_t, epf_bar->size / 2, BAR_MEM_MIN_APERTURE); + sz = 1 << fls64(sz - 1); + sz = ALIGN(sz, mem->window.page_size); + atr_size = fls64(sz - 1) - 1; + + base = 0xE00 + fn * 0x40 + (barno - 1) * 0x8; + phytium_hpb_writel(priv, base, upper_32_bits(epf_bar->phys_addr)); + phytium_hpb_writel(priv, (base + 0x4), (lower_32_bits(epf_bar->phys_addr) | (atr_size))); + phytium_pcie_writel(priv, fn, PHYTIUM_PCI_BAR(barno), setting); if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) - phytium_pcie_writel(priv, fn, PHYTIUM_PCI_BAR(barno + 1), + phytium_pcie_writel(priv, fn, PHYTIUM_PCI_BAR((barno + 1)), upper_32_bits(sz_mask)); - dev_dbg(epc->dev.parent, "set bar%d mapping address 0x%pa size 0x%llx 0x%x\n", - barno, &(epf_bar->phys_addr), sz, lower_32_bits(epf_bar->phys_addr)); - sz = ALIGN(sz, mem->window.page_size); - atr_size = fls64(sz - 1) - 1; + + if (barno == 2) { + phytium_pcie_writel(priv, fn, PHYTIUM_PCI_BAR((barno + 1)), 0); + return 0; + } src_addr0 = ATR_IMPL | ((atr_size & ATR_SIZE_MASK) << ATR_SIZE_SHIFT); - src_addr1 = 0; - trsl_addr0 = (lower_32_bits(epf_bar->phys_addr) & TRSL_ADDR_32_12_MASK); - trsl_addr1 = upper_32_bits(epf_bar->phys_addr); phytium_pcie_writel(priv, fn, PHYTIUM_PCI_WIN0_SRC_ADDR0(barno), src_addr0); phytium_pcie_writel(priv, fn, PHYTIUM_PCI_WIN0_SRC_ADDR1(barno), - src_addr1); + 0x0); + phytium_pcie_writel(priv, fn, PHYTIUM_PCI_WIN0_TRSL_ADDR0(barno), - trsl_addr0); + (barno == 1) ? lower_32_bits(epf_bar->phys_addr) : 0); phytium_pcie_writel(priv, fn, PHYTIUM_PCI_WIN0_TRSL_ADDR1(barno), - trsl_addr1); + (barno == 1) ? upper_32_bits(epf_bar->phys_addr) : barno << 10); + phytium_pcie_writel(priv, fn, PHYTIUM_PCI_WIN0_TRSL_PARAM(barno), trsl_param); @@ -130,7 +140,7 @@ static void phytium_pcie_ep_clear_bar(struct pci_epc *epc, u8 fn, u8 vfn, struct phytium_pcie_ep *priv = epc_get_drvdata(epc); int flags = epf_bar->flags; enum pci_barno barno = epf_bar->barno; - + fn++; phytium_pcie_writel(priv, fn, PHYTIUM_PCI_BAR(barno), 0); if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) phytium_pcie_writel(priv, fn, PHYTIUM_PCI_BAR(barno + 1), 0); @@ -151,7 +161,7 @@ static int phytium_pcie_ep_map_addr(struct pci_epc *epc, u8 fn, u8 vfn, u64 sz = 0; u32 r; struct pci_epc_mem *mem = epc->mem; - + fn++; r = find_first_zero_bit(&priv->ob_region_map, sizeof(priv->ob_region_map) * BITS_PER_LONG); if (r >= priv->max_regions) { @@ -192,7 +202,7 @@ static void phytium_pcie_ep_unmap_addr(struct pci_epc *epc, u8 fn, u8 vfn, { struct phytium_pcie_ep *priv = epc_get_drvdata(epc); u32 r; - + fn++; for (r = 0; r < priv->max_regions; r++) if (priv->ob_addr[r] == addr) break; @@ -216,7 +226,7 @@ static int phytium_pcie_ep_set_msi(struct pci_epc *epc, u8 fn, u8 vfn, u8 mmc) { struct phytium_pcie_ep *priv = epc_get_drvdata(epc); u16 flags = 0; - + fn++; flags = (mmc & MSI_NUM_MASK) << MSI_NUM_SHIFT; flags &= ~MSI_MASK_SUPPORT; phytium_pcie_writew(priv, fn, PHYTIUM_PCI_INTERRUPT_PIN, flags); @@ -229,7 +239,7 @@ static int phytium_pcie_ep_get_msi(struct pci_epc *epc, u8 fn, u8 vfn) struct phytium_pcie_ep *priv = epc_get_drvdata(epc); u16 flags, mme; u32 cap = PHYTIUM_PCI_CF_MSI_BASE; - + fn++; flags = phytium_pcie_readw(priv, fn, cap + PCI_MSI_FLAGS); if (!(flags & PCI_MSI_FLAGS_ENABLE)) return -EINVAL; @@ -247,7 +257,7 @@ static int phytium_pcie_ep_send_msi_irq(struct phytium_pcie_ep *priv, u8 fn, u8 msi_count; u64 pci_addr, pci_addr_mask = IRQ_MAPPING_SIZE - 1; u32 src_addr0, src_addr1, trsl_addr0, trsl_addr1, trsl_param, atr_size; - + fn++; flags = phytium_pcie_readw(priv, fn, cap + PCI_MSI_FLAGS); if (!(flags & PCI_MSI_FLAGS_ENABLE)) return -EINVAL; @@ -302,7 +312,7 @@ static int phytium_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn, u8 vfn, u16 interrupt_num) { struct phytium_pcie_ep *priv = epc_get_drvdata(epc); - + fn++; switch (type) { case PCI_EPC_IRQ_MSI: return phytium_pcie_ep_send_msi_irq(priv, fn, interrupt_num); @@ -326,6 +336,68 @@ static int phytium_pcie_ep_start(struct pci_epc *epc) return 0; } +static int phytium_pcie_ep_start_dma(struct pci_epc *epc, u8 func_no, u64 cpu_addr, u64 pci_addr, + size_t size, u8 mode) +{ + u32 value; + struct phytium_pcie_ep *priv = epc_get_drvdata(epc); + + func_no++; + dev_dbg(&epc->dev, "%s func_no %d cpu_addr %llu pci_addr %llu size %zu mode %d\n", + __func__, func_no, cpu_addr, pci_addr, size, mode); + + u64 base = 0xE00 + func_no * 0x40; + + phytium_hpb_writel(priv, base, upper_32_bits(cpu_addr)); + phytium_hpb_writel(priv, (base + 0x4), + (lower_32_bits(cpu_addr) & (~0x3f)) | (fls64(size - 1))); + cpu_addr = 0x40000000000 | (cpu_addr & 0x3f); + + phytium_pcie_writel(priv, func_no, DMA_SHARE_ACCESS(mode), + PHYTIUM_PCIE_EP_DMA_SHARE_ACCESS); + if (mode == DMA_READ_ENGINE) { + phytium_pcie_writel(priv, func_no, DMA_SRCPARAM(mode), + PHYTIUM_PCIE_EP_PCIE_INTERFACE_ID); + phytium_pcie_writel(priv, func_no, DMA_DSTPARAM(mode), + PHYTIUM_PCIE_EP_AXI_MASTER_INTERFACE_ID); + value = pci_addr & PHYTIUM_PCIE_EP_ADDR_LO_MASK; + phytium_pcie_writel(priv, func_no, DMA_SRCADDR_LO(mode), value); + value = (pci_addr >> 32); + phytium_pcie_writel(priv, func_no, DMA_SRCADDR_UP(mode), value); + + value = cpu_addr & PHYTIUM_PCIE_EP_ADDR_LO_MASK; + phytium_pcie_writel(priv, func_no, DMA_DESTADDR_LO(mode), value); + value = ((cpu_addr >> 32) | 0x400); + phytium_pcie_writel(priv, func_no, DMA_DESTADDR_UP(mode), value); + } else { + phytium_pcie_writel(priv, func_no, DMA_SRCPARAM(mode), + PHYTIUM_PCIE_EP_AXI_MASTER_INTERFACE_ID); + phytium_pcie_writel(priv, func_no, DMA_DSTPARAM(mode), + PHYTIUM_PCIE_EP_PCIE_INTERFACE_ID); + value = cpu_addr & PHYTIUM_PCIE_EP_ADDR_LO_MASK; + phytium_pcie_writel(priv, func_no, DMA_SRCADDR_LO(mode), value); + value = ((cpu_addr >> 32) | 0x400); + phytium_pcie_writel(priv, func_no, DMA_SRCADDR_UP(mode), value); + + value = pci_addr & PHYTIUM_PCIE_EP_ADDR_LO_MASK; + phytium_pcie_writel(priv, func_no, DMA_DESTADDR_LO(mode), value); + value = (pci_addr >> 32); + phytium_pcie_writel(priv, func_no, DMA_DESTADDR_UP(mode), value); + } + phytium_pcie_writel(priv, func_no, DMA_LENGTH(mode), size); + phytium_pcie_writel(priv, func_no, DMA_CONTROL(mode), PHYTIUM_PCIE_EP_DMA_CONTROL_VALUE); + + return 0; +} + +static int phytium_pcie_ep_dma_status(struct pci_epc *epc, u8 func_no, u8 mode) +{ + struct phytium_pcie_ep *priv = epc_get_drvdata(epc); + + func_no++; + return phytium_pcie_readl(priv, func_no, DMA_STATUS(mode)); +} + static const struct pci_epc_ops phytium_pcie_epc_ops = { .write_header = phytium_pcie_ep_write_header, .set_bar = phytium_pcie_ep_set_bar, @@ -336,10 +408,10 @@ static const struct pci_epc_ops phytium_pcie_epc_ops = { .get_msi = phytium_pcie_ep_get_msi, .raise_irq = phytium_pcie_ep_raise_irq, .start = phytium_pcie_ep_start, + .start_dma = phytium_pcie_ep_start_dma, + .dma_status = phytium_pcie_ep_dma_status, }; - - static int phytium_pcie_ep_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -348,11 +420,33 @@ static int phytium_pcie_ep_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; struct pci_epc *epc; int ret = 0, value; + const char *compatible; + u32 hpb_c0_pref_base_limit; + u32 hpb_c0_pref_base_limit_up32; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; + compatible = of_get_property(np, "compatible", NULL); + if (!compatible) { + dev_err(dev, "Compatible property not found\n"); + return -EINVAL; + } + + if (strcmp(compatible, "phytium,pd2008-pcie-ep") == 0) { + hpb_c0_pref_base_limit = PHYTIUM_PD2008_HPB_C0_PREF_BASE_LIMIT; + hpb_c0_pref_base_limit_up32 = + PHYTIUM_PD2008_HPB_C0_PREF_BASE_LIMIT_UP32; + } else if (strcmp(compatible, "phytium,pe2201-pcie-ep") == 0) { + hpb_c0_pref_base_limit = PHYTIUM_PE2201_HPB_C0_PREF_BASE_LIMIT; + hpb_c0_pref_base_limit_up32 = + PHYTIUM_PE2201_HPB_C0_PREF_BASE_LIMIT_UP32; + } else { + dev_err(dev, "Unsupported chip model\n"); + return -ENODEV; + } + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "reg"); priv->reg_base = devm_ioremap_resource(dev, res); if (IS_ERR(priv->reg_base)) { @@ -397,6 +491,8 @@ static int phytium_pcie_ep_probe(struct platform_device *pdev) priv->epc = epc; epc_set_drvdata(epc, priv); + priv->pdev = pdev; + if (of_property_read_u8(np, "max-functions", &epc->max_functions) < 0) epc->max_functions = 1; dev_info(dev, "%s epc->max_functions %d\n", __func__, epc->max_functions); @@ -424,13 +520,13 @@ static int phytium_pcie_ep_probe(struct platform_device *pdev) & C0_PREF_BASE_MASK) << C0_PREF_BASE_SHIFT; value |= (((lower_32_bits(priv->mem_res->end) >> C0_PREF_VALUE_SHIFT) & C0_PREF_LIMIT_MASK) << C0_PREF_LIMIT_SHIFT); - phytium_hpb_writel(priv, PHYTIUM_HPB_C0_PREF_BASE_LIMIT, value); + phytium_hpb_writel(priv, hpb_c0_pref_base_limit, value); value = ((upper_32_bits(priv->mem_res->start) >> C0_PREF_UP32_VALUE_SHIFT) & C0_PREF_BASE_UP32_MASK) << C0_PREF_BASE_UP32_SHIFT; value |= (((upper_32_bits(priv->mem_res->end) >> C0_PREF_UP32_VALUE_SHIFT) & C0_PREF_LIMIT_UP32_MASK) << C0_PREF_LIMIT_UP32_SHIFT); - phytium_hpb_writel(priv, PHYTIUM_HPB_C0_PREF_BASE_LIMIT_UP32, value); + phytium_hpb_writel(priv, hpb_c0_pref_base_limit_up32, value); dev_dbg(dev, "exit %s successful\n", __func__); return 0; @@ -453,6 +549,7 @@ static int phytium_pcie_ep_remove(struct platform_device *pdev) static const struct of_device_id phytium_pcie_ep_of_match[] = { { .compatible = "phytium,pd2008-pcie-ep" }, + { .compatible = "phytium,pe2201-pcie-ep" }, { }, }; @@ -468,6 +565,6 @@ MODULE_DEVICE_TABLE(of, phytium_pcie_ep_of_match); module_platform_driver(phytium_pcie_ep_driver); MODULE_LICENSE("GPL"); -MODULE_VERSION(PHYTIUM_PCIE_RP_DRIVER_VERSION); +MODULE_VERSION(PHYTIUM_PCIE_EP_DRIVER_VERSION); MODULE_AUTHOR("Yang Xun "); MODULE_DESCRIPTION("Phytium PCIe Controller Endpoint driver"); diff --git a/drivers/pci/controller/pcie-phytium-ep.h b/drivers/pci/controller/pcie-phytium-ep.h index 1c38181fc19d9..bb5f67b57c086 100644 --- a/drivers/pci/controller/pcie-phytium-ep.h +++ b/drivers/pci/controller/pcie-phytium-ep.h @@ -23,6 +23,8 @@ struct phytium_pcie_ep { unsigned long irq_pci_addr; u8 irq_pci_fn; struct pci_epc *epc; + + struct platform_device *pdev; }; static inline void diff --git a/drivers/pci/controller/pcie-phytium-register.h b/drivers/pci/controller/pcie-phytium-register.h index 458df0014504e..5f98a93e4b8ab 100644 --- a/drivers/pci/controller/pcie-phytium-register.h +++ b/drivers/pci/controller/pcie-phytium-register.h @@ -63,13 +63,16 @@ #define PHYTIUM_PCI_CF_MSI_BASE 0x10e0 #define PHYTIUM_PCI_CF_MSI_CONTROL 0x10e2 -#define PHYTIUM_HPB_C0_PREF_BASE_LIMIT 0xa30 +#define PHYTIUM_PD2008_HPB_C0_PREF_BASE_LIMIT 0xa30 +#define PHYTIUM_PE2201_HPB_C0_PREF_BASE_LIMIT 0xa40 #define C0_PREF_LIMIT_MASK 0xfff #define C0_PREF_LIMIT_SHIFT 20 #define C0_PREF_BASE_MASK 0xfff #define C0_PREF_BASE_SHIFT 4 #define C0_PREF_VALUE_SHIFT 20 #define PHYTIUM_HPB_C0_PREF_BASE_LIMIT_UP32 0xa34 +#define PHYTIUM_PD2008_HPB_C0_PREF_BASE_LIMIT_UP32 0xa34 +#define PHYTIUM_PE2201_HPB_C0_PREF_BASE_LIMIT_UP32 0xa44 #define C0_PREF_LIMIT_UP32_MASK 0xff #define C0_PREF_LIMIT_UP32_SHIFT 8 #define C0_PREF_BASE_UP32_MASK 0xff @@ -77,4 +80,19 @@ #define C0_PREF_UP32_VALUE_SHIFT 0 #endif +#define DMA_READ_ENGINE 0 +#define DMA_WRITE_ENGINE 1 +#define DMA_ENGINE0_BASE 0x400 + +#define DMA_SHARE_ACCESS(engnum) (DMA_ENGINE0_BASE + 0x40 * engnum + 0x28) // 0x468 +#define DMA_SRCPARAM(engnum) (DMA_ENGINE0_BASE + 0x40 * engnum + 0x0) // 0x440 +#define DMA_DSTPARAM(engnum) (DMA_ENGINE0_BASE + 0x40 * engnum + 0x4) // 0x444 +#define DMA_SRCADDR_LO(engnum) (DMA_ENGINE0_BASE + 0x40 * engnum + 0x8) // 0x448 +#define DMA_SRCADDR_UP(engnum) (DMA_ENGINE0_BASE + 0x40 * engnum + 0xc) // 0x44C +#define DMA_DESTADDR_LO(engnum) (DMA_ENGINE0_BASE + 0x40 * engnum + 0x10) // 0x450 +#define DMA_DESTADDR_UP(engnum) (DMA_ENGINE0_BASE + 0x40 * engnum + 0x14) // 0x454 +#define DMA_LENGTH(engnum) (DMA_ENGINE0_BASE + 0x40 * engnum + 0x18) // 0x458 +#define DMA_CONTROL(engnum) (DMA_ENGINE0_BASE + 0x40 * engnum + 0x1c) // 0x45C +#define DMA_STATUS(engnum) (DMA_ENGINE0_BASE + 0x40 * engnum + 0x20) // 0x460 + diff --git a/drivers/pci/endpoint/pci-ep-cfs.c b/drivers/pci/endpoint/pci-ep-cfs.c index 5b64203f100fc..6d43a640206b3 100644 --- a/drivers/pci/endpoint/pci-ep-cfs.c +++ b/drivers/pci/endpoint/pci-ep-cfs.c @@ -31,6 +31,10 @@ struct pci_epc_group { struct config_group group; struct pci_epc *epc; bool start; + +#ifdef CONFIG_ARCH_PHYTIUM + unsigned long function_num_map; +#endif }; static inline struct pci_epf_group *to_pci_epf_group(struct config_item *item) @@ -204,8 +208,41 @@ static ssize_t pci_epc_start_show(struct config_item *item, char *page) CONFIGFS_ATTR(pci_epc_, start); +#ifdef CONFIG_ARCH_PHYTIUM +static ssize_t pci_epc_function_num_map_store(struct config_item *item, const char *page, + size_t len) +{ + int ret; + unsigned long function_num_map; + struct pci_epc *epc; + struct pci_epc_group *epc_group = to_pci_epc_group(item); + + epc = epc_group->epc; + + ret = kstrtoul(page, 10, &function_num_map); + if (ret) + return ret; + + epc_group->function_num_map = function_num_map; + epc->function_num_map = function_num_map; + + return len; +} + +static ssize_t pci_epc_function_num_map_show(struct config_item *item, char *page) +{ + return sprintf(page, "%ld\n", + to_pci_epc_group(item)->function_num_map); +} + +CONFIGFS_ATTR(pci_epc_, function_num_map); +#endif + static struct configfs_attribute *pci_epc_attrs[] = { &pci_epc_attr_start, +#ifdef CONFIG_ARCH_PHYTIUM + &pci_epc_attr_function_num_map, +#endif NULL, }; diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index 3a82c6a613a3c..75bd16507e2cc 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -15,6 +15,9 @@ #include static struct class *pci_epc_class; +#ifdef CONFIG_ARCH_PHYTIUM +static DEFINE_SPINLOCK(epc_dma_lock); +#endif static void devm_pci_epc_release(struct device *dev, void *res) { @@ -206,6 +209,54 @@ int pci_epc_start(struct pci_epc *epc) } EXPORT_SYMBOL_GPL(pci_epc_start); +/** + * pci_epc_start_dma() - start dma with phytium-d2000 ep + * @cpu_addr: ep local mem addr + * @pci_addr: pci doamin addr,which means rc mem addr + * @size: transfer total data size bytes + * @mode: which direct the dma work,EP_TO_RC/RC_TO_EP + */ +#ifdef CONFIG_ARCH_PHYTIUM +int pci_epc_start_dma(struct pci_epc *epc, u8 func_no, u64 cpu_addr, u64 pci_addr, + size_t size, u8 mode) +{ + int ret; + unsigned long flags; + + if (IS_ERR(epc)) + return -EINVAL; + + if (!epc->ops->start_dma) + return 0; + + spin_lock_irqsave(&epc_dma_lock, flags); + ret = epc->ops->start_dma(epc, func_no, cpu_addr, pci_addr, size, mode); + spin_unlock_irqrestore(&epc_dma_lock, flags); + + return ret; +} +EXPORT_SYMBOL_GPL(pci_epc_start_dma); + +int pci_epc_dma_status(struct pci_epc *epc, u8 func_no, u8 mode) +{ + int ret; + unsigned long flags; + + if (IS_ERR(epc)) + return -EINVAL; + + if (!epc->ops->dma_status) + return 0; + + spin_lock_irqsave(&epc_dma_lock, flags); + ret = epc->ops->dma_status(epc, func_no, mode); + spin_unlock_irqrestore(&epc_dma_lock, flags); + + return ret; +} +EXPORT_SYMBOL_GPL(pci_epc_dma_status); +#endif + /** * pci_epc_raise_irq() - interrupt the host system * @epc: the EPC device which has to interrupt the host diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index 5cb6940310729..d700332362950 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -86,6 +86,13 @@ struct pci_epc_ops { u32 *msi_addr_offset); int (*start)(struct pci_epc *epc); void (*stop)(struct pci_epc *epc); + +#ifdef CONFIG_ARCH_PHYTIUM + int (*start_dma)(struct pci_epc *epc, u8 func_no, u64 cpu_addr, u64 pci_addr, + size_t size, u8 mode); + int (*dma_status)(struct pci_epc *epc, u8 func_no, u8 mode); +#endif + const struct pci_epc_features* (*get_features)(struct pci_epc *epc, u8 func_no, u8 vfunc_no); struct module *owner; @@ -251,4 +258,11 @@ void __iomem *pci_epc_mem_alloc_addr(struct pci_epc *epc, phys_addr_t *phys_addr, size_t size); void pci_epc_mem_free_addr(struct pci_epc *epc, phys_addr_t phys_addr, void __iomem *virt_addr, size_t size); + +#ifdef CONFIG_ARCH_PHYTIUM +int pci_epc_start_dma(struct pci_epc *epc, u8 func_no, u64 cpu_addr, + u64 pci_addr, size_t size, u8 mode); +int pci_epc_dma_status(struct pci_epc *epc, u8 func_no, u8 mode); +#endif + #endif /* __LINUX_PCI_EPC_H */ From cbeebd85e510371db821f552c2614322cbbdaf34 Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Sat, 9 May 2026 15:31:29 +0800 Subject: [PATCH 78/81] pci: phytium: Fix middle bridge I/O BAR address error issue On PD2308 platform, it only setup root bridge I/O BAR address, and don't modify other bridge I/O BAR address after root. Signed-off-by: Zhang Fuxiang Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng --- drivers/pci/pci.h | 156 +++++++++++++++------------------------- drivers/pci/setup-bus.c | 5 -- 2 files changed, 57 insertions(+), 104 deletions(-) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index ffc03439b23a9..c3d912cb71c01 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -886,105 +886,63 @@ static inline pci_power_t mid_pci_get_power_state(struct pci_dev *pdev) static inline int phytium_pcie_ctrl_smc_op(struct pci_dev *pdev, int op) { - int socket; - u8 bus = pdev->bus->number; - u8 device = PCI_SLOT(pdev->devfn); - u8 function = PCI_FUNC(pdev->devfn); - u16 vendor_id = pdev->vendor; - u16 device_id = pdev->device; - struct arm_smccc_res res; - u32 arg; - - if (vendor_id != PHYTIUM_PCI_VENDOR_ID || - device_id != PHYTIUM_PCI_CTRL_ID || - pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) - return; - - socket = dev_to_node(&pdev->dev); - if (socket < 0) { - pci_err(pdev, "Cannot find socket, stop clean pcie protection\n"); - return; - } - - arg = (socket << 16) | (bus << 8) | (device << 3) | function; - arm_smccc_smc(PHYTIUM_PCIE_CLEAR_CTRL_PROT_SMC_FUNC_ID, arg, op, 0, 0, 0, 0, 0, &res); - if (res.a0 != 0) - pci_err(pdev, "Error: Firmware call PCIE protection clear Failed: %d, sbdf: 0x%x\n", - (int)res.a0, arg); - else - pci_info(pdev, "%s : Clear pcie protection successfully\n", - op ? "HotPlug" : "HotReset"); -} -#endif - -#ifdef CONFIG_ARCH_PHYTIUM -#include - -#define PHYTIUM_PCIE_HOTRESET 0 -#define PHYTIUM_PCIE_HOTPLUG 1 -#define PHYTIUM_PCI_VENDOR_ID 0x1DB7 -#define PHYTIUM_PCI_CTRL_ID 0x0100 -#define PHYTIUM_PCIE_CLEAR_CTRL_PROT_SMC_FUNC_ID 0xC2000020 - -static inline void phytium_clear_ctrl_prot(struct pci_dev *pdev, int op) -{ - int socket; - u8 bus = pdev->bus->number; - u8 device = PCI_SLOT(pdev->devfn); - u8 function = PCI_FUNC(pdev->devfn); - u16 vendor_id = pdev->vendor; - u16 device_id = pdev->device; - struct arm_smccc_res res; - const char *op_str; - u16 link_status = 0; - u32 arg; - - switch (op) { - case PHYTIUM_PCIE_HOTRESET: - op_str = "hotreset"; - break; - case PHYTIUM_PCIE_HOTPLUG: - op_str = "hotplug"; - break; - case PHYTIUM_PCIE_GET_LNKSTA: - op_str = "get-link-status"; - break; - default: - op_str = "unknown"; - break; - } - - if (vendor_id != PCI_VENDOR_ID_PHYTIUM || - device_id != PHYTIUM_PCI_CTRL_ID || - pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) - return -ENOENT; - - socket = dev_to_node(&pdev->dev); - if (socket < 0) { - pci_err(pdev, "no socket found, stop calling SMC 0x%x (%s)\n", - PHYTIUM_PCIE_CTRL_SMC_FUNC_ID, op_str); - return -ENOENT; - } - - arg = (socket << 16) | (bus << 8) | (device << 3) | function; - arm_smccc_smc(PHYTIUM_PCIE_CTRL_SMC_FUNC_ID, arg, op, 0, 0, 0, 0, 0, &res); - if (res.a0 != 0) { - pci_err(pdev, "Error: %s call SMC 0x%x failed (%d), sbdf: 0x%x\n", - op_str, PHYTIUM_PCIE_CTRL_SMC_FUNC_ID, - (int)res.a0, arg); - return -ENOENT; - } - pci_info(pdev, "%s: call SMC completed successfully\n", op_str); - - /* Update link_status bit if op == get-link-status */ - if (op == PHYTIUM_PCIE_GET_LNKSTA) { - if (res.a1 == PHYTIUM_PCIE_LINKUP) - link_status |= PCI_EXP_LNKSTA_DLLLA; - else if (res.a1 == PHYTIUM_PCIE_LINKDOWN) - link_status &= ~PCI_EXP_LNKSTA_DLLLA; - } - - return link_status; + int socket; + u8 bus = pdev->bus->number; + u8 device = PCI_SLOT(pdev->devfn); + u8 function = PCI_FUNC(pdev->devfn); + u16 vendor_id = pdev->vendor; + u16 device_id = pdev->device; + struct arm_smccc_res res; + const char *op_str; + u16 link_status = 0; + u32 arg; + + switch (op) { + case PHYTIUM_PCIE_HOTRESET: + op_str = "hotreset"; + break; + case PHYTIUM_PCIE_HOTPLUG: + op_str = "hotplug"; + break; + case PHYTIUM_PCIE_GET_LNKSTA: + op_str = "get-link-status"; + break; + default: + op_str = "unknown"; + break; + } + + if (vendor_id != PCI_VENDOR_ID_PHYTIUM || + device_id != PHYTIUM_PCI_CTRL_ID || + pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) + return -ENOENT; + + socket = dev_to_node(&pdev->dev); + if (socket < 0) { + pci_err(pdev, "no socket found, stop calling SMC 0x%x (%s)\n", + PHYTIUM_PCIE_CTRL_SMC_FUNC_ID, op_str); + return -ENOENT; + } + + arg = (socket << 16) | (bus << 8) | (device << 3) | function; + arm_smccc_smc(PHYTIUM_PCIE_CTRL_SMC_FUNC_ID, arg, op, 0, 0, 0, 0, 0, &res); + if (res.a0 != 0) { + pci_err(pdev, "Error: %s call SMC 0x%x failed (%d), sbdf: 0x%x\n", + op_str, PHYTIUM_PCIE_CTRL_SMC_FUNC_ID, + (int)res.a0, arg); + return -ENOENT; + } + pci_info(pdev, "%s: call SMC completed successfully\n", op_str); + + /* Update link_status bit if op == get-link-status */ + if (op == PHYTIUM_PCIE_GET_LNKSTA) { + if (res.a1 == PHYTIUM_PCIE_LINKUP) + link_status |= PCI_EXP_LNKSTA_DLLLA; + else if (res.a1 == PHYTIUM_PCIE_LINKDOWN) + link_status &= ~PCI_EXP_LNKSTA_DLLLA; + } + + return link_status; } #endif diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 195bb8d1388a0..094e6bb3ddc6c 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -618,11 +618,6 @@ static void pci_setup_bridge_io(struct pci_dev *bridge) "io-upper", &io_base_limit)) pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, io_base_limit); } - if (bridge->dev.parent->parent) { - if (!fwnode_property_read_u32(dev_fwnode(bridge->dev.parent->parent), - "io-upper", &io_base_limit)) - pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, io_base_limit); - } #endif } From 8209bf10256a54ff3d42180593f8c0d91115021a Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Wed, 13 May 2026 13:47:00 +0800 Subject: [PATCH 79/81] Revert "PCI: Add ACS quirk for phytium root and switch ports" This reverts commit 6dd7290d4f0301997ae2903d77efc82b38701912. Signed-off-by: Zhang Fuxiang --- drivers/pci/quirks.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 4f583c8c679af..cf8dc19954481 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -5391,9 +5391,11 @@ static const struct pci_dev_acs_enabled { { PCI_VENDOR_ID_MUCSE, 0x1061, pci_quirk_mf_endpoint_acs }, { PCI_VENDOR_ID_MUCSE, 0x1c61, pci_quirk_mf_endpoint_acs }, /* Phytium Technology */ - { 0x10b5, PCI_ANY_ID, pci_quirk_xgene_acs }, - { 0x17cd, PCI_ANY_ID, pci_quirk_xgene_acs }, - { 0x1db7, PCI_ANY_ID, pci_quirk_xgene_acs }, +#ifdef CONFIG_ARCH_PHYTIUM + { PCI_VENDOR_ID_PLX, PCI_ANY_ID, pci_quirk_xgene_acs }, + { PCI_VENDOR_ID_CDNS, PCI_ANY_ID, pci_quirk_xgene_acs }, +#endif /* CONFIG_ARCH_PHYTIUM */ + { PCI_VENDOR_ID_PHYTIUM, PCI_ANY_ID, pci_quirk_xgene_acs }, { 0 } }; From 2564b288b122eeacedca8b09674fb563a28b9708 Mon Sep 17 00:00:00 2001 From: Xia Qian Date: Fri, 15 May 2026 11:38:14 +0800 Subject: [PATCH 80/81] qspi: phytium: Use the corresponding configurations for each chip Solve the problem of abnormal reading and writing of data when simultaneously accessing flash chips of different capacities. Mainline: Open-Source Signed-off-by: Peng Min Signed-off-by: Wang Yinfeng Signed-off-by: Xia Qian --- drivers/spi/spi-phytium-qspi.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi-phytium-qspi.c b/drivers/spi/spi-phytium-qspi.c index 25c4af62a1cf5..579f98a1073aa 100644 --- a/drivers/spi/spi-phytium-qspi.c +++ b/drivers/spi/spi-phytium-qspi.c @@ -181,8 +181,8 @@ struct phytium_qspi { u8 fnum; bool nodirmap; - u32 wr_cfg_reg; - u32 rd_cfg_reg; + u32 wr_cfg_reg[PHYTIUM_QSPI_MAX_NORCHIP]; + u32 rd_cfg_reg[PHYTIUM_QSPI_MAX_NORCHIP]; u32 flash_cap; }; @@ -538,7 +538,7 @@ static int phytium_qspi_dirmap_create(struct spi_mem_dirmap_desc *desc) cmd |= flash->clk_div & QSPI_RD_CFG_RD_SCK_SEL_MASK; writel_relaxed(cmd, qspi->io_base + QSPI_RD_CFG_REG); - qspi->rd_cfg_reg = cmd; + qspi->rd_cfg_reg[spi->chip_select] = cmd; dev_dbg(qspi->dev, "Create read dirmap and setup RD_CFG_REG [%#x].\n", cmd); } else if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT) { @@ -555,7 +555,7 @@ static int phytium_qspi_dirmap_create(struct spi_mem_dirmap_desc *desc) cmd |= QSPI_WR_CFG_WR_MODE_MASK; cmd |= flash->clk_div & QSPI_WR_CFG_WR_SCK_SEL_MASK; - qspi->wr_cfg_reg = cmd; + qspi->wr_cfg_reg[spi->chip_select] = cmd; } else { ret = -EINVAL; } @@ -574,6 +574,7 @@ static ssize_t phytium_qspi_dirmap_read(struct spi_mem_dirmap_desc *desc, void __iomem *src = flash->base + offs; u8 *buf_rx = buf; + writel_relaxed(qspi->rd_cfg_reg[spi->chip_select], qspi->io_base + QSPI_RD_CFG_REG); memcpy_fromio(buf_rx, src, len); return len; @@ -593,7 +594,7 @@ static ssize_t phytium_qspi_dirmap_write(struct spi_mem_dirmap_desc *desc, u_char tmp[4] = {0}; /* set wr_cfg for drimap write */ - writel_relaxed(qspi->wr_cfg_reg, qspi->io_base + QSPI_WR_CFG_REG); + writel_relaxed(qspi->wr_cfg_reg[spi->chip_select], qspi->io_base + QSPI_WR_CFG_REG); if (offs & 0x03) { dev_err(qspi->dev, "Addr not four-byte aligned!\n"); @@ -889,7 +890,6 @@ static int __maybe_unused phytium_qspi_resume(struct device *dev) if (!qspi->nodirmap) { /* set rd_cfg reg and flash_capacity reg after resume */ - writel_relaxed(qspi->rd_cfg_reg, qspi->io_base + QSPI_RD_CFG_REG); writel_relaxed(qspi->flash_cap, qspi->io_base + QSPI_FLASH_CAP_REG); } else { writel_relaxed(WR_CFG_NODIR_VALUE, qspi->io_base + QSPI_WR_CFG_REG); From 187ca4c1cf1f460e0caacb2dfaef71e107a3564f Mon Sep 17 00:00:00 2001 From: Xia Qian Date: Fri, 15 May 2026 11:40:10 +0800 Subject: [PATCH 81/81] spi: phytium: Strict QSPI compile dependency Require both ARCH_PHYTIUM and COMPILE_TEST for SPI_PHYTIUM_QSPI. Ensures driver only builds when explicitly tested on Phytium platforms. Prevents accidental inclusion in production builds, enforcing controlled testing visibility for experimental hardware support. Mainline: Open-Source Signed-off-by: zhuling Signed-off-by: Wang Yinfeng Signed-off-by: Xia Qian --- drivers/spi/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 10e9ea557baa9..e7010c601c56c 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -818,7 +818,7 @@ config SPI_PHYTIUM_PCI config SPI_PHYTIUM_QSPI tristate "Phytium Quad SPI controller" - depends on ARCH_PHYTIUM || COMPILE_TEST + depends on ARCH_PHYTIUM && COMPILE_TEST depends on OF depends on SPI_MEM help